From b970ae4bad024af47080456aba88c2548e5ea182 Mon Sep 17 00:00:00 2001 From: jwplatt Date: Sat, 12 Mar 2011 12:34:52 -0500 Subject: [PATCH] Initial Commit of CyanWorlds.com Engine Open Source Client/Plugin --- LICENSE.txt | 622 + MOULOpenSourceClientPlugin/LICENSE.txt | 622 + .../Plasma20/Docs/Distributor.doc | Bin 0 -> 400384 bytes .../Docs/ReleaseNotes/ReleaseNotes.txt | 31 + .../Plasma20/Docs/Resource Manager.doc | Bin 0 -> 54784 bytes .../Plasma20/Docs/sound cones.htm | 40 + .../MsDevProjects/AllDlls/AllDlls.sln | 1444 ++ .../MsDevProjects/AllDlls/AllDlls.vcproj | 58 + .../Plasma/Apps/AllClient/AllClient.sln | 1394 ++ .../Plasma/Apps/AllClient/AllClient.vcproj | 58 + .../Plasma/Apps/plClient/plClient.vcproj | 599 + .../Apps/plClientKey/plClientKey.vcproj | 216 + .../plClientPatcher/plClientPatcher.vcproj | 219 + .../Apps/plFileEncrypt/plFileEncrypt.vcproj | 174 + .../Apps/plFileSecure/plFileSecure.vcproj | 175 + .../Apps/plLogDecrypt/plLogDecrypt.vcproj | 134 + .../Plasma/Apps/plMD5/plMD5.vcproj | 135 + .../Plasma/Apps/plPageInfo/plPageInfo.vcproj | 198 + .../plPageOptimizer/plPageOptimizer.vcproj | 157 + .../plPlasmaInstaller.vcproj | 326 + .../Apps/plPlasmaUpdate/plPlasmaUpdate.vcproj | 267 + .../Apps/plPythonPack/plPythonPack.vcproj | 201 + .../Apps/plUruLauncher/plUruLauncher.vcproj | 275 + .../Plasma/CoreLib/CoreLib.vcproj | 1928 +++ .../Plasma/CoreLibExe/CoreLibExe.vcproj | 319 + .../FeatureLibInc/FeatureLibInc.vcproj | 60 + .../FeatureLib/pfAnimation/pfAnimation.vcproj | 348 + .../Plasma/FeatureLib/pfAudio/pfAudio.vcproj | 181 + .../Plasma/FeatureLib/pfCCR/pfCCR.vcproj | 182 + .../FeatureLib/pfCamera/pfCamera.vcproj | 250 + .../FeatureLib/pfCharacter/pfCharacter.vcproj | 205 + .../pfConditional/pfConditional.vcproj | 411 + .../FeatureLib/pfConsole/pfConsole.vcproj | 373 + .../FeatureLib/pfCsrSrv/pfCsrSrv.vcproj | 131 + .../pfGameGUIMgr/pfGameGUIMgr.vcproj | 713 + .../FeatureLib/pfGameMgr/pfGameMgr.vcproj | 180 + .../pfGameScoreMgr/pfGameScoreMgr.vcproj | 117 + .../pfJournalBook/pfJournalBook.vcproj | 158 + .../pfLocalizationMgr.vcproj | 132 + .../pfLoginDialog/pfLoginDialog.vcproj | 174 + .../FeatureLib/pfMessage/pfMessage.vcproj | 259 + .../FeatureLib/pfPython/pfPython.vcproj | 2332 +++ .../pfSecurePreloader.vcproj | 122 + .../pfStackTrace/pfStackTrace.vcproj | 239 + .../FeatureLib/pfSurface/pfSurface.vcproj | 296 + .../NucleusLib/pnAddrInfo/pnAddrInfo.vcproj | 216 + .../NucleusLib/pnAsyncCore/pnAsyncCore.vcproj | 345 + .../pnAsyncCoreExe/pnAsyncCoreExe.vcproj | 399 + .../Plasma/NucleusLib/pnCrash/pnCrash.vcproj | 227 + .../NucleusLib/pnCrashExe/pnCrashExe.vcproj | 230 + .../NucleusLib/pnCsrCli/pnCsrCli.vcproj | 125 + .../NucleusLib/pnCsrNet/pnCsrNet.vcproj | 125 + .../NucleusLib/pnDispatch/pnDispatch.vcproj | 389 + .../NucleusLib/pnFactory/pnFactory.vcproj | 347 + .../NucleusLib/pnGameMgr/pnGameMgr.vcproj | 262 + .../Plasma/NucleusLib/pnIni/pnIni.vcproj | 337 + .../NucleusLib/pnIniExe/pnIniExe.vcproj | 326 + .../NucleusLib/pnInputCore/pnInputCore.vcproj | 170 + .../pnKeyedObject/pnKeyedObject.vcproj | 590 + .../Plasma/NucleusLib/pnMail/pnMail.vcproj | 228 + .../NucleusLib/pnMessage/pnMessage.vcproj | 994 ++ .../NucleusLib/pnModifier/pnModifier.vcproj | 251 + .../NucleusLib/pnNetBase/pnNetBase.vcproj | 365 + .../NucleusLib/pnNetCli/pnNetCli.vcproj | 334 + .../NucleusLib/pnNetCommon/pnNetCommon.vcproj | 791 + .../NucleusLib/pnNetDiag/pnNetDiag.vcproj | 129 + .../NucleusLib/pnNetLog/pnNetLog.vcproj | 221 + .../pnNetProtocol/pnNetProtocol.vcproj | 449 + .../pnNucleusInc/pnNucleusInc.vcproj | 438 + .../NucleusLib/pnOraLib/pnOraLib.vcproj | 207 + .../NucleusLib/pnProduct/pnProduct.vcproj | 351 + .../pnSceneObject/pnSceneObject.vcproj | 484 + .../NucleusLib/pnSimpleNet/pnSimpleNet.vcproj | 125 + .../NucleusLib/pnSqlLib/pnSqlLib.vcproj | 245 + .../NucleusLib/pnSrvUtils/pnSrvUtils.vcproj | 141 + .../pnSrvUtilsExe/pnSrvUtilsExe.vcproj | 133 + .../Plasma/NucleusLib/pnTimer/pnTimer.vcproj | 534 + .../Plasma/NucleusLib/pnUtils/pnUtils.vcproj | 505 + .../NucleusLib/pnUtilsExe/pnUtilsExe.vcproj | 343 + .../PubUtilLib/PubUtilInc/PubUtilInc.vcproj | 64 + .../plAgeDescription/plAgeDescription.vcproj | 316 + .../PubUtilLib/plAgeLoader/plAgeLoader.vcproj | 207 + .../PubUtilLib/plAudible/plAudible.vcproj | 204 + .../Plasma/PubUtilLib/plAudio/plAudio.vcproj | 460 + .../PubUtilLib/plAudioCore/plAudioCore.vcproj | 522 + .../PubUtilLib/plAvatar/plAvatar.vcproj | 1063 ++ .../plCompression/plCompression.vcproj | 529 + .../PubUtilLib/plContainer/plContainer.vcproj | 224 + .../plDeviceSelector/plDeviceSelector.vcproj | 155 + .../PubUtilLib/plDrawable/plDrawable.vcproj | 1122 ++ .../plEncryption/plEncryption.vcproj | 460 + .../Plasma/PubUtilLib/plFile/plFile.vcproj | 986 ++ .../Plasma/PubUtilLib/plGClip/plGClip.vcproj | 129 + .../PubUtilLib/plGImage/plGImage.vcproj | 483 + .../PubUtilLib/plGLight/plGLight.vcproj | 345 + .../PubUtilLib/plGeometry/plGeometry.vcproj | 132 + .../PubUtilLib/plInputCore/plInputCore.vcproj | 342 + .../PubUtilLib/plInterp/plInterp.vcproj | 299 + .../PubUtilLib/plIntersect/plIntersect.vcproj | 299 + .../Plasma/PubUtilLib/plJPEG/plJPEG.vcproj | 155 + .../Plasma/PubUtilLib/plMath/plMath.vcproj | 317 + .../PubUtilLib/plMessage/plMessage.vcproj | 2780 +++ .../PubUtilLib/plModifier/plModifier.vcproj | 572 + .../PubUtilLib/plNetClient/PlNetClient.vcproj | 474 + .../plNetClientComm/plNetClientComm.vcproj | 160 + .../plNetClientRecorder.vcproj | 195 + .../PubUtilLib/plNetCommon/plNetCommon.vcproj | 599 + .../plNetGameLib/plNetGameLib.vcproj | 344 + .../plNetMessage/plNetMessage.vcproj | 402 + .../plNetTransport/plNetTransport.vcproj | 178 + .../plParticleSystem/plParticleSystem.vcproj | 325 + .../Plasma/PubUtilLib/plPhysX/plPhysX.vcproj | 149 + .../PubUtilLib/plPhysical/plPhysical.vcproj | 211 + .../PubUtilLib/plPipeline/plPipeline.vcproj | 711 + .../plProgressMgr/plProgressMgr.vcproj | 155 + .../PubUtilLib/plResMgr/plResMgr.vcproj | 747 + .../Plasma/PubUtilLib/plSDL/plSDL.vcproj | 655 + .../Plasma/PubUtilLib/plScene/plScene.vcproj | 388 + .../PubUtilLib/plSockets/plSockets.vcproj | 362 + .../plStatGather/plStatGather.vcproj | 204 + .../PubUtilLib/plStatusLog/plStatusLog.vcproj | 440 + .../plStreamLogger/plStreamLogger.vcproj | 215 + .../PubUtilLib/plSurface/plSurface.vcproj | 475 + .../PubUtilLib/plTransform/plTransform.vcproj | 201 + .../Plasma/PubUtilLib/plUUID/plUUID.vcproj | 304 + .../plUnifiedTime/plUnifiedTime.vcproj | 591 + .../Plasma/PubUtilLib/plVault/plVault.vcproj | 385 + .../plWinStrBlock/plWinStrBlock.vcproj | 148 + .../PubUtilLib/plWndCtrls/plWndCtrls.vcproj | 245 + .../pyNetClientComm/pyNetClientComm.vcproj | 169 + .../Plasma/PythonLib/pyPlasma/pyPlasma.vcproj | 964 ++ .../PythonLib/pyPloticus/pyPloticus.vcproj | 193 + .../Plasma/PythonLib/pyVault/pyVault.vcproj | 166 + .../Tools/MaxComponent/MaxComponent.vcproj | 1908 ++ .../Tools/MaxConvert/MaxConvert.vcproj | 503 + .../Tools/MaxExport/MaxExport.vcproj | 271 + .../Tools/MaxMain/MaxMain.vcproj | 959 + .../MaxPlasmaLights/MaxPlasmaLights.vcproj | 430 + .../Tools/MaxPlasmaMtls/MaxPlasmaMtls.vcproj | 1046 ++ .../MaxSceneViewer/MaxSceneViewer.vcproj | 246 + .../plFontConverter/plFontConverter.vcproj | 332 + .../Win32/HawkVoice/lib/HawkVoiceDIstatic.lib | Bin 0 -> 189518 bytes .../Win32/HawkVoice/src/BLOWFISH/Blowfish.001 | 122 + .../Win32/HawkVoice/src/BLOWFISH/Blowfish.dsp | 128 + .../Win32/HawkVoice/src/BLOWFISH/COPYRIGHT | 46 + .../SDKs/Win32/HawkVoice/src/BLOWFISH/INSTALL | 14 + .../Win32/HawkVoice/src/BLOWFISH/Makefile | 160 + .../Win32/HawkVoice/src/BLOWFISH/Makefile.ssl | 104 + .../Win32/HawkVoice/src/BLOWFISH/Makefile.uni | 160 + .../SDKs/Win32/HawkVoice/src/BLOWFISH/README | 8 + .../Win32/HawkVoice/src/BLOWFISH/asm/bf586.pl | 159 + .../HawkVoice/src/BLOWFISH/asm/bx86-cpp.s | 666 + .../HawkVoice/src/BLOWFISH/asm/bx86unix.cpp | 33 + .../Win32/HawkVoice/src/BLOWFISH/asm/readme | 3 + .../HawkVoice/src/BLOWFISH/asm/win32.asm | 663 + .../Win32/HawkVoice/src/BLOWFISH/asm/x86ms.pl | 249 + .../HawkVoice/src/BLOWFISH/asm/x86unix.pl | 313 + .../Win32/HawkVoice/src/BLOWFISH/bf_cbc.c | 143 + .../Win32/HawkVoice/src/BLOWFISH/bf_cfb64.c | 122 + .../Win32/HawkVoice/src/BLOWFISH/bf_ecb.c | 91 + .../Win32/HawkVoice/src/BLOWFISH/bf_enc.c | 137 + .../Win32/HawkVoice/src/BLOWFISH/bf_locl.h | 236 + .../Win32/HawkVoice/src/BLOWFISH/bf_locl.org | 215 + .../Win32/HawkVoice/src/BLOWFISH/bf_ofb64.c | 110 + .../SDKs/Win32/HawkVoice/src/BLOWFISH/bf_pi.h | 325 + .../Win32/HawkVoice/src/BLOWFISH/bf_skey.c | 116 + .../Win32/HawkVoice/src/BLOWFISH/blowfish.doc | 149 + .../Win32/HawkVoice/src/BLOWFISH/blowfish.h | 114 + .../Plasma20/SDKs/Win32/HawkVoice/src/hvdi.h | 194 + .../Cypython-2.3.3/Grammar/.cvsignore | 3 + .../XPlatform/Cypython-2.3.3/Grammar/Grammar | 107 + .../SDKs/XPlatform/Cypython-2.3.3/LICENSE | 262 + .../Cypython-2.3.3/Modules/.cvsignore | 9 + .../Cypython-2.3.3/Modules/Setup.config.in | 13 + .../Cypython-2.3.3/Modules/Setup.dist | 486 + .../XPlatform/Cypython-2.3.3/Modules/_bsddb.c | 4715 +++++ .../Cypython-2.3.3/Modules/_codecsmodule.c | 816 + .../XPlatform/Cypython-2.3.3/Modules/_csv.c | 1556 ++ .../Cypython-2.3.3/Modules/_curses_panel.c | 471 + .../Cypython-2.3.3/Modules/_cursesmodule.c | 2595 +++ .../Cypython-2.3.3/Modules/_hotshot.c | 1666 ++ .../Cypython-2.3.3/Modules/_localemodule.c | 783 + .../Cypython-2.3.3/Modules/_randommodule.c | 531 + .../XPlatform/Cypython-2.3.3/Modules/_sre.c | 3166 ++++ .../XPlatform/Cypython-2.3.3/Modules/_ssl.c | 639 + .../Cypython-2.3.3/Modules/_testcapimodule.c | 642 + .../Cypython-2.3.3/Modules/_tkinter.c | 3203 ++++ .../Cypython-2.3.3/Modules/_weakref.c | 131 + .../Cypython-2.3.3/Modules/addrinfo.h | 176 + .../Cypython-2.3.3/Modules/almodule.c | 3222 ++++ .../XPlatform/Cypython-2.3.3/Modules/ar_beos | 73 + .../Cypython-2.3.3/Modules/arraymodule.c | 2037 +++ .../Cypython-2.3.3/Modules/audioop.c | 1381 ++ .../Cypython-2.3.3/Modules/binascii.c | 1337 ++ .../Cypython-2.3.3/Modules/bsddbmodule.c | 856 + .../Cypython-2.3.3/Modules/bz2module.c | 2191 +++ .../Cypython-2.3.3/Modules/cPickle.c | 5761 +++++++ .../Cypython-2.3.3/Modules/cStringIO.c | 731 + .../Cypython-2.3.3/Modules/ccpython.cc | 11 + .../Cypython-2.3.3/Modules/cdmodule.c | 794 + .../XPlatform/Cypython-2.3.3/Modules/cgen.py | 520 + .../Cypython-2.3.3/Modules/cgensupport.c | 310 + .../Cypython-2.3.3/Modules/cgensupport.h | 64 + .../Cypython-2.3.3/Modules/clmodule.c | 2557 +++ .../Cypython-2.3.3/Modules/cmathmodule.c | 404 + .../Cypython-2.3.3/Modules/config.c.in | 48 + .../Cypython-2.3.3/Modules/cryptmodule.c | 44 + .../XPlatform/Cypython-2.3.3/Modules/cstubs | 1364 ++ .../Cypython-2.3.3/Modules/datetimemodule.c | 4854 ++++++ .../Cypython-2.3.3/Modules/dbmmodule.c | 372 + .../Cypython-2.3.3/Modules/dlmodule.c | 254 + .../Cypython-2.3.3/Modules/errnomodule.c | 795 + .../Cypython-2.3.3/Modules/expat/Makefile.in | 158 + .../Cypython-2.3.3/Modules/expat/ascii.h | 85 + .../Cypython-2.3.3/Modules/expat/asciitab.h | 36 + .../Cypython-2.3.3/Modules/expat/expat.h | 1001 ++ .../Cypython-2.3.3/Modules/expat/iasciitab.h | 37 + .../Cypython-2.3.3/Modules/expat/internal.h | 73 + .../Cypython-2.3.3/Modules/expat/latin1tab.h | 36 + .../Cypython-2.3.3/Modules/expat/macconfig.h | 104 + .../Cypython-2.3.3/Modules/expat/nametab.h | 150 + .../Cypython-2.3.3/Modules/expat/utf8tab.h | 37 + .../Cypython-2.3.3/Modules/expat/winconfig.h | 30 + .../Cypython-2.3.3/Modules/expat/xmlparse.c | 5814 +++++++ .../Cypython-2.3.3/Modules/expat/xmlrole.c | 1323 ++ .../Cypython-2.3.3/Modules/expat/xmlrole.h | 114 + .../Cypython-2.3.3/Modules/expat/xmltok.c | 1634 ++ .../Cypython-2.3.3/Modules/expat/xmltok.h | 315 + .../Modules/expat/xmltok_impl.c | 1779 ++ .../Modules/expat/xmltok_impl.h | 46 + .../Cypython-2.3.3/Modules/expat/xmltok_ns.c | 106 + .../Cypython-2.3.3/Modules/fcntlmodule.c | 594 + .../Cypython-2.3.3/Modules/flmodule.c | 2138 +++ .../Cypython-2.3.3/Modules/fmmodule.c | 262 + .../Cypython-2.3.3/Modules/fpectlmodule.c | 276 + .../Cypython-2.3.3/Modules/fpetestmodule.c | 184 + .../Cypython-2.3.3/Modules/gc_weakref.txt | 107 + .../Cypython-2.3.3/Modules/gcmodule.c | 1232 ++ .../Cypython-2.3.3/Modules/gdbmmodule.c | 523 + .../Cypython-2.3.3/Modules/getaddrinfo.c | 638 + .../Cypython-2.3.3/Modules/getbuildinfo.c | 38 + .../Cypython-2.3.3/Modules/getnameinfo.c | 214 + .../Cypython-2.3.3/Modules/getpath.c | 650 + .../Cypython-2.3.3/Modules/glmodule.c | 7628 ++++++++ .../Cypython-2.3.3/Modules/grpmodule.c | 177 + .../Cypython-2.3.3/Modules/imageop.c | 708 + .../Cypython-2.3.3/Modules/imgfile.c | 502 + .../Cypython-2.3.3/Modules/itertoolsmodule.c | 1852 ++ .../Cypython-2.3.3/Modules/ld_so_aix | 183 + .../Cypython-2.3.3/Modules/ld_so_beos | 78 + .../Cypython-2.3.3/Modules/linuxaudiodev.c | 519 + .../XPlatform/Cypython-2.3.3/Modules/main.c | 466 + .../Cypython-2.3.3/Modules/makesetup | 297 + .../Cypython-2.3.3/Modules/makexp_aix | 75 + .../Cypython-2.3.3/Modules/mathmodule.c | 391 + .../XPlatform/Cypython-2.3.3/Modules/md5.h | 62 + .../XPlatform/Cypython-2.3.3/Modules/md5c.c | 290 + .../Cypython-2.3.3/Modules/md5module.c | 268 + .../Cypython-2.3.3/Modules/mmapmodule.c | 1124 ++ .../Cypython-2.3.3/Modules/mpzmodule.c | 1690 ++ .../Cypython-2.3.3/Modules/nismodule.c | 382 + .../Cypython-2.3.3/Modules/operator.c | 264 + .../Cypython-2.3.3/Modules/ossaudiodev.c | 1094 ++ .../Cypython-2.3.3/Modules/parsermodule.c | 2957 ++++ .../Cypython-2.3.3/Modules/pcre-int.h | 303 + .../XPlatform/Cypython-2.3.3/Modules/pcre.h | 84 + .../Cypython-2.3.3/Modules/pcremodule.c | 636 + .../Cypython-2.3.3/Modules/posixmodule.c | 7535 ++++++++ .../Cypython-2.3.3/Modules/puremodule.c | 986 ++ .../Cypython-2.3.3/Modules/pwdmodule.c | 188 + .../Cypython-2.3.3/Modules/pyexpat.c | 2057 +++ .../XPlatform/Cypython-2.3.3/Modules/pypcre.c | 4756 +++++ .../XPlatform/Cypython-2.3.3/Modules/python.c | 24 + .../Cypython-2.3.3/Modules/readline.c | 736 + .../Cypython-2.3.3/Modules/regexmodule.c | 689 + .../Cypython-2.3.3/Modules/regexpr.c | 2094 +++ .../Cypython-2.3.3/Modules/regexpr.h | 155 + .../Cypython-2.3.3/Modules/resource.c | 316 + .../Cypython-2.3.3/Modules/rgbimgmodule.c | 763 + .../Cypython-2.3.3/Modules/rotormodule.c | 627 + .../Cypython-2.3.3/Modules/selectmodule.c | 688 + .../Cypython-2.3.3/Modules/sgimodule.c | 55 + .../Cypython-2.3.3/Modules/shamodule.c | 542 + .../Cypython-2.3.3/Modules/signalmodule.c | 674 + .../Cypython-2.3.3/Modules/socketmodule.c | 4151 +++++ .../Cypython-2.3.3/Modules/socketmodule.h | 221 + .../XPlatform/Cypython-2.3.3/Modules/sre.h | 91 + .../Cypython-2.3.3/Modules/sre_constants.h | 85 + .../Cypython-2.3.3/Modules/stropmodule.c | 1244 ++ .../Cypython-2.3.3/Modules/structmodule.c | 1297 ++ .../Cypython-2.3.3/Modules/sunaudiodev.c | 463 + .../Cypython-2.3.3/Modules/svmodule.c | 964 ++ .../Cypython-2.3.3/Modules/symtablemodule.c | 79 + .../Cypython-2.3.3/Modules/syslogmodule.c | 222 + .../Cypython-2.3.3/Modules/termios.c | 924 + .../Cypython-2.3.3/Modules/testcapi_long.h | 166 + .../Cypython-2.3.3/Modules/threadmodule.c | 406 + .../Cypython-2.3.3/Modules/timemodule.c | 944 + .../XPlatform/Cypython-2.3.3/Modules/timing.h | 67 + .../Cypython-2.3.3/Modules/timingmodule.c | 58 + .../Cypython-2.3.3/Modules/tkappinit.c | 139 + .../Cypython-2.3.3/Modules/unicodedata.c | 899 + .../Cypython-2.3.3/Modules/unicodedata_db.h | 3781 ++++ .../Cypython-2.3.3/Modules/unicodename_db.h | 10289 +++++++++++ .../Cypython-2.3.3/Modules/xreadlinesmodule.c | 171 + .../Cypython-2.3.3/Modules/xxmodule.c | 374 + .../Cypython-2.3.3/Modules/xxsubtype.c | 303 + .../XPlatform/Cypython-2.3.3/Modules/yuv.h | 99 + .../Cypython-2.3.3/Modules/yuvconvert.c | 118 + .../Cypython-2.3.3/Modules/zipimport.c | 1188 ++ .../Cypython-2.3.3/Modules/zlibmodule.c | 907 + .../Cypython-2.3.3/Objects/.cvsignore | 2 + .../Cypython-2.3.3/Objects/abstract.c | 2192 +++ .../Cypython-2.3.3/Objects/boolobject.c | 201 + .../Cypython-2.3.3/Objects/bufferobject.c | 568 + .../Cypython-2.3.3/Objects/cellobject.c | 114 + .../Cypython-2.3.3/Objects/classobject.c | 2516 +++ .../Cypython-2.3.3/Objects/cobject.c | 146 + .../Cypython-2.3.3/Objects/complexobject.c | 1022 ++ .../Cypython-2.3.3/Objects/descrobject.c | 1255 ++ .../Cypython-2.3.3/Objects/dictnotes.txt | 245 + .../Cypython-2.3.3/Objects/dictobject.c | 2086 +++ .../Cypython-2.3.3/Objects/enumobject.c | 161 + .../Cypython-2.3.3/Objects/fileobject.c | 2402 +++ .../Cypython-2.3.3/Objects/floatobject.c | 1253 ++ .../Cypython-2.3.3/Objects/frameobject.c | 815 + .../Cypython-2.3.3/Objects/funcobject.c | 875 + .../Cypython-2.3.3/Objects/intobject.c | 1188 ++ .../Cypython-2.3.3/Objects/iterobject.c | 228 + .../Cypython-2.3.3/Objects/listobject.c | 2501 +++ .../Cypython-2.3.3/Objects/listsort.txt | 667 + .../Cypython-2.3.3/Objects/longobject.c | 2912 ++++ .../Cypython-2.3.3/Objects/methodobject.c | 374 + .../Cypython-2.3.3/Objects/moduleobject.c | 260 + .../XPlatform/Cypython-2.3.3/Objects/object.c | 2228 +++ .../Cypython-2.3.3/Objects/obmalloc.c | 1375 ++ .../Cypython-2.3.3/Objects/rangeobject.c | 287 + .../Cypython-2.3.3/Objects/sliceobject.c | 328 + .../Cypython-2.3.3/Objects/stringobject.c | 4228 +++++ .../Cypython-2.3.3/Objects/structseq.c | 395 + .../Cypython-2.3.3/Objects/tupleobject.c | 836 + .../Cypython-2.3.3/Objects/typeobject.c | 5707 ++++++ .../Cypython-2.3.3/Objects/unicodectype.c | 423 + .../Cypython-2.3.3/Objects/unicodeobject.c | 6859 ++++++++ .../Cypython-2.3.3/Objects/unicodetype_db.h | 1090 ++ .../Cypython-2.3.3/Objects/weakrefobject.c | 783 + .../XPlatform/Cypython-2.3.3/PC/.cvsignore | 2 + .../Cypython-2.3.3/PC/PlasmaPack.cpp | 17 + .../XPlatform/Cypython-2.3.3/PC/PlasmaPack.h | 15 + .../XPlatform/Cypython-2.3.3/PC/WinMain.c | 16 + .../XPlatform/Cypython-2.3.3/PC/_winreg.c | 1526 ++ .../PC/bdist_wininst/.cvsignore | 4 + .../PC/bdist_wininst/PythonPowered.bmp | Bin 0 -> 2582 bytes .../PC/bdist_wininst/README.txt | 5 + .../Cypython-2.3.3/PC/bdist_wininst/archive.h | 96 + .../Cypython-2.3.3/PC/bdist_wininst/extract.c | 312 + .../Cypython-2.3.3/PC/bdist_wininst/install.c | 2294 +++ .../PC/bdist_wininst/install.rc | 220 + .../PC/bdist_wininst/resource.h | 38 + .../PC/bdist_wininst/wininst.dsp | 128 + .../PC/bdist_wininst/wininst.dsw | 29 + .../SDKs/XPlatform/Cypython-2.3.3/PC/config.c | 127 + .../SDKs/XPlatform/Cypython-2.3.3/PC/dl_nt.c | 44 + .../Cypython-2.3.3/PC/dllbase_nt.txt | 76 + .../Cypython-2.3.3/PC/example_nt/.cvsignore | 5 + .../Cypython-2.3.3/PC/example_nt/example.c | 20 + .../Cypython-2.3.3/PC/example_nt/example.def | 2 + .../Cypython-2.3.3/PC/example_nt/example.dsp | 117 + .../Cypython-2.3.3/PC/example_nt/example.dsw | 29 + .../Cypython-2.3.3/PC/example_nt/readme.txt | 165 + .../Cypython-2.3.3/PC/frozen_dllmain.c | 134 + .../XPlatform/Cypython-2.3.3/PC/getpathp.c | 686 + .../XPlatform/Cypython-2.3.3/PC/import_nt.c | 93 + .../Cypython-2.3.3/PC/make_versioninfo.c | 33 + .../Cypython-2.3.3/PC/msvcrtmodule.c | 232 + .../Cypython-2.3.3/PC/os2emx/Makefile | 671 + .../Cypython-2.3.3/PC/os2emx/README.os2emx | 671 + .../Cypython-2.3.3/PC/os2emx/config.c | 180 + .../Cypython-2.3.3/PC/os2emx/dlfcn.c | 223 + .../Cypython-2.3.3/PC/os2emx/dlfcn.h | 51 + .../Cypython-2.3.3/PC/os2emx/dllentry.c | 42 + .../Cypython-2.3.3/PC/os2emx/getpathp.c | 407 + .../Cypython-2.3.3/PC/os2emx/pyconfig.h | 327 + .../Cypython-2.3.3/PC/os2emx/python23.def | 1161 ++ .../Cypython-2.3.3/PC/os2emx/pythonpm.c | 124 + .../Cypython-2.3.3/PC/os2vacpp/_tkinter.def | 8 + .../Cypython-2.3.3/PC/os2vacpp/config.c | 110 + .../Cypython-2.3.3/PC/os2vacpp/getpathp.c | 471 + .../Cypython-2.3.3/PC/os2vacpp/makefile | 1928 +++ .../Cypython-2.3.3/PC/os2vacpp/makefile.omk | 1301 ++ .../Cypython-2.3.3/PC/os2vacpp/pyconfig.h | 213 + .../Cypython-2.3.3/PC/os2vacpp/python.def | 480 + .../Cypython-2.3.3/PC/os2vacpp/readme.txt | 119 + .../SDKs/XPlatform/Cypython-2.3.3/PC/py.ico | Bin 0 -> 766 bytes .../SDKs/XPlatform/Cypython-2.3.3/PC/pyc.ico | Bin 0 -> 766 bytes .../XPlatform/Cypython-2.3.3/PC/pycon.ico | Bin 0 -> 766 bytes .../XPlatform/Cypython-2.3.3/PC/python.mk | 5 + .../XPlatform/Cypython-2.3.3/PC/python_exe.rc | 1 + .../XPlatform/Cypython-2.3.3/PC/python_nt.rc | 74 + .../XPlatform/Cypython-2.3.3/PC/pythonnt_rc.h | 4 + .../Cypython-2.3.3/PC/pythonnt_rc_d.h | 4 + .../XPlatform/Cypython-2.3.3/PC/readme.txt | 95 + .../XPlatform/Cypython-2.3.3/PC/testpy.py | 32 + .../XPlatform/Cypython-2.3.3/PC/w9xpopen.c | 112 + .../XPlatform/Cypython-2.3.3/PC/winsound.c | 246 + .../Cypython-2.3.3/PCbuild/.cvsignore | 14 + .../Cypython-2.3.3/PCbuild/BUILDno.txt | 137 + .../Cypython-2.3.3/PCbuild/Uninstal.wse | 514 + .../Cypython-2.3.3/PCbuild/_bsddb.dsp | 99 + .../XPlatform/Cypython-2.3.3/PCbuild/_csv.dsp | 99 + .../Cypython-2.3.3/PCbuild/_socket.dsp | 99 + .../XPlatform/Cypython-2.3.3/PCbuild/_sre.dsp | 99 + .../Cypython-2.3.3/PCbuild/_sre_static.vcproj | 112 + .../XPlatform/Cypython-2.3.3/PCbuild/_ssl.dsp | 89 + .../XPlatform/Cypython-2.3.3/PCbuild/_ssl.mak | 21 + .../Cypython-2.3.3/PCbuild/_symtable.dsp | 99 + .../Cypython-2.3.3/PCbuild/_testcapi.dsp | 99 + .../Cypython-2.3.3/PCbuild/_tkinter.dsp | 103 + .../Cypython-2.3.3/PCbuild/build_ssl.py | 163 + .../XPlatform/Cypython-2.3.3/PCbuild/bz2.dsp | 99 + .../Cypython-2.3.3/PCbuild/datetime.dsp | 99 + .../Cypython-2.3.3/PCbuild/field3.py | 35 + .../Cypython-2.3.3/PCbuild/installer.bmp | Bin 0 -> 50324 bytes .../XPlatform/Cypython-2.3.3/PCbuild/mmap.dsp | 99 + .../Cypython-2.3.3/PCbuild/parser.dsp | 99 + .../Cypython-2.3.3/PCbuild/pcbuild.dsw | 359 + .../Cypython-2.3.3/PCbuild/pyexpat.dsp | 111 + .../Cypython-2.3.3/PCbuild/python.dsp | 100 + .../Cypython-2.3.3/PCbuild/python.iss | 241 + .../Cypython-2.3.3/PCbuild/python20.wse | 3168 ++++ .../Cypython-2.3.3/PCbuild/pythoncore.dsp | 592 + .../Cypython-2.3.3/PCbuild/pythoncore.vcproj | 2427 +++ .../PCbuild/pythoncore_dyn_server.vcproj | 4337 +++++ .../PCbuild/pythoncore_static.vcproj | 549 + .../Cypython-2.3.3/PCbuild/pythonw.dsp | 101 + .../Cypython-2.3.3/PCbuild/readme.txt | 314 + .../XPlatform/Cypython-2.3.3/PCbuild/rmpyc.py | 25 + .../XPlatform/Cypython-2.3.3/PCbuild/rt.bat | 41 + .../Cypython-2.3.3/PCbuild/select.dsp | 99 + .../Cypython-2.3.3/PCbuild/unicodedata.dsp | 99 + .../Cypython-2.3.3/PCbuild/w9xpopen.dsp | 97 + .../Cypython-2.3.3/PCbuild/winreg.dsp | 108 + .../Cypython-2.3.3/PCbuild/winsound.dsp | 99 + .../XPlatform/Cypython-2.3.3/PCbuild/zlib.dsp | 109 + .../Cypython-2.3.3/Parser/.cvsignore | 3 + .../XPlatform/Cypython-2.3.3/Parser/acceler.c | 138 + .../XPlatform/Cypython-2.3.3/Parser/bitset.c | 66 + .../Cypython-2.3.3/Parser/firstsets.c | 110 + .../XPlatform/Cypython-2.3.3/Parser/grammar.c | 246 + .../Cypython-2.3.3/Parser/grammar.mak | 45 + .../Cypython-2.3.3/Parser/grammar1.c | 57 + .../Cypython-2.3.3/Parser/intrcheck.c | 193 + .../Cypython-2.3.3/Parser/listnode.c | 66 + .../Cypython-2.3.3/Parser/metagrammar.c | 159 + .../Cypython-2.3.3/Parser/myreadline.c | 191 + .../XPlatform/Cypython-2.3.3/Parser/node.c | 134 + .../XPlatform/Cypython-2.3.3/Parser/parser.c | 418 + .../XPlatform/Cypython-2.3.3/Parser/parser.h | 42 + .../Cypython-2.3.3/Parser/parsetok.c | 222 + .../XPlatform/Cypython-2.3.3/Parser/pgen.c | 706 + .../Cypython-2.3.3/Parser/pgenmain.c | 212 + .../Cypython-2.3.3/Parser/printgrammar.c | 113 + .../Cypython-2.3.3/Parser/tokenizer.c | 1432 ++ .../Cypython-2.3.3/Parser/tokenizer.h | 65 + .../Cypython-2.3.3/Parser/tokenizer_pgen.c | 2 + .../Cypython-2.3.3/Python/.cvsignore | 2 + .../XPlatform/Cypython-2.3.3/Python/atof.c | 50 + .../Cypython-2.3.3/Python/bltinmodule.c | 2406 +++ .../XPlatform/Cypython-2.3.3/Python/ceval.c | 4142 +++++ .../XPlatform/Cypython-2.3.3/Python/codecs.c | 831 + .../XPlatform/Cypython-2.3.3/Python/compile.c | 5779 +++++++ .../XPlatform/Cypython-2.3.3/Python/dup2.c | 30 + .../Cypython-2.3.3/Python/dynload_aix.c | 183 + .../Cypython-2.3.3/Python/dynload_atheos.c | 47 + .../Cypython-2.3.3/Python/dynload_beos.c | 254 + .../Cypython-2.3.3/Python/dynload_dl.c | 26 + .../Cypython-2.3.3/Python/dynload_hpux.c | 58 + .../Cypython-2.3.3/Python/dynload_mac.c | 113 + .../Cypython-2.3.3/Python/dynload_next.c | 114 + .../Cypython-2.3.3/Python/dynload_os2.c | 46 + .../Cypython-2.3.3/Python/dynload_shlib.c | 139 + .../Cypython-2.3.3/Python/dynload_stub.c | 11 + .../Cypython-2.3.3/Python/dynload_win.c | 252 + .../XPlatform/Cypython-2.3.3/Python/errors.c | 785 + .../Cypython-2.3.3/Python/exceptions.c | 1857 ++ .../XPlatform/Cypython-2.3.3/Python/fmod.c | 27 + .../XPlatform/Cypython-2.3.3/Python/frozen.c | 38 + .../Cypython-2.3.3/Python/frozenmain.c | 68 + .../XPlatform/Cypython-2.3.3/Python/future.c | 260 + .../XPlatform/Cypython-2.3.3/Python/getargs.c | 1562 ++ .../Cypython-2.3.3/Python/getcompiler.c | 28 + .../Cypython-2.3.3/Python/getcopyright.c | 23 + .../XPlatform/Cypython-2.3.3/Python/getcwd.c | 79 + .../Cypython-2.3.3/Python/getmtime.c | 21 + .../XPlatform/Cypython-2.3.3/Python/getopt.c | 83 + .../Cypython-2.3.3/Python/getplatform.c | 12 + .../Cypython-2.3.3/Python/getversion.c | 15 + .../Cypython-2.3.3/Python/graminit.c | 1770 ++ .../XPlatform/Cypython-2.3.3/Python/hypot.c | 23 + .../XPlatform/Cypython-2.3.3/Python/import.c | 2920 ++++ .../Cypython-2.3.3/Python/import_pack.c | 2977 ++++ .../Cypython-2.3.3/Python/importdl.c | 78 + .../Cypython-2.3.3/Python/importdl.h | 54 + .../Cypython-2.3.3/Python/mactoolboxglue.c | 621 + .../XPlatform/Cypython-2.3.3/Python/marshal.c | 882 + .../XPlatform/Cypython-2.3.3/Python/memmove.c | 25 + .../Cypython-2.3.3/Python/modsupport.c | 551 + .../Cypython-2.3.3/Python/mysnprintf.c | 93 + .../Cypython-2.3.3/Python/mystrtoul.c | 153 + .../XPlatform/Cypython-2.3.3/Python/pyfpe.c | 23 + .../XPlatform/Cypython-2.3.3/Python/pystate.c | 483 + .../Cypython-2.3.3/Python/pythonrun.c | 1724 ++ .../Cypython-2.3.3/Python/sigcheck.c | 19 + .../XPlatform/Cypython-2.3.3/Python/strdup.c | 14 + .../Cypython-2.3.3/Python/strerror.c | 24 + .../XPlatform/Cypython-2.3.3/Python/strtod.c | 156 + .../Cypython-2.3.3/Python/structmember.c | 276 + .../Cypython-2.3.3/Python/symtable.c | 180 + .../Cypython-2.3.3/Python/sysmodule.c | 1298 ++ .../XPlatform/Cypython-2.3.3/Python/thread.c | 248 + .../Cypython-2.3.3/Python/thread_atheos.h | 300 + .../Cypython-2.3.3/Python/thread_beos.h | 287 + .../Cypython-2.3.3/Python/thread_cthread.h | 156 + .../Cypython-2.3.3/Python/thread_foobar.h | 115 + .../Cypython-2.3.3/Python/thread_lwp.h | 149 + .../Cypython-2.3.3/Python/thread_nt.h | 324 + .../Cypython-2.3.3/Python/thread_os2.h | 272 + .../Cypython-2.3.3/Python/thread_pth.h | 213 + .../Cypython-2.3.3/Python/thread_pthread.h | 545 + .../Cypython-2.3.3/Python/thread_sgi.h | 379 + .../Cypython-2.3.3/Python/thread_solaris.h | 174 + .../Cypython-2.3.3/Python/thread_wince.h | 171 + .../Cypython-2.3.3/Python/traceback.c | 277 + .../SDKs/XPlatform/Cypython-2.3.3/README | 1199 ++ .../XboxBuild/_sre_static.vcproj | 212 + .../XboxBuild/pythoncore_static.vcproj | 562 + .../XPlatform/Cypython-2.3.3/include/Python.h | 157 + .../Cypython-2.3.3/include/abstract.h | 1217 ++ .../XPlatform/Cypython-2.3.3/include/bitset.h | 32 + .../Cypython-2.3.3/include/boolobject.h | 32 + .../Cypython-2.3.3/include/bufferobject.h | 33 + .../Cypython-2.3.3/include/cStringIO.h | 70 + .../Cypython-2.3.3/include/cellobject.h | 28 + .../XPlatform/Cypython-2.3.3/include/ceval.h | 138 + .../Cypython-2.3.3/include/classobject.h | 81 + .../Cypython-2.3.3/include/cobject.h | 51 + .../XPlatform/Cypython-2.3.3/include/codecs.h | 153 + .../Cypython-2.3.3/include/compile.h | 92 + .../Cypython-2.3.3/include/complexobject.h | 58 + .../Cypython-2.3.3/include/datetime.h | 150 + .../Cypython-2.3.3/include/descrobject.h | 91 + .../Cypython-2.3.3/include/dictobject.h | 132 + .../Cypython-2.3.3/include/enumobject.h | 16 + .../Cypython-2.3.3/include/errcode.h | 35 + .../XPlatform/Cypython-2.3.3/include/eval.h | 25 + .../Cypython-2.3.3/include/fileobject.h | 70 + .../Cypython-2.3.3/include/floatobject.h | 95 + .../Cypython-2.3.3/include/frameobject.h | 76 + .../Cypython-2.3.3/include/funcobject.h | 59 + .../Cypython-2.3.3/include/graminit.h | 68 + .../Cypython-2.3.3/include/grammar.h | 93 + .../XPlatform/Cypython-2.3.3/include/import.h | 51 + .../Cypython-2.3.3/include/intobject.h | 61 + .../Cypython-2.3.3/include/intrcheck.h | 15 + .../Cypython-2.3.3/include/iterobject.h | 23 + .../Cypython-2.3.3/include/listobject.h | 52 + .../Cypython-2.3.3/include/longintrepr.h | 58 + .../Cypython-2.3.3/include/longobject.h | 109 + .../Cypython-2.3.3/include/marshal.h | 23 + .../Cypython-2.3.3/include/metagrammar.h | 18 + .../Cypython-2.3.3/include/methodobject.h | 79 + .../Cypython-2.3.3/include/modsupport.h | 101 + .../Cypython-2.3.3/include/moduleobject.h | 24 + .../XPlatform/Cypython-2.3.3/include/node.h | 37 + .../XPlatform/Cypython-2.3.3/include/object.h | 783 + .../Cypython-2.3.3/include/objimpl.h | 326 + .../XPlatform/Cypython-2.3.3/include/opcode.h | 151 + .../XPlatform/Cypython-2.3.3/include/osdefs.h | 58 + .../Cypython-2.3.3/include/parsetok.h | 48 + .../Cypython-2.3.3/include/patchlevel.h | 37 + .../XPlatform/Cypython-2.3.3/include/pgen.h | 18 + .../Cypython-2.3.3/include/pgenheaders.h | 42 + .../Cypython-2.3.3/include/py_curses.h | 176 + .../Cypython-2.3.3/include/pydebug.h | 39 + .../Cypython-2.3.3/include/pyerrors.h | 233 + .../XPlatform/Cypython-2.3.3/include/pyfpe.h | 176 + .../Cypython-2.3.3/include/pygetopt.h | 17 + .../Cypython-2.3.3/include/pymactoolbox.h | 204 + .../XPlatform/Cypython-2.3.3/include/pymem.h | 108 + .../XPlatform/Cypython-2.3.3/include/pyport.h | 572 + .../Cypython-2.3.3/include/pystate.h | 172 + .../Cypython-2.3.3/include/pythonrun.h | 149 + .../Cypython-2.3.3/include/pythread.h | 44 + .../Cypython-2.3.3/include/rangeobject.h | 27 + .../Cypython-2.3.3/include/sliceobject.h | 42 + .../Cypython-2.3.3/include/stringobject.h | 174 + .../Cypython-2.3.3/include/structmember.h | 95 + .../Cypython-2.3.3/include/structseq.h | 41 + .../Cypython-2.3.3/include/symtable.h | 99 + .../Cypython-2.3.3/include/sysmodule.h | 30 + .../XPlatform/Cypython-2.3.3/include/token.h | 82 + .../Cypython-2.3.3/include/traceback.h | 22 + .../Cypython-2.3.3/include/tupleobject.h | 49 + .../Cypython-2.3.3/include/ucnhash.h | 29 + .../Cypython-2.3.3/include/unicodeobject.h | 1138 ++ .../Cypython-2.3.3/include/weakrefobject.h | 50 + .../Cypython-2.3.3/pyconfig_dyn/pyconfig.h | 596 + .../Cypython-2.3.3/pyconfig_static/pyconfig.h | 600 + .../Plasma20/Scripts/Installer/LargeMOUL.nsi | 149 + .../Plasma20/Scripts/Installer/MOUL.nsi | 147 + .../Scripts/Installer/MOULInstaller.exe | Bin 0 -> 431262 bytes .../Plasma20/Scripts/Max/PlasmaExport.ms | 39 + .../Plasma20/Scripts/Python/plasma/Plasma.py | 9688 +++++++++++ .../Scripts/Python/plasma/PlasmaConstants.py | 256 + .../Python/plasma/PlasmaControlKeys.py | 108 + .../Scripts/Python/plasma/PlasmaGame.py | 2615 +++ .../Python/plasma/PlasmaGameConstants.py | 159 + .../Scripts/Python/plasma/PlasmaKITypes.py | 469 + .../Python/plasma/PlasmaNetConstants.py | 34 + .../Scripts/Python/plasma/PlasmaTypes.py | 820 + .../Python/plasma/PlasmaVaultConstants.py | 98 + .../Plasma20/Scripts/Python/plasma/glue.py | 208 + .../Plasma20/Scripts/Python/plasma/pch.py | 286 + .../Scripts/Python/plasma/ptWordFilter.py | 140 + .../Plasma20/Scripts/Python/system/Bastion.py | 177 + .../Scripts/Python/system/ConfigParser.py | 616 + .../Scripts/Python/system/HTMLParser.py | 382 + .../Scripts/Python/system/StringIO.py | 251 + .../Scripts/Python/system/UserDict.py | 164 + .../Scripts/Python/system/__future__.py | 104 + .../Plasma20/Scripts/Python/system/ascii.py | 35 + .../Plasma20/Scripts/Python/system/atexit.py | 50 + .../Plasma20/Scripts/Python/system/bdb.py | 564 + .../Plasma20/Scripts/Python/system/bisect.py | 78 + .../Scripts/Python/system/calendar.py | 246 + .../Plasma20/Scripts/Python/system/cmd.py | 396 + .../Plasma20/Scripts/Python/system/codecs.py | 712 + .../Scripts/Python/system/colorsys.py | 123 + .../Scripts/Python/system/commands.py | 84 + .../Scripts/Python/system/compileall.py | 155 + .../Plasma20/Scripts/Python/system/copy.py | 430 + .../Scripts/Python/system/copy_reg.py | 189 + .../Scripts/Python/system/decompyle.py | 1500 ++ .../Plasma20/Scripts/Python/system/difflib.py | 1317 ++ .../Scripts/Python/system/dircache.py | 44 + .../Plasma20/Scripts/Python/system/dis.py | 227 + .../Plasma20/Scripts/Python/system/dospath.py | 341 + .../Scripts/Python/system/encoding_utf_8.py | 30 + .../Scripts/Python/system/encodings.py | 132 + .../Plasma20/Scripts/Python/system/filecmp.py | 306 + .../Scripts/Python/system/fileinput.py | 359 + .../Plasma20/Scripts/Python/system/fnmatch.py | 107 + .../Scripts/Python/system/fpformat.py | 142 + .../Plasma20/Scripts/Python/system/getopt.py | 211 + .../Plasma20/Scripts/Python/system/gettext.py | 493 + .../Plasma20/Scripts/Python/system/glob.py | 56 + .../Plasma20/Scripts/Python/system/ihooks.py | 514 + .../Plasma20/Scripts/Python/system/imghdr.py | 154 + .../Plasma20/Scripts/Python/system/imputil.py | 719 + .../Plasma20/Scripts/Python/system/inspect.py | 799 + .../Plasma20/Scripts/Python/system/keyword.py | 97 + .../Plasma20/Scripts/Python/system/knee.py | 126 + .../Scripts/Python/system/linecache.py | 100 + .../Plasma20/Scripts/Python/system/locale.py | 778 + .../Scripts/Python/system/markupbase.py | 383 + .../Plasma20/Scripts/Python/system/mutex.py | 50 + .../Plasma20/Scripts/Python/system/netrc.py | 111 + .../Plasma20/Scripts/Python/system/ntpath.py | 493 + .../Plasma20/Scripts/Python/system/opcode.py | 188 + .../Plasma20/Scripts/Python/system/os.py | 659 + .../Plasma20/Scripts/Python/system/pdb.py | 957 + .../Plasma20/Scripts/Python/system/pickle.py | 1403 ++ .../Plasma20/Scripts/Python/system/popen2.py | 202 + .../Scripts/Python/system/posixpath.py | 414 + .../Plasma20/Scripts/Python/system/pre.py | 656 + .../Plasma20/Scripts/Python/system/profile.py | 555 + .../Plasma20/Scripts/Python/system/pstats.py | 646 + .../Scripts/Python/system/py_compile.py | 164 + .../Plasma20/Scripts/Python/system/pyclbr.py | 344 + .../Plasma20/Scripts/Python/system/random.py | 843 + .../Plasma20/Scripts/Python/system/re.py | 6 + .../Scripts/Python/system/regex_syntax.py | 53 + .../Plasma20/Scripts/Python/system/repr.py | 117 + .../Plasma20/Scripts/Python/system/shlex.py | 292 + .../Plasma20/Scripts/Python/system/shutil.py | 171 + .../Plasma20/Scripts/Python/system/site.py | 376 + .../Plasma20/Scripts/Python/system/spark.py | 566 + .../Plasma20/Scripts/Python/system/sre.py | 311 + .../Scripts/Python/system/sre_compile.py | 487 + .../Scripts/Python/system/sre_constants.py | 261 + .../Scripts/Python/system/sre_parse.py | 739 + .../Plasma20/Scripts/Python/system/stat.py | 86 + .../Scripts/Python/system/statcache.py | 77 + .../Plasma20/Scripts/Python/system/string.py | 382 + .../Plasma20/Scripts/Python/system/symbol.py | 97 + .../Scripts/Python/system/tabnanny.py | 326 + .../Plasma20/Scripts/Python/system/token.py | 140 + .../Scripts/Python/system/tokenize.py | 295 + .../Scripts/Python/system/traceback.py | 301 + .../Plasma20/Scripts/Python/system/types.py | 90 + .../Plasma20/Scripts/Python/system/verify.py | 182 + .../Scripts/Python/system/warnings.py | 259 + .../Plasma20/Scripts/Python/system/weakref.py | 302 + .../Scripts/Python/system/webbrowser.py | 369 + .../Scripts/Python/system/whrandom.py | 140 + .../python/Do_Not_Load_Neighborhood.py | 486 + .../python/Do_Not_Load_RestorationGuild.py | 496 + .../ServerPython/python/system/Bastion.py | 175 + .../python/system/ConfigParser.py | 472 + .../ServerPython/python/system/StringIO.py | 239 + .../ServerPython/python/system/__future__.py | 104 + .../ServerPython/python/system/_sre.pyd | Bin 0 -> 53340 bytes .../ServerPython/python/system/_sre_d.pyd | Bin 0 -> 65630 bytes .../ServerPython/python/system/atexit.py | 50 + .../Scripts/ServerPython/python/system/bdb.py | 563 + .../ServerPython/python/system/bisect.py | 78 + .../ServerPython/python/system/calendar.py | 246 + .../Scripts/ServerPython/python/system/cmd.py | 330 + .../ServerPython/python/system/codecs.py | 636 + .../ServerPython/python/system/colorsys.py | 123 + .../ServerPython/python/system/commands.py | 84 + .../ServerPython/python/system/compileall.py | 148 + .../ServerPython/python/system/copy.py | 378 + .../ServerPython/python/system/copy_reg.py | 72 + .../ServerPython/python/system/decompyle.py | 1500 ++ .../ServerPython/python/system/difflib.py | 1083 ++ .../ServerPython/python/system/dircache.py | 44 + .../Scripts/ServerPython/python/system/dis.py | 327 + .../ServerPython/python/system/dospath.py | 341 + .../ServerPython/python/system/filecmp.py | 331 + .../ServerPython/python/system/fileinput.py | 349 + .../ServerPython/python/system/fnmatch.py | 107 + .../ServerPython/python/system/fpformat.py | 142 + .../ServerPython/python/system/getopt.py | 144 + .../ServerPython/python/system/gettext.py | 304 + .../ServerPython/python/system/glob.py | 56 + .../ServerPython/python/system/ihooks.py | 511 + .../ServerPython/python/system/imghdr.py | 154 + .../ServerPython/python/system/imputil.py | 720 + .../ServerPython/python/system/inspect.py | 779 + .../ServerPython/python/system/keyword.py | 97 + .../ServerPython/python/system/knee.py | 126 + .../ServerPython/python/system/linecache.py | 101 + .../ServerPython/python/system/locale.py | 734 + .../ServerPython/python/system/mutex.py | 51 + .../ServerPython/python/system/netrc.py | 108 + .../ServerPython/python/system/ntpath.py | 481 + .../Scripts/ServerPython/python/system/os.py | 612 + .../Scripts/ServerPython/python/system/pdb.py | 957 + .../ServerPython/python/system/pickle.py | 986 ++ .../ServerPython/python/system/popen2.py | 199 + .../ServerPython/python/system/posixpath.py | 414 + .../Scripts/ServerPython/python/system/pre.py | 656 + .../ServerPython/python/system/profile.py | 556 + .../ServerPython/python/system/pstats.py | 641 + .../ServerPython/python/system/py_compile.py | 82 + .../ServerPython/python/system/pyclbr.py | 337 + .../ServerPython/python/system/random.py | 777 + .../Scripts/ServerPython/python/system/re.py | 33 + .../python/system/regex_syntax.py | 53 + .../ServerPython/python/system/repr.py | 95 + .../ServerPython/python/system/shlex.py | 209 + .../ServerPython/python/system/shutil.py | 138 + .../ServerPython/python/system/site.py | 330 + .../ServerPython/python/system/spark.py | 566 + .../Scripts/ServerPython/python/system/sre.py | 311 + .../ServerPython/python/system/sre_compile.py | 455 + .../python/system/sre_constants.py | 259 + .../ServerPython/python/system/sre_parse.py | 738 + .../ServerPython/python/system/stat.py | 86 + .../ServerPython/python/system/statcache.py | 77 + .../ServerPython/python/system/string.py | 382 + .../ServerPython/python/system/symbol.py | 95 + .../ServerPython/python/system/tabnanny.py | 327 + .../ServerPython/python/system/token.py | 140 + .../ServerPython/python/system/tokenize.py | 286 + .../ServerPython/python/system/traceback.py | 301 + .../ServerPython/python/system/types.py | 86 + .../ServerPython/python/system/verify.py | 182 + .../ServerPython/python/system/warnings.py | 258 + .../ServerPython/python/system/weakref.py | 284 + .../ServerPython/python/system/whrandom.py | 140 + .../Plasma/Apps/plClient/pfAllCreatables.cpp | 26 + .../Plasma/Apps/plClient/plAllCreatables.cpp | 27 + .../Sources/Plasma/Apps/plClient/plClient.cpp | 2538 +++ .../Sources/Plasma/Apps/plClient/plClient.h | 295 + .../Plasma/Apps/plClient/plClientCreatable.h | 36 + .../Apps/plClient/plClientUpdateFormat.h | 51 + .../Plasma/Apps/plClient/plSimStateMsg.h | 26 + .../Plasma/Apps/plClient/pnAllCreatables.cpp | 28 + .../Sources/Plasma/Apps/plClient/res/Dirt.ICO | Bin 0 -> 2238 bytes .../Plasma/Apps/plClient/res/Microphone.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/Speaker.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/banner.bmp | Bin 0 -> 71960 bytes .../Plasma/Apps/plClient/res/bitmap1.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/bitmap2.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/bitmap3.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/bmp00001.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/bmp00002.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/bmp00003.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/bmp00004.bmp | Bin 0 -> 2166 bytes .../Plasma/Apps/plClient/res/bmp00005.bmp | Bin 0 -> 630 bytes .../Sources/Plasma/Apps/plClient/res/book.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/book_cli.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/book_hig.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/cnsl1.bin | 1 + .../Apps/plClient/res/cursor_clicked.bmp | Bin 0 -> 630 bytes .../Apps/plClient/res/cursor_disabled.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/cursor_down.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/cursor_grab.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/cursor_left.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/cursor_open.bmp | Bin 0 -> 630 bytes .../Apps/plClient/res/cursor_poised.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/cursor_right.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/cursor_u.bmp | Bin 0 -> 2102 bytes .../Plasma/Apps/plClient/res/cursor_up.bmp | Bin 0 -> 2102 bytes .../Apps/plClient/res/cursor_upward.bmp | Bin 0 -> 630 bytes .../Plasma/Apps/plClient/res/headspin.ico | Bin 0 -> 766 bytes .../Apps/plClient/res/plClient.exe.manifest | 22 + .../Plasma/Apps/plClient/res/plClient.rc | 380 + .../Plasma/Apps/plClient/res/resource.h | 103 + .../Apps/plClient/res/xLoading_Linking.01.jpg | Bin 0 -> 6506 bytes .../Apps/plClient/res/xLoading_Linking.02.jpg | Bin 0 -> 6493 bytes .../Apps/plClient/res/xLoading_Linking.03.jpg | Bin 0 -> 6464 bytes .../Apps/plClient/res/xLoading_Linking.04.jpg | Bin 0 -> 6515 bytes .../Apps/plClient/res/xLoading_Linking.05.jpg | Bin 0 -> 6437 bytes .../Apps/plClient/res/xLoading_Linking.06.jpg | Bin 0 -> 6566 bytes .../Apps/plClient/res/xLoading_Linking.07.jpg | Bin 0 -> 6414 bytes .../Apps/plClient/res/xLoading_Linking.08.jpg | Bin 0 -> 6526 bytes .../Apps/plClient/res/xLoading_Linking.09.jpg | Bin 0 -> 6440 bytes .../Apps/plClient/res/xLoading_Linking.10.jpg | Bin 0 -> 6501 bytes .../Apps/plClient/res/xLoading_Linking.11.jpg | Bin 0 -> 6450 bytes .../Apps/plClient/res/xLoading_Linking.12.jpg | Bin 0 -> 6510 bytes .../Apps/plClient/res/xLoading_Linking.13.jpg | Bin 0 -> 6393 bytes .../Apps/plClient/res/xLoading_Linking.14.jpg | Bin 0 -> 6547 bytes .../Apps/plClient/res/xLoading_Linking.15.jpg | Bin 0 -> 6432 bytes .../Apps/plClient/res/xLoading_Linking.16.jpg | Bin 0 -> 6495 bytes .../Apps/plClient/res/xLoading_Linking.17.jpg | Bin 0 -> 6466 bytes .../Apps/plClient/res/xLoading_Linking.18.jpg | Bin 0 -> 6519 bytes .../plClient/res/xLoading_Linking_Text.jpg | Bin 0 -> 30056 bytes .../plClient/res/xLoading_Updating_Text.jpg | Bin 0 -> 30050 bytes .../Sources/Plasma/Apps/plClient/winmain.cpp | 2032 +++ .../Plasma/Apps/plClientKey/DllMain.cpp | 35 + .../Plasma/Apps/plClientKey/plClientKey.cpp | 48 + .../Plasma/Apps/plClientKey/plClientKey.def | 5 + .../Plasma/Apps/plClientKey/plClientKey.h | 43 + .../Plasma/Apps/plClientPatcher/Intern.h | 42 + .../Sources/Plasma/Apps/plClientPatcher/Pch.h | 58 + .../Plasma/Apps/plClientPatcher/UruPlayer.cpp | 1023 ++ .../Plasma/Apps/plClientPatcher/UruPlayer.h | 51 + .../Apps/plClientPatcher/plLauncherCallback.h | 49 + .../Plasma/Apps/plFileEncrypt/main.cpp | 109 + .../Sources/Plasma/Apps/plFileSecure/main.cpp | 196 + .../Plasma/Apps/plLogDecrypt/plLogDecrypt.cpp | 110 + .../Sources/Plasma/Apps/plMD5/Intern.h | 35 + .../Sources/Plasma/Apps/plMD5/Main.cpp | 58 + .../Plasma20/Sources/Plasma/Apps/plMD5/Pch.h | 41 + .../Apps/plPageInfo/plAllCreatables.cpp | 54 + .../Plasma/Apps/plPageInfo/plPageInfo.cpp | 254 + .../Plasma/Apps/plPageOptimizer/main.cpp | 105 + .../Apps/plPageOptimizer/pfAllCreatables.cpp | 38 + .../Apps/plPageOptimizer/plPageOptimizer.cpp | 269 + .../Apps/plPageOptimizer/plPageOptimizer.h | 70 + .../Plasma/Apps/plPlasmaInstaller/Dirt.ICO | Bin 0 -> 2550 bytes .../Plasma/Apps/plPlasmaInstaller/main.cpp | 47 + .../Apps/plPlasmaInstaller/plInstallerReg.cpp | 97 + .../Apps/plPlasmaInstaller/plInstallerReg.h | 34 + .../plPlasmaInstaller/plPlasmaInstaller.cpp | 392 + .../plPlasmaInstaller/plPlasmaInstaller.h | 57 + .../plPlasmaInstaller/plPlasmaInstaller.rc | 129 + .../plPlasmaInstaller/plSetPlasmaPath.cpp | 104 + .../Apps/plPlasmaInstaller/plSetPlasmaPath.h | 26 + .../Plasma/Apps/plPlasmaInstaller/plUnzip.cpp | 153 + .../Plasma/Apps/plPlasmaInstaller/plUnzip.h | 50 + .../Plasma/Apps/plPlasmaInstaller/resource.h | 30 + .../Plasma/Apps/plPlasmaUpdate/Dirt.ICO | Bin 0 -> 2550 bytes .../Plasma/Apps/plPlasmaUpdate/main.cpp | 52 + .../Apps/plPlasmaUpdate/plFileGrabber.cpp | 170 + .../Apps/plPlasmaUpdate/plFileGrabber.h | 73 + .../Plasma/Apps/plPlasmaUpdate/plManifest.cpp | 346 + .../Plasma/Apps/plPlasmaUpdate/plManifest.h | 77 + .../Apps/plPlasmaUpdate/plPlasmaServers.cpp | 79 + .../Apps/plPlasmaUpdate/plPlasmaServers.h | 59 + .../Apps/plPlasmaUpdate/plPlasmaUpdate.cpp | 499 + .../Apps/plPlasmaUpdate/plPlasmaUpdate.h | 78 + .../Apps/plPlasmaUpdate/plPlasmaUpdate.rc | 138 + .../Plasma/Apps/plPlasmaUpdate/resource.h | 27 + .../Apps/plPythonPack/PythonInterface.cpp | 281 + .../Apps/plPythonPack/PythonInterface.h | 43 + .../Sources/Plasma/Apps/plPythonPack/main.cpp | 451 + .../Plasma/Apps/plUruLauncher/Dirt.ICO | Bin 0 -> 2238 bytes .../Plasma/Apps/plUruLauncher/Intern.h | 45 + .../Plasma/Apps/plUruLauncher/Main.cpp | 1121 ++ .../Sources/Plasma/Apps/plUruLauncher/Pch.h | 53 + .../Plasma/Apps/plUruLauncher/SelfPatcher.cpp | 314 + .../Plasma/Apps/plUruLauncher/banner.bmp | Bin 0 -> 71960 bytes .../Apps/plUruLauncher/plLauncherInfo.h | 87 + .../Apps/plUruLauncher/plUruLauncher.rc | 125 + .../Plasma/Apps/plUruLauncher/resource.h | 25 + .../Sources/Plasma/CoreLib/HS_POINT2.inc | 61 + .../Sources/Plasma/CoreLib/HeadSpin.cpp | 174 + .../Sources/Plasma/CoreLib/HeadSpin.h | 35 + .../Sources/Plasma/CoreLib/hsBiExpander.h | 477 + .../Sources/Plasma/CoreLib/hsBitVector.cpp | 205 + .../Sources/Plasma/CoreLib/hsBitVector.h | 394 + .../Sources/Plasma/CoreLib/hsBounds.cpp | 3108 ++++ .../Sources/Plasma/CoreLib/hsBounds.h | 423 + .../Sources/Plasma/CoreLib/hsColorRGBA.h | 171 + .../Sources/Plasma/CoreLib/hsConfig.h | 169 + .../Sources/Plasma/CoreLib/hsCritSect.cpp | 67 + .../Sources/Plasma/CoreLib/hsCritSect.h | 63 + .../Plasma/CoreLib/hsExceptionStack.cpp | 35 + .../Sources/Plasma/CoreLib/hsExceptionStack.h | 110 + .../Sources/Plasma/CoreLib/hsExceptions.h | 196 + .../Sources/Plasma/CoreLib/hsFastMath.cpp | 626 + .../Sources/Plasma/CoreLib/hsFastMath.h | 290 + .../Sources/Plasma/CoreLib/hsFixedTypes.h | 118 + .../Sources/Plasma/CoreLib/hsGeometry3.cpp | 117 + .../Sources/Plasma/CoreLib/hsGeometry3.h | 496 + .../Sources/Plasma/CoreLib/hsHashTable.h | 183 + .../Plasma20/Sources/Plasma/CoreLib/hsLOD.h | 40 + .../Sources/Plasma/CoreLib/hsMMIOStream.cpp | 89 + .../Sources/Plasma/CoreLib/hsMMIOStream.h | 56 + .../Sources/Plasma/CoreLib/hsMalloc.cpp | 40 + .../Sources/Plasma/CoreLib/hsMalloc.h | 167 + .../Sources/Plasma/CoreLib/hsMatrix33.cpp | 97 + .../Sources/Plasma/CoreLib/hsMatrix33.h | 56 + .../Sources/Plasma/CoreLib/hsMatrix44.cpp | 954 + .../Sources/Plasma/CoreLib/hsMatrix44.h | 170 + .../Sources/Plasma/CoreLib/hsMemory.cpp | 858 + .../Sources/Plasma/CoreLib/hsMemory.h | 235 + .../Sources/Plasma/CoreLib/hsPoint2.h | 121 + .../Sources/Plasma/CoreLib/hsQuat.cpp | 411 + .../Plasma20/Sources/Plasma/CoreLib/hsQuat.h | 100 + .../Plasma20/Sources/Plasma/CoreLib/hsQueue.h | 353 + .../Sources/Plasma/CoreLib/hsRefCnt.h | 51 + .../Sources/Plasma/CoreLib/hsSTLStream.cpp | 377 + .../Sources/Plasma/CoreLib/hsSTLStream.h | 113 + .../Sources/Plasma/CoreLib/hsSafeRefCnt.cpp | 28 + .../Sources/Plasma/CoreLib/hsSafeRefCnt.h | 49 + .../Sources/Plasma/CoreLib/hsScalar.h | 213 + .../Sources/Plasma/CoreLib/hsStlSortUtils.h | 49 + .../Sources/Plasma/CoreLib/hsStlUtils.cpp | 431 + .../Sources/Plasma/CoreLib/hsStlUtils.h | 373 + .../Sources/Plasma/CoreLib/hsStream.cpp | 1890 ++ .../Sources/Plasma/CoreLib/hsStream.h | 568 + .../Plasma/CoreLib/hsStringTokenizer.cpp | 293 + .../Plasma/CoreLib/hsStringTokenizer.h | 94 + .../Sources/Plasma/CoreLib/hsTempPointer.h | 158 + .../Sources/Plasma/CoreLib/hsTemplates.cpp | 320 + .../Sources/Plasma/CoreLib/hsTemplates.h | 1252 ++ .../Sources/Plasma/CoreLib/hsThread.cpp | 99 + .../Sources/Plasma/CoreLib/hsThread.h | 285 + .../Sources/Plasma/CoreLib/hsThread_Mac.cpp | 163 + .../Sources/Plasma/CoreLib/hsThread_Unix.cpp | 537 + .../Sources/Plasma/CoreLib/hsThread_Win.cpp | 218 + .../Plasma20/Sources/Plasma/CoreLib/hsTypes.h | 594 + .../Sources/Plasma/CoreLib/hsUtils.cpp | 752 + .../Plasma20/Sources/Plasma/CoreLib/hsUtils.h | 188 + .../Sources/Plasma/CoreLib/hsWide.cpp | 337 + .../Plasma20/Sources/Plasma/CoreLib/hsWide.h | 205 + .../Sources/Plasma/CoreLib/hsWindowHndl.h | 46 + .../Sources/Plasma/CoreLib/hsWindows.h | 36 + .../Plasma20/Sources/Plasma/CoreLib/notes.txt | 3 + .../Sources/Plasma/CoreLib/pcSmallRect.cpp | 49 + .../Sources/Plasma/CoreLib/pcSmallRect.h | 64 + .../Sources/Plasma/CoreLib/plGeneric.cpp | 187 + .../Sources/Plasma/CoreLib/plGeneric.h | 85 + .../Sources/Plasma/CoreLib/plLoadMask.cpp | 170 + .../Sources/Plasma/CoreLib/plLoadMask.h | 119 + .../Sources/Plasma/CoreLib/plQuality.h | 67 + .../Sources/Plasma/CoreLib/plRefCnt.h | 73 + .../Sources/Plasma/CoreLib/plRenderLevel.h | 89 + .../Plasma20/Sources/Plasma/CoreLib/plTweak.h | 60 + .../Plasma/CoreLib/plViewTransform.cpp | 373 + .../Sources/Plasma/CoreLib/plViewTransform.h | 326 + .../Sources/Plasma/CoreLibExe/Intern.h | 49 + .../Plasma20/Sources/Plasma/CoreLibExe/Pch.h | 56 + .../Sources/Plasma/CoreLibExe/hsExeError.cpp | 259 + .../Sources/Plasma/CoreLibExe/hsExeMalloc.cpp | 642 + .../Plasma/FeatureLib/inc/pfAllCreatables.h | 46 + .../pfAnimation/pfAnimationCreatable.h | 72 + .../pfAnimation/pfObjectFlocker.cpp | 1060 ++ .../FeatureLib/pfAnimation/pfObjectFlocker.h | 438 + .../pfAnimation/plAnimDebugList.cpp | 149 + .../FeatureLib/pfAnimation/plAnimDebugList.h | 47 + .../FeatureLib/pfAnimation/plBlower.cpp | 224 + .../Plasma/FeatureLib/pfAnimation/plBlower.h | 101 + .../pfAnimation/plFilterCoordInterface.cpp | 133 + .../pfAnimation/plFilterCoordInterface.h | 68 + .../FeatureLib/pfAnimation/plFollowMod.cpp | 282 + .../FeatureLib/pfAnimation/plFollowMod.h | 101 + .../pfAnimation/plLightModifier.cpp | 413 + .../FeatureLib/pfAnimation/plLightModifier.h | 181 + .../pfAnimation/plLineFollowMod.cpp | 687 + .../FeatureLib/pfAnimation/plLineFollowMod.h | 175 + .../pfAnimation/plRandomCommandMod.cpp | 290 + .../pfAnimation/plRandomCommandMod.h | 111 + .../FeatureLib/pfAnimation/plStereizer.cpp | 294 + .../FeatureLib/pfAnimation/plStereizer.h | 124 + .../pfAnimation/plViewFaceModifier.cpp | 432 + .../pfAnimation/plViewFaceModifier.h | 124 + .../FeatureLib/pfAudio/pfAudioCreatable.h | 40 + .../Plasma/FeatureLib/pfAudio/plListener.cpp | 312 + .../Plasma/FeatureLib/pfAudio/plListener.h | 82 + .../FeatureLib/pfAudio/plRandomSoundMod.cpp | 415 + .../FeatureLib/pfAudio/plRandomSoundMod.h | 89 + .../Plasma/FeatureLib/pfCCR/plCCRCreatable.h | 32 + .../Plasma/FeatureLib/pfCCR/plCCRMgr.cpp | 31 + .../Plasma/FeatureLib/pfCCR/plCCRMgr.h | 56 + .../Plasma/FeatureLib/pfCCR/plCCRVault.cpp | 25 + .../Plasma/FeatureLib/pfCCR/plCCRVault.h | 30 + .../FeatureLib/pfCamera/pfCameraCreatable.h | 54 + .../FeatureLib/pfCamera/pfCameraProxy.cpp | 68 + .../FeatureLib/pfCamera/pfCameraProxy.h | 51 + .../FeatureLib/pfCamera/plCameraBrain.cpp | 1846 ++ .../FeatureLib/pfCamera/plCameraBrain.h | 390 + .../FeatureLib/pfCamera/plCameraModifier.cpp | 478 + .../FeatureLib/pfCamera/plCameraModifier.h | 157 + .../pfCamera/plInterestingModifier.cpp | 72 + .../pfCamera/plInterestingModifier.h | 79 + .../FeatureLib/pfCamera/plVirtualCamNeu.cpp | 2085 +++ .../FeatureLib/pfCamera/plVirtualCamNeu.h | 235 + .../pfCharacter/pfCharacterCreatable.h | 35 + .../FeatureLib/pfCharacter/pfMarkerInfo.cpp | 250 + .../FeatureLib/pfCharacter/pfMarkerInfo.h | 84 + .../FeatureLib/pfCharacter/pfMarkerMgr.cpp | 313 + .../FeatureLib/pfCharacter/pfMarkerMgr.h | 97 + .../pfCharacter/plPlayerModifier.cpp | 610 + .../FeatureLib/pfCharacter/plPlayerModifier.h | 133 + .../pfConditional/plANDConditionalObject.cpp | 107 + .../pfConditional/plANDConditionalObject.h | 55 + .../plActivatorConditionalObject.cpp | 205 + .../plActivatorConditionalObject.h | 90 + .../plAnimationEventConditionalObject.cpp | 82 + .../plAnimationEventConditionalObject.h | 65 + .../plConditionalObjectCreatable.h | 84 + .../plControlEventConditionalObject.cpp | 70 + .../plControlEventConditionalObject.h | 60 + .../plFacingConditionalObject.cpp | 133 + .../pfConditional/plFacingConditionalObject.h | 64 + .../plKeyPressConditionalObject.cpp | 70 + .../plKeyPressConditionalObject.h | 59 + .../plLocalPlayerInBoxConditionalObject.cpp | 53 + .../plLocalPlayerInBoxConditionalObject.h | 57 + ...lPlayerIntersectPlaneConditionalObject.cpp | 52 + ...calPlayerIntersectPlaneConditionalObject.h | 58 + .../pfConditional/plORConditionalObject.cpp | 112 + .../pfConditional/plORConditionalObject.h | 60 + .../plObjectInBoxConditionalObject.cpp | 432 + .../plObjectInBoxConditionalObject.h | 116 + ...lObjectIntersectPlaneConditionalObject.cpp | 52 + .../plObjectIntersectPlaneConditionalObject.h | 58 + .../plPickedConditionalObject.cpp | 51 + .../pfConditional/plPickedConditionalObject.h | 54 + .../pfConsole/pfAvatarConsoleCommands.cpp | 924 + .../pfConsole/pfCCRConsoleCommands.cpp | 60 + .../Plasma/FeatureLib/pfConsole/pfConsole.cpp | 1214 ++ .../Plasma/FeatureLib/pfConsole/pfConsole.h | 149 + .../FeatureLib/pfConsole/pfConsoleCmd.cpp | 657 + .../FeatureLib/pfConsole/pfConsoleCmd.h | 272 + .../pfConsole/pfConsoleCommands.cpp | 7165 ++++++++ .../pfConsole/pfConsoleCommandsNet.cpp | 1057 ++ .../FeatureLib/pfConsole/pfConsoleContext.cpp | 276 + .../FeatureLib/pfConsole/pfConsoleContext.h | 95 + .../FeatureLib/pfConsole/pfConsoleCreatable.h | 36 + .../FeatureLib/pfConsole/pfConsoleDirSrc.cpp | 149 + .../FeatureLib/pfConsole/pfConsoleDirSrc.h | 89 + .../FeatureLib/pfConsole/pfConsoleEngine.cpp | 579 + .../FeatureLib/pfConsole/pfConsoleEngine.h | 100 + .../FeatureLib/pfConsole/pfDispatchLog.cpp | 313 + .../FeatureLib/pfConsole/pfDispatchLog.h | 59 + .../pfConsole/pfGameConsoleCommands.cpp | 451 + .../Sources/Plasma/FeatureLib/pfCsrSrv/Pch.h | 42 + .../Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.cpp | 145 + .../Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.h | 50 + .../pfGameGUIMgr/pfGUIButtonMod.cpp | 392 + .../FeatureLib/pfGameGUIMgr/pfGUIButtonMod.h | 121 + .../pfGameGUIMgr/pfGUICheckBoxCtrl.cpp | 206 + .../pfGameGUIMgr/pfGUICheckBoxCtrl.h | 95 + .../pfGameGUIMgr/pfGUIClickMapCtrl.cpp | 130 + .../pfGameGUIMgr/pfGUIClickMapCtrl.h | 89 + .../pfGameGUIMgr/pfGUIControlHandlers.cpp | 186 + .../pfGameGUIMgr/pfGUIControlHandlers.h | 180 + .../pfGameGUIMgr/pfGUIControlMod.cpp | 996 ++ .../FeatureLib/pfGameGUIMgr/pfGUIControlMod.h | 250 + .../pfGameGUIMgr/pfGUICtrlGenerator.cpp | 508 + .../pfGameGUIMgr/pfGUICtrlGenerator.h | 113 + .../pfGameGUIMgr/pfGUIDialogHandlers.h | 97 + .../pfGameGUIMgr/pfGUIDialogMod.cpp | 832 + .../FeatureLib/pfGameGUIMgr/pfGUIDialogMod.h | 198 + .../pfGameGUIMgr/pfGUIDialogNotifyProc.cpp | 131 + .../pfGameGUIMgr/pfGUIDialogNotifyProc.h | 66 + .../pfGameGUIMgr/pfGUIDragBarCtrl.cpp | 149 + .../pfGameGUIMgr/pfGUIDragBarCtrl.h | 77 + .../pfGameGUIMgr/pfGUIDraggableMod.cpp | 165 + .../pfGameGUIMgr/pfGUIDraggableMod.h | 90 + .../pfGameGUIMgr/pfGUIDynDisplayCtrl.cpp | 169 + .../pfGameGUIMgr/pfGUIDynDisplayCtrl.h | 97 + .../pfGameGUIMgr/pfGUIEditBoxMod.cpp | 650 + .../FeatureLib/pfGameGUIMgr/pfGUIEditBoxMod.h | 125 + .../FeatureLib/pfGameGUIMgr/pfGUIKnobCtrl.cpp | 368 + .../FeatureLib/pfGameGUIMgr/pfGUIKnobCtrl.h | 100 + .../pfGameGUIMgr/pfGUIListBoxMod.cpp | 1196 ++ .../FeatureLib/pfGameGUIMgr/pfGUIListBoxMod.h | 183 + .../pfGameGUIMgr/pfGUIListElement.cpp | 456 + .../pfGameGUIMgr/pfGUIListElement.h | 228 + .../FeatureLib/pfGameGUIMgr/pfGUIMenuItem.cpp | 463 + .../FeatureLib/pfGameGUIMgr/pfGUIMenuItem.h | 112 + .../pfGameGUIMgr/pfGUIMultiLineEditCtrl.cpp | 2037 +++ .../pfGameGUIMgr/pfGUIMultiLineEditCtrl.h | 273 + .../pfGameGUIMgr/pfGUIPopUpMenu.cpp | 940 + .../FeatureLib/pfGameGUIMgr/pfGUIPopUpMenu.h | 225 + .../pfGameGUIMgr/pfGUIProgressCtrl.cpp | 255 + .../pfGameGUIMgr/pfGUIProgressCtrl.h | 93 + .../pfGameGUIMgr/pfGUIRadioGroupCtrl.cpp | 257 + .../pfGameGUIMgr/pfGUIRadioGroupCtrl.h | 94 + .../FeatureLib/pfGameGUIMgr/pfGUITagDefs.cpp | 82 + .../FeatureLib/pfGameGUIMgr/pfGUITagDefs.h | 88 + .../pfGameGUIMgr/pfGUITextBoxMod.cpp | 246 + .../FeatureLib/pfGameGUIMgr/pfGUITextBoxMod.h | 93 + .../pfGameGUIMgr/pfGUIUpDownPairMod.cpp | 225 + .../pfGameGUIMgr/pfGUIUpDownPairMod.h | 84 + .../pfGameGUIMgr/pfGUIValueCtrl.cpp | 95 + .../FeatureLib/pfGameGUIMgr/pfGUIValueCtrl.h | 68 + .../FeatureLib/pfGameGUIMgr/pfGameGUIMgr.cpp | 943 + .../FeatureLib/pfGameGUIMgr/pfGameGUIMgr.h | 221 + .../pfGameGUIMgr/pfGameGUIMgrCreatable.h | 77 + .../pfGameMgr/BlueSpiral/pfGmBlueSpiral.cpp | 269 + .../pfGameMgr/BlueSpiral/pfGmBlueSpiral.h | 77 + .../ClimbingWall/pfGmClimbingWall.cpp | 346 + .../pfGameMgr/ClimbingWall/pfGmClimbingWall.h | 82 + .../FeatureLib/pfGameMgr/Heek/pfGmHeek.cpp | 341 + .../FeatureLib/pfGameMgr/Heek/pfGmHeek.h | 78 + .../Plasma/FeatureLib/pfGameMgr/Intern.h | 57 + .../pfGameMgr/Marker/pfGmMarker.cpp | 475 + .../FeatureLib/pfGameMgr/Marker/pfGmMarker.h | 85 + .../Sources/Plasma/FeatureLib/pfGameMgr/Pch.h | 57 + .../pfGameMgr/TicTacToe/pfGmTicTacToe.cpp | 277 + .../pfGameMgr/TicTacToe/pfGmTicTacToe.h | 77 + .../pfGameMgr/VarSync/pfGmVarSync.cpp | 311 + .../pfGameMgr/VarSync/pfGmVarSync.h | 80 + .../Plasma/FeatureLib/pfGameMgr/pfGameMgr.cpp | 657 + .../Plasma/FeatureLib/pfGameMgr/pfGameMgr.h | 256 + .../pfGameMgr/pfGameMgrCreatables.h | 49 + .../pfGameScoreMgr/pfGameScoreMgr.cpp | 458 + .../pfGameScoreMgr/pfGameScoreMgr.h | 137 + .../pfJournalBook/pfJournalBook.cpp | 3569 ++++ .../FeatureLib/pfJournalBook/pfJournalBook.h | 568 + .../pfJournalBook/pfJournalBookCreatable.h | 38 + .../Sources/Plasma/FeatureLib/pfKI/pfKI.cpp | 1362 ++ .../Sources/Plasma/FeatureLib/pfKI/pfKI.h | 82 + .../Plasma/FeatureLib/pfKI/pfKICreatable.h | 41 + .../FeatureLib/pfKI/pfPlayerBookMod.cpp | 142 + .../Plasma/FeatureLib/pfKI/pfPlayerBookMod.h | 83 + .../pfLocalizationDataMgr.cpp | 1408 ++ .../pfLocalizationMgr/pfLocalizationDataMgr.h | 141 + .../pfLocalizationMgr/pfLocalizationMgr.cpp | 91 + .../pfLocalizationMgr/pfLocalizationMgr.h | 62 + .../pfLocalizationMgr/pfLocalizedString.cpp | 368 + .../pfLocalizationMgr/pfLocalizedString.h | 102 + .../FeatureLib/pfLoginDialog/pfLoginDialog.rc | 110 + .../pfLoginDialog/plLoginDialog.cpp | 657 + .../FeatureLib/pfLoginDialog/plLoginDialog.h | 122 + .../FeatureLib/pfLoginDialog/resource.h | 24 + .../FeatureLib/pfMessage/pfBackdoorMsg.h | 84 + .../FeatureLib/pfMessage/pfGUINotifyMsg.h | 126 + .../FeatureLib/pfMessage/pfGameGUIMsg.h | 87 + .../Plasma/FeatureLib/pfMessage/pfKIMsg.cpp | 50 + .../Plasma/FeatureLib/pfMessage/pfKIMsg.h | 227 + .../FeatureLib/pfMessage/pfMarkerMsg.cpp | 70 + .../Plasma/FeatureLib/pfMessage/pfMarkerMsg.h | 57 + .../FeatureLib/pfMessage/pfMessageCreatable.h | 86 + .../FeatureLib/pfMessage/pfMovieEventMsg.cpp | 53 + .../FeatureLib/pfMessage/pfMovieEventMsg.h | 66 + .../pfMessage/plArmatureEffectMsg.cpp | 49 + .../pfMessage/plArmatureEffectMsg.h | 65 + .../FeatureLib/pfMessage/plClothingMsg.cpp | 114 + .../FeatureLib/pfMessage/plClothingMsg.h | 111 + .../Games/BlueSpiral/pyBlueSpiralGame.cpp | 69 + .../Games/BlueSpiral/pyBlueSpiralGame.h | 66 + .../Games/BlueSpiral/pyBlueSpiralGameGlue.cpp | 142 + .../Games/BlueSpiral/pyBlueSpiralMsg.cpp | 139 + .../Games/BlueSpiral/pyBlueSpiralMsg.h | 153 + .../Games/BlueSpiral/pyBlueSpiralMsgGlue.cpp | 281 + .../Games/ClimbingWall/pyClimbingWallGame.cpp | 114 + .../Games/ClimbingWall/pyClimbingWallGame.h | 72 + .../ClimbingWall/pyClimbingWallGameGlue.cpp | 196 + .../Games/ClimbingWall/pyClimbingWallMsg.cpp | 308 + .../Games/ClimbingWall/pyClimbingWallMsg.h | 187 + .../ClimbingWall/pyClimbingWallMsgGlue.cpp | 413 + .../pfPython/Games/Heek/pyHeekGame.cpp | 86 + .../pfPython/Games/Heek/pyHeekGame.h | 69 + .../pfPython/Games/Heek/pyHeekGameGlue.cpp | 207 + .../pfPython/Games/Heek/pyHeekMsg.cpp | 386 + .../pfPython/Games/Heek/pyHeekMsg.h | 283 + .../pfPython/Games/Heek/pyHeekMsgGlue.cpp | 618 + .../pfPython/Games/Marker/pyMarkerGame.cpp | 145 + .../pfPython/Games/Marker/pyMarkerGame.h | 75 + .../Games/Marker/pyMarkerGameGlue.cpp | 381 + .../pfPython/Games/Marker/pyMarkerMsg.cpp | 408 + .../pfPython/Games/Marker/pyMarkerMsg.h | 331 + .../pfPython/Games/Marker/pyMarkerMsgGlue.cpp | 706 + .../pfPython/Games/TicTacToe/pyTTTGame.cpp | 77 + .../pfPython/Games/TicTacToe/pyTTTGame.h | 68 + .../Games/TicTacToe/pyTTTGameGlue.cpp | 172 + .../pfPython/Games/TicTacToe/pyTTTMsg.cpp | 155 + .../pfPython/Games/TicTacToe/pyTTTMsg.h | 124 + .../pfPython/Games/TicTacToe/pyTTTMsgGlue.cpp | 231 + .../pfPython/Games/VarSync/pyVarSyncGame.cpp | 99 + .../pfPython/Games/VarSync/pyVarSyncGame.h | 69 + .../Games/VarSync/pyVarSyncGameGlue.cpp | 308 + .../pfPython/Games/VarSync/pyVarSyncMsg.cpp | 217 + .../pfPython/Games/VarSync/pyVarSyncMsg.h | 163 + .../Games/VarSync/pyVarSyncMsgGlue.cpp | 329 + .../FeatureLib/pfPython/Games/pyGameCli.cpp | 163 + .../FeatureLib/pfPython/Games/pyGameCli.h | 84 + .../pfPython/Games/pyGameCliGlue.cpp | 239 + .../pfPython/Games/pyGameCliMsg.cpp | 226 + .../FeatureLib/pfPython/Games/pyGameCliMsg.h | 161 + .../pfPython/Games/pyGameCliMsgGlue.cpp | 297 + .../pfPython/Games/pyGameMgrMsg.cpp | 146 + .../FeatureLib/pfPython/Games/pyGameMgrMsg.h | 109 + .../pfPython/Games/pyGameMgrMsgGlue.cpp | 203 + .../pfPython/cyAccountManagement.cpp | 125 + .../FeatureLib/pfPython/cyAccountManagement.h | 59 + .../pfPython/cyAccountManagementGlue.cpp | 230 + .../FeatureLib/pfPython/cyAnimation.cpp | 614 + .../Plasma/FeatureLib/pfPython/cyAnimation.h | 149 + .../FeatureLib/pfPython/cyAnimationGlue.cpp | 318 + .../Plasma/FeatureLib/pfPython/cyAvatar.cpp | 1964 +++ .../Plasma/FeatureLib/pfPython/cyAvatar.h | 555 + .../FeatureLib/pfPython/cyAvatarGlue.cpp | 886 + .../Plasma/FeatureLib/pfPython/cyCamera.cpp | 370 + .../Plasma/FeatureLib/pfPython/cyCamera.h | 107 + .../FeatureLib/pfPython/cyCameraGlue.cpp | 261 + .../Plasma/FeatureLib/pfPython/cyDraw.cpp | 105 + .../Plasma/FeatureLib/pfPython/cyDraw.h | 70 + .../Plasma/FeatureLib/pfPython/cyDrawGlue.cpp | 109 + .../FeatureLib/pfPython/cyInputInterface.cpp | 81 + .../FeatureLib/pfPython/cyInputInterface.h | 70 + .../pfPython/cyInputInterfaceGlue.cpp | 67 + .../Plasma/FeatureLib/pfPython/cyMisc.cpp | 2839 +++ .../Plasma/FeatureLib/pfPython/cyMisc.h | 920 + .../Plasma/FeatureLib/pfPython/cyMiscGlue.cpp | 522 + .../FeatureLib/pfPython/cyMiscGlue2.cpp | 514 + .../FeatureLib/pfPython/cyMiscGlue3.cpp | 762 + .../FeatureLib/pfPython/cyMiscGlue4.cpp | 838 + .../FeatureLib/pfPython/cyParticleSys.cpp | 148 + .../FeatureLib/pfPython/cyParticleSys.h | 84 + .../FeatureLib/pfPython/cyParticleSysGlue.cpp | 131 + .../Plasma/FeatureLib/pfPython/cyPhysics.cpp | 787 + .../Plasma/FeatureLib/pfPython/cyPhysics.h | 133 + .../FeatureLib/pfPython/cyPhysicsGlue.cpp | 407 + .../FeatureLib/pfPython/cyPythonInterface.cpp | 2060 +++ .../FeatureLib/pfPython/cyPythonInterface.h | 223 + .../FeatureLib/pfPython/pfPythonCreatable.h | 37 + .../FeatureLib/pfPython/plPythonFileMod.cpp | 2919 ++++ .../FeatureLib/pfPython/plPythonFileMod.h | 203 + .../FeatureLib/pfPython/plPythonHelpers.h | 60 + .../FeatureLib/pfPython/plPythonPack.cpp | 229 + .../Plasma/FeatureLib/pfPython/plPythonPack.h | 38 + .../FeatureLib/pfPython/plPythonParameter.h | 450 + .../pfPython/plPythonSDLModifier.cpp | 703 + .../FeatureLib/pfPython/plPythonSDLModifier.h | 141 + .../pfPython/plPythonSDLModifierGlue.cpp | 243 + .../FeatureLib/pfPython/pyAgeInfoStruct.cpp | 217 + .../FeatureLib/pfPython/pyAgeInfoStruct.h | 134 + .../pfPython/pyAgeInfoStructGlue.cpp | 436 + .../FeatureLib/pfPython/pyAgeLinkStruct.cpp | 159 + .../FeatureLib/pfPython/pyAgeLinkStruct.h | 122 + .../pfPython/pyAgeLinkStructGlue.cpp | 366 + .../Plasma/FeatureLib/pfPython/pyAgeVault.cpp | 329 + .../Plasma/FeatureLib/pfPython/pyAgeVault.h | 110 + .../FeatureLib/pfPython/pyAgeVaultGlue.cpp | 268 + .../Plasma/FeatureLib/pfPython/pyAlarm.cpp | 127 + .../Plasma/FeatureLib/pfPython/pyAlarm.h | 47 + .../FeatureLib/pfPython/pyAudioControl.cpp | 433 + .../FeatureLib/pfPython/pyAudioControl.h | 152 + .../pfPython/pyAudioControlGlue.cpp | 518 + .../Plasma/FeatureLib/pfPython/pyCCRMgr.cpp | 33 + .../Plasma/FeatureLib/pfPython/pyCCRMgr.h | 40 + .../FeatureLib/pfPython/pyCCRMgrGlue.cpp | 29 + .../FeatureLib/pfPython/pyCCRMgrGlue2.cpp | 28 + .../Plasma/FeatureLib/pfPython/pyCluster.cpp | 47 + .../Plasma/FeatureLib/pfPython/pyCluster.h | 66 + .../FeatureLib/pfPython/pyClusterGlue.cpp | 100 + .../Plasma/FeatureLib/pfPython/pyColor.cpp | 36 + .../Plasma/FeatureLib/pfPython/pyColor.h | 109 + .../FeatureLib/pfPython/pyColorGlue.cpp | 296 + .../FeatureLib/pfPython/pyCritterBrain.cpp | 256 + .../FeatureLib/pfPython/pyCritterBrain.h | 110 + .../pfPython/pyCritterBrainGlue.cpp | 527 + .../FeatureLib/pfPython/pyDniCoordinates.cpp | 112 + .../FeatureLib/pfPython/pyDniCoordinates.h | 73 + .../pfPython/pyDniCoordinatesGlue.cpp | 115 + .../FeatureLib/pfPython/pyDniInfoSource.cpp | 95 + .../FeatureLib/pfPython/pyDniInfoSource.h | 69 + .../pfPython/pyDniInfoSourceGlue.cpp | 86 + .../FeatureLib/pfPython/pyDrawControl.cpp | 171 + .../FeatureLib/pfPython/pyDrawControl.h | 71 + .../FeatureLib/pfPython/pyDrawControlGlue.cpp | 130 + .../FeatureLib/pfPython/pyDynamicText.cpp | 390 + .../FeatureLib/pfPython/pyDynamicText.h | 127 + .../FeatureLib/pfPython/pyDynamicTextGlue.cpp | 478 + .../Plasma/FeatureLib/pfPython/pyEnum.cpp | 719 + .../Plasma/FeatureLib/pfPython/pyEnum.h | 51 + .../FeatureLib/pfPython/pyGUIControl.cpp | 430 + .../Plasma/FeatureLib/pfPython/pyGUIControl.h | 117 + .../pfPython/pyGUIControlButton.cpp | 84 + .../FeatureLib/pfPython/pyGUIControlButton.h | 69 + .../pfPython/pyGUIControlButtonGlue.cpp | 125 + .../pfPython/pyGUIControlCheckBox.cpp | 79 + .../pfPython/pyGUIControlCheckBox.h | 68 + .../pfPython/pyGUIControlCheckBoxGlue.cpp | 108 + .../pfPython/pyGUIControlClickMap.cpp | 90 + .../pfPython/pyGUIControlClickMap.h | 71 + .../pfPython/pyGUIControlClickMapGlue.cpp | 107 + .../pfPython/pyGUIControlDragBar.cpp | 90 + .../FeatureLib/pfPython/pyGUIControlDragBar.h | 70 + .../pfPython/pyGUIControlDragBarGlue.cpp | 100 + .../pfPython/pyGUIControlDraggable.cpp | 76 + .../pfPython/pyGUIControlDraggable.h | 70 + .../pfPython/pyGUIControlDraggableGlue.cpp | 108 + .../pfPython/pyGUIControlDynamicText.cpp | 96 + .../pfPython/pyGUIControlDynamicText.h | 69 + .../pfPython/pyGUIControlDynamicTextGlue.cpp | 108 + .../pfPython/pyGUIControlEditBox.cpp | 242 + .../FeatureLib/pfPython/pyGUIControlEditBox.h | 87 + .../pfPython/pyGUIControlEditBoxGlue.cpp | 270 + .../FeatureLib/pfPython/pyGUIControlGlue.cpp | 369 + .../pfPython/pyGUIControlListBox.cpp | 1059 ++ .../FeatureLib/pfPython/pyGUIControlListBox.h | 130 + .../pfPython/pyGUIControlListBoxGlue.cpp | 552 + .../pfPython/pyGUIControlMultiLineEdit.cpp | 544 + .../pfPython/pyGUIControlMultiLineEdit.h | 103 + .../pyGUIControlMultiLineEditGlue.cpp | 460 + .../pfPython/pyGUIControlRadioGroup.cpp | 73 + .../pfPython/pyGUIControlRadioGroup.h | 66 + .../pfPython/pyGUIControlRadioGroupGlue.cpp | 108 + .../pfPython/pyGUIControlTextBox.cpp | 200 + .../FeatureLib/pfPython/pyGUIControlTextBox.h | 79 + .../pfPython/pyGUIControlTextBoxGlue.cpp | 225 + .../FeatureLib/pfPython/pyGUIControlValue.cpp | 209 + .../FeatureLib/pfPython/pyGUIControlValue.h | 133 + .../pfPython/pyGUIControlValueGlue.cpp | 356 + .../FeatureLib/pfPython/pyGUIDialog.cpp | 521 + .../Plasma/FeatureLib/pfPython/pyGUIDialog.h | 139 + .../FeatureLib/pfPython/pyGUIDialogGlue.cpp | 365 + .../FeatureLib/pfPython/pyGUIPopUpMenu.cpp | 390 + .../FeatureLib/pfPython/pyGUIPopUpMenu.h | 123 + .../pfPython/pyGUIPopUpMenuGlue.cpp | 430 + .../Plasma/FeatureLib/pfPython/pyGUISkin.cpp | 88 + .../Plasma/FeatureLib/pfPython/pyGUISkin.h | 76 + .../FeatureLib/pfPython/pyGUISkinGlue.cpp | 138 + .../FeatureLib/pfPython/pyGameScore.cpp | 121 + .../Plasma/FeatureLib/pfPython/pyGameScore.h | 74 + .../FeatureLib/pfPython/pyGameScoreGlue.cpp | 144 + .../FeatureLib/pfPython/pyGeometry3.cpp | 26 + .../Plasma/FeatureLib/pfPython/pyGeometry3.h | 124 + .../FeatureLib/pfPython/pyGeometry3Glue.cpp | 449 + .../FeatureLib/pfPython/pyGlueHelpers.h | 508 + .../FeatureLib/pfPython/pyGrassShader.cpp | 216 + .../FeatureLib/pfPython/pyGrassShader.h | 76 + .../FeatureLib/pfPython/pyGrassShaderGlue.cpp | 213 + .../Plasma/FeatureLib/pfPython/pyImage.cpp | 210 + .../Plasma/FeatureLib/pfPython/pyImage.h | 157 + .../FeatureLib/pfPython/pyImageGlue.cpp | 268 + .../FeatureLib/pfPython/pyJournalBook.cpp | 277 + .../FeatureLib/pfPython/pyJournalBook.h | 117 + .../FeatureLib/pfPython/pyJournalBookGlue.cpp | 352 + .../Plasma/FeatureLib/pfPython/pyKey.cpp | 231 + .../Plasma/FeatureLib/pfPython/pyKey.h | 126 + .../Plasma/FeatureLib/pfPython/pyKeyGlue.cpp | 198 + .../Plasma/FeatureLib/pfPython/pyKeyMap.cpp | 268 + .../Plasma/FeatureLib/pfPython/pyKeyMap.h | 90 + .../FeatureLib/pfPython/pyKeyMapGlue.cpp | 226 + .../FeatureLib/pfPython/pyMarkerMgr.cpp | 126 + .../Plasma/FeatureLib/pfPython/pyMarkerMgr.h | 76 + .../FeatureLib/pfPython/pyMarkerMgrGlue.cpp | 182 + .../Plasma/FeatureLib/pfPython/pyMatrix44.cpp | 116 + .../Plasma/FeatureLib/pfPython/pyMatrix44.h | 87 + .../FeatureLib/pfPython/pyMatrix44Glue.cpp | 438 + .../FeatureLib/pfPython/pyMoviePlayer.cpp | 186 + .../FeatureLib/pfPython/pyMoviePlayer.h | 79 + .../FeatureLib/pfPython/pyMoviePlayerGlue.cpp | 174 + .../FeatureLib/pfPython/pyNetLinkingMgr.cpp | 96 + .../FeatureLib/pfPython/pyNetLinkingMgr.h | 89 + .../pfPython/pyNetLinkingMgrGlue.cpp | 185 + .../pfPython/pyNetServerSessionInfo.cpp | 32 + .../pfPython/pyNetServerSessionInfo.h | 123 + .../pfPython/pyNetServerSessionInfoGlue.cpp | 355 + .../Plasma/FeatureLib/pfPython/pyNotify.cpp | 208 + .../Plasma/FeatureLib/pfPython/pyNotify.h | 98 + .../FeatureLib/pfPython/pyNotifyGlue.cpp | 399 + .../Plasma/FeatureLib/pfPython/pyPlayer.cpp | 97 + .../Plasma/FeatureLib/pfPython/pyPlayer.h | 103 + .../FeatureLib/pfPython/pyPlayerGlue.cpp | 213 + .../Plasma/FeatureLib/pfPython/pySDL.cpp | 267 + .../Plasma/FeatureLib/pfPython/pySDL.h | 135 + .../Plasma/FeatureLib/pfPython/pySDLGlue.cpp | 297 + .../FeatureLib/pfPython/pySceneObject.cpp | 979 ++ .../FeatureLib/pfPython/pySceneObject.h | 189 + .../FeatureLib/pfPython/pySceneObjectGlue.cpp | 605 + .../Plasma/FeatureLib/pfPython/pyScoreMgr.cpp | 338 + .../Plasma/FeatureLib/pfPython/pyScoreMgr.h | 99 + .../FeatureLib/pfPython/pyScoreMgrGlue.cpp | 219 + .../FeatureLib/pfPython/pySpawnPointInfo.cpp | 47 + .../FeatureLib/pfPython/pySpawnPointInfo.h | 103 + .../pfPython/pySpawnPointInfoGlue.cpp | 257 + .../FeatureLib/pfPython/pyStatusLog.cpp | 105 + .../Plasma/FeatureLib/pfPython/pyStatusLog.h | 76 + .../FeatureLib/pfPython/pyStatusLogGlue.cpp | 137 + .../Plasma/FeatureLib/pfPython/pyStream.cpp | 142 + .../Plasma/FeatureLib/pfPython/pyStream.h | 72 + .../FeatureLib/pfPython/pyStreamGlue.cpp | 172 + .../pfPython/pySwimCurrentInterface.cpp | 209 + .../pfPython/pySwimCurrentInterface.h | 75 + .../pfPython/pySwimCurrentInterfaceGlue.cpp | 218 + .../Plasma/FeatureLib/pfPython/pyVault.cpp | 694 + .../Plasma/FeatureLib/pfPython/pyVault.h | 175 + .../pfPython/pyVaultAgeInfoListNode.cpp | 79 + .../pfPython/pyVaultAgeInfoListNode.h | 73 + .../pfPython/pyVaultAgeInfoListNodeGlue.cpp | 123 + .../pfPython/pyVaultAgeInfoNode.cpp | 315 + .../FeatureLib/pfPython/pyVaultAgeInfoNode.h | 119 + .../pfPython/pyVaultAgeInfoNodeGlue.cpp | 298 + .../pfPython/pyVaultAgeLinkNode.cpp | 196 + .../FeatureLib/pfPython/pyVaultAgeLinkNode.h | 98 + .../pfPython/pyVaultAgeLinkNodeGlue.cpp | 211 + .../pfPython/pyVaultChronicleNode.cpp | 132 + .../pfPython/pyVaultChronicleNode.h | 81 + .../pfPython/pyVaultChronicleNodeGlue.cpp | 202 + .../FeatureLib/pfPython/pyVaultFolderNode.cpp | 120 + .../FeatureLib/pfPython/pyVaultFolderNode.h | 79 + .../pfPython/pyVaultFolderNodeGlue.cpp | 202 + .../FeatureLib/pfPython/pyVaultGlue.cpp | 594 + .../FeatureLib/pfPython/pyVaultImageNode.cpp | 236 + .../FeatureLib/pfPython/pyVaultImageNode.h | 89 + .../pfPython/pyVaultImageNodeGlue.cpp | 230 + .../pfPython/pyVaultMarkerGameNode.cpp | 95 + .../pfPython/pyVaultMarkerGameNode.h | 78 + .../pfPython/pyVaultMarkerGameNodeGlue.cpp | 125 + .../FeatureLib/pfPython/pyVaultNode.cpp | 829 + .../Plasma/FeatureLib/pfPython/pyVaultNode.h | 194 + .../FeatureLib/pfPython/pyVaultNodeGlue.cpp | 545 + .../FeatureLib/pfPython/pyVaultNodeRef.cpp | 150 + .../FeatureLib/pfPython/pyVaultNodeRef.h | 78 + .../pfPython/pyVaultNodeRefGlue.cpp | 134 + .../pfPython/pyVaultPlayerInfoListNode.cpp | 155 + .../pfPython/pyVaultPlayerInfoListNode.h | 79 + .../pyVaultPlayerInfoListNodeGlue.cpp | 189 + .../pfPython/pyVaultPlayerInfoNode.cpp | 180 + .../pfPython/pyVaultPlayerInfoNode.h | 84 + .../pfPython/pyVaultPlayerInfoNodeGlue.cpp | 175 + .../FeatureLib/pfPython/pyVaultPlayerNode.cpp | 316 + .../FeatureLib/pfPython/pyVaultPlayerNode.h | 108 + .../pfPython/pyVaultPlayerNodeGlue.cpp | 326 + .../FeatureLib/pfPython/pyVaultSDLNode.cpp | 108 + .../FeatureLib/pfPython/pyVaultSDLNode.h | 77 + .../pfPython/pyVaultSDLNodeGlue.cpp | 134 + .../FeatureLib/pfPython/pyVaultSystemNode.cpp | 51 + .../FeatureLib/pfPython/pyVaultSystemNode.h | 66 + .../pfPython/pyVaultSystemNodeGlue.cpp | 75 + .../pfPython/pyVaultTextNoteNode.cpp | 219 + .../FeatureLib/pfPython/pyVaultTextNoteNode.h | 87 + .../pfPython/pyVaultTextNoteNodeGlue.cpp | 323 + .../Plasma/FeatureLib/pfPython/pyWaveSet.cpp | 931 + .../Plasma/FeatureLib/pfPython/pyWaveSet.h | 203 + .../FeatureLib/pfPython/pyWaveSetGlue.cpp | 225 + .../pfSecurePreloader/pfSecurePreloader.cpp | 630 + .../pfSecurePreloader/pfSecurePreloader.h | 118 + .../pfSecurePreloaderCreatable.h | 41 + .../Plasma/FeatureLib/pfStackTrace/pfArray.h | 153 + .../FeatureLib/pfStackTrace/pfMapFile.cpp | 362 + .../FeatureLib/pfStackTrace/pfMapFile.h | 119 + .../pfStackTrace/pfMapFileEntry.cpp | 95 + .../FeatureLib/pfStackTrace/pfMapFileEntry.h | 89 + .../pfStackTrace/pfPrintStackTrace.cpp | 95 + .../pfStackTrace/pfPrintStackTrace.h | 51 + .../FeatureLib/pfStackTrace/pfStackTrace.cpp | 215 + .../FeatureLib/pfStackTrace/pfStackTrace.h | 74 + .../FeatureLib/pfStackTrace/pfTextFile.cpp | 286 + .../FeatureLib/pfStackTrace/pfTextFile.h | 127 + .../FeatureLib/pfSurface/pfSurfaceCreatable.h | 56 + .../FeatureLib/pfSurface/plDistOpacityMod.cpp | 296 + .../FeatureLib/pfSurface/plDistOpacityMod.h | 112 + .../FeatureLib/pfSurface/plFadeOpacityLay.cpp | 74 + .../FeatureLib/pfSurface/plFadeOpacityLay.h | 55 + .../FeatureLib/pfSurface/plFadeOpacityMod.cpp | 376 + .../FeatureLib/pfSurface/plFadeOpacityMod.h | 120 + .../FeatureLib/pfSurface/plGrabCubeMap.cpp | 150 + .../FeatureLib/pfSurface/plGrabCubeMap.h | 64 + .../FeatureLib/pfSurface/plLayerAVI.cpp | 232 + .../Plasma/FeatureLib/pfSurface/plLayerAVI.h | 58 + .../FeatureLib/pfSurface/plLayerMovie.cpp | 209 + .../FeatureLib/pfSurface/plLayerMovie.h | 89 + .../Plasma/NucleusLib/inc/hsGMatState.h | 253 + .../Plasma/NucleusLib/inc/hsGMatState.inl | 53 + .../Sources/Plasma/NucleusLib/inc/hsResMgr.h | 141 + .../Sources/Plasma/NucleusLib/inc/hsTimer.h | 156 + .../Sources/Plasma/NucleusLib/inc/plAudible.h | 102 + .../Plasma/NucleusLib/inc/plCCRMgrBase.h | 44 + .../NucleusLib/inc/plClassIndexMacros.h | 58 + .../Plasma/NucleusLib/inc/plCreatableIndex.h | 936 + .../NucleusLib/inc/plCreatableStrings.h | 78 + .../Plasma/NucleusLib/inc/plDrawable.h | 186 + .../plNetServerDatabaseStructClassIndexes.h | 25 + .../Plasma/NucleusLib/inc/plPhysical.h | 116 + .../Plasma/NucleusLib/inc/plPipeResReq.h | 53 + .../Plasma/NucleusLib/inc/plPipeline.h | 346 + .../Sources/Plasma/NucleusLib/inc/plProfile.h | 236 + .../NucleusLib/inc/plProfileManager.cpp | 519 + .../Plasma/NucleusLib/inc/plProfileManager.h | 96 + .../Plasma/NucleusLib/inc/plRefFlags.h | 37 + .../Plasma/NucleusLib/inc/plgDispatch.h | 65 + .../Plasma/NucleusLib/inc/pnAllCreatables.h | 33 + .../NucleusLib/inc/pnNucleusCreatables.h | 56 + .../Plasma/NucleusLib/inc/pnSingletons.cpp | 68 + .../NucleusLib/pnAddrInfo/pnAddrInfo.cpp | 161 + .../Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.h | 69 + .../Plasma/NucleusLib/pnAsyncCore/Pch.h | 41 + .../pnAsyncCore/Private/pnAcAllIncludes.h | 42 + .../NucleusLib/pnAsyncCore/Private/pnAcCore.h | 97 + .../NucleusLib/pnAsyncCore/Private/pnAcIo.h | 475 + .../pnAsyncCore/Private/pnAcLog.cpp | 174 + .../NucleusLib/pnAsyncCore/Private/pnAcLog.h | 111 + .../pnAsyncCore/Private/pnAcThread.h | 130 + .../pnAsyncCore/Private/pnAcTimer.h | 89 + .../NucleusLib/pnAsyncCore/pnAsyncCore.h | 46 + .../Plasma/NucleusLib/pnAsyncCoreExe/Pch.h | 52 + .../pnAsyncCoreExe/Private/Nt/pnAceNt.cpp | 487 + .../pnAsyncCoreExe/Private/Nt/pnAceNt.h | 48 + .../pnAsyncCoreExe/Private/Nt/pnAceNtFile.cpp | 994 ++ .../pnAsyncCoreExe/Private/Nt/pnAceNtInt.h | 320 + .../Private/Nt/pnAceNtSocket.cpp | 1467 ++ .../Private/Nt/pnAceNtThread.cpp | 52 + .../pnAsyncCoreExe/Private/Unix/pnAceUx.h | 48 + .../pnAsyncCoreExe/Private/W9x/pnAceW9x.cpp | 75 + .../pnAsyncCoreExe/Private/W9x/pnAceW9x.h | 48 + .../Private/W9x/pnAceW9xFile.cpp | 502 + .../pnAsyncCoreExe/Private/W9x/pnAceW9xInt.h | 181 + .../Private/W9x/pnAceW9xSocket.cpp | 1221 ++ .../Private/W9x/pnAceW9xThread.cpp | 437 + .../Private/Win32/pnAceW32Dns.cpp | 384 + .../Private/Win32/pnAceW32Thread.cpp | 264 + .../pnAsyncCoreExe/Private/pnAceInt.h | 264 + .../NucleusLib/pnAsyncCoreExe/pnAceCore.cpp | 230 + .../NucleusLib/pnAsyncCoreExe/pnAceIo.cpp | 590 + .../NucleusLib/pnAsyncCoreExe/pnAceLog.cpp | 478 + .../NucleusLib/pnAsyncCoreExe/pnAceThread.cpp | 120 + .../NucleusLib/pnAsyncCoreExe/pnAceTimer.cpp | 352 + .../Sources/Plasma/NucleusLib/pnCrash/Pch.h | 38 + .../Plasma/NucleusLib/pnCrash/pnCrash.bat | 6 + .../Plasma/NucleusLib/pnCrash/pnCrash.cpp | 33 + .../Plasma/NucleusLib/pnCrash/pnCrash.h | 83 + .../Plasma/NucleusLib/pnCrash/pnCrash.py | 453 + .../Plasma/NucleusLib/pnCrashExe/Pch.h | 49 + .../NucleusLib/pnCrashExe/Win32/W32Int.h | 131 + .../pnCrashExe/Win32/pnCreError.cpp | 1067 ++ .../NucleusLib/pnCrashExe/Win32/pnCreGui.cpp | 33 + .../NucleusLib/pnCrashExe/Win32/pnCreMail.cpp | 198 + .../pnCrashExe/Win32/pnCreTools.cpp | 185 + .../Sources/Plasma/NucleusLib/pnCsrCli/Pch.h | 40 + .../Plasma/NucleusLib/pnCsrCli/pnCsrCli.cpp | 253 + .../Plasma/NucleusLib/pnCsrCli/pnCsrCli.h | 67 + .../Sources/Plasma/NucleusLib/pnCsrNet/Pch.h | 38 + .../Plasma/NucleusLib/pnCsrNet/pnCsrNet.cpp | 33 + .../Plasma/NucleusLib/pnCsrNet/pnCsrNet.h | 72 + .../NucleusLib/pnDispatch/plDispatch.cpp | 630 + .../Plasma/NucleusLib/pnDispatch/plDispatch.h | 138 + .../pnDispatch/plDispatchLogBase.cpp | 29 + .../NucleusLib/pnDispatch/plDispatchLogBase.h | 73 + .../pnDispatch/pnDispatchCreatable.h | 36 + .../Plasma/NucleusLib/pnFactory/plCreatable.h | 285 + .../Plasma/NucleusLib/pnFactory/plCreator.h | 171 + .../Plasma/NucleusLib/pnFactory/plFactory.cpp | 341 + .../Plasma/NucleusLib/pnFactory/plFactory.h | 96 + .../pnGameMgr/BlueSpiral/pnGmBlueSpiral.cpp | 33 + .../pnGameMgr/BlueSpiral/pnGmBlueSpiral.h | 122 + .../ClimbingWall/pnGmClimbingWall.cpp | 33 + .../pnGameMgr/ClimbingWall/pnGmClimbingWall.h | 168 + .../NucleusLib/pnGameMgr/Heek/pnGmHeek.cpp | 33 + .../NucleusLib/pnGameMgr/Heek/pnGmHeek.h | 187 + .../Plasma/NucleusLib/pnGameMgr/Intern.h | 35 + .../pnGameMgr/Marker/pnGmMarker.cpp | 33 + .../NucleusLib/pnGameMgr/Marker/pnGmMarker.h | 213 + .../Sources/Plasma/NucleusLib/pnGameMgr/Pch.h | 43 + .../pnGameMgr/TicTacToe/pnGmTicTacToe.cpp | 33 + .../pnGameMgr/TicTacToe/pnGmTicTacToe.h | 120 + .../pnGameMgr/VarSync/pnGmVarSync.cpp | 33 + .../pnGameMgr/VarSync/pnGmVarSync.h | 141 + .../Plasma/NucleusLib/pnGameMgr/pnGameMgr.cpp | 33 + .../Plasma/NucleusLib/pnGameMgr/pnGameMgr.h | 252 + .../Sources/Plasma/NucleusLib/pnIni/Intern.h | 85 + .../Sources/Plasma/NucleusLib/pnIni/Pch.h | 47 + .../pnIni/Private/pnIniAllIncludes.h | 40 + .../NucleusLib/pnIni/Private/pnIniChange.h | 65 + .../NucleusLib/pnIni/Private/pnIniCore.cpp | 770 + .../NucleusLib/pnIni/Private/pnIniCore.h | 154 + .../NucleusLib/pnIni/Private/pnIniSrv.cpp | 40 + .../NucleusLib/pnIni/Private/pnIniSrv.h | 61 + .../Sources/Plasma/NucleusLib/pnIni/pnIni.h | 37 + .../Sources/Plasma/NucleusLib/pnIniExe/Pch.h | 52 + .../pnIniExe/Private/Win32/pnW32IniChange.cpp | 381 + .../NucleusLib/pnIniExe/Private/pnIniSrv.cpp | 202 + .../pnInputCore/plControlDefinition.h | 133 + .../pnInputCore/plControlEventCodes.h | 146 + .../NucleusLib/pnInputCore/plInputMap.cpp | 1096 ++ .../NucleusLib/pnInputCore/plInputMap.h | 51 + .../Plasma/NucleusLib/pnInputCore/plKeyDef.h | 157 + .../Plasma/NucleusLib/pnInputCore/plKeyMap.h | 223 + .../Plasma/NucleusLib/pnInputCore/plOSMsg.h | 78 + .../pnKeyedObject/hsKeyedObject.cpp | 175 + .../NucleusLib/pnKeyedObject/hsKeyedObject.h | 89 + .../NucleusLib/pnKeyedObject/plFixedKey.cpp | 165 + .../NucleusLib/pnKeyedObject/plFixedKey.h | 78 + .../Plasma/NucleusLib/pnKeyedObject/plKey.cpp | 286 + .../Plasma/NucleusLib/pnKeyedObject/plKey.h | 141 + .../NucleusLib/pnKeyedObject/plKeyImp.cpp | 649 + .../NucleusLib/pnKeyedObject/plKeyImp.h | 161 + .../pnKeyedObject/plMsgForwarder.cpp | 208 + .../NucleusLib/pnKeyedObject/plMsgForwarder.h | 63 + .../NucleusLib/pnKeyedObject/plReceiver.h | 45 + .../NucleusLib/pnKeyedObject/plTempKey.cpp | 59 + .../NucleusLib/pnKeyedObject/plTempKey.h | 51 + .../NucleusLib/pnKeyedObject/plUoid.cpp | 268 + .../Plasma/NucleusLib/pnKeyedObject/plUoid.h | 179 + .../pnKeyedObject/pnKeyedObjectCreatable.h | 43 + .../Sources/Plasma/NucleusLib/pnMail/Pch.h | 44 + .../Plasma/NucleusLib/pnMail/pnMail.cpp | 714 + .../Sources/Plasma/NucleusLib/pnMail/pnMail.h | 96 + .../Plasma/NucleusLib/pnMessage/plAttachMsg.h | 50 + .../NucleusLib/pnMessage/plAudioSysMsg.h | 93 + .../NucleusLib/pnMessage/plCameraMsg.cpp | 157 + .../Plasma/NucleusLib/pnMessage/plCameraMsg.h | 245 + .../NucleusLib/pnMessage/plClientMsg.cpp | 50 + .../Plasma/NucleusLib/pnMessage/plClientMsg.h | 153 + .../NucleusLib/pnMessage/plCmdIfaceModMsg.h | 87 + .../NucleusLib/pnMessage/plCollisionMsg.h | 50 + .../NucleusLib/pnMessage/plCorrectionMsg.h | 70 + .../NucleusLib/pnMessage/plCursorChangeMsg.h | 91 + .../NucleusLib/pnMessage/plDISpansMsg.h | 60 + .../Plasma/NucleusLib/pnMessage/plEnableMsg.h | 114 + .../NucleusLib/pnMessage/plEnvEffectMsg.cpp | 141 + .../NucleusLib/pnMessage/plEnvEffectMsg.h | 214 + .../NucleusLib/pnMessage/plEventCallbackMsg.h | 118 + .../NucleusLib/pnMessage/plFakeOutMsg.h | 79 + .../Plasma/NucleusLib/pnMessage/plIntRefMsg.h | 79 + .../Plasma/NucleusLib/pnMessage/plMessage.cpp | 375 + .../Plasma/NucleusLib/pnMessage/plMessage.h | 196 + .../pnMessage/plMessageWithCallbacks.cpp | 138 + .../pnMessage/plMessageWithCallbacks.h | 73 + .../NucleusLib/pnMessage/plMultiModMsg.h | 74 + .../NucleusLib/pnMessage/plNodeChangeMsg.cpp | 45 + .../NucleusLib/pnMessage/plNodeChangeMsg.h | 52 + .../NucleusLib/pnMessage/plNodeRefMsg.h | 53 + .../NucleusLib/pnMessage/plNotifyMsg.cpp | 1759 ++ .../Plasma/NucleusLib/pnMessage/plNotifyMsg.h | 386 + .../Plasma/NucleusLib/pnMessage/plObjRefMsg.h | 73 + .../NucleusLib/pnMessage/plPipeResMakeMsg.h | 93 + .../NucleusLib/pnMessage/plPlayerPageMsg.h | 78 + .../NucleusLib/pnMessage/plProxyDrawMsg.cpp | 65 + .../NucleusLib/pnMessage/plProxyDrawMsg.h | 82 + .../Plasma/NucleusLib/pnMessage/plRefMsg.cpp | 99 + .../Plasma/NucleusLib/pnMessage/plRefMsg.h | 93 + .../pnMessage/plRemoteAvatarInfoMsg.h | 70 + .../NucleusLib/pnMessage/plSDLModifierMsg.cpp | 46 + .../NucleusLib/pnMessage/plSDLModifierMsg.h | 84 + .../pnMessage/plSDLNotificationMsg.h | 53 + .../NucleusLib/pnMessage/plSatisfiedMsg.h | 46 + .../NucleusLib/pnMessage/plSelfDestructMsg.h | 50 + .../NucleusLib/pnMessage/plServerReplyMsg.cpp | 68 + .../NucleusLib/pnMessage/plServerReplyMsg.h | 70 + .../NucleusLib/pnMessage/plSetNetGroupIDMsg.h | 45 + .../NucleusLib/pnMessage/plSharedStateMsg.h | 57 + .../NucleusLib/pnMessage/plSimulationMsg.cpp | 37 + .../NucleusLib/pnMessage/plSimulationMsg.h | 51 + .../pnMessage/plSimulationSynchMsg.h | 62 + .../NucleusLib/pnMessage/plSingleModMsg.h | 74 + .../NucleusLib/pnMessage/plSoundMsg.cpp | 78 + .../Plasma/NucleusLib/pnMessage/plSoundMsg.h | 106 + .../Plasma/NucleusLib/pnMessage/plTimeMsg.cpp | 80 + .../Plasma/NucleusLib/pnMessage/plTimeMsg.h | 118 + .../Plasma/NucleusLib/pnMessage/plWarpMsg.h | 84 + .../NucleusLib/pnMessage/pnMessageCreatable.h | 183 + .../pnModifier/plConditionalObject.cpp | 48 + .../pnModifier/plConditionalObject.h | 89 + .../NucleusLib/pnModifier/plLogicModBase.cpp | 387 + .../NucleusLib/pnModifier/plLogicModBase.h | 112 + .../NucleusLib/pnModifier/plModifier.cpp | 86 + .../Plasma/NucleusLib/pnModifier/plModifier.h | 76 + .../NucleusLib/pnModifier/plMultiModifier.cpp | 63 + .../NucleusLib/pnModifier/plMultiModifier.h | 65 + .../pnModifier/plSingleModifier.cpp | 55 + .../NucleusLib/pnModifier/plSingleModifier.h | 70 + .../pnModifier/pnModifierCreatable.h | 54 + .../Plasma/NucleusLib/pnNetBase/Intern.h | 38 + .../Sources/Plasma/NucleusLib/pnNetBase/Pch.h | 41 + .../NucleusLib/pnNetBase/Private/pnNbAges.cpp | 25 + .../NucleusLib/pnNetBase/Private/pnNbAges.h | 35 + .../pnNetBase/Private/pnNbAllIncludes.h | 43 + .../pnNetBase/Private/pnNbEchoMsgs.h | 153 + .../pnNetBase/Private/pnNbError.cpp | 176 + .../NucleusLib/pnNetBase/Private/pnNbError.h | 116 + .../pnNetBase/Private/pnNbProtocol.cpp | 76 + .../pnNetBase/Private/pnNbProtocol.h | 76 + .../NucleusLib/pnNetBase/Private/pnNbSrvs.cpp | 262 + .../NucleusLib/pnNetBase/Private/pnNbSrvs.h | 90 + .../NucleusLib/pnNetBase/pnNbAuthKey.hpp | 58 + .../Plasma/NucleusLib/pnNetBase/pnNbConst.h | 152 + .../NucleusLib/pnNetBase/pnNbCsrKey.hpp | 58 + .../NucleusLib/pnNetBase/pnNbGameKey.hpp | 58 + .../pnNetBase/pnNbGateKeeperKey.hpp | 57 + .../Plasma/NucleusLib/pnNetBase/pnNetBase.h | 45 + .../Plasma/NucleusLib/pnNetCli/Intern.h | 114 + .../Sources/Plasma/NucleusLib/pnNetCli/Pch.h | 45 + .../NucleusLib/pnNetCli/pnNcChannel.cpp | 400 + .../Plasma/NucleusLib/pnNetCli/pnNcCli.cpp | 1035 ++ .../NucleusLib/pnNetCli/pnNcEncrypt.cpp | 135 + .../Plasma/NucleusLib/pnNetCli/pnNcUtils.cpp | 84 + .../Plasma/NucleusLib/pnNetCli/pnNetCli.h | 423 + .../NucleusLib/pnNetCommon/plGenericVar.cpp | 345 + .../NucleusLib/pnNetCommon/plGenericVar.h | 181 + .../NucleusLib/pnNetCommon/plNetAddress.cpp | 149 + .../NucleusLib/pnNetCommon/plNetAddress.h | 86 + .../NucleusLib/pnNetCommon/plNetApp.cpp | 153 + .../Plasma/NucleusLib/pnNetCommon/plNetApp.h | 245 + .../NucleusLib/pnNetCommon/plNetCommonStats.h | 44 + .../NucleusLib/pnNetCommon/plNetGroup.cpp | 32 + .../NucleusLib/pnNetCommon/plNetGroup.h | 77 + .../NucleusLib/pnNetCommon/plNetServers.cpp | 128 + .../NucleusLib/pnNetCommon/plNetServers.h | 148 + .../pnNetCommon/plNetSharedState.cpp | 99 + .../NucleusLib/pnNetCommon/plNetSharedState.h | 70 + .../NucleusLib/pnNetCommon/plSDLTypes.cpp | 39 + .../NucleusLib/pnNetCommon/plSDLTypes.h | 42 + .../pnNetCommon/plSynchedObject.cpp | 600 + .../NucleusLib/pnNetCommon/plSynchedObject.h | 270 + .../NucleusLib/pnNetCommon/plSynchedValue.cpp | 153 + .../NucleusLib/pnNetCommon/plSynchedValue.h | 396 + .../NucleusLib/pnNetCommon/pnNetCommon.cpp | 108 + .../NucleusLib/pnNetCommon/pnNetCommon.h | 131 + .../pnNetCommon/pnNetCommonCreatable.h | 47 + .../Plasma/NucleusLib/pnNetDiag/Intern.h | 127 + .../Sources/Plasma/NucleusLib/pnNetDiag/Pch.h | 52 + .../Plasma/NucleusLib/pnNetDiag/pnNdDns.cpp | 180 + .../Plasma/NucleusLib/pnNetDiag/pnNdIcmp.cpp | 246 + .../Plasma/NucleusLib/pnNetDiag/pnNdTcp.cpp | 851 + .../Plasma/NucleusLib/pnNetDiag/pnNetDiag.cpp | 151 + .../Plasma/NucleusLib/pnNetDiag/pnNetDiag.h | 129 + .../Plasma/NucleusLib/pnNetDiag/pnNetSys.cpp | 161 + .../Plasma/NucleusLib/pnNetLog/Intern.h | 67 + .../Sources/Plasma/NucleusLib/pnNetLog/Pch.h | 58 + .../Plasma/NucleusLib/pnNetLog/pnNetLog.h | 148 + .../Plasma/NucleusLib/pnNetLog/pnNlApi.cpp | 215 + .../Plasma/NucleusLib/pnNetLog/pnNlCli.cpp | 542 + .../Plasma/NucleusLib/pnNetLog/pnNlSrv.cpp | 587 + .../Plasma/NucleusLib/pnNetProtocol/Intern.h | 36 + .../Plasma/NucleusLib/pnNetProtocol/Pch.h | 52 + .../Protocols/Cli2Auth/pnNpCli2Auth.cpp | 705 + .../Private/Protocols/Cli2Auth/pnNpCli2Auth.h | 1033 ++ .../Private/Protocols/Cli2Csr/pnNpCli2Csr.cpp | 106 + .../Private/Protocols/Cli2Csr/pnNpCli2Csr.h | 162 + .../Protocols/Cli2File/pnNpCli2File.cpp | 34 + .../Private/Protocols/Cli2File/pnNpCli2File.h | 195 + .../Protocols/Cli2Game/pnNpCli2Game.cpp | 101 + .../Private/Protocols/Cli2Game/pnNpCli2Game.h | 178 + .../Cli2GateKeeper/pnNpCli2GateKeeper.cpp | 94 + .../Cli2GateKeeper/pnNpCli2GateKeeper.h | 141 + .../Private/Protocols/Srv2Db/pnNpSrv2Db.cpp | 77 + .../Private/Protocols/Srv2Db/pnNpSrv2Db.h | 553 + .../Private/Protocols/Srv2Log/pnNpSrv2Log.cpp | 74 + .../Private/Protocols/Srv2Log/pnNpSrv2Log.h | 104 + .../Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.cpp | 71 + .../Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.h | 251 + .../Protocols/Srv2Score/pnNpSrv2Score.cpp | 72 + .../Protocols/Srv2Score/pnNpSrv2Score.h | 169 + .../Protocols/Srv2State/pnNpSrv2State.cpp | 72 + .../Protocols/Srv2State/pnNpSrv2State.h | 131 + .../Protocols/Srv2Vault/pnNpSrv2Vault.cpp | 71 + .../Protocols/Srv2Vault/pnNpSrv2Vault.h | 452 + .../pnNetProtocol/Private/pnNpAllIncludes.h | 104 + .../pnNetProtocol/Private/pnNpCommon.cpp | 1201 ++ .../pnNetProtocol/Private/pnNpCommon.h | 483 + .../NucleusLib/pnNetProtocol/pnNetProtocol.h | 39 + .../Plasma/NucleusLib/pnOraLib/Intern.h | 35 + .../Sources/Plasma/NucleusLib/pnOraLib/Pch.h | 56 + .../Plasma/NucleusLib/pnOraLib/pnOraLib.cpp | 552 + .../Plasma/NucleusLib/pnOraLib/pnOraLib.h | 128 + .../Sources/Plasma/NucleusLib/pnProduct/Pch.h | 40 + .../pnProduct/Private/pnPrAllIncludes.h | 44 + .../pnProduct/Private/pnPrBranchId.cpp | 67 + .../pnProduct/Private/pnPrBranchId.h | 44 + .../pnProduct/Private/pnPrBuildId.cpp | 71 + .../pnProduct/Private/pnPrBuildId.h | 45 + .../pnProduct/Private/pnPrBuildString.cpp | 51 + .../pnProduct/Private/pnPrBuildString.h | 45 + .../pnProduct/Private/pnPrBuildType.cpp | 83 + .../pnProduct/Private/pnPrBuildType.h | 62 + .../pnProduct/Private/pnPrProductId.cpp | 125 + .../pnProduct/Private/pnPrProductId.h | 78 + .../Plasma/NucleusLib/pnProduct/pnProduct.h | 39 + .../pnSceneObject/plAudioInterface.cpp | 361 + .../pnSceneObject/plAudioInterface.h | 94 + .../pnSceneObject/plCoordinateInterface.cpp | 644 + .../pnSceneObject/plCoordinateInterface.h | 187 + .../pnSceneObject/plDrawInterface.cpp | 371 + .../pnSceneObject/plDrawInterface.h | 108 + .../pnSceneObject/plObjInterface.cpp | 116 + .../NucleusLib/pnSceneObject/plObjInterface.h | 103 + .../pnSceneObject/plSceneObject.cpp | 870 + .../NucleusLib/pnSceneObject/plSceneObject.h | 165 + .../pnSceneObject/plSimulationInterface.cpp | 193 + .../pnSceneObject/plSimulationInterface.h | 97 + .../pnSceneObject/pnSceneObjectCreatable.h | 57 + .../Plasma/NucleusLib/pnSimpleNet/Pch.h | 38 + .../NucleusLib/pnSimpleNet/pnSimpleNet.cpp | 631 + .../NucleusLib/pnSimpleNet/pnSimpleNet.h | 167 + .../Plasma/NucleusLib/pnSqlLib/Intern.h | 35 + .../Sources/Plasma/NucleusLib/pnSqlLib/Pch.h | 46 + .../pnSqlLib/Private/pnSqlAllIncludes.h | 39 + .../NucleusLib/pnSqlLib/Private/pnSqlConn.cpp | 1423 ++ .../NucleusLib/pnSqlLib/Private/pnSqlConn.h | 292 + .../NucleusLib/pnSqlLib/Private/pnSqlUtil.cpp | 180 + .../NucleusLib/pnSqlLib/Private/pnSqlUtil.h | 70 + .../Plasma/NucleusLib/pnSqlLib/pnSqlLib.h | 43 + .../Plasma/NucleusLib/pnTimer/hsTimer.cpp | 405 + .../NucleusLib/pnTimer/plTimedValue.cpp | 31 + .../Plasma/NucleusLib/pnTimer/plTimedValue.h | 155 + .../pnTimer/plTimerCallbackManager.cpp | 183 + .../pnTimer/plTimerCallbackManager.h | 95 + .../NucleusLib/pnTimer/pnBranchDate.cpp | 28 + .../NucleusLib/pnTimer/pnBuildDates.cpp | 78 + .../Plasma/NucleusLib/pnTimer/pnBuildDates.h | 55 + .../NucleusLib/pnTimer/pnTimerCreatable.h | 38 + .../Plasma/NucleusLib/pnUtils/Intern.h | 77 + .../Sources/Plasma/NucleusLib/pnUtils/Pch.h | 52 + .../pnUtils/Private/Unix/pnUtUxStr.cpp | 46 + .../pnUtils/Private/Unix/pnUtUxSync.cpp | 45 + .../pnUtils/Private/Unix/pnUtUxUuid.cpp | 55 + .../NucleusLib/pnUtils/Private/Win32/W32Int.h | 25 + .../pnUtils/Private/Win32/pnUtW32Addr.cpp | 373 + .../pnUtils/Private/Win32/pnUtW32Dll.cpp | 41 + .../pnUtils/Private/Win32/pnUtW32Misc.cpp | 207 + .../pnUtils/Private/Win32/pnUtW32Path.cpp | 845 + .../pnUtils/Private/Win32/pnUtW32Str.cpp | 82 + .../pnUtils/Private/Win32/pnUtW32Sync.cpp | 359 + .../pnUtils/Private/Win32/pnUtW32Time.cpp | 146 + .../pnUtils/Private/Win32/pnUtW32Uuid.cpp | 171 + .../NucleusLib/pnUtils/Private/pnUtAddr.cpp | 80 + .../NucleusLib/pnUtils/Private/pnUtAddr.h | 154 + .../pnUtils/Private/pnUtAllIncludes.h | 63 + .../NucleusLib/pnUtils/Private/pnUtArray.cpp | 86 + .../NucleusLib/pnUtils/Private/pnUtArray.h | 1071 ++ .../NucleusLib/pnUtils/Private/pnUtBase64.cpp | 170 + .../NucleusLib/pnUtils/Private/pnUtBase64.h | 66 + .../NucleusLib/pnUtils/Private/pnUtBigNum.cpp | 1382 ++ .../NucleusLib/pnUtils/Private/pnUtBigNum.h | 110 + .../NucleusLib/pnUtils/Private/pnUtCmd.cpp | 683 + .../NucleusLib/pnUtils/Private/pnUtCmd.h | 128 + .../NucleusLib/pnUtils/Private/pnUtCoreLib.h | 44 + .../NucleusLib/pnUtils/Private/pnUtCrypt.cpp | 641 + .../NucleusLib/pnUtils/Private/pnUtCrypt.h | 166 + .../NucleusLib/pnUtils/Private/pnUtEndian.cpp | 130 + .../NucleusLib/pnUtils/Private/pnUtEndian.h | 198 + .../NucleusLib/pnUtils/Private/pnUtHash.cpp | 78 + .../NucleusLib/pnUtils/Private/pnUtHash.h | 724 + .../NucleusLib/pnUtils/Private/pnUtList.cpp | 113 + .../NucleusLib/pnUtils/Private/pnUtList.h | 634 + .../NucleusLib/pnUtils/Private/pnUtMath.cpp | 81 + .../NucleusLib/pnUtils/Private/pnUtMath.h | 100 + .../NucleusLib/pnUtils/Private/pnUtMisc.cpp | 76 + .../NucleusLib/pnUtils/Private/pnUtMisc.h | 150 + .../NucleusLib/pnUtils/Private/pnUtPath.cpp | 274 + .../NucleusLib/pnUtils/Private/pnUtPath.h | 309 + .../NucleusLib/pnUtils/Private/pnUtPragma.h | 39 + .../NucleusLib/pnUtils/Private/pnUtPriQ.h | 471 + .../NucleusLib/pnUtils/Private/pnUtRand.cpp | 169 + .../NucleusLib/pnUtils/Private/pnUtRand.h | 51 + .../NucleusLib/pnUtils/Private/pnUtRef.h | 169 + .../NucleusLib/pnUtils/Private/pnUtSkipList.h | 461 + .../NucleusLib/pnUtils/Private/pnUtSort.h | 225 + .../pnUtils/Private/pnUtSpareList.cpp | 155 + .../pnUtils/Private/pnUtSpareList.h | 131 + .../NucleusLib/pnUtils/Private/pnUtStr.cpp | 756 + .../NucleusLib/pnUtils/Private/pnUtStr.h | 140 + .../NucleusLib/pnUtils/Private/pnUtSubst.cpp | 286 + .../NucleusLib/pnUtils/Private/pnUtSubst.h | 103 + .../NucleusLib/pnUtils/Private/pnUtSync.h | 118 + .../NucleusLib/pnUtils/Private/pnUtTime.cpp | 76 + .../NucleusLib/pnUtils/Private/pnUtTime.h | 113 + .../NucleusLib/pnUtils/Private/pnUtTls.cpp | 67 + .../NucleusLib/pnUtils/Private/pnUtTls.h | 56 + .../NucleusLib/pnUtils/Private/pnUtTypes.h | 41 + .../NucleusLib/pnUtils/Private/pnUtUuid.cpp | 87 + .../NucleusLib/pnUtils/Private/pnUtUuid.h | 103 + .../Plasma/NucleusLib/pnUtils/pnUtils.cpp | 33 + .../Plasma/NucleusLib/pnUtils/pnUtils.h | 39 + .../Plasma/NucleusLib/pnUtilsExe/Intern.h | 49 + .../Plasma/NucleusLib/pnUtilsExe/Pch.h | 41 + .../pnUtilsExe/Private/Unix/pnUteUxTime.cpp | 33 + .../pnUtilsExe/Private/Win32/pnUteW32Time.cpp | 53 + .../pnUtilsExe/Private/pnUteTime.cpp | 91 + .../pnUtilsExe/Private/pnUteTls.cpp | 91 + .../Plasma/PubUtilLib/inc/plAllCreatables.h | 59 + .../plAgeDescription/plAgeDescription.cpp | 549 + .../plAgeDescription/plAgeDescription.h | 196 + .../plAgeDescription/plAgeManifest.cpp | 248 + .../plAgeDescription/plAgeManifest.h | 118 + .../PubUtilLib/plAgeLoader/plAgeLoader.cpp | 455 + .../PubUtilLib/plAgeLoader/plAgeLoader.h | 129 + .../plAgeLoader/plAgeLoaderCreatable.h | 34 + .../plAgeLoader/plAgeLoaderPaging.cpp | 321 + .../plAgeLoader/plBackgroundDownloader.cpp | 529 + .../plAgeLoader/plBackgroundDownloader.h | 82 + .../PubUtilLib/plAgeLoader/plResPatcher.cpp | 502 + .../PubUtilLib/plAgeLoader/plResPatcher.h | 87 + .../PubUtilLib/plAudible/plAudibleCreatable.h | 42 + .../PubUtilLib/plAudible/plAudibleNull.cpp | 67 + .../PubUtilLib/plAudible/plAudibleNull.h | 93 + .../PubUtilLib/plAudible/plWinAudible.cpp | 771 + .../PubUtilLib/plAudible/plWinAudible.h | 151 + .../plAudible/plWinAudibleProxy.cpp | 66 + .../PubUtilLib/plAudible/plWinAudibleProxy.h | 47 + .../Plasma/PubUtilLib/plAudio/plAudioCaps.cpp | 220 + .../Plasma/PubUtilLib/plAudio/plAudioCaps.h | 84 + .../PubUtilLib/plAudio/plAudioCreatable.h | 57 + .../plAudio/plAudioReaderCreatable.h | 25 + .../PubUtilLib/plAudio/plAudioSystem.cpp | 1279 ++ .../Plasma/PubUtilLib/plAudio/plAudioSystem.h | 264 + .../PubUtilLib/plAudio/plDSoundBuffer.cpp | 783 + .../PubUtilLib/plAudio/plDSoundBuffer.h | 152 + .../PubUtilLib/plAudio/plEAXEffects.cpp | 679 + .../Plasma/PubUtilLib/plAudio/plEAXEffects.h | 192 + .../PubUtilLib/plAudio/plEAXListenerMod.cpp | 228 + .../PubUtilLib/plAudio/plEAXListenerMod.h | 75 + .../Plasma/PubUtilLib/plAudio/plOGGCodec.cpp | 342 + .../Plasma/PubUtilLib/plAudio/plOGGCodec.h | 106 + .../Plasma/PubUtilLib/plAudio/plSound.cpp | 1528 ++ .../Plasma/PubUtilLib/plAudio/plSound.h | 396 + .../PubUtilLib/plAudio/plSoundEvent.cpp | 183 + .../Plasma/PubUtilLib/plAudio/plSoundEvent.h | 83 + .../Plasma/PubUtilLib/plAudio/plVoiceChat.cpp | 698 + .../Plasma/PubUtilLib/plAudio/plVoiceChat.h | 196 + .../PubUtilLib/plAudio/plWAVClipBuffer.cpp | 210 + .../PubUtilLib/plAudio/plWAVClipBuffer.h | 78 + .../Plasma/PubUtilLib/plAudio/plWavFile.cpp | 1088 ++ .../Plasma/PubUtilLib/plAudio/plWavFile.h | 114 + .../plAudio/plWin32GroupedSound.cpp | 393 + .../PubUtilLib/plAudio/plWin32GroupedSound.h | 83 + .../PubUtilLib/plAudio/plWin32Sound.cpp | 430 + .../Plasma/PubUtilLib/plAudio/plWin32Sound.h | 120 + .../PubUtilLib/plAudio/plWin32StaticSound.cpp | 327 + .../PubUtilLib/plAudio/plWin32StaticSound.h | 80 + .../plAudio/plWin32StreamingSound.cpp | 493 + .../plAudio/plWin32StreamingSound.h | 90 + .../PubUtilLib/plAudio/plWinMicLevel.cpp | 361 + .../Plasma/PubUtilLib/plAudio/plWinMicLevel.h | 64 + .../PubUtilLib/plAudioCore/plAudioCore.h | 68 + .../plAudioCore/plAudioCoreCreatable.h | 36 + .../plAudioCore/plAudioFileReader.cpp | 169 + .../plAudioCore/plAudioFileReader.h | 83 + .../plAudioCore/plBufferedFileReader.cpp | 166 + .../plAudioCore/plBufferedFileReader.h | 66 + .../plAudioCore/plFastWavReader.cpp | 334 + .../PubUtilLib/plAudioCore/plFastWavReader.h | 82 + .../PubUtilLib/plAudioCore/plSoundBuffer.cpp | 565 + .../PubUtilLib/plAudioCore/plSoundBuffer.h | 146 + .../plAudioCore/plSoundDeswizzler.cpp | 81 + .../plAudioCore/plSoundDeswizzler.h | 57 + .../Plasma/PubUtilLib/plAvatar/plAGAnim.cpp | 766 + .../Plasma/PubUtilLib/plAvatar/plAGAnim.h | 430 + .../PubUtilLib/plAvatar/plAGAnimInstance.cpp | 606 + .../PubUtilLib/plAvatar/plAGAnimInstance.h | 278 + .../PubUtilLib/plAvatar/plAGApplicator.cpp | 165 + .../PubUtilLib/plAvatar/plAGApplicator.h | 154 + .../PubUtilLib/plAvatar/plAGChannel.cpp | 112 + .../Plasma/PubUtilLib/plAvatar/plAGChannel.h | 182 + .../PubUtilLib/plAvatar/plAGMasterMod.cpp | 806 + .../PubUtilLib/plAvatar/plAGMasterMod.h | 252 + .../plAvatar/plAGMasterSDLModifier.cpp | 195 + .../plAvatar/plAGMasterSDLModifier.h | 70 + .../PubUtilLib/plAvatar/plAGModifier.cpp | 306 + .../Plasma/PubUtilLib/plAvatar/plAGModifier.h | 154 + .../PubUtilLib/plAvatar/plAnimStage.cpp | 780 + .../Plasma/PubUtilLib/plAvatar/plAnimStage.h | 273 + .../PubUtilLib/plAvatar/plAntiGravAction.cpp | 155 + .../PubUtilLib/plAvatar/plAntiGravAction.h | 63 + .../PubUtilLib/plAvatar/plArmatureEffects.cpp | 325 + .../PubUtilLib/plAvatar/plArmatureEffects.h | 148 + .../PubUtilLib/plAvatar/plArmatureMod.cpp | 2840 +++ .../PubUtilLib/plAvatar/plArmatureMod.h | 469 + .../PubUtilLib/plAvatar/plAvBehaviors.cpp | 121 + .../PubUtilLib/plAvatar/plAvBehaviors.h | 71 + .../Plasma/PubUtilLib/plAvatar/plAvBrain.cpp | 184 + .../Plasma/PubUtilLib/plAvatar/plAvBrain.h | 87 + .../PubUtilLib/plAvatar/plAvBrainClimb.cpp | 1035 ++ .../PubUtilLib/plAvatar/plAvBrainClimb.h | 211 + .../PubUtilLib/plAvatar/plAvBrainCoop.cpp | 241 + .../PubUtilLib/plAvatar/plAvBrainCoop.h | 81 + .../PubUtilLib/plAvatar/plAvBrainCritter.cpp | 726 + .../PubUtilLib/plAvatar/plAvBrainCritter.h | 168 + .../PubUtilLib/plAvatar/plAvBrainDrive.cpp | 183 + .../PubUtilLib/plAvatar/plAvBrainDrive.h | 85 + .../PubUtilLib/plAvatar/plAvBrainGeneric.cpp | 957 + .../PubUtilLib/plAvatar/plAvBrainGeneric.h | 314 + .../PubUtilLib/plAvatar/plAvBrainHuman.cpp | 1453 ++ .../PubUtilLib/plAvatar/plAvBrainHuman.h | 393 + .../PubUtilLib/plAvatar/plAvBrainPuppet.cpp | 34 + .../PubUtilLib/plAvatar/plAvBrainPuppet.h | 26 + .../plAvBrainRideAnimatedPhysical.cpp | 176 + .../plAvatar/plAvBrainRideAnimatedPhysical.h | 49 + .../plAvatar/plAvBrainStaticNPC.cpp | 131 + .../PubUtilLib/plAvatar/plAvBrainSwim.cpp | 682 + .../PubUtilLib/plAvatar/plAvBrainSwim.h | 108 + .../PubUtilLib/plAvatar/plAvBrainUser.cpp | 26 + .../PubUtilLib/plAvatar/plAvBrainUser.h | 26 + .../plAvatar/plAvCallbackAction.cpp | 559 + .../PubUtilLib/plAvatar/plAvCallbackAction.h | 201 + .../Plasma/PubUtilLib/plAvatar/plAvDefs.h | 79 + .../plAvatar/plAvLadderModifier.cpp | 378 + .../PubUtilLib/plAvatar/plAvLadderModifier.h | 87 + .../PubUtilLib/plAvatar/plAvMotorHuman.cpp | 115 + .../PubUtilLib/plAvatar/plAvMotorHuman.h | 25 + .../Plasma/PubUtilLib/plAvatar/plAvTask.cpp | 25 + .../Plasma/PubUtilLib/plAvatar/plAvTask.h | 100 + .../PubUtilLib/plAvatar/plAvTaskBrain.cpp | 115 + .../PubUtilLib/plAvatar/plAvTaskBrain.h | 80 + .../PubUtilLib/plAvatar/plAvTaskOrient.cpp | 26 + .../PubUtilLib/plAvatar/plAvTaskOrient.h | 26 + .../PubUtilLib/plAvatar/plAvTaskSeek.cpp | 623 + .../Plasma/PubUtilLib/plAvatar/plAvTaskSeek.h | 174 + .../PubUtilLib/plAvatar/plAvatarClothing.cpp | 1828 ++ .../PubUtilLib/plAvatar/plAvatarClothing.h | 313 + .../PubUtilLib/plAvatar/plAvatarCreatable.h | 198 + .../PubUtilLib/plAvatar/plAvatarMgr.cpp | 1029 ++ .../Plasma/PubUtilLib/plAvatar/plAvatarMgr.h | 220 + .../plAvatar/plAvatarSDLModifier.cpp | 555 + .../PubUtilLib/plAvatar/plAvatarSDLModifier.h | 167 + .../PubUtilLib/plAvatar/plAvatarTasks.cpp | 834 + .../PubUtilLib/plAvatar/plAvatarTasks.h | 255 + .../PubUtilLib/plAvatar/plClothingLayout.h | 153 + .../plAvatar/plClothingSDLModifier.cpp | 308 + .../plAvatar/plClothingSDLModifier.h | 89 + .../PubUtilLib/plAvatar/plCoopCoordinator.cpp | 427 + .../PubUtilLib/plAvatar/plCoopCoordinator.h | 118 + .../PubUtilLib/plAvatar/plCritterCommands.h | 160 + .../PubUtilLib/plAvatar/plMatrixChannel.cpp | 1002 ++ .../PubUtilLib/plAvatar/plMatrixChannel.h | 368 + .../plAvatar/plMultistageBehMod.cpp | 245 + .../PubUtilLib/plAvatar/plMultistageBehMod.h | 72 + .../PubUtilLib/plAvatar/plNPCSpawnMod.cpp | 184 + .../PubUtilLib/plAvatar/plNPCSpawnMod.h | 62 + .../PubUtilLib/plAvatar/plOneShotMod.cpp | 171 + .../Plasma/PubUtilLib/plAvatar/plOneShotMod.h | 66 + .../plAvatar/plPhysicalControllerCore.cpp | 965 ++ .../plAvatar/plPhysicalControllerCore.h | 333 + .../PubUtilLib/plAvatar/plPointChannel.cpp | 485 + .../PubUtilLib/plAvatar/plPointChannel.h | 286 + .../PubUtilLib/plAvatar/plPuppetBrainMsg.h | 26 + .../PubUtilLib/plAvatar/plPuppetCommands.h | 26 + .../PubUtilLib/plAvatar/plQuatChannel.cpp | 314 + .../PubUtilLib/plAvatar/plQuatChannel.h | 171 + .../PubUtilLib/plAvatar/plScalarChannel.cpp | 590 + .../PubUtilLib/plAvatar/plScalarChannel.h | 346 + .../PubUtilLib/plAvatar/plSeekPointMod.cpp | 95 + .../PubUtilLib/plAvatar/plSeekPointMod.h | 62 + .../PubUtilLib/plAvatar/plSittingModifier.cpp | 374 + .../PubUtilLib/plAvatar/plSittingModifier.h | 112 + .../PubUtilLib/plAvatar/plSwimRegion.cpp | 265 + .../Plasma/PubUtilLib/plAvatar/plSwimRegion.h | 110 + .../PubUtilLib/plCompression/plCompress.h | 51 + .../plCompression/plZlibCompress.cpp | 268 + .../PubUtilLib/plCompression/plZlibCompress.h | 54 + .../PubUtilLib/plCompression/plZlibStream.cpp | 299 + .../PubUtilLib/plCompression/plZlibStream.h | 74 + .../PubUtilLib/plContainer/hsStringTable.cpp | 223 + .../PubUtilLib/plContainer/hsStringTable.h | 71 + .../PubUtilLib/plContainer/plConfigInfo.cpp | 1127 ++ .../PubUtilLib/plContainer/plConfigInfo.h | 481 + .../plContainer/plConfigInfoLogging.cpp | 307 + .../PubUtilLib/plContainer/plContainer.h | 63 + .../plContainer/plKeysAndValues.cpp | 263 + .../PubUtilLib/plContainer/plKeysAndValues.h | 103 + .../plDeviceSelector/plDeviceSelector.cpp | 571 + .../plDeviceSelector/plDeviceSelector.h | 144 + .../PubUtilLib/plDrawable/plAccMeshSmooth.cpp | 341 + .../PubUtilLib/plDrawable/plAccMeshSmooth.h | 91 + .../plDrawable/plAccessGeometry.cpp | 583 + .../PubUtilLib/plDrawable/plAccessGeometry.h | 161 + .../PubUtilLib/plDrawable/plAccessPartySpan.h | 39 + .../plDrawable/plAccessSnapShot.cpp | 270 + .../PubUtilLib/plDrawable/plAccessSnapShot.h | 71 + .../PubUtilLib/plDrawable/plAccessSpan.h | 130 + .../PubUtilLib/plDrawable/plAccessTriSpan.h | 158 + .../PubUtilLib/plDrawable/plAccessVtxSpan.h | 447 + .../plDrawable/plActivePrintShape.cpp | 114 + .../plDrawable/plActivePrintShape.h | 61 + .../Plasma/PubUtilLib/plDrawable/plAuxSpan.h | 100 + .../PubUtilLib/plDrawable/plAvMeshSmooth.cpp | 270 + .../PubUtilLib/plDrawable/plAvMeshSmooth.h | 92 + .../PubUtilLib/plDrawable/plCluster.cpp | 196 + .../Plasma/PubUtilLib/plDrawable/plCluster.h | 94 + .../PubUtilLib/plDrawable/plClusterGroup.cpp | 327 + .../PubUtilLib/plDrawable/plClusterGroup.h | 147 + .../Plasma/PubUtilLib/plDrawable/plCutter.cpp | 1371 ++ .../Plasma/PubUtilLib/plDrawable/plCutter.h | 162 + .../plDrawable/plDrawableCreatable.h | 122 + .../plDrawable/plDrawableGenerator.cpp | 749 + .../plDrawable/plDrawableGenerator.h | 152 + .../PubUtilLib/plDrawable/plDrawableSpans.cpp | 3945 +++++ .../PubUtilLib/plDrawable/plDrawableSpans.h | 374 + .../plDrawable/plDrawableSpansExport.cpp | 768 + .../PubUtilLib/plDrawable/plDynaBulletMgr.cpp | 116 + .../PubUtilLib/plDrawable/plDynaBulletMgr.h | 57 + .../PubUtilLib/plDrawable/plDynaDecal.cpp | 370 + .../PubUtilLib/plDrawable/plDynaDecal.h | 145 + .../PubUtilLib/plDrawable/plDynaDecalMgr.cpp | 1787 ++ .../PubUtilLib/plDrawable/plDynaDecalMgr.h | 276 + .../PubUtilLib/plDrawable/plDynaFootMgr.cpp | 155 + .../PubUtilLib/plDrawable/plDynaFootMgr.h | 54 + .../PubUtilLib/plDrawable/plDynaPuddleMgr.cpp | 97 + .../PubUtilLib/plDrawable/plDynaPuddleMgr.h | 51 + .../PubUtilLib/plDrawable/plDynaRippleMgr.cpp | 233 + .../PubUtilLib/plDrawable/plDynaRippleMgr.h | 61 + .../plDrawable/plDynaRippleMgrVS.cpp | 222 + .../PubUtilLib/plDrawable/plDynaRippleVSMgr.h | 63 + .../plDrawable/plDynaTorpedoMgr.cpp | 143 + .../PubUtilLib/plDrawable/plDynaTorpedoMgr.h | 54 + .../plDrawable/plDynaTorpedoVSMgr.cpp | 143 + .../plDrawable/plDynaTorpedoVSMgr.h | 64 + .../PubUtilLib/plDrawable/plDynaWakeMgr.cpp | 212 + .../PubUtilLib/plDrawable/plDynaWakeMgr.h | 73 + .../plDrawable/plFixedWaterState7.cpp | 193 + .../plDrawable/plFixedWaterState7.h | 116 + .../PubUtilLib/plDrawable/plGeoSpanDice.cpp | 318 + .../PubUtilLib/plDrawable/plGeoSpanDice.h | 64 + .../PubUtilLib/plDrawable/plGeometrySpan.cpp | 1194 ++ .../PubUtilLib/plDrawable/plGeometrySpan.h | 296 + .../plDrawable/plInstanceDrawInterface.cpp | 232 + .../plDrawable/plInstanceDrawInterface.h | 68 + .../plDrawable/plInterMeshSmooth.cpp | 232 + .../PubUtilLib/plDrawable/plInterMeshSmooth.h | 64 + .../PubUtilLib/plDrawable/plMorphArray.cpp | 84 + .../PubUtilLib/plDrawable/plMorphArray.h | 53 + .../PubUtilLib/plDrawable/plMorphDelta.cpp | 337 + .../PubUtilLib/plDrawable/plMorphDelta.h | 92 + .../PubUtilLib/plDrawable/plMorphSequence.cpp | 807 + .../PubUtilLib/plDrawable/plMorphSequence.h | 174 + .../plDrawable/plMorphSequenceSDLMod.cpp | 168 + .../plDrawable/plMorphSequenceSDLMod.h | 79 + .../plDrawable/plParticleFiller.cpp | 800 + .../PubUtilLib/plDrawable/plParticleFiller.h | 41 + .../PubUtilLib/plDrawable/plPrintShape.cpp | 61 + .../PubUtilLib/plDrawable/plPrintShape.h | 61 + .../PubUtilLib/plDrawable/plProxyGen.cpp | 352 + .../Plasma/PubUtilLib/plDrawable/plProxyGen.h | 116 + .../PubUtilLib/plDrawable/plRipVSConsts.h | 46 + .../PubUtilLib/plDrawable/plSharedMesh.cpp | 121 + .../PubUtilLib/plDrawable/plSharedMesh.h | 87 + .../PubUtilLib/plDrawable/plSpaceTree.cpp | 582 + .../PubUtilLib/plDrawable/plSpaceTree.h | 175 + .../plDrawable/plSpaceTreeMaker.cpp | 721 + .../PubUtilLib/plDrawable/plSpaceTreeMaker.h | 91 + .../PubUtilLib/plDrawable/plSpanInstance.cpp | 238 + .../PubUtilLib/plDrawable/plSpanInstance.h | 288 + .../PubUtilLib/plDrawable/plSpanTemplate.cpp | 137 + .../PubUtilLib/plDrawable/plSpanTemplate.h | 264 + .../PubUtilLib/plDrawable/plSpanTypes.cpp | 562 + .../PubUtilLib/plDrawable/plSpanTypes.h | 314 + .../PubUtilLib/plDrawable/plTimedInterp.h | 148 + .../PubUtilLib/plDrawable/plVisLOSMgr.cpp | 447 + .../PubUtilLib/plDrawable/plVisLOSMgr.h | 84 + .../PubUtilLib/plDrawable/plWaveSet7.cpp | 4453 +++++ .../Plasma/PubUtilLib/plDrawable/plWaveSet7.h | 642 + .../PubUtilLib/plDrawable/plWaveSetBase.cpp | 50 + .../PubUtilLib/plDrawable/plWaveSetBase.h | 62 + .../plDrawable/plWaveSetShaderConsts.h | 410 + .../PubUtilLib/plEncryption/plChecksum.cpp | 197 + .../PubUtilLib/plEncryption/plChecksum.h | 85 + .../Plasma/PubUtilLib/plFile/hsFiles.cpp | 168 + .../Plasma/PubUtilLib/plFile/hsFiles.h | 188 + .../Plasma/PubUtilLib/plFile/hsFiles_Mac.cpp | 393 + .../Plasma/PubUtilLib/plFile/hsFiles_PS2.cpp | 68 + .../Plasma/PubUtilLib/plFile/hsFiles_Unix.cpp | 130 + .../Plasma/PubUtilLib/plFile/hsFiles_Win.cpp | 304 + .../PubUtilLib/plFile/plBrowseFolder.cpp | 72 + .../Plasma/PubUtilLib/plFile/plBrowseFolder.h | 58 + .../PubUtilLib/plFile/plEncryptedStream.cpp | 577 + .../PubUtilLib/plFile/plEncryptedStream.h | 100 + .../Plasma/PubUtilLib/plFile/plFileUtils.cpp | 500 + .../Plasma/PubUtilLib/plFile/plFileUtils.h | 106 + .../PubUtilLib/plFile/plInitFileReader.cpp | 202 + .../PubUtilLib/plFile/plInitFileReader.h | 137 + .../PubUtilLib/plFile/plSecureStream.cpp | 624 + .../Plasma/PubUtilLib/plFile/plSecureStream.h | 111 + .../PubUtilLib/plFile/plStreamSource.cpp | 196 + .../Plasma/PubUtilLib/plFile/plStreamSource.h | 68 + .../Plasma/PubUtilLib/plGImage/HS_RECT.inc | 235 + .../Plasma/PubUtilLib/plGImage/hsCodec.h | 39 + .../PubUtilLib/plGImage/hsCodecManager.cpp | 162 + .../PubUtilLib/plGImage/hsCodecManager.h | 97 + .../PubUtilLib/plGImage/hsDXTDirectXCodec.cpp | 1226 ++ .../PubUtilLib/plGImage/hsDXTDirectXCodec.h | 91 + .../plGImage/hsDXTSoftwareCodec.cpp | 2483 +++ .../PubUtilLib/plGImage/hsDXTSoftwareCodec.h | 142 + .../Plasma/PubUtilLib/plGImage/hsRect.h | 151 + .../Plasma/PubUtilLib/plGImage/notes.txt | 2 + .../PubUtilLib/plGImage/plAVIWriter.cpp | 294 + .../Plasma/PubUtilLib/plGImage/plAVIWriter.h | 55 + .../Plasma/PubUtilLib/plGImage/plBitmap.cpp | 164 + .../Plasma/PubUtilLib/plGImage/plBitmap.h | 188 + .../PubUtilLib/plGImage/plBumpMapGen.cpp | 298 + .../Plasma/PubUtilLib/plGImage/plBumpMapGen.h | 51 + .../PubUtilLib/plGImage/plCubicEnvironmap.cpp | 179 + .../PubUtilLib/plGImage/plCubicEnvironmap.h | 98 + .../plGImage/plDynSurfaceWriter.cpp | 1109 ++ .../PubUtilLib/plGImage/plDynSurfaceWriter.h | 205 + .../PubUtilLib/plGImage/plDynamicTextMap.cpp | 911 + .../PubUtilLib/plGImage/plDynamicTextMap.h | 228 + .../Plasma/PubUtilLib/plGImage/plFont.cpp | 1966 +++ .../Plasma/PubUtilLib/plGImage/plFont.h | 282 + .../PubUtilLib/plGImage/plFontCache.cpp | 203 + .../Plasma/PubUtilLib/plGImage/plFontCache.h | 90 + .../PubUtilLib/plGImage/plGImageCreatable.h | 72 + .../PubUtilLib/plGImage/plImageConvert.h | 43 + .../PubUtilLib/plGImage/plLODMipmap.cpp | 284 + .../Plasma/PubUtilLib/plGImage/plLODMipmap.h | 92 + .../Plasma/PubUtilLib/plGImage/plMipmap.cpp | 2194 +++ .../Plasma/PubUtilLib/plGImage/plMipmap.h | 370 + .../PubUtilLib/plGImage/plTGAWriter.cpp | 95 + .../Plasma/PubUtilLib/plGImage/plTGAWriter.h | 63 + .../PubUtilLib/plGImage/plWinFontCache.cpp | 283 + .../PubUtilLib/plGImage/plWinFontCache.h | 115 + .../plGLight/plDirectShadowMaster.cpp | 189 + .../plGLight/plDirectShadowMaster.h | 63 + .../PubUtilLib/plGLight/plGLightCreatable.h | 60 + .../PubUtilLib/plGLight/plLightInfo.cpp | 1003 ++ .../Plasma/PubUtilLib/plGLight/plLightInfo.h | 403 + .../PubUtilLib/plGLight/plLightKonstants.h | 41 + .../PubUtilLib/plGLight/plLightProxy.cpp | 66 + .../Plasma/PubUtilLib/plGLight/plLightProxy.h | 49 + .../PubUtilLib/plGLight/plLightSpace.cpp | 76 + .../Plasma/PubUtilLib/plGLight/plLightSpace.h | 59 + .../PubUtilLib/plGLight/plPerspDirSlave.cpp | 372 + .../PubUtilLib/plGLight/plPerspDirSlave.h | 47 + .../plGLight/plPointShadowMaster.cpp | 203 + .../PubUtilLib/plGLight/plPointShadowMaster.h | 60 + .../PubUtilLib/plGLight/plShadowCaster.cpp | 208 + .../PubUtilLib/plGLight/plShadowCaster.h | 141 + .../PubUtilLib/plGLight/plShadowMaster.cpp | 1288 ++ .../PubUtilLib/plGLight/plShadowMaster.h | 154 + .../PubUtilLib/plGLight/plShadowSlave.cpp | 200 + .../PubUtilLib/plGLight/plShadowSlave.h | 124 + .../plGRenderProcs/hsGRenderProcs.cpp | 234 + .../plGRenderProcs/hsGRenderProcs.h | 202 + .../plGRenderProcs/hsSfxAngleFade.cpp | 218 + .../plGRenderProcs/hsSfxAngleFade.h | 74 + .../plGRenderProcs/hsSfxDistFade.cpp | 315 + .../PubUtilLib/plGRenderProcs/hsSfxDistFade.h | 91 + .../plGRenderProcs/hsSfxDistShade.cpp | 276 + .../plGRenderProcs/hsSfxDistShade.h | 81 + .../plGRenderProcs/hsSfxGlobalShade.cpp | 226 + .../plGRenderProcs/hsSfxGlobalShade.h | 104 + .../plGRenderProcs/hsSfxIntenseAlpha.cpp | 65 + .../plGRenderProcs/hsSfxIntenseAlpha.h | 66 + .../plGRenderProcs/hsSfxObjDistFade.cpp | 304 + .../plGRenderProcs/hsSfxObjDistFade.h | 100 + .../plGRenderProcs/hsSfxObjDistShade.cpp | 201 + .../plGRenderProcs/hsSfxObjDistShade.h | 82 + .../plGRenderProcs/plGRenderProcsCreatable.h | 64 + .../PubUtilLib/plGeometry/hsOscillator.cpp | 747 + .../PubUtilLib/plGeometry/hsOscillator.h | 164 + .../PubUtilLib/plGeometry/hsPerterber.cpp | 334 + .../PubUtilLib/plGeometry/hsPerterber.h | 91 + .../plGeometry/plGeometryCreatable.h | 54 + .../plInputCore/plAvatarInputInterface.cpp | 1122 ++ .../plInputCore/plAvatarInputInterface.h | 252 + .../PubUtilLib/plInputCore/plDInputDevice.cpp | 277 + .../PubUtilLib/plInputCore/plDInputDevice.h | 50 + .../plInputCore/plDebugInputInterface.cpp | 341 + .../plInputCore/plDebugInputInterface.h | 78 + .../plInputCore/plInputCoreCreatable.h | 40 + .../PubUtilLib/plInputCore/plInputDevice.cpp | 944 + .../PubUtilLib/plInputCore/plInputDevice.h | 227 + .../plInputCore/plInputInterface.cpp | 289 + .../PubUtilLib/plInputCore/plInputInterface.h | 206 + .../plInputCore/plInputInterfaceMgr.cpp | 947 + .../plInputCore/plInputInterfaceMgr.h | 186 + .../PubUtilLib/plInputCore/plInputManager.cpp | 693 + .../PubUtilLib/plInputCore/plInputManager.h | 96 + .../plInputCore/plSceneInputInterface.cpp | 1226 ++ .../plInputCore/plSceneInputInterface.h | 133 + .../plInputCore/plTelescopeInputInterface.cpp | 125 + .../plInputCore/plTelescopeInputInterface.h | 74 + .../Plasma/PubUtilLib/plInterp/hsInterp.cpp | 494 + .../Plasma/PubUtilLib/plInterp/hsInterp.h | 82 + .../Plasma/PubUtilLib/plInterp/hsKeys.cpp | 554 + .../Plasma/PubUtilLib/plInterp/hsKeys.h | 239 + .../Plasma/PubUtilLib/plInterp/hsTimedValue.h | 220 + .../PubUtilLib/plInterp/plATCEaseCurves.cpp | 347 + .../PubUtilLib/plInterp/plAnimEaseTypes.h | 43 + .../Plasma/PubUtilLib/plInterp/plAnimPath.cpp | 793 + .../Plasma/PubUtilLib/plInterp/plAnimPath.h | 172 + .../PubUtilLib/plInterp/plAnimTimeConvert.cpp | 1386 ++ .../PubUtilLib/plInterp/plAnimTimeConvert.h | 306 + .../PubUtilLib/plInterp/plController.cpp | 900 + .../Plasma/PubUtilLib/plInterp/plController.h | 214 + .../PubUtilLib/plInterp/plInterpCreatable.h | 53 + .../PubUtilLib/plInterp/plModulator.cpp | 107 + .../Plasma/PubUtilLib/plInterp/plModulator.h | 65 + .../Plasma/PubUtilLib/plIntersect/notes.txt | 4 + .../PubUtilLib/plIntersect/plClosest.cpp | 685 + .../Plasma/PubUtilLib/plIntersect/plClosest.h | 122 + .../PubUtilLib/plIntersect/plHardRegion.cpp | 96 + .../PubUtilLib/plIntersect/plHardRegion.h | 80 + .../plIntersect/plHardRegionPlanes.cpp | 152 + .../plIntersect/plHardRegionPlanes.h | 74 + .../plIntersect/plHardRegionTypes.cpp | 196 + .../plIntersect/plHardRegionTypes.h | 104 + .../plIntersect/plIntersectCreatable.h | 83 + .../PubUtilLib/plIntersect/plRegionBase.h | 46 + .../PubUtilLib/plIntersect/plSoftVolume.cpp | 143 + .../PubUtilLib/plIntersect/plSoftVolume.h | 100 + .../plIntersect/plSoftVolumeTypes.cpp | 279 + .../plIntersect/plSoftVolumeTypes.h | 144 + .../PubUtilLib/plIntersect/plVolumeIsect.cpp | 989 ++ .../PubUtilLib/plIntersect/plVolumeIsect.h | 308 + .../Plasma/PubUtilLib/plJPEG/plJPEG.cpp | 328 + .../Sources/Plasma/PubUtilLib/plJPEG/plJPEG.h | 76 + .../Plasma/PubUtilLib/plMath/hsNoiseFunc.cpp | 140 + .../Plasma/PubUtilLib/plMath/hsNoiseFunc.h | 66 + .../Plasma/PubUtilLib/plMath/hsRadixSort.cpp | 165 + .../Plasma/PubUtilLib/plMath/hsRadixSort.h | 75 + .../PubUtilLib/plMath/hsSearchVersion.h | 165 + .../Plasma/PubUtilLib/plMath/plAvg.cpp | 143 + .../Sources/Plasma/PubUtilLib/plMath/plAvg.h | 93 + .../Plasma/PubUtilLib/plMath/plRandom.h | 121 + .../Plasma/PubUtilLib/plMath/plTriUtils.cpp | 314 + .../Plasma/PubUtilLib/plMath/plTriUtils.h | 71 + .../Plasma/PubUtilLib/plMessage/plAIMsg.cpp | 83 + .../Plasma/PubUtilLib/plMessage/plAIMsg.h | 104 + .../plMessage/plAccountUpdateMsg.cpp | 84 + .../PubUtilLib/plMessage/plAccountUpdateMsg.h | 72 + .../PubUtilLib/plMessage/plActivatorMsg.h | 96 + .../PubUtilLib/plMessage/plAgeLoadedMsg.h | 102 + .../plMessage/plAngularVelocityMsg.h | 42 + .../PubUtilLib/plMessage/plAnimCmdMsg.cpp | 178 + .../PubUtilLib/plMessage/plAnimCmdMsg.h | 206 + .../plMessage/plAnimationEventCallbackMsg.h | 64 + .../plApplyAvatarCustomizationsMsg.h | 66 + .../plApplyStoredAvatarSettingsMsg.h | 64 + .../PubUtilLib/plMessage/plAvCoopMsg.cpp | 112 + .../Plasma/PubUtilLib/plMessage/plAvCoopMsg.h | 115 + .../PubUtilLib/plMessage/plAvatarFootMsg.cpp | 53 + .../PubUtilLib/plMessage/plAvatarFootMsg.h | 74 + .../PubUtilLib/plMessage/plAvatarMsg.cpp | 658 + .../Plasma/PubUtilLib/plMessage/plAvatarMsg.h | 448 + .../PubUtilLib/plMessage/plBulletMsg.cpp | 81 + .../Plasma/PubUtilLib/plMessage/plBulletMsg.h | 81 + .../plMessage/plCCRMessageCreatable.h | 44 + .../Plasma/PubUtilLib/plMessage/plCCRMsg.cpp | 135 + .../Plasma/PubUtilLib/plMessage/plCCRMsg.h | 170 + .../plMessage/plCaptureRenderMsg.cpp | 34 + .../PubUtilLib/plMessage/plCaptureRenderMsg.h | 55 + .../PubUtilLib/plMessage/plClimbEventMsg.h | 44 + .../PubUtilLib/plMessage/plClimbMsg.cpp | 68 + .../Plasma/PubUtilLib/plMessage/plClimbMsg.h | 75 + .../PubUtilLib/plMessage/plCollideMsg.cpp | 25 + .../PubUtilLib/plMessage/plCollideMsg.h | 50 + .../PubUtilLib/plMessage/plCondRefMsg.h | 63 + .../plMessage/plConnectedToVaultMsg.h | 49 + .../PubUtilLib/plMessage/plConsoleMsg.h | 85 + .../plMessage/plDeviceRecreateMsg.h | 52 + .../plMessage/plDynaDecalEnableMsg.cpp | 85 + .../plMessage/plDynaDecalEnableMsg.h | 82 + .../plMessage/plDynamicEnvMapMsg.cpp | 57 + .../PubUtilLib/plMessage/plDynamicEnvMapMsg.h | 72 + .../PubUtilLib/plMessage/plDynamicTextMsg.cpp | 360 + .../PubUtilLib/plMessage/plDynamicTextMsg.h | 132 + .../PubUtilLib/plMessage/plElementRefMsg.h | 26 + .../PubUtilLib/plMessage/plEnvEffectMsg.h | 102 + .../PubUtilLib/plMessage/plExcludeRegionMsg.h | 75 + .../PubUtilLib/plMessage/plInputEventMsg.cpp | 316 + .../PubUtilLib/plMessage/plInputEventMsg.h | 407 + .../plMessage/plInputIfaceMgrMsg.cpp | 50 + .../PubUtilLib/plMessage/plInputIfaceMgrMsg.h | 123 + .../PubUtilLib/plMessage/plInterestingPing.h | 102 + .../PubUtilLib/plMessage/plLOSHitMsg.cpp | 42 + .../Plasma/PubUtilLib/plMessage/plLOSHitMsg.h | 82 + .../PubUtilLib/plMessage/plLOSRequestMsg.cpp | 54 + .../PubUtilLib/plMessage/plLOSRequestMsg.h | 101 + .../PubUtilLib/plMessage/plLayRefMsg.cpp | 43 + .../Plasma/PubUtilLib/plMessage/plLayRefMsg.h | 59 + .../PubUtilLib/plMessage/plLightRefMsg.h | 47 + .../plMessage/plLinearVelocityMsg.h | 42 + .../PubUtilLib/plMessage/plLinkToAgeMsg.cpp | 408 + .../PubUtilLib/plMessage/plLinkToAgeMsg.h | 284 + .../PubUtilLib/plMessage/plListenerMsg.cpp | 87 + .../PubUtilLib/plMessage/plListenerMsg.h | 106 + .../PubUtilLib/plMessage/plLoadAgeMsg.cpp | 119 + .../PubUtilLib/plMessage/plLoadAgeMsg.h | 106 + .../PubUtilLib/plMessage/plLoadAvatarMsg.cpp | 225 + .../PubUtilLib/plMessage/plLoadAvatarMsg.h | 121 + .../PubUtilLib/plMessage/plLoadCloneMsg.cpp | 259 + .../PubUtilLib/plMessage/plLoadCloneMsg.h | 119 + .../Plasma/PubUtilLib/plMessage/plMatRefMsg.h | 48 + .../plMessage/plMatrixUpdateMsg.cpp | 44 + .../PubUtilLib/plMessage/plMatrixUpdateMsg.h | 54 + .../PubUtilLib/plMessage/plMemberUpdateMsg.h | 50 + .../PubUtilLib/plMessage/plMeshRefMsg.h | 69 + .../PubUtilLib/plMessage/plMessageCreatable.h | 365 + .../Plasma/PubUtilLib/plMessage/plMovieMsg.h | 180 + .../PubUtilLib/plMessage/plMultistageMsg.cpp | 45 + .../PubUtilLib/plMessage/plMultistageMsg.h | 62 + .../PubUtilLib/plMessage/plNCAgeJoinerMsg.cpp | 32 + .../PubUtilLib/plMessage/plNCAgeJoinerMsg.h | 54 + .../plMessage/plNetClientMgrMsg.cpp | 32 + .../PubUtilLib/plMessage/plNetClientMgrMsg.h | 57 + .../PubUtilLib/plMessage/plNetCommMsgs.cpp | 32 + .../PubUtilLib/plMessage/plNetCommMsgs.h | 160 + .../PubUtilLib/plMessage/plNetOwnershipMsg.h | 61 + .../plMessage/plNetVoiceListMsg.cpp | 54 + .../PubUtilLib/plMessage/plNetVoiceListMsg.h | 70 + .../PubUtilLib/plMessage/plNodeCleanupMsg.h | 52 + .../plMessage/plOneShotCallbacks.cpp | 82 + .../PubUtilLib/plMessage/plOneShotCallbacks.h | 68 + .../PubUtilLib/plMessage/plOneShotMsg.cpp | 50 + .../PubUtilLib/plMessage/plOneShotMsg.h | 52 + .../plMessage/plParticleUpdateMsg.h | 207 + .../Plasma/PubUtilLib/plMessage/plPickedMsg.h | 72 + .../Plasma/PubUtilLib/plMessage/plPlayerMsg.h | 88 + .../PubUtilLib/plMessage/plPreloaderMsg.h | 51 + .../Plasma/PubUtilLib/plMessage/plRenderMsg.h | 74 + .../plMessage/plRenderRequestMsg.cpp | 93 + .../PubUtilLib/plMessage/plRenderRequestMsg.h | 100 + .../plMessage/plReplaceGeometryMsg.h | 74 + .../PubUtilLib/plMessage/plResMgrHelperMsg.h | 75 + .../PubUtilLib/plMessage/plResponderMsg.h | 50 + .../plMessage/plRideAnimatedPhysMsg.cpp | 54 + .../plMessage/plRideAnimatedPhysMsg.h | 50 + .../PubUtilLib/plMessage/plRippleShapeMsg.cpp | 58 + .../PubUtilLib/plMessage/plRippleShapeMsg.h | 58 + .../plMessage/plRoomLoadNotifyMsg.h | 95 + .../PubUtilLib/plMessage/plShadowCastMsg.h | 59 + .../plMessage/plSimInfluenceMsg.cpp | 166 + .../PubUtilLib/plMessage/plSimInfluenceMsg.h | 282 + .../PubUtilLib/plMessage/plSimStateMsg.cpp | 39 + .../PubUtilLib/plMessage/plSimStateMsg.h | 58 + .../PubUtilLib/plMessage/plSpawnModMsg.h | 70 + .../PubUtilLib/plMessage/plSpawnRequestMsg.h | 61 + .../Plasma/PubUtilLib/plMessage/plSwimMsg.cpp | 82 + .../Plasma/PubUtilLib/plMessage/plSwimMsg.h | 62 + .../PubUtilLib/plMessage/plSynchEnableMsg.cpp | 46 + .../PubUtilLib/plMessage/plSynchEnableMsg.h | 52 + .../PubUtilLib/plMessage/plTimerCallbackMsg.h | 63 + .../PubUtilLib/plMessage/plTransitionMsg.cpp | 33 + .../PubUtilLib/plMessage/plTransitionMsg.h | 81 + .../PubUtilLib/plMessage/plTriggerMsg.h | 56 + .../PubUtilLib/plMessage/plUniqueIdsMsg.h | 25 + .../PubUtilLib/plMessage/plVaultNotifyMsg.cpp | 54 + .../PubUtilLib/plMessage/plVaultNotifyMsg.h | 70 + .../plModifier/plAnimEventModifier.cpp | 127 + .../plModifier/plAnimEventModifier.h | 62 + .../plAnimTimeConvertSDLModifier.cpp | 127 + .../plModifier/plAnimTimeConvertSDLModifier.h | 63 + .../plModifier/plAxisAnimModifier.cpp | 373 + .../plModifier/plAxisAnimModifier.h | 92 + .../plModifier/plCloneSpawnModifier.cpp | 118 + .../plModifier/plCloneSpawnModifier.h | 61 + .../plModifier/plDecalEnableMod.cpp | 100 + .../PubUtilLib/plModifier/plDecalEnableMod.h | 64 + .../PubUtilLib/plModifier/plDetectorLog.cpp | 78 + .../PubUtilLib/plModifier/plDetectorLog.h | 34 + .../plModifier/plExcludeRegionModifier.cpp | 387 + .../plModifier/plExcludeRegionModifier.h | 100 + .../plModifier/plGameMarkerModifier.cpp | 86 + .../plModifier/plGameMarkerModifier.h | 73 + .../PubUtilLib/plModifier/plImageLibMod.cpp | 86 + .../PubUtilLib/plModifier/plImageLibMod.h | 63 + .../plModifier/plInterfaceInfoModifier.cpp | 56 + .../plModifier/plInterfaceInfoModifier.h | 56 + .../plModifier/plLayerSDLModifier.cpp | 189 + .../plModifier/plLayerSDLModifier.h | 68 + .../PubUtilLib/plModifier/plLogicModifier.cpp | 265 + .../PubUtilLib/plModifier/plLogicModifier.h | 61 + .../plMaintainersMarkerModifier.cpp | 54 + .../plModifier/plMaintainersMarkerModifier.h | 63 + .../plModifier/plModifierCreatable.h | 92 + .../plModifier/plResponderModifier.cpp | 791 + .../plModifier/plResponderModifier.h | 155 + .../plModifier/plResponderSDLModifier.cpp | 160 + .../plModifier/plResponderSDLModifier.h | 65 + .../PubUtilLib/plModifier/plSDLModifier.cpp | 229 + .../PubUtilLib/plModifier/plSDLModifier.h | 70 + .../plModifier/plSimpleModifier.cpp | 185 + .../PubUtilLib/plModifier/plSimpleModifier.h | 94 + .../plModifier/plSoundSDLModifier.cpp | 130 + .../plModifier/plSoundSDLModifier.h | 54 + .../PubUtilLib/plModifier/plSpawnModifier.cpp | 85 + .../PubUtilLib/plModifier/plSpawnModifier.h | 54 + .../PubUtilLib/plModifier/plTagModifier.cpp | 103 + .../PubUtilLib/plModifier/plTagModifier.h | 51 + .../plNetClient/plLinkEffectsMgr.cpp | 594 + .../PubUtilLib/plNetClient/plLinkEffectsMgr.h | 81 + .../plNetClient/plNetCliAgeJoiner.cpp | 476 + .../plNetClient/plNetCliAgeJoiner.h | 85 + .../plNetClient/plNetCliAgeLeaver.cpp | 284 + .../plNetClient/plNetCliAgeLeaver.h | 84 + .../plNetClient/plNetClientCommInterface.cpp | 68 + .../plNetClient/plNetClientCreatable.h | 41 + .../plNetClient/plNetClientGroup.cpp | 55 + .../PubUtilLib/plNetClient/plNetClientGroup.h | 122 + .../PubUtilLib/plNetClient/plNetClientMgr.cpp | 1550 ++ .../PubUtilLib/plNetClient/plNetClientMgr.h | 398 + .../plNetClient/plNetClientMgrLoad.cpp | 171 + .../plNetClient/plNetClientMgrRecord.cpp | 117 + .../plNetClient/plNetClientMgrSend.cpp | 460 + .../plNetClient/plNetClientMgrShow.cpp | 375 + .../plNetClient/plNetClientMgrTask.cpp | 25 + .../plNetClient/plNetClientMgrVault.cpp | 25 + .../plNetClient/plNetClientMgrVoice.cpp | 394 + .../plNetClient/plNetClientMsgHandler.cpp | 593 + .../plNetClient/plNetClientMsgHandler.h | 72 + .../plNetClient/plNetClientMsgScreener.cpp | 109 + .../plNetClient/plNetClientMsgScreener.h | 52 + .../plNetClient/plNetClientStats.cpp | 91 + .../PubUtilLib/plNetClient/plNetClientStats.h | 76 + .../plNetClient/plNetClientVNodeMgr.cpp | 164 + .../plNetClient/plNetClientVault.cpp | 234 + .../PubUtilLib/plNetClient/plNetClientVault.h | 71 + .../plNetClient/plNetLinkingMgr.cpp | 1012 ++ .../PubUtilLib/plNetClient/plNetLinkingMgr.h | 148 + .../plNetClient/plNetObjectDebugger.cpp | 306 + .../plNetClient/plNetObjectDebugger.h | 85 + .../PubUtilLib/plNetClient/plNetVoiceList.cpp | 145 + .../PubUtilLib/plNetClient/plNetVoiceList.h | 107 + .../plNetClientComm/plNetClientComm.cpp | 1527 ++ .../plNetClientComm/plNetClientComm.h | 376 + .../plNetClientCommCreatable.h | 25 + .../plNetClientComm/plNetClientCommTask.cpp | 25 + .../plNetClientComm/plNetClientCommTask.h | 25 + .../plNetClientRecorder.cpp | 160 + .../plNetClientRecorder/plNetClientRecorder.h | 178 + .../plNetClientStatsRecorder.cpp | 144 + .../plNetClientStreamRecorder.cpp | 362 + .../PubUtilLib/plNetCommon/plClientGuid.cpp | 424 + .../PubUtilLib/plNetCommon/plClientGuid.h | 129 + .../PubUtilLib/plNetCommon/plNetCommon.cpp | 114 + .../PubUtilLib/plNetCommon/plNetCommon.h | 242 + .../plNetCommon/plNetCommonConstants.h | 33 + .../plNetCommon/plNetCommonCreatable.h | 55 + .../plNetCommon/plNetCommonHelpers.cpp | 344 + .../plNetCommon/plNetCommonHelpers.h | 164 + .../PubUtilLib/plNetCommon/plNetMember.cpp | 50 + .../PubUtilLib/plNetCommon/plNetMember.h | 88 + .../PubUtilLib/plNetCommon/plNetMsgHandler.h | 80 + .../plNetCommon/plNetMsgScreener.cpp | 248 + .../PubUtilLib/plNetCommon/plNetMsgScreener.h | 65 + .../plNetCommon/plNetResManager.cpp | 86 + .../PubUtilLib/plNetCommon/plNetResManager.h | 50 + .../plNetCommon/plNetServerSessionInfo.cpp | 792 + .../plNetCommon/plNetServerSessionInfo.h | 334 + .../PubUtilLib/plNetCommon/plServerGuid.cpp | 275 + .../PubUtilLib/plNetCommon/plServerGuid.h | 95 + .../plNetCommon/plSpawnPointInfo.cpp | 113 + .../PubUtilLib/plNetCommon/plSpawnPointInfo.h | 68 + .../Plasma/PubUtilLib/plNetGameLib/Intern.h | 407 + .../Plasma/PubUtilLib/plNetGameLib/Pch.h | 53 + .../plNetGameLib/Private/plNglAllIncludes.h | 46 + .../plNetGameLib/Private/plNglAuth.cpp | 6106 +++++++ .../plNetGameLib/Private/plNglAuth.h | 700 + .../plNetGameLib/Private/plNglCore.cpp | 187 + .../plNetGameLib/Private/plNglCore.h | 58 + .../plNetGameLib/Private/plNglCsr.cpp | 892 + .../plNetGameLib/Private/plNglCsr.h | 78 + .../plNetGameLib/Private/plNglFile.cpp | 1460 ++ .../plNetGameLib/Private/plNglFile.h | 121 + .../plNetGameLib/Private/plNglGame.cpp | 813 + .../plNetGameLib/Private/plNglGame.h | 94 + .../plNetGameLib/Private/plNglGateKeeper.cpp | 1129 ++ .../plNetGameLib/Private/plNglGateKeeper.h | 121 + .../plNetGameLib/Private/plNglMisc.cpp | 73 + .../plNetGameLib/Private/plNglTrans.cpp | 343 + .../PubUtilLib/plNetGameLib/plNetGameLib.h | 44 + .../plNetMessage/plNetCommonMessage.h | 83 + .../PubUtilLib/plNetMessage/plNetMessage.cpp | 1376 ++ .../PubUtilLib/plNetMessage/plNetMessage.h | 997 ++ .../plNetMessage/plNetMessageCreatable.h | 77 + .../plNetMessage/plNetMsgHelpers.cpp | 547 + .../PubUtilLib/plNetMessage/plNetMsgHelpers.h | 327 + .../PubUtilLib/plNetMessage/plNetMsgVersion.h | 145 + .../plNetTransport/plNetTransport.cpp | 355 + .../plNetTransport/plNetTransport.h | 79 + .../plNetTransport/plNetTransportMember.cpp | 70 + .../plNetTransport/plNetTransportMember.h | 108 + .../plParticleSystem/plBoundInterface.cpp | 76 + .../plParticleSystem/plBoundInterface.h | 67 + .../plParticleSystem/plConvexVolume.cpp | 193 + .../plParticleSystem/plConvexVolume.h | 87 + .../plParticleSystem/plEffectTargetInfo.h | 82 + .../PubUtilLib/plParticleSystem/plParticle.h | 98 + .../plParticleSystem/plParticleApplicator.cpp | 119 + .../plParticleSystem/plParticleApplicator.h | 145 + .../plParticleSystem/plParticleCreatable.h | 73 + .../plParticleSystem/plParticleEffect.cpp | 895 + .../plParticleSystem/plParticleEffect.h | 340 + .../plParticleSystem/plParticleEmitter.cpp | 569 + .../plParticleSystem/plParticleEmitter.h | 143 + .../plParticleSystem/plParticleGenerator.cpp | 438 + .../plParticleSystem/plParticleGenerator.h | 132 + .../plParticleSystem/plParticleSDLMod.cpp | 76 + .../plParticleSystem/plParticleSDLMod.h | 60 + .../plParticleSystem/plParticleSystem.cpp | 701 + .../plParticleSystem/plParticleSystem.h | 185 + .../PubUtilLib/plPhysX/plLOSDispatch.cpp | 296 + .../Plasma/PubUtilLib/plPhysX/plLOSDispatch.h | 51 + .../Plasma/PubUtilLib/plPhysX/plPXConvert.cpp | 66 + .../Plasma/PubUtilLib/plPhysX/plPXConvert.h | 63 + .../PubUtilLib/plPhysX/plPXPhysical.cpp | 1450 ++ .../Plasma/PubUtilLib/plPhysX/plPXPhysical.h | 277 + .../plPhysX/plPXPhysicalController.cpp | 1263 ++ .../plPhysX/plPXPhysicalController.h | 195 + .../plPhysX/plPXPhysicalControllerCore.cpp | 1225 ++ .../plPhysX/plPXPhysicalControllerCore.h | 145 + .../Plasma/PubUtilLib/plPhysX/plPXStream.h | 57 + .../PubUtilLib/plPhysX/plPhysXCreatable.h | 55 + .../PubUtilLib/plPhysX/plSimulationMgr.cpp | 1029 ++ .../PubUtilLib/plPhysX/plSimulationMgr.h | 193 + .../plPhysical/plCollisionDetector.cpp | 1155 ++ .../plPhysical/plCollisionDetector.h | 290 + .../plPhysical/plDetectorModifier.h | 85 + .../plPhysical/plEnvEffectDetector.cpp | 76 + .../plPhysical/plEnvEffectDetector.h | 57 + .../plPhysical/plPhysicalCreatable.h | 58 + .../PubUtilLib/plPhysical/plPhysicalProxy.cpp | 95 + .../PubUtilLib/plPhysical/plPhysicalProxy.h | 54 + .../plPhysical/plPhysicalSDLModifier.cpp | 256 + .../plPhysical/plPhysicalSDLModifier.h | 61 + .../plPhysical/plPhysicalSndGroup.cpp | 167 + .../plPhysical/plPhysicalSndGroup.h | 103 + .../plPhysical/plPhysicsSoundMgr.cpp | 216 + .../PubUtilLib/plPhysical/plPhysicsSoundMgr.h | 78 + .../plPhysical/plPickingDetector.cpp | 95 + .../PubUtilLib/plPhysical/plPickingDetector.h | 66 + .../Plasma/PubUtilLib/plPhysical/plSimDefs.h | 123 + .../plPhysical/plSittingModifier.cpp | 26 + .../PubUtilLib/plPhysical/plSittingModifier.h | 26 + .../PubUtilLib/plPipeline/hsFogControl.h | 135 + .../plPipeline/hsG3DDeviceSelector.cpp | 1968 +++ .../plPipeline/hsG3DDeviceSelector.h | 468 + .../PubUtilLib/plPipeline/hsGColorizer.cpp | 69 + .../PubUtilLib/plPipeline/hsGColorizer.h | 50 + .../PubUtilLib/plPipeline/hsGDDrawDllLoad.cpp | 57 + .../PubUtilLib/plPipeline/hsGDDrawDllLoad.h | 43 + .../PubUtilLib/plPipeline/hsGDeviceRef.h | 61 + .../PubUtilLib/plPipeline/hsGEnviron.cpp | 1254 ++ .../Plasma/PubUtilLib/plPipeline/hsGEnviron.h | 273 + .../Plasma/PubUtilLib/plPipeline/hsWinRef.h | 46 + .../PubUtilLib/plPipeline/plCaptureRender.cpp | 219 + .../PubUtilLib/plPipeline/plCaptureRender.h | 93 + .../plPipeline/plCubicRenderTarget.h | 121 + .../plCubicRenderTargetModifier.cpp | 243 + .../plPipeline/plCubicRenderTargetModifier.h | 87 + .../PubUtilLib/plPipeline/plCullTree.cpp | 1071 ++ .../Plasma/PubUtilLib/plPipeline/plCullTree.h | 207 + .../Plasma/PubUtilLib/plPipeline/plCuller.h | 44 + .../PubUtilLib/plPipeline/plDTProgressMgr.cpp | 238 + .../PubUtilLib/plPipeline/plDTProgressMgr.h | 72 + .../PubUtilLib/plPipeline/plDXBufferRefs.h | 155 + .../PubUtilLib/plPipeline/plDXDeviceRef.h | 66 + .../PubUtilLib/plPipeline/plDXDeviceRefs.cpp | 514 + .../PubUtilLib/plPipeline/plDXEnumerate.cpp | 893 + .../PubUtilLib/plPipeline/plDXEnumerate.h | 184 + .../PubUtilLib/plPipeline/plDXLightRef.h | 81 + .../PubUtilLib/plPipeline/plDXPipeline.cpp | 14396 ++++++++++++++++ .../PubUtilLib/plPipeline/plDXPipeline.h | 818 + .../PubUtilLib/plPipeline/plDXPixelShader.cpp | 149 + .../PubUtilLib/plPipeline/plDXPixelShader.h | 56 + .../plPipeline/plDXRenderTargetRef.h | 89 + .../PubUtilLib/plPipeline/plDXSettings.h | 285 + .../PubUtilLib/plPipeline/plDXShader.cpp | 86 + .../Plasma/PubUtilLib/plPipeline/plDXShader.h | 56 + .../PubUtilLib/plPipeline/plDXTextFont.cpp | 308 + .../PubUtilLib/plPipeline/plDXTextFont.h | 74 + .../PubUtilLib/plPipeline/plDXTextureRef.h | 107 + .../plPipeline/plDXVertexShader.cpp | 152 + .../PubUtilLib/plPipeline/plDXVertexShader.h | 57 + .../PubUtilLib/plPipeline/plDebugText.cpp | 288 + .../PubUtilLib/plPipeline/plDebugText.h | 187 + .../Plasma/PubUtilLib/plPipeline/plDrawPrim.h | 88 + .../PubUtilLib/plPipeline/plDynamicEnvMap.cpp | 952 + .../PubUtilLib/plPipeline/plDynamicEnvMap.h | 209 + .../plPipeline/plFogEnvironment.cpp | 197 + .../PubUtilLib/plPipeline/plFogEnvironment.h | 104 + .../PubUtilLib/plPipeline/plGBufferGroup.cpp | 1277 ++ .../PubUtilLib/plPipeline/plGBufferGroup.h | 292 + .../PubUtilLib/plPipeline/plPipeDebugFlags.h | 73 + .../plPipeline/plPipelineCreatable.h | 66 + .../PubUtilLib/plPipeline/plPipelineCreate.h | 75 + .../Plasma/PubUtilLib/plPipeline/plPlates.cpp | 1046 ++ .../Plasma/PubUtilLib/plPipeline/plPlates.h | 236 + .../PubUtilLib/plPipeline/plRenderTarget.cpp | 189 + .../PubUtilLib/plPipeline/plRenderTarget.h | 189 + .../plPipeline/plStatusLogDrawer.cpp | 112 + .../PubUtilLib/plPipeline/plStatusLogDrawer.h | 58 + .../Plasma/PubUtilLib/plPipeline/plStencil.h | 87 + .../PubUtilLib/plPipeline/plTextFont.cpp | 431 + .../Plasma/PubUtilLib/plPipeline/plTextFont.h | 141 + .../PubUtilLib/plPipeline/plTextGenerator.cpp | 594 + .../PubUtilLib/plPipeline/plTextGenerator.h | 124 + .../PubUtilLib/plPipeline/plTransitionMgr.cpp | 358 + .../PubUtilLib/plPipeline/plTransitionMgr.h | 90 + .../PubUtilLib/plPipeline/plVertCoder.cpp | 447 + .../PubUtilLib/plPipeline/plVertCoder.h | 106 + .../plProgressMgr/plProgressMgr.cpp | 387 + .../PubUtilLib/plProgressMgr/plProgressMgr.h | 234 + .../PubUtilLib/plResMgr/plBSDiffBuffer.cpp | 628 + .../PubUtilLib/plResMgr/plBSDiffBuffer.h | 111 + .../PubUtilLib/plResMgr/plDiffBuffer.cpp | 250 + .../Plasma/PubUtilLib/plResMgr/plDiffBuffer.h | 103 + .../PubUtilLib/plResMgr/plIndexFile.cpp | 195 + .../Plasma/PubUtilLib/plResMgr/plIndexFile.h | 152 + .../PubUtilLib/plResMgr/plKeyFinder.cpp | 498 + .../Plasma/PubUtilLib/plResMgr/plKeyFinder.h | 106 + .../Plasma/PubUtilLib/plResMgr/plLoc.cpp | 75 + .../Plasma/PubUtilLib/plResMgr/plLoc.h | 64 + .../PubUtilLib/plResMgr/plLocalization.cpp | 244 + .../PubUtilLib/plResMgr/plLocalization.h | 117 + .../Plasma/PubUtilLib/plResMgr/plPageInfo.cpp | 193 + .../Plasma/PubUtilLib/plResMgr/plPageInfo.h | 96 + .../PubUtilLib/plResMgr/plRegistryHelpers.cpp | 43 + .../PubUtilLib/plResMgr/plRegistryHelpers.h | 86 + .../PubUtilLib/plResMgr/plRegistryKeyList.cpp | 347 + .../PubUtilLib/plResMgr/plRegistryKeyList.h | 113 + .../PubUtilLib/plResMgr/plRegistryNode.cpp | 403 + .../PubUtilLib/plResMgr/plRegistryNode.h | 132 + .../PubUtilLib/plResMgr/plResManager.cpp | 1826 ++ .../Plasma/PubUtilLib/plResMgr/plResManager.h | 227 + .../plResMgr/plResManagerHelper.cpp | 725 + .../PubUtilLib/plResMgr/plResManagerHelper.h | 130 + .../Plasma/PubUtilLib/plResMgr/plResMgr.cpp | 29 + .../Plasma/PubUtilLib/plResMgr/plResMgr.h | 26 + .../PubUtilLib/plResMgr/plResMgrCreatable.h | 37 + .../PubUtilLib/plResMgr/plResMgrSettings.h | 92 + .../Plasma/PubUtilLib/plResMgr/plVersion.cpp | 91 + .../Plasma/PubUtilLib/plResMgr/plVersion.h | 277 + .../Plasma/PubUtilLib/plSDL/SDL/animation.sdl | 72 + .../Plasma/PubUtilLib/plSDL/SDL/avatar.sdl | 150 + .../PubUtilLib/plSDL/SDL/cloneMessage.sdl | 23 + .../Plasma/PubUtilLib/plSDL/SDL/clothing.sdl | 46 + .../Plasma/PubUtilLib/plSDL/SDL/morph.sdl | 28 + .../Plasma/PubUtilLib/plSDL/SDL/particle.sdl | 17 + .../Plasma/PubUtilLib/plSDL/SDL/physical.sdl | 23 + .../Plasma/PubUtilLib/plSDL/SDL/responder.sdl | 24 + .../Plasma/PubUtilLib/plSDL/SDL/sound.sdl | 29 + .../Plasma/PubUtilLib/plSDL/SDL/xregion.sdl | 15 + .../Sources/Plasma/PubUtilLib/plSDL/plSDL.h | 552 + .../Plasma/PubUtilLib/plSDL/plSDLCreatable.h | 33 + .../Plasma/PubUtilLib/plSDL/plSDLDescriptor.h | 240 + .../Plasma/PubUtilLib/plSDL/plSDLMgr.cpp | 191 + .../Plasma/PubUtilLib/plSDL/plSDLParser.cpp | 422 + .../plSDL/plStateChangeNotifier.cpp | 125 + .../PubUtilLib/plSDL/plStateDataRecord.cpp | 831 + .../PubUtilLib/plSDL/plStateDescriptor.cpp | 131 + .../PubUtilLib/plSDL/plStateVariable.cpp | 2762 +++ .../PubUtilLib/plSDL/plVarDescriptor.cpp | 404 + .../PubUtilLib/plSDLBrowser/plSDLBrowser.rc | 106 + .../plSDLBrowser/plSDLBrowserDlg.cpp | 171 + .../PubUtilLib/plSDLBrowser/plSDLBrowserDlg.h | 90 + .../plSDLBrowser/plSDLBrowserDlgHandlers.cpp | 167 + .../Plasma/PubUtilLib/plSDLBrowser/resource.h | 22 + .../Plasma/PubUtilLib/plScene/plCullPoly.cpp | 186 + .../Plasma/PubUtilLib/plScene/plCullPoly.h | 82 + .../Plasma/PubUtilLib/plScene/plOccTree.cpp | 221 + .../Plasma/PubUtilLib/plScene/plOccTree.h | 164 + .../Plasma/PubUtilLib/plScene/plOccluder.cpp | 387 + .../Plasma/PubUtilLib/plScene/plOccluder.h | 153 + .../PubUtilLib/plScene/plOccluderProxy.cpp | 66 + .../PubUtilLib/plScene/plOccluderProxy.h | 51 + .../PubUtilLib/plScene/plPageTreeMgr.cpp | 696 + .../Plasma/PubUtilLib/plScene/plPageTreeMgr.h | 117 + .../PubUtilLib/plScene/plPostEffectMod.cpp | 321 + .../PubUtilLib/plScene/plPostEffectMod.h | 126 + .../PubUtilLib/plScene/plRelevanceMgr.cpp | 255 + .../PubUtilLib/plScene/plRelevanceMgr.h | 76 + .../PubUtilLib/plScene/plRelevanceRegion.cpp | 82 + .../PubUtilLib/plScene/plRelevanceRegion.h | 61 + .../PubUtilLib/plScene/plRenderRequest.cpp | 154 + .../PubUtilLib/plScene/plRenderRequest.h | 191 + .../PubUtilLib/plScene/plSceneCreatable.h | 61 + .../Plasma/PubUtilLib/plScene/plSceneNode.cpp | 507 + .../Plasma/PubUtilLib/plScene/plSceneNode.h | 139 + .../Plasma/PubUtilLib/plScene/plVisMgr.cpp | 207 + .../Plasma/PubUtilLib/plScene/plVisMgr.h | 122 + .../Plasma/PubUtilLib/plScene/plVisRegion.cpp | 129 + .../Plasma/PubUtilLib/plScene/plVisRegion.h | 93 + .../plSockets/plBufferedSocketReader.cpp | 196 + .../plSockets/plBufferedSocketReader.h | 61 + .../plSockets/plBufferedSocketWriter.cpp | 109 + .../plSockets/plBufferedSocketWriter.h | 62 + .../Plasma/PubUtilLib/plSockets/plFdSet.cpp | 157 + .../Plasma/PubUtilLib/plSockets/plFdSet.h | 62 + .../PubUtilLib/plSockets/plMemBuffer.cpp | 135 + .../Plasma/PubUtilLib/plSockets/plMemBuffer.h | 55 + .../Plasma/PubUtilLib/plSockets/plNet.cpp | 278 + .../Plasma/PubUtilLib/plSockets/plNet.h | 101 + .../plSockets/plOutgoingUdpSocket.cpp | 95 + .../plSockets/plOutgoingUdpSocket.h | 60 + .../PubUtilLib/plSockets/plRingBuffer.cpp | 136 + .../PubUtilLib/plSockets/plRingBuffer.h | 58 + .../Plasma/PubUtilLib/plSockets/plSocket.cpp | 195 + .../Plasma/PubUtilLib/plSockets/plSocket.h | 82 + .../plSockets/plTcpListenSocket.cpp | 75 + .../PubUtilLib/plSockets/plTcpListenSocket.h | 43 + .../PubUtilLib/plSockets/plTcpSocket.cpp | 140 + .../Plasma/PubUtilLib/plSockets/plTcpSocket.h | 63 + .../PubUtilLib/plStatGather/plAutoProfile.cpp | 383 + .../PubUtilLib/plStatGather/plAutoProfile.h | 46 + .../plStatGather/plCalculatedProfiles.cpp | 304 + .../plStatGather/plCalculatedProfiles.h | 29 + .../plStatGather/plProfileManagerFull.cpp | 676 + .../plStatGather/plProfileManagerFull.h | 115 + .../plStatGather/plStatGatherCreatable.h | 34 + .../plStatusLog/plEncryptLogLine.cpp | 79 + .../PubUtilLib/plStatusLog/plEncryptLogLine.h | 48 + .../PubUtilLib/plStatusLog/plLoggable.cpp | 144 + .../PubUtilLib/plStatusLog/plLoggable.h | 69 + .../PubUtilLib/plStatusLog/plStatusLog.cpp | 906 + .../PubUtilLib/plStatusLog/plStatusLog.h | 261 + .../plStreamLogger/plStreamLogger.cpp | 204 + .../plStreamLogger/plStreamLogger.h | 143 + .../plStreamLogger/plStreamLoggerC.h | 25 + .../plSurface/ShaderSrc/AssShader.zip | Bin 0 -> 9619 bytes .../plSurface/ShaderSrc/ps_BiasNormals.inl | 17 + .../plSurface/ShaderSrc/ps_CaddAadd.inl | 14 + .../plSurface/ShaderSrc/ps_CaddAbase.inl | 14 + .../plSurface/ShaderSrc/ps_CaddAmult.inl | 14 + .../plSurface/ShaderSrc/ps_CalphaAadd.inl | 14 + .../plSurface/ShaderSrc/ps_CalphaAbase.inl | 14 + .../plSurface/ShaderSrc/ps_CalphaAmult.inl | 14 + .../plSurface/ShaderSrc/ps_CbaseAbase.inl | 9 + .../plSurface/ShaderSrc/ps_CmultAadd.inl | 14 + .../plSurface/ShaderSrc/ps_CmultAbase.inl | 14 + .../plSurface/ShaderSrc/ps_CmultAmult.inl | 14 + .../plSurface/ShaderSrc/ps_CompCosines.inl | 31 + .../plSurface/ShaderSrc/ps_GrassShader.inl | 6 + .../plSurface/ShaderSrc/ps_MoreCosines.inl | 35 + .../plSurface/ShaderSrc/ps_ShoreLeave6.inl | 21 + .../plSurface/ShaderSrc/ps_WaveDecEnv.inl | 35 + .../plSurface/ShaderSrc/ps_WaveFixed.inl | 77 + .../plSurface/ShaderSrc/ps_WaveGraph.inl | 30 + .../plSurface/ShaderSrc/ps_WaveGrid.inl | 63 + .../plSurface/ShaderSrc/ps_WaveRip.inl | 21 + .../plSurface/ShaderSrc/vs_BiasNormals.inl | 34 + .../plSurface/ShaderSrc/vs_CompCosines.inl | 31 + .../plSurface/ShaderSrc/vs_GrassShader.inl | 60 + .../plSurface/ShaderSrc/vs_ShoreLeave6.inl | 245 + .../plSurface/ShaderSrc/vs_ShoreLeave7.inl | 203 + .../plSurface/ShaderSrc/vs_WaveDec1Lay.inl | 207 + .../plSurface/ShaderSrc/vs_WaveDec1Lay_7.inl | 189 + .../plSurface/ShaderSrc/vs_WaveDec2Lay11.inl | 209 + .../ShaderSrc/vs_WaveDec2Lay11_7.inl | 191 + .../plSurface/ShaderSrc/vs_WaveDec2Lay12.inl | 210 + .../ShaderSrc/vs_WaveDec2Lay12_7.inl | 192 + .../plSurface/ShaderSrc/vs_WaveDecEnv.inl | 298 + .../plSurface/ShaderSrc/vs_WaveDecEnv_7.inl | 331 + .../plSurface/ShaderSrc/vs_WaveFixedFin6.inl | 449 + .../plSurface/ShaderSrc/vs_WaveFixedFin7.inl | 437 + .../plSurface/ShaderSrc/vs_WaveGraph2.inl | 166 + .../plSurface/ShaderSrc/vs_WaveGridFin.inl | 471 + .../plSurface/ShaderSrc/vs_WaveRip.inl | 243 + .../plSurface/ShaderSrc/vs_WaveRip7.inl | 226 + .../PubUtilLib/plSurface/hsGMaterial.cpp | 322 + .../Plasma/PubUtilLib/plSurface/hsGMaterial.h | 130 + .../PubUtilLib/plSurface/plGrassShaderMod.cpp | 259 + .../PubUtilLib/plSurface/plGrassShaderMod.h | 116 + .../Plasma/PubUtilLib/plSurface/plLayer.cpp | 293 + .../Plasma/PubUtilLib/plSurface/plLayer.h | 83 + .../PubUtilLib/plSurface/plLayerAnimation.cpp | 742 + .../PubUtilLib/plSurface/plLayerAnimation.h | 196 + .../PubUtilLib/plSurface/plLayerDepth.cpp | 47 + .../PubUtilLib/plSurface/plLayerDepth.h | 43 + .../PubUtilLib/plSurface/plLayerInterface.cpp | 359 + .../PubUtilLib/plSurface/plLayerInterface.h | 204 + .../PubUtilLib/plSurface/plLayerMultiply.cpp | 168 + .../PubUtilLib/plSurface/plLayerMultiply.h | 64 + .../Plasma/PubUtilLib/plSurface/plLayerOr.cpp | 79 + .../Plasma/PubUtilLib/plSurface/plLayerOr.h | 58 + .../plSurface/plLayerShadowBase.cpp | 144 + .../PubUtilLib/plSurface/plLayerShadowBase.h | 74 + .../PubUtilLib/plSurface/plLayerWrapper.cpp | 42 + .../PubUtilLib/plSurface/plLayerWrapper.h | 55 + .../Plasma/PubUtilLib/plSurface/plShader.cpp | 321 + .../Plasma/PubUtilLib/plSurface/plShader.h | 266 + .../PubUtilLib/plSurface/plShaderTable.cpp | 103 + .../PubUtilLib/plSurface/plShaderTable.h | 160 + .../PubUtilLib/plSurface/plSurfaceCreatable.h | 72 + .../PubUtilLib/plSurface/ps_BiasNormals.h | 55 + .../Plasma/PubUtilLib/plSurface/ps_CaddAAdd.h | 54 + .../PubUtilLib/plSurface/ps_CaddAMult.h | 54 + .../PubUtilLib/plSurface/ps_CaddAbase.h | 53 + .../PubUtilLib/plSurface/ps_CalphaAMult.h | 55 + .../PubUtilLib/plSurface/ps_CalphaAadd.h | 55 + .../PubUtilLib/plSurface/ps_CalphaAbase.h | 54 + .../PubUtilLib/plSurface/ps_CbaseAbase.h | 44 + .../PubUtilLib/plSurface/ps_CmultAAdd.h | 54 + .../PubUtilLib/plSurface/ps_CmultAMult.h | 54 + .../PubUtilLib/plSurface/ps_CmultAbase.h | 53 + .../PubUtilLib/plSurface/ps_CompCosines.h | 73 + .../PubUtilLib/plSurface/ps_GrassShader.h | 44 + .../PubUtilLib/plSurface/ps_MoreCosines.h | 76 + .../PubUtilLib/plSurface/ps_ShoreLeave6.h | 79 + .../PubUtilLib/plSurface/ps_WaveDecEnv.h | 57 + .../PubUtilLib/plSurface/ps_WaveFixed.h | 63 + .../PubUtilLib/plSurface/ps_WaveGraph.h | 56 + .../Plasma/PubUtilLib/plSurface/ps_WaveGrid.h | 57 + .../Plasma/PubUtilLib/plSurface/ps_WaveRip.h | 44 + .../PubUtilLib/plSurface/vs_BiasNormals.h | 78 + .../PubUtilLib/plSurface/vs_CompCosines.h | 78 + .../PubUtilLib/plSurface/vs_GrassShader.h | 161 + .../PubUtilLib/plSurface/vs_ShoreLeave6.h | 341 + .../PubUtilLib/plSurface/vs_ShoreLeave7.h | 279 + .../PubUtilLib/plSurface/vs_WaveDec1Lay.h | 311 + .../PubUtilLib/plSurface/vs_WaveDec1Lay_7.h | 277 + .../PubUtilLib/plSurface/vs_WaveDec2Lay11.h | 322 + .../PubUtilLib/plSurface/vs_WaveDec2Lay11_7.h | 288 + .../PubUtilLib/plSurface/vs_WaveDec2Lay12.h | 325 + .../PubUtilLib/plSurface/vs_WaveDec2Lay12_7.h | 291 + .../PubUtilLib/plSurface/vs_WaveDecEnv.h | 440 + .../PubUtilLib/plSurface/vs_WaveDecEnv_7.h | 474 + .../PubUtilLib/plSurface/vs_WaveFixedFin6.h | 518 + .../PubUtilLib/plSurface/vs_WaveFixedFin7.h | 505 + .../PubUtilLib/plSurface/vs_WaveGraph2.h | 188 + .../PubUtilLib/plSurface/vs_WaveGridFin.h | 468 + .../Plasma/PubUtilLib/plSurface/vs_WaveRip.h | 372 + .../Plasma/PubUtilLib/plSurface/vs_WaveRip7.h | 338 + .../PubUtilLib/plTransform/hsAffineParts.cpp | 426 + .../PubUtilLib/plTransform/hsAffineParts.h | 73 + .../Plasma/PubUtilLib/plTransform/hsEuler.cpp | 212 + .../Plasma/PubUtilLib/plTransform/hsEuler.h | 116 + .../PubUtilLib/plTransform/mat_decomp.cpp | 536 + .../PubUtilLib/plTransform/mat_decomp.h | 57 + .../Plasma/PubUtilLib/plTransform/notes.txt | 1 + .../Plasma/PubUtilLib/plUUID/plUUID.cpp | 105 + .../Sources/Plasma/PubUtilLib/plUUID/plUUID.h | 88 + .../Plasma/PubUtilLib/plUUID/plUUID_Unix.cpp | 128 + .../Plasma/PubUtilLib/plUUID/plUUID_Win32.cpp | 86 + .../plUnifiedTime/plClientUnifiedTime.cpp | 120 + .../plUnifiedTime/plClientUnifiedTime.h | 60 + .../PubUtilLib/plUnifiedTime/plTimeSpan.cpp | 50 + .../PubUtilLib/plUnifiedTime/plTimeSpan.h | 48 + .../plUnifiedTime/plUnifiedTime.cpp | 1032 ++ .../PubUtilLib/plUnifiedTime/plUnifiedTime.h | 229 + .../plUnifiedTime/plUnifiedTimeCreatable.h | 36 + .../Plasma/PubUtilLib/plVault/Intern.h | 35 + .../Sources/Plasma/PubUtilLib/plVault/Pch.h | 76 + .../PubUtilLib/plVault/plAgeInfoSource.h | 46 + .../plVault/plDniCoordinateInfo.cpp | 85 + .../PubUtilLib/plVault/plDniCoordinateInfo.h | 72 + .../Plasma/PubUtilLib/plVault/plVault.cpp | 34 + .../Plasma/PubUtilLib/plVault/plVault.h | 48 + .../PubUtilLib/plVault/plVaultClientApi.cpp | 4608 +++++ .../PubUtilLib/plVault/plVaultClientApi.h | 478 + .../PubUtilLib/plVault/plVaultConstants.cpp | 126 + .../PubUtilLib/plVault/plVaultConstants.h | 175 + .../PubUtilLib/plVault/plVaultCreatable.h | 34 + .../Plasma/PubUtilLib/plVault/plVaultNode.cpp | 2743 +++ .../Plasma/PubUtilLib/plVault/plVaultNode.h | 1013 ++ .../PubUtilLib/plVault/plVaultNodeAccess.cpp | 1073 ++ .../PubUtilLib/plVault/plVaultNodeAccess.h | 457 + .../PubUtilLib/plWinStrBlock/strblock.c | 691 + .../PubUtilLib/plWinStrBlock/strblock.h | 244 + .../Plasma/PubUtilLib/plWndCtrls/basewnd.cpp | 109 + .../Plasma/PubUtilLib/plWndCtrls/basewnd.h | 58 + .../Plasma/PubUtilLib/plWndCtrls/plButton.h | 93 + .../Plasma/PubUtilLib/plWndCtrls/plCheckBox.h | 85 + .../Plasma/PubUtilLib/plWndCtrls/plComboBox.h | 213 + .../Plasma/PubUtilLib/plWndCtrls/plControl.h | 95 + .../Plasma/PubUtilLib/plWndCtrls/plDialog.h | 135 + .../Plasma/PubUtilLib/plWndCtrls/plEdit.h | 146 + .../Plasma/PubUtilLib/plWndCtrls/plLabel.h | 59 + .../Plasma/PubUtilLib/plWndCtrls/plListBox.h | 236 + .../PubUtilLib/plWndCtrls/plProgressBar.h | 90 + .../PubUtilLib/plWndCtrls/plRadioButton.h | 81 + .../PubUtilLib/plWndCtrls/plStatusBar.h | 68 + .../Plasma/PubUtilLib/plWndCtrls/plTrackBar.h | 102 + .../Plasma/PubUtilLib/plWndCtrls/plWindow.h | 661 + .../PubUtilLib/plWndCtrls/plWndCtrls.cpp | 91 + .../Plasma/PubUtilLib/plWndCtrls/plWndCtrls.h | 307 + .../Plasma/PubUtilLib/plWndCtrls/webhost.cpp | 495 + .../Plasma/PubUtilLib/plWndCtrls/webhost.h | 168 + .../pyNetClientComm/pyNetClientComm.cpp | 316 + .../pyNetClientComm/pyNetClientComm.h | 193 + .../pyNetClientComm/pyNetClientCommGlue.cpp | 320 + .../pyNetClientGame/pyNetClientGame.cpp | 25 + .../pyNetClientGame/pyNetClientGame.h | 25 + .../Plasma/PythonLib/pyPlasma/creatables.cpp | 34 + .../Plasma/PythonLib/pyPlasma/dllmain.cpp | 139 + .../Plasma/PythonLib/pyPlasma/pyPlasmaTest.py | 147 + .../Plasma/PythonLib/pyPloticus/dllmain.cpp | 39 + .../PythonLib/pyPloticus/pyPloticus.cpp | 200 + .../Plasma/PythonLib/pyPloticus/pyPloticus.h | 47 + .../Plasma/PythonLib/pyVault/pyVNodeMgr.cpp | 487 + .../Plasma/PythonLib/pyVault/pyVNodeMgr.h | 231 + .../PythonLib/pyVault/pyVNodeMgrGlue.cpp | 461 + .../Tools/CheckFolderVar/CheckFolderVar.cpp | 150 + .../Sources/Tools/MaxAss/ValdezInterface.h | 96 + .../Sources/Tools/MaxComponent/BipedKiller.h | 29 + .../Tools/MaxComponent/ComponentDummies.h | 77 + .../Tools/MaxComponent/WavFileStructs.h | 25 + .../Sources/Tools/MaxComponent/icon1.ico | Bin 0 -> 766 bytes .../Tools/MaxComponent/pfGUISkinComp.cpp | 770 + .../Tools/MaxComponent/pfGUISkinComp.h | 154 + .../Tools/MaxComponent/plAGComponents.cpp | 445 + .../MaxComponent/plActivatorBaseComponent.cpp | 97 + .../MaxComponent/plActivatorBaseComponent.h | 68 + .../MaxComponent/plActivatorComponent.cpp | 54 + .../Tools/MaxComponent/plActivatorComponent.h | 25 + .../Tools/MaxComponent/plAnimCompProc.cpp | 425 + .../Tools/MaxComponent/plAnimCompProc.h | 108 + .../Tools/MaxComponent/plAnimComponent.cpp | 1241 ++ .../Tools/MaxComponent/plAnimComponent.h | 198 + .../MaxComponent/plAnimEventComponent.cpp | 615 + .../Tools/MaxComponent/plAnimEventComponent.h | 60 + .../Tools/MaxComponent/plAnimObjInterface.h | 77 + .../Tools/MaxComponent/plAudioComponents.cpp | 3985 +++++ .../Tools/MaxComponent/plAudioComponents.h | 192 + .../Tools/MaxComponent/plAutoComponent.cpp | 83 + .../Tools/MaxComponent/plAutoUIBase.cpp | 560 + .../Sources/Tools/MaxComponent/plAutoUIBase.h | 123 + .../Tools/MaxComponent/plAutoUIBlock.cpp | 66 + .../Tools/MaxComponent/plAutoUIBlock.h | 50 + .../Tools/MaxComponent/plAutoUIComp.cpp | 93 + .../Sources/Tools/MaxComponent/plAutoUIComp.h | 102 + .../Tools/MaxComponent/plAutoUIParams.cpp | 1864 ++ .../Tools/MaxComponent/plAutoUIParams.h | 407 + .../Tools/MaxComponent/plAvatarComponent.cpp | 1220 ++ .../Tools/MaxComponent/plAvatarComponent.h | 227 + .../MaxComponent/plBehavioralComponents.cpp | 170 + .../MaxComponent/plBehavioralComponents.h | 57 + .../Tools/MaxComponent/plBipedKiller.cpp | 758 + .../Tools/MaxComponent/plBlowComponent.cpp | 189 + .../Tools/MaxComponent/plBlowComponent.h | 54 + .../Tools/MaxComponent/plCAnimParamBlock.cpp | 158 + .../Tools/MaxComponent/plCAnimParamBlock.h | 130 + .../Tools/MaxComponent/plCameraComponent.cpp | 162 + .../Tools/MaxComponent/plCameraComponent.h | 48 + .../Tools/MaxComponent/plCameraComponents.cpp | 2559 +++ .../Tools/MaxComponent/plCameraComponents.h | 238 + .../MaxComponent/plClickDragComponent.cpp | 605 + .../Tools/MaxComponent/plClickDragComponent.h | 56 + .../MaxComponent/plClickableComponent.cpp | 409 + .../Tools/MaxComponent/plClickableComponent.h | 46 + .../Tools/MaxComponent/plClimbComponent.cpp | 403 + .../Tools/MaxComponent/plClimbComponent.h | 64 + .../MaxComponent/plClothingComponent.cpp | 345 + .../Tools/MaxComponent/plClothingComponent.h | 64 + .../Tools/MaxComponent/plClusterComponent.cpp | 1179 ++ .../Tools/MaxComponent/plClusterComponent.h | 132 + .../Tools/MaxComponent/plComponent.cpp | 31 + .../Sources/Tools/MaxComponent/plComponent.h | 246 + .../Sources/Tools/MaxComponent/plComponent.rc | 7332 ++++++++ .../Tools/MaxComponent/plComponentBase.cpp | 694 + .../Tools/MaxComponent/plComponentBase.h | 387 + .../Tools/MaxComponent/plComponentExt.h | 51 + .../Tools/MaxComponent/plComponentMgr.cpp | 119 + .../Tools/MaxComponent/plComponentMgr.h | 79 + .../Tools/MaxComponent/plComponentProcBase.h | 122 + .../Tools/MaxComponent/plComponentReg.h | 143 + .../Tools/MaxComponent/plComponentTools.cpp | 70 + .../Tools/MaxComponent/plComponentTools.h | 100 + .../Sources/Tools/MaxComponent/plDicer.cpp | 259 + .../Sources/Tools/MaxComponent/plDicer.h | 66 + .../Tools/MaxComponent/plDistribComponent.cpp | 1266 ++ .../Tools/MaxComponent/plDistribComponent.h | 209 + .../MaxComponent/plDistribComponent_old.cpp | 875 + .../MaxComponent/plDistribComponent_old.h | 167 + .../MaxComponent/plExcludeRegionComponent.cpp | 209 + .../MaxComponent/plExcludeRegionComponent.h | 59 + .../MaxComponent/plFlexibilityComponent.h | 54 + .../MaxComponent/plFootPrintComponent.cpp | 1794 ++ .../MaxComponent/plFootstepComponent.cpp | 186 + .../Tools/MaxComponent/plFootstepComponent.h | 61 + .../Tools/MaxComponent/plGUICompClassIDs.h | 56 + .../Tools/MaxComponent/plGUIComponents.cpp | 5059 ++++++ .../Tools/MaxComponent/plGUIComponents.h | 222 + .../Tools/MaxComponent/plGrassComponent.cpp | 302 + .../Tools/MaxComponent/plGrassComponent.h | 69 + .../Tools/MaxComponent/plIgnoreComponent.cpp | 417 + .../MaxComponent/plImpactGadgetComponent.cpp | 180 + .../MaxComponent/plImpactGadgetComponent.h | 42 + .../MaxComponent/plInventoryObjComponent.cpp | 261 + .../MaxComponent/plInventoryObjComponent.h | 110 + .../Tools/MaxComponent/plLODFadeComponent.cpp | 789 + .../Tools/MaxComponent/plLODFadeComponent.h | 135 + .../MaxComponent/plLightGrpComponent.cpp | 298 + .../Tools/MaxComponent/plLightGrpComponent.h | 66 + .../MaxComponent/plLightMapComponent.cpp | 189 + .../Tools/MaxComponent/plLightMapComponent.h | 63 + .../Tools/MaxComponent/plLineFollowComp.cpp | 842 + .../Tools/MaxComponent/plMaxAnimUtils.cpp | 369 + .../Tools/MaxComponent/plMaxAnimUtils.h | 75 + .../Tools/MaxComponent/plMaxWaveUtils.cpp | 48 + .../Tools/MaxComponent/plMaxWaveUtils.h | 33 + .../Tools/MaxComponent/plMiscComponents.cpp | 2557 +++ .../Tools/MaxComponent/plMiscComponents.h | 119 + .../Tools/MaxComponent/plMorphSeqComp.cpp | 362 + .../MaxComponent/plMultistageBehComponent.cpp | 605 + .../MaxComponent/plMultistageBehComponent.h | 39 + .../Tools/MaxComponent/plMultistageStage.cpp | 474 + .../Tools/MaxComponent/plMultistageStage.h | 116 + .../Tools/MaxComponent/plNPCSpawnComp.cpp | 213 + .../Tools/MaxComponent/plNPCSpawnComp.h | 34 + .../MaxComponent/plNavigableComponents.cpp | 280 + .../MaxComponent/plNavigableComponents.h | 62 + .../Tools/MaxComponent/plNoteTrackDlgComp.cpp | 44 + .../Tools/MaxComponent/plNoteTrackDlgComp.h | 41 + .../Tools/MaxComponent/plNotetrackAnim.cpp | 250 + .../Tools/MaxComponent/plNotetrackAnim.h | 79 + .../Tools/MaxComponent/plNotetrackDlg.cpp | 183 + .../Tools/MaxComponent/plNotetrackDlg.h | 79 + .../MaxComponent/plObjectFlockerComponent.cpp | 271 + .../MaxComponent/plObjectFlockerComponent.h | 65 + .../Tools/MaxComponent/plOneShotComponent.cpp | 257 + .../Tools/MaxComponent/plOneShotComponent.h | 37 + .../MaxComponent/plParticleComponents.cpp | 1611 ++ .../Tools/MaxComponent/plParticleComponents.h | 315 + .../plPhysConstraintComponents.cpp | 1092 ++ .../MaxComponent/plPhysicalComponents.cpp | 1999 +++ .../Tools/MaxComponent/plPhysicalComponents.h | 181 + .../Tools/MaxComponent/plPhysicsGroups.h | 69 + .../MaxComponent/plPickLocalizationDlg.cpp | 249 + .../MaxComponent/plPickLocalizationDlg.h | 54 + .../Tools/MaxComponent/plPickMaterialMap.cpp | 179 + .../Tools/MaxComponent/plPickMaterialMap.h | 48 + .../Sources/Tools/MaxComponent/plPickNode.cpp | 331 + .../Sources/Tools/MaxComponent/plPickNode.h | 61 + .../Tools/MaxComponent/plPickNodeBase.cpp | 253 + .../Tools/MaxComponent/plPickNodeBase.h | 104 + .../Tools/MaxComponent/plPickNodeComp.cpp | 252 + .../MaxComponent/plPythonFileComponent.cpp | 1311 ++ .../MaxComponent/plPythonFileComponent.h | 35 + .../Tools/MaxComponent/plRepComponent.cpp | 479 + .../Tools/MaxComponent/plResponderAnim.cpp | 774 + .../Tools/MaxComponent/plResponderAnim.h | 55 + .../Tools/MaxComponent/plResponderCmd.h | 78 + .../MaxComponent/plResponderComponent.cpp | 1328 ++ .../Tools/MaxComponent/plResponderComponent.h | 48 + .../MaxComponent/plResponderComponentPriv.h | 116 + .../Tools/MaxComponent/plResponderGetComp.cpp | 341 + .../Tools/MaxComponent/plResponderGetComp.h | 111 + .../Tools/MaxComponent/plResponderLink.cpp | 1765 ++ .../Tools/MaxComponent/plResponderLink.h | 236 + .../Tools/MaxComponent/plResponderMtl.cpp | 637 + .../Tools/MaxComponent/plResponderMtl.h | 64 + .../Tools/MaxComponent/plResponderWait.cpp | 470 + .../Tools/MaxComponent/plResponderWait.h | 50 + .../Tools/MaxComponent/plSeekPoint.cpp | 120 + .../Tools/MaxComponent/plShadowComponents.cpp | 448 + .../Tools/MaxComponent/plShadowComponents.h | 112 + .../Tools/MaxComponent/plSmoothComponent.cpp | 789 + .../MaxComponent/plSoftVolumeComponent.cpp | 1594 ++ .../MaxComponent/plSoftVolumeComponent.h | 146 + .../MaxComponent/plTemplateComponent.cpp | 217 + .../Tools/MaxComponent/plTypesComponents.cpp | 832 + .../MaxComponent/plVolumeGadgetComponent.cpp | 482 + .../MaxComponent/plVolumeGadgetComponent.h | 57 + .../Tools/MaxComponent/plWaterComponent.cpp | 1379 ++ .../Tools/MaxComponent/plWaterComponent.h | 242 + .../Tools/MaxComponent/plXImposter.cpp | 448 + .../Sources/Tools/MaxComponent/plXImposter.h | 50 + .../Sources/Tools/MaxComponent/resource.h | 1893 ++ .../Sources/Tools/MaxComponent/zoomin1.ico | Bin 0 -> 1078 bytes .../Tools/MaxConvert/StringTokenizer.cpp | 92 + .../Tools/MaxConvert/StringTokenizer.h | 51 + .../Sources/Tools/MaxConvert/UserPropMgr.cpp | 869 + .../Sources/Tools/MaxConvert/UserPropMgr.h | 143 + .../Tools/MaxConvert/hsControlConverter.cpp | 2153 +++ .../Tools/MaxConvert/hsControlConverter.h | 185 + .../Tools/MaxConvert/hsConverterUtils.cpp | 547 + .../Tools/MaxConvert/hsConverterUtils.h | 135 + .../Tools/MaxConvert/hsMaterialConverter.cpp | 5147 ++++++ .../Tools/MaxConvert/hsMaterialConverter.h | 351 + .../Sources/Tools/MaxConvert/hsMaxLayerBase.h | 188 + .../Tools/MaxConvert/hsVertexShader.cpp | 441 + .../Sources/Tools/MaxConvert/hsVertexShader.h | 85 + .../Tools/MaxConvert/plBitmapCreator.cpp | 684 + .../Tools/MaxConvert/plBitmapCreator.h | 113 + .../Tools/MaxConvert/plClusterUtil.cpp | 787 + .../Sources/Tools/MaxConvert/plClusterUtil.h | 134 + .../Sources/Tools/MaxConvert/plConvert.cpp | 498 + .../Sources/Tools/MaxConvert/plConvert.h | 115 + .../Sources/Tools/MaxConvert/plDistTree.cpp | 301 + .../Sources/Tools/MaxConvert/plDistTree.h | 103 + .../Tools/MaxConvert/plDistributor.cpp | 1467 ++ .../Sources/Tools/MaxConvert/plDistributor.h | 405 + .../Tools/MaxConvert/plLayerConverter.cpp | 1118 ++ .../Tools/MaxConvert/plLayerConverter.h | 123 + .../Tools/MaxConvert/plLightMapGen.cpp | 1614 ++ .../Sources/Tools/MaxConvert/plLightMapGen.h | 174 + .../Tools/MaxConvert/plMaxLightContext.h | 124 + .../Tools/MaxConvert/plMeshConverter.cpp | 2543 +++ .../Tools/MaxConvert/plMeshConverter.h | 129 + .../MaxConvert/plRenderGlobalContext.cpp | 205 + .../Tools/MaxConvert/plRenderGlobalContext.h | 57 + .../Tools/MaxConvert/plRenderInstance.cpp | 300 + .../Tools/MaxConvert/plRenderInstance.h | 87 + .../Sources/Tools/MaxExport/SimpleExport.cpp | 393 + .../Sources/Tools/MaxExport/SimpleExport.h | 108 + .../Sources/Tools/MaxExport/plErrorMsg.cpp | 209 + .../Sources/Tools/MaxExport/plErrorMsg.h | 97 + .../Sources/Tools/MaxExport/plExportDlg.cpp | 518 + .../Sources/Tools/MaxExport/plExportDlg.h | 49 + .../Tools/MaxExport/plExportErrorMsg.cpp | 126 + .../Tools/MaxExport/plExportErrorMsg.h | 133 + .../Tools/MaxExport/plExportLogErrorMsg.cpp | 187 + .../Tools/MaxExport/plExportLogErrorMsg.h | 156 + .../Tools/MaxExport/plExportProgressBar.cpp | 122 + .../Tools/MaxExport/plExportProgressBar.h | 53 + .../Sources/Tools/MaxExport/plProgressBar.h | 59 + .../Sources/Tools/MaxMain/GlobalUtility.cpp | 282 + .../Sources/Tools/MaxMain/GlobalUtility.h | 70 + .../Sources/Tools/MaxMain/MaxAllocDll.cpp | 98 + .../Sources/Tools/MaxMain/MaxAllocDll.h | 33 + .../Sources/Tools/MaxMain/SimpleExport.rc | 538 + .../Sources/Tools/MaxMain/indexes.cpp | 46 + .../Plasma20/Sources/Tools/MaxMain/main.cpp | 276 + .../Plasma20/Sources/Tools/MaxMain/main.def | 8 + .../Tools/MaxMain/plActionTableMgr.cpp | 141 + .../Sources/Tools/MaxMain/plActionTableMgr.h | 126 + .../Tools/MaxMain/plAgeDescInterface.cpp | 1331 ++ .../Tools/MaxMain/plAgeDescInterface.h | 121 + .../Sources/Tools/MaxMain/plCommonObjLib.cpp | 224 + .../Sources/Tools/MaxMain/plCommonObjLib.h | 92 + .../Sources/Tools/MaxMain/plComponentDlg.cpp | 1027 ++ .../Sources/Tools/MaxMain/plComponentDlg.h | 106 + .../Tools/MaxMain/plComponentPanel.cpp | 510 + .../Sources/Tools/MaxMain/plComponentPanel.h | 76 + .../Tools/MaxMain/plGetLocationDlg.cpp | 179 + .../Sources/Tools/MaxMain/plGetLocationDlg.h | 54 + .../Tools/MaxMain/plMaxAccelerators.cpp | 46 + .../Sources/Tools/MaxMain/plMaxAccelerators.h | 34 + .../Sources/Tools/MaxMain/plMaxCFGFile.cpp | 103 + .../Sources/Tools/MaxMain/plMaxCFGFile.h | 41 + .../Sources/Tools/MaxMain/plMaxMenu.cpp | 431 + .../Sources/Tools/MaxMain/plMaxMenu.h | 31 + .../Tools/MaxMain/plMaxMeshExtractor.cpp | 222 + .../Tools/MaxMain/plMaxMeshExtractor.h | 52 + .../Sources/Tools/MaxMain/plMaxNode.cpp | 4104 +++++ .../Sources/Tools/MaxMain/plMaxNode.h | 245 + .../Sources/Tools/MaxMain/plMaxNodeBase.cpp | 1185 ++ .../Sources/Tools/MaxMain/plMaxNodeBase.h | 299 + .../Sources/Tools/MaxMain/plMaxNodeData.h | 435 + .../Sources/Tools/MaxMain/plMaxUtils.cpp | 147 + .../Sources/Tools/MaxMain/plMaxUtils.h | 49 + .../Sources/Tools/MaxMain/plMtlCollector.cpp | 273 + .../Sources/Tools/MaxMain/plMtlCollector.h | 65 + .../Sources/Tools/MaxMain/plNodeLock.cpp | 58 + .../Sources/Tools/MaxMain/plNodeLock.h | 43 + .../Sources/Tools/MaxMain/plPhysXCooking.cpp | 498 + .../Sources/Tools/MaxMain/plPhysXCooking.h | 64 + .../Sources/Tools/MaxMain/plPhysicalProps.cpp | 309 + .../Sources/Tools/MaxMain/plPhysicalProps.h | 163 + .../Sources/Tools/MaxMain/plPlasmaRefMsgs.h | 38 + .../Tools/MaxMain/plPluginResManager.cpp | 525 + .../Tools/MaxMain/plPluginResManager.h | 98 + .../Sources/Tools/MaxMain/plPythonMgr.cpp | 634 + .../Sources/Tools/MaxMain/plPythonMgr.h | 68 + .../Sources/Tools/MaxMain/plResCollector.cpp | 92 + .../Sources/Tools/MaxMain/plResCollector.h | 29 + .../Sources/Tools/MaxMain/plResetXform.cpp | 168 + .../Sources/Tools/MaxMain/plResetXform.h | 55 + .../Sources/Tools/MaxMain/plSaveSelected.cpp | 317 + .../Sources/Tools/MaxMain/plSaveSelected.h | 27 + .../Tools/MaxMain/plTextureExportLog.cpp | 255 + .../Tools/MaxMain/plTextureExportLog.h | 75 + .../Sources/Tools/MaxMain/plTextureSearch.cpp | 374 + .../Sources/Tools/MaxMain/plTextureSearch.h | 47 + .../Plasma20/Sources/Tools/MaxMain/resource.h | 134 + .../Sources/Tools/MaxMtlUpdate/DllEntry.cpp | 91 + .../Tools/MaxMtlUpdate/OldMat/hsMaxLayer.h | 421 + .../MaxMtlUpdate/OldMat/hsMaxLayerBase.h | 190 + .../Tools/MaxMtlUpdate/plMaterialUpdate.cpp | 395 + .../Tools/MaxMtlUpdate/plMaterialUpdate.def | 8 + .../Tools/MaxMtlUpdate/plMaterialUpdate.h | 55 + .../Tools/MaxMtlUpdate/plMaterialUpdate.rc | 110 + .../Sources/Tools/MaxMtlUpdate/resource.h | 28 + .../Tools/MaxPlasmaLights/DLLEntry.cpp | 96 + .../plRTLightBaseAnimDlgProc.h | 70 + .../MaxPlasmaLights/plRTLightBaseAnimPBDec.h | 68 + .../Tools/MaxPlasmaLights/plRTLights.rc | 682 + .../Tools/MaxPlasmaLights/plRTObjLightDesc.h | 151 + .../MaxPlasmaLights/plRTPlasmaLights.def | 8 + .../MaxPlasmaLights/plRTProjDirLight.cpp | 474 + .../Tools/MaxPlasmaLights/plRTProjDirLight.h | 155 + .../plRTProjDirLightClassDesc.h | 62 + .../MaxPlasmaLights/plRTProjDirLightPBDec.h | 162 + .../MaxPlasmaLights/plRealTimeLightBase.cpp | 2087 +++ .../MaxPlasmaLights/plRealTimeLightBase.h | 498 + .../MaxPlasmaLights/plRealTimeLights.cpp | 890 + .../Tools/MaxPlasmaLights/plRealTimeLights.h | 203 + .../MaxPlasmaLights/plRealTimeLightsPBDec.h | 323 + .../Sources/Tools/MaxPlasmaLights/prim.h | 104 + .../Sources/Tools/MaxPlasmaLights/resource.h | 1135 ++ .../Sources/Tools/MaxPlasmaLights/target.h | 108 + .../Layers/plAngleAttenLayer.cpp | 492 + .../MaxPlasmaMtls/Layers/plAngleAttenLayer.h | 165 + .../Layers/plDynamicEnvLayer.cpp | 402 + .../MaxPlasmaMtls/Layers/plDynamicEnvLayer.h | 168 + .../Layers/plDynamicEnvLayerBitmapPB.cpp | 250 + .../Layers/plDynamicTextLayer.cpp | 559 + .../MaxPlasmaMtls/Layers/plDynamicTextLayer.h | 212 + .../Layers/plDynamicTextLayerBitmapPB.cpp | 304 + .../Tools/MaxPlasmaMtls/Layers/plLayerTex.cpp | 683 + .../Tools/MaxPlasmaMtls/Layers/plLayerTex.h | 163 + .../Layers/plLayerTexBasicPB.cpp | 105 + .../MaxPlasmaMtls/Layers/plLayerTexBasicPB.h | 52 + .../Layers/plLayerTexBitmapPB.cpp | 751 + .../MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h | 87 + .../MaxPlasmaMtls/Layers/plMAXCameraLayer.cpp | 399 + .../MaxPlasmaMtls/Layers/plMAXCameraLayer.h | 145 + .../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.cpp | 442 + .../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h | 149 + .../MaxPlasmaMtls/Layers/plStaticEnvLayer.cpp | 696 + .../MaxPlasmaMtls/Layers/plStaticEnvLayer.h | 237 + .../Layers/plStaticEnvLayerBitmapPB.cpp | 499 + .../Layers/plStaticEnvLayerBitmapPB.h | 72 + .../Materials/plAnimStealthConvert.cpp | 266 + .../Materials/plAnimStealthNode.cpp | 970 ++ .../Materials/plAnimStealthNode.h | 247 + .../MaxPlasmaMtls/Materials/plBumpMtl.cpp | 660 + .../Tools/MaxPlasmaMtls/Materials/plBumpMtl.h | 178 + .../MaxPlasmaMtls/Materials/plBumpMtlAnimPB.h | 37 + .../Materials/plBumpMtlAnimPBDec.h | 93 + .../Materials/plBumpMtlBasicPB.h | 38 + .../Materials/plBumpMtlBasicPBDec.h | 92 + .../MaxPlasmaMtls/Materials/plClothingMtl.cpp | 699 + .../MaxPlasmaMtls/Materials/plClothingMtl.h | 201 + .../Materials/plClothingMtlPBDec.h | 426 + .../Materials/plCompositeMtl.cpp | 568 + .../MaxPlasmaMtls/Materials/plCompositeMtl.h | 154 + .../Materials/plCompositeMtlDlg.cpp | 277 + .../Materials/plCompositeMtlDlg.h | 86 + .../Materials/plCompositeMtlPB.h | 65 + .../Materials/plCompositeMtlPBDec.h | 51 + .../MaxPlasmaMtls/Materials/plDecalMtl.cpp | 784 + .../MaxPlasmaMtls/Materials/plDecalMtl.h | 193 + .../MaxPlasmaMtls/Materials/plDecalMtlAdvPB.h | 67 + .../Materials/plDecalMtlAdvPBDec.h | 95 + .../Materials/plDecalMtlAnimPB.h | 47 + .../Materials/plDecalMtlAnimPBDec.h | 93 + .../Materials/plDecalMtlBasicPB.h | 52 + .../Materials/plDecalMtlBasicPBDec.h | 236 + .../Materials/plDecalMtlLayersPB.h | 40 + .../Materials/plDecalMtlLayersPBDec.h | 87 + .../Materials/plMultipassMtl.cpp | 370 + .../MaxPlasmaMtls/Materials/plMultipassMtl.h | 120 + .../Materials/plMultipassMtlDlg.cpp | 326 + .../Materials/plMultipassMtlDlg.h | 89 + .../Materials/plMultipassMtlPB.cpp | 50 + .../Materials/plMultipassMtlPB.h | 37 + .../Materials/plNoteTrackWatcher.cpp | 127 + .../Materials/plNoteTrackWatcher.h | 88 + .../MaxPlasmaMtls/Materials/plParticleMtl.cpp | 620 + .../MaxPlasmaMtls/Materials/plParticleMtl.h | 182 + .../Materials/plParticleMtlPBDec.h | 234 + .../Materials/plPassAnimDlgProc.cpp | 305 + .../Materials/plPassAnimDlgProc.h | 84 + .../Materials/plPassBaseParamIDs.h | 100 + .../MaxPlasmaMtls/Materials/plPassMtl.cpp | 846 + .../Tools/MaxPlasmaMtls/Materials/plPassMtl.h | 197 + .../MaxPlasmaMtls/Materials/plPassMtlAdvPB.h | 59 + .../Materials/plPassMtlAdvPBDec.h | 94 + .../MaxPlasmaMtls/Materials/plPassMtlAnimPB.h | 85 + .../Materials/plPassMtlAnimPBDec.h | 95 + .../MaxPlasmaMtls/Materials/plPassMtlBase.cpp | 660 + .../MaxPlasmaMtls/Materials/plPassMtlBase.h | 285 + .../Materials/plPassMtlBasicPB.h | 52 + .../Materials/plPassMtlBasicPBDec.h | 240 + .../Materials/plPassMtlLayersPB.h | 41 + .../Materials/plPassMtlLayersPBDec.h | 86 + .../Tools/MaxPlasmaMtls/MaxPlasmaMtls.rc | 1181 ++ .../Sources/Tools/MaxPlasmaMtls/Shaders.cpp | 285 + .../Sources/Tools/MaxPlasmaMtls/Shaders.h | 140 + .../Tools/MaxPlasmaMtls/plBMSampler.cpp | 206 + .../Sources/Tools/MaxPlasmaMtls/plBMSampler.h | 85 + .../Tools/MaxPlasmaMtls/plDetailCurveCtrl.cpp | 680 + .../Tools/MaxPlasmaMtls/plDetailCurveCtrl.h | 128 + .../Tools/MaxPlasmaMtls/plDrawCurve.cpp | 83 + .../Tools/MaxPlasmaMtls/plMaterialRefMsg.h | 32 + .../Tools/MaxPlasmaMtls/plMtlImport.cpp | 70 + .../Sources/Tools/MaxPlasmaMtls/plMtlImport.h | 38 + .../MaxPlasmaMtls/res/detailBgnd8bit.bmp | Bin 0 -> 10678 bytes .../Tools/MaxPlasmaMtls/res/dmtlbut.bmp | Bin 0 -> 478 bytes .../Tools/MaxPlasmaMtls/res/dmtlmsk.bmp | Bin 0 -> 478 bytes .../Sources/Tools/MaxPlasmaMtls/resource.h | 338 + .../Tools/MaxSceneViewer/SceneSync.cpp | 664 + .../Sources/Tools/MaxSceneViewer/SceneSync.h | 102 + .../Tools/MaxSceneViewer/SceneViewer.cpp | 280 + .../Tools/MaxSceneViewer/SceneViewer.h | 61 + .../Tools/MaxSceneViewer/SceneWatcher.cpp | 352 + .../Tools/MaxSceneViewer/SceneWatcher.h | 93 + .../Tools/MaxSceneViewer/plKeyRefSort.cpp | 87 + .../Tools/MaxSceneViewer/plKeyRefSort.h | 59 + .../Tools/MaxSceneViewer/plMaxFileData.cpp | 277 + .../Tools/MaxSceneViewer/plMaxFileData.h | 26 + .../Tools/MaxSceneViewer/plPluginApp.cpp | 51 + .../Tools/MaxSceneViewer/plPluginApp.h | 43 + .../Tools/MaxSceneViewer/plPluginClient.cpp | 291 + .../Tools/MaxSceneViewer/plPluginClient.h | 65 + .../Sources/Tools/Migration/Migration.cpp | 232 + .../Sources/Tools/Migration/Migration.h | 49 + .../Sources/Tools/Migration/Migration.ico | Bin 0 -> 1078 bytes .../Sources/Tools/Migration/Migration.rc | 126 + .../Sources/Tools/Migration/MigrationTask.cpp | 419 + .../Sources/Tools/Migration/MigrationTask.h | 139 + .../Tools/Migration/OptionalDialog.cpp | 106 + .../Sources/Tools/Migration/OptionalDialog.h | 33 + .../Sources/Tools/Migration/resource.h | 30 + .../Sources/Tools/Migration/small.ico | Bin 0 -> 318 bytes .../Tools/plDatMerger/plAllCreatables.cpp | 58 + .../Sources/Tools/plDatMerger/plDatMerger.cpp | 429 + .../Tools/plDatMerger/plRawKeyedObject.cpp | 209 + .../Tools/plDatMerger/plRawKeyedObject.h | 76 + .../Tools/plDatMerger/plRawPageAccessor.cpp | 224 + .../Tools/plDatMerger/plRawPageAccessor.h | 69 + .../Tools/plDatMerger/plRawResManager.cpp | 116 + .../Tools/plDatMerger/plRawResManager.h | 56 + .../plFontConverter/hsCodecManagerStub.cpp | 67 + .../Tools/plFontConverter/plFontConverter.cpp | 75 + .../plFontConverter/plFontConverterProc.cpp | 739 + .../Tools/plFontConverter/plFontFreeType.cpp | 249 + .../Tools/plFontConverter/plFontFreeType.h | 52 + .../Tools/plFontConverter/res/icon1.ico | Bin 0 -> 2998 bytes .../plFontConverter/res/plFontConverter.rc | 277 + .../Tools/plFontConverter/res/resource.h | 52 + .../Tools/plResBrowser/plResBrowser.cpp | 114 + .../Tools/plResBrowser/plResBrowser.dsp | 175 + .../Tools/plResBrowser/plResBrowser.vcproj | 292 + .../plResBrowser/plResBrowserWndProc.cpp | 424 + .../Tools/plResBrowser/plResTreeView.cpp | 506 + .../Tools/plResBrowser/plResTreeView.h | 59 + .../Tools/plResBrowser/plWinRegistryTools.cpp | 162 + .../Tools/plResBrowser/plWinRegistryTools.h | 52 + .../Tools/plResBrowser/res/dataicon.ico | Bin 0 -> 2998 bytes .../Sources/Tools/plResBrowser/res/icon1.ico | Bin 0 -> 2998 bytes .../Sources/Tools/plResBrowser/res/icon2.ico | Bin 0 -> 2998 bytes .../Tools/plResBrowser/res/indexico.ico | Bin 0 -> 2998 bytes .../Tools/plResBrowser/res/mergedda.ico | Bin 0 -> 2998 bytes .../Tools/plResBrowser/res/mergedin.ico | Bin 0 -> 2998 bytes .../Tools/plResBrowser/res/plResBrowser.rc | 214 + .../Sources/Tools/plResBrowser/res/resource.h | 67 + .../StaticSDKs/Win32/IJL/ijlnotes.htm | 157 + .../StaticSDKs/Win32/MySQL/ReadMe.txt | 1 + .../StaticSDKs/Win32/PhysX/release_notes.html | 1042 ++ .../StaticSDKs/Win32/PlatformSDK/ReadMe.txt | 1 + .../StaticSDKs/Win32/Scintilla/License.txt | 20 + .../StaticSDKs/Win32/Scintilla/README | 69 + .../Win32/Scintilla/bin/SciLexer.dll | Bin 0 -> 537088 bytes .../Win32/Scintilla/bin/SciLexer.exp | Bin 0 -> 639 bytes .../Win32/Scintilla/bin/SciLexer.lib | Bin 0 -> 1814 bytes .../Win32/Scintilla/bin/SciLexer.pdb | Bin 0 -> 1403904 bytes .../Win32/Scintilla/bin/Scintilla.dll | Bin 0 -> 279552 bytes .../Win32/Scintilla/bin/Scintilla.exp | Bin 0 -> 644 bytes .../Win32/Scintilla/bin/Scintilla.lib | Bin 0 -> 1826 bytes .../Win32/Scintilla/bin/Scintilla.pdb | Bin 0 -> 969728 bytes .../Win32/Scintilla/doc/Design.html | 249 + .../StaticSDKs/Win32/Scintilla/doc/Icons.html | 56 + .../StaticSDKs/Win32/Scintilla/doc/Lexer.txt | 226 + .../Win32/Scintilla/doc/SciBreak.jpg | Bin 0 -> 14998 bytes .../Win32/Scintilla/doc/SciCoding.html | 251 + .../Win32/Scintilla/doc/SciRest.jpg | Bin 0 -> 16680 bytes .../Win32/Scintilla/doc/SciTEIco.png | Bin 0 -> 10091 bytes .../Win32/Scintilla/doc/SciWord.jpg | Bin 0 -> 6164 bytes .../Win32/Scintilla/doc/ScintillaDoc.html | 5350 ++++++ .../Scintilla/doc/ScintillaDownload.html | 70 + .../Win32/Scintilla/doc/ScintillaHistory.html | 5296 ++++++ .../Win32/Scintilla/doc/ScintillaRelated.html | 507 + .../Win32/Scintilla/doc/ScintillaToDo.html | 178 + .../Win32/Scintilla/doc/ScintillaUsage.html | 375 + .../StaticSDKs/Win32/Scintilla/doc/Steps.html | 142 + .../StaticSDKs/Win32/Scintilla/doc/index.html | 198 + .../Win32/Scintilla/include/Accessor.h | 78 + .../Win32/Scintilla/include/Face.py | 107 + .../Win32/Scintilla/include/HFacer.py | 76 + .../Win32/Scintilla/include/KeyWords.h | 82 + .../Win32/Scintilla/include/Platform.h | 517 + .../Win32/Scintilla/include/PropSet.h | 114 + .../Win32/Scintilla/include/SString.h | 280 + .../Win32/Scintilla/include/SciLexer.h | 1074 ++ .../Win32/Scintilla/include/Scintilla.h | 780 + .../Win32/Scintilla/include/Scintilla.iface | 3012 ++++ .../Win32/Scintilla/include/ScintillaWidget.h | 59 + .../Win32/Scintilla/include/WindowAccessor.h | 57 + .../Win32/Scintilla/src/AutoComplete.cxx | 174 + .../Win32/Scintilla/src/AutoComplete.h | 70 + .../Win32/Scintilla/src/CallTip.cxx | 314 + .../StaticSDKs/Win32/Scintilla/src/CallTip.h | 79 + .../Win32/Scintilla/src/CellBuffer.cxx | 1120 ++ .../Win32/Scintilla/src/CellBuffer.h | 250 + .../Win32/Scintilla/src/CharClassify.cxx | 43 + .../Win32/Scintilla/src/CharClassify.h | 25 + .../Win32/Scintilla/src/ContractionState.cxx | 289 + .../Win32/Scintilla/src/ContractionState.h | 65 + .../Win32/Scintilla/src/Document.cxx | 1577 ++ .../StaticSDKs/Win32/Scintilla/src/Document.h | 305 + .../Win32/Scintilla/src/DocumentAccessor.cxx | 187 + .../Win32/Scintilla/src/DocumentAccessor.h | 67 + .../StaticSDKs/Win32/Scintilla/src/Editor.cxx | 7297 ++++++++ .../StaticSDKs/Win32/Scintilla/src/Editor.h | 582 + .../Win32/Scintilla/src/ExternalLexer.cxx | 259 + .../Win32/Scintilla/src/ExternalLexer.h | 95 + .../Win32/Scintilla/src/Indicator.cxx | 77 + .../Win32/Scintilla/src/Indicator.h | 22 + .../StaticSDKs/Win32/Scintilla/src/KeyMap.cxx | 148 + .../StaticSDKs/Win32/Scintilla/src/KeyMap.h | 43 + .../Win32/Scintilla/src/KeyWords.cxx | 221 + .../Win32/Scintilla/src/LexAPDL.cxx | 136 + .../StaticSDKs/Win32/Scintilla/src/LexAU3.cxx | 891 + .../StaticSDKs/Win32/Scintilla/src/LexAVE.cxx | 225 + .../StaticSDKs/Win32/Scintilla/src/LexAda.cxx | 520 + .../StaticSDKs/Win32/Scintilla/src/LexAsm.cxx | 177 + .../Win32/Scintilla/src/LexAsn1.cxx | 181 + .../Win32/Scintilla/src/LexBaan.cxx | 189 + .../Win32/Scintilla/src/LexBash.cxx | 663 + .../Win32/Scintilla/src/LexBasic.cxx | 369 + .../Win32/Scintilla/src/LexBullant.cxx | 225 + .../StaticSDKs/Win32/Scintilla/src/LexCLW.cxx | 675 + .../StaticSDKs/Win32/Scintilla/src/LexCPP.cxx | 489 + .../StaticSDKs/Win32/Scintilla/src/LexCSS.cxx | 303 + .../Win32/Scintilla/src/LexCaml.cxx | 399 + .../Win32/Scintilla/src/LexConf.cxx | 184 + .../Win32/Scintilla/src/LexCrontab.cxx | 218 + .../Win32/Scintilla/src/LexCsound.cxx | 207 + .../Win32/Scintilla/src/LexEScript.cxx | 270 + .../Win32/Scintilla/src/LexEiffel.cxx | 234 + .../Win32/Scintilla/src/LexErlang.cxx | 522 + .../Win32/Scintilla/src/LexFlagship.cxx | 226 + .../Win32/Scintilla/src/LexForth.cxx | 348 + .../Win32/Scintilla/src/LexFortran.cxx | 452 + .../StaticSDKs/Win32/Scintilla/src/LexGen.py | 241 + .../Win32/Scintilla/src/LexGui4Cli.cxx | 309 + .../Win32/Scintilla/src/LexHTML.cxx | 2042 +++ .../Win32/Scintilla/src/LexHaskell.cxx | 263 + .../Win32/Scintilla/src/LexInno.cxx | 290 + .../StaticSDKs/Win32/Scintilla/src/LexKix.cxx | 122 + .../Win32/Scintilla/src/LexLisp.cxx | 275 + .../Win32/Scintilla/src/LexLout.cxx | 208 + .../StaticSDKs/Win32/Scintilla/src/LexLua.cxx | 357 + .../Win32/Scintilla/src/LexMMIXAL.cxx | 183 + .../StaticSDKs/Win32/Scintilla/src/LexMPT.cxx | 182 + .../Win32/Scintilla/src/LexMSSQL.cxx | 359 + .../Win32/Scintilla/src/LexMatlab.cxx | 225 + .../Win32/Scintilla/src/LexMetapost.cxx | 320 + .../Win32/Scintilla/src/LexNsis.cxx | 647 + .../Win32/Scintilla/src/LexOpal.cxx | 518 + .../Win32/Scintilla/src/LexOthers.cxx | 1140 ++ .../StaticSDKs/Win32/Scintilla/src/LexPB.cxx | 358 + .../StaticSDKs/Win32/Scintilla/src/LexPOV.cxx | 312 + .../StaticSDKs/Win32/Scintilla/src/LexPS.cxx | 343 + .../Win32/Scintilla/src/LexPascal.cxx | 369 + .../Win32/Scintilla/src/LexPerl.cxx | 1256 ++ .../Win32/Scintilla/src/LexPython.cxx | 449 + .../Win32/Scintilla/src/LexRebol.cxx | 319 + .../Win32/Scintilla/src/LexRuby.cxx | 1542 ++ .../StaticSDKs/Win32/Scintilla/src/LexSQL.cxx | 342 + .../Win32/Scintilla/src/LexScriptol.cxx | 404 + .../Win32/Scintilla/src/LexSmalltalk.cxx | 317 + .../Win32/Scintilla/src/LexSpecman.cxx | 286 + .../Win32/Scintilla/src/LexSpice.cxx | 221 + .../Win32/Scintilla/src/LexTADS3.cxx | 837 + .../StaticSDKs/Win32/Scintilla/src/LexTCL.cxx | 362 + .../StaticSDKs/Win32/Scintilla/src/LexTeX.cxx | 288 + .../StaticSDKs/Win32/Scintilla/src/LexVB.cxx | 278 + .../Win32/Scintilla/src/LexVHDL.cxx | 473 + .../Win32/Scintilla/src/LexVerilog.cxx | 299 + .../Win32/Scintilla/src/LexYAML.cxx | 305 + .../Win32/Scintilla/src/LineMarker.cxx | 301 + .../Win32/Scintilla/src/LineMarker.h | 54 + .../Win32/Scintilla/src/PropSet.cxx | 1170 ++ .../Win32/Scintilla/src/RESearch.cxx | 788 + .../StaticSDKs/Win32/Scintilla/src/RESearch.h | 65 + .../StaticSDKs/Win32/Scintilla/src/SVector.h | 127 + .../Win32/Scintilla/src/SciTE.properties | 6 + .../Win32/Scintilla/src/ScintillaBase.cxx | 727 + .../Win32/Scintilla/src/ScintillaBase.h | 93 + .../StaticSDKs/Win32/Scintilla/src/Style.cxx | 154 + .../StaticSDKs/Win32/Scintilla/src/Style.h | 56 + .../Win32/Scintilla/src/StyleContext.cxx | 51 + .../Win32/Scintilla/src/StyleContext.h | 169 + .../Win32/Scintilla/src/UniConversion.cxx | 76 + .../Win32/Scintilla/src/UniConversion.h | 12 + .../Win32/Scintilla/src/ViewStyle.cxx | 297 + .../Win32/Scintilla/src/ViewStyle.h | 108 + .../Win32/Scintilla/src/WindowAccessor.cxx | 178 + .../StaticSDKs/Win32/Scintilla/src/XPM.cxx | 322 + .../StaticSDKs/Win32/Scintilla/src/XPM.h | 72 + .../StaticSDKs/Win32/Scintilla/version.txt | 1 + .../Win32/Scintilla/win32/Margin.cur | Bin 0 -> 326 bytes .../Win32/Scintilla/win32/PlatWin.cxx | 2176 +++ .../Win32/Scintilla/win32/PlatformRes.h | 8 + .../Win32/Scintilla/win32/SciTE.properties | 21 + .../Win32/Scintilla/win32/ScintRes.rc | 40 + .../Win32/Scintilla/win32/Scintilla.def | 2 + .../Win32/Scintilla/win32/ScintillaWin.cxx | 2413 +++ .../StaticSDKs/Win32/Scintilla/win32/deps.mak | 211 + .../StaticSDKs/Win32/Scintilla/win32/makefile | 114 + .../Win32/Scintilla/win32/scintilla.mak | 453 + .../Win32/Scintilla/win32/scintilla_vc6.mak | 452 + .../StaticSDKs/Win32/UnicoWS/License.Txt | 179 + .../StaticSDKs/Win32/UnicoWS/redist.txt | 12 + .../StaticSDKs/Win32/UnicoWS/unicows.dll | Bin 0 -> 245408 bytes .../StaticSDKs/Win32/UnicoWS/unicows.pdb | Bin 0 -> 355328 bytes .../StaticSDKs/Win32/UnicoWS/unicows.sym | Bin 0 -> 84868 bytes .../StaticSDKs/Win32/speex-1.0.1/AUTHORS | 15 + .../StaticSDKs/Win32/speex-1.0.1/COPYING | 26 + .../StaticSDKs/Win32/speex-1.0.1/ChangeLog | 6 + .../StaticSDKs/Win32/speex-1.0.1/INSTALL | 8 + .../StaticSDKs/Win32/speex-1.0.1/Makefile.am | 11 + .../StaticSDKs/Win32/speex-1.0.1/Makefile.in | 347 + .../StaticSDKs/Win32/speex-1.0.1/NEWS | 1 + .../StaticSDKs/Win32/speex-1.0.1/README | 9 + .../StaticSDKs/Win32/speex-1.0.1/Speex.spec | 68 + .../Win32/speex-1.0.1/Speex.spec.in | 68 + .../StaticSDKs/Win32/speex-1.0.1/TODO | 18 + .../StaticSDKs/Win32/speex-1.0.1/acinclude.m4 | 84 + .../StaticSDKs/Win32/speex-1.0.1/aclocal.m4 | 3894 +++++ .../StaticSDKs/Win32/speex-1.0.1/config.guess | 1363 ++ .../StaticSDKs/Win32/speex-1.0.1/config.sub | 1470 ++ .../StaticSDKs/Win32/speex-1.0.1/configure | 6110 +++++++ .../StaticSDKs/Win32/speex-1.0.1/configure.in | 69 + .../Win32/speex-1.0.1/doc/Makefile.am | 5 + .../Win32/speex-1.0.1/doc/Makefile.in | 213 + .../Win32/speex-1.0.1/doc/manual.pdf | Bin 0 -> 152166 bytes .../StaticSDKs/Win32/speex-1.0.1/install-sh | 251 + .../Win32/speex-1.0.1/libspeex/Makefile.am | 77 + .../Win32/speex-1.0.1/libspeex/Makefile.in | 450 + .../Win32/speex-1.0.1/libspeex/bits.c | 352 + .../Win32/speex-1.0.1/libspeex/cb_search.c | 387 + .../Win32/speex-1.0.1/libspeex/cb_search.h | 95 + .../speex-1.0.1/libspeex/exc_10_16_table.c | 50 + .../speex-1.0.1/libspeex/exc_10_32_table.c | 66 + .../speex-1.0.1/libspeex/exc_20_32_table.c | 66 + .../speex-1.0.1/libspeex/exc_5_256_table.c | 290 + .../speex-1.0.1/libspeex/exc_5_64_table.c | 98 + .../speex-1.0.1/libspeex/exc_8_128_table.c | 162 + .../Win32/speex-1.0.1/libspeex/filters.c | 292 + .../Win32/speex-1.0.1/libspeex/filters.h | 79 + .../Win32/speex-1.0.1/libspeex/filters_sse.h | 289 + .../Win32/speex-1.0.1/libspeex/gain_table.c | 160 + .../speex-1.0.1/libspeex/gain_table_lbr.c | 64 + .../speex-1.0.1/libspeex/hexc_10_32_table.c | 66 + .../Win32/speex-1.0.1/libspeex/hexc_table.c | 162 + .../speex-1.0.1/libspeex/high_lsp_tables.c | 163 + .../Win32/speex-1.0.1/libspeex/lpc.c | 119 + .../Win32/speex-1.0.1/libspeex/lpc.h | 50 + .../Win32/speex-1.0.1/libspeex/lsp.c | 328 + .../Win32/speex-1.0.1/libspeex/lsp.h | 57 + .../speex-1.0.1/libspeex/lsp_tables_nb.c | 360 + .../Win32/speex-1.0.1/libspeex/ltp.c | 548 + .../Win32/speex-1.0.1/libspeex/ltp.h | 138 + .../Win32/speex-1.0.1/libspeex/ltp_sse.h | 95 + .../Win32/speex-1.0.1/libspeex/math_approx.c | 105 + .../Win32/speex-1.0.1/libspeex/math_approx.h | 39 + .../Win32/speex-1.0.1/libspeex/misc.c | 145 + .../Win32/speex-1.0.1/libspeex/misc.h | 83 + .../Win32/speex-1.0.1/libspeex/modes.c | 650 + .../Win32/speex-1.0.1/libspeex/modes.h | 146 + .../Win32/speex-1.0.1/libspeex/nb_celp.c | 1715 ++ .../Win32/speex-1.0.1/libspeex/nb_celp.h | 202 + .../Win32/speex-1.0.1/libspeex/quant_lsp.c | 311 + .../Win32/speex-1.0.1/libspeex/quant_lsp.h | 71 + .../Win32/speex-1.0.1/libspeex/sb_celp.c | 1333 ++ .../Win32/speex-1.0.1/libspeex/sb_celp.h | 167 + .../Win32/speex-1.0.1/libspeex/speex.h | 331 + .../Win32/speex-1.0.1/libspeex/speex_bits.h | 154 + .../speex-1.0.1/libspeex/speex_callbacks.c | 135 + .../speex-1.0.1/libspeex/speex_callbacks.h | 128 + .../Win32/speex-1.0.1/libspeex/speex_header.c | 162 + .../Win32/speex-1.0.1/libspeex/speex_header.h | 82 + .../Win32/speex-1.0.1/libspeex/speex_stereo.h | 62 + .../Win32/speex-1.0.1/libspeex/stack_alloc.h | 62 + .../Win32/speex-1.0.1/libspeex/stereo.c | 122 + .../Win32/speex-1.0.1/libspeex/testenc.c | 127 + .../Win32/speex-1.0.1/libspeex/testenc_uwb.c | 106 + .../Win32/speex-1.0.1/libspeex/testenc_wb.c | 106 + .../Win32/speex-1.0.1/libspeex/vbr.c | 268 + .../Win32/speex-1.0.1/libspeex/vbr.h | 66 + .../Win32/speex-1.0.1/libspeex/vq.c | 116 + .../Win32/speex-1.0.1/libspeex/vq.h | 42 + .../StaticSDKs/Win32/speex-1.0.1/ltmain.sh | 5130 ++++++ .../StaticSDKs/Win32/speex-1.0.1/missing | 198 + .../Win32/speex-1.0.1/mkinstalldirs | 40 + .../Win32/speex-1.0.1/src/Makefile.am | 24 + .../Win32/speex-1.0.1/src/Makefile.in | 418 + .../StaticSDKs/Win32/speex-1.0.1/src/getopt.c | 1047 ++ .../Win32/speex-1.0.1/src/getopt1.c | 188 + .../Win32/speex-1.0.1/src/getopt_win.h | 169 + .../StaticSDKs/Win32/speex-1.0.1/src/ogg.h | 184 + .../Win32/speex-1.0.1/src/os_types.h | 84 + .../Win32/speex-1.0.1/src/speexdec.1 | 78 + .../Win32/speex-1.0.1/src/speexdec.c | 686 + .../Win32/speex-1.0.1/src/speexenc.1 | 105 + .../Win32/speex-1.0.1/src/speexenc.c | 809 + .../StaticSDKs/Win32/speex-1.0.1/src/wav_io.c | 226 + .../StaticSDKs/Win32/speex-1.0.1/src/wav_io.h | 40 + .../Win32/speex-1.0.1/src/wave_out.c | 216 + .../Win32/speex-1.0.1/src/wave_out.h | 65 + .../Win32/speex-1.0.1/win32/Makefile.am | 7 + .../Win32/speex-1.0.1/win32/Makefile.in | 294 + .../win32/libspeex/Debug/libspeex.lib | Bin 0 -> 267360 bytes .../speex-1.0.1/win32/libspeex/Debug/vc70.pdb | Bin 0 -> 69632 bytes .../speex-1.0.1/win32/libspeex/Makefile.am | 6 + .../speex-1.0.1/win32/libspeex/Makefile.in | 190 + .../win32/libspeex/Release/libspeex.lib | Bin 0 -> 157148 bytes .../speex-1.0.1/win32/libspeex/libspeex.dsp | 282 + .../speex-1.0.1/win32/libspeex/libspeex.dsw | 29 + .../speex-1.0.1/win32/libspeex/libspeex.opt | Bin 0 -> 53760 bytes .../speex-1.0.1/win32/libspeex/libspeex.plg | 116 + .../speex-1.0.1/win32/libspeex/libspeex.sln | 21 + .../win32/libspeex/libspeex.vcproj | 702 + .../speex-1.0.1/win32/speexdec/Makefile.am | 6 + .../speex-1.0.1/win32/speexdec/Makefile.in | 190 + .../speex-1.0.1/win32/speexdec/speexdec.dsp | 129 + .../speex-1.0.1/win32/speexdec/speexdec.dsw | 59 + .../speex-1.0.1/win32/speexdec/speexdec.opt | Bin 0 -> 59904 bytes .../speex-1.0.1/win32/speexenc/Copy of ogg.h | 184 + .../speex-1.0.1/win32/speexenc/Makefile.am | 6 + .../speex-1.0.1/win32/speexenc/Makefile.in | 190 + .../Win32/speex-1.0.1/win32/speexenc/ogg.h | 184 + .../speex-1.0.1/win32/speexenc/speexenc.dsp | 121 + .../speex-1.0.1/win32/speexenc/speexenc.dsw | 59 + .../speex-1.0.1/win32/speexenc/speexenc.opt | Bin 0 -> 59904 bytes .../speex-1.0.1/win32/speexenc/speexenc.plg | 35 + .../XPlatform/expat-1.95.7/COPYING.txt | 22 + .../XPlatform/expat-1.95.7/Changes.txt | 124 + .../XPlatform/expat-1.95.7/Include/expat.h | 1001 ++ .../XPlatform/expat-1.95.7/Libs/libexpat.dll | Bin 0 -> 184320 bytes .../XPlatform/expat-1.95.7/Libs/libexpat.exp | Bin 0 -> 10883 bytes .../XPlatform/expat-1.95.7/Libs/libexpat.lib | Bin 0 -> 16592 bytes .../XPlatform/expat-1.95.7/Libs/libexpatw.dll | Bin 0 -> 188416 bytes .../XPlatform/expat-1.95.7/Libs/libexpatw.exp | Bin 0 -> 10890 bytes .../XPlatform/expat-1.95.7/Libs/libexpatw.lib | Bin 0 -> 16674 bytes .../XPlatform/expat-1.95.7/README.txt | 119 + .../XPlatform/expat-1.95.7/Source/README.txt | 58 + .../XPlatform/expat-1.95.7/Source/expat.sln | 86 + .../XPlatform/expat-1.95.7/Source/lib/ascii.h | 85 + .../expat-1.95.7/Source/lib/asciitab.h | 36 + .../XPlatform/expat-1.95.7/Source/lib/expat.h | 1001 ++ .../expat-1.95.7/Source/lib/expat.vcproj | 246 + .../Source/lib/expat_static.vcproj | 213 + .../expat-1.95.7/Source/lib/expatw.vcproj | 246 + .../Source/lib/expatw_static.vcproj | 213 + .../Source/lib/expatw_static_xbox.vcproj | 239 + .../expat-1.95.7/Source/lib/iasciitab.h | 37 + .../expat-1.95.7/Source/lib/internal.h | 73 + .../expat-1.95.7/Source/lib/latin1tab.h | 36 + .../expat-1.95.7/Source/lib/libexpat.def | 69 + .../expat-1.95.7/Source/lib/libexpatw.def | 69 + .../expat-1.95.7/Source/lib/macconfig.h | 104 + .../expat-1.95.7/Source/lib/nametab.h | 150 + .../expat-1.95.7/Source/lib/utf8tab.h | 37 + .../expat-1.95.7/Source/lib/winconfig.h | 34 + .../expat-1.95.7/Source/lib/xmlparse.c | 5814 +++++++ .../expat-1.95.7/Source/lib/xmlrole.c | 1323 ++ .../expat-1.95.7/Source/lib/xmlrole.h | 114 + .../expat-1.95.7/Source/lib/xmltok.c | 1634 ++ .../expat-1.95.7/Source/lib/xmltok.h | 315 + .../expat-1.95.7/Source/lib/xmltok_impl.c | 1779 ++ .../expat-1.95.7/Source/lib/xmltok_impl.h | 46 + .../expat-1.95.7/Source/lib/xmltok_ns.c | 106 + .../StaticLibs/Win32/libexpatwMT.lib | Bin 0 -> 234832 bytes .../StaticLibs/Xbox/libexpatwMT.lib | Bin 0 -> 508408 bytes .../expat-1.95.7/StaticLibs/Xbox/vc70.pdb | Bin 0 -> 102400 bytes .../StaticSDKs/XPlatform/png/ANNOUNCE | 31 + .../StaticSDKs/XPlatform/png/CHANGES | 1021 ++ .../StaticSDKs/XPlatform/png/INSTALL | 143 + .../StaticSDKs/XPlatform/png/KNOWNBUG | 9 + .../StaticSDKs/XPlatform/png/LICENSE | 102 + .../StaticSDKs/XPlatform/png/Makefile | 112 + .../StaticSDKs/XPlatform/png/README | 254 + .../StaticSDKs/XPlatform/png/TODO | 24 + .../StaticSDKs/XPlatform/png/Y2KINFO | 55 + .../StaticSDKs/XPlatform/png/example.c | 804 + .../StaticSDKs/XPlatform/png/lib/libpng.lib | Bin 0 -> 284224 bytes .../XPlatform/png/lib/libpng_dbg.lib | Bin 0 -> 582974 bytes .../StaticSDKs/XPlatform/png/libpng.dsp | 176 + .../StaticSDKs/XPlatform/png/libpng.dsw | 44 + .../StaticSDKs/XPlatform/png/libpng.txt | 2903 ++++ .../StaticSDKs/XPlatform/png/png.c | 777 + .../StaticSDKs/XPlatform/png/png.h | 3200 ++++ .../StaticSDKs/XPlatform/png/pngasmrd.h | 11 + .../StaticSDKs/XPlatform/png/pngbar.jpg | Bin 0 -> 2498 bytes .../StaticSDKs/XPlatform/png/pngbar.png | Bin 0 -> 2399 bytes .../StaticSDKs/XPlatform/png/pngconf.h | 1331 ++ .../StaticSDKs/XPlatform/png/pngerror.c | 289 + .../StaticSDKs/XPlatform/png/pnggccrd.c | 5340 ++++++ .../StaticSDKs/XPlatform/png/pngget.c | 917 + .../StaticSDKs/XPlatform/png/pngmem.c | 517 + .../StaticSDKs/XPlatform/png/pngnow.png | Bin 0 -> 2018 bytes .../StaticSDKs/XPlatform/png/pngpread.c | 1502 ++ .../StaticSDKs/XPlatform/png/pngread.c | 1398 ++ .../StaticSDKs/XPlatform/png/pngrio.c | 161 + .../StaticSDKs/XPlatform/png/pngrtran.c | 4137 +++++ .../StaticSDKs/XPlatform/png/pngrutil.c | 3001 ++++ .../StaticSDKs/XPlatform/png/pngset.c | 1033 ++ .../StaticSDKs/XPlatform/png/pngtest.c | 1515 ++ .../StaticSDKs/XPlatform/png/pngtest.dsp | 104 + .../StaticSDKs/XPlatform/png/pngtest.plg | 99 + .../StaticSDKs/XPlatform/png/pngtest.png | Bin 0 -> 8574 bytes .../StaticSDKs/XPlatform/png/pngtrans.c | 640 + .../StaticSDKs/XPlatform/png/pngvcrd.c | 3845 +++++ .../StaticSDKs/XPlatform/png/pngwio.c | 228 + .../StaticSDKs/XPlatform/png/pngwrite.c | 1432 ++ .../StaticSDKs/XPlatform/png/pngwtran.c | 563 + .../StaticSDKs/XPlatform/png/pngwutil.c | 2669 +++ .../XPlatform/png/scripts/SCOPTIONS.ppc | 7 + .../XPlatform/png/scripts/descrip.mms | 52 + .../XPlatform/png/scripts/libpng.icc | 44 + .../XPlatform/png/scripts/makefile.acorn | 51 + .../XPlatform/png/scripts/makefile.aix | 84 + .../XPlatform/png/scripts/makefile.amiga | 48 + .../XPlatform/png/scripts/makefile.atari | 33 + .../XPlatform/png/scripts/makefile.bc32 | 151 + .../XPlatform/png/scripts/makefile.bd32 | 76 + .../XPlatform/png/scripts/makefile.beos | 104 + .../XPlatform/png/scripts/makefile.bor | 162 + .../XPlatform/png/scripts/makefile.cygwin | 187 + .../XPlatform/png/scripts/makefile.darwin | 106 + .../XPlatform/png/scripts/makefile.dec | 91 + .../XPlatform/png/scripts/makefile.dj2 | 52 + .../XPlatform/png/scripts/makefile.gcc | 66 + .../XPlatform/png/scripts/makefile.gcmmx | 147 + .../XPlatform/png/scripts/makefile.hpgcc | 112 + .../XPlatform/png/scripts/makefile.hpux | 98 + .../XPlatform/png/scripts/makefile.ibmc | 71 + .../XPlatform/png/scripts/makefile.intel | 114 + .../XPlatform/png/scripts/makefile.knr | 82 + .../XPlatform/png/scripts/makefile.linux | 112 + .../XPlatform/png/scripts/makefile.macosx | 92 + .../XPlatform/png/scripts/makefile.mips | 66 + .../XPlatform/png/scripts/makefile.msc | 86 + .../XPlatform/png/scripts/makefile.netbsd | 42 + .../XPlatform/png/scripts/makefile.os2 | 69 + .../XPlatform/png/scripts/makefile.sco | 102 + .../XPlatform/png/scripts/makefile.sggcc | 97 + .../XPlatform/png/scripts/makefile.sgi | 110 + .../XPlatform/png/scripts/makefile.so9 | 124 + .../XPlatform/png/scripts/makefile.solaris | 120 + .../XPlatform/png/scripts/makefile.std | 72 + .../XPlatform/png/scripts/makefile.sunos | 76 + .../XPlatform/png/scripts/makefile.tc3 | 89 + .../XPlatform/png/scripts/makefile.watcom | 109 + .../XPlatform/png/scripts/makevms.com | 144 + .../XPlatform/png/scripts/pngdef.pas | 795 + .../XPlatform/png/scripts/pngos2.def | 234 + .../XPlatform/png/scripts/smakefile.ppc | 30 + .../StaticSDKs/XPlatform/zlib/SRC/ChangeLog | 481 + .../StaticSDKs/XPlatform/zlib/SRC/FAQ | 100 + .../StaticSDKs/XPlatform/zlib/SRC/INDEX | 86 + .../XPlatform/zlib/SRC/Make_vms.com | 115 + .../StaticSDKs/XPlatform/zlib/SRC/Makefile | 175 + .../StaticSDKs/XPlatform/zlib/SRC/Makefile.in | 175 + .../XPlatform/zlib/SRC/Makefile.riscos | 151 + .../StaticSDKs/XPlatform/zlib/SRC/README | 147 + .../StaticSDKs/XPlatform/zlib/SRC/adler32.c | 48 + .../XPlatform/zlib/SRC/algorithm.txt | 213 + .../XPlatform/zlib/SRC/amiga/Makefile.pup | 66 + .../XPlatform/zlib/SRC/amiga/Makefile.sas | 64 + .../StaticSDKs/XPlatform/zlib/SRC/compress.c | 68 + .../StaticSDKs/XPlatform/zlib/SRC/configure | 212 + .../XPlatform/zlib/SRC/contrib/README.contrib | 34 + .../zlib/SRC/contrib/asm386/gvmat32.asm | 559 + .../zlib/SRC/contrib/asm386/gvmat32c.c | 200 + .../zlib/SRC/contrib/asm386/mkgvmt32.bat | 1 + .../zlib/SRC/contrib/asm386/zlibvc.def | 74 + .../zlib/SRC/contrib/asm386/zlibvc.dsp | 651 + .../zlib/SRC/contrib/asm386/zlibvc.dsw | 41 + .../zlib/SRC/contrib/asm586/README.586 | 43 + .../XPlatform/zlib/SRC/contrib/asm586/match.S | 354 + .../zlib/SRC/contrib/asm686/README.686 | 34 + .../XPlatform/zlib/SRC/contrib/asm686/match.S | 327 + .../zlib/SRC/contrib/delphi/zlib.mak | 36 + .../zlib/SRC/contrib/delphi/zlibdef.pas | 169 + .../zlib/SRC/contrib/delphi2/d_zlib.bpr | 224 + .../zlib/SRC/contrib/delphi2/d_zlib.cpp | 17 + .../zlib/SRC/contrib/delphi2/readme.txt | 17 + .../zlib/SRC/contrib/delphi2/zlib.bpg | 26 + .../zlib/SRC/contrib/delphi2/zlib.bpr | 225 + .../zlib/SRC/contrib/delphi2/zlib.cpp | 22 + .../zlib/SRC/contrib/delphi2/zlib.pas | 534 + .../zlib/SRC/contrib/delphi2/zlib32.bpr | 174 + .../zlib/SRC/contrib/delphi2/zlib32.cpp | 42 + .../zlib/SRC/contrib/iostream/test.cpp | 24 + .../zlib/SRC/contrib/iostream/zfstream.cpp | 329 + .../zlib/SRC/contrib/iostream/zfstream.h | 142 + .../zlib/SRC/contrib/iostream2/zstream.h | 307 + .../SRC/contrib/iostream2/zstream_test.cpp | 25 + .../zlib/SRC/contrib/minizip/ChangeLogUnzip | 38 + .../zlib/SRC/contrib/minizip/Makefile | 25 + .../zlib/SRC/contrib/minizip/miniunz.c | 508 + .../zlib/SRC/contrib/minizip/minizip.c | 302 + .../zlib/SRC/contrib/minizip/readme.txt | 37 + .../zlib/SRC/contrib/minizip/unzip.c | 1294 ++ .../zlib/SRC/contrib/minizip/unzip.def | 15 + .../zlib/SRC/contrib/minizip/unzip.h | 275 + .../XPlatform/zlib/SRC/contrib/minizip/zip.c | 718 + .../zlib/SRC/contrib/minizip/zip.def | 5 + .../XPlatform/zlib/SRC/contrib/minizip/zip.h | 150 + .../zlib/SRC/contrib/minizip/zlibvc.def | 74 + .../zlib/SRC/contrib/minizip/zlibvc.dsp | 651 + .../zlib/SRC/contrib/minizip/zlibvc.dsw | 41 + .../XPlatform/zlib/SRC/contrib/untgz/Makefile | 14 + .../zlib/SRC/contrib/untgz/makefile.w32 | 63 + .../XPlatform/zlib/SRC/contrib/untgz/untgz.c | 522 + .../zlib/SRC/contrib/visual-basic.txt | 69 + .../StaticSDKs/XPlatform/zlib/SRC/crc32.c | 162 + .../StaticSDKs/XPlatform/zlib/SRC/deflate.c | 1350 ++ .../StaticSDKs/XPlatform/zlib/SRC/deflate.h | 318 + .../StaticSDKs/XPlatform/zlib/SRC/descrip.mms | 48 + .../StaticSDKs/XPlatform/zlib/SRC/example.c | 556 + .../StaticSDKs/XPlatform/zlib/SRC/gzio.c | 875 + .../StaticSDKs/XPlatform/zlib/SRC/infblock.c | 403 + .../StaticSDKs/XPlatform/zlib/SRC/infblock.h | 39 + .../StaticSDKs/XPlatform/zlib/SRC/infcodes.c | 251 + .../StaticSDKs/XPlatform/zlib/SRC/infcodes.h | 27 + .../StaticSDKs/XPlatform/zlib/SRC/inffast.c | 183 + .../StaticSDKs/XPlatform/zlib/SRC/inffast.h | 17 + .../StaticSDKs/XPlatform/zlib/SRC/inffixed.h | 151 + .../StaticSDKs/XPlatform/zlib/SRC/inflate.c | 366 + .../StaticSDKs/XPlatform/zlib/SRC/inftrees.c | 454 + .../StaticSDKs/XPlatform/zlib/SRC/inftrees.h | 58 + .../StaticSDKs/XPlatform/zlib/SRC/infutil.c | 87 + .../StaticSDKs/XPlatform/zlib/SRC/infutil.h | 98 + .../StaticSDKs/XPlatform/zlib/SRC/maketree.c | 85 + .../StaticSDKs/XPlatform/zlib/SRC/minigzip.c | 320 + .../XPlatform/zlib/SRC/msdos/Makefile.b32 | 104 + .../XPlatform/zlib/SRC/msdos/Makefile.bor | 125 + .../XPlatform/zlib/SRC/msdos/Makefile.dj2 | 100 + .../XPlatform/zlib/SRC/msdos/Makefile.emx | 69 + .../XPlatform/zlib/SRC/msdos/Makefile.msc | 121 + .../XPlatform/zlib/SRC/msdos/Makefile.tc | 108 + .../XPlatform/zlib/SRC/msdos/Makefile.w32 | 97 + .../XPlatform/zlib/SRC/msdos/Makefile.wat | 103 + .../XPlatform/zlib/SRC/msdos/zlib.def | 60 + .../XPlatform/zlib/SRC/msdos/zlib.rc | 32 + .../XPlatform/zlib/SRC/nt/Makefile.emx | 138 + .../XPlatform/zlib/SRC/nt/Makefile.gcc | 87 + .../XPlatform/zlib/SRC/nt/Makefile.nt | 88 + .../StaticSDKs/XPlatform/zlib/SRC/nt/zlib.dnt | 47 + .../XPlatform/zlib/SRC/os2/Makefile.os2 | 136 + .../XPlatform/zlib/SRC/os2/zlib.def | 51 + .../StaticSDKs/XPlatform/zlib/SRC/trees.c | 1214 ++ .../StaticSDKs/XPlatform/zlib/SRC/trees.h | 128 + .../StaticSDKs/XPlatform/zlib/SRC/uncompr.c | 58 + .../StaticSDKs/XPlatform/zlib/SRC/zconf.h | 279 + .../StaticSDKs/XPlatform/zlib/SRC/zlib.3 | 107 + .../StaticSDKs/XPlatform/zlib/SRC/zlib.h | 893 + .../StaticSDKs/XPlatform/zlib/SRC/zlib.html | 971 ++ .../StaticSDKs/XPlatform/zlib/SRC/zutil.c | 225 + .../StaticSDKs/XPlatform/zlib/SRC/zutil.h | 220 + .../XPlatform/zlib/ZLIB_VSS_MAKE/zlib.dsw | 29 + .../XPlatform/zlib/ZLIB_VSS_MAKE/zlib.opt | Bin 0 -> 53760 bytes .../XPlatform/zlib/ZLIB_VSS_MAKE/zlib.sln | 27 + .../zlib/ZLIB_VSS_MAKE/zlib/zlib.dsp | 196 + .../zlib/ZLIB_VSS_MAKE/zlib/zlib.plg | 74 + .../zlib/ZLIB_VSS_MAKE/zlib/zlib.vcproj | 759 + .../StaticSDKs/XPlatform/zlib/inc/zconf.h | 279 + .../StaticSDKs/XPlatform/zlib/inc/zlib.h | 893 + .../StaticSDKs/XPlatform/zlib/lib/vc70.pdb | Bin 0 -> 69632 bytes .../StaticSDKs/XPlatform/zlib/lib/zlib.lib | Bin 0 -> 83876 bytes .../XPlatform/zlib/lib/zlib_srv.lib | Bin 0 -> 91578 bytes .../StaticSDKs/XPlatform/zlib/lib/zlibd.lib | Bin 0 -> 164968 bytes .../XPlatform/zlib/lib/zlibd_srv.lib | Bin 0 -> 187378 bytes 3976 files changed, 1301356 insertions(+) create mode 100644 LICENSE.txt create mode 100644 MOULOpenSourceClientPlugin/LICENSE.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Docs/Distributor.doc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Docs/ReleaseNotes/ReleaseNotes.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Docs/Resource Manager.doc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Docs/sound cones.htm create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/AllDlls/AllDlls.sln create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/AllDlls/AllDlls.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/AllClient/AllClient.sln create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/AllClient/AllClient.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClient/plClient.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClientKey/plClientKey.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClientPatcher/plClientPatcher.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plFileEncrypt/plFileEncrypt.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plFileSecure/plFileSecure.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plLogDecrypt/plLogDecrypt.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plMD5/plMD5.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPageInfo/plPageInfo.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPageOptimizer/plPageOptimizer.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPythonPack/plPythonPack.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plUruLauncher/plUruLauncher.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/CoreLib/CoreLib.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/CoreLibExe/CoreLibExe.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/FeatureLibInc/FeatureLibInc.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfAnimation/pfAnimation.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfAudio/pfAudio.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCCR/pfCCR.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCamera/pfCamera.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCharacter/pfCharacter.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfConditional/pfConditional.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfConsole/pfConsole.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameMgr/pfGameMgr.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfJournalBook/pfJournalBook.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfLoginDialog/pfLoginDialog.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfMessage/pfMessage.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfPython/pfPython.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfStackTrace/pfStackTrace.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfSurface/pfSurface.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAsyncCore/pnAsyncCore.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAsyncCoreExe/pnAsyncCoreExe.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCrash/pnCrash.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCrashExe/pnCrashExe.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCsrCli/pnCsrCli.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCsrNet/pnCsrNet.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnDispatch/pnDispatch.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnFactory/pnFactory.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnGameMgr/pnGameMgr.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnIni/pnIni.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnIniExe/pnIniExe.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnInputCore/pnInputCore.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnKeyedObject/pnKeyedObject.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnMail/pnMail.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnMessage/pnMessage.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnModifier/pnModifier.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetBase/pnNetBase.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetCli/pnNetCli.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetCommon/pnNetCommon.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetDiag/pnNetDiag.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetLog/pnNetLog.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetProtocol/pnNetProtocol.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNucleusInc/pnNucleusInc.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnOraLib/pnOraLib.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnProduct/pnProduct.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSceneObject/pnSceneObject.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSqlLib/pnSqlLib.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSrvUtils/pnSrvUtils.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSrvUtilsExe/pnSrvUtilsExe.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnTimer/pnTimer.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnUtils/pnUtils.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnUtilsExe/pnUtilsExe.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/PubUtilInc/PubUtilInc.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAgeDescription/plAgeDescription.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudible/plAudible.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudio/plAudio.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAvatar/plAvatar.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plCompression/plCompression.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plContainer/plContainer.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plDrawable/plDrawable.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plEncryption/plEncryption.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plFile/plFile.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGClip/plGClip.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGImage/plGImage.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGLight/plGLight.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGeometry/plGeometry.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plInputCore/plInputCore.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plInterp/plInterp.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plIntersect/plIntersect.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plJPEG/plJPEG.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plMath/plMath.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plMessage/plMessage.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plModifier/plModifier.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClient/PlNetClient.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetCommon/plNetCommon.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetGameLib/plNetGameLib.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetMessage/plNetMessage.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetTransport/plNetTransport.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPhysX/plPhysX.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPhysical/plPhysical.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPipeline/plPipeline.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plResMgr/plResMgr.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSDL/plSDL.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plScene/plScene.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSockets/plSockets.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStatGather/plStatGather.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStatusLog/plStatusLog.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSurface/plSurface.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plTransform/plTransform.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plUUID/plUUID.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plVault/plVault.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plWinStrBlock/plWinStrBlock.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyPlasma/pyPlasma.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyPloticus/pyPloticus.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyVault/pyVault.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxComponent/MaxComponent.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxConvert/MaxConvert.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxExport/MaxExport.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxMain/MaxMain.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxPlasmaLights/MaxPlasmaLights.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxPlasmaMtls/MaxPlasmaMtls.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxSceneViewer/MaxSceneViewer.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/plFontConverter/plFontConverter.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/lib/HawkVoiceDIstatic.lib create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Blowfish.001 create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Blowfish.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/COPYRIGHT create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/INSTALL create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile.ssl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile.uni create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/README create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bf586.pl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bx86-cpp.s create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bx86unix.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/readme create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/win32.asm create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/x86ms.pl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/x86unix.pl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_cbc.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_cfb64.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_ecb.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_enc.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_locl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_locl.org create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_ofb64.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_pi.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_skey.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/blowfish.doc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/blowfish.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/hvdi.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Grammar/.cvsignore create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Grammar/Grammar create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/LICENSE create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/.cvsignore create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/Setup.config.in create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/Setup.dist create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_bsddb.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_codecsmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_csv.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_curses_panel.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_cursesmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_hotshot.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_localemodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_randommodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_sre.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_ssl.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_testcapimodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_tkinter.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_weakref.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/addrinfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/almodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ar_beos create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/arraymodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/audioop.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/binascii.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/bsddbmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/bz2module.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cPickle.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cStringIO.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ccpython.cc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cdmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgen.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgensupport.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgensupport.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/clmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cmathmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/config.c.in create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cryptmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cstubs create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/datetimemodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/dbmmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/dlmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/errnomodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/Makefile.in create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/ascii.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/asciitab.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/expat.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/iasciitab.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/internal.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/latin1tab.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/macconfig.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/nametab.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/utf8tab.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/winconfig.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlparse.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlrole.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlrole.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_impl.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_impl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_ns.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fcntlmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/flmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fmmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fpectlmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fpetestmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gc_weakref.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gcmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gdbmmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getaddrinfo.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getbuildinfo.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getnameinfo.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getpath.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/glmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/grpmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/imageop.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/imgfile.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/itertoolsmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ld_so_aix create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ld_so_beos create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/linuxaudiodev.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/main.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/makesetup create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/makexp_aix create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mathmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5c.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5module.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mmapmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mpzmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/nismodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/operator.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ossaudiodev.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/parsermodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcre-int.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcre.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcremodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/posixmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/puremodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pwdmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pyexpat.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pypcre.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/python.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/readline.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexpr.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexpr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/resource.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/rgbimgmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/rotormodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/selectmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sgimodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/shamodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/signalmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/socketmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/socketmodule.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sre.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sre_constants.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/stropmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/structmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sunaudiodev.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/svmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/symtablemodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/syslogmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/termios.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/testcapi_long.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/threadmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timemodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timing.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timingmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/tkappinit.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodedata.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodedata_db.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodename_db.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xreadlinesmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xxmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xxsubtype.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/yuv.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/yuvconvert.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/zipimport.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/zlibmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/.cvsignore create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/abstract.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/boolobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/bufferobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/cellobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/classobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/cobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/complexobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/descrobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/dictnotes.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/dictobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/enumobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/fileobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/floatobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/frameobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/funcobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/intobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/iterobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/listobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/listsort.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/longobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/methodobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/moduleobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/object.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/obmalloc.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/rangeobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/sliceobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/stringobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/structseq.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/tupleobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/typeobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodectype.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodeobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodetype_db.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/weakrefobject.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/.cvsignore create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/PlasmaPack.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/PlasmaPack.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/WinMain.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/_winreg.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/.cvsignore create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/PythonPowered.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/README.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/archive.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/extract.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/install.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/install.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/wininst.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/wininst.dsw create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/config.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/dl_nt.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/dllbase_nt.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/.cvsignore create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.def create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.dsw create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/readme.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/frozen_dllmain.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/getpathp.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/import_nt.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/make_versioninfo.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/msvcrtmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/Makefile create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/README.os2emx create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/config.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dlfcn.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dlfcn.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dllentry.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/getpathp.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/pyconfig.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/python23.def create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/pythonpm.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/_tkinter.def create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/config.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/getpathp.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/makefile create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/makefile.omk create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/pyconfig.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/python.def create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/readme.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/py.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pyc.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pycon.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/python.mk create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/python_exe.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/python_nt.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pythonnt_rc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pythonnt_rc_d.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/readme.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/testpy.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/w9xpopen.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/winsound.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/.cvsignore create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/BUILDno.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/Uninstal.wse create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_bsddb.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_csv.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_socket.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_sre.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_sre_static.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_ssl.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_ssl.mak create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_symtable.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_testcapi.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_tkinter.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/build_ssl.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/bz2.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/datetime.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/field3.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/installer.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/mmap.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/parser.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pcbuild.dsw create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pyexpat.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python.iss create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python20.wse create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore_dyn_server.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore_static.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythonw.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/readme.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/rmpyc.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/rt.bat create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/select.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/unicodedata.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/w9xpopen.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/winreg.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/winsound.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/zlib.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/.cvsignore create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/acceler.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/bitset.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/firstsets.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar.mak create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar1.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/intrcheck.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/listnode.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/metagrammar.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/myreadline.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/node.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parser.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parser.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parsetok.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/pgen.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/pgenmain.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/printgrammar.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer_pgen.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/.cvsignore create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/atof.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/bltinmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/ceval.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/codecs.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/compile.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dup2.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_aix.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_atheos.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_beos.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_dl.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_hpux.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_mac.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_next.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_os2.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_shlib.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_stub.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_win.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/errors.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/exceptions.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/fmod.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/frozen.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/frozenmain.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/future.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getargs.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcompiler.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcopyright.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcwd.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getmtime.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getopt.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getplatform.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getversion.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/graminit.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/hypot.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/import.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/import_pack.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/importdl.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/importdl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mactoolboxglue.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/marshal.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/memmove.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/modsupport.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mysnprintf.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mystrtoul.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pyfpe.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pystate.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pythonrun.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/sigcheck.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strdup.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strerror.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strtod.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/structmember.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/symtable.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/sysmodule.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_atheos.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_beos.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_cthread.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_foobar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_lwp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_nt.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_os2.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_pth.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_pthread.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_sgi.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_solaris.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_wince.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/traceback.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/README create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/XboxBuild/_sre_static.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/XboxBuild/pythoncore_static.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/Python.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/abstract.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/bitset.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/boolobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/bufferobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cStringIO.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cellobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/ceval.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/classobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/codecs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/compile.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/complexobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/datetime.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/descrobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/dictobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/enumobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/errcode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/eval.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/fileobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/floatobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/frameobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/funcobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/graminit.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/grammar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/import.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/intobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/intrcheck.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/iterobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/listobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/longintrepr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/longobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/marshal.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/metagrammar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/methodobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/modsupport.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/moduleobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/node.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/object.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/objimpl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/opcode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/osdefs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/parsetok.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/patchlevel.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pgen.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pgenheaders.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/py_curses.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pydebug.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyerrors.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyfpe.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pygetopt.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pymactoolbox.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pymem.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyport.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pystate.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pythonrun.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pythread.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/rangeobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/sliceobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/stringobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/structmember.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/structseq.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/symtable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/sysmodule.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/token.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/traceback.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/tupleobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/ucnhash.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/unicodeobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/weakrefobject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/pyconfig_dyn/pyconfig.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/pyconfig_static/pyconfig.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Installer/LargeMOUL.nsi create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Installer/MOUL.nsi create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Installer/MOULInstaller.exe create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Max/PlasmaExport.ms create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/Plasma.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaConstants.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaControlKeys.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaGame.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaGameConstants.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaKITypes.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaNetConstants.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaTypes.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaVaultConstants.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/glue.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/pch.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/ptWordFilter.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/Bastion.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/ConfigParser.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/HTMLParser.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/StringIO.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/UserDict.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/__future__.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/ascii.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/atexit.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/bdb.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/bisect.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/calendar.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/cmd.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/codecs.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/colorsys.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/commands.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/compileall.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/copy.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/copy_reg.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/decompyle.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/difflib.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/dircache.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/dis.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/dospath.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/encoding_utf_8.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/encodings.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/filecmp.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/fileinput.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/fnmatch.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/fpformat.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/getopt.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/gettext.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/glob.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/ihooks.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/imghdr.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/imputil.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/inspect.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/keyword.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/knee.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/linecache.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/locale.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/markupbase.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/mutex.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/netrc.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/ntpath.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/opcode.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/os.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/pdb.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/pickle.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/popen2.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/posixpath.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/pre.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/profile.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/pstats.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/py_compile.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/pyclbr.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/random.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/re.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/regex_syntax.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/repr.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/shlex.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/shutil.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/site.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/spark.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/sre.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/sre_compile.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/sre_constants.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/sre_parse.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/stat.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/statcache.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/string.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/symbol.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/tabnanny.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/token.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/tokenize.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/traceback.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/types.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/verify.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/warnings.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/weakref.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/webbrowser.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/whrandom.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/Do_Not_Load_Neighborhood.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/Do_Not_Load_RestorationGuild.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/Bastion.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/ConfigParser.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/StringIO.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/__future__.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/_sre.pyd create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/_sre_d.pyd create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/atexit.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/bdb.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/bisect.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/calendar.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/cmd.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/codecs.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/colorsys.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/commands.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/compileall.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/copy.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/copy_reg.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/decompyle.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/difflib.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dircache.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dis.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dospath.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/filecmp.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fileinput.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fnmatch.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fpformat.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/getopt.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/gettext.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/glob.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/ihooks.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/imghdr.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/imputil.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/inspect.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/keyword.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/knee.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/linecache.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/locale.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/mutex.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/netrc.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/ntpath.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/os.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pdb.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pickle.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/popen2.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/posixpath.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pre.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/profile.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pstats.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/py_compile.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pyclbr.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/random.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/re.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/regex_syntax.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/repr.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/shlex.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/shutil.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/site.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/spark.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_compile.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_constants.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_parse.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/stat.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/statcache.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/string.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/symbol.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/tabnanny.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/token.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/tokenize.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/traceback.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/types.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/verify.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/warnings.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/weakref.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/whrandom.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/pfAllCreatables.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plAllCreatables.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClient.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClient.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClientCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClientUpdateFormat.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plSimStateMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/pnAllCreatables.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/Dirt.ICO create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/Microphone.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/Speaker.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/banner.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bitmap1.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bitmap2.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bitmap3.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bmp00001.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bmp00002.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bmp00003.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bmp00004.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bmp00005.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/book.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/book_cli.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/book_hig.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cnsl1.bin create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_clicked.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_disabled.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_down.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_grab.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_left.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_open.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_poised.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_right.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_u.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_up.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_upward.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/headspin.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/plClient.exe.manifest create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/plClient.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.01.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.02.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.03.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.04.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.05.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.06.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.07.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.08.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.09.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.10.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.11.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.12.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.13.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.14.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.15.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.16.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.17.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.18.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking_Text.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Updating_Text.jpg create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/winmain.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/DllMain.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.def create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/plLauncherCallback.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plFileEncrypt/main.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plFileSecure/main.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plLogDecrypt/plLogDecrypt.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Main.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageInfo/plAllCreatables.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageInfo/plPageInfo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/main.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/pfAllCreatables.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/plPageOptimizer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/plPageOptimizer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/Dirt.ICO create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/main.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plInstallerReg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plInstallerReg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plSetPlasmaPath.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plSetPlasmaPath.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plUnzip.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plUnzip.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/Dirt.ICO create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/main.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plFileGrabber.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plFileGrabber.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plManifest.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plManifest.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaServers.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaServers.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/PythonInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/main.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Dirt.ICO create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Main.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/banner.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HS_POINT2.inc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HeadSpin.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HeadSpin.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBiExpander.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBitVector.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBitVector.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBounds.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBounds.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsColorRGBA.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsConfig.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsCritSect.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsCritSect.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptionStack.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptionStack.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptions.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFastMath.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFastMath.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFixedTypes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsGeometry3.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsGeometry3.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsHashTable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsLOD.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMMIOStream.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMMIOStream.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMalloc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMalloc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix33.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix33.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix44.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix44.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMemory.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMemory.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsPoint2.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQuat.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQuat.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQueue.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsRefCnt.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSTLStream.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSTLStream.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSafeRefCnt.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSafeRefCnt.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsScalar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlSortUtils.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlUtils.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlUtils.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStream.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStream.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStringTokenizer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStringTokenizer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTempPointer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTemplates.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTemplates.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Mac.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Unix.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Win.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTypes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsUtils.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsUtils.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWide.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWide.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWindowHndl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWindows.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/notes.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/pcSmallRect.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/pcSmallRect.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plGeneric.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plGeneric.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plLoadMask.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plLoadMask.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plQuality.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plRefCnt.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plRenderLevel.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plTweak.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plViewTransform.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plViewTransform.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/hsExeError.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/hsExeMalloc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/inc/pfAllCreatables.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfAnimationCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfObjectFlocker.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfObjectFlocker.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plAnimDebugList.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plAnimDebugList.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plBlower.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plBlower.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFilterCoordInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFilterCoordInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFollowMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFollowMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLightModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLightModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLineFollowMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLineFollowMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plRandomCommandMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plRandomCommandMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plStereizer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plStereizer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plViewFaceModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plViewFaceModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/pfAudioCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plListener.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plRandomSoundMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plRandomSoundMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRVault.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRVault.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraProxy.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraProxy.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraBrain.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraBrain.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plInterestingModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plInterestingModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfCharacterCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerInfo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/plPlayerModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/plPlayerModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plANDConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plANDConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plActivatorConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plActivatorConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plAnimationEventConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plAnimationEventConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plConditionalObjectCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plControlEventConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plControlEventConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plFacingConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plFacingConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plKeyPressConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plKeyPressConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerInBoxConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerInBoxConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerIntersectPlaneConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerIntersectPlaneConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plORConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plORConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectIntersectPlaneConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectIntersectPlaneConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plPickedConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plPickedConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfAvatarConsoleCommands.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfCCRConsoleCommands.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsole.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsole.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCmd.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCmd.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommandsNet.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleContext.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleContext.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleDirSrc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleDirSrc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleEngine.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleEngine.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIButtonMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIButtonMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICheckBoxCtrl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICheckBoxCtrl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIClickMapCtrl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIClickMapCtrl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlHandlers.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlHandlers.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICtrlGenerator.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICtrlGenerator.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogHandlers.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogNotifyProc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogNotifyProc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDragBarCtrl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDragBarCtrl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDraggableMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDraggableMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDynDisplayCtrl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDynDisplayCtrl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIEditBoxMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIEditBoxMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIKnobCtrl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIKnobCtrl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListBoxMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListBoxMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListElement.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListElement.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMenuItem.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMenuItem.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMultiLineEditCtrl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMultiLineEditCtrl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIPopUpMenu.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIPopUpMenu.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIProgressCtrl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIProgressCtrl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIRadioGroupCtrl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIRadioGroupCtrl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITagDefs.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITagDefs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITextBoxMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITextBoxMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIUpDownPairMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIUpDownPairMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIValueCtrl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIValueCtrl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgrCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/BlueSpiral/pfGmBlueSpiral.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/BlueSpiral/pfGmBlueSpiral.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/ClimbingWall/pfGmClimbingWall.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/ClimbingWall/pfGmClimbingWall.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Heek/pfGmHeek.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Heek/pfGmHeek.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Marker/pfGmMarker.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Marker/pfGmMarker.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/VarSync/pfGmVarSync.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/VarSync/pfGmVarSync.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgrCreatables.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBook.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBook.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBookCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKI.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKI.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKICreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfPlayerBookMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfPlayerBookMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationDataMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationDataMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizedString.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizedString.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/pfLoginDialog.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/plLoginDialog.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/plLoginDialog.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfBackdoorMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfGUINotifyMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfGameGUIMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfKIMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfKIMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMarkerMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMarkerMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMessageCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMovieEventMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMovieEventMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plArmatureEffectMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plArmatureEffectMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plClothingMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plClothingMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGame.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGame.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGameGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsgGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGame.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGame.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGameGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsgGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGame.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGame.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGameGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsgGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGame.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGame.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGameGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsgGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGame.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGame.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGameGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsgGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGame.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGame.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGameGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsgGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCli.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCli.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsgGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsgGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagement.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagement.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagementGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimation.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimation.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimationGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCamera.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCamera.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCameraGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDraw.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDraw.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDrawGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterfaceGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMisc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSys.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSys.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSysGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysics.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysics.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysicsGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pfPythonCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonHelpers.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonPack.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonPack.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonParameter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifierGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStruct.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStruct.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStructGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStruct.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStruct.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStructGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVault.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVault.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVaultGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAlarm.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAlarm.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgrGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgrGlue2.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCluster.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCluster.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyClusterGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColor.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColor.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColorGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrainGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinates.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinates.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinatesGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSource.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSourceGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControlGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicText.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicText.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicTextGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyEnum.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyEnum.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButton.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButton.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButtonGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBox.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBox.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBoxGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMap.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMapGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBar.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBarGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggable.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggableGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicText.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicText.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicTextGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBox.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBox.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBoxGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBox.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBox.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBoxGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEdit.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEdit.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroup.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroup.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroupGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBox.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBox.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBoxGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValue.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValueGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialog.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialog.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialogGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenu.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenu.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenuGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkin.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkin.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkinGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScore.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScore.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScoreGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3Glue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGlueHelpers.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShaderGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImage.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImage.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImageGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBook.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBook.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBookGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKey.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKey.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMap.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMapGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgrGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44Glue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayerGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgrGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfoGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotify.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotify.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotifyGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayerGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDL.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDL.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDLGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgrGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfoGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLog.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLog.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLogGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStream.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStream.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStreamGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterfaceGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVault.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVault.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRef.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRef.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRefGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNodeGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSet.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSet.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfArray.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFile.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFile.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFileEntry.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFileEntry.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfPrintStackTrace.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfPrintStackTrace.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfStackTrace.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfStackTrace.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfTextFile.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfTextFile.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/pfSurfaceCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plDistOpacityMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plDistOpacityMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityLay.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityLay.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plGrabCubeMap.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plGrabCubeMap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerAVI.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerAVI.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerMovie.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerMovie.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsGMatState.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsGMatState.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsResMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsTimer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plAudible.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCCRMgrBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plClassIndexMacros.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCreatableStrings.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plDrawable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plNetServerDatabaseStructClassIndexes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPhysical.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPipeResReq.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPipeline.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfile.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfileManager.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfileManager.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plRefFlags.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plgDispatch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnAllCreatables.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnNucleusCreatables.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnSingletons.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcAllIncludes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcCore.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcIo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcLog.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcLog.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcThread.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcTimer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/pnAsyncCore.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNt.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNt.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtFile.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtInt.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtSocket.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtThread.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Unix/pnAceUx.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9x.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9x.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xFile.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xInt.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xSocket.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xThread.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Win32/pnAceW32Dns.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Win32/pnAceW32Thread.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/pnAceInt.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceCore.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceIo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceLog.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceThread.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceTimer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.bat create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/W32Int.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreError.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreGui.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreMail.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreTools.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/pnCsrCli.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/pnCsrCli.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/pnCsrNet.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/pnCsrNet.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatch.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatchLogBase.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatchLogBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/pnDispatchCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plCreator.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plFactory.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plFactory.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/BlueSpiral/pnGmBlueSpiral.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/BlueSpiral/pnGmBlueSpiral.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/ClimbingWall/pnGmClimbingWall.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/ClimbingWall/pnGmClimbingWall.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Heek/pnGmHeek.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Heek/pnGmHeek.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Marker/pnGmMarker.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Marker/pnGmMarker.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/TicTacToe/pnGmTicTacToe.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/TicTacToe/pnGmTicTacToe.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/VarSync/pnGmVarSync.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/VarSync/pnGmVarSync.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/pnGameMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/pnGameMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniAllIncludes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniChange.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniCore.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniCore.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniSrv.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniSrv.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/pnIni.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Private/Win32/pnW32IniChange.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Private/pnIniSrv.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plControlDefinition.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plControlEventCodes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plInputMap.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plInputMap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plKeyDef.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plKeyMap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plOSMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/hsKeyedObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/hsKeyedObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKey.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKey.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKeyImp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKeyImp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plMsgForwarder.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plMsgForwarder.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plReceiver.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plTempKey.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plTempKey.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/pnKeyedObjectCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/pnMail.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/pnMail.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plAttachMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plAudioSysMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCameraMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCameraMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCmdIfaceModMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCollisionMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCorrectionMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCursorChangeMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plDISpansMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnableMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnvEffectMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnvEffectMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEventCallbackMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plFakeOutMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plIntRefMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessage.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessage.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessageWithCallbacks.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessageWithCallbacks.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMultiModMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeChangeMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeChangeMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeRefMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNotifyMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNotifyMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plObjRefMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plPipeResMakeMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plPlayerPageMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plProxyDrawMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plProxyDrawMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRefMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRefMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRemoteAvatarInfoMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLModifierMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLModifierMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLNotificationMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSatisfiedMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSelfDestructMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSetNetGroupIDMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSharedStateMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationSynchMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSingleModMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSoundMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSoundMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plTimeMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plTimeMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plWarpMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/pnMessageCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plConditionalObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plConditionalObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plMultiModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plMultiModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plSingleModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plSingleModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/pnModifierCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAges.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAges.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAllIncludes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbEchoMsgs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbError.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbError.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbProtocol.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbProtocol.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbAuthKey.hpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbConst.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbCsrKey.hpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbGameKey.hpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbGateKeeperKey.hpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNetBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcChannel.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcCli.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcEncrypt.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcUtils.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNetCli.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plGenericVar.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plGenericVar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetAddress.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetAddress.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetApp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetApp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetCommonStats.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetGroup.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetGroup.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetServers.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetServers.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetSharedState.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetSharedState.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSDLTypes.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSDLTypes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedValue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedValue.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommon.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommon.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommonCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdDns.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdIcmp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdTcp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetDiag.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetDiag.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetSys.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNetLog.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlApi.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlCli.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlSrv.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Auth/pnNpCli2Auth.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Auth/pnNpCli2Auth.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Csr/pnNpCli2Csr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Csr/pnNpCli2Csr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2File/pnNpCli2File.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2File/pnNpCli2File.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Game/pnNpCli2Game.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Game/pnNpCli2Game.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2GateKeeper/pnNpCli2GateKeeper.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2GateKeeper/pnNpCli2GateKeeper.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Db/pnNpSrv2Db.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Db/pnNpSrv2Db.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Log/pnNpSrv2Log.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Log/pnNpSrv2Log.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Score/pnNpSrv2Score.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Score/pnNpSrv2Score.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2State/pnNpSrv2State.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2State/pnNpSrv2State.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Vault/pnNpSrv2Vault.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Vault/pnNpSrv2Vault.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpAllIncludes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpCommon.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpCommon.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/pnNetProtocol.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/pnOraLib.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/pnOraLib.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrAllIncludes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBranchId.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBranchId.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildId.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildId.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildString.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildString.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildType.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildType.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrProductId.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrProductId.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/pnProduct.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plAudioInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plAudioInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plCoordinateInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plCoordinateInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plDrawInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plDrawInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plObjInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plObjInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSceneObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSceneObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSimulationInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSimulationInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/pnSceneObjectCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlAllIncludes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlConn.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlConn.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlUtil.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlUtil.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/pnSqlLib.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/hsTimer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimedValue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimedValue.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimerCallbackManager.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimerCallbackManager.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBranchDate.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBuildDates.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBuildDates.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnTimerCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxStr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxSync.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxUuid.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/W32Int.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Addr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Dll.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Misc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Path.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Str.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Sync.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Time.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Uuid.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAddr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAddr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAllIncludes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtArray.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtArray.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBase64.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBase64.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCoreLib.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtEndian.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtEndian.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMath.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMath.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMisc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMisc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPath.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPath.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPragma.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPriQ.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRand.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRand.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRef.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSkipList.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSort.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSpareList.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSpareList.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSubst.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSubst.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSync.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTime.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTime.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTls.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTls.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTypes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtUuid.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtUuid.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/pnUtils.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/pnUtils.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/Unix/pnUteUxTime.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/Win32/pnUteW32Time.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/pnUteTime.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/pnUteTls.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/inc/plAllCreatables.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeDescription.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeDescription.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeManifest.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeManifest.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderPaging.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plBackgroundDownloader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plBackgroundDownloader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleNull.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleNull.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudible.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudible.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudibleProxy.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudibleProxy.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioReaderCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSound.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSound.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWavFile.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWavFile.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioCore.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioCoreCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioFileReader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioFileReader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plBufferedFileReader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plBufferedFileReader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plFastWavReader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plFastWavReader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundDeswizzler.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundDeswizzler.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnim.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnim.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnimInstance.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnimInstance.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGApplicator.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGApplicator.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGChannel.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGChannel.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterSDLModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterSDLModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBehaviors.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBehaviors.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrain.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrain.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCoop.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCoop.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainPuppet.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainPuppet.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainStaticNPC.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainUser.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainUser.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvDefs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvMotorHuman.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvMotorHuman.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTask.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTask.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskBrain.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskBrain.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskOrient.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskOrient.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingLayout.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingSDLModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingSDLModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCoopCoordinator.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCoopCoordinator.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCritterCommands.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMatrixChannel.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMatrixChannel.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMultistageBehMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMultistageBehMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plOneShotMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plOneShotMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPointChannel.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPointChannel.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPuppetBrainMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPuppetCommands.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plQuatChannel.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plQuatChannel.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plScalarChannel.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plScalarChannel.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSeekPointMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSeekPointMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSittingModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSittingModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSwimRegion.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSwimRegion.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plCompress.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibCompress.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibCompress.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibStream.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibStream.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/hsStringTable.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/hsStringTable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfoLogging.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plContainer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plKeysAndValues.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plKeysAndValues.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccMeshSmooth.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccMeshSmooth.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessGeometry.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessGeometry.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessPartySpan.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSnapShot.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSnapShot.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSpan.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessTriSpan.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessVtxSpan.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plActivePrintShape.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plActivePrintShape.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAuxSpan.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAvMeshSmooth.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAvMeshSmooth.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCluster.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCluster.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plClusterGroup.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plClusterGroup.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCutter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCutter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableGenerator.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableGenerator.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpans.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpans.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpansExport.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaBulletMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaBulletMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecal.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecal.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecalMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecalMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaFootMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaFootMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaPuddleMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaPuddleMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgrVS.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleVSMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoVSMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoVSMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaWakeMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaWakeMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plFixedWaterState7.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plFixedWaterState7.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeoSpanDice.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeoSpanDice.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeometrySpan.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeometrySpan.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInstanceDrawInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInstanceDrawInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInterMeshSmooth.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInterMeshSmooth.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphArray.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphArray.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphDelta.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphDelta.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequence.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequence.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequenceSDLMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequenceSDLMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plParticleFiller.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plParticleFiller.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plPrintShape.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plPrintShape.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plProxyGen.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plProxyGen.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plRipVSConsts.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSharedMesh.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSharedMesh.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTree.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTree.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTreeMaker.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTreeMaker.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanInstance.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanInstance.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTemplate.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTemplate.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTypes.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTypes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plTimedInterp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plVisLOSMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plVisLOSMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSet7.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSet7.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetBase.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetShaderConsts.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plEncryption/plChecksum.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plEncryption/plChecksum.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Mac.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_PS2.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Unix.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Win.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plBrowseFolder.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plBrowseFolder.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plEncryptedStream.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plEncryptedStream.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plFileUtils.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plFileUtils.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plInitFileReader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plInitFileReader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plSecureStream.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plSecureStream.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plStreamSource.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plStreamSource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/HS_RECT.inc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodecManager.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodecManager.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTDirectXCodec.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTDirectXCodec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTSoftwareCodec.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTSoftwareCodec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsRect.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/notes.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plAVIWriter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plAVIWriter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBitmap.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBitmap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBumpMapGen.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBumpMapGen.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plCubicEnvironmap.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plCubicEnvironmap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynSurfaceWriter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynSurfaceWriter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynamicTextMap.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynamicTextMap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFont.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFont.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFontCache.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFontCache.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plGImageCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plImageConvert.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plLODMipmap.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plLODMipmap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plMipmap.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plMipmap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plTGAWriter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plTGAWriter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plWinFontCache.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plWinFontCache.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plDirectShadowMaster.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plDirectShadowMaster.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plGLightCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightInfo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightKonstants.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightProxy.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightProxy.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightSpace.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightSpace.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPerspDirSlave.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPerspDirSlave.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPointShadowMaster.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPointShadowMaster.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowCaster.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowCaster.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowMaster.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowMaster.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowSlave.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowSlave.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsGRenderProcs.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsGRenderProcs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxAngleFade.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxAngleFade.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistFade.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistFade.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistShade.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistShade.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxGlobalShade.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxGlobalShade.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxIntenseAlpha.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxIntenseAlpha.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistFade.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistFade.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistShade.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistShade.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/plGRenderProcsCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsOscillator.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsOscillator.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsPerterber.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsPerterber.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/plGeometryCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plAvatarInputInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plAvatarInputInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDInputDevice.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDInputDevice.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDebugInputInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDebugInputInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputCoreCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputManager.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputManager.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plTelescopeInputInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plTelescopeInputInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsInterp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsInterp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsKeys.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsKeys.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsTimedValue.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plATCEaseCurves.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimEaseTypes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimPath.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimPath.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plController.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plController.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plInterpCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plModulator.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plModulator.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/notes.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plClosest.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plClosest.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegion.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegion.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionPlanes.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionPlanes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionTypes.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionTypes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plIntersectCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plRegionBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolume.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolume.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolumeTypes.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolumeTypes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plVolumeIsect.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plVolumeIsect.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plJPEG/plJPEG.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plJPEG/plJPEG.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsNoiseFunc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsNoiseFunc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsRadixSort.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsRadixSort.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsSearchVersion.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plAvg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plAvg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plRandom.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plTriUtils.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plTriUtils.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAIMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAIMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAccountUpdateMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAccountUpdateMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plActivatorMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAgeLoadedMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAngularVelocityMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimCmdMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimCmdMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimationEventCallbackMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plApplyAvatarCustomizationsMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plApplyStoredAvatarSettingsMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvCoopMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvCoopMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarFootMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarFootMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plBulletMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plBulletMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMessageCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCaptureRenderMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCaptureRenderMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbEventMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCollideMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCollideMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCondRefMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plConnectedToVaultMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plConsoleMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDeviceRecreateMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynaDecalEnableMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynaDecalEnableMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicEnvMapMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicEnvMapMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicTextMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicTextMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plElementRefMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plEnvEffectMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plExcludeRegionMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputEventMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputEventMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputIfaceMgrMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputIfaceMgrMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInterestingPing.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSHitMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSHitMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSRequestMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSRequestMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLayRefMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLayRefMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLightRefMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinearVelocityMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinkToAgeMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinkToAgeMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plListenerMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plListenerMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAgeMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAgeMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadCloneMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadCloneMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatRefMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatrixUpdateMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatrixUpdateMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMemberUpdateMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMeshRefMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMovieMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMultistageMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMultistageMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNCAgeJoinerMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNCAgeJoinerMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetClientMgrMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetClientMgrMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetCommMsgs.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetCommMsgs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetOwnershipMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetVoiceListMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetVoiceListMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNodeCleanupMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotCallbacks.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotCallbacks.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plParticleUpdateMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPickedMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPlayerMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPreloaderMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderRequestMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderRequestMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plReplaceGeometryMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plResMgrHelperMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plResponderMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRideAnimatedPhysMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRideAnimatedPhysMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRippleShapeMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRippleShapeMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRoomLoadNotifyMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plShadowCastMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimInfluenceMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimInfluenceMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimStateMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimStateMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSpawnModMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSpawnRequestMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSwimMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSwimMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSynchEnableMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSynchEnableMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTimerCallbackMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTransitionMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTransitionMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTriggerMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plUniqueIdsMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plVaultNotifyMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plVaultNotifyMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimEventModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimEventModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimTimeConvertSDLModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimTimeConvertSDLModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAxisAnimModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAxisAnimModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plCloneSpawnModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plCloneSpawnModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDecalEnableMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDecalEnableMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDetectorLog.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDetectorLog.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plGameMarkerModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plGameMarkerModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plImageLibMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plImageLibMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plInterfaceInfoModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plInterfaceInfoModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLayerSDLModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLayerSDLModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plMaintainersMarkerModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plMaintainersMarkerModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plModifierCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderSDLModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderSDLModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSDLModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSDLModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSimpleModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSimpleModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSoundSDLModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSoundSDLModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSpawnModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSpawnModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plTagModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plTagModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plLinkEffectsMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plLinkEffectsMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientCommInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientGroup.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientGroup.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrLoad.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrRecord.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrSend.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrShow.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrTask.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrVault.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrVoice.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientStats.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientStats.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVNodeMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVault.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVault.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetObjectDebugger.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetObjectDebugger.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetVoiceList.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetVoiceList.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommTask.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommTask.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientStatsRecorder.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientStreamRecorder.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plClientGuid.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plClientGuid.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommon.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommon.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonConstants.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonHelpers.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonHelpers.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMember.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMember.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgHandler.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetResManager.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetResManager.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetServerSessionInfo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetServerSessionInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plServerGuid.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plServerGuid.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plSpawnPointInfo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plSpawnPointInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAllIncludes.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAuth.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAuth.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCore.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCore.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCsr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCsr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglFile.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglFile.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGame.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGame.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglMisc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglTrans.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/plNetGameLib.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetCommonMessage.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessageCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgHelpers.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgHelpers.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgVersion.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransport.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransport.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransportMember.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransportMember.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plBoundInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plBoundInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plConvexVolume.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plConvexVolume.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plEffectTargetInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticle.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleApplicator.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleApplicator.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEffect.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEffect.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEmitter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEmitter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleGenerator.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleGenerator.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSDLMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSDLMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXConvert.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXConvert.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXStream.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plDetectorModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plEnvEffectDetector.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plEnvEffectDetector.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalProxy.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalProxy.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSDLModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSDLModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSndGroup.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSndGroup.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicsSoundMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicsSoundMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPickingDetector.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPickingDetector.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSittingModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSittingModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsFogControl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsG3DDeviceSelector.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsG3DDeviceSelector.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGColorizer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGColorizer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDDrawDllLoad.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDDrawDllLoad.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDeviceRef.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGEnviron.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGEnviron.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsWinRef.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCaptureRender.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCaptureRender.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTarget.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTargetModifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTargetModifier.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCullTree.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCullTree.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCuller.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXBufferRefs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXDeviceRef.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXDeviceRefs.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXEnumerate.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXEnumerate.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXLightRef.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPipeline.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPipeline.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPixelShader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPixelShader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXRenderTargetRef.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXSettings.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXShader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXShader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextFont.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextFont.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextureRef.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXVertexShader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXVertexShader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDebugText.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDebugText.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDrawPrim.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDynamicEnvMap.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDynamicEnvMap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plFogEnvironment.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plFogEnvironment.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plGBufferGroup.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plGBufferGroup.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipeDebugFlags.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipelineCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipelineCreate.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPlates.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPlates.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plRenderTarget.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plRenderTarget.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStatusLogDrawer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStatusLogDrawer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStencil.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextGenerator.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextGenerator.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTransitionMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTransitionMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plVertCoder.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plVertCoder.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plBSDiffBuffer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plBSDiffBuffer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plDiffBuffer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plDiffBuffer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plIndexFile.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plIndexFile.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plKeyFinder.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plKeyFinder.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLoc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLoc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLocalization.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLocalization.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plPageInfo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plPageInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryHelpers.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryHelpers.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManager.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManager.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManagerHelper.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManagerHelper.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgrCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgrSettings.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plVersion.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plVersion.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/animation.sdl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/avatar.sdl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/cloneMessage.sdl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/clothing.sdl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/morph.sdl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/particle.sdl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/physical.sdl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/responder.sdl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/sound.sdl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/xregion.sdl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDL.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLDescriptor.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLParser.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateChangeNotifier.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateDataRecord.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateDescriptor.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateVariable.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plVarDescriptor.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowser.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlgHandlers.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plCullPoly.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plCullPoly.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccTree.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccTree.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluder.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluder.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluderProxy.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluderProxy.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPageTreeMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPageTreeMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPostEffectMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPostEffectMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceRegion.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceRegion.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRenderRequest.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRenderRequest.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisRegion.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisRegion.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketReader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketReader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketWriter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketWriter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plFdSet.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plFdSet.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plMemBuffer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plMemBuffer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plNet.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plNet.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plOutgoingUdpSocket.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plOutgoingUdpSocket.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plRingBuffer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plRingBuffer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plSocket.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plSocket.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpListenSocket.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpListenSocket.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpSocket.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpSocket.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plAutoProfile.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plAutoProfile.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plCalculatedProfiles.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plCalculatedProfiles.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plProfileManagerFull.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plProfileManagerFull.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plStatGatherCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plEncryptLogLine.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plEncryptLogLine.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plLoggable.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plLoggable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLoggerC.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/AssShader.zip create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_BiasNormals.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAadd.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAbase.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAmult.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAadd.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAbase.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAmult.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CbaseAbase.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAadd.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAbase.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAmult.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CompCosines.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_GrassShader.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_MoreCosines.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_ShoreLeave6.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveDecEnv.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveFixed.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveGraph.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveGrid.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveRip.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_BiasNormals.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_CompCosines.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_GrassShader.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_ShoreLeave6.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_ShoreLeave7.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec1Lay.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec1Lay_7.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay11.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay11_7.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay12.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay12_7.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDecEnv.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDecEnv_7.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveFixedFin6.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveFixedFin7.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveGraph2.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveGridFin.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveRip.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveRip7.inl create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plGrassShaderMod.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plGrassShaderMod.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerAnimation.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerAnimation.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerDepth.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerDepth.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerMultiply.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerMultiply.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerOr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerOr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerShadowBase.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerShadowBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerWrapper.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerWrapper.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShaderTable.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShaderTable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plSurfaceCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_BiasNormals.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAAdd.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAMult.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAbase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAMult.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAadd.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAbase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CbaseAbase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAAdd.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAMult.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAbase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CompCosines.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_GrassShader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_MoreCosines.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_ShoreLeave6.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveDecEnv.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveFixed.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveGraph.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveGrid.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveRip.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_BiasNormals.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_CompCosines.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_GrassShader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_ShoreLeave6.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_ShoreLeave7.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec1Lay.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec1Lay_7.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay11.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay11_7.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay12.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay12_7.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDecEnv.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDecEnv_7.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveFixedFin6.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveFixedFin7.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveGraph2.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveGridFin.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveRip.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveRip7.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsAffineParts.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsAffineParts.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsEuler.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsEuler.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/mat_decomp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/mat_decomp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/notes.txt create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID_Unix.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID_Win32.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plClientUnifiedTime.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plClientUnifiedTime.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plTimeSpan.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plTimeSpan.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTimeCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/Intern.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/Pch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plAgeInfoSource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plDniCoordinateInfo.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plDniCoordinateInfo.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVault.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVault.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultClientApi.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultClientApi.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultConstants.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultConstants.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultCreatable.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNodeAccess.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNodeAccess.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWinStrBlock/strblock.c create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWinStrBlock/strblock.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/basewnd.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/basewnd.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plButton.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plCheckBox.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plComboBox.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plControl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plDialog.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plEdit.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plLabel.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plListBox.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plProgressBar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plRadioButton.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plStatusBar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plTrackBar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWindow.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/webhost.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/webhost.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientCommGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientGame/pyNetClientGame.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientGame/pyNetClientGame.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/creatables.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/dllmain.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/pyPlasmaTest.py create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/dllmain.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/pyPloticus.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/pyPloticus.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgrGlue.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/CheckFolderVar/CheckFolderVar.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxAss/ValdezInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/BipedKiller.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/ComponentDummies.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/WavFileStructs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/icon1.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/pfGUISkinComp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/pfGUISkinComp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAGComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorBaseComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorBaseComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimCompProc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimCompProc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimEventComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimEventComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimObjInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAudioComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAudioComponents.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBase.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBlock.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBlock.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIComp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIComp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIParams.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIParams.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAvatarComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAvatarComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBehavioralComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBehavioralComponents.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBipedKiller.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBlowComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBlowComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCAnimParamBlock.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCAnimParamBlock.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponents.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickDragComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickDragComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickableComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickableComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClimbComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClimbComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClothingComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClothingComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClusterComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClusterComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentBase.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentExt.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentProcBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentReg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentTools.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentTools.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDicer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDicer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent_old.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent_old.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plExcludeRegionComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plExcludeRegionComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFlexibilityComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootPrintComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootstepComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootstepComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUICompClassIDs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUIComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUIComponents.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGrassComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGrassComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plIgnoreComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plImpactGadgetComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plImpactGadgetComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plInventoryObjComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plInventoryObjComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLODFadeComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLODFadeComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightGrpComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightGrpComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightMapComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightMapComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLineFollowComp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxAnimUtils.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxAnimUtils.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxWaveUtils.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxWaveUtils.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMiscComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMiscComponents.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMorphSeqComp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageBehComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageBehComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageStage.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageStage.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNPCSpawnComp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNPCSpawnComp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNavigableComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNavigableComponents.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNoteTrackDlgComp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNoteTrackDlgComp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackAnim.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackAnim.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackDlg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackDlg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plObjectFlockerComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plObjectFlockerComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plOneShotComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plOneShotComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plParticleComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plParticleComponents.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysConstraintComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicalComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicalComponents.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicsGroups.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickLocalizationDlg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickLocalizationDlg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickMaterialMap.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickMaterialMap.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeBase.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeComp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPythonFileComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPythonFileComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plRepComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderAnim.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderAnim.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderCmd.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponentPriv.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderGetComp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderGetComp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderLink.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderLink.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderMtl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderMtl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderWait.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderWait.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSeekPoint.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plShadowComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plShadowComponents.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSmoothComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSoftVolumeComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSoftVolumeComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plTemplateComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plTypesComponents.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plVolumeGadgetComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plVolumeGadgetComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plWaterComponent.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plWaterComponent.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plXImposter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plXImposter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/zoomin1.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/StringTokenizer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/StringTokenizer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/UserPropMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/UserPropMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsControlConverter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsControlConverter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsConverterUtils.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsConverterUtils.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaterialConverter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaterialConverter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaxLayerBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsVertexShader.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsVertexShader.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plBitmapCreator.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plBitmapCreator.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plClusterUtil.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plClusterUtil.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plConvert.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plConvert.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistTree.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistTree.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistributor.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistributor.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLayerConverter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLayerConverter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLightMapGen.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLightMapGen.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMaxLightContext.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMeshConverter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMeshConverter.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderGlobalContext.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderGlobalContext.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderInstance.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderInstance.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/SimpleExport.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/SimpleExport.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plErrorMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plErrorMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportDlg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportDlg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportErrorMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportErrorMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportLogErrorMsg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportLogErrorMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportProgressBar.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportProgressBar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plProgressBar.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/GlobalUtility.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/GlobalUtility.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/MaxAllocDll.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/MaxAllocDll.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/SimpleExport.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/indexes.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/main.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/main.def create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plActionTableMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plActionTableMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plAgeDescInterface.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plAgeDescInterface.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plCommonObjLib.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plCommonObjLib.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentDlg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentDlg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentPanel.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentPanel.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plGetLocationDlg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plGetLocationDlg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxAccelerators.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxAccelerators.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxCFGFile.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxCFGFile.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMenu.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMenu.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMeshExtractor.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMeshExtractor.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeBase.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeData.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxUtils.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxUtils.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMtlCollector.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMtlCollector.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plNodeLock.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plNodeLock.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysXCooking.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysXCooking.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysicalProps.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysicalProps.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPlasmaRefMsgs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPluginResManager.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPluginResManager.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPythonMgr.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPythonMgr.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResCollector.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResCollector.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResetXform.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResetXform.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plSaveSelected.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plSaveSelected.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureExportLog.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureExportLog.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureSearch.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureSearch.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/DllEntry.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/OldMat/hsMaxLayer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/OldMat/hsMaxLayerBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.def create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/DLLEntry.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLightBaseAnimDlgProc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLightBaseAnimPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLights.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTObjLightDesc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTPlasmaLights.def create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLight.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLight.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLightClassDesc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLightPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightBase.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLights.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLights.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightsPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/prim.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/target.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plAngleAttenLayer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plAngleAttenLayer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayerBitmapPB.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayerBitmapPB.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTex.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTex.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBasicPB.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBasicPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBitmapPB.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plMAXCameraLayer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plMAXCameraLayer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plPlasmaMAXLayer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayerBitmapPB.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayerBitmapPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthConvert.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthNode.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthNode.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlAnimPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlAnimPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlBasicPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlBasicPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtlPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlDlg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlDlg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAdvPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAdvPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAnimPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAnimPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlBasicPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlBasicPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlLayersPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlLayersPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlDlg.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlDlg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlPB.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plNoteTrackWatcher.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plNoteTrackWatcher.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtlPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassAnimDlgProc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassAnimDlgProc.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassBaseParamIDs.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAdvPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAdvPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAnimPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAnimPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBase.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBase.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBasicPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBasicPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlLayersPB.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlLayersPBDec.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/MaxPlasmaMtls.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Shaders.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Shaders.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plBMSampler.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plBMSampler.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDetailCurveCtrl.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDetailCurveCtrl.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDrawCurve.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMaterialRefMsg.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMtlImport.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMtlImport.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/res/detailBgnd8bit.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/res/dmtlbut.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/res/dmtlmsk.bmp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneSync.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneSync.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneViewer.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneViewer.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneWatcher.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneWatcher.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plKeyRefSort.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plKeyRefSort.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plMaxFileData.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plMaxFileData.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginApp.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginApp.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginClient.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginClient.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/MigrationTask.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/MigrationTask.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/OptionalDialog.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/OptionalDialog.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/small.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plAllCreatables.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plDatMerger.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawKeyedObject.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawKeyedObject.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawPageAccessor.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawPageAccessor.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawResManager.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawResManager.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/hsCodecManagerStub.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontConverter.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontConverterProc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontFreeType.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontFreeType.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/res/icon1.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/res/plFontConverter.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/res/resource.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.dsp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.vcproj create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowserWndProc.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResTreeView.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResTreeView.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plWinRegistryTools.cpp create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plWinRegistryTools.h create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/dataicon.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/icon1.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/icon2.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/indexico.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/mergedda.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/mergedin.ico create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/plResBrowser.rc create mode 100644 MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/resource.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/IJL/ijlnotes.htm create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/MySQL/ReadMe.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/PhysX/release_notes.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/PlatformSDK/ReadMe.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/License.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/README create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.dll create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.exp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.pdb create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/Scintilla.dll create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/Scintilla.exp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/Scintilla.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/Scintilla.pdb create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Design.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Icons.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Lexer.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciBreak.jpg create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciCoding.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciRest.jpg create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciTEIco.png create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciWord.jpg create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaDoc.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaDownload.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaHistory.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaRelated.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaToDo.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaUsage.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Steps.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/index.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Accessor.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Face.py create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/HFacer.py create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/KeyWords.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Platform.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/PropSet.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/SString.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/SciLexer.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Scintilla.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Scintilla.iface create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/ScintillaWidget.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/WindowAccessor.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/AutoComplete.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/AutoComplete.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CallTip.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CallTip.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CellBuffer.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CellBuffer.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CharClassify.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CharClassify.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ContractionState.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ContractionState.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Document.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Document.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/DocumentAccessor.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/DocumentAccessor.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Editor.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Editor.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ExternalLexer.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ExternalLexer.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Indicator.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Indicator.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyMap.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyMap.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyWords.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAPDL.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAU3.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAVE.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAda.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAsm.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAsn1.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBaan.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBash.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBasic.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBullant.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCLW.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCPP.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCSS.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCaml.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexConf.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCrontab.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCsound.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexEScript.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexEiffel.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexErlang.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexFlagship.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexForth.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexFortran.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexGen.py create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexGui4Cli.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexHTML.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexHaskell.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexInno.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexKix.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexLisp.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexLout.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexLua.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexMMIXAL.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexMPT.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexMSSQL.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexMatlab.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexMetapost.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexNsis.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexOpal.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexOthers.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexPB.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexPOV.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexPS.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexPascal.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexPerl.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexPython.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexRebol.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexRuby.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexSQL.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexScriptol.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexSmalltalk.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexSpecman.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexSpice.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexTADS3.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexTCL.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexTeX.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexVB.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexVHDL.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexVerilog.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexYAML.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LineMarker.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LineMarker.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/PropSet.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/RESearch.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/RESearch.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/SVector.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/SciTE.properties create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ScintillaBase.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ScintillaBase.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Style.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Style.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/StyleContext.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/StyleContext.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/UniConversion.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/UniConversion.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ViewStyle.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ViewStyle.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/WindowAccessor.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/XPM.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/XPM.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/version.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/win32/Margin.cur create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/win32/PlatWin.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/win32/PlatformRes.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/win32/SciTE.properties create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/win32/ScintRes.rc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/win32/Scintilla.def create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/win32/ScintillaWin.cxx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/win32/deps.mak create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/win32/makefile create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/win32/scintilla.mak create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/win32/scintilla_vc6.mak create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/UnicoWS/License.Txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/UnicoWS/redist.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/UnicoWS/unicows.dll create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/UnicoWS/unicows.pdb create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/UnicoWS/unicows.sym create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/AUTHORS create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/COPYING create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/ChangeLog create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/INSTALL create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/Makefile.am create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/Makefile.in create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/NEWS create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/README create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/Speex.spec create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/Speex.spec.in create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/TODO create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/acinclude.m4 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/aclocal.m4 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/config.guess create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/config.sub create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/configure create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/configure.in create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/doc/Makefile.am create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/doc/Makefile.in create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/doc/manual.pdf create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/install-sh create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/Makefile.am create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/Makefile.in create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/bits.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/cb_search.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/cb_search.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/exc_10_16_table.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/exc_10_32_table.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/exc_20_32_table.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/exc_5_256_table.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/exc_5_64_table.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/exc_8_128_table.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/filters.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/filters.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/filters_sse.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/gain_table.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/gain_table_lbr.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/hexc_10_32_table.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/hexc_table.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/high_lsp_tables.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/lpc.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/lpc.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/lsp.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/lsp.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/lsp_tables_nb.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/ltp.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/ltp.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/ltp_sse.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/math_approx.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/math_approx.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/misc.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/misc.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/modes.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/modes.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/nb_celp.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/nb_celp.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/quant_lsp.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/quant_lsp.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/sb_celp.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/sb_celp.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/speex.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/speex_bits.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/speex_callbacks.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/speex_callbacks.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/speex_header.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/speex_header.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/speex_stereo.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/stack_alloc.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/stereo.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/testenc.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/testenc_uwb.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/testenc_wb.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/vbr.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/vbr.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/vq.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/libspeex/vq.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/ltmain.sh create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/missing create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/mkinstalldirs create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/Makefile.am create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/Makefile.in create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/getopt.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/getopt1.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/getopt_win.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/ogg.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/os_types.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/speexdec.1 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/speexdec.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/speexenc.1 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/speexenc.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/wav_io.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/wav_io.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/wave_out.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/src/wave_out.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/Makefile.am create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/Makefile.in create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/libspeex/Debug/libspeex.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/libspeex/Debug/vc70.pdb create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/libspeex/Makefile.am create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/libspeex/Makefile.in create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/libspeex/Release/libspeex.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/libspeex/libspeex.dsp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/libspeex/libspeex.dsw create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/libspeex/libspeex.opt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/libspeex/libspeex.plg create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/libspeex/libspeex.sln create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/libspeex/libspeex.vcproj create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexdec/Makefile.am create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexdec/Makefile.in create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexdec/speexdec.dsp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexdec/speexdec.dsw create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexdec/speexdec.opt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexenc/Copy of ogg.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexenc/Makefile.am create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexenc/Makefile.in create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexenc/ogg.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexenc/speexenc.dsp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexenc/speexenc.dsw create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexenc/speexenc.opt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/Win32/speex-1.0.1/win32/speexenc/speexenc.plg create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/COPYING.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Changes.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Include/expat.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Libs/libexpat.dll create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Libs/libexpat.exp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Libs/libexpat.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Libs/libexpatw.dll create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Libs/libexpatw.exp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Libs/libexpatw.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/README.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/README.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/expat.sln create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/ascii.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/asciitab.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/expat.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/expat.vcproj create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/expat_static.vcproj create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/expatw.vcproj create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/expatw_static.vcproj create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/expatw_static_xbox.vcproj create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/iasciitab.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/internal.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/latin1tab.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/libexpat.def create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/libexpatw.def create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/macconfig.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/nametab.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/utf8tab.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/winconfig.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/xmlparse.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/xmlrole.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/xmlrole.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/xmltok.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/xmltok.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/xmltok_impl.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/xmltok_impl.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/Source/lib/xmltok_ns.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/StaticLibs/Win32/libexpatwMT.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/StaticLibs/Xbox/libexpatwMT.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/expat-1.95.7/StaticLibs/Xbox/vc70.pdb create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/ANNOUNCE create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/CHANGES create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/INSTALL create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/KNOWNBUG create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/LICENSE create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/Makefile create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/README create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/TODO create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/Y2KINFO create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/example.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/lib/libpng.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/lib/libpng_dbg.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/libpng.dsp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/libpng.dsw create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/libpng.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/png.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/png.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngasmrd.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngbar.jpg create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngbar.png create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngconf.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngerror.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pnggccrd.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngget.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngmem.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngnow.png create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngpread.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngread.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngrio.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngrtran.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngrutil.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngset.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngtest.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngtest.dsp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngtest.plg create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngtest.png create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngtrans.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngvcrd.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngwio.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngwrite.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngwtran.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/pngwutil.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/SCOPTIONS.ppc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/descrip.mms create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/libpng.icc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.acorn create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.aix create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.amiga create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.atari create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.bc32 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.bd32 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.beos create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.bor create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.cygwin create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.darwin create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.dec create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.dj2 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.gcc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.gcmmx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.hpgcc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.hpux create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.ibmc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.intel create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.knr create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.linux create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.macosx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.mips create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.msc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.netbsd create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.os2 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.sco create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.sggcc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.sgi create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.so9 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.solaris create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.std create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.sunos create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.tc3 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makefile.watcom create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/makevms.com create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/pngdef.pas create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/pngos2.def create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/png/scripts/smakefile.ppc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/ChangeLog create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/FAQ create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/INDEX create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/Make_vms.com create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/Makefile create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/Makefile.in create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/Makefile.riscos create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/README create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/adler32.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/algorithm.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/amiga/Makefile.pup create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/amiga/Makefile.sas create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/compress.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/configure create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/README.contrib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/asm386/gvmat32.asm create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/asm386/gvmat32c.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/asm386/mkgvmt32.bat create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/asm386/zlibvc.def create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/asm386/zlibvc.dsp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/asm386/zlibvc.dsw create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/asm586/README.586 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/asm586/match.S create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/asm686/README.686 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/asm686/match.S create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/delphi/zlib.mak create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/delphi/zlibdef.pas create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/delphi2/d_zlib.bpr create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/delphi2/d_zlib.cpp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/delphi2/readme.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/delphi2/zlib.bpg create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/delphi2/zlib.bpr create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/delphi2/zlib.cpp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/delphi2/zlib.pas create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/delphi2/zlib32.bpr create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/delphi2/zlib32.cpp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/iostream/test.cpp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/iostream/zfstream.cpp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/iostream/zfstream.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/iostream2/zstream.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/iostream2/zstream_test.cpp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/ChangeLogUnzip create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/Makefile create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/miniunz.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/minizip.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/readme.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/unzip.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/unzip.def create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/unzip.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/zip.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/zip.def create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/zip.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/zlibvc.def create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/zlibvc.dsp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/minizip/zlibvc.dsw create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/untgz/Makefile create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/untgz/makefile.w32 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/untgz/untgz.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/contrib/visual-basic.txt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/crc32.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/deflate.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/deflate.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/descrip.mms create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/example.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/gzio.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/infblock.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/infblock.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/infcodes.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/infcodes.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/inffast.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/inffast.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/inffixed.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/inflate.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/inftrees.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/inftrees.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/infutil.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/infutil.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/maketree.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/minigzip.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/msdos/Makefile.b32 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/msdos/Makefile.bor create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/msdos/Makefile.dj2 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/msdos/Makefile.emx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/msdos/Makefile.msc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/msdos/Makefile.tc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/msdos/Makefile.w32 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/msdos/Makefile.wat create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/msdos/zlib.def create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/msdos/zlib.rc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/nt/Makefile.emx create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/nt/Makefile.gcc create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/nt/Makefile.nt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/nt/zlib.dnt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/os2/Makefile.os2 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/os2/zlib.def create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/trees.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/trees.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/uncompr.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/zconf.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/zlib.3 create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/zlib.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/zlib.html create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/zutil.c create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/SRC/zutil.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/ZLIB_VSS_MAKE/zlib.dsw create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/ZLIB_VSS_MAKE/zlib.opt create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/ZLIB_VSS_MAKE/zlib.sln create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/ZLIB_VSS_MAKE/zlib/zlib.dsp create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/ZLIB_VSS_MAKE/zlib/zlib.plg create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/ZLIB_VSS_MAKE/zlib/zlib.vcproj create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/inc/zconf.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/inc/zlib.h create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/lib/vc70.pdb create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/lib/zlib.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/lib/zlib_srv.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/lib/zlibd.lib create mode 100644 MOULOpenSourceClientPlugin/StaticSDKs/XPlatform/zlib/lib/zlibd_srv.lib diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..92bc3d8d --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,622 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + diff --git a/MOULOpenSourceClientPlugin/LICENSE.txt b/MOULOpenSourceClientPlugin/LICENSE.txt new file mode 100644 index 00000000..92bc3d8d --- /dev/null +++ b/MOULOpenSourceClientPlugin/LICENSE.txt @@ -0,0 +1,622 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Docs/Distributor.doc b/MOULOpenSourceClientPlugin/Plasma20/Docs/Distributor.doc new file mode 100644 index 0000000000000000000000000000000000000000..1e62a1eeff24620b36755b0e3ec83b109cb37adf GIT binary patch literal 400384 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

KvMQb?y(d;i?RH`j0l7I^jctCiYZHC_ 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 U{4)3C%m3*0QOf%JpHvF`Kd$z%_5c6? literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Docs/sound cones.htm b/MOULOpenSourceClientPlugin/Plasma20/Docs/sound cones.htm new file mode 100644 index 00000000..d73dfb59 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Docs/sound cones.htm @@ -0,0 +1,40 @@ + + + + + + + + +

+

Sound Cones

+

A sound with no orientation has the same amplitude at a given distance in all +directions. A sound with an orientation is loudest in the direction of +orientation. The model that describes the loudness of the oriented sound is +called a sound cone. Sound cones are made up of an inside (or inner) cone and an +outside (or outer) cone.

+

At any angle within the inner cone, the volume of the sound is just what it +would be if there were no cone, after taking into account the basic volume of +the buffer, the distance from the listener, the listener's orientation, and so +on.

+

At any angle outside the outer cone, the normal volume is attenuated by a +factor set by the application. The outside cone volume is expressed in +hundredths of decibels and is a negative value, because it represents +attenuation from the default volume of 0.

+

Between the inner and outer cones is a zone of transition from the inside +volume to the outside volume. The volume decreases as the angle increases.

+

The following illustration shows the concept of sound cones.

+

+

Every 3-D sound buffer has a sound cone, but by default a buffer behaves like +an omnidirectional sound source, because the outside volume is not attenuated, +and the inside and outside cone angles are 360 degrees. Unless the application +changes these values, the sound does not have any apparent orientation.

+

Designing sound cones properly can add dramatic effects to your application. +For example, you could position a sound source in the center of a room, setting +its orientation toward an open door in a hallway. Then set the angle of the +inside cone so that it extends to the width of the doorway, make the outside +cone a bit wider, and set the outside cone volume to inaudible. A listener +moving along the hallway will begin to hear the sound only when near the +doorway, and the sound will be loudest as the listener passes in front of the +open door.

diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/AllDlls/AllDlls.sln b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/AllDlls/AllDlls.sln new file mode 100644 index 00000000..7e6a1f70 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/AllDlls/AllDlls.sln @@ -0,0 +1,1444 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AllDlls", "AllDlls.vcproj", "{F1EC656C-E052-497A-A4CF-5DAC08E11EE9}" + ProjectSection(ProjectDependencies) = postProject + {9196762E-90C8-44D6-A645-B1D9928D245B} = {9196762E-90C8-44D6-A645-B1D9928D245B} + {93864FCA-227D-48A5-A182-CB983BB90FBF} = {93864FCA-227D-48A5-A182-CB983BB90FBF} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoreLib", "..\Plasma\CoreLib\CoreLib.vcproj", "{80F4D4A5-90EA-40F2-AF73-123AF27E8CD4}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaxComponent", "..\Tools\MaxComponent\MaxComponent.vcproj", "{F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0}" + ProjectSection(ProjectDependencies) = postProject + {A32201A8-0255-4863-97B8-4A569C18C624} = {A32201A8-0255-4863-97B8-4A569C18C624} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaxConvert", "..\Tools\MaxConvert\MaxConvert.vcproj", "{E98F4CAC-2120-43EC-87BC-742D04CF6FA8}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaxExport", "..\Tools\MaxExport\MaxExport.vcproj", "{0F2CE895-1AE7-44AC-8236-DDF08A0ED70C}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaxMain", "..\Tools\MaxMain\MaxMain.vcproj", "{9196762E-90C8-44D6-A645-B1D9928D245B}" + ProjectSection(ProjectDependencies) = postProject + {2F678900-C50B-477E-8CCC-3A91B99F9DDA} = {2F678900-C50B-477E-8CCC-3A91B99F9DDA} + {2C929A01-78B2-4E87-96BC-71736BA62917} = {2C929A01-78B2-4E87-96BC-71736BA62917} + {10C8C001-079B-42A7-BA7F-D98EB8285FBB} = {10C8C001-079B-42A7-BA7F-D98EB8285FBB} + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED} = {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED} + {854DAC02-D95F-4E90-AB8A-A8259BD6F677} = {854DAC02-D95F-4E90-AB8A-A8259BD6F677} + {C9ECCF02-DE6D-41DB-8F12-316D56F119D2} = {C9ECCF02-DE6D-41DB-8F12-316D56F119D2} + {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6} = {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6} + {08096103-4BF9-4A6B-8088-AA9F6C989575} = {08096103-4BF9-4A6B-8088-AA9F6C989575} + {DE62CF05-8E3E-40A4-967D-330216C2D2ED} = {DE62CF05-8E3E-40A4-967D-330216C2D2ED} + {F046340D-639F-4522-9FD5-5D88A3D957DB} = {F046340D-639F-4522-9FD5-5D88A3D957DB} + {2F891210-5851-4C21-A7F3-E37B9C007190} = {2F891210-5851-4C21-A7F3-E37B9C007190} + {50A7D510-800C-421A-A0D4-B3AF8FBC1060} = {50A7D510-800C-421A-A0D4-B3AF8FBC1060} + {4E31D613-1372-476A-A206-04319A401318} = {4E31D613-1372-476A-A206-04319A401318} + {4F0E6414-0FC0-4819-B2A9-90774C0FC340} = {4F0E6414-0FC0-4819-B2A9-90774C0FC340} + {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F} = {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F} + {B49FF11C-1231-40AE-8893-0A15AB89224C} = {B49FF11C-1231-40AE-8893-0A15AB89224C} + {5D5B8F1D-0201-4A68-807C-3B08C5435972} = {5D5B8F1D-0201-4A68-807C-3B08C5435972} + {92E2BE1F-98A0-41CA-AE63-D90774D15F06} = {92E2BE1F-98A0-41CA-AE63-D90774D15F06} + {D97E0E20-0502-4E63-BD05-41B42EED695F} = {D97E0E20-0502-4E63-BD05-41B42EED695F} + {ECF7D320-CB30-4628-92FD-E7672A72064F} = {ECF7D320-CB30-4628-92FD-E7672A72064F} + {3BF79823-CD88-4F09-8BCB-A13D39481322} = {3BF79823-CD88-4F09-8BCB-A13D39481322} + {4E991726-D69F-4786-A873-DF37FB4DABF9} = {4E991726-D69F-4786-A873-DF37FB4DABF9} + {30A84728-A436-40B9-A009-B117407145C2} = {30A84728-A436-40B9-A009-B117407145C2} + {58BE352F-CDB2-4A08-AC62-981800D018B0} = {58BE352F-CDB2-4A08-AC62-981800D018B0} + {907DBE31-5B7D-483D-AAF7-CABC24434EA7} = {907DBE31-5B7D-483D-AAF7-CABC24434EA7} + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA} = {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA} + {CF98D440-1A73-4CD5-8760-9548F7D27F41} = {CF98D440-1A73-4CD5-8760-9548F7D27F41} + {28E5E146-D581-4492-9DD7-D56247A9F027} = {28E5E146-D581-4492-9DD7-D56247A9F027} + {C3726247-83D8-4C6A-BB44-1D6ADC7CA137} = {C3726247-83D8-4C6A-BB44-1D6ADC7CA137} + {538AF749-DDD7-4DBB-B549-4613246FB4DB} = {538AF749-DDD7-4DBB-B549-4613246FB4DB} + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032} = {9CF43A4A-4830-4DF6-BB3C-693AE88C2032} + {32EC294E-2A45-4627-B5C4-B5CD236C2674} = {32EC294E-2A45-4627-B5C4-B5CD236C2674} + {4054C94F-866A-4AA7-874B-2AFCFEF23A71} = {4054C94F-866A-4AA7-874B-2AFCFEF23A71} + {3F37A050-1F2B-4249-B0D5-81DBCF574678} = {3F37A050-1F2B-4249-B0D5-81DBCF574678} + {94BEB453-9F25-40CF-89F8-A5BC9AE9465E} = {94BEB453-9F25-40CF-89F8-A5BC9AE9465E} + {89316659-F4A9-4E92-8200-C36288A61B9B} = {89316659-F4A9-4E92-8200-C36288A61B9B} + {579E045E-36ED-4EC1-90FD-3AF45674FD27} = {579E045E-36ED-4EC1-90FD-3AF45674FD27} + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6} = {65C4DE61-E599-4F0F-9B75-66C0DA887BB6} + {C664C563-FA33-4E48-BE77-7CCCD922B040} = {C664C563-FA33-4E48-BE77-7CCCD922B040} + {D6D56A64-A79B-4F48-BB9D-8C216D66632F} = {D6D56A64-A79B-4F48-BB9D-8C216D66632F} + {A927606C-7646-43C0-A3BB-B21836CC7D02} = {A927606C-7646-43C0-A3BB-B21836CC7D02} + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3} = {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3} + {CA208971-6C77-47F6-AA4B-FB6ECC071132} = {CA208971-6C77-47F6-AA4B-FB6ECC071132} + {7D83F872-F06C-4D42-A8F2-D77DC22C5981} = {7D83F872-F06C-4D42-A8F2-D77DC22C5981} + {4BAAB07A-B68D-44FA-8275-9478337CF80D} = {4BAAB07A-B68D-44FA-8275-9478337CF80D} + {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF} = {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF} + {C10FF87E-ED60-4183-81BB-B5C35489CE85} = {C10FF87E-ED60-4183-81BB-B5C35489CE85} + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C} = {D62C987F-3FB4-43D0-BA36-EB0D8517A42C} + {D8D19980-BCE6-47E5-9661-030ED27FE6C5} = {D8D19980-BCE6-47E5-9661-030ED27FE6C5} + {AE796882-E608-4312-B68C-91124215B4D7} = {AE796882-E608-4312-B68C-91124215B4D7} + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D} = {AB45BA83-4921-4A3C-80CF-1984D49ACA0D} + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F} = {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F} + {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231} = {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231} + {FCFFAE8F-D233-44D2-AAAD-4100FA274A32} = {FCFFAE8F-D233-44D2-AAAD-4100FA274A32} + {FA8FD791-F130-4D91-A10C-5ACA3694C5B8} = {FA8FD791-F130-4D91-A10C-5ACA3694C5B8} + {77309195-77FA-4C01-B5CD-FB4733C0D3F3} = {77309195-77FA-4C01-B5CD-FB4733C0D3F3} + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C} = {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C} + {0AE17996-56DF-4E88-8681-B115786D0DC9} = {0AE17996-56DF-4E88-8681-B115786D0DC9} + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8} = {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8} + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4} = {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4} + {CD2B90A8-9B9C-4866-B77D-DB343D528F88} = {CD2B90A8-9B9C-4866-B77D-DB343D528F88} + {E98F4CAC-2120-43EC-87BC-742D04CF6FA8} = {E98F4CAC-2120-43EC-87BC-742D04CF6FA8} + {430EC1AE-7E25-4039-A83E-09F956285BAC} = {430EC1AE-7E25-4039-A83E-09F956285BAC} + {87B244B0-EAC3-4109-A395-898D1CFF0EBE} = {87B244B0-EAC3-4109-A395-898D1CFF0EBE} + {E8FE47B0-DF75-4D9F-9096-0BC204239B52} = {E8FE47B0-DF75-4D9F-9096-0BC204239B52} + {7A5E02B6-896A-49C2-8016-139CE23B3654} = {7A5E02B6-896A-49C2-8016-139CE23B3654} + {69E240B8-0ED6-4397-9DA6-133CC14F9E25} = {69E240B8-0ED6-4397-9DA6-133CC14F9E25} + {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921} = {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921} + {20917EBD-2A7E-49F5-A638-F5864265FF8C} = {20917EBD-2A7E-49F5-A638-F5864265FF8C} + {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2} = {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2} + {2F78AEC2-02A4-47CE-B15E-7553FD9953B0} = {2F78AEC2-02A4-47CE-B15E-7553FD9953B0} + {55C8DDC2-0675-4977-A18F-166109D89F4B} = {55C8DDC2-0675-4977-A18F-166109D89F4B} + {DBE0CBC3-0EC0-4363-9B1D-239227B3C985} = {DBE0CBC3-0EC0-4363-9B1D-239227B3C985} + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1} = {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1} + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0} = {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0} + {5E6680CE-4294-41AE-957D-8C19656A1D4F} = {5E6680CE-4294-41AE-957D-8C19656A1D4F} + {1629BBD2-4116-4EAC-8D78-5D9D1521C99A} = {1629BBD2-4116-4EAC-8D78-5D9D1521C99A} + {CF869ED5-0924-4C6C-95BF-455F3EE48844} = {CF869ED5-0924-4C6C-95BF-455F3EE48844} + {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91} = {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91} + {50A8E2E3-B2DA-4A40-9AF0-233E725C2694} = {50A8E2E3-B2DA-4A40-9AF0-233E725C2694} + {6A79EBE3-87CD-4814-A57C-E24A61332979} = {6A79EBE3-87CD-4814-A57C-E24A61332979} + {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2} = {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2} + {949195EB-0E59-4357-9FD6-D761E64C0494} = {949195EB-0E59-4357-9FD6-D761E64C0494} + {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C} = {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C} + {5DD406F4-68A7-4164-ACB8-5CE8134746F1} = {5DD406F4-68A7-4164-ACB8-5CE8134746F1} + {7EFB10F8-B984-467C-BD50-B117DD633840} = {7EFB10F8-B984-467C-BD50-B117DD633840} + {5F6B7CF9-F4BD-4956-A064-5726350D9C90} = {5F6B7CF9-F4BD-4956-A064-5726350D9C90} + {BCAE65FD-0FB1-4E03-B827-8339B601F575} = {BCAE65FD-0FB1-4E03-B827-8339B601F575} + {55D67FFD-FEBB-44C2-B010-6C03146660D0} = {55D67FFD-FEBB-44C2-B010-6C03146660D0} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaxPlasmaLights", "..\Tools\MaxPlasmaLights\MaxPlasmaLights.vcproj", "{93864FCA-227D-48A5-A182-CB983BB90FBF}" + ProjectSection(ProjectDependencies) = postProject + {08096103-4BF9-4A6B-8088-AA9F6C989575} = {08096103-4BF9-4A6B-8088-AA9F6C989575} + {ECF7D320-CB30-4628-92FD-E7672A72064F} = {ECF7D320-CB30-4628-92FD-E7672A72064F} + {CF98D440-1A73-4CD5-8760-9548F7D27F41} = {CF98D440-1A73-4CD5-8760-9548F7D27F41} + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032} = {9CF43A4A-4830-4DF6-BB3C-693AE88C2032} + {4054C94F-866A-4AA7-874B-2AFCFEF23A71} = {4054C94F-866A-4AA7-874B-2AFCFEF23A71} + {89316659-F4A9-4E92-8200-C36288A61B9B} = {89316659-F4A9-4E92-8200-C36288A61B9B} + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6} = {65C4DE61-E599-4F0F-9B75-66C0DA887BB6} + {A927606C-7646-43C0-A3BB-B21836CC7D02} = {A927606C-7646-43C0-A3BB-B21836CC7D02} + {CA208971-6C77-47F6-AA4B-FB6ECC071132} = {CA208971-6C77-47F6-AA4B-FB6ECC071132} + {C10FF87E-ED60-4183-81BB-B5C35489CE85} = {C10FF87E-ED60-4183-81BB-B5C35489CE85} + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C} = {D62C987F-3FB4-43D0-BA36-EB0D8517A42C} + {77309195-77FA-4C01-B5CD-FB4733C0D3F3} = {77309195-77FA-4C01-B5CD-FB4733C0D3F3} + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C} = {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C} + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4} = {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4} + {E8FE47B0-DF75-4D9F-9096-0BC204239B52} = {E8FE47B0-DF75-4D9F-9096-0BC204239B52} + {20917EBD-2A7E-49F5-A638-F5864265FF8C} = {20917EBD-2A7E-49F5-A638-F5864265FF8C} + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1} = {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1} + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0} = {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0} + {5DD406F4-68A7-4164-ACB8-5CE8134746F1} = {5DD406F4-68A7-4164-ACB8-5CE8134746F1} + {7EFB10F8-B984-467C-BD50-B117DD633840} = {7EFB10F8-B984-467C-BD50-B117DD633840} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaxPlasmaMtls", "..\Tools\MaxPlasmaMtls\MaxPlasmaMtls.vcproj", "{9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MaxSceneViewer", "..\Tools\MaxSceneViewer\MaxSceneViewer.vcproj", "{F046340D-639F-4522-9FD5-5D88A3D957DB}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PubUtilInc", "..\Plasma\PubUtilLib\PubUtilInc\PubUtilInc.vcproj", "{92E2BE1F-98A0-41CA-AE63-D90774D15F06}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfAnimation", "..\Plasma\FeatureLib\pfAnimation\pfAnimation.vcproj", "{C9ECCF02-DE6D-41DB-8F12-316D56F119D2}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfAudio", "..\Plasma\FeatureLib\pfAudio\pfAudio.vcproj", "{AE796882-E608-4312-B68C-91124215B4D7}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfCCR", "..\Plasma\FeatureLib\pfCCR\pfCCR.vcproj", "{B49FF11C-1231-40AE-8893-0A15AB89224C}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfCamera", "..\Plasma\FeatureLib\pfCamera\pfCamera.vcproj", "{907DBE31-5B7D-483D-AAF7-CABC24434EA7}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfCharacter", "..\Plasma\FeatureLib\pfCharacter\pfCharacter.vcproj", "{0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfConditional", "..\Plasma\FeatureLib\pfConditional\pfConditional.vcproj", "{5D5B8F1D-0201-4A68-807C-3B08C5435972}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfConsole", "..\Plasma\FeatureLib\pfConsole\pfConsole.vcproj", "{F7908945-B39A-4F34-B886-D5AFD862CD64}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfGameGUIMgr", "..\Plasma\FeatureLib\pfGameGUIMgr\pfGameGUIMgr.vcproj", "{CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfJournalBook", "..\Plasma\FeatureLib\pfJournalBook\pfJournalBook.vcproj", "{5E6680CE-4294-41AE-957D-8C19656A1D4F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfMessage", "..\Plasma\FeatureLib\pfMessage\pfMessage.vcproj", "{50A8E2E3-B2DA-4A40-9AF0-233E725C2694}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfPython", "..\Plasma\FeatureLib\pfPython\pfPython.vcproj", "{2F78AEC2-02A4-47CE-B15E-7553FD9953B0}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfSurface", "..\Plasma\FeatureLib\pfSurface\pfSurface.vcproj", "{4BAAB07A-B68D-44FA-8275-9478337CF80D}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAgeDescription", "..\Plasma\PubUtilLib\plAgeDescription\plAgeDescription.vcproj", "{E0C3E7E6-9794-4AFE-875D-C7CD45497DA2}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAgeLoader", "..\Plasma\PubUtilLib\plAgeLoader\plAgeLoader.vcproj", "{F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAudible", "..\Plasma\PubUtilLib\plAudible\plAudible.vcproj", "{CD2B90A8-9B9C-4866-B77D-DB343D528F88}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAudio", "..\Plasma\PubUtilLib\plAudio\plAudio.vcproj", "{2C929A01-78B2-4E87-96BC-71736BA62917}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAudioCore", "..\Plasma\PubUtilLib\plAudioCore\plAudioCore.vcproj", "{1629BBD2-4116-4EAC-8D78-5D9D1521C99A}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAvatar", "..\Plasma\PubUtilLib\plAvatar\plAvatar.vcproj", "{538AF749-DDD7-4DBB-B549-4613246FB4DB}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plCompression", "..\Plasma\PubUtilLib\plCompression\plCompression.vcproj", "{30A84728-A436-40B9-A009-B117407145C2}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plContainer", "..\Plasma\PubUtilLib\plContainer\plContainer.vcproj", "{C3726247-83D8-4C6A-BB44-1D6ADC7CA137}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plDrawable", "..\Plasma\PubUtilLib\plDrawable\plDrawable.vcproj", "{854DAC02-D95F-4E90-AB8A-A8259BD6F677}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plEncryption", "..\Plasma\PubUtilLib\plEncryption\plEncryption.vcproj", "{C664C563-FA33-4E48-BE77-7CCCD922B040}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plFile", "..\Plasma\PubUtilLib\plFile\plFile.vcproj", "{5DD406F4-68A7-4164-ACB8-5CE8134746F1}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plGImage", "..\Plasma\PubUtilLib\plGImage\plGImage.vcproj", "{2F678900-C50B-477E-8CCC-3A91B99F9DDA}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plGLight", "..\Plasma\PubUtilLib\plGLight\plGLight.vcproj", "{E865EEE1-E3F8-4723-8B49-ACF97FAD7D91}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plInputCore", "..\Plasma\PubUtilLib\plInputCore\plInputCore.vcproj", "{4E31D613-1372-476A-A206-04319A401318}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plInterp", "..\Plasma\PubUtilLib\plInterp\plInterp.vcproj", "{77E0B38B-04E2-42AD-89A5-B4CE5EDCA231}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plIntersect", "..\Plasma\PubUtilLib\plIntersect\plIntersect.vcproj", "{0AE17996-56DF-4E88-8681-B115786D0DC9}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plJPEG", "..\Plasma\PubUtilLib\plJPEG\plJPEG.vcproj", "{69E240B8-0ED6-4397-9DA6-133CC14F9E25}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plMath", "..\Plasma\PubUtilLib\plMath\plMath.vcproj", "{FCFFAE8F-D233-44D2-AAAD-4100FA274A32}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plMessage", "..\Plasma\PubUtilLib\plMessage\plMessage.vcproj", "{10C8C001-079B-42A7-BA7F-D98EB8285FBB}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plModifier", "..\Plasma\PubUtilLib\plModifier\plModifier.vcproj", "{FA8FD791-F130-4D91-A10C-5ACA3694C5B8}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetClient", "..\Plasma\PubUtilLib\plNetClient\PlNetClient.vcproj", "{BCAE65FD-0FB1-4E03-B827-8339B601F575}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetClientComm", "..\Plasma\PubUtilLib\plNetClientComm\plNetClientComm.vcproj", "{9CAD787D-D89C-4BFE-AF0A-0189A5C581EF}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetClientRecorder", "..\Plasma\PubUtilLib\plNetClientRecorder\plNetClientRecorder.vcproj", "{1C1F0318-4BAD-4E78-A52C-9BFC60369A2F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetCommon", "..\Plasma\PubUtilLib\plNetCommon\plNetCommon.vcproj", "{3BF79823-CD88-4F09-8BCB-A13D39481322}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetMessage", "..\Plasma\PubUtilLib\plNetMessage\plNetMessage.vcproj", "{55D67FFD-FEBB-44C2-B010-6C03146660D0}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetTransport", "..\Plasma\PubUtilLib\plNetTransport\plNetTransport.vcproj", "{CF869ED5-0924-4C6C-95BF-455F3EE48844}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plParticleSystem", "..\Plasma\PubUtilLib\plParticleSystem\plParticleSystem.vcproj", "{7A5E02B6-896A-49C2-8016-139CE23B3654}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plPhysical", "..\Plasma\PubUtilLib\plPhysical\plPhysical.vcproj", "{87B244B0-EAC3-4109-A395-898D1CFF0EBE}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plPipeline", "..\Plasma\PubUtilLib\plPipeline\plPipeline.vcproj", "{58BE352F-CDB2-4A08-AC62-981800D018B0}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plProgressMgr", "..\Plasma\PubUtilLib\plProgressMgr\plProgressMgr.vcproj", "{6A79EBE3-87CD-4814-A57C-E24A61332979}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plResMgr", "..\Plasma\PubUtilLib\plResMgr\plResMgr.vcproj", "{77309195-77FA-4C01-B5CD-FB4733C0D3F3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plSDL", "..\Plasma\PubUtilLib\plSDL\plSDL.vcproj", "{2F891210-5851-4C21-A7F3-E37B9C007190}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plScene", "..\Plasma\PubUtilLib\plScene\plScene.vcproj", "{5F6B7CF9-F4BD-4956-A064-5726350D9C90}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plSockets", "..\Plasma\PubUtilLib\plSockets\plSockets.vcproj", "{D6D56A64-A79B-4F48-BB9D-8C216D66632F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plStatGather", "..\Plasma\PubUtilLib\plStatGather\plStatGather.vcproj", "{CF98D440-1A73-4CD5-8760-9548F7D27F41}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plStatusLog", "..\Plasma\PubUtilLib\plStatusLog\plStatusLog.vcproj", "{D62C987F-3FB4-43D0-BA36-EB0D8517A42C}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plStreamLogger", "..\Plasma\PubUtilLib\plStreamLogger\plStreamLogger.vcproj", "{949195EB-0E59-4357-9FD6-D761E64C0494}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plSurface", "..\Plasma\PubUtilLib\plSurface\plSurface.vcproj", "{94BEB453-9F25-40CF-89F8-A5BC9AE9465E}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plTransform", "..\Plasma\PubUtilLib\plTransform\plTransform.vcproj", "{3F37A050-1F2B-4249-B0D5-81DBCF574678}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plUUID", "..\Plasma\PubUtilLib\plUUID\plUUID.vcproj", "{579E045E-36ED-4EC1-90FD-3AF45674FD27}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plUnifiedTime", "..\Plasma\PubUtilLib\plUnifiedTime\plUnifiedTime.vcproj", "{ECF7D320-CB30-4628-92FD-E7672A72064F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plVault", "..\Plasma\PubUtilLib\plVault\plVault.vcproj", "{DBE0CBC3-0EC0-4363-9B1D-239227B3C985}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnAddrInfo", "..\Plasma\NucleusLib\pnAddrInfo\pnAddrInfo.vcproj", "{9CF43A4A-4830-4DF6-BB3C-693AE88C2032}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnDispatch", "..\Plasma\NucleusLib\pnDispatch\pnDispatch.vcproj", "{08096103-4BF9-4A6B-8088-AA9F6C989575}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnFactory", "..\Plasma\NucleusLib\pnFactory\pnFactory.vcproj", "{20917EBD-2A7E-49F5-A638-F5864265FF8C}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnInputCore", "..\Plasma\NucleusLib\pnInputCore\pnInputCore.vcproj", "{4F0E6414-0FC0-4819-B2A9-90774C0FC340}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnKeyedObject", "..\Plasma\NucleusLib\pnKeyedObject\pnKeyedObject.vcproj", "{7EFB10F8-B984-467C-BD50-B117DD633840}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnMessage", "..\Plasma\NucleusLib\pnMessage\pnMessage.vcproj", "{A927606C-7646-43C0-A3BB-B21836CC7D02}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnModifier", "..\Plasma\NucleusLib\pnModifier\pnModifier.vcproj", "{50A7D510-800C-421A-A0D4-B3AF8FBC1060}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnNetCommon", "..\Plasma\NucleusLib\pnNetCommon\pnNetCommon.vcproj", "{C10FF87E-ED60-4183-81BB-B5C35489CE85}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnNucleusInc", "..\Plasma\NucleusLib\pnNucleusInc\pnNucleusInc.vcproj", "{E8FE47B0-DF75-4D9F-9096-0BC204239B52}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnSceneObject", "..\Plasma\NucleusLib\pnSceneObject\pnSceneObject.vcproj", "{D8D19980-BCE6-47E5-9661-030ED27FE6C5}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnTimer", "..\Plasma\NucleusLib\pnTimer\pnTimer.vcproj", "{65C4DE61-E599-4F0F-9B75-66C0DA887BB6}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pythoncore_static", "..\..\SDKs\XPlatform\Cypython-2.3.3\PCbuild\pythoncore_static.vcproj", "{A32201A8-0255-4863-97B8-4A569C18C624}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoreLibExe", "..\Plasma\CoreLibExe\CoreLibExe.vcproj", "{4054C94F-866A-4AA7-874B-2AFCFEF23A71}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnAsyncCore", "..\Plasma\NucleusLib\pnAsyncCore\pnAsyncCore.vcproj", "{4E991726-D69F-4786-A873-DF37FB4DABF9}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnAsyncCoreExe", "..\Plasma\NucleusLib\pnAsyncCoreExe\pnAsyncCoreExe.vcproj", "{55C8DDC2-0675-4977-A18F-166109D89F4B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnProduct", "..\Plasma\NucleusLib\pnProduct\pnProduct.vcproj", "{CA208971-6C77-47F6-AA4B-FB6ECC071132}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnNetBase", "..\Plasma\NucleusLib\pnNetBase\pnNetBase.vcproj", "{AB45BA83-4921-4A3C-80CF-1984D49ACA0D}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnIni", "..\Plasma\NucleusLib\pnIni\pnIni.vcproj", "{430EC1AE-7E25-4039-A83E-09F956285BAC}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnIniExe", "..\Plasma\NucleusLib\pnIniExe\pnIniExe.vcproj", "{28E5E146-D581-4492-9DD7-D56247A9F027}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnNetCli", "..\Plasma\NucleusLib\pnNetCli\pnNetCli.vcproj", "{6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnUtils", "..\Plasma\NucleusLib\pnUtils\pnUtils.vcproj", "{89316659-F4A9-4E92-8200-C36288A61B9B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnUtilsExe", "..\Plasma\NucleusLib\pnUtilsExe\pnUtilsExe.vcproj", "{08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetGameLib", "..\Plasma\PubUtilLib\plNetGameLib\plNetGameLib.vcproj", "{CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnNetProtocol", "..\Plasma\NucleusLib\pnNetProtocol\pnNetProtocol.vcproj", "{32EC294E-2A45-4627-B5C4-B5CD236C2674}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfLocalizationMgr", "..\Plasma\FeatureLib\pfLocalizationMgr\pfLocalizationMgr.vcproj", "{8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plPhysX", "..\Plasma\PubUtilLib\plPhysX\plPhysX.vcproj", "{5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnGameMgr", "..\Plasma\NucleusLib\pnGameMgr\pnGameMgr.vcproj", "{D97E0E20-0502-4E63-BD05-41B42EED695F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfGameMgr", "..\Plasma\FeatureLib\pfGameMgr\pfGameMgr.vcproj", "{7D83F872-F06C-4D42-A8F2-D77DC22C5981}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfGameScoreMgr", "..\Plasma\FeatureLib\pfGameScoreMgr\pfGameScoreMgr.vcproj", "{2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plCyDebug", "..\Plasma\Apps\CyPythonIDE\plCyDebug\plCyDebug.vcproj", "{DE62CF05-8E3E-40A4-967D-330216C2D2ED}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Debug_Server = Debug_Server + Ethereal Build = Ethereal Build + Release = Release + Release_Server = Release_Server + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {F1EC656C-E052-497A-A4CF-5DAC08E11EE9}.Debug.ActiveCfg = Debug|Win32 + {F1EC656C-E052-497A-A4CF-5DAC08E11EE9}.Debug.Build.0 = Debug|Win32 + {F1EC656C-E052-497A-A4CF-5DAC08E11EE9}.Debug_Server.ActiveCfg = Debug|Win32 + {F1EC656C-E052-497A-A4CF-5DAC08E11EE9}.Debug_Server.Build.0 = Debug|Win32 + {F1EC656C-E052-497A-A4CF-5DAC08E11EE9}.Ethereal Build.ActiveCfg = Release|Win32 + {F1EC656C-E052-497A-A4CF-5DAC08E11EE9}.Ethereal Build.Build.0 = Release|Win32 + {F1EC656C-E052-497A-A4CF-5DAC08E11EE9}.Release.ActiveCfg = Release|Win32 + {F1EC656C-E052-497A-A4CF-5DAC08E11EE9}.Release.Build.0 = Release|Win32 + {F1EC656C-E052-497A-A4CF-5DAC08E11EE9}.Release_Server.ActiveCfg = Release|Win32 + {F1EC656C-E052-497A-A4CF-5DAC08E11EE9}.Release_Server.Build.0 = Release|Win32 + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4}.Debug.ActiveCfg = Debug|Win32 + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4}.Debug.Build.0 = Debug|Win32 + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4}.Debug_Server.Build.0 = Debug_Server|Win32 + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4}.Release.ActiveCfg = Release|Win32 + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4}.Release.Build.0 = Release|Win32 + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4}.Release_Server.ActiveCfg = Release_Server|Win32 + {80F4D4A5-90EA-40F2-AF73-123AF27E8CD4}.Release_Server.Build.0 = Release_Server|Win32 + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0}.Debug.ActiveCfg = Debug|Win32 + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0}.Debug.Build.0 = Debug|Win32 + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0}.Debug_Server.ActiveCfg = Debug|Win32 + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0}.Debug_Server.Build.0 = Debug|Win32 + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0}.Ethereal Build.ActiveCfg = Release|Win32 + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0}.Ethereal Build.Build.0 = Release|Win32 + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0}.Release.ActiveCfg = Release|Win32 + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0}.Release.Build.0 = Release|Win32 + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0}.Release_Server.ActiveCfg = Release|Win32 + {F2BBC9CB-90DF-4F58-9F3E-3F4150F647B0}.Release_Server.Build.0 = Release|Win32 + {E98F4CAC-2120-43EC-87BC-742D04CF6FA8}.Debug.ActiveCfg = Debug|Win32 + {E98F4CAC-2120-43EC-87BC-742D04CF6FA8}.Debug.Build.0 = Debug|Win32 + {E98F4CAC-2120-43EC-87BC-742D04CF6FA8}.Debug_Server.ActiveCfg = Debug|Win32 + {E98F4CAC-2120-43EC-87BC-742D04CF6FA8}.Debug_Server.Build.0 = Debug|Win32 + {E98F4CAC-2120-43EC-87BC-742D04CF6FA8}.Ethereal Build.ActiveCfg = Release|Win32 + {E98F4CAC-2120-43EC-87BC-742D04CF6FA8}.Ethereal Build.Build.0 = Release|Win32 + {E98F4CAC-2120-43EC-87BC-742D04CF6FA8}.Release.ActiveCfg = Release|Win32 + {E98F4CAC-2120-43EC-87BC-742D04CF6FA8}.Release.Build.0 = Release|Win32 + {E98F4CAC-2120-43EC-87BC-742D04CF6FA8}.Release_Server.ActiveCfg = Release|Win32 + {E98F4CAC-2120-43EC-87BC-742D04CF6FA8}.Release_Server.Build.0 = Release|Win32 + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C}.Debug.ActiveCfg = Debug|Win32 + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C}.Debug.Build.0 = Debug|Win32 + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C}.Debug_Server.ActiveCfg = Debug|Win32 + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C}.Debug_Server.Build.0 = Debug|Win32 + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C}.Ethereal Build.ActiveCfg = Release|Win32 + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C}.Ethereal Build.Build.0 = Release|Win32 + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C}.Release.ActiveCfg = Release|Win32 + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C}.Release.Build.0 = Release|Win32 + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C}.Release_Server.ActiveCfg = Release|Win32 + {0F2CE895-1AE7-44AC-8236-DDF08A0ED70C}.Release_Server.Build.0 = Release|Win32 + {9196762E-90C8-44D6-A645-B1D9928D245B}.Debug.ActiveCfg = Debug|Win32 + {9196762E-90C8-44D6-A645-B1D9928D245B}.Debug.Build.0 = Debug|Win32 + {9196762E-90C8-44D6-A645-B1D9928D245B}.Debug_Server.ActiveCfg = Debug|Win32 + {9196762E-90C8-44D6-A645-B1D9928D245B}.Debug_Server.Build.0 = Debug|Win32 + {9196762E-90C8-44D6-A645-B1D9928D245B}.Ethereal Build.ActiveCfg = Debug|Win32 + {9196762E-90C8-44D6-A645-B1D9928D245B}.Ethereal Build.Build.0 = Debug|Win32 + {9196762E-90C8-44D6-A645-B1D9928D245B}.Release.ActiveCfg = Release|Win32 + {9196762E-90C8-44D6-A645-B1D9928D245B}.Release.Build.0 = Release|Win32 + {9196762E-90C8-44D6-A645-B1D9928D245B}.Release_Server.ActiveCfg = Release|Win32 + {9196762E-90C8-44D6-A645-B1D9928D245B}.Release_Server.Build.0 = Release|Win32 + {93864FCA-227D-48A5-A182-CB983BB90FBF}.Debug.ActiveCfg = Debug|Win32 + {93864FCA-227D-48A5-A182-CB983BB90FBF}.Debug.Build.0 = Debug|Win32 + {93864FCA-227D-48A5-A182-CB983BB90FBF}.Debug_Server.ActiveCfg = Debug|Win32 + {93864FCA-227D-48A5-A182-CB983BB90FBF}.Debug_Server.Build.0 = Debug|Win32 + {93864FCA-227D-48A5-A182-CB983BB90FBF}.Ethereal Build.ActiveCfg = Debug|Win32 + {93864FCA-227D-48A5-A182-CB983BB90FBF}.Ethereal Build.Build.0 = Debug|Win32 + {93864FCA-227D-48A5-A182-CB983BB90FBF}.Release.ActiveCfg = Release|Win32 + {93864FCA-227D-48A5-A182-CB983BB90FBF}.Release.Build.0 = Release|Win32 + {93864FCA-227D-48A5-A182-CB983BB90FBF}.Release_Server.ActiveCfg = Release|Win32 + {93864FCA-227D-48A5-A182-CB983BB90FBF}.Release_Server.Build.0 = Release|Win32 + {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6}.Debug.ActiveCfg = Debug|Win32 + {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6}.Debug.Build.0 = Debug|Win32 + {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6}.Debug_Server.ActiveCfg = Debug|Win32 + {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6}.Debug_Server.Build.0 = Debug|Win32 + {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6}.Ethereal Build.ActiveCfg = Debug|Win32 + {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6}.Ethereal Build.Build.0 = Debug|Win32 + {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6}.Release.ActiveCfg = Release|Win32 + {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6}.Release.Build.0 = Release|Win32 + {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6}.Release_Server.ActiveCfg = Release|Win32 + {9DC7F702-9C2C-4DF8-99B8-E19FE121BCF6}.Release_Server.Build.0 = Release|Win32 + {F046340D-639F-4522-9FD5-5D88A3D957DB}.Debug.ActiveCfg = Debug|Win32 + {F046340D-639F-4522-9FD5-5D88A3D957DB}.Debug.Build.0 = Debug|Win32 + {F046340D-639F-4522-9FD5-5D88A3D957DB}.Debug_Server.ActiveCfg = Debug|Win32 + {F046340D-639F-4522-9FD5-5D88A3D957DB}.Debug_Server.Build.0 = Debug|Win32 + {F046340D-639F-4522-9FD5-5D88A3D957DB}.Ethereal Build.ActiveCfg = Debug|Win32 + {F046340D-639F-4522-9FD5-5D88A3D957DB}.Ethereal Build.Build.0 = Debug|Win32 + {F046340D-639F-4522-9FD5-5D88A3D957DB}.Release.ActiveCfg = Release|Win32 + {F046340D-639F-4522-9FD5-5D88A3D957DB}.Release.Build.0 = Release|Win32 + {F046340D-639F-4522-9FD5-5D88A3D957DB}.Release_Server.ActiveCfg = Release|Win32 + {F046340D-639F-4522-9FD5-5D88A3D957DB}.Release_Server.Build.0 = Release|Win32 + {92E2BE1F-98A0-41CA-AE63-D90774D15F06}.Debug.ActiveCfg = Debug|Win32 + {92E2BE1F-98A0-41CA-AE63-D90774D15F06}.Debug.Build.0 = Debug|Win32 + {92E2BE1F-98A0-41CA-AE63-D90774D15F06}.Debug_Server.ActiveCfg = Debug|Win32 + {92E2BE1F-98A0-41CA-AE63-D90774D15F06}.Debug_Server.Build.0 = Debug|Win32 + {92E2BE1F-98A0-41CA-AE63-D90774D15F06}.Ethereal Build.ActiveCfg = Debug|Win32 + {92E2BE1F-98A0-41CA-AE63-D90774D15F06}.Ethereal Build.Build.0 = Debug|Win32 + {92E2BE1F-98A0-41CA-AE63-D90774D15F06}.Release.ActiveCfg = Release|Win32 + {92E2BE1F-98A0-41CA-AE63-D90774D15F06}.Release.Build.0 = Release|Win32 + {92E2BE1F-98A0-41CA-AE63-D90774D15F06}.Release_Server.ActiveCfg = Release|Win32 + {92E2BE1F-98A0-41CA-AE63-D90774D15F06}.Release_Server.Build.0 = Release|Win32 + {C9ECCF02-DE6D-41DB-8F12-316D56F119D2}.Debug.ActiveCfg = Debug|Win32 + {C9ECCF02-DE6D-41DB-8F12-316D56F119D2}.Debug.Build.0 = Debug|Win32 + {C9ECCF02-DE6D-41DB-8F12-316D56F119D2}.Debug_Server.ActiveCfg = Debug|Win32 + {C9ECCF02-DE6D-41DB-8F12-316D56F119D2}.Debug_Server.Build.0 = Debug|Win32 + {C9ECCF02-DE6D-41DB-8F12-316D56F119D2}.Ethereal Build.ActiveCfg = Debug|Win32 + {C9ECCF02-DE6D-41DB-8F12-316D56F119D2}.Ethereal Build.Build.0 = Debug|Win32 + {C9ECCF02-DE6D-41DB-8F12-316D56F119D2}.Release.ActiveCfg = Release|Win32 + {C9ECCF02-DE6D-41DB-8F12-316D56F119D2}.Release.Build.0 = Release|Win32 + {C9ECCF02-DE6D-41DB-8F12-316D56F119D2}.Release_Server.ActiveCfg = Release|Win32 + {C9ECCF02-DE6D-41DB-8F12-316D56F119D2}.Release_Server.Build.0 = Release|Win32 + {AE796882-E608-4312-B68C-91124215B4D7}.Debug.ActiveCfg = Debug|Win32 + {AE796882-E608-4312-B68C-91124215B4D7}.Debug.Build.0 = Debug|Win32 + {AE796882-E608-4312-B68C-91124215B4D7}.Debug_Server.ActiveCfg = Debug|Win32 + {AE796882-E608-4312-B68C-91124215B4D7}.Debug_Server.Build.0 = Debug|Win32 + {AE796882-E608-4312-B68C-91124215B4D7}.Ethereal Build.ActiveCfg = Debug|Win32 + {AE796882-E608-4312-B68C-91124215B4D7}.Ethereal Build.Build.0 = Debug|Win32 + {AE796882-E608-4312-B68C-91124215B4D7}.Release.ActiveCfg = Release|Win32 + {AE796882-E608-4312-B68C-91124215B4D7}.Release.Build.0 = Release|Win32 + {AE796882-E608-4312-B68C-91124215B4D7}.Release_Server.ActiveCfg = Release|Win32 + {AE796882-E608-4312-B68C-91124215B4D7}.Release_Server.Build.0 = Release|Win32 + {B49FF11C-1231-40AE-8893-0A15AB89224C}.Debug.ActiveCfg = Debug|Win32 + {B49FF11C-1231-40AE-8893-0A15AB89224C}.Debug.Build.0 = Debug|Win32 + {B49FF11C-1231-40AE-8893-0A15AB89224C}.Debug_Server.ActiveCfg = Debug|Win32 + {B49FF11C-1231-40AE-8893-0A15AB89224C}.Debug_Server.Build.0 = Debug|Win32 + {B49FF11C-1231-40AE-8893-0A15AB89224C}.Ethereal Build.ActiveCfg = Release|Win32 + {B49FF11C-1231-40AE-8893-0A15AB89224C}.Ethereal Build.Build.0 = Release|Win32 + {B49FF11C-1231-40AE-8893-0A15AB89224C}.Release.ActiveCfg = Release|Win32 + {B49FF11C-1231-40AE-8893-0A15AB89224C}.Release.Build.0 = Release|Win32 + {B49FF11C-1231-40AE-8893-0A15AB89224C}.Release_Server.ActiveCfg = Release|Win32 + {B49FF11C-1231-40AE-8893-0A15AB89224C}.Release_Server.Build.0 = Release|Win32 + {907DBE31-5B7D-483D-AAF7-CABC24434EA7}.Debug.ActiveCfg = Debug|Win32 + {907DBE31-5B7D-483D-AAF7-CABC24434EA7}.Debug.Build.0 = Debug|Win32 + {907DBE31-5B7D-483D-AAF7-CABC24434EA7}.Debug_Server.ActiveCfg = Debug|Win32 + {907DBE31-5B7D-483D-AAF7-CABC24434EA7}.Debug_Server.Build.0 = Debug|Win32 + {907DBE31-5B7D-483D-AAF7-CABC24434EA7}.Ethereal Build.ActiveCfg = Release|Win32 + {907DBE31-5B7D-483D-AAF7-CABC24434EA7}.Ethereal Build.Build.0 = Release|Win32 + {907DBE31-5B7D-483D-AAF7-CABC24434EA7}.Release.ActiveCfg = Release|Win32 + {907DBE31-5B7D-483D-AAF7-CABC24434EA7}.Release.Build.0 = Release|Win32 + {907DBE31-5B7D-483D-AAF7-CABC24434EA7}.Release_Server.ActiveCfg = Release|Win32 + {907DBE31-5B7D-483D-AAF7-CABC24434EA7}.Release_Server.Build.0 = Release|Win32 + {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C}.Debug.ActiveCfg = Debug|Win32 + {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C}.Debug.Build.0 = Debug|Win32 + {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C}.Debug_Server.ActiveCfg = Debug|Win32 + {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C}.Debug_Server.Build.0 = Debug|Win32 + {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C}.Ethereal Build.ActiveCfg = Release|Win32 + {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C}.Ethereal Build.Build.0 = Release|Win32 + {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C}.Release.ActiveCfg = Release|Win32 + {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C}.Release.Build.0 = Release|Win32 + {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C}.Release_Server.ActiveCfg = Release|Win32 + {0DF56BEC-6E8C-4927-BEE4-0D270CEEEB6C}.Release_Server.Build.0 = Release|Win32 + {5D5B8F1D-0201-4A68-807C-3B08C5435972}.Debug.ActiveCfg = Debug|Win32 + {5D5B8F1D-0201-4A68-807C-3B08C5435972}.Debug.Build.0 = Debug|Win32 + {5D5B8F1D-0201-4A68-807C-3B08C5435972}.Debug_Server.ActiveCfg = Debug|Win32 + {5D5B8F1D-0201-4A68-807C-3B08C5435972}.Debug_Server.Build.0 = Debug|Win32 + {5D5B8F1D-0201-4A68-807C-3B08C5435972}.Ethereal Build.ActiveCfg = Debug|Win32 + {5D5B8F1D-0201-4A68-807C-3B08C5435972}.Ethereal Build.Build.0 = Debug|Win32 + {5D5B8F1D-0201-4A68-807C-3B08C5435972}.Release.ActiveCfg = Release|Win32 + {5D5B8F1D-0201-4A68-807C-3B08C5435972}.Release.Build.0 = Release|Win32 + {5D5B8F1D-0201-4A68-807C-3B08C5435972}.Release_Server.ActiveCfg = Release|Win32 + {5D5B8F1D-0201-4A68-807C-3B08C5435972}.Release_Server.Build.0 = Release|Win32 + {F7908945-B39A-4F34-B886-D5AFD862CD64}.Debug.ActiveCfg = Debug|Win32 + {F7908945-B39A-4F34-B886-D5AFD862CD64}.Debug.Build.0 = Debug|Win32 + {F7908945-B39A-4F34-B886-D5AFD862CD64}.Debug_Server.ActiveCfg = Debug|Win32 + {F7908945-B39A-4F34-B886-D5AFD862CD64}.Debug_Server.Build.0 = Debug|Win32 + {F7908945-B39A-4F34-B886-D5AFD862CD64}.Ethereal Build.ActiveCfg = Debug|Win32 + {F7908945-B39A-4F34-B886-D5AFD862CD64}.Ethereal Build.Build.0 = Debug|Win32 + {F7908945-B39A-4F34-B886-D5AFD862CD64}.Release.ActiveCfg = Release|Win32 + {F7908945-B39A-4F34-B886-D5AFD862CD64}.Release.Build.0 = Release|Win32 + {F7908945-B39A-4F34-B886-D5AFD862CD64}.Release_Server.ActiveCfg = Release|Win32 + {F7908945-B39A-4F34-B886-D5AFD862CD64}.Release_Server.Build.0 = Release|Win32 + {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2}.Debug.ActiveCfg = Debug|Win32 + {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2}.Debug.Build.0 = Debug|Win32 + {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2}.Debug_Server.ActiveCfg = Debug|Win32 + {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2}.Debug_Server.Build.0 = Debug|Win32 + {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2}.Ethereal Build.ActiveCfg = Release|Win32 + {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2}.Ethereal Build.Build.0 = Release|Win32 + {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2}.Release.ActiveCfg = Release|Win32 + {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2}.Release.Build.0 = Release|Win32 + {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2}.Release_Server.ActiveCfg = Release|Win32 + {CFAA9EBF-A502-41F6-9784-0FC9CE7BFBA2}.Release_Server.Build.0 = Release|Win32 + {5E6680CE-4294-41AE-957D-8C19656A1D4F}.Debug.ActiveCfg = Debug|Win32 + {5E6680CE-4294-41AE-957D-8C19656A1D4F}.Debug.Build.0 = Debug|Win32 + {5E6680CE-4294-41AE-957D-8C19656A1D4F}.Debug_Server.ActiveCfg = Debug|Win32 + {5E6680CE-4294-41AE-957D-8C19656A1D4F}.Debug_Server.Build.0 = Debug|Win32 + {5E6680CE-4294-41AE-957D-8C19656A1D4F}.Ethereal Build.ActiveCfg = Release|Win32 + {5E6680CE-4294-41AE-957D-8C19656A1D4F}.Ethereal Build.Build.0 = Release|Win32 + {5E6680CE-4294-41AE-957D-8C19656A1D4F}.Release.ActiveCfg = Release|Win32 + {5E6680CE-4294-41AE-957D-8C19656A1D4F}.Release.Build.0 = Release|Win32 + {5E6680CE-4294-41AE-957D-8C19656A1D4F}.Release_Server.ActiveCfg = Release|Win32 + {5E6680CE-4294-41AE-957D-8C19656A1D4F}.Release_Server.Build.0 = Release|Win32 + {50A8E2E3-B2DA-4A40-9AF0-233E725C2694}.Debug.ActiveCfg = Debug|Win32 + {50A8E2E3-B2DA-4A40-9AF0-233E725C2694}.Debug.Build.0 = Debug|Win32 + {50A8E2E3-B2DA-4A40-9AF0-233E725C2694}.Debug_Server.ActiveCfg = Debug|Win32 + {50A8E2E3-B2DA-4A40-9AF0-233E725C2694}.Debug_Server.Build.0 = Debug|Win32 + {50A8E2E3-B2DA-4A40-9AF0-233E725C2694}.Ethereal Build.ActiveCfg = Debug|Win32 + {50A8E2E3-B2DA-4A40-9AF0-233E725C2694}.Ethereal Build.Build.0 = Debug|Win32 + {50A8E2E3-B2DA-4A40-9AF0-233E725C2694}.Release.ActiveCfg = Release|Win32 + {50A8E2E3-B2DA-4A40-9AF0-233E725C2694}.Release.Build.0 = Release|Win32 + {50A8E2E3-B2DA-4A40-9AF0-233E725C2694}.Release_Server.ActiveCfg = Release|Win32 + {50A8E2E3-B2DA-4A40-9AF0-233E725C2694}.Release_Server.Build.0 = Release|Win32 + {2F78AEC2-02A4-47CE-B15E-7553FD9953B0}.Debug.ActiveCfg = Debug|Win32 + {2F78AEC2-02A4-47CE-B15E-7553FD9953B0}.Debug.Build.0 = Debug|Win32 + {2F78AEC2-02A4-47CE-B15E-7553FD9953B0}.Debug_Server.ActiveCfg = Debug|Win32 + {2F78AEC2-02A4-47CE-B15E-7553FD9953B0}.Debug_Server.Build.0 = Debug|Win32 + {2F78AEC2-02A4-47CE-B15E-7553FD9953B0}.Ethereal Build.ActiveCfg = Debug|Win32 + {2F78AEC2-02A4-47CE-B15E-7553FD9953B0}.Ethereal Build.Build.0 = Debug|Win32 + {2F78AEC2-02A4-47CE-B15E-7553FD9953B0}.Release.ActiveCfg = Release|Win32 + {2F78AEC2-02A4-47CE-B15E-7553FD9953B0}.Release.Build.0 = Release|Win32 + {2F78AEC2-02A4-47CE-B15E-7553FD9953B0}.Release_Server.ActiveCfg = Release|Win32 + {2F78AEC2-02A4-47CE-B15E-7553FD9953B0}.Release_Server.Build.0 = Release|Win32 + {4BAAB07A-B68D-44FA-8275-9478337CF80D}.Debug.ActiveCfg = Debug|Win32 + {4BAAB07A-B68D-44FA-8275-9478337CF80D}.Debug.Build.0 = Debug|Win32 + {4BAAB07A-B68D-44FA-8275-9478337CF80D}.Debug_Server.ActiveCfg = Debug|Win32 + {4BAAB07A-B68D-44FA-8275-9478337CF80D}.Debug_Server.Build.0 = Debug|Win32 + {4BAAB07A-B68D-44FA-8275-9478337CF80D}.Ethereal Build.ActiveCfg = Release|Win32 + {4BAAB07A-B68D-44FA-8275-9478337CF80D}.Ethereal Build.Build.0 = Release|Win32 + {4BAAB07A-B68D-44FA-8275-9478337CF80D}.Release.ActiveCfg = Release|Win32 + {4BAAB07A-B68D-44FA-8275-9478337CF80D}.Release.Build.0 = Release|Win32 + {4BAAB07A-B68D-44FA-8275-9478337CF80D}.Release_Server.ActiveCfg = Release|Win32 + {4BAAB07A-B68D-44FA-8275-9478337CF80D}.Release_Server.Build.0 = Release|Win32 + {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2}.Debug.ActiveCfg = Debug|Win32 + {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2}.Debug.Build.0 = Debug|Win32 + {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2}.Debug_Server.ActiveCfg = Debug|Win32 + {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2}.Debug_Server.Build.0 = Debug|Win32 + {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2}.Ethereal Build.ActiveCfg = Release|Win32 + {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2}.Ethereal Build.Build.0 = Release|Win32 + {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2}.Release.ActiveCfg = Release|Win32 + {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2}.Release.Build.0 = Release|Win32 + {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2}.Release_Server.ActiveCfg = Release|Win32 + {E0C3E7E6-9794-4AFE-875D-C7CD45497DA2}.Release_Server.Build.0 = Release|Win32 + {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921}.Debug.ActiveCfg = Debug|Win32 + {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921}.Debug.Build.0 = Debug|Win32 + {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921}.Debug_Server.ActiveCfg = Debug|Win32 + {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921}.Debug_Server.Build.0 = Debug|Win32 + {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921}.Ethereal Build.ActiveCfg = Debug|Win32 + {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921}.Ethereal Build.Build.0 = Debug|Win32 + {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921}.Release.ActiveCfg = Release|Win32 + {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921}.Release.Build.0 = Release|Win32 + {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921}.Release_Server.ActiveCfg = Release|Win32 + {F57EFCBB-9B7F-46D2-946B-D5DCA4D6C921}.Release_Server.Build.0 = Release|Win32 + {CD2B90A8-9B9C-4866-B77D-DB343D528F88}.Debug.ActiveCfg = Debug|Win32 + {CD2B90A8-9B9C-4866-B77D-DB343D528F88}.Debug.Build.0 = Debug|Win32 + {CD2B90A8-9B9C-4866-B77D-DB343D528F88}.Debug_Server.ActiveCfg = Debug|Win32 + {CD2B90A8-9B9C-4866-B77D-DB343D528F88}.Debug_Server.Build.0 = Debug|Win32 + {CD2B90A8-9B9C-4866-B77D-DB343D528F88}.Ethereal Build.ActiveCfg = Release|Win32 + {CD2B90A8-9B9C-4866-B77D-DB343D528F88}.Ethereal Build.Build.0 = Release|Win32 + {CD2B90A8-9B9C-4866-B77D-DB343D528F88}.Release.ActiveCfg = Release|Win32 + {CD2B90A8-9B9C-4866-B77D-DB343D528F88}.Release.Build.0 = Release|Win32 + {CD2B90A8-9B9C-4866-B77D-DB343D528F88}.Release_Server.ActiveCfg = Release|Win32 + {CD2B90A8-9B9C-4866-B77D-DB343D528F88}.Release_Server.Build.0 = Release|Win32 + {2C929A01-78B2-4E87-96BC-71736BA62917}.Debug.ActiveCfg = Debug|Win32 + {2C929A01-78B2-4E87-96BC-71736BA62917}.Debug.Build.0 = Debug|Win32 + {2C929A01-78B2-4E87-96BC-71736BA62917}.Debug_Server.ActiveCfg = Debug|Win32 + {2C929A01-78B2-4E87-96BC-71736BA62917}.Debug_Server.Build.0 = Debug|Win32 + {2C929A01-78B2-4E87-96BC-71736BA62917}.Ethereal Build.ActiveCfg = Debug|Win32 + {2C929A01-78B2-4E87-96BC-71736BA62917}.Ethereal Build.Build.0 = Debug|Win32 + {2C929A01-78B2-4E87-96BC-71736BA62917}.Release.ActiveCfg = Release|Win32 + {2C929A01-78B2-4E87-96BC-71736BA62917}.Release.Build.0 = Release|Win32 + {2C929A01-78B2-4E87-96BC-71736BA62917}.Release_Server.ActiveCfg = Release|Win32 + {2C929A01-78B2-4E87-96BC-71736BA62917}.Release_Server.Build.0 = Release|Win32 + {1629BBD2-4116-4EAC-8D78-5D9D1521C99A}.Debug.ActiveCfg = Debug|Win32 + {1629BBD2-4116-4EAC-8D78-5D9D1521C99A}.Debug.Build.0 = Debug|Win32 + {1629BBD2-4116-4EAC-8D78-5D9D1521C99A}.Debug_Server.ActiveCfg = Debug|Win32 + {1629BBD2-4116-4EAC-8D78-5D9D1521C99A}.Debug_Server.Build.0 = Debug|Win32 + {1629BBD2-4116-4EAC-8D78-5D9D1521C99A}.Ethereal Build.ActiveCfg = Release|Win32 + {1629BBD2-4116-4EAC-8D78-5D9D1521C99A}.Ethereal Build.Build.0 = Release|Win32 + {1629BBD2-4116-4EAC-8D78-5D9D1521C99A}.Release.ActiveCfg = Release|Win32 + {1629BBD2-4116-4EAC-8D78-5D9D1521C99A}.Release.Build.0 = Release|Win32 + {1629BBD2-4116-4EAC-8D78-5D9D1521C99A}.Release_Server.ActiveCfg = Release|Win32 + {1629BBD2-4116-4EAC-8D78-5D9D1521C99A}.Release_Server.Build.0 = Release|Win32 + {538AF749-DDD7-4DBB-B549-4613246FB4DB}.Debug.ActiveCfg = Debug|Win32 + {538AF749-DDD7-4DBB-B549-4613246FB4DB}.Debug.Build.0 = Debug|Win32 + {538AF749-DDD7-4DBB-B549-4613246FB4DB}.Debug_Server.ActiveCfg = Debug|Win32 + {538AF749-DDD7-4DBB-B549-4613246FB4DB}.Debug_Server.Build.0 = Debug|Win32 + {538AF749-DDD7-4DBB-B549-4613246FB4DB}.Ethereal Build.ActiveCfg = Release|Win32 + {538AF749-DDD7-4DBB-B549-4613246FB4DB}.Ethereal Build.Build.0 = Release|Win32 + {538AF749-DDD7-4DBB-B549-4613246FB4DB}.Release.ActiveCfg = Release|Win32 + {538AF749-DDD7-4DBB-B549-4613246FB4DB}.Release.Build.0 = Release|Win32 + {538AF749-DDD7-4DBB-B549-4613246FB4DB}.Release_Server.ActiveCfg = Release|Win32 + {538AF749-DDD7-4DBB-B549-4613246FB4DB}.Release_Server.Build.0 = Release|Win32 + {30A84728-A436-40B9-A009-B117407145C2}.Debug.ActiveCfg = Debug|Win32 + {30A84728-A436-40B9-A009-B117407145C2}.Debug.Build.0 = Debug|Win32 + {30A84728-A436-40B9-A009-B117407145C2}.Debug_Server.ActiveCfg = Debug|Win32 + {30A84728-A436-40B9-A009-B117407145C2}.Debug_Server.Build.0 = Debug|Win32 + {30A84728-A436-40B9-A009-B117407145C2}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {30A84728-A436-40B9-A009-B117407145C2}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {30A84728-A436-40B9-A009-B117407145C2}.Release.ActiveCfg = Release|Win32 + {30A84728-A436-40B9-A009-B117407145C2}.Release.Build.0 = Release|Win32 + {30A84728-A436-40B9-A009-B117407145C2}.Release_Server.ActiveCfg = Release|Win32 + {30A84728-A436-40B9-A009-B117407145C2}.Release_Server.Build.0 = Release|Win32 + {C3726247-83D8-4C6A-BB44-1D6ADC7CA137}.Debug.ActiveCfg = Debug|Win32 + {C3726247-83D8-4C6A-BB44-1D6ADC7CA137}.Debug.Build.0 = Debug|Win32 + {C3726247-83D8-4C6A-BB44-1D6ADC7CA137}.Debug_Server.ActiveCfg = Debug|Win32 + {C3726247-83D8-4C6A-BB44-1D6ADC7CA137}.Debug_Server.Build.0 = Debug|Win32 + {C3726247-83D8-4C6A-BB44-1D6ADC7CA137}.Ethereal Build.ActiveCfg = Debug|Win32 + {C3726247-83D8-4C6A-BB44-1D6ADC7CA137}.Ethereal Build.Build.0 = Debug|Win32 + {C3726247-83D8-4C6A-BB44-1D6ADC7CA137}.Release.ActiveCfg = Release|Win32 + {C3726247-83D8-4C6A-BB44-1D6ADC7CA137}.Release.Build.0 = Release|Win32 + {C3726247-83D8-4C6A-BB44-1D6ADC7CA137}.Release_Server.ActiveCfg = Release|Win32 + {C3726247-83D8-4C6A-BB44-1D6ADC7CA137}.Release_Server.Build.0 = Release|Win32 + {854DAC02-D95F-4E90-AB8A-A8259BD6F677}.Debug.ActiveCfg = Debug|Win32 + {854DAC02-D95F-4E90-AB8A-A8259BD6F677}.Debug.Build.0 = Debug|Win32 + {854DAC02-D95F-4E90-AB8A-A8259BD6F677}.Debug_Server.ActiveCfg = Debug|Win32 + {854DAC02-D95F-4E90-AB8A-A8259BD6F677}.Debug_Server.Build.0 = Debug|Win32 + {854DAC02-D95F-4E90-AB8A-A8259BD6F677}.Ethereal Build.ActiveCfg = Debug|Win32 + {854DAC02-D95F-4E90-AB8A-A8259BD6F677}.Ethereal Build.Build.0 = Debug|Win32 + {854DAC02-D95F-4E90-AB8A-A8259BD6F677}.Release.ActiveCfg = Release|Win32 + {854DAC02-D95F-4E90-AB8A-A8259BD6F677}.Release.Build.0 = Release|Win32 + {854DAC02-D95F-4E90-AB8A-A8259BD6F677}.Release_Server.ActiveCfg = Release|Win32 + {854DAC02-D95F-4E90-AB8A-A8259BD6F677}.Release_Server.Build.0 = Release|Win32 + {C664C563-FA33-4E48-BE77-7CCCD922B040}.Debug.ActiveCfg = Debug|Win32 + {C664C563-FA33-4E48-BE77-7CCCD922B040}.Debug.Build.0 = Debug|Win32 + {C664C563-FA33-4E48-BE77-7CCCD922B040}.Debug_Server.ActiveCfg = Debug|Win32 + {C664C563-FA33-4E48-BE77-7CCCD922B040}.Debug_Server.Build.0 = Debug|Win32 + {C664C563-FA33-4E48-BE77-7CCCD922B040}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {C664C563-FA33-4E48-BE77-7CCCD922B040}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {C664C563-FA33-4E48-BE77-7CCCD922B040}.Release.ActiveCfg = Release|Win32 + {C664C563-FA33-4E48-BE77-7CCCD922B040}.Release.Build.0 = Release|Win32 + {C664C563-FA33-4E48-BE77-7CCCD922B040}.Release_Server.ActiveCfg = Release|Win32 + {C664C563-FA33-4E48-BE77-7CCCD922B040}.Release_Server.Build.0 = Release|Win32 + {5DD406F4-68A7-4164-ACB8-5CE8134746F1}.Debug.ActiveCfg = Debug|Win32 + {5DD406F4-68A7-4164-ACB8-5CE8134746F1}.Debug.Build.0 = Debug|Win32 + {5DD406F4-68A7-4164-ACB8-5CE8134746F1}.Debug_Server.ActiveCfg = Debug|Win32 + {5DD406F4-68A7-4164-ACB8-5CE8134746F1}.Debug_Server.Build.0 = Debug|Win32 + {5DD406F4-68A7-4164-ACB8-5CE8134746F1}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {5DD406F4-68A7-4164-ACB8-5CE8134746F1}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {5DD406F4-68A7-4164-ACB8-5CE8134746F1}.Release.ActiveCfg = Release|Win32 + {5DD406F4-68A7-4164-ACB8-5CE8134746F1}.Release.Build.0 = Release|Win32 + {5DD406F4-68A7-4164-ACB8-5CE8134746F1}.Release_Server.ActiveCfg = Release|Win32 + {5DD406F4-68A7-4164-ACB8-5CE8134746F1}.Release_Server.Build.0 = Release|Win32 + {2F678900-C50B-477E-8CCC-3A91B99F9DDA}.Debug.ActiveCfg = Debug|Win32 + {2F678900-C50B-477E-8CCC-3A91B99F9DDA}.Debug.Build.0 = Debug|Win32 + {2F678900-C50B-477E-8CCC-3A91B99F9DDA}.Debug_Server.ActiveCfg = Debug|Win32 + {2F678900-C50B-477E-8CCC-3A91B99F9DDA}.Debug_Server.Build.0 = Debug|Win32 + {2F678900-C50B-477E-8CCC-3A91B99F9DDA}.Ethereal Build.ActiveCfg = Debug|Win32 + {2F678900-C50B-477E-8CCC-3A91B99F9DDA}.Ethereal Build.Build.0 = Debug|Win32 + {2F678900-C50B-477E-8CCC-3A91B99F9DDA}.Release.ActiveCfg = Release|Win32 + {2F678900-C50B-477E-8CCC-3A91B99F9DDA}.Release.Build.0 = Release|Win32 + {2F678900-C50B-477E-8CCC-3A91B99F9DDA}.Release_Server.ActiveCfg = Release|Win32 + {2F678900-C50B-477E-8CCC-3A91B99F9DDA}.Release_Server.Build.0 = Release|Win32 + {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91}.Debug.ActiveCfg = Debug|Win32 + {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91}.Debug.Build.0 = Debug|Win32 + {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91}.Debug_Server.ActiveCfg = Debug|Win32 + {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91}.Debug_Server.Build.0 = Debug|Win32 + {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91}.Ethereal Build.ActiveCfg = Debug|Win32 + {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91}.Ethereal Build.Build.0 = Debug|Win32 + {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91}.Release.ActiveCfg = Release|Win32 + {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91}.Release.Build.0 = Release|Win32 + {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91}.Release_Server.ActiveCfg = Release|Win32 + {E865EEE1-E3F8-4723-8B49-ACF97FAD7D91}.Release_Server.Build.0 = Release|Win32 + {4E31D613-1372-476A-A206-04319A401318}.Debug.ActiveCfg = Debug|Win32 + {4E31D613-1372-476A-A206-04319A401318}.Debug.Build.0 = Debug|Win32 + {4E31D613-1372-476A-A206-04319A401318}.Debug_Server.ActiveCfg = Debug|Win32 + {4E31D613-1372-476A-A206-04319A401318}.Debug_Server.Build.0 = Debug|Win32 + {4E31D613-1372-476A-A206-04319A401318}.Ethereal Build.ActiveCfg = Release|Win32 + {4E31D613-1372-476A-A206-04319A401318}.Ethereal Build.Build.0 = Release|Win32 + {4E31D613-1372-476A-A206-04319A401318}.Release.ActiveCfg = Release|Win32 + {4E31D613-1372-476A-A206-04319A401318}.Release.Build.0 = Release|Win32 + {4E31D613-1372-476A-A206-04319A401318}.Release_Server.ActiveCfg = Release|Win32 + {4E31D613-1372-476A-A206-04319A401318}.Release_Server.Build.0 = Release|Win32 + {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231}.Debug.ActiveCfg = Debug|Win32 + {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231}.Debug.Build.0 = Debug|Win32 + {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231}.Debug_Server.ActiveCfg = Debug|Win32 + {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231}.Debug_Server.Build.0 = Debug|Win32 + {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231}.Ethereal Build.ActiveCfg = Debug|Win32 + {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231}.Ethereal Build.Build.0 = Debug|Win32 + {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231}.Release.ActiveCfg = Release|Win32 + {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231}.Release.Build.0 = Release|Win32 + {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231}.Release_Server.ActiveCfg = Release|Win32 + {77E0B38B-04E2-42AD-89A5-B4CE5EDCA231}.Release_Server.Build.0 = Release|Win32 + {0AE17996-56DF-4E88-8681-B115786D0DC9}.Debug.ActiveCfg = Debug|Win32 + {0AE17996-56DF-4E88-8681-B115786D0DC9}.Debug.Build.0 = Debug|Win32 + {0AE17996-56DF-4E88-8681-B115786D0DC9}.Debug_Server.ActiveCfg = Debug|Win32 + {0AE17996-56DF-4E88-8681-B115786D0DC9}.Debug_Server.Build.0 = Debug|Win32 + {0AE17996-56DF-4E88-8681-B115786D0DC9}.Ethereal Build.ActiveCfg = Release|Win32 + {0AE17996-56DF-4E88-8681-B115786D0DC9}.Ethereal Build.Build.0 = Release|Win32 + {0AE17996-56DF-4E88-8681-B115786D0DC9}.Release.ActiveCfg = Release|Win32 + {0AE17996-56DF-4E88-8681-B115786D0DC9}.Release.Build.0 = Release|Win32 + {0AE17996-56DF-4E88-8681-B115786D0DC9}.Release_Server.ActiveCfg = Release|Win32 + {0AE17996-56DF-4E88-8681-B115786D0DC9}.Release_Server.Build.0 = Release|Win32 + {69E240B8-0ED6-4397-9DA6-133CC14F9E25}.Debug.ActiveCfg = Debug|Win32 + {69E240B8-0ED6-4397-9DA6-133CC14F9E25}.Debug.Build.0 = Debug|Win32 + {69E240B8-0ED6-4397-9DA6-133CC14F9E25}.Debug_Server.ActiveCfg = Debug|Win32 + {69E240B8-0ED6-4397-9DA6-133CC14F9E25}.Debug_Server.Build.0 = Debug|Win32 + {69E240B8-0ED6-4397-9DA6-133CC14F9E25}.Ethereal Build.ActiveCfg = Release|Win32 + {69E240B8-0ED6-4397-9DA6-133CC14F9E25}.Ethereal Build.Build.0 = Release|Win32 + {69E240B8-0ED6-4397-9DA6-133CC14F9E25}.Release.ActiveCfg = Release|Win32 + {69E240B8-0ED6-4397-9DA6-133CC14F9E25}.Release.Build.0 = Release|Win32 + {69E240B8-0ED6-4397-9DA6-133CC14F9E25}.Release_Server.ActiveCfg = Release|Win32 + {69E240B8-0ED6-4397-9DA6-133CC14F9E25}.Release_Server.Build.0 = Release|Win32 + {FCFFAE8F-D233-44D2-AAAD-4100FA274A32}.Debug.ActiveCfg = Debug|Win32 + {FCFFAE8F-D233-44D2-AAAD-4100FA274A32}.Debug.Build.0 = Debug|Win32 + {FCFFAE8F-D233-44D2-AAAD-4100FA274A32}.Debug_Server.ActiveCfg = Debug|Win32 + {FCFFAE8F-D233-44D2-AAAD-4100FA274A32}.Debug_Server.Build.0 = Debug|Win32 + {FCFFAE8F-D233-44D2-AAAD-4100FA274A32}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {FCFFAE8F-D233-44D2-AAAD-4100FA274A32}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {FCFFAE8F-D233-44D2-AAAD-4100FA274A32}.Release.ActiveCfg = Release|Win32 + {FCFFAE8F-D233-44D2-AAAD-4100FA274A32}.Release.Build.0 = Release|Win32 + {FCFFAE8F-D233-44D2-AAAD-4100FA274A32}.Release_Server.ActiveCfg = Release|Win32 + {FCFFAE8F-D233-44D2-AAAD-4100FA274A32}.Release_Server.Build.0 = Release|Win32 + {10C8C001-079B-42A7-BA7F-D98EB8285FBB}.Debug.ActiveCfg = Debug|Win32 + {10C8C001-079B-42A7-BA7F-D98EB8285FBB}.Debug.Build.0 = Debug|Win32 + {10C8C001-079B-42A7-BA7F-D98EB8285FBB}.Debug_Server.ActiveCfg = Debug|Win32 + {10C8C001-079B-42A7-BA7F-D98EB8285FBB}.Debug_Server.Build.0 = Debug|Win32 + {10C8C001-079B-42A7-BA7F-D98EB8285FBB}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {10C8C001-079B-42A7-BA7F-D98EB8285FBB}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {10C8C001-079B-42A7-BA7F-D98EB8285FBB}.Release.ActiveCfg = Release|Win32 + {10C8C001-079B-42A7-BA7F-D98EB8285FBB}.Release.Build.0 = Release|Win32 + {10C8C001-079B-42A7-BA7F-D98EB8285FBB}.Release_Server.ActiveCfg = Release|Win32 + {10C8C001-079B-42A7-BA7F-D98EB8285FBB}.Release_Server.Build.0 = Release|Win32 + {FA8FD791-F130-4D91-A10C-5ACA3694C5B8}.Debug.ActiveCfg = Debug|Win32 + {FA8FD791-F130-4D91-A10C-5ACA3694C5B8}.Debug.Build.0 = Debug|Win32 + {FA8FD791-F130-4D91-A10C-5ACA3694C5B8}.Debug_Server.ActiveCfg = Debug|Win32 + {FA8FD791-F130-4D91-A10C-5ACA3694C5B8}.Debug_Server.Build.0 = Debug|Win32 + {FA8FD791-F130-4D91-A10C-5ACA3694C5B8}.Ethereal Build.ActiveCfg = Release|Win32 + {FA8FD791-F130-4D91-A10C-5ACA3694C5B8}.Ethereal Build.Build.0 = Release|Win32 + {FA8FD791-F130-4D91-A10C-5ACA3694C5B8}.Release.ActiveCfg = Release|Win32 + {FA8FD791-F130-4D91-A10C-5ACA3694C5B8}.Release.Build.0 = Release|Win32 + {FA8FD791-F130-4D91-A10C-5ACA3694C5B8}.Release_Server.ActiveCfg = Release|Win32 + {FA8FD791-F130-4D91-A10C-5ACA3694C5B8}.Release_Server.Build.0 = Release|Win32 + {BCAE65FD-0FB1-4E03-B827-8339B601F575}.Debug.ActiveCfg = Debug|Win32 + {BCAE65FD-0FB1-4E03-B827-8339B601F575}.Debug.Build.0 = Debug|Win32 + {BCAE65FD-0FB1-4E03-B827-8339B601F575}.Debug_Server.ActiveCfg = Debug|Win32 + {BCAE65FD-0FB1-4E03-B827-8339B601F575}.Debug_Server.Build.0 = Debug|Win32 + {BCAE65FD-0FB1-4E03-B827-8339B601F575}.Ethereal Build.ActiveCfg = Release|Win32 + {BCAE65FD-0FB1-4E03-B827-8339B601F575}.Ethereal Build.Build.0 = Release|Win32 + {BCAE65FD-0FB1-4E03-B827-8339B601F575}.Release.ActiveCfg = Release|Win32 + {BCAE65FD-0FB1-4E03-B827-8339B601F575}.Release.Build.0 = Release|Win32 + {BCAE65FD-0FB1-4E03-B827-8339B601F575}.Release_Server.ActiveCfg = Release|Win32 + {BCAE65FD-0FB1-4E03-B827-8339B601F575}.Release_Server.Build.0 = Release|Win32 + {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF}.Debug.ActiveCfg = Debug|Win32 + {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF}.Debug.Build.0 = Debug|Win32 + {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF}.Debug_Server.ActiveCfg = Debug|Win32 + {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF}.Debug_Server.Build.0 = Debug|Win32 + {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF}.Ethereal Build.ActiveCfg = Release|Win32 + {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF}.Ethereal Build.Build.0 = Release|Win32 + {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF}.Release.ActiveCfg = Release|Win32 + {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF}.Release.Build.0 = Release|Win32 + {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF}.Release_Server.ActiveCfg = Release|Win32 + {9CAD787D-D89C-4BFE-AF0A-0189A5C581EF}.Release_Server.Build.0 = Release|Win32 + {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F}.Debug.ActiveCfg = Debug|Win32 + {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F}.Debug.Build.0 = Debug|Win32 + {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F}.Debug_Server.ActiveCfg = Debug|Win32 + {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F}.Debug_Server.Build.0 = Debug|Win32 + {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F}.Ethereal Build.ActiveCfg = Debug|Win32 + {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F}.Ethereal Build.Build.0 = Debug|Win32 + {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F}.Release.ActiveCfg = Release|Win32 + {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F}.Release.Build.0 = Release|Win32 + {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F}.Release_Server.ActiveCfg = Release|Win32 + {1C1F0318-4BAD-4E78-A52C-9BFC60369A2F}.Release_Server.Build.0 = Release|Win32 + {3BF79823-CD88-4F09-8BCB-A13D39481322}.Debug.ActiveCfg = Debug|Win32 + {3BF79823-CD88-4F09-8BCB-A13D39481322}.Debug.Build.0 = Debug|Win32 + {3BF79823-CD88-4F09-8BCB-A13D39481322}.Debug_Server.ActiveCfg = Debug|Win32 + {3BF79823-CD88-4F09-8BCB-A13D39481322}.Debug_Server.Build.0 = Debug|Win32 + {3BF79823-CD88-4F09-8BCB-A13D39481322}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {3BF79823-CD88-4F09-8BCB-A13D39481322}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {3BF79823-CD88-4F09-8BCB-A13D39481322}.Release.ActiveCfg = Release|Win32 + {3BF79823-CD88-4F09-8BCB-A13D39481322}.Release.Build.0 = Release|Win32 + {3BF79823-CD88-4F09-8BCB-A13D39481322}.Release_Server.ActiveCfg = Release|Win32 + {3BF79823-CD88-4F09-8BCB-A13D39481322}.Release_Server.Build.0 = Release|Win32 + {55D67FFD-FEBB-44C2-B010-6C03146660D0}.Debug.ActiveCfg = Debug|Win32 + {55D67FFD-FEBB-44C2-B010-6C03146660D0}.Debug.Build.0 = Debug|Win32 + {55D67FFD-FEBB-44C2-B010-6C03146660D0}.Debug_Server.ActiveCfg = Debug|Win32 + {55D67FFD-FEBB-44C2-B010-6C03146660D0}.Debug_Server.Build.0 = Debug|Win32 + {55D67FFD-FEBB-44C2-B010-6C03146660D0}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {55D67FFD-FEBB-44C2-B010-6C03146660D0}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {55D67FFD-FEBB-44C2-B010-6C03146660D0}.Release.ActiveCfg = Release|Win32 + {55D67FFD-FEBB-44C2-B010-6C03146660D0}.Release.Build.0 = Release|Win32 + {55D67FFD-FEBB-44C2-B010-6C03146660D0}.Release_Server.ActiveCfg = Release|Win32 + {55D67FFD-FEBB-44C2-B010-6C03146660D0}.Release_Server.Build.0 = Release|Win32 + {CF869ED5-0924-4C6C-95BF-455F3EE48844}.Debug.ActiveCfg = Debug|Win32 + {CF869ED5-0924-4C6C-95BF-455F3EE48844}.Debug.Build.0 = Debug|Win32 + {CF869ED5-0924-4C6C-95BF-455F3EE48844}.Debug_Server.ActiveCfg = Debug|Win32 + {CF869ED5-0924-4C6C-95BF-455F3EE48844}.Debug_Server.Build.0 = Debug|Win32 + {CF869ED5-0924-4C6C-95BF-455F3EE48844}.Ethereal Build.ActiveCfg = Release|Win32 + {CF869ED5-0924-4C6C-95BF-455F3EE48844}.Ethereal Build.Build.0 = Release|Win32 + {CF869ED5-0924-4C6C-95BF-455F3EE48844}.Release.ActiveCfg = Release|Win32 + {CF869ED5-0924-4C6C-95BF-455F3EE48844}.Release.Build.0 = Release|Win32 + {CF869ED5-0924-4C6C-95BF-455F3EE48844}.Release_Server.ActiveCfg = Release|Win32 + {CF869ED5-0924-4C6C-95BF-455F3EE48844}.Release_Server.Build.0 = Release|Win32 + {7A5E02B6-896A-49C2-8016-139CE23B3654}.Debug.ActiveCfg = Debug|Win32 + {7A5E02B6-896A-49C2-8016-139CE23B3654}.Debug.Build.0 = Debug|Win32 + {7A5E02B6-896A-49C2-8016-139CE23B3654}.Debug_Server.ActiveCfg = Debug|Win32 + {7A5E02B6-896A-49C2-8016-139CE23B3654}.Debug_Server.Build.0 = Debug|Win32 + {7A5E02B6-896A-49C2-8016-139CE23B3654}.Ethereal Build.ActiveCfg = Debug|Win32 + {7A5E02B6-896A-49C2-8016-139CE23B3654}.Ethereal Build.Build.0 = Debug|Win32 + {7A5E02B6-896A-49C2-8016-139CE23B3654}.Release.ActiveCfg = Release|Win32 + {7A5E02B6-896A-49C2-8016-139CE23B3654}.Release.Build.0 = Release|Win32 + {7A5E02B6-896A-49C2-8016-139CE23B3654}.Release_Server.ActiveCfg = Release|Win32 + {7A5E02B6-896A-49C2-8016-139CE23B3654}.Release_Server.Build.0 = Release|Win32 + {87B244B0-EAC3-4109-A395-898D1CFF0EBE}.Debug.ActiveCfg = Debug|Win32 + {87B244B0-EAC3-4109-A395-898D1CFF0EBE}.Debug.Build.0 = Debug|Win32 + {87B244B0-EAC3-4109-A395-898D1CFF0EBE}.Debug_Server.ActiveCfg = Debug|Win32 + {87B244B0-EAC3-4109-A395-898D1CFF0EBE}.Debug_Server.Build.0 = Debug|Win32 + {87B244B0-EAC3-4109-A395-898D1CFF0EBE}.Ethereal Build.ActiveCfg = Release|Win32 + {87B244B0-EAC3-4109-A395-898D1CFF0EBE}.Ethereal Build.Build.0 = Release|Win32 + {87B244B0-EAC3-4109-A395-898D1CFF0EBE}.Release.ActiveCfg = Release|Win32 + {87B244B0-EAC3-4109-A395-898D1CFF0EBE}.Release.Build.0 = Release|Win32 + {87B244B0-EAC3-4109-A395-898D1CFF0EBE}.Release_Server.ActiveCfg = Release|Win32 + {87B244B0-EAC3-4109-A395-898D1CFF0EBE}.Release_Server.Build.0 = Release|Win32 + {58BE352F-CDB2-4A08-AC62-981800D018B0}.Debug.ActiveCfg = Debug|Win32 + {58BE352F-CDB2-4A08-AC62-981800D018B0}.Debug.Build.0 = Debug|Win32 + {58BE352F-CDB2-4A08-AC62-981800D018B0}.Debug_Server.ActiveCfg = Debug|Win32 + {58BE352F-CDB2-4A08-AC62-981800D018B0}.Debug_Server.Build.0 = Debug|Win32 + {58BE352F-CDB2-4A08-AC62-981800D018B0}.Ethereal Build.ActiveCfg = Release|Win32 + {58BE352F-CDB2-4A08-AC62-981800D018B0}.Ethereal Build.Build.0 = Release|Win32 + {58BE352F-CDB2-4A08-AC62-981800D018B0}.Release.ActiveCfg = Release|Win32 + {58BE352F-CDB2-4A08-AC62-981800D018B0}.Release.Build.0 = Release|Win32 + {58BE352F-CDB2-4A08-AC62-981800D018B0}.Release_Server.ActiveCfg = Release|Win32 + {58BE352F-CDB2-4A08-AC62-981800D018B0}.Release_Server.Build.0 = Release|Win32 + {6A79EBE3-87CD-4814-A57C-E24A61332979}.Debug.ActiveCfg = Debug|Win32 + {6A79EBE3-87CD-4814-A57C-E24A61332979}.Debug.Build.0 = Debug|Win32 + {6A79EBE3-87CD-4814-A57C-E24A61332979}.Debug_Server.ActiveCfg = Debug|Win32 + {6A79EBE3-87CD-4814-A57C-E24A61332979}.Debug_Server.Build.0 = Debug|Win32 + {6A79EBE3-87CD-4814-A57C-E24A61332979}.Ethereal Build.ActiveCfg = Debug|Win32 + {6A79EBE3-87CD-4814-A57C-E24A61332979}.Ethereal Build.Build.0 = Debug|Win32 + {6A79EBE3-87CD-4814-A57C-E24A61332979}.Release.ActiveCfg = Release|Win32 + {6A79EBE3-87CD-4814-A57C-E24A61332979}.Release.Build.0 = Release|Win32 + {6A79EBE3-87CD-4814-A57C-E24A61332979}.Release_Server.ActiveCfg = Release|Win32 + {6A79EBE3-87CD-4814-A57C-E24A61332979}.Release_Server.Build.0 = Release|Win32 + {77309195-77FA-4C01-B5CD-FB4733C0D3F3}.Debug.ActiveCfg = Debug|Win32 + {77309195-77FA-4C01-B5CD-FB4733C0D3F3}.Debug.Build.0 = Debug|Win32 + {77309195-77FA-4C01-B5CD-FB4733C0D3F3}.Debug_Server.ActiveCfg = Debug|Win32 + {77309195-77FA-4C01-B5CD-FB4733C0D3F3}.Debug_Server.Build.0 = Debug|Win32 + {77309195-77FA-4C01-B5CD-FB4733C0D3F3}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {77309195-77FA-4C01-B5CD-FB4733C0D3F3}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {77309195-77FA-4C01-B5CD-FB4733C0D3F3}.Release.ActiveCfg = Release|Win32 + {77309195-77FA-4C01-B5CD-FB4733C0D3F3}.Release.Build.0 = Release|Win32 + {77309195-77FA-4C01-B5CD-FB4733C0D3F3}.Release_Server.ActiveCfg = Release|Win32 + {77309195-77FA-4C01-B5CD-FB4733C0D3F3}.Release_Server.Build.0 = Release|Win32 + {2F891210-5851-4C21-A7F3-E37B9C007190}.Debug.ActiveCfg = Debug|Win32 + {2F891210-5851-4C21-A7F3-E37B9C007190}.Debug.Build.0 = Debug|Win32 + {2F891210-5851-4C21-A7F3-E37B9C007190}.Debug_Server.ActiveCfg = Debug|Win32 + {2F891210-5851-4C21-A7F3-E37B9C007190}.Debug_Server.Build.0 = Debug|Win32 + {2F891210-5851-4C21-A7F3-E37B9C007190}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {2F891210-5851-4C21-A7F3-E37B9C007190}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {2F891210-5851-4C21-A7F3-E37B9C007190}.Release.ActiveCfg = Release|Win32 + {2F891210-5851-4C21-A7F3-E37B9C007190}.Release.Build.0 = Release|Win32 + {2F891210-5851-4C21-A7F3-E37B9C007190}.Release_Server.ActiveCfg = Release|Win32 + {2F891210-5851-4C21-A7F3-E37B9C007190}.Release_Server.Build.0 = Release|Win32 + {5F6B7CF9-F4BD-4956-A064-5726350D9C90}.Debug.ActiveCfg = Debug|Win32 + {5F6B7CF9-F4BD-4956-A064-5726350D9C90}.Debug.Build.0 = Debug|Win32 + {5F6B7CF9-F4BD-4956-A064-5726350D9C90}.Debug_Server.ActiveCfg = Debug|Win32 + {5F6B7CF9-F4BD-4956-A064-5726350D9C90}.Debug_Server.Build.0 = Debug|Win32 + {5F6B7CF9-F4BD-4956-A064-5726350D9C90}.Ethereal Build.ActiveCfg = Release|Win32 + {5F6B7CF9-F4BD-4956-A064-5726350D9C90}.Ethereal Build.Build.0 = Release|Win32 + {5F6B7CF9-F4BD-4956-A064-5726350D9C90}.Release.ActiveCfg = Release|Win32 + {5F6B7CF9-F4BD-4956-A064-5726350D9C90}.Release.Build.0 = Release|Win32 + {5F6B7CF9-F4BD-4956-A064-5726350D9C90}.Release_Server.ActiveCfg = Release|Win32 + {5F6B7CF9-F4BD-4956-A064-5726350D9C90}.Release_Server.Build.0 = Release|Win32 + {D6D56A64-A79B-4F48-BB9D-8C216D66632F}.Debug.ActiveCfg = Debug|Win32 + {D6D56A64-A79B-4F48-BB9D-8C216D66632F}.Debug.Build.0 = Debug|Win32 + {D6D56A64-A79B-4F48-BB9D-8C216D66632F}.Debug_Server.ActiveCfg = Debug|Win32 + {D6D56A64-A79B-4F48-BB9D-8C216D66632F}.Debug_Server.Build.0 = Debug|Win32 + {D6D56A64-A79B-4F48-BB9D-8C216D66632F}.Ethereal Build.ActiveCfg = Debug|Win32 + {D6D56A64-A79B-4F48-BB9D-8C216D66632F}.Ethereal Build.Build.0 = Debug|Win32 + {D6D56A64-A79B-4F48-BB9D-8C216D66632F}.Release.ActiveCfg = Release|Win32 + {D6D56A64-A79B-4F48-BB9D-8C216D66632F}.Release.Build.0 = Release|Win32 + {D6D56A64-A79B-4F48-BB9D-8C216D66632F}.Release_Server.ActiveCfg = Release|Win32 + {D6D56A64-A79B-4F48-BB9D-8C216D66632F}.Release_Server.Build.0 = Release|Win32 + {CF98D440-1A73-4CD5-8760-9548F7D27F41}.Debug.ActiveCfg = Debug|Win32 + {CF98D440-1A73-4CD5-8760-9548F7D27F41}.Debug.Build.0 = Debug|Win32 + {CF98D440-1A73-4CD5-8760-9548F7D27F41}.Debug_Server.ActiveCfg = Debug|Win32 + {CF98D440-1A73-4CD5-8760-9548F7D27F41}.Debug_Server.Build.0 = Debug|Win32 + {CF98D440-1A73-4CD5-8760-9548F7D27F41}.Ethereal Build.ActiveCfg = Release|Win32 + {CF98D440-1A73-4CD5-8760-9548F7D27F41}.Ethereal Build.Build.0 = Release|Win32 + {CF98D440-1A73-4CD5-8760-9548F7D27F41}.Release.ActiveCfg = Release|Win32 + {CF98D440-1A73-4CD5-8760-9548F7D27F41}.Release.Build.0 = Release|Win32 + {CF98D440-1A73-4CD5-8760-9548F7D27F41}.Release_Server.ActiveCfg = Release|Win32 + {CF98D440-1A73-4CD5-8760-9548F7D27F41}.Release_Server.Build.0 = Release|Win32 + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C}.Debug.ActiveCfg = Debug|Win32 + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C}.Debug.Build.0 = Debug|Win32 + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C}.Debug_Server.ActiveCfg = Debug|Win32 + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C}.Debug_Server.Build.0 = Debug|Win32 + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C}.Release.ActiveCfg = Release|Win32 + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C}.Release.Build.0 = Release|Win32 + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C}.Release_Server.ActiveCfg = Release|Win32 + {D62C987F-3FB4-43D0-BA36-EB0D8517A42C}.Release_Server.Build.0 = Release|Win32 + {949195EB-0E59-4357-9FD6-D761E64C0494}.Debug.ActiveCfg = Debug|Win32 + {949195EB-0E59-4357-9FD6-D761E64C0494}.Debug.Build.0 = Debug|Win32 + {949195EB-0E59-4357-9FD6-D761E64C0494}.Debug_Server.ActiveCfg = Debug|Win32 + {949195EB-0E59-4357-9FD6-D761E64C0494}.Debug_Server.Build.0 = Debug|Win32 + {949195EB-0E59-4357-9FD6-D761E64C0494}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {949195EB-0E59-4357-9FD6-D761E64C0494}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {949195EB-0E59-4357-9FD6-D761E64C0494}.Release.ActiveCfg = Release|Win32 + {949195EB-0E59-4357-9FD6-D761E64C0494}.Release.Build.0 = Release|Win32 + {949195EB-0E59-4357-9FD6-D761E64C0494}.Release_Server.ActiveCfg = Release|Win32 + {949195EB-0E59-4357-9FD6-D761E64C0494}.Release_Server.Build.0 = Release|Win32 + {94BEB453-9F25-40CF-89F8-A5BC9AE9465E}.Debug.ActiveCfg = Debug|Win32 + {94BEB453-9F25-40CF-89F8-A5BC9AE9465E}.Debug.Build.0 = Debug|Win32 + {94BEB453-9F25-40CF-89F8-A5BC9AE9465E}.Debug_Server.ActiveCfg = Debug|Win32 + {94BEB453-9F25-40CF-89F8-A5BC9AE9465E}.Debug_Server.Build.0 = Debug|Win32 + {94BEB453-9F25-40CF-89F8-A5BC9AE9465E}.Ethereal Build.ActiveCfg = Release|Win32 + {94BEB453-9F25-40CF-89F8-A5BC9AE9465E}.Ethereal Build.Build.0 = Release|Win32 + {94BEB453-9F25-40CF-89F8-A5BC9AE9465E}.Release.ActiveCfg = Release|Win32 + {94BEB453-9F25-40CF-89F8-A5BC9AE9465E}.Release.Build.0 = Release|Win32 + {94BEB453-9F25-40CF-89F8-A5BC9AE9465E}.Release_Server.ActiveCfg = Release|Win32 + {94BEB453-9F25-40CF-89F8-A5BC9AE9465E}.Release_Server.Build.0 = Release|Win32 + {3F37A050-1F2B-4249-B0D5-81DBCF574678}.Debug.ActiveCfg = Debug|Win32 + {3F37A050-1F2B-4249-B0D5-81DBCF574678}.Debug.Build.0 = Debug|Win32 + {3F37A050-1F2B-4249-B0D5-81DBCF574678}.Debug_Server.ActiveCfg = Debug|Win32 + {3F37A050-1F2B-4249-B0D5-81DBCF574678}.Debug_Server.Build.0 = Debug|Win32 + {3F37A050-1F2B-4249-B0D5-81DBCF574678}.Ethereal Build.ActiveCfg = Debug|Win32 + {3F37A050-1F2B-4249-B0D5-81DBCF574678}.Ethereal Build.Build.0 = Debug|Win32 + {3F37A050-1F2B-4249-B0D5-81DBCF574678}.Release.ActiveCfg = Release|Win32 + {3F37A050-1F2B-4249-B0D5-81DBCF574678}.Release.Build.0 = Release|Win32 + {3F37A050-1F2B-4249-B0D5-81DBCF574678}.Release_Server.ActiveCfg = Release|Win32 + {3F37A050-1F2B-4249-B0D5-81DBCF574678}.Release_Server.Build.0 = Release|Win32 + {579E045E-36ED-4EC1-90FD-3AF45674FD27}.Debug.ActiveCfg = Debug|Win32 + {579E045E-36ED-4EC1-90FD-3AF45674FD27}.Debug.Build.0 = Debug|Win32 + {579E045E-36ED-4EC1-90FD-3AF45674FD27}.Debug_Server.ActiveCfg = Debug|Win32 + {579E045E-36ED-4EC1-90FD-3AF45674FD27}.Debug_Server.Build.0 = Debug|Win32 + {579E045E-36ED-4EC1-90FD-3AF45674FD27}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {579E045E-36ED-4EC1-90FD-3AF45674FD27}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {579E045E-36ED-4EC1-90FD-3AF45674FD27}.Release.ActiveCfg = Release|Win32 + {579E045E-36ED-4EC1-90FD-3AF45674FD27}.Release.Build.0 = Release|Win32 + {579E045E-36ED-4EC1-90FD-3AF45674FD27}.Release_Server.ActiveCfg = Release|Win32 + {579E045E-36ED-4EC1-90FD-3AF45674FD27}.Release_Server.Build.0 = Release|Win32 + {ECF7D320-CB30-4628-92FD-E7672A72064F}.Debug.ActiveCfg = Debug|Win32 + {ECF7D320-CB30-4628-92FD-E7672A72064F}.Debug.Build.0 = Debug|Win32 + {ECF7D320-CB30-4628-92FD-E7672A72064F}.Debug_Server.ActiveCfg = Debug|Win32 + {ECF7D320-CB30-4628-92FD-E7672A72064F}.Debug_Server.Build.0 = Debug|Win32 + {ECF7D320-CB30-4628-92FD-E7672A72064F}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {ECF7D320-CB30-4628-92FD-E7672A72064F}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {ECF7D320-CB30-4628-92FD-E7672A72064F}.Release.ActiveCfg = Release|Win32 + {ECF7D320-CB30-4628-92FD-E7672A72064F}.Release.Build.0 = Release|Win32 + {ECF7D320-CB30-4628-92FD-E7672A72064F}.Release_Server.ActiveCfg = Release|Win32 + {ECF7D320-CB30-4628-92FD-E7672A72064F}.Release_Server.Build.0 = Release|Win32 + {DBE0CBC3-0EC0-4363-9B1D-239227B3C985}.Debug.ActiveCfg = Debug|Win32 + {DBE0CBC3-0EC0-4363-9B1D-239227B3C985}.Debug.Build.0 = Debug|Win32 + {DBE0CBC3-0EC0-4363-9B1D-239227B3C985}.Debug_Server.ActiveCfg = Debug|Win32 + {DBE0CBC3-0EC0-4363-9B1D-239227B3C985}.Debug_Server.Build.0 = Debug|Win32 + {DBE0CBC3-0EC0-4363-9B1D-239227B3C985}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {DBE0CBC3-0EC0-4363-9B1D-239227B3C985}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {DBE0CBC3-0EC0-4363-9B1D-239227B3C985}.Release.ActiveCfg = Release|Win32 + {DBE0CBC3-0EC0-4363-9B1D-239227B3C985}.Release.Build.0 = Release|Win32 + {DBE0CBC3-0EC0-4363-9B1D-239227B3C985}.Release_Server.ActiveCfg = Release|Win32 + {DBE0CBC3-0EC0-4363-9B1D-239227B3C985}.Release_Server.Build.0 = Release|Win32 + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032}.Debug.ActiveCfg = Debug|Win32 + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032}.Debug.Build.0 = Debug|Win32 + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032}.Debug_Server.ActiveCfg = Debug|Win32 + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032}.Debug_Server.Build.0 = Debug|Win32 + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032}.Release.ActiveCfg = Release|Win32 + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032}.Release.Build.0 = Release|Win32 + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032}.Release_Server.ActiveCfg = Release|Win32 + {9CF43A4A-4830-4DF6-BB3C-693AE88C2032}.Release_Server.Build.0 = Release|Win32 + {08096103-4BF9-4A6B-8088-AA9F6C989575}.Debug.ActiveCfg = Debug|Win32 + {08096103-4BF9-4A6B-8088-AA9F6C989575}.Debug.Build.0 = Debug|Win32 + {08096103-4BF9-4A6B-8088-AA9F6C989575}.Debug_Server.ActiveCfg = Debug|Win32 + {08096103-4BF9-4A6B-8088-AA9F6C989575}.Debug_Server.Build.0 = Debug|Win32 + {08096103-4BF9-4A6B-8088-AA9F6C989575}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {08096103-4BF9-4A6B-8088-AA9F6C989575}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {08096103-4BF9-4A6B-8088-AA9F6C989575}.Release.ActiveCfg = Release|Win32 + {08096103-4BF9-4A6B-8088-AA9F6C989575}.Release.Build.0 = Release|Win32 + {08096103-4BF9-4A6B-8088-AA9F6C989575}.Release_Server.ActiveCfg = Release|Win32 + {08096103-4BF9-4A6B-8088-AA9F6C989575}.Release_Server.Build.0 = Release|Win32 + {20917EBD-2A7E-49F5-A638-F5864265FF8C}.Debug.ActiveCfg = Debug|Win32 + {20917EBD-2A7E-49F5-A638-F5864265FF8C}.Debug.Build.0 = Debug|Win32 + {20917EBD-2A7E-49F5-A638-F5864265FF8C}.Debug_Server.ActiveCfg = Debug|Win32 + {20917EBD-2A7E-49F5-A638-F5864265FF8C}.Debug_Server.Build.0 = Debug|Win32 + {20917EBD-2A7E-49F5-A638-F5864265FF8C}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {20917EBD-2A7E-49F5-A638-F5864265FF8C}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {20917EBD-2A7E-49F5-A638-F5864265FF8C}.Release.ActiveCfg = Release|Win32 + {20917EBD-2A7E-49F5-A638-F5864265FF8C}.Release.Build.0 = Release|Win32 + {20917EBD-2A7E-49F5-A638-F5864265FF8C}.Release_Server.ActiveCfg = Release|Win32 + {20917EBD-2A7E-49F5-A638-F5864265FF8C}.Release_Server.Build.0 = Release|Win32 + {4F0E6414-0FC0-4819-B2A9-90774C0FC340}.Debug.ActiveCfg = Debug|Win32 + {4F0E6414-0FC0-4819-B2A9-90774C0FC340}.Debug.Build.0 = Debug|Win32 + {4F0E6414-0FC0-4819-B2A9-90774C0FC340}.Debug_Server.ActiveCfg = Debug|Win32 + {4F0E6414-0FC0-4819-B2A9-90774C0FC340}.Debug_Server.Build.0 = Debug|Win32 + {4F0E6414-0FC0-4819-B2A9-90774C0FC340}.Ethereal Build.ActiveCfg = Release|Win32 + {4F0E6414-0FC0-4819-B2A9-90774C0FC340}.Ethereal Build.Build.0 = Release|Win32 + {4F0E6414-0FC0-4819-B2A9-90774C0FC340}.Release.ActiveCfg = Release|Win32 + {4F0E6414-0FC0-4819-B2A9-90774C0FC340}.Release.Build.0 = Release|Win32 + {4F0E6414-0FC0-4819-B2A9-90774C0FC340}.Release_Server.ActiveCfg = Release|Win32 + {4F0E6414-0FC0-4819-B2A9-90774C0FC340}.Release_Server.Build.0 = Release|Win32 + {7EFB10F8-B984-467C-BD50-B117DD633840}.Debug.ActiveCfg = Debug|Win32 + {7EFB10F8-B984-467C-BD50-B117DD633840}.Debug.Build.0 = Debug|Win32 + {7EFB10F8-B984-467C-BD50-B117DD633840}.Debug_Server.ActiveCfg = Debug|Win32 + {7EFB10F8-B984-467C-BD50-B117DD633840}.Debug_Server.Build.0 = Debug|Win32 + {7EFB10F8-B984-467C-BD50-B117DD633840}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {7EFB10F8-B984-467C-BD50-B117DD633840}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {7EFB10F8-B984-467C-BD50-B117DD633840}.Release.ActiveCfg = Release|Win32 + {7EFB10F8-B984-467C-BD50-B117DD633840}.Release.Build.0 = Release|Win32 + {7EFB10F8-B984-467C-BD50-B117DD633840}.Release_Server.ActiveCfg = Release|Win32 + {7EFB10F8-B984-467C-BD50-B117DD633840}.Release_Server.Build.0 = Release|Win32 + {A927606C-7646-43C0-A3BB-B21836CC7D02}.Debug.ActiveCfg = Debug|Win32 + {A927606C-7646-43C0-A3BB-B21836CC7D02}.Debug.Build.0 = Debug|Win32 + {A927606C-7646-43C0-A3BB-B21836CC7D02}.Debug_Server.ActiveCfg = Debug|Win32 + {A927606C-7646-43C0-A3BB-B21836CC7D02}.Debug_Server.Build.0 = Debug|Win32 + {A927606C-7646-43C0-A3BB-B21836CC7D02}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {A927606C-7646-43C0-A3BB-B21836CC7D02}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {A927606C-7646-43C0-A3BB-B21836CC7D02}.Release.ActiveCfg = Release|Win32 + {A927606C-7646-43C0-A3BB-B21836CC7D02}.Release.Build.0 = Release|Win32 + {A927606C-7646-43C0-A3BB-B21836CC7D02}.Release_Server.ActiveCfg = Release|Win32 + {A927606C-7646-43C0-A3BB-B21836CC7D02}.Release_Server.Build.0 = Release|Win32 + {50A7D510-800C-421A-A0D4-B3AF8FBC1060}.Debug.ActiveCfg = Debug|Win32 + {50A7D510-800C-421A-A0D4-B3AF8FBC1060}.Debug.Build.0 = Debug|Win32 + {50A7D510-800C-421A-A0D4-B3AF8FBC1060}.Debug_Server.ActiveCfg = Debug|Win32 + {50A7D510-800C-421A-A0D4-B3AF8FBC1060}.Debug_Server.Build.0 = Debug|Win32 + {50A7D510-800C-421A-A0D4-B3AF8FBC1060}.Ethereal Build.ActiveCfg = Release|Win32 + {50A7D510-800C-421A-A0D4-B3AF8FBC1060}.Ethereal Build.Build.0 = Release|Win32 + {50A7D510-800C-421A-A0D4-B3AF8FBC1060}.Release.ActiveCfg = Release|Win32 + {50A7D510-800C-421A-A0D4-B3AF8FBC1060}.Release.Build.0 = Release|Win32 + {50A7D510-800C-421A-A0D4-B3AF8FBC1060}.Release_Server.ActiveCfg = Release|Win32 + {50A7D510-800C-421A-A0D4-B3AF8FBC1060}.Release_Server.Build.0 = Release|Win32 + {C10FF87E-ED60-4183-81BB-B5C35489CE85}.Debug.ActiveCfg = Debug|Win32 + {C10FF87E-ED60-4183-81BB-B5C35489CE85}.Debug.Build.0 = Debug|Win32 + {C10FF87E-ED60-4183-81BB-B5C35489CE85}.Debug_Server.ActiveCfg = Debug|Win32 + {C10FF87E-ED60-4183-81BB-B5C35489CE85}.Debug_Server.Build.0 = Debug|Win32 + {C10FF87E-ED60-4183-81BB-B5C35489CE85}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {C10FF87E-ED60-4183-81BB-B5C35489CE85}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {C10FF87E-ED60-4183-81BB-B5C35489CE85}.Release.ActiveCfg = Release|Win32 + {C10FF87E-ED60-4183-81BB-B5C35489CE85}.Release.Build.0 = Release|Win32 + {C10FF87E-ED60-4183-81BB-B5C35489CE85}.Release_Server.ActiveCfg = Release|Win32 + {C10FF87E-ED60-4183-81BB-B5C35489CE85}.Release_Server.Build.0 = Release|Win32 + {E8FE47B0-DF75-4D9F-9096-0BC204239B52}.Debug.ActiveCfg = Debug|Win32 + {E8FE47B0-DF75-4D9F-9096-0BC204239B52}.Debug.Build.0 = Debug|Win32 + {E8FE47B0-DF75-4D9F-9096-0BC204239B52}.Debug_Server.ActiveCfg = Debug|Win32 + {E8FE47B0-DF75-4D9F-9096-0BC204239B52}.Debug_Server.Build.0 = Debug|Win32 + {E8FE47B0-DF75-4D9F-9096-0BC204239B52}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {E8FE47B0-DF75-4D9F-9096-0BC204239B52}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {E8FE47B0-DF75-4D9F-9096-0BC204239B52}.Release.ActiveCfg = Release|Win32 + {E8FE47B0-DF75-4D9F-9096-0BC204239B52}.Release.Build.0 = Release|Win32 + {E8FE47B0-DF75-4D9F-9096-0BC204239B52}.Release_Server.ActiveCfg = Release|Win32 + {E8FE47B0-DF75-4D9F-9096-0BC204239B52}.Release_Server.Build.0 = Release|Win32 + {D8D19980-BCE6-47E5-9661-030ED27FE6C5}.Debug.ActiveCfg = Debug|Win32 + {D8D19980-BCE6-47E5-9661-030ED27FE6C5}.Debug.Build.0 = Debug|Win32 + {D8D19980-BCE6-47E5-9661-030ED27FE6C5}.Debug_Server.ActiveCfg = Debug|Win32 + {D8D19980-BCE6-47E5-9661-030ED27FE6C5}.Debug_Server.Build.0 = Debug|Win32 + {D8D19980-BCE6-47E5-9661-030ED27FE6C5}.Ethereal Build.ActiveCfg = Release|Win32 + {D8D19980-BCE6-47E5-9661-030ED27FE6C5}.Ethereal Build.Build.0 = Release|Win32 + {D8D19980-BCE6-47E5-9661-030ED27FE6C5}.Release.ActiveCfg = Release|Win32 + {D8D19980-BCE6-47E5-9661-030ED27FE6C5}.Release.Build.0 = Release|Win32 + {D8D19980-BCE6-47E5-9661-030ED27FE6C5}.Release_Server.ActiveCfg = Release|Win32 + {D8D19980-BCE6-47E5-9661-030ED27FE6C5}.Release_Server.Build.0 = Release|Win32 + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6}.Debug.ActiveCfg = Debug|Win32 + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6}.Debug.Build.0 = Debug|Win32 + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6}.Debug_Server.ActiveCfg = Debug|Win32 + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6}.Debug_Server.Build.0 = Debug|Win32 + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6}.Ethereal Build.ActiveCfg = Ethereal Build|Win32 + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6}.Ethereal Build.Build.0 = Ethereal Build|Win32 + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6}.Release.ActiveCfg = Release|Win32 + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6}.Release.Build.0 = Release|Win32 + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6}.Release_Server.ActiveCfg = Release|Win32 + {65C4DE61-E599-4F0F-9B75-66C0DA887BB6}.Release_Server.Build.0 = Release|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Debug.ActiveCfg = Debug|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Debug.Build.0 = Debug|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Debug_Server.ActiveCfg = Debug|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Debug_Server.Build.0 = Debug|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Ethereal Build.ActiveCfg = Release|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Ethereal Build.Build.0 = Release|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Release.ActiveCfg = Release|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Release.Build.0 = Release|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Release_Server.ActiveCfg = Release|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Release_Server.Build.0 = Release|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Debug.ActiveCfg = Debug|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Debug.Build.0 = Debug|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Debug_Server.Build.0 = Debug_Server|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Ethereal Build.Build.0 = Release_Server|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Release.ActiveCfg = Release|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Release.Build.0 = Release|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Release_Server.ActiveCfg = Release_Server|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Release_Server.Build.0 = Release_Server|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Debug.ActiveCfg = Debug|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Debug.Build.0 = Debug|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Debug_Server.Build.0 = Debug_Server|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Ethereal Build.Build.0 = Release_Server|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Release.ActiveCfg = Release|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Release.Build.0 = Release|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Release_Server.ActiveCfg = Release_Server|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Release_Server.Build.0 = Release_Server|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Debug.ActiveCfg = Debug|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Debug.Build.0 = Debug|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Debug_Server.Build.0 = Debug_Server|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Ethereal Build.Build.0 = Release_Server|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Release.ActiveCfg = Release|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Release.Build.0 = Release|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Release_Server.ActiveCfg = Release_Server|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Release_Server.Build.0 = Release_Server|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Debug.ActiveCfg = Debug|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Debug.Build.0 = Debug|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Debug_Server.Build.0 = Debug_Server|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Ethereal Build.Build.0 = Release_Server|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Release.ActiveCfg = Release|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Release.Build.0 = Release|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Release_Server.ActiveCfg = Release_Server|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Release_Server.Build.0 = Release_Server|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Debug.ActiveCfg = Debug|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Debug.Build.0 = Debug|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Debug_Server.Build.0 = Debug_Server|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Ethereal Build.Build.0 = Release_Server|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Release.ActiveCfg = Release|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Release.Build.0 = Release|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Release_Server.ActiveCfg = Release_Server|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Release_Server.Build.0 = Release_Server|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Debug.ActiveCfg = Debug|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Debug.Build.0 = Debug|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Debug_Server.Build.0 = Debug_Server|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Ethereal Build.Build.0 = Release_Server|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Release.ActiveCfg = Release|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Release.Build.0 = Release|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Release_Server.ActiveCfg = Release_Server|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Release_Server.Build.0 = Release_Server|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Debug.ActiveCfg = Debug|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Debug.Build.0 = Debug|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Debug_Server.Build.0 = Debug_Server|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Ethereal Build.Build.0 = Release_Server|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Release.ActiveCfg = Release|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Release.Build.0 = Release|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Release_Server.ActiveCfg = Release_Server|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Release_Server.Build.0 = Release_Server|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Debug.ActiveCfg = Debug|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Debug.Build.0 = Debug|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Debug_Server.Build.0 = Debug_Server|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Ethereal Build.Build.0 = Release_Server|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Release.ActiveCfg = Release|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Release.Build.0 = Release|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Release_Server.ActiveCfg = Release_Server|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Release_Server.Build.0 = Release_Server|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Debug.ActiveCfg = Debug|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Debug.Build.0 = Debug|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Debug_Server.Build.0 = Debug_Server|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Ethereal Build.Build.0 = Release_Server|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Release.ActiveCfg = Release|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Release.Build.0 = Release|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Release_Server.ActiveCfg = Release_Server|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Release_Server.Build.0 = Release_Server|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Debug.ActiveCfg = Debug|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Debug.Build.0 = Debug|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Debug_Server.Build.0 = Debug_Server|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Ethereal Build.Build.0 = Release_Server|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Release.ActiveCfg = Release|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Release.Build.0 = Release|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Release_Server.ActiveCfg = Release_Server|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Release_Server.Build.0 = Release_Server|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Debug.ActiveCfg = Debug|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Debug.Build.0 = Debug|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Debug_Server.ActiveCfg = Debug|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Debug_Server.Build.0 = Debug|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Ethereal Build.ActiveCfg = Release|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Ethereal Build.Build.0 = Release|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Release.ActiveCfg = Release|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Release.Build.0 = Release|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Release_Server.ActiveCfg = Release|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Release_Server.Build.0 = Release|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Debug.ActiveCfg = Debug|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Debug.Build.0 = Debug|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Debug_Server.Build.0 = Debug_Server|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Ethereal Build.Build.0 = Release_Server|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Release.ActiveCfg = Release|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Release.Build.0 = Release|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Release_Server.ActiveCfg = Release_Server|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Release_Server.Build.0 = Release_Server|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Debug.ActiveCfg = Debug|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Debug.Build.0 = Debug|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Debug_Server.ActiveCfg = Debug|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Debug_Server.Build.0 = Debug|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Ethereal Build.ActiveCfg = Release|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Ethereal Build.Build.0 = Release|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Release.ActiveCfg = Release|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Release.Build.0 = Release|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Release_Server.ActiveCfg = Release|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Release_Server.Build.0 = Release|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Debug.ActiveCfg = Debug|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Debug.Build.0 = Debug|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Debug_Server.ActiveCfg = Debug|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Debug_Server.Build.0 = Debug|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Ethereal Build.ActiveCfg = Release|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Ethereal Build.Build.0 = Release|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Release.ActiveCfg = Release|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Release.Build.0 = Release|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Release_Server.ActiveCfg = Release|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Release_Server.Build.0 = Release|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Debug.ActiveCfg = Debug|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Debug.Build.0 = Debug|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Debug_Server.Build.0 = Debug_Server|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Ethereal Build.ActiveCfg = Release_Server|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Ethereal Build.Build.0 = Release_Server|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Release.ActiveCfg = Release|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Release.Build.0 = Release|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Release_Server.ActiveCfg = Release_Server|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Release_Server.Build.0 = Release_Server|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Debug.ActiveCfg = Debug|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Debug.Build.0 = Debug|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Debug_Server.ActiveCfg = Debug|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Debug_Server.Build.0 = Debug|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Ethereal Build.ActiveCfg = Release|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Ethereal Build.Build.0 = Release|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Release.ActiveCfg = Release|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Release.Build.0 = Release|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Release_Server.ActiveCfg = Release|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Release_Server.Build.0 = Release|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Debug.ActiveCfg = Debug|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Debug.Build.0 = Debug|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Debug_Server.ActiveCfg = Debug|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Debug_Server.Build.0 = Debug|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Ethereal Build.ActiveCfg = Release|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Ethereal Build.Build.0 = Release|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Release.ActiveCfg = Release|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Release.Build.0 = Release|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Release_Server.ActiveCfg = Release|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Release_Server.Build.0 = Release|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Debug.ActiveCfg = Debug|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Debug.Build.0 = Debug|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Debug_Server.ActiveCfg = Debug|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Debug_Server.Build.0 = Debug|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Ethereal Build.ActiveCfg = Release|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Ethereal Build.Build.0 = Release|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Release.ActiveCfg = Release|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Release.Build.0 = Release|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Release_Server.ActiveCfg = Release|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Release_Server.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/AllDlls/AllDlls.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/AllDlls/AllDlls.vcproj new file mode 100644 index 00000000..efe26079 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/AllDlls/AllDlls.vcproj @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/AllClient/AllClient.sln b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/AllClient/AllClient.sln new file mode 100644 index 00000000..a2487f9e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/AllClient/AllClient.sln @@ -0,0 +1,1394 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AllClient", "AllClient.vcproj", "{0736D624-C5F6-4B44-96FC-B27C2841D2F9}" + ProjectSection(ProjectDependencies) = postProject + {EED64223-B75A-4476-9F16-6ACE587158CD} = {EED64223-B75A-4476-9F16-6ACE587158CD} + {5F97BA7C-6E08-45DA-A68C-338246A3A4E7} = {5F97BA7C-6E08-45DA-A68C-338246A3A4E7} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoreLib", "..\..\CoreLib\CoreLib.vcproj", "{8444A94C-FD1E-47D2-B978-B42ED198F234}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FeatureLibInc", "..\..\FeatureLib\FeatureLibInc\FeatureLibInc.vcproj", "{0182EF47-D0B1-4886-B4FC-F126C638FD3F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PubUtilInc", "..\..\PubUtilLib\PubUtilInc\PubUtilInc.vcproj", "{3E917DA0-6B4B-4C82-83DB-C246311458A9}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfAnimation", "..\..\FeatureLib\pfAnimation\pfAnimation.vcproj", "{86881785-432A-4AA3-A664-43A9E8FD9BA0}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfAudio", "..\..\FeatureLib\pfAudio\pfAudio.vcproj", "{58853F23-308E-4698-AAA6-795ABF98790C}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfCamera", "..\..\FeatureLib\pfCamera\pfCamera.vcproj", "{DA08E306-B61E-4C1D-90F3-194C70FA7027}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfCharacter", "..\..\FeatureLib\pfCharacter\pfCharacter.vcproj", "{6CB8A937-3C28-447E-B753-081E275E58FF}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfConditional", "..\..\FeatureLib\pfConditional\pfConditional.vcproj", "{5D217AA3-6A2C-4718-9985-4C9037F21733}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfConsole", "..\..\FeatureLib\pfConsole\pfConsole.vcproj", "{79A647BA-E0B7-49E1-85CF-2087EC0CE096}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfGameGUIMgr", "..\..\FeatureLib\pfGameGUIMgr\pfGameGUIMgr.vcproj", "{96755478-80D1-4FF6-AF80-5013209C5FD6}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfJournalBook", "..\..\FEATURELIB\pfJournalBook\pfJournalBook.vcproj", "{C6CC69A5-E133-4146-BA9B-B4B448419896}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfMessage", "..\..\FeatureLib\pfMessage\pfMessage.vcproj", "{B24DB433-EA09-45F3-9296-38FB6AABA9E2}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfPython", "..\..\FeatureLib\pfPython\pfPython.vcproj", "{A47A0DF4-F080-42B4-BC3B-865A02387089}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfStackTrace", "..\..\FEATURELIB\pfStackTrace\pfStackTrace.vcproj", "{4C405BDB-4D4F-4466-A62A-16D59DBD0076}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfSurface", "..\..\FeatureLib\pfSurface\pfSurface.vcproj", "{88DB3B21-DBD4-4325-910B-F9256356F802}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAgeDescription", "..\..\PubUtilLib\plAgeDescription\plAgeDescription.vcproj", "{A200654F-E610-4959-B3D2-D2AFD6E078CE}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAgeLoader", "..\..\PUBUTILLIB\plAgeLoader\plAgeLoader.vcproj", "{A7A75DBE-34C6-4A64-9238-2A284A8AB456}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAudible", "..\..\PubUtilLib\plAudible\plAudible.vcproj", "{07726299-2609-4461-90BC-FF5A742766D5}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAudio", "..\..\PubUtilLib\plAudio\plAudio.vcproj", "{888D68F8-40F4-43DB-B786-53EA906517F3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAudioCore", "..\..\PUBUTILLIB\plAudioCore\plAudioCore.vcproj", "{2D715490-C219-4952-ABE9-3D23FA8775EB}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plAvatar", "..\..\PubUtilLib\plAvatar\plAvatar.vcproj", "{4FD2804C-465E-40F1-B766-48A1632FEEA7}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plClient", "..\plClient\plClient.vcproj", "{5F97BA7C-6E08-45DA-A68C-338246A3A4E7}" + ProjectSection(ProjectDependencies) = postProject + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED} = {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED} + {BBDF8302-BE9C-43EC-AAF8-912E601515B3} = {BBDF8302-BE9C-43EC-AAF8-912E601515B3} + {663AB605-0B37-41F6-A015-7219D9B5F84B} = {663AB605-0B37-41F6-A015-7219D9B5F84B} + {DE62CF05-8E3E-40A4-967D-330216C2D2ED} = {DE62CF05-8E3E-40A4-967D-330216C2D2ED} + {DA08E306-B61E-4C1D-90F3-194C70FA7027} = {DA08E306-B61E-4C1D-90F3-194C70FA7027} + {A8350709-C3E0-4B19-9218-D1B23C6E97DC} = {A8350709-C3E0-4B19-9218-D1B23C6E97DC} + {C3A1070E-8C00-4CE5-B4E8-69660032DB71} = {C3A1070E-8C00-4CE5-B4E8-69660032DB71} + {49365412-EA0C-4851-9FA9-2E1799ED7A82} = {49365412-EA0C-4851-9FA9-2E1799ED7A82} + {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30} = {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30} + {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53} = {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53} + {D97E0E20-0502-4E63-BD05-41B42EED695F} = {D97E0E20-0502-4E63-BD05-41B42EED695F} + {56C50721-716D-468B-9EEB-750DA8C50EF7} = {56C50721-716D-468B-9EEB-750DA8C50EF7} + {88DB3B21-DBD4-4325-910B-F9256356F802} = {88DB3B21-DBD4-4325-910B-F9256356F802} + {1E924D22-D5D8-4042-8A0F-F75266A0B3B6} = {1E924D22-D5D8-4042-8A0F-F75266A0B3B6} + {58853F23-308E-4698-AAA6-795ABF98790C} = {58853F23-308E-4698-AAA6-795ABF98790C} + {4E991726-D69F-4786-A873-DF37FB4DABF9} = {4E991726-D69F-4786-A873-DF37FB4DABF9} + {4576792A-58CA-478A-9CB1-FBE05E559C2F} = {4576792A-58CA-478A-9CB1-FBE05E559C2F} + {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E} = {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E} + {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF} = {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF} + {D20A0F2B-6822-4EE7-88C0-37ED22E38583} = {D20A0F2B-6822-4EE7-88C0-37ED22E38583} + {6F09B62B-34FD-4A60-9030-DB8FFACA66BA} = {6F09B62B-34FD-4A60-9030-DB8FFACA66BA} + {E710BB2B-8E1E-434E-8480-10E3AF6DF53B} = {E710BB2B-8E1E-434E-8480-10E3AF6DF53B} + {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E} = {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E} + {677C542F-9027-418D-9D9E-2D526E9FCD09} = {677C542F-9027-418D-9D9E-2D526E9FCD09} + {CE472632-E2F1-446C-ABAB-1CE5BD9F367B} = {CE472632-E2F1-446C-ABAB-1CE5BD9F367B} + {B24DB433-EA09-45F3-9296-38FB6AABA9E2} = {B24DB433-EA09-45F3-9296-38FB6AABA9E2} + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA} = {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA} + {9CDD8836-9B27-4AB8-82DB-8200308CE38A} = {9CDD8836-9B27-4AB8-82DB-8200308CE38A} + {6CB8A937-3C28-447E-B753-081E275E58FF} = {6CB8A937-3C28-447E-B753-081E275E58FF} + {C28B9838-04AE-4EBD-A93F-A94A64230887} = {C28B9838-04AE-4EBD-A93F-A94A64230887} + {46B6473A-BD67-4DB8-9479-3C26B8626CFA} = {46B6473A-BD67-4DB8-9479-3C26B8626CFA} + {65A72F3C-FE82-4014-81A3-4647938F40AA} = {65A72F3C-FE82-4014-81A3-4647938F40AA} + {A58C3644-55E7-44EB-BE85-0D53773B8BA9} = {A58C3644-55E7-44EB-BE85-0D53773B8BA9} + {28E5E146-D581-4492-9DD7-D56247A9F027} = {28E5E146-D581-4492-9DD7-D56247A9F027} + {0182EF47-D0B1-4886-B4FC-F126C638FD3F} = {0182EF47-D0B1-4886-B4FC-F126C638FD3F} + {4FD2804C-465E-40F1-B766-48A1632FEEA7} = {4FD2804C-465E-40F1-B766-48A1632FEEA7} + {4AD6934C-977D-4A01-9FAA-D2C32D96DD0B} = {4AD6934C-977D-4A01-9FAA-D2C32D96DD0B} + {8444A94C-FD1E-47D2-B978-B42ED198F234} = {8444A94C-FD1E-47D2-B978-B42ED198F234} + {32EC294E-2A45-4627-B5C4-B5CD236C2674} = {32EC294E-2A45-4627-B5C4-B5CD236C2674} + {A200654F-E610-4959-B3D2-D2AFD6E078CE} = {A200654F-E610-4959-B3D2-D2AFD6E078CE} + {4054C94F-866A-4AA7-874B-2AFCFEF23A71} = {4054C94F-866A-4AA7-874B-2AFCFEF23A71} + {0BC97654-C80F-4870-B56B-56DDCEB09B59} = {0BC97654-C80F-4870-B56B-56DDCEB09B59} + {89316659-F4A9-4E92-8200-C36288A61B9B} = {89316659-F4A9-4E92-8200-C36288A61B9B} + {1599855E-CC20-4C3A-A382-5290C40BE08E} = {1599855E-CC20-4C3A-A382-5290C40BE08E} + {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477} = {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477} + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3} = {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3} + {CA208971-6C77-47F6-AA4B-FB6ECC071132} = {CA208971-6C77-47F6-AA4B-FB6ECC071132} + {7D83F872-F06C-4D42-A8F2-D77DC22C5981} = {7D83F872-F06C-4D42-A8F2-D77DC22C5981} + {ECFC7F74-12DC-40FE-B201-9D5002F53721} = {ECFC7F74-12DC-40FE-B201-9D5002F53721} + {B7B9B275-1059-40FE-9502-0CF42B5BA32F} = {B7B9B275-1059-40FE-9502-0CF42B5BA32F} + {96755478-80D1-4FF6-AF80-5013209C5FD6} = {96755478-80D1-4FF6-AF80-5013209C5FD6} + {42503779-D12B-435D-9EA8-28D9FE0C9B08} = {42503779-D12B-435D-9EA8-28D9FE0C9B08} + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D} = {AB45BA83-4921-4A3C-80CF-1984D49ACA0D} + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F} = {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F} + {86881785-432A-4AA3-A664-43A9E8FD9BA0} = {86881785-432A-4AA3-A664-43A9E8FD9BA0} + {F83B1689-144D-4183-A2A5-0BE3D1892C49} = {F83B1689-144D-4183-A2A5-0BE3D1892C49} + {C0FC318E-8E10-488C-939F-84C97D9B5972} = {C0FC318E-8E10-488C-939F-84C97D9B5972} + {2D715490-C219-4952-ABE9-3D23FA8775EB} = {2D715490-C219-4952-ABE9-3D23FA8775EB} + {54AC8E94-D1FE-4533-9E3A-B35C143CDD93} = {54AC8E94-D1FE-4533-9E3A-B35C143CDD93} + {07726299-2609-4461-90BC-FF5A742766D5} = {07726299-2609-4461-90BC-FF5A742766D5} + {3E917DA0-6B4B-4C82-83DB-C246311458A9} = {3E917DA0-6B4B-4C82-83DB-C246311458A9} + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8} = {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8} + {5D217AA3-6A2C-4718-9985-4C9037F21733} = {5D217AA3-6A2C-4718-9985-4C9037F21733} + {C6CC69A5-E133-4146-BA9B-B4B448419896} = {C6CC69A5-E133-4146-BA9B-B4B448419896} + {64DFE3A7-EC39-43BC-B79D-90C572D76BEF} = {64DFE3A7-EC39-43BC-B79D-90C572D76BEF} + {A32201A8-0255-4863-97B8-4A569C18C624} = {A32201A8-0255-4863-97B8-4A569C18C624} + {885A77A9-11CA-4313-B60C-E90B3A1940B6} = {885A77A9-11CA-4313-B60C-E90B3A1940B6} + {0DF4C6AB-6381-4FA1-9AED-F954141DD046} = {0DF4C6AB-6381-4FA1-9AED-F954141DD046} + {430EC1AE-7E25-4039-A83E-09F956285BAC} = {430EC1AE-7E25-4039-A83E-09F956285BAC} + {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7} = {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7} + {79A647BA-E0B7-49E1-85CF-2087EC0CE096} = {79A647BA-E0B7-49E1-85CF-2087EC0CE096} + {A7A75DBE-34C6-4A64-9238-2A284A8AB456} = {A7A75DBE-34C6-4A64-9238-2A284A8AB456} + {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F} = {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F} + {55C8DDC2-0675-4977-A18F-166109D89F4B} = {55C8DDC2-0675-4977-A18F-166109D89F4B} + {92BC9EC6-F01A-48D3-915E-A7B54700FE11} = {92BC9EC6-F01A-48D3-915E-A7B54700FE11} + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1} = {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1} + {C55760CC-3A50-47D7-8605-FD73564AB3E9} = {C55760CC-3A50-47D7-8605-FD73564AB3E9} + {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5} = {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5} + {C878C7D1-E336-4668-B682-088C6B0A18D7} = {C878C7D1-E336-4668-B682-088C6B0A18D7} + {DC9938D5-9D10-4B7D-9E91-507AABBAF924} = {DC9938D5-9D10-4B7D-9E91-507AABBAF924} + {B96E87D6-321A-4A30-B157-899E3B894019} = {B96E87D6-321A-4A30-B157-899E3B894019} + {4C405BDB-4D4F-4466-A62A-16D59DBD0076} = {4C405BDB-4D4F-4466-A62A-16D59DBD0076} + {1D3203DE-A44B-41DD-AC7E-CCB724B6E954} = {1D3203DE-A44B-41DD-AC7E-CCB724B6E954} + {A49383E6-2CE6-49E2-86EC-5074AF793B35} = {A49383E6-2CE6-49E2-86EC-5074AF793B35} + {032566E8-A751-4863-89E3-CEC33703B3C1} = {032566E8-A751-4863-89E3-CEC33703B3C1} + {210889EC-9A23-450C-98F6-B353B73F3457} = {210889EC-9A23-450C-98F6-B353B73F3457} + {E44AE4EE-B215-4C60-91B9-9D3D99A71657} = {E44AE4EE-B215-4C60-91B9-9D3D99A71657} + {F7AFF9EF-7126-4378-9405-3F96B151258C} = {F7AFF9EF-7126-4378-9405-3F96B151258C} + {71C4EDF1-4301-4A25-BE09-69CD54A5037C} = {71C4EDF1-4301-4A25-BE09-69CD54A5037C} + {A47A0DF4-F080-42B4-BC3B-865A02387089} = {A47A0DF4-F080-42B4-BC3B-865A02387089} + {710933F4-5E7C-4EA2-9B3B-91281A933562} = {710933F4-5E7C-4EA2-9B3B-91281A933562} + {888D68F8-40F4-43DB-B786-53EA906517F3} = {888D68F8-40F4-43DB-B786-53EA906517F3} + {C0DE0FFD-88EC-4B75-A621-042B101A5278} = {C0DE0FFD-88EC-4B75-A621-042B101A5278} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plClientKey", "..\plClientKey\plClientKey.vcproj", "{CE472632-E2F1-446C-ABAB-1CE5BD9F367B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plCompression", "..\..\PubUtilLib\plCompression\plCompression.vcproj", "{EF33BFC0-C38B-4BA6-B5B7-E7F90323956F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plContainer", "..\..\PubUtilLib\plContainer\plContainer.vcproj", "{6AB4BC2A-F27B-4614-90FA-58F10CD71F8E}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plDrawable", "..\..\PubUtilLib\plDrawable\plDrawable.vcproj", "{1E924D22-D5D8-4042-8A0F-F75266A0B3B6}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plFile", "..\..\PubUtilLib\plFile\plFile.vcproj", "{677C542F-9027-418D-9D9E-2D526E9FCD09}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plGImage", "..\..\PubUtilLib\plGImage\plGImage.vcproj", "{0DF4C6AB-6381-4FA1-9AED-F954141DD046}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plGLight", "..\..\PubUtilLib\plGLight\plGLight.vcproj", "{6F09B62B-34FD-4A60-9030-DB8FFACA66BA}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plInputCore", "..\..\PubUtilLib\plInputCore\plInputCore.vcproj", "{C55760CC-3A50-47D7-8605-FD73564AB3E9}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plInterp", "..\..\PubUtilLib\plInterp\plInterp.vcproj", "{4576792A-58CA-478A-9CB1-FBE05E559C2F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plIntersect", "..\..\PubUtilLib\plIntersect\plIntersect.vcproj", "{42503779-D12B-435D-9EA8-28D9FE0C9B08}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plJPEG", "..\..\PubUtilLib\plJPEG\plJPEG.vcproj", "{ECFC7F74-12DC-40FE-B201-9D5002F53721}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plMath", "..\..\PubUtilLib\plMath\plMath.vcproj", "{F83B1689-144D-4183-A2A5-0BE3D1892C49}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plMessage", "..\..\PubUtilLib\plMessage\plMessage.vcproj", "{DC9938D5-9D10-4B7D-9E91-507AABBAF924}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plModifier", "..\..\PubUtilLib\plModifier\plModifier.vcproj", "{5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetClient", "..\..\PubUtilLib\plNetClient\plNetClient.vcproj", "{A49383E6-2CE6-49E2-86EC-5074AF793B35}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetClientComm", "..\..\PubUtilLib\plNetClientComm\plNetClientComm.vcproj", "{C0FC318E-8E10-488C-939F-84C97D9B5972}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetClientRecorder", "..\..\PubUtilLib\plNetClientRecorder\plNetClientRecorder.vcproj", "{A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetCommon", "..\..\PubUtilLib\plNetCommon\plNetCommon.vcproj", "{A58C3644-55E7-44EB-BE85-0D53773B8BA9}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetMessage", "..\..\PubUtilLib\plNetMessage\plNetMessage.vcproj", "{B7B9B275-1059-40FE-9502-0CF42B5BA32F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetTransport", "..\..\PubUtilLib\plNetTransport\plNetTransport.vcproj", "{210889EC-9A23-450C-98F6-B353B73F3457}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plParticleSystem", "..\..\PubUtilLib\plParticleSystem\plParticleSystem.vcproj", "{F7AFF9EF-7126-4378-9405-3F96B151258C}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plPhysical", "..\..\PubUtilLib\plPhysical\plPhysical.vcproj", "{A8350709-C3E0-4B19-9218-D1B23C6E97DC}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plPipeline", "..\..\PubUtilLib\plPipeline\plPipeline.vcproj", "{92BC9EC6-F01A-48D3-915E-A7B54700FE11}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plProgressMgr", "..\..\PubUtilLib\plProgressMgr\plProgressMgr.vcproj", "{65A72F3C-FE82-4014-81A3-4647938F40AA}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plResMgr", "..\..\PubUtilLib\plResMgr\plResMgr.vcproj", "{4009FA12-FA43-4ED8-B69E-8FBAF4C43B30}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plSDL", "..\..\pubutillib\plSDL\plSDL.vcproj", "{C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plScene", "..\..\PubUtilLib\plScene\plScene.vcproj", "{E44AE4EE-B215-4C60-91B9-9D3D99A71657}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plSockets", "..\..\PubUtilLib\plSockets\plSockets.vcproj", "{9CDD8836-9B27-4AB8-82DB-8200308CE38A}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plStatGather", "..\..\PubUtilLib\plStatGather\plStatGather.vcproj", "{710933F4-5E7C-4EA2-9B3B-91281A933562}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plStatusLog", "..\..\PubUtilLib\plStatusLog\plStatusLog.vcproj", "{49365412-EA0C-4851-9FA9-2E1799ED7A82}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plStreamLogger", "..\..\PubUtilLib\plStreamLogger\plStreamLogger.vcproj", "{0BC97654-C80F-4870-B56B-56DDCEB09B59}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plSurface", "..\..\PubUtilLib\plSurface\plSurface.vcproj", "{C878C7D1-E336-4668-B682-088C6B0A18D7}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plTransform", "..\..\PubUtilLib\plTransform\plTransform.vcproj", "{54AC8E94-D1FE-4533-9E3A-B35C143CDD93}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plUnifiedTime", "..\..\PubUtilLib\plUnifiedTime\plUnifiedTime.vcproj", "{032566E8-A751-4863-89E3-CEC33703B3C1}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plVault", "..\..\PUBUTILLIB\plVault\plVault.vcproj", "{35BE9A13-6922-40D5-B0F0-7D65AF4BAA53}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnAddrInfo", "..\..\NucleusLib\pnAddrInfo\pnAddrInfo.vcproj", "{64DFE3A7-EC39-43BC-B79D-90C572D76BEF}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnDispatch", "..\..\NucleusLib\pnDispatch\pnDispatch.vcproj", "{4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnFactory", "..\..\NucleusLib\pnFactory\pnFactory.vcproj", "{1D3203DE-A44B-41DD-AC7E-CCB724B6E954}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnInputCore", "..\..\NucleusLib\pnInputCore\pnInputCore.vcproj", "{C3A1070E-8C00-4CE5-B4E8-69660032DB71}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnKeyedObject", "..\..\NucleusLib\pnKeyedObject\pnKeyedObject.vcproj", "{46B6473A-BD67-4DB8-9479-3C26B8626CFA}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnMessage", "..\..\NucleusLib\pnMessage\pnMessage.vcproj", "{71C4EDF1-4301-4A25-BE09-69CD54A5037C}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnModifier", "..\..\NucleusLib\pnModifier\pnModifier.vcproj", "{6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnNetCommon", "..\..\NucleusLib\pnNetCommon\pnNetCommon.vcproj", "{C0DE0FFD-88EC-4B75-A621-042B101A5278}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnNucleusInc", "..\..\NucleusLib\pnNucleusInc\pnNucleusInc.vcproj", "{56C50721-716D-468B-9EEB-750DA8C50EF7}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnSceneObject", "..\..\NucleusLib\pnSceneObject\pnSceneObject.vcproj", "{663AB605-0B37-41F6-A015-7219D9B5F84B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnTimer", "..\..\NucleusLib\pnTimer\pnTimer.vcproj", "{885A77A9-11CA-4313-B60C-E90B3A1940B6}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pythoncore_static", "..\..\..\..\SDKs\XPlatform\Cypython-2.3.3\PCbuild\pythoncore_static.vcproj", "{A32201A8-0255-4863-97B8-4A569C18C624}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoreLibExe", "..\..\CoreLibExe\CoreLibExe.vcproj", "{4054C94F-866A-4AA7-874B-2AFCFEF23A71}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnAsyncCore", "..\..\NucleusLib\pnAsyncCore\pnAsyncCore.vcproj", "{4E991726-D69F-4786-A873-DF37FB4DABF9}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnAsyncCoreExe", "..\..\NucleusLib\pnAsyncCoreExe\pnAsyncCoreExe.vcproj", "{55C8DDC2-0675-4977-A18F-166109D89F4B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnUtils", "..\..\NucleusLib\pnUtils\pnUtils.vcproj", "{89316659-F4A9-4E92-8200-C36288A61B9B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnUtilsExe", "..\..\NucleusLib\pnUtilsExe\pnUtilsExe.vcproj", "{08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnNetBase", "..\..\NucleusLib\pnNetBase\pnNetBase.vcproj", "{AB45BA83-4921-4A3C-80CF-1984D49ACA0D}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnNetCli", "..\..\NucleusLib\pnNetCli\pnNetCli.vcproj", "{6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnProduct", "..\..\NucleusLib\pnProduct\pnProduct.vcproj", "{CA208971-6C77-47F6-AA4B-FB6ECC071132}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plNetGameLib", "..\..\PubUtilLib\plNetGameLib\plNetGameLib.vcproj", "{CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnNetProtocol", "..\..\NucleusLib\pnNetProtocol\pnNetProtocol.vcproj", "{32EC294E-2A45-4627-B5C4-B5CD236C2674}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plEncryption", "..\..\PubUtilLib\plEncryption\plEncryption.vcproj", "{B96E87D6-321A-4A30-B157-899E3B894019}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plUUID", "..\..\PubUtilLib\plUUID\plUUID.vcproj", "{BBDF8302-BE9C-43EC-AAF8-912E601515B3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnIni", "..\..\NucleusLib\pnIni\pnIni.vcproj", "{430EC1AE-7E25-4039-A83E-09F956285BAC}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnIniExe", "..\..\NucleusLib\pnIniExe\pnIniExe.vcproj", "{28E5E146-D581-4492-9DD7-D56247A9F027}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfLocalizationMgr", "..\..\FeatureLib\pfLocalizationMgr\pfLocalizationMgr.vcproj", "{8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfSecurePreloader", "..\..\FeatureLib\pfSecurePreloader\pfSecurePreloader.vcproj", "{C28B9838-04AE-4EBD-A93F-A94A64230887}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfCCR", "..\..\FeatureLib\pfCCR\pfCCR.vcproj", "{1599855E-CC20-4C3A-A382-5290C40BE08E}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plPhysX", "..\..\PubUtilLib\plPhysX\plPhysX.vcproj", "{5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnGameMgr", "..\..\NucleusLib\pnGameMgr\pnGameMgr.vcproj", "{D97E0E20-0502-4E63-BD05-41B42EED695F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfGameMgr", "..\..\FeatureLib\pfGameMgr\pfGameMgr.vcproj", "{7D83F872-F06C-4D42-A8F2-D77DC22C5981}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exe_NetDiag", "..\NetDiag\NetDiag.vcproj", "{E5E4CE2B-484C-4250-9F8C-F8D5D4570AD3}" + ProjectSection(ProjectDependencies) = postProject + {4E991726-D69F-4786-A873-DF37FB4DABF9} = {4E991726-D69F-4786-A873-DF37FB4DABF9} + {8444A94C-FD1E-47D2-B978-B42ED198F234} = {8444A94C-FD1E-47D2-B978-B42ED198F234} + {32EC294E-2A45-4627-B5C4-B5CD236C2674} = {32EC294E-2A45-4627-B5C4-B5CD236C2674} + {4054C94F-866A-4AA7-874B-2AFCFEF23A71} = {4054C94F-866A-4AA7-874B-2AFCFEF23A71} + {89316659-F4A9-4E92-8200-C36288A61B9B} = {89316659-F4A9-4E92-8200-C36288A61B9B} + {A10FCF60-7425-48D7-9153-CB9D8AD94C8F} = {A10FCF60-7425-48D7-9153-CB9D8AD94C8F} + {CA208971-6C77-47F6-AA4B-FB6ECC071132} = {CA208971-6C77-47F6-AA4B-FB6ECC071132} + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D} = {AB45BA83-4921-4A3C-80CF-1984D49ACA0D} + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F} = {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F} + {55C8DDC2-0675-4977-A18F-166109D89F4B} = {55C8DDC2-0675-4977-A18F-166109D89F4B} + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1} = {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1} + {FB47BBFF-239D-46B3-98C6-46C3A994B5B7} = {FB47BBFF-239D-46B3-98C6-46C3A994B5B7} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnNetDiag", "..\..\NucleusLib\pnNetDiag\pnNetDiag.vcproj", "{FB47BBFF-239D-46B3-98C6-46C3A994B5B7}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnMail", "..\..\NucleusLib\pnMail\pnMail.vcproj", "{A10FCF60-7425-48D7-9153-CB9D8AD94C8F}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plPageOptimizer", "..\plPageOptimizer\plPageOptimizer.vcproj", "{EED64223-B75A-4476-9F16-6ACE587158CD}" + ProjectSection(ProjectDependencies) = postProject + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED} = {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED} + {BBDF8302-BE9C-43EC-AAF8-912E601515B3} = {BBDF8302-BE9C-43EC-AAF8-912E601515B3} + {663AB605-0B37-41F6-A015-7219D9B5F84B} = {663AB605-0B37-41F6-A015-7219D9B5F84B} + {DE62CF05-8E3E-40A4-967D-330216C2D2ED} = {DE62CF05-8E3E-40A4-967D-330216C2D2ED} + {DA08E306-B61E-4C1D-90F3-194C70FA7027} = {DA08E306-B61E-4C1D-90F3-194C70FA7027} + {A8350709-C3E0-4B19-9218-D1B23C6E97DC} = {A8350709-C3E0-4B19-9218-D1B23C6E97DC} + {C3A1070E-8C00-4CE5-B4E8-69660032DB71} = {C3A1070E-8C00-4CE5-B4E8-69660032DB71} + {49365412-EA0C-4851-9FA9-2E1799ED7A82} = {49365412-EA0C-4851-9FA9-2E1799ED7A82} + {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30} = {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30} + {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53} = {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53} + {D97E0E20-0502-4E63-BD05-41B42EED695F} = {D97E0E20-0502-4E63-BD05-41B42EED695F} + {56C50721-716D-468B-9EEB-750DA8C50EF7} = {56C50721-716D-468B-9EEB-750DA8C50EF7} + {88DB3B21-DBD4-4325-910B-F9256356F802} = {88DB3B21-DBD4-4325-910B-F9256356F802} + {1E924D22-D5D8-4042-8A0F-F75266A0B3B6} = {1E924D22-D5D8-4042-8A0F-F75266A0B3B6} + {58853F23-308E-4698-AAA6-795ABF98790C} = {58853F23-308E-4698-AAA6-795ABF98790C} + {4E991726-D69F-4786-A873-DF37FB4DABF9} = {4E991726-D69F-4786-A873-DF37FB4DABF9} + {4576792A-58CA-478A-9CB1-FBE05E559C2F} = {4576792A-58CA-478A-9CB1-FBE05E559C2F} + {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E} = {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E} + {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF} = {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF} + {6F09B62B-34FD-4A60-9030-DB8FFACA66BA} = {6F09B62B-34FD-4A60-9030-DB8FFACA66BA} + {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E} = {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E} + {677C542F-9027-418D-9D9E-2D526E9FCD09} = {677C542F-9027-418D-9D9E-2D526E9FCD09} + {B24DB433-EA09-45F3-9296-38FB6AABA9E2} = {B24DB433-EA09-45F3-9296-38FB6AABA9E2} + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA} = {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA} + {6CB8A937-3C28-447E-B753-081E275E58FF} = {6CB8A937-3C28-447E-B753-081E275E58FF} + {46B6473A-BD67-4DB8-9479-3C26B8626CFA} = {46B6473A-BD67-4DB8-9479-3C26B8626CFA} + {65A72F3C-FE82-4014-81A3-4647938F40AA} = {65A72F3C-FE82-4014-81A3-4647938F40AA} + {A58C3644-55E7-44EB-BE85-0D53773B8BA9} = {A58C3644-55E7-44EB-BE85-0D53773B8BA9} + {28E5E146-D581-4492-9DD7-D56247A9F027} = {28E5E146-D581-4492-9DD7-D56247A9F027} + {4FD2804C-465E-40F1-B766-48A1632FEEA7} = {4FD2804C-465E-40F1-B766-48A1632FEEA7} + {8444A94C-FD1E-47D2-B978-B42ED198F234} = {8444A94C-FD1E-47D2-B978-B42ED198F234} + {32EC294E-2A45-4627-B5C4-B5CD236C2674} = {32EC294E-2A45-4627-B5C4-B5CD236C2674} + {A200654F-E610-4959-B3D2-D2AFD6E078CE} = {A200654F-E610-4959-B3D2-D2AFD6E078CE} + {4054C94F-866A-4AA7-874B-2AFCFEF23A71} = {4054C94F-866A-4AA7-874B-2AFCFEF23A71} + {89316659-F4A9-4E92-8200-C36288A61B9B} = {89316659-F4A9-4E92-8200-C36288A61B9B} + {1599855E-CC20-4C3A-A382-5290C40BE08E} = {1599855E-CC20-4C3A-A382-5290C40BE08E} + {A10FCF60-7425-48D7-9153-CB9D8AD94C8F} = {A10FCF60-7425-48D7-9153-CB9D8AD94C8F} + {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477} = {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477} + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3} = {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3} + {CA208971-6C77-47F6-AA4B-FB6ECC071132} = {CA208971-6C77-47F6-AA4B-FB6ECC071132} + {7D83F872-F06C-4D42-A8F2-D77DC22C5981} = {7D83F872-F06C-4D42-A8F2-D77DC22C5981} + {ECFC7F74-12DC-40FE-B201-9D5002F53721} = {ECFC7F74-12DC-40FE-B201-9D5002F53721} + {B7B9B275-1059-40FE-9502-0CF42B5BA32F} = {B7B9B275-1059-40FE-9502-0CF42B5BA32F} + {96755478-80D1-4FF6-AF80-5013209C5FD6} = {96755478-80D1-4FF6-AF80-5013209C5FD6} + {42503779-D12B-435D-9EA8-28D9FE0C9B08} = {42503779-D12B-435D-9EA8-28D9FE0C9B08} + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D} = {AB45BA83-4921-4A3C-80CF-1984D49ACA0D} + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F} = {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F} + {86881785-432A-4AA3-A664-43A9E8FD9BA0} = {86881785-432A-4AA3-A664-43A9E8FD9BA0} + {F83B1689-144D-4183-A2A5-0BE3D1892C49} = {F83B1689-144D-4183-A2A5-0BE3D1892C49} + {C0FC318E-8E10-488C-939F-84C97D9B5972} = {C0FC318E-8E10-488C-939F-84C97D9B5972} + {2D715490-C219-4952-ABE9-3D23FA8775EB} = {2D715490-C219-4952-ABE9-3D23FA8775EB} + {54AC8E94-D1FE-4533-9E3A-B35C143CDD93} = {54AC8E94-D1FE-4533-9E3A-B35C143CDD93} + {07726299-2609-4461-90BC-FF5A742766D5} = {07726299-2609-4461-90BC-FF5A742766D5} + {3E917DA0-6B4B-4C82-83DB-C246311458A9} = {3E917DA0-6B4B-4C82-83DB-C246311458A9} + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8} = {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8} + {5D217AA3-6A2C-4718-9985-4C9037F21733} = {5D217AA3-6A2C-4718-9985-4C9037F21733} + {C6CC69A5-E133-4146-BA9B-B4B448419896} = {C6CC69A5-E133-4146-BA9B-B4B448419896} + {64DFE3A7-EC39-43BC-B79D-90C572D76BEF} = {64DFE3A7-EC39-43BC-B79D-90C572D76BEF} + {A32201A8-0255-4863-97B8-4A569C18C624} = {A32201A8-0255-4863-97B8-4A569C18C624} + {885A77A9-11CA-4313-B60C-E90B3A1940B6} = {885A77A9-11CA-4313-B60C-E90B3A1940B6} + {0DF4C6AB-6381-4FA1-9AED-F954141DD046} = {0DF4C6AB-6381-4FA1-9AED-F954141DD046} + {430EC1AE-7E25-4039-A83E-09F956285BAC} = {430EC1AE-7E25-4039-A83E-09F956285BAC} + {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7} = {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7} + {A7A75DBE-34C6-4A64-9238-2A284A8AB456} = {A7A75DBE-34C6-4A64-9238-2A284A8AB456} + {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F} = {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F} + {55C8DDC2-0675-4977-A18F-166109D89F4B} = {55C8DDC2-0675-4977-A18F-166109D89F4B} + {92BC9EC6-F01A-48D3-915E-A7B54700FE11} = {92BC9EC6-F01A-48D3-915E-A7B54700FE11} + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1} = {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1} + {C55760CC-3A50-47D7-8605-FD73564AB3E9} = {C55760CC-3A50-47D7-8605-FD73564AB3E9} + {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5} = {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5} + {C878C7D1-E336-4668-B682-088C6B0A18D7} = {C878C7D1-E336-4668-B682-088C6B0A18D7} + {DC9938D5-9D10-4B7D-9E91-507AABBAF924} = {DC9938D5-9D10-4B7D-9E91-507AABBAF924} + {B96E87D6-321A-4A30-B157-899E3B894019} = {B96E87D6-321A-4A30-B157-899E3B894019} + {1D3203DE-A44B-41DD-AC7E-CCB724B6E954} = {1D3203DE-A44B-41DD-AC7E-CCB724B6E954} + {A49383E6-2CE6-49E2-86EC-5074AF793B35} = {A49383E6-2CE6-49E2-86EC-5074AF793B35} + {032566E8-A751-4863-89E3-CEC33703B3C1} = {032566E8-A751-4863-89E3-CEC33703B3C1} + {210889EC-9A23-450C-98F6-B353B73F3457} = {210889EC-9A23-450C-98F6-B353B73F3457} + {E44AE4EE-B215-4C60-91B9-9D3D99A71657} = {E44AE4EE-B215-4C60-91B9-9D3D99A71657} + {F7AFF9EF-7126-4378-9405-3F96B151258C} = {F7AFF9EF-7126-4378-9405-3F96B151258C} + {71C4EDF1-4301-4A25-BE09-69CD54A5037C} = {71C4EDF1-4301-4A25-BE09-69CD54A5037C} + {A47A0DF4-F080-42B4-BC3B-865A02387089} = {A47A0DF4-F080-42B4-BC3B-865A02387089} + {888D68F8-40F4-43DB-B786-53EA906517F3} = {888D68F8-40F4-43DB-B786-53EA906517F3} + {C0DE0FFD-88EC-4B75-A621-042B101A5278} = {C0DE0FFD-88EC-4B75-A621-042B101A5278} + {FB47BBFF-239D-46B3-98C6-46C3A994B5B7} = {FB47BBFF-239D-46B3-98C6-46C3A994B5B7} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnSimpleNet", "..\..\NucleusLib\pnSimpleNet\pnSimpleNet.vcproj", "{D20A0F2B-6822-4EE7-88C0-37ED22E38583}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnCsrNet", "..\..\NucleusLib\pnCsrNet\pnCsrNet.vcproj", "{4AD6934C-977D-4A01-9FAA-D2C32D96DD0B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfCsrSrv", "..\..\FeatureLib\pfCsrSrv\pfCsrSrv.vcproj", "{E710BB2B-8E1E-434E-8480-10E3AF6DF53B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfGameScoreMgr", "..\..\FeatureLib\pfGameScoreMgr\pfGameScoreMgr.vcproj", "{2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plCyDebug", "..\CyPythonIDE\plCyDebug\plCyDebug.vcproj", "{DE62CF05-8E3E-40A4-967D-330216C2D2ED}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Debug_Server = Debug_Server + Release = Release + Release_Server = Release_Server + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {0736D624-C5F6-4B44-96FC-B27C2841D2F9}.Debug.ActiveCfg = Debug|Win32 + {0736D624-C5F6-4B44-96FC-B27C2841D2F9}.Debug.Build.0 = Debug|Win32 + {0736D624-C5F6-4B44-96FC-B27C2841D2F9}.Debug_Server.ActiveCfg = Debug|Win32 + {0736D624-C5F6-4B44-96FC-B27C2841D2F9}.Debug_Server.Build.0 = Debug|Win32 + {0736D624-C5F6-4B44-96FC-B27C2841D2F9}.Release.ActiveCfg = Release|Win32 + {0736D624-C5F6-4B44-96FC-B27C2841D2F9}.Release.Build.0 = Release|Win32 + {0736D624-C5F6-4B44-96FC-B27C2841D2F9}.Release_Server.ActiveCfg = Release|Win32 + {0736D624-C5F6-4B44-96FC-B27C2841D2F9}.Release_Server.Build.0 = Release|Win32 + {8444A94C-FD1E-47D2-B978-B42ED198F234}.Debug.ActiveCfg = Debug|Win32 + {8444A94C-FD1E-47D2-B978-B42ED198F234}.Debug.Build.0 = Debug|Win32 + {8444A94C-FD1E-47D2-B978-B42ED198F234}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {8444A94C-FD1E-47D2-B978-B42ED198F234}.Debug_Server.Build.0 = Debug_Server|Win32 + {8444A94C-FD1E-47D2-B978-B42ED198F234}.Release.ActiveCfg = Release|Win32 + {8444A94C-FD1E-47D2-B978-B42ED198F234}.Release.Build.0 = Release|Win32 + {8444A94C-FD1E-47D2-B978-B42ED198F234}.Release_Server.ActiveCfg = Release_Server|Win32 + {8444A94C-FD1E-47D2-B978-B42ED198F234}.Release_Server.Build.0 = Release_Server|Win32 + {0182EF47-D0B1-4886-B4FC-F126C638FD3F}.Debug.ActiveCfg = Debug|Win32 + {0182EF47-D0B1-4886-B4FC-F126C638FD3F}.Debug.Build.0 = Debug|Win32 + {0182EF47-D0B1-4886-B4FC-F126C638FD3F}.Debug_Server.ActiveCfg = Debug|Win32 + {0182EF47-D0B1-4886-B4FC-F126C638FD3F}.Debug_Server.Build.0 = Debug|Win32 + {0182EF47-D0B1-4886-B4FC-F126C638FD3F}.Release.ActiveCfg = Release|Win32 + {0182EF47-D0B1-4886-B4FC-F126C638FD3F}.Release.Build.0 = Release|Win32 + {0182EF47-D0B1-4886-B4FC-F126C638FD3F}.Release_Server.ActiveCfg = Release|Win32 + {0182EF47-D0B1-4886-B4FC-F126C638FD3F}.Release_Server.Build.0 = Release|Win32 + {3E917DA0-6B4B-4C82-83DB-C246311458A9}.Debug.ActiveCfg = Debug|Win32 + {3E917DA0-6B4B-4C82-83DB-C246311458A9}.Debug.Build.0 = Debug|Win32 + {3E917DA0-6B4B-4C82-83DB-C246311458A9}.Debug_Server.ActiveCfg = Debug|Win32 + {3E917DA0-6B4B-4C82-83DB-C246311458A9}.Debug_Server.Build.0 = Debug|Win32 + {3E917DA0-6B4B-4C82-83DB-C246311458A9}.Release.ActiveCfg = Release|Win32 + {3E917DA0-6B4B-4C82-83DB-C246311458A9}.Release.Build.0 = Release|Win32 + {3E917DA0-6B4B-4C82-83DB-C246311458A9}.Release_Server.ActiveCfg = Release|Win32 + {3E917DA0-6B4B-4C82-83DB-C246311458A9}.Release_Server.Build.0 = Release|Win32 + {86881785-432A-4AA3-A664-43A9E8FD9BA0}.Debug.ActiveCfg = Debug|Win32 + {86881785-432A-4AA3-A664-43A9E8FD9BA0}.Debug.Build.0 = Debug|Win32 + {86881785-432A-4AA3-A664-43A9E8FD9BA0}.Debug_Server.ActiveCfg = Debug|Win32 + {86881785-432A-4AA3-A664-43A9E8FD9BA0}.Debug_Server.Build.0 = Debug|Win32 + {86881785-432A-4AA3-A664-43A9E8FD9BA0}.Release.ActiveCfg = Release|Win32 + {86881785-432A-4AA3-A664-43A9E8FD9BA0}.Release.Build.0 = Release|Win32 + {86881785-432A-4AA3-A664-43A9E8FD9BA0}.Release_Server.ActiveCfg = Release|Win32 + {86881785-432A-4AA3-A664-43A9E8FD9BA0}.Release_Server.Build.0 = Release|Win32 + {58853F23-308E-4698-AAA6-795ABF98790C}.Debug.ActiveCfg = Debug|Win32 + {58853F23-308E-4698-AAA6-795ABF98790C}.Debug.Build.0 = Debug|Win32 + {58853F23-308E-4698-AAA6-795ABF98790C}.Debug_Server.ActiveCfg = Debug|Win32 + {58853F23-308E-4698-AAA6-795ABF98790C}.Debug_Server.Build.0 = Debug|Win32 + {58853F23-308E-4698-AAA6-795ABF98790C}.Release.ActiveCfg = Release|Win32 + {58853F23-308E-4698-AAA6-795ABF98790C}.Release.Build.0 = Release|Win32 + {58853F23-308E-4698-AAA6-795ABF98790C}.Release_Server.ActiveCfg = Release|Win32 + {58853F23-308E-4698-AAA6-795ABF98790C}.Release_Server.Build.0 = Release|Win32 + {DA08E306-B61E-4C1D-90F3-194C70FA7027}.Debug.ActiveCfg = Debug|Win32 + {DA08E306-B61E-4C1D-90F3-194C70FA7027}.Debug.Build.0 = Debug|Win32 + {DA08E306-B61E-4C1D-90F3-194C70FA7027}.Debug_Server.ActiveCfg = Debug|Win32 + {DA08E306-B61E-4C1D-90F3-194C70FA7027}.Debug_Server.Build.0 = Debug|Win32 + {DA08E306-B61E-4C1D-90F3-194C70FA7027}.Release.ActiveCfg = Release|Win32 + {DA08E306-B61E-4C1D-90F3-194C70FA7027}.Release.Build.0 = Release|Win32 + {DA08E306-B61E-4C1D-90F3-194C70FA7027}.Release_Server.ActiveCfg = Release|Win32 + {DA08E306-B61E-4C1D-90F3-194C70FA7027}.Release_Server.Build.0 = Release|Win32 + {6CB8A937-3C28-447E-B753-081E275E58FF}.Debug.ActiveCfg = Debug|Win32 + {6CB8A937-3C28-447E-B753-081E275E58FF}.Debug.Build.0 = Debug|Win32 + {6CB8A937-3C28-447E-B753-081E275E58FF}.Debug_Server.ActiveCfg = Debug|Win32 + {6CB8A937-3C28-447E-B753-081E275E58FF}.Debug_Server.Build.0 = Debug|Win32 + {6CB8A937-3C28-447E-B753-081E275E58FF}.Release.ActiveCfg = Release|Win32 + {6CB8A937-3C28-447E-B753-081E275E58FF}.Release.Build.0 = Release|Win32 + {6CB8A937-3C28-447E-B753-081E275E58FF}.Release_Server.ActiveCfg = Release|Win32 + {6CB8A937-3C28-447E-B753-081E275E58FF}.Release_Server.Build.0 = Release|Win32 + {5D217AA3-6A2C-4718-9985-4C9037F21733}.Debug.ActiveCfg = Debug|Win32 + {5D217AA3-6A2C-4718-9985-4C9037F21733}.Debug.Build.0 = Debug|Win32 + {5D217AA3-6A2C-4718-9985-4C9037F21733}.Debug_Server.ActiveCfg = Debug|Win32 + {5D217AA3-6A2C-4718-9985-4C9037F21733}.Debug_Server.Build.0 = Debug|Win32 + {5D217AA3-6A2C-4718-9985-4C9037F21733}.Release.ActiveCfg = Release|Win32 + {5D217AA3-6A2C-4718-9985-4C9037F21733}.Release.Build.0 = Release|Win32 + {5D217AA3-6A2C-4718-9985-4C9037F21733}.Release_Server.ActiveCfg = Release|Win32 + {5D217AA3-6A2C-4718-9985-4C9037F21733}.Release_Server.Build.0 = Release|Win32 + {79A647BA-E0B7-49E1-85CF-2087EC0CE096}.Debug.ActiveCfg = Debug|Win32 + {79A647BA-E0B7-49E1-85CF-2087EC0CE096}.Debug.Build.0 = Debug|Win32 + {79A647BA-E0B7-49E1-85CF-2087EC0CE096}.Debug_Server.ActiveCfg = Debug|Win32 + {79A647BA-E0B7-49E1-85CF-2087EC0CE096}.Debug_Server.Build.0 = Debug|Win32 + {79A647BA-E0B7-49E1-85CF-2087EC0CE096}.Release.ActiveCfg = Release|Win32 + {79A647BA-E0B7-49E1-85CF-2087EC0CE096}.Release.Build.0 = Release|Win32 + {79A647BA-E0B7-49E1-85CF-2087EC0CE096}.Release_Server.ActiveCfg = Release|Win32 + {79A647BA-E0B7-49E1-85CF-2087EC0CE096}.Release_Server.Build.0 = Release|Win32 + {96755478-80D1-4FF6-AF80-5013209C5FD6}.Debug.ActiveCfg = Debug|Win32 + {96755478-80D1-4FF6-AF80-5013209C5FD6}.Debug.Build.0 = Debug|Win32 + {96755478-80D1-4FF6-AF80-5013209C5FD6}.Debug_Server.ActiveCfg = Debug|Win32 + {96755478-80D1-4FF6-AF80-5013209C5FD6}.Debug_Server.Build.0 = Debug|Win32 + {96755478-80D1-4FF6-AF80-5013209C5FD6}.Release.ActiveCfg = Release|Win32 + {96755478-80D1-4FF6-AF80-5013209C5FD6}.Release.Build.0 = Release|Win32 + {96755478-80D1-4FF6-AF80-5013209C5FD6}.Release_Server.ActiveCfg = Release|Win32 + {96755478-80D1-4FF6-AF80-5013209C5FD6}.Release_Server.Build.0 = Release|Win32 + {C6CC69A5-E133-4146-BA9B-B4B448419896}.Debug.ActiveCfg = Debug|Win32 + {C6CC69A5-E133-4146-BA9B-B4B448419896}.Debug.Build.0 = Debug|Win32 + {C6CC69A5-E133-4146-BA9B-B4B448419896}.Debug_Server.ActiveCfg = Debug|Win32 + {C6CC69A5-E133-4146-BA9B-B4B448419896}.Debug_Server.Build.0 = Debug|Win32 + {C6CC69A5-E133-4146-BA9B-B4B448419896}.Release.ActiveCfg = Release|Win32 + {C6CC69A5-E133-4146-BA9B-B4B448419896}.Release.Build.0 = Release|Win32 + {C6CC69A5-E133-4146-BA9B-B4B448419896}.Release_Server.ActiveCfg = Release|Win32 + {C6CC69A5-E133-4146-BA9B-B4B448419896}.Release_Server.Build.0 = Release|Win32 + {B24DB433-EA09-45F3-9296-38FB6AABA9E2}.Debug.ActiveCfg = Debug|Win32 + {B24DB433-EA09-45F3-9296-38FB6AABA9E2}.Debug.Build.0 = Debug|Win32 + {B24DB433-EA09-45F3-9296-38FB6AABA9E2}.Debug_Server.ActiveCfg = Debug|Win32 + {B24DB433-EA09-45F3-9296-38FB6AABA9E2}.Debug_Server.Build.0 = Debug|Win32 + {B24DB433-EA09-45F3-9296-38FB6AABA9E2}.Release.ActiveCfg = Release|Win32 + {B24DB433-EA09-45F3-9296-38FB6AABA9E2}.Release.Build.0 = Release|Win32 + {B24DB433-EA09-45F3-9296-38FB6AABA9E2}.Release_Server.ActiveCfg = Release|Win32 + {B24DB433-EA09-45F3-9296-38FB6AABA9E2}.Release_Server.Build.0 = Release|Win32 + {A47A0DF4-F080-42B4-BC3B-865A02387089}.Debug.ActiveCfg = Debug|Win32 + {A47A0DF4-F080-42B4-BC3B-865A02387089}.Debug.Build.0 = Debug|Win32 + {A47A0DF4-F080-42B4-BC3B-865A02387089}.Debug_Server.ActiveCfg = Debug|Win32 + {A47A0DF4-F080-42B4-BC3B-865A02387089}.Debug_Server.Build.0 = Debug|Win32 + {A47A0DF4-F080-42B4-BC3B-865A02387089}.Release.ActiveCfg = Release|Win32 + {A47A0DF4-F080-42B4-BC3B-865A02387089}.Release.Build.0 = Release|Win32 + {A47A0DF4-F080-42B4-BC3B-865A02387089}.Release_Server.ActiveCfg = Release|Win32 + {A47A0DF4-F080-42B4-BC3B-865A02387089}.Release_Server.Build.0 = Release|Win32 + {4C405BDB-4D4F-4466-A62A-16D59DBD0076}.Debug.ActiveCfg = Debug|Win32 + {4C405BDB-4D4F-4466-A62A-16D59DBD0076}.Debug.Build.0 = Debug|Win32 + {4C405BDB-4D4F-4466-A62A-16D59DBD0076}.Debug_Server.ActiveCfg = Debug|Win32 + {4C405BDB-4D4F-4466-A62A-16D59DBD0076}.Debug_Server.Build.0 = Debug|Win32 + {4C405BDB-4D4F-4466-A62A-16D59DBD0076}.Release.ActiveCfg = Release|Win32 + {4C405BDB-4D4F-4466-A62A-16D59DBD0076}.Release.Build.0 = Release|Win32 + {4C405BDB-4D4F-4466-A62A-16D59DBD0076}.Release_Server.ActiveCfg = Release|Win32 + {4C405BDB-4D4F-4466-A62A-16D59DBD0076}.Release_Server.Build.0 = Release|Win32 + {88DB3B21-DBD4-4325-910B-F9256356F802}.Debug.ActiveCfg = Debug|Win32 + {88DB3B21-DBD4-4325-910B-F9256356F802}.Debug.Build.0 = Debug|Win32 + {88DB3B21-DBD4-4325-910B-F9256356F802}.Debug_Server.ActiveCfg = Debug|Win32 + {88DB3B21-DBD4-4325-910B-F9256356F802}.Debug_Server.Build.0 = Debug|Win32 + {88DB3B21-DBD4-4325-910B-F9256356F802}.Release.ActiveCfg = Release|Win32 + {88DB3B21-DBD4-4325-910B-F9256356F802}.Release.Build.0 = Release|Win32 + {88DB3B21-DBD4-4325-910B-F9256356F802}.Release_Server.ActiveCfg = Release|Win32 + {88DB3B21-DBD4-4325-910B-F9256356F802}.Release_Server.Build.0 = Release|Win32 + {A200654F-E610-4959-B3D2-D2AFD6E078CE}.Debug.ActiveCfg = Debug|Win32 + {A200654F-E610-4959-B3D2-D2AFD6E078CE}.Debug.Build.0 = Debug|Win32 + {A200654F-E610-4959-B3D2-D2AFD6E078CE}.Debug_Server.ActiveCfg = Debug|Win32 + {A200654F-E610-4959-B3D2-D2AFD6E078CE}.Debug_Server.Build.0 = Debug|Win32 + {A200654F-E610-4959-B3D2-D2AFD6E078CE}.Release.ActiveCfg = Release|Win32 + {A200654F-E610-4959-B3D2-D2AFD6E078CE}.Release.Build.0 = Release|Win32 + {A200654F-E610-4959-B3D2-D2AFD6E078CE}.Release_Server.ActiveCfg = Release|Win32 + {A200654F-E610-4959-B3D2-D2AFD6E078CE}.Release_Server.Build.0 = Release|Win32 + {A7A75DBE-34C6-4A64-9238-2A284A8AB456}.Debug.ActiveCfg = Debug|Win32 + {A7A75DBE-34C6-4A64-9238-2A284A8AB456}.Debug.Build.0 = Debug|Win32 + {A7A75DBE-34C6-4A64-9238-2A284A8AB456}.Debug_Server.ActiveCfg = Debug|Win32 + {A7A75DBE-34C6-4A64-9238-2A284A8AB456}.Debug_Server.Build.0 = Debug|Win32 + {A7A75DBE-34C6-4A64-9238-2A284A8AB456}.Release.ActiveCfg = Release|Win32 + {A7A75DBE-34C6-4A64-9238-2A284A8AB456}.Release.Build.0 = Release|Win32 + {A7A75DBE-34C6-4A64-9238-2A284A8AB456}.Release_Server.ActiveCfg = Release|Win32 + {A7A75DBE-34C6-4A64-9238-2A284A8AB456}.Release_Server.Build.0 = Release|Win32 + {07726299-2609-4461-90BC-FF5A742766D5}.Debug.ActiveCfg = Debug|Win32 + {07726299-2609-4461-90BC-FF5A742766D5}.Debug.Build.0 = Debug|Win32 + {07726299-2609-4461-90BC-FF5A742766D5}.Debug_Server.ActiveCfg = Debug|Win32 + {07726299-2609-4461-90BC-FF5A742766D5}.Debug_Server.Build.0 = Debug|Win32 + {07726299-2609-4461-90BC-FF5A742766D5}.Release.ActiveCfg = Release|Win32 + {07726299-2609-4461-90BC-FF5A742766D5}.Release.Build.0 = Release|Win32 + {07726299-2609-4461-90BC-FF5A742766D5}.Release_Server.ActiveCfg = Release|Win32 + {07726299-2609-4461-90BC-FF5A742766D5}.Release_Server.Build.0 = Release|Win32 + {888D68F8-40F4-43DB-B786-53EA906517F3}.Debug.ActiveCfg = Debug|Win32 + {888D68F8-40F4-43DB-B786-53EA906517F3}.Debug.Build.0 = Debug|Win32 + {888D68F8-40F4-43DB-B786-53EA906517F3}.Debug_Server.ActiveCfg = Debug|Win32 + {888D68F8-40F4-43DB-B786-53EA906517F3}.Debug_Server.Build.0 = Debug|Win32 + {888D68F8-40F4-43DB-B786-53EA906517F3}.Release.ActiveCfg = Release|Win32 + {888D68F8-40F4-43DB-B786-53EA906517F3}.Release.Build.0 = Release|Win32 + {888D68F8-40F4-43DB-B786-53EA906517F3}.Release_Server.ActiveCfg = Release|Win32 + {888D68F8-40F4-43DB-B786-53EA906517F3}.Release_Server.Build.0 = Release|Win32 + {2D715490-C219-4952-ABE9-3D23FA8775EB}.Debug.ActiveCfg = Debug|Win32 + {2D715490-C219-4952-ABE9-3D23FA8775EB}.Debug.Build.0 = Debug|Win32 + {2D715490-C219-4952-ABE9-3D23FA8775EB}.Debug_Server.ActiveCfg = Debug|Win32 + {2D715490-C219-4952-ABE9-3D23FA8775EB}.Debug_Server.Build.0 = Debug|Win32 + {2D715490-C219-4952-ABE9-3D23FA8775EB}.Release.ActiveCfg = Release|Win32 + {2D715490-C219-4952-ABE9-3D23FA8775EB}.Release.Build.0 = Release|Win32 + {2D715490-C219-4952-ABE9-3D23FA8775EB}.Release_Server.ActiveCfg = Release|Win32 + {2D715490-C219-4952-ABE9-3D23FA8775EB}.Release_Server.Build.0 = Release|Win32 + {4FD2804C-465E-40F1-B766-48A1632FEEA7}.Debug.ActiveCfg = Debug|Win32 + {4FD2804C-465E-40F1-B766-48A1632FEEA7}.Debug.Build.0 = Debug|Win32 + {4FD2804C-465E-40F1-B766-48A1632FEEA7}.Debug_Server.ActiveCfg = Debug|Win32 + {4FD2804C-465E-40F1-B766-48A1632FEEA7}.Debug_Server.Build.0 = Debug|Win32 + {4FD2804C-465E-40F1-B766-48A1632FEEA7}.Release.ActiveCfg = Release|Win32 + {4FD2804C-465E-40F1-B766-48A1632FEEA7}.Release.Build.0 = Release|Win32 + {4FD2804C-465E-40F1-B766-48A1632FEEA7}.Release_Server.ActiveCfg = Release|Win32 + {4FD2804C-465E-40F1-B766-48A1632FEEA7}.Release_Server.Build.0 = Release|Win32 + {5F97BA7C-6E08-45DA-A68C-338246A3A4E7}.Debug.ActiveCfg = Debug|Win32 + {5F97BA7C-6E08-45DA-A68C-338246A3A4E7}.Debug.Build.0 = Debug|Win32 + {5F97BA7C-6E08-45DA-A68C-338246A3A4E7}.Debug_Server.ActiveCfg = Debug|Win32 + {5F97BA7C-6E08-45DA-A68C-338246A3A4E7}.Debug_Server.Build.0 = Debug|Win32 + {5F97BA7C-6E08-45DA-A68C-338246A3A4E7}.Release.ActiveCfg = Release|Win32 + {5F97BA7C-6E08-45DA-A68C-338246A3A4E7}.Release.Build.0 = Release|Win32 + {5F97BA7C-6E08-45DA-A68C-338246A3A4E7}.Release_Server.ActiveCfg = Release|Win32 + {5F97BA7C-6E08-45DA-A68C-338246A3A4E7}.Release_Server.Build.0 = Release|Win32 + {CE472632-E2F1-446C-ABAB-1CE5BD9F367B}.Debug.ActiveCfg = Debug|Win32 + {CE472632-E2F1-446C-ABAB-1CE5BD9F367B}.Debug.Build.0 = Debug|Win32 + {CE472632-E2F1-446C-ABAB-1CE5BD9F367B}.Debug_Server.ActiveCfg = Debug|Win32 + {CE472632-E2F1-446C-ABAB-1CE5BD9F367B}.Debug_Server.Build.0 = Debug|Win32 + {CE472632-E2F1-446C-ABAB-1CE5BD9F367B}.Release.ActiveCfg = Release|Win32 + {CE472632-E2F1-446C-ABAB-1CE5BD9F367B}.Release.Build.0 = Release|Win32 + {CE472632-E2F1-446C-ABAB-1CE5BD9F367B}.Release_Server.ActiveCfg = Release|Win32 + {CE472632-E2F1-446C-ABAB-1CE5BD9F367B}.Release_Server.Build.0 = Release|Win32 + {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F}.Debug.ActiveCfg = Debug|Win32 + {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F}.Debug.Build.0 = Debug|Win32 + {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F}.Debug_Server.ActiveCfg = Debug|Win32 + {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F}.Debug_Server.Build.0 = Debug|Win32 + {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F}.Release.ActiveCfg = Release|Win32 + {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F}.Release.Build.0 = Release|Win32 + {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F}.Release_Server.ActiveCfg = Release|Win32 + {EF33BFC0-C38B-4BA6-B5B7-E7F90323956F}.Release_Server.Build.0 = Release|Win32 + {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E}.Debug.ActiveCfg = Debug|Win32 + {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E}.Debug.Build.0 = Debug|Win32 + {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E}.Debug_Server.ActiveCfg = Debug|Win32 + {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E}.Debug_Server.Build.0 = Debug|Win32 + {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E}.Release.ActiveCfg = Release|Win32 + {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E}.Release.Build.0 = Release|Win32 + {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E}.Release_Server.ActiveCfg = Release|Win32 + {6AB4BC2A-F27B-4614-90FA-58F10CD71F8E}.Release_Server.Build.0 = Release|Win32 + {1E924D22-D5D8-4042-8A0F-F75266A0B3B6}.Debug.ActiveCfg = Debug|Win32 + {1E924D22-D5D8-4042-8A0F-F75266A0B3B6}.Debug.Build.0 = Debug|Win32 + {1E924D22-D5D8-4042-8A0F-F75266A0B3B6}.Debug_Server.ActiveCfg = Debug|Win32 + {1E924D22-D5D8-4042-8A0F-F75266A0B3B6}.Debug_Server.Build.0 = Debug|Win32 + {1E924D22-D5D8-4042-8A0F-F75266A0B3B6}.Release.ActiveCfg = Release|Win32 + {1E924D22-D5D8-4042-8A0F-F75266A0B3B6}.Release.Build.0 = Release|Win32 + {1E924D22-D5D8-4042-8A0F-F75266A0B3B6}.Release_Server.ActiveCfg = Release|Win32 + {1E924D22-D5D8-4042-8A0F-F75266A0B3B6}.Release_Server.Build.0 = Release|Win32 + {677C542F-9027-418D-9D9E-2D526E9FCD09}.Debug.ActiveCfg = Debug|Win32 + {677C542F-9027-418D-9D9E-2D526E9FCD09}.Debug.Build.0 = Debug|Win32 + {677C542F-9027-418D-9D9E-2D526E9FCD09}.Debug_Server.ActiveCfg = Debug|Win32 + {677C542F-9027-418D-9D9E-2D526E9FCD09}.Debug_Server.Build.0 = Debug|Win32 + {677C542F-9027-418D-9D9E-2D526E9FCD09}.Release.ActiveCfg = Release|Win32 + {677C542F-9027-418D-9D9E-2D526E9FCD09}.Release.Build.0 = Release|Win32 + {677C542F-9027-418D-9D9E-2D526E9FCD09}.Release_Server.ActiveCfg = Release|Win32 + {677C542F-9027-418D-9D9E-2D526E9FCD09}.Release_Server.Build.0 = Release|Win32 + {0DF4C6AB-6381-4FA1-9AED-F954141DD046}.Debug.ActiveCfg = Debug|Win32 + {0DF4C6AB-6381-4FA1-9AED-F954141DD046}.Debug.Build.0 = Debug|Win32 + {0DF4C6AB-6381-4FA1-9AED-F954141DD046}.Debug_Server.ActiveCfg = Debug|Win32 + {0DF4C6AB-6381-4FA1-9AED-F954141DD046}.Debug_Server.Build.0 = Debug|Win32 + {0DF4C6AB-6381-4FA1-9AED-F954141DD046}.Release.ActiveCfg = Release|Win32 + {0DF4C6AB-6381-4FA1-9AED-F954141DD046}.Release.Build.0 = Release|Win32 + {0DF4C6AB-6381-4FA1-9AED-F954141DD046}.Release_Server.ActiveCfg = Release|Win32 + {0DF4C6AB-6381-4FA1-9AED-F954141DD046}.Release_Server.Build.0 = Release|Win32 + {6F09B62B-34FD-4A60-9030-DB8FFACA66BA}.Debug.ActiveCfg = Debug|Win32 + {6F09B62B-34FD-4A60-9030-DB8FFACA66BA}.Debug.Build.0 = Debug|Win32 + {6F09B62B-34FD-4A60-9030-DB8FFACA66BA}.Debug_Server.ActiveCfg = Debug|Win32 + {6F09B62B-34FD-4A60-9030-DB8FFACA66BA}.Debug_Server.Build.0 = Debug|Win32 + {6F09B62B-34FD-4A60-9030-DB8FFACA66BA}.Release.ActiveCfg = Release|Win32 + {6F09B62B-34FD-4A60-9030-DB8FFACA66BA}.Release.Build.0 = Release|Win32 + {6F09B62B-34FD-4A60-9030-DB8FFACA66BA}.Release_Server.ActiveCfg = Release|Win32 + {6F09B62B-34FD-4A60-9030-DB8FFACA66BA}.Release_Server.Build.0 = Release|Win32 + {C55760CC-3A50-47D7-8605-FD73564AB3E9}.Debug.ActiveCfg = Debug|Win32 + {C55760CC-3A50-47D7-8605-FD73564AB3E9}.Debug.Build.0 = Debug|Win32 + {C55760CC-3A50-47D7-8605-FD73564AB3E9}.Debug_Server.ActiveCfg = Debug|Win32 + {C55760CC-3A50-47D7-8605-FD73564AB3E9}.Debug_Server.Build.0 = Debug|Win32 + {C55760CC-3A50-47D7-8605-FD73564AB3E9}.Release.ActiveCfg = Release|Win32 + {C55760CC-3A50-47D7-8605-FD73564AB3E9}.Release.Build.0 = Release|Win32 + {C55760CC-3A50-47D7-8605-FD73564AB3E9}.Release_Server.ActiveCfg = Release|Win32 + {C55760CC-3A50-47D7-8605-FD73564AB3E9}.Release_Server.Build.0 = Release|Win32 + {4576792A-58CA-478A-9CB1-FBE05E559C2F}.Debug.ActiveCfg = Debug|Win32 + {4576792A-58CA-478A-9CB1-FBE05E559C2F}.Debug.Build.0 = Debug|Win32 + {4576792A-58CA-478A-9CB1-FBE05E559C2F}.Debug_Server.ActiveCfg = Debug|Win32 + {4576792A-58CA-478A-9CB1-FBE05E559C2F}.Debug_Server.Build.0 = Debug|Win32 + {4576792A-58CA-478A-9CB1-FBE05E559C2F}.Release.ActiveCfg = Release|Win32 + {4576792A-58CA-478A-9CB1-FBE05E559C2F}.Release.Build.0 = Release|Win32 + {4576792A-58CA-478A-9CB1-FBE05E559C2F}.Release_Server.ActiveCfg = Release|Win32 + {4576792A-58CA-478A-9CB1-FBE05E559C2F}.Release_Server.Build.0 = Release|Win32 + {42503779-D12B-435D-9EA8-28D9FE0C9B08}.Debug.ActiveCfg = Debug|Win32 + {42503779-D12B-435D-9EA8-28D9FE0C9B08}.Debug.Build.0 = Debug|Win32 + {42503779-D12B-435D-9EA8-28D9FE0C9B08}.Debug_Server.ActiveCfg = Debug|Win32 + {42503779-D12B-435D-9EA8-28D9FE0C9B08}.Debug_Server.Build.0 = Debug|Win32 + {42503779-D12B-435D-9EA8-28D9FE0C9B08}.Release.ActiveCfg = Release|Win32 + {42503779-D12B-435D-9EA8-28D9FE0C9B08}.Release.Build.0 = Release|Win32 + {42503779-D12B-435D-9EA8-28D9FE0C9B08}.Release_Server.ActiveCfg = Release|Win32 + {42503779-D12B-435D-9EA8-28D9FE0C9B08}.Release_Server.Build.0 = Release|Win32 + {ECFC7F74-12DC-40FE-B201-9D5002F53721}.Debug.ActiveCfg = Debug|Win32 + {ECFC7F74-12DC-40FE-B201-9D5002F53721}.Debug.Build.0 = Debug|Win32 + {ECFC7F74-12DC-40FE-B201-9D5002F53721}.Debug_Server.ActiveCfg = Debug|Win32 + {ECFC7F74-12DC-40FE-B201-9D5002F53721}.Debug_Server.Build.0 = Debug|Win32 + {ECFC7F74-12DC-40FE-B201-9D5002F53721}.Release.ActiveCfg = Release|Win32 + {ECFC7F74-12DC-40FE-B201-9D5002F53721}.Release.Build.0 = Release|Win32 + {ECFC7F74-12DC-40FE-B201-9D5002F53721}.Release_Server.ActiveCfg = Release|Win32 + {ECFC7F74-12DC-40FE-B201-9D5002F53721}.Release_Server.Build.0 = Release|Win32 + {F83B1689-144D-4183-A2A5-0BE3D1892C49}.Debug.ActiveCfg = Debug|Win32 + {F83B1689-144D-4183-A2A5-0BE3D1892C49}.Debug.Build.0 = Debug|Win32 + {F83B1689-144D-4183-A2A5-0BE3D1892C49}.Debug_Server.ActiveCfg = Debug|Win32 + {F83B1689-144D-4183-A2A5-0BE3D1892C49}.Debug_Server.Build.0 = Debug|Win32 + {F83B1689-144D-4183-A2A5-0BE3D1892C49}.Release.ActiveCfg = Release|Win32 + {F83B1689-144D-4183-A2A5-0BE3D1892C49}.Release.Build.0 = Release|Win32 + {F83B1689-144D-4183-A2A5-0BE3D1892C49}.Release_Server.ActiveCfg = Release|Win32 + {F83B1689-144D-4183-A2A5-0BE3D1892C49}.Release_Server.Build.0 = Release|Win32 + {DC9938D5-9D10-4B7D-9E91-507AABBAF924}.Debug.ActiveCfg = Debug|Win32 + {DC9938D5-9D10-4B7D-9E91-507AABBAF924}.Debug.Build.0 = Debug|Win32 + {DC9938D5-9D10-4B7D-9E91-507AABBAF924}.Debug_Server.ActiveCfg = Debug|Win32 + {DC9938D5-9D10-4B7D-9E91-507AABBAF924}.Debug_Server.Build.0 = Debug|Win32 + {DC9938D5-9D10-4B7D-9E91-507AABBAF924}.Release.ActiveCfg = Release|Win32 + {DC9938D5-9D10-4B7D-9E91-507AABBAF924}.Release.Build.0 = Release|Win32 + {DC9938D5-9D10-4B7D-9E91-507AABBAF924}.Release_Server.ActiveCfg = Release|Win32 + {DC9938D5-9D10-4B7D-9E91-507AABBAF924}.Release_Server.Build.0 = Release|Win32 + {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477}.Debug.ActiveCfg = Debug|Win32 + {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477}.Debug.Build.0 = Debug|Win32 + {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477}.Debug_Server.ActiveCfg = Debug|Win32 + {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477}.Debug_Server.Build.0 = Debug|Win32 + {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477}.Release.ActiveCfg = Release|Win32 + {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477}.Release.Build.0 = Release|Win32 + {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477}.Release_Server.ActiveCfg = Release|Win32 + {5C9C6E61-02D6-4BA6-A4F5-6E6AE440E477}.Release_Server.Build.0 = Release|Win32 + {A49383E6-2CE6-49E2-86EC-5074AF793B35}.Debug.ActiveCfg = Debug|Win32 + {A49383E6-2CE6-49E2-86EC-5074AF793B35}.Debug.Build.0 = Debug|Win32 + {A49383E6-2CE6-49E2-86EC-5074AF793B35}.Debug_Server.ActiveCfg = Debug|Win32 + {A49383E6-2CE6-49E2-86EC-5074AF793B35}.Debug_Server.Build.0 = Debug|Win32 + {A49383E6-2CE6-49E2-86EC-5074AF793B35}.Release.ActiveCfg = Release|Win32 + {A49383E6-2CE6-49E2-86EC-5074AF793B35}.Release.Build.0 = Release|Win32 + {A49383E6-2CE6-49E2-86EC-5074AF793B35}.Release_Server.ActiveCfg = Release|Win32 + {A49383E6-2CE6-49E2-86EC-5074AF793B35}.Release_Server.Build.0 = Release|Win32 + {C0FC318E-8E10-488C-939F-84C97D9B5972}.Debug.ActiveCfg = Debug|Win32 + {C0FC318E-8E10-488C-939F-84C97D9B5972}.Debug.Build.0 = Debug|Win32 + {C0FC318E-8E10-488C-939F-84C97D9B5972}.Debug_Server.ActiveCfg = Debug|Win32 + {C0FC318E-8E10-488C-939F-84C97D9B5972}.Debug_Server.Build.0 = Debug|Win32 + {C0FC318E-8E10-488C-939F-84C97D9B5972}.Release.ActiveCfg = Release|Win32 + {C0FC318E-8E10-488C-939F-84C97D9B5972}.Release.Build.0 = Release|Win32 + {C0FC318E-8E10-488C-939F-84C97D9B5972}.Release_Server.ActiveCfg = Release|Win32 + {C0FC318E-8E10-488C-939F-84C97D9B5972}.Release_Server.Build.0 = Release|Win32 + {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF}.Debug.ActiveCfg = Debug|Win32 + {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF}.Debug.Build.0 = Debug|Win32 + {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF}.Debug_Server.ActiveCfg = Debug|Win32 + {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF}.Debug_Server.Build.0 = Debug|Win32 + {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF}.Release.ActiveCfg = Release|Win32 + {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF}.Release.Build.0 = Release|Win32 + {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF}.Release_Server.ActiveCfg = Release|Win32 + {A594EB2A-9EA1-4A8F-8BD8-4D32CBB8FAFF}.Release_Server.Build.0 = Release|Win32 + {A58C3644-55E7-44EB-BE85-0D53773B8BA9}.Debug.ActiveCfg = Debug|Win32 + {A58C3644-55E7-44EB-BE85-0D53773B8BA9}.Debug.Build.0 = Debug|Win32 + {A58C3644-55E7-44EB-BE85-0D53773B8BA9}.Debug_Server.ActiveCfg = Debug|Win32 + {A58C3644-55E7-44EB-BE85-0D53773B8BA9}.Debug_Server.Build.0 = Debug|Win32 + {A58C3644-55E7-44EB-BE85-0D53773B8BA9}.Release.ActiveCfg = Release|Win32 + {A58C3644-55E7-44EB-BE85-0D53773B8BA9}.Release.Build.0 = Release|Win32 + {A58C3644-55E7-44EB-BE85-0D53773B8BA9}.Release_Server.ActiveCfg = Release|Win32 + {A58C3644-55E7-44EB-BE85-0D53773B8BA9}.Release_Server.Build.0 = Release|Win32 + {B7B9B275-1059-40FE-9502-0CF42B5BA32F}.Debug.ActiveCfg = Debug|Win32 + {B7B9B275-1059-40FE-9502-0CF42B5BA32F}.Debug.Build.0 = Debug|Win32 + {B7B9B275-1059-40FE-9502-0CF42B5BA32F}.Debug_Server.ActiveCfg = Debug|Win32 + {B7B9B275-1059-40FE-9502-0CF42B5BA32F}.Debug_Server.Build.0 = Debug|Win32 + {B7B9B275-1059-40FE-9502-0CF42B5BA32F}.Release.ActiveCfg = Release|Win32 + {B7B9B275-1059-40FE-9502-0CF42B5BA32F}.Release.Build.0 = Release|Win32 + {B7B9B275-1059-40FE-9502-0CF42B5BA32F}.Release_Server.ActiveCfg = Release|Win32 + {B7B9B275-1059-40FE-9502-0CF42B5BA32F}.Release_Server.Build.0 = Release|Win32 + {210889EC-9A23-450C-98F6-B353B73F3457}.Debug.ActiveCfg = Debug|Win32 + {210889EC-9A23-450C-98F6-B353B73F3457}.Debug.Build.0 = Debug|Win32 + {210889EC-9A23-450C-98F6-B353B73F3457}.Debug_Server.ActiveCfg = Debug|Win32 + {210889EC-9A23-450C-98F6-B353B73F3457}.Debug_Server.Build.0 = Debug|Win32 + {210889EC-9A23-450C-98F6-B353B73F3457}.Release.ActiveCfg = Release|Win32 + {210889EC-9A23-450C-98F6-B353B73F3457}.Release.Build.0 = Release|Win32 + {210889EC-9A23-450C-98F6-B353B73F3457}.Release_Server.ActiveCfg = Release|Win32 + {210889EC-9A23-450C-98F6-B353B73F3457}.Release_Server.Build.0 = Release|Win32 + {F7AFF9EF-7126-4378-9405-3F96B151258C}.Debug.ActiveCfg = Debug|Win32 + {F7AFF9EF-7126-4378-9405-3F96B151258C}.Debug.Build.0 = Debug|Win32 + {F7AFF9EF-7126-4378-9405-3F96B151258C}.Debug_Server.ActiveCfg = Debug|Win32 + {F7AFF9EF-7126-4378-9405-3F96B151258C}.Debug_Server.Build.0 = Debug|Win32 + {F7AFF9EF-7126-4378-9405-3F96B151258C}.Release.ActiveCfg = Release|Win32 + {F7AFF9EF-7126-4378-9405-3F96B151258C}.Release.Build.0 = Release|Win32 + {F7AFF9EF-7126-4378-9405-3F96B151258C}.Release_Server.ActiveCfg = Release|Win32 + {F7AFF9EF-7126-4378-9405-3F96B151258C}.Release_Server.Build.0 = Release|Win32 + {A8350709-C3E0-4B19-9218-D1B23C6E97DC}.Debug.ActiveCfg = Debug|Win32 + {A8350709-C3E0-4B19-9218-D1B23C6E97DC}.Debug.Build.0 = Debug|Win32 + {A8350709-C3E0-4B19-9218-D1B23C6E97DC}.Debug_Server.ActiveCfg = Debug|Win32 + {A8350709-C3E0-4B19-9218-D1B23C6E97DC}.Debug_Server.Build.0 = Debug|Win32 + {A8350709-C3E0-4B19-9218-D1B23C6E97DC}.Release.ActiveCfg = Release|Win32 + {A8350709-C3E0-4B19-9218-D1B23C6E97DC}.Release.Build.0 = Release|Win32 + {A8350709-C3E0-4B19-9218-D1B23C6E97DC}.Release_Server.ActiveCfg = Release|Win32 + {A8350709-C3E0-4B19-9218-D1B23C6E97DC}.Release_Server.Build.0 = Release|Win32 + {92BC9EC6-F01A-48D3-915E-A7B54700FE11}.Debug.ActiveCfg = Debug|Win32 + {92BC9EC6-F01A-48D3-915E-A7B54700FE11}.Debug.Build.0 = Debug|Win32 + {92BC9EC6-F01A-48D3-915E-A7B54700FE11}.Debug_Server.ActiveCfg = Debug|Win32 + {92BC9EC6-F01A-48D3-915E-A7B54700FE11}.Debug_Server.Build.0 = Debug|Win32 + {92BC9EC6-F01A-48D3-915E-A7B54700FE11}.Release.ActiveCfg = Release|Win32 + {92BC9EC6-F01A-48D3-915E-A7B54700FE11}.Release.Build.0 = Release|Win32 + {92BC9EC6-F01A-48D3-915E-A7B54700FE11}.Release_Server.ActiveCfg = Release|Win32 + {92BC9EC6-F01A-48D3-915E-A7B54700FE11}.Release_Server.Build.0 = Release|Win32 + {65A72F3C-FE82-4014-81A3-4647938F40AA}.Debug.ActiveCfg = Debug|Win32 + {65A72F3C-FE82-4014-81A3-4647938F40AA}.Debug.Build.0 = Debug|Win32 + {65A72F3C-FE82-4014-81A3-4647938F40AA}.Debug_Server.ActiveCfg = Debug|Win32 + {65A72F3C-FE82-4014-81A3-4647938F40AA}.Debug_Server.Build.0 = Debug|Win32 + {65A72F3C-FE82-4014-81A3-4647938F40AA}.Release.ActiveCfg = Release|Win32 + {65A72F3C-FE82-4014-81A3-4647938F40AA}.Release.Build.0 = Release|Win32 + {65A72F3C-FE82-4014-81A3-4647938F40AA}.Release_Server.ActiveCfg = Release|Win32 + {65A72F3C-FE82-4014-81A3-4647938F40AA}.Release_Server.Build.0 = Release|Win32 + {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30}.Debug.ActiveCfg = Debug|Win32 + {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30}.Debug.Build.0 = Debug|Win32 + {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30}.Debug_Server.ActiveCfg = Debug|Win32 + {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30}.Debug_Server.Build.0 = Debug|Win32 + {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30}.Release.ActiveCfg = Release|Win32 + {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30}.Release.Build.0 = Release|Win32 + {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30}.Release_Server.ActiveCfg = Release|Win32 + {4009FA12-FA43-4ED8-B69E-8FBAF4C43B30}.Release_Server.Build.0 = Release|Win32 + {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7}.Debug.ActiveCfg = Debug|Win32 + {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7}.Debug.Build.0 = Debug|Win32 + {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7}.Debug_Server.ActiveCfg = Debug|Win32 + {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7}.Debug_Server.Build.0 = Debug|Win32 + {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7}.Release.ActiveCfg = Release|Win32 + {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7}.Release.Build.0 = Release|Win32 + {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7}.Release_Server.ActiveCfg = Release|Win32 + {C3BB26BA-B5C7-4D1E-9307-B0D20056E6F7}.Release_Server.Build.0 = Release|Win32 + {E44AE4EE-B215-4C60-91B9-9D3D99A71657}.Debug.ActiveCfg = Debug|Win32 + {E44AE4EE-B215-4C60-91B9-9D3D99A71657}.Debug.Build.0 = Debug|Win32 + {E44AE4EE-B215-4C60-91B9-9D3D99A71657}.Debug_Server.ActiveCfg = Debug|Win32 + {E44AE4EE-B215-4C60-91B9-9D3D99A71657}.Debug_Server.Build.0 = Debug|Win32 + {E44AE4EE-B215-4C60-91B9-9D3D99A71657}.Release.ActiveCfg = Release|Win32 + {E44AE4EE-B215-4C60-91B9-9D3D99A71657}.Release.Build.0 = Release|Win32 + {E44AE4EE-B215-4C60-91B9-9D3D99A71657}.Release_Server.ActiveCfg = Release|Win32 + {E44AE4EE-B215-4C60-91B9-9D3D99A71657}.Release_Server.Build.0 = Release|Win32 + {9CDD8836-9B27-4AB8-82DB-8200308CE38A}.Debug.ActiveCfg = Debug|Win32 + {9CDD8836-9B27-4AB8-82DB-8200308CE38A}.Debug.Build.0 = Debug|Win32 + {9CDD8836-9B27-4AB8-82DB-8200308CE38A}.Debug_Server.ActiveCfg = Debug|Win32 + {9CDD8836-9B27-4AB8-82DB-8200308CE38A}.Debug_Server.Build.0 = Debug|Win32 + {9CDD8836-9B27-4AB8-82DB-8200308CE38A}.Release.ActiveCfg = Release|Win32 + {9CDD8836-9B27-4AB8-82DB-8200308CE38A}.Release.Build.0 = Release|Win32 + {9CDD8836-9B27-4AB8-82DB-8200308CE38A}.Release_Server.ActiveCfg = Release|Win32 + {9CDD8836-9B27-4AB8-82DB-8200308CE38A}.Release_Server.Build.0 = Release|Win32 + {710933F4-5E7C-4EA2-9B3B-91281A933562}.Debug.ActiveCfg = Debug|Win32 + {710933F4-5E7C-4EA2-9B3B-91281A933562}.Debug.Build.0 = Debug|Win32 + {710933F4-5E7C-4EA2-9B3B-91281A933562}.Debug_Server.ActiveCfg = Debug|Win32 + {710933F4-5E7C-4EA2-9B3B-91281A933562}.Debug_Server.Build.0 = Debug|Win32 + {710933F4-5E7C-4EA2-9B3B-91281A933562}.Release.ActiveCfg = Release|Win32 + {710933F4-5E7C-4EA2-9B3B-91281A933562}.Release.Build.0 = Release|Win32 + {710933F4-5E7C-4EA2-9B3B-91281A933562}.Release_Server.ActiveCfg = Release|Win32 + {710933F4-5E7C-4EA2-9B3B-91281A933562}.Release_Server.Build.0 = Release|Win32 + {49365412-EA0C-4851-9FA9-2E1799ED7A82}.Debug.ActiveCfg = Debug|Win32 + {49365412-EA0C-4851-9FA9-2E1799ED7A82}.Debug.Build.0 = Debug|Win32 + {49365412-EA0C-4851-9FA9-2E1799ED7A82}.Debug_Server.ActiveCfg = Debug|Win32 + {49365412-EA0C-4851-9FA9-2E1799ED7A82}.Debug_Server.Build.0 = Debug|Win32 + {49365412-EA0C-4851-9FA9-2E1799ED7A82}.Release.ActiveCfg = Release|Win32 + {49365412-EA0C-4851-9FA9-2E1799ED7A82}.Release.Build.0 = Release|Win32 + {49365412-EA0C-4851-9FA9-2E1799ED7A82}.Release_Server.ActiveCfg = Release|Win32 + {49365412-EA0C-4851-9FA9-2E1799ED7A82}.Release_Server.Build.0 = Release|Win32 + {0BC97654-C80F-4870-B56B-56DDCEB09B59}.Debug.ActiveCfg = Debug|Win32 + {0BC97654-C80F-4870-B56B-56DDCEB09B59}.Debug.Build.0 = Debug|Win32 + {0BC97654-C80F-4870-B56B-56DDCEB09B59}.Debug_Server.ActiveCfg = Debug|Win32 + {0BC97654-C80F-4870-B56B-56DDCEB09B59}.Debug_Server.Build.0 = Debug|Win32 + {0BC97654-C80F-4870-B56B-56DDCEB09B59}.Release.ActiveCfg = Release|Win32 + {0BC97654-C80F-4870-B56B-56DDCEB09B59}.Release.Build.0 = Release|Win32 + {0BC97654-C80F-4870-B56B-56DDCEB09B59}.Release_Server.ActiveCfg = Release|Win32 + {0BC97654-C80F-4870-B56B-56DDCEB09B59}.Release_Server.Build.0 = Release|Win32 + {C878C7D1-E336-4668-B682-088C6B0A18D7}.Debug.ActiveCfg = Debug|Win32 + {C878C7D1-E336-4668-B682-088C6B0A18D7}.Debug.Build.0 = Debug|Win32 + {C878C7D1-E336-4668-B682-088C6B0A18D7}.Debug_Server.ActiveCfg = Debug|Win32 + {C878C7D1-E336-4668-B682-088C6B0A18D7}.Debug_Server.Build.0 = Debug|Win32 + {C878C7D1-E336-4668-B682-088C6B0A18D7}.Release.ActiveCfg = Release|Win32 + {C878C7D1-E336-4668-B682-088C6B0A18D7}.Release.Build.0 = Release|Win32 + {C878C7D1-E336-4668-B682-088C6B0A18D7}.Release_Server.ActiveCfg = Release|Win32 + {C878C7D1-E336-4668-B682-088C6B0A18D7}.Release_Server.Build.0 = Release|Win32 + {54AC8E94-D1FE-4533-9E3A-B35C143CDD93}.Debug.ActiveCfg = Debug|Win32 + {54AC8E94-D1FE-4533-9E3A-B35C143CDD93}.Debug.Build.0 = Debug|Win32 + {54AC8E94-D1FE-4533-9E3A-B35C143CDD93}.Debug_Server.ActiveCfg = Debug|Win32 + {54AC8E94-D1FE-4533-9E3A-B35C143CDD93}.Debug_Server.Build.0 = Debug|Win32 + {54AC8E94-D1FE-4533-9E3A-B35C143CDD93}.Release.ActiveCfg = Release|Win32 + {54AC8E94-D1FE-4533-9E3A-B35C143CDD93}.Release.Build.0 = Release|Win32 + {54AC8E94-D1FE-4533-9E3A-B35C143CDD93}.Release_Server.ActiveCfg = Release|Win32 + {54AC8E94-D1FE-4533-9E3A-B35C143CDD93}.Release_Server.Build.0 = Release|Win32 + {032566E8-A751-4863-89E3-CEC33703B3C1}.Debug.ActiveCfg = Debug|Win32 + {032566E8-A751-4863-89E3-CEC33703B3C1}.Debug.Build.0 = Debug|Win32 + {032566E8-A751-4863-89E3-CEC33703B3C1}.Debug_Server.ActiveCfg = Debug|Win32 + {032566E8-A751-4863-89E3-CEC33703B3C1}.Debug_Server.Build.0 = Debug|Win32 + {032566E8-A751-4863-89E3-CEC33703B3C1}.Release.ActiveCfg = Release|Win32 + {032566E8-A751-4863-89E3-CEC33703B3C1}.Release.Build.0 = Release|Win32 + {032566E8-A751-4863-89E3-CEC33703B3C1}.Release_Server.ActiveCfg = Release|Win32 + {032566E8-A751-4863-89E3-CEC33703B3C1}.Release_Server.Build.0 = Release|Win32 + {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53}.Debug.ActiveCfg = Debug|Win32 + {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53}.Debug.Build.0 = Debug|Win32 + {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53}.Debug_Server.ActiveCfg = Debug|Win32 + {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53}.Debug_Server.Build.0 = Debug|Win32 + {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53}.Release.ActiveCfg = Release|Win32 + {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53}.Release.Build.0 = Release|Win32 + {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53}.Release_Server.ActiveCfg = Release|Win32 + {35BE9A13-6922-40D5-B0F0-7D65AF4BAA53}.Release_Server.Build.0 = Release|Win32 + {64DFE3A7-EC39-43BC-B79D-90C572D76BEF}.Debug.ActiveCfg = Debug|Win32 + {64DFE3A7-EC39-43BC-B79D-90C572D76BEF}.Debug.Build.0 = Debug|Win32 + {64DFE3A7-EC39-43BC-B79D-90C572D76BEF}.Debug_Server.ActiveCfg = Debug|Win32 + {64DFE3A7-EC39-43BC-B79D-90C572D76BEF}.Debug_Server.Build.0 = Debug|Win32 + {64DFE3A7-EC39-43BC-B79D-90C572D76BEF}.Release.ActiveCfg = Release|Win32 + {64DFE3A7-EC39-43BC-B79D-90C572D76BEF}.Release.Build.0 = Release|Win32 + {64DFE3A7-EC39-43BC-B79D-90C572D76BEF}.Release_Server.ActiveCfg = Release|Win32 + {64DFE3A7-EC39-43BC-B79D-90C572D76BEF}.Release_Server.Build.0 = Release|Win32 + {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5}.Debug.ActiveCfg = Debug|Win32 + {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5}.Debug.Build.0 = Debug|Win32 + {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5}.Debug_Server.ActiveCfg = Debug|Win32 + {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5}.Debug_Server.Build.0 = Debug|Win32 + {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5}.Release.ActiveCfg = Release|Win32 + {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5}.Release.Build.0 = Release|Win32 + {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5}.Release_Server.ActiveCfg = Release|Win32 + {4B2DF1CE-C32E-4815-93BB-1E1204B2DEF5}.Release_Server.Build.0 = Release|Win32 + {1D3203DE-A44B-41DD-AC7E-CCB724B6E954}.Debug.ActiveCfg = Debug|Win32 + {1D3203DE-A44B-41DD-AC7E-CCB724B6E954}.Debug.Build.0 = Debug|Win32 + {1D3203DE-A44B-41DD-AC7E-CCB724B6E954}.Debug_Server.ActiveCfg = Debug|Win32 + {1D3203DE-A44B-41DD-AC7E-CCB724B6E954}.Debug_Server.Build.0 = Debug|Win32 + {1D3203DE-A44B-41DD-AC7E-CCB724B6E954}.Release.ActiveCfg = Release|Win32 + {1D3203DE-A44B-41DD-AC7E-CCB724B6E954}.Release.Build.0 = Release|Win32 + {1D3203DE-A44B-41DD-AC7E-CCB724B6E954}.Release_Server.ActiveCfg = Release|Win32 + {1D3203DE-A44B-41DD-AC7E-CCB724B6E954}.Release_Server.Build.0 = Release|Win32 + {C3A1070E-8C00-4CE5-B4E8-69660032DB71}.Debug.ActiveCfg = Debug|Win32 + {C3A1070E-8C00-4CE5-B4E8-69660032DB71}.Debug.Build.0 = Debug|Win32 + {C3A1070E-8C00-4CE5-B4E8-69660032DB71}.Debug_Server.ActiveCfg = Debug|Win32 + {C3A1070E-8C00-4CE5-B4E8-69660032DB71}.Debug_Server.Build.0 = Debug|Win32 + {C3A1070E-8C00-4CE5-B4E8-69660032DB71}.Release.ActiveCfg = Release|Win32 + {C3A1070E-8C00-4CE5-B4E8-69660032DB71}.Release.Build.0 = Release|Win32 + {C3A1070E-8C00-4CE5-B4E8-69660032DB71}.Release_Server.ActiveCfg = Release|Win32 + {C3A1070E-8C00-4CE5-B4E8-69660032DB71}.Release_Server.Build.0 = Release|Win32 + {46B6473A-BD67-4DB8-9479-3C26B8626CFA}.Debug.ActiveCfg = Debug|Win32 + {46B6473A-BD67-4DB8-9479-3C26B8626CFA}.Debug.Build.0 = Debug|Win32 + {46B6473A-BD67-4DB8-9479-3C26B8626CFA}.Debug_Server.ActiveCfg = Debug|Win32 + {46B6473A-BD67-4DB8-9479-3C26B8626CFA}.Debug_Server.Build.0 = Debug|Win32 + {46B6473A-BD67-4DB8-9479-3C26B8626CFA}.Release.ActiveCfg = Release|Win32 + {46B6473A-BD67-4DB8-9479-3C26B8626CFA}.Release.Build.0 = Release|Win32 + {46B6473A-BD67-4DB8-9479-3C26B8626CFA}.Release_Server.ActiveCfg = Release|Win32 + {46B6473A-BD67-4DB8-9479-3C26B8626CFA}.Release_Server.Build.0 = Release|Win32 + {71C4EDF1-4301-4A25-BE09-69CD54A5037C}.Debug.ActiveCfg = Debug|Win32 + {71C4EDF1-4301-4A25-BE09-69CD54A5037C}.Debug.Build.0 = Debug|Win32 + {71C4EDF1-4301-4A25-BE09-69CD54A5037C}.Debug_Server.ActiveCfg = Debug|Win32 + {71C4EDF1-4301-4A25-BE09-69CD54A5037C}.Debug_Server.Build.0 = Debug|Win32 + {71C4EDF1-4301-4A25-BE09-69CD54A5037C}.Release.ActiveCfg = Release|Win32 + {71C4EDF1-4301-4A25-BE09-69CD54A5037C}.Release.Build.0 = Release|Win32 + {71C4EDF1-4301-4A25-BE09-69CD54A5037C}.Release_Server.ActiveCfg = Release|Win32 + {71C4EDF1-4301-4A25-BE09-69CD54A5037C}.Release_Server.Build.0 = Release|Win32 + {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E}.Debug.ActiveCfg = Debug|Win32 + {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E}.Debug.Build.0 = Debug|Win32 + {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E}.Debug_Server.ActiveCfg = Debug|Win32 + {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E}.Debug_Server.Build.0 = Debug|Win32 + {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E}.Release.ActiveCfg = Release|Win32 + {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E}.Release.Build.0 = Release|Win32 + {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E}.Release_Server.ActiveCfg = Release|Win32 + {6DDD1A2D-0CDF-44FC-919A-11CD9DE9511E}.Release_Server.Build.0 = Release|Win32 + {C0DE0FFD-88EC-4B75-A621-042B101A5278}.Debug.ActiveCfg = Debug|Win32 + {C0DE0FFD-88EC-4B75-A621-042B101A5278}.Debug.Build.0 = Debug|Win32 + {C0DE0FFD-88EC-4B75-A621-042B101A5278}.Debug_Server.ActiveCfg = Debug|Win32 + {C0DE0FFD-88EC-4B75-A621-042B101A5278}.Debug_Server.Build.0 = Debug|Win32 + {C0DE0FFD-88EC-4B75-A621-042B101A5278}.Release.ActiveCfg = Release|Win32 + {C0DE0FFD-88EC-4B75-A621-042B101A5278}.Release.Build.0 = Release|Win32 + {C0DE0FFD-88EC-4B75-A621-042B101A5278}.Release_Server.ActiveCfg = Release|Win32 + {C0DE0FFD-88EC-4B75-A621-042B101A5278}.Release_Server.Build.0 = Release|Win32 + {56C50721-716D-468B-9EEB-750DA8C50EF7}.Debug.ActiveCfg = Debug|Win32 + {56C50721-716D-468B-9EEB-750DA8C50EF7}.Debug.Build.0 = Debug|Win32 + {56C50721-716D-468B-9EEB-750DA8C50EF7}.Debug_Server.ActiveCfg = Debug|Win32 + {56C50721-716D-468B-9EEB-750DA8C50EF7}.Debug_Server.Build.0 = Debug|Win32 + {56C50721-716D-468B-9EEB-750DA8C50EF7}.Release.ActiveCfg = Release|Win32 + {56C50721-716D-468B-9EEB-750DA8C50EF7}.Release.Build.0 = Release|Win32 + {56C50721-716D-468B-9EEB-750DA8C50EF7}.Release_Server.ActiveCfg = Release|Win32 + {56C50721-716D-468B-9EEB-750DA8C50EF7}.Release_Server.Build.0 = Release|Win32 + {663AB605-0B37-41F6-A015-7219D9B5F84B}.Debug.ActiveCfg = Debug|Win32 + {663AB605-0B37-41F6-A015-7219D9B5F84B}.Debug.Build.0 = Debug|Win32 + {663AB605-0B37-41F6-A015-7219D9B5F84B}.Debug_Server.ActiveCfg = Debug|Win32 + {663AB605-0B37-41F6-A015-7219D9B5F84B}.Debug_Server.Build.0 = Debug|Win32 + {663AB605-0B37-41F6-A015-7219D9B5F84B}.Release.ActiveCfg = Release|Win32 + {663AB605-0B37-41F6-A015-7219D9B5F84B}.Release.Build.0 = Release|Win32 + {663AB605-0B37-41F6-A015-7219D9B5F84B}.Release_Server.ActiveCfg = Release|Win32 + {663AB605-0B37-41F6-A015-7219D9B5F84B}.Release_Server.Build.0 = Release|Win32 + {885A77A9-11CA-4313-B60C-E90B3A1940B6}.Debug.ActiveCfg = Debug|Win32 + {885A77A9-11CA-4313-B60C-E90B3A1940B6}.Debug.Build.0 = Debug|Win32 + {885A77A9-11CA-4313-B60C-E90B3A1940B6}.Debug_Server.ActiveCfg = Debug|Win32 + {885A77A9-11CA-4313-B60C-E90B3A1940B6}.Debug_Server.Build.0 = Debug|Win32 + {885A77A9-11CA-4313-B60C-E90B3A1940B6}.Release.ActiveCfg = Release|Win32 + {885A77A9-11CA-4313-B60C-E90B3A1940B6}.Release.Build.0 = Release|Win32 + {885A77A9-11CA-4313-B60C-E90B3A1940B6}.Release_Server.ActiveCfg = Release|Win32 + {885A77A9-11CA-4313-B60C-E90B3A1940B6}.Release_Server.Build.0 = Release|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Debug.ActiveCfg = Debug|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Debug.Build.0 = Debug|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Debug_Server.ActiveCfg = Debug|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Debug_Server.Build.0 = Debug|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Release.ActiveCfg = Release|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Release.Build.0 = Release|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Release_Server.ActiveCfg = Release|Win32 + {A32201A8-0255-4863-97B8-4A569C18C624}.Release_Server.Build.0 = Release|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Debug.ActiveCfg = Debug|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Debug.Build.0 = Debug|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Debug_Server.Build.0 = Debug_Server|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Release.ActiveCfg = Release|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Release.Build.0 = Release|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Release_Server.ActiveCfg = Release_Server|Win32 + {4054C94F-866A-4AA7-874B-2AFCFEF23A71}.Release_Server.Build.0 = Release_Server|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Debug.ActiveCfg = Debug|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Debug.Build.0 = Debug|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Debug_Server.Build.0 = Debug_Server|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Release.ActiveCfg = Release|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Release.Build.0 = Release|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Release_Server.ActiveCfg = Release_Server|Win32 + {4E991726-D69F-4786-A873-DF37FB4DABF9}.Release_Server.Build.0 = Release_Server|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Debug.ActiveCfg = Debug|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Debug.Build.0 = Debug|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Debug_Server.Build.0 = Debug_Server|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Release.ActiveCfg = Release|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Release.Build.0 = Release|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Release_Server.ActiveCfg = Release_Server|Win32 + {55C8DDC2-0675-4977-A18F-166109D89F4B}.Release_Server.Build.0 = Release_Server|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Debug.ActiveCfg = Debug|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Debug.Build.0 = Debug|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Debug_Server.Build.0 = Debug_Server|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Release.ActiveCfg = Release|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Release.Build.0 = Release|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Release_Server.ActiveCfg = Release_Server|Win32 + {89316659-F4A9-4E92-8200-C36288A61B9B}.Release_Server.Build.0 = Release_Server|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Debug.ActiveCfg = Debug|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Debug.Build.0 = Debug|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Debug_Server.Build.0 = Debug_Server|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Release.ActiveCfg = Release|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Release.Build.0 = Release|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Release_Server.ActiveCfg = Release_Server|Win32 + {08EB4DCB-75F7-419E-A212-0BEAD6A10FF1}.Release_Server.Build.0 = Release_Server|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Debug.ActiveCfg = Debug|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Debug.Build.0 = Debug|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Debug_Server.ActiveCfg = Debug|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Debug_Server.Build.0 = Debug|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Release.ActiveCfg = Release|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Release.Build.0 = Release|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Release_Server.ActiveCfg = Release|Win32 + {AB45BA83-4921-4A3C-80CF-1984D49ACA0D}.Release_Server.Build.0 = Release|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Debug.ActiveCfg = Debug|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Debug.Build.0 = Debug|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Debug_Server.Build.0 = Debug_Server|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Release.ActiveCfg = Release|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Release.Build.0 = Release|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Release_Server.ActiveCfg = Release_Server|Win32 + {6B9CDD84-AB0F-4643-8D0B-D0C0714E706F}.Release_Server.Build.0 = Release_Server|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Debug.ActiveCfg = Debug|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Debug.Build.0 = Debug|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Debug_Server.Build.0 = Debug_Server|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Release.ActiveCfg = Release|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Release.Build.0 = Release|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Release_Server.ActiveCfg = Release_Server|Win32 + {CA208971-6C77-47F6-AA4B-FB6ECC071132}.Release_Server.Build.0 = Release_Server|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Debug.ActiveCfg = Debug|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Debug.Build.0 = Debug|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Debug_Server.ActiveCfg = Debug|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Debug_Server.Build.0 = Debug|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Release.ActiveCfg = Release|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Release.Build.0 = Release|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Release_Server.ActiveCfg = Release|Win32 + {CBED4802-1FAD-49E7-BBDC-FC851C27D7ED}.Release_Server.Build.0 = Release|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Debug.ActiveCfg = Debug|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Debug.Build.0 = Debug|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Debug_Server.ActiveCfg = Debug|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Debug_Server.Build.0 = Debug|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Release.ActiveCfg = Release|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Release.Build.0 = Release|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Release_Server.ActiveCfg = Release|Win32 + {32EC294E-2A45-4627-B5C4-B5CD236C2674}.Release_Server.Build.0 = Release|Win32 + {B96E87D6-321A-4A30-B157-899E3B894019}.Debug.ActiveCfg = Debug|Win32 + {B96E87D6-321A-4A30-B157-899E3B894019}.Debug.Build.0 = Debug|Win32 + {B96E87D6-321A-4A30-B157-899E3B894019}.Debug_Server.ActiveCfg = Debug|Win32 + {B96E87D6-321A-4A30-B157-899E3B894019}.Debug_Server.Build.0 = Debug|Win32 + {B96E87D6-321A-4A30-B157-899E3B894019}.Release.ActiveCfg = Release|Win32 + {B96E87D6-321A-4A30-B157-899E3B894019}.Release.Build.0 = Release|Win32 + {B96E87D6-321A-4A30-B157-899E3B894019}.Release_Server.ActiveCfg = Release|Win32 + {B96E87D6-321A-4A30-B157-899E3B894019}.Release_Server.Build.0 = Release|Win32 + {BBDF8302-BE9C-43EC-AAF8-912E601515B3}.Debug.ActiveCfg = Debug|Win32 + {BBDF8302-BE9C-43EC-AAF8-912E601515B3}.Debug.Build.0 = Debug|Win32 + {BBDF8302-BE9C-43EC-AAF8-912E601515B3}.Debug_Server.ActiveCfg = Debug|Win32 + {BBDF8302-BE9C-43EC-AAF8-912E601515B3}.Debug_Server.Build.0 = Debug|Win32 + {BBDF8302-BE9C-43EC-AAF8-912E601515B3}.Release.ActiveCfg = Release|Win32 + {BBDF8302-BE9C-43EC-AAF8-912E601515B3}.Release.Build.0 = Release|Win32 + {BBDF8302-BE9C-43EC-AAF8-912E601515B3}.Release_Server.ActiveCfg = Release|Win32 + {BBDF8302-BE9C-43EC-AAF8-912E601515B3}.Release_Server.Build.0 = Release|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Debug.ActiveCfg = Debug|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Debug.Build.0 = Debug|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Debug_Server.Build.0 = Debug_Server|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Release.ActiveCfg = Release|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Release.Build.0 = Release|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Release_Server.ActiveCfg = Release_Server|Win32 + {430EC1AE-7E25-4039-A83E-09F956285BAC}.Release_Server.Build.0 = Release_Server|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Debug.ActiveCfg = Debug|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Debug.Build.0 = Debug|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Debug_Server.Build.0 = Debug_Server|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Release.ActiveCfg = Release|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Release.Build.0 = Release|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Release_Server.ActiveCfg = Release_Server|Win32 + {28E5E146-D581-4492-9DD7-D56247A9F027}.Release_Server.Build.0 = Release_Server|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Debug.ActiveCfg = Debug|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Debug.Build.0 = Debug|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Debug_Server.ActiveCfg = Debug|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Debug_Server.Build.0 = Debug|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Release.ActiveCfg = Release|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Release.Build.0 = Release|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Release_Server.ActiveCfg = Release|Win32 + {8E44FB70-5EA0-4D33-B829-E4D16D7C11E3}.Release_Server.Build.0 = Release|Win32 + {C28B9838-04AE-4EBD-A93F-A94A64230887}.Debug.ActiveCfg = Debug|Win32 + {C28B9838-04AE-4EBD-A93F-A94A64230887}.Debug.Build.0 = Debug|Win32 + {C28B9838-04AE-4EBD-A93F-A94A64230887}.Debug_Server.ActiveCfg = Debug|Win32 + {C28B9838-04AE-4EBD-A93F-A94A64230887}.Debug_Server.Build.0 = Debug|Win32 + {C28B9838-04AE-4EBD-A93F-A94A64230887}.Release.ActiveCfg = Release|Win32 + {C28B9838-04AE-4EBD-A93F-A94A64230887}.Release.Build.0 = Release|Win32 + {C28B9838-04AE-4EBD-A93F-A94A64230887}.Release_Server.ActiveCfg = Release|Win32 + {C28B9838-04AE-4EBD-A93F-A94A64230887}.Release_Server.Build.0 = Release|Win32 + {1599855E-CC20-4C3A-A382-5290C40BE08E}.Debug.ActiveCfg = Debug|Win32 + {1599855E-CC20-4C3A-A382-5290C40BE08E}.Debug.Build.0 = Debug|Win32 + {1599855E-CC20-4C3A-A382-5290C40BE08E}.Debug_Server.ActiveCfg = Debug|Win32 + {1599855E-CC20-4C3A-A382-5290C40BE08E}.Debug_Server.Build.0 = Debug|Win32 + {1599855E-CC20-4C3A-A382-5290C40BE08E}.Release.ActiveCfg = Release|Win32 + {1599855E-CC20-4C3A-A382-5290C40BE08E}.Release.Build.0 = Release|Win32 + {1599855E-CC20-4C3A-A382-5290C40BE08E}.Release_Server.ActiveCfg = Release|Win32 + {1599855E-CC20-4C3A-A382-5290C40BE08E}.Release_Server.Build.0 = Release|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Debug.ActiveCfg = Debug|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Debug.Build.0 = Debug|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Debug_Server.ActiveCfg = Debug|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Debug_Server.Build.0 = Debug|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Release.ActiveCfg = Release|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Release.Build.0 = Release|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Release_Server.ActiveCfg = Release|Win32 + {5CB24835-47C1-4AE7-900E-70CBD1CC1DEA}.Release_Server.Build.0 = Release|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Debug.ActiveCfg = Debug|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Debug.Build.0 = Debug|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Debug_Server.ActiveCfg = Debug|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Debug_Server.Build.0 = Debug|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Release.ActiveCfg = Release|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Release.Build.0 = Release|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Release_Server.ActiveCfg = Release|Win32 + {D97E0E20-0502-4E63-BD05-41B42EED695F}.Release_Server.Build.0 = Release|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Debug.ActiveCfg = Debug|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Debug.Build.0 = Debug|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Debug_Server.ActiveCfg = Debug|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Debug_Server.Build.0 = Debug|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Release.ActiveCfg = Release|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Release.Build.0 = Release|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Release_Server.ActiveCfg = Release|Win32 + {7D83F872-F06C-4D42-A8F2-D77DC22C5981}.Release_Server.Build.0 = Release|Win32 + {E5E4CE2B-484C-4250-9F8C-F8D5D4570AD3}.Debug.ActiveCfg = Debug|Win32 + {E5E4CE2B-484C-4250-9F8C-F8D5D4570AD3}.Debug.Build.0 = Debug|Win32 + {E5E4CE2B-484C-4250-9F8C-F8D5D4570AD3}.Debug_Server.ActiveCfg = Debug|Win32 + {E5E4CE2B-484C-4250-9F8C-F8D5D4570AD3}.Debug_Server.Build.0 = Debug|Win32 + {E5E4CE2B-484C-4250-9F8C-F8D5D4570AD3}.Release.ActiveCfg = Release|Win32 + {E5E4CE2B-484C-4250-9F8C-F8D5D4570AD3}.Release.Build.0 = Release|Win32 + {E5E4CE2B-484C-4250-9F8C-F8D5D4570AD3}.Release_Server.ActiveCfg = Release|Win32 + {E5E4CE2B-484C-4250-9F8C-F8D5D4570AD3}.Release_Server.Build.0 = Release|Win32 + {FB47BBFF-239D-46B3-98C6-46C3A994B5B7}.Debug.ActiveCfg = Debug|Win32 + {FB47BBFF-239D-46B3-98C6-46C3A994B5B7}.Debug.Build.0 = Debug|Win32 + {FB47BBFF-239D-46B3-98C6-46C3A994B5B7}.Debug_Server.ActiveCfg = Debug|Win32 + {FB47BBFF-239D-46B3-98C6-46C3A994B5B7}.Debug_Server.Build.0 = Debug|Win32 + {FB47BBFF-239D-46B3-98C6-46C3A994B5B7}.Release.ActiveCfg = Release|Win32 + {FB47BBFF-239D-46B3-98C6-46C3A994B5B7}.Release.Build.0 = Release|Win32 + {FB47BBFF-239D-46B3-98C6-46C3A994B5B7}.Release_Server.ActiveCfg = Release|Win32 + {FB47BBFF-239D-46B3-98C6-46C3A994B5B7}.Release_Server.Build.0 = Release|Win32 + {A10FCF60-7425-48D7-9153-CB9D8AD94C8F}.Debug.ActiveCfg = Debug|Win32 + {A10FCF60-7425-48D7-9153-CB9D8AD94C8F}.Debug.Build.0 = Debug|Win32 + {A10FCF60-7425-48D7-9153-CB9D8AD94C8F}.Debug_Server.ActiveCfg = Debug_Server|Win32 + {A10FCF60-7425-48D7-9153-CB9D8AD94C8F}.Debug_Server.Build.0 = Debug_Server|Win32 + {A10FCF60-7425-48D7-9153-CB9D8AD94C8F}.Release.ActiveCfg = Release|Win32 + {A10FCF60-7425-48D7-9153-CB9D8AD94C8F}.Release.Build.0 = Release|Win32 + {A10FCF60-7425-48D7-9153-CB9D8AD94C8F}.Release_Server.ActiveCfg = Release_Server|Win32 + {A10FCF60-7425-48D7-9153-CB9D8AD94C8F}.Release_Server.Build.0 = Release_Server|Win32 + {EED64223-B75A-4476-9F16-6ACE587158CD}.Debug.ActiveCfg = Debug|Win32 + {EED64223-B75A-4476-9F16-6ACE587158CD}.Debug.Build.0 = Debug|Win32 + {EED64223-B75A-4476-9F16-6ACE587158CD}.Debug_Server.ActiveCfg = Debug|Win32 + {EED64223-B75A-4476-9F16-6ACE587158CD}.Debug_Server.Build.0 = Debug|Win32 + {EED64223-B75A-4476-9F16-6ACE587158CD}.Release.ActiveCfg = Release|Win32 + {EED64223-B75A-4476-9F16-6ACE587158CD}.Release.Build.0 = Release|Win32 + {EED64223-B75A-4476-9F16-6ACE587158CD}.Release_Server.ActiveCfg = Release|Win32 + {EED64223-B75A-4476-9F16-6ACE587158CD}.Release_Server.Build.0 = Release|Win32 + {D20A0F2B-6822-4EE7-88C0-37ED22E38583}.Debug.ActiveCfg = Debug|Win32 + {D20A0F2B-6822-4EE7-88C0-37ED22E38583}.Debug.Build.0 = Debug|Win32 + {D20A0F2B-6822-4EE7-88C0-37ED22E38583}.Debug_Server.ActiveCfg = Debug|Win32 + {D20A0F2B-6822-4EE7-88C0-37ED22E38583}.Debug_Server.Build.0 = Debug|Win32 + {D20A0F2B-6822-4EE7-88C0-37ED22E38583}.Release.ActiveCfg = Release|Win32 + {D20A0F2B-6822-4EE7-88C0-37ED22E38583}.Release.Build.0 = Release|Win32 + {D20A0F2B-6822-4EE7-88C0-37ED22E38583}.Release_Server.ActiveCfg = Release|Win32 + {D20A0F2B-6822-4EE7-88C0-37ED22E38583}.Release_Server.Build.0 = Release|Win32 + {4AD6934C-977D-4A01-9FAA-D2C32D96DD0B}.Debug.ActiveCfg = Debug|Win32 + {4AD6934C-977D-4A01-9FAA-D2C32D96DD0B}.Debug.Build.0 = Debug|Win32 + {4AD6934C-977D-4A01-9FAA-D2C32D96DD0B}.Debug_Server.ActiveCfg = Debug|Win32 + {4AD6934C-977D-4A01-9FAA-D2C32D96DD0B}.Debug_Server.Build.0 = Debug|Win32 + {4AD6934C-977D-4A01-9FAA-D2C32D96DD0B}.Release.ActiveCfg = Release|Win32 + {4AD6934C-977D-4A01-9FAA-D2C32D96DD0B}.Release.Build.0 = Release|Win32 + {4AD6934C-977D-4A01-9FAA-D2C32D96DD0B}.Release_Server.ActiveCfg = Release|Win32 + {4AD6934C-977D-4A01-9FAA-D2C32D96DD0B}.Release_Server.Build.0 = Release|Win32 + {E710BB2B-8E1E-434E-8480-10E3AF6DF53B}.Debug.ActiveCfg = Debug|Win32 + {E710BB2B-8E1E-434E-8480-10E3AF6DF53B}.Debug.Build.0 = Debug|Win32 + {E710BB2B-8E1E-434E-8480-10E3AF6DF53B}.Debug_Server.ActiveCfg = Debug|Win32 + {E710BB2B-8E1E-434E-8480-10E3AF6DF53B}.Debug_Server.Build.0 = Debug|Win32 + {E710BB2B-8E1E-434E-8480-10E3AF6DF53B}.Release.ActiveCfg = Release|Win32 + {E710BB2B-8E1E-434E-8480-10E3AF6DF53B}.Release.Build.0 = Release|Win32 + {E710BB2B-8E1E-434E-8480-10E3AF6DF53B}.Release_Server.ActiveCfg = Release|Win32 + {E710BB2B-8E1E-434E-8480-10E3AF6DF53B}.Release_Server.Build.0 = Release|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Debug.ActiveCfg = Debug|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Debug.Build.0 = Debug|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Debug_Server.ActiveCfg = Debug|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Debug_Server.Build.0 = Debug|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Release.ActiveCfg = Release|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Release.Build.0 = Release|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Release_Server.ActiveCfg = Release|Win32 + {2CF2D6A0-6F62-43B5-81FC-BB85DE01BDF8}.Release_Server.Build.0 = Release|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Debug.ActiveCfg = Debug|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Debug.Build.0 = Debug|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Debug_Server.ActiveCfg = Debug|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Debug_Server.Build.0 = Debug|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Release.ActiveCfg = Release|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Release.Build.0 = Release|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Release_Server.ActiveCfg = Release|Win32 + {DE62CF05-8E3E-40A4-967D-330216C2D2ED}.Release_Server.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/AllClient/AllClient.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/AllClient/AllClient.vcproj new file mode 100644 index 00000000..8945f8df --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/AllClient/AllClient.vcproj @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClient/plClient.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClient/plClient.vcproj new file mode 100644 index 00000000..21538e5d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClient/plClient.vcproj @@ -0,0 +1,599 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClientKey/plClientKey.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClientKey/plClientKey.vcproj new file mode 100644 index 00000000..c66f4568 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClientKey/plClientKey.vcproj @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClientPatcher/plClientPatcher.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClientPatcher/plClientPatcher.vcproj new file mode 100644 index 00000000..a21d2a3c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plClientPatcher/plClientPatcher.vcproj @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plFileEncrypt/plFileEncrypt.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plFileEncrypt/plFileEncrypt.vcproj new file mode 100644 index 00000000..582630e9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plFileEncrypt/plFileEncrypt.vcproj @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plFileSecure/plFileSecure.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plFileSecure/plFileSecure.vcproj new file mode 100644 index 00000000..cb954ca6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plFileSecure/plFileSecure.vcproj @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plLogDecrypt/plLogDecrypt.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plLogDecrypt/plLogDecrypt.vcproj new file mode 100644 index 00000000..aa8209cc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plLogDecrypt/plLogDecrypt.vcproj @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plMD5/plMD5.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plMD5/plMD5.vcproj new file mode 100644 index 00000000..91678ff9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plMD5/plMD5.vcproj @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPageInfo/plPageInfo.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPageInfo/plPageInfo.vcproj new file mode 100644 index 00000000..4121b2c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPageInfo/plPageInfo.vcproj @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPageOptimizer/plPageOptimizer.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPageOptimizer/plPageOptimizer.vcproj new file mode 100644 index 00000000..3c40c8bc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPageOptimizer/plPageOptimizer.vcproj @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.vcproj new file mode 100644 index 00000000..a2fbb03f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.vcproj @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.vcproj new file mode 100644 index 00000000..f250f97d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.vcproj @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPythonPack/plPythonPack.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPythonPack/plPythonPack.vcproj new file mode 100644 index 00000000..56a06d43 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plPythonPack/plPythonPack.vcproj @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plUruLauncher/plUruLauncher.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plUruLauncher/plUruLauncher.vcproj new file mode 100644 index 00000000..0f88c00b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/Apps/plUruLauncher/plUruLauncher.vcproj @@ -0,0 +1,275 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/CoreLib/CoreLib.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/CoreLib/CoreLib.vcproj new file mode 100644 index 00000000..9f2cf2f9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/CoreLib/CoreLib.vcproj @@ -0,0 +1,1928 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/CoreLibExe/CoreLibExe.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/CoreLibExe/CoreLibExe.vcproj new file mode 100644 index 00000000..c2557cd3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/CoreLibExe/CoreLibExe.vcproj @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/FeatureLibInc/FeatureLibInc.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/FeatureLibInc/FeatureLibInc.vcproj new file mode 100644 index 00000000..648e6385 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/FeatureLibInc/FeatureLibInc.vcproj @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfAnimation/pfAnimation.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfAnimation/pfAnimation.vcproj new file mode 100644 index 00000000..7f06bd82 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfAnimation/pfAnimation.vcproj @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfAudio/pfAudio.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfAudio/pfAudio.vcproj new file mode 100644 index 00000000..ab49434c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfAudio/pfAudio.vcproj @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCCR/pfCCR.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCCR/pfCCR.vcproj new file mode 100644 index 00000000..44839973 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCCR/pfCCR.vcproj @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCamera/pfCamera.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCamera/pfCamera.vcproj new file mode 100644 index 00000000..548a83aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCamera/pfCamera.vcproj @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCharacter/pfCharacter.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCharacter/pfCharacter.vcproj new file mode 100644 index 00000000..7ec23301 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCharacter/pfCharacter.vcproj @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfConditional/pfConditional.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfConditional/pfConditional.vcproj new file mode 100644 index 00000000..072b6e7e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfConditional/pfConditional.vcproj @@ -0,0 +1,411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfConsole/pfConsole.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfConsole/pfConsole.vcproj new file mode 100644 index 00000000..7a5d57b6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfConsole/pfConsole.vcproj @@ -0,0 +1,373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.vcproj new file mode 100644 index 00000000..0a342e5a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.vcproj @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.vcproj new file mode 100644 index 00000000..dac69c0b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.vcproj @@ -0,0 +1,713 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameMgr/pfGameMgr.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameMgr/pfGameMgr.vcproj new file mode 100644 index 00000000..ba6379a4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameMgr/pfGameMgr.vcproj @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.vcproj new file mode 100644 index 00000000..f8264c9a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.vcproj @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfJournalBook/pfJournalBook.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfJournalBook/pfJournalBook.vcproj new file mode 100644 index 00000000..8771f49e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfJournalBook/pfJournalBook.vcproj @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.vcproj new file mode 100644 index 00000000..48645a57 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.vcproj @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfLoginDialog/pfLoginDialog.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfLoginDialog/pfLoginDialog.vcproj new file mode 100644 index 00000000..c5b253fa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfLoginDialog/pfLoginDialog.vcproj @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfMessage/pfMessage.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfMessage/pfMessage.vcproj new file mode 100644 index 00000000..4526c863 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfMessage/pfMessage.vcproj @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfPython/pfPython.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfPython/pfPython.vcproj new file mode 100644 index 00000000..31dab962 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfPython/pfPython.vcproj @@ -0,0 +1,2332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.vcproj new file mode 100644 index 00000000..a95d0af9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.vcproj @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfStackTrace/pfStackTrace.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfStackTrace/pfStackTrace.vcproj new file mode 100644 index 00000000..cde4fd6b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfStackTrace/pfStackTrace.vcproj @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfSurface/pfSurface.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfSurface/pfSurface.vcproj new file mode 100644 index 00000000..9647c337 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/FeatureLib/pfSurface/pfSurface.vcproj @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.vcproj new file mode 100644 index 00000000..9abeec38 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.vcproj @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAsyncCore/pnAsyncCore.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAsyncCore/pnAsyncCore.vcproj new file mode 100644 index 00000000..bafb8e78 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAsyncCore/pnAsyncCore.vcproj @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAsyncCoreExe/pnAsyncCoreExe.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAsyncCoreExe/pnAsyncCoreExe.vcproj new file mode 100644 index 00000000..229662bc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnAsyncCoreExe/pnAsyncCoreExe.vcproj @@ -0,0 +1,399 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCrash/pnCrash.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCrash/pnCrash.vcproj new file mode 100644 index 00000000..815e1274 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCrash/pnCrash.vcproj @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCrashExe/pnCrashExe.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCrashExe/pnCrashExe.vcproj new file mode 100644 index 00000000..4f991bc3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCrashExe/pnCrashExe.vcproj @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCsrCli/pnCsrCli.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCsrCli/pnCsrCli.vcproj new file mode 100644 index 00000000..fdf7ccb1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCsrCli/pnCsrCli.vcproj @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCsrNet/pnCsrNet.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCsrNet/pnCsrNet.vcproj new file mode 100644 index 00000000..7d227286 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnCsrNet/pnCsrNet.vcproj @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnDispatch/pnDispatch.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnDispatch/pnDispatch.vcproj new file mode 100644 index 00000000..6d68b194 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnDispatch/pnDispatch.vcproj @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnFactory/pnFactory.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnFactory/pnFactory.vcproj new file mode 100644 index 00000000..a8454029 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnFactory/pnFactory.vcproj @@ -0,0 +1,347 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnGameMgr/pnGameMgr.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnGameMgr/pnGameMgr.vcproj new file mode 100644 index 00000000..14ba1b7c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnGameMgr/pnGameMgr.vcproj @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnIni/pnIni.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnIni/pnIni.vcproj new file mode 100644 index 00000000..52dc0c48 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnIni/pnIni.vcproj @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnIniExe/pnIniExe.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnIniExe/pnIniExe.vcproj new file mode 100644 index 00000000..be7f78ab --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnIniExe/pnIniExe.vcproj @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnInputCore/pnInputCore.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnInputCore/pnInputCore.vcproj new file mode 100644 index 00000000..59a55c87 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnInputCore/pnInputCore.vcproj @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnKeyedObject/pnKeyedObject.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnKeyedObject/pnKeyedObject.vcproj new file mode 100644 index 00000000..3aba905b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnKeyedObject/pnKeyedObject.vcproj @@ -0,0 +1,590 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnMail/pnMail.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnMail/pnMail.vcproj new file mode 100644 index 00000000..62070c29 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnMail/pnMail.vcproj @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnMessage/pnMessage.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnMessage/pnMessage.vcproj new file mode 100644 index 00000000..5420db3f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnMessage/pnMessage.vcproj @@ -0,0 +1,994 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnModifier/pnModifier.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnModifier/pnModifier.vcproj new file mode 100644 index 00000000..1ec6b050 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnModifier/pnModifier.vcproj @@ -0,0 +1,251 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetBase/pnNetBase.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetBase/pnNetBase.vcproj new file mode 100644 index 00000000..ebd28be9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetBase/pnNetBase.vcproj @@ -0,0 +1,365 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetCli/pnNetCli.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetCli/pnNetCli.vcproj new file mode 100644 index 00000000..da27c158 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetCli/pnNetCli.vcproj @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetCommon/pnNetCommon.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetCommon/pnNetCommon.vcproj new file mode 100644 index 00000000..3659f4b0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetCommon/pnNetCommon.vcproj @@ -0,0 +1,791 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetDiag/pnNetDiag.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetDiag/pnNetDiag.vcproj new file mode 100644 index 00000000..d8f38340 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetDiag/pnNetDiag.vcproj @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetLog/pnNetLog.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetLog/pnNetLog.vcproj new file mode 100644 index 00000000..5b41cf25 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetLog/pnNetLog.vcproj @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetProtocol/pnNetProtocol.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetProtocol/pnNetProtocol.vcproj new file mode 100644 index 00000000..e65d6025 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNetProtocol/pnNetProtocol.vcproj @@ -0,0 +1,449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNucleusInc/pnNucleusInc.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNucleusInc/pnNucleusInc.vcproj new file mode 100644 index 00000000..81b05bd8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnNucleusInc/pnNucleusInc.vcproj @@ -0,0 +1,438 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnOraLib/pnOraLib.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnOraLib/pnOraLib.vcproj new file mode 100644 index 00000000..262ce0cc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnOraLib/pnOraLib.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnProduct/pnProduct.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnProduct/pnProduct.vcproj new file mode 100644 index 00000000..e38824ed --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnProduct/pnProduct.vcproj @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSceneObject/pnSceneObject.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSceneObject/pnSceneObject.vcproj new file mode 100644 index 00000000..68568965 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSceneObject/pnSceneObject.vcproj @@ -0,0 +1,484 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.vcproj new file mode 100644 index 00000000..97f4683a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.vcproj @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSqlLib/pnSqlLib.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSqlLib/pnSqlLib.vcproj new file mode 100644 index 00000000..0ead0235 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSqlLib/pnSqlLib.vcproj @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSrvUtils/pnSrvUtils.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSrvUtils/pnSrvUtils.vcproj new file mode 100644 index 00000000..c2184de5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSrvUtils/pnSrvUtils.vcproj @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSrvUtilsExe/pnSrvUtilsExe.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSrvUtilsExe/pnSrvUtilsExe.vcproj new file mode 100644 index 00000000..526ba3aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnSrvUtilsExe/pnSrvUtilsExe.vcproj @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnTimer/pnTimer.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnTimer/pnTimer.vcproj new file mode 100644 index 00000000..f863510a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnTimer/pnTimer.vcproj @@ -0,0 +1,534 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnUtils/pnUtils.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnUtils/pnUtils.vcproj new file mode 100644 index 00000000..a7b3e47e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnUtils/pnUtils.vcproj @@ -0,0 +1,505 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnUtilsExe/pnUtilsExe.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnUtilsExe/pnUtilsExe.vcproj new file mode 100644 index 00000000..14b6a3c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/NucleusLib/pnUtilsExe/pnUtilsExe.vcproj @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/PubUtilInc/PubUtilInc.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/PubUtilInc/PubUtilInc.vcproj new file mode 100644 index 00000000..ca022c66 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/PubUtilInc/PubUtilInc.vcproj @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAgeDescription/plAgeDescription.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAgeDescription/plAgeDescription.vcproj new file mode 100644 index 00000000..302d90a6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAgeDescription/plAgeDescription.vcproj @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.vcproj new file mode 100644 index 00000000..22b5fcb6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.vcproj @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudible/plAudible.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudible/plAudible.vcproj new file mode 100644 index 00000000..828c4cf9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudible/plAudible.vcproj @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudio/plAudio.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudio/plAudio.vcproj new file mode 100644 index 00000000..cfdecb5a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudio/plAudio.vcproj @@ -0,0 +1,460 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcproj new file mode 100644 index 00000000..4586104b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcproj @@ -0,0 +1,522 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAvatar/plAvatar.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAvatar/plAvatar.vcproj new file mode 100644 index 00000000..c7b0f17f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plAvatar/plAvatar.vcproj @@ -0,0 +1,1063 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plCompression/plCompression.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plCompression/plCompression.vcproj new file mode 100644 index 00000000..d018fe4d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plCompression/plCompression.vcproj @@ -0,0 +1,529 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plContainer/plContainer.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plContainer/plContainer.vcproj new file mode 100644 index 00000000..4c0a79c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plContainer/plContainer.vcproj @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.vcproj new file mode 100644 index 00000000..9c795a59 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.vcproj @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plDrawable/plDrawable.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plDrawable/plDrawable.vcproj new file mode 100644 index 00000000..46801edc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plDrawable/plDrawable.vcproj @@ -0,0 +1,1122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plEncryption/plEncryption.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plEncryption/plEncryption.vcproj new file mode 100644 index 00000000..45129677 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plEncryption/plEncryption.vcproj @@ -0,0 +1,460 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plFile/plFile.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plFile/plFile.vcproj new file mode 100644 index 00000000..b5ddff1e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plFile/plFile.vcproj @@ -0,0 +1,986 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGClip/plGClip.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGClip/plGClip.vcproj new file mode 100644 index 00000000..eee68568 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGClip/plGClip.vcproj @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGImage/plGImage.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGImage/plGImage.vcproj new file mode 100644 index 00000000..18e8d43b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGImage/plGImage.vcproj @@ -0,0 +1,483 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGLight/plGLight.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGLight/plGLight.vcproj new file mode 100644 index 00000000..d4e2665a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGLight/plGLight.vcproj @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGeometry/plGeometry.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGeometry/plGeometry.vcproj new file mode 100644 index 00000000..4332cc85 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plGeometry/plGeometry.vcproj @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plInputCore/plInputCore.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plInputCore/plInputCore.vcproj new file mode 100644 index 00000000..6a99d8df --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plInputCore/plInputCore.vcproj @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plInterp/plInterp.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plInterp/plInterp.vcproj new file mode 100644 index 00000000..5a060e2a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plInterp/plInterp.vcproj @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plIntersect/plIntersect.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plIntersect/plIntersect.vcproj new file mode 100644 index 00000000..38de9949 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plIntersect/plIntersect.vcproj @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plJPEG/plJPEG.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plJPEG/plJPEG.vcproj new file mode 100644 index 00000000..a5cf7fca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plJPEG/plJPEG.vcproj @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plMath/plMath.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plMath/plMath.vcproj new file mode 100644 index 00000000..9cc787d1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plMath/plMath.vcproj @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plMessage/plMessage.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plMessage/plMessage.vcproj new file mode 100644 index 00000000..ede1dd7a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plMessage/plMessage.vcproj @@ -0,0 +1,2780 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plModifier/plModifier.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plModifier/plModifier.vcproj new file mode 100644 index 00000000..5467a260 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plModifier/plModifier.vcproj @@ -0,0 +1,572 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClient/PlNetClient.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClient/PlNetClient.vcproj new file mode 100644 index 00000000..93eb2286 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClient/PlNetClient.vcproj @@ -0,0 +1,474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.vcproj new file mode 100644 index 00000000..f7775405 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.vcproj @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.vcproj new file mode 100644 index 00000000..cc7c8dce --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.vcproj @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetCommon/plNetCommon.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetCommon/plNetCommon.vcproj new file mode 100644 index 00000000..3fdca2d3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetCommon/plNetCommon.vcproj @@ -0,0 +1,599 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetGameLib/plNetGameLib.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetGameLib/plNetGameLib.vcproj new file mode 100644 index 00000000..e62e3bf5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetGameLib/plNetGameLib.vcproj @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetMessage/plNetMessage.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetMessage/plNetMessage.vcproj new file mode 100644 index 00000000..2a083f6d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetMessage/plNetMessage.vcproj @@ -0,0 +1,402 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetTransport/plNetTransport.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetTransport/plNetTransport.vcproj new file mode 100644 index 00000000..759d06ce --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plNetTransport/plNetTransport.vcproj @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.vcproj new file mode 100644 index 00000000..42a88925 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.vcproj @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPhysX/plPhysX.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPhysX/plPhysX.vcproj new file mode 100644 index 00000000..9c2ca82c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPhysX/plPhysX.vcproj @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPhysical/plPhysical.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPhysical/plPhysical.vcproj new file mode 100644 index 00000000..b41c3b43 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPhysical/plPhysical.vcproj @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPipeline/plPipeline.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPipeline/plPipeline.vcproj new file mode 100644 index 00000000..b903d168 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plPipeline/plPipeline.vcproj @@ -0,0 +1,711 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.vcproj new file mode 100644 index 00000000..f1780163 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.vcproj @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plResMgr/plResMgr.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plResMgr/plResMgr.vcproj new file mode 100644 index 00000000..1b4138f4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plResMgr/plResMgr.vcproj @@ -0,0 +1,747 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSDL/plSDL.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSDL/plSDL.vcproj new file mode 100644 index 00000000..0dace3e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSDL/plSDL.vcproj @@ -0,0 +1,655 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plScene/plScene.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plScene/plScene.vcproj new file mode 100644 index 00000000..4638071e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plScene/plScene.vcproj @@ -0,0 +1,388 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSockets/plSockets.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSockets/plSockets.vcproj new file mode 100644 index 00000000..02990921 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSockets/plSockets.vcproj @@ -0,0 +1,362 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStatGather/plStatGather.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStatGather/plStatGather.vcproj new file mode 100644 index 00000000..ad8baffb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStatGather/plStatGather.vcproj @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStatusLog/plStatusLog.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStatusLog/plStatusLog.vcproj new file mode 100644 index 00000000..6066c06b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStatusLog/plStatusLog.vcproj @@ -0,0 +1,440 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.vcproj new file mode 100644 index 00000000..c9cbea5d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.vcproj @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSurface/plSurface.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSurface/plSurface.vcproj new file mode 100644 index 00000000..b5cd217e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plSurface/plSurface.vcproj @@ -0,0 +1,475 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plTransform/plTransform.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plTransform/plTransform.vcproj new file mode 100644 index 00000000..2143104f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plTransform/plTransform.vcproj @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plUUID/plUUID.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plUUID/plUUID.vcproj new file mode 100644 index 00000000..43958616 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plUUID/plUUID.vcproj @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.vcproj new file mode 100644 index 00000000..d147b105 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.vcproj @@ -0,0 +1,591 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plVault/plVault.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plVault/plVault.vcproj new file mode 100644 index 00000000..f06eb3d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plVault/plVault.vcproj @@ -0,0 +1,385 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plWinStrBlock/plWinStrBlock.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plWinStrBlock/plWinStrBlock.vcproj new file mode 100644 index 00000000..f3ee67c7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plWinStrBlock/plWinStrBlock.vcproj @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.vcproj new file mode 100644 index 00000000..61ed5560 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.vcproj @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.vcproj new file mode 100644 index 00000000..10a302e6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.vcproj @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyPlasma/pyPlasma.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyPlasma/pyPlasma.vcproj new file mode 100644 index 00000000..6b169309 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyPlasma/pyPlasma.vcproj @@ -0,0 +1,964 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyPloticus/pyPloticus.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyPloticus/pyPloticus.vcproj new file mode 100644 index 00000000..11ee99ce --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyPloticus/pyPloticus.vcproj @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyVault/pyVault.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyVault/pyVault.vcproj new file mode 100644 index 00000000..398a4bb3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Plasma/PythonLib/pyVault/pyVault.vcproj @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxComponent/MaxComponent.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxComponent/MaxComponent.vcproj new file mode 100644 index 00000000..90319aae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxComponent/MaxComponent.vcproj @@ -0,0 +1,1908 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxConvert/MaxConvert.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxConvert/MaxConvert.vcproj new file mode 100644 index 00000000..0361a957 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxConvert/MaxConvert.vcproj @@ -0,0 +1,503 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxExport/MaxExport.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxExport/MaxExport.vcproj new file mode 100644 index 00000000..94c64815 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxExport/MaxExport.vcproj @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxMain/MaxMain.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxMain/MaxMain.vcproj new file mode 100644 index 00000000..3b8c6203 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxMain/MaxMain.vcproj @@ -0,0 +1,959 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxPlasmaLights/MaxPlasmaLights.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxPlasmaLights/MaxPlasmaLights.vcproj new file mode 100644 index 00000000..a63f70de --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxPlasmaLights/MaxPlasmaLights.vcproj @@ -0,0 +1,430 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxPlasmaMtls/MaxPlasmaMtls.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxPlasmaMtls/MaxPlasmaMtls.vcproj new file mode 100644 index 00000000..2ac92b52 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxPlasmaMtls/MaxPlasmaMtls.vcproj @@ -0,0 +1,1046 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxSceneViewer/MaxSceneViewer.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxSceneViewer/MaxSceneViewer.vcproj new file mode 100644 index 00000000..42d30342 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/MaxSceneViewer/MaxSceneViewer.vcproj @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/plFontConverter/plFontConverter.vcproj b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/plFontConverter/plFontConverter.vcproj new file mode 100644 index 00000000..d7f70a73 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/MsDevProjects/Tools/plFontConverter/plFontConverter.vcproj @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/lib/HawkVoiceDIstatic.lib b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/lib/HawkVoiceDIstatic.lib new file mode 100644 index 0000000000000000000000000000000000000000..16b3e0d5b1e7d243a96bb7ff2d3747342169db03 GIT binary patch literal 189518 zcmd>n34Baf`~RKHWRZjkf*=ST8bY?oY8^`=G>9drs;o1FNXTMogD|2=MpRo}ELFO^ z)vD5>mLi$N7FA86)KV3+im0L`vF87M&b^b|nao7`>ihQneg0=YckWr9^PJ~w&w0*s zmfNv+OiJ98zV=>j#wIve=@Z}`5a?~}?PhQX`1u9#LDBXc$5nD%U;zL0{h8y!vN
  • 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 b3*P)s=Edv~=0&zN_hNRDd+AhxOQGuD${^)V literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Blowfish.001 b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Blowfish.001 new file mode 100644 index 00000000..d5cbae6d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Blowfish.001 @@ -0,0 +1,122 @@ +# Microsoft Developer Studio Project File - Name="blowfish" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=blowfish - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Blowfish.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Blowfish.mak" CFG="blowfish - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "blowfish - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "blowfish - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe + +!IF "$(CFG)" == "blowfish - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /GB /Za /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "blowfish - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /GB /Za /W4 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "blowfish - Win32 Release" +# Name "blowfish - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\bf_cbc.c +# End Source File +# Begin Source File + +SOURCE=.\bf_cfb64.c +# End Source File +# Begin Source File + +SOURCE=.\bf_ecb.c +# End Source File +# Begin Source File + +SOURCE=.\bf_enc.c +# End Source File +# Begin Source File + +SOURCE=.\bf_ofb64.c +# End Source File +# Begin Source File + +SOURCE=.\bf_skey.c +# End Source File +# End Group +# Begin Group "Include Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\bf_locl.h +# End Source File +# Begin Source File + +SOURCE=.\bf_pi.h +# End Source File +# Begin Source File + +SOURCE=.\blowfish.h +# End Source File +# End Group +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Blowfish.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Blowfish.dsp new file mode 100644 index 00000000..f55ac27f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Blowfish.dsp @@ -0,0 +1,128 @@ +# Microsoft Developer Studio Project File - Name="blowfish" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=blowfish - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Blowfish.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Blowfish.mak" CFG="blowfish - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "blowfish - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "blowfish - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "blowfish - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /Za /W4 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "blowfish - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /Za /W4 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "blowfish - Win32 Release" +# Name "blowfish - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter ".c" +# Begin Source File + +SOURCE=.\bf_cbc.c +# End Source File +# Begin Source File + +SOURCE=.\bf_cfb64.c +# End Source File +# Begin Source File + +SOURCE=.\bf_ecb.c +# End Source File +# Begin Source File + +SOURCE=.\bf_enc.c +# End Source File +# Begin Source File + +SOURCE=.\bf_ofb64.c +# End Source File +# Begin Source File + +SOURCE=.\bf_skey.c +# End Source File +# End Group +# Begin Group "Include Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\bf_locl.h +# End Source File +# Begin Source File + +SOURCE=.\bf_pi.h +# End Source File +# Begin Source File + +SOURCE=.\blowfish.h +# End Source File +# End Group +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/COPYRIGHT b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/COPYRIGHT new file mode 100644 index 00000000..00529e75 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/COPYRIGHT @@ -0,0 +1,46 @@ +Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) +All rights reserved. + +This package is an Blowfish implementation written +by Eric Young (eay@mincom.oz.au). + +This library is free for commercial and non-commercial use as long as +the following conditions are aheared to. The following conditions +apply to all code found in this distribution. + +Copyright remains Eric Young's, and as such any Copyright notices in +the code are not to be removed. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by Eric Young (eay@mincom.oz.au) + +THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +The license and distribution terms for any publically available version or +derivative of this code cannot be changed. i.e. this code cannot simply be +copied and put under another distrubution license +[including the GNU Public License.] + +The reason behind this being stated in this direct manner is past +experience in code simply being copied and the attribution removed +from it and then being distributed as part of other packages. This +implementation was a non-trivial and unpaid effort. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/INSTALL b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/INSTALL new file mode 100644 index 00000000..fa86cb45 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/INSTALL @@ -0,0 +1,14 @@ +This Eric Young's blowfish implementation, taken from his SSLeay library +and made available as a separate library. + +The version number (0.7.2m) is the SSLeay version that this library was +taken from. + +To build, just unpack and type make. +If you are not using gcc, edit the Makefile. +If you are compiling for an x86 box, try the assembler (it needs improving). +There are also some compile time options that can improve performance, +these are documented in the Makefile. + +eric 15-Apr-1997 + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile new file mode 100644 index 00000000..03e910aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile @@ -0,0 +1,160 @@ +# Targets +# make - twidle the options yourself :-) +# make cc - standard cc options +# make gcc - standard gcc options +# make x86-elf - linux-elf etc +# make x86-out - linux-a.out, FreeBSD etc +# make x86-solaris +# make x86-bdsi + +# use BF_PTR2 for intel boxes, +# BF_PTR for sparc and MIPS/SGI +# use nothing for Alpha and HP. + +# There are 3 possible performance options, experiment :-) +#OPTS= -DBF_PTR # usr for sparc and MIPS/SGI +#OPTS= -DBF_PTR2 # use for pentium +#OPTS= # use for pentium pro, Alpha and HP + +MAKE=make -f Makefile +CC=cc +CFLAG= -O + +#CC=gcc +#CFLAG= -O4 -funroll-loops -fomit-frame-pointer +#CFLAG= -O3 -fomit-frame-pointer + +CFLAGS=$(OPTS) $(CFLAG) +CPP=$(CC) -E +AS=as + +# Assember version of bf_encrypt(). +BF_ENC=bf_enc.o # normal C version +#BF_ENC=asm/bx86-elf.o # elf format x86 +#BF_ENC=asm/bx86-out.o # a.out format x86 +#BF_ENC=asm/bx86-sol.o # solaris format x86 +#BF_ENC=asm/bx86bsdi.o # bsdi format x86 + +LIBDIR=/usr/local/lib +BINDIR=/usr/local/bin +INCDIR=/usr/local/include +MANDIR=/usr/local/man +MAN1=1 +MAN3=3 +SHELL=/bin/sh +LIBOBJ=bf_skey.o bf_ecb.o $(BF_ENC) bf_cbc.o bf_cfb64.o bf_ofb64.o +LIBSRC=bf_skey.c bf_ecb.c bf_enc.c bf_cbc.c bf_cfb64.c bf_ofb64.c + +GENERAL=Makefile Makefile.ssl Makefile.uni asm bf_locl.org README \ + COPYRIGHT INSTALL blowfish.doc + +TESTING= bftest bfspeed +TESTING_SRC=bftest.c bfspeed.c +HEADERS=bf_locl.h blowfish.h bf_pi.h + +ALL= $(GENERAL) $(TESTING_SRC) $(LIBSRC) $(HEADERS) + +BLIB= libblowfish.a + +all: $(BLIB) $(TESTING) + +cc: + $(MAKE) CC=cc CFLAGS="-O $(OPTS) $(CFLAG)" all + +gcc: + $(MAKE) CC=gcc CFLAGS="-O3 -fomit-frame-pointer $(OPTS) $(CFLAG)" all + +x86-elf: + $(MAKE) BF_ENC='asm/bx86-elf.o' CC=$(CC) CFLAGS="-DELF $(OPTS) $(CFLAG)" all + +x86-out: + $(MAKE) BF_ENC='asm/bx86-out.o' CC=$(CC) CFLAGS="-DOUT $(OPTS) $(CFLAG)" all + +x86-solaris: + $(MAKE) BF_ENC='asm/bx86-sol.o' CC=$(CC) CFLAGS="-DSOL $(OPTS) $(CFLAG)" all + +x86-bsdi: + $(MAKE) BF_ENC='asm/bx86bsdi.o' CC=$(CC) CFLAGS="-DBSDI $(OPTS) $(CFLAG)" all + +# elf +asm/bx86-elf.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CPP) -DELF asm/bx86unix.cpp | $(AS) -o asm/bx86-elf.o + +# solaris +asm/bx86-sol.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CC) -E -DSOL asm/bx86unix.cpp | sed 's/^#.*//' > asm/bx86-sol.s + as -o asm/bx86-sol.o asm/bx86-sol.s + rm -f asm/bx86-sol.s + +# a.out +asm/bx86-out.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CPP) -DOUT asm/bx86unix.cpp | $(AS) -o asm/bx86-out.o + +# bsdi +asm/bx86bsdi.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CPP) -DBSDI asm/bx86unix.cpp | $(AS) -o asm/bx86bsdi.o + +test: all + ./bftest + +$(BLIB): $(LIBOBJ) + /bin/rm -f $(BLIB) + ar cr $(BLIB) $(LIBOBJ) + -if test -s /bin/ranlib; then /bin/ranlib $(BLIB); \ + else if test -s /usr/bin/ranlib; then /usr/bin/ranlib $(BLIB); \ + else exit 0; fi; fi + +bftest: bftest.o $(BLIB) + $(CC) $(CFLAGS) -o bftest bftest.o $(BLIB) + +bfspeed: bfspeed.o $(BLIB) + $(CC) $(CFLAGS) -o bfspeed bfspeed.o $(BLIB) + +tags: + ctags $(TESTING_SRC) $(LIBBF) + +tar: + tar chf libbf.tar $(ALL) + +shar: + shar $(ALL) >libbf.shar + +depend: + makedepend $(LIBBF) $(TESTING_SRC) + +clean: + rm -f *.o tags core $(TESTING) $(BLIB) .nfs* *.old *.bak asm/*.o + +dclean: + sed -e '/^# DO NOT DELETE THIS LINE/ q' Makefile >Makefile.new + mv -f Makefile.new Makefile + +# Eric is probably going to choke when he next looks at this --tjh +install: $(BLIB) + if test $(INSTALLTOP); then \ + echo SSL style install; \ + cp $(BLIB) $(INSTALLTOP)/lib; \ + if test -s /bin/ranlib; then \ + /bin/ranlib $(INSTALLTOP)/lib/$(BLIB); \ + else \ + if test -s /usr/bin/ranlib; then \ + /usr/bin/ranlib $(INSTALLTOP)/lib/$(BLIB); \ + fi; fi; \ + chmod 644 $(INSTALLTOP)/lib/$(BLIB); \ + cp blowfish.h $(INSTALLTOP)/include; \ + chmod 644 $(INSTALLTOP)/include/blowfish.h; \ + else \ + echo Standalone install; \ + cp $(BLIB) $(LIBDIR)/$(BLIB); \ + if test -s /bin/ranlib; then \ + /bin/ranlib $(LIBDIR)/$(BLIB); \ + else \ + if test -s /usr/bin/ranlib; then \ + /usr/bin/ranlib $(LIBDIR)/$(BLIB); \ + fi; \ + fi; \ + chmod 644 $(LIBDIR)/$(BLIB); \ + cp blowfish.h $(INCDIR)/blowfish.h; \ + chmod 644 $(INCDIR)/blowfish.h; \ + fi +# DO NOT DELETE THIS LINE -- make depend depends on it. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile.ssl b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile.ssl new file mode 100644 index 00000000..e44786bf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile.ssl @@ -0,0 +1,104 @@ +# +# SSLeay/crypto/blowfish/Makefile +# + +DIR= bf +TOP= ../.. +CC= cc +CPP= $(CC) -E +INCLUDES= +CFLAG=-g +INSTALLTOP=/usr/local/ssl +MAKE= make -f Makefile.ssl +MAKEDEPEND= makedepend -fMakefile.ssl +MAKEFILE= Makefile.ssl + +BF_ENC= bf_enc.o +# or use +#DES_ENC= bx86-elf.o + +CFLAGS= $(INCLUDES) $(CFLAG) + +GENERAL=Makefile +TEST=bftest.c +APPS= + +LIB=$(TOP)/libcrypto.a +LIBSRC=bf_skey.c bf_ecb.c bf_enc.c bf_cbc.c bf_cfb64.c bf_ofb64.c +LIBOBJ=bf_skey.o bf_ecb.o $(BF_ENC) bf_cbc.o bf_cfb64.o bf_ofb64.o + +SRC= $(LIBSRC) + +EXHEADER= blowfish.h +HEADER= bf_pi.h bf_locl.h $(EXHEADER) + +ALL= $(GENERAL) $(SRC) $(HEADER) + +top: + (cd ../..; $(MAKE) DIRS=crypto SDIRS=$(DIR) sub_all) + +all: lib + +lib: $(LIBOBJ) + ar r $(LIB) $(LIBOBJ) + sh $(TOP)/util/ranlib.sh $(LIB) + @touch lib + +# elf +asm/bx86-elf.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CPP) -DELF asm/bx86unix.cpp | as -o asm/bx86-elf.o + +# solaris +asm/bx86-sol.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CC) -E -DSOL asm/bx86unix.cpp | sed 's/^#.*//' > asm/bx86-sol.s + as -o asm/bx86-sol.o asm/bx86-sol.s + rm -f asm/bx86-sol.s + +# a.out +asm/bx86-out.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CPP) -DOUT asm/bx86unix.cpp | as -o asm/bx86-out.o + +# bsdi +asm/bx86bsdi.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CPP) -DBSDI asm/bx86unix.cpp | as -o asm/bx86bsdi.o + +files: + perl $(TOP)/util/files.pl Makefile.ssl >> $(TOP)/MINFO + +links: + /bin/rm -f Makefile + $(TOP)/util/point.sh Makefile.ssl Makefile ; + /bin/rm -f des.doc + $(TOP)/util/point.sh ../../doc/blowfish.doc blowfish.doc ; + $(TOP)/util/mklink.sh ../../include $(EXHEADER) + $(TOP)/util/mklink.sh ../../test $(TEST) + $(TOP)/util/mklink.sh ../../apps $(APPS) + +install: + @for i in $(EXHEADER) ; \ + do \ + (cp $$i $(INSTALLTOP)/include/$$i; \ + chmod 644 $(INSTALLTOP)/include/$$i ); \ + done; + +tags: + ctags $(SRC) + +tests: + +lint: + lint -DLINT $(INCLUDES) $(SRC)>fluff + +depend: + $(MAKEDEPEND) $(INCLUDES) $(PROGS) $(LIBSRC) + +dclean: + perl -pe 'if (/^# DO NOT DELETE THIS LINE/) {print; exit(0);}' $(MAKEFILE) >Makefile.new + mv -f Makefile.new $(MAKEFILE) + +clean: + /bin/rm -f *.o asm/*.o *.obj lib tags core .pure .nfs* *.old *.bak fluff + +errors: + +# DO NOT DELETE THIS LINE -- make depend depends on it. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile.uni b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile.uni new file mode 100644 index 00000000..b0d2e26d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/Makefile.uni @@ -0,0 +1,160 @@ +# Targets +# make - twidle the options yourself :-) +# make cc - standard cc options +# make gcc - standard gcc options +# make x86-elf - linux-elf etc +# make x86-out - linux-a.out, FreeBSD etc +# make x86-solaris +# make x86-bdsi + +# use BF_PTR2 for intel boxes, +# BF_PTR for sparc and MIPS/SGI +# use nothing for Alpha and HP. + +# There are 3 possible performance options, experiment :-) +OPTS= -DBF_PTR # usr for sparc and MIPS/SGI +#OPTS= -DBF_PTR2 # use for pentium +#OPTS= # use for pentium pro, Alpha and HP + +MAKE=make -f Makefile +#CC=cc +#CFLAG= -O + +CC=gcc +#CFLAG= -O4 -funroll-loops -fomit-frame-pointer +CFLAG= -O3 -fomit-frame-pointer + +CFLAGS=$(OPTS) $(CFLAG) +CPP=$(CC) -E +AS=as + +# Assember version of bf_encrypt(). +BF_ENC=bf_enc.o # normal C version +#BF_ENC=asm/bx86-elf.o # elf format x86 +#BF_ENC=asm/bx86-out.o # a.out format x86 +#BF_ENC=asm/bx86-sol.o # solaris format x86 +#BF_ENC=asm/bx86bsdi.o # bsdi format x86 + +LIBDIR=/usr/local/lib +BINDIR=/usr/local/bin +INCDIR=/usr/local/include +MANDIR=/usr/local/man +MAN1=1 +MAN3=3 +SHELL=/bin/sh +LIBOBJ=bf_skey.o bf_ecb.o $(BF_ENC) bf_cbc.o bf_cfb64.o bf_ofb64.o +LIBSRC=bf_skey.c bf_ecb.c bf_enc.c bf_cbc.c bf_cfb64.c bf_ofb64.c + +GENERAL=Makefile Makefile.ssl Makefile.uni asm bf_locl.org README \ + COPYRIGHT INSTALL blowfish.doc + +TESTING= bftest bfspeed +TESTING_SRC=bftest.c bfspeed.c +HEADERS=bf_locl.h blowfish.h bf_pi.h + +ALL= $(GENERAL) $(TESTING_SRC) $(LIBSRC) $(HEADERS) + +BLIB= libblowfish.a + +all: $(BLIB) $(TESTING) + +cc: + $(MAKE) CC=cc CFLAGS="-O $(OPTS) $(CFLAG)" all + +gcc: + $(MAKE) CC=gcc CFLAGS="-O3 -fomit-frame-pointer $(OPTS) $(CFLAG)" all + +x86-elf: + $(MAKE) BF_ENC='asm/bx86-elf.o' CC=$(CC) CFLAGS="-DELF $(OPTS) $(CFLAG)" all + +x86-out: + $(MAKE) BF_ENC='asm/bx86-out.o' CC=$(CC) CFLAGS="-DOUT $(OPTS) $(CFLAG)" all + +x86-solaris: + $(MAKE) BF_ENC='asm/bx86-sol.o' CC=$(CC) CFLAGS="-DSOL $(OPTS) $(CFLAG)" all + +x86-bsdi: + $(MAKE) BF_ENC='asm/bx86bsdi.o' CC=$(CC) CFLAGS="-DBSDI $(OPTS) $(CFLAG)" all + +# elf +asm/bx86-elf.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CPP) -DELF asm/bx86unix.cpp | $(AS) -o asm/bx86-elf.o + +# solaris +asm/bx86-sol.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CC) -E -DSOL asm/bx86unix.cpp | sed 's/^#.*//' > asm/bx86-sol.s + as -o asm/bx86-sol.o asm/bx86-sol.s + rm -f asm/bx86-sol.s + +# a.out +asm/bx86-out.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CPP) -DOUT asm/bx86unix.cpp | $(AS) -o asm/bx86-out.o + +# bsdi +asm/bx86bsdi.o: asm/bx86-cpp.s asm/bx86unix.cpp + $(CPP) -DBSDI asm/bx86unix.cpp | $(AS) -o asm/bx86bsdi.o + +test: all + ./bftest + +$(BLIB): $(LIBOBJ) + /bin/rm -f $(BLIB) + ar cr $(BLIB) $(LIBOBJ) + -if test -s /bin/ranlib; then /bin/ranlib $(BLIB); \ + else if test -s /usr/bin/ranlib; then /usr/bin/ranlib $(BLIB); \ + else exit 0; fi; fi + +bftest: bftest.o $(BLIB) + $(CC) $(CFLAGS) -o bftest bftest.o $(BLIB) + +bfspeed: bfspeed.o $(BLIB) + $(CC) $(CFLAGS) -o bfspeed bfspeed.o $(BLIB) + +tags: + ctags $(TESTING_SRC) $(LIBBF) + +tar: + tar chf libbf.tar $(ALL) + +shar: + shar $(ALL) >libbf.shar + +depend: + makedepend $(LIBBF) $(TESTING_SRC) + +clean: + /bin/rm -f *.o tags core $(TESTING) $(BLIB) .nfs* *.old *.bak asm/*.o + +dclean: + sed -e '/^# DO NOT DELETE THIS LINE/ q' Makefile >Makefile.new + mv -f Makefile.new Makefile + +# Eric is probably going to choke when he next looks at this --tjh +install: $(BLIB) + if test $(INSTALLTOP); then \ + echo SSL style install; \ + cp $(BLIB) $(INSTALLTOP)/lib; \ + if test -s /bin/ranlib; then \ + /bin/ranlib $(INSTALLTOP)/lib/$(BLIB); \ + else \ + if test -s /usr/bin/ranlib; then \ + /usr/bin/ranlib $(INSTALLTOP)/lib/$(BLIB); \ + fi; fi; \ + chmod 644 $(INSTALLTOP)/lib/$(BLIB); \ + cp blowfish.h $(INSTALLTOP)/include; \ + chmod 644 $(INSTALLTOP)/include/blowfish.h; \ + else \ + echo Standalone install; \ + cp $(BLIB) $(LIBDIR)/$(BLIB); \ + if test -s /bin/ranlib; then \ + /bin/ranlib $(LIBDIR)/$(BLIB); \ + else \ + if test -s /usr/bin/ranlib; then \ + /usr/bin/ranlib $(LIBDIR)/$(BLIB); \ + fi; \ + fi; \ + chmod 644 $(LIBDIR)/$(BLIB); \ + cp blowfish.h $(INCDIR)/blowfish.h; \ + chmod 644 $(INCDIR)/blowfish.h; \ + fi +# DO NOT DELETE THIS LINE -- make depend depends on it. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/README b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/README new file mode 100644 index 00000000..84d77a78 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/README @@ -0,0 +1,8 @@ +This is a quick packaging up of my blowfish code into a library. +It has been lifted from SSLeay. +The copyright notices seem a little harsh because I have not spent the +time to rewrite the conditions from the normal SSLeay ones. + +Basically if you just want to play with the library, not a problem. + +eric 15-Apr-1997 diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bf586.pl b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bf586.pl new file mode 100644 index 00000000..67fb7ee2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bf586.pl @@ -0,0 +1,159 @@ + +#!/usr/local/bin/perl + +$prog="bf586.pl"; + +# base code is in microsft +# op dest, source +# format. +# + +if ( ($ARGV[0] eq "elf")) + { require "x86unix.pl"; } +elsif ( ($ARGV[0] eq "a.out")) + { $aout=1; require "x86unix.pl"; } +elsif ( ($ARGV[0] eq "sol")) + { $sol=1; require "x86unix.pl"; } +elsif ( ($ARGV[0] eq "cpp")) + { $cpp=1; require "x86unix.pl"; } +elsif ( ($ARGV[0] eq "win32")) + { require "x86ms.pl"; } +else + { + print STDERR <<"EOF"; +Pick one target type from + elf - linux, FreeBSD etc + a.out - old linux + sol - x86 solaris + cpp - format so x86unix.cpp can be used + win32 - Windows 95/Windows NT +EOF + exit(1); + } + +&comment("Don't even think of reading this code"); +&comment("It was automatically generated by $prog"); +&comment("Which is a perl program used to generate the x86 assember for"); +&comment("any of elf, a.out, Win32, or Solaris"); +&comment("It can be found in SSLeay 0.7.0+"); +&comment("eric "); +&comment(""); + +&file("bfx86xxxx"); + +$BF_ROUNDS=16; +$BF_OFF=($BF_ROUNDS+2)*4; +$L="ecx"; +$R="edx"; +$P="edi"; +$tot="esi"; +$tmp1="eax"; +$tmp2="ebx"; +$tmp3="ebp"; + +&des_encrypt("BF_encrypt"); + +&file_end(); + +sub des_encrypt + { + local($name)=@_; + + &function_begin($name,3); + + &comment(""); + &comment("Load the 2 words"); + &mov("eax",&wparam(0)); + &mov($L,&DWP(0,"eax","",0)); + &mov($R,&DWP(4,"eax","",0)); + + &comment(""); + &comment("P pointer, s and enc flag"); + &mov($P,&wparam(1)); + + &xor( $tmp1, $tmp1); + &xor( $tmp2, $tmp2); + + # encrypting part + + &mov("ebp",&wparam(2)); # get encrypt flag + &cmp("ebp","0"); + &je(&label("start_decrypt")); + + &xor($L,&DWP(0,$P,"",0)); + for ($i=0; $i<$BF_ROUNDS; $i+=2) + { + &comment(""); + &comment("Round $i"); + &BF_ENCRYPT($i+1,$R,$L,$P,$tot,$tmp1,$tmp2,$tmp3); + + &comment(""); + &comment("Round ".sprintf("%d",$i+1)); + &BF_ENCRYPT($i+2,$L,$R,$P,$tot,$tmp1,$tmp2,$tmp3); + } + &xor($R,&DWP(($BF_ROUNDS+1)*4,$P,"",0)); + + &mov("eax",&wparam(0)); + &mov(&DWP(0,"eax","",0),$R); + &mov(&DWP(4,"eax","",0),$L); + &function_end_A($name); + + &set_label("start_decrypt"); + + &xor($L,&DWP(($BF_ROUNDS+1)*4,$P,"",0)); + for ($i=$BF_ROUNDS; $i>0; $i-=2) + { + &comment(""); + &comment("Round $i"); + &BF_ENCRYPT($i,$R,$L,$P,$tot,$tmp1,$tmp2,$tmp3); + &comment(""); + &comment("Round ".sprintf("%d",$i-1)); + &BF_ENCRYPT($i-1,$L,$R,$P,$tot,$tmp1,$tmp2,$tmp3); + } + &xor($R,&DWP(0,$P,"",0)); + + &mov("eax",&wparam(0)); + &mov(&DWP(0,"eax","",0),$R); + &mov(&DWP(4,"eax","",0),$L); + &function_end_A($name); + + &function_end_B($name); + } + +sub BF_ENCRYPT + { + local($i,$L,$R,$P,$tot,$tmp1,$tmp2,$tmp3)=@_; + + &rotr( $R, 16); + &mov( $tot, &DWP(&n2a($i*4),$P,"",0)); + + &movb( &LB($tmp1), &HB($R)); + &movb( &LB($tmp2), &LB($R)); + + &rotr( $R, 16); + &xor( $L, $tot); + + &mov( $tot, &DWP(&n2a($BF_OFF+0x0000),$P,$tmp1,4)); + &mov( $tmp3, &DWP(&n2a($BF_OFF+0x0400),$P,$tmp2,4)); + + &movb( &LB($tmp1), &HB($R)); + &movb( &LB($tmp2), &LB($R)); + + &add( $tot, $tmp3); + &mov( $tmp1, &DWP(&n2a($BF_OFF+0x0800),$P,$tmp1,4)); # delay + + &xor( $tot, $tmp1); + &mov( $tmp3, &DWP(&n2a($BF_OFF+0x0C00),$P,$tmp2,4)); + + &add( $tot, $tmp3); + &xor( $tmp1, $tmp1); + + &xor( $L, $tot); + # delay + } + +sub n2a + { + sprintf("%d",$_[0]); + } + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bx86-cpp.s b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bx86-cpp.s new file mode 100644 index 00000000..47babe26 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bx86-cpp.s @@ -0,0 +1,666 @@ + /* Don't even think of reading this code */ + /* It was automatically generated by bf586.pl */ + /* Which is a perl program used to generate the x86 assember for */ + /* any of elf, a.out, Win32, or Solaris */ + /* It can be found in SSLeay 0.7.0+ */ + /* eric */ + + .file "bfx86xxxx.s" + .version "01.01" +gcc2_compiled.: +.text + .align ALIGN +.globl BF_encrypt + TYPE(BF_encrypt,@function) +BF_encrypt: + pushl %ebp + pushl %ebx + pushl %esi + pushl %edi + + + /* Load the 2 words */ + movl 20(%esp), %eax + movl (%eax), %ecx + movl 4(%eax), %edx + + /* P pointer, s and enc flag */ + movl 24(%esp), %edi + xorl %eax, %eax + xorl %ebx, %ebx + movl 28(%esp), %ebp + cmpl $0, %ebp + je .L000start_decrypt + xorl (%edi), %ecx + + /* Round 0 */ + rorl $16, %ecx + movl 4(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 1 */ + rorl $16, %edx + movl 8(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 2 */ + rorl $16, %ecx + movl 12(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 3 */ + rorl $16, %edx + movl 16(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 4 */ + rorl $16, %ecx + movl 20(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 5 */ + rorl $16, %edx + movl 24(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 6 */ + rorl $16, %ecx + movl 28(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 7 */ + rorl $16, %edx + movl 32(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 8 */ + rorl $16, %ecx + movl 36(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 9 */ + rorl $16, %edx + movl 40(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 10 */ + rorl $16, %ecx + movl 44(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 11 */ + rorl $16, %edx + movl 48(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 12 */ + rorl $16, %ecx + movl 52(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 13 */ + rorl $16, %edx + movl 56(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 14 */ + rorl $16, %ecx + movl 60(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 15 */ + rorl $16, %edx + movl 64(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + xorl 68(%edi), %edx + movl 20(%esp), %eax + movl %edx, (%eax) + movl %ecx, 4(%eax) + popl %edi + popl %esi + popl %ebx + popl %ebp + ret +.align ALIGN +.L000start_decrypt: + xorl 68(%edi), %ecx + + /* Round 16 */ + rorl $16, %ecx + movl 64(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 15 */ + rorl $16, %edx + movl 60(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 14 */ + rorl $16, %ecx + movl 56(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 13 */ + rorl $16, %edx + movl 52(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 12 */ + rorl $16, %ecx + movl 48(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 11 */ + rorl $16, %edx + movl 44(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 10 */ + rorl $16, %ecx + movl 40(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 9 */ + rorl $16, %edx + movl 36(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 8 */ + rorl $16, %ecx + movl 32(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 7 */ + rorl $16, %edx + movl 28(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 6 */ + rorl $16, %ecx + movl 24(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 5 */ + rorl $16, %edx + movl 20(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 4 */ + rorl $16, %ecx + movl 16(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 3 */ + rorl $16, %edx + movl 12(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + + /* Round 2 */ + rorl $16, %ecx + movl 8(%edi), %esi + movb %ch, %al + movb %cl, %bl + rorl $16, %ecx + xorl %esi, %edx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %ch, %al + movb %cl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %edx + + /* Round 1 */ + rorl $16, %edx + movl 4(%edi), %esi + movb %dh, %al + movb %dl, %bl + rorl $16, %edx + xorl %esi, %ecx + movl 72(%edi,%eax,4),%esi + movl 1096(%edi,%ebx,4),%ebp + movb %dh, %al + movb %dl, %bl + addl %ebp, %esi + movl 2120(%edi,%eax,4),%eax + xorl %eax, %esi + movl 3144(%edi,%ebx,4),%ebp + addl %ebp, %esi + xorl %eax, %eax + xorl %esi, %ecx + xorl (%edi), %edx + movl 20(%esp), %eax + movl %edx, (%eax) + movl %ecx, 4(%eax) + popl %edi + popl %esi + popl %ebx + popl %ebp + ret +.BF_encrypt_end: + SIZE(BF_encrypt,.BF_encrypt_end-BF_encrypt) +.ident "desasm.pl" diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bx86unix.cpp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bx86unix.cpp new file mode 100644 index 00000000..98214534 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/bx86unix.cpp @@ -0,0 +1,33 @@ + +#define TYPE(a,b) .type a,b +#define SIZE(a,b) .size a,b + +#ifdef OUT +#define OK 1 +#define BF_encrypt _BF_encrypt +#define ALIGN 4 +#endif + +#ifdef BSDI +#define OK 1 +#define BF_encrypt _BF_encrypt +#define ALIGN 4 +#undef SIZE +#undef TYPE +#endif + +#if defined(ELF) || defined(SOL) +#define OK 1 +#define ALIGN 16 +#endif + +#ifndef OK +You need to define one of +ELF - elf systems - linux-elf, NetBSD and DG-UX +OUT - a.out systems - linux-a.out and FreeBSD +SOL - solaris systems, which are elf with strange comment lines +BSDI - a.out with a very primative version of as. +#endif + +#include "bx86-cpp.s" + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/readme b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/readme new file mode 100644 index 00000000..d1b1702a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/readme @@ -0,0 +1,3 @@ +If you want more of an idea of how this all works, +have a read of the readme file in SSLeay/crypto/des/asm. +SSLeay can be found at ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/win32.asm b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/win32.asm new file mode 100644 index 00000000..42363d78 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/win32.asm @@ -0,0 +1,663 @@ + ; Don't even think of reading this code + ; It was automatically generated by bf586.pl + ; Which is a perl program used to generate the x86 assember for + ; any of elf, a.out, Win32, or Solaris + ; It can be found in SSLeay 0.7.0+ + ; eric + ; + TITLE bfx86xxxx.asm + .386 +.model FLAT +_TEXT SEGMENT +PUBLIC _BF_encrypt +EXTRN _des_SPtrans:DWORD +_BF_encrypt PROC NEAR + push ebp + push ebx + push esi + push edi + ; + ; Load the 2 words + mov eax, DWORD PTR 20[esp] + mov ecx, DWORD PTR [eax] + mov edx, DWORD PTR 4[eax] + ; + ; P pointer, s and enc flag + mov edi, DWORD PTR 24[esp] + xor eax, eax + xor ebx, ebx + mov ebp, DWORD PTR 28[esp] + cmp ebp, 0 + je $L000start_decrypt + xor ecx, DWORD PTR [edi] + ; + ; Round 0 + ror ecx, 16 + mov esi, DWORD PTR 4[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 1 + ror edx, 16 + mov esi, DWORD PTR 8[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 2 + ror ecx, 16 + mov esi, DWORD PTR 12[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 3 + ror edx, 16 + mov esi, DWORD PTR 16[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 4 + ror ecx, 16 + mov esi, DWORD PTR 20[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 5 + ror edx, 16 + mov esi, DWORD PTR 24[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 6 + ror ecx, 16 + mov esi, DWORD PTR 28[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 7 + ror edx, 16 + mov esi, DWORD PTR 32[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 8 + ror ecx, 16 + mov esi, DWORD PTR 36[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 9 + ror edx, 16 + mov esi, DWORD PTR 40[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 10 + ror ecx, 16 + mov esi, DWORD PTR 44[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 11 + ror edx, 16 + mov esi, DWORD PTR 48[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 12 + ror ecx, 16 + mov esi, DWORD PTR 52[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 13 + ror edx, 16 + mov esi, DWORD PTR 56[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 14 + ror ecx, 16 + mov esi, DWORD PTR 60[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 15 + ror edx, 16 + mov esi, DWORD PTR 64[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + xor edx, DWORD PTR 68[edi] + mov eax, DWORD PTR 20[esp] + mov DWORD PTR [eax],edx + mov DWORD PTR 4[eax],ecx + pop edi + pop esi + pop ebx + pop ebp + ret +$L000start_decrypt: + xor ecx, DWORD PTR 68[edi] + ; + ; Round 16 + ror ecx, 16 + mov esi, DWORD PTR 64[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 15 + ror edx, 16 + mov esi, DWORD PTR 60[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 14 + ror ecx, 16 + mov esi, DWORD PTR 56[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 13 + ror edx, 16 + mov esi, DWORD PTR 52[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 12 + ror ecx, 16 + mov esi, DWORD PTR 48[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 11 + ror edx, 16 + mov esi, DWORD PTR 44[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 10 + ror ecx, 16 + mov esi, DWORD PTR 40[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 9 + ror edx, 16 + mov esi, DWORD PTR 36[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 8 + ror ecx, 16 + mov esi, DWORD PTR 32[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 7 + ror edx, 16 + mov esi, DWORD PTR 28[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 6 + ror ecx, 16 + mov esi, DWORD PTR 24[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 5 + ror edx, 16 + mov esi, DWORD PTR 20[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 4 + ror ecx, 16 + mov esi, DWORD PTR 16[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 3 + ror edx, 16 + mov esi, DWORD PTR 12[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + ; + ; Round 2 + ror ecx, 16 + mov esi, DWORD PTR 8[edi] + mov al, ch + mov bl, cl + ror ecx, 16 + xor edx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, ch + mov bl, cl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor edx, esi + ; + ; Round 1 + ror edx, 16 + mov esi, DWORD PTR 4[edi] + mov al, dh + mov bl, dl + ror edx, 16 + xor ecx, esi + mov esi, DWORD PTR 72[eax*4+edi] + mov ebp, DWORD PTR 1096[ebx*4+edi] + mov al, dh + mov bl, dl + add esi, ebp + mov eax, DWORD PTR 2120[eax*4+edi] + xor esi, eax + mov ebp, DWORD PTR 3144[ebx*4+edi] + add esi, ebp + xor eax, eax + xor ecx, esi + xor edx, DWORD PTR [edi] + mov eax, DWORD PTR 20[esp] + mov DWORD PTR [eax],edx + mov DWORD PTR 4[eax],ecx + pop edi + pop esi + pop ebx + pop ebp + ret +_BF_encrypt ENDP +_TEXT ENDS +END diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/x86ms.pl b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/x86ms.pl new file mode 100644 index 00000000..d85bdfe8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/x86ms.pl @@ -0,0 +1,249 @@ +#!/usr/local/bin/perl + +package x86ms; + +$label="L000"; + +%lb=( 'eax', 'al', + 'ebx', 'bl', + 'ecx', 'cl', + 'edx', 'dl', + 'ax', 'al', + 'bx', 'bl', + 'cx', 'cl', + 'dx', 'dl', + ); + +%hb=( 'eax', 'ah', + 'ebx', 'bh', + 'ecx', 'ch', + 'edx', 'dh', + 'ax', 'ah', + 'bx', 'bh', + 'cx', 'ch', + 'dx', 'dh', + ); + +sub main'LB + { + (defined($lb{$_[0]})) || die "$_[0] does not have a 'low byte'\n"; + return($lb{$_[0]}); + } + +sub main'HB + { + (defined($hb{$_[0]})) || die "$_[0] does not have a 'high byte'\n"; + return($hb{$_[0]}); + } + +sub main'DWP + { + local($addr,$reg1,$reg2,$idx)=@_; + local($t); + local($ret)="DWORD PTR "; + + $addr =~ s/^\s+//; + if ($addr =~ /^(.+)\+(.+)$/) + { + $reg2=&conv($1); + $addr="_$2"; + } + elsif ($addr =~ /^[_a-zA-Z]/) + { + $addr="_$addr"; + } + + $reg1="$regs{$reg1}" if defined($regs{$reg1}); + $reg2="$regs{$reg2}" if defined($regs{$reg2}); + $ret.=$addr if ($addr ne "") && ($addr ne 0); + if ($reg2 ne "") + { + $t=""; + $t="*$idx" if ($idx != 0); + $ret.="[$reg2$t+$reg1]"; + } + else + { + $ret.="[$reg1]" + } + return($ret); + } + +sub main'mov { &out2("mov",@_); } +sub main'movb { &out2("mov",@_); } +sub main'and { &out2("and",@_); } +sub main'or { &out2("or",@_); } +sub main'shl { &out2("shl",@_); } +sub main'shr { &out2("shr",@_); } +sub main'xor { &out2("xor",@_); } +sub main'add { &out2("add",@_); } +sub main'sub { &out2("sub",@_); } +sub main'rotl { &out2("rol",@_); } +sub main'rotr { &out2("ror",@_); } +sub main'exch { &out2("xchg",@_); } +sub main'cmp { &out2("cmp",@_); } +sub main'dec { &out1("dec",@_); } +sub main'jmp { &out1("jmp",@_); } +sub main'je { &out1("je",@_); } +sub main'jz { &out1("jz",@_); } +sub main'jnz { &out1("jnz",@_); } +sub main'push { &out1("push",@_); } +sub main'call { &out1("call",'_'.$_[0]); } + + +sub out2 + { + local($name,$p1,$p2)=@_; + local($l,$t); + + print "\t$name\t"; + $t=&conv($p1).","; + $l=length($t); + print $t; + $l=4-($l+9)/8; + print "\t" x $l; + print &conv($p2); + print "\n"; + } + +sub out1 + { + local($name,$p1)=@_; + local($l,$t); + + print "\t$name\t"; + print &conv($p1); + print "\n"; + } + +sub conv + { + local($p)=@_; + + $p =~ s/0x([0-9A-Fa-f]+)/0$1h/; + return $p; + } + +sub main'file + { + local($file)=@_; + + print <<"EOF"; + TITLE $file.asm + .386 +.model FLAT +EOF + } + +sub main'function_begin + { + local($func,$num)=@_; + + $params=$num*4; + + print <<"EOF"; +_TEXT SEGMENT +PUBLIC _$func +EXTRN _des_SPtrans:DWORD +_$func PROC NEAR + push ebp + push ebx + push esi + push edi +EOF + $stack=20; + } + +sub main'function_end + { + local($func)=@_; + + print <<"EOF"; + pop edi + pop esi + pop ebx + pop ebp + ret +_$func ENDP +_TEXT ENDS +EOF + $stack=0; + %label=(); + } + +sub main'function_end_A + { + local($func)=@_; + + print <<"EOF"; + pop edi + pop esi + pop ebx + pop ebp + ret +EOF + } + +sub main'function_end_B + { + local($func)=@_; + + print <<"EOF"; +_$func ENDP +_TEXT ENDS +EOF + $stack=0; + %label=(); + } + +sub main'file_end + { + print "END\n" + } + +sub main'wparam + { + local($num)=@_; + + return(&main'DWP($stack+$num*4,"esp","",0)); + } + +sub main'wtmp + { + local($num)=@_; + + return(&main'DWP($stack+$params+$num*4,"esp","",0)); + } + +sub main'comment + { + foreach (@_) + { + print "\t; $_\n"; + } + } + +sub main'label + { + if (!defined($label{$_[0]})) + { + $label{$_[0]}="\$${label}${_[0]}"; + $label++; + } + return($label{$_[0]}); + } + +sub main'set_label + { + if (!defined($label{$_[0]})) + { + $label{$_[0]}="${label}${_[0]}"; + $label++; + } + print "$label{$_[0]}:\n"; + } + +sub main'file_end + { + print "END\n"; + } diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/x86unix.pl b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/x86unix.pl new file mode 100644 index 00000000..5c77f123 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/asm/x86unix.pl @@ -0,0 +1,313 @@ +#!/usr/local/bin/perl + +package x86ms; + +$label="L000"; + +$align=($main'aout)?"4":"16"; +$under=($main'aout)?"_":""; +$com_start=($main'sol)?"/":"#"; + +if ($main'cpp) + { + $align="ALIGN"; + $under=""; + $com_start='/*'; + $com_end='*/'; + } + +%lb=( 'eax', '%al', + 'ebx', '%bl', + 'ecx', '%cl', + 'edx', '%dl', + 'ax', '%al', + 'bx', '%bl', + 'cx', '%cl', + 'dx', '%dl', + ); + +%hb=( 'eax', '%ah', + 'ebx', '%bh', + 'ecx', '%ch', + 'edx', '%dh', + 'ax', '%ah', + 'bx', '%bh', + 'cx', '%ch', + 'dx', '%dh', + ); + +%regs=( 'eax', '%eax', + 'ebx', '%ebx', + 'ecx', '%ecx', + 'edx', '%edx', + 'esi', '%esi', + 'edi', '%edi', + 'ebp', '%ebp', + 'esp', '%esp', + ); + +sub main'LB + { + (defined($lb{$_[0]})) || die "$_[0] does not have a 'low byte'\n"; + return($lb{$_[0]}); + } + +sub main'HB + { + (defined($hb{$_[0]})) || die "$_[0] does not have a 'high byte'\n"; + return($hb{$_[0]}); + } + +sub main'DWP + { + local($addr,$reg1,$reg2,$idx)=@_; + + + $ret=""; + + $addr =~ s/(^|[+ \t])([A-Za-z_]+)($|[+ \t])/$1$under$2$3/; + + $reg1="$regs{$reg1}" if defined($regs{$reg1}); + $reg2="$regs{$reg2}" if defined($regs{$reg2}); + $ret.=$addr if ($addr ne "") && ($addr ne 0); + if ($reg2 ne "") + { + $ret.="($reg1,$reg2,$idx)"; + } + else + { + $ret.="($reg1)" + } + return($ret); + } + +sub main'BP + { + local($addr,$reg1,$reg2,$idx)=@_; + + + $ret=""; + + $addr =~ s/(^|[+ \t])([A-Za-z_]+)($|[+ \t])/$1$under$2$3/; + + $reg1="$regs{$reg1}" if defined($regs{$reg1}); + $reg2="$regs{$reg2}" if defined($regs{$reg2}); + $ret.=$addr if ($addr ne "") && ($addr ne 0); + if ($reg2 ne "") + { + $ret.="($reg1,$reg2,$idx)"; + } + else + { + $ret.="($reg1)" + } + return($ret); + } + +sub main'mov { &out2("movl",@_); } +sub main'movb { &out2("movb",@_); } +sub main'and { &out2("andl",@_); } +sub main'or { &out2("orl",@_); } +sub main'shl { &out2("sall",@_); } +sub main'shr { &out2("shrl",@_); } +sub main'xor { &out2("xorl",@_); } +sub main'add { &out2("addl",@_); } +sub main'sub { &out2("subl",@_); } +sub main'rotl { &out2("roll",@_); } +sub main'rotr { &out2("rorl",@_); } +sub main'exch { &out2("xchg",@_); } +sub main'cmp { &out2("cmpl",@_); } +sub main'jmp { &out1("jmp",@_); } +sub main'je { &out1("je",@_); } +sub main'jne { &out1("jne",@_); } +sub main'jnz { &out1("jnz",@_); } +sub main'jz { &out1("jz",@_); } +sub main'dec { &out1("decl",@_); } +sub main'push { &out1("pushl",@_); } +sub main'call { &out1("call",$under.$_[0]); } + + +sub out2 + { + local($name,$p1,$p2)=@_; + local($l,$ll,$t); + + print "\t$name\t"; + $t=&conv($p2).","; + $l=length($t); + print $t; + $ll=4-($l+9)/8; + print "\t" x $ll; + print &conv($p1); + print "\n"; + } + +sub out1 + { + local($name,$p1)=@_; + local($l,$t); + + print "\t$name\t"; + print &conv($p1); + print "\n"; + } + +sub conv + { + local($p)=@_; + +# $p =~ s/0x([0-9A-Fa-f]+)/0$1h/; + + $p=$regs{$p} if (defined($regs{$p})); + + $p =~ s/^([0-9A-Fa-f]+)$/\$$1/; + $p =~ s/^(0x[0-9A-Fa-f]+)$/\$$1/; + return $p; + } + +sub main'file + { + local($file)=@_; + + print <<"EOF"; + .file "$file.s" + .version "01.01" +gcc2_compiled.: +EOF + } + +sub main'function_begin + { + local($func,$num)=@_; + + $params=$num*4; + + $func=$under.$func; + + print <<"EOF"; +.text + .align $align +.globl $func +EOF + if ($main'cpp) + { printf("\tTYPE($func,\@function)\n"); } + else { printf("\t.type $func,\@function\n"); } + print <<"EOF"; +$func: + pushl %ebp + pushl %ebx + pushl %esi + pushl %edi + +EOF + $stack=20; + } + +sub main'function_end + { + local($func)=@_; + + $func=$under.$func; + + print <<"EOF"; + popl %edi + popl %esi + popl %ebx + popl %ebp + ret +.${func}_end: +EOF + if ($main'cpp) + { printf("\tSIZE($func,.${func}_end-$func)\n"); } + else { printf("\t.size\t$func,.${func}_end-$func\n"); } + print ".ident \"desasm.pl\"\n"; + $stack=0; + %label=(); + } + +sub main'function_end_A + { + local($func)=@_; + + print <<"EOF"; + popl %edi + popl %esi + popl %ebx + popl %ebp + ret +EOF + } + +sub main'function_end_B + { + local($func)=@_; + + $func=$under.$func; + + print <<"EOF"; +.${func}_end: +EOF + if ($main'cpp) + { printf("\tSIZE($func,.${func}_end-$func)\n"); } + else { printf("\t.size\t$func,.${func}_end-$func\n"); } + print ".ident \"desasm.pl\"\n"; + $stack=0; + %label=(); + } + +sub main'wparam + { + local($num)=@_; + + return(&main'DWP($stack+$num*4,"esp","",0)); + } + +sub main'wtmp_b + { + local($num,$b)=@_; + + return(&main'BP(-(($num+1)*4)+$b,"esp","",0)); + } + +sub main'wtmp + { + local($num)=@_; + + return(&main'DWP(-($num+1)*4,"esp","",0)); + } + +sub main'comment + { + foreach (@_) + { + if (/^\s*$/) + { print "\n"; } + else + { print "\t$com_start $_ $com_end\n"; } + } + } + +sub main'label + { + if (!defined($label{$_[0]})) + { + $label{$_[0]}=".${label}${_[0]}"; + $label++; + } + return($label{$_[0]}); + } + +sub main'set_label + { + if (!defined($label{$_[0]})) + { + $label{$_[0]}=".${label}${_[0]}"; + $label++; + } + print ".align $align\n"; + print "$label{$_[0]}:\n"; + } + +sub main'file_end + { + } diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_cbc.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_cbc.c new file mode 100644 index 00000000..b988f609 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_cbc.c @@ -0,0 +1,143 @@ +/* crypto/bf/bf_cbc.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@mincom.oz.au). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@mincom.oz.au). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@mincom.oz.au)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include "blowfish.h" +#include "bf_locl.h" + +void BF_cbc_encrypt(unsigned char *in, unsigned char *out, long length, BF_KEY *ks, + unsigned char *iv, int encrypt) +{ + register BF_LONG tin0,tin1; + register BF_LONG tout0,tout1,xor0,xor1; + register long l=length; + BF_LONG tin[2]; + + if (encrypt != 0) + { + n2l(iv,tout0); + n2l(iv,tout1); + iv-=8; + for (l-=8; l>=0; l-=8) + { + n2l(in,tin0); + n2l(in,tin1); + tin0^=tout0; + tin1^=tout1; + tin[0]=tin0; + tin[1]=tin1; + BF_encrypt(tin,ks,BF_ENCRYPT); + tout0=tin[0]; + tout1=tin[1]; + l2n(tout0,out); + l2n(tout1,out); + } + if (l != -8) + { + n2ln(in,tin0,tin1,l+8); + tin0^=tout0; + tin1^=tout1; + tin[0]=tin0; + tin[1]=tin1; + BF_encrypt(tin,ks,BF_ENCRYPT); + tout0=tin[0]; + tout1=tin[1]; + l2n(tout0,out); + l2n(tout1,out); + } + l2n(tout0,iv); + l2n(tout1,iv); + } + else + { + n2l(iv,xor0); + n2l(iv,xor1); + iv-=8; + for (l-=8; l>=0; l-=8) + { + n2l(in,tin0); + n2l(in,tin1); + tin[0]=tin0; + tin[1]=tin1; + BF_encrypt(tin,ks,BF_DECRYPT); + tout0=tin[0]^xor0; + tout1=tin[1]^xor1; + l2n(tout0,out); + l2n(tout1,out); + xor0=tin0; + xor1=tin1; + } + if (l != -8) + { + n2l(in,tin0); + n2l(in,tin1); + tin[0]=tin0; + tin[1]=tin1; + BF_encrypt(tin,ks,BF_DECRYPT); + tout0=tin[0]^xor0; + tout1=tin[1]^xor1; + l2nn(tout0,tout1,out,l+8); + xor0=tin0; + xor1=tin1; + } + l2n(xor0,iv); + l2n(xor1,iv); + } + tin0=tin1=tout0=tout1=xor0=xor1=0; + tin[0]=tin[1]=0; + } + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_cfb64.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_cfb64.c new file mode 100644 index 00000000..09c4cf9d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_cfb64.c @@ -0,0 +1,122 @@ +/* crypto/bf/bf_cfb64.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@mincom.oz.au). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@mincom.oz.au). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@mincom.oz.au)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include "blowfish.h" +#include "bf_locl.h" + +/* The input and output encrypted as though 64bit cfb mode is being + * used. The extra state information to record how much of the + * 64bit block we have used is contained in *num; + */ + +void BF_cfb64_encrypt(unsigned char *in, unsigned char *out, long length, BF_KEY *schedule, + unsigned char *ivec, int *num, int encrypt) +{ + register BF_LONG v0,v1,t; + register int n= *num; + register long l=length; + BF_LONG ti[2]; + unsigned char *iv,c,cc; + + iv=(unsigned char *)ivec; + if (encrypt != 0) + { + while (l-- != 0) + { + if (n == 0) + { + n2l(iv,v0); ti[0]=v0; + n2l(iv,v1); ti[1]=v1; + BF_encrypt((unsigned long *)ti,schedule,BF_ENCRYPT); + iv=(unsigned char *)ivec; + t=ti[0]; l2n(t,iv); + t=ti[1]; l2n(t,iv); + iv=(unsigned char *)ivec; + } + c= (unsigned char)(*(in++)^iv[n]); + *(out++)=c; + iv[n]=c; + n=(n+1)&0x07; + } + } + else + { + while (l-- != 0) + { + if (n == 0) + { + n2l(iv,v0); ti[0]=v0; + n2l(iv,v1); ti[1]=v1; + BF_encrypt((unsigned long *)ti,schedule,BF_ENCRYPT); + iv=(unsigned char *)ivec; + t=ti[0]; l2n(t,iv); + t=ti[1]; l2n(t,iv); + iv=(unsigned char *)ivec; + } + cc= *(in++); + c=iv[n]; + iv[n]=cc; + *(out++)=(unsigned char)(c^cc); + n=(n+1)&0x07; + } + } + v0=v1=ti[0]=ti[1]=t=0; + c=cc=(unsigned char)0; + *num=n; + } + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_ecb.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_ecb.c new file mode 100644 index 00000000..68ed7407 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_ecb.c @@ -0,0 +1,91 @@ +/* crypto/bf/bf_ecb.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@mincom.oz.au). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@mincom.oz.au). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@mincom.oz.au)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include "blowfish.h" +#include "bf_locl.h" + +/* Blowfish as implemented from 'Blowfish: Springer-Verlag paper' + * (From LECTURE NOTES IN COIMPUTER SCIENCE 809, FAST SOFTWARE ENCRYPTION, + * CAMBRIDGE SECURITY WORKSHOP, CAMBRIDGE, U.K., DECEMBER 9-11, 1993) + */ + +char *BF_version="BlowFish part of SSLeay 0.7.0 30-Jan-1997"; + +char *BF_options() + { +#ifdef BF_PTR + return("blowfish(ptr)"); +#elif defined(BF_PTR2) + return("blowfish(ptr2)"); +#else + return("blowfish(idx)"); +#endif + } + +void BF_ecb_encrypt(unsigned char *in, unsigned char *out, BF_KEY *ks, int encrypt) +{ + BF_LONG l,d[2]; + + n2l(in,l); d[0]=l; + n2l(in,l); d[1]=l; + BF_encrypt(d,ks,encrypt); + l=d[0]; l2n(l,out); + l=d[1]; l2n(l,out); + l=d[0]=d[1]=0; + } + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_enc.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_enc.c new file mode 100644 index 00000000..e647a9a5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_enc.c @@ -0,0 +1,137 @@ +/* crypto/bf/bf_enc.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@mincom.oz.au). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@mincom.oz.au). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@mincom.oz.au)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include "blowfish.h" +#include "bf_locl.h" + +/* Blowfish as implemented from 'Blowfish: Springer-Verlag paper' + * (From LECTURE NOTES IN COIMPUTER SCIENCE 809, FAST SOFTWARE ENCRYPTION, + * CAMBRIDGE SECURITY WORKSHOP, CAMBRIDGE, U.K., DECEMBER 9-11, 1993) + */ + +#if (BF_ROUNDS != 16) && (BF_ROUNDS != 20) +If you set BF_ROUNDS to some value other than 16 or 20, you will have +to modify the code. +#endif + +void BF_encrypt(BF_LONG *data, BF_KEY *key, int encrypt) +{ + register BF_LONG l,r,*p,*s; + + p=key->P; + s= &(key->S[0]); + l=data[0]; + r=data[1]; + + if (encrypt != 0) + { + l^=p[0]; + BF_ENC(r,l,s,p[ 1]); + BF_ENC(l,r,s,p[ 2]); + BF_ENC(r,l,s,p[ 3]); + BF_ENC(l,r,s,p[ 4]); + BF_ENC(r,l,s,p[ 5]); + BF_ENC(l,r,s,p[ 6]); + BF_ENC(r,l,s,p[ 7]); + BF_ENC(l,r,s,p[ 8]); + BF_ENC(r,l,s,p[ 9]); + BF_ENC(l,r,s,p[10]); + BF_ENC(r,l,s,p[11]); + BF_ENC(l,r,s,p[12]); + BF_ENC(r,l,s,p[13]); + BF_ENC(l,r,s,p[14]); + BF_ENC(r,l,s,p[15]); + BF_ENC(l,r,s,p[16]); +#if BF_ROUNDS == 20 + BF_ENC(r,l,s,p[17]); + BF_ENC(l,r,s,p[18]); + BF_ENC(r,l,s,p[19]); + BF_ENC(l,r,s,p[20]); +#endif + r^=p[BF_ROUNDS+1]; + } + else + { + l^=p[BF_ROUNDS+1]; +#if BF_ROUNDS == 20 + BF_ENC(r,l,s,p[20]); + BF_ENC(l,r,s,p[19]); + BF_ENC(r,l,s,p[18]); + BF_ENC(l,r,s,p[17]); +#endif + BF_ENC(r,l,s,p[16]); + BF_ENC(l,r,s,p[15]); + BF_ENC(r,l,s,p[14]); + BF_ENC(l,r,s,p[13]); + BF_ENC(r,l,s,p[12]); + BF_ENC(l,r,s,p[11]); + BF_ENC(r,l,s,p[10]); + BF_ENC(l,r,s,p[ 9]); + BF_ENC(r,l,s,p[ 8]); + BF_ENC(l,r,s,p[ 7]); + BF_ENC(r,l,s,p[ 6]); + BF_ENC(l,r,s,p[ 5]); + BF_ENC(r,l,s,p[ 4]); + BF_ENC(l,r,s,p[ 3]); + BF_ENC(r,l,s,p[ 2]); + BF_ENC(l,r,s,p[ 1]); + r^=p[0]; + } + data[1]=l&0xffffffff; + data[0]=r&0xffffffff; + } diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_locl.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_locl.h new file mode 100644 index 00000000..a03bbaf6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_locl.h @@ -0,0 +1,236 @@ +/* crypto/bf/bf_local.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@mincom.oz.au). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@mincom.oz.au). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@mincom.oz.au)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * + * Always modify bf_locl.org since bf_locl.h is automatically generated from + * it during SSLeay configuration. + * + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + */ + +/* + Notwithstanding the above warning, bf_locl.h is *not* regenerated + in this stand-alone implementation. The following auto-configuration + based on compiler CPU identification was added by John Walker to + avoid having to modify the Makefile to choose the best optimisation + for an individual platform. +*/ + +#ifdef sparc +#define BF_PTR +#endif +#ifdef mips +#define BF_PTR +#endif +/* In addition, for best performance we should define BF_PTR2 for + Pentium and below x86 machines, but not define it for Pentium + Pro and above. I don't know of any compiler-independent way + to distinguish these platforms, so I'm leaving the optimisation + as best for the newer platforms. The actual performance difference + for this application is trivial in any case. */ + +#undef c2l +#define c2l(c,l) (l =((unsigned long)(*((c)++))) , \ + l|=((unsigned long)(*((c)++)))<< 8L, \ + l|=((unsigned long)(*((c)++)))<<16L, \ + l|=((unsigned long)(*((c)++)))<<24L) + +/* NOTE - c is not incremented as per c2l */ +#undef c2ln +#define c2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((unsigned long)(*(--(c))))<<24L; \ + case 7: l2|=((unsigned long)(*(--(c))))<<16L; \ + case 6: l2|=((unsigned long)(*(--(c))))<< 8L; \ + case 5: l2|=((unsigned long)(*(--(c)))); \ + case 4: l1 =((unsigned long)(*(--(c))))<<24L; \ + case 3: l1|=((unsigned long)(*(--(c))))<<16L; \ + case 2: l1|=((unsigned long)(*(--(c))))<< 8L; \ + case 1: l1|=((unsigned long)(*(--(c)))); \ + } \ + } + +#undef l2c +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24L)&0xff)) + +/* NOTE - c is not incremented as per l2c */ +#undef l2cn +#define l2cn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ + } \ + } + +/* NOTE - c is not incremented as per n2l */ +#define n2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((unsigned long)(*(--(c)))) ; \ + case 7: l2|=((unsigned long)(*(--(c))))<< 8; \ + case 6: l2|=((unsigned long)(*(--(c))))<<16; \ + case 5: l2|=((unsigned long)(*(--(c))))<<24; \ + case 4: l1 =((unsigned long)(*(--(c)))) ; \ + case 3: l1|=((unsigned long)(*(--(c))))<< 8; \ + case 2: l1|=((unsigned long)(*(--(c))))<<16; \ + case 1: l1|=((unsigned long)(*(--(c))))<<24; \ + } \ + } + +/* NOTE - c is not incremented as per l2n */ +#define l2nn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1) )&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \ + } \ + } + +#undef n2l +#define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24L, \ + l|=((unsigned long)(*((c)++)))<<16L, \ + l|=((unsigned long)(*((c)++)))<< 8L, \ + l|=((unsigned long)(*((c)++)))) + +#undef l2n +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +/* This is actually a big endian algorithm, the most significate byte + * is used to lookup array 0 */ + +/* use BF_PTR2 for intel boxes, + * BF_PTR for sparc and MIPS/SGI + * use nothing for Alpha and HP. + */ +#if !defined(BF_PTR) && !defined(BF_PTR2) +#undef BF_PTR +#endif + +#define BF_M 0x3fc +#define BF_0 22L +#define BF_1 14L +#define BF_2 6L +#define BF_3 2L /* left shift */ + +#if defined(BF_PTR2) + +/* This is basically a special pentium verson */ +#define BF_ENC(LL,R,S,P) \ + { \ + BF_LONG t,u,v; \ + u=R>>BF_0; \ + v=R>>BF_1; \ + u&=BF_M; \ + v&=BF_M; \ + t= *(BF_LONG *)((unsigned char *)&(S[ 0])+u); \ + u=R>>BF_2; \ + t+= *(BF_LONG *)((unsigned char *)&(S[256])+v); \ + v=R<>BF_0)&BF_M))+ \ + *(BF_LONG *)((unsigned char *)&(S[256])+((R>>BF_1)&BF_M)))^ \ + *(BF_LONG *)((unsigned char *)&(S[512])+((R>>BF_2)&BF_M)))+ \ + *(BF_LONG *)((unsigned char *)&(S[768])+((R<>24L) ] + \ + S[0x0100+((R>>16L)&0xff)])^ \ + S[0x0200+((R>> 8L)&0xff)])+ \ + S[0x0300+((R )&0xff)])&0xffffffff; +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_locl.org b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_locl.org new file mode 100644 index 00000000..0a3983a6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_locl.org @@ -0,0 +1,215 @@ +/* crypto/bf/bf_local.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@mincom.oz.au). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@mincom.oz.au). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@mincom.oz.au)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * + * Always modify bf_locl.org since bf_locl.h is automatically generated from + * it during SSLeay configuration. + * + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + */ + +#undef c2l +#define c2l(c,l) (l =((unsigned long)(*((c)++))) , \ + l|=((unsigned long)(*((c)++)))<< 8L, \ + l|=((unsigned long)(*((c)++)))<<16L, \ + l|=((unsigned long)(*((c)++)))<<24L) + +/* NOTE - c is not incremented as per c2l */ +#undef c2ln +#define c2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((unsigned long)(*(--(c))))<<24L; \ + case 7: l2|=((unsigned long)(*(--(c))))<<16L; \ + case 6: l2|=((unsigned long)(*(--(c))))<< 8L; \ + case 5: l2|=((unsigned long)(*(--(c)))); \ + case 4: l1 =((unsigned long)(*(--(c))))<<24L; \ + case 3: l1|=((unsigned long)(*(--(c))))<<16L; \ + case 2: l1|=((unsigned long)(*(--(c))))<< 8L; \ + case 1: l1|=((unsigned long)(*(--(c)))); \ + } \ + } + +#undef l2c +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24L)&0xff)) + +/* NOTE - c is not incremented as per l2c */ +#undef l2cn +#define l2cn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ + } \ + } + +/* NOTE - c is not incremented as per n2l */ +#define n2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((unsigned long)(*(--(c)))) ; \ + case 7: l2|=((unsigned long)(*(--(c))))<< 8; \ + case 6: l2|=((unsigned long)(*(--(c))))<<16; \ + case 5: l2|=((unsigned long)(*(--(c))))<<24; \ + case 4: l1 =((unsigned long)(*(--(c)))) ; \ + case 3: l1|=((unsigned long)(*(--(c))))<< 8; \ + case 2: l1|=((unsigned long)(*(--(c))))<<16; \ + case 1: l1|=((unsigned long)(*(--(c))))<<24; \ + } \ + } + +/* NOTE - c is not incremented as per l2n */ +#define l2nn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1) )&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \ + } \ + } + +#undef n2l +#define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24L, \ + l|=((unsigned long)(*((c)++)))<<16L, \ + l|=((unsigned long)(*((c)++)))<< 8L, \ + l|=((unsigned long)(*((c)++)))) + +#undef l2n +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +/* This is actually a big endian algorithm, the most significate byte + * is used to lookup array 0 */ + +/* use BF_PTR2 for intel boxes, + * BF_PTR for sparc and MIPS/SGI + * use nothing for Alpha and HP. + */ +#if !defined(BF_PTR) && !defined(BF_PTR2) +#undef BF_PTR +#endif + +#define BF_M 0x3fc +#define BF_0 22L +#define BF_1 14L +#define BF_2 6L +#define BF_3 2L /* left shift */ + +#if defined(BF_PTR2) + +/* This is basically a special pentium verson */ +#define BF_ENC(LL,R,S,P) \ + { \ + BF_LONG t,u,v; \ + u=R>>BF_0; \ + v=R>>BF_1; \ + u&=BF_M; \ + v&=BF_M; \ + t= *(BF_LONG *)((unsigned char *)&(S[ 0])+u); \ + u=R>>BF_2; \ + t+= *(BF_LONG *)((unsigned char *)&(S[256])+v); \ + v=R<>BF_0)&BF_M))+ \ + *(BF_LONG *)((unsigned char *)&(S[256])+((R>>BF_1)&BF_M)))^ \ + *(BF_LONG *)((unsigned char *)&(S[512])+((R>>BF_2)&BF_M)))+ \ + *(BF_LONG *)((unsigned char *)&(S[768])+((R<>24L) ] + \ + S[0x0100+((R>>16L)&0xff)])^ \ + S[0x0200+((R>> 8L)&0xff)])+ \ + S[0x0300+((R )&0xff)])&0xffffffff; +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_ofb64.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_ofb64.c new file mode 100644 index 00000000..f46c56d4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_ofb64.c @@ -0,0 +1,110 @@ +/* crypto/bf/bf_ofb64.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@mincom.oz.au). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@mincom.oz.au). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@mincom.oz.au)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include "blowfish.h" +#include "bf_locl.h" + +/* The input and output encrypted as though 64bit ofb mode is being + * used. The extra state information to record how much of the + * 64bit block we have used is contained in *num; + */ +void BF_ofb64_encrypt(unsigned char *in, unsigned char *out, long length, BF_KEY *schedule, + unsigned char *ivec, int *num) +{ + register unsigned long v0,v1,t; + register int n= *num; + register long l=length; + unsigned char d[8]; + register unsigned char *dp; + unsigned long ti[2]; + unsigned char *iv; + int save=0; + + iv=(unsigned char *)ivec; + n2l(iv,v0); + n2l(iv,v1); + ti[0]=v0; + ti[1]=v1; + dp=(unsigned char *)d; + l2n(v0,dp); + l2n(v1,dp); + while (l-- != 0) + { + if (n == 0) + { + BF_encrypt((unsigned long *)ti,schedule,BF_ENCRYPT); + dp=(unsigned char *)d; + t=ti[0]; l2n(t,dp); + t=ti[1]; l2n(t,dp); + save++; + } + *(out++)= (unsigned char)(*(in++)^d[n]); + n=(n+1)&0x07; + } + if (save != 0) + { + v0=ti[0]; + v1=ti[1]; + iv=(unsigned char *)ivec; + l2n(v0,iv); + l2n(v1,iv); + } + t=v0=v1=ti[0]=ti[1]=0; + *num=n; + } + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_pi.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_pi.h new file mode 100644 index 00000000..cfbaf32d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_pi.h @@ -0,0 +1,325 @@ +/* crypto/bf/bf_pi.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@mincom.oz.au). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@mincom.oz.au). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@mincom.oz.au)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +static BF_KEY bf_init= { + { + 0x243f6a88UL, 0x85a308d3UL, 0x13198a2eUL, 0x03707344UL, + 0xa4093822UL, 0x299f31d0UL, 0x082efa98UL, 0xec4e6c89UL, + 0x452821e6UL, 0x38d01377UL, 0xbe5466cfUL, 0x34e90c6cUL, + 0xc0ac29b7UL, 0xc97c50ddUL, 0x3f84d5b5UL, 0xb5470917UL, + 0x9216d5d9UL, 0x8979fb1bUL + },{ + 0xd1310ba6UL, 0x98dfb5acUL, 0x2ffd72dbUL, 0xd01adfb7UL, + 0xb8e1afedUL, 0x6a267e96UL, 0xba7c9045UL, 0xf12c7f99UL, + 0x24a19947UL, 0xb3916cf7UL, 0x0801f2e2UL, 0x858efc16UL, + 0x636920d8UL, 0x71574e69UL, 0xa458fea3UL, 0xf4933d7eUL, + 0x0d95748fUL, 0x728eb658UL, 0x718bcd58UL, 0x82154aeeUL, + 0x7b54a41dUL, 0xc25a59b5UL, 0x9c30d539UL, 0x2af26013UL, + 0xc5d1b023UL, 0x286085f0UL, 0xca417918UL, 0xb8db38efUL, + 0x8e79dcb0UL, 0x603a180eUL, 0x6c9e0e8bUL, 0xb01e8a3eUL, + 0xd71577c1UL, 0xbd314b27UL, 0x78af2fdaUL, 0x55605c60UL, + 0xe65525f3UL, 0xaa55ab94UL, 0x57489862UL, 0x63e81440UL, + 0x55ca396aUL, 0x2aab10b6UL, 0xb4cc5c34UL, 0x1141e8ceUL, + 0xa15486afUL, 0x7c72e993UL, 0xb3ee1411UL, 0x636fbc2aUL, + 0x2ba9c55dUL, 0x741831f6UL, 0xce5c3e16UL, 0x9b87931eUL, + 0xafd6ba33UL, 0x6c24cf5cUL, 0x7a325381UL, 0x28958677UL, + 0x3b8f4898UL, 0x6b4bb9afUL, 0xc4bfe81bUL, 0x66282193UL, + 0x61d809ccUL, 0xfb21a991UL, 0x487cac60UL, 0x5dec8032UL, + 0xef845d5dUL, 0xe98575b1UL, 0xdc262302UL, 0xeb651b88UL, + 0x23893e81UL, 0xd396acc5UL, 0x0f6d6ff3UL, 0x83f44239UL, + 0x2e0b4482UL, 0xa4842004UL, 0x69c8f04aUL, 0x9e1f9b5eUL, + 0x21c66842UL, 0xf6e96c9aUL, 0x670c9c61UL, 0xabd388f0UL, + 0x6a51a0d2UL, 0xd8542f68UL, 0x960fa728UL, 0xab5133a3UL, + 0x6eef0b6cUL, 0x137a3be4UL, 0xba3bf050UL, 0x7efb2a98UL, + 0xa1f1651dUL, 0x39af0176UL, 0x66ca593eUL, 0x82430e88UL, + 0x8cee8619UL, 0x456f9fb4UL, 0x7d84a5c3UL, 0x3b8b5ebeUL, + 0xe06f75d8UL, 0x85c12073UL, 0x401a449fUL, 0x56c16aa6UL, + 0x4ed3aa62UL, 0x363f7706UL, 0x1bfedf72UL, 0x429b023dUL, + 0x37d0d724UL, 0xd00a1248UL, 0xdb0fead3UL, 0x49f1c09bUL, + 0x075372c9UL, 0x80991b7bUL, 0x25d479d8UL, 0xf6e8def7UL, + 0xe3fe501aUL, 0xb6794c3bUL, 0x976ce0bdUL, 0x04c006baUL, + 0xc1a94fb6UL, 0x409f60c4UL, 0x5e5c9ec2UL, 0x196a2463UL, + 0x68fb6fafUL, 0x3e6c53b5UL, 0x1339b2ebUL, 0x3b52ec6fUL, + 0x6dfc511fUL, 0x9b30952cUL, 0xcc814544UL, 0xaf5ebd09UL, + 0xbee3d004UL, 0xde334afdUL, 0x660f2807UL, 0x192e4bb3UL, + 0xc0cba857UL, 0x45c8740fUL, 0xd20b5f39UL, 0xb9d3fbdbUL, + 0x5579c0bdUL, 0x1a60320aUL, 0xd6a100c6UL, 0x402c7279UL, + 0x679f25feUL, 0xfb1fa3ccUL, 0x8ea5e9f8UL, 0xdb3222f8UL, + 0x3c7516dfUL, 0xfd616b15UL, 0x2f501ec8UL, 0xad0552abUL, + 0x323db5faUL, 0xfd238760UL, 0x53317b48UL, 0x3e00df82UL, + 0x9e5c57bbUL, 0xca6f8ca0UL, 0x1a87562eUL, 0xdf1769dbUL, + 0xd542a8f6UL, 0x287effc3UL, 0xac6732c6UL, 0x8c4f5573UL, + 0x695b27b0UL, 0xbbca58c8UL, 0xe1ffa35dUL, 0xb8f011a0UL, + 0x10fa3d98UL, 0xfd2183b8UL, 0x4afcb56cUL, 0x2dd1d35bUL, + 0x9a53e479UL, 0xb6f84565UL, 0xd28e49bcUL, 0x4bfb9790UL, + 0xe1ddf2daUL, 0xa4cb7e33UL, 0x62fb1341UL, 0xcee4c6e8UL, + 0xef20cadaUL, 0x36774c01UL, 0xd07e9efeUL, 0x2bf11fb4UL, + 0x95dbda4dUL, 0xae909198UL, 0xeaad8e71UL, 0x6b93d5a0UL, + 0xd08ed1d0UL, 0xafc725e0UL, 0x8e3c5b2fUL, 0x8e7594b7UL, + 0x8ff6e2fbUL, 0xf2122b64UL, 0x8888b812UL, 0x900df01cUL, + 0x4fad5ea0UL, 0x688fc31cUL, 0xd1cff191UL, 0xb3a8c1adUL, + 0x2f2f2218UL, 0xbe0e1777UL, 0xea752dfeUL, 0x8b021fa1UL, + 0xe5a0cc0fUL, 0xb56f74e8UL, 0x18acf3d6UL, 0xce89e299UL, + 0xb4a84fe0UL, 0xfd13e0b7UL, 0x7cc43b81UL, 0xd2ada8d9UL, + 0x165fa266UL, 0x80957705UL, 0x93cc7314UL, 0x211a1477UL, + 0xe6ad2065UL, 0x77b5fa86UL, 0xc75442f5UL, 0xfb9d35cfUL, + 0xebcdaf0cUL, 0x7b3e89a0UL, 0xd6411bd3UL, 0xae1e7e49UL, + 0x00250e2dUL, 0x2071b35eUL, 0x226800bbUL, 0x57b8e0afUL, + 0x2464369bUL, 0xf009b91eUL, 0x5563911dUL, 0x59dfa6aaUL, + 0x78c14389UL, 0xd95a537fUL, 0x207d5ba2UL, 0x02e5b9c5UL, + 0x83260376UL, 0x6295cfa9UL, 0x11c81968UL, 0x4e734a41UL, + 0xb3472dcaUL, 0x7b14a94aUL, 0x1b510052UL, 0x9a532915UL, + 0xd60f573fUL, 0xbc9bc6e4UL, 0x2b60a476UL, 0x81e67400UL, + 0x08ba6fb5UL, 0x571be91fUL, 0xf296ec6bUL, 0x2a0dd915UL, + 0xb6636521UL, 0xe7b9f9b6UL, 0xff34052eUL, 0xc5855664UL, + 0x53b02d5dUL, 0xa99f8fa1UL, 0x08ba4799UL, 0x6e85076aUL, + 0x4b7a70e9UL, 0xb5b32944UL, 0xdb75092eUL, 0xc4192623UL, + 0xad6ea6b0UL, 0x49a7df7dUL, 0x9cee60b8UL, 0x8fedb266UL, + 0xecaa8c71UL, 0x699a17ffUL, 0x5664526cUL, 0xc2b19ee1UL, + 0x193602a5UL, 0x75094c29UL, 0xa0591340UL, 0xe4183a3eUL, + 0x3f54989aUL, 0x5b429d65UL, 0x6b8fe4d6UL, 0x99f73fd6UL, + 0xa1d29c07UL, 0xefe830f5UL, 0x4d2d38e6UL, 0xf0255dc1UL, + 0x4cdd2086UL, 0x8470eb26UL, 0x6382e9c6UL, 0x021ecc5eUL, + 0x09686b3fUL, 0x3ebaefc9UL, 0x3c971814UL, 0x6b6a70a1UL, + 0x687f3584UL, 0x52a0e286UL, 0xb79c5305UL, 0xaa500737UL, + 0x3e07841cUL, 0x7fdeae5cUL, 0x8e7d44ecUL, 0x5716f2b8UL, + 0xb03ada37UL, 0xf0500c0dUL, 0xf01c1f04UL, 0x0200b3ffUL, + 0xae0cf51aUL, 0x3cb574b2UL, 0x25837a58UL, 0xdc0921bdUL, + 0xd19113f9UL, 0x7ca92ff6UL, 0x94324773UL, 0x22f54701UL, + 0x3ae5e581UL, 0x37c2dadcUL, 0xc8b57634UL, 0x9af3dda7UL, + 0xa9446146UL, 0x0fd0030eUL, 0xecc8c73eUL, 0xa4751e41UL, + 0xe238cd99UL, 0x3bea0e2fUL, 0x3280bba1UL, 0x183eb331UL, + 0x4e548b38UL, 0x4f6db908UL, 0x6f420d03UL, 0xf60a04bfUL, + 0x2cb81290UL, 0x24977c79UL, 0x5679b072UL, 0xbcaf89afUL, + 0xde9a771fUL, 0xd9930810UL, 0xb38bae12UL, 0xdccf3f2eUL, + 0x5512721fUL, 0x2e6b7124UL, 0x501adde6UL, 0x9f84cd87UL, + 0x7a584718UL, 0x7408da17UL, 0xbc9f9abcUL, 0xe94b7d8cUL, + 0xec7aec3aUL, 0xdb851dfaUL, 0x63094366UL, 0xc464c3d2UL, + 0xef1c1847UL, 0x3215d908UL, 0xdd433b37UL, 0x24c2ba16UL, + 0x12a14d43UL, 0x2a65c451UL, 0x50940002UL, 0x133ae4ddUL, + 0x71dff89eUL, 0x10314e55UL, 0x81ac77d6UL, 0x5f11199bUL, + 0x043556f1UL, 0xd7a3c76bUL, 0x3c11183bUL, 0x5924a509UL, + 0xf28fe6edUL, 0x97f1fbfaUL, 0x9ebabf2cUL, 0x1e153c6eUL, + 0x86e34570UL, 0xeae96fb1UL, 0x860e5e0aUL, 0x5a3e2ab3UL, + 0x771fe71cUL, 0x4e3d06faUL, 0x2965dcb9UL, 0x99e71d0fUL, + 0x803e89d6UL, 0x5266c825UL, 0x2e4cc978UL, 0x9c10b36aUL, + 0xc6150ebaUL, 0x94e2ea78UL, 0xa5fc3c53UL, 0x1e0a2df4UL, + 0xf2f74ea7UL, 0x361d2b3dUL, 0x1939260fUL, 0x19c27960UL, + 0x5223a708UL, 0xf71312b6UL, 0xebadfe6eUL, 0xeac31f66UL, + 0xe3bc4595UL, 0xa67bc883UL, 0xb17f37d1UL, 0x018cff28UL, + 0xc332ddefUL, 0xbe6c5aa5UL, 0x65582185UL, 0x68ab9802UL, + 0xeecea50fUL, 0xdb2f953bUL, 0x2aef7dadUL, 0x5b6e2f84UL, + 0x1521b628UL, 0x29076170UL, 0xecdd4775UL, 0x619f1510UL, + 0x13cca830UL, 0xeb61bd96UL, 0x0334fe1eUL, 0xaa0363cfUL, + 0xb5735c90UL, 0x4c70a239UL, 0xd59e9e0bUL, 0xcbaade14UL, + 0xeecc86bcUL, 0x60622ca7UL, 0x9cab5cabUL, 0xb2f3846eUL, + 0x648b1eafUL, 0x19bdf0caUL, 0xa02369b9UL, 0x655abb50UL, + 0x40685a32UL, 0x3c2ab4b3UL, 0x319ee9d5UL, 0xc021b8f7UL, + 0x9b540b19UL, 0x875fa099UL, 0x95f7997eUL, 0x623d7da8UL, + 0xf837889aUL, 0x97e32d77UL, 0x11ed935fUL, 0x16681281UL, + 0x0e358829UL, 0xc7e61fd6UL, 0x96dedfa1UL, 0x7858ba99UL, + 0x57f584a5UL, 0x1b227263UL, 0x9b83c3ffUL, 0x1ac24696UL, + 0xcdb30aebUL, 0x532e3054UL, 0x8fd948e4UL, 0x6dbc3128UL, + 0x58ebf2efUL, 0x34c6ffeaUL, 0xfe28ed61UL, 0xee7c3c73UL, + 0x5d4a14d9UL, 0xe864b7e3UL, 0x42105d14UL, 0x203e13e0UL, + 0x45eee2b6UL, 0xa3aaabeaUL, 0xdb6c4f15UL, 0xfacb4fd0UL, + 0xc742f442UL, 0xef6abbb5UL, 0x654f3b1dUL, 0x41cd2105UL, + 0xd81e799eUL, 0x86854dc7UL, 0xe44b476aUL, 0x3d816250UL, + 0xcf62a1f2UL, 0x5b8d2646UL, 0xfc8883a0UL, 0xc1c7b6a3UL, + 0x7f1524c3UL, 0x69cb7492UL, 0x47848a0bUL, 0x5692b285UL, + 0x095bbf00UL, 0xad19489dUL, 0x1462b174UL, 0x23820e00UL, + 0x58428d2aUL, 0x0c55f5eaUL, 0x1dadf43eUL, 0x233f7061UL, + 0x3372f092UL, 0x8d937e41UL, 0xd65fecf1UL, 0x6c223bdbUL, + 0x7cde3759UL, 0xcbee7460UL, 0x4085f2a7UL, 0xce77326eUL, + 0xa6078084UL, 0x19f8509eUL, 0xe8efd855UL, 0x61d99735UL, + 0xa969a7aaUL, 0xc50c06c2UL, 0x5a04abfcUL, 0x800bcadcUL, + 0x9e447a2eUL, 0xc3453484UL, 0xfdd56705UL, 0x0e1e9ec9UL, + 0xdb73dbd3UL, 0x105588cdUL, 0x675fda79UL, 0xe3674340UL, + 0xc5c43465UL, 0x713e38d8UL, 0x3d28f89eUL, 0xf16dff20UL, + 0x153e21e7UL, 0x8fb03d4aUL, 0xe6e39f2bUL, 0xdb83adf7UL, + 0xe93d5a68UL, 0x948140f7UL, 0xf64c261cUL, 0x94692934UL, + 0x411520f7UL, 0x7602d4f7UL, 0xbcf46b2eUL, 0xd4a20068UL, + 0xd4082471UL, 0x3320f46aUL, 0x43b7d4b7UL, 0x500061afUL, + 0x1e39f62eUL, 0x97244546UL, 0x14214f74UL, 0xbf8b8840UL, + 0x4d95fc1dUL, 0x96b591afUL, 0x70f4ddd3UL, 0x66a02f45UL, + 0xbfbc09ecUL, 0x03bd9785UL, 0x7fac6dd0UL, 0x31cb8504UL, + 0x96eb27b3UL, 0x55fd3941UL, 0xda2547e6UL, 0xabca0a9aUL, + 0x28507825UL, 0x530429f4UL, 0x0a2c86daUL, 0xe9b66dfbUL, + 0x68dc1462UL, 0xd7486900UL, 0x680ec0a4UL, 0x27a18deeUL, + 0x4f3ffea2UL, 0xe887ad8cUL, 0xb58ce006UL, 0x7af4d6b6UL, + 0xaace1e7cUL, 0xd3375fecUL, 0xce78a399UL, 0x406b2a42UL, + 0x20fe9e35UL, 0xd9f385b9UL, 0xee39d7abUL, 0x3b124e8bUL, + 0x1dc9faf7UL, 0x4b6d1856UL, 0x26a36631UL, 0xeae397b2UL, + 0x3a6efa74UL, 0xdd5b4332UL, 0x6841e7f7UL, 0xca7820fbUL, + 0xfb0af54eUL, 0xd8feb397UL, 0x454056acUL, 0xba489527UL, + 0x55533a3aUL, 0x20838d87UL, 0xfe6ba9b7UL, 0xd096954bUL, + 0x55a867bcUL, 0xa1159a58UL, 0xcca92963UL, 0x99e1db33UL, + 0xa62a4a56UL, 0x3f3125f9UL, 0x5ef47e1cUL, 0x9029317cUL, + 0xfdf8e802UL, 0x04272f70UL, 0x80bb155cUL, 0x05282ce3UL, + 0x95c11548UL, 0xe4c66d22UL, 0x48c1133fUL, 0xc70f86dcUL, + 0x07f9c9eeUL, 0x41041f0fUL, 0x404779a4UL, 0x5d886e17UL, + 0x325f51ebUL, 0xd59bc0d1UL, 0xf2bcc18fUL, 0x41113564UL, + 0x257b7834UL, 0x602a9c60UL, 0xdff8e8a3UL, 0x1f636c1bUL, + 0x0e12b4c2UL, 0x02e1329eUL, 0xaf664fd1UL, 0xcad18115UL, + 0x6b2395e0UL, 0x333e92e1UL, 0x3b240b62UL, 0xeebeb922UL, + 0x85b2a20eUL, 0xe6ba0d99UL, 0xde720c8cUL, 0x2da2f728UL, + 0xd0127845UL, 0x95b794fdUL, 0x647d0862UL, 0xe7ccf5f0UL, + 0x5449a36fUL, 0x877d48faUL, 0xc39dfd27UL, 0xf33e8d1eUL, + 0x0a476341UL, 0x992eff74UL, 0x3a6f6eabUL, 0xf4f8fd37UL, + 0xa812dc60UL, 0xa1ebddf8UL, 0x991be14cUL, 0xdb6e6b0dUL, + 0xc67b5510UL, 0x6d672c37UL, 0x2765d43bUL, 0xdcd0e804UL, + 0xf1290dc7UL, 0xcc00ffa3UL, 0xb5390f92UL, 0x690fed0bUL, + 0x667b9ffbUL, 0xcedb7d9cUL, 0xa091cf0bUL, 0xd9155ea3UL, + 0xbb132f88UL, 0x515bad24UL, 0x7b9479bfUL, 0x763bd6ebUL, + 0x37392eb3UL, 0xcc115979UL, 0x8026e297UL, 0xf42e312dUL, + 0x6842ada7UL, 0xc66a2b3bUL, 0x12754cccUL, 0x782ef11cUL, + 0x6a124237UL, 0xb79251e7UL, 0x06a1bbe6UL, 0x4bfb6350UL, + 0x1a6b1018UL, 0x11caedfaUL, 0x3d25bdd8UL, 0xe2e1c3c9UL, + 0x44421659UL, 0x0a121386UL, 0xd90cec6eUL, 0xd5abea2aUL, + 0x64af674eUL, 0xda86a85fUL, 0xbebfe988UL, 0x64e4c3feUL, + 0x9dbc8057UL, 0xf0f7c086UL, 0x60787bf8UL, 0x6003604dUL, + 0xd1fd8346UL, 0xf6381fb0UL, 0x7745ae04UL, 0xd736fcccUL, + 0x83426b33UL, 0xf01eab71UL, 0xb0804187UL, 0x3c005e5fUL, + 0x77a057beUL, 0xbde8ae24UL, 0x55464299UL, 0xbf582e61UL, + 0x4e58f48fUL, 0xf2ddfda2UL, 0xf474ef38UL, 0x8789bdc2UL, + 0x5366f9c3UL, 0xc8b38e74UL, 0xb475f255UL, 0x46fcd9b9UL, + 0x7aeb2661UL, 0x8b1ddf84UL, 0x846a0e79UL, 0x915f95e2UL, + 0x466e598eUL, 0x20b45770UL, 0x8cd55591UL, 0xc902de4cUL, + 0xb90bace1UL, 0xbb8205d0UL, 0x11a86248UL, 0x7574a99eUL, + 0xb77f19b6UL, 0xe0a9dc09UL, 0x662d09a1UL, 0xc4324633UL, + 0xe85a1f02UL, 0x09f0be8cUL, 0x4a99a025UL, 0x1d6efe10UL, + 0x1ab93d1dUL, 0x0ba5a4dfUL, 0xa186f20fUL, 0x2868f169UL, + 0xdcb7da83UL, 0x573906feUL, 0xa1e2ce9bUL, 0x4fcd7f52UL, + 0x50115e01UL, 0xa70683faUL, 0xa002b5c4UL, 0x0de6d027UL, + 0x9af88c27UL, 0x773f8641UL, 0xc3604c06UL, 0x61a806b5UL, + 0xf0177a28UL, 0xc0f586e0UL, 0x006058aaUL, 0x30dc7d62UL, + 0x11e69ed7UL, 0x2338ea63UL, 0x53c2dd94UL, 0xc2c21634UL, + 0xbbcbee56UL, 0x90bcb6deUL, 0xebfc7da1UL, 0xce591d76UL, + 0x6f05e409UL, 0x4b7c0188UL, 0x39720a3dUL, 0x7c927c24UL, + 0x86e3725fUL, 0x724d9db9UL, 0x1ac15bb4UL, 0xd39eb8fcUL, + 0xed545578UL, 0x08fca5b5UL, 0xd83d7cd3UL, 0x4dad0fc4UL, + 0x1e50ef5eUL, 0xb161e6f8UL, 0xa28514d9UL, 0x6c51133cUL, + 0x6fd5c7e7UL, 0x56e14ec4UL, 0x362abfceUL, 0xddc6c837UL, + 0xd79a3234UL, 0x92638212UL, 0x670efa8eUL, 0x406000e0UL, + 0x3a39ce37UL, 0xd3faf5cfUL, 0xabc27737UL, 0x5ac52d1bUL, + 0x5cb0679eUL, 0x4fa33742UL, 0xd3822740UL, 0x99bc9bbeUL, + 0xd5118e9dUL, 0xbf0f7315UL, 0xd62d1c7eUL, 0xc700c47bUL, + 0xb78c1b6bUL, 0x21a19045UL, 0xb26eb1beUL, 0x6a366eb4UL, + 0x5748ab2fUL, 0xbc946e79UL, 0xc6a376d2UL, 0x6549c2c8UL, + 0x530ff8eeUL, 0x468dde7dUL, 0xd5730a1dUL, 0x4cd04dc6UL, + 0x2939bbdbUL, 0xa9ba4650UL, 0xac9526e8UL, 0xbe5ee304UL, + 0xa1fad5f0UL, 0x6a2d519aUL, 0x63ef8ce2UL, 0x9a86ee22UL, + 0xc089c2b8UL, 0x43242ef6UL, 0xa51e03aaUL, 0x9cf2d0a4UL, + 0x83c061baUL, 0x9be96a4dUL, 0x8fe51550UL, 0xba645bd6UL, + 0x2826a2f9UL, 0xa73a3ae1UL, 0x4ba99586UL, 0xef5562e9UL, + 0xc72fefd3UL, 0xf752f7daUL, 0x3f046f69UL, 0x77fa0a59UL, + 0x80e4a915UL, 0x87b08601UL, 0x9b09e6adUL, 0x3b3ee593UL, + 0xe990fd5aUL, 0x9e34d797UL, 0x2cf0b7d9UL, 0x022b8b51UL, + 0x96d5ac3aUL, 0x017da67dUL, 0xd1cf3ed6UL, 0x7c7d2d28UL, + 0x1f9f25cfUL, 0xadf2b89bUL, 0x5ad6b472UL, 0x5a88f54cUL, + 0xe029ac71UL, 0xe019a5e6UL, 0x47b0acfdUL, 0xed93fa9bUL, + 0xe8d3c48dUL, 0x283b57ccUL, 0xf8d56629UL, 0x79132e28UL, + 0x785f0191UL, 0xed756055UL, 0xf7960e44UL, 0xe3d35e8cUL, + 0x15056dd4UL, 0x88f46dbaUL, 0x03a16125UL, 0x0564f0bdUL, + 0xc3eb9e15UL, 0x3c9057a2UL, 0x97271aecUL, 0xa93a072aUL, + 0x1b3f6d9bUL, 0x1e6321f5UL, 0xf59c66fbUL, 0x26dcf319UL, + 0x7533d928UL, 0xb155fdf5UL, 0x03563482UL, 0x8aba3cbbUL, + 0x28517711UL, 0xc20ad9f8UL, 0xabcc5167UL, 0xccad925fUL, + 0x4de81751UL, 0x3830dc8eUL, 0x379d5862UL, 0x9320f991UL, + 0xea7a90c2UL, 0xfb3e7bceUL, 0x5121ce64UL, 0x774fbe32UL, + 0xa8b6e37eUL, 0xc3293d46UL, 0x48de5369UL, 0x6413e680UL, + 0xa2ae0810UL, 0xdd6db224UL, 0x69852dfdUL, 0x09072166UL, + 0xb39a460aUL, 0x6445c0ddUL, 0x586cdecfUL, 0x1c20c8aeUL, + 0x5bbef7ddUL, 0x1b588d40UL, 0xccd2017fUL, 0x6bb4e3bbUL, + 0xdda26a7eUL, 0x3a59ff45UL, 0x3e350a44UL, 0xbcb4cdd5UL, + 0x72eacea8UL, 0xfa6484bbUL, 0x8d6612aeUL, 0xbf3c6f47UL, + 0xd29be463UL, 0x542f5d9eUL, 0xaec2771bUL, 0xf64e6370UL, + 0x740e0d8dUL, 0xe75b1357UL, 0xf8721671UL, 0xaf537d5dUL, + 0x4040cb08UL, 0x4eb4e2ccUL, 0x34d2466aUL, 0x0115af84UL, + 0xe1b00428UL, 0x95983a1dUL, 0x06b89fb4UL, 0xce6ea048UL, + 0x6f3f3b82UL, 0x3520ab82UL, 0x011a1d4bUL, 0x277227f8UL, + 0x611560b1UL, 0xe7933fdcUL, 0xbb3a792bUL, 0x344525bdUL, + 0xa08839e1UL, 0x51ce794bUL, 0x2f32c9b7UL, 0xa01fbac9UL, + 0xe01cc87eUL, 0xbcc7d1f6UL, 0xcf0111c3UL, 0xa1e8aac7UL, + 0x1a908749UL, 0xd44fbd9aUL, 0xd0dadecbUL, 0xd50ada38UL, + 0x0339c32aUL, 0xc6913667UL, 0x8df9317cUL, 0xe0b12b4fUL, + 0xf79e59b7UL, 0x43f5bb3aUL, 0xf2d519ffUL, 0x27d9459cUL, + 0xbf97222cUL, 0x15e6fc2aUL, 0x0f91fc71UL, 0x9b941525UL, + 0xfae59361UL, 0xceb69cebUL, 0xc2a86459UL, 0x12baa8d1UL, + 0xb6c1075eUL, 0xe3056a0cUL, 0x10d25065UL, 0xcb03a442UL, + 0xe0ec6e0eUL, 0x1698db3bUL, 0x4c98a0beUL, 0x3278e964UL, + 0x9f1f9532UL, 0xe0d392dfUL, 0xd3a0342bUL, 0x8971f21eUL, + 0x1b0a7441UL, 0x4ba3348cUL, 0xc5be7120UL, 0xc37632d8UL, + 0xdf359f8dUL, 0x9b992f2eUL, 0xe60b6f47UL, 0x0fe3f11dUL, + 0xe54cda54UL, 0x1edad891UL, 0xce6279cfUL, 0xcd3e7e6fUL, + 0x1618b166UL, 0xfd2c1d05UL, 0x848fd2c5UL, 0xf6fb2299UL, + 0xf523f357UL, 0xa6327623UL, 0x93a83531UL, 0x56cccd02UL, + 0xacf08162UL, 0x5a75ebb5UL, 0x6e163697UL, 0x88d273ccUL, + 0xde966292UL, 0x81b949d0UL, 0x4c50901bUL, 0x71c65614UL, + 0xe6c6c7bdUL, 0x327a140aUL, 0x45e1d006UL, 0xc3f27b9aUL, + 0xc9aa53fdUL, 0x62a80f00UL, 0xbb25bfe2UL, 0x35bdd2f6UL, + 0x71126905UL, 0xb2040222UL, 0xb6cbcf7cUL, 0xcd769c2bUL, + 0x53113ec0UL, 0x1640e3d3UL, 0x38abbd60UL, 0x2547adf0UL, + 0xba38209cUL, 0xf746ce76UL, 0x77afa1c5UL, 0x20756060UL, + 0x85cbfe4eUL, 0x8ae88dd8UL, 0x7aaaf9b0UL, 0x4cf9aa7eUL, + 0x1948c25cUL, 0x02fb8a8cUL, 0x01c36ae4UL, 0xd6ebe1f9UL, + 0x90d4f869UL, 0xa65cdea0UL, 0x3f09252dUL, 0xc208e69fUL, + 0xb74e6132UL, 0xce77e25bUL, 0x578fdfe3UL, 0x3ac372e6UL, + } + }; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_skey.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_skey.c new file mode 100644 index 00000000..6cae059b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/bf_skey.c @@ -0,0 +1,116 @@ +/* crypto/bf/bf_skey.c */ +/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@mincom.oz.au). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@mincom.oz.au). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@mincom.oz.au)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include +#include +#include "blowfish.h" +#include "bf_locl.h" +#include "bf_pi.h" + +void BF_set_key(BF_KEY *key, int len, unsigned char *data) +{ + int i; + BF_LONG *p,ri,in[2]; + unsigned char *d,*end; + + + memcpy((char *)key,(char *)&bf_init,sizeof(BF_KEY)); + p=key->P; + + if (len > ((BF_ROUNDS+2)*4)) len=(BF_ROUNDS+2)*4; + + d=data; + end= &(data[len]); + for (i=0; i<(BF_ROUNDS+2); i++) + { + ri= (BF_LONG)*(d++); + if (d >= end) d=data; + + ri<<=8; + ri|= *(d++); + if (d >= end) d=data; + + ri<<=8; + ri|= *(d++); + if (d >= end) d=data; + + ri<<=8; + ri|= *(d++); + if (d >= end) d=data; + + p[i]^=ri; + } + + in[0]=0L; + in[1]=0L; + for (i=0; i<(BF_ROUNDS+2); i+=2) + { + BF_encrypt(in,key,BF_ENCRYPT); + p[i ]=in[0]; + p[i+1]=in[1]; + } + + p=key->S; + for (i=0; i<4*256; i+=2) + { + BF_encrypt(in,key,BF_ENCRYPT); + p[i ]=in[0]; + p[i+1]=in[1]; + } + } + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/blowfish.doc b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/blowfish.doc new file mode 100644 index 00000000..ed84bf06 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/blowfish.doc @@ -0,0 +1,149 @@ +The Blowfish library. + +Blowfish is a block cipher that operates on 64bit (8 byte) quantities. It +uses variable size key, but 128bit (16 byte) key would normally be considered +good. It can be used in all the modes that DES can be used. This +library implements the ecb, cbc, cfb64, ofb64 modes. + +Blowfish is quite a bit faster that DES, and much faster than IDEA or +RC2. It is one of the faster block ciphers. + +For all calls that have an 'input' and 'output' variables, they can be the +same. + +This library requires the inclusion of 'blowfish.h'. + +All of the encryption functions take what is called an BF_KEY as an +argument. An BF_KEY is an expanded form of the Blowfish key. +For all modes of the Blowfish algorithm, the BF_KEY used for +decryption is the same one that was used for encryption. + +The define BF_ENCRYPT is passed to specify encryption for the functions +that require an encryption/decryption flag. BF_DECRYPT is passed to +specify decryption. + +Please note that any of the encryption modes specified in my DES library +could be used with Blowfish. I have only implemented ecb, cbc, cfb64 and +ofb64 for the following reasons. +- ecb is the basic Blowfish encryption. +- cbc is the normal 'chaining' form for block ciphers. +- cfb64 can be used to encrypt single characters, therefore input and output + do not need to be a multiple of 8. +- ofb64 is similar to cfb64 but is more like a stream cipher, not as + secure (not cipher feedback) but it does not have an encrypt/decrypt mode. +- If you want triple Blowfish, thats 384 bits of key and you must be totally + obsessed with security. Still, if you want it, it is simple enough to + copy the function from the DES library and change the des_encrypt to + BF_encrypt; an exercise left for the paranoid reader :-). + +The functions are as follows: + +void BF_set_key( +BF_KEY *ks; +int len; +unsigned char *key; + BF_set_key converts an 'len' byte key into a BF_KEY. + A 'ks' is an expanded form of the 'key' which is used to + perform actual encryption. It can be regenerated from the Blowfish key + so it only needs to be kept when encryption or decryption is about + to occur. Don't save or pass around BF_KEY's since they + are CPU architecture dependent, 'key's are not. Blowfish is an + interesting cipher in that it can be used with a variable length + key. 'len' is the length of 'key' to be used as the key. + A 'len' of 16 is recomended by me, but blowfish can use upto + 72 bytes. As a warning, blowfish has a very very slow set_key + function, it actually runs BF_encrypt 521 times. + +void BF_encrypt( +unsigned long *data, +BF_KEY *key, +int encrypt); + This is the Blowfish encryption function that gets called by just about + every other Blowfish routine in the library. You should not use this + function except to implement 'modes' of Blowfish. + I say this because the + functions that call this routine do the conversion from 'char *' to + long, and this needs to be done to make sure 'non-aligned' memory + access do not occur. + Data is a pointer to 2 unsigned long's and key is the + BF_KEY to use. Encryption or decryption is indicated by 'encrypt'. + which can have the values BF_ENCRYPT or BF_DECRYPT. + +void BF_ecb_encrypt( +unsigned char *in, +unsigned char *out, +BF_KEY *key, +int encrypt); + This is the basic Electronic Code Book form of Blowfish (in DES this + mode is called Electronic Code Book so I'm going to use the term + for blowfish as well. + Input is encrypted into output using the key represented by + key. Depending on the encrypt, encryption or + decryption occurs. Input is 8 bytes long and output is 8 bytes. + +void BF_cbc_encrypt( +unsigned char *in, +unsigned char *out, +long length, +BF_KEY *ks, +unsigned char *ivec, +int encrypt); + This routine implements Blowfish in Cipher Block Chaining mode. + Input, which should be a multiple of 8 bytes is encrypted + (or decrypted) to output which will also be a multiple of 8 bytes. + The number of bytes is in length (and from what I've said above, + should be a multiple of 8). If length is not a multiple of 8, bad + things will probably happen. ivec is the initialisation vector. + This function updates iv after each call so that it can be passed to + the next call to BF_cbc_encrypt(). + +void BF_cfb64_encrypt( +unsigned char *in, +unsigned char *out, +long length, +BF_KEY *schedule, +unsigned char *ivec, +int *num, +int encrypt); + This is one of the more useful functions in this Blowfish library, it + implements CFB mode of Blowfish with 64bit feedback. + This allows you to encrypt an arbitrary number of bytes, + you do not require 8 byte padding. Each call to this + routine will encrypt the input bytes to output and then update ivec + and num. Num contains 'how far' we are though ivec. + 'Encrypt' is used to indicate encryption or decryption. + CFB64 mode operates by using the cipher to generate a stream + of bytes which is used to encrypt the plain text. + The cipher text is then encrypted to generate the next 64 bits to + be xored (incrementally) with the next 64 bits of plain + text. As can be seen from this, to encrypt or decrypt, + the same 'cipher stream' needs to be generated but the way the next + block of data is gathered for encryption is different for + encryption and decryption. + +void BF_ofb64_encrypt( +unsigned char *in, +unsigned char *out, +long length, +BF_KEY *schedule, +unsigned char *ivec, +int *num); + This functions implements OFB mode of Blowfish with 64bit feedback. + This allows you to encrypt an arbitrary number of bytes, + you do not require 8 byte padding. Each call to this + routine will encrypt the input bytes to output and then update ivec + and num. Num contains 'how far' we are though ivec. + This is in effect a stream cipher, there is no encryption or + decryption mode. + +For reading passwords, I suggest using des_read_pw_string() from my DES library. +To generate a password from a text string, I suggest using MD5 (or MD2) to +produce a 16 byte message digest that can then be passed directly to +BF_set_key(). + +===== +For more information about the specific Blowfish modes in this library +(ecb, cbc, cfb and ofb), read the section entitled 'Modes of DES' from the +documentation on my DES library. What is said about DES is directly +applicable for Blowfish. + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/blowfish.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/blowfish.h new file mode 100644 index 00000000..72892407 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/BLOWFISH/blowfish.h @@ -0,0 +1,114 @@ +/* crypto/bf/blowfish.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@mincom.oz.au) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@mincom.oz.au). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@mincom.oz.au). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@mincom.oz.au)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@mincom.oz.au)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_BLOWFISH_H +#define HEADER_BLOWFISH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define BF_ENCRYPT 1 +#define BF_DECRYPT 0 + +/* If you make this 'unsigned int' the pointer variants will work on + * the Alpha, otherwise they will not. Strangly using the '8 byte' + * BF_LONG and the default 'non-pointer' inner loop is the best configuration + * for the Alpha */ +#define BF_LONG unsigned long + +#define BF_ROUNDS 16 +#define BF_BLOCK 8 + +typedef struct bf_key_st + { + BF_LONG P[BF_ROUNDS+2]; + BF_LONG S[4*256]; + } BF_KEY; + +#ifndef NOPROTO + +void BF_set_key(BF_KEY *key, int len, unsigned char *data); +void BF_ecb_encrypt(unsigned char *in,unsigned char *out,BF_KEY *key, + int encrypt); +void BF_encrypt(BF_LONG *data,BF_KEY *key,int encrypt); +void BF_cbc_encrypt(unsigned char *in, unsigned char *out, long length, + BF_KEY *ks, unsigned char *iv, int encrypt); +void BF_cfb64_encrypt(unsigned char *in, unsigned char *out, long length, + BF_KEY *schedule, unsigned char *ivec, int *num, int encrypt); +void BF_ofb64_encrypt(unsigned char *in, unsigned char *out, long length, + BF_KEY *schedule, unsigned char *ivec, int *num); +char *BF_options(void); + +#else + +void BF_set_key(); +void BF_ecb_encrypt(); +void BF_encrypt(); +void BF_cbc_encrypt(); +void BF_cfb64_encrypt(); +void BF_ofb64_encrypt(); +char *BF_options(); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/hvdi.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/hvdi.h new file mode 100644 index 00000000..c7993e09 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/Win32/HawkVoice/src/hvdi.h @@ -0,0 +1,194 @@ +/* + HawkVoice Direct Interface (HVDI) cross platform network voice library + Copyright (C) 2001 Phil Frisbie, Jr. (phil@hawksoft.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Or go to http://www.gnu.org/copyleft/lgpl.html +*/ + +#ifndef HVDI_H +#define HVDI_H + +#include "blowfish/blowfish.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define HVDI_MAJOR_VERSION 0 +#define HVDI_MINOR_VERSION 7 +#define HVDI_VERSION_STRING "HVDI 0.7 beta" + +/* This was copied from nl.h so that it did not need to be included */ + +#if defined WIN32 || defined WIN64 + #pragma warning (disable:4514) /* disable "unreferenced inline function has + been removed" warning */ + /* The default build for Windows is as a DLL. */ + /* If you want a static library, define WIN_STATIC_LIB. */ + #ifdef WIN_STATIC_LIB + #define NL_EXP + #else + #if defined __LCC__ + #define NL_EXP extern + #else + #define NL_EXP __declspec(dllexport) + #endif + #endif + #define NL_APIENTRY __stdcall + #define NL_CALLBACK __cdecl + #ifdef __GNUC__ + #define NL_INLINE extern __inline__ + #else + #define NL_INLINE __inline + #endif +#else + #define NL_EXP extern + #define NL_APIENTRY + #define NL_CALLBACK + #ifdef __GNUC__ + #define NL_INLINE extern __inline__ + #else + #define NL_INLINE inline /* assuming C99 compliant compiler */ + #endif /* __GNUC__ */ +#endif /* WIN32 || WIN64 */ + +#ifndef NL_INVALID +#define NL_INVALID (-1) +#define NL_FALSE (0) +#define NL_TRUE (1) +#endif + + +/* We will use HVDI or hvdi to prefix all HawkVoiceDI defines and functions */ + +/* + The internal state of the codec. This is READ ONLY! You can read hvdi_dec_state->codec + if you want to know what type of codec is being used on the other side, but DO NOT + write to this structure!! I could have hidden these structures behind an index, but + this IS low level quick and dirty ;) + + hvdi_enc_state and hvdi_dec_state are defined separately to help the compiler spot + your mistakes ;) +*/ +typedef struct +{ + unsigned char codec; /* the codec used with the last packet */ + unsigned short sequence;/* the sequence number of the last packet */ + void *state; /* the codec state */ +} hvdi_enc_state; + +typedef struct +{ + unsigned char codec; /* the codec used with the last packet */ + unsigned short sequence;/* the sequence number of the last packet */ + void *state; /* the codec state */ +} hvdi_dec_state; + +typedef struct +{ + int rate; /* HVDI_VOX_FAST, HVDI_VOX_MEDIUM, or HVDI_VOX_SLOW */ + int noisethreshold; /* 0(always pass) to 1000(never pass), 300 is a good starting point */ + int samplecount; /* init to 0; used internally by hvdiVOX */ +} hvdi_vox; + +typedef struct +{ + unsigned long lcmrate; /* least common multiple of rates */ + unsigned long inskip, outskip; /* LCM increments for I & O rates */ + unsigned long total; + unsigned long intot, outtot; /* total samples in terms of LCM rate */ + long lastsamp; +} hvdi_rate; + +typedef struct +{ + unsigned int sample_max; + int counter; + float gain; + float peak; + int silence_counter; +} hvdi_agc; + +/* The basic codecs, from hawkvoice.h */ +#define HV_2_4K_CODEC 0x0001 /* LPC-10 2.4 Kbps codec */ +#define HV_4_8K_CODEC 0x0002 /* LPC 4.8 Kbps codec */ +#define HV_13_2K_CODEC 0x0004 /* GSM 13.2 Kbps codec */ +#define HV_32K_CODEC 0x0008 /* Intel/DVI ADPCM 32 Kbps codec */ +#define HV_64K_CODEC 0x0010 /* G.711 u-law 64 Kbps codec */ +#define HV_1_4K_CODEC 0x0011 /* OpenLPC 1.4 Kbps codec */ +#define HV_1_8K_CODEC 0x0012 /* OpenLPC 1.8 Kbps codec */ + +/* Alternate codec names */ +#define HV_LPC10_CODEC HV_2_4K_CODEC +#define HV_LPC_CODEC HV_4_8K_CODEC +#define HV_GSM_CODEC HV_13_2K_CODEC +#define HV_ADPCM_32_CODEC HV_32K_CODEC +#define HV_PCM_64_CODEC HV_64K_CODEC +#define HV_G_711_CODEC HV_64K_CODEC +#define HV_ULAW_CODEC HV_64K_CODEC +#define HV_LPC_1_4_CODEC HV_1_4K_CODEC +#define HV_LPC_1_8_CODEC HV_1_8K_CODEC + +/* VOX options */ +/* how many samples of silence to wait after voice stops */ +#define HVDI_VOX_FAST 4000 /* 1/2 second */ +#define HVDI_VOX_MEDIUM 8000 /* 1 second */ +#define HVDI_VOX_SLOW 12000 /* 1 1/2 seconds */ + + +/* HawkVoiceDI API */ + +NL_EXP hvdi_enc_state* NL_APIENTRY hvdiCreateEncoderState(void); + +NL_EXP hvdi_dec_state* NL_APIENTRY hvdiCreateDecoderState(void); + +NL_EXP void NL_APIENTRY hvdiFreeEncoderState(hvdi_enc_state *state); + +NL_EXP void NL_APIENTRY hvdiFreeDecoderState(hvdi_dec_state *state); + +NL_EXP int NL_APIENTRY hvdiSetCodec(unsigned char codec, hvdi_enc_state *state); + +NL_EXP BF_KEY* NL_APIENTRY hvdiMakeEncryptionKey(const char *string); + +NL_EXP int NL_APIENTRY hvdiIsVoicePacket(unsigned char *packet, int length); + +NL_EXP int NL_APIENTRY hvdiDecodePacket(unsigned char *packet, int paclen, short *buffer, + int buflen, BF_KEY *key, hvdi_dec_state *state); + +NL_EXP int NL_APIENTRY hvdiEncodePacket(short *buffer, int buflen, unsigned char *packet, + int paclen, BF_KEY *key, hvdi_enc_state *state); + +NL_EXP int NL_APIENTRY hvdiVOX(short *buffer, int buflen, hvdi_vox *vox); + +NL_EXP void NL_APIENTRY hvdiRateInit(hvdi_rate *rate, int inrate, int outrate); + +NL_EXP void NL_APIENTRY hvdiRateFlow(hvdi_rate *rate, short *inbuf, short *outbuf, int *inlen, int *outlen); + +NL_EXP void NL_APIENTRY hvdiAGCInit(hvdi_agc *agc, float level); + +NL_EXP void NL_APIENTRY hvdiAGC(hvdi_agc *agc, short *buffer, int len); + +NL_EXP void NL_APIENTRY hvdiMix(short *outbuf, short **inbuf, int number, int inlen); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* HVDI_H */ + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Grammar/.cvsignore b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Grammar/.cvsignore new file mode 100644 index 00000000..5498146d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Grammar/.cvsignore @@ -0,0 +1,3 @@ +graminit.h +graminit.c +Makefile diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Grammar/Grammar b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Grammar/Grammar new file mode 100644 index 00000000..05b5911f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Grammar/Grammar @@ -0,0 +1,107 @@ +# Grammar for Python + +# Note: Changing the grammar specified in this file will most likely +# require corresponding changes in the parser module +# (../Modules/parsermodule.c). If you can't make the changes to +# that module yourself, please co-ordinate the required changes +# with someone who can; ask around on python-dev for help. Fred +# Drake will probably be listening there. + +# Commands for Kees Blom's railroad program +#diagram:token NAME +#diagram:token NUMBER +#diagram:token STRING +#diagram:token NEWLINE +#diagram:token ENDMARKER +#diagram:token INDENT +#diagram:output\input python.bla +#diagram:token DEDENT +#diagram:output\textwidth 20.04cm\oddsidemargin 0.0cm\evensidemargin 0.0cm +#diagram:rules + +# Start symbols for the grammar: +# single_input is a single interactive statement; +# file_input is a module or sequence of commands read from an input file; +# eval_input is the input for the eval() and input() functions. +# NB: compound_stmt in single_input is followed by extra NEWLINE! +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: (NEWLINE | stmt)* ENDMARKER +eval_input: testlist NEWLINE* ENDMARKER + +funcdef: 'def' NAME parameters ':' suite +parameters: '(' [varargslist] ')' +varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) | fpdef ['=' test] (',' fpdef ['=' test])* [','] +fpdef: NAME | '(' fplist ')' +fplist: fpdef (',' fpdef)* [','] + +stmt: simple_stmt | compound_stmt +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | exec_stmt | assert_stmt +expr_stmt: testlist (augassign testlist | ('=' testlist)*) +augassign: '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=' +# For normal assignments, additional restrictions enforced by the interpreter +print_stmt: 'print' ( [ test (',' test)* [','] ] | '>>' test [ (',' test)+ [','] ] ) +del_stmt: 'del' exprlist +pass_stmt: 'pass' +flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt +break_stmt: 'break' +continue_stmt: 'continue' +return_stmt: 'return' [testlist] +yield_stmt: 'yield' testlist +raise_stmt: 'raise' [test [',' test [',' test]]] +import_stmt: 'import' dotted_as_name (',' dotted_as_name)* | 'from' dotted_name 'import' ('*' | import_as_name (',' import_as_name)*) +import_as_name: NAME [NAME NAME] +dotted_as_name: dotted_name [NAME NAME] +dotted_name: NAME ('.' NAME)* +global_stmt: 'global' NAME (',' NAME)* +exec_stmt: 'exec' expr ['in' test [',' test]] +assert_stmt: 'assert' test [',' test] + +compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef +if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] +while_stmt: 'while' test ':' suite ['else' ':' suite] +for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] +try_stmt: ('try' ':' suite (except_clause ':' suite)+ #diagram:break + ['else' ':' suite] | 'try' ':' suite 'finally' ':' suite) +# NB compile.c makes sure that the default except clause is last +except_clause: 'except' [test [',' test]] +suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT + +test: and_test ('or' and_test)* | lambdef +and_test: not_test ('and' not_test)* +not_test: 'not' not_test | comparison +comparison: expr (comp_op expr)* +comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not' +expr: xor_expr ('|' xor_expr)* +xor_expr: and_expr ('^' and_expr)* +and_expr: shift_expr ('&' shift_expr)* +shift_expr: arith_expr (('<<'|'>>') arith_expr)* +arith_expr: term (('+'|'-') term)* +term: factor (('*'|'/'|'%'|'//') factor)* +factor: ('+'|'-'|'~') factor | power +power: atom trailer* ['**' factor] +atom: '(' [testlist] ')' | '[' [listmaker] ']' | '{' [dictmaker] '}' | '`' testlist1 '`' | NAME | NUMBER | STRING+ +listmaker: test ( list_for | (',' test)* [','] ) +lambdef: 'lambda' [varargslist] ':' test +trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME +subscriptlist: subscript (',' subscript)* [','] +subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop] +sliceop: ':' [test] +exprlist: expr (',' expr)* [','] +testlist: test (',' test)* [','] +testlist_safe: test [(',' test)+ [',']] +dictmaker: test ':' test (',' test ':' test)* [','] + +classdef: 'class' NAME ['(' testlist ')'] ':' suite + +arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test) +argument: [test '='] test # Really [keyword '='] test + +list_iter: list_for | list_if +list_for: 'for' exprlist 'in' testlist_safe [list_iter] +list_if: 'if' test [list_iter] + +testlist1: test (',' test)* + +# not used in grammar, but may appear in "node" passed from Parser to Compiler +encoding_decl: NAME diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/LICENSE b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/LICENSE new file mode 100644 index 00000000..73ef1c4b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/LICENSE @@ -0,0 +1,262 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations (now Zope +Corporation, see http://www.zope.com). In 2001, the Python Software +Foundation (PSF, see http://www.python.org/psf/) was formed, a +non-profit organization created specifically to own Python-related +Intellectual Property. Zope Corporation is a sponsoring member of +the PSF. + +All Python releases are Open Source (see http://www.opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.2 2.1.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2.1 2.2 2002 PSF yes + 2.2.2 2.2.1 2002 PSF yes + 2.2.3 2.2.2 2003 PSF yes + 2.3 2.2.2 2002-2003 PSF yes + 2.3.1 2.3 2002-2003 PSF yes + 2.3.2 2.3.1 2002-2003 PSF yes + 2.3.3 2.3.2 2002-2003 PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +PSF LICENSE AGREEMENT FOR PYTHON 2.3 +------------------------------------ + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using Python 2.3 software in source or binary form and its +associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 2.3 +alone or in any derivative version, provided, however, that PSF's +License Agreement and PSF's notice of copyright, i.e., "Copyright (c) +2001, 2002, 2003 Python Software Foundation; All Rights Reserved" are +retained in Python 2.3 alone or in any derivative version prepared by +Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 2.3 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 2.3. + +4. PSF is making Python 2.3 available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.3 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +2.3 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.3, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python 2.3, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the Internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the Internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/.cvsignore b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/.cvsignore new file mode 100644 index 00000000..11c89aa0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/.cvsignore @@ -0,0 +1,9 @@ +Setup +Makefile.pre +Setup.thread +Setup.config +Setup.local +hassignal +config.c +Makefile +add2lib diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/Setup.config.in b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/Setup.config.in new file mode 100644 index 00000000..9358a7bb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/Setup.config.in @@ -0,0 +1,13 @@ +# This file is transmogrified into Setup.config by config.status. + +# The purpose of this file is to conditionally enable certain modules +# based on configure-time options. + +# Threading +@USE_THREAD_MODULE@thread threadmodule.c + +# The signal module +@USE_SIGNAL_MODULE@signal signalmodule.c + +# The rest of the modules previously listed in this file are built +# by the setup.py script in Python 2.1 and later. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/Setup.dist b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/Setup.dist new file mode 100644 index 00000000..7dada1c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/Setup.dist @@ -0,0 +1,486 @@ +# -*- makefile -*- +# The file Setup is used by the makesetup script to construct the files +# Makefile and config.c, from Makefile.pre and config.c.in, +# respectively. The file Setup itself is initially copied from +# Setup.dist; once it exists it will not be overwritten, so you can edit +# Setup to your heart's content. Note that Makefile.pre is created +# from Makefile.pre.in by the toplevel configure script. + +# (VPATH notes: Setup and Makefile.pre are in the build directory, as +# are Makefile and config.c; the *.in and *.dist files are in the source +# directory.) + +# Each line in this file describes one or more optional modules. +# Modules enabled here will not be compiled by the setup.py script, +# so the file can be used to override setup.py's behavior. + +# Lines have the following structure: +# +# ... [ ...] [ ...] [ ...] +# +# is anything ending in .c (.C, .cc, .c++ are C++ files) +# is anything starting with -I, -D, -U or -C +# is anything ending in .a or beginning with -l or -L +# is anything else but should be a valid Python +# identifier (letters, digits, underscores, beginning with non-digit) +# +# (As the makesetup script changes, it may recognize some other +# arguments as well, e.g. *.so and *.sl as libraries. See the big +# case statement in the makesetup script.) +# +# Lines can also have the form +# +# = +# +# which defines a Make variable definition inserted into Makefile.in +# +# Finally, if a line contains just the word "*shared*" (without the +# quotes but with the stars), then the following modules will not be +# built statically. The build process works like this: +# +# 1. Build all modules that are declared as static in Modules/Setup, +# combine them into libpythonxy.a, combine that into python. +# 2. Build all modules that are listed as shared in Modules/Setup. +# 3. Invoke setup.py. That builds all modules that +# a) are not builtin, and +# b) are not listed in Modules/Setup, and +# c) can be build on the target +# +# Therefore, modules declared to be shared will not be +# included in the config.c file, nor in the list of objects to be +# added to the library archive, and their linker options won't be +# added to the linker options. Rules to create their .o files and +# their shared libraries will still be added to the Makefile, and +# their names will be collected in the Make variable SHAREDMODS. This +# is used to build modules as shared libraries. (They can be +# installed using "make sharedinstall", which is implied by the +# toplevel "make install" target.) (For compatibility, +# *noconfig* has the same effect as *shared*.) +# +# In addition, *static* explicitly declares the following modules to +# be static. Lines containing "*static*" and "*shared*" may thus +# alternate thoughout this file. + +# NOTE: As a standard policy, as many modules as can be supported by a +# platform should be present. The distribution comes with all modules +# enabled that are supported by most platforms and don't require you +# to ftp sources from elsewhere. + + +# Some special rules to define PYTHONPATH. +# Edit the definitions below to indicate which options you are using. +# Don't add any whitespace or comments! + +# Directories where library files get installed. +# DESTLIB is for Python modules; MACHDESTLIB for shared libraries. +DESTLIB=$(LIBDEST) +MACHDESTLIB=$(BINLIBDEST) + +# NOTE: all the paths are now relative to the prefix that is computed +# at run time! + +# Standard path -- don't edit. +# No leading colon since this is the first entry. +# Empty since this is now just the runtime prefix. +DESTPATH= + +# Site specific path components -- should begin with : if non-empty +SITEPATH= + +# Standard path components for test modules +TESTPATH= + +# Path components for machine- or system-dependent modules and shared libraries +MACHDEPPATH=:plat-$(MACHDEP) +EXTRAMACHDEPPATH= + +# Path component for the Tkinter-related modules +# The TKPATH variable is always enabled, to save you the effort. +TKPATH=:lib-tk + +COREPYTHONPATH=$(DESTPATH)$(SITEPATH)$(TESTPATH)$(MACHDEPPATH)$(EXTRAMACHDEPPATH)$(TKPATH) +PYTHONPATH=$(COREPYTHONPATH) + + +# The modules listed here can't be built as shared libraries for +# various reasons; therefore they are listed here instead of in the +# normal order. + +# This only contains the minimal set of modules required to run the +# setup.py script in the root of the Python source tree. + +posix posixmodule.c # posix (UNIX) system calls +errno errnomodule.c # posix (UNIX) errno values +_sre _sre.c # Fredrik Lundh's new regular expressions +_codecs _codecsmodule.c # access to the builtin codecs and codec registry + +# The zipimport module is always imported at startup. Having it as a +# builtin module avoids some bootstrapping problems and reduces overhead. +zipimport zipimport.c + +# The rest of the modules listed in this file are all commented out by +# default. Usually they can be detected and built as dynamically +# loaded modules by the new setup.py script added in Python 2.1. If +# you're on a platform that doesn't support dynamic loading, want to +# compile modules statically into the Python binary, or need to +# specify some odd set of compiler switches, you can uncomment the +# appropriate lines below. + +# ====================================================================== + +# The Python symtable module depends on .h files that setup.py doesn't track +_symtable symtablemodule.c + +# The SGI specific GL module: + +GLHACK=-Dclear=__GLclear +#gl glmodule.c cgensupport.c -I$(srcdir) $(GLHACK) -lgl -lX11 + +# Pure module. Cannot be linked dynamically. +# -DWITH_QUANTIFY, -DWITH_PURIFY, or -DWITH_ALL_PURE +#WHICH_PURE_PRODUCTS=-DWITH_ALL_PURE +#PURE_INCLS=-I/usr/local/include +#PURE_STUBLIBS=-L/usr/local/lib -lpurify_stubs -lquantify_stubs +#pure puremodule.c $(WHICH_PURE_PRODUCTS) $(PURE_INCLS) $(PURE_STUBLIBS) + +# Uncommenting the following line tells makesetup that all following +# modules are to be built as shared libraries (see above for more +# detail; also note that *static* reverses this effect): + +#*shared* + +# GNU readline. Unlike previous Python incarnations, GNU readline is +# now incorporated in an optional module, configured in the Setup file +# instead of by a configure script switch. You may have to insert a +# -L option pointing to the directory where libreadline.* lives, +# and you may have to change -ltermcap to -ltermlib or perhaps remove +# it, depending on your system -- see the GNU readline instructions. +# It's okay for this to be a shared library, too. + +#readline readline.c -lreadline -ltermcap + + +# Modules that should always be present (non UNIX dependent): + +#array arraymodule.c # array objects +#cmath cmathmodule.c # -lm # complex math library functions +#math mathmodule.c # -lm # math library functions, e.g. sin() +#struct structmodule.c # binary structure packing/unpacking +#time timemodule.c # -lm # time operations and variables +#operator operator.c # operator.add() and similar goodies +#_weakref _weakref.c # basic weak reference support +#_testcapi _testcapimodule.c # Python C API test module + +#unicodedata unicodedata.c # static Unicode character database + +# access to ISO C locale support +#_locale _localemodule.c # -lintl + + +# Modules with some UNIX dependencies -- on by default: +# (If you have a really backward UNIX, select and socket may not be +# supported...) + +#fcntl fcntlmodule.c # fcntl(2) and ioctl(2) +#pwd pwdmodule.c # pwd(3) +#grp grpmodule.c # grp(3) +#select selectmodule.c # select(2); not on ancient System V + +# Memory-mapped files (also works on Win32). +#mmap mmapmodule.c + +# Dynamic readlines +#xreadlines xreadlinesmodule.c + +# CSV file helper +#_csv _csv.c + +# Socket module helper for socket(2) +#_socket socketmodule.c + +# Socket module helper for SSL support; you must comment out the other +# socket line above, and possibly edit the SSL variable: +#SSL=/usr/local/ssl +#_ssl _ssl.c \ +# -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \ +# -L$(SSL)/lib -lssl -lcrypto + +# The crypt module is now disabled by default because it breaks builds +# on many systems (where -lcrypt is needed), e.g. Linux (I believe). +# +# First, look at Setup.config; configure may have set this for you. + +#crypt cryptmodule.c # -lcrypt # crypt(3); needs -lcrypt on some systems + + +# Some more UNIX dependent modules -- off by default, since these +# are not supported by all UNIX systems: + +#nis nismodule.c -lnsl # Sun yellow pages -- not everywhere +#termios termios.c # Steen Lumholt's termios module +#resource resource.c # Jeremy Hylton's rlimit interface + + +# Multimedia modules -- off by default. +# These don't work for 64-bit platforms!!! +# These represent audio samples or images as strings: + +#audioop audioop.c # Operations on audio samples +#imageop imageop.c # Operations on images +#rgbimg rgbimgmodule.c # Read SGI RGB image files (but coded portably) + + +# The md5 module implements the RSA Data Security, Inc. MD5 +# Message-Digest Algorithm, described in RFC 1321. The necessary files +# md5c.c and md5.h are included here. + +#md5 md5module.c md5c.c + + +# The sha module implements the SHA checksum algorithm. +# (NIST's Secure Hash Algorithm.) +#sha shamodule.c + + +# The mpz module interfaces to the GNU Multiple Precision library. +# You need to ftp the GNU MP library. +# The GMP variable must point to the GMP source directory. +# This was originally written and tested against GMP 1.2 and 1.3.2. +# It has been modified by Rob Hooft to work with 2.0.2 as well, but I +# haven't tested it recently. + +# A compatible MP library unencombered by the GPL also exists. It was +# posted to comp.sources.misc in volume 40 and is widely available from +# FTP archive sites. One URL for it is: +# ftp://gatekeeper.dec.com/.b/usenet/comp.sources.misc/volume40/fgmp/part01.Z + +#GMP=/ufs/guido/src/gmp +#mpz mpzmodule.c -I$(GMP) $(GMP)/libgmp.a + + +# SGI IRIX specific modules -- off by default. + +# These module work on any SGI machine: + +# *** gl must be enabled higher up in this file *** +#fm fmmodule.c $(GLHACK) -lfm -lgl # Font Manager +#sgi sgimodule.c # sgi.nap() and a few more + +# This module requires the header file +# /usr/people/4Dgifts/iristools/include/izoom.h: +#imgfile imgfile.c -limage -lgutil -lgl -lm # Image Processing Utilities + + +# These modules require the Multimedia Development Option (I think): + +#al almodule.c -laudio # Audio Library +#cd cdmodule.c -lcdaudio -lds -lmediad # CD Audio Library +#cl clmodule.c -lcl -lawareaudio # Compression Library +#sv svmodule.c yuvconvert.c -lsvideo -lXext -lX11 # Starter Video + + +# The FORMS library, by Mark Overmars, implements user interface +# components such as dialogs and buttons using SGI's GL and FM +# libraries. You must ftp the FORMS library separately from +# ftp://ftp.cs.ruu.nl/pub/SGI/FORMS. It was tested with FORMS 2.2a. +# NOTE: if you want to be able to use FORMS and curses simultaneously +# (or both link them statically into the same binary), you must +# compile all of FORMS with the cc option "-Dclear=__GLclear". + +# The FORMS variable must point to the FORMS subdirectory of the forms +# toplevel directory: + +#FORMS=/ufs/guido/src/forms/FORMS +#fl flmodule.c -I$(FORMS) $(GLHACK) $(FORMS)/libforms.a -lfm -lgl + + +# SunOS specific modules -- off by default: + +#sunaudiodev sunaudiodev.c + + +# A Linux specific module -- off by default; this may also work on +# some *BSDs. + +#linuxaudiodev linuxaudiodev.c + + +# George Neville-Neil's timing module: + +#timing timingmodule.c + + +# The _tkinter module. +# +# The command for _tkinter is long and site specific. Please +# uncomment and/or edit those parts as indicated. If you don't have a +# specific extension (e.g. Tix or BLT), leave the corresponding line +# commented out. (Leave the trailing backslashes in! If you +# experience strange errors, you may want to join all uncommented +# lines and remove the backslashes -- the backslash interpretation is +# done by the shell's "read" command and it may not be implemented on +# every system. + +# *** Always uncomment this (leave the leading underscore in!): +# _tkinter _tkinter.c tkappinit.c -DWITH_APPINIT \ +# *** Uncomment and edit to reflect where your Tcl/Tk libraries are: +# -L/usr/local/lib \ +# *** Uncomment and edit to reflect where your Tcl/Tk headers are: +# -I/usr/local/include \ +# *** Uncomment and edit to reflect where your X11 header files are: +# -I/usr/X11R6/include \ +# *** Or uncomment this for Solaris: +# -I/usr/openwin/include \ +# *** Uncomment and edit for Tix extension only: +# -DWITH_TIX -ltix8.1.8.2 \ +# *** Uncomment and edit for BLT extension only: +# -DWITH_BLT -I/usr/local/blt/blt8.0-unoff/include -lBLT8.0 \ +# *** Uncomment and edit for PIL (TkImaging) extension only: +# (See http://www.pythonware.com/products/pil/ for more info) +# -DWITH_PIL -I../Extensions/Imaging/libImaging tkImaging.c \ +# *** Uncomment and edit for TOGL extension only: +# -DWITH_TOGL togl.c \ +# *** Uncomment and edit to reflect your Tcl/Tk versions: +# -ltk8.2 -ltcl8.2 \ +# *** Uncomment and edit to reflect where your X11 libraries are: +# -L/usr/X11R6/lib \ +# *** Or uncomment this for Solaris: +# -L/usr/openwin/lib \ +# *** Uncomment these for TOGL extension only: +# -lGL -lGLU -lXext -lXmu \ +# *** Uncomment for AIX: +# -lld \ +# *** Always uncomment this; X11 libraries to link with: +# -lX11 + +# Lance Ellinghaus's modules: + +#rotor rotormodule.c # enigma-inspired encryption +#syslog syslogmodule.c # syslog daemon interface + + +# Curses support, requring the System V version of curses, often +# provided by the ncurses library. e.g. on Linux, link with -lncurses +# instead of -lcurses; on SunOS 4.1.3, insert -I/usr/5include +# -L/usr/5lib before -lcurses). +# +# First, look at Setup.config; configure may have set this for you. + +#_curses _cursesmodule.c -lcurses -ltermcap +# Wrapper for the panel library that's part of ncurses and SYSV curses. +#_curses_panel _curses_panel.c -lpanel -lncurses + + +# Generic (SunOS / SVR4) dynamic loading module. +# This is not needed for dynamic loading of Python modules -- +# it is a highly experimental and dangerous device for calling +# *arbitrary* C functions in *arbitrary* shared libraries: + +#dl dlmodule.c + + +# Modules that provide persistent dictionary-like semantics. You will +# probably want to arrange for at least one of them to be available on +# your machine, though none are defined by default because of library +# dependencies. The Python module anydbm.py provides an +# implementation independent wrapper for these; dumbdbm.py provides +# similar functionality (but slower of course) implemented in Python. + +# The standard Unix dbm module has been moved to Setup.config so that +# it will be compiled as a shared library by default. Compiling it as +# a built-in module causes conflicts with the pybsddb3 module since it +# creates a static dependency on an out-of-date version of db.so. +# +# First, look at Setup.config; configure may have set this for you. + +#dbm dbmmodule.c # dbm(3) may require -lndbm or similar + +# Anthony Baxter's gdbm module. GNU dbm(3) will require -lgdbm: +# +# First, look at Setup.config; configure may have set this for you. + +#gdbm gdbmmodule.c -I/usr/local/include -L/usr/local/lib -lgdbm + + +# Sleepycat Berkeley DB interface. +# +# This requires the Sleepycat DB code, see http://www.sleepycat.com/ +# The earliest supported version of that library is 3.0, the latest +# supported version is 4.0 (4.1 is specifically not supported, as that +# changes the semantics of transactional databases). A list of available +# releases can be found at +# +# http://www.sleepycat.com/update/index.html +# +# Edit the variables DB and DBLIBVERto point to the db top directory +# and the subdirectory of PORT where you built it. +#DB=/usr/local/BerkeleyDB.4.0 +#DBLIBVER=4.0 +#DBINC=$(DB)/include +#DBLIB=$(DB)/lib +#_bsddb _bsddb.c -I$(DBINC) -L$(DBLIB) -ldb-$(DBLIBVER) + +# Historical Berkeley DB 1.85 +# +# This module is deprecated; the 1.85 version of the Berkeley DB library has +# bugs that can cause data corruption. If you can, use later versions of the +# library instead, available from . + +#DB=/depot/sundry/src/berkeley-db/db.1.85 +#DBPORT=$(DB)/PORT/irix.5.3 +#bsddb185 bsddbmodule.c -I$(DBPORT)/include -I$(DBPORT) $(DBPORT)/libdb.a + + + +# Helper module for various ascii-encoders +#binascii binascii.c + +# Fred Drake's interface to the Python parser +#parser parsermodule.c + +# cStringIO and cPickle +#cStringIO cStringIO.c +#cPickle cPickle.c + + +# Lee Busby's SIGFPE modules. +# The library to link fpectl with is platform specific. +# Choose *one* of the options below for fpectl: + +# For SGI IRIX (tested on 5.3): +#fpectl fpectlmodule.c -lfpe + +# For Solaris with SunPro compiler (tested on Solaris 2.5 with SunPro C 4.2): +# (Without the compiler you don't have -lsunmath.) +#fpectl fpectlmodule.c -R/opt/SUNWspro/lib -lsunmath -lm + +# For other systems: see instructions in fpectlmodule.c. +#fpectl fpectlmodule.c ... + +# Test module for fpectl. No extra libraries needed. +#fpetest fpetestmodule.c + +# Andrew Kuchling's zlib module. +# This require zlib 1.1.3 (or later). +# See http://www.cdrom.com/pub/infozip/zlib/ +#zlib zlibmodule.c -I$(prefix)/include -L$(exec_prefix)/lib -lz + +# Interface to the Expat XML parser +# +# Expat was written by James Clark and is now maintained by a group of +# developers on SourceForge; see www.libexpat.org for more +# information. The pyexpat module was written by Paul Prescod after a +# prototype by Jack Jansen. Source of Expat 1.95.2 is included in +# Modules/expat/. Usage of a system shared libexpat.so/expat.dll is +# not advised. +# +# More information on Expat can be found at www.libexpat.org. +# +#EXPAT_DIR=/usr/local/src/expat-1.95.2 +#pyexpat pyexpat.c -DHAVE_EXPAT_H -I$(EXPAT_DIR)/lib -L$(EXPAT_DIR) -lexpat + +# Example -- included for reference only: +# xx xxmodule.c + +# Another example -- the 'xxsubtype' module shows C-level subtyping in action +xxsubtype xxsubtype.c diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_bsddb.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_bsddb.c new file mode 100644 index 00000000..51923c65 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_bsddb.c @@ -0,0 +1,4715 @@ +/*---------------------------------------------------------------------- + Copyright (c) 1999-2001, Digital Creations, Fredericksburg, VA, USA + and Andrew Kuchling. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + o Redistributions of source code must retain the above copyright + notice, this list of conditions, and the disclaimer that follows. + + o Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + o Neither the name of Digital Creations nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS AND CONTRIBUTORS *AS + IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL + CREATIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. +------------------------------------------------------------------------*/ + + +/* + * Handwritten code to wrap version 3.x of the Berkeley DB library, + * written to replace a SWIG-generated file. It has since been updated + * to compile with BerkeleyDB versions 3.2 through 4.2. + * + * This module was started by Andrew Kuchling to remove the dependency + * on SWIG in a package by Gregory P. Smith who + * based his work on a similar package by Robin Dunn + * which wrapped Berkeley DB 2.7.x. + * + * Development of this module then returned full circle back to Robin Dunn + * who worked on behalf of Digital Creations to complete the wrapping of + * the DB 3.x API and to build a solid unit test suite. Robin has + * since gone onto other projects (wxPython). + * + * Gregory P. Smith is once again the maintainer. + * + * Use the pybsddb-users@lists.sf.net mailing list for all questions. + * Things can change faster than the header of this file is updated. This + * file is shared with the PyBSDDB project at SourceForge: + * + * http://pybsddb.sf.net + * + * This file should remain backward compatible with Python 2.1, but see PEP + * 291 for the most current backward compatibility requirements: + * + * http://www.python.org/peps/pep-0291.html + * + * This module contains 5 types: + * + * DB (Database) + * DBCursor (Database Cursor) + * DBEnv (database environment) + * DBTxn (An explicit database transaction) + * DBLock (A lock handle) + * + */ + +/* --------------------------------------------------------------------- */ + +/* + * Portions of this module, associated unit tests and build scripts are the + * result of a contract with The Written Word (http://thewrittenword.com/) + * Many thanks go out to them for causing me to raise the bar on quality and + * functionality, resulting in a better bsddb3 package for all of us to use. + * + * --Robin + */ + +/* --------------------------------------------------------------------- */ + +#include +#include + +/* --------------------------------------------------------------------- */ +/* Various macro definitions */ + +/* 40 = 4.0, 33 = 3.3; this will break if the second number is > 9 */ +#define DBVER (DB_VERSION_MAJOR * 10 + DB_VERSION_MINOR) + +#define PY_BSDDB_VERSION "4.2.0" +static char *rcs_id = "$Id: _bsddb.c,v 1.17.6.2 2003/09/21 23:10:23 greg Exp $"; + + +#ifdef WITH_THREAD + +/* These are for when calling Python --> C */ +#define MYDB_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS; +#define MYDB_END_ALLOW_THREADS Py_END_ALLOW_THREADS; + +/* For 2.3, use the PyGILState_ calls */ +#if (PY_VERSION_HEX >= 0x02030000) +#define MYDB_USE_GILSTATE +#endif + +/* and these are for calling C --> Python */ +#if defined(MYDB_USE_GILSTATE) +#define MYDB_BEGIN_BLOCK_THREADS \ + PyGILState_STATE __savestate = PyGILState_Ensure(); +#define MYDB_END_BLOCK_THREADS \ + PyGILState_Release(__savestate); +#else /* MYDB_USE_GILSTATE */ +/* Pre GILState API - do it the long old way */ +static PyInterpreterState* _db_interpreterState = NULL; +#define MYDB_BEGIN_BLOCK_THREADS { \ + PyThreadState* prevState; \ + PyThreadState* newState; \ + PyEval_AcquireLock(); \ + newState = PyThreadState_New(_db_interpreterState); \ + prevState = PyThreadState_Swap(newState); + +#define MYDB_END_BLOCK_THREADS \ + newState = PyThreadState_Swap(prevState); \ + PyThreadState_Clear(newState); \ + PyEval_ReleaseLock(); \ + PyThreadState_Delete(newState); \ + } +#endif /* MYDB_USE_GILSTATE */ + +#else +/* Compiled without threads - avoid all this cruft */ +#define MYDB_BEGIN_ALLOW_THREADS +#define MYDB_END_ALLOW_THREADS +#define MYDB_BEGIN_BLOCK_THREADS +#define MYDB_END_BLOCK_THREADS + +#endif + +/* Should DB_INCOMPLETE be turned into a warning or an exception? */ +#define INCOMPLETE_IS_WARNING 1 + +/* --------------------------------------------------------------------- */ +/* Exceptions */ + +static PyObject* DBError; /* Base class, all others derive from this */ +static PyObject* DBKeyEmptyError; /* DB_KEYEMPTY */ +static PyObject* DBKeyExistError; /* DB_KEYEXIST */ +static PyObject* DBLockDeadlockError; /* DB_LOCK_DEADLOCK */ +static PyObject* DBLockNotGrantedError; /* DB_LOCK_NOTGRANTED */ +static PyObject* DBNotFoundError; /* DB_NOTFOUND: also derives from KeyError */ +static PyObject* DBOldVersionError; /* DB_OLD_VERSION */ +static PyObject* DBRunRecoveryError; /* DB_RUNRECOVERY */ +static PyObject* DBVerifyBadError; /* DB_VERIFY_BAD */ +static PyObject* DBNoServerError; /* DB_NOSERVER */ +static PyObject* DBNoServerHomeError; /* DB_NOSERVER_HOME */ +static PyObject* DBNoServerIDError; /* DB_NOSERVER_ID */ +#if (DBVER >= 33) +static PyObject* DBPageNotFoundError; /* DB_PAGE_NOTFOUND */ +static PyObject* DBSecondaryBadError; /* DB_SECONDARY_BAD */ +#endif + +#if !INCOMPLETE_IS_WARNING +static PyObject* DBIncompleteError; /* DB_INCOMPLETE */ +#endif + +static PyObject* DBInvalidArgError; /* EINVAL */ +static PyObject* DBAccessError; /* EACCES */ +static PyObject* DBNoSpaceError; /* ENOSPC */ +static PyObject* DBNoMemoryError; /* ENOMEM */ +static PyObject* DBAgainError; /* EAGAIN */ +static PyObject* DBBusyError; /* EBUSY */ +static PyObject* DBFileExistsError; /* EEXIST */ +static PyObject* DBNoSuchFileError; /* ENOENT */ +static PyObject* DBPermissionsError; /* EPERM */ + + + +/* --------------------------------------------------------------------- */ +/* Structure definitions */ + +struct behaviourFlags { + /* What is the default behaviour when DB->get or DBCursor->get returns a + DB_NOTFOUND error? Return None or raise an exception? */ + unsigned int getReturnsNone : 1; + /* What is the default behaviour for DBCursor.set* methods when DBCursor->get + * returns a DB_NOTFOUND error? Return None or raise an exception? */ + unsigned int cursorSetReturnsNone : 1; +}; + +#define DEFAULT_GET_RETURNS_NONE 1 +#define DEFAULT_CURSOR_SET_RETURNS_NONE 0 /* 0 in pybsddb < 4.2, python < 2.4 */ + +typedef struct { + PyObject_HEAD + DB_ENV* db_env; + u_int32_t flags; /* saved flags from open() */ + int closed; + struct behaviourFlags moduleFlags; +} DBEnvObject; + + +typedef struct { + PyObject_HEAD + DB* db; + DBEnvObject* myenvobj; /* PyObject containing the DB_ENV */ + u_int32_t flags; /* saved flags from open() */ + u_int32_t setflags; /* saved flags from set_flags() */ + int haveStat; + struct behaviourFlags moduleFlags; +#if (DBVER >= 33) + PyObject* associateCallback; + int primaryDBType; +#endif +} DBObject; + + +typedef struct { + PyObject_HEAD + DBC* dbc; + DBObject* mydb; +} DBCursorObject; + + +typedef struct { + PyObject_HEAD + DB_TXN* txn; +} DBTxnObject; + + +typedef struct { + PyObject_HEAD + DB_LOCK lock; +} DBLockObject; + + + +staticforward PyTypeObject DB_Type, DBCursor_Type, DBEnv_Type, DBTxn_Type, DBLock_Type; + +#define DBObject_Check(v) ((v)->ob_type == &DB_Type) +#define DBCursorObject_Check(v) ((v)->ob_type == &DBCursor_Type) +#define DBEnvObject_Check(v) ((v)->ob_type == &DBEnv_Type) +#define DBTxnObject_Check(v) ((v)->ob_type == &DBTxn_Type) +#define DBLockObject_Check(v) ((v)->ob_type == &DBLock_Type) + + +/* --------------------------------------------------------------------- */ +/* Utility macros and functions */ + +#define RETURN_IF_ERR() \ + if (makeDBError(err)) { \ + return NULL; \ + } + +#define RETURN_NONE() Py_INCREF(Py_None); return Py_None; + +#define CHECK_DB_NOT_CLOSED(dbobj) \ + if (dbobj->db == NULL) { \ + PyErr_SetObject(DBError, Py_BuildValue("(is)", 0, \ + "DB object has been closed")); \ + return NULL; \ + } + +#define CHECK_ENV_NOT_CLOSED(env) \ + if (env->db_env == NULL) { \ + PyErr_SetObject(DBError, Py_BuildValue("(is)", 0, \ + "DBEnv object has been closed"));\ + return NULL; \ + } + +#define CHECK_CURSOR_NOT_CLOSED(curs) \ + if (curs->dbc == NULL) { \ + PyErr_SetObject(DBError, Py_BuildValue("(is)", 0, \ + "DBCursor object has been closed"));\ + return NULL; \ + } + + + +#define CHECK_DBFLAG(mydb, flag) (((mydb)->flags & (flag)) || \ + (((mydb)->myenvobj != NULL) && ((mydb)->myenvobj->flags & (flag)))) + +#define CLEAR_DBT(dbt) (memset(&(dbt), 0, sizeof(dbt))) + +#define FREE_DBT(dbt) if ((dbt.flags & (DB_DBT_MALLOC|DB_DBT_REALLOC)) && \ + dbt.data != NULL) { free(dbt.data); } + + +static int makeDBError(int err); + + +/* Return the access method type of the DBObject */ +static int _DB_get_type(DBObject* self) +{ +#if (DBVER >= 33) + DBTYPE type; + int err; + err = self->db->get_type(self->db, &type); + if (makeDBError(err)) { + return -1; + } + return type; +#else + return self->db->get_type(self->db); +#endif +} + + +/* Create a DBT structure (containing key and data values) from Python + strings. Returns 1 on success, 0 on an error. */ +static int make_dbt(PyObject* obj, DBT* dbt) +{ + CLEAR_DBT(*dbt); + if (obj == Py_None) { + /* no need to do anything, the structure has already been zeroed */ + } + else if (!PyArg_Parse(obj, "s#", &dbt->data, &dbt->size)) { + PyErr_SetString(PyExc_TypeError, + "Key and Data values must be of type string or None."); + return 0; + } + return 1; +} + + +/* Recno and Queue DBs can have integer keys. This function figures out + what's been given, verifies that it's allowed, and then makes the DBT. + + Caller should call FREE_DBT(key) when done. */ +static int +make_key_dbt(DBObject* self, PyObject* keyobj, DBT* key, int* pflags) +{ + db_recno_t recno; + int type; + + CLEAR_DBT(*key); + if (keyobj == Py_None) { /* TODO: is None really okay for keys? */ + /* no need to do anything, the structure has already been zeroed */ + } + + else if (PyString_Check(keyobj)) { + /* verify access method type */ + type = _DB_get_type(self); + if (type == -1) + return 0; + if (type == DB_RECNO || type == DB_QUEUE) { + PyErr_SetString( + PyExc_TypeError, + "String keys not allowed for Recno and Queue DB's"); + return 0; + } + + key->data = PyString_AS_STRING(keyobj); + key->size = PyString_GET_SIZE(keyobj); + } + + else if (PyInt_Check(keyobj)) { + /* verify access method type */ + type = _DB_get_type(self); + if (type == -1) + return 0; + if (type == DB_BTREE && pflags != NULL) { + /* if BTREE then an Integer key is allowed with the + * DB_SET_RECNO flag */ + *pflags |= DB_SET_RECNO; + } + else if (type != DB_RECNO && type != DB_QUEUE) { + PyErr_SetString( + PyExc_TypeError, + "Integer keys only allowed for Recno and Queue DB's"); + return 0; + } + + /* Make a key out of the requested recno, use allocated space so DB + * will be able to realloc room for the real key if needed. */ + recno = PyInt_AS_LONG(keyobj); + key->data = malloc(sizeof(db_recno_t)); + if (key->data == NULL) { + PyErr_SetString(PyExc_MemoryError, "Key memory allocation failed"); + return 0; + } + key->ulen = key->size = sizeof(db_recno_t); + memcpy(key->data, &recno, sizeof(db_recno_t)); + key->flags = DB_DBT_REALLOC; + } + else { + PyErr_Format(PyExc_TypeError, + "String or Integer object expected for key, %s found", + keyobj->ob_type->tp_name); + return 0; + } + + return 1; +} + + +/* Add partial record access to an existing DBT data struct. + If dlen and doff are set, then the DB_DBT_PARTIAL flag will be set + and the data storage/retrieval will be done using dlen and doff. */ +static int add_partial_dbt(DBT* d, int dlen, int doff) { + /* if neither were set we do nothing (-1 is the default value) */ + if ((dlen == -1) && (doff == -1)) { + return 1; + } + + if ((dlen < 0) || (doff < 0)) { + PyErr_SetString(PyExc_TypeError, "dlen and doff must both be >= 0"); + return 0; + } + + d->flags = d->flags | DB_DBT_PARTIAL; + d->dlen = (unsigned int) dlen; + d->doff = (unsigned int) doff; + return 1; +} + + +/* Callback used to save away more information about errors from the DB + * library. */ +static char _db_errmsg[1024]; +static void _db_errorCallback(const char* prefix, char* msg) +{ + strcpy(_db_errmsg, msg); +} + + +/* make a nice exception object to raise for errors. */ +static int makeDBError(int err) +{ + char errTxt[2048]; /* really big, just in case... */ + PyObject *errObj = NULL; + PyObject *errTuple = NULL; + int exceptionRaised = 0; + + switch (err) { + case 0: /* successful, no error */ break; + +#if (DBVER < 41) + case DB_INCOMPLETE: +#if INCOMPLETE_IS_WARNING + strcpy(errTxt, db_strerror(err)); + if (_db_errmsg[0]) { + strcat(errTxt, " -- "); + strcat(errTxt, _db_errmsg); + _db_errmsg[0] = 0; + } +/* if Python 2.1 or better use warning framework */ +#if PYTHON_API_VERSION >= 1010 + exceptionRaised = PyErr_Warn(PyExc_RuntimeWarning, errTxt); +#else + fprintf(stderr, errTxt); + fprintf(stderr, "\n"); +#endif + +#else /* do an exception instead */ + errObj = DBIncompleteError; +#endif + break; +#endif /* DBVER < 41 */ + + case DB_KEYEMPTY: errObj = DBKeyEmptyError; break; + case DB_KEYEXIST: errObj = DBKeyExistError; break; + case DB_LOCK_DEADLOCK: errObj = DBLockDeadlockError; break; + case DB_LOCK_NOTGRANTED: errObj = DBLockNotGrantedError; break; + case DB_NOTFOUND: errObj = DBNotFoundError; break; + case DB_OLD_VERSION: errObj = DBOldVersionError; break; + case DB_RUNRECOVERY: errObj = DBRunRecoveryError; break; + case DB_VERIFY_BAD: errObj = DBVerifyBadError; break; + case DB_NOSERVER: errObj = DBNoServerError; break; + case DB_NOSERVER_HOME: errObj = DBNoServerHomeError; break; + case DB_NOSERVER_ID: errObj = DBNoServerIDError; break; +#if (DBVER >= 33) + case DB_PAGE_NOTFOUND: errObj = DBPageNotFoundError; break; + case DB_SECONDARY_BAD: errObj = DBSecondaryBadError; break; +#endif + + case EINVAL: errObj = DBInvalidArgError; break; + case EACCES: errObj = DBAccessError; break; + case ENOSPC: errObj = DBNoSpaceError; break; + case ENOMEM: errObj = DBNoMemoryError; break; + case EAGAIN: errObj = DBAgainError; break; + case EBUSY : errObj = DBBusyError; break; + case EEXIST: errObj = DBFileExistsError; break; + case ENOENT: errObj = DBNoSuchFileError; break; + case EPERM : errObj = DBPermissionsError; break; + + default: errObj = DBError; break; + } + + if (errObj != NULL) { + /* FIXME this needs proper bounds checking on errTxt */ + strcpy(errTxt, db_strerror(err)); + if (_db_errmsg[0]) { + strcat(errTxt, " -- "); + strcat(errTxt, _db_errmsg); + _db_errmsg[0] = 0; + } + + errTuple = Py_BuildValue("(is)", err, errTxt); + PyErr_SetObject(errObj, errTuple); + Py_DECREF(errTuple); + } + + return ((errObj != NULL) || exceptionRaised); +} + + + +/* set a type exception */ +static void makeTypeError(char* expected, PyObject* found) +{ + PyErr_Format(PyExc_TypeError, "Expected %s argument, %s found.", + expected, found->ob_type->tp_name); +} + + +/* verify that an obj is either None or a DBTxn, and set the txn pointer */ +static int checkTxnObj(PyObject* txnobj, DB_TXN** txn) +{ + if (txnobj == Py_None || txnobj == NULL) { + *txn = NULL; + return 1; + } + if (DBTxnObject_Check(txnobj)) { + *txn = ((DBTxnObject*)txnobj)->txn; + return 1; + } + else + makeTypeError("DBTxn", txnobj); + return 0; +} + + +/* Delete a key from a database + Returns 0 on success, -1 on an error. */ +static int _DB_delete(DBObject* self, DB_TXN *txn, DBT *key, int flags) +{ + int err; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->del(self->db, txn, key, 0); + MYDB_END_ALLOW_THREADS; + if (makeDBError(err)) { + return -1; + } + self->haveStat = 0; + return 0; +} + + +/* Store a key into a database + Returns 0 on success, -1 on an error. */ +static int _DB_put(DBObject* self, DB_TXN *txn, DBT *key, DBT *data, int flags) +{ + int err; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->put(self->db, txn, key, data, flags); + MYDB_END_ALLOW_THREADS; + if (makeDBError(err)) { + return -1; + } + self->haveStat = 0; + return 0; +} + +/* Get a key/data pair from a cursor */ +static PyObject* _DBCursor_get(DBCursorObject* self, int extra_flags, + PyObject *args, PyObject *kwargs, char *format) +{ + int err; + PyObject* retval = NULL; + DBT key, data; + int dlen = -1; + int doff = -1; + int flags = 0; + char* kwnames[] = { "flags", "dlen", "doff", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, format, kwnames, + &flags, &dlen, &doff)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + flags |= extra_flags; + CLEAR_DBT(key); + CLEAR_DBT(data); + if (CHECK_DBFLAG(self->mydb, DB_THREAD)) { + /* Tell BerkeleyDB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + key.flags = DB_DBT_MALLOC; + } + if (!add_partial_dbt(&data, dlen, doff)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_get(self->dbc, &key, &data, flags); + MYDB_END_ALLOW_THREADS; + + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.getReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { /* otherwise, success! */ + + /* if Recno or Queue, return the key as an Int */ + switch (_DB_get_type(self->mydb)) { + case -1: + retval = NULL; + break; + + case DB_RECNO: + case DB_QUEUE: + retval = Py_BuildValue("is#", *((db_recno_t*)key.data), + data.data, data.size); + break; + case DB_HASH: + case DB_BTREE: + default: + retval = Py_BuildValue("s#s#", key.data, key.size, + data.data, data.size); + break; + } + } + if (!err) { + FREE_DBT(key); + FREE_DBT(data); + } + return retval; +} + + +/* add an integer to a dictionary using the given name as a key */ +static void _addIntToDict(PyObject* dict, char *name, int value) +{ + PyObject* v = PyInt_FromLong((long) value); + if (!v || PyDict_SetItemString(dict, name, v)) + PyErr_Clear(); + + Py_XDECREF(v); +} + + + + +/* --------------------------------------------------------------------- */ +/* Allocators and deallocators */ + +static DBObject* +newDBObject(DBEnvObject* arg, int flags) +{ + DBObject* self; + DB_ENV* db_env = NULL; + int err; + +#if PYTHON_API_VERSION <= 1007 + /* 1.5 compatibility */ + self = PyObject_NEW(DBObject, &DB_Type); +#else + self = PyObject_New(DBObject, &DB_Type); +#endif + + if (self == NULL) + return NULL; + + self->haveStat = 0; + self->flags = 0; + self->setflags = 0; + self->myenvobj = NULL; +#if (DBVER >= 33) + self->associateCallback = NULL; + self->primaryDBType = 0; +#endif + + /* keep a reference to our python DBEnv object */ + if (arg) { + Py_INCREF(arg); + self->myenvobj = arg; + db_env = arg->db_env; + } + + if (self->myenvobj) + self->moduleFlags = self->myenvobj->moduleFlags; + else + self->moduleFlags.getReturnsNone = DEFAULT_GET_RETURNS_NONE; + self->moduleFlags.cursorSetReturnsNone = DEFAULT_CURSOR_SET_RETURNS_NONE; + + MYDB_BEGIN_ALLOW_THREADS; + err = db_create(&self->db, db_env, flags); + self->db->set_errcall(self->db, _db_errorCallback); +#if (DBVER >= 33) + self->db->app_private = (void*)self; +#endif + MYDB_END_ALLOW_THREADS; + if (makeDBError(err)) { + if (self->myenvobj) { + Py_DECREF(self->myenvobj); + self->myenvobj = NULL; + } + self = NULL; + } + return self; +} + + +static void +DB_dealloc(DBObject* self) +{ + if (self->db != NULL) { + /* avoid closing a DB when its DBEnv has been closed out from under + * it */ + if (!self->myenvobj || + (self->myenvobj && self->myenvobj->db_env)) + { + MYDB_BEGIN_ALLOW_THREADS; + self->db->close(self->db, 0); + MYDB_END_ALLOW_THREADS; + /* if Python 2.1 or better use warning framework */ +#if PYTHON_API_VERSION >= 1010 + } else { + PyErr_Warn(PyExc_RuntimeWarning, + "DB could not be closed in destructor: DBEnv already closed"); +#endif + } + self->db = NULL; + } + if (self->myenvobj) { + Py_DECREF(self->myenvobj); + self->myenvobj = NULL; + } +#if (DBVER >= 33) + if (self->associateCallback != NULL) { + Py_DECREF(self->associateCallback); + self->associateCallback = NULL; + } +#endif +#if PYTHON_API_VERSION <= 1007 + PyMem_DEL(self); +#else + PyObject_Del(self); +#endif +} + + +static DBCursorObject* +newDBCursorObject(DBC* dbc, DBObject* db) +{ + DBCursorObject* self; +#if PYTHON_API_VERSION <= 1007 + self = PyObject_NEW(DBCursorObject, &DBCursor_Type); +#else + self = PyObject_New(DBCursorObject, &DBCursor_Type); +#endif + if (self == NULL) + return NULL; + + self->dbc = dbc; + self->mydb = db; + Py_INCREF(self->mydb); + return self; +} + + +static void +DBCursor_dealloc(DBCursorObject* self) +{ + int err; + if (self->dbc != NULL) { + MYDB_BEGIN_ALLOW_THREADS; + /* If the underlying database has been closed, we don't + need to do anything. If the environment has been closed + we need to leak, as BerkeleyDB will crash trying to access + the environment. There was an exception when the + user closed the environment even though there still was + a database open. */ + if (self->mydb->db && self->mydb->myenvobj && + !self->mydb->myenvobj->closed) + err = self->dbc->c_close(self->dbc); + self->dbc = NULL; + MYDB_END_ALLOW_THREADS; + } + Py_XDECREF( self->mydb ); +#if PYTHON_API_VERSION <= 1007 + PyMem_DEL(self); +#else + PyObject_Del(self); +#endif +} + + +static DBEnvObject* +newDBEnvObject(int flags) +{ + int err; + DBEnvObject* self; +#if PYTHON_API_VERSION <= 1007 + self = PyObject_NEW(DBEnvObject, &DBEnv_Type); +#else + self = PyObject_New(DBEnvObject, &DBEnv_Type); +#endif + + if (self == NULL) + return NULL; + + self->closed = 1; + self->flags = flags; + self->moduleFlags.getReturnsNone = DEFAULT_GET_RETURNS_NONE; + self->moduleFlags.cursorSetReturnsNone = DEFAULT_CURSOR_SET_RETURNS_NONE; + + MYDB_BEGIN_ALLOW_THREADS; + err = db_env_create(&self->db_env, flags); + MYDB_END_ALLOW_THREADS; + if (makeDBError(err)) { + self = NULL; + } + else { + self->db_env->set_errcall(self->db_env, _db_errorCallback); + } + return self; +} + + +static void +DBEnv_dealloc(DBEnvObject* self) +{ + if (!self->closed) { + MYDB_BEGIN_ALLOW_THREADS; + self->db_env->close(self->db_env, 0); + MYDB_END_ALLOW_THREADS; + } +#if PYTHON_API_VERSION <= 1007 + PyMem_DEL(self); +#else + PyObject_Del(self); +#endif +} + + +static DBTxnObject* +newDBTxnObject(DBEnvObject* myenv, DB_TXN *parent, int flags) +{ + int err; + DBTxnObject* self; + +#if PYTHON_API_VERSION <= 1007 + self = PyObject_NEW(DBTxnObject, &DBTxn_Type); +#else + self = PyObject_New(DBTxnObject, &DBTxn_Type); +#endif + if (self == NULL) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = myenv->db_env->txn_begin(myenv->db_env, parent, &(self->txn), flags); +#else + err = txn_begin(myenv->db_env, parent, &(self->txn), flags); +#endif + MYDB_END_ALLOW_THREADS; + if (makeDBError(err)) { + self = NULL; + } + return self; +} + + +static void +DBTxn_dealloc(DBTxnObject* self) +{ + /* XXX nothing to do for transaction objects?!? */ + + /* TODO: if it hasn't been commited, should we abort it? */ + +#if PYTHON_API_VERSION <= 1007 + PyMem_DEL(self); +#else + PyObject_Del(self); +#endif +} + + +static DBLockObject* +newDBLockObject(DBEnvObject* myenv, u_int32_t locker, DBT* obj, + db_lockmode_t lock_mode, int flags) +{ + int err; + DBLockObject* self; + +#if PYTHON_API_VERSION <= 1007 + self = PyObject_NEW(DBLockObject, &DBLock_Type); +#else + self = PyObject_New(DBLockObject, &DBLock_Type); +#endif + if (self == NULL) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = myenv->db_env->lock_get(myenv->db_env, locker, flags, obj, lock_mode, + &self->lock); +#else + err = lock_get(myenv->db_env, locker, flags, obj, lock_mode, &self->lock); +#endif + MYDB_END_ALLOW_THREADS; + if (makeDBError(err)) { + self = NULL; + } + + return self; +} + + +static void +DBLock_dealloc(DBLockObject* self) +{ + /* TODO: if it hasn't been released, should we do it? */ + +#if PYTHON_API_VERSION <= 1007 + PyMem_DEL(self); +#else + PyObject_Del(self); +#endif +} + + +/* --------------------------------------------------------------------- */ +/* DB methods */ + +static PyObject* +DB_append(DBObject* self, PyObject* args) +{ + PyObject* txnobj = NULL; + PyObject* dataobj; + db_recno_t recno; + DBT key, data; + DB_TXN *txn = NULL; + + if (!PyArg_ParseTuple(args, "O|O:append", &dataobj, &txnobj)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + + /* make a dummy key out of a recno */ + recno = 0; + CLEAR_DBT(key); + key.data = &recno; + key.size = sizeof(recno); + key.ulen = key.size; + key.flags = DB_DBT_USERMEM; + + if (!make_dbt(dataobj, &data)) return NULL; + if (!checkTxnObj(txnobj, &txn)) return NULL; + + if (-1 == _DB_put(self, txn, &key, &data, DB_APPEND)) + return NULL; + + return PyInt_FromLong(recno); +} + + +#if (DBVER >= 33) + +static int +_db_associateCallback(DB* db, const DBT* priKey, const DBT* priData, + DBT* secKey) +{ + int retval = DB_DONOTINDEX; + DBObject* secondaryDB = (DBObject*)db->app_private; + PyObject* callback = secondaryDB->associateCallback; + int type = secondaryDB->primaryDBType; + PyObject* key; + PyObject* data; + PyObject* args; + PyObject* result; + + + if (callback != NULL) { + MYDB_BEGIN_BLOCK_THREADS; + + if (type == DB_RECNO || type == DB_QUEUE) { + key = PyInt_FromLong( *((db_recno_t*)priKey->data)); + } + else { + key = PyString_FromStringAndSize(priKey->data, priKey->size); + } + data = PyString_FromStringAndSize(priData->data, priData->size); + args = PyTuple_New(2); + PyTuple_SET_ITEM(args, 0, key); /* steals reference */ + PyTuple_SET_ITEM(args, 1, data); /* steals reference */ + + result = PyEval_CallObject(callback, args); + + if (result == NULL) { + PyErr_Print(); + } + else if (result == Py_None) { + retval = DB_DONOTINDEX; + } + else if (PyInt_Check(result)) { + retval = PyInt_AsLong(result); + } + else if (PyString_Check(result)) { + char* data; + int size; + + CLEAR_DBT(*secKey); +#if PYTHON_API_VERSION <= 1007 + /* 1.5 compatibility */ + size = PyString_Size(result); + data = PyString_AsString(result); +#else + PyString_AsStringAndSize(result, &data, &size); +#endif + secKey->flags = DB_DBT_APPMALLOC; /* DB will free */ + secKey->data = malloc(size); /* TODO, check this */ + if (secKey->data) { + memcpy(secKey->data, data, size); + secKey->size = size; + retval = 0; + } + else { + PyErr_SetString(PyExc_MemoryError, + "malloc failed in _db_associateCallback"); + PyErr_Print(); + } + } + else { + PyErr_SetString( + PyExc_TypeError, + "DB associate callback should return DB_DONOTINDEX or string."); + PyErr_Print(); + } + + Py_DECREF(args); + if (result) { + Py_DECREF(result); + } + + MYDB_END_BLOCK_THREADS; + } + return retval; +} + + +static PyObject* +DB_associate(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + DBObject* secondaryDB; + PyObject* callback; +#if (DBVER >= 41) + PyObject *txnobj = NULL; + DB_TXN *txn = NULL; + char* kwnames[] = {"secondaryDB", "callback", "flags", "txn", NULL}; +#else + char* kwnames[] = {"secondaryDB", "callback", "flags", NULL}; +#endif + +#if (DBVER >= 41) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|iO:associate", kwnames, + &secondaryDB, &callback, &flags, + &txnobj)) { +#else + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|i:associate", kwnames, + &secondaryDB, &callback, &flags)) { +#endif + return NULL; + } + +#if (DBVER >= 41) + if (!checkTxnObj(txnobj, &txn)) return NULL; +#endif + + CHECK_DB_NOT_CLOSED(self); + if (!DBObject_Check(secondaryDB)) { + makeTypeError("DB", (PyObject*)secondaryDB); + return NULL; + } + if (callback == Py_None) { + callback = NULL; + } + else if (!PyCallable_Check(callback)) { + makeTypeError("Callable", callback); + return NULL; + } + + /* Save a reference to the callback in the secondary DB. */ + if (self->associateCallback != NULL) { + Py_DECREF(self->associateCallback); + } + Py_INCREF(callback); + secondaryDB->associateCallback = callback; + secondaryDB->primaryDBType = _DB_get_type(self); + + /* PyEval_InitThreads is called here due to a quirk in python 1.5 + * - 2.2.1 (at least) according to Russell Williamson : + * The global interepreter lock is not initialized until the first + * thread is created using thread.start_new_thread() or fork() is + * called. that would cause the ALLOW_THREADS here to segfault due + * to a null pointer reference if no threads or child processes + * have been created. This works around that and is a no-op if + * threads have already been initialized. + * (see pybsddb-users mailing list post on 2002-08-07) + */ +#ifdef WITH_THREAD + PyEval_InitThreads(); +#endif + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 41) + err = self->db->associate(self->db, + txn, + secondaryDB->db, + _db_associateCallback, + flags); +#else + err = self->db->associate(self->db, + secondaryDB->db, + _db_associateCallback, + flags); +#endif + MYDB_END_ALLOW_THREADS; + + if (err) { + Py_DECREF(self->associateCallback); + self->associateCallback = NULL; + secondaryDB->primaryDBType = 0; + } + + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +#endif + + +static PyObject* +DB_close(DBObject* self, PyObject* args) +{ + int err, flags=0; + if (!PyArg_ParseTuple(args,"|i:close", &flags)) + return NULL; + if (self->db != NULL) { + if (self->myenvobj) + CHECK_ENV_NOT_CLOSED(self->myenvobj); + err = self->db->close(self->db, flags); + self->db = NULL; + RETURN_IF_ERR(); + } + RETURN_NONE(); +} + + +#if (DBVER >= 32) +static PyObject* +_DB_consume(DBObject* self, PyObject* args, PyObject* kwargs, int consume_flag) +{ + int err, flags=0, type; + PyObject* txnobj = NULL; + PyObject* retval = NULL; + DBT key, data; + DB_TXN *txn = NULL; + char* kwnames[] = { "txn", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:consume", kwnames, + &txnobj, &flags)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + type = _DB_get_type(self); + if (type == -1) + return NULL; + if (type != DB_QUEUE) { + PyErr_SetString(PyExc_TypeError, + "Consume methods only allowed for Queue DB's"); + return NULL; + } + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + CLEAR_DBT(key); + CLEAR_DBT(data); + if (CHECK_DBFLAG(self, DB_THREAD)) { + /* Tell BerkeleyDB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + key.flags = DB_DBT_MALLOC; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get(self->db, txn, &key, &data, flags|consume_flag); + MYDB_END_ALLOW_THREADS; + + if ((err == DB_NOTFOUND) && self->moduleFlags.getReturnsNone) { + err = 0; + Py_INCREF(Py_None); + retval = Py_None; + } + else if (!err) { + retval = Py_BuildValue("s#s#", key.data, key.size, data.data, + data.size); + FREE_DBT(key); + FREE_DBT(data); + } + + RETURN_IF_ERR(); + return retval; +} + +static PyObject* +DB_consume(DBObject* self, PyObject* args, PyObject* kwargs, int consume_flag) +{ + return _DB_consume(self, args, kwargs, DB_CONSUME); +} + +static PyObject* +DB_consume_wait(DBObject* self, PyObject* args, PyObject* kwargs, + int consume_flag) +{ + return _DB_consume(self, args, kwargs, DB_CONSUME_WAIT); +} +#endif + + + +static PyObject* +DB_cursor(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + DBC* dbc; + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + char* kwnames[] = { "txn", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:cursor", kwnames, + &txnobj, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->cursor(self->db, txn, &dbc, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return (PyObject*) newDBCursorObject(dbc, self); +} + + +static PyObject* +DB_delete(DBObject* self, PyObject* args, PyObject* kwargs) +{ + PyObject* txnobj = NULL; + int flags = 0; + PyObject* keyobj; + DBT key; + DB_TXN *txn = NULL; + char* kwnames[] = { "key", "txn", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oi:delete", kwnames, + &keyobj, &txnobj, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, NULL)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + if (-1 == _DB_delete(self, txn, &key, 0)) + return NULL; + + FREE_DBT(key); + RETURN_NONE(); +} + + +static PyObject* +DB_fd(DBObject* self, PyObject* args) +{ + int err, the_fd; + + if (!PyArg_ParseTuple(args,":fd")) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->fd(self->db, &the_fd); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return PyInt_FromLong(the_fd); +} + + +static PyObject* +DB_get(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + PyObject* txnobj = NULL; + PyObject* keyobj; + PyObject* dfltobj = NULL; + PyObject* retval = NULL; + int dlen = -1; + int doff = -1; + DBT key, data; + DB_TXN *txn = NULL; + char* kwnames[] = {"key", "default", "txn", "flags", "dlen", "doff", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOiii:get", kwnames, + &keyobj, &dfltobj, &txnobj, &flags, &dlen, + &doff)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, &flags)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + CLEAR_DBT(data); + if (CHECK_DBFLAG(self, DB_THREAD)) { + /* Tell BerkeleyDB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + } + if (!add_partial_dbt(&data, dlen, doff)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get(self->db, txn, &key, &data, flags); + MYDB_END_ALLOW_THREADS; + + if ((err == DB_NOTFOUND) && (dfltobj != NULL)) { + err = 0; + Py_INCREF(dfltobj); + retval = dfltobj; + } + else if ((err == DB_NOTFOUND) && self->moduleFlags.getReturnsNone) { + err = 0; + Py_INCREF(Py_None); + retval = Py_None; + } + else if (!err) { + if (flags & DB_SET_RECNO) /* return both key and data */ + retval = Py_BuildValue("s#s#", key.data, key.size, data.data, + data.size); + else /* return just the data */ + retval = PyString_FromStringAndSize((char*)data.data, data.size); + FREE_DBT(key); + FREE_DBT(data); + } + + RETURN_IF_ERR(); + return retval; +} + + +/* Return size of entry */ +static PyObject* +DB_get_size(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + PyObject* txnobj = NULL; + PyObject* keyobj; + PyObject* retval = NULL; + DBT key, data; + DB_TXN *txn = NULL; + char* kwnames[] = { "key", "txn", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:get_size", kwnames, + &keyobj, &txnobj)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, &flags)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + CLEAR_DBT(data); + + /* We don't allocate any memory, forcing a ENOMEM error and thus + getting the record size. */ + data.flags = DB_DBT_USERMEM; + data.ulen = 0; + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get(self->db, txn, &key, &data, flags); + MYDB_END_ALLOW_THREADS; + if (err == ENOMEM) { + retval = PyInt_FromLong((long)data.size); + err = 0; + } + + FREE_DBT(key); + FREE_DBT(data); + RETURN_IF_ERR(); + return retval; +} + + +static PyObject* +DB_get_both(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + PyObject* txnobj = NULL; + PyObject* keyobj; + PyObject* dataobj; + PyObject* retval = NULL; + DBT key, data; + DB_TXN *txn = NULL; + char* kwnames[] = { "key", "data", "txn", "flags", NULL }; + + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|Oi:get_both", kwnames, + &keyobj, &dataobj, &txnobj, &flags)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, NULL)) + return NULL; + if (!make_dbt(dataobj, &data)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + flags |= DB_GET_BOTH; + + if (CHECK_DBFLAG(self, DB_THREAD)) { + /* Tell BerkeleyDB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + /* TODO: Is this flag needed? We're passing a data object that should + match what's in the DB, so there should be no need to malloc. + We run the risk of freeing something twice! Check this. */ + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get(self->db, txn, &key, &data, flags); + MYDB_END_ALLOW_THREADS; + + if ((err == DB_NOTFOUND) && self->moduleFlags.getReturnsNone) { + err = 0; + Py_INCREF(Py_None); + retval = Py_None; + } + else if (!err) { + retval = PyString_FromStringAndSize((char*)data.data, data.size); + FREE_DBT(data); /* Only if retrieval was successful */ + } + + FREE_DBT(key); + RETURN_IF_ERR(); + return retval; +} + + +static PyObject* +DB_get_byteswapped(DBObject* self, PyObject* args) +{ +#if (DBVER >= 33) + int err = 0; +#endif + int retval = -1; + + if (!PyArg_ParseTuple(args,":get_byteswapped")) + return NULL; + CHECK_DB_NOT_CLOSED(self); + +#if (DBVER >= 33) + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get_byteswapped(self->db, &retval); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); +#else + MYDB_BEGIN_ALLOW_THREADS; + retval = self->db->get_byteswapped(self->db); + MYDB_END_ALLOW_THREADS; +#endif + return PyInt_FromLong(retval); +} + + +static PyObject* +DB_get_type(DBObject* self, PyObject* args) +{ + int type; + + if (!PyArg_ParseTuple(args,":get_type")) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + type = _DB_get_type(self); + MYDB_END_ALLOW_THREADS; + if (type == -1) + return NULL; + return PyInt_FromLong(type); +} + + +static PyObject* +DB_join(DBObject* self, PyObject* args) +{ + int err, flags=0; + int length, x; + PyObject* cursorsObj; + DBC** cursors; + DBC* dbc; + + + if (!PyArg_ParseTuple(args,"O|i:join", &cursorsObj, &flags)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + + if (!PySequence_Check(cursorsObj)) { + PyErr_SetString(PyExc_TypeError, + "Sequence of DBCursor objects expected"); + return NULL; + } + + length = PyObject_Length(cursorsObj); + cursors = malloc((length+1) * sizeof(DBC*)); + cursors[length] = NULL; + for (x=0; xdbc; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->join(self->db, cursors, &dbc, flags); + MYDB_END_ALLOW_THREADS; + free(cursors); + RETURN_IF_ERR(); + + // FIXME: this is a buggy interface. The returned cursor + // contains internal references to the passed in cursors + // but does not hold python references to them or prevent + // them from being closed prematurely. This can cause + // python to crash when things are done in the wrong order. + return (PyObject*) newDBCursorObject(dbc, self); +} + + +static PyObject* +DB_key_range(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + PyObject* txnobj = NULL; + PyObject* keyobj; + DBT key; + DB_TXN *txn = NULL; + DB_KEY_RANGE range; + char* kwnames[] = { "key", "txn", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oi:key_range", kwnames, + &keyobj, &txnobj, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + if (!make_dbt(keyobj, &key)) + /* BTree only, don't need to allow for an int key */ + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->key_range(self->db, txn, &key, &range, flags); + MYDB_END_ALLOW_THREADS; + + RETURN_IF_ERR(); + return Py_BuildValue("ddd", range.less, range.equal, range.greater); +} + + +static PyObject* +DB_open(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, type = DB_UNKNOWN, flags=0, mode=0660; + char* filename = NULL; + char* dbname = NULL; +#if (DBVER >= 41) + PyObject *txnobj = NULL; + DB_TXN *txn = NULL; + /* with dbname */ + char* kwnames[] = { + "filename", "dbname", "dbtype", "flags", "mode", "txn", NULL}; + /* without dbname */ + char* kwnames_basic[] = { + "filename", "dbtype", "flags", "mode", "txn", NULL}; +#else + /* with dbname */ + char* kwnames[] = { + "filename", "dbname", "dbtype", "flags", "mode", NULL}; + /* without dbname */ + char* kwnames_basic[] = { + "filename", "dbtype", "flags", "mode", NULL}; +#endif + +#if (DBVER >= 41) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|ziiiO:open", kwnames, + &filename, &dbname, &type, &flags, &mode, + &txnobj)) +#else + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|ziii:open", kwnames, + &filename, &dbname, &type, &flags, + &mode)) +#endif + { + PyErr_Clear(); + type = DB_UNKNOWN; flags = 0; mode = 0660; + filename = NULL; dbname = NULL; +#if (DBVER >= 41) + if (!PyArg_ParseTupleAndKeywords(args, kwargs,"z|iiiO:open", + kwnames_basic, + &filename, &type, &flags, &mode, + &txnobj)) + return NULL; +#else + if (!PyArg_ParseTupleAndKeywords(args, kwargs,"z|iii:open", + kwnames_basic, + &filename, &type, &flags, &mode)) + return NULL; +#endif + } + +#if (DBVER >= 41) + if (!checkTxnObj(txnobj, &txn)) return NULL; +#endif + + if (NULL == self->db) { + PyErr_SetObject(DBError, Py_BuildValue("(is)", 0, + "Cannot call open() twice for DB object")); + return NULL; + } + +#if 0 && (DBVER >= 41) + if ((!txn) && (txnobj != Py_None) && self->myenvobj + && (self->myenvobj->flags & DB_INIT_TXN)) + { + /* If no 'txn' parameter was supplied (no DbTxn object and None was not + * explicitly passed) but we are in a transaction ready environment: + * add DB_AUTO_COMMIT to allow for older pybsddb apps using transactions + * to work on BerkeleyDB 4.1 without needing to modify their + * DBEnv or DB open calls. + * TODO make this behaviour of the library configurable. + */ + flags |= DB_AUTO_COMMIT; + } +#endif + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 41) + err = self->db->open(self->db, txn, filename, dbname, type, flags, mode); +#else + err = self->db->open(self->db, filename, dbname, type, flags, mode); +#endif + MYDB_END_ALLOW_THREADS; + if (makeDBError(err)) { + self->db->close(self->db, 0); + self->db = NULL; + return NULL; + } + + self->flags = flags; + RETURN_NONE(); +} + + +static PyObject* +DB_put(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int flags=0; + PyObject* txnobj = NULL; + int dlen = -1; + int doff = -1; + PyObject* keyobj, *dataobj, *retval; + DBT key, data; + DB_TXN *txn = NULL; + char* kwnames[] = { "key", "data", "txn", "flags", "dlen", "doff", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|Oiii:put", kwnames, + &keyobj, &dataobj, &txnobj, &flags, &dlen, &doff)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, NULL)) return NULL; + if (!make_dbt(dataobj, &data)) return NULL; + if (!add_partial_dbt(&data, dlen, doff)) return NULL; + if (!checkTxnObj(txnobj, &txn)) return NULL; + + if (-1 == _DB_put(self, txn, &key, &data, flags)) { + FREE_DBT(key); + return NULL; + } + + if (flags & DB_APPEND) + retval = PyInt_FromLong(*((db_recno_t*)key.data)); + else { + retval = Py_None; + Py_INCREF(retval); + } + FREE_DBT(key); + return retval; +} + + + +static PyObject* +DB_remove(DBObject* self, PyObject* args, PyObject* kwargs) +{ + char* filename; + char* database = NULL; + int err, flags=0; + char* kwnames[] = { "filename", "dbname", "flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zi:remove", kwnames, + &filename, &database, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + err = self->db->remove(self->db, filename, database, flags); + self->db = NULL; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + + +static PyObject* +DB_rename(DBObject* self, PyObject* args) +{ + char* filename; + char* database; + char* newname; + int err, flags=0; + + if (!PyArg_ParseTuple(args, "sss|i:rename", &filename, &database, &newname, + &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->rename(self->db, filename, database, newname, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_bt_minkey(DBObject* self, PyObject* args) +{ + int err, minkey; + + if (!PyArg_ParseTuple(args,"i:set_bt_minkey", &minkey )) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_bt_minkey(self->db, minkey); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_cachesize(DBObject* self, PyObject* args) +{ + int err; + int gbytes = 0, bytes = 0, ncache = 0; + + if (!PyArg_ParseTuple(args,"ii|i:set_cachesize", + &gbytes,&bytes,&ncache)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_cachesize(self->db, gbytes, bytes, ncache); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_flags(DBObject* self, PyObject* args) +{ + int err, flags; + + if (!PyArg_ParseTuple(args,"i:set_flags", &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_flags(self->db, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + self->setflags |= flags; + RETURN_NONE(); +} + + +static PyObject* +DB_set_h_ffactor(DBObject* self, PyObject* args) +{ + int err, ffactor; + + if (!PyArg_ParseTuple(args,"i:set_h_ffactor", &ffactor)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_h_ffactor(self->db, ffactor); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_h_nelem(DBObject* self, PyObject* args) +{ + int err, nelem; + + if (!PyArg_ParseTuple(args,"i:set_h_nelem", &nelem)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_h_nelem(self->db, nelem); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_lorder(DBObject* self, PyObject* args) +{ + int err, lorder; + + if (!PyArg_ParseTuple(args,"i:set_lorder", &lorder)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_lorder(self->db, lorder); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_pagesize(DBObject* self, PyObject* args) +{ + int err, pagesize; + + if (!PyArg_ParseTuple(args,"i:set_pagesize", &pagesize)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_pagesize(self->db, pagesize); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_re_delim(DBObject* self, PyObject* args) +{ + int err; + char delim; + + if (!PyArg_ParseTuple(args,"b:set_re_delim", &delim)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args,"c:set_re_delim", &delim)) + return NULL; + } + + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_re_delim(self->db, delim); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DB_set_re_len(DBObject* self, PyObject* args) +{ + int err, len; + + if (!PyArg_ParseTuple(args,"i:set_re_len", &len)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_re_len(self->db, len); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_re_pad(DBObject* self, PyObject* args) +{ + int err; + char pad; + + if (!PyArg_ParseTuple(args,"b:set_re_pad", &pad)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args,"c:set_re_pad", &pad)) + return NULL; + } + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_re_pad(self->db, pad); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_re_source(DBObject* self, PyObject* args) +{ + int err; + char *re_source; + + if (!PyArg_ParseTuple(args,"s:set_re_source", &re_source)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_re_source(self->db, re_source); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +#if (DBVER >= 32) +static PyObject* +DB_set_q_extentsize(DBObject* self, PyObject* args) +{ + int err; + int extentsize; + + if (!PyArg_ParseTuple(args,"i:set_q_extentsize", &extentsize)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_q_extentsize(self->db, extentsize); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif + +static PyObject* +DB_stat(DBObject* self, PyObject* args) +{ + int err, flags = 0, type; + void* sp; + PyObject* d; + + + if (!PyArg_ParseTuple(args, "|i:stat", &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 33) + err = self->db->stat(self->db, &sp, flags); +#else + err = self->db->stat(self->db, &sp, NULL, flags); +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + self->haveStat = 1; + + /* Turn the stat structure into a dictionary */ + type = _DB_get_type(self); + if ((type == -1) || ((d = PyDict_New()) == NULL)) { + free(sp); + return NULL; + } + +#define MAKE_HASH_ENTRY(name) _addIntToDict(d, #name, ((DB_HASH_STAT*)sp)->hash_##name) +#define MAKE_BT_ENTRY(name) _addIntToDict(d, #name, ((DB_BTREE_STAT*)sp)->bt_##name) +#define MAKE_QUEUE_ENTRY(name) _addIntToDict(d, #name, ((DB_QUEUE_STAT*)sp)->qs_##name) + + switch (type) { + case DB_HASH: + MAKE_HASH_ENTRY(magic); + MAKE_HASH_ENTRY(version); + MAKE_HASH_ENTRY(nkeys); + MAKE_HASH_ENTRY(ndata); + MAKE_HASH_ENTRY(pagesize); +#if (DBVER < 41) + MAKE_HASH_ENTRY(nelem); +#endif + MAKE_HASH_ENTRY(ffactor); + MAKE_HASH_ENTRY(buckets); + MAKE_HASH_ENTRY(free); + MAKE_HASH_ENTRY(bfree); + MAKE_HASH_ENTRY(bigpages); + MAKE_HASH_ENTRY(big_bfree); + MAKE_HASH_ENTRY(overflows); + MAKE_HASH_ENTRY(ovfl_free); + MAKE_HASH_ENTRY(dup); + MAKE_HASH_ENTRY(dup_free); + break; + + case DB_BTREE: + case DB_RECNO: + MAKE_BT_ENTRY(magic); + MAKE_BT_ENTRY(version); + MAKE_BT_ENTRY(nkeys); + MAKE_BT_ENTRY(ndata); + MAKE_BT_ENTRY(pagesize); + MAKE_BT_ENTRY(minkey); + MAKE_BT_ENTRY(re_len); + MAKE_BT_ENTRY(re_pad); + MAKE_BT_ENTRY(levels); + MAKE_BT_ENTRY(int_pg); + MAKE_BT_ENTRY(leaf_pg); + MAKE_BT_ENTRY(dup_pg); + MAKE_BT_ENTRY(over_pg); + MAKE_BT_ENTRY(free); + MAKE_BT_ENTRY(int_pgfree); + MAKE_BT_ENTRY(leaf_pgfree); + MAKE_BT_ENTRY(dup_pgfree); + MAKE_BT_ENTRY(over_pgfree); + break; + + case DB_QUEUE: + MAKE_QUEUE_ENTRY(magic); + MAKE_QUEUE_ENTRY(version); + MAKE_QUEUE_ENTRY(nkeys); + MAKE_QUEUE_ENTRY(ndata); + MAKE_QUEUE_ENTRY(pagesize); + MAKE_QUEUE_ENTRY(pages); + MAKE_QUEUE_ENTRY(re_len); + MAKE_QUEUE_ENTRY(re_pad); + MAKE_QUEUE_ENTRY(pgfree); +#if (DBVER == 31) + MAKE_QUEUE_ENTRY(start); +#endif + MAKE_QUEUE_ENTRY(first_recno); + MAKE_QUEUE_ENTRY(cur_recno); + break; + + default: + PyErr_SetString(PyExc_TypeError, "Unknown DB type, unable to stat"); + Py_DECREF(d); + d = NULL; + } + +#undef MAKE_HASH_ENTRY +#undef MAKE_BT_ENTRY +#undef MAKE_QUEUE_ENTRY + + free(sp); + return d; +} + +static PyObject* +DB_sync(DBObject* self, PyObject* args) +{ + int err; + int flags = 0; + + if (!PyArg_ParseTuple(args,"|i:sync", &flags )) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->sync(self->db, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +#if (DBVER >= 33) +static PyObject* +DB_truncate(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + u_int32_t count=0; + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + char* kwnames[] = { "txn", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:cursor", kwnames, + &txnobj, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->truncate(self->db, txn, &count, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return PyInt_FromLong(count); +} +#endif + + +static PyObject* +DB_upgrade(DBObject* self, PyObject* args) +{ + int err, flags=0; + char *filename; + + if (!PyArg_ParseTuple(args,"s|i:upgrade", &filename, &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->upgrade(self->db, filename, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_verify(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags=0; + char* fileName; + char* dbName=NULL; + char* outFileName=NULL; + FILE* outFile=NULL; + char* kwnames[] = { "filename", "dbname", "outfile", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zzi:verify", kwnames, + &fileName, &dbName, &outFileName, &flags)) + return NULL; + + CHECK_DB_NOT_CLOSED(self); + if (outFileName) + outFile = fopen(outFileName, "w"); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->verify(self->db, fileName, dbName, outFile, flags); + MYDB_END_ALLOW_THREADS; + if (outFileName) + fclose(outFile); + + /* DB.verify acts as a DB handle destructor (like close); this was + * documented in BerkeleyDB 4.2 but had the undocumented effect + * of not being safe in prior versions while still requiring an explicit + * DB.close call afterwards. Lets call close for the user to emulate + * the safe 4.2 behaviour. */ +#if (DBVER <= 41) + self->db->close(self->db, 0); +#endif + self->db = NULL; + + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DB_set_get_returns_none(DBObject* self, PyObject* args) +{ + int flags=0; + int oldValue=0; + + if (!PyArg_ParseTuple(args,"i:set_get_returns_none", &flags)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + + if (self->moduleFlags.getReturnsNone) + ++oldValue; + if (self->moduleFlags.cursorSetReturnsNone) + ++oldValue; + self->moduleFlags.getReturnsNone = (flags >= 1); + self->moduleFlags.cursorSetReturnsNone = (flags >= 2); + return PyInt_FromLong(oldValue); +} + +#if (DBVER >= 41) +static PyObject* +DB_set_encrypt(DBObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + u_int32_t flags=0; + char *passwd = NULL; + char* kwnames[] = { "passwd", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i:set_encrypt", kwnames, + &passwd, &flags)) { + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->set_encrypt(self->db, passwd, flags); + MYDB_END_ALLOW_THREADS; + + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif /* DBVER >= 41 */ + + +/*-------------------------------------------------------------- */ +/* Mapping and Dictionary-like access routines */ + +int DB_length(DBObject* self) +{ + int err; + long size = 0; + int flags = 0; + void* sp; + + if (self->db == NULL) { + PyErr_SetObject(DBError, + Py_BuildValue("(is)", 0, "DB object has been closed")); + return -1; + } + + if (self->haveStat) { /* Has the stat function been called recently? If + so, we can use the cached value. */ + flags = DB_CACHED_COUNTS; + } + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 33) + err = self->db->stat(self->db, &sp, flags); +#else + err = self->db->stat(self->db, &sp, NULL, flags); +#endif + MYDB_END_ALLOW_THREADS; + + if (err) + return -1; + + self->haveStat = 1; + + /* All the stat structures have matching fields upto the ndata field, + so we can use any of them for the type cast */ + size = ((DB_BTREE_STAT*)sp)->bt_ndata; + free(sp); + return size; +} + + +PyObject* DB_subscript(DBObject* self, PyObject* keyobj) +{ + int err; + PyObject* retval; + DBT key; + DBT data; + + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, NULL)) + return NULL; + + CLEAR_DBT(data); + if (CHECK_DBFLAG(self, DB_THREAD)) { + /* Tell BerkeleyDB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + } + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get(self->db, NULL, &key, &data, 0); + MYDB_END_ALLOW_THREADS; + if (err == DB_NOTFOUND || err == DB_KEYEMPTY) { + PyErr_SetObject(PyExc_KeyError, keyobj); + retval = NULL; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { + retval = PyString_FromStringAndSize((char*)data.data, data.size); + FREE_DBT(data); + } + + FREE_DBT(key); + return retval; +} + + +static int +DB_ass_sub(DBObject* self, PyObject* keyobj, PyObject* dataobj) +{ + DBT key, data; + int retval; + int flags = 0; + + if (self->db == NULL) { + PyErr_SetObject(DBError, + Py_BuildValue("(is)", 0, "DB object has been closed")); + return -1; + } + + if (!make_key_dbt(self, keyobj, &key, NULL)) + return -1; + + if (dataobj != NULL) { + if (!make_dbt(dataobj, &data)) + retval = -1; + else { + if (self->setflags & (DB_DUP|DB_DUPSORT)) + /* dictionaries shouldn't have duplicate keys */ + flags = DB_NOOVERWRITE; + retval = _DB_put(self, NULL, &key, &data, flags); + + if ((retval == -1) && (self->setflags & (DB_DUP|DB_DUPSORT))) { + /* try deleting any old record that matches and then PUT it + * again... */ + _DB_delete(self, NULL, &key, 0); + PyErr_Clear(); + retval = _DB_put(self, NULL, &key, &data, flags); + } + } + } + else { + /* dataobj == NULL, so delete the key */ + retval = _DB_delete(self, NULL, &key, 0); + } + FREE_DBT(key); + return retval; +} + + +static PyObject* +DB_has_key(DBObject* self, PyObject* args) +{ + int err; + PyObject* keyobj; + DBT key, data; + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + + if (!PyArg_ParseTuple(args,"O|O:has_key", &keyobj, &txnobj)) + return NULL; + CHECK_DB_NOT_CLOSED(self); + if (!make_key_dbt(self, keyobj, &key, NULL)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + + /* This causes ENOMEM to be returned when the db has the key because + it has a record but can't allocate a buffer for the data. This saves + having to deal with data we won't be using. + */ + CLEAR_DBT(data); + data.flags = DB_DBT_USERMEM; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->get(self->db, NULL, &key, &data, 0); + MYDB_END_ALLOW_THREADS; + FREE_DBT(key); + return PyInt_FromLong((err == ENOMEM) || (err == 0)); +} + + +#define _KEYS_LIST 1 +#define _VALUES_LIST 2 +#define _ITEMS_LIST 3 + +static PyObject* +_DB_make_list(DBObject* self, DB_TXN* txn, int type) +{ + int err, dbtype; + DBT key; + DBT data; + DBC *cursor; + PyObject* list; + PyObject* item = NULL; + + CHECK_DB_NOT_CLOSED(self); + CLEAR_DBT(key); + CLEAR_DBT(data); + + dbtype = _DB_get_type(self); + if (dbtype == -1) + return NULL; + + list = PyList_New(0); + if (list == NULL) { + PyErr_SetString(PyExc_MemoryError, "PyList_New failed"); + return NULL; + } + + /* get a cursor */ + MYDB_BEGIN_ALLOW_THREADS; + err = self->db->cursor(self->db, NULL, &cursor, 0); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + if (CHECK_DBFLAG(self, DB_THREAD)) { + key.flags = DB_DBT_REALLOC; + data.flags = DB_DBT_REALLOC; + } + + while (1) { /* use the cursor to traverse the DB, collecting items */ + MYDB_BEGIN_ALLOW_THREADS; + err = cursor->c_get(cursor, &key, &data, DB_NEXT); + MYDB_END_ALLOW_THREADS; + + if (err) { + /* for any error, break out of the loop */ + break; + } + + switch (type) { + case _KEYS_LIST: + switch(dbtype) { + case DB_BTREE: + case DB_HASH: + default: + item = PyString_FromStringAndSize((char*)key.data, key.size); + break; + case DB_RECNO: + case DB_QUEUE: + item = PyInt_FromLong(*((db_recno_t*)key.data)); + break; + } + break; + + case _VALUES_LIST: + item = PyString_FromStringAndSize((char*)data.data, data.size); + break; + + case _ITEMS_LIST: + switch(dbtype) { + case DB_BTREE: + case DB_HASH: + default: + item = Py_BuildValue("s#s#", key.data, key.size, data.data, + data.size); + break; + case DB_RECNO: + case DB_QUEUE: + item = Py_BuildValue("is#", *((db_recno_t*)key.data), + data.data, data.size); + break; + } + break; + } + if (item == NULL) { + Py_DECREF(list); + PyErr_SetString(PyExc_MemoryError, "List item creation failed"); + list = NULL; + goto done; + } + PyList_Append(list, item); + Py_DECREF(item); + } + + /* DB_NOTFOUND is okay, it just means we got to the end */ + if (err != DB_NOTFOUND && makeDBError(err)) { + Py_DECREF(list); + list = NULL; + } + + done: + FREE_DBT(key); + FREE_DBT(data); + MYDB_BEGIN_ALLOW_THREADS; + cursor->c_close(cursor); + MYDB_END_ALLOW_THREADS; + return list; +} + + +static PyObject* +DB_keys(DBObject* self, PyObject* args) +{ + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + + if (!PyArg_ParseTuple(args,"|O:keys", &txnobj)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + return _DB_make_list(self, txn, _KEYS_LIST); +} + + +static PyObject* +DB_items(DBObject* self, PyObject* args) +{ + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + + if (!PyArg_ParseTuple(args,"|O:items", &txnobj)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + return _DB_make_list(self, txn, _ITEMS_LIST); +} + + +static PyObject* +DB_values(DBObject* self, PyObject* args) +{ + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + + if (!PyArg_ParseTuple(args,"|O:values", &txnobj)) + return NULL; + if (!checkTxnObj(txnobj, &txn)) + return NULL; + return _DB_make_list(self, txn, _VALUES_LIST); +} + +/* --------------------------------------------------------------------- */ +/* DBCursor methods */ + + +static PyObject* +DBC_close(DBCursorObject* self, PyObject* args) +{ + int err = 0; + + if (!PyArg_ParseTuple(args, ":close")) + return NULL; + + if (self->dbc != NULL) { + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_close(self->dbc); + self->dbc = NULL; + MYDB_END_ALLOW_THREADS; + } + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBC_count(DBCursorObject* self, PyObject* args) +{ + int err = 0; + db_recno_t count; + int flags = 0; + + if (!PyArg_ParseTuple(args, "|i:count", &flags)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_count(self->dbc, &count, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + return PyInt_FromLong(count); +} + + +static PyObject* +DBC_current(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_CURRENT,args,kwargs,"|iii:current"); +} + + +static PyObject* +DBC_delete(DBCursorObject* self, PyObject* args) +{ + int err, flags=0; + + if (!PyArg_ParseTuple(args, "|i:delete", &flags)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_del(self->dbc, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + self->mydb->haveStat = 0; + RETURN_NONE(); +} + + +static PyObject* +DBC_dup(DBCursorObject* self, PyObject* args) +{ + int err, flags =0; + DBC* dbc = NULL; + + if (!PyArg_ParseTuple(args, "|i:dup", &flags)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_dup(self->dbc, &dbc, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + return (PyObject*) newDBCursorObject(dbc, self->mydb); +} + +static PyObject* +DBC_first(DBCursorObject* self, PyObject* args, PyObject* kwargs) +{ + return _DBCursor_get(self,DB_FIRST,args,kwargs,"|iii:first"); +} + + +static PyObject* +DBC_get(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + int err, flags=0; + PyObject* keyobj = NULL; + PyObject* dataobj = NULL; + PyObject* retval = NULL; + int dlen = -1; + int doff = -1; + DBT key, data; + char* kwnames[] = { "key","data", "flags", "dlen", "doff", NULL }; + + CLEAR_DBT(key); + CLEAR_DBT(data); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|ii:get", &kwnames[2], + &flags, &dlen, &doff)) + { + PyErr_Clear(); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi|ii:get", + &kwnames[1], + &keyobj, &flags, &dlen, &doff)) + { + PyErr_Clear(); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOi|ii:get", + kwnames, &keyobj, &dataobj, + &flags, &dlen, &doff)) + { + return NULL; + } + } + } + + CHECK_CURSOR_NOT_CLOSED(self); + + if (keyobj && !make_key_dbt(self->mydb, keyobj, &key, NULL)) + return NULL; + if (dataobj && !make_dbt(dataobj, &data)) + return NULL; + if (!add_partial_dbt(&data, dlen, doff)) + return NULL; + + if (CHECK_DBFLAG(self->mydb, DB_THREAD)) { + data.flags = DB_DBT_MALLOC; + key.flags = DB_DBT_MALLOC; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_get(self->dbc, &key, &data, flags); + MYDB_END_ALLOW_THREADS; + + + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.getReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { + switch (_DB_get_type(self->mydb)) { + case -1: + retval = NULL; + break; + case DB_BTREE: + case DB_HASH: + default: + retval = Py_BuildValue("s#s#", key.data, key.size, + data.data, data.size); + break; + case DB_RECNO: + case DB_QUEUE: + retval = Py_BuildValue("is#", *((db_recno_t*)key.data), + data.data, data.size); + break; + } + FREE_DBT(key); + FREE_DBT(data); + } + return retval; +} + + +static PyObject* +DBC_get_recno(DBCursorObject* self, PyObject* args) +{ + int err; + db_recno_t recno; + DBT key; + DBT data; + + if (!PyArg_ParseTuple(args, ":get_recno")) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + CLEAR_DBT(key); + CLEAR_DBT(data); + if (CHECK_DBFLAG(self->mydb, DB_THREAD)) { + /* Tell BerkeleyDB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + key.flags = DB_DBT_MALLOC; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_get(self->dbc, &key, &data, DB_GET_RECNO); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + recno = *((db_recno_t*)data.data); + FREE_DBT(key); + FREE_DBT(data); + return PyInt_FromLong(recno); +} + + +static PyObject* +DBC_last(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_LAST,args,kwargs,"|iii:last"); +} + + +static PyObject* +DBC_next(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_NEXT,args,kwargs,"|iii:next"); +} + + +static PyObject* +DBC_prev(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_PREV,args,kwargs,"|iii:prev"); +} + + +static PyObject* +DBC_put(DBCursorObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags = 0; + PyObject* keyobj, *dataobj; + DBT key, data; + char* kwnames[] = { "key", "data", "flags", "dlen", "doff", NULL }; + int dlen = -1; + int doff = -1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|iii:put", kwnames, + &keyobj, &dataobj, &flags, &dlen, &doff)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + if (!make_key_dbt(self->mydb, keyobj, &key, NULL)) + return NULL; + if (!make_dbt(dataobj, &data)) + return NULL; + if (!add_partial_dbt(&data, dlen, doff)) return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_put(self->dbc, &key, &data, flags); + MYDB_END_ALLOW_THREADS; + FREE_DBT(key); + RETURN_IF_ERR(); + self->mydb->haveStat = 0; + RETURN_NONE(); +} + + +static PyObject* +DBC_set(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + int err, flags = 0; + DBT key, data; + PyObject* retval, *keyobj; + char* kwnames[] = { "key", "flags", "dlen", "doff", NULL }; + int dlen = -1; + int doff = -1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|iii:set", kwnames, + &keyobj, &flags, &dlen, &doff)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + if (!make_key_dbt(self->mydb, keyobj, &key, NULL)) + return NULL; + + CLEAR_DBT(data); + if (CHECK_DBFLAG(self->mydb, DB_THREAD)) { + /* Tell BerkeleyDB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + } + if (!add_partial_dbt(&data, dlen, doff)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET); + MYDB_END_ALLOW_THREADS; + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.cursorSetReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { + switch (_DB_get_type(self->mydb)) { + case -1: + retval = NULL; + break; + case DB_BTREE: + case DB_HASH: + default: + retval = Py_BuildValue("s#s#", key.data, key.size, + data.data, data.size); + break; + case DB_RECNO: + case DB_QUEUE: + retval = Py_BuildValue("is#", *((db_recno_t*)key.data), + data.data, data.size); + break; + } + FREE_DBT(key); + FREE_DBT(data); + } + + return retval; +} + + +static PyObject* +DBC_set_range(DBCursorObject* self, PyObject* args, PyObject* kwargs) +{ + int err, flags = 0; + DBT key, data; + PyObject* retval, *keyobj; + char* kwnames[] = { "key", "flags", "dlen", "doff", NULL }; + int dlen = -1; + int doff = -1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|iii:set_range", kwnames, + &keyobj, &flags, &dlen, &doff)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + if (!make_key_dbt(self->mydb, keyobj, &key, NULL)) + return NULL; + + CLEAR_DBT(data); + if (CHECK_DBFLAG(self->mydb, DB_THREAD)) { + /* Tell BerkeleyDB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + key.flags = DB_DBT_MALLOC; + } + if (!add_partial_dbt(&data, dlen, doff)) + return NULL; + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET_RANGE); + MYDB_END_ALLOW_THREADS; + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.cursorSetReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { + switch (_DB_get_type(self->mydb)) { + case -1: + retval = NULL; + break; + case DB_BTREE: + case DB_HASH: + default: + retval = Py_BuildValue("s#s#", key.data, key.size, + data.data, data.size); + break; + case DB_RECNO: + case DB_QUEUE: + retval = Py_BuildValue("is#", *((db_recno_t*)key.data), + data.data, data.size); + break; + } + FREE_DBT(key); + FREE_DBT(data); + } + + return retval; +} + +static PyObject* +_DBC_get_set_both(DBCursorObject* self, PyObject* keyobj, PyObject* dataobj, + int flags, unsigned int returnsNone) +{ + int err; + DBT key, data; + PyObject* retval; + + // the caller did this: CHECK_CURSOR_NOT_CLOSED(self); + if (!make_key_dbt(self->mydb, keyobj, &key, NULL)) + return NULL; + if (!make_dbt(dataobj, &data)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_GET_BOTH); + MYDB_END_ALLOW_THREADS; + if ((err == DB_NOTFOUND) && returnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { + switch (_DB_get_type(self->mydb)) { + case -1: + retval = NULL; + break; + case DB_BTREE: + case DB_HASH: + default: + retval = Py_BuildValue("s#s#", key.data, key.size, + data.data, data.size); + break; + case DB_RECNO: + case DB_QUEUE: + retval = Py_BuildValue("is#", *((db_recno_t*)key.data), + data.data, data.size); + break; + } + } + + FREE_DBT(key); + return retval; +} + +static PyObject* +DBC_get_both(DBCursorObject* self, PyObject* args) +{ + int flags=0; + PyObject *keyobj, *dataobj; + + if (!PyArg_ParseTuple(args, "OO|i:get_both", &keyobj, &dataobj, &flags)) + return NULL; + + // if the cursor is closed, self->mydb may be invalid + CHECK_CURSOR_NOT_CLOSED(self); + + return _DBC_get_set_both(self, keyobj, dataobj, flags, + self->mydb->moduleFlags.getReturnsNone); +} + +static PyObject* +DBC_set_both(DBCursorObject* self, PyObject* args) +{ + int flags=0; + PyObject *keyobj, *dataobj; + + if (!PyArg_ParseTuple(args, "OO|i:set_both", &keyobj, &dataobj, &flags)) + return NULL; + + // if the cursor is closed, self->mydb may be invalid + CHECK_CURSOR_NOT_CLOSED(self); + + return _DBC_get_set_both(self, keyobj, dataobj, flags, + self->mydb->moduleFlags.cursorSetReturnsNone); +} + + +static PyObject* +DBC_set_recno(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + int err, irecno, flags=0; + db_recno_t recno; + DBT key, data; + PyObject* retval; + int dlen = -1; + int doff = -1; + char* kwnames[] = { "recno","flags", "dlen", "doff", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|iii:set_recno", kwnames, + &irecno, &flags, &dlen, &doff)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + CLEAR_DBT(key); + recno = (db_recno_t) irecno; + /* use allocated space so DB will be able to realloc room for the real + * key */ + key.data = malloc(sizeof(db_recno_t)); + if (key.data == NULL) { + PyErr_SetString(PyExc_MemoryError, "Key memory allocation failed"); + return NULL; + } + key.size = sizeof(db_recno_t); + key.ulen = key.size; + memcpy(key.data, &recno, sizeof(db_recno_t)); + key.flags = DB_DBT_REALLOC; + + CLEAR_DBT(data); + if (CHECK_DBFLAG(self->mydb, DB_THREAD)) { + /* Tell BerkeleyDB to malloc the return value (thread safe) */ + data.flags = DB_DBT_MALLOC; + } + if (!add_partial_dbt(&data, dlen, doff)) + return NULL; + + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_get(self->dbc, &key, &data, flags|DB_SET_RECNO); + MYDB_END_ALLOW_THREADS; + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.cursorSetReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { /* Can only be used for BTrees, so no need to return int key */ + retval = Py_BuildValue("s#s#", key.data, key.size, + data.data, data.size); + FREE_DBT(key); + FREE_DBT(data); + } + + return retval; +} + + +static PyObject* +DBC_consume(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_CONSUME,args,kwargs,"|iii:consume"); +} + + +static PyObject* +DBC_next_dup(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_NEXT_DUP,args,kwargs,"|iii:next_dup"); +} + + +static PyObject* +DBC_next_nodup(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_NEXT_NODUP,args,kwargs,"|iii:next_nodup"); +} + + +static PyObject* +DBC_prev_nodup(DBCursorObject* self, PyObject* args, PyObject *kwargs) +{ + return _DBCursor_get(self,DB_PREV_NODUP,args,kwargs,"|iii:prev_nodup"); +} + + +static PyObject* +DBC_join_item(DBCursorObject* self, PyObject* args) +{ + int err, flags=0; + DBT key, data; + PyObject* retval; + + if (!PyArg_ParseTuple(args, "|i:join_item", &flags)) + return NULL; + + CHECK_CURSOR_NOT_CLOSED(self); + + CLEAR_DBT(key); + CLEAR_DBT(data); + if (CHECK_DBFLAG(self->mydb, DB_THREAD)) { + /* Tell BerkeleyDB to malloc the return value (thread safe) */ + key.flags = DB_DBT_MALLOC; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->dbc->c_get(self->dbc, &key, &data, flags | DB_JOIN_ITEM); + MYDB_END_ALLOW_THREADS; + if ((err == DB_NOTFOUND) && self->mydb->moduleFlags.getReturnsNone) { + Py_INCREF(Py_None); + retval = Py_None; + } + else if (makeDBError(err)) { + retval = NULL; + } + else { + retval = Py_BuildValue("s#", key.data, key.size); + FREE_DBT(key); + } + + return retval; +} + + + +/* --------------------------------------------------------------------- */ +/* DBEnv methods */ + + +static PyObject* +DBEnv_close(DBEnvObject* self, PyObject* args) +{ + int err, flags = 0; + + if (!PyArg_ParseTuple(args, "|i:close", &flags)) + return NULL; + if (!self->closed) { /* Don't close more than once */ + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->close(self->db_env, flags); + MYDB_END_ALLOW_THREADS; + /* after calling DBEnv->close, regardless of error, this DBEnv + * may not be accessed again (BerkeleyDB docs). */ + self->closed = 1; + self->db_env = NULL; + RETURN_IF_ERR(); + } + RETURN_NONE(); +} + + +static PyObject* +DBEnv_open(DBEnvObject* self, PyObject* args) +{ + int err, flags=0, mode=0660; + char *db_home; + + if (!PyArg_ParseTuple(args, "z|ii:open", &db_home, &flags, &mode)) + return NULL; + + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->open(self->db_env, db_home, flags, mode); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + self->closed = 0; + self->flags = flags; + RETURN_NONE(); +} + + +static PyObject* +DBEnv_remove(DBEnvObject* self, PyObject* args) +{ + int err, flags=0; + char *db_home; + + if (!PyArg_ParseTuple(args, "s|i:remove", &db_home, &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->remove(self->db_env, db_home, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +#if (DBVER >= 41) +static PyObject* +DBEnv_dbremove(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + u_int32_t flags=0; + char *file = NULL; + char *database = NULL; + PyObject *txnobj = NULL; + DB_TXN *txn = NULL; + char* kwnames[] = { "file", "database", "txn", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss|Oi:dbremove", kwnames, + &file, &database, &txnobj, &flags)) { + return NULL; + } + if (!checkTxnObj(txnobj, &txn)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->dbremove(self->db_env, txn, file, database, flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_dbrename(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + u_int32_t flags=0; + char *file = NULL; + char *database = NULL; + char *newname = NULL; + PyObject *txnobj = NULL; + DB_TXN *txn = NULL; + char* kwnames[] = { "file", "database", "newname", "txn", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sss|Oi:dbrename", kwnames, + &file, &database, &newname, &txnobj, &flags)) { + return NULL; + } + if (!checkTxnObj(txnobj, &txn)) { + return NULL; + } + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->dbrename(self->db_env, txn, file, database, newname, + flags); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_set_encrypt(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + u_int32_t flags=0; + char *passwd = NULL; + char* kwnames[] = { "passwd", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|i:set_encrypt", kwnames, + &passwd, &flags)) { + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_encrypt(self->db_env, passwd, flags); + MYDB_END_ALLOW_THREADS; + + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif /* DBVER >= 41 */ + +#if (DBVER >= 40) +static PyObject* +DBEnv_set_timeout(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int err; + u_int32_t flags=0; + u_int32_t timeout = 0; + char* kwnames[] = { "timeout", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii:set_timeout", kwnames, + &timeout, &flags)) { + return NULL; + } + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_timeout(self->db_env, (db_timeout_t)timeout, flags); + MYDB_END_ALLOW_THREADS; + + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif /* DBVER >= 40 */ + +static PyObject* +DBEnv_set_shm_key(DBEnvObject* self, PyObject* args) +{ + int err; + long shm_key = 0; + + if (!PyArg_ParseTuple(args, "l:set_shm_key", &shm_key)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + err = self->db_env->set_shm_key(self->db_env, shm_key); + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_set_cachesize(DBEnvObject* self, PyObject* args) +{ + int err, gbytes=0, bytes=0, ncache=0; + + if (!PyArg_ParseTuple(args, "ii|i:set_cachesize", + &gbytes, &bytes, &ncache)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_cachesize(self->db_env, gbytes, bytes, ncache); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +#if (DBVER >= 32) +static PyObject* +DBEnv_set_flags(DBEnvObject* self, PyObject* args) +{ + int err, flags=0, onoff=0; + + if (!PyArg_ParseTuple(args, "ii:set_flags", + &flags, &onoff)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_flags(self->db_env, flags, onoff); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} +#endif + + +static PyObject* +DBEnv_set_data_dir(DBEnvObject* self, PyObject* args) +{ + int err; + char *dir; + + if (!PyArg_ParseTuple(args, "s:set_data_dir", &dir)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_data_dir(self->db_env, dir); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_lg_bsize(DBEnvObject* self, PyObject* args) +{ + int err, lg_bsize; + + if (!PyArg_ParseTuple(args, "i:set_lg_bsize", &lg_bsize)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lg_bsize(self->db_env, lg_bsize); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_lg_dir(DBEnvObject* self, PyObject* args) +{ + int err; + char *dir; + + if (!PyArg_ParseTuple(args, "s:set_lg_dir", &dir)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lg_dir(self->db_env, dir); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBEnv_set_lg_max(DBEnvObject* self, PyObject* args) +{ + int err, lg_max; + + if (!PyArg_ParseTuple(args, "i:set_lg_max", &lg_max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lg_max(self->db_env, lg_max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_lk_detect(DBEnvObject* self, PyObject* args) +{ + int err, lk_detect; + + if (!PyArg_ParseTuple(args, "i:set_lk_detect", &lk_detect)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lk_detect(self->db_env, lk_detect); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_lk_max(DBEnvObject* self, PyObject* args) +{ + int err, max; + + if (!PyArg_ParseTuple(args, "i:set_lk_max", &max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lk_max(self->db_env, max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +#if (DBVER >= 32) + +static PyObject* +DBEnv_set_lk_max_locks(DBEnvObject* self, PyObject* args) +{ + int err, max; + + if (!PyArg_ParseTuple(args, "i:set_lk_max_locks", &max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lk_max_locks(self->db_env, max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_lk_max_lockers(DBEnvObject* self, PyObject* args) +{ + int err, max; + + if (!PyArg_ParseTuple(args, "i:set_lk_max_lockers", &max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lk_max_lockers(self->db_env, max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_lk_max_objects(DBEnvObject* self, PyObject* args) +{ + int err, max; + + if (!PyArg_ParseTuple(args, "i:set_lk_max_objects", &max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_lk_max_objects(self->db_env, max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +#endif + + +static PyObject* +DBEnv_set_mp_mmapsize(DBEnvObject* self, PyObject* args) +{ + int err, mp_mmapsize; + + if (!PyArg_ParseTuple(args, "i:set_mp_mmapsize", &mp_mmapsize)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_mp_mmapsize(self->db_env, mp_mmapsize); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_tmp_dir(DBEnvObject* self, PyObject* args) +{ + int err; + char *dir; + + if (!PyArg_ParseTuple(args, "s:set_tmp_dir", &dir)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_tmp_dir(self->db_env, dir); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_txn_begin(DBEnvObject* self, PyObject* args, PyObject* kwargs) +{ + int flags = 0; + PyObject* txnobj = NULL; + DB_TXN *txn = NULL; + char* kwnames[] = { "parent", "flags", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:txn_begin", kwnames, + &txnobj, &flags)) + return NULL; + + if (!checkTxnObj(txnobj, &txn)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + return (PyObject*)newDBTxnObject(self, txn, flags); +} + + +static PyObject* +DBEnv_txn_checkpoint(DBEnvObject* self, PyObject* args) +{ + int err, kbyte=0, min=0, flags=0; + + if (!PyArg_ParseTuple(args, "|iii:txn_checkpoint", &kbyte, &min, &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = self->db_env->txn_checkpoint(self->db_env, kbyte, min, flags); +#else + err = txn_checkpoint(self->db_env, kbyte, min, flags); +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_set_tx_max(DBEnvObject* self, PyObject* args) +{ + int err, max; + + if (!PyArg_ParseTuple(args, "i:set_tx_max", &max)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; + err = self->db_env->set_tx_max(self->db_env, max); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_lock_detect(DBEnvObject* self, PyObject* args) +{ + int err, atype, flags=0; + int aborted = 0; + + if (!PyArg_ParseTuple(args, "i|i:lock_detect", &atype, &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = self->db_env->lock_detect(self->db_env, flags, atype, &aborted); +#else + err = lock_detect(self->db_env, flags, atype, &aborted); +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + return PyInt_FromLong(aborted); +} + + +static PyObject* +DBEnv_lock_get(DBEnvObject* self, PyObject* args) +{ + int flags=0; + int locker, lock_mode; + DBT obj; + PyObject* objobj; + + if (!PyArg_ParseTuple(args, "iOi|i:lock_get", &locker, &objobj, &lock_mode, &flags)) + return NULL; + + + if (!make_dbt(objobj, &obj)) + return NULL; + + return (PyObject*)newDBLockObject(self, locker, &obj, lock_mode, flags); +} + + +static PyObject* +DBEnv_lock_id(DBEnvObject* self, PyObject* args) +{ + int err; + u_int32_t theID; + + if (!PyArg_ParseTuple(args, ":lock_id")) + return NULL; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = self->db_env->lock_id(self->db_env, &theID); +#else + err = lock_id(self->db_env, &theID); +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + return PyInt_FromLong((long)theID); +} + + +static PyObject* +DBEnv_lock_put(DBEnvObject* self, PyObject* args) +{ + int err; + DBLockObject* dblockobj; + + if (!PyArg_ParseTuple(args, "O!:lock_put", &DBLock_Type, &dblockobj)) + return NULL; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = self->db_env->lock_put(self->db_env, &dblockobj->lock); +#else + err = lock_put(self->db_env, &dblockobj->lock); +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBEnv_lock_stat(DBEnvObject* self, PyObject* args) +{ + int err; + DB_LOCK_STAT* sp; + PyObject* d = NULL; + u_int32_t flags = 0; + + if (!PyArg_ParseTuple(args, "|i:lock_stat", &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = self->db_env->lock_stat(self->db_env, &sp, flags); +#else +#if (DBVER >= 33) + err = lock_stat(self->db_env, &sp); +#else + err = lock_stat(self->db_env, &sp, NULL); +#endif +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + /* Turn the stat structure into a dictionary */ + d = PyDict_New(); + if (d == NULL) { + free(sp); + return NULL; + } + +#define MAKE_ENTRY(name) _addIntToDict(d, #name, sp->st_##name) + +#if (DBVER < 41) + MAKE_ENTRY(lastid); +#endif + MAKE_ENTRY(nmodes); +#if (DBVER >= 32) + MAKE_ENTRY(maxlocks); + MAKE_ENTRY(maxlockers); + MAKE_ENTRY(maxobjects); + MAKE_ENTRY(nlocks); + MAKE_ENTRY(maxnlocks); +#endif + MAKE_ENTRY(nlockers); + MAKE_ENTRY(maxnlockers); +#if (DBVER >= 32) + MAKE_ENTRY(nobjects); + MAKE_ENTRY(maxnobjects); +#endif + MAKE_ENTRY(nrequests); + MAKE_ENTRY(nreleases); + MAKE_ENTRY(nnowaits); + MAKE_ENTRY(nconflicts); + MAKE_ENTRY(ndeadlocks); + MAKE_ENTRY(regsize); + MAKE_ENTRY(region_wait); + MAKE_ENTRY(region_nowait); + +#undef MAKE_ENTRY + free(sp); + return d; +} + + +static PyObject* +DBEnv_log_archive(DBEnvObject* self, PyObject* args) +{ + int flags=0; + int err; + char **log_list_start, **log_list; + PyObject* list; + PyObject* item = NULL; + + if (!PyArg_ParseTuple(args, "|i:log_archive", &flags)) + return NULL; + + CHECK_ENV_NOT_CLOSED(self); + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = self->db_env->log_archive(self->db_env, &log_list, flags); +#elif (DBVER == 33) + err = log_archive(self->db_env, &log_list, flags); +#else + err = log_archive(self->db_env, &log_list, flags, NULL); +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + list = PyList_New(0); + if (list == NULL) { + PyErr_SetString(PyExc_MemoryError, "PyList_New failed"); + return NULL; + } + + if (log_list) { + for (log_list_start = log_list; *log_list != NULL; ++log_list) { + item = PyString_FromString (*log_list); + if (item == NULL) { + Py_DECREF(list); + PyErr_SetString(PyExc_MemoryError, + "List item creation failed"); + list = NULL; + break; + } + PyList_Append(list, item); + Py_DECREF(item); + } + free(log_list_start); + } + return list; +} + + +static PyObject* +DBEnv_txn_stat(DBEnvObject* self, PyObject* args) +{ + int err; + DB_TXN_STAT* sp; + PyObject* d = NULL; + u_int32_t flags=0; + + if (!PyArg_ParseTuple(args, "|i:txn_stat", &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = self->db_env->txn_stat(self->db_env, &sp, flags); +#elif (DBVER == 33) + err = txn_stat(self->db_env, &sp); +#else + err = txn_stat(self->db_env, &sp, NULL); +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + + /* Turn the stat structure into a dictionary */ + d = PyDict_New(); + if (d == NULL) { + free(sp); + return NULL; + } + +#define MAKE_ENTRY(name) _addIntToDict(d, #name, sp->st_##name) + + MAKE_ENTRY(time_ckp); + MAKE_ENTRY(last_txnid); + MAKE_ENTRY(maxtxns); + MAKE_ENTRY(nactive); + MAKE_ENTRY(maxnactive); + MAKE_ENTRY(nbegins); + MAKE_ENTRY(naborts); + MAKE_ENTRY(ncommits); + MAKE_ENTRY(regsize); + MAKE_ENTRY(region_wait); + MAKE_ENTRY(region_nowait); + +#undef MAKE_ENTRY + free(sp); + return d; +} + + +static PyObject* +DBEnv_set_get_returns_none(DBEnvObject* self, PyObject* args) +{ + int flags=0; + int oldValue=0; + + if (!PyArg_ParseTuple(args,"i:set_get_returns_none", &flags)) + return NULL; + CHECK_ENV_NOT_CLOSED(self); + + if (self->moduleFlags.getReturnsNone) + ++oldValue; + if (self->moduleFlags.cursorSetReturnsNone) + ++oldValue; + self->moduleFlags.getReturnsNone = (flags >= 1); + self->moduleFlags.cursorSetReturnsNone = (flags >= 2); + return PyInt_FromLong(oldValue); +} + + +/* --------------------------------------------------------------------- */ +/* DBTxn methods */ + + +static PyObject* +DBTxn_commit(DBTxnObject* self, PyObject* args) +{ + int flags=0, err; + DB_TXN *txn; + + if (!PyArg_ParseTuple(args, "|i:commit", &flags)) + return NULL; + + if (!self->txn) { + PyErr_SetObject(DBError, Py_BuildValue("(is)", 0, + "DBTxn must not be used after txn_commit or txn_abort")); + return NULL; + } + txn = self->txn; + self->txn = NULL; /* this DB_TXN is no longer valid after this call */ + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = txn->commit(txn, flags); +#else + err = txn_commit(txn, flags); +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + +static PyObject* +DBTxn_prepare(DBTxnObject* self, PyObject* args) +{ +#if (DBVER >= 33) + int err; + char* gid=NULL; + int gid_size=0; + + if (!PyArg_ParseTuple(args, "s#:prepare", &gid, &gid_size)) + return NULL; + + if (gid_size != DB_XIDDATASIZE) { + PyErr_SetString(PyExc_TypeError, + "gid must be DB_XIDDATASIZE bytes long"); + return NULL; + } + + if (!self->txn) { + PyErr_SetObject(DBError, Py_BuildValue("(is)", 0, + "DBTxn must not be used after txn_commit or txn_abort")); + return NULL; + } + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = self->txn->prepare(self->txn, (u_int8_t*)gid); +#else + err = txn_prepare(self->txn, (u_int8_t*)gid); +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +#else + int err; + + if (!PyArg_ParseTuple(args, ":prepare")) + return NULL; + + if (!self->txn) { + PyErr_SetObject(DBError, Py_BuildValue("(is)", 0, + "DBTxn must not be used after txn_commit or txn_abort")); + return NULL; + } + MYDB_BEGIN_ALLOW_THREADS; + err = txn_prepare(self->txn); + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +#endif +} + + +static PyObject* +DBTxn_abort(DBTxnObject* self, PyObject* args) +{ + int err; + DB_TXN *txn; + + if (!PyArg_ParseTuple(args, ":abort")) + return NULL; + + if (!self->txn) { + PyErr_SetObject(DBError, Py_BuildValue("(is)", 0, + "DBTxn must not be used after txn_commit or txn_abort")); + return NULL; + } + txn = self->txn; + self->txn = NULL; /* this DB_TXN is no longer valid after this call */ + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + err = txn->abort(txn); +#else + err = txn_abort(txn); +#endif + MYDB_END_ALLOW_THREADS; + RETURN_IF_ERR(); + RETURN_NONE(); +} + + +static PyObject* +DBTxn_id(DBTxnObject* self, PyObject* args) +{ + int id; + + if (!PyArg_ParseTuple(args, ":id")) + return NULL; + + if (!self->txn) { + PyErr_SetObject(DBError, Py_BuildValue("(is)", 0, + "DBTxn must not be used after txn_commit or txn_abort")); + return NULL; + } + MYDB_BEGIN_ALLOW_THREADS; +#if (DBVER >= 40) + id = self->txn->id(self->txn); +#else + id = txn_id(self->txn); +#endif + MYDB_END_ALLOW_THREADS; + return PyInt_FromLong(id); +} + +/* --------------------------------------------------------------------- */ +/* Method definition tables and type objects */ + +static PyMethodDef DB_methods[] = { + {"append", (PyCFunction)DB_append, METH_VARARGS}, +#if (DBVER >= 33) + {"associate", (PyCFunction)DB_associate, METH_VARARGS|METH_KEYWORDS}, +#endif + {"close", (PyCFunction)DB_close, METH_VARARGS}, +#if (DBVER >= 32) + {"consume", (PyCFunction)DB_consume, METH_VARARGS|METH_KEYWORDS}, + {"consume_wait", (PyCFunction)DB_consume_wait, METH_VARARGS|METH_KEYWORDS}, +#endif + {"cursor", (PyCFunction)DB_cursor, METH_VARARGS|METH_KEYWORDS}, + {"delete", (PyCFunction)DB_delete, METH_VARARGS|METH_KEYWORDS}, + {"fd", (PyCFunction)DB_fd, METH_VARARGS}, + {"get", (PyCFunction)DB_get, METH_VARARGS|METH_KEYWORDS}, + {"get_both", (PyCFunction)DB_get_both, METH_VARARGS|METH_KEYWORDS}, + {"get_byteswapped", (PyCFunction)DB_get_byteswapped,METH_VARARGS}, + {"get_size", (PyCFunction)DB_get_size, METH_VARARGS|METH_KEYWORDS}, + {"get_type", (PyCFunction)DB_get_type, METH_VARARGS}, + {"join", (PyCFunction)DB_join, METH_VARARGS}, + {"key_range", (PyCFunction)DB_key_range, METH_VARARGS|METH_KEYWORDS}, + {"has_key", (PyCFunction)DB_has_key, METH_VARARGS}, + {"items", (PyCFunction)DB_items, METH_VARARGS}, + {"keys", (PyCFunction)DB_keys, METH_VARARGS}, + {"open", (PyCFunction)DB_open, METH_VARARGS|METH_KEYWORDS}, + {"put", (PyCFunction)DB_put, METH_VARARGS|METH_KEYWORDS}, + {"remove", (PyCFunction)DB_remove, METH_VARARGS|METH_KEYWORDS}, + {"rename", (PyCFunction)DB_rename, METH_VARARGS}, + {"set_bt_minkey", (PyCFunction)DB_set_bt_minkey, METH_VARARGS}, + {"set_cachesize", (PyCFunction)DB_set_cachesize, METH_VARARGS}, +#if (DBVER >= 41) + {"set_encrypt", (PyCFunction)DB_set_encrypt, METH_VARARGS|METH_KEYWORDS}, +#endif + {"set_flags", (PyCFunction)DB_set_flags, METH_VARARGS}, + {"set_h_ffactor", (PyCFunction)DB_set_h_ffactor, METH_VARARGS}, + {"set_h_nelem", (PyCFunction)DB_set_h_nelem, METH_VARARGS}, + {"set_lorder", (PyCFunction)DB_set_lorder, METH_VARARGS}, + {"set_pagesize", (PyCFunction)DB_set_pagesize, METH_VARARGS}, + {"set_re_delim", (PyCFunction)DB_set_re_delim, METH_VARARGS}, + {"set_re_len", (PyCFunction)DB_set_re_len, METH_VARARGS}, + {"set_re_pad", (PyCFunction)DB_set_re_pad, METH_VARARGS}, + {"set_re_source", (PyCFunction)DB_set_re_source, METH_VARARGS}, +#if (DBVER >= 32) + {"set_q_extentsize",(PyCFunction)DB_set_q_extentsize,METH_VARARGS}, +#endif + {"stat", (PyCFunction)DB_stat, METH_VARARGS}, + {"sync", (PyCFunction)DB_sync, METH_VARARGS}, +#if (DBVER >= 33) + {"truncate", (PyCFunction)DB_truncate, METH_VARARGS|METH_KEYWORDS}, +#endif + {"type", (PyCFunction)DB_get_type, METH_VARARGS}, + {"upgrade", (PyCFunction)DB_upgrade, METH_VARARGS}, + {"values", (PyCFunction)DB_values, METH_VARARGS}, + {"verify", (PyCFunction)DB_verify, METH_VARARGS|METH_KEYWORDS}, + {"set_get_returns_none",(PyCFunction)DB_set_get_returns_none, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +static PyMappingMethods DB_mapping = { + (inquiry)DB_length, /*mp_length*/ + (binaryfunc)DB_subscript, /*mp_subscript*/ + (objobjargproc)DB_ass_sub, /*mp_ass_subscript*/ +}; + + +static PyMethodDef DBCursor_methods[] = { + {"close", (PyCFunction)DBC_close, METH_VARARGS}, + {"count", (PyCFunction)DBC_count, METH_VARARGS}, + {"current", (PyCFunction)DBC_current, METH_VARARGS|METH_KEYWORDS}, + {"delete", (PyCFunction)DBC_delete, METH_VARARGS}, + {"dup", (PyCFunction)DBC_dup, METH_VARARGS}, + {"first", (PyCFunction)DBC_first, METH_VARARGS|METH_KEYWORDS}, + {"get", (PyCFunction)DBC_get, METH_VARARGS|METH_KEYWORDS}, + {"get_recno", (PyCFunction)DBC_get_recno, METH_VARARGS}, + {"last", (PyCFunction)DBC_last, METH_VARARGS|METH_KEYWORDS}, + {"next", (PyCFunction)DBC_next, METH_VARARGS|METH_KEYWORDS}, + {"prev", (PyCFunction)DBC_prev, METH_VARARGS|METH_KEYWORDS}, + {"put", (PyCFunction)DBC_put, METH_VARARGS|METH_KEYWORDS}, + {"set", (PyCFunction)DBC_set, METH_VARARGS|METH_KEYWORDS}, + {"set_range", (PyCFunction)DBC_set_range, METH_VARARGS|METH_KEYWORDS}, + {"get_both", (PyCFunction)DBC_get_both, METH_VARARGS}, + {"set_both", (PyCFunction)DBC_set_both, METH_VARARGS}, + {"set_recno", (PyCFunction)DBC_set_recno, METH_VARARGS|METH_KEYWORDS}, + {"consume", (PyCFunction)DBC_consume, METH_VARARGS|METH_KEYWORDS}, + {"next_dup", (PyCFunction)DBC_next_dup, METH_VARARGS|METH_KEYWORDS}, + {"next_nodup", (PyCFunction)DBC_next_nodup, METH_VARARGS|METH_KEYWORDS}, + {"prev_nodup", (PyCFunction)DBC_prev_nodup, METH_VARARGS|METH_KEYWORDS}, + {"join_item", (PyCFunction)DBC_join_item, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +static PyMethodDef DBEnv_methods[] = { + {"close", (PyCFunction)DBEnv_close, METH_VARARGS}, + {"open", (PyCFunction)DBEnv_open, METH_VARARGS}, + {"remove", (PyCFunction)DBEnv_remove, METH_VARARGS}, +#if (DBVER >= 41) + {"dbremove", (PyCFunction)DBEnv_dbremove, METH_VARARGS|METH_KEYWORDS}, + {"dbrename", (PyCFunction)DBEnv_dbrename, METH_VARARGS|METH_KEYWORDS}, + {"set_encrypt", (PyCFunction)DBEnv_set_encrypt, METH_VARARGS|METH_KEYWORDS}, +#endif +#if (DBVER >= 40) + {"set_timeout", (PyCFunction)DBEnv_set_timeout, METH_VARARGS|METH_KEYWORDS}, +#endif + {"set_shm_key", (PyCFunction)DBEnv_set_shm_key, METH_VARARGS}, + {"set_cachesize", (PyCFunction)DBEnv_set_cachesize, METH_VARARGS}, + {"set_data_dir", (PyCFunction)DBEnv_set_data_dir, METH_VARARGS}, +#if (DBVER >= 32) + {"set_flags", (PyCFunction)DBEnv_set_flags, METH_VARARGS}, +#endif + {"set_lg_bsize", (PyCFunction)DBEnv_set_lg_bsize, METH_VARARGS}, + {"set_lg_dir", (PyCFunction)DBEnv_set_lg_dir, METH_VARARGS}, + {"set_lg_max", (PyCFunction)DBEnv_set_lg_max, METH_VARARGS}, + {"set_lk_detect", (PyCFunction)DBEnv_set_lk_detect, METH_VARARGS}, + {"set_lk_max", (PyCFunction)DBEnv_set_lk_max, METH_VARARGS}, +#if (DBVER >= 32) + {"set_lk_max_locks", (PyCFunction)DBEnv_set_lk_max_locks, METH_VARARGS}, + {"set_lk_max_lockers", (PyCFunction)DBEnv_set_lk_max_lockers, METH_VARARGS}, + {"set_lk_max_objects", (PyCFunction)DBEnv_set_lk_max_objects, METH_VARARGS}, +#endif + {"set_mp_mmapsize", (PyCFunction)DBEnv_set_mp_mmapsize, METH_VARARGS}, + {"set_tmp_dir", (PyCFunction)DBEnv_set_tmp_dir, METH_VARARGS}, + {"txn_begin", (PyCFunction)DBEnv_txn_begin, METH_VARARGS|METH_KEYWORDS}, + {"txn_checkpoint", (PyCFunction)DBEnv_txn_checkpoint, METH_VARARGS}, + {"txn_stat", (PyCFunction)DBEnv_txn_stat, METH_VARARGS}, + {"set_tx_max", (PyCFunction)DBEnv_set_tx_max, METH_VARARGS}, + {"lock_detect", (PyCFunction)DBEnv_lock_detect, METH_VARARGS}, + {"lock_get", (PyCFunction)DBEnv_lock_get, METH_VARARGS}, + {"lock_id", (PyCFunction)DBEnv_lock_id, METH_VARARGS}, + {"lock_put", (PyCFunction)DBEnv_lock_put, METH_VARARGS}, + {"lock_stat", (PyCFunction)DBEnv_lock_stat, METH_VARARGS}, + {"log_archive", (PyCFunction)DBEnv_log_archive, METH_VARARGS}, + {"set_get_returns_none",(PyCFunction)DBEnv_set_get_returns_none, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +static PyMethodDef DBTxn_methods[] = { + {"commit", (PyCFunction)DBTxn_commit, METH_VARARGS}, + {"prepare", (PyCFunction)DBTxn_prepare, METH_VARARGS}, + {"abort", (PyCFunction)DBTxn_abort, METH_VARARGS}, + {"id", (PyCFunction)DBTxn_id, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +static PyObject* +DB_getattr(DBObject* self, char *name) +{ + return Py_FindMethod(DB_methods, (PyObject* )self, name); +} + + +static PyObject* +DBEnv_getattr(DBEnvObject* self, char *name) +{ + if (!strcmp(name, "db_home")) { + CHECK_ENV_NOT_CLOSED(self); + if (self->db_env->db_home == NULL) { + RETURN_NONE(); + } + return PyString_FromString(self->db_env->db_home); + } + + return Py_FindMethod(DBEnv_methods, (PyObject* )self, name); +} + + +static PyObject* +DBCursor_getattr(DBCursorObject* self, char *name) +{ + return Py_FindMethod(DBCursor_methods, (PyObject* )self, name); +} + +static PyObject* +DBTxn_getattr(DBTxnObject* self, char *name) +{ + return Py_FindMethod(DBTxn_methods, (PyObject* )self, name); +} + +static PyObject* +DBLock_getattr(DBLockObject* self, char *name) +{ + return NULL; +} + +statichere PyTypeObject DB_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "DB", /*tp_name*/ + sizeof(DBObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DB_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)DB_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &DB_mapping,/*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + + +statichere PyTypeObject DBCursor_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "DBCursor", /*tp_name*/ + sizeof(DBCursorObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DBCursor_dealloc,/*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)DBCursor_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + + +statichere PyTypeObject DBEnv_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "DBEnv", /*tp_name*/ + sizeof(DBEnvObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DBEnv_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)DBEnv_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + +statichere PyTypeObject DBTxn_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "DBTxn", /*tp_name*/ + sizeof(DBTxnObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DBTxn_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)DBTxn_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + + +statichere PyTypeObject DBLock_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "DBLock", /*tp_name*/ + sizeof(DBLockObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)DBLock_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)DBLock_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + + +/* --------------------------------------------------------------------- */ +/* Module-level functions */ + +static PyObject* +DB_construct(PyObject* self, PyObject* args, PyObject* kwargs) +{ + PyObject* dbenvobj = NULL; + int flags = 0; + char* kwnames[] = { "dbEnv", "flags", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:DB", kwnames, + &dbenvobj, &flags)) + return NULL; + if (dbenvobj == Py_None) + dbenvobj = NULL; + else if (dbenvobj && !DBEnvObject_Check(dbenvobj)) { + makeTypeError("DBEnv", dbenvobj); + return NULL; + } + + return (PyObject* )newDBObject((DBEnvObject*)dbenvobj, flags); +} + + +static PyObject* +DBEnv_construct(PyObject* self, PyObject* args) +{ + int flags = 0; + if (!PyArg_ParseTuple(args, "|i:DbEnv", &flags)) return NULL; + return (PyObject* )newDBEnvObject(flags); +} + + +static char bsddb_version_doc[] = +"Returns a tuple of major, minor, and patch release numbers of the\n\ +underlying DB library."; + +static PyObject* +bsddb_version(PyObject* self, PyObject* args) +{ + int major, minor, patch; + + if (!PyArg_ParseTuple(args, ":version")) + return NULL; + db_version(&major, &minor, &patch); + return Py_BuildValue("(iii)", major, minor, patch); +} + + +/* List of functions defined in the module */ + +static PyMethodDef bsddb_methods[] = { + {"DB", (PyCFunction)DB_construct, METH_VARARGS | METH_KEYWORDS }, + {"DBEnv", (PyCFunction)DBEnv_construct, METH_VARARGS}, + {"version", (PyCFunction)bsddb_version, METH_VARARGS, bsddb_version_doc}, + {NULL, NULL} /* sentinel */ +}; + + +/* --------------------------------------------------------------------- */ +/* Module initialization */ + + +/* Convenience routine to export an integer value. + * Errors are silently ignored, for better or for worse... + */ +#define ADD_INT(dict, NAME) _addIntToDict(dict, #NAME, NAME) + +DL_EXPORT(void) init_bsddb(void) +{ + PyObject* m; + PyObject* d; + PyObject* pybsddb_version_s = PyString_FromString( PY_BSDDB_VERSION ); + PyObject* db_version_s = PyString_FromString( DB_VERSION_STRING ); + PyObject* cvsid_s = PyString_FromString( rcs_id ); + + /* Initialize the type of the new type objects here; doing it here + is required for portability to Windows without requiring C++. */ + DB_Type.ob_type = &PyType_Type; + DBCursor_Type.ob_type = &PyType_Type; + DBEnv_Type.ob_type = &PyType_Type; + DBTxn_Type.ob_type = &PyType_Type; + DBLock_Type.ob_type = &PyType_Type; + + +#if defined(WITH_THREAD) && !defined(MYDB_USE_GILSTATE) + /* Save the current interpreter, so callbacks can do the right thing. */ + _db_interpreterState = PyThreadState_Get()->interp; +#endif + + /* Create the module and add the functions */ + m = Py_InitModule("_bsddb", bsddb_methods); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + PyDict_SetItemString(d, "__version__", pybsddb_version_s); + PyDict_SetItemString(d, "cvsid", cvsid_s); + PyDict_SetItemString(d, "DB_VERSION_STRING", db_version_s); + Py_DECREF(pybsddb_version_s); + pybsddb_version_s = NULL; + Py_DECREF(cvsid_s); + cvsid_s = NULL; + Py_DECREF(db_version_s); + db_version_s = NULL; + + ADD_INT(d, DB_VERSION_MAJOR); + ADD_INT(d, DB_VERSION_MINOR); + ADD_INT(d, DB_VERSION_PATCH); + + ADD_INT(d, DB_MAX_PAGES); + ADD_INT(d, DB_MAX_RECORDS); + +#if (DBVER >= 42) + ADD_INT(d, DB_RPCCLIENT); +#else + ADD_INT(d, DB_CLIENT); + /* allow apps to be written using DB_RPCCLIENT on older BerkeleyDB */ + _addIntToDict(d, "DB_RPCCLIENT", DB_CLIENT); +#endif + ADD_INT(d, DB_XA_CREATE); + + ADD_INT(d, DB_CREATE); + ADD_INT(d, DB_NOMMAP); + ADD_INT(d, DB_THREAD); + + ADD_INT(d, DB_FORCE); + ADD_INT(d, DB_INIT_CDB); + ADD_INT(d, DB_INIT_LOCK); + ADD_INT(d, DB_INIT_LOG); + ADD_INT(d, DB_INIT_MPOOL); + ADD_INT(d, DB_INIT_TXN); +#if (DBVER >= 32) + ADD_INT(d, DB_JOINENV); +#endif + + ADD_INT(d, DB_RECOVER); + ADD_INT(d, DB_RECOVER_FATAL); + ADD_INT(d, DB_TXN_NOSYNC); + ADD_INT(d, DB_USE_ENVIRON); + ADD_INT(d, DB_USE_ENVIRON_ROOT); + + ADD_INT(d, DB_LOCKDOWN); + ADD_INT(d, DB_PRIVATE); + ADD_INT(d, DB_SYSTEM_MEM); + + ADD_INT(d, DB_TXN_SYNC); + ADD_INT(d, DB_TXN_NOWAIT); + + ADD_INT(d, DB_EXCL); + ADD_INT(d, DB_FCNTL_LOCKING); + ADD_INT(d, DB_ODDFILESIZE); + ADD_INT(d, DB_RDWRMASTER); + ADD_INT(d, DB_RDONLY); + ADD_INT(d, DB_TRUNCATE); +#if (DBVER >= 32) + ADD_INT(d, DB_EXTENT); + ADD_INT(d, DB_CDB_ALLDB); + ADD_INT(d, DB_VERIFY); +#endif + ADD_INT(d, DB_UPGRADE); + + ADD_INT(d, DB_AGGRESSIVE); + ADD_INT(d, DB_NOORDERCHK); + ADD_INT(d, DB_ORDERCHKONLY); + ADD_INT(d, DB_PR_PAGE); +#if ! (DBVER >= 33) + ADD_INT(d, DB_VRFY_FLAGMASK); + ADD_INT(d, DB_PR_HEADERS); +#endif + ADD_INT(d, DB_PR_RECOVERYTEST); + ADD_INT(d, DB_SALVAGE); + + ADD_INT(d, DB_LOCK_NORUN); + ADD_INT(d, DB_LOCK_DEFAULT); + ADD_INT(d, DB_LOCK_OLDEST); + ADD_INT(d, DB_LOCK_RANDOM); + ADD_INT(d, DB_LOCK_YOUNGEST); +#if (DBVER >= 33) + ADD_INT(d, DB_LOCK_MAXLOCKS); + ADD_INT(d, DB_LOCK_MINLOCKS); + ADD_INT(d, DB_LOCK_MINWRITE); +#endif + + +#if (DBVER >= 33) + /* docs say to use zero instead */ + _addIntToDict(d, "DB_LOCK_CONFLICT", 0); +#else + ADD_INT(d, DB_LOCK_CONFLICT); +#endif + + ADD_INT(d, DB_LOCK_DUMP); + ADD_INT(d, DB_LOCK_GET); + ADD_INT(d, DB_LOCK_INHERIT); + ADD_INT(d, DB_LOCK_PUT); + ADD_INT(d, DB_LOCK_PUT_ALL); + ADD_INT(d, DB_LOCK_PUT_OBJ); + + ADD_INT(d, DB_LOCK_NG); + ADD_INT(d, DB_LOCK_READ); + ADD_INT(d, DB_LOCK_WRITE); + ADD_INT(d, DB_LOCK_NOWAIT); +#if (DBVER >= 32) + ADD_INT(d, DB_LOCK_WAIT); +#endif + ADD_INT(d, DB_LOCK_IWRITE); + ADD_INT(d, DB_LOCK_IREAD); + ADD_INT(d, DB_LOCK_IWR); +#if (DBVER >= 33) + ADD_INT(d, DB_LOCK_DIRTY); + ADD_INT(d, DB_LOCK_WWRITE); +#endif + + ADD_INT(d, DB_LOCK_RECORD); + ADD_INT(d, DB_LOCK_UPGRADE); +#if (DBVER >= 32) + ADD_INT(d, DB_LOCK_SWITCH); +#endif +#if (DBVER >= 33) + ADD_INT(d, DB_LOCK_UPGRADE_WRITE); +#endif + + ADD_INT(d, DB_LOCK_NOWAIT); + ADD_INT(d, DB_LOCK_RECORD); + ADD_INT(d, DB_LOCK_UPGRADE); + +#if (DBVER >= 33) + ADD_INT(d, DB_LSTAT_ABORTED); + ADD_INT(d, DB_LSTAT_ERR); + ADD_INT(d, DB_LSTAT_FREE); + ADD_INT(d, DB_LSTAT_HELD); +#if (DBVER == 33) + ADD_INT(d, DB_LSTAT_NOGRANT); +#endif + ADD_INT(d, DB_LSTAT_PENDING); + ADD_INT(d, DB_LSTAT_WAITING); +#endif + + ADD_INT(d, DB_ARCH_ABS); + ADD_INT(d, DB_ARCH_DATA); + ADD_INT(d, DB_ARCH_LOG); + + ADD_INT(d, DB_BTREE); + ADD_INT(d, DB_HASH); + ADD_INT(d, DB_RECNO); + ADD_INT(d, DB_QUEUE); + ADD_INT(d, DB_UNKNOWN); + + ADD_INT(d, DB_DUP); + ADD_INT(d, DB_DUPSORT); + ADD_INT(d, DB_RECNUM); + ADD_INT(d, DB_RENUMBER); + ADD_INT(d, DB_REVSPLITOFF); + ADD_INT(d, DB_SNAPSHOT); + + ADD_INT(d, DB_JOIN_NOSORT); + + ADD_INT(d, DB_AFTER); + ADD_INT(d, DB_APPEND); + ADD_INT(d, DB_BEFORE); + ADD_INT(d, DB_CACHED_COUNTS); +#if (DBVER >= 41) + _addIntToDict(d, "DB_CHECKPOINT", 0); +#else + ADD_INT(d, DB_CHECKPOINT); + ADD_INT(d, DB_CURLSN); +#endif +#if ((DBVER >= 33) && (DBVER <= 41)) + ADD_INT(d, DB_COMMIT); +#endif + ADD_INT(d, DB_CONSUME); +#if (DBVER >= 32) + ADD_INT(d, DB_CONSUME_WAIT); +#endif + ADD_INT(d, DB_CURRENT); +#if (DBVER >= 33) + ADD_INT(d, DB_FAST_STAT); +#endif + ADD_INT(d, DB_FIRST); + ADD_INT(d, DB_FLUSH); + ADD_INT(d, DB_GET_BOTH); + ADD_INT(d, DB_GET_RECNO); + ADD_INT(d, DB_JOIN_ITEM); + ADD_INT(d, DB_KEYFIRST); + ADD_INT(d, DB_KEYLAST); + ADD_INT(d, DB_LAST); + ADD_INT(d, DB_NEXT); + ADD_INT(d, DB_NEXT_DUP); + ADD_INT(d, DB_NEXT_NODUP); + ADD_INT(d, DB_NODUPDATA); + ADD_INT(d, DB_NOOVERWRITE); + ADD_INT(d, DB_NOSYNC); + ADD_INT(d, DB_POSITION); + ADD_INT(d, DB_PREV); + ADD_INT(d, DB_PREV_NODUP); + ADD_INT(d, DB_RECORDCOUNT); + ADD_INT(d, DB_SET); + ADD_INT(d, DB_SET_RANGE); + ADD_INT(d, DB_SET_RECNO); + ADD_INT(d, DB_WRITECURSOR); + + ADD_INT(d, DB_OPFLAGS_MASK); + ADD_INT(d, DB_RMW); +#if (DBVER >= 33) + ADD_INT(d, DB_DIRTY_READ); + ADD_INT(d, DB_MULTIPLE); + ADD_INT(d, DB_MULTIPLE_KEY); +#endif + +#if (DBVER >= 33) + ADD_INT(d, DB_DONOTINDEX); +#endif + +#if (DBVER >= 41) + _addIntToDict(d, "DB_INCOMPLETE", 0); +#else + ADD_INT(d, DB_INCOMPLETE); +#endif + ADD_INT(d, DB_KEYEMPTY); + ADD_INT(d, DB_KEYEXIST); + ADD_INT(d, DB_LOCK_DEADLOCK); + ADD_INT(d, DB_LOCK_NOTGRANTED); + ADD_INT(d, DB_NOSERVER); + ADD_INT(d, DB_NOSERVER_HOME); + ADD_INT(d, DB_NOSERVER_ID); + ADD_INT(d, DB_NOTFOUND); + ADD_INT(d, DB_OLD_VERSION); + ADD_INT(d, DB_RUNRECOVERY); + ADD_INT(d, DB_VERIFY_BAD); +#if (DBVER >= 33) + ADD_INT(d, DB_PAGE_NOTFOUND); + ADD_INT(d, DB_SECONDARY_BAD); +#endif +#if (DBVER >= 40) + ADD_INT(d, DB_STAT_CLEAR); + ADD_INT(d, DB_REGION_INIT); + ADD_INT(d, DB_NOLOCKING); + ADD_INT(d, DB_YIELDCPU); + ADD_INT(d, DB_PANIC_ENVIRONMENT); + ADD_INT(d, DB_NOPANIC); +#endif + +#if (DBVER >= 42) + ADD_INT(d, DB_TIME_NOTGRANTED); + ADD_INT(d, DB_TXN_NOT_DURABLE); + ADD_INT(d, DB_TXN_WRITE_NOSYNC); + ADD_INT(d, DB_LOG_AUTOREMOVE); + ADD_INT(d, DB_DIRECT_LOG); + ADD_INT(d, DB_DIRECT_DB); + ADD_INT(d, DB_INIT_REP); + ADD_INT(d, DB_ENCRYPT); + ADD_INT(d, DB_CHKSUM); +#endif + +#if (DBVER >= 41) + ADD_INT(d, DB_ENCRYPT_AES); + ADD_INT(d, DB_AUTO_COMMIT); +#else + /* allow berkeleydb 4.1 aware apps to run on older versions */ + _addIntToDict(d, "DB_AUTO_COMMIT", 0); +#endif + + ADD_INT(d, EINVAL); + ADD_INT(d, EACCES); + ADD_INT(d, ENOSPC); + ADD_INT(d, ENOMEM); + ADD_INT(d, EAGAIN); + ADD_INT(d, EBUSY); + ADD_INT(d, EEXIST); + ADD_INT(d, ENOENT); + ADD_INT(d, EPERM); + +#if (DBVER >= 40) + ADD_INT(d, DB_SET_LOCK_TIMEOUT); + ADD_INT(d, DB_SET_TXN_TIMEOUT); +#endif + + /* The base exception class is DBError */ + DBError = PyErr_NewException("bsddb._db.DBError", NULL, NULL); + PyDict_SetItemString(d, "DBError", DBError); + + /* Some magic to make DBNotFoundError derive from both DBError and + KeyError, since the API only supports using one base class. */ + PyDict_SetItemString(d, "KeyError", PyExc_KeyError); + PyRun_String("class DBNotFoundError(DBError, KeyError): pass", + Py_file_input, d, d); + DBNotFoundError = PyDict_GetItemString(d, "DBNotFoundError"); + PyDict_DelItemString(d, "KeyError"); + + + /* All the rest of the exceptions derive only from DBError */ +#define MAKE_EX(name) name = PyErr_NewException("bsddb._db." #name, DBError, NULL); \ + PyDict_SetItemString(d, #name, name) + +#if !INCOMPLETE_IS_WARNING + MAKE_EX(DBIncompleteError); +#endif + MAKE_EX(DBKeyEmptyError); + MAKE_EX(DBKeyExistError); + MAKE_EX(DBLockDeadlockError); + MAKE_EX(DBLockNotGrantedError); + MAKE_EX(DBOldVersionError); + MAKE_EX(DBRunRecoveryError); + MAKE_EX(DBVerifyBadError); + MAKE_EX(DBNoServerError); + MAKE_EX(DBNoServerHomeError); + MAKE_EX(DBNoServerIDError); +#if (DBVER >= 33) + MAKE_EX(DBPageNotFoundError); + MAKE_EX(DBSecondaryBadError); +#endif + + MAKE_EX(DBInvalidArgError); + MAKE_EX(DBAccessError); + MAKE_EX(DBNoSpaceError); + MAKE_EX(DBNoMemoryError); + MAKE_EX(DBAgainError); + MAKE_EX(DBBusyError); + MAKE_EX(DBFileExistsError); + MAKE_EX(DBNoSuchFileError); + MAKE_EX(DBPermissionsError); + +#undef MAKE_EX + + /* Check for errors */ + if (PyErr_Occurred()) { + PyErr_Print(); + Py_FatalError("can't initialize module _bsddb"); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_codecsmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_codecsmodule.c new file mode 100644 index 00000000..5a45bafb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_codecsmodule.c @@ -0,0 +1,816 @@ +/* ------------------------------------------------------------------------ + + _codecs -- Provides access to the codec registry and the builtin + codecs. + + This module should never be imported directly. The standard library + module "codecs" wraps this builtin module for use within Python. + + The codec registry is accessible via: + + register(search_function) -> None + + lookup(encoding) -> (encoder, decoder, stream_reader, stream_writer) + + The builtin Unicode codecs use the following interface: + + _encode(Unicode_object[,errors='strict']) -> + (string object, bytes consumed) + + _decode(char_buffer_obj[,errors='strict']) -> + (Unicode object, bytes consumed) + + _encode() interfaces also accept non-Unicode object as + input. The objects are then converted to Unicode using + PyUnicode_FromObject() prior to applying the conversion. + + These s are available: utf_8, unicode_escape, + raw_unicode_escape, unicode_internal, latin_1, ascii (7-bit), + mbcs (on win32). + + +Written by Marc-Andre Lemburg (mal@lemburg.com). + +Copyright (c) Corporation for National Research Initiatives. + + ------------------------------------------------------------------------ */ + +#include "Python.h" + +/* --- Registry ----------------------------------------------------------- */ + +PyDoc_STRVAR(register__doc__, +"register(search_function)\n\ +\n\ +Register a codec search function. Search functions are expected to take\n\ +one argument, the encoding name in all lower case letters, and return\n\ +a tuple of functions (encoder, decoder, stream_reader, stream_writer)."); + +static +PyObject *codecregister(PyObject *self, PyObject *args) +{ + PyObject *search_function; + + if (!PyArg_ParseTuple(args, "O:register", &search_function)) + goto onError; + + if (PyCodec_Register(search_function)) + goto onError; + + Py_INCREF(Py_None); + return Py_None; + + onError: + return NULL; +} + +PyDoc_STRVAR(lookup__doc__, +"lookup(encoding) -> (encoder, decoder, stream_reader, stream_writer)\n\ +\n\ +Looks up a codec tuple in the Python codec registry and returns\n\ +a tuple of functions."); + +static +PyObject *codeclookup(PyObject *self, PyObject *args) +{ + char *encoding; + + if (!PyArg_ParseTuple(args, "s:lookup", &encoding)) + goto onError; + + return _PyCodec_Lookup(encoding); + + onError: + return NULL; +} + +/* --- Helpers ------------------------------------------------------------ */ + +static +PyObject *codec_tuple(PyObject *unicode, + int len) +{ + PyObject *v,*w; + + if (unicode == NULL) + return NULL; + v = PyTuple_New(2); + if (v == NULL) { + Py_DECREF(unicode); + return NULL; + } + PyTuple_SET_ITEM(v,0,unicode); + w = PyInt_FromLong(len); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyTuple_SET_ITEM(v,1,w); + return v; +} + +/* --- String codecs ------------------------------------------------------ */ +static PyObject * +escape_decode(PyObject *self, + PyObject *args) +{ + const char *errors = NULL; + const char *data; + int size; + + if (!PyArg_ParseTuple(args, "s#|z:escape_decode", + &data, &size, &errors)) + return NULL; + return codec_tuple(PyString_DecodeEscape(data, size, errors, 0, NULL), + size); +} + +static PyObject * +escape_encode(PyObject *self, + PyObject *args) +{ + PyObject *str; + const char *errors = NULL; + char *buf; + int len; + + if (!PyArg_ParseTuple(args, "O!|z:escape_encode", + &PyString_Type, &str, &errors)) + return NULL; + + str = PyString_Repr(str, 0); + if (!str) + return NULL; + + /* The string will be quoted. Unquote, similar to unicode-escape. */ + buf = PyString_AS_STRING (str); + len = PyString_GET_SIZE (str); + memmove(buf, buf+1, len-2); + _PyString_Resize(&str, len-2); + + return codec_tuple(str, PyString_Size(str)); +} + +#ifdef Py_USING_UNICODE +/* --- Decoder ------------------------------------------------------------ */ + +static PyObject * +unicode_internal_decode(PyObject *self, + PyObject *args) +{ + PyObject *obj; + const char *errors = NULL; + const char *data; + int size; + + if (!PyArg_ParseTuple(args, "O|z:unicode_internal_decode", + &obj, &errors)) + return NULL; + + if (PyUnicode_Check(obj)) { + Py_INCREF(obj); + return codec_tuple(obj, PyUnicode_GET_SIZE(obj)); + } + else { + if (PyObject_AsReadBuffer(obj, (const void **)&data, &size)) + return NULL; + return codec_tuple(PyUnicode_FromUnicode((Py_UNICODE *)data, + size / sizeof(Py_UNICODE)), + size); + } +} + +static PyObject * +utf_7_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "t#|z:utf_7_decode", + &data, &size, &errors)) + return NULL; + + return codec_tuple(PyUnicode_DecodeUTF7(data, size, errors), + size); +} + +static PyObject * +utf_8_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "t#|z:utf_8_decode", + &data, &size, &errors)) + return NULL; + + return codec_tuple(PyUnicode_DecodeUTF8(data, size, errors), + size); +} + +static PyObject * +utf_16_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + int byteorder = 0; + + if (!PyArg_ParseTuple(args, "t#|z:utf_16_decode", + &data, &size, &errors)) + return NULL; + return codec_tuple(PyUnicode_DecodeUTF16(data, size, errors, &byteorder), + size); +} + +static PyObject * +utf_16_le_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + int byteorder = -1; + + if (!PyArg_ParseTuple(args, "t#|z:utf_16_le_decode", + &data, &size, &errors)) + return NULL; + return codec_tuple(PyUnicode_DecodeUTF16(data, size, errors, &byteorder), + size); +} + +static PyObject * +utf_16_be_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + int byteorder = 1; + + if (!PyArg_ParseTuple(args, "t#|z:utf_16_be_decode", + &data, &size, &errors)) + return NULL; + return codec_tuple(PyUnicode_DecodeUTF16(data, size, errors, &byteorder), + size); +} + +/* This non-standard version also provides access to the byteorder + parameter of the builtin UTF-16 codec. + + It returns a tuple (unicode, bytesread, byteorder) with byteorder + being the value in effect at the end of data. + +*/ + +static PyObject * +utf_16_ex_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + int byteorder = 0; + PyObject *unicode, *tuple; + + if (!PyArg_ParseTuple(args, "t#|zi:utf_16_ex_decode", + &data, &size, &errors, &byteorder)) + return NULL; + + unicode = PyUnicode_DecodeUTF16(data, size, errors, &byteorder); + if (unicode == NULL) + return NULL; + tuple = Py_BuildValue("Oii", unicode, size, byteorder); + Py_DECREF(unicode); + return tuple; +} + +static PyObject * +unicode_escape_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "t#|z:unicode_escape_decode", + &data, &size, &errors)) + return NULL; + + return codec_tuple(PyUnicode_DecodeUnicodeEscape(data, size, errors), + size); +} + +static PyObject * +raw_unicode_escape_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "t#|z:raw_unicode_escape_decode", + &data, &size, &errors)) + return NULL; + + return codec_tuple(PyUnicode_DecodeRawUnicodeEscape(data, size, errors), + size); +} + +static PyObject * +latin_1_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "t#|z:latin_1_decode", + &data, &size, &errors)) + return NULL; + + return codec_tuple(PyUnicode_DecodeLatin1(data, size, errors), + size); +} + +static PyObject * +ascii_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "t#|z:ascii_decode", + &data, &size, &errors)) + return NULL; + + return codec_tuple(PyUnicode_DecodeASCII(data, size, errors), + size); +} + +static PyObject * +charmap_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + PyObject *mapping = NULL; + + if (!PyArg_ParseTuple(args, "t#|zO:charmap_decode", + &data, &size, &errors, &mapping)) + return NULL; + if (mapping == Py_None) + mapping = NULL; + + return codec_tuple(PyUnicode_DecodeCharmap(data, size, mapping, errors), + size); +} + +#if defined(MS_WINDOWS) && defined(HAVE_USABLE_WCHAR_T) + +static PyObject * +mbcs_decode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "t#|z:mbcs_decode", + &data, &size, &errors)) + return NULL; + + return codec_tuple(PyUnicode_DecodeMBCS(data, size, errors), + size); +} + +#endif /* MS_WINDOWS */ + +/* --- Encoder ------------------------------------------------------------ */ + +static PyObject * +readbuffer_encode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "s#|z:readbuffer_encode", + &data, &size, &errors)) + return NULL; + + return codec_tuple(PyString_FromStringAndSize(data, size), + size); +} + +static PyObject * +charbuffer_encode(PyObject *self, + PyObject *args) +{ + const char *data; + int size; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "t#|z:charbuffer_encode", + &data, &size, &errors)) + return NULL; + + return codec_tuple(PyString_FromStringAndSize(data, size), + size); +} + +static PyObject * +unicode_internal_encode(PyObject *self, + PyObject *args) +{ + PyObject *obj; + const char *errors = NULL; + const char *data; + int size; + + if (!PyArg_ParseTuple(args, "O|z:unicode_internal_encode", + &obj, &errors)) + return NULL; + + if (PyUnicode_Check(obj)) { + data = PyUnicode_AS_DATA(obj); + size = PyUnicode_GET_DATA_SIZE(obj); + return codec_tuple(PyString_FromStringAndSize(data, size), + size); + } + else { + if (PyObject_AsReadBuffer(obj, (const void **)&data, &size)) + return NULL; + return codec_tuple(PyString_FromStringAndSize(data, size), + size); + } +} + +static PyObject * +utf_7_encode(PyObject *self, + PyObject *args) +{ + PyObject *str, *v; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "O|z:utf_7_encode", + &str, &errors)) + return NULL; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return NULL; + v = codec_tuple(PyUnicode_EncodeUTF7(PyUnicode_AS_UNICODE(str), + PyUnicode_GET_SIZE(str), + 0, + 0, + errors), + PyUnicode_GET_SIZE(str)); + Py_DECREF(str); + return v; +} + +static PyObject * +utf_8_encode(PyObject *self, + PyObject *args) +{ + PyObject *str, *v; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "O|z:utf_8_encode", + &str, &errors)) + return NULL; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return NULL; + v = codec_tuple(PyUnicode_EncodeUTF8(PyUnicode_AS_UNICODE(str), + PyUnicode_GET_SIZE(str), + errors), + PyUnicode_GET_SIZE(str)); + Py_DECREF(str); + return v; +} + +/* This version provides access to the byteorder parameter of the + builtin UTF-16 codecs as optional third argument. It defaults to 0 + which means: use the native byte order and prepend the data with a + BOM mark. + +*/ + +static PyObject * +utf_16_encode(PyObject *self, + PyObject *args) +{ + PyObject *str, *v; + const char *errors = NULL; + int byteorder = 0; + + if (!PyArg_ParseTuple(args, "O|zi:utf_16_encode", + &str, &errors, &byteorder)) + return NULL; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return NULL; + v = codec_tuple(PyUnicode_EncodeUTF16(PyUnicode_AS_UNICODE(str), + PyUnicode_GET_SIZE(str), + errors, + byteorder), + PyUnicode_GET_SIZE(str)); + Py_DECREF(str); + return v; +} + +static PyObject * +utf_16_le_encode(PyObject *self, + PyObject *args) +{ + PyObject *str, *v; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "O|z:utf_16_le_encode", + &str, &errors)) + return NULL; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return NULL; + v = codec_tuple(PyUnicode_EncodeUTF16(PyUnicode_AS_UNICODE(str), + PyUnicode_GET_SIZE(str), + errors, + -1), + PyUnicode_GET_SIZE(str)); + Py_DECREF(str); + return v; +} + +static PyObject * +utf_16_be_encode(PyObject *self, + PyObject *args) +{ + PyObject *str, *v; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "O|z:utf_16_be_encode", + &str, &errors)) + return NULL; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return NULL; + v = codec_tuple(PyUnicode_EncodeUTF16(PyUnicode_AS_UNICODE(str), + PyUnicode_GET_SIZE(str), + errors, + +1), + PyUnicode_GET_SIZE(str)); + Py_DECREF(str); + return v; +} + +static PyObject * +unicode_escape_encode(PyObject *self, + PyObject *args) +{ + PyObject *str, *v; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "O|z:unicode_escape_encode", + &str, &errors)) + return NULL; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return NULL; + v = codec_tuple(PyUnicode_EncodeUnicodeEscape(PyUnicode_AS_UNICODE(str), + PyUnicode_GET_SIZE(str)), + PyUnicode_GET_SIZE(str)); + Py_DECREF(str); + return v; +} + +static PyObject * +raw_unicode_escape_encode(PyObject *self, + PyObject *args) +{ + PyObject *str, *v; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "O|z:raw_unicode_escape_encode", + &str, &errors)) + return NULL; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return NULL; + v = codec_tuple(PyUnicode_EncodeRawUnicodeEscape( + PyUnicode_AS_UNICODE(str), + PyUnicode_GET_SIZE(str)), + PyUnicode_GET_SIZE(str)); + Py_DECREF(str); + return v; +} + +static PyObject * +latin_1_encode(PyObject *self, + PyObject *args) +{ + PyObject *str, *v; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "O|z:latin_1_encode", + &str, &errors)) + return NULL; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return NULL; + v = codec_tuple(PyUnicode_EncodeLatin1( + PyUnicode_AS_UNICODE(str), + PyUnicode_GET_SIZE(str), + errors), + PyUnicode_GET_SIZE(str)); + Py_DECREF(str); + return v; +} + +static PyObject * +ascii_encode(PyObject *self, + PyObject *args) +{ + PyObject *str, *v; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "O|z:ascii_encode", + &str, &errors)) + return NULL; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return NULL; + v = codec_tuple(PyUnicode_EncodeASCII( + PyUnicode_AS_UNICODE(str), + PyUnicode_GET_SIZE(str), + errors), + PyUnicode_GET_SIZE(str)); + Py_DECREF(str); + return v; +} + +static PyObject * +charmap_encode(PyObject *self, + PyObject *args) +{ + PyObject *str, *v; + const char *errors = NULL; + PyObject *mapping = NULL; + + if (!PyArg_ParseTuple(args, "O|zO:charmap_encode", + &str, &errors, &mapping)) + return NULL; + if (mapping == Py_None) + mapping = NULL; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return NULL; + v = codec_tuple(PyUnicode_EncodeCharmap( + PyUnicode_AS_UNICODE(str), + PyUnicode_GET_SIZE(str), + mapping, + errors), + PyUnicode_GET_SIZE(str)); + Py_DECREF(str); + return v; +} + +#if defined(MS_WINDOWS) && defined(HAVE_USABLE_WCHAR_T) + +static PyObject * +mbcs_encode(PyObject *self, + PyObject *args) +{ + PyObject *str, *v; + const char *errors = NULL; + + if (!PyArg_ParseTuple(args, "O|z:mbcs_encode", + &str, &errors)) + return NULL; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return NULL; + v = codec_tuple(PyUnicode_EncodeMBCS( + PyUnicode_AS_UNICODE(str), + PyUnicode_GET_SIZE(str), + errors), + PyUnicode_GET_SIZE(str)); + Py_DECREF(str); + return v; +} + +#endif /* MS_WINDOWS */ +#endif /* Py_USING_UNICODE */ + +/* --- Error handler registry --------------------------------------------- */ + +PyDoc_STRVAR(register_error__doc__, +"register_error(errors, handler)\n\ +\n\ +Register the specified error handler under the name\n\ +errors. handler must be a callable object, that\n\ +will be called with an exception instance containing\n\ +information about the location of the encoding/decoding\n\ +error and must return a (replacement, new position) tuple."); + +static PyObject *register_error(PyObject *self, PyObject *args) +{ + const char *name; + PyObject *handler; + + if (!PyArg_ParseTuple(args, "sO:register_error", + &name, &handler)) + return NULL; + if (PyCodec_RegisterError(name, handler)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(lookup_error__doc__, +"lookup_error(errors) -> handler\n\ +\n\ +Return the error handler for the specified error handling name\n\ +or raise a LookupError, if no handler exists under this name."); + +static PyObject *lookup_error(PyObject *self, PyObject *args) +{ + const char *name; + + if (!PyArg_ParseTuple(args, "s:lookup_error", + &name)) + return NULL; + return PyCodec_LookupError(name); +} + +/* --- Module API --------------------------------------------------------- */ + +static PyMethodDef _codecs_functions[] = { + {"register", codecregister, METH_VARARGS, + register__doc__}, + {"lookup", codeclookup, METH_VARARGS, + lookup__doc__}, + {"escape_encode", escape_encode, METH_VARARGS}, + {"escape_decode", escape_decode, METH_VARARGS}, +#ifdef Py_USING_UNICODE + {"utf_8_encode", utf_8_encode, METH_VARARGS}, + {"utf_8_decode", utf_8_decode, METH_VARARGS}, + {"utf_7_encode", utf_7_encode, METH_VARARGS}, + {"utf_7_decode", utf_7_decode, METH_VARARGS}, + {"utf_16_encode", utf_16_encode, METH_VARARGS}, + {"utf_16_le_encode", utf_16_le_encode, METH_VARARGS}, + {"utf_16_be_encode", utf_16_be_encode, METH_VARARGS}, + {"utf_16_decode", utf_16_decode, METH_VARARGS}, + {"utf_16_le_decode", utf_16_le_decode, METH_VARARGS}, + {"utf_16_be_decode", utf_16_be_decode, METH_VARARGS}, + {"utf_16_ex_decode", utf_16_ex_decode, METH_VARARGS}, + {"unicode_escape_encode", unicode_escape_encode, METH_VARARGS}, + {"unicode_escape_decode", unicode_escape_decode, METH_VARARGS}, + {"unicode_internal_encode", unicode_internal_encode, METH_VARARGS}, + {"unicode_internal_decode", unicode_internal_decode, METH_VARARGS}, + {"raw_unicode_escape_encode", raw_unicode_escape_encode, METH_VARARGS}, + {"raw_unicode_escape_decode", raw_unicode_escape_decode, METH_VARARGS}, + {"latin_1_encode", latin_1_encode, METH_VARARGS}, + {"latin_1_decode", latin_1_decode, METH_VARARGS}, + {"ascii_encode", ascii_encode, METH_VARARGS}, + {"ascii_decode", ascii_decode, METH_VARARGS}, + {"charmap_encode", charmap_encode, METH_VARARGS}, + {"charmap_decode", charmap_decode, METH_VARARGS}, + {"readbuffer_encode", readbuffer_encode, METH_VARARGS}, + {"charbuffer_encode", charbuffer_encode, METH_VARARGS}, +#if defined(MS_WINDOWS) && defined(HAVE_USABLE_WCHAR_T) + {"mbcs_encode", mbcs_encode, METH_VARARGS}, + {"mbcs_decode", mbcs_decode, METH_VARARGS}, +#endif +#endif /* Py_USING_UNICODE */ + {"register_error", register_error, METH_VARARGS, + register_error__doc__}, + {"lookup_error", lookup_error, METH_VARARGS, + lookup_error__doc__}, + {NULL, NULL} /* sentinel */ +}; + +PyMODINIT_FUNC +init_codecs(void) +{ + Py_InitModule("_codecs", _codecs_functions); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_csv.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_csv.c new file mode 100644 index 00000000..0462ef2e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_csv.c @@ -0,0 +1,1556 @@ +/* csv module */ + +/* + +This module provides the low-level underpinnings of a CSV reading/writing +module. Users should not use this module directly, but import the csv.py +module instead. + +**** For people modifying this code, please note that as of this writing +**** (2003-03-23), it is intended that this code should work with Python +**** 2.2. + +*/ + +#define MODULE_VERSION "1.0" + +#include "Python.h" +#include "structmember.h" + + +/* begin 2.2 compatibility macros */ +#ifndef PyDoc_STRVAR +/* Define macros for inline documentation. */ +#define PyDoc_VAR(name) static char name[] +#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str) +#ifdef WITH_DOC_STRINGS +#define PyDoc_STR(str) str +#else +#define PyDoc_STR(str) "" +#endif +#endif /* ifndef PyDoc_STRVAR */ + +#ifndef PyMODINIT_FUNC +# if defined(__cplusplus) +# define PyMODINIT_FUNC extern "C" void +# else /* __cplusplus */ +# define PyMODINIT_FUNC void +# endif /* __cplusplus */ +#endif +/* end 2.2 compatibility macros */ + +static PyObject *error_obj; /* CSV exception */ +static PyObject *dialects; /* Dialect registry */ + +typedef enum { + START_RECORD, START_FIELD, ESCAPED_CHAR, IN_FIELD, + IN_QUOTED_FIELD, ESCAPE_IN_QUOTED_FIELD, QUOTE_IN_QUOTED_FIELD +} ParserState; + +typedef enum { + QUOTE_MINIMAL, QUOTE_ALL, QUOTE_NONNUMERIC, QUOTE_NONE +} QuoteStyle; + +typedef struct { + QuoteStyle style; + char *name; +} StyleDesc; + +static StyleDesc quote_styles[] = { + { QUOTE_MINIMAL, "QUOTE_MINIMAL" }, + { QUOTE_ALL, "QUOTE_ALL" }, + { QUOTE_NONNUMERIC, "QUOTE_NONNUMERIC" }, + { QUOTE_NONE, "QUOTE_NONE" }, + { 0 } +}; + +typedef struct { + PyObject_HEAD + + int doublequote; /* is " represented by ""? */ + char delimiter; /* field separator */ + char quotechar; /* quote character */ + char escapechar; /* escape character */ + int skipinitialspace; /* ignore spaces following delimiter? */ + PyObject *lineterminator; /* string to write between records */ + QuoteStyle quoting; /* style of quoting to write */ + + int strict; /* raise exception on bad CSV */ +} DialectObj; + +staticforward PyTypeObject Dialect_Type; + +typedef struct { + PyObject_HEAD + + PyObject *input_iter; /* iterate over this for input lines */ + + DialectObj *dialect; /* parsing dialect */ + + PyObject *fields; /* field list for current record */ + ParserState state; /* current CSV parse state */ + char *field; /* build current field in here */ + int field_size; /* size of allocated buffer */ + int field_len; /* length of current field */ + int had_parse_error; /* did we have a parse error? */ +} ReaderObj; + +staticforward PyTypeObject Reader_Type; + +#define ReaderObject_Check(v) ((v)->ob_type == &Reader_Type) + +typedef struct { + PyObject_HEAD + + PyObject *writeline; /* write output lines to this file */ + + DialectObj *dialect; /* parsing dialect */ + + char *rec; /* buffer for parser.join */ + int rec_size; /* size of allocated record */ + int rec_len; /* length of record */ + int num_fields; /* number of fields in record */ +} WriterObj; + +staticforward PyTypeObject Writer_Type; + +/* + * DIALECT class + */ + +static PyObject * +get_dialect_from_registry(PyObject * name_obj) +{ + PyObject *dialect_obj; + + dialect_obj = PyDict_GetItem(dialects, name_obj); + if (dialect_obj == NULL) + return PyErr_Format(error_obj, "unknown dialect"); + Py_INCREF(dialect_obj); + return dialect_obj; +} + +static int +check_delattr(PyObject *v) +{ + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, + "Cannot delete attribute"); + return -1; + } + return 0; +} + +static PyObject * +get_string(PyObject *str) +{ + Py_XINCREF(str); + return str; +} + +static int +set_string(PyObject **str, PyObject *v) +{ + if (check_delattr(v) < 0) + return -1; + if (!PyString_Check(v) +#ifdef Py_USING_UNICODE +&& !PyUnicode_Check(v) +#endif +) { + PyErr_BadArgument(); + return -1; + } + Py_XDECREF(*str); + Py_INCREF(v); + *str = v; + return 0; +} + +static PyObject * +get_nullchar_as_None(char c) +{ + if (c == '\0') { + Py_INCREF(Py_None); + return Py_None; + } + else + return PyString_FromStringAndSize((char*)&c, 1); +} + +static int +set_None_as_nullchar(char * addr, PyObject *v) +{ + if (check_delattr(v) < 0) + return -1; + if (v == Py_None) + *addr = '\0'; + else if (!PyString_Check(v) || PyString_Size(v) != 1) { + PyErr_BadArgument(); + return -1; + } + else { + char *s = PyString_AsString(v); + if (s == NULL) + return -1; + *addr = s[0]; + } + return 0; +} + +static PyObject * +Dialect_get_lineterminator(DialectObj *self) +{ + return get_string(self->lineterminator); +} + +static int +Dialect_set_lineterminator(DialectObj *self, PyObject *value) +{ + return set_string(&self->lineterminator, value); +} + +static PyObject * +Dialect_get_escapechar(DialectObj *self) +{ + return get_nullchar_as_None(self->escapechar); +} + +static int +Dialect_set_escapechar(DialectObj *self, PyObject *value) +{ + return set_None_as_nullchar(&self->escapechar, value); +} + +static PyObject * +Dialect_get_quoting(DialectObj *self) +{ + return PyInt_FromLong(self->quoting); +} + +static int +Dialect_set_quoting(DialectObj *self, PyObject *v) +{ + int quoting; + StyleDesc *qs = quote_styles; + + if (check_delattr(v) < 0) + return -1; + if (!PyInt_Check(v)) { + PyErr_BadArgument(); + return -1; + } + quoting = PyInt_AsLong(v); + for (qs = quote_styles; qs->name; qs++) { + if (qs->style == quoting) { + self->quoting = quoting; + return 0; + } + } + PyErr_BadArgument(); + return -1; +} + +static struct PyMethodDef Dialect_methods[] = { + { NULL, NULL } +}; + +#define D_OFF(x) offsetof(DialectObj, x) + +static struct PyMemberDef Dialect_memberlist[] = { + { "quotechar", T_CHAR, D_OFF(quotechar) }, + { "delimiter", T_CHAR, D_OFF(delimiter) }, + { "skipinitialspace", T_INT, D_OFF(skipinitialspace) }, + { "doublequote", T_INT, D_OFF(doublequote) }, + { "strict", T_INT, D_OFF(strict) }, + { NULL } +}; + +static PyGetSetDef Dialect_getsetlist[] = { + { "escapechar", (getter)Dialect_get_escapechar, + (setter)Dialect_set_escapechar }, + { "lineterminator", (getter)Dialect_get_lineterminator, + (setter)Dialect_set_lineterminator }, + { "quoting", (getter)Dialect_get_quoting, + (setter)Dialect_set_quoting }, + {NULL}, +}; + +static void +Dialect_dealloc(DialectObj *self) +{ + Py_XDECREF(self->lineterminator); + self->ob_type->tp_free((PyObject *)self); +} + +static int +dialect_init(DialectObj * self, PyObject * args, PyObject * kwargs) +{ + PyObject *dialect = NULL, *name_obj, *value_obj; + + self->quotechar = '"'; + self->delimiter = ','; + self->escapechar = '\0'; + self->skipinitialspace = 0; + Py_XDECREF(self->lineterminator); + self->lineterminator = PyString_FromString("\r\n"); + if (self->lineterminator == NULL) + return -1; + self->quoting = QUOTE_MINIMAL; + self->doublequote = 1; + self->strict = 0; + + if (!PyArg_ParseTuple(args, "|O", &dialect)) + return -1; + Py_XINCREF(dialect); + if (kwargs != NULL) { + PyObject * key = PyString_FromString("dialect"); + PyObject * d; + + d = PyDict_GetItem(kwargs, key); + if (d) { + Py_INCREF(d); + Py_XDECREF(dialect); + PyDict_DelItem(kwargs, key); + dialect = d; + } + Py_DECREF(key); + } + if (dialect != NULL) { + int i; + PyObject * dir_list; + + /* If dialect is a string, look it up in our registry */ + if (PyString_Check(dialect) +#ifdef Py_USING_UNICODE + || PyUnicode_Check(dialect) +#endif + ) { + PyObject * new_dia; + new_dia = get_dialect_from_registry(dialect); + Py_DECREF(dialect); + if (new_dia == NULL) + return -1; + dialect = new_dia; + } + /* A class rather than an instance? Instantiate */ + if (PyObject_TypeCheck(dialect, &PyClass_Type)) { + PyObject * new_dia; + new_dia = PyObject_CallFunction(dialect, ""); + Py_DECREF(dialect); + if (new_dia == NULL) + return -1; + dialect = new_dia; + } + /* Make sure we finally have an instance */ + if (!PyInstance_Check(dialect) || + (dir_list = PyObject_Dir(dialect)) == NULL) { + PyErr_SetString(PyExc_TypeError, + "dialect must be an instance"); + Py_DECREF(dialect); + return -1; + } + /* And extract the attributes */ + for (i = 0; i < PyList_GET_SIZE(dir_list); ++i) { + char *s; + name_obj = PyList_GET_ITEM(dir_list, i); + s = PyString_AsString(name_obj); + if (s == NULL) + return -1; + if (s[0] == '_') + continue; + value_obj = PyObject_GetAttr(dialect, name_obj); + if (value_obj) { + if (PyObject_SetAttr((PyObject *)self, + name_obj, value_obj)) { + Py_DECREF(value_obj); + Py_DECREF(dir_list); + Py_DECREF(dialect); + return -1; + } + Py_DECREF(value_obj); + } + } + Py_DECREF(dir_list); + Py_DECREF(dialect); + } + if (kwargs != NULL) { + int pos = 0; + + while (PyDict_Next(kwargs, &pos, &name_obj, &value_obj)) { + if (PyObject_SetAttr((PyObject *)self, + name_obj, value_obj)) + return -1; + } + } + return 0; +} + +static PyObject * +dialect_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + DialectObj *self; + self = (DialectObj *)type->tp_alloc(type, 0); + if (self != NULL) { + self->lineterminator = NULL; + } + return (PyObject *)self; +} + + +PyDoc_STRVAR(Dialect_Type_doc, +"CSV dialect\n" +"\n" +"The Dialect type records CSV parsing and generation options.\n"); + +static PyTypeObject Dialect_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "_csv.Dialect", /* tp_name */ + sizeof(DialectObj), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)Dialect_dealloc, /* tp_dealloc */ + (printfunc)0, /* tp_print */ + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ + (cmpfunc)0, /* tp_compare */ + (reprfunc)0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Dialect_Type_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Dialect_methods, /* tp_methods */ + Dialect_memberlist, /* tp_members */ + Dialect_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)dialect_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + dialect_new, /* tp_new */ + 0, /* tp_free */ +}; + +static void +parse_save_field(ReaderObj *self) +{ + PyObject *field; + + field = PyString_FromStringAndSize(self->field, self->field_len); + if (field != NULL) { + PyList_Append(self->fields, field); + Py_XDECREF(field); + } + self->field_len = 0; +} + +static int +parse_grow_buff(ReaderObj *self) +{ + if (self->field_size == 0) { + self->field_size = 4096; + if (self->field != NULL) + PyMem_Free(self->field); + self->field = PyMem_Malloc(self->field_size); + } + else { + self->field_size *= 2; + self->field = PyMem_Realloc(self->field, self->field_size); + } + if (self->field == NULL) { + PyErr_NoMemory(); + return 0; + } + return 1; +} + +static void +parse_add_char(ReaderObj *self, char c) +{ + if (self->field_len == self->field_size && !parse_grow_buff(self)) + return; + self->field[self->field_len++] = c; +} + +static void +parse_process_char(ReaderObj *self, char c) +{ + DialectObj *dialect = self->dialect; + + switch (self->state) { + case START_RECORD: + /* start of record */ + if (c == '\n') + /* empty line - return [] */ + break; + /* normal character - handle as START_FIELD */ + self->state = START_FIELD; + /* fallthru */ + case START_FIELD: + /* expecting field */ + if (c == '\n') { + /* save empty field - return [fields] */ + parse_save_field(self); + self->state = START_RECORD; + } + else if (c == dialect->quotechar) { + /* start quoted field */ + self->state = IN_QUOTED_FIELD; + } + else if (c == dialect->escapechar) { + /* possible escaped character */ + self->state = ESCAPED_CHAR; + } + else if (c == ' ' && dialect->skipinitialspace) + /* ignore space at start of field */ + ; + else if (c == dialect->delimiter) { + /* save empty field */ + parse_save_field(self); + } + else { + /* begin new unquoted field */ + parse_add_char(self, c); + self->state = IN_FIELD; + } + break; + + case ESCAPED_CHAR: + if (c != dialect->escapechar && + c != dialect->delimiter && + c != dialect->quotechar) + parse_add_char(self, dialect->escapechar); + parse_add_char(self, c); + self->state = IN_FIELD; + break; + + case IN_FIELD: + /* in unquoted field */ + if (c == '\n') { + /* end of line - return [fields] */ + parse_save_field(self); + self->state = START_RECORD; + } + else if (c == dialect->escapechar) { + /* possible escaped character */ + self->state = ESCAPED_CHAR; + } + else if (c == dialect->delimiter) { + /* save field - wait for new field */ + parse_save_field(self); + self->state = START_FIELD; + } + else { + /* normal character - save in field */ + parse_add_char(self, c); + } + break; + + case IN_QUOTED_FIELD: + /* in quoted field */ + if (c == '\n') { + /* end of line - save '\n' in field */ + parse_add_char(self, '\n'); + } + else if (c == dialect->escapechar) { + /* Possible escape character */ + self->state = ESCAPE_IN_QUOTED_FIELD; + } + else if (c == dialect->quotechar) { + if (dialect->doublequote) { + /* doublequote; " represented by "" */ + self->state = QUOTE_IN_QUOTED_FIELD; + } + else { + /* end of quote part of field */ + self->state = IN_FIELD; + } + } + else { + /* normal character - save in field */ + parse_add_char(self, c); + } + break; + + case ESCAPE_IN_QUOTED_FIELD: + if (c != dialect->escapechar && + c != dialect->delimiter && + c != dialect->quotechar) + parse_add_char(self, dialect->escapechar); + parse_add_char(self, c); + self->state = IN_QUOTED_FIELD; + break; + + case QUOTE_IN_QUOTED_FIELD: + /* doublequote - seen a quote in an quoted field */ + if (dialect->quoting != QUOTE_NONE && + c == dialect->quotechar) { + /* save "" as " */ + parse_add_char(self, c); + self->state = IN_QUOTED_FIELD; + } + else if (c == dialect->delimiter) { + /* save field - wait for new field */ + parse_save_field(self); + self->state = START_FIELD; + } + else if (c == '\n') { + /* end of line - return [fields] */ + parse_save_field(self); + self->state = START_RECORD; + } + else if (!dialect->strict) { + parse_add_char(self, c); + self->state = IN_FIELD; + } + else { + /* illegal */ + self->had_parse_error = 1; + PyErr_Format(error_obj, "%c expected after %c", + dialect->delimiter, + dialect->quotechar); + } + break; + + } +} + +/* + * READER + */ +#define R_OFF(x) offsetof(ReaderObj, x) + +static struct PyMemberDef Reader_memberlist[] = { + { "dialect", T_OBJECT, R_OFF(dialect), RO }, + { NULL } +}; + +static PyObject * +Reader_getiter(ReaderObj *self) +{ + Py_INCREF(self); + return (PyObject *)self; +} + +static PyObject * +Reader_iternext(ReaderObj *self) +{ + PyObject *lineobj; + PyObject *fields; + char *line; + + do { + lineobj = PyIter_Next(self->input_iter); + if (lineobj == NULL) { + /* End of input OR exception */ + if (!PyErr_Occurred() && self->field_len != 0) + return PyErr_Format(error_obj, + "newline inside string"); + return NULL; + } + + if (self->had_parse_error) { + if (self->fields) { + Py_XDECREF(self->fields); + } + self->fields = PyList_New(0); + self->field_len = 0; + self->state = START_RECORD; + self->had_parse_error = 0; + } + line = PyString_AsString(lineobj); + + if (line == NULL) { + Py_DECREF(lineobj); + return NULL; + } + if (strlen(line) < (size_t)PyString_GET_SIZE(lineobj)) { + self->had_parse_error = 1; + Py_DECREF(lineobj); + return PyErr_Format(error_obj, + "string with NUL bytes"); + } + + /* Process line of text - send '\n' to processing code to + represent end of line. End of line which is not at end of + string is an error. */ + while (*line) { + char c; + + c = *line++; + if (c == '\r') { + c = *line++; + if (c == '\0') + /* macintosh end of line */ + break; + if (c == '\n') { + c = *line++; + if (c == '\0') + /* DOS end of line */ + break; + } + self->had_parse_error = 1; + Py_DECREF(lineobj); + return PyErr_Format(error_obj, + "newline inside string"); + } + if (c == '\n') { + c = *line++; + if (c == '\0') + /* unix end of line */ + break; + self->had_parse_error = 1; + Py_DECREF(lineobj); + return PyErr_Format(error_obj, + "newline inside string"); + } + parse_process_char(self, c); + if (PyErr_Occurred()) { + Py_DECREF(lineobj); + return NULL; + } + } + parse_process_char(self, '\n'); + Py_DECREF(lineobj); + } while (self->state != START_RECORD); + + fields = self->fields; + self->fields = PyList_New(0); + return fields; +} + +static void +Reader_dealloc(ReaderObj *self) +{ + Py_XDECREF(self->dialect); + Py_XDECREF(self->input_iter); + Py_XDECREF(self->fields); + if (self->field != NULL) + PyMem_Free(self->field); + PyObject_GC_Del(self); +} + +static int +Reader_traverse(ReaderObj *self, visitproc visit, void *arg) +{ + int err; +#define VISIT(SLOT) \ + if (SLOT) { \ + err = visit((PyObject *)(SLOT), arg); \ + if (err) \ + return err; \ + } + VISIT(self->dialect); + VISIT(self->input_iter); + VISIT(self->fields); + return 0; +} + +static int +Reader_clear(ReaderObj *self) +{ + Py_XDECREF(self->dialect); + Py_XDECREF(self->input_iter); + Py_XDECREF(self->fields); + self->dialect = NULL; + self->input_iter = NULL; + self->fields = NULL; + return 0; +} + +PyDoc_STRVAR(Reader_Type_doc, +"CSV reader\n" +"\n" +"Reader objects are responsible for reading and parsing tabular data\n" +"in CSV format.\n" +); + +static struct PyMethodDef Reader_methods[] = { + { NULL, NULL } +}; + +static PyTypeObject Reader_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_csv.reader", /*tp_name*/ + sizeof(ReaderObj), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)Reader_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)0, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + Reader_Type_doc, /*tp_doc*/ + (traverseproc)Reader_traverse, /*tp_traverse*/ + (inquiry)Reader_clear, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + (getiterfunc)Reader_getiter, /*tp_iter*/ + (getiterfunc)Reader_iternext, /*tp_iternext*/ + Reader_methods, /*tp_methods*/ + Reader_memberlist, /*tp_members*/ + 0, /*tp_getset*/ + +}; + +static PyObject * +csv_reader(PyObject *module, PyObject *args, PyObject *keyword_args) +{ + PyObject * iterator, * dialect = NULL, *ctor_args; + ReaderObj * self = PyObject_GC_New(ReaderObj, &Reader_Type); + + if (!self) + return NULL; + + self->dialect = NULL; + self->input_iter = self->fields = NULL; + + self->fields = NULL; + self->input_iter = NULL; + self->had_parse_error = 0; + self->field = NULL; + self->field_size = 0; + self->field_len = 0; + self->state = START_RECORD; + + if (!PyArg_ParseTuple(args, "O|O", &iterator, &dialect)) { + Py_DECREF(self); + return NULL; + } + self->input_iter = PyObject_GetIter(iterator); + if (self->input_iter == NULL) { + PyErr_SetString(PyExc_TypeError, + "argument 1 must be an iterator"); + Py_DECREF(self); + return NULL; + } + ctor_args = Py_BuildValue(dialect ? "(O)" : "()", dialect); + if (ctor_args == NULL) { + Py_DECREF(self); + return NULL; + } + self->dialect = (DialectObj *)PyObject_Call((PyObject *)&Dialect_Type, + ctor_args, keyword_args); + Py_DECREF(ctor_args); + if (self->dialect == NULL) { + Py_DECREF(self); + return NULL; + } + self->fields = PyList_New(0); + if (self->fields == NULL) { + Py_DECREF(self); + return NULL; + } + + return (PyObject *)self; +} + +/* + * WRITER + */ +/* ---------------------------------------------------------------- */ +static void +join_reset(WriterObj *self) +{ + self->rec_len = 0; + self->num_fields = 0; +} + +#define MEM_INCR 32768 + +/* Calculate new record length or append field to record. Return new + * record length. + */ +static int +join_append_data(WriterObj *self, char *field, int quote_empty, + int *quoted, int copy_phase) +{ + DialectObj *dialect = self->dialect; + int i, rec_len; + + rec_len = self->rec_len; + + /* If this is not the first field we need a field separator. + */ + if (self->num_fields > 0) { + if (copy_phase) + self->rec[rec_len] = dialect->delimiter; + rec_len++; + } + /* Handle preceding quote. + */ + switch (dialect->quoting) { + case QUOTE_ALL: + *quoted = 1; + if (copy_phase) + self->rec[rec_len] = dialect->quotechar; + rec_len++; + break; + case QUOTE_MINIMAL: + case QUOTE_NONNUMERIC: + /* We only know about quoted in the copy phase. + */ + if (copy_phase && *quoted) { + self->rec[rec_len] = dialect->quotechar; + rec_len++; + } + break; + case QUOTE_NONE: + break; + } + /* Copy/count field data. + */ + for (i = 0;; i++) { + char c = field[i]; + + if (c == '\0') + break; + /* If in doublequote mode we escape quote chars with a + * quote. + */ + if (dialect->quoting != QUOTE_NONE && + c == dialect->quotechar && dialect->doublequote) { + if (copy_phase) + self->rec[rec_len] = dialect->quotechar; + *quoted = 1; + rec_len++; + } + + /* Some special characters need to be escaped. If we have a + * quote character switch to quoted field instead of escaping + * individual characters. + */ + if (!*quoted + && (c == dialect->delimiter || + c == dialect->escapechar || + c == '\n' || c == '\r')) { + if (dialect->quoting != QUOTE_NONE) + *quoted = 1; + else if (dialect->escapechar) { + if (copy_phase) + self->rec[rec_len] = dialect->escapechar; + rec_len++; + } + else { + PyErr_Format(error_obj, + "delimiter must be quoted or escaped"); + return -1; + } + } + /* Copy field character into record buffer. + */ + if (copy_phase) + self->rec[rec_len] = c; + rec_len++; + } + + /* If field is empty check if it needs to be quoted. + */ + if (i == 0 && quote_empty) { + if (dialect->quoting == QUOTE_NONE) { + PyErr_Format(error_obj, + "single empty field record must be quoted"); + return -1; + } else + *quoted = 1; + } + + /* Handle final quote character on field. + */ + if (*quoted) { + if (copy_phase) + self->rec[rec_len] = dialect->quotechar; + else + /* Didn't know about leading quote until we found it + * necessary in field data - compensate for it now. + */ + rec_len++; + rec_len++; + } + + return rec_len; +} + +static int +join_check_rec_size(WriterObj *self, int rec_len) +{ + if (rec_len > self->rec_size) { + if (self->rec_size == 0) { + self->rec_size = (rec_len / MEM_INCR + 1) * MEM_INCR; + if (self->rec != NULL) + PyMem_Free(self->rec); + self->rec = PyMem_Malloc(self->rec_size); + } + else { + char *old_rec = self->rec; + + self->rec_size = (rec_len / MEM_INCR + 1) * MEM_INCR; + self->rec = PyMem_Realloc(self->rec, self->rec_size); + if (self->rec == NULL) + PyMem_Free(old_rec); + } + if (self->rec == NULL) { + PyErr_NoMemory(); + return 0; + } + } + return 1; +} + +static int +join_append(WriterObj *self, char *field, int *quoted, int quote_empty) +{ + int rec_len; + + rec_len = join_append_data(self, field, quote_empty, quoted, 0); + if (rec_len < 0) + return 0; + + /* grow record buffer if necessary */ + if (!join_check_rec_size(self, rec_len)) + return 0; + + self->rec_len = join_append_data(self, field, quote_empty, quoted, 1); + self->num_fields++; + + return 1; +} + +static int +join_append_lineterminator(WriterObj *self) +{ + int terminator_len; + + terminator_len = PyString_Size(self->dialect->lineterminator); + + /* grow record buffer if necessary */ + if (!join_check_rec_size(self, self->rec_len + terminator_len)) + return 0; + + memmove(self->rec + self->rec_len, + /* should not be NULL */ + PyString_AsString(self->dialect->lineterminator), + terminator_len); + self->rec_len += terminator_len; + + return 1; +} + +PyDoc_STRVAR(csv_writerow_doc, +"writerow(sequence)\n" +"\n" +"Construct and write a CSV record from a sequence of fields. Non-string\n" +"elements will be converted to string."); + +static PyObject * +csv_writerow(WriterObj *self, PyObject *seq) +{ + DialectObj *dialect = self->dialect; + int len, i; + + if (!PySequence_Check(seq)) + return PyErr_Format(error_obj, "sequence expected"); + + len = PySequence_Length(seq); + if (len < 0) + return NULL; + + /* Join all fields in internal buffer. + */ + join_reset(self); + for (i = 0; i < len; i++) { + PyObject *field; + int append_ok; + int quoted; + + field = PySequence_GetItem(seq, i); + if (field == NULL) + return NULL; + + quoted = 0; + if (dialect->quoting == QUOTE_NONNUMERIC) { + PyObject *num; + + num = PyNumber_Float(field); + if (num == NULL) { + quoted = 1; + PyErr_Clear(); + } + else { + Py_DECREF(num); + } + } + + if (PyString_Check(field)) { + append_ok = join_append(self, + PyString_AS_STRING(field), + "ed, len == 1); + Py_DECREF(field); + } + else if (field == Py_None) { + append_ok = join_append(self, "", "ed, len == 1); + Py_DECREF(field); + } + else { + PyObject *str; + + str = PyObject_Str(field); + Py_DECREF(field); + if (str == NULL) + return NULL; + + append_ok = join_append(self, PyString_AS_STRING(str), + "ed, len == 1); + Py_DECREF(str); + } + if (!append_ok) + return NULL; + } + + /* Add line terminator. + */ + if (!join_append_lineterminator(self)) + return 0; + + return PyObject_CallFunction(self->writeline, + "(s#)", self->rec, self->rec_len); +} + +PyDoc_STRVAR(csv_writerows_doc, +"writerows(sequence of sequences)\n" +"\n" +"Construct and write a series of sequences to a csv file. Non-string\n" +"elements will be converted to string."); + +static PyObject * +csv_writerows(WriterObj *self, PyObject *seqseq) +{ + PyObject *row_iter, *row_obj, *result; + + row_iter = PyObject_GetIter(seqseq); + if (row_iter == NULL) { + PyErr_SetString(PyExc_TypeError, + "writerows() argument must be iterable"); + return NULL; + } + while ((row_obj = PyIter_Next(row_iter))) { + result = csv_writerow(self, row_obj); + Py_DECREF(row_obj); + if (!result) { + Py_DECREF(row_iter); + return NULL; + } + else + Py_DECREF(result); + } + Py_DECREF(row_iter); + if (PyErr_Occurred()) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef Writer_methods[] = { + { "writerow", (PyCFunction)csv_writerow, METH_O, csv_writerow_doc}, + { "writerows", (PyCFunction)csv_writerows, METH_O, csv_writerows_doc}, + { NULL, NULL } +}; + +#define W_OFF(x) offsetof(WriterObj, x) + +static struct PyMemberDef Writer_memberlist[] = { + { "dialect", T_OBJECT, W_OFF(dialect), RO }, + { NULL } +}; + +static void +Writer_dealloc(WriterObj *self) +{ + Py_XDECREF(self->dialect); + Py_XDECREF(self->writeline); + if (self->rec != NULL) + PyMem_Free(self->rec); + PyObject_GC_Del(self); +} + +static int +Writer_traverse(WriterObj *self, visitproc visit, void *arg) +{ + int err; +#define VISIT(SLOT) \ + if (SLOT) { \ + err = visit((PyObject *)(SLOT), arg); \ + if (err) \ + return err; \ + } + VISIT(self->dialect); + VISIT(self->writeline); + return 0; +} + +static int +Writer_clear(WriterObj *self) +{ + Py_XDECREF(self->dialect); + Py_XDECREF(self->writeline); + self->dialect = NULL; + self->writeline = NULL; + return 0; +} + +PyDoc_STRVAR(Writer_Type_doc, +"CSV writer\n" +"\n" +"Writer objects are responsible for generating tabular data\n" +"in CSV format from sequence input.\n" +); + +static PyTypeObject Writer_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_csv.writer", /*tp_name*/ + sizeof(WriterObj), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)Writer_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)0, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + Writer_Type_doc, + (traverseproc)Writer_traverse, /*tp_traverse*/ + (inquiry)Writer_clear, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + (getiterfunc)0, /*tp_iter*/ + (getiterfunc)0, /*tp_iternext*/ + Writer_methods, /*tp_methods*/ + Writer_memberlist, /*tp_members*/ + 0, /*tp_getset*/ +}; + +static PyObject * +csv_writer(PyObject *module, PyObject *args, PyObject *keyword_args) +{ + PyObject * output_file, * dialect = NULL, *ctor_args; + WriterObj * self = PyObject_GC_New(WriterObj, &Writer_Type); + + if (!self) + return NULL; + + self->dialect = NULL; + self->writeline = NULL; + + self->rec = NULL; + self->rec_size = 0; + self->rec_len = 0; + self->num_fields = 0; + + if (!PyArg_ParseTuple(args, "O|O", &output_file, &dialect)) { + Py_DECREF(self); + return NULL; + } + self->writeline = PyObject_GetAttrString(output_file, "write"); + if (self->writeline == NULL || !PyCallable_Check(self->writeline)) { + PyErr_SetString(PyExc_TypeError, + "argument 1 must be an instance with a write method"); + Py_DECREF(self); + return NULL; + } + ctor_args = Py_BuildValue(dialect ? "(O)" : "()", dialect); + if (ctor_args == NULL) { + Py_DECREF(self); + return NULL; + } + self->dialect = (DialectObj *)PyObject_Call((PyObject *)&Dialect_Type, + ctor_args, keyword_args); + Py_DECREF(ctor_args); + if (self->dialect == NULL) { + Py_DECREF(self); + return NULL; + } + return (PyObject *)self; +} + +/* + * DIALECT REGISTRY + */ +static PyObject * +csv_list_dialects(PyObject *module, PyObject *args) +{ + return PyDict_Keys(dialects); +} + +static PyObject * +csv_register_dialect(PyObject *module, PyObject *args) +{ + PyObject *name_obj, *dialect_obj; + + if (!PyArg_ParseTuple(args, "OO", &name_obj, &dialect_obj)) + return NULL; + if (!PyString_Check(name_obj) +#ifdef Py_USING_UNICODE +&& !PyUnicode_Check(name_obj) +#endif +) { + PyErr_SetString(PyExc_TypeError, + "dialect name must be a string or unicode"); + return NULL; + } + Py_INCREF(dialect_obj); + /* A class rather than an instance? Instanciate */ + if (PyObject_TypeCheck(dialect_obj, &PyClass_Type)) { + PyObject * new_dia; + new_dia = PyObject_CallFunction(dialect_obj, ""); + Py_DECREF(dialect_obj); + if (new_dia == NULL) + return NULL; + dialect_obj = new_dia; + } + /* Make sure we finally have an instance */ + if (!PyInstance_Check(dialect_obj)) { + PyErr_SetString(PyExc_TypeError, "dialect must be an instance"); + Py_DECREF(dialect_obj); + return NULL; + } + if (PyObject_SetAttrString(dialect_obj, "_name", name_obj) < 0) { + Py_DECREF(dialect_obj); + return NULL; + } + if (PyDict_SetItem(dialects, name_obj, dialect_obj) < 0) { + Py_DECREF(dialect_obj); + return NULL; + } + Py_DECREF(dialect_obj); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +csv_unregister_dialect(PyObject *module, PyObject *name_obj) +{ + if (PyDict_DelItem(dialects, name_obj) < 0) + return PyErr_Format(error_obj, "unknown dialect"); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +csv_get_dialect(PyObject *module, PyObject *name_obj) +{ + return get_dialect_from_registry(name_obj); +} + +/* + * MODULE + */ + +PyDoc_STRVAR(csv_module_doc, +"CSV parsing and writing.\n" +"\n" +"This module provides classes that assist in the reading and writing\n" +"of Comma Separated Value (CSV) files, and implements the interface\n" +"described by PEP 305. Although many CSV files are simple to parse,\n" +"the format is not formally defined by a stable specification and\n" +"is subtle enough that parsing lines of a CSV file with something\n" +"like line.split(\",\") is bound to fail. The module supports three\n" +"basic APIs: reading, writing, and registration of dialects.\n" +"\n" +"\n" +"DIALECT REGISTRATION:\n" +"\n" +"Readers and writers support a dialect argument, which is a convenient\n" +"handle on a group of settings. When the dialect argument is a string,\n" +"it identifies one of the dialects previously registered with the module.\n" +"If it is a class or instance, the attributes of the argument are used as\n" +"the settings for the reader or writer:\n" +"\n" +" class excel:\n" +" delimiter = ','\n" +" quotechar = '\"'\n" +" escapechar = None\n" +" doublequote = True\n" +" skipinitialspace = False\n" +" lineterminator = '\r\n'\n" +" quoting = QUOTE_MINIMAL\n" +"\n" +"SETTINGS:\n" +"\n" +" * quotechar - specifies a one-character string to use as the \n" +" quoting character. It defaults to '\"'.\n" +" * delimiter - specifies a one-character string to use as the \n" +" field separator. It defaults to ','.\n" +" * skipinitialspace - specifies how to interpret whitespace which\n" +" immediately follows a delimiter. It defaults to False, which\n" +" means that whitespace immediately following a delimiter is part\n" +" of the following field.\n" +" * lineterminator - specifies the character sequence which should \n" +" terminate rows.\n" +" * quoting - controls when quotes should be generated by the writer.\n" +" It can take on any of the following module constants:\n" +"\n" +" csv.QUOTE_MINIMAL means only when required, for example, when a\n" +" field contains either the quotechar or the delimiter\n" +" csv.QUOTE_ALL means that quotes are always placed around fields.\n" +" csv.QUOTE_NONNUMERIC means that quotes are always placed around\n" +" fields which do not parse as integers or floating point\n" +" numbers.\n" +" csv.QUOTE_NONE means that quotes are never placed around fields.\n" +" * escapechar - specifies a one-character string used to escape \n" +" the delimiter when quoting is set to QUOTE_NONE.\n" +" * doublequote - controls the handling of quotes inside fields. When\n" +" True, two consecutive quotes are interpreted as one during read,\n" +" and when writing, each quote character embedded in the data is\n" +" written as two quotes\n"); + +PyDoc_STRVAR(csv_reader_doc, +" csv_reader = reader(iterable [, dialect='excel']\n" +" [optional keyword args])\n" +" for row in csv_reader:\n" +" process(row)\n" +"\n" +"The \"iterable\" argument can be any object that returns a line\n" +"of input for each iteration, such as a file object or a list. The\n" +"optional \"dialect\" parameter is discussed below. The function\n" +"also accepts optional keyword arguments which override settings\n" +"provided by the dialect.\n" +"\n" +"The returned object is an iterator. Each iteration returns a row\n" + "of the CSV file (which can span multiple input lines):\n"); + +PyDoc_STRVAR(csv_writer_doc, +" csv_writer = csv.writer(fileobj [, dialect='excel']\n" +" [optional keyword args])\n" +" for row in csv_writer:\n" +" csv_writer.writerow(row)\n" +"\n" +" [or]\n" +"\n" +" csv_writer = csv.writer(fileobj [, dialect='excel']\n" +" [optional keyword args])\n" +" csv_writer.writerows(rows)\n" +"\n" +"The \"fileobj\" argument can be any object that supports the file API.\n"); + +PyDoc_STRVAR(csv_list_dialects_doc, +"Return a list of all know dialect names.\n" +" names = csv.list_dialects()"); + +PyDoc_STRVAR(csv_get_dialect_doc, +"Return the dialect instance associated with name.\n" +" dialect = csv.get_dialect(name)"); + +PyDoc_STRVAR(csv_register_dialect_doc, +"Create a mapping from a string name to a dialect class.\n" +" dialect = csv.register_dialect(name, dialect)"); + +PyDoc_STRVAR(csv_unregister_dialect_doc, +"Delete the name/dialect mapping associated with a string name.\n" +" csv.unregister_dialect(name)"); + +static struct PyMethodDef csv_methods[] = { + { "reader", (PyCFunction)csv_reader, + METH_VARARGS | METH_KEYWORDS, csv_reader_doc}, + { "writer", (PyCFunction)csv_writer, + METH_VARARGS | METH_KEYWORDS, csv_writer_doc}, + { "list_dialects", (PyCFunction)csv_list_dialects, + METH_NOARGS, csv_list_dialects_doc}, + { "register_dialect", (PyCFunction)csv_register_dialect, + METH_VARARGS, csv_register_dialect_doc}, + { "unregister_dialect", (PyCFunction)csv_unregister_dialect, + METH_O, csv_unregister_dialect_doc}, + { "get_dialect", (PyCFunction)csv_get_dialect, + METH_O, csv_get_dialect_doc}, + { NULL, NULL } +}; + +PyMODINIT_FUNC +init_csv(void) +{ + PyObject *module; + StyleDesc *style; + + if (PyType_Ready(&Dialect_Type) < 0) + return; + + if (PyType_Ready(&Reader_Type) < 0) + return; + + if (PyType_Ready(&Writer_Type) < 0) + return; + + /* Create the module and add the functions */ + module = Py_InitModule3("_csv", csv_methods, csv_module_doc); + if (module == NULL) + return; + + /* Add version to the module. */ + if (PyModule_AddStringConstant(module, "__version__", + MODULE_VERSION) == -1) + return; + + /* Add _dialects dictionary */ + dialects = PyDict_New(); + if (dialects == NULL) + return; + if (PyModule_AddObject(module, "_dialects", dialects)) + return; + + /* Add quote styles into dictionary */ + for (style = quote_styles; style->name; style++) { + if (PyModule_AddIntConstant(module, style->name, + style->style) == -1) + return; + } + + /* Add the Dialect type */ + if (PyModule_AddObject(module, "Dialect", (PyObject *)&Dialect_Type)) + return; + + /* Add the CSV exception object to the module. */ + error_obj = PyErr_NewException("_csv.Error", NULL, NULL); + if (error_obj == NULL) + return; + PyModule_AddObject(module, "Error", error_obj); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_curses_panel.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_curses_panel.c new file mode 100644 index 00000000..d665f22b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_curses_panel.c @@ -0,0 +1,471 @@ +/* + * Interface to the ncurses panel library + * + * Original version by Thomas Gellekum + */ + +/* Release Number */ + +static char *PyCursesVersion = "2.1"; + +/* Includes */ + +#include "Python.h" + +#include "py_curses.h" + +#include + +static PyObject *PyCursesError; + + +/* Utility Functions */ + +/* + * Check the return code from a curses function and return None + * or raise an exception as appropriate. + */ + +static PyObject * +PyCursesCheckERR(int code, char *fname) +{ + if (code != ERR) { + Py_INCREF(Py_None); + return Py_None; + } else { + if (fname == NULL) { + PyErr_SetString(PyCursesError, catchall_ERR); + } else { + PyErr_Format(PyCursesError, "%s() returned ERR", fname); + } + return NULL; + } +} + +/***************************************************************************** + The Panel Object +******************************************************************************/ + +/* Definition of the panel object and panel type */ + +typedef struct { + PyObject_HEAD + PANEL *pan; + PyCursesWindowObject *wo; /* for reference counts */ +} PyCursesPanelObject; + +PyTypeObject PyCursesPanel_Type; + +#define PyCursesPanel_Check(v) ((v)->ob_type == &PyCursesPanel_Type) + +/* Some helper functions. The problem is that there's always a window + associated with a panel. To ensure that Python's GC doesn't pull + this window from under our feet we need to keep track of references + to the corresponding window object within Python. We can't use + dupwin(oldwin) to keep a copy of the curses WINDOW because the + contents of oldwin is copied only once; code like + + win = newwin(...) + pan = win.panel() + win.addstr(some_string) + pan.window().addstr(other_string) + + will fail. */ + +/* We keep a linked list of PyCursesPanelObjects, lop. A list should + suffice, I don't expect more than a handful or at most a few + dozens of panel objects within a typical program. */ +typedef struct _list_of_panels { + PyCursesPanelObject *po; + struct _list_of_panels *next; +} list_of_panels; + +/* list anchor */ +static list_of_panels *lop; + +/* Insert a new panel object into lop */ +static int +insert_lop(PyCursesPanelObject *po) +{ + list_of_panels *new; + + if ((new = (list_of_panels *)malloc(sizeof(list_of_panels))) == NULL) { + PyErr_NoMemory(); + return -1; + } + new->po = po; + new->next = lop; + lop = new; + return 0; +} + +/* Remove the panel object from lop */ +static void +remove_lop(PyCursesPanelObject *po) +{ + list_of_panels *temp, *n; + + temp = lop; + if (temp->po == po) { + lop = temp->next; + free(temp); + return; + } + while (temp->next->po != po) { + if (temp->next == NULL) + PyErr_SetString(PyExc_RuntimeError, + "remove_lop: can't find Panel Object"); + temp = temp->next; + } + n = temp->next->next; + free(temp->next); + temp->next = n; + return; +} + +/* Return the panel object that corresponds to pan */ +static PyCursesPanelObject * +find_po(PANEL *pan) +{ + list_of_panels *temp; + for (temp = lop; temp->po->pan != pan; temp = temp->next) + if (temp->next == NULL) return NULL; /* not found!? */ + return temp->po; +} + +/* Function Prototype Macros - They are ugly but very, very useful. ;-) + + X - function name + TYPE - parameter Type + ERGSTR - format string for construction of the return value + PARSESTR - format string for argument parsing */ + +#define Panel_NoArgNoReturnFunction(X) \ +static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \ +{ return PyCursesCheckERR(X(self->pan), # X); } + +#define Panel_NoArgTrueFalseFunction(X) \ +static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self) \ +{ \ + if (X (self->pan) == FALSE) { Py_INCREF(Py_False); return Py_False; } \ + else { Py_INCREF(Py_True); return Py_True; } } + +#define Panel_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \ +static PyObject *PyCursesPanel_##X(PyCursesPanelObject *self, PyObject *args) \ +{ \ + TYPE arg1, arg2; \ + if (!PyArg_ParseTuple(args, PARSESTR, &arg1, &arg2)) return NULL; \ + return PyCursesCheckERR(X(self->pan, arg1, arg2), # X); } + +/* ------------- PANEL routines --------------- */ + +Panel_NoArgNoReturnFunction(bottom_panel) +Panel_NoArgNoReturnFunction(hide_panel) +Panel_NoArgNoReturnFunction(show_panel) +Panel_NoArgNoReturnFunction(top_panel) +Panel_NoArgTrueFalseFunction(panel_hidden) +Panel_TwoArgNoReturnFunction(move_panel, int, "ii;y,x") + +/* Allocation and deallocation of Panel Objects */ + +static PyObject * +PyCursesPanel_New(PANEL *pan, PyCursesWindowObject *wo) +{ + PyCursesPanelObject *po; + + po = PyObject_NEW(PyCursesPanelObject, &PyCursesPanel_Type); + if (po == NULL) return NULL; + po->pan = pan; + po->wo = wo; + Py_INCREF(wo); + if (insert_lop(po) < 0) { + PyObject_DEL(po); + return NULL; + } + return (PyObject *)po; +} + +static void +PyCursesPanel_Dealloc(PyCursesPanelObject *po) +{ + (void)del_panel(po->pan); + Py_DECREF(po->wo); + remove_lop(po); + PyObject_DEL(po); +} + +/* panel_above(NULL) returns the bottom panel in the stack. To get + this behaviour we use curses.panel.bottom_panel(). */ +static PyObject * +PyCursesPanel_above(PyCursesPanelObject *self) +{ + PANEL *pan; + PyCursesPanelObject *po; + + pan = panel_above(self->pan); + + if (pan == NULL) { /* valid output, it means the calling panel + is on top of the stack */ + Py_INCREF(Py_None); + return Py_None; + } + po = find_po(pan); + if (po == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "panel_above: can't find Panel Object"); + return NULL; + } + Py_INCREF(po); + return (PyObject *)po; +} + +/* panel_below(NULL) returns the top panel in the stack. To get + this behaviour we use curses.panel.top_panel(). */ +static PyObject * +PyCursesPanel_below(PyCursesPanelObject *self) +{ + PANEL *pan; + PyCursesPanelObject *po; + + pan = panel_below(self->pan); + + if (pan == NULL) { /* valid output, it means the calling panel + is on the bottom of the stack */ + Py_INCREF(Py_None); + return Py_None; + } + po = find_po(pan); + if (po == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "panel_below: can't find Panel Object"); + return NULL; + } + Py_INCREF(po); + return (PyObject *)po; +} + +static PyObject * +PyCursesPanel_window(PyCursesPanelObject *self) +{ + Py_INCREF(self->wo); + return (PyObject *)self->wo; +} + +static PyObject * +PyCursesPanel_replace_panel(PyCursesPanelObject *self, PyObject *args) +{ + PyCursesPanelObject *po; + PyCursesWindowObject *temp; + int rtn; + + if (PyTuple_Size(args) != 1) { + PyErr_SetString(PyExc_TypeError, "replace requires one argument"); + return NULL; + } + if (!PyArg_ParseTuple(args, "O!;window object", + &PyCursesWindow_Type, &temp)) + return NULL; + + po = find_po(self->pan); + if (po == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "replace_panel: can't find Panel Object"); + return NULL; + } + + rtn = replace_panel(self->pan, temp->win); + if (rtn == ERR) { + PyErr_SetString(PyCursesError, "replace_panel() returned ERR"); + return NULL; + } + Py_DECREF(po->wo); + po->wo = temp; + Py_INCREF(po->wo); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCursesPanel_set_panel_userptr(PyCursesPanelObject *self, PyObject *obj) +{ + Py_INCREF(obj); + return PyCursesCheckERR(set_panel_userptr(self->pan, (void*)obj), + "set_panel_userptr"); +} + +static PyObject * +PyCursesPanel_userptr(PyCursesPanelObject *self) +{ + PyObject *obj; + PyCursesInitialised; + obj = (PyObject *) panel_userptr(self->pan); + Py_INCREF(obj); + return obj; +} + + +/* Module interface */ + +static PyMethodDef PyCursesPanel_Methods[] = { + {"above", (PyCFunction)PyCursesPanel_above, METH_NOARGS}, + {"below", (PyCFunction)PyCursesPanel_below, METH_NOARGS}, + {"bottom", (PyCFunction)PyCursesPanel_bottom_panel, METH_NOARGS}, + {"hidden", (PyCFunction)PyCursesPanel_panel_hidden, METH_NOARGS}, + {"hide", (PyCFunction)PyCursesPanel_hide_panel, METH_NOARGS}, + {"move", (PyCFunction)PyCursesPanel_move_panel, METH_VARARGS}, + {"replace", (PyCFunction)PyCursesPanel_replace_panel, METH_VARARGS}, + {"set_userptr", (PyCFunction)PyCursesPanel_set_panel_userptr, METH_O}, + {"show", (PyCFunction)PyCursesPanel_show_panel, METH_NOARGS}, + {"top", (PyCFunction)PyCursesPanel_top_panel, METH_NOARGS}, + {"userptr", (PyCFunction)PyCursesPanel_userptr, METH_NOARGS}, + {"window", (PyCFunction)PyCursesPanel_window, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +PyCursesPanel_GetAttr(PyCursesPanelObject *self, char *name) +{ + return Py_FindMethod(PyCursesPanel_Methods, (PyObject *)self, name); +} + +/* -------------------------------------------------------*/ + +PyTypeObject PyCursesPanel_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_curses_panel.curses panel", /*tp_name*/ + sizeof(PyCursesPanelObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyCursesPanel_Dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)PyCursesPanel_GetAttr, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + +/* Wrapper for panel_above(NULL). This function returns the bottom + panel of the stack, so it's renamed to bottom_panel(). + panel.above() *requires* a panel object in the first place which + may be undesirable. */ +static PyObject * +PyCurses_bottom_panel(PyObject *self) +{ + PANEL *pan; + PyCursesPanelObject *po; + + PyCursesInitialised; + + pan = panel_above(NULL); + + if (pan == NULL) { /* valid output, it means + there's no panel at all */ + Py_INCREF(Py_None); + return Py_None; + } + po = find_po(pan); + if (po == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "panel_above: can't find Panel Object"); + return NULL; + } + Py_INCREF(po); + return (PyObject *)po; +} + +static PyObject * +PyCurses_new_panel(PyObject *self, PyObject *args) +{ + PyCursesWindowObject *win; + PANEL *pan; + + if (!PyArg_ParseTuple(args, "O!", &PyCursesWindow_Type, &win)) + return NULL; + pan = new_panel(win->win); + if (pan == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + return (PyObject *)PyCursesPanel_New(pan, win); +} + + +/* Wrapper for panel_below(NULL). This function returns the top panel + of the stack, so it's renamed to top_panel(). panel.below() + *requires* a panel object in the first place which may be + undesirable. */ +static PyObject * +PyCurses_top_panel(PyObject *self) +{ + PANEL *pan; + PyCursesPanelObject *po; + + PyCursesInitialised; + + pan = panel_below(NULL); + + if (pan == NULL) { /* valid output, it means + there's no panel at all */ + Py_INCREF(Py_None); + return Py_None; + } + po = find_po(pan); + if (po == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "panel_below: can't find Panel Object"); + return NULL; + } + Py_INCREF(po); + return (PyObject *)po; +} + +static PyObject *PyCurses_update_panels(PyObject *self) +{ + PyCursesInitialised; + update_panels(); + Py_INCREF(Py_None); + return Py_None; +} + + +/* List of functions defined in the module */ + +static PyMethodDef PyCurses_methods[] = { + {"bottom_panel", (PyCFunction)PyCurses_bottom_panel, METH_NOARGS}, + {"new_panel", (PyCFunction)PyCurses_new_panel, METH_VARARGS}, + {"top_panel", (PyCFunction)PyCurses_top_panel, METH_NOARGS}, + {"update_panels", (PyCFunction)PyCurses_update_panels, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* Initialization function for the module */ + +PyMODINIT_FUNC +init_curses_panel(void) +{ + PyObject *m, *d, *v; + + /* Initialize object type */ + PyCursesPanel_Type.ob_type = &PyType_Type; + + import_curses(); + + /* Create the module and add the functions */ + m = Py_InitModule("_curses_panel", PyCurses_methods); + d = PyModule_GetDict(m); + + /* For exception _curses_panel.error */ + PyCursesError = PyErr_NewException("_curses_panel.error", NULL, NULL); + PyDict_SetItemString(d, "error", PyCursesError); + + /* Make the version available */ + v = PyString_FromString(PyCursesVersion); + PyDict_SetItemString(d, "version", v); + PyDict_SetItemString(d, "__version__", v); + Py_DECREF(v); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_cursesmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_cursesmodule.c new file mode 100644 index 00000000..0f493462 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_cursesmodule.c @@ -0,0 +1,2595 @@ +/* + * This is a curses module for Python. + * + * Based on prior work by Lance Ellinghaus and Oliver Andrich + * Version 1.2 of this module: Copyright 1994 by Lance Ellinghouse, + * Cathedral City, California Republic, United States of America. + * + * Version 1.5b1, heavily extended for ncurses by Oliver Andrich: + * Copyright 1996,1997 by Oliver Andrich, Koblenz, Germany. + * + * Tidied for Python 1.6, and currently maintained by . + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this source file to use, copy, modify, merge, or publish it + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or in any new file that contains a substantial portion of + * this file. + * + * THE AUTHOR MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF + * THE SOFTWARE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT + * EXPRESS OR IMPLIED WARRANTY. THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE, STRICT LIABILITY OR + * ANY OTHER ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* CVS: $Id: _cursesmodule.c,v 2.71 2002/11/21 14:17:51 mwh Exp $ */ + +/* + +A number of SysV or ncurses functions don't have wrappers yet; if you need +a given function, add it and send a patch. Here's a list of currently +unsupported functions: + + addchnstr addchstr chgat color_set define_key + del_curterm delscreen dupwin inchnstr inchstr innstr keyok + mcprint mvaddchnstr mvaddchstr mvchgat mvcur mvinchnstr + mvinchstr mvinnstr mmvwaddchnstr mvwaddchstr mvwchgat + mvwgetnstr mvwinchnstr mvwinchstr mvwinnstr newterm + resizeterm restartterm ripoffline scr_dump + scr_init scr_restore scr_set scrl set_curterm set_term setterm + tgetent tgetflag tgetnum tgetstr tgoto timeout tputs + use_default_colors vidattr vidputs waddchnstr waddchstr wchgat + wcolor_set winchnstr winchstr winnstr wmouse_trafo wscrl + +Low-priority: + slk_attr slk_attr_off slk_attr_on slk_attr_set slk_attroff + slk_attron slk_attrset slk_clear slk_color slk_init slk_label + slk_noutrefresh slk_refresh slk_restore slk_set slk_touch + +Menu extension (ncurses and probably SYSV): + current_item free_item free_menu item_count item_description + item_index item_init item_name item_opts item_opts_off + item_opts_on item_term item_userptr item_value item_visible + menu_back menu_driver menu_fore menu_format menu_grey + menu_init menu_items menu_mark menu_opts menu_opts_off + menu_opts_on menu_pad menu_pattern menu_request_by_name + menu_request_name menu_spacing menu_sub menu_term menu_userptr + menu_win new_item new_menu pos_menu_cursor post_menu + scale_menu set_current_item set_item_init set_item_opts + set_item_term set_item_userptr set_item_value set_menu_back + set_menu_fore set_menu_format set_menu_grey set_menu_init + set_menu_items set_menu_mark set_menu_opts set_menu_pad + set_menu_pattern set_menu_spacing set_menu_sub set_menu_term + set_menu_userptr set_menu_win set_top_row top_row unpost_menu + +Form extension (ncurses and probably SYSV): + current_field data_ahead data_behind dup_field + dynamic_fieldinfo field_arg field_back field_buffer + field_count field_fore field_index field_info field_init + field_just field_opts field_opts_off field_opts_on field_pad + field_status field_term field_type field_userptr form_driver + form_fields form_init form_opts form_opts_off form_opts_on + form_page form_request_by_name form_request_name form_sub + form_term form_userptr form_win free_field free_form + link_field link_fieldtype move_field new_field new_form + new_page pos_form_cursor post_form scale_form + set_current_field set_field_back set_field_buffer + set_field_fore set_field_init set_field_just set_field_opts + set_field_pad set_field_status set_field_term set_field_type + set_field_userptr set_fieldtype_arg set_fieldtype_choice + set_form_fields set_form_init set_form_opts set_form_page + set_form_sub set_form_term set_form_userptr set_form_win + set_max_field set_new_page unpost_form + + + */ + +/* Release Number */ + +char *PyCursesVersion = "2.2"; + +/* Includes */ + +#include "Python.h" + +#ifdef __osf__ +#define STRICT_SYSV_CURSES /* Don't use ncurses extensions */ +#endif + +#ifdef __hpux +#define STRICT_SYSV_CURSES +#endif + +#define CURSES_MODULE +#include "py_curses.h" + +/* These prototypes are in , but including this header + #defines many common symbols (such as "lines") which breaks the + curses module in other ways. So the code will just specify + explicit prototypes here. */ +extern int setupterm(char *,int,int *); +#ifdef __sgi +#include +#endif + +#if !defined(HAVE_NCURSES_H) && (defined(sgi) || defined(__sun) || defined(SCO5)) +#define STRICT_SYSV_CURSES /* Don't use ncurses extensions */ +typedef chtype attr_t; /* No attr_t type is available */ +#endif + +#if defined(_AIX) +#define STRICT_SYSV_CURSES +#endif + +/* Definition of exception curses.error */ + +static PyObject *PyCursesError; + +/* Tells whether setupterm() has been called to initialise terminfo. */ +static int initialised_setupterm = FALSE; + +/* Tells whether initscr() has been called to initialise curses. */ +static int initialised = FALSE; + +/* Tells whether start_color() has been called to initialise color usage. */ +static int initialisedcolors = FALSE; + +/* Utility Macros */ +#define PyCursesSetupTermCalled \ + if (initialised_setupterm != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call (at least) setupterm() first"); \ + return 0; } + +#define PyCursesInitialised \ + if (initialised != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call initscr() first"); \ + return 0; } + +#define PyCursesInitialisedColor \ + if (initialisedcolors != TRUE) { \ + PyErr_SetString(PyCursesError, \ + "must call start_color() first"); \ + return 0; } + +/* Utility Functions */ + +/* + * Check the return code from a curses function and return None + * or raise an exception as appropriate. These are exported using the + * CObject API. + */ + +static PyObject * +PyCursesCheckERR(int code, char *fname) +{ + if (code != ERR) { + Py_INCREF(Py_None); + return Py_None; + } else { + if (fname == NULL) { + PyErr_SetString(PyCursesError, catchall_ERR); + } else { + PyErr_Format(PyCursesError, "%s() returned ERR", fname); + } + return NULL; + } +} + +static int +PyCurses_ConvertToChtype(PyObject *obj, chtype *ch) +{ + if (PyInt_Check(obj)) { + *ch = (chtype) PyInt_AsLong(obj); + } else if(PyString_Check(obj) + && (PyString_Size(obj) == 1)) { + *ch = (chtype) *PyString_AsString(obj); + } else { + return 0; + } + return 1; +} + +/* Function versions of the 3 functions for tested whether curses has been + initialised or not. */ + +static int func_PyCursesSetupTermCalled(void) +{ + PyCursesSetupTermCalled; + return 1; +} + +static int func_PyCursesInitialised(void) +{ + PyCursesInitialised; + return 1; +} + +static int func_PyCursesInitialisedColor(void) +{ + PyCursesInitialisedColor; + return 1; +} + +/***************************************************************************** + The Window Object +******************************************************************************/ + +/* Definition of the window type */ + +PyTypeObject PyCursesWindow_Type; + +/* Function prototype macros for Window object + + X - function name + TYPE - parameter Type + ERGSTR - format string for construction of the return value + PARSESTR - format string for argument parsing + */ + +#define Window_NoArgNoReturnFunction(X) \ +static PyObject *PyCursesWindow_ ## X (PyCursesWindowObject *self, PyObject *args) \ +{ return PyCursesCheckERR(X(self->win), # X); } + +#define Window_NoArgTrueFalseFunction(X) \ +static PyObject * PyCursesWindow_ ## X (PyCursesWindowObject *self) \ +{ \ + if (X (self->win) == FALSE) { Py_INCREF(Py_False); return Py_False; } \ + else { Py_INCREF(Py_True); return Py_True; } } + +#define Window_NoArgNoReturnVoidFunction(X) \ +static PyObject * PyCursesWindow_ ## X (PyCursesWindowObject *self) \ +{ \ + X(self->win); Py_INCREF(Py_None); return Py_None; } + +#define Window_NoArg2TupleReturnFunction(X, TYPE, ERGSTR) \ +static PyObject * PyCursesWindow_ ## X (PyCursesWindowObject *self) \ +{ \ + TYPE arg1, arg2; \ + X(self->win,arg1,arg2); return Py_BuildValue(ERGSTR, arg1, arg2); } + +#define Window_OneArgNoReturnVoidFunction(X, TYPE, PARSESTR) \ +static PyObject * PyCursesWindow_ ## X (PyCursesWindowObject *self, PyObject *args) \ +{ \ + TYPE arg1; \ + if (!PyArg_ParseTuple(args, PARSESTR, &arg1)) return NULL; \ + X(self->win,arg1); Py_INCREF(Py_None); return Py_None; } + +#define Window_OneArgNoReturnFunction(X, TYPE, PARSESTR) \ +static PyObject * PyCursesWindow_ ## X (PyCursesWindowObject *self, PyObject *args) \ +{ \ + TYPE arg1; \ + if (!PyArg_ParseTuple(args,PARSESTR, &arg1)) return NULL; \ + return PyCursesCheckERR(X(self->win, arg1), # X); } + +#define Window_TwoArgNoReturnFunction(X, TYPE, PARSESTR) \ +static PyObject * PyCursesWindow_ ## X (PyCursesWindowObject *self, PyObject *args) \ +{ \ + TYPE arg1, arg2; \ + if (!PyArg_ParseTuple(args,PARSESTR, &arg1, &arg2)) return NULL; \ + return PyCursesCheckERR(X(self->win, arg1, arg2), # X); } + +/* ------------- WINDOW routines --------------- */ + +Window_NoArgNoReturnFunction(untouchwin) +Window_NoArgNoReturnFunction(touchwin) +Window_NoArgNoReturnFunction(redrawwin) +Window_NoArgNoReturnFunction(winsertln) +Window_NoArgNoReturnFunction(werase) +Window_NoArgNoReturnFunction(wdeleteln) + +Window_NoArgTrueFalseFunction(is_wintouched) + +Window_NoArgNoReturnVoidFunction(wsyncup) +Window_NoArgNoReturnVoidFunction(wsyncdown) +Window_NoArgNoReturnVoidFunction(wstandend) +Window_NoArgNoReturnVoidFunction(wstandout) +Window_NoArgNoReturnVoidFunction(wcursyncup) +Window_NoArgNoReturnVoidFunction(wclrtoeol) +Window_NoArgNoReturnVoidFunction(wclrtobot) +Window_NoArgNoReturnVoidFunction(wclear) + +Window_OneArgNoReturnVoidFunction(idcok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnVoidFunction(immedok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnVoidFunction(wtimeout, int, "i;delay") + +Window_NoArg2TupleReturnFunction(getyx, int, "ii") +Window_NoArg2TupleReturnFunction(getbegyx, int, "ii") +Window_NoArg2TupleReturnFunction(getmaxyx, int, "ii") +Window_NoArg2TupleReturnFunction(getparyx, int, "ii") + +Window_OneArgNoReturnFunction(wattron, attr_t, "l;attr") +Window_OneArgNoReturnFunction(wattroff, attr_t, "l;attr") +Window_OneArgNoReturnFunction(wattrset, attr_t, "l;attr") +Window_OneArgNoReturnFunction(clearok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnFunction(idlok, int, "i;True(1) or False(0)") +#if defined(__NetBSD__) +Window_OneArgNoReturnVoidFunction(keypad, int, "i;True(1) or False(0)") +#else +Window_OneArgNoReturnFunction(keypad, int, "i;True(1) or False(0)") +#endif +Window_OneArgNoReturnFunction(leaveok, int, "i;True(1) or False(0)") +#if defined(__NetBSD__) +Window_OneArgNoReturnVoidFunction(nodelay, int, "i;True(1) or False(0)") +#else +Window_OneArgNoReturnFunction(nodelay, int, "i;True(1) or False(0)") +#endif +Window_OneArgNoReturnFunction(notimeout, int, "i;True(1) or False(0)") +Window_OneArgNoReturnFunction(scrollok, int, "i;True(1) or False(0)") +Window_OneArgNoReturnFunction(winsdelln, int, "i;nlines") +Window_OneArgNoReturnFunction(syncok, int, "i;True(1) or False(0)") + +Window_TwoArgNoReturnFunction(mvwin, int, "ii;y,x") +Window_TwoArgNoReturnFunction(mvderwin, int, "ii;y,x") +Window_TwoArgNoReturnFunction(wmove, int, "ii;y,x") +#ifndef STRICT_SYSV_CURSES +Window_TwoArgNoReturnFunction(wresize, int, "ii;lines,columns") +#endif + +/* Allocation and deallocation of Window Objects */ + +static PyObject * +PyCursesWindow_New(WINDOW *win) +{ + PyCursesWindowObject *wo; + + wo = PyObject_NEW(PyCursesWindowObject, &PyCursesWindow_Type); + if (wo == NULL) return NULL; + wo->win = win; + return (PyObject *)wo; +} + +static void +PyCursesWindow_Dealloc(PyCursesWindowObject *wo) +{ + if (wo->win != stdscr) delwin(wo->win); + PyObject_DEL(wo); +} + +/* Addch, Addstr, Addnstr */ + +static PyObject * +PyCursesWindow_AddCh(PyCursesWindowObject *self, PyObject *args) +{ + int rtn, x, y, use_xy = FALSE; + PyObject *temp; + chtype ch = 0; + attr_t attr = A_NORMAL; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args, "Ol;ch or int,attr", &temp, &attr)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args,"iiO;y,x,ch or int", &y, &x, &temp)) + return NULL; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOl;y,x,ch or int, attr", + &y, &x, &temp, &attr)) + return NULL; + use_xy = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "addch requires 1 to 4 arguments"); + return NULL; + } + + if (!PyCurses_ConvertToChtype(temp, &ch)) { + PyErr_SetString(PyExc_TypeError, "argument 1 or 3 must be a ch or an int"); + return NULL; + } + + if (use_xy == TRUE) + rtn = mvwaddch(self->win,y,x, ch | attr); + else { + rtn = waddch(self->win, ch | attr); + } + return PyCursesCheckERR(rtn, "addch"); +} + +static PyObject * +PyCursesWindow_AddStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + char *str; + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + int use_xy = FALSE, use_attr = FALSE; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"s;str", &str)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"sl;str,attr", &str, &attr)) + return NULL; + use_attr = TRUE; + break; + case 3: + if (!PyArg_ParseTuple(args,"iis;int,int,str", &y, &x, &str)) + return NULL; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iisl;int,int,str,attr", &y, &x, &str, &attr)) + return NULL; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "addstr requires 1 to 4 arguments"); + return NULL; + } + + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + wattrset(self->win,attr); + } + if (use_xy == TRUE) + rtn = mvwaddstr(self->win,y,x,str); + else + rtn = waddstr(self->win,str); + if (use_attr == TRUE) + wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, "addstr"); +} + +static PyObject * +PyCursesWindow_AddNStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn, x, y, n; + char *str; + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + int use_xy = FALSE, use_attr = FALSE; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"si;str,n", &str, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args,"sil;str,n,attr", &str, &n, &attr)) + return NULL; + use_attr = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iisi;y,x,str,n", &y, &x, &str, &n)) + return NULL; + use_xy = TRUE; + break; + case 5: + if (!PyArg_ParseTuple(args,"iisil;y,x,str,n,attr", &y, &x, &str, &n, &attr)) + return NULL; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "addnstr requires 2 to 5 arguments"); + return NULL; + } + + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + wattrset(self->win,attr); + } + if (use_xy == TRUE) + rtn = mvwaddnstr(self->win,y,x,str,n); + else + rtn = waddnstr(self->win,str,n); + if (use_attr == TRUE) + wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, "addnstr"); +} + +static PyObject * +PyCursesWindow_Bkgd(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype bkgd; + attr_t attr = A_NORMAL; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;ch or int,attr", &temp, &attr)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "bkgd requires 1 or 2 arguments"); + return NULL; + } + + if (!PyCurses_ConvertToChtype(temp, &bkgd)) { + PyErr_SetString(PyExc_TypeError, "argument 1 or 3 must be a ch or an int"); + return NULL; + } + + return PyCursesCheckERR(wbkgd(self->win, bkgd | attr), "bkgd"); +} + +static PyObject * +PyCursesWindow_BkgdSet(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype bkgd; + attr_t attr = A_NORMAL; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;ch or int,attr", &temp, &attr)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "bkgdset requires 1 or 2 arguments"); + return NULL; + } + + if (!PyCurses_ConvertToChtype(temp, &bkgd)) { + PyErr_SetString(PyExc_TypeError, "argument 1 must be a ch or an int"); + return NULL; + } + + wbkgdset(self->win, bkgd | attr); + return PyCursesCheckERR(0, "bkgdset"); +} + +static PyObject * +PyCursesWindow_Border(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp[8]; + chtype ch[8]; + int i; + + /* Clear the array of parameters */ + for(i=0; i<8; i++) { + temp[i] = NULL; + ch[i] = 0; + } + + if (!PyArg_ParseTuple(args,"|OOOOOOOO;ls,rs,ts,bs,tl,tr,bl,br", + &temp[0], &temp[1], &temp[2], &temp[3], + &temp[4], &temp[5], &temp[6], &temp[7])) + return NULL; + + for(i=0; i<8; i++) { + if (temp[i] != NULL && !PyCurses_ConvertToChtype(temp[i], &ch[i])) { + PyErr_Format(PyExc_TypeError, + "argument %i must be a ch or an int", i+1); + return NULL; + } + } + + wborder(self->win, + ch[0], ch[1], ch[2], ch[3], + ch[4], ch[5], ch[6], ch[7]); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCursesWindow_Box(PyCursesWindowObject *self, PyObject *args) +{ + chtype ch1=0,ch2=0; + switch(PyTuple_Size(args)){ + case 0: break; + default: + if (!PyArg_ParseTuple(args,"ll;vertint,horint", &ch1, &ch2)) + return NULL; + } + box(self->win,ch1,ch2); + Py_INCREF(Py_None); + return Py_None; +} + +#if defined(HAVE_NCURSES_H) || defined(MVWDELCH_IS_EXPRESSION) +#define py_mvwdelch mvwdelch +#else +int py_mvwdelch(WINDOW *w, int y, int x) +{ + mvwdelch(w,y,x); + /* On HP/UX, mvwdelch already returns. On other systems, + we may well run into this return statement. */ + return 0; +} +#endif + + +static PyObject * +PyCursesWindow_DelCh(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + + switch (PyTuple_Size(args)) { + case 0: + rtn = wdelch(self->win); + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x", &y, &x)) + return NULL; + rtn = py_mvwdelch(self->win,y,x); + break; + default: + PyErr_SetString(PyExc_TypeError, "delch requires 0 or 2 arguments"); + return NULL; + } + return PyCursesCheckERR(rtn, "[mv]wdelch"); +} + +static PyObject * +PyCursesWindow_DerWin(PyCursesWindowObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols, begin_y, begin_x; + + nlines = 0; + ncols = 0; + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;begin_y,begin_x",&begin_y,&begin_x)) + return NULL; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiii;nlines,ncols,begin_y,begin_x", + &nlines,&ncols,&begin_y,&begin_x)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "derwin requires 2 or 4 arguments"); + return NULL; + } + + win = derwin(self->win,nlines,ncols,begin_y,begin_x); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win); +} + +static PyObject * +PyCursesWindow_EchoChar(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + attr_t attr = A_NORMAL; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"Ol;ch or int,attr", &temp, &attr)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "echochar requires 1 or 2 arguments"); + + + return NULL; + } + + if (!PyCurses_ConvertToChtype(temp, &ch)) { + PyErr_SetString(PyExc_TypeError, "argument 1 must be a ch or an int"); + return NULL; + } + +#ifdef WINDOW_HAS_FLAGS + if (self->win->_flags & _ISPAD) + return PyCursesCheckERR(pechochar(self->win, ch | attr), + "echochar"); + else +#endif + return PyCursesCheckERR(wechochar(self->win, ch | attr), + "echochar"); +} + +#ifdef NCURSES_MOUSE_VERSION +static PyObject * +PyCursesWindow_Enclose(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + if (!PyArg_ParseTuple(args,"ii;y,x", &y, &x)) + return NULL; + + return PyInt_FromLong( wenclose(self->win,y,x) ); +} +#endif + +static PyObject * +PyCursesWindow_GetBkgd(PyCursesWindowObject *self) +{ + return PyInt_FromLong((long) getbkgd(self->win)); +} + +static PyObject * +PyCursesWindow_GetCh(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + int rtn; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + rtn = wgetch(self->win); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = mvwgetch(self->win,y,x); + Py_END_ALLOW_THREADS + break; + default: + PyErr_SetString(PyExc_TypeError, "getch requires 0 or 2 arguments"); + return NULL; + } + return PyInt_FromLong((long)rtn); +} + +static PyObject * +PyCursesWindow_GetKey(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + int rtn; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + rtn = wgetch(self->win); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = mvwgetch(self->win,y,x); + Py_END_ALLOW_THREADS + break; + default: + PyErr_SetString(PyExc_TypeError, "getkey requires 0 or 2 arguments"); + return NULL; + } + if (rtn == ERR) { + /* getch() returns ERR in nodelay mode */ + PyErr_SetString(PyCursesError, "no input"); + return NULL; + } else if (rtn<=255) + return Py_BuildValue("c", rtn); + else +#if defined(__NetBSD__) + return PyString_FromString(unctrl(rtn)); +#else + return PyString_FromString((char *)keyname(rtn)); +#endif +} + +static PyObject * +PyCursesWindow_GetStr(PyCursesWindowObject *self, PyObject *args) +{ + int x, y, n; + char rtn[1024]; /* This should be big enough.. I hope */ + int rtn2; + + switch (PyTuple_Size(args)) { + case 0: + Py_BEGIN_ALLOW_THREADS + rtn2 = wgetstr(self->win,rtn); + Py_END_ALLOW_THREADS + break; + case 1: + if (!PyArg_ParseTuple(args,"i;n", &n)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn2 = wgetnstr(self->win,rtn,n); + Py_END_ALLOW_THREADS + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn2 = mvwgetstr(self->win,y,x,rtn); + Py_END_ALLOW_THREADS + break; + case 3: + if (!PyArg_ParseTuple(args,"iii;y,x,n", &y, &x, &n)) + return NULL; +#ifdef STRICT_SYSV_CURSES + /* Untested */ + Py_BEGIN_ALLOW_THREADS + rtn2 = wmove(self->win,y,x)==ERR ? ERR : + wgetnstr(self->win, rtn, n); + Py_END_ALLOW_THREADS +#else + Py_BEGIN_ALLOW_THREADS + rtn2 = mvwgetnstr(self->win, y, x, rtn, n); + Py_END_ALLOW_THREADS +#endif + break; + default: + PyErr_SetString(PyExc_TypeError, "getstr requires 0 to 2 arguments"); + return NULL; + } + if (rtn2 == ERR) + rtn[0] = 0; + return PyString_FromString(rtn); +} + +static PyObject * +PyCursesWindow_Hline(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + int n, x, y, code = OK; + attr_t attr = A_NORMAL; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args, "Oi;ch or int,n", &temp, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args, "Oil;ch or int,n,attr", &temp, &n, &attr)) + return NULL; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiOi;y,x,ch or int,n", &y, &x, &temp, &n)) + return NULL; + code = wmove(self->win, y, x); + break; + case 5: + if (!PyArg_ParseTuple(args, "iiOil; y,x,ch or int,n,attr", + &y, &x, &temp, &n, &attr)) + return NULL; + code = wmove(self->win, y, x); + break; + default: + PyErr_SetString(PyExc_TypeError, "hline requires 2 to 5 arguments"); + return NULL; + } + + if (code != ERR) { + if (!PyCurses_ConvertToChtype(temp, &ch)) { + PyErr_SetString(PyExc_TypeError, + "argument 1 or 3 must be a ch or an int"); + return NULL; + } + return PyCursesCheckERR(whline(self->win, ch | attr, n), "hline"); + } else + return PyCursesCheckERR(code, "wmove"); +} + +static PyObject * +PyCursesWindow_InsCh(PyCursesWindowObject *self, PyObject *args) +{ + int rtn, x, y, use_xy = FALSE; + PyObject *temp; + chtype ch = 0; + attr_t attr = A_NORMAL; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O;ch or int", &temp)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args, "Ol;ch or int,attr", &temp, &attr)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args,"iiO;y,x,ch or int", &y, &x, &temp)) + return NULL; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iiOl;y,x,ch or int, attr", &y, &x, &temp, &attr)) + return NULL; + use_xy = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "insch requires 1 or 4 arguments"); + return NULL; + } + + if (!PyCurses_ConvertToChtype(temp, &ch)) { + PyErr_SetString(PyExc_TypeError, + "argument 1 or 3 must be a ch or an int"); + return NULL; + } + + if (use_xy == TRUE) + rtn = mvwinsch(self->win,y,x, ch | attr); + else { + rtn = winsch(self->win, ch | attr); + } + return PyCursesCheckERR(rtn, "insch"); +} + +static PyObject * +PyCursesWindow_InCh(PyCursesWindowObject *self, PyObject *args) +{ + int x, y, rtn; + + switch (PyTuple_Size(args)) { + case 0: + rtn = winch(self->win); + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + rtn = mvwinch(self->win,y,x); + break; + default: + PyErr_SetString(PyExc_TypeError, "inch requires 0 or 2 arguments"); + return NULL; + } + return PyInt_FromLong((long) rtn); +} + +static PyObject * +PyCursesWindow_InStr(PyCursesWindowObject *self, PyObject *args) +{ + int x, y, n; + char rtn[1024]; /* This should be big enough.. I hope */ + int rtn2; + + switch (PyTuple_Size(args)) { + case 0: + rtn2 = winstr(self->win,rtn); + break; + case 1: + if (!PyArg_ParseTuple(args,"i;n", &n)) + return NULL; + rtn2 = winnstr(self->win,rtn,n); + break; + case 2: + if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x)) + return NULL; + rtn2 = mvwinstr(self->win,y,x,rtn); + break; + case 3: + if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n)) + return NULL; + rtn2 = mvwinnstr(self->win, y, x, rtn, n); + break; + default: + PyErr_SetString(PyExc_TypeError, "instr requires 0 or 3 arguments"); + return NULL; + } + if (rtn2 == ERR) + rtn[0] = 0; + return PyString_FromString(rtn); +} + +static PyObject * +PyCursesWindow_InsStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn; + int x, y; + char *str; + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + int use_xy = FALSE, use_attr = FALSE; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"s;str", &str)) + return NULL; + break; + case 2: + if (!PyArg_ParseTuple(args,"sl;str,attr", &str, &attr)) + return NULL; + use_attr = TRUE; + break; + case 3: + if (!PyArg_ParseTuple(args,"iis;y,x,str", &y, &x, &str)) + return NULL; + use_xy = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iisl;y,x,str,attr", &y, &x, &str, &attr)) + return NULL; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "insstr requires 1 to 4 arguments"); + return NULL; + } + + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + wattrset(self->win,attr); + } + if (use_xy == TRUE) + rtn = mvwinsstr(self->win,y,x,str); + else + rtn = winsstr(self->win,str); + if (use_attr == TRUE) + wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, "insstr"); +} + +static PyObject * +PyCursesWindow_InsNStr(PyCursesWindowObject *self, PyObject *args) +{ + int rtn, x, y, n; + char *str; + attr_t attr = A_NORMAL , attr_old = A_NORMAL; + int use_xy = FALSE, use_attr = FALSE; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"si;str,n", &str, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args,"sil;str,n,attr", &str, &n, &attr)) + return NULL; + use_attr = TRUE; + break; + case 4: + if (!PyArg_ParseTuple(args,"iisi;y,x,str,n", &y, &x, &str, &n)) + return NULL; + use_xy = TRUE; + break; + case 5: + if (!PyArg_ParseTuple(args,"iisil;y,x,str,n,attr", &y, &x, &str, &n, &attr)) + return NULL; + use_xy = use_attr = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, "insnstr requires 2 to 5 arguments"); + return NULL; + } + + if (use_attr == TRUE) { + attr_old = getattrs(self->win); + wattrset(self->win,attr); + } + if (use_xy == TRUE) + rtn = mvwinsnstr(self->win,y,x,str,n); + else + rtn = winsnstr(self->win,str,n); + if (use_attr == TRUE) + wattrset(self->win,attr_old); + return PyCursesCheckERR(rtn, "insnstr"); +} + +static PyObject * +PyCursesWindow_Is_LineTouched(PyCursesWindowObject *self, PyObject *args) +{ + int line, erg; + if (!PyArg_ParseTuple(args,"i;line", &line)) + return NULL; + erg = is_linetouched(self->win, line); + if (erg == ERR) { + PyErr_SetString(PyExc_TypeError, + "is_linetouched: line number outside of boundaries"); + return NULL; + } else + if (erg == FALSE) { + Py_INCREF(Py_False); + return Py_False; + } else { + Py_INCREF(Py_True); + return Py_True; + } +} + +static PyObject * +PyCursesWindow_NoOutRefresh(PyCursesWindowObject *self, PyObject *args) +{ + int pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol; + int rtn; + +#ifndef WINDOW_HAS_FLAGS + if (0) { +#else + if (self->win->_flags & _ISPAD) { +#endif + switch(PyTuple_Size(args)) { + case 6: + if (!PyArg_ParseTuple(args, + "iiiiii;" \ + "pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol", + &pminrow, &pmincol, &sminrow, + &smincol, &smaxrow, &smaxcol)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = pnoutrefresh(self->win, + pminrow, pmincol, sminrow, + smincol, smaxrow, smaxcol); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "pnoutrefresh"); + default: + PyErr_SetString(PyCursesError, + "noutrefresh() called for a pad " + "requires 6 arguments"); + return NULL; + } + } else { + if (!PyArg_ParseTuple(args, ":noutrefresh")) + return NULL; + + Py_BEGIN_ALLOW_THREADS + rtn = wnoutrefresh(self->win); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "wnoutrefresh"); + } +} + +static PyObject * +PyCursesWindow_Overlay(PyCursesWindowObject *self, PyObject *args) +{ + PyCursesWindowObject *temp; + int use_copywin = FALSE; + int sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol; + int rtn; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O!;window object", + &PyCursesWindow_Type, &temp)) + return NULL; + break; + case 7: + if (!PyArg_ParseTuple(args, "(O!iiiiii);window object, int, int, int, int, int, int", + &PyCursesWindow_Type, &temp, &sminrow, &smincol, + &dminrow, &dmincol, &dmaxrow, &dmaxcol)) + return NULL; + use_copywin = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, + "overlay requires one or seven arguments"); + return NULL; + } + + if (use_copywin == TRUE) { + rtn = copywin(self->win, temp->win, sminrow, smincol, + dminrow, dmincol, dmaxrow, dmaxcol, TRUE); + return PyCursesCheckERR(rtn, "copywin"); + } + else { + rtn = overlay(self->win, temp->win); + return PyCursesCheckERR(rtn, "overlay"); + } +} + +static PyObject * +PyCursesWindow_Overwrite(PyCursesWindowObject *self, PyObject *args) +{ + PyCursesWindowObject *temp; + int use_copywin = FALSE; + int sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol; + int rtn; + + switch (PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "O!;window object", + &PyCursesWindow_Type, &temp)) + return NULL; + break; + case 7: + if (!PyArg_ParseTuple(args, "(O!iiiiii);window object, int, int, int, int, int, int", + &PyCursesWindow_Type, &temp, &sminrow, &smincol, + &dminrow, &dmincol, &dmaxrow, &dmaxcol)) + return NULL; + use_copywin = TRUE; + break; + default: + PyErr_SetString(PyExc_TypeError, + "overwrite requires one or seven arguments"); + return NULL; + } + + if (use_copywin == TRUE) { + rtn = copywin(self->win, temp->win, sminrow, smincol, + dminrow, dmincol, dmaxrow, dmaxcol, FALSE); + return PyCursesCheckERR(rtn, "copywin"); + } + else { + rtn = overwrite(self->win, temp->win); + return PyCursesCheckERR(rtn, "overwrite"); + } +} + +static PyObject * +PyCursesWindow_PutWin(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + + if (!PyArg_ParseTuple(args, "O;fileobj", &temp)) + return NULL; + if (!PyFile_Check(temp)) { + PyErr_SetString(PyExc_TypeError, "argument must be a file object"); + return NULL; + } + return PyCursesCheckERR(putwin(self->win, PyFile_AsFile(temp)), + "putwin"); +} + +static PyObject * +PyCursesWindow_RedrawLine(PyCursesWindowObject *self, PyObject *args) +{ + int beg, num; + if (!PyArg_ParseTuple(args,"ii;beg,num", &beg, &num)) + return NULL; + return PyCursesCheckERR(wredrawln(self->win,beg,num), "redrawln"); +} + +static PyObject * +PyCursesWindow_Refresh(PyCursesWindowObject *self, PyObject *args) +{ + int pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol; + int rtn; + +#ifndef WINDOW_HAS_FLAGS + if (0) { +#else + if (self->win->_flags & _ISPAD) { +#endif + switch(PyTuple_Size(args)) { + case 6: + if (!PyArg_ParseTuple(args, + "iiiiii;" \ + "pminrow,pmincol,sminrow,smincol,smaxrow,smaxcol", + &pminrow, &pmincol, &sminrow, + &smincol, &smaxrow, &smaxcol)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + rtn = prefresh(self->win, + pminrow, pmincol, sminrow, + smincol, smaxrow, smaxcol); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "prefresh"); + default: + PyErr_SetString(PyCursesError, + "refresh() for a pad requires 6 arguments"); + return NULL; + } + } else { + if (!PyArg_ParseTuple(args, ":refresh")) + return NULL; + Py_BEGIN_ALLOW_THREADS + rtn = wrefresh(self->win); + Py_END_ALLOW_THREADS + return PyCursesCheckERR(rtn, "prefresh"); + } +} + +static PyObject * +PyCursesWindow_SetScrollRegion(PyCursesWindowObject *self, PyObject *args) +{ + int x, y; + if (!PyArg_ParseTuple(args,"ii;top, bottom",&y,&x)) + return NULL; + return PyCursesCheckERR(wsetscrreg(self->win,y,x), "wsetscrreg"); +} + +static PyObject * +PyCursesWindow_SubWin(PyCursesWindowObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols, begin_y, begin_x; + + nlines = 0; + ncols = 0; + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;begin_y,begin_x",&begin_y,&begin_x)) + return NULL; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiii;nlines,ncols,begin_y,begin_x", + &nlines,&ncols,&begin_y,&begin_x)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "subwin requires 2 or 4 arguments"); + return NULL; + } + + /* printf("Subwin: %i %i %i %i \n", nlines, ncols, begin_y, begin_x); */ +#ifdef WINDOW_HAS_FLAGS + if (self->win->_flags & _ISPAD) + win = subpad(self->win, nlines, ncols, begin_y, begin_x); + else +#endif + win = subwin(self->win, nlines, ncols, begin_y, begin_x); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win); +} + +static PyObject * +PyCursesWindow_Scroll(PyCursesWindowObject *self, PyObject *args) +{ + int nlines; + switch(PyTuple_Size(args)) { + case 0: + return PyCursesCheckERR(scroll(self->win), "scroll"); + case 1: + if (!PyArg_ParseTuple(args, "i;nlines", &nlines)) + return NULL; + return PyCursesCheckERR(wscrl(self->win, nlines), "scroll"); + default: + PyErr_SetString(PyExc_TypeError, "scroll requires 0 or 1 arguments"); + return NULL; + } +} + +static PyObject * +PyCursesWindow_TouchLine(PyCursesWindowObject *self, PyObject *args) +{ + int st, cnt, val; + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;start,count",&st,&cnt)) + return NULL; + return PyCursesCheckERR(touchline(self->win,st,cnt), "touchline"); + case 3: + if (!PyArg_ParseTuple(args, "iii;start,count,val", &st, &cnt, &val)) + return NULL; + return PyCursesCheckERR(wtouchln(self->win, st, cnt, val), "touchline"); + default: + PyErr_SetString(PyExc_TypeError, "touchline requires 2 or 3 arguments"); + return NULL; + } +} + +static PyObject * +PyCursesWindow_Vline(PyCursesWindowObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + int n, x, y, code = OK; + attr_t attr = A_NORMAL; + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args, "Oi;ch or int,n", &temp, &n)) + return NULL; + break; + case 3: + if (!PyArg_ParseTuple(args, "Oil;ch or int,n,attr", &temp, &n, &attr)) + return NULL; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiOi;y,x,ch or int,n", &y, &x, &temp, &n)) + return NULL; + code = wmove(self->win, y, x); + break; + case 5: + if (!PyArg_ParseTuple(args, "iiOil; y,x,ch or int,n,attr", + &y, &x, &temp, &n, &attr)) + return NULL; + code = wmove(self->win, y, x); + break; + default: + PyErr_SetString(PyExc_TypeError, "vline requires 2 to 5 arguments"); + return NULL; + } + + if (code != ERR) { + if (!PyCurses_ConvertToChtype(temp, &ch)) { + PyErr_SetString(PyExc_TypeError, + "argument 1 or 3 must be a ch or an int"); + return NULL; + } + return PyCursesCheckERR(wvline(self->win, ch | attr, n), "vline"); + } else + return PyCursesCheckERR(code, "wmove"); +} + +static PyMethodDef PyCursesWindow_Methods[] = { + {"addch", (PyCFunction)PyCursesWindow_AddCh, METH_VARARGS}, + {"addnstr", (PyCFunction)PyCursesWindow_AddNStr, METH_VARARGS}, + {"addstr", (PyCFunction)PyCursesWindow_AddStr, METH_VARARGS}, + {"attroff", (PyCFunction)PyCursesWindow_wattroff, METH_VARARGS}, + {"attron", (PyCFunction)PyCursesWindow_wattron, METH_VARARGS}, + {"attrset", (PyCFunction)PyCursesWindow_wattrset, METH_VARARGS}, + {"bkgd", (PyCFunction)PyCursesWindow_Bkgd, METH_VARARGS}, + {"bkgdset", (PyCFunction)PyCursesWindow_BkgdSet, METH_VARARGS}, + {"border", (PyCFunction)PyCursesWindow_Border, METH_VARARGS}, + {"box", (PyCFunction)PyCursesWindow_Box, METH_VARARGS}, + {"clear", (PyCFunction)PyCursesWindow_wclear, METH_NOARGS}, + {"clearok", (PyCFunction)PyCursesWindow_clearok, METH_VARARGS}, + {"clrtobot", (PyCFunction)PyCursesWindow_wclrtobot, METH_NOARGS}, + {"clrtoeol", (PyCFunction)PyCursesWindow_wclrtoeol, METH_NOARGS}, + {"cursyncup", (PyCFunction)PyCursesWindow_wcursyncup, METH_NOARGS}, + {"delch", (PyCFunction)PyCursesWindow_DelCh, METH_VARARGS}, + {"deleteln", (PyCFunction)PyCursesWindow_wdeleteln, METH_NOARGS}, + {"derwin", (PyCFunction)PyCursesWindow_DerWin, METH_VARARGS}, + {"echochar", (PyCFunction)PyCursesWindow_EchoChar, METH_VARARGS}, +#ifdef NCURSES_MOUSE_VERSION + {"enclose", (PyCFunction)PyCursesWindow_Enclose, METH_VARARGS}, +#endif + {"erase", (PyCFunction)PyCursesWindow_werase, METH_NOARGS}, + {"getbegyx", (PyCFunction)PyCursesWindow_getbegyx, METH_NOARGS}, + {"getbkgd", (PyCFunction)PyCursesWindow_GetBkgd, METH_NOARGS}, + {"getch", (PyCFunction)PyCursesWindow_GetCh, METH_VARARGS}, + {"getkey", (PyCFunction)PyCursesWindow_GetKey, METH_VARARGS}, + {"getmaxyx", (PyCFunction)PyCursesWindow_getmaxyx, METH_NOARGS}, + {"getparyx", (PyCFunction)PyCursesWindow_getparyx, METH_NOARGS}, + {"getstr", (PyCFunction)PyCursesWindow_GetStr, METH_VARARGS}, + {"getyx", (PyCFunction)PyCursesWindow_getyx, METH_NOARGS}, + {"hline", (PyCFunction)PyCursesWindow_Hline, METH_VARARGS}, + {"idcok", (PyCFunction)PyCursesWindow_idcok, METH_VARARGS}, + {"idlok", (PyCFunction)PyCursesWindow_idlok, METH_VARARGS}, + {"immedok", (PyCFunction)PyCursesWindow_immedok, METH_VARARGS}, + {"inch", (PyCFunction)PyCursesWindow_InCh, METH_VARARGS}, + {"insch", (PyCFunction)PyCursesWindow_InsCh, METH_VARARGS}, + {"insdelln", (PyCFunction)PyCursesWindow_winsdelln, METH_VARARGS}, + {"insertln", (PyCFunction)PyCursesWindow_winsertln, METH_NOARGS}, + {"insnstr", (PyCFunction)PyCursesWindow_InsNStr, METH_VARARGS}, + {"insstr", (PyCFunction)PyCursesWindow_InsStr, METH_VARARGS}, + {"instr", (PyCFunction)PyCursesWindow_InStr, METH_VARARGS}, + {"is_linetouched", (PyCFunction)PyCursesWindow_Is_LineTouched, METH_VARARGS}, + {"is_wintouched", (PyCFunction)PyCursesWindow_is_wintouched, METH_NOARGS}, + {"keypad", (PyCFunction)PyCursesWindow_keypad, METH_VARARGS}, + {"leaveok", (PyCFunction)PyCursesWindow_leaveok, METH_VARARGS}, + {"move", (PyCFunction)PyCursesWindow_wmove, METH_VARARGS}, + {"mvderwin", (PyCFunction)PyCursesWindow_mvderwin, METH_VARARGS}, + {"mvwin", (PyCFunction)PyCursesWindow_mvwin, METH_VARARGS}, + {"nodelay", (PyCFunction)PyCursesWindow_nodelay, METH_VARARGS}, + {"notimeout", (PyCFunction)PyCursesWindow_notimeout, METH_VARARGS}, + {"noutrefresh", (PyCFunction)PyCursesWindow_NoOutRefresh, METH_VARARGS}, + /* Backward compatibility alias -- remove in Python 2.3 */ + {"nooutrefresh", (PyCFunction)PyCursesWindow_NoOutRefresh, METH_VARARGS}, + {"overlay", (PyCFunction)PyCursesWindow_Overlay, METH_VARARGS}, + {"overwrite", (PyCFunction)PyCursesWindow_Overwrite, + METH_VARARGS}, + {"putwin", (PyCFunction)PyCursesWindow_PutWin, METH_VARARGS}, + {"redrawln", (PyCFunction)PyCursesWindow_RedrawLine}, + {"redrawwin", (PyCFunction)PyCursesWindow_redrawwin, METH_NOARGS}, + {"refresh", (PyCFunction)PyCursesWindow_Refresh, METH_VARARGS}, +#ifndef STRICT_SYSV_CURSES + {"resize", (PyCFunction)PyCursesWindow_wresize, METH_VARARGS}, +#endif + {"scroll", (PyCFunction)PyCursesWindow_Scroll, METH_VARARGS}, + {"scrollok", (PyCFunction)PyCursesWindow_scrollok, METH_VARARGS}, + {"setscrreg", (PyCFunction)PyCursesWindow_SetScrollRegion, METH_VARARGS}, + {"standend", (PyCFunction)PyCursesWindow_wstandend, METH_NOARGS}, + {"standout", (PyCFunction)PyCursesWindow_wstandout, METH_NOARGS}, + {"subpad", (PyCFunction)PyCursesWindow_SubWin, METH_VARARGS}, + {"subwin", (PyCFunction)PyCursesWindow_SubWin, METH_VARARGS}, + {"syncdown", (PyCFunction)PyCursesWindow_wsyncdown, METH_NOARGS}, + {"syncok", (PyCFunction)PyCursesWindow_syncok, METH_VARARGS}, + {"syncup", (PyCFunction)PyCursesWindow_wsyncup, METH_NOARGS}, + {"timeout", (PyCFunction)PyCursesWindow_wtimeout, METH_VARARGS}, + {"touchline", (PyCFunction)PyCursesWindow_TouchLine, METH_VARARGS}, + {"touchwin", (PyCFunction)PyCursesWindow_touchwin, METH_NOARGS}, + {"untouchwin", (PyCFunction)PyCursesWindow_untouchwin, METH_NOARGS}, + {"vline", (PyCFunction)PyCursesWindow_Vline, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +PyCursesWindow_GetAttr(PyCursesWindowObject *self, char *name) +{ + return Py_FindMethod(PyCursesWindow_Methods, (PyObject *)self, name); +} + +/* -------------------------------------------------------*/ + +PyTypeObject PyCursesWindow_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_curses.curses window", /*tp_name*/ + sizeof(PyCursesWindowObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyCursesWindow_Dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)PyCursesWindow_GetAttr, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + +/********************************************************************* + Global Functions +**********************************************************************/ + +NoArgNoReturnFunction(beep) +NoArgNoReturnFunction(def_prog_mode) +NoArgNoReturnFunction(def_shell_mode) +NoArgNoReturnFunction(doupdate) +NoArgNoReturnFunction(endwin) +NoArgNoReturnFunction(flash) +NoArgNoReturnFunction(nocbreak) +NoArgNoReturnFunction(noecho) +NoArgNoReturnFunction(nonl) +NoArgNoReturnFunction(noraw) +NoArgNoReturnFunction(reset_prog_mode) +NoArgNoReturnFunction(reset_shell_mode) +NoArgNoReturnFunction(resetty) +NoArgNoReturnFunction(savetty) + +NoArgOrFlagNoReturnFunction(cbreak) +NoArgOrFlagNoReturnFunction(echo) +NoArgOrFlagNoReturnFunction(nl) +NoArgOrFlagNoReturnFunction(raw) + +NoArgReturnIntFunction(baudrate) +NoArgReturnIntFunction(termattrs) + +NoArgReturnStringFunction(termname) +NoArgReturnStringFunction(longname) + +NoArgTrueFalseFunction(can_change_color) +NoArgTrueFalseFunction(has_colors) +NoArgTrueFalseFunction(has_ic) +NoArgTrueFalseFunction(has_il) +NoArgTrueFalseFunction(isendwin) +NoArgNoReturnVoidFunction(filter) +NoArgNoReturnVoidFunction(flushinp) +NoArgNoReturnVoidFunction(noqiflush) + +static PyObject * +PyCurses_Color_Content(PyObject *self, PyObject *args) +{ + short color,r,g,b; + + PyCursesInitialised + PyCursesInitialisedColor + + if (!PyArg_ParseTuple(args, "h:color_content", &color)) return NULL; + + if (color_content(color, &r, &g, &b) != ERR) + return Py_BuildValue("(iii)", r, g, b); + else { + PyErr_SetString(PyCursesError, + "Argument 1 was out of range. Check value of COLORS."); + return NULL; + } +} + +static PyObject * +PyCurses_color_pair(PyObject *self, PyObject *args) +{ + int n; + + PyCursesInitialised + PyCursesInitialisedColor + + if (!PyArg_ParseTuple(args, "i:color_pair", &n)) return NULL; + return PyInt_FromLong((long) (n << 8)); +} + +static PyObject * +PyCurses_Curs_Set(PyObject *self, PyObject *args) +{ + int vis,erg; + + PyCursesInitialised + + if (!PyArg_ParseTuple(args, "i:curs_set", &vis)) return NULL; + + erg = curs_set(vis); + if (erg == ERR) return PyCursesCheckERR(erg, "curs_set"); + + return PyInt_FromLong((long) erg); +} + +static PyObject * +PyCurses_Delay_Output(PyObject *self, PyObject *args) +{ + int ms; + + PyCursesInitialised + + if (!PyArg_ParseTuple(args, "i:delay_output", &ms)) return NULL; + + return PyCursesCheckERR(delay_output(ms), "delay_output"); +} + +static PyObject * +PyCurses_EraseChar(PyObject *self) +{ + char ch; + + PyCursesInitialised + + ch = erasechar(); + + return PyString_FromStringAndSize(&ch, 1); +} + +static PyObject * +PyCurses_getsyx(PyObject *self) +{ + int x,y; + + PyCursesInitialised + + getsyx(y, x); + + return Py_BuildValue("(ii)", y, x); +} + +#ifdef NCURSES_MOUSE_VERSION +static PyObject * +PyCurses_GetMouse(PyObject *self) +{ + int rtn; + MEVENT event; + + PyCursesInitialised + + rtn = getmouse( &event ); + if (rtn == ERR) { + PyErr_SetString(PyCursesError, "getmouse() returned ERR"); + return NULL; + } + return Py_BuildValue("(hiiil)", + (short)event.id, + event.x, event.y, event.z, + (long) event.bstate); +} + +static PyObject * +PyCurses_UngetMouse(PyObject *self, PyObject *args) +{ + MEVENT event; + + PyCursesInitialised + if (!PyArg_ParseTuple(args, "(hiiil)", + &event.id, + &event.x, &event.y, &event.z, + (int *) &event.bstate)) + return NULL; + + return PyCursesCheckERR(ungetmouse(&event), "ungetmouse"); +} +#endif + +static PyObject * +PyCurses_GetWin(PyCursesWindowObject *self, PyObject *temp) +{ + WINDOW *win; + + PyCursesInitialised + + if (!PyFile_Check(temp)) { + PyErr_SetString(PyExc_TypeError, "argument must be a file object"); + return NULL; + } + + win = getwin(PyFile_AsFile(temp)); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return PyCursesWindow_New(win); +} + +static PyObject * +PyCurses_HalfDelay(PyObject *self, PyObject *args) +{ + unsigned char tenths; + + PyCursesInitialised + + if (!PyArg_ParseTuple(args, "b:halfdelay", &tenths)) return NULL; + + return PyCursesCheckERR(halfdelay(tenths), "halfdelay"); +} + +#ifndef STRICT_SYSV_CURSES + /* No has_key! */ +static PyObject * PyCurses_has_key(PyObject *self, PyObject *args) +{ + int ch; + + PyCursesInitialised + + if (!PyArg_ParseTuple(args,"i",&ch)) return NULL; + + if (has_key(ch) == FALSE) { + Py_INCREF(Py_False); + return Py_False; + } + Py_INCREF(Py_True); + return Py_True; +} +#endif /* STRICT_SYSV_CURSES */ + +static PyObject * +PyCurses_Init_Color(PyObject *self, PyObject *args) +{ + short color, r, g, b; + + PyCursesInitialised + PyCursesInitialisedColor + + switch(PyTuple_Size(args)) { + case 4: + if (!PyArg_ParseTuple(args, "hhhh;color,r,g,b", &color, &r, &g, &b)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "init_color requires 4 arguments"); + return NULL; + } + + return PyCursesCheckERR(init_color(color, r, g, b), "init_color"); +} + +static PyObject * +PyCurses_Init_Pair(PyObject *self, PyObject *args) +{ + short pair, f, b; + + PyCursesInitialised + PyCursesInitialisedColor + + if (PyTuple_Size(args) != 3) { + PyErr_SetString(PyExc_TypeError, "init_pair requires 3 arguments"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "hhh;pair, f, b", &pair, &f, &b)) return NULL; + + return PyCursesCheckERR(init_pair(pair, f, b), "init_pair"); +} + +static PyObject *ModDict; + +static PyObject * +PyCurses_InitScr(PyObject *self) +{ + WINDOW *win; + PyObject *nlines, *cols; + + if (initialised == TRUE) { + wrefresh(stdscr); + return (PyObject *)PyCursesWindow_New(stdscr); + } + + win = initscr(); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + initialised = initialised_setupterm = TRUE; + +/* This was moved from initcurses() because it core dumped on SGI, + where they're not defined until you've called initscr() */ +#define SetDictInt(string,ch) \ + PyDict_SetItemString(ModDict,string,PyInt_FromLong((long) (ch))); + + /* Here are some graphic symbols you can use */ + SetDictInt("ACS_ULCORNER", (ACS_ULCORNER)); + SetDictInt("ACS_LLCORNER", (ACS_LLCORNER)); + SetDictInt("ACS_URCORNER", (ACS_URCORNER)); + SetDictInt("ACS_LRCORNER", (ACS_LRCORNER)); + SetDictInt("ACS_LTEE", (ACS_LTEE)); + SetDictInt("ACS_RTEE", (ACS_RTEE)); + SetDictInt("ACS_BTEE", (ACS_BTEE)); + SetDictInt("ACS_TTEE", (ACS_TTEE)); + SetDictInt("ACS_HLINE", (ACS_HLINE)); + SetDictInt("ACS_VLINE", (ACS_VLINE)); + SetDictInt("ACS_PLUS", (ACS_PLUS)); +#if !defined(__hpux) || defined(HAVE_NCURSES_H) + /* On HP/UX 11, these are of type cchar_t, which is not an + integral type. If this is a problem on more platforms, a + configure test should be added to determine whether ACS_S1 + is of integral type. */ + SetDictInt("ACS_S1", (ACS_S1)); + SetDictInt("ACS_S9", (ACS_S9)); + SetDictInt("ACS_DIAMOND", (ACS_DIAMOND)); + SetDictInt("ACS_CKBOARD", (ACS_CKBOARD)); + SetDictInt("ACS_DEGREE", (ACS_DEGREE)); + SetDictInt("ACS_PLMINUS", (ACS_PLMINUS)); + SetDictInt("ACS_BULLET", (ACS_BULLET)); + SetDictInt("ACS_LARROW", (ACS_LARROW)); + SetDictInt("ACS_RARROW", (ACS_RARROW)); + SetDictInt("ACS_DARROW", (ACS_DARROW)); + SetDictInt("ACS_UARROW", (ACS_UARROW)); + SetDictInt("ACS_BOARD", (ACS_BOARD)); + SetDictInt("ACS_LANTERN", (ACS_LANTERN)); + SetDictInt("ACS_BLOCK", (ACS_BLOCK)); +#endif + SetDictInt("ACS_BSSB", (ACS_ULCORNER)); + SetDictInt("ACS_SSBB", (ACS_LLCORNER)); + SetDictInt("ACS_BBSS", (ACS_URCORNER)); + SetDictInt("ACS_SBBS", (ACS_LRCORNER)); + SetDictInt("ACS_SBSS", (ACS_RTEE)); + SetDictInt("ACS_SSSB", (ACS_LTEE)); + SetDictInt("ACS_SSBS", (ACS_BTEE)); + SetDictInt("ACS_BSSS", (ACS_TTEE)); + SetDictInt("ACS_BSBS", (ACS_HLINE)); + SetDictInt("ACS_SBSB", (ACS_VLINE)); + SetDictInt("ACS_SSSS", (ACS_PLUS)); + + /* The following are never available with strict SYSV curses */ +#ifdef ACS_S3 + SetDictInt("ACS_S3", (ACS_S3)); +#endif +#ifdef ACS_S7 + SetDictInt("ACS_S7", (ACS_S7)); +#endif +#ifdef ACS_LEQUAL + SetDictInt("ACS_LEQUAL", (ACS_LEQUAL)); +#endif +#ifdef ACS_GEQUAL + SetDictInt("ACS_GEQUAL", (ACS_GEQUAL)); +#endif +#ifdef ACS_PI + SetDictInt("ACS_PI", (ACS_PI)); +#endif +#ifdef ACS_NEQUAL + SetDictInt("ACS_NEQUAL", (ACS_NEQUAL)); +#endif +#ifdef ACS_STERLING + SetDictInt("ACS_STERLING", (ACS_STERLING)); +#endif + + nlines = PyInt_FromLong((long) LINES); + PyDict_SetItemString(ModDict, "LINES", nlines); + Py_DECREF(nlines); + cols = PyInt_FromLong((long) COLS); + PyDict_SetItemString(ModDict, "COLS", cols); + Py_DECREF(cols); + + return (PyObject *)PyCursesWindow_New(win); +} + +static PyObject * +PyCurses_setupterm(PyObject* self, PyObject *args, PyObject* keywds) +{ + int fd = -1; + int err; + char* termstr = NULL; + + static char *kwlist[] = {"term", "fd", NULL}; + + if (!PyArg_ParseTupleAndKeywords( + args,keywds,"|zi:setupterm",kwlist,&termstr,&fd)) { + return NULL; + } + + if (fd == -1) { + PyObject* sys_stdout; + + sys_stdout = PySys_GetObject("stdout"); + + if (sys_stdout == NULL) { + PyErr_SetString( + PyCursesError, + "lost sys.stdout"); + return NULL; + } + + fd = PyObject_AsFileDescriptor(sys_stdout); + + if (fd == -1) { + return NULL; + } + } + + if (setupterm(termstr,fd,&err) == ERR) { + char* s = "setupterm: unknown error"; + + if (err == 0) { + s = "setupterm: could not find terminal"; + } else if (err == -1) { + s = "setupterm: could not find terminfo database"; + } + + PyErr_SetString(PyCursesError,s); + return NULL; + } + + initialised_setupterm = TRUE; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCurses_IntrFlush(PyObject *self, PyObject *args) +{ + int ch; + + PyCursesInitialised + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"i;True(1), False(0)",&ch)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "intrflush requires 1 argument"); + return NULL; + } + + return PyCursesCheckERR(intrflush(NULL,ch), "intrflush"); +} + +#if !defined(__NetBSD__) +static PyObject * +PyCurses_KeyName(PyObject *self, PyObject *args) +{ + const char *knp; + int ch; + + PyCursesInitialised + + if (!PyArg_ParseTuple(args,"i",&ch)) return NULL; + + if (ch < 0) { + PyErr_SetString(PyExc_ValueError, "invalid key number"); + return NULL; + } + knp = keyname(ch); + + return PyString_FromString((knp == NULL) ? "" : (char *)knp); +} +#endif + +static PyObject * +PyCurses_KillChar(PyObject *self) +{ + char ch; + + ch = killchar(); + + return PyString_FromStringAndSize(&ch, 1); +} + +static PyObject * +PyCurses_Meta(PyObject *self, PyObject *args) +{ + int ch; + + PyCursesInitialised + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"i;True(1), False(0)",&ch)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "meta requires 1 argument"); + return NULL; + } + + return PyCursesCheckERR(meta(stdscr, ch), "meta"); +} + +#ifdef NCURSES_MOUSE_VERSION +static PyObject * +PyCurses_MouseInterval(PyObject *self, PyObject *args) +{ + int interval; + PyCursesInitialised + + if (!PyArg_ParseTuple(args,"i;interval",&interval)) + return NULL; + return PyCursesCheckERR(mouseinterval(interval), "mouseinterval"); +} + +static PyObject * +PyCurses_MouseMask(PyObject *self, PyObject *args) +{ + int newmask; + mmask_t oldmask, availmask; + + PyCursesInitialised + if (!PyArg_ParseTuple(args,"i;mousemask",&newmask)) + return NULL; + availmask = mousemask(newmask, &oldmask); + return Py_BuildValue("(ll)", (long)availmask, (long)oldmask); +} +#endif + +static PyObject * +PyCurses_Napms(PyObject *self, PyObject *args) +{ + int ms; + + PyCursesInitialised + if (!PyArg_ParseTuple(args, "i;ms", &ms)) return NULL; + + return Py_BuildValue("i", napms(ms)); +} + + +static PyObject * +PyCurses_NewPad(PyObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols; + + PyCursesInitialised + + if (!PyArg_ParseTuple(args,"ii;nlines,ncols",&nlines,&ncols)) return NULL; + + win = newpad(nlines, ncols); + + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win); +} + +static PyObject * +PyCurses_NewWindow(PyObject *self, PyObject *args) +{ + WINDOW *win; + int nlines, ncols, begin_y=0, begin_x=0; + + PyCursesInitialised + + switch (PyTuple_Size(args)) { + case 2: + if (!PyArg_ParseTuple(args,"ii;nlines,ncols",&nlines,&ncols)) + return NULL; + break; + case 4: + if (!PyArg_ParseTuple(args, "iiii;nlines,ncols,begin_y,begin_x", + &nlines,&ncols,&begin_y,&begin_x)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "newwin requires 2 or 4 arguments"); + return NULL; + } + + win = newwin(nlines,ncols,begin_y,begin_x); + if (win == NULL) { + PyErr_SetString(PyCursesError, catchall_NULL); + return NULL; + } + + return (PyObject *)PyCursesWindow_New(win); +} + +static PyObject * +PyCurses_Pair_Content(PyObject *self, PyObject *args) +{ + short pair,f,b; + + PyCursesInitialised + PyCursesInitialisedColor + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "h;pair", &pair)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "pair_content requires 1 argument"); + return NULL; + } + + if (!pair_content(pair, &f, &b)) { + PyErr_SetString(PyCursesError, + "Argument 1 was out of range. (1..COLOR_PAIRS-1)"); + return NULL; + } + + return Py_BuildValue("(ii)", f, b); +} + +static PyObject * +PyCurses_pair_number(PyObject *self, PyObject *args) +{ + int n; + + PyCursesInitialised + PyCursesInitialisedColor + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args, "i;pairvalue", &n)) return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, + "pair_number requires 1 argument"); + return NULL; + } + + return PyInt_FromLong((long) ((n & A_COLOR) >> 8)); +} + +static PyObject * +PyCurses_Putp(PyObject *self, PyObject *args) +{ + char *str; + + if (!PyArg_ParseTuple(args,"s;str", &str)) return NULL; + return PyCursesCheckERR(putp(str), "putp"); +} + +static PyObject * +PyCurses_QiFlush(PyObject *self, PyObject *args) +{ + int flag = 0; + + PyCursesInitialised + + switch(PyTuple_Size(args)) { + case 0: + qiflush(); + Py_INCREF(Py_None); + return Py_None; + case 1: + if (!PyArg_ParseTuple(args, "i;True(1) or False(0)", &flag)) return NULL; + if (flag) qiflush(); + else noqiflush(); + Py_INCREF(Py_None); + return Py_None; + default: + PyErr_SetString(PyExc_TypeError, "qiflush requires 0 or 1 arguments"); + return NULL; + } +} + +static PyObject * +PyCurses_setsyx(PyObject *self, PyObject *args) +{ + int y,x; + + PyCursesInitialised + + if (PyTuple_Size(args)!=2) { + PyErr_SetString(PyExc_TypeError, "setsyx requires 2 arguments"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "ii;y, x", &y, &x)) return NULL; + + setsyx(y,x); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCurses_Start_Color(PyObject *self) +{ + int code; + PyObject *c, *cp; + + PyCursesInitialised + + code = start_color(); + if (code != ERR) { + initialisedcolors = TRUE; + c = PyInt_FromLong((long) COLORS); + PyDict_SetItemString(ModDict, "COLORS", c); + Py_DECREF(c); + cp = PyInt_FromLong((long) COLOR_PAIRS); + PyDict_SetItemString(ModDict, "COLOR_PAIRS", cp); + Py_DECREF(cp); + Py_INCREF(Py_None); + return Py_None; + } else { + PyErr_SetString(PyCursesError, "start_color() returned ERR"); + return NULL; + } +} + +static PyObject * +PyCurses_tigetflag(PyObject *self, PyObject *args) +{ + char *capname; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "z", &capname)) + return NULL; + + return PyInt_FromLong( (long) tigetflag( capname ) ); +} + +static PyObject * +PyCurses_tigetnum(PyObject *self, PyObject *args) +{ + char *capname; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "z", &capname)) + return NULL; + + return PyInt_FromLong( (long) tigetnum( capname ) ); +} + +static PyObject * +PyCurses_tigetstr(PyObject *self, PyObject *args) +{ + char *capname; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "z", &capname)) + return NULL; + + capname = tigetstr( capname ); + if (capname == 0 || capname == (char*) -1) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString( capname ); +} + +static PyObject * +PyCurses_tparm(PyObject *self, PyObject *args) +{ + char* fmt; + char* result = NULL; + int i1=0,i2=0,i3=0,i4=0,i5=0,i6=0,i7=0,i8=0,i9=0; + + PyCursesSetupTermCalled; + + if (!PyArg_ParseTuple(args, "s|iiiiiiiii:tparm", + &fmt, &i1, &i2, &i3, &i4, + &i5, &i6, &i7, &i8, &i9)) { + return NULL; + } + + result = tparm(fmt,i1,i2,i3,i4,i5,i6,i7,i8,i9); + + return PyString_FromString(result); +} + +static PyObject * +PyCurses_TypeAhead(PyObject *self, PyObject *args) +{ + int fd; + + PyCursesInitialised + + if (!PyArg_ParseTuple(args,"i;fd",&fd)) return NULL; + + PyCursesCheckERR(typeahead( fd ), "typeahead"); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyCurses_UnCtrl(PyObject *self, PyObject *args) +{ + PyObject *temp; + chtype ch; + + PyCursesInitialised + + if (!PyArg_ParseTuple(args,"O;ch or int",&temp)) return NULL; + + if (PyInt_Check(temp)) + ch = (chtype) PyInt_AsLong(temp); + else if (PyString_Check(temp)) + ch = (chtype) *PyString_AsString(temp); + else { + PyErr_SetString(PyExc_TypeError, "argument must be a ch or an int"); + return NULL; + } + + return PyString_FromString(unctrl(ch)); +} + +static PyObject * +PyCurses_UngetCh(PyObject *self, PyObject *args) +{ + PyObject *temp; + int ch; + + PyCursesInitialised + + if (!PyArg_ParseTuple(args,"O;ch or int",&temp)) return NULL; + + if (PyInt_Check(temp)) + ch = (int) PyInt_AsLong(temp); + else if (PyString_Check(temp)) + ch = (int) *PyString_AsString(temp); + else { + PyErr_SetString(PyExc_TypeError, "argument must be a ch or an int"); + return NULL; + } + + return PyCursesCheckERR(ungetch(ch), "ungetch"); +} + +static PyObject * +PyCurses_Use_Env(PyObject *self, PyObject *args) +{ + int flag; + + PyCursesInitialised + + switch(PyTuple_Size(args)) { + case 1: + if (!PyArg_ParseTuple(args,"i;True(1), False(0)",&flag)) + return NULL; + break; + default: + PyErr_SetString(PyExc_TypeError, "use_env requires 1 argument"); + return NULL; + } + use_env(flag); + Py_INCREF(Py_None); + return Py_None; +} + +/* List of functions defined in the module */ + +static PyMethodDef PyCurses_methods[] = { + {"baudrate", (PyCFunction)PyCurses_baudrate, METH_NOARGS}, + {"beep", (PyCFunction)PyCurses_beep, METH_NOARGS}, + {"can_change_color", (PyCFunction)PyCurses_can_change_color, METH_NOARGS}, + {"cbreak", (PyCFunction)PyCurses_cbreak, METH_VARARGS}, + {"color_content", (PyCFunction)PyCurses_Color_Content, METH_VARARGS}, + {"color_pair", (PyCFunction)PyCurses_color_pair, METH_VARARGS}, + {"curs_set", (PyCFunction)PyCurses_Curs_Set, METH_VARARGS}, + {"def_prog_mode", (PyCFunction)PyCurses_def_prog_mode, METH_NOARGS}, + {"def_shell_mode", (PyCFunction)PyCurses_def_shell_mode, METH_NOARGS}, + {"delay_output", (PyCFunction)PyCurses_Delay_Output, METH_VARARGS}, + {"doupdate", (PyCFunction)PyCurses_doupdate, METH_NOARGS}, + {"echo", (PyCFunction)PyCurses_echo, METH_VARARGS}, + {"endwin", (PyCFunction)PyCurses_endwin, METH_NOARGS}, + {"erasechar", (PyCFunction)PyCurses_EraseChar, METH_NOARGS}, + {"filter", (PyCFunction)PyCurses_filter, METH_NOARGS}, + {"flash", (PyCFunction)PyCurses_flash, METH_NOARGS}, + {"flushinp", (PyCFunction)PyCurses_flushinp, METH_NOARGS}, +#ifdef NCURSES_MOUSE_VERSION + {"getmouse", (PyCFunction)PyCurses_GetMouse, METH_NOARGS}, + {"ungetmouse", (PyCFunction)PyCurses_UngetMouse, METH_VARARGS}, +#endif + {"getsyx", (PyCFunction)PyCurses_getsyx, METH_NOARGS}, + {"getwin", (PyCFunction)PyCurses_GetWin, METH_O}, + {"has_colors", (PyCFunction)PyCurses_has_colors, METH_NOARGS}, + {"has_ic", (PyCFunction)PyCurses_has_ic, METH_NOARGS}, + {"has_il", (PyCFunction)PyCurses_has_il, METH_NOARGS}, +#ifndef STRICT_SYSV_CURSES + {"has_key", (PyCFunction)PyCurses_has_key, METH_VARARGS}, +#endif + {"halfdelay", (PyCFunction)PyCurses_HalfDelay, METH_VARARGS}, + {"init_color", (PyCFunction)PyCurses_Init_Color, METH_VARARGS}, + {"init_pair", (PyCFunction)PyCurses_Init_Pair, METH_VARARGS}, + {"initscr", (PyCFunction)PyCurses_InitScr, METH_NOARGS}, + {"intrflush", (PyCFunction)PyCurses_IntrFlush, METH_VARARGS}, + {"isendwin", (PyCFunction)PyCurses_isendwin, METH_NOARGS}, +#if !defined(__NetBSD__) + {"keyname", (PyCFunction)PyCurses_KeyName, METH_VARARGS}, +#endif + {"killchar", (PyCFunction)PyCurses_KillChar, METH_NOARGS}, + {"longname", (PyCFunction)PyCurses_longname, METH_NOARGS}, + {"meta", (PyCFunction)PyCurses_Meta, METH_VARARGS}, +#ifdef NCURSES_MOUSE_VERSION + {"mouseinterval", (PyCFunction)PyCurses_MouseInterval, METH_VARARGS}, + {"mousemask", (PyCFunction)PyCurses_MouseMask, METH_VARARGS}, +#endif + {"napms", (PyCFunction)PyCurses_Napms, METH_VARARGS}, + {"newpad", (PyCFunction)PyCurses_NewPad, METH_VARARGS}, + {"newwin", (PyCFunction)PyCurses_NewWindow, METH_VARARGS}, + {"nl", (PyCFunction)PyCurses_nl, METH_VARARGS}, + {"nocbreak", (PyCFunction)PyCurses_nocbreak, METH_NOARGS}, + {"noecho", (PyCFunction)PyCurses_noecho, METH_NOARGS}, + {"nonl", (PyCFunction)PyCurses_nonl, METH_NOARGS}, + {"noqiflush", (PyCFunction)PyCurses_noqiflush, METH_NOARGS}, + {"noraw", (PyCFunction)PyCurses_noraw, METH_NOARGS}, + {"pair_content", (PyCFunction)PyCurses_Pair_Content, METH_VARARGS}, + {"pair_number", (PyCFunction)PyCurses_pair_number, METH_VARARGS}, + {"putp", (PyCFunction)PyCurses_Putp, METH_VARARGS}, + {"qiflush", (PyCFunction)PyCurses_QiFlush, METH_VARARGS}, + {"raw", (PyCFunction)PyCurses_raw, METH_VARARGS}, + {"reset_prog_mode", (PyCFunction)PyCurses_reset_prog_mode, METH_NOARGS}, + {"reset_shell_mode", (PyCFunction)PyCurses_reset_shell_mode, METH_NOARGS}, + {"resetty", (PyCFunction)PyCurses_resetty, METH_NOARGS}, + {"savetty", (PyCFunction)PyCurses_savetty, METH_NOARGS}, + {"setsyx", (PyCFunction)PyCurses_setsyx, METH_VARARGS}, + {"setupterm", (PyCFunction)PyCurses_setupterm, + METH_VARARGS|METH_KEYWORDS}, + {"start_color", (PyCFunction)PyCurses_Start_Color, METH_NOARGS}, + {"termattrs", (PyCFunction)PyCurses_termattrs, METH_NOARGS}, + {"termname", (PyCFunction)PyCurses_termname, METH_NOARGS}, + {"tigetflag", (PyCFunction)PyCurses_tigetflag, METH_VARARGS}, + {"tigetnum", (PyCFunction)PyCurses_tigetnum, METH_VARARGS}, + {"tigetstr", (PyCFunction)PyCurses_tigetstr, METH_VARARGS}, + {"tparm", (PyCFunction)PyCurses_tparm, METH_VARARGS}, + {"typeahead", (PyCFunction)PyCurses_TypeAhead, METH_VARARGS}, + {"unctrl", (PyCFunction)PyCurses_UnCtrl, METH_VARARGS}, + {"ungetch", (PyCFunction)PyCurses_UngetCh, METH_VARARGS}, + {"use_env", (PyCFunction)PyCurses_Use_Env, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* Initialization function for the module */ + +PyMODINIT_FUNC +init_curses(void) +{ + PyObject *m, *d, *v, *c_api_object; + static void *PyCurses_API[PyCurses_API_pointers]; + + /* Initialize object type */ + PyCursesWindow_Type.ob_type = &PyType_Type; + + /* Initialize the C API pointer array */ + PyCurses_API[0] = (void *)&PyCursesWindow_Type; + PyCurses_API[1] = (void *)func_PyCursesSetupTermCalled; + PyCurses_API[2] = (void *)func_PyCursesInitialised; + PyCurses_API[3] = (void *)func_PyCursesInitialisedColor; + + /* Create the module and add the functions */ + m = Py_InitModule("_curses", PyCurses_methods); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + ModDict = d; /* For PyCurses_InitScr to use later */ + + /* Add a CObject for the C API */ + c_api_object = PyCObject_FromVoidPtr((void *)PyCurses_API, NULL); + PyDict_SetItemString(d, "_C_API", c_api_object); + Py_DECREF(c_api_object); + + /* For exception curses.error */ + PyCursesError = PyErr_NewException("_curses.error", NULL, NULL); + PyDict_SetItemString(d, "error", PyCursesError); + + /* Make the version available */ + v = PyString_FromString(PyCursesVersion); + PyDict_SetItemString(d, "version", v); + PyDict_SetItemString(d, "__version__", v); + Py_DECREF(v); + + SetDictInt("ERR", ERR); + SetDictInt("OK", OK); + + /* Here are some attributes you can add to chars to print */ + + SetDictInt("A_ATTRIBUTES", A_ATTRIBUTES); + SetDictInt("A_NORMAL", A_NORMAL); + SetDictInt("A_STANDOUT", A_STANDOUT); + SetDictInt("A_UNDERLINE", A_UNDERLINE); + SetDictInt("A_REVERSE", A_REVERSE); + SetDictInt("A_BLINK", A_BLINK); + SetDictInt("A_DIM", A_DIM); + SetDictInt("A_BOLD", A_BOLD); + SetDictInt("A_ALTCHARSET", A_ALTCHARSET); +#if !defined(__NetBSD__) + SetDictInt("A_INVIS", A_INVIS); +#endif + SetDictInt("A_PROTECT", A_PROTECT); + SetDictInt("A_CHARTEXT", A_CHARTEXT); + SetDictInt("A_COLOR", A_COLOR); + + /* The following are never available with strict SYSV curses */ +#ifdef A_HORIZONTAL + SetDictInt("A_HORIZONTAL", A_HORIZONTAL); +#endif +#ifdef A_LEFT + SetDictInt("A_LEFT", A_LEFT); +#endif +#ifdef A_LOW + SetDictInt("A_LOW", A_LOW); +#endif +#ifdef A_RIGHT + SetDictInt("A_RIGHT", A_RIGHT); +#endif +#ifdef A_TOP + SetDictInt("A_TOP", A_TOP); +#endif +#ifdef A_VERTICAL + SetDictInt("A_VERTICAL", A_VERTICAL); +#endif + + SetDictInt("COLOR_BLACK", COLOR_BLACK); + SetDictInt("COLOR_RED", COLOR_RED); + SetDictInt("COLOR_GREEN", COLOR_GREEN); + SetDictInt("COLOR_YELLOW", COLOR_YELLOW); + SetDictInt("COLOR_BLUE", COLOR_BLUE); + SetDictInt("COLOR_MAGENTA", COLOR_MAGENTA); + SetDictInt("COLOR_CYAN", COLOR_CYAN); + SetDictInt("COLOR_WHITE", COLOR_WHITE); + +#ifdef NCURSES_MOUSE_VERSION + /* Mouse-related constants */ + SetDictInt("BUTTON1_PRESSED", BUTTON1_PRESSED); + SetDictInt("BUTTON1_RELEASED", BUTTON1_RELEASED); + SetDictInt("BUTTON1_CLICKED", BUTTON1_CLICKED); + SetDictInt("BUTTON1_DOUBLE_CLICKED", BUTTON1_DOUBLE_CLICKED); + SetDictInt("BUTTON1_TRIPLE_CLICKED", BUTTON1_TRIPLE_CLICKED); + + SetDictInt("BUTTON2_PRESSED", BUTTON2_PRESSED); + SetDictInt("BUTTON2_RELEASED", BUTTON2_RELEASED); + SetDictInt("BUTTON2_CLICKED", BUTTON2_CLICKED); + SetDictInt("BUTTON2_DOUBLE_CLICKED", BUTTON2_DOUBLE_CLICKED); + SetDictInt("BUTTON2_TRIPLE_CLICKED", BUTTON2_TRIPLE_CLICKED); + + SetDictInt("BUTTON3_PRESSED", BUTTON3_PRESSED); + SetDictInt("BUTTON3_RELEASED", BUTTON3_RELEASED); + SetDictInt("BUTTON3_CLICKED", BUTTON3_CLICKED); + SetDictInt("BUTTON3_DOUBLE_CLICKED", BUTTON3_DOUBLE_CLICKED); + SetDictInt("BUTTON3_TRIPLE_CLICKED", BUTTON3_TRIPLE_CLICKED); + + SetDictInt("BUTTON4_PRESSED", BUTTON4_PRESSED); + SetDictInt("BUTTON4_RELEASED", BUTTON4_RELEASED); + SetDictInt("BUTTON4_CLICKED", BUTTON4_CLICKED); + SetDictInt("BUTTON4_DOUBLE_CLICKED", BUTTON4_DOUBLE_CLICKED); + SetDictInt("BUTTON4_TRIPLE_CLICKED", BUTTON4_TRIPLE_CLICKED); + + SetDictInt("BUTTON_SHIFT", BUTTON_SHIFT); + SetDictInt("BUTTON_CTRL", BUTTON_CTRL); + SetDictInt("BUTTON_ALT", BUTTON_ALT); + + SetDictInt("ALL_MOUSE_EVENTS", ALL_MOUSE_EVENTS); + SetDictInt("REPORT_MOUSE_POSITION", REPORT_MOUSE_POSITION); +#endif + /* Now set everything up for KEY_ variables */ + { + int key; + char *key_n; + char *key_n2; +#if !defined(__NetBSD__) + for (key=KEY_MIN;key < KEY_MAX; key++) { + key_n = (char *)keyname(key); + if (key_n == NULL || strcmp(key_n,"UNKNOWN KEY")==0) + continue; + if (strncmp(key_n,"KEY_F(",6)==0) { + char *p1, *p2; + key_n2 = malloc(strlen(key_n)+1); + p1 = key_n; + p2 = key_n2; + while (*p1) { + if (*p1 != '(' && *p1 != ')') { + *p2 = *p1; + p2++; + } + p1++; + } + *p2 = (char)0; + } else + key_n2 = key_n; + PyDict_SetItemString(d,key_n2,PyInt_FromLong((long) key)); + if (key_n2 != key_n) + free(key_n2); + } +#endif + SetDictInt("KEY_MIN", KEY_MIN); + SetDictInt("KEY_MAX", KEY_MAX); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_hotshot.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_hotshot.c new file mode 100644 index 00000000..9fad9b06 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_hotshot.c @@ -0,0 +1,1666 @@ +/* + * This is the High Performance Python Profiler portion of HotShot. + */ + +#include "Python.h" +#include "compile.h" +#include "eval.h" +#include "frameobject.h" +#include "structmember.h" + +/* + * Which timer to use should be made more configurable, but that should not + * be difficult. This will do for now. + */ +#ifdef MS_WINDOWS +#ifdef MS_XBOX +#include +#else +#include +#endif +#include /* for getcwd() */ +typedef __int64 hs_time; +#define GETTIMEOFDAY(P_HS_TIME) \ + { LARGE_INTEGER _temp; \ + QueryPerformanceCounter(&_temp); \ + *(P_HS_TIME) = _temp.QuadPart; } + + +#else +#ifndef HAVE_GETTIMEOFDAY +#error "This module requires gettimeofday() on non-Windows platforms!" +#endif +#if defined(macintosh) || (defined(PYOS_OS2) && defined(PYCC_GCC)) +#include +#else +#include +#include +#endif +typedef struct timeval hs_time; +#endif + +#if !defined(__cplusplus) && !defined(inline) +#ifdef __GNUC__ +#define inline __inline +#endif +#endif + +#ifndef inline +#define inline +#endif + +#define BUFFERSIZE 10240 + +#ifdef macintosh +#define PATH_MAX 254 +#endif + +#if defined(PYOS_OS2) && defined(PYCC_GCC) +#define PATH_MAX 260 +#endif + +#if defined(__sgi) && _COMPILER_VERSION>700 && !defined(PATH_MAX) +/* fix PATH_MAX not being defined with MIPSPro 7.x + if mode is ANSI C (default) */ +#define PATH_MAX 1024 +#endif + +#ifndef PATH_MAX +# ifdef MAX_PATH +# define PATH_MAX MAX_PATH +# elif defined (_POSIX_PATH_MAX) +# define PATH_MAX _POSIX_PATH_MAX +# else +# error "Need a defn. for PATH_MAX in _hotshot.c" +# endif +#endif + +typedef struct { + PyObject_HEAD + PyObject *filemap; + PyObject *logfilename; + int index; + unsigned char buffer[BUFFERSIZE]; + FILE *logfp; + int lineevents; + int linetimings; + int frametimings; + /* size_t filled; */ + int active; + int next_fileno; + hs_time prev_timeofday; +} ProfilerObject; + +typedef struct { + PyObject_HEAD + PyObject *info; + FILE *logfp; + int linetimings; + int frametimings; +} LogReaderObject; + +static PyObject * ProfilerError = NULL; + + +#ifndef MS_WINDOWS +#ifdef GETTIMEOFDAY_NO_TZ +#define GETTIMEOFDAY(ptv) gettimeofday((ptv)) +#else +#define GETTIMEOFDAY(ptv) gettimeofday((ptv), (struct timezone *)NULL) +#endif +#endif + + +/* The log reader... */ + +PyDoc_STRVAR(logreader_close__doc__, +"close()\n" +"Close the log file, preventing additional records from being read."); + +static PyObject * +logreader_close(LogReaderObject *self, PyObject *args) +{ + if (self->logfp != NULL) { + fclose(self->logfp); + self->logfp = NULL; + } + Py_INCREF(Py_None); + + return Py_None; +} + +PyDoc_STRVAR(logreader_fileno__doc__, +"fileno() -> file descriptor\n" +"Returns the file descriptor for the log file, if open.\n" +"Raises ValueError if the log file is closed."); + +static PyObject * +logreader_fileno(LogReaderObject *self) +{ + if (self->logfp == NULL) { + PyErr_SetString(PyExc_ValueError, + "logreader's file object already closed"); + return NULL; + } + return PyInt_FromLong(fileno(self->logfp)); +} + + +/* Log File Format + * --------------- + * + * The log file consists of a sequence of variable-length records. + * Each record is identified with a record type identifier in two + * bits of the first byte. The two bits are the "least significant" + * bits of the byte. + * + * Low bits: Opcode: Meaning: + * 0x00 ENTER enter a frame + * 0x01 EXIT exit a frame + * 0x02 LINENO execution moved onto a different line + * 0x03 OTHER more bits are needed to deecode + * + * If the type is OTHER, the record is not packed so tightly, and the + * remaining bits are used to disambiguate the record type. These + * records are not used as frequently so compaction is not an issue. + * Each of the first three record types has a highly tailored + * structure that allows it to be packed tightly. + * + * The OTHER records have the following identifiers: + * + * First byte: Opcode: Meaning: + * 0x13 ADD_INFO define a key/value pair + * 0x23 DEFINE_FILE define an int->filename mapping + * 0x33 LINE_TIMES indicates if LINENO events have tdeltas + * 0x43 DEFINE_FUNC define a (fileno,lineno)->funcname mapping + * 0x53 FRAME_TIMES indicates if ENTER/EXIT events have tdeltas + * + * Packed Integers + * + * "Packed integers" are non-negative integer values encoded as a + * sequence of bytes. Each byte is encoded such that the most + * significant bit is set if the next byte is also part of the + * integer. Each byte provides bits to the least-significant end of + * the result; the accumulated value must be shifted up to place the + * new bits into the result. + * + * "Modified packed integers" are packed integers where only a portion + * of the first byte is used. In the rest of the specification, these + * are referred to as "MPI(n,name)", where "n" is the number of bits + * discarded from the least-signicant positions of the byte, and + * "name" is a name being given to those "discarded" bits, since they + * are a field themselves. + * + * ENTER records: + * + * MPI(2,type) fileno -- type is 0x00 + * PI lineno + * PI tdelta -- iff frame times are enabled + * + * EXIT records + * + * MPI(2,type) tdelta -- type is 0x01; tdelta will be 0 + * if frame times are disabled + * + * LINENO records + * + * MPI(2,type) lineno -- type is 0x02 + * PI tdelta -- iff LINENO includes it + * + * ADD_INFO records + * + * BYTE type -- always 0x13 + * PI len1 -- length of first string + * BYTE string1[len1] -- len1 bytes of string data + * PI len2 -- length of second string + * BYTE string2[len2] -- len2 bytes of string data + * + * DEFINE_FILE records + * + * BYTE type -- always 0x23 + * PI fileno + * PI len -- length of filename + * BYTE filename[len] -- len bytes of string data + * + * DEFINE_FUNC records + * + * BYTE type -- always 0x43 + * PI fileno + * PI lineno + * PI len -- length of funcname + * BYTE funcname[len] -- len bytes of string data + * + * LINE_TIMES records + * + * This record can be used only before the start of ENTER/EXIT/LINENO + * records. If have_tdelta is true, LINENO records will include the + * tdelta field, otherwise it will be omitted. If this record is not + * given, LINENO records will not contain the tdelta field. + * + * BYTE type -- always 0x33 + * BYTE have_tdelta -- 0 if LINENO does *not* have + * timing information + * FRAME_TIMES records + * + * This record can be used only before the start of ENTER/EXIT/LINENO + * records. If have_tdelta is true, ENTER and EXIT records will + * include the tdelta field, otherwise it will be omitted. If this + * record is not given, ENTER and EXIT records will contain the tdelta + * field. + * + * BYTE type -- always 0x53 + * BYTE have_tdelta -- 0 if ENTER/EXIT do *not* have + * timing information + */ + +#define WHAT_ENTER 0x00 +#define WHAT_EXIT 0x01 +#define WHAT_LINENO 0x02 +#define WHAT_OTHER 0x03 /* only used in decoding */ +#define WHAT_ADD_INFO 0x13 +#define WHAT_DEFINE_FILE 0x23 +#define WHAT_LINE_TIMES 0x33 +#define WHAT_DEFINE_FUNC 0x43 +#define WHAT_FRAME_TIMES 0x53 + +#define ERR_NONE 0 +#define ERR_EOF -1 +#define ERR_EXCEPTION -2 +#define ERR_BAD_RECTYPE -3 + +#define PISIZE (sizeof(int) + 1) +#define MPISIZE (PISIZE + 1) + +/* Maximum size of "normal" events -- nothing that contains string data */ +#define MAXEVENTSIZE (MPISIZE + PISIZE*2) + + +/* Unpack a packed integer; if "discard" is non-zero, unpack a modified + * packed integer with "discard" discarded bits. + */ +static int +unpack_packed_int(LogReaderObject *self, int *pvalue, int discard) +{ + int c; + int accum = 0; + int bits = 0; + int cont; + + do { + /* read byte */ + if ((c = fgetc(self->logfp)) == EOF) + return ERR_EOF; + accum |= ((c & 0x7F) >> discard) << bits; + bits += (7 - discard); + cont = c & 0x80; + discard = 0; + } while (cont); + + *pvalue = accum; + + return 0; +} + +/* Unpack a string, which is encoded as a packed integer giving the + * length of the string, followed by the string data. + */ +static int +unpack_string(LogReaderObject *self, PyObject **pvalue) +{ + int i; + int len; + int err; + int ch; + char *buf; + + if ((err = unpack_packed_int(self, &len, 0))) + return err; + + buf = malloc(len); + for (i=0; i < len; i++) { + ch = fgetc(self->logfp); + buf[i] = ch; + if (ch == EOF) { + free(buf); + return ERR_EOF; + } + } + *pvalue = PyString_FromStringAndSize(buf, len); + free(buf); + if (*pvalue == NULL) { + return ERR_EXCEPTION; + } + return 0; +} + + +static int +unpack_add_info(LogReaderObject *self) +{ + PyObject *key; + PyObject *value = NULL; + int err; + + err = unpack_string(self, &key); + if (!err) { + err = unpack_string(self, &value); + if (err) + Py_DECREF(key); + else { + PyObject *list = PyDict_GetItem(self->info, key); + if (list == NULL) { + list = PyList_New(0); + if (list == NULL) { + err = ERR_EXCEPTION; + goto finally; + } + if (PyDict_SetItem(self->info, key, list)) { + err = ERR_EXCEPTION; + goto finally; + } + } + if (PyList_Append(list, value)) + err = ERR_EXCEPTION; + } + } + finally: + Py_XDECREF(key); + Py_XDECREF(value); + return err; +} + + +static void +eof_error(LogReaderObject *self) +{ + fclose(self->logfp); + self->logfp = NULL; + PyErr_SetString(PyExc_EOFError, + "end of file with incomplete profile record"); +} + +static PyObject * +logreader_tp_iternext(LogReaderObject *self) +{ + int c; + int what; + int err = ERR_NONE; + int lineno = -1; + int fileno = -1; + int tdelta = -1; + PyObject *s1 = NULL, *s2 = NULL; + PyObject *result = NULL; +#if 0 + unsigned char b0, b1; +#endif + + if (self->logfp == NULL) { + PyErr_SetString(ProfilerError, + "cannot iterate over closed LogReader object"); + return NULL; + } + +restart: + /* decode the record type */ + if ((c = fgetc(self->logfp)) == EOF) { + fclose(self->logfp); + self->logfp = NULL; + return NULL; + } + what = c & WHAT_OTHER; + if (what == WHAT_OTHER) + what = c; /* need all the bits for type */ + else + ungetc(c, self->logfp); /* type byte includes packed int */ + + switch (what) { + case WHAT_ENTER: + err = unpack_packed_int(self, &fileno, 2); + if (!err) { + err = unpack_packed_int(self, &lineno, 0); + if (self->frametimings && !err) + err = unpack_packed_int(self, &tdelta, 0); + } + break; + case WHAT_EXIT: + err = unpack_packed_int(self, &tdelta, 2); + break; + case WHAT_LINENO: + err = unpack_packed_int(self, &lineno, 2); + if (self->linetimings && !err) + err = unpack_packed_int(self, &tdelta, 0); + break; + case WHAT_ADD_INFO: + err = unpack_add_info(self); + break; + case WHAT_DEFINE_FILE: + err = unpack_packed_int(self, &fileno, 0); + if (!err) { + err = unpack_string(self, &s1); + if (!err) { + Py_INCREF(Py_None); + s2 = Py_None; + } + } + break; + case WHAT_DEFINE_FUNC: + err = unpack_packed_int(self, &fileno, 0); + if (!err) { + err = unpack_packed_int(self, &lineno, 0); + if (!err) + err = unpack_string(self, &s1); + } + break; + case WHAT_LINE_TIMES: + if ((c = fgetc(self->logfp)) == EOF) + err = ERR_EOF; + else { + self->linetimings = c ? 1 : 0; + goto restart; + } + break; + case WHAT_FRAME_TIMES: + if ((c = fgetc(self->logfp)) == EOF) + err = ERR_EOF; + else { + self->frametimings = c ? 1 : 0; + goto restart; + } + break; + default: + err = ERR_BAD_RECTYPE; + } + if (err == ERR_BAD_RECTYPE) { + PyErr_SetString(PyExc_ValueError, + "unknown record type in log file"); + } + else if (err == ERR_EOF) { + eof_error(self); + } + else if (!err) { + result = PyTuple_New(4); + PyTuple_SET_ITEM(result, 0, PyInt_FromLong(what)); + PyTuple_SET_ITEM(result, 2, PyInt_FromLong(fileno)); + if (s1 == NULL) + PyTuple_SET_ITEM(result, 1, PyInt_FromLong(tdelta)); + else + PyTuple_SET_ITEM(result, 1, s1); + if (s2 == NULL) + PyTuple_SET_ITEM(result, 3, PyInt_FromLong(lineno)); + else + PyTuple_SET_ITEM(result, 3, s2); + } + /* The only other case is err == ERR_EXCEPTION, in which case the + * exception is already set. + */ +#if 0 + b0 = self->buffer[self->index]; + b1 = self->buffer[self->index + 1]; + if (b0 & 1) { + /* This is a line-number event. */ + what = PyTrace_LINE; + lineno = ((b0 & ~1) << 7) + b1; + self->index += 2; + } + else { + what = (b0 & 0x0E) >> 1; + tdelta = ((b0 & 0xF0) << 4) + b1; + if (what == PyTrace_CALL) { + /* we know there's a 2-byte file ID & 2-byte line number */ + fileno = ((self->buffer[self->index + 2] << 8) + + self->buffer[self->index + 3]); + lineno = ((self->buffer[self->index + 4] << 8) + + self->buffer[self->index + 5]); + self->index += 6; + } + else + self->index += 2; + } +#endif + return result; +} + +static void +logreader_dealloc(LogReaderObject *self) +{ + if (self->logfp != NULL) { + fclose(self->logfp); + self->logfp = NULL; + } + PyObject_Del(self); +} + +static PyObject * +logreader_sq_item(LogReaderObject *self, int index) +{ + PyObject *result = logreader_tp_iternext(self); + if (result == NULL && !PyErr_Occurred()) { + PyErr_SetString(PyExc_IndexError, "no more events in log"); + return NULL; + } + return result; +} + +static void +do_stop(ProfilerObject *self); + +static int +flush_data(ProfilerObject *self) +{ + /* Need to dump data to the log file... */ + size_t written = fwrite(self->buffer, 1, self->index, self->logfp); + if (written == (size_t)self->index) + self->index = 0; + else { + memmove(self->buffer, &self->buffer[written], + self->index - written); + self->index -= written; + if (written == 0) { + char *s = PyString_AsString(self->logfilename); + PyErr_SetFromErrnoWithFilename(PyExc_IOError, s); + do_stop(self); + return -1; + } + } + if (written > 0) { + if (fflush(self->logfp)) { + char *s = PyString_AsString(self->logfilename); + PyErr_SetFromErrnoWithFilename(PyExc_IOError, s); + do_stop(self); + return -1; + } + } + return 0; +} + +static inline int +pack_packed_int(ProfilerObject *self, int value) +{ + unsigned char partial; + + do { + partial = value & 0x7F; + value >>= 7; + if (value) + partial |= 0x80; + self->buffer[self->index] = partial; + self->index++; + } while (value); + return 0; +} + +/* Encode a modified packed integer, with a subfield of modsize bits + * containing the value "subfield". The value of subfield is not + * checked to ensure it actually fits in modsize bits. + */ +static inline int +pack_modified_packed_int(ProfilerObject *self, int value, + int modsize, int subfield) +{ + const int maxvalues[] = {-1, 1, 3, 7, 15, 31, 63, 127}; + + int bits = 7 - modsize; + int partial = value & maxvalues[bits]; + unsigned char b = subfield | (partial << modsize); + + if (partial != value) { + b |= 0x80; + self->buffer[self->index] = b; + self->index++; + return pack_packed_int(self, value >> bits); + } + self->buffer[self->index] = b; + self->index++; + return 0; +} + +static int +pack_string(ProfilerObject *self, const char *s, int len) +{ + if (len + PISIZE + self->index >= BUFFERSIZE) { + if (flush_data(self) < 0) + return -1; + } + if (pack_packed_int(self, len) < 0) + return -1; + memcpy(self->buffer + self->index, s, len); + self->index += len; + return 0; +} + +static int +pack_add_info(ProfilerObject *self, const char *s1, const char *s2) +{ + int len1 = strlen(s1); + int len2 = strlen(s2); + + if (len1 + len2 + PISIZE*2 + 1 + self->index >= BUFFERSIZE) { + if (flush_data(self) < 0) + return -1; + } + self->buffer[self->index] = WHAT_ADD_INFO; + self->index++; + if (pack_string(self, s1, len1) < 0) + return -1; + return pack_string(self, s2, len2); +} + +static int +pack_define_file(ProfilerObject *self, int fileno, const char *filename) +{ + int len = strlen(filename); + + if (len + PISIZE*2 + 1 + self->index >= BUFFERSIZE) { + if (flush_data(self) < 0) + return -1; + } + self->buffer[self->index] = WHAT_DEFINE_FILE; + self->index++; + if (pack_packed_int(self, fileno) < 0) + return -1; + return pack_string(self, filename, len); +} + +static int +pack_define_func(ProfilerObject *self, int fileno, int lineno, + const char *funcname) +{ + int len = strlen(funcname); + + if (len + PISIZE*3 + 1 + self->index >= BUFFERSIZE) { + if (flush_data(self) < 0) + return -1; + } + self->buffer[self->index] = WHAT_DEFINE_FUNC; + self->index++; + if (pack_packed_int(self, fileno) < 0) + return -1; + if (pack_packed_int(self, lineno) < 0) + return -1; + return pack_string(self, funcname, len); +} + +static int +pack_line_times(ProfilerObject *self) +{ + if (2 + self->index >= BUFFERSIZE) { + if (flush_data(self) < 0) + return -1; + } + self->buffer[self->index] = WHAT_LINE_TIMES; + self->buffer[self->index + 1] = self->linetimings ? 1 : 0; + self->index += 2; + return 0; +} + +static int +pack_frame_times(ProfilerObject *self) +{ + if (2 + self->index >= BUFFERSIZE) { + if (flush_data(self) < 0) + return -1; + } + self->buffer[self->index] = WHAT_FRAME_TIMES; + self->buffer[self->index + 1] = self->frametimings ? 1 : 0; + self->index += 2; + return 0; +} + +static inline int +pack_enter(ProfilerObject *self, int fileno, int tdelta, int lineno) +{ + if (MPISIZE + PISIZE*2 + self->index >= BUFFERSIZE) { + if (flush_data(self) < 0) + return -1; + } + pack_modified_packed_int(self, fileno, 2, WHAT_ENTER); + pack_packed_int(self, lineno); + if (self->frametimings) + return pack_packed_int(self, tdelta); + else + return 0; +} + +static inline int +pack_exit(ProfilerObject *self, int tdelta) +{ + if (MPISIZE + self->index >= BUFFERSIZE) { + if (flush_data(self) < 0) + return -1; + } + if (self->frametimings) + return pack_modified_packed_int(self, tdelta, 2, WHAT_EXIT); + self->buffer[self->index] = WHAT_EXIT; + self->index++; + return 0; +} + +static inline int +pack_lineno(ProfilerObject *self, int lineno) +{ + if (MPISIZE + self->index >= BUFFERSIZE) { + if (flush_data(self) < 0) + return -1; + } + return pack_modified_packed_int(self, lineno, 2, WHAT_LINENO); +} + +static inline int +pack_lineno_tdelta(ProfilerObject *self, int lineno, int tdelta) +{ + if (MPISIZE + PISIZE + self->index >= BUFFERSIZE) { + if (flush_data(self) < 0) + return 0; + } + if (pack_modified_packed_int(self, lineno, 2, WHAT_LINENO) < 0) + return -1; + return pack_packed_int(self, tdelta); +} + +static inline int +get_fileno(ProfilerObject *self, PyCodeObject *fcode) +{ + /* This is only used for ENTER events. */ + + PyObject *obj; + PyObject *dict; + int fileno; + + obj = PyDict_GetItem(self->filemap, fcode->co_filename); + if (obj == NULL) { + /* first sighting of this file */ + dict = PyDict_New(); + if (dict == NULL) { + return -1; + } + fileno = self->next_fileno; + obj = Py_BuildValue("iN", fileno, dict); + if (obj == NULL) { + return -1; + } + if (PyDict_SetItem(self->filemap, fcode->co_filename, obj)) { + Py_DECREF(obj); + return -1; + } + self->next_fileno++; + Py_DECREF(obj); + if (pack_define_file(self, fileno, + PyString_AS_STRING(fcode->co_filename)) < 0) + return -1; + } + else { + /* already know this ID */ + fileno = PyInt_AS_LONG(PyTuple_GET_ITEM(obj, 0)); + dict = PyTuple_GET_ITEM(obj, 1); + } + /* make sure we save a function name for this (fileno, lineno) */ + obj = PyInt_FromLong(fcode->co_firstlineno); + if (obj == NULL) { + /* We just won't have it saved; too bad. */ + PyErr_Clear(); + } + else { + PyObject *name = PyDict_GetItem(dict, obj); + if (name == NULL) { + if (pack_define_func(self, fileno, fcode->co_firstlineno, + PyString_AS_STRING(fcode->co_name)) < 0) + return -1; + if (PyDict_SetItem(dict, obj, fcode->co_name)) + return -1; + } + } + return fileno; +} + +static inline int +get_tdelta(ProfilerObject *self) +{ + int tdelta; +#ifdef MS_WINDOWS + hs_time tv; + hs_time diff; + + GETTIMEOFDAY(&tv); + diff = tv - self->prev_timeofday; + tdelta = (int)diff; +#else + struct timeval tv; + + GETTIMEOFDAY(&tv); + + if (tv.tv_sec == self->prev_timeofday.tv_sec) + tdelta = tv.tv_usec - self->prev_timeofday.tv_usec; + else + tdelta = ((tv.tv_sec - self->prev_timeofday.tv_sec) * 1000000 + + tv.tv_usec); +#endif + self->prev_timeofday = tv; + return tdelta; +} + + +/* The workhorse: the profiler callback function. */ + +static int +profiler_callback(ProfilerObject *self, PyFrameObject *frame, int what, + PyObject *arg) +{ + int tdelta = -1; + int fileno; + + if (self->frametimings) + tdelta = get_tdelta(self); + switch (what) { + case PyTrace_CALL: + fileno = get_fileno(self, frame->f_code); + if (fileno < 0) + return -1; + if (pack_enter(self, fileno, tdelta, + frame->f_code->co_firstlineno) < 0) + return -1; + break; + case PyTrace_RETURN: + if (pack_exit(self, tdelta) < 0) + return -1; + break; + default: + /* should never get here */ + break; + } + return 0; +} + + +/* Alternate callback when we want PyTrace_LINE events */ + +static int +tracer_callback(ProfilerObject *self, PyFrameObject *frame, int what, + PyObject *arg) +{ + int fileno; + + switch (what) { + case PyTrace_CALL: + fileno = get_fileno(self, frame->f_code); + if (fileno < 0) + return -1; + return pack_enter(self, fileno, + self->frametimings ? get_tdelta(self) : -1, + frame->f_code->co_firstlineno); + + case PyTrace_RETURN: + return pack_exit(self, get_tdelta(self)); + + case PyTrace_LINE: + if (self->linetimings) + return pack_lineno_tdelta(self, frame->f_lineno, + get_tdelta(self)); + else + return pack_lineno(self, frame->f_lineno); + + default: + /* ignore PyTrace_EXCEPTION */ + break; + } + return 0; +} + + +/* A couple of useful helper functions. */ + +#ifdef MS_WINDOWS +static LARGE_INTEGER frequency = {0, 0}; +#endif + +static unsigned long timeofday_diff = 0; +static unsigned long rusage_diff = 0; + +static void +calibrate(void) +{ + hs_time tv1, tv2; + +#ifdef MS_WINDOWS + hs_time diff; + QueryPerformanceFrequency(&frequency); +#endif + + GETTIMEOFDAY(&tv1); + while (1) { + GETTIMEOFDAY(&tv2); +#ifdef MS_WINDOWS + diff = tv2 - tv1; + if (diff != 0) { + timeofday_diff = (unsigned long)diff; + break; + } +#else + if (tv1.tv_sec != tv2.tv_sec || tv1.tv_usec != tv2.tv_usec) { + if (tv1.tv_sec == tv2.tv_sec) + timeofday_diff = tv2.tv_usec - tv1.tv_usec; + else + timeofday_diff = (1000000 - tv1.tv_usec) + tv2.tv_usec; + break; + } +#endif + } +#if defined(MS_WINDOWS) || defined(macintosh) || defined(PYOS_OS2) || \ + defined(__VMS) + rusage_diff = -1; +#else + { + struct rusage ru1, ru2; + + getrusage(RUSAGE_SELF, &ru1); + while (1) { + getrusage(RUSAGE_SELF, &ru2); + if (ru1.ru_utime.tv_sec != ru2.ru_utime.tv_sec) { + rusage_diff = ((1000000 - ru1.ru_utime.tv_usec) + + ru2.ru_utime.tv_usec); + break; + } + else if (ru1.ru_utime.tv_usec != ru2.ru_utime.tv_usec) { + rusage_diff = ru2.ru_utime.tv_usec - ru1.ru_utime.tv_usec; + break; + } + else if (ru1.ru_stime.tv_sec != ru2.ru_stime.tv_sec) { + rusage_diff = ((1000000 - ru1.ru_stime.tv_usec) + + ru2.ru_stime.tv_usec); + break; + } + else if (ru1.ru_stime.tv_usec != ru2.ru_stime.tv_usec) { + rusage_diff = ru2.ru_stime.tv_usec - ru1.ru_stime.tv_usec; + break; + } + } + } +#endif +} + +static void +do_start(ProfilerObject *self) +{ + self->active = 1; + GETTIMEOFDAY(&self->prev_timeofday); + if (self->lineevents) + PyEval_SetTrace((Py_tracefunc) tracer_callback, (PyObject *)self); + else + PyEval_SetProfile((Py_tracefunc) profiler_callback, (PyObject *)self); +} + +static void +do_stop(ProfilerObject *self) +{ + if (self->active) { + self->active = 0; + if (self->lineevents) + PyEval_SetTrace(NULL, NULL); + else + PyEval_SetProfile(NULL, NULL); + } + if (self->index > 0) { + /* Best effort to dump out any remaining data. */ + flush_data(self); + } +} + +static int +is_available(ProfilerObject *self) +{ + if (self->active) { + PyErr_SetString(ProfilerError, "profiler already active"); + return 0; + } + if (self->logfp == NULL) { + PyErr_SetString(ProfilerError, "profiler already closed"); + return 0; + } + return 1; +} + + +/* Profiler object interface methods. */ + +PyDoc_STRVAR(addinfo__doc__, +"addinfo(key, value)\n" +"Insert an ADD_INFO record into the log."); + +static PyObject * +profiler_addinfo(ProfilerObject *self, PyObject *args) +{ + PyObject *result = NULL; + char *key, *value; + + if (PyArg_ParseTuple(args, "ss:addinfo", &key, &value)) { + if (self->logfp == NULL) + PyErr_SetString(ProfilerError, "profiler already closed"); + else { + if (pack_add_info(self, key, value) == 0) { + result = Py_None; + Py_INCREF(result); + } + } + } + return result; +} + +PyDoc_STRVAR(close__doc__, +"close()\n" +"Shut down this profiler and close the log files, even if its active."); + +static PyObject * +profiler_close(ProfilerObject *self) +{ + do_stop(self); + if (self->logfp != NULL) { + fclose(self->logfp); + self->logfp = NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +#define fileno__doc__ logreader_fileno__doc__ + +static PyObject * +profiler_fileno(ProfilerObject *self) +{ + if (self->logfp == NULL) { + PyErr_SetString(PyExc_ValueError, + "profiler's file object already closed"); + return NULL; + } + return PyInt_FromLong(fileno(self->logfp)); +} + +PyDoc_STRVAR(runcall__doc__, +"runcall(callable[, args[, kw]]) -> callable()\n" +"Profile a specific function call, returning the result of that call."); + +static PyObject * +profiler_runcall(ProfilerObject *self, PyObject *args) +{ + PyObject *result = NULL; + PyObject *callargs = NULL; + PyObject *callkw = NULL; + PyObject *callable; + + if (PyArg_ParseTuple(args, "O|OO:runcall", + &callable, &callargs, &callkw)) { + if (is_available(self)) { + do_start(self); + result = PyEval_CallObjectWithKeywords(callable, callargs, callkw); + do_stop(self); + } + } + return result; +} + +PyDoc_STRVAR(runcode__doc__, +"runcode(code, globals[, locals])\n" +"Execute a code object while collecting profile data. If locals is\n" +"omitted, globals is used for the locals as well."); + +static PyObject * +profiler_runcode(ProfilerObject *self, PyObject *args) +{ + PyObject *result = NULL; + PyCodeObject *code; + PyObject *globals; + PyObject *locals = NULL; + + if (PyArg_ParseTuple(args, "O!O!|O:runcode", + &PyCode_Type, &code, + &PyDict_Type, &globals, + &locals)) { + if (is_available(self)) { + if (locals == NULL || locals == Py_None) + locals = globals; + else if (!PyDict_Check(locals)) { + PyErr_SetString(PyExc_TypeError, + "locals must be a dictionary or None"); + return NULL; + } + do_start(self); + result = PyEval_EvalCode(code, globals, locals); + do_stop(self); +#if 0 + if (!PyErr_Occurred()) { + result = Py_None; + Py_INCREF(result); + } +#endif + } + } + return result; +} + +PyDoc_STRVAR(start__doc__, +"start()\n" +"Install this profiler for the current thread."); + +static PyObject * +profiler_start(ProfilerObject *self, PyObject *args) +{ + PyObject *result = NULL; + + if (is_available(self)) { + do_start(self); + result = Py_None; + Py_INCREF(result); + } + return result; +} + +PyDoc_STRVAR(stop__doc__, +"stop()\n" +"Remove this profiler from the current thread."); + +static PyObject * +profiler_stop(ProfilerObject *self, PyObject *args) +{ + PyObject *result = NULL; + + if (!self->active) + PyErr_SetString(ProfilerError, "profiler not active"); + else { + do_stop(self); + result = Py_None; + Py_INCREF(result); + } + return result; +} + + +/* Python API support. */ + +static void +profiler_dealloc(ProfilerObject *self) +{ + do_stop(self); + if (self->logfp != NULL) + fclose(self->logfp); + Py_XDECREF(self->filemap); + Py_XDECREF(self->logfilename); + PyObject_Del((PyObject *)self); +} + +static PyMethodDef profiler_methods[] = { + {"addinfo", (PyCFunction)profiler_addinfo, METH_VARARGS, addinfo__doc__}, + {"close", (PyCFunction)profiler_close, METH_NOARGS, close__doc__}, + {"fileno", (PyCFunction)profiler_fileno, METH_NOARGS, fileno__doc__}, + {"runcall", (PyCFunction)profiler_runcall, METH_VARARGS, runcall__doc__}, + {"runcode", (PyCFunction)profiler_runcode, METH_VARARGS, runcode__doc__}, + {"start", (PyCFunction)profiler_start, METH_NOARGS, start__doc__}, + {"stop", (PyCFunction)profiler_stop, METH_NOARGS, stop__doc__}, + {NULL, NULL} +}; + +static PyMemberDef profiler_members[] = { + {"frametimings", T_LONG, offsetof(ProfilerObject, linetimings), READONLY}, + {"lineevents", T_LONG, offsetof(ProfilerObject, lineevents), READONLY}, + {"linetimings", T_LONG, offsetof(ProfilerObject, linetimings), READONLY}, + {NULL} +}; + +static PyObject * +profiler_get_closed(ProfilerObject *self, void *closure) +{ + PyObject *result = (self->logfp == NULL) ? Py_True : Py_False; + Py_INCREF(result); + return result; +} + +static PyGetSetDef profiler_getsets[] = { + {"closed", (getter)profiler_get_closed, NULL, + PyDoc_STR("True if the profiler's output file has already been closed.")}, + {NULL} +}; + + +PyDoc_STRVAR(profiler_object__doc__, +"High-performance profiler object.\n" +"\n" +"Methods:\n" +"\n" +"close(): Stop the profiler and close the log files.\n" +"fileno(): Returns the file descriptor of the log file.\n" +"runcall(): Run a single function call with profiling enabled.\n" +"runcode(): Execute a code object with profiling enabled.\n" +"start(): Install the profiler and return.\n" +"stop(): Remove the profiler.\n" +"\n" +"Attributes (read-only):\n" +"\n" +"closed: True if the profiler has already been closed.\n" +"frametimings: True if ENTER/EXIT events collect timing information.\n" +"lineevents: True if line events are reported to the profiler.\n" +"linetimings: True if line events collect timing information."); + +static PyTypeObject ProfilerType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "_hotshot.ProfilerType", /* tp_name */ + (int) sizeof(ProfilerObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)profiler_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + profiler_object__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + profiler_methods, /* tp_methods */ + profiler_members, /* tp_members */ + profiler_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + + +static PyMethodDef logreader_methods[] = { + {"close", (PyCFunction)logreader_close, METH_NOARGS, + logreader_close__doc__}, + {"fileno", (PyCFunction)logreader_fileno, METH_NOARGS, + logreader_fileno__doc__}, + {NULL, NULL} +}; + +static PyMemberDef logreader_members[] = { + {"info", T_OBJECT, offsetof(LogReaderObject, info), RO, + PyDoc_STR("Dictionary mapping informational keys to lists of values.")}, + {NULL} +}; + + +PyDoc_STRVAR(logreader__doc__, +"logreader(filename) --> log-iterator\n\ +Create a log-reader for the timing information file."); + +static PySequenceMethods logreader_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + (intargfunc)logreader_sq_item, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + 0, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyObject * +logreader_get_closed(LogReaderObject *self, void *closure) +{ + PyObject *result = (self->logfp == NULL) ? Py_True : Py_False; + Py_INCREF(result); + return result; +} + +static PyGetSetDef logreader_getsets[] = { + {"closed", (getter)logreader_get_closed, NULL, + PyDoc_STR("True if the logreader's input file has already been closed.")}, + {NULL} +}; + +static PyTypeObject LogReaderType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "_hotshot.LogReaderType", /* tp_name */ + (int) sizeof(LogReaderObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)logreader_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &logreader_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + logreader__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)logreader_tp_iternext,/* tp_iternext */ + logreader_methods, /* tp_methods */ + logreader_members, /* tp_members */ + logreader_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + +static PyObject * +hotshot_logreader(PyObject *unused, PyObject *args) +{ + LogReaderObject *self = NULL; + char *filename; + int c; + int err = 0; + + if (PyArg_ParseTuple(args, "s:logreader", &filename)) { + self = PyObject_New(LogReaderObject, &LogReaderType); + if (self != NULL) { + self->frametimings = 1; + self->linetimings = 0; + self->info = NULL; + self->logfp = fopen(filename, "rb"); + if (self->logfp == NULL) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename); + Py_DECREF(self); + self = NULL; + goto finally; + } + self->info = PyDict_New(); + if (self->info == NULL) { + Py_DECREF(self); + goto finally; + } + /* read initial info */ + for (;;) { + if ((c = fgetc(self->logfp)) == EOF) { + eof_error(self); + break; + } + if (c != WHAT_ADD_INFO) { + ungetc(c, self->logfp); + break; + } + err = unpack_add_info(self); + if (err) { + if (err == ERR_EOF) + eof_error(self); + else + PyErr_SetString(PyExc_RuntimeError, + "unexpected error"); + break; + } + } + } + } + finally: + return (PyObject *) self; +} + + +/* Return a Python string that represents the version number without the + * extra cruft added by revision control, even if the right options were + * given to the "cvs export" command to make it not include the extra + * cruft. + */ +static char * +get_version_string(void) +{ + static char *rcsid = "$Revision: 1.34 $"; + char *rev = rcsid; + char *buffer; + int i = 0; + + while (*rev && !isdigit((int)*rev)) + ++rev; + while (rev[i] != ' ' && rev[i] != '\0') + ++i; + buffer = malloc(i + 1); + if (buffer != NULL) { + memmove(buffer, rev, i); + buffer[i] = '\0'; + } + return buffer; +} + +/* Write out a RFC 822-style header with various useful bits of + * information to make the output easier to manage. + */ +static int +write_header(ProfilerObject *self) +{ + char *buffer; + char cwdbuffer[PATH_MAX]; + PyObject *temp; + int i, len; + + buffer = get_version_string(); + if (buffer == NULL) { + PyErr_NoMemory(); + return -1; + } + pack_add_info(self, "hotshot-version", buffer); + pack_add_info(self, "requested-frame-timings", + (self->frametimings ? "yes" : "no")); + pack_add_info(self, "requested-line-events", + (self->lineevents ? "yes" : "no")); + pack_add_info(self, "requested-line-timings", + (self->linetimings ? "yes" : "no")); + pack_add_info(self, "platform", Py_GetPlatform()); + pack_add_info(self, "executable", Py_GetProgramFullPath()); + free(buffer); + buffer = (char *) Py_GetVersion(); + if (buffer == NULL) + PyErr_Clear(); + else + pack_add_info(self, "executable-version", buffer); + +#ifdef MS_WINDOWS + PyOS_snprintf(cwdbuffer, sizeof(cwdbuffer), "%I64d", frequency.QuadPart); + pack_add_info(self, "reported-performance-frequency", cwdbuffer); +#else + PyOS_snprintf(cwdbuffer, sizeof(cwdbuffer), "%lu", rusage_diff); + pack_add_info(self, "observed-interval-getrusage", cwdbuffer); + PyOS_snprintf(cwdbuffer, sizeof(cwdbuffer), "%lu", timeofday_diff); + pack_add_info(self, "observed-interval-gettimeofday", cwdbuffer); +#endif + +#ifndef MS_XBOX + pack_add_info(self, "current-directory", + getcwd(cwdbuffer, sizeof cwdbuffer)); +#endif + + temp = PySys_GetObject("path"); + len = PyList_GET_SIZE(temp); + for (i = 0; i < len; ++i) { + PyObject *item = PyList_GET_ITEM(temp, i); + buffer = PyString_AsString(item); + if (buffer == NULL) { + pack_add_info(self, "sys-path-entry", ""); + PyErr_Clear(); + } + else { + pack_add_info(self, "sys-path-entry", buffer); + } + } + pack_frame_times(self); + pack_line_times(self); + + return 0; +} + +PyDoc_STRVAR(profiler__doc__, +"profiler(logfilename[, lineevents[, linetimes]]) -> profiler\n\ +Create a new profiler object."); + +static PyObject * +hotshot_profiler(PyObject *unused, PyObject *args) +{ + char *logfilename; + ProfilerObject *self = NULL; + int lineevents = 0; + int linetimings = 1; + + if (PyArg_ParseTuple(args, "s|ii:profiler", &logfilename, + &lineevents, &linetimings)) { + self = PyObject_New(ProfilerObject, &ProfilerType); + if (self == NULL) + return NULL; + self->frametimings = 1; + self->lineevents = lineevents ? 1 : 0; + self->linetimings = (lineevents && linetimings) ? 1 : 0; + self->index = 0; + self->active = 0; + self->next_fileno = 0; + self->logfp = NULL; + self->logfilename = PyTuple_GET_ITEM(args, 0); + Py_INCREF(self->logfilename); + self->filemap = PyDict_New(); + if (self->filemap == NULL) { + Py_DECREF(self); + return NULL; + } + self->logfp = fopen(logfilename, "wb"); + if (self->logfp == NULL) { + Py_DECREF(self); + PyErr_SetFromErrnoWithFilename(PyExc_IOError, logfilename); + return NULL; + } + if (timeofday_diff == 0) { + /* Run this several times since sometimes the first + * doesn't give the lowest values, and we're really trying + * to determine the lowest. + */ + calibrate(); + calibrate(); + calibrate(); + } + if (write_header(self)) + /* some error occurred, exception has been set */ + self = NULL; + } + return (PyObject *) self; +} + +PyDoc_STRVAR(coverage__doc__, +"coverage(logfilename) -> profiler\n\ +Returns a profiler that doesn't collect any timing information, which is\n\ +useful in building a coverage analysis tool."); + +static PyObject * +hotshot_coverage(PyObject *unused, PyObject *args) +{ + char *logfilename; + PyObject *result = NULL; + + if (PyArg_ParseTuple(args, "s:coverage", &logfilename)) { + result = hotshot_profiler(unused, args); + if (result != NULL) { + ProfilerObject *self = (ProfilerObject *) result; + self->frametimings = 0; + self->linetimings = 0; + self->lineevents = 1; + } + } + return result; +} + +PyDoc_VAR(resolution__doc__) = +#ifdef MS_WINDOWS +PyDoc_STR( +"resolution() -> (performance-counter-ticks, update-frequency)\n" +"Return the resolution of the timer provided by the QueryPerformanceCounter()\n" +"function. The first value is the smallest observed change, and the second\n" +"is the result of QueryPerformanceFrequency()." +) +#else +PyDoc_STR( +"resolution() -> (gettimeofday-usecs, getrusage-usecs)\n" +"Return the resolution of the timers provided by the gettimeofday() and\n" +"getrusage() system calls, or -1 if the call is not supported." +) +#endif +; + +static PyObject * +hotshot_resolution(PyObject *unused, PyObject *args) +{ + PyObject *result = NULL; + + if (PyArg_ParseTuple(args, ":resolution")) { + if (timeofday_diff == 0) { + calibrate(); + calibrate(); + calibrate(); + } +#ifdef MS_WINDOWS + result = Py_BuildValue("ii", timeofday_diff, frequency.LowPart); +#else + result = Py_BuildValue("ii", timeofday_diff, rusage_diff); +#endif + } + return result; +} + + +static PyMethodDef functions[] = { + {"coverage", hotshot_coverage, METH_VARARGS, coverage__doc__}, + {"profiler", hotshot_profiler, METH_VARARGS, profiler__doc__}, + {"logreader", hotshot_logreader, METH_VARARGS, logreader__doc__}, + {"resolution", hotshot_resolution, METH_VARARGS, resolution__doc__}, + {NULL, NULL} +}; + + +void +init_hotshot(void) +{ + PyObject *module; + + LogReaderType.ob_type = &PyType_Type; + ProfilerType.ob_type = &PyType_Type; + module = Py_InitModule("_hotshot", functions); + if (module != NULL) { + char *s = get_version_string(); + + PyModule_AddStringConstant(module, "__version__", s); + free(s); + Py_INCREF(&LogReaderType); + PyModule_AddObject(module, "LogReaderType", + (PyObject *)&LogReaderType); + Py_INCREF(&ProfilerType); + PyModule_AddObject(module, "ProfilerType", + (PyObject *)&ProfilerType); + + if (ProfilerError == NULL) + ProfilerError = PyErr_NewException("hotshot.ProfilerError", + NULL, NULL); + if (ProfilerError != NULL) { + Py_INCREF(ProfilerError); + PyModule_AddObject(module, "ProfilerError", ProfilerError); + } + PyModule_AddIntConstant(module, "WHAT_ENTER", WHAT_ENTER); + PyModule_AddIntConstant(module, "WHAT_EXIT", WHAT_EXIT); + PyModule_AddIntConstant(module, "WHAT_LINENO", WHAT_LINENO); + PyModule_AddIntConstant(module, "WHAT_OTHER", WHAT_OTHER); + PyModule_AddIntConstant(module, "WHAT_ADD_INFO", WHAT_ADD_INFO); + PyModule_AddIntConstant(module, "WHAT_DEFINE_FILE", WHAT_DEFINE_FILE); + PyModule_AddIntConstant(module, "WHAT_DEFINE_FUNC", WHAT_DEFINE_FUNC); + PyModule_AddIntConstant(module, "WHAT_LINE_TIMES", WHAT_LINE_TIMES); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_localemodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_localemodule.c new file mode 100644 index 00000000..7e7bd9dd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_localemodule.c @@ -0,0 +1,783 @@ +/*********************************************************** +Copyright (C) 1997, 2002, 2003 Martin von Loewis + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies. + +This software comes with no warranty. Use at your own risk. + +******************************************************************/ + +#include "Python.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_LANGINFO_H +#include +#endif + +#ifdef HAVE_LIBINTL_H +#include +#endif + +#ifdef HAVE_WCHAR_H +#include +#endif + +#if defined(MS_WINDOWS) +#ifdef MS_XBOX +#include +#else +#define WIN32_LEAN_AND_MEAN +#include +#endif +#endif + +#if defined(__APPLE__) || defined(__MWERKS__) +#include "macglue.h" +#endif + +#ifdef RISCOS +char *strdup(const char *); +#endif + +PyDoc_STRVAR(locale__doc__, "Support for POSIX locales."); + +static PyObject *Error; + +/* support functions for formatting floating point numbers */ + +PyDoc_STRVAR(setlocale__doc__, +"(integer,string=None) -> string. Activates/queries locale processing."); + +/* to record the LC_NUMERIC settings */ +static PyObject* grouping = NULL; +static PyObject* thousands_sep = NULL; +static PyObject* decimal_point = NULL; +/* if non-null, indicates that LC_NUMERIC is different from "C" */ +static char* saved_numeric = NULL; + +/* the grouping is terminated by either 0 or CHAR_MAX */ +static PyObject* +copy_grouping(char* s) +{ + int i; + PyObject *result, *val = NULL; + + if (s[0] == '\0') + /* empty string: no grouping at all */ + return PyList_New(0); + + for (i = 0; s[i] != '\0' && s[i] != CHAR_MAX; i++) + ; /* nothing */ + + result = PyList_New(i+1); + if (!result) + return NULL; + + i = -1; + do { + i++; + val = PyInt_FromLong(s[i]); + if (!val) + break; + if (PyList_SetItem(result, i, val)) { + Py_DECREF(val); + val = NULL; + break; + } + } while (s[i] != '\0' && s[i] != CHAR_MAX); + + if (!val) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +static void +fixup_ulcase(void) +{ + PyObject *mods, *strop, *string, *ulo; + unsigned char ul[256]; + int n, c; + + /* find the string and strop modules */ + mods = PyImport_GetModuleDict(); + if (!mods) + return; + string = PyDict_GetItemString(mods, "string"); + if (string) + string = PyModule_GetDict(string); + strop=PyDict_GetItemString(mods, "strop"); + if (strop) + strop = PyModule_GetDict(strop); + if (!string && !strop) + return; + + /* create uppercase map string */ + n = 0; + for (c = 0; c < 256; c++) { + if (isupper(c)) + ul[n++] = c; + } + ulo = PyString_FromStringAndSize((const char *)ul, n); + if (!ulo) + return; + if (string) + PyDict_SetItemString(string, "uppercase", ulo); + if (strop) + PyDict_SetItemString(strop, "uppercase", ulo); + Py_DECREF(ulo); + + /* create lowercase string */ + n = 0; + for (c = 0; c < 256; c++) { + if (islower(c)) + ul[n++] = c; + } + ulo = PyString_FromStringAndSize((const char *)ul, n); + if (!ulo) + return; + if (string) + PyDict_SetItemString(string, "lowercase", ulo); + if (strop) + PyDict_SetItemString(strop, "lowercase", ulo); + Py_DECREF(ulo); + + /* create letters string */ + n = 0; + for (c = 0; c < 256; c++) { + if (isalpha(c)) + ul[n++] = c; + } + ulo = PyString_FromStringAndSize((const char *)ul, n); + if (!ulo) + return; + if (string) + PyDict_SetItemString(string, "letters", ulo); + Py_DECREF(ulo); +} + +static PyObject* +PyLocale_setlocale(PyObject* self, PyObject* args) +{ + int category; + char *locale = NULL, *result; + PyObject *result_object; + struct lconv *lc; + + if (!PyArg_ParseTuple(args, "i|z:setlocale", &category, &locale)) + return NULL; + + if (locale) { + /* set locale */ + result = setlocale(category, locale); + if (!result) { + /* operation failed, no setting was changed */ + PyErr_SetString(Error, "unsupported locale setting"); + return NULL; + } + result_object = PyString_FromString(result); + if (!result_object) + return NULL; + /* record changes to LC_NUMERIC */ + if (category == LC_NUMERIC || category == LC_ALL) { + if (strcmp(locale, "C") == 0 || strcmp(locale, "POSIX") == 0) { + /* user just asked for default numeric locale */ + if (saved_numeric) + free(saved_numeric); + saved_numeric = NULL; + } else { + /* remember values */ + lc = localeconv(); + Py_XDECREF(grouping); + grouping = copy_grouping(lc->grouping); + Py_XDECREF(thousands_sep); + thousands_sep = PyString_FromString(lc->thousands_sep); + Py_XDECREF(decimal_point); + decimal_point = PyString_FromString(lc->decimal_point); + if (saved_numeric) + free(saved_numeric); + saved_numeric = strdup(locale); + /* restore to "C" */ + setlocale(LC_NUMERIC, "C"); + } + } + /* record changes to LC_CTYPE */ + if (category == LC_CTYPE || category == LC_ALL) + fixup_ulcase(); + /* things that got wrong up to here are ignored */ + PyErr_Clear(); + } else { + /* get locale */ + /* restore LC_NUMERIC first, if appropriate */ + if (saved_numeric) + setlocale(LC_NUMERIC, saved_numeric); + result = setlocale(category, NULL); + if (!result) { + PyErr_SetString(Error, "locale query failed"); + return NULL; + } + result_object = PyString_FromString(result); + /* restore back to "C" */ + if (saved_numeric) + setlocale(LC_NUMERIC, "C"); + } + return result_object; +} + +PyDoc_STRVAR(localeconv__doc__, +"() -> dict. Returns numeric and monetary locale-specific parameters."); + +static PyObject* +PyLocale_localeconv(PyObject* self) +{ + PyObject* result; + struct lconv *l; + PyObject *x; + + result = PyDict_New(); + if (!result) + return NULL; + + /* if LC_NUMERIC is different in the C library, use saved value */ + l = localeconv(); + + /* hopefully, the localeconv result survives the C library calls + involved herein */ + +#define RESULT_STRING(s)\ + x = PyString_FromString(l->s);\ + if (!x) goto failed;\ + PyDict_SetItemString(result, #s, x);\ + Py_XDECREF(x) + +#define RESULT_INT(i)\ + x = PyInt_FromLong(l->i);\ + if (!x) goto failed;\ + PyDict_SetItemString(result, #i, x);\ + Py_XDECREF(x) + + /* Numeric information */ + if (saved_numeric){ + /* cannot use localeconv results */ + PyDict_SetItemString(result, "decimal_point", decimal_point); + PyDict_SetItemString(result, "grouping", grouping); + PyDict_SetItemString(result, "thousands_sep", thousands_sep); + } else { + RESULT_STRING(decimal_point); + RESULT_STRING(thousands_sep); + x = copy_grouping(l->grouping); + if (!x) + goto failed; + PyDict_SetItemString(result, "grouping", x); + Py_XDECREF(x); + } + + /* Monetary information */ + RESULT_STRING(int_curr_symbol); + RESULT_STRING(currency_symbol); + RESULT_STRING(mon_decimal_point); + RESULT_STRING(mon_thousands_sep); + x = copy_grouping(l->mon_grouping); + if (!x) + goto failed; + PyDict_SetItemString(result, "mon_grouping", x); + Py_XDECREF(x); + RESULT_STRING(positive_sign); + RESULT_STRING(negative_sign); + RESULT_INT(int_frac_digits); + RESULT_INT(frac_digits); + RESULT_INT(p_cs_precedes); + RESULT_INT(p_sep_by_space); + RESULT_INT(n_cs_precedes); + RESULT_INT(n_sep_by_space); + RESULT_INT(p_sign_posn); + RESULT_INT(n_sign_posn); + return result; + + failed: + Py_XDECREF(result); + Py_XDECREF(x); + return NULL; +} + +PyDoc_STRVAR(strcoll__doc__, +"string,string -> int. Compares two strings according to the locale."); + +static PyObject* +PyLocale_strcoll(PyObject* self, PyObject* args) +{ +#if !defined(HAVE_WCSCOLL) || !defined(Py_USING_UNICODE) + char *s1,*s2; + + if (!PyArg_ParseTuple(args, "ss:strcoll", &s1, &s2)) + return NULL; + return PyInt_FromLong(strcoll(s1, s2)); +#else + PyObject *os1, *os2, *result = NULL; + wchar_t *ws1 = NULL, *ws2 = NULL; + int rel1 = 0, rel2 = 0, len1, len2; + + if (!PyArg_ParseTuple(args, "OO:strcoll", &os1, &os2)) + return NULL; + /* If both arguments are byte strings, use strcoll. */ + if (PyString_Check(os1) && PyString_Check(os2)) + return PyInt_FromLong(strcoll(PyString_AS_STRING(os1), + PyString_AS_STRING(os2))); + /* If neither argument is unicode, it's an error. */ + if (!PyUnicode_Check(os1) && !PyUnicode_Check(os2)) { + PyErr_SetString(PyExc_ValueError, "strcoll arguments must be strings"); + } + /* Convert the non-unicode argument to unicode. */ + if (!PyUnicode_Check(os1)) { + os1 = PyUnicode_FromObject(os1); + if (!os1) + return NULL; + rel1 = 1; + } + if (!PyUnicode_Check(os2)) { + os2 = PyUnicode_FromObject(os2); + if (!os2) { + Py_DECREF(os1); + return NULL; + } + rel2 = 1; + } + /* Convert the unicode strings to wchar[]. */ + len1 = PyUnicode_GET_SIZE(os1) + 1; + len2 = PyUnicode_GET_SIZE(os2) + 1; + ws1 = PyMem_MALLOC(len1 * sizeof(wchar_t)); + if (!ws1) { + PyErr_NoMemory(); + goto done; + } + if (PyUnicode_AsWideChar((PyUnicodeObject*)os1, ws1, len1) == -1) + goto done; + ws2 = PyMem_MALLOC(len2 * sizeof(wchar_t)); + if (!ws2) { + PyErr_NoMemory(); + goto done; + } + if (PyUnicode_AsWideChar((PyUnicodeObject*)os2, ws2, len2) == -1) + goto done; + /* Collate the strings. */ + result = PyInt_FromLong(wcscoll(ws1, ws2)); + done: + /* Deallocate everything. */ + if (ws1) PyMem_FREE(ws1); + if (ws2) PyMem_FREE(ws2); + if (rel1) { + Py_DECREF(os1); + } + if (rel2) { + Py_DECREF(os2); + } + return result; +#endif +} + + +PyDoc_STRVAR(strxfrm__doc__, +"string -> string. Returns a string that behaves for cmp locale-aware."); + +static PyObject* +PyLocale_strxfrm(PyObject* self, PyObject* args) +{ + char *s, *buf; + size_t n1, n2; + PyObject *result; + + if (!PyArg_ParseTuple(args, "s:strxfrm", &s)) + return NULL; + + /* assume no change in size, first */ + n1 = strlen(s) + 1; + buf = PyMem_Malloc(n1); + if (!buf) + return PyErr_NoMemory(); + n2 = strxfrm(buf, s, n1); + if (n2 > n1) { + /* more space needed */ + buf = PyMem_Realloc(buf, n2); + if (!buf) + return PyErr_NoMemory(); + strxfrm(buf, s, n2); + } + result = PyString_FromString(buf); + PyMem_Free(buf); + return result; +} + +#if defined(MS_WINDOWS) && !defined(MS_XBOX) +static PyObject* +PyLocale_getdefaultlocale(PyObject* self) +{ + char encoding[100]; + char locale[100]; + + PyOS_snprintf(encoding, sizeof(encoding), "cp%d", GetACP()); + + if (GetLocaleInfo(LOCALE_USER_DEFAULT, + LOCALE_SISO639LANGNAME, + locale, sizeof(locale))) { + int i = strlen(locale); + locale[i++] = '_'; + if (GetLocaleInfo(LOCALE_USER_DEFAULT, + LOCALE_SISO3166CTRYNAME, + locale+i, sizeof(locale)-i)) + return Py_BuildValue("ss", locale, encoding); + } + + /* If we end up here, this windows version didn't know about + ISO639/ISO3166 names (it's probably Windows 95). Return the + Windows language identifier instead (a hexadecimal number) */ + + locale[0] = '0'; + locale[1] = 'x'; + if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTLANGUAGE, + locale+2, sizeof(locale)-2)) { + return Py_BuildValue("ss", locale, encoding); + } + + /* cannot determine the language code (very unlikely) */ + Py_INCREF(Py_None); + return Py_BuildValue("Os", Py_None, encoding); +} +#endif + +#if defined(__APPLE__) +static PyObject* +PyLocale_getdefaultlocale(PyObject* self) +{ + return Py_BuildValue("Os", Py_None, PyMac_getscript()); +} +#endif + +#ifdef HAVE_LANGINFO_H +#define LANGINFO(X) {#X, X} +struct langinfo_constant{ + char* name; + int value; +} langinfo_constants[] = +{ + /* These constants should exist on any langinfo implementation */ + LANGINFO(DAY_1), + LANGINFO(DAY_2), + LANGINFO(DAY_3), + LANGINFO(DAY_4), + LANGINFO(DAY_5), + LANGINFO(DAY_6), + LANGINFO(DAY_7), + + LANGINFO(ABDAY_1), + LANGINFO(ABDAY_2), + LANGINFO(ABDAY_3), + LANGINFO(ABDAY_4), + LANGINFO(ABDAY_5), + LANGINFO(ABDAY_6), + LANGINFO(ABDAY_7), + + LANGINFO(MON_1), + LANGINFO(MON_2), + LANGINFO(MON_3), + LANGINFO(MON_4), + LANGINFO(MON_5), + LANGINFO(MON_6), + LANGINFO(MON_7), + LANGINFO(MON_8), + LANGINFO(MON_9), + LANGINFO(MON_10), + LANGINFO(MON_11), + LANGINFO(MON_12), + + LANGINFO(ABMON_1), + LANGINFO(ABMON_2), + LANGINFO(ABMON_3), + LANGINFO(ABMON_4), + LANGINFO(ABMON_5), + LANGINFO(ABMON_6), + LANGINFO(ABMON_7), + LANGINFO(ABMON_8), + LANGINFO(ABMON_9), + LANGINFO(ABMON_10), + LANGINFO(ABMON_11), + LANGINFO(ABMON_12), + +#ifdef RADIXCHAR + /* The following are not available with glibc 2.0 */ + LANGINFO(RADIXCHAR), + LANGINFO(THOUSEP), + /* YESSTR and NOSTR are deprecated in glibc, since they are + a special case of message translation, which should be rather + done using gettext. So we don't expose it to Python in the + first place. + LANGINFO(YESSTR), + LANGINFO(NOSTR), + */ + LANGINFO(CRNCYSTR), +#endif + + LANGINFO(D_T_FMT), + LANGINFO(D_FMT), + LANGINFO(T_FMT), + LANGINFO(AM_STR), + LANGINFO(PM_STR), + + /* The following constants are available only with XPG4, but... + AIX 3.2. only has CODESET. + OpenBSD doesn't have CODESET but has T_FMT_AMPM, and doesn't have + a few of the others. + Solution: ifdef-test them all. */ +#ifdef CODESET + LANGINFO(CODESET), +#endif +#ifdef T_FMT_AMPM + LANGINFO(T_FMT_AMPM), +#endif +#ifdef ERA + LANGINFO(ERA), +#endif +#ifdef ERA_D_FMT + LANGINFO(ERA_D_FMT), +#endif +#ifdef ERA_D_T_FMT + LANGINFO(ERA_D_T_FMT), +#endif +#ifdef ERA_T_FMT + LANGINFO(ERA_T_FMT), +#endif +#ifdef ALT_DIGITS + LANGINFO(ALT_DIGITS), +#endif +#ifdef YESEXPR + LANGINFO(YESEXPR), +#endif +#ifdef NOEXPR + LANGINFO(NOEXPR), +#endif +#ifdef _DATE_FMT + /* This is not available in all glibc versions that have CODESET. */ + LANGINFO(_DATE_FMT), +#endif + {0, 0} +}; + +PyDoc_STRVAR(nl_langinfo__doc__, +"nl_langinfo(key) -> string\n" +"Return the value for the locale information associated with key."); + +static PyObject* +PyLocale_nl_langinfo(PyObject* self, PyObject* args) +{ + int item, i; + if (!PyArg_ParseTuple(args, "i:nl_langinfo", &item)) + return NULL; + /* Check whether this is a supported constant. GNU libc sometimes + returns numeric values in the char* return value, which would + crash PyString_FromString. */ +#ifdef RADIXCHAR + if (saved_numeric) { + if(item == RADIXCHAR) { + Py_INCREF(decimal_point); + return decimal_point; + } + if(item == THOUSEP) { + Py_INCREF(thousands_sep); + return thousands_sep; + } + } +#endif + for (i = 0; langinfo_constants[i].name; i++) + if (langinfo_constants[i].value == item) + return PyString_FromString(nl_langinfo(item)); + PyErr_SetString(PyExc_ValueError, "unsupported langinfo constant"); + return NULL; +} +#endif /* HAVE_LANGINFO_H */ + +#ifdef HAVE_LIBINTL_H + +PyDoc_STRVAR(gettext__doc__, +"gettext(msg) -> string\n" +"Return translation of msg."); + +static PyObject* +PyIntl_gettext(PyObject* self, PyObject *args) +{ + char *in; + if (!PyArg_ParseTuple(args, "z", &in)) + return 0; + return PyString_FromString(gettext(in)); +} + +PyDoc_STRVAR(dgettext__doc__, +"dgettext(domain, msg) -> string\n" +"Return translation of msg in domain."); + +static PyObject* +PyIntl_dgettext(PyObject* self, PyObject *args) +{ + char *domain, *in; + if (!PyArg_ParseTuple(args, "zz", &domain, &in)) + return 0; + return PyString_FromString(dgettext(domain, in)); +} + +PyDoc_STRVAR(dcgettext__doc__, +"dcgettext(domain, msg, category) -> string\n" +"Return translation of msg in domain and category."); + +static PyObject* +PyIntl_dcgettext(PyObject *self, PyObject *args) +{ + char *domain, *msgid; + int category; + if (!PyArg_ParseTuple(args, "zzi", &domain, &msgid, &category)) + return 0; + return PyString_FromString(dcgettext(domain,msgid,category)); +} + +PyDoc_STRVAR(textdomain__doc__, +"textdomain(domain) -> string\n" +"Set the C library's textdmain to domain, returning the new domain."); + +static PyObject* +PyIntl_textdomain(PyObject* self, PyObject* args) +{ + char *domain; + if (!PyArg_ParseTuple(args, "z", &domain)) + return 0; + domain = textdomain(domain); + if (!domain) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + return PyString_FromString(domain); +} + +PyDoc_STRVAR(bindtextdomain__doc__, +"bindtextdomain(domain, dir) -> string\n" +"Bind the C library's domain to dir."); + +static PyObject* +PyIntl_bindtextdomain(PyObject* self,PyObject*args) +{ + char *domain,*dirname; + if (!PyArg_ParseTuple(args, "zz", &domain, &dirname)) + return 0; + dirname = bindtextdomain(domain, dirname); + if (!dirname) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + return PyString_FromString(dirname); +} + +#endif + +static struct PyMethodDef PyLocale_Methods[] = { + {"setlocale", (PyCFunction) PyLocale_setlocale, + METH_VARARGS, setlocale__doc__}, + {"localeconv", (PyCFunction) PyLocale_localeconv, + METH_NOARGS, localeconv__doc__}, + {"strcoll", (PyCFunction) PyLocale_strcoll, + METH_VARARGS, strcoll__doc__}, + {"strxfrm", (PyCFunction) PyLocale_strxfrm, + METH_VARARGS, strxfrm__doc__}, +#if (defined(MS_WINDOWS) && !defined(MS_XBOX)) || defined(__APPLE__) + {"_getdefaultlocale", (PyCFunction) PyLocale_getdefaultlocale, METH_NOARGS}, +#endif +#ifdef HAVE_LANGINFO_H + {"nl_langinfo", (PyCFunction) PyLocale_nl_langinfo, + METH_VARARGS, nl_langinfo__doc__}, +#endif +#ifdef HAVE_LIBINTL_H + {"gettext",(PyCFunction)PyIntl_gettext,METH_VARARGS, + gettext__doc__}, + {"dgettext",(PyCFunction)PyIntl_dgettext,METH_VARARGS, + dgettext__doc__}, + {"dcgettext",(PyCFunction)PyIntl_dcgettext,METH_VARARGS, + dcgettext__doc__}, + {"textdomain",(PyCFunction)PyIntl_textdomain,METH_VARARGS, + textdomain__doc__}, + {"bindtextdomain",(PyCFunction)PyIntl_bindtextdomain,METH_VARARGS, + bindtextdomain__doc__}, +#endif + {NULL, NULL} +}; + +PyMODINIT_FUNC +init_locale(void) +{ + PyObject *m, *d, *x; +#ifdef HAVE_LANGINFO_H + int i; +#endif + + m = Py_InitModule("_locale", PyLocale_Methods); + + d = PyModule_GetDict(m); + + x = PyInt_FromLong(LC_CTYPE); + PyDict_SetItemString(d, "LC_CTYPE", x); + Py_XDECREF(x); + + x = PyInt_FromLong(LC_TIME); + PyDict_SetItemString(d, "LC_TIME", x); + Py_XDECREF(x); + + x = PyInt_FromLong(LC_COLLATE); + PyDict_SetItemString(d, "LC_COLLATE", x); + Py_XDECREF(x); + + x = PyInt_FromLong(LC_MONETARY); + PyDict_SetItemString(d, "LC_MONETARY", x); + Py_XDECREF(x); + +#ifdef LC_MESSAGES + x = PyInt_FromLong(LC_MESSAGES); + PyDict_SetItemString(d, "LC_MESSAGES", x); + Py_XDECREF(x); +#endif /* LC_MESSAGES */ + + x = PyInt_FromLong(LC_NUMERIC); + PyDict_SetItemString(d, "LC_NUMERIC", x); + Py_XDECREF(x); + + x = PyInt_FromLong(LC_ALL); + PyDict_SetItemString(d, "LC_ALL", x); + Py_XDECREF(x); + + x = PyInt_FromLong(CHAR_MAX); + PyDict_SetItemString(d, "CHAR_MAX", x); + Py_XDECREF(x); + + Error = PyErr_NewException("locale.Error", NULL, NULL); + PyDict_SetItemString(d, "Error", Error); + + x = PyString_FromString(locale__doc__); + PyDict_SetItemString(d, "__doc__", x); + Py_XDECREF(x); + +#ifdef HAVE_LANGINFO_H + for (i = 0; langinfo_constants[i].name; i++) { + PyModule_AddIntConstant(m, langinfo_constants[i].name, + langinfo_constants[i].value); + } +#endif +} + +/* +Local variables: +c-basic-offset: 4 +indent-tabs-mode: nil +End: +*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_randommodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_randommodule.c new file mode 100644 index 00000000..d59b316c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_randommodule.c @@ -0,0 +1,531 @@ +/* Random objects */ + +/* ------------------------------------------------------------------ + The code in this module was based on a download from: + http://www.math.keio.ac.jp/~matumoto/MT2002/emt19937ar.html + + It was modified in 2002 by Raymond Hettinger as follows: + + * the principal computational lines untouched except for tabbing. + + * renamed genrand_res53() to random_random() and wrapped + in python calling/return code. + + * genrand_int32() and the helper functions, init_genrand() + and init_by_array(), were declared static, wrapped in + Python calling/return code. also, their global data + references were replaced with structure references. + + * unused functions from the original were deleted. + new, original C python code was added to implement the + Random() interface. + + The following are the verbatim comments from the original code: + + A C-program for MT19937, with initialization improved 2002/1/26. + Coded by Takuji Nishimura and Makoto Matsumoto. + + Before using, initialize the state by using init_genrand(seed) + or init_by_array(init_key, key_length). + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Any feedback is very welcome. + http://www.math.keio.ac.jp/matumoto/emt.html + email: matumoto@math.keio.ac.jp +*/ + +/* ---------------------------------------------------------------*/ + +#include "Python.h" +#include /* for seeding to current time */ + +/* Period parameters -- These are all magic. Don't change. */ +#define N 624 +#define M 397 +#define MATRIX_A 0x9908b0dfUL /* constant vector a */ +#define UPPER_MASK 0x80000000UL /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ + +typedef struct { + PyObject_HEAD + unsigned long state[N]; + int index; +} RandomObject; + +static PyTypeObject Random_Type; + +#define RandomObject_Check(v) ((v)->ob_type == &Random_Type) + + +/* Random methods */ + + +/* generates a random number on [0,0xffffffff]-interval */ +static unsigned long +genrand_int32(RandomObject *self) +{ + unsigned long y; + static unsigned long mag01[2]={0x0UL, MATRIX_A}; + /* mag01[x] = x * MATRIX_A for x=0,1 */ + unsigned long *mt; + + mt = self->state; + if (self->index >= N) { /* generate N words at one time */ + int kk; + + for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; + } + for (;kk> 1) ^ mag01[y & 0x1UL]; + } + y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); + mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; + + self->index = 0; + } + + y = mt[self->index++]; + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + return y; +} + +/* random_random is the function named genrand_res53 in the original code; + * generates a random number on [0,1) with 53-bit resolution; note that + * 9007199254740992 == 2**53; I assume they're spelling "/2**53" as + * multiply-by-reciprocal in the (likely vain) hope that the compiler will + * optimize the division away at compile-time. 67108864 is 2**26. In + * effect, a contains 27 random bits shifted left 26, and b fills in the + * lower 26 bits of the 53-bit numerator. + * The orginal code credited Isaku Wada for this algorithm, 2002/01/09. + */ +static PyObject * +random_random(RandomObject *self) +{ + unsigned long a=genrand_int32(self)>>5, b=genrand_int32(self)>>6; + return PyFloat_FromDouble((a*67108864.0+b)*(1.0/9007199254740992.0)); +} + +/* initializes mt[N] with a seed */ +static void +init_genrand(RandomObject *self, unsigned long s) +{ + int mti; + unsigned long *mt; + + mt = self->state; + mt[0]= s & 0xffffffffUL; + for (mti=1; mti> 30)) + mti); + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + mt[mti] &= 0xffffffffUL; + /* for >32 bit machines */ + } + self->index = mti; + return; +} + +/* initialize by an array with array-length */ +/* init_key is the array for initializing keys */ +/* key_length is its length */ +static PyObject * +init_by_array(RandomObject *self, unsigned long init_key[], unsigned long key_length) +{ + unsigned int i, j, k; /* was signed in the original code. RDH 12/16/2002 */ + unsigned long *mt; + + mt = self->state; + init_genrand(self, 19650218UL); + i=1; j=0; + k = (N>key_length ? N : key_length); + for (; k; k--) { + mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) + + init_key[j] + j; /* non linear */ + mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ + i++; j++; + if (i>=N) { mt[0] = mt[N-1]; i=1; } + if (j>=key_length) j=0; + } + for (k=N-1; k; k--) { + mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) + - i; /* non linear */ + mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ + i++; + if (i>=N) { mt[0] = mt[N-1]; i=1; } + } + + mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ + Py_INCREF(Py_None); + return Py_None; +} + +/* + * The rest is Python-specific code, neither part of, nor derived from, the + * Twister download. + */ + +static PyObject * +random_seed(RandomObject *self, PyObject *args) +{ + PyObject *result = NULL; /* guilty until proved innocent */ + PyObject *masklower = NULL; + PyObject *thirtytwo = NULL; + PyObject *n = NULL; + unsigned long *key = NULL; + unsigned long keymax; /* # of allocated slots in key */ + unsigned long keyused; /* # of used slots in key */ + int err; + + PyObject *arg = NULL; + + if (!PyArg_UnpackTuple(args, "seed", 0, 1, &arg)) + return NULL; + + if (arg == NULL || arg == Py_None) { + time_t now; + + time(&now); + init_genrand(self, (unsigned long)now); + Py_INCREF(Py_None); + return Py_None; + } + /* If the arg is an int or long, use its absolute value; else use + * the absolute value of its hash code. + */ + if (PyInt_Check(arg) || PyLong_Check(arg)) + n = PyNumber_Absolute(arg); + else { + long hash = PyObject_Hash(arg); + if (hash == -1) + goto Done; + n = PyLong_FromUnsignedLong((unsigned long)hash); + } + if (n == NULL) + goto Done; + + /* Now split n into 32-bit chunks, from the right. Each piece is + * stored into key, which has a capacity of keymax chunks, of which + * keyused are filled. Alas, the repeated shifting makes this a + * quadratic-time algorithm; we'd really like to use + * _PyLong_AsByteArray here, but then we'd have to break into the + * long representation to figure out how big an array was needed + * in advance. + */ + keymax = 8; /* arbitrary; grows later if needed */ + keyused = 0; + key = (unsigned long *)PyMem_Malloc(keymax * sizeof(*key)); + if (key == NULL) + goto Done; + + masklower = PyLong_FromUnsignedLong(0xffffffffU); + if (masklower == NULL) + goto Done; + thirtytwo = PyInt_FromLong(32L); + if (thirtytwo == NULL) + goto Done; + while ((err=PyObject_IsTrue(n))) { + PyObject *newn; + PyObject *pychunk; + unsigned long chunk; + + if (err == -1) + goto Done; + pychunk = PyNumber_And(n, masklower); + if (pychunk == NULL) + goto Done; + chunk = PyLong_AsUnsignedLong(pychunk); + Py_DECREF(pychunk); + if (chunk == (unsigned long)-1 && PyErr_Occurred()) + goto Done; + newn = PyNumber_Rshift(n, thirtytwo); + if (newn == NULL) + goto Done; + Py_DECREF(n); + n = newn; + if (keyused >= keymax) { + unsigned long bigger = keymax << 1; + if ((bigger >> 1) != keymax) { + PyErr_NoMemory(); + goto Done; + } + key = (unsigned long *)PyMem_Realloc(key, + bigger * sizeof(*key)); + if (key == NULL) + goto Done; + keymax = bigger; + } + assert(keyused < keymax); + key[keyused++] = chunk; + } + + if (keyused == 0) + key[keyused++] = 0UL; + result = init_by_array(self, key, keyused); +Done: + Py_XDECREF(masklower); + Py_XDECREF(thirtytwo); + Py_XDECREF(n); + PyMem_Free(key); + return result; +} + +static PyObject * +random_getstate(RandomObject *self) +{ + PyObject *state; + PyObject *element; + int i; + + state = PyTuple_New(N+1); + if (state == NULL) + return NULL; + for (i=0; istate[i])); + if (element == NULL) + goto Fail; + PyTuple_SET_ITEM(state, i, element); + } + element = PyInt_FromLong((long)(self->index)); + if (element == NULL) + goto Fail; + PyTuple_SET_ITEM(state, i, element); + return state; + +Fail: + Py_DECREF(state); + return NULL; +} + +static PyObject * +random_setstate(RandomObject *self, PyObject *state) +{ + int i; + long element; + + if (!PyTuple_Check(state)) { + PyErr_SetString(PyExc_TypeError, + "state vector must be a tuple"); + return NULL; + } + if (PyTuple_Size(state) != N+1) { + PyErr_SetString(PyExc_ValueError, + "state vector is the wrong size"); + return NULL; + } + + for (i=0; istate[i] = (unsigned long)element; + } + + element = PyInt_AsLong(PyTuple_GET_ITEM(state, i)); + if (element == -1 && PyErr_Occurred()) + return NULL; + self->index = (int)element; + + Py_INCREF(Py_None); + return Py_None; +} + +/* +Jumpahead should be a fast way advance the generator n-steps ahead, but +lacking a formula for that, the next best is to use n and the existing +state to create a new state far away from the original. + +The generator uses constant spaced additive feedback, so shuffling the +state elements ought to produce a state which would not be encountered +(in the near term) by calls to random(). Shuffling is normally +implemented by swapping the ith element with another element ranging +from 0 to i inclusive. That allows the element to have the possibility +of not being moved. Since the goal is to produce a new, different +state, the swap element is ranged from 0 to i-1 inclusive. This assures +that each element gets moved at least once. + +To make sure that consecutive calls to jumpahead(n) produce different +states (even in the rare case of involutory shuffles), i+1 is added to +each element at position i. Successive calls are then guaranteed to +have changing (growing) values as well as shuffled positions. + +Finally, the self->index value is set to N so that the generator itself +kicks in on the next call to random(). This assures that all results +have been through the generator and do not just reflect alterations to +the underlying state. +*/ + +static PyObject * +random_jumpahead(RandomObject *self, PyObject *n) +{ + long i, j; + PyObject *iobj; + PyObject *remobj; + unsigned long *mt, tmp; + + if (!PyInt_Check(n) && !PyLong_Check(n)) { + PyErr_Format(PyExc_TypeError, "jumpahead requires an " + "integer, not '%s'", + n->ob_type->tp_name); + return NULL; + } + + mt = self->state; + for (i = N-1; i > 1; i--) { + iobj = PyInt_FromLong(i); + if (iobj == NULL) + return NULL; + remobj = PyNumber_Remainder(n, iobj); + Py_DECREF(iobj); + if (remobj == NULL) + return NULL; + j = PyInt_AsLong(remobj); + Py_DECREF(remobj); + if (j == -1L && PyErr_Occurred()) + return NULL; + tmp = mt[i]; + mt[i] = mt[j]; + mt[j] = tmp; + } + + for (i = 0; i < N; i++) + mt[i] += i+1; + + self->index = N; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +random_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + RandomObject *self; + PyObject *tmp; + + self = (RandomObject *)type->tp_alloc(type, 0); + if (self == NULL) + return NULL; + tmp = random_seed(self, args); + if (tmp == NULL) { + Py_DECREF(self); + return NULL; + } + Py_DECREF(tmp); + return (PyObject *)self; +} + +static PyMethodDef random_methods[] = { + {"random", (PyCFunction)random_random, METH_NOARGS, + PyDoc_STR("random() -> x in the interval [0, 1).")}, + {"seed", (PyCFunction)random_seed, METH_VARARGS, + PyDoc_STR("seed([n]) -> None. Defaults to current time.")}, + {"getstate", (PyCFunction)random_getstate, METH_NOARGS, + PyDoc_STR("getstate() -> tuple containing the current state.")}, + {"setstate", (PyCFunction)random_setstate, METH_O, + PyDoc_STR("setstate(state) -> None. Restores generator state.")}, + {"jumpahead", (PyCFunction)random_jumpahead, METH_O, + PyDoc_STR("jumpahead(int) -> None. Create new state from " + "existing state and integer.")}, + {NULL, NULL} /* sentinel */ +}; + +PyDoc_STRVAR(random_doc, +"Random() -> create a random number generator with its own internal state."); + +static PyTypeObject Random_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_random.Random", /*tp_name*/ + sizeof(RandomObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + random_doc, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + random_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + random_new, /*tp_new*/ + _PyObject_Del, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +PyDoc_STRVAR(module_doc, +"Module implements the Mersenne Twister random number generator."); + +PyMODINIT_FUNC +init_random(void) +{ + PyObject *m; + + if (PyType_Ready(&Random_Type) < 0) + return; + m = Py_InitModule3("_random", NULL, module_doc); + Py_INCREF(&Random_Type); + PyModule_AddObject(m, "Random", (PyObject *)&Random_Type); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_sre.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_sre.c new file mode 100644 index 00000000..14bdeedf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_sre.c @@ -0,0 +1,3166 @@ +/* + * Secret Labs' Regular Expression Engine + * + * regular expression matching engine + * + * partial history: + * 1999-10-24 fl created (based on existing template matcher code) + * 2000-03-06 fl first alpha, sort of + * 2000-08-01 fl fixes for 1.6b1 + * 2000-08-07 fl use PyOS_CheckStack() if available + * 2000-09-20 fl added expand method + * 2001-03-20 fl lots of fixes for 2.1b2 + * 2001-04-15 fl export copyright as Python attribute, not global + * 2001-04-28 fl added __copy__ methods (work in progress) + * 2001-05-14 fl fixes for 1.5.2 compatibility + * 2001-07-01 fl added BIGCHARSET support (from Martin von Loewis) + * 2001-10-18 fl fixed group reset issue (from Matthew Mueller) + * 2001-10-20 fl added split primitive; reenable unicode for 1.6/2.0/2.1 + * 2001-10-21 fl added sub/subn primitive + * 2001-10-24 fl added finditer primitive (for 2.2 only) + * 2001-12-07 fl fixed memory leak in sub/subn (Guido van Rossum) + * 2002-11-09 fl fixed empty sub/subn return type + * 2003-04-18 mvl fully support 4-byte codes + * + * Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved. + * + * This version of the SRE library can be redistributed under CNRI's + * Python 1.6 license. For any other use, please contact Secret Labs + * AB (info@pythonware.com). + * + * Portions of this engine have been developed in cooperation with + * CNRI. Hewlett-Packard provided funding for 1.6 integration and + * other compatibility work. + */ + +#ifndef SRE_RECURSIVE + +static char copyright[] = + " SRE 2.2.2 Copyright (c) 1997-2002 by Secret Labs AB "; + +#include "Python.h" +#include "structmember.h" /* offsetof */ + +#include "sre.h" + +#include + +/* name of this module, minus the leading underscore */ +#if !defined(SRE_MODULE) +#define SRE_MODULE "sre" +#endif + +/* defining this one enables tracing */ +#undef VERBOSE + +#if PY_VERSION_HEX >= 0x01060000 +#if PY_VERSION_HEX < 0x02020000 || defined(Py_USING_UNICODE) +/* defining this enables unicode support (default under 1.6a1 and later) */ +#define HAVE_UNICODE +#endif +#endif + +/* -------------------------------------------------------------------- */ +/* optional features */ + +/* prevent run-away recursion (bad patterns on long strings) */ + +#ifndef USE_STACKCHECK + #if defined(MS_WIN64) || defined(__LP64__) || defined(_LP64) + /* require smaller recursion limit for a number of 64-bit platforms: + * Win64 (MS_WIN64), Linux64 (__LP64__), Monterey (64-bit AIX) (_LP64) + */ + /* FIXME: maybe the limit should be 40000 / sizeof(void*) ? */ + #define USE_RECURSION_LIMIT 7500 + + #elif defined(__FreeBSD__) + /* FreeBSD/amd64 and /sparc64 require even smaller limits */ + #if defined(__amd64__) + #define USE_RECURSION_LIMIT 6000 + #elif defined(__sparc64__) + #define USE_RECURSION_LIMIT 3000 + #elif defined(__GNUC__) && defined(WITH_THREAD) + /* the pthreads library on FreeBSD has a fixed 1MB stack size for + * the initial (or "primary") thread, which is insufficient for + * the default recursion limit. gcc 3.x at the default + * optimisation level (-O3) uses stack space more aggressively + * than gcc 2.95. + */ + #if (__GNUC__ > 2) + #define USE_RECURSION_LIMIT 6500 + #else + #define USE_RECURSION_LIMIT 7500 + #endif + #endif + #endif /* special cases for USE_RECURSION_LIMIT */ + + #ifndef USE_RECURSION_LIMIT /* default if not overriden above */ + #define USE_RECURSION_LIMIT 10000 + #endif +#endif /* !USE_STACKCHECK */ + +/* enables fast searching */ +#define USE_FAST_SEARCH + +/* enables aggressive inlining (always on for Visual C) */ +#undef USE_INLINE + +/* enables copy/deepcopy handling (work in progress) */ +#undef USE_BUILTIN_COPY + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_DEL(op) PyMem_DEL((op)) +#endif + +/* -------------------------------------------------------------------- */ + +#if defined(_MSC_VER) +#pragma optimize("agtw", on) /* doesn't seem to make much difference... */ +#pragma warning(disable: 4710) /* who cares if functions are not inlined ;-) */ +/* fastest possible local call under MSVC */ +#define LOCAL(type) static __inline type __fastcall +#elif defined(USE_INLINE) +#define LOCAL(type) static inline type +#else +#define LOCAL(type) static type +#endif + +/* error codes */ +#define SRE_ERROR_ILLEGAL -1 /* illegal opcode */ +#define SRE_ERROR_STATE -2 /* illegal state */ +#define SRE_ERROR_RECURSION_LIMIT -3 /* runaway recursion */ +#define SRE_ERROR_MEMORY -9 /* out of memory */ + +#if defined(VERBOSE) +#define TRACE(v) printf v +#else +#define TRACE(v) +#endif + +/* -------------------------------------------------------------------- */ +/* search engine state */ + +/* default character predicates (run sre_chars.py to regenerate tables) */ + +#define SRE_DIGIT_MASK 1 +#define SRE_SPACE_MASK 2 +#define SRE_LINEBREAK_MASK 4 +#define SRE_ALNUM_MASK 8 +#define SRE_WORD_MASK 16 + +/* FIXME: this assumes ASCII. create tables in init_sre() instead */ + +static char sre_char_info[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 6, 2, +2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, +0, 0, 16, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0 }; + +static char sre_char_lower[128] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, +27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, +44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, +61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, +108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, +122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, +106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, +120, 121, 122, 123, 124, 125, 126, 127 }; + +#define SRE_IS_DIGIT(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_DIGIT_MASK) : 0) +#define SRE_IS_SPACE(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_SPACE_MASK) : 0) +#define SRE_IS_LINEBREAK(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_LINEBREAK_MASK) : 0) +#define SRE_IS_ALNUM(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_ALNUM_MASK) : 0) +#define SRE_IS_WORD(ch)\ + ((ch) < 128 ? (sre_char_info[(ch)] & SRE_WORD_MASK) : 0) + +static unsigned int sre_lower(unsigned int ch) +{ + return ((ch) < 128 ? sre_char_lower[ch] : ch); +} + +/* locale-specific character predicates */ + +#define SRE_LOC_IS_DIGIT(ch) ((ch) < 256 ? isdigit((ch)) : 0) +#define SRE_LOC_IS_SPACE(ch) ((ch) < 256 ? isspace((ch)) : 0) +#define SRE_LOC_IS_LINEBREAK(ch) ((ch) == '\n') +#define SRE_LOC_IS_ALNUM(ch) ((ch) < 256 ? isalnum((ch)) : 0) +#define SRE_LOC_IS_WORD(ch) (SRE_LOC_IS_ALNUM((ch)) || (ch) == '_') + +static unsigned int sre_lower_locale(unsigned int ch) +{ + return ((ch) < 256 ? tolower((ch)) : ch); +} + +/* unicode-specific character predicates */ + +#if defined(HAVE_UNICODE) + +#define SRE_UNI_IS_DIGIT(ch) Py_UNICODE_ISDIGIT((Py_UNICODE)(ch)) +#define SRE_UNI_IS_SPACE(ch) Py_UNICODE_ISSPACE((Py_UNICODE)(ch)) +#define SRE_UNI_IS_LINEBREAK(ch) Py_UNICODE_ISLINEBREAK((Py_UNICODE)(ch)) +#define SRE_UNI_IS_ALNUM(ch) Py_UNICODE_ISALNUM((Py_UNICODE)(ch)) +#define SRE_UNI_IS_WORD(ch) (SRE_UNI_IS_ALNUM((ch)) || (ch) == '_') + +static unsigned int sre_lower_unicode(unsigned int ch) +{ + return (unsigned int) Py_UNICODE_TOLOWER((Py_UNICODE)(ch)); +} + +#endif /* HAVE_UNICODE */ + +LOCAL(int) +sre_category(SRE_CODE category, unsigned int ch) +{ + switch (category) { + + case SRE_CATEGORY_DIGIT: + return SRE_IS_DIGIT(ch); + case SRE_CATEGORY_NOT_DIGIT: + return !SRE_IS_DIGIT(ch); + case SRE_CATEGORY_SPACE: + return SRE_IS_SPACE(ch); + case SRE_CATEGORY_NOT_SPACE: + return !SRE_IS_SPACE(ch); + case SRE_CATEGORY_WORD: + return SRE_IS_WORD(ch); + case SRE_CATEGORY_NOT_WORD: + return !SRE_IS_WORD(ch); + case SRE_CATEGORY_LINEBREAK: + return SRE_IS_LINEBREAK(ch); + case SRE_CATEGORY_NOT_LINEBREAK: + return !SRE_IS_LINEBREAK(ch); + + case SRE_CATEGORY_LOC_WORD: + return SRE_LOC_IS_WORD(ch); + case SRE_CATEGORY_LOC_NOT_WORD: + return !SRE_LOC_IS_WORD(ch); + +#if defined(HAVE_UNICODE) + case SRE_CATEGORY_UNI_DIGIT: + return SRE_UNI_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_NOT_DIGIT: + return !SRE_UNI_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_SPACE: + return SRE_UNI_IS_SPACE(ch); + case SRE_CATEGORY_UNI_NOT_SPACE: + return !SRE_UNI_IS_SPACE(ch); + case SRE_CATEGORY_UNI_WORD: + return SRE_UNI_IS_WORD(ch); + case SRE_CATEGORY_UNI_NOT_WORD: + return !SRE_UNI_IS_WORD(ch); + case SRE_CATEGORY_UNI_LINEBREAK: + return SRE_UNI_IS_LINEBREAK(ch); + case SRE_CATEGORY_UNI_NOT_LINEBREAK: + return !SRE_UNI_IS_LINEBREAK(ch); +#else + case SRE_CATEGORY_UNI_DIGIT: + return SRE_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_NOT_DIGIT: + return !SRE_IS_DIGIT(ch); + case SRE_CATEGORY_UNI_SPACE: + return SRE_IS_SPACE(ch); + case SRE_CATEGORY_UNI_NOT_SPACE: + return !SRE_IS_SPACE(ch); + case SRE_CATEGORY_UNI_WORD: + return SRE_LOC_IS_WORD(ch); + case SRE_CATEGORY_UNI_NOT_WORD: + return !SRE_LOC_IS_WORD(ch); + case SRE_CATEGORY_UNI_LINEBREAK: + return SRE_IS_LINEBREAK(ch); + case SRE_CATEGORY_UNI_NOT_LINEBREAK: + return !SRE_IS_LINEBREAK(ch); +#endif + } + return 0; +} + +/* helpers */ + +static void +mark_fini(SRE_STATE* state) +{ + if (state->mark_stack) { + free(state->mark_stack); + state->mark_stack = NULL; + } + state->mark_stack_size = state->mark_stack_base = 0; +} + +static int +mark_save(SRE_STATE* state, int lo, int hi, int *mark_stack_base) +{ + void* stack; + int size; + int minsize, newsize; + + if (hi <= lo) + return 0; + + size = (hi - lo) + 1; + + newsize = state->mark_stack_size; + minsize = state->mark_stack_base + size; + + if (newsize < minsize) { + /* create new stack */ + if (!newsize) { + newsize = 512; + if (newsize < minsize) + newsize = minsize; + TRACE(("allocate stack %d\n", newsize)); + stack = malloc(sizeof(void*) * newsize); + } else { + /* grow the stack */ + while (newsize < minsize) + newsize += newsize; + TRACE(("grow stack to %d\n", newsize)); + stack = realloc(state->mark_stack, sizeof(void*) * newsize); + } + if (!stack) { + mark_fini(state); + return SRE_ERROR_MEMORY; + } + state->mark_stack = stack; + state->mark_stack_size = newsize; + } + + TRACE(("copy %d:%d to %d (%d)\n", lo, hi, state->mark_stack_base, size)); + + memcpy(state->mark_stack + state->mark_stack_base, state->mark + lo, + size * sizeof(void*)); + + state->mark_stack_base += size; + + *mark_stack_base = state->mark_stack_base; + + return 0; +} + +static int +mark_restore(SRE_STATE* state, int lo, int hi, int *mark_stack_base) +{ + int size; + + if (hi <= lo) + return 0; + + size = (hi - lo) + 1; + + state->mark_stack_base = *mark_stack_base - size; + + TRACE(("copy %d:%d from %d\n", lo, hi, state->mark_stack_base)); + + memcpy(state->mark + lo, state->mark_stack + state->mark_stack_base, + size * sizeof(void*)); + + return 0; +} + +/* generate 8-bit version */ + +#define SRE_CHAR unsigned char +#define SRE_AT sre_at +#define SRE_COUNT sre_count +#define SRE_CHARSET sre_charset +#define SRE_INFO sre_info +#define SRE_MATCH sre_match +#define SRE_SEARCH sre_search +#define SRE_LITERAL_TEMPLATE sre_literal_template + +#if defined(HAVE_UNICODE) + +#define SRE_RECURSIVE +#include "_sre.c" +#undef SRE_RECURSIVE + +#undef SRE_LITERAL_TEMPLATE +#undef SRE_SEARCH +#undef SRE_MATCH +#undef SRE_INFO +#undef SRE_CHARSET +#undef SRE_COUNT +#undef SRE_AT +#undef SRE_CHAR + +/* generate 16-bit unicode version */ + +#define SRE_CHAR Py_UNICODE +#define SRE_AT sre_uat +#define SRE_COUNT sre_ucount +#define SRE_CHARSET sre_ucharset +#define SRE_INFO sre_uinfo +#define SRE_MATCH sre_umatch +#define SRE_SEARCH sre_usearch +#define SRE_LITERAL_TEMPLATE sre_uliteral_template +#endif + +#endif /* SRE_RECURSIVE */ + +/* -------------------------------------------------------------------- */ +/* String matching engine */ + +/* the following section is compiled twice, with different character + settings */ + +LOCAL(int) +SRE_AT(SRE_STATE* state, SRE_CHAR* ptr, SRE_CODE at) +{ + /* check if pointer is at given position */ + + int this, that; + + switch (at) { + + case SRE_AT_BEGINNING: + case SRE_AT_BEGINNING_STRING: + return ((void*) ptr == state->beginning); + + case SRE_AT_BEGINNING_LINE: + return ((void*) ptr == state->beginning || + SRE_IS_LINEBREAK((int) ptr[-1])); + + case SRE_AT_END: + return (((void*) (ptr+1) == state->end && + SRE_IS_LINEBREAK((int) ptr[0])) || + ((void*) ptr == state->end)); + + case SRE_AT_END_LINE: + return ((void*) ptr == state->end || + SRE_IS_LINEBREAK((int) ptr[0])); + + case SRE_AT_END_STRING: + return ((void*) ptr == state->end); + + case SRE_AT_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_IS_WORD((int) ptr[0]) : 0; + return this != that; + + case SRE_AT_NON_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_IS_WORD((int) ptr[0]) : 0; + return this == that; + + case SRE_AT_LOC_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_LOC_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_LOC_IS_WORD((int) ptr[0]) : 0; + return this != that; + + case SRE_AT_LOC_NON_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_LOC_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_LOC_IS_WORD((int) ptr[0]) : 0; + return this == that; + +#if defined(HAVE_UNICODE) + case SRE_AT_UNI_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_UNI_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_UNI_IS_WORD((int) ptr[0]) : 0; + return this != that; + + case SRE_AT_UNI_NON_BOUNDARY: + if (state->beginning == state->end) + return 0; + that = ((void*) ptr > state->beginning) ? + SRE_UNI_IS_WORD((int) ptr[-1]) : 0; + this = ((void*) ptr < state->end) ? + SRE_UNI_IS_WORD((int) ptr[0]) : 0; + return this == that; +#endif + + } + + return 0; +} + +LOCAL(int) +SRE_CHARSET(SRE_CODE* set, SRE_CODE ch) +{ + /* check if character is a member of the given set */ + + int ok = 1; + + for (;;) { + switch (*set++) { + + case SRE_OP_LITERAL: + /* */ + if (ch == set[0]) + return ok; + set++; + break; + + case SRE_OP_RANGE: + /* */ + if (set[0] <= ch && ch <= set[1]) + return ok; + set += 2; + break; + + case SRE_OP_CHARSET: + if (sizeof(SRE_CODE) == 2) { + /* (16 bits per code word) */ + if (ch < 256 && (set[ch >> 4] & (1 << (ch & 15)))) + return ok; + set += 16; + } + else { + /* (32 bits per code word) */ + if (ch < 256 && (set[ch >> 5] & (1 << (ch & 31)))) + return ok; + set += 8; + } + break; + + case SRE_OP_BIGCHARSET: + /* <256 blockindices> */ + { + int count, block; + count = *(set++); + + if (sizeof(SRE_CODE) == 2) { + block = ((unsigned char*)set)[ch >> 8]; + set += 128; + if (set[block*16 + ((ch & 255)>>4)] & (1 << (ch & 15))) + return ok; + set += count*16; + } + else { + if (ch < 65536) + block = ((unsigned char*)set)[ch >> 8]; + else + block = -1; + set += 64; + if (block >=0 && + (set[block*8 + ((ch & 255)>>5)] & (1 << (ch & 31)))) + return ok; + set += count*8; + } + break; + } + + case SRE_OP_CATEGORY: + /* */ + if (sre_category(set[0], (int) ch)) + return ok; + set += 1; + break; + + case SRE_OP_NEGATE: + ok = !ok; + break; + + case SRE_OP_FAILURE: + return !ok; + + default: + /* internal error -- there's not much we can do about it + here, so let's just pretend it didn't match... */ + return 0; + } + } +} + +LOCAL(int) SRE_MATCH(SRE_STATE* state, SRE_CODE* pattern, int level); + +LOCAL(int) +SRE_COUNT(SRE_STATE* state, SRE_CODE* pattern, int maxcount, int level) +{ + SRE_CODE chr; + SRE_CHAR* ptr = state->ptr; + SRE_CHAR* end = state->end; + int i; + + /* adjust end */ + if (maxcount < end - ptr && maxcount != 65535) + end = ptr + maxcount; + + switch (pattern[0]) { + + case SRE_OP_ANY: + /* repeated dot wildcard. */ + TRACE(("|%p|%p|COUNT ANY\n", pattern, ptr)); + while (ptr < end && !SRE_IS_LINEBREAK(*ptr)) + ptr++; + break; + + case SRE_OP_ANY_ALL: + /* repeated dot wildcare. skip to the end of the target + string, and backtrack from there */ + TRACE(("|%p|%p|COUNT ANY_ALL\n", pattern, ptr)); + ptr = end; + break; + + case SRE_OP_LITERAL: + /* repeated literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT LITERAL %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) *ptr == chr) + ptr++; + break; + + case SRE_OP_LITERAL_IGNORE: + /* repeated literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT LITERAL_IGNORE %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) state->lower(*ptr) == chr) + ptr++; + break; + + case SRE_OP_NOT_LITERAL: + /* repeated non-literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT NOT_LITERAL %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) *ptr != chr) + ptr++; + break; + + case SRE_OP_NOT_LITERAL_IGNORE: + /* repeated non-literal */ + chr = pattern[1]; + TRACE(("|%p|%p|COUNT NOT_LITERAL_IGNORE %d\n", pattern, ptr, chr)); + while (ptr < end && (SRE_CODE) state->lower(*ptr) != chr) + ptr++; + break; + + case SRE_OP_IN: + /* repeated set */ + TRACE(("|%p|%p|COUNT IN\n", pattern, ptr)); + while (ptr < end && SRE_CHARSET(pattern + 2, *ptr)) + ptr++; + break; + + default: + /* repeated single character pattern */ + TRACE(("|%p|%p|COUNT SUBPATTERN\n", pattern, ptr)); + while ((SRE_CHAR*) state->ptr < end) { + i = SRE_MATCH(state, pattern, level); + if (i < 0) + return i; + if (!i) + break; + } + TRACE(("|%p|%p|COUNT %d\n", pattern, ptr, + (SRE_CHAR*) state->ptr - ptr)); + return (SRE_CHAR*) state->ptr - ptr; + } + + TRACE(("|%p|%p|COUNT %d\n", pattern, ptr, ptr - (SRE_CHAR*) state->ptr)); + return ptr - (SRE_CHAR*) state->ptr; +} + +#if 0 /* not used in this release */ +LOCAL(int) +SRE_INFO(SRE_STATE* state, SRE_CODE* pattern) +{ + /* check if an SRE_OP_INFO block matches at the current position. + returns the number of SRE_CODE objects to skip if successful, 0 + if no match */ + + SRE_CHAR* end = state->end; + SRE_CHAR* ptr = state->ptr; + int i; + + /* check minimal length */ + if (pattern[3] && (end - ptr) < pattern[3]) + return 0; + + /* check known prefix */ + if (pattern[2] & SRE_INFO_PREFIX && pattern[5] > 1) { + /* */ + for (i = 0; i < pattern[5]; i++) + if ((SRE_CODE) ptr[i] != pattern[7 + i]) + return 0; + return pattern[0] + 2 * pattern[6]; + } + return pattern[0]; +} +#endif + +/* The macros below should be used to protect recursive SRE_MATCH() + * calls that *failed* and do *not* return immediately (IOW, those + * that will backtrack). Explaining: + * + * - Recursive SRE_MATCH() returned true: that's usually a success + * (besides atypical cases like ASSERT_NOT), therefore there's no + * reason to restore lastmark; + * + * - Recursive SRE_MATCH() returned false but the current SRE_MATCH() + * is returning to the caller: If the current SRE_MATCH() is the + * top function of the recursion, returning false will be a matching + * failure, and it doesn't matter where lastmark is pointing to. + * If it's *not* the top function, it will be a recursive SRE_MATCH() + * failure by itself, and the calling SRE_MATCH() will have to deal + * with the failure by the same rules explained here (it will restore + * lastmark by itself if necessary); + * + * - Recursive SRE_MATCH() returned false, and will continue the + * outside 'for' loop: must be protected when breaking, since the next + * OP could potentially depend on lastmark; + * + * - Recursive SRE_MATCH() returned false, and will be called again + * inside a local for/while loop: must be protected between each + * loop iteration, since the recursive SRE_MATCH() could do anything, + * and could potentially depend on lastmark. + * + * For more information, check the discussion at SF patch #712900. + */ +#define LASTMARK_SAVE() \ + do { \ + lastmark = state->lastmark; \ + lastindex = state->lastindex; \ + } while (0) +#define LASTMARK_RESTORE() \ + do { \ + if (state->lastmark > lastmark) { \ + memset(state->mark + lastmark + 1, 0, \ + (state->lastmark - lastmark) * sizeof(void*)); \ + state->lastmark = lastmark; \ + state->lastindex = lastindex; \ + } \ + } while (0) + +LOCAL(int) +SRE_MATCH(SRE_STATE* state, SRE_CODE* pattern, int level) +{ + /* check if string matches the given pattern. returns <0 for + error, 0 for failure, and 1 for success */ + + SRE_CHAR* end = state->end; + SRE_CHAR* ptr = state->ptr; + int i, count; + SRE_REPEAT* rp; + int lastmark, lastindex, mark_stack_base; + SRE_CODE chr; + + SRE_REPEAT rep; /* FIXME: allocate in STATE instead */ + + TRACE(("|%p|%p|ENTER %d\n", pattern, ptr, level)); + +#if defined(USE_STACKCHECK) + if (level % 10 == 0 && PyOS_CheckStack()) + return SRE_ERROR_RECURSION_LIMIT; +#endif + +#if defined(USE_RECURSION_LIMIT) + if (level > USE_RECURSION_LIMIT) + return SRE_ERROR_RECURSION_LIMIT; +#endif + + if (pattern[0] == SRE_OP_INFO) { + /* optimization info block */ + /* <1=skip> <2=flags> <3=min> ... */ + if (pattern[3] && (end - ptr) < pattern[3]) { + TRACE(("reject (got %d chars, need %d)\n", + (end - ptr), pattern[3])); + return 0; + } + pattern += pattern[1] + 1; + } + + for (;;) { + + switch (*pattern++) { + + case SRE_OP_FAILURE: + /* immediate failure */ + TRACE(("|%p|%p|FAILURE\n", pattern, ptr)); + return 0; + + case SRE_OP_SUCCESS: + /* end of pattern */ + TRACE(("|%p|%p|SUCCESS\n", pattern, ptr)); + state->ptr = ptr; + return 1; + + case SRE_OP_AT: + /* match at given position */ + /* */ + TRACE(("|%p|%p|AT %d\n", pattern, ptr, *pattern)); + if (!SRE_AT(state, ptr, *pattern)) + return 0; + pattern++; + break; + + case SRE_OP_CATEGORY: + /* match at given category */ + /* */ + TRACE(("|%p|%p|CATEGORY %d\n", pattern, ptr, *pattern)); + if (ptr >= end || !sre_category(pattern[0], ptr[0])) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_LITERAL: + /* match literal string */ + /* */ + TRACE(("|%p|%p|LITERAL %d\n", pattern, ptr, *pattern)); + if (ptr >= end || (SRE_CODE) ptr[0] != pattern[0]) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_NOT_LITERAL: + /* match anything that is not literal character */ + /* */ + TRACE(("|%p|%p|NOT_LITERAL %d\n", pattern, ptr, *pattern)); + if (ptr >= end || (SRE_CODE) ptr[0] == pattern[0]) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_ANY: + /* match anything (except a newline) */ + /* */ + TRACE(("|%p|%p|ANY\n", pattern, ptr)); + if (ptr >= end || SRE_IS_LINEBREAK(ptr[0])) + return 0; + ptr++; + break; + + case SRE_OP_ANY_ALL: + /* match anything */ + /* */ + TRACE(("|%p|%p|ANY_ALL\n", pattern, ptr)); + if (ptr >= end) + return 0; + ptr++; + break; + + case SRE_OP_IN: + /* match set member (or non_member) */ + /* */ + TRACE(("|%p|%p|IN\n", pattern, ptr)); + if (ptr >= end || !SRE_CHARSET(pattern + 1, *ptr)) + return 0; + pattern += pattern[0]; + ptr++; + break; + + case SRE_OP_GROUPREF: + /* match backreference */ + TRACE(("|%p|%p|GROUPREF %d\n", pattern, ptr, pattern[0])); + i = pattern[0]; + { + SRE_CHAR* p = (SRE_CHAR*) state->mark[i+i]; + SRE_CHAR* e = (SRE_CHAR*) state->mark[i+i+1]; + if (!p || !e || e < p) + return 0; + while (p < e) { + if (ptr >= end || *ptr != *p) + return 0; + p++; ptr++; + } + } + pattern++; + break; + + case SRE_OP_GROUPREF_IGNORE: + /* match backreference */ + TRACE(("|%p|%p|GROUPREF_IGNORE %d\n", pattern, ptr, pattern[0])); + i = pattern[0]; + { + SRE_CHAR* p = (SRE_CHAR*) state->mark[i+i]; + SRE_CHAR* e = (SRE_CHAR*) state->mark[i+i+1]; + if (!p || !e || e < p) + return 0; + while (p < e) { + if (ptr >= end || + state->lower(*ptr) != state->lower(*p)) + return 0; + p++; ptr++; + } + } + pattern++; + break; + + case SRE_OP_LITERAL_IGNORE: + TRACE(("|%p|%p|LITERAL_IGNORE %d\n", pattern, ptr, pattern[0])); + if (ptr >= end || + state->lower(*ptr) != state->lower(*pattern)) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_NOT_LITERAL_IGNORE: + TRACE(("|%p|%p|NOT_LITERAL_IGNORE %d\n", pattern, ptr, *pattern)); + if (ptr >= end || + state->lower(*ptr) == state->lower(*pattern)) + return 0; + pattern++; + ptr++; + break; + + case SRE_OP_IN_IGNORE: + TRACE(("|%p|%p|IN_IGNORE\n", pattern, ptr)); + if (ptr >= end + || !SRE_CHARSET(pattern + 1, (SRE_CODE) state->lower(*ptr))) + return 0; + pattern += pattern[0]; + ptr++; + break; + + case SRE_OP_MARK: + /* set mark */ + /* */ + TRACE(("|%p|%p|MARK %d\n", pattern, ptr, pattern[0])); + i = pattern[0]; + if (i & 1) + state->lastindex = i/2 + 1; + if (i > state->lastmark) + state->lastmark = i; + state->mark[i] = ptr; + pattern++; + break; + + case SRE_OP_JUMP: + case SRE_OP_INFO: + /* jump forward */ + /* */ + TRACE(("|%p|%p|JUMP %d\n", pattern, ptr, pattern[0])); + pattern += pattern[0]; + break; + + case SRE_OP_ASSERT: + /* assert subpattern */ + /* */ + TRACE(("|%p|%p|ASSERT %d\n", pattern, ptr, pattern[1])); + state->ptr = ptr - pattern[1]; + if (state->ptr < state->beginning) + return 0; + i = SRE_MATCH(state, pattern + 2, level + 1); + if (i <= 0) + return i; + pattern += pattern[0]; + break; + + case SRE_OP_ASSERT_NOT: + /* assert not subpattern */ + /* */ + TRACE(("|%p|%p|ASSERT_NOT %d\n", pattern, ptr, pattern[1])); + state->ptr = ptr - pattern[1]; + if (state->ptr >= state->beginning) { + i = SRE_MATCH(state, pattern + 2, level + 1); + if (i < 0) + return i; + if (i) + return 0; + } + pattern += pattern[0]; + break; + + case SRE_OP_BRANCH: + /* alternation */ + /* <0=skip> code ... */ + TRACE(("|%p|%p|BRANCH\n", pattern, ptr)); + LASTMARK_SAVE(); + if (state->repeat) { + i = mark_save(state, 0, lastmark, &mark_stack_base); + if (i < 0) + return i; + } + for (; pattern[0]; pattern += pattern[0]) { + if (pattern[1] == SRE_OP_LITERAL && + (ptr >= end || (SRE_CODE) *ptr != pattern[2])) + continue; + if (pattern[1] == SRE_OP_IN && + (ptr >= end || !SRE_CHARSET(pattern + 3, (SRE_CODE) *ptr))) + continue; + state->ptr = ptr; + i = SRE_MATCH(state, pattern + 1, level + 1); + if (i) + return i; + if (state->repeat) { + i = mark_restore(state, 0, lastmark, &mark_stack_base); + if (i < 0) + return i; + } + LASTMARK_RESTORE(); + } + return 0; + + case SRE_OP_REPEAT_ONE: + /* match repeated sequence (maximizing regexp) */ + + /* this operator only works if the repeated item is + exactly one character wide, and we're not already + collecting backtracking points. for other cases, + use the MAX_REPEAT operator */ + + /* <1=min> <2=max> item tail */ + + TRACE(("|%p|%p|REPEAT_ONE %d %d\n", pattern, ptr, + pattern[1], pattern[2])); + + if (ptr + pattern[1] > end) + return 0; /* cannot match */ + + state->ptr = ptr; + + count = SRE_COUNT(state, pattern + 3, pattern[2], level + 1); + if (count < 0) + return count; + + ptr += count; + + /* when we arrive here, count contains the number of + matches, and ptr points to the tail of the target + string. check if the rest of the pattern matches, + and backtrack if not. */ + + if (count < (int) pattern[1]) + return 0; + + if (pattern[pattern[0]] == SRE_OP_SUCCESS) { + /* tail is empty. we're finished */ + state->ptr = ptr; + return 1; + } + + LASTMARK_SAVE(); + + if (pattern[pattern[0]] == SRE_OP_LITERAL) { + /* tail starts with a literal. skip positions where + the rest of the pattern cannot possibly match */ + chr = pattern[pattern[0]+1]; + for (;;) { + while (count >= (int) pattern[1] && + (ptr >= end || *ptr != chr)) { + ptr--; + count--; + } + if (count < (int) pattern[1]) + break; + state->ptr = ptr; + i = SRE_MATCH(state, pattern + pattern[0], level + 1); + if (i) + return i; + ptr--; + count--; + LASTMARK_RESTORE(); + } + + } else { + /* general case */ + while (count >= (int) pattern[1]) { + state->ptr = ptr; + i = SRE_MATCH(state, pattern + pattern[0], level + 1); + if (i) + return i; + ptr--; + count--; + LASTMARK_RESTORE(); + } + } + return 0; + + case SRE_OP_MIN_REPEAT_ONE: + /* match repeated sequence (minimizing regexp) */ + + /* this operator only works if the repeated item is + exactly one character wide, and we're not already + collecting backtracking points. for other cases, + use the MIN_REPEAT operator */ + + /* <1=min> <2=max> item tail */ + + TRACE(("|%p|%p|MIN_REPEAT_ONE %d %d\n", pattern, ptr, + pattern[1], pattern[2])); + + if (ptr + pattern[1] > end) + return 0; /* cannot match */ + + state->ptr = ptr; + + if (pattern[1] == 0) + count = 0; + else { + /* count using pattern min as the maximum */ + count = SRE_COUNT(state, pattern + 3, pattern[1], level + 1); + + if (count < 0) + return count; /* exception */ + if (count < (int) pattern[1]) + return 0; /* did not match minimum number of times */ + ptr += count; /* advance past minimum matches of repeat */ + } + + if (pattern[pattern[0]] == SRE_OP_SUCCESS) { + /* tail is empty. we're finished */ + state->ptr = ptr; + return 1; + + } else { + /* general case */ + int matchmax = ((int)pattern[2] == 65535); + int c; + LASTMARK_SAVE(); + while (matchmax || count <= (int) pattern[2]) { + state->ptr = ptr; + i = SRE_MATCH(state, pattern + pattern[0], level + 1); + if (i) + return i; + state->ptr = ptr; + c = SRE_COUNT(state, pattern+3, 1, level+1); + if (c < 0) + return c; + if (c == 0) + break; + assert(c == 1); + ptr++; + count++; + LASTMARK_RESTORE(); + } + } + return 0; + + case SRE_OP_REPEAT: + /* create repeat context. all the hard work is done + by the UNTIL operator (MAX_UNTIL, MIN_UNTIL) */ + /* <1=min> <2=max> item tail */ + TRACE(("|%p|%p|REPEAT %d %d\n", pattern, ptr, + pattern[1], pattern[2])); + + rep.count = -1; + rep.pattern = pattern; + + /* install new repeat context */ + rep.prev = state->repeat; + state->repeat = &rep; + + state->ptr = ptr; + i = SRE_MATCH(state, pattern + pattern[0], level + 1); + + state->repeat = rep.prev; + + return i; + + case SRE_OP_MAX_UNTIL: + /* maximizing repeat */ + /* <1=min> <2=max> item tail */ + + /* FIXME: we probably need to deal with zero-width + matches in here... */ + + rp = state->repeat; + if (!rp) + return SRE_ERROR_STATE; + + state->ptr = ptr; + + count = rp->count + 1; + + TRACE(("|%p|%p|MAX_UNTIL %d\n", pattern, ptr, count)); + + if (count < rp->pattern[1]) { + /* not enough matches */ + rp->count = count; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + rp->count = count - 1; + state->ptr = ptr; + return 0; + } + + if (count < rp->pattern[2] || rp->pattern[2] == 65535) { + /* we may have enough matches, but if we can + match another item, do so */ + rp->count = count; + LASTMARK_SAVE(); + i = mark_save(state, 0, lastmark, &mark_stack_base); + if (i < 0) + return i; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + i = mark_restore(state, 0, lastmark, &mark_stack_base); + if (i < 0) + return i; + LASTMARK_RESTORE(); + rp->count = count - 1; + state->ptr = ptr; + } + + /* cannot match more repeated items here. make sure the + tail matches */ + state->repeat = rp->prev; + i = SRE_MATCH(state, pattern, level + 1); + if (i) + return i; + state->repeat = rp; + state->ptr = ptr; + return 0; + + case SRE_OP_MIN_UNTIL: + /* minimizing repeat */ + /* <1=min> <2=max> item tail */ + + rp = state->repeat; + if (!rp) + return SRE_ERROR_STATE; + + state->ptr = ptr; + + count = rp->count + 1; + + TRACE(("|%p|%p|MIN_UNTIL %d %p\n", pattern, ptr, count, + rp->pattern)); + + if (count < rp->pattern[1]) { + /* not enough matches */ + rp->count = count; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + rp->count = count-1; + state->ptr = ptr; + return 0; + } + + LASTMARK_SAVE(); + + /* see if the tail matches */ + state->repeat = rp->prev; + i = SRE_MATCH(state, pattern, level + 1); + if (i) + return i; + + state->ptr = ptr; + state->repeat = rp; + + if (count >= rp->pattern[2] && rp->pattern[2] != 65535) + return 0; + + LASTMARK_RESTORE(); + + rp->count = count; + /* RECURSIVE */ + i = SRE_MATCH(state, rp->pattern + 3, level + 1); + if (i) + return i; + rp->count = count - 1; + state->ptr = ptr; + + return 0; + + default: + TRACE(("|%p|%p|UNKNOWN %d\n", pattern, ptr, pattern[-1])); + return SRE_ERROR_ILLEGAL; + } + } + + /* can't end up here */ + /* return SRE_ERROR_ILLEGAL; -- see python-dev discussion */ +} + +LOCAL(int) +SRE_SEARCH(SRE_STATE* state, SRE_CODE* pattern) +{ + SRE_CHAR* ptr = state->start; + SRE_CHAR* end = state->end; + int status = 0; + int prefix_len = 0; + int prefix_skip = 0; + SRE_CODE* prefix = NULL; + SRE_CODE* charset = NULL; + SRE_CODE* overlap = NULL; + int flags = 0; + + if (pattern[0] == SRE_OP_INFO) { + /* optimization info block */ + /* <1=skip> <2=flags> <3=min> <4=max> <5=prefix info> */ + + flags = pattern[2]; + + if (pattern[3] > 1) { + /* adjust end point (but make sure we leave at least one + character in there, so literal search will work) */ + end -= pattern[3]-1; + if (end <= ptr) + end = ptr+1; + } + + if (flags & SRE_INFO_PREFIX) { + /* pattern starts with a known prefix */ + /* */ + prefix_len = pattern[5]; + prefix_skip = pattern[6]; + prefix = pattern + 7; + overlap = prefix + prefix_len - 1; + } else if (flags & SRE_INFO_CHARSET) + /* pattern starts with a character from a known set */ + /* */ + charset = pattern + 5; + + pattern += 1 + pattern[1]; + } + + TRACE(("prefix = %p %d %d\n", prefix, prefix_len, prefix_skip)); + TRACE(("charset = %p\n", charset)); + +#if defined(USE_FAST_SEARCH) + if (prefix_len > 1) { + /* pattern starts with a known prefix. use the overlap + table to skip forward as fast as we possibly can */ + int i = 0; + end = state->end; + while (ptr < end) { + for (;;) { + if ((SRE_CODE) ptr[0] != prefix[i]) { + if (!i) + break; + else + i = overlap[i]; + } else { + if (++i == prefix_len) { + /* found a potential match */ + TRACE(("|%p|%p|SEARCH SCAN\n", pattern, ptr)); + state->start = ptr + 1 - prefix_len; + state->ptr = ptr + 1 - prefix_len + prefix_skip; + if (flags & SRE_INFO_LITERAL) + return 1; /* we got all of it */ + status = SRE_MATCH(state, pattern + 2*prefix_skip, 1); + if (status != 0) + return status; + /* close but no cigar -- try again */ + i = overlap[i]; + } + break; + } + + } + ptr++; + } + return 0; + } +#endif + + if (pattern[0] == SRE_OP_LITERAL) { + /* pattern starts with a literal character. this is used + for short prefixes, and if fast search is disabled */ + SRE_CODE chr = pattern[1]; + end = state->end; + for (;;) { + while (ptr < end && (SRE_CODE) ptr[0] != chr) + ptr++; + if (ptr >= end) + return 0; + TRACE(("|%p|%p|SEARCH LITERAL\n", pattern, ptr)); + state->start = ptr; + state->ptr = ++ptr; + if (flags & SRE_INFO_LITERAL) + return 1; /* we got all of it */ + status = SRE_MATCH(state, pattern + 2, 1); + if (status != 0) + break; + } + } else if (charset) { + /* pattern starts with a character from a known set */ + end = state->end; + for (;;) { + while (ptr < end && !SRE_CHARSET(charset, ptr[0])) + ptr++; + if (ptr >= end) + return 0; + TRACE(("|%p|%p|SEARCH CHARSET\n", pattern, ptr)); + state->start = ptr; + state->ptr = ptr; + status = SRE_MATCH(state, pattern, 1); + if (status != 0) + break; + ptr++; + } + } else + /* general case */ + while (ptr <= end) { + TRACE(("|%p|%p|SEARCH\n", pattern, ptr)); + state->start = state->ptr = ptr++; + status = SRE_MATCH(state, pattern, 1); + if (status != 0) + break; + } + + return status; +} + +LOCAL(int) +SRE_LITERAL_TEMPLATE(SRE_CHAR* ptr, int len) +{ + /* check if given string is a literal template (i.e. no escapes) */ + while (len-- > 0) + if (*ptr++ == '\\') + return 0; + return 1; +} + +#if !defined(SRE_RECURSIVE) + +/* -------------------------------------------------------------------- */ +/* factories and destructors */ + +/* see sre.h for object declarations */ + +static PyTypeObject Pattern_Type; +static PyTypeObject Match_Type; +static PyTypeObject Scanner_Type; + +static PyObject * +_compile(PyObject* self_, PyObject* args) +{ + /* "compile" pattern descriptor to pattern object */ + + PatternObject* self; + int i, n; + + PyObject* pattern; + int flags = 0; + PyObject* code; + int groups = 0; + PyObject* groupindex = NULL; + PyObject* indexgroup = NULL; + if (!PyArg_ParseTuple(args, "OiO!|iOO", &pattern, &flags, + &PyList_Type, &code, &groups, + &groupindex, &indexgroup)) + return NULL; + + n = PyList_GET_SIZE(code); + + self = PyObject_NEW_VAR(PatternObject, &Pattern_Type, n); + if (!self) + return NULL; + + self->codesize = n; + + for (i = 0; i < n; i++) { + PyObject *o = PyList_GET_ITEM(code, i); + if (PyInt_Check(o)) + self->code[i] = (SRE_CODE) PyInt_AsLong(o); + else + self->code[i] = (SRE_CODE) PyLong_AsUnsignedLong(o); + } + + if (PyErr_Occurred()) { + PyObject_DEL(self); + return NULL; + } + + Py_INCREF(pattern); + self->pattern = pattern; + + self->flags = flags; + + self->groups = groups; + + Py_XINCREF(groupindex); + self->groupindex = groupindex; + + Py_XINCREF(indexgroup); + self->indexgroup = indexgroup; + + return (PyObject*) self; +} + +static PyObject * +sre_codesize(PyObject* self, PyObject* args) +{ + return Py_BuildValue("i", sizeof(SRE_CODE)); +} + +static PyObject * +sre_getlower(PyObject* self, PyObject* args) +{ + int character, flags; + if (!PyArg_ParseTuple(args, "ii", &character, &flags)) + return NULL; + if (flags & SRE_FLAG_LOCALE) + return Py_BuildValue("i", sre_lower_locale(character)); + if (flags & SRE_FLAG_UNICODE) +#if defined(HAVE_UNICODE) + return Py_BuildValue("i", sre_lower_unicode(character)); +#else + return Py_BuildValue("i", sre_lower_locale(character)); +#endif + return Py_BuildValue("i", sre_lower(character)); +} + +LOCAL(void) +state_reset(SRE_STATE* state) +{ + state->lastmark = 0; + + /* FIXME: dynamic! */ + memset(state->mark, 0, sizeof(*state->mark) * SRE_MARK_SIZE); + + state->lastindex = -1; + + state->repeat = NULL; + + mark_fini(state); +} + +static void* +getstring(PyObject* string, int* p_length, int* p_charsize) +{ + /* given a python object, return a data pointer, a length (in + characters), and a character size. return NULL if the object + is not a string (or not compatible) */ + + PyBufferProcs *buffer; + int size, bytes, charsize; + void* ptr; + +#if defined(HAVE_UNICODE) + if (PyUnicode_Check(string)) { + /* unicode strings doesn't always support the buffer interface */ + ptr = (void*) PyUnicode_AS_DATA(string); + bytes = PyUnicode_GET_DATA_SIZE(string); + size = PyUnicode_GET_SIZE(string); + charsize = sizeof(Py_UNICODE); + + } else { +#endif + + /* get pointer to string buffer */ + buffer = string->ob_type->tp_as_buffer; + if (!buffer || !buffer->bf_getreadbuffer || !buffer->bf_getsegcount || + buffer->bf_getsegcount(string, NULL) != 1) { + PyErr_SetString(PyExc_TypeError, "expected string or buffer"); + return NULL; + } + + /* determine buffer size */ + bytes = buffer->bf_getreadbuffer(string, 0, &ptr); + if (bytes < 0) { + PyErr_SetString(PyExc_TypeError, "buffer has negative size"); + return NULL; + } + + /* determine character size */ +#if PY_VERSION_HEX >= 0x01060000 + size = PyObject_Size(string); +#else + size = PyObject_Length(string); +#endif + + if (PyString_Check(string) || bytes == size) + charsize = 1; +#if defined(HAVE_UNICODE) + else if (bytes == (int) (size * sizeof(Py_UNICODE))) + charsize = sizeof(Py_UNICODE); +#endif + else { + PyErr_SetString(PyExc_TypeError, "buffer size mismatch"); + return NULL; + } + +#if defined(HAVE_UNICODE) + } +#endif + + *p_length = size; + *p_charsize = charsize; + + return ptr; +} + +LOCAL(PyObject*) +state_init(SRE_STATE* state, PatternObject* pattern, PyObject* string, + int start, int end) +{ + /* prepare state object */ + + int length; + int charsize; + void* ptr; + + memset(state, 0, sizeof(SRE_STATE)); + + state->lastindex = -1; + + ptr = getstring(string, &length, &charsize); + if (!ptr) + return NULL; + + /* adjust boundaries */ + if (start < 0) + start = 0; + else if (start > length) + start = length; + + if (end < 0) + end = 0; + else if (end > length) + end = length; + + state->charsize = charsize; + + state->beginning = ptr; + + state->start = (void*) ((char*) ptr + start * state->charsize); + state->end = (void*) ((char*) ptr + end * state->charsize); + + Py_INCREF(string); + state->string = string; + state->pos = start; + state->endpos = end; + + if (pattern->flags & SRE_FLAG_LOCALE) + state->lower = sre_lower_locale; + else if (pattern->flags & SRE_FLAG_UNICODE) +#if defined(HAVE_UNICODE) + state->lower = sre_lower_unicode; +#else + state->lower = sre_lower_locale; +#endif + else + state->lower = sre_lower; + + return string; +} + +LOCAL(void) +state_fini(SRE_STATE* state) +{ + Py_XDECREF(state->string); + mark_fini(state); +} + +/* calculate offset from start of string */ +#define STATE_OFFSET(state, member)\ + (((char*)(member) - (char*)(state)->beginning) / (state)->charsize) + +LOCAL(PyObject*) +state_getslice(SRE_STATE* state, int index, PyObject* string, int empty) +{ + int i, j; + + index = (index - 1) * 2; + + if (string == Py_None || !state->mark[index] || !state->mark[index+1]) { + if (empty) + /* want empty string */ + i = j = 0; + else { + Py_INCREF(Py_None); + return Py_None; + } + } else { + i = STATE_OFFSET(state, state->mark[index]); + j = STATE_OFFSET(state, state->mark[index+1]); + } + + return PySequence_GetSlice(string, i, j); +} + +static void +pattern_error(int status) +{ + switch (status) { + case SRE_ERROR_RECURSION_LIMIT: + PyErr_SetString( + PyExc_RuntimeError, + "maximum recursion limit exceeded" + ); + break; + case SRE_ERROR_MEMORY: + PyErr_NoMemory(); + break; + default: + /* other error codes indicate compiler/engine bugs */ + PyErr_SetString( + PyExc_RuntimeError, + "internal error in regular expression engine" + ); + } +} + +static PyObject* +pattern_new_match(PatternObject* pattern, SRE_STATE* state, int status) +{ + /* create match object (from state object) */ + + MatchObject* match; + int i, j; + char* base; + int n; + + if (status > 0) { + + /* create match object (with room for extra group marks) */ + match = PyObject_NEW_VAR(MatchObject, &Match_Type, + 2*(pattern->groups+1)); + if (!match) + return NULL; + + Py_INCREF(pattern); + match->pattern = pattern; + + Py_INCREF(state->string); + match->string = state->string; + + match->regs = NULL; + match->groups = pattern->groups+1; + + /* fill in group slices */ + + base = (char*) state->beginning; + n = state->charsize; + + match->mark[0] = ((char*) state->start - base) / n; + match->mark[1] = ((char*) state->ptr - base) / n; + + for (i = j = 0; i < pattern->groups; i++, j+=2) + if (j+1 <= state->lastmark && state->mark[j] && state->mark[j+1]) { + match->mark[j+2] = ((char*) state->mark[j] - base) / n; + match->mark[j+3] = ((char*) state->mark[j+1] - base) / n; + } else + match->mark[j+2] = match->mark[j+3] = -1; /* undefined */ + + match->pos = state->pos; + match->endpos = state->endpos; + + match->lastindex = state->lastindex; + + return (PyObject*) match; + + } else if (status == 0) { + + /* no match */ + Py_INCREF(Py_None); + return Py_None; + + } + + /* internal error */ + pattern_error(status); + return NULL; +} + +static PyObject* +pattern_scanner(PatternObject* pattern, PyObject* args) +{ + /* create search state object */ + + ScannerObject* self; + + PyObject* string; + int start = 0; + int end = INT_MAX; + if (!PyArg_ParseTuple(args, "O|ii:scanner", &string, &start, &end)) + return NULL; + + /* create scanner object */ + self = PyObject_NEW(ScannerObject, &Scanner_Type); + if (!self) + return NULL; + + string = state_init(&self->state, pattern, string, start, end); + if (!string) { + PyObject_DEL(self); + return NULL; + } + + Py_INCREF(pattern); + self->pattern = (PyObject*) pattern; + + return (PyObject*) self; +} + +static void +pattern_dealloc(PatternObject* self) +{ + Py_XDECREF(self->pattern); + Py_XDECREF(self->groupindex); + Py_XDECREF(self->indexgroup); + PyObject_DEL(self); +} + +static PyObject* +pattern_match(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + int status; + + PyObject* string; + int start = 0; + int end = INT_MAX; + static char* kwlist[] = { "pattern", "pos", "endpos", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|ii:match", kwlist, + &string, &start, &end)) + return NULL; + + string = state_init(&state, self, string, start, end); + if (!string) + return NULL; + + state.ptr = state.start; + + TRACE(("|%p|%p|MATCH\n", PatternObject_GetCode(self), state.ptr)); + + if (state.charsize == 1) { + status = sre_match(&state, PatternObject_GetCode(self), 1); + } else { +#if defined(HAVE_UNICODE) + status = sre_umatch(&state, PatternObject_GetCode(self), 1); +#endif + } + + TRACE(("|%p|%p|END\n", PatternObject_GetCode(self), state.ptr)); + + state_fini(&state); + + return pattern_new_match(self, &state, status); +} + +static PyObject* +pattern_search(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + int status; + + PyObject* string; + int start = 0; + int end = INT_MAX; + static char* kwlist[] = { "pattern", "pos", "endpos", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|ii:search", kwlist, + &string, &start, &end)) + return NULL; + + string = state_init(&state, self, string, start, end); + if (!string) + return NULL; + + TRACE(("|%p|%p|SEARCH\n", PatternObject_GetCode(self), state.ptr)); + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + TRACE(("|%p|%p|END\n", PatternObject_GetCode(self), state.ptr)); + + state_fini(&state); + + return pattern_new_match(self, &state, status); +} + +static PyObject* +call(char* module, char* function, PyObject* args) +{ + PyObject* name; + PyObject* mod; + PyObject* func; + PyObject* result; + + if (!args) + return NULL; + name = PyString_FromString(module); + if (!name) + return NULL; + mod = PyImport_Import(name); + Py_DECREF(name); + if (!mod) + return NULL; + func = PyObject_GetAttrString(mod, function); + Py_DECREF(mod); + if (!func) + return NULL; + result = PyObject_CallObject(func, args); + Py_DECREF(func); + Py_DECREF(args); + return result; +} + +#ifdef USE_BUILTIN_COPY +static int +deepcopy(PyObject** object, PyObject* memo) +{ + PyObject* copy; + + copy = call( + "copy", "deepcopy", + Py_BuildValue("OO", *object, memo) + ); + if (!copy) + return 0; + + Py_DECREF(*object); + *object = copy; + + return 1; /* success */ +} +#endif + +static PyObject* +join_list(PyObject* list, PyObject* pattern) +{ + /* join list elements */ + + PyObject* joiner; +#if PY_VERSION_HEX >= 0x01060000 + PyObject* function; + PyObject* args; +#endif + PyObject* result; + + switch (PyList_GET_SIZE(list)) { + case 0: + Py_DECREF(list); + return PySequence_GetSlice(pattern, 0, 0); + case 1: + result = PyList_GET_ITEM(list, 0); + Py_INCREF(result); + Py_DECREF(list); + return result; + } + + /* two or more elements: slice out a suitable separator from the + first member, and use that to join the entire list */ + + joiner = PySequence_GetSlice(pattern, 0, 0); + if (!joiner) + return NULL; + +#if PY_VERSION_HEX >= 0x01060000 + function = PyObject_GetAttrString(joiner, "join"); + if (!function) { + Py_DECREF(joiner); + return NULL; + } + args = PyTuple_New(1); + if (!args) { + Py_DECREF(function); + Py_DECREF(joiner); + return NULL; + } + PyTuple_SET_ITEM(args, 0, list); + result = PyObject_CallObject(function, args); + Py_DECREF(args); /* also removes list */ + Py_DECREF(function); +#else + result = call( + "string", "join", + Py_BuildValue("OO", list, joiner) + ); +#endif + Py_DECREF(joiner); + + return result; +} + +static PyObject* +pattern_findall(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + PyObject* list; + int status; + int i, b, e; + + PyObject* string; + int start = 0; + int end = INT_MAX; + static char* kwlist[] = { "source", "pos", "endpos", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|ii:findall", kwlist, + &string, &start, &end)) + return NULL; + + string = state_init(&state, self, string, start, end); + if (!string) + return NULL; + + list = PyList_New(0); + if (!list) { + state_fini(&state); + return NULL; + } + + while (state.start <= state.end) { + + PyObject* item; + + state_reset(&state); + + state.ptr = state.start; + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + if (status <= 0) { + if (status == 0) + break; + pattern_error(status); + goto error; + } + + /* don't bother to build a match object */ + switch (self->groups) { + case 0: + b = STATE_OFFSET(&state, state.start); + e = STATE_OFFSET(&state, state.ptr); + item = PySequence_GetSlice(string, b, e); + if (!item) + goto error; + break; + case 1: + item = state_getslice(&state, 1, string, 1); + if (!item) + goto error; + break; + default: + item = PyTuple_New(self->groups); + if (!item) + goto error; + for (i = 0; i < self->groups; i++) { + PyObject* o = state_getslice(&state, i+1, string, 1); + if (!o) { + Py_DECREF(item); + goto error; + } + PyTuple_SET_ITEM(item, i, o); + } + break; + } + + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + if (state.ptr == state.start) + state.start = (void*) ((char*) state.ptr + state.charsize); + else + state.start = state.ptr; + + } + + state_fini(&state); + return list; + +error: + Py_DECREF(list); + state_fini(&state); + return NULL; + +} + +#if PY_VERSION_HEX >= 0x02020000 +static PyObject* +pattern_finditer(PatternObject* pattern, PyObject* args) +{ + PyObject* scanner; + PyObject* search; + PyObject* iterator; + + scanner = pattern_scanner(pattern, args); + if (!scanner) + return NULL; + + search = PyObject_GetAttrString(scanner, "search"); + Py_DECREF(scanner); + if (!search) + return NULL; + + iterator = PyCallIter_New(search, Py_None); + Py_DECREF(search); + + return iterator; +} +#endif + +static PyObject* +pattern_split(PatternObject* self, PyObject* args, PyObject* kw) +{ + SRE_STATE state; + PyObject* list; + PyObject* item; + int status; + int n; + int i; + void* last; + + PyObject* string; + int maxsplit = 0; + static char* kwlist[] = { "source", "maxsplit", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "O|i:split", kwlist, + &string, &maxsplit)) + return NULL; + + string = state_init(&state, self, string, 0, INT_MAX); + if (!string) + return NULL; + + list = PyList_New(0); + if (!list) { + state_fini(&state); + return NULL; + } + + n = 0; + last = state.start; + + while (!maxsplit || n < maxsplit) { + + state_reset(&state); + + state.ptr = state.start; + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + if (status <= 0) { + if (status == 0) + break; + pattern_error(status); + goto error; + } + + if (state.start == state.ptr) { + if (last == state.end) + break; + /* skip one character */ + state.start = (void*) ((char*) state.ptr + state.charsize); + continue; + } + + /* get segment before this match */ + item = PySequence_GetSlice( + string, STATE_OFFSET(&state, last), + STATE_OFFSET(&state, state.start) + ); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + /* add groups (if any) */ + for (i = 0; i < self->groups; i++) { + item = state_getslice(&state, i+1, string, 0); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + n = n + 1; + + last = state.start = state.ptr; + + } + + /* get segment following last match (even if empty) */ + item = PySequence_GetSlice( + string, STATE_OFFSET(&state, last), state.endpos + ); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + state_fini(&state); + return list; + +error: + Py_DECREF(list); + state_fini(&state); + return NULL; + +} + +static PyObject* +pattern_subx(PatternObject* self, PyObject* template, PyObject* string, + int count, int subn) +{ + SRE_STATE state; + PyObject* list; + PyObject* item; + PyObject* filter; + PyObject* args; + PyObject* match; + void* ptr; + int status; + int n; + int i, b, e; + int filter_is_callable; + + if (PyCallable_Check(template)) { + /* sub/subn takes either a function or a template */ + filter = template; + Py_INCREF(filter); + filter_is_callable = 1; + } else { + /* if not callable, check if it's a literal string */ + int literal; + ptr = getstring(template, &n, &b); + if (ptr) { + if (b == 1) { + literal = sre_literal_template(ptr, n); + } else { +#if defined(HAVE_UNICODE) + literal = sre_uliteral_template(ptr, n); +#endif + } + } else { + PyErr_Clear(); + literal = 0; + } + if (literal) { + filter = template; + Py_INCREF(filter); + filter_is_callable = 0; + } else { + /* not a literal; hand it over to the template compiler */ + filter = call( + SRE_MODULE, "_subx", + Py_BuildValue("OO", self, template) + ); + if (!filter) + return NULL; + filter_is_callable = PyCallable_Check(filter); + } + } + + string = state_init(&state, self, string, 0, INT_MAX); + if (!string) { + Py_DECREF(filter); + return NULL; + } + + list = PyList_New(0); + if (!list) { + Py_DECREF(filter); + state_fini(&state); + return NULL; + } + + n = i = 0; + + while (!count || n < count) { + + state_reset(&state); + + state.ptr = state.start; + + if (state.charsize == 1) { + status = sre_search(&state, PatternObject_GetCode(self)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(&state, PatternObject_GetCode(self)); +#endif + } + + if (status <= 0) { + if (status == 0) + break; + pattern_error(status); + goto error; + } + + b = STATE_OFFSET(&state, state.start); + e = STATE_OFFSET(&state, state.ptr); + + if (i < b) { + /* get segment before this match */ + item = PySequence_GetSlice(string, i, b); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + + } else if (i == b && i == e && n > 0) + /* ignore empty match on latest position */ + goto next; + + if (filter_is_callable) { + /* pass match object through filter */ + match = pattern_new_match(self, &state, 1); + if (!match) + goto error; + args = Py_BuildValue("(O)", match); + if (!args) { + Py_DECREF(match); + goto error; + } + item = PyObject_CallObject(filter, args); + Py_DECREF(args); + Py_DECREF(match); + if (!item) + goto error; + } else { + /* filter is literal string */ + item = filter; + Py_INCREF(item); + } + + /* add to list */ + if (item != Py_None) { + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + i = e; + n = n + 1; + +next: + /* move on */ + if (state.ptr == state.start) + state.start = (void*) ((char*) state.ptr + state.charsize); + else + state.start = state.ptr; + + } + + /* get segment following last match */ + if (i < state.endpos) { + item = PySequence_GetSlice(string, i, state.endpos); + if (!item) + goto error; + status = PyList_Append(list, item); + Py_DECREF(item); + if (status < 0) + goto error; + } + + state_fini(&state); + + Py_DECREF(filter); + + /* convert list to single string (also removes list) */ + item = join_list(list, self->pattern); + + if (!item) + return NULL; + + if (subn) + return Py_BuildValue("Ni", item, n); + + return item; + +error: + Py_DECREF(list); + state_fini(&state); + Py_DECREF(filter); + return NULL; + +} + +static PyObject* +pattern_sub(PatternObject* self, PyObject* args, PyObject* kw) +{ + PyObject* template; + PyObject* string; + int count = 0; + static char* kwlist[] = { "repl", "string", "count", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|i:sub", kwlist, + &template, &string, &count)) + return NULL; + + return pattern_subx(self, template, string, count, 0); +} + +static PyObject* +pattern_subn(PatternObject* self, PyObject* args, PyObject* kw) +{ + PyObject* template; + PyObject* string; + int count = 0; + static char* kwlist[] = { "repl", "string", "count", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "OO|i:subn", kwlist, + &template, &string, &count)) + return NULL; + + return pattern_subx(self, template, string, count, 1); +} + +static PyObject* +pattern_copy(PatternObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + PatternObject* copy; + int offset; + + if (args != Py_None && !PyArg_ParseTuple(args, ":__copy__")) + return NULL; + + copy = PyObject_NEW_VAR(PatternObject, &Pattern_Type, self->codesize); + if (!copy) + return NULL; + + offset = offsetof(PatternObject, groups); + + Py_XINCREF(self->groupindex); + Py_XINCREF(self->indexgroup); + Py_XINCREF(self->pattern); + + memcpy((char*) copy + offset, (char*) self + offset, + sizeof(PatternObject) + self->codesize * sizeof(SRE_CODE) - offset); + + return (PyObject*) copy; +#else + PyErr_SetString(PyExc_TypeError, "cannot copy this pattern object"); + return NULL; +#endif +} + +static PyObject* +pattern_deepcopy(PatternObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + PatternObject* copy; + + PyObject* memo; + if (!PyArg_ParseTuple(args, "O:__deepcopy__", &memo)) + return NULL; + + copy = (PatternObject*) pattern_copy(self, Py_None); + if (!copy) + return NULL; + + if (!deepcopy(©->groupindex, memo) || + !deepcopy(©->indexgroup, memo) || + !deepcopy(©->pattern, memo)) { + Py_DECREF(copy); + return NULL; + } + +#else + PyErr_SetString(PyExc_TypeError, "cannot deepcopy this pattern object"); + return NULL; +#endif +} + +static PyMethodDef pattern_methods[] = { + {"match", (PyCFunction) pattern_match, METH_VARARGS|METH_KEYWORDS}, + {"search", (PyCFunction) pattern_search, METH_VARARGS|METH_KEYWORDS}, + {"sub", (PyCFunction) pattern_sub, METH_VARARGS|METH_KEYWORDS}, + {"subn", (PyCFunction) pattern_subn, METH_VARARGS|METH_KEYWORDS}, + {"split", (PyCFunction) pattern_split, METH_VARARGS|METH_KEYWORDS}, + {"findall", (PyCFunction) pattern_findall, METH_VARARGS|METH_KEYWORDS}, +#if PY_VERSION_HEX >= 0x02020000 + {"finditer", (PyCFunction) pattern_finditer, METH_VARARGS}, +#endif + {"scanner", (PyCFunction) pattern_scanner, METH_VARARGS}, + {"__copy__", (PyCFunction) pattern_copy, METH_VARARGS}, + {"__deepcopy__", (PyCFunction) pattern_deepcopy, METH_VARARGS}, + {NULL, NULL} +}; + +static PyObject* +pattern_getattr(PatternObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(pattern_methods, (PyObject*) self, name); + + if (res) + return res; + + PyErr_Clear(); + + /* attributes */ + if (!strcmp(name, "pattern")) { + Py_INCREF(self->pattern); + return self->pattern; + } + + if (!strcmp(name, "flags")) + return Py_BuildValue("i", self->flags); + + if (!strcmp(name, "groups")) + return Py_BuildValue("i", self->groups); + + if (!strcmp(name, "groupindex") && self->groupindex) { + Py_INCREF(self->groupindex); + return self->groupindex; + } + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +statichere PyTypeObject Pattern_Type = { + PyObject_HEAD_INIT(NULL) + 0, "_" SRE_MODULE ".SRE_Pattern", + sizeof(PatternObject), sizeof(SRE_CODE), + (destructor)pattern_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)pattern_getattr /*tp_getattr*/ +}; + +/* -------------------------------------------------------------------- */ +/* match methods */ + +static void +match_dealloc(MatchObject* self) +{ + Py_XDECREF(self->regs); + Py_XDECREF(self->string); + Py_DECREF(self->pattern); + PyObject_DEL(self); +} + +static PyObject* +match_getslice_by_index(MatchObject* self, int index, PyObject* def) +{ + if (index < 0 || index >= self->groups) { + /* raise IndexError if we were given a bad group number */ + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + index *= 2; + + if (self->string == Py_None || self->mark[index] < 0) { + /* return default value if the string or group is undefined */ + Py_INCREF(def); + return def; + } + + return PySequence_GetSlice( + self->string, self->mark[index], self->mark[index+1] + ); +} + +static int +match_getindex(MatchObject* self, PyObject* index) +{ + int i; + + if (PyInt_Check(index)) + return (int) PyInt_AS_LONG(index); + + i = -1; + + if (self->pattern->groupindex) { + index = PyObject_GetItem(self->pattern->groupindex, index); + if (index) { + if (PyInt_Check(index)) + i = (int) PyInt_AS_LONG(index); + Py_DECREF(index); + } else + PyErr_Clear(); + } + + return i; +} + +static PyObject* +match_getslice(MatchObject* self, PyObject* index, PyObject* def) +{ + return match_getslice_by_index(self, match_getindex(self, index), def); +} + +static PyObject* +match_expand(MatchObject* self, PyObject* args) +{ + PyObject* template; + if (!PyArg_ParseTuple(args, "O:expand", &template)) + return NULL; + + /* delegate to Python code */ + return call( + SRE_MODULE, "_expand", + Py_BuildValue("OOO", self->pattern, self, template) + ); +} + +static PyObject* +match_group(MatchObject* self, PyObject* args) +{ + PyObject* result; + int i, size; + + size = PyTuple_GET_SIZE(args); + + switch (size) { + case 0: + result = match_getslice(self, Py_False, Py_None); + break; + case 1: + result = match_getslice(self, PyTuple_GET_ITEM(args, 0), Py_None); + break; + default: + /* fetch multiple items */ + result = PyTuple_New(size); + if (!result) + return NULL; + for (i = 0; i < size; i++) { + PyObject* item = match_getslice( + self, PyTuple_GET_ITEM(args, i), Py_None + ); + if (!item) { + Py_DECREF(result); + return NULL; + } + PyTuple_SET_ITEM(result, i, item); + } + break; + } + return result; +} + +static PyObject* +match_groups(MatchObject* self, PyObject* args, PyObject* kw) +{ + PyObject* result; + int index; + + PyObject* def = Py_None; + static char* kwlist[] = { "default", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "|O:groups", kwlist, &def)) + return NULL; + + result = PyTuple_New(self->groups-1); + if (!result) + return NULL; + + for (index = 1; index < self->groups; index++) { + PyObject* item; + item = match_getslice_by_index(self, index, def); + if (!item) { + Py_DECREF(result); + return NULL; + } + PyTuple_SET_ITEM(result, index-1, item); + } + + return result; +} + +static PyObject* +match_groupdict(MatchObject* self, PyObject* args, PyObject* kw) +{ + PyObject* result; + PyObject* keys; + int index; + + PyObject* def = Py_None; + static char* kwlist[] = { "default", NULL }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "|O:groupdict", kwlist, &def)) + return NULL; + + result = PyDict_New(); + if (!result || !self->pattern->groupindex) + return result; + + keys = PyMapping_Keys(self->pattern->groupindex); + if (!keys) + goto failed; + + for (index = 0; index < PyList_GET_SIZE(keys); index++) { + int status; + PyObject* key; + PyObject* value; + key = PyList_GET_ITEM(keys, index); + if (!key) + goto failed; + value = match_getslice(self, key, def); + if (!value) { + Py_DECREF(key); + goto failed; + } + status = PyDict_SetItem(result, key, value); + Py_DECREF(value); + if (status < 0) + goto failed; + } + + Py_DECREF(keys); + + return result; + +failed: + Py_DECREF(keys); + Py_DECREF(result); + return NULL; +} + +static PyObject* +match_start(MatchObject* self, PyObject* args) +{ + int index; + + PyObject* index_ = Py_False; /* zero */ + if (!PyArg_ParseTuple(args, "|O:start", &index_)) + return NULL; + + index = match_getindex(self, index_); + + if (index < 0 || index >= self->groups) { + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + /* mark is -1 if group is undefined */ + return Py_BuildValue("i", self->mark[index*2]); +} + +static PyObject* +match_end(MatchObject* self, PyObject* args) +{ + int index; + + PyObject* index_ = Py_False; /* zero */ + if (!PyArg_ParseTuple(args, "|O:end", &index_)) + return NULL; + + index = match_getindex(self, index_); + + if (index < 0 || index >= self->groups) { + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + /* mark is -1 if group is undefined */ + return Py_BuildValue("i", self->mark[index*2+1]); +} + +LOCAL(PyObject*) +_pair(int i1, int i2) +{ + PyObject* pair; + PyObject* item; + + pair = PyTuple_New(2); + if (!pair) + return NULL; + + item = PyInt_FromLong(i1); + if (!item) + goto error; + PyTuple_SET_ITEM(pair, 0, item); + + item = PyInt_FromLong(i2); + if (!item) + goto error; + PyTuple_SET_ITEM(pair, 1, item); + + return pair; + + error: + Py_DECREF(pair); + return NULL; +} + +static PyObject* +match_span(MatchObject* self, PyObject* args) +{ + int index; + + PyObject* index_ = Py_False; /* zero */ + if (!PyArg_ParseTuple(args, "|O:span", &index_)) + return NULL; + + index = match_getindex(self, index_); + + if (index < 0 || index >= self->groups) { + PyErr_SetString( + PyExc_IndexError, + "no such group" + ); + return NULL; + } + + /* marks are -1 if group is undefined */ + return _pair(self->mark[index*2], self->mark[index*2+1]); +} + +static PyObject* +match_regs(MatchObject* self) +{ + PyObject* regs; + PyObject* item; + int index; + + regs = PyTuple_New(self->groups); + if (!regs) + return NULL; + + for (index = 0; index < self->groups; index++) { + item = _pair(self->mark[index*2], self->mark[index*2+1]); + if (!item) { + Py_DECREF(regs); + return NULL; + } + PyTuple_SET_ITEM(regs, index, item); + } + + Py_INCREF(regs); + self->regs = regs; + + return regs; +} + +static PyObject* +match_copy(MatchObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + MatchObject* copy; + int slots, offset; + + if (args != Py_None && !PyArg_ParseTuple(args, ":__copy__")) + return NULL; + + slots = 2 * (self->pattern->groups+1); + + copy = PyObject_NEW_VAR(MatchObject, &Match_Type, slots); + if (!copy) + return NULL; + + /* this value a constant, but any compiler should be able to + figure that out all by itself */ + offset = offsetof(MatchObject, string); + + Py_XINCREF(self->pattern); + Py_XINCREF(self->string); + Py_XINCREF(self->regs); + + memcpy((char*) copy + offset, (char*) self + offset, + sizeof(MatchObject) + slots * sizeof(int) - offset); + + return (PyObject*) copy; +#else + PyErr_SetString(PyExc_TypeError, "cannot copy this match object"); + return NULL; +#endif +} + +static PyObject* +match_deepcopy(MatchObject* self, PyObject* args) +{ +#ifdef USE_BUILTIN_COPY + MatchObject* copy; + + PyObject* memo; + if (!PyArg_ParseTuple(args, "O:__deepcopy__", &memo)) + return NULL; + + copy = (MatchObject*) match_copy(self, Py_None); + if (!copy) + return NULL; + + if (!deepcopy((PyObject**) ©->pattern, memo) || + !deepcopy(©->string, memo) || + !deepcopy(©->regs, memo)) { + Py_DECREF(copy); + return NULL; + } + +#else + PyErr_SetString(PyExc_TypeError, "cannot deepcopy this match object"); + return NULL; +#endif +} + +static PyMethodDef match_methods[] = { + {"group", (PyCFunction) match_group, METH_VARARGS}, + {"start", (PyCFunction) match_start, METH_VARARGS}, + {"end", (PyCFunction) match_end, METH_VARARGS}, + {"span", (PyCFunction) match_span, METH_VARARGS}, + {"groups", (PyCFunction) match_groups, METH_VARARGS|METH_KEYWORDS}, + {"groupdict", (PyCFunction) match_groupdict, METH_VARARGS|METH_KEYWORDS}, + {"expand", (PyCFunction) match_expand, METH_VARARGS}, + {"__copy__", (PyCFunction) match_copy, METH_VARARGS}, + {"__deepcopy__", (PyCFunction) match_deepcopy, METH_VARARGS}, + {NULL, NULL} +}; + +static PyObject* +match_getattr(MatchObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(match_methods, (PyObject*) self, name); + if (res) + return res; + + PyErr_Clear(); + + if (!strcmp(name, "lastindex")) { + if (self->lastindex >= 0) + return Py_BuildValue("i", self->lastindex); + Py_INCREF(Py_None); + return Py_None; + } + + if (!strcmp(name, "lastgroup")) { + if (self->pattern->indexgroup && self->lastindex >= 0) { + PyObject* result = PySequence_GetItem( + self->pattern->indexgroup, self->lastindex + ); + if (result) + return result; + PyErr_Clear(); + } + Py_INCREF(Py_None); + return Py_None; + } + + if (!strcmp(name, "string")) { + if (self->string) { + Py_INCREF(self->string); + return self->string; + } else { + Py_INCREF(Py_None); + return Py_None; + } + } + + if (!strcmp(name, "regs")) { + if (self->regs) { + Py_INCREF(self->regs); + return self->regs; + } else + return match_regs(self); + } + + if (!strcmp(name, "re")) { + Py_INCREF(self->pattern); + return (PyObject*) self->pattern; + } + + if (!strcmp(name, "pos")) + return Py_BuildValue("i", self->pos); + + if (!strcmp(name, "endpos")) + return Py_BuildValue("i", self->endpos); + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +/* FIXME: implement setattr("string", None) as a special case (to + detach the associated string, if any */ + +statichere PyTypeObject Match_Type = { + PyObject_HEAD_INIT(NULL) + 0, "_" SRE_MODULE ".SRE_Match", + sizeof(MatchObject), sizeof(int), + (destructor)match_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)match_getattr /*tp_getattr*/ +}; + +/* -------------------------------------------------------------------- */ +/* scanner methods (experimental) */ + +static void +scanner_dealloc(ScannerObject* self) +{ + state_fini(&self->state); + Py_DECREF(self->pattern); + PyObject_DEL(self); +} + +static PyObject* +scanner_match(ScannerObject* self, PyObject* args) +{ + SRE_STATE* state = &self->state; + PyObject* match; + int status; + + state_reset(state); + + state->ptr = state->start; + + if (state->charsize == 1) { + status = sre_match(state, PatternObject_GetCode(self->pattern), 1); + } else { +#if defined(HAVE_UNICODE) + status = sre_umatch(state, PatternObject_GetCode(self->pattern), 1); +#endif + } + + match = pattern_new_match((PatternObject*) self->pattern, + state, status); + + if ((status == 0 || state->ptr == state->start) && + state->ptr < state->end) + state->start = (void*) ((char*) state->ptr + state->charsize); + else + state->start = state->ptr; + + return match; +} + + +static PyObject* +scanner_search(ScannerObject* self, PyObject* args) +{ + SRE_STATE* state = &self->state; + PyObject* match; + int status; + + state_reset(state); + + state->ptr = state->start; + + if (state->charsize == 1) { + status = sre_search(state, PatternObject_GetCode(self->pattern)); + } else { +#if defined(HAVE_UNICODE) + status = sre_usearch(state, PatternObject_GetCode(self->pattern)); +#endif + } + + match = pattern_new_match((PatternObject*) self->pattern, + state, status); + + if ((status == 0 || state->ptr == state->start) && + state->ptr < state->end) + state->start = (void*) ((char*) state->ptr + state->charsize); + else + state->start = state->ptr; + + return match; +} + +static PyMethodDef scanner_methods[] = { + /* FIXME: use METH_OLDARGS instead of 0 or fix to use METH_VARARGS */ + /* METH_OLDARGS is not in Python 1.5.2 */ + {"match", (PyCFunction) scanner_match, 0}, + {"search", (PyCFunction) scanner_search, 0}, + {NULL, NULL} +}; + +static PyObject* +scanner_getattr(ScannerObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(scanner_methods, (PyObject*) self, name); + if (res) + return res; + + PyErr_Clear(); + + /* attributes */ + if (!strcmp(name, "pattern")) { + Py_INCREF(self->pattern); + return self->pattern; + } + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +statichere PyTypeObject Scanner_Type = { + PyObject_HEAD_INIT(NULL) + 0, "_" SRE_MODULE ".SRE_Scanner", + sizeof(ScannerObject), 0, + (destructor)scanner_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)scanner_getattr, /*tp_getattr*/ +}; + +static PyMethodDef _functions[] = { + {"compile", _compile, METH_VARARGS}, + {"getcodesize", sre_codesize, METH_VARARGS}, + {"getlower", sre_getlower, METH_VARARGS}, + {NULL, NULL} +}; + +#if PY_VERSION_HEX < 0x02030000 +DL_EXPORT(void) init_sre(void) +#else +PyMODINIT_FUNC init_sre(void) +#endif +{ + PyObject* m; + PyObject* d; + PyObject* x; + + /* Patch object types */ + Pattern_Type.ob_type = Match_Type.ob_type = + Scanner_Type.ob_type = &PyType_Type; + + m = Py_InitModule("_" SRE_MODULE, _functions); + d = PyModule_GetDict(m); + + x = PyInt_FromLong(SRE_MAGIC); + if (x) { + PyDict_SetItemString(d, "MAGIC", x); + Py_DECREF(x); + } + + x = PyInt_FromLong(sizeof(SRE_CODE)); + if (x) { + PyDict_SetItemString(d, "CODESIZE", x); + Py_DECREF(x); + } + + x = PyString_FromString(copyright); + if (x) { + PyDict_SetItemString(d, "copyright", x); + Py_DECREF(x); + } +} + +#endif /* !defined(SRE_RECURSIVE) */ + +/* vim:ts=4:sw=4:et +*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_ssl.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_ssl.c new file mode 100644 index 00000000..9d26f32c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_ssl.c @@ -0,0 +1,639 @@ +/* SSL socket module + + SSL support based on patches by Brian E Gallew and Laszlo Kovacs. + + This module is imported by socket.py. It should *not* be used + directly. + +*/ + +#include "Python.h" +enum py_ssl_error { + /* these mirror ssl.h */ + PY_SSL_ERROR_NONE, + PY_SSL_ERROR_SSL, + PY_SSL_ERROR_WANT_READ, + PY_SSL_ERROR_WANT_WRITE, + PY_SSL_ERROR_WANT_X509_LOOKUP, + PY_SSL_ERROR_SYSCALL, /* look at error stack/return value/errno */ + PY_SSL_ERROR_ZERO_RETURN, + PY_SSL_ERROR_WANT_CONNECT, + /* start of non ssl.h errorcodes */ + PY_SSL_ERROR_EOF, /* special case of SSL_ERROR_SYSCALL */ + PY_SSL_ERROR_INVALID_ERROR_CODE +}; + +/* Include symbols from _socket module */ +#include "socketmodule.h" + +/* Include OpenSSL header files */ +#include "openssl/rsa.h" +#include "openssl/crypto.h" +#include "openssl/x509.h" +#include "openssl/pem.h" +#include "openssl/ssl.h" +#include "openssl/err.h" +#include "openssl/rand.h" + +/* SSL error object */ +static PyObject *PySSLErrorObject; + +/* SSL socket object */ + +#define X509_NAME_MAXLEN 256 + +/* RAND_* APIs got added to OpenSSL in 0.9.5 */ +#if OPENSSL_VERSION_NUMBER >= 0x0090500fL +# define HAVE_OPENSSL_RAND 1 +#else +# undef HAVE_OPENSSL_RAND +#endif + +typedef struct { + PyObject_HEAD + PySocketSockObject *Socket; /* Socket on which we're layered */ + SSL_CTX* ctx; + SSL* ssl; + X509* server_cert; + BIO* sbio; + char server[X509_NAME_MAXLEN]; + char issuer[X509_NAME_MAXLEN]; + +} PySSLObject; + +static PyTypeObject PySSL_Type; +static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args); +static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args); +static int wait_for_timeout(PySocketSockObject *s, int writing); + +#define PySSLObject_Check(v) ((v)->ob_type == &PySSL_Type) + +/* XXX It might be helpful to augment the error message generated + below with the name of the SSL function that generated the error. + I expect it's obvious most of the time. +*/ + +static PyObject * +PySSL_SetError(PySSLObject *obj, int ret) +{ + PyObject *v, *n, *s; + char *errstr; + int err; + enum py_ssl_error p; + + assert(ret <= 0); + + err = SSL_get_error(obj->ssl, ret); + + switch (err) { + case SSL_ERROR_ZERO_RETURN: + errstr = "TLS/SSL connection has been closed"; + p = PY_SSL_ERROR_ZERO_RETURN; + break; + case SSL_ERROR_WANT_READ: + errstr = "The operation did not complete (read)"; + p = PY_SSL_ERROR_WANT_READ; + break; + case SSL_ERROR_WANT_WRITE: + p = PY_SSL_ERROR_WANT_WRITE; + errstr = "The operation did not complete (write)"; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + p = PY_SSL_ERROR_WANT_X509_LOOKUP; + errstr = "The operation did not complete (X509 lookup)"; + break; + case SSL_ERROR_WANT_CONNECT: + p = PY_SSL_ERROR_WANT_CONNECT; + errstr = "The operation did not complete (connect)"; + break; + case SSL_ERROR_SYSCALL: + { + unsigned long e = ERR_get_error(); + if (e == 0) { + if (ret == 0 || !obj->Socket) { + p = PY_SSL_ERROR_EOF; + errstr = "EOF occurred in violation of protocol"; + } else if (ret == -1) { + /* the underlying BIO reported an I/O error */ + return obj->Socket->errorhandler(); + } else { /* possible? */ + p = PY_SSL_ERROR_SYSCALL; + errstr = "Some I/O error occurred"; + } + } else { + p = PY_SSL_ERROR_SYSCALL; + /* XXX Protected by global interpreter lock */ + errstr = ERR_error_string(e, NULL); + } + break; + } + case SSL_ERROR_SSL: + { + unsigned long e = ERR_get_error(); + p = PY_SSL_ERROR_SSL; + if (e != 0) + /* XXX Protected by global interpreter lock */ + errstr = ERR_error_string(e, NULL); + else { /* possible? */ + errstr = "A failure in the SSL library occurred"; + } + break; + } + default: + p = PY_SSL_ERROR_INVALID_ERROR_CODE; + errstr = "Invalid error code"; + } + n = PyInt_FromLong((long) p); + if (n == NULL) + return NULL; + v = PyTuple_New(2); + if (v == NULL) { + Py_DECREF(n); + return NULL; + } + + s = PyString_FromString(errstr); + if (s == NULL) { + Py_DECREF(v); + Py_DECREF(n); + } + PyTuple_SET_ITEM(v, 0, n); + PyTuple_SET_ITEM(v, 1, s); + PyErr_SetObject(PySSLErrorObject, v); + return NULL; +} + +static PySSLObject * +newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file) +{ + PySSLObject *self; + char *errstr = NULL; + int ret; + int err; + int timedout; + + self = PyObject_New(PySSLObject, &PySSL_Type); /* Create new object */ + if (self == NULL){ + errstr = "newPySSLObject error"; + goto fail; + } + memset(self->server, '\0', sizeof(char) * X509_NAME_MAXLEN); + memset(self->issuer, '\0', sizeof(char) * X509_NAME_MAXLEN); + self->server_cert = NULL; + self->ssl = NULL; + self->ctx = NULL; + self->Socket = NULL; + + if ((key_file && !cert_file) || (!key_file && cert_file)) { + errstr = "Both the key & certificate files must be specified"; + goto fail; + } + + Py_BEGIN_ALLOW_THREADS + self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */ + Py_END_ALLOW_THREADS + if (self->ctx == NULL) { + errstr = "SSL_CTX_new error"; + goto fail; + } + + if (key_file) { + Py_BEGIN_ALLOW_THREADS + ret = SSL_CTX_use_PrivateKey_file(self->ctx, key_file, + SSL_FILETYPE_PEM); + Py_END_ALLOW_THREADS + if (ret < 1) { + errstr = "SSL_CTX_use_PrivateKey_file error"; + goto fail; + } + + Py_BEGIN_ALLOW_THREADS + ret = SSL_CTX_use_certificate_chain_file(self->ctx, + cert_file); + Py_END_ALLOW_THREADS + if (ret < 1) { + errstr = "SSL_CTX_use_certificate_chain_file error"; + goto fail; + } + } + + Py_BEGIN_ALLOW_THREADS + SSL_CTX_set_verify(self->ctx, + SSL_VERIFY_NONE, NULL); /* set verify lvl */ + self->ssl = SSL_new(self->ctx); /* New ssl struct */ + Py_END_ALLOW_THREADS + SSL_set_fd(self->ssl, Sock->sock_fd); /* Set the socket for SSL */ + + /* If the socket is in non-blocking mode or timeout mode, set the BIO + * to non-blocking mode (blocking is the default) + */ + if (Sock->sock_timeout >= 0.0) { + /* Set both the read and write BIO's to non-blocking mode */ + BIO_set_nbio(SSL_get_rbio(self->ssl), 1); + BIO_set_nbio(SSL_get_wbio(self->ssl), 1); + } + + Py_BEGIN_ALLOW_THREADS + SSL_set_connect_state(self->ssl); + Py_END_ALLOW_THREADS + + /* Actually negotiate SSL connection */ + /* XXX If SSL_connect() returns 0, it's also a failure. */ + timedout = 0; + do { + Py_BEGIN_ALLOW_THREADS + ret = SSL_connect(self->ssl); + err = SSL_get_error(self->ssl, ret); + Py_END_ALLOW_THREADS + if(PyErr_CheckSignals()) { + goto fail; + } + if (err == SSL_ERROR_WANT_READ) { + timedout = wait_for_timeout(Sock, 0); + } else if (err == SSL_ERROR_WANT_WRITE) { + timedout = wait_for_timeout(Sock, 1); + } + if (timedout) { + errstr = "The connect operation timed out"; + goto fail; + } + } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); + if (ret <= 0) { + PySSL_SetError(self, ret); + goto fail; + } + self->ssl->debug = 1; + + Py_BEGIN_ALLOW_THREADS + if ((self->server_cert = SSL_get_peer_certificate(self->ssl))) { + X509_NAME_oneline(X509_get_subject_name(self->server_cert), + self->server, X509_NAME_MAXLEN); + X509_NAME_oneline(X509_get_issuer_name(self->server_cert), + self->issuer, X509_NAME_MAXLEN); + } + Py_END_ALLOW_THREADS + self->Socket = Sock; + Py_INCREF(self->Socket); + return self; + fail: + if (errstr) + PyErr_SetString(PySSLErrorObject, errstr); + Py_DECREF(self); + return NULL; +} + +static PyObject * +PySocket_ssl(PyObject *self, PyObject *args) +{ + PySSLObject *rv; + PySocketSockObject *Sock; + char *key_file = NULL; + char *cert_file = NULL; + + if (!PyArg_ParseTuple(args, "O!|zz:ssl", + PySocketModule.Sock_Type, + (PyObject*)&Sock, + &key_file, &cert_file)) + return NULL; + + rv = newPySSLObject(Sock, key_file, cert_file); + if (rv == NULL) + return NULL; + return (PyObject *)rv; +} + +PyDoc_STRVAR(ssl_doc, +"ssl(socket, [keyfile, certfile]) -> sslobject"); + +/* SSL object methods */ + +static PyObject * +PySSL_server(PySSLObject *self) +{ + return PyString_FromString(self->server); +} + +static PyObject * +PySSL_issuer(PySSLObject *self) +{ + return PyString_FromString(self->issuer); +} + + +static void PySSL_dealloc(PySSLObject *self) +{ + if (self->server_cert) /* Possible not to have one? */ + X509_free (self->server_cert); + if (self->ssl) + SSL_free(self->ssl); + if (self->ctx) + SSL_CTX_free(self->ctx); + Py_XDECREF(self->Socket); + PyObject_Del(self); +} + +/* If the socket has a timeout, do a select() on the socket. + The argument writing indicates the direction. + Return non-zero if the socket timed out, zero otherwise. + */ +static int +wait_for_timeout(PySocketSockObject *s, int writing) +{ + fd_set fds; + struct timeval tv; + int rc; + + /* Nothing to do unless we're in timeout mode (not non-blocking) */ + if (s->sock_timeout <= 0.0) + return 0; + + /* Guard against closed socket */ + if (s->sock_fd < 0) + return 0; + + /* Construct the arguments to select */ + tv.tv_sec = (int)s->sock_timeout; + tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); + FD_ZERO(&fds); + FD_SET(s->sock_fd, &fds); + + /* See if the socket is ready */ + Py_BEGIN_ALLOW_THREADS + if (writing) + rc = select(s->sock_fd+1, NULL, &fds, NULL, &tv); + else + rc = select(s->sock_fd+1, &fds, NULL, NULL, &tv); + Py_END_ALLOW_THREADS + + /* Return 1 on timeout, 0 otherwise */ + return rc == 0; +} + +static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) +{ + char *data; + int len; + int count; + int timedout; + int err; + + if (!PyArg_ParseTuple(args, "s#:write", &data, &count)) + return NULL; + + timedout = wait_for_timeout(self->Socket, 1); + if (timedout) { + PyErr_SetString(PySSLErrorObject, "The write operation timed out"); + return NULL; + } + do { + err = 0; + Py_BEGIN_ALLOW_THREADS + len = SSL_write(self->ssl, data, count); + err = SSL_get_error(self->ssl, len); + Py_END_ALLOW_THREADS + if(PyErr_CheckSignals()) { + return NULL; + } + if (err == SSL_ERROR_WANT_READ) { + timedout = wait_for_timeout(self->Socket, 0); + } else if (err == SSL_ERROR_WANT_WRITE) { + timedout = wait_for_timeout(self->Socket, 1); + } + if (timedout) { + PyErr_SetString(PySSLErrorObject, "The write operation timed out"); + return NULL; + } + } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); + if (len > 0) + return PyInt_FromLong(len); + else + return PySSL_SetError(self, len); +} + +PyDoc_STRVAR(PySSL_SSLwrite_doc, +"write(s) -> len\n\ +\n\ +Writes the string s into the SSL object. Returns the number\n\ +of bytes written."); + +static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args) +{ + PyObject *buf; + int count = 0; + int len = 1024; + int timedout; + int err; + + if (!PyArg_ParseTuple(args, "|i:read", &len)) + return NULL; + + if (!(buf = PyString_FromStringAndSize((char *) 0, len))) + return NULL; + + timedout = wait_for_timeout(self->Socket, 0); + if (timedout) { + PyErr_SetString(PySSLErrorObject, "The read operation timed out"); + Py_DECREF(buf); + return NULL; + } + do { + err = 0; + Py_BEGIN_ALLOW_THREADS + count = SSL_read(self->ssl, PyString_AsString(buf), len); + err = SSL_get_error(self->ssl, count); + Py_END_ALLOW_THREADS + if(PyErr_CheckSignals()) { + Py_DECREF(buf); + return NULL; + } + if (err == SSL_ERROR_WANT_READ) { + timedout = wait_for_timeout(self->Socket, 0); + } else if (err == SSL_ERROR_WANT_WRITE) { + timedout = wait_for_timeout(self->Socket, 1); + } + if (timedout) { + PyErr_SetString(PySSLErrorObject, "The read operation timed out"); + Py_DECREF(buf); + return NULL; + } + } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); + if (count <= 0) { + Py_DECREF(buf); + return PySSL_SetError(self, count); + } + if (count != len) + _PyString_Resize(&buf, count); + return buf; +} + +PyDoc_STRVAR(PySSL_SSLread_doc, +"read([len]) -> string\n\ +\n\ +Read up to len bytes from the SSL socket."); + +static PyMethodDef PySSLMethods[] = { + {"write", (PyCFunction)PySSL_SSLwrite, METH_VARARGS, + PySSL_SSLwrite_doc}, + {"read", (PyCFunction)PySSL_SSLread, METH_VARARGS, + PySSL_SSLread_doc}, + {"server", (PyCFunction)PySSL_server, METH_NOARGS}, + {"issuer", (PyCFunction)PySSL_issuer, METH_NOARGS}, + {NULL, NULL} +}; + +static PyObject *PySSL_getattr(PySSLObject *self, char *name) +{ + return Py_FindMethod(PySSLMethods, (PyObject *)self, name); +} + +static PyTypeObject PySSL_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "socket.SSL", /*tp_name*/ + sizeof(PySSLObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PySSL_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)PySSL_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + +#ifdef HAVE_OPENSSL_RAND + +/* helper routines for seeding the SSL PRNG */ +static PyObject * +PySSL_RAND_add(PyObject *self, PyObject *args) +{ + char *buf; + int len; + double entropy; + + if (!PyArg_ParseTuple(args, "s#d:RAND_add", &buf, &len, &entropy)) + return NULL; + RAND_add(buf, len, entropy); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(PySSL_RAND_add_doc, +"RAND_add(string, entropy)\n\ +\n\ +Mix string into the OpenSSL PRNG state. entropy (a float) is a lower\n\ +bound on the entropy contained in string."); + +static PyObject * +PySSL_RAND_status(PyObject *self) +{ + return PyInt_FromLong(RAND_status()); +} + +PyDoc_STRVAR(PySSL_RAND_status_doc, +"RAND_status() -> 0 or 1\n\ +\n\ +Returns 1 if the OpenSSL PRNG has been seeded with enough data and 0 if not.\n\ +It is necessary to seed the PRNG with RAND_add() on some platforms before\n\ +using the ssl() function."); + +static PyObject * +PySSL_RAND_egd(PyObject *self, PyObject *arg) +{ + int bytes; + + if (!PyString_Check(arg)) + return PyErr_Format(PyExc_TypeError, + "RAND_egd() expected string, found %s", + arg->ob_type->tp_name); + bytes = RAND_egd(PyString_AS_STRING(arg)); + if (bytes == -1) { + PyErr_SetString(PySSLErrorObject, + "EGD connection failed or EGD did not return " + "enough data to seed the PRNG"); + return NULL; + } + return PyInt_FromLong(bytes); +} + +PyDoc_STRVAR(PySSL_RAND_egd_doc, +"RAND_egd(path) -> bytes\n\ +\n\ +Queries the entropy gather daemon (EGD) on socket path. Returns number\n\ +of bytes read. Raises socket.sslerror if connection to EGD fails or\n\ +if it does provide enough data to seed PRNG."); + +#endif + +/* List of functions exported by this module. */ + +static PyMethodDef PySSL_methods[] = { + {"ssl", PySocket_ssl, + METH_VARARGS, ssl_doc}, +#ifdef HAVE_OPENSSL_RAND + {"RAND_add", PySSL_RAND_add, METH_VARARGS, + PySSL_RAND_add_doc}, + {"RAND_egd", PySSL_RAND_egd, METH_O, + PySSL_RAND_egd_doc}, + {"RAND_status", (PyCFunction)PySSL_RAND_status, METH_NOARGS, + PySSL_RAND_status_doc}, +#endif + {NULL, NULL} /* Sentinel */ +}; + + +PyDoc_STRVAR(module_doc, +"Implementation module for SSL socket operations. See the socket module\n\ +for documentation."); + +PyMODINIT_FUNC +init_ssl(void) +{ + PyObject *m, *d; + + PySSL_Type.ob_type = &PyType_Type; + + m = Py_InitModule3("_ssl", PySSL_methods, module_doc); + d = PyModule_GetDict(m); + + /* Load _socket module and its C API */ + if (PySocketModule_ImportModuleAndAPI()) + return; + + /* Init OpenSSL */ + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + + /* Add symbols to module dict */ + PySSLErrorObject = PyErr_NewException("socket.sslerror", NULL, NULL); + if (PySSLErrorObject == NULL) + return; + PyDict_SetItemString(d, "sslerror", PySSLErrorObject); + if (PyDict_SetItemString(d, "SSLType", + (PyObject *)&PySSL_Type) != 0) + return; + PyModule_AddIntConstant(m, "SSL_ERROR_ZERO_RETURN", + PY_SSL_ERROR_ZERO_RETURN); + PyModule_AddIntConstant(m, "SSL_ERROR_WANT_READ", + PY_SSL_ERROR_WANT_READ); + PyModule_AddIntConstant(m, "SSL_ERROR_WANT_WRITE", + PY_SSL_ERROR_WANT_WRITE); + PyModule_AddIntConstant(m, "SSL_ERROR_WANT_X509_LOOKUP", + PY_SSL_ERROR_WANT_X509_LOOKUP); + PyModule_AddIntConstant(m, "SSL_ERROR_SYSCALL", + PY_SSL_ERROR_SYSCALL); + PyModule_AddIntConstant(m, "SSL_ERROR_SSL", + PY_SSL_ERROR_SSL); + PyModule_AddIntConstant(m, "SSL_ERROR_WANT_CONNECT", + PY_SSL_ERROR_WANT_CONNECT); + /* non ssl.h errorcodes */ + PyModule_AddIntConstant(m, "SSL_ERROR_EOF", + PY_SSL_ERROR_EOF); + PyModule_AddIntConstant(m, "SSL_ERROR_INVALID_ERROR_CODE", + PY_SSL_ERROR_INVALID_ERROR_CODE); + +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_testcapimodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_testcapimodule.c new file mode 100644 index 00000000..caae20b6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_testcapimodule.c @@ -0,0 +1,642 @@ +/* + * C Extension module to test Python interpreter C APIs. + * + * The 'test_*' functions exported by this module are run as part of the + * standard Python regression test, via Lib/test/test_capi.py. + */ + +#include "Python.h" + +#ifdef WITH_THREAD +#include "pythread.h" +#endif /* WITH_THREAD */ + +static PyObject *TestError; /* set to exception object in init */ + +/* Raise TestError with test_name + ": " + msg, and return NULL. */ + +static PyObject * +raiseTestError(const char* test_name, const char* msg) +{ + char buf[2048]; + + if (strlen(test_name) + strlen(msg) > sizeof(buf) - 50) + PyErr_SetString(TestError, "internal error msg too large"); + else { + PyOS_snprintf(buf, sizeof(buf), "%s: %s", test_name, msg); + PyErr_SetString(TestError, buf); + } + return NULL; +} + +/* Test #defines from pyconfig.h (particularly the SIZEOF_* defines). + + The ones derived from autoconf on the UNIX-like OSes can be relied + upon (in the absence of sloppy cross-compiling), but the Windows + platforms have these hardcoded. Better safe than sorry. +*/ +static PyObject* +sizeof_error(const char* fatname, const char* typename, + int expected, int got) +{ + char buf[1024]; + PyOS_snprintf(buf, sizeof(buf), + "%.200s #define == %d but sizeof(%.200s) == %d", + fatname, expected, typename, got); + PyErr_SetString(TestError, buf); + return (PyObject*)NULL; +} + +static PyObject* +test_config(PyObject *self) +{ +#define CHECK_SIZEOF(FATNAME, TYPE) \ + if (FATNAME != sizeof(TYPE)) \ + return sizeof_error(#FATNAME, #TYPE, FATNAME, sizeof(TYPE)) + + CHECK_SIZEOF(SIZEOF_SHORT, short); + CHECK_SIZEOF(SIZEOF_INT, int); + CHECK_SIZEOF(SIZEOF_LONG, long); + CHECK_SIZEOF(SIZEOF_VOID_P, void*); + CHECK_SIZEOF(SIZEOF_TIME_T, time_t); +#ifdef HAVE_LONG_LONG + CHECK_SIZEOF(SIZEOF_LONG_LONG, PY_LONG_LONG); +#endif + +#undef CHECK_SIZEOF + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +test_list_api(PyObject *self) +{ + PyObject* list; + int i; + + /* SF bug 132008: PyList_Reverse segfaults */ +#define NLIST 30 + list = PyList_New(NLIST); + if (list == (PyObject*)NULL) + return (PyObject*)NULL; + /* list = range(NLIST) */ + for (i = 0; i < NLIST; ++i) { + PyObject* anint = PyInt_FromLong(i); + if (anint == (PyObject*)NULL) { + Py_DECREF(list); + return (PyObject*)NULL; + } + PyList_SET_ITEM(list, i, anint); + } + /* list.reverse(), via PyList_Reverse() */ + i = PyList_Reverse(list); /* should not blow up! */ + if (i != 0) { + Py_DECREF(list); + return (PyObject*)NULL; + } + /* Check that list == range(29, -1, -1) now */ + for (i = 0; i < NLIST; ++i) { + PyObject* anint = PyList_GET_ITEM(list, i); + if (PyInt_AS_LONG(anint) != NLIST-1-i) { + PyErr_SetString(TestError, + "test_list_api: reverse screwed up"); + Py_DECREF(list); + return (PyObject*)NULL; + } + } + Py_DECREF(list); +#undef NLIST + + Py_INCREF(Py_None); + return Py_None; +} + +static int +test_dict_inner(int count) +{ + int pos = 0, iterations = 0, i; + PyObject *dict = PyDict_New(); + PyObject *v, *k; + + if (dict == NULL) + return -1; + + for (i = 0; i < count; i++) { + v = PyInt_FromLong(i); + PyDict_SetItem(dict, v, v); + Py_DECREF(v); + } + + while (PyDict_Next(dict, &pos, &k, &v)) { + PyObject *o; + iterations++; + + i = PyInt_AS_LONG(v) + 1; + o = PyInt_FromLong(i); + if (o == NULL) + return -1; + if (PyDict_SetItem(dict, k, o) < 0) { + Py_DECREF(o); + return -1; + } + Py_DECREF(o); + } + + Py_DECREF(dict); + + if (iterations != count) { + PyErr_SetString( + TestError, + "test_dict_iteration: dict iteration went wrong "); + return -1; + } else { + return 0; + } +} + +static PyObject* +test_dict_iteration(PyObject* self) +{ + int i; + + for (i = 0; i < 200; i++) { + if (test_dict_inner(i) < 0) { + return NULL; + } + } + + Py_INCREF(Py_None); + return Py_None; +} + + +/* Tests of PyLong_{As, From}{Unsigned,}Long(), and (#ifdef HAVE_LONG_LONG) + PyLong_{As, From}{Unsigned,}LongLong(). + + Note that the meat of the test is contained in testcapi_long.h. + This is revolting, but delicate code duplication is worse: "almost + exactly the same" code is needed to test PY_LONG_LONG, but the ubiquitous + dependence on type names makes it impossible to use a parameterized + function. A giant macro would be even worse than this. A C++ template + would be perfect. + + The "report an error" functions are deliberately not part of the #include + file: if the test fails, you can set a breakpoint in the appropriate + error function directly, and crawl back from there in the debugger. +*/ + +#define UNBIND(X) Py_DECREF(X); (X) = NULL + +static PyObject * +raise_test_long_error(const char* msg) +{ + return raiseTestError("test_long_api", msg); +} + +#define TESTNAME test_long_api_inner +#define TYPENAME long +#define F_S_TO_PY PyLong_FromLong +#define F_PY_TO_S PyLong_AsLong +#define F_U_TO_PY PyLong_FromUnsignedLong +#define F_PY_TO_U PyLong_AsUnsignedLong + +#include "testcapi_long.h" + +static PyObject * +test_long_api(PyObject* self) +{ + return TESTNAME(raise_test_long_error); +} + +#undef TESTNAME +#undef TYPENAME +#undef F_S_TO_PY +#undef F_PY_TO_S +#undef F_U_TO_PY +#undef F_PY_TO_U + +#ifdef HAVE_LONG_LONG + +static PyObject * +raise_test_longlong_error(const char* msg) +{ + return raiseTestError("test_longlong_api", msg); +} + +#define TESTNAME test_longlong_api_inner +#define TYPENAME PY_LONG_LONG +#define F_S_TO_PY PyLong_FromLongLong +#define F_PY_TO_S PyLong_AsLongLong +#define F_U_TO_PY PyLong_FromUnsignedLongLong +#define F_PY_TO_U PyLong_AsUnsignedLongLong + +#include "testcapi_long.h" + +static PyObject * +test_longlong_api(PyObject* self) +{ + return TESTNAME(raise_test_longlong_error); +} + +#undef TESTNAME +#undef TYPENAME +#undef F_S_TO_PY +#undef F_PY_TO_S +#undef F_U_TO_PY +#undef F_PY_TO_U + +/* Test the L code for PyArg_ParseTuple. This should deliver a PY_LONG_LONG + for both long and int arguments. The test may leak a little memory if + it fails. +*/ +static PyObject * +test_L_code(PyObject *self) +{ + PyObject *tuple, *num; + PY_LONG_LONG value; + + tuple = PyTuple_New(1); + if (tuple == NULL) + return NULL; + + num = PyLong_FromLong(42); + if (num == NULL) + return NULL; + + PyTuple_SET_ITEM(tuple, 0, num); + + value = -1; + if (PyArg_ParseTuple(tuple, "L:test_L_code", &value) < 0) + return NULL; + if (value != 42) + return raiseTestError("test_L_code", + "L code returned wrong value for long 42"); + + Py_DECREF(num); + num = PyInt_FromLong(42); + if (num == NULL) + return NULL; + + PyTuple_SET_ITEM(tuple, 0, num); + + value = -1; + if (PyArg_ParseTuple(tuple, "L:test_L_code", &value) < 0) + return NULL; + if (value != 42) + return raiseTestError("test_L_code", + "L code returned wrong value for int 42"); + + Py_DECREF(tuple); + Py_INCREF(Py_None); + return Py_None; +} + +#endif /* ifdef HAVE_LONG_LONG */ + +/* Functions to call PyArg_ParseTuple with integer format codes, + and return the result. +*/ +static PyObject * +getargs_b(PyObject *self, PyObject *args) +{ + unsigned char value; + if (!PyArg_ParseTuple(args, "b", &value)) + return NULL; + return PyLong_FromUnsignedLong((unsigned long)value); +} + +static PyObject * +getargs_B(PyObject *self, PyObject *args) +{ + unsigned char value; + if (!PyArg_ParseTuple(args, "B", &value)) + return NULL; + return PyLong_FromUnsignedLong((unsigned long)value); +} + +static PyObject * +getargs_H(PyObject *self, PyObject *args) +{ + unsigned short value; + if (!PyArg_ParseTuple(args, "H", &value)) + return NULL; + return PyLong_FromUnsignedLong((unsigned long)value); +} + +static PyObject * +getargs_I(PyObject *self, PyObject *args) +{ + unsigned int value; + if (!PyArg_ParseTuple(args, "I", &value)) + return NULL; + return PyLong_FromUnsignedLong((unsigned long)value); +} + +static PyObject * +getargs_k(PyObject *self, PyObject *args) +{ + unsigned long value; + if (!PyArg_ParseTuple(args, "k", &value)) + return NULL; + return PyLong_FromUnsignedLong(value); +} + +static PyObject * +getargs_i(PyObject *self, PyObject *args) +{ + int value; + if (!PyArg_ParseTuple(args, "i", &value)) + return NULL; + return PyLong_FromLong((long)value); +} + +static PyObject * +getargs_l(PyObject *self, PyObject *args) +{ + long value; + if (!PyArg_ParseTuple(args, "l", &value)) + return NULL; + return PyLong_FromLong(value); +} + +#ifdef HAVE_LONG_LONG +static PyObject * +getargs_L(PyObject *self, PyObject *args) +{ + PY_LONG_LONG value; + if (!PyArg_ParseTuple(args, "L", &value)) + return NULL; + return PyLong_FromLongLong(value); +} + +static PyObject * +getargs_K(PyObject *self, PyObject *args) +{ + unsigned PY_LONG_LONG value; + if (!PyArg_ParseTuple(args, "K", &value)) + return NULL; + return PyLong_FromUnsignedLongLong(value); +} +#endif + +/* This function not only tests the 'k' getargs code, but also the + PyInt_AsUnsignedLongMask() and PyInt_AsUnsignedLongMask() functions. */ +static PyObject * +test_k_code(PyObject *self) +{ + PyObject *tuple, *num; + unsigned long value; + + tuple = PyTuple_New(1); + if (tuple == NULL) + return NULL; + + /* a number larger than ULONG_MAX even on 64-bit platforms */ + num = PyLong_FromString("FFFFFFFFFFFFFFFFFFFFFFFF", NULL, 16); + if (num == NULL) + return NULL; + + value = PyInt_AsUnsignedLongMask(num); + if (value != ULONG_MAX) + return raiseTestError("test_k_code", + "PyInt_AsUnsignedLongMask() returned wrong value for long 0xFFF...FFF"); + + PyTuple_SET_ITEM(tuple, 0, num); + + value = -1; + if (PyArg_ParseTuple(tuple, "k:test_k_code", &value) < 0) + return NULL; + if (value != ULONG_MAX) + return raiseTestError("test_k_code", + "k code returned wrong value for long 0xFFF...FFF"); + + Py_DECREF(num); + num = PyLong_FromString("-FFFFFFFF000000000000000042", NULL, 16); + if (num == NULL) + return NULL; + + value = PyInt_AsUnsignedLongMask(num); + if (value != (unsigned long)-0x42) + return raiseTestError("test_k_code", + "PyInt_AsUnsignedLongMask() returned wrong value for long 0xFFF...FFF"); + + PyTuple_SET_ITEM(tuple, 0, num); + + value = -1; + if (PyArg_ParseTuple(tuple, "k:test_k_code", &value) < 0) + return NULL; + if (value != (unsigned long)-0x42) + return raiseTestError("test_k_code", + "k code returned wrong value for long -0xFFF..000042"); + + Py_DECREF(tuple); + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef Py_USING_UNICODE + +/* Test the u and u# codes for PyArg_ParseTuple. May leak memory in case + of an error. +*/ +static PyObject * +test_u_code(PyObject *self) +{ + PyObject *tuple, *obj; + Py_UNICODE *value; + int len; + + tuple = PyTuple_New(1); + if (tuple == NULL) + return NULL; + + obj = PyUnicode_Decode("test", strlen("test"), + "ascii", NULL); + if (obj == NULL) + return NULL; + + PyTuple_SET_ITEM(tuple, 0, obj); + + value = 0; + if (PyArg_ParseTuple(tuple, "u:test_u_code", &value) < 0) + return NULL; + if (value != PyUnicode_AS_UNICODE(obj)) + return raiseTestError("test_u_code", + "u code returned wrong value for u'test'"); + value = 0; + if (PyArg_ParseTuple(tuple, "u#:test_u_code", &value, &len) < 0) + return NULL; + if (value != PyUnicode_AS_UNICODE(obj) || + len != PyUnicode_GET_SIZE(obj)) + return raiseTestError("test_u_code", + "u# code returned wrong values for u'test'"); + + Py_DECREF(tuple); + Py_INCREF(Py_None); + return Py_None; +} + +#endif + +/* Simple test of _PyLong_NumBits and _PyLong_Sign. */ +static PyObject * +test_long_numbits(PyObject *self) +{ + struct triple { + long input; + size_t nbits; + int sign; + } testcases[] = {{0, 0, 0}, + {1L, 1, 1}, + {-1L, 1, -1}, + {2L, 2, 1}, + {-2L, 2, -1}, + {3L, 2, 1}, + {-3L, 2, -1}, + {4L, 3, 1}, + {-4L, 3, -1}, + {0x7fffL, 15, 1}, /* one Python long digit */ + {-0x7fffL, 15, -1}, + {0xffffL, 16, 1}, + {-0xffffL, 16, -1}, + {0xfffffffL, 28, 1}, + {-0xfffffffL, 28, -1}}; + int i; + + for (i = 0; i < sizeof(testcases) / sizeof(struct triple); ++i) { + PyObject *plong = PyLong_FromLong(testcases[i].input); + size_t nbits = _PyLong_NumBits(plong); + int sign = _PyLong_Sign(plong); + + Py_DECREF(plong); + if (nbits != testcases[i].nbits) + return raiseTestError("test_long_numbits", + "wrong result for _PyLong_NumBits"); + if (sign != testcases[i].sign) + return raiseTestError("test_long_numbits", + "wrong result for _PyLong_Sign"); + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +raise_exception(PyObject *self, PyObject *args) +{ + PyObject *exc; + PyObject *exc_args, *v; + int num_args, i; + + if (!PyArg_ParseTuple(args, "Oi:raise_exception", + &exc, &num_args)) + return NULL; + + exc_args = PyTuple_New(num_args); + if (exc_args == NULL) + return NULL; + for (i = 0; i < num_args; ++i) { + v = PyInt_FromLong(i); + if (v == NULL) { + Py_DECREF(exc_args); + return NULL; + } + PyTuple_SET_ITEM(exc_args, i, v); + } + PyErr_SetObject(exc, exc_args); + return NULL; +} + +#ifdef WITH_THREAD + +void _make_call(void *callable) +{ + PyObject *rc; + PyGILState_STATE s = PyGILState_Ensure(); + rc = PyObject_CallFunction(callable, ""); + Py_XDECREF(rc); + PyGILState_Release(s); +} + +static PyObject * +test_thread_state(PyObject *self, PyObject *args) +{ + PyObject *fn; + if (!PyArg_ParseTuple(args, "O:test_thread_state", &fn)) + return NULL; + /* Ensure Python is setup for threading */ + PyEval_InitThreads(); + /* Start a new thread for our callback. */ + PyThread_start_new_thread( _make_call, fn); + /* Make the callback with the thread lock held by this thread */ + _make_call(fn); + /* Do it all again, but this time with the thread-lock released */ + Py_BEGIN_ALLOW_THREADS + _make_call(fn); + Py_END_ALLOW_THREADS + /* And once more with and without a thread + XXX - should use a lock and work out exactly what we are trying + to test + */ + Py_BEGIN_ALLOW_THREADS + PyThread_start_new_thread( _make_call, fn); + _make_call(fn); + Py_END_ALLOW_THREADS + Py_INCREF(Py_None); + return Py_None; +} +#endif + +static PyMethodDef TestMethods[] = { + {"raise_exception", raise_exception, METH_VARARGS}, + {"test_config", (PyCFunction)test_config, METH_NOARGS}, + {"test_list_api", (PyCFunction)test_list_api, METH_NOARGS}, + {"test_dict_iteration", (PyCFunction)test_dict_iteration,METH_NOARGS}, + {"test_long_api", (PyCFunction)test_long_api, METH_NOARGS}, + {"test_long_numbits", (PyCFunction)test_long_numbits, METH_NOARGS}, + {"test_k_code", (PyCFunction)test_k_code, METH_NOARGS}, + + {"getargs_b", (PyCFunction)getargs_b, METH_VARARGS}, + {"getargs_B", (PyCFunction)getargs_B, METH_VARARGS}, + {"getargs_H", (PyCFunction)getargs_H, METH_VARARGS}, + {"getargs_I", (PyCFunction)getargs_I, METH_VARARGS}, + {"getargs_k", (PyCFunction)getargs_k, METH_VARARGS}, + {"getargs_i", (PyCFunction)getargs_i, METH_VARARGS}, + {"getargs_l", (PyCFunction)getargs_l, METH_VARARGS}, +#ifdef HAVE_LONG_LONG + {"getargs_L", (PyCFunction)getargs_L, METH_VARARGS}, + {"getargs_K", (PyCFunction)getargs_K, METH_VARARGS}, + {"test_longlong_api", (PyCFunction)test_longlong_api, METH_NOARGS}, + {"test_L_code", (PyCFunction)test_L_code, METH_NOARGS}, +#endif +#ifdef Py_USING_UNICODE + {"test_u_code", (PyCFunction)test_u_code, METH_NOARGS}, +#endif +#ifdef WITH_THREAD + {"_test_thread_state", (PyCFunction)test_thread_state, METH_VARARGS}, +#endif + {NULL, NULL} /* sentinel */ +}; + +#define AddSym(d, n, f, v) {PyObject *o = f(v); PyDict_SetItemString(d, n, o); Py_DECREF(o);} + +PyMODINIT_FUNC +init_testcapi(void) +{ + PyObject *m; + + m = Py_InitModule("_testcapi", TestMethods); + + PyModule_AddObject(m, "UCHAR_MAX", PyInt_FromLong(UCHAR_MAX)); + PyModule_AddObject(m, "USHRT_MAX", PyInt_FromLong(USHRT_MAX)); + PyModule_AddObject(m, "UINT_MAX", PyLong_FromUnsignedLong(UINT_MAX)); + PyModule_AddObject(m, "ULONG_MAX", PyLong_FromUnsignedLong(ULONG_MAX)); + PyModule_AddObject(m, "INT_MIN", PyInt_FromLong(INT_MIN)); + PyModule_AddObject(m, "LONG_MIN", PyInt_FromLong(LONG_MIN)); + PyModule_AddObject(m, "INT_MAX", PyInt_FromLong(INT_MAX)); + PyModule_AddObject(m, "LONG_MAX", PyInt_FromLong(LONG_MAX)); + + TestError = PyErr_NewException("_testcapi.error", NULL, NULL); + Py_INCREF(TestError); + PyModule_AddObject(m, "error", TestError); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_tkinter.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_tkinter.c new file mode 100644 index 00000000..772fe110 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_tkinter.c @@ -0,0 +1,3203 @@ +/*********************************************************** +Copyright (C) 1994 Steen Lumholt. + + All Rights Reserved + +******************************************************************/ + +/* _tkinter.c -- Interface to libtk.a and libtcl.a. */ + +/* TCL/TK VERSION INFO: + + Only Tcl/Tk 8.2 and later are supported. Older versions are not + supported. (Use Python 2.2 if you cannot upgrade your Tcl/Tk + libraries.) +*/ + +/* XXX Further speed-up ideas, involving Tcl 8.0 features: + + - Register a new Tcl type, "Python callable", which can be called more + efficiently and passed to Tcl_EvalObj() directly (if this is possible). + +*/ + + +#include "Python.h" +#include + +#ifdef WITH_THREAD +#include "pythread.h" +#endif + +#ifdef MS_WINDOWS +#include +#endif + +#ifdef macintosh +#define MAC_TCL +#endif + +/* Allow using this code in Python 2.[12] */ +#ifndef PyDoc_STRVAR +#define PyDoc_STRVAR(name,str) static char name[] = str +#endif + +#ifndef PyMODINIT_FUNC +#define PyMODINIT_FUNC void +#endif + +#ifndef PyBool_Check +#define PyBool_Check(o) 0 +#define PyBool_FromLong PyInt_FromLong +#endif + +/* Starting with Tcl 8.4, many APIs offer const-correctness. Unfortunately, + making _tkinter correct for this API means to break earlier + versions. USE_COMPAT_CONST allows to make _tkinter work with both 8.4 and + earlier versions. Once Tcl releases before 8.4 don't need to be supported + anymore, this should go. */ +#define USE_COMPAT_CONST + +/* If Tcl is compiled for threads, we must also define TCL_THREAD. We define + it always; if Tcl is not threaded, the thread functions in + Tcl are empty. */ +#define TCL_THREADS + +#ifdef TK_FRAMEWORK +#include +#include +#else +#include +#include +#endif + +/* For Tcl 8.2 and 8.3, CONST* is not defined (except on Cygwin). */ +#ifndef CONST84_RETURN +#define CONST84_RETURN +#undef CONST +#define CONST +#endif + +#define TKMAJORMINOR (TK_MAJOR_VERSION*1000 + TK_MINOR_VERSION) + +#if TKMAJORMINOR < 8002 +#error "Tk older than 8.2 not supported" +#endif + +/* Unicode conversion assumes that Tcl_UniChar is two bytes. + We cannot test this directly, so we test UTF-8 size instead, + expecting that TCL_UTF_MAX is changed if Tcl ever supports + either UTF-16 or UCS-4. + Redhat 8 sets TCL_UTF_MAX to 6, and uses wchar_t for + Tcl_Unichar. This is also ok as long as Python uses UCS-4, + as well. +*/ +#if TCL_UTF_MAX != 3 && !(defined(Py_UNICODE_WIDE) && TCL_UTF_MAX==6) +#error "unsupported Tcl configuration" +#endif + +#if defined(macintosh) +/* Sigh, we have to include this to get at the tcl qd pointer */ +#include +/* And this one we need to clear the menu bar */ +#include +#endif + +#if !(defined(MS_WINDOWS) || defined(__CYGWIN__) || defined(macintosh)) +/* Mac has it, but it doesn't really work:-( */ +#define HAVE_CREATEFILEHANDLER +#endif + +#ifdef HAVE_CREATEFILEHANDLER + +/* Tcl_CreateFileHandler() changed several times; these macros deal with the + messiness. In Tcl 8.0 and later, it is not available on Windows (and on + Unix, only because Jack added it back); when available on Windows, it only + applies to sockets. */ + +#ifdef MS_WINDOWS +#define FHANDLETYPE TCL_WIN_SOCKET +#else +#define FHANDLETYPE TCL_UNIX_FD +#endif + +/* If Tcl can wait for a Unix file descriptor, define the EventHook() routine + which uses this to handle Tcl events while the user is typing commands. */ + +#if FHANDLETYPE == TCL_UNIX_FD +#define WAIT_FOR_STDIN +#endif + +#endif /* HAVE_CREATEFILEHANDLER */ + +#ifdef MS_WINDOWS +#include +#define WAIT_FOR_STDIN +#endif + +#ifdef WITH_THREAD + +/* The threading situation is complicated. Tcl is not thread-safe, except + when configured with --enable-threads. + So we need to use a lock around all uses of Tcl. Previously, the Python + interpreter lock was used for this. However, this causes problems when + other Python threads need to run while Tcl is blocked waiting for events. + + To solve this problem, a separate lock for Tcl is introduced. Holding it + is incompatible with holding Python's interpreter lock. The following four + macros manipulate both locks together. + + ENTER_TCL and LEAVE_TCL are brackets, just like Py_BEGIN_ALLOW_THREADS and + Py_END_ALLOW_THREADS. They should be used whenever a call into Tcl is made + that could call an event handler, or otherwise affect the state of a Tcl + interpreter. These assume that the surrounding code has the Python + interpreter lock; inside the brackets, the Python interpreter lock has been + released and the lock for Tcl has been acquired. + + Sometimes, it is necessary to have both the Python lock and the Tcl lock. + (For example, when transferring data from the Tcl interpreter result to a + Python string object.) This can be done by using different macros to close + the ENTER_TCL block: ENTER_OVERLAP reacquires the Python lock (and restores + the thread state) but doesn't release the Tcl lock; LEAVE_OVERLAP_TCL + releases the Tcl lock. + + By contrast, ENTER_PYTHON and LEAVE_PYTHON are used in Tcl event + handlers when the handler needs to use Python. Such event handlers are + entered while the lock for Tcl is held; the event handler presumably needs + to use Python. ENTER_PYTHON releases the lock for Tcl and acquires + the Python interpreter lock, restoring the appropriate thread state, and + LEAVE_PYTHON releases the Python interpreter lock and re-acquires the lock + for Tcl. It is okay for ENTER_TCL/LEAVE_TCL pairs to be contained inside + the code between ENTER_PYTHON and LEAVE_PYTHON. + + These locks expand to several statements and brackets; they should not be + used in branches of if statements and the like. + + If Tcl is threaded, this approach won't work anymore. The Tcl interpreter is + only valid in the thread that created it, and all Tk activity must happen in this + thread, also. That means that the mainloop must be invoked in the thread that + created the interpreter. Invoking commands from other threads is possible; + _tkinter will queue an event for the interpreter thread, which will then + execute the command and pass back the result. If the main thread is not in the + mainloop, and invoking commands causes an exception; if the main loop is running + but not processing events, the command invocation will block. + + In addition, for a threaded Tcl, a single global tcl_tstate won't be sufficient + anymore, since multiple Tcl interpreters may simultaneously dispatch in different + threads. So we use the Tcl TLS API. + +*/ + +static PyThread_type_lock tcl_lock = 0; + +#ifdef TCL_THREADS +static Tcl_ThreadDataKey state_key; +typedef PyThreadState *ThreadSpecificData; +#define tcl_tstate (*(PyThreadState**)Tcl_GetThreadData(&state_key, sizeof(PyThreadState*))) +#else +static PyThreadState *tcl_tstate = NULL; +#endif + +#define ENTER_TCL \ + { PyThreadState *tstate = PyThreadState_Get(); Py_BEGIN_ALLOW_THREADS \ + if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); tcl_tstate = tstate; + +#define LEAVE_TCL \ + tcl_tstate = NULL; if(tcl_lock)PyThread_release_lock(tcl_lock); Py_END_ALLOW_THREADS} + +#define ENTER_OVERLAP \ + Py_END_ALLOW_THREADS + +#define LEAVE_OVERLAP_TCL \ + tcl_tstate = NULL; if(tcl_lock)PyThread_release_lock(tcl_lock); } + +#define ENTER_PYTHON \ + { PyThreadState *tstate = tcl_tstate; tcl_tstate = NULL; \ + if(tcl_lock)PyThread_release_lock(tcl_lock); PyEval_RestoreThread((tstate)); } + +#define LEAVE_PYTHON \ + { PyThreadState *tstate = PyEval_SaveThread(); \ + if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); tcl_tstate = tstate; } + +#define CHECK_TCL_APPARTMENT \ + if (((TkappObject *)self)->threaded && \ + ((TkappObject *)self)->thread_id != Tcl_GetCurrentThread()) { \ + PyErr_SetString(PyExc_RuntimeError, "Calling Tcl from different appartment"); \ + return 0; \ + } + +#else + +#define ENTER_TCL +#define LEAVE_TCL +#define ENTER_OVERLAP +#define LEAVE_OVERLAP_TCL +#define ENTER_PYTHON +#define LEAVE_PYTHON +#define CHECK_TCL_APPARTMENT + +#endif + +#ifdef macintosh + +/* +** Additional cruft needed by Tcl/Tk on the Mac. +** This is for Tcl 7.5 and Tk 4.1 (patch release 1). +*/ + +/* ckfree() expects a char* */ +#define FREECAST (char *) + +#include /* For EventRecord */ + +typedef int (*TclMacConvertEventPtr) (EventRecord *eventPtr); +void Tcl_MacSetEventProc(TclMacConvertEventPtr procPtr); +int TkMacConvertEvent(EventRecord *eventPtr); + +static int PyMacConvertEvent(EventRecord *eventPtr); + +#include +extern int SIOUXIsAppWindow(WindowPtr); + +#endif /* macintosh */ + +#ifndef FREECAST +#define FREECAST (char *) +#endif + +/**** Tkapp Object Declaration ****/ + +static PyTypeObject Tkapp_Type; + +typedef struct { + PyObject_HEAD + Tcl_Interp *interp; + int wantobjects; + int threaded; /* True if tcl_platform[threaded] */ + Tcl_ThreadId thread_id; + int dispatching; + /* We cannot include tclInt.h, as this is internal. + So we cache interesting types here. */ + Tcl_ObjType *BooleanType; + Tcl_ObjType *ByteArrayType; + Tcl_ObjType *DoubleType; + Tcl_ObjType *IntType; + Tcl_ObjType *ListType; + Tcl_ObjType *ProcBodyType; + Tcl_ObjType *StringType; +} TkappObject; + +#define Tkapp_Check(v) ((v)->ob_type == &Tkapp_Type) +#define Tkapp_Interp(v) (((TkappObject *) (v))->interp) +#define Tkapp_Result(v) Tcl_GetStringResult(Tkapp_Interp(v)) + +#define DEBUG_REFCNT(v) (printf("DEBUG: id=%p, refcnt=%i\n", \ +(void *) v, ((PyObject *) v)->ob_refcnt)) + + + +/**** Error Handling ****/ + +static PyObject *Tkinter_TclError; +static int quitMainLoop = 0; +static int errorInCmd = 0; +static PyObject *excInCmd; +static PyObject *valInCmd; +static PyObject *trbInCmd; + + + +static PyObject * +Tkinter_Error(PyObject *v) +{ + PyErr_SetString(Tkinter_TclError, Tkapp_Result(v)); + return NULL; +} + + + +/**** Utils ****/ + +static int Tkinter_busywaitinterval = 20; + +#ifdef WITH_THREAD +#ifndef MS_WINDOWS + +/* Millisecond sleep() for Unix platforms. */ + +static void +Sleep(int milli) +{ + /* XXX Too bad if you don't have select(). */ + struct timeval t; + t.tv_sec = milli/1000; + t.tv_usec = (milli%1000) * 1000; + select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t); +} +#endif /* MS_WINDOWS */ + +/* Wait up to 1s for the mainloop to come up. */ + +static int +WaitForMainloop(TkappObject* self) +{ + int i; + for (i = 0; i < 10; i++) { + if (self->dispatching) + return 1; + Py_BEGIN_ALLOW_THREADS + Sleep(100); + Py_END_ALLOW_THREADS + } + if (self->dispatching) + return 1; + PyErr_SetString(PyExc_RuntimeError, "main thread is not in main loop"); + return 0; +} +#endif /* WITH_THREAD */ + + +static char * +AsString(PyObject *value, PyObject *tmp) +{ + if (PyString_Check(value)) + return PyString_AsString(value); +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(value)) { + PyObject *v = PyUnicode_AsUTF8String(value); + if (v == NULL) + return NULL; + if (PyList_Append(tmp, v) != 0) { + Py_DECREF(v); + return NULL; + } + Py_DECREF(v); + return PyString_AsString(v); + } +#endif + else { + PyObject *v = PyObject_Str(value); + if (v == NULL) + return NULL; + if (PyList_Append(tmp, v) != 0) { + Py_DECREF(v); + return NULL; + } + Py_DECREF(v); + return PyString_AsString(v); + } +} + + + +#define ARGSZ 64 + +static char * +Merge(PyObject *args) +{ + PyObject *tmp = NULL; + char *argvStore[ARGSZ]; + char **argv = NULL; + int fvStore[ARGSZ]; + int *fv = NULL; + int argc = 0, fvc = 0, i; + char *res = NULL; + + if (!(tmp = PyList_New(0))) + return NULL; + + argv = argvStore; + fv = fvStore; + + if (args == NULL) + argc = 0; + + else if (!PyTuple_Check(args)) { + argc = 1; + fv[0] = 0; + if (!(argv[0] = AsString(args, tmp))) + goto finally; + } + else { + argc = PyTuple_Size(args); + + if (argc > ARGSZ) { + argv = (char **)ckalloc(argc * sizeof(char *)); + fv = (int *)ckalloc(argc * sizeof(int)); + if (argv == NULL || fv == NULL) { + PyErr_NoMemory(); + goto finally; + } + } + + for (i = 0; i < argc; i++) { + PyObject *v = PyTuple_GetItem(args, i); + if (PyTuple_Check(v)) { + fv[i] = 1; + if (!(argv[i] = Merge(v))) + goto finally; + fvc++; + } + else if (v == Py_None) { + argc = i; + break; + } + else { + fv[i] = 0; + if (!(argv[i] = AsString(v, tmp))) + goto finally; + fvc++; + } + } + } + res = Tcl_Merge(argc, argv); + if (res == NULL) + PyErr_SetString(Tkinter_TclError, "merge failed"); + + finally: + for (i = 0; i < fvc; i++) + if (fv[i]) { + ckfree(argv[i]); + } + if (argv != argvStore) + ckfree(FREECAST argv); + if (fv != fvStore) + ckfree(FREECAST fv); + + Py_DECREF(tmp); + return res; +} + + + +static PyObject * +Split(char *list) +{ + int argc; + char **argv; + PyObject *v; + + if (list == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + + if (Tcl_SplitList((Tcl_Interp *)NULL, list, &argc, &argv) != TCL_OK) { + /* Not a list. + * Could be a quoted string containing funnies, e.g. {"}. + * Return the string itself. + */ + return PyString_FromString(list); + } + + if (argc == 0) + v = PyString_FromString(""); + else if (argc == 1) + v = PyString_FromString(argv[0]); + else if ((v = PyTuple_New(argc)) != NULL) { + int i; + PyObject *w; + + for (i = 0; i < argc; i++) { + if ((w = Split(argv[i])) == NULL) { + Py_DECREF(v); + v = NULL; + break; + } + PyTuple_SetItem(v, i, w); + } + } + Tcl_Free(FREECAST argv); + return v; +} + +/* In some cases, Tcl will still return strings that are supposed to be + lists. SplitObj walks through a nested tuple, finding string objects that + need to be split. */ + +PyObject * +SplitObj(PyObject *arg) +{ + if (PyTuple_Check(arg)) { + int i, size; + PyObject *elem, *newelem, *result; + + size = PyTuple_Size(arg); + result = NULL; + /* Recursively invoke SplitObj for all tuple items. + If this does not return a new object, no action is + needed. */ + for(i = 0; i < size; i++) { + elem = PyTuple_GetItem(arg, i); + newelem = SplitObj(elem); + if (!newelem) { + Py_XDECREF(result); + return NULL; + } + if (!result) { + int k; + if (newelem == elem) { + Py_DECREF(newelem); + continue; + } + result = PyTuple_New(size); + if (!result) + return NULL; + for(k = 0; k < i; k++) { + elem = PyTuple_GetItem(arg, k); + Py_INCREF(elem); + PyTuple_SetItem(result, k, elem); + } + } + PyTuple_SetItem(result, i, newelem); + } + if (result) + return result; + /* Fall through, returning arg. */ + } + else if (PyString_Check(arg)) { + int argc; + char **argv; + char *list = PyString_AsString(arg); + + if (Tcl_SplitList((Tcl_Interp *)NULL, list, &argc, &argv) != TCL_OK) { + Py_INCREF(arg); + return arg; + } + Tcl_Free(FREECAST argv); + if (argc > 1) + return Split(PyString_AsString(arg)); + /* Fall through, returning arg. */ + } + Py_INCREF(arg); + return arg; +} + + +/**** Tkapp Object ****/ + +#ifndef WITH_APPINIT +int +Tcl_AppInit(Tcl_Interp *interp) +{ + Tk_Window main; + + main = Tk_MainWindow(interp); + if (Tcl_Init(interp) == TCL_ERROR) { + PySys_WriteStderr("Tcl_Init error: %s\n", Tcl_GetStringResult(interp)); + return TCL_ERROR; + } + if (Tk_Init(interp) == TCL_ERROR) { + PySys_WriteStderr("Tk_Init error: %s\n", Tcl_GetStringResult(interp)); + return TCL_ERROR; + } + return TCL_OK; +} +#endif /* !WITH_APPINIT */ + + + + +/* Initialize the Tk application; see the `main' function in + * `tkMain.c'. + */ + +static void EnableEventHook(void); /* Forward */ +static void DisableEventHook(void); /* Forward */ + +static TkappObject * +Tkapp_New(char *screenName, char *baseName, char *className, + int interactive, int wantobjects) +{ + TkappObject *v; + char *argv0; + + v = PyObject_New(TkappObject, &Tkapp_Type); + if (v == NULL) + return NULL; + + v->interp = Tcl_CreateInterp(); + v->wantobjects = wantobjects; + v->threaded = Tcl_GetVar2Ex(v->interp, "tcl_platform", "threaded", + TCL_GLOBAL_ONLY) != NULL; + v->thread_id = Tcl_GetCurrentThread(); + v->dispatching = 0; + +#ifndef TCL_THREADS + if (v->threaded) { + PyErr_SetString(PyExc_RuntimeError, "Tcl is threaded but _tkinter is not"); + Py_DECREF(v); + return 0; + } +#endif +#ifdef WITH_THREAD + if (v->threaded && tcl_lock) { + /* If Tcl is threaded, we don't need the lock. */ + PyThread_free_lock(tcl_lock); + tcl_lock = NULL; + } +#endif + + v->BooleanType = Tcl_GetObjType("boolean"); + v->ByteArrayType = Tcl_GetObjType("bytearray"); + v->DoubleType = Tcl_GetObjType("double"); + v->IntType = Tcl_GetObjType("int"); + v->ListType = Tcl_GetObjType("list"); + v->ProcBodyType = Tcl_GetObjType("procbody"); + v->StringType = Tcl_GetObjType("string"); + +#if defined(macintosh) + /* This seems to be needed */ + ClearMenuBar(); + TkMacInitMenus(v->interp); +#endif + + /* Delete the 'exit' command, which can screw things up */ + Tcl_DeleteCommand(v->interp, "exit"); + + if (screenName != NULL) + Tcl_SetVar2(v->interp, "env", "DISPLAY", + screenName, TCL_GLOBAL_ONLY); + + if (interactive) + Tcl_SetVar(v->interp, "tcl_interactive", "1", TCL_GLOBAL_ONLY); + else + Tcl_SetVar(v->interp, "tcl_interactive", "0", TCL_GLOBAL_ONLY); + + /* This is used to get the application class for Tk 4.1 and up */ + argv0 = (char*)ckalloc(strlen(className) + 1); + if (!argv0) { + PyErr_NoMemory(); + Py_DECREF(v); + return NULL; + } + + strcpy(argv0, className); + if (isupper((int)(argv0[0]))) + argv0[0] = tolower(argv0[0]); + Tcl_SetVar(v->interp, "argv0", argv0, TCL_GLOBAL_ONLY); + ckfree(argv0); + + if (Tcl_AppInit(v->interp) != TCL_OK) + return (TkappObject *)Tkinter_Error((PyObject *)v); + + EnableEventHook(); + + return v; +} + + +static void +Tkapp_ThreadSend(TkappObject *self, Tcl_Event *ev, + Tcl_Condition *cond, Tcl_Mutex *mutex) +{ + Py_BEGIN_ALLOW_THREADS; + Tcl_MutexLock(mutex); + Tcl_ThreadQueueEvent(self->thread_id, ev, TCL_QUEUE_TAIL); + Tcl_ThreadAlert(self->thread_id); + Tcl_ConditionWait(cond, mutex, NULL); + Tcl_MutexUnlock(mutex); + Py_END_ALLOW_THREADS +} + + +/** Tcl Eval **/ + +typedef struct { + PyObject_HEAD + Tcl_Obj *value; + PyObject *string; /* This cannot cause cycles. */ +} PyTclObject; + +staticforward PyTypeObject PyTclObject_Type; +#define PyTclObject_Check(v) ((v)->ob_type == &PyTclObject_Type) + +static PyObject * +newPyTclObject(Tcl_Obj *arg) +{ + PyTclObject *self; + self = PyObject_New(PyTclObject, &PyTclObject_Type); + if (self == NULL) + return NULL; + Tcl_IncrRefCount(arg); + self->value = arg; + self->string = NULL; + return (PyObject*)self; +} + +static void +PyTclObject_dealloc(PyTclObject *self) +{ + Tcl_DecrRefCount(self->value); + Py_XDECREF(self->string); + PyObject_Del(self); +} + +static PyObject * +PyTclObject_str(PyTclObject *self) +{ + if (self->string && PyString_Check(self->string)) { + Py_INCREF(self->string); + return self->string; + } + /* XXX Could cache value if it is an ASCII string. */ + return PyString_FromString(Tcl_GetString(self->value)); +} + +static char* +PyTclObject_TclString(PyObject *self) +{ + return Tcl_GetString(((PyTclObject*)self)->value); +} + +/* Like _str, but create Unicode if necessary. */ +PyDoc_STRVAR(PyTclObject_string__doc__, +"the string representation of this object, either as string or Unicode"); + +static PyObject * +PyTclObject_string(PyTclObject *self, void *ignored) +{ + char *s; + int i, len; + if (!self->string) { + s = Tcl_GetStringFromObj(self->value, &len); + for (i = 0; i < len; i++) + if (s[i] & 0x80) + break; +#ifdef Py_USING_UNICODE + if (i == len) + /* It is an ASCII string. */ + self->string = PyString_FromStringAndSize(s, len); + else { + self->string = PyUnicode_DecodeUTF8(s, len, "strict"); + if (!self->string) { + PyErr_Clear(); + self->string = PyString_FromStringAndSize(s, len); + } + } +#else + self->string = PyString_FromStringAndSize(s, len); +#endif + if (!self->string) + return NULL; + } + Py_INCREF(self->string); + return self->string; +} + +#ifdef Py_USING_UNICODE +PyDoc_STRVAR(PyTclObject_unicode__doc__, "convert argument to unicode"); + +static PyObject * +PyTclObject_unicode(PyTclObject *self, void *ignored) +{ + char *s; + int len; + if (self->string && PyUnicode_Check(self->string)) { + Py_INCREF(self->string); + return self->string; + } + /* XXX Could chache result if it is non-ASCII. */ + s = Tcl_GetStringFromObj(self->value, &len); + return PyUnicode_DecodeUTF8(s, len, "strict"); +} +#endif + +static PyObject * +PyTclObject_repr(PyTclObject *self) +{ + char buf[50]; + PyOS_snprintf(buf, 50, "<%s object at 0x%.8x>", + self->value->typePtr->name, (int)self->value); + return PyString_FromString(buf); +} + +static int +PyTclObject_cmp(PyTclObject *self, PyTclObject *other) +{ + int res; + res = strcmp(Tcl_GetString(self->value), + Tcl_GetString(other->value)); + if (res < 0) return -1; + if (res > 0) return 1; + return 0; +} + +PyDoc_STRVAR(get_typename__doc__, "name of the Tcl type"); + +static PyObject* +get_typename(PyTclObject* obj, void* ignored) +{ + return PyString_FromString(obj->value->typePtr->name); +} + + +static PyGetSetDef PyTclObject_getsetlist[] = { + {"typename", (getter)get_typename, NULL, get_typename__doc__}, + {"string", (getter)PyTclObject_string, NULL, + PyTclObject_string__doc__}, + {0}, +}; + +static PyMethodDef PyTclObject_methods[] = { + {"__unicode__", (PyCFunction)PyTclObject_unicode, METH_NOARGS, + PyTclObject_unicode__doc__}, + {0} +}; + +statichere PyTypeObject PyTclObject_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_tkinter.Tcl_Obj", /*tp_name*/ + sizeof(PyTclObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyTclObject_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + (cmpfunc)PyTclObject_cmp, /*tp_compare*/ + (reprfunc)PyTclObject_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + (reprfunc)PyTclObject_str, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + PyTclObject_methods, /*tp_methods*/ + 0, /*tp_members*/ + PyTclObject_getsetlist, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +static Tcl_Obj* +AsObj(PyObject *value) +{ + Tcl_Obj *result; + + if (PyString_Check(value)) + return Tcl_NewStringObj(PyString_AS_STRING(value), + PyString_GET_SIZE(value)); + else if (PyBool_Check(value)) + return Tcl_NewBooleanObj(PyObject_IsTrue(value)); + else if (PyInt_Check(value)) + return Tcl_NewLongObj(PyInt_AS_LONG(value)); + else if (PyFloat_Check(value)) + return Tcl_NewDoubleObj(PyFloat_AS_DOUBLE(value)); + else if (PyTuple_Check(value)) { + Tcl_Obj **argv = (Tcl_Obj**) + ckalloc(PyTuple_Size(value)*sizeof(Tcl_Obj*)); + int i; + if(!argv) + return 0; + for(i=0;i= 0x10000) { + /* Tcl doesn't do UTF-16, yet. */ + PyErr_SetString(PyExc_ValueError, + "unsupported character"); + ckfree(FREECAST outbuf); + return NULL; + } + outbuf[i] = inbuf[i]; + } + result = Tcl_NewUnicodeObj(outbuf, size); + ckfree(FREECAST outbuf); + return result; +#else + return Tcl_NewUnicodeObj(inbuf, size); +#endif + + } +#endif + else if(PyTclObject_Check(value)) { + Tcl_Obj *v = ((PyTclObject*)value)->value; + Tcl_IncrRefCount(v); + return v; + } + else { + PyObject *v = PyObject_Str(value); + if (!v) + return 0; + result = AsObj(v); + Py_DECREF(v); + return result; + } +} + +static PyObject* +FromObj(PyObject* tkapp, Tcl_Obj *value) +{ + PyObject *result = NULL; + TkappObject *app = (TkappObject*)tkapp; + + if (value->typePtr == NULL) { + /* If the result contains any bytes with the top bit set, + it's UTF-8 and we should decode it to Unicode */ +#ifdef Py_USING_UNICODE + int i; + char *s = value->bytes; + int len = value->length; + for (i = 0; i < len; i++) { + if (value->bytes[i] & 0x80) + break; + } + + if (i == value->length) + result = PyString_FromStringAndSize(s, len); + else { + /* Convert UTF-8 to Unicode string */ + result = PyUnicode_DecodeUTF8(s, len, "strict"); + if (result == NULL) { + PyErr_Clear(); + result = PyString_FromStringAndSize(s, len); + } + } +#else + res = PyString_FromStringAndSize(value->bytes, value->length); +#endif + return result; + } + + if (value->typePtr == app->BooleanType) { + result = value->internalRep.longValue ? Py_True : Py_False; + Py_INCREF(result); + return result; + } + + if (value->typePtr == app->ByteArrayType) { + int size; + char *data = (char*)Tcl_GetByteArrayFromObj(value, &size); + return PyString_FromStringAndSize(data, size); + } + + if (value->typePtr == app->DoubleType) { + return PyFloat_FromDouble(value->internalRep.doubleValue); + } + + if (value->typePtr == app->IntType) { + return PyInt_FromLong(value->internalRep.longValue); + } + + if (value->typePtr == app->ListType) { + int size; + int i, status; + PyObject *elem; + Tcl_Obj *tcl_elem; + + status = Tcl_ListObjLength(Tkapp_Interp(tkapp), value, &size); + if (status == TCL_ERROR) + return Tkinter_Error(tkapp); + result = PyTuple_New(size); + if (!result) + return NULL; + for (i = 0; i < size; i++) { + status = Tcl_ListObjIndex(Tkapp_Interp(tkapp), + value, i, &tcl_elem); + if (status == TCL_ERROR) { + Py_DECREF(result); + return Tkinter_Error(tkapp); + } + elem = FromObj(tkapp, tcl_elem); + if (!elem) { + Py_DECREF(result); + return NULL; + } + PyTuple_SetItem(result, i, elem); + } + return result; + } + + if (value->typePtr == app->ProcBodyType) { + /* fall through: return tcl object. */ + } + + if (value->typePtr == app->StringType) { +#ifdef Py_USING_UNICODE +#if defined(Py_UNICODE_WIDE) && TCL_UTF_MAX==3 + PyObject *result; + int size; + Tcl_UniChar *input; + Py_UNICODE *output; + + size = Tcl_GetCharLength(value); + result = PyUnicode_FromUnicode(NULL, size); + if (!result) + return NULL; + input = Tcl_GetUnicode(value); + output = PyUnicode_AS_UNICODE(result); + while (size--) + *output++ = *input++; + return result; +#else + return PyUnicode_FromUnicode(Tcl_GetUnicode(value), + Tcl_GetCharLength(value)); +#endif +#else + int size; + char *c; + c = Tcl_GetStringFromObj(value, &size); + return PyString_FromStringAndSize(c, size); +#endif + } + + return newPyTclObject(value); +} + +/* This mutex synchronizes inter-thread command calls. */ + +TCL_DECLARE_MUTEX(call_mutex) + +typedef struct Tkapp_CallEvent { + Tcl_Event ev; /* Must be first */ + TkappObject *self; + PyObject *args; + int flags; + PyObject **res; + PyObject **exc_type, **exc_value, **exc_tb; + Tcl_Condition done; +} Tkapp_CallEvent; + +void +Tkapp_CallDeallocArgs(Tcl_Obj** objv, Tcl_Obj** objStore, int objc) +{ + int i; + for (i = 0; i < objc; i++) + Tcl_DecrRefCount(objv[i]); + if (objv != objStore) + ckfree(FREECAST objv); +} + +/* Convert Python objects to Tcl objects. This must happen in the + interpreter thread, which may or may not be the calling thread. */ + +static Tcl_Obj** +Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, int *pobjc) +{ + Tcl_Obj **objv = objStore; + int objc = 0, i; + if (args == NULL) + /* do nothing */; + + else if (!PyTuple_Check(args)) { + objv[0] = AsObj(args); + if (objv[0] == 0) + goto finally; + objc = 1; + Tcl_IncrRefCount(objv[0]); + } + else { + objc = PyTuple_Size(args); + + if (objc > ARGSZ) { + objv = (Tcl_Obj **)ckalloc(objc * sizeof(char *)); + if (objv == NULL) { + PyErr_NoMemory(); + objc = 0; + goto finally; + } + } + + for (i = 0; i < objc; i++) { + PyObject *v = PyTuple_GetItem(args, i); + if (v == Py_None) { + objc = i; + break; + } + objv[i] = AsObj(v); + if (!objv[i]) { + /* Reset objc, so it attempts to clear + objects only up to i. */ + objc = i; + goto finally; + } + Tcl_IncrRefCount(objv[i]); + } + } + *pobjc = objc; + return objv; +finally: + Tkapp_CallDeallocArgs(objv, objStore, objc); + return NULL; +} + +/* Convert the results of a command call into a Python objects. */ + +static PyObject* +Tkapp_CallResult(TkappObject *self) +{ + PyObject *res = NULL; + if(self->wantobjects) { + Tcl_Obj *value = Tcl_GetObjResult(self->interp); + /* Not sure whether the IncrRef is necessary, but something + may overwrite the interpreter result while we are + converting it. */ + Tcl_IncrRefCount(value); + res = FromObj((PyObject*)self, value); + Tcl_DecrRefCount(value); + } else { + const char *s = Tcl_GetStringResult(self->interp); + const char *p = s; + + /* If the result contains any bytes with the top bit set, + it's UTF-8 and we should decode it to Unicode */ +#ifdef Py_USING_UNICODE + while (*p != '\0') { + if (*p & 0x80) + break; + p++; + } + + if (*p == '\0') + res = PyString_FromStringAndSize(s, (int)(p-s)); + else { + /* Convert UTF-8 to Unicode string */ + p = strchr(p, '\0'); + res = PyUnicode_DecodeUTF8(s, (int)(p-s), "strict"); + if (res == NULL) { + PyErr_Clear(); + res = PyString_FromStringAndSize(s, (int)(p-s)); + } + } +#else + p = strchr(p, '\0'); + res = PyString_FromStringAndSize(s, (int)(p-s)); +#endif + } + return res; +} + +/* Tkapp_CallProc is the event procedure that is executed in the context of + the Tcl interpreter thread. Initially, it holds the Tcl lock, and doesn't + hold the Python lock. */ + +static int +Tkapp_CallProc(Tkapp_CallEvent *e, int flags) +{ + Tcl_Obj *objStore[ARGSZ]; + Tcl_Obj **objv; + int objc; + int i; + ENTER_PYTHON + objv = Tkapp_CallArgs(e->args, objStore, &objc); + if (!objv) { + PyErr_Fetch(e->exc_type, e->exc_value, e->exc_tb); + *(e->res) = NULL; + } + LEAVE_PYTHON + if (!objv) + goto done; + i = Tcl_EvalObjv(e->self->interp, objc, objv, e->flags); + ENTER_PYTHON + if (i == TCL_ERROR) { + *(e->res) = NULL; + *(e->exc_type) = NULL; + *(e->exc_tb) = NULL; + *(e->exc_value) = PyObject_CallFunction( + Tkinter_TclError, "s", + Tcl_GetStringResult(e->self->interp)); + } + else { + *(e->res) = Tkapp_CallResult(e->self); + } + LEAVE_PYTHON + done: + /* Wake up calling thread. */ + Tcl_MutexLock(&call_mutex); + Tcl_ConditionNotify(&e->done); + Tcl_MutexUnlock(&call_mutex); + return 1; +} + +/* This is the main entry point for calling a Tcl command. + It supports three cases, with regard to threading: + 1. Tcl is not threaded: Must have the Tcl lock, then can invoke command in + the context of the calling thread. + 2. Tcl is threaded, caller of the command is in the interpreter thread: + Execute the command in the calling thread. Since the Tcl lock will + not be used, we can merge that with case 1. + 3. Tcl is threaded, caller is in a different thread: Must queue an event to + the interpreter thread. Allocation of Tcl objects needs to occur in the + interpreter thread, so we ship the PyObject* args to the target thread, + and perform processing there. */ + +static PyObject * +Tkapp_Call(PyObject *_self, PyObject *args) +{ + Tcl_Obj *objStore[ARGSZ]; + Tcl_Obj **objv = NULL; + int objc, i; + PyObject *res = NULL; + TkappObject *self = (TkappObject*)_self; + /* Could add TCL_EVAL_GLOBAL if wrapped by GlobalCall... */ + int flags = TCL_EVAL_DIRECT; + +#ifdef WITH_THREAD + if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) { + /* We cannot call the command directly. Instead, we must + marshal the parameters to the interpreter thread. */ + Tkapp_CallEvent *ev; + PyObject *exc_type, *exc_value, *exc_tb; + if (!WaitForMainloop(self)) + return NULL; + ev = (Tkapp_CallEvent*)ckalloc(sizeof(Tkapp_CallEvent)); + ev->ev.proc = (Tcl_EventProc*)Tkapp_CallProc; + ev->self = self; + ev->args = args; + ev->res = &res; + ev->exc_type = &exc_type; + ev->exc_value = &exc_value; + ev->exc_tb = &exc_tb; + ev->done = (Tcl_Condition)0; + + Tkapp_ThreadSend(self, (Tcl_Event*)ev, &ev->done, &call_mutex); + + if (res == NULL) { + if (exc_type) + PyErr_Restore(exc_type, exc_value, exc_tb); + else + PyErr_SetObject(Tkinter_TclError, exc_value); + } + } + else +#endif + { + + objv = Tkapp_CallArgs(args, objStore, &objc); + if (!objv) + return NULL; + + ENTER_TCL + + i = Tcl_EvalObjv(self->interp, objc, objv, flags); + + ENTER_OVERLAP + + if (i == TCL_ERROR) + Tkinter_Error(_self); + else + res = Tkapp_CallResult(self); + + LEAVE_OVERLAP_TCL + + Tkapp_CallDeallocArgs(objv, objStore, objc); + } + return res; +} + + +static PyObject * +Tkapp_GlobalCall(PyObject *self, PyObject *args) +{ + /* Could do the same here as for Tkapp_Call(), but this is not used + much, so I can't be bothered. Unfortunately Tcl doesn't export a + way for the user to do what all its Global* variants do (save and + reset the scope pointer, call the local version, restore the saved + scope pointer). */ + + char *cmd; + PyObject *res = NULL; + + CHECK_TCL_APPARTMENT; + + cmd = Merge(args); + if (cmd) { + int err; + ENTER_TCL + err = Tcl_GlobalEval(Tkapp_Interp(self), cmd); + ENTER_OVERLAP + if (err == TCL_ERROR) + res = Tkinter_Error(self); + else + res = PyString_FromString(Tkapp_Result(self)); + LEAVE_OVERLAP_TCL + ckfree(cmd); + } + + return res; +} + +static PyObject * +Tkapp_Eval(PyObject *self, PyObject *args) +{ + char *script; + PyObject *res = NULL; + int err; + + if (!PyArg_ParseTuple(args, "s:eval", &script)) + return NULL; + + CHECK_TCL_APPARTMENT; + + ENTER_TCL + err = Tcl_Eval(Tkapp_Interp(self), script); + ENTER_OVERLAP + if (err == TCL_ERROR) + res = Tkinter_Error(self); + else + res = PyString_FromString(Tkapp_Result(self)); + LEAVE_OVERLAP_TCL + return res; +} + +static PyObject * +Tkapp_GlobalEval(PyObject *self, PyObject *args) +{ + char *script; + PyObject *res = NULL; + int err; + + if (!PyArg_ParseTuple(args, "s:globaleval", &script)) + return NULL; + + CHECK_TCL_APPARTMENT; + + ENTER_TCL + err = Tcl_GlobalEval(Tkapp_Interp(self), script); + ENTER_OVERLAP + if (err == TCL_ERROR) + res = Tkinter_Error(self); + else + res = PyString_FromString(Tkapp_Result(self)); + LEAVE_OVERLAP_TCL + return res; +} + +static PyObject * +Tkapp_EvalFile(PyObject *self, PyObject *args) +{ + char *fileName; + PyObject *res = NULL; + int err; + + if (!PyArg_ParseTuple(args, "s:evalfile", &fileName)) + return NULL; + + CHECK_TCL_APPARTMENT; + + ENTER_TCL + err = Tcl_EvalFile(Tkapp_Interp(self), fileName); + ENTER_OVERLAP + if (err == TCL_ERROR) + res = Tkinter_Error(self); + + else + res = PyString_FromString(Tkapp_Result(self)); + LEAVE_OVERLAP_TCL + return res; +} + +static PyObject * +Tkapp_Record(PyObject *self, PyObject *args) +{ + char *script; + PyObject *res = NULL; + int err; + + if (!PyArg_ParseTuple(args, "s", &script)) + return NULL; + + CHECK_TCL_APPARTMENT; + + ENTER_TCL + err = Tcl_RecordAndEval(Tkapp_Interp(self), script, TCL_NO_EVAL); + ENTER_OVERLAP + if (err == TCL_ERROR) + res = Tkinter_Error(self); + else + res = PyString_FromString(Tkapp_Result(self)); + LEAVE_OVERLAP_TCL + return res; +} + +static PyObject * +Tkapp_AddErrorInfo(PyObject *self, PyObject *args) +{ + char *msg; + + if (!PyArg_ParseTuple(args, "s:adderrorinfo", &msg)) + return NULL; + CHECK_TCL_APPARTMENT; + + ENTER_TCL + Tcl_AddErrorInfo(Tkapp_Interp(self), msg); + LEAVE_TCL + + Py_INCREF(Py_None); + return Py_None; +} + + + +/** Tcl Variable **/ + +TCL_DECLARE_MUTEX(var_mutex) + +typedef PyObject* (*EventFunc)(PyObject*, PyObject *args, int flags); +typedef struct VarEvent { + Tcl_Event ev; /* must be first */ + PyObject *self; + PyObject *args; + int flags; + EventFunc func; + PyObject **res; + PyObject **exc_type; + PyObject **exc_val; + Tcl_Condition cond; +} VarEvent; + +static int +varname_converter(PyObject *in, void *_out) +{ + char **out = (char**)_out; + if (PyString_Check(in)) { + *out = PyString_AsString(in); + return 1; + } + if (PyTclObject_Check(in)) { + *out = PyTclObject_TclString(in); + return 1; + } + /* XXX: Should give diagnostics. */ + return 0; +} + +void +var_perform(VarEvent *ev) +{ + *(ev->res) = ev->func(ev->self, ev->args, ev->flags); + if (!*(ev->res)) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + PyErr_NormalizeException(&exc, &val, &tb); + *(ev->exc_type) = exc; + *(ev->exc_val) = val; + Py_DECREF(tb); + } + +} + +static int +var_proc(VarEvent* ev, int flags) +{ + ENTER_PYTHON + var_perform(ev); + Tcl_MutexLock(&var_mutex); + Tcl_ConditionNotify(&ev->cond); + Tcl_MutexUnlock(&var_mutex); + LEAVE_PYTHON + return 1; +} + +static PyObject* +var_invoke(EventFunc func, PyObject *_self, PyObject *args, int flags) +{ + TkappObject *self = (TkappObject*)_self; +#ifdef WITH_THREAD + if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) { + TkappObject *self = (TkappObject*)_self; + VarEvent *ev; + PyObject *res, *exc_type, *exc_val; + + /* The current thread is not the interpreter thread. Marshal + the call to the interpreter thread, then wait for + completion. */ + if (!WaitForMainloop(self)) + return NULL; + + ev = (VarEvent*)ckalloc(sizeof(VarEvent)); + + ev->self = _self; + ev->args = args; + ev->flags = flags; + ev->func = func; + ev->res = &res; + ev->exc_type = &exc_type; + ev->exc_val = &exc_val; + ev->cond = NULL; + ev->ev.proc = (Tcl_EventProc*)var_proc; + Tkapp_ThreadSend(self, (Tcl_Event*)ev, &ev->cond, &var_mutex); + if (!res) { + PyErr_SetObject(exc_type, exc_val); + Py_DECREF(exc_type); + Py_DECREF(exc_val); + return NULL; + } + return res; + } +#endif + /* Tcl is not threaded, or this is the interpreter thread. */ + return func(_self, args, flags); +} + +static PyObject * +SetVar(PyObject *self, PyObject *args, int flags) +{ + char *name1, *name2; + PyObject *newValue; + PyObject *res = NULL; + Tcl_Obj *newval, *ok; + + if (PyArg_ParseTuple(args, "O&O:setvar", + varname_converter, &name1, &newValue)) { + /* XXX Acquire tcl lock??? */ + newval = AsObj(newValue); + if (newval == NULL) + return NULL; + ENTER_TCL + ok = Tcl_SetVar2Ex(Tkapp_Interp(self), name1, NULL, + newval, flags); + ENTER_OVERLAP + if (!ok) + Tkinter_Error(self); + else { + res = Py_None; + Py_INCREF(res); + } + LEAVE_OVERLAP_TCL + } + else { + PyErr_Clear(); + if (PyArg_ParseTuple(args, "ssO:setvar", + &name1, &name2, &newValue)) { + /* XXX must hold tcl lock already??? */ + newval = AsObj(newValue); + ENTER_TCL + ok = Tcl_SetVar2Ex(Tkapp_Interp(self), name1, name2, newval, flags); + ENTER_OVERLAP + if (!ok) + Tkinter_Error(self); + else { + res = Py_None; + Py_INCREF(res); + } + LEAVE_OVERLAP_TCL + } + else { + return NULL; + } + } + return res; +} + +static PyObject * +Tkapp_SetVar(PyObject *self, PyObject *args) +{ + return var_invoke(SetVar, self, args, TCL_LEAVE_ERR_MSG); +} + +static PyObject * +Tkapp_GlobalSetVar(PyObject *self, PyObject *args) +{ + return var_invoke(SetVar, self, args, TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY); +} + + + +static PyObject * +GetVar(PyObject *self, PyObject *args, int flags) +{ + char *name1, *name2=NULL; + PyObject *res = NULL; + Tcl_Obj *tres; + + if (!PyArg_ParseTuple(args, "O&|s:getvar", + varname_converter, &name1, &name2)) + return NULL; + + ENTER_TCL + tres = Tcl_GetVar2Ex(Tkapp_Interp(self), name1, name2, flags); + ENTER_OVERLAP + if (tres == NULL) { + PyErr_SetString(Tkinter_TclError, Tcl_GetStringResult(Tkapp_Interp(self))); + } else { + if (((TkappObject*)self)->wantobjects) { + res = FromObj(self, tres); + } + else { + res = PyString_FromString(Tcl_GetString(tres)); + } + } + LEAVE_OVERLAP_TCL + return res; +} + +static PyObject * +Tkapp_GetVar(PyObject *self, PyObject *args) +{ + return var_invoke(GetVar, self, args, TCL_LEAVE_ERR_MSG); +} + +static PyObject * +Tkapp_GlobalGetVar(PyObject *self, PyObject *args) +{ + return var_invoke(GetVar, self, args, TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY); +} + + + +static PyObject * +UnsetVar(PyObject *self, PyObject *args, int flags) +{ + char *name1, *name2=NULL; + int code; + PyObject *res = NULL; + + if (!PyArg_ParseTuple(args, "s|s:unsetvar", &name1, &name2)) + return NULL; + + ENTER_TCL + code = Tcl_UnsetVar2(Tkapp_Interp(self), name1, name2, flags); + ENTER_OVERLAP + if (code == TCL_ERROR) + res = Tkinter_Error(self); + else { + Py_INCREF(Py_None); + res = Py_None; + } + LEAVE_OVERLAP_TCL + return res; +} + +static PyObject * +Tkapp_UnsetVar(PyObject *self, PyObject *args) +{ + return var_invoke(UnsetVar, self, args, TCL_LEAVE_ERR_MSG); +} + +static PyObject * +Tkapp_GlobalUnsetVar(PyObject *self, PyObject *args) +{ + return var_invoke(UnsetVar, self, args, TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY); +} + + + +/** Tcl to Python **/ + +static PyObject * +Tkapp_GetInt(PyObject *self, PyObject *args) +{ + char *s; + int v; + + if (PyTuple_Size(args) == 1) { + PyObject* o = PyTuple_GetItem(args, 0); + if (PyInt_Check(o)) { + Py_INCREF(o); + return o; + } + } + if (!PyArg_ParseTuple(args, "s:getint", &s)) + return NULL; + if (Tcl_GetInt(Tkapp_Interp(self), s, &v) == TCL_ERROR) + return Tkinter_Error(self); + return Py_BuildValue("i", v); +} + +static PyObject * +Tkapp_GetDouble(PyObject *self, PyObject *args) +{ + char *s; + double v; + + if (PyTuple_Size(args) == 1) { + PyObject *o = PyTuple_GetItem(args, 0); + if (PyFloat_Check(o)) { + Py_INCREF(o); + return o; + } + } + if (!PyArg_ParseTuple(args, "s:getdouble", &s)) + return NULL; + if (Tcl_GetDouble(Tkapp_Interp(self), s, &v) == TCL_ERROR) + return Tkinter_Error(self); + return Py_BuildValue("d", v); +} + +static PyObject * +Tkapp_GetBoolean(PyObject *self, PyObject *args) +{ + char *s; + int v; + + if (PyTuple_Size(args) == 1) { + PyObject *o = PyTuple_GetItem(args, 0); + if (PyInt_Check(o)) { + Py_INCREF(o); + return o; + } + } + if (!PyArg_ParseTuple(args, "s:getboolean", &s)) + return NULL; + if (Tcl_GetBoolean(Tkapp_Interp(self), s, &v) == TCL_ERROR) + return Tkinter_Error(self); + return PyBool_FromLong(v); +} + +static PyObject * +Tkapp_ExprString(PyObject *self, PyObject *args) +{ + char *s; + PyObject *res = NULL; + int retval; + + if (!PyArg_ParseTuple(args, "s:exprstring", &s)) + return NULL; + + CHECK_TCL_APPARTMENT; + + ENTER_TCL + retval = Tcl_ExprString(Tkapp_Interp(self), s); + ENTER_OVERLAP + if (retval == TCL_ERROR) + res = Tkinter_Error(self); + else + res = Py_BuildValue("s", Tkapp_Result(self)); + LEAVE_OVERLAP_TCL + return res; +} + +static PyObject * +Tkapp_ExprLong(PyObject *self, PyObject *args) +{ + char *s; + PyObject *res = NULL; + int retval; + long v; + + if (!PyArg_ParseTuple(args, "s:exprlong", &s)) + return NULL; + + CHECK_TCL_APPARTMENT; + + ENTER_TCL + retval = Tcl_ExprLong(Tkapp_Interp(self), s, &v); + ENTER_OVERLAP + if (retval == TCL_ERROR) + res = Tkinter_Error(self); + else + res = Py_BuildValue("l", v); + LEAVE_OVERLAP_TCL + return res; +} + +static PyObject * +Tkapp_ExprDouble(PyObject *self, PyObject *args) +{ + char *s; + PyObject *res = NULL; + double v; + int retval; + + if (!PyArg_ParseTuple(args, "s:exprdouble", &s)) + return NULL; + CHECK_TCL_APPARTMENT; + PyFPE_START_PROTECT("Tkapp_ExprDouble", return 0) + ENTER_TCL + retval = Tcl_ExprDouble(Tkapp_Interp(self), s, &v); + ENTER_OVERLAP + PyFPE_END_PROTECT(retval) + if (retval == TCL_ERROR) + res = Tkinter_Error(self); + else + res = Py_BuildValue("d", v); + LEAVE_OVERLAP_TCL + return res; +} + +static PyObject * +Tkapp_ExprBoolean(PyObject *self, PyObject *args) +{ + char *s; + PyObject *res = NULL; + int retval; + int v; + + if (!PyArg_ParseTuple(args, "s:exprboolean", &s)) + return NULL; + CHECK_TCL_APPARTMENT; + ENTER_TCL + retval = Tcl_ExprBoolean(Tkapp_Interp(self), s, &v); + ENTER_OVERLAP + if (retval == TCL_ERROR) + res = Tkinter_Error(self); + else + res = Py_BuildValue("i", v); + LEAVE_OVERLAP_TCL + return res; +} + + + +static PyObject * +Tkapp_SplitList(PyObject *self, PyObject *args) +{ + char *list; + int argc; + char **argv; + PyObject *v; + int i; + + if (PyTuple_Size(args) == 1) { + v = PyTuple_GetItem(args, 0); + if (PyTuple_Check(v)) { + Py_INCREF(v); + return v; + } + } + if (!PyArg_ParseTuple(args, "et:splitlist", "utf-8", &list)) + return NULL; + + if (Tcl_SplitList(Tkapp_Interp(self), list, + &argc, &argv) == TCL_ERROR) { + PyMem_Free(list); + return Tkinter_Error(self); + } + + if (!(v = PyTuple_New(argc))) + goto finally; + + for (i = 0; i < argc; i++) { + PyObject *s = PyString_FromString(argv[i]); + if (!s || PyTuple_SetItem(v, i, s)) { + Py_DECREF(v); + v = NULL; + goto finally; + } + } + + finally: + ckfree(FREECAST argv); + PyMem_Free(list); + return v; +} + +static PyObject * +Tkapp_Split(PyObject *self, PyObject *args) +{ + PyObject *v; + char *list; + + if (PyTuple_Size(args) == 1) { + PyObject* o = PyTuple_GetItem(args, 0); + if (PyTuple_Check(o)) { + o = SplitObj(o); + return o; + } + } + if (!PyArg_ParseTuple(args, "et:split", "utf-8", &list)) + return NULL; + v = Split(list); + PyMem_Free(list); + return v; +} + +static PyObject * +Tkapp_Merge(PyObject *self, PyObject *args) +{ + char *s = Merge(args); + PyObject *res = NULL; + + if (s) { + res = PyString_FromString(s); + ckfree(s); + } + + return res; +} + + + +/** Tcl Command **/ + +/* Client data struct */ +typedef struct { + PyObject *self; + PyObject *func; +} PythonCmd_ClientData; + +static int +PythonCmd_Error(Tcl_Interp *interp) +{ + errorInCmd = 1; + PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd); + LEAVE_PYTHON + return TCL_ERROR; +} + +/* This is the Tcl command that acts as a wrapper for Python + * function or method. + */ +static int +PythonCmd(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) +{ + PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData; + PyObject *self, *func, *arg, *res, *tmp; + int i, rv; + char *s; + + ENTER_PYTHON + + /* TBD: no error checking here since we know, via the + * Tkapp_CreateCommand() that the client data is a two-tuple + */ + self = data->self; + func = data->func; + + /* Create argument list (argv1, ..., argvN) */ + if (!(arg = PyTuple_New(argc - 1))) + return PythonCmd_Error(interp); + + for (i = 0; i < (argc - 1); i++) { + PyObject *s = PyString_FromString(argv[i + 1]); + if (!s || PyTuple_SetItem(arg, i, s)) { + Py_DECREF(arg); + return PythonCmd_Error(interp); + } + } + res = PyEval_CallObject(func, arg); + Py_DECREF(arg); + + if (res == NULL) + return PythonCmd_Error(interp); + + if (!(tmp = PyList_New(0))) { + Py_DECREF(res); + return PythonCmd_Error(interp); + } + + s = AsString(res, tmp); + if (s == NULL) { + rv = PythonCmd_Error(interp); + } + else { + Tcl_SetResult(Tkapp_Interp(self), s, TCL_VOLATILE); + rv = TCL_OK; + } + + Py_DECREF(res); + Py_DECREF(tmp); + + LEAVE_PYTHON + + return rv; +} + +static void +PythonCmdDelete(ClientData clientData) +{ + PythonCmd_ClientData *data = (PythonCmd_ClientData *)clientData; + + ENTER_PYTHON + Py_XDECREF(data->self); + Py_XDECREF(data->func); + PyMem_DEL(data); + LEAVE_PYTHON +} + + + + +TCL_DECLARE_MUTEX(command_mutex) + +typedef struct CommandEvent{ + Tcl_Event ev; + Tcl_Interp* interp; + char *name; + int create; + int *status; + ClientData *data; + Tcl_Condition done; +} CommandEvent; + +static int +Tkapp_CommandProc(CommandEvent *ev, int flags) +{ + if (ev->create) + *ev->status = Tcl_CreateCommand( + ev->interp, ev->name, PythonCmd, + ev->data, PythonCmdDelete) == NULL; + else + *ev->status = Tcl_DeleteCommand(ev->interp, ev->name); + Tcl_MutexLock(&command_mutex); + Tcl_ConditionNotify(&ev->done); + Tcl_MutexUnlock(&command_mutex); + return 1; +} + +static PyObject * +Tkapp_CreateCommand(PyObject *_self, PyObject *args) +{ + TkappObject *self = (TkappObject*)_self; + PythonCmd_ClientData *data; + char *cmdName; + PyObject *func; + int err; + + if (!PyArg_ParseTuple(args, "sO:createcommand", &cmdName, &func)) + return NULL; + if (!PyCallable_Check(func)) { + PyErr_SetString(PyExc_TypeError, "command not callable"); + return NULL; + } + +#ifdef WITH_THREAD + if (self->threaded && self->thread_id != Tcl_GetCurrentThread() && + !WaitForMainloop(self)) + return NULL; +#endif + + data = PyMem_NEW(PythonCmd_ClientData, 1); + if (!data) + return PyErr_NoMemory(); + Py_XINCREF(self); + Py_XINCREF(func); + data->self = _self; + data->func = func; + + if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) { + CommandEvent *ev = (CommandEvent*)ckalloc(sizeof(CommandEvent)); + ev->ev.proc = (Tcl_EventProc*)Tkapp_CommandProc; + ev->interp = self->interp; + ev->create = 1; + ev->name = cmdName; + ev->data = (ClientData)data; + ev->status = &err; + ev->done = NULL; + Tkapp_ThreadSend(self, (Tcl_Event*)ev, &ev->done, &command_mutex); + } + else { + ENTER_TCL + err = Tcl_CreateCommand( + Tkapp_Interp(self), cmdName, PythonCmd, + (ClientData)data, PythonCmdDelete) == NULL; + LEAVE_TCL + } + if (err) { + PyErr_SetString(Tkinter_TclError, "can't create Tcl command"); + PyMem_DEL(data); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + + + +static PyObject * +Tkapp_DeleteCommand(PyObject *_self, PyObject *args) +{ + TkappObject *self = (TkappObject*)_self; + char *cmdName; + int err; + + if (!PyArg_ParseTuple(args, "s:deletecommand", &cmdName)) + return NULL; + if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) { + CommandEvent *ev; + ev = (CommandEvent*)ckalloc(sizeof(CommandEvent)); + ev->ev.proc = (Tcl_EventProc*)Tkapp_CommandProc; + ev->interp = self->interp; + ev->create = 0; + ev->name = cmdName; + ev->status = &err; + ev->done = NULL; + Tkapp_ThreadSend(self, (Tcl_Event*)ev, &ev->done, + &command_mutex); + } + else { + ENTER_TCL + err = Tcl_DeleteCommand(self->interp, cmdName); + LEAVE_TCL + } + if (err == -1) { + PyErr_SetString(Tkinter_TclError, "can't delete Tcl command"); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + + + +#ifdef HAVE_CREATEFILEHANDLER +/** File Handler **/ + +typedef struct _fhcdata { + PyObject *func; + PyObject *file; + int id; + struct _fhcdata *next; +} FileHandler_ClientData; + +static FileHandler_ClientData *HeadFHCD; + +static FileHandler_ClientData * +NewFHCD(PyObject *func, PyObject *file, int id) +{ + FileHandler_ClientData *p; + p = PyMem_NEW(FileHandler_ClientData, 1); + if (p != NULL) { + Py_XINCREF(func); + Py_XINCREF(file); + p->func = func; + p->file = file; + p->id = id; + p->next = HeadFHCD; + HeadFHCD = p; + } + return p; +} + +static void +DeleteFHCD(int id) +{ + FileHandler_ClientData *p, **pp; + + pp = &HeadFHCD; + while ((p = *pp) != NULL) { + if (p->id == id) { + *pp = p->next; + Py_XDECREF(p->func); + Py_XDECREF(p->file); + PyMem_DEL(p); + } + else + pp = &p->next; + } +} + +static void +FileHandler(ClientData clientData, int mask) +{ + FileHandler_ClientData *data = (FileHandler_ClientData *)clientData; + PyObject *func, *file, *arg, *res; + + ENTER_PYTHON + func = data->func; + file = data->file; + + arg = Py_BuildValue("(Oi)", file, (long) mask); + res = PyEval_CallObject(func, arg); + Py_DECREF(arg); + + if (res == NULL) { + errorInCmd = 1; + PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd); + } + Py_XDECREF(res); + LEAVE_PYTHON +} + +static PyObject * +Tkapp_CreateFileHandler(PyObject *self, PyObject *args) + /* args is (file, mask, func) */ +{ + FileHandler_ClientData *data; + PyObject *file, *func; + int mask, tfile; + + if (!PyArg_ParseTuple(args, "OiO:createfilehandler", + &file, &mask, &func)) + return NULL; + +#ifdef WITH_THREAD + if (!self && !tcl_lock) { + /* We don't have the Tcl lock since Tcl is threaded. */ + PyErr_SetString(PyExc_RuntimeError, + "_tkinter.createfilehandler not supported " + "for threaded Tcl"); + return NULL; + } +#endif + + if (self) { + CHECK_TCL_APPARTMENT; + } + + tfile = PyObject_AsFileDescriptor(file); + if (tfile < 0) + return NULL; + if (!PyCallable_Check(func)) { + PyErr_SetString(PyExc_TypeError, "bad argument list"); + return NULL; + } + + data = NewFHCD(func, file, tfile); + if (data == NULL) + return NULL; + + /* Ought to check for null Tcl_File object... */ + ENTER_TCL + Tcl_CreateFileHandler(tfile, mask, FileHandler, (ClientData) data); + LEAVE_TCL + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +Tkapp_DeleteFileHandler(PyObject *self, PyObject *args) +{ + PyObject *file; + int tfile; + + if (!PyArg_ParseTuple(args, "O:deletefilehandler", &file)) + return NULL; + +#ifdef WITH_THREAD + if (!self && !tcl_lock) { + /* We don't have the Tcl lock since Tcl is threaded. */ + PyErr_SetString(PyExc_RuntimeError, + "_tkinter.deletefilehandler not supported " + "for threaded Tcl"); + return NULL; + } +#endif + + if (self) { + CHECK_TCL_APPARTMENT; + } + + tfile = PyObject_AsFileDescriptor(file); + if (tfile < 0) + return NULL; + + DeleteFHCD(tfile); + + /* Ought to check for null Tcl_File object... */ + ENTER_TCL + Tcl_DeleteFileHandler(tfile); + LEAVE_TCL + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_CREATEFILEHANDLER */ + + +/**** Tktt Object (timer token) ****/ + +static PyTypeObject Tktt_Type; + +typedef struct { + PyObject_HEAD + Tcl_TimerToken token; + PyObject *func; +} TkttObject; + +static PyObject * +Tktt_DeleteTimerHandler(PyObject *self, PyObject *args) +{ + TkttObject *v = (TkttObject *)self; + PyObject *func = v->func; + + if (!PyArg_ParseTuple(args, ":deletetimerhandler")) + return NULL; + if (v->token != NULL) { + Tcl_DeleteTimerHandler(v->token); + v->token = NULL; + } + if (func != NULL) { + v->func = NULL; + Py_DECREF(func); + Py_DECREF(v); /* See Tktt_New() */ + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef Tktt_methods[] = +{ + {"deletetimerhandler", Tktt_DeleteTimerHandler, METH_VARARGS}, + {NULL, NULL} +}; + +static TkttObject * +Tktt_New(PyObject *func) +{ + TkttObject *v; + + v = PyObject_New(TkttObject, &Tktt_Type); + if (v == NULL) + return NULL; + + Py_INCREF(func); + v->token = NULL; + v->func = func; + + /* Extra reference, deleted when called or when handler is deleted */ + Py_INCREF(v); + return v; +} + +static void +Tktt_Dealloc(PyObject *self) +{ + TkttObject *v = (TkttObject *)self; + PyObject *func = v->func; + + Py_XDECREF(func); + + PyObject_Del(self); +} + +static PyObject * +Tktt_Repr(PyObject *self) +{ + TkttObject *v = (TkttObject *)self; + char buf[100]; + + PyOS_snprintf(buf, sizeof(buf), "", v, + v->func == NULL ? ", handler deleted" : ""); + return PyString_FromString(buf); +} + +static PyObject * +Tktt_GetAttr(PyObject *self, char *name) +{ + return Py_FindMethod(Tktt_methods, self, name); +} + +static PyTypeObject Tktt_Type = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size */ + "tktimertoken", /*tp_name */ + sizeof(TkttObject), /*tp_basicsize */ + 0, /*tp_itemsize */ + Tktt_Dealloc, /*tp_dealloc */ + 0, /*tp_print */ + Tktt_GetAttr, /*tp_getattr */ + 0, /*tp_setattr */ + 0, /*tp_compare */ + Tktt_Repr, /*tp_repr */ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash */ +}; + + + +/** Timer Handler **/ + +static void +TimerHandler(ClientData clientData) +{ + TkttObject *v = (TkttObject *)clientData; + PyObject *func = v->func; + PyObject *res; + + if (func == NULL) + return; + + v->func = NULL; + + ENTER_PYTHON + + res = PyEval_CallObject(func, NULL); + Py_DECREF(func); + Py_DECREF(v); /* See Tktt_New() */ + + if (res == NULL) { + errorInCmd = 1; + PyErr_Fetch(&excInCmd, &valInCmd, &trbInCmd); + } + else + Py_DECREF(res); + + LEAVE_PYTHON +} + +static PyObject * +Tkapp_CreateTimerHandler(PyObject *self, PyObject *args) +{ + int milliseconds; + PyObject *func; + TkttObject *v; + + if (!PyArg_ParseTuple(args, "iO:createtimerhandler", + &milliseconds, &func)) + return NULL; + if (!PyCallable_Check(func)) { + PyErr_SetString(PyExc_TypeError, "bad argument list"); + return NULL; + } + +#ifdef WITH_THREAD + if (!self && !tcl_lock) { + /* We don't have the Tcl lock since Tcl is threaded. */ + PyErr_SetString(PyExc_RuntimeError, + "_tkinter.createtimerhandler not supported " + "for threaded Tcl"); + return NULL; + } +#endif + + if (self) { + CHECK_TCL_APPARTMENT; + } + + v = Tktt_New(func); + v->token = Tcl_CreateTimerHandler(milliseconds, TimerHandler, + (ClientData)v); + + return (PyObject *) v; +} + + +/** Event Loop **/ + +static PyObject * +Tkapp_MainLoop(PyObject *_self, PyObject *args) +{ + int threshold = 0; + TkappObject *self = (TkappObject*)_self; +#ifdef WITH_THREAD + PyThreadState *tstate = PyThreadState_Get(); +#endif + + if (!PyArg_ParseTuple(args, "|i:mainloop", &threshold)) + return NULL; + +#ifdef WITH_THREAD + if (!self && !tcl_lock) { + /* We don't have the Tcl lock since Tcl is threaded. */ + PyErr_SetString(PyExc_RuntimeError, + "_tkinter.mainloop not supported " + "for threaded Tcl"); + return NULL; + } +#endif + + if (self) { + CHECK_TCL_APPARTMENT; + self->dispatching = 1; + } + + quitMainLoop = 0; + while (Tk_GetNumMainWindows() > threshold && + !quitMainLoop && + !errorInCmd) + { + int result; + +#ifdef WITH_THREAD + if (self && self->threaded) { + /* Allow other Python threads to run. */ + ENTER_TCL + result = Tcl_DoOneEvent(0); + LEAVE_TCL + } + else { + Py_BEGIN_ALLOW_THREADS + if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); + tcl_tstate = tstate; + result = Tcl_DoOneEvent(TCL_DONT_WAIT); + tcl_tstate = NULL; + if(tcl_lock)PyThread_release_lock(tcl_lock); + if (result == 0) + Sleep(Tkinter_busywaitinterval); + Py_END_ALLOW_THREADS + } +#else + result = Tcl_DoOneEvent(0); +#endif + + if (PyErr_CheckSignals() != 0) { + if (self) + self->dispatching = 0; + return NULL; + } + if (result < 0) + break; + } + if (self) + self->dispatching = 0; + quitMainLoop = 0; + + if (errorInCmd) { + errorInCmd = 0; + PyErr_Restore(excInCmd, valInCmd, trbInCmd); + excInCmd = valInCmd = trbInCmd = NULL; + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +Tkapp_DoOneEvent(PyObject *self, PyObject *args) +{ + int flags = 0; + int rv; + + if (!PyArg_ParseTuple(args, "|i:dooneevent", &flags)) + return NULL; + + ENTER_TCL + rv = Tcl_DoOneEvent(flags); + LEAVE_TCL + return Py_BuildValue("i", rv); +} + +static PyObject * +Tkapp_Quit(PyObject *self, PyObject *args) +{ + + if (!PyArg_ParseTuple(args, ":quit")) + return NULL; + + quitMainLoop = 1; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +Tkapp_InterpAddr(PyObject *self, PyObject *args) +{ + + if (!PyArg_ParseTuple(args, ":interpaddr")) + return NULL; + + return PyInt_FromLong((long)Tkapp_Interp(self)); +} + + +static PyObject * +Tkapp_WantObjects(PyObject *self, PyObject *args) +{ + + int wantobjects = -1; + if (!PyArg_ParseTuple(args, "|i:wantobjects", &wantobjects)) + return NULL; + if (wantobjects == -1) + return PyBool_FromLong(((TkappObject*)self)->wantobjects); + ((TkappObject*)self)->wantobjects = wantobjects; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +Tkapp_WillDispatch(PyObject *self, PyObject *args) +{ + + ((TkappObject*)self)->dispatching = 1; + + Py_INCREF(Py_None); + return Py_None; +} + + +/**** Tkapp Method List ****/ + +static PyMethodDef Tkapp_methods[] = +{ + {"willdispatch", Tkapp_WillDispatch, METH_NOARGS}, + {"wantobjects", Tkapp_WantObjects, METH_VARARGS}, + {"call", Tkapp_Call, METH_OLDARGS}, + {"globalcall", Tkapp_GlobalCall, METH_OLDARGS}, + {"eval", Tkapp_Eval, METH_VARARGS}, + {"globaleval", Tkapp_GlobalEval, METH_VARARGS}, + {"evalfile", Tkapp_EvalFile, METH_VARARGS}, + {"record", Tkapp_Record, METH_VARARGS}, + {"adderrorinfo", Tkapp_AddErrorInfo, METH_VARARGS}, + {"setvar", Tkapp_SetVar, METH_VARARGS}, + {"globalsetvar", Tkapp_GlobalSetVar, METH_VARARGS}, + {"getvar", Tkapp_GetVar, METH_VARARGS}, + {"globalgetvar", Tkapp_GlobalGetVar, METH_VARARGS}, + {"unsetvar", Tkapp_UnsetVar, METH_VARARGS}, + {"globalunsetvar", Tkapp_GlobalUnsetVar, METH_VARARGS}, + {"getint", Tkapp_GetInt, METH_VARARGS}, + {"getdouble", Tkapp_GetDouble, METH_VARARGS}, + {"getboolean", Tkapp_GetBoolean, METH_VARARGS}, + {"exprstring", Tkapp_ExprString, METH_VARARGS}, + {"exprlong", Tkapp_ExprLong, METH_VARARGS}, + {"exprdouble", Tkapp_ExprDouble, METH_VARARGS}, + {"exprboolean", Tkapp_ExprBoolean, METH_VARARGS}, + {"splitlist", Tkapp_SplitList, METH_VARARGS}, + {"split", Tkapp_Split, METH_VARARGS}, + {"merge", Tkapp_Merge, METH_OLDARGS}, + {"createcommand", Tkapp_CreateCommand, METH_VARARGS}, + {"deletecommand", Tkapp_DeleteCommand, METH_VARARGS}, +#ifdef HAVE_CREATEFILEHANDLER + {"createfilehandler", Tkapp_CreateFileHandler, METH_VARARGS}, + {"deletefilehandler", Tkapp_DeleteFileHandler, METH_VARARGS}, +#endif + {"createtimerhandler", Tkapp_CreateTimerHandler, METH_VARARGS}, + {"mainloop", Tkapp_MainLoop, METH_VARARGS}, + {"dooneevent", Tkapp_DoOneEvent, METH_VARARGS}, + {"quit", Tkapp_Quit, METH_VARARGS}, + {"interpaddr", Tkapp_InterpAddr, METH_VARARGS}, + {NULL, NULL} +}; + + + +/**** Tkapp Type Methods ****/ + +static void +Tkapp_Dealloc(PyObject *self) +{ + /*CHECK_TCL_APPARTMENT;*/ + ENTER_TCL + Tcl_DeleteInterp(Tkapp_Interp(self)); + LEAVE_TCL + PyObject_Del(self); + DisableEventHook(); +} + +static PyObject * +Tkapp_GetAttr(PyObject *self, char *name) +{ + return Py_FindMethod(Tkapp_methods, self, name); +} + +static PyTypeObject Tkapp_Type = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size */ + "tkapp", /*tp_name */ + sizeof(TkappObject), /*tp_basicsize */ + 0, /*tp_itemsize */ + Tkapp_Dealloc, /*tp_dealloc */ + 0, /*tp_print */ + Tkapp_GetAttr, /*tp_getattr */ + 0, /*tp_setattr */ + 0, /*tp_compare */ + 0, /*tp_repr */ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash */ +}; + + + +/**** Tkinter Module ****/ + +typedef struct { + PyObject* tuple; + int size; /* current size */ + int maxsize; /* allocated size */ +} FlattenContext; + +static int +_bump(FlattenContext* context, int size) +{ + /* expand tuple to hold (at least) size new items. + return true if successful, false if an exception was raised */ + + int maxsize = context->maxsize * 2; + + if (maxsize < context->size + size) + maxsize = context->size + size; + + context->maxsize = maxsize; + + return _PyTuple_Resize(&context->tuple, maxsize) >= 0; +} + +static int +_flatten1(FlattenContext* context, PyObject* item, int depth) +{ + /* add tuple or list to argument tuple (recursively) */ + + int i, size; + + if (depth > 1000) { + PyErr_SetString(PyExc_ValueError, + "nesting too deep in _flatten"); + return 0; + } else if (PyList_Check(item)) { + size = PyList_GET_SIZE(item); + /* preallocate (assume no nesting) */ + if (context->size + size > context->maxsize && + !_bump(context, size)) + return 0; + /* copy items to output tuple */ + for (i = 0; i < size; i++) { + PyObject *o = PyList_GET_ITEM(item, i); + if (PyList_Check(o) || PyTuple_Check(o)) { + if (!_flatten1(context, o, depth + 1)) + return 0; + } else if (o != Py_None) { + if (context->size + 1 > context->maxsize && + !_bump(context, 1)) + return 0; + Py_INCREF(o); + PyTuple_SET_ITEM(context->tuple, + context->size++, o); + } + } + } else if (PyTuple_Check(item)) { + /* same, for tuples */ + size = PyTuple_GET_SIZE(item); + if (context->size + size > context->maxsize && + !_bump(context, size)) + return 0; + for (i = 0; i < size; i++) { + PyObject *o = PyTuple_GET_ITEM(item, i); + if (PyList_Check(o) || PyTuple_Check(o)) { + if (!_flatten1(context, o, depth + 1)) + return 0; + } else if (o != Py_None) { + if (context->size + 1 > context->maxsize && + !_bump(context, 1)) + return 0; + Py_INCREF(o); + PyTuple_SET_ITEM(context->tuple, + context->size++, o); + } + } + } else { + PyErr_SetString(PyExc_TypeError, "argument must be sequence"); + return 0; + } + return 1; +} + +static PyObject * +Tkinter_Flatten(PyObject* self, PyObject* args) +{ + FlattenContext context; + PyObject* item; + + if (!PyArg_ParseTuple(args, "O:_flatten", &item)) + return NULL; + + context.maxsize = PySequence_Size(item); + if (context.maxsize <= 0) + return PyTuple_New(0); + + context.tuple = PyTuple_New(context.maxsize); + if (!context.tuple) + return NULL; + + context.size = 0; + + if (!_flatten1(&context, item,0)) + return NULL; + + if (_PyTuple_Resize(&context.tuple, context.size)) + return NULL; + + return context.tuple; +} + +static PyObject * +Tkinter_Create(PyObject *self, PyObject *args) +{ + char *screenName = NULL; + char *baseName = NULL; + char *className = NULL; + int interactive = 0; + int wantobjects = 0; + + baseName = strrchr(Py_GetProgramName(), '/'); + if (baseName != NULL) + baseName++; + else + baseName = Py_GetProgramName(); + className = "Tk"; + + if (!PyArg_ParseTuple(args, "|zssii:create", + &screenName, &baseName, &className, + &interactive, &wantobjects)) + return NULL; + + return (PyObject *) Tkapp_New(screenName, baseName, className, + interactive, wantobjects); +} + +static PyObject * +Tkinter_setbusywaitinterval(PyObject *self, PyObject *args) +{ + int new_val; + if (!PyArg_ParseTuple(args, "i:setbusywaitinterval", &new_val)) + return NULL; + if (new_val < 0) { + PyErr_SetString(PyExc_ValueError, + "busywaitinterval must be >= 0"); + return NULL; + } + Tkinter_busywaitinterval = new_val; + Py_INCREF(Py_None); + return Py_None; +} + +static char setbusywaitinterval_doc[] = +"setbusywaitinterval(n) -> None\n\ +\n\ +Set the busy-wait interval in milliseconds between successive\n\ +calls to Tcl_DoOneEvent in a threaded Python interpreter.\n\ +It should be set to a divisor of the maximum time between\n\ +frames in an animation."; + +static PyObject * +Tkinter_getbusywaitinterval(PyObject *self, PyObject *args) +{ + return PyInt_FromLong(Tkinter_busywaitinterval); +} + +static char getbusywaitinterval_doc[] = +"getbusywaitinterval() -> int\n\ +\n\ +Return the current busy-wait interval between successive\n\ +calls to Tcl_DoOneEvent in a threaded Python interpreter."; + +static PyMethodDef moduleMethods[] = +{ + {"_flatten", Tkinter_Flatten, METH_VARARGS}, + {"create", Tkinter_Create, METH_VARARGS}, +#ifdef HAVE_CREATEFILEHANDLER + {"createfilehandler", Tkapp_CreateFileHandler, METH_VARARGS}, + {"deletefilehandler", Tkapp_DeleteFileHandler, METH_VARARGS}, +#endif + {"createtimerhandler", Tkapp_CreateTimerHandler, METH_VARARGS}, + {"mainloop", Tkapp_MainLoop, METH_VARARGS}, + {"dooneevent", Tkapp_DoOneEvent, METH_VARARGS}, + {"quit", Tkapp_Quit, METH_VARARGS}, + {"setbusywaitinterval",Tkinter_setbusywaitinterval, METH_VARARGS, + setbusywaitinterval_doc}, + {"getbusywaitinterval",(PyCFunction)Tkinter_getbusywaitinterval, + METH_NOARGS, getbusywaitinterval_doc}, + {NULL, NULL} +}; + +#ifdef WAIT_FOR_STDIN + +static int stdin_ready = 0; + +#ifndef MS_WINDOWS +static void +MyFileProc(void *clientData, int mask) +{ + stdin_ready = 1; +} +#endif + +#ifdef WITH_THREAD +static PyThreadState *event_tstate = NULL; +#endif + +static int +EventHook(void) +{ +#ifndef MS_WINDOWS + int tfile; +#endif +#ifdef WITH_THREAD + PyEval_RestoreThread(event_tstate); +#endif + stdin_ready = 0; + errorInCmd = 0; +#ifndef MS_WINDOWS + tfile = fileno(stdin); + Tcl_CreateFileHandler(tfile, TCL_READABLE, MyFileProc, NULL); +#endif + while (!errorInCmd && !stdin_ready) { + int result; +#ifdef MS_WINDOWS + if (_kbhit()) { + stdin_ready = 1; + break; + } +#endif +#if defined(WITH_THREAD) || defined(MS_WINDOWS) + Py_BEGIN_ALLOW_THREADS + if(tcl_lock)PyThread_acquire_lock(tcl_lock, 1); + tcl_tstate = event_tstate; + + result = Tcl_DoOneEvent(TCL_DONT_WAIT); + + tcl_tstate = NULL; + if(tcl_lock)PyThread_release_lock(tcl_lock); + if (result == 0) + Sleep(Tkinter_busywaitinterval); + Py_END_ALLOW_THREADS +#else + result = Tcl_DoOneEvent(0); +#endif + + if (result < 0) + break; + } +#ifndef MS_WINDOWS + Tcl_DeleteFileHandler(tfile); +#endif + if (errorInCmd) { + errorInCmd = 0; + PyErr_Restore(excInCmd, valInCmd, trbInCmd); + excInCmd = valInCmd = trbInCmd = NULL; + PyErr_Print(); + } +#ifdef WITH_THREAD + PyEval_SaveThread(); +#endif + return 0; +} + +#endif + +static void +EnableEventHook(void) +{ +#ifdef WAIT_FOR_STDIN + if (PyOS_InputHook == NULL) { +#ifdef WITH_THREAD + event_tstate = PyThreadState_Get(); +#endif + PyOS_InputHook = EventHook; + } +#endif +} + +static void +DisableEventHook(void) +{ +#ifdef WAIT_FOR_STDIN + if (Tk_GetNumMainWindows() == 0 && PyOS_InputHook == EventHook) { + PyOS_InputHook = NULL; + } +#endif +} + + +/* all errors will be checked in one fell swoop in init_tkinter() */ +static void +ins_long(PyObject *d, char *name, long val) +{ + PyObject *v = PyInt_FromLong(val); + if (v) { + PyDict_SetItemString(d, name, v); + Py_DECREF(v); + } +} +static void +ins_string(PyObject *d, char *name, char *val) +{ + PyObject *v = PyString_FromString(val); + if (v) { + PyDict_SetItemString(d, name, v); + Py_DECREF(v); + } +} + + +PyMODINIT_FUNC +init_tkinter(void) +{ + PyObject *m, *d; + + Tkapp_Type.ob_type = &PyType_Type; + +#ifdef WITH_THREAD + tcl_lock = PyThread_allocate_lock(); +#endif + + m = Py_InitModule("_tkinter", moduleMethods); + + d = PyModule_GetDict(m); + Tkinter_TclError = PyErr_NewException("_tkinter.TclError", NULL, NULL); + PyDict_SetItemString(d, "TclError", Tkinter_TclError); + + ins_long(d, "READABLE", TCL_READABLE); + ins_long(d, "WRITABLE", TCL_WRITABLE); + ins_long(d, "EXCEPTION", TCL_EXCEPTION); + ins_long(d, "WINDOW_EVENTS", TCL_WINDOW_EVENTS); + ins_long(d, "FILE_EVENTS", TCL_FILE_EVENTS); + ins_long(d, "TIMER_EVENTS", TCL_TIMER_EVENTS); + ins_long(d, "IDLE_EVENTS", TCL_IDLE_EVENTS); + ins_long(d, "ALL_EVENTS", TCL_ALL_EVENTS); + ins_long(d, "DONT_WAIT", TCL_DONT_WAIT); + ins_string(d, "TK_VERSION", TK_VERSION); + ins_string(d, "TCL_VERSION", TCL_VERSION); + + PyDict_SetItemString(d, "TkappType", (PyObject *)&Tkapp_Type); + + Tktt_Type.ob_type = &PyType_Type; + PyDict_SetItemString(d, "TkttType", (PyObject *)&Tktt_Type); + + PyTclObject_Type.ob_type = &PyType_Type; + PyDict_SetItemString(d, "Tcl_Obj", (PyObject *)&PyTclObject_Type); + +#ifdef TK_AQUA + /* Tk_MacOSXSetupTkNotifier must be called before Tcl's subsystems + * start waking up. Note that Tcl_FindExecutable will do this, this + * code must be above it! The original warning from + * tkMacOSXAppInit.c is copied below. + * + * NB - You have to swap in the Tk Notifier BEFORE you start up the + * Tcl interpreter for now. It probably should work to do this + * in the other order, but for now it doesn't seem to. + * + */ + Tk_MacOSXSetupTkNotifier(); +#endif + + + /* This helps the dynamic loader; in Unicode aware Tcl versions + it also helps Tcl find its encodings. */ + Tcl_FindExecutable(Py_GetProgramName()); + + if (PyErr_Occurred()) + return; + +#if 0 + /* This was not a good idea; through bindings, + Tcl_Finalize() may invoke Python code but at that point the + interpreter and thread state have already been destroyed! */ + Py_AtExit(Tcl_Finalize); +#endif + +#ifdef macintosh + /* + ** Part of this code is stolen from MacintoshInit in tkMacAppInit. + ** Most of the initializations in that routine (toolbox init calls and + ** such) have already been done for us, so we only need these. + */ + tcl_macQdPtr = &qd; + + Tcl_MacSetEventProc(PyMacConvertEvent); +#if GENERATINGCFM + mac_addlibresources(); +#endif /* GENERATINGCFM */ +#endif /* macintosh */ +} + + + +#ifdef macintosh + +/* +** Anyone who embeds Tcl/Tk on the Mac must define panic(). +*/ + +void +panic(char * format, ...) +{ + va_list varg; + + va_start(varg, format); + + vfprintf(stderr, format, varg); + (void) fflush(stderr); + + va_end(varg); + + Py_FatalError("Tcl/Tk panic"); +} + +/* +** Pass events to SIOUX before passing them to Tk. +*/ + +static int +PyMacConvertEvent(EventRecord *eventPtr) +{ + WindowPtr frontwin; + /* + ** Sioux eats too many events, so we don't pass it everything. We + ** always pass update events to Sioux, and we only pass other events if + ** the Sioux window is frontmost. This means that Tk menus don't work + ** in that case, but at least we can scroll the sioux window. + ** Note that the SIOUXIsAppWindow() routine we use here is not really + ** part of the external interface of Sioux... + */ + frontwin = FrontWindow(); + if ( eventPtr->what == updateEvt || SIOUXIsAppWindow(frontwin) ) { + if (SIOUXHandleOneEvent(eventPtr)) + return 0; /* Nothing happened to the Tcl event queue */ + } + return TkMacConvertEvent(eventPtr); +} + +#if GENERATINGCFM + +/* +** Additional Mac specific code for dealing with shared libraries. +*/ + +#include +#include + +static int loaded_from_shlib = 0; +static FSSpec library_fss; + +/* +** If this module is dynamically loaded the following routine should +** be the init routine. It takes care of adding the shared library to +** the resource-file chain, so that the tk routines can find their +** resources. +*/ +OSErr pascal +init_tkinter_shlib(CFragInitBlockPtr data) +{ + __initialize(); + if ( data == nil ) return noErr; + if ( data->fragLocator.where == kDataForkCFragLocator ) { + library_fss = *data->fragLocator.u.onDisk.fileSpec; + loaded_from_shlib = 1; + } else if ( data->fragLocator.where == kResourceCFragLocator ) { + library_fss = *data->fragLocator.u.inSegs.fileSpec; + loaded_from_shlib = 1; + } + return noErr; +} + +/* +** Insert the library resources into the search path. Put them after +** the resources from the application. Again, we ignore errors. +*/ +static +mac_addlibresources(void) +{ + if ( !loaded_from_shlib ) + return; + (void)FSpOpenResFile(&library_fss, fsRdPerm); +} + +#endif /* GENERATINGCFM */ +#endif /* macintosh */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_weakref.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_weakref.c new file mode 100644 index 00000000..40d716e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/_weakref.c @@ -0,0 +1,131 @@ +#include "Python.h" + + +#define GET_WEAKREFS_LISTPTR(o) \ + ((PyWeakReference **) PyObject_GET_WEAKREFS_LISTPTR(o)) + + +PyDoc_STRVAR(weakref_getweakrefcount__doc__, +"getweakrefcount(object) -- return the number of weak references\n" +"to 'object'."); + +static PyObject * +weakref_getweakrefcount(PyObject *self, PyObject *object) +{ + PyObject *result = NULL; + + if (PyType_SUPPORTS_WEAKREFS(object->ob_type)) { + PyWeakReference **list = GET_WEAKREFS_LISTPTR(object); + + result = PyInt_FromLong(_PyWeakref_GetWeakrefCount(*list)); + } + else + result = PyInt_FromLong(0); + + return result; +} + + +PyDoc_STRVAR(weakref_getweakrefs__doc__, +"getweakrefs(object) -- return a list of all weak reference objects\n" +"that point to 'object'."); + +static PyObject * +weakref_getweakrefs(PyObject *self, PyObject *object) +{ + PyObject *result = NULL; + + if (PyType_SUPPORTS_WEAKREFS(object->ob_type)) { + PyWeakReference **list = GET_WEAKREFS_LISTPTR(object); + long count = _PyWeakref_GetWeakrefCount(*list); + + result = PyList_New(count); + if (result != NULL) { + PyWeakReference *current = *list; + long i; + for (i = 0; i < count; ++i) { + PyList_SET_ITEM(result, i, (PyObject *) current); + Py_INCREF(current); + current = current->wr_next; + } + } + } + else { + result = PyList_New(0); + } + return result; +} + + +PyDoc_STRVAR(weakref_ref__doc__, +"ref(object[, callback]) -- create a weak reference to 'object';\n" +"when 'object' is finalized, 'callback' will be called and passed\n" +"a reference to the weak reference object when 'object' is about\n" +"to be finalized."); + +static PyObject * +weakref_ref(PyObject *self, PyObject *args) +{ + PyObject *object; + PyObject *callback = NULL; + PyObject *result = NULL; + + if (PyArg_UnpackTuple(args, "ref", 1, 2, &object, &callback)) { + result = PyWeakref_NewRef(object, callback); + } + return result; +} + + +PyDoc_STRVAR(weakref_proxy__doc__, +"proxy(object[, callback]) -- create a proxy object that weakly\n" +"references 'object'. 'callback', if given, is called with a\n" +"reference to the proxy when 'object' is about to be finalized."); + +static PyObject * +weakref_proxy(PyObject *self, PyObject *args) +{ + PyObject *object; + PyObject *callback = NULL; + PyObject *result = NULL; + + if (PyArg_UnpackTuple(args, "proxy", 1, 2, &object, &callback)) { + result = PyWeakref_NewProxy(object, callback); + } + return result; +} + + +static PyMethodDef +weakref_functions[] = { + {"getweakrefcount", weakref_getweakrefcount, METH_O, + weakref_getweakrefcount__doc__}, + {"getweakrefs", weakref_getweakrefs, METH_O, + weakref_getweakrefs__doc__}, + {"proxy", weakref_proxy, METH_VARARGS, + weakref_proxy__doc__}, + {"ref", weakref_ref, METH_VARARGS, + weakref_ref__doc__}, + {NULL, NULL, 0, NULL} +}; + + +PyMODINIT_FUNC +init_weakref(void) +{ + PyObject *m; + + m = Py_InitModule3("_weakref", weakref_functions, + "Weak-reference support module."); + if (m != NULL) { + Py_INCREF(&_PyWeakref_RefType); + PyModule_AddObject(m, "ReferenceType", + (PyObject *) &_PyWeakref_RefType); + Py_INCREF(&_PyWeakref_ProxyType); + PyModule_AddObject(m, "ProxyType", + (PyObject *) &_PyWeakref_ProxyType); + Py_INCREF(&_PyWeakref_CallableProxyType); + PyModule_AddObject(m, "CallableProxyType", + (PyObject *) &_PyWeakref_CallableProxyType); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/addrinfo.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/addrinfo.h new file mode 100644 index 00000000..6b528c08 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/addrinfo.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef HAVE_GETADDRINFO + +/* + * Error return codes from getaddrinfo() + */ +#ifdef EAI_ADDRFAMILY +/* If this is defined, there is a conflicting implementation + in the C library, which can't be used for some reason. + Make sure it won't interfere with this emulation. */ + +#undef EAI_ADDRFAMILY +#undef EAI_AGAIN +#undef EAI_BADFLAGS +#undef EAI_FAIL +#undef EAI_FAMILY +#undef EAI_MEMORY +#undef EAI_NODATA +#undef EAI_NONAME +#undef EAI_SERVICE +#undef EAI_SOCKTYPE +#undef EAI_SYSTEM +#undef EAI_BADHINTS +#undef EAI_PROTOCOL +#undef EAI_MAX +#undef getaddrinfo +#define getaddrinfo fake_getaddrinfo +#endif + +#define EAI_ADDRFAMILY 1 /* address family for hostname not supported */ +#define EAI_AGAIN 2 /* temporary failure in name resolution */ +#define EAI_BADFLAGS 3 /* invalid value for ai_flags */ +#define EAI_FAIL 4 /* non-recoverable failure in name resolution */ +#define EAI_FAMILY 5 /* ai_family not supported */ +#define EAI_MEMORY 6 /* memory allocation failure */ +#define EAI_NODATA 7 /* no address associated with hostname */ +#define EAI_NONAME 8 /* hostname nor servname provided, or not known */ +#define EAI_SERVICE 9 /* servname not supported for ai_socktype */ +#define EAI_SOCKTYPE 10 /* ai_socktype not supported */ +#define EAI_SYSTEM 11 /* system error returned in errno */ +#define EAI_BADHINTS 12 +#define EAI_PROTOCOL 13 +#define EAI_MAX 14 + +/* + * Flag values for getaddrinfo() + */ +#ifdef AI_PASSIVE +#undef AI_PASSIVE +#undef AI_CANONNAME +#undef AI_NUMERICHOST +#undef AI_MASK +#undef AI_ALL +#undef AI_V4MAPPED_CFG +#undef AI_ADDRCONFIG +#undef AI_V4MAPPED +#undef AI_DEFAULT +#endif + +#define AI_PASSIVE 0x00000001 /* get address to use bind() */ +#define AI_CANONNAME 0x00000002 /* fill ai_canonname */ +#define AI_NUMERICHOST 0x00000004 /* prevent name resolution */ +/* valid flags for addrinfo */ +#define AI_MASK (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST) + +#define AI_ALL 0x00000100 /* IPv6 and IPv4-mapped (with AI_V4MAPPED) */ +#define AI_V4MAPPED_CFG 0x00000200 /* accept IPv4-mapped if kernel supports */ +#define AI_ADDRCONFIG 0x00000400 /* only if any address is assigned */ +#define AI_V4MAPPED 0x00000800 /* accept IPv4-mapped IPv6 address */ +/* special recommended flags for getipnodebyname */ +#define AI_DEFAULT (AI_V4MAPPED_CFG | AI_ADDRCONFIG) + +#endif /* HAVE_GETADDRINFO */ + +#ifndef HAVE_GETNAMEINFO + +/* + * Constants for getnameinfo() + */ +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#define NI_MAXSERV 32 +#endif + +/* + * Flag values for getnameinfo() + */ +#ifndef NI_NOFQDN +#define NI_NOFQDN 0x00000001 +#define NI_NUMERICHOST 0x00000002 +#define NI_NAMEREQD 0x00000004 +#define NI_NUMERICSERV 0x00000008 +#define NI_DGRAM 0x00000010 +#endif + +#endif /* HAVE_GETNAMEINFO */ + +#ifndef HAVE_ADDRINFO +struct addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for hostname */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ +}; +#endif + +#ifndef HAVE_SOCKADDR_STORAGE +/* + * RFC 2553: protocol-independent placeholder for socket addresses + */ +#define _SS_MAXSIZE 128 +#ifdef HAVE_LONG_LONG +#define _SS_ALIGNSIZE (sizeof(PY_LONG_LONG)) +#else +#define _SS_ALIGNSIZE (sizeof(double)) +#endif +#define _SS_PAD1SIZE (_SS_ALIGNSIZE - sizeof(u_char) * 2) +#define _SS_PAD2SIZE (_SS_MAXSIZE - sizeof(u_char) * 2 - \ + _SS_PAD1SIZE - _SS_ALIGNSIZE) + +struct sockaddr_storage { +#ifdef HAVE_SOCKADDR_SA_LEN + unsigned char ss_len; /* address length */ + unsigned char ss_family; /* address family */ +#else + unsigned short ss_family; /* address family */ +#endif + char __ss_pad1[_SS_PAD1SIZE]; +#ifdef HAVE_LONG_LONG + PY_LONG_LONG __ss_align; /* force desired structure storage alignment */ +#else + double __ss_align; /* force desired structure storage alignment */ +#endif + char __ss_pad2[_SS_PAD2SIZE]; +}; +#endif + +#ifdef __cplusplus +extern "C" { +#endif +extern void freehostent Py_PROTO((struct hostent *)); +#ifdef __cplusplus +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/almodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/almodule.c new file mode 100644 index 00000000..1cf9d9e9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/almodule.c @@ -0,0 +1,3222 @@ + +#define OLD_INTERFACE /* define for pre-Irix 6 interface */ + +#include "Python.h" +#include "stringobject.h" +#include +#include + +#ifndef AL_NO_ELEM +#ifndef OLD_INTERFACE +#define OLD_INTERFACE +#endif /* OLD_INTERFACE */ +#endif /* AL_NO_ELEM */ + +static PyObject *ErrorObject; + +/* ----------------------------------------------------- */ + +/* Declarations for objects of type port */ + +typedef struct { + PyObject_HEAD + /* XXXX Add your own stuff here */ + ALport port; +} alpobject; + +static PyTypeObject Alptype; + + + +/* ---------------------------------------------------------------- */ + +/* Declarations for objects of type config */ + +typedef struct { + PyObject_HEAD + /* XXXX Add your own stuff here */ + ALconfig config; +} alcobject; + +static PyTypeObject Alctype; + + +static void +ErrorHandler(long code, const char *fmt, ...) +{ + va_list args; + char buf[128]; + + va_start(args, fmt); + vsprintf(buf, fmt, args); + va_end(args); + PyErr_SetString(ErrorObject, buf); +} + +#ifdef AL_NO_ELEM /* IRIX 6 */ + +static PyObject * +param2python(int resource, int param, ALvalue value, ALparamInfo *pinfo) +{ + ALparamInfo info; + + if (pinfo == NULL) { + pinfo = &info; + if (alGetParamInfo(resource, param, &info) < 0) + return NULL; + } + switch (pinfo->elementType) { + case AL_PTR_ELEM: + /* XXXX don't know how to handle this */ + case AL_NO_ELEM: + Py_INCREF(Py_None); + return Py_None; + case AL_INT32_ELEM: + case AL_RESOURCE_ELEM: + case AL_ENUM_ELEM: + return PyInt_FromLong((long) value.i); + case AL_INT64_ELEM: + return PyLong_FromLongLong(value.ll); + case AL_FIXED_ELEM: + return PyFloat_FromDouble(alFixedToDouble(value.ll)); + case AL_CHAR_ELEM: + if (value.ptr == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString((char *) value.ptr); + default: + PyErr_SetString(ErrorObject, "unknown element type"); + return NULL; + } +} + +static int +python2elem(PyObject *item, void *ptr, int elementType) +{ + switch (elementType) { + case AL_INT32_ELEM: + case AL_RESOURCE_ELEM: + case AL_ENUM_ELEM: + if (!PyInt_Check(item)) { + PyErr_BadArgument(); + return -1; + } + *((int *) ptr) = PyInt_AsLong(item); + break; + case AL_INT64_ELEM: + if (PyInt_Check(item)) + *((long long *) ptr) = PyInt_AsLong(item); + else if (PyLong_Check(item)) + *((long long *) ptr) = PyLong_AsLongLong(item); + else { + PyErr_BadArgument(); + return -1; + } + break; + case AL_FIXED_ELEM: + if (PyInt_Check(item)) + *((long long *) ptr) = alDoubleToFixed((double) PyInt_AsLong(item)); + else if (PyFloat_Check(item)) + *((long long *) ptr) = alDoubleToFixed(PyFloat_AsDouble(item)); + else { + PyErr_BadArgument(); + return -1; + } + break; + default: + PyErr_SetString(ErrorObject, "unknown element type"); + return -1; + } + return 0; +} + +static int +python2param(int resource, ALpv *param, PyObject *value, ALparamInfo *pinfo) +{ + ALparamInfo info; + int i, stepsize; + PyObject *item; + + if (pinfo == NULL) { + pinfo = &info; + if (alGetParamInfo(resource, param->param, &info) < 0) + return -1; + } + switch (pinfo->valueType) { + case AL_STRING_VAL: + if (pinfo->elementType != AL_CHAR_ELEM) { + PyErr_SetString(ErrorObject, "unknown element type"); + return -1; + } + if (!PyString_Check(value)) { + PyErr_BadArgument(); + return -1; + } + param->value.ptr = PyString_AS_STRING(value); + param->sizeIn = PyString_GET_SIZE(value)+1; /*account for NUL*/ + break; + case AL_SET_VAL: + case AL_VECTOR_VAL: + if (!PyList_Check(value) && !PyTuple_Check(value)) { + PyErr_BadArgument(); + return -1; + } + switch (pinfo->elementType) { + case AL_INT32_ELEM: + case AL_RESOURCE_ELEM: + case AL_ENUM_ELEM: + param->sizeIn = PySequence_Size(value); + param->value.ptr = PyMem_NEW(int, param->sizeIn); + stepsize = sizeof(int); + break; + case AL_INT64_ELEM: + case AL_FIXED_ELEM: + param->sizeIn = PySequence_Size(value); + param->value.ptr = PyMem_NEW(long long, param->sizeIn); + stepsize = sizeof(long long); + break; + } + for (i = 0; i < param->sizeIn; i++) { + item = PySequence_GetItem(value, i); + if (python2elem(item, (void *) ((char *) param->value.ptr + i*stepsize), pinfo->elementType) < 0) { + PyMem_DEL(param->value.ptr); + return -1; + } + } + break; + case AL_SCALAR_VAL: + switch (pinfo->elementType) { + case AL_INT32_ELEM: + case AL_RESOURCE_ELEM: + case AL_ENUM_ELEM: + return python2elem(value, (void *) ¶m->value.i, + pinfo->elementType); + case AL_INT64_ELEM: + case AL_FIXED_ELEM: + return python2elem(value, (void *) ¶m->value.ll, + pinfo->elementType); + default: + PyErr_SetString(ErrorObject, "unknown element type"); + return -1; + } + } + return 0; +} + +static int +python2params(int resource1, int resource2, PyObject *list, ALpv **pvsp, ALparamInfo **pinfop) +{ + PyObject *item; + ALpv *pvs; + ALparamInfo *pinfo; + int npvs, i; + + npvs = PyList_Size(list); + pvs = PyMem_NEW(ALpv, npvs); + pinfo = PyMem_NEW(ALparamInfo, npvs); + for (i = 0; i < npvs; i++) { + item = PyList_GetItem(list, i); + if (!PyArg_ParseTuple(item, "iO", &pvs[i].param, &item)) + goto error; + if (alGetParamInfo(resource1, pvs[i].param, &pinfo[i]) < 0 && + alGetParamInfo(resource2, pvs[i].param, &pinfo[i]) < 0) + goto error; + if (python2param(resource1, &pvs[i], item, &pinfo[i]) < 0) + goto error; + } + + *pvsp = pvs; + *pinfop = pinfo; + return npvs; + + error: + /* XXXX we should clean up everything */ + if (pvs) + PyMem_DEL(pvs); + if (pinfo) + PyMem_DEL(pinfo); + return -1; +} + +/* -------------------------------------------------------- */ + + +static PyObject * +SetConfig(alcobject *self, PyObject *args, int (*func)(ALconfig, int)) +{ + int par; + + if (!PyArg_ParseTuple(args, "i:SetConfig", &par)) + return NULL; + + if ((*func)(self->config, par) == -1) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +GetConfig(alcobject *self, PyObject *args, int (*func)(ALconfig)) +{ + int par; + + if (!PyArg_ParseTuple(args, ":GetConfig")) + return NULL; + + if ((par = (*func)(self->config)) == -1) + return NULL; + + return PyInt_FromLong((long) par); +} + +PyDoc_STRVAR(alc_SetWidth__doc__, +"alSetWidth: set the wordsize for integer audio data."); + +static PyObject * +alc_SetWidth(alcobject *self, PyObject *args) +{ + return SetConfig(self, args, alSetWidth); +} + + +PyDoc_STRVAR(alc_GetWidth__doc__, +"alGetWidth: get the wordsize for integer audio data."); + +static PyObject * +alc_GetWidth(alcobject *self, PyObject *args) +{ + return GetConfig(self, args, alGetWidth); +} + + +PyDoc_STRVAR(alc_SetSampFmt__doc__, +"alSetSampFmt: set the sample format setting in an audio ALconfig " +"structure."); + +static PyObject * +alc_SetSampFmt(alcobject *self, PyObject *args) +{ + return SetConfig(self, args, alSetSampFmt); +} + + +PyDoc_STRVAR(alc_GetSampFmt__doc__, +"alGetSampFmt: get the sample format setting in an audio ALconfig " +"structure."); + +static PyObject * +alc_GetSampFmt(alcobject *self, PyObject *args) +{ + return GetConfig(self, args, alGetSampFmt); +} + + +PyDoc_STRVAR(alc_SetChannels__doc__, +"alSetChannels: set the channel settings in an audio ALconfig."); + +static PyObject * +alc_SetChannels(alcobject *self, PyObject *args) +{ + return SetConfig(self, args, alSetChannels); +} + + +PyDoc_STRVAR(alc_GetChannels__doc__, +"alGetChannels: get the channel settings in an audio ALconfig."); + +static PyObject * +alc_GetChannels(alcobject *self, PyObject *args) +{ + return GetConfig(self, args, alGetChannels); +} + + +PyDoc_STRVAR(alc_SetFloatMax__doc__, +"alSetFloatMax: set the maximum value of floating point sample data."); + +static PyObject * +alc_SetFloatMax(alcobject *self, PyObject *args) +{ + double maximum_value; + + if (!PyArg_ParseTuple(args, "d:SetFloatMax", &maximum_value)) + return NULL; + if (alSetFloatMax(self->config, maximum_value) < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(alc_GetFloatMax__doc__, +"alGetFloatMax: get the maximum value of floating point sample data."); + +static PyObject * +alc_GetFloatMax(alcobject *self, PyObject *args) +{ + double maximum_value; + + if (!PyArg_ParseTuple(args, ":GetFloatMax")) + return NULL; + if ((maximum_value = alGetFloatMax(self->config)) == 0) + return NULL; + return PyFloat_FromDouble(maximum_value); +} + + +PyDoc_STRVAR(alc_SetDevice__doc__, +"alSetDevice: set the device setting in an audio ALconfig structure."); + +static PyObject * +alc_SetDevice(alcobject *self, PyObject *args) +{ + return SetConfig(self, args, alSetDevice); +} + + +PyDoc_STRVAR(alc_GetDevice__doc__, +"alGetDevice: get the device setting in an audio ALconfig structure."); + +static PyObject * +alc_GetDevice(alcobject *self, PyObject *args) +{ + return GetConfig(self, args, alGetDevice); +} + + +PyDoc_STRVAR(alc_SetQueueSize__doc__, +"alSetQueueSize: set audio port buffer size."); + +static PyObject * +alc_SetQueueSize(alcobject *self, PyObject *args) +{ + return SetConfig(self, args, alSetQueueSize); +} + + +PyDoc_STRVAR(alc_GetQueueSize__doc__, +"alGetQueueSize: get audio port buffer size."); + +static PyObject * +alc_GetQueueSize(alcobject *self, PyObject *args) +{ + return GetConfig(self, args, alGetQueueSize); +} + +#endif /* AL_NO_ELEM */ + +static PyObject * +setconfig(alcobject *self, PyObject *args, int (*func)(ALconfig, long)) +{ + long par; + + if (!PyArg_ParseTuple(args, "l:SetConfig", &par)) + return NULL; + + if ((*func)(self->config, par) == -1) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +getconfig(alcobject *self, PyObject *args, long (*func)(ALconfig)) +{ + long par; + + if (!PyArg_ParseTuple(args, ":GetConfig")) + return NULL; + + if ((par = (*func)(self->config)) == -1) + return NULL; + + return PyInt_FromLong((long) par); +} + +static PyObject * +alc_setqueuesize (alcobject *self, PyObject *args) +{ + return setconfig(self, args, ALsetqueuesize); +} + +static PyObject * +alc_getqueuesize (alcobject *self, PyObject *args) +{ + return getconfig(self, args, ALgetqueuesize); +} + +static PyObject * +alc_setwidth (alcobject *self, PyObject *args) +{ + return setconfig(self, args, ALsetwidth); +} + +static PyObject * +alc_getwidth (alcobject *self, PyObject *args) +{ + return getconfig(self, args, ALgetwidth); +} + +static PyObject * +alc_getchannels (alcobject *self, PyObject *args) +{ + return getconfig(self, args, ALgetchannels); +} + +static PyObject * +alc_setchannels (alcobject *self, PyObject *args) +{ + return setconfig(self, args, ALsetchannels); +} + +#ifdef AL_405 + +static PyObject * +alc_getsampfmt (alcobject *self, PyObject *args) +{ + return getconfig(self, args, ALgetsampfmt); +} + +static PyObject * +alc_setsampfmt (alcobject *self, PyObject *args) +{ + return setconfig(self, args, ALsetsampfmt); +} + +static PyObject * +alc_getfloatmax(alcobject *self, PyObject *args) +{ + double arg; + + if (!PyArg_ParseTuple(args, ":GetFloatMax")) + return 0; + if ((arg = ALgetfloatmax(self->config)) == 0) + return NULL; + return PyFloat_FromDouble(arg); +} + +static PyObject * +alc_setfloatmax(alcobject *self, PyObject *args) +{ + double arg; + + if (!PyArg_ParseTuple(args, "d:SetFloatMax", &arg)) + return 0; + if (ALsetfloatmax(self->config, arg) == -1) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} +#endif /* AL_405 */ + +static struct PyMethodDef alc_methods[] = { +#ifdef AL_NO_ELEM /* IRIX 6 */ + {"SetWidth", (PyCFunction)alc_SetWidth, METH_VARARGS, alc_SetWidth__doc__}, + {"GetWidth", (PyCFunction)alc_GetWidth, METH_VARARGS, alc_GetWidth__doc__}, + {"SetSampFmt", (PyCFunction)alc_SetSampFmt, METH_VARARGS, alc_SetSampFmt__doc__}, + {"GetSampFmt", (PyCFunction)alc_GetSampFmt, METH_VARARGS, alc_GetSampFmt__doc__}, + {"SetChannels", (PyCFunction)alc_SetChannels, METH_VARARGS, alc_SetChannels__doc__}, + {"GetChannels", (PyCFunction)alc_GetChannels, METH_VARARGS, alc_GetChannels__doc__}, + {"SetFloatMax", (PyCFunction)alc_SetFloatMax, METH_VARARGS, alc_SetFloatMax__doc__}, + {"GetFloatMax", (PyCFunction)alc_GetFloatMax, METH_VARARGS, alc_GetFloatMax__doc__}, + {"SetDevice", (PyCFunction)alc_SetDevice, METH_VARARGS, alc_SetDevice__doc__}, + {"GetDevice", (PyCFunction)alc_GetDevice, METH_VARARGS, alc_GetDevice__doc__}, + {"SetQueueSize", (PyCFunction)alc_SetQueueSize, METH_VARARGS, alc_SetQueueSize__doc__}, + {"GetQueueSize", (PyCFunction)alc_GetQueueSize, METH_VARARGS, alc_GetQueueSize__doc__}, +#endif /* AL_NO_ELEM */ + {"getqueuesize", (PyCFunction)alc_getqueuesize, METH_VARARGS}, + {"setqueuesize", (PyCFunction)alc_setqueuesize, METH_VARARGS}, + {"getwidth", (PyCFunction)alc_getwidth, METH_VARARGS}, + {"setwidth", (PyCFunction)alc_setwidth, METH_VARARGS}, + {"getchannels", (PyCFunction)alc_getchannels, METH_VARARGS}, + {"setchannels", (PyCFunction)alc_setchannels, METH_VARARGS}, +#ifdef AL_405 + {"getsampfmt", (PyCFunction)alc_getsampfmt, METH_VARARGS}, + {"setsampfmt", (PyCFunction)alc_setsampfmt, METH_VARARGS}, + {"getfloatmax", (PyCFunction)alc_getfloatmax, METH_VARARGS}, + {"setfloatmax", (PyCFunction)alc_setfloatmax, METH_VARARGS}, +#endif /* AL_405 */ + + {NULL, NULL} /* sentinel */ +}; + +/* ---------- */ + + +static PyObject * +newalcobject(ALconfig config) +{ + alcobject *self; + + self = PyObject_New(alcobject, &Alctype); + if (self == NULL) + return NULL; + /* XXXX Add your own initializers here */ + self->config = config; + return (PyObject *) self; +} + + +static void +alc_dealloc(alcobject *self) +{ + /* XXXX Add your own cleanup code here */ +#ifdef AL_NO_ELEM /* IRIX 6 */ + (void) alFreeConfig(self->config); /* ignore errors */ +#else + (void) ALfreeconfig(self->config); /* ignore errors */ +#endif + PyObject_Del(self); +} + +static PyObject * +alc_getattr(alcobject *self, char *name) +{ + /* XXXX Add your own getattr code here */ + return Py_FindMethod(alc_methods, (PyObject *)self, name); +} + +PyDoc_STRVAR(Alctype__doc__, ""); + +static PyTypeObject Alctype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "al.config", /*tp_name*/ + sizeof(alcobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)alc_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)alc_getattr, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + Alctype__doc__ /* Documentation string */ +}; + +/* End of code for config objects */ +/* ---------------------------------------------------------------- */ + +#ifdef AL_NO_ELEM /* IRIX 6 */ + +PyDoc_STRVAR(alp_SetConfig__doc__, +"alSetConfig: set the ALconfig of an audio ALport."); + +static PyObject * +alp_SetConfig(alpobject *self, PyObject *args) +{ + alcobject *config; + if (!PyArg_ParseTuple(args, "O!:SetConfig", &Alctype, &config)) + return NULL; + if (alSetConfig(self->port, config->config) < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(alp_GetConfig__doc__, +"alGetConfig: get the ALconfig of an audio ALport."); + +static PyObject * +alp_GetConfig(alpobject *self, PyObject *args) +{ + ALconfig config; + if (!PyArg_ParseTuple(args, ":GetConfig")) + return NULL; + if ((config = alGetConfig(self->port)) == NULL) + return NULL; + return newalcobject(config); +} + + +PyDoc_STRVAR(alp_GetResource__doc__, +"alGetResource: get the resource associated with an audio port."); + +static PyObject * +alp_GetResource(alpobject *self, PyObject *args) +{ + int resource; + + if (!PyArg_ParseTuple(args, ":GetResource")) + return NULL; + if ((resource = alGetResource(self->port)) == 0) + return NULL; + return PyInt_FromLong((long) resource); +} + + +PyDoc_STRVAR(alp_GetFD__doc__, +"alGetFD: get the file descriptor for an audio port."); + +static PyObject * +alp_GetFD(alpobject *self, PyObject *args) +{ + int fd; + + if (!PyArg_ParseTuple(args, ":GetFD")) + return NULL; + + if ((fd = alGetFD(self->port)) < 0) + return NULL; + + return PyInt_FromLong((long) fd); +} + + +PyDoc_STRVAR(alp_GetFilled__doc__, +"alGetFilled: return the number of filled sample frames in " +"an audio port."); + +static PyObject * +alp_GetFilled(alpobject *self, PyObject *args) +{ + int filled; + + if (!PyArg_ParseTuple(args, ":GetFilled")) + return NULL; + if ((filled = alGetFilled(self->port)) < 0) + return NULL; + return PyInt_FromLong((long) filled); +} + + +PyDoc_STRVAR(alp_GetFillable__doc__, +"alGetFillable: report the number of unfilled sample frames " +"in an audio port."); + +static PyObject * +alp_GetFillable(alpobject *self, PyObject *args) +{ + int fillable; + + if (!PyArg_ParseTuple(args, ":GetFillable")) + return NULL; + if ((fillable = alGetFillable(self->port)) < 0) + return NULL; + return PyInt_FromLong((long) fillable); +} + + +PyDoc_STRVAR(alp_ReadFrames__doc__, +"alReadFrames: read sample frames from an audio port."); + +static PyObject * +alp_ReadFrames(alpobject *self, PyObject *args) +{ + int framecount; + PyObject *v; + int size; + int ch; + ALconfig c; + + if (!PyArg_ParseTuple(args, "i:ReadFrames", &framecount)) + return NULL; + if (framecount < 0) { + PyErr_SetString(ErrorObject, "negative framecount"); + return NULL; + } + c = alGetConfig(self->port); + switch (alGetSampFmt(c)) { + case AL_SAMPFMT_TWOSCOMP: + switch (alGetWidth(c)) { + case AL_SAMPLE_8: + size = 1; + break; + case AL_SAMPLE_16: + size = 2; + break; + case AL_SAMPLE_24: + size = 4; + break; + default: + PyErr_SetString(ErrorObject, "can't determine width"); + alFreeConfig(c); + return NULL; + } + break; + case AL_SAMPFMT_FLOAT: + size = 4; + break; + case AL_SAMPFMT_DOUBLE: + size = 8; + break; + default: + PyErr_SetString(ErrorObject, "can't determine format"); + alFreeConfig(c); + return NULL; + } + ch = alGetChannels(c); + alFreeConfig(c); + if (ch < 0) { + PyErr_SetString(ErrorObject, "can't determine # of channels"); + return NULL; + } + size *= ch; + v = PyString_FromStringAndSize((char *) NULL, size * framecount); + if (v == NULL) + return NULL; + + Py_BEGIN_ALLOW_THREADS + alReadFrames(self->port, (void *) PyString_AS_STRING(v), framecount); + Py_END_ALLOW_THREADS + + return v; +} + + +PyDoc_STRVAR(alp_DiscardFrames__doc__, +"alDiscardFrames: discard audio from an audio port."); + +static PyObject * +alp_DiscardFrames(alpobject *self, PyObject *args) +{ + int framecount; + + if (!PyArg_ParseTuple(args, "i:DiscardFrames", &framecount)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + framecount = alDiscardFrames(self->port, framecount); + Py_END_ALLOW_THREADS + + if (framecount < 0) + return NULL; + + return PyInt_FromLong((long) framecount); +} + + +PyDoc_STRVAR(alp_ZeroFrames__doc__, +"alZeroFrames: write zero-valued sample frames to an audio port."); + +static PyObject * +alp_ZeroFrames(alpobject *self, PyObject *args) +{ + int framecount; + + if (!PyArg_ParseTuple(args, "i:ZeroFrames", &framecount)) + return NULL; + + if (framecount < 0) { + PyErr_SetString(ErrorObject, "negative framecount"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + alZeroFrames(self->port, framecount); + Py_END_ALLOW_THREADS + + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(alp_SetFillPoint__doc__, +"alSetFillPoint: set low- or high-water mark for an audio port."); + +static PyObject * +alp_SetFillPoint(alpobject *self, PyObject *args) +{ + int fillpoint; + + if (!PyArg_ParseTuple(args, "i:SetFillPoint", &fillpoint)) + return NULL; + + if (alSetFillPoint(self->port, fillpoint) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(alp_GetFillPoint__doc__, +"alGetFillPoint: get low- or high-water mark for an audio port."); + +static PyObject * +alp_GetFillPoint(alpobject *self, PyObject *args) +{ + int fillpoint; + + if (!PyArg_ParseTuple(args, ":GetFillPoint")) + return NULL; + + if ((fillpoint = alGetFillPoint(self->port)) < 0) + return NULL; + + return PyInt_FromLong((long) fillpoint); +} + + +PyDoc_STRVAR(alp_GetFrameNumber__doc__, +"alGetFrameNumber: get the absolute sample frame number " +"associated with a port."); + +static PyObject * +alp_GetFrameNumber(alpobject *self, PyObject *args) +{ + stamp_t fnum; + + if (!PyArg_ParseTuple(args, ":GetFrameNumber")) + return NULL; + + if (alGetFrameNumber(self->port, &fnum) < 0) + return NULL; + + return PyLong_FromLongLong((long long) fnum); +} + + +PyDoc_STRVAR(alp_GetFrameTime__doc__, +"alGetFrameTime: get the time at which a sample frame came " +"in or will go out."); + +static PyObject * +alp_GetFrameTime(alpobject *self, PyObject *args) +{ + stamp_t fnum, time; + PyObject *ret, *v0, *v1; + + if (!PyArg_ParseTuple(args, ":GetFrameTime")) + return NULL; + if (alGetFrameTime(self->port, &fnum, &time) < 0) + return NULL; + v0 = PyLong_FromLongLong((long long) fnum); + v1 = PyLong_FromLongLong((long long) time); + if (PyErr_Occurred()) { + Py_XDECREF(v0); + Py_XDECREF(v1); + return NULL; + } + ret = Py_BuildValue("(OO)", v0, v1); + Py_DECREF(v0); + Py_DECREF(v1); + return ret; +} + + +PyDoc_STRVAR(alp_WriteFrames__doc__, +"alWriteFrames: write sample frames to an audio port."); + +static PyObject * +alp_WriteFrames(alpobject *self, PyObject *args) +{ + char *samples; + int length; + int size, ch; + ALconfig c; + + if (!PyArg_ParseTuple(args, "s#:WriteFrames", &samples, &length)) + return NULL; + c = alGetConfig(self->port); + switch (alGetSampFmt(c)) { + case AL_SAMPFMT_TWOSCOMP: + switch (alGetWidth(c)) { + case AL_SAMPLE_8: + size = 1; + break; + case AL_SAMPLE_16: + size = 2; + break; + case AL_SAMPLE_24: + size = 4; + break; + default: + PyErr_SetString(ErrorObject, "can't determine width"); + alFreeConfig(c); + return NULL; + } + break; + case AL_SAMPFMT_FLOAT: + size = 4; + break; + case AL_SAMPFMT_DOUBLE: + size = 8; + break; + default: + PyErr_SetString(ErrorObject, "can't determine format"); + alFreeConfig(c); + return NULL; + } + ch = alGetChannels(c); + alFreeConfig(c); + if (ch < 0) { + PyErr_SetString(ErrorObject, "can't determine # of channels"); + return NULL; + } + size *= ch; + if (length % size != 0) { + PyErr_SetString(ErrorObject, + "buffer length not whole number of frames"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + alWriteFrames(self->port, (void *) samples, length / size); + Py_END_ALLOW_THREADS + + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(alp_ClosePort__doc__, "alClosePort: close an audio port."); + +static PyObject * +alp_ClosePort(alpobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":ClosePort")) + return NULL; + if (alClosePort(self->port) < 0) + return NULL; + self->port = NULL; + Py_INCREF(Py_None); + return Py_None; +} + +#endif /* AL_NO_ELEM */ + +#ifdef OLD_INTERFACE +static PyObject * +alp_closeport(alpobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":ClosePort")) + return NULL; + if (ALcloseport(self->port) < 0) + return NULL; + self->port = NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +alp_getfd(alpobject *self, PyObject *args) +{ + int fd; + + if (!PyArg_ParseTuple(args, ":GetFD")) + return NULL; + if ((fd = ALgetfd(self-> port)) == -1) + return NULL; + return PyInt_FromLong(fd); +} + +static PyObject * +alp_getfilled(alpobject *self, PyObject *args) +{ + long count; + + if (!PyArg_ParseTuple(args, ":GetFilled")) + return NULL; + if ((count = ALgetfilled(self-> port)) == -1) + return NULL; + return PyInt_FromLong(count); +} + +static PyObject * +alp_getfillable(alpobject *self, PyObject *args) +{ + long count; + + if (!PyArg_ParseTuple(args, ":GetFillable")) + return NULL; + if ((count = ALgetfillable(self-> port)) == -1) + return NULL; + return PyInt_FromLong (count); +} + +static PyObject * +alp_readsamps(alpobject *self, PyObject *args) +{ + long count; + PyObject *v; + ALconfig c; + int width; + int ret; + + if (!PyArg_ParseTuple(args, "l:readsamps", &count)) + return NULL; + + if (count <= 0) { + PyErr_SetString(ErrorObject, "al.readsamps : arg <= 0"); + return NULL; + } + + c = ALgetconfig(self->port); +#ifdef AL_405 + width = ALgetsampfmt(c); + if (width == AL_SAMPFMT_FLOAT) + width = sizeof(float); + else if (width == AL_SAMPFMT_DOUBLE) + width = sizeof(double); + else + width = ALgetwidth(c); +#else + width = ALgetwidth(c); +#endif /* AL_405 */ + ALfreeconfig(c); + v = PyString_FromStringAndSize((char *)NULL, width * count); + if (v == NULL) + return NULL; + + Py_BEGIN_ALLOW_THREADS + ret = ALreadsamps(self->port, (void *) PyString_AsString(v), count); + Py_END_ALLOW_THREADS + if (ret == -1) { + Py_DECREF(v); + return NULL; + } + + return (v); +} + +static PyObject * +alp_writesamps(alpobject *self, PyObject *args) +{ + char *buf; + int size, width; + ALconfig c; + int ret; + + if (!PyArg_ParseTuple(args, "s#:writesamps", &buf, &size)) + return NULL; + + c = ALgetconfig(self->port); +#ifdef AL_405 + width = ALgetsampfmt(c); + if (width == AL_SAMPFMT_FLOAT) + width = sizeof(float); + else if (width == AL_SAMPFMT_DOUBLE) + width = sizeof(double); + else + width = ALgetwidth(c); +#else + width = ALgetwidth(c); +#endif /* AL_405 */ + ALfreeconfig(c); + Py_BEGIN_ALLOW_THREADS + ret = ALwritesamps (self->port, (void *) buf, (long) size / width); + Py_END_ALLOW_THREADS + if (ret == -1) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +alp_getfillpoint(alpobject *self, PyObject *args) +{ + long count; + + if (!PyArg_ParseTuple(args, ":GetFillPoint")) + return NULL; + if ((count = ALgetfillpoint(self->port)) == -1) + return NULL; + return PyInt_FromLong(count); +} + +static PyObject * +alp_setfillpoint(alpobject *self, PyObject *args) +{ + long count; + + if (!PyArg_ParseTuple(args, "l:SetFillPoint", &count)) + return NULL; + if (ALsetfillpoint(self->port, count) == -1) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +alp_setconfig(alpobject *self, PyObject *args) +{ + alcobject *config; + + if (!PyArg_ParseTuple(args, "O!:SetConfig", &Alctype, &config)) + return NULL; + if (ALsetconfig(self->port, config->config) == -1) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +alp_getconfig(alpobject *self, PyObject *args) +{ + ALconfig config; + + if (!PyArg_ParseTuple(args, ":GetConfig")) + return NULL; + config = ALgetconfig(self->port); + if (config == NULL) + return NULL; + return newalcobject(config); +} + +#ifdef AL_405 +static PyObject * +alp_getstatus(alpobject *self, PyObject *args) +{ + PyObject *list, *v; + long *PVbuffer; + long length; + int i; + + if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &list)) + return NULL; + length = PyList_Size(list); + PVbuffer = PyMem_NEW(long, length); + if (PVbuffer == NULL) + return PyErr_NoMemory(); + for (i = 0; i < length; i++) { + v = PyList_GetItem(list, i); + if (!PyInt_Check(v)) { + PyMem_DEL(PVbuffer); + PyErr_BadArgument(); + return NULL; + } + PVbuffer[i] = PyInt_AsLong(v); + } + + if (ALgetstatus(self->port, PVbuffer, length) == -1) + return NULL; + + for (i = 0; i < length; i++) + PyList_SetItem(list, i, PyInt_FromLong(PVbuffer[i])); + + PyMem_DEL(PVbuffer); + + Py_INCREF(Py_None); + return Py_None; +} +#endif /* AL_405 */ + +#endif /* OLD_INTERFACE */ + +static struct PyMethodDef alp_methods[] = { +#ifdef AL_NO_ELEM /* IRIX 6 */ + {"SetConfig", (PyCFunction)alp_SetConfig, METH_VARARGS, alp_SetConfig__doc__}, + {"GetConfig", (PyCFunction)alp_GetConfig, METH_VARARGS, alp_GetConfig__doc__}, + {"GetResource", (PyCFunction)alp_GetResource, METH_VARARGS, alp_GetResource__doc__}, + {"GetFD", (PyCFunction)alp_GetFD, METH_VARARGS, alp_GetFD__doc__}, + {"GetFilled", (PyCFunction)alp_GetFilled, METH_VARARGS, alp_GetFilled__doc__}, + {"GetFillable", (PyCFunction)alp_GetFillable, METH_VARARGS, alp_GetFillable__doc__}, + {"ReadFrames", (PyCFunction)alp_ReadFrames, METH_VARARGS, alp_ReadFrames__doc__}, + {"DiscardFrames", (PyCFunction)alp_DiscardFrames, METH_VARARGS, alp_DiscardFrames__doc__}, + {"ZeroFrames", (PyCFunction)alp_ZeroFrames, METH_VARARGS, alp_ZeroFrames__doc__}, + {"SetFillPoint", (PyCFunction)alp_SetFillPoint, METH_VARARGS, alp_SetFillPoint__doc__}, + {"GetFillPoint", (PyCFunction)alp_GetFillPoint, METH_VARARGS, alp_GetFillPoint__doc__}, + {"GetFrameNumber", (PyCFunction)alp_GetFrameNumber, METH_VARARGS, alp_GetFrameNumber__doc__}, + {"GetFrameTime", (PyCFunction)alp_GetFrameTime, METH_VARARGS, alp_GetFrameTime__doc__}, + {"WriteFrames", (PyCFunction)alp_WriteFrames, METH_VARARGS, alp_WriteFrames__doc__}, + {"ClosePort", (PyCFunction)alp_ClosePort, METH_VARARGS, alp_ClosePort__doc__}, +#endif /* AL_NO_ELEM */ +#ifdef OLD_INTERFACE + {"closeport", (PyCFunction)alp_closeport, METH_VARARGS}, + {"getfd", (PyCFunction)alp_getfd, METH_VARARGS}, + {"fileno", (PyCFunction)alp_getfd, METH_VARARGS}, + {"getfilled", (PyCFunction)alp_getfilled, METH_VARARGS}, + {"getfillable", (PyCFunction)alp_getfillable, METH_VARARGS}, + {"readsamps", (PyCFunction)alp_readsamps, METH_VARARGS}, + {"writesamps", (PyCFunction)alp_writesamps, METH_VARARGS}, + {"setfillpoint", (PyCFunction)alp_setfillpoint, METH_VARARGS}, + {"getfillpoint", (PyCFunction)alp_getfillpoint, METH_VARARGS}, + {"setconfig", (PyCFunction)alp_setconfig, METH_VARARGS}, + {"getconfig", (PyCFunction)alp_getconfig, METH_VARARGS}, +#ifdef AL_405 + {"getstatus", (PyCFunction)alp_getstatus, METH_VARARGS}, +#endif /* AL_405 */ +#endif /* OLD_INTERFACE */ + + {NULL, NULL} /* sentinel */ +}; + +/* ---------- */ + + +static PyObject * +newalpobject(ALport port) +{ + alpobject *self; + + self = PyObject_New(alpobject, &Alptype); + if (self == NULL) + return NULL; + /* XXXX Add your own initializers here */ + self->port = port; + return (PyObject *) self; +} + + +static void +alp_dealloc(alpobject *self) +{ + /* XXXX Add your own cleanup code here */ + if (self->port) { +#ifdef AL_NO_ELEM /* IRIX 6 */ + alClosePort(self->port); +#else + ALcloseport(self->port); +#endif + } + PyObject_Del(self); +} + +static PyObject * +alp_getattr(alpobject *self, char *name) +{ + /* XXXX Add your own getattr code here */ + if (self->port == NULL) { + PyErr_SetString(ErrorObject, "port already closed"); + return NULL; + } + return Py_FindMethod(alp_methods, (PyObject *)self, name); +} + +PyDoc_STRVAR(Alptype__doc__, ""); + +static PyTypeObject Alptype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "al.port", /*tp_name*/ + sizeof(alpobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)alp_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)alp_getattr, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + Alptype__doc__ /* Documentation string */ +}; + +/* End of code for port objects */ +/* -------------------------------------------------------- */ + + +#ifdef AL_NO_ELEM /* IRIX 6 */ + +PyDoc_STRVAR(al_NewConfig__doc__, +"alNewConfig: create and initialize an audio ALconfig structure."); + +static PyObject * +al_NewConfig(PyObject *self, PyObject *args) +{ + ALconfig config; + + if (!PyArg_ParseTuple(args, ":NewConfig")) + return NULL; + if ((config = alNewConfig()) == NULL) + return NULL; + return newalcobject(config); +} + +PyDoc_STRVAR(al_OpenPort__doc__, +"alOpenPort: open an audio port."); + +static PyObject * +al_OpenPort(PyObject *self, PyObject *args) +{ + ALport port; + char *name, *dir; + alcobject *config = NULL; + + if (!PyArg_ParseTuple(args, "ss|O!:OpenPort", &name, &dir, &Alctype, &config)) + return NULL; + if ((port = alOpenPort(name, dir, config ? config->config : NULL)) == NULL) + return NULL; + return newalpobject(port); +} + +PyDoc_STRVAR(al_Connect__doc__, +"alConnect: connect two audio I/O resources."); + +static PyObject * +al_Connect(PyObject *self, PyObject *args) +{ + int source, dest, nprops = 0, id, i; + ALpv *props = NULL; + ALparamInfo *propinfo = NULL; + PyObject *propobj = NULL; + + if (!PyArg_ParseTuple(args, "ii|O!:Connect", &source, &dest, &PyList_Type, &propobj)) + return NULL; + if (propobj != NULL) { + nprops = python2params(source, dest, propobj, &props, &propinfo); + if (nprops < 0) + return NULL; + } + + id = alConnect(source, dest, props, nprops); + + if (props) { + for (i = 0; i < nprops; i++) { + switch (propinfo[i].valueType) { + case AL_SET_VAL: + case AL_VECTOR_VAL: + PyMem_DEL(props[i].value.ptr); + break; + } + } + PyMem_DEL(props); + PyMem_DEL(propinfo); + } + + if (id < 0) + return NULL; + return PyInt_FromLong((long) id); +} + +PyDoc_STRVAR(al_Disconnect__doc__, +"alDisconnect: delete a connection between two audio I/O resources."); + +static PyObject * +al_Disconnect(PyObject *self, PyObject *args) +{ + int res; + + if (!PyArg_ParseTuple(args, "i:Disconnect", &res)) + return NULL; + if (alDisconnect(res) < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(al_GetParams__doc__, +"alGetParams: get the values of audio resource parameters."); + +static PyObject * +al_GetParams(PyObject *self, PyObject *args) +{ + int resource; + PyObject *pvslist, *item = NULL, *v = NULL; + ALpv *pvs; + int i, j, npvs; + ALparamInfo *pinfo; + + if (!PyArg_ParseTuple(args, "iO!:GetParams", &resource, &PyList_Type, &pvslist)) + return NULL; + npvs = PyList_Size(pvslist); + pvs = PyMem_NEW(ALpv, npvs); + pinfo = PyMem_NEW(ALparamInfo, npvs); + for (i = 0; i < npvs; i++) { + item = PyList_GetItem(pvslist, i); + if (!PyInt_Check(item)) { + item = NULL; + PyErr_SetString(ErrorObject, "list of integers expected"); + goto error; + } + pvs[i].param = (int) PyInt_AsLong(item); + item = NULL; /* not needed anymore */ + if (alGetParamInfo(resource, pvs[i].param, &pinfo[i]) < 0) + goto error; + switch (pinfo[i].valueType) { + case AL_NO_VAL: + break; + case AL_MATRIX_VAL: + pinfo[i].maxElems *= pinfo[i].maxElems2; + /* fall through */ + case AL_STRING_VAL: + case AL_SET_VAL: + case AL_VECTOR_VAL: + switch (pinfo[i].elementType) { + case AL_INT32_ELEM: + case AL_RESOURCE_ELEM: + case AL_ENUM_ELEM: + pvs[i].value.ptr = PyMem_NEW(int, pinfo[i].maxElems); + pvs[i].sizeIn = pinfo[i].maxElems; + break; + case AL_INT64_ELEM: + case AL_FIXED_ELEM: + pvs[i].value.ptr = PyMem_NEW(long long, pinfo[i].maxElems); + pvs[i].sizeIn = pinfo[i].maxElems; + break; + case AL_CHAR_ELEM: + pvs[i].value.ptr = PyMem_NEW(char, 32); + pvs[i].sizeIn = 32; + break; + case AL_NO_ELEM: + case AL_PTR_ELEM: + default: + PyErr_SetString(ErrorObject, "internal error"); + goto error; + } + break; + case AL_SCALAR_VAL: + break; + default: + PyErr_SetString(ErrorObject, "internal error"); + goto error; + } + if (pinfo[i].valueType == AL_MATRIX_VAL) { + pinfo[i].maxElems /= pinfo[i].maxElems2; + pvs[i].sizeIn /= pinfo[i].maxElems2; + pvs[i].size2In = pinfo[i].maxElems2; + } + } + if (alGetParams(resource, pvs, npvs) < 0) + goto error; + v = PyList_New(npvs); + for (i = 0; i < npvs; i++) { + if (pvs[i].sizeOut < 0) { + char buf[32]; + PyOS_snprintf(buf, sizeof(buf), + "problem with param %d", i); + PyErr_SetString(ErrorObject, buf); + goto error; + } + switch (pinfo[i].valueType) { + case AL_NO_VAL: + item = Py_None; + Py_INCREF(item); + break; + case AL_STRING_VAL: + item = PyString_FromString(pvs[i].value.ptr); + PyMem_DEL(pvs[i].value.ptr); + break; + case AL_MATRIX_VAL: + /* XXXX this is not right */ + pvs[i].sizeOut *= pvs[i].size2Out; + /* fall through */ + case AL_SET_VAL: + case AL_VECTOR_VAL: + item = PyList_New(pvs[i].sizeOut); + for (j = 0; j < pvs[i].sizeOut; j++) { + switch (pinfo[i].elementType) { + case AL_INT32_ELEM: + case AL_RESOURCE_ELEM: + case AL_ENUM_ELEM: + PyList_SetItem(item, j, PyInt_FromLong((long) ((int *) pvs[i].value.ptr)[j])); + break; + case AL_INT64_ELEM: + PyList_SetItem(item, j, PyLong_FromLongLong(((long long *) pvs[i].value.ptr)[j])); + break; + case AL_FIXED_ELEM: + PyList_SetItem(item, j, PyFloat_FromDouble(alFixedToDouble(((long long *) pvs[i].value.ptr)[j]))); + break; + default: + PyErr_SetString(ErrorObject, "internal error"); + goto error; + } + } + PyMem_DEL(pvs[i].value.ptr); + break; + case AL_SCALAR_VAL: + item = param2python(resource, pvs[i].param, pvs[i].value, &pinfo[i]); + break; + } + if (PyErr_Occurred() || + PyList_SetItem(v, i, Py_BuildValue("(iO)", pvs[i].param, + item)) < 0 || + PyErr_Occurred()) + goto error; + Py_DECREF(item); + } + PyMem_DEL(pvs); + PyMem_DEL(pinfo); + return v; + + error: + Py_XDECREF(v); + Py_XDECREF(item); + if (pvs) + PyMem_DEL(pvs); + if (pinfo) + PyMem_DEL(pinfo); + return NULL; +} + +PyDoc_STRVAR(al_SetParams__doc__, +"alSetParams: set the values of audio resource parameters."); + +static PyObject * +al_SetParams(PyObject *self, PyObject *args) +{ + int resource; + PyObject *pvslist; + ALpv *pvs; + ALparamInfo *pinfo; + int npvs, i; + + if (!PyArg_ParseTuple(args, "iO!:SetParams", &resource, &PyList_Type, &pvslist)) + return NULL; + npvs = python2params(resource, -1, pvslist, &pvs, &pinfo); + if (npvs < 0) + return NULL; + + if (alSetParams(resource, pvs, npvs) < 0) + goto error; + + /* cleanup */ + for (i = 0; i < npvs; i++) { + switch (pinfo[i].valueType) { + case AL_SET_VAL: + case AL_VECTOR_VAL: + PyMem_DEL(pvs[i].value.ptr); + break; + } + } + PyMem_DEL(pvs); + PyMem_DEL(pinfo); + + Py_INCREF(Py_None); + return Py_None; + + error: + /* XXXX we should clean up everything */ + if (pvs) + PyMem_DEL(pvs); + if (pinfo) + PyMem_DEL(pinfo); + return NULL; +} + +PyDoc_STRVAR(al_QueryValues__doc__, +"alQueryValues: get the set of possible values for a parameter."); + +static PyObject * +al_QueryValues(PyObject *self, PyObject *args) +{ + int resource, param; + ALvalue *return_set = NULL; + int setsize = 32, qualsize = 0, nvals, i; + ALpv *quals = NULL; + ALparamInfo pinfo; + ALparamInfo *qualinfo = NULL; + PyObject *qualobj = NULL; + PyObject *res = NULL, *item; + + if (!PyArg_ParseTuple(args, "ii|O!:QueryValues", &resource, ¶m, + &PyList_Type, &qualobj)) + return NULL; + if (qualobj != NULL) { + qualsize = python2params(resource, param, qualobj, &quals, &qualinfo); + if (qualsize < 0) + return NULL; + } + setsize = 32; + return_set = PyMem_NEW(ALvalue, setsize); + if (return_set == NULL) { + PyErr_NoMemory(); + goto cleanup; + } + + retry: + nvals = alQueryValues(resource, param, return_set, setsize, quals, qualsize); + if (nvals < 0) + goto cleanup; + if (nvals > setsize) { + setsize = nvals; + PyMem_RESIZE(return_set, ALvalue, setsize); + if (return_set == NULL) { + PyErr_NoMemory(); + goto cleanup; + } + goto retry; + } + + if (alGetParamInfo(resource, param, &pinfo) < 0) + goto cleanup; + + res = PyList_New(nvals); + if (res == NULL) + goto cleanup; + for (i = 0; i < nvals; i++) { + item = param2python(resource, param, return_set[i], &pinfo); + if (item == NULL || + PyList_SetItem(res, i, item) < 0) { + Py_DECREF(res); + res = NULL; + goto cleanup; + } + } + + cleanup: + if (return_set) + PyMem_DEL(return_set); + if (quals) { + for (i = 0; i < qualsize; i++) { + switch (qualinfo[i].valueType) { + case AL_SET_VAL: + case AL_VECTOR_VAL: + PyMem_DEL(quals[i].value.ptr); + break; + } + } + PyMem_DEL(quals); + PyMem_DEL(qualinfo); + } + + return res; +} + +PyDoc_STRVAR(al_GetParamInfo__doc__, +"alGetParamInfo: get information about a parameter on " +"a particular audio resource."); + +static PyObject * +al_GetParamInfo(PyObject *self, PyObject *args) +{ + int res, param; + ALparamInfo pinfo; + PyObject *v, *item;; + + if (!PyArg_ParseTuple(args, "ii:GetParamInfo", &res, ¶m)) + return NULL; + if (alGetParamInfo(res, param, &pinfo) < 0) + return NULL; + v = PyDict_New(); + + item = PyInt_FromLong((long) pinfo.resource); + PyDict_SetItemString(v, "resource", item); + Py_DECREF(item); + + item = PyInt_FromLong((long) pinfo.param); + PyDict_SetItemString(v, "param", item); + Py_DECREF(item); + + item = PyInt_FromLong((long) pinfo.valueType); + PyDict_SetItemString(v, "valueType", item); + Py_DECREF(item); + + if (pinfo.valueType != AL_NO_VAL && pinfo.valueType != AL_SCALAR_VAL) { + /* multiple values */ + item = PyInt_FromLong((long) pinfo.maxElems); + PyDict_SetItemString(v, "maxElems", item); + Py_DECREF(item); + + if (pinfo.valueType == AL_MATRIX_VAL) { + /* 2 dimensional */ + item = PyInt_FromLong((long) pinfo.maxElems2); + PyDict_SetItemString(v, "maxElems2", item); + Py_DECREF(item); + } + } + + item = PyInt_FromLong((long) pinfo.elementType); + PyDict_SetItemString(v, "elementType", item); + Py_DECREF(item); + + item = PyString_FromString(pinfo.name); + PyDict_SetItemString(v, "name", item); + Py_DECREF(item); + + item = param2python(res, param, pinfo.initial, &pinfo); + PyDict_SetItemString(v, "initial", item); + Py_DECREF(item); + + if (pinfo.elementType != AL_ENUM_ELEM && + pinfo.elementType != AL_RESOURCE_ELEM && + pinfo.elementType != AL_CHAR_ELEM) { + /* range param */ + item = param2python(res, param, pinfo.min, &pinfo); + PyDict_SetItemString(v, "min", item); + Py_DECREF(item); + + item = param2python(res, param, pinfo.max, &pinfo); + PyDict_SetItemString(v, "max", item); + Py_DECREF(item); + + item = param2python(res, param, pinfo.minDelta, &pinfo); + PyDict_SetItemString(v, "minDelta", item); + Py_DECREF(item); + + item = param2python(res, param, pinfo.maxDelta, &pinfo); + PyDict_SetItemString(v, "maxDelta", item); + Py_DECREF(item); + + item = PyInt_FromLong((long) pinfo.specialVals); + PyDict_SetItemString(v, "specialVals", item); + Py_DECREF(item); + } + + return v; +} + +PyDoc_STRVAR(al_GetResourceByName__doc__, +"alGetResourceByName: find an audio resource by name."); + +static PyObject * +al_GetResourceByName(PyObject *self, PyObject *args) +{ + int res, start_res, type; + char *name; + + if (!PyArg_ParseTuple(args, "isi:GetResourceByName", &start_res, &name, &type)) + return NULL; + if ((res = alGetResourceByName(start_res, name, type)) == 0) + return NULL; + return PyInt_FromLong((long) res); +} + +PyDoc_STRVAR(al_IsSubtype__doc__, +"alIsSubtype: indicate if one resource type is a subtype of another."); + +static PyObject * +al_IsSubtype(PyObject *self, PyObject *args) +{ + int type, subtype; + + if (!PyArg_ParseTuple(args, "ii:IsSubtype", &type, &subtype)) + return NULL; + return PyInt_FromLong((long) alIsSubtype(type, subtype)); +} + +PyDoc_STRVAR(al_SetErrorHandler__doc__, ""); + +static PyObject * +al_SetErrorHandler(PyObject *self, PyObject *args) +{ + + if (!PyArg_ParseTuple(args, ":SetErrorHandler")) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +#endif /* AL_NO_ELEM */ + +#ifdef OLD_INTERFACE + +static PyObject * +al_openport(PyObject *self, PyObject *args) +{ + char *name, *dir; + ALport port; + alcobject *config = NULL; + + if (!PyArg_ParseTuple(args, "ss|O!:OpenPort", &name, &dir, &Alctype, &config)) + return NULL; + if ((port = ALopenport(name, dir, config ? config->config : NULL)) == NULL) + return NULL; + return newalpobject(port); +} + +static PyObject * +al_newconfig(PyObject *self, PyObject *args) +{ + ALconfig config; + + if (!PyArg_ParseTuple(args, ":NewConfig")) + return NULL; + if ((config = ALnewconfig ()) == NULL) + return NULL; + return newalcobject(config); +} + +static PyObject * +al_queryparams(PyObject *self, PyObject *args) +{ + long device; + long length; + long *PVbuffer; + long PVdummy[2]; + PyObject *v = NULL; + int i; + + if (!PyArg_ParseTuple(args, "l:queryparams", &device)) + return NULL; + if ((length = ALqueryparams(device, PVdummy, 2L)) == -1) + return NULL; + if ((PVbuffer = PyMem_NEW(long, length)) == NULL) + return PyErr_NoMemory(); + if (ALqueryparams(device, PVbuffer, length) >= 0 && + (v = PyList_New((int)length)) != NULL) { + for (i = 0; i < length; i++) + PyList_SetItem(v, i, PyInt_FromLong(PVbuffer[i])); + } + PyMem_DEL(PVbuffer); + return v; +} + +static PyObject * +doParams(PyObject *args, int (*func)(long, long *, long), int modified) +{ + long device; + PyObject *list, *v; + long *PVbuffer; + long length; + int i; + + if (!PyArg_ParseTuple(args, "lO!", &device, &PyList_Type, &list)) + return NULL; + length = PyList_Size(list); + PVbuffer = PyMem_NEW(long, length); + if (PVbuffer == NULL) + return PyErr_NoMemory(); + for (i = 0; i < length; i++) { + v = PyList_GetItem(list, i); + if (!PyInt_Check(v)) { + PyMem_DEL(PVbuffer); + PyErr_BadArgument(); + return NULL; + } + PVbuffer[i] = PyInt_AsLong(v); + } + + if ((*func)(device, PVbuffer, length) == -1) { + PyMem_DEL(PVbuffer); + return NULL; + } + + if (modified) { + for (i = 0; i < length; i++) + PyList_SetItem(list, i, PyInt_FromLong(PVbuffer[i])); + } + + PyMem_DEL(PVbuffer); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +al_getparams(PyObject *self, PyObject *args) +{ + return doParams(args, ALgetparams, 1); +} + +static PyObject * +al_setparams(PyObject *self, PyObject *args) +{ + return doParams(args, ALsetparams, 0); +} + +static PyObject * +al_getname(PyObject *self, PyObject *args) +{ + long device, descriptor; + char *name; + + if (!PyArg_ParseTuple(args, "ll:getname", &device, &descriptor)) + return NULL; + if ((name = ALgetname(device, descriptor)) == NULL) + return NULL; + return PyString_FromString(name); +} + +static PyObject * +al_getdefault(PyObject *self, PyObject *args) +{ + long device, descriptor, value; + + if (!PyArg_ParseTuple(args, "ll:getdefault", &device, &descriptor)) + return NULL; + if ((value = ALgetdefault(device, descriptor)) == -1) + return NULL; + return PyLong_FromLong(value); +} + +static PyObject * +al_getminmax(PyObject *self, PyObject *args) +{ + long device, descriptor, min, max; + + if (!PyArg_ParseTuple(args, "ll:getminmax", &device, &descriptor)) + return NULL; + min = -1; + max = -1; + if (ALgetminmax(device, descriptor, &min, &max) == -1) + return NULL; + return Py_BuildValue("ll", min, max); +} + +#endif /* OLD_INTERFACE */ + +/* List of methods defined in the module */ + +static struct PyMethodDef al_methods[] = { +#ifdef AL_NO_ELEM /* IRIX 6 */ + {"NewConfig", (PyCFunction)al_NewConfig, METH_VARARGS, al_NewConfig__doc__}, + {"OpenPort", (PyCFunction)al_OpenPort, METH_VARARGS, al_OpenPort__doc__}, + {"Connect", (PyCFunction)al_Connect, METH_VARARGS, al_Connect__doc__}, + {"Disconnect", (PyCFunction)al_Disconnect, METH_VARARGS, al_Disconnect__doc__}, + {"GetParams", (PyCFunction)al_GetParams, METH_VARARGS, al_GetParams__doc__}, + {"SetParams", (PyCFunction)al_SetParams, METH_VARARGS, al_SetParams__doc__}, + {"QueryValues", (PyCFunction)al_QueryValues, METH_VARARGS, al_QueryValues__doc__}, + {"GetParamInfo", (PyCFunction)al_GetParamInfo, METH_VARARGS, al_GetParamInfo__doc__}, + {"GetResourceByName", (PyCFunction)al_GetResourceByName, METH_VARARGS, al_GetResourceByName__doc__}, + {"IsSubtype", (PyCFunction)al_IsSubtype, METH_VARARGS, al_IsSubtype__doc__}, +#if 0 + /* this one not supported */ + {"SetErrorHandler", (PyCFunction)al_SetErrorHandler, METH_VARARGS, al_SetErrorHandler__doc__}, +#endif +#endif /* AL_NO_ELEM */ +#ifdef OLD_INTERFACE + {"openport", (PyCFunction)al_openport, METH_VARARGS}, + {"newconfig", (PyCFunction)al_newconfig, METH_VARARGS}, + {"queryparams", (PyCFunction)al_queryparams, METH_VARARGS}, + {"getparams", (PyCFunction)al_getparams, METH_VARARGS}, + {"setparams", (PyCFunction)al_setparams, METH_VARARGS}, + {"getname", (PyCFunction)al_getname, METH_VARARGS}, + {"getdefault", (PyCFunction)al_getdefault, METH_VARARGS}, + {"getminmax", (PyCFunction)al_getminmax, METH_VARARGS}, +#endif /* OLD_INTERFACE */ + + {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ +}; + + +/* Initialization function for the module (*must* be called inital) */ + +PyDoc_STRVAR(al_module_documentation, ""); + +void +inital(void) +{ + PyObject *m, *d, *x; + + /* Create the module and add the functions */ + m = Py_InitModule4("al", al_methods, + al_module_documentation, + (PyObject*)NULL,PYTHON_API_VERSION); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + ErrorObject = PyErr_NewException("al.error", NULL, NULL); + PyDict_SetItemString(d, "error", ErrorObject); + + /* XXXX Add constants here */ +#ifdef AL_4CHANNEL + x = PyInt_FromLong((long) AL_4CHANNEL); + if (x == NULL || PyDict_SetItemString(d, "FOURCHANNEL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ADAT_IF_TYPE + x = PyInt_FromLong((long) AL_ADAT_IF_TYPE); + if (x == NULL || PyDict_SetItemString(d, "ADAT_IF_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ADAT_MCLK_TYPE + x = PyInt_FromLong((long) AL_ADAT_MCLK_TYPE); + if (x == NULL || PyDict_SetItemString(d, "ADAT_MCLK_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_AES_IF_TYPE + x = PyInt_FromLong((long) AL_AES_IF_TYPE); + if (x == NULL || PyDict_SetItemString(d, "AES_IF_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_AES_MCLK_TYPE + x = PyInt_FromLong((long) AL_AES_MCLK_TYPE); + if (x == NULL || PyDict_SetItemString(d, "AES_MCLK_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ANALOG_IF_TYPE + x = PyInt_FromLong((long) AL_ANALOG_IF_TYPE); + if (x == NULL || PyDict_SetItemString(d, "ANALOG_IF_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ASSOCIATE + x = PyInt_FromLong((long) AL_ASSOCIATE); + if (x == NULL || PyDict_SetItemString(d, "ASSOCIATE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_BUFFER_NULL + x = PyInt_FromLong((long) AL_BAD_BUFFER_NULL); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFER_NULL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_BUFFERLENGTH + x = PyInt_FromLong((long) AL_BAD_BUFFERLENGTH); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFERLENGTH", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_BUFFERLENGTH_NEG + x = PyInt_FromLong((long) AL_BAD_BUFFERLENGTH_NEG); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFERLENGTH_NEG", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_BUFFERLENGTH_ODD + x = PyInt_FromLong((long) AL_BAD_BUFFERLENGTH_ODD); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFERLENGTH_ODD", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_CHANNELS + x = PyInt_FromLong((long) AL_BAD_CHANNELS); + if (x == NULL || PyDict_SetItemString(d, "BAD_CHANNELS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_CONFIG + x = PyInt_FromLong((long) AL_BAD_CONFIG); + if (x == NULL || PyDict_SetItemString(d, "BAD_CONFIG", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_COUNT_NEG + x = PyInt_FromLong((long) AL_BAD_COUNT_NEG); + if (x == NULL || PyDict_SetItemString(d, "BAD_COUNT_NEG", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_DEVICE + x = PyInt_FromLong((long) AL_BAD_DEVICE); + if (x == NULL || PyDict_SetItemString(d, "BAD_DEVICE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_DEVICE_ACCESS + x = PyInt_FromLong((long) AL_BAD_DEVICE_ACCESS); + if (x == NULL || PyDict_SetItemString(d, "BAD_DEVICE_ACCESS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_DIRECTION + x = PyInt_FromLong((long) AL_BAD_DIRECTION); + if (x == NULL || PyDict_SetItemString(d, "BAD_DIRECTION", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_FILLPOINT + x = PyInt_FromLong((long) AL_BAD_FILLPOINT); + if (x == NULL || PyDict_SetItemString(d, "BAD_FILLPOINT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_FLOATMAX + x = PyInt_FromLong((long) AL_BAD_FLOATMAX); + if (x == NULL || PyDict_SetItemString(d, "BAD_FLOATMAX", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_ILLEGAL_STATE + x = PyInt_FromLong((long) AL_BAD_ILLEGAL_STATE); + if (x == NULL || PyDict_SetItemString(d, "BAD_ILLEGAL_STATE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_NO_PORTS + x = PyInt_FromLong((long) AL_BAD_NO_PORTS); + if (x == NULL || PyDict_SetItemString(d, "BAD_NO_PORTS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_NOT_FOUND + x = PyInt_FromLong((long) AL_BAD_NOT_FOUND); + if (x == NULL || PyDict_SetItemString(d, "BAD_NOT_FOUND", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_NOT_IMPLEMENTED + x = PyInt_FromLong((long) AL_BAD_NOT_IMPLEMENTED); + if (x == NULL || PyDict_SetItemString(d, "BAD_NOT_IMPLEMENTED", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_OUT_OF_MEM + x = PyInt_FromLong((long) AL_BAD_OUT_OF_MEM); + if (x == NULL || PyDict_SetItemString(d, "BAD_OUT_OF_MEM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_PARAM + x = PyInt_FromLong((long) AL_BAD_PARAM); + if (x == NULL || PyDict_SetItemString(d, "BAD_PARAM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_PERMISSIONS + x = PyInt_FromLong((long) AL_BAD_PERMISSIONS); + if (x == NULL || PyDict_SetItemString(d, "BAD_PERMISSIONS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_PORT + x = PyInt_FromLong((long) AL_BAD_PORT); + if (x == NULL || PyDict_SetItemString(d, "BAD_PORT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_PORTSTYLE + x = PyInt_FromLong((long) AL_BAD_PORTSTYLE); + if (x == NULL || PyDict_SetItemString(d, "BAD_PORTSTYLE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_PVBUFFER + x = PyInt_FromLong((long) AL_BAD_PVBUFFER); + if (x == NULL || PyDict_SetItemString(d, "BAD_PVBUFFER", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_QSIZE + x = PyInt_FromLong((long) AL_BAD_QSIZE); + if (x == NULL || PyDict_SetItemString(d, "BAD_QSIZE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_RATE + x = PyInt_FromLong((long) AL_BAD_RATE); + if (x == NULL || PyDict_SetItemString(d, "BAD_RATE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_RESOURCE + x = PyInt_FromLong((long) AL_BAD_RESOURCE); + if (x == NULL || PyDict_SetItemString(d, "BAD_RESOURCE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_SAMPFMT + x = PyInt_FromLong((long) AL_BAD_SAMPFMT); + if (x == NULL || PyDict_SetItemString(d, "BAD_SAMPFMT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_TRANSFER_SIZE + x = PyInt_FromLong((long) AL_BAD_TRANSFER_SIZE); + if (x == NULL || PyDict_SetItemString(d, "BAD_TRANSFER_SIZE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_BAD_WIDTH + x = PyInt_FromLong((long) AL_BAD_WIDTH); + if (x == NULL || PyDict_SetItemString(d, "BAD_WIDTH", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_CHANNEL_MODE + x = PyInt_FromLong((long) AL_CHANNEL_MODE); + if (x == NULL || PyDict_SetItemString(d, "CHANNEL_MODE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_CHANNELS + x = PyInt_FromLong((long) AL_CHANNELS); + if (x == NULL || PyDict_SetItemString(d, "CHANNELS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_CHAR_ELEM + x = PyInt_FromLong((long) AL_CHAR_ELEM); + if (x == NULL || PyDict_SetItemString(d, "CHAR_ELEM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_CLOCK_GEN + x = PyInt_FromLong((long) AL_CLOCK_GEN); + if (x == NULL || PyDict_SetItemString(d, "CLOCK_GEN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_CLOCKGEN_TYPE + x = PyInt_FromLong((long) AL_CLOCKGEN_TYPE); + if (x == NULL || PyDict_SetItemString(d, "CLOCKGEN_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_CONNECT + x = PyInt_FromLong((long) AL_CONNECT); + if (x == NULL || PyDict_SetItemString(d, "CONNECT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_CONNECTION_TYPE + x = PyInt_FromLong((long) AL_CONNECTION_TYPE); + if (x == NULL || PyDict_SetItemString(d, "CONNECTION_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_CONNECTIONS + x = PyInt_FromLong((long) AL_CONNECTIONS); + if (x == NULL || PyDict_SetItemString(d, "CONNECTIONS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_CRYSTAL_MCLK_TYPE + x = PyInt_FromLong((long) AL_CRYSTAL_MCLK_TYPE); + if (x == NULL || PyDict_SetItemString(d, "CRYSTAL_MCLK_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_DEFAULT_DEVICE + x = PyInt_FromLong((long) AL_DEFAULT_DEVICE); + if (x == NULL || PyDict_SetItemString(d, "DEFAULT_DEVICE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_DEFAULT_INPUT + x = PyInt_FromLong((long) AL_DEFAULT_INPUT); + if (x == NULL || PyDict_SetItemString(d, "DEFAULT_INPUT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_DEFAULT_OUTPUT + x = PyInt_FromLong((long) AL_DEFAULT_OUTPUT); + if (x == NULL || PyDict_SetItemString(d, "DEFAULT_OUTPUT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_DEST + x = PyInt_FromLong((long) AL_DEST); + if (x == NULL || PyDict_SetItemString(d, "DEST", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_DEVICE_TYPE + x = PyInt_FromLong((long) AL_DEVICE_TYPE); + if (x == NULL || PyDict_SetItemString(d, "DEVICE_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_DEVICES + x = PyInt_FromLong((long) AL_DEVICES); + if (x == NULL || PyDict_SetItemString(d, "DEVICES", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_DIGITAL_IF_TYPE + x = PyInt_FromLong((long) AL_DIGITAL_IF_TYPE); + if (x == NULL || PyDict_SetItemString(d, "DIGITAL_IF_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_DIGITAL_INPUT_RATE + x = PyInt_FromLong((long) AL_DIGITAL_INPUT_RATE); + if (x == NULL || PyDict_SetItemString(d, "DIGITAL_INPUT_RATE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_DISCONNECT + x = PyInt_FromLong((long) AL_DISCONNECT); + if (x == NULL || PyDict_SetItemString(d, "DISCONNECT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ENUM_ELEM + x = PyInt_FromLong((long) AL_ENUM_ELEM); + if (x == NULL || PyDict_SetItemString(d, "ENUM_ELEM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ENUM_VALUE + x = PyInt_FromLong((long) AL_ENUM_VALUE); + if (x == NULL || PyDict_SetItemString(d, "ENUM_VALUE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ERROR_INPUT_OVERFLOW + x = PyInt_FromLong((long) AL_ERROR_INPUT_OVERFLOW); + if (x == NULL || PyDict_SetItemString(d, "ERROR_INPUT_OVERFLOW", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ERROR_LENGTH + x = PyInt_FromLong((long) AL_ERROR_LENGTH); + if (x == NULL || PyDict_SetItemString(d, "ERROR_LENGTH", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ERROR_LOCATION_LSP + x = PyInt_FromLong((long) AL_ERROR_LOCATION_LSP); + if (x == NULL || PyDict_SetItemString(d, "ERROR_LOCATION_LSP", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ERROR_LOCATION_MSP + x = PyInt_FromLong((long) AL_ERROR_LOCATION_MSP); + if (x == NULL || PyDict_SetItemString(d, "ERROR_LOCATION_MSP", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ERROR_NUMBER + x = PyInt_FromLong((long) AL_ERROR_NUMBER); + if (x == NULL || PyDict_SetItemString(d, "ERROR_NUMBER", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ERROR_OUTPUT_UNDERFLOW + x = PyInt_FromLong((long) AL_ERROR_OUTPUT_UNDERFLOW); + if (x == NULL || PyDict_SetItemString(d, "ERROR_OUTPUT_UNDERFLOW", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_ERROR_TYPE + x = PyInt_FromLong((long) AL_ERROR_TYPE); + if (x == NULL || PyDict_SetItemString(d, "ERROR_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_FIXED_ELEM + x = PyInt_FromLong((long) AL_FIXED_ELEM); + if (x == NULL || PyDict_SetItemString(d, "FIXED_ELEM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_FIXED_MCLK_TYPE + x = PyInt_FromLong((long) AL_FIXED_MCLK_TYPE); + if (x == NULL || PyDict_SetItemString(d, "FIXED_MCLK_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_GAIN + x = PyInt_FromLong((long) AL_GAIN); + if (x == NULL || PyDict_SetItemString(d, "GAIN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_GAIN_REF + x = PyInt_FromLong((long) AL_GAIN_REF); + if (x == NULL || PyDict_SetItemString(d, "GAIN_REF", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_HRB_TYPE + x = PyInt_FromLong((long) AL_HRB_TYPE); + if (x == NULL || PyDict_SetItemString(d, "HRB_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INPUT_COUNT + x = PyInt_FromLong((long) AL_INPUT_COUNT); + if (x == NULL || PyDict_SetItemString(d, "INPUT_COUNT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INPUT_DEVICE_TYPE + x = PyInt_FromLong((long) AL_INPUT_DEVICE_TYPE); + if (x == NULL || PyDict_SetItemString(d, "INPUT_DEVICE_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INPUT_DIGITAL + x = PyInt_FromLong((long) AL_INPUT_DIGITAL); + if (x == NULL || PyDict_SetItemString(d, "INPUT_DIGITAL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INPUT_HRB_TYPE + x = PyInt_FromLong((long) AL_INPUT_HRB_TYPE); + if (x == NULL || PyDict_SetItemString(d, "INPUT_HRB_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INPUT_LINE + x = PyInt_FromLong((long) AL_INPUT_LINE); + if (x == NULL || PyDict_SetItemString(d, "INPUT_LINE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INPUT_MIC + x = PyInt_FromLong((long) AL_INPUT_MIC); + if (x == NULL || PyDict_SetItemString(d, "INPUT_MIC", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INPUT_PORT_TYPE + x = PyInt_FromLong((long) AL_INPUT_PORT_TYPE); + if (x == NULL || PyDict_SetItemString(d, "INPUT_PORT_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INPUT_RATE + x = PyInt_FromLong((long) AL_INPUT_RATE); + if (x == NULL || PyDict_SetItemString(d, "INPUT_RATE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INPUT_SOURCE + x = PyInt_FromLong((long) AL_INPUT_SOURCE); + if (x == NULL || PyDict_SetItemString(d, "INPUT_SOURCE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INT32_ELEM + x = PyInt_FromLong((long) AL_INT32_ELEM); + if (x == NULL || PyDict_SetItemString(d, "INT32_ELEM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INT64_ELEM + x = PyInt_FromLong((long) AL_INT64_ELEM); + if (x == NULL || PyDict_SetItemString(d, "INT64_ELEM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INTERFACE + x = PyInt_FromLong((long) AL_INTERFACE); + if (x == NULL || PyDict_SetItemString(d, "INTERFACE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INTERFACE_TYPE + x = PyInt_FromLong((long) AL_INTERFACE_TYPE); + if (x == NULL || PyDict_SetItemString(d, "INTERFACE_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INVALID_PARAM + x = PyInt_FromLong((long) AL_INVALID_PARAM); + if (x == NULL || PyDict_SetItemString(d, "INVALID_PARAM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_INVALID_VALUE + x = PyInt_FromLong((long) AL_INVALID_VALUE); + if (x == NULL || PyDict_SetItemString(d, "INVALID_VALUE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_JITTER + x = PyInt_FromLong((long) AL_JITTER); + if (x == NULL || PyDict_SetItemString(d, "JITTER", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_LABEL + x = PyInt_FromLong((long) AL_LABEL); + if (x == NULL || PyDict_SetItemString(d, "LABEL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_LEFT_INPUT_ATTEN + x = PyInt_FromLong((long) AL_LEFT_INPUT_ATTEN); + if (x == NULL || PyDict_SetItemString(d, "LEFT_INPUT_ATTEN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_LEFT_MONITOR_ATTEN + x = PyInt_FromLong((long) AL_LEFT_MONITOR_ATTEN); + if (x == NULL || PyDict_SetItemString(d, "LEFT_MONITOR_ATTEN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_LEFT_SPEAKER_GAIN + x = PyInt_FromLong((long) AL_LEFT_SPEAKER_GAIN); + if (x == NULL || PyDict_SetItemString(d, "LEFT_SPEAKER_GAIN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_LEFT1_INPUT_ATTEN + x = PyInt_FromLong((long) AL_LEFT1_INPUT_ATTEN); + if (x == NULL || PyDict_SetItemString(d, "LEFT1_INPUT_ATTEN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_LEFT2_INPUT_ATTEN + x = PyInt_FromLong((long) AL_LEFT2_INPUT_ATTEN); + if (x == NULL || PyDict_SetItemString(d, "LEFT2_INPUT_ATTEN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_LINE_IF_TYPE + x = PyInt_FromLong((long) AL_LINE_IF_TYPE); + if (x == NULL || PyDict_SetItemString(d, "LINE_IF_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_LOCKED + x = PyInt_FromLong((long) AL_LOCKED); + if (x == NULL || PyDict_SetItemString(d, "LOCKED", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MASTER_CLOCK + x = PyInt_FromLong((long) AL_MASTER_CLOCK); + if (x == NULL || PyDict_SetItemString(d, "MASTER_CLOCK", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MATRIX_VAL + x = PyInt_FromLong((long) AL_MATRIX_VAL); + if (x == NULL || PyDict_SetItemString(d, "MATRIX_VAL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MAX_ERROR + x = PyInt_FromLong((long) AL_MAX_ERROR); + if (x == NULL || PyDict_SetItemString(d, "MAX_ERROR", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MAX_EVENT_PARAM + x = PyInt_FromLong((long) AL_MAX_EVENT_PARAM); + if (x == NULL || PyDict_SetItemString(d, "MAX_EVENT_PARAM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MAX_PBUFSIZE + x = PyInt_FromLong((long) AL_MAX_PBUFSIZE); + if (x == NULL || PyDict_SetItemString(d, "MAX_PBUFSIZE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MAX_PORTS + x = PyInt_FromLong((long) AL_MAX_PORTS); + if (x == NULL || PyDict_SetItemString(d, "MAX_PORTS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MAX_RESOURCE_ID + x = PyInt_FromLong((long) AL_MAX_RESOURCE_ID); + if (x == NULL || PyDict_SetItemString(d, "MAX_RESOURCE_ID", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MAX_SETSIZE + x = PyInt_FromLong((long) AL_MAX_SETSIZE); + if (x == NULL || PyDict_SetItemString(d, "MAX_SETSIZE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MAX_STRLEN + x = PyInt_FromLong((long) AL_MAX_STRLEN); + if (x == NULL || PyDict_SetItemString(d, "MAX_STRLEN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MCLK_TYPE + x = PyInt_FromLong((long) AL_MCLK_TYPE); + if (x == NULL || PyDict_SetItemString(d, "MCLK_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MIC_IF_TYPE + x = PyInt_FromLong((long) AL_MIC_IF_TYPE); + if (x == NULL || PyDict_SetItemString(d, "MIC_IF_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MONITOR_CTL + x = PyInt_FromLong((long) AL_MONITOR_CTL); + if (x == NULL || PyDict_SetItemString(d, "MONITOR_CTL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MONITOR_OFF + x = PyInt_FromLong((long) AL_MONITOR_OFF); + if (x == NULL || PyDict_SetItemString(d, "MONITOR_OFF", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MONITOR_ON + x = PyInt_FromLong((long) AL_MONITOR_ON); + if (x == NULL || PyDict_SetItemString(d, "MONITOR_ON", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MONO + x = PyInt_FromLong((long) AL_MONO); + if (x == NULL || PyDict_SetItemString(d, "MONO", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_MUTE + x = PyInt_FromLong((long) AL_MUTE); + if (x == NULL || PyDict_SetItemString(d, "MUTE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_NAME + x = PyInt_FromLong((long) AL_NAME); + if (x == NULL || PyDict_SetItemString(d, "NAME", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_NEG_INFINITY + x = PyInt_FromLong((long) AL_NEG_INFINITY); + if (x == NULL || PyDict_SetItemString(d, "NEG_INFINITY", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_NEG_INFINITY_BIT + x = PyInt_FromLong((long) AL_NEG_INFINITY_BIT); + if (x == NULL || PyDict_SetItemString(d, "NEG_INFINITY_BIT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_NO_CHANGE + x = PyInt_FromLong((long) AL_NO_CHANGE); + if (x == NULL || PyDict_SetItemString(d, "NO_CHANGE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_NO_CHANGE_BIT + x = PyInt_FromLong((long) AL_NO_CHANGE_BIT); + if (x == NULL || PyDict_SetItemString(d, "NO_CHANGE_BIT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_NO_ELEM + x = PyInt_FromLong((long) AL_NO_ELEM); + if (x == NULL || PyDict_SetItemString(d, "NO_ELEM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_NO_ERRORS + x = PyInt_FromLong((long) AL_NO_ERRORS); + if (x == NULL || PyDict_SetItemString(d, "NO_ERRORS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_NO_OP + x = PyInt_FromLong((long) AL_NO_OP); + if (x == NULL || PyDict_SetItemString(d, "NO_OP", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_NO_VAL + x = PyInt_FromLong((long) AL_NO_VAL); + if (x == NULL || PyDict_SetItemString(d, "NO_VAL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_NULL_INTERFACE + x = PyInt_FromLong((long) AL_NULL_INTERFACE); + if (x == NULL || PyDict_SetItemString(d, "NULL_INTERFACE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_NULL_RESOURCE + x = PyInt_FromLong((long) AL_NULL_RESOURCE); + if (x == NULL || PyDict_SetItemString(d, "NULL_RESOURCE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_OPTICAL_IF_TYPE + x = PyInt_FromLong((long) AL_OPTICAL_IF_TYPE); + if (x == NULL || PyDict_SetItemString(d, "OPTICAL_IF_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_OUTPUT_COUNT + x = PyInt_FromLong((long) AL_OUTPUT_COUNT); + if (x == NULL || PyDict_SetItemString(d, "OUTPUT_COUNT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_OUTPUT_DEVICE_TYPE + x = PyInt_FromLong((long) AL_OUTPUT_DEVICE_TYPE); + if (x == NULL || PyDict_SetItemString(d, "OUTPUT_DEVICE_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_OUTPUT_HRB_TYPE + x = PyInt_FromLong((long) AL_OUTPUT_HRB_TYPE); + if (x == NULL || PyDict_SetItemString(d, "OUTPUT_HRB_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_OUTPUT_PORT_TYPE + x = PyInt_FromLong((long) AL_OUTPUT_PORT_TYPE); + if (x == NULL || PyDict_SetItemString(d, "OUTPUT_PORT_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_OUTPUT_RATE + x = PyInt_FromLong((long) AL_OUTPUT_RATE); + if (x == NULL || PyDict_SetItemString(d, "OUTPUT_RATE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_PARAM_BIT + x = PyInt_FromLong((long) AL_PARAM_BIT); + if (x == NULL || PyDict_SetItemString(d, "PARAM_BIT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_PARAMS + x = PyInt_FromLong((long) AL_PARAMS); + if (x == NULL || PyDict_SetItemString(d, "PARAMS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_PORT_COUNT + x = PyInt_FromLong((long) AL_PORT_COUNT); + if (x == NULL || PyDict_SetItemString(d, "PORT_COUNT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_PORT_TYPE + x = PyInt_FromLong((long) AL_PORT_TYPE); + if (x == NULL || PyDict_SetItemString(d, "PORT_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_PORTS + x = PyInt_FromLong((long) AL_PORTS); + if (x == NULL || PyDict_SetItemString(d, "PORTS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_PORTSTYLE_DIRECT + x = PyInt_FromLong((long) AL_PORTSTYLE_DIRECT); + if (x == NULL || PyDict_SetItemString(d, "PORTSTYLE_DIRECT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_PORTSTYLE_SERIAL + x = PyInt_FromLong((long) AL_PORTSTYLE_SERIAL); + if (x == NULL || PyDict_SetItemString(d, "PORTSTYLE_SERIAL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_PRINT_ERRORS + x = PyInt_FromLong((long) AL_PRINT_ERRORS); + if (x == NULL || PyDict_SetItemString(d, "PRINT_ERRORS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_PTR_ELEM + x = PyInt_FromLong((long) AL_PTR_ELEM); + if (x == NULL || PyDict_SetItemString(d, "PTR_ELEM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RANGE_VALUE + x = PyInt_FromLong((long) AL_RANGE_VALUE); + if (x == NULL || PyDict_SetItemString(d, "RANGE_VALUE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE + x = PyInt_FromLong((long) AL_RATE); + if (x == NULL || PyDict_SetItemString(d, "RATE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_11025 + x = PyInt_FromLong((long) AL_RATE_11025); + if (x == NULL || PyDict_SetItemString(d, "RATE_11025", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_16000 + x = PyInt_FromLong((long) AL_RATE_16000); + if (x == NULL || PyDict_SetItemString(d, "RATE_16000", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_22050 + x = PyInt_FromLong((long) AL_RATE_22050); + if (x == NULL || PyDict_SetItemString(d, "RATE_22050", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_32000 + x = PyInt_FromLong((long) AL_RATE_32000); + if (x == NULL || PyDict_SetItemString(d, "RATE_32000", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_44100 + x = PyInt_FromLong((long) AL_RATE_44100); + if (x == NULL || PyDict_SetItemString(d, "RATE_44100", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_48000 + x = PyInt_FromLong((long) AL_RATE_48000); + if (x == NULL || PyDict_SetItemString(d, "RATE_48000", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_8000 + x = PyInt_FromLong((long) AL_RATE_8000); + if (x == NULL || PyDict_SetItemString(d, "RATE_8000", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_AES_1 + x = PyInt_FromLong((long) AL_RATE_AES_1); + if (x == NULL || PyDict_SetItemString(d, "RATE_AES_1", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_AES_1s + x = PyInt_FromLong((long) AL_RATE_AES_1s); + if (x == NULL || PyDict_SetItemString(d, "RATE_AES_1s", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_AES_2 + x = PyInt_FromLong((long) AL_RATE_AES_2); + if (x == NULL || PyDict_SetItemString(d, "RATE_AES_2", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_AES_3 + x = PyInt_FromLong((long) AL_RATE_AES_3); + if (x == NULL || PyDict_SetItemString(d, "RATE_AES_3", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_AES_4 + x = PyInt_FromLong((long) AL_RATE_AES_4); + if (x == NULL || PyDict_SetItemString(d, "RATE_AES_4", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_AES_6 + x = PyInt_FromLong((long) AL_RATE_AES_6); + if (x == NULL || PyDict_SetItemString(d, "RATE_AES_6", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_FRACTION_D + x = PyInt_FromLong((long) AL_RATE_FRACTION_D); + if (x == NULL || PyDict_SetItemString(d, "RATE_FRACTION_D", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_FRACTION_N + x = PyInt_FromLong((long) AL_RATE_FRACTION_N); + if (x == NULL || PyDict_SetItemString(d, "RATE_FRACTION_N", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_INPUTRATE + x = PyInt_FromLong((long) AL_RATE_INPUTRATE); + if (x == NULL || PyDict_SetItemString(d, "RATE_INPUTRATE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_NO_DIGITAL_INPUT + x = PyInt_FromLong((long) AL_RATE_NO_DIGITAL_INPUT); + if (x == NULL || PyDict_SetItemString(d, "RATE_NO_DIGITAL_INPUT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_UNACQUIRED + x = PyInt_FromLong((long) AL_RATE_UNACQUIRED); + if (x == NULL || PyDict_SetItemString(d, "RATE_UNACQUIRED", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RATE_UNDEFINED + x = PyInt_FromLong((long) AL_RATE_UNDEFINED); + if (x == NULL || PyDict_SetItemString(d, "RATE_UNDEFINED", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_REF_0DBV + x = PyInt_FromLong((long) AL_REF_0DBV); + if (x == NULL || PyDict_SetItemString(d, "REF_0DBV", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_REF_NONE + x = PyInt_FromLong((long) AL_REF_NONE); + if (x == NULL || PyDict_SetItemString(d, "REF_NONE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RESERVED1_TYPE + x = PyInt_FromLong((long) AL_RESERVED1_TYPE); + if (x == NULL || PyDict_SetItemString(d, "RESERVED1_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RESERVED2_TYPE + x = PyInt_FromLong((long) AL_RESERVED2_TYPE); + if (x == NULL || PyDict_SetItemString(d, "RESERVED2_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RESERVED3_TYPE + x = PyInt_FromLong((long) AL_RESERVED3_TYPE); + if (x == NULL || PyDict_SetItemString(d, "RESERVED3_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RESERVED4_TYPE + x = PyInt_FromLong((long) AL_RESERVED4_TYPE); + if (x == NULL || PyDict_SetItemString(d, "RESERVED4_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RESOURCE + x = PyInt_FromLong((long) AL_RESOURCE); + if (x == NULL || PyDict_SetItemString(d, "RESOURCE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RESOURCE_ELEM + x = PyInt_FromLong((long) AL_RESOURCE_ELEM); + if (x == NULL || PyDict_SetItemString(d, "RESOURCE_ELEM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RESOURCE_TYPE + x = PyInt_FromLong((long) AL_RESOURCE_TYPE); + if (x == NULL || PyDict_SetItemString(d, "RESOURCE_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RIGHT_INPUT_ATTEN + x = PyInt_FromLong((long) AL_RIGHT_INPUT_ATTEN); + if (x == NULL || PyDict_SetItemString(d, "RIGHT_INPUT_ATTEN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RIGHT_MONITOR_ATTEN + x = PyInt_FromLong((long) AL_RIGHT_MONITOR_ATTEN); + if (x == NULL || PyDict_SetItemString(d, "RIGHT_MONITOR_ATTEN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RIGHT_SPEAKER_GAIN + x = PyInt_FromLong((long) AL_RIGHT_SPEAKER_GAIN); + if (x == NULL || PyDict_SetItemString(d, "RIGHT_SPEAKER_GAIN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RIGHT1_INPUT_ATTEN + x = PyInt_FromLong((long) AL_RIGHT1_INPUT_ATTEN); + if (x == NULL || PyDict_SetItemString(d, "RIGHT1_INPUT_ATTEN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_RIGHT2_INPUT_ATTEN + x = PyInt_FromLong((long) AL_RIGHT2_INPUT_ATTEN); + if (x == NULL || PyDict_SetItemString(d, "RIGHT2_INPUT_ATTEN", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SAMPFMT_DOUBLE + x = PyInt_FromLong((long) AL_SAMPFMT_DOUBLE); + if (x == NULL || PyDict_SetItemString(d, "SAMPFMT_DOUBLE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SAMPFMT_FLOAT + x = PyInt_FromLong((long) AL_SAMPFMT_FLOAT); + if (x == NULL || PyDict_SetItemString(d, "SAMPFMT_FLOAT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SAMPFMT_TWOSCOMP + x = PyInt_FromLong((long) AL_SAMPFMT_TWOSCOMP); + if (x == NULL || PyDict_SetItemString(d, "SAMPFMT_TWOSCOMP", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SAMPLE_16 + x = PyInt_FromLong((long) AL_SAMPLE_16); + if (x == NULL || PyDict_SetItemString(d, "SAMPLE_16", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SAMPLE_24 + x = PyInt_FromLong((long) AL_SAMPLE_24); + if (x == NULL || PyDict_SetItemString(d, "SAMPLE_24", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SAMPLE_8 + x = PyInt_FromLong((long) AL_SAMPLE_8); + if (x == NULL || PyDict_SetItemString(d, "SAMPLE_8", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SCALAR_VAL + x = PyInt_FromLong((long) AL_SCALAR_VAL); + if (x == NULL || PyDict_SetItemString(d, "SCALAR_VAL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SET_VAL + x = PyInt_FromLong((long) AL_SET_VAL); + if (x == NULL || PyDict_SetItemString(d, "SET_VAL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SHORT_NAME + x = PyInt_FromLong((long) AL_SHORT_NAME); + if (x == NULL || PyDict_SetItemString(d, "SHORT_NAME", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SMPTE272M_IF_TYPE + x = PyInt_FromLong((long) AL_SMPTE272M_IF_TYPE); + if (x == NULL || PyDict_SetItemString(d, "SMPTE272M_IF_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SOURCE + x = PyInt_FromLong((long) AL_SOURCE); + if (x == NULL || PyDict_SetItemString(d, "SOURCE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SPEAKER_IF_TYPE + x = PyInt_FromLong((long) AL_SPEAKER_IF_TYPE); + if (x == NULL || PyDict_SetItemString(d, "SPEAKER_IF_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SPEAKER_MUTE_CTL + x = PyInt_FromLong((long) AL_SPEAKER_MUTE_CTL); + if (x == NULL || PyDict_SetItemString(d, "SPEAKER_MUTE_CTL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SPEAKER_MUTE_OFF + x = PyInt_FromLong((long) AL_SPEAKER_MUTE_OFF); + if (x == NULL || PyDict_SetItemString(d, "SPEAKER_MUTE_OFF", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SPEAKER_MUTE_ON + x = PyInt_FromLong((long) AL_SPEAKER_MUTE_ON); + if (x == NULL || PyDict_SetItemString(d, "SPEAKER_MUTE_ON", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SPEAKER_PLUS_LINE_IF_TYPE + x = PyInt_FromLong((long) AL_SPEAKER_PLUS_LINE_IF_TYPE); + if (x == NULL || PyDict_SetItemString(d, "SPEAKER_PLUS_LINE_IF_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_STEREO + x = PyInt_FromLong((long) AL_STEREO); + if (x == NULL || PyDict_SetItemString(d, "STEREO", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_STRING_VAL + x = PyInt_FromLong((long) AL_STRING_VAL); + if (x == NULL || PyDict_SetItemString(d, "STRING_VAL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SUBSYSTEM + x = PyInt_FromLong((long) AL_SUBSYSTEM); + if (x == NULL || PyDict_SetItemString(d, "SUBSYSTEM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SUBSYSTEM_TYPE + x = PyInt_FromLong((long) AL_SUBSYSTEM_TYPE); + if (x == NULL || PyDict_SetItemString(d, "SUBSYSTEM_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SYNC_INPUT_TO_AES + x = PyInt_FromLong((long) AL_SYNC_INPUT_TO_AES); + if (x == NULL || PyDict_SetItemString(d, "SYNC_INPUT_TO_AES", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SYNC_OUTPUT_TO_AES + x = PyInt_FromLong((long) AL_SYNC_OUTPUT_TO_AES); + if (x == NULL || PyDict_SetItemString(d, "SYNC_OUTPUT_TO_AES", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SYSTEM + x = PyInt_FromLong((long) AL_SYSTEM); + if (x == NULL || PyDict_SetItemString(d, "SYSTEM", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_SYSTEM_TYPE + x = PyInt_FromLong((long) AL_SYSTEM_TYPE); + if (x == NULL || PyDict_SetItemString(d, "SYSTEM_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_TEST_IF_TYPE + x = PyInt_FromLong((long) AL_TEST_IF_TYPE); + if (x == NULL || PyDict_SetItemString(d, "TEST_IF_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_TYPE + x = PyInt_FromLong((long) AL_TYPE); + if (x == NULL || PyDict_SetItemString(d, "TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_TYPE_BIT + x = PyInt_FromLong((long) AL_TYPE_BIT); + if (x == NULL || PyDict_SetItemString(d, "TYPE_BIT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_UNUSED_COUNT + x = PyInt_FromLong((long) AL_UNUSED_COUNT); + if (x == NULL || PyDict_SetItemString(d, "UNUSED_COUNT", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_UNUSED_PORTS + x = PyInt_FromLong((long) AL_UNUSED_PORTS); + if (x == NULL || PyDict_SetItemString(d, "UNUSED_PORTS", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_VARIABLE_MCLK_TYPE + x = PyInt_FromLong((long) AL_VARIABLE_MCLK_TYPE); + if (x == NULL || PyDict_SetItemString(d, "VARIABLE_MCLK_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_VECTOR_VAL + x = PyInt_FromLong((long) AL_VECTOR_VAL); + if (x == NULL || PyDict_SetItemString(d, "VECTOR_VAL", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_VIDEO_MCLK_TYPE + x = PyInt_FromLong((long) AL_VIDEO_MCLK_TYPE); + if (x == NULL || PyDict_SetItemString(d, "VIDEO_MCLK_TYPE", x) < 0) + goto error; + Py_DECREF(x); +#endif +#ifdef AL_WORDSIZE + x = PyInt_FromLong((long) AL_WORDSIZE); + if (x == NULL || PyDict_SetItemString(d, "WORDSIZE", x) < 0) + goto error; + Py_DECREF(x); +#endif + +#ifdef AL_NO_ELEM /* IRIX 6 */ + (void) alSetErrorHandler(ErrorHandler); +#endif /* AL_NO_ELEM */ +#ifdef OLD_INTERFACE + (void) ALseterrorhandler(ErrorHandler); +#endif /* OLD_INTERFACE */ + + error: + return; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ar_beos b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ar_beos new file mode 100644 index 00000000..40a6b3da --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ar_beos @@ -0,0 +1,73 @@ +#!/bin/sh +# +# Truly fake ar, using a directory to store object files. +# +# Donn Cave, donn@oz.net + +usage='Usage: ar-fake cr libpython.dir obj.o ... + ar-fake d libpython.dir obj.o ... + ar-fake so libpython.dir libpython.so' + +case $# in +0|1|2) + echo "$usage" >&2 + exit 1 + ;; +esac + +command=$1 +library=$2 +shift 2 + +case $command in +cr) + if test -d $library + then : + else + mkdir $library + fi + if cp -p $* $library + then + # To force directory modify date, create or delete a file. + if test -e $library/.tch + then rm $library/.tch + else echo tch > $library/.tch + fi + exit 0 + fi + ;; +d) + if test -d $library + then + cd $library + rm -f $* + fi + ;; +so) + case $BE_HOST_CPU in + ppc) + # In case your libpython.a refers to any exotic libraries, + # mwld needs to know that here. The following hack makes + # a couple of assumptions about Modules/Makefile. If it + # doesn't work, you may as well add the necessary libraries + # here explicitly instead. + extralibs=$( + (cd Modules; make -f Makefile -n link) | + sed -n 's/.*\.so \(.*\) -o python.*/\1/p' + ) + mwld -xms -export pragma -nodup -o $1 $library/* $extralibs + ;; + x86) + ld -shared -soname $(basename $1) -o $1 $library/* + ;; + esac + status=$? + cd $(dirname $1) + ln -sf $PWD lib + exit $status + ;; +*) + echo "$usage" >&2 + exit 1 + ;; +esac diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/arraymodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/arraymodule.c new file mode 100644 index 00000000..8a96022e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/arraymodule.c @@ -0,0 +1,2037 @@ +/* Array object implementation */ + +/* An array is a uniform list -- all items have the same type. + The item type is restricted to simple C types like int or float */ + +#include "Python.h" + +#ifdef STDC_HEADERS +#include +#else /* !STDC_HEADERS */ +#ifndef DONT_HAVE_SYS_TYPES_H +#include /* For size_t */ +#endif /* DONT_HAVE_SYS_TYPES_H */ +#endif /* !STDC_HEADERS */ + +/* Shamelessy stolen from listobject.c */ +static int +roundupsize(int n) +{ + unsigned int nbits = 0; + unsigned int n2 = (unsigned int)n >> 5; + + /* Round up: + * If n < 256, to a multiple of 8. + * If n < 2048, to a multiple of 64. + * If n < 16384, to a multiple of 512. + * If n < 131072, to a multiple of 4096. + * If n < 1048576, to a multiple of 32768. + * If n < 8388608, to a multiple of 262144. + * If n < 67108864, to a multiple of 2097152. + * If n < 536870912, to a multiple of 16777216. + * ... + * If n < 2**(5+3*i), to a multiple of 2**(3*i). + * + * This over-allocates proportional to the list size, making room + * for additional growth. The over-allocation is mild, but is + * enough to give linear-time amortized behavior over a long + * sequence of appends() in the presence of a poorly-performing + * system realloc() (which is a reality, e.g., across all flavors + * of Windows, with Win9x behavior being particularly bad -- and + * we've still got address space fragmentation problems on Win9x + * even with this scheme, although it requires much longer lists to + * provoke them than it used to). + */ + do { + n2 >>= 3; + nbits += 3; + } while (n2); + return ((n >> nbits) + 1) << nbits; + } + +#define NRESIZE(var, type, nitems) \ +do { \ + size_t _new_size = roundupsize(nitems); \ + if (_new_size <= ((~(size_t)0) / sizeof(type))) \ + PyMem_RESIZE(var, type, _new_size); \ + else \ + var = NULL; \ +} while (0) +/* END SHAMELESSLY STOLEN CODE */ + +struct arrayobject; /* Forward */ + +/* All possible arraydescr values are defined in the vector "descriptors" + * below. That's defined later because the appropriate get and set + * functions aren't visible yet. + */ +struct arraydescr { + int typecode; + int itemsize; + PyObject * (*getitem)(struct arrayobject *, int); + int (*setitem)(struct arrayobject *, int, PyObject *); +}; + +typedef struct arrayobject { + PyObject_HEAD + int ob_size; + char *ob_item; + struct arraydescr *ob_descr; +} arrayobject; + +static PyTypeObject Arraytype; + +#define array_Check(op) PyObject_TypeCheck(op, &Arraytype) +#define array_CheckExact(op) ((op)->ob_type == &Arraytype) + +/**************************************************************************** +Get and Set functions for each type. +A Get function takes an arrayobject* and an integer index, returning the +array value at that index wrapped in an appropriate PyObject*. +A Set function takes an arrayobject, integer index, and PyObject*; sets +the array value at that index to the raw C data extracted from the PyObject*, +and returns 0 if successful, else nonzero on failure (PyObject* not of an +appropriate type or value). +Note that the basic Get and Set functions do NOT check that the index is +in bounds; that's the responsibility of the caller. +****************************************************************************/ + +static PyObject * +c_getitem(arrayobject *ap, int i) +{ + return PyString_FromStringAndSize(&((char *)ap->ob_item)[i], 1); +} + +static int +c_setitem(arrayobject *ap, int i, PyObject *v) +{ + char x; + if (!PyArg_Parse(v, "c;array item must be char", &x)) + return -1; + if (i >= 0) + ((char *)ap->ob_item)[i] = x; + return 0; +} + +static PyObject * +b_getitem(arrayobject *ap, int i) +{ + long x = ((char *)ap->ob_item)[i]; + if (x >= 128) + x -= 256; + return PyInt_FromLong(x); +} + +static int +b_setitem(arrayobject *ap, int i, PyObject *v) +{ + short x; + /* PyArg_Parse's 'b' formatter is for an unsigned char, therefore + must use the next size up that is signed ('h') and manually do + the overflow checking */ + if (!PyArg_Parse(v, "h;array item must be integer", &x)) + return -1; + else if (x < -128) { + PyErr_SetString(PyExc_OverflowError, + "signed char is less than minimum"); + return -1; + } + else if (x > 127) { + PyErr_SetString(PyExc_OverflowError, + "signed char is greater than maximum"); + return -1; + } + if (i >= 0) + ((char *)ap->ob_item)[i] = (char)x; + return 0; +} + +static PyObject * +BB_getitem(arrayobject *ap, int i) +{ + long x = ((unsigned char *)ap->ob_item)[i]; + return PyInt_FromLong(x); +} + +static int +BB_setitem(arrayobject *ap, int i, PyObject *v) +{ + unsigned char x; + /* 'B' == unsigned char, maps to PyArg_Parse's 'b' formatter */ + if (!PyArg_Parse(v, "b;array item must be integer", &x)) + return -1; + if (i >= 0) + ((char *)ap->ob_item)[i] = x; + return 0; +} + +#ifdef Py_USING_UNICODE +static PyObject * +u_getitem(arrayobject *ap, int i) +{ + return PyUnicode_FromUnicode(&((Py_UNICODE *) ap->ob_item)[i], 1); +} + +static int +u_setitem(arrayobject *ap, int i, PyObject *v) +{ + Py_UNICODE *p; + int len; + + if (!PyArg_Parse(v, "u#;array item must be unicode character", &p, &len)) + return -1; + if (len != 1) { + PyErr_SetString(PyExc_TypeError, "array item must be unicode character"); + return -1; + } + if (i >= 0) + ((Py_UNICODE *)ap->ob_item)[i] = p[0]; + return 0; +} +#endif + +static PyObject * +h_getitem(arrayobject *ap, int i) +{ + return PyInt_FromLong((long) ((short *)ap->ob_item)[i]); +} + +static int +h_setitem(arrayobject *ap, int i, PyObject *v) +{ + short x; + /* 'h' == signed short, maps to PyArg_Parse's 'h' formatter */ + if (!PyArg_Parse(v, "h;array item must be integer", &x)) + return -1; + if (i >= 0) + ((short *)ap->ob_item)[i] = x; + return 0; +} + +static PyObject * +HH_getitem(arrayobject *ap, int i) +{ + return PyInt_FromLong((long) ((unsigned short *)ap->ob_item)[i]); +} + +static int +HH_setitem(arrayobject *ap, int i, PyObject *v) +{ + int x; + /* PyArg_Parse's 'h' formatter is for a signed short, therefore + must use the next size up and manually do the overflow checking */ + if (!PyArg_Parse(v, "i;array item must be integer", &x)) + return -1; + else if (x < 0) { + PyErr_SetString(PyExc_OverflowError, + "unsigned short is less than minimum"); + return -1; + } + else if (x > USHRT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "unsigned short is greater than maximum"); + return -1; + } + if (i >= 0) + ((short *)ap->ob_item)[i] = (short)x; + return 0; +} + +static PyObject * +i_getitem(arrayobject *ap, int i) +{ + return PyInt_FromLong((long) ((int *)ap->ob_item)[i]); +} + +static int +i_setitem(arrayobject *ap, int i, PyObject *v) +{ + int x; + /* 'i' == signed int, maps to PyArg_Parse's 'i' formatter */ + if (!PyArg_Parse(v, "i;array item must be integer", &x)) + return -1; + if (i >= 0) + ((int *)ap->ob_item)[i] = x; + return 0; +} + +static PyObject * +II_getitem(arrayobject *ap, int i) +{ + return PyLong_FromUnsignedLong( + (unsigned long) ((unsigned int *)ap->ob_item)[i]); +} + +static int +II_setitem(arrayobject *ap, int i, PyObject *v) +{ + unsigned long x; + if (PyLong_Check(v)) { + x = PyLong_AsUnsignedLong(v); + if (x == (unsigned long) -1 && PyErr_Occurred()) + return -1; + } + else { + long y; + if (!PyArg_Parse(v, "l;array item must be integer", &y)) + return -1; + if (y < 0) { + PyErr_SetString(PyExc_OverflowError, + "unsigned int is less than minimum"); + return -1; + } + x = (unsigned long)y; + + } + if (x > UINT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "unsigned int is greater than maximum"); + return -1; + } + + if (i >= 0) + ((unsigned int *)ap->ob_item)[i] = (unsigned int)x; + return 0; +} + +static PyObject * +l_getitem(arrayobject *ap, int i) +{ + return PyInt_FromLong(((long *)ap->ob_item)[i]); +} + +static int +l_setitem(arrayobject *ap, int i, PyObject *v) +{ + long x; + if (!PyArg_Parse(v, "l;array item must be integer", &x)) + return -1; + if (i >= 0) + ((long *)ap->ob_item)[i] = x; + return 0; +} + +static PyObject * +LL_getitem(arrayobject *ap, int i) +{ + return PyLong_FromUnsignedLong(((unsigned long *)ap->ob_item)[i]); +} + +static int +LL_setitem(arrayobject *ap, int i, PyObject *v) +{ + unsigned long x; + if (PyLong_Check(v)) { + x = PyLong_AsUnsignedLong(v); + if (x == (unsigned long) -1 && PyErr_Occurred()) + return -1; + } + else { + long y; + if (!PyArg_Parse(v, "l;array item must be integer", &y)) + return -1; + if (y < 0) { + PyErr_SetString(PyExc_OverflowError, + "unsigned long is less than minimum"); + return -1; + } + x = (unsigned long)y; + + } + if (x > ULONG_MAX) { + PyErr_SetString(PyExc_OverflowError, + "unsigned long is greater than maximum"); + return -1; + } + + if (i >= 0) + ((unsigned long *)ap->ob_item)[i] = x; + return 0; +} + +static PyObject * +f_getitem(arrayobject *ap, int i) +{ + return PyFloat_FromDouble((double) ((float *)ap->ob_item)[i]); +} + +static int +f_setitem(arrayobject *ap, int i, PyObject *v) +{ + float x; + if (!PyArg_Parse(v, "f;array item must be float", &x)) + return -1; + if (i >= 0) + ((float *)ap->ob_item)[i] = x; + return 0; +} + +static PyObject * +d_getitem(arrayobject *ap, int i) +{ + return PyFloat_FromDouble(((double *)ap->ob_item)[i]); +} + +static int +d_setitem(arrayobject *ap, int i, PyObject *v) +{ + double x; + if (!PyArg_Parse(v, "d;array item must be float", &x)) + return -1; + if (i >= 0) + ((double *)ap->ob_item)[i] = x; + return 0; +} + +/* Description of types */ +static struct arraydescr descriptors[] = { + {'c', sizeof(char), c_getitem, c_setitem}, + {'b', sizeof(char), b_getitem, b_setitem}, + {'B', sizeof(char), BB_getitem, BB_setitem}, +#ifdef Py_USING_UNICODE + {'u', sizeof(Py_UNICODE), u_getitem, u_setitem}, +#endif + {'h', sizeof(short), h_getitem, h_setitem}, + {'H', sizeof(short), HH_getitem, HH_setitem}, + {'i', sizeof(int), i_getitem, i_setitem}, + {'I', sizeof(int), II_getitem, II_setitem}, + {'l', sizeof(long), l_getitem, l_setitem}, + {'L', sizeof(long), LL_getitem, LL_setitem}, + {'f', sizeof(float), f_getitem, f_setitem}, + {'d', sizeof(double), d_getitem, d_setitem}, + {'\0', 0, 0, 0} /* Sentinel */ +}; + +/**************************************************************************** +Implementations of array object methods. +****************************************************************************/ + +static PyObject * +newarrayobject(PyTypeObject *type, int size, struct arraydescr *descr) +{ + arrayobject *op; + size_t nbytes; + + if (size < 0) { + PyErr_BadInternalCall(); + return NULL; + } + + nbytes = size * descr->itemsize; + /* Check for overflow */ + if (nbytes / descr->itemsize != (size_t)size) { + return PyErr_NoMemory(); + } + op = (arrayobject *) type->tp_alloc(type, 0); + if (op == NULL) { + return NULL; + } + op->ob_size = size; + if (size <= 0) { + op->ob_item = NULL; + } + else { + op->ob_item = PyMem_NEW(char, nbytes); + if (op->ob_item == NULL) { + PyObject_Del(op); + return PyErr_NoMemory(); + } + } + op->ob_descr = descr; + return (PyObject *) op; +} + +static PyObject * +getarrayitem(PyObject *op, int i) +{ + register arrayobject *ap; + assert(array_Check(op)); + ap = (arrayobject *)op; + assert(i>=0 && iob_size); + return (*ap->ob_descr->getitem)(ap, i); +} + +static int +ins1(arrayobject *self, int where, PyObject *v) +{ + char *items; + if (v == NULL) { + PyErr_BadInternalCall(); + return -1; + } + if ((*self->ob_descr->setitem)(self, -1, v) < 0) + return -1; + items = self->ob_item; + NRESIZE(items, char, (self->ob_size+1) * self->ob_descr->itemsize); + if (items == NULL) { + PyErr_NoMemory(); + return -1; + } + if (where < 0) { + where += self->ob_size; + if (where < 0) + where = 0; + } + if (where > self->ob_size) + where = self->ob_size; + memmove(items + (where+1)*self->ob_descr->itemsize, + items + where*self->ob_descr->itemsize, + (self->ob_size-where)*self->ob_descr->itemsize); + self->ob_item = items; + self->ob_size++; + return (*self->ob_descr->setitem)(self, where, v); +} + +/* Methods */ + +static void +array_dealloc(arrayobject *op) +{ + if (op->ob_item != NULL) + PyMem_DEL(op->ob_item); + op->ob_type->tp_free((PyObject *)op); +} + +static PyObject * +array_richcompare(PyObject *v, PyObject *w, int op) +{ + arrayobject *va, *wa; + PyObject *vi = NULL; + PyObject *wi = NULL; + int i, k; + PyObject *res; + + if (!array_Check(v) || !array_Check(w)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + va = (arrayobject *)v; + wa = (arrayobject *)w; + + if (va->ob_size != wa->ob_size && (op == Py_EQ || op == Py_NE)) { + /* Shortcut: if the lengths differ, the arrays differ */ + if (op == Py_EQ) + res = Py_False; + else + res = Py_True; + Py_INCREF(res); + return res; + } + + /* Search for the first index where items are different */ + k = 1; + for (i = 0; i < va->ob_size && i < wa->ob_size; i++) { + vi = getarrayitem(v, i); + wi = getarrayitem(w, i); + if (vi == NULL || wi == NULL) { + Py_XDECREF(vi); + Py_XDECREF(wi); + return NULL; + } + k = PyObject_RichCompareBool(vi, wi, Py_EQ); + if (k == 0) + break; /* Keeping vi and wi alive! */ + Py_DECREF(vi); + Py_DECREF(wi); + if (k < 0) + return NULL; + } + + if (k) { + /* No more items to compare -- compare sizes */ + int vs = va->ob_size; + int ws = wa->ob_size; + int cmp; + switch (op) { + case Py_LT: cmp = vs < ws; break; + case Py_LE: cmp = vs <= ws; break; + case Py_EQ: cmp = vs == ws; break; + case Py_NE: cmp = vs != ws; break; + case Py_GT: cmp = vs > ws; break; + case Py_GE: cmp = vs >= ws; break; + default: return NULL; /* cannot happen */ + } + if (cmp) + res = Py_True; + else + res = Py_False; + Py_INCREF(res); + return res; + } + + /* We have an item that differs. First, shortcuts for EQ/NE */ + if (op == Py_EQ) { + Py_INCREF(Py_False); + res = Py_False; + } + else if (op == Py_NE) { + Py_INCREF(Py_True); + res = Py_True; + } + else { + /* Compare the final item again using the proper operator */ + res = PyObject_RichCompare(vi, wi, op); + } + Py_DECREF(vi); + Py_DECREF(wi); + return res; +} + +static int +array_length(arrayobject *a) +{ + return a->ob_size; +} + +static PyObject * +array_item(arrayobject *a, int i) +{ + if (i < 0 || i >= a->ob_size) { + PyErr_SetString(PyExc_IndexError, "array index out of range"); + return NULL; + } + return getarrayitem((PyObject *)a, i); +} + +static PyObject * +array_slice(arrayobject *a, int ilow, int ihigh) +{ + arrayobject *np; + if (ilow < 0) + ilow = 0; + else if (ilow > a->ob_size) + ilow = a->ob_size; + if (ihigh < 0) + ihigh = 0; + if (ihigh < ilow) + ihigh = ilow; + else if (ihigh > a->ob_size) + ihigh = a->ob_size; + np = (arrayobject *) newarrayobject(&Arraytype, ihigh - ilow, a->ob_descr); + if (np == NULL) + return NULL; + memcpy(np->ob_item, a->ob_item + ilow * a->ob_descr->itemsize, + (ihigh-ilow) * a->ob_descr->itemsize); + return (PyObject *)np; +} + +static PyObject * +array_concat(arrayobject *a, PyObject *bb) +{ + int size; + arrayobject *np; + if (!array_Check(bb)) { + PyErr_Format(PyExc_TypeError, + "can only append array (not \"%.200s\") to array", + bb->ob_type->tp_name); + return NULL; + } +#define b ((arrayobject *)bb) + if (a->ob_descr != b->ob_descr) { + PyErr_BadArgument(); + return NULL; + } + size = a->ob_size + b->ob_size; + np = (arrayobject *) newarrayobject(&Arraytype, size, a->ob_descr); + if (np == NULL) { + return NULL; + } + memcpy(np->ob_item, a->ob_item, a->ob_size*a->ob_descr->itemsize); + memcpy(np->ob_item + a->ob_size*a->ob_descr->itemsize, + b->ob_item, b->ob_size*b->ob_descr->itemsize); + return (PyObject *)np; +#undef b +} + +static PyObject * +array_repeat(arrayobject *a, int n) +{ + int i; + int size; + arrayobject *np; + char *p; + int nbytes; + if (n < 0) + n = 0; + size = a->ob_size * n; + np = (arrayobject *) newarrayobject(&Arraytype, size, a->ob_descr); + if (np == NULL) + return NULL; + p = np->ob_item; + nbytes = a->ob_size * a->ob_descr->itemsize; + for (i = 0; i < n; i++) { + memcpy(p, a->ob_item, nbytes); + p += nbytes; + } + return (PyObject *) np; +} + +static int +array_ass_slice(arrayobject *a, int ilow, int ihigh, PyObject *v) +{ + char *item; + int n; /* Size of replacement array */ + int d; /* Change in size */ +#define b ((arrayobject *)v) + if (v == NULL) + n = 0; + else if (array_Check(v)) { + n = b->ob_size; + if (a == b) { + /* Special case "a[i:j] = a" -- copy b first */ + int ret; + v = array_slice(b, 0, n); + ret = array_ass_slice(a, ilow, ihigh, v); + Py_DECREF(v); + return ret; + } + if (b->ob_descr != a->ob_descr) { + PyErr_BadArgument(); + return -1; + } + } + else { + PyErr_Format(PyExc_TypeError, + "can only assign array (not \"%.200s\") to array slice", + v->ob_type->tp_name); + return -1; + } + if (ilow < 0) + ilow = 0; + else if (ilow > a->ob_size) + ilow = a->ob_size; + if (ihigh < 0) + ihigh = 0; + if (ihigh < ilow) + ihigh = ilow; + else if (ihigh > a->ob_size) + ihigh = a->ob_size; + item = a->ob_item; + d = n - (ihigh-ilow); + if (d < 0) { /* Delete -d items */ + memmove(item + (ihigh+d)*a->ob_descr->itemsize, + item + ihigh*a->ob_descr->itemsize, + (a->ob_size-ihigh)*a->ob_descr->itemsize); + a->ob_size += d; + PyMem_RESIZE(item, char, a->ob_size*a->ob_descr->itemsize); + /* Can't fail */ + a->ob_item = item; + } + else if (d > 0) { /* Insert d items */ + PyMem_RESIZE(item, char, + (a->ob_size + d)*a->ob_descr->itemsize); + if (item == NULL) { + PyErr_NoMemory(); + return -1; + } + memmove(item + (ihigh+d)*a->ob_descr->itemsize, + item + ihigh*a->ob_descr->itemsize, + (a->ob_size-ihigh)*a->ob_descr->itemsize); + a->ob_item = item; + a->ob_size += d; + } + if (n > 0) + memcpy(item + ilow*a->ob_descr->itemsize, b->ob_item, + n*b->ob_descr->itemsize); + return 0; +#undef b +} + +static int +array_ass_item(arrayobject *a, int i, PyObject *v) +{ + if (i < 0 || i >= a->ob_size) { + PyErr_SetString(PyExc_IndexError, + "array assignment index out of range"); + return -1; + } + if (v == NULL) + return array_ass_slice(a, i, i+1, v); + return (*a->ob_descr->setitem)(a, i, v); +} + +static int +setarrayitem(PyObject *a, int i, PyObject *v) +{ + assert(array_Check(a)); + return array_ass_item((arrayobject *)a, i, v); +} + +static int +array_do_extend(arrayobject *self, PyObject *bb) +{ + int size; + + if (!array_Check(bb)) { + PyErr_Format(PyExc_TypeError, + "can only extend array with array (not \"%.200s\")", + bb->ob_type->tp_name); + return -1; + } +#define b ((arrayobject *)bb) + if (self->ob_descr != b->ob_descr) { + PyErr_SetString(PyExc_TypeError, + "can only extend with array of same kind"); + return -1; + } + size = self->ob_size + b->ob_size; + PyMem_RESIZE(self->ob_item, char, size*self->ob_descr->itemsize); + if (self->ob_item == NULL) { + PyObject_Del(self); + PyErr_NoMemory(); + return -1; + } + memcpy(self->ob_item + self->ob_size*self->ob_descr->itemsize, + b->ob_item, b->ob_size*b->ob_descr->itemsize); + self->ob_size = size; + + return 0; +#undef b +} + +static PyObject * +array_inplace_concat(arrayobject *self, PyObject *bb) +{ + if (array_do_extend(self, bb) == -1) + return NULL; + Py_INCREF(self); + return (PyObject *)self; +} + +static PyObject * +array_inplace_repeat(arrayobject *self, int n) +{ + char *items, *p; + int size, i; + + if (self->ob_size > 0) { + if (n < 0) + n = 0; + items = self->ob_item; + size = self->ob_size * self->ob_descr->itemsize; + if (n == 0) { + PyMem_FREE(items); + self->ob_item = NULL; + self->ob_size = 0; + } + else { + PyMem_Resize(items, char, n * size); + if (items == NULL) + return PyErr_NoMemory(); + p = items; + for (i = 1; i < n; i++) { + p += size; + memcpy(p, items, size); + } + self->ob_item = items; + self->ob_size *= n; + } + } + Py_INCREF(self); + return (PyObject *)self; +} + + +static PyObject * +ins(arrayobject *self, int where, PyObject *v) +{ + if (ins1(self, where, v) != 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +array_count(arrayobject *self, PyObject *v) +{ + int count = 0; + int i; + + for (i = 0; i < self->ob_size; i++) { + PyObject *selfi = getarrayitem((PyObject *)self, i); + int cmp = PyObject_RichCompareBool(selfi, v, Py_EQ); + Py_DECREF(selfi); + if (cmp > 0) + count++; + else if (cmp < 0) + return NULL; + } + return PyInt_FromLong((long)count); +} + +PyDoc_STRVAR(count_doc, +"count(x)\n\ +\n\ +Return number of occurences of x in the array."); + +static PyObject * +array_index(arrayobject *self, PyObject *v) +{ + int i; + + for (i = 0; i < self->ob_size; i++) { + PyObject *selfi = getarrayitem((PyObject *)self, i); + int cmp = PyObject_RichCompareBool(selfi, v, Py_EQ); + Py_DECREF(selfi); + if (cmp > 0) { + return PyInt_FromLong((long)i); + } + else if (cmp < 0) + return NULL; + } + PyErr_SetString(PyExc_ValueError, "array.index(x): x not in list"); + return NULL; +} + +PyDoc_STRVAR(index_doc, +"index(x)\n\ +\n\ +Return index of first occurence of x in the array."); + +static int +array_contains(arrayobject *self, PyObject *v) +{ + int i, cmp; + + for (i = 0, cmp = 0 ; cmp == 0 && i < self->ob_size; i++) { + PyObject *selfi = getarrayitem((PyObject *)self, i); + cmp = PyObject_RichCompareBool(selfi, v, Py_EQ); + Py_DECREF(selfi); + } + return cmp; +} + +static PyObject * +array_remove(arrayobject *self, PyObject *v) +{ + int i; + + for (i = 0; i < self->ob_size; i++) { + PyObject *selfi = getarrayitem((PyObject *)self,i); + int cmp = PyObject_RichCompareBool(selfi, v, Py_EQ); + Py_DECREF(selfi); + if (cmp > 0) { + if (array_ass_slice(self, i, i+1, + (PyObject *)NULL) != 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; + } + else if (cmp < 0) + return NULL; + } + PyErr_SetString(PyExc_ValueError, "array.remove(x): x not in list"); + return NULL; +} + +PyDoc_STRVAR(remove_doc, +"remove(x)\n\ +\n\ +Remove the first occurence of x in the array."); + +static PyObject * +array_pop(arrayobject *self, PyObject *args) +{ + int i = -1; + PyObject *v; + if (!PyArg_ParseTuple(args, "|i:pop", &i)) + return NULL; + if (self->ob_size == 0) { + /* Special-case most common failure cause */ + PyErr_SetString(PyExc_IndexError, "pop from empty array"); + return NULL; + } + if (i < 0) + i += self->ob_size; + if (i < 0 || i >= self->ob_size) { + PyErr_SetString(PyExc_IndexError, "pop index out of range"); + return NULL; + } + v = getarrayitem((PyObject *)self,i); + if (array_ass_slice(self, i, i+1, (PyObject *)NULL) != 0) { + Py_DECREF(v); + return NULL; + } + return v; +} + +PyDoc_STRVAR(pop_doc, +"pop([i])\n\ +\n\ +Return the i-th element and delete it from the array. i defaults to -1."); + +static PyObject * +array_extend(arrayobject *self, PyObject *bb) +{ + if (array_do_extend(self, bb) == -1) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(extend_doc, +"extend(array)\n\ +\n\ + Append array items to the end of the array."); + +static PyObject * +array_insert(arrayobject *self, PyObject *args) +{ + int i; + PyObject *v; + if (!PyArg_ParseTuple(args, "iO:insert", &i, &v)) + return NULL; + return ins(self, i, v); +} + +PyDoc_STRVAR(insert_doc, +"insert(i,x)\n\ +\n\ +Insert a new item x into the array before position i."); + + +static PyObject * +array_buffer_info(arrayobject *self, PyObject *unused) +{ + PyObject* retval = NULL; + retval = PyTuple_New(2); + if (!retval) + return NULL; + + PyTuple_SET_ITEM(retval, 0, PyLong_FromVoidPtr(self->ob_item)); + PyTuple_SET_ITEM(retval, 1, PyInt_FromLong((long)(self->ob_size))); + + return retval; +} + +PyDoc_STRVAR(buffer_info_doc, +"buffer_info() -> (address, length)\n\ +\n\ +Return a tuple (address, length) giving the current memory address and\n\ +the length in items of the buffer used to hold array's contents\n\ +The length should be multiplied by the itemsize attribute to calculate\n\ +the buffer length in bytes."); + + +static PyObject * +array_append(arrayobject *self, PyObject *v) +{ + return ins(self, (int) self->ob_size, v); +} + +PyDoc_STRVAR(append_doc, +"append(x)\n\ +\n\ +Append new value x to the end of the array."); + + +static PyObject * +array_byteswap(arrayobject *self, PyObject *unused) +{ + char *p; + int i; + + switch (self->ob_descr->itemsize) { + case 1: + break; + case 2: + for (p = self->ob_item, i = self->ob_size; --i >= 0; p += 2) { + char p0 = p[0]; + p[0] = p[1]; + p[1] = p0; + } + break; + case 4: + for (p = self->ob_item, i = self->ob_size; --i >= 0; p += 4) { + char p0 = p[0]; + char p1 = p[1]; + p[0] = p[3]; + p[1] = p[2]; + p[2] = p1; + p[3] = p0; + } + break; + case 8: + for (p = self->ob_item, i = self->ob_size; --i >= 0; p += 8) { + char p0 = p[0]; + char p1 = p[1]; + char p2 = p[2]; + char p3 = p[3]; + p[0] = p[7]; + p[1] = p[6]; + p[2] = p[5]; + p[3] = p[4]; + p[4] = p3; + p[5] = p2; + p[6] = p1; + p[7] = p0; + } + break; + default: + PyErr_SetString(PyExc_RuntimeError, + "don't know how to byteswap this array type"); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(byteswap_doc, +"byteswap()\n\ +\n\ +Byteswap all items of the array. If the items in the array are not 1, 2,\n\ +4, or 8 bytes in size, RuntimeError is raised."); + +static PyObject * +array_reverse(arrayobject *self, PyObject *unused) +{ + register int itemsize = self->ob_descr->itemsize; + register char *p, *q; + /* little buffer to hold items while swapping */ + char tmp[256]; /* 8 is probably enough -- but why skimp */ + assert(itemsize <= sizeof(tmp)); + + if (self->ob_size > 1) { + for (p = self->ob_item, + q = self->ob_item + (self->ob_size - 1)*itemsize; + p < q; + p += itemsize, q -= itemsize) { + /* memory areas guaranteed disjoint, so memcpy + * is safe (& memmove may be slower). + */ + memcpy(tmp, p, itemsize); + memcpy(p, q, itemsize); + memcpy(q, tmp, itemsize); + } + } + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(reverse_doc, +"reverse()\n\ +\n\ +Reverse the order of the items in the array."); + +static PyObject * +array_fromfile(arrayobject *self, PyObject *args) +{ + PyObject *f; + int n; + FILE *fp; + if (!PyArg_ParseTuple(args, "Oi:fromfile", &f, &n)) + return NULL; + fp = PyFile_AsFile(f); + if (fp == NULL) { + PyErr_SetString(PyExc_TypeError, "arg1 must be open file"); + return NULL; + } + if (n > 0) { + char *item = self->ob_item; + int itemsize = self->ob_descr->itemsize; + size_t nread; + int newlength; + size_t newbytes; + /* Be careful here about overflow */ + if ((newlength = self->ob_size + n) <= 0 || + (newbytes = newlength * itemsize) / itemsize != + (size_t)newlength) + goto nomem; + PyMem_RESIZE(item, char, newbytes); + if (item == NULL) { + nomem: + PyErr_NoMemory(); + return NULL; + } + self->ob_item = item; + self->ob_size += n; + nread = fread(item + (self->ob_size - n) * itemsize, + itemsize, n, fp); + if (nread < (size_t)n) { + self->ob_size -= (n - nread); + PyMem_RESIZE(item, char, self->ob_size*itemsize); + self->ob_item = item; + PyErr_SetString(PyExc_EOFError, + "not enough items in file"); + return NULL; + } + } + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(fromfile_doc, +"fromfile(f, n)\n\ +\n\ +Read n objects from the file object f and append them to the end of the\n\ +array. Also called as read."); + + +static PyObject * +array_tofile(arrayobject *self, PyObject *f) +{ + FILE *fp; + + fp = PyFile_AsFile(f); + if (fp == NULL) { + PyErr_SetString(PyExc_TypeError, "arg must be open file"); + return NULL; + } + if (self->ob_size > 0) { + if (fwrite(self->ob_item, self->ob_descr->itemsize, + self->ob_size, fp) != (size_t)self->ob_size) { + PyErr_SetFromErrno(PyExc_IOError); + clearerr(fp); + return NULL; + } + } + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(tofile_doc, +"tofile(f)\n\ +\n\ +Write all items (as machine values) to the file object f. Also called as\n\ +write."); + + +static PyObject * +array_fromlist(arrayobject *self, PyObject *list) +{ + int n; + int itemsize = self->ob_descr->itemsize; + + if (!PyList_Check(list)) { + PyErr_SetString(PyExc_TypeError, "arg must be list"); + return NULL; + } + n = PyList_Size(list); + if (n > 0) { + char *item = self->ob_item; + int i; + PyMem_RESIZE(item, char, (self->ob_size + n) * itemsize); + if (item == NULL) { + PyErr_NoMemory(); + return NULL; + } + self->ob_item = item; + self->ob_size += n; + for (i = 0; i < n; i++) { + PyObject *v = PyList_GetItem(list, i); + if ((*self->ob_descr->setitem)(self, + self->ob_size - n + i, v) != 0) { + self->ob_size -= n; + PyMem_RESIZE(item, char, + self->ob_size * itemsize); + self->ob_item = item; + return NULL; + } + } + } + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(fromlist_doc, +"fromlist(list)\n\ +\n\ +Append items to array from list."); + + +static PyObject * +array_tolist(arrayobject *self, PyObject *unused) +{ + PyObject *list = PyList_New(self->ob_size); + int i; + + if (list == NULL) + return NULL; + for (i = 0; i < self->ob_size; i++) { + PyObject *v = getarrayitem((PyObject *)self, i); + if (v == NULL) { + Py_DECREF(list); + return NULL; + } + PyList_SetItem(list, i, v); + } + return list; +} + +PyDoc_STRVAR(tolist_doc, +"tolist() -> list\n\ +\n\ +Convert array to an ordinary list with the same items."); + + +static PyObject * +array_fromstring(arrayobject *self, PyObject *args) +{ + char *str; + int n; + int itemsize = self->ob_descr->itemsize; + if (!PyArg_ParseTuple(args, "s#:fromstring", &str, &n)) + return NULL; + if (n % itemsize != 0) { + PyErr_SetString(PyExc_ValueError, + "string length not a multiple of item size"); + return NULL; + } + n = n / itemsize; + if (n > 0) { + char *item = self->ob_item; + PyMem_RESIZE(item, char, (self->ob_size + n) * itemsize); + if (item == NULL) { + PyErr_NoMemory(); + return NULL; + } + self->ob_item = item; + self->ob_size += n; + memcpy(item + (self->ob_size - n) * itemsize, + str, itemsize*n); + } + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(fromstring_doc, +"fromstring(string)\n\ +\n\ +Appends items from the string, interpreting it as an array of machine\n\ +values,as if it had been read from a file using the fromfile() method)."); + + +static PyObject * +array_tostring(arrayobject *self, PyObject *unused) +{ + return PyString_FromStringAndSize(self->ob_item, + self->ob_size * self->ob_descr->itemsize); +} + +PyDoc_STRVAR(tostring_doc, +"tostring() -> string\n\ +\n\ +Convert the array to an array of machine values and return the string\n\ +representation."); + + + +#ifdef Py_USING_UNICODE +static PyObject * +array_fromunicode(arrayobject *self, PyObject *args) +{ + Py_UNICODE *ustr; + int n; + + if (!PyArg_ParseTuple(args, "u#:fromunicode", &ustr, &n)) + return NULL; + if (self->ob_descr->typecode != 'u') { + PyErr_SetString(PyExc_ValueError, + "fromunicode() may only be called on " + "type 'u' arrays"); + return NULL; + } + if (n > 0) { + Py_UNICODE *item = (Py_UNICODE *) self->ob_item; + PyMem_RESIZE(item, Py_UNICODE, self->ob_size + n); + if (item == NULL) { + PyErr_NoMemory(); + return NULL; + } + self->ob_item = (char *) item; + self->ob_size += n; + memcpy(item + self->ob_size - n, + ustr, n * sizeof(Py_UNICODE)); + } + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(fromunicode_doc, +"fromunicode(ustr)\n\ +\n\ +Extends this array with data from the unicode string ustr.\n\ +The array must be a type 'u' array; otherwise a ValueError\n\ +is raised. Use array.fromstring(ustr.decode(...)) to\n\ +append Unicode data to an array of some other type."); + + +static PyObject * +array_tounicode(arrayobject *self, PyObject *unused) +{ + if (self->ob_descr->typecode != 'u') { + PyErr_SetString(PyExc_ValueError, + "tounicode() may only be called on type 'u' arrays"); + return NULL; + } + return PyUnicode_FromUnicode((Py_UNICODE *) self->ob_item, self->ob_size); +} + +PyDoc_STRVAR(tounicode_doc, +"tounicode() -> unicode\n\ +\n\ +Convert the array to a unicode string. The array must be\n\ +a type 'u' array; otherwise a ValueError is raised. Use\n\ +array.tostring().decode() to obtain a unicode string from\n\ +an array of some other type."); + +#endif /* Py_USING_UNICODE */ + + +static PyObject * +array_get_typecode(arrayobject *a, void *closure) +{ + char tc = a->ob_descr->typecode; + return PyString_FromStringAndSize(&tc, 1); +} + +static PyObject * +array_get_itemsize(arrayobject *a, void *closure) +{ + return PyInt_FromLong((long)a->ob_descr->itemsize); +} + +static PyGetSetDef array_getsets [] = { + {"typecode", (getter) array_get_typecode, NULL, + "the typecode character used to create the array"}, + {"itemsize", (getter) array_get_itemsize, NULL, + "the size, in bytes, of one array item"}, + {NULL} +}; + +PyMethodDef array_methods[] = { + {"append", (PyCFunction)array_append, METH_O, + append_doc}, + {"buffer_info", (PyCFunction)array_buffer_info, METH_NOARGS, + buffer_info_doc}, + {"byteswap", (PyCFunction)array_byteswap, METH_NOARGS, + byteswap_doc}, + {"count", (PyCFunction)array_count, METH_O, + count_doc}, + {"extend", (PyCFunction)array_extend, METH_O, + extend_doc}, + {"fromfile", (PyCFunction)array_fromfile, METH_VARARGS, + fromfile_doc}, + {"fromlist", (PyCFunction)array_fromlist, METH_O, + fromlist_doc}, + {"fromstring", (PyCFunction)array_fromstring, METH_VARARGS, + fromstring_doc}, +#ifdef Py_USING_UNICODE + {"fromunicode", (PyCFunction)array_fromunicode, METH_VARARGS, + fromunicode_doc}, +#endif + {"index", (PyCFunction)array_index, METH_O, + index_doc}, + {"insert", (PyCFunction)array_insert, METH_VARARGS, + insert_doc}, + {"pop", (PyCFunction)array_pop, METH_VARARGS, + pop_doc}, + {"read", (PyCFunction)array_fromfile, METH_VARARGS, + fromfile_doc}, + {"remove", (PyCFunction)array_remove, METH_O, + remove_doc}, + {"reverse", (PyCFunction)array_reverse, METH_NOARGS, + reverse_doc}, +/* {"sort", (PyCFunction)array_sort, METH_VARARGS, + sort_doc},*/ + {"tofile", (PyCFunction)array_tofile, METH_O, + tofile_doc}, + {"tolist", (PyCFunction)array_tolist, METH_NOARGS, + tolist_doc}, + {"tostring", (PyCFunction)array_tostring, METH_NOARGS, + tostring_doc}, +#ifdef Py_USING_UNICODE + {"tounicode", (PyCFunction)array_tounicode, METH_NOARGS, + tounicode_doc}, +#endif + {"write", (PyCFunction)array_tofile, METH_O, + tofile_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +array_repr(arrayobject *a) +{ + char buf[256], typecode; + PyObject *s, *t, *v = NULL; + int len; + + len = a->ob_size; + typecode = a->ob_descr->typecode; + if (len == 0) { + PyOS_snprintf(buf, sizeof(buf), "array('%c')", typecode); + return PyString_FromString(buf); + } + + if (typecode == 'c') + v = array_tostring(a, NULL); +#ifdef Py_USING_UNICODE + else if (typecode == 'u') + v = array_tounicode(a, NULL); +#endif + else + v = array_tolist(a, NULL); + t = PyObject_Repr(v); + Py_XDECREF(v); + + PyOS_snprintf(buf, sizeof(buf), "array('%c', ", typecode); + s = PyString_FromString(buf); + PyString_ConcatAndDel(&s, t); + PyString_ConcatAndDel(&s, PyString_FromString(")")); + return s; +} + +static PyObject* +array_subscr(arrayobject* self, PyObject* item) +{ + if (PyInt_Check(item)) { + long i = PyInt_AS_LONG(item); + if (i < 0) + i += self->ob_size; + return array_item(self, i); + } + else if (PyLong_Check(item)) { + long i = PyLong_AsLong(item); + if (i == -1 && PyErr_Occurred()) + return NULL; + if (i < 0) + i += self->ob_size; + return array_item(self, i); + } + else if (PySlice_Check(item)) { + int start, stop, step, slicelength, cur, i; + PyObject* result; + arrayobject* ar; + int itemsize = self->ob_descr->itemsize; + + if (PySlice_GetIndicesEx((PySliceObject*)item, self->ob_size, + &start, &stop, &step, &slicelength) < 0) { + return NULL; + } + + if (slicelength <= 0) { + return newarrayobject(&Arraytype, 0, self->ob_descr); + } + else { + result = newarrayobject(&Arraytype, slicelength, self->ob_descr); + if (!result) return NULL; + + ar = (arrayobject*)result; + + for (cur = start, i = 0; i < slicelength; + cur += step, i++) { + memcpy(ar->ob_item + i*itemsize, + self->ob_item + cur*itemsize, + itemsize); + } + + return result; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "list indices must be integers"); + return NULL; + } +} + +static int +array_ass_subscr(arrayobject* self, PyObject* item, PyObject* value) +{ + if (PyInt_Check(item)) { + long i = PyInt_AS_LONG(item); + if (i < 0) + i += self->ob_size; + return array_ass_item(self, i, value); + } + else if (PyLong_Check(item)) { + long i = PyLong_AsLong(item); + if (i == -1 && PyErr_Occurred()) + return -1; + if (i < 0) + i += self->ob_size; + return array_ass_item(self, i, value); + } + else if (PySlice_Check(item)) { + int start, stop, step, slicelength; + int itemsize = self->ob_descr->itemsize; + + if (PySlice_GetIndicesEx((PySliceObject*)item, self->ob_size, + &start, &stop, &step, &slicelength) < 0) { + return -1; + } + + /* treat A[slice(a,b)] = v _exactly_ like A[a:b] = v */ + if (step == 1 && ((PySliceObject*)item)->step == Py_None) + return array_ass_slice(self, start, stop, value); + + if (value == NULL) { + /* delete slice */ + int cur, i, extra; + + if (slicelength <= 0) + return 0; + + if (step < 0) { + stop = start + 1; + start = stop + step*(slicelength - 1) - 1; + step = -step; + } + + for (cur = start, i = 0; i < slicelength - 1; + cur += step, i++) { + memmove(self->ob_item + (cur - i)*itemsize, + self->ob_item + (cur + 1)*itemsize, + (step - 1) * itemsize); + } + extra = self->ob_size - (cur + 1); + if (extra > 0) { + memmove(self->ob_item + (cur - i)*itemsize, + self->ob_item + (cur + 1)*itemsize, + extra*itemsize); + } + + self->ob_size -= slicelength; + self->ob_item = PyMem_REALLOC(self->ob_item, itemsize*self->ob_size); + + + return 0; + } + else { + /* assign slice */ + int cur, i; + arrayobject* av; + + if (!array_Check(value)) { + PyErr_Format(PyExc_TypeError, + "must assign array (not \"%.200s\") to slice", + value->ob_type->tp_name); + return -1; + } + + av = (arrayobject*)value; + + if (av->ob_size != slicelength) { + PyErr_Format(PyExc_ValueError, + "attempt to assign array of size %d to extended slice of size %d", + av->ob_size, slicelength); + return -1; + } + + if (!slicelength) + return 0; + + /* protect against a[::-1] = a */ + if (self == av) { + value = array_slice(av, 0, av->ob_size); + av = (arrayobject*)value; + } + else { + Py_INCREF(value); + } + + for (cur = start, i = 0; i < slicelength; + cur += step, i++) { + memcpy(self->ob_item + cur*itemsize, + av->ob_item + i*itemsize, + itemsize); + } + + Py_DECREF(value); + + return 0; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "list indices must be integers"); + return -1; + } +} + +static PyMappingMethods array_as_mapping = { + (inquiry)array_length, + (binaryfunc)array_subscr, + (objobjargproc)array_ass_subscr +}; + +static int +array_buffer_getreadbuf(arrayobject *self, int index, const void **ptr) +{ + if ( index != 0 ) { + PyErr_SetString(PyExc_SystemError, + "Accessing non-existent array segment"); + return -1; + } + *ptr = (void *)self->ob_item; + return self->ob_size*self->ob_descr->itemsize; +} + +static int +array_buffer_getwritebuf(arrayobject *self, int index, const void **ptr) +{ + if ( index != 0 ) { + PyErr_SetString(PyExc_SystemError, + "Accessing non-existent array segment"); + return -1; + } + *ptr = (void *)self->ob_item; + return self->ob_size*self->ob_descr->itemsize; +} + +static int +array_buffer_getsegcount(arrayobject *self, int *lenp) +{ + if ( lenp ) + *lenp = self->ob_size*self->ob_descr->itemsize; + return 1; +} + +static PySequenceMethods array_as_sequence = { + (inquiry)array_length, /*sq_length*/ + (binaryfunc)array_concat, /*sq_concat*/ + (intargfunc)array_repeat, /*sq_repeat*/ + (intargfunc)array_item, /*sq_item*/ + (intintargfunc)array_slice, /*sq_slice*/ + (intobjargproc)array_ass_item, /*sq_ass_item*/ + (intintobjargproc)array_ass_slice, /*sq_ass_slice*/ + (objobjproc)array_contains, /*sq_contains*/ + (binaryfunc)array_inplace_concat, /*sq_inplace_concat*/ + (intargfunc)array_inplace_repeat /*sq_inplace_repeat*/ +}; + +static PyBufferProcs array_as_buffer = { + (getreadbufferproc)array_buffer_getreadbuf, + (getwritebufferproc)array_buffer_getwritebuf, + (getsegcountproc)array_buffer_getsegcount, +}; + +static PyObject * +array_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + char c; + PyObject *initial = NULL; + struct arraydescr *descr; + + if (kwds != NULL) { + int i = PyObject_Length(kwds); + if (i < 0) + return NULL; + else if (i > 0) { + PyErr_SetString(PyExc_TypeError, + "array.array constructor takes " + "no keyword arguments"); + return NULL; + } + } + + if (!PyArg_ParseTuple(args, "c|O:array", &c, &initial)) + return NULL; + + if (!(initial == NULL || PyList_Check(initial) + || PyString_Check(initial) || PyTuple_Check(initial) + || (c == 'u' && PyUnicode_Check(initial)))) { + PyErr_SetString(PyExc_TypeError, + "array initializer must be list or string"); + return NULL; + } + for (descr = descriptors; descr->typecode != '\0'; descr++) { + if (descr->typecode == c) { + PyObject *a; + int len; + + if (initial == NULL || !(PyList_Check(initial) + || PyTuple_Check(initial))) + len = 0; + else + len = PySequence_Size(initial); + + a = newarrayobject(type, len, descr); + if (a == NULL) + return NULL; + + if (len > 0) { + int i; + for (i = 0; i < len; i++) { + PyObject *v = + PySequence_GetItem(initial, i); + if (v == NULL) { + Py_DECREF(a); + return NULL; + } + if (setarrayitem(a, i, v) != 0) { + Py_DECREF(v); + Py_DECREF(a); + return NULL; + } + Py_DECREF(v); + } + } else if (initial != NULL && PyString_Check(initial)) { + PyObject *t_initial = Py_BuildValue("(O)", + initial); + PyObject *v = + array_fromstring((arrayobject *)a, + t_initial); + Py_DECREF(t_initial); + if (v == NULL) { + Py_DECREF(a); + return NULL; + } + Py_DECREF(v); +#ifdef Py_USING_UNICODE + } else if (initial != NULL && PyUnicode_Check(initial)) { + int n = PyUnicode_GET_DATA_SIZE(initial); + if (n > 0) { + arrayobject *self = (arrayobject *)a; + char *item = self->ob_item; + item = PyMem_Realloc(item, n); + if (item == NULL) { + PyErr_NoMemory(); + Py_DECREF(a); + return NULL; + } + self->ob_item = item; + self->ob_size = n / sizeof(Py_UNICODE); + memcpy(item, PyUnicode_AS_DATA(initial), n); + } +#endif + } + return a; + } + } + PyErr_SetString(PyExc_ValueError, + "bad typecode (must be c, b, B, u, h, H, i, I, l, L, f or d)"); + return NULL; +} + + +PyDoc_STRVAR(module_doc, +"This module defines an object type which can efficiently represent\n\ +an array of basic values: characters, integers, floating point\n\ +numbers. Arrays are sequence types and behave very much like lists,\n\ +except that the type of objects stored in them is constrained. The\n\ +type is specified at object creation time by using a type code, which\n\ +is a single character. The following type codes are defined:\n\ +\n\ + Type code C Type Minimum size in bytes \n\ + 'c' character 1 \n\ + 'b' signed integer 1 \n\ + 'B' unsigned integer 1 \n\ + 'u' Unicode character 2 \n\ + 'h' signed integer 2 \n\ + 'H' unsigned integer 2 \n\ + 'i' signed integer 2 \n\ + 'I' unsigned integer 2 \n\ + 'l' signed integer 4 \n\ + 'L' unsigned integer 4 \n\ + 'f' floating point 4 \n\ + 'd' floating point 8 \n\ +\n\ +The constructor is:\n\ +\n\ +array(typecode [, initializer]) -- create a new array\n\ +"); + +PyDoc_STRVAR(arraytype_doc, +"array(typecode [, initializer]) -> array\n\ +\n\ +Return a new array whose items are restricted by typecode, and\n\ +initialized from the optional initializer value, which must be a list\n\ +or a string.\n\ +\n\ +Arrays represent basic values and behave very much like lists, except\n\ +the type of objects stored in them is constrained.\n\ +\n\ +Methods:\n\ +\n\ +append() -- append a new item to the end of the array\n\ +buffer_info() -- return information giving the current memory info\n\ +byteswap() -- byteswap all the items of the array\n\ +count() -- return number of occurences of an object\n\ +extend() -- extend array by appending array elements\n\ +fromfile() -- read items from a file object\n\ +fromlist() -- append items from the list\n\ +fromstring() -- append items from the string\n\ +index() -- return index of first occurence of an object\n\ +insert() -- insert a new item into the array at a provided position\n\ +pop() -- remove and return item (default last)\n\ +read() -- DEPRECATED, use fromfile()\n\ +remove() -- remove first occurence of an object\n\ +reverse() -- reverse the order of the items in the array\n\ +tofile() -- write all items to a file object\n\ +tolist() -- return the array converted to an ordinary list\n\ +tostring() -- return the array converted to a string\n\ +write() -- DEPRECATED, use tofile()\n\ +\n\ +Attributes:\n\ +\n\ +typecode -- the typecode character used to create the array\n\ +itemsize -- the length in bytes of one array item\n\ +"); + +static PyObject *array_iter(arrayobject *ao); + +static PyTypeObject Arraytype = { + PyObject_HEAD_INIT(NULL) + 0, + "array.array", + sizeof(arrayobject), + 0, + (destructor)array_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)array_repr, /* tp_repr */ + 0, /* tp_as _number*/ + &array_as_sequence, /* tp_as _sequence*/ + &array_as_mapping, /* tp_as _mapping*/ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + &array_as_buffer, /* tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + arraytype_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + array_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)array_iter, /* tp_iter */ + 0, /* tp_iternext */ + array_methods, /* tp_methods */ + 0, /* tp_members */ + array_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + array_new, /* tp_new */ + PyObject_Del, /* tp_free */ +}; + + +/*********************** Array Iterator **************************/ + +typedef struct { + PyObject_HEAD + long index; + arrayobject *ao; + PyObject * (*getitem)(struct arrayobject *, int); +} arrayiterobject; + +static PyTypeObject PyArrayIter_Type; + +#define PyArrayIter_Check(op) PyObject_TypeCheck(op, &PyArrayIter_Type) + +static PyObject * +array_iter(arrayobject *ao) +{ + arrayiterobject *it; + + if (!array_Check(ao)) { + PyErr_BadInternalCall(); + return NULL; + } + + it = PyObject_GC_New(arrayiterobject, &PyArrayIter_Type); + if (it == NULL) + return NULL; + + Py_INCREF(ao); + it->ao = ao; + it->index = 0; + it->getitem = ao->ob_descr->getitem; + PyObject_GC_Track(it); + return (PyObject *)it; +} + +static PyObject * +arrayiter_next(arrayiterobject *it) +{ + assert(PyArrayIter_Check(it)); + if (it->index < it->ao->ob_size) + return (*it->getitem)(it->ao, it->index++); + return NULL; +} + +static void +arrayiter_dealloc(arrayiterobject *it) +{ + PyObject_GC_UnTrack(it); + Py_XDECREF(it->ao); + PyObject_GC_Del(it); +} + +static int +arrayiter_traverse(arrayiterobject *it, visitproc visit, void *arg) +{ + if (it->ao != NULL) + return visit((PyObject *)(it->ao), arg); + return 0; +} + +static PyTypeObject PyArrayIter_Type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "arrayiterator", /* tp_name */ + sizeof(arrayiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)arrayiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)arrayiter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)arrayiter_next, /* tp_iternext */ + 0, /* tp_methods */ +}; + + +/*********************** Install Module **************************/ + +/* No functions in array module. */ +static PyMethodDef a_methods[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + + +PyMODINIT_FUNC +initarray(void) +{ + PyObject *m; + + Arraytype.ob_type = &PyType_Type; + PyArrayIter_Type.ob_type = &PyType_Type; + m = Py_InitModule3("array", a_methods, module_doc); + + Py_INCREF((PyObject *)&Arraytype); + PyModule_AddObject(m, "ArrayType", (PyObject *)&Arraytype); + Py_INCREF((PyObject *)&Arraytype); + PyModule_AddObject(m, "array", (PyObject *)&Arraytype); + /* No need to check the error here, the caller will do that */ +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/audioop.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/audioop.c new file mode 100644 index 00000000..e270f618 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/audioop.c @@ -0,0 +1,1381 @@ + +/* audioopmodule - Module to detect peak values in arrays */ + +#include "Python.h" + +#if SIZEOF_INT == 4 +typedef int Py_Int32; +typedef unsigned int Py_UInt32; +#else +#if SIZEOF_LONG == 4 +typedef long Py_Int32; +typedef unsigned long Py_UInt32; +#else +#error "No 4-byte integral type" +#endif +#endif + +#if defined(__CHAR_UNSIGNED__) +#if defined(signed) +/* This module currently does not work on systems where only unsigned + characters are available. Take it out of Setup. Sorry. */ +#endif +#endif + +/* Code shamelessly stolen from sox, +** (c) Craig Reese, Joe Campbell and Jeff Poskanzer 1989 */ + +#define MINLIN -32768 +#define MAXLIN 32767 +#define LINCLIP(x) do { if ( x < MINLIN ) x = MINLIN ; \ + else if ( x > MAXLIN ) x = MAXLIN; \ + } while ( 0 ) + +static unsigned char st_linear_to_ulaw(int sample); + +/* +** This macro converts from ulaw to 16 bit linear, faster. +** +** Jef Poskanzer +** 23 October 1989 +** +** Input: 8 bit ulaw sample +** Output: signed 16 bit linear sample +*/ +#define st_ulaw_to_linear(ulawbyte) ulaw_table[ulawbyte] + +static int ulaw_table[256] = { + -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, + -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, + -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, + -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0 }; + +/* #define ZEROTRAP */ /* turn on the trap as per the MIL-STD */ +#define BIAS 0x84 /* define the add-in bias for 16 bit samples */ +#define CLIP 32635 + +static unsigned char +st_linear_to_ulaw(int sample) +{ + static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; + int sign, exponent, mantissa; + unsigned char ulawbyte; + + /* Get the sample into sign-magnitude. */ + sign = (sample >> 8) & 0x80; /* set aside the sign */ + if ( sign != 0 ) sample = -sample; /* get magnitude */ + if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[( sample >> 7 ) & 0xFF]; + mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F; + ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa ); +#ifdef ZEROTRAP + if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */ +#endif + + return ulawbyte; +} +/* End of code taken from sox */ + +/* Intel ADPCM step variation table */ +static int indexTable[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8, +}; + +static int stepsizeTable[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 +}; + +#define CHARP(cp, i) ((signed char *)(cp+i)) +#define SHORTP(cp, i) ((short *)(cp+i)) +#define LONGP(cp, i) ((Py_Int32 *)(cp+i)) + + + +static PyObject *AudioopError; + +static PyObject * +audioop_getsample(PyObject *self, PyObject *args) +{ + signed char *cp; + int len, size, val = 0; + int i; + + if ( !PyArg_Parse(args, "(s#ii)", &cp, &len, &size, &i) ) + return 0; + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + if ( i < 0 || i >= len/size ) { + PyErr_SetString(AudioopError, "Index out of range"); + return 0; + } + if ( size == 1 ) val = (int)*CHARP(cp, i); + else if ( size == 2 ) val = (int)*SHORTP(cp, i*2); + else if ( size == 4 ) val = (int)*LONGP(cp, i*4); + return PyInt_FromLong(val); +} + +static PyObject * +audioop_max(PyObject *self, PyObject *args) +{ + signed char *cp; + int len, size, val = 0; + int i; + int max = 0; + + if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) + return 0; + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + for ( i=0; i max ) max = val; + } + return PyInt_FromLong(max); +} + +static PyObject * +audioop_minmax(PyObject *self, PyObject *args) +{ + signed char *cp; + int len, size, val = 0; + int i; + int min = 0x7fffffff, max = -0x7fffffff; + + if (!PyArg_Parse(args, "(s#i)", &cp, &len, &size)) + return NULL; + if (size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return NULL; + } + for (i = 0; i < len; i += size) { + if (size == 1) val = (int) *CHARP(cp, i); + else if (size == 2) val = (int) *SHORTP(cp, i); + else if (size == 4) val = (int) *LONGP(cp, i); + if (val > max) max = val; + if (val < min) min = val; + } + return Py_BuildValue("(ii)", min, max); +} + +static PyObject * +audioop_avg(PyObject *self, PyObject *args) +{ + signed char *cp; + int len, size, val = 0; + int i; + double avg = 0.0; + + if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) + return 0; + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + for ( i=0; i n, and let all sums be over i from 0 to n-1. +** +** Now, for each j in {0..N-n} we compute a factor fj so that -fj*R matches A +** as good as possible, i.e. sum( (A[j+i]+fj*R[i])^2 ) is minimal. This +** equation gives fj = sum( A[j+i]R[i] ) / sum(R[i]^2). +** +** Next, we compute the relative distance between the original signal and +** the modified signal and minimize that over j: +** vj = sum( (A[j+i]-fj*R[i])^2 ) / sum( A[j+i]^2 ) => +** vj = ( sum(A[j+i]^2)*sum(R[i]^2) - sum(A[j+i]R[i])^2 ) / sum( A[j+i]^2 ) +** +** In the code variables correspond as follows: +** cp1 A +** cp2 R +** len1 N +** len2 n +** aj_m1 A[j-1] +** aj_lm1 A[j+n-1] +** sum_ri_2 sum(R[i]^2) +** sum_aij_2 sum(A[i+j]^2) +** sum_aij_ri sum(A[i+j]R[i]) +** +** sum_ri is calculated once, sum_aij_2 is updated each step and sum_aij_ri +** is completely recalculated each step. +*/ +static PyObject * +audioop_findfit(PyObject *self, PyObject *args) +{ + short *cp1, *cp2; + int len1, len2; + int j, best_j; + double aj_m1, aj_lm1; + double sum_ri_2, sum_aij_2, sum_aij_ri, result, best_result, factor; + + if ( !PyArg_Parse(args, "(s#s#)", &cp1, &len1, &cp2, &len2) ) + return 0; + if ( len1 & 1 || len2 & 1 ) { + PyErr_SetString(AudioopError, "Strings should be even-sized"); + return 0; + } + len1 >>= 1; + len2 >>= 1; + + if ( len1 < len2 ) { + PyErr_SetString(AudioopError, "First sample should be longer"); + return 0; + } + sum_ri_2 = _sum2(cp2, cp2, len2); + sum_aij_2 = _sum2(cp1, cp1, len2); + sum_aij_ri = _sum2(cp1, cp2, len2); + + result = (sum_ri_2*sum_aij_2 - sum_aij_ri*sum_aij_ri) / sum_aij_2; + + best_result = result; + best_j = 0; + j = 0; + + for ( j=1; j<=len1-len2; j++) { + aj_m1 = (double)cp1[j-1]; + aj_lm1 = (double)cp1[j+len2-1]; + + sum_aij_2 = sum_aij_2 + aj_lm1*aj_lm1 - aj_m1*aj_m1; + sum_aij_ri = _sum2(cp1+j, cp2, len2); + + result = (sum_ri_2*sum_aij_2 - sum_aij_ri*sum_aij_ri) + / sum_aij_2; + + if ( result < best_result ) { + best_result = result; + best_j = j; + } + + } + + factor = _sum2(cp1+best_j, cp2, len2) / sum_ri_2; + + return Py_BuildValue("(if)", best_j, factor); +} + +/* +** findfactor finds a factor f so that the energy in A-fB is minimal. +** See the comment for findfit for details. +*/ +static PyObject * +audioop_findfactor(PyObject *self, PyObject *args) +{ + short *cp1, *cp2; + int len1, len2; + double sum_ri_2, sum_aij_ri, result; + + if ( !PyArg_Parse(args, "(s#s#)", &cp1, &len1, &cp2, &len2) ) + return 0; + if ( len1 & 1 || len2 & 1 ) { + PyErr_SetString(AudioopError, "Strings should be even-sized"); + return 0; + } + if ( len1 != len2 ) { + PyErr_SetString(AudioopError, "Samples should be same size"); + return 0; + } + len2 >>= 1; + sum_ri_2 = _sum2(cp2, cp2, len2); + sum_aij_ri = _sum2(cp1, cp2, len2); + + result = sum_aij_ri / sum_ri_2; + + return PyFloat_FromDouble(result); +} + +/* +** findmax returns the index of the n-sized segment of the input sample +** that contains the most energy. +*/ +static PyObject * +audioop_findmax(PyObject *self, PyObject *args) +{ + short *cp1; + int len1, len2; + int j, best_j; + double aj_m1, aj_lm1; + double result, best_result; + + if ( !PyArg_Parse(args, "(s#i)", &cp1, &len1, &len2) ) + return 0; + if ( len1 & 1 ) { + PyErr_SetString(AudioopError, "Strings should be even-sized"); + return 0; + } + len1 >>= 1; + + if ( len1 < len2 ) { + PyErr_SetString(AudioopError, "Input sample should be longer"); + return 0; + } + + result = _sum2(cp1, cp1, len2); + + best_result = result; + best_j = 0; + j = 0; + + for ( j=1; j<=len1-len2; j++) { + aj_m1 = (double)cp1[j-1]; + aj_lm1 = (double)cp1[j+len2-1]; + + result = result + aj_lm1*aj_lm1 - aj_m1*aj_m1; + + if ( result > best_result ) { + best_result = result; + best_j = j; + } + + } + + return PyInt_FromLong(best_j); +} + +static PyObject * +audioop_avgpp(PyObject *self, PyObject *args) +{ + signed char *cp; + int len, size, val = 0, prevval = 0, prevextremevalid = 0, + prevextreme = 0; + int i; + double avg = 0.0; + int diff, prevdiff, extremediff, nextreme = 0; + + if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) + return 0; + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + /* Compute first delta value ahead. Also automatically makes us + ** skip the first extreme value + */ + if ( size == 1 ) prevval = (int)*CHARP(cp, 0); + else if ( size == 2 ) prevval = (int)*SHORTP(cp, 0); + else if ( size == 4 ) prevval = (int)*LONGP(cp, 0); + if ( size == 1 ) val = (int)*CHARP(cp, size); + else if ( size == 2 ) val = (int)*SHORTP(cp, size); + else if ( size == 4 ) val = (int)*LONGP(cp, size); + prevdiff = val - prevval; + + for ( i=size; i max ) + max = extremediff; + } + prevextremevalid = 1; + prevextreme = prevval; + } + prevval = val; + if ( diff != 0 ) + prevdiff = diff; + } + return PyInt_FromLong(max); +} + +static PyObject * +audioop_cross(PyObject *self, PyObject *args) +{ + signed char *cp; + int len, size, val = 0; + int i; + int prevval, ncross; + + if ( !PyArg_Parse(args, "(s#i)", &cp, &len, &size) ) + return 0; + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + ncross = -1; + prevval = 17; /* Anything <> 0,1 */ + for ( i=0; i> 7; + else if ( size == 2 ) val = ((int)*SHORTP(cp, i)) >> 15; + else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 31; + val = val & 1; + if ( val != prevval ) ncross++; + prevval = val; + } + return PyInt_FromLong(ncross); +} + +static PyObject * +audioop_mul(PyObject *self, PyObject *args) +{ + signed char *cp, *ncp; + int len, size, val = 0; + double factor, fval, maxval; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#id)", &cp, &len, &size, &factor ) ) + return 0; + + if ( size == 1 ) maxval = (double) 0x7f; + else if ( size == 2 ) maxval = (double) 0x7fff; + else if ( size == 4 ) maxval = (double) 0x7fffffff; + else { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); + + + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = (int)*CHARP(cp, i); + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = (int)*LONGP(cp, i); + fval = (double)val*factor; + if ( fval > maxval ) fval = maxval; + else if ( fval < -maxval ) fval = -maxval; + val = (int)fval; + if ( size == 1 ) *CHARP(ncp, i) = (signed char)val; + else if ( size == 2 ) *SHORTP(ncp, i) = (short)val; + else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)val; + } + return rv; +} + +static PyObject * +audioop_tomono(PyObject *self, PyObject *args) +{ + signed char *cp, *ncp; + int len, size, val1 = 0, val2 = 0; + double fac1, fac2, fval, maxval; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#idd)", &cp, &len, &size, &fac1, &fac2 ) ) + return 0; + + if ( size == 1 ) maxval = (double) 0x7f; + else if ( size == 2 ) maxval = (double) 0x7fff; + else if ( size == 4 ) maxval = (double) 0x7fffffff; + else { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len/2); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); + + + for ( i=0; i < len; i += size*2 ) { + if ( size == 1 ) val1 = (int)*CHARP(cp, i); + else if ( size == 2 ) val1 = (int)*SHORTP(cp, i); + else if ( size == 4 ) val1 = (int)*LONGP(cp, i); + if ( size == 1 ) val2 = (int)*CHARP(cp, i+1); + else if ( size == 2 ) val2 = (int)*SHORTP(cp, i+2); + else if ( size == 4 ) val2 = (int)*LONGP(cp, i+4); + fval = (double)val1*fac1 + (double)val2*fac2; + if ( fval > maxval ) fval = maxval; + else if ( fval < -maxval ) fval = -maxval; + val1 = (int)fval; + if ( size == 1 ) *CHARP(ncp, i/2) = (signed char)val1; + else if ( size == 2 ) *SHORTP(ncp, i/2) = (short)val1; + else if ( size == 4 ) *LONGP(ncp, i/2)= (Py_Int32)val1; + } + return rv; +} + +static PyObject * +audioop_tostereo(PyObject *self, PyObject *args) +{ + signed char *cp, *ncp; + int len, size, val1, val2, val = 0; + double fac1, fac2, fval, maxval; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#idd)", &cp, &len, &size, &fac1, &fac2 ) ) + return 0; + + if ( size == 1 ) maxval = (double) 0x7f; + else if ( size == 2 ) maxval = (double) 0x7fff; + else if ( size == 4 ) maxval = (double) 0x7fffffff; + else { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len*2); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); + + + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = (int)*CHARP(cp, i); + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = (int)*LONGP(cp, i); + + fval = (double)val*fac1; + if ( fval > maxval ) fval = maxval; + else if ( fval < -maxval ) fval = -maxval; + val1 = (int)fval; + + fval = (double)val*fac2; + if ( fval > maxval ) fval = maxval; + else if ( fval < -maxval ) fval = -maxval; + val2 = (int)fval; + + if ( size == 1 ) *CHARP(ncp, i*2) = (signed char)val1; + else if ( size == 2 ) *SHORTP(ncp, i*2) = (short)val1; + else if ( size == 4 ) *LONGP(ncp, i*2) = (Py_Int32)val1; + + if ( size == 1 ) *CHARP(ncp, i*2+1) = (signed char)val2; + else if ( size == 2 ) *SHORTP(ncp, i*2+2) = (short)val2; + else if ( size == 4 ) *LONGP(ncp, i*2+4) = (Py_Int32)val2; + } + return rv; +} + +static PyObject * +audioop_add(PyObject *self, PyObject *args) +{ + signed char *cp1, *cp2, *ncp; + int len1, len2, size, val1 = 0, val2 = 0, maxval, newval; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#s#i)", + &cp1, &len1, &cp2, &len2, &size ) ) + return 0; + + if ( len1 != len2 ) { + PyErr_SetString(AudioopError, "Lengths should be the same"); + return 0; + } + + if ( size == 1 ) maxval = 0x7f; + else if ( size == 2 ) maxval = 0x7fff; + else if ( size == 4 ) maxval = 0x7fffffff; + else { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len1); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); + + for ( i=0; i < len1; i += size ) { + if ( size == 1 ) val1 = (int)*CHARP(cp1, i); + else if ( size == 2 ) val1 = (int)*SHORTP(cp1, i); + else if ( size == 4 ) val1 = (int)*LONGP(cp1, i); + + if ( size == 1 ) val2 = (int)*CHARP(cp2, i); + else if ( size == 2 ) val2 = (int)*SHORTP(cp2, i); + else if ( size == 4 ) val2 = (int)*LONGP(cp2, i); + + newval = val1 + val2; + /* truncate in case of overflow */ + if (newval > maxval) newval = maxval; + else if (newval < -maxval) newval = -maxval; + else if (size == 4 && (newval^val1) < 0 && (newval^val2) < 0) + newval = val1 > 0 ? maxval : - maxval; + + if ( size == 1 ) *CHARP(ncp, i) = (signed char)newval; + else if ( size == 2 ) *SHORTP(ncp, i) = (short)newval; + else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)newval; + } + return rv; +} + +static PyObject * +audioop_bias(PyObject *self, PyObject *args) +{ + signed char *cp, *ncp; + int len, size, val = 0; + PyObject *rv; + int i; + int bias; + + if ( !PyArg_Parse(args, "(s#ii)", + &cp, &len, &size , &bias) ) + return 0; + + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); + + + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = (int)*CHARP(cp, i); + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = (int)*LONGP(cp, i); + + if ( size == 1 ) *CHARP(ncp, i) = (signed char)(val+bias); + else if ( size == 2 ) *SHORTP(ncp, i) = (short)(val+bias); + else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(val+bias); + } + return rv; +} + +static PyObject * +audioop_reverse(PyObject *self, PyObject *args) +{ + signed char *cp; + unsigned char *ncp; + int len, size, val = 0; + PyObject *rv; + int i, j; + + if ( !PyArg_Parse(args, "(s#i)", + &cp, &len, &size) ) + return 0; + + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; + + j = len - i - size; + + if ( size == 1 ) *CHARP(ncp, j) = (signed char)(val >> 8); + else if ( size == 2 ) *SHORTP(ncp, j) = (short)(val); + else if ( size == 4 ) *LONGP(ncp, j) = (Py_Int32)(val<<16); + } + return rv; +} + +static PyObject * +audioop_lin2lin(PyObject *self, PyObject *args) +{ + signed char *cp; + unsigned char *ncp; + int len, size, size2, val = 0; + PyObject *rv; + int i, j; + + if ( !PyArg_Parse(args, "(s#ii)", + &cp, &len, &size, &size2) ) + return 0; + + if ( (size != 1 && size != 2 && size != 4) || + (size2 != 1 && size2 != 2 && size2 != 4)) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, (len/size)*size2); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + for ( i=0, j=0; i < len; i += size, j += size2 ) { + if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; + + if ( size2 == 1 ) *CHARP(ncp, j) = (signed char)(val >> 8); + else if ( size2 == 2 ) *SHORTP(ncp, j) = (short)(val); + else if ( size2 == 4 ) *LONGP(ncp, j) = (Py_Int32)(val<<16); + } + return rv; +} + +static int +gcd(int a, int b) +{ + while (b > 0) { + int tmp = a % b; + a = b; + b = tmp; + } + return a; +} + +static PyObject * +audioop_ratecv(PyObject *self, PyObject *args) +{ + char *cp, *ncp; + int len, size, nchannels, inrate, outrate, weightA, weightB; + int chan, d, *prev_i, *cur_i, cur_o; + PyObject *state, *samps, *str, *rv = NULL; + int bytes_per_frame; + + weightA = 1; + weightB = 0; + if (!PyArg_ParseTuple(args, "s#iiiiO|ii:ratecv", &cp, &len, &size, &nchannels, + &inrate, &outrate, &state, &weightA, &weightB)) + return NULL; + if (size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return NULL; + } + if (nchannels < 1) { + PyErr_SetString(AudioopError, "# of channels should be >= 1"); + return NULL; + } + bytes_per_frame = size * nchannels; + if (bytes_per_frame / nchannels != size) { + /* This overflow test is rigorously correct because + both multiplicands are >= 1. Use the argument names + from the docs for the error msg. */ + PyErr_SetString(PyExc_OverflowError, + "width * nchannels too big for a C int"); + return NULL; + } + if (weightA < 1 || weightB < 0) { + PyErr_SetString(AudioopError, + "weightA should be >= 1, weightB should be >= 0"); + return NULL; + } + if (len % bytes_per_frame != 0) { + PyErr_SetString(AudioopError, "not a whole number of frames"); + return NULL; + } + if (inrate <= 0 || outrate <= 0) { + PyErr_SetString(AudioopError, "sampling rate not > 0"); + return NULL; + } + /* divide inrate and outrate by their greatest common divisor */ + d = gcd(inrate, outrate); + inrate /= d; + outrate /= d; + + prev_i = (int *) malloc(nchannels * sizeof(int)); + cur_i = (int *) malloc(nchannels * sizeof(int)); + if (prev_i == NULL || cur_i == NULL) { + (void) PyErr_NoMemory(); + goto exit; + } + + len /= bytes_per_frame; /* # of frames */ + + if (state == Py_None) { + d = -outrate; + for (chan = 0; chan < nchannels; chan++) + prev_i[chan] = cur_i[chan] = 0; + } + else { + if (!PyArg_ParseTuple(state, + "iO!;audioop.ratecv: illegal state argument", + &d, &PyTuple_Type, &samps)) + goto exit; + if (PyTuple_Size(samps) != nchannels) { + PyErr_SetString(AudioopError, + "illegal state argument"); + goto exit; + } + for (chan = 0; chan < nchannels; chan++) { + if (!PyArg_ParseTuple(PyTuple_GetItem(samps, chan), + "ii:ratecv",&prev_i[chan],&cur_i[chan])) + goto exit; + } + } + + /* str <- Space for the output buffer. */ + { + /* There are len input frames, so we need (mathematically) + ceiling(len*outrate/inrate) output frames, and each frame + requires bytes_per_frame bytes. Computing this + without spurious overflow is the challenge; we can + settle for a reasonable upper bound, though. */ + int ceiling; /* the number of output frames */ + int nbytes; /* the number of output bytes needed */ + int q = len / inrate; + /* Now len = q * inrate + r exactly (with r = len % inrate), + and this is less than q * inrate + inrate = (q+1)*inrate. + So a reasonable upper bound on len*outrate/inrate is + ((q+1)*inrate)*outrate/inrate = + (q+1)*outrate. + */ + ceiling = (q+1) * outrate; + nbytes = ceiling * bytes_per_frame; + /* See whether anything overflowed; if not, get the space. */ + if (q+1 < 0 || + ceiling / outrate != q+1 || + nbytes / bytes_per_frame != ceiling) + str = NULL; + else + str = PyString_FromStringAndSize(NULL, nbytes); + + if (str == NULL) { + PyErr_SetString(PyExc_MemoryError, + "not enough memory for output buffer"); + goto exit; + } + } + ncp = PyString_AsString(str); + + for (;;) { + while (d < 0) { + if (len == 0) { + samps = PyTuple_New(nchannels); + for (chan = 0; chan < nchannels; chan++) + PyTuple_SetItem(samps, chan, + Py_BuildValue("(ii)", + prev_i[chan], + cur_i[chan])); + if (PyErr_Occurred()) + goto exit; + len = ncp - PyString_AsString(str); + if (len == 0) { + /*don't want to resize to zero length*/ + rv = PyString_FromStringAndSize("", 0); + Py_DECREF(str); + str = rv; + } else if (_PyString_Resize(&str, len) < 0) + goto exit; + rv = Py_BuildValue("(O(iO))", str, d, samps); + Py_DECREF(samps); + Py_DECREF(str); + goto exit; /* return rv */ + } + for (chan = 0; chan < nchannels; chan++) { + prev_i[chan] = cur_i[chan]; + if (size == 1) + cur_i[chan] = ((int)*CHARP(cp, 0)) << 8; + else if (size == 2) + cur_i[chan] = (int)*SHORTP(cp, 0); + else if (size == 4) + cur_i[chan] = ((int)*LONGP(cp, 0)) >> 16; + cp += size; + /* implements a simple digital filter */ + cur_i[chan] = + (weightA * cur_i[chan] + + weightB * prev_i[chan]) / + (weightA + weightB); + } + len--; + d += outrate; + } + while (d >= 0) { + for (chan = 0; chan < nchannels; chan++) { + cur_o = (prev_i[chan] * d + + cur_i[chan] * (outrate - d)) / + outrate; + if (size == 1) + *CHARP(ncp, 0) = (signed char)(cur_o >> 8); + else if (size == 2) + *SHORTP(ncp, 0) = (short)(cur_o); + else if (size == 4) + *LONGP(ncp, 0) = (Py_Int32)(cur_o<<16); + ncp += size; + } + d -= inrate; + } + } + exit: + if (prev_i != NULL) + free(prev_i); + if (cur_i != NULL) + free(cur_i); + return rv; +} + +static PyObject * +audioop_lin2ulaw(PyObject *self, PyObject *args) +{ + signed char *cp; + unsigned char *ncp; + int len, size, val = 0; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#i)", + &cp, &len, &size) ) + return 0; + + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len/size); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; + + *ncp++ = st_linear_to_ulaw(val); + } + return rv; +} + +static PyObject * +audioop_ulaw2lin(PyObject *self, PyObject *args) +{ + unsigned char *cp; + unsigned char cval; + signed char *ncp; + int len, size, val; + PyObject *rv; + int i; + + if ( !PyArg_Parse(args, "(s#i)", + &cp, &len, &size) ) + return 0; + + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len*size); + if ( rv == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(rv); + + for ( i=0; i < len*size; i += size ) { + cval = *cp++; + val = st_ulaw_to_linear(cval); + + if ( size == 1 ) *CHARP(ncp, i) = (signed char)(val >> 8); + else if ( size == 2 ) *SHORTP(ncp, i) = (short)(val); + else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(val<<16); + } + return rv; +} + +static PyObject * +audioop_lin2adpcm(PyObject *self, PyObject *args) +{ + signed char *cp; + signed char *ncp; + int len, size, val = 0, step, valpred, delta, + index, sign, vpdiff, diff; + PyObject *rv, *state, *str; + int i, outputbuffer = 0, bufferstep; + + if ( !PyArg_Parse(args, "(s#iO)", + &cp, &len, &size, &state) ) + return 0; + + + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + str = PyString_FromStringAndSize(NULL, len/(size*2)); + if ( str == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(str); + + /* Decode state, should have (value, step) */ + if ( state == Py_None ) { + /* First time, it seems. Set defaults */ + valpred = 0; + step = 7; + index = 0; + } else if ( !PyArg_Parse(state, "(ii)", &valpred, &index) ) + return 0; + + step = stepsizeTable[index]; + bufferstep = 1; + + for ( i=0; i < len; i += size ) { + if ( size == 1 ) val = ((int)*CHARP(cp, i)) << 8; + else if ( size == 2 ) val = (int)*SHORTP(cp, i); + else if ( size == 4 ) val = ((int)*LONGP(cp, i)) >> 16; + + /* Step 1 - compute difference with previous value */ + diff = val - valpred; + sign = (diff < 0) ? 8 : 0; + if ( sign ) diff = (-diff); + + /* Step 2 - Divide and clamp */ + /* Note: + ** This code *approximately* computes: + ** delta = diff*4/step; + ** vpdiff = (delta+0.5)*step/4; + ** but in shift step bits are dropped. The net result of this + ** is that even if you have fast mul/div hardware you cannot + ** put it to good use since the fixup would be too expensive. + */ + delta = 0; + vpdiff = (step >> 3); + + if ( diff >= step ) { + delta = 4; + diff -= step; + vpdiff += step; + } + step >>= 1; + if ( diff >= step ) { + delta |= 2; + diff -= step; + vpdiff += step; + } + step >>= 1; + if ( diff >= step ) { + delta |= 1; + vpdiff += step; + } + + /* Step 3 - Update previous value */ + if ( sign ) + valpred -= vpdiff; + else + valpred += vpdiff; + + /* Step 4 - Clamp previous value to 16 bits */ + if ( valpred > 32767 ) + valpred = 32767; + else if ( valpred < -32768 ) + valpred = -32768; + + /* Step 5 - Assemble value, update index and step values */ + delta |= sign; + + index += indexTable[delta]; + if ( index < 0 ) index = 0; + if ( index > 88 ) index = 88; + step = stepsizeTable[index]; + + /* Step 6 - Output value */ + if ( bufferstep ) { + outputbuffer = (delta << 4) & 0xf0; + } else { + *ncp++ = (delta & 0x0f) | outputbuffer; + } + bufferstep = !bufferstep; + } + rv = Py_BuildValue("(O(ii))", str, valpred, index); + Py_DECREF(str); + return rv; +} + +static PyObject * +audioop_adpcm2lin(PyObject *self, PyObject *args) +{ + signed char *cp; + signed char *ncp; + int len, size, valpred, step, delta, index, sign, vpdiff; + PyObject *rv, *str, *state; + int i, inputbuffer = 0, bufferstep; + + if ( !PyArg_Parse(args, "(s#iO)", + &cp, &len, &size, &state) ) + return 0; + + if ( size != 1 && size != 2 && size != 4) { + PyErr_SetString(AudioopError, "Size should be 1, 2 or 4"); + return 0; + } + + /* Decode state, should have (value, step) */ + if ( state == Py_None ) { + /* First time, it seems. Set defaults */ + valpred = 0; + step = 7; + index = 0; + } else if ( !PyArg_Parse(state, "(ii)", &valpred, &index) ) + return 0; + + str = PyString_FromStringAndSize(NULL, len*size*2); + if ( str == 0 ) + return 0; + ncp = (signed char *)PyString_AsString(str); + + step = stepsizeTable[index]; + bufferstep = 0; + + for ( i=0; i < len*size*2; i += size ) { + /* Step 1 - get the delta value and compute next index */ + if ( bufferstep ) { + delta = inputbuffer & 0xf; + } else { + inputbuffer = *cp++; + delta = (inputbuffer >> 4) & 0xf; + } + + bufferstep = !bufferstep; + + /* Step 2 - Find new index value (for later) */ + index += indexTable[delta]; + if ( index < 0 ) index = 0; + if ( index > 88 ) index = 88; + + /* Step 3 - Separate sign and magnitude */ + sign = delta & 8; + delta = delta & 7; + + /* Step 4 - Compute difference and new predicted value */ + /* + ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment + ** in adpcm_coder. + */ + vpdiff = step >> 3; + if ( delta & 4 ) vpdiff += step; + if ( delta & 2 ) vpdiff += step>>1; + if ( delta & 1 ) vpdiff += step>>2; + + if ( sign ) + valpred -= vpdiff; + else + valpred += vpdiff; + + /* Step 5 - clamp output value */ + if ( valpred > 32767 ) + valpred = 32767; + else if ( valpred < -32768 ) + valpred = -32768; + + /* Step 6 - Update step value */ + step = stepsizeTable[index]; + + /* Step 6 - Output value */ + if ( size == 1 ) *CHARP(ncp, i) = (signed char)(valpred >> 8); + else if ( size == 2 ) *SHORTP(ncp, i) = (short)(valpred); + else if ( size == 4 ) *LONGP(ncp, i) = (Py_Int32)(valpred<<16); + } + + rv = Py_BuildValue("(O(ii))", str, valpred, index); + Py_DECREF(str); + return rv; +} + +static PyMethodDef audioop_methods[] = { + { "max", audioop_max, METH_OLDARGS }, + { "minmax", audioop_minmax, METH_OLDARGS }, + { "avg", audioop_avg, METH_OLDARGS }, + { "maxpp", audioop_maxpp, METH_OLDARGS }, + { "avgpp", audioop_avgpp, METH_OLDARGS }, + { "rms", audioop_rms, METH_OLDARGS }, + { "findfit", audioop_findfit, METH_OLDARGS }, + { "findmax", audioop_findmax, METH_OLDARGS }, + { "findfactor", audioop_findfactor, METH_OLDARGS }, + { "cross", audioop_cross, METH_OLDARGS }, + { "mul", audioop_mul, METH_OLDARGS }, + { "add", audioop_add, METH_OLDARGS }, + { "bias", audioop_bias, METH_OLDARGS }, + { "ulaw2lin", audioop_ulaw2lin, METH_OLDARGS }, + { "lin2ulaw", audioop_lin2ulaw, METH_OLDARGS }, + { "lin2lin", audioop_lin2lin, METH_OLDARGS }, + { "adpcm2lin", audioop_adpcm2lin, METH_OLDARGS }, + { "lin2adpcm", audioop_lin2adpcm, METH_OLDARGS }, + { "tomono", audioop_tomono, METH_OLDARGS }, + { "tostereo", audioop_tostereo, METH_OLDARGS }, + { "getsample", audioop_getsample, METH_OLDARGS }, + { "reverse", audioop_reverse, METH_OLDARGS }, + { "ratecv", audioop_ratecv, METH_VARARGS }, + { 0, 0 } +}; + +PyMODINIT_FUNC +initaudioop(void) +{ + PyObject *m, *d; + m = Py_InitModule("audioop", audioop_methods); + d = PyModule_GetDict(m); + AudioopError = PyErr_NewException("audioop.error", NULL, NULL); + if (AudioopError != NULL) + PyDict_SetItemString(d,"error",AudioopError); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/binascii.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/binascii.c new file mode 100644 index 00000000..15d5e785 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/binascii.c @@ -0,0 +1,1337 @@ +/* +** Routines to represent binary data in ASCII and vice-versa +** +** This module currently supports the following encodings: +** uuencode: +** each line encodes 45 bytes (except possibly the last) +** First char encodes (binary) length, rest data +** each char encodes 6 bits, as follows: +** binary: 01234567 abcdefgh ijklmnop +** ascii: 012345 67abcd efghij klmnop +** ASCII encoding method is "excess-space": 000000 is encoded as ' ', etc. +** short binary data is zero-extended (so the bits are always in the +** right place), this does *not* reflect in the length. +** base64: +** Line breaks are insignificant, but lines are at most 76 chars +** each char encodes 6 bits, in similar order as uucode/hqx. Encoding +** is done via a table. +** Short binary data is filled (in ASCII) with '='. +** hqx: +** File starts with introductory text, real data starts and ends +** with colons. +** Data consists of three similar parts: info, datafork, resourcefork. +** Each part is protected (at the end) with a 16-bit crc +** The binary data is run-length encoded, and then ascii-fied: +** binary: 01234567 abcdefgh ijklmnop +** ascii: 012345 67abcd efghij klmnop +** ASCII encoding is table-driven, see the code. +** Short binary data results in the runt ascii-byte being output with +** the bits in the right place. +** +** While I was reading dozens of programs that encode or decode the formats +** here (documentation? hihi:-) I have formulated Jansen's Observation: +** +** Programs that encode binary data in ASCII are written in +** such a style that they are as unreadable as possible. Devices used +** include unnecessary global variables, burying important tables +** in unrelated sourcefiles, putting functions in include files, +** using seemingly-descriptive variable names for different purposes, +** calls to empty subroutines and a host of others. +** +** I have attempted to break with this tradition, but I guess that that +** does make the performance sub-optimal. Oh well, too bad... +** +** Jack Jansen, CWI, July 1995. +** +** Added support for quoted-printable encoding, based on rfc 1521 et al +** quoted-printable encoding specifies that non printable characters (anything +** below 32 and above 126) be encoded as =XX where XX is the hexadecimal value +** of the character. It also specifies some other behavior to enable 8bit data +** in a mail message with little difficulty (maximum line sizes, protecting +** some cases of whitespace, etc). +** +** Brandon Long, September 2001. +*/ + + +#include "Python.h" + +static PyObject *Error; +static PyObject *Incomplete; + +/* +** hqx lookup table, ascii->binary. +*/ + +#define RUNCHAR 0x90 + +#define DONE 0x7F +#define SKIP 0x7E +#define FAIL 0x7D + +static unsigned char table_a2b_hqx[256] = { +/* ^@ ^A ^B ^C ^D ^E ^F ^G */ +/* 0*/ FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, +/* \b \t \n ^K ^L \r ^N ^O */ +/* 1*/ FAIL, FAIL, SKIP, FAIL, FAIL, SKIP, FAIL, FAIL, +/* ^P ^Q ^R ^S ^T ^U ^V ^W */ +/* 2*/ FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, +/* ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */ +/* 3*/ FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, +/* ! " # $ % & ' */ +/* 4*/ FAIL, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, +/* ( ) * + , - . / */ +/* 5*/ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, FAIL, FAIL, +/* 0 1 2 3 4 5 6 7 */ +/* 6*/ 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, FAIL, +/* 8 9 : ; < = > ? */ +/* 7*/ 0x14, 0x15, DONE, FAIL, FAIL, FAIL, FAIL, FAIL, +/* @ A B C D E F G */ +/* 8*/ 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, +/* H I J K L M N O */ +/* 9*/ 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, FAIL, +/* P Q R S T U V W */ +/*10*/ 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, FAIL, +/* X Y Z [ \ ] ^ _ */ +/*11*/ 0x2C, 0x2D, 0x2E, 0x2F, FAIL, FAIL, FAIL, FAIL, +/* ` a b c d e f g */ +/*12*/ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, FAIL, +/* h i j k l m n o */ +/*13*/ 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, FAIL, FAIL, +/* p q r s t u v w */ +/*14*/ 0x3D, 0x3E, 0x3F, FAIL, FAIL, FAIL, FAIL, FAIL, +/* x y z { | } ~ ^? */ +/*15*/ FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, +/*16*/ FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, + FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, +}; + +static unsigned char table_b2a_hqx[] = +"!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr"; + +static char table_a2b_base64[] = { + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, /* Note PAD->0 */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +}; + +#define BASE64_PAD '=' + +/* Max binary chunk size; limited only by available memory */ +#define BASE64_MAXBIN (INT_MAX/2 - sizeof(PyStringObject) - 3) + +static unsigned char table_b2a_base64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + + +static unsigned short crctab_hqx[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, +}; + +PyDoc_STRVAR(doc_a2b_uu, "(ascii) -> bin. Decode a line of uuencoded data"); + +static PyObject * +binascii_a2b_uu(PyObject *self, PyObject *args) +{ + unsigned char *ascii_data, *bin_data; + int leftbits = 0; + unsigned char this_ch; + unsigned int leftchar = 0; + PyObject *rv; + int ascii_len, bin_len; + + if ( !PyArg_ParseTuple(args, "t#:a2b_uu", &ascii_data, &ascii_len) ) + return NULL; + + /* First byte: binary data length (in bytes) */ + bin_len = (*ascii_data++ - ' ') & 077; + ascii_len--; + + /* Allocate the buffer */ + if ( (rv=PyString_FromStringAndSize(NULL, bin_len)) == NULL ) + return NULL; + bin_data = (unsigned char *)PyString_AsString(rv); + + for( ; bin_len > 0 ; ascii_len--, ascii_data++ ) { + this_ch = *ascii_data; + if ( this_ch == '\n' || this_ch == '\r' || ascii_len <= 0) { + /* + ** Whitespace. Assume some spaces got eaten at + ** end-of-line. (We check this later) + */ + this_ch = 0; + } else { + /* Check the character for legality + ** The 64 in stead of the expected 63 is because + ** there are a few uuencodes out there that use + ** '`' as zero instead of space. + */ + if ( this_ch < ' ' || this_ch > (' ' + 64)) { + PyErr_SetString(Error, "Illegal char"); + Py_DECREF(rv); + return NULL; + } + this_ch = (this_ch - ' ') & 077; + } + /* + ** Shift it in on the low end, and see if there's + ** a byte ready for output. + */ + leftchar = (leftchar << 6) | (this_ch); + leftbits += 6; + if ( leftbits >= 8 ) { + leftbits -= 8; + *bin_data++ = (leftchar >> leftbits) & 0xff; + leftchar &= ((1 << leftbits) - 1); + bin_len--; + } + } + /* + ** Finally, check that if there's anything left on the line + ** that it's whitespace only. + */ + while( ascii_len-- > 0 ) { + this_ch = *ascii_data++; + /* Extra '`' may be written as padding in some cases */ + if ( this_ch != ' ' && this_ch != ' '+64 && + this_ch != '\n' && this_ch != '\r' ) { + PyErr_SetString(Error, "Trailing garbage"); + Py_DECREF(rv); + return NULL; + } + } + return rv; +} + +PyDoc_STRVAR(doc_b2a_uu, "(bin) -> ascii. Uuencode line of data"); + +static PyObject * +binascii_b2a_uu(PyObject *self, PyObject *args) +{ + unsigned char *ascii_data, *bin_data; + int leftbits = 0; + unsigned char this_ch; + unsigned int leftchar = 0; + PyObject *rv; + int bin_len; + + if ( !PyArg_ParseTuple(args, "s#:b2a_uu", &bin_data, &bin_len) ) + return NULL; + if ( bin_len > 45 ) { + /* The 45 is a limit that appears in all uuencode's */ + PyErr_SetString(Error, "At most 45 bytes at once"); + return NULL; + } + + /* We're lazy and allocate to much (fixed up later) */ + if ( (rv=PyString_FromStringAndSize(NULL, bin_len*2)) == NULL ) + return NULL; + ascii_data = (unsigned char *)PyString_AsString(rv); + + /* Store the length */ + *ascii_data++ = ' ' + (bin_len & 077); + + for( ; bin_len > 0 || leftbits != 0 ; bin_len--, bin_data++ ) { + /* Shift the data (or padding) into our buffer */ + if ( bin_len > 0 ) /* Data */ + leftchar = (leftchar << 8) | *bin_data; + else /* Padding */ + leftchar <<= 8; + leftbits += 8; + + /* See if there are 6-bit groups ready */ + while ( leftbits >= 6 ) { + this_ch = (leftchar >> (leftbits-6)) & 0x3f; + leftbits -= 6; + *ascii_data++ = this_ch + ' '; + } + } + *ascii_data++ = '\n'; /* Append a courtesy newline */ + + _PyString_Resize(&rv, (ascii_data - + (unsigned char *)PyString_AsString(rv))); + return rv; +} + + +static int +binascii_find_valid(unsigned char *s, int slen, int num) +{ + /* Finds & returns the (num+1)th + ** valid character for base64, or -1 if none. + */ + + int ret = -1; + unsigned char c, b64val; + + while ((slen > 0) && (ret == -1)) { + c = *s; + b64val = table_a2b_base64[c & 0x7f]; + if ( ((c <= 0x7f) && (b64val != (unsigned char)-1)) ) { + if (num == 0) + ret = *s; + num--; + } + + s++; + slen--; + } + return ret; +} + +PyDoc_STRVAR(doc_a2b_base64, "(ascii) -> bin. Decode a line of base64 data"); + +static PyObject * +binascii_a2b_base64(PyObject *self, PyObject *args) +{ + unsigned char *ascii_data, *bin_data; + int leftbits = 0; + unsigned char this_ch; + unsigned int leftchar = 0; + PyObject *rv; + int ascii_len, bin_len; + int quad_pos = 0; + + if ( !PyArg_ParseTuple(args, "t#:a2b_base64", &ascii_data, &ascii_len) ) + return NULL; + + bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later */ + + /* Allocate the buffer */ + if ( (rv=PyString_FromStringAndSize(NULL, bin_len)) == NULL ) + return NULL; + bin_data = (unsigned char *)PyString_AsString(rv); + bin_len = 0; + + for( ; ascii_len > 0; ascii_len--, ascii_data++) { + this_ch = *ascii_data; + + if (this_ch > 0x7f || + this_ch == '\r' || this_ch == '\n' || this_ch == ' ') + continue; + + /* Check for pad sequences and ignore + ** the invalid ones. + */ + if (this_ch == BASE64_PAD) { + if ( (quad_pos < 2) || + ((quad_pos == 2) && + (binascii_find_valid(ascii_data, ascii_len, 1) + != BASE64_PAD)) ) + { + continue; + } + else { + /* A pad sequence means no more input. + ** We've already interpreted the data + ** from the quad at this point. + */ + leftbits = 0; + break; + } + } + + this_ch = table_a2b_base64[*ascii_data]; + if ( this_ch == (unsigned char) -1 ) + continue; + + /* + ** Shift it in on the low end, and see if there's + ** a byte ready for output. + */ + quad_pos = (quad_pos + 1) & 0x03; + leftchar = (leftchar << 6) | (this_ch); + leftbits += 6; + + if ( leftbits >= 8 ) { + leftbits -= 8; + *bin_data++ = (leftchar >> leftbits) & 0xff; + bin_len++; + leftchar &= ((1 << leftbits) - 1); + } + } + + if (leftbits != 0) { + PyErr_SetString(Error, "Incorrect padding"); + Py_DECREF(rv); + return NULL; + } + + /* And set string size correctly. If the result string is empty + ** (because the input was all invalid) return the shared empty + ** string instead; _PyString_Resize() won't do this for us. + */ + if (bin_len > 0) + _PyString_Resize(&rv, bin_len); + else { + Py_DECREF(rv); + rv = PyString_FromString(""); + } + return rv; +} + +PyDoc_STRVAR(doc_b2a_base64, "(bin) -> ascii. Base64-code line of data"); + +static PyObject * +binascii_b2a_base64(PyObject *self, PyObject *args) +{ + unsigned char *ascii_data, *bin_data; + int leftbits = 0; + unsigned char this_ch; + unsigned int leftchar = 0; + PyObject *rv; + int bin_len; + + if ( !PyArg_ParseTuple(args, "s#:b2a_base64", &bin_data, &bin_len) ) + return NULL; + if ( bin_len > BASE64_MAXBIN ) { + PyErr_SetString(Error, "Too much data for base64 line"); + return NULL; + } + + /* We're lazy and allocate too much (fixed up later). + "+3" leaves room for up to two pad characters and a trailing + newline. Note that 'b' gets encoded as 'Yg==\n' (1 in, 5 out). */ + if ( (rv=PyString_FromStringAndSize(NULL, bin_len*2 + 3)) == NULL ) + return NULL; + ascii_data = (unsigned char *)PyString_AsString(rv); + + for( ; bin_len > 0 ; bin_len--, bin_data++ ) { + /* Shift the data into our buffer */ + leftchar = (leftchar << 8) | *bin_data; + leftbits += 8; + + /* See if there are 6-bit groups ready */ + while ( leftbits >= 6 ) { + this_ch = (leftchar >> (leftbits-6)) & 0x3f; + leftbits -= 6; + *ascii_data++ = table_b2a_base64[this_ch]; + } + } + if ( leftbits == 2 ) { + *ascii_data++ = table_b2a_base64[(leftchar&3) << 4]; + *ascii_data++ = BASE64_PAD; + *ascii_data++ = BASE64_PAD; + } else if ( leftbits == 4 ) { + *ascii_data++ = table_b2a_base64[(leftchar&0xf) << 2]; + *ascii_data++ = BASE64_PAD; + } + *ascii_data++ = '\n'; /* Append a courtesy newline */ + + _PyString_Resize(&rv, (ascii_data - + (unsigned char *)PyString_AsString(rv))); + return rv; +} + +PyDoc_STRVAR(doc_a2b_hqx, "ascii -> bin, done. Decode .hqx coding"); + +static PyObject * +binascii_a2b_hqx(PyObject *self, PyObject *args) +{ + unsigned char *ascii_data, *bin_data; + int leftbits = 0; + unsigned char this_ch; + unsigned int leftchar = 0; + PyObject *rv; + int len; + int done = 0; + + if ( !PyArg_ParseTuple(args, "t#:a2b_hqx", &ascii_data, &len) ) + return NULL; + + /* Allocate a string that is too big (fixed later) */ + if ( (rv=PyString_FromStringAndSize(NULL, len)) == NULL ) + return NULL; + bin_data = (unsigned char *)PyString_AsString(rv); + + for( ; len > 0 ; len--, ascii_data++ ) { + /* Get the byte and look it up */ + this_ch = table_a2b_hqx[*ascii_data]; + if ( this_ch == SKIP ) + continue; + if ( this_ch == FAIL ) { + PyErr_SetString(Error, "Illegal char"); + Py_DECREF(rv); + return NULL; + } + if ( this_ch == DONE ) { + /* The terminating colon */ + done = 1; + break; + } + + /* Shift it into the buffer and see if any bytes are ready */ + leftchar = (leftchar << 6) | (this_ch); + leftbits += 6; + if ( leftbits >= 8 ) { + leftbits -= 8; + *bin_data++ = (leftchar >> leftbits) & 0xff; + leftchar &= ((1 << leftbits) - 1); + } + } + + if ( leftbits && !done ) { + PyErr_SetString(Incomplete, + "String has incomplete number of bytes"); + Py_DECREF(rv); + return NULL; + } + _PyString_Resize( + &rv, (bin_data - (unsigned char *)PyString_AsString(rv))); + if (rv) { + PyObject *rrv = Py_BuildValue("Oi", rv, done); + Py_DECREF(rv); + return rrv; + } + + return NULL; +} + +PyDoc_STRVAR(doc_rlecode_hqx, "Binhex RLE-code binary data"); + +static PyObject * +binascii_rlecode_hqx(PyObject *self, PyObject *args) +{ + unsigned char *in_data, *out_data; + PyObject *rv; + unsigned char ch; + int in, inend, len; + + if ( !PyArg_ParseTuple(args, "s#:rlecode_hqx", &in_data, &len) ) + return NULL; + + /* Worst case: output is twice as big as input (fixed later) */ + if ( (rv=PyString_FromStringAndSize(NULL, len*2)) == NULL ) + return NULL; + out_data = (unsigned char *)PyString_AsString(rv); + + for( in=0; in 3 ) { + /* More than 3 in a row. Output RLE. */ + *out_data++ = ch; + *out_data++ = RUNCHAR; + *out_data++ = inend-in; + in = inend-1; + } else { + /* Less than 3. Output the byte itself */ + *out_data++ = ch; + } + } + } + _PyString_Resize(&rv, (out_data - + (unsigned char *)PyString_AsString(rv))); + return rv; +} + +PyDoc_STRVAR(doc_b2a_hqx, "Encode .hqx data"); + +static PyObject * +binascii_b2a_hqx(PyObject *self, PyObject *args) +{ + unsigned char *ascii_data, *bin_data; + int leftbits = 0; + unsigned char this_ch; + unsigned int leftchar = 0; + PyObject *rv; + int len; + + if ( !PyArg_ParseTuple(args, "s#:b2a_hqx", &bin_data, &len) ) + return NULL; + + /* Allocate a buffer that is at least large enough */ + if ( (rv=PyString_FromStringAndSize(NULL, len*2)) == NULL ) + return NULL; + ascii_data = (unsigned char *)PyString_AsString(rv); + + for( ; len > 0 ; len--, bin_data++ ) { + /* Shift into our buffer, and output any 6bits ready */ + leftchar = (leftchar << 8) | *bin_data; + leftbits += 8; + while ( leftbits >= 6 ) { + this_ch = (leftchar >> (leftbits-6)) & 0x3f; + leftbits -= 6; + *ascii_data++ = table_b2a_hqx[this_ch]; + } + } + /* Output a possible runt byte */ + if ( leftbits ) { + leftchar <<= (6-leftbits); + *ascii_data++ = table_b2a_hqx[leftchar & 0x3f]; + } + _PyString_Resize(&rv, (ascii_data - + (unsigned char *)PyString_AsString(rv))); + return rv; +} + +PyDoc_STRVAR(doc_rledecode_hqx, "Decode hexbin RLE-coded string"); + +static PyObject * +binascii_rledecode_hqx(PyObject *self, PyObject *args) +{ + unsigned char *in_data, *out_data; + unsigned char in_byte, in_repeat; + PyObject *rv; + int in_len, out_len, out_len_left; + + if ( !PyArg_ParseTuple(args, "s#:rledecode_hqx", &in_data, &in_len) ) + return NULL; + + /* Empty string is a special case */ + if ( in_len == 0 ) + return Py_BuildValue("s", ""); + + /* Allocate a buffer of reasonable size. Resized when needed */ + out_len = in_len*2; + if ( (rv=PyString_FromStringAndSize(NULL, out_len)) == NULL ) + return NULL; + out_len_left = out_len; + out_data = (unsigned char *)PyString_AsString(rv); + + /* + ** We need two macros here to get/put bytes and handle + ** end-of-buffer for input and output strings. + */ +#define INBYTE(b) \ + do { \ + if ( --in_len < 0 ) { \ + PyErr_SetString(Incomplete, ""); \ + Py_DECREF(rv); \ + return NULL; \ + } \ + b = *in_data++; \ + } while(0) + +#define OUTBYTE(b) \ + do { \ + if ( --out_len_left < 0 ) { \ + _PyString_Resize(&rv, 2*out_len); \ + if ( rv == NULL ) return NULL; \ + out_data = (unsigned char *)PyString_AsString(rv) \ + + out_len; \ + out_len_left = out_len-1; \ + out_len = out_len * 2; \ + } \ + *out_data++ = b; \ + } while(0) + + /* + ** Handle first byte separately (since we have to get angry + ** in case of an orphaned RLE code). + */ + INBYTE(in_byte); + + if (in_byte == RUNCHAR) { + INBYTE(in_repeat); + if (in_repeat != 0) { + /* Note Error, not Incomplete (which is at the end + ** of the string only). This is a programmer error. + */ + PyErr_SetString(Error, "Orphaned RLE code at start"); + Py_DECREF(rv); + return NULL; + } + OUTBYTE(RUNCHAR); + } else { + OUTBYTE(in_byte); + } + + while( in_len > 0 ) { + INBYTE(in_byte); + + if (in_byte == RUNCHAR) { + INBYTE(in_repeat); + if ( in_repeat == 0 ) { + /* Just an escaped RUNCHAR value */ + OUTBYTE(RUNCHAR); + } else { + /* Pick up value and output a sequence of it */ + in_byte = out_data[-1]; + while ( --in_repeat > 0 ) + OUTBYTE(in_byte); + } + } else { + /* Normal byte */ + OUTBYTE(in_byte); + } + } + _PyString_Resize(&rv, (out_data - + (unsigned char *)PyString_AsString(rv))); + return rv; +} + +PyDoc_STRVAR(doc_crc_hqx, +"(data, oldcrc) -> newcrc. Compute hqx CRC incrementally"); + +static PyObject * +binascii_crc_hqx(PyObject *self, PyObject *args) +{ + unsigned char *bin_data; + unsigned int crc; + int len; + + if ( !PyArg_ParseTuple(args, "s#i:crc_hqx", &bin_data, &len, &crc) ) + return NULL; + + while(len--) { + crc=((crc<<8)&0xff00)^crctab_hqx[((crc>>8)&0xff)^*bin_data++]; + } + + return Py_BuildValue("i", crc); +} + +PyDoc_STRVAR(doc_crc32, +"(data, oldcrc = 0) -> newcrc. Compute CRC-32 incrementally"); + +/* Crc - 32 BIT ANSI X3.66 CRC checksum files + Also known as: ISO 3307 +**********************************************************************| +* *| +* Demonstration program to compute the 32-bit CRC used as the frame *| +* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| +* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| +* protocol). The 32-bit FCS was added via the Federal Register, *| +* 1 June 1982, p.23798. I presume but don't know for certain that *| +* this polynomial is or will be included in CCITT V.41, which *| +* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| +* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| +* errors by a factor of 10^-5 over 16-bit FCS. *| +* *| +**********************************************************************| + + Copyright (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction. + + First, the polynomial itself and its table of feedback terms. The + polynomial is + X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + Note that we take it "backwards" and put the highest-order term in + the lowest-order bit. The X^32 term is "implied"; the LSB is the + X^31 term, etc. The X^0 term (usually shown as "+1") results in + the MSB being 1. + + Note that the usual hardware shift register implementation, which + is what we're using (we're merely optimizing it by doing eight-bit + chunks at a time) shifts bits into the lowest-order term. In our + implementation, that means shifting towards the right. Why do we + do it this way? Because the calculated CRC must be transmitted in + order from highest-order term to lowest-order term. UARTs transmit + characters in order from LSB to MSB. By storing the CRC this way, + we hand it to the UART in the order low-byte to high-byte; the UART + sends each low-bit to hight-bit; and the result is transmission bit + by bit from highest- to lowest-order term without requiring any bit + shuffling on our part. Reception works similarly. + + The feedback terms table consists of 256, 32-bit entries. Notes: + + 1. The table can be generated at runtime if desired; code to do so + is shown later. It might not be obvious, but the feedback + terms simply represent the results of eight shift/xor opera- + tions for all combinations of data and CRC register values. + + 2. The CRC accumulation logic is the same for all CRC polynomials, + be they sixteen or thirty-two bits wide. You simply choose the + appropriate table. Alternatively, because the table can be + generated at runtime, you can start by generating the table for + the polynomial in question and use exactly the same "updcrc", + if your application needn't simultaneously handle two CRC + polynomials. (Note, however, that XMODEM is strange.) + + 3. For 16-bit CRCs, the table entries need be only 16 bits wide; + of course, 32-bit entries work OK if the high 16 bits are zero. + + 4. The values must be right-shifted by eight bits by the "updcrc" + logic; the shift must be unsigned (bring in zeroes). On some + hardware you could probably optimize the shift in assembler by + using byte-swap instructions. +********************************************************************/ + +static unsigned long crc_32_tab[256] = { +0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, +0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, +0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, +0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, +0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, +0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, +0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, +0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, +0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, +0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, +0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, +0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, +0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, +0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, +0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, +0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, +0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, +0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, +0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, +0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, +0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, +0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, +0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, +0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, +0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, +0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, +0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, +0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, +0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, +0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, +0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, +0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, +0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, +0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, +0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, +0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, +0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, +0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, +0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, +0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, +0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, +0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, +0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, +0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, +0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, +0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, +0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, +0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, +0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, +0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, +0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, +0x2d02ef8dUL +}; + +static PyObject * +binascii_crc32(PyObject *self, PyObject *args) +{ /* By Jim Ahlstrom; All rights transferred to CNRI */ + unsigned char *bin_data; + unsigned long crc = 0UL; /* initial value of CRC */ + int len; + long result; + + if ( !PyArg_ParseTuple(args, "s#|l:crc32", &bin_data, &len, &crc) ) + return NULL; + + crc = ~ crc; +#if SIZEOF_LONG > 4 + /* only want the trailing 32 bits */ + crc &= 0xFFFFFFFFUL; +#endif + while (len--) + crc = crc_32_tab[(crc ^ *bin_data++) & 0xffUL] ^ (crc >> 8); + /* Note: (crc >> 8) MUST zero fill on left */ + + result = (long)(crc ^ 0xFFFFFFFFUL); +#if SIZEOF_LONG > 4 + /* Extend the sign bit. This is one way to ensure the result is the + * same across platforms. The other way would be to return an + * unbounded unsigned long, but the evidence suggests that lots of + * code outside this treats the result as if it were a signed 4-byte + * integer. + */ + result |= -(result & (1L << 31)); +#endif + return PyInt_FromLong(result); +} + + +static PyObject * +binascii_hexlify(PyObject *self, PyObject *args) +{ + char* argbuf; + int arglen; + PyObject *retval; + char* retbuf; + int i, j; + + if (!PyArg_ParseTuple(args, "t#:b2a_hex", &argbuf, &arglen)) + return NULL; + + retval = PyString_FromStringAndSize(NULL, arglen*2); + if (!retval) + return NULL; + retbuf = PyString_AsString(retval); + if (!retbuf) + goto finally; + + /* make hex version of string, taken from shamodule.c */ + for (i=j=0; i < arglen; i++) { + char c; + c = (argbuf[i] >> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + retbuf[j++] = c; + c = argbuf[i] & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + retbuf[j++] = c; + } + return retval; + + finally: + Py_DECREF(retval); + return NULL; +} + +PyDoc_STRVAR(doc_hexlify, +"b2a_hex(data) -> s; Hexadecimal representation of binary data.\n\ +\n\ +This function is also available as \"hexlify()\"."); + + +static int +to_int(int c) +{ + if (isdigit(c)) + return c - '0'; + else { + if (isupper(c)) + c = tolower(c); + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + } + return -1; +} + + +static PyObject * +binascii_unhexlify(PyObject *self, PyObject *args) +{ + char* argbuf; + int arglen; + PyObject *retval; + char* retbuf; + int i, j; + + if (!PyArg_ParseTuple(args, "s#:a2b_hex", &argbuf, &arglen)) + return NULL; + + /* XXX What should we do about strings with an odd length? Should + * we add an implicit leading zero, or a trailing zero? For now, + * raise an exception. + */ + if (arglen % 2) { + PyErr_SetString(PyExc_TypeError, "Odd-length string"); + return NULL; + } + + retval = PyString_FromStringAndSize(NULL, (arglen/2)); + if (!retval) + return NULL; + retbuf = PyString_AsString(retval); + if (!retbuf) + goto finally; + + for (i=j=0; i < arglen; i += 2) { + int top = to_int(Py_CHARMASK(argbuf[i])); + int bot = to_int(Py_CHARMASK(argbuf[i+1])); + if (top == -1 || bot == -1) { + PyErr_SetString(PyExc_TypeError, + "Non-hexadecimal digit found"); + goto finally; + } + retbuf[j++] = (top << 4) + bot; + } + return retval; + + finally: + Py_DECREF(retval); + return NULL; +} + +PyDoc_STRVAR(doc_unhexlify, +"a2b_hex(hexstr) -> s; Binary data of hexadecimal representation.\n\ +\n\ +hexstr must contain an even number of hex digits (upper or lower case).\n\ +This function is also available as \"unhexlify()\""); + +static int table_hex[128] = { + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, + -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 +}; + +#define hexval(c) table_hex[(unsigned int)(c)] + +#define MAXLINESIZE 76 + +PyDoc_STRVAR(doc_a2b_qp, "Decode a string of qp-encoded data"); + +static PyObject* +binascii_a2b_qp(PyObject *self, PyObject *args, PyObject *kwargs) +{ + unsigned int in, out; + char ch; + unsigned char *data, *odata; + unsigned int datalen = 0; + PyObject *rv; + static char *kwlist[] = {"data", "header", NULL}; + int header = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|i", kwlist, &data, + &datalen, &header)) + return NULL; + + /* We allocate the output same size as input, this is overkill */ + odata = (unsigned char *) calloc(1, datalen); + + if (odata == NULL) { + PyErr_NoMemory(); + return NULL; + } + + in = out = 0; + while (in < datalen) { + if (data[in] == '=') { + in++; + if (in >= datalen) break; + /* Soft line breaks */ + if ((data[in] == '\n') || (data[in] == '\r') || + (data[in] == ' ') || (data[in] == '\t')) { + if (data[in] != '\n') { + while (in < datalen && data[in] != '\n') in++; + } + if (in < datalen) in++; + } + else if (data[in] == '=') { + /* broken case from broken python qp */ + odata[out++] = '='; + in++; + } + else if (((data[in] >= 'A' && data[in] <= 'F') || + (data[in] >= 'a' && data[in] <= 'f') || + (data[in] >= '0' && data[in] <= '9')) && + ((data[in+1] >= 'A' && data[in+1] <= 'F') || + (data[in+1] >= 'a' && data[in+1] <= 'f') || + (data[in+1] >= '0' && data[in+1] <= '9'))) { + /* hexval */ + ch = hexval(data[in]) << 4; + in++; + ch |= hexval(data[in]); + in++; + odata[out++] = ch; + } + else { + odata[out++] = '='; + } + } + else if (header && data[in] == '_') { + odata[out++] = ' '; + in++; + } + else { + odata[out] = data[in]; + in++; + out++; + } + } + if ((rv = PyString_FromStringAndSize((char *)odata, out)) == NULL) { + free (odata); + return NULL; + } + free (odata); + return rv; +} + +static int +to_hex (unsigned char ch, unsigned char *s) +{ + unsigned int uvalue = ch; + + s[1] = "0123456789ABCDEF"[uvalue % 16]; + uvalue = (uvalue / 16); + s[0] = "0123456789ABCDEF"[uvalue % 16]; + return 0; +} + +PyDoc_STRVAR(doc_b2a_qp, +"b2a_qp(data, quotetabs=0, istext=1, header=0) -> s; \n\ + Encode a string using quoted-printable encoding. \n\ +\n\ +On encoding, when istext is set, newlines are not encoded, and white \n\ +space at end of lines is. When istext is not set, \\r and \\n (CR/LF) are \n\ +both encoded. When quotetabs is set, space and tabs are encoded."); + +/* XXX: This is ridiculously complicated to be backward compatible + * (mostly) with the quopri module. It doesn't re-create the quopri + * module bug where text ending in CRLF has the CR encoded */ +static PyObject* +binascii_b2a_qp (PyObject *self, PyObject *args, PyObject *kwargs) +{ + unsigned int in, out; + unsigned char *data, *odata; + unsigned int datalen = 0, odatalen = 0; + PyObject *rv; + unsigned int linelen = 0; + static char *kwlist[] = {"data", "quotetabs", "istext", "header", NULL}; + int istext = 1; + int quotetabs = 0; + int header = 0; + unsigned char ch; + int crlf = 0; + unsigned char *p; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|iii", kwlist, &data, + &datalen, "etabs, &istext, &header)) + return NULL; + + /* See if this string is using CRLF line ends */ + /* XXX: this function has the side effect of converting all of + * the end of lines to be the same depending on this detection + * here */ + p = (unsigned char *) strchr((char *)data, '\n'); + if ((p != NULL) && (p > data) && (*(p-1) == '\r')) + crlf = 1; + + /* First, scan to see how many characters need to be encoded */ + in = 0; + while (in < datalen) { + if ((data[in] > 126) || + (data[in] == '=') || + (header && data[in] == '_') || + ((data[in] == '.') && (linelen == 1)) || + (!istext && ((data[in] == '\r') || (data[in] == '\n'))) || + ((data[in] == '\t' || data[in] == ' ') && (in + 1 == datalen)) || + ((data[in] < 33) && + (data[in] != '\r') && (data[in] != '\n') && + (quotetabs && ((data[in] != '\t') || (data[in] != ' '))))) + { + if ((linelen + 3) >= MAXLINESIZE) { + linelen = 0; + if (crlf) + odatalen += 3; + else + odatalen += 2; + } + linelen += 3; + odatalen += 3; + in++; + } + else { + if (istext && + ((data[in] == '\n') || + ((in+1 < datalen) && (data[in] == '\r') && + (data[in+1] == '\n')))) + { + linelen = 0; + /* Protect against whitespace on end of line */ + if (in && ((data[in-1] == ' ') || (data[in-1] == '\t'))) + odatalen += 2; + if (crlf) + odatalen += 2; + else + odatalen += 1; + if (data[in] == '\r') + in += 2; + else + in++; + } + else { + if ((in + 1 != datalen) && + (data[in+1] != '\n') && + (linelen + 1) >= MAXLINESIZE) { + linelen = 0; + if (crlf) + odatalen += 3; + else + odatalen += 2; + } + linelen++; + odatalen++; + in++; + } + } + } + + odata = (unsigned char *) calloc(1, odatalen); + + if (odata == NULL) { + PyErr_NoMemory(); + return NULL; + } + + in = out = linelen = 0; + while (in < datalen) { + if ((data[in] > 126) || + (data[in] == '=') || + (header && data[in] == '_') || + ((data[in] == '.') && (linelen == 1)) || + (!istext && ((data[in] == '\r') || (data[in] == '\n'))) || + ((data[in] == '\t' || data[in] == ' ') && (in + 1 == datalen)) || + ((data[in] < 33) && + (data[in] != '\r') && (data[in] != '\n') && + (quotetabs && ((data[in] != '\t') || (data[in] != ' '))))) + { + if ((linelen + 3 )>= MAXLINESIZE) { + odata[out++] = '='; + if (crlf) odata[out++] = '\r'; + odata[out++] = '\n'; + linelen = 0; + } + odata[out++] = '='; + to_hex(data[in], &odata[out]); + out += 2; + in++; + linelen += 3; + } + else { + if (istext && + ((data[in] == '\n') || + ((in+1 < datalen) && (data[in] == '\r') && + (data[in+1] == '\n')))) + { + linelen = 0; + /* Protect against whitespace on end of line */ + if (out && ((odata[out-1] == ' ') || (odata[out-1] == '\t'))) { + ch = odata[out-1]; + odata[out-1] = '='; + to_hex(ch, &odata[out]); + out += 2; + } + + if (crlf) odata[out++] = '\r'; + odata[out++] = '\n'; + if (data[in] == '\r') + in += 2; + else + in++; + } + else { + if ((in + 1 != datalen) && + (data[in+1] != '\n') && + (linelen + 1) >= MAXLINESIZE) { + odata[out++] = '='; + if (crlf) odata[out++] = '\r'; + odata[out++] = '\n'; + linelen = 0; + } + linelen++; + if (header && data[in] == ' ') { + odata[out++] = '_'; + in++; + } + else { + odata[out++] = data[in++]; + } + } + } + } + if ((rv = PyString_FromStringAndSize((char *)odata, out)) == NULL) { + free (odata); + return NULL; + } + free (odata); + return rv; +} + +/* List of functions defined in the module */ + +static struct PyMethodDef binascii_module_methods[] = { + {"a2b_uu", binascii_a2b_uu, METH_VARARGS, doc_a2b_uu}, + {"b2a_uu", binascii_b2a_uu, METH_VARARGS, doc_b2a_uu}, + {"a2b_base64", binascii_a2b_base64, METH_VARARGS, doc_a2b_base64}, + {"b2a_base64", binascii_b2a_base64, METH_VARARGS, doc_b2a_base64}, + {"a2b_hqx", binascii_a2b_hqx, METH_VARARGS, doc_a2b_hqx}, + {"b2a_hqx", binascii_b2a_hqx, METH_VARARGS, doc_b2a_hqx}, + {"b2a_hex", binascii_hexlify, METH_VARARGS, doc_hexlify}, + {"a2b_hex", binascii_unhexlify, METH_VARARGS, doc_unhexlify}, + {"hexlify", binascii_hexlify, METH_VARARGS, doc_hexlify}, + {"unhexlify", binascii_unhexlify, METH_VARARGS, doc_unhexlify}, + {"rlecode_hqx", binascii_rlecode_hqx, METH_VARARGS, doc_rlecode_hqx}, + {"rledecode_hqx", binascii_rledecode_hqx, METH_VARARGS, + doc_rledecode_hqx}, + {"crc_hqx", binascii_crc_hqx, METH_VARARGS, doc_crc_hqx}, + {"crc32", binascii_crc32, METH_VARARGS, doc_crc32}, + {"a2b_qp", (PyCFunction)binascii_a2b_qp, METH_VARARGS | METH_KEYWORDS, + doc_a2b_qp}, + {"b2a_qp", (PyCFunction)binascii_b2a_qp, METH_VARARGS | METH_KEYWORDS, + doc_b2a_qp}, + {NULL, NULL} /* sentinel */ +}; + + +/* Initialization function for the module (*must* be called initbinascii) */ +PyDoc_STRVAR(doc_binascii, "Conversion between binary data and ASCII"); + +PyMODINIT_FUNC +initbinascii(void) +{ + PyObject *m, *d, *x; + + /* Create the module and add the functions */ + m = Py_InitModule("binascii", binascii_module_methods); + + d = PyModule_GetDict(m); + x = PyString_FromString(doc_binascii); + PyDict_SetItemString(d, "__doc__", x); + Py_XDECREF(x); + + Error = PyErr_NewException("binascii.Error", NULL, NULL); + PyDict_SetItemString(d, "Error", Error); + Incomplete = PyErr_NewException("binascii.Incomplete", NULL, NULL); + PyDict_SetItemString(d, "Incomplete", Incomplete); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/bsddbmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/bsddbmodule.c new file mode 100644 index 00000000..2a52212f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/bsddbmodule.c @@ -0,0 +1,856 @@ +/* Berkeley DB interface. + Author: Michael McLay + Hacked: Guido van Rossum + Btree and Recno additions plus sequence methods: David Ely + Hacked by Gustavo Niemeyer fixing recno + support. + + XXX To do: + - provide a way to access the various hash functions + - support more open flags + + The windows port of the Berkeley DB code is hard to find on the web: + www.nightmare.com/software.html +*/ + +#include "Python.h" +#ifdef WITH_THREAD +#include "pythread.h" +#endif + +#include +#include +#include +#ifdef HAVE_DB_185_H +#include +#else +#include +#endif +/* Please don't include internal header files of the Berkeley db package + (it messes up the info required in the Setup file) */ + +typedef struct { + PyObject_HEAD + DB *di_bsddb; + int di_size; /* -1 means recompute */ + int di_type; +#ifdef WITH_THREAD + PyThread_type_lock di_lock; +#endif +} bsddbobject; + +static PyTypeObject Bsddbtype; + +#define is_bsddbobject(v) ((v)->ob_type == &Bsddbtype) +#define check_bsddbobject_open(v, r) if ((v)->di_bsddb == NULL) \ + { PyErr_SetString(BsddbError, \ + "BSDDB object has already been closed"); \ + return r; } + +static PyObject *BsddbError; + +static PyObject * +newdbhashobject(char *file, int flags, int mode, + int bsize, int ffactor, int nelem, int cachesize, + int hash, int lorder) +{ + bsddbobject *dp; + HASHINFO info; + + if ((dp = PyObject_New(bsddbobject, &Bsddbtype)) == NULL) + return NULL; + + info.bsize = bsize; + info.ffactor = ffactor; + info.nelem = nelem; + info.cachesize = cachesize; + info.hash = NULL; /* XXX should derive from hash argument */ + info.lorder = lorder; + +#ifdef O_BINARY + flags |= O_BINARY; +#endif + Py_BEGIN_ALLOW_THREADS + dp->di_bsddb = dbopen(file, flags, mode, DB_HASH, &info); + Py_END_ALLOW_THREADS + if (dp->di_bsddb == NULL) { + PyErr_SetFromErrno(BsddbError); +#ifdef WITH_THREAD + dp->di_lock = NULL; +#endif + Py_DECREF(dp); + return NULL; + } + + dp->di_size = -1; + dp->di_type = DB_HASH; + +#ifdef WITH_THREAD + dp->di_lock = PyThread_allocate_lock(); + if (dp->di_lock == NULL) { + PyErr_SetString(BsddbError, "can't allocate lock"); + Py_DECREF(dp); + return NULL; + } +#endif + + return (PyObject *)dp; +} + +static PyObject * +newdbbtobject(char *file, int flags, int mode, + int btflags, int cachesize, int maxkeypage, + int minkeypage, int psize, int lorder) +{ + bsddbobject *dp; + BTREEINFO info; + + if ((dp = PyObject_New(bsddbobject, &Bsddbtype)) == NULL) + return NULL; + + info.flags = btflags; + info.cachesize = cachesize; + info.maxkeypage = maxkeypage; + info.minkeypage = minkeypage; + info.psize = psize; + info.lorder = lorder; + info.compare = 0; /* Use default comparison functions, for now..*/ + info.prefix = 0; + +#ifdef O_BINARY + flags |= O_BINARY; +#endif + Py_BEGIN_ALLOW_THREADS + dp->di_bsddb = dbopen(file, flags, mode, DB_BTREE, &info); + Py_END_ALLOW_THREADS + if (dp->di_bsddb == NULL) { + PyErr_SetFromErrno(BsddbError); +#ifdef WITH_THREAD + dp->di_lock = NULL; +#endif + Py_DECREF(dp); + return NULL; + } + + dp->di_size = -1; + dp->di_type = DB_BTREE; + +#ifdef WITH_THREAD + dp->di_lock = PyThread_allocate_lock(); + if (dp->di_lock == NULL) { + PyErr_SetString(BsddbError, "can't allocate lock"); + Py_DECREF(dp); + return NULL; + } +#endif + + return (PyObject *)dp; +} + +static PyObject * +newdbrnobject(char *file, int flags, int mode, + int rnflags, int cachesize, int psize, int lorder, + size_t reclen, u_char bval, char *bfname) +{ + bsddbobject *dp; + RECNOINFO info; + int fd; + + if ((dp = PyObject_New(bsddbobject, &Bsddbtype)) == NULL) + return NULL; + + info.flags = rnflags; + info.cachesize = cachesize; + info.psize = psize; + info.lorder = lorder; + info.reclen = reclen; + info.bval = bval; + info.bfname = bfname; + +#ifdef O_BINARY + flags |= O_BINARY; +#endif + /* This is a hack to avoid a dbopen() bug that happens when + * it fails. */ + fd = open(file, flags); + if (fd == -1) { + dp->di_bsddb = NULL; + } + else { + close(fd); + Py_BEGIN_ALLOW_THREADS + dp->di_bsddb = dbopen(file, flags, mode, DB_RECNO, &info); + Py_END_ALLOW_THREADS + } + if (dp->di_bsddb == NULL) { + PyErr_SetFromErrno(BsddbError); +#ifdef WITH_THREAD + dp->di_lock = NULL; +#endif + Py_DECREF(dp); + return NULL; + } + + dp->di_size = -1; + dp->di_type = DB_RECNO; + +#ifdef WITH_THREAD + dp->di_lock = PyThread_allocate_lock(); + if (dp->di_lock == NULL) { + PyErr_SetString(BsddbError, "can't allocate lock"); + Py_DECREF(dp); + return NULL; + } +#endif + + return (PyObject *)dp; +} + +static void +bsddb_dealloc(bsddbobject *dp) +{ +#ifdef WITH_THREAD + if (dp->di_lock) { + PyThread_acquire_lock(dp->di_lock, 0); + PyThread_release_lock(dp->di_lock); + PyThread_free_lock(dp->di_lock); + dp->di_lock = NULL; + } +#endif + if (dp->di_bsddb != NULL) { + int status; + Py_BEGIN_ALLOW_THREADS + status = (dp->di_bsddb->close)(dp->di_bsddb); + Py_END_ALLOW_THREADS + if (status != 0) + fprintf(stderr, + "Python bsddb: close errno %d in dealloc\n", + errno); + } + PyObject_Del(dp); +} + +#ifdef WITH_THREAD +#define BSDDB_BGN_SAVE(_dp) \ + Py_BEGIN_ALLOW_THREADS PyThread_acquire_lock(_dp->di_lock,1); +#define BSDDB_END_SAVE(_dp) \ + PyThread_release_lock(_dp->di_lock); Py_END_ALLOW_THREADS +#else +#define BSDDB_BGN_SAVE(_dp) Py_BEGIN_ALLOW_THREADS +#define BSDDB_END_SAVE(_dp) Py_END_ALLOW_THREADS +#endif + +static int +bsddb_length(bsddbobject *dp) +{ + check_bsddbobject_open(dp, -1); + if (dp->di_size < 0) { + DBT krec, drec; + int status; + int size = 0; + BSDDB_BGN_SAVE(dp) + for (status = (dp->di_bsddb->seq)(dp->di_bsddb, + &krec, &drec,R_FIRST); + status == 0; + status = (dp->di_bsddb->seq)(dp->di_bsddb, + &krec, &drec, R_NEXT)) + size++; + BSDDB_END_SAVE(dp) + if (status < 0) { + PyErr_SetFromErrno(BsddbError); + return -1; + } + dp->di_size = size; + } + return dp->di_size; +} + +static PyObject * +bsddb_subscript(bsddbobject *dp, PyObject *key) +{ + int status; + DBT krec, drec; + char *data,buf[4096]; + int size; + PyObject *result; + recno_t recno; + + if (dp->di_type == DB_RECNO) { + if (!PyArg_Parse(key, "i", &recno)) { + PyErr_SetString(PyExc_TypeError, + "key type must be integer"); + return NULL; + } + krec.data = &recno; + krec.size = sizeof(recno); + } + else { + if (!PyArg_Parse(key, "s#", &data, &size)) { + PyErr_SetString(PyExc_TypeError, + "key type must be string"); + return NULL; + } + krec.data = data; + krec.size = size; + } + check_bsddbobject_open(dp, NULL); + + BSDDB_BGN_SAVE(dp) + status = (dp->di_bsddb->get)(dp->di_bsddb, &krec, &drec, 0); + if (status == 0) { + if (drec.size > sizeof(buf)) data = malloc(drec.size); + else data = buf; + if (data!=NULL) memcpy(data,drec.data,drec.size); + } + BSDDB_END_SAVE(dp) + if (data==NULL) return PyErr_NoMemory(); + if (status != 0) { + if (status < 0) + PyErr_SetFromErrno(BsddbError); + else + PyErr_SetObject(PyExc_KeyError, key); + return NULL; + } + + result = PyString_FromStringAndSize(data, (int)drec.size); + if (data != buf) free(data); + return result; +} + +static int +bsddb_ass_sub(bsddbobject *dp, PyObject *key, PyObject *value) +{ + int status; + DBT krec, drec; + char *data; + int size; + recno_t recno; + + if (dp->di_type == DB_RECNO) { + if (!PyArg_Parse(key, "i", &recno)) { + PyErr_SetString(PyExc_TypeError, + "bsddb key type must be integer"); + return -1; + } + krec.data = &recno; + krec.size = sizeof(recno); + } + else { + if (!PyArg_Parse(key, "s#", &data, &size)) { + PyErr_SetString(PyExc_TypeError, + "bsddb key type must be string"); + return -1; + } + krec.data = data; + krec.size = size; + } + check_bsddbobject_open(dp, -1); + dp->di_size = -1; + if (value == NULL) { + BSDDB_BGN_SAVE(dp) + status = (dp->di_bsddb->del)(dp->di_bsddb, &krec, 0); + BSDDB_END_SAVE(dp) + } + else { + if (!PyArg_Parse(value, "s#", &data, &size)) { + PyErr_SetString(PyExc_TypeError, + "bsddb value type must be string"); + return -1; + } + drec.data = data; + drec.size = size; + BSDDB_BGN_SAVE(dp) + status = (dp->di_bsddb->put)(dp->di_bsddb, &krec, &drec, 0); + BSDDB_END_SAVE(dp) + } + if (status != 0) { + if (status < 0) + PyErr_SetFromErrno(BsddbError); + else + PyErr_SetObject(PyExc_KeyError, key); + return -1; + } + return 0; +} + +static PyMappingMethods bsddb_as_mapping = { + (inquiry)bsddb_length, /*mp_length*/ + (binaryfunc)bsddb_subscript, /*mp_subscript*/ + (objobjargproc)bsddb_ass_sub, /*mp_ass_subscript*/ +}; + +static PyObject * +bsddb_close(bsddbobject *dp) +{ + if (dp->di_bsddb != NULL) { + int status; + BSDDB_BGN_SAVE(dp) + status = (dp->di_bsddb->close)(dp->di_bsddb); + BSDDB_END_SAVE(dp) + if (status != 0) { + dp->di_bsddb = NULL; + PyErr_SetFromErrno(BsddbError); + return NULL; + } + } + dp->di_bsddb = NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +bsddb_keys(bsddbobject *dp) +{ + PyObject *list, *item=NULL; + DBT krec, drec; + char *data=NULL,buf[4096]; + int status; + int err; + + check_bsddbobject_open(dp, NULL); + list = PyList_New(0); + if (list == NULL) + return NULL; + BSDDB_BGN_SAVE(dp) + status = (dp->di_bsddb->seq)(dp->di_bsddb, &krec, &drec, R_FIRST); + if (status == 0) { + if (krec.size > sizeof(buf)) data = malloc(krec.size); + else data = buf; + if (data != NULL) memcpy(data,krec.data,krec.size); + } + BSDDB_END_SAVE(dp) + if (status == 0 && data==NULL) return PyErr_NoMemory(); + while (status == 0) { + if (dp->di_type == DB_RECNO) + item = PyInt_FromLong(*((int*)data)); + else + item = PyString_FromStringAndSize(data, + (int)krec.size); + if (data != buf) free(data); + if (item == NULL) { + Py_DECREF(list); + return NULL; + } + err = PyList_Append(list, item); + Py_DECREF(item); + if (err != 0) { + Py_DECREF(list); + return NULL; + } + BSDDB_BGN_SAVE(dp) + status = (dp->di_bsddb->seq) + (dp->di_bsddb, &krec, &drec, R_NEXT); + if (status == 0) { + if (krec.size > sizeof(buf)) + data = malloc(krec.size); + else data = buf; + if (data != NULL) + memcpy(data,krec.data,krec.size); + } + BSDDB_END_SAVE(dp) + if (data == NULL) return PyErr_NoMemory(); + } + if (status < 0) { + PyErr_SetFromErrno(BsddbError); + Py_DECREF(list); + return NULL; + } + if (dp->di_size < 0) + dp->di_size = PyList_Size(list); /* We just did the work */ + return list; +} + +static PyObject * +bsddb_has_key(bsddbobject *dp, PyObject *args) +{ + DBT krec, drec; + int status; + char *data; + int size; + recno_t recno; + + if (dp->di_type == DB_RECNO) { + if (!PyArg_ParseTuple(args, "i;key type must be integer", + &recno)) { + return NULL; + } + krec.data = &recno; + krec.size = sizeof(recno); + } + else { + if (!PyArg_ParseTuple(args, "s#;key type must be string", + &data, &size)) { + return NULL; + } + krec.data = data; + krec.size = size; + } + check_bsddbobject_open(dp, NULL); + + BSDDB_BGN_SAVE(dp) + status = (dp->di_bsddb->get)(dp->di_bsddb, &krec, &drec, 0); + BSDDB_END_SAVE(dp) + if (status < 0) { + PyErr_SetFromErrno(BsddbError); + return NULL; + } + + return PyInt_FromLong(status == 0); +} + +static PyObject * +bsddb_set_location(bsddbobject *dp, PyObject *key) +{ + int status; + DBT krec, drec; + char *data,buf[4096]; + int size; + PyObject *result; + recno_t recno; + + if (dp->di_type == DB_RECNO) { + if (!PyArg_ParseTuple(key, "i;key type must be integer", + &recno)) { + return NULL; + } + krec.data = &recno; + krec.size = sizeof(recno); + } + else { + if (!PyArg_ParseTuple(key, "s#;key type must be string", + &data, &size)) { + return NULL; + } + krec.data = data; + krec.size = size; + } + check_bsddbobject_open(dp, NULL); + + BSDDB_BGN_SAVE(dp) + status = (dp->di_bsddb->seq)(dp->di_bsddb, &krec, &drec, R_CURSOR); + if (status == 0) { + if (drec.size > sizeof(buf)) data = malloc(drec.size); + else data = buf; + if (data!=NULL) memcpy(data,drec.data,drec.size); + } + BSDDB_END_SAVE(dp) + if (data==NULL) return PyErr_NoMemory(); + if (status != 0) { + if (status < 0) + PyErr_SetFromErrno(BsddbError); + else + PyErr_SetObject(PyExc_KeyError, key); + return NULL; + } + + if (dp->di_type == DB_RECNO) + result = Py_BuildValue("is#", *((int*)krec.data), + data, drec.size); + else + result = Py_BuildValue("s#s#", krec.data, krec.size, + data, drec.size); + if (data != buf) free(data); + return result; +} + +static PyObject * +bsddb_seq(bsddbobject *dp, int sequence_request) +{ + int status; + DBT krec, drec; + char *kdata=NULL,kbuf[4096]; + char *ddata=NULL,dbuf[4096]; + PyObject *result; + + check_bsddbobject_open(dp, NULL); + krec.data = 0; + krec.size = 0; + + BSDDB_BGN_SAVE(dp) + status = (dp->di_bsddb->seq)(dp->di_bsddb, &krec, + &drec, sequence_request); + if (status == 0) { + if (krec.size > sizeof(kbuf)) kdata = malloc(krec.size); + else kdata = kbuf; + if (kdata != NULL) memcpy(kdata,krec.data,krec.size); + if (drec.size > sizeof(dbuf)) ddata = malloc(drec.size); + else ddata = dbuf; + if (ddata != NULL) memcpy(ddata,drec.data,drec.size); + } + BSDDB_END_SAVE(dp) + if (status == 0) { + if ((kdata == NULL) || (ddata == NULL)) + return PyErr_NoMemory(); + } + else { + /* (status != 0) */ + if (status < 0) + PyErr_SetFromErrno(BsddbError); + else + PyErr_SetString(PyExc_KeyError, "no key/data pairs"); + return NULL; + } + + if (dp->di_type == DB_RECNO) + result = Py_BuildValue("is#", *((int*)kdata), + ddata, drec.size); + else + result = Py_BuildValue("s#s#", kdata, krec.size, + ddata, drec.size); + if (kdata != kbuf) free(kdata); + if (ddata != dbuf) free(ddata); + return result; +} + +static PyObject * +bsddb_next(bsddbobject *dp) +{ + return bsddb_seq(dp, R_NEXT); +} +static PyObject * +bsddb_previous(bsddbobject *dp) +{ + return bsddb_seq(dp, R_PREV); +} +static PyObject * +bsddb_first(bsddbobject *dp) +{ + return bsddb_seq(dp, R_FIRST); +} +static PyObject * +bsddb_last(bsddbobject *dp) +{ + return bsddb_seq(dp, R_LAST); +} +static PyObject * +bsddb_sync(bsddbobject *dp) +{ + int status; + + check_bsddbobject_open(dp, NULL); + BSDDB_BGN_SAVE(dp) + status = (dp->di_bsddb->sync)(dp->di_bsddb, 0); + BSDDB_END_SAVE(dp) + if (status != 0) { + PyErr_SetFromErrno(BsddbError); + return NULL; + } + return PyInt_FromLong(status = 0); +} +static PyMethodDef bsddb_methods[] = { + {"close", (PyCFunction)bsddb_close, METH_NOARGS}, + {"keys", (PyCFunction)bsddb_keys, METH_NOARGS}, + {"has_key", (PyCFunction)bsddb_has_key, METH_VARARGS}, + {"set_location", (PyCFunction)bsddb_set_location, METH_VARARGS}, + {"next", (PyCFunction)bsddb_next, METH_NOARGS}, + {"previous", (PyCFunction)bsddb_previous, METH_NOARGS}, + {"first", (PyCFunction)bsddb_first, METH_NOARGS}, + {"last", (PyCFunction)bsddb_last, METH_NOARGS}, + {"sync", (PyCFunction)bsddb_sync, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +bsddb_getattr(PyObject *dp, char *name) +{ + return Py_FindMethod(bsddb_methods, dp, name); +} + +static PyTypeObject Bsddbtype = { + PyObject_HEAD_INIT(NULL) + 0, + "bsddb.bsddb", + sizeof(bsddbobject), + 0, + (destructor)bsddb_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)bsddb_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &bsddb_as_mapping, /*tp_as_mapping*/ +}; + +static PyObject * +bsdhashopen(PyObject *self, PyObject *args) +{ + char *file; + char *flag = NULL; + int flags = O_RDONLY; + int mode = 0666; + int bsize = 0; + int ffactor = 0; + int nelem = 0; + int cachesize = 0; + int hash = 0; /* XXX currently ignored */ + int lorder = 0; + + if (!PyArg_ParseTuple(args, "z|siiiiiii:hashopen", + &file, &flag, &mode, + &bsize, &ffactor, &nelem, &cachesize, + &hash, &lorder)) + return NULL; + if (flag != NULL) { + /* XXX need to pass O_EXCL, O_EXLOCK, O_NONBLOCK, O_SHLOCK */ + if (flag[0] == 'r') + flags = O_RDONLY; + else if (flag[0] == 'w') + flags = O_RDWR; + else if (flag[0] == 'c') + flags = O_RDWR|O_CREAT; + else if (flag[0] == 'n') + flags = O_RDWR|O_CREAT|O_TRUNC; + else { + PyErr_SetString(BsddbError, + "Flag should begin with 'r', 'w', 'c' or 'n'"); + return NULL; + } + if (flag[1] == 'l') { +#if defined(O_EXLOCK) && defined(O_SHLOCK) + if (flag[0] == 'r') + flags |= O_SHLOCK; + else + flags |= O_EXLOCK; +#else + PyErr_SetString(BsddbError, + "locking not supported on this platform"); + return NULL; +#endif + } + } + return newdbhashobject(file, flags, mode, + bsize, ffactor, nelem, cachesize, hash, lorder); +} + +static PyObject * +bsdbtopen(PyObject *self, PyObject *args) +{ + char *file; + char *flag = NULL; + int flags = O_RDONLY; + int mode = 0666; + int cachesize = 0; + int maxkeypage = 0; + int minkeypage = 0; + int btflags = 0; + unsigned int psize = 0; + int lorder = 0; + + if (!PyArg_ParseTuple(args, "z|siiiiiii:btopen", + &file, &flag, &mode, + &btflags, &cachesize, &maxkeypage, &minkeypage, + &psize, &lorder)) + return NULL; + if (flag != NULL) { + /* XXX need to pass O_EXCL, O_EXLOCK, O_NONBLOCK, O_SHLOCK */ + if (flag[0] == 'r') + flags = O_RDONLY; + else if (flag[0] == 'w') + flags = O_RDWR; + else if (flag[0] == 'c') + flags = O_RDWR|O_CREAT; + else if (flag[0] == 'n') + flags = O_RDWR|O_CREAT|O_TRUNC; + else { + PyErr_SetString(BsddbError, + "Flag should begin with 'r', 'w', 'c' or 'n'"); + return NULL; + } + if (flag[1] == 'l') { +#if defined(O_EXLOCK) && defined(O_SHLOCK) + if (flag[0] == 'r') + flags |= O_SHLOCK; + else + flags |= O_EXLOCK; +#else + PyErr_SetString(BsddbError, + "locking not supported on this platform"); + return NULL; +#endif + } + } + return newdbbtobject(file, flags, mode, + btflags, cachesize, maxkeypage, minkeypage, + psize, lorder); +} + +static PyObject * +bsdrnopen(PyObject *self, PyObject *args) +{ + char *file; + char *flag = NULL; + int flags = O_RDONLY; + int mode = 0666; + int cachesize = 0; + int rnflags = 0; + unsigned int psize = 0; + int lorder = 0; + size_t reclen = 0; + char *bval = ""; + char *bfname = NULL; + + if (!PyArg_ParseTuple(args, "z|siiiiiiss:rnopen", + &file, &flag, &mode, + &rnflags, &cachesize, &psize, &lorder, + &reclen, &bval, &bfname)) + return NULL; + + if (flag != NULL) { + /* XXX need to pass O_EXCL, O_EXLOCK, O_NONBLOCK, O_SHLOCK */ + if (flag[0] == 'r') + flags = O_RDONLY; + else if (flag[0] == 'w') + flags = O_RDWR; + else if (flag[0] == 'c') + flags = O_RDWR|O_CREAT; + else if (flag[0] == 'n') + flags = O_RDWR|O_CREAT|O_TRUNC; + else { + PyErr_SetString(BsddbError, + "Flag should begin with 'r', 'w', 'c' or 'n'"); + return NULL; + } + if (flag[1] == 'l') { +#if defined(O_EXLOCK) && defined(O_SHLOCK) + if (flag[0] == 'r') + flags |= O_SHLOCK; + else + flags |= O_EXLOCK; +#else + PyErr_SetString(BsddbError, + "locking not supported on this platform"); + return NULL; +#endif + } + else if (flag[1] != '\0') { + PyErr_SetString(BsddbError, + "Flag char 2 should be 'l' or absent"); + return NULL; + } + } + return newdbrnobject(file, flags, mode, rnflags, cachesize, + psize, lorder, reclen, bval[0], bfname); +} + +static PyMethodDef bsddbmodule_methods[] = { + {"hashopen", (PyCFunction)bsdhashopen, METH_VARARGS}, + {"btopen", (PyCFunction)bsdbtopen, METH_VARARGS}, + {"rnopen", (PyCFunction)bsdrnopen, METH_VARARGS}, + /* strictly for use by dbhhash!!! */ + {"open", (PyCFunction)bsdhashopen, METH_VARARGS}, + {0, 0}, +}; + +PyMODINIT_FUNC +initbsddb185(void) { + PyObject *m, *d; + + Bsddbtype.ob_type = &PyType_Type; + m = Py_InitModule("bsddb185", bsddbmodule_methods); + d = PyModule_GetDict(m); + BsddbError = PyErr_NewException("bsddb.error", NULL, NULL); + if (BsddbError != NULL) + PyDict_SetItemString(d, "error", BsddbError); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/bz2module.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/bz2module.c new file mode 100644 index 00000000..2b609aec --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/bz2module.c @@ -0,0 +1,2191 @@ +/* + +python-bz2 - python bz2 library interface + +Copyright (c) 2002 Gustavo Niemeyer +Copyright (c) 2002 Python Software Foundation; All Rights Reserved + +*/ + +#include "Python.h" +#include +#include +#include "structmember.h" + +#ifdef WITH_THREAD +#include "pythread.h" +#endif + +static char __author__[] = +"The bz2 python module was written by:\n\ +\n\ + Gustavo Niemeyer \n\ +"; + +#define BUF(v) PyString_AS_STRING((PyStringObject *)v) + +#define MODE_CLOSED 0 +#define MODE_READ 1 +#define MODE_READ_EOF 2 +#define MODE_WRITE 3 + +#define BZ2FileObject_Check(v) ((v)->ob_type == &BZ2File_Type) + + +#ifdef BZ_CONFIG_ERROR + +#if SIZEOF_LONG >= 8 +#define BZS_TOTAL_OUT(bzs) \ + (((long)bzs->total_out_hi32 << 32) + bzs->total_out_lo32) +#elif SIZEOF_LONG_LONG >= 8 +#define BZS_TOTAL_OUT(bzs) \ + (((PY_LONG_LONG)bzs->total_out_hi32 << 32) + bzs->total_out_lo32) +#else +#define BZS_TOTAL_OUT(bzs) \ + bzs->total_out_lo32; +#endif + +#else /* ! BZ_CONFIG_ERROR */ + +#define BZ2_bzRead bzRead +#define BZ2_bzReadOpen bzReadOpen +#define BZ2_bzReadClose bzReadClose +#define BZ2_bzWrite bzWrite +#define BZ2_bzWriteOpen bzWriteOpen +#define BZ2_bzWriteClose bzWriteClose +#define BZ2_bzCompress bzCompress +#define BZ2_bzCompressInit bzCompressInit +#define BZ2_bzCompressEnd bzCompressEnd +#define BZ2_bzDecompress bzDecompress +#define BZ2_bzDecompressInit bzDecompressInit +#define BZ2_bzDecompressEnd bzDecompressEnd + +#define BZS_TOTAL_OUT(bzs) bzs->total_out + +#endif /* ! BZ_CONFIG_ERROR */ + + +#ifdef WITH_THREAD +#define ACQUIRE_LOCK(obj) PyThread_acquire_lock(obj->lock, 1) +#define RELEASE_LOCK(obj) PyThread_release_lock(obj->lock) +#else +#define ACQUIRE_LOCK(obj) +#define RELEASE_LOCK(obj) +#endif + +#ifdef WITH_UNIVERSAL_NEWLINES +/* Bits in f_newlinetypes */ +#define NEWLINE_UNKNOWN 0 /* No newline seen, yet */ +#define NEWLINE_CR 1 /* \r newline seen */ +#define NEWLINE_LF 2 /* \n newline seen */ +#define NEWLINE_CRLF 4 /* \r\n newline seen */ +#endif + +/* ===================================================================== */ +/* Structure definitions. */ + +typedef struct { + PyObject_HEAD + PyObject *file; + + char* f_buf; /* Allocated readahead buffer */ + char* f_bufend; /* Points after last occupied position */ + char* f_bufptr; /* Current buffer position */ + + int f_softspace; /* Flag used by 'print' command */ + +#ifdef WITH_UNIVERSAL_NEWLINES + int f_univ_newline; /* Handle any newline convention */ + int f_newlinetypes; /* Types of newlines seen */ + int f_skipnextlf; /* Skip next \n */ +#endif + + BZFILE *fp; + int mode; + long pos; + long size; +#ifdef WITH_THREAD + PyThread_type_lock lock; +#endif +} BZ2FileObject; + +typedef struct { + PyObject_HEAD + bz_stream bzs; + int running; +#ifdef WITH_THREAD + PyThread_type_lock lock; +#endif +} BZ2CompObject; + +typedef struct { + PyObject_HEAD + bz_stream bzs; + int running; + PyObject *unused_data; +#ifdef WITH_THREAD + PyThread_type_lock lock; +#endif +} BZ2DecompObject; + +/* ===================================================================== */ +/* Utility functions. */ + +static int +Util_CatchBZ2Error(int bzerror) +{ + int ret = 0; + switch(bzerror) { + case BZ_OK: + case BZ_STREAM_END: + break; + +#ifdef BZ_CONFIG_ERROR + case BZ_CONFIG_ERROR: + PyErr_SetString(PyExc_SystemError, + "the bz2 library was not compiled " + "correctly"); + ret = 1; + break; +#endif + + case BZ_PARAM_ERROR: + PyErr_SetString(PyExc_ValueError, + "the bz2 library has received wrong " + "parameters"); + ret = 1; + break; + + case BZ_MEM_ERROR: + PyErr_NoMemory(); + ret = 1; + break; + + case BZ_DATA_ERROR: + case BZ_DATA_ERROR_MAGIC: + PyErr_SetString(PyExc_IOError, "invalid data stream"); + ret = 1; + break; + + case BZ_IO_ERROR: + PyErr_SetString(PyExc_IOError, "unknown IO error"); + ret = 1; + break; + + case BZ_UNEXPECTED_EOF: + PyErr_SetString(PyExc_EOFError, + "compressed file ended before the " + "logical end-of-stream was detected"); + ret = 1; + break; + + case BZ_SEQUENCE_ERROR: + PyErr_SetString(PyExc_RuntimeError, + "wrong sequence of bz2 library " + "commands used"); + ret = 1; + break; + } + return ret; +} + +#if BUFSIZ < 8192 +#define SMALLCHUNK 8192 +#else +#define SMALLCHUNK BUFSIZ +#endif + +#if SIZEOF_INT < 4 +#define BIGCHUNK (512 * 32) +#else +#define BIGCHUNK (512 * 1024) +#endif + +/* This is a hacked version of Python's fileobject.c:new_buffersize(). */ +static size_t +Util_NewBufferSize(size_t currentsize) +{ + if (currentsize > SMALLCHUNK) { + /* Keep doubling until we reach BIGCHUNK; + then keep adding BIGCHUNK. */ + if (currentsize <= BIGCHUNK) + return currentsize + currentsize; + else + return currentsize + BIGCHUNK; + } + return currentsize + SMALLCHUNK; +} + +/* This is a hacked version of Python's fileobject.c:get_line(). */ +static PyObject * +Util_GetLine(BZ2FileObject *f, int n) +{ + char c; + char *buf, *end; + size_t total_v_size; /* total # of slots in buffer */ + size_t used_v_size; /* # used slots in buffer */ + size_t increment; /* amount to increment the buffer */ + PyObject *v; + int bzerror; +#ifdef WITH_UNIVERSAL_NEWLINES + int newlinetypes = f->f_newlinetypes; + int skipnextlf = f->f_skipnextlf; + int univ_newline = f->f_univ_newline; +#endif + + total_v_size = n > 0 ? n : 100; + v = PyString_FromStringAndSize((char *)NULL, total_v_size); + if (v == NULL) + return NULL; + + buf = BUF(v); + end = buf + total_v_size; + + for (;;) { + Py_BEGIN_ALLOW_THREADS +#ifdef WITH_UNIVERSAL_NEWLINES + if (univ_newline) { + while (1) { + BZ2_bzRead(&bzerror, f->fp, &c, 1); + f->pos++; + if (bzerror != BZ_OK || buf == end) + break; + if (skipnextlf) { + skipnextlf = 0; + if (c == '\n') { + /* Seeing a \n here with + * skipnextlf true means we + * saw a \r before. + */ + newlinetypes |= NEWLINE_CRLF; + BZ2_bzRead(&bzerror, f->fp, + &c, 1); + if (bzerror != BZ_OK) + break; + } else { + newlinetypes |= NEWLINE_CR; + } + } + if (c == '\r') { + skipnextlf = 1; + c = '\n'; + } else if ( c == '\n') + newlinetypes |= NEWLINE_LF; + *buf++ = c; + if (c == '\n') break; + } + if (bzerror == BZ_STREAM_END && skipnextlf) + newlinetypes |= NEWLINE_CR; + } else /* If not universal newlines use the normal loop */ +#endif + do { + BZ2_bzRead(&bzerror, f->fp, &c, 1); + f->pos++; + *buf++ = c; + } while (bzerror == BZ_OK && c != '\n' && buf != end); + Py_END_ALLOW_THREADS +#ifdef WITH_UNIVERSAL_NEWLINES + f->f_newlinetypes = newlinetypes; + f->f_skipnextlf = skipnextlf; +#endif + if (bzerror == BZ_STREAM_END) { + f->size = f->pos; + f->mode = MODE_READ_EOF; + break; + } else if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + Py_DECREF(v); + return NULL; + } + if (c == '\n') + break; + /* Must be because buf == end */ + if (n > 0) + break; + used_v_size = total_v_size; + increment = total_v_size >> 2; /* mild exponential growth */ + total_v_size += increment; + if (total_v_size > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "line is longer than a Python string can hold"); + Py_DECREF(v); + return NULL; + } + if (_PyString_Resize(&v, total_v_size) < 0) + return NULL; + buf = BUF(v) + used_v_size; + end = BUF(v) + total_v_size; + } + + used_v_size = buf - BUF(v); + if (used_v_size != total_v_size) + _PyString_Resize(&v, used_v_size); + return v; +} + +#ifndef WITH_UNIVERSAL_NEWLINES +#define Util_UnivNewlineRead(a,b,c,d,e) BZ2_bzRead(a,b,c,d) +#else +/* This is a hacked version of Python's + * fileobject.c:Py_UniversalNewlineFread(). */ +size_t +Util_UnivNewlineRead(int *bzerror, BZFILE *stream, + char* buf, size_t n, BZ2FileObject *f) +{ + char *dst = buf; + int newlinetypes, skipnextlf; + + assert(buf != NULL); + assert(stream != NULL); + + if (!f->f_univ_newline) + return BZ2_bzRead(bzerror, stream, buf, n); + + newlinetypes = f->f_newlinetypes; + skipnextlf = f->f_skipnextlf; + + /* Invariant: n is the number of bytes remaining to be filled + * in the buffer. + */ + while (n) { + size_t nread; + int shortread; + char *src = dst; + + nread = BZ2_bzRead(bzerror, stream, dst, n); + assert(nread <= n); + n -= nread; /* assuming 1 byte out for each in; will adjust */ + shortread = n != 0; /* true iff EOF or error */ + while (nread--) { + char c = *src++; + if (c == '\r') { + /* Save as LF and set flag to skip next LF. */ + *dst++ = '\n'; + skipnextlf = 1; + } + else if (skipnextlf && c == '\n') { + /* Skip LF, and remember we saw CR LF. */ + skipnextlf = 0; + newlinetypes |= NEWLINE_CRLF; + ++n; + } + else { + /* Normal char to be stored in buffer. Also + * update the newlinetypes flag if either this + * is an LF or the previous char was a CR. + */ + if (c == '\n') + newlinetypes |= NEWLINE_LF; + else if (skipnextlf) + newlinetypes |= NEWLINE_CR; + *dst++ = c; + skipnextlf = 0; + } + } + if (shortread) { + /* If this is EOF, update type flags. */ + if (skipnextlf && *bzerror == BZ_STREAM_END) + newlinetypes |= NEWLINE_CR; + break; + } + } + f->f_newlinetypes = newlinetypes; + f->f_skipnextlf = skipnextlf; + return dst - buf; +} +#endif + +/* This is a hacked version of Python's fileobject.c:drop_readahead(). */ +static void +Util_DropReadAhead(BZ2FileObject *f) +{ + if (f->f_buf != NULL) { + PyMem_Free(f->f_buf); + f->f_buf = NULL; + } +} + +/* This is a hacked version of Python's fileobject.c:readahead(). */ +static int +Util_ReadAhead(BZ2FileObject *f, int bufsize) +{ + int chunksize; + int bzerror; + + if (f->f_buf != NULL) { + if((f->f_bufend - f->f_bufptr) >= 1) + return 0; + else + Util_DropReadAhead(f); + } + if (f->mode == MODE_READ_EOF) { + return -1; + } + if ((f->f_buf = PyMem_Malloc(bufsize)) == NULL) { + return -1; + } + Py_BEGIN_ALLOW_THREADS + chunksize = Util_UnivNewlineRead(&bzerror, f->fp, f->f_buf, + bufsize, f); + Py_END_ALLOW_THREADS + f->pos += chunksize; + if (bzerror == BZ_STREAM_END) { + f->size = f->pos; + f->mode = MODE_READ_EOF; + } else if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + Util_DropReadAhead(f); + return -1; + } + f->f_bufptr = f->f_buf; + f->f_bufend = f->f_buf + chunksize; + return 0; +} + +/* This is a hacked version of Python's + * fileobject.c:readahead_get_line_skip(). */ +static PyStringObject * +Util_ReadAheadGetLineSkip(BZ2FileObject *f, int skip, int bufsize) +{ + PyStringObject* s; + char *bufptr; + char *buf; + int len; + + if (f->f_buf == NULL) + if (Util_ReadAhead(f, bufsize) < 0) + return NULL; + + len = f->f_bufend - f->f_bufptr; + if (len == 0) + return (PyStringObject *) + PyString_FromStringAndSize(NULL, skip); + bufptr = memchr(f->f_bufptr, '\n', len); + if (bufptr != NULL) { + bufptr++; /* Count the '\n' */ + len = bufptr - f->f_bufptr; + s = (PyStringObject *) + PyString_FromStringAndSize(NULL, skip+len); + if (s == NULL) + return NULL; + memcpy(PyString_AS_STRING(s)+skip, f->f_bufptr, len); + f->f_bufptr = bufptr; + if (bufptr == f->f_bufend) + Util_DropReadAhead(f); + } else { + bufptr = f->f_bufptr; + buf = f->f_buf; + f->f_buf = NULL; /* Force new readahead buffer */ + s = Util_ReadAheadGetLineSkip(f, skip+len, + bufsize + (bufsize>>2)); + if (s == NULL) { + PyMem_Free(buf); + return NULL; + } + memcpy(PyString_AS_STRING(s)+skip, bufptr, len); + PyMem_Free(buf); + } + return s; +} + +/* ===================================================================== */ +/* Methods of BZ2File. */ + +PyDoc_STRVAR(BZ2File_read__doc__, +"read([size]) -> string\n\ +\n\ +Read at most size uncompressed bytes, returned as a string. If the size\n\ +argument is negative or omitted, read until EOF is reached.\n\ +"); + +/* This is a hacked version of Python's fileobject.c:file_read(). */ +static PyObject * +BZ2File_read(BZ2FileObject *self, PyObject *args) +{ + long bytesrequested = -1; + size_t bytesread, buffersize, chunksize; + int bzerror; + PyObject *ret = NULL; + + if (!PyArg_ParseTuple(args, "|l:read", &bytesrequested)) + return NULL; + + ACQUIRE_LOCK(self); + switch (self->mode) { + case MODE_READ: + break; + case MODE_READ_EOF: + ret = PyString_FromString(""); + goto cleanup; + case MODE_CLOSED: + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + goto cleanup; + default: + PyErr_SetString(PyExc_IOError, + "file is not ready for reading"); + goto cleanup; + } + + if (bytesrequested < 0) + buffersize = Util_NewBufferSize((size_t)0); + else + buffersize = bytesrequested; + if (buffersize > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "requested number of bytes is " + "more than a Python string can hold"); + goto cleanup; + } + ret = PyString_FromStringAndSize((char *)NULL, buffersize); + if (ret == NULL) + goto cleanup; + bytesread = 0; + + for (;;) { + Py_BEGIN_ALLOW_THREADS + chunksize = Util_UnivNewlineRead(&bzerror, self->fp, + BUF(ret)+bytesread, + buffersize-bytesread, + self); + self->pos += chunksize; + Py_END_ALLOW_THREADS + bytesread += chunksize; + if (bzerror == BZ_STREAM_END) { + self->size = self->pos; + self->mode = MODE_READ_EOF; + break; + } else if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + Py_DECREF(ret); + ret = NULL; + goto cleanup; + } + if (bytesrequested < 0) { + buffersize = Util_NewBufferSize(buffersize); + if (_PyString_Resize(&ret, buffersize) < 0) + goto cleanup; + } else { + break; + } + } + if (bytesread != buffersize) + _PyString_Resize(&ret, bytesread); + +cleanup: + RELEASE_LOCK(self); + return ret; +} + +PyDoc_STRVAR(BZ2File_readline__doc__, +"readline([size]) -> string\n\ +\n\ +Return the next line from the file, as a string, retaining newline.\n\ +A non-negative size argument will limit the maximum number of bytes to\n\ +return (an incomplete line may be returned then). Return an empty\n\ +string at EOF.\n\ +"); + +static PyObject * +BZ2File_readline(BZ2FileObject *self, PyObject *args) +{ + PyObject *ret = NULL; + int sizehint = -1; + + if (!PyArg_ParseTuple(args, "|i:readline", &sizehint)) + return NULL; + + ACQUIRE_LOCK(self); + switch (self->mode) { + case MODE_READ: + break; + case MODE_READ_EOF: + ret = PyString_FromString(""); + goto cleanup; + case MODE_CLOSED: + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + goto cleanup; + default: + PyErr_SetString(PyExc_IOError, + "file is not ready for reading"); + goto cleanup; + } + + if (sizehint == 0) + ret = PyString_FromString(""); + else + ret = Util_GetLine(self, (sizehint < 0) ? 0 : sizehint); + +cleanup: + RELEASE_LOCK(self); + return ret; +} + +PyDoc_STRVAR(BZ2File_readlines__doc__, +"readlines([size]) -> list\n\ +\n\ +Call readline() repeatedly and return a list of lines read.\n\ +The optional size argument, if given, is an approximate bound on the\n\ +total number of bytes in the lines returned.\n\ +"); + +/* This is a hacked version of Python's fileobject.c:file_readlines(). */ +static PyObject * +BZ2File_readlines(BZ2FileObject *self, PyObject *args) +{ + long sizehint = 0; + PyObject *list = NULL; + PyObject *line; + char small_buffer[SMALLCHUNK]; + char *buffer = small_buffer; + size_t buffersize = SMALLCHUNK; + PyObject *big_buffer = NULL; + size_t nfilled = 0; + size_t nread; + size_t totalread = 0; + char *p, *q, *end; + int err; + int shortread = 0; + int bzerror; + + if (!PyArg_ParseTuple(args, "|l:readlines", &sizehint)) + return NULL; + + ACQUIRE_LOCK(self); + switch (self->mode) { + case MODE_READ: + break; + case MODE_READ_EOF: + list = PyList_New(0); + goto cleanup; + case MODE_CLOSED: + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + goto cleanup; + default: + PyErr_SetString(PyExc_IOError, + "file is not ready for reading"); + goto cleanup; + } + + if ((list = PyList_New(0)) == NULL) + goto cleanup; + + for (;;) { + Py_BEGIN_ALLOW_THREADS + nread = Util_UnivNewlineRead(&bzerror, self->fp, + buffer+nfilled, + buffersize-nfilled, self); + self->pos += nread; + Py_END_ALLOW_THREADS + if (bzerror == BZ_STREAM_END) { + self->size = self->pos; + self->mode = MODE_READ_EOF; + if (nread == 0) { + sizehint = 0; + break; + } + shortread = 1; + } else if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + error: + Py_DECREF(list); + list = NULL; + goto cleanup; + } + totalread += nread; + p = memchr(buffer+nfilled, '\n', nread); + if (p == NULL) { + /* Need a larger buffer to fit this line */ + nfilled += nread; + buffersize *= 2; + if (buffersize > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "line is longer than a Python string can hold"); + goto error; + } + if (big_buffer == NULL) { + /* Create the big buffer */ + big_buffer = PyString_FromStringAndSize( + NULL, buffersize); + if (big_buffer == NULL) + goto error; + buffer = PyString_AS_STRING(big_buffer); + memcpy(buffer, small_buffer, nfilled); + } + else { + /* Grow the big buffer */ + _PyString_Resize(&big_buffer, buffersize); + buffer = PyString_AS_STRING(big_buffer); + } + continue; + } + end = buffer+nfilled+nread; + q = buffer; + do { + /* Process complete lines */ + p++; + line = PyString_FromStringAndSize(q, p-q); + if (line == NULL) + goto error; + err = PyList_Append(list, line); + Py_DECREF(line); + if (err != 0) + goto error; + q = p; + p = memchr(q, '\n', end-q); + } while (p != NULL); + /* Move the remaining incomplete line to the start */ + nfilled = end-q; + memmove(buffer, q, nfilled); + if (sizehint > 0) + if (totalread >= (size_t)sizehint) + break; + if (shortread) { + sizehint = 0; + break; + } + } + if (nfilled != 0) { + /* Partial last line */ + line = PyString_FromStringAndSize(buffer, nfilled); + if (line == NULL) + goto error; + if (sizehint > 0) { + /* Need to complete the last line */ + PyObject *rest = Util_GetLine(self, 0); + if (rest == NULL) { + Py_DECREF(line); + goto error; + } + PyString_Concat(&line, rest); + Py_DECREF(rest); + if (line == NULL) + goto error; + } + err = PyList_Append(list, line); + Py_DECREF(line); + if (err != 0) + goto error; + } + + cleanup: + RELEASE_LOCK(self); + if (big_buffer) { + Py_DECREF(big_buffer); + } + return list; +} + +PyDoc_STRVAR(BZ2File_xreadlines__doc__, +"xreadlines() -> self\n\ +\n\ +For backward compatibility. BZ2File objects now include the performance\n\ +optimizations previously implemented in the xreadlines module.\n\ +"); + +PyDoc_STRVAR(BZ2File_write__doc__, +"write(data) -> None\n\ +\n\ +Write the 'data' string to file. Note that due to buffering, close() may\n\ +be needed before the file on disk reflects the data written.\n\ +"); + +/* This is a hacked version of Python's fileobject.c:file_write(). */ +static PyObject * +BZ2File_write(BZ2FileObject *self, PyObject *args) +{ + PyObject *ret = NULL; + char *buf; + int len; + int bzerror; + + if (!PyArg_ParseTuple(args, "s#", &buf, &len)) + return NULL; + + ACQUIRE_LOCK(self); + switch (self->mode) { + case MODE_WRITE: + break; + + case MODE_CLOSED: + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + goto cleanup;; + + default: + PyErr_SetString(PyExc_IOError, + "file is not ready for writing"); + goto cleanup;; + } + + self->f_softspace = 0; + + Py_BEGIN_ALLOW_THREADS + BZ2_bzWrite (&bzerror, self->fp, buf, len); + self->pos += len; + Py_END_ALLOW_THREADS + + if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + goto cleanup; + } + + Py_INCREF(Py_None); + ret = Py_None; + +cleanup: + RELEASE_LOCK(self); + return ret; +} + +PyDoc_STRVAR(BZ2File_writelines__doc__, +"writelines(sequence_of_strings) -> None\n\ +\n\ +Write the sequence of strings to the file. Note that newlines are not\n\ +added. The sequence can be any iterable object producing strings. This is\n\ +equivalent to calling write() for each string.\n\ +"); + +/* This is a hacked version of Python's fileobject.c:file_writelines(). */ +static PyObject * +BZ2File_writelines(BZ2FileObject *self, PyObject *seq) +{ +#define CHUNKSIZE 1000 + PyObject *list = NULL; + PyObject *iter = NULL; + PyObject *ret = NULL; + PyObject *line; + int i, j, index, len, islist; + int bzerror; + + ACQUIRE_LOCK(self); + islist = PyList_Check(seq); + if (!islist) { + iter = PyObject_GetIter(seq); + if (iter == NULL) { + PyErr_SetString(PyExc_TypeError, + "writelines() requires an iterable argument"); + goto error; + } + list = PyList_New(CHUNKSIZE); + if (list == NULL) + goto error; + } + + /* Strategy: slurp CHUNKSIZE lines into a private list, + checking that they are all strings, then write that list + without holding the interpreter lock, then come back for more. */ + for (index = 0; ; index += CHUNKSIZE) { + if (islist) { + Py_XDECREF(list); + list = PyList_GetSlice(seq, index, index+CHUNKSIZE); + if (list == NULL) + goto error; + j = PyList_GET_SIZE(list); + } + else { + for (j = 0; j < CHUNKSIZE; j++) { + line = PyIter_Next(iter); + if (line == NULL) { + if (PyErr_Occurred()) + goto error; + break; + } + PyList_SetItem(list, j, line); + } + } + if (j == 0) + break; + + /* Check that all entries are indeed strings. If not, + apply the same rules as for file.write() and + convert the rets to strings. This is slow, but + seems to be the only way since all conversion APIs + could potentially execute Python code. */ + for (i = 0; i < j; i++) { + PyObject *v = PyList_GET_ITEM(list, i); + if (!PyString_Check(v)) { + const char *buffer; + int len; + if (PyObject_AsCharBuffer(v, &buffer, &len)) { + PyErr_SetString(PyExc_TypeError, + "writelines() " + "argument must be " + "a sequence of " + "strings"); + goto error; + } + line = PyString_FromStringAndSize(buffer, + len); + if (line == NULL) + goto error; + Py_DECREF(v); + PyList_SET_ITEM(list, i, line); + } + } + + self->f_softspace = 0; + + /* Since we are releasing the global lock, the + following code may *not* execute Python code. */ + Py_BEGIN_ALLOW_THREADS + for (i = 0; i < j; i++) { + line = PyList_GET_ITEM(list, i); + len = PyString_GET_SIZE(line); + BZ2_bzWrite (&bzerror, self->fp, + PyString_AS_STRING(line), len); + if (bzerror != BZ_OK) { + Py_BLOCK_THREADS + Util_CatchBZ2Error(bzerror); + goto error; + } + } + Py_END_ALLOW_THREADS + + if (j < CHUNKSIZE) + break; + } + + Py_INCREF(Py_None); + ret = Py_None; + + error: + RELEASE_LOCK(self); + Py_XDECREF(list); + Py_XDECREF(iter); + return ret; +#undef CHUNKSIZE +} + +PyDoc_STRVAR(BZ2File_seek__doc__, +"seek(offset [, whence]) -> None\n\ +\n\ +Move to new file position. Argument offset is a byte count. Optional\n\ +argument whence defaults to 0 (offset from start of file, offset\n\ +should be >= 0); other values are 1 (move relative to current position,\n\ +positive or negative), and 2 (move relative to end of file, usually\n\ +negative, although many platforms allow seeking beyond the end of a file).\n\ +\n\ +Note that seeking of bz2 files is emulated, and depending on the parameters\n\ +the operation may be extremely slow.\n\ +"); + +static PyObject * +BZ2File_seek(BZ2FileObject *self, PyObject *args) +{ + int where = 0; + long offset; + char small_buffer[SMALLCHUNK]; + char *buffer = small_buffer; + size_t buffersize = SMALLCHUNK; + int bytesread = 0; + int readsize; + int chunksize; + int bzerror; + int rewind = 0; + PyObject *ret = NULL; + + if (!PyArg_ParseTuple(args, "l|i:seek", &offset, &where)) + return NULL; + + ACQUIRE_LOCK(self); + Util_DropReadAhead(self); + switch (self->mode) { + case MODE_READ: + case MODE_READ_EOF: + break; + + case MODE_CLOSED: + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + goto cleanup;; + + default: + PyErr_SetString(PyExc_IOError, + "seek works only while reading"); + goto cleanup;; + } + + if (offset < 0) { + if (where == 1) { + offset = self->pos + offset; + rewind = 1; + } else if (where == 2) { + if (self->size == -1) { + assert(self->mode != MODE_READ_EOF); + for (;;) { + Py_BEGIN_ALLOW_THREADS + chunksize = Util_UnivNewlineRead( + &bzerror, self->fp, + buffer, buffersize, + self); + self->pos += chunksize; + Py_END_ALLOW_THREADS + + bytesread += chunksize; + if (bzerror == BZ_STREAM_END) { + break; + } else if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + goto cleanup; + } + } + self->mode = MODE_READ_EOF; + self->size = self->pos; + bytesread = 0; + } + offset = self->size + offset; + if (offset >= self->pos) + offset -= self->pos; + else + rewind = 1; + } + if (offset < 0) + offset = 0; + } else if (where == 0) { + if (offset >= self->pos) + offset -= self->pos; + else + rewind = 1; + } + + if (rewind) { + BZ2_bzReadClose(&bzerror, self->fp); + if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + goto cleanup; + } + ret = PyObject_CallMethod(self->file, "seek", "(i)", 0); + if (!ret) + goto cleanup; + Py_DECREF(ret); + ret = NULL; + self->pos = 0; + self->fp = BZ2_bzReadOpen(&bzerror, PyFile_AsFile(self->file), + 0, 0, NULL, 0); + if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + goto cleanup; + } + self->mode = MODE_READ; + } else if (self->mode == MODE_READ_EOF) { + goto exit; + } + + if (offset == 0) + goto exit; + + /* Before getting here, offset must be set to the number of bytes + * to walk forward. */ + for (;;) { + if ((size_t)offset-bytesread > buffersize) + readsize = buffersize; + else + readsize = offset-bytesread; + Py_BEGIN_ALLOW_THREADS + chunksize = Util_UnivNewlineRead(&bzerror, self->fp, + buffer, readsize, self); + self->pos += chunksize; + Py_END_ALLOW_THREADS + bytesread += chunksize; + if (bzerror == BZ_STREAM_END) { + self->size = self->pos; + self->mode = MODE_READ_EOF; + break; + } else if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + goto cleanup; + } + if (bytesread == offset) + break; + } + +exit: + Py_INCREF(Py_None); + ret = Py_None; + +cleanup: + RELEASE_LOCK(self); + return ret; +} + +PyDoc_STRVAR(BZ2File_tell__doc__, +"tell() -> int\n\ +\n\ +Return the current file position, an integer (may be a long integer).\n\ +"); + +static PyObject * +BZ2File_tell(BZ2FileObject *self, PyObject *args) +{ + PyObject *ret = NULL; + + if (self->mode == MODE_CLOSED) { + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + goto cleanup; + } + + ret = PyInt_FromLong(self->pos); + +cleanup: + return ret; +} + +PyDoc_STRVAR(BZ2File_close__doc__, +"close() -> None or (perhaps) an integer\n\ +\n\ +Close the file. Sets data attribute .closed to true. A closed file\n\ +cannot be used for further I/O operations. close() may be called more\n\ +than once without error.\n\ +"); + +static PyObject * +BZ2File_close(BZ2FileObject *self) +{ + PyObject *ret = NULL; + int bzerror = BZ_OK; + + ACQUIRE_LOCK(self); + switch (self->mode) { + case MODE_READ: + case MODE_READ_EOF: + BZ2_bzReadClose(&bzerror, self->fp); + break; + case MODE_WRITE: + BZ2_bzWriteClose(&bzerror, self->fp, + 0, NULL, NULL); + break; + } + self->mode = MODE_CLOSED; + ret = PyObject_CallMethod(self->file, "close", NULL); + if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + Py_XDECREF(ret); + ret = NULL; + } + + RELEASE_LOCK(self); + return ret; +} + +static PyObject *BZ2File_getiter(BZ2FileObject *self); + +static PyMethodDef BZ2File_methods[] = { + {"read", (PyCFunction)BZ2File_read, METH_VARARGS, BZ2File_read__doc__}, + {"readline", (PyCFunction)BZ2File_readline, METH_VARARGS, BZ2File_readline__doc__}, + {"readlines", (PyCFunction)BZ2File_readlines, METH_VARARGS, BZ2File_readlines__doc__}, + {"xreadlines", (PyCFunction)BZ2File_getiter, METH_VARARGS, BZ2File_xreadlines__doc__}, + {"write", (PyCFunction)BZ2File_write, METH_VARARGS, BZ2File_write__doc__}, + {"writelines", (PyCFunction)BZ2File_writelines, METH_O, BZ2File_writelines__doc__}, + {"seek", (PyCFunction)BZ2File_seek, METH_VARARGS, BZ2File_seek__doc__}, + {"tell", (PyCFunction)BZ2File_tell, METH_NOARGS, BZ2File_tell__doc__}, + {"close", (PyCFunction)BZ2File_close, METH_NOARGS, BZ2File_close__doc__}, + {NULL, NULL} /* sentinel */ +}; + + +/* ===================================================================== */ +/* Getters and setters of BZ2File. */ + +#ifdef WITH_UNIVERSAL_NEWLINES +/* This is a hacked version of Python's fileobject.c:get_newlines(). */ +static PyObject * +BZ2File_get_newlines(BZ2FileObject *self, void *closure) +{ + switch (self->f_newlinetypes) { + case NEWLINE_UNKNOWN: + Py_INCREF(Py_None); + return Py_None; + case NEWLINE_CR: + return PyString_FromString("\r"); + case NEWLINE_LF: + return PyString_FromString("\n"); + case NEWLINE_CR|NEWLINE_LF: + return Py_BuildValue("(ss)", "\r", "\n"); + case NEWLINE_CRLF: + return PyString_FromString("\r\n"); + case NEWLINE_CR|NEWLINE_CRLF: + return Py_BuildValue("(ss)", "\r", "\r\n"); + case NEWLINE_LF|NEWLINE_CRLF: + return Py_BuildValue("(ss)", "\n", "\r\n"); + case NEWLINE_CR|NEWLINE_LF|NEWLINE_CRLF: + return Py_BuildValue("(sss)", "\r", "\n", "\r\n"); + default: + PyErr_Format(PyExc_SystemError, + "Unknown newlines value 0x%x\n", + self->f_newlinetypes); + return NULL; + } +} +#endif + +static PyObject * +BZ2File_get_closed(BZ2FileObject *self, void *closure) +{ + return PyInt_FromLong(self->mode == MODE_CLOSED); +} + +static PyObject * +BZ2File_get_mode(BZ2FileObject *self, void *closure) +{ + return PyObject_GetAttrString(self->file, "mode"); +} + +static PyObject * +BZ2File_get_name(BZ2FileObject *self, void *closure) +{ + return PyObject_GetAttrString(self->file, "name"); +} + +static PyGetSetDef BZ2File_getset[] = { + {"closed", (getter)BZ2File_get_closed, NULL, + "True if the file is closed"}, +#ifdef WITH_UNIVERSAL_NEWLINES + {"newlines", (getter)BZ2File_get_newlines, NULL, + "end-of-line convention used in this file"}, +#endif + {"mode", (getter)BZ2File_get_mode, NULL, + "file mode ('r', 'w', or 'U')"}, + {"name", (getter)BZ2File_get_name, NULL, + "file name"}, + {NULL} /* Sentinel */ +}; + + +/* ===================================================================== */ +/* Members of BZ2File_Type. */ + +#undef OFF +#define OFF(x) offsetof(BZ2FileObject, x) + +static PyMemberDef BZ2File_members[] = { + {"softspace", T_INT, OFF(f_softspace), 0, + "flag indicating that a space needs to be printed; used by print"}, + {NULL} /* Sentinel */ +}; + +/* ===================================================================== */ +/* Slot definitions for BZ2File_Type. */ + +static int +BZ2File_init(BZ2FileObject *self, PyObject *args, PyObject *kwargs) +{ + static char *kwlist[] = {"filename", "mode", "buffering", + "compresslevel", 0}; + PyObject *name; + char *mode = "r"; + int buffering = -1; + int compresslevel = 9; + int bzerror; + int mode_char = 0; + + self->size = -1; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|sii:BZ2File", + kwlist, &name, &mode, &buffering, + &compresslevel)) + return -1; + + if (compresslevel < 1 || compresslevel > 9) { + PyErr_SetString(PyExc_ValueError, + "compresslevel must be between 1 and 9"); + return -1; + } + + for (;;) { + int error = 0; + switch (*mode) { + case 'r': + case 'w': + if (mode_char) + error = 1; + mode_char = *mode; + break; + + case 'b': + break; + + case 'U': +#ifdef WITH_UNIVERSAL_NEWLINES + self->f_univ_newline = 1; +#endif + break; + + default: + error = 1; + break; + } + if (error) { + PyErr_Format(PyExc_ValueError, + "invalid mode char %c", *mode); + return -1; + } + mode++; + if (*mode == '\0') + break; + } + + mode = (mode_char == 'r') ? "rb" : "wb"; + + self->file = PyObject_CallFunction((PyObject*)&PyFile_Type, "(Osi)", + name, mode, buffering); + if (self->file == NULL) + return -1; + + /* From now on, we have stuff to dealloc, so jump to error label + * instead of returning */ + +#ifdef WITH_THREAD + self->lock = PyThread_allocate_lock(); + if (!self->lock) + goto error; +#endif + + if (mode_char == 'r') + self->fp = BZ2_bzReadOpen(&bzerror, + PyFile_AsFile(self->file), + 0, 0, NULL, 0); + else + self->fp = BZ2_bzWriteOpen(&bzerror, + PyFile_AsFile(self->file), + compresslevel, 0, 0); + + if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + goto error; + } + + self->mode = (mode_char == 'r') ? MODE_READ : MODE_WRITE; + + return 0; + +error: + Py_DECREF(self->file); +#ifdef WITH_THREAD + if (self->lock) + PyThread_free_lock(self->lock); +#endif + return -1; +} + +static void +BZ2File_dealloc(BZ2FileObject *self) +{ + int bzerror; +#ifdef WITH_THREAD + if (self->lock) + PyThread_free_lock(self->lock); +#endif + switch (self->mode) { + case MODE_READ: + case MODE_READ_EOF: + BZ2_bzReadClose(&bzerror, self->fp); + break; + case MODE_WRITE: + BZ2_bzWriteClose(&bzerror, self->fp, + 0, NULL, NULL); + break; + } + Util_DropReadAhead(self); + Py_XDECREF(self->file); + self->ob_type->tp_free((PyObject *)self); +} + +/* This is a hacked version of Python's fileobject.c:file_getiter(). */ +static PyObject * +BZ2File_getiter(BZ2FileObject *self) +{ + if (self->mode == MODE_CLOSED) { + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + return NULL; + } + Py_INCREF((PyObject*)self); + return (PyObject *)self; +} + +/* This is a hacked version of Python's fileobject.c:file_iternext(). */ +#define READAHEAD_BUFSIZE 8192 +static PyObject * +BZ2File_iternext(BZ2FileObject *self) +{ + PyStringObject* ret; + ACQUIRE_LOCK(self); + if (self->mode == MODE_CLOSED) { + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + return NULL; + } + ret = Util_ReadAheadGetLineSkip(self, 0, READAHEAD_BUFSIZE); + RELEASE_LOCK(self); + if (ret == NULL || PyString_GET_SIZE(ret) == 0) { + Py_XDECREF(ret); + return NULL; + } + return (PyObject *)ret; +} + +/* ===================================================================== */ +/* BZ2File_Type definition. */ + +PyDoc_VAR(BZ2File__doc__) = +PyDoc_STR( +"BZ2File(name [, mode='r', buffering=0, compresslevel=9]) -> file object\n\ +\n\ +Open a bz2 file. The mode can be 'r' or 'w', for reading (default) or\n\ +writing. When opened for writing, the file will be created if it doesn't\n\ +exist, and truncated otherwise. If the buffering argument is given, 0 means\n\ +unbuffered, and larger numbers specify the buffer size. If compresslevel\n\ +is given, must be a number between 1 and 9.\n\ +") +#ifdef WITH_UNIVERSAL_NEWLINES +PyDoc_STR( +"\n\ +Add a 'U' to mode to open the file for input with universal newline\n\ +support. Any line ending in the input file will be seen as a '\\n' in\n\ +Python. Also, a file so opened gains the attribute 'newlines'; the value\n\ +for this attribute is one of None (no newline read yet), '\\r', '\\n',\n\ +'\\r\\n' or a tuple containing all the newline types seen. Universal\n\ +newlines are available only when reading.\n\ +") +#endif +; + +static PyTypeObject BZ2File_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "bz2.BZ2File", /*tp_name*/ + sizeof(BZ2FileObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)BZ2File_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + PyObject_GenericSetAttr,/*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + BZ2File__doc__, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + (getiterfunc)BZ2File_getiter, /*tp_iter*/ + (iternextfunc)BZ2File_iternext, /*tp_iternext*/ + BZ2File_methods, /*tp_methods*/ + BZ2File_members, /*tp_members*/ + BZ2File_getset, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + (initproc)BZ2File_init, /*tp_init*/ + PyType_GenericAlloc, /*tp_alloc*/ + PyType_GenericNew, /*tp_new*/ + _PyObject_Del, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + + +/* ===================================================================== */ +/* Methods of BZ2Comp. */ + +PyDoc_STRVAR(BZ2Comp_compress__doc__, +"compress(data) -> string\n\ +\n\ +Provide more data to the compressor object. It will return chunks of\n\ +compressed data whenever possible. When you've finished providing data\n\ +to compress, call the flush() method to finish the compression process,\n\ +and return what is left in the internal buffers.\n\ +"); + +static PyObject * +BZ2Comp_compress(BZ2CompObject *self, PyObject *args) +{ + char *data; + int datasize; + int bufsize = SMALLCHUNK; + PY_LONG_LONG totalout; + PyObject *ret = NULL; + bz_stream *bzs = &self->bzs; + int bzerror; + + if (!PyArg_ParseTuple(args, "s#", &data, &datasize)) + return NULL; + + ACQUIRE_LOCK(self); + if (!self->running) { + PyErr_SetString(PyExc_ValueError, + "this object was already flushed"); + goto error; + } + + ret = PyString_FromStringAndSize(NULL, bufsize); + if (!ret) + goto error; + + bzs->next_in = data; + bzs->avail_in = datasize; + bzs->next_out = BUF(ret); + bzs->avail_out = bufsize; + + totalout = BZS_TOTAL_OUT(bzs); + + for (;;) { + Py_BEGIN_ALLOW_THREADS + bzerror = BZ2_bzCompress(bzs, BZ_RUN); + Py_END_ALLOW_THREADS + if (bzerror != BZ_RUN_OK) { + Util_CatchBZ2Error(bzerror); + goto error; + } + if (bzs->avail_out == 0) { + bufsize = Util_NewBufferSize(bufsize); + if (_PyString_Resize(&ret, bufsize) < 0) { + BZ2_bzCompressEnd(bzs); + goto error; + } + bzs->next_out = BUF(ret) + (BZS_TOTAL_OUT(bzs) + - totalout); + bzs->avail_out = bufsize - (bzs->next_out - BUF(ret)); + } else if (bzs->avail_in == 0) { + break; + } + } + + _PyString_Resize(&ret, (int)(BZS_TOTAL_OUT(bzs) - totalout)); + + RELEASE_LOCK(self); + return ret; + +error: + RELEASE_LOCK(self); + Py_XDECREF(ret); + return NULL; +} + +PyDoc_STRVAR(BZ2Comp_flush__doc__, +"flush() -> string\n\ +\n\ +Finish the compression process and return what is left in internal buffers.\n\ +You must not use the compressor object after calling this method.\n\ +"); + +static PyObject * +BZ2Comp_flush(BZ2CompObject *self) +{ + int bufsize = SMALLCHUNK; + PyObject *ret = NULL; + bz_stream *bzs = &self->bzs; + PY_LONG_LONG totalout; + int bzerror; + + ACQUIRE_LOCK(self); + if (!self->running) { + PyErr_SetString(PyExc_ValueError, "object was already " + "flushed"); + goto error; + } + self->running = 0; + + ret = PyString_FromStringAndSize(NULL, bufsize); + if (!ret) + goto error; + + bzs->next_out = BUF(ret); + bzs->avail_out = bufsize; + + totalout = BZS_TOTAL_OUT(bzs); + + for (;;) { + Py_BEGIN_ALLOW_THREADS + bzerror = BZ2_bzCompress(bzs, BZ_FINISH); + Py_END_ALLOW_THREADS + if (bzerror == BZ_STREAM_END) { + break; + } else if (bzerror != BZ_FINISH_OK) { + Util_CatchBZ2Error(bzerror); + goto error; + } + if (bzs->avail_out == 0) { + bufsize = Util_NewBufferSize(bufsize); + if (_PyString_Resize(&ret, bufsize) < 0) + goto error; + bzs->next_out = BUF(ret); + bzs->next_out = BUF(ret) + (BZS_TOTAL_OUT(bzs) + - totalout); + bzs->avail_out = bufsize - (bzs->next_out - BUF(ret)); + } + } + + if (bzs->avail_out != 0) + _PyString_Resize(&ret, (int)(BZS_TOTAL_OUT(bzs) - totalout)); + + RELEASE_LOCK(self); + return ret; + +error: + RELEASE_LOCK(self); + Py_XDECREF(ret); + return NULL; +} + +static PyMethodDef BZ2Comp_methods[] = { + {"compress", (PyCFunction)BZ2Comp_compress, METH_VARARGS, + BZ2Comp_compress__doc__}, + {"flush", (PyCFunction)BZ2Comp_flush, METH_NOARGS, + BZ2Comp_flush__doc__}, + {NULL, NULL} /* sentinel */ +}; + + +/* ===================================================================== */ +/* Slot definitions for BZ2Comp_Type. */ + +static int +BZ2Comp_init(BZ2CompObject *self, PyObject *args, PyObject *kwargs) +{ + int compresslevel = 9; + int bzerror; + static char *kwlist[] = {"compresslevel", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:BZ2Compressor", + kwlist, &compresslevel)) + return -1; + + if (compresslevel < 1 || compresslevel > 9) { + PyErr_SetString(PyExc_ValueError, + "compresslevel must be between 1 and 9"); + goto error; + } + +#ifdef WITH_THREAD + self->lock = PyThread_allocate_lock(); + if (!self->lock) + goto error; +#endif + + memset(&self->bzs, 0, sizeof(bz_stream)); + bzerror = BZ2_bzCompressInit(&self->bzs, compresslevel, 0, 0); + if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + goto error; + } + + self->running = 1; + + return 0; +error: +#ifdef WITH_THREAD + if (self->lock) + PyThread_free_lock(self->lock); +#endif + return -1; +} + +static void +BZ2Comp_dealloc(BZ2CompObject *self) +{ +#ifdef WITH_THREAD + if (self->lock) + PyThread_free_lock(self->lock); +#endif + BZ2_bzCompressEnd(&self->bzs); + self->ob_type->tp_free((PyObject *)self); +} + + +/* ===================================================================== */ +/* BZ2Comp_Type definition. */ + +PyDoc_STRVAR(BZ2Comp__doc__, +"BZ2Compressor([compresslevel=9]) -> compressor object\n\ +\n\ +Create a new compressor object. This object may be used to compress\n\ +data sequentially. If you want to compress data in one shot, use the\n\ +compress() function instead. The compresslevel parameter, if given,\n\ +must be a number between 1 and 9.\n\ +"); + +static PyTypeObject BZ2Comp_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "bz2.BZ2Compressor", /*tp_name*/ + sizeof(BZ2CompObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)BZ2Comp_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + PyObject_GenericSetAttr,/*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + BZ2Comp__doc__, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + BZ2Comp_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + (initproc)BZ2Comp_init, /*tp_init*/ + PyType_GenericAlloc, /*tp_alloc*/ + PyType_GenericNew, /*tp_new*/ + _PyObject_Del, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + + +/* ===================================================================== */ +/* Members of BZ2Decomp. */ + +#undef OFF +#define OFF(x) offsetof(BZ2DecompObject, x) + +static PyMemberDef BZ2Decomp_members[] = { + {"unused_data", T_OBJECT, OFF(unused_data), RO}, + {NULL} /* Sentinel */ +}; + + +/* ===================================================================== */ +/* Methods of BZ2Decomp. */ + +PyDoc_STRVAR(BZ2Decomp_decompress__doc__, +"decompress(data) -> string\n\ +\n\ +Provide more data to the decompressor object. It will return chunks\n\ +of decompressed data whenever possible. If you try to decompress data\n\ +after the end of stream is found, EOFError will be raised. If any data\n\ +was found after the end of stream, it'll be ignored and saved in\n\ +unused_data attribute.\n\ +"); + +static PyObject * +BZ2Decomp_decompress(BZ2DecompObject *self, PyObject *args) +{ + char *data; + int datasize; + int bufsize = SMALLCHUNK; + PY_LONG_LONG totalout; + PyObject *ret = NULL; + bz_stream *bzs = &self->bzs; + int bzerror; + + if (!PyArg_ParseTuple(args, "s#", &data, &datasize)) + return NULL; + + ACQUIRE_LOCK(self); + if (!self->running) { + PyErr_SetString(PyExc_EOFError, "end of stream was " + "already found"); + goto error; + } + + ret = PyString_FromStringAndSize(NULL, bufsize); + if (!ret) + goto error; + + bzs->next_in = data; + bzs->avail_in = datasize; + bzs->next_out = BUF(ret); + bzs->avail_out = bufsize; + + totalout = BZS_TOTAL_OUT(bzs); + + for (;;) { + Py_BEGIN_ALLOW_THREADS + bzerror = BZ2_bzDecompress(bzs); + Py_END_ALLOW_THREADS + if (bzerror == BZ_STREAM_END) { + if (bzs->avail_in != 0) { + Py_DECREF(self->unused_data); + self->unused_data = + PyString_FromStringAndSize(bzs->next_in, + bzs->avail_in); + } + self->running = 0; + break; + } + if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + goto error; + } + if (bzs->avail_out == 0) { + bufsize = Util_NewBufferSize(bufsize); + if (_PyString_Resize(&ret, bufsize) < 0) { + BZ2_bzDecompressEnd(bzs); + goto error; + } + bzs->next_out = BUF(ret); + bzs->next_out = BUF(ret) + (BZS_TOTAL_OUT(bzs) + - totalout); + bzs->avail_out = bufsize - (bzs->next_out - BUF(ret)); + } else if (bzs->avail_in == 0) { + break; + } + } + + if (bzs->avail_out != 0) + _PyString_Resize(&ret, (int)(BZS_TOTAL_OUT(bzs) - totalout)); + + RELEASE_LOCK(self); + return ret; + +error: + RELEASE_LOCK(self); + Py_XDECREF(ret); + return NULL; +} + +static PyMethodDef BZ2Decomp_methods[] = { + {"decompress", (PyCFunction)BZ2Decomp_decompress, METH_VARARGS, BZ2Decomp_decompress__doc__}, + {NULL, NULL} /* sentinel */ +}; + + +/* ===================================================================== */ +/* Slot definitions for BZ2Decomp_Type. */ + +static int +BZ2Decomp_init(BZ2DecompObject *self, PyObject *args, PyObject *kwargs) +{ + int bzerror; + + if (!PyArg_ParseTuple(args, ":BZ2Decompressor")) + return -1; + +#ifdef WITH_THREAD + self->lock = PyThread_allocate_lock(); + if (!self->lock) + goto error; +#endif + + self->unused_data = PyString_FromString(""); + if (!self->unused_data) + goto error; + + memset(&self->bzs, 0, sizeof(bz_stream)); + bzerror = BZ2_bzDecompressInit(&self->bzs, 0, 0); + if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + goto error; + } + + self->running = 1; + + return 0; + +error: +#ifdef WITH_THREAD + if (self->lock) + PyThread_free_lock(self->lock); +#endif + Py_XDECREF(self->unused_data); + return -1; +} + +static void +BZ2Decomp_dealloc(BZ2DecompObject *self) +{ +#ifdef WITH_THREAD + if (self->lock) + PyThread_free_lock(self->lock); +#endif + Py_XDECREF(self->unused_data); + BZ2_bzDecompressEnd(&self->bzs); + self->ob_type->tp_free((PyObject *)self); +} + + +/* ===================================================================== */ +/* BZ2Decomp_Type definition. */ + +PyDoc_STRVAR(BZ2Decomp__doc__, +"BZ2Decompressor() -> decompressor object\n\ +\n\ +Create a new decompressor object. This object may be used to decompress\n\ +data sequentially. If you want to decompress data in one shot, use the\n\ +decompress() function instead.\n\ +"); + +static PyTypeObject BZ2Decomp_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "bz2.BZ2Decompressor", /*tp_name*/ + sizeof(BZ2DecompObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)BZ2Decomp_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + PyObject_GenericGetAttr,/*tp_getattro*/ + PyObject_GenericSetAttr,/*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ + BZ2Decomp__doc__, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + BZ2Decomp_methods, /*tp_methods*/ + BZ2Decomp_members, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + (initproc)BZ2Decomp_init, /*tp_init*/ + PyType_GenericAlloc, /*tp_alloc*/ + PyType_GenericNew, /*tp_new*/ + _PyObject_Del, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + + +/* ===================================================================== */ +/* Module functions. */ + +PyDoc_STRVAR(bz2_compress__doc__, +"compress(data [, compresslevel=9]) -> string\n\ +\n\ +Compress data in one shot. If you want to compress data sequentially,\n\ +use an instance of BZ2Compressor instead. The compresslevel parameter, if\n\ +given, must be a number between 1 and 9.\n\ +"); + +static PyObject * +bz2_compress(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int compresslevel=9; + char *data; + int datasize; + int bufsize; + PyObject *ret = NULL; + bz_stream _bzs; + bz_stream *bzs = &_bzs; + int bzerror; + static char *kwlist[] = {"data", "compresslevel", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|i", + kwlist, &data, &datasize, + &compresslevel)) + return NULL; + + if (compresslevel < 1 || compresslevel > 9) { + PyErr_SetString(PyExc_ValueError, + "compresslevel must be between 1 and 9"); + return NULL; + } + + /* Conforming to bz2 manual, this is large enough to fit compressed + * data in one shot. We will check it later anyway. */ + bufsize = datasize + (datasize/100+1) + 600; + + ret = PyString_FromStringAndSize(NULL, bufsize); + if (!ret) + return NULL; + + memset(bzs, 0, sizeof(bz_stream)); + + bzs->next_in = data; + bzs->avail_in = datasize; + bzs->next_out = BUF(ret); + bzs->avail_out = bufsize; + + bzerror = BZ2_bzCompressInit(bzs, compresslevel, 0, 0); + if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + Py_DECREF(ret); + return NULL; + } + + for (;;) { + Py_BEGIN_ALLOW_THREADS + bzerror = BZ2_bzCompress(bzs, BZ_FINISH); + Py_END_ALLOW_THREADS + if (bzerror == BZ_STREAM_END) { + break; + } else if (bzerror != BZ_FINISH_OK) { + BZ2_bzCompressEnd(bzs); + Util_CatchBZ2Error(bzerror); + Py_DECREF(ret); + return NULL; + } + if (bzs->avail_out == 0) { + bufsize = Util_NewBufferSize(bufsize); + if (_PyString_Resize(&ret, bufsize) < 0) { + BZ2_bzCompressEnd(bzs); + Py_DECREF(ret); + return NULL; + } + bzs->next_out = BUF(ret) + BZS_TOTAL_OUT(bzs); + bzs->avail_out = bufsize - (bzs->next_out - BUF(ret)); + } + } + + if (bzs->avail_out != 0) + _PyString_Resize(&ret, (int)BZS_TOTAL_OUT(bzs)); + BZ2_bzCompressEnd(bzs); + + return ret; +} + +PyDoc_STRVAR(bz2_decompress__doc__, +"decompress(data) -> decompressed data\n\ +\n\ +Decompress data in one shot. If you want to decompress data sequentially,\n\ +use an instance of BZ2Decompressor instead.\n\ +"); + +static PyObject * +bz2_decompress(PyObject *self, PyObject *args) +{ + char *data; + int datasize; + int bufsize = SMALLCHUNK; + PyObject *ret; + bz_stream _bzs; + bz_stream *bzs = &_bzs; + int bzerror; + + if (!PyArg_ParseTuple(args, "s#", &data, &datasize)) + return NULL; + + if (datasize == 0) + return PyString_FromString(""); + + ret = PyString_FromStringAndSize(NULL, bufsize); + if (!ret) + return NULL; + + memset(bzs, 0, sizeof(bz_stream)); + + bzs->next_in = data; + bzs->avail_in = datasize; + bzs->next_out = BUF(ret); + bzs->avail_out = bufsize; + + bzerror = BZ2_bzDecompressInit(bzs, 0, 0); + if (bzerror != BZ_OK) { + Util_CatchBZ2Error(bzerror); + Py_DECREF(ret); + return NULL; + } + + for (;;) { + Py_BEGIN_ALLOW_THREADS + bzerror = BZ2_bzDecompress(bzs); + Py_END_ALLOW_THREADS + if (bzerror == BZ_STREAM_END) { + break; + } else if (bzerror != BZ_OK) { + BZ2_bzDecompressEnd(bzs); + Util_CatchBZ2Error(bzerror); + Py_DECREF(ret); + return NULL; + } + if (bzs->avail_out == 0) { + bufsize = Util_NewBufferSize(bufsize); + if (_PyString_Resize(&ret, bufsize) < 0) { + BZ2_bzDecompressEnd(bzs); + Py_DECREF(ret); + return NULL; + } + bzs->next_out = BUF(ret) + BZS_TOTAL_OUT(bzs); + bzs->avail_out = bufsize - (bzs->next_out - BUF(ret)); + } else if (bzs->avail_in == 0) { + BZ2_bzDecompressEnd(bzs); + PyErr_SetString(PyExc_ValueError, + "couldn't find end of stream"); + Py_DECREF(ret); + return NULL; + } + } + + if (bzs->avail_out != 0) + _PyString_Resize(&ret, (int)BZS_TOTAL_OUT(bzs)); + BZ2_bzDecompressEnd(bzs); + + return ret; +} + +static PyMethodDef bz2_methods[] = { + {"compress", (PyCFunction) bz2_compress, METH_VARARGS|METH_KEYWORDS, + bz2_compress__doc__}, + {"decompress", (PyCFunction) bz2_decompress, METH_VARARGS, + bz2_decompress__doc__}, + {NULL, NULL} /* sentinel */ +}; + +/* ===================================================================== */ +/* Initialization function. */ + +PyDoc_STRVAR(bz2__doc__, +"The python bz2 module provides a comprehensive interface for\n\ +the bz2 compression library. It implements a complete file\n\ +interface, one shot (de)compression functions, and types for\n\ +sequential (de)compression.\n\ +"); + +PyMODINIT_FUNC +initbz2(void) +{ + PyObject *m; + + BZ2File_Type.ob_type = &PyType_Type; + BZ2Comp_Type.ob_type = &PyType_Type; + BZ2Decomp_Type.ob_type = &PyType_Type; + + m = Py_InitModule3("bz2", bz2_methods, bz2__doc__); + + PyModule_AddObject(m, "__author__", PyString_FromString(__author__)); + + Py_INCREF(&BZ2File_Type); + PyModule_AddObject(m, "BZ2File", (PyObject *)&BZ2File_Type); + + Py_INCREF(&BZ2Comp_Type); + PyModule_AddObject(m, "BZ2Compressor", (PyObject *)&BZ2Comp_Type); + + Py_INCREF(&BZ2Decomp_Type); + PyModule_AddObject(m, "BZ2Decompressor", (PyObject *)&BZ2Decomp_Type); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cPickle.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cPickle.c new file mode 100644 index 00000000..3adaa11a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cPickle.c @@ -0,0 +1,5761 @@ +#include "Python.h" +#include "cStringIO.h" +#include "structmember.h" + +PyDoc_STRVAR(cPickle_module_documentation, +"C implementation and optimization of the Python pickle module."); + +#ifndef Py_eval_input +#include +#define Py_eval_input eval_input +#endif /* Py_eval_input */ + +#define DEL_LIST_SLICE(list, from, to) (PyList_SetSlice(list, from, to, NULL)) + +#define WRITE_BUF_SIZE 256 + +/* Bump this when new opcodes are added to the pickle protocol. */ +#define HIGHEST_PROTOCOL 2 + +/* + * Pickle opcodes. These must be kept in synch with pickle.py. Extensive + * docs are in pickletools.py. + */ +#define MARK '(' +#define STOP '.' +#define POP '0' +#define POP_MARK '1' +#define DUP '2' +#define FLOAT 'F' +#define BINFLOAT 'G' +#define INT 'I' +#define BININT 'J' +#define BININT1 'K' +#define LONG 'L' +#define BININT2 'M' +#define NONE 'N' +#define PERSID 'P' +#define BINPERSID 'Q' +#define REDUCE 'R' +#define STRING 'S' +#define BINSTRING 'T' +#define SHORT_BINSTRING 'U' +#define UNICODE 'V' +#define BINUNICODE 'X' +#define APPEND 'a' +#define BUILD 'b' +#define GLOBAL 'c' +#define DICT 'd' +#define EMPTY_DICT '}' +#define APPENDS 'e' +#define GET 'g' +#define BINGET 'h' +#define INST 'i' +#define LONG_BINGET 'j' +#define LIST 'l' +#define EMPTY_LIST ']' +#define OBJ 'o' +#define PUT 'p' +#define BINPUT 'q' +#define LONG_BINPUT 'r' +#define SETITEM 's' +#define TUPLE 't' +#define EMPTY_TUPLE ')' +#define SETITEMS 'u' + +/* Protocol 2. */ +#define PROTO '\x80' /* identify pickle protocol */ +#define NEWOBJ '\x81' /* build object by applying cls.__new__ to argtuple */ +#define EXT1 '\x82' /* push object from extension registry; 1-byte index */ +#define EXT2 '\x83' /* ditto, but 2-byte index */ +#define EXT4 '\x84' /* ditto, but 4-byte index */ +#define TUPLE1 '\x85' /* build 1-tuple from stack top */ +#define TUPLE2 '\x86' /* build 2-tuple from two topmost stack items */ +#define TUPLE3 '\x87' /* build 3-tuple from three topmost stack items */ +#define NEWTRUE '\x88' /* push True */ +#define NEWFALSE '\x89' /* push False */ +#define LONG1 '\x8a' /* push long from < 256 bytes */ +#define LONG4 '\x8b' /* push really big long */ + +/* There aren't opcodes -- they're ways to pickle bools before protocol 2, + * so that unpicklers written before bools were introduced unpickle them + * as ints, but unpicklers after can recognize that bools were intended. + * Note that protocol 2 added direct ways to pickle bools. + */ +#undef TRUE +#define TRUE "I01\n" +#undef FALSE +#define FALSE "I00\n" + +/* Keep in synch with pickle.Pickler._BATCHSIZE. This is how many elements + * batch_list/dict() pumps out before doing APPENDS/SETITEMS. Nothing will + * break if this gets out of synch with pickle.py, but it's unclear that + * would help anything either. + */ +#define BATCHSIZE 1000 + +static char MARKv = MARK; + +static PyObject *PickleError; +static PyObject *PicklingError; +static PyObject *UnpickleableError; +static PyObject *UnpicklingError; +static PyObject *BadPickleGet; + +/* As the name says, an empty tuple. */ +static PyObject *empty_tuple; + +/* copy_reg.dispatch_table, {type_object: pickling_function} */ +static PyObject *dispatch_table; + +/* For EXT[124] opcodes. */ +/* copy_reg._extension_registry, {(module_name, function_name): code} */ +static PyObject *extension_registry; +/* copy_reg._inverted_registry, {code: (module_name, function_name)} */ +static PyObject *inverted_registry; +/* copy_reg._extension_cache, {code: object} */ +static PyObject *extension_cache; + +/* For looking up name pairs in copy_reg._extension_registry. */ +static PyObject *two_tuple; + +static PyObject *__class___str, *__getinitargs___str, *__dict___str, + *__getstate___str, *__setstate___str, *__name___str, *__reduce___str, + *__reduce_ex___str, + *write_str, *append_str, + *read_str, *readline_str, *__main___str, *__basicnew___str, + *copy_reg_str, *dispatch_table_str; + +/************************************************************************* + Internal Data type for pickle data. */ + +typedef struct { + PyObject_HEAD + int length; /* number of initial slots in data currently used */ + int size; /* number of slots in data allocated */ + PyObject **data; +} Pdata; + +static void +Pdata_dealloc(Pdata *self) +{ + int i; + PyObject **p; + + for (i = self->length, p = self->data; --i >= 0; p++) { + Py_DECREF(*p); + } + if (self->data) + free(self->data); + PyObject_Del(self); +} + +static PyTypeObject PdataType = { + PyObject_HEAD_INIT(NULL) 0, "cPickle.Pdata", sizeof(Pdata), 0, + (destructor)Pdata_dealloc, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0L,0L,0L,0L, "" +}; + +#define Pdata_Check(O) ((O)->ob_type == &PdataType) + +static PyObject * +Pdata_New(void) +{ + Pdata *self; + + if (!(self = PyObject_New(Pdata, &PdataType))) + return NULL; + self->size = 8; + self->length = 0; + self->data = malloc(self->size * sizeof(PyObject*)); + if (self->data) + return (PyObject*)self; + Py_DECREF(self); + return PyErr_NoMemory(); +} + +static int +stackUnderflow(void) +{ + PyErr_SetString(UnpicklingError, "unpickling stack underflow"); + return -1; +} + +/* Retain only the initial clearto items. If clearto >= the current + * number of items, this is a (non-erroneous) NOP. + */ +static int +Pdata_clear(Pdata *self, int clearto) +{ + int i; + PyObject **p; + + if (clearto < 0) return stackUnderflow(); + if (clearto >= self->length) return 0; + + for (i = self->length, p = self->data + clearto; + --i >= clearto; + p++) { + Py_DECREF(*p); + } + self->length = clearto; + + return 0; +} + +static int +Pdata_grow(Pdata *self) +{ + int bigger; + size_t nbytes; + + bigger = self->size << 1; + if (bigger <= 0) /* was 0, or new value overflows */ + goto nomemory; + if ((int)(size_t)bigger != bigger) + goto nomemory; + nbytes = (size_t)bigger * sizeof(PyObject *); + if (nbytes / sizeof(PyObject *) != (size_t)bigger) + goto nomemory; + self->data = realloc(self->data, nbytes); + if (self->data == NULL) + goto nomemory; + self->size = bigger; + return 0; + + nomemory: + self->size = 0; + PyErr_NoMemory(); + return -1; +} + +/* D is a Pdata*. Pop the topmost element and store it into V, which + * must be an lvalue holding PyObject*. On stack underflow, UnpicklingError + * is raised and V is set to NULL. D and V may be evaluated several times. + */ +#define PDATA_POP(D, V) { \ + if ((D)->length) \ + (V) = (D)->data[--((D)->length)]; \ + else { \ + PyErr_SetString(UnpicklingError, "bad pickle data"); \ + (V) = NULL; \ + } \ +} + +/* PDATA_PUSH and PDATA_APPEND both push rvalue PyObject* O on to Pdata* + * D. If the Pdata stack can't be grown to hold the new value, both + * raise MemoryError and execute "return ER". The difference is in ownership + * of O after: _PUSH transfers ownership of O from the caller to the stack + * (no incref of O is done, and in case of error O is decrefed), while + * _APPEND pushes a new reference. + */ + +/* Push O on stack D, giving ownership of O to the stack. */ +#define PDATA_PUSH(D, O, ER) { \ + if (((Pdata*)(D))->length == ((Pdata*)(D))->size && \ + Pdata_grow((Pdata*)(D)) < 0) { \ + Py_DECREF(O); \ + return ER; \ + } \ + ((Pdata*)(D))->data[((Pdata*)(D))->length++] = (O); \ +} + +/* Push O on stack D, pushing a new reference. */ +#define PDATA_APPEND(D, O, ER) { \ + if (((Pdata*)(D))->length == ((Pdata*)(D))->size && \ + Pdata_grow((Pdata*)(D)) < 0) \ + return ER; \ + Py_INCREF(O); \ + ((Pdata*)(D))->data[((Pdata*)(D))->length++] = (O); \ +} + + +static PyObject * +Pdata_popTuple(Pdata *self, int start) +{ + PyObject *r; + int i, j, l; + + l = self->length-start; + r = PyTuple_New(l); + if (r == NULL) + return NULL; + for (i = start, j = 0 ; j < l; i++, j++) + PyTuple_SET_ITEM(r, j, self->data[i]); + + self->length = start; + return r; +} + +static PyObject * +Pdata_popList(Pdata *self, int start) +{ + PyObject *r; + int i, j, l; + + l=self->length-start; + if (!( r=PyList_New(l))) return NULL; + for (i=start, j=0 ; j < l; i++, j++) + PyList_SET_ITEM(r, j, self->data[i]); + + self->length=start; + return r; +} + +/*************************************************************************/ + +#define ARG_TUP(self, o) { \ + if (self->arg || (self->arg=PyTuple_New(1))) { \ + Py_XDECREF(PyTuple_GET_ITEM(self->arg,0)); \ + PyTuple_SET_ITEM(self->arg,0,o); \ + } \ + else { \ + Py_DECREF(o); \ + } \ +} + +#define FREE_ARG_TUP(self) { \ + if (self->arg->ob_refcnt > 1) { \ + Py_DECREF(self->arg); \ + self->arg=NULL; \ + } \ + } + +typedef struct Picklerobject { + PyObject_HEAD + FILE *fp; + PyObject *write; + PyObject *file; + PyObject *memo; + PyObject *arg; + PyObject *pers_func; + PyObject *inst_pers_func; + + /* pickle protocol number, >= 0 */ + int proto; + + /* bool, true if proto > 0 */ + int bin; + + int fast; /* Fast mode doesn't save in memo, don't use if circ ref */ + int nesting; + int (*write_func)(struct Picklerobject *, char *, int); + char *write_buf; + int buf_size; + PyObject *dispatch_table; + int fast_container; /* count nested container dumps */ + PyObject *fast_memo; +} Picklerobject; + +#ifndef PY_CPICKLE_FAST_LIMIT +#define PY_CPICKLE_FAST_LIMIT 50 +#endif + +static PyTypeObject Picklertype; + +typedef struct Unpicklerobject { + PyObject_HEAD + FILE *fp; + PyObject *file; + PyObject *readline; + PyObject *read; + PyObject *memo; + PyObject *arg; + Pdata *stack; + PyObject *mark; + PyObject *pers_func; + PyObject *last_string; + int *marks; + int num_marks; + int marks_size; + int (*read_func)(struct Unpicklerobject *, char **, int); + int (*readline_func)(struct Unpicklerobject *, char **); + int buf_size; + char *buf; + PyObject *find_class; +} Unpicklerobject; + +static PyTypeObject Unpicklertype; + +/* Forward decls that need the above structs */ +static int save(Picklerobject *, PyObject *, int); +static int put2(Picklerobject *, PyObject *); + +static +PyObject * +cPickle_ErrFormat(PyObject *ErrType, char *stringformat, char *format, ...) +{ + va_list va; + PyObject *args=0, *retval=0; + va_start(va, format); + + if (format) args = Py_VaBuildValue(format, va); + va_end(va); + if (format && ! args) return NULL; + if (stringformat && !(retval=PyString_FromString(stringformat))) + return NULL; + + if (retval) { + if (args) { + PyObject *v; + v=PyString_Format(retval, args); + Py_DECREF(retval); + Py_DECREF(args); + if (! v) return NULL; + retval=v; + } + } + else + if (args) retval=args; + else { + PyErr_SetObject(ErrType,Py_None); + return NULL; + } + PyErr_SetObject(ErrType,retval); + Py_DECREF(retval); + return NULL; +} + +static int +write_file(Picklerobject *self, char *s, int n) +{ + size_t nbyteswritten; + + if (s == NULL) { + return 0; + } + + Py_BEGIN_ALLOW_THREADS + nbyteswritten = fwrite(s, sizeof(char), n, self->fp); + Py_END_ALLOW_THREADS + if (nbyteswritten != (size_t)n) { + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + + return n; +} + +static int +write_cStringIO(Picklerobject *self, char *s, int n) +{ + if (s == NULL) { + return 0; + } + + if (PycStringIO->cwrite((PyObject *)self->file, s, n) != n) { + return -1; + } + + return n; +} + +static int +write_none(Picklerobject *self, char *s, int n) +{ + if (s == NULL) return 0; + return n; +} + +static int +write_other(Picklerobject *self, char *s, int n) +{ + PyObject *py_str = 0, *junk = 0; + + if (s == NULL) { + if (!( self->buf_size )) return 0; + py_str = PyString_FromStringAndSize(self->write_buf, + self->buf_size); + if (!py_str) + return -1; + } + else { + if (self->buf_size && (n + self->buf_size) > WRITE_BUF_SIZE) { + if (write_other(self, NULL, 0) < 0) + return -1; + } + + if (n > WRITE_BUF_SIZE) { + if (!( py_str = + PyString_FromStringAndSize(s, n))) + return -1; + } + else { + memcpy(self->write_buf + self->buf_size, s, n); + self->buf_size += n; + return n; + } + } + + if (self->write) { + /* object with write method */ + ARG_TUP(self, py_str); + if (self->arg) { + junk = PyObject_Call(self->write, self->arg, NULL); + FREE_ARG_TUP(self); + } + if (junk) Py_DECREF(junk); + else return -1; + } + else + PDATA_PUSH(self->file, py_str, -1); + + self->buf_size = 0; + return n; +} + + +static int +read_file(Unpicklerobject *self, char **s, int n) +{ + size_t nbytesread; + + if (self->buf_size == 0) { + int size; + + size = ((n < 32) ? 32 : n); + if (!( self->buf = (char *)malloc(size))) { + PyErr_NoMemory(); + return -1; + } + + self->buf_size = size; + } + else if (n > self->buf_size) { + self->buf = (char *)realloc(self->buf, n); + if (!self->buf) { + PyErr_NoMemory(); + return -1; + } + self->buf_size = n; + } + + Py_BEGIN_ALLOW_THREADS + nbytesread = fread(self->buf, sizeof(char), n, self->fp); + Py_END_ALLOW_THREADS + if (nbytesread != (size_t)n) { + if (feof(self->fp)) { + PyErr_SetNone(PyExc_EOFError); + return -1; + } + + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + + *s = self->buf; + + return n; +} + + +static int +readline_file(Unpicklerobject *self, char **s) +{ + int i; + + if (self->buf_size == 0) { + if (!( self->buf = (char *)malloc(40))) { + PyErr_NoMemory(); + return -1; + } + self->buf_size = 40; + } + + i = 0; + while (1) { + int bigger; + for (; i < (self->buf_size - 1); i++) { + if (feof(self->fp) || + (self->buf[i] = getc(self->fp)) == '\n') { + self->buf[i + 1] = '\0'; + *s = self->buf; + return i + 1; + } + } + bigger = self->buf_size << 1; + if (bigger <= 0) { /* overflow */ + PyErr_NoMemory(); + return -1; + } + self->buf = (char *)realloc(self->buf, bigger); + if (!self->buf) { + PyErr_NoMemory(); + return -1; + } + self->buf_size = bigger; + } +} + + +static int +read_cStringIO(Unpicklerobject *self, char **s, int n) +{ + char *ptr; + + if (PycStringIO->cread((PyObject *)self->file, &ptr, n) != n) { + PyErr_SetNone(PyExc_EOFError); + return -1; + } + + *s = ptr; + + return n; +} + + +static int +readline_cStringIO(Unpicklerobject *self, char **s) +{ + int n; + char *ptr; + + if ((n = PycStringIO->creadline((PyObject *)self->file, &ptr)) < 0) { + return -1; + } + + *s = ptr; + + return n; +} + + +static int +read_other(Unpicklerobject *self, char **s, int n) +{ + PyObject *bytes, *str=0; + + if (!( bytes = PyInt_FromLong(n))) return -1; + + ARG_TUP(self, bytes); + if (self->arg) { + str = PyObject_Call(self->read, self->arg, NULL); + FREE_ARG_TUP(self); + } + if (! str) return -1; + + Py_XDECREF(self->last_string); + self->last_string = str; + + if (! (*s = PyString_AsString(str))) return -1; + return n; +} + + +static int +readline_other(Unpicklerobject *self, char **s) +{ + PyObject *str; + int str_size; + + if (!( str = PyObject_CallObject(self->readline, empty_tuple))) { + return -1; + } + + if ((str_size = PyString_Size(str)) < 0) + return -1; + + Py_XDECREF(self->last_string); + self->last_string = str; + + if (! (*s = PyString_AsString(str))) + return -1; + + return str_size; +} + +/* Copy the first n bytes from s into newly malloc'ed memory, plus a + * trailing 0 byte. Return a pointer to that, or NULL if out of memory. + * The caller is responsible for free()'ing the return value. + */ +static char * +pystrndup(char *s, int n) +{ + char *r = (char *)malloc(n+1); + if (r == NULL) + return (char*)PyErr_NoMemory(); + memcpy(r, s, n); + r[n] = 0; + return r; +} + + +static int +get(Picklerobject *self, PyObject *id) +{ + PyObject *value, *mv; + long c_value; + char s[30]; + size_t len; + + if (!( mv = PyDict_GetItem(self->memo, id))) { + PyErr_SetObject(PyExc_KeyError, id); + return -1; + } + + if (!( value = PyTuple_GetItem(mv, 0))) + return -1; + + if (!( PyInt_Check(value))) { + PyErr_SetString(PicklingError, "no int where int expected in memo"); + return -1; + } + c_value = PyInt_AS_LONG((PyIntObject*)value); + + if (!self->bin) { + s[0] = GET; + PyOS_snprintf(s + 1, sizeof(s) - 1, "%ld\n", c_value); + len = strlen(s); + } + else if (Pdata_Check(self->file)) { + if (write_other(self, NULL, 0) < 0) return -1; + PDATA_APPEND(self->file, mv, -1); + return 0; + } + else { + if (c_value < 256) { + s[0] = BINGET; + s[1] = (int)(c_value & 0xff); + len = 2; + } + else { + s[0] = LONG_BINGET; + s[1] = (int)(c_value & 0xff); + s[2] = (int)((c_value >> 8) & 0xff); + s[3] = (int)((c_value >> 16) & 0xff); + s[4] = (int)((c_value >> 24) & 0xff); + len = 5; + } + } + + if (self->write_func(self, s, len) < 0) + return -1; + + return 0; +} + + +static int +put(Picklerobject *self, PyObject *ob) +{ + if (ob->ob_refcnt < 2 || self->fast) + return 0; + + return put2(self, ob); +} + + +static int +put2(Picklerobject *self, PyObject *ob) +{ + char c_str[30]; + int p; + size_t len; + int res = -1; + PyObject *py_ob_id = 0, *memo_len = 0, *t = 0; + + if (self->fast) + return 0; + + if ((p = PyDict_Size(self->memo)) < 0) + goto finally; + + /* Make sure memo keys are positive! */ + /* XXX Why? + * XXX And does "positive" really mean non-negative? + * XXX pickle.py starts with PUT index 0, not 1. This makes for + * XXX gratuitous differences between the pickling modules. + */ + p++; + + if (!( py_ob_id = PyLong_FromVoidPtr(ob))) + goto finally; + + if (!( memo_len = PyInt_FromLong(p))) + goto finally; + + if (!( t = PyTuple_New(2))) + goto finally; + + PyTuple_SET_ITEM(t, 0, memo_len); + Py_INCREF(memo_len); + PyTuple_SET_ITEM(t, 1, ob); + Py_INCREF(ob); + + if (PyDict_SetItem(self->memo, py_ob_id, t) < 0) + goto finally; + + if (!self->bin) { + c_str[0] = PUT; + PyOS_snprintf(c_str + 1, sizeof(c_str) - 1, "%d\n", p); + len = strlen(c_str); + } + else if (Pdata_Check(self->file)) { + if (write_other(self, NULL, 0) < 0) return -1; + PDATA_APPEND(self->file, memo_len, -1); + res=0; /* Job well done ;) */ + goto finally; + } + else { + if (p >= 256) { + c_str[0] = LONG_BINPUT; + c_str[1] = (int)(p & 0xff); + c_str[2] = (int)((p >> 8) & 0xff); + c_str[3] = (int)((p >> 16) & 0xff); + c_str[4] = (int)((p >> 24) & 0xff); + len = 5; + } + else { + c_str[0] = BINPUT; + c_str[1] = p; + len = 2; + } + } + + if (self->write_func(self, c_str, len) < 0) + goto finally; + + res = 0; + + finally: + Py_XDECREF(py_ob_id); + Py_XDECREF(memo_len); + Py_XDECREF(t); + + return res; +} + +static PyObject * +whichmodule(PyObject *global, PyObject *global_name) +{ + int i, j; + PyObject *module = 0, *modules_dict = 0, + *global_name_attr = 0, *name = 0; + + module = PyObject_GetAttrString(global, "__module__"); + if (module) + return module; + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + return NULL; + + if (!( modules_dict = PySys_GetObject("modules"))) + return NULL; + + i = 0; + while ((j = PyDict_Next(modules_dict, &i, &name, &module))) { + + if (PyObject_Compare(name, __main___str)==0) continue; + + global_name_attr = PyObject_GetAttr(module, global_name); + if (!global_name_attr) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + return NULL; + continue; + } + + if (global_name_attr != global) { + Py_DECREF(global_name_attr); + continue; + } + + Py_DECREF(global_name_attr); + + break; + } + + /* The following implements the rule in pickle.py added in 1.5 + that used __main__ if no module is found. I don't actually + like this rule. jlf + */ + if (!j) { + j=1; + name=__main___str; + } + + Py_INCREF(name); + return name; +} + + +static int +fast_save_enter(Picklerobject *self, PyObject *obj) +{ + /* if fast_container < 0, we're doing an error exit. */ + if (++self->fast_container >= PY_CPICKLE_FAST_LIMIT) { + PyObject *key = NULL; + if (self->fast_memo == NULL) { + self->fast_memo = PyDict_New(); + if (self->fast_memo == NULL) { + self->fast_container = -1; + return 0; + } + } + key = PyLong_FromVoidPtr(obj); + if (key == NULL) + return 0; + if (PyDict_GetItem(self->fast_memo, key)) { + Py_DECREF(key); + PyErr_Format(PyExc_ValueError, + "fast mode: can't pickle cyclic objects " + "including object type %s at %p", + obj->ob_type->tp_name, obj); + self->fast_container = -1; + return 0; + } + if (PyDict_SetItem(self->fast_memo, key, Py_None) < 0) { + Py_DECREF(key); + self->fast_container = -1; + return 0; + } + Py_DECREF(key); + } + return 1; +} + +int +fast_save_leave(Picklerobject *self, PyObject *obj) +{ + if (self->fast_container-- >= PY_CPICKLE_FAST_LIMIT) { + PyObject *key = PyLong_FromVoidPtr(obj); + if (key == NULL) + return 0; + if (PyDict_DelItem(self->fast_memo, key) < 0) { + Py_DECREF(key); + return 0; + } + Py_DECREF(key); + } + return 1; +} + +static int +save_none(Picklerobject *self, PyObject *args) +{ + static char none = NONE; + if (self->write_func(self, &none, 1) < 0) + return -1; + + return 0; +} + +static int +save_bool(Picklerobject *self, PyObject *args) +{ + static char *buf[2] = {FALSE, TRUE}; + static char len[2] = {sizeof(FALSE)-1, sizeof(TRUE)-1}; + long l = PyInt_AS_LONG((PyIntObject *)args); + + if (self->proto >= 2) { + char opcode = l ? NEWTRUE : NEWFALSE; + if (self->write_func(self, &opcode, 1) < 0) + return -1; + } + else if (self->write_func(self, buf[l], len[l]) < 0) + return -1; + return 0; +} + +static int +save_int(Picklerobject *self, PyObject *args) +{ + char c_str[32]; + long l = PyInt_AS_LONG((PyIntObject *)args); + int len = 0; + + if (!self->bin +#if SIZEOF_LONG > 4 + || l > 0x7fffffffL + || l < -0x80000000L +#endif + ) { + /* Text-mode pickle, or long too big to fit in the 4-byte + * signed BININT format: store as a string. + */ + c_str[0] = INT; + PyOS_snprintf(c_str + 1, sizeof(c_str) - 1, "%ld\n", l); + if (self->write_func(self, c_str, strlen(c_str)) < 0) + return -1; + } + else { + /* Binary pickle and l fits in a signed 4-byte int. */ + c_str[1] = (int)( l & 0xff); + c_str[2] = (int)((l >> 8) & 0xff); + c_str[3] = (int)((l >> 16) & 0xff); + c_str[4] = (int)((l >> 24) & 0xff); + + if ((c_str[4] == 0) && (c_str[3] == 0)) { + if (c_str[2] == 0) { + c_str[0] = BININT1; + len = 2; + } + else { + c_str[0] = BININT2; + len = 3; + } + } + else { + c_str[0] = BININT; + len = 5; + } + + if (self->write_func(self, c_str, len) < 0) + return -1; + } + + return 0; +} + + +static int +save_long(Picklerobject *self, PyObject *args) +{ + int size; + int res = -1; + PyObject *repr = NULL; + + static char l = LONG; + + if (self->proto >= 2) { + /* Linear-time pickling. */ + size_t nbits; + size_t nbytes; + unsigned char *pdata; + char c_str[5]; + int i; + int sign = _PyLong_Sign(args); + + if (sign == 0) { + /* It's 0 -- an empty bytestring. */ + c_str[0] = LONG1; + c_str[1] = 0; + i = self->write_func(self, c_str, 2); + if (i < 0) goto finally; + res = 0; + goto finally; + } + nbits = _PyLong_NumBits(args); + if (nbits == (size_t)-1 && PyErr_Occurred()) + goto finally; + /* How many bytes do we need? There are nbits >> 3 full + * bytes of data, and nbits & 7 leftover bits. If there + * are any leftover bits, then we clearly need another + * byte. Wnat's not so obvious is that we *probably* + * need another byte even if there aren't any leftovers: + * the most-significant bit of the most-significant byte + * acts like a sign bit, and it's usually got a sense + * opposite of the one we need. The exception is longs + * of the form -(2**(8*j-1)) for j > 0. Such a long is + * its own 256's-complement, so has the right sign bit + * even without the extra byte. That's a pain to check + * for in advance, though, so we always grab an extra + * byte at the start, and cut it back later if possible. + */ + nbytes = (nbits >> 3) + 1; + if ((int)nbytes < 0 || (size_t)(int)nbytes != nbytes) { + PyErr_SetString(PyExc_OverflowError, "long too large " + "to pickle"); + goto finally; + } + repr = PyString_FromStringAndSize(NULL, (int)nbytes); + if (repr == NULL) goto finally; + pdata = (unsigned char *)PyString_AS_STRING(repr); + i = _PyLong_AsByteArray((PyLongObject *)args, + pdata, nbytes, + 1 /* little endian */, 1 /* signed */); + if (i < 0) goto finally; + /* If the long is negative, this may be a byte more than + * needed. This is so iff the MSB is all redundant sign + * bits. + */ + if (sign < 0 && nbytes > 1 && pdata[nbytes - 1] == 0xff && + (pdata[nbytes - 2] & 0x80) != 0) + --nbytes; + + if (nbytes < 256) { + c_str[0] = LONG1; + c_str[1] = (char)nbytes; + size = 2; + } + else { + c_str[0] = LONG4; + size = (int)nbytes; + for (i = 1; i < 5; i++) { + c_str[i] = (char)(size & 0xff); + size >>= 8; + } + size = 5; + } + i = self->write_func(self, c_str, size); + if (i < 0) goto finally; + i = self->write_func(self, (char *)pdata, (int)nbytes); + if (i < 0) goto finally; + res = 0; + goto finally; + } + + /* proto < 2: write the repr and newline. This is quadratic-time + * (in the number of digits), in both directions. + */ + if (!( repr = PyObject_Repr(args))) + goto finally; + + if ((size = PyString_Size(repr)) < 0) + goto finally; + + if (self->write_func(self, &l, 1) < 0) + goto finally; + + if (self->write_func(self, + PyString_AS_STRING((PyStringObject *)repr), + size) < 0) + goto finally; + + if (self->write_func(self, "\n", 1) < 0) + goto finally; + + res = 0; + + finally: + Py_XDECREF(repr); + return res; +} + + +static int +save_float(Picklerobject *self, PyObject *args) +{ + double x = PyFloat_AS_DOUBLE((PyFloatObject *)args); + + if (self->bin) { + char str[9]; + str[0] = BINFLOAT; + if (_PyFloat_Pack8(x, (unsigned char *)&str[1], 0) < 0) + return -1; + if (self->write_func(self, str, 9) < 0) + return -1; + } + else { + char c_str[250]; + c_str[0] = FLOAT; + PyOS_snprintf(c_str + 1, sizeof(c_str) - 1, "%.17g\n", x); + + if (self->write_func(self, c_str, strlen(c_str)) < 0) + return -1; + } + + return 0; +} + + +static int +save_string(Picklerobject *self, PyObject *args, int doput) +{ + int size, len; + PyObject *repr=0; + + if ((size = PyString_Size(args)) < 0) + return -1; + + if (!self->bin) { + char *repr_str; + + static char string = STRING; + + if (!( repr = PyObject_Repr(args))) + return -1; + + if ((len = PyString_Size(repr)) < 0) + goto err; + repr_str = PyString_AS_STRING((PyStringObject *)repr); + + if (self->write_func(self, &string, 1) < 0) + goto err; + + if (self->write_func(self, repr_str, len) < 0) + goto err; + + if (self->write_func(self, "\n", 1) < 0) + goto err; + + Py_XDECREF(repr); + } + else { + int i; + char c_str[5]; + + if ((size = PyString_Size(args)) < 0) + return -1; + + if (size < 256) { + c_str[0] = SHORT_BINSTRING; + c_str[1] = size; + len = 2; + } + else { + c_str[0] = BINSTRING; + for (i = 1; i < 5; i++) + c_str[i] = (int)(size >> ((i - 1) * 8)); + len = 5; + } + + if (self->write_func(self, c_str, len) < 0) + return -1; + + if (size > 128 && Pdata_Check(self->file)) { + if (write_other(self, NULL, 0) < 0) return -1; + PDATA_APPEND(self->file, args, -1); + } + else { + if (self->write_func(self, + PyString_AS_STRING( + (PyStringObject *)args), + size) < 0) + return -1; + } + } + + if (doput) + if (put(self, args) < 0) + return -1; + + return 0; + + err: + Py_XDECREF(repr); + return -1; +} + + +#ifdef Py_USING_UNICODE +/* A copy of PyUnicode_EncodeRawUnicodeEscape() that also translates + backslash and newline characters to \uXXXX escapes. */ +static PyObject * +modified_EncodeRawUnicodeEscape(const Py_UNICODE *s, int size) +{ + PyObject *repr; + char *p; + char *q; + + static const char *hexdigit = "0123456789ABCDEF"; + + repr = PyString_FromStringAndSize(NULL, 6 * size); + if (repr == NULL) + return NULL; + if (size == 0) + return repr; + + p = q = PyString_AS_STRING(repr); + while (size-- > 0) { + Py_UNICODE ch = *s++; + /* Map 16-bit characters to '\uxxxx' */ + if (ch >= 256 || ch == '\\' || ch == '\n') { + *p++ = '\\'; + *p++ = 'u'; + *p++ = hexdigit[(ch >> 12) & 0xf]; + *p++ = hexdigit[(ch >> 8) & 0xf]; + *p++ = hexdigit[(ch >> 4) & 0xf]; + *p++ = hexdigit[ch & 15]; + } + /* Copy everything else as-is */ + else + *p++ = (char) ch; + } + *p = '\0'; + _PyString_Resize(&repr, p - q); + return repr; +} + + +static int +save_unicode(Picklerobject *self, PyObject *args, int doput) +{ + int size, len; + PyObject *repr=0; + + if (!PyUnicode_Check(args)) + return -1; + + if (!self->bin) { + char *repr_str; + static char string = UNICODE; + + repr = modified_EncodeRawUnicodeEscape( + PyUnicode_AS_UNICODE(args), PyUnicode_GET_SIZE(args)); + if (!repr) + return -1; + + if ((len = PyString_Size(repr)) < 0) + goto err; + repr_str = PyString_AS_STRING((PyStringObject *)repr); + + if (self->write_func(self, &string, 1) < 0) + goto err; + + if (self->write_func(self, repr_str, len) < 0) + goto err; + + if (self->write_func(self, "\n", 1) < 0) + goto err; + + Py_XDECREF(repr); + } + else { + int i; + char c_str[5]; + + if (!( repr = PyUnicode_AsUTF8String(args))) + return -1; + + if ((size = PyString_Size(repr)) < 0) + goto err; + + c_str[0] = BINUNICODE; + for (i = 1; i < 5; i++) + c_str[i] = (int)(size >> ((i - 1) * 8)); + len = 5; + + if (self->write_func(self, c_str, len) < 0) + goto err; + + if (size > 128 && Pdata_Check(self->file)) { + if (write_other(self, NULL, 0) < 0) + goto err; + PDATA_APPEND(self->file, repr, -1); + } + else { + if (self->write_func(self, PyString_AS_STRING(repr), + size) < 0) + goto err; + } + + Py_DECREF(repr); + } + + if (doput) + if (put(self, args) < 0) + return -1; + + return 0; + + err: + Py_XDECREF(repr); + return -1; +} +#endif + +/* A helper for save_tuple. Push the len elements in tuple t on the stack. */ +static int +store_tuple_elements(Picklerobject *self, PyObject *t, int len) +{ + int i; + int res = -1; /* guilty until proved innocent */ + + assert(PyTuple_Size(t) == len); + + for (i = 0; i < len; i++) { + PyObject *element = PyTuple_GET_ITEM(t, i); + + if (element == NULL) + goto finally; + if (save(self, element, 0) < 0) + goto finally; + } + res = 0; + + finally: + return res; +} + +/* Tuples are ubiquitous in the pickle protocols, so many techniques are + * used across protocols to minimize the space needed to pickle them. + * Tuples are also the only builtin immutable type that can be recursive + * (a tuple can be reached from itself), and that requires some subtle + * magic so that it works in all cases. IOW, this is a long routine. + */ +static int +save_tuple(Picklerobject *self, PyObject *args) +{ + PyObject *py_tuple_id = NULL; + int len, i; + int res = -1; + + static char tuple = TUPLE; + static char pop = POP; + static char pop_mark = POP_MARK; + static char len2opcode[] = {EMPTY_TUPLE, TUPLE1, TUPLE2, TUPLE3}; + + if ((len = PyTuple_Size(args)) < 0) + goto finally; + + if (len == 0) { + char c_str[2]; + + if (self->proto) { + c_str[0] = EMPTY_TUPLE; + len = 1; + } + else { + c_str[0] = MARK; + c_str[1] = TUPLE; + len = 2; + } + if (self->write_func(self, c_str, len) >= 0) + res = 0; + /* Don't memoize an empty tuple. */ + goto finally; + } + + /* A non-empty tuple. */ + + /* id(tuple) isn't in the memo now. If it shows up there after + * saving the tuple elements, the tuple must be recursive, in + * which case we'll pop everything we put on the stack, and fetch + * its value from the memo. + */ + py_tuple_id = PyLong_FromVoidPtr(args); + if (py_tuple_id == NULL) + goto finally; + + if (len <= 3 && self->proto >= 2) { + /* Use TUPLE{1,2,3} opcodes. */ + if (store_tuple_elements(self, args, len) < 0) + goto finally; + if (PyDict_GetItem(self->memo, py_tuple_id)) { + /* pop the len elements */ + for (i = 0; i < len; ++i) + if (self->write_func(self, &pop, 1) < 0) + goto finally; + /* fetch from memo */ + if (get(self, py_tuple_id) < 0) + goto finally; + res = 0; + goto finally; + } + /* Not recursive. */ + if (self->write_func(self, len2opcode + len, 1) < 0) + goto finally; + goto memoize; + } + + /* proto < 2 and len > 0, or proto >= 2 and len > 3. + * Generate MARK elt1 elt2 ... TUPLE + */ + if (self->write_func(self, &MARKv, 1) < 0) + goto finally; + + if (store_tuple_elements(self, args, len) < 0) + goto finally; + + if (PyDict_GetItem(self->memo, py_tuple_id)) { + /* pop the stack stuff we pushed */ + if (self->bin) { + if (self->write_func(self, &pop_mark, 1) < 0) + goto finally; + } + else { + /* Note that we pop one more than len, to remove + * the MARK too. + */ + for (i = 0; i <= len; i++) + if (self->write_func(self, &pop, 1) < 0) + goto finally; + } + /* fetch from memo */ + if (get(self, py_tuple_id) >= 0) + res = 0; + goto finally; + } + + /* Not recursive. */ + if (self->write_func(self, &tuple, 1) < 0) + goto finally; + + memoize: + if (put(self, args) >= 0) + res = 0; + + finally: + Py_XDECREF(py_tuple_id); + return res; +} + +/* iter is an iterator giving items, and we batch up chunks of + * MARK item item ... item APPENDS + * opcode sequences. Calling code should have arranged to first create an + * empty list, or list-like object, for the APPENDS to operate on. + * Returns 0 on success, <0 on error. + */ +static int +batch_list(Picklerobject *self, PyObject *iter) +{ + PyObject *obj; + PyObject *slice[BATCHSIZE]; + int i, n; + + static char append = APPEND; + static char appends = APPENDS; + + assert(iter != NULL); + + if (self->proto == 0) { + /* APPENDS isn't available; do one at a time. */ + for (;;) { + obj = PyIter_Next(iter); + if (obj == NULL) { + if (PyErr_Occurred()) + return -1; + break; + } + i = save(self, obj, 0); + Py_DECREF(obj); + if (i < 0) + return -1; + if (self->write_func(self, &append, 1) < 0) + return -1; + } + return 0; + } + + /* proto > 0: write in batches of BATCHSIZE. */ + do { + /* Get next group of (no more than) BATCHSIZE elements. */ + for (n = 0; n < BATCHSIZE; ++n) { + obj = PyIter_Next(iter); + if (obj == NULL) { + if (PyErr_Occurred()) + goto BatchFailed; + break; + } + slice[n] = obj; + } + + if (n > 1) { + /* Pump out MARK, slice[0:n], APPENDS. */ + if (self->write_func(self, &MARKv, 1) < 0) + goto BatchFailed; + for (i = 0; i < n; ++i) { + if (save(self, slice[i], 0) < 0) + goto BatchFailed; + } + if (self->write_func(self, &appends, 1) < 0) + goto BatchFailed; + } + else if (n == 1) { + if (save(self, slice[0], 0) < 0) + goto BatchFailed; + if (self->write_func(self, &append, 1) < 0) + goto BatchFailed; + } + + for (i = 0; i < n; ++i) { + Py_DECREF(slice[i]); + } + } while (n == BATCHSIZE); + return 0; + +BatchFailed: + while (--n >= 0) { + Py_DECREF(slice[n]); + } + return -1; +} + +static int +save_list(Picklerobject *self, PyObject *args) +{ + int res = -1; + char s[3]; + int len; + PyObject *iter; + + if (self->fast && !fast_save_enter(self, args)) + goto finally; + + /* Create an empty list. */ + if (self->bin) { + s[0] = EMPTY_LIST; + len = 1; + } + else { + s[0] = MARK; + s[1] = LIST; + len = 2; + } + + if (self->write_func(self, s, len) < 0) + goto finally; + + /* Get list length, and bow out early if empty. */ + if ((len = PyList_Size(args)) < 0) + goto finally; + + /* Memoize. */ + if (len == 0) { + if (put(self, args) >= 0) + res = 0; + goto finally; + } + if (put2(self, args) < 0) + goto finally; + + /* Materialize the list elements. */ + iter = PyObject_GetIter(args); + if (iter == NULL) + goto finally; + res = batch_list(self, iter); + Py_DECREF(iter); + + finally: + if (self->fast && !fast_save_leave(self, args)) + res = -1; + + return res; +} + + +/* iter is an iterator giving (key, value) pairs, and we batch up chunks of + * MARK key value ... key value SETITEMS + * opcode sequences. Calling code should have arranged to first create an + * empty dict, or dict-like object, for the SETITEMS to operate on. + * Returns 0 on success, <0 on error. + * + * This is very much like batch_list(). The difference between saving + * elements directly, and picking apart two-tuples, is so long-winded at + * the C level, though, that attempts to combine these routines were too + * ugly to bear. + */ +static int +batch_dict(Picklerobject *self, PyObject *iter) +{ + PyObject *p; + PyObject *slice[BATCHSIZE]; + int i, n; + + static char setitem = SETITEM; + static char setitems = SETITEMS; + + assert(iter != NULL); + + if (self->proto == 0) { + /* SETITEMS isn't available; do one at a time. */ + for (;;) { + p = PyIter_Next(iter); + if (p == NULL) { + if (PyErr_Occurred()) + return -1; + break; + } + if (!PyTuple_Check(p) || PyTuple_Size(p) != 2) { + PyErr_SetString(PyExc_TypeError, "dict items " + "iterator must return 2-tuples"); + return -1; + } + i = save(self, PyTuple_GET_ITEM(p, 0), 0); + if (i >= 0) + i = save(self, PyTuple_GET_ITEM(p, 1), 0); + Py_DECREF(p); + if (i < 0) + return -1; + if (self->write_func(self, &setitem, 1) < 0) + return -1; + } + return 0; + } + + /* proto > 0: write in batches of BATCHSIZE. */ + do { + /* Get next group of (no more than) BATCHSIZE elements. */ + for (n = 0; n < BATCHSIZE; ++n) { + p = PyIter_Next(iter); + if (p == NULL) { + if (PyErr_Occurred()) + goto BatchFailed; + break; + } + if (!PyTuple_Check(p) || PyTuple_Size(p) != 2) { + PyErr_SetString(PyExc_TypeError, "dict items " + "iterator must return 2-tuples"); + goto BatchFailed; + } + slice[n] = p; + } + + if (n > 1) { + /* Pump out MARK, slice[0:n], SETITEMS. */ + if (self->write_func(self, &MARKv, 1) < 0) + goto BatchFailed; + for (i = 0; i < n; ++i) { + p = slice[i]; + if (save(self, PyTuple_GET_ITEM(p, 0), 0) < 0) + goto BatchFailed; + if (save(self, PyTuple_GET_ITEM(p, 1), 0) < 0) + goto BatchFailed; + } + if (self->write_func(self, &setitems, 1) < 0) + goto BatchFailed; + } + else if (n == 1) { + p = slice[0]; + if (save(self, PyTuple_GET_ITEM(p, 0), 0) < 0) + goto BatchFailed; + if (save(self, PyTuple_GET_ITEM(p, 1), 0) < 0) + goto BatchFailed; + if (self->write_func(self, &setitem, 1) < 0) + goto BatchFailed; + } + + for (i = 0; i < n; ++i) { + Py_DECREF(slice[i]); + } + } while (n == BATCHSIZE); + return 0; + +BatchFailed: + while (--n >= 0) { + Py_DECREF(slice[n]); + } + return -1; +} + +static int +save_dict(Picklerobject *self, PyObject *args) +{ + int res = -1; + char s[3]; + int len; + PyObject *iter; + + if (self->fast && !fast_save_enter(self, args)) + goto finally; + + /* Create an empty dict. */ + if (self->bin) { + s[0] = EMPTY_DICT; + len = 1; + } + else { + s[0] = MARK; + s[1] = DICT; + len = 2; + } + + if (self->write_func(self, s, len) < 0) + goto finally; + + /* Get dict size, and bow out early if empty. */ + if ((len = PyDict_Size(args)) < 0) + goto finally; + + if (len == 0) { + if (put(self, args) >= 0) + res = 0; + goto finally; + } + if (put2(self, args) < 0) + goto finally; + + /* Materialize the dict items. */ + iter = PyObject_CallMethod(args, "iteritems", "()"); + if (iter == NULL) + goto finally; + res = batch_dict(self, iter); + Py_DECREF(iter); + + finally: + if (self->fast && !fast_save_leave(self, args)) + res = -1; + + return res; +} + + +static int +save_inst(Picklerobject *self, PyObject *args) +{ + PyObject *class = 0, *module = 0, *name = 0, *state = 0, + *getinitargs_func = 0, *getstate_func = 0, *class_args = 0; + char *module_str, *name_str; + int module_size, name_size, res = -1; + + static char inst = INST, obj = OBJ, build = BUILD; + + if (self->fast && !fast_save_enter(self, args)) + goto finally; + + if (self->write_func(self, &MARKv, 1) < 0) + goto finally; + + if (!( class = PyObject_GetAttr(args, __class___str))) + goto finally; + + if (self->bin) { + if (save(self, class, 0) < 0) + goto finally; + } + + if ((getinitargs_func = PyObject_GetAttr(args, __getinitargs___str))) { + PyObject *element = 0; + int i, len; + + if (!( class_args = + PyObject_Call(getinitargs_func, empty_tuple, NULL))) + goto finally; + + if ((len = PyObject_Size(class_args)) < 0) + goto finally; + + for (i = 0; i < len; i++) { + if (!( element = PySequence_GetItem(class_args, i))) + goto finally; + + if (save(self, element, 0) < 0) { + Py_DECREF(element); + goto finally; + } + + Py_DECREF(element); + } + } + else { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + goto finally; + } + + if (!self->bin) { + if (!( name = ((PyClassObject *)class)->cl_name )) { + PyErr_SetString(PicklingError, "class has no name"); + goto finally; + } + + if (!( module = whichmodule(class, name))) + goto finally; + + + if ((module_size = PyString_Size(module)) < 0 || + (name_size = PyString_Size(name)) < 0) + goto finally; + + module_str = PyString_AS_STRING((PyStringObject *)module); + name_str = PyString_AS_STRING((PyStringObject *)name); + + if (self->write_func(self, &inst, 1) < 0) + goto finally; + + if (self->write_func(self, module_str, module_size) < 0) + goto finally; + + if (self->write_func(self, "\n", 1) < 0) + goto finally; + + if (self->write_func(self, name_str, name_size) < 0) + goto finally; + + if (self->write_func(self, "\n", 1) < 0) + goto finally; + } + else if (self->write_func(self, &obj, 1) < 0) { + goto finally; + } + + if ((getstate_func = PyObject_GetAttr(args, __getstate___str))) { + state = PyObject_Call(getstate_func, empty_tuple, NULL); + if (!state) + goto finally; + } + else { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + goto finally; + + if (!( state = PyObject_GetAttr(args, __dict___str))) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + goto finally; + res = 0; + goto finally; + } + } + + if (!PyDict_Check(state)) { + if (put2(self, args) < 0) + goto finally; + } + else { + if (put(self, args) < 0) + goto finally; + } + + if (save(self, state, 0) < 0) + goto finally; + + if (self->write_func(self, &build, 1) < 0) + goto finally; + + res = 0; + + finally: + if (self->fast && !fast_save_leave(self, args)) + res = -1; + + Py_XDECREF(module); + Py_XDECREF(class); + Py_XDECREF(state); + Py_XDECREF(getinitargs_func); + Py_XDECREF(getstate_func); + Py_XDECREF(class_args); + + return res; +} + + +static int +save_global(Picklerobject *self, PyObject *args, PyObject *name) +{ + PyObject *global_name = 0, *module = 0, *mod = 0, *klass = 0; + char *name_str, *module_str; + int module_size, name_size, res = -1; + + static char global = GLOBAL; + + if (name) { + global_name = name; + Py_INCREF(global_name); + } + else { + if (!( global_name = PyObject_GetAttr(args, __name___str))) + goto finally; + } + + if (!( module = whichmodule(args, global_name))) + goto finally; + + if ((module_size = PyString_Size(module)) < 0 || + (name_size = PyString_Size(global_name)) < 0) + goto finally; + + module_str = PyString_AS_STRING((PyStringObject *)module); + name_str = PyString_AS_STRING((PyStringObject *)global_name); + + /* XXX This can be doing a relative import. Clearly it shouldn't, + but I don't know how to stop it. :-( */ + mod = PyImport_ImportModule(module_str); + if (mod == NULL) { + cPickle_ErrFormat(PicklingError, + "Can't pickle %s: import of module %s " + "failed", + "OS", args, module); + goto finally; + } + klass = PyObject_GetAttrString(mod, name_str); + if (klass == NULL) { + cPickle_ErrFormat(PicklingError, + "Can't pickle %s: attribute lookup %s.%s " + "failed", + "OSS", args, module, global_name); + goto finally; + } + if (klass != args) { + Py_DECREF(klass); + cPickle_ErrFormat(PicklingError, + "Can't pickle %s: it's not the same object " + "as %s.%s", + "OSS", args, module, global_name); + goto finally; + } + Py_DECREF(klass); + + if (self->proto >= 2) { + /* See whether this is in the extension registry, and if + * so generate an EXT opcode. + */ + PyObject *py_code; /* extension code as Python object */ + long code; /* extension code as C value */ + char c_str[5]; + int n; + + PyTuple_SET_ITEM(two_tuple, 0, module); + PyTuple_SET_ITEM(two_tuple, 1, global_name); + py_code = PyDict_GetItem(extension_registry, two_tuple); + if (py_code == NULL) + goto gen_global; /* not registered */ + + /* Verify py_code has the right type and value. */ + if (!PyInt_Check(py_code)) { + cPickle_ErrFormat(PicklingError, "Can't pickle %s: " + "extension code %s isn't an integer", + "OO", args, py_code); + goto finally; + } + code = PyInt_AS_LONG(py_code); + if (code <= 0 || code > 0x7fffffffL) { + cPickle_ErrFormat(PicklingError, "Can't pickle %s: " + "extension code %ld is out of range", + "Ol", args, code); + goto finally; + } + + /* Generate an EXT opcode. */ + if (code <= 0xff) { + c_str[0] = EXT1; + c_str[1] = (char)code; + n = 2; + } + else if (code <= 0xffff) { + c_str[0] = EXT2; + c_str[1] = (char)(code & 0xff); + c_str[2] = (char)((code >> 8) & 0xff); + n = 3; + } + else { + c_str[0] = EXT4; + c_str[1] = (char)(code & 0xff); + c_str[2] = (char)((code >> 8) & 0xff); + c_str[3] = (char)((code >> 16) & 0xff); + c_str[4] = (char)((code >> 24) & 0xff); + n = 5; + } + + if (self->write_func(self, c_str, n) >= 0) + res = 0; + goto finally; /* and don't memoize */ + } + + gen_global: + if (self->write_func(self, &global, 1) < 0) + goto finally; + + if (self->write_func(self, module_str, module_size) < 0) + goto finally; + + if (self->write_func(self, "\n", 1) < 0) + goto finally; + + if (self->write_func(self, name_str, name_size) < 0) + goto finally; + + if (self->write_func(self, "\n", 1) < 0) + goto finally; + + if (put(self, args) < 0) + goto finally; + + res = 0; + + finally: + Py_XDECREF(module); + Py_XDECREF(global_name); + Py_XDECREF(mod); + + return res; +} + +static int +save_pers(Picklerobject *self, PyObject *args, PyObject *f) +{ + PyObject *pid = 0; + int size, res = -1; + + static char persid = PERSID, binpersid = BINPERSID; + + Py_INCREF(args); + ARG_TUP(self, args); + if (self->arg) { + pid = PyObject_Call(f, self->arg, NULL); + FREE_ARG_TUP(self); + } + if (! pid) return -1; + + if (pid != Py_None) { + if (!self->bin) { + if (!PyString_Check(pid)) { + PyErr_SetString(PicklingError, + "persistent id must be string"); + goto finally; + } + + if (self->write_func(self, &persid, 1) < 0) + goto finally; + + if ((size = PyString_Size(pid)) < 0) + goto finally; + + if (self->write_func(self, + PyString_AS_STRING( + (PyStringObject *)pid), + size) < 0) + goto finally; + + if (self->write_func(self, "\n", 1) < 0) + goto finally; + + res = 1; + goto finally; + } + else if (save(self, pid, 1) >= 0) { + if (self->write_func(self, &binpersid, 1) < 0) + res = -1; + else + res = 1; + } + + goto finally; + } + + res = 0; + + finally: + Py_XDECREF(pid); + + return res; +} + +/* We're saving ob, and args is the 2-thru-5 tuple returned by the + * appropriate __reduce__ method for ob. + */ +static int +save_reduce(Picklerobject *self, PyObject *args, PyObject *ob) +{ + PyObject *callable; + PyObject *argtup; + PyObject *state = NULL; + PyObject *listitems = NULL; + PyObject *dictitems = NULL; + + int use_newobj = self->proto >= 2; + + static char reduce = REDUCE; + static char build = BUILD; + static char newobj = NEWOBJ; + + if (! PyArg_UnpackTuple(args, "save_reduce", 2, 5, + &callable, + &argtup, + &state, + &listitems, + &dictitems)) + return -1; + + if (state == Py_None) + state = NULL; + if (listitems == Py_None) + listitems = NULL; + if (dictitems == Py_None) + dictitems = NULL; + + /* Protocol 2 special case: if callable's name is __newobj__, use + * NEWOBJ. This consumes a lot of code. + */ + if (use_newobj) { + PyObject *temp = PyObject_GetAttr(callable, __name___str); + + if (temp == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + return -1; + use_newobj = 0; + } + else { + use_newobj = PyString_Check(temp) && + strcmp(PyString_AS_STRING(temp), + "__newobj__") == 0; + Py_DECREF(temp); + } + } + if (use_newobj) { + PyObject *cls; + PyObject *newargtup; + int n, i; + + /* Sanity checks. */ + n = PyTuple_Size(argtup); + if (n < 1) { + PyErr_SetString(PicklingError, "__newobj__ arglist " + "is empty"); + return -1; + } + + cls = PyTuple_GET_ITEM(argtup, 0); + if (! PyObject_HasAttrString(cls, "__new__")) { + PyErr_SetString(PicklingError, "args[0] from " + "__newobj__ args has no __new__"); + return -1; + } + + /* XXX How could ob be NULL? */ + if (ob != NULL) { + PyObject *ob_dot_class; + + ob_dot_class = PyObject_GetAttr(ob, __class___str); + if (ob_dot_class == NULL) { + if (PyErr_ExceptionMatches( + PyExc_AttributeError)) + PyErr_Clear(); + else + return -1; + } + i = ob_dot_class != cls; /* true iff a problem */ + Py_XDECREF(ob_dot_class); + if (i) { + PyErr_SetString(PicklingError, "args[0] from " + "__newobj__ args has the wrong class"); + return -1; + } + } + + /* Save the class and its __new__ arguments. */ + if (save(self, cls, 0) < 0) + return -1; + + newargtup = PyTuple_New(n-1); /* argtup[1:] */ + if (newargtup == NULL) + return -1; + for (i = 1; i < n; ++i) { + PyObject *temp = PyTuple_GET_ITEM(argtup, i); + Py_INCREF(temp); + PyTuple_SET_ITEM(newargtup, i-1, temp); + } + i = save(self, newargtup, 0) < 0; + Py_DECREF(newargtup); + if (i < 0) + return -1; + + /* Add NEWOBJ opcode. */ + if (self->write_func(self, &newobj, 1) < 0) + return -1; + } + else { + /* Not using NEWOBJ. */ + if (save(self, callable, 0) < 0 || + save(self, argtup, 0) < 0 || + self->write_func(self, &reduce, 1) < 0) + return -1; + } + + /* Memoize. */ + /* XXX How can ob be NULL? */ + if (ob != NULL) { + if (state && !PyDict_Check(state)) { + if (put2(self, ob) < 0) + return -1; + } + else if (put(self, ob) < 0) + return -1; + } + + + if (listitems && batch_list(self, listitems) < 0) + return -1; + + if (dictitems && batch_dict(self, dictitems) < 0) + return -1; + + if (state) { + if (save(self, state, 0) < 0 || + self->write_func(self, &build, 1) < 0) + return -1; + } + + return 0; +} + +static int +save(Picklerobject *self, PyObject *args, int pers_save) +{ + PyTypeObject *type; + PyObject *py_ob_id = 0, *__reduce__ = 0, *t = 0; + PyObject *arg_tup; + int res = -1; + int tmp, size; + + if (self->nesting++ > Py_GetRecursionLimit()){ + PyErr_SetString(PyExc_RuntimeError, + "maximum recursion depth exceeded"); + goto finally; + } + + if (!pers_save && self->pers_func) { + if ((tmp = save_pers(self, args, self->pers_func)) != 0) { + res = tmp; + goto finally; + } + } + + if (args == Py_None) { + res = save_none(self, args); + goto finally; + } + + type = args->ob_type; + + switch (type->tp_name[0]) { + case 'b': + if (args == Py_False || args == Py_True) { + res = save_bool(self, args); + goto finally; + } + break; + case 'i': + if (type == &PyInt_Type) { + res = save_int(self, args); + goto finally; + } + break; + + case 'l': + if (type == &PyLong_Type) { + res = save_long(self, args); + goto finally; + } + break; + + case 'f': + if (type == &PyFloat_Type) { + res = save_float(self, args); + goto finally; + } + break; + + case 't': + if (type == &PyTuple_Type && PyTuple_Size(args) == 0) { + res = save_tuple(self, args); + goto finally; + } + break; + + case 's': + if ((type == &PyString_Type) && (PyString_GET_SIZE(args) < 2)) { + res = save_string(self, args, 0); + goto finally; + } + +#ifdef Py_USING_UNICODE + case 'u': + if ((type == &PyUnicode_Type) && (PyString_GET_SIZE(args) < 2)) { + res = save_unicode(self, args, 0); + goto finally; + } +#endif + } + + if (args->ob_refcnt > 1) { + if (!( py_ob_id = PyLong_FromVoidPtr(args))) + goto finally; + + if (PyDict_GetItem(self->memo, py_ob_id)) { + if (get(self, py_ob_id) < 0) + goto finally; + + res = 0; + goto finally; + } + } + + switch (type->tp_name[0]) { + case 's': + if (type == &PyString_Type) { + res = save_string(self, args, 1); + goto finally; + } + break; + +#ifdef Py_USING_UNICODE + case 'u': + if (type == &PyUnicode_Type) { + res = save_unicode(self, args, 1); + goto finally; + } + break; +#endif + + case 't': + if (type == &PyTuple_Type) { + res = save_tuple(self, args); + goto finally; + } + if (type == &PyType_Type) { + res = save_global(self, args, NULL); + goto finally; + } + break; + + case 'l': + if (type == &PyList_Type) { + res = save_list(self, args); + goto finally; + } + break; + + case 'd': + if (type == &PyDict_Type) { + res = save_dict(self, args); + goto finally; + } + break; + + case 'i': + if (type == &PyInstance_Type) { + res = save_inst(self, args); + goto finally; + } + break; + + case 'c': + if (type == &PyClass_Type) { + res = save_global(self, args, NULL); + goto finally; + } + break; + + case 'f': + if (type == &PyFunction_Type) { + res = save_global(self, args, NULL); + goto finally; + } + break; + + case 'b': + if (type == &PyCFunction_Type) { + res = save_global(self, args, NULL); + goto finally; + } + } + + if (!pers_save && self->inst_pers_func) { + if ((tmp = save_pers(self, args, self->inst_pers_func)) != 0) { + res = tmp; + goto finally; + } + } + + if (PyType_IsSubtype(type, &PyType_Type)) { + res = save_global(self, args, NULL); + goto finally; + } + + /* Get a reduction callable, and call it. This may come from + * copy_reg.dispatch_table, the object's __reduce_ex__ method, + * or the object's __reduce__ method. + */ + __reduce__ = PyDict_GetItem(dispatch_table, (PyObject *)type); + if (__reduce__ != NULL) { + Py_INCREF(__reduce__); + Py_INCREF(args); + ARG_TUP(self, args); + if (self->arg) { + t = PyObject_Call(__reduce__, self->arg, NULL); + FREE_ARG_TUP(self); + } + } + else { + /* Check for a __reduce_ex__ method. */ + __reduce__ = PyObject_GetAttr(args, __reduce_ex___str); + if (__reduce__ != NULL) { + t = PyInt_FromLong(self->proto); + if (t != NULL) { + ARG_TUP(self, t); + t = NULL; + if (self->arg) { + t = PyObject_Call(__reduce__, + self->arg, NULL); + FREE_ARG_TUP(self); + } + } + } + else { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + else + goto finally; + /* Check for a __reduce__ method. */ + __reduce__ = PyObject_GetAttr(args, __reduce___str); + if (__reduce__ != NULL) { + t = PyObject_Call(__reduce__, + empty_tuple, NULL); + } + else { + PyErr_SetObject(UnpickleableError, args); + goto finally; + } + } + } + + if (t == NULL) + goto finally; + + if (PyString_Check(t)) { + res = save_global(self, args, t); + goto finally; + } + + if (! PyTuple_Check(t)) { + cPickle_ErrFormat(PicklingError, "Value returned by " + "%s must be string or tuple", + "O", __reduce__); + goto finally; + } + + size = PyTuple_Size(t); + if (size < 2 || size > 5) { + cPickle_ErrFormat(PicklingError, "tuple returned by " + "%s must contain 2 through 5 elements", + "O", __reduce__); + goto finally; + } + + arg_tup = PyTuple_GET_ITEM(t, 1); + if (!(PyTuple_Check(arg_tup) || arg_tup == Py_None)) { + cPickle_ErrFormat(PicklingError, "Second element of " + "tuple returned by %s must be a tuple", + "O", __reduce__); + goto finally; + } + + res = save_reduce(self, t, args); + + finally: + self->nesting--; + Py_XDECREF(py_ob_id); + Py_XDECREF(__reduce__); + Py_XDECREF(t); + + return res; +} + + +static int +dump(Picklerobject *self, PyObject *args) +{ + static char stop = STOP; + + if (self->proto >= 2) { + char bytes[2]; + + bytes[0] = PROTO; + assert(self->proto >= 0 && self->proto < 256); + bytes[1] = (char)self->proto; + if (self->write_func(self, bytes, 2) < 0) + return -1; + } + + if (save(self, args, 0) < 0) + return -1; + + if (self->write_func(self, &stop, 1) < 0) + return -1; + + if (self->write_func(self, NULL, 0) < 0) + return -1; + + return 0; +} + +static PyObject * +Pickle_clear_memo(Picklerobject *self, PyObject *args) +{ + if (self->memo) + PyDict_Clear(self->memo); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +Pickle_getvalue(Picklerobject *self, PyObject *args) +{ + int l, i, rsize, ssize, clear=1, lm; + long ik; + PyObject *k, *r; + char *s, *p, *have_get; + Pdata *data; + + /* Can be called by Python code or C code */ + if (args && !PyArg_ParseTuple(args, "|i:getvalue", &clear)) + return NULL; + + /* Check to make sure we are based on a list */ + if (! Pdata_Check(self->file)) { + PyErr_SetString(PicklingError, + "Attempt to getvalue() a non-list-based pickler"); + return NULL; + } + + /* flush write buffer */ + if (write_other(self, NULL, 0) < 0) return NULL; + + data=(Pdata*)self->file; + l=data->length; + + /* set up an array to hold get/put status */ + lm = PyDict_Size(self->memo); + if (lm < 0) return NULL; + lm++; + have_get = malloc(lm); + if (have_get == NULL) return PyErr_NoMemory(); + memset(have_get, 0, lm); + + /* Scan for gets. */ + for (rsize = 0, i = l; --i >= 0; ) { + k = data->data[i]; + + if (PyString_Check(k)) + rsize += PyString_GET_SIZE(k); + + else if (PyInt_Check(k)) { /* put */ + ik = PyInt_AS_LONG((PyIntObject*)k); + if (ik >= lm || ik == 0) { + PyErr_SetString(PicklingError, + "Invalid get data"); + return NULL; + } + if (have_get[ik]) /* with matching get */ + rsize += ik < 256 ? 2 : 5; + } + + else if (! (PyTuple_Check(k) && + PyTuple_GET_SIZE(k) == 2 && + PyInt_Check((k = PyTuple_GET_ITEM(k, 0)))) + ) { + PyErr_SetString(PicklingError, + "Unexpected data in internal list"); + return NULL; + } + + else { /* put */ + ik = PyInt_AS_LONG((PyIntObject *)k); + if (ik >= lm || ik == 0) { + PyErr_SetString(PicklingError, + "Invalid get data"); + return NULL; + } + have_get[ik] = 1; + rsize += ik < 256 ? 2 : 5; + } + } + + /* Now generate the result */ + r = PyString_FromStringAndSize(NULL, rsize); + if (r == NULL) goto err; + s = PyString_AS_STRING((PyStringObject *)r); + + for (i = 0; i < l; i++) { + k = data->data[i]; + + if (PyString_Check(k)) { + ssize = PyString_GET_SIZE(k); + if (ssize) { + p=PyString_AS_STRING((PyStringObject *)k); + while (--ssize >= 0) + *s++ = *p++; + } + } + + else if (PyTuple_Check(k)) { /* get */ + ik = PyInt_AS_LONG((PyIntObject *) + PyTuple_GET_ITEM(k, 0)); + if (ik < 256) { + *s++ = BINGET; + *s++ = (int)(ik & 0xff); + } + else { + *s++ = LONG_BINGET; + *s++ = (int)(ik & 0xff); + *s++ = (int)((ik >> 8) & 0xff); + *s++ = (int)((ik >> 16) & 0xff); + *s++ = (int)((ik >> 24) & 0xff); + } + } + + else { /* put */ + ik = PyInt_AS_LONG((PyIntObject*)k); + + if (have_get[ik]) { /* with matching get */ + if (ik < 256) { + *s++ = BINPUT; + *s++ = (int)(ik & 0xff); + } + else { + *s++ = LONG_BINPUT; + *s++ = (int)(ik & 0xff); + *s++ = (int)((ik >> 8) & 0xff); + *s++ = (int)((ik >> 16) & 0xff); + *s++ = (int)((ik >> 24) & 0xff); + } + } + } + } + + if (clear) { + PyDict_Clear(self->memo); + Pdata_clear(data, 0); + } + + free(have_get); + return r; + err: + free(have_get); + return NULL; +} + +static PyObject * +Pickler_dump(Picklerobject *self, PyObject *args) +{ + PyObject *ob; + int get=0; + + if (!( PyArg_ParseTuple(args, "O|i:dump", &ob, &get))) + return NULL; + + if (dump(self, ob) < 0) + return NULL; + + if (get) return Pickle_getvalue(self, NULL); + + /* XXX Why does dump() return self? */ + Py_INCREF(self); + return (PyObject*)self; +} + + +static struct PyMethodDef Pickler_methods[] = +{ + {"dump", (PyCFunction)Pickler_dump, METH_VARARGS, + PyDoc_STR("dump(object) -- " + "Write an object in pickle format to the object's pickle stream")}, + {"clear_memo", (PyCFunction)Pickle_clear_memo, METH_NOARGS, + PyDoc_STR("clear_memo() -- Clear the picklers memo")}, + {"getvalue", (PyCFunction)Pickle_getvalue, METH_VARARGS, + PyDoc_STR("getvalue() -- Finish picking a list-based pickle")}, + {NULL, NULL} /* sentinel */ +}; + + +static Picklerobject * +newPicklerobject(PyObject *file, int proto) +{ + Picklerobject *self; + + if (proto < 0) + proto = HIGHEST_PROTOCOL; + if (proto > HIGHEST_PROTOCOL) { + PyErr_Format(PyExc_ValueError, "pickle protocol %d asked for; " + "the highest available protocol is %d", + proto, HIGHEST_PROTOCOL); + return NULL; + } + + self = PyObject_GC_New(Picklerobject, &Picklertype); + if (self == NULL) + return NULL; + self->proto = proto; + self->bin = proto > 0; + self->fp = NULL; + self->write = NULL; + self->memo = NULL; + self->arg = NULL; + self->pers_func = NULL; + self->inst_pers_func = NULL; + self->write_buf = NULL; + self->fast = 0; + self->nesting = 0; + self->fast_container = 0; + self->fast_memo = NULL; + self->buf_size = 0; + self->dispatch_table = NULL; + + self->file = NULL; + if (file) + Py_INCREF(file); + else { + file = Pdata_New(); + if (file == NULL) + goto err; + } + self->file = file; + + if (!( self->memo = PyDict_New())) + goto err; + + if (PyFile_Check(file)) { + self->fp = PyFile_AsFile(file); + if (self->fp == NULL) { + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + goto err; + } + self->write_func = write_file; + } + else if (PycStringIO_OutputCheck(file)) { + self->write_func = write_cStringIO; + } + else if (file == Py_None) { + self->write_func = write_none; + } + else { + self->write_func = write_other; + + if (! Pdata_Check(file)) { + self->write = PyObject_GetAttr(file, write_str); + if (!self->write) { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, + "argument must have 'write' " + "attribute"); + goto err; + } + } + + self->write_buf = (char *)PyMem_Malloc(WRITE_BUF_SIZE); + if (self->write_buf == NULL) { + PyErr_NoMemory(); + goto err; + } + } + + if (PyEval_GetRestricted()) { + /* Restricted execution, get private tables */ + PyObject *m = PyImport_Import(copy_reg_str); + + if (m == NULL) + goto err; + self->dispatch_table = PyObject_GetAttr(m, dispatch_table_str); + Py_DECREF(m); + if (self->dispatch_table == NULL) + goto err; + } + else { + self->dispatch_table = dispatch_table; + Py_INCREF(dispatch_table); + } + PyObject_GC_Track(self); + + return self; + + err: + Py_DECREF(self); + return NULL; +} + + +static PyObject * +get_Pickler(PyObject *self, PyObject *args) +{ + PyObject *file = NULL; + int proto = 0; + + /* XXX + * The documented signature is Pickler(file, proto=0), but this + * accepts Pickler() and Pickler(integer) too. The meaning then + * is clear as mud, undocumented, and not supported by pickle.py. + * I'm told Zope uses this, but I haven't traced into this code + * far enough to figure out what it means. + */ + if (!PyArg_ParseTuple(args, "|i:Pickler", &proto)) { + PyErr_Clear(); + proto = 0; + if (!PyArg_ParseTuple(args, "O|i:Pickler", &file, &proto)) + return NULL; + } + return (PyObject *)newPicklerobject(file, proto); +} + + +static void +Pickler_dealloc(Picklerobject *self) +{ + PyObject_GC_UnTrack(self); + Py_XDECREF(self->write); + Py_XDECREF(self->memo); + Py_XDECREF(self->fast_memo); + Py_XDECREF(self->arg); + Py_XDECREF(self->file); + Py_XDECREF(self->pers_func); + Py_XDECREF(self->inst_pers_func); + Py_XDECREF(self->dispatch_table); + PyMem_Free(self->write_buf); + self->ob_type->tp_free((PyObject *)self); +} + +static int +Pickler_traverse(Picklerobject *self, visitproc visit, void *arg) +{ + int err; +#define VISIT(SLOT) \ + if (SLOT) { \ + err = visit((PyObject *)(SLOT), arg); \ + if (err) \ + return err; \ + } + VISIT(self->write); + VISIT(self->memo); + VISIT(self->fast_memo); + VISIT(self->arg); + VISIT(self->file); + VISIT(self->pers_func); + VISIT(self->inst_pers_func); + VISIT(self->dispatch_table); +#undef VISIT + return 0; +} + +static int +Pickler_clear(Picklerobject *self) +{ +#define CLEAR(SLOT) Py_XDECREF(SLOT); SLOT = NULL; + CLEAR(self->write); + CLEAR(self->memo); + CLEAR(self->fast_memo); + CLEAR(self->arg); + CLEAR(self->file); + CLEAR(self->pers_func); + CLEAR(self->inst_pers_func); + CLEAR(self->dispatch_table); +#undef CLEAR + return 0; +} + +static PyObject * +Pickler_get_pers_func(Picklerobject *p) +{ + if (p->pers_func == NULL) + PyErr_SetString(PyExc_AttributeError, "persistent_id"); + else + Py_INCREF(p->pers_func); + return p->pers_func; +} + +static int +Pickler_set_pers_func(Picklerobject *p, PyObject *v) +{ + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, + "attribute deletion is not supported"); + return -1; + } + Py_XDECREF(p->pers_func); + Py_INCREF(v); + p->pers_func = v; + return 0; +} + +static int +Pickler_set_inst_pers_func(Picklerobject *p, PyObject *v) +{ + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, + "attribute deletion is not supported"); + return -1; + } + Py_XDECREF(p->inst_pers_func); + Py_INCREF(v); + p->inst_pers_func = v; + return 0; +} + +static PyObject * +Pickler_get_memo(Picklerobject *p) +{ + if (p->memo == NULL) + PyErr_SetString(PyExc_AttributeError, "memo"); + else + Py_INCREF(p->memo); + return p->memo; +} + +static int +Pickler_set_memo(Picklerobject *p, PyObject *v) +{ + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, + "attribute deletion is not supported"); + return -1; + } + if (!PyDict_Check(v)) { + PyErr_SetString(PyExc_TypeError, "memo must be a dictionary"); + return -1; + } + Py_XDECREF(p->memo); + Py_INCREF(v); + p->memo = v; + return 0; +} + +static PyObject * +Pickler_get_error(Picklerobject *p) +{ + /* why is this an attribute on the Pickler? */ + Py_INCREF(PicklingError); + return PicklingError; +} + +static PyMemberDef Pickler_members[] = { + {"binary", T_INT, offsetof(Picklerobject, bin)}, + {"fast", T_INT, offsetof(Picklerobject, fast)}, + {NULL} +}; + +static PyGetSetDef Pickler_getsets[] = { + {"persistent_id", (getter)Pickler_get_pers_func, + (setter)Pickler_set_pers_func}, + {"inst_persistent_id", NULL, (setter)Pickler_set_inst_pers_func}, + {"memo", (getter)Pickler_get_memo, (setter)Pickler_set_memo}, + {"PicklingError", (getter)Pickler_get_error, NULL}, + {NULL} +}; + +PyDoc_STRVAR(Picklertype__doc__, +"Objects that know how to pickle objects\n"); + +static PyTypeObject Picklertype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "cPickle.Pickler", /*tp_name*/ + sizeof(Picklerobject), /*tp_basicsize*/ + 0, + (destructor)Pickler_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + Picklertype__doc__, /* tp_doc */ + (traverseproc)Pickler_traverse, /* tp_traverse */ + (inquiry)Pickler_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Pickler_methods, /* tp_methods */ + Pickler_members, /* tp_members */ + Pickler_getsets, /* tp_getset */ +}; + +static PyObject * +find_class(PyObject *py_module_name, PyObject *py_global_name, PyObject *fc) +{ + PyObject *global = 0, *module; + + if (fc) { + if (fc==Py_None) { + PyErr_SetString(UnpicklingError, "Global and instance " + "pickles are not supported."); + return NULL; + } + return PyObject_CallFunction(fc, "OO", py_module_name, + py_global_name); + } + + module = PySys_GetObject("modules"); + if (module == NULL) + return NULL; + + module = PyDict_GetItem(module, py_module_name); + if (module == NULL) { + module = PyImport_Import(py_module_name); + if (!module) + return NULL; + global = PyObject_GetAttr(module, py_global_name); + Py_DECREF(module); + } + else + global = PyObject_GetAttr(module, py_global_name); + return global; +} + +static int +marker(Unpicklerobject *self) +{ + if (self->num_marks < 1) { + PyErr_SetString(UnpicklingError, "could not find MARK"); + return -1; + } + + return self->marks[--self->num_marks]; +} + + +static int +load_none(Unpicklerobject *self) +{ + PDATA_APPEND(self->stack, Py_None, -1); + return 0; +} + +static int +bad_readline(void) +{ + PyErr_SetString(UnpicklingError, "pickle data was truncated"); + return -1; +} + +static int +load_int(Unpicklerobject *self) +{ + PyObject *py_int = 0; + char *endptr, *s; + int len, res = -1; + long l; + + if ((len = self->readline_func(self, &s)) < 0) return -1; + if (len < 2) return bad_readline(); + if (!( s=pystrndup(s,len))) return -1; + + errno = 0; + l = strtol(s, &endptr, 0); + + if (errno || (*endptr != '\n') || (endptr[1] != '\0')) { + /* Hm, maybe we've got something long. Let's try reading + it as a Python long object. */ + errno = 0; + py_int = PyLong_FromString(s, NULL, 0); + if (py_int == NULL) { + PyErr_SetString(PyExc_ValueError, + "could not convert string to int"); + goto finally; + } + } + else { + if (len == 3 && (l == 0 || l == 1)) { + if (!( py_int = PyBool_FromLong(l))) goto finally; + } + else { + if (!( py_int = PyInt_FromLong(l))) goto finally; + } + } + + free(s); + PDATA_PUSH(self->stack, py_int, -1); + return 0; + + finally: + free(s); + + return res; +} + +static int +load_bool(Unpicklerobject *self, PyObject *boolean) +{ + assert(boolean == Py_True || boolean == Py_False); + PDATA_APPEND(self->stack, boolean, -1); + return 0; +} + +/* s contains x bytes of a little-endian integer. Return its value as a + * C int. Obscure: when x is 1 or 2, this is an unsigned little-endian + * int, but when x is 4 it's a signed one. This is an historical source + * of x-platform bugs. + */ +static long +calc_binint(char *s, int x) +{ + unsigned char c; + int i; + long l; + + for (i = 0, l = 0L; i < x; i++) { + c = (unsigned char)s[i]; + l |= (long)c << (i * 8); + } +#if SIZEOF_LONG > 4 + /* Unlike BININT1 and BININT2, BININT (more accurately BININT4) + * is signed, so on a box with longs bigger than 4 bytes we need + * to extend a BININT's sign bit to the full width. + */ + if (x == 4 && l & (1L << 31)) + l |= (~0L) << 32; +#endif + return l; +} + + +static int +load_binintx(Unpicklerobject *self, char *s, int x) +{ + PyObject *py_int = 0; + long l; + + l = calc_binint(s, x); + + if (!( py_int = PyInt_FromLong(l))) + return -1; + + PDATA_PUSH(self->stack, py_int, -1); + return 0; +} + + +static int +load_binint(Unpicklerobject *self) +{ + char *s; + + if (self->read_func(self, &s, 4) < 0) + return -1; + + return load_binintx(self, s, 4); +} + + +static int +load_binint1(Unpicklerobject *self) +{ + char *s; + + if (self->read_func(self, &s, 1) < 0) + return -1; + + return load_binintx(self, s, 1); +} + + +static int +load_binint2(Unpicklerobject *self) +{ + char *s; + + if (self->read_func(self, &s, 2) < 0) + return -1; + + return load_binintx(self, s, 2); +} + +static int +load_long(Unpicklerobject *self) +{ + PyObject *l = 0; + char *end, *s; + int len, res = -1; + + if ((len = self->readline_func(self, &s)) < 0) return -1; + if (len < 2) return bad_readline(); + if (!( s=pystrndup(s,len))) return -1; + + if (!( l = PyLong_FromString(s, &end, 0))) + goto finally; + + free(s); + PDATA_PUSH(self->stack, l, -1); + return 0; + + finally: + free(s); + + return res; +} + +/* 'size' bytes contain the # of bytes of little-endian 256's-complement + * data following. + */ +static int +load_counted_long(Unpicklerobject *self, int size) +{ + int i; + char *nbytes; + unsigned char *pdata; + PyObject *along; + + assert(size == 1 || size == 4); + i = self->read_func(self, &nbytes, size); + if (i < 0) return -1; + + size = calc_binint(nbytes, size); + if (size < 0) { + /* Corrupt or hostile pickle -- we never write one like + * this. + */ + PyErr_SetString(UnpicklingError, "LONG pickle has negative " + "byte count"); + return -1; + } + + if (size == 0) + along = PyLong_FromLong(0L); + else { + /* Read the raw little-endian bytes & convert. */ + i = self->read_func(self, (char **)&pdata, size); + if (i < 0) return -1; + along = _PyLong_FromByteArray(pdata, (size_t)size, + 1 /* little endian */, 1 /* signed */); + } + if (along == NULL) + return -1; + PDATA_PUSH(self->stack, along, -1); + return 0; +} + +static int +load_float(Unpicklerobject *self) +{ + PyObject *py_float = 0; + char *endptr, *s; + int len, res = -1; + double d; + + if ((len = self->readline_func(self, &s)) < 0) return -1; + if (len < 2) return bad_readline(); + if (!( s=pystrndup(s,len))) return -1; + + errno = 0; + d = strtod(s, &endptr); + + if (errno || (endptr[0] != '\n') || (endptr[1] != '\0')) { + PyErr_SetString(PyExc_ValueError, + "could not convert string to float"); + goto finally; + } + + if (!( py_float = PyFloat_FromDouble(d))) + goto finally; + + free(s); + PDATA_PUSH(self->stack, py_float, -1); + return 0; + + finally: + free(s); + + return res; +} + +static int +load_binfloat(Unpicklerobject *self) +{ + PyObject *py_float; + double x; + char *p; + + if (self->read_func(self, &p, 8) < 0) + return -1; + + x = _PyFloat_Unpack8((unsigned char *)p, 0); + if (x == -1.0 && PyErr_Occurred()) + return -1; + + py_float = PyFloat_FromDouble(x); + if (py_float == NULL) + return -1; + + PDATA_PUSH(self->stack, py_float, -1); + return 0; +} + +static int +load_string(Unpicklerobject *self) +{ + PyObject *str = 0; + int len, res = -1; + char *s, *p; + + if ((len = self->readline_func(self, &s)) < 0) return -1; + if (len < 2) return bad_readline(); + if (!( s=pystrndup(s,len))) return -1; + + + /* Strip outermost quotes */ + while (s[len-1] <= ' ') + len--; + if(s[0]=='"' && s[len-1]=='"'){ + s[len-1] = '\0'; + p = s + 1 ; + len -= 2; + } else if(s[0]=='\'' && s[len-1]=='\''){ + s[len-1] = '\0'; + p = s + 1 ; + len -= 2; + } else + goto insecure; + /********************************************/ + + str = PyString_DecodeEscape(p, len, NULL, 0, NULL); + if (str) { + PDATA_PUSH(self->stack, str, -1); + res = 0; + } + free(s); + return res; + + insecure: + free(s); + PyErr_SetString(PyExc_ValueError,"insecure string pickle"); + return -1; +} + + +static int +load_binstring(Unpicklerobject *self) +{ + PyObject *py_string = 0; + long l; + char *s; + + if (self->read_func(self, &s, 4) < 0) return -1; + + l = calc_binint(s, 4); + + if (self->read_func(self, &s, l) < 0) + return -1; + + if (!( py_string = PyString_FromStringAndSize(s, l))) + return -1; + + PDATA_PUSH(self->stack, py_string, -1); + return 0; +} + + +static int +load_short_binstring(Unpicklerobject *self) +{ + PyObject *py_string = 0; + unsigned char l; + char *s; + + if (self->read_func(self, &s, 1) < 0) + return -1; + + l = (unsigned char)s[0]; + + if (self->read_func(self, &s, l) < 0) return -1; + + if (!( py_string = PyString_FromStringAndSize(s, l))) return -1; + + PDATA_PUSH(self->stack, py_string, -1); + return 0; +} + + +#ifdef Py_USING_UNICODE +static int +load_unicode(Unpicklerobject *self) +{ + PyObject *str = 0; + int len, res = -1; + char *s; + + if ((len = self->readline_func(self, &s)) < 0) return -1; + if (len < 1) return bad_readline(); + + if (!( str = PyUnicode_DecodeRawUnicodeEscape(s, len - 1, NULL))) + goto finally; + + PDATA_PUSH(self->stack, str, -1); + return 0; + + finally: + return res; +} +#endif + + +#ifdef Py_USING_UNICODE +static int +load_binunicode(Unpicklerobject *self) +{ + PyObject *unicode; + long l; + char *s; + + if (self->read_func(self, &s, 4) < 0) return -1; + + l = calc_binint(s, 4); + + if (self->read_func(self, &s, l) < 0) + return -1; + + if (!( unicode = PyUnicode_DecodeUTF8(s, l, NULL))) + return -1; + + PDATA_PUSH(self->stack, unicode, -1); + return 0; +} +#endif + + +static int +load_tuple(Unpicklerobject *self) +{ + PyObject *tup; + int i; + + if ((i = marker(self)) < 0) return -1; + if (!( tup=Pdata_popTuple(self->stack, i))) return -1; + PDATA_PUSH(self->stack, tup, -1); + return 0; +} + +static int +load_counted_tuple(Unpicklerobject *self, int len) +{ + PyObject *tup = PyTuple_New(len); + + if (tup == NULL) + return -1; + + while (--len >= 0) { + PyObject *element; + + PDATA_POP(self->stack, element); + if (element == NULL) + return -1; + PyTuple_SET_ITEM(tup, len, element); + } + PDATA_PUSH(self->stack, tup, -1); + return 0; +} + +static int +load_empty_list(Unpicklerobject *self) +{ + PyObject *list; + + if (!( list=PyList_New(0))) return -1; + PDATA_PUSH(self->stack, list, -1); + return 0; +} + +static int +load_empty_dict(Unpicklerobject *self) +{ + PyObject *dict; + + if (!( dict=PyDict_New())) return -1; + PDATA_PUSH(self->stack, dict, -1); + return 0; +} + + +static int +load_list(Unpicklerobject *self) +{ + PyObject *list = 0; + int i; + + if ((i = marker(self)) < 0) return -1; + if (!( list=Pdata_popList(self->stack, i))) return -1; + PDATA_PUSH(self->stack, list, -1); + return 0; +} + +static int +load_dict(Unpicklerobject *self) +{ + PyObject *dict, *key, *value; + int i, j, k; + + if ((i = marker(self)) < 0) return -1; + j=self->stack->length; + + if (!( dict = PyDict_New())) return -1; + + for (k = i+1; k < j; k += 2) { + key =self->stack->data[k-1]; + value=self->stack->data[k ]; + if (PyDict_SetItem(dict, key, value) < 0) { + Py_DECREF(dict); + return -1; + } + } + Pdata_clear(self->stack, i); + PDATA_PUSH(self->stack, dict, -1); + return 0; +} + +static PyObject * +Instance_New(PyObject *cls, PyObject *args) +{ + PyObject *r = 0; + + if (PyClass_Check(cls)) { + int l; + + if ((l=PyObject_Size(args)) < 0) goto err; + if (!( l )) { + PyObject *__getinitargs__; + + __getinitargs__ = PyObject_GetAttr(cls, + __getinitargs___str); + if (!__getinitargs__) { + /* We have a class with no __getinitargs__, + so bypass usual construction */ + PyObject *inst; + + PyErr_Clear(); + if (!( inst=PyInstance_NewRaw(cls, NULL))) + goto err; + return inst; + } + Py_DECREF(__getinitargs__); + } + + if ((r=PyInstance_New(cls, args, NULL))) return r; + else goto err; + } + + if (args==Py_None) { + /* Special case, call cls.__basicnew__() */ + PyObject *basicnew; + + basicnew = PyObject_GetAttr(cls, __basicnew___str); + if (!basicnew) return NULL; + r=PyObject_CallObject(basicnew, NULL); + Py_DECREF(basicnew); + if (r) return r; + } + + if ((r=PyObject_CallObject(cls, args))) return r; + + err: + { + PyObject *tp, *v, *tb; + + PyErr_Fetch(&tp, &v, &tb); + if ((r=Py_BuildValue("OOO",v,cls,args))) { + Py_XDECREF(v); + v=r; + } + PyErr_Restore(tp,v,tb); + } + return NULL; +} + + +static int +load_obj(Unpicklerobject *self) +{ + PyObject *class, *tup, *obj=0; + int i; + + if ((i = marker(self)) < 0) return -1; + if (!( tup=Pdata_popTuple(self->stack, i+1))) return -1; + PDATA_POP(self->stack, class); + if (class) { + obj = Instance_New(class, tup); + Py_DECREF(class); + } + Py_DECREF(tup); + + if (! obj) return -1; + PDATA_PUSH(self->stack, obj, -1); + return 0; +} + + +static int +load_inst(Unpicklerobject *self) +{ + PyObject *tup, *class=0, *obj=0, *module_name, *class_name; + int i, len; + char *s; + + if ((i = marker(self)) < 0) return -1; + + if ((len = self->readline_func(self, &s)) < 0) return -1; + if (len < 2) return bad_readline(); + module_name = PyString_FromStringAndSize(s, len - 1); + if (!module_name) return -1; + + if ((len = self->readline_func(self, &s)) >= 0) { + if (len < 2) return bad_readline(); + if ((class_name = PyString_FromStringAndSize(s, len - 1))) { + class = find_class(module_name, class_name, + self->find_class); + Py_DECREF(class_name); + } + } + Py_DECREF(module_name); + + if (! class) return -1; + + if ((tup=Pdata_popTuple(self->stack, i))) { + obj = Instance_New(class, tup); + Py_DECREF(tup); + } + Py_DECREF(class); + + if (! obj) return -1; + + PDATA_PUSH(self->stack, obj, -1); + return 0; +} + +static int +load_newobj(Unpicklerobject *self) +{ + PyObject *args = NULL; + PyObject *clsraw = NULL; + PyTypeObject *cls; /* clsraw cast to its true type */ + PyObject *obj; + + /* Stack is ... cls argtuple, and we want to call + * cls.__new__(cls, *argtuple). + */ + PDATA_POP(self->stack, args); + if (args == NULL) goto Fail; + if (! PyTuple_Check(args)) { + PyErr_SetString(UnpicklingError, "NEWOBJ expected an arg " + "tuple."); + goto Fail; + } + + PDATA_POP(self->stack, clsraw); + cls = (PyTypeObject *)clsraw; + if (cls == NULL) goto Fail; + if (! PyType_Check(cls)) { + PyErr_SetString(UnpicklingError, "NEWOBJ class argument " + "isn't a type object"); + goto Fail; + } + if (cls->tp_new == NULL) { + PyErr_SetString(UnpicklingError, "NEWOBJ class argument " + "has NULL tp_new"); + goto Fail; + } + + /* Call __new__. */ + obj = cls->tp_new(cls, args, NULL); + if (obj == NULL) goto Fail; + + Py_DECREF(args); + Py_DECREF(clsraw); + PDATA_PUSH(self->stack, obj, -1); + return 0; + + Fail: + Py_XDECREF(args); + Py_XDECREF(clsraw); + return -1; +} + +static int +load_global(Unpicklerobject *self) +{ + PyObject *class = 0, *module_name = 0, *class_name = 0; + int len; + char *s; + + if ((len = self->readline_func(self, &s)) < 0) return -1; + if (len < 2) return bad_readline(); + module_name = PyString_FromStringAndSize(s, len - 1); + if (!module_name) return -1; + + if ((len = self->readline_func(self, &s)) >= 0) { + if (len < 2) { + Py_DECREF(module_name); + return bad_readline(); + } + if ((class_name = PyString_FromStringAndSize(s, len - 1))) { + class = find_class(module_name, class_name, + self->find_class); + Py_DECREF(class_name); + } + } + Py_DECREF(module_name); + + if (! class) return -1; + PDATA_PUSH(self->stack, class, -1); + return 0; +} + + +static int +load_persid(Unpicklerobject *self) +{ + PyObject *pid = 0; + int len; + char *s; + + if (self->pers_func) { + if ((len = self->readline_func(self, &s)) < 0) return -1; + if (len < 2) return bad_readline(); + + pid = PyString_FromStringAndSize(s, len - 1); + if (!pid) return -1; + + if (PyList_Check(self->pers_func)) { + if (PyList_Append(self->pers_func, pid) < 0) { + Py_DECREF(pid); + return -1; + } + } + else { + ARG_TUP(self, pid); + if (self->arg) { + pid = PyObject_Call(self->pers_func, self->arg, + NULL); + FREE_ARG_TUP(self); + } + } + + if (! pid) return -1; + + PDATA_PUSH(self->stack, pid, -1); + return 0; + } + else { + PyErr_SetString(UnpicklingError, + "A load persistent id instruction was encountered,\n" + "but no persistent_load function was specified."); + return -1; + } +} + +static int +load_binpersid(Unpicklerobject *self) +{ + PyObject *pid = 0; + + if (self->pers_func) { + PDATA_POP(self->stack, pid); + if (! pid) return -1; + + if (PyList_Check(self->pers_func)) { + if (PyList_Append(self->pers_func, pid) < 0) { + Py_DECREF(pid); + return -1; + } + } + else { + ARG_TUP(self, pid); + if (self->arg) { + pid = PyObject_Call(self->pers_func, self->arg, + NULL); + FREE_ARG_TUP(self); + } + if (! pid) return -1; + } + + PDATA_PUSH(self->stack, pid, -1); + return 0; + } + else { + PyErr_SetString(UnpicklingError, + "A load persistent id instruction was encountered,\n" + "but no persistent_load function was specified."); + return -1; + } +} + + +static int +load_pop(Unpicklerobject *self) +{ + int len; + + if (!( (len=self->stack->length) > 0 )) return stackUnderflow(); + + /* Note that we split the (pickle.py) stack into two stacks, + an object stack and a mark stack. We have to be clever and + pop the right one. We do this by looking at the top of the + mark stack. + */ + + if ((self->num_marks > 0) && + (self->marks[self->num_marks - 1] == len)) + self->num_marks--; + else { + len--; + Py_DECREF(self->stack->data[len]); + self->stack->length=len; + } + + return 0; +} + + +static int +load_pop_mark(Unpicklerobject *self) +{ + int i; + + if ((i = marker(self)) < 0) + return -1; + + Pdata_clear(self->stack, i); + + return 0; +} + + +static int +load_dup(Unpicklerobject *self) +{ + PyObject *last; + int len; + + if ((len = self->stack->length) <= 0) return stackUnderflow(); + last=self->stack->data[len-1]; + Py_INCREF(last); + PDATA_PUSH(self->stack, last, -1); + return 0; +} + + +static int +load_get(Unpicklerobject *self) +{ + PyObject *py_str = 0, *value = 0; + int len; + char *s; + int rc; + + if ((len = self->readline_func(self, &s)) < 0) return -1; + if (len < 2) return bad_readline(); + + if (!( py_str = PyString_FromStringAndSize(s, len - 1))) return -1; + + value = PyDict_GetItem(self->memo, py_str); + if (! value) { + PyErr_SetObject(BadPickleGet, py_str); + rc = -1; + } + else { + PDATA_APPEND(self->stack, value, -1); + rc = 0; + } + + Py_DECREF(py_str); + return rc; +} + + +static int +load_binget(Unpicklerobject *self) +{ + PyObject *py_key = 0, *value = 0; + unsigned char key; + char *s; + int rc; + + if (self->read_func(self, &s, 1) < 0) return -1; + + key = (unsigned char)s[0]; + if (!( py_key = PyInt_FromLong((long)key))) return -1; + + value = PyDict_GetItem(self->memo, py_key); + if (! value) { + PyErr_SetObject(BadPickleGet, py_key); + rc = -1; + } + else { + PDATA_APPEND(self->stack, value, -1); + rc = 0; + } + + Py_DECREF(py_key); + return rc; +} + + +static int +load_long_binget(Unpicklerobject *self) +{ + PyObject *py_key = 0, *value = 0; + unsigned char c; + char *s; + long key; + int rc; + + if (self->read_func(self, &s, 4) < 0) return -1; + + c = (unsigned char)s[0]; + key = (long)c; + c = (unsigned char)s[1]; + key |= (long)c << 8; + c = (unsigned char)s[2]; + key |= (long)c << 16; + c = (unsigned char)s[3]; + key |= (long)c << 24; + + if (!( py_key = PyInt_FromLong((long)key))) return -1; + + value = PyDict_GetItem(self->memo, py_key); + if (! value) { + PyErr_SetObject(BadPickleGet, py_key); + rc = -1; + } + else { + PDATA_APPEND(self->stack, value, -1); + rc = 0; + } + + Py_DECREF(py_key); + return rc; +} + +/* Push an object from the extension registry (EXT[124]). nbytes is + * the number of bytes following the opcode, holding the index (code) value. + */ +static int +load_extension(Unpicklerobject *self, int nbytes) +{ + char *codebytes; /* the nbytes bytes after the opcode */ + long code; /* calc_binint returns long */ + PyObject *py_code; /* code as a Python int */ + PyObject *obj; /* the object to push */ + PyObject *pair; /* (module_name, class_name) */ + PyObject *module_name, *class_name; + + assert(nbytes == 1 || nbytes == 2 || nbytes == 4); + if (self->read_func(self, &codebytes, nbytes) < 0) return -1; + code = calc_binint(codebytes, nbytes); + if (code <= 0) { /* note that 0 is forbidden */ + /* Corrupt or hostile pickle. */ + PyErr_SetString(UnpicklingError, "EXT specifies code <= 0"); + return -1; + } + + /* Look for the code in the cache. */ + py_code = PyInt_FromLong(code); + if (py_code == NULL) return -1; + obj = PyDict_GetItem(extension_cache, py_code); + if (obj != NULL) { + /* Bingo. */ + Py_DECREF(py_code); + PDATA_APPEND(self->stack, obj, -1); + return 0; + } + + /* Look up the (module_name, class_name) pair. */ + pair = PyDict_GetItem(inverted_registry, py_code); + if (pair == NULL) { + Py_DECREF(py_code); + PyErr_Format(PyExc_ValueError, "unregistered extension " + "code %ld", code); + return -1; + } + /* Since the extension registry is manipulable via Python code, + * confirm that pair is really a 2-tuple of strings. + */ + if (!PyTuple_Check(pair) || PyTuple_Size(pair) != 2 || + !PyString_Check(module_name = PyTuple_GET_ITEM(pair, 0)) || + !PyString_Check(class_name = PyTuple_GET_ITEM(pair, 1))) { + Py_DECREF(py_code); + PyErr_Format(PyExc_ValueError, "_inverted_registry[%ld] " + "isn't a 2-tuple of strings", code); + return -1; + } + /* Load the object. */ + obj = find_class(module_name, class_name, self->find_class); + if (obj == NULL) { + Py_DECREF(py_code); + return -1; + } + /* Cache code -> obj. */ + code = PyDict_SetItem(extension_cache, py_code, obj); + Py_DECREF(py_code); + if (code < 0) { + Py_DECREF(obj); + return -1; + } + PDATA_PUSH(self->stack, obj, -1); + return 0; +} + +static int +load_put(Unpicklerobject *self) +{ + PyObject *py_str = 0, *value = 0; + int len, l; + char *s; + + if ((l = self->readline_func(self, &s)) < 0) return -1; + if (l < 2) return bad_readline(); + if (!( len=self->stack->length )) return stackUnderflow(); + if (!( py_str = PyString_FromStringAndSize(s, l - 1))) return -1; + value=self->stack->data[len-1]; + l=PyDict_SetItem(self->memo, py_str, value); + Py_DECREF(py_str); + return l; +} + + +static int +load_binput(Unpicklerobject *self) +{ + PyObject *py_key = 0, *value = 0; + unsigned char key; + char *s; + int len; + + if (self->read_func(self, &s, 1) < 0) return -1; + if (!( (len=self->stack->length) > 0 )) return stackUnderflow(); + + key = (unsigned char)s[0]; + + if (!( py_key = PyInt_FromLong((long)key))) return -1; + value=self->stack->data[len-1]; + len=PyDict_SetItem(self->memo, py_key, value); + Py_DECREF(py_key); + return len; +} + + +static int +load_long_binput(Unpicklerobject *self) +{ + PyObject *py_key = 0, *value = 0; + long key; + unsigned char c; + char *s; + int len; + + if (self->read_func(self, &s, 4) < 0) return -1; + if (!( len=self->stack->length )) return stackUnderflow(); + + c = (unsigned char)s[0]; + key = (long)c; + c = (unsigned char)s[1]; + key |= (long)c << 8; + c = (unsigned char)s[2]; + key |= (long)c << 16; + c = (unsigned char)s[3]; + key |= (long)c << 24; + + if (!( py_key = PyInt_FromLong(key))) return -1; + value=self->stack->data[len-1]; + len=PyDict_SetItem(self->memo, py_key, value); + Py_DECREF(py_key); + return len; +} + + +static int +do_append(Unpicklerobject *self, int x) +{ + PyObject *value = 0, *list = 0, *append_method = 0; + int len, i; + + len=self->stack->length; + if (!( len >= x && x > 0 )) return stackUnderflow(); + /* nothing to do */ + if (len==x) return 0; + + list=self->stack->data[x-1]; + + if (PyList_Check(list)) { + PyObject *slice; + int list_len; + + slice=Pdata_popList(self->stack, x); + list_len = PyList_GET_SIZE(list); + i=PyList_SetSlice(list, list_len, list_len, slice); + Py_DECREF(slice); + return i; + } + else { + + if (!( append_method = PyObject_GetAttr(list, append_str))) + return -1; + + for (i = x; i < len; i++) { + PyObject *junk; + + value=self->stack->data[i]; + junk=0; + ARG_TUP(self, value); + if (self->arg) { + junk = PyObject_Call(append_method, self->arg, + NULL); + FREE_ARG_TUP(self); + } + if (! junk) { + Pdata_clear(self->stack, i+1); + self->stack->length=x; + Py_DECREF(append_method); + return -1; + } + Py_DECREF(junk); + } + self->stack->length=x; + Py_DECREF(append_method); + } + + return 0; +} + + +static int +load_append(Unpicklerobject *self) +{ + return do_append(self, self->stack->length - 1); +} + + +static int +load_appends(Unpicklerobject *self) +{ + return do_append(self, marker(self)); +} + + +static int +do_setitems(Unpicklerobject *self, int x) +{ + PyObject *value = 0, *key = 0, *dict = 0; + int len, i, r=0; + + if (!( (len=self->stack->length) >= x + && x > 0 )) return stackUnderflow(); + + dict=self->stack->data[x-1]; + + for (i = x+1; i < len; i += 2) { + key =self->stack->data[i-1]; + value=self->stack->data[i ]; + if (PyObject_SetItem(dict, key, value) < 0) { + r=-1; + break; + } + } + + Pdata_clear(self->stack, x); + + return r; +} + + +static int +load_setitem(Unpicklerobject *self) +{ + return do_setitems(self, self->stack->length - 2); +} + +static int +load_setitems(Unpicklerobject *self) +{ + return do_setitems(self, marker(self)); +} + + +static int +load_build(Unpicklerobject *self) +{ + PyObject *state, *inst, *slotstate; + PyObject *__setstate__; + PyObject *d_key, *d_value; + int i; + int res = -1; + + /* Stack is ... instance, state. We want to leave instance at + * the stack top, possibly mutated via instance.__setstate__(state). + */ + if (self->stack->length < 2) + return stackUnderflow(); + PDATA_POP(self->stack, state); + if (state == NULL) + return -1; + inst = self->stack->data[self->stack->length - 1]; + + __setstate__ = PyObject_GetAttr(inst, __setstate___str); + if (__setstate__ != NULL) { + PyObject *junk = NULL; + + /* The explicit __setstate__ is responsible for everything. */ + ARG_TUP(self, state); + if (self->arg) { + junk = PyObject_Call(__setstate__, self->arg, NULL); + FREE_ARG_TUP(self); + } + Py_DECREF(__setstate__); + if (junk == NULL) + return -1; + Py_DECREF(junk); + return 0; + } + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; + PyErr_Clear(); + + /* A default __setstate__. First see whether state embeds a + * slot state dict too (a proto 2 addition). + */ + if (PyTuple_Check(state) && PyTuple_Size(state) == 2) { + PyObject *temp = state; + state = PyTuple_GET_ITEM(temp, 0); + slotstate = PyTuple_GET_ITEM(temp, 1); + Py_INCREF(state); + Py_INCREF(slotstate); + Py_DECREF(temp); + } + else + slotstate = NULL; + + /* Set inst.__dict__ from the state dict (if any). */ + if (state != Py_None) { + PyObject *dict; + if (! PyDict_Check(state)) { + PyErr_SetString(UnpicklingError, "state is not a " + "dictionary"); + goto finally; + } + dict = PyObject_GetAttr(inst, __dict___str); + if (dict == NULL) + goto finally; + + i = 0; + while (PyDict_Next(state, &i, &d_key, &d_value)) { + if (PyObject_SetItem(dict, d_key, d_value) < 0) + goto finally; + } + Py_DECREF(dict); + } + + /* Also set instance attributes from the slotstate dict (if any). */ + if (slotstate != NULL) { + if (! PyDict_Check(slotstate)) { + PyErr_SetString(UnpicklingError, "slot state is not " + "a dictionary"); + goto finally; + } + i = 0; + while (PyDict_Next(slotstate, &i, &d_key, &d_value)) { + if (PyObject_SetAttr(inst, d_key, d_value) < 0) + goto finally; + } + } + res = 0; + + finally: + Py_DECREF(state); + Py_XDECREF(slotstate); + return res; +} + + +static int +load_mark(Unpicklerobject *self) +{ + int s; + + /* Note that we split the (pickle.py) stack into two stacks, an + object stack and a mark stack. Here we push a mark onto the + mark stack. + */ + + if ((self->num_marks + 1) >= self->marks_size) { + s=self->marks_size+20; + if (s <= self->num_marks) s=self->num_marks + 1; + if (self->marks == NULL) + self->marks=(int *)malloc(s * sizeof(int)); + else + self->marks=(int *)realloc(self->marks, + s * sizeof(int)); + if (! self->marks) { + PyErr_NoMemory(); + return -1; + } + self->marks_size = s; + } + + self->marks[self->num_marks++] = self->stack->length; + + return 0; +} + +static int +load_reduce(Unpicklerobject *self) +{ + PyObject *callable = 0, *arg_tup = 0, *ob = 0; + + PDATA_POP(self->stack, arg_tup); + if (! arg_tup) return -1; + PDATA_POP(self->stack, callable); + if (callable) { + ob = Instance_New(callable, arg_tup); + Py_DECREF(callable); + } + Py_DECREF(arg_tup); + + if (! ob) return -1; + + PDATA_PUSH(self->stack, ob, -1); + return 0; +} + +/* Just raises an error if we don't know the protocol specified. PROTO + * is the first opcode for protocols >= 2. + */ +static int +load_proto(Unpicklerobject *self) +{ + int i; + char *protobyte; + + i = self->read_func(self, &protobyte, 1); + if (i < 0) + return -1; + + i = calc_binint(protobyte, 1); + /* No point checking for < 0, since calc_binint returns an unsigned + * int when chewing on 1 byte. + */ + assert(i >= 0); + if (i <= HIGHEST_PROTOCOL) + return 0; + + PyErr_Format(PyExc_ValueError, "unsupported pickle protocol: %d", i); + return -1; +} + +static PyObject * +load(Unpicklerobject *self) +{ + PyObject *err = 0, *val = 0; + char *s; + + self->num_marks = 0; + if (self->stack->length) Pdata_clear(self->stack, 0); + + while (1) { + if (self->read_func(self, &s, 1) < 0) + break; + + switch (s[0]) { + case NONE: + if (load_none(self) < 0) + break; + continue; + + case BININT: + if (load_binint(self) < 0) + break; + continue; + + case BININT1: + if (load_binint1(self) < 0) + break; + continue; + + case BININT2: + if (load_binint2(self) < 0) + break; + continue; + + case INT: + if (load_int(self) < 0) + break; + continue; + + case LONG: + if (load_long(self) < 0) + break; + continue; + + case LONG1: + if (load_counted_long(self, 1) < 0) + break; + continue; + + case LONG4: + if (load_counted_long(self, 4) < 0) + break; + continue; + + case FLOAT: + if (load_float(self) < 0) + break; + continue; + + case BINFLOAT: + if (load_binfloat(self) < 0) + break; + continue; + + case BINSTRING: + if (load_binstring(self) < 0) + break; + continue; + + case SHORT_BINSTRING: + if (load_short_binstring(self) < 0) + break; + continue; + + case STRING: + if (load_string(self) < 0) + break; + continue; + +#ifdef Py_USING_UNICODE + case UNICODE: + if (load_unicode(self) < 0) + break; + continue; + + case BINUNICODE: + if (load_binunicode(self) < 0) + break; + continue; +#endif + + case EMPTY_TUPLE: + if (load_counted_tuple(self, 0) < 0) + break; + continue; + + case TUPLE1: + if (load_counted_tuple(self, 1) < 0) + break; + continue; + + case TUPLE2: + if (load_counted_tuple(self, 2) < 0) + break; + continue; + + case TUPLE3: + if (load_counted_tuple(self, 3) < 0) + break; + continue; + + case TUPLE: + if (load_tuple(self) < 0) + break; + continue; + + case EMPTY_LIST: + if (load_empty_list(self) < 0) + break; + continue; + + case LIST: + if (load_list(self) < 0) + break; + continue; + + case EMPTY_DICT: + if (load_empty_dict(self) < 0) + break; + continue; + + case DICT: + if (load_dict(self) < 0) + break; + continue; + + case OBJ: + if (load_obj(self) < 0) + break; + continue; + + case INST: + if (load_inst(self) < 0) + break; + continue; + + case NEWOBJ: + if (load_newobj(self) < 0) + break; + continue; + + case GLOBAL: + if (load_global(self) < 0) + break; + continue; + + case APPEND: + if (load_append(self) < 0) + break; + continue; + + case APPENDS: + if (load_appends(self) < 0) + break; + continue; + + case BUILD: + if (load_build(self) < 0) + break; + continue; + + case DUP: + if (load_dup(self) < 0) + break; + continue; + + case BINGET: + if (load_binget(self) < 0) + break; + continue; + + case LONG_BINGET: + if (load_long_binget(self) < 0) + break; + continue; + + case GET: + if (load_get(self) < 0) + break; + continue; + + case EXT1: + if (load_extension(self, 1) < 0) + break; + continue; + + case EXT2: + if (load_extension(self, 2) < 0) + break; + continue; + + case EXT4: + if (load_extension(self, 4) < 0) + break; + continue; + case MARK: + if (load_mark(self) < 0) + break; + continue; + + case BINPUT: + if (load_binput(self) < 0) + break; + continue; + + case LONG_BINPUT: + if (load_long_binput(self) < 0) + break; + continue; + + case PUT: + if (load_put(self) < 0) + break; + continue; + + case POP: + if (load_pop(self) < 0) + break; + continue; + + case POP_MARK: + if (load_pop_mark(self) < 0) + break; + continue; + + case SETITEM: + if (load_setitem(self) < 0) + break; + continue; + + case SETITEMS: + if (load_setitems(self) < 0) + break; + continue; + + case STOP: + break; + + case PERSID: + if (load_persid(self) < 0) + break; + continue; + + case BINPERSID: + if (load_binpersid(self) < 0) + break; + continue; + + case REDUCE: + if (load_reduce(self) < 0) + break; + continue; + + case PROTO: + if (load_proto(self) < 0) + break; + continue; + + case NEWTRUE: + if (load_bool(self, Py_True) < 0) + break; + continue; + + case NEWFALSE: + if (load_bool(self, Py_False) < 0) + break; + continue; + + case '\0': + /* end of file */ + PyErr_SetNone(PyExc_EOFError); + break; + + default: + cPickle_ErrFormat(UnpicklingError, + "invalid load key, '%s'.", + "c", s[0]); + return NULL; + } + + break; + } + + if ((err = PyErr_Occurred())) { + if (err == PyExc_EOFError) { + PyErr_SetNone(PyExc_EOFError); + } + return NULL; + } + + PDATA_POP(self->stack, val); + return val; +} + + +/* No-load functions to support noload, which is used to + find persistent references. */ + +static int +noload_obj(Unpicklerobject *self) +{ + int i; + + if ((i = marker(self)) < 0) return -1; + return Pdata_clear(self->stack, i+1); +} + + +static int +noload_inst(Unpicklerobject *self) +{ + int i; + char *s; + + if ((i = marker(self)) < 0) return -1; + Pdata_clear(self->stack, i); + if (self->readline_func(self, &s) < 0) return -1; + if (self->readline_func(self, &s) < 0) return -1; + PDATA_APPEND(self->stack, Py_None, -1); + return 0; +} + +static int +noload_newobj(Unpicklerobject *self) +{ + PyObject *obj; + + PDATA_POP(self->stack, obj); /* pop argtuple */ + if (obj == NULL) return -1; + Py_DECREF(obj); + + PDATA_POP(self->stack, obj); /* pop cls */ + if (obj == NULL) return -1; + Py_DECREF(obj); + + PDATA_APPEND(self->stack, Py_None, -1); + return 0; +} + +static int +noload_global(Unpicklerobject *self) +{ + char *s; + + if (self->readline_func(self, &s) < 0) return -1; + if (self->readline_func(self, &s) < 0) return -1; + PDATA_APPEND(self->stack, Py_None,-1); + return 0; +} + +static int +noload_reduce(Unpicklerobject *self) +{ + + if (self->stack->length < 2) return stackUnderflow(); + Pdata_clear(self->stack, self->stack->length-2); + PDATA_APPEND(self->stack, Py_None,-1); + return 0; +} + +static int +noload_build(Unpicklerobject *self) { + + if (self->stack->length < 1) return stackUnderflow(); + Pdata_clear(self->stack, self->stack->length-1); + return 0; +} + +static int +noload_extension(Unpicklerobject *self, int nbytes) +{ + char *codebytes; + + assert(nbytes == 1 || nbytes == 2 || nbytes == 4); + if (self->read_func(self, &codebytes, nbytes) < 0) return -1; + PDATA_APPEND(self->stack, Py_None, -1); + return 0; +} + + +static PyObject * +noload(Unpicklerobject *self) +{ + PyObject *err = 0, *val = 0; + char *s; + + self->num_marks = 0; + Pdata_clear(self->stack, 0); + + while (1) { + if (self->read_func(self, &s, 1) < 0) + break; + + switch (s[0]) { + case NONE: + if (load_none(self) < 0) + break; + continue; + + case BININT: + if (load_binint(self) < 0) + break; + continue; + + case BININT1: + if (load_binint1(self) < 0) + break; + continue; + + case BININT2: + if (load_binint2(self) < 0) + break; + continue; + + case INT: + if (load_int(self) < 0) + break; + continue; + + case LONG: + if (load_long(self) < 0) + break; + continue; + + case LONG1: + if (load_counted_long(self, 1) < 0) + break; + continue; + + case LONG4: + if (load_counted_long(self, 4) < 0) + break; + continue; + + case FLOAT: + if (load_float(self) < 0) + break; + continue; + + case BINFLOAT: + if (load_binfloat(self) < 0) + break; + continue; + + case BINSTRING: + if (load_binstring(self) < 0) + break; + continue; + + case SHORT_BINSTRING: + if (load_short_binstring(self) < 0) + break; + continue; + + case STRING: + if (load_string(self) < 0) + break; + continue; + +#ifdef Py_USING_UNICODE + case UNICODE: + if (load_unicode(self) < 0) + break; + continue; + + case BINUNICODE: + if (load_binunicode(self) < 0) + break; + continue; +#endif + + case EMPTY_TUPLE: + if (load_counted_tuple(self, 0) < 0) + break; + continue; + + case TUPLE1: + if (load_counted_tuple(self, 1) < 0) + break; + continue; + + case TUPLE2: + if (load_counted_tuple(self, 2) < 0) + break; + continue; + + case TUPLE3: + if (load_counted_tuple(self, 3) < 0) + break; + continue; + + case TUPLE: + if (load_tuple(self) < 0) + break; + continue; + + case EMPTY_LIST: + if (load_empty_list(self) < 0) + break; + continue; + + case LIST: + if (load_list(self) < 0) + break; + continue; + + case EMPTY_DICT: + if (load_empty_dict(self) < 0) + break; + continue; + + case DICT: + if (load_dict(self) < 0) + break; + continue; + + case OBJ: + if (noload_obj(self) < 0) + break; + continue; + + case INST: + if (noload_inst(self) < 0) + break; + continue; + + case NEWOBJ: + if (noload_newobj(self) < 0) + break; + continue; + + case GLOBAL: + if (noload_global(self) < 0) + break; + continue; + + case APPEND: + if (load_append(self) < 0) + break; + continue; + + case APPENDS: + if (load_appends(self) < 0) + break; + continue; + + case BUILD: + if (noload_build(self) < 0) + break; + continue; + + case DUP: + if (load_dup(self) < 0) + break; + continue; + + case BINGET: + if (load_binget(self) < 0) + break; + continue; + + case LONG_BINGET: + if (load_long_binget(self) < 0) + break; + continue; + + case GET: + if (load_get(self) < 0) + break; + continue; + + case EXT1: + if (noload_extension(self, 1) < 0) + break; + continue; + + case EXT2: + if (noload_extension(self, 2) < 0) + break; + continue; + + case EXT4: + if (noload_extension(self, 4) < 0) + break; + continue; + + case MARK: + if (load_mark(self) < 0) + break; + continue; + + case BINPUT: + if (load_binput(self) < 0) + break; + continue; + + case LONG_BINPUT: + if (load_long_binput(self) < 0) + break; + continue; + + case PUT: + if (load_put(self) < 0) + break; + continue; + + case POP: + if (load_pop(self) < 0) + break; + continue; + + case POP_MARK: + if (load_pop_mark(self) < 0) + break; + continue; + + case SETITEM: + if (load_setitem(self) < 0) + break; + continue; + + case SETITEMS: + if (load_setitems(self) < 0) + break; + continue; + + case STOP: + break; + + case PERSID: + if (load_persid(self) < 0) + break; + continue; + + case BINPERSID: + if (load_binpersid(self) < 0) + break; + continue; + + case REDUCE: + if (noload_reduce(self) < 0) + break; + continue; + + case PROTO: + if (load_proto(self) < 0) + break; + continue; + + case NEWTRUE: + if (load_bool(self, Py_True) < 0) + break; + continue; + + case NEWFALSE: + if (load_bool(self, Py_False) < 0) + break; + continue; + default: + cPickle_ErrFormat(UnpicklingError, + "invalid load key, '%s'.", + "c", s[0]); + return NULL; + } + + break; + } + + if ((err = PyErr_Occurred())) { + if (err == PyExc_EOFError) { + PyErr_SetNone(PyExc_EOFError); + } + return NULL; + } + + PDATA_POP(self->stack, val); + return val; +} + + +static PyObject * +Unpickler_load(Unpicklerobject *self, PyObject *args) +{ + if (!( PyArg_ParseTuple(args, ":load"))) + return NULL; + + return load(self); +} + +static PyObject * +Unpickler_noload(Unpicklerobject *self, PyObject *args) +{ + if (!( PyArg_ParseTuple(args, ":noload"))) + return NULL; + + return noload(self); +} + + +static struct PyMethodDef Unpickler_methods[] = { + {"load", (PyCFunction)Unpickler_load, METH_VARARGS, + PyDoc_STR("load() -- Load a pickle") + }, + {"noload", (PyCFunction)Unpickler_noload, METH_VARARGS, + PyDoc_STR( + "noload() -- not load a pickle, but go through most of the motions\n" + "\n" + "This function can be used to read past a pickle without instantiating\n" + "any objects or importing any modules. It can also be used to find all\n" + "persistent references without instantiating any objects or importing\n" + "any modules.\n") + }, + {NULL, NULL} /* sentinel */ +}; + + +static Unpicklerobject * +newUnpicklerobject(PyObject *f) +{ + Unpicklerobject *self; + + if (!( self = PyObject_GC_New(Unpicklerobject, &Unpicklertype))) + return NULL; + + self->file = NULL; + self->arg = NULL; + self->stack = (Pdata*)Pdata_New(); + self->pers_func = NULL; + self->last_string = NULL; + self->marks = NULL; + self->num_marks = 0; + self->marks_size = 0; + self->buf_size = 0; + self->read = NULL; + self->readline = NULL; + self->find_class = NULL; + + if (!( self->memo = PyDict_New())) + goto err; + + Py_INCREF(f); + self->file = f; + + /* Set read, readline based on type of f */ + if (PyFile_Check(f)) { + self->fp = PyFile_AsFile(f); + if (self->fp == NULL) { + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + goto err; + } + self->read_func = read_file; + self->readline_func = readline_file; + } + else if (PycStringIO_InputCheck(f)) { + self->fp = NULL; + self->read_func = read_cStringIO; + self->readline_func = readline_cStringIO; + } + else { + + self->fp = NULL; + self->read_func = read_other; + self->readline_func = readline_other; + + if (!( (self->readline = PyObject_GetAttr(f, readline_str)) && + (self->read = PyObject_GetAttr(f, read_str)))) { + PyErr_Clear(); + PyErr_SetString( PyExc_TypeError, + "argument must have 'read' and " + "'readline' attributes" ); + goto err; + } + } + PyObject_GC_Track(self); + + return self; + + err: + Py_DECREF((PyObject *)self); + return NULL; +} + + +static PyObject * +get_Unpickler(PyObject *self, PyObject *args) +{ + PyObject *file; + + if (!( PyArg_ParseTuple(args, "O:Unpickler", &file))) + return NULL; + return (PyObject *)newUnpicklerobject(file); +} + + +static void +Unpickler_dealloc(Unpicklerobject *self) +{ + PyObject_GC_UnTrack((PyObject *)self); + Py_XDECREF(self->readline); + Py_XDECREF(self->read); + Py_XDECREF(self->file); + Py_XDECREF(self->memo); + Py_XDECREF(self->stack); + Py_XDECREF(self->pers_func); + Py_XDECREF(self->arg); + Py_XDECREF(self->last_string); + Py_XDECREF(self->find_class); + + if (self->marks) { + free(self->marks); + } + + if (self->buf_size) { + free(self->buf); + } + + self->ob_type->tp_free((PyObject *)self); +} + +static int +Unpickler_traverse(Unpicklerobject *self, visitproc visit, void *arg) +{ + int err; + +#define VISIT(SLOT) \ + if (SLOT) { \ + err = visit((PyObject *)(SLOT), arg); \ + if (err) \ + return err; \ + } + VISIT(self->readline); + VISIT(self->read); + VISIT(self->file); + VISIT(self->memo); + VISIT(self->stack); + VISIT(self->pers_func); + VISIT(self->arg); + VISIT(self->last_string); + VISIT(self->find_class); +#undef VISIT + return 0; +} + +static int +Unpickler_clear(Unpicklerobject *self) +{ +#define CLEAR(SLOT) Py_XDECREF(SLOT); SLOT = NULL + CLEAR(self->readline); + CLEAR(self->read); + CLEAR(self->file); + CLEAR(self->memo); + CLEAR(self->stack); + CLEAR(self->pers_func); + CLEAR(self->arg); + CLEAR(self->last_string); + CLEAR(self->find_class); +#undef CLEAR + return 0; +} + +static PyObject * +Unpickler_getattr(Unpicklerobject *self, char *name) +{ + if (!strcmp(name, "persistent_load")) { + if (!self->pers_func) { + PyErr_SetString(PyExc_AttributeError, name); + return NULL; + } + + Py_INCREF(self->pers_func); + return self->pers_func; + } + + if (!strcmp(name, "find_global")) { + if (!self->find_class) { + PyErr_SetString(PyExc_AttributeError, name); + return NULL; + } + + Py_INCREF(self->find_class); + return self->find_class; + } + + if (!strcmp(name, "memo")) { + if (!self->memo) { + PyErr_SetString(PyExc_AttributeError, name); + return NULL; + } + + Py_INCREF(self->memo); + return self->memo; + } + + if (!strcmp(name, "UnpicklingError")) { + Py_INCREF(UnpicklingError); + return UnpicklingError; + } + + return Py_FindMethod(Unpickler_methods, (PyObject *)self, name); +} + + +static int +Unpickler_setattr(Unpicklerobject *self, char *name, PyObject *value) +{ + + if (!strcmp(name, "persistent_load")) { + Py_XDECREF(self->pers_func); + self->pers_func = value; + Py_XINCREF(value); + return 0; + } + + if (!strcmp(name, "find_global")) { + Py_XDECREF(self->find_class); + self->find_class = value; + Py_XINCREF(value); + return 0; + } + + if (! value) { + PyErr_SetString(PyExc_TypeError, + "attribute deletion is not supported"); + return -1; + } + + if (strcmp(name, "memo") == 0) { + if (!PyDict_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "memo must be a dictionary"); + return -1; + } + Py_XDECREF(self->memo); + self->memo = value; + Py_INCREF(value); + return 0; + } + + PyErr_SetString(PyExc_AttributeError, name); + return -1; +} + +/* --------------------------------------------------------------------------- + * Module-level functions. + */ + +/* dump(obj, file, proto=0). */ +static PyObject * +cpm_dump(PyObject *self, PyObject *args) +{ + PyObject *ob, *file, *res = NULL; + Picklerobject *pickler = 0; + int proto = 0; + + if (!( PyArg_ParseTuple(args, "OO|i", &ob, &file, &proto))) + goto finally; + + if (!( pickler = newPicklerobject(file, proto))) + goto finally; + + if (dump(pickler, ob) < 0) + goto finally; + + Py_INCREF(Py_None); + res = Py_None; + + finally: + Py_XDECREF(pickler); + + return res; +} + + +/* dumps(obj, proto=0). */ +static PyObject * +cpm_dumps(PyObject *self, PyObject *args) +{ + PyObject *ob, *file = 0, *res = NULL; + Picklerobject *pickler = 0; + int proto = 0; + + if (!( PyArg_ParseTuple(args, "O|i:dumps", &ob, &proto))) + goto finally; + + if (!( file = PycStringIO->NewOutput(128))) + goto finally; + + if (!( pickler = newPicklerobject(file, proto))) + goto finally; + + if (dump(pickler, ob) < 0) + goto finally; + + res = PycStringIO->cgetvalue(file); + + finally: + Py_XDECREF(pickler); + Py_XDECREF(file); + + return res; +} + + +/* load(fileobj). */ +static PyObject * +cpm_load(PyObject *self, PyObject *args) +{ + Unpicklerobject *unpickler = 0; + PyObject *ob, *res = NULL; + + if (!( PyArg_ParseTuple(args, "O:load", &ob))) + goto finally; + + if (!( unpickler = newUnpicklerobject(ob))) + goto finally; + + res = load(unpickler); + + finally: + Py_XDECREF(unpickler); + + return res; +} + + +/* loads(string) */ +static PyObject * +cpm_loads(PyObject *self, PyObject *args) +{ + PyObject *ob, *file = 0, *res = NULL; + Unpicklerobject *unpickler = 0; + + if (!( PyArg_ParseTuple(args, "S:loads", &ob))) + goto finally; + + if (!( file = PycStringIO->NewInput(ob))) + goto finally; + + if (!( unpickler = newUnpicklerobject(file))) + goto finally; + + res = load(unpickler); + + finally: + Py_XDECREF(file); + Py_XDECREF(unpickler); + + return res; +} + + +PyDoc_STRVAR(Unpicklertype__doc__, +"Objects that know how to unpickle"); + +static PyTypeObject Unpicklertype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "cPickle.Unpickler", /*tp_name*/ + sizeof(Unpicklerobject), /*tp_basicsize*/ + 0, + (destructor)Unpickler_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc)Unpickler_getattr, /* tp_getattr */ + (setattrfunc)Unpickler_setattr, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + Unpicklertype__doc__, /* tp_doc */ + (traverseproc)Unpickler_traverse, /* tp_traverse */ + (inquiry)Unpickler_clear, /* tp_clear */ +}; + +static struct PyMethodDef cPickle_methods[] = { + {"dump", (PyCFunction)cpm_dump, METH_VARARGS, + PyDoc_STR("dump(object, file, proto=0) -- " + "Write an object in pickle format to the given file.\n" + "\n" + "See the Pickler docstring for the meaning of optional argument proto.") + }, + + {"dumps", (PyCFunction)cpm_dumps, METH_VARARGS, + PyDoc_STR("dumps(object, proto=0) -- " + "Return a string containing an object in pickle format.\n" + "\n" + "See the Pickler docstring for the meaning of optional argument proto.") + }, + + {"load", (PyCFunction)cpm_load, METH_VARARGS, + PyDoc_STR("load(file) -- Load a pickle from the given file")}, + + {"loads", (PyCFunction)cpm_loads, METH_VARARGS, + PyDoc_STR("loads(string) -- Load a pickle from the given string")}, + + {"Pickler", (PyCFunction)get_Pickler, METH_VARARGS, + PyDoc_STR("Pickler(file, proto=0) -- Create a pickler.\n" + "\n" + "This takes a file-like object for writing a pickle data stream.\n" + "The optional proto argument tells the pickler to use the given\n" + "protocol; supported protocols are 0, 1, 2. The default\n" + "protocol is 0, to be backwards compatible. (Protocol 0 is the\n" + "only protocol that can be written to a file opened in text\n" + "mode and read back successfully. When using a protocol higher\n" + "than 0, make sure the file is opened in binary mode, both when\n" + "pickling and unpickling.)\n" + "\n" + "Protocol 1 is more efficient than protocol 0; protocol 2 is\n" + "more efficient than protocol 1.\n" + "\n" + "Specifying a negative protocol version selects the highest\n" + "protocol version supported. The higher the protocol used, the\n" + "more recent the version of Python needed to read the pickle\n" + "produced.\n" + "\n" + "The file parameter must have a write() method that accepts a single\n" + "string argument. It can thus be an open file object, a StringIO\n" + "object, or any other custom object that meets this interface.\n") + }, + + {"Unpickler", (PyCFunction)get_Unpickler, METH_VARARGS, + PyDoc_STR("Unpickler(file) -- Create an unpickler.")}, + + { NULL, NULL } +}; + +static int +init_stuff(PyObject *module_dict) +{ + PyObject *copy_reg, *t, *r; + +#define INIT_STR(S) if (!( S ## _str=PyString_InternFromString(#S))) return -1; + + if (PyType_Ready(&Unpicklertype) < 0) + return -1; + if (PyType_Ready(&Picklertype) < 0) + return -1; + + INIT_STR(__class__); + INIT_STR(__getinitargs__); + INIT_STR(__dict__); + INIT_STR(__getstate__); + INIT_STR(__setstate__); + INIT_STR(__name__); + INIT_STR(__main__); + INIT_STR(__reduce__); + INIT_STR(__reduce_ex__); + INIT_STR(write); + INIT_STR(append); + INIT_STR(read); + INIT_STR(readline); + INIT_STR(copy_reg); + INIT_STR(dispatch_table); + INIT_STR(__basicnew__); + + if (!( copy_reg = PyImport_ImportModule("copy_reg"))) + return -1; + + /* This is special because we want to use a different + one in restricted mode. */ + dispatch_table = PyObject_GetAttr(copy_reg, dispatch_table_str); + if (!dispatch_table) return -1; + + extension_registry = PyObject_GetAttrString(copy_reg, + "_extension_registry"); + if (!extension_registry) return -1; + + inverted_registry = PyObject_GetAttrString(copy_reg, + "_inverted_registry"); + if (!inverted_registry) return -1; + + extension_cache = PyObject_GetAttrString(copy_reg, + "_extension_cache"); + if (!extension_cache) return -1; + + Py_DECREF(copy_reg); + + if (!(empty_tuple = PyTuple_New(0))) + return -1; + + two_tuple = PyTuple_New(2); + if (two_tuple == NULL) + return -1; + /* We use this temp container with no regard to refcounts, or to + * keeping containees alive. Exempt from GC, because we don't + * want anything looking at two_tuple() by magic. + */ + PyObject_GC_UnTrack(two_tuple); + + /* Ugh */ + if (!( t=PyImport_ImportModule("__builtin__"))) return -1; + if (PyDict_SetItemString(module_dict, "__builtins__", t) < 0) + return -1; + + if (!( t=PyDict_New())) return -1; + if (!( r=PyRun_String( + "def __init__(self, *args): self.args=args\n\n" + "def __str__(self):\n" + " return self.args and ('%s' % self.args[0]) or '(what)'\n", + Py_file_input, + module_dict, t) )) return -1; + Py_DECREF(r); + + PickleError = PyErr_NewException("cPickle.PickleError", NULL, t); + if (!PickleError) + return -1; + + Py_DECREF(t); + + PicklingError = PyErr_NewException("cPickle.PicklingError", + PickleError, NULL); + if (!PicklingError) + return -1; + + if (!( t=PyDict_New())) return -1; + if (!( r=PyRun_String( + "def __init__(self, *args): self.args=args\n\n" + "def __str__(self):\n" + " a=self.args\n" + " a=a and type(a[0]) or '(what)'\n" + " return 'Cannot pickle %s objects' % a\n" + , Py_file_input, + module_dict, t) )) return -1; + Py_DECREF(r); + + if (!( UnpickleableError = PyErr_NewException( + "cPickle.UnpickleableError", PicklingError, t))) + return -1; + + Py_DECREF(t); + + if (!( UnpicklingError = PyErr_NewException("cPickle.UnpicklingError", + PickleError, NULL))) + return -1; + + if (!( BadPickleGet = PyErr_NewException("cPickle.BadPickleGet", + UnpicklingError, NULL))) + return -1; + + if (PyDict_SetItemString(module_dict, "PickleError", + PickleError) < 0) + return -1; + + if (PyDict_SetItemString(module_dict, "PicklingError", + PicklingError) < 0) + return -1; + + if (PyDict_SetItemString(module_dict, "UnpicklingError", + UnpicklingError) < 0) + return -1; + + if (PyDict_SetItemString(module_dict, "UnpickleableError", + UnpickleableError) < 0) + return -1; + + if (PyDict_SetItemString(module_dict, "BadPickleGet", + BadPickleGet) < 0) + return -1; + + PycString_IMPORT; + + return 0; +} + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC +initcPickle(void) +{ + PyObject *m, *d, *di, *v, *k; + int i; + char *rev = "1.71"; /* XXX when does this change? */ + PyObject *format_version; + PyObject *compatible_formats; + + Picklertype.ob_type = &PyType_Type; + Unpicklertype.ob_type = &PyType_Type; + PdataType.ob_type = &PyType_Type; + + /* Initialize some pieces. We need to do this before module creation, + * so we're forced to use a temporary dictionary. :( + */ + di = PyDict_New(); + if (!di) return; + if (init_stuff(di) < 0) return; + + /* Create the module and add the functions */ + m = Py_InitModule4("cPickle", cPickle_methods, + cPickle_module_documentation, + (PyObject*)NULL,PYTHON_API_VERSION); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + v = PyString_FromString(rev); + PyDict_SetItemString(d, "__version__", v); + Py_XDECREF(v); + + /* Copy data from di. Waaa. */ + for (i=0; PyDict_Next(di, &i, &k, &v); ) { + if (PyObject_SetItem(d, k, v) < 0) { + Py_DECREF(di); + return; + } + } + Py_DECREF(di); + + i = PyModule_AddIntConstant(m, "HIGHEST_PROTOCOL", HIGHEST_PROTOCOL); + if (i < 0) + return; + + /* These are purely informational; no code uses them. */ + /* File format version we write. */ + format_version = PyString_FromString("2.0"); + /* Format versions we can read. */ + compatible_formats = Py_BuildValue("[sssss]", + "1.0", /* Original protocol 0 */ + "1.1", /* Protocol 0 + INST */ + "1.2", /* Original protocol 1 */ + "1.3", /* Protocol 1 + BINFLOAT */ + "2.0"); /* Original protocol 2 */ + PyDict_SetItemString(d, "format_version", format_version); + PyDict_SetItemString(d, "compatible_formats", compatible_formats); + Py_XDECREF(format_version); + Py_XDECREF(compatible_formats); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cStringIO.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cStringIO.c new file mode 100644 index 00000000..ba2bfa97 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cStringIO.c @@ -0,0 +1,731 @@ + +#include "Python.h" +#include "import.h" +#include "cStringIO.h" +#include "structmember.h" + +PyDoc_STRVAR(cStringIO_module_documentation, +"A simple fast partial StringIO replacement.\n" +"\n" +"This module provides a simple useful replacement for\n" +"the StringIO module that is written in C. It does not provide the\n" +"full generality of StringIO, but it provides enough for most\n" +"applications and is especially useful in conjunction with the\n" +"pickle module.\n" +"\n" +"Usage:\n" +"\n" +" from cStringIO import StringIO\n" +"\n" +" an_output_stream=StringIO()\n" +" an_output_stream.write(some_stuff)\n" +" ...\n" +" value=an_output_stream.getvalue()\n" +"\n" +" an_input_stream=StringIO(a_string)\n" +" spam=an_input_stream.readline()\n" +" spam=an_input_stream.read(5)\n" +" an_input_stream.seek(0) # OK, start over\n" +" spam=an_input_stream.read() # and read it all\n" +" \n" +"If someone else wants to provide a more complete implementation,\n" +"go for it. :-) \n" +"\n" +"cStringIO.c,v 1.29 1999/06/15 14:10:27 jim Exp\n"); + +#define UNLESS(E) if (!(E)) + + +/* Declaration for file-like objects that manage data as strings + + The IOobject type should be though of as a common base type for + Iobjects, which provide input (read-only) StringIO objects and + Oobjects, which provide read-write objects. Most of the methods + depend only on common data. +*/ + +typedef struct { + PyObject_HEAD + char *buf; + int pos, string_size; +} IOobject; + +#define IOOOBJECT(O) ((IOobject*)(O)) + +/* Declarations for objects of type StringO */ + +typedef struct { /* Subtype of IOobject */ + PyObject_HEAD + char *buf; + int pos, string_size; + + int buf_size, softspace; +} Oobject; + +/* Declarations for objects of type StringI */ + +typedef struct { /* Subtype of IOobject */ + PyObject_HEAD + char *buf; + int pos, string_size; + /* We store a reference to the object here in order to keep + the buffer alive during the lifetime of the Iobject. */ + PyObject *pbuf; +} Iobject; + +/* IOobject (common) methods */ + +PyDoc_STRVAR(IO_flush__doc__, "flush(): does nothing."); + +static int +IO__opencheck(IOobject *self) { + UNLESS (self->buf) { + PyErr_SetString(PyExc_ValueError, + "I/O operation on closed file"); + return 0; + } + return 1; +} + +static PyObject * +IO_get_closed(IOobject *self, void *closure) +{ + PyObject *result = Py_False; + + if (self->buf == NULL) + result = Py_True; + Py_INCREF(result); + return result; +} + +static PyGetSetDef file_getsetlist[] = { + {"closed", (getter)IO_get_closed, NULL, "True if the file is closed"}, + {0}, +}; + +static PyObject * +IO_flush(IOobject *self, PyObject *unused) { + + UNLESS (IO__opencheck(self)) return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(IO_getval__doc__, +"getvalue([use_pos]) -- Get the string value." +"\n" +"If use_pos is specified and is a true value, then the string returned\n" +"will include only the text up to the current file position.\n"); + +static PyObject * +IO_cgetval(PyObject *self) { + UNLESS (IO__opencheck(IOOOBJECT(self))) return NULL; + return PyString_FromStringAndSize(((IOobject*)self)->buf, + ((IOobject*)self)->pos); +} + +static PyObject * +IO_getval(IOobject *self, PyObject *args) { + PyObject *use_pos=Py_None; + int s; + + UNLESS (IO__opencheck(self)) return NULL; + UNLESS (PyArg_UnpackTuple(args,"getval", 0, 1,&use_pos)) return NULL; + + if (PyObject_IsTrue(use_pos)) { + s=self->pos; + if (s > self->string_size) s=self->string_size; + } + else + s=self->string_size; + return PyString_FromStringAndSize(self->buf, s); +} + +PyDoc_STRVAR(IO_isatty__doc__, "isatty(): always returns 0"); + +static PyObject * +IO_isatty(IOobject *self, PyObject *unused) { + Py_INCREF(Py_False); + return Py_False; +} + +PyDoc_STRVAR(IO_read__doc__, +"read([s]) -- Read s characters, or the rest of the string"); + +static int +IO_cread(PyObject *self, char **output, int n) { + int l; + + UNLESS (IO__opencheck(IOOOBJECT(self))) return -1; + l = ((IOobject*)self)->string_size - ((IOobject*)self)->pos; + if (n < 0 || n > l) { + n = l; + if (n < 0) n=0; + } + + *output=((IOobject*)self)->buf + ((IOobject*)self)->pos; + ((IOobject*)self)->pos += n; + return n; +} + +static PyObject * +IO_read(IOobject *self, PyObject *args) { + int n = -1; + char *output; + + UNLESS (PyArg_ParseTuple(args, "|i:read", &n)) return NULL; + + if ( (n=IO_cread((PyObject*)self,&output,n)) < 0) return NULL; + + return PyString_FromStringAndSize(output, n); +} + +PyDoc_STRVAR(IO_readline__doc__, "readline() -- Read one line"); + +static int +IO_creadline(PyObject *self, char **output) { + char *n, *s; + int l; + + UNLESS (IO__opencheck(IOOOBJECT(self))) return -1; + + for (n = ((IOobject*)self)->buf + ((IOobject*)self)->pos, + s = ((IOobject*)self)->buf + ((IOobject*)self)->string_size; + n < s && *n != '\n'; n++); + if (n < s) n++; + + *output=((IOobject*)self)->buf + ((IOobject*)self)->pos; + l = n - ((IOobject*)self)->buf - ((IOobject*)self)->pos; + ((IOobject*)self)->pos += l; + return l; +} + +static PyObject * +IO_readline(IOobject *self, PyObject *args) { + int n, m=-1; + char *output; + + if (args) + UNLESS (PyArg_ParseTuple(args, "|i:readline", &m)) return NULL; + + if( (n=IO_creadline((PyObject*)self,&output)) < 0) return NULL; + if (m >= 0 && m < n) { + m = n - m; + n -= m; + self->pos -= m; + } + return PyString_FromStringAndSize(output, n); +} + +PyDoc_STRVAR(IO_readlines__doc__, "readlines() -- Read all lines"); + +static PyObject * +IO_readlines(IOobject *self, PyObject *args) { + int n; + char *output; + PyObject *result, *line; + int hint = 0, length = 0; + + UNLESS (PyArg_ParseTuple(args, "|i:readlines", &hint)) return NULL; + + result = PyList_New(0); + if (!result) + return NULL; + + while (1){ + if ( (n = IO_creadline((PyObject*)self,&output)) < 0) + goto err; + if (n == 0) + break; + line = PyString_FromStringAndSize (output, n); + if (!line) + goto err; + PyList_Append (result, line); + Py_DECREF (line); + length += n; + if (hint > 0 && length >= hint) + break; + } + return result; + err: + Py_DECREF(result); + return NULL; +} + +PyDoc_STRVAR(IO_reset__doc__, +"reset() -- Reset the file position to the beginning"); + +static PyObject * +IO_reset(IOobject *self, PyObject *unused) { + + UNLESS (IO__opencheck(self)) return NULL; + + self->pos = 0; + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(IO_tell__doc__, "tell() -- get the current position."); + +static PyObject * +IO_tell(IOobject *self, PyObject *unused) { + + UNLESS (IO__opencheck(self)) return NULL; + + return PyInt_FromLong(self->pos); +} + +PyDoc_STRVAR(IO_truncate__doc__, +"truncate(): truncate the file at the current position."); + +static PyObject * +IO_truncate(IOobject *self, PyObject *args) { + int pos = -1; + + UNLESS (IO__opencheck(self)) return NULL; + UNLESS (PyArg_ParseTuple(args, "|i:truncate", &pos)) return NULL; + if (pos < 0) pos = self->pos; + + if (self->string_size > pos) self->string_size = pos; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +IO_iternext(Iobject *self) +{ + PyObject *next; + next = IO_readline((IOobject *)self, NULL); + if (!next) + return NULL; + if (!PyString_GET_SIZE(next)) { + Py_DECREF(next); + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + return next; +} + + + + +/* Read-write object methods */ + +PyDoc_STRVAR(O_seek__doc__, +"seek(position) -- set the current position\n" +"seek(position, mode) -- mode 0: absolute; 1: relative; 2: relative to EOF"); + +static PyObject * +O_seek(Oobject *self, PyObject *args) { + int position, mode = 0; + + UNLESS (IO__opencheck(IOOOBJECT(self))) return NULL; + UNLESS (PyArg_ParseTuple(args, "i|i:seek", &position, &mode)) + return NULL; + + if (mode == 2) { + position += self->string_size; + } + else if (mode == 1) { + position += self->pos; + } + + if (position > self->buf_size) { + self->buf_size*=2; + if (self->buf_size <= position) self->buf_size=position+1; + UNLESS (self->buf=(char*) + realloc(self->buf,self->buf_size*sizeof(char))) { + self->buf_size=self->pos=0; + return PyErr_NoMemory(); + } + } + else if (position < 0) position=0; + + self->pos=position; + + while (--position >= self->string_size) self->buf[position]=0; + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(O_write__doc__, +"write(s) -- Write a string to the file" +"\n\nNote (hack:) writing None resets the buffer"); + + +static int +O_cwrite(PyObject *self, char *c, int l) { + int newl; + Oobject *oself; + + UNLESS (IO__opencheck(IOOOBJECT(self))) return -1; + oself = (Oobject *)self; + + newl = oself->pos+l; + if (newl >= oself->buf_size) { + oself->buf_size *= 2; + if (oself->buf_size <= newl) + oself->buf_size = newl+1; + UNLESS (oself->buf = + (char*)realloc(oself->buf, + (oself->buf_size) * sizeof(char))) { + PyErr_SetString(PyExc_MemoryError,"out of memory"); + oself->buf_size = oself->pos = 0; + return -1; + } + } + + memcpy(oself->buf+oself->pos,c,l); + + oself->pos += l; + + if (oself->string_size < oself->pos) { + oself->string_size = oself->pos; + } + + return l; +} + +static PyObject * +O_write(Oobject *self, PyObject *args) { + char *c; + int l; + + UNLESS (PyArg_ParseTuple(args, "t#:write", &c, &l)) return NULL; + + if (O_cwrite((PyObject*)self,c,l) < 0) return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(O_close__doc__, "close(): explicitly release resources held."); + +static PyObject * +O_close(Oobject *self, PyObject *unused) { + if (self->buf != NULL) free(self->buf); + self->buf = NULL; + + self->pos = self->string_size = self->buf_size = 0; + + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(O_writelines__doc__, +"writelines(sequence_of_strings): write each string"); +static PyObject * +O_writelines(Oobject *self, PyObject *args) { + PyObject *tmp = 0; + static PyObject *joiner = NULL; + + if (!joiner) { + PyObject *empty_string = PyString_FromString(""); + if (empty_string == NULL) + return NULL; + joiner = PyObject_GetAttrString(empty_string, "join"); + Py_DECREF(empty_string); + if (joiner == NULL) + return NULL; + } + + if (PyObject_Size(args) < 0) return NULL; + + tmp = PyObject_CallFunction(joiner, "O", args); + UNLESS (tmp) return NULL; + + args = Py_BuildValue("(O)", tmp); + Py_DECREF(tmp); + UNLESS (args) return NULL; + + tmp = O_write(self, args); + Py_DECREF(args); + return tmp; +} + +static struct PyMethodDef O_methods[] = { + /* Common methods: */ + {"flush", (PyCFunction)IO_flush, METH_NOARGS, IO_flush__doc__}, + {"getvalue", (PyCFunction)IO_getval, METH_VARARGS, IO_getval__doc__}, + {"isatty", (PyCFunction)IO_isatty, METH_NOARGS, IO_isatty__doc__}, + {"read", (PyCFunction)IO_read, METH_VARARGS, IO_read__doc__}, + {"readline", (PyCFunction)IO_readline, METH_VARARGS, IO_readline__doc__}, + {"readlines", (PyCFunction)IO_readlines,METH_VARARGS, IO_readlines__doc__}, + {"reset", (PyCFunction)IO_reset, METH_NOARGS, IO_reset__doc__}, + {"tell", (PyCFunction)IO_tell, METH_NOARGS, IO_tell__doc__}, + {"truncate", (PyCFunction)IO_truncate, METH_VARARGS, IO_truncate__doc__}, + + /* Read-write StringIO specific methods: */ + {"close", (PyCFunction)O_close, METH_NOARGS, O_close__doc__}, + {"seek", (PyCFunction)O_seek, METH_VARARGS, O_seek__doc__}, + {"write", (PyCFunction)O_write, METH_VARARGS, O_write__doc__}, + {"writelines", (PyCFunction)O_writelines, METH_O, O_writelines__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyMemberDef O_memberlist[] = { + {"softspace", T_INT, offsetof(Oobject, softspace), 0, + "flag indicating that a space needs to be printed; used by print"}, + /* getattr(f, "closed") is implemented without this table */ + {NULL} /* Sentinel */ +}; + +static void +O_dealloc(Oobject *self) { + if (self->buf != NULL) + free(self->buf); + PyObject_Del(self); +} + +PyDoc_STRVAR(Otype__doc__, "Simple type for output to strings."); + +static PyTypeObject Otype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "cStringIO.StringO", /*tp_name*/ + sizeof(Oobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)O_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + 0, /*tp_getattr */ + 0, /*tp_setattr */ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + 0, /*tp_getattro */ + 0, /*tp_setattro */ + 0, /*tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + Otype__doc__, /*tp_doc */ + 0, /*tp_traverse */ + 0, /*tp_clear */ + 0, /*tp_richcompare */ + 0, /*tp_weaklistoffset */ + PyObject_SelfIter, /*tp_iter */ + (iternextfunc)IO_iternext, /*tp_iternext */ + O_methods, /*tp_methods */ + O_memberlist, /*tp_members */ + file_getsetlist, /*tp_getset */ +}; + +static PyObject * +newOobject(int size) { + Oobject *self; + + self = PyObject_New(Oobject, &Otype); + if (self == NULL) + return NULL; + self->pos=0; + self->string_size = 0; + self->softspace = 0; + + UNLESS (self->buf=malloc(size*sizeof(char))) { + PyErr_SetString(PyExc_MemoryError,"out of memory"); + self->buf_size = 0; + return NULL; + } + + self->buf_size=size; + return (PyObject*)self; +} + +/* End of code for StringO objects */ +/* -------------------------------------------------------- */ + +static PyObject * +I_close(Iobject *self, PyObject *unused) { + Py_XDECREF(self->pbuf); + self->pbuf = NULL; + self->buf = NULL; + + self->pos = self->string_size = 0; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +I_seek(Iobject *self, PyObject *args) { + int position, mode = 0; + + UNLESS (IO__opencheck(IOOOBJECT(self))) return NULL; + UNLESS (PyArg_ParseTuple(args, "i|i:seek", &position, &mode)) + return NULL; + + if (mode == 2) position += self->string_size; + else if (mode == 1) position += self->pos; + + if (position < 0) position=0; + + self->pos=position; + + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef I_methods[] = { + /* Common methods: */ + {"flush", (PyCFunction)IO_flush, METH_NOARGS, IO_flush__doc__}, + {"getvalue", (PyCFunction)IO_getval, METH_VARARGS, IO_getval__doc__}, + {"isatty", (PyCFunction)IO_isatty, METH_NOARGS, IO_isatty__doc__}, + {"read", (PyCFunction)IO_read, METH_VARARGS, IO_read__doc__}, + {"readline", (PyCFunction)IO_readline, METH_VARARGS, IO_readline__doc__}, + {"readlines", (PyCFunction)IO_readlines,METH_VARARGS, IO_readlines__doc__}, + {"reset", (PyCFunction)IO_reset, METH_NOARGS, IO_reset__doc__}, + {"tell", (PyCFunction)IO_tell, METH_NOARGS, IO_tell__doc__}, + {"truncate", (PyCFunction)IO_truncate, METH_VARARGS, IO_truncate__doc__}, + + /* Read-only StringIO specific methods: */ + {"close", (PyCFunction)I_close, METH_NOARGS, O_close__doc__}, + {"seek", (PyCFunction)I_seek, METH_VARARGS, O_seek__doc__}, + {NULL, NULL} +}; + +static void +I_dealloc(Iobject *self) { + Py_XDECREF(self->pbuf); + PyObject_Del(self); +} + + +PyDoc_STRVAR(Itype__doc__, +"Simple type for treating strings as input file streams"); + +static PyTypeObject Itype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "cStringIO.StringI", /*tp_name*/ + sizeof(Iobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)I_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + 0, /* tp_getattr */ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + Itype__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)IO_iternext, /* tp_iternext */ + I_methods, /* tp_methods */ + 0, /* tp_members */ + file_getsetlist, /* tp_getset */ +}; + +static PyObject * +newIobject(PyObject *s) { + Iobject *self; + char *buf; + int size; + + if (PyObject_AsReadBuffer(s, (const void **)&buf, &size)) { + PyErr_Format(PyExc_TypeError, "expected read buffer, %.200s found", + s->ob_type->tp_name); + return NULL; + } + UNLESS (self = PyObject_New(Iobject, &Itype)) return NULL; + Py_INCREF(s); + self->buf=buf; + self->string_size=size; + self->pbuf=s; + self->pos=0; + + return (PyObject*)self; +} + +/* End of code for StringI objects */ +/* -------------------------------------------------------- */ + + +PyDoc_STRVAR(IO_StringIO__doc__, +"StringIO([s]) -- Return a StringIO-like stream for reading or writing"); + +static PyObject * +IO_StringIO(PyObject *self, PyObject *args) { + PyObject *s=0; + + if (!PyArg_UnpackTuple(args, "StringIO", 0, 1, &s)) return NULL; + + if (s) return newIobject(s); + return newOobject(128); +} + +/* List of methods defined in the module */ + +static struct PyMethodDef IO_methods[] = { + {"StringIO", (PyCFunction)IO_StringIO, + METH_VARARGS, IO_StringIO__doc__}, + {NULL, NULL} /* sentinel */ +}; + + +/* Initialization function for the module (*must* be called initcStringIO) */ + +static struct PycStringIO_CAPI CAPI = { + IO_cread, + IO_creadline, + O_cwrite, + IO_cgetval, + newOobject, + newIobject, + &Itype, + &Otype, +}; + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC +initcStringIO(void) { + PyObject *m, *d, *v; + + + /* Create the module and add the functions */ + m = Py_InitModule4("cStringIO", IO_methods, + cStringIO_module_documentation, + (PyObject*)NULL,PYTHON_API_VERSION); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + + /* Export C API */ + Itype.ob_type=&PyType_Type; + Otype.ob_type=&PyType_Type; + if (PyType_Ready(&Otype) < 0) return; + if (PyType_Ready(&Itype) < 0) return; + PyDict_SetItemString(d,"cStringIO_CAPI", + v = PyCObject_FromVoidPtr(&CAPI,NULL)); + Py_XDECREF(v); + + /* Export Types */ + PyDict_SetItemString(d,"InputType", (PyObject*)&Itype); + PyDict_SetItemString(d,"OutputType", (PyObject*)&Otype); + + /* Maybe make certain warnings go away */ + if (0) PycString_IMPORT; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ccpython.cc b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ccpython.cc new file mode 100644 index 00000000..6f5fb159 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ccpython.cc @@ -0,0 +1,11 @@ +/* Minimal main program -- everything is loaded from the library */ + +#include "Python.h" + +extern "C" +DL_EXPORT(int) Py_Main( int argc, char *argv[] ); + +int main( int argc, char *argv[] ) +{ + return Py_Main(argc, argv); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cdmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cdmodule.c new file mode 100644 index 00000000..a39f92ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cdmodule.c @@ -0,0 +1,794 @@ +/* CD module -- interface to Mark Callow's and Roger Chickering's */ + /* CD Audio Library (CD). */ + +#include +#include +#include "Python.h" + +#define NCALLBACKS 8 + +typedef struct { + PyObject_HEAD + CDPLAYER *ob_cdplayer; +} cdplayerobject; + +static PyObject *CdError; /* exception cd.error */ + +static PyObject * +CD_allowremoval(cdplayerobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":allowremoval")) + return NULL; + + CDallowremoval(self->ob_cdplayer); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_preventremoval(cdplayerobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":preventremoval")) + return NULL; + + CDpreventremoval(self->ob_cdplayer); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_bestreadsize(cdplayerobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":bestreadsize")) + return NULL; + + return PyInt_FromLong((long) CDbestreadsize(self->ob_cdplayer)); +} + +static PyObject * +CD_close(cdplayerobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":close")) + return NULL; + + if (!CDclose(self->ob_cdplayer)) { + PyErr_SetFromErrno(CdError); /* XXX - ??? */ + return NULL; + } + self->ob_cdplayer = NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_eject(cdplayerobject *self, PyObject *args) +{ + CDSTATUS status; + + if (!PyArg_ParseTuple(args, ":eject")) + return NULL; + + if (!CDeject(self->ob_cdplayer)) { + if (CDgetstatus(self->ob_cdplayer, &status) && + status.state == CD_NODISC) + PyErr_SetString(CdError, "no disc in player"); + else + PyErr_SetString(CdError, "eject failed"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_getstatus(cdplayerobject *self, PyObject *args) +{ + CDSTATUS status; + + if (!PyArg_ParseTuple(args, ":getstatus")) + return NULL; + + if (!CDgetstatus(self->ob_cdplayer, &status)) { + PyErr_SetFromErrno(CdError); /* XXX - ??? */ + return NULL; + } + + return Py_BuildValue("(ii(iii)(iii)(iii)iiii)", status.state, + status.track, status.min, status.sec, status.frame, + status.abs_min, status.abs_sec, status.abs_frame, + status.total_min, status.total_sec, status.total_frame, + status.first, status.last, status.scsi_audio, + status.cur_block); +} + +static PyObject * +CD_gettrackinfo(cdplayerobject *self, PyObject *args) +{ + int track; + CDTRACKINFO info; + CDSTATUS status; + + if (!PyArg_ParseTuple(args, "i:gettrackinfo", &track)) + return NULL; + + if (!CDgettrackinfo(self->ob_cdplayer, track, &info)) { + if (CDgetstatus(self->ob_cdplayer, &status) && + status.state == CD_NODISC) + PyErr_SetString(CdError, "no disc in player"); + else + PyErr_SetString(CdError, "gettrackinfo failed"); + return NULL; + } + + return Py_BuildValue("((iii)(iii))", + info.start_min, info.start_sec, info.start_frame, + info.total_min, info.total_sec, info.total_frame); +} + +static PyObject * +CD_msftoblock(cdplayerobject *self, PyObject *args) +{ + int min, sec, frame; + + if (!PyArg_ParseTuple(args, "iii:msftoblock", &min, &sec, &frame)) + return NULL; + + return PyInt_FromLong((long) CDmsftoblock(self->ob_cdplayer, + min, sec, frame)); +} + +static PyObject * +CD_play(cdplayerobject *self, PyObject *args) +{ + int start, play; + CDSTATUS status; + + if (!PyArg_ParseTuple(args, "ii:play", &start, &play)) + return NULL; + + if (!CDplay(self->ob_cdplayer, start, play)) { + if (CDgetstatus(self->ob_cdplayer, &status) && + status.state == CD_NODISC) + PyErr_SetString(CdError, "no disc in player"); + else + PyErr_SetString(CdError, "play failed"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_playabs(cdplayerobject *self, PyObject *args) +{ + int min, sec, frame, play; + CDSTATUS status; + + if (!PyArg_ParseTuple(args, "iiii:playabs", &min, &sec, &frame, &play)) + return NULL; + + if (!CDplayabs(self->ob_cdplayer, min, sec, frame, play)) { + if (CDgetstatus(self->ob_cdplayer, &status) && + status.state == CD_NODISC) + PyErr_SetString(CdError, "no disc in player"); + else + PyErr_SetString(CdError, "playabs failed"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_playtrack(cdplayerobject *self, PyObject *args) +{ + int start, play; + CDSTATUS status; + + if (!PyArg_ParseTuple(args, "ii:playtrack", &start, &play)) + return NULL; + + if (!CDplaytrack(self->ob_cdplayer, start, play)) { + if (CDgetstatus(self->ob_cdplayer, &status) && + status.state == CD_NODISC) + PyErr_SetString(CdError, "no disc in player"); + else + PyErr_SetString(CdError, "playtrack failed"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_playtrackabs(cdplayerobject *self, PyObject *args) +{ + int track, min, sec, frame, play; + CDSTATUS status; + + if (!PyArg_ParseTuple(args, "iiiii:playtrackabs", &track, &min, &sec, + &frame, &play)) + return NULL; + + if (!CDplaytrackabs(self->ob_cdplayer, track, min, sec, frame, play)) { + if (CDgetstatus(self->ob_cdplayer, &status) && + status.state == CD_NODISC) + PyErr_SetString(CdError, "no disc in player"); + else + PyErr_SetString(CdError, "playtrackabs failed"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_readda(cdplayerobject *self, PyObject *args) +{ + int numframes, n; + PyObject *result; + + if (!PyArg_ParseTuple(args, "i:readda", &numframes)) + return NULL; + + result = PyString_FromStringAndSize(NULL, numframes * sizeof(CDFRAME)); + if (result == NULL) + return NULL; + + n = CDreadda(self->ob_cdplayer, + (CDFRAME *) PyString_AsString(result), numframes); + if (n == -1) { + Py_DECREF(result); + PyErr_SetFromErrno(CdError); + return NULL; + } + if (n < numframes) + _PyString_Resize(&result, n * sizeof(CDFRAME)); + + return result; +} + +static PyObject * +CD_seek(cdplayerobject *self, PyObject *args) +{ + int min, sec, frame; + long PyTryBlock; + + if (!PyArg_ParseTuple(args, "iii:seek", &min, &sec, &frame)) + return NULL; + + PyTryBlock = CDseek(self->ob_cdplayer, min, sec, frame); + if (PyTryBlock == -1) { + PyErr_SetFromErrno(CdError); + return NULL; + } + + return PyInt_FromLong(PyTryBlock); +} + +static PyObject * +CD_seektrack(cdplayerobject *self, PyObject *args) +{ + int track; + long PyTryBlock; + + if (!PyArg_ParseTuple(args, "i:seektrack", &track)) + return NULL; + + PyTryBlock = CDseektrack(self->ob_cdplayer, track); + if (PyTryBlock == -1) { + PyErr_SetFromErrno(CdError); + return NULL; + } + + return PyInt_FromLong(PyTryBlock); +} + +static PyObject * +CD_seekblock(cdplayerobject *self, PyObject *args) +{ + unsigned long PyTryBlock; + + if (!PyArg_ParseTuple(args, "l:seekblock", &PyTryBlock)) + return NULL; + + PyTryBlock = CDseekblock(self->ob_cdplayer, PyTryBlock); + if (PyTryBlock == (unsigned long) -1) { + PyErr_SetFromErrno(CdError); + return NULL; + } + + return PyInt_FromLong(PyTryBlock); +} + +static PyObject * +CD_stop(cdplayerobject *self, PyObject *args) +{ + CDSTATUS status; + + if (!PyArg_ParseTuple(args, ":stop")) + return NULL; + + if (!CDstop(self->ob_cdplayer)) { + if (CDgetstatus(self->ob_cdplayer, &status) && + status.state == CD_NODISC) + PyErr_SetString(CdError, "no disc in player"); + else + PyErr_SetString(CdError, "stop failed"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_togglepause(cdplayerobject *self, PyObject *args) +{ + CDSTATUS status; + + if (!PyArg_ParseTuple(args, ":togglepause")) + return NULL; + + if (!CDtogglepause(self->ob_cdplayer)) { + if (CDgetstatus(self->ob_cdplayer, &status) && + status.state == CD_NODISC) + PyErr_SetString(CdError, "no disc in player"); + else + PyErr_SetString(CdError, "togglepause failed"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef cdplayer_methods[] = { + {"allowremoval", (PyCFunction)CD_allowremoval, METH_VARARGS}, + {"bestreadsize", (PyCFunction)CD_bestreadsize, METH_VARARGS}, + {"close", (PyCFunction)CD_close, METH_VARARGS}, + {"eject", (PyCFunction)CD_eject, METH_VARARGS}, + {"getstatus", (PyCFunction)CD_getstatus, METH_VARARGS}, + {"gettrackinfo", (PyCFunction)CD_gettrackinfo, METH_VARARGS}, + {"msftoblock", (PyCFunction)CD_msftoblock, METH_VARARGS}, + {"play", (PyCFunction)CD_play, METH_VARARGS}, + {"playabs", (PyCFunction)CD_playabs, METH_VARARGS}, + {"playtrack", (PyCFunction)CD_playtrack, METH_VARARGS}, + {"playtrackabs", (PyCFunction)CD_playtrackabs, METH_VARARGS}, + {"preventremoval", (PyCFunction)CD_preventremoval, METH_VARARGS}, + {"readda", (PyCFunction)CD_readda, METH_VARARGS}, + {"seek", (PyCFunction)CD_seek, METH_VARARGS}, + {"seekblock", (PyCFunction)CD_seekblock, METH_VARARGS}, + {"seektrack", (PyCFunction)CD_seektrack, METH_VARARGS}, + {"stop", (PyCFunction)CD_stop, METH_VARARGS}, + {"togglepause", (PyCFunction)CD_togglepause, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +static void +cdplayer_dealloc(cdplayerobject *self) +{ + if (self->ob_cdplayer != NULL) + CDclose(self->ob_cdplayer); + PyObject_Del(self); +} + +static PyObject * +cdplayer_getattr(cdplayerobject *self, char *name) +{ + if (self->ob_cdplayer == NULL) { + PyErr_SetString(PyExc_RuntimeError, "no player active"); + return NULL; + } + return Py_FindMethod(cdplayer_methods, (PyObject *)self, name); +} + +PyTypeObject CdPlayertype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "cd.cdplayer", /*tp_name*/ + sizeof(cdplayerobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)cdplayer_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)cdplayer_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + +static PyObject * +newcdplayerobject(CDPLAYER *cdp) +{ + cdplayerobject *p; + + p = PyObject_New(cdplayerobject, &CdPlayertype); + if (p == NULL) + return NULL; + p->ob_cdplayer = cdp; + return (PyObject *) p; +} + +static PyObject * +CD_open(PyObject *self, PyObject *args) +{ + char *dev, *direction; + CDPLAYER *cdp; + + /* + * Variable number of args. + * First defaults to "None", second defaults to "r". + */ + dev = NULL; + direction = "r"; + if (!PyArg_ParseTuple(args, "|zs:open", &dev, &direction)) + return NULL; + + cdp = CDopen(dev, direction); + if (cdp == NULL) { + PyErr_SetFromErrno(CdError); + return NULL; + } + + return newcdplayerobject(cdp); +} + +typedef struct { + PyObject_HEAD + CDPARSER *ob_cdparser; + struct { + PyObject *ob_cdcallback; + PyObject *ob_cdcallbackarg; + } ob_cdcallbacks[NCALLBACKS]; +} cdparserobject; + +static void +CD_callback(void *arg, CDDATATYPES type, void *data) +{ + PyObject *result, *args, *v = NULL; + char *p; + int i; + cdparserobject *self; + + self = (cdparserobject *) arg; + args = PyTuple_New(3); + if (args == NULL) + return; + Py_INCREF(self->ob_cdcallbacks[type].ob_cdcallbackarg); + PyTuple_SetItem(args, 0, self->ob_cdcallbacks[type].ob_cdcallbackarg); + PyTuple_SetItem(args, 1, PyInt_FromLong((long) type)); + switch (type) { + case cd_audio: + v = PyString_FromStringAndSize(data, CDDA_DATASIZE); + break; + case cd_pnum: + case cd_index: + v = PyInt_FromLong(((CDPROGNUM *) data)->value); + break; + case cd_ptime: + case cd_atime: +#define ptr ((struct cdtimecode *) data) + v = Py_BuildValue("(iii)", + ptr->mhi * 10 + ptr->mlo, + ptr->shi * 10 + ptr->slo, + ptr->fhi * 10 + ptr->flo); +#undef ptr + break; + case cd_catalog: + v = PyString_FromStringAndSize(NULL, 13); + p = PyString_AsString(v); + for (i = 0; i < 13; i++) + *p++ = ((char *) data)[i] + '0'; + break; + case cd_ident: +#define ptr ((struct cdident *) data) + v = PyString_FromStringAndSize(NULL, 12); + p = PyString_AsString(v); + CDsbtoa(p, ptr->country, 2); + p += 2; + CDsbtoa(p, ptr->owner, 3); + p += 3; + *p++ = ptr->year[0] + '0'; + *p++ = ptr->year[1] + '0'; + *p++ = ptr->serial[0] + '0'; + *p++ = ptr->serial[1] + '0'; + *p++ = ptr->serial[2] + '0'; + *p++ = ptr->serial[3] + '0'; + *p++ = ptr->serial[4] + '0'; +#undef ptr + break; + case cd_control: + v = PyInt_FromLong((long) *((unchar *) data)); + break; + } + PyTuple_SetItem(args, 2, v); + if (PyErr_Occurred()) { + Py_DECREF(args); + return; + } + + result = PyEval_CallObject(self->ob_cdcallbacks[type].ob_cdcallback, + args); + Py_DECREF(args); + Py_XDECREF(result); +} + +static PyObject * +CD_deleteparser(cdparserobject *self, PyObject *args) +{ + int i; + + if (!PyArg_ParseTuple(args, ":deleteparser")) + return NULL; + + CDdeleteparser(self->ob_cdparser); + self->ob_cdparser = NULL; + + /* no sense in keeping the callbacks, so remove them */ + for (i = 0; i < NCALLBACKS; i++) { + Py_XDECREF(self->ob_cdcallbacks[i].ob_cdcallback); + self->ob_cdcallbacks[i].ob_cdcallback = NULL; + Py_XDECREF(self->ob_cdcallbacks[i].ob_cdcallbackarg); + self->ob_cdcallbacks[i].ob_cdcallbackarg = NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_parseframe(cdparserobject *self, PyObject *args) +{ + char *cdfp; + int length; + CDFRAME *p; + + if (!PyArg_ParseTuple(args, "s#:parseframe", &cdfp, &length)) + return NULL; + + if (length % sizeof(CDFRAME) != 0) { + PyErr_SetString(PyExc_TypeError, "bad length"); + return NULL; + } + + p = (CDFRAME *) cdfp; + while (length > 0) { + CDparseframe(self->ob_cdparser, p); + length -= sizeof(CDFRAME); + p++; + if (PyErr_Occurred()) + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_removecallback(cdparserobject *self, PyObject *args) +{ + int type; + + if (!PyArg_ParseTuple(args, "i:removecallback", &type)) + return NULL; + + if (type < 0 || type >= NCALLBACKS) { + PyErr_SetString(PyExc_TypeError, "bad type"); + return NULL; + } + + CDremovecallback(self->ob_cdparser, (CDDATATYPES) type); + + Py_XDECREF(self->ob_cdcallbacks[type].ob_cdcallback); + self->ob_cdcallbacks[type].ob_cdcallback = NULL; + + Py_XDECREF(self->ob_cdcallbacks[type].ob_cdcallbackarg); + self->ob_cdcallbacks[type].ob_cdcallbackarg = NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_resetparser(cdparserobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":resetparser")) + return NULL; + + CDresetparser(self->ob_cdparser); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +CD_addcallback(cdparserobject *self, PyObject *args) +{ + int type; + PyObject *func, *funcarg; + + /* XXX - more work here */ + if (!PyArg_ParseTuple(args, "iOO:addcallback", &type, &func, &funcarg)) + return NULL; + + if (type < 0 || type >= NCALLBACKS) { + PyErr_SetString(PyExc_TypeError, "argument out of range"); + return NULL; + } + +#ifdef CDsetcallback + CDaddcallback(self->ob_cdparser, (CDDATATYPES) type, CD_callback, + (void *) self); +#else + CDsetcallback(self->ob_cdparser, (CDDATATYPES) type, CD_callback, + (void *) self); +#endif + Py_XDECREF(self->ob_cdcallbacks[type].ob_cdcallback); + Py_INCREF(func); + self->ob_cdcallbacks[type].ob_cdcallback = func; + Py_XDECREF(self->ob_cdcallbacks[type].ob_cdcallbackarg); + Py_INCREF(funcarg); + self->ob_cdcallbacks[type].ob_cdcallbackarg = funcarg; + +/* + if (type == cd_audio) { + sigfpe_[_UNDERFL].repls = _ZERO; + handle_sigfpes(_ON, _EN_UNDERFL, NULL, + _ABORT_ON_ERROR, NULL); + } +*/ + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef cdparser_methods[] = { + {"addcallback", (PyCFunction)CD_addcallback, METH_VARARGS}, + {"deleteparser", (PyCFunction)CD_deleteparser, METH_VARARGS}, + {"parseframe", (PyCFunction)CD_parseframe, METH_VARARGS}, + {"removecallback", (PyCFunction)CD_removecallback, METH_VARARGS}, + {"resetparser", (PyCFunction)CD_resetparser, METH_VARARGS}, + /* backward compatibility */ + {"setcallback", (PyCFunction)CD_addcallback, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +static void +cdparser_dealloc(cdparserobject *self) +{ + int i; + + for (i = 0; i < NCALLBACKS; i++) { + Py_XDECREF(self->ob_cdcallbacks[i].ob_cdcallback); + self->ob_cdcallbacks[i].ob_cdcallback = NULL; + Py_XDECREF(self->ob_cdcallbacks[i].ob_cdcallbackarg); + self->ob_cdcallbacks[i].ob_cdcallbackarg = NULL; + } + CDdeleteparser(self->ob_cdparser); + PyObject_Del(self); +} + +static PyObject * +cdparser_getattr(cdparserobject *self, char *name) +{ + if (self->ob_cdparser == NULL) { + PyErr_SetString(PyExc_RuntimeError, "no parser active"); + return NULL; + } + + return Py_FindMethod(cdparser_methods, (PyObject *)self, name); +} + +PyTypeObject CdParsertype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "cd.cdparser", /*tp_name*/ + sizeof(cdparserobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)cdparser_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)cdparser_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + +static PyObject * +newcdparserobject(CDPARSER *cdp) +{ + cdparserobject *p; + int i; + + p = PyObject_New(cdparserobject, &CdParsertype); + if (p == NULL) + return NULL; + p->ob_cdparser = cdp; + for (i = 0; i < NCALLBACKS; i++) { + p->ob_cdcallbacks[i].ob_cdcallback = NULL; + p->ob_cdcallbacks[i].ob_cdcallbackarg = NULL; + } + return (PyObject *) p; +} + +static PyObject * +CD_createparser(PyObject *self, PyObject *args) +{ + CDPARSER *cdp; + + if (!PyArg_ParseTuple(args, ":createparser")) + return NULL; + cdp = CDcreateparser(); + if (cdp == NULL) { + PyErr_SetString(CdError, "createparser failed"); + return NULL; + } + + return newcdparserobject(cdp); +} + +static PyObject * +CD_msftoframe(PyObject *self, PyObject *args) +{ + int min, sec, frame; + + if (!PyArg_ParseTuple(args, "iii:msftoframe", &min, &sec, &frame)) + return NULL; + + return PyInt_FromLong((long) CDmsftoframe(min, sec, frame)); +} + +static PyMethodDef CD_methods[] = { + {"open", (PyCFunction)CD_open, METH_VARARGS}, + {"createparser", (PyCFunction)CD_createparser, METH_VARARGS}, + {"msftoframe", (PyCFunction)CD_msftoframe, METH_VARARGS}, + {NULL, NULL} /* Sentinel */ +}; + +void +initcd(void) +{ + PyObject *m, *d; + + m = Py_InitModule("cd", CD_methods); + d = PyModule_GetDict(m); + + CdError = PyErr_NewException("cd.error", NULL, NULL); + PyDict_SetItemString(d, "error", CdError); + + /* Identifiers for the different types of callbacks from the parser */ + PyDict_SetItemString(d, "audio", PyInt_FromLong((long) cd_audio)); + PyDict_SetItemString(d, "pnum", PyInt_FromLong((long) cd_pnum)); + PyDict_SetItemString(d, "index", PyInt_FromLong((long) cd_index)); + PyDict_SetItemString(d, "ptime", PyInt_FromLong((long) cd_ptime)); + PyDict_SetItemString(d, "atime", PyInt_FromLong((long) cd_atime)); + PyDict_SetItemString(d, "catalog", PyInt_FromLong((long) cd_catalog)); + PyDict_SetItemString(d, "ident", PyInt_FromLong((long) cd_ident)); + PyDict_SetItemString(d, "control", PyInt_FromLong((long) cd_control)); + + /* Block size information for digital audio data */ + PyDict_SetItemString(d, "DATASIZE", + PyInt_FromLong((long) CDDA_DATASIZE)); + PyDict_SetItemString(d, "BLOCKSIZE", + PyInt_FromLong((long) CDDA_BLOCKSIZE)); + + /* Possible states for the cd player */ + PyDict_SetItemString(d, "ERROR", PyInt_FromLong((long) CD_ERROR)); + PyDict_SetItemString(d, "NODISC", PyInt_FromLong((long) CD_NODISC)); + PyDict_SetItemString(d, "READY", PyInt_FromLong((long) CD_READY)); + PyDict_SetItemString(d, "PLAYING", PyInt_FromLong((long) CD_PLAYING)); + PyDict_SetItemString(d, "PAUSED", PyInt_FromLong((long) CD_PAUSED)); + PyDict_SetItemString(d, "STILL", PyInt_FromLong((long) CD_STILL)); +#ifdef CD_CDROM /* only newer versions of the library */ + PyDict_SetItemString(d, "CDROM", PyInt_FromLong((long) CD_CDROM)); +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgen.py b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgen.py new file mode 100644 index 00000000..5ec01535 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgen.py @@ -0,0 +1,520 @@ +######################################################################## +# Copyright (c) 2000, BeOpen.com. +# Copyright (c) 1995-2000, Corporation for National Research Initiatives. +# Copyright (c) 1990-1995, Stichting Mathematisch Centrum. +# All rights reserved. +# +# See the file "Misc/COPYRIGHT" for information on usage and +# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. +######################################################################## + +# Python script to parse cstubs file for gl and generate C stubs. +# usage: python cgen.py glmodule.c +# +# NOTE: You must first make a python binary without the "GL" option +# before you can run this, when building Python for the first time. +# See comments in the Makefile. +# +# XXX BUG return arrays generate wrong code +# XXX need to change error returns into gotos to free mallocked arrays + + +import string +import sys + + +# Function to print to stderr +# +def err(*args): + savestdout = sys.stdout + try: + sys.stdout = sys.stderr + for i in args: + print i, + print + finally: + sys.stdout = savestdout + + +# The set of digits that form a number +# +digits = '0123456789' + + +# Function to extract a string of digits from the front of the string. +# Returns the leading string of digits and the remaining string. +# If no number is found, returns '' and the original string. +# +def getnum(s): + n = '' + while s and s[0] in digits: + n = n + s[0] + s = s[1:] + return n, s + + +# Function to check if a string is a number +# +def isnum(s): + if not s: return False + for c in s: + if not c in digits: return False + return True + + +# Allowed function return types +# +return_types = ['void', 'short', 'long'] + + +# Allowed function argument types +# +arg_types = ['char', 'string', 'short', 'u_short', 'float', 'long', 'double'] + + +# Need to classify arguments as follows +# simple input variable +# simple output variable +# input array +# output array +# input giving size of some array +# +# Array dimensions can be specified as follows +# constant +# argN +# constant * argN +# retval +# constant * retval +# +# The dimensions given as constants * something are really +# arrays of points where points are 2- 3- or 4-tuples +# +# We have to consider three lists: +# python input arguments +# C stub arguments (in & out) +# python output arguments (really return values) +# +# There is a mapping from python input arguments to the input arguments +# of the C stub, and a further mapping from C stub arguments to the +# python return values + + +# Exception raised by checkarg() and generate() +# +arg_error = 'bad arg' + + +# Function to check one argument. +# Arguments: the type and the arg "name" (really mode plus subscript). +# Raises arg_error if something's wrong. +# Return type, mode, factor, rest of subscript; factor and rest may be empty. +# +def checkarg(type, arg): + # + # Turn "char *x" into "string x". + # + if type == 'char' and arg[0] == '*': + type = 'string' + arg = arg[1:] + # + # Check that the type is supported. + # + if type not in arg_types: + raise arg_error, ('bad type', type) + if type[:2] == 'u_': + type = 'unsigned ' + type[2:] + # + # Split it in the mode (first character) and the rest. + # + mode, rest = arg[:1], arg[1:] + # + # The mode must be 's' for send (= input) or 'r' for return argument. + # + if mode not in ('r', 's'): + raise arg_error, ('bad arg mode', mode) + # + # Is it a simple argument: if so, we are done. + # + if not rest: + return type, mode, '', '' + # + # Not a simple argument; must be an array. + # The 'rest' must be a subscript enclosed in [ and ]. + # The subscript must be one of the following forms, + # otherwise we don't handle it (where N is a number): + # N + # argN + # retval + # N*argN + # N*retval + # + if rest[:1] <> '[' or rest[-1:] <> ']': + raise arg_error, ('subscript expected', rest) + sub = rest[1:-1] + # + # Is there a leading number? + # + num, sub = getnum(sub) + if num: + # There is a leading number + if not sub: + # The subscript is just a number + return type, mode, num, '' + if sub[:1] == '*': + # There is a factor prefix + sub = sub[1:] + else: + raise arg_error, ('\'*\' expected', sub) + if sub == 'retval': + # size is retval -- must be a reply argument + if mode <> 'r': + raise arg_error, ('non-r mode with [retval]', mode) + elif not isnum(sub) and (sub[:3] <> 'arg' or not isnum(sub[3:])): + raise arg_error, ('bad subscript', sub) + # + return type, mode, num, sub + + +# List of functions for which we have generated stubs +# +functions = [] + + +# Generate the stub for the given function, using the database of argument +# information build by successive calls to checkarg() +# +def generate(type, func, database): + # + # Check that we can handle this case: + # no variable size reply arrays yet + # + n_in_args = 0 + n_out_args = 0 + # + for a_type, a_mode, a_factor, a_sub in database: + if a_mode == 's': + n_in_args = n_in_args + 1 + elif a_mode == 'r': + n_out_args = n_out_args + 1 + else: + # Can't happen + raise arg_error, ('bad a_mode', a_mode) + if (a_mode == 'r' and a_sub) or a_sub == 'retval': + err('Function', func, 'too complicated:', + a_type, a_mode, a_factor, a_sub) + print '/* XXX Too complicated to generate code for */' + return + # + functions.append(func) + # + # Stub header + # + print + print 'static PyObject *' + print 'gl_' + func + '(self, args)' + print '\tPyObject *self;' + print '\tPyObject *args;' + print '{' + # + # Declare return value if any + # + if type <> 'void': + print '\t' + type, 'retval;' + # + # Declare arguments + # + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + print '\t' + a_type, + brac = ket = '' + if a_sub and not isnum(a_sub): + if a_factor: + brac = '(' + ket = ')' + print brac + '*', + print 'arg' + `i+1` + ket, + if a_sub and isnum(a_sub): + print '[', a_sub, ']', + if a_factor: + print '[', a_factor, ']', + print ';' + # + # Find input arguments derived from array sizes + # + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode == 's' and a_sub[:3] == 'arg' and isnum(a_sub[3:]): + # Sending a variable-length array + n = eval(a_sub[3:]) + if 1 <= n <= len(database): + b_type, b_mode, b_factor, b_sub = database[n-1] + if b_mode == 's': + database[n-1] = b_type, 'i', a_factor, `i` + n_in_args = n_in_args - 1 + # + # Assign argument positions in the Python argument list + # + in_pos = [] + i_in = 0 + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode == 's': + in_pos.append(i_in) + i_in = i_in + 1 + else: + in_pos.append(-1) + # + # Get input arguments + # + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_type[:9] == 'unsigned ': + xtype = a_type[9:] + else: + xtype = a_type + if a_mode == 'i': + # + # Implicit argument; + # a_factor is divisor if present, + # a_sub indicates which arg (`database index`) + # + j = eval(a_sub) + print '\tif', + print '(!geti' + xtype + 'arraysize(args,', + print `n_in_args` + ',', + print `in_pos[j]` + ',', + if xtype <> a_type: + print '('+xtype+' *)', + print '&arg' + `i+1` + '))' + print '\t\treturn NULL;' + if a_factor: + print '\targ' + `i+1`, + print '= arg' + `i+1`, + print '/', a_factor + ';' + elif a_mode == 's': + if a_sub and not isnum(a_sub): + # Allocate memory for varsize array + print '\tif ((arg' + `i+1`, '=', + if a_factor: + print '('+a_type+'(*)['+a_factor+'])', + print 'PyMem_NEW(' + a_type, ',', + if a_factor: + print a_factor, '*', + print a_sub, ')) == NULL)' + print '\t\treturn PyErr_NoMemory();' + print '\tif', + if a_factor or a_sub: # Get a fixed-size array array + print '(!geti' + xtype + 'array(args,', + print `n_in_args` + ',', + print `in_pos[i]` + ',', + if a_factor: print a_factor, + if a_factor and a_sub: print '*', + if a_sub: print a_sub, + print ',', + if (a_sub and a_factor) or xtype <> a_type: + print '('+xtype+' *)', + print 'arg' + `i+1` + '))' + else: # Get a simple variable + print '(!geti' + xtype + 'arg(args,', + print `n_in_args` + ',', + print `in_pos[i]` + ',', + if xtype <> a_type: + print '('+xtype+' *)', + print '&arg' + `i+1` + '))' + print '\t\treturn NULL;' + # + # Begin of function call + # + if type <> 'void': + print '\tretval =', func + '(', + else: + print '\t' + func + '(', + # + # Argument list + # + for i in range(len(database)): + if i > 0: print ',', + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode == 'r' and not a_factor: + print '&', + print 'arg' + `i+1`, + # + # End of function call + # + print ');' + # + # Free varsize arrays + # + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode == 's' and a_sub and not isnum(a_sub): + print '\tPyMem_DEL(arg' + `i+1` + ');' + # + # Return + # + if n_out_args: + # + # Multiple return values -- construct a tuple + # + if type <> 'void': + n_out_args = n_out_args + 1 + if n_out_args == 1: + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode == 'r': + break + else: + raise arg_error, 'expected r arg not found' + print '\treturn', + print mkobject(a_type, 'arg' + `i+1`) + ';' + else: + print '\t{ PyObject *v = PyTuple_New(', + print n_out_args, ');' + print '\t if (v == NULL) return NULL;' + i_out = 0 + if type <> 'void': + print '\t PyTuple_SetItem(v,', + print `i_out` + ',', + print mkobject(type, 'retval') + ');' + i_out = i_out + 1 + for i in range(len(database)): + a_type, a_mode, a_factor, a_sub = database[i] + if a_mode == 'r': + print '\t PyTuple_SetItem(v,', + print `i_out` + ',', + s = mkobject(a_type, 'arg' + `i+1`) + print s + ');' + i_out = i_out + 1 + print '\t return v;' + print '\t}' + else: + # + # Simple function return + # Return None or return value + # + if type == 'void': + print '\tPy_INCREF(Py_None);' + print '\treturn Py_None;' + else: + print '\treturn', mkobject(type, 'retval') + ';' + # + # Stub body closing brace + # + print '}' + + +# Subroutine to return a function call to mknewobject() +# +def mkobject(type, arg): + if type[:9] == 'unsigned ': + type = type[9:] + return 'mknew' + type + 'object((' + type + ') ' + arg + ')' + return 'mknew' + type + 'object(' + arg + ')' + + +defined_archs = [] + +# usage: cgen [ -Dmach ... ] [ file ] +for arg in sys.argv[1:]: + if arg[:2] == '-D': + defined_archs.append(arg[2:]) + else: + # Open optional file argument + sys.stdin = open(arg, 'r') + + +# Input line number +lno = 0 + + +# Input is divided in two parts, separated by a line containing '%%'. +# -- literally copied to stdout +# -- stub definitions + +# Variable indicating the current input part. +# +part = 1 + +# Main loop over the input +# +while 1: + try: + line = raw_input() + except EOFError: + break + # + lno = lno+1 + words = string.split(line) + # + if part == 1: + # + # In part 1, copy everything literally + # except look for a line of just '%%' + # + if words == ['%%']: + part = part + 1 + else: + # + # Look for names of manually written + # stubs: a single percent followed by the name + # of the function in Python. + # The stub name is derived by prefixing 'gl_'. + # + if words and words[0][0] == '%': + func = words[0][1:] + if (not func) and words[1:]: + func = words[1] + if func: + functions.append(func) + else: + print line + continue + if not words: + continue # skip empty line + elif words[0] == 'if': + # if XXX rest + # if !XXX rest + if words[1][0] == '!': + if words[1][1:] in defined_archs: + continue + elif words[1] not in defined_archs: + continue + words = words[2:] + if words[0] == '#include': + print line + elif words[0][:1] == '#': + pass # ignore comment + elif words[0] not in return_types: + err('Line', lno, ': bad return type :', words[0]) + elif len(words) < 2: + err('Line', lno, ': no funcname :', line) + else: + if len(words) % 2 <> 0: + err('Line', lno, ': odd argument list :', words[2:]) + else: + database = [] + try: + for i in range(2, len(words), 2): + x = checkarg(words[i], words[i+1]) + database.append(x) + print + print '/*', + for w in words: print w, + print '*/' + generate(words[0], words[1], database) + except arg_error, msg: + err('Line', lno, ':', msg) + + +print +print 'static struct PyMethodDef gl_methods[] = {' +for func in functions: + print '\t{"' + func + '", gl_' + func + '},' +print '\t{NULL, NULL} /* Sentinel */' +print '};' +print +print 'void' +print 'initgl()' +print '{' +print '\t(void) Py_InitModule("gl", gl_methods);' +print '}' diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgensupport.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgensupport.c new file mode 100644 index 00000000..b66ec8b9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgensupport.c @@ -0,0 +1,310 @@ + +/* Functions used by cgen output */ + +#include "Python.h" +#include "cgensupport.h" + + +/* Functions to extract arguments. + These needs to know the total number of arguments supplied, + since the argument list is a tuple only of there is more than + one argument. */ + +int +PyArg_GetObject(register PyObject *args, int nargs, int i, PyObject **p_arg) +{ + if (nargs != 1) { + if (args == NULL || !PyTuple_Check(args) || + nargs != PyTuple_Size(args) || + i < 0 || i >= nargs) { + return PyErr_BadArgument(); + } + else { + args = PyTuple_GetItem(args, i); + } + } + if (args == NULL) { + return PyErr_BadArgument(); + } + *p_arg = args; + return 1; +} + +int +PyArg_GetLong(register PyObject *args, int nargs, int i, long *p_arg) +{ + if (nargs != 1) { + if (args == NULL || !PyTuple_Check(args) || + nargs != PyTuple_Size(args) || + i < 0 || i >= nargs) { + return PyErr_BadArgument(); + } + args = PyTuple_GetItem(args, i); + } + if (args == NULL || !PyInt_Check(args)) { + return PyErr_BadArgument(); + } + *p_arg = PyInt_AsLong(args); + return 1; +} + +int +PyArg_GetShort(register PyObject *args, int nargs, int i, short *p_arg) +{ + long x; + if (!PyArg_GetLong(args, nargs, i, &x)) + return 0; + *p_arg = (short) x; + return 1; +} + +static int +extractdouble(register PyObject *v, double *p_arg) +{ + if (v == NULL) { + /* Fall through to error return at end of function */ + } + else if (PyFloat_Check(v)) { + *p_arg = PyFloat_AS_DOUBLE((PyFloatObject *)v); + return 1; + } + else if (PyInt_Check(v)) { + *p_arg = PyInt_AS_LONG((PyIntObject *)v); + return 1; + } + else if (PyLong_Check(v)) { + *p_arg = PyLong_AsDouble(v); + return 1; + } + return PyErr_BadArgument(); +} + +static int +extractfloat(register PyObject *v, float *p_arg) +{ + if (v == NULL) { + /* Fall through to error return at end of function */ + } + else if (PyFloat_Check(v)) { + *p_arg = (float) PyFloat_AS_DOUBLE((PyFloatObject *)v); + return 1; + } + else if (PyInt_Check(v)) { + *p_arg = (float) PyInt_AS_LONG((PyIntObject *)v); + return 1; + } + else if (PyLong_Check(v)) { + *p_arg = (float) PyLong_AsDouble(v); + return 1; + } + return PyErr_BadArgument(); +} + +int +PyArg_GetFloat(register PyObject *args, int nargs, int i, float *p_arg) +{ + PyObject *v; + float x; + if (!PyArg_GetObject(args, nargs, i, &v)) + return 0; + if (!extractfloat(v, &x)) + return 0; + *p_arg = x; + return 1; +} + +int +PyArg_GetString(PyObject *args, int nargs, int i, string *p_arg) +{ + PyObject *v; + if (!PyArg_GetObject(args, nargs, i, &v)) + return 0; + if (!PyString_Check(v)) { + return PyErr_BadArgument(); + } + *p_arg = PyString_AsString(v); + return 1; +} + +int +PyArg_GetChar(PyObject *args, int nargs, int i, char *p_arg) +{ + string x; + if (!PyArg_GetString(args, nargs, i, &x)) + return 0; + if (x[0] == '\0' || x[1] != '\0') { + /* Not exactly one char */ + return PyErr_BadArgument(); + } + *p_arg = x[0]; + return 1; +} + +int +PyArg_GetLongArraySize(PyObject *args, int nargs, int i, long *p_arg) +{ + PyObject *v; + if (!PyArg_GetObject(args, nargs, i, &v)) + return 0; + if (PyTuple_Check(v)) { + *p_arg = PyTuple_Size(v); + return 1; + } + if (PyList_Check(v)) { + *p_arg = PyList_Size(v); + return 1; + } + return PyErr_BadArgument(); +} + +int +PyArg_GetShortArraySize(PyObject *args, int nargs, int i, short *p_arg) +{ + long x; + if (!PyArg_GetLongArraySize(args, nargs, i, &x)) + return 0; + *p_arg = (short) x; + return 1; +} + +/* XXX The following four are too similar. Should share more code. */ + +int +PyArg_GetLongArray(PyObject *args, int nargs, int i, int n, long *p_arg) +{ + PyObject *v, *w; + if (!PyArg_GetObject(args, nargs, i, &v)) + return 0; + if (PyTuple_Check(v)) { + if (PyTuple_Size(v) != n) { + return PyErr_BadArgument(); + } + for (i = 0; i < n; i++) { + w = PyTuple_GetItem(v, i); + if (!PyInt_Check(w)) { + return PyErr_BadArgument(); + } + p_arg[i] = PyInt_AsLong(w); + } + return 1; + } + else if (PyList_Check(v)) { + if (PyList_Size(v) != n) { + return PyErr_BadArgument(); + } + for (i = 0; i < n; i++) { + w = PyList_GetItem(v, i); + if (!PyInt_Check(w)) { + return PyErr_BadArgument(); + } + p_arg[i] = PyInt_AsLong(w); + } + return 1; + } + else { + return PyErr_BadArgument(); + } +} + +int +PyArg_GetShortArray(PyObject *args, int nargs, int i, int n, short *p_arg) +{ + PyObject *v, *w; + if (!PyArg_GetObject(args, nargs, i, &v)) + return 0; + if (PyTuple_Check(v)) { + if (PyTuple_Size(v) != n) { + return PyErr_BadArgument(); + } + for (i = 0; i < n; i++) { + w = PyTuple_GetItem(v, i); + if (!PyInt_Check(w)) { + return PyErr_BadArgument(); + } + p_arg[i] = (short) PyInt_AsLong(w); + } + return 1; + } + else if (PyList_Check(v)) { + if (PyList_Size(v) != n) { + return PyErr_BadArgument(); + } + for (i = 0; i < n; i++) { + w = PyList_GetItem(v, i); + if (!PyInt_Check(w)) { + return PyErr_BadArgument(); + } + p_arg[i] = (short) PyInt_AsLong(w); + } + return 1; + } + else { + return PyErr_BadArgument(); + } +} + +int +PyArg_GetDoubleArray(PyObject *args, int nargs, int i, int n, double *p_arg) +{ + PyObject *v, *w; + if (!PyArg_GetObject(args, nargs, i, &v)) + return 0; + if (PyTuple_Check(v)) { + if (PyTuple_Size(v) != n) { + return PyErr_BadArgument(); + } + for (i = 0; i < n; i++) { + w = PyTuple_GetItem(v, i); + if (!extractdouble(w, &p_arg[i])) + return 0; + } + return 1; + } + else if (PyList_Check(v)) { + if (PyList_Size(v) != n) { + return PyErr_BadArgument(); + } + for (i = 0; i < n; i++) { + w = PyList_GetItem(v, i); + if (!extractdouble(w, &p_arg[i])) + return 0; + } + return 1; + } + else { + return PyErr_BadArgument(); + } +} + +int +PyArg_GetFloatArray(PyObject *args, int nargs, int i, int n, float *p_arg) +{ + PyObject *v, *w; + if (!PyArg_GetObject(args, nargs, i, &v)) + return 0; + if (PyTuple_Check(v)) { + if (PyTuple_Size(v) != n) { + return PyErr_BadArgument(); + } + for (i = 0; i < n; i++) { + w = PyTuple_GetItem(v, i); + if (!extractfloat(w, &p_arg[i])) + return 0; + } + return 1; + } + else if (PyList_Check(v)) { + if (PyList_Size(v) != n) { + return PyErr_BadArgument(); + } + for (i = 0; i < n; i++) { + w = PyList_GetItem(v, i); + if (!extractfloat(w, &p_arg[i])) + return 0; + } + return 1; + } + else { + return PyErr_BadArgument(); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgensupport.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgensupport.h new file mode 100644 index 00000000..23404661 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cgensupport.h @@ -0,0 +1,64 @@ +#ifndef Py_CGENSUPPORT_H +#define Py_CGENSUPPORT_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Definitions used by cgen output */ + +/* XXX This file is obsolete. It is *only* used by glmodule.c. */ + +typedef char *string; + +#define mknewlongobject(x) PyInt_FromLong(x) +#define mknewshortobject(x) PyInt_FromLong((long)x) +#define mknewfloatobject(x) PyFloat_FromDouble(x) +#define mknewcharobject(ch) Py_BuildValue("c", ch) + +#define getichararg PyArg_GetChar +#define getidoublearray PyArg_GetDoubleArray +#define getifloatarg PyArg_GetFloat +#define getifloatarray PyArg_GetFloatArray +#define getilongarg PyArg_GetLong +#define getilongarray PyArg_GetLongArray +#define getilongarraysize PyArg_GetLongArraySize +#define getiobjectarg PyArg_GetObject +#define getishortarg PyArg_GetShort +#define getishortarray PyArg_GetShortArray +#define getishortarraysize PyArg_GetShortArraySize +#define getistringarg PyArg_GetString + +extern int PyArg_GetObject(PyObject *args, int nargs, + int i, PyObject **p_a); +extern int PyArg_GetLong(PyObject *args, int nargs, + int i, long *p_a); +extern int PyArg_GetShort(PyObject *args, int nargs, + int i, short *p_a); +extern int PyArg_GetFloat(PyObject *args, int nargs, + int i, float *p_a); +extern int PyArg_GetString(PyObject *args, int nargs, + int i, string *p_a); +extern int PyArg_GetChar(PyObject *args, int nargs, + int i, char *p_a); +extern int PyArg_GetLongArray(PyObject *args, int nargs, + int i, int n, long *p_a); +extern int PyArg_GetShortArray(PyObject *args, int nargs, + int i, int n, short *p_a); +extern int PyArg_GetDoubleArray(PyObject *args, int nargs, + int i, int n, double *p_a); +extern int PyArg_GetFloatArray(PyObject *args, int nargs, + int i, int n, float *p_a); +extern int PyArg_GetLongArraySize(PyObject *args, int nargs, + int i, long *p_a); +extern int PyArg_GetShortArraySize(PyObject *args, int nargs, + int i, short *p_a); +extern int PyArg_GetDoubleArraySize(PyObject *args, int nargs, + int i, double *p_a); +extern int PyArg_GetFloatArraySize(PyObject *args, int nargs, + int i, float *p_a); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CGENSUPPORT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/clmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/clmodule.c new file mode 100644 index 00000000..54343866 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/clmodule.c @@ -0,0 +1,2557 @@ + + +/* Cl objects */ + +#define CLDEBUG + +#include +#include +#if defined(CL_JPEG_SOFTWARE) && !defined(CL_JPEG_COSMO) +#include +#endif +#include "Python.h" + +typedef struct { + PyObject_HEAD + int ob_isCompressor; /* Compressor or Decompressor */ + CL_Handle ob_compressorHdl; + int *ob_paramtypes; + int ob_nparams; +} clobject; + +static PyObject *ClError; /* exception cl.error */ + +static int error_handler_called = 0; + +/* + * We want to use the function prototypes that are available in the C + * compiler on the SGI. Because of that, we need to declare the first + * argument of the compressor and decompressor methods as "object *", + * even though they are really "clobject *". Therefore we cast the + * argument to the proper type using this macro. + */ +#define SELF ((clobject *) self) + +/******************************************************************** + Utility routines. +********************************************************************/ +static void +cl_ErrorHandler(CL_Handle handle, int code, const char *fmt, ...) +{ + va_list ap; + char errbuf[BUFSIZ]; /* hopefully big enough */ + char *p; + + if (PyErr_Occurred()) /* don't change existing error */ + return; + error_handler_called = 1; + va_start(ap, fmt); + vsprintf(errbuf, fmt, ap); + va_end(ap); + p = &errbuf[strlen(errbuf) - 1]; /* swat the line feed */ + if (*p == '\n') + *p = 0; + PyErr_SetString(ClError, errbuf); +} + +/* + * This assumes that params are always in the range 0 to some maximum. + */ +static int +param_type_is_float(clobject *self, int param) +{ + int bufferlength; + + if (self->ob_paramtypes == NULL) { + error_handler_called = 0; + bufferlength = clQueryParams(self->ob_compressorHdl, 0, 0); + if (error_handler_called) + return -1; + + self->ob_paramtypes = PyMem_NEW(int, bufferlength); + if (self->ob_paramtypes == NULL) + return -1; + self->ob_nparams = bufferlength / 2; + + (void) clQueryParams(self->ob_compressorHdl, + self->ob_paramtypes, bufferlength); + if (error_handler_called) { + PyMem_DEL(self->ob_paramtypes); + self->ob_paramtypes = NULL; + return -1; + } + } + + if (param < 0 || param >= self->ob_nparams) + return -1; + + if (self->ob_paramtypes[param*2 + 1] == CL_FLOATING_ENUM_VALUE || + self->ob_paramtypes[param*2 + 1] == CL_FLOATING_RANGE_VALUE) + return 1; + else + return 0; +} + +/******************************************************************** + Single image compression/decompression. +********************************************************************/ +static PyObject * +cl_CompressImage(PyObject *self, PyObject *args) +{ + int compressionScheme, width, height, originalFormat; + float compressionRatio; + int frameBufferSize, compressedBufferSize; + char *frameBuffer; + PyObject *compressedBuffer; + + if (!PyArg_ParseTuple(args, "iiiifs#", &compressionScheme, + &width, &height, + &originalFormat, &compressionRatio, &frameBuffer, + &frameBufferSize)) + return NULL; + + retry: + compressedBuffer = PyString_FromStringAndSize(NULL, frameBufferSize); + if (compressedBuffer == NULL) + return NULL; + + compressedBufferSize = frameBufferSize; + error_handler_called = 0; + if (clCompressImage(compressionScheme, width, height, originalFormat, + compressionRatio, (void *) frameBuffer, + &compressedBufferSize, + (void *) PyString_AsString(compressedBuffer)) + == FAILURE || error_handler_called) { + Py_DECREF(compressedBuffer); + if (!error_handler_called) + PyErr_SetString(ClError, "clCompressImage failed"); + return NULL; + } + + if (compressedBufferSize > frameBufferSize) { + frameBufferSize = compressedBufferSize; + Py_DECREF(compressedBuffer); + goto retry; + } + + if (compressedBufferSize < frameBufferSize) + _PyString_Resize(&compressedBuffer, compressedBufferSize); + + return compressedBuffer; +} + +static PyObject * +cl_DecompressImage(PyObject *self, PyObject *args) +{ + int compressionScheme, width, height, originalFormat; + char *compressedBuffer; + int compressedBufferSize, frameBufferSize; + PyObject *frameBuffer; + + if (!PyArg_ParseTuple(args, "iiiis#", &compressionScheme, &width, &height, + &originalFormat, &compressedBuffer, + &compressedBufferSize)) + return NULL; + + frameBufferSize = width * height * CL_BytesPerPixel(originalFormat); + + frameBuffer = PyString_FromStringAndSize(NULL, frameBufferSize); + if (frameBuffer == NULL) + return NULL; + + error_handler_called = 0; + if (clDecompressImage(compressionScheme, width, height, originalFormat, + compressedBufferSize, compressedBuffer, + (void *) PyString_AsString(frameBuffer)) + == FAILURE || error_handler_called) { + Py_DECREF(frameBuffer); + if (!error_handler_called) + PyErr_SetString(ClError, "clDecompressImage failed"); + return NULL; + } + + return frameBuffer; +} + +/******************************************************************** + Sequential compression/decompression. +********************************************************************/ +#define CheckCompressor(self) if ((self)->ob_compressorHdl == NULL) { \ + PyErr_SetString(PyExc_RuntimeError, "(de)compressor not active"); \ + return NULL; \ +} + +static PyObject * +doClose(clobject *self, int (*close_func)(CL_Handle)) +{ + CheckCompressor(self); + + error_handler_called = 0; + if ((*close_func)(self->ob_compressorHdl) == FAILURE || + error_handler_called) { + if (!error_handler_called) + PyErr_SetString(ClError, "close failed"); + return NULL; + } + + self->ob_compressorHdl = NULL; + + if (self->ob_paramtypes) + PyMem_DEL(self->ob_paramtypes); + self->ob_paramtypes = NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +clm_CloseCompressor(PyObject *self) +{ + return doClose(SELF, clCloseCompressor); +} + +static PyObject * +clm_CloseDecompressor(PyObject *self) +{ + return doClose(SELF, clCloseDecompressor); +} + +static PyObject * +clm_Compress(PyObject *self, PyObject *args) +{ + int numberOfFrames; + int frameBufferSize, compressedBufferSize, size; + char *frameBuffer; + PyObject *data; + + CheckCompressor(SELF); + + if (!PyArg_Parse(args, "(is#)", &numberOfFrames, + &frameBuffer, &frameBufferSize)) + return NULL; + + error_handler_called = 0; + size = clGetParam(SELF->ob_compressorHdl, CL_COMPRESSED_BUFFER_SIZE); + compressedBufferSize = size; + if (error_handler_called) + return NULL; + + data = PyString_FromStringAndSize(NULL, size); + if (data == NULL) + return NULL; + + error_handler_called = 0; + if (clCompress(SELF->ob_compressorHdl, numberOfFrames, + (void *) frameBuffer, &compressedBufferSize, + (void *) PyString_AsString(data)) == FAILURE || + error_handler_called) { + Py_DECREF(data); + if (!error_handler_called) + PyErr_SetString(ClError, "compress failed"); + return NULL; + } + + if (compressedBufferSize < size) + if (_PyString_Resize(&data, compressedBufferSize)) + return NULL; + + if (compressedBufferSize > size) { + /* we didn't get all "compressed" data */ + Py_DECREF(data); + PyErr_SetString(ClError, + "compressed data is more than fitted"); + return NULL; + } + + return data; +} + +static PyObject * +clm_Decompress(PyObject *self, PyObject *args) +{ + PyObject *data; + int numberOfFrames; + char *compressedData; + int compressedDataSize, dataSize; + + CheckCompressor(SELF); + + if (!PyArg_Parse(args, "(is#)", &numberOfFrames, &compressedData, + &compressedDataSize)) + return NULL; + + error_handler_called = 0; + dataSize = clGetParam(SELF->ob_compressorHdl, CL_FRAME_BUFFER_SIZE); + if (error_handler_called) + return NULL; + + data = PyString_FromStringAndSize(NULL, dataSize); + if (data == NULL) + return NULL; + + error_handler_called = 0; + if (clDecompress(SELF->ob_compressorHdl, numberOfFrames, + compressedDataSize, (void *) compressedData, + (void *) PyString_AsString(data)) == FAILURE || + error_handler_called) { + Py_DECREF(data); + if (!error_handler_called) + PyErr_SetString(ClError, "decompress failed"); + return NULL; + } + + return data; +} + +static PyObject * +doParams(clobject *self, PyObject *args, int (*func)(CL_Handle, int *, int), + int modified) +{ + PyObject *list, *v; + int *PVbuffer; + int length; + int i; + float number; + + CheckCompressor(self); + + if (!PyArg_Parse(args, "O", &list)) + return NULL; + if (!PyList_Check(list)) { + PyErr_BadArgument(); + return NULL; + } + length = PyList_Size(list); + PVbuffer = PyMem_NEW(int, length); + if (PVbuffer == NULL) + return PyErr_NoMemory(); + for (i = 0; i < length; i++) { + v = PyList_GetItem(list, i); + if (PyFloat_Check(v)) { + number = PyFloat_AsDouble(v); + PVbuffer[i] = CL_TypeIsInt(number); + } else if (PyInt_Check(v)) { + PVbuffer[i] = PyInt_AsLong(v); + if ((i & 1) && + param_type_is_float(self, PVbuffer[i-1]) > 0) { + number = PVbuffer[i]; + PVbuffer[i] = CL_TypeIsInt(number); + } + } else { + PyMem_DEL(PVbuffer); + PyErr_BadArgument(); + return NULL; + } + } + + error_handler_called = 0; + (*func)(self->ob_compressorHdl, PVbuffer, length); + if (error_handler_called) { + PyMem_DEL(PVbuffer); + return NULL; + } + + if (modified) { + for (i = 0; i < length; i++) { + if ((i & 1) && + param_type_is_float(self, PVbuffer[i-1]) > 0) { + number = CL_TypeIsFloat(PVbuffer[i]); + v = PyFloat_FromDouble(number); + } else + v = PyInt_FromLong(PVbuffer[i]); + PyList_SetItem(list, i, v); + } + } + + PyMem_DEL(PVbuffer); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +clm_GetParams(PyObject *self, PyObject *args) +{ + return doParams(SELF, args, clGetParams, 1); +} + +static PyObject * +clm_SetParams(PyObject *self, PyObject *args) +{ + return doParams(SELF, args, clSetParams, 0); +} + +static PyObject * +do_get(clobject *self, PyObject *args, int (*func)(CL_Handle, int)) +{ + int paramID, value; + float fvalue; + + CheckCompressor(self); + + if (!PyArg_Parse(args, "i", ¶mID)) + return NULL; + + error_handler_called = 0; + value = (*func)(self->ob_compressorHdl, paramID); + if (error_handler_called) + return NULL; + + if (param_type_is_float(self, paramID) > 0) { + fvalue = CL_TypeIsFloat(value); + return PyFloat_FromDouble(fvalue); + } + + return PyInt_FromLong(value); +} + +static PyObject * +clm_GetParam(PyObject *self, PyObject *args) +{ + return do_get(SELF, args, clGetParam); +} + +static PyObject * +clm_GetDefault(PyObject *self, PyObject *args) +{ + return do_get(SELF, args, clGetDefault); +} + +static PyObject * +clm_SetParam(PyObject *self, PyObject *args) +{ + int paramID, value; + float fvalue; + + CheckCompressor(SELF); + + if (!PyArg_Parse(args, "(ii)", ¶mID, &value)) { + PyErr_Clear(); + if (!PyArg_Parse(args, "(if)", ¶mID, &fvalue)) { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, + "bad argument list (format '(ii)' or '(if)')"); + return NULL; + } + value = CL_TypeIsInt(fvalue); + } else { + if (param_type_is_float(SELF, paramID) > 0) { + fvalue = value; + value = CL_TypeIsInt(fvalue); + } + } + + error_handler_called = 0; + value = clSetParam(SELF->ob_compressorHdl, paramID, value); + if (error_handler_called) + return NULL; + + if (param_type_is_float(SELF, paramID) > 0) + return PyFloat_FromDouble(CL_TypeIsFloat(value)); + else + return PyInt_FromLong(value); +} + +static PyObject * +clm_GetParamID(PyObject *self, PyObject *args) +{ + char *name; + int value; + + CheckCompressor(SELF); + + if (!PyArg_Parse(args, "s", &name)) + return NULL; + + error_handler_called = 0; + value = clGetParamID(SELF->ob_compressorHdl, name); + if (value == FAILURE || error_handler_called) { + if (!error_handler_called) + PyErr_SetString(ClError, "getparamid failed"); + return NULL; + } + + return PyInt_FromLong(value); +} + +static PyObject * +clm_QueryParams(PyObject *self) +{ + int bufferlength; + int *PVbuffer; + PyObject *list; + int i; + + CheckCompressor(SELF); + + error_handler_called = 0; + bufferlength = clQueryParams(SELF->ob_compressorHdl, 0, 0); + if (error_handler_called) + return NULL; + + PVbuffer = PyMem_NEW(int, bufferlength); + if (PVbuffer == NULL) + return PyErr_NoMemory(); + + bufferlength = clQueryParams(SELF->ob_compressorHdl, PVbuffer, + bufferlength); + if (error_handler_called) { + PyMem_DEL(PVbuffer); + return NULL; + } + + list = PyList_New(bufferlength); + if (list == NULL) { + PyMem_DEL(PVbuffer); + return NULL; + } + + for (i = 0; i < bufferlength; i++) { + if (i & 1) + PyList_SetItem(list, i, PyInt_FromLong(PVbuffer[i])); + else if (PVbuffer[i] == 0) { + Py_INCREF(Py_None); + PyList_SetItem(list, i, Py_None); + } else + PyList_SetItem(list, i, + PyString_FromString((char *) PVbuffer[i])); + } + + PyMem_DEL(PVbuffer); + + return list; +} + +static PyObject * +clm_GetMinMax(PyObject *self, PyObject *args) +{ + int param, min, max; + float fmin, fmax; + + CheckCompressor(SELF); + + if (!PyArg_Parse(args, "i", ¶m)) + return NULL; + + clGetMinMax(SELF->ob_compressorHdl, param, &min, &max); + + if (param_type_is_float(SELF, param) > 0) { + fmin = CL_TypeIsFloat(min); + fmax = CL_TypeIsFloat(max); + return Py_BuildValue("(ff)", fmin, fmax); + } + + return Py_BuildValue("(ii)", min, max); +} + +static PyObject * +clm_GetName(PyObject *self, PyObject *args) +{ + int param; + char *name; + + CheckCompressor(SELF); + + if (!PyArg_Parse(args, "i", ¶m)) + return NULL; + + error_handler_called = 0; + name = clGetName(SELF->ob_compressorHdl, param); + if (name == NULL || error_handler_called) { + if (!error_handler_called) + PyErr_SetString(ClError, "getname failed"); + return NULL; + } + + return PyString_FromString(name); +} + +static PyObject * +clm_QuerySchemeFromHandle(PyObject *self) +{ + CheckCompressor(SELF); + return PyInt_FromLong(clQuerySchemeFromHandle(SELF->ob_compressorHdl)); +} + +static PyObject * +clm_ReadHeader(PyObject *self, PyObject *args) +{ + char *header; + int headerSize; + + CheckCompressor(SELF); + + if (!PyArg_Parse(args, "s#", &header, &headerSize)) + return NULL; + + return PyInt_FromLong(clReadHeader(SELF->ob_compressorHdl, + headerSize, header)); +} + +static PyMethodDef compressor_methods[] = { + {"close", clm_CloseCompressor, METH_NOARGS}, /* alias */ + {"CloseCompressor", clm_CloseCompressor, METH_NOARGS}, + {"Compress", clm_Compress, METH_OLDARGS}, + {"GetDefault", clm_GetDefault, METH_OLDARGS}, + {"GetMinMax", clm_GetMinMax, METH_OLDARGS}, + {"GetName", clm_GetName, METH_OLDARGS}, + {"GetParam", clm_GetParam, METH_OLDARGS}, + {"GetParamID", clm_GetParamID, METH_OLDARGS}, + {"GetParams", clm_GetParams, METH_OLDARGS}, + {"QueryParams", clm_QueryParams, METH_NOARGS}, + {"QuerySchemeFromHandle",clm_QuerySchemeFromHandle, METH_NOARGS}, + {"SetParam", clm_SetParam, METH_OLDARGS}, + {"SetParams", clm_SetParams, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyMethodDef decompressor_methods[] = { + {"close", clm_CloseDecompressor, METH_NOARGS}, /* alias */ + {"CloseDecompressor", clm_CloseDecompressor, METH_NOARGS}, + {"Decompress", clm_Decompress, METH_OLDARGS}, + {"GetDefault", clm_GetDefault, METH_OLDARGS}, + {"GetMinMax", clm_GetMinMax, METH_OLDARGS}, + {"GetName", clm_GetName, METH_OLDARGS}, + {"GetParam", clm_GetParam, METH_OLDARGS}, + {"GetParamID", clm_GetParamID, METH_OLDARGS}, + {"GetParams", clm_GetParams, METH_OLDARGS}, + {"ReadHeader", clm_ReadHeader, METH_OLDARGS}, + {"QueryParams", clm_QueryParams, METH_NOARGS}, + {"QuerySchemeFromHandle",clm_QuerySchemeFromHandle, METH_NOARGS}, + {"SetParam", clm_SetParam, METH_OLDARGS}, + {"SetParams", clm_SetParams, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +static void +cl_dealloc(PyObject *self) +{ + if (SELF->ob_compressorHdl) { + if (SELF->ob_isCompressor) + clCloseCompressor(SELF->ob_compressorHdl); + else + clCloseDecompressor(SELF->ob_compressorHdl); + } + PyObject_Del(self); +} + +static PyObject * +cl_getattr(PyObject *self, char *name) +{ + if (SELF->ob_isCompressor) + return Py_FindMethod(compressor_methods, self, name); + else + return Py_FindMethod(decompressor_methods, self, name); +} + +static PyTypeObject Cltype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "cl.cl", /*tp_name*/ + sizeof(clobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)cl_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)cl_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ +}; + +static PyObject * +doOpen(PyObject *self, PyObject *args, int (*open_func)(int, CL_Handle *), + int iscompressor) +{ + int scheme; + clobject *new; + + if (!PyArg_ParseTuple(args, "i", &scheme)) + return NULL; + + new = PyObject_New(clobject, &Cltype); + if (new == NULL) + return NULL; + + new->ob_compressorHdl = NULL; + new->ob_isCompressor = iscompressor; + new->ob_paramtypes = NULL; + + error_handler_called = 0; + if ((*open_func)(scheme, &new->ob_compressorHdl) == FAILURE || + error_handler_called) { + Py_DECREF(new); + if (!error_handler_called) + PyErr_SetString(ClError, "Open(De)Compressor failed"); + return NULL; + } + return (PyObject *)new; +} + +static PyObject * +cl_OpenCompressor(PyObject *self, PyObject *args) +{ + return doOpen(self, args, clOpenCompressor, 1); +} + +static PyObject * +cl_OpenDecompressor(PyObject *self, PyObject *args) +{ + return doOpen(self, args, clOpenDecompressor, 0); +} + +static PyObject * +cl_QueryScheme(PyObject *self, PyObject *args) +{ + char *header; + int headerlen; + int scheme; + + if (!PyArg_ParseTuple(args, "s#", &header, &headerlen)) + return NULL; + + scheme = clQueryScheme(header); + if (scheme < 0) { + PyErr_SetString(ClError, "unknown compression scheme"); + return NULL; + } + + return PyInt_FromLong(scheme); +} + +static PyObject * +cl_QueryMaxHeaderSize(PyObject *self, PyObject *args) +{ + int scheme; + + if (!PyArg_ParseTuple(args, "i", &scheme)) + return NULL; + + return PyInt_FromLong(clQueryMaxHeaderSize(scheme)); +} + +static PyObject * +cl_QueryAlgorithms(PyObject *self, PyObject *args) +{ + int algorithmMediaType; + int bufferlength; + int *PVbuffer; + PyObject *list; + int i; + + if (!PyArg_ParseTuple(args, "i", &algorithmMediaType)) + return NULL; + + error_handler_called = 0; + bufferlength = clQueryAlgorithms(algorithmMediaType, 0, 0); + if (error_handler_called) + return NULL; + + PVbuffer = PyMem_NEW(int, bufferlength); + if (PVbuffer == NULL) + return PyErr_NoMemory(); + + bufferlength = clQueryAlgorithms(algorithmMediaType, PVbuffer, + bufferlength); + if (error_handler_called) { + PyMem_DEL(PVbuffer); + return NULL; + } + + list = PyList_New(bufferlength); + if (list == NULL) { + PyMem_DEL(PVbuffer); + return NULL; + } + + for (i = 0; i < bufferlength; i++) { + if (i & 1) + PyList_SetItem(list, i, PyInt_FromLong(PVbuffer[i])); + else if (PVbuffer[i] == 0) { + Py_INCREF(Py_None); + PyList_SetItem(list, i, Py_None); + } else + PyList_SetItem(list, i, + PyString_FromString((char *) PVbuffer[i])); + } + + PyMem_DEL(PVbuffer); + + return list; +} + +static PyObject * +cl_QuerySchemeFromName(PyObject *self, PyObject *args) +{ + int algorithmMediaType; + char *name; + int scheme; + + if (!PyArg_ParseTuple(args, "is", &algorithmMediaType, &name)) + return NULL; + + error_handler_called = 0; + scheme = clQuerySchemeFromName(algorithmMediaType, name); + if (error_handler_called) { + PyErr_SetString(ClError, "unknown compression scheme"); + return NULL; + } + + return PyInt_FromLong(scheme); +} + +static PyObject * +cl_GetAlgorithmName(PyObject *self, PyObject *args) +{ + int scheme; + char *name; + + if (!PyArg_ParseTuple(args, "i", &scheme)) + return NULL; + + name = clGetAlgorithmName(scheme); + if (name == 0) { + PyErr_SetString(ClError, "unknown compression scheme"); + return NULL; + } + + return PyString_FromString(name); +} + +static PyObject * +do_set(PyObject *self, PyObject *args, int (*func)(int, int, int)) +{ + int scheme, paramID, value; + float fvalue; + int is_float = 0; + + if (!PyArg_ParseTuple(args, "iii", &scheme, ¶mID, &value)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "iif", &scheme, ¶mID, &fvalue)) { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, + "bad argument list (format '(iii)' or '(iif)')"); + return NULL; + } + value = CL_TypeIsInt(fvalue); + is_float = 1; + } else { + /* check some parameters which we know to be floats */ + switch (scheme) { + case CL_COMPRESSION_RATIO: + case CL_SPEED: + fvalue = value; + value = CL_TypeIsInt(fvalue); + is_float = 1; + break; + } + } + + error_handler_called = 0; + value = (*func)(scheme, paramID, value); + if (error_handler_called) + return NULL; + + if (is_float) + return PyFloat_FromDouble(CL_TypeIsFloat(value)); + else + return PyInt_FromLong(value); +} + +static PyObject * +cl_SetDefault(PyObject *self, PyObject *args) +{ + return do_set(self, args, clSetDefault); +} + +static PyObject * +cl_SetMin(PyObject *self, PyObject *args) +{ + return do_set(self, args, clSetMin); +} + +static PyObject * +cl_SetMax(PyObject *self, PyObject *args) +{ + return do_set(self, args, clSetMax); +} + +#define func(name, handler) \ +static PyObject *cl_##name(PyObject *self, PyObject *args) \ +{ \ + int x; \ + if (!PyArg_ParseTuple(args, "i", &x)) return NULL; \ + return Py##handler(CL_##name(x)); \ +} + +#define func2(name, handler) \ +static PyObject *cl_##name(PyObject *self, PyObject *args) \ +{ \ + int a1, a2; \ + if (!PyArg_ParseTuple(args, "ii", &a1, &a2)) return NULL; \ + return Py##handler(CL_##name(a1, a2)); \ +} + +func(BytesPerSample, Int_FromLong) +func(BytesPerPixel, Int_FromLong) +func(AudioFormatName, String_FromString) +func(VideoFormatName, String_FromString) +func(AlgorithmNumber, Int_FromLong) +func(AlgorithmType, Int_FromLong) +func2(Algorithm, Int_FromLong) +func(ParamNumber, Int_FromLong) +func(ParamType, Int_FromLong) +func2(ParamID, Int_FromLong) + +#ifdef CLDEBUG + static PyObject * +cvt_type(PyObject *self, PyObject *args) +{ + int number; + float fnumber; + + if (PyArg_Parse(args, "i", &number)) + return PyFloat_FromDouble(CL_TypeIsFloat(number)); + else { + PyErr_Clear(); + if (PyArg_Parse(args, "f", &fnumber)) + return PyInt_FromLong(CL_TypeIsInt(fnumber)); + return NULL; + } +} +#endif + +static PyMethodDef cl_methods[] = { + {"CompressImage", cl_CompressImage, METH_VARARGS}, + {"DecompressImage", cl_DecompressImage, METH_VARARGS}, + {"GetAlgorithmName", cl_GetAlgorithmName, METH_VARARGS}, + {"OpenCompressor", cl_OpenCompressor, METH_VARARGS}, + {"OpenDecompressor", cl_OpenDecompressor, METH_VARARGS}, + {"QueryAlgorithms", cl_QueryAlgorithms, METH_VARARGS}, + {"QueryMaxHeaderSize", cl_QueryMaxHeaderSize, METH_VARARGS}, + {"QueryScheme", cl_QueryScheme, METH_VARARGS}, + {"QuerySchemeFromName", cl_QuerySchemeFromName, METH_VARARGS}, + {"SetDefault", cl_SetDefault, METH_VARARGS}, + {"SetMax", cl_SetMax, METH_VARARGS}, + {"SetMin", cl_SetMin, METH_VARARGS}, + {"BytesPerSample", cl_BytesPerSample, METH_VARARGS}, + {"BytesPerPixel", cl_BytesPerPixel, METH_VARARGS}, + {"AudioFormatName", cl_AudioFormatName, METH_VARARGS}, + {"VideoFormatName", cl_VideoFormatName, METH_VARARGS}, + {"AlgorithmNumber", cl_AlgorithmNumber, METH_VARARGS}, + {"AlgorithmType", cl_AlgorithmType, METH_VARARGS}, + {"Algorithm", cl_Algorithm, METH_VARARGS}, + {"ParamNumber", cl_ParamNumber, METH_VARARGS}, + {"ParamType", cl_ParamType, METH_VARARGS}, + {"ParamID", cl_ParamID, METH_VARARGS}, +#ifdef CLDEBUG + {"cvt_type", cvt_type, METH_VARARGS}, +#endif + {NULL, NULL} /* Sentinel */ +}; + +#ifdef CL_JPEG_SOFTWARE +#define IRIX_5_3_LIBRARY +#endif + +void +initcl(void) +{ + PyObject *m, *d, *x; + + m = Py_InitModule("cl", cl_methods); + d = PyModule_GetDict(m); + + ClError = PyErr_NewException("cl.error", NULL, NULL); + (void) PyDict_SetItemString(d, "error", ClError); + +#ifdef CL_ADDED_ALGORITHM_ERROR + x = PyInt_FromLong(CL_ADDED_ALGORITHM_ERROR); + if (x == NULL || PyDict_SetItemString(d, "ADDED_ALGORITHM_ERROR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_ALAW + x = PyInt_FromLong(CL_ALAW); + if (x == NULL || PyDict_SetItemString(d, "ALAW", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_ALGORITHM_ID + x = PyInt_FromLong(CL_ALGORITHM_ID); + if (x == NULL || PyDict_SetItemString(d, "ALGORITHM_ID", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_ALGORITHM_TABLE_FULL + x = PyInt_FromLong(CL_ALGORITHM_TABLE_FULL); + if (x == NULL || PyDict_SetItemString(d, "ALGORITHM_TABLE_FULL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_ALGORITHM_VERSION + x = PyInt_FromLong(CL_ALGORITHM_VERSION); + if (x == NULL || PyDict_SetItemString(d, "ALGORITHM_VERSION", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_ALG_AUDIO + x = PyInt_FromLong(CL_ALG_AUDIO); + if (x == NULL || PyDict_SetItemString(d, "ALG_AUDIO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_ALG_VIDEO + x = PyInt_FromLong(CL_ALG_VIDEO); + if (x == NULL || PyDict_SetItemString(d, "ALG_VIDEO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AUDIO + x = PyInt_FromLong(CL_AUDIO); + if (x == NULL || PyDict_SetItemString(d, "AUDIO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_BITRATE_POLICY + x = PyInt_FromLong(CL_AWARE_BITRATE_POLICY); + if (x == NULL || PyDict_SetItemString(d, "AWARE_BITRATE_POLICY", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_BITRATE_TARGET + x = PyInt_FromLong(CL_AWARE_BITRATE_TARGET); + if (x == NULL || PyDict_SetItemString(d, "AWARE_BITRATE_TARGET", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_CHANNEL_POLICY + x = PyInt_FromLong(CL_AWARE_CHANNEL_POLICY); + if (x == NULL || PyDict_SetItemString(d, "AWARE_CHANNEL_POLICY", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_CONST_QUAL + x = PyInt_FromLong(CL_AWARE_CONST_QUAL); + if (x == NULL || PyDict_SetItemString(d, "AWARE_CONST_QUAL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_ERROR + x = PyInt_FromLong(CL_AWARE_ERROR); + if (x == NULL || PyDict_SetItemString(d, "AWARE_ERROR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_FIXED_RATE + x = PyInt_FromLong(CL_AWARE_FIXED_RATE); + if (x == NULL || PyDict_SetItemString(d, "AWARE_FIXED_RATE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_INDEPENDENT + x = PyInt_FromLong(CL_AWARE_INDEPENDENT); + if (x == NULL || PyDict_SetItemString(d, "AWARE_INDEPENDENT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_JOINT_STEREO + x = PyInt_FromLong(CL_AWARE_JOINT_STEREO); + if (x == NULL || PyDict_SetItemString(d, "AWARE_JOINT_STEREO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_LAYER + x = PyInt_FromLong(CL_AWARE_LAYER); + if (x == NULL || PyDict_SetItemString(d, "AWARE_LAYER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_LOSSLESS + x = PyInt_FromLong(CL_AWARE_LOSSLESS); + if (x == NULL || PyDict_SetItemString(d, "AWARE_LOSSLESS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_MPEG_AUDIO + x = PyInt_FromLong(CL_AWARE_MPEG_AUDIO); + if (x == NULL || PyDict_SetItemString(d, "AWARE_MPEG_AUDIO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_MPEG_LAYER_I + x = PyInt_FromLong(CL_AWARE_MPEG_LAYER_I); + if (x == NULL || PyDict_SetItemString(d, "AWARE_MPEG_LAYER_I", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_MPEG_LAYER_II + x = PyInt_FromLong(CL_AWARE_MPEG_LAYER_II); + if (x == NULL || PyDict_SetItemString(d, "AWARE_MPEG_LAYER_II", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_MULTIRATE + x = PyInt_FromLong(CL_AWARE_MULTIRATE); + if (x == NULL || PyDict_SetItemString(d, "AWARE_MULTIRATE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_NOISE_MARGIN + x = PyInt_FromLong(CL_AWARE_NOISE_MARGIN); + if (x == NULL || PyDict_SetItemString(d, "AWARE_NOISE_MARGIN", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_AWARE_STEREO + x = PyInt_FromLong(CL_AWARE_STEREO); + if (x == NULL || PyDict_SetItemString(d, "AWARE_STEREO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_ALGORITHM_NAME + x = PyInt_FromLong(CL_BAD_ALGORITHM_NAME); + if (x == NULL || PyDict_SetItemString(d, "BAD_ALGORITHM_NAME", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_ALGORITHM_TYPE + x = PyInt_FromLong(CL_BAD_ALGORITHM_TYPE); + if (x == NULL || PyDict_SetItemString(d, "BAD_ALGORITHM_TYPE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BLOCK_SIZE + x = PyInt_FromLong(CL_BAD_BLOCK_SIZE); + if (x == NULL || PyDict_SetItemString(d, "BAD_BLOCK_SIZE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BOARD + x = PyInt_FromLong(CL_BAD_BOARD); + if (x == NULL || PyDict_SetItemString(d, "BAD_BOARD", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BUFFERING + x = PyInt_FromLong(CL_BAD_BUFFERING); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFERING", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BUFFERLENGTH_NEG + x = PyInt_FromLong(CL_BAD_BUFFERLENGTH_NEG); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFERLENGTH_NEG", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BUFFERLENGTH_ODD + x = PyInt_FromLong(CL_BAD_BUFFERLENGTH_ODD); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFERLENGTH_ODD", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BUFFER_EXISTS + x = PyInt_FromLong(CL_BAD_BUFFER_EXISTS); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFER_EXISTS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BUFFER_HANDLE + x = PyInt_FromLong(CL_BAD_BUFFER_HANDLE); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFER_HANDLE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BUFFER_POINTER + x = PyInt_FromLong(CL_BAD_BUFFER_POINTER); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFER_POINTER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BUFFER_QUERY_SIZE + x = PyInt_FromLong(CL_BAD_BUFFER_QUERY_SIZE); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFER_QUERY_SIZE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BUFFER_SIZE + x = PyInt_FromLong(CL_BAD_BUFFER_SIZE); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFER_SIZE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BUFFER_SIZE_POINTER + x = PyInt_FromLong(CL_BAD_BUFFER_SIZE_POINTER); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFER_SIZE_POINTER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_BUFFER_TYPE + x = PyInt_FromLong(CL_BAD_BUFFER_TYPE); + if (x == NULL || PyDict_SetItemString(d, "BAD_BUFFER_TYPE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_COMPRESSION_SCHEME + x = PyInt_FromLong(CL_BAD_COMPRESSION_SCHEME); + if (x == NULL || PyDict_SetItemString(d, "BAD_COMPRESSION_SCHEME", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_COMPRESSOR_HANDLE + x = PyInt_FromLong(CL_BAD_COMPRESSOR_HANDLE); + if (x == NULL || PyDict_SetItemString(d, "BAD_COMPRESSOR_HANDLE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_COMPRESSOR_HANDLE_POINTER + x = PyInt_FromLong(CL_BAD_COMPRESSOR_HANDLE_POINTER); + if (x == NULL || PyDict_SetItemString(d, "BAD_COMPRESSOR_HANDLE_POINTER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_FRAME_SIZE + x = PyInt_FromLong(CL_BAD_FRAME_SIZE); + if (x == NULL || PyDict_SetItemString(d, "BAD_FRAME_SIZE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_FUNCTIONALITY + x = PyInt_FromLong(CL_BAD_FUNCTIONALITY); + if (x == NULL || PyDict_SetItemString(d, "BAD_FUNCTIONALITY", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_FUNCTION_POINTER + x = PyInt_FromLong(CL_BAD_FUNCTION_POINTER); + if (x == NULL || PyDict_SetItemString(d, "BAD_FUNCTION_POINTER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_HEADER_SIZE + x = PyInt_FromLong(CL_BAD_HEADER_SIZE); + if (x == NULL || PyDict_SetItemString(d, "BAD_HEADER_SIZE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_INITIAL_VALUE + x = PyInt_FromLong(CL_BAD_INITIAL_VALUE); + if (x == NULL || PyDict_SetItemString(d, "BAD_INITIAL_VALUE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_INTERNAL_FORMAT + x = PyInt_FromLong(CL_BAD_INTERNAL_FORMAT); + if (x == NULL || PyDict_SetItemString(d, "BAD_INTERNAL_FORMAT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_LICENSE + x = PyInt_FromLong(CL_BAD_LICENSE); + if (x == NULL || PyDict_SetItemString(d, "BAD_LICENSE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_MIN_GT_MAX + x = PyInt_FromLong(CL_BAD_MIN_GT_MAX); + if (x == NULL || PyDict_SetItemString(d, "BAD_MIN_GT_MAX", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_NO_BUFFERSPACE + x = PyInt_FromLong(CL_BAD_NO_BUFFERSPACE); + if (x == NULL || PyDict_SetItemString(d, "BAD_NO_BUFFERSPACE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_NUMBER_OF_BLOCKS + x = PyInt_FromLong(CL_BAD_NUMBER_OF_BLOCKS); + if (x == NULL || PyDict_SetItemString(d, "BAD_NUMBER_OF_BLOCKS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_PARAM + x = PyInt_FromLong(CL_BAD_PARAM); + if (x == NULL || PyDict_SetItemString(d, "BAD_PARAM", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_PARAM_ID_POINTER + x = PyInt_FromLong(CL_BAD_PARAM_ID_POINTER); + if (x == NULL || PyDict_SetItemString(d, "BAD_PARAM_ID_POINTER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_PARAM_TYPE + x = PyInt_FromLong(CL_BAD_PARAM_TYPE); + if (x == NULL || PyDict_SetItemString(d, "BAD_PARAM_TYPE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_POINTER + x = PyInt_FromLong(CL_BAD_POINTER); + if (x == NULL || PyDict_SetItemString(d, "BAD_POINTER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_PVBUFFER + x = PyInt_FromLong(CL_BAD_PVBUFFER); + if (x == NULL || PyDict_SetItemString(d, "BAD_PVBUFFER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_SCHEME_POINTER + x = PyInt_FromLong(CL_BAD_SCHEME_POINTER); + if (x == NULL || PyDict_SetItemString(d, "BAD_SCHEME_POINTER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_STREAM_HEADER + x = PyInt_FromLong(CL_BAD_STREAM_HEADER); + if (x == NULL || PyDict_SetItemString(d, "BAD_STREAM_HEADER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_STRING_POINTER + x = PyInt_FromLong(CL_BAD_STRING_POINTER); + if (x == NULL || PyDict_SetItemString(d, "BAD_STRING_POINTER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BAD_TEXT_STRING_PTR + x = PyInt_FromLong(CL_BAD_TEXT_STRING_PTR); + if (x == NULL || PyDict_SetItemString(d, "BAD_TEXT_STRING_PTR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BEST_FIT + x = PyInt_FromLong(CL_BEST_FIT); + if (x == NULL || PyDict_SetItemString(d, "BEST_FIT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BIDIRECTIONAL + x = PyInt_FromLong(CL_BIDIRECTIONAL); + if (x == NULL || PyDict_SetItemString(d, "BIDIRECTIONAL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BITRATE + x = PyInt_FromLong(CL_BITRATE); + if (x == NULL || PyDict_SetItemString(d, "BITRATE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BITRATE_POLICY + x = PyInt_FromLong(CL_BITRATE_POLICY); + if (x == NULL || PyDict_SetItemString(d, "BITRATE_POLICY", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BITRATE_TARGET + x = PyInt_FromLong(CL_BITRATE_TARGET); + if (x == NULL || PyDict_SetItemString(d, "BITRATE_TARGET", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BITS_PER_COMPONENT + x = PyInt_FromLong(CL_BITS_PER_COMPONENT); + if (x == NULL || PyDict_SetItemString(d, "BITS_PER_COMPONENT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BLENDING + x = PyInt_FromLong(CL_BLENDING); + if (x == NULL || PyDict_SetItemString(d, "BLENDING", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BLOCK_SIZE + x = PyInt_FromLong(CL_BLOCK_SIZE); + if (x == NULL || PyDict_SetItemString(d, "BLOCK_SIZE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BOTTOM_UP + x = PyInt_FromLong(CL_BOTTOM_UP); + if (x == NULL || PyDict_SetItemString(d, "BOTTOM_UP", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BUFFER_NOT_CREATED + x = PyInt_FromLong(CL_BUFFER_NOT_CREATED); + if (x == NULL || PyDict_SetItemString(d, "BUFFER_NOT_CREATED", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BUF_COMPRESSED + x = PyInt_FromLong(CL_BUF_COMPRESSED); + if (x == NULL || PyDict_SetItemString(d, "BUF_COMPRESSED", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BUF_DATA + x = PyInt_FromLong(CL_BUF_DATA); + if (x == NULL || PyDict_SetItemString(d, "BUF_DATA", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_BUF_FRAME + x = PyInt_FromLong(CL_BUF_FRAME); + if (x == NULL || PyDict_SetItemString(d, "BUF_FRAME", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_CHANNEL_POLICY + x = PyInt_FromLong(CL_CHANNEL_POLICY); + if (x == NULL || PyDict_SetItemString(d, "CHANNEL_POLICY", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_CHROMA_THRESHOLD + x = PyInt_FromLong(CL_CHROMA_THRESHOLD); + if (x == NULL || PyDict_SetItemString(d, "CHROMA_THRESHOLD", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_CODEC + x = PyInt_FromLong(CL_CODEC); + if (x == NULL || PyDict_SetItemString(d, "CODEC", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_COMPONENTS + x = PyInt_FromLong(CL_COMPONENTS); + if (x == NULL || PyDict_SetItemString(d, "COMPONENTS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_COMPRESSED_BUFFER_SIZE + x = PyInt_FromLong(CL_COMPRESSED_BUFFER_SIZE); + if (x == NULL || PyDict_SetItemString(d, "COMPRESSED_BUFFER_SIZE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_COMPRESSION_RATIO + x = PyInt_FromLong(CL_COMPRESSION_RATIO); + if (x == NULL || PyDict_SetItemString(d, "COMPRESSION_RATIO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_COMPRESSOR + x = PyInt_FromLong(CL_COMPRESSOR); + if (x == NULL || PyDict_SetItemString(d, "COMPRESSOR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_CONTINUOUS_BLOCK + x = PyInt_FromLong(CL_CONTINUOUS_BLOCK); + if (x == NULL || PyDict_SetItemString(d, "CONTINUOUS_BLOCK", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_CONTINUOUS_NONBLOCK + x = PyInt_FromLong(CL_CONTINUOUS_NONBLOCK); + if (x == NULL || PyDict_SetItemString(d, "CONTINUOUS_NONBLOCK", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_COSMO_CODEC_CONTROL + x = PyInt_FromLong(CL_COSMO_CODEC_CONTROL); + if (x == NULL || PyDict_SetItemString(d, "COSMO_CODEC_CONTROL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_COSMO_NUM_PARAMS + x = PyInt_FromLong(CL_COSMO_NUM_PARAMS); + if (x == NULL || PyDict_SetItemString(d, "COSMO_NUM_PARAMS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_COSMO_VALUE_BASE + x = PyInt_FromLong(CL_COSMO_VALUE_BASE); + if (x == NULL || PyDict_SetItemString(d, "COSMO_VALUE_BASE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_COSMO_VIDEO_MANUAL_CONTROL + x = PyInt_FromLong(CL_COSMO_VIDEO_MANUAL_CONTROL); + if (x == NULL || PyDict_SetItemString(d, "COSMO_VIDEO_MANUAL_CONTROL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_COSMO_VIDEO_TRANSFER_MODE + x = PyInt_FromLong(CL_COSMO_VIDEO_TRANSFER_MODE); + if (x == NULL || PyDict_SetItemString(d, "COSMO_VIDEO_TRANSFER_MODE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_DATA + x = PyInt_FromLong(CL_DATA); + if (x == NULL || PyDict_SetItemString(d, "DATA", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_DECOMPRESSOR + x = PyInt_FromLong(CL_DECOMPRESSOR); + if (x == NULL || PyDict_SetItemString(d, "DECOMPRESSOR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_DSO_ERROR + x = PyInt_FromLong(CL_DSO_ERROR); + if (x == NULL || PyDict_SetItemString(d, "DSO_ERROR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_EDGE_THRESHOLD + x = PyInt_FromLong(CL_EDGE_THRESHOLD); + if (x == NULL || PyDict_SetItemString(d, "EDGE_THRESHOLD", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_ENABLE_IMAGEINFO + x = PyInt_FromLong(CL_ENABLE_IMAGEINFO); + if (x == NULL || PyDict_SetItemString(d, "ENABLE_IMAGEINFO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_END_OF_SEQUENCE + x = PyInt_FromLong(CL_END_OF_SEQUENCE); + if (x == NULL || PyDict_SetItemString(d, "END_OF_SEQUENCE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_ENUM_VALUE + x = PyInt_FromLong(CL_ENUM_VALUE); + if (x == NULL || PyDict_SetItemString(d, "ENUM_VALUE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_EXACT_COMPRESSION_RATIO + x = PyInt_FromLong(CL_EXACT_COMPRESSION_RATIO); + if (x == NULL || PyDict_SetItemString(d, "EXACT_COMPRESSION_RATIO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_EXTERNAL_DEVICE + x = PyInt_FromLong((long) CL_EXTERNAL_DEVICE); + if (x == NULL || PyDict_SetItemString(d, "EXTERNAL_DEVICE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FLOATING_ENUM_VALUE + x = PyInt_FromLong(CL_FLOATING_ENUM_VALUE); + if (x == NULL || PyDict_SetItemString(d, "FLOATING_ENUM_VALUE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FLOATING_RANGE_VALUE + x = PyInt_FromLong(CL_FLOATING_RANGE_VALUE); + if (x == NULL || PyDict_SetItemString(d, "FLOATING_RANGE_VALUE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT + x = PyInt_FromLong(CL_FORMAT); + if (x == NULL || PyDict_SetItemString(d, "FORMAT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT_ABGR + x = PyInt_FromLong(CL_FORMAT_ABGR); + if (x == NULL || PyDict_SetItemString(d, "FORMAT_ABGR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT_BGR + x = PyInt_FromLong(CL_FORMAT_BGR); + if (x == NULL || PyDict_SetItemString(d, "FORMAT_BGR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT_BGR233 + x = PyInt_FromLong(CL_FORMAT_BGR233); + if (x == NULL || PyDict_SetItemString(d, "FORMAT_BGR233", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT_GRAYSCALE + x = PyInt_FromLong(CL_FORMAT_GRAYSCALE); + if (x == NULL || PyDict_SetItemString(d, "FORMAT_GRAYSCALE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT_MONO + x = PyInt_FromLong(CL_FORMAT_MONO); + if (x == NULL || PyDict_SetItemString(d, "FORMAT_MONO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT_RBG323 + x = PyInt_FromLong(CL_FORMAT_RBG323); + if (x == NULL || PyDict_SetItemString(d, "FORMAT_RBG323", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT_STEREO_INTERLEAVED + x = PyInt_FromLong(CL_FORMAT_STEREO_INTERLEAVED); + if (x == NULL || PyDict_SetItemString(d, "FORMAT_STEREO_INTERLEAVED", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT_XBGR + x = PyInt_FromLong(CL_FORMAT_XBGR); + if (x == NULL || PyDict_SetItemString(d, "FORMAT_XBGR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT_YCbCr + x = PyInt_FromLong(CL_FORMAT_YCbCr); + if (x == NULL || PyDict_SetItemString(d, "FORMAT_YCbCr", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT_YCbCr422 + x = PyInt_FromLong(CL_FORMAT_YCbCr422); + if (x == NULL || PyDict_SetItemString(d, "FORMAT_YCbCr422", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FORMAT_YCbCr422DC + x = PyInt_FromLong(CL_FORMAT_YCbCr422DC); + if (x == NULL || PyDict_SetItemString(d, "FORMAT_YCbCr422DC", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FRAME + x = PyInt_FromLong(CL_FRAME); + if (x == NULL || PyDict_SetItemString(d, "FRAME", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FRAMES_PER_CHUNK + x = PyInt_FromLong(CL_FRAMES_PER_CHUNK); + if (x == NULL || PyDict_SetItemString(d, "FRAMES_PER_CHUNK", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FRAME_BUFFER_SIZE + x = PyInt_FromLong(CL_FRAME_BUFFER_SIZE); + if (x == NULL || PyDict_SetItemString(d, "FRAME_BUFFER_SIZE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FRAME_BUFFER_SIZE_ZERO + x = PyInt_FromLong(CL_FRAME_BUFFER_SIZE_ZERO); + if (x == NULL || PyDict_SetItemString(d, "FRAME_BUFFER_SIZE_ZERO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FRAME_INDEX + x = PyInt_FromLong(CL_FRAME_INDEX); + if (x == NULL || PyDict_SetItemString(d, "FRAME_INDEX", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FRAME_RATE + x = PyInt_FromLong(CL_FRAME_RATE); + if (x == NULL || PyDict_SetItemString(d, "FRAME_RATE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FRAME_SIZE + x = PyInt_FromLong(CL_FRAME_SIZE); + if (x == NULL || PyDict_SetItemString(d, "FRAME_SIZE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_FRAME_TYPE + x = PyInt_FromLong(CL_FRAME_TYPE); + if (x == NULL || PyDict_SetItemString(d, "FRAME_TYPE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_G711_ALAW + x = PyInt_FromLong(CL_G711_ALAW); + if (x == NULL || PyDict_SetItemString(d, "G711_ALAW", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_G711_ALAW_SOFTWARE + x = PyInt_FromLong(CL_G711_ALAW_SOFTWARE); + if (x == NULL || PyDict_SetItemString(d, "G711_ALAW_SOFTWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_G711_ULAW + x = PyInt_FromLong(CL_G711_ULAW); + if (x == NULL || PyDict_SetItemString(d, "G711_ULAW", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_G711_ULAW_SOFTWARE + x = PyInt_FromLong(CL_G711_ULAW_SOFTWARE); + if (x == NULL || PyDict_SetItemString(d, "G711_ULAW_SOFTWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_GRAYSCALE + x = PyInt_FromLong(CL_GRAYSCALE); + if (x == NULL || PyDict_SetItemString(d, "GRAYSCALE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_HDCC + x = PyInt_FromLong(CL_HDCC); + if (x == NULL || PyDict_SetItemString(d, "HDCC", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_HDCC_SAMPLES_PER_TILE + x = PyInt_FromLong(CL_HDCC_SAMPLES_PER_TILE); + if (x == NULL || PyDict_SetItemString(d, "HDCC_SAMPLES_PER_TILE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_HDCC_SOFTWARE + x = PyInt_FromLong(CL_HDCC_SOFTWARE); + if (x == NULL || PyDict_SetItemString(d, "HDCC_SOFTWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_HDCC_TILE_THRESHOLD + x = PyInt_FromLong(CL_HDCC_TILE_THRESHOLD); + if (x == NULL || PyDict_SetItemString(d, "HDCC_TILE_THRESHOLD", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_HEADER_START_CODE + x = PyInt_FromLong(CL_HEADER_START_CODE); + if (x == NULL || PyDict_SetItemString(d, "HEADER_START_CODE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_IMAGEINFO_FIELDMASK + x = PyInt_FromLong(CL_IMAGEINFO_FIELDMASK); + if (x == NULL || PyDict_SetItemString(d, "IMAGEINFO_FIELDMASK", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_IMAGE_CROP_BOTTOM + x = PyInt_FromLong(CL_IMAGE_CROP_BOTTOM); + if (x == NULL || PyDict_SetItemString(d, "IMAGE_CROP_BOTTOM", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_IMAGE_CROP_LEFT + x = PyInt_FromLong(CL_IMAGE_CROP_LEFT); + if (x == NULL || PyDict_SetItemString(d, "IMAGE_CROP_LEFT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_IMAGE_CROP_RIGHT + x = PyInt_FromLong(CL_IMAGE_CROP_RIGHT); + if (x == NULL || PyDict_SetItemString(d, "IMAGE_CROP_RIGHT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_IMAGE_CROP_TOP + x = PyInt_FromLong(CL_IMAGE_CROP_TOP); + if (x == NULL || PyDict_SetItemString(d, "IMAGE_CROP_TOP", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_IMAGE_HEIGHT + x = PyInt_FromLong(CL_IMAGE_HEIGHT); + if (x == NULL || PyDict_SetItemString(d, "IMAGE_HEIGHT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_IMAGE_WIDTH + x = PyInt_FromLong(CL_IMAGE_WIDTH); + if (x == NULL || PyDict_SetItemString(d, "IMAGE_WIDTH", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_IMPACT_CODEC_CONTROL + x = PyInt_FromLong(CL_IMPACT_CODEC_CONTROL); + if (x == NULL || PyDict_SetItemString(d, "IMPACT_CODEC_CONTROL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_IMPACT_FRAME_INTERLEAVE + x = PyInt_FromLong(CL_IMPACT_FRAME_INTERLEAVE); + if (x == NULL || PyDict_SetItemString(d, "IMPACT_FRAME_INTERLEAVE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_IMPACT_NUM_PARAMS + x = PyInt_FromLong(CL_IMPACT_NUM_PARAMS); + if (x == NULL || PyDict_SetItemString(d, "IMPACT_NUM_PARAMS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_INTERNAL_FORMAT + x = PyInt_FromLong(CL_INTERNAL_FORMAT); + if (x == NULL || PyDict_SetItemString(d, "INTERNAL_FORMAT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_INTERNAL_IMAGE_HEIGHT + x = PyInt_FromLong(CL_INTERNAL_IMAGE_HEIGHT); + if (x == NULL || PyDict_SetItemString(d, "INTERNAL_IMAGE_HEIGHT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_INTERNAL_IMAGE_WIDTH + x = PyInt_FromLong(CL_INTERNAL_IMAGE_WIDTH); + if (x == NULL || PyDict_SetItemString(d, "INTERNAL_IMAGE_WIDTH", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_INTRA + x = PyInt_FromLong(CL_INTRA); + if (x == NULL || PyDict_SetItemString(d, "INTRA", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_JPEG + x = PyInt_FromLong(CL_JPEG); + if (x == NULL || PyDict_SetItemString(d, "JPEG", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_JPEG_COSMO + x = PyInt_FromLong(CL_JPEG_COSMO); + if (x == NULL || PyDict_SetItemString(d, "JPEG_COSMO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_JPEG_ERROR + x = PyInt_FromLong(CL_JPEG_ERROR); + if (x == NULL || PyDict_SetItemString(d, "JPEG_ERROR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_JPEG_IMPACT + x = PyInt_FromLong(CL_JPEG_IMPACT); + if (x == NULL || PyDict_SetItemString(d, "JPEG_IMPACT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_JPEG_NUM_PARAMS + x = PyInt_FromLong(CL_JPEG_NUM_PARAMS); + if (x == NULL || PyDict_SetItemString(d, "JPEG_NUM_PARAMS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_JPEG_QUALITY_FACTOR + x = PyInt_FromLong(CL_JPEG_QUALITY_FACTOR); + if (x == NULL || PyDict_SetItemString(d, "JPEG_QUALITY_FACTOR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_JPEG_QUANTIZATION_TABLES + x = PyInt_FromLong(CL_JPEG_QUANTIZATION_TABLES); + if (x == NULL || PyDict_SetItemString(d, "JPEG_QUANTIZATION_TABLES", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_JPEG_SOFTWARE + x = PyInt_FromLong(CL_JPEG_SOFTWARE); + if (x == NULL || PyDict_SetItemString(d, "JPEG_SOFTWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_JPEG_STREAM_HEADERS + x = PyInt_FromLong(CL_JPEG_STREAM_HEADERS); + if (x == NULL || PyDict_SetItemString(d, "JPEG_STREAM_HEADERS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_KEYFRAME + x = PyInt_FromLong(CL_KEYFRAME); + if (x == NULL || PyDict_SetItemString(d, "KEYFRAME", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_KEYFRAME_DISTANCE + x = PyInt_FromLong(CL_KEYFRAME_DISTANCE); + if (x == NULL || PyDict_SetItemString(d, "KEYFRAME_DISTANCE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_LAST_FRAME_INDEX + x = PyInt_FromLong(CL_LAST_FRAME_INDEX); + if (x == NULL || PyDict_SetItemString(d, "LAST_FRAME_INDEX", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_LAYER + x = PyInt_FromLong(CL_LAYER); + if (x == NULL || PyDict_SetItemString(d, "LAYER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_LUMA_THRESHOLD + x = PyInt_FromLong(CL_LUMA_THRESHOLD); + if (x == NULL || PyDict_SetItemString(d, "LUMA_THRESHOLD", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MAX_NUMBER_OF_AUDIO_ALGORITHMS + x = PyInt_FromLong(CL_MAX_NUMBER_OF_AUDIO_ALGORITHMS); + if (x == NULL || PyDict_SetItemString(d, "MAX_NUMBER_OF_AUDIO_ALGORITHMS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MAX_NUMBER_OF_FORMATS + x = PyInt_FromLong(CL_MAX_NUMBER_OF_FORMATS); + if (x == NULL || PyDict_SetItemString(d, "MAX_NUMBER_OF_FORMATS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MAX_NUMBER_OF_ORIGINAL_FORMATS + x = PyInt_FromLong(CL_MAX_NUMBER_OF_ORIGINAL_FORMATS); + if (x == NULL || PyDict_SetItemString(d, "MAX_NUMBER_OF_ORIGINAL_FORMATS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MAX_NUMBER_OF_PARAMS + x = PyInt_FromLong(CL_MAX_NUMBER_OF_PARAMS); + if (x == NULL || PyDict_SetItemString(d, "MAX_NUMBER_OF_PARAMS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MAX_NUMBER_OF_VIDEO_ALGORITHMS + x = PyInt_FromLong(CL_MAX_NUMBER_OF_VIDEO_ALGORITHMS); + if (x == NULL || PyDict_SetItemString(d, "MAX_NUMBER_OF_VIDEO_ALGORITHMS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MONO + x = PyInt_FromLong(CL_MONO); + if (x == NULL || PyDict_SetItemString(d, "MONO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_AUDIO_AWARE + x = PyInt_FromLong(CL_MPEG1_AUDIO_AWARE); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_AUDIO_AWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_AUDIO_LAYER + x = PyInt_FromLong(CL_MPEG1_AUDIO_LAYER); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_AUDIO_LAYER", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_AUDIO_LAYER_I + x = PyInt_FromLong(CL_MPEG1_AUDIO_LAYER_I); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_AUDIO_LAYER_I", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_AUDIO_LAYER_II + x = PyInt_FromLong(CL_MPEG1_AUDIO_LAYER_II); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_AUDIO_LAYER_II", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_AUDIO_MODE + x = PyInt_FromLong(CL_MPEG1_AUDIO_MODE); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_AUDIO_MODE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_AUDIO_MODE_DUAL + x = PyInt_FromLong(CL_MPEG1_AUDIO_MODE_DUAL); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_AUDIO_MODE_DUAL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_AUDIO_MODE_JOINT + x = PyInt_FromLong(CL_MPEG1_AUDIO_MODE_JOINT); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_AUDIO_MODE_JOINT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_AUDIO_MODE_SINGLE + x = PyInt_FromLong(CL_MPEG1_AUDIO_MODE_SINGLE); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_AUDIO_MODE_SINGLE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_AUDIO_MODE_STEREO + x = PyInt_FromLong(CL_MPEG1_AUDIO_MODE_STEREO); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_AUDIO_MODE_STEREO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_AUDIO_SOFTWARE + x = PyInt_FromLong(CL_MPEG1_AUDIO_SOFTWARE); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_AUDIO_SOFTWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_END_OF_STREAM + x = PyInt_FromLong(CL_MPEG1_END_OF_STREAM); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_END_OF_STREAM", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_ERROR + x = PyInt_FromLong(CL_MPEG1_ERROR); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_ERROR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_NUM_PARAMS + x = PyInt_FromLong(CL_MPEG1_NUM_PARAMS); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_NUM_PARAMS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_VIDEO_M + x = PyInt_FromLong(CL_MPEG1_VIDEO_M); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_VIDEO_M", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_B_X + x = PyInt_FromLong(CL_MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_B_X); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_B_X", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_B_Y + x = PyInt_FromLong(CL_MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_B_Y); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_B_Y", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_P_X + x = PyInt_FromLong(CL_MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_P_X); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_P_X", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_P_Y + x = PyInt_FromLong(CL_MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_P_Y); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_VIDEO_MAX_MOTION_VECTOR_LENGTH_P_Y", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_VIDEO_N + x = PyInt_FromLong(CL_MPEG1_VIDEO_N); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_VIDEO_N", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_VIDEO_SOFTNESS + x = PyInt_FromLong(CL_MPEG1_VIDEO_SOFTNESS); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_VIDEO_SOFTNESS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_VIDEO_SOFTNESS_MAXIMUM + x = PyInt_FromLong(CL_MPEG1_VIDEO_SOFTNESS_MAXIMUM); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_VIDEO_SOFTNESS_MAXIMUM", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_VIDEO_SOFTNESS_MEDIUM + x = PyInt_FromLong(CL_MPEG1_VIDEO_SOFTNESS_MEDIUM); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_VIDEO_SOFTNESS_MEDIUM", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_VIDEO_SOFTNESS_NONE + x = PyInt_FromLong(CL_MPEG1_VIDEO_SOFTNESS_NONE); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_VIDEO_SOFTNESS_NONE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG1_VIDEO_SOFTWARE + x = PyInt_FromLong(CL_MPEG1_VIDEO_SOFTWARE); + if (x == NULL || PyDict_SetItemString(d, "MPEG1_VIDEO_SOFTWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MPEG_VIDEO + x = PyInt_FromLong(CL_MPEG_VIDEO); + if (x == NULL || PyDict_SetItemString(d, "MPEG_VIDEO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MULTIRATE_AWARE + x = PyInt_FromLong(CL_MULTIRATE_AWARE); + if (x == NULL || PyDict_SetItemString(d, "MULTIRATE_AWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC1 + x = PyInt_FromLong(CL_MVC1); + if (x == NULL || PyDict_SetItemString(d, "MVC1", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC1_SOFTWARE + x = PyInt_FromLong(CL_MVC1_SOFTWARE); + if (x == NULL || PyDict_SetItemString(d, "MVC1_SOFTWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC2 + x = PyInt_FromLong(CL_MVC2); + if (x == NULL || PyDict_SetItemString(d, "MVC2", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC2_BLENDING + x = PyInt_FromLong(CL_MVC2_BLENDING); + if (x == NULL || PyDict_SetItemString(d, "MVC2_BLENDING", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC2_BLENDING_OFF + x = PyInt_FromLong(CL_MVC2_BLENDING_OFF); + if (x == NULL || PyDict_SetItemString(d, "MVC2_BLENDING_OFF", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC2_BLENDING_ON + x = PyInt_FromLong(CL_MVC2_BLENDING_ON); + if (x == NULL || PyDict_SetItemString(d, "MVC2_BLENDING_ON", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC2_CHROMA_THRESHOLD + x = PyInt_FromLong(CL_MVC2_CHROMA_THRESHOLD); + if (x == NULL || PyDict_SetItemString(d, "MVC2_CHROMA_THRESHOLD", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC2_EDGE_THRESHOLD + x = PyInt_FromLong(CL_MVC2_EDGE_THRESHOLD); + if (x == NULL || PyDict_SetItemString(d, "MVC2_EDGE_THRESHOLD", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC2_ERROR + x = PyInt_FromLong(CL_MVC2_ERROR); + if (x == NULL || PyDict_SetItemString(d, "MVC2_ERROR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC2_LUMA_THRESHOLD + x = PyInt_FromLong(CL_MVC2_LUMA_THRESHOLD); + if (x == NULL || PyDict_SetItemString(d, "MVC2_LUMA_THRESHOLD", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC2_SOFTWARE + x = PyInt_FromLong(CL_MVC2_SOFTWARE); + if (x == NULL || PyDict_SetItemString(d, "MVC2_SOFTWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC3_QUALITY_LEVEL + x = PyInt_FromLong(CL_MVC3_QUALITY_LEVEL); + if (x == NULL || PyDict_SetItemString(d, "MVC3_QUALITY_LEVEL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_MVC3_SOFTWARE + x = PyInt_FromLong(CL_MVC3_SOFTWARE); + if (x == NULL || PyDict_SetItemString(d, "MVC3_SOFTWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_NEXT_NOT_AVAILABLE + x = PyInt_FromLong(CL_NEXT_NOT_AVAILABLE); + if (x == NULL || PyDict_SetItemString(d, "NEXT_NOT_AVAILABLE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_NOISE_MARGIN + x = PyInt_FromLong(CL_NOISE_MARGIN); + if (x == NULL || PyDict_SetItemString(d, "NOISE_MARGIN", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_NONE + x = PyInt_FromLong(CL_NONE); + if (x == NULL || PyDict_SetItemString(d, "NONE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_NUMBER_OF_FORMATS + x = PyInt_FromLong(CL_NUMBER_OF_FORMATS); + if (x == NULL || PyDict_SetItemString(d, "NUMBER_OF_FORMATS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_NUMBER_OF_FRAMES + x = PyInt_FromLong(CL_NUMBER_OF_FRAMES); + if (x == NULL || PyDict_SetItemString(d, "NUMBER_OF_FRAMES", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_NUMBER_OF_PARAMS + x = PyInt_FromLong(CL_NUMBER_OF_PARAMS); + if (x == NULL || PyDict_SetItemString(d, "NUMBER_OF_PARAMS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_NUMBER_OF_PARAMS_FREEZE + x = PyInt_FromLong(CL_NUMBER_OF_PARAMS_FREEZE); + if (x == NULL || PyDict_SetItemString(d, "NUMBER_OF_PARAMS_FREEZE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_NUMBER_OF_VIDEO_FORMATS + x = PyInt_FromLong(CL_NUMBER_OF_VIDEO_FORMATS); + if (x == NULL || PyDict_SetItemString(d, "NUMBER_OF_VIDEO_FORMATS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_ORIENTATION + x = PyInt_FromLong(CL_ORIENTATION); + if (x == NULL || PyDict_SetItemString(d, "ORIENTATION", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_ORIGINAL_FORMAT + x = PyInt_FromLong(CL_ORIGINAL_FORMAT); + if (x == NULL || PyDict_SetItemString(d, "ORIGINAL_FORMAT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_PARAM_OUT_OF_RANGE + x = PyInt_FromLong(CL_PARAM_OUT_OF_RANGE); + if (x == NULL || PyDict_SetItemString(d, "PARAM_OUT_OF_RANGE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_PIXEL_ASPECT + x = PyInt_FromLong(CL_PIXEL_ASPECT); + if (x == NULL || PyDict_SetItemString(d, "PIXEL_ASPECT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_PREDICTED + x = PyInt_FromLong(CL_PREDICTED); + if (x == NULL || PyDict_SetItemString(d, "PREDICTED", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_PREROLL + x = PyInt_FromLong(CL_PREROLL); + if (x == NULL || PyDict_SetItemString(d, "PREROLL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_QUALITY_FACTOR + x = PyInt_FromLong(CL_QUALITY_FACTOR); + if (x == NULL || PyDict_SetItemString(d, "QUALITY_FACTOR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_QUALITY_LEVEL + x = PyInt_FromLong(CL_QUALITY_LEVEL); + if (x == NULL || PyDict_SetItemString(d, "QUALITY_LEVEL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_QUALITY_SPATIAL + x = PyInt_FromLong(CL_QUALITY_SPATIAL); + if (x == NULL || PyDict_SetItemString(d, "QUALITY_SPATIAL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_QUALITY_TEMPORAL + x = PyInt_FromLong(CL_QUALITY_TEMPORAL); + if (x == NULL || PyDict_SetItemString(d, "QUALITY_TEMPORAL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_QUANTIZATION_TABLES + x = PyInt_FromLong(CL_QUANTIZATION_TABLES); + if (x == NULL || PyDict_SetItemString(d, "QUANTIZATION_TABLES", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RANGE_VALUE + x = PyInt_FromLong(CL_RANGE_VALUE); + if (x == NULL || PyDict_SetItemString(d, "RANGE_VALUE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RGB + x = PyInt_FromLong(CL_RGB); + if (x == NULL || PyDict_SetItemString(d, "RGB", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RGB332 + x = PyInt_FromLong(CL_RGB332); + if (x == NULL || PyDict_SetItemString(d, "RGB332", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RGB8 + x = PyInt_FromLong(CL_RGB8); + if (x == NULL || PyDict_SetItemString(d, "RGB8", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RGBA + x = PyInt_FromLong(CL_RGBA); + if (x == NULL || PyDict_SetItemString(d, "RGBA", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RGBX + x = PyInt_FromLong(CL_RGBX); + if (x == NULL || PyDict_SetItemString(d, "RGBX", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RLE + x = PyInt_FromLong(CL_RLE); + if (x == NULL || PyDict_SetItemString(d, "RLE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RLE24 + x = PyInt_FromLong(CL_RLE24); + if (x == NULL || PyDict_SetItemString(d, "RLE24", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RLE24_SOFTWARE + x = PyInt_FromLong(CL_RLE24_SOFTWARE); + if (x == NULL || PyDict_SetItemString(d, "RLE24_SOFTWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RLE_SOFTWARE + x = PyInt_FromLong(CL_RLE_SOFTWARE); + if (x == NULL || PyDict_SetItemString(d, "RLE_SOFTWARE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RTR + x = PyInt_FromLong(CL_RTR); + if (x == NULL || PyDict_SetItemString(d, "RTR", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RTR1 + x = PyInt_FromLong(CL_RTR1); + if (x == NULL || PyDict_SetItemString(d, "RTR1", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_RTR_QUALITY_LEVEL + x = PyInt_FromLong(CL_RTR_QUALITY_LEVEL); + if (x == NULL || PyDict_SetItemString(d, "RTR_QUALITY_LEVEL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_SAMPLES_PER_TILE + x = PyInt_FromLong(CL_SAMPLES_PER_TILE); + if (x == NULL || PyDict_SetItemString(d, "SAMPLES_PER_TILE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_SCHEME_BUSY + x = PyInt_FromLong(CL_SCHEME_BUSY); + if (x == NULL || PyDict_SetItemString(d, "SCHEME_BUSY", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_SCHEME_NOT_AVAILABLE + x = PyInt_FromLong(CL_SCHEME_NOT_AVAILABLE); + if (x == NULL || PyDict_SetItemString(d, "SCHEME_NOT_AVAILABLE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_SPEED + x = PyInt_FromLong(CL_SPEED); + if (x == NULL || PyDict_SetItemString(d, "SPEED", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_STEREO_INTERLEAVED + x = PyInt_FromLong(CL_STEREO_INTERLEAVED); + if (x == NULL || PyDict_SetItemString(d, "STEREO_INTERLEAVED", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_STREAM_HEADERS + x = PyInt_FromLong(CL_STREAM_HEADERS); + if (x == NULL || PyDict_SetItemString(d, "STREAM_HEADERS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_TILE_THRESHOLD + x = PyInt_FromLong(CL_TILE_THRESHOLD); + if (x == NULL || PyDict_SetItemString(d, "TILE_THRESHOLD", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_TOP_DOWN + x = PyInt_FromLong(CL_TOP_DOWN); + if (x == NULL || PyDict_SetItemString(d, "TOP_DOWN", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_ULAW + x = PyInt_FromLong(CL_ULAW); + if (x == NULL || PyDict_SetItemString(d, "ULAW", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_UNCOMPRESSED + x = PyInt_FromLong(CL_UNCOMPRESSED); + if (x == NULL || PyDict_SetItemString(d, "UNCOMPRESSED", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_UNCOMPRESSED_AUDIO + x = PyInt_FromLong(CL_UNCOMPRESSED_AUDIO); + if (x == NULL || PyDict_SetItemString(d, "UNCOMPRESSED_AUDIO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_UNCOMPRESSED_VIDEO + x = PyInt_FromLong(CL_UNCOMPRESSED_VIDEO); + if (x == NULL || PyDict_SetItemString(d, "UNCOMPRESSED_VIDEO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_UNKNOWN_SCHEME + x = PyInt_FromLong(CL_UNKNOWN_SCHEME); + if (x == NULL || PyDict_SetItemString(d, "UNKNOWN_SCHEME", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_VIDEO + x = PyInt_FromLong(CL_VIDEO); + if (x == NULL || PyDict_SetItemString(d, "VIDEO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_Y + x = PyInt_FromLong(CL_Y); + if (x == NULL || PyDict_SetItemString(d, "Y", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_YCbCr + x = PyInt_FromLong(CL_YCbCr); + if (x == NULL || PyDict_SetItemString(d, "YCbCr", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_YCbCr422 + x = PyInt_FromLong(CL_YCbCr422); + if (x == NULL || PyDict_SetItemString(d, "YCbCr422", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_YCbCr422DC + x = PyInt_FromLong(CL_YCbCr422DC); + if (x == NULL || PyDict_SetItemString(d, "YCbCr422DC", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_YCbCr422HC + x = PyInt_FromLong(CL_YCbCr422HC); + if (x == NULL || PyDict_SetItemString(d, "YCbCr422HC", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_YUV + x = PyInt_FromLong(CL_YUV); + if (x == NULL || PyDict_SetItemString(d, "YUV", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_YUV422 + x = PyInt_FromLong(CL_YUV422); + if (x == NULL || PyDict_SetItemString(d, "YUV422", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_YUV422DC + x = PyInt_FromLong(CL_YUV422DC); + if (x == NULL || PyDict_SetItemString(d, "YUV422DC", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef CL_YUV422HC + x = PyInt_FromLong(CL_YUV422HC); + if (x == NULL || PyDict_SetItemString(d, "YUV422HC", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef AWCMP_STEREO + x = PyInt_FromLong(AWCMP_STEREO); + if (x == NULL || PyDict_SetItemString(d, "AWCMP_STEREO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef AWCMP_JOINT_STEREO + x = PyInt_FromLong(AWCMP_JOINT_STEREO); + if (x == NULL || PyDict_SetItemString(d, "AWCMP_JOINT_STEREO", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef AWCMP_INDEPENDENT + x = PyInt_FromLong(AWCMP_INDEPENDENT); + if (x == NULL || PyDict_SetItemString(d, "AWCMP_INDEPENDENT", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef AWCMP_FIXED_RATE + x = PyInt_FromLong(AWCMP_FIXED_RATE); + if (x == NULL || PyDict_SetItemString(d, "AWCMP_FIXED_RATE", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef AWCMP_CONST_QUAL + x = PyInt_FromLong(AWCMP_CONST_QUAL); + if (x == NULL || PyDict_SetItemString(d, "AWCMP_CONST_QUAL", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef AWCMP_LOSSLESS + x = PyInt_FromLong(AWCMP_LOSSLESS); + if (x == NULL || PyDict_SetItemString(d, "AWCMP_LOSSLESS", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef AWCMP_MPEG_LAYER_I + x = PyInt_FromLong(AWCMP_MPEG_LAYER_I); + if (x == NULL || PyDict_SetItemString(d, "AWCMP_MPEG_LAYER_I", x) < 0) + return; + Py_DECREF(x); +#endif +#ifdef AWCMP_MPEG_LAYER_II + x = PyInt_FromLong(AWCMP_MPEG_LAYER_II); + if (x == NULL || PyDict_SetItemString(d, "AWCMP_MPEG_LAYER_II", x) < 0) + return; + Py_DECREF(x); +#endif + + (void) clSetErrorHandler(cl_ErrorHandler); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cmathmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cmathmodule.c new file mode 100644 index 00000000..9d767ab0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cmathmodule.c @@ -0,0 +1,404 @@ +/* Complex math module */ + +/* much code borrowed from mathmodule.c */ + +#include "Python.h" + +#ifndef M_PI +#define M_PI (3.141592653589793239) +#endif + +/* First, the C functions that do the real work */ + +/* constants */ +static Py_complex c_one = {1., 0.}; +static Py_complex c_half = {0.5, 0.}; +static Py_complex c_i = {0., 1.}; +static Py_complex c_halfi = {0., 0.5}; + +/* forward declarations */ +static Py_complex c_log(Py_complex); +static Py_complex c_prodi(Py_complex); +static Py_complex c_sqrt(Py_complex); + + +static Py_complex +c_acos(Py_complex x) +{ + return c_neg(c_prodi(c_log(c_sum(x,c_prod(c_i, + c_sqrt(c_diff(c_one,c_prod(x,x)))))))); +} + +PyDoc_STRVAR(c_acos_doc, +"acos(x)\n" +"\n" +"Return the arc cosine of x."); + + +static Py_complex +c_acosh(Py_complex x) +{ + Py_complex z; + z = c_sqrt(c_half); + z = c_log(c_prod(z, c_sum(c_sqrt(c_sum(x,c_one)), + c_sqrt(c_diff(x,c_one))))); + return c_sum(z, z); +} + +PyDoc_STRVAR(c_acosh_doc, +"acosh(x)\n" +"\n" +"Return the hyperbolic arccosine of x."); + + +static Py_complex +c_asin(Py_complex x) +{ + /* -i * log[(sqrt(1-x**2) + i*x] */ + const Py_complex squared = c_prod(x, x); + const Py_complex sqrt_1_minus_x_sq = c_sqrt(c_diff(c_one, squared)); + return c_neg(c_prodi(c_log( + c_sum(sqrt_1_minus_x_sq, c_prodi(x)) + ) ) ); +} + +PyDoc_STRVAR(c_asin_doc, +"asin(x)\n" +"\n" +"Return the arc sine of x."); + + +static Py_complex +c_asinh(Py_complex x) +{ + Py_complex z; + z = c_sqrt(c_half); + z = c_log(c_prod(z, c_sum(c_sqrt(c_sum(x, c_i)), + c_sqrt(c_diff(x, c_i))))); + return c_sum(z, z); +} + +PyDoc_STRVAR(c_asinh_doc, +"asinh(x)\n" +"\n" +"Return the hyperbolic arc sine of x."); + + +static Py_complex +c_atan(Py_complex x) +{ + return c_prod(c_halfi,c_log(c_quot(c_sum(c_i,x),c_diff(c_i,x)))); +} + +PyDoc_STRVAR(c_atan_doc, +"atan(x)\n" +"\n" +"Return the arc tangent of x."); + + +static Py_complex +c_atanh(Py_complex x) +{ + return c_prod(c_half,c_log(c_quot(c_sum(c_one,x),c_diff(c_one,x)))); +} + +PyDoc_STRVAR(c_atanh_doc, +"atanh(x)\n" +"\n" +"Return the hyperbolic arc tangent of x."); + + +static Py_complex +c_cos(Py_complex x) +{ + Py_complex r; + r.real = cos(x.real)*cosh(x.imag); + r.imag = -sin(x.real)*sinh(x.imag); + return r; +} + +PyDoc_STRVAR(c_cos_doc, +"cos(x)\n" +"n" +"Return the cosine of x."); + + +static Py_complex +c_cosh(Py_complex x) +{ + Py_complex r; + r.real = cos(x.imag)*cosh(x.real); + r.imag = sin(x.imag)*sinh(x.real); + return r; +} + +PyDoc_STRVAR(c_cosh_doc, +"cosh(x)\n" +"n" +"Return the hyperbolic cosine of x."); + + +static Py_complex +c_exp(Py_complex x) +{ + Py_complex r; + double l = exp(x.real); + r.real = l*cos(x.imag); + r.imag = l*sin(x.imag); + return r; +} + +PyDoc_STRVAR(c_exp_doc, +"exp(x)\n" +"\n" +"Return the exponential value e**x."); + + +static Py_complex +c_log(Py_complex x) +{ + Py_complex r; + double l = hypot(x.real,x.imag); + r.imag = atan2(x.imag, x.real); + r.real = log(l); + return r; +} + +PyDoc_STRVAR(c_log_doc, +"log(x)\n" +"\n" +"Return the natural logarithm of x."); + + +static Py_complex +c_log10(Py_complex x) +{ + Py_complex r; + double l = hypot(x.real,x.imag); + r.imag = atan2(x.imag, x.real)/log(10.); + r.real = log10(l); + return r; +} + +PyDoc_STRVAR(c_log10_doc, +"log10(x)\n" +"\n" +"Return the base-10 logarithm of x."); + + +/* internal function not available from Python */ +static Py_complex +c_prodi(Py_complex x) +{ + Py_complex r; + r.real = -x.imag; + r.imag = x.real; + return r; +} + + +static Py_complex +c_sin(Py_complex x) +{ + Py_complex r; + r.real = sin(x.real) * cosh(x.imag); + r.imag = cos(x.real) * sinh(x.imag); + return r; +} + +PyDoc_STRVAR(c_sin_doc, +"sin(x)\n" +"\n" +"Return the sine of x."); + + +static Py_complex +c_sinh(Py_complex x) +{ + Py_complex r; + r.real = cos(x.imag) * sinh(x.real); + r.imag = sin(x.imag) * cosh(x.real); + return r; +} + +PyDoc_STRVAR(c_sinh_doc, +"sinh(x)\n" +"\n" +"Return the hyperbolic sine of x."); + + +static Py_complex +c_sqrt(Py_complex x) +{ + Py_complex r; + double s,d; + if (x.real == 0. && x.imag == 0.) + r = x; + else { + s = sqrt(0.5*(fabs(x.real) + hypot(x.real,x.imag))); + d = 0.5*x.imag/s; + if (x.real > 0.) { + r.real = s; + r.imag = d; + } + else if (x.imag >= 0.) { + r.real = d; + r.imag = s; + } + else { + r.real = -d; + r.imag = -s; + } + } + return r; +} + +PyDoc_STRVAR(c_sqrt_doc, +"sqrt(x)\n" +"\n" +"Return the square root of x."); + + +static Py_complex +c_tan(Py_complex x) +{ + Py_complex r; + double sr,cr,shi,chi; + double rs,is,rc,ic; + double d; + sr = sin(x.real); + cr = cos(x.real); + shi = sinh(x.imag); + chi = cosh(x.imag); + rs = sr * chi; + is = cr * shi; + rc = cr * chi; + ic = -sr * shi; + d = rc*rc + ic * ic; + r.real = (rs*rc + is*ic) / d; + r.imag = (is*rc - rs*ic) / d; + return r; +} + +PyDoc_STRVAR(c_tan_doc, +"tan(x)\n" +"\n" +"Return the tangent of x."); + + +static Py_complex +c_tanh(Py_complex x) +{ + Py_complex r; + double si,ci,shr,chr; + double rs,is,rc,ic; + double d; + si = sin(x.imag); + ci = cos(x.imag); + shr = sinh(x.real); + chr = cosh(x.real); + rs = ci * shr; + is = si * chr; + rc = ci * chr; + ic = si * shr; + d = rc*rc + ic*ic; + r.real = (rs*rc + is*ic) / d; + r.imag = (is*rc - rs*ic) / d; + return r; +} + +PyDoc_STRVAR(c_tanh_doc, +"tanh(x)\n" +"\n" +"Return the hyperbolic tangent of x."); + + +/* And now the glue to make them available from Python: */ + +static PyObject * +math_error(void) +{ + if (errno == EDOM) + PyErr_SetString(PyExc_ValueError, "math domain error"); + else if (errno == ERANGE) + PyErr_SetString(PyExc_OverflowError, "math range error"); + else /* Unexpected math error */ + PyErr_SetFromErrno(PyExc_ValueError); + return NULL; +} + +static PyObject * +math_1(PyObject *args, Py_complex (*func)(Py_complex)) +{ + Py_complex x; + if (!PyArg_ParseTuple(args, "D", &x)) + return NULL; + errno = 0; + PyFPE_START_PROTECT("complex function", return 0) + x = (*func)(x); + PyFPE_END_PROTECT(x) + Py_ADJUST_ERANGE2(x.real, x.imag); + if (errno != 0) + return math_error(); + else + return PyComplex_FromCComplex(x); +} + +#define FUNC1(stubname, func) \ + static PyObject * stubname(PyObject *self, PyObject *args) { \ + return math_1(args, func); \ + } + +FUNC1(cmath_acos, c_acos) +FUNC1(cmath_acosh, c_acosh) +FUNC1(cmath_asin, c_asin) +FUNC1(cmath_asinh, c_asinh) +FUNC1(cmath_atan, c_atan) +FUNC1(cmath_atanh, c_atanh) +FUNC1(cmath_cos, c_cos) +FUNC1(cmath_cosh, c_cosh) +FUNC1(cmath_exp, c_exp) +FUNC1(cmath_log, c_log) +FUNC1(cmath_log10, c_log10) +FUNC1(cmath_sin, c_sin) +FUNC1(cmath_sinh, c_sinh) +FUNC1(cmath_sqrt, c_sqrt) +FUNC1(cmath_tan, c_tan) +FUNC1(cmath_tanh, c_tanh) + + +PyDoc_STRVAR(module_doc, +"This module is always available. It provides access to mathematical\n" +"functions for complex numbers."); + +static PyMethodDef cmath_methods[] = { + {"acos", cmath_acos, METH_VARARGS, c_acos_doc}, + {"acosh", cmath_acosh, METH_VARARGS, c_acosh_doc}, + {"asin", cmath_asin, METH_VARARGS, c_asin_doc}, + {"asinh", cmath_asinh, METH_VARARGS, c_asinh_doc}, + {"atan", cmath_atan, METH_VARARGS, c_atan_doc}, + {"atanh", cmath_atanh, METH_VARARGS, c_atanh_doc}, + {"cos", cmath_cos, METH_VARARGS, c_cos_doc}, + {"cosh", cmath_cosh, METH_VARARGS, c_cosh_doc}, + {"exp", cmath_exp, METH_VARARGS, c_exp_doc}, + {"log", cmath_log, METH_VARARGS, c_log_doc}, + {"log10", cmath_log10, METH_VARARGS, c_log10_doc}, + {"sin", cmath_sin, METH_VARARGS, c_sin_doc}, + {"sinh", cmath_sinh, METH_VARARGS, c_sinh_doc}, + {"sqrt", cmath_sqrt, METH_VARARGS, c_sqrt_doc}, + {"tan", cmath_tan, METH_VARARGS, c_tan_doc}, + {"tanh", cmath_tanh, METH_VARARGS, c_tanh_doc}, + {NULL, NULL} /* sentinel */ +}; + +PyMODINIT_FUNC +initcmath(void) +{ + PyObject *m; + + m = Py_InitModule3("cmath", cmath_methods, module_doc); + + PyModule_AddObject(m, "pi", + PyFloat_FromDouble(atan(1.0) * 4.0)); + PyModule_AddObject(m, "e", PyFloat_FromDouble(exp(1.0))); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/config.c.in b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/config.c.in new file mode 100644 index 00000000..0b782021 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/config.c.in @@ -0,0 +1,48 @@ +/* -*- C -*- *********************************************** +Copyright (c) 2000, BeOpen.com. +Copyright (c) 1995-2000, Corporation for National Research Initiatives. +Copyright (c) 1990-1995, Stichting Mathematisch Centrum. +All rights reserved. + +See the file "Misc/COPYRIGHT" for information on usage and +redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. +******************************************************************/ + +/* Module configuration */ + +/* !!! !!! !!! This file is edited by the makesetup script !!! !!! !!! */ + +/* This file contains the table of built-in modules. + See init_builtin() in import.c. */ + +#include "Python.h" + + +/* -- ADDMODULE MARKER 1 -- */ + +extern void PyMarshal_Init(void); +extern void initimp(void); +extern void initgc(void); + +struct _inittab _PyImport_Inittab[] = { + +/* -- ADDMODULE MARKER 2 -- */ + + /* This module lives in marshal.c */ + {"marshal", PyMarshal_Init}, + + /* This lives in import.c */ + {"imp", initimp}, + + /* These entries are here for sys.builtin_module_names */ + {"__main__", NULL}, + {"__builtin__", NULL}, + {"sys", NULL}, + {"exceptions", NULL}, + + /* This lives in gcmodule.c */ + {"gc", initgc}, + + /* Sentinel */ + {0, 0} +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cryptmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cryptmodule.c new file mode 100644 index 00000000..757166d7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cryptmodule.c @@ -0,0 +1,44 @@ +/* cryptmodule.c - by Steve Majewski + */ + +#include "Python.h" + +#include + + +/* Module crypt */ + + +static PyObject *crypt_crypt(PyObject *self, PyObject *args) +{ + char *word, *salt; + extern char * crypt(const char *, const char *); + + if (!PyArg_ParseTuple(args, "ss:crypt", &word, &salt)) { + return NULL; + } + /* On some platforms (AtheOS) crypt returns NULL for an invalid + salt. Return None in that case. XXX Maybe raise an exception? */ + return Py_BuildValue("s", crypt(word, salt)); + +} + +PyDoc_STRVAR(crypt_crypt__doc__, +"crypt(word, salt) -> string\n\ +word will usually be a user's password. salt is a 2-character string\n\ +which will be used to select one of 4096 variations of DES. The characters\n\ +in salt must be either \".\", \"/\", or an alphanumeric character. Returns\n\ +the hashed password as a string, which will be composed of characters from\n\ +the same alphabet as the salt."); + + +static PyMethodDef crypt_methods[] = { + {"crypt", crypt_crypt, METH_VARARGS, crypt_crypt__doc__}, + {NULL, NULL} /* sentinel */ +}; + +PyMODINIT_FUNC +initcrypt(void) +{ + Py_InitModule("crypt", crypt_methods); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cstubs b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cstubs new file mode 100644 index 00000000..8448ae24 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/cstubs @@ -0,0 +1,1364 @@ + +/* +Input used to generate the Python module "glmodule.c". +The stub generator is a Python script called "cgen.py". + +Each definition must be contained on one line: + + + + can be: void, short, long (XXX maybe others?) + + can be: char, string, short, float, long, or double + string indicates a null terminated string; + if is char and begins with a *, the * is stripped + and is changed into string + + has the form or [] + where can be + s: arg is sent + r: arg is received (arg is a pointer) + and can be (N and I are numbers): + N + argI + retval + N*argI + N*I + N*retval + In the case where the subscript consists of two parts + separated by *, the first part is the width of the matrix, and + the second part is the length of the matrix. This order is + opposite from the order used in C to declare a two-dimensional + matrix. +*/ + +/* + * An attempt has been made to make this module switch threads on qread + * calls. It is far from safe, though. + */ + +#include +#include + +#ifdef __sgi +extern int devport(); +extern int textwritemask(); +extern int pagewritemask(); +extern int gewrite(); +extern int gettp(); +#endif + +#include "Python.h" +#include "cgensupport.h" + +/* +Some stubs are too complicated for the stub generator. +We can include manually written versions of them here. +A line starting with '%' gives the name of the function so the stub +generator can include it in the table of functions. +*/ + +% qread + +static PyObject * +gl_qread(self, args) + PyObject *self; + PyObject *args; +{ + long retval; + short arg1 ; + Py_BEGIN_ALLOW_THREADS + retval = qread( & arg1 ); + Py_END_ALLOW_THREADS + { PyObject *v = PyTuple_New( 2 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewlongobject(retval)); + PyTuple_SetItem(v, 1, mknewshortobject(arg1)); + return v; + } +} + + +/* +varray -- an array of v.. calls. +The argument is an array (maybe list or tuple) of points. +Each point must be a tuple or list of coordinates (x, y, z). +The points may be 2- or 3-dimensional but must all have the +same dimension. Float and int values may be mixed however. +The points are always converted to 3D double precision points +by assuming z=0.0 if necessary (as indicated in the man page), +and for each point v3d() is called. +*/ + +% varray + +static PyObject * +gl_varray(self, args) + PyObject *self; + PyObject *args; +{ + PyObject *v, *w=NULL; + int i, n, width; + double vec[3]; + PyObject * (*getitem)(PyObject *, int); + + if (!PyArg_GetObject(args, 1, 0, &v)) + return NULL; + + if (PyList_Check(v)) { + n = PyList_Size(v); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(v)) { + n = PyTuple_Size(v); + getitem = PyTuple_GetItem; + } + else { + PyErr_BadArgument(); + return NULL; + } + + if (n == 0) { + Py_INCREF(Py_None); + return Py_None; + } + if (n > 0) + w = (*getitem)(v, 0); + + width = 0; + if (w == NULL) { + } + else if (PyList_Check(w)) { + width = PyList_Size(w); + } + else if (PyTuple_Check(w)) { + width = PyTuple_Size(w); + } + + switch (width) { + case 2: + vec[2] = 0.0; + /* Fall through */ + case 3: + break; + default: + PyErr_BadArgument(); + return NULL; + } + + for (i = 0; i < n; i++) { + w = (*getitem)(v, i); + if (!PyArg_GetDoubleArray(w, 1, 0, width, vec)) + return NULL; + v3d(vec); + } + + Py_INCREF(Py_None); + return Py_None; +} + +/* +vnarray, nvarray -- an array of n3f and v3f calls. +The argument is an array (list or tuple) of pairs of points and normals. +Each pair is a tuple (NOT a list) of a point and a normal for that point. +Each point or normal must be a tuple (NOT a list) of coordinates (x, y, z). +Three coordinates must be given. Float and int values may be mixed. +For each pair, n3f() is called for the normal, and then v3f() is called +for the vector. + +vnarray and nvarray differ only in the order of the vector and normal in +the pair: vnarray expects (v, n) while nvarray expects (n, v). +*/ + +static PyObject *gen_nvarray(); /* Forward */ + +% nvarray + +static PyObject * +gl_nvarray(self, args) + PyObject *self; + PyObject *args; +{ + return gen_nvarray(args, 0); +} + +% vnarray + +static PyObject * +gl_vnarray(self, args) + PyObject *self; + PyObject *args; +{ + return gen_nvarray(args, 1); +} + +/* Generic, internal version of {nv,nv}array: inorm indicates the + argument order, 0: normal first, 1: vector first. */ + +static PyObject * +gen_nvarray(args, inorm) + PyObject *args; + int inorm; +{ + PyObject *v, *w, *wnorm, *wvec; + int i, n; + float norm[3], vec[3]; + PyObject * (*getitem)(PyObject *, int); + + if (!PyArg_GetObject(args, 1, 0, &v)) + return NULL; + + if (PyList_Check(v)) { + n = PyList_Size(v); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(v)) { + n = PyTuple_Size(v); + getitem = PyTuple_GetItem; + } + else { + PyErr_BadArgument(); + return NULL; + } + + for (i = 0; i < n; i++) { + w = (*getitem)(v, i); + if (!PyTuple_Check(w) || PyTuple_Size(w) != 2) { + PyErr_BadArgument(); + return NULL; + } + wnorm = PyTuple_GetItem(w, inorm); + wvec = PyTuple_GetItem(w, 1 - inorm); + if (!PyArg_GetFloatArray(wnorm, 1, 0, 3, norm) || + !PyArg_GetFloatArray(wvec, 1, 0, 3, vec)) + return NULL; + n3f(norm); + v3f(vec); + } + + Py_INCREF(Py_None); + return Py_None; +} + +/* nurbssurface(s_knots[], t_knots[], ctl[][], s_order, t_order, type). + The dimensions of ctl[] are computed as follows: + [len(s_knots) - s_order], [len(t_knots) - t_order] +*/ + +% nurbssurface + +static PyObject * +gl_nurbssurface(self, args) + PyObject *self; + PyObject *args; +{ + long arg1 ; + double * arg2 ; + long arg3 ; + double * arg4 ; + double *arg5 ; + long arg6 ; + long arg7 ; + long arg8 ; + long ncoords; + long s_byte_stride, t_byte_stride; + long s_nctl, t_nctl; + long s, t; + PyObject *v, *w, *pt; + double *pnext; + if (!PyArg_GetLongArraySize(args, 6, 0, &arg1)) + return NULL; + if ((arg2 = PyMem_NEW(double, arg1 )) == NULL) { + return PyErr_NoMemory(); + } + if (!PyArg_GetDoubleArray(args, 6, 0, arg1 , arg2)) + return NULL; + if (!PyArg_GetLongArraySize(args, 6, 1, &arg3)) + return NULL; + if ((arg4 = PyMem_NEW(double, arg3 )) == NULL) { + return PyErr_NoMemory(); + } + if (!PyArg_GetDoubleArray(args, 6, 1, arg3 , arg4)) + return NULL; + if (!PyArg_GetLong(args, 6, 3, &arg6)) + return NULL; + if (!PyArg_GetLong(args, 6, 4, &arg7)) + return NULL; + if (!PyArg_GetLong(args, 6, 5, &arg8)) + return NULL; + if (arg8 == N_XYZ) + ncoords = 3; + else if (arg8 == N_XYZW) + ncoords = 4; + else { + PyErr_BadArgument(); + return NULL; + } + s_nctl = arg1 - arg6; + t_nctl = arg3 - arg7; + if (!PyArg_GetObject(args, 6, 2, &v)) + return NULL; + if (!PyList_Check(v) || PyList_Size(v) != s_nctl) { + PyErr_BadArgument(); + return NULL; + } + if ((arg5 = PyMem_NEW(double, s_nctl*t_nctl*ncoords )) == NULL) { + return PyErr_NoMemory(); + } + pnext = arg5; + for (s = 0; s < s_nctl; s++) { + w = PyList_GetItem(v, s); + if (w == NULL || !PyList_Check(w) || + PyList_Size(w) != t_nctl) { + PyErr_BadArgument(); + return NULL; + } + for (t = 0; t < t_nctl; t++) { + pt = PyList_GetItem(w, t); + if (!PyArg_GetDoubleArray(pt, 1, 0, ncoords, pnext)) + return NULL; + pnext += ncoords; + } + } + s_byte_stride = sizeof(double) * ncoords; + t_byte_stride = s_byte_stride * s_nctl; + nurbssurface( arg1 , arg2 , arg3 , arg4 , + s_byte_stride , t_byte_stride , arg5 , arg6 , arg7 , arg8 ); + PyMem_DEL(arg2); + PyMem_DEL(arg4); + PyMem_DEL(arg5); + Py_INCREF(Py_None); + return Py_None; +} + +/* nurbscurve(knots, ctlpoints, order, type). + The length of ctlpoints is len(knots)-order. */ + +%nurbscurve + +static PyObject * +gl_nurbscurve(self, args) + PyObject *self; + PyObject *args; +{ + long arg1 ; + double * arg2 ; + long arg3 ; + double * arg4 ; + long arg5 ; + long arg6 ; + int ncoords, npoints; + int i; + PyObject *v; + double *pnext; + if (!PyArg_GetLongArraySize(args, 4, 0, &arg1)) + return NULL; + if ((arg2 = PyMem_NEW(double, arg1 )) == NULL) { + return PyErr_NoMemory(); + } + if (!PyArg_GetDoubleArray(args, 4, 0, arg1 , arg2)) + return NULL; + if (!PyArg_GetLong(args, 4, 2, &arg5)) + return NULL; + if (!PyArg_GetLong(args, 4, 3, &arg6)) + return NULL; + if (arg6 == N_ST) + ncoords = 2; + else if (arg6 == N_STW) + ncoords = 3; + else { + PyErr_BadArgument(); + return NULL; + } + npoints = arg1 - arg5; + if (!PyArg_GetObject(args, 4, 1, &v)) + return NULL; + if (!PyList_Check(v) || PyList_Size(v) != npoints) { + PyErr_BadArgument(); + return NULL; + } + if ((arg4 = PyMem_NEW(double, npoints*ncoords )) == NULL) { + return PyErr_NoMemory(); + } + pnext = arg4; + for (i = 0; i < npoints; i++) { + if (!PyArg_GetDoubleArray(PyList_GetItem(v, i), 1, 0, ncoords, pnext)) + return NULL; + pnext += ncoords; + } + arg3 = (sizeof(double)) * ncoords; + nurbscurve( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 ); + PyMem_DEL(arg2); + PyMem_DEL(arg4); + Py_INCREF(Py_None); + return Py_None; +} + +/* pwlcurve(points, type). + Points is a list of points. Type must be N_ST. */ + +%pwlcurve + +static PyObject * +gl_pwlcurve(self, args) + PyObject *self; + PyObject *args; +{ + PyObject *v; + long type; + double *data, *pnext; + long npoints, ncoords; + int i; + if (!PyArg_GetObject(args, 2, 0, &v)) + return NULL; + if (!PyArg_GetLong(args, 2, 1, &type)) + return NULL; + if (!PyList_Check(v)) { + PyErr_BadArgument(); + return NULL; + } + npoints = PyList_Size(v); + if (type == N_ST) + ncoords = 2; + else { + PyErr_BadArgument(); + return NULL; + } + if ((data = PyMem_NEW(double, npoints*ncoords)) == NULL) { + return PyErr_NoMemory(); + } + pnext = data; + for (i = 0; i < npoints; i++) { + if (!PyArg_GetDoubleArray(PyList_GetItem(v, i), 1, 0, ncoords, pnext)) + return NULL; + pnext += ncoords; + } + pwlcurve(npoints, data, sizeof(double)*ncoords, type); + PyMem_DEL(data); + Py_INCREF(Py_None); + return Py_None; +} + + +/* Picking and Selecting */ + +static short *pickbuffer = NULL; +static long pickbuffersize; + +static PyObject * +pick_select(args, func) + PyObject *args; + void (*func)(); +{ + if (!PyArg_GetLong(args, 1, 0, &pickbuffersize)) + return NULL; + if (pickbuffer != NULL) { + PyErr_SetString(PyExc_RuntimeError, + "pick/gselect: already picking/selecting"); + return NULL; + } + if ((pickbuffer = PyMem_NEW(short, pickbuffersize)) == NULL) { + return PyErr_NoMemory(); + } + (*func)(pickbuffer, pickbuffersize); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +endpick_select(args, func) + PyObject *args; + long (*func)(); +{ + PyObject *v, *w; + int i, nhits, n; + if (!PyArg_NoArgs(args)) + return NULL; + if (pickbuffer == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "endpick/endselect: not in pick/select mode"); + return NULL; + } + nhits = (*func)(pickbuffer); + if (nhits < 0) { + nhits = -nhits; /* How to report buffer overflow otherwise? */ + } + /* Scan the buffer to see how many integers */ + n = 0; + for (; nhits > 0; nhits--) { + n += 1 + pickbuffer[n]; + } + v = PyList_New(n); + if (v == NULL) + return NULL; + /* XXX Could do it nicer and interpret the data structure here, + returning a list of lists. But this can be done in Python... */ + for (i = 0; i < n; i++) { + w = PyInt_FromLong((long)pickbuffer[i]); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(v, i, w); + } + PyMem_DEL(pickbuffer); + pickbuffer = NULL; + return v; +} + +extern void pick(), gselect(); +extern long endpick(), endselect(); + +%pick +static PyObject *gl_pick(self, args) PyObject *self, *args; { + return pick_select(args, pick); +} + +%endpick +static PyObject *gl_endpick(self, args) PyObject *self, *args; { + return endpick_select(args, endpick); +} + +%gselect +static PyObject *gl_gselect(self, args) PyObject *self, *args; { + return pick_select(args, gselect); +} + +%endselect +static PyObject *gl_endselect(self, args) PyObject *self, *args; { + return endpick_select(args, endselect); +} + + +/* XXX The generator botches this one. Here's a quick hack to fix it. */ + +/* XXX The generator botches this one. Here's a quick hack to fix it. */ + +% getmatrix float r[16] + +static PyObject * +gl_getmatrix(self, args) + PyObject *self; + PyObject *args; +{ + Matrix arg1; + PyObject *v, *w; + int i, j; + getmatrix( arg1 ); + v = PyList_New(16); + if (v == NULL) { + return PyErr_NoMemory(); + } + for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) { + w = mknewfloatobject(arg1[i][j]); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(v, i*4+j, w); + } + return v; +} + +/* Here's an alternate version that returns a 4x4 matrix instead of + a vector. Unfortunately it is incompatible with loadmatrix and + multmatrix... */ + +% altgetmatrix float r[4][4] + +static PyObject * +gl_altgetmatrix(self, args) + PyObject *self; + PyObject *args; +{ + Matrix arg1; + PyObject *v, *w; + int i, j; + getmatrix( arg1 ); + v = PyList_New(4); + if (v == NULL) { + return NULL; + } + for (i = 0; i < 4; i++) { + w = PyList_New(4); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(v, i, w); + } + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + w = mknewfloatobject(arg1[i][j]); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(PyList_GetItem(v, i), j, w); + } + } + return v; +} + +% lrectwrite + +static PyObject * +gl_lrectwrite(self, args) + PyObject *self; + PyObject *args; +{ + short x1 ; + short y1 ; + short x2 ; + short y2 ; + string parray ; + PyObject *s; +#if 0 + int pixcount; +#endif + if (!PyArg_GetShort(args, 5, 0, &x1)) + return NULL; + if (!PyArg_GetShort(args, 5, 1, &y1)) + return NULL; + if (!PyArg_GetShort(args, 5, 2, &x2)) + return NULL; + if (!PyArg_GetShort(args, 5, 3, &y2)) + return NULL; + if (!PyArg_GetString(args, 5, 4, &parray)) + return NULL; + if (!PyArg_GetObject(args, 5, 4, &s)) + return NULL; +#if 0 +/* Don't check this, it breaks experiments with pixmode(PM_SIZE, ...) */ + pixcount = (long)(x2+1-x1) * (long)(y2+1-y1); + if (!PyString_Check(s) || PyString_Size(s) != pixcount*sizeof(long)) { + PyErr_SetString(PyExc_RuntimeError, + "string arg to lrectwrite has wrong size"); + return NULL; + } +#endif + lrectwrite( x1 , y1 , x2 , y2 , (unsigned long *) parray ); + Py_INCREF(Py_None); + return Py_None; +} + +% lrectread + +static PyObject * +gl_lrectread(self, args) + PyObject *self; + PyObject *args; +{ + short x1 ; + short y1 ; + short x2 ; + short y2 ; + PyObject *parray; + int pixcount; + if (!PyArg_GetShort(args, 4, 0, &x1)) + return NULL; + if (!PyArg_GetShort(args, 4, 1, &y1)) + return NULL; + if (!PyArg_GetShort(args, 4, 2, &x2)) + return NULL; + if (!PyArg_GetShort(args, 4, 3, &y2)) + return NULL; + pixcount = (long)(x2+1-x1) * (long)(y2+1-y1); + parray = PyString_FromStringAndSize((char *)NULL, pixcount*sizeof(long)); + if (parray == NULL) + return NULL; /* No memory */ + lrectread(x1, y1, x2, y2, (unsigned long *) PyString_AsString(parray)); + return parray; +} + +% readdisplay + +static PyObject * +gl_readdisplay(self, args) + PyObject *self; + PyObject *args; +{ + short x1, y1, x2, y2; + unsigned long *parray, hints; + long size, size_ret; + PyObject *rv; + + if ( !PyArg_Parse(args, "hhhhl", &x1, &y1, &x2, &y2, &hints) ) + return 0; + size = (long)(x2+1-x1) * (long)(y2+1-y1); + rv = PyString_FromStringAndSize((char *)NULL, size*sizeof(long)); + if ( rv == NULL ) + return NULL; + parray = (unsigned long *)PyString_AsString(rv); + size_ret = readdisplay(x1, y1, x2, y2, parray, hints); + if ( size_ret != size ) { + printf("gl_readdisplay: got %ld pixels, expected %ld\n", + size_ret, size); + PyErr_SetString(PyExc_RuntimeError, "readdisplay returned unexpected length"); + return NULL; + } + return rv; +} + +/* Desperately needed, here are tools to compress and decompress + the data manipulated by lrectread/lrectwrite. + + gl.packrect(width, height, packfactor, bigdata) --> smalldata + makes 'bigdata' 4*(packfactor**2) times smaller by: + - turning it into B/W (a factor 4) + - replacing squares of size pacfactor by one + representative + + gl.unpackrect(width, height, packfactor, smalldata) --> bigdata + is the inverse; the numeric arguments must be *the same*. + + Both work best if width and height are multiples of packfactor + (in fact unpackrect will leave garbage bytes). +*/ + +% packrect + +static PyObject * +gl_packrect(self, args) + PyObject *self; + PyObject *args; +{ + long width, height, packfactor; + char *s; + PyObject *unpacked, *packed; + int pixcount, packedcount, x, y, r, g, b; + unsigned long pixel; + unsigned char *p; + unsigned long *parray; + if (!PyArg_GetLong(args, 4, 0, &width)) + return NULL; + if (!PyArg_GetLong(args, 4, 1, &height)) + return NULL; + if (!PyArg_GetLong(args, 4, 2, &packfactor)) + return NULL; + if (!PyArg_GetString(args, 4, 3, &s)) /* For type checking only */ + return NULL; + if (!PyArg_GetObject(args, 4, 3, &unpacked)) + return NULL; + if (width <= 0 || height <= 0 || packfactor <= 0) { + PyErr_SetString(PyExc_RuntimeError, "packrect args must be > 0"); + return NULL; + } + pixcount = width*height; + packedcount = ((width+packfactor-1)/packfactor) * + ((height+packfactor-1)/packfactor); + if (PyString_Size(unpacked) != pixcount*sizeof(long)) { + PyErr_SetString(PyExc_RuntimeError, + "string arg to packrect has wrong size"); + return NULL; + } + packed = PyString_FromStringAndSize((char *)NULL, packedcount); + if (packed == NULL) + return NULL; + parray = (unsigned long *) PyString_AsString(unpacked); + p = (unsigned char *) PyString_AsString(packed); + for (y = 0; y < height; y += packfactor, parray += packfactor*width) { + for (x = 0; x < width; x += packfactor) { + pixel = parray[x]; + r = pixel & 0xff; + g = (pixel >> 8) & 0xff; + b = (pixel >> 16) & 0xff; + *p++ = (30*r+59*g+11*b) / 100; + } + } + return packed; +} + +% unpackrect + +static unsigned long unpacktab[256]; +static int unpacktab_inited = 0; + +static PyObject * +gl_unpackrect(self, args) + PyObject *self; + PyObject *args; +{ + long width, height, packfactor; + char *s; + PyObject *unpacked, *packed; + int pixcount, packedcount; + register unsigned char *p; + register unsigned long *parray; + if (!unpacktab_inited) { + register int white; + for (white = 256; --white >= 0; ) + unpacktab[white] = white * 0x010101L; + unpacktab_inited++; + } + if (!PyArg_GetLong(args, 4, 0, &width)) + return NULL; + if (!PyArg_GetLong(args, 4, 1, &height)) + return NULL; + if (!PyArg_GetLong(args, 4, 2, &packfactor)) + return NULL; + if (!PyArg_GetString(args, 4, 3, &s)) /* For type checking only */ + return NULL; + if (!PyArg_GetObject(args, 4, 3, &packed)) + return NULL; + if (width <= 0 || height <= 0 || packfactor <= 0) { + PyErr_SetString(PyExc_RuntimeError, "packrect args must be > 0"); + return NULL; + } + pixcount = width*height; + packedcount = ((width+packfactor-1)/packfactor) * + ((height+packfactor-1)/packfactor); + if (PyString_Size(packed) != packedcount) { + PyErr_SetString(PyExc_RuntimeError, + "string arg to unpackrect has wrong size"); + return NULL; + } + unpacked = PyString_FromStringAndSize((char *)NULL, pixcount*sizeof(long)); + if (unpacked == NULL) + return NULL; + parray = (unsigned long *) PyString_AsString(unpacked); + p = (unsigned char *) PyString_AsString(packed); + if (packfactor == 1 && width*height > 0) { + /* Just expand bytes to longs */ + register int x = width * height; + do { + *parray++ = unpacktab[*p++]; + } while (--x >= 0); + } + else { + register int y; + for (y = 0; y < height-packfactor+1; + y += packfactor, parray += packfactor*width) { + register int x; + for (x = 0; x < width-packfactor+1; x += packfactor) { + register unsigned long pixel = unpacktab[*p++]; + register int i; + for (i = packfactor*width; (i-=width) >= 0;) { + register int j; + for (j = packfactor; --j >= 0; ) + parray[i+x+j] = pixel; + } + } + } + } + return unpacked; +} + +% gversion +static PyObject * +gl_gversion(self, args) + PyObject *self; + PyObject *args; +{ + char buf[20]; + gversion(buf); + return PyString_FromString(buf); +} + + +/* void clear - Manual because of clash with termcap */ +%clear +static PyObject * +gl_clear(self, args) + PyObject *self; + PyObject *args; +{ + __GLclear( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* End of manually written stubs */ + +%% + +long getshade +if !solaris void devport short s long s +void rdr2i long s long s +void rectfs short s short s short s short s +void rects short s short s short s short s +void rmv2i long s long s +void noport +void popviewport +void clearhitcode +void closeobj +void cursoff +void curson +void doublebuffer +void finish +void gconfig +void ginit +void greset +void multimap +void onemap +void popattributes +void popmatrix +void pushattributes +void pushmatrix +void pushviewport +void qreset +void RGBmode +void singlebuffer +void swapbuffers +void gsync +void gflush +void tpon +void tpoff +void clkon +void clkoff +void ringbell +#void callfunc +void gbegin +void textinit +void initnames +void pclos +void popname +if !solaris void spclos +void zclear +void screenspace +void reshapeviewport +void winpush +void winpop +void foreground +void endfullscrn +if !solaris void endpupmode +void fullscrn +if !solaris void pupmode +void winconstraints +void pagecolor short s +void textcolor short s +void color short s +void curveit short s +void font short s +void linewidth short s +void setlinestyle short s +void setmap short s +void swapinterval short s +void writemask short s +if !solaris void textwritemask short s +void qdevice short s +void unqdevice short s +void curvebasis short s +void curveprecision short s +void loadname short s +void passthrough short s +void pushname short s +void setmonitor short s +if !solaris void setshade short s +void setpattern short s +if !solaris void pagewritemask short s +# +void callobj long s +void delobj long s +void editobj long s +void makeobj long s +void maketag long s +void chunksize long s +void compactify long s +void deltag long s +void lsrepeat long s +void objinsert long s +void objreplace long s +void winclose long s +void blanktime long s +void freepup long s +# This is not in the library!? +###void pupcolor long s +# +void backbuffer long s +void frontbuffer long s +if !solaris void lsbackup long s +void resetls long s +void lampon long s +void lampoff long s +void setbell long s +void blankscreen long s +void depthcue long s +void zbuffer long s +void backface long s +# +void cmov2i long s long s +void draw2i long s long s +void move2i long s long s +void pnt2i long s long s +void patchbasis long s long s +void patchprecision long s long s +void pdr2i long s long s +void pmv2i long s long s +void rpdr2i long s long s +void rpmv2i long s long s +void xfpt2i long s long s +void objdelete long s long s +void patchcurves long s long s +void minsize long s long s +void maxsize long s long s +void keepaspect long s long s +void prefsize long s long s +void stepunit long s long s +void fudge long s long s +void winmove long s long s +# +void attachcursor short s short s +void deflinestyle short s short s +void noise short s short s +void picksize short s short s +void qenter short s short s +void setdepth short s short s +void cmov2s short s short s +void draw2s short s short s +void move2s short s short s +void pdr2s short s short s +void pmv2s short s short s +void pnt2s short s short s +void rdr2s short s short s +void rmv2s short s short s +void rpdr2s short s short s +void rpmv2s short s short s +void xfpt2s short s short s +# +void cmov2 float s float s +void draw2 float s float s +void move2 float s float s +void pnt2 float s float s +void pdr2 float s float s +void pmv2 float s float s +void rdr2 float s float s +void rmv2 float s float s +void rpdr2 float s float s +void rpmv2 float s float s +void xfpt2 float s float s +# +void loadmatrix float s[4*4] +# Really [4][4] +void multmatrix float s[4*4] +# Really [4][4] +void crv float s[3*4] +# Really [4][3] +void rcrv float s[4*4] +# Really [4][4] +# +# Methods that have strings. +# +void addtopup long s char *s long s +void charstr char *s +void getport char *s +long strwidth char *s +long winopen char *s +void wintitle char *s +# +# Methods that have 1 long (# of elements) and an array +# +void polf long s float s[3*arg1] +void polf2 long s float s[2*arg1] +void poly long s float s[3*arg1] +void poly2 long s float s[2*arg1] +void crvn long s float s[3*arg1] +void rcrvn long s float s[4*arg1] +# +void polf2i long s long s[2*arg1] +void polfi long s long s[3*arg1] +void poly2i long s long s[2*arg1] +void polyi long s long s[3*arg1] +# +void polf2s long s short s[2*arg1] +void polfs long s short s[3*arg1] +void polys long s short s[3*arg1] +void poly2s long s short s[2*arg1] +# +void defcursor short s u_short s[128] +# Is this useful? +void writepixels short s u_short s[arg1] +# Should be unsigned short... +void defbasis long s float s[4*4] +if !solaris void gewrite short s short s[arg1] +# +void rotate short s char s +# This is not in the library!? +###void setbutton short s char s +void rot float s char s +# +void circfi long s long s long s +void circi long s long s long s +void cmovi long s long s long s +void drawi long s long s long s +void movei long s long s long s +void pnti long s long s long s +void newtag long s long s long s +void pdri long s long s long s +void pmvi long s long s long s +void rdri long s long s long s +void rmvi long s long s long s +void rpdri long s long s long s +void rpmvi long s long s long s +void xfpti long s long s long s +# +void circ float s float s float s +void circf float s float s float s +void cmov float s float s float s +void draw float s float s float s +void move float s float s float s +void pnt float s float s float s +void scale float s float s float s +void translate float s float s float s +void pdr float s float s float s +void pmv float s float s float s +void rdr float s float s float s +void rmv float s float s float s +void rpdr float s float s float s +void rpmv float s float s float s +void xfpt float s float s float s +# +void RGBcolor short s short s short s +void RGBwritemask short s short s short s +void setcursor short s short s short s +void tie short s short s short s +void circfs short s short s short s +void circs short s short s short s +void cmovs short s short s short s +void draws short s short s short s +void moves short s short s short s +void pdrs short s short s short s +void pmvs short s short s short s +void pnts short s short s short s +void rdrs short s short s short s +void rmvs short s short s short s +void rpdrs short s short s short s +void rpmvs short s short s short s +void xfpts short s short s short s +void curorigin short s short s short s +void cyclemap short s short s short s +# +void patch float s[4*4] float s[4*4] float s[4*4] +void splf long s float s[3*arg1] u_short s[arg1] +void splf2 long s float s[2*arg1] u_short s[arg1] +void splfi long s long s[3*arg1] u_short s[arg1] +void splf2i long s long s[2*arg1] u_short s[arg1] +void splfs long s short s[3*arg1] u_short s[arg1] +void splf2s long s short s[2*arg1] u_short s[arg1] +###void defpattern short s short s u_short s[arg2*arg2/16] +# +void rpatch float s[4*4] float s[4*4] float s[4*4] float s[4*4] +# +# routines that send 4 floats +# +void ortho2 float s float s float s float s +void rect float s float s float s float s +void rectf float s float s float s float s +void xfpt4 float s float s float s float s +# +void textport short s short s short s short s +void mapcolor short s short s short s short s +void scrmask short s short s short s short s +void setvaluator short s short s short s short s +void viewport short s short s short s short s +void shaderange short s short s short s short s +void xfpt4s short s short s short s short s +void rectfi long s long s long s long s +void recti long s long s long s long s +void xfpt4i long s long s long s long s +void prefposition long s long s long s long s +# +void arc float s float s float s short s short s +void arcf float s float s float s short s short s +void arcfi long s long s long s short s short s +void arci long s long s long s short s short s +# +void bbox2 short s short s float s float s float s float s +void bbox2i short s short s long s long s long s long s +void bbox2s short s short s short s short s short s short s +void blink short s short s short s short s short s +void ortho float s float s float s float s float s float s +void window float s float s float s float s float s float s +void lookat float s float s float s float s float s float s short s +# +void perspective short s float s float s float s +void polarview float s short s short s short s +# XXX getichararray not supported +#void writeRGB short s char s[arg1] char s[arg1] char s[arg1] +# +void arcfs short s short s short s short s short s +void arcs short s short s short s short s short s +void rectcopy short s short s short s short s short s short s +if !solaris void RGBcursor short s short s short s short s short s short s short s +# +long getbutton short s +long getcmmode +long getlsbackup +long getresetls +long getdcm +long getzbuffer +long ismex +long isobj long s +long isqueued short s +long istag long s +# +long genobj +long gentag +long getbuffer +long getcolor +long getdisplaymode +long getfont +long getheight +long gethitcode +long getlstyle +long getlwidth +long getmap +long getplanes +long getwritemask +long qtest +long getlsrepeat +long getmonitor +long getopenobj +long getpattern +long winget +long winattach +long getothermonitor +long newpup +# +long getvaluator short s +void winset long s +long dopup long s +void getdepth short r short r +void getcpos short r short r +void getsize long r long r +void getorigin long r long r +void getviewport short r short r short r short r +if !solaris void gettp short r short r short r short r +void getgpos float r float r float r float r +void winposition long s long s long s long s +void gRGBcolor short r short r short r +void gRGBmask short r short r short r +void getscrmask short r short r short r short r +###void gRGBcursor short r short r short r short r short r short r short r short r +void getmcolor short s short r short r short r +void mapw long s short s short s float r float r float r float r float r float r +void mapw2 long s short s short s float r float r +###void defrasterfont short s short s short s Fontchar s[arg3] short s short s[4*arg5] +###long qread short r +void getcursor short r u_short r u_short r long r +# +# For these we receive arrays of stuff +# +###void getdev long s short s[arg1] short r[arg1] +#XXX not generated correctly yet +#void getmatrix float r[16] +###long readpixels short s short r[retval] +###long readRGB short s char r[retval] char r[retval] char r[retval] +###long blkqread short s short r[arg1] +# +# New 4D routines +# +void cmode +void concave long s +void curstype long s +void drawmode long s +void gammaramp short s[256] short s[256] short s[256] +long getbackface +long getdescender +long getdrawmode +long getmmode +long getsm +long getvideo long s +void imakebackground +void lmbind short s short s +void lmdef long s long s long s float s[arg3] +void mmode long s +void normal float s[3] +void overlay long s +void RGBrange short s short s short s short s short s short s short s short s +if !solaris void setvideo long s long s +void shademodel long s +void underlay long s +# +# New Personal Iris/GT Routines +# +void bgnclosedline +void bgnline +void bgnpoint +void bgnpolygon +void bgnsurface +void bgntmesh +void bgntrim +void endclosedline +void endline +void endpoint +void endpolygon +void endsurface +void endtmesh +void endtrim +void blendfunction long s long s +void c3f float s[3] +void c3i long s[3] +void c3s short s[3] +void c4f float s[4] +void c4i long s[4] +void c4s short s[4] +void colorf float s +void cpack long s +void czclear long s long s +void dglclose long s +long dglopen char *s long s +long getgdesc long s +void getnurbsproperty long s float r +void glcompat long s long s +void iconsize long s long s +void icontitle char *s +void lRGBrange short s short s short s short s short s short s long s long s +void linesmooth long s +void lmcolor long s +void logicop long s +###long lrectread short s short s short s short s long r[retval] +###void lrectwrite short s short s short s short s long s[(arg2-arg1+1)*(arg4-arg3+1)] +### Now manual, with string last arg +###long rectread short s short s short s short s short r[retval] +###void rectwrite short s short s short s short s short s[(arg2-arg1+1)*(arg4-arg3+1)] +void lsetdepth long s long s +void lshaderange short s short s long s long s +void n3f float s[3] +void noborder +void pntsmooth long s +void readsource long s +void rectzoom float s float s +void sbox float s float s float s float s +void sboxi long s long s long s long s +void sboxs short s short s short s short s +void sboxf float s float s float s float s +void sboxfi long s long s long s long s +void sboxfs short s short s short s short s +void setnurbsproperty long s float s +void setpup long s long s long s +void smoothline long s +void subpixel long s +void swaptmesh +long swinopen long s +void v2f float s[2] +void v2i long s[2] +void v2s short s[2] +void v3f float s[3] +void v3i long s[3] +void v3s short s[3] +void v4f float s[4] +void v4i long s[4] +void v4s short s[4] +void videocmd long s +long windepth long s +void wmpack long s +void zdraw long s +void zfunction long s +void zsource long s +void zwritemask long s +# +# uses doubles +# +void v2d double s[2] +void v3d double s[3] +void v4d double s[4] +# +# Why isn't this here? +# +void pixmode long s long s +# +# New in IRIX 4.0 +# +long qgetfd +void dither long s diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/datetimemodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/datetimemodule.c new file mode 100644 index 00000000..e09cbbba --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/datetimemodule.c @@ -0,0 +1,4854 @@ +/* C implementation for the date/time type documented at + * http://www.zope.org/Members/fdrake/DateTimeWiki/FrontPage + */ + +#include "Python.h" +#include "modsupport.h" +#include "structmember.h" + +#include + +#include "datetime.h" + +/* We require that C int be at least 32 bits, and use int virtually + * everywhere. In just a few cases we use a temp long, where a Python + * API returns a C long. In such cases, we have to ensure that the + * final result fits in a C int (this can be an issue on 64-bit boxes). + */ +#if SIZEOF_INT < 4 +# error "datetime.c requires that C int have at least 32 bits" +#endif + +#define MINYEAR 1 +#define MAXYEAR 9999 + +/* Nine decimal digits is easy to communicate, and leaves enough room + * so that two delta days can be added w/o fear of overflowing a signed + * 32-bit int, and with plenty of room left over to absorb any possible + * carries from adding seconds. + */ +#define MAX_DELTA_DAYS 999999999 + +/* Rename the long macros in datetime.h to more reasonable short names. */ +#define GET_YEAR PyDateTime_GET_YEAR +#define GET_MONTH PyDateTime_GET_MONTH +#define GET_DAY PyDateTime_GET_DAY +#define DATE_GET_HOUR PyDateTime_DATE_GET_HOUR +#define DATE_GET_MINUTE PyDateTime_DATE_GET_MINUTE +#define DATE_GET_SECOND PyDateTime_DATE_GET_SECOND +#define DATE_GET_MICROSECOND PyDateTime_DATE_GET_MICROSECOND + +/* Date accessors for date and datetime. */ +#define SET_YEAR(o, v) (((o)->data[0] = ((v) & 0xff00) >> 8), \ + ((o)->data[1] = ((v) & 0x00ff))) +#define SET_MONTH(o, v) (PyDateTime_GET_MONTH(o) = (v)) +#define SET_DAY(o, v) (PyDateTime_GET_DAY(o) = (v)) + +/* Date/Time accessors for datetime. */ +#define DATE_SET_HOUR(o, v) (PyDateTime_DATE_GET_HOUR(o) = (v)) +#define DATE_SET_MINUTE(o, v) (PyDateTime_DATE_GET_MINUTE(o) = (v)) +#define DATE_SET_SECOND(o, v) (PyDateTime_DATE_GET_SECOND(o) = (v)) +#define DATE_SET_MICROSECOND(o, v) \ + (((o)->data[7] = ((v) & 0xff0000) >> 16), \ + ((o)->data[8] = ((v) & 0x00ff00) >> 8), \ + ((o)->data[9] = ((v) & 0x0000ff))) + +/* Time accessors for time. */ +#define TIME_GET_HOUR PyDateTime_TIME_GET_HOUR +#define TIME_GET_MINUTE PyDateTime_TIME_GET_MINUTE +#define TIME_GET_SECOND PyDateTime_TIME_GET_SECOND +#define TIME_GET_MICROSECOND PyDateTime_TIME_GET_MICROSECOND +#define TIME_SET_HOUR(o, v) (PyDateTime_TIME_GET_HOUR(o) = (v)) +#define TIME_SET_MINUTE(o, v) (PyDateTime_TIME_GET_MINUTE(o) = (v)) +#define TIME_SET_SECOND(o, v) (PyDateTime_TIME_GET_SECOND(o) = (v)) +#define TIME_SET_MICROSECOND(o, v) \ + (((o)->data[3] = ((v) & 0xff0000) >> 16), \ + ((o)->data[4] = ((v) & 0x00ff00) >> 8), \ + ((o)->data[5] = ((v) & 0x0000ff))) + +/* Delta accessors for timedelta. */ +#define GET_TD_DAYS(o) (((PyDateTime_Delta *)(o))->days) +#define GET_TD_SECONDS(o) (((PyDateTime_Delta *)(o))->seconds) +#define GET_TD_MICROSECONDS(o) (((PyDateTime_Delta *)(o))->microseconds) + +#define SET_TD_DAYS(o, v) ((o)->days = (v)) +#define SET_TD_SECONDS(o, v) ((o)->seconds = (v)) +#define SET_TD_MICROSECONDS(o, v) ((o)->microseconds = (v)) + +/* p is a pointer to a time or a datetime object; HASTZINFO(p) returns + * p->hastzinfo. + */ +#define HASTZINFO(p) (((_PyDateTime_BaseTZInfo *)(p))->hastzinfo) + +/* Forward declarations. */ +static PyTypeObject PyDateTime_DateType; +static PyTypeObject PyDateTime_DateTimeType; +static PyTypeObject PyDateTime_DeltaType; +static PyTypeObject PyDateTime_TimeType; +static PyTypeObject PyDateTime_TZInfoType; + +/* --------------------------------------------------------------------------- + * Math utilities. + */ + +/* k = i+j overflows iff k differs in sign from both inputs, + * iff k^i has sign bit set and k^j has sign bit set, + * iff (k^i)&(k^j) has sign bit set. + */ +#define SIGNED_ADD_OVERFLOWED(RESULT, I, J) \ + ((((RESULT) ^ (I)) & ((RESULT) ^ (J))) < 0) + +/* Compute Python divmod(x, y), returning the quotient and storing the + * remainder into *r. The quotient is the floor of x/y, and that's + * the real point of this. C will probably truncate instead (C99 + * requires truncation; C89 left it implementation-defined). + * Simplification: we *require* that y > 0 here. That's appropriate + * for all the uses made of it. This simplifies the code and makes + * the overflow case impossible (divmod(LONG_MIN, -1) is the only + * overflow case). + */ +static int +divmod(int x, int y, int *r) +{ + int quo; + + assert(y > 0); + quo = x / y; + *r = x - quo * y; + if (*r < 0) { + --quo; + *r += y; + } + assert(0 <= *r && *r < y); + return quo; +} + +/* Round a double to the nearest long. |x| must be small enough to fit + * in a C long; this is not checked. + */ +static long +round_to_long(double x) +{ + if (x >= 0.0) + x = floor(x + 0.5); + else + x = ceil(x - 0.5); + return (long)x; +} + +/* --------------------------------------------------------------------------- + * General calendrical helper functions + */ + +/* For each month ordinal in 1..12, the number of days in that month, + * and the number of days before that month in the same year. These + * are correct for non-leap years only. + */ +static int _days_in_month[] = { + 0, /* unused; this vector uses 1-based indexing */ + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static int _days_before_month[] = { + 0, /* unused; this vector uses 1-based indexing */ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +}; + +/* year -> 1 if leap year, else 0. */ +static int +is_leap(int year) +{ + /* Cast year to unsigned. The result is the same either way, but + * C can generate faster code for unsigned mod than for signed + * mod (especially for % 4 -- a good compiler should just grab + * the last 2 bits when the LHS is unsigned). + */ + const unsigned int ayear = (unsigned int)year; + return ayear % 4 == 0 && (ayear % 100 != 0 || ayear % 400 == 0); +} + +/* year, month -> number of days in that month in that year */ +static int +days_in_month(int year, int month) +{ + assert(month >= 1); + assert(month <= 12); + if (month == 2 && is_leap(year)) + return 29; + else + return _days_in_month[month]; +} + +/* year, month -> number of days in year preceeding first day of month */ +static int +days_before_month(int year, int month) +{ + int days; + + assert(month >= 1); + assert(month <= 12); + days = _days_before_month[month]; + if (month > 2 && is_leap(year)) + ++days; + return days; +} + +/* year -> number of days before January 1st of year. Remember that we + * start with year 1, so days_before_year(1) == 0. + */ +static int +days_before_year(int year) +{ + int y = year - 1; + /* This is incorrect if year <= 0; we really want the floor + * here. But so long as MINYEAR is 1, the smallest year this + * can see is 0 (this can happen in some normalization endcases), + * so we'll just special-case that. + */ + assert (year >= 0); + if (y >= 0) + return y*365 + y/4 - y/100 + y/400; + else { + assert(y == -1); + return -366; + } +} + +/* Number of days in 4, 100, and 400 year cycles. That these have + * the correct values is asserted in the module init function. + */ +#define DI4Y 1461 /* days_before_year(5); days in 4 years */ +#define DI100Y 36524 /* days_before_year(101); days in 100 years */ +#define DI400Y 146097 /* days_before_year(401); days in 400 years */ + +/* ordinal -> year, month, day, considering 01-Jan-0001 as day 1. */ +static void +ord_to_ymd(int ordinal, int *year, int *month, int *day) +{ + int n, n1, n4, n100, n400, leapyear, preceding; + + /* ordinal is a 1-based index, starting at 1-Jan-1. The pattern of + * leap years repeats exactly every 400 years. The basic strategy is + * to find the closest 400-year boundary at or before ordinal, then + * work with the offset from that boundary to ordinal. Life is much + * clearer if we subtract 1 from ordinal first -- then the values + * of ordinal at 400-year boundaries are exactly those divisible + * by DI400Y: + * + * D M Y n n-1 + * -- --- ---- ---------- ---------------- + * 31 Dec -400 -DI400Y -DI400Y -1 + * 1 Jan -399 -DI400Y +1 -DI400Y 400-year boundary + * ... + * 30 Dec 000 -1 -2 + * 31 Dec 000 0 -1 + * 1 Jan 001 1 0 400-year boundary + * 2 Jan 001 2 1 + * 3 Jan 001 3 2 + * ... + * 31 Dec 400 DI400Y DI400Y -1 + * 1 Jan 401 DI400Y +1 DI400Y 400-year boundary + */ + assert(ordinal >= 1); + --ordinal; + n400 = ordinal / DI400Y; + n = ordinal % DI400Y; + *year = n400 * 400 + 1; + + /* Now n is the (non-negative) offset, in days, from January 1 of + * year, to the desired date. Now compute how many 100-year cycles + * precede n. + * Note that it's possible for n100 to equal 4! In that case 4 full + * 100-year cycles precede the desired day, which implies the + * desired day is December 31 at the end of a 400-year cycle. + */ + n100 = n / DI100Y; + n = n % DI100Y; + + /* Now compute how many 4-year cycles precede it. */ + n4 = n / DI4Y; + n = n % DI4Y; + + /* And now how many single years. Again n1 can be 4, and again + * meaning that the desired day is December 31 at the end of the + * 4-year cycle. + */ + n1 = n / 365; + n = n % 365; + + *year += n100 * 100 + n4 * 4 + n1; + if (n1 == 4 || n100 == 4) { + assert(n == 0); + *year -= 1; + *month = 12; + *day = 31; + return; + } + + /* Now the year is correct, and n is the offset from January 1. We + * find the month via an estimate that's either exact or one too + * large. + */ + leapyear = n1 == 3 && (n4 != 24 || n100 == 3); + assert(leapyear == is_leap(*year)); + *month = (n + 50) >> 5; + preceding = (_days_before_month[*month] + (*month > 2 && leapyear)); + if (preceding > n) { + /* estimate is too large */ + *month -= 1; + preceding -= days_in_month(*year, *month); + } + n -= preceding; + assert(0 <= n); + assert(n < days_in_month(*year, *month)); + + *day = n + 1; +} + +/* year, month, day -> ordinal, considering 01-Jan-0001 as day 1. */ +static int +ymd_to_ord(int year, int month, int day) +{ + return days_before_year(year) + days_before_month(year, month) + day; +} + +/* Day of week, where Monday==0, ..., Sunday==6. 1/1/1 was a Monday. */ +static int +weekday(int year, int month, int day) +{ + return (ymd_to_ord(year, month, day) + 6) % 7; +} + +/* Ordinal of the Monday starting week 1 of the ISO year. Week 1 is the + * first calendar week containing a Thursday. + */ +static int +iso_week1_monday(int year) +{ + int first_day = ymd_to_ord(year, 1, 1); /* ord of 1/1 */ + /* 0 if 1/1 is a Monday, 1 if a Tue, etc. */ + int first_weekday = (first_day + 6) % 7; + /* ordinal of closest Monday at or before 1/1 */ + int week1_monday = first_day - first_weekday; + + if (first_weekday > 3) /* if 1/1 was Fri, Sat, Sun */ + week1_monday += 7; + return week1_monday; +} + +/* --------------------------------------------------------------------------- + * Range checkers. + */ + +/* Check that -MAX_DELTA_DAYS <= days <= MAX_DELTA_DAYS. If so, return 0. + * If not, raise OverflowError and return -1. + */ +static int +check_delta_day_range(int days) +{ + if (-MAX_DELTA_DAYS <= days && days <= MAX_DELTA_DAYS) + return 0; + PyErr_Format(PyExc_OverflowError, + "days=%d; must have magnitude <= %d", + days, MAX_DELTA_DAYS); + return -1; +} + +/* Check that date arguments are in range. Return 0 if they are. If they + * aren't, raise ValueError and return -1. + */ +static int +check_date_args(int year, int month, int day) +{ + + if (year < MINYEAR || year > MAXYEAR) { + PyErr_SetString(PyExc_ValueError, + "year is out of range"); + return -1; + } + if (month < 1 || month > 12) { + PyErr_SetString(PyExc_ValueError, + "month must be in 1..12"); + return -1; + } + if (day < 1 || day > days_in_month(year, month)) { + PyErr_SetString(PyExc_ValueError, + "day is out of range for month"); + return -1; + } + return 0; +} + +/* Check that time arguments are in range. Return 0 if they are. If they + * aren't, raise ValueError and return -1. + */ +static int +check_time_args(int h, int m, int s, int us) +{ + if (h < 0 || h > 23) { + PyErr_SetString(PyExc_ValueError, + "hour must be in 0..23"); + return -1; + } + if (m < 0 || m > 59) { + PyErr_SetString(PyExc_ValueError, + "minute must be in 0..59"); + return -1; + } + if (s < 0 || s > 59) { + PyErr_SetString(PyExc_ValueError, + "second must be in 0..59"); + return -1; + } + if (us < 0 || us > 999999) { + PyErr_SetString(PyExc_ValueError, + "microsecond must be in 0..999999"); + return -1; + } + return 0; +} + +/* --------------------------------------------------------------------------- + * Normalization utilities. + */ + +/* One step of a mixed-radix conversion. A "hi" unit is equivalent to + * factor "lo" units. factor must be > 0. If *lo is less than 0, or + * at least factor, enough of *lo is converted into "hi" units so that + * 0 <= *lo < factor. The input values must be such that int overflow + * is impossible. + */ +static void +normalize_pair(int *hi, int *lo, int factor) +{ + assert(factor > 0); + assert(lo != hi); + if (*lo < 0 || *lo >= factor) { + const int num_hi = divmod(*lo, factor, lo); + const int new_hi = *hi + num_hi; + assert(! SIGNED_ADD_OVERFLOWED(new_hi, *hi, num_hi)); + *hi = new_hi; + } + assert(0 <= *lo && *lo < factor); +} + +/* Fiddle days (d), seconds (s), and microseconds (us) so that + * 0 <= *s < 24*3600 + * 0 <= *us < 1000000 + * The input values must be such that the internals don't overflow. + * The way this routine is used, we don't get close. + */ +static void +normalize_d_s_us(int *d, int *s, int *us) +{ + if (*us < 0 || *us >= 1000000) { + normalize_pair(s, us, 1000000); + /* |s| can't be bigger than about + * |original s| + |original us|/1000000 now. + */ + + } + if (*s < 0 || *s >= 24*3600) { + normalize_pair(d, s, 24*3600); + /* |d| can't be bigger than about + * |original d| + + * (|original s| + |original us|/1000000) / (24*3600) now. + */ + } + assert(0 <= *s && *s < 24*3600); + assert(0 <= *us && *us < 1000000); +} + +/* Fiddle years (y), months (m), and days (d) so that + * 1 <= *m <= 12 + * 1 <= *d <= days_in_month(*y, *m) + * The input values must be such that the internals don't overflow. + * The way this routine is used, we don't get close. + */ +static void +normalize_y_m_d(int *y, int *m, int *d) +{ + int dim; /* # of days in month */ + + /* This gets muddy: the proper range for day can't be determined + * without knowing the correct month and year, but if day is, e.g., + * plus or minus a million, the current month and year values make + * no sense (and may also be out of bounds themselves). + * Saying 12 months == 1 year should be non-controversial. + */ + if (*m < 1 || *m > 12) { + --*m; + normalize_pair(y, m, 12); + ++*m; + /* |y| can't be bigger than about + * |original y| + |original m|/12 now. + */ + } + assert(1 <= *m && *m <= 12); + + /* Now only day can be out of bounds (year may also be out of bounds + * for a datetime object, but we don't care about that here). + * If day is out of bounds, what to do is arguable, but at least the + * method here is principled and explainable. + */ + dim = days_in_month(*y, *m); + if (*d < 1 || *d > dim) { + /* Move day-1 days from the first of the month. First try to + * get off cheap if we're only one day out of range + * (adjustments for timezone alone can't be worse than that). + */ + if (*d == 0) { + --*m; + if (*m > 0) + *d = days_in_month(*y, *m); + else { + --*y; + *m = 12; + *d = 31; + } + } + else if (*d == dim + 1) { + /* move forward a day */ + ++*m; + *d = 1; + if (*m > 12) { + *m = 1; + ++*y; + } + } + else { + int ordinal = ymd_to_ord(*y, *m, 1) + + *d - 1; + ord_to_ymd(ordinal, y, m, d); + } + } + assert(*m > 0); + assert(*d > 0); +} + +/* Fiddle out-of-bounds months and days so that the result makes some kind + * of sense. The parameters are both inputs and outputs. Returns < 0 on + * failure, where failure means the adjusted year is out of bounds. + */ +static int +normalize_date(int *year, int *month, int *day) +{ + int result; + + normalize_y_m_d(year, month, day); + if (MINYEAR <= *year && *year <= MAXYEAR) + result = 0; + else { + PyErr_SetString(PyExc_OverflowError, + "date value out of range"); + result = -1; + } + return result; +} + +/* Force all the datetime fields into range. The parameters are both + * inputs and outputs. Returns < 0 on error. + */ +static int +normalize_datetime(int *year, int *month, int *day, + int *hour, int *minute, int *second, + int *microsecond) +{ + normalize_pair(second, microsecond, 1000000); + normalize_pair(minute, second, 60); + normalize_pair(hour, minute, 60); + normalize_pair(day, hour, 24); + return normalize_date(year, month, day); +} + +/* --------------------------------------------------------------------------- + * Basic object allocation: tp_alloc implementations. These allocate + * Python objects of the right size and type, and do the Python object- + * initialization bit. If there's not enough memory, they return NULL after + * setting MemoryError. All data members remain uninitialized trash. + * + * We abuse the tp_alloc "nitems" argument to communicate whether a tzinfo + * member is needed. This is ugly, imprecise, and possibly insecure. + * tp_basicsize for the time and datetime types is set to the size of the + * struct that has room for the tzinfo member, so subclasses in Python will + * allocate enough space for a tzinfo member whether or not one is actually + * needed. That's the "ugly and imprecise" parts. The "possibly insecure" + * part is that PyType_GenericAlloc() (which subclasses in Python end up + * using) just happens today to effectively ignore the nitems argument + * when tp_itemsize is 0, which it is for these type objects. If that + * changes, perhaps the callers of tp_alloc slots in this file should + * be changed to force a 0 nitems argument unless the type being allocated + * is a base type implemented in this file (so that tp_alloc is time_alloc + * or datetime_alloc below, which know about the nitems abuse). + */ + +static PyObject * +time_alloc(PyTypeObject *type, int aware) +{ + PyObject *self; + + self = (PyObject *) + PyObject_MALLOC(aware ? + sizeof(PyDateTime_Time) : + sizeof(_PyDateTime_BaseTime)); + if (self == NULL) + return (PyObject *)PyErr_NoMemory(); + PyObject_INIT(self, type); + return self; +} + +static PyObject * +datetime_alloc(PyTypeObject *type, int aware) +{ + PyObject *self; + + self = (PyObject *) + PyObject_MALLOC(aware ? + sizeof(PyDateTime_DateTime) : + sizeof(_PyDateTime_BaseDateTime)); + if (self == NULL) + return (PyObject *)PyErr_NoMemory(); + PyObject_INIT(self, type); + return self; +} + +/* --------------------------------------------------------------------------- + * Helpers for setting object fields. These work on pointers to the + * appropriate base class. + */ + +/* For date and datetime. */ +static void +set_date_fields(PyDateTime_Date *self, int y, int m, int d) +{ + self->hashcode = -1; + SET_YEAR(self, y); + SET_MONTH(self, m); + SET_DAY(self, d); +} + +/* --------------------------------------------------------------------------- + * Create various objects, mostly without range checking. + */ + +/* Create a date instance with no range checking. */ +static PyObject * +new_date_ex(int year, int month, int day, PyTypeObject *type) +{ + PyDateTime_Date *self; + + self = (PyDateTime_Date *) (type->tp_alloc(type, 0)); + if (self != NULL) + set_date_fields(self, year, month, day); + return (PyObject *) self; +} + +#define new_date(year, month, day) \ + new_date_ex(year, month, day, &PyDateTime_DateType) + +/* Create a datetime instance with no range checking. */ +static PyObject * +new_datetime_ex(int year, int month, int day, int hour, int minute, + int second, int usecond, PyObject *tzinfo, PyTypeObject *type) +{ + PyDateTime_DateTime *self; + char aware = tzinfo != Py_None; + + self = (PyDateTime_DateTime *) (type->tp_alloc(type, aware)); + if (self != NULL) { + self->hastzinfo = aware; + set_date_fields((PyDateTime_Date *)self, year, month, day); + DATE_SET_HOUR(self, hour); + DATE_SET_MINUTE(self, minute); + DATE_SET_SECOND(self, second); + DATE_SET_MICROSECOND(self, usecond); + if (aware) { + Py_INCREF(tzinfo); + self->tzinfo = tzinfo; + } + } + return (PyObject *)self; +} + +#define new_datetime(y, m, d, hh, mm, ss, us, tzinfo) \ + new_datetime_ex(y, m, d, hh, mm, ss, us, tzinfo, \ + &PyDateTime_DateTimeType) + +/* Create a time instance with no range checking. */ +static PyObject * +new_time_ex(int hour, int minute, int second, int usecond, + PyObject *tzinfo, PyTypeObject *type) +{ + PyDateTime_Time *self; + char aware = tzinfo != Py_None; + + self = (PyDateTime_Time *) (type->tp_alloc(type, aware)); + if (self != NULL) { + self->hastzinfo = aware; + self->hashcode = -1; + TIME_SET_HOUR(self, hour); + TIME_SET_MINUTE(self, minute); + TIME_SET_SECOND(self, second); + TIME_SET_MICROSECOND(self, usecond); + if (aware) { + Py_INCREF(tzinfo); + self->tzinfo = tzinfo; + } + } + return (PyObject *)self; +} + +#define new_time(hh, mm, ss, us, tzinfo) \ + new_time_ex(hh, mm, ss, us, tzinfo, &PyDateTime_TimeType) + +/* Create a timedelta instance. Normalize the members iff normalize is + * true. Passing false is a speed optimization, if you know for sure + * that seconds and microseconds are already in their proper ranges. In any + * case, raises OverflowError and returns NULL if the normalized days is out + * of range). + */ +static PyObject * +new_delta_ex(int days, int seconds, int microseconds, int normalize, + PyTypeObject *type) +{ + PyDateTime_Delta *self; + + if (normalize) + normalize_d_s_us(&days, &seconds, µseconds); + assert(0 <= seconds && seconds < 24*3600); + assert(0 <= microseconds && microseconds < 1000000); + + if (check_delta_day_range(days) < 0) + return NULL; + + self = (PyDateTime_Delta *) (type->tp_alloc(type, 0)); + if (self != NULL) { + self->hashcode = -1; + SET_TD_DAYS(self, days); + SET_TD_SECONDS(self, seconds); + SET_TD_MICROSECONDS(self, microseconds); + } + return (PyObject *) self; +} + +#define new_delta(d, s, us, normalize) \ + new_delta_ex(d, s, us, normalize, &PyDateTime_DeltaType) + +/* --------------------------------------------------------------------------- + * tzinfo helpers. + */ + +/* Ensure that p is None or of a tzinfo subclass. Return 0 if OK; if not + * raise TypeError and return -1. + */ +static int +check_tzinfo_subclass(PyObject *p) +{ + if (p == Py_None || PyTZInfo_Check(p)) + return 0; + PyErr_Format(PyExc_TypeError, + "tzinfo argument must be None or of a tzinfo subclass, " + "not type '%s'", + p->ob_type->tp_name); + return -1; +} + +/* Return tzinfo.methname(tzinfoarg), without any checking of results. + * If tzinfo is None, returns None. + */ +static PyObject * +call_tzinfo_method(PyObject *tzinfo, char *methname, PyObject *tzinfoarg) +{ + PyObject *result; + + assert(tzinfo && methname && tzinfoarg); + assert(check_tzinfo_subclass(tzinfo) >= 0); + if (tzinfo == Py_None) { + result = Py_None; + Py_INCREF(result); + } + else + result = PyObject_CallMethod(tzinfo, methname, "O", tzinfoarg); + return result; +} + +/* If self has a tzinfo member, return a BORROWED reference to it. Else + * return NULL, which is NOT AN ERROR. There are no error returns here, + * and the caller must not decref the result. + */ +static PyObject * +get_tzinfo_member(PyObject *self) +{ + PyObject *tzinfo = NULL; + + if (PyDateTime_Check(self) && HASTZINFO(self)) + tzinfo = ((PyDateTime_DateTime *)self)->tzinfo; + else if (PyTime_Check(self) && HASTZINFO(self)) + tzinfo = ((PyDateTime_Time *)self)->tzinfo; + + return tzinfo; +} + +/* Call getattr(tzinfo, name)(tzinfoarg), and extract an int from the + * result. tzinfo must be an instance of the tzinfo class. If the method + * returns None, this returns 0 and sets *none to 1. If the method doesn't + * return None or timedelta, TypeError is raised and this returns -1. If it + * returnsa timedelta and the value is out of range or isn't a whole number + * of minutes, ValueError is raised and this returns -1. + * Else *none is set to 0 and the integer method result is returned. + */ +static int +call_utc_tzinfo_method(PyObject *tzinfo, char *name, PyObject *tzinfoarg, + int *none) +{ + PyObject *u; + int result = -1; + + assert(tzinfo != NULL); + assert(PyTZInfo_Check(tzinfo)); + assert(tzinfoarg != NULL); + + *none = 0; + u = call_tzinfo_method(tzinfo, name, tzinfoarg); + if (u == NULL) + return -1; + + else if (u == Py_None) { + result = 0; + *none = 1; + } + else if (PyDelta_Check(u)) { + const int days = GET_TD_DAYS(u); + if (days < -1 || days > 0) + result = 24*60; /* trigger ValueError below */ + else { + /* next line can't overflow because we know days + * is -1 or 0 now + */ + int ss = days * 24 * 3600 + GET_TD_SECONDS(u); + result = divmod(ss, 60, &ss); + if (ss || GET_TD_MICROSECONDS(u)) { + PyErr_Format(PyExc_ValueError, + "tzinfo.%s() must return a " + "whole number of minutes", + name); + result = -1; + } + } + } + else { + PyErr_Format(PyExc_TypeError, + "tzinfo.%s() must return None or " + "timedelta, not '%s'", + name, u->ob_type->tp_name); + } + + Py_DECREF(u); + if (result < -1439 || result > 1439) { + PyErr_Format(PyExc_ValueError, + "tzinfo.%s() returned %d; must be in " + "-1439 .. 1439", + name, result); + result = -1; + } + return result; +} + +/* Call tzinfo.utcoffset(tzinfoarg), and extract an integer from the + * result. tzinfo must be an instance of the tzinfo class. If utcoffset() + * returns None, call_utcoffset returns 0 and sets *none to 1. If uctoffset() + * doesn't return None or timedelta, TypeError is raised and this returns -1. + * If utcoffset() returns an invalid timedelta (out of range, or not a whole + * # of minutes), ValueError is raised and this returns -1. Else *none is + * set to 0 and the offset is returned (as int # of minutes east of UTC). + */ +static int +call_utcoffset(PyObject *tzinfo, PyObject *tzinfoarg, int *none) +{ + return call_utc_tzinfo_method(tzinfo, "utcoffset", tzinfoarg, none); +} + +/* Call tzinfo.name(tzinfoarg), and return the offset as a timedelta or None. + */ +static PyObject * +offset_as_timedelta(PyObject *tzinfo, char *name, PyObject *tzinfoarg) { + PyObject *result; + + assert(tzinfo && name && tzinfoarg); + if (tzinfo == Py_None) { + result = Py_None; + Py_INCREF(result); + } + else { + int none; + int offset = call_utc_tzinfo_method(tzinfo, name, tzinfoarg, + &none); + if (offset < 0 && PyErr_Occurred()) + return NULL; + if (none) { + result = Py_None; + Py_INCREF(result); + } + else + result = new_delta(0, offset * 60, 0, 1); + } + return result; +} + +/* Call tzinfo.dst(tzinfoarg), and extract an integer from the + * result. tzinfo must be an instance of the tzinfo class. If dst() + * returns None, call_dst returns 0 and sets *none to 1. If dst() + & doesn't return None or timedelta, TypeError is raised and this + * returns -1. If dst() returns an invalid timedelta for a UTC offset, + * ValueError is raised and this returns -1. Else *none is set to 0 and + * the offset is returned (as an int # of minutes east of UTC). + */ +static int +call_dst(PyObject *tzinfo, PyObject *tzinfoarg, int *none) +{ + return call_utc_tzinfo_method(tzinfo, "dst", tzinfoarg, none); +} + +/* Call tzinfo.tzname(tzinfoarg), and return the result. tzinfo must be + * an instance of the tzinfo class or None. If tzinfo isn't None, and + * tzname() doesn't return None or a string, TypeError is raised and this + * returns NULL. + */ +static PyObject * +call_tzname(PyObject *tzinfo, PyObject *tzinfoarg) +{ + PyObject *result; + + assert(tzinfo != NULL); + assert(check_tzinfo_subclass(tzinfo) >= 0); + assert(tzinfoarg != NULL); + + if (tzinfo == Py_None) { + result = Py_None; + Py_INCREF(result); + } + else + result = PyObject_CallMethod(tzinfo, "tzname", "O", tzinfoarg); + + if (result != NULL && result != Py_None && ! PyString_Check(result)) { + PyErr_Format(PyExc_TypeError, "tzinfo.tzname() must " + "return None or a string, not '%s'", + result->ob_type->tp_name); + Py_DECREF(result); + result = NULL; + } + return result; +} + +typedef enum { + /* an exception has been set; the caller should pass it on */ + OFFSET_ERROR, + + /* type isn't date, datetime, or time subclass */ + OFFSET_UNKNOWN, + + /* date, + * datetime with !hastzinfo + * datetime with None tzinfo, + * datetime where utcoffset() returns None + * time with !hastzinfo + * time with None tzinfo, + * time where utcoffset() returns None + */ + OFFSET_NAIVE, + + /* time or datetime where utcoffset() doesn't return None */ + OFFSET_AWARE, +} naivety; + +/* Classify an object as to whether it's naive or offset-aware. See + * the "naivety" typedef for details. If the type is aware, *offset is set + * to minutes east of UTC (as returned by the tzinfo.utcoffset() method). + * If the type is offset-naive (or unknown, or error), *offset is set to 0. + * tzinfoarg is the argument to pass to the tzinfo.utcoffset() method. + */ +static naivety +classify_utcoffset(PyObject *op, PyObject *tzinfoarg, int *offset) +{ + int none; + PyObject *tzinfo; + + assert(tzinfoarg != NULL); + *offset = 0; + tzinfo = get_tzinfo_member(op); /* NULL means no tzinfo, not error */ + if (tzinfo == Py_None) + return OFFSET_NAIVE; + if (tzinfo == NULL) { + /* note that a datetime passes the PyDate_Check test */ + return (PyTime_Check(op) || PyDate_Check(op)) ? + OFFSET_NAIVE : OFFSET_UNKNOWN; + } + *offset = call_utcoffset(tzinfo, tzinfoarg, &none); + if (*offset == -1 && PyErr_Occurred()) + return OFFSET_ERROR; + return none ? OFFSET_NAIVE : OFFSET_AWARE; +} + +/* Classify two objects as to whether they're naive or offset-aware. + * This isn't quite the same as calling classify_utcoffset() twice: for + * binary operations (comparison and subtraction), we generally want to + * ignore the tzinfo members if they're identical. This is by design, + * so that results match "naive" expectations when mixing objects from a + * single timezone. So in that case, this sets both offsets to 0 and + * both naiveties to OFFSET_NAIVE. + * The function returns 0 if everything's OK, and -1 on error. + */ +static int +classify_two_utcoffsets(PyObject *o1, int *offset1, naivety *n1, + PyObject *tzinfoarg1, + PyObject *o2, int *offset2, naivety *n2, + PyObject *tzinfoarg2) +{ + if (get_tzinfo_member(o1) == get_tzinfo_member(o2)) { + *offset1 = *offset2 = 0; + *n1 = *n2 = OFFSET_NAIVE; + } + else { + *n1 = classify_utcoffset(o1, tzinfoarg1, offset1); + if (*n1 == OFFSET_ERROR) + return -1; + *n2 = classify_utcoffset(o2, tzinfoarg2, offset2); + if (*n2 == OFFSET_ERROR) + return -1; + } + return 0; +} + +/* repr is like "someclass(arg1, arg2)". If tzinfo isn't None, + * stuff + * ", tzinfo=" + repr(tzinfo) + * before the closing ")". + */ +static PyObject * +append_keyword_tzinfo(PyObject *repr, PyObject *tzinfo) +{ + PyObject *temp; + + assert(PyString_Check(repr)); + assert(tzinfo); + if (tzinfo == Py_None) + return repr; + /* Get rid of the trailing ')'. */ + assert(PyString_AsString(repr)[PyString_Size(repr)-1] == ')'); + temp = PyString_FromStringAndSize(PyString_AsString(repr), + PyString_Size(repr) - 1); + Py_DECREF(repr); + if (temp == NULL) + return NULL; + repr = temp; + + /* Append ", tzinfo=". */ + PyString_ConcatAndDel(&repr, PyString_FromString(", tzinfo=")); + + /* Append repr(tzinfo). */ + PyString_ConcatAndDel(&repr, PyObject_Repr(tzinfo)); + + /* Add a closing paren. */ + PyString_ConcatAndDel(&repr, PyString_FromString(")")); + return repr; +} + +/* --------------------------------------------------------------------------- + * String format helpers. + */ + +static PyObject * +format_ctime(PyDateTime_Date *date, int hours, int minutes, int seconds) +{ + static char *DayNames[] = { + "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" + }; + static char *MonthNames[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + char buffer[128]; + int wday = weekday(GET_YEAR(date), GET_MONTH(date), GET_DAY(date)); + + PyOS_snprintf(buffer, sizeof(buffer), "%s %s %2d %02d:%02d:%02d %04d", + DayNames[wday], MonthNames[GET_MONTH(date) - 1], + GET_DAY(date), hours, minutes, seconds, + GET_YEAR(date)); + return PyString_FromString(buffer); +} + +/* Add an hours & minutes UTC offset string to buf. buf has no more than + * buflen bytes remaining. The UTC offset is gotten by calling + * tzinfo.uctoffset(tzinfoarg). If that returns None, \0 is stored into + * *buf, and that's all. Else the returned value is checked for sanity (an + * integer in range), and if that's OK it's converted to an hours & minutes + * string of the form + * sign HH sep MM + * Returns 0 if everything is OK. If the return value from utcoffset() is + * bogus, an appropriate exception is set and -1 is returned. + */ +static int +format_utcoffset(char *buf, size_t buflen, const char *sep, + PyObject *tzinfo, PyObject *tzinfoarg) +{ + int offset; + int hours; + int minutes; + char sign; + int none; + + offset = call_utcoffset(tzinfo, tzinfoarg, &none); + if (offset == -1 && PyErr_Occurred()) + return -1; + if (none) { + *buf = '\0'; + return 0; + } + sign = '+'; + if (offset < 0) { + sign = '-'; + offset = - offset; + } + hours = divmod(offset, 60, &minutes); + PyOS_snprintf(buf, buflen, "%c%02d%s%02d", sign, hours, sep, minutes); + return 0; +} + +/* I sure don't want to reproduce the strftime code from the time module, + * so this imports the module and calls it. All the hair is due to + * giving special meanings to the %z and %Z format codes via a preprocessing + * step on the format string. + * tzinfoarg is the argument to pass to the object's tzinfo method, if + * needed. + */ +static PyObject * +wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, + PyObject *tzinfoarg) +{ + PyObject *result = NULL; /* guilty until proved innocent */ + + PyObject *zreplacement = NULL; /* py string, replacement for %z */ + PyObject *Zreplacement = NULL; /* py string, replacement for %Z */ + + char *pin; /* pointer to next char in input format */ + char ch; /* next char in input format */ + + PyObject *newfmt = NULL; /* py string, the output format */ + char *pnew; /* pointer to available byte in output format */ + char totalnew; /* number bytes total in output format buffer, + exclusive of trailing \0 */ + char usednew; /* number bytes used so far in output format buffer */ + + char *ptoappend; /* pointer to string to append to output buffer */ + int ntoappend; /* # of bytes to append to output buffer */ + + assert(object && format && timetuple); + assert(PyString_Check(format)); + + /* Give up if the year is before 1900. + * Python strftime() plays games with the year, and different + * games depending on whether envar PYTHON2K is set. This makes + * years before 1900 a nightmare, even if the platform strftime + * supports them (and not all do). + * We could get a lot farther here by avoiding Python's strftime + * wrapper and calling the C strftime() directly, but that isn't + * an option in the Python implementation of this module. + */ + { + long year; + PyObject *pyyear = PySequence_GetItem(timetuple, 0); + if (pyyear == NULL) return NULL; + assert(PyInt_Check(pyyear)); + year = PyInt_AsLong(pyyear); + Py_DECREF(pyyear); + if (year < 1900) { + PyErr_Format(PyExc_ValueError, "year=%ld is before " + "1900; the datetime strftime() " + "methods require year >= 1900", + year); + return NULL; + } + } + + /* Scan the input format, looking for %z and %Z escapes, building + * a new format. Since computing the replacements for those codes + * is expensive, don't unless they're actually used. + */ + totalnew = PyString_Size(format) + 1; /* realistic if no %z/%Z */ + newfmt = PyString_FromStringAndSize(NULL, totalnew); + if (newfmt == NULL) goto Done; + pnew = PyString_AsString(newfmt); + usednew = 0; + + pin = PyString_AsString(format); + while ((ch = *pin++) != '\0') { + if (ch != '%') { + ptoappend = pin - 1; + ntoappend = 1; + } + else if ((ch = *pin++) == '\0') { + /* There's a lone trailing %; doesn't make sense. */ + PyErr_SetString(PyExc_ValueError, "strftime format " + "ends with raw %"); + goto Done; + } + /* A % has been seen and ch is the character after it. */ + else if (ch == 'z') { + if (zreplacement == NULL) { + /* format utcoffset */ + char buf[100]; + PyObject *tzinfo = get_tzinfo_member(object); + zreplacement = PyString_FromString(""); + if (zreplacement == NULL) goto Done; + if (tzinfo != Py_None && tzinfo != NULL) { + assert(tzinfoarg != NULL); + if (format_utcoffset(buf, + sizeof(buf), + "", + tzinfo, + tzinfoarg) < 0) + goto Done; + Py_DECREF(zreplacement); + zreplacement = PyString_FromString(buf); + if (zreplacement == NULL) goto Done; + } + } + assert(zreplacement != NULL); + ptoappend = PyString_AsString(zreplacement); + ntoappend = PyString_Size(zreplacement); + } + else if (ch == 'Z') { + /* format tzname */ + if (Zreplacement == NULL) { + PyObject *tzinfo = get_tzinfo_member(object); + Zreplacement = PyString_FromString(""); + if (Zreplacement == NULL) goto Done; + if (tzinfo != Py_None && tzinfo != NULL) { + PyObject *temp; + assert(tzinfoarg != NULL); + temp = call_tzname(tzinfo, tzinfoarg); + if (temp == NULL) goto Done; + if (temp != Py_None) { + assert(PyString_Check(temp)); + /* Since the tzname is getting + * stuffed into the format, we + * have to double any % signs + * so that strftime doesn't + * treat them as format codes. + */ + Py_DECREF(Zreplacement); + Zreplacement = PyObject_CallMethod( + temp, "replace", + "ss", "%", "%%"); + Py_DECREF(temp); + if (Zreplacement == NULL) + goto Done; + } + else + Py_DECREF(temp); + } + } + assert(Zreplacement != NULL); + ptoappend = PyString_AsString(Zreplacement); + ntoappend = PyString_Size(Zreplacement); + } + else { + /* percent followed by neither z nor Z */ + ptoappend = pin - 2; + ntoappend = 2; + } + + /* Append the ntoappend chars starting at ptoappend to + * the new format. + */ + assert(ntoappend >= 0); + if (ntoappend == 0) + continue; + while (usednew + ntoappend > totalnew) { + int bigger = totalnew << 1; + if ((bigger >> 1) != totalnew) { /* overflow */ + PyErr_NoMemory(); + goto Done; + } + if (_PyString_Resize(&newfmt, bigger) < 0) + goto Done; + totalnew = bigger; + pnew = PyString_AsString(newfmt) + usednew; + } + memcpy(pnew, ptoappend, ntoappend); + pnew += ntoappend; + usednew += ntoappend; + assert(usednew <= totalnew); + } /* end while() */ + + if (_PyString_Resize(&newfmt, usednew) < 0) + goto Done; + { + PyObject *time = PyImport_ImportModule("time"); + if (time == NULL) + goto Done; + result = PyObject_CallMethod(time, "strftime", "OO", + newfmt, timetuple); + Py_DECREF(time); + } + Done: + Py_XDECREF(zreplacement); + Py_XDECREF(Zreplacement); + Py_XDECREF(newfmt); + return result; +} + +static char * +isoformat_date(PyDateTime_Date *dt, char buffer[], int bufflen) +{ + int x; + x = PyOS_snprintf(buffer, bufflen, + "%04d-%02d-%02d", + GET_YEAR(dt), GET_MONTH(dt), GET_DAY(dt)); + return buffer + x; +} + +static void +isoformat_time(PyDateTime_DateTime *dt, char buffer[], int bufflen) +{ + int us = DATE_GET_MICROSECOND(dt); + + PyOS_snprintf(buffer, bufflen, + "%02d:%02d:%02d", /* 8 characters */ + DATE_GET_HOUR(dt), + DATE_GET_MINUTE(dt), + DATE_GET_SECOND(dt)); + if (us) + PyOS_snprintf(buffer + 8, bufflen - 8, ".%06d", us); +} + +/* --------------------------------------------------------------------------- + * Wrap functions from the time module. These aren't directly available + * from C. Perhaps they should be. + */ + +/* Call time.time() and return its result (a Python float). */ +static PyObject * +time_time(void) +{ + PyObject *result = NULL; + PyObject *time = PyImport_ImportModule("time"); + + if (time != NULL) { + result = PyObject_CallMethod(time, "time", "()"); + Py_DECREF(time); + } + return result; +} + +/* Build a time.struct_time. The weekday and day number are automatically + * computed from the y,m,d args. + */ +static PyObject * +build_struct_time(int y, int m, int d, int hh, int mm, int ss, int dstflag) +{ + PyObject *time; + PyObject *result = NULL; + + time = PyImport_ImportModule("time"); + if (time != NULL) { + result = PyObject_CallMethod(time, "struct_time", + "((iiiiiiiii))", + y, m, d, + hh, mm, ss, + weekday(y, m, d), + days_before_month(y, m) + d, + dstflag); + Py_DECREF(time); + } + return result; +} + +/* --------------------------------------------------------------------------- + * Miscellaneous helpers. + */ + +/* For obscure reasons, we need to use tp_richcompare instead of tp_compare. + * The comparisons here all most naturally compute a cmp()-like result. + * This little helper turns that into a bool result for rich comparisons. + */ +static PyObject * +diff_to_bool(int diff, int op) +{ + PyObject *result; + int istrue; + + switch (op) { + case Py_EQ: istrue = diff == 0; break; + case Py_NE: istrue = diff != 0; break; + case Py_LE: istrue = diff <= 0; break; + case Py_GE: istrue = diff >= 0; break; + case Py_LT: istrue = diff < 0; break; + case Py_GT: istrue = diff > 0; break; + default: + assert(! "op unknown"); + istrue = 0; /* To shut up compiler */ + } + result = istrue ? Py_True : Py_False; + Py_INCREF(result); + return result; +} + +/* Raises a "can't compare" TypeError and returns NULL. */ +static PyObject * +cmperror(PyObject *a, PyObject *b) +{ + PyErr_Format(PyExc_TypeError, + "can't compare %s to %s", + a->ob_type->tp_name, b->ob_type->tp_name); + return NULL; +} + +/* --------------------------------------------------------------------------- + * Cached Python objects; these are set by the module init function. + */ + +/* Conversion factors. */ +static PyObject *us_per_us = NULL; /* 1 */ +static PyObject *us_per_ms = NULL; /* 1000 */ +static PyObject *us_per_second = NULL; /* 1000000 */ +static PyObject *us_per_minute = NULL; /* 1e6 * 60 as Python int */ +static PyObject *us_per_hour = NULL; /* 1e6 * 3600 as Python long */ +static PyObject *us_per_day = NULL; /* 1e6 * 3600 * 24 as Python long */ +static PyObject *us_per_week = NULL; /* 1e6*3600*24*7 as Python long */ +static PyObject *seconds_per_day = NULL; /* 3600*24 as Python int */ + +/* --------------------------------------------------------------------------- + * Class implementations. + */ + +/* + * PyDateTime_Delta implementation. + */ + +/* Convert a timedelta to a number of us, + * (24*3600*self.days + self.seconds)*1000000 + self.microseconds + * as a Python int or long. + * Doing mixed-radix arithmetic by hand instead is excruciating in C, + * due to ubiquitous overflow possibilities. + */ +static PyObject * +delta_to_microseconds(PyDateTime_Delta *self) +{ + PyObject *x1 = NULL; + PyObject *x2 = NULL; + PyObject *x3 = NULL; + PyObject *result = NULL; + + x1 = PyInt_FromLong(GET_TD_DAYS(self)); + if (x1 == NULL) + goto Done; + x2 = PyNumber_Multiply(x1, seconds_per_day); /* days in seconds */ + if (x2 == NULL) + goto Done; + Py_DECREF(x1); + x1 = NULL; + + /* x2 has days in seconds */ + x1 = PyInt_FromLong(GET_TD_SECONDS(self)); /* seconds */ + if (x1 == NULL) + goto Done; + x3 = PyNumber_Add(x1, x2); /* days and seconds in seconds */ + if (x3 == NULL) + goto Done; + Py_DECREF(x1); + Py_DECREF(x2); + x1 = x2 = NULL; + + /* x3 has days+seconds in seconds */ + x1 = PyNumber_Multiply(x3, us_per_second); /* us */ + if (x1 == NULL) + goto Done; + Py_DECREF(x3); + x3 = NULL; + + /* x1 has days+seconds in us */ + x2 = PyInt_FromLong(GET_TD_MICROSECONDS(self)); + if (x2 == NULL) + goto Done; + result = PyNumber_Add(x1, x2); + +Done: + Py_XDECREF(x1); + Py_XDECREF(x2); + Py_XDECREF(x3); + return result; +} + +/* Convert a number of us (as a Python int or long) to a timedelta. + */ +static PyObject * +microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) +{ + int us; + int s; + int d; + long temp; + + PyObject *tuple = NULL; + PyObject *num = NULL; + PyObject *result = NULL; + + tuple = PyNumber_Divmod(pyus, us_per_second); + if (tuple == NULL) + goto Done; + + num = PyTuple_GetItem(tuple, 1); /* us */ + if (num == NULL) + goto Done; + temp = PyLong_AsLong(num); + num = NULL; + if (temp == -1 && PyErr_Occurred()) + goto Done; + assert(0 <= temp && temp < 1000000); + us = (int)temp; + if (us < 0) { + /* The divisor was positive, so this must be an error. */ + assert(PyErr_Occurred()); + goto Done; + } + + num = PyTuple_GetItem(tuple, 0); /* leftover seconds */ + if (num == NULL) + goto Done; + Py_INCREF(num); + Py_DECREF(tuple); + + tuple = PyNumber_Divmod(num, seconds_per_day); + if (tuple == NULL) + goto Done; + Py_DECREF(num); + + num = PyTuple_GetItem(tuple, 1); /* seconds */ + if (num == NULL) + goto Done; + temp = PyLong_AsLong(num); + num = NULL; + if (temp == -1 && PyErr_Occurred()) + goto Done; + assert(0 <= temp && temp < 24*3600); + s = (int)temp; + + if (s < 0) { + /* The divisor was positive, so this must be an error. */ + assert(PyErr_Occurred()); + goto Done; + } + + num = PyTuple_GetItem(tuple, 0); /* leftover days */ + if (num == NULL) + goto Done; + Py_INCREF(num); + temp = PyLong_AsLong(num); + if (temp == -1 && PyErr_Occurred()) + goto Done; + d = (int)temp; + if ((long)d != temp) { + PyErr_SetString(PyExc_OverflowError, "normalized days too " + "large to fit in a C int"); + goto Done; + } + result = new_delta_ex(d, s, us, 0, type); + +Done: + Py_XDECREF(tuple); + Py_XDECREF(num); + return result; +} + +#define microseconds_to_delta(pymicros) \ + microseconds_to_delta_ex(pymicros, &PyDateTime_DeltaType) + +static PyObject * +multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta) +{ + PyObject *pyus_in; + PyObject *pyus_out; + PyObject *result; + + pyus_in = delta_to_microseconds(delta); + if (pyus_in == NULL) + return NULL; + + pyus_out = PyNumber_Multiply(pyus_in, intobj); + Py_DECREF(pyus_in); + if (pyus_out == NULL) + return NULL; + + result = microseconds_to_delta(pyus_out); + Py_DECREF(pyus_out); + return result; +} + +static PyObject * +divide_timedelta_int(PyDateTime_Delta *delta, PyObject *intobj) +{ + PyObject *pyus_in; + PyObject *pyus_out; + PyObject *result; + + pyus_in = delta_to_microseconds(delta); + if (pyus_in == NULL) + return NULL; + + pyus_out = PyNumber_FloorDivide(pyus_in, intobj); + Py_DECREF(pyus_in); + if (pyus_out == NULL) + return NULL; + + result = microseconds_to_delta(pyus_out); + Py_DECREF(pyus_out); + return result; +} + +static PyObject * +delta_add(PyObject *left, PyObject *right) +{ + PyObject *result = Py_NotImplemented; + + if (PyDelta_Check(left) && PyDelta_Check(right)) { + /* delta + delta */ + /* The C-level additions can't overflow because of the + * invariant bounds. + */ + int days = GET_TD_DAYS(left) + GET_TD_DAYS(right); + int seconds = GET_TD_SECONDS(left) + GET_TD_SECONDS(right); + int microseconds = GET_TD_MICROSECONDS(left) + + GET_TD_MICROSECONDS(right); + result = new_delta(days, seconds, microseconds, 1); + } + + if (result == Py_NotImplemented) + Py_INCREF(result); + return result; +} + +static PyObject * +delta_negative(PyDateTime_Delta *self) +{ + return new_delta(-GET_TD_DAYS(self), + -GET_TD_SECONDS(self), + -GET_TD_MICROSECONDS(self), + 1); +} + +static PyObject * +delta_positive(PyDateTime_Delta *self) +{ + /* Could optimize this (by returning self) if this isn't a + * subclass -- but who uses unary + ? Approximately nobody. + */ + return new_delta(GET_TD_DAYS(self), + GET_TD_SECONDS(self), + GET_TD_MICROSECONDS(self), + 0); +} + +static PyObject * +delta_abs(PyDateTime_Delta *self) +{ + PyObject *result; + + assert(GET_TD_MICROSECONDS(self) >= 0); + assert(GET_TD_SECONDS(self) >= 0); + + if (GET_TD_DAYS(self) < 0) + result = delta_negative(self); + else + result = delta_positive(self); + + return result; +} + +static PyObject * +delta_subtract(PyObject *left, PyObject *right) +{ + PyObject *result = Py_NotImplemented; + + if (PyDelta_Check(left) && PyDelta_Check(right)) { + /* delta - delta */ + PyObject *minus_right = PyNumber_Negative(right); + if (minus_right) { + result = delta_add(left, minus_right); + Py_DECREF(minus_right); + } + else + result = NULL; + } + + if (result == Py_NotImplemented) + Py_INCREF(result); + return result; +} + +/* This is more natural as a tp_compare, but doesn't work then: for whatever + * reason, Python's try_3way_compare ignores tp_compare unless + * PyInstance_Check returns true, but these aren't old-style classes. + */ +static PyObject * +delta_richcompare(PyDateTime_Delta *self, PyObject *other, int op) +{ + int diff = 42; /* nonsense */ + + if (PyDelta_Check(other)) { + diff = GET_TD_DAYS(self) - GET_TD_DAYS(other); + if (diff == 0) { + diff = GET_TD_SECONDS(self) - GET_TD_SECONDS(other); + if (diff == 0) + diff = GET_TD_MICROSECONDS(self) - + GET_TD_MICROSECONDS(other); + } + } + else if (op == Py_EQ || op == Py_NE) + diff = 1; /* any non-zero value will do */ + + else /* stop this from falling back to address comparison */ + return cmperror((PyObject *)self, other); + + return diff_to_bool(diff, op); +} + +static PyObject *delta_getstate(PyDateTime_Delta *self); + +static long +delta_hash(PyDateTime_Delta *self) +{ + if (self->hashcode == -1) { + PyObject *temp = delta_getstate(self); + if (temp != NULL) { + self->hashcode = PyObject_Hash(temp); + Py_DECREF(temp); + } + } + return self->hashcode; +} + +static PyObject * +delta_multiply(PyObject *left, PyObject *right) +{ + PyObject *result = Py_NotImplemented; + + if (PyDelta_Check(left)) { + /* delta * ??? */ + if (PyInt_Check(right) || PyLong_Check(right)) + result = multiply_int_timedelta(right, + (PyDateTime_Delta *) left); + } + else if (PyInt_Check(left) || PyLong_Check(left)) + result = multiply_int_timedelta(left, + (PyDateTime_Delta *) right); + + if (result == Py_NotImplemented) + Py_INCREF(result); + return result; +} + +static PyObject * +delta_divide(PyObject *left, PyObject *right) +{ + PyObject *result = Py_NotImplemented; + + if (PyDelta_Check(left)) { + /* delta * ??? */ + if (PyInt_Check(right) || PyLong_Check(right)) + result = divide_timedelta_int( + (PyDateTime_Delta *)left, + right); + } + + if (result == Py_NotImplemented) + Py_INCREF(result); + return result; +} + +/* Fold in the value of the tag ("seconds", "weeks", etc) component of a + * timedelta constructor. sofar is the # of microseconds accounted for + * so far, and there are factor microseconds per current unit, the number + * of which is given by num. num * factor is added to sofar in a + * numerically careful way, and that's the result. Any fractional + * microseconds left over (this can happen if num is a float type) are + * added into *leftover. + * Note that there are many ways this can give an error (NULL) return. + */ +static PyObject * +accum(const char* tag, PyObject *sofar, PyObject *num, PyObject *factor, + double *leftover) +{ + PyObject *prod; + PyObject *sum; + + assert(num != NULL); + + if (PyInt_Check(num) || PyLong_Check(num)) { + prod = PyNumber_Multiply(num, factor); + if (prod == NULL) + return NULL; + sum = PyNumber_Add(sofar, prod); + Py_DECREF(prod); + return sum; + } + + if (PyFloat_Check(num)) { + double dnum; + double fracpart; + double intpart; + PyObject *x; + PyObject *y; + + /* The Plan: decompose num into an integer part and a + * fractional part, num = intpart + fracpart. + * Then num * factor == + * intpart * factor + fracpart * factor + * and the LHS can be computed exactly in long arithmetic. + * The RHS is again broken into an int part and frac part. + * and the frac part is added into *leftover. + */ + dnum = PyFloat_AsDouble(num); + if (dnum == -1.0 && PyErr_Occurred()) + return NULL; + fracpart = modf(dnum, &intpart); + x = PyLong_FromDouble(intpart); + if (x == NULL) + return NULL; + + prod = PyNumber_Multiply(x, factor); + Py_DECREF(x); + if (prod == NULL) + return NULL; + + sum = PyNumber_Add(sofar, prod); + Py_DECREF(prod); + if (sum == NULL) + return NULL; + + if (fracpart == 0.0) + return sum; + /* So far we've lost no information. Dealing with the + * fractional part requires float arithmetic, and may + * lose a little info. + */ + assert(PyInt_Check(factor) || PyLong_Check(factor)); + if (PyInt_Check(factor)) + dnum = (double)PyInt_AsLong(factor); + else + dnum = PyLong_AsDouble(factor); + + dnum *= fracpart; + fracpart = modf(dnum, &intpart); + x = PyLong_FromDouble(intpart); + if (x == NULL) { + Py_DECREF(sum); + return NULL; + } + + y = PyNumber_Add(sum, x); + Py_DECREF(sum); + Py_DECREF(x); + *leftover += fracpart; + return y; + } + + PyErr_Format(PyExc_TypeError, + "unsupported type for timedelta %s component: %s", + tag, num->ob_type->tp_name); + return NULL; +} + +static PyObject * +delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + PyObject *self = NULL; + + /* Argument objects. */ + PyObject *day = NULL; + PyObject *second = NULL; + PyObject *us = NULL; + PyObject *ms = NULL; + PyObject *minute = NULL; + PyObject *hour = NULL; + PyObject *week = NULL; + + PyObject *x = NULL; /* running sum of microseconds */ + PyObject *y = NULL; /* temp sum of microseconds */ + double leftover_us = 0.0; + + static char *keywords[] = { + "days", "seconds", "microseconds", "milliseconds", + "minutes", "hours", "weeks", NULL + }; + + if (PyArg_ParseTupleAndKeywords(args, kw, "|OOOOOOO:__new__", + keywords, + &day, &second, &us, + &ms, &minute, &hour, &week) == 0) + goto Done; + + x = PyInt_FromLong(0); + if (x == NULL) + goto Done; + +#define CLEANUP \ + Py_DECREF(x); \ + x = y; \ + if (x == NULL) \ + goto Done + + if (us) { + y = accum("microseconds", x, us, us_per_us, &leftover_us); + CLEANUP; + } + if (ms) { + y = accum("milliseconds", x, ms, us_per_ms, &leftover_us); + CLEANUP; + } + if (second) { + y = accum("seconds", x, second, us_per_second, &leftover_us); + CLEANUP; + } + if (minute) { + y = accum("minutes", x, minute, us_per_minute, &leftover_us); + CLEANUP; + } + if (hour) { + y = accum("hours", x, hour, us_per_hour, &leftover_us); + CLEANUP; + } + if (day) { + y = accum("days", x, day, us_per_day, &leftover_us); + CLEANUP; + } + if (week) { + y = accum("weeks", x, week, us_per_week, &leftover_us); + CLEANUP; + } + if (leftover_us) { + /* Round to nearest whole # of us, and add into x. */ + PyObject *temp = PyLong_FromLong(round_to_long(leftover_us)); + if (temp == NULL) { + Py_DECREF(x); + goto Done; + } + y = PyNumber_Add(x, temp); + Py_DECREF(temp); + CLEANUP; + } + + self = microseconds_to_delta_ex(x, type); + Py_DECREF(x); +Done: + return self; + +#undef CLEANUP +} + +static int +delta_nonzero(PyDateTime_Delta *self) +{ + return (GET_TD_DAYS(self) != 0 + || GET_TD_SECONDS(self) != 0 + || GET_TD_MICROSECONDS(self) != 0); +} + +static PyObject * +delta_repr(PyDateTime_Delta *self) +{ + if (GET_TD_MICROSECONDS(self) != 0) + return PyString_FromFormat("%s(%d, %d, %d)", + self->ob_type->tp_name, + GET_TD_DAYS(self), + GET_TD_SECONDS(self), + GET_TD_MICROSECONDS(self)); + if (GET_TD_SECONDS(self) != 0) + return PyString_FromFormat("%s(%d, %d)", + self->ob_type->tp_name, + GET_TD_DAYS(self), + GET_TD_SECONDS(self)); + + return PyString_FromFormat("%s(%d)", + self->ob_type->tp_name, + GET_TD_DAYS(self)); +} + +static PyObject * +delta_str(PyDateTime_Delta *self) +{ + int days = GET_TD_DAYS(self); + int seconds = GET_TD_SECONDS(self); + int us = GET_TD_MICROSECONDS(self); + int hours; + int minutes; + char buf[100]; + char *pbuf = buf; + size_t buflen = sizeof(buf); + int n; + + minutes = divmod(seconds, 60, &seconds); + hours = divmod(minutes, 60, &minutes); + + if (days) { + n = PyOS_snprintf(pbuf, buflen, "%d day%s, ", days, + (days == 1 || days == -1) ? "" : "s"); + if (n < 0 || (size_t)n >= buflen) + goto Fail; + pbuf += n; + buflen -= (size_t)n; + } + + n = PyOS_snprintf(pbuf, buflen, "%d:%02d:%02d", + hours, minutes, seconds); + if (n < 0 || (size_t)n >= buflen) + goto Fail; + pbuf += n; + buflen -= (size_t)n; + + if (us) { + n = PyOS_snprintf(pbuf, buflen, ".%06d", us); + if (n < 0 || (size_t)n >= buflen) + goto Fail; + pbuf += n; + } + + return PyString_FromStringAndSize(buf, pbuf - buf); + + Fail: + PyErr_SetString(PyExc_SystemError, "goofy result from PyOS_snprintf"); + return NULL; +} + +/* Pickle support, a simple use of __reduce__. */ + +/* __getstate__ isn't exposed */ +static PyObject * +delta_getstate(PyDateTime_Delta *self) +{ + return Py_BuildValue("iii", GET_TD_DAYS(self), + GET_TD_SECONDS(self), + GET_TD_MICROSECONDS(self)); +} + +static PyObject * +delta_reduce(PyDateTime_Delta* self) +{ + return Py_BuildValue("ON", self->ob_type, delta_getstate(self)); +} + +#define OFFSET(field) offsetof(PyDateTime_Delta, field) + +static PyMemberDef delta_members[] = { + + {"days", T_INT, OFFSET(days), READONLY, + PyDoc_STR("Number of days.")}, + + {"seconds", T_INT, OFFSET(seconds), READONLY, + PyDoc_STR("Number of seconds (>= 0 and less than 1 day).")}, + + {"microseconds", T_INT, OFFSET(microseconds), READONLY, + PyDoc_STR("Number of microseconds (>= 0 and less than 1 second).")}, + {NULL} +}; + +static PyMethodDef delta_methods[] = { + {"__reduce__", (PyCFunction)delta_reduce, METH_NOARGS, + PyDoc_STR("__reduce__() -> (cls, state)")}, + + {NULL, NULL}, +}; + +static char delta_doc[] = +PyDoc_STR("Difference between two datetime values."); + +static PyNumberMethods delta_as_number = { + delta_add, /* nb_add */ + delta_subtract, /* nb_subtract */ + delta_multiply, /* nb_multiply */ + delta_divide, /* nb_divide */ + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + (unaryfunc)delta_negative, /* nb_negative */ + (unaryfunc)delta_positive, /* nb_positive */ + (unaryfunc)delta_abs, /* nb_absolute */ + (inquiry)delta_nonzero, /* nb_nonzero */ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + 0, /*nb_and*/ + 0, /*nb_xor*/ + 0, /*nb_or*/ + 0, /*nb_coerce*/ + 0, /*nb_int*/ + 0, /*nb_long*/ + 0, /*nb_float*/ + 0, /*nb_oct*/ + 0, /*nb_hex*/ + 0, /*nb_inplace_add*/ + 0, /*nb_inplace_subtract*/ + 0, /*nb_inplace_multiply*/ + 0, /*nb_inplace_divide*/ + 0, /*nb_inplace_remainder*/ + 0, /*nb_inplace_power*/ + 0, /*nb_inplace_lshift*/ + 0, /*nb_inplace_rshift*/ + 0, /*nb_inplace_and*/ + 0, /*nb_inplace_xor*/ + 0, /*nb_inplace_or*/ + delta_divide, /* nb_floor_divide */ + 0, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +}; + +static PyTypeObject PyDateTime_DeltaType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "datetime.timedelta", /* tp_name */ + sizeof(PyDateTime_Delta), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)delta_repr, /* tp_repr */ + &delta_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)delta_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)delta_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + delta_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)delta_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + delta_methods, /* tp_methods */ + delta_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + delta_new, /* tp_new */ + 0, /* tp_free */ +}; + +/* + * PyDateTime_Date implementation. + */ + +/* Accessor properties. */ + +static PyObject * +date_year(PyDateTime_Date *self, void *unused) +{ + return PyInt_FromLong(GET_YEAR(self)); +} + +static PyObject * +date_month(PyDateTime_Date *self, void *unused) +{ + return PyInt_FromLong(GET_MONTH(self)); +} + +static PyObject * +date_day(PyDateTime_Date *self, void *unused) +{ + return PyInt_FromLong(GET_DAY(self)); +} + +static PyGetSetDef date_getset[] = { + {"year", (getter)date_year}, + {"month", (getter)date_month}, + {"day", (getter)date_day}, + {NULL} +}; + +/* Constructors. */ + +static char *date_kws[] = {"year", "month", "day", NULL}; + +static PyObject * +date_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + PyObject *self = NULL; + PyObject *state; + int year; + int month; + int day; + + /* Check for invocation from pickle with __getstate__ state */ + if (PyTuple_GET_SIZE(args) == 1 && + PyString_Check(state = PyTuple_GET_ITEM(args, 0)) && + PyString_GET_SIZE(state) == _PyDateTime_DATE_DATASIZE) + { + PyDateTime_Date *me; + + me = PyObject_New(PyDateTime_Date, type); + if (me != NULL) { + char *pdata = PyString_AS_STRING(state); + memcpy(me->data, pdata, _PyDateTime_DATE_DATASIZE); + me->hashcode = -1; + } + return (PyObject *)me; + } + + if (PyArg_ParseTupleAndKeywords(args, kw, "iii", date_kws, + &year, &month, &day)) { + if (check_date_args(year, month, day) < 0) + return NULL; + self = new_date_ex(year, month, day, type); + } + return self; +} + +/* Return new date from localtime(t). */ +static PyObject * +date_local_from_time_t(PyObject *cls, time_t t) +{ + struct tm *tm; + PyObject *result = NULL; + + tm = localtime(&t); + if (tm) + result = PyObject_CallFunction(cls, "iii", + tm->tm_year + 1900, + tm->tm_mon + 1, + tm->tm_mday); + else + PyErr_SetString(PyExc_ValueError, + "timestamp out of range for " + "platform localtime() function"); + return result; +} + +/* Return new date from current time. + * We say this is equivalent to fromtimestamp(time.time()), and the + * only way to be sure of that is to *call* time.time(). That's not + * generally the same as calling C's time. + */ +static PyObject * +date_today(PyObject *cls, PyObject *dummy) +{ + PyObject *time; + PyObject *result; + + time = time_time(); + if (time == NULL) + return NULL; + + /* Note well: today() is a class method, so this may not call + * date.fromtimestamp. For example, it may call + * datetime.fromtimestamp. That's why we need all the accuracy + * time.time() delivers; if someone were gonzo about optimization, + * date.today() could get away with plain C time(). + */ + result = PyObject_CallMethod(cls, "fromtimestamp", "O", time); + Py_DECREF(time); + return result; +} + +/* Return new date from given timestamp (Python timestamp -- a double). */ +static PyObject * +date_fromtimestamp(PyObject *cls, PyObject *args) +{ + double timestamp; + PyObject *result = NULL; + + if (PyArg_ParseTuple(args, "d:fromtimestamp", ×tamp)) + result = date_local_from_time_t(cls, (time_t)timestamp); + return result; +} + +/* Return new date from proleptic Gregorian ordinal. Raises ValueError if + * the ordinal is out of range. + */ +static PyObject * +date_fromordinal(PyObject *cls, PyObject *args) +{ + PyObject *result = NULL; + int ordinal; + + if (PyArg_ParseTuple(args, "i:fromordinal", &ordinal)) { + int year; + int month; + int day; + + if (ordinal < 1) + PyErr_SetString(PyExc_ValueError, "ordinal must be " + ">= 1"); + else { + ord_to_ymd(ordinal, &year, &month, &day); + result = PyObject_CallFunction(cls, "iii", + year, month, day); + } + } + return result; +} + +/* + * Date arithmetic. + */ + +/* date + timedelta -> date. If arg negate is true, subtract the timedelta + * instead. + */ +static PyObject * +add_date_timedelta(PyDateTime_Date *date, PyDateTime_Delta *delta, int negate) +{ + PyObject *result = NULL; + int year = GET_YEAR(date); + int month = GET_MONTH(date); + int deltadays = GET_TD_DAYS(delta); + /* C-level overflow is impossible because |deltadays| < 1e9. */ + int day = GET_DAY(date) + (negate ? -deltadays : deltadays); + + if (normalize_date(&year, &month, &day) >= 0) + result = new_date(year, month, day); + return result; +} + +static PyObject * +date_add(PyObject *left, PyObject *right) +{ + if (PyDateTime_Check(left) || PyDateTime_Check(right)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + if (PyDate_Check(left)) { + /* date + ??? */ + if (PyDelta_Check(right)) + /* date + delta */ + return add_date_timedelta((PyDateTime_Date *) left, + (PyDateTime_Delta *) right, + 0); + } + else { + /* ??? + date + * 'right' must be one of us, or we wouldn't have been called + */ + if (PyDelta_Check(left)) + /* delta + date */ + return add_date_timedelta((PyDateTime_Date *) right, + (PyDateTime_Delta *) left, + 0); + } + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static PyObject * +date_subtract(PyObject *left, PyObject *right) +{ + if (PyDateTime_Check(left) || PyDateTime_Check(right)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + if (PyDate_Check(left)) { + if (PyDate_Check(right)) { + /* date - date */ + int left_ord = ymd_to_ord(GET_YEAR(left), + GET_MONTH(left), + GET_DAY(left)); + int right_ord = ymd_to_ord(GET_YEAR(right), + GET_MONTH(right), + GET_DAY(right)); + return new_delta(left_ord - right_ord, 0, 0, 0); + } + if (PyDelta_Check(right)) { + /* date - delta */ + return add_date_timedelta((PyDateTime_Date *) left, + (PyDateTime_Delta *) right, + 1); + } + } + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + + +/* Various ways to turn a date into a string. */ + +static PyObject * +date_repr(PyDateTime_Date *self) +{ + char buffer[1028]; + char *typename; + + typename = self->ob_type->tp_name; + PyOS_snprintf(buffer, sizeof(buffer), "%s(%d, %d, %d)", + typename, + GET_YEAR(self), GET_MONTH(self), GET_DAY(self)); + + return PyString_FromString(buffer); +} + +static PyObject * +date_isoformat(PyDateTime_Date *self) +{ + char buffer[128]; + + isoformat_date(self, buffer, sizeof(buffer)); + return PyString_FromString(buffer); +} + +/* str() calls the appropriate isoformat() method. */ +static PyObject * +date_str(PyDateTime_Date *self) +{ + return PyObject_CallMethod((PyObject *)self, "isoformat", "()"); +} + + +static PyObject * +date_ctime(PyDateTime_Date *self) +{ + return format_ctime(self, 0, 0, 0); +} + +static PyObject * +date_strftime(PyDateTime_Date *self, PyObject *args, PyObject *kw) +{ + /* This method can be inherited, and needs to call the + * timetuple() method appropriate to self's class. + */ + PyObject *result; + PyObject *format; + PyObject *tuple; + static char *keywords[] = {"format", NULL}; + + if (! PyArg_ParseTupleAndKeywords(args, kw, "O!:strftime", keywords, + &PyString_Type, &format)) + return NULL; + + tuple = PyObject_CallMethod((PyObject *)self, "timetuple", "()"); + if (tuple == NULL) + return NULL; + result = wrap_strftime((PyObject *)self, format, tuple, + (PyObject *)self); + Py_DECREF(tuple); + return result; +} + +/* ISO methods. */ + +static PyObject * +date_isoweekday(PyDateTime_Date *self) +{ + int dow = weekday(GET_YEAR(self), GET_MONTH(self), GET_DAY(self)); + + return PyInt_FromLong(dow + 1); +} + +static PyObject * +date_isocalendar(PyDateTime_Date *self) +{ + int year = GET_YEAR(self); + int week1_monday = iso_week1_monday(year); + int today = ymd_to_ord(year, GET_MONTH(self), GET_DAY(self)); + int week; + int day; + + week = divmod(today - week1_monday, 7, &day); + if (week < 0) { + --year; + week1_monday = iso_week1_monday(year); + week = divmod(today - week1_monday, 7, &day); + } + else if (week >= 52 && today >= iso_week1_monday(year + 1)) { + ++year; + week = 0; + } + return Py_BuildValue("iii", year, week + 1, day + 1); +} + +/* Miscellaneous methods. */ + +/* This is more natural as a tp_compare, but doesn't work then: for whatever + * reason, Python's try_3way_compare ignores tp_compare unless + * PyInstance_Check returns true, but these aren't old-style classes. + */ +static PyObject * +date_richcompare(PyDateTime_Date *self, PyObject *other, int op) +{ + int diff = 42; /* nonsense */ + + if (PyDate_Check(other)) + diff = memcmp(self->data, ((PyDateTime_Date *)other)->data, + _PyDateTime_DATE_DATASIZE); + + else if (PyObject_HasAttrString(other, "timetuple")) { + /* A hook for other kinds of date objects. */ + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + else if (op == Py_EQ || op == Py_NE) + diff = 1; /* any non-zero value will do */ + + else /* stop this from falling back to address comparison */ + return cmperror((PyObject *)self, other); + + return diff_to_bool(diff, op); +} + +static PyObject * +date_timetuple(PyDateTime_Date *self) +{ + return build_struct_time(GET_YEAR(self), + GET_MONTH(self), + GET_DAY(self), + 0, 0, 0, -1); +} + +static PyObject * +date_replace(PyDateTime_Date *self, PyObject *args, PyObject *kw) +{ + PyObject *clone; + PyObject *tuple; + int year = GET_YEAR(self); + int month = GET_MONTH(self); + int day = GET_DAY(self); + + if (! PyArg_ParseTupleAndKeywords(args, kw, "|iii:replace", date_kws, + &year, &month, &day)) + return NULL; + tuple = Py_BuildValue("iii", year, month, day); + if (tuple == NULL) + return NULL; + clone = date_new(self->ob_type, tuple, NULL); + Py_DECREF(tuple); + return clone; +} + +static PyObject *date_getstate(PyDateTime_Date *self); + +static long +date_hash(PyDateTime_Date *self) +{ + if (self->hashcode == -1) { + PyObject *temp = date_getstate(self); + if (temp != NULL) { + self->hashcode = PyObject_Hash(temp); + Py_DECREF(temp); + } + } + return self->hashcode; +} + +static PyObject * +date_toordinal(PyDateTime_Date *self) +{ + return PyInt_FromLong(ymd_to_ord(GET_YEAR(self), GET_MONTH(self), + GET_DAY(self))); +} + +static PyObject * +date_weekday(PyDateTime_Date *self) +{ + int dow = weekday(GET_YEAR(self), GET_MONTH(self), GET_DAY(self)); + + return PyInt_FromLong(dow); +} + +/* Pickle support, a simple use of __reduce__. */ + +/* __getstate__ isn't exposed */ +static PyObject * +date_getstate(PyDateTime_Date *self) +{ + return Py_BuildValue( + "(N)", + PyString_FromStringAndSize((char *)self->data, + _PyDateTime_DATE_DATASIZE)); +} + +static PyObject * +date_reduce(PyDateTime_Date *self, PyObject *arg) +{ + return Py_BuildValue("(ON)", self->ob_type, date_getstate(self)); +} + +static PyMethodDef date_methods[] = { + + /* Class methods: */ + + {"fromtimestamp", (PyCFunction)date_fromtimestamp, METH_VARARGS | + METH_CLASS, + PyDoc_STR("timestamp -> local date from a POSIX timestamp (like " + "time.time()).")}, + + {"fromordinal", (PyCFunction)date_fromordinal, METH_VARARGS | + METH_CLASS, + PyDoc_STR("int -> date corresponding to a proleptic Gregorian " + "ordinal.")}, + + {"today", (PyCFunction)date_today, METH_NOARGS | METH_CLASS, + PyDoc_STR("Current date or datetime: same as " + "self.__class__.fromtimestamp(time.time()).")}, + + /* Instance methods: */ + + {"ctime", (PyCFunction)date_ctime, METH_NOARGS, + PyDoc_STR("Return ctime() style string.")}, + + {"strftime", (PyCFunction)date_strftime, METH_KEYWORDS, + PyDoc_STR("format -> strftime() style string.")}, + + {"timetuple", (PyCFunction)date_timetuple, METH_NOARGS, + PyDoc_STR("Return time tuple, compatible with time.localtime().")}, + + {"isocalendar", (PyCFunction)date_isocalendar, METH_NOARGS, + PyDoc_STR("Return a 3-tuple containing ISO year, week number, and " + "weekday.")}, + + {"isoformat", (PyCFunction)date_isoformat, METH_NOARGS, + PyDoc_STR("Return string in ISO 8601 format, YYYY-MM-DD.")}, + + {"isoweekday", (PyCFunction)date_isoweekday, METH_NOARGS, + PyDoc_STR("Return the day of the week represented by the date.\n" + "Monday == 1 ... Sunday == 7")}, + + {"toordinal", (PyCFunction)date_toordinal, METH_NOARGS, + PyDoc_STR("Return proleptic Gregorian ordinal. January 1 of year " + "1 is day 1.")}, + + {"weekday", (PyCFunction)date_weekday, METH_NOARGS, + PyDoc_STR("Return the day of the week represented by the date.\n" + "Monday == 0 ... Sunday == 6")}, + + {"replace", (PyCFunction)date_replace, METH_KEYWORDS, + PyDoc_STR("Return date with new specified fields.")}, + + {"__reduce__", (PyCFunction)date_reduce, METH_NOARGS, + PyDoc_STR("__reduce__() -> (cls, state)")}, + + {NULL, NULL} +}; + +static char date_doc[] = +PyDoc_STR("Basic date type."); + +static PyNumberMethods date_as_number = { + date_add, /* nb_add */ + date_subtract, /* nb_subtract */ + 0, /* nb_multiply */ + 0, /* nb_divide */ + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + 0, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ + 0, /* nb_nonzero */ +}; + +static PyTypeObject PyDateTime_DateType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "datetime.date", /* tp_name */ + sizeof(PyDateTime_Date), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)date_repr, /* tp_repr */ + &date_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)date_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)date_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + date_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)date_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + date_methods, /* tp_methods */ + 0, /* tp_members */ + date_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + date_new, /* tp_new */ + 0, /* tp_free */ +}; + +/* + * PyDateTime_TZInfo implementation. + */ + +/* This is a pure abstract base class, so doesn't do anything beyond + * raising NotImplemented exceptions. Real tzinfo classes need + * to derive from this. This is mostly for clarity, and for efficiency in + * datetime and time constructors (their tzinfo arguments need to + * be subclasses of this tzinfo class, which is easy and quick to check). + * + * Note: For reasons having to do with pickling of subclasses, we have + * to allow tzinfo objects to be instantiated. This wasn't an issue + * in the Python implementation (__init__() could raise NotImplementedError + * there without ill effect), but doing so in the C implementation hit a + * brick wall. + */ + +static PyObject * +tzinfo_nogo(const char* methodname) +{ + PyErr_Format(PyExc_NotImplementedError, + "a tzinfo subclass must implement %s()", + methodname); + return NULL; +} + +/* Methods. A subclass must implement these. */ + +static PyObject * +tzinfo_tzname(PyDateTime_TZInfo *self, PyObject *dt) +{ + return tzinfo_nogo("tzname"); +} + +static PyObject * +tzinfo_utcoffset(PyDateTime_TZInfo *self, PyObject *dt) +{ + return tzinfo_nogo("utcoffset"); +} + +static PyObject * +tzinfo_dst(PyDateTime_TZInfo *self, PyObject *dt) +{ + return tzinfo_nogo("dst"); +} + +static PyObject * +tzinfo_fromutc(PyDateTime_TZInfo *self, PyDateTime_DateTime *dt) +{ + int y, m, d, hh, mm, ss, us; + + PyObject *result; + int off, dst; + int none; + int delta; + + if (! PyDateTime_Check(dt)) { + PyErr_SetString(PyExc_TypeError, + "fromutc: argument must be a datetime"); + return NULL; + } + if (! HASTZINFO(dt) || dt->tzinfo != (PyObject *)self) { + PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo " + "is not self"); + return NULL; + } + + off = call_utcoffset(dt->tzinfo, (PyObject *)dt, &none); + if (off == -1 && PyErr_Occurred()) + return NULL; + if (none) { + PyErr_SetString(PyExc_ValueError, "fromutc: non-None " + "utcoffset() result required"); + return NULL; + } + + dst = call_dst(dt->tzinfo, (PyObject *)dt, &none); + if (dst == -1 && PyErr_Occurred()) + return NULL; + if (none) { + PyErr_SetString(PyExc_ValueError, "fromutc: non-None " + "dst() result required"); + return NULL; + } + + y = GET_YEAR(dt); + m = GET_MONTH(dt); + d = GET_DAY(dt); + hh = DATE_GET_HOUR(dt); + mm = DATE_GET_MINUTE(dt); + ss = DATE_GET_SECOND(dt); + us = DATE_GET_MICROSECOND(dt); + + delta = off - dst; + mm += delta; + if ((mm < 0 || mm >= 60) && + normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0) + return NULL; + result = new_datetime(y, m, d, hh, mm, ss, us, dt->tzinfo); + if (result == NULL) + return result; + + dst = call_dst(dt->tzinfo, result, &none); + if (dst == -1 && PyErr_Occurred()) + goto Fail; + if (none) + goto Inconsistent; + if (dst == 0) + return result; + + mm += dst; + if ((mm < 0 || mm >= 60) && + normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0) + goto Fail; + Py_DECREF(result); + result = new_datetime(y, m, d, hh, mm, ss, us, dt->tzinfo); + return result; + +Inconsistent: + PyErr_SetString(PyExc_ValueError, "fromutc: tz.dst() gave" + "inconsistent results; cannot convert"); + + /* fall thru to failure */ +Fail: + Py_DECREF(result); + return NULL; +} + +/* + * Pickle support. This is solely so that tzinfo subclasses can use + * pickling -- tzinfo itself is supposed to be uninstantiable. + */ + +static PyObject * +tzinfo_reduce(PyObject *self) +{ + PyObject *args, *state, *tmp; + PyObject *getinitargs, *getstate; + + tmp = PyTuple_New(0); + if (tmp == NULL) + return NULL; + + getinitargs = PyObject_GetAttrString(self, "__getinitargs__"); + if (getinitargs != NULL) { + args = PyObject_CallObject(getinitargs, tmp); + Py_DECREF(getinitargs); + if (args == NULL) { + Py_DECREF(tmp); + return NULL; + } + } + else { + PyErr_Clear(); + args = tmp; + Py_INCREF(args); + } + + getstate = PyObject_GetAttrString(self, "__getstate__"); + if (getstate != NULL) { + state = PyObject_CallObject(getstate, tmp); + Py_DECREF(getstate); + if (state == NULL) { + Py_DECREF(args); + Py_DECREF(tmp); + return NULL; + } + } + else { + PyObject **dictptr; + PyErr_Clear(); + state = Py_None; + dictptr = _PyObject_GetDictPtr(self); + if (dictptr && *dictptr && PyDict_Size(*dictptr)) + state = *dictptr; + Py_INCREF(state); + } + + Py_DECREF(tmp); + + if (state == Py_None) { + Py_DECREF(state); + return Py_BuildValue("(ON)", self->ob_type, args); + } + else + return Py_BuildValue("(ONN)", self->ob_type, args, state); +} + +static PyMethodDef tzinfo_methods[] = { + + {"tzname", (PyCFunction)tzinfo_tzname, METH_O, + PyDoc_STR("datetime -> string name of time zone.")}, + + {"utcoffset", (PyCFunction)tzinfo_utcoffset, METH_O, + PyDoc_STR("datetime -> minutes east of UTC (negative for " + "west of UTC).")}, + + {"dst", (PyCFunction)tzinfo_dst, METH_O, + PyDoc_STR("datetime -> DST offset in minutes east of UTC.")}, + + {"fromutc", (PyCFunction)tzinfo_fromutc, METH_O, + PyDoc_STR("datetime in UTC -> datetime in local time.")}, + + {"__reduce__", (PyCFunction)tzinfo_reduce, METH_NOARGS, + PyDoc_STR("-> (cls, state)")}, + + {NULL, NULL} +}; + +static char tzinfo_doc[] = +PyDoc_STR("Abstract base class for time zone info objects."); + +statichere PyTypeObject PyDateTime_TZInfoType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "datetime.tzinfo", /* tp_name */ + sizeof(PyDateTime_TZInfo), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + tzinfo_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + tzinfo_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + 0, /* tp_free */ +}; + +/* + * PyDateTime_Time implementation. + */ + +/* Accessor properties. + */ + +static PyObject * +time_hour(PyDateTime_Time *self, void *unused) +{ + return PyInt_FromLong(TIME_GET_HOUR(self)); +} + +static PyObject * +time_minute(PyDateTime_Time *self, void *unused) +{ + return PyInt_FromLong(TIME_GET_MINUTE(self)); +} + +/* The name time_second conflicted with some platform header file. */ +static PyObject * +py_time_second(PyDateTime_Time *self, void *unused) +{ + return PyInt_FromLong(TIME_GET_SECOND(self)); +} + +static PyObject * +time_microsecond(PyDateTime_Time *self, void *unused) +{ + return PyInt_FromLong(TIME_GET_MICROSECOND(self)); +} + +static PyObject * +time_tzinfo(PyDateTime_Time *self, void *unused) +{ + PyObject *result = HASTZINFO(self) ? self->tzinfo : Py_None; + Py_INCREF(result); + return result; +} + +static PyGetSetDef time_getset[] = { + {"hour", (getter)time_hour}, + {"minute", (getter)time_minute}, + {"second", (getter)py_time_second}, + {"microsecond", (getter)time_microsecond}, + {"tzinfo", (getter)time_tzinfo}, + {NULL} +}; + +/* + * Constructors. + */ + +static char *time_kws[] = {"hour", "minute", "second", "microsecond", + "tzinfo", NULL}; + +static PyObject * +time_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + PyObject *self = NULL; + PyObject *state; + int hour = 0; + int minute = 0; + int second = 0; + int usecond = 0; + PyObject *tzinfo = Py_None; + + /* Check for invocation from pickle with __getstate__ state */ + if (PyTuple_GET_SIZE(args) >= 1 && + PyTuple_GET_SIZE(args) <= 2 && + PyString_Check(state = PyTuple_GET_ITEM(args, 0)) && + PyString_GET_SIZE(state) == _PyDateTime_TIME_DATASIZE) + { + PyDateTime_Time *me; + char aware; + + if (PyTuple_GET_SIZE(args) == 2) { + tzinfo = PyTuple_GET_ITEM(args, 1); + if (check_tzinfo_subclass(tzinfo) < 0) { + PyErr_SetString(PyExc_TypeError, "bad " + "tzinfo state arg"); + return NULL; + } + } + aware = (char)(tzinfo != Py_None); + me = (PyDateTime_Time *) time_alloc(&PyDateTime_TimeType, + aware); + if (me != NULL) { + char *pdata = PyString_AS_STRING(state); + + memcpy(me->data, pdata, _PyDateTime_TIME_DATASIZE); + me->hashcode = -1; + me->hastzinfo = aware; + if (aware) { + Py_INCREF(tzinfo); + me->tzinfo = tzinfo; + } + } + return (PyObject *)me; + } + + if (PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO", time_kws, + &hour, &minute, &second, &usecond, + &tzinfo)) { + if (check_time_args(hour, minute, second, usecond) < 0) + return NULL; + if (check_tzinfo_subclass(tzinfo) < 0) + return NULL; + self = new_time_ex(hour, minute, second, usecond, tzinfo, + type); + } + return self; +} + +/* + * Destructor. + */ + +static void +time_dealloc(PyDateTime_Time *self) +{ + if (HASTZINFO(self)) { + Py_XDECREF(self->tzinfo); + } + self->ob_type->tp_free((PyObject *)self); +} + +/* + * Indirect access to tzinfo methods. + */ + +/* These are all METH_NOARGS, so don't need to check the arglist. */ +static PyObject * +time_utcoffset(PyDateTime_Time *self, PyObject *unused) { + return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None, + "utcoffset", Py_None); +} + +static PyObject * +time_dst(PyDateTime_Time *self, PyObject *unused) { + return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None, + "dst", Py_None); +} + +static PyObject * +time_tzname(PyDateTime_Time *self, PyObject *unused) { + return call_tzname(HASTZINFO(self) ? self->tzinfo : Py_None, + Py_None); +} + +/* + * Various ways to turn a time into a string. + */ + +static PyObject * +time_repr(PyDateTime_Time *self) +{ + char buffer[100]; + char *typename = self->ob_type->tp_name; + int h = TIME_GET_HOUR(self); + int m = TIME_GET_MINUTE(self); + int s = TIME_GET_SECOND(self); + int us = TIME_GET_MICROSECOND(self); + PyObject *result = NULL; + + if (us) + PyOS_snprintf(buffer, sizeof(buffer), + "%s(%d, %d, %d, %d)", typename, h, m, s, us); + else if (s) + PyOS_snprintf(buffer, sizeof(buffer), + "%s(%d, %d, %d)", typename, h, m, s); + else + PyOS_snprintf(buffer, sizeof(buffer), + "%s(%d, %d)", typename, h, m); + result = PyString_FromString(buffer); + if (result != NULL && HASTZINFO(self)) + result = append_keyword_tzinfo(result, self->tzinfo); + return result; +} + +static PyObject * +time_str(PyDateTime_Time *self) +{ + return PyObject_CallMethod((PyObject *)self, "isoformat", "()"); +} + +static PyObject * +time_isoformat(PyDateTime_Time *self) +{ + char buf[100]; + PyObject *result; + /* Reuse the time format code from the datetime type. */ + PyDateTime_DateTime datetime; + PyDateTime_DateTime *pdatetime = &datetime; + + /* Copy over just the time bytes. */ + memcpy(pdatetime->data + _PyDateTime_DATE_DATASIZE, + self->data, + _PyDateTime_TIME_DATASIZE); + + isoformat_time(pdatetime, buf, sizeof(buf)); + result = PyString_FromString(buf); + if (result == NULL || ! HASTZINFO(self) || self->tzinfo == Py_None) + return result; + + /* We need to append the UTC offset. */ + if (format_utcoffset(buf, sizeof(buf), ":", self->tzinfo, + Py_None) < 0) { + Py_DECREF(result); + return NULL; + } + PyString_ConcatAndDel(&result, PyString_FromString(buf)); + return result; +} + +static PyObject * +time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw) +{ + PyObject *result; + PyObject *format; + PyObject *tuple; + static char *keywords[] = {"format", NULL}; + + if (! PyArg_ParseTupleAndKeywords(args, kw, "O!:strftime", keywords, + &PyString_Type, &format)) + return NULL; + + /* Python's strftime does insane things with the year part of the + * timetuple. The year is forced to (the otherwise nonsensical) + * 1900 to worm around that. + */ + tuple = Py_BuildValue("iiiiiiiii", + 1900, 0, 0, /* year, month, day */ + TIME_GET_HOUR(self), + TIME_GET_MINUTE(self), + TIME_GET_SECOND(self), + 0, 0, -1); /* weekday, daynum, dst */ + if (tuple == NULL) + return NULL; + assert(PyTuple_Size(tuple) == 9); + result = wrap_strftime((PyObject *)self, format, tuple, Py_None); + Py_DECREF(tuple); + return result; +} + +/* + * Miscellaneous methods. + */ + +/* This is more natural as a tp_compare, but doesn't work then: for whatever + * reason, Python's try_3way_compare ignores tp_compare unless + * PyInstance_Check returns true, but these aren't old-style classes. + */ +static PyObject * +time_richcompare(PyDateTime_Time *self, PyObject *other, int op) +{ + int diff; + naivety n1, n2; + int offset1, offset2; + + if (! PyTime_Check(other)) { + if (op == Py_EQ || op == Py_NE) { + PyObject *result = op == Py_EQ ? Py_False : Py_True; + Py_INCREF(result); + return result; + } + /* Stop this from falling back to address comparison. */ + return cmperror((PyObject *)self, other); + } + if (classify_two_utcoffsets((PyObject *)self, &offset1, &n1, Py_None, + other, &offset2, &n2, Py_None) < 0) + return NULL; + assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN); + /* If they're both naive, or both aware and have the same offsets, + * we get off cheap. Note that if they're both naive, offset1 == + * offset2 == 0 at this point. + */ + if (n1 == n2 && offset1 == offset2) { + diff = memcmp(self->data, ((PyDateTime_Time *)other)->data, + _PyDateTime_TIME_DATASIZE); + return diff_to_bool(diff, op); + } + + if (n1 == OFFSET_AWARE && n2 == OFFSET_AWARE) { + assert(offset1 != offset2); /* else last "if" handled it */ + /* Convert everything except microseconds to seconds. These + * can't overflow (no more than the # of seconds in 2 days). + */ + offset1 = TIME_GET_HOUR(self) * 3600 + + (TIME_GET_MINUTE(self) - offset1) * 60 + + TIME_GET_SECOND(self); + offset2 = TIME_GET_HOUR(other) * 3600 + + (TIME_GET_MINUTE(other) - offset2) * 60 + + TIME_GET_SECOND(other); + diff = offset1 - offset2; + if (diff == 0) + diff = TIME_GET_MICROSECOND(self) - + TIME_GET_MICROSECOND(other); + return diff_to_bool(diff, op); + } + + assert(n1 != n2); + PyErr_SetString(PyExc_TypeError, + "can't compare offset-naive and " + "offset-aware times"); + return NULL; +} + +static long +time_hash(PyDateTime_Time *self) +{ + if (self->hashcode == -1) { + naivety n; + int offset; + PyObject *temp; + + n = classify_utcoffset((PyObject *)self, Py_None, &offset); + assert(n != OFFSET_UNKNOWN); + if (n == OFFSET_ERROR) + return -1; + + /* Reduce this to a hash of another object. */ + if (offset == 0) + temp = PyString_FromStringAndSize((char *)self->data, + _PyDateTime_TIME_DATASIZE); + else { + int hour; + int minute; + + assert(n == OFFSET_AWARE); + assert(HASTZINFO(self)); + hour = divmod(TIME_GET_HOUR(self) * 60 + + TIME_GET_MINUTE(self) - offset, + 60, + &minute); + if (0 <= hour && hour < 24) + temp = new_time(hour, minute, + TIME_GET_SECOND(self), + TIME_GET_MICROSECOND(self), + Py_None); + else + temp = Py_BuildValue("iiii", + hour, minute, + TIME_GET_SECOND(self), + TIME_GET_MICROSECOND(self)); + } + if (temp != NULL) { + self->hashcode = PyObject_Hash(temp); + Py_DECREF(temp); + } + } + return self->hashcode; +} + +static PyObject * +time_replace(PyDateTime_Time *self, PyObject *args, PyObject *kw) +{ + PyObject *clone; + PyObject *tuple; + int hh = TIME_GET_HOUR(self); + int mm = TIME_GET_MINUTE(self); + int ss = TIME_GET_SECOND(self); + int us = TIME_GET_MICROSECOND(self); + PyObject *tzinfo = HASTZINFO(self) ? self->tzinfo : Py_None; + + if (! PyArg_ParseTupleAndKeywords(args, kw, "|iiiiO:replace", + time_kws, + &hh, &mm, &ss, &us, &tzinfo)) + return NULL; + tuple = Py_BuildValue("iiiiO", hh, mm, ss, us, tzinfo); + if (tuple == NULL) + return NULL; + clone = time_new(self->ob_type, tuple, NULL); + Py_DECREF(tuple); + return clone; +} + +static int +time_nonzero(PyDateTime_Time *self) +{ + int offset; + int none; + + if (TIME_GET_SECOND(self) || TIME_GET_MICROSECOND(self)) { + /* Since utcoffset is in whole minutes, nothing can + * alter the conclusion that this is nonzero. + */ + return 1; + } + offset = 0; + if (HASTZINFO(self) && self->tzinfo != Py_None) { + offset = call_utcoffset(self->tzinfo, Py_None, &none); + if (offset == -1 && PyErr_Occurred()) + return -1; + } + return (TIME_GET_MINUTE(self) - offset + TIME_GET_HOUR(self)*60) != 0; +} + +/* Pickle support, a simple use of __reduce__. */ + +/* Let basestate be the non-tzinfo data string. + * If tzinfo is None, this returns (basestate,), else (basestate, tzinfo). + * So it's a tuple in any (non-error) case. + * __getstate__ isn't exposed. + */ +static PyObject * +time_getstate(PyDateTime_Time *self) +{ + PyObject *basestate; + PyObject *result = NULL; + + basestate = PyString_FromStringAndSize((char *)self->data, + _PyDateTime_TIME_DATASIZE); + if (basestate != NULL) { + if (! HASTZINFO(self) || self->tzinfo == Py_None) + result = Py_BuildValue("(O)", basestate); + else + result = Py_BuildValue("OO", basestate, self->tzinfo); + Py_DECREF(basestate); + } + return result; +} + +static PyObject * +time_reduce(PyDateTime_Time *self, PyObject *arg) +{ + return Py_BuildValue("(ON)", self->ob_type, time_getstate(self)); +} + +static PyMethodDef time_methods[] = { + + {"isoformat", (PyCFunction)time_isoformat, METH_KEYWORDS, + PyDoc_STR("Return string in ISO 8601 format, HH:MM:SS[.mmmmmm]" + "[+HH:MM].")}, + + {"strftime", (PyCFunction)time_strftime, METH_KEYWORDS, + PyDoc_STR("format -> strftime() style string.")}, + + {"utcoffset", (PyCFunction)time_utcoffset, METH_NOARGS, + PyDoc_STR("Return self.tzinfo.utcoffset(self).")}, + + {"tzname", (PyCFunction)time_tzname, METH_NOARGS, + PyDoc_STR("Return self.tzinfo.tzname(self).")}, + + {"dst", (PyCFunction)time_dst, METH_NOARGS, + PyDoc_STR("Return self.tzinfo.dst(self).")}, + + {"replace", (PyCFunction)time_replace, METH_KEYWORDS, + PyDoc_STR("Return time with new specified fields.")}, + + {"__reduce__", (PyCFunction)time_reduce, METH_NOARGS, + PyDoc_STR("__reduce__() -> (cls, state)")}, + + {NULL, NULL} +}; + +static char time_doc[] = +PyDoc_STR("Time type."); + +static PyNumberMethods time_as_number = { + 0, /* nb_add */ + 0, /* nb_subtract */ + 0, /* nb_multiply */ + 0, /* nb_divide */ + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + 0, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ + (inquiry)time_nonzero, /* nb_nonzero */ +}; + +statichere PyTypeObject PyDateTime_TimeType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "datetime.time", /* tp_name */ + sizeof(PyDateTime_Time), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)time_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)time_repr, /* tp_repr */ + &time_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)time_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)time_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + time_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)time_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + time_methods, /* tp_methods */ + 0, /* tp_members */ + time_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + time_alloc, /* tp_alloc */ + time_new, /* tp_new */ + 0, /* tp_free */ +}; + +/* + * PyDateTime_DateTime implementation. + */ + +/* Accessor properties. Properties for day, month, and year are inherited + * from date. + */ + +static PyObject * +datetime_hour(PyDateTime_DateTime *self, void *unused) +{ + return PyInt_FromLong(DATE_GET_HOUR(self)); +} + +static PyObject * +datetime_minute(PyDateTime_DateTime *self, void *unused) +{ + return PyInt_FromLong(DATE_GET_MINUTE(self)); +} + +static PyObject * +datetime_second(PyDateTime_DateTime *self, void *unused) +{ + return PyInt_FromLong(DATE_GET_SECOND(self)); +} + +static PyObject * +datetime_microsecond(PyDateTime_DateTime *self, void *unused) +{ + return PyInt_FromLong(DATE_GET_MICROSECOND(self)); +} + +static PyObject * +datetime_tzinfo(PyDateTime_DateTime *self, void *unused) +{ + PyObject *result = HASTZINFO(self) ? self->tzinfo : Py_None; + Py_INCREF(result); + return result; +} + +static PyGetSetDef datetime_getset[] = { + {"hour", (getter)datetime_hour}, + {"minute", (getter)datetime_minute}, + {"second", (getter)datetime_second}, + {"microsecond", (getter)datetime_microsecond}, + {"tzinfo", (getter)datetime_tzinfo}, + {NULL} +}; + +/* + * Constructors. + */ + +static char *datetime_kws[] = { + "year", "month", "day", "hour", "minute", "second", + "microsecond", "tzinfo", NULL +}; + +static PyObject * +datetime_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + PyObject *self = NULL; + PyObject *state; + int year; + int month; + int day; + int hour = 0; + int minute = 0; + int second = 0; + int usecond = 0; + PyObject *tzinfo = Py_None; + + /* Check for invocation from pickle with __getstate__ state */ + if (PyTuple_GET_SIZE(args) >= 1 && + PyTuple_GET_SIZE(args) <= 2 && + PyString_Check(state = PyTuple_GET_ITEM(args, 0)) && + PyString_GET_SIZE(state) == _PyDateTime_DATETIME_DATASIZE) + { + PyDateTime_DateTime *me; + char aware; + + if (PyTuple_GET_SIZE(args) == 2) { + tzinfo = PyTuple_GET_ITEM(args, 1); + if (check_tzinfo_subclass(tzinfo) < 0) { + PyErr_SetString(PyExc_TypeError, "bad " + "tzinfo state arg"); + return NULL; + } + } + aware = (char)(tzinfo != Py_None); + me = (PyDateTime_DateTime *) datetime_alloc( + &PyDateTime_DateTimeType, + aware); + if (me != NULL) { + char *pdata = PyString_AS_STRING(state); + + memcpy(me->data, pdata, _PyDateTime_DATETIME_DATASIZE); + me->hashcode = -1; + me->hastzinfo = aware; + if (aware) { + Py_INCREF(tzinfo); + me->tzinfo = tzinfo; + } + } + return (PyObject *)me; + } + + if (PyArg_ParseTupleAndKeywords(args, kw, "iii|iiiiO", datetime_kws, + &year, &month, &day, &hour, &minute, + &second, &usecond, &tzinfo)) { + if (check_date_args(year, month, day) < 0) + return NULL; + if (check_time_args(hour, minute, second, usecond) < 0) + return NULL; + if (check_tzinfo_subclass(tzinfo) < 0) + return NULL; + self = new_datetime_ex(year, month, day, + hour, minute, second, usecond, + tzinfo, type); + } + return self; +} + +/* TM_FUNC is the shared type of localtime() and gmtime(). */ +typedef struct tm *(*TM_FUNC)(const time_t *timer); + +/* Internal helper. + * Build datetime from a time_t and a distinct count of microseconds. + * Pass localtime or gmtime for f, to control the interpretation of timet. + */ +static PyObject * +datetime_from_timet_and_us(PyObject *cls, TM_FUNC f, time_t timet, int us, + PyObject *tzinfo) +{ + struct tm *tm; + PyObject *result = NULL; + + tm = f(&timet); + if (tm) { + /* The platform localtime/gmtime may insert leap seconds, + * indicated by tm->tm_sec > 59. We don't care about them, + * except to the extent that passing them on to the datetime + * constructor would raise ValueError for a reason that + * made no sense to the user. + */ + if (tm->tm_sec > 59) + tm->tm_sec = 59; + result = PyObject_CallFunction(cls, "iiiiiiiO", + tm->tm_year + 1900, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec, + us, + tzinfo); + } + else + PyErr_SetString(PyExc_ValueError, + "timestamp out of range for " + "platform localtime()/gmtime() function"); + return result; +} + +/* Internal helper. + * Build datetime from a Python timestamp. Pass localtime or gmtime for f, + * to control the interpretation of the timestamp. Since a double doesn't + * have enough bits to cover a datetime's full range of precision, it's + * better to call datetime_from_timet_and_us provided you have a way + * to get that much precision (e.g., C time() isn't good enough). + */ +static PyObject * +datetime_from_timestamp(PyObject *cls, TM_FUNC f, double timestamp, + PyObject *tzinfo) +{ + time_t timet = (time_t)timestamp; + double fraction = timestamp - (double)timet; + int us = (int)round_to_long(fraction * 1e6); + + return datetime_from_timet_and_us(cls, f, timet, us, tzinfo); +} + +/* Internal helper. + * Build most accurate possible datetime for current time. Pass localtime or + * gmtime for f as appropriate. + */ +static PyObject * +datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo) +{ +#ifdef HAVE_GETTIMEOFDAY + struct timeval t; + +#ifdef GETTIMEOFDAY_NO_TZ + gettimeofday(&t); +#else + gettimeofday(&t, (struct timezone *)NULL); +#endif + return datetime_from_timet_and_us(cls, f, t.tv_sec, (int)t.tv_usec, + tzinfo); + +#else /* ! HAVE_GETTIMEOFDAY */ + /* No flavor of gettimeofday exists on this platform. Python's + * time.time() does a lot of other platform tricks to get the + * best time it can on the platform, and we're not going to do + * better than that (if we could, the better code would belong + * in time.time()!) We're limited by the precision of a double, + * though. + */ + PyObject *time; + double dtime; + + time = time_time(); + if (time == NULL) + return NULL; + dtime = PyFloat_AsDouble(time); + Py_DECREF(time); + if (dtime == -1.0 && PyErr_Occurred()) + return NULL; + return datetime_from_timestamp(cls, f, dtime, tzinfo); +#endif /* ! HAVE_GETTIMEOFDAY */ +} + +/* Return best possible local time -- this isn't constrained by the + * precision of a timestamp. + */ +static PyObject * +datetime_now(PyObject *cls, PyObject *args, PyObject *kw) +{ + PyObject *self; + PyObject *tzinfo = Py_None; + static char *keywords[] = {"tz", NULL}; + + if (! PyArg_ParseTupleAndKeywords(args, kw, "|O:now", keywords, + &tzinfo)) + return NULL; + if (check_tzinfo_subclass(tzinfo) < 0) + return NULL; + + self = datetime_best_possible(cls, + tzinfo == Py_None ? localtime : gmtime, + tzinfo); + if (self != NULL && tzinfo != Py_None) { + /* Convert UTC to tzinfo's zone. */ + PyObject *temp = self; + self = PyObject_CallMethod(tzinfo, "fromutc", "O", self); + Py_DECREF(temp); + } + return self; +} + +/* Return best possible UTC time -- this isn't constrained by the + * precision of a timestamp. + */ +static PyObject * +datetime_utcnow(PyObject *cls, PyObject *dummy) +{ + return datetime_best_possible(cls, gmtime, Py_None); +} + +/* Return new local datetime from timestamp (Python timestamp -- a double). */ +static PyObject * +datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw) +{ + PyObject *self; + double timestamp; + PyObject *tzinfo = Py_None; + static char *keywords[] = {"timestamp", "tz", NULL}; + + if (! PyArg_ParseTupleAndKeywords(args, kw, "d|O:fromtimestamp", + keywords, ×tamp, &tzinfo)) + return NULL; + if (check_tzinfo_subclass(tzinfo) < 0) + return NULL; + + self = datetime_from_timestamp(cls, + tzinfo == Py_None ? localtime : gmtime, + timestamp, + tzinfo); + if (self != NULL && tzinfo != Py_None) { + /* Convert UTC to tzinfo's zone. */ + PyObject *temp = self; + self = PyObject_CallMethod(tzinfo, "fromutc", "O", self); + Py_DECREF(temp); + } + return self; +} + +/* Return new UTC datetime from timestamp (Python timestamp -- a double). */ +static PyObject * +datetime_utcfromtimestamp(PyObject *cls, PyObject *args) +{ + double timestamp; + PyObject *result = NULL; + + if (PyArg_ParseTuple(args, "d:utcfromtimestamp", ×tamp)) + result = datetime_from_timestamp(cls, gmtime, timestamp, + Py_None); + return result; +} + +/* Return new datetime from date/datetime and time arguments. */ +static PyObject * +datetime_combine(PyObject *cls, PyObject *args, PyObject *kw) +{ + static char *keywords[] = {"date", "time", NULL}; + PyObject *date; + PyObject *time; + PyObject *result = NULL; + + if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!:combine", keywords, + &PyDateTime_DateType, &date, + &PyDateTime_TimeType, &time)) { + PyObject *tzinfo = Py_None; + + if (HASTZINFO(time)) + tzinfo = ((PyDateTime_Time *)time)->tzinfo; + result = PyObject_CallFunction(cls, "iiiiiiiO", + GET_YEAR(date), + GET_MONTH(date), + GET_DAY(date), + TIME_GET_HOUR(time), + TIME_GET_MINUTE(time), + TIME_GET_SECOND(time), + TIME_GET_MICROSECOND(time), + tzinfo); + } + return result; +} + +/* + * Destructor. + */ + +static void +datetime_dealloc(PyDateTime_DateTime *self) +{ + if (HASTZINFO(self)) { + Py_XDECREF(self->tzinfo); + } + self->ob_type->tp_free((PyObject *)self); +} + +/* + * Indirect access to tzinfo methods. + */ + +/* These are all METH_NOARGS, so don't need to check the arglist. */ +static PyObject * +datetime_utcoffset(PyDateTime_DateTime *self, PyObject *unused) { + return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None, + "utcoffset", (PyObject *)self); +} + +static PyObject * +datetime_dst(PyDateTime_DateTime *self, PyObject *unused) { + return offset_as_timedelta(HASTZINFO(self) ? self->tzinfo : Py_None, + "dst", (PyObject *)self); +} + +static PyObject * +datetime_tzname(PyDateTime_DateTime *self, PyObject *unused) { + return call_tzname(HASTZINFO(self) ? self->tzinfo : Py_None, + (PyObject *)self); +} + +/* + * datetime arithmetic. + */ + +/* factor must be 1 (to add) or -1 (to subtract). The result inherits + * the tzinfo state of date. + */ +static PyObject * +add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta, + int factor) +{ + /* Note that the C-level additions can't overflow, because of + * invariant bounds on the member values. + */ + int year = GET_YEAR(date); + int month = GET_MONTH(date); + int day = GET_DAY(date) + GET_TD_DAYS(delta) * factor; + int hour = DATE_GET_HOUR(date); + int minute = DATE_GET_MINUTE(date); + int second = DATE_GET_SECOND(date) + GET_TD_SECONDS(delta) * factor; + int microsecond = DATE_GET_MICROSECOND(date) + + GET_TD_MICROSECONDS(delta) * factor; + + assert(factor == 1 || factor == -1); + if (normalize_datetime(&year, &month, &day, + &hour, &minute, &second, µsecond) < 0) + return NULL; + else + return new_datetime(year, month, day, + hour, minute, second, microsecond, + HASTZINFO(date) ? date->tzinfo : Py_None); +} + +static PyObject * +datetime_add(PyObject *left, PyObject *right) +{ + if (PyDateTime_Check(left)) { + /* datetime + ??? */ + if (PyDelta_Check(right)) + /* datetime + delta */ + return add_datetime_timedelta( + (PyDateTime_DateTime *)left, + (PyDateTime_Delta *)right, + 1); + } + else if (PyDelta_Check(left)) { + /* delta + datetime */ + return add_datetime_timedelta((PyDateTime_DateTime *) right, + (PyDateTime_Delta *) left, + 1); + } + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static PyObject * +datetime_subtract(PyObject *left, PyObject *right) +{ + PyObject *result = Py_NotImplemented; + + if (PyDateTime_Check(left)) { + /* datetime - ??? */ + if (PyDateTime_Check(right)) { + /* datetime - datetime */ + naivety n1, n2; + int offset1, offset2; + int delta_d, delta_s, delta_us; + + if (classify_two_utcoffsets(left, &offset1, &n1, left, + right, &offset2, &n2, + right) < 0) + return NULL; + assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN); + if (n1 != n2) { + PyErr_SetString(PyExc_TypeError, + "can't subtract offset-naive and " + "offset-aware datetimes"); + return NULL; + } + delta_d = ymd_to_ord(GET_YEAR(left), + GET_MONTH(left), + GET_DAY(left)) - + ymd_to_ord(GET_YEAR(right), + GET_MONTH(right), + GET_DAY(right)); + /* These can't overflow, since the values are + * normalized. At most this gives the number of + * seconds in one day. + */ + delta_s = (DATE_GET_HOUR(left) - + DATE_GET_HOUR(right)) * 3600 + + (DATE_GET_MINUTE(left) - + DATE_GET_MINUTE(right)) * 60 + + (DATE_GET_SECOND(left) - + DATE_GET_SECOND(right)); + delta_us = DATE_GET_MICROSECOND(left) - + DATE_GET_MICROSECOND(right); + /* (left - offset1) - (right - offset2) = + * (left - right) + (offset2 - offset1) + */ + delta_s += (offset2 - offset1) * 60; + result = new_delta(delta_d, delta_s, delta_us, 1); + } + else if (PyDelta_Check(right)) { + /* datetime - delta */ + result = add_datetime_timedelta( + (PyDateTime_DateTime *)left, + (PyDateTime_Delta *)right, + -1); + } + } + + if (result == Py_NotImplemented) + Py_INCREF(result); + return result; +} + +/* Various ways to turn a datetime into a string. */ + +static PyObject * +datetime_repr(PyDateTime_DateTime *self) +{ + char buffer[1000]; + char *typename = self->ob_type->tp_name; + PyObject *baserepr; + + if (DATE_GET_MICROSECOND(self)) { + PyOS_snprintf(buffer, sizeof(buffer), + "%s(%d, %d, %d, %d, %d, %d, %d)", + typename, + GET_YEAR(self), GET_MONTH(self), GET_DAY(self), + DATE_GET_HOUR(self), DATE_GET_MINUTE(self), + DATE_GET_SECOND(self), + DATE_GET_MICROSECOND(self)); + } + else if (DATE_GET_SECOND(self)) { + PyOS_snprintf(buffer, sizeof(buffer), + "%s(%d, %d, %d, %d, %d, %d)", + typename, + GET_YEAR(self), GET_MONTH(self), GET_DAY(self), + DATE_GET_HOUR(self), DATE_GET_MINUTE(self), + DATE_GET_SECOND(self)); + } + else { + PyOS_snprintf(buffer, sizeof(buffer), + "%s(%d, %d, %d, %d, %d)", + typename, + GET_YEAR(self), GET_MONTH(self), GET_DAY(self), + DATE_GET_HOUR(self), DATE_GET_MINUTE(self)); + } + baserepr = PyString_FromString(buffer); + if (baserepr == NULL || ! HASTZINFO(self)) + return baserepr; + return append_keyword_tzinfo(baserepr, self->tzinfo); +} + +static PyObject * +datetime_str(PyDateTime_DateTime *self) +{ + return PyObject_CallMethod((PyObject *)self, "isoformat", "(s)", " "); +} + +static PyObject * +datetime_isoformat(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) +{ + char sep = 'T'; + static char *keywords[] = {"sep", NULL}; + char buffer[100]; + char *cp; + PyObject *result; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "|c:isoformat", keywords, + &sep)) + return NULL; + cp = isoformat_date((PyDateTime_Date *)self, buffer, sizeof(buffer)); + assert(cp != NULL); + *cp++ = sep; + isoformat_time(self, cp, sizeof(buffer) - (cp - buffer)); + result = PyString_FromString(buffer); + if (result == NULL || ! HASTZINFO(self)) + return result; + + /* We need to append the UTC offset. */ + if (format_utcoffset(buffer, sizeof(buffer), ":", self->tzinfo, + (PyObject *)self) < 0) { + Py_DECREF(result); + return NULL; + } + PyString_ConcatAndDel(&result, PyString_FromString(buffer)); + return result; +} + +static PyObject * +datetime_ctime(PyDateTime_DateTime *self) +{ + return format_ctime((PyDateTime_Date *)self, + DATE_GET_HOUR(self), + DATE_GET_MINUTE(self), + DATE_GET_SECOND(self)); +} + +/* Miscellaneous methods. */ + +/* This is more natural as a tp_compare, but doesn't work then: for whatever + * reason, Python's try_3way_compare ignores tp_compare unless + * PyInstance_Check returns true, but these aren't old-style classes. + */ +static PyObject * +datetime_richcompare(PyDateTime_DateTime *self, PyObject *other, int op) +{ + int diff; + naivety n1, n2; + int offset1, offset2; + + if (! PyDateTime_Check(other)) { + if (PyObject_HasAttrString(other, "timetuple")) { + /* A hook for other kinds of datetime objects. */ + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + if (op == Py_EQ || op == Py_NE) { + PyObject *result = op == Py_EQ ? Py_False : Py_True; + Py_INCREF(result); + return result; + } + /* Stop this from falling back to address comparison. */ + return cmperror((PyObject *)self, other); + } + + if (classify_two_utcoffsets((PyObject *)self, &offset1, &n1, + (PyObject *)self, + other, &offset2, &n2, + other) < 0) + return NULL; + assert(n1 != OFFSET_UNKNOWN && n2 != OFFSET_UNKNOWN); + /* If they're both naive, or both aware and have the same offsets, + * we get off cheap. Note that if they're both naive, offset1 == + * offset2 == 0 at this point. + */ + if (n1 == n2 && offset1 == offset2) { + diff = memcmp(self->data, ((PyDateTime_DateTime *)other)->data, + _PyDateTime_DATETIME_DATASIZE); + return diff_to_bool(diff, op); + } + + if (n1 == OFFSET_AWARE && n2 == OFFSET_AWARE) { + PyDateTime_Delta *delta; + + assert(offset1 != offset2); /* else last "if" handled it */ + delta = (PyDateTime_Delta *)datetime_subtract((PyObject *)self, + other); + if (delta == NULL) + return NULL; + diff = GET_TD_DAYS(delta); + if (diff == 0) + diff = GET_TD_SECONDS(delta) | + GET_TD_MICROSECONDS(delta); + Py_DECREF(delta); + return diff_to_bool(diff, op); + } + + assert(n1 != n2); + PyErr_SetString(PyExc_TypeError, + "can't compare offset-naive and " + "offset-aware datetimes"); + return NULL; +} + +static long +datetime_hash(PyDateTime_DateTime *self) +{ + if (self->hashcode == -1) { + naivety n; + int offset; + PyObject *temp; + + n = classify_utcoffset((PyObject *)self, (PyObject *)self, + &offset); + assert(n != OFFSET_UNKNOWN); + if (n == OFFSET_ERROR) + return -1; + + /* Reduce this to a hash of another object. */ + if (n == OFFSET_NAIVE) + temp = PyString_FromStringAndSize( + (char *)self->data, + _PyDateTime_DATETIME_DATASIZE); + else { + int days; + int seconds; + + assert(n == OFFSET_AWARE); + assert(HASTZINFO(self)); + days = ymd_to_ord(GET_YEAR(self), + GET_MONTH(self), + GET_DAY(self)); + seconds = DATE_GET_HOUR(self) * 3600 + + (DATE_GET_MINUTE(self) - offset) * 60 + + DATE_GET_SECOND(self); + temp = new_delta(days, + seconds, + DATE_GET_MICROSECOND(self), + 1); + } + if (temp != NULL) { + self->hashcode = PyObject_Hash(temp); + Py_DECREF(temp); + } + } + return self->hashcode; +} + +static PyObject * +datetime_replace(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) +{ + PyObject *clone; + PyObject *tuple; + int y = GET_YEAR(self); + int m = GET_MONTH(self); + int d = GET_DAY(self); + int hh = DATE_GET_HOUR(self); + int mm = DATE_GET_MINUTE(self); + int ss = DATE_GET_SECOND(self); + int us = DATE_GET_MICROSECOND(self); + PyObject *tzinfo = HASTZINFO(self) ? self->tzinfo : Py_None; + + if (! PyArg_ParseTupleAndKeywords(args, kw, "|iiiiiiiO:replace", + datetime_kws, + &y, &m, &d, &hh, &mm, &ss, &us, + &tzinfo)) + return NULL; + tuple = Py_BuildValue("iiiiiiiO", y, m, d, hh, mm, ss, us, tzinfo); + if (tuple == NULL) + return NULL; + clone = datetime_new(self->ob_type, tuple, NULL); + Py_DECREF(tuple); + return clone; +} + +static PyObject * +datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) +{ + int y, m, d, hh, mm, ss, us; + PyObject *result; + int offset, none; + + PyObject *tzinfo; + static char *keywords[] = {"tz", NULL}; + + if (! PyArg_ParseTupleAndKeywords(args, kw, "O!:astimezone", keywords, + &PyDateTime_TZInfoType, &tzinfo)) + return NULL; + + if (!HASTZINFO(self) || self->tzinfo == Py_None) + goto NeedAware; + + /* Conversion to self's own time zone is a NOP. */ + if (self->tzinfo == tzinfo) { + Py_INCREF(self); + return (PyObject *)self; + } + + /* Convert self to UTC. */ + offset = call_utcoffset(self->tzinfo, (PyObject *)self, &none); + if (offset == -1 && PyErr_Occurred()) + return NULL; + if (none) + goto NeedAware; + + y = GET_YEAR(self); + m = GET_MONTH(self); + d = GET_DAY(self); + hh = DATE_GET_HOUR(self); + mm = DATE_GET_MINUTE(self); + ss = DATE_GET_SECOND(self); + us = DATE_GET_MICROSECOND(self); + + mm -= offset; + if ((mm < 0 || mm >= 60) && + normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us) < 0) + return NULL; + + /* Attach new tzinfo and let fromutc() do the rest. */ + result = new_datetime(y, m, d, hh, mm, ss, us, tzinfo); + if (result != NULL) { + PyObject *temp = result; + + result = PyObject_CallMethod(tzinfo, "fromutc", "O", temp); + Py_DECREF(temp); + } + return result; + +NeedAware: + PyErr_SetString(PyExc_ValueError, "astimezone() cannot be applied to " + "a naive datetime"); + return NULL; +} + +static PyObject * +datetime_timetuple(PyDateTime_DateTime *self) +{ + int dstflag = -1; + + if (HASTZINFO(self) && self->tzinfo != Py_None) { + int none; + + dstflag = call_dst(self->tzinfo, (PyObject *)self, &none); + if (dstflag == -1 && PyErr_Occurred()) + return NULL; + + if (none) + dstflag = -1; + else if (dstflag != 0) + dstflag = 1; + + } + return build_struct_time(GET_YEAR(self), + GET_MONTH(self), + GET_DAY(self), + DATE_GET_HOUR(self), + DATE_GET_MINUTE(self), + DATE_GET_SECOND(self), + dstflag); +} + +static PyObject * +datetime_getdate(PyDateTime_DateTime *self) +{ + return new_date(GET_YEAR(self), + GET_MONTH(self), + GET_DAY(self)); +} + +static PyObject * +datetime_gettime(PyDateTime_DateTime *self) +{ + return new_time(DATE_GET_HOUR(self), + DATE_GET_MINUTE(self), + DATE_GET_SECOND(self), + DATE_GET_MICROSECOND(self), + Py_None); +} + +static PyObject * +datetime_gettimetz(PyDateTime_DateTime *self) +{ + return new_time(DATE_GET_HOUR(self), + DATE_GET_MINUTE(self), + DATE_GET_SECOND(self), + DATE_GET_MICROSECOND(self), + HASTZINFO(self) ? self->tzinfo : Py_None); +} + +static PyObject * +datetime_utctimetuple(PyDateTime_DateTime *self) +{ + int y = GET_YEAR(self); + int m = GET_MONTH(self); + int d = GET_DAY(self); + int hh = DATE_GET_HOUR(self); + int mm = DATE_GET_MINUTE(self); + int ss = DATE_GET_SECOND(self); + int us = 0; /* microseconds are ignored in a timetuple */ + int offset = 0; + + if (HASTZINFO(self) && self->tzinfo != Py_None) { + int none; + + offset = call_utcoffset(self->tzinfo, (PyObject *)self, &none); + if (offset == -1 && PyErr_Occurred()) + return NULL; + } + /* Even if offset is 0, don't call timetuple() -- tm_isdst should be + * 0 in a UTC timetuple regardless of what dst() says. + */ + if (offset) { + /* Subtract offset minutes & normalize. */ + int stat; + + mm -= offset; + stat = normalize_datetime(&y, &m, &d, &hh, &mm, &ss, &us); + if (stat < 0) { + /* At the edges, it's possible we overflowed + * beyond MINYEAR or MAXYEAR. + */ + if (PyErr_ExceptionMatches(PyExc_OverflowError)) + PyErr_Clear(); + else + return NULL; + } + } + return build_struct_time(y, m, d, hh, mm, ss, 0); +} + +/* Pickle support, a simple use of __reduce__. */ + +/* Let basestate be the non-tzinfo data string. + * If tzinfo is None, this returns (basestate,), else (basestate, tzinfo). + * So it's a tuple in any (non-error) case. + * __getstate__ isn't exposed. + */ +static PyObject * +datetime_getstate(PyDateTime_DateTime *self) +{ + PyObject *basestate; + PyObject *result = NULL; + + basestate = PyString_FromStringAndSize((char *)self->data, + _PyDateTime_DATETIME_DATASIZE); + if (basestate != NULL) { + if (! HASTZINFO(self) || self->tzinfo == Py_None) + result = Py_BuildValue("(O)", basestate); + else + result = Py_BuildValue("OO", basestate, self->tzinfo); + Py_DECREF(basestate); + } + return result; +} + +static PyObject * +datetime_reduce(PyDateTime_DateTime *self, PyObject *arg) +{ + return Py_BuildValue("(ON)", self->ob_type, datetime_getstate(self)); +} + +static PyMethodDef datetime_methods[] = { + + /* Class methods: */ + + {"now", (PyCFunction)datetime_now, + METH_KEYWORDS | METH_CLASS, + PyDoc_STR("[tz] -> new datetime with tz's local day and time.")}, + + {"utcnow", (PyCFunction)datetime_utcnow, + METH_NOARGS | METH_CLASS, + PyDoc_STR("Return a new datetime representing UTC day and time.")}, + + {"fromtimestamp", (PyCFunction)datetime_fromtimestamp, + METH_KEYWORDS | METH_CLASS, + PyDoc_STR("timestamp[, tz] -> tz's local time from POSIX timestamp.")}, + + {"utcfromtimestamp", (PyCFunction)datetime_utcfromtimestamp, + METH_VARARGS | METH_CLASS, + PyDoc_STR("timestamp -> UTC datetime from a POSIX timestamp " + "(like time.time()).")}, + + {"combine", (PyCFunction)datetime_combine, + METH_VARARGS | METH_KEYWORDS | METH_CLASS, + PyDoc_STR("date, time -> datetime with same date and time fields")}, + + /* Instance methods: */ + + {"date", (PyCFunction)datetime_getdate, METH_NOARGS, + PyDoc_STR("Return date object with same year, month and day.")}, + + {"time", (PyCFunction)datetime_gettime, METH_NOARGS, + PyDoc_STR("Return time object with same time but with tzinfo=None.")}, + + {"timetz", (PyCFunction)datetime_gettimetz, METH_NOARGS, + PyDoc_STR("Return time object with same time and tzinfo.")}, + + {"ctime", (PyCFunction)datetime_ctime, METH_NOARGS, + PyDoc_STR("Return ctime() style string.")}, + + {"timetuple", (PyCFunction)datetime_timetuple, METH_NOARGS, + PyDoc_STR("Return time tuple, compatible with time.localtime().")}, + + {"utctimetuple", (PyCFunction)datetime_utctimetuple, METH_NOARGS, + PyDoc_STR("Return UTC time tuple, compatible with time.localtime().")}, + + {"isoformat", (PyCFunction)datetime_isoformat, METH_KEYWORDS, + PyDoc_STR("[sep] -> string in ISO 8601 format, " + "YYYY-MM-DDTHH:MM:SS[.mmmmmm][+HH:MM].\n\n" + "sep is used to separate the year from the time, and " + "defaults to 'T'.")}, + + {"utcoffset", (PyCFunction)datetime_utcoffset, METH_NOARGS, + PyDoc_STR("Return self.tzinfo.utcoffset(self).")}, + + {"tzname", (PyCFunction)datetime_tzname, METH_NOARGS, + PyDoc_STR("Return self.tzinfo.tzname(self).")}, + + {"dst", (PyCFunction)datetime_dst, METH_NOARGS, + PyDoc_STR("Return self.tzinfo.dst(self).")}, + + {"replace", (PyCFunction)datetime_replace, METH_KEYWORDS, + PyDoc_STR("Return datetime with new specified fields.")}, + + {"astimezone", (PyCFunction)datetime_astimezone, METH_KEYWORDS, + PyDoc_STR("tz -> convert to local time in new timezone tz\n")}, + + {"__reduce__", (PyCFunction)datetime_reduce, METH_NOARGS, + PyDoc_STR("__reduce__() -> (cls, state)")}, + + {NULL, NULL} +}; + +static char datetime_doc[] = +PyDoc_STR("date/time type."); + +static PyNumberMethods datetime_as_number = { + datetime_add, /* nb_add */ + datetime_subtract, /* nb_subtract */ + 0, /* nb_multiply */ + 0, /* nb_divide */ + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + 0, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ + 0, /* nb_nonzero */ +}; + +statichere PyTypeObject PyDateTime_DateTimeType = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "datetime.datetime", /* tp_name */ + sizeof(PyDateTime_DateTime), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)datetime_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)datetime_repr, /* tp_repr */ + &datetime_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)datetime_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)datetime_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + datetime_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)datetime_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + datetime_methods, /* tp_methods */ + 0, /* tp_members */ + datetime_getset, /* tp_getset */ + &PyDateTime_DateType, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + datetime_alloc, /* tp_alloc */ + datetime_new, /* tp_new */ + 0, /* tp_free */ +}; + +/* --------------------------------------------------------------------------- + * Module methods and initialization. + */ + +static PyMethodDef module_methods[] = { + {NULL, NULL} +}; + +PyMODINIT_FUNC +initdatetime(void) +{ + PyObject *m; /* a module object */ + PyObject *d; /* its dict */ + PyObject *x; + + m = Py_InitModule3("datetime", module_methods, + "Fast implementation of the datetime type."); + + if (PyType_Ready(&PyDateTime_DateType) < 0) + return; + if (PyType_Ready(&PyDateTime_DateTimeType) < 0) + return; + if (PyType_Ready(&PyDateTime_DeltaType) < 0) + return; + if (PyType_Ready(&PyDateTime_TimeType) < 0) + return; + if (PyType_Ready(&PyDateTime_TZInfoType) < 0) + return; + + /* timedelta values */ + d = PyDateTime_DeltaType.tp_dict; + + x = new_delta(0, 0, 1, 0); + if (x == NULL || PyDict_SetItemString(d, "resolution", x) < 0) + return; + Py_DECREF(x); + + x = new_delta(-MAX_DELTA_DAYS, 0, 0, 0); + if (x == NULL || PyDict_SetItemString(d, "min", x) < 0) + return; + Py_DECREF(x); + + x = new_delta(MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0); + if (x == NULL || PyDict_SetItemString(d, "max", x) < 0) + return; + Py_DECREF(x); + + /* date values */ + d = PyDateTime_DateType.tp_dict; + + x = new_date(1, 1, 1); + if (x == NULL || PyDict_SetItemString(d, "min", x) < 0) + return; + Py_DECREF(x); + + x = new_date(MAXYEAR, 12, 31); + if (x == NULL || PyDict_SetItemString(d, "max", x) < 0) + return; + Py_DECREF(x); + + x = new_delta(1, 0, 0, 0); + if (x == NULL || PyDict_SetItemString(d, "resolution", x) < 0) + return; + Py_DECREF(x); + + /* time values */ + d = PyDateTime_TimeType.tp_dict; + + x = new_time(0, 0, 0, 0, Py_None); + if (x == NULL || PyDict_SetItemString(d, "min", x) < 0) + return; + Py_DECREF(x); + + x = new_time(23, 59, 59, 999999, Py_None); + if (x == NULL || PyDict_SetItemString(d, "max", x) < 0) + return; + Py_DECREF(x); + + x = new_delta(0, 0, 1, 0); + if (x == NULL || PyDict_SetItemString(d, "resolution", x) < 0) + return; + Py_DECREF(x); + + /* datetime values */ + d = PyDateTime_DateTimeType.tp_dict; + + x = new_datetime(1, 1, 1, 0, 0, 0, 0, Py_None); + if (x == NULL || PyDict_SetItemString(d, "min", x) < 0) + return; + Py_DECREF(x); + + x = new_datetime(MAXYEAR, 12, 31, 23, 59, 59, 999999, Py_None); + if (x == NULL || PyDict_SetItemString(d, "max", x) < 0) + return; + Py_DECREF(x); + + x = new_delta(0, 0, 1, 0); + if (x == NULL || PyDict_SetItemString(d, "resolution", x) < 0) + return; + Py_DECREF(x); + + /* module initialization */ + PyModule_AddIntConstant(m, "MINYEAR", MINYEAR); + PyModule_AddIntConstant(m, "MAXYEAR", MAXYEAR); + + Py_INCREF(&PyDateTime_DateType); + PyModule_AddObject(m, "date", (PyObject *) &PyDateTime_DateType); + + Py_INCREF(&PyDateTime_DateTimeType); + PyModule_AddObject(m, "datetime", + (PyObject *)&PyDateTime_DateTimeType); + + Py_INCREF(&PyDateTime_TimeType); + PyModule_AddObject(m, "time", (PyObject *) &PyDateTime_TimeType); + + Py_INCREF(&PyDateTime_DeltaType); + PyModule_AddObject(m, "timedelta", (PyObject *) &PyDateTime_DeltaType); + + Py_INCREF(&PyDateTime_TZInfoType); + PyModule_AddObject(m, "tzinfo", (PyObject *) &PyDateTime_TZInfoType); + + /* A 4-year cycle has an extra leap day over what we'd get from + * pasting together 4 single years. + */ + assert(DI4Y == 4 * 365 + 1); + assert(DI4Y == days_before_year(4+1)); + + /* Similarly, a 400-year cycle has an extra leap day over what we'd + * get from pasting together 4 100-year cycles. + */ + assert(DI400Y == 4 * DI100Y + 1); + assert(DI400Y == days_before_year(400+1)); + + /* OTOH, a 100-year cycle has one fewer leap day than we'd get from + * pasting together 25 4-year cycles. + */ + assert(DI100Y == 25 * DI4Y - 1); + assert(DI100Y == days_before_year(100+1)); + + us_per_us = PyInt_FromLong(1); + us_per_ms = PyInt_FromLong(1000); + us_per_second = PyInt_FromLong(1000000); + us_per_minute = PyInt_FromLong(60000000); + seconds_per_day = PyInt_FromLong(24 * 3600); + if (us_per_us == NULL || us_per_ms == NULL || us_per_second == NULL || + us_per_minute == NULL || seconds_per_day == NULL) + return; + + /* The rest are too big for 32-bit ints, but even + * us_per_week fits in 40 bits, so doubles should be exact. + */ + us_per_hour = PyLong_FromDouble(3600000000.0); + us_per_day = PyLong_FromDouble(86400000000.0); + us_per_week = PyLong_FromDouble(604800000000.0); + if (us_per_hour == NULL || us_per_day == NULL || us_per_week == NULL) + return; +} + +/* --------------------------------------------------------------------------- +Some time zone algebra. For a datetime x, let + x.n = x stripped of its timezone -- its naive time. + x.o = x.utcoffset(), and assuming that doesn't raise an exception or + return None + x.d = x.dst(), and assuming that doesn't raise an exception or + return None + x.s = x's standard offset, x.o - x.d + +Now some derived rules, where k is a duration (timedelta). + +1. x.o = x.s + x.d + This follows from the definition of x.s. + +2. If x and y have the same tzinfo member, x.s = y.s. + This is actually a requirement, an assumption we need to make about + sane tzinfo classes. + +3. The naive UTC time corresponding to x is x.n - x.o. + This is again a requirement for a sane tzinfo class. + +4. (x+k).s = x.s + This follows from #2, and that datimetimetz+timedelta preserves tzinfo. + +5. (x+k).n = x.n + k + Again follows from how arithmetic is defined. + +Now we can explain tz.fromutc(x). Let's assume it's an interesting case +(meaning that the various tzinfo methods exist, and don't blow up or return +None when called). + +The function wants to return a datetime y with timezone tz, equivalent to x. +x is already in UTC. + +By #3, we want + + y.n - y.o = x.n [1] + +The algorithm starts by attaching tz to x.n, and calling that y. So +x.n = y.n at the start. Then it wants to add a duration k to y, so that [1] +becomes true; in effect, we want to solve [2] for k: + + (y+k).n - (y+k).o = x.n [2] + +By #1, this is the same as + + (y+k).n - ((y+k).s + (y+k).d) = x.n [3] + +By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start. +Substituting that into [3], + + x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving + k - (y+k).s - (y+k).d = 0; rearranging, + k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so + k = y.s - (y+k).d + +On the RHS, (y+k).d can't be computed directly, but y.s can be, and we +approximate k by ignoring the (y+k).d term at first. Note that k can't be +very large, since all offset-returning methods return a duration of magnitude +less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must +be 0, so ignoring it has no consequence then. + +In any case, the new value is + + z = y + y.s [4] + +It's helpful to step back at look at [4] from a higher level: it's simply +mapping from UTC to tz's standard time. + +At this point, if + + z.n - z.o = x.n [5] + +we have an equivalent time, and are almost done. The insecurity here is +at the start of daylight time. Picture US Eastern for concreteness. The wall +time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good +sense then. The docs ask that an Eastern tzinfo class consider such a time to +be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST +on the day DST starts. We want to return the 1:MM EST spelling because that's +the only spelling that makes sense on the local wall clock. + +In fact, if [5] holds at this point, we do have the standard-time spelling, +but that takes a bit of proof. We first prove a stronger result. What's the +difference between the LHS and RHS of [5]? Let + + diff = x.n - (z.n - z.o) [6] + +Now + z.n = by [4] + (y + y.s).n = by #5 + y.n + y.s = since y.n = x.n + x.n + y.s = since z and y are have the same tzinfo member, + y.s = z.s by #2 + x.n + z.s + +Plugging that back into [6] gives + + diff = + x.n - ((x.n + z.s) - z.o) = expanding + x.n - x.n - z.s + z.o = cancelling + - z.s + z.o = by #2 + z.d + +So diff = z.d. + +If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time +spelling we wanted in the endcase described above. We're done. Contrarily, +if z.d = 0, then we have a UTC equivalent, and are also done. + +If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to +add to z (in effect, z is in tz's standard time, and we need to shift the +local clock into tz's daylight time). + +Let + + z' = z + z.d = z + diff [7] + +and we can again ask whether + + z'.n - z'.o = x.n [8] + +If so, we're done. If not, the tzinfo class is insane, according to the +assumptions we've made. This also requires a bit of proof. As before, let's +compute the difference between the LHS and RHS of [8] (and skipping some of +the justifications for the kinds of substitutions we've done several times +already): + + diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7] + x.n - (z.n + diff - z'.o) = replacing diff via [6] + x.n - (z.n + x.n - (z.n - z.o) - z'.o) = + x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n + - z.n + z.n - z.o + z'.o = cancel z.n + - z.o + z'.o = #1 twice + -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo + z'.d - z.d + +So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal, +we've found the UTC-equivalent so are done. In fact, we stop with [7] and +return z', not bothering to compute z'.d. + +How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by +a dst() offset, and starting *from* a time already in DST (we know z.d != 0), +would have to change the result dst() returns: we start in DST, and moving +a little further into it takes us out of DST. + +There isn't a sane case where this can happen. The closest it gets is at +the end of DST, where there's an hour in UTC with no spelling in a hybrid +tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During +that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM +UTC) because the docs insist on that, but 0:MM is taken as being in daylight +time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local +clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in +standard time. Since that's what the local clock *does*, we want to map both +UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous +in local time, but so it goes -- it's the way the local clock works. + +When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0, +so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going. +z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8] +(correctly) concludes that z' is not UTC-equivalent to x. + +Because we know z.d said z was in daylight time (else [5] would have held and +we would have stopped then), and we know z.d != z'.d (else [8] would have held +and we would have stopped then), and there are only 2 possible values dst() can +return in Eastern, it follows that z'.d must be 0 (which it is in the example, +but the reasoning doesn't depend on the example -- it depends on there being +two possible dst() outcomes, one zero and the other non-zero). Therefore +z' must be in standard time, and is the spelling we want in this case. + +Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is +concerned (because it takes z' as being in standard time rather than the +daylight time we intend here), but returning it gives the real-life "local +clock repeats an hour" behavior when mapping the "unspellable" UTC hour into +tz. + +When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with +the 1:MM standard time spelling we want. + +So how can this break? One of the assumptions must be violated. Two +possibilities: + +1) [2] effectively says that y.s is invariant across all y belong to a given + time zone. This isn't true if, for political reasons or continental drift, + a region decides to change its base offset from UTC. + +2) There may be versions of "double daylight" time where the tail end of + the analysis gives up a step too early. I haven't thought about that + enough to say. + +In any case, it's clear that the default fromutc() is strong enough to handle +"almost all" time zones: so long as the standard offset is invariant, it +doesn't matter if daylight time transition points change from year to year, or +if daylight time is skipped in some years; it doesn't matter how large or +small dst() may get within its bounds; and it doesn't even matter if some +perverse time zone returns a negative dst()). So a breaking case must be +pretty bizarre, and a tzinfo subclass can override fromutc() if it is. +--------------------------------------------------------------------------- */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/dbmmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/dbmmodule.c new file mode 100644 index 00000000..691a292c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/dbmmodule.c @@ -0,0 +1,372 @@ + +/* DBM module using dictionary interface */ + + +#include "Python.h" + +#include +#include +#include + +/* Some Linux systems install gdbm/ndbm.h, but not ndbm.h. This supports + * whichever configure was able to locate. + */ +#if defined(HAVE_NDBM_H) +#include +#if defined(PYOS_OS2) && !defined(PYCC_GCC) +static char *which_dbm = "ndbm"; +#else +static char *which_dbm = "GNU gdbm"; /* EMX port of GDBM */ +#endif +#elif defined(HAVE_GDBM_NDBM_H) +#include +static char *which_dbm = "GNU gdbm"; +#elif defined(HAVE_BERKDB_H) +#include +static char *which_dbm = "Berkeley DB"; +#else +#error "No ndbm.h available!" +#endif + +typedef struct { + PyObject_HEAD + int di_size; /* -1 means recompute */ + DBM *di_dbm; +} dbmobject; + +static PyTypeObject Dbmtype; + +#define is_dbmobject(v) ((v)->ob_type == &Dbmtype) +#define check_dbmobject_open(v) if ((v)->di_dbm == NULL) \ + { PyErr_SetString(DbmError, "DBM object has already been closed"); \ + return NULL; } + +static PyObject *DbmError; + +static PyObject * +newdbmobject(char *file, int flags, int mode) +{ + dbmobject *dp; + + dp = PyObject_New(dbmobject, &Dbmtype); + if (dp == NULL) + return NULL; + dp->di_size = -1; + if ( (dp->di_dbm = dbm_open(file, flags, mode)) == 0 ) { + PyErr_SetFromErrno(DbmError); + Py_DECREF(dp); + return NULL; + } + return (PyObject *)dp; +} + +/* Methods */ + +static void +dbm_dealloc(register dbmobject *dp) +{ + if ( dp->di_dbm ) + dbm_close(dp->di_dbm); + PyObject_Del(dp); +} + +static int +dbm_length(dbmobject *dp) +{ + if (dp->di_dbm == NULL) { + PyErr_SetString(DbmError, "DBM object has already been closed"); + return -1; + } + if ( dp->di_size < 0 ) { + datum key; + int size; + + size = 0; + for ( key=dbm_firstkey(dp->di_dbm); key.dptr; + key = dbm_nextkey(dp->di_dbm)) + size++; + dp->di_size = size; + } + return dp->di_size; +} + +static PyObject * +dbm_subscript(dbmobject *dp, register PyObject *key) +{ + datum drec, krec; + int tmp_size; + + if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size) ) + return NULL; + + krec.dsize = tmp_size; + check_dbmobject_open(dp); + drec = dbm_fetch(dp->di_dbm, krec); + if ( drec.dptr == 0 ) { + PyErr_SetString(PyExc_KeyError, + PyString_AS_STRING((PyStringObject *)key)); + return NULL; + } + if ( dbm_error(dp->di_dbm) ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, ""); + return NULL; + } + return PyString_FromStringAndSize(drec.dptr, drec.dsize); +} + +static int +dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) +{ + datum krec, drec; + int tmp_size; + + if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) { + PyErr_SetString(PyExc_TypeError, + "dbm mappings have string indices only"); + return -1; + } + krec.dsize = tmp_size; + if (dp->di_dbm == NULL) { + PyErr_SetString(DbmError, "DBM object has already been closed"); + return -1; + } + dp->di_size = -1; + if (w == NULL) { + if ( dbm_delete(dp->di_dbm, krec) < 0 ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(PyExc_KeyError, + PyString_AS_STRING((PyStringObject *)v)); + return -1; + } + } else { + if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) { + PyErr_SetString(PyExc_TypeError, + "dbm mappings have string elements only"); + return -1; + } + drec.dsize = tmp_size; + if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, + "cannot add item to database"); + return -1; + } + } + if ( dbm_error(dp->di_dbm) ) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, ""); + return -1; + } + return 0; +} + +static PyMappingMethods dbm_as_mapping = { + (inquiry)dbm_length, /*mp_length*/ + (binaryfunc)dbm_subscript, /*mp_subscript*/ + (objobjargproc)dbm_ass_sub, /*mp_ass_subscript*/ +}; + +static PyObject * +dbm__close(register dbmobject *dp, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":close")) + return NULL; + if (dp->di_dbm) + dbm_close(dp->di_dbm); + dp->di_dbm = NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +dbm_keys(register dbmobject *dp, PyObject *args) +{ + register PyObject *v, *item; + datum key; + int err; + + if (!PyArg_ParseTuple(args, ":keys")) + return NULL; + check_dbmobject_open(dp); + v = PyList_New(0); + if (v == NULL) + return NULL; + for (key = dbm_firstkey(dp->di_dbm); key.dptr; + key = dbm_nextkey(dp->di_dbm)) { + item = PyString_FromStringAndSize(key.dptr, key.dsize); + if (item == NULL) { + Py_DECREF(v); + return NULL; + } + err = PyList_Append(v, item); + Py_DECREF(item); + if (err != 0) { + Py_DECREF(v); + return NULL; + } + } + return v; +} + +static PyObject * +dbm_has_key(register dbmobject *dp, PyObject *args) +{ + datum key, val; + int tmp_size; + + if (!PyArg_ParseTuple(args, "s#:has_key", &key.dptr, &tmp_size)) + return NULL; + key.dsize = tmp_size; + check_dbmobject_open(dp); + val = dbm_fetch(dp->di_dbm, key); + return PyInt_FromLong(val.dptr != NULL); +} + +static PyObject * +dbm_get(register dbmobject *dp, PyObject *args) +{ + datum key, val; + PyObject *defvalue = Py_None; + int tmp_size; + + if (!PyArg_ParseTuple(args, "s#|O:get", + &key.dptr, &tmp_size, &defvalue)) + return NULL; + key.dsize = tmp_size; + check_dbmobject_open(dp); + val = dbm_fetch(dp->di_dbm, key); + if (val.dptr != NULL) + return PyString_FromStringAndSize(val.dptr, val.dsize); + else { + Py_INCREF(defvalue); + return defvalue; + } +} + +static PyObject * +dbm_setdefault(register dbmobject *dp, PyObject *args) +{ + datum key, val; + PyObject *defvalue = NULL; + int tmp_size; + + if (!PyArg_ParseTuple(args, "s#|S:setdefault", + &key.dptr, &tmp_size, &defvalue)) + return NULL; + key.dsize = tmp_size; + check_dbmobject_open(dp); + val = dbm_fetch(dp->di_dbm, key); + if (val.dptr != NULL) + return PyString_FromStringAndSize(val.dptr, val.dsize); + if (defvalue == NULL) { + defvalue = PyString_FromStringAndSize(NULL, 0); + if (defvalue == NULL) + return NULL; + } + else + Py_INCREF(defvalue); + val.dptr = PyString_AS_STRING(defvalue); + val.dsize = PyString_GET_SIZE(defvalue); + if (dbm_store(dp->di_dbm, key, val, DBM_INSERT) < 0) { + dbm_clearerr(dp->di_dbm); + PyErr_SetString(DbmError, "cannot add item to database"); + return NULL; + } + return defvalue; +} + +static PyMethodDef dbm_methods[] = { + {"close", (PyCFunction)dbm__close, METH_VARARGS, + "close()\nClose the database."}, + {"keys", (PyCFunction)dbm_keys, METH_VARARGS, + "keys() -> list\nReturn a list of all keys in the database."}, + {"has_key", (PyCFunction)dbm_has_key, METH_VARARGS, + "has_key(key} -> boolean\nReturn true iff key is in the database."}, + {"get", (PyCFunction)dbm_get, METH_VARARGS, + "get(key[, default]) -> value\n" + "Return the value for key if present, otherwise default."}, + {"setdefault", (PyCFunction)dbm_setdefault, METH_VARARGS, + "setdefault(key[, default]) -> value\n" + "Return the value for key if present, otherwise default. If key\n" + "is not in the database, it is inserted with default as the value."}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +dbm_getattr(dbmobject *dp, char *name) +{ + return Py_FindMethod(dbm_methods, (PyObject *)dp, name); +} + +static PyTypeObject Dbmtype = { + PyObject_HEAD_INIT(NULL) + 0, + "dbm.dbm", + sizeof(dbmobject), + 0, + (destructor)dbm_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)dbm_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &dbm_as_mapping, /*tp_as_mapping*/ +}; + +/* ----------------------------------------------------------------- */ + +static PyObject * +dbmopen(PyObject *self, PyObject *args) +{ + char *name; + char *flags = "r"; + int iflags; + int mode = 0666; + + if ( !PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode) ) + return NULL; + if ( strcmp(flags, "r") == 0 ) + iflags = O_RDONLY; + else if ( strcmp(flags, "w") == 0 ) + iflags = O_RDWR; + else if ( strcmp(flags, "rw") == 0 ) /* B/W compat */ + iflags = O_RDWR|O_CREAT; + else if ( strcmp(flags, "c") == 0 ) + iflags = O_RDWR|O_CREAT; + else if ( strcmp(flags, "n") == 0 ) + iflags = O_RDWR|O_CREAT|O_TRUNC; + else { + PyErr_SetString(DbmError, + "arg 2 to open should be 'r', 'w', 'c', or 'n'"); + return NULL; + } + return newdbmobject(name, iflags, mode); +} + +static PyMethodDef dbmmodule_methods[] = { + { "open", (PyCFunction)dbmopen, METH_VARARGS, + "open(path[, flag[, mode]]) -> mapping\n" + "Return a database object."}, + { 0, 0 }, +}; + +PyMODINIT_FUNC +initdbm(void) { + PyObject *m, *d, *s; + + Dbmtype.ob_type = &PyType_Type; + m = Py_InitModule("dbm", dbmmodule_methods); + d = PyModule_GetDict(m); + if (DbmError == NULL) + DbmError = PyErr_NewException("dbm.error", NULL, NULL); + s = PyString_FromString(which_dbm); + if (s != NULL) { + PyDict_SetItemString(d, "library", s); + Py_DECREF(s); + } + if (DbmError != NULL) + PyDict_SetItemString(d, "error", DbmError); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/dlmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/dlmodule.c new file mode 100644 index 00000000..bf66eb52 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/dlmodule.c @@ -0,0 +1,254 @@ + +/* dl module */ + +#include "Python.h" + +#include + +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif + +typedef void *PyUnivPtr; +typedef struct { + PyObject_HEAD + PyUnivPtr *dl_handle; +} dlobject; + +static PyTypeObject Dltype; + +static PyObject *Dlerror; + +static PyObject * +newdlobject(PyUnivPtr *handle) +{ + dlobject *xp; + xp = PyObject_New(dlobject, &Dltype); + if (xp == NULL) + return NULL; + xp->dl_handle = handle; + return (PyObject *)xp; +} + +static void +dl_dealloc(dlobject *xp) +{ + if (xp->dl_handle != NULL) + dlclose(xp->dl_handle); + PyObject_Del(xp); +} + +static PyObject * +dl_close(dlobject *xp) +{ + if (xp->dl_handle != NULL) { + dlclose(xp->dl_handle); + xp->dl_handle = NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +dl_sym(dlobject *xp, PyObject *args) +{ + char *name; + PyUnivPtr *func; + if (PyString_Check(args)) { + name = PyString_AS_STRING(args); + } else { + PyErr_Format(PyExc_TypeError, "expected string, found %.200s", + args->ob_type->tp_name); + return NULL; + } + func = dlsym(xp->dl_handle, name); + if (func == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyInt_FromLong((long)func); +} + +static PyObject * +dl_call(dlobject *xp, PyObject *args) +{ + PyObject *name; + long (*func)(long, long, long, long, long, + long, long, long, long, long); + long alist[10]; + long res; + int i; + int n = PyTuple_Size(args); + if (n < 1) { + PyErr_SetString(PyExc_TypeError, "at least a name is needed"); + return NULL; + } + name = PyTuple_GetItem(args, 0); + if (!PyString_Check(name)) { + PyErr_SetString(PyExc_TypeError, + "function name must be a string"); + return NULL; + } + func = (long (*)(long, long, long, long, long, + long, long, long, long, long)) + dlsym(xp->dl_handle, PyString_AsString(name)); + if (func == NULL) { + PyErr_SetString(PyExc_ValueError, dlerror()); + return NULL; + } + if (n-1 > 10) { + PyErr_SetString(PyExc_TypeError, + "too many arguments (max 10)"); + return NULL; + } + for (i = 1; i < n; i++) { + PyObject *v = PyTuple_GetItem(args, i); + if (PyInt_Check(v)) + alist[i-1] = PyInt_AsLong(v); + else if (PyString_Check(v)) + alist[i-1] = (long)PyString_AsString(v); + else if (v == Py_None) + alist[i-1] = (long) ((char *)NULL); + else { + PyErr_SetString(PyExc_TypeError, + "arguments must be int, string or None"); + return NULL; + } + } + for (; i <= 10; i++) + alist[i-1] = 0; + res = (*func)(alist[0], alist[1], alist[2], alist[3], alist[4], + alist[5], alist[6], alist[7], alist[8], alist[9]); + return PyInt_FromLong(res); +} + +static PyMethodDef dlobject_methods[] = { + {"call", (PyCFunction)dl_call, METH_VARARGS}, + {"sym", (PyCFunction)dl_sym, METH_O}, + {"close", (PyCFunction)dl_close, METH_NOARGS}, + {NULL, NULL} /* Sentinel */ +}; + +static PyObject * +dl_getattr(dlobject *xp, char *name) +{ + return Py_FindMethod(dlobject_methods, (PyObject *)xp, name); +} + + +static PyTypeObject Dltype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "dl.dl", /*tp_name*/ + sizeof(dlobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)dl_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)dl_getattr,/*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + +static PyObject * +dl_open(PyObject *self, PyObject *args) +{ + char *name; + int mode; + PyUnivPtr *handle; + if (sizeof(int) != sizeof(long) || + sizeof(long) != sizeof(char *)) { + PyErr_SetString(PyExc_SystemError, + "module dl requires sizeof(int) == sizeof(long) == sizeof(char*)"); + return NULL; + } + + if (PyArg_ParseTuple(args, "z:open", &name)) + mode = RTLD_LAZY; + else { + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "zi:open", &name, &mode)) + return NULL; +#ifndef RTLD_NOW + if (mode != RTLD_LAZY) { + PyErr_SetString(PyExc_ValueError, "mode must be 1"); + return NULL; + } +#endif + } + handle = dlopen(name, mode); + if (handle == NULL) { + PyErr_SetString(Dlerror, dlerror()); + return NULL; + } + return newdlobject(handle); +} + +static PyMethodDef dl_methods[] = { + {"open", dl_open, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* From socketmodule.c + * Convenience routine to export an integer value. + * + * Errors are silently ignored, for better or for worse... + */ +static void +insint(PyObject *d, char *name, int value) +{ + PyObject *v = PyInt_FromLong((long) value); + if (!v || PyDict_SetItemString(d, name, v)) + PyErr_Clear(); + + Py_XDECREF(v); +} + +PyMODINIT_FUNC +initdl(void) +{ + PyObject *m, *d, *x; + + /* Initialize object type */ + Dltype.ob_type = &PyType_Type; + + /* Create the module and add the functions */ + m = Py_InitModule("dl", dl_methods); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + Dlerror = x = PyErr_NewException("dl.error", NULL, NULL); + PyDict_SetItemString(d, "error", x); + x = PyInt_FromLong((long)RTLD_LAZY); + PyDict_SetItemString(d, "RTLD_LAZY", x); +#define INSINT(X) insint(d,#X,X) +#ifdef RTLD_NOW + INSINT(RTLD_NOW); +#endif +#ifdef RTLD_NOLOAD + INSINT(RTLD_NOLOAD); +#endif +#ifdef RTLD_GLOBAL + INSINT(RTLD_GLOBAL); +#endif +#ifdef RTLD_LOCAL + INSINT(RTLD_LOCAL); +#endif +#ifdef RTLD_PARENT + INSINT(RTLD_PARENT); +#endif +#ifdef RTLD_GROUP + INSINT(RTLD_GROUP); +#endif +#ifdef RTLD_WORLD + INSINT(RTLD_WORLD); +#endif +#ifdef RTLD_NODELETE + INSINT(RTLD_NODELETE); +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/errnomodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/errnomodule.c new file mode 100644 index 00000000..67379124 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/errnomodule.c @@ -0,0 +1,795 @@ + +/* Errno module */ + +#include "Python.h" + +/* Mac with GUSI has more errors than those in errno.h */ +#ifdef USE_GUSI +#include +#endif + +/* Windows socket errors (WSA*) */ +#ifdef MS_WINDOWS +#ifdef MS_XBOX +#include +#else +#include +#endif +#endif + +/* + * Pull in the system error definitions + */ + +static PyMethodDef errno_methods[] = { + {NULL, NULL} +}; + +/* Helper function doing the dictionary inserting */ + +static void +_inscode(PyObject *d, PyObject *de, char *name, int code) +{ + PyObject *u = PyString_FromString(name); + PyObject *v = PyInt_FromLong((long) code); + + /* Don't bother checking for errors; they'll be caught at the end + * of the module initialization function by the caller of + * initerrno(). + */ + if (u && v) { + /* insert in modules dict */ + PyDict_SetItem(d, u, v); + /* insert in errorcode dict */ + PyDict_SetItem(de, v, u); + } + Py_XDECREF(u); + Py_XDECREF(v); +} + +PyDoc_STRVAR(errno__doc__, +"This module makes available standard errno system symbols.\n\ +\n\ +The value of each symbol is the corresponding integer value,\n\ +e.g., on most systems, errno.ENOENT equals the integer 2.\n\ +\n\ +The dictionary errno.errorcode maps numeric codes to symbol names,\n\ +e.g., errno.errorcode[2] could be the string 'ENOENT'.\n\ +\n\ +Symbols that are not relevant to the underlying system are not defined.\n\ +\n\ +To map error codes to error messages, use the function os.strerror(),\n\ +e.g. os.strerror(2) could return 'No such file or directory'."); + +PyMODINIT_FUNC +initerrno(void) +{ + PyObject *m, *d, *de; + m = Py_InitModule3("errno", errno_methods, errno__doc__); + d = PyModule_GetDict(m); + de = PyDict_New(); + if (!d || !de || PyDict_SetItemString(d, "errorcode", de) < 0) + return; + +/* Macro so I don't have to edit each and every line below... */ +#define inscode(d, ds, de, name, code, comment) _inscode(d, de, name, code) + + /* + * The names and comments are borrowed from linux/include/errno.h, + * which should be pretty all-inclusive + */ + +#ifdef ENODEV + inscode(d, ds, de, "ENODEV", ENODEV, "No such device"); +#endif +#ifdef ENOCSI + inscode(d, ds, de, "ENOCSI", ENOCSI, "No CSI structure available"); +#endif +#ifdef EHOSTUNREACH + inscode(d, ds, de, "EHOSTUNREACH", EHOSTUNREACH, "No route to host"); +#else +#ifdef WSAEHOSTUNREACH + inscode(d, ds, de, "EHOSTUNREACH", WSAEHOSTUNREACH, "No route to host"); +#endif +#endif +#ifdef ENOMSG + inscode(d, ds, de, "ENOMSG", ENOMSG, "No message of desired type"); +#endif +#ifdef EUCLEAN + inscode(d, ds, de, "EUCLEAN", EUCLEAN, "Structure needs cleaning"); +#endif +#ifdef EL2NSYNC + inscode(d, ds, de, "EL2NSYNC", EL2NSYNC, "Level 2 not synchronized"); +#endif +#ifdef EL2HLT + inscode(d, ds, de, "EL2HLT", EL2HLT, "Level 2 halted"); +#endif +#ifdef ENODATA + inscode(d, ds, de, "ENODATA", ENODATA, "No data available"); +#endif +#ifdef ENOTBLK + inscode(d, ds, de, "ENOTBLK", ENOTBLK, "Block device required"); +#endif +#ifdef ENOSYS + inscode(d, ds, de, "ENOSYS", ENOSYS, "Function not implemented"); +#endif +#ifdef EPIPE + inscode(d, ds, de, "EPIPE", EPIPE, "Broken pipe"); +#endif +#ifdef EINVAL + inscode(d, ds, de, "EINVAL", EINVAL, "Invalid argument"); +#else +#ifdef WSAEINVAL + inscode(d, ds, de, "EINVAL", WSAEINVAL, "Invalid argument"); +#endif +#endif +#ifdef EOVERFLOW + inscode(d, ds, de, "EOVERFLOW", EOVERFLOW, "Value too large for defined data type"); +#endif +#ifdef EADV + inscode(d, ds, de, "EADV", EADV, "Advertise error"); +#endif +#ifdef EINTR + inscode(d, ds, de, "EINTR", EINTR, "Interrupted system call"); +#else +#ifdef WSAEINTR + inscode(d, ds, de, "EINTR", WSAEINTR, "Interrupted system call"); +#endif +#endif +#ifdef EUSERS + inscode(d, ds, de, "EUSERS", EUSERS, "Too many users"); +#else +#ifdef WSAEUSERS + inscode(d, ds, de, "EUSERS", WSAEUSERS, "Too many users"); +#endif +#endif +#ifdef ENOTEMPTY + inscode(d, ds, de, "ENOTEMPTY", ENOTEMPTY, "Directory not empty"); +#else +#ifdef WSAENOTEMPTY + inscode(d, ds, de, "ENOTEMPTY", WSAENOTEMPTY, "Directory not empty"); +#endif +#endif +#ifdef ENOBUFS + inscode(d, ds, de, "ENOBUFS", ENOBUFS, "No buffer space available"); +#else +#ifdef WSAENOBUFS + inscode(d, ds, de, "ENOBUFS", WSAENOBUFS, "No buffer space available"); +#endif +#endif +#ifdef EPROTO + inscode(d, ds, de, "EPROTO", EPROTO, "Protocol error"); +#endif +#ifdef EREMOTE + inscode(d, ds, de, "EREMOTE", EREMOTE, "Object is remote"); +#else +#ifdef WSAEREMOTE + inscode(d, ds, de, "EREMOTE", WSAEREMOTE, "Object is remote"); +#endif +#endif +#ifdef ENAVAIL + inscode(d, ds, de, "ENAVAIL", ENAVAIL, "No XENIX semaphores available"); +#endif +#ifdef ECHILD + inscode(d, ds, de, "ECHILD", ECHILD, "No child processes"); +#endif +#ifdef ELOOP + inscode(d, ds, de, "ELOOP", ELOOP, "Too many symbolic links encountered"); +#else +#ifdef WSAELOOP + inscode(d, ds, de, "ELOOP", WSAELOOP, "Too many symbolic links encountered"); +#endif +#endif +#ifdef EXDEV + inscode(d, ds, de, "EXDEV", EXDEV, "Cross-device link"); +#endif +#ifdef E2BIG + inscode(d, ds, de, "E2BIG", E2BIG, "Arg list too long"); +#endif +#ifdef ESRCH + inscode(d, ds, de, "ESRCH", ESRCH, "No such process"); +#endif +#ifdef EMSGSIZE + inscode(d, ds, de, "EMSGSIZE", EMSGSIZE, "Message too long"); +#else +#ifdef WSAEMSGSIZE + inscode(d, ds, de, "EMSGSIZE", WSAEMSGSIZE, "Message too long"); +#endif +#endif +#ifdef EAFNOSUPPORT + inscode(d, ds, de, "EAFNOSUPPORT", EAFNOSUPPORT, "Address family not supported by protocol"); +#else +#ifdef WSAEAFNOSUPPORT + inscode(d, ds, de, "EAFNOSUPPORT", WSAEAFNOSUPPORT, "Address family not supported by protocol"); +#endif +#endif +#ifdef EBADR + inscode(d, ds, de, "EBADR", EBADR, "Invalid request descriptor"); +#endif +#ifdef EHOSTDOWN + inscode(d, ds, de, "EHOSTDOWN", EHOSTDOWN, "Host is down"); +#else +#ifdef WSAEHOSTDOWN + inscode(d, ds, de, "EHOSTDOWN", WSAEHOSTDOWN, "Host is down"); +#endif +#endif +#ifdef EPFNOSUPPORT + inscode(d, ds, de, "EPFNOSUPPORT", EPFNOSUPPORT, "Protocol family not supported"); +#else +#ifdef WSAEPFNOSUPPORT + inscode(d, ds, de, "EPFNOSUPPORT", WSAEPFNOSUPPORT, "Protocol family not supported"); +#endif +#endif +#ifdef ENOPROTOOPT + inscode(d, ds, de, "ENOPROTOOPT", ENOPROTOOPT, "Protocol not available"); +#else +#ifdef WSAENOPROTOOPT + inscode(d, ds, de, "ENOPROTOOPT", WSAENOPROTOOPT, "Protocol not available"); +#endif +#endif +#ifdef EBUSY + inscode(d, ds, de, "EBUSY", EBUSY, "Device or resource busy"); +#endif +#ifdef EWOULDBLOCK + inscode(d, ds, de, "EWOULDBLOCK", EWOULDBLOCK, "Operation would block"); +#else +#ifdef WSAEWOULDBLOCK + inscode(d, ds, de, "EWOULDBLOCK", WSAEWOULDBLOCK, "Operation would block"); +#endif +#endif +#ifdef EBADFD + inscode(d, ds, de, "EBADFD", EBADFD, "File descriptor in bad state"); +#endif +#ifdef EDOTDOT + inscode(d, ds, de, "EDOTDOT", EDOTDOT, "RFS specific error"); +#endif +#ifdef EISCONN + inscode(d, ds, de, "EISCONN", EISCONN, "Transport endpoint is already connected"); +#else +#ifdef WSAEISCONN + inscode(d, ds, de, "EISCONN", WSAEISCONN, "Transport endpoint is already connected"); +#endif +#endif +#ifdef ENOANO + inscode(d, ds, de, "ENOANO", ENOANO, "No anode"); +#endif +#ifdef ESHUTDOWN + inscode(d, ds, de, "ESHUTDOWN", ESHUTDOWN, "Cannot send after transport endpoint shutdown"); +#else +#ifdef WSAESHUTDOWN + inscode(d, ds, de, "ESHUTDOWN", WSAESHUTDOWN, "Cannot send after transport endpoint shutdown"); +#endif +#endif +#ifdef ECHRNG + inscode(d, ds, de, "ECHRNG", ECHRNG, "Channel number out of range"); +#endif +#ifdef ELIBBAD + inscode(d, ds, de, "ELIBBAD", ELIBBAD, "Accessing a corrupted shared library"); +#endif +#ifdef ENONET + inscode(d, ds, de, "ENONET", ENONET, "Machine is not on the network"); +#endif +#ifdef EBADE + inscode(d, ds, de, "EBADE", EBADE, "Invalid exchange"); +#endif +#ifdef EBADF + inscode(d, ds, de, "EBADF", EBADF, "Bad file number"); +#else +#ifdef WSAEBADF + inscode(d, ds, de, "EBADF", WSAEBADF, "Bad file number"); +#endif +#endif +#ifdef EMULTIHOP + inscode(d, ds, de, "EMULTIHOP", EMULTIHOP, "Multihop attempted"); +#endif +#ifdef EIO + inscode(d, ds, de, "EIO", EIO, "I/O error"); +#endif +#ifdef EUNATCH + inscode(d, ds, de, "EUNATCH", EUNATCH, "Protocol driver not attached"); +#endif +#ifdef EPROTOTYPE + inscode(d, ds, de, "EPROTOTYPE", EPROTOTYPE, "Protocol wrong type for socket"); +#else +#ifdef WSAEPROTOTYPE + inscode(d, ds, de, "EPROTOTYPE", WSAEPROTOTYPE, "Protocol wrong type for socket"); +#endif +#endif +#ifdef ENOSPC + inscode(d, ds, de, "ENOSPC", ENOSPC, "No space left on device"); +#endif +#ifdef ENOEXEC + inscode(d, ds, de, "ENOEXEC", ENOEXEC, "Exec format error"); +#endif +#ifdef EALREADY + inscode(d, ds, de, "EALREADY", EALREADY, "Operation already in progress"); +#else +#ifdef WSAEALREADY + inscode(d, ds, de, "EALREADY", WSAEALREADY, "Operation already in progress"); +#endif +#endif +#ifdef ENETDOWN + inscode(d, ds, de, "ENETDOWN", ENETDOWN, "Network is down"); +#else +#ifdef WSAENETDOWN + inscode(d, ds, de, "ENETDOWN", WSAENETDOWN, "Network is down"); +#endif +#endif +#ifdef ENOTNAM + inscode(d, ds, de, "ENOTNAM", ENOTNAM, "Not a XENIX named type file"); +#endif +#ifdef EACCES + inscode(d, ds, de, "EACCES", EACCES, "Permission denied"); +#else +#ifdef WSAEACCES + inscode(d, ds, de, "EACCES", WSAEACCES, "Permission denied"); +#endif +#endif +#ifdef ELNRNG + inscode(d, ds, de, "ELNRNG", ELNRNG, "Link number out of range"); +#endif +#ifdef EILSEQ + inscode(d, ds, de, "EILSEQ", EILSEQ, "Illegal byte sequence"); +#endif +#ifdef ENOTDIR + inscode(d, ds, de, "ENOTDIR", ENOTDIR, "Not a directory"); +#endif +#ifdef ENOTUNIQ + inscode(d, ds, de, "ENOTUNIQ", ENOTUNIQ, "Name not unique on network"); +#endif +#ifdef EPERM + inscode(d, ds, de, "EPERM", EPERM, "Operation not permitted"); +#endif +#ifdef EDOM + inscode(d, ds, de, "EDOM", EDOM, "Math argument out of domain of func"); +#endif +#ifdef EXFULL + inscode(d, ds, de, "EXFULL", EXFULL, "Exchange full"); +#endif +#ifdef ECONNREFUSED + inscode(d, ds, de, "ECONNREFUSED", ECONNREFUSED, "Connection refused"); +#else +#ifdef WSAECONNREFUSED + inscode(d, ds, de, "ECONNREFUSED", WSAECONNREFUSED, "Connection refused"); +#endif +#endif +#ifdef EISDIR + inscode(d, ds, de, "EISDIR", EISDIR, "Is a directory"); +#endif +#ifdef EPROTONOSUPPORT + inscode(d, ds, de, "EPROTONOSUPPORT", EPROTONOSUPPORT, "Protocol not supported"); +#else +#ifdef WSAEPROTONOSUPPORT + inscode(d, ds, de, "EPROTONOSUPPORT", WSAEPROTONOSUPPORT, "Protocol not supported"); +#endif +#endif +#ifdef EROFS + inscode(d, ds, de, "EROFS", EROFS, "Read-only file system"); +#endif +#ifdef EADDRNOTAVAIL + inscode(d, ds, de, "EADDRNOTAVAIL", EADDRNOTAVAIL, "Cannot assign requested address"); +#else +#ifdef WSAEADDRNOTAVAIL + inscode(d, ds, de, "EADDRNOTAVAIL", WSAEADDRNOTAVAIL, "Cannot assign requested address"); +#endif +#endif +#ifdef EIDRM + inscode(d, ds, de, "EIDRM", EIDRM, "Identifier removed"); +#endif +#ifdef ECOMM + inscode(d, ds, de, "ECOMM", ECOMM, "Communication error on send"); +#endif +#ifdef ESRMNT + inscode(d, ds, de, "ESRMNT", ESRMNT, "Srmount error"); +#endif +#ifdef EREMOTEIO + inscode(d, ds, de, "EREMOTEIO", EREMOTEIO, "Remote I/O error"); +#endif +#ifdef EL3RST + inscode(d, ds, de, "EL3RST", EL3RST, "Level 3 reset"); +#endif +#ifdef EBADMSG + inscode(d, ds, de, "EBADMSG", EBADMSG, "Not a data message"); +#endif +#ifdef ENFILE + inscode(d, ds, de, "ENFILE", ENFILE, "File table overflow"); +#endif +#ifdef ELIBMAX + inscode(d, ds, de, "ELIBMAX", ELIBMAX, "Attempting to link in too many shared libraries"); +#endif +#ifdef ESPIPE + inscode(d, ds, de, "ESPIPE", ESPIPE, "Illegal seek"); +#endif +#ifdef ENOLINK + inscode(d, ds, de, "ENOLINK", ENOLINK, "Link has been severed"); +#endif +#ifdef ENETRESET + inscode(d, ds, de, "ENETRESET", ENETRESET, "Network dropped connection because of reset"); +#else +#ifdef WSAENETRESET + inscode(d, ds, de, "ENETRESET", WSAENETRESET, "Network dropped connection because of reset"); +#endif +#endif +#ifdef ETIMEDOUT + inscode(d, ds, de, "ETIMEDOUT", ETIMEDOUT, "Connection timed out"); +#else +#ifdef WSAETIMEDOUT + inscode(d, ds, de, "ETIMEDOUT", WSAETIMEDOUT, "Connection timed out"); +#endif +#endif +#ifdef ENOENT + inscode(d, ds, de, "ENOENT", ENOENT, "No such file or directory"); +#endif +#ifdef EEXIST + inscode(d, ds, de, "EEXIST", EEXIST, "File exists"); +#endif +#ifdef EDQUOT + inscode(d, ds, de, "EDQUOT", EDQUOT, "Quota exceeded"); +#else +#ifdef WSAEDQUOT + inscode(d, ds, de, "EDQUOT", WSAEDQUOT, "Quota exceeded"); +#endif +#endif +#ifdef ENOSTR + inscode(d, ds, de, "ENOSTR", ENOSTR, "Device not a stream"); +#endif +#ifdef EBADSLT + inscode(d, ds, de, "EBADSLT", EBADSLT, "Invalid slot"); +#endif +#ifdef EBADRQC + inscode(d, ds, de, "EBADRQC", EBADRQC, "Invalid request code"); +#endif +#ifdef ELIBACC + inscode(d, ds, de, "ELIBACC", ELIBACC, "Can not access a needed shared library"); +#endif +#ifdef EFAULT + inscode(d, ds, de, "EFAULT", EFAULT, "Bad address"); +#else +#ifdef WSAEFAULT + inscode(d, ds, de, "EFAULT", WSAEFAULT, "Bad address"); +#endif +#endif +#ifdef EFBIG + inscode(d, ds, de, "EFBIG", EFBIG, "File too large"); +#endif +#ifdef EDEADLK + inscode(d, ds, de, "EDEADLK", EDEADLK, "Resource deadlock would occur"); +#endif +#ifdef ENOTCONN + inscode(d, ds, de, "ENOTCONN", ENOTCONN, "Transport endpoint is not connected"); +#else +#ifdef WSAENOTCONN + inscode(d, ds, de, "ENOTCONN", WSAENOTCONN, "Transport endpoint is not connected"); +#endif +#endif +#ifdef EDESTADDRREQ + inscode(d, ds, de, "EDESTADDRREQ", EDESTADDRREQ, "Destination address required"); +#else +#ifdef WSAEDESTADDRREQ + inscode(d, ds, de, "EDESTADDRREQ", WSAEDESTADDRREQ, "Destination address required"); +#endif +#endif +#ifdef ELIBSCN + inscode(d, ds, de, "ELIBSCN", ELIBSCN, ".lib section in a.out corrupted"); +#endif +#ifdef ENOLCK + inscode(d, ds, de, "ENOLCK", ENOLCK, "No record locks available"); +#endif +#ifdef EISNAM + inscode(d, ds, de, "EISNAM", EISNAM, "Is a named type file"); +#endif +#ifdef ECONNABORTED + inscode(d, ds, de, "ECONNABORTED", ECONNABORTED, "Software caused connection abort"); +#else +#ifdef WSAECONNABORTED + inscode(d, ds, de, "ECONNABORTED", WSAECONNABORTED, "Software caused connection abort"); +#endif +#endif +#ifdef ENETUNREACH + inscode(d, ds, de, "ENETUNREACH", ENETUNREACH, "Network is unreachable"); +#else +#ifdef WSAENETUNREACH + inscode(d, ds, de, "ENETUNREACH", WSAENETUNREACH, "Network is unreachable"); +#endif +#endif +#ifdef ESTALE + inscode(d, ds, de, "ESTALE", ESTALE, "Stale NFS file handle"); +#else +#ifdef WSAESTALE + inscode(d, ds, de, "ESTALE", WSAESTALE, "Stale NFS file handle"); +#endif +#endif +#ifdef ENOSR + inscode(d, ds, de, "ENOSR", ENOSR, "Out of streams resources"); +#endif +#ifdef ENOMEM + inscode(d, ds, de, "ENOMEM", ENOMEM, "Out of memory"); +#endif +#ifdef ENOTSOCK + inscode(d, ds, de, "ENOTSOCK", ENOTSOCK, "Socket operation on non-socket"); +#else +#ifdef WSAENOTSOCK + inscode(d, ds, de, "ENOTSOCK", WSAENOTSOCK, "Socket operation on non-socket"); +#endif +#endif +#ifdef ESTRPIPE + inscode(d, ds, de, "ESTRPIPE", ESTRPIPE, "Streams pipe error"); +#endif +#ifdef EMLINK + inscode(d, ds, de, "EMLINK", EMLINK, "Too many links"); +#endif +#ifdef ERANGE + inscode(d, ds, de, "ERANGE", ERANGE, "Math result not representable"); +#endif +#ifdef ELIBEXEC + inscode(d, ds, de, "ELIBEXEC", ELIBEXEC, "Cannot exec a shared library directly"); +#endif +#ifdef EL3HLT + inscode(d, ds, de, "EL3HLT", EL3HLT, "Level 3 halted"); +#endif +#ifdef ECONNRESET + inscode(d, ds, de, "ECONNRESET", ECONNRESET, "Connection reset by peer"); +#else +#ifdef WSAECONNRESET + inscode(d, ds, de, "ECONNRESET", WSAECONNRESET, "Connection reset by peer"); +#endif +#endif +#ifdef EADDRINUSE + inscode(d, ds, de, "EADDRINUSE", EADDRINUSE, "Address already in use"); +#else +#ifdef WSAEADDRINUSE + inscode(d, ds, de, "EADDRINUSE", WSAEADDRINUSE, "Address already in use"); +#endif +#endif +#ifdef EOPNOTSUPP + inscode(d, ds, de, "EOPNOTSUPP", EOPNOTSUPP, "Operation not supported on transport endpoint"); +#else +#ifdef WSAEOPNOTSUPP + inscode(d, ds, de, "EOPNOTSUPP", WSAEOPNOTSUPP, "Operation not supported on transport endpoint"); +#endif +#endif +#ifdef EREMCHG + inscode(d, ds, de, "EREMCHG", EREMCHG, "Remote address changed"); +#endif +#ifdef EAGAIN + inscode(d, ds, de, "EAGAIN", EAGAIN, "Try again"); +#endif +#ifdef ENAMETOOLONG + inscode(d, ds, de, "ENAMETOOLONG", ENAMETOOLONG, "File name too long"); +#else +#ifdef WSAENAMETOOLONG + inscode(d, ds, de, "ENAMETOOLONG", WSAENAMETOOLONG, "File name too long"); +#endif +#endif +#ifdef ENOTTY + inscode(d, ds, de, "ENOTTY", ENOTTY, "Not a typewriter"); +#endif +#ifdef ERESTART + inscode(d, ds, de, "ERESTART", ERESTART, "Interrupted system call should be restarted"); +#endif +#ifdef ESOCKTNOSUPPORT + inscode(d, ds, de, "ESOCKTNOSUPPORT", ESOCKTNOSUPPORT, "Socket type not supported"); +#else +#ifdef WSAESOCKTNOSUPPORT + inscode(d, ds, de, "ESOCKTNOSUPPORT", WSAESOCKTNOSUPPORT, "Socket type not supported"); +#endif +#endif +#ifdef ETIME + inscode(d, ds, de, "ETIME", ETIME, "Timer expired"); +#endif +#ifdef EBFONT + inscode(d, ds, de, "EBFONT", EBFONT, "Bad font file format"); +#endif +#ifdef EDEADLOCK + inscode(d, ds, de, "EDEADLOCK", EDEADLOCK, "Error EDEADLOCK"); +#endif +#ifdef ETOOMANYREFS + inscode(d, ds, de, "ETOOMANYREFS", ETOOMANYREFS, "Too many references: cannot splice"); +#else +#ifdef WSAETOOMANYREFS + inscode(d, ds, de, "ETOOMANYREFS", WSAETOOMANYREFS, "Too many references: cannot splice"); +#endif +#endif +#ifdef EMFILE + inscode(d, ds, de, "EMFILE", EMFILE, "Too many open files"); +#else +#ifdef WSAEMFILE + inscode(d, ds, de, "EMFILE", WSAEMFILE, "Too many open files"); +#endif +#endif +#ifdef ETXTBSY + inscode(d, ds, de, "ETXTBSY", ETXTBSY, "Text file busy"); +#endif +#ifdef EINPROGRESS + inscode(d, ds, de, "EINPROGRESS", EINPROGRESS, "Operation now in progress"); +#else +#ifdef WSAEINPROGRESS + inscode(d, ds, de, "EINPROGRESS", WSAEINPROGRESS, "Operation now in progress"); +#endif +#endif +#ifdef ENXIO + inscode(d, ds, de, "ENXIO", ENXIO, "No such device or address"); +#endif +#ifdef ENOPKG + inscode(d, ds, de, "ENOPKG", ENOPKG, "Package not installed"); +#endif +#ifdef WSASY + inscode(d, ds, de, "WSASY", WSASY, "Error WSASY"); +#endif +#ifdef WSAEHOSTDOWN + inscode(d, ds, de, "WSAEHOSTDOWN", WSAEHOSTDOWN, "Host is down"); +#endif +#ifdef WSAENETDOWN + inscode(d, ds, de, "WSAENETDOWN", WSAENETDOWN, "Network is down"); +#endif +#ifdef WSAENOTSOCK + inscode(d, ds, de, "WSAENOTSOCK", WSAENOTSOCK, "Socket operation on non-socket"); +#endif +#ifdef WSAEHOSTUNREACH + inscode(d, ds, de, "WSAEHOSTUNREACH", WSAEHOSTUNREACH, "No route to host"); +#endif +#ifdef WSAELOOP + inscode(d, ds, de, "WSAELOOP", WSAELOOP, "Too many symbolic links encountered"); +#endif +#ifdef WSAEMFILE + inscode(d, ds, de, "WSAEMFILE", WSAEMFILE, "Too many open files"); +#endif +#ifdef WSAESTALE + inscode(d, ds, de, "WSAESTALE", WSAESTALE, "Stale NFS file handle"); +#endif +#ifdef WSAVERNOTSUPPORTED + inscode(d, ds, de, "WSAVERNOTSUPPORTED", WSAVERNOTSUPPORTED, "Error WSAVERNOTSUPPORTED"); +#endif +#ifdef WSAENETUNREACH + inscode(d, ds, de, "WSAENETUNREACH", WSAENETUNREACH, "Network is unreachable"); +#endif +#ifdef WSAEPROCLIM + inscode(d, ds, de, "WSAEPROCLIM", WSAEPROCLIM, "Error WSAEPROCLIM"); +#endif +#ifdef WSAEFAULT + inscode(d, ds, de, "WSAEFAULT", WSAEFAULT, "Bad address"); +#endif +#ifdef WSANOTINITIALISED + inscode(d, ds, de, "WSANOTINITIALISED", WSANOTINITIALISED, "Error WSANOTINITIALISED"); +#endif +#ifdef WSAEUSERS + inscode(d, ds, de, "WSAEUSERS", WSAEUSERS, "Too many users"); +#endif +#ifdef WSAMAKEASYNCREPL + inscode(d, ds, de, "WSAMAKEASYNCREPL", WSAMAKEASYNCREPL, "Error WSAMAKEASYNCREPL"); +#endif +#ifdef WSAENOPROTOOPT + inscode(d, ds, de, "WSAENOPROTOOPT", WSAENOPROTOOPT, "Protocol not available"); +#endif +#ifdef WSAECONNABORTED + inscode(d, ds, de, "WSAECONNABORTED", WSAECONNABORTED, "Software caused connection abort"); +#endif +#ifdef WSAENAMETOOLONG + inscode(d, ds, de, "WSAENAMETOOLONG", WSAENAMETOOLONG, "File name too long"); +#endif +#ifdef WSAENOTEMPTY + inscode(d, ds, de, "WSAENOTEMPTY", WSAENOTEMPTY, "Directory not empty"); +#endif +#ifdef WSAESHUTDOWN + inscode(d, ds, de, "WSAESHUTDOWN", WSAESHUTDOWN, "Cannot send after transport endpoint shutdown"); +#endif +#ifdef WSAEAFNOSUPPORT + inscode(d, ds, de, "WSAEAFNOSUPPORT", WSAEAFNOSUPPORT, "Address family not supported by protocol"); +#endif +#ifdef WSAETOOMANYREFS + inscode(d, ds, de, "WSAETOOMANYREFS", WSAETOOMANYREFS, "Too many references: cannot splice"); +#endif +#ifdef WSAEACCES + inscode(d, ds, de, "WSAEACCES", WSAEACCES, "Permission denied"); +#endif +#ifdef WSATR + inscode(d, ds, de, "WSATR", WSATR, "Error WSATR"); +#endif +#ifdef WSABASEERR + inscode(d, ds, de, "WSABASEERR", WSABASEERR, "Error WSABASEERR"); +#endif +#ifdef WSADESCRIPTIO + inscode(d, ds, de, "WSADESCRIPTIO", WSADESCRIPTIO, "Error WSADESCRIPTIO"); +#endif +#ifdef WSAEMSGSIZE + inscode(d, ds, de, "WSAEMSGSIZE", WSAEMSGSIZE, "Message too long"); +#endif +#ifdef WSAEBADF + inscode(d, ds, de, "WSAEBADF", WSAEBADF, "Bad file number"); +#endif +#ifdef WSAECONNRESET + inscode(d, ds, de, "WSAECONNRESET", WSAECONNRESET, "Connection reset by peer"); +#endif +#ifdef WSAGETSELECTERRO + inscode(d, ds, de, "WSAGETSELECTERRO", WSAGETSELECTERRO, "Error WSAGETSELECTERRO"); +#endif +#ifdef WSAETIMEDOUT + inscode(d, ds, de, "WSAETIMEDOUT", WSAETIMEDOUT, "Connection timed out"); +#endif +#ifdef WSAENOBUFS + inscode(d, ds, de, "WSAENOBUFS", WSAENOBUFS, "No buffer space available"); +#endif +#ifdef WSAEDISCON + inscode(d, ds, de, "WSAEDISCON", WSAEDISCON, "Error WSAEDISCON"); +#endif +#ifdef WSAEINTR + inscode(d, ds, de, "WSAEINTR", WSAEINTR, "Interrupted system call"); +#endif +#ifdef WSAEPROTOTYPE + inscode(d, ds, de, "WSAEPROTOTYPE", WSAEPROTOTYPE, "Protocol wrong type for socket"); +#endif +#ifdef WSAHOS + inscode(d, ds, de, "WSAHOS", WSAHOS, "Error WSAHOS"); +#endif +#ifdef WSAEADDRINUSE + inscode(d, ds, de, "WSAEADDRINUSE", WSAEADDRINUSE, "Address already in use"); +#endif +#ifdef WSAEADDRNOTAVAIL + inscode(d, ds, de, "WSAEADDRNOTAVAIL", WSAEADDRNOTAVAIL, "Cannot assign requested address"); +#endif +#ifdef WSAEALREADY + inscode(d, ds, de, "WSAEALREADY", WSAEALREADY, "Operation already in progress"); +#endif +#ifdef WSAEPROTONOSUPPORT + inscode(d, ds, de, "WSAEPROTONOSUPPORT", WSAEPROTONOSUPPORT, "Protocol not supported"); +#endif +#ifdef WSASYSNOTREADY + inscode(d, ds, de, "WSASYSNOTREADY", WSASYSNOTREADY, "Error WSASYSNOTREADY"); +#endif +#ifdef WSAEWOULDBLOCK + inscode(d, ds, de, "WSAEWOULDBLOCK", WSAEWOULDBLOCK, "Operation would block"); +#endif +#ifdef WSAEPFNOSUPPORT + inscode(d, ds, de, "WSAEPFNOSUPPORT", WSAEPFNOSUPPORT, "Protocol family not supported"); +#endif +#ifdef WSAEOPNOTSUPP + inscode(d, ds, de, "WSAEOPNOTSUPP", WSAEOPNOTSUPP, "Operation not supported on transport endpoint"); +#endif +#ifdef WSAEISCONN + inscode(d, ds, de, "WSAEISCONN", WSAEISCONN, "Transport endpoint is already connected"); +#endif +#ifdef WSAEDQUOT + inscode(d, ds, de, "WSAEDQUOT", WSAEDQUOT, "Quota exceeded"); +#endif +#ifdef WSAENOTCONN + inscode(d, ds, de, "WSAENOTCONN", WSAENOTCONN, "Transport endpoint is not connected"); +#endif +#ifdef WSAEREMOTE + inscode(d, ds, de, "WSAEREMOTE", WSAEREMOTE, "Object is remote"); +#endif +#ifdef WSAEINVAL + inscode(d, ds, de, "WSAEINVAL", WSAEINVAL, "Invalid argument"); +#endif +#ifdef WSAEINPROGRESS + inscode(d, ds, de, "WSAEINPROGRESS", WSAEINPROGRESS, "Operation now in progress"); +#endif +#ifdef WSAGETSELECTEVEN + inscode(d, ds, de, "WSAGETSELECTEVEN", WSAGETSELECTEVEN, "Error WSAGETSELECTEVEN"); +#endif +#ifdef WSAESOCKTNOSUPPORT + inscode(d, ds, de, "WSAESOCKTNOSUPPORT", WSAESOCKTNOSUPPORT, "Socket type not supported"); +#endif +#ifdef WSAGETASYNCERRO + inscode(d, ds, de, "WSAGETASYNCERRO", WSAGETASYNCERRO, "Error WSAGETASYNCERRO"); +#endif +#ifdef WSAMAKESELECTREPL + inscode(d, ds, de, "WSAMAKESELECTREPL", WSAMAKESELECTREPL, "Error WSAMAKESELECTREPL"); +#endif +#ifdef WSAGETASYNCBUFLE + inscode(d, ds, de, "WSAGETASYNCBUFLE", WSAGETASYNCBUFLE, "Error WSAGETASYNCBUFLE"); +#endif +#ifdef WSAEDESTADDRREQ + inscode(d, ds, de, "WSAEDESTADDRREQ", WSAEDESTADDRREQ, "Destination address required"); +#endif +#ifdef WSAECONNREFUSED + inscode(d, ds, de, "WSAECONNREFUSED", WSAECONNREFUSED, "Connection refused"); +#endif +#ifdef WSAENETRESET + inscode(d, ds, de, "WSAENETRESET", WSAENETRESET, "Network dropped connection because of reset"); +#endif +#ifdef WSAN + inscode(d, ds, de, "WSAN", WSAN, "Error WSAN"); +#endif + + Py_DECREF(de); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/Makefile.in b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/Makefile.in new file mode 100644 index 00000000..d6851844 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/Makefile.in @@ -0,0 +1,158 @@ +################################################################ +# Process this file with top-level configure script to produce Makefile +# +# Copyright 2000 Clark Cooper +# +# This file is part of EXPAT. +# +# EXPAT is free software; you can redistribute it and/or modify it +# under the terms of the License (based on the MIT/X license) contained +# in the file COPYING that comes with this distribution. +# +# EXPAT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN EXPAT. +# + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +subdir = lib + +top_builddir = .. + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_DATA = @INSTALL_DATA@ + +host_alias = @host_alias@ +host_triplet = @host@ +AS = @AS@ +CC = @CC@ +DLLTOOL = @DLLTOOL@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +OBJDUMP = @OBJDUMP@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +LIBRARY = libexpat.la +SOURCES = xmlparse.c xmltok.c xmlrole.c +OBJECTS = $(SOURCES:.c=.o) +LTOBJECTS = $(SOURCES:.c=.lo) + +TEMPLATES = xmltok_impl.c xmltok_ns.c +APIHEADER = expat.h +HEADERS = ascii.h iasciitab.h utf8tab.h xmltok.h asciitab.h latin1tab.h \ + nametab.h xmldef.h xmlrole.h xmltok_impl.h + +mkinstalldirs = $(SHELL) $(top_srcdir)/conftools/mkinstalldirs +CONFIG_HEADER = ../config.h +CONFIG_CLEAN_FILES = + +INCLUDES = -I$(srcdir) -I. -I.. +DEFS = @DEFS@ -DPACKAGE='"$(PACKAGE)"' -DVERSION='"$(PACKAGE)_$(VERSION)"' + +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +CFLAGS = @CFLAGS@ + +LIBREVISION = @LIBREVISION@ +LIBCURRENT = @LIBCURRENT@ +LIBAGE = @LIBAGE@ + +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) -version-info $(LIBCURRENT):$(LIBREVISION):$(LIBAGE) $(CFLAGS) $(LDFLAGS) -o $@ +DIST_COMMON = Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(TEMPLATES) $(APIHEADER) $(HEADERS) + +TAR = gtar +GZIP_ENV = --best + +all: $(LIBRARY) + +.SUFFIXES: .c .lo .o +.PHONY: all clean distclean maintainer-clean + +.c.o: + $(COMPILE) -c $< + +.c.lo: + $(LTCOMPILE) -c $< + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +$(top_builddir)/config.status: $(top_builddir)/configure + cd $(top_builddir) && $(MAKE) config.status + +$(top_builddir)/config.h: $(top_builddir)/config.h.in + cd $(top_builddir) && $(MAKE) config.h + +clean: + rm -f $(LIBRARY) *.o *.lo *~ + rm -rf .libs _libs + +distclean: clean + rm -f Makefile + +maintainer-clean: distclean + +check: $(SUBDIRS) + @echo + @echo This package does not yet have a regression test. + @echo + +install: $(LIBRARY) $(APIHEADER) + $(mkinstalldirs) $(libdir) $(includedir) + $(LIBTOOL) --mode=install $(INSTALL) $(LIBRARY) $(libdir)/$(LIBRARY) + $(INSTALL_DATA) $(APIHEADER) $(includedir) + +uninstall: + $(LIBTOOL) --mode=uninstall rm -f $(libdir)/$(LIBRARY); + rm -f $(libdir)/$(APIHEADER) + +$(LIBRARY): $(LTOBJECTS) + $(LINK) -rpath $(libdir) $(LDFLAGS) $(LTOBJECTS) + +xmlparse.o \ +xmlparse.lo: xmlparse.c expat.h xmlrole.h xmltok.h $(top_builddir)/config.h + +xmlrole.o \ +xmlrole.lo: xmlrole.c ascii.h xmlrole.h $(top_builddir)/config.h + +xmltok.o \ +xmltok.lo: xmltok.c xmltok_impl.c xmltok_ns.c \ + ascii.h asciitab.h iasciitab.h latin1tab.h nametab.h utf8tab.h \ + xmltok.h xmltok_impl.h $(top_builddir)/config.h diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/ascii.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/ascii.h new file mode 100644 index 00000000..035cd39a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/ascii.h @@ -0,0 +1,85 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#define ASCII_A 0x41 +#define ASCII_B 0x42 +#define ASCII_C 0x43 +#define ASCII_D 0x44 +#define ASCII_E 0x45 +#define ASCII_F 0x46 +#define ASCII_G 0x47 +#define ASCII_H 0x48 +#define ASCII_I 0x49 +#define ASCII_J 0x4A +#define ASCII_K 0x4B +#define ASCII_L 0x4C +#define ASCII_M 0x4D +#define ASCII_N 0x4E +#define ASCII_O 0x4F +#define ASCII_P 0x50 +#define ASCII_Q 0x51 +#define ASCII_R 0x52 +#define ASCII_S 0x53 +#define ASCII_T 0x54 +#define ASCII_U 0x55 +#define ASCII_V 0x56 +#define ASCII_W 0x57 +#define ASCII_X 0x58 +#define ASCII_Y 0x59 +#define ASCII_Z 0x5A + +#define ASCII_a 0x61 +#define ASCII_b 0x62 +#define ASCII_c 0x63 +#define ASCII_d 0x64 +#define ASCII_e 0x65 +#define ASCII_f 0x66 +#define ASCII_g 0x67 +#define ASCII_h 0x68 +#define ASCII_i 0x69 +#define ASCII_j 0x6A +#define ASCII_k 0x6B +#define ASCII_l 0x6C +#define ASCII_m 0x6D +#define ASCII_n 0x6E +#define ASCII_o 0x6F +#define ASCII_p 0x70 +#define ASCII_q 0x71 +#define ASCII_r 0x72 +#define ASCII_s 0x73 +#define ASCII_t 0x74 +#define ASCII_u 0x75 +#define ASCII_v 0x76 +#define ASCII_w 0x77 +#define ASCII_x 0x78 +#define ASCII_y 0x79 +#define ASCII_z 0x7A + +#define ASCII_0 0x30 +#define ASCII_1 0x31 +#define ASCII_2 0x32 +#define ASCII_3 0x33 +#define ASCII_4 0x34 +#define ASCII_5 0x35 +#define ASCII_6 0x36 +#define ASCII_7 0x37 +#define ASCII_8 0x38 +#define ASCII_9 0x39 + +#define ASCII_TAB 0x09 +#define ASCII_SPACE 0x20 +#define ASCII_EXCL 0x21 +#define ASCII_QUOT 0x22 +#define ASCII_AMP 0x26 +#define ASCII_APOS 0x27 +#define ASCII_MINUS 0x2D +#define ASCII_PERIOD 0x2E +#define ASCII_COLON 0x3A +#define ASCII_SEMI 0x3B +#define ASCII_LT 0x3C +#define ASCII_EQUALS 0x3D +#define ASCII_GT 0x3E +#define ASCII_LSQB 0x5B +#define ASCII_RSQB 0x5D +#define ASCII_UNDERSCORE 0x5F diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/asciitab.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/asciitab.h new file mode 100644 index 00000000..2ef65c79 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/asciitab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/expat.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/expat.h new file mode 100644 index 00000000..4e8f32e6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/expat.h @@ -0,0 +1,1001 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlParse_INCLUDED +#define XmlParse_INCLUDED 1 + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XML_SetProcessingInstructionHandler XML_SetProcessingInstrHandler +#define XML_SetUnparsedEntityDeclHandler XML_SetUnparsedEntDeclHandler +#define XML_SetStartNamespaceDeclHandler XML_SetStartNamespcDeclHandler +#define XML_SetExternalEntityRefHandlerArg XML_SetExternalEntRefHandlerArg +#endif + +#include + +#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__) +#define XML_USE_MSC_EXTENSIONS 1 +#endif + +/* Expat tries very hard to make the API boundary very specifically + defined. There are two macros defined to control this boundary; + each of these can be defined before including this header to + achieve some different behavior, but doing so it not recommended or + tested frequently. + + XMLCALL - The calling convention to use for all calls across the + "library boundary." This will default to cdecl, and + try really hard to tell the compiler that's what we + want. + + XMLIMPORT - Whatever magic is needed to note that a function is + to be imported from a dynamically loaded library + (.dll, .so, or .sl, depending on your platform). + + The XMLCALL macro was added in Expat 1.95.7. The only one which is + expected to be directly useful in client code is XMLCALL. + + Note that on at least some Unix versions, the Expat library must be + compiled with the cdecl calling convention as the default since + system headers may assume the cdecl convention. +*/ +#ifndef XMLCALL +#if defined(XML_USE_MSC_EXTENSIONS) +#define XMLCALL __cdecl +#elif defined(__GNUC__) +#define XMLCALL __attribute__((cdecl)) +#else +/* For any platform which uses this definition and supports more than + one calling convention, we need to extend this definition to + declare the convention used on that platform, if it's possible to + do so. + + If this is the case for your platform, please file a bug report + with information on how to identify your platform via the C + pre-processor and how to specify the same calling convention as the + platform's malloc() implementation. +*/ +#define XMLCALL +#endif +#endif /* not defined XMLCALL */ + + +#if !defined(XML_STATIC) && !defined(XMLIMPORT) +#ifndef XML_BUILDING_EXPAT +/* using Expat from an application */ + +#ifdef XML_USE_MSC_EXTENSIONS +#define XMLIMPORT __declspec(dllimport) +#endif + +#endif +#endif /* not defined XML_STATIC */ + +/* If we didn't define it above, define it away: */ +#ifndef XMLIMPORT +#define XMLIMPORT +#endif + + +#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef XML_UNICODE_WCHAR_T +#define XML_UNICODE +#endif + +struct XML_ParserStruct; +typedef struct XML_ParserStruct *XML_Parser; + +#ifdef XML_UNICODE /* Information is UTF-16 encoded. */ +#ifdef XML_UNICODE_WCHAR_T +typedef wchar_t XML_Char; +typedef wchar_t XML_LChar; +#else +typedef unsigned short XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE_WCHAR_T */ +#else /* Information is UTF-8 encoded. */ +typedef char XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE */ + +/* Should this be defined using stdbool.h when C99 is available? */ +typedef unsigned char XML_Bool; +#define XML_TRUE ((XML_Bool) 1) +#define XML_FALSE ((XML_Bool) 0) + +/* The XML_Status enum gives the possible return values for several + API functions. The preprocessor #defines are included so this + stanza can be added to code that still needs to support older + versions of Expat 1.95.x: + + #ifndef XML_STATUS_OK + #define XML_STATUS_OK 1 + #define XML_STATUS_ERROR 0 + #endif + + Otherwise, the #define hackery is quite ugly and would have been + dropped. +*/ +enum XML_Status { + XML_STATUS_ERROR = 0, +#define XML_STATUS_ERROR XML_STATUS_ERROR + XML_STATUS_OK = 1 +#define XML_STATUS_OK XML_STATUS_OK +}; + +enum XML_Error { + XML_ERROR_NONE, + XML_ERROR_NO_MEMORY, + XML_ERROR_SYNTAX, + XML_ERROR_NO_ELEMENTS, + XML_ERROR_INVALID_TOKEN, + XML_ERROR_UNCLOSED_TOKEN, + XML_ERROR_PARTIAL_CHAR, + XML_ERROR_TAG_MISMATCH, + XML_ERROR_DUPLICATE_ATTRIBUTE, + XML_ERROR_JUNK_AFTER_DOC_ELEMENT, + XML_ERROR_PARAM_ENTITY_REF, + XML_ERROR_UNDEFINED_ENTITY, + XML_ERROR_RECURSIVE_ENTITY_REF, + XML_ERROR_ASYNC_ENTITY, + XML_ERROR_BAD_CHAR_REF, + XML_ERROR_BINARY_ENTITY_REF, + XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, + XML_ERROR_MISPLACED_XML_PI, + XML_ERROR_UNKNOWN_ENCODING, + XML_ERROR_INCORRECT_ENCODING, + XML_ERROR_UNCLOSED_CDATA_SECTION, + XML_ERROR_EXTERNAL_ENTITY_HANDLING, + XML_ERROR_NOT_STANDALONE, + XML_ERROR_UNEXPECTED_STATE, + XML_ERROR_ENTITY_DECLARED_IN_PE, + XML_ERROR_FEATURE_REQUIRES_XML_DTD, + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING, + XML_ERROR_UNBOUND_PREFIX +}; + +enum XML_Content_Type { + XML_CTYPE_EMPTY = 1, + XML_CTYPE_ANY, + XML_CTYPE_MIXED, + XML_CTYPE_NAME, + XML_CTYPE_CHOICE, + XML_CTYPE_SEQ +}; + +enum XML_Content_Quant { + XML_CQUANT_NONE, + XML_CQUANT_OPT, + XML_CQUANT_REP, + XML_CQUANT_PLUS +}; + +/* If type == XML_CTYPE_EMPTY or XML_CTYPE_ANY, then quant will be + XML_CQUANT_NONE, and the other fields will be zero or NULL. + If type == XML_CTYPE_MIXED, then quant will be NONE or REP and + numchildren will contain number of elements that may be mixed in + and children point to an array of XML_Content cells that will be + all of XML_CTYPE_NAME type with no quantification. + + If type == XML_CTYPE_NAME, then the name points to the name, and + the numchildren field will be zero and children will be NULL. The + quant fields indicates any quantifiers placed on the name. + + CHOICE and SEQ will have name NULL, the number of children in + numchildren and children will point, recursively, to an array + of XML_Content cells. + + The EMPTY, ANY, and MIXED types will only occur at top level. +*/ + +typedef struct XML_cp XML_Content; + +struct XML_cp { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + XML_Char * name; + unsigned int numchildren; + XML_Content * children; +}; + + +/* This is called for an element declaration. See above for + description of the model argument. It's the caller's responsibility + to free model when finished with it. +*/ +typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData, + const XML_Char *name, + XML_Content *model); + +XMLPARSEAPI(void) +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl); + +/* The Attlist declaration handler is called for *each* attribute. So + a single Attlist declaration with multiple attributes declared will + generate multiple calls to this handler. The "default" parameter + may be NULL in the case of the "#IMPLIED" or "#REQUIRED" + keyword. The "isrequired" parameter will be true and the default + value will be NULL in the case of "#REQUIRED". If "isrequired" is + true and default is non-NULL, then this is a "#FIXED" default. +*/ +typedef void (XMLCALL *XML_AttlistDeclHandler) ( + void *userData, + const XML_Char *elname, + const XML_Char *attname, + const XML_Char *att_type, + const XML_Char *dflt, + int isrequired); + +XMLPARSEAPI(void) +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl); + +/* The XML declaration handler is called for *both* XML declarations + and text declarations. The way to distinguish is that the version + parameter will be NULL for text declarations. The encoding + parameter may be NULL for XML declarations. The standalone + parameter will be -1, 0, or 1 indicating respectively that there + was no standalone parameter in the declaration, that it was given + as no, or that it was given as yes. +*/ +typedef void (XMLCALL *XML_XmlDeclHandler) (void *userData, + const XML_Char *version, + const XML_Char *encoding, + int standalone); + +XMLPARSEAPI(void) +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler xmldecl); + + +typedef struct { + void *(XMLCALL *malloc_fcn)(size_t size); + void *(XMLCALL *realloc_fcn)(void *ptr, size_t size); + void (XMLCALL *free_fcn)(void *ptr); +} XML_Memory_Handling_Suite; + +/* Constructs a new parser; encoding is the encoding specified by the + external protocol or NULL if there is none specified. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate(const XML_Char *encoding); + +/* Constructs a new parser and namespace processor. Element type + names and attribute names that belong to a namespace will be + expanded; unprefixed attribute names are never expanded; unprefixed + element type names are expanded only if there is a default + namespace. The expanded name is the concatenation of the namespace + URI, the namespace separator character, and the local part of the + name. If the namespace separator is '\0' then the namespace URI + and the local part will be concatenated without any separator. + When a namespace is not declared, the name and prefix will be + passed through without expansion. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); + + +/* Constructs a new parser using the memory management suite referred to + by memsuite. If memsuite is NULL, then use the standard library memory + suite. If namespaceSeparator is non-NULL it creates a parser with + namespace processing as described above. The character pointed at + will serve as the namespace separator. + + All further memory operations used for the created parser will come from + the given suite. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate_MM(const XML_Char *encoding, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *namespaceSeparator); + +/* Prepare a parser object to be re-used. This is particularly + valuable when memory allocation overhead is disproportionatly high, + such as when a large number of small documnents need to be parsed. + All handlers are cleared from the parser, except for the + unknownEncodingHandler. The parser's external state is re-initialized + except for the values of ns and ns_triplets. + + Added in Expat 1.95.3. +*/ +XMLPARSEAPI(XML_Bool) +XML_ParserReset(XML_Parser parser, const XML_Char *encoding); + +/* atts is array of name/value pairs, terminated by 0; + names and values are 0 terminated. +*/ +typedef void (XMLCALL *XML_StartElementHandler) (void *userData, + const XML_Char *name, + const XML_Char **atts); + +typedef void (XMLCALL *XML_EndElementHandler) (void *userData, + const XML_Char *name); + + +/* s is not 0 terminated. */ +typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData, + const XML_Char *s, + int len); + +/* target and data are 0 terminated */ +typedef void (XMLCALL *XML_ProcessingInstructionHandler) ( + void *userData, + const XML_Char *target, + const XML_Char *data); + +/* data is 0 terminated */ +typedef void (XMLCALL *XML_CommentHandler) (void *userData, + const XML_Char *data); + +typedef void (XMLCALL *XML_StartCdataSectionHandler) (void *userData); +typedef void (XMLCALL *XML_EndCdataSectionHandler) (void *userData); + +/* This is called for any characters in the XML document for which + there is no applicable handler. This includes both characters that + are part of markup which is of a kind that is not reported + (comments, markup declarations), or characters that are part of a + construct which could be reported but for which no handler has been + supplied. The characters are passed exactly as they were in the XML + document except that they will be encoded in UTF-8 or UTF-16. + Line boundaries are not normalized. Note that a byte order mark + character is not passed to the default handler. There are no + guarantees about how characters are divided between calls to the + default handler: for example, a comment might be split between + multiple calls. +*/ +typedef void (XMLCALL *XML_DefaultHandler) (void *userData, + const XML_Char *s, + int len); + +/* This is called for the start of the DOCTYPE declaration, before + any DTD or internal subset is parsed. +*/ +typedef void (XMLCALL *XML_StartDoctypeDeclHandler) ( + void *userData, + const XML_Char *doctypeName, + const XML_Char *sysid, + const XML_Char *pubid, + int has_internal_subset); + +/* This is called for the start of the DOCTYPE declaration when the + closing > is encountered, but after processing any external + subset. +*/ +typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData); + +/* This is called for entity declarations. The is_parameter_entity + argument will be non-zero if the entity is a parameter entity, zero + otherwise. + + For internal entities (), value will + be non-NULL and systemId, publicID, and notationName will be NULL. + The value string is NOT nul-terminated; the length is provided in + the value_length argument. Since it is legal to have zero-length + values, do not use this argument to test for internal entities. + + For external entities, value will be NULL and systemId will be + non-NULL. The publicId argument will be NULL unless a public + identifier was provided. The notationName argument will have a + non-NULL value only for unparsed entity declarations. + + Note that is_parameter_entity can't be changed to XML_Bool, since + that would break binary compatibility. +*/ +typedef void (XMLCALL *XML_EntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity, + const XML_Char *value, + int value_length, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +XMLPARSEAPI(void) +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler); + +/* OBSOLETE -- OBSOLETE -- OBSOLETE + This handler has been superceded by the EntityDeclHandler above. + It is provided here for backward compatibility. + + This is called for a declaration of an unparsed (NDATA) entity. + The base argument is whatever was set by XML_SetBase. The + entityName, systemId and notationName arguments will never be + NULL. The other arguments may be. +*/ +typedef void (XMLCALL *XML_UnparsedEntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +/* This is called for a declaration of notation. The base argument is + whatever was set by XML_SetBase. The notationName will never be + NULL. The other arguments can be. +*/ +typedef void (XMLCALL *XML_NotationDeclHandler) ( + void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* When namespace processing is enabled, these are called once for + each namespace declaration. The call to the start and end element + handlers occur between the calls to the start and end namespace + declaration handlers. For an xmlns attribute, prefix will be + NULL. For an xmlns="" attribute, uri will be NULL. +*/ +typedef void (XMLCALL *XML_StartNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix, + const XML_Char *uri); + +typedef void (XMLCALL *XML_EndNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix); + +/* This is called if the document is not standalone, that is, it has an + external subset or a reference to a parameter entity, but does not + have standalone="yes". If this handler returns XML_STATUS_ERROR, + then processing will not continue, and the parser will return a + XML_ERROR_NOT_STANDALONE error. + If parameter entity parsing is enabled, then in addition to the + conditions above this handler will only be called if the referenced + entity was actually read. +*/ +typedef int (XMLCALL *XML_NotStandaloneHandler) (void *userData); + +/* This is called for a reference to an external parsed general + entity. The referenced entity is not automatically parsed. The + application can parse it immediately or later using + XML_ExternalEntityParserCreate. + + The parser argument is the parser parsing the entity containing the + reference; it can be passed as the parser argument to + XML_ExternalEntityParserCreate. The systemId argument is the + system identifier as specified in the entity declaration; it will + not be NULL. + + The base argument is the system identifier that should be used as + the base for resolving systemId if systemId was relative; this is + set by XML_SetBase; it may be NULL. + + The publicId argument is the public identifier as specified in the + entity declaration, or NULL if none was specified; the whitespace + in the public identifier will have been normalized as required by + the XML spec. + + The context argument specifies the parsing context in the format + expected by the context argument to XML_ExternalEntityParserCreate; + context is valid only until the handler returns, so if the + referenced entity is to be parsed later, it must be copied. + context is NULL only when the entity is a parameter entity. + + The handler should return XML_STATUS_ERROR if processing should not + continue because of a fatal error in the handling of the external + entity. In this case the calling parser will return an + XML_ERROR_EXTERNAL_ENTITY_HANDLING error. + + Note that unlike other handlers the first argument is the parser, + not userData. +*/ +typedef int (XMLCALL *XML_ExternalEntityRefHandler) ( + XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* This is called in two situations: + 1) An entity reference is encountered for which no declaration + has been read *and* this is not an error. + 2) An internal entity reference is read, but not expanded, because + XML_SetDefaultHandler has been called. + Note: skipped parameter entities in declarations and skipped general + entities in attribute values cannot be reported, because + the event would be out of sync with the reporting of the + declarations or attribute values +*/ +typedef void (XMLCALL *XML_SkippedEntityHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity); + +/* This structure is filled in by the XML_UnknownEncodingHandler to + provide information to the parser about encodings that are unknown + to the parser. + + The map[b] member gives information about byte sequences whose + first byte is b. + + If map[b] is c where c is >= 0, then b by itself encodes the + Unicode scalar value c. + + If map[b] is -1, then the byte sequence is malformed. + + If map[b] is -n, where n >= 2, then b is the first byte of an + n-byte sequence that encodes a single Unicode scalar value. + + The data member will be passed as the first argument to the convert + function. + + The convert function is used to convert multibyte sequences; s will + point to a n-byte sequence where map[(unsigned char)*s] == -n. The + convert function must return the Unicode scalar value represented + by this byte sequence or -1 if the byte sequence is malformed. + + The convert function may be NULL if the encoding is a single-byte + encoding, that is if map[b] >= -1 for all bytes b. + + When the parser is finished with the encoding, then if release is + not NULL, it will call release passing it the data member; once + release has been called, the convert function will not be called + again. + + Expat places certain restrictions on the encodings that are supported + using this mechanism. + + 1. Every ASCII character that can appear in a well-formed XML document, + other than the characters + + $@\^`{}~ + + must be represented by a single byte, and that byte must be the + same byte that represents that character in ASCII. + + 2. No character may require more than 4 bytes to encode. + + 3. All characters encoded must have Unicode scalar values <= + 0xFFFF, (i.e., characters that would be encoded by surrogates in + UTF-16 are not allowed). Note that this restriction doesn't + apply to the built-in support for UTF-8 and UTF-16. + + 4. No Unicode character may be encoded by more than one distinct + sequence of bytes. +*/ +typedef struct { + int map[256]; + void *data; + int (XMLCALL *convert)(void *data, const char *s); + void (XMLCALL *release)(void *data); +} XML_Encoding; + +/* This is called for an encoding that is unknown to the parser. + + The encodingHandlerData argument is that which was passed as the + second argument to XML_SetUnknownEncodingHandler. + + The name argument gives the name of the encoding as specified in + the encoding declaration. + + If the callback can provide information about the encoding, it must + fill in the XML_Encoding structure, and return XML_STATUS_OK. + Otherwise it must return XML_STATUS_ERROR. + + If info does not describe a suitable encoding, then the parser will + return an XML_UNKNOWN_ENCODING error. +*/ +typedef int (XMLCALL *XML_UnknownEncodingHandler) ( + void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info); + +XMLPARSEAPI(void) +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end); + +XMLPARSEAPI(void) +XML_SetStartElementHandler(XML_Parser, XML_StartElementHandler); + +XMLPARSEAPI(void) +XML_SetEndElementHandler(XML_Parser, XML_EndElementHandler); + +XMLPARSEAPI(void) +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler); + +XMLPARSEAPI(void) +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler); +XMLPARSEAPI(void) +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler); + +XMLPARSEAPI(void) +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end); + +XMLPARSEAPI(void) +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start); + +XMLPARSEAPI(void) +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end); + +/* This sets the default handler and also inhibits expansion of + internal entities. These entity references will be passed to the + default handler, or to the skipped entity handler, if one is set. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler); + +/* This sets the default handler but does not inhibit expansion of + internal entities. The entity reference will not be passed to the + default handler. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler); + +XMLPARSEAPI(void) +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler); + +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler); + +/* If a non-NULL value for arg is specified here, then it will be + passed as the first argument to the external entity ref handler + instead of the parser object. +*/ +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandlerArg(XML_Parser, void *arg); + +XMLPARSEAPI(void) +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler); + +XMLPARSEAPI(void) +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *encodingHandlerData); + +/* This can be called within a handler for a start element, end + element, processing instruction or character data. It causes the + corresponding markup to be passed to the default handler. +*/ +XMLPARSEAPI(void) +XML_DefaultCurrent(XML_Parser parser); + +/* If do_nst is non-zero, and namespace processing is in effect, and + a name has a prefix (i.e. an explicit namespace qualifier) then + that name is returned as a triplet in a single string separated by + the separator character specified when the parser was created: URI + + sep + local_name + sep + prefix. + + If do_nst is zero, then namespace information is returned in the + default manner (URI + sep + local_name) whether or not the name + has a prefix. + + Note: Calling XML_SetReturnNSTriplet after XML_Parse or + XML_ParseBuffer has no effect. +*/ + +XMLPARSEAPI(void) +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst); + +/* This value is passed as the userData argument to callbacks. */ +XMLPARSEAPI(void) +XML_SetUserData(XML_Parser parser, void *userData); + +/* Returns the last value set by XML_SetUserData or NULL. */ +#define XML_GetUserData(parser) (*(void **)(parser)) + +/* This is equivalent to supplying an encoding argument to + XML_ParserCreate. On success XML_SetEncoding returns non-zero, + zero otherwise. + Note: Calling XML_SetEncoding after XML_Parse or XML_ParseBuffer + has no effect and returns XML_STATUS_ERROR. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetEncoding(XML_Parser parser, const XML_Char *encoding); + +/* If this function is called, then the parser will be passed as the + first argument to callbacks instead of userData. The userData will + still be accessible using XML_GetUserData. +*/ +XMLPARSEAPI(void) +XML_UseParserAsHandlerArg(XML_Parser parser); + +/* If useDTD == XML_TRUE is passed to this function, then the parser + will assume that there is an external subset, even if none is + specified in the document. In such a case the parser will call the + externalEntityRefHandler with a value of NULL for the systemId + argument (the publicId and context arguments will be NULL as well). + Note: If this function is called, then this must be done before + the first call to XML_Parse or XML_ParseBuffer, since it will + have no effect after that. Returns + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING. + Note: If the document does not have a DOCTYPE declaration at all, + then startDoctypeDeclHandler and endDoctypeDeclHandler will not + be called, despite an external subset being parsed. + Note: If XML_DTD is not defined when Expat is compiled, returns + XML_ERROR_FEATURE_REQUIRES_XML_DTD. +*/ +XMLPARSEAPI(enum XML_Error) +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD); + + +/* Sets the base to be used for resolving relative URIs in system + identifiers in declarations. Resolving relative identifiers is + left to the application: this value will be passed through as the + base argument to the XML_ExternalEntityRefHandler, + XML_NotationDeclHandler and XML_UnparsedEntityDeclHandler. The base + argument will be copied. Returns XML_STATUS_ERROR if out of memory, + XML_STATUS_OK otherwise. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetBase(XML_Parser parser, const XML_Char *base); + +XMLPARSEAPI(const XML_Char *) +XML_GetBase(XML_Parser parser); + +/* Returns the number of the attribute/value pairs passed in last call + to the XML_StartElementHandler that were specified in the start-tag + rather than defaulted. Each attribute/value pair counts as 2; thus + this correspondds to an index into the atts array passed to the + XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetSpecifiedAttributeCount(XML_Parser parser); + +/* Returns the index of the ID attribute passed in the last call to + XML_StartElementHandler, or -1 if there is no ID attribute. Each + attribute/value pair counts as 2; thus this correspondds to an + index into the atts array passed to the XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetIdAttributeIndex(XML_Parser parser); + +/* Parses some input. Returns XML_STATUS_ERROR if a fatal error is + detected. The last call to XML_Parse must have isFinal true; len + may be zero for this call (or any other). + + Though the return values for these functions has always been + described as a Boolean value, the implementation, at least for the + 1.95.x series, has always returned exactly one of the XML_Status + values. +*/ +XMLPARSEAPI(enum XML_Status) +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal); + +XMLPARSEAPI(void *) +XML_GetBuffer(XML_Parser parser, int len); + +XMLPARSEAPI(enum XML_Status) +XML_ParseBuffer(XML_Parser parser, int len, int isFinal); + +/* Creates an XML_Parser object that can parse an external general + entity; context is a '\0'-terminated string specifying the parse + context; encoding is a '\0'-terminated string giving the name of + the externally specified encoding, or NULL if there is no + externally specified encoding. The context string consists of a + sequence of tokens separated by formfeeds (\f); a token consisting + of a name specifies that the general entity of the name is open; a + token of the form prefix=uri specifies the namespace for a + particular prefix; a token of the form =uri specifies the default + namespace. This can be called at any point after the first call to + an ExternalEntityRefHandler so longer as the parser has not yet + been freed. The new parser is completely independent and may + safely be used in a separate thread. The handlers and userData are + initialized from the parser argument. Returns NULL if out of memory. + Otherwise returns a new XML_Parser object. +*/ +XMLPARSEAPI(XML_Parser) +XML_ExternalEntityParserCreate(XML_Parser parser, + const XML_Char *context, + const XML_Char *encoding); + +enum XML_ParamEntityParsing { + XML_PARAM_ENTITY_PARSING_NEVER, + XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE, + XML_PARAM_ENTITY_PARSING_ALWAYS +}; + +/* Controls parsing of parameter entities (including the external DTD + subset). If parsing of parameter entities is enabled, then + references to external parameter entities (including the external + DTD subset) will be passed to the handler set with + XML_SetExternalEntityRefHandler. The context passed will be 0. + + Unlike external general entities, external parameter entities can + only be parsed synchronously. If the external parameter entity is + to be parsed, it must be parsed during the call to the external + entity ref handler: the complete sequence of + XML_ExternalEntityParserCreate, XML_Parse/XML_ParseBuffer and + XML_ParserFree calls must be made during this call. After + XML_ExternalEntityParserCreate has been called to create the parser + for the external parameter entity (context must be 0 for this + call), it is illegal to make any calls on the old parser until + XML_ParserFree has been called on the newly created parser. + If the library has been compiled without support for parameter + entity parsing (ie without XML_DTD being defined), then + XML_SetParamEntityParsing will return 0 if parsing of parameter + entities is requested; otherwise it will return non-zero. + Note: If XML_SetParamEntityParsing is called after XML_Parse or + XML_ParseBuffer, then it has no effect and will always return 0. +*/ +XMLPARSEAPI(int) +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing parsing); + +/* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then + XML_GetErrorCode returns information about the error. +*/ +XMLPARSEAPI(enum XML_Error) +XML_GetErrorCode(XML_Parser parser); + +/* These functions return information about the current parse + location. They may be called from any callback called to report + some parse event; in this case the location is the location of the + first of the sequence of characters that generated the event. When + called from callbacks generated by declarations in the document + prologue, the location identified isn't as neatly defined, but will + be within the relevant markup. When called outside of the callback + functions, the position indicated will be just past the last parse + event (regardless of whether there was an associated callback). + + They may also be called after returning from a call to XML_Parse + or XML_ParseBuffer. If the return value is XML_STATUS_ERROR then + the location is the location of the character at which the error + was detected; otherwise the location is the location of the last + parse event, as described above. +*/ +XMLPARSEAPI(int) XML_GetCurrentLineNumber(XML_Parser parser); +XMLPARSEAPI(int) XML_GetCurrentColumnNumber(XML_Parser parser); +XMLPARSEAPI(long) XML_GetCurrentByteIndex(XML_Parser parser); + +/* Return the number of bytes in the current event. + Returns 0 if the event is in an internal entity. +*/ +XMLPARSEAPI(int) +XML_GetCurrentByteCount(XML_Parser parser); + +/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets + the integer pointed to by offset to the offset within this buffer + of the current parse position, and sets the integer pointed to by size + to the size of this buffer (the number of input bytes). Otherwise + returns a NULL pointer. Also returns a NULL pointer if a parse isn't + active. + + NOTE: The character pointer returned should not be used outside + the handler that makes the call. +*/ +XMLPARSEAPI(const char *) +XML_GetInputContext(XML_Parser parser, + int *offset, + int *size); + +/* For backwards compatibility with previous versions. */ +#define XML_GetErrorLineNumber XML_GetCurrentLineNumber +#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber +#define XML_GetErrorByteIndex XML_GetCurrentByteIndex + +/* Frees the content model passed to the element declaration handler */ +XMLPARSEAPI(void) +XML_FreeContentModel(XML_Parser parser, XML_Content *model); + +/* Exposing the memory handling functions used in Expat */ +XMLPARSEAPI(void *) +XML_MemMalloc(XML_Parser parser, size_t size); + +XMLPARSEAPI(void *) +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size); + +XMLPARSEAPI(void) +XML_MemFree(XML_Parser parser, void *ptr); + +/* Frees memory used by the parser. */ +XMLPARSEAPI(void) +XML_ParserFree(XML_Parser parser); + +/* Returns a string describing the error. */ +XMLPARSEAPI(const XML_LChar *) +XML_ErrorString(enum XML_Error code); + +/* Return a string containing the version number of this expat */ +XMLPARSEAPI(const XML_LChar *) +XML_ExpatVersion(void); + +typedef struct { + int major; + int minor; + int micro; +} XML_Expat_Version; + +/* Return an XML_Expat_Version structure containing numeric version + number information for this version of expat. +*/ +XMLPARSEAPI(XML_Expat_Version) +XML_ExpatVersionInfo(void); + +/* Added in Expat 1.95.5. */ +enum XML_FeatureEnum { + XML_FEATURE_END = 0, + XML_FEATURE_UNICODE, + XML_FEATURE_UNICODE_WCHAR_T, + XML_FEATURE_DTD, + XML_FEATURE_CONTEXT_BYTES, + XML_FEATURE_MIN_SIZE, + XML_FEATURE_SIZEOF_XML_CHAR, + XML_FEATURE_SIZEOF_XML_LCHAR + /* Additional features must be added to the end of this enum. */ +}; + +typedef struct { + enum XML_FeatureEnum feature; + const XML_LChar *name; + long int value; +} XML_Feature; + +XMLPARSEAPI(const XML_Feature *) +XML_GetFeatureList(void); + + +/* Expat follows the GNU/Linux convention of odd number minor version for + beta/development releases and even number minor version for stable + releases. Micro is bumped with each release, and set to 0 with each + change to major or minor version. +*/ +#define XML_MAJOR_VERSION 1 +#define XML_MINOR_VERSION 95 +#define XML_MICRO_VERSION 7 + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlParse_INCLUDED */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/iasciitab.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/iasciitab.h new file mode 100644 index 00000000..980686f1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/iasciitab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/internal.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/internal.h new file mode 100644 index 00000000..09dbaf46 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/internal.h @@ -0,0 +1,73 @@ +/* internal.h + + Internal definitions used by Expat. This is not needed to compile + client code. + + The following calling convention macros are defined for frequently + called functions: + + FASTCALL - Used for those internal functions that have a simple + body and a low number of arguments and local variables. + + PTRCALL - Used for functions called though function pointers. + + PTRFASTCALL - Like PTRCALL, but for low number of arguments. + + inline - Used for selected internal functions for which inlining + may improve performance on some platforms. + + Note: Use of these macros is based on judgement, not hard rules, + and therefore subject to change. +*/ + +#if defined(__GNUC__) && defined(__i386__) +/* We'll use this version by default only where we know it helps. + + regparm() generates warnings on Solaris boxes. See SF bug #692878. + + Instability reported with egcs on a RedHat Linux 7.3. + Let's comment out: + #define FASTCALL __attribute__((stdcall, regparm(3))) + and let's try this: +*/ +#define FASTCALL __attribute__((regparm(3))) +#define PTRFASTCALL __attribute__((regparm(3))) +#endif + +/* Using __fastcall seems to have an unexpected negative effect under + MS VC++, especially for function pointers, so we won't use it for + now on that platform. It may be reconsidered for a future release + if it can be made more effective. + Likely reason: __fastcall on Windows is like stdcall, therefore + the compiler cannot perform stack optimizations for call clusters. +*/ + +/* Make sure all of these are defined if they aren't already. */ + +#ifndef FASTCALL +#define FASTCALL +#endif + +#ifndef PTRCALL +#define PTRCALL +#endif + +#ifndef PTRFASTCALL +#define PTRFASTCALL +#endif + +#ifndef XML_MIN_SIZE +#if !defined(__cplusplus) && !defined(inline) +#ifdef __GNUC__ +#define inline __inline +#endif /* __GNUC__ */ +#endif +#endif /* XML_MIN_SIZE */ + +#ifdef __cplusplus +#define inline inline +#else +#ifndef inline +#define inline +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/latin1tab.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/latin1tab.h new file mode 100644 index 00000000..0c5b1ca0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/latin1tab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, +/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/macconfig.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/macconfig.h new file mode 100644 index 00000000..5c44c25b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/macconfig.h @@ -0,0 +1,104 @@ +/*================================================================ +** Copyright 2000, Clark Cooper +** All rights reserved. +** +** This is free software. You are permitted to copy, distribute, or modify +** it under the terms of the MIT/X license (contained in the COPYING file +** with this distribution.) +** +*/ + +#ifndef MACCONFIG_H +#define MACCONFIG_H + + +/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ +#define BYTEORDER 4321 + +/* Define to 1 if you have the `bcopy' function. */ +#undef HAVE_BCOPY + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `getpagesize' function. */ +#undef HAVE_GETPAGESIZE + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS + +/* whether byteorder is bigendian */ +#define WORDS_BIGENDIAN + +/* Define to specify how much context to retain around the current parse + point. */ +#undef XML_CONTEXT_BYTES + +/* Define to make parameter entity parsing functionality available. */ +#define XML_DTD + +/* Define to make XML Namespaces functionality available. */ +#define XML_NS + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `long' if does not define. */ +#define off_t long + +/* Define to `unsigned' if does not define. */ +#undef size_t + + +#endif /* ifndef MACCONFIG_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/nametab.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/nametab.h new file mode 100644 index 00000000..adea34c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/nametab.h @@ -0,0 +1,150 @@ +static const unsigned namingBitmap[] = { +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, +0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, +0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, +0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, +0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, +0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, +0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, +0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003, +0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, +0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, +0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, +0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, +0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, +0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, +0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, +0x40000000, 0xF580C900, 0x00000007, 0x02010800, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, +0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, +0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, +0x00000000, 0x00004C40, 0x00000000, 0x00000000, +0x00000007, 0x00000000, 0x00000000, 0x00000000, +0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, +0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, +0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000, +0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, +0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, +0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, +0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, +0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, +0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, +0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, +0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, +0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80, +0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, +0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, +0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, +0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, +0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, +0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF, +}; +static const unsigned char nmstrtPages[] = { +0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, +0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char namePages[] = { +0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, +0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/utf8tab.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/utf8tab.h new file mode 100644 index 00000000..34196013 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/utf8tab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + + +/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, +/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/winconfig.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/winconfig.h new file mode 100644 index 00000000..922ba106 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/winconfig.h @@ -0,0 +1,30 @@ +/*================================================================ +** Copyright 2000, Clark Cooper +** All rights reserved. +** +** This is free software. You are permitted to copy, distribute, or modify +** it under the terms of the MIT/X license (contained in the COPYING file +** with this distribution.) +*/ + +#ifndef WINCONFIG_H +#define WINCONFIG_H + +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN + +#include +#include + +#define XML_NS 1 +#define XML_DTD 1 +#define XML_CONTEXT_BYTES 1024 + +/* we will assume all Windows platforms are little endian */ +#define BYTEORDER 1234 + +/* Windows has memmove() available. */ +#define HAVE_MEMMOVE + +#endif /* ndef WINCONFIG_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlparse.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlparse.c new file mode 100644 index 00000000..24cb8705 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlparse.c @@ -0,0 +1,5814 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include +#include /* memset(), memcpy() */ + +#define XML_BUILDING_EXPAT 1 + +#ifdef COMPILED_FROM_DSP +#include "winconfig.h" +#elif defined(MACOS_CLASSIC) +#include "macconfig.h" +#else +#ifdef HAVE_EXPAT_CONFIG_H +#include +#endif +#endif /* ndef COMPILED_FROM_DSP */ + +#include "expat.h" + +#ifdef XML_UNICODE +#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX +#define XmlConvert XmlUtf16Convert +#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS +#define XmlEncode XmlUtf16Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((unsigned long)s) & 1)) +typedef unsigned short ICHAR; +#else +#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX +#define XmlConvert XmlUtf8Convert +#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS +#define XmlEncode XmlUtf8Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) +typedef char ICHAR; +#endif + + +#ifndef XML_NS + +#define XmlInitEncodingNS XmlInitEncoding +#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding +#undef XmlGetInternalEncodingNS +#define XmlGetInternalEncodingNS XmlGetInternalEncoding +#define XmlParseXmlDeclNS XmlParseXmlDecl + +#endif + +#ifdef XML_UNICODE + +#ifdef XML_UNICODE_WCHAR_T +#define XML_T(x) (const wchar_t)x +#define XML_L(x) L ## x +#else +#define XML_T(x) (const unsigned short)x +#define XML_L(x) x +#endif + +#else + +#define XML_T(x) x +#define XML_L(x) x + +#endif + +/* Round up n to be a multiple of sz, where sz is a power of 2. */ +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) + +/* Handle the case where memmove() doesn't exist. */ +#ifndef HAVE_MEMMOVE +#ifdef HAVE_BCOPY +#define memmove(d,s,l) bcopy((s),(d),(l)) +#else +#error memmove does not exist on this platform, nor is a substitute available +#endif /* HAVE_BCOPY */ +#endif /* HAVE_MEMMOVE */ + +#include "internal.h" +#include "xmltok.h" +#include "xmlrole.h" + +typedef const XML_Char *KEY; + +typedef struct { + KEY name; +} NAMED; + +typedef struct { + NAMED **v; + unsigned char power; + size_t size; + size_t used; + const XML_Memory_Handling_Suite *mem; +} HASH_TABLE; + +/* Basic character hash algorithm, taken from Python's string hash: + h = h * 1000003 ^ character, the constant being a prime number. + +*/ +#ifdef XML_UNICODE +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned short)(c)) +#else +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned char)(c)) +#endif + +/* For probing (after a collision) we need a step size relative prime + to the hash table size, which is a power of 2. We use double-hashing, + since we can calculate a second hash value cheaply by taking those bits + of the first hash value that were discarded (masked out) when the table + index was calculated: index = hash & mask, where mask = table->size - 1. + We limit the maximum step size to table->size / 4 (mask >> 2) and make + it odd, since odd numbers are always relative prime to a power of 2. +*/ +#define SECOND_HASH(hash, mask, power) \ + ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) +#define PROBE_STEP(hash, mask, power) \ + ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) + +typedef struct { + NAMED **p; + NAMED **end; +} HASH_TABLE_ITER; + +#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ +#define INIT_DATA_BUF_SIZE 1024 +#define INIT_ATTS_SIZE 16 +#define INIT_ATTS_VERSION 0xFFFFFFFF +#define INIT_BLOCK_SIZE 1024 +#define INIT_BUFFER_SIZE 1024 + +#define EXPAND_SPARE 24 + +typedef struct binding { + struct prefix *prefix; + struct binding *nextTagBinding; + struct binding *prevPrefixBinding; + const struct attribute_id *attId; + XML_Char *uri; + int uriLen; + int uriAlloc; +} BINDING; + +typedef struct prefix { + const XML_Char *name; + BINDING *binding; +} PREFIX; + +typedef struct { + const XML_Char *str; + const XML_Char *localPart; + const XML_Char *prefix; + int strLen; + int uriLen; + int prefixLen; +} TAG_NAME; + +/* TAG represents an open element. + The name of the element is stored in both the document and API + encodings. The memory buffer 'buf' is a separately-allocated + memory area which stores the name. During the XML_Parse()/ + XMLParseBuffer() when the element is open, the memory for the 'raw' + version of the name (in the document encoding) is shared with the + document buffer. If the element is open across calls to + XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to + contain the 'raw' name as well. + + A parser re-uses these structures, maintaining a list of allocated + TAG objects in a free list. +*/ +typedef struct tag { + struct tag *parent; /* parent of this element */ + const char *rawName; /* tagName in the original encoding */ + int rawNameLength; + TAG_NAME name; /* tagName in the API encoding */ + char *buf; /* buffer for name components */ + char *bufEnd; /* end of the buffer */ + BINDING *bindings; +} TAG; + +typedef struct { + const XML_Char *name; + const XML_Char *textPtr; + int textLen; + const XML_Char *systemId; + const XML_Char *base; + const XML_Char *publicId; + const XML_Char *notation; + XML_Bool open; + XML_Bool is_param; + XML_Bool is_internal; /* true if declared in internal subset outside PE */ +} ENTITY; + +typedef struct { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + const XML_Char * name; + int firstchild; + int lastchild; + int childcnt; + int nextsib; +} CONTENT_SCAFFOLD; + +#define INIT_SCAFFOLD_ELEMENTS 32 + +typedef struct block { + struct block *next; + int size; + XML_Char s[1]; +} BLOCK; + +typedef struct { + BLOCK *blocks; + BLOCK *freeBlocks; + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; + const XML_Memory_Handling_Suite *mem; +} STRING_POOL; + +/* The XML_Char before the name is used to determine whether + an attribute has been specified. */ +typedef struct attribute_id { + XML_Char *name; + PREFIX *prefix; + XML_Bool maybeTokenized; + XML_Bool xmlns; +} ATTRIBUTE_ID; + +typedef struct { + const ATTRIBUTE_ID *id; + XML_Bool isCdata; + const XML_Char *value; +} DEFAULT_ATTRIBUTE; + +typedef struct { + unsigned long version; + unsigned long hash; + const XML_Char *uriName; +} NS_ATT; + +typedef struct { + const XML_Char *name; + PREFIX *prefix; + const ATTRIBUTE_ID *idAtt; + int nDefaultAtts; + int allocDefaultAtts; + DEFAULT_ATTRIBUTE *defaultAtts; +} ELEMENT_TYPE; + +typedef struct { + HASH_TABLE generalEntities; + HASH_TABLE elementTypes; + HASH_TABLE attributeIds; + HASH_TABLE prefixes; + STRING_POOL pool; + STRING_POOL entityValuePool; + /* false once a parameter entity reference has been skipped */ + XML_Bool keepProcessing; + /* true once an internal or external PE reference has been encountered; + this includes the reference to an external subset */ + XML_Bool hasParamEntityRefs; + XML_Bool standalone; +#ifdef XML_DTD + /* indicates if external PE has been read */ + XML_Bool paramEntityRead; + HASH_TABLE paramEntities; +#endif /* XML_DTD */ + PREFIX defaultPrefix; + /* === scaffolding for building content model === */ + XML_Bool in_eldecl; + CONTENT_SCAFFOLD *scaffold; + unsigned contentStringLen; + unsigned scaffSize; + unsigned scaffCount; + int scaffLevel; + int *scaffIndex; +} DTD; + +typedef struct open_internal_entity { + const char *internalEventPtr; + const char *internalEventEndPtr; + struct open_internal_entity *next; + ENTITY *entity; +} OPEN_INTERNAL_ENTITY; + +typedef enum XML_Error PTRCALL Processor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr); + +static Processor prologProcessor; +static Processor prologInitProcessor; +static Processor contentProcessor; +static Processor cdataSectionProcessor; +#ifdef XML_DTD +static Processor ignoreSectionProcessor; +static Processor externalParEntProcessor; +static Processor externalParEntInitProcessor; +static Processor entityValueProcessor; +static Processor entityValueInitProcessor; +#endif /* XML_DTD */ +static Processor epilogProcessor; +static Processor errorProcessor; +static Processor externalEntityInitProcessor; +static Processor externalEntityInitProcessor2; +static Processor externalEntityInitProcessor3; +static Processor externalEntityContentProcessor; + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName); +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *, const char *); +static enum XML_Error +initializeEncoding(XML_Parser parser); +static enum XML_Error +doProlog(XML_Parser parser, const ENCODING *enc, const char *s, + const char *end, int tok, const char *next, const char **nextPtr); +static enum XML_Error +processInternalParamEntity(XML_Parser parser, ENTITY *entity); +static enum XML_Error +doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + const char *start, const char *end, const char **endPtr); +static enum XML_Error +doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr); +#ifdef XML_DTD +static enum XML_Error +doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr); +#endif /* XML_DTD */ + +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *, const char *s, + TAG_NAME *tagNamePtr, BINDING **bindingsPtr); +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr); +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, + XML_Bool isCdata, XML_Bool isId, const XML_Char *dfltValue, + XML_Parser parser); +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); +static enum XML_Error +storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); +static int +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static void +reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); + +static const XML_Char * getContext(XML_Parser parser); +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context); + +static void FASTCALL normalizePublicId(XML_Char *s); + +static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms); +/* do not call if parentParser != NULL */ +static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms); +static int +dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms); +static int +copyEntityTable(HASH_TABLE *, STRING_POOL *, const HASH_TABLE *); + +static NAMED * +lookup(HASH_TABLE *table, KEY name, size_t createSize); +static void FASTCALL +hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL hashTableClear(HASH_TABLE *); +static void FASTCALL hashTableDestroy(HASH_TABLE *); +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); +static NAMED * FASTCALL hashTableIterNext(HASH_TABLE_ITER *); + +static void FASTCALL +poolInit(STRING_POOL *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL poolClear(STRING_POOL *); +static void FASTCALL poolDestroy(STRING_POOL *); +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s); + +static int FASTCALL nextScaffoldPart(XML_Parser parser); +static XML_Content * build_model(XML_Parser parser); +static ELEMENT_TYPE * +getElementType(XML_Parser parser, const ENCODING *enc, + const char *ptr, const char *end); + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd); +static void +parserInit(XML_Parser parser, const XML_Char *encodingName); + +#define poolStart(pool) ((pool)->start) +#define poolEnd(pool) ((pool)->ptr) +#define poolLength(pool) ((pool)->ptr - (pool)->start) +#define poolChop(pool) ((void)--(pool->ptr)) +#define poolLastChar(pool) (((pool)->ptr)[-1]) +#define poolDiscard(pool) ((pool)->ptr = (pool)->start) +#define poolFinish(pool) ((pool)->start = (pool)->ptr) +#define poolAppendChar(pool, c) \ + (((pool)->ptr == (pool)->end && !poolGrow(pool)) \ + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) + +struct XML_ParserStruct { + /* The first member must be userData so that the XML_GetUserData + macro works. */ + void *m_userData; + void *m_handlerArg; + char *m_buffer; + const XML_Memory_Handling_Suite m_mem; + /* first character to be parsed */ + const char *m_bufferPtr; + /* past last character to be parsed */ + char *m_bufferEnd; + /* allocated end of buffer */ + const char *m_bufferLim; + long m_parseEndByteIndex; + const char *m_parseEndPtr; + XML_Char *m_dataBuf; + XML_Char *m_dataBufEnd; + XML_StartElementHandler m_startElementHandler; + XML_EndElementHandler m_endElementHandler; + XML_CharacterDataHandler m_characterDataHandler; + XML_ProcessingInstructionHandler m_processingInstructionHandler; + XML_CommentHandler m_commentHandler; + XML_StartCdataSectionHandler m_startCdataSectionHandler; + XML_EndCdataSectionHandler m_endCdataSectionHandler; + XML_DefaultHandler m_defaultHandler; + XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler; + XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler; + XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; + XML_NotationDeclHandler m_notationDeclHandler; + XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; + XML_NotStandaloneHandler m_notStandaloneHandler; + XML_ExternalEntityRefHandler m_externalEntityRefHandler; + XML_Parser m_externalEntityRefHandlerArg; + XML_SkippedEntityHandler m_skippedEntityHandler; + XML_UnknownEncodingHandler m_unknownEncodingHandler; + XML_ElementDeclHandler m_elementDeclHandler; + XML_AttlistDeclHandler m_attlistDeclHandler; + XML_EntityDeclHandler m_entityDeclHandler; + XML_XmlDeclHandler m_xmlDeclHandler; + const ENCODING *m_encoding; + INIT_ENCODING m_initEncoding; + const ENCODING *m_internalEncoding; + const XML_Char *m_protocolEncodingName; + XML_Bool m_ns; + XML_Bool m_ns_triplets; + void *m_unknownEncodingMem; + void *m_unknownEncodingData; + void *m_unknownEncodingHandlerData; + void (*m_unknownEncodingRelease)(void *); + PROLOG_STATE m_prologState; + Processor *m_processor; + enum XML_Error m_errorCode; + const char *m_eventPtr; + const char *m_eventEndPtr; + const char *m_positionPtr; + OPEN_INTERNAL_ENTITY *m_openInternalEntities; + XML_Bool m_defaultExpandInternalEntities; + int m_tagLevel; + ENTITY *m_declEntity; + const XML_Char *m_doctypeName; + const XML_Char *m_doctypeSysid; + const XML_Char *m_doctypePubid; + const XML_Char *m_declAttributeType; + const XML_Char *m_declNotationName; + const XML_Char *m_declNotationPublicId; + ELEMENT_TYPE *m_declElementType; + ATTRIBUTE_ID *m_declAttributeId; + XML_Bool m_declAttributeIsCdata; + XML_Bool m_declAttributeIsId; + DTD *m_dtd; + const XML_Char *m_curBase; + TAG *m_tagStack; + TAG *m_freeTagList; + BINDING *m_inheritedBindings; + BINDING *m_freeBindingList; + int m_attsSize; + int m_nSpecifiedAtts; + int m_idAttIndex; + ATTRIBUTE *m_atts; + NS_ATT *m_nsAtts; + unsigned long m_nsAttsVersion; + unsigned char m_nsAttsPower; + POSITION m_position; + STRING_POOL m_tempPool; + STRING_POOL m_temp2Pool; + char *m_groupConnector; + unsigned int m_groupSize; + XML_Char m_namespaceSeparator; + XML_Parser m_parentParser; +#ifdef XML_DTD + XML_Bool m_isParamEntity; + XML_Bool m_useForeignDTD; + enum XML_ParamEntityParsing m_paramEntityParsing; +#endif +}; + +#define MALLOC(s) (parser->m_mem.malloc_fcn((s))) +#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s))) +#define FREE(p) (parser->m_mem.free_fcn((p))) + +#define userData (parser->m_userData) +#define handlerArg (parser->m_handlerArg) +#define startElementHandler (parser->m_startElementHandler) +#define endElementHandler (parser->m_endElementHandler) +#define characterDataHandler (parser->m_characterDataHandler) +#define processingInstructionHandler \ + (parser->m_processingInstructionHandler) +#define commentHandler (parser->m_commentHandler) +#define startCdataSectionHandler \ + (parser->m_startCdataSectionHandler) +#define endCdataSectionHandler (parser->m_endCdataSectionHandler) +#define defaultHandler (parser->m_defaultHandler) +#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler) +#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler) +#define unparsedEntityDeclHandler \ + (parser->m_unparsedEntityDeclHandler) +#define notationDeclHandler (parser->m_notationDeclHandler) +#define startNamespaceDeclHandler \ + (parser->m_startNamespaceDeclHandler) +#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler) +#define notStandaloneHandler (parser->m_notStandaloneHandler) +#define externalEntityRefHandler \ + (parser->m_externalEntityRefHandler) +#define externalEntityRefHandlerArg \ + (parser->m_externalEntityRefHandlerArg) +#define internalEntityRefHandler \ + (parser->m_internalEntityRefHandler) +#define skippedEntityHandler (parser->m_skippedEntityHandler) +#define unknownEncodingHandler (parser->m_unknownEncodingHandler) +#define elementDeclHandler (parser->m_elementDeclHandler) +#define attlistDeclHandler (parser->m_attlistDeclHandler) +#define entityDeclHandler (parser->m_entityDeclHandler) +#define xmlDeclHandler (parser->m_xmlDeclHandler) +#define encoding (parser->m_encoding) +#define initEncoding (parser->m_initEncoding) +#define internalEncoding (parser->m_internalEncoding) +#define unknownEncodingMem (parser->m_unknownEncodingMem) +#define unknownEncodingData (parser->m_unknownEncodingData) +#define unknownEncodingHandlerData \ + (parser->m_unknownEncodingHandlerData) +#define unknownEncodingRelease (parser->m_unknownEncodingRelease) +#define protocolEncodingName (parser->m_protocolEncodingName) +#define ns (parser->m_ns) +#define ns_triplets (parser->m_ns_triplets) +#define prologState (parser->m_prologState) +#define processor (parser->m_processor) +#define errorCode (parser->m_errorCode) +#define eventPtr (parser->m_eventPtr) +#define eventEndPtr (parser->m_eventEndPtr) +#define positionPtr (parser->m_positionPtr) +#define position (parser->m_position) +#define openInternalEntities (parser->m_openInternalEntities) +#define defaultExpandInternalEntities \ + (parser->m_defaultExpandInternalEntities) +#define tagLevel (parser->m_tagLevel) +#define buffer (parser->m_buffer) +#define bufferPtr (parser->m_bufferPtr) +#define bufferEnd (parser->m_bufferEnd) +#define parseEndByteIndex (parser->m_parseEndByteIndex) +#define parseEndPtr (parser->m_parseEndPtr) +#define bufferLim (parser->m_bufferLim) +#define dataBuf (parser->m_dataBuf) +#define dataBufEnd (parser->m_dataBufEnd) +#define _dtd (parser->m_dtd) +#define curBase (parser->m_curBase) +#define declEntity (parser->m_declEntity) +#define doctypeName (parser->m_doctypeName) +#define doctypeSysid (parser->m_doctypeSysid) +#define doctypePubid (parser->m_doctypePubid) +#define declAttributeType (parser->m_declAttributeType) +#define declNotationName (parser->m_declNotationName) +#define declNotationPublicId (parser->m_declNotationPublicId) +#define declElementType (parser->m_declElementType) +#define declAttributeId (parser->m_declAttributeId) +#define declAttributeIsCdata (parser->m_declAttributeIsCdata) +#define declAttributeIsId (parser->m_declAttributeIsId) +#define freeTagList (parser->m_freeTagList) +#define freeBindingList (parser->m_freeBindingList) +#define inheritedBindings (parser->m_inheritedBindings) +#define tagStack (parser->m_tagStack) +#define atts (parser->m_atts) +#define attsSize (parser->m_attsSize) +#define nSpecifiedAtts (parser->m_nSpecifiedAtts) +#define idAttIndex (parser->m_idAttIndex) +#define nsAtts (parser->m_nsAtts) +#define nsAttsVersion (parser->m_nsAttsVersion) +#define nsAttsPower (parser->m_nsAttsPower) +#define tempPool (parser->m_tempPool) +#define temp2Pool (parser->m_temp2Pool) +#define groupConnector (parser->m_groupConnector) +#define groupSize (parser->m_groupSize) +#define namespaceSeparator (parser->m_namespaceSeparator) +#define parentParser (parser->m_parentParser) +#ifdef XML_DTD +#define isParamEntity (parser->m_isParamEntity) +#define useForeignDTD (parser->m_useForeignDTD) +#define paramEntityParsing (parser->m_paramEntityParsing) +#endif /* XML_DTD */ + +#ifdef XML_DTD +#define parsing \ + (parentParser \ + ? \ + (isParamEntity \ + ? \ + (processor != externalParEntInitProcessor) \ + : \ + (processor != externalEntityInitProcessor)) \ + : \ + (processor != prologInitProcessor)) +#else +#define parsing \ + (parentParser \ + ? \ + (processor != externalEntityInitProcessor) \ + : \ + (processor != prologInitProcessor)) +#endif /* XML_DTD */ + +XML_Parser XMLCALL +XML_ParserCreate(const XML_Char *encodingName) +{ + return XML_ParserCreate_MM(encodingName, NULL, NULL); +} + +XML_Parser XMLCALL +XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) +{ + XML_Char tmp[2]; + *tmp = nsSep; + return XML_ParserCreate_MM(encodingName, NULL, tmp); +} + +static const XML_Char implicitContext[] = { + 'x', 'm', 'l', '=', 'h', 't', 't', 'p', ':', '/', '/', + 'w', 'w', 'w', '.', 'w', '3', '.', 'o', 'r', 'g', '/', + 'X', 'M', 'L', '/', '1', '9', '9', '8', '/', + 'n', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e', '\0' +}; + +XML_Parser XMLCALL +XML_ParserCreate_MM(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep) +{ + XML_Parser parser = parserCreate(encodingName, memsuite, nameSep, NULL); + if (parser != NULL && ns) { + /* implicit context only set for root parser, since child + parsers (i.e. external entity parsers) will inherit it + */ + if (!setContext(parser, implicitContext)) { + XML_ParserFree(parser); + return NULL; + } + } + return parser; +} + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd) +{ + XML_Parser parser; + + if (memsuite) { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser) + memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = memsuite->malloc_fcn; + mtemp->realloc_fcn = memsuite->realloc_fcn; + mtemp->free_fcn = memsuite->free_fcn; + } + } + else { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = malloc; + mtemp->realloc_fcn = realloc; + mtemp->free_fcn = free; + } + } + + if (!parser) + return parser; + + buffer = NULL; + bufferLim = NULL; + + attsSize = INIT_ATTS_SIZE; + atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE)); + if (atts == NULL) { + FREE(parser); + return NULL; + } + dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char)); + if (dataBuf == NULL) { + FREE(atts); + FREE(parser); + return NULL; + } + dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE; + + if (dtd) + _dtd = dtd; + else { + _dtd = dtdCreate(&parser->m_mem); + if (_dtd == NULL) { + FREE(dataBuf); + FREE(atts); + FREE(parser); + return NULL; + } + } + + freeBindingList = NULL; + freeTagList = NULL; + + groupSize = 0; + groupConnector = NULL; + + unknownEncodingHandler = NULL; + unknownEncodingHandlerData = NULL; + + namespaceSeparator = '!'; + ns = XML_FALSE; + ns_triplets = XML_FALSE; + + nsAtts = NULL; + nsAttsVersion = 0; + nsAttsPower = 0; + + poolInit(&tempPool, &(parser->m_mem)); + poolInit(&temp2Pool, &(parser->m_mem)); + parserInit(parser, encodingName); + + if (encodingName && !protocolEncodingName) { + XML_ParserFree(parser); + return NULL; + } + + if (nameSep) { + ns = XML_TRUE; + internalEncoding = XmlGetInternalEncodingNS(); + namespaceSeparator = *nameSep; + } + else { + internalEncoding = XmlGetInternalEncoding(); + } + + return parser; +} + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName) +{ + processor = prologInitProcessor; + XmlPrologStateInit(&prologState); + protocolEncodingName = (encodingName != NULL + ? poolCopyString(&tempPool, encodingName) + : NULL); + curBase = NULL; + XmlInitEncoding(&initEncoding, &encoding, 0); + userData = NULL; + handlerArg = NULL; + startElementHandler = NULL; + endElementHandler = NULL; + characterDataHandler = NULL; + processingInstructionHandler = NULL; + commentHandler = NULL; + startCdataSectionHandler = NULL; + endCdataSectionHandler = NULL; + defaultHandler = NULL; + startDoctypeDeclHandler = NULL; + endDoctypeDeclHandler = NULL; + unparsedEntityDeclHandler = NULL; + notationDeclHandler = NULL; + startNamespaceDeclHandler = NULL; + endNamespaceDeclHandler = NULL; + notStandaloneHandler = NULL; + externalEntityRefHandler = NULL; + externalEntityRefHandlerArg = parser; + skippedEntityHandler = NULL; + elementDeclHandler = NULL; + attlistDeclHandler = NULL; + entityDeclHandler = NULL; + xmlDeclHandler = NULL; + bufferPtr = buffer; + bufferEnd = buffer; + parseEndByteIndex = 0; + parseEndPtr = NULL; + declElementType = NULL; + declAttributeId = NULL; + declEntity = NULL; + doctypeName = NULL; + doctypeSysid = NULL; + doctypePubid = NULL; + declAttributeType = NULL; + declNotationName = NULL; + declNotationPublicId = NULL; + declAttributeIsCdata = XML_FALSE; + declAttributeIsId = XML_FALSE; + memset(&position, 0, sizeof(POSITION)); + errorCode = XML_ERROR_NONE; + eventPtr = NULL; + eventEndPtr = NULL; + positionPtr = NULL; + openInternalEntities = 0; + defaultExpandInternalEntities = XML_TRUE; + tagLevel = 0; + tagStack = NULL; + inheritedBindings = NULL; + nSpecifiedAtts = 0; + unknownEncodingMem = NULL; + unknownEncodingRelease = NULL; + unknownEncodingData = NULL; + parentParser = NULL; +#ifdef XML_DTD + isParamEntity = XML_FALSE; + useForeignDTD = XML_FALSE; + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif +} + +/* moves list of bindings to freeBindingList */ +static void FASTCALL +moveToFreeBindingList(XML_Parser parser, BINDING *bindings) +{ + while (bindings) { + BINDING *b = bindings; + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + } +} + +XML_Bool XMLCALL +XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) +{ + TAG *tStk; + if (parentParser) + return XML_FALSE; + /* move tagStack to freeTagList */ + tStk = tagStack; + while (tStk) { + TAG *tag = tStk; + tStk = tStk->parent; + tag->parent = freeTagList; + moveToFreeBindingList(parser, tag->bindings); + tag->bindings = NULL; + freeTagList = tag; + } + moveToFreeBindingList(parser, inheritedBindings); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + poolClear(&tempPool); + poolClear(&temp2Pool); + parserInit(parser, encodingName); + dtdReset(_dtd, &parser->m_mem); + return setContext(parser, implicitContext); +} + +enum XML_Status XMLCALL +XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + /* Block after XML_Parse()/XML_ParseBuffer() has been called. + XXX There's no way for the caller to determine which of the + XXX possible error cases caused the XML_STATUS_ERROR return. + */ + if (parsing) + return XML_STATUS_ERROR; + if (encodingName == NULL) + protocolEncodingName = NULL; + else { + protocolEncodingName = poolCopyString(&tempPool, encodingName); + if (!protocolEncodingName) + return XML_STATUS_ERROR; + } + return XML_STATUS_OK; +} + +XML_Parser XMLCALL +XML_ExternalEntityParserCreate(XML_Parser oldParser, + const XML_Char *context, + const XML_Char *encodingName) +{ + XML_Parser parser = oldParser; + DTD *newDtd = NULL; + DTD *oldDtd = _dtd; + XML_StartElementHandler oldStartElementHandler = startElementHandler; + XML_EndElementHandler oldEndElementHandler = endElementHandler; + XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler; + XML_ProcessingInstructionHandler oldProcessingInstructionHandler + = processingInstructionHandler; + XML_CommentHandler oldCommentHandler = commentHandler; + XML_StartCdataSectionHandler oldStartCdataSectionHandler + = startCdataSectionHandler; + XML_EndCdataSectionHandler oldEndCdataSectionHandler + = endCdataSectionHandler; + XML_DefaultHandler oldDefaultHandler = defaultHandler; + XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler + = unparsedEntityDeclHandler; + XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler; + XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler + = startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler + = endNamespaceDeclHandler; + XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler; + XML_ExternalEntityRefHandler oldExternalEntityRefHandler + = externalEntityRefHandler; + XML_SkippedEntityHandler oldSkippedEntityHandler = skippedEntityHandler; + XML_UnknownEncodingHandler oldUnknownEncodingHandler + = unknownEncodingHandler; + XML_ElementDeclHandler oldElementDeclHandler = elementDeclHandler; + XML_AttlistDeclHandler oldAttlistDeclHandler = attlistDeclHandler; + XML_EntityDeclHandler oldEntityDeclHandler = entityDeclHandler; + XML_XmlDeclHandler oldXmlDeclHandler = xmlDeclHandler; + ELEMENT_TYPE * oldDeclElementType = declElementType; + + void *oldUserData = userData; + void *oldHandlerArg = handlerArg; + XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities; + XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg; +#ifdef XML_DTD + enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing; + int oldInEntityValue = prologState.inEntityValue; +#endif + XML_Bool oldns_triplets = ns_triplets; + +#ifdef XML_DTD + if (!context) + newDtd = oldDtd; +#endif /* XML_DTD */ + + /* Note that the magical uses of the pre-processor to make field + access look more like C++ require that `parser' be overwritten + here. This makes this function more painful to follow than it + would be otherwise. + */ + if (ns) { + XML_Char tmp[2]; + *tmp = namespaceSeparator; + parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); + } + else { + parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); + } + + if (!parser) + return NULL; + + startElementHandler = oldStartElementHandler; + endElementHandler = oldEndElementHandler; + characterDataHandler = oldCharacterDataHandler; + processingInstructionHandler = oldProcessingInstructionHandler; + commentHandler = oldCommentHandler; + startCdataSectionHandler = oldStartCdataSectionHandler; + endCdataSectionHandler = oldEndCdataSectionHandler; + defaultHandler = oldDefaultHandler; + unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; + notationDeclHandler = oldNotationDeclHandler; + startNamespaceDeclHandler = oldStartNamespaceDeclHandler; + endNamespaceDeclHandler = oldEndNamespaceDeclHandler; + notStandaloneHandler = oldNotStandaloneHandler; + externalEntityRefHandler = oldExternalEntityRefHandler; + skippedEntityHandler = oldSkippedEntityHandler; + unknownEncodingHandler = oldUnknownEncodingHandler; + elementDeclHandler = oldElementDeclHandler; + attlistDeclHandler = oldAttlistDeclHandler; + entityDeclHandler = oldEntityDeclHandler; + xmlDeclHandler = oldXmlDeclHandler; + declElementType = oldDeclElementType; + userData = oldUserData; + if (oldUserData == oldHandlerArg) + handlerArg = userData; + else + handlerArg = parser; + if (oldExternalEntityRefHandlerArg != oldParser) + externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; + defaultExpandInternalEntities = oldDefaultExpandInternalEntities; + ns_triplets = oldns_triplets; + parentParser = oldParser; +#ifdef XML_DTD + paramEntityParsing = oldParamEntityParsing; + prologState.inEntityValue = oldInEntityValue; + if (context) { +#endif /* XML_DTD */ + if (!dtdCopy(_dtd, oldDtd, &parser->m_mem) + || !setContext(parser, context)) { + XML_ParserFree(parser); + return NULL; + } + processor = externalEntityInitProcessor; +#ifdef XML_DTD + } + else { + /* The DTD instance referenced by _dtd is shared between the document's + root parser and external PE parsers, therefore one does not need to + call setContext. In addition, one also *must* not call setContext, + because this would overwrite existing prefix->binding pointers in + _dtd with ones that get destroyed with the external PE parser. + This would leave those prefixes with dangling pointers. + */ + isParamEntity = XML_TRUE; + XmlPrologStateInitExternalEntity(&prologState); + processor = externalParEntInitProcessor; + } +#endif /* XML_DTD */ + return parser; +} + +static void FASTCALL +destroyBindings(BINDING *bindings, XML_Parser parser) +{ + for (;;) { + BINDING *b = bindings; + if (!b) + break; + bindings = b->nextTagBinding; + FREE(b->uri); + FREE(b); + } +} + +void XMLCALL +XML_ParserFree(XML_Parser parser) +{ + for (;;) { + TAG *p; + if (tagStack == NULL) { + if (freeTagList == NULL) + break; + tagStack = freeTagList; + freeTagList = NULL; + } + p = tagStack; + tagStack = tagStack->parent; + FREE(p->buf); + destroyBindings(p->bindings, parser); + FREE(p); + } + destroyBindings(freeBindingList, parser); + destroyBindings(inheritedBindings, parser); + poolDestroy(&tempPool); + poolDestroy(&temp2Pool); +#ifdef XML_DTD + /* external parameter entity parsers share the DTD structure + parser->m_dtd with the root parser, so we must not destroy it + */ + if (!isParamEntity && _dtd) +#else + if (_dtd) +#endif /* XML_DTD */ + dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem); + FREE((void *)atts); + FREE(groupConnector); + FREE(buffer); + FREE(dataBuf); + FREE(nsAtts); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + FREE(parser); +} + +void XMLCALL +XML_UseParserAsHandlerArg(XML_Parser parser) +{ + handlerArg = parser; +} + +enum XML_Error XMLCALL +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) +{ +#ifdef XML_DTD + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (parsing) + return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; + useForeignDTD = useDTD; + return XML_ERROR_NONE; +#else + return XML_ERROR_FEATURE_REQUIRES_XML_DTD; +#endif +} + +void XMLCALL +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (parsing) + return; + ns_triplets = do_nst ? XML_TRUE : XML_FALSE; +} + +void XMLCALL +XML_SetUserData(XML_Parser parser, void *p) +{ + if (handlerArg == userData) + handlerArg = userData = p; + else + userData = p; +} + +enum XML_Status XMLCALL +XML_SetBase(XML_Parser parser, const XML_Char *p) +{ + if (p) { + p = poolCopyString(&_dtd->pool, p); + if (!p) + return XML_STATUS_ERROR; + curBase = p; + } + else + curBase = NULL; + return XML_STATUS_OK; +} + +const XML_Char * XMLCALL +XML_GetBase(XML_Parser parser) +{ + return curBase; +} + +int XMLCALL +XML_GetSpecifiedAttributeCount(XML_Parser parser) +{ + return nSpecifiedAtts; +} + +int XMLCALL +XML_GetIdAttributeIndex(XML_Parser parser) +{ + return idAttIndex; +} + +void XMLCALL +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end) +{ + startElementHandler = start; + endElementHandler = end; +} + +void XMLCALL +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler start) { + startElementHandler = start; +} + +void XMLCALL +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler end) { + endElementHandler = end; +} + +void XMLCALL +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler) +{ + characterDataHandler = handler; +} + +void XMLCALL +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler) +{ + processingInstructionHandler = handler; +} + +void XMLCALL +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler) +{ + commentHandler = handler; +} + +void XMLCALL +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end) +{ + startCdataSectionHandler = start; + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start) { + startCdataSectionHandler = start; +} + +void XMLCALL +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end) { + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_FALSE; +} + +void XMLCALL +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_TRUE; +} + +void XMLCALL +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end) +{ + startDoctypeDeclHandler = start; + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start) { + startDoctypeDeclHandler = start; +} + +void XMLCALL +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end) { + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler) +{ + unparsedEntityDeclHandler = handler; +} + +void XMLCALL +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler) +{ + notationDeclHandler = handler; +} + +void XMLCALL +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end) +{ + startNamespaceDeclHandler = start; + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start) { + startNamespaceDeclHandler = start; +} + +void XMLCALL +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end) { + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler) +{ + notStandaloneHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler) +{ + externalEntityRefHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) +{ + if (arg) + externalEntityRefHandlerArg = (XML_Parser)arg; + else + externalEntityRefHandlerArg = parser; +} + +void XMLCALL +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler) +{ + skippedEntityHandler = handler; +} + +void XMLCALL +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *data) +{ + unknownEncodingHandler = handler; + unknownEncodingHandlerData = data; +} + +void XMLCALL +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl) +{ + elementDeclHandler = eldecl; +} + +void XMLCALL +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl) +{ + attlistDeclHandler = attdecl; +} + +void XMLCALL +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler) +{ + entityDeclHandler = handler; +} + +void XMLCALL +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler handler) { + xmlDeclHandler = handler; +} + +int XMLCALL +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing peParsing) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (parsing) + return 0; +#ifdef XML_DTD + paramEntityParsing = peParsing; + return 1; +#else + return peParsing == XML_PARAM_ENTITY_PARSING_NEVER; +#endif +} + +enum XML_Status XMLCALL +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +{ + if (len == 0) { + if (!isFinal) + return XML_STATUS_OK; + positionPtr = bufferPtr; + errorCode = processor(parser, bufferPtr, parseEndPtr = bufferEnd, 0); + if (errorCode == XML_ERROR_NONE) + return XML_STATUS_OK; + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } +#ifndef XML_CONTEXT_BYTES + else if (bufferPtr == bufferEnd) { + const char *end; + int nLeftOver; + parseEndByteIndex += len; + positionPtr = s; + if (isFinal) { + errorCode = processor(parser, s, parseEndPtr = s + len, 0); + if (errorCode == XML_ERROR_NONE) + return XML_STATUS_OK; + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + errorCode = processor(parser, s, parseEndPtr = s + len, &end); + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + XmlUpdatePosition(encoding, positionPtr, end, &position); + positionPtr = end; + nLeftOver = s + len - end; + if (nLeftOver) { + if (buffer == NULL || nLeftOver > bufferLim - buffer) { + /* FIXME avoid integer overflow */ + char *temp; + temp = (buffer == NULL + ? (char *)MALLOC(len * 2) + : (char *)REALLOC(buffer, len * 2)); + if (temp == NULL) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + buffer = temp; + if (!buffer) { + errorCode = XML_ERROR_NO_MEMORY; + eventPtr = eventEndPtr = NULL; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + bufferLim = buffer + len * 2; + } + memcpy(buffer, end, nLeftOver); + bufferPtr = buffer; + bufferEnd = buffer + nLeftOver; + } + return XML_STATUS_OK; + } +#endif /* not defined XML_CONTEXT_BYTES */ + else { + void *buff = XML_GetBuffer(parser, len); + if (buff == NULL) + return XML_STATUS_ERROR; + else { + memcpy(buff, s, len); + return XML_ParseBuffer(parser, len, isFinal); + } + } +} + +enum XML_Status XMLCALL +XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +{ + const char *start = bufferPtr; + positionPtr = start; + bufferEnd += len; + parseEndByteIndex += len; + errorCode = processor(parser, start, parseEndPtr = bufferEnd, + isFinal ? (const char **)NULL : &bufferPtr); + if (errorCode == XML_ERROR_NONE) { + if (!isFinal) { + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + } + return XML_STATUS_OK; + } + else { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } +} + +void * XMLCALL +XML_GetBuffer(XML_Parser parser, int len) +{ + if (len > bufferLim - bufferEnd) { + /* FIXME avoid integer overflow */ + int neededSize = len + (bufferEnd - bufferPtr); +#ifdef XML_CONTEXT_BYTES + int keep = bufferPtr - buffer; + + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + neededSize += keep; +#endif /* defined XML_CONTEXT_BYTES */ + if (neededSize <= bufferLim - buffer) { +#ifdef XML_CONTEXT_BYTES + if (keep < bufferPtr - buffer) { + int offset = (bufferPtr - buffer) - keep; + memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep); + bufferEnd -= offset; + bufferPtr -= offset; + } +#else + memmove(buffer, bufferPtr, bufferEnd - bufferPtr); + bufferEnd = buffer + (bufferEnd - bufferPtr); + bufferPtr = buffer; +#endif /* not defined XML_CONTEXT_BYTES */ + } + else { + char *newBuf; + int bufferSize = bufferLim - bufferPtr; + if (bufferSize == 0) + bufferSize = INIT_BUFFER_SIZE; + do { + bufferSize *= 2; + } while (bufferSize < neededSize); + newBuf = (char *)MALLOC(bufferSize); + if (newBuf == 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + bufferLim = newBuf + bufferSize; +#ifdef XML_CONTEXT_BYTES + if (bufferPtr) { + int keep = bufferPtr - buffer; + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep); + FREE(buffer); + buffer = newBuf; + bufferEnd = buffer + (bufferEnd - bufferPtr) + keep; + bufferPtr = buffer + keep; + } + else { + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; + } +#else + if (bufferPtr) { + memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr); + FREE(buffer); + } + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; +#endif /* not defined XML_CONTEXT_BYTES */ + } + } + return bufferEnd; +} + +enum XML_Error XMLCALL +XML_GetErrorCode(XML_Parser parser) +{ + return errorCode; +} + +long XMLCALL +XML_GetCurrentByteIndex(XML_Parser parser) +{ + if (eventPtr) + return parseEndByteIndex - (parseEndPtr - eventPtr); + return -1; +} + +int XMLCALL +XML_GetCurrentByteCount(XML_Parser parser) +{ + if (eventEndPtr && eventPtr) + return eventEndPtr - eventPtr; + return 0; +} + +const char * XMLCALL +XML_GetInputContext(XML_Parser parser, int *offset, int *size) +{ +#ifdef XML_CONTEXT_BYTES + if (eventPtr && buffer) { + *offset = eventPtr - buffer; + *size = bufferEnd - buffer; + return buffer; + } +#endif /* defined XML_CONTEXT_BYTES */ + return (char *) 0; +} + +int XMLCALL +XML_GetCurrentLineNumber(XML_Parser parser) +{ + if (eventPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.lineNumber + 1; +} + +int XMLCALL +XML_GetCurrentColumnNumber(XML_Parser parser) +{ + if (eventPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.columnNumber; +} + +void XMLCALL +XML_FreeContentModel(XML_Parser parser, XML_Content *model) +{ + FREE(model); +} + +void * XMLCALL +XML_MemMalloc(XML_Parser parser, size_t size) +{ + return MALLOC(size); +} + +void * XMLCALL +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) +{ + return REALLOC(ptr, size); +} + +void XMLCALL +XML_MemFree(XML_Parser parser, void *ptr) +{ + FREE(ptr); +} + +void XMLCALL +XML_DefaultCurrent(XML_Parser parser) +{ + if (defaultHandler) { + if (openInternalEntities) + reportDefault(parser, + internalEncoding, + openInternalEntities->internalEventPtr, + openInternalEntities->internalEventEndPtr); + else + reportDefault(parser, encoding, eventPtr, eventEndPtr); + } +} + +const XML_LChar * XMLCALL +XML_ErrorString(enum XML_Error code) +{ + static const XML_LChar *message[] = { + 0, + XML_L("out of memory"), + XML_L("syntax error"), + XML_L("no element found"), + XML_L("not well-formed (invalid token)"), + XML_L("unclosed token"), + XML_L("partial character"), + XML_L("mismatched tag"), + XML_L("duplicate attribute"), + XML_L("junk after document element"), + XML_L("illegal parameter entity reference"), + XML_L("undefined entity"), + XML_L("recursive entity reference"), + XML_L("asynchronous entity"), + XML_L("reference to invalid character number"), + XML_L("reference to binary entity"), + XML_L("reference to external entity in attribute"), + XML_L("xml declaration not at start of external entity"), + XML_L("unknown encoding"), + XML_L("encoding specified in XML declaration is incorrect"), + XML_L("unclosed CDATA section"), + XML_L("error in processing external entity reference"), + XML_L("document is not standalone"), + XML_L("unexpected parser state - please send a bug report"), + XML_L("entity declared in parameter entity"), + XML_L("requested feature requires XML_DTD support in Expat"), + XML_L("cannot change setting once parsing has begun"), + XML_L("unbound prefix") + }; + if (code > 0 && code < sizeof(message)/sizeof(message[0])) + return message[code]; + return NULL; +} + +const XML_LChar * XMLCALL +XML_ExpatVersion(void) { + + /* V1 is used to string-ize the version number. However, it would + string-ize the actual version macro *names* unless we get them + substituted before being passed to V1. CPP is defined to expand + a macro, then rescan for more expansions. Thus, we use V2 to expand + the version macros, then CPP will expand the resulting V1() macro + with the correct numerals. */ + /* ### I'm assuming cpp is portable in this respect... */ + +#define V1(a,b,c) XML_L(#a)XML_L(".")XML_L(#b)XML_L(".")XML_L(#c) +#define V2(a,b,c) XML_L("expat_")V1(a,b,c) + + return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION); + +#undef V1 +#undef V2 +} + +XML_Expat_Version XMLCALL +XML_ExpatVersionInfo(void) +{ + XML_Expat_Version version; + + version.major = XML_MAJOR_VERSION; + version.minor = XML_MINOR_VERSION; + version.micro = XML_MICRO_VERSION; + + return version; +} + +const XML_Feature * XMLCALL +XML_GetFeatureList(void) +{ + static XML_Feature features[] = { + {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), 0}, + {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), 0}, +#ifdef XML_UNICODE + {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, +#endif +#ifdef XML_UNICODE_WCHAR_T + {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, +#endif +#ifdef XML_DTD + {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, +#endif +#ifdef XML_CONTEXT_BYTES + {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), + XML_CONTEXT_BYTES}, +#endif +#ifdef XML_MIN_SIZE + {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, +#endif + {XML_FEATURE_END, NULL, 0} + }; + + features[0].value = sizeof(XML_Char); + features[1].value = sizeof(XML_LChar); + return features; +} + +/* Initially tag->rawName always points into the parse buffer; + for those TAG instances opened while the current parse buffer was + processed, and not yet closed, we need to store tag->rawName in a more + permanent location, since the parse buffer is about to be discarded. +*/ +static XML_Bool +storeRawNames(XML_Parser parser) +{ + TAG *tag = tagStack; + while (tag) { + int bufSize; + int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); + char *rawNameBuf = tag->buf + nameLen; + /* Stop if already stored. Since tagStack is a stack, we can stop + at the first entry that has already been copied; everything + below it in the stack is already been accounted for in a + previous call to this function. + */ + if (tag->rawName == rawNameBuf) + break; + /* For re-use purposes we need to ensure that the + size of tag->buf is a multiple of sizeof(XML_Char). + */ + bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); + if (bufSize > tag->bufEnd - tag->buf) { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_FALSE; + /* if tag->name.str points to tag->buf (only when namespace + processing is off) then we have to update it + */ + if (tag->name.str == (XML_Char *)tag->buf) + tag->name.str = (XML_Char *)temp; + /* if tag->name.localPart is set (when namespace processing is on) + then update it as well, since it will always point into tag->buf + */ + if (tag->name.localPart) + tag->name.localPart = (XML_Char *)temp + (tag->name.localPart - + (XML_Char *)tag->buf); + tag->buf = temp; + tag->bufEnd = temp + bufSize; + rawNameBuf = temp + nameLen; + } + memcpy(rawNameBuf, tag->rawName, tag->rawNameLength); + tag->rawName = rawNameBuf; + tag = tag->parent; + } + return XML_TRUE; +} + +static enum XML_Error PTRCALL +contentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = + doContent(parser, 0, encoding, start, end, endPtr); + if (result != XML_ERROR_NONE) + return result; + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + return result; +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = externalEntityInitProcessor2; + return externalEntityInitProcessor2(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor2(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(encoding, start, end, &next); + switch (tok) { + case XML_TOK_BOM: + /* If we are at the end of the buffer, this would cause the next stage, + i.e. externalEntityInitProcessor3, to pass control directly to + doContent (by detecting XML_TOK_NONE) without processing any xml text + declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent. + */ + if (next == end && endPtr) { + *endPtr = next; + return XML_ERROR_NONE; + } + start = next; + break; + case XML_TOK_PARTIAL: + if (endPtr) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (endPtr) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityInitProcessor3; + return externalEntityInitProcessor3(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor3(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(encoding, start, end, &next); + switch (tok) { + case XML_TOK_XML_DECL: + { + enum XML_Error result = processXmlDecl(parser, 1, start, next); + if (result != XML_ERROR_NONE) + return result; + start = next; + } + break; + case XML_TOK_PARTIAL: + if (endPtr) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (endPtr) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityContentProcessor; + tagLevel = 1; + return externalEntityContentProcessor(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityContentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = + doContent(parser, 1, encoding, start, end, endPtr); + if (result != XML_ERROR_NONE) + return result; + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + return result; +} + +static enum XML_Error +doContent(XML_Parser parser, + int startTagLevel, + const ENCODING *enc, + const char *s, + const char *end, + const char **nextPtr) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + for (;;) { + const char *next = s; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_TRAILING_CR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + *eventEndPP = end; + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + if (startTagLevel == 0) + return XML_ERROR_NO_ELEMENTS; + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + return XML_ERROR_NONE; + case XML_TOK_NONE: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (startTagLevel > 0) { + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + return XML_ERROR_NONE; + } + return XML_ERROR_NO_ELEMENTS; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (characterDataHandler) + characterDataHandler(handlerArg, &ch, 1); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0); + poolDiscard(&dtd->pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity or default handler. + */ + if (!dtd->hasParamEntityRefs || dtd->standalone) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->notation) + return XML_ERROR_BINARY_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + OPEN_INTERNAL_ENTITY openEntity; + if (!defaultExpandInternalEntities) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, entity->name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + entity->open = XML_TRUE; + openEntity.next = openInternalEntities; + openInternalEntities = &openEntity; + openEntity.entity = entity; + openEntity.internalEventPtr = NULL; + openEntity.internalEventEndPtr = NULL; + result = doContent(parser, + tagLevel, + internalEncoding, + (char *)entity->textPtr, + (char *)(entity->textPtr + entity->textLen), + 0); + entity->open = XML_FALSE; + openInternalEntities = openEntity.next; + if (result) + return result; + } + else if (externalEntityRefHandler) { + const XML_Char *context; + entity->open = XML_TRUE; + context = getContext(parser); + entity->open = XML_FALSE; + if (!context) + return XML_ERROR_NO_MEMORY; + if (!externalEntityRefHandler((XML_Parser)externalEntityRefHandlerArg, + context, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + poolDiscard(&tempPool); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + case XML_TOK_START_TAG_NO_ATTS: + /* fall through */ + case XML_TOK_START_TAG_WITH_ATTS: + { + TAG *tag; + enum XML_Error result; + XML_Char *toPtr; + if (freeTagList) { + tag = freeTagList; + freeTagList = freeTagList->parent; + } + else { + tag = (TAG *)MALLOC(sizeof(TAG)); + if (!tag) + return XML_ERROR_NO_MEMORY; + tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE); + if (!tag->buf) { + FREE(tag); + return XML_ERROR_NO_MEMORY; + } + tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; + } + tag->bindings = NULL; + tag->parent = tagStack; + tagStack = tag; + tag->name.localPart = NULL; + tag->name.prefix = NULL; + tag->rawName = s + enc->minBytesPerChar; + tag->rawNameLength = XmlNameLength(enc, tag->rawName); + ++tagLevel; + { + const char *rawNameEnd = tag->rawName + tag->rawNameLength; + const char *fromPtr = tag->rawName; + toPtr = (XML_Char *)tag->buf; + for (;;) { + int bufSize; + int convLen; + XmlConvert(enc, + &fromPtr, rawNameEnd, + (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); + convLen = toPtr - (XML_Char *)tag->buf; + if (fromPtr == rawNameEnd) { + tag->name.strLen = convLen; + break; + } + bufSize = (tag->bufEnd - tag->buf) << 1; + { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + tag->buf = temp; + tag->bufEnd = temp + bufSize; + toPtr = (XML_Char *)temp + convLen; + } + } + } + tag->name.str = (XML_Char *)tag->buf; + *toPtr = XML_T('\0'); + result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); + if (result) + return result; + if (startElementHandler) + startElementHandler(handlerArg, tag->name.str, + (const XML_Char **)atts); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + break; + } + case XML_TOK_EMPTY_ELEMENT_NO_ATTS: + /* fall through */ + case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: + { + const char *rawName = s + enc->minBytesPerChar; + enum XML_Error result; + BINDING *bindings = NULL; + XML_Bool noElmHandlers = XML_TRUE; + TAG_NAME name; + name.str = poolStoreString(&tempPool, enc, rawName, + rawName + XmlNameLength(enc, rawName)); + if (!name.str) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + result = storeAtts(parser, enc, s, &name, &bindings); + if (result) + return result; + poolFinish(&tempPool); + if (startElementHandler) { + startElementHandler(handlerArg, name.str, (const XML_Char **)atts); + noElmHandlers = XML_FALSE; + } + if (endElementHandler) { + if (startElementHandler) + *eventPP = *eventEndPP; + endElementHandler(handlerArg, name.str); + noElmHandlers = XML_FALSE; + } + if (noElmHandlers && defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + while (bindings) { + BINDING *b = bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + break; + case XML_TOK_END_TAG: + if (tagLevel == startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + else { + int len; + const char *rawName; + TAG *tag = tagStack; + tagStack = tag->parent; + tag->parent = freeTagList; + freeTagList = tag; + rawName = s + enc->minBytesPerChar*2; + len = XmlNameLength(enc, rawName); + if (len != tag->rawNameLength + || memcmp(tag->rawName, rawName, len) != 0) { + *eventPP = rawName; + return XML_ERROR_TAG_MISMATCH; + } + --tagLevel; + if (endElementHandler) { + const XML_Char *localPart; + const XML_Char *prefix; + XML_Char *uri; + localPart = tag->name.localPart; + if (ns && localPart) { + /* localPart and prefix may have been overwritten in + tag->name.str, since this points to the binding->uri + buffer which gets re-used; so we have to add them again + */ + uri = (XML_Char *)tag->name.str + tag->name.uriLen; + /* don't need to check for space - already done in storeAtts() */ + while (*localPart) *uri++ = *localPart++; + prefix = (XML_Char *)tag->name.prefix; + if (ns_triplets && prefix) { + *uri++ = namespaceSeparator; + while (*prefix) *uri++ = *prefix++; + } + *uri = XML_T('\0'); + } + endElementHandler(handlerArg, tag->name.str); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + while (tag->bindings) { + BINDING *b = tag->bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + tag->bindings = tag->bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + } + break; + case XML_TOK_CHAR_REF: + { + int n = XmlCharRefNumber(enc, s); + if (n < 0) + return XML_ERROR_BAD_CHAR_REF; + if (characterDataHandler) { + XML_Char buf[XML_ENCODE_MAX]; + characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_CDATA_SECT_OPEN: + { + enum XML_Error result; + if (startCdataSectionHandler) + startCdataSectionHandler(handlerArg); +#if 0 + /* Suppose you doing a transformation on a document that involves + changing only the character data. You set up a defaultHandler + and a characterDataHandler. The defaultHandler simply copies + characters through. The characterDataHandler does the + transformation and writes the characters out escaping them as + necessary. This case will fail to work if we leave out the + following two lines (because & and < inside CDATA sections will + be incorrectly escaped). + + However, now we have a start/endCdataSectionHandler, so it seems + easier to let the user deal with this. + */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + result = doCdataSection(parser, enc, &next, end, nextPtr); + if (!next) { + processor = cdataSectionProcessor; + return result; + } + } + break; + case XML_TOK_TRAILING_RSQB: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + characterDataHandler(handlerArg, dataBuf, + dataPtr - (ICHAR *)dataBuf); + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (XML_Char *)end - (XML_Char *)s); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + if (startTagLevel == 0) { + *eventPP = end; + return XML_ERROR_NO_ELEMENTS; + } + if (tagLevel != startTagLevel) { + *eventPP = end; + return XML_ERROR_ASYNC_ENTITY; + } + return XML_ERROR_NONE; + case XML_TOK_DATA_CHARS: + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + characterDataHandler(handlerArg, dataBuf, + dataPtr - (ICHAR *)dataBuf); + if (s == next) + break; + *eventPP = s; + } + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (XML_Char *)next - (XML_Char *)s); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + default: + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + *eventPP = s = next; + } + /* not reached */ +} + +/* Precondition: all arguments must be non-NULL; + Purpose: + - normalize attributes + - check attributes for well-formedness + - generate namespace aware attribute names (URI, prefix) + - build list of attributes for startElementHandler + - default attributes + - process namespace declarations (check and report them) + - generate namespace aware element name (URI, prefix) +*/ +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *enc, + const char *attStr, TAG_NAME *tagNamePtr, + BINDING **bindingsPtr) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ELEMENT_TYPE *elementType; + int nDefaultAtts; + const XML_Char **appAtts; /* the attribute list for the application */ + int attIndex = 0; + int prefixLen; + int i; + int n; + XML_Char *uri; + int nPrefixes = 0; + BINDING *binding; + const XML_Char *localPart; + + /* lookup the element type name */ + elementType = (ELEMENT_TYPE *)lookup(&dtd->elementTypes, tagNamePtr->str,0); + if (!elementType) { + const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str); + if (!name) + return XML_ERROR_NO_MEMORY; + elementType = (ELEMENT_TYPE *)lookup(&dtd->elementTypes, name, + sizeof(ELEMENT_TYPE)); + if (!elementType) + return XML_ERROR_NO_MEMORY; + if (ns && !setElementTypePrefix(parser, elementType)) + return XML_ERROR_NO_MEMORY; + } + nDefaultAtts = elementType->nDefaultAtts; + + /* get the attributes from the tokenizer */ + n = XmlGetAttributes(enc, attStr, attsSize, atts); + if (n + nDefaultAtts > attsSize) { + int oldAttsSize = attsSize; + ATTRIBUTE *temp; + attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; + temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + atts = temp; + if (n > oldAttsSize) + XmlGetAttributes(enc, attStr, n, atts); + } + + appAtts = (const XML_Char **)atts; + for (i = 0; i < n; i++) { + /* add the name and value to the attribute list */ + ATTRIBUTE_ID *attId = getAttributeId(parser, enc, atts[i].name, + atts[i].name + + XmlNameLength(enc, atts[i].name)); + if (!attId) + return XML_ERROR_NO_MEMORY; + /* Detect duplicate attributes by their QNames. This does not work when + namespace processing is turned on and different prefixes for the same + namespace are used. For this case we have a check further down. + */ + if ((attId->name)[-1]) { + if (enc == encoding) + eventPtr = atts[i].name; + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + (attId->name)[-1] = 1; + appAtts[attIndex++] = attId->name; + if (!atts[i].normalized) { + enum XML_Error result; + XML_Bool isCdata = XML_TRUE; + + /* figure out whether declared as other than CDATA */ + if (attId->maybeTokenized) { + int j; + for (j = 0; j < nDefaultAtts; j++) { + if (attId == elementType->defaultAtts[j].id) { + isCdata = elementType->defaultAtts[j].isCdata; + break; + } + } + } + + /* normalize the attribute value */ + result = storeAttributeValue(parser, enc, isCdata, + atts[i].valuePtr, atts[i].valueEnd, + &tempPool); + if (result) + return result; + appAtts[attIndex] = poolStart(&tempPool); + poolFinish(&tempPool); + } + else { + /* the value did not need normalizing */ + appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, + atts[i].valueEnd); + if (appAtts[attIndex] == 0) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + } + /* handle prefixed attribute names */ + if (attId->prefix) { + if (attId->xmlns) { + /* deal with namespace declarations here */ + enum XML_Error result = addBinding(parser, attId->prefix, attId, + appAtts[attIndex], bindingsPtr); + if (result) + return result; + --attIndex; + } + else { + /* deal with other prefixed names later */ + attIndex++; + nPrefixes++; + (attId->name)[-1] = 2; + } + } + else + attIndex++; + } + + /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */ + nSpecifiedAtts = attIndex; + if (elementType->idAtt && (elementType->idAtt->name)[-1]) { + for (i = 0; i < attIndex; i += 2) + if (appAtts[i] == elementType->idAtt->name) { + idAttIndex = i; + break; + } + } + else + idAttIndex = -1; + + /* do attribute defaulting */ + for (i = 0; i < nDefaultAtts; i++) { + const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i; + if (!(da->id->name)[-1] && da->value) { + if (da->id->prefix) { + if (da->id->xmlns) { + enum XML_Error result = addBinding(parser, da->id->prefix, da->id, + da->value, bindingsPtr); + if (result) + return result; + } + else { + (da->id->name)[-1] = 2; + nPrefixes++; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + else { + (da->id->name)[-1] = 1; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + } + appAtts[attIndex] = 0; + + /* expand prefixed attribute names, check for duplicates, + and clear flags that say whether attributes were specified */ + i = 0; + if (nPrefixes) { + int j; /* hash table index */ + unsigned long version = nsAttsVersion; + int nsAttsSize = (int)1 << nsAttsPower; + /* size of hash table must be at least 2 * (# of prefixed attributes) */ + if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */ + NS_ATT *temp; + /* hash table size must also be a power of 2 and >= 8 */ + while (nPrefixes >> nsAttsPower++); + if (nsAttsPower < 3) + nsAttsPower = 3; + nsAttsSize = (int)1 << nsAttsPower; + temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT)); + if (!temp) + return XML_ERROR_NO_MEMORY; + nsAtts = temp; + version = 0; /* force re-initialization of nsAtts hash table */ + } + /* using a version flag saves us from initializing nsAtts every time */ + if (!version) { /* initialize version flags when version wraps around */ + version = INIT_ATTS_VERSION; + for (j = nsAttsSize; j != 0; ) + nsAtts[--j].version = version; + } + nsAttsVersion = --version; + + /* expand prefixed names and check for duplicates */ + for (; i < attIndex; i += 2) { + const XML_Char *s = appAtts[i]; + if (s[-1] == 2) { /* prefixed */ + ATTRIBUTE_ID *id; + const BINDING *b; + unsigned long uriHash = 0; + ((XML_Char *)s)[-1] = 0; /* clear flag */ + id = (ATTRIBUTE_ID *)lookup(&dtd->attributeIds, s, 0); + b = id->prefix->binding; + if (!b) + return XML_ERROR_UNBOUND_PREFIX; + + /* as we expand the name we also calculate its hash value */ + for (j = 0; j < b->uriLen; j++) { + const XML_Char c = b->uri[j]; + if (!poolAppendChar(&tempPool, c)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } + while (*s++ != XML_T(':')) + ; + do { /* copies null terminator */ + const XML_Char c = *s; + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } while (*s++); + + { /* Check hash table for duplicate of expanded name (uriName). + Derived from code in lookup(HASH_TABLE *table, ...). + */ + unsigned char step = 0; + unsigned long mask = nsAttsSize - 1; + j = uriHash & mask; /* index into hash table */ + while (nsAtts[j].version == version) { + /* for speed we compare stored hash values first */ + if (uriHash == nsAtts[j].hash) { + const XML_Char *s1 = poolStart(&tempPool); + const XML_Char *s2 = nsAtts[j].uriName; + /* s1 is null terminated, but not s2 */ + for (; *s1 == *s2 && *s1 != 0; s1++, s2++); + if (*s1 == 0) + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + if (!step) + step = PROBE_STEP(uriHash, mask, nsAttsPower); + j < step ? ( j += nsAttsSize - step) : (j -= step); + } + } + + if (ns_triplets) { /* append namespace separator and prefix */ + tempPool.ptr[-1] = namespaceSeparator; + s = b->prefix->name; + do { + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + } while (*s++); + } + + /* store expanded name in attribute list */ + s = poolStart(&tempPool); + poolFinish(&tempPool); + appAtts[i] = s; + + /* fill empty slot with new version, uriName and hash value */ + nsAtts[j].version = version; + nsAtts[j].hash = uriHash; + nsAtts[j].uriName = s; + + if (!--nPrefixes) + break; + } + else /* not prefixed */ + ((XML_Char *)s)[-1] = 0; /* clear flag */ + } + } + /* clear flags for the remaining attributes */ + for (; i < attIndex; i += 2) + ((XML_Char *)(appAtts[i]))[-1] = 0; + for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) + binding->attId->name[-1] = 0; + + if (!ns) + return XML_ERROR_NONE; + + /* expand the element type name */ + if (elementType->prefix) { + binding = elementType->prefix->binding; + if (!binding) + return XML_ERROR_UNBOUND_PREFIX; + localPart = tagNamePtr->str; + while (*localPart++ != XML_T(':')) + ; + } + else if (dtd->defaultPrefix.binding) { + binding = dtd->defaultPrefix.binding; + localPart = tagNamePtr->str; + } + else + return XML_ERROR_NONE; + prefixLen = 0; + if (ns_triplets && binding->prefix->name) { + for (; binding->prefix->name[prefixLen++];) + ; + } + tagNamePtr->localPart = localPart; + tagNamePtr->uriLen = binding->uriLen; + tagNamePtr->prefix = binding->prefix->name; + tagNamePtr->prefixLen = prefixLen; + for (i = 0; localPart[i++];) + ; + n = i + binding->uriLen + prefixLen; + if (n > binding->uriAlloc) { + TAG *p; + uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char)); + if (!uri) + return XML_ERROR_NO_MEMORY; + binding->uriAlloc = n + EXPAND_SPARE; + memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char)); + for (p = tagStack; p; p = p->parent) + if (p->name.str == binding->uri) + p->name.str = uri; + FREE(binding->uri); + binding->uri = uri; + } + uri = binding->uri + binding->uriLen; + memcpy(uri, localPart, i * sizeof(XML_Char)); + if (prefixLen) { + uri = uri + (i - 1); + if (namespaceSeparator) + *uri = namespaceSeparator; + memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char)); + } + tagNamePtr->str = binding->uri; + return XML_ERROR_NONE; +} + +/* addBinding() overwrites the value of prefix->binding without checking. + Therefore one must keep track of the old value outside of addBinding(). +*/ +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr) +{ + BINDING *b; + int len; + + /* empty string is only valid when there is no prefix per XML NS 1.0 */ + if (*uri == XML_T('\0') && prefix->name) + return XML_ERROR_SYNTAX; + + for (len = 0; uri[len]; len++) + ; + if (namespaceSeparator) + len++; + if (freeBindingList) { + b = freeBindingList; + if (len > b->uriAlloc) { + XML_Char *temp = (XML_Char *)REALLOC(b->uri, + sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + b->uri = temp; + b->uriAlloc = len + EXPAND_SPARE; + } + freeBindingList = b->nextTagBinding; + } + else { + b = (BINDING *)MALLOC(sizeof(BINDING)); + if (!b) + return XML_ERROR_NO_MEMORY; + b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (!b->uri) { + FREE(b); + return XML_ERROR_NO_MEMORY; + } + b->uriAlloc = len + EXPAND_SPARE; + } + b->uriLen = len; + memcpy(b->uri, uri, len * sizeof(XML_Char)); + if (namespaceSeparator) + b->uri[len - 1] = namespaceSeparator; + b->prefix = prefix; + b->attId = attId; + b->prevPrefixBinding = prefix->binding; + /* NULL binding when default namespace undeclared */ + if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix) + prefix->binding = NULL; + else + prefix->binding = b; + b->nextTagBinding = *bindingsPtr; + *bindingsPtr = b; + if (startNamespaceDeclHandler) + startNamespaceDeclHandler(handlerArg, prefix->name, + prefix->binding ? uri : 0); + return XML_ERROR_NONE; +} + +/* The idea here is to avoid using stack for each CDATA section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +cdataSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doCdataSection(parser, encoding, &start, + end, endPtr); + if (start) { + if (parentParser) { /* we are parsing an external entity */ + processor = externalEntityContentProcessor; + return externalEntityContentProcessor(parser, start, end, endPtr); + } + else { + processor = contentProcessor; + return contentProcessor(parser, start, end, endPtr); + } + } + return result; +} + +/* startPtr gets set to non-null is the section is closed, and to null if + the section is not yet closed. +*/ +static enum XML_Error +doCdataSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr) +{ + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + for (;;) { + const char *next; + int tok = XmlCdataSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_CDATA_SECT_CLOSE: + if (endCdataSectionHandler) + endCdataSectionHandler(handlerArg); +#if 0 + /* see comment under XML_TOK_CDATA_SECT_OPEN */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + return XML_ERROR_NONE; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_DATA_CHARS: + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = next; + characterDataHandler(handlerArg, dataBuf, + dataPtr - (ICHAR *)dataBuf); + if (s == next) + break; + *eventPP = s; + } + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (XML_Char *)next - (XML_Char *)s); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_CDATA_SECTION; + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + *eventPP = s = next; + } + /* not reached */ +} + +#ifdef XML_DTD + +/* The idea here is to avoid using stack for each IGNORE section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +ignoreSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doIgnoreSection(parser, encoding, &start, + end, endPtr); + if (start) { + processor = prologProcessor; + return prologProcessor(parser, start, end, endPtr); + } + return result; +} + +/* startPtr gets set to non-null is the section is closed, and to null + if the section is not yet closed. +*/ +static enum XML_Error +doIgnoreSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr) +{ + const char *next; + int tok; + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + tok = XmlIgnoreSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_IGNORE_SECT: + if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + return XML_ERROR_NONE; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */ + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + /* not reached */ +} + +#endif /* XML_DTD */ + +static enum XML_Error +initializeEncoding(XML_Parser parser) +{ + const char *s; +#ifdef XML_UNICODE + char encodingBuf[128]; + if (!protocolEncodingName) + s = NULL; + else { + int i; + for (i = 0; protocolEncodingName[i]; i++) { + if (i == sizeof(encodingBuf) - 1 + || (protocolEncodingName[i] & ~0x7f) != 0) { + encodingBuf[0] = '\0'; + break; + } + encodingBuf[i] = (char)protocolEncodingName[i]; + } + encodingBuf[i] = '\0'; + s = encodingBuf; + } +#else + s = protocolEncodingName; +#endif + if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s)) + return XML_ERROR_NONE; + return handleUnknownEncoding(parser, protocolEncodingName); +} + +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next) +{ + const char *encodingName = NULL; + const XML_Char *storedEncName = NULL; + const ENCODING *newEncoding = NULL; + const char *version = NULL; + const char *versionend; + const XML_Char *storedversion = NULL; + int standalone = -1; + if (!(ns + ? XmlParseXmlDeclNS + : XmlParseXmlDecl)(isGeneralTextEntity, + encoding, + s, + next, + &eventPtr, + &version, + &versionend, + &encodingName, + &newEncoding, + &standalone)) + return XML_ERROR_SYNTAX; + if (!isGeneralTextEntity && standalone == 1) { + _dtd->standalone = XML_TRUE; +#ifdef XML_DTD + if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif /* XML_DTD */ + } + if (xmlDeclHandler) { + if (encodingName != NULL) { + storedEncName = poolStoreString(&temp2Pool, + encoding, + encodingName, + encodingName + + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + poolFinish(&temp2Pool); + } + if (version) { + storedversion = poolStoreString(&temp2Pool, + encoding, + version, + versionend - encoding->minBytesPerChar); + if (!storedversion) + return XML_ERROR_NO_MEMORY; + } + xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone); + } + else if (defaultHandler) + reportDefault(parser, encoding, s, next); + if (protocolEncodingName == NULL) { + if (newEncoding) { + if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) { + eventPtr = encodingName; + return XML_ERROR_INCORRECT_ENCODING; + } + encoding = newEncoding; + } + else if (encodingName) { + enum XML_Error result; + if (!storedEncName) { + storedEncName = poolStoreString( + &temp2Pool, encoding, encodingName, + encodingName + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + } + result = handleUnknownEncoding(parser, storedEncName); + poolClear(&temp2Pool); + if (result == XML_ERROR_UNKNOWN_ENCODING) + eventPtr = encodingName; + return result; + } + } + + if (storedEncName || storedversion) + poolClear(&temp2Pool); + + return XML_ERROR_NONE; +} + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + if (unknownEncodingHandler) { + XML_Encoding info; + int i; + for (i = 0; i < 256; i++) + info.map[i] = -1; + info.convert = NULL; + info.data = NULL; + info.release = NULL; + if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, + &info)) { + ENCODING *enc; + unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding()); + if (!unknownEncodingMem) { + if (info.release) + info.release(info.data); + return XML_ERROR_NO_MEMORY; + } + enc = (ns + ? XmlInitUnknownEncodingNS + : XmlInitUnknownEncoding)(unknownEncodingMem, + info.map, + info.convert, + info.data); + if (enc) { + unknownEncodingData = info.data; + unknownEncodingRelease = info.release; + encoding = enc; + return XML_ERROR_NONE; + } + } + if (info.release != NULL) + info.release(info.data); + } + return XML_ERROR_UNKNOWN_ENCODING; +} + +static enum XML_Error PTRCALL +prologInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = prologProcessor; + return prologProcessor(parser, s, end, nextPtr); +} + +#ifdef XML_DTD + +static enum XML_Error PTRCALL +externalParEntInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + + /* we know now that XML_Parse(Buffer) has been called, + so we consider the external parameter entity read */ + _dtd->paramEntityRead = XML_TRUE; + + if (prologState.inEntityValue) { + processor = entityValueInitProcessor; + return entityValueInitProcessor(parser, s, end, nextPtr); + } + else { + processor = externalParEntProcessor; + return externalParEntProcessor(parser, s, end, nextPtr); + } +} + +static enum XML_Error PTRCALL +entityValueInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *start = s; + const char *next = s; + int tok; + + for (;;) { + tok = XmlPrologTok(encoding, start, end, &next); + if (tok <= 0) { + if (nextPtr != 0 && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + return storeEntityValue(parser, encoding, s, end); + } + else if (tok == XML_TOK_XML_DECL) { + enum XML_Error result = processXmlDecl(parser, 0, start, next); + if (result != XML_ERROR_NONE) + return result; + if (nextPtr) *nextPtr = next; + /* stop scanning for text declaration - we found one */ + processor = entityValueProcessor; + return entityValueProcessor(parser, next, end, nextPtr); + } + /* If we are at the end of the buffer, this would cause XmlPrologTok to + return XML_TOK_NONE on the next call, which would then cause the + function to exit with *nextPtr set to s - that is what we want for other + tokens, but not for the BOM - we would rather like to skip it; + then, when this routine is entered the next time, XmlPrologTok will + return XML_TOK_INVALID, since the BOM is still in the buffer + */ + else if (tok == XML_TOK_BOM && next == end && nextPtr) { + *nextPtr = next; + return XML_ERROR_NONE; + } + start = next; + } +} + +static enum XML_Error PTRCALL +externalParEntProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *start = s; + const char *next = s; + int tok; + + tok = XmlPrologTok(encoding, start, end, &next); + if (tok <= 0) { + if (nextPtr != 0 && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + } + /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM. + However, when parsing an external subset, doProlog will not accept a BOM + as valid, and report a syntax error, so we have to skip the BOM + */ + else if (tok == XML_TOK_BOM) { + s = next; + tok = XmlPrologTok(encoding, s, end, &next); + } + + processor = prologProcessor; + return doProlog(parser, encoding, s, end, tok, next, nextPtr); +} + +static enum XML_Error PTRCALL +entityValueProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *start = s; + const char *next = s; + const ENCODING *enc = encoding; + int tok; + + for (;;) { + tok = XmlPrologTok(enc, start, end, &next); + if (tok <= 0) { + if (nextPtr != 0 && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + return storeEntityValue(parser, enc, s, end); + } + start = next; + } +} + +#endif /* XML_DTD */ + +static enum XML_Error PTRCALL +prologProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, nextPtr); +} + +static enum XML_Error +doProlog(XML_Parser parser, + const ENCODING *enc, + const char *s, + const char *end, + int tok, + const char *next, + const char **nextPtr) +{ +#ifdef XML_DTD + static const XML_Char externalSubsetName[] = { '#' , '\0' }; +#endif /* XML_DTD */ + static const XML_Char atypeCDATA[] = { 'C', 'D', 'A', 'T', 'A', '\0' }; + static const XML_Char atypeID[] = { 'I', 'D', '\0' }; + static const XML_Char atypeIDREF[] = { 'I', 'D', 'R', 'E', 'F', '\0' }; + static const XML_Char atypeIDREFS[] = { 'I', 'D', 'R', 'E', 'F', 'S', '\0' }; + static const XML_Char atypeENTITY[] = { 'E', 'N', 'T', 'I', 'T', 'Y', '\0' }; + static const XML_Char atypeENTITIES[] = + { 'E', 'N', 'T', 'I', 'T', 'I', 'E', 'S', '\0' }; + static const XML_Char atypeNMTOKEN[] = { + 'N', 'M', 'T', 'O', 'K', 'E', 'N', '\0' }; + static const XML_Char atypeNMTOKENS[] = { + 'N', 'M', 'T', 'O', 'K', 'E', 'N', 'S', '\0' }; + static const XML_Char notationPrefix[] = { + 'N', 'O', 'T', 'A', 'T', 'I', 'O', 'N', '(', '\0' }; + static const XML_Char enumValueSep[] = { '|', '\0' }; + static const XML_Char enumValueStart[] = { '(', '\0' }; + + DTD * const dtd = _dtd; /* save one level of indirection */ + + const char **eventPP; + const char **eventEndPP; + enum XML_Content_Quant quant; + + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + for (;;) { + int role; + XML_Bool handleDefault = XML_TRUE; + *eventPP = s; + *eventEndPP = next; + if (tok <= 0) { + if (nextPtr != 0 && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: +#ifdef XML_DTD + if (enc != encoding) + return XML_ERROR_NONE; + if (isParamEntity) { + if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc) + == XML_ROLE_ERROR) + return XML_ERROR_SYNTAX; + return XML_ERROR_NONE; + } +#endif /* XML_DTD */ + return XML_ERROR_NO_ELEMENTS; + default: + tok = -tok; + next = end; + break; + } + } + role = XmlTokenRole(&prologState, tok, s, next, enc); + switch (role) { + case XML_ROLE_XML_DECL: + { + enum XML_Error result = processXmlDecl(parser, 0, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_NAME: + if (startDoctypeDeclHandler) { + doctypeName = poolStoreString(&tempPool, enc, s, next); + if (!doctypeName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + doctypePubid = NULL; + handleDefault = XML_FALSE; + } + doctypeSysid = NULL; /* always initialize to NULL */ + break; + case XML_ROLE_DOCTYPE_INTERNAL_SUBSET: + if (startDoctypeDeclHandler) { + startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid, + doctypePubid, 1); + doctypeName = NULL; + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + break; +#ifdef XML_DTD + case XML_ROLE_TEXT_DECL: + { + enum XML_Error result = processXmlDecl(parser, 1, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; +#endif /* XML_DTD */ + case XML_ROLE_DOCTYPE_PUBLIC_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + doctypePubid = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!doctypePubid) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } +#ifdef XML_DTD + declEntity = (ENTITY *)lookup(&dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; +#endif /* XML_DTD */ + /* fall through */ + case XML_ROLE_ENTITY_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_SYNTAX; + if (dtd->keepProcessing && declEntity) { + XML_Char *tem = poolStoreString(&dtd->pool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declEntity->publicId = tem; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_CLOSE: + if (doctypeName) { + startDoctypeDeclHandler(handlerArg, doctypeName, + doctypeSysid, doctypePubid, 0); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + /* doctypeSysid will be non-NULL in the case of a previous + XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler + was not set, indicating an external subset + */ +#ifdef XML_DTD + if (doctypeSysid || useForeignDTD) { + dtd->hasParamEntityRefs = XML_TRUE; /* when docTypeSysid == NULL */ + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + if (useForeignDTD) + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead && + !dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + /* end of DTD - no need to update dtd->keepProcessing */ + } + useForeignDTD = XML_FALSE; + } +#endif /* XML_DTD */ + if (endDoctypeDeclHandler) { + endDoctypeDeclHandler(handlerArg); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_INSTANCE_START: +#ifdef XML_DTD + /* if there is no DOCTYPE declaration then now is the + last chance to read the foreign DTD + */ + if (useForeignDTD) { + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead && + !dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + /* end of DTD - no need to update dtd->keepProcessing */ + } + } +#endif /* XML_DTD */ + processor = contentProcessor; + return contentProcessor(parser, s, end, nextPtr); + case XML_ROLE_ATTLIST_ELEMENT_NAME: + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_NAME: + declAttributeId = getAttributeId(parser, enc, s, next); + if (!declAttributeId) + return XML_ERROR_NO_MEMORY; + declAttributeIsCdata = XML_FALSE; + declAttributeType = NULL; + declAttributeIsId = XML_FALSE; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_CDATA: + declAttributeIsCdata = XML_TRUE; + declAttributeType = atypeCDATA; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ID: + declAttributeIsId = XML_TRUE; + declAttributeType = atypeID; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREF: + declAttributeType = atypeIDREF; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREFS: + declAttributeType = atypeIDREFS; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITY: + declAttributeType = atypeENTITY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES: + declAttributeType = atypeENTITIES; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN: + declAttributeType = atypeNMTOKEN; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS: + declAttributeType = atypeNMTOKENS; + checkAttListDeclHandler: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTRIBUTE_ENUM_VALUE: + case XML_ROLE_ATTRIBUTE_NOTATION_VALUE: + if (dtd->keepProcessing && attlistDeclHandler) { + const XML_Char *prefix; + if (declAttributeType) { + prefix = enumValueSep; + } + else { + prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE + ? notationPrefix + : enumValueStart); + } + if (!poolAppendString(&tempPool, prefix)) + return XML_ERROR_NO_MEMORY; + if (!poolAppend(&tempPool, enc, s, next)) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: + case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, declAttributeIsId, + 0, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T('(') + || (*declAttributeType == XML_T('N') + && declAttributeType[1] == XML_T('O'))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(')')) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + 0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: + case XML_ROLE_FIXED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + const XML_Char *attVal; + enum XML_Error result = + storeAttributeValue(parser, enc, declAttributeIsCdata, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar, + &dtd->pool); + if (result) + return result; + attVal = poolStart(&dtd->pool); + poolFinish(&dtd->pool); + /* ID attributes aren't allowed to have a default */ + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, XML_FALSE, attVal, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T('(') + || (*declAttributeType == XML_T('N') + && declAttributeType[1] == XML_T('O'))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(')')) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + attVal, + role == XML_ROLE_FIXED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_ENTITY_VALUE: + if (dtd->keepProcessing) { + enum XML_Error result = storeEntityValue(parser, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (declEntity) { + declEntity->textPtr = poolStart(&dtd->entityValuePool); + declEntity->textLen = poolLength(&dtd->entityValuePool); + poolFinish(&dtd->entityValuePool); + if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + declEntity->textPtr, + declEntity->textLen, + curBase, 0, 0, 0); + handleDefault = XML_FALSE; + } + } + else + poolDiscard(&dtd->entityValuePool); + if (result != XML_ERROR_NONE) + return result; + } + break; + case XML_ROLE_DOCTYPE_SYSTEM_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + doctypeSysid = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (doctypeSysid == NULL) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } +#ifdef XML_DTD + else + /* use externalSubsetName to make doctypeSysid non-NULL + for the case where no startDoctypeDeclHandler is set */ + doctypeSysid = externalSubsetName; +#endif /* XML_DTD */ + if (!dtd->standalone +#ifdef XML_DTD + && !paramEntityParsing +#endif /* XML_DTD */ + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; +#ifndef XML_DTD + break; +#else /* XML_DTD */ + if (!declEntity) { + declEntity = (ENTITY *)lookup(&dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + declEntity->publicId = NULL; + } + /* fall through */ +#endif /* XML_DTD */ + case XML_ROLE_ENTITY_SYSTEM_ID: + if (dtd->keepProcessing && declEntity) { + declEntity->systemId = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!declEntity->systemId) + return XML_ERROR_NO_MEMORY; + declEntity->base = curBase; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_COMPLETE: + if (dtd->keepProcessing && declEntity && entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + 0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + 0); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_NOTATION_NAME: + if (dtd->keepProcessing && declEntity) { + declEntity->notation = poolStoreString(&dtd->pool, enc, s, next); + if (!declEntity->notation) + return XML_ERROR_NO_MEMORY; + poolFinish(&dtd->pool); + if (unparsedEntityDeclHandler) { + *eventEndPP = s; + unparsedEntityDeclHandler(handlerArg, + declEntity->name, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + else if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + 0,0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_GENERAL_ENTITY_NAME: + { + if (XmlPredefinedEntityName(enc, s, next)) { + declEntity = NULL; + break; + } + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(&dtd->generalEntities, name, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_FALSE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + } + break; + case XML_ROLE_PARAM_ENTITY_NAME: +#ifdef XML_DTD + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(&dtd->paramEntities, + name, sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_TRUE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } +#else /* not XML_DTD */ + declEntity = NULL; +#endif /* XML_DTD */ + break; + case XML_ROLE_NOTATION_NAME: + declNotationPublicId = NULL; + declNotationName = NULL; + if (notationDeclHandler) { + declNotationName = poolStoreString(&tempPool, enc, s, next); + if (!declNotationName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_SYNTAX; + if (declNotationName) { /* means notationDeclHandler != NULL */ + XML_Char *tem = poolStoreString(&tempPool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declNotationPublicId = tem; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_SYSTEM_ID: + if (declNotationName && notationDeclHandler) { + const XML_Char *systemId + = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!systemId) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + systemId, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_NOTATION_NO_SYSTEM_ID: + if (declNotationPublicId && notationDeclHandler) { + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + 0, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_ERROR: + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: + return XML_ERROR_PARAM_ENTITY_REF; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + default: + return XML_ERROR_SYNTAX; + } +#ifdef XML_DTD + case XML_ROLE_IGNORE_SECT: + { + enum XML_Error result; + if (defaultHandler) + reportDefault(parser, enc, s, next); + handleDefault = XML_FALSE; + result = doIgnoreSection(parser, enc, &next, end, nextPtr); + if (!next) { + processor = ignoreSectionProcessor; + return result; + } + } + break; +#endif /* XML_DTD */ + case XML_ROLE_GROUP_OPEN: + if (prologState.level >= groupSize) { + if (groupSize) { + char *temp = (char *)REALLOC(groupConnector, groupSize *= 2); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + groupConnector = temp; + if (dtd->scaffIndex) { + int *temp = (int *)REALLOC(dtd->scaffIndex, + groupSize * sizeof(int)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex = temp; + } + } + else { + groupConnector = (char *)MALLOC(groupSize = 32); + if (!groupConnector) + return XML_ERROR_NO_MEMORY; + } + } + groupConnector[prologState.level] = 0; + if (dtd->in_eldecl) { + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex[dtd->scaffLevel] = myindex; + dtd->scaffLevel++; + dtd->scaffold[myindex].type = XML_CTYPE_SEQ; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_GROUP_SEQUENCE: + if (groupConnector[prologState.level] == '|') + return XML_ERROR_SYNTAX; + groupConnector[prologState.level] = ','; + if (dtd->in_eldecl && elementDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_GROUP_CHOICE: + if (groupConnector[prologState.level] == ',') + return XML_ERROR_SYNTAX; + if (dtd->in_eldecl + && !groupConnector[prologState.level] + && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + != XML_CTYPE_MIXED) + ) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_CHOICE; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + groupConnector[prologState.level] = '|'; + break; + case XML_ROLE_PARAM_ENTITY_REF: +#ifdef XML_DTD + case XML_ROLE_INNER_PARAM_ENTITY_REF: + /* PE references in internal subset are + not allowed within declarations */ + if (prologState.documentEntity && + role == XML_ROLE_INNER_PARAM_ENTITY_REF) + return XML_ERROR_PARAM_ENTITY_REF; + dtd->hasParamEntityRefs = XML_TRUE; + if (!paramEntityParsing) + dtd->keepProcessing = dtd->standalone; + else { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0); + poolDiscard(&dtd->pool); + /* first, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity handler + */ + if (prologState.documentEntity && + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs)) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + dtd->keepProcessing = dtd->standalone; + /* cannot report skipped entities in declarations */ + if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) { + skippedEntityHandler(handlerArg, name, 1); + handleDefault = XML_FALSE; + } + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + result = processInternalParamEntity(parser, entity); + if (result != XML_ERROR_NONE) + return result; + handleDefault = XML_FALSE; + break; + } + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } + entity->open = XML_FALSE; + handleDefault = XML_FALSE; + if (!dtd->paramEntityRead) { + dtd->keepProcessing = dtd->standalone; + break; + } + } + else { + dtd->keepProcessing = dtd->standalone; + break; + } + } +#endif /* XML_DTD */ + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + break; + + /* Element declaration stuff */ + + case XML_ROLE_ELEMENT_NAME: + if (elementDeclHandler) { + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + dtd->scaffLevel = 0; + dtd->scaffCount = 0; + dtd->in_eldecl = XML_TRUE; + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ANY: + case XML_ROLE_CONTENT_EMPTY: + if (dtd->in_eldecl) { + if (elementDeclHandler) { + XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content)); + if (!content) + return XML_ERROR_NO_MEMORY; + content->quant = XML_CQUANT_NONE; + content->name = NULL; + content->numchildren = 0; + content->children = NULL; + content->type = ((role == XML_ROLE_CONTENT_ANY) ? + XML_CTYPE_ANY : + XML_CTYPE_EMPTY); + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, content); + handleDefault = XML_FALSE; + } + dtd->in_eldecl = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_PCDATA: + if (dtd->in_eldecl) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_MIXED; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ELEMENT: + quant = XML_CQUANT_NONE; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_OPT: + quant = XML_CQUANT_OPT; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_REP: + quant = XML_CQUANT_REP; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_PLUS: + quant = XML_CQUANT_PLUS; + elementContent: + if (dtd->in_eldecl) { + ELEMENT_TYPE *el; + const XML_Char *name; + int nameLen; + const char *nxt = (quant == XML_CQUANT_NONE + ? next + : next - enc->minBytesPerChar); + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffold[myindex].type = XML_CTYPE_NAME; + dtd->scaffold[myindex].quant = quant; + el = getElementType(parser, enc, s, nxt); + if (!el) + return XML_ERROR_NO_MEMORY; + name = el->name; + dtd->scaffold[myindex].name = name; + nameLen = 0; + for (; name[nameLen++]; ); + dtd->contentStringLen += nameLen; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_GROUP_CLOSE: + quant = XML_CQUANT_NONE; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_OPT: + quant = XML_CQUANT_OPT; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_REP: + quant = XML_CQUANT_REP; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_PLUS: + quant = XML_CQUANT_PLUS; + closeGroup: + if (dtd->in_eldecl) { + if (elementDeclHandler) + handleDefault = XML_FALSE; + dtd->scaffLevel--; + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant; + if (dtd->scaffLevel == 0) { + if (!handleDefault) { + XML_Content *model = build_model(parser); + if (!model) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, model); + } + dtd->in_eldecl = XML_FALSE; + dtd->contentStringLen = 0; + } + } + break; + /* End element declaration stuff */ + + case XML_ROLE_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_NONE: + switch (tok) { + case XML_TOK_BOM: + handleDefault = XML_FALSE; + break; + } + break; + case XML_ROLE_DOCTYPE_NONE: + if (startDoctypeDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ENTITY_NONE: + if (dtd->keepProcessing && entityDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_NOTATION_NONE: + if (notationDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTLIST_NONE: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ELEMENT_NONE: + if (elementDeclHandler) + handleDefault = XML_FALSE; + break; + } /* end of big switch */ + + if (handleDefault && defaultHandler) + reportDefault(parser, enc, s, next); + + s = next; + tok = XmlPrologTok(enc, s, end, &next); + } + /* not reached */ +} + +static enum XML_Error PTRCALL +epilogProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + processor = epilogProcessor; + eventPtr = s; + for (;;) { + const char *next = NULL; + int tok = XmlPrologTok(encoding, s, end, &next); + eventEndPtr = next; + switch (tok) { + /* report partial linebreak - it might be the last token */ + case -XML_TOK_PROLOG_S: + if (defaultHandler) { + eventEndPtr = next; + reportDefault(parser, encoding, s, next); + } + if (nextPtr) + *nextPtr = next; + return XML_ERROR_NONE; + case XML_TOK_NONE: + if (nextPtr) + *nextPtr = s; + return XML_ERROR_NONE; + case XML_TOK_PROLOG_S: + if (defaultHandler) + reportDefault(parser, encoding, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_INVALID: + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + default: + return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; + } + eventPtr = s = next; + } +} + +#ifdef XML_DTD + +static enum XML_Error +processInternalParamEntity(XML_Parser parser, ENTITY *entity) +{ + const char *s, *end, *next; + int tok; + enum XML_Error result; + OPEN_INTERNAL_ENTITY openEntity; + entity->open = XML_TRUE; + openEntity.next = openInternalEntities; + openInternalEntities = &openEntity; + openEntity.entity = entity; + openEntity.internalEventPtr = NULL; + openEntity.internalEventEndPtr = NULL; + s = (char *)entity->textPtr; + end = (char *)(entity->textPtr + entity->textLen); + tok = XmlPrologTok(internalEncoding, s, end, &next); + result = doProlog(parser, internalEncoding, s, end, tok, next, 0); + entity->open = XML_FALSE; + openInternalEntities = openEntity.next; + return result; +} + +#endif /* XML_DTD */ + +static enum XML_Error PTRCALL +errorProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + return errorCode; +} + +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, + end, pool); + if (result) + return result; + if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) + poolChop(pool); + if (!poolAppendChar(pool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + return XML_ERROR_NONE; +} + +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + for (;;) { + const char *next; + int tok = XmlAttributeValueTok(enc, ptr, end, &next); + switch (tok) { + case XML_TOK_NONE: + return XML_ERROR_NONE; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, ptr); + if (n < 0) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + if (!isCdata + && n == 0x20 /* space */ + && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + for (i = 0; i < n; i++) { + if (!poolAppendChar(pool, buf[i])) + return XML_ERROR_NO_MEMORY; + } + } + break; + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, ptr, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_TRAILING_CR: + next = ptr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_ATTRIBUTE_VALUE_S: + case XML_TOK_DATA_NEWLINE: + if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + if (!poolAppendChar(pool, 0x20)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + char checkEntityDecl; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (!poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; + } + name = poolStoreString(&temp2Pool, enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0); + poolDiscard(&temp2Pool); + /* first, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the default handler (if called from content) + */ + if (pool == &dtd->pool) /* are we called from prolog? */ + checkEntityDecl = +#ifdef XML_DTD + prologState.documentEntity && +#endif /* XML_DTD */ + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs); + else /* if (pool == &tempPool): we are called from content */ + checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone; + if (checkEntityDecl) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + /* cannot report skipped entity here - see comments on + skippedEntityHandler + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + if ((pool == &tempPool) && defaultHandler) + reportDefault(parser, enc, ptr, next); + break; + } + if (entity->open) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + if (entity->notation) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BINARY_ENTITY_REF; + } + if (!entity->textPtr) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } + else { + enum XML_Error result; + const XML_Char *textEnd = entity->textPtr + entity->textLen; + entity->open = XML_TRUE; + result = appendAttributeValue(parser, internalEncoding, isCdata, + (char *)entity->textPtr, + (char *)textEnd, pool); + entity->open = XML_FALSE; + if (result) + return result; + } + } + break; + default: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_UNEXPECTED_STATE; + } + ptr = next; + } + /* not reached */ +} + +static enum XML_Error +storeEntityValue(XML_Parser parser, + const ENCODING *enc, + const char *entityTextPtr, + const char *entityTextEnd) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + STRING_POOL *pool = &(dtd->entityValuePool); + enum XML_Error result = XML_ERROR_NONE; +#ifdef XML_DTD + int oldInEntityValue = prologState.inEntityValue; + prologState.inEntityValue = 1; +#endif /* XML_DTD */ + /* never return Null for the value argument in EntityDeclHandler, + since this would indicate an external entity; therefore we + have to make sure that entityValuePool.start is not null */ + if (!pool->blocks) { + if (!poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + } + + for (;;) { + const char *next; + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: +#ifdef XML_DTD + if (isParamEntity || enc != encoding) { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&tempPool, enc, + entityTextPtr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0); + poolDiscard(&tempPool); + if (!entity) { + /* not a well-formedness error - see XML 1.0: WFC Entity Declared */ + /* cannot report skipped entity here - see comments on + skippedEntityHandler + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + dtd->keepProcessing = dtd->standalone; + goto endEntityValue; + } + if (entity->open) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_RECURSIVE_ENTITY_REF; + goto endEntityValue; + } + if (entity->systemId) { + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; + goto endEntityValue; + } + entity->open = XML_FALSE; + if (!dtd->paramEntityRead) + dtd->keepProcessing = dtd->standalone; + } + else + dtd->keepProcessing = dtd->standalone; + } + else { + entity->open = XML_TRUE; + result = storeEntityValue(parser, + internalEncoding, + (char *)entity->textPtr, + (char *)(entity->textPtr + + entity->textLen)); + entity->open = XML_FALSE; + if (result) + goto endEntityValue; + } + break; + } +#endif /* XML_DTD */ + /* in the internal subset, PE references are not legal + within markup declarations, e.g entity values in this case */ + eventPtr = entityTextPtr; + result = XML_ERROR_PARAM_ENTITY_REF; + goto endEntityValue; + case XML_TOK_NONE: + result = XML_ERROR_NONE; + goto endEntityValue; + case XML_TOK_ENTITY_REF: + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, entityTextPtr, next)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + break; + case XML_TOK_TRAILING_CR: + next = entityTextPtr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_DATA_NEWLINE: + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = 0xA; + break; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, entityTextPtr); + if (n < 0) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + for (i = 0; i < n; i++) { + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = buf[i]; + } + } + break; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + default: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_UNEXPECTED_STATE; + goto endEntityValue; + } + entityTextPtr = next; + } +endEntityValue: +#ifdef XML_DTD + prologState.inEntityValue = oldInEntityValue; +#endif /* XML_DTD */ + return result; +} + +static void FASTCALL +normalizeLines(XML_Char *s) +{ + XML_Char *p; + for (;; s++) { + if (*s == XML_T('\0')) + return; + if (*s == 0xD) + break; + } + p = s; + do { + if (*s == 0xD) { + *p++ = 0xA; + if (*++s == 0xA) + s++; + } + else + *p++ = *s++; + } while (*s); + *p = XML_T('\0'); +} + +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + const XML_Char *target; + XML_Char *data; + const char *tem; + if (!processingInstructionHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + start += enc->minBytesPerChar * 2; + tem = start + XmlNameLength(enc, start); + target = poolStoreString(&tempPool, enc, start, tem); + if (!target) + return 0; + poolFinish(&tempPool); + data = poolStoreString(&tempPool, enc, + XmlSkipS(enc, tem), + end - enc->minBytesPerChar*2); + if (!data) + return 0; + normalizeLines(data); + processingInstructionHandler(handlerArg, target, data); + poolClear(&tempPool); + return 1; +} + +static int +reportComment(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + XML_Char *data; + if (!commentHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + data = poolStoreString(&tempPool, + enc, + start + enc->minBytesPerChar * 4, + end - enc->minBytesPerChar * 3); + if (!data) + return 0; + normalizeLines(data); + commentHandler(handlerArg, data); + poolClear(&tempPool); + return 1; +} + +static void +reportDefault(XML_Parser parser, const ENCODING *enc, + const char *s, const char *end) +{ + if (MUST_CONVERT(enc, s)) { + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + do { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + defaultHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf); + *eventPP = s; + } while (s != end); + } + else + defaultHandler(handlerArg, (XML_Char *)s, (XML_Char *)end - (XML_Char *)s); +} + + +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, + XML_Bool isId, const XML_Char *value, XML_Parser parser) +{ + DEFAULT_ATTRIBUTE *att; + if (value || isId) { + /* The handling of default attributes gets messed up if we have + a default which duplicates a non-default. */ + int i; + for (i = 0; i < type->nDefaultAtts; i++) + if (attId == type->defaultAtts[i].id) + return 1; + if (isId && !type->idAtt && !attId->xmlns) + type->idAtt = attId; + } + if (type->nDefaultAtts == type->allocDefaultAtts) { + if (type->allocDefaultAtts == 0) { + type->allocDefaultAtts = 8; + type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts + * sizeof(DEFAULT_ATTRIBUTE)); + if (!type->defaultAtts) + return 0; + } + else { + DEFAULT_ATTRIBUTE *temp; + int count = type->allocDefaultAtts * 2; + temp = (DEFAULT_ATTRIBUTE *) + REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE))); + if (temp == NULL) + return 0; + type->allocDefaultAtts = count; + type->defaultAtts = temp; + } + } + att = type->defaultAtts + type->nDefaultAtts; + att->id = attId; + att->value = value; + att->isCdata = isCdata; + if (!isCdata) + attId->maybeTokenized = XML_TRUE; + type->nDefaultAtts += 1; + return 1; +} + +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name; + for (name = elementType->name; *name; name++) { + if (*name == XML_T(':')) { + PREFIX *prefix; + const XML_Char *s; + for (s = elementType->name; s != name; s++) { + if (!poolAppendChar(&dtd->pool, *s)) + return 0; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return 0; + prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (!prefix) + return 0; + if (prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + elementType->prefix = prefix; + + } + } + return 1; +} + +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ATTRIBUTE_ID *id; + const XML_Char *name; + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + name = poolStoreString(&dtd->pool, enc, start, end); + if (!name) + return NULL; + /* skip quotation mark - its storage will be re-used (like in name[-1]) */ + ++name; + id = (ATTRIBUTE_ID *)lookup(&dtd->attributeIds, name, sizeof(ATTRIBUTE_ID)); + if (!id) + return NULL; + if (id->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!ns) + ; + else if (name[0] == XML_T('x') + && name[1] == XML_T('m') + && name[2] == XML_T('l') + && name[3] == XML_T('n') + && name[4] == XML_T('s') + && (name[5] == XML_T('\0') || name[5] == XML_T(':'))) { + if (name[5] == XML_T('\0')) + id->prefix = &dtd->defaultPrefix; + else + id->prefix = (PREFIX *)lookup(&dtd->prefixes, name + 6, sizeof(PREFIX)); + id->xmlns = XML_TRUE; + } + else { + int i; + for (i = 0; name[i]; i++) { + /* attributes without prefix are *not* in the default namespace */ + if (name[i] == XML_T(':')) { + int j; + for (j = 0; j < i; j++) { + if (!poolAppendChar(&dtd->pool, name[j])) + return NULL; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + id->prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (id->prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + break; + } + } + } + } + return id; +} + +#define CONTEXT_SEP XML_T('\f') + +static const XML_Char * +getContext(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + HASH_TABLE_ITER iter; + XML_Bool needSep = XML_FALSE; + + if (dtd->defaultPrefix.binding) { + int i; + int len; + if (!poolAppendChar(&tempPool, XML_T('='))) + return NULL; + len = dtd->defaultPrefix.binding->uriLen; + if (namespaceSeparator != XML_T('\0')) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + hashTableIterInit(&iter, &(dtd->prefixes)); + for (;;) { + int i; + int len; + const XML_Char *s; + PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); + if (!prefix) + break; + if (!prefix->binding) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = prefix->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return NULL; + if (!poolAppendChar(&tempPool, XML_T('='))) + return NULL; + len = prefix->binding->uriLen; + if (namespaceSeparator != XML_T('\0')) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, prefix->binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + + hashTableIterInit(&iter, &(dtd->generalEntities)); + for (;;) { + const XML_Char *s; + ENTITY *e = (ENTITY *)hashTableIterNext(&iter); + if (!e) + break; + if (!e->open) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = e->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + needSep = XML_TRUE; + } + + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return NULL; + return tempPool.start; +} + +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *s = context; + + while (*context != XML_T('\0')) { + if (*s == CONTEXT_SEP || *s == XML_T('\0')) { + ENTITY *e; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + e = (ENTITY *)lookup(&dtd->generalEntities, poolStart(&tempPool), 0); + if (e) + e->open = XML_TRUE; + if (*s != XML_T('\0')) + s++; + context = s; + poolDiscard(&tempPool); + } + else if (*s == XML_T('=')) { + PREFIX *prefix; + if (poolLength(&tempPool) == 0) + prefix = &dtd->defaultPrefix; + else { + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&tempPool), + sizeof(PREFIX)); + if (!prefix) + return XML_FALSE; + if (prefix->name == poolStart(&tempPool)) { + prefix->name = poolCopyString(&dtd->pool, prefix->name); + if (!prefix->name) + return XML_FALSE; + } + poolDiscard(&tempPool); + } + for (context = s + 1; + *context != CONTEXT_SEP && *context != XML_T('\0'); + context++) + if (!poolAppendChar(&tempPool, *context)) + return XML_FALSE; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + if (addBinding(parser, prefix, 0, poolStart(&tempPool), + &inheritedBindings) != XML_ERROR_NONE) + return XML_FALSE; + poolDiscard(&tempPool); + if (*context != XML_T('\0')) + ++context; + s = context; + } + else { + if (!poolAppendChar(&tempPool, *s)) + return XML_FALSE; + s++; + } + } + return XML_TRUE; +} + +static void FASTCALL +normalizePublicId(XML_Char *publicId) +{ + XML_Char *p = publicId; + XML_Char *s; + for (s = publicId; *s; s++) { + switch (*s) { + case 0x20: + case 0xD: + case 0xA: + if (p != publicId && p[-1] != 0x20) + *p++ = 0x20; + break; + default: + *p++ = *s; + } + } + if (p != publicId && p[-1] == 0x20) + --p; + *p = XML_T('\0'); +} + +static DTD * +dtdCreate(const XML_Memory_Handling_Suite *ms) +{ + DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD)); + if (p == NULL) + return p; + poolInit(&(p->pool), ms); +#ifdef XML_DTD + poolInit(&(p->entityValuePool), ms); +#endif /* XML_DTD */ + hashTableInit(&(p->generalEntities), ms); + hashTableInit(&(p->elementTypes), ms); + hashTableInit(&(p->attributeIds), ms); + hashTableInit(&(p->prefixes), ms); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableInit(&(p->paramEntities), ms); +#endif /* XML_DTD */ + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + p->scaffIndex = NULL; + p->scaffold = NULL; + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; + return p; +} + +static void +dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableClear(&(p->generalEntities)); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableClear(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableClear(&(p->elementTypes)); + hashTableClear(&(p->attributeIds)); + hashTableClear(&(p->prefixes)); + poolClear(&(p->pool)); +#ifdef XML_DTD + poolClear(&(p->entityValuePool)); +#endif /* XML_DTD */ + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + + ms->free_fcn(p->scaffIndex); + p->scaffIndex = NULL; + ms->free_fcn(p->scaffold); + p->scaffold = NULL; + + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; +} + +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); +#ifdef XML_DTD + hashTableDestroy(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableDestroy(&(p->elementTypes)); + hashTableDestroy(&(p->attributeIds)); + hashTableDestroy(&(p->prefixes)); + poolDestroy(&(p->pool)); +#ifdef XML_DTD + poolDestroy(&(p->entityValuePool)); +#endif /* XML_DTD */ + if (isDocEntity) { + ms->free_fcn(p->scaffIndex); + ms->free_fcn(p->scaffold); + } + ms->free_fcn(p); +} + +/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. + The new DTD has already been initialized. +*/ +static int +dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + + /* Copy the prefix table. */ + + hashTableIterInit(&iter, &(oldDtd->prefixes)); + for (;;) { + const XML_Char *name; + const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); + if (!oldP) + break; + name = poolCopyString(&(newDtd->pool), oldP->name); + if (!name) + return 0; + if (!lookup(&(newDtd->prefixes), name, sizeof(PREFIX))) + return 0; + } + + hashTableIterInit(&iter, &(oldDtd->attributeIds)); + + /* Copy the attribute id table. */ + + for (;;) { + ATTRIBUTE_ID *newA; + const XML_Char *name; + const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); + + if (!oldA) + break; + /* Remember to allocate the scratch byte before the name. */ + if (!poolAppendChar(&(newDtd->pool), XML_T('\0'))) + return 0; + name = poolCopyString(&(newDtd->pool), oldA->name); + if (!name) + return 0; + ++name; + newA = (ATTRIBUTE_ID *)lookup(&(newDtd->attributeIds), name, + sizeof(ATTRIBUTE_ID)); + if (!newA) + return 0; + newA->maybeTokenized = oldA->maybeTokenized; + if (oldA->prefix) { + newA->xmlns = oldA->xmlns; + if (oldA->prefix == &oldDtd->defaultPrefix) + newA->prefix = &newDtd->defaultPrefix; + else + newA->prefix = (PREFIX *)lookup(&(newDtd->prefixes), + oldA->prefix->name, 0); + } + } + + /* Copy the element type table. */ + + hashTableIterInit(&iter, &(oldDtd->elementTypes)); + + for (;;) { + int i; + ELEMENT_TYPE *newE; + const XML_Char *name; + const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(&(newDtd->pool), oldE->name); + if (!name) + return 0; + newE = (ELEMENT_TYPE *)lookup(&(newDtd->elementTypes), name, + sizeof(ELEMENT_TYPE)); + if (!newE) + return 0; + if (oldE->nDefaultAtts) { + newE->defaultAtts = (DEFAULT_ATTRIBUTE *) + ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (!newE->defaultAtts) { + ms->free_fcn(newE); + return 0; + } + } + if (oldE->idAtt) + newE->idAtt = (ATTRIBUTE_ID *) + lookup(&(newDtd->attributeIds), oldE->idAtt->name, 0); + newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts; + if (oldE->prefix) + newE->prefix = (PREFIX *)lookup(&(newDtd->prefixes), + oldE->prefix->name, 0); + for (i = 0; i < newE->nDefaultAtts; i++) { + newE->defaultAtts[i].id = (ATTRIBUTE_ID *) + lookup(&(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); + newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; + if (oldE->defaultAtts[i].value) { + newE->defaultAtts[i].value + = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); + if (!newE->defaultAtts[i].value) + return 0; + } + else + newE->defaultAtts[i].value = NULL; + } + } + + /* Copy the entity tables. */ + if (!copyEntityTable(&(newDtd->generalEntities), + &(newDtd->pool), + &(oldDtd->generalEntities))) + return 0; + +#ifdef XML_DTD + if (!copyEntityTable(&(newDtd->paramEntities), + &(newDtd->pool), + &(oldDtd->paramEntities))) + return 0; + newDtd->paramEntityRead = oldDtd->paramEntityRead; +#endif /* XML_DTD */ + + newDtd->keepProcessing = oldDtd->keepProcessing; + newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs; + newDtd->standalone = oldDtd->standalone; + + /* Don't want deep copying for scaffolding */ + newDtd->in_eldecl = oldDtd->in_eldecl; + newDtd->scaffold = oldDtd->scaffold; + newDtd->contentStringLen = oldDtd->contentStringLen; + newDtd->scaffSize = oldDtd->scaffSize; + newDtd->scaffLevel = oldDtd->scaffLevel; + newDtd->scaffIndex = oldDtd->scaffIndex; + + return 1; +} /* End dtdCopy */ + +static int +copyEntityTable(HASH_TABLE *newTable, + STRING_POOL *newPool, + const HASH_TABLE *oldTable) +{ + HASH_TABLE_ITER iter; + const XML_Char *cachedOldBase = NULL; + const XML_Char *cachedNewBase = NULL; + + hashTableIterInit(&iter, oldTable); + + for (;;) { + ENTITY *newE; + const XML_Char *name; + const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(newPool, oldE->name); + if (!name) + return 0; + newE = (ENTITY *)lookup(newTable, name, sizeof(ENTITY)); + if (!newE) + return 0; + if (oldE->systemId) { + const XML_Char *tem = poolCopyString(newPool, oldE->systemId); + if (!tem) + return 0; + newE->systemId = tem; + if (oldE->base) { + if (oldE->base == cachedOldBase) + newE->base = cachedNewBase; + else { + cachedOldBase = oldE->base; + tem = poolCopyString(newPool, cachedOldBase); + if (!tem) + return 0; + cachedNewBase = newE->base = tem; + } + } + if (oldE->publicId) { + tem = poolCopyString(newPool, oldE->publicId); + if (!tem) + return 0; + newE->publicId = tem; + } + } + else { + const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr, + oldE->textLen); + if (!tem) + return 0; + newE->textPtr = tem; + newE->textLen = oldE->textLen; + } + if (oldE->notation) { + const XML_Char *tem = poolCopyString(newPool, oldE->notation); + if (!tem) + return 0; + newE->notation = tem; + } + newE->is_param = oldE->is_param; + newE->is_internal = oldE->is_internal; + } + return 1; +} + +#define INIT_POWER 6 + +static XML_Bool FASTCALL +keyeq(KEY s1, KEY s2) +{ + for (; *s1 == *s2; s1++, s2++) + if (*s1 == 0) + return XML_TRUE; + return XML_FALSE; +} + +static unsigned long FASTCALL +hash(KEY s) +{ + unsigned long h = 0; + while (*s) + h = CHAR_HASH(h, *s++); + return h; +} + +static NAMED * +lookup(HASH_TABLE *table, KEY name, size_t createSize) +{ + size_t i; + if (table->size == 0) { + size_t tsize; + if (!createSize) + return NULL; + table->power = INIT_POWER; + /* table->size is a power of 2 */ + table->size = (size_t)1 << INIT_POWER; + tsize = table->size * sizeof(NAMED *); + table->v = (NAMED **)table->mem->malloc_fcn(tsize); + if (!table->v) + return NULL; + memset(table->v, 0, tsize); + i = hash(name) & ((unsigned long)table->size - 1); + } + else { + unsigned long h = hash(name); + unsigned long mask = (unsigned long)table->size - 1; + unsigned char step = 0; + i = h & mask; + while (table->v[i]) { + if (keyeq(name, table->v[i]->name)) + return table->v[i]; + if (!step) + step = PROBE_STEP(h, mask, table->power); + i < step ? (i += table->size - step) : (i -= step); + } + if (!createSize) + return NULL; + + /* check for overflow (table is half full) */ + if (table->used >> (table->power - 1)) { + unsigned char newPower = table->power + 1; + size_t newSize = (size_t)1 << newPower; + unsigned long newMask = (unsigned long)newSize - 1; + size_t tsize = newSize * sizeof(NAMED *); + NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize); + if (!newV) + return NULL; + memset(newV, 0, tsize); + for (i = 0; i < table->size; i++) + if (table->v[i]) { + unsigned long newHash = hash(table->v[i]->name); + size_t j = newHash & newMask; + step = 0; + while (newV[j]) { + if (!step) + step = PROBE_STEP(newHash, newMask, newPower); + j < step ? (j += newSize - step) : (j -= step); + } + newV[j] = table->v[i]; + } + table->mem->free_fcn(table->v); + table->v = newV; + table->power = newPower; + table->size = newSize; + i = h & newMask; + step = 0; + while (table->v[i]) { + if (!step) + step = PROBE_STEP(h, newMask, newPower); + i < step ? (i += newSize - step) : (i -= step); + } + } + } + table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize); + if (!table->v[i]) + return NULL; + memset(table->v[i], 0, createSize); + table->v[i]->name = name; + (table->used)++; + return table->v[i]; +} + +static void FASTCALL +hashTableClear(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) { + table->mem->free_fcn(table->v[i]); + table->v[i] = NULL; + } + table->used = 0; +} + +static void FASTCALL +hashTableDestroy(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) + table->mem->free_fcn(table->v[i]); + table->mem->free_fcn(table->v); +} + +static void FASTCALL +hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) +{ + p->power = 0; + p->size = 0; + p->used = 0; + p->v = NULL; + p->mem = ms; +} + +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) +{ + iter->p = table->v; + iter->end = iter->p + table->size; +} + +static NAMED * FASTCALL +hashTableIterNext(HASH_TABLE_ITER *iter) +{ + while (iter->p != iter->end) { + NAMED *tem = *(iter->p)++; + if (tem) + return tem; + } + return NULL; +} + +static void FASTCALL +poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) +{ + pool->blocks = NULL; + pool->freeBlocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; + pool->mem = ms; +} + +static void FASTCALL +poolClear(STRING_POOL *pool) +{ + if (!pool->freeBlocks) + pool->freeBlocks = pool->blocks; + else { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + p->next = pool->freeBlocks; + pool->freeBlocks = p; + p = tem; + } + } + pool->blocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; +} + +static void FASTCALL +poolDestroy(STRING_POOL *pool) +{ + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } +} + +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (;;) { + XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); + if (ptr == end) + break; + if (!poolGrow(pool)) + return NULL; + } + return pool->start; +} + +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s) +{ + do { + if (!poolAppendChar(pool, *s)) + return NULL; + } while (*s++); + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (; n > 0; --n, s++) { + if (!poolAppendChar(pool, *s)) + return NULL; + } + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s) +{ + while (*s) { + if (!poolAppendChar(pool, *s)) + return NULL; + s++; + } + return pool->start; +} + +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!poolAppend(pool, enc, ptr, end)) + return NULL; + if (pool->ptr == pool->end && !poolGrow(pool)) + return NULL; + *(pool->ptr)++ = 0; + return pool->start; +} + +static XML_Bool FASTCALL +poolGrow(STRING_POOL *pool) +{ + if (pool->freeBlocks) { + if (pool->start == 0) { + pool->blocks = pool->freeBlocks; + pool->freeBlocks = pool->freeBlocks->next; + pool->blocks->next = NULL; + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + pool->ptr = pool->start; + return XML_TRUE; + } + if (pool->end - pool->start < pool->freeBlocks->size) { + BLOCK *tem = pool->freeBlocks->next; + pool->freeBlocks->next = pool->blocks; + pool->blocks = pool->freeBlocks; + pool->freeBlocks = tem; + memcpy(pool->blocks->s, pool->start, + (pool->end - pool->start) * sizeof(XML_Char)); + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + return XML_TRUE; + } + } + if (pool->blocks && pool->start == pool->blocks->s) { + int blockSize = (pool->end - pool->start)*2; + pool->blocks = (BLOCK *) + pool->mem->realloc_fcn(pool->blocks, + (offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char))); + if (pool->blocks == NULL) + return XML_FALSE; + pool->blocks->size = blockSize; + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + blockSize; + } + else { + BLOCK *tem; + int blockSize = pool->end - pool->start; + if (blockSize < INIT_BLOCK_SIZE) + blockSize = INIT_BLOCK_SIZE; + else + blockSize *= 2; + tem = (BLOCK *)pool->mem->malloc_fcn(offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char)); + if (!tem) + return XML_FALSE; + tem->size = blockSize; + tem->next = pool->blocks; + pool->blocks = tem; + if (pool->ptr != pool->start) + memcpy(tem->s, pool->start, + (pool->ptr - pool->start) * sizeof(XML_Char)); + pool->ptr = tem->s + (pool->ptr - pool->start); + pool->start = tem->s; + pool->end = tem->s + blockSize; + } + return XML_TRUE; +} + +static int FASTCALL +nextScaffoldPart(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + CONTENT_SCAFFOLD * me; + int next; + + if (!dtd->scaffIndex) { + dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int)); + if (!dtd->scaffIndex) + return -1; + dtd->scaffIndex[0] = 0; + } + + if (dtd->scaffCount >= dtd->scaffSize) { + CONTENT_SCAFFOLD *temp; + if (dtd->scaffold) { + temp = (CONTENT_SCAFFOLD *) + REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize *= 2; + } + else { + temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS + * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; + } + dtd->scaffold = temp; + } + next = dtd->scaffCount++; + me = &dtd->scaffold[next]; + if (dtd->scaffLevel) { + CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel-1]]; + if (parent->lastchild) { + dtd->scaffold[parent->lastchild].nextsib = next; + } + if (!parent->childcnt) + parent->firstchild = next; + parent->lastchild = next; + parent->childcnt++; + } + me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0; + return next; +} + +static void +build_node(XML_Parser parser, + int src_node, + XML_Content *dest, + XML_Content **contpos, + XML_Char **strpos) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + dest->type = dtd->scaffold[src_node].type; + dest->quant = dtd->scaffold[src_node].quant; + if (dest->type == XML_CTYPE_NAME) { + const XML_Char *src; + dest->name = *strpos; + src = dtd->scaffold[src_node].name; + for (;;) { + *(*strpos)++ = *src; + if (!*src) + break; + src++; + } + dest->numchildren = 0; + dest->children = NULL; + } + else { + unsigned int i; + int cn; + dest->numchildren = dtd->scaffold[src_node].childcnt; + dest->children = *contpos; + *contpos += dest->numchildren; + for (i = 0, cn = dtd->scaffold[src_node].firstchild; + i < dest->numchildren; + i++, cn = dtd->scaffold[cn].nextsib) { + build_node(parser, cn, &(dest->children[i]), contpos, strpos); + } + dest->name = NULL; + } +} + +static XML_Content * +build_model (XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + XML_Content *ret; + XML_Content *cpos; + XML_Char * str; + int allocsize = (dtd->scaffCount * sizeof(XML_Content) + + (dtd->contentStringLen * sizeof(XML_Char))); + + ret = (XML_Content *)MALLOC(allocsize); + if (!ret) + return NULL; + + str = (XML_Char *) (&ret[dtd->scaffCount]); + cpos = &ret[1]; + + build_node(parser, 0, ret, &cpos, &str); + return ret; +} + +static ELEMENT_TYPE * +getElementType(XML_Parser parser, + const ENCODING *enc, + const char *ptr, + const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end); + ELEMENT_TYPE *ret; + + if (!name) + return NULL; + ret = (ELEMENT_TYPE *) lookup(&dtd->elementTypes, name, sizeof(ELEMENT_TYPE)); + if (!ret) + return NULL; + if (ret->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!setElementTypePrefix(parser, ret)) + return NULL; + } + return ret; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlrole.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlrole.c new file mode 100644 index 00000000..da6bc35e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlrole.c @@ -0,0 +1,1323 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifdef COMPILED_FROM_DSP +#include "winconfig.h" +#elif defined(MACOS_CLASSIC) +#include "macconfig.h" +#else +#ifdef HAVE_EXPAT_CONFIG_H +#include +#endif +#endif /* ndef COMPILED_FROM_DSP */ + +#include "internal.h" +#include "xmlrole.h" +#include "ascii.h" + +/* Doesn't check: + + that ,| are not mixed in a model group + content of literals + +*/ + +static const char KW_ANY[] = { + ASCII_A, ASCII_N, ASCII_Y, '\0' }; +static const char KW_ATTLIST[] = { + ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' }; +static const char KW_CDATA[] = { + ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_DOCTYPE[] = { + ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' }; +static const char KW_ELEMENT[] = { + ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' }; +static const char KW_EMPTY[] = { + ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' }; +static const char KW_ENTITIES[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, + '\0' }; +static const char KW_ENTITY[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; +static const char KW_FIXED[] = { + ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' }; +static const char KW_ID[] = { + ASCII_I, ASCII_D, '\0' }; +static const char KW_IDREF[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; +static const char KW_IDREFS[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; +static const char KW_IGNORE[] = { + ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' }; +static const char KW_IMPLIED[] = { + ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' }; +static const char KW_INCLUDE[] = { + ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' }; +static const char KW_NDATA[] = { + ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_NMTOKEN[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; +static const char KW_NMTOKENS[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, + '\0' }; +static const char KW_NOTATION[] = + { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, + '\0' }; +static const char KW_PCDATA[] = { + ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_PUBLIC[] = { + ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' }; +static const char KW_REQUIRED[] = { + ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D, + '\0' }; +static const char KW_SYSTEM[] = { + ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' }; + +#ifndef MIN_BYTES_PER_CHAR +#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) +#endif + +#ifdef XML_DTD +#define setTopLevel(state) \ + ((state)->handler = ((state)->documentEntity \ + ? internalSubset \ + : externalSubset1)) +#else /* not XML_DTD */ +#define setTopLevel(state) ((state)->handler = internalSubset) +#endif /* not XML_DTD */ + +typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + +static PROLOG_HANDLER + prolog0, prolog1, prolog2, + doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, + internalSubset, + entity0, entity1, entity2, entity3, entity4, entity5, entity6, + entity7, entity8, entity9, entity10, + notation0, notation1, notation2, notation3, notation4, + attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6, + attlist7, attlist8, attlist9, + element0, element1, element2, element3, element4, element5, element6, + element7, +#ifdef XML_DTD + externalSubset0, externalSubset1, + condSect0, condSect1, condSect2, +#endif /* XML_DTD */ + declClose, + error; + +static int FASTCALL common(PROLOG_STATE *state, int tok); + +static int PTRCALL +prolog0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_XML_DECL: + state->handler = prolog1; + return XML_ROLE_XML_DECL; + case XML_TOK_PI: + state->handler = prolog1; + return XML_ROLE_PI; + case XML_TOK_COMMENT: + state->handler = prolog1; + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +doctype0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = doctype1; + return XML_ROLE_DOCTYPE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +doctype1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = doctype3; + return XML_ROLE_DOCTYPE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = doctype2; + return XML_ROLE_DOCTYPE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +doctype2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype3; + return XML_ROLE_DOCTYPE_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype4; + return XML_ROLE_DOCTYPE_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +doctype5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +internalSubset(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ENTITY)) { + state->handler = entity0; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ATTLIST)) { + state->handler = attlist0; + return XML_ROLE_ATTLIST_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ELEMENT)) { + state->handler = element0; + return XML_ROLE_ELEMENT_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_NOTATION)) { + state->handler = notation0; + return XML_ROLE_NOTATION_NONE; + } + break; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_PARAM_ENTITY_REF: + return XML_ROLE_PARAM_ENTITY_REF; + case XML_TOK_CLOSE_BRACKET: + state->handler = doctype5; + return XML_ROLE_DOCTYPE_NONE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +externalSubset0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + state->handler = externalSubset1; + if (tok == XML_TOK_XML_DECL) + return XML_ROLE_TEXT_DECL; + return externalSubset1(state, tok, ptr, end, enc); +} + +static int PTRCALL +externalSubset1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_COND_SECT_OPEN: + state->handler = condSect0; + return XML_ROLE_NONE; + case XML_TOK_COND_SECT_CLOSE: + if (state->includeLevel == 0) + break; + state->includeLevel -= 1; + return XML_ROLE_NONE; + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_BRACKET: + break; + case XML_TOK_NONE: + if (state->includeLevel) + break; + return XML_ROLE_NONE; + default: + return internalSubset(state, tok, ptr, end, enc); + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +entity0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_PERCENT: + state->handler = entity1; + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity2; + return XML_ROLE_GENERAL_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity7; + return XML_ROLE_PARAM_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity4; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity3; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity4; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity5; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) { + state->handler = entity6; + return XML_ROLE_ENTITY_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +entity6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity9; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity8; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity9; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity10; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity10(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + } + return common(state, tok); +} + +static int PTRCALL +notation0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + state->handler = notation1; + return XML_ROLE_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +notation1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = notation3; + return XML_ROLE_NOTATION_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = notation2; + return XML_ROLE_NOTATION_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +notation2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = notation4; + return XML_ROLE_NOTATION_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_NOTATION_NO_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +attlist0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist1; + return XML_ROLE_ATTLIST_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist2; + return XML_ROLE_ATTRIBUTE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + { + static const char *types[] = { + KW_CDATA, + KW_ID, + KW_IDREF, + KW_IDREFS, + KW_ENTITY, + KW_ENTITIES, + KW_NMTOKEN, + KW_NMTOKENS, + }; + int i; + for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++) + if (XmlNameMatchesAscii(enc, ptr, end, types[i])) { + state->handler = attlist8; + return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; + } + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) { + state->handler = attlist5; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NMTOKEN: + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist4; + return XML_ROLE_ATTRIBUTE_ENUM_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OPEN_PAREN: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + state->handler = attlist7; + return XML_ROLE_ATTRIBUTE_NOTATION_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +/* default value */ +static int PTRCALL +attlist8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_IMPLIED)) { + state->handler = attlist1; + return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_REQUIRED)) { + state->handler = attlist1; + return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_FIXED)) { + state->handler = attlist9; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_FIXED_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +element0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element1; + return XML_ROLE_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +element1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_EMPTY; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_ANY; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = element2; + state->level = 1; + return XML_ROLE_GROUP_OPEN; + } + return common(state, tok); +} + +static int PTRCALL +element2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_PCDATA)) { + state->handler = element3; + return XML_ROLE_CONTENT_PCDATA; + } + break; + case XML_TOK_OPEN_PAREN: + state->level = 2; + state->handler = element6; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element5; + return XML_ROLE_CONTENT_ELEMENT; + } + return common(state, tok); +} + +static int PTRCALL +element5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_OPEN_PAREN: + state->level += 1; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_CLOSE_PAREN_QUESTION: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_OPT; + case XML_TOK_CLOSE_PAREN_PLUS: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_PLUS; + case XML_TOK_COMMA: + state->handler = element6; + return XML_ROLE_GROUP_SEQUENCE; + case XML_TOK_OR: + state->handler = element6; + return XML_ROLE_GROUP_CHOICE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +condSect0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) { + state->handler = condSect1; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) { + state->handler = condSect2; + return XML_ROLE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +condSect1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + state->includeLevel += 1; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +static int PTRCALL +condSect2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + return XML_ROLE_IGNORE_SECT; + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +declClose(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return state->role_none; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return state->role_none; + } + return common(state, tok); +} + +static int PTRCALL +error(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + return XML_ROLE_NONE; +} + +static int FASTCALL +common(PROLOG_STATE *state, int tok) +{ +#ifdef XML_DTD + if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF) + return XML_ROLE_INNER_PARAM_ENTITY_REF; +#endif + state->handler = error; + return XML_ROLE_ERROR; +} + +void +XmlPrologStateInit(PROLOG_STATE *state) +{ + state->handler = prolog0; +#ifdef XML_DTD + state->documentEntity = 1; + state->includeLevel = 0; + state->inEntityValue = 0; +#endif /* XML_DTD */ +} + +#ifdef XML_DTD + +void +XmlPrologStateInitExternalEntity(PROLOG_STATE *state) +{ + state->handler = externalSubset0; + state->documentEntity = 0; + state->includeLevel = 0; +} + +#endif /* XML_DTD */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlrole.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlrole.h new file mode 100644 index 00000000..25be6b02 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmlrole.h @@ -0,0 +1,114 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlRole_INCLUDED +#define XmlRole_INCLUDED 1 + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt +#endif + +#include "xmltok.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + XML_ROLE_ERROR = -1, + XML_ROLE_NONE = 0, + XML_ROLE_XML_DECL, + XML_ROLE_INSTANCE_START, + XML_ROLE_DOCTYPE_NONE, + XML_ROLE_DOCTYPE_NAME, + XML_ROLE_DOCTYPE_SYSTEM_ID, + XML_ROLE_DOCTYPE_PUBLIC_ID, + XML_ROLE_DOCTYPE_INTERNAL_SUBSET, + XML_ROLE_DOCTYPE_CLOSE, + XML_ROLE_GENERAL_ENTITY_NAME, + XML_ROLE_PARAM_ENTITY_NAME, + XML_ROLE_ENTITY_NONE, + XML_ROLE_ENTITY_VALUE, + XML_ROLE_ENTITY_SYSTEM_ID, + XML_ROLE_ENTITY_PUBLIC_ID, + XML_ROLE_ENTITY_COMPLETE, + XML_ROLE_ENTITY_NOTATION_NAME, + XML_ROLE_NOTATION_NONE, + XML_ROLE_NOTATION_NAME, + XML_ROLE_NOTATION_SYSTEM_ID, + XML_ROLE_NOTATION_NO_SYSTEM_ID, + XML_ROLE_NOTATION_PUBLIC_ID, + XML_ROLE_ATTRIBUTE_NAME, + XML_ROLE_ATTRIBUTE_TYPE_CDATA, + XML_ROLE_ATTRIBUTE_TYPE_ID, + XML_ROLE_ATTRIBUTE_TYPE_IDREF, + XML_ROLE_ATTRIBUTE_TYPE_IDREFS, + XML_ROLE_ATTRIBUTE_TYPE_ENTITY, + XML_ROLE_ATTRIBUTE_TYPE_ENTITIES, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS, + XML_ROLE_ATTRIBUTE_ENUM_VALUE, + XML_ROLE_ATTRIBUTE_NOTATION_VALUE, + XML_ROLE_ATTLIST_NONE, + XML_ROLE_ATTLIST_ELEMENT_NAME, + XML_ROLE_IMPLIED_ATTRIBUTE_VALUE, + XML_ROLE_REQUIRED_ATTRIBUTE_VALUE, + XML_ROLE_DEFAULT_ATTRIBUTE_VALUE, + XML_ROLE_FIXED_ATTRIBUTE_VALUE, + XML_ROLE_ELEMENT_NONE, + XML_ROLE_ELEMENT_NAME, + XML_ROLE_CONTENT_ANY, + XML_ROLE_CONTENT_EMPTY, + XML_ROLE_CONTENT_PCDATA, + XML_ROLE_GROUP_OPEN, + XML_ROLE_GROUP_CLOSE, + XML_ROLE_GROUP_CLOSE_REP, + XML_ROLE_GROUP_CLOSE_OPT, + XML_ROLE_GROUP_CLOSE_PLUS, + XML_ROLE_GROUP_CHOICE, + XML_ROLE_GROUP_SEQUENCE, + XML_ROLE_CONTENT_ELEMENT, + XML_ROLE_CONTENT_ELEMENT_REP, + XML_ROLE_CONTENT_ELEMENT_OPT, + XML_ROLE_CONTENT_ELEMENT_PLUS, + XML_ROLE_PI, + XML_ROLE_COMMENT, +#ifdef XML_DTD + XML_ROLE_TEXT_DECL, + XML_ROLE_IGNORE_SECT, + XML_ROLE_INNER_PARAM_ENTITY_REF, +#endif /* XML_DTD */ + XML_ROLE_PARAM_ENTITY_REF +}; + +typedef struct prolog_state { + int (PTRCALL *handler) (struct prolog_state *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + unsigned level; + int role_none; +#ifdef XML_DTD + unsigned includeLevel; + int documentEntity; + int inEntityValue; +#endif /* XML_DTD */ +} PROLOG_STATE; + +void XmlPrologStateInit(PROLOG_STATE *); +#ifdef XML_DTD +void XmlPrologStateInitExternalEntity(PROLOG_STATE *); +#endif /* XML_DTD */ + +#define XmlTokenRole(state, tok, ptr, end, enc) \ + (((state)->handler)(state, tok, ptr, end, enc)) + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlRole_INCLUDED */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok.c new file mode 100644 index 00000000..b1b1ee6d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok.c @@ -0,0 +1,1634 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifdef COMPILED_FROM_DSP +#include "winconfig.h" +#elif defined(MACOS_CLASSIC) +#include "macconfig.h" +#else +#ifdef HAVE_EXPAT_CONFIG_H +#include +#endif +#endif /* ndef COMPILED_FROM_DSP */ + +#include "internal.h" +#include "xmltok.h" +#include "nametab.h" + +#ifdef XML_DTD +#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok) +#else +#define IGNORE_SECTION_TOK_VTABLE /* as nothing */ +#endif + +#define VTABLE1 \ + { PREFIX(prologTok), PREFIX(contentTok), \ + PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \ + { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \ + PREFIX(sameName), \ + PREFIX(nameMatchesAscii), \ + PREFIX(nameLength), \ + PREFIX(skipS), \ + PREFIX(getAtts), \ + PREFIX(charRefNumber), \ + PREFIX(predefinedEntityName), \ + PREFIX(updatePosition), \ + PREFIX(isPublicId) + +#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) + +#define UCS2_GET_NAMING(pages, hi, lo) \ + (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F))) + +/* A 2 byte UTF-8 representation splits the characters 11 bits between + the bottom 5 and 6 bits of the bytes. We need 8 bits to index into + pages, 3 bits to add to that index and 5 bits to generate the mask. +*/ +#define UTF8_GET_NAMING2(pages, byte) \ + (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ + + ((((byte)[0]) & 3) << 1) \ + + ((((byte)[1]) >> 5) & 1)] \ + & (1 << (((byte)[1]) & 0x1F))) + +/* A 3 byte UTF-8 representation splits the characters 16 bits between + the bottom 4, 6 and 6 bits of the bytes. We need 8 bits to index + into pages, 3 bits to add to that index and 5 bits to generate the + mask. +*/ +#define UTF8_GET_NAMING3(pages, byte) \ + (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \ + + ((((byte)[1]) >> 2) & 0xF)] \ + << 3) \ + + ((((byte)[1]) & 3) << 1) \ + + ((((byte)[2]) >> 5) & 1)] \ + & (1 << (((byte)[2]) & 0x1F))) + +#define UTF8_GET_NAMING(pages, p, n) \ + ((n) == 2 \ + ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ + : ((n) == 3 \ + ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \ + : 0)) + +/* Detection of invalid UTF-8 sequences is based on Table 3.1B + of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/ + with the additional restriction of not allowing the Unicode + code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE). + Implementation details: + (A & 0x80) == 0 means A < 0x80 + and + (A & 0xC0) == 0xC0 means A > 0xBF +*/ + +#define UTF8_INVALID2(p) \ + ((*p) < 0xC2 || ((p)[1] & 0x80) == 0 || ((p)[1] & 0xC0) == 0xC0) + +#define UTF8_INVALID3(p) \ + (((p)[2] & 0x80) == 0 \ + || \ + ((*p) == 0xEF && (p)[1] == 0xBF \ + ? \ + (p)[2] > 0xBD \ + : \ + ((p)[2] & 0xC0) == 0xC0) \ + || \ + ((*p) == 0xE0 \ + ? \ + (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0))) + +#define UTF8_INVALID4(p) \ + (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 \ + || \ + ((p)[2] & 0x80) == 0 || ((p)[2] & 0xC0) == 0xC0 \ + || \ + ((*p) == 0xF0 \ + ? \ + (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0))) + +static int PTRFASTCALL +isNever(const ENCODING *enc, const char *p) +{ + return 0; +} + +static int PTRFASTCALL +utf8_isName2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isName3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); +} + +#define utf8_isName4 isNever + +static int PTRFASTCALL +utf8_isNmstrt2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isNmstrt3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); +} + +#define utf8_isNmstrt4 isNever + +static int PTRFASTCALL +utf8_isInvalid2(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID2((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid3(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID3((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid4(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID4((const unsigned char *)p); +} + +struct normal_encoding { + ENCODING enc; + unsigned char type[256]; +#ifdef XML_MIN_SIZE + int (PTRFASTCALL *byteType)(const ENCODING *, const char *); + int (PTRFASTCALL *isNameMin)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *); + int (PTRFASTCALL *byteToAscii)(const ENCODING *, const char *); + int (PTRCALL *charMatches)(const ENCODING *, const char *, int); +#endif /* XML_MIN_SIZE */ + int (PTRFASTCALL *isName2)(const ENCODING *, const char *); + int (PTRFASTCALL *isName3)(const ENCODING *, const char *); + int (PTRFASTCALL *isName4)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid2)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid3)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid4)(const ENCODING *, const char *); +}; + +#define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *) (enc)) + +#ifdef XML_MIN_SIZE + +#define STANDARD_VTABLE(E) \ + E ## byteType, \ + E ## isNameMin, \ + E ## isNmstrtMin, \ + E ## byteToAscii, \ + E ## charMatches, + +#else + +#define STANDARD_VTABLE(E) /* as nothing */ + +#endif + +#define NORMAL_VTABLE(E) \ + E ## isName2, \ + E ## isName3, \ + E ## isName4, \ + E ## isNmstrt2, \ + E ## isNmstrt3, \ + E ## isNmstrt4, \ + E ## isInvalid2, \ + E ## isInvalid3, \ + E ## isInvalid4 + +static int FASTCALL checkCharRefNumber(int); + +#include "xmltok_impl.h" +#include "ascii.h" + +#ifdef XML_MIN_SIZE +#define sb_isNameMin isNever +#define sb_isNmstrtMin isNever +#endif + +#ifdef XML_MIN_SIZE +#define MINBPC(enc) ((enc)->minBytesPerChar) +#else +/* minimum bytes per character */ +#define MINBPC(enc) 1 +#endif + +#define SB_BYTE_TYPE(enc, p) \ + (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) + +#ifdef XML_MIN_SIZE +static int PTRFASTCALL +sb_byteType(const ENCODING *enc, const char *p) +{ + return SB_BYTE_TYPE(enc, p); +} +#define BYTE_TYPE(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteType(enc, p)) +#else +#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) +#endif + +#ifdef XML_MIN_SIZE +#define BYTE_TO_ASCII(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p)) +static int PTRFASTCALL +sb_byteToAscii(const ENCODING *enc, const char *p) +{ + return *p; +} +#else +#define BYTE_TO_ASCII(enc, p) (*(p)) +#endif + +#define IS_NAME_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isName ## n(enc, p)) +#define IS_NMSTRT_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isNmstrt ## n(enc, p)) +#define IS_INVALID_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isInvalid ## n(enc, p)) + +#ifdef XML_MIN_SIZE +#define IS_NAME_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p)) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p)) +#else +#define IS_NAME_CHAR_MINBPC(enc, p) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) +#endif + +#ifdef XML_MIN_SIZE +#define CHAR_MATCHES(enc, p, c) \ + (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c)) +static int PTRCALL +sb_charMatches(const ENCODING *enc, const char *p, int c) +{ + return *p == c; +} +#else +/* c is an ASCII character */ +#define CHAR_MATCHES(enc, p, c) (*(p) == c) +#endif + +#define PREFIX(ident) normal_ ## ident +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ + UTF8_cval1 = 0x00, + UTF8_cval2 = 0xc0, + UTF8_cval3 = 0xe0, + UTF8_cval4 = 0xf0 +}; + +static void PTRCALL +utf8_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + char *to; + const char *from; + if (fromLim - *fromP > toLim - *toP) { + /* Avoid copying partial characters. */ + for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--) + if (((unsigned char)fromLim[-1] & 0xc0) != 0x80) + break; + } + for (to = *toP, from = *fromP; from != fromLim; from++, to++) + *to = *from; + *fromP = from; + *toP = to; +} + +static void PTRCALL +utf8_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + unsigned short *to = *toP; + const char *from = *fromP; + while (from != fromLim && to != toLim) { + switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { + case BT_LEAD2: + *to++ = (unsigned short)(((from[0] & 0x1f) << 6) | (from[1] & 0x3f)); + from += 2; + break; + case BT_LEAD3: + *to++ = (unsigned short)(((from[0] & 0xf) << 12) + | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f)); + from += 3; + break; + case BT_LEAD4: + { + unsigned long n; + if (to + 1 == toLim) + goto after; + n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) + | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); + n -= 0x10000; + to[0] = (unsigned short)((n >> 10) | 0xD800); + to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); + to += 2; + from += 4; + } + break; + default: + *to++ = *from++; + break; + } + } +after: + *fromP = from; + *toP = to; +} + +#ifdef XML_NS +static const struct normal_encoding utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; +#endif + +static const struct normal_encoding utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#ifdef XML_NS + +static const struct normal_encoding internal_utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "iasciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#endif + +static const struct normal_encoding internal_utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +static void PTRCALL +latin1_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + for (;;) { + unsigned char c; + if (*fromP == fromLim) + break; + c = (unsigned char)**fromP; + if (c & 0x80) { + if (toLim - *toP < 2) + break; + *(*toP)++ = (char)((c >> 6) | UTF8_cval2); + *(*toP)++ = (char)((c & 0x3f) | 0x80); + (*fromP)++; + } + else { + if (*toP == toLim) + break; + *(*toP)++ = *(*fromP)++; + } + } +} + +static void PTRCALL +latin1_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = (unsigned char)*(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding latin1_encoding_ns = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) +}; + +#endif + +static const struct normal_encoding latin1_encoding = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) +}; + +static void PTRCALL +ascii_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = *(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding ascii_encoding_ns = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) +}; + +#endif + +static const struct normal_encoding ascii_encoding = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) +}; + +static int PTRFASTCALL +unicode_byte_type(char hi, char lo) +{ + switch ((unsigned char)hi) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + return BT_LEAD4; + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return BT_TRAIL; + case 0xFF: + switch ((unsigned char)lo) { + case 0xFF: + case 0xFE: + return BT_NONXML; + } + break; + } + return BT_NONASCII; +} + +#define DEFINE_UTF16_TO_UTF8(E) \ +static void PTRCALL \ +E ## toUtf8(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + char **toP, const char *toLim) \ +{ \ + const char *from; \ + for (from = *fromP; from != fromLim; from += 2) { \ + int plane; \ + unsigned char lo2; \ + unsigned char lo = GET_LO(from); \ + unsigned char hi = GET_HI(from); \ + switch (hi) { \ + case 0: \ + if (lo < 0x80) { \ + if (*toP == toLim) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = lo; \ + break; \ + } \ + /* fall through */ \ + case 0x1: case 0x2: case 0x3: \ + case 0x4: case 0x5: case 0x6: case 0x7: \ + if (toLim - *toP < 2) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + default: \ + if (toLim - *toP < 3) { \ + *fromP = from; \ + return; \ + } \ + /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ + *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ + *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + case 0xD8: case 0xD9: case 0xDA: case 0xDB: \ + if (toLim - *toP < 4) { \ + *fromP = from; \ + return; \ + } \ + plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ + *(*toP)++ = ((plane >> 2) | UTF8_cval4); \ + *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ + from += 2; \ + lo2 = GET_LO(from); \ + *(*toP)++ = (((lo & 0x3) << 4) \ + | ((GET_HI(from) & 0x3) << 2) \ + | (lo2 >> 6) \ + | 0x80); \ + *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ + break; \ + } \ + } \ + *fromP = from; \ +} + +#define DEFINE_UTF16_TO_UTF16(E) \ +static void PTRCALL \ +E ## toUtf16(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + unsigned short **toP, const unsigned short *toLim) \ +{ \ + /* Avoid copying first half only of surrogate */ \ + if (fromLim - *fromP > ((toLim - *toP) << 1) \ + && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \ + fromLim -= 2; \ + for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \ + *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ +} + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8))) +#define GET_LO(ptr) ((unsigned char)(ptr)[0]) +#define GET_HI(ptr) ((unsigned char)(ptr)[1]) + +DEFINE_UTF16_TO_UTF8(little2_) +DEFINE_UTF16_TO_UTF16(little2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF))) +#define GET_LO(ptr) ((unsigned char)(ptr)[1]) +#define GET_HI(ptr) ((unsigned char)(ptr)[0]) + +DEFINE_UTF16_TO_UTF8(big2_) +DEFINE_UTF16_TO_UTF16(big2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define LITTLE2_BYTE_TYPE(enc, p) \ + ((p)[1] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ + : unicode_byte_type((p)[1], (p)[0])) +#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1) +#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c) +#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) +#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +little2_byteType(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +little2_byteToAscii(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +little2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return LITTLE2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +little2_isNameMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +little2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) little2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding little2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + +static const struct normal_encoding little2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#if BYTEORDER != 4321 + +#ifdef XML_NS + +static const struct normal_encoding internal_little2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + +static const struct normal_encoding internal_little2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + + +#define BIG2_BYTE_TYPE(enc, p) \ + ((p)[0] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ + : unicode_byte_type((p)[0], (p)[1])) +#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1) +#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c) +#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) +#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +big2_byteType(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +big2_byteToAscii(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +big2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return BIG2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +big2_isNameMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +big2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) big2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding big2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +static const struct normal_encoding big2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#if BYTEORDER != 1234 + +#ifdef XML_NS + +static const struct normal_encoding internal_big2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +static const struct normal_encoding internal_big2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +#undef PREFIX + +static int FASTCALL +streqci(const char *s1, const char *s2) +{ + for (;;) { + char c1 = *s1++; + char c2 = *s2++; + if (ASCII_a <= c1 && c1 <= ASCII_z) + c1 += ASCII_A - ASCII_a; + if (ASCII_a <= c2 && c2 <= ASCII_z) + c2 += ASCII_A - ASCII_a; + if (c1 != c2) + return 0; + if (!c1) + break; + } + return 1; +} + +static void PTRCALL +initUpdatePosition(const ENCODING *enc, const char *ptr, + const char *end, POSITION *pos) +{ + normal_updatePosition(&utf8_encoding.enc, ptr, end, pos); +} + +static int +toAscii(const ENCODING *enc, const char *ptr, const char *end) +{ + char buf[1]; + char *p = buf; + XmlUtf8Convert(enc, &ptr, end, &p, p + 1); + if (p == buf) + return -1; + else + return buf[0]; +} + +static int FASTCALL +isSpace(int c) +{ + switch (c) { + case 0x20: + case 0xD: + case 0xA: + case 0x9: + return 1; + } + return 0; +} + +/* Return 1 if there's just optional white space or there's an S + followed by name=val. +*/ +static int +parsePseudoAttribute(const ENCODING *enc, + const char *ptr, + const char *end, + const char **namePtr, + const char **nameEndPtr, + const char **valPtr, + const char **nextTokPtr) +{ + int c; + char open; + if (ptr == end) { + *namePtr = NULL; + return 1; + } + if (!isSpace(toAscii(enc, ptr, end))) { + *nextTokPtr = ptr; + return 0; + } + do { + ptr += enc->minBytesPerChar; + } while (isSpace(toAscii(enc, ptr, end))); + if (ptr == end) { + *namePtr = NULL; + return 1; + } + *namePtr = ptr; + for (;;) { + c = toAscii(enc, ptr, end); + if (c == -1) { + *nextTokPtr = ptr; + return 0; + } + if (c == ASCII_EQUALS) { + *nameEndPtr = ptr; + break; + } + if (isSpace(c)) { + *nameEndPtr = ptr; + do { + ptr += enc->minBytesPerChar; + } while (isSpace(c = toAscii(enc, ptr, end))); + if (c != ASCII_EQUALS) { + *nextTokPtr = ptr; + return 0; + } + break; + } + ptr += enc->minBytesPerChar; + } + if (ptr == *namePtr) { + *nextTokPtr = ptr; + return 0; + } + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + while (isSpace(c)) { + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + } + if (c != ASCII_QUOT && c != ASCII_APOS) { + *nextTokPtr = ptr; + return 0; + } + open = (char)c; + ptr += enc->minBytesPerChar; + *valPtr = ptr; + for (;; ptr += enc->minBytesPerChar) { + c = toAscii(enc, ptr, end); + if (c == open) + break; + if (!(ASCII_a <= c && c <= ASCII_z) + && !(ASCII_A <= c && c <= ASCII_Z) + && !(ASCII_0 <= c && c <= ASCII_9) + && c != ASCII_PERIOD + && c != ASCII_MINUS + && c != ASCII_UNDERSCORE) { + *nextTokPtr = ptr; + return 0; + } + } + *nextTokPtr = ptr + enc->minBytesPerChar; + return 1; +} + +static const char KW_version[] = { + ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0' +}; + +static const char KW_encoding[] = { + ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0' +}; + +static const char KW_standalone[] = { + ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o, + ASCII_n, ASCII_e, '\0' +}; + +static const char KW_yes[] = { + ASCII_y, ASCII_e, ASCII_s, '\0' +}; + +static const char KW_no[] = { + ASCII_n, ASCII_o, '\0' +}; + +static int +doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, + const char *, + const char *), + int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + const char *val = NULL; + const char *name = NULL; + const char *nameEnd = NULL; + ptr += 5 * enc->minBytesPerChar; + end -= 2 * enc->minBytesPerChar; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr) + || !name) { + *badPtr = ptr; + return 0; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) { + if (!isGeneralTextEntity) { + *badPtr = name; + return 0; + } + } + else { + if (versionPtr) + *versionPtr = val; + if (versionEndPtr) + *versionEndPtr = ptr; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) { + if (isGeneralTextEntity) { + /* a TextDecl must have an EncodingDecl */ + *badPtr = ptr; + return 0; + } + return 1; + } + } + if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) { + int c = toAscii(enc, val, end); + if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) { + *badPtr = val; + return 0; + } + if (encodingName) + *encodingName = val; + if (encoding) + *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar); + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) + return 1; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) + || isGeneralTextEntity) { + *badPtr = name; + return 0; + } + if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) { + if (standalone) + *standalone = 1; + } + else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) { + if (standalone) + *standalone = 0; + } + else { + *badPtr = val; + return 0; + } + while (isSpace(toAscii(enc, ptr, end))) + ptr += enc->minBytesPerChar; + if (ptr != end) { + *badPtr = ptr; + return 0; + } + return 1; +} + +static int FASTCALL +checkCharRefNumber(int result) +{ + switch (result >> 8) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return -1; + case 0: + if (latin1_encoding.type[result] == BT_NONXML) + return -1; + break; + case 0xFF: + if (result == 0xFFFE || result == 0xFFFF) + return -1; + break; + } + return result; +} + +int FASTCALL +XmlUtf8Encode(int c, char *buf) +{ + enum { + /* minN is minimum legal resulting value for N byte sequence */ + min2 = 0x80, + min3 = 0x800, + min4 = 0x10000 + }; + + if (c < 0) + return 0; + if (c < min2) { + buf[0] = (char)(c | UTF8_cval1); + return 1; + } + if (c < min3) { + buf[0] = (char)((c >> 6) | UTF8_cval2); + buf[1] = (char)((c & 0x3f) | 0x80); + return 2; + } + if (c < min4) { + buf[0] = (char)((c >> 12) | UTF8_cval3); + buf[1] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[2] = (char)((c & 0x3f) | 0x80); + return 3; + } + if (c < 0x110000) { + buf[0] = (char)((c >> 18) | UTF8_cval4); + buf[1] = (char)(((c >> 12) & 0x3f) | 0x80); + buf[2] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[3] = (char)((c & 0x3f) | 0x80); + return 4; + } + return 0; +} + +int FASTCALL +XmlUtf16Encode(int charNum, unsigned short *buf) +{ + if (charNum < 0) + return 0; + if (charNum < 0x10000) { + buf[0] = (unsigned short)charNum; + return 1; + } + if (charNum < 0x110000) { + charNum -= 0x10000; + buf[0] = (unsigned short)((charNum >> 10) + 0xD800); + buf[1] = (unsigned short)((charNum & 0x3FF) + 0xDC00); + return 2; + } + return 0; +} + +struct unknown_encoding { + struct normal_encoding normal; + int (*convert)(void *userData, const char *p); + void *userData; + unsigned short utf16[256]; + char utf8[256][4]; +}; + +#define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *) (enc)) + +int +XmlSizeOfUnknownEncoding(void) +{ + return sizeof(struct unknown_encoding); +} + +static int PTRFASTCALL +unknown_isName(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isNmstrt(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isInvalid(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; +} + +static void PTRCALL +unknown_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + char buf[XML_UTF8_ENCODE_MAX]; + for (;;) { + const char *utf8; + int n; + if (*fromP == fromLim) + break; + utf8 = uenc->utf8[(unsigned char)**fromP]; + n = *utf8++; + if (n == 0) { + int c = uenc->convert(uenc->userData, *fromP); + n = XmlUtf8Encode(c, buf); + if (n > toLim - *toP) + break; + utf8 = buf; + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else { + if (n > toLim - *toP) + break; + (*fromP)++; + } + do { + *(*toP)++ = *utf8++; + } while (--n != 0); + } +} + +static void PTRCALL +unknown_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + while (*fromP != fromLim && *toP != toLim) { + unsigned short c = uenc->utf16[(unsigned char)**fromP]; + if (c == 0) { + c = (unsigned short) + uenc->convert(uenc->userData, *fromP); + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else + (*fromP)++; + *(*toP)++ = c; + } +} + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + int i; + struct unknown_encoding *e = (struct unknown_encoding *)mem; + for (i = 0; i < (int)sizeof(struct normal_encoding); i++) + ((char *)mem)[i] = ((char *)&latin1_encoding)[i]; + for (i = 0; i < 128; i++) + if (latin1_encoding.type[i] != BT_OTHER + && latin1_encoding.type[i] != BT_NONXML + && table[i] != i) + return 0; + for (i = 0; i < 256; i++) { + int c = table[i]; + if (c == -1) { + e->normal.type[i] = BT_MALFORM; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else if (c < 0) { + if (c < -4) + return 0; + e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2)); + e->utf8[i][0] = 0; + e->utf16[i] = 0; + } + else if (c < 0x80) { + if (latin1_encoding.type[c] != BT_OTHER + && latin1_encoding.type[c] != BT_NONXML + && c != i) + return 0; + e->normal.type[i] = latin1_encoding.type[c]; + e->utf8[i][0] = 1; + e->utf8[i][1] = (char)c; + e->utf16[i] = (unsigned short)(c == 0 ? 0xFFFF : c); + } + else if (checkCharRefNumber(c) < 0) { + e->normal.type[i] = BT_NONXML; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else { + if (c > 0xFFFF) + return 0; + if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NMSTRT; + else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NAME; + else + e->normal.type[i] = BT_OTHER; + e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1); + e->utf16[i] = (unsigned short)c; + } + } + e->userData = userData; + e->convert = convert; + if (convert) { + e->normal.isName2 = unknown_isName; + e->normal.isName3 = unknown_isName; + e->normal.isName4 = unknown_isName; + e->normal.isNmstrt2 = unknown_isNmstrt; + e->normal.isNmstrt3 = unknown_isNmstrt; + e->normal.isNmstrt4 = unknown_isNmstrt; + e->normal.isInvalid2 = unknown_isInvalid; + e->normal.isInvalid3 = unknown_isInvalid; + e->normal.isInvalid4 = unknown_isInvalid; + } + e->normal.enc.utf8Convert = unknown_toUtf8; + e->normal.enc.utf16Convert = unknown_toUtf16; + return &(e->normal.enc); +} + +/* If this enumeration is changed, getEncodingIndex and encodings +must also be changed. */ +enum { + UNKNOWN_ENC = -1, + ISO_8859_1_ENC = 0, + US_ASCII_ENC, + UTF_8_ENC, + UTF_16_ENC, + UTF_16BE_ENC, + UTF_16LE_ENC, + /* must match encodingNames up to here */ + NO_ENC +}; + +static const char KW_ISO_8859_1[] = { + ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9, + ASCII_MINUS, ASCII_1, '\0' +}; +static const char KW_US_ASCII[] = { + ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I, + '\0' +}; +static const char KW_UTF_8[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0' +}; +static const char KW_UTF_16[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0' +}; +static const char KW_UTF_16BE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E, + '\0' +}; +static const char KW_UTF_16LE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E, + '\0' +}; + +static int FASTCALL +getEncodingIndex(const char *name) +{ + static const char *encodingNames[] = { + KW_ISO_8859_1, + KW_US_ASCII, + KW_UTF_8, + KW_UTF_16, + KW_UTF_16BE, + KW_UTF_16LE, + }; + int i; + if (name == NULL) + return NO_ENC; + for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++) + if (streqci(name, encodingNames[i])) + return i; + return UNKNOWN_ENC; +} + +/* For binary compatibility, we store the index of the encoding + specified at initialization in the isUtf16 member. +*/ + +#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16) +#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i) + +/* This is what detects the encoding. encodingTable maps from + encoding indices to encodings; INIT_ENC_INDEX(enc) is the index of + the external (protocol) specified encoding; state is + XML_CONTENT_STATE if we're parsing an external text entity, and + XML_PROLOG_STATE otherwise. +*/ + + +static int +initScan(const ENCODING **encodingTable, + const INIT_ENCODING *enc, + int state, + const char *ptr, + const char *end, + const char **nextTokPtr) +{ + const ENCODING **encPtr; + + if (ptr == end) + return XML_TOK_NONE; + encPtr = enc->encPtr; + if (ptr + 1 == end) { + /* only a single byte available for auto-detection */ +#ifndef XML_DTD /* FIXME */ + /* a well-formed document entity must have more than one byte */ + if (state != XML_CONTENT_STATE) + return XML_TOK_PARTIAL; +#endif + /* so we're parsing an external text entity... */ + /* if UTF-16 was externally specified, then we need at least 2 bytes */ + switch (INIT_ENC_INDEX(enc)) { + case UTF_16_ENC: + case UTF_16LE_ENC: + case UTF_16BE_ENC: + return XML_TOK_PARTIAL; + } + switch ((unsigned char)*ptr) { + case 0xFE: + case 0xFF: + case 0xEF: /* possibly first byte of UTF-8 BOM */ + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + /* fall through */ + case 0x00: + case 0x3C: + return XML_TOK_PARTIAL; + } + } + else { + switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) { + case 0xFEFF: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XML_TOK_BOM; + /* 00 3C is handled in the default case */ + case 0x3C00: + if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC + || INIT_ENC_INDEX(enc) == UTF_16_ENC) + && state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + case 0xFFFE: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XML_TOK_BOM; + case 0xEFBB: + /* Maybe a UTF-8 BOM (EF BB BF) */ + /* If there's an explicitly specified (external) encoding + of ISO-8859-1 or some flavour of UTF-16 + and this is an external text entity, + don't look for the BOM, + because it might be a legal data. + */ + if (state == XML_CONTENT_STATE) { + int e = INIT_ENC_INDEX(enc); + if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC + || e == UTF_16LE_ENC || e == UTF_16_ENC) + break; + } + if (ptr + 2 == end) + return XML_TOK_PARTIAL; + if ((unsigned char)ptr[2] == 0xBF) { + *nextTokPtr = ptr + 3; + *encPtr = encodingTable[UTF_8_ENC]; + return XML_TOK_BOM; + } + break; + default: + if (ptr[0] == '\0') { + /* 0 isn't a legal data character. Furthermore a document + entity can only start with ASCII characters. So the only + way this can fail to be big-endian UTF-16 if it it's an + external parsed general entity that's labelled as + UTF-16LE. + */ + if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC) + break; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + else if (ptr[1] == '\0') { + /* We could recover here in the case: + - parsing an external entity + - second byte is 0 + - no externally specified encoding + - no encoding declaration + by assuming UTF-16LE. But we don't, because this would mean when + presented just with a single byte, we couldn't reliably determine + whether we needed further bytes. + */ + if (state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + break; + } + } + *encPtr = encodingTable[INIT_ENC_INDEX(enc)]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); +} + + +#define NS(x) x +#define ns(x) x +#include "xmltok_ns.c" +#undef NS +#undef ns + +#ifdef XML_NS + +#define NS(x) x ## NS +#define ns(x) x ## _ns + +#include "xmltok_ns.c" + +#undef NS +#undef ns + +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); + if (enc) + ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON; + return enc; +} + +#endif /* XML_NS */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok.h new file mode 100644 index 00000000..811e3f26 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok.h @@ -0,0 +1,315 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlTok_INCLUDED +#define XmlTok_INCLUDED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* The following token may be returned by XmlContentTok */ +#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be + start of illegal ]]> sequence */ +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_NONE -4 /* The string to be scanned is empty */ +#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan; + might be part of CRLF sequence */ +#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ +#define XML_TOK_PARTIAL -1 /* only part of a token */ +#define XML_TOK_INVALID 0 + +/* The following tokens are returned by XmlContentTok; some are also + returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok. +*/ +#define XML_TOK_START_TAG_WITH_ATTS 1 +#define XML_TOK_START_TAG_NO_ATTS 2 +#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag */ +#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 +#define XML_TOK_END_TAG 5 +#define XML_TOK_DATA_CHARS 6 +#define XML_TOK_DATA_NEWLINE 7 +#define XML_TOK_CDATA_SECT_OPEN 8 +#define XML_TOK_ENTITY_REF 9 +#define XML_TOK_CHAR_REF 10 /* numeric character reference */ + +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_PI 11 /* processing instruction */ +#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ +#define XML_TOK_COMMENT 13 +#define XML_TOK_BOM 14 /* Byte order mark */ + +/* The following tokens are returned only by XmlPrologTok */ +#define XML_TOK_PROLOG_S 15 +#define XML_TOK_DECL_OPEN 16 /* */ +#define XML_TOK_NAME 18 +#define XML_TOK_NMTOKEN 19 +#define XML_TOK_POUND_NAME 20 /* #name */ +#define XML_TOK_OR 21 /* | */ +#define XML_TOK_PERCENT 22 +#define XML_TOK_OPEN_PAREN 23 +#define XML_TOK_CLOSE_PAREN 24 +#define XML_TOK_OPEN_BRACKET 25 +#define XML_TOK_CLOSE_BRACKET 26 +#define XML_TOK_LITERAL 27 +#define XML_TOK_PARAM_ENTITY_REF 28 +#define XML_TOK_INSTANCE_START 29 + +/* The following occur only in element type declarations */ +#define XML_TOK_NAME_QUESTION 30 /* name? */ +#define XML_TOK_NAME_ASTERISK 31 /* name* */ +#define XML_TOK_NAME_PLUS 32 /* name+ */ +#define XML_TOK_COND_SECT_OPEN 33 /* */ +#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ +#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ +#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ +#define XML_TOK_COMMA 38 + +/* The following token is returned only by XmlAttributeValueTok */ +#define XML_TOK_ATTRIBUTE_VALUE_S 39 + +/* The following token is returned only by XmlCdataSectionTok */ +#define XML_TOK_CDATA_SECT_CLOSE 40 + +/* With namespace processing this is returned by XmlPrologTok for a + name with a colon. +*/ +#define XML_TOK_PREFIXED_NAME 41 + +#ifdef XML_DTD +#define XML_TOK_IGNORE_SECT 42 +#endif /* XML_DTD */ + +#ifdef XML_DTD +#define XML_N_STATES 4 +#else /* not XML_DTD */ +#define XML_N_STATES 3 +#endif /* not XML_DTD */ + +#define XML_PROLOG_STATE 0 +#define XML_CONTENT_STATE 1 +#define XML_CDATA_SECTION_STATE 2 +#ifdef XML_DTD +#define XML_IGNORE_SECTION_STATE 3 +#endif /* XML_DTD */ + +#define XML_N_LITERAL_TYPES 2 +#define XML_ATTRIBUTE_VALUE_LITERAL 0 +#define XML_ENTITY_VALUE_LITERAL 1 + +/* The size of the buffer passed to XmlUtf8Encode must be at least this. */ +#define XML_UTF8_ENCODE_MAX 4 +/* The size of the buffer passed to XmlUtf16Encode must be at least this. */ +#define XML_UTF16_ENCODE_MAX 2 + +typedef struct position { + /* first line and first column are 0 not 1 */ + unsigned long lineNumber; + unsigned long columnNumber; +} POSITION; + +typedef struct { + const char *name; + const char *valuePtr; + const char *valueEnd; + char normalized; +} ATTRIBUTE; + +struct encoding; +typedef struct encoding ENCODING; + +typedef int (PTRCALL *SCANNER)(const ENCODING *, + const char *, + const char *, + const char **); + +struct encoding { + SCANNER scanners[XML_N_STATES]; + SCANNER literalScanners[XML_N_LITERAL_TYPES]; + int (PTRCALL *sameName)(const ENCODING *, + const char *, + const char *); + int (PTRCALL *nameMatchesAscii)(const ENCODING *, + const char *, + const char *, + const char *); + int (PTRFASTCALL *nameLength)(const ENCODING *, const char *); + const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *); + int (PTRCALL *getAtts)(const ENCODING *enc, + const char *ptr, + int attsMax, + ATTRIBUTE *atts); + int (PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr); + int (PTRCALL *predefinedEntityName)(const ENCODING *, + const char *, + const char *); + void (PTRCALL *updatePosition)(const ENCODING *, + const char *ptr, + const char *end, + POSITION *); + int (PTRCALL *isPublicId)(const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr); + void (PTRCALL *utf8Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + char **toP, + const char *toLim); + void (PTRCALL *utf16Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + unsigned short **toP, + const unsigned short *toLim); + int minBytesPerChar; + char isUtf8; + char isUtf16; +}; + +/* Scan the string starting at ptr until the end of the next complete + token, but do not scan past eptr. Return an integer giving the + type of token. + + Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set. + + Return XML_TOK_PARTIAL when the string does not contain a complete + token; nextTokPtr will not be set. + + Return XML_TOK_INVALID when the string does not start a valid + token; nextTokPtr will be set to point to the character which made + the token invalid. + + Otherwise the string starts with a valid token; nextTokPtr will be + set to point to the character following the end of that token. + + Each data character counts as a single token, but adjacent data + characters may be returned together. Similarly for characters in + the prolog outside literals, comments and processing instructions. +*/ + + +#define XmlTok(enc, state, ptr, end, nextTokPtr) \ + (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) + +#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) + +#define XmlContentTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) + +#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) + +#ifdef XML_DTD + +#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) + +#endif /* XML_DTD */ + +/* This is used for performing a 2nd-level tokenization on the content + of a literal that has already been returned by XmlTok. +*/ +#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ + (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) + +#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2)) + +#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ + (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) + +#define XmlNameLength(enc, ptr) \ + (((enc)->nameLength)(enc, ptr)) + +#define XmlSkipS(enc, ptr) \ + (((enc)->skipS)(enc, ptr)) + +#define XmlGetAttributes(enc, ptr, attsMax, atts) \ + (((enc)->getAtts)(enc, ptr, attsMax, atts)) + +#define XmlCharRefNumber(enc, ptr) \ + (((enc)->charRefNumber)(enc, ptr)) + +#define XmlPredefinedEntityName(enc, ptr, end) \ + (((enc)->predefinedEntityName)(enc, ptr, end)) + +#define XmlUpdatePosition(enc, ptr, end, pos) \ + (((enc)->updatePosition)(enc, ptr, end, pos)) + +#define XmlIsPublicId(enc, ptr, end, badPtr) \ + (((enc)->isPublicId)(enc, ptr, end, badPtr)) + +#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) + +#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) + +typedef struct { + ENCODING initEnc; + const ENCODING **encPtr; +} INIT_ENCODING; + +int XmlParseXmlDecl(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncoding(void); +const ENCODING *XmlGetUtf16InternalEncoding(void); +int FASTCALL XmlUtf8Encode(int charNumber, char *buf); +int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf); +int XmlSizeOfUnknownEncoding(void); + +typedef int (*CONVERTER)(void *userData, const char *p); + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData); + +int XmlParseXmlDeclNS(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncodingNS(void); +const ENCODING *XmlGetUtf16InternalEncodingNS(void); +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData); +#ifdef __cplusplus +} +#endif + +#endif /* not XmlTok_INCLUDED */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_impl.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_impl.c new file mode 100644 index 00000000..9d13be3b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_impl.c @@ -0,0 +1,1779 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef IS_INVALID_CHAR +#define IS_INVALID_CHAR(enc, ptr, n) (0) +#endif + +#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n)) { \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define INVALID_CASES(ptr, nextTokPtr) \ + INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ + case BT_NONXML: \ + case BT_MALFORM: \ + case BT_TRAIL: \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; + +#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NAME_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + case BT_DIGIT: \ + case BT_NAME: \ + case BT_MINUS: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) + +#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) + +#ifndef PREFIX +#define PREFIX(ident) ident +#endif + +/* ptr points to character following " */ + switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) { + case BT_S: case BT_CR: case BT_LF: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* fall through */ + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DECL_OPEN; + case BT_NMSTRT: + case BT_HEX: + ptr += MINBPC(enc); + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, + const char *end, int *tokPtr) +{ + int upper = 0; + *tokPtr = XML_TOK_PI; + if (end - ptr != MINBPC(enc)*3) + return 1; + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_x: + break; + case ASCII_X: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_m: + break; + case ASCII_M: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + break; + case ASCII_L: + upper = 1; + break; + default: + return 1; + } + if (upper) + return 0; + *tokPtr = XML_TOK_XML_DECL; + return 1; +} + +/* ptr points to character following " 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CDATA_SECT_CLOSE; + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + case BT_RSQB: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following " 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_LT: + return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_AMP: + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_RSQB: + if (ptr + MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) { + ptr += MINBPC(enc); + break; + } + if (ptr + 2*MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) { + ptr += MINBPC(enc); + break; + } + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_INVALID; + } + } + /* fall through */ + case BT_AMP: + case BT_LT: + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "%" */ + +static int PTRCALL +PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return -XML_TOK_PERCENT; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_LF: case BT_CR: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_PERCENT; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_PARAM_ENTITY_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_CR: case BT_LF: case BT_S: + case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR: + *nextTokPtr = ptr; + return XML_TOK_POUND_NAME; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -XML_TOK_POUND_NAME; +} + +static int PTRCALL +PREFIX(scanLit)(int open, const ENCODING *enc, + const char *ptr, const char *end, + const char **nextTokPtr) +{ + while (ptr != end) { + int t = BYTE_TYPE(enc, ptr); + switch (t) { + INVALID_CASES(ptr, nextTokPtr) + case BT_QUOT: + case BT_APOS: + ptr += MINBPC(enc); + if (t != open) + break; + if (ptr == end) + return -XML_TOK_LITERAL; + *nextTokPtr = ptr; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_CR: case BT_LF: + case BT_GT: case BT_PERCNT: case BT_LSQB: + return XML_TOK_LITERAL; + default: + return XML_TOK_INVALID; + } + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int tok; + if (ptr == end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_QUOT: + return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_APOS: + return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LT: + { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_EXCL: + return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_NMSTRT: + case BT_HEX: + case BT_NONASCII: + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + *nextTokPtr = ptr - MINBPC(enc); + return XML_TOK_INSTANCE_START; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + case BT_CR: + if (ptr + MINBPC(enc) == end) { + *nextTokPtr = end; + /* indicate that this might be part of a CR/LF pair */ + return -XML_TOK_PROLOG_S; + } + /* fall through */ + case BT_S: case BT_LF: + for (;;) { + ptr += MINBPC(enc); + if (ptr == end) + break; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_LF: + break; + case BT_CR: + /* don't split CR/LF pair */ + if (ptr + MINBPC(enc) != end) + break; + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + } + } + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + case BT_PERCNT: + return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_COMMA: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COMMA; + case BT_LSQB: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_BRACKET; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return -XML_TOK_CLOSE_BRACKET; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + if (ptr + MINBPC(enc) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) { + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_COND_SECT_CLOSE; + } + } + *nextTokPtr = ptr; + return XML_TOK_CLOSE_BRACKET; + case BT_LPAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_PAREN; + case BT_RPAR: + ptr += MINBPC(enc); + if (ptr == end) + return -XML_TOK_CLOSE_PAREN; + switch (BYTE_TYPE(enc, ptr)) { + case BT_AST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_ASTERISK; + case BT_QUEST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_QUESTION; + case BT_PLUS: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_PLUS; + case BT_CR: case BT_LF: case BT_S: + case BT_GT: case BT_COMMA: case BT_VERBAR: + case BT_RPAR: + *nextTokPtr = ptr; + return XML_TOK_CLOSE_PAREN; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_VERBAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OR; + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DECL_CLOSE; + case BT_NUM: + return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr); +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NAME; \ + break; \ + } \ + if (IS_NAME_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NMTOKEN; \ + break; \ + } \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NMSTRT: + case BT_HEX: + tok = XML_TOK_NAME; + ptr += MINBPC(enc); + break; + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: +#ifdef XML_NS + case BT_COLON: +#endif + tok = XML_TOK_NMTOKEN; + ptr += MINBPC(enc); + break; + case BT_NONASCII: + if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NAME; + break; + } + if (IS_NAME_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NMTOKEN; + break; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: case BT_RPAR: case BT_COMMA: + case BT_VERBAR: case BT_LSQB: case BT_PERCNT: + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return tok; +#ifdef XML_NS + case BT_COLON: + ptr += MINBPC(enc); + switch (tok) { + case XML_TOK_NAME: + if (ptr == end) + return XML_TOK_PARTIAL; + tok = XML_TOK_PREFIXED_NAME; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + default: + tok = XML_TOK_NMTOKEN; + break; + } + break; + case XML_TOK_PREFIXED_NAME: + tok = XML_TOK_NMTOKEN; + break; + } + break; +#endif + case BT_PLUS: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_PLUS; + case BT_AST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_ASTERISK; + case BT_QUEST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_QUESTION; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -tok; +} + +static int PTRCALL +PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LT: + /* this is for inside entity references */ + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_S: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_ATTRIBUTE_VALUE_S; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +static int PTRCALL +PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_PERCNT: + if (ptr == start) { + int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc), + end, nextTokPtr); + return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +#ifdef XML_DTD + +static int PTRCALL +PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + int level = 0; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + end = ptr + n; + } + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + INVALID_CASES(ptr, nextTokPtr) + case BT_LT: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) { + ++level; + ptr += MINBPC(enc); + } + } + break; + case BT_RSQB: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr += MINBPC(enc); + if (level == 0) { + *nextTokPtr = ptr; + return XML_TOK_IGNORE_SECT; + } + --level; + } + } + break; + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +#endif /* XML_DTD */ + +static int PTRCALL +PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr) +{ + ptr += MINBPC(enc); + end -= MINBPC(enc); + for (; ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + case BT_MINUS: + case BT_APOS: + case BT_LPAR: + case BT_RPAR: + case BT_PLUS: + case BT_COMMA: + case BT_SOL: + case BT_EQUALS: + case BT_QUEST: + case BT_CR: + case BT_LF: + case BT_SEMI: + case BT_EXCL: + case BT_AST: + case BT_PERCNT: + case BT_NUM: +#ifdef XML_NS + case BT_COLON: +#endif + break; + case BT_S: + if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) { + *badPtr = ptr; + return 0; + } + break; + case BT_NAME: + case BT_NMSTRT: + if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f)) + break; + default: + switch (BYTE_TO_ASCII(enc, ptr)) { + case 0x24: /* $ */ + case 0x40: /* @ */ + break; + default: + *badPtr = ptr; + return 0; + } + break; + } + } + return 1; +} + +/* This must only be called for a well-formed start-tag or empty + element tag. Returns the number of attributes. Pointers to the + first attsMax attributes are stored in atts. +*/ + +static int PTRCALL +PREFIX(getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts) +{ + enum { other, inName, inValue } state = inName; + int nAtts = 0; + int open = 0; /* defined when state == inValue; + initialization just to shut up compilers */ + + for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { +#define START_NAME \ + if (state == other) { \ + if (nAtts < attsMax) { \ + atts[nAtts].name = ptr; \ + atts[nAtts].normalized = 1; \ + } \ + state = inName; \ + } +#define LEAD_CASE(n) \ + case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + START_NAME + break; +#undef START_NAME + case BT_QUOT: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_QUOT; + } + else if (open == BT_QUOT) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_APOS: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_APOS; + } + else if (open == BT_APOS) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_AMP: + if (nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_S: + if (state == inName) + state = other; + else if (state == inValue + && nAtts < attsMax + && atts[nAtts].normalized + && (ptr == atts[nAtts].valuePtr + || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE + || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE + || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open)) + atts[nAtts].normalized = 0; + break; + case BT_CR: case BT_LF: + /* This case ensures that the first attribute name is counted + Apart from that we could just change state on the quote. */ + if (state == inName) + state = other; + else if (state == inValue && nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_GT: + case BT_SOL: + if (state != inValue) + return nAtts; + break; + default: + break; + } + } + /* not reached */ +} + +static int PTRFASTCALL +PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) +{ + int result = 0; + /* skip &# */ + ptr += 2*MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_x)) { + for (ptr += MINBPC(enc); + !CHAR_MATCHES(enc, ptr, ASCII_SEMI); + ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + switch (c) { + case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4: + case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9: + result <<= 4; + result |= (c - ASCII_0); + break; + case ASCII_A: case ASCII_B: case ASCII_C: + case ASCII_D: case ASCII_E: case ASCII_F: + result <<= 4; + result += 10 + (c - ASCII_A); + break; + case ASCII_a: case ASCII_b: case ASCII_c: + case ASCII_d: case ASCII_e: case ASCII_f: + result <<= 4; + result += 10 + (c - ASCII_a); + break; + } + if (result >= 0x110000) + return -1; + } + } + else { + for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + result *= 10; + result += (c - ASCII_0); + if (result >= 0x110000) + return -1; + } + } + return checkCharRefNumber(result); +} + +static int PTRCALL +PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, + const char *end) +{ + switch ((end - ptr)/MINBPC(enc)) { + case 2: + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) { + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + return ASCII_LT; + case ASCII_g: + return ASCII_GT; + } + } + break; + case 3: + if (CHAR_MATCHES(enc, ptr, ASCII_a)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_m)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) + return ASCII_AMP; + } + } + break; + case 4: + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_q: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_u)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_t)) + return ASCII_QUOT; + } + } + break; + case ASCII_a: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_s)) + return ASCII_APOS; + } + } + break; + } + } + return 0; +} + +static int PTRCALL +PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr1)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (*ptr1++ != *ptr2++) \ + return 0; + LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2) +#undef LEAD_CASE + /* fall through */ + if (*ptr1++ != *ptr2++) + return 0; + break; + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 1) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 2) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 3) { + if (*ptr2++ != *ptr1++) + return 0; + } + } + } + break; + default: + if (MINBPC(enc) == 1 && *ptr1 == *ptr2) + return 1; + switch (BYTE_TYPE(enc, ptr2)) { + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + return 0; + default: + return 1; + } + } + } + /* not reached */ +} + +static int PTRCALL +PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, + const char *end1, const char *ptr2) +{ + for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) { + if (ptr1 == end1) + return 0; + if (!CHAR_MATCHES(enc, ptr1, *ptr2)) + return 0; + } + return ptr1 == end1; +} + +static int PTRFASTCALL +PREFIX(nameLength)(const ENCODING *enc, const char *ptr) +{ + const char *start = ptr; + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + ptr += MINBPC(enc); + break; + default: + return ptr - start; + } + } +} + +static const char * PTRFASTCALL +PREFIX(skipS)(const ENCODING *enc, const char *ptr) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_LF: + case BT_CR: + case BT_S: + ptr += MINBPC(enc); + break; + default: + return ptr; + } + } +} + +static void PTRCALL +PREFIX(updatePosition)(const ENCODING *enc, + const char *ptr, + const char *end, + POSITION *pos) +{ + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_LF: + pos->columnNumber = (unsigned)-1; + pos->lineNumber++; + ptr += MINBPC(enc); + break; + case BT_CR: + pos->lineNumber++; + ptr += MINBPC(enc); + if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + pos->columnNumber = (unsigned)-1; + break; + default: + ptr += MINBPC(enc); + break; + } + pos->columnNumber++; + } +} + +#undef DO_LEAD_CASE +#undef MULTIBYTE_CASES +#undef INVALID_CASES +#undef CHECK_NAME_CASE +#undef CHECK_NAME_CASES +#undef CHECK_NMSTRT_CASE +#undef CHECK_NMSTRT_CASES + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_impl.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_impl.h new file mode 100644 index 00000000..94c7db61 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_impl.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file COPYING for copying permission. +*/ + +enum { + BT_NONXML, + BT_MALFORM, + BT_LT, + BT_AMP, + BT_RSQB, + BT_LEAD2, + BT_LEAD3, + BT_LEAD4, + BT_TRAIL, + BT_CR, + BT_LF, + BT_GT, + BT_QUOT, + BT_APOS, + BT_EQUALS, + BT_QUEST, + BT_EXCL, + BT_SOL, + BT_SEMI, + BT_NUM, + BT_LSQB, + BT_S, + BT_NMSTRT, + BT_COLON, + BT_HEX, + BT_DIGIT, + BT_NAME, + BT_MINUS, + BT_OTHER, /* known not to be a name or name start character */ + BT_NONASCII, /* might be a name or name start character */ + BT_PERCNT, + BT_LPAR, + BT_RPAR, + BT_AST, + BT_PLUS, + BT_COMMA, + BT_VERBAR +}; + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_ns.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_ns.c new file mode 100644 index 00000000..b8d02219 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/expat/xmltok_ns.c @@ -0,0 +1,106 @@ +const ENCODING * +NS(XmlGetUtf8InternalEncoding)(void) +{ + return &ns(internal_utf8_encoding).enc; +} + +const ENCODING * +NS(XmlGetUtf16InternalEncoding)(void) +{ +#if BYTEORDER == 1234 + return &ns(internal_little2_encoding).enc; +#elif BYTEORDER == 4321 + return &ns(internal_big2_encoding).enc; +#else + const short n = 1; + return (*(const char *)&n + ? &ns(internal_little2_encoding).enc + : &ns(internal_big2_encoding).enc); +#endif +} + +static const ENCODING *NS(encodings)[] = { + &ns(latin1_encoding).enc, + &ns(ascii_encoding).enc, + &ns(utf8_encoding).enc, + &ns(big2_encoding).enc, + &ns(big2_encoding).enc, + &ns(little2_encoding).enc, + &ns(utf8_encoding).enc /* NO_ENC */ +}; + +static int PTRCALL +NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_PROLOG_STATE, ptr, end, nextTokPtr); +} + +static int PTRCALL +NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_CONTENT_STATE, ptr, end, nextTokPtr); +} + +int +NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, + const char *name) +{ + int i = getEncodingIndex(name); + if (i == UNKNOWN_ENC) + return 0; + SET_INIT_ENC_INDEX(p, i); + p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog); + p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent); + p->initEnc.updatePosition = initUpdatePosition; + p->encPtr = encPtr; + *encPtr = &(p->initEnc); + return 1; +} + +static const ENCODING * +NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) +{ +#define ENCODING_MAX 128 + char buf[ENCODING_MAX]; + char *p = buf; + int i; + XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); + if (ptr != end) + return 0; + *p = 0; + if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) + return enc; + i = getEncodingIndex(buf); + if (i == UNKNOWN_ENC) + return 0; + return NS(encodings)[i]; +} + +int +NS(XmlParseXmlDecl)(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + return doParseXmlDecl(NS(findEncoding), + isGeneralTextEntity, + enc, + ptr, + end, + badPtr, + versionPtr, + versionEndPtr, + encodingName, + encoding, + standalone); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fcntlmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fcntlmodule.c new file mode 100644 index 00000000..b96f4363 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fcntlmodule.c @@ -0,0 +1,594 @@ + +/* fcntl module */ + +#include "Python.h" + +#ifdef HAVE_SYS_FILE_H +#include +#endif + +#include +#include +#ifdef HAVE_STROPTS_H +#include +#endif + +static int +conv_descriptor(PyObject *object, int *target) +{ + int fd = PyObject_AsFileDescriptor(object); + + if (fd < 0) + return 0; + *target = fd; + return 1; +} + + +/* fcntl(fd, opt, [arg]) */ + +static PyObject * +fcntl_fcntl(PyObject *self, PyObject *args) +{ + int fd; + int code; + int arg; + int ret; + char *str; + int len; + char buf[1024]; + + if (PyArg_ParseTuple(args, "O&is#:fcntl", + conv_descriptor, &fd, &code, &str, &len)) { + if (len > sizeof buf) { + PyErr_SetString(PyExc_ValueError, + "fcntl string arg too long"); + return NULL; + } + memcpy(buf, str, len); + Py_BEGIN_ALLOW_THREADS + ret = fcntl(fd, code, buf); + Py_END_ALLOW_THREADS + if (ret < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return PyString_FromStringAndSize(buf, len); + } + + PyErr_Clear(); + arg = 0; + if (!PyArg_ParseTuple(args, + "O&i|i;fcntl requires a file or file descriptor," + " an integer and optionally a third integer or a string", + conv_descriptor, &fd, &code, &arg)) { + return NULL; + } + Py_BEGIN_ALLOW_THREADS + ret = fcntl(fd, code, arg); + Py_END_ALLOW_THREADS + if (ret < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return PyInt_FromLong((long)ret); +} + +PyDoc_STRVAR(fcntl_doc, +"fcntl(fd, opt, [arg])\n\ +\n\ +Perform the requested operation on file descriptor fd. The operation\n\ +is defined by op and is operating system dependent. These constants are\n\ +available from the fcntl module. The argument arg is optional, and\n\ +defaults to 0; it may be an int or a string. If arg is given as a string,\n\ +the return value of fcntl is a string of that length, containing the\n\ +resulting value put in the arg buffer by the operating system.The length\n\ +of the arg string is not allowed to exceed 1024 bytes. If the arg given\n\ +is an integer or if none is specified, the result value is an integer\n\ +corresponding to the return value of the fcntl call in the C code."); + + +/* ioctl(fd, opt, [arg]) */ + +static PyObject * +fcntl_ioctl(PyObject *self, PyObject *args) +{ + int fd; + int code; + int arg; + int ret; + char *str; + int len; + int mutate_arg = 0; + char buf[1024]; + + if (PyArg_ParseTuple(args, "O&iw#|i:ioctl", + conv_descriptor, &fd, &code, + &str, &len, &mutate_arg)) { + char *arg; + + if (PyTuple_Size(args) == 3) { + /* warning goes here in 2.4 */ + mutate_arg = 0; + } + if (mutate_arg) { + if (len <= sizeof buf) { + memcpy(buf, str, len); + arg = buf; + } + else { + arg = str; + } + } + else { + if (len > sizeof buf) { + PyErr_SetString(PyExc_ValueError, + "ioctl string arg too long"); + return NULL; + } + else { + memcpy(buf, str, len); + arg = buf; + } + } + if (buf == arg) { + Py_BEGIN_ALLOW_THREADS /* think array.resize() */ + ret = ioctl(fd, code, arg); + Py_END_ALLOW_THREADS + } + else { + ret = ioctl(fd, code, arg); + } + if (mutate_arg && (len < sizeof buf)) { + memcpy(str, buf, len); + } + if (ret < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + if (mutate_arg) { + return PyInt_FromLong(ret); + } + else { + return PyString_FromStringAndSize(buf, len); + } + } + + PyErr_Clear(); + if (PyArg_ParseTuple(args, "O&is#:ioctl", + conv_descriptor, &fd, &code, &str, &len)) { + if (len > sizeof buf) { + PyErr_SetString(PyExc_ValueError, + "ioctl string arg too long"); + return NULL; + } + memcpy(buf, str, len); + Py_BEGIN_ALLOW_THREADS + ret = ioctl(fd, code, buf); + Py_END_ALLOW_THREADS + if (ret < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return PyString_FromStringAndSize(buf, len); + } + + PyErr_Clear(); + arg = 0; + if (!PyArg_ParseTuple(args, + "O&i|i;ioctl requires a file or file descriptor," + " an integer and optionally a integer or buffer argument", + conv_descriptor, &fd, &code, &arg)) { + return NULL; + } + Py_BEGIN_ALLOW_THREADS +#ifdef __VMS + ret = ioctl(fd, code, (void *)arg); +#else + ret = ioctl(fd, code, arg); +#endif + Py_END_ALLOW_THREADS + if (ret < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return PyInt_FromLong((long)ret); +} + +PyDoc_STRVAR(ioctl_doc, +"ioctl(fd, opt[, arg[, mutate_flag]])\n\ +\n\ +Perform the requested operation on file descriptor fd. The operation is\n\ +defined by opt and is operating system dependent. Typically these codes are\n\ +retrieved from the fcntl or termios library modules.\n\ +\n\ +The argument arg is optional, and defaults to 0; it may be an int or a\n\ +buffer containing character data (most likely a string or an array). \n\ +\n\ +If the argument is a mutable buffer (such as an array) and if the\n\ +mutate_flag argument (which is only allowed in this case) is true then the\n\ +buffer is (in effect) passed to the operating system and changes made by\n\ +the OS will be reflected in the contents of the buffer after the call has\n\ +returned. The return value is the integer returned by the ioctl system\n\ +call.\n\ +\n\ +If the argument is a mutable buffer and the mutable_flag argument is not\n\ +passed or is false, the behavior is as if a string had been passed. This\n\ +behavior will change in future releases of Python.\n\ +\n\ +If the argument is an immutable buffer (most likely a string) then a copy\n\ +of the buffer is passed to the operating system and the return value is a\n\ +string of the same length containing whatever the operating system put in\n\ +the buffer. The length of the arg buffer in this case is not allowed to\n\ +exceed 1024 bytes.\n\ +\n\ +If the arg given is an integer or if none is specified, the result value is\n\ +an integer corresponding to the return value of the ioctl call in the C\n\ +code."); + + +/* flock(fd, operation) */ + +static PyObject * +fcntl_flock(PyObject *self, PyObject *args) +{ + int fd; + int code; + int ret; + + if (!PyArg_ParseTuple(args, "O&i:flock", + conv_descriptor, &fd, &code)) + return NULL; + +#ifdef HAVE_FLOCK + Py_BEGIN_ALLOW_THREADS + ret = flock(fd, code); + Py_END_ALLOW_THREADS +#else + +#ifndef LOCK_SH +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* don't block when locking */ +#define LOCK_UN 8 /* unlock */ +#endif + { + struct flock l; + if (code == LOCK_UN) + l.l_type = F_UNLCK; + else if (code & LOCK_SH) + l.l_type = F_RDLCK; + else if (code & LOCK_EX) + l.l_type = F_WRLCK; + else { + PyErr_SetString(PyExc_ValueError, + "unrecognized flock argument"); + return NULL; + } + l.l_whence = l.l_start = l.l_len = 0; + Py_BEGIN_ALLOW_THREADS + ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l); + Py_END_ALLOW_THREADS + } +#endif /* HAVE_FLOCK */ + if (ret < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(flock_doc, +"flock(fd, operation)\n\ +\n\ +Perform the lock operation op on file descriptor fd. See the Unix \n\ +manual flock(3) for details. (On some systems, this function is\n\ +emulated using fcntl().)"); + + +/* lockf(fd, operation) */ +static PyObject * +fcntl_lockf(PyObject *self, PyObject *args) +{ + int fd, code, ret, whence = 0; + PyObject *lenobj = NULL, *startobj = NULL; + + if (!PyArg_ParseTuple(args, "O&i|OOi:lockf", + conv_descriptor, &fd, &code, + &lenobj, &startobj, &whence)) + return NULL; + +#if defined(PYOS_OS2) && defined(PYCC_GCC) + PyErr_SetString(PyExc_NotImplementedError, + "lockf not supported on OS/2 (EMX)"); + return NULL; +#else +#ifndef LOCK_SH +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* don't block when locking */ +#define LOCK_UN 8 /* unlock */ +#endif /* LOCK_SH */ + { + struct flock l; + if (code == LOCK_UN) + l.l_type = F_UNLCK; + else if (code & LOCK_SH) + l.l_type = F_RDLCK; + else if (code & LOCK_EX) + l.l_type = F_WRLCK; + else { + PyErr_SetString(PyExc_ValueError, + "unrecognized flock argument"); + return NULL; + } + l.l_start = l.l_len = 0; + if (startobj != NULL) { +#if !defined(HAVE_LARGEFILE_SUPPORT) + l.l_start = PyInt_AsLong(startobj); +#else + l.l_start = PyLong_Check(startobj) ? + PyLong_AsLongLong(startobj) : + PyInt_AsLong(startobj); +#endif + if (PyErr_Occurred()) + return NULL; + } + if (lenobj != NULL) { +#if !defined(HAVE_LARGEFILE_SUPPORT) + l.l_len = PyInt_AsLong(lenobj); +#else + l.l_len = PyLong_Check(lenobj) ? + PyLong_AsLongLong(lenobj) : + PyInt_AsLong(lenobj); +#endif + if (PyErr_Occurred()) + return NULL; + } + l.l_whence = whence; + Py_BEGIN_ALLOW_THREADS + ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l); + Py_END_ALLOW_THREADS + } + if (ret < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +#endif /* defined(PYOS_OS2) && defined(PYCC_GCC) */ +} + +PyDoc_STRVAR(lockf_doc, +"lockf (fd, operation, length=0, start=0, whence=0)\n\ +\n\ +This is essentially a wrapper around the fcntl() locking calls. fd is the\n\ +file descriptor of the file to lock or unlock, and operation is one of the\n\ +following values:\n\ +\n\ + LOCK_UN - unlock\n\ + LOCK_SH - acquire a shared lock\n\ + LOCK_EX - acquire an exclusive lock\n\ +\n\ +When operation is LOCK_SH or LOCK_EX, it can also be bit-wise OR'd with\n\ +LOCK_NB to avoid blocking on lock acquisition. If LOCK_NB is used and the\n\ +lock cannot be acquired, an IOError will be raised and the exception will\n\ +have an errno attribute set to EACCES or EAGAIN (depending on the operating\n\ +system -- for portability, check for either value).\n\ +\n\ +length is the number of bytes to lock, with the default meaning to lock to\n\ +EOF. start is the byte offset, relative to whence, to that the lock\n\ +starts. whence is as with fileobj.seek(), specifically:\n\ +\n\ + 0 - relative to the start of the file (SEEK_SET)\n\ + 1 - relative to the current buffer position (SEEK_CUR)\n\ + 2 - relative to the end of the file (SEEK_END)"); + +/* List of functions */ + +static PyMethodDef fcntl_methods[] = { + {"fcntl", fcntl_fcntl, METH_VARARGS, fcntl_doc}, + {"ioctl", fcntl_ioctl, METH_VARARGS, ioctl_doc}, + {"flock", fcntl_flock, METH_VARARGS, flock_doc}, + {"lockf", fcntl_lockf, METH_VARARGS, lockf_doc}, + {NULL, NULL} /* sentinel */ +}; + + +PyDoc_STRVAR(module_doc, +"This module performs file control and I/O control on file \n\ +descriptors. It is an interface to the fcntl() and ioctl() Unix\n\ +routines. File descriptors can be obtained with the fileno() method of\n\ +a file or socket object."); + +/* Module initialisation */ + +static int +ins(PyObject* d, char* symbol, long value) +{ + PyObject* v = PyInt_FromLong(value); + if (!v || PyDict_SetItemString(d, symbol, v) < 0) + return -1; + + Py_DECREF(v); + return 0; +} + +#define INS(x) if (ins(d, #x, (long)x)) return -1 + +static int +all_ins(PyObject* d) +{ + if (ins(d, "LOCK_SH", (long)LOCK_SH)) return -1; + if (ins(d, "LOCK_EX", (long)LOCK_EX)) return -1; + if (ins(d, "LOCK_NB", (long)LOCK_NB)) return -1; + if (ins(d, "LOCK_UN", (long)LOCK_UN)) return -1; +/* GNU extensions, as of glibc 2.2.4 */ +#ifdef LOCK_MAND + if (ins(d, "LOCK_MAND", (long)LOCK_MAND)) return -1; +#endif +#ifdef LOCK_READ + if (ins(d, "LOCK_READ", (long)LOCK_READ)) return -1; +#endif +#ifdef LOCK_WRITE + if (ins(d, "LOCK_WRITE", (long)LOCK_WRITE)) return -1; +#endif +#ifdef LOCK_RW + if (ins(d, "LOCK_RW", (long)LOCK_RW)) return -1; +#endif + +#ifdef F_DUPFD + if (ins(d, "F_DUPFD", (long)F_DUPFD)) return -1; +#endif +#ifdef F_GETFD + if (ins(d, "F_GETFD", (long)F_GETFD)) return -1; +#endif +#ifdef F_SETFD + if (ins(d, "F_SETFD", (long)F_SETFD)) return -1; +#endif +#ifdef F_GETFL + if (ins(d, "F_GETFL", (long)F_GETFL)) return -1; +#endif +#ifdef F_SETFL + if (ins(d, "F_SETFL", (long)F_SETFL)) return -1; +#endif +#ifdef F_GETLK + if (ins(d, "F_GETLK", (long)F_GETLK)) return -1; +#endif +#ifdef F_SETLK + if (ins(d, "F_SETLK", (long)F_SETLK)) return -1; +#endif +#ifdef F_SETLKW + if (ins(d, "F_SETLKW", (long)F_SETLKW)) return -1; +#endif +#ifdef F_GETOWN + if (ins(d, "F_GETOWN", (long)F_GETOWN)) return -1; +#endif +#ifdef F_SETOWN + if (ins(d, "F_SETOWN", (long)F_SETOWN)) return -1; +#endif +#ifdef F_GETSIG + if (ins(d, "F_GETSIG", (long)F_GETSIG)) return -1; +#endif +#ifdef F_SETSIG + if (ins(d, "F_SETSIG", (long)F_SETSIG)) return -1; +#endif +#ifdef F_RDLCK + if (ins(d, "F_RDLCK", (long)F_RDLCK)) return -1; +#endif +#ifdef F_WRLCK + if (ins(d, "F_WRLCK", (long)F_WRLCK)) return -1; +#endif +#ifdef F_UNLCK + if (ins(d, "F_UNLCK", (long)F_UNLCK)) return -1; +#endif +/* LFS constants */ +#ifdef F_GETLK64 + if (ins(d, "F_GETLK64", (long)F_GETLK64)) return -1; +#endif +#ifdef F_SETLK64 + if (ins(d, "F_SETLK64", (long)F_SETLK64)) return -1; +#endif +#ifdef F_SETLKW64 + if (ins(d, "F_SETLKW64", (long)F_SETLKW64)) return -1; +#endif +/* GNU extensions, as of glibc 2.2.4. */ +#ifdef F_SETLEASE + if (ins(d, "F_SETLEASE", (long)F_SETLEASE)) return -1; +#endif +#ifdef F_GETLEASE + if (ins(d, "F_GETLEASE", (long)F_GETLEASE)) return -1; +#endif +#ifdef F_NOTIFY + if (ins(d, "F_NOTIFY", (long)F_NOTIFY)) return -1; +#endif +/* Old BSD flock(). */ +#ifdef F_EXLCK + if (ins(d, "F_EXLCK", (long)F_EXLCK)) return -1; +#endif +#ifdef F_SHLCK + if (ins(d, "F_SHLCK", (long)F_SHLCK)) return -1; +#endif + +/* For F_{GET|SET}FL */ +#ifdef FD_CLOEXEC + if (ins(d, "FD_CLOEXEC", (long)FD_CLOEXEC)) return -1; +#endif + +/* For F_NOTIFY */ +#ifdef DN_ACCESS + if (ins(d, "DN_ACCESS", (long)DN_ACCESS)) return -1; +#endif +#ifdef DN_MODIFY + if (ins(d, "DN_MODIFY", (long)DN_MODIFY)) return -1; +#endif +#ifdef DN_CREATE + if (ins(d, "DN_CREATE", (long)DN_CREATE)) return -1; +#endif +#ifdef DN_DELETE + if (ins(d, "DN_DELETE", (long)DN_DELETE)) return -1; +#endif +#ifdef DN_RENAME + if (ins(d, "DN_RENAME", (long)DN_RENAME)) return -1; +#endif +#ifdef DN_ATTRIB + if (ins(d, "DN_ATTRIB", (long)DN_ATTRIB)) return -1; +#endif +#ifdef DN_MULTISHOT + if (ins(d, "DN_MULTISHOT", (long)DN_MULTISHOT)) return -1; +#endif + +#ifdef HAVE_STROPTS_H + /* Unix 98 guarantees that these are in stropts.h. */ + INS(I_PUSH); + INS(I_POP); + INS(I_LOOK); + INS(I_FLUSH); + INS(I_FLUSHBAND); + INS(I_SETSIG); + INS(I_GETSIG); + INS(I_FIND); + INS(I_PEEK); + INS(I_SRDOPT); + INS(I_GRDOPT); + INS(I_NREAD); + INS(I_FDINSERT); + INS(I_STR); + INS(I_SWROPT); +#ifdef I_GWROPT + /* despite the comment above, old-ish glibcs miss a couple... */ + INS(I_GWROPT); +#endif + INS(I_SENDFD); + INS(I_RECVFD); + INS(I_LIST); + INS(I_ATMARK); + INS(I_CKBAND); + INS(I_GETBAND); + INS(I_CANPUT); + INS(I_SETCLTIME); +#ifdef I_GETCLTIME + INS(I_GETCLTIME); +#endif + INS(I_LINK); + INS(I_UNLINK); + INS(I_PLINK); + INS(I_PUNLINK); +#endif + + return 0; +} + +PyMODINIT_FUNC +initfcntl(void) +{ + PyObject *m, *d; + + /* Create the module and add the functions and documentation */ + m = Py_InitModule3("fcntl", fcntl_methods, module_doc); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + all_ins(d); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/flmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/flmodule.c new file mode 100644 index 00000000..d84834eb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/flmodule.c @@ -0,0 +1,2138 @@ +/* FL module -- interface to Mark Overmars' FORMS Library. */ + +/* This code works with FORMS version 2.2 (if you defined + OBSOLETE_FORMS_CALLS), and 2.3. + FORMS can be ftp'ed from ftp.cs.ruu.nl (131.211.80.17), directory + /pub/SGI/FORMS. */ + +/* A half-hearted attempt has been made to allow programs using this + * module to exploit parallelism (through the threads module). No provisions + * have been made for multiple threads to use this module at the same time, + * though. So, a program with a forms thread and a non-forms thread will work + * fine but a program with two threads using forms will probably crash (unless + * the program takes precaution to ensure that only one thread can be in + * this module at any time). This will have to be fixed some time. + * (A fix will probably also have to synchronize with the gl module). + */ + +#include "Python.h" +#include "forms.h" +#include "structmember.h" + +/* Generic Forms Objects */ + +typedef struct { + PyObject_HEAD + FL_OBJECT *ob_generic; + PyMethodDef *ob_methods; + PyObject *ob_callback; + PyObject *ob_callback_arg; +} genericobject; + +static PyTypeObject GenericObjecttype; + +#define is_genericobject(g) ((g)->ob_type == &GenericObjecttype) + +/* List of all objects (XXX this should be a hash table on address...) */ + +static PyObject *allgenerics = NULL; +static int nfreeslots = 0; + +/* Add an object to the list of known objects */ + +static void +knowgeneric(genericobject *g) +{ + int i, n; + /* Create the list if it doesn't already exist */ + if (allgenerics == NULL) { + allgenerics = PyList_New(0); + if (allgenerics == NULL) { + PyErr_Clear(); + return; /* Too bad, live without allgenerics... */ + } + } + if (nfreeslots > 0) { + /* Search the list for reusable slots (NULL items) */ + /* XXX This can be made faster! */ + n = PyList_Size(allgenerics); + for (i = 0; i < n; i++) { + if (PyList_GetItem(allgenerics, i) == NULL) { + Py_INCREF(g); + PyList_SetItem(allgenerics, i, (PyObject *)g); + nfreeslots--; + return; + } + } + /* Strange... no free slots found... */ + nfreeslots = 0; + } + /* No free entries, append new item to the end */ + PyList_Append(allgenerics, (PyObject *)g); +} + +/* Find an object in the list of known objects */ + +static genericobject * +findgeneric(FL_OBJECT *generic) +{ + int i, n; + genericobject *g; + + if (allgenerics == NULL) + return NULL; /* No objects known yet */ + n = PyList_Size(allgenerics); + for (i = 0; i < n; i++) { + g = (genericobject *)PyList_GetItem(allgenerics, i); + if (g != NULL && g->ob_generic == generic) + return g; + } + return NULL; /* Unknown object */ +} + +/* Remove an object from the list of known objects */ + +static void +forgetgeneric(genericobject *g) +{ + int i, n; + + Py_XDECREF(g->ob_callback); + g->ob_callback = NULL; + Py_XDECREF(g->ob_callback_arg); + g->ob_callback_arg = NULL; + if (allgenerics == NULL) + return; /* No objects known yet */ + n = PyList_Size(allgenerics); + for (i = 0; i < n; i++) { + if (g == (genericobject *)PyList_GetItem(allgenerics, i)) { + PyList_SetItem(allgenerics, i, (PyObject *)NULL); + nfreeslots++; + break; + } + } +} + +/* Called when a form is about to be freed -- + remove all the objects that we know about from it. */ + +static void +releaseobjects(FL_FORM *form) +{ + int i, n; + genericobject *g; + + if (allgenerics == NULL) + return; /* No objects known yet */ + n = PyList_Size(allgenerics); + for (i = 0; i < n; i++) { + g = (genericobject *)PyList_GetItem(allgenerics, i); + if (g != NULL && g->ob_generic->form == form) { + fl_delete_object(g->ob_generic); + /* The object is now unreachable for + do_forms and check_forms, so + delete it from the list of known objects */ + Py_XDECREF(g->ob_callback); + g->ob_callback = NULL; + Py_XDECREF(g->ob_callback_arg); + g->ob_callback_arg = NULL; + PyList_SetItem(allgenerics, i, (PyObject *)NULL); + nfreeslots++; + } + } +} + + +/* Methods of generic objects */ + +static PyObject * +generic_set_call_back(genericobject *g, PyObject *args) +{ + if (args == NULL) { + Py_XDECREF(g->ob_callback); + Py_XDECREF(g->ob_callback_arg); + g->ob_callback = NULL; + g->ob_callback_arg = NULL; + } + else { + if (!PyTuple_Check(args) || PyTuple_Size(args) != 2) { + PyErr_BadArgument(); + return NULL; + } + Py_XDECREF(g->ob_callback); + Py_XDECREF(g->ob_callback_arg); + g->ob_callback = PyTuple_GetItem(args, 0); + Py_INCREF(g->ob_callback); + g->ob_callback_arg = PyTuple_GetItem(args, 1); + Py_INCREF(g->ob_callback_arg); + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +generic_call(genericobject *g, void (*func)(FL_OBJECT *)) +{ + (*func)(g->ob_generic); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +generic_delete_object(genericobject *g) +{ + PyObject *res; + res = generic_call(g, fl_delete_object); + if (res != NULL) + forgetgeneric(g); + return res; +} + +static PyObject * +generic_show_object(genericobject *g) +{ + return generic_call(g, fl_show_object); +} + +static PyObject * +generic_hide_object(genericobject *g) +{ + return generic_call(g, fl_hide_object); +} + +static PyObject * +generic_redraw_object(genericobject *g) +{ + return generic_call(g, fl_redraw_object); +} + +#ifdef OBSOLETE_FORMS_CALLS + + /* (un)freeze_object() are obsolete in FORMS 2.2 and unsupported + in 2.3. Since there's no foolproof way to tell which version we're + using, we omit them unconditionally. */ + +static PyObject * +generic_freeze_object(genericobject *g) +{ + return generic_call(g, fl_freeze_object); +} + +static PyObject * +generic_unfreeze_object(genericobject *g) +{ + return generic_call(g, fl_unfreeze_object); +} + +#endif /* OBSOLETE_FORMS_CALLS */ + +static PyObject * +generic_activate_object(genericobject *g) +{ + return generic_call(g, fl_activate_object); +} + +static PyObject * +generic_deactivate_object(genericobject *g) +{ + return generic_call(g, fl_deactivate_object); +} + +static PyObject * +generic_set_object_shortcut(genericobject *g, PyObject *args) +{ + char *str; + if (!PyArg_ParseTuple(args, "s:set_object_shortcut", &str)) + return NULL; + fl_set_object_shortcut(g->ob_generic, str); + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef generic_methods[] = { + {"set_call_back", (PyCFunction)generic_set_call_back, METH_OLDARGS}, + {"delete_object", (PyCFunction)generic_delete_object, METH_NOARGS}, + {"show_object", (PyCFunction)generic_show_object, METH_NOARGS}, + {"hide_object", (PyCFunction)generic_hide_object, METH_NOARGS}, + {"redraw_object", (PyCFunction)generic_redraw_object, METH_NOARGS}, +#ifdef OBSOLETE_FORMS_CALLS + {"freeze_object", (PyCFunction)generic_freeze_object, METH_NOARGS}, + {"unfreeze_object", (PyCFunction)generic_unfreeze_object, METH_NOARGS}, +#endif + {"activate_object", (PyCFunction)generic_activate_object, METH_NOARGS}, + {"deactivate_object", (PyCFunction)generic_deactivate_object, METH_NOARGS}, + {"set_object_shortcut", (PyCFunction)generic_set_object_shortcut, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +static void +generic_dealloc(genericobject *g) +{ + fl_free_object(g->ob_generic); + Py_XDECREF(g->ob_callback); + Py_XDECREF(g->ob_callback_arg); + PyObject_Del(g); +} + +#define OFF(x) offsetof(FL_OBJECT, x) + +static struct memberlist generic_memberlist[] = { + {"objclass", T_INT, OFF(objclass), RO}, + {"type", T_INT, OFF(type), RO}, + {"boxtype", T_INT, OFF(boxtype)}, + {"x", T_FLOAT, OFF(x)}, + {"y", T_FLOAT, OFF(y)}, + {"w", T_FLOAT, OFF(w)}, + {"h", T_FLOAT, OFF(h)}, + {"col1", T_INT, OFF(col1)}, + {"col2", T_INT, OFF(col2)}, + {"align", T_INT, OFF(align)}, + {"lcol", T_INT, OFF(lcol)}, + {"lsize", T_FLOAT, OFF(lsize)}, + /* "label" is treated specially! */ + {"lstyle", T_INT, OFF(lstyle)}, + {"pushed", T_INT, OFF(pushed), RO}, + {"focus", T_INT, OFF(focus), RO}, + {"belowmouse", T_INT, OFF(belowmouse),RO}, +/* {"frozen", T_INT, OFF(frozen), RO}, */ + {"active", T_INT, OFF(active)}, + {"input", T_INT, OFF(input)}, + {"visible", T_INT, OFF(visible), RO}, + {"radio", T_INT, OFF(radio)}, + {"automatic", T_INT, OFF(automatic)}, + {NULL} /* Sentinel */ +}; + +#undef OFF + +static PyObject * +generic_getattr(genericobject *g, char *name) +{ + PyObject *meth; + + /* XXX Ought to special-case name "__methods__" */ + if (g-> ob_methods) { + meth = Py_FindMethod(g->ob_methods, (PyObject *)g, name); + if (meth != NULL) return meth; + PyErr_Clear(); + } + + meth = Py_FindMethod(generic_methods, (PyObject *)g, name); + if (meth != NULL) + return meth; + PyErr_Clear(); + + /* "label" is an exception, getmember only works for char pointers, + not for char arrays */ + if (strcmp(name, "label") == 0) + return PyString_FromString(g->ob_generic->label); + + return PyMember_Get((char *)g->ob_generic, generic_memberlist, name); +} + +static int +generic_setattr(genericobject *g, char *name, PyObject *v) +{ + int ret; + + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, + "can't delete forms object attributes"); + return -1; + } + + /* "label" is an exception: setmember doesn't set strings; + and FORMS wants you to call a function to set the label */ + if (strcmp(name, "label") == 0) { + if (!PyString_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "label attr must be string"); + return -1; + } + fl_set_object_label(g->ob_generic, PyString_AsString(v)); + return 0; + } + + ret = PyMember_Set((char *)g->ob_generic, generic_memberlist, name, v); + + /* Rather than calling all the various set_object_* functions, + we call fl_redraw_object here. This is sometimes redundant + but I doubt that's a big problem */ + if (ret == 0) + fl_redraw_object(g->ob_generic); + + return ret; +} + +static PyObject * +generic_repr(genericobject *g) +{ + char buf[100]; + PyOS_snprintf(buf, sizeof(buf), "", + g, g->ob_generic->objclass); + return PyString_FromString(buf); +} + +static PyTypeObject GenericObjecttype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "fl.FORMS_object", /*tp_name*/ + sizeof(genericobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)generic_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)generic_getattr, /*tp_getattr*/ + (setattrfunc)generic_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)generic_repr, /*tp_repr*/ +}; + +static PyObject * +newgenericobject(FL_OBJECT *generic, PyMethodDef *methods) +{ + genericobject *g; + g = PyObject_New(genericobject, &GenericObjecttype); + if (g == NULL) + return NULL; + g-> ob_generic = generic; + g->ob_methods = methods; + g->ob_callback = NULL; + g->ob_callback_arg = NULL; + knowgeneric(g); + return (PyObject *)g; +} + +/**********************************************************************/ +/* Some common calling sequences */ + +/* void func (object, float) */ +static PyObject * +call_forms_INf (void (*func)(FL_OBJECT *, float), FL_OBJECT *obj, PyObject *args) +{ + float parameter; + + if (!PyArg_Parse(args, "f", ¶meter)) return NULL; + + (*func) (obj, parameter); + + Py_INCREF(Py_None); + return Py_None; +} + +/* void func (object, float) */ +static PyObject * +call_forms_INfINf (void (*func)(FL_OBJECT *, float, float), FL_OBJECT *obj, PyObject *args) +{ + float par1, par2; + + if (!PyArg_Parse(args, "(ff)", &par1, &par2)) return NULL; + + (*func) (obj, par1, par2); + + Py_INCREF(Py_None); + return Py_None; +} + +/* void func (object, int) */ +static PyObject * +call_forms_INi (void (*func)(FL_OBJECT *, int), FL_OBJECT *obj, PyObject *args) +{ + int parameter; + + if (!PyArg_Parse(args, "i", ¶meter)) return NULL; + + (*func) (obj, parameter); + + Py_INCREF(Py_None); + return Py_None; +} + +/* void func (object, char) */ +static PyObject * +call_forms_INc (void (*func)(FL_OBJECT *, int), FL_OBJECT *obj, PyObject *args) +{ + char *a; + + if (!PyArg_Parse(args, "s", &a)) return NULL; + + (*func) (obj, a[0]); + + Py_INCREF(Py_None); + return Py_None; +} + +/* void func (object, string) */ +static PyObject * +call_forms_INstr (void (*func)(FL_OBJECT *, char *), FL_OBJECT *obj, PyObject *args) +{ + char *a; + + if (!PyArg_Parse(args, "s", &a)) return NULL; + + (*func) (obj, a); + + Py_INCREF(Py_None); + return Py_None; +} + + +/* void func (object, int, string) */ +static PyObject * +call_forms_INiINstr (void (*func)(FL_OBJECT *, int, char *), FL_OBJECT *obj, PyObject *args) +{ + char *b; + int a; + + if (!PyArg_Parse(args, "(is)", &a, &b)) return NULL; + + (*func) (obj, a, b); + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef UNUSED +/* void func (object, int, int) */ +static PyObject * +call_forms_INiINi (void (*func)(FL_OBJECT *, int, int), FL_OBJECT *obj, PyObject *args) +{ + int par1, par2; + + if (!PyArg_Parse(args, "(ii)", &par1, &par2)) return NULL; + + (*func) (obj, par1, par2); + + Py_INCREF(Py_None); + return Py_None; +} +#endif + +/* int func (object) */ +static PyObject * +call_forms_Ri (int (*func)(FL_OBJECT *), FL_OBJECT *obj) +{ + int retval; + + retval = (*func) (obj); + + return PyInt_FromLong ((long) retval); +} + +/* char * func (object) */ +static PyObject * +call_forms_Rstr (char * (*func)(FL_OBJECT *), FL_OBJECT *obj) +{ + char *str; + + str = (*func) (obj); + + if (str == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString (str); +} + +/* int func (object) */ +static PyObject * +call_forms_Rf (float (*func)(FL_OBJECT *), FL_OBJECT *obj) +{ + float retval; + + retval = (*func) (obj); + + return PyFloat_FromDouble (retval); +} + +static PyObject * +call_forms_OUTfOUTf (void (*func)(FL_OBJECT *, float *, float *), FL_OBJECT *obj) +{ + float f1, f2; + + (*func) (obj, &f1, &f2); + + return Py_BuildValue("(ff)", f1, f2); +} + +#ifdef UNUSED +static PyObject * +call_forms_OUTf (void (*func)(FL_OBJECT *, float *), FL_OBJECT *obj) +{ + float f; + + (*func) (obj, &f); + + return PyFloat_FromDouble (f); +} +#endif + +/**********************************************************************/ +/* Class : browser */ + +static PyObject * +set_browser_topline(genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_set_browser_topline, g-> ob_generic, args); +} + +static PyObject * +clear_browser(genericobject *g) +{ + return generic_call (g, fl_clear_browser); +} + +static PyObject * +add_browser_line (genericobject *g, PyObject *args) +{ + return call_forms_INstr (fl_add_browser_line, g-> ob_generic, args); +} + +static PyObject * +addto_browser (genericobject *g, PyObject *args) +{ + return call_forms_INstr (fl_addto_browser, g-> ob_generic, args); +} + +static PyObject * +insert_browser_line (genericobject *g, PyObject *args) +{ + return call_forms_INiINstr (fl_insert_browser_line, + g-> ob_generic, args); +} + +static PyObject * +delete_browser_line (genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_delete_browser_line, g-> ob_generic, args); +} + +static PyObject * +replace_browser_line (genericobject *g, PyObject *args) +{ + return call_forms_INiINstr (fl_replace_browser_line, + g-> ob_generic, args); +} + +static PyObject * +get_browser_line(genericobject *g, PyObject *args) +{ + int i; + char *str; + + if (!PyArg_Parse(args, "i", &i)) + return NULL; + + str = fl_get_browser_line (g->ob_generic, i); + + if (str == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString (str); +} + +static PyObject * +load_browser (genericobject *g, PyObject *args) +{ + /* XXX strictly speaking this is wrong since fl_load_browser + XXX returns int, not void */ + return call_forms_INstr (fl_load_browser, g-> ob_generic, args); +} + +static PyObject * +get_browser_maxline(genericobject *g) +{ + return call_forms_Ri (fl_get_browser_maxline, g-> ob_generic); +} + +static PyObject * +select_browser_line (genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_select_browser_line, g-> ob_generic, args); +} + +static PyObject * +deselect_browser_line (genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_deselect_browser_line, g-> ob_generic, args); +} + +static PyObject * +deselect_browser (genericobject *g) +{ + return generic_call (g, fl_deselect_browser); +} + +static PyObject * +isselected_browser_line (genericobject *g, PyObject *args) +{ + int i, j; + + if (!PyArg_Parse(args, "i", &i)) + return NULL; + + j = fl_isselected_browser_line (g->ob_generic, i); + + return PyInt_FromLong (j); +} + +static PyObject * +get_browser (genericobject *g) +{ + return call_forms_Ri (fl_get_browser, g-> ob_generic); +} + +static PyObject * +set_browser_fontsize (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_browser_fontsize, g-> ob_generic, args); +} + +static PyObject * +set_browser_fontstyle (genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_set_browser_fontstyle, g-> ob_generic, args); +} + +static PyObject * +set_browser_specialkey (genericobject *g, PyObject *args) +{ + return call_forms_INc(fl_set_browser_specialkey, g-> ob_generic, args); +} + +static PyMethodDef browser_methods[] = { + {"set_browser_topline", (PyCFunction)set_browser_topline, + METH_OLDARGS}, + {"clear_browser", (PyCFunction)clear_browser, + METH_NOARGS}, + {"add_browser_line", (PyCFunction)add_browser_line, + METH_OLDARGS}, + {"addto_browser", (PyCFunction)addto_browser, + METH_OLDARGS}, + {"insert_browser_line", (PyCFunction)insert_browser_line, + METH_OLDARGS}, + {"delete_browser_line", (PyCFunction)delete_browser_line, + METH_OLDARGS}, + {"replace_browser_line", (PyCFunction)replace_browser_line, + METH_OLDARGS}, + {"get_browser_line", (PyCFunction)get_browser_line, + METH_OLDARGS}, + {"load_browser", (PyCFunction)load_browser, + METH_OLDARGS}, + {"get_browser_maxline", (PyCFunction)get_browser_maxline, + METH_NOARGS,} + {"select_browser_line", (PyCFunction)select_browser_line, + METH_OLDARGS}, + {"deselect_browser_line", (PyCFunction)deselect_browser_line, + METH_OLDARGS}, + {"deselect_browser", (PyCFunction)deselect_browser, + METH_NOARGS,} + {"isselected_browser_line", (PyCFunction)isselected_browser_line, + METH_OLDARGS}, + {"get_browser", (PyCFunction)get_browser, + METH_NOARGS,} + {"set_browser_fontsize", (PyCFunction)set_browser_fontsize, + METH_OLDARGS}, + {"set_browser_fontstyle", (PyCFunction)set_browser_fontstyle, + METH_OLDARGS}, + {"set_browser_specialkey", (PyCFunction)set_browser_specialkey, + METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* Class: button */ + +static PyObject * +set_button(genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_set_button, g-> ob_generic, args); +} + +static PyObject * +get_button(genericobject *g) +{ + return call_forms_Ri (fl_get_button, g-> ob_generic); +} + +static PyObject * +get_button_numb(genericobject *g) +{ + return call_forms_Ri (fl_get_button_numb, g-> ob_generic); +} + +static PyObject * +set_button_shortcut(genericobject *g, PyObject *args) +{ + return call_forms_INstr (fl_set_button_shortcut, g-> ob_generic, args); +} + +static PyMethodDef button_methods[] = { + {"set_button", (PyCFunction)set_button, METH_OLDARGS}, + {"get_button", (PyCFunction)get_button, METH_NOARGS}, + {"get_button_numb", (PyCFunction)get_button_numb, METH_NOARGS}, + {"set_button_shortcut", (PyCFunction)set_button_shortcut, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* Class: choice */ + +static PyObject * +set_choice(genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_set_choice, g-> ob_generic, args); +} + +static PyObject * +get_choice(genericobject *g) +{ + return call_forms_Ri (fl_get_choice, g-> ob_generic); +} + +static PyObject * +clear_choice (genericobject *g) +{ + return generic_call (g, fl_clear_choice); +} + +static PyObject * +addto_choice (genericobject *g, PyObject *args) +{ + return call_forms_INstr (fl_addto_choice, g-> ob_generic, args); +} + +static PyObject * +replace_choice (genericobject *g, PyObject *args) +{ + return call_forms_INiINstr (fl_replace_choice, g-> ob_generic, args); +} + +static PyObject * +delete_choice (genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_delete_choice, g-> ob_generic, args); +} + +static PyObject * +get_choice_text (genericobject *g) +{ + return call_forms_Rstr (fl_get_choice_text, g-> ob_generic); +} + +static PyObject * +set_choice_fontsize (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_choice_fontsize, g-> ob_generic, args); +} + +static PyObject * +set_choice_fontstyle (genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_set_choice_fontstyle, g-> ob_generic, args); +} + +static PyMethodDef choice_methods[] = { + {"set_choice", (PyCFunction)set_choice, METH_OLDARGS}, + {"get_choice", (PyCFunction)get_choice, METH_NOARGS}, + {"clear_choice", (PyCFunction)clear_choice, METH_NOARGS}, + {"addto_choice", (PyCFunction)addto_choice, METH_OLDARGS}, + {"replace_choice", (PyCFunction)replace_choice, METH_OLDARGS}, + {"delete_choice", (PyCFunction)delete_choice, METH_OLDARGS}, + {"get_choice_text", (PyCFunction)get_choice_text, METH_NOARGS}, + {"set_choice_fontsize", (PyCFunction)set_choice_fontsize, METH_OLDARGS}, + {"set_choice_fontstyle",(PyCFunction)set_choice_fontstyle, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* Class : Clock */ + +static PyObject * +get_clock(genericobject *g) +{ + int i0, i1, i2; + + fl_get_clock (g->ob_generic, &i0, &i1, &i2); + + return Py_BuildValue("(iii)", i0, i1, i2); +} + +static PyMethodDef clock_methods[] = { + {"get_clock", (PyCFunction)get_clock, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* CLass : Counters */ + +static PyObject * +get_counter_value(genericobject *g) +{ + return call_forms_Rf (fl_get_counter_value, g-> ob_generic); +} + +static PyObject * +set_counter_value (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_counter_value, g-> ob_generic, args); +} + +static PyObject * +set_counter_precision (genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_set_counter_precision, g-> ob_generic, args); +} + +static PyObject * +set_counter_bounds (genericobject *g, PyObject *args) +{ + return call_forms_INfINf (fl_set_counter_bounds, g-> ob_generic, args); +} + +static PyObject * +set_counter_step (genericobject *g, PyObject *args) +{ + return call_forms_INfINf (fl_set_counter_step, g-> ob_generic, args); +} + +static PyObject * +set_counter_return (genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_set_counter_return, g-> ob_generic, args); +} + +static PyMethodDef counter_methods[] = { + {"set_counter_value", (PyCFunction)set_counter_value, + METH_OLDARGS}, + {"get_counter_value", (PyCFunction)get_counter_value, + METH_NOARGS}, + {"set_counter_bounds", (PyCFunction)set_counter_bounds, + METH_OLDARGS}, + {"set_counter_step", (PyCFunction)set_counter_step, + METH_OLDARGS}, + {"set_counter_precision", (PyCFunction)set_counter_precision, + METH_OLDARGS}, + {"set_counter_return", (PyCFunction)set_counter_return, + METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + + +/* Class: Dials */ + +static PyObject * +get_dial_value(genericobject *g) +{ + return call_forms_Rf (fl_get_dial_value, g-> ob_generic); +} + +static PyObject * +set_dial_value (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_dial_value, g-> ob_generic, args); +} + +static PyObject * +set_dial_bounds (genericobject *g, PyObject *args) +{ + return call_forms_INfINf (fl_set_dial_bounds, g-> ob_generic, args); +} + +static PyObject * +get_dial_bounds (genericobject *g) +{ + return call_forms_OUTfOUTf (fl_get_dial_bounds, g-> ob_generic); +} + +static PyObject * +set_dial_step (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_dial_step, g-> ob_generic, args); +} + +static PyMethodDef dial_methods[] = { + {"set_dial_value", (PyCFunction)set_dial_value, METH_OLDARGS}, + {"get_dial_value", (PyCFunction)get_dial_value, METH_NOARGS}, + {"set_dial_bounds", (PyCFunction)set_dial_bounds, METH_OLDARGS}, + {"get_dial_bounds", (PyCFunction)get_dial_bounds, METH_NOARGS}, + {"set_dial_step", (PyCFunction)set_dial_step, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* Class : Input */ + +static PyObject * +set_input (genericobject *g, PyObject *args) +{ + return call_forms_INstr (fl_set_input, g-> ob_generic, args); +} + +static PyObject * +get_input (genericobject *g) +{ + return call_forms_Rstr (fl_get_input, g-> ob_generic); +} + +static PyObject * +set_input_color (genericobject *g, PyObject *args) +{ + return call_forms_INfINf (fl_set_input_color, g-> ob_generic, args); +} + +static PyObject * +set_input_return (genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_set_input_return, g-> ob_generic, args); +} + +static PyMethodDef input_methods[] = { + {"set_input", (PyCFunction)set_input, METH_OLDARGS}, + {"get_input", (PyCFunction)get_input, METH_NOARGS}, + {"set_input_color", (PyCFunction)set_input_color, METH_OLDARGS}, + {"set_input_return", (PyCFunction)set_input_return, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + + +/* Class : Menu */ + +static PyObject * +set_menu (genericobject *g, PyObject *args) +{ + return call_forms_INstr (fl_set_menu, g-> ob_generic, args); +} + +static PyObject * +get_menu (genericobject *g) +{ + /* XXX strictly speaking this is wrong since fl_get_menu + XXX returns long, not int */ + return call_forms_Ri (fl_get_menu, g-> ob_generic); +} + +static PyObject * +get_menu_text (genericobject *g) +{ + return call_forms_Rstr (fl_get_menu_text, g-> ob_generic); +} + +static PyObject * +addto_menu (genericobject *g, PyObject *args) +{ + return call_forms_INstr (fl_addto_menu, g-> ob_generic, args); +} + +static PyMethodDef menu_methods[] = { + {"set_menu", (PyCFunction)set_menu, METH_OLDARGS}, + {"get_menu", (PyCFunction)get_menu, METH_NOARGS}, + {"get_menu_text", (PyCFunction)get_menu_text, METH_NOARGS}, + {"addto_menu", (PyCFunction)addto_menu, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + + +/* Class: Sliders */ + +static PyObject * +get_slider_value(genericobject *g) +{ + return call_forms_Rf (fl_get_slider_value, g-> ob_generic); +} + +static PyObject * +set_slider_value (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_slider_value, g-> ob_generic, args); +} + +static PyObject * +set_slider_bounds (genericobject *g, PyObject *args) +{ + return call_forms_INfINf (fl_set_slider_bounds, g-> ob_generic, args); +} + +static PyObject * +get_slider_bounds (genericobject *g) +{ + return call_forms_OUTfOUTf(fl_get_slider_bounds, g-> ob_generic); +} + +static PyObject * +set_slider_return (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_slider_return, g-> ob_generic, args); +} + +static PyObject * +set_slider_size (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_slider_size, g-> ob_generic, args); +} + +static PyObject * +set_slider_precision (genericobject *g, PyObject *args) +{ + return call_forms_INi (fl_set_slider_precision, g-> ob_generic, args); +} + +static PyObject * +set_slider_step (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_slider_step, g-> ob_generic, args); +} + + +static PyMethodDef slider_methods[] = { + {"set_slider_value", (PyCFunction)set_slider_value, METH_OLDARGS}, + {"get_slider_value", (PyCFunction)get_slider_value, METH_NOARGS}, + {"set_slider_bounds", (PyCFunction)set_slider_bounds, METH_OLDARGS}, + {"get_slider_bounds", (PyCFunction)get_slider_bounds, METH_NOARGS}, + {"set_slider_return", (PyCFunction)set_slider_return, METH_OLDARGS}, + {"set_slider_size", (PyCFunction)set_slider_size, METH_OLDARGS}, + {"set_slider_precision",(PyCFunction)set_slider_precision, METH_OLDARGS}, + {"set_slider_step", (PyCFunction)set_slider_step, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +set_positioner_xvalue (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_positioner_xvalue, g-> ob_generic, args); +} + +static PyObject * +set_positioner_xbounds (genericobject *g, PyObject *args) +{ + return call_forms_INfINf (fl_set_positioner_xbounds, + g-> ob_generic, args); +} + +static PyObject * +set_positioner_yvalue (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_positioner_yvalue, g-> ob_generic, args); +} + +static PyObject * +set_positioner_ybounds (genericobject *g, PyObject *args) +{ + return call_forms_INfINf (fl_set_positioner_ybounds, + g-> ob_generic, args); +} + +static PyObject * +get_positioner_xvalue (genericobject *g) +{ + return call_forms_Rf (fl_get_positioner_xvalue, g-> ob_generic); +} + +static PyObject * +get_positioner_xbounds (genericobject *g) +{ + return call_forms_OUTfOUTf (fl_get_positioner_xbounds, g-> ob_generic); +} + +static PyObject * +get_positioner_yvalue (genericobject *g) +{ + return call_forms_Rf (fl_get_positioner_yvalue, g-> ob_generic); +} + +static PyObject * +get_positioner_ybounds (genericobject *g) +{ + return call_forms_OUTfOUTf (fl_get_positioner_ybounds, g-> ob_generic); +} + +static PyMethodDef positioner_methods[] = { + {"set_positioner_xvalue", (PyCFunction)set_positioner_xvalue, + METH_OLDARGS}, + {"set_positioner_yvalue", (PyCFunction)set_positioner_yvalue, + METH_OLDARGS}, + {"set_positioner_xbounds", (PyCFunction)set_positioner_xbounds, + METH_OLDARGS}, + {"set_positioner_ybounds", (PyCFunction)set_positioner_ybounds, + METH_OLDARGS}, + {"get_positioner_xvalue", (PyCFunction)get_positioner_xvalue, + METH_NOARGS}, + {"get_positioner_yvalue", (PyCFunction)get_positioner_yvalue, + METH_NOARGS}, + {"get_positioner_xbounds", (PyCFunction)get_positioner_xbounds, + METH_NOARGS}, + {"get_positioner_ybounds", (PyCFunction)get_positioner_ybounds, + METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* Class timer */ + +static PyObject * +set_timer (genericobject *g, PyObject *args) +{ + return call_forms_INf (fl_set_timer, g-> ob_generic, args); +} + +static PyObject * +get_timer (genericobject *g) +{ + return call_forms_Rf (fl_get_timer, g-> ob_generic); +} + +static PyMethodDef timer_methods[] = { + {"set_timer", (PyCFunction)set_timer, METH_OLDARGS}, + {"get_timer", (PyCFunction)get_timer, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* Form objects */ + +typedef struct { + PyObject_HEAD + FL_FORM *ob_form; +} formobject; + +static PyTypeObject Formtype; + +#define is_formobject(v) ((v)->ob_type == &Formtype) + +static PyObject * +form_show_form(formobject *f, PyObject *args) +{ + int place, border; + char *name; + if (!PyArg_Parse(args, "(iis)", &place, &border, &name)) + return NULL; + fl_show_form(f->ob_form, place, border, name); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +form_call(void (*func)(FL_FORM *), FL_FORM *f) +{ + (*func)(f); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +form_call_INiINi(void (*func)(FL_FORM *, int, int), FL_FORM *f, PyObject *args) +{ + int a, b; + + if (!PyArg_Parse(args, "(ii)", &a, &b)) return NULL; + + (*func)(f, a, b); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +form_call_INfINf(void (*func)(FL_FORM *, float, float), FL_FORM *f, PyObject *args) +{ + float a, b; + + if (!PyArg_Parse(args, "(ff)", &a, &b)) return NULL; + + (*func)(f, a, b); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +form_hide_form(formobject *f) +{ + return form_call(fl_hide_form, f-> ob_form); +} + +static PyObject * +form_redraw_form(formobject *f) +{ + return form_call(fl_redraw_form, f-> ob_form); +} + +static PyObject * +form_set_form_position(formobject *f, PyObject *args) +{ + return form_call_INiINi(fl_set_form_position, f-> ob_form, args); +} + +static PyObject * +form_set_form_size(formobject *f, PyObject *args) +{ + return form_call_INiINi(fl_set_form_size, f-> ob_form, args); +} + +static PyObject * +form_scale_form(formobject *f, PyObject *args) +{ + return form_call_INfINf(fl_scale_form, f-> ob_form, args); +} + +static PyObject * +generic_add_object(formobject *f, PyObject *args, FL_OBJECT *(*func)(int, float, float, float, float, char*), PyMethodDef *internal_methods) +{ + int type; + float x, y, w, h; + char *name; + FL_OBJECT *obj; + + if (!PyArg_Parse(args,"(iffffs)", &type,&x,&y,&w,&h,&name)) + return NULL; + + fl_addto_form (f-> ob_form); + + obj = (*func) (type, x, y, w, h, name); + + fl_end_form(); + + if (obj == NULL) { + PyErr_NoMemory(); + return NULL; + } + + return newgenericobject (obj, internal_methods); +} + +static PyObject * +form_add_button(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_button, button_methods); +} + +static PyObject * +form_add_lightbutton(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_lightbutton, button_methods); +} + +static PyObject * +form_add_roundbutton(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_roundbutton, button_methods); +} + +static PyObject * +form_add_menu (formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_menu, menu_methods); +} + +static PyObject * +form_add_slider(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_slider, slider_methods); +} + +static PyObject * +form_add_valslider(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_valslider, slider_methods); +} + +static PyObject * +form_add_dial(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_dial, dial_methods); +} + +static PyObject * +form_add_counter(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_counter, counter_methods); +} + +static PyObject * +form_add_clock(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_clock, clock_methods); +} + +static PyObject * +form_add_box(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_box, + (PyMethodDef *)NULL); +} + +static PyObject * +form_add_choice(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_choice, choice_methods); +} + +static PyObject * +form_add_browser(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_browser, browser_methods); +} + +static PyObject * +form_add_positioner(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_positioner, + positioner_methods); +} + +static PyObject * +form_add_input(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_input, input_methods); +} + +static PyObject * +form_add_text(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_text, + (PyMethodDef *)NULL); +} + +static PyObject * +form_add_timer(formobject *f, PyObject *args) +{ + return generic_add_object(f, args, fl_add_timer, timer_methods); +} + +static PyObject * +form_freeze_form(formobject *f) +{ + return form_call(fl_freeze_form, f-> ob_form); +} + +static PyObject * +form_unfreeze_form(formobject *f) +{ + return form_call(fl_unfreeze_form, f-> ob_form); +} + +static PyObject * +form_activate_form(formobject *f) +{ + return form_call(fl_activate_form, f-> ob_form); +} + +static PyObject * +form_deactivate_form(formobject *f) +{ + return form_call(fl_deactivate_form, f-> ob_form); +} + +static PyObject * +form_bgn_group(formobject *f, PyObject *args) +{ + FL_OBJECT *obj; + + fl_addto_form(f-> ob_form); + obj = fl_bgn_group(); + fl_end_form(); + + if (obj == NULL) { + PyErr_NoMemory(); + return NULL; + } + + return newgenericobject (obj, (PyMethodDef *) NULL); +} + +static PyObject * +form_end_group(formobject *f, PyObject *args) +{ + fl_addto_form(f-> ob_form); + fl_end_group(); + fl_end_form(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_find_first_or_last(FL_OBJECT *(*func)(FL_FORM *, int, float, float), formobject *f, PyObject *args) +{ + int type; + float mx, my; + FL_OBJECT *generic; + genericobject *g; + + if (!PyArg_Parse(args, "(iff)", &type, &mx, &my)) return NULL; + + generic = (*func) (f-> ob_form, type, mx, my); + + if (generic == NULL) + { + Py_INCREF(Py_None); + return Py_None; + } + + g = findgeneric(generic); + if (g == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "forms_find_{first|last} returns unknown object"); + return NULL; + } + Py_INCREF(g); + return (PyObject *) g; +} + +static PyObject * +form_find_first(formobject *f, PyObject *args) +{ + return forms_find_first_or_last(fl_find_first, f, args); +} + +static PyObject * +form_find_last(formobject *f, PyObject *args) +{ + return forms_find_first_or_last(fl_find_last, f, args); +} + +static PyObject * +form_set_object_focus(formobject *f, PyObject *args) +{ + genericobject *g; + if (args == NULL || !is_genericobject(args)) { + PyErr_BadArgument(); + return NULL; + } + g = (genericobject *)args; + fl_set_object_focus(f->ob_form, g->ob_generic); + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef form_methods[] = { +/* adm */ + {"show_form", (PyCFunction)form_show_form, METH_OLDARGS}, + {"hide_form", (PyCFunction)form_hide_form, METH_NOARGS}, + {"redraw_form", (PyCFunction)form_redraw_form, METH_NOARGS}, + {"set_form_position", (PyCFunction)form_set_form_position, METH_OLDARGS}, + {"set_form_size", (PyCFunction)form_set_form_size, METH_OLDARGS}, + {"scale_form", (PyCFunction)form_scale_form, METH_OLDARGS}, + {"freeze_form", (PyCFunction)form_freeze_form, METH_NOARGS}, + {"unfreeze_form", (PyCFunction)form_unfreeze_form, METH_NOARGS}, + {"activate_form", (PyCFunction)form_activate_form, METH_NOARGS}, + {"deactivate_form", (PyCFunction)form_deactivate_form, METH_NOARGS}, + {"bgn_group", (PyCFunction)form_bgn_group, METH_OLDARGS}, + {"end_group", (PyCFunction)form_end_group, METH_OLDARGS}, + {"find_first", (PyCFunction)form_find_first, METH_OLDARGS}, + {"find_last", (PyCFunction)form_find_last, METH_OLDARGS}, + {"set_object_focus", (PyCFunction)form_set_object_focus, METH_OLDARGS}, + +/* basic objects */ + {"add_button", (PyCFunction)form_add_button, METH_OLDARGS}, +/* {"add_bitmap", (method)form_add_bitmap, METH_OLDARGS}, */ + {"add_lightbutton", (PyCFunction)form_add_lightbutton, METH_OLDARGS}, + {"add_roundbutton", (PyCFunction)form_add_roundbutton, METH_OLDARGS}, + {"add_menu", (PyCFunction)form_add_menu, METH_OLDARGS}, + {"add_slider", (PyCFunction)form_add_slider, METH_OLDARGS}, + {"add_positioner", (PyCFunction)form_add_positioner, METH_OLDARGS}, + {"add_valslider", (PyCFunction)form_add_valslider, METH_OLDARGS}, + {"add_dial", (PyCFunction)form_add_dial, METH_OLDARGS}, + {"add_counter", (PyCFunction)form_add_counter, METH_OLDARGS}, + {"add_box", (PyCFunction)form_add_box, METH_OLDARGS}, + {"add_clock", (PyCFunction)form_add_clock, METH_OLDARGS}, + {"add_choice", (PyCFunction)form_add_choice, METH_OLDARGS}, + {"add_browser", (PyCFunction)form_add_browser, METH_OLDARGS}, + {"add_input", (PyCFunction)form_add_input, METH_OLDARGS}, + {"add_timer", (PyCFunction)form_add_timer, METH_OLDARGS}, + {"add_text", (PyCFunction)form_add_text, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +static void +form_dealloc(formobject *f) +{ + releaseobjects(f->ob_form); + if (f->ob_form->visible) + fl_hide_form(f->ob_form); + fl_free_form(f->ob_form); + PyObject_Del(f); +} + +#define OFF(x) offsetof(FL_FORM, x) + +static struct memberlist form_memberlist[] = { + {"window", T_LONG, OFF(window), RO}, + {"w", T_FLOAT, OFF(w)}, + {"h", T_FLOAT, OFF(h)}, + {"x", T_FLOAT, OFF(x), RO}, + {"y", T_FLOAT, OFF(y), RO}, + {"deactivated", T_INT, OFF(deactivated)}, + {"visible", T_INT, OFF(visible), RO}, + {"frozen", T_INT, OFF(frozen), RO}, + {"doublebuf", T_INT, OFF(doublebuf)}, + {NULL} /* Sentinel */ +}; + +#undef OFF + +static PyObject * +form_getattr(formobject *f, char *name) +{ + PyObject *meth; + + meth = Py_FindMethod(form_methods, (PyObject *)f, name); + if (meth != NULL) + return meth; + PyErr_Clear(); + return PyMember_Get((char *)f->ob_form, form_memberlist, name); +} + +static int +form_setattr(formobject *f, char *name, PyObject *v) +{ + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, + "can't delete form attributes"); + return -1; + } + + return PyMember_Set((char *)f->ob_form, form_memberlist, name, v); +} + +static PyObject * +form_repr(formobject *f) +{ + char buf[100]; + PyOS_snprintf(buf, sizeof(buf), "", + f, f->ob_form->window); + return PyString_FromString(buf); +} + +static PyTypeObject Formtype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "fl.FORMS_form", /*tp_name*/ + sizeof(formobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)form_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)form_getattr, /*tp_getattr*/ + (setattrfunc)form_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)form_repr, /*tp_repr*/ +}; + +static PyObject * +newformobject(FL_FORM *form) +{ + formobject *f; + f = PyObject_New(formobject, &Formtype); + if (f == NULL) + return NULL; + f->ob_form = form; + return (PyObject *)f; +} + + +/* The "fl" module */ + +static PyObject * +forms_make_form(PyObject *dummy, PyObject *args) +{ + int type; + float w, h; + FL_FORM *form; + if (!PyArg_Parse(args, "(iff)", &type, &w, &h)) + return NULL; + form = fl_bgn_form(type, w, h); + if (form == NULL) { + /* XXX Actually, cannot happen! */ + PyErr_NoMemory(); + return NULL; + } + fl_end_form(); + return newformobject(form); +} + +static PyObject * +forms_activate_all_forms(PyObject *f, PyObject *args) +{ + fl_activate_all_forms(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_deactivate_all_forms(PyObject *f, PyObject *args) +{ + fl_deactivate_all_forms(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *my_event_callback = NULL; + +static PyObject * +forms_set_event_call_back(PyObject *dummy, PyObject *args) +{ + if (args == Py_None) + args = NULL; + my_event_callback = args; + Py_XINCREF(args); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_do_or_check_forms(PyObject *dummy, FL_OBJECT *(*func)(void)) +{ + FL_OBJECT *generic; + genericobject *g; + PyObject *arg, *res; + + for (;;) { + Py_BEGIN_ALLOW_THREADS + generic = (*func)(); + Py_END_ALLOW_THREADS + if (generic == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + if (generic == FL_EVENT) { + int dev; + short val; + if (my_event_callback == NULL) + return PyInt_FromLong(-1L); + dev = fl_qread(&val); + arg = Py_BuildValue("(ih)", dev, val); + if (arg == NULL) + return NULL; + res = PyEval_CallObject(my_event_callback, arg); + Py_XDECREF(res); + Py_DECREF(arg); + if (res == NULL) + return NULL; /* Callback raised exception */ + continue; + } + g = findgeneric(generic); + if (g == NULL) { + /* Object not known to us (some dialogs cause this) */ + continue; /* Ignore it */ + } + if (g->ob_callback == NULL) { + Py_INCREF(g); + return ((PyObject *) g); + } + arg = Py_BuildValue("(OO)", (PyObject *)g, g->ob_callback_arg); + if (arg == NULL) + return NULL; + res = PyEval_CallObject(g->ob_callback, arg); + Py_XDECREF(res); + Py_DECREF(arg); + if (res == NULL) + return NULL; /* Callback raised exception */ + } +} + +static PyObject * +forms_do_forms(PyObject *dummy) +{ + return forms_do_or_check_forms(dummy, fl_do_forms); +} + +static PyObject * +forms_check_forms(PyObject *dummy) +{ + return forms_do_or_check_forms(dummy, fl_check_forms); +} + +static PyObject * +forms_do_only_forms(PyObject *dummy) +{ + return forms_do_or_check_forms(dummy, fl_do_only_forms); +} + +static PyObject * +forms_check_only_forms(PyObject *dummy) +{ + return forms_do_or_check_forms(dummy, fl_check_only_forms); +} + +#ifdef UNUSED +static PyObject * +fl_call(void (*func)(void)) +{ + (*func)(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +static PyObject * +forms_set_graphics_mode(PyObject *dummy, PyObject *args) +{ + int rgbmode, doublebuf; + + if (!PyArg_Parse(args, "(ii)", &rgbmode, &doublebuf)) + return NULL; + fl_set_graphics_mode(rgbmode,doublebuf); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_get_rgbmode(PyObject *dummy, PyObject *args) +{ + extern int fl_rgbmode; + + if (args != NULL) { + PyErr_BadArgument(); + return NULL; + } + return PyInt_FromLong((long)fl_rgbmode); +} + +static PyObject * +forms_show_errors(PyObject *dummy, PyObject *args) +{ + int show; + if (!PyArg_Parse(args, "i", &show)) + return NULL; + fl_show_errors(show); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_set_font_name(PyObject *dummy, PyObject *args) +{ + int numb; + char *name; + if (!PyArg_Parse(args, "(is)", &numb, &name)) + return NULL; + fl_set_font_name(numb, name); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +forms_qdevice(PyObject *self, PyObject *args) +{ + short arg1; + if (!PyArg_Parse(args, "h", &arg1)) + return NULL; + fl_qdevice(arg1); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_unqdevice(PyObject *self, PyObject *args) +{ + short arg1; + if (!PyArg_Parse(args, "h", &arg1)) + return NULL; + fl_unqdevice(arg1); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_isqueued(PyObject *self, PyObject *args) +{ + long retval; + short arg1; + if (!PyArg_Parse(args, "h", &arg1)) + return NULL; + retval = fl_isqueued(arg1); + + return PyInt_FromLong(retval); +} + +static PyObject * +forms_qtest(PyObject *self, PyObject *args) +{ + long retval; + retval = fl_qtest(); + return PyInt_FromLong(retval); +} + + +static PyObject * +forms_qread(PyObject *self, PyObject *args) +{ + int dev; + short val; + Py_BEGIN_ALLOW_THREADS + dev = fl_qread(&val); + Py_END_ALLOW_THREADS + return Py_BuildValue("(ih)", dev, val); +} + +static PyObject * +forms_qreset(PyObject *self) +{ + fl_qreset(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_qenter(PyObject *self, PyObject *args) +{ + short arg1, arg2; + if (!PyArg_Parse(args, "(hh)", &arg1, &arg2)) + return NULL; + fl_qenter(arg1, arg2); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_color(PyObject *self, PyObject *args) +{ + int arg; + + if (!PyArg_Parse(args, "i", &arg)) return NULL; + + fl_color((short) arg); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_mapcolor(PyObject *self, PyObject *args) +{ + int arg0, arg1, arg2, arg3; + + if (!PyArg_Parse(args, "(iiii)", &arg0, &arg1, &arg2, &arg3)) + return NULL; + + fl_mapcolor(arg0, (short) arg1, (short) arg2, (short) arg3); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_getmcolor(PyObject *self, PyObject *args) +{ + int arg; + short r, g, b; + + if (!PyArg_Parse(args, "i", &arg)) return NULL; + + fl_getmcolor(arg, &r, &g, &b); + + return Py_BuildValue("(hhh)", r, g, b); +} + +static PyObject * +forms_get_mouse(PyObject *self) +{ + float x, y; + + fl_get_mouse(&x, &y); + + return Py_BuildValue("(ff)", x, y); +} + +static PyObject * +forms_tie(PyObject *self, PyObject *args) +{ + short arg1, arg2, arg3; + if (!PyArg_Parse(args, "(hhh)", &arg1, &arg2, &arg3)) + return NULL; + fl_tie(arg1, arg2, arg3); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_show_message(PyObject *f, PyObject *args) +{ + char *a, *b, *c; + + if (!PyArg_Parse(args, "(sss)", &a, &b, &c)) return NULL; + + Py_BEGIN_ALLOW_THREADS + fl_show_message(a, b, c); + Py_END_ALLOW_THREADS + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +forms_show_choice(PyObject *f, PyObject *args) +{ + char *m1, *m2, *m3, *b1, *b2, *b3; + int nb; + char *format; + long rv; + + if (args == NULL || !PyTuple_Check(args)) { + PyErr_BadArgument(); + return NULL; + } + nb = PyTuple_Size(args) - 3; + if (nb <= 0) { + PyErr_SetString(PyExc_TypeError, + "need at least one button label"); + return NULL; + } + if (PyInt_Check(PyTuple_GetItem(args, 3))) { + PyErr_SetString(PyExc_TypeError, + "'number-of-buttons' argument not needed"); + return NULL; + } + switch (nb) { + case 1: format = "(ssss)"; break; + case 2: format = "(sssss)"; break; + case 3: format = "(ssssss)"; break; + default: + PyErr_SetString(PyExc_TypeError, "too many button labels"); + return NULL; + } + + if (!PyArg_Parse(args, format, &m1, &m2, &m3, &b1, &b2, &b3)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + rv = fl_show_choice(m1, m2, m3, nb, b1, b2, b3); + Py_END_ALLOW_THREADS + return PyInt_FromLong(rv); +} + +static PyObject * +forms_show_question(PyObject *f, PyObject *args) +{ + int ret; + char *a, *b, *c; + + if (!PyArg_Parse(args, "(sss)", &a, &b, &c)) return NULL; + + Py_BEGIN_ALLOW_THREADS + ret = fl_show_question(a, b, c); + Py_END_ALLOW_THREADS + + return PyInt_FromLong((long) ret); +} + +static PyObject * +forms_show_input(PyObject *f, PyObject *args) +{ + char *str; + char *a, *b; + + if (!PyArg_Parse(args, "(ss)", &a, &b)) return NULL; + + Py_BEGIN_ALLOW_THREADS + str = fl_show_input(a, b); + Py_END_ALLOW_THREADS + + if (str == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString(str); +} + +static PyObject * +forms_file_selector(PyObject *f, PyObject *args) +{ + char *str; + char *a, *b, *c, *d; + + if (!PyArg_Parse(args, "(ssss)", &a, &b, &c, &d)) return NULL; + + Py_BEGIN_ALLOW_THREADS + str = fl_show_file_selector(a, b, c, d); + Py_END_ALLOW_THREADS + + if (str == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString(str); +} + + +static PyObject * +forms_file_selector_func(PyObject *args, char *(*func)(void)) +{ + char *str; + + str = (*func) (); + + if (str == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString(str); +} + +static PyObject * +forms_get_directory(PyObject *f, PyObject *args) +{ + return forms_file_selector_func(args, fl_get_directory); +} + +static PyObject * +forms_get_pattern(PyObject *f, PyObject *args) +{ + return forms_file_selector_func(args, fl_get_pattern); +} + +static PyObject * +forms_get_filename(PyObject *f, PyObject *args) +{ + return forms_file_selector_func(args, fl_get_filename); +} + +static PyMethodDef forms_methods[] = { +/* adm */ + {"make_form", forms_make_form, METH_OLDARGS}, + {"activate_all_forms", forms_activate_all_forms, METH_OLDARGS}, + {"deactivate_all_forms",forms_deactivate_all_forms, METH_OLDARGS}, +/* gl support wrappers */ + {"qdevice", forms_qdevice, METH_OLDARGS}, + {"unqdevice", forms_unqdevice, METH_OLDARGS}, + {"isqueued", forms_isqueued, METH_OLDARGS}, + {"qtest", forms_qtest, METH_OLDARGS}, + {"qread", forms_qread, METH_OLDARGS}, +/* {"blkqread", forms_blkqread, METH_OLDARGS}, */ + {"qreset", forms_qreset, METH_NOARGS}, + {"qenter", forms_qenter, METH_OLDARGS}, + {"get_mouse", forms_get_mouse, METH_NOARGS}, + {"tie", forms_tie, METH_OLDARGS}, +/* {"new_events", forms_new_events, METH_OLDARGS}, */ + {"color", forms_color, METH_OLDARGS}, + {"mapcolor", forms_mapcolor, METH_OLDARGS}, + {"getmcolor", forms_getmcolor, METH_OLDARGS}, +/* interaction */ + {"do_forms", forms_do_forms, METH_NOARGS}, + {"do_only_forms", forms_do_only_forms, METH_NOARGS}, + {"check_forms", forms_check_forms, METH_NOARGS}, + {"check_only_forms", forms_check_only_forms, METH_NOARGS}, + {"set_event_call_back", forms_set_event_call_back, METH_OLDARGS}, +/* goodies */ + {"show_message", forms_show_message, METH_OLDARGS}, + {"show_question", forms_show_question, METH_OLDARGS}, + {"show_choice", forms_show_choice, METH_OLDARGS}, + {"show_input", forms_show_input, METH_OLDARGS}, + {"show_file_selector", forms_file_selector, METH_OLDARGS}, + {"file_selector", forms_file_selector, METH_OLDARGS}, /* BW compat */ + {"get_directory", forms_get_directory, METH_OLDARGS}, + {"get_pattern", forms_get_pattern, METH_OLDARGS}, + {"get_filename", forms_get_filename, METH_OLDARGS}, + {"set_graphics_mode", forms_set_graphics_mode, METH_OLDARGS}, + {"get_rgbmode", forms_get_rgbmode, METH_OLDARGS}, + {"show_errors", forms_show_errors, METH_OLDARGS}, + {"set_font_name", forms_set_font_name, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +PyMODINIT_FUNC +initfl(void) +{ + Py_InitModule("fl", forms_methods); + foreground(); + fl_init(); +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fmmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fmmodule.c new file mode 100644 index 00000000..e8710840 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fmmodule.c @@ -0,0 +1,262 @@ + +/* Font Manager module */ + +#include "Python.h" + +#include +#include +#include + + +/* Font Handle object implementation */ + +typedef struct { + PyObject_HEAD + fmfonthandle fh_fh; +} fhobject; + +static PyTypeObject Fhtype; + +#define is_fhobject(v) ((v)->ob_type == &Fhtype) + +static PyObject * +newfhobject(fmfonthandle fh) +{ + fhobject *fhp; + if (fh == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "error creating new font handle"); + return NULL; + } + fhp = PyObject_New(fhobject, &Fhtype); + if (fhp == NULL) + return NULL; + fhp->fh_fh = fh; + return (PyObject *)fhp; +} + +/* Font Handle methods */ + +static PyObject * +fh_scalefont(fhobject *self, PyObject *args) +{ + double size; + if (!PyArg_Parse(args, "d", &size)) + return NULL; + return newfhobject(fmscalefont(self->fh_fh, size)); +} + +/* XXX fmmakefont */ + +static PyObject * +fh_setfont(fhobject *self) +{ + fmsetfont(self->fh_fh); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +fh_getfontname(fhobject *self) +{ + char fontname[256]; + int len; + len = fmgetfontname(self->fh_fh, sizeof fontname, fontname); + if (len < 0) { + PyErr_SetString(PyExc_RuntimeError, "error in fmgetfontname"); + return NULL; + } + return PyString_FromStringAndSize(fontname, len); +} + +static PyObject * +fh_getcomment(fhobject *self) +{ + char comment[256]; + int len; + len = fmgetcomment(self->fh_fh, sizeof comment, comment); + if (len < 0) { + PyErr_SetString(PyExc_RuntimeError, "error in fmgetcomment"); + return NULL; + } + return PyString_FromStringAndSize(comment, len); +} + +static PyObject * +fh_getfontinfo(fhobject *self) +{ + fmfontinfo info; + if (fmgetfontinfo(self->fh_fh, &info) < 0) { + PyErr_SetString(PyExc_RuntimeError, "error in fmgetfontinfo"); + return NULL; + } + return Py_BuildValue("(llllllll)", + info.printermatched, + info.fixed_width, + info.xorig, + info.yorig, + info.xsize, + info.ysize, + info.height, + info.nglyphs); +} + +#if 0 +static PyObject * +fh_getwholemetrics(fhobject *self, PyObject *args) +{ +} +#endif + +static PyObject * +fh_getstrwidth(fhobject *self, PyObject *args) +{ + char *str; + if (!PyArg_Parse(args, "s", &str)) + return NULL; + return PyInt_FromLong(fmgetstrwidth(self->fh_fh, str)); +} + +static PyMethodDef fh_methods[] = { + {"scalefont", (PyCFunction)fh_scalefont, METH_OLDARGS}, + {"setfont", (PyCFunction)fh_setfont, METH_NOARGS}, + {"getfontname", (PyCFunction)fh_getfontname, METH_NOARGS}, + {"getcomment", (PyCFunction)fh_getcomment, METH_NOARGS}, + {"getfontinfo", (PyCFunction)fh_getfontinfo, METH_NOARGS}, +#if 0 + {"getwholemetrics", (PyCFunction)fh_getwholemetrics, METH_OLDARGS}, +#endif + {"getstrwidth", (PyCFunction)fh_getstrwidth, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +fh_getattr(fhobject *fhp, char *name) +{ + return Py_FindMethod(fh_methods, (PyObject *)fhp, name); +} + +static void +fh_dealloc(fhobject *fhp) +{ + fmfreefont(fhp->fh_fh); + PyObject_Del(fhp); +} + +static PyTypeObject Fhtype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "fm.font handle", /*tp_name*/ + sizeof(fhobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)fh_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)fh_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + + +/* Font Manager functions */ + +static PyObject * +fm_init(PyObject *self) +{ + fminit(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +fm_findfont(PyObject *self, PyObject *args) +{ + char *str; + if (!PyArg_Parse(args, "s", &str)) + return NULL; + return newfhobject(fmfindfont(str)); +} + +static PyObject * +fm_prstr(PyObject *self, PyObject *args) +{ + char *str; + if (!PyArg_Parse(args, "s", &str)) + return NULL; + fmprstr(str); + Py_INCREF(Py_None); + return Py_None; +} + +/* XXX This uses a global variable as temporary! Not re-entrant! */ + +static PyObject *fontlist; + +static void +clientproc(char *fontname) +{ + int err; + PyObject *v; + if (fontlist == NULL) + return; + v = PyString_FromString(fontname); + if (v == NULL) + err = -1; + else { + err = PyList_Append(fontlist, v); + Py_DECREF(v); + } + if (err != 0) { + Py_DECREF(fontlist); + fontlist = NULL; + } +} + +static PyObject * +fm_enumerate(PyObject *self) +{ + PyObject *res; + fontlist = PyList_New(0); + if (fontlist == NULL) + return NULL; + fmenumerate(clientproc); + res = fontlist; + fontlist = NULL; + return res; +} + +static PyObject * +fm_setpath(PyObject *self, PyObject *args) +{ + char *str; + if (!PyArg_Parse(args, "s", &str)) + return NULL; + fmsetpath(str); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +fm_fontpath(PyObject *self) +{ + return PyString_FromString(fmfontpath()); +} + +static PyMethodDef fm_methods[] = { + {"init", fm_init, METH_NOARGS}, + {"findfont", fm_findfont, METH_OLDARGS}, + {"enumerate", fm_enumerate, METH_NOARGS}, + {"prstr", fm_prstr, METH_OLDARGS}, + {"setpath", fm_setpath, METH_OLDARGS}, + {"fontpath", fm_fontpath, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + + +void +initfm(void) +{ + Py_InitModule("fm", fm_methods); + fminit(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fpectlmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fpectlmodule.c new file mode 100644 index 00000000..57466b58 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fpectlmodule.c @@ -0,0 +1,276 @@ +/* + --------------------------------------------------------------------- + / Copyright (c) 1996. \ + | The Regents of the University of California. | + | All rights reserved. | + | | + | Permission to use, copy, modify, and distribute this software for | + | any purpose without fee is hereby granted, provided that this en- | + | tire notice is included in all copies of any software which is or | + | includes a copy or modification of this software and in all | + | copies of the supporting documentation for such software. | + | | + | This work was produced at the University of California, Lawrence | + | Livermore National Laboratory under contract no. W-7405-ENG-48 | + | between the U.S. Department of Energy and The Regents of the | + | University of California for the operation of UC LLNL. | + | | + | DISCLAIMER | + | | + | This software was prepared as an account of work sponsored by an | + | agency of the United States Government. Neither the United States | + | Government nor the University of California nor any of their em- | + | ployees, makes any warranty, express or implied, or assumes any | + | liability or responsibility for the accuracy, completeness, or | + | usefulness of any information, apparatus, product, or process | + | disclosed, or represents that its use would not infringe | + | privately-owned rights. Reference herein to any specific commer- | + | cial products, process, or service by trade name, trademark, | + | manufacturer, or otherwise, does not necessarily constitute or | + | imply its endorsement, recommendation, or favoring by the United | + | States Government or the University of California. The views and | + | opinions of authors expressed herein do not necessarily state or | + | reflect those of the United States Government or the University | + | of California, and shall not be used for advertising or product | + \ endorsement purposes. / + --------------------------------------------------------------------- +*/ + +/* + Floating point exception control module. + + This Python module provides bare-bones control over floating point + units from several hardware manufacturers. Specifically, it allows + the user to turn on the generation of SIGFPE whenever any of the + three serious IEEE 754 exceptions (Division by Zero, Overflow, + Invalid Operation) occurs. We currently ignore Underflow and + Inexact Result exceptions, although those could certainly be added + if desired. + + The module also establishes a signal handler for SIGFPE during + initialization. This builds on code found in the Python + distribution at Include/pyfpe.h and Python/pyfpe.c. If those files + are not in your Python distribution, find them in a patch at + ftp://icf.llnl.gov/pub/python/busby/patches.961108.tgz. + + This module is only useful to you if it happens to include code + specific for your hardware and software environment. If you can + contribute OS-specific code for new platforms, or corrections for + the code provided, it will be greatly appreciated. + + ** Version 1.0: September 20, 1996. Lee Busby, LLNL. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "Python.h" +#include + +#if defined(__FreeBSD__) +# include +#endif + +#ifndef WANT_SIGFPE_HANDLER +/* Define locally if they are not defined in Python. This gives only + * the limited control to induce a core dump in case of an exception. + */ +#include +static jmp_buf PyFPE_jbuf; +static int PyFPE_counter = 0; +#endif + +typedef void Sigfunc(int); +static Sigfunc sigfpe_handler; +static void fpe_reset(Sigfunc *); + +static PyObject *fpe_error; +PyMODINIT_FUNC initfpectl(void); +static PyObject *turnon_sigfpe (PyObject *self,PyObject *args); +static PyObject *turnoff_sigfpe (PyObject *self,PyObject *args); + +static PyMethodDef fpectl_methods[] = { + {"turnon_sigfpe", (PyCFunction) turnon_sigfpe, METH_VARARGS}, + {"turnoff_sigfpe", (PyCFunction) turnoff_sigfpe, METH_VARARGS}, + {0,0} +}; + +static PyObject *turnon_sigfpe(PyObject *self,PyObject *args) +{ + /* Do any architecture-specific one-time only initialization here. */ + + fpe_reset(sigfpe_handler); + Py_INCREF (Py_None); + return Py_None; +} + +static void fpe_reset(Sigfunc *handler) +{ + /* Reset the exception handling machinery, and reset the signal + * handler for SIGFPE to the given handler. + */ + +/*-- IRIX -----------------------------------------------------------------*/ +#if defined(sgi) + /* See man page on handle_sigfpes -- must link with -lfpe + * My usage doesn't follow the man page exactly. Maybe somebody + * else can explain handle_sigfpes to me.... + * cc -c -I/usr/local/python/include fpectlmodule.c + * ld -shared -o fpectlmodule.so fpectlmodule.o -lfpe + */ +#include + typedef void user_routine (unsigned[5], int[2]); + typedef void abort_routine (unsigned long); + handle_sigfpes(_OFF, 0, + (user_routine *)0, + _TURN_OFF_HANDLER_ON_ERROR, + NULL); + handle_sigfpes(_ON, _EN_OVERFL | _EN_DIVZERO | _EN_INVALID, + (user_routine *)0, + _ABORT_ON_ERROR, + NULL); + PyOS_setsig(SIGFPE, handler); + +/*-- SunOS and Solaris ----------------------------------------------------*/ +#elif defined(sun) + /* References: ieee_handler, ieee_sun, ieee_functions, and ieee_flags + man pages (SunOS or Solaris) + cc -c -I/usr/local/python/include fpectlmodule.c + ld -G -o fpectlmodule.so -L/opt/SUNWspro/lib fpectlmodule.o -lsunmath -lm + */ +#include +#ifndef _SUNMATH_H + extern void nonstandard_arithmetic(void); + extern int ieee_flags(const char*, const char*, const char*, char **); + extern long ieee_handler(const char*, const char*, sigfpe_handler_type); +#endif + + char *mode="exception", *in="all", *out; + (void) nonstandard_arithmetic(); + (void) ieee_flags("clearall",mode,in,&out); + (void) ieee_handler("set","common",(sigfpe_handler_type)handler); + PyOS_setsig(SIGFPE, handler); + +/*-- HPUX -----------------------------------------------------------------*/ +#elif defined(__hppa) || defined(hppa) + /* References: fpsetmask man page */ + /* cc -Aa +z -c -I/usr/local/python/include fpectlmodule.c */ + /* ld -b -o fpectlmodule.sl fpectlmodule.o -lm */ +#include + fpsetdefaults(); + PyOS_setsig(SIGFPE, handler); + +/*-- IBM AIX --------------------------------------------------------------*/ +#elif defined(__AIX) || defined(_AIX) + /* References: fp_trap, fp_enable man pages */ +#include + fp_trap(FP_TRAP_SYNC); + fp_enable(TRP_INVALID | TRP_DIV_BY_ZERO | TRP_OVERFLOW); + PyOS_setsig(SIGFPE, handler); + +/*-- DEC ALPHA OSF --------------------------------------------------------*/ +#elif defined(__alpha) && defined(__osf__) + /* References: exception_intro, ieee man pages */ + /* cc -c -I/usr/local/python/include fpectlmodule.c */ + /* ld -shared -o fpectlmodule.so fpectlmodule.o */ +#include + unsigned long fp_control = + IEEE_TRAP_ENABLE_INV | IEEE_TRAP_ENABLE_DZE | IEEE_TRAP_ENABLE_OVF; + ieee_set_fp_control(fp_control); + PyOS_setsig(SIGFPE, handler); + +/*-- DEC ALPHA LINUX ------------------------------------------------------*/ +#elif defined(__alpha) && defined(linux) +#include + unsigned long fp_control = + IEEE_TRAP_ENABLE_INV | IEEE_TRAP_ENABLE_DZE | IEEE_TRAP_ENABLE_OVF; + ieee_set_fp_control(fp_control); + PyOS_setsig(SIGFPE, handler); + +/*-- DEC ALPHA VMS --------------------------------------------------------*/ +#elif defined(__ALPHA) && defined(__VMS) + PyOS_setsig(SIGFPE, handler); + +/*-- Cray Unicos ----------------------------------------------------------*/ +#elif defined(cray) + /* UNICOS delivers SIGFPE by default, but no matherr */ +#ifdef HAS_LIBMSET + libmset(-1); +#endif + PyOS_setsig(SIGFPE, handler); + +/*-- FreeBSD ----------------------------------------------------------------*/ +#elif defined(__FreeBSD__) + fpresetsticky(fpgetsticky()); + fpsetmask(FP_X_INV | FP_X_DZ | FP_X_OFL); + PyOS_setsig(SIGFPE, handler); + +/*-- Linux ----------------------------------------------------------------*/ +#elif defined(linux) +#ifdef __GLIBC__ +#include +#else +#include +#endif +#ifdef _FPU_SETCW + { + fpu_control_t cw = 0x1372; + _FPU_SETCW(cw); + } +#else + __setfpucw(0x1372); +#endif + PyOS_setsig(SIGFPE, handler); + +/*-- Microsoft Windows, NT ------------------------------------------------*/ +#elif defined(_MSC_VER) + /* Reference: Visual C++ Books Online 4.2, + Run-Time Library Reference, _control87, _controlfp */ +#include + unsigned int cw = _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW; + (void)_controlfp(0, cw); + PyOS_setsig(SIGFPE, handler); + +/*-- Give Up --------------------------------------------------------------*/ +#else + fputs("Operation not implemented\n", stderr); +#endif + +} + +static PyObject *turnoff_sigfpe(PyObject *self,PyObject *args) +{ +#ifdef __FreeBSD__ + fpresetsticky(fpgetsticky()); + fpsetmask(0); +#else + fputs("Operation not implemented\n", stderr); +#endif + Py_INCREF(Py_None); + return Py_None; +} + +static void sigfpe_handler(int signo) +{ + fpe_reset(sigfpe_handler); + if(PyFPE_counter) { + longjmp(PyFPE_jbuf, 1); + } else { + Py_FatalError("Unprotected floating point exception"); + } +} + +PyMODINIT_FUNC initfpectl(void) +{ + PyObject *m, *d; + m = Py_InitModule("fpectl", fpectl_methods); + d = PyModule_GetDict(m); + fpe_error = PyErr_NewException("fpectl.error", NULL, NULL); + if (fpe_error != NULL) + PyDict_SetItemString(d, "error", fpe_error); +} + +#ifdef __cplusplus +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fpetestmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fpetestmodule.c new file mode 100644 index 00000000..5932bb12 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/fpetestmodule.c @@ -0,0 +1,184 @@ +/* + --------------------------------------------------------------------- + / Copyright (c) 1996. \ + | The Regents of the University of California. | + | All rights reserved. | + | | + | Permission to use, copy, modify, and distribute this software for | + | any purpose without fee is hereby granted, provided that this en- | + | tire notice is included in all copies of any software which is or | + | includes a copy or modification of this software and in all | + | copies of the supporting documentation for such software. | + | | + | This work was produced at the University of California, Lawrence | + | Livermore National Laboratory under contract no. W-7405-ENG-48 | + | between the U.S. Department of Energy and The Regents of the | + | University of California for the operation of UC LLNL. | + | | + | DISCLAIMER | + | | + | This software was prepared as an account of work sponsored by an | + | agency of the United States Government. Neither the United States | + | Government nor the University of California nor any of their em- | + | ployees, makes any warranty, express or implied, or assumes any | + | liability or responsibility for the accuracy, completeness, or | + | usefulness of any information, apparatus, product, or process | + | disclosed, or represents that its use would not infringe | + | privately-owned rights. Reference herein to any specific commer- | + | cial products, process, or service by trade name, trademark, | + | manufacturer, or otherwise, does not necessarily constitute or | + | imply its endorsement, recommendation, or favoring by the United | + | States Government or the University of California. The views and | + | opinions of authors expressed herein do not necessarily state or | + | reflect those of the United States Government or the University | + | of California, and shall not be used for advertising or product | + \ endorsement purposes. / + --------------------------------------------------------------------- +*/ + +/* + Floating point exception test module. + + */ + +#include "Python.h" + +static PyObject *fpe_error; +PyMODINIT_FUNC initfpetest(void); +static PyObject *test(PyObject *self,PyObject *args); +static double db0(double); +static double overflow(double); +static double nest1(int, double); +static double nest2(int, double); +static double nest3(double); +static void printerr(double); + +static PyMethodDef fpetest_methods[] = { + {"test", (PyCFunction) test, METH_VARARGS}, + {0,0} +}; + +static PyObject *test(PyObject *self,PyObject *args) +{ + double r; + + fprintf(stderr,"overflow"); + r = overflow(1.e160); + printerr(r); + + fprintf(stderr,"\ndiv by 0"); + r = db0(0.0); + printerr(r); + + fprintf(stderr,"\nnested outer"); + r = nest1(0, 0.0); + printerr(r); + + fprintf(stderr,"\nnested inner"); + r = nest1(1, 1.0); + printerr(r); + + fprintf(stderr,"\ntrailing outer"); + r = nest1(2, 2.0); + printerr(r); + + fprintf(stderr,"\nnested prior"); + r = nest2(0, 0.0); + printerr(r); + + fprintf(stderr,"\nnested interior"); + r = nest2(1, 1.0); + printerr(r); + + fprintf(stderr,"\nnested trailing"); + r = nest2(2, 2.0); + printerr(r); + + Py_INCREF (Py_None); + return Py_None; +} + +static void printerr(double r) +{ + if(r == 3.1416){ + fprintf(stderr,"\tPASS\n"); + PyErr_Print(); + }else{ + fprintf(stderr,"\tFAIL\n"); + } + PyErr_Clear(); +} + +static double nest1(int i, double x) +{ + double a = 1.0; + + PyFPE_START_PROTECT("Division by zero, outer zone", return 3.1416) + if(i == 0){ + a = 1./x; + }else if(i == 1){ + /* This (following) message is never seen. */ + PyFPE_START_PROTECT("Division by zero, inner zone", return 3.1416) + a = 1./(1. - x); + PyFPE_END_PROTECT(a) + }else if(i == 2){ + a = 1./(2. - x); + } + PyFPE_END_PROTECT(a) + + return a; +} + +static double nest2(int i, double x) +{ + double a = 1.0; + PyFPE_START_PROTECT("Division by zero, prior error", return 3.1416) + if(i == 0){ + a = 1./x; + }else if(i == 1){ + a = nest3(x); + }else if(i == 2){ + a = 1./(2. - x); + } + PyFPE_END_PROTECT(a) + return a; +} + +static double nest3(double x) +{ + double result; + /* This (following) message is never seen. */ + PyFPE_START_PROTECT("Division by zero, nest3 error", return 3.1416) + result = 1./(1. - x); + PyFPE_END_PROTECT(result) + return result; +} + +static double db0(double x) +{ + double a; + PyFPE_START_PROTECT("Division by zero", return 3.1416) + a = 1./x; + PyFPE_END_PROTECT(a) + return a; +} + +static double overflow(double b) +{ + double a; + PyFPE_START_PROTECT("Overflow", return 3.1416) + a = b*b; + PyFPE_END_PROTECT(a) + return a; +} + +PyMODINIT_FUNC initfpetest(void) +{ + PyObject *m, *d; + + m = Py_InitModule("fpetest", fpetest_methods); + d = PyModule_GetDict(m); + fpe_error = PyErr_NewException("fpetest.error", NULL, NULL); + if (fpe_error != NULL) + PyDict_SetItemString(d, "error", fpe_error); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gc_weakref.txt b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gc_weakref.txt new file mode 100644 index 00000000..c6f71e7a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gc_weakref.txt @@ -0,0 +1,107 @@ +Before 2.3.3, Python's cyclic gc didn't pay any attention to weakrefs. +Segfaults in Zope3 resulted. + +weakrefs in Python are designed to, at worst, let *other* objects learn +that a given object has died, via a callback function. The weakly +referenced object itself is not passed to the callback, and the presumption +is that the weakly referenced object is unreachable trash at the time the +callback is invoked. + +That's usually true, but not always. Suppose a weakly referenced object +becomes part of a clump of cyclic trash. When enough cycles are broken by +cyclic gc that the object is reclaimed, the callback is invoked. If it's +possible for the callback to get at objects in the cycle(s), then it may be +possible for those objects to access (via strong references in the cycle) +the weakly referenced object being torn down, or other objects in the cycle +that have already suffered a tp_clear() call. There's no guarantee that an +object is in a sane state after tp_clear(). Bad things (including +segfaults) can happen right then, during the callback's execution, or can +happen at any later time if the callback manages to resurrect an insane +object. + +Note that if it's possible for the callback to get at objects in the trash +cycles, it must also be the case that the callback itself is part of the +trash cycles. Else the callback would have acted as an external root to +the current collection, and nothing reachable from it would be in cyclic +trash either. + +More, if the callback itself is in cyclic trash, then the weakref to which +the callback is attached must also be trash, and for the same kind of +reason: if the weakref acted as an external root, then the callback could +not have been cyclic trash. + +So a problem here requires that a weakref, that weakref's callback, and the +weakly referenced object, all be in cyclic trash at the same time. This +isn't easy to stumble into by accident while Python is running, and, indeed, +it took quite a while to dream up failing test cases. Zope3 saw segfaults +during shutdown, during the second call of gc in Py_Finalize, after most +modules had been torn down. That creates many trash cycles (esp. those +involving new-style classes), making the problem much more likely. Once you +know what's required to provoke the problem, though, it's easy to create +tests that segfault before shutdown. + +In 2.3.3, before breaking cycles, we first clear all the weakrefs with +callbacks in cyclic trash. Since the weakrefs *are* trash, and there's no +defined-- or even predictable --order in which tp_clear() gets called on +cyclic trash, it's defensible to first clear weakrefs with callbacks. It's +a feature of Python's weakrefs too that when a weakref goes away, the +callback (if any) associated with it is thrown away too, unexecuted. + +Just that much is almost enough to prevent problems, by throwing away +*almost* all the weakref callbacks that could get triggered by gc. The +problem remaining is that clearing a weakref with a callback decrefs the +callback object, and the callback object may *itself* be weakly referenced, +via another weakref with another callback. So the process of clearing +weakrefs can trigger callbacks attached to other weakrefs, and those +latter weakrefs may or may not be part of cyclic trash. + +So, to prevent any Python code from running while gc is invoking tp_clear() +on all the objects in cyclic trash, it's not quite enough just to invoke +tp_clear() on weakrefs with callbacks first. Instead the weakref module +grew a new private function (_PyWeakref_ClearRef) that does only part of +tp_clear(): it removes the weakref from the weakly-referenced object's list +of weakrefs, but does not decref the callback object. So calling +_PyWeakref_ClearRef(wr) ensures that wr's callback object will never +trigger, and (unlike weakref's tp_clear()) also prevents any callback +associated *with* wr's callback object from triggering. + +Then we can call tp_clear on all the cyclic objects and never trigger +Python code. + +After we do that, the callback objects still need to be decref'ed. Callbacks +(if any) *on* the callback objects that were also part of cyclic trash won't +get invoked, because we cleared all trash weakrefs with callbacks at the +start. Callbacks on the callback objects that were not part of cyclic trash +acted as external roots to everything reachable from them, so nothing +reachable from them was part of cyclic trash, so gc didn't do any damage to +objects reachable from them, and it's safe to call them at the end of gc. + +An alternative would have been to treat objects with callbacks like objects +with __del__ methods, refusing to collect them, appending them to gc.garbage +instead. That would have been much easier. Jim Fulton gave a strong +argument against that (on Python-Dev): + + There's a big difference between __del__ and weakref callbacks. + The __del__ method is "internal" to a design. When you design a + class with a del method, you know you have to avoid including the + class in cycles. + + Now, suppose you have a design that makes has no __del__ methods but + that does use cyclic data structures. You reason about the design, + run tests, and convince yourself you don't have a leak. + + Now, suppose some external code creates a weakref to one of your + objects. All of a sudden, you start leaking. You can look at your + code all you want and you won't find a reason for the leak. + +IOW, a class designer can out-think __del__ problems, but has no control +over who creates weakrefs to his classes or class instances. The class +user has little chance either of predicting when the weakrefs he creates +may end up in cycles. + +Callbacks on weakref callbacks are executed in an arbitrary order, and +that's not good (a primary reason not to collect cycles with objects with +__del__ methods is to avoid running finalizers in an arbitrary order). +However, a weakref callback on a weakref callback has got to be rare. +It's possible to do such a thing, so gc has to be robust against it, but +I doubt anyone has done it outside the test case I wrote for it. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gcmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gcmodule.c new file mode 100644 index 00000000..a3fcc9b1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gcmodule.c @@ -0,0 +1,1232 @@ +/* + + Reference Cycle Garbage Collection + ================================== + + Neil Schemenauer + + Based on a post on the python-dev list. Ideas from Guido van Rossum, + Eric Tiedemann, and various others. + + http://www.arctrix.com/nas/python/gc/ + http://www.python.org/pipermail/python-dev/2000-March/003869.html + http://www.python.org/pipermail/python-dev/2000-March/004010.html + http://www.python.org/pipermail/python-dev/2000-March/004022.html + + For a highlevel view of the collection process, read the collect + function. + +*/ + +#include "Python.h" + +/* Get an object's GC head */ +#define AS_GC(o) ((PyGC_Head *)(o)-1) + +/* Get the object given the GC head */ +#define FROM_GC(g) ((PyObject *)(((PyGC_Head *)g)+1)) + +/*** Global GC state ***/ + +struct gc_generation { + PyGC_Head head; + int threshold; /* collection threshold */ + int count; /* count of allocations or collections of younger + generations */ +}; + +#define NUM_GENERATIONS 3 +#define GEN_HEAD(n) (&generations[n].head) + +/* linked lists of container objects */ +static struct gc_generation generations[NUM_GENERATIONS] = { + /* PyGC_Head, threshold, count */ + {{{GEN_HEAD(0), GEN_HEAD(0), 0}}, 700, 0}, + {{{GEN_HEAD(1), GEN_HEAD(1), 0}}, 10, 0}, + {{{GEN_HEAD(2), GEN_HEAD(2), 0}}, 10, 0}, +}; + +PyGC_Head *_PyGC_generation0 = GEN_HEAD(0); + +static int enabled = 1; /* automatic collection enabled? */ + +/* true if we are currently running the collector */ +static int collecting = 0; + +/* list of uncollectable objects */ +static PyObject *garbage = NULL; + +/* Python string to use if unhandled exception occurs */ +static PyObject *gc_str = NULL; + +/* Python string used to look for __del__ attribute. */ +static PyObject *delstr = NULL; + +/* set for debugging information */ +#define DEBUG_STATS (1<<0) /* print collection statistics */ +#define DEBUG_COLLECTABLE (1<<1) /* print collectable objects */ +#define DEBUG_UNCOLLECTABLE (1<<2) /* print uncollectable objects */ +#define DEBUG_INSTANCES (1<<3) /* print instances */ +#define DEBUG_OBJECTS (1<<4) /* print other objects */ +#define DEBUG_SAVEALL (1<<5) /* save all garbage in gc.garbage */ +#define DEBUG_LEAK DEBUG_COLLECTABLE | \ + DEBUG_UNCOLLECTABLE | \ + DEBUG_INSTANCES | \ + DEBUG_OBJECTS | \ + DEBUG_SAVEALL +static int debug; + +/*-------------------------------------------------------------------------- +gc_refs values. + +Between collections, every gc'ed object has one of two gc_refs values: + +GC_UNTRACKED + The initial state; objects returned by PyObject_GC_Malloc are in this + state. The object doesn't live in any generation list, and its + tp_traverse slot must not be called. + +GC_REACHABLE + The object lives in some generation list, and its tp_traverse is safe to + call. An object transitions to GC_REACHABLE when PyObject_GC_Track + is called. + +During a collection, gc_refs can temporarily take on other states: + +>= 0 + At the start of a collection, update_refs() copies the true refcount + to gc_refs, for each object in the generation being collected. + subtract_refs() then adjusts gc_refs so that it equals the number of + times an object is referenced directly from outside the generation + being collected. + gc_refs remains >= 0 throughout these steps. + +GC_TENTATIVELY_UNREACHABLE + move_unreachable() then moves objects not reachable (whether directly or + indirectly) from outside the generation into an "unreachable" set. + Objects that are found to be reachable have gc_refs set to GC_REACHABLE + again. Objects that are found to be unreachable have gc_refs set to + GC_TENTATIVELY_UNREACHABLE. It's "tentatively" because the pass doing + this can't be sure until it ends, and GC_TENTATIVELY_UNREACHABLE may + transition back to GC_REACHABLE. + + Only objects with GC_TENTATIVELY_UNREACHABLE still set are candidates + for collection. If it's decided not to collect such an object (e.g., + it has a __del__ method), its gc_refs is restored to GC_REACHABLE again. +---------------------------------------------------------------------------- +*/ +#define GC_UNTRACKED _PyGC_REFS_UNTRACKED +#define GC_REACHABLE _PyGC_REFS_REACHABLE +#define GC_TENTATIVELY_UNREACHABLE _PyGC_REFS_TENTATIVELY_UNREACHABLE + +#define IS_TRACKED(o) ((AS_GC(o))->gc.gc_refs != GC_UNTRACKED) +#define IS_REACHABLE(o) ((AS_GC(o))->gc.gc_refs == GC_REACHABLE) +#define IS_TENTATIVELY_UNREACHABLE(o) ( \ + (AS_GC(o))->gc.gc_refs == GC_TENTATIVELY_UNREACHABLE) + +/*** list functions ***/ + +static void +gc_list_init(PyGC_Head *list) +{ + list->gc.gc_prev = list; + list->gc.gc_next = list; +} + +static int +gc_list_is_empty(PyGC_Head *list) +{ + return (list->gc.gc_next == list); +} + +static void +gc_list_append(PyGC_Head *node, PyGC_Head *list) +{ + node->gc.gc_next = list; + node->gc.gc_prev = list->gc.gc_prev; + node->gc.gc_prev->gc.gc_next = node; + list->gc.gc_prev = node; +} + +static void +gc_list_remove(PyGC_Head *node) +{ + node->gc.gc_prev->gc.gc_next = node->gc.gc_next; + node->gc.gc_next->gc.gc_prev = node->gc.gc_prev; + node->gc.gc_next = NULL; /* object is not currently tracked */ +} + +/* append a list onto another list, from becomes an empty list */ +static void +gc_list_merge(PyGC_Head *from, PyGC_Head *to) +{ + PyGC_Head *tail; + if (!gc_list_is_empty(from)) { + tail = to->gc.gc_prev; + tail->gc.gc_next = from->gc.gc_next; + tail->gc.gc_next->gc.gc_prev = tail; + to->gc.gc_prev = from->gc.gc_prev; + to->gc.gc_prev->gc.gc_next = to; + } + gc_list_init(from); +} + +static long +gc_list_size(PyGC_Head *list) +{ + PyGC_Head *gc; + long n = 0; + for (gc = list->gc.gc_next; gc != list; gc = gc->gc.gc_next) { + n++; + } + return n; +} + +/* Append objects in a GC list to a Python list. + * Return 0 if all OK, < 0 if error (out of memory for list). + */ +static int +append_objects(PyObject *py_list, PyGC_Head *gc_list) +{ + PyGC_Head *gc; + for (gc = gc_list->gc.gc_next; gc != gc_list; gc = gc->gc.gc_next) { + PyObject *op = FROM_GC(gc); + if (op != py_list) { + if (PyList_Append(py_list, op)) { + return -1; /* exception */ + } + } + } + return 0; +} + +/*** end of list stuff ***/ + + +/* Set all gc_refs = ob_refcnt. After this, gc_refs is > 0 for all objects + * in containers, and is GC_REACHABLE for all tracked gc objects not in + * containers. + */ +static void +update_refs(PyGC_Head *containers) +{ + PyGC_Head *gc = containers->gc.gc_next; + for (; gc != containers; gc = gc->gc.gc_next) { + assert(gc->gc.gc_refs == GC_REACHABLE); + gc->gc.gc_refs = FROM_GC(gc)->ob_refcnt; + } +} + +/* A traversal callback for subtract_refs. */ +static int +visit_decref(PyObject *op, void *data) +{ + assert(op != NULL); + if (PyObject_IS_GC(op)) { + PyGC_Head *gc = AS_GC(op); + /* We're only interested in gc_refs for objects in the + * generation being collected, which can be recognized + * because only they have positive gc_refs. + */ + assert(gc->gc.gc_refs != 0); /* else refcount was too small */ + if (gc->gc.gc_refs > 0) + gc->gc.gc_refs--; + } + return 0; +} + +/* Subtract internal references from gc_refs. After this, gc_refs is >= 0 + * for all objects in containers, and is GC_REACHABLE for all tracked gc + * objects not in containers. The ones with gc_refs > 0 are directly + * reachable from outside containers, and so can't be collected. + */ +static void +subtract_refs(PyGC_Head *containers) +{ + traverseproc traverse; + PyGC_Head *gc = containers->gc.gc_next; + for (; gc != containers; gc=gc->gc.gc_next) { + traverse = FROM_GC(gc)->ob_type->tp_traverse; + (void) traverse(FROM_GC(gc), + (visitproc)visit_decref, + NULL); + } +} + +/* A traversal callback for move_unreachable. */ +static int +visit_reachable(PyObject *op, PyGC_Head *reachable) +{ + if (PyObject_IS_GC(op)) { + PyGC_Head *gc = AS_GC(op); + const int gc_refs = gc->gc.gc_refs; + + if (gc_refs == 0) { + /* This is in move_unreachable's 'young' list, but + * the traversal hasn't yet gotten to it. All + * we need to do is tell move_unreachable that it's + * reachable. + */ + gc->gc.gc_refs = 1; + } + else if (gc_refs == GC_TENTATIVELY_UNREACHABLE) { + /* This had gc_refs = 0 when move_unreachable got + * to it, but turns out it's reachable after all. + * Move it back to move_unreachable's 'young' list, + * and move_unreachable will eventually get to it + * again. + */ + gc_list_remove(gc); + gc_list_append(gc, reachable); + gc->gc.gc_refs = 1; + } + /* Else there's nothing to do. + * If gc_refs > 0, it must be in move_unreachable's 'young' + * list, and move_unreachable will eventually get to it. + * If gc_refs == GC_REACHABLE, it's either in some other + * generation so we don't care about it, or move_unreachable + * already dealt with it. + * If gc_refs == GC_UNTRACKED, it must be ignored. + */ + else { + assert(gc_refs > 0 + || gc_refs == GC_REACHABLE + || gc_refs == GC_UNTRACKED); + } + } + return 0; +} + +/* Move the unreachable objects from young to unreachable. After this, + * all objects in young have gc_refs = GC_REACHABLE, and all objects in + * unreachable have gc_refs = GC_TENTATIVELY_UNREACHABLE. All tracked + * gc objects not in young or unreachable still have gc_refs = GC_REACHABLE. + * All objects in young after this are directly or indirectly reachable + * from outside the original young; and all objects in unreachable are + * not. + */ +static void +move_unreachable(PyGC_Head *young, PyGC_Head *unreachable) +{ + PyGC_Head *gc = young->gc.gc_next; + + /* Invariants: all objects "to the left" of us in young have gc_refs + * = GC_REACHABLE, and are indeed reachable (directly or indirectly) + * from outside the young list as it was at entry. All other objects + * from the original young "to the left" of us are in unreachable now, + * and have gc_refs = GC_TENTATIVELY_UNREACHABLE. All objects to the + * left of us in 'young' now have been scanned, and no objects here + * or to the right have been scanned yet. + */ + + while (gc != young) { + PyGC_Head *next; + + if (gc->gc.gc_refs) { + /* gc is definitely reachable from outside the + * original 'young'. Mark it as such, and traverse + * its pointers to find any other objects that may + * be directly reachable from it. Note that the + * call to tp_traverse may append objects to young, + * so we have to wait until it returns to determine + * the next object to visit. + */ + PyObject *op = FROM_GC(gc); + traverseproc traverse = op->ob_type->tp_traverse; + assert(gc->gc.gc_refs > 0); + gc->gc.gc_refs = GC_REACHABLE; + (void) traverse(op, + (visitproc)visit_reachable, + (void *)young); + next = gc->gc.gc_next; + } + else { + /* This *may* be unreachable. To make progress, + * assume it is. gc isn't directly reachable from + * any object we've already traversed, but may be + * reachable from an object we haven't gotten to yet. + * visit_reachable will eventually move gc back into + * young if that's so, and we'll see it again. + */ + next = gc->gc.gc_next; + gc_list_remove(gc); + gc_list_append(gc, unreachable); + gc->gc.gc_refs = GC_TENTATIVELY_UNREACHABLE; + } + gc = next; + } +} + +/* Return true if object has a finalization method. + * CAUTION: An instance of an old-style class has to be checked for a + *__del__ method, and earlier versions of this used to call PyObject_HasAttr, + * which in turn could call the class's __getattr__ hook (if any). That + * could invoke arbitrary Python code, mutating the object graph in arbitrary + * ways, and that was the source of some excruciatingly subtle bugs. + */ +static int +has_finalizer(PyObject *op) +{ + if (PyInstance_Check(op)) { + assert(delstr != NULL); + return _PyInstance_Lookup(op, delstr) != NULL; + } + else if (PyType_HasFeature(op->ob_type, Py_TPFLAGS_HEAPTYPE)) + return op->ob_type->tp_del != NULL; + else + return 0; +} + +/* Move the objects in unreachable with __del__ methods into finalizers, + * and weakrefs with callbacks into wr_callbacks. + * The objects remaining in unreachable do not have __del__ methods, and are + * not weakrefs with callbacks. + * The objects moved have gc_refs changed to GC_REACHABLE; the objects + * remaining in unreachable are left at GC_TENTATIVELY_UNREACHABLE. + */ +static void +move_troublemakers(PyGC_Head *unreachable, + PyGC_Head *finalizers, + PyGC_Head *wr_callbacks) +{ + PyGC_Head *gc = unreachable->gc.gc_next; + + while (gc != unreachable) { + PyObject *op = FROM_GC(gc); + PyGC_Head *next = gc->gc.gc_next; + + assert(IS_TENTATIVELY_UNREACHABLE(op)); + + if (has_finalizer(op)) { + gc_list_remove(gc); + gc_list_append(gc, finalizers); + gc->gc.gc_refs = GC_REACHABLE; + } + else if (PyWeakref_Check(op) && + ((PyWeakReference *)op)->wr_callback) { + gc_list_remove(gc); + gc_list_append(gc, wr_callbacks); + gc->gc.gc_refs = GC_REACHABLE; + } + gc = next; + } +} + +/* A traversal callback for move_finalizer_reachable. */ +static int +visit_move(PyObject *op, PyGC_Head *tolist) +{ + if (PyObject_IS_GC(op)) { + if (IS_TENTATIVELY_UNREACHABLE(op)) { + PyGC_Head *gc = AS_GC(op); + gc_list_remove(gc); + gc_list_append(gc, tolist); + gc->gc.gc_refs = GC_REACHABLE; + } + } + return 0; +} + +/* Move objects that are reachable from finalizers, from the unreachable set + * into finalizers set. + */ +static void +move_finalizer_reachable(PyGC_Head *finalizers) +{ + traverseproc traverse; + PyGC_Head *gc = finalizers->gc.gc_next; + for (; gc != finalizers; gc = gc->gc.gc_next) { + /* Note that the finalizers list may grow during this. */ + traverse = FROM_GC(gc)->ob_type->tp_traverse; + (void) traverse(FROM_GC(gc), + (visitproc)visit_move, + (void *)finalizers); + } +} + +/* Clear all trash weakrefs with callbacks. This clears weakrefs first, + * which has the happy result of disabling the callbacks without executing + * them. A nasty technical complication: a weakref callback can itself be + * the target of a weakref, in which case decrefing the callback can cause + * another callback to trigger. But we can't allow arbitrary Python code to + * get executed at this point (the callback on the callback may try to muck + * with other cyclic trash we're trying to collect, even resurrecting it + * while we're in the middle of doing tp_clear() on the trash). + * + * The private _PyWeakref_ClearRef() function exists so that we can clear + * the reference in a weakref without triggering a callback on the callback. + * + * We have to save the callback objects and decref them later. But we can't + * allocate new memory to save them (if we can't get new memory, we're dead). + * So we grab a new reference on the clear'ed weakref, which prevents the + * rest of gc from reclaiming it. _PyWeakref_ClearRef() leaves the + * weakref's wr_callback member intact. + * + * In the end, then, wr_callbacks consists of cleared weakrefs that are + * immune from collection. Near the end of gc, after collecting all the + * cyclic trash, we call release_weakrefs(). That releases our references + * to the cleared weakrefs, which in turn may trigger callbacks on their + * callbacks. + */ +static void +clear_weakrefs(PyGC_Head *wr_callbacks) +{ + PyGC_Head *gc = wr_callbacks->gc.gc_next; + + for (; gc != wr_callbacks; gc = gc->gc.gc_next) { + PyObject *op = FROM_GC(gc); + PyWeakReference *wr; + + assert(IS_REACHABLE(op)); + assert(PyWeakref_Check(op)); + wr = (PyWeakReference *)op; + assert(wr->wr_callback != NULL); + Py_INCREF(op); + _PyWeakref_ClearRef(wr); + } +} + +/* Called near the end of gc. This gives up the references we own to + * cleared weakrefs, allowing them to get collected, and in turn decref'ing + * their callbacks. + * + * If a callback object is itself the target of a weakref callback, + * decref'ing the callback object may trigger that other callback. If + * that other callback was part of the cyclic trash in this generation, + * that won't happen, since we cleared *all* trash-weakref callbacks near + * the start of gc. If that other callback was not part of the cyclic trash + * in this generation, then it acted like an external root to this round + * of gc, so all the objects reachable from that callback are still alive. + * + * Giving up the references to the weakref objects will probably make + * them go away too. However, if a weakref is reachable from finalizers, + * it won't go away. We move it to the old generation then. Since a + * weakref object doesn't have a finalizer, that's the right thing to do (it + * doesn't belong in gc.garbage). + * + * We return the number of weakref objects freed (those not appended to old). + */ +static int +release_weakrefs(PyGC_Head *wr_callbacks, PyGC_Head *old) +{ + int num_freed = 0; + + while (! gc_list_is_empty(wr_callbacks)) { + PyGC_Head *gc = wr_callbacks->gc.gc_next; + PyObject *op = FROM_GC(gc); + + assert(IS_REACHABLE(op)); + assert(PyWeakref_Check(op)); + assert(((PyWeakReference *)op)->wr_callback != NULL); + Py_DECREF(op); + if (wr_callbacks->gc.gc_next == gc) { + /* object is still alive -- move it */ + gc_list_remove(gc); + gc_list_append(gc, old); + } + else + ++num_freed; + } + return num_freed; +} + +static void +debug_instance(char *msg, PyInstanceObject *inst) +{ + char *cname; + /* simple version of instance_repr */ + PyObject *classname = inst->in_class->cl_name; + if (classname != NULL && PyString_Check(classname)) + cname = PyString_AsString(classname); + else + cname = "?"; + PySys_WriteStderr("gc: %.100s <%.100s instance at %p>\n", + msg, cname, inst); +} + +static void +debug_cycle(char *msg, PyObject *op) +{ + if ((debug & DEBUG_INSTANCES) && PyInstance_Check(op)) { + debug_instance(msg, (PyInstanceObject *)op); + } + else if (debug & DEBUG_OBJECTS) { + PySys_WriteStderr("gc: %.100s <%.100s %p>\n", + msg, op->ob_type->tp_name, op); + } +} + +/* Handle uncollectable garbage (cycles with finalizers, and stuff reachable + * only from such cycles). + * If DEBUG_SAVEALL, all objects in finalizers are appended to the module + * garbage list (a Python list), else only the objects in finalizers with + * __del__ methods are appended to garbage. All objects in finalizers are + * merged into the old list regardless. + * Returns 0 if all OK, <0 on error (out of memory to grow the garbage list). + * The finalizers list is made empty on a successful return. + */ +static int +handle_finalizers(PyGC_Head *finalizers, PyGC_Head *old) +{ + PyGC_Head *gc = finalizers->gc.gc_next; + + if (garbage == NULL) { + garbage = PyList_New(0); + if (garbage == NULL) + Py_FatalError("gc couldn't create gc.garbage list"); + } + for (; gc != finalizers; gc = gc->gc.gc_next) { + PyObject *op = FROM_GC(gc); + + if ((debug & DEBUG_SAVEALL) || has_finalizer(op)) { + if (PyList_Append(garbage, op) < 0) + return -1; + } + } + + gc_list_merge(finalizers, old); + return 0; +} + +/* Break reference cycles by clearing the containers involved. This is + * tricky business as the lists can be changing and we don't know which + * objects may be freed. It is possible I screwed something up here. + */ +static void +delete_garbage(PyGC_Head *collectable, PyGC_Head *old) +{ + inquiry clear; + + while (!gc_list_is_empty(collectable)) { + PyGC_Head *gc = collectable->gc.gc_next; + PyObject *op = FROM_GC(gc); + + assert(IS_TENTATIVELY_UNREACHABLE(op)); + if (debug & DEBUG_SAVEALL) { + PyList_Append(garbage, op); + } + else { + if ((clear = op->ob_type->tp_clear) != NULL) { + Py_INCREF(op); + clear(op); + Py_DECREF(op); + } + } + if (collectable->gc.gc_next == gc) { + /* object is still alive, move it, it may die later */ + gc_list_remove(gc); + gc_list_append(gc, old); + gc->gc.gc_refs = GC_REACHABLE; + } + } +} + +/* This is the main function. Read this to understand how the + * collection process works. */ +static long +collect(int generation) +{ + int i; + long m = 0; /* # objects collected */ + long n = 0; /* # unreachable objects that couldn't be collected */ + PyGC_Head *young; /* the generation we are examining */ + PyGC_Head *old; /* next older generation */ + PyGC_Head unreachable; /* non-problematic unreachable trash */ + PyGC_Head finalizers; /* objects with, & reachable from, __del__ */ + PyGC_Head wr_callbacks; /* weakrefs with callbacks */ + PyGC_Head *gc; + + if (delstr == NULL) { + delstr = PyString_InternFromString("__del__"); + if (delstr == NULL) + Py_FatalError("gc couldn't allocate \"__del__\""); + } + + if (debug & DEBUG_STATS) { + PySys_WriteStderr("gc: collecting generation %d...\n", + generation); + PySys_WriteStderr("gc: objects in each generation:"); + for (i = 0; i < NUM_GENERATIONS; i++) { + PySys_WriteStderr(" %ld", gc_list_size(GEN_HEAD(i))); + } + PySys_WriteStderr("\n"); + } + + /* update collection and allocation counters */ + if (generation+1 < NUM_GENERATIONS) + generations[generation+1].count += 1; + for (i = 0; i <= generation; i++) + generations[i].count = 0; + + /* merge younger generations with one we are currently collecting */ + for (i = 0; i < generation; i++) { + gc_list_merge(GEN_HEAD(i), GEN_HEAD(generation)); + } + + /* handy references */ + young = GEN_HEAD(generation); + if (generation < NUM_GENERATIONS-1) + old = GEN_HEAD(generation+1); + else + old = young; + + /* Using ob_refcnt and gc_refs, calculate which objects in the + * container set are reachable from outside the set (ie. have a + * refcount greater than 0 when all the references within the + * set are taken into account + */ + update_refs(young); + subtract_refs(young); + + /* Leave everything reachable from outside young in young, and move + * everything else (in young) to unreachable. + * NOTE: This used to move the reachable objects into a reachable + * set instead. But most things usually turn out to be reachable, + * so it's more efficient to move the unreachable things. + */ + gc_list_init(&unreachable); + move_unreachable(young, &unreachable); + + /* Move reachable objects to next generation. */ + if (young != old) + gc_list_merge(young, old); + + /* All objects in unreachable are trash, but objects reachable from + * finalizers can't safely be deleted. Python programmers should take + * care not to create such things. For Python, finalizers means + * instance objects with __del__ methods. Weakrefs with callbacks + * can call arbitrary Python code, so those are special-cased too. + * + * Move unreachable objects with finalizers, and weakrefs with + * callbacks, into different lists. + */ + gc_list_init(&finalizers); + gc_list_init(&wr_callbacks); + move_troublemakers(&unreachable, &finalizers, &wr_callbacks); + /* Clear the trash weakrefs with callbacks. This prevents their + * callbacks from getting invoked (when a weakref goes away, so does + * its callback). + * We do this even if the weakrefs are reachable from finalizers. + * If we didn't, breaking cycles in unreachable later could trigger + * deallocation of objects in finalizers, which could in turn + * cause callbacks to trigger. This may not be ideal behavior. + */ + clear_weakrefs(&wr_callbacks); + /* finalizers contains the unreachable objects with a finalizer; + * unreachable objects reachable *from* those are also uncollectable, + * and we move those into the finalizers list too. + */ + move_finalizer_reachable(&finalizers); + + /* Collect statistics on collectable objects found and print + * debugging information. + */ + for (gc = unreachable.gc.gc_next; gc != &unreachable; + gc = gc->gc.gc_next) { + m++; + if (debug & DEBUG_COLLECTABLE) { + debug_cycle("collectable", FROM_GC(gc)); + } + } + /* Call tp_clear on objects in the unreachable set. This will cause + * the reference cycles to be broken. It may also cause some objects + * in finalizers to be freed. + */ + delete_garbage(&unreachable, old); + + /* Now that we're done analyzing stuff and breaking cycles, let + * delayed weakref callbacks run. + */ + m += release_weakrefs(&wr_callbacks, old); + + /* Collect statistics on uncollectable objects found and print + * debugging information. */ + for (gc = finalizers.gc.gc_next; + gc != &finalizers; + gc = gc->gc.gc_next) { + n++; + if (debug & DEBUG_UNCOLLECTABLE) + debug_cycle("uncollectable", FROM_GC(gc)); + } + if (debug & DEBUG_STATS) { + if (m == 0 && n == 0) { + PySys_WriteStderr("gc: done.\n"); + } + else { + PySys_WriteStderr( + "gc: done, %ld unreachable, %ld uncollectable.\n", + n+m, n); + } + } + + /* Append instances in the uncollectable set to a Python + * reachable list of garbage. The programmer has to deal with + * this if they insist on creating this type of structure. + */ + (void)handle_finalizers(&finalizers, old); + + if (PyErr_Occurred()) { + if (gc_str == NULL) + gc_str = PyString_FromString("garbage collection"); + PyErr_WriteUnraisable(gc_str); + Py_FatalError("unexpected exception during garbage collection"); + } + return n+m; +} + +static long +collect_generations(void) +{ + int i; + long n = 0; + + /* Find the oldest generation (higest numbered) where the count + * exceeds the threshold. Objects in the that generation and + * generations younger than it will be collected. */ + for (i = NUM_GENERATIONS-1; i >= 0; i--) { + if (generations[i].count > generations[i].threshold) { + n = collect(i); + break; + } + } + return n; +} + +PyDoc_STRVAR(gc_enable__doc__, +"enable() -> None\n" +"\n" +"Enable automatic garbage collection.\n"); + +static PyObject * +gc_enable(PyObject *self, PyObject *noargs) +{ + enabled = 1; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(gc_disable__doc__, +"disable() -> None\n" +"\n" +"Disable automatic garbage collection.\n"); + +static PyObject * +gc_disable(PyObject *self, PyObject *noargs) +{ + enabled = 0; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(gc_isenabled__doc__, +"isenabled() -> status\n" +"\n" +"Returns true if automatic garbage collection is enabled.\n"); + +static PyObject * +gc_isenabled(PyObject *self, PyObject *noargs) +{ + return Py_BuildValue("i", enabled); +} + +PyDoc_STRVAR(gc_collect__doc__, +"collect() -> n\n" +"\n" +"Run a full collection. The number of unreachable objects is returned.\n"); + +static PyObject * +gc_collect(PyObject *self, PyObject *noargs) +{ + long n; + + if (collecting) + n = 0; /* already collecting, don't do anything */ + else { + collecting = 1; + n = collect(NUM_GENERATIONS - 1); + collecting = 0; + } + + return Py_BuildValue("l", n); +} + +PyDoc_STRVAR(gc_set_debug__doc__, +"set_debug(flags) -> None\n" +"\n" +"Set the garbage collection debugging flags. Debugging information is\n" +"written to sys.stderr.\n" +"\n" +"flags is an integer and can have the following bits turned on:\n" +"\n" +" DEBUG_STATS - Print statistics during collection.\n" +" DEBUG_COLLECTABLE - Print collectable objects found.\n" +" DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects found.\n" +" DEBUG_INSTANCES - Print instance objects.\n" +" DEBUG_OBJECTS - Print objects other than instances.\n" +" DEBUG_SAVEALL - Save objects to gc.garbage rather than freeing them.\n" +" DEBUG_LEAK - Debug leaking programs (everything but STATS).\n"); + +static PyObject * +gc_set_debug(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "i:set_debug", &debug)) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(gc_get_debug__doc__, +"get_debug() -> flags\n" +"\n" +"Get the garbage collection debugging flags.\n"); + +static PyObject * +gc_get_debug(PyObject *self, PyObject *noargs) +{ + return Py_BuildValue("i", debug); +} + +PyDoc_STRVAR(gc_set_thresh__doc__, +"set_threshold(threshold0, [threshold1, threshold2]) -> None\n" +"\n" +"Sets the collection thresholds. Setting threshold0 to zero disables\n" +"collection.\n"); + +static PyObject * +gc_set_thresh(PyObject *self, PyObject *args) +{ + int i; + if (!PyArg_ParseTuple(args, "i|ii:set_threshold", + &generations[0].threshold, + &generations[1].threshold, + &generations[2].threshold)) + return NULL; + for (i = 2; i < NUM_GENERATIONS; i++) { + /* generations higher than 2 get the same threshold */ + generations[i].threshold = generations[2].threshold; + } + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(gc_get_thresh__doc__, +"get_threshold() -> (threshold0, threshold1, threshold2)\n" +"\n" +"Return the current collection thresholds\n"); + +static PyObject * +gc_get_thresh(PyObject *self, PyObject *noargs) +{ + return Py_BuildValue("(iii)", + generations[0].threshold, + generations[1].threshold, + generations[2].threshold); +} + +static int +referrersvisit(PyObject* obj, PyObject *objs) +{ + int i; + for (i = 0; i < PyTuple_GET_SIZE(objs); i++) + if (PyTuple_GET_ITEM(objs, i) == obj) + return 1; + return 0; +} + +static int +gc_referrers_for(PyObject *objs, PyGC_Head *list, PyObject *resultlist) +{ + PyGC_Head *gc; + PyObject *obj; + traverseproc traverse; + for (gc = list->gc.gc_next; gc != list; gc = gc->gc.gc_next) { + obj = FROM_GC(gc); + traverse = obj->ob_type->tp_traverse; + if (obj == objs || obj == resultlist) + continue; + if (traverse(obj, (visitproc)referrersvisit, objs)) { + if (PyList_Append(resultlist, obj) < 0) + return 0; /* error */ + } + } + return 1; /* no error */ +} + +PyDoc_STRVAR(gc_get_referrers__doc__, +"get_referrers(*objs) -> list\n\ +Return the list of objects that directly refer to any of objs."); + +static PyObject * +gc_get_referrers(PyObject *self, PyObject *args) +{ + int i; + PyObject *result = PyList_New(0); + for (i = 0; i < NUM_GENERATIONS; i++) { + if (!(gc_referrers_for(args, GEN_HEAD(i), result))) { + Py_DECREF(result); + return NULL; + } + } + return result; +} + +/* Append obj to list; return true if error (out of memory), false if OK. */ +static int +referentsvisit(PyObject *obj, PyObject *list) +{ + return PyList_Append(list, obj) < 0; +} + +PyDoc_STRVAR(gc_get_referents__doc__, +"get_referents(*objs) -> list\n\ +Return the list of objects that are directly referred to by objs."); + +static PyObject * +gc_get_referents(PyObject *self, PyObject *args) +{ + int i; + PyObject *result = PyList_New(0); + + if (result == NULL) + return NULL; + + for (i = 0; i < PyTuple_GET_SIZE(args); i++) { + traverseproc traverse; + PyObject *obj = PyTuple_GET_ITEM(args, i); + + if (! PyObject_IS_GC(obj)) + continue; + traverse = obj->ob_type->tp_traverse; + if (! traverse) + continue; + if (traverse(obj, (visitproc)referentsvisit, result)) { + Py_DECREF(result); + return NULL; + } + } + return result; +} + +PyDoc_STRVAR(gc_get_objects__doc__, +"get_objects() -> [...]\n" +"\n" +"Return a list of objects tracked by the collector (excluding the list\n" +"returned).\n"); + +static PyObject * +gc_get_objects(PyObject *self, PyObject *noargs) +{ + int i; + PyObject* result; + + result = PyList_New(0); + if (result == NULL) + return NULL; + for (i = 0; i < NUM_GENERATIONS; i++) { + if (append_objects(result, GEN_HEAD(i))) { + Py_DECREF(result); + return NULL; + } + } + return result; +} + + +PyDoc_STRVAR(gc__doc__, +"This module provides access to the garbage collector for reference cycles.\n" +"\n" +"enable() -- Enable automatic garbage collection.\n" +"disable() -- Disable automatic garbage collection.\n" +"isenabled() -- Returns true if automatic collection is enabled.\n" +"collect() -- Do a full collection right now.\n" +"set_debug() -- Set debugging flags.\n" +"get_debug() -- Get debugging flags.\n" +"set_threshold() -- Set the collection thresholds.\n" +"get_threshold() -- Return the current the collection thresholds.\n" +"get_objects() -- Return a list of all objects tracked by the collector.\n" +"get_referrers() -- Return the list of objects that refer to an object.\n" +"get_referents() -- Return the list of objects that an object refers to.\n"); + +static PyMethodDef GcMethods[] = { + {"enable", gc_enable, METH_NOARGS, gc_enable__doc__}, + {"disable", gc_disable, METH_NOARGS, gc_disable__doc__}, + {"isenabled", gc_isenabled, METH_NOARGS, gc_isenabled__doc__}, + {"set_debug", gc_set_debug, METH_VARARGS, gc_set_debug__doc__}, + {"get_debug", gc_get_debug, METH_NOARGS, gc_get_debug__doc__}, + {"set_threshold", gc_set_thresh, METH_VARARGS, gc_set_thresh__doc__}, + {"get_threshold", gc_get_thresh, METH_NOARGS, gc_get_thresh__doc__}, + {"collect", gc_collect, METH_NOARGS, gc_collect__doc__}, + {"get_objects", gc_get_objects,METH_NOARGS, gc_get_objects__doc__}, + {"get_referrers", gc_get_referrers, METH_VARARGS, + gc_get_referrers__doc__}, + {"get_referents", gc_get_referents, METH_VARARGS, + gc_get_referents__doc__}, + {NULL, NULL} /* Sentinel */ +}; + +void +initgc(void) +{ + PyObject *m; + + m = Py_InitModule4("gc", + GcMethods, + gc__doc__, + NULL, + PYTHON_API_VERSION); + + if (garbage == NULL) { + garbage = PyList_New(0); + if (garbage == NULL) + return; + } + if (PyModule_AddObject(m, "garbage", garbage) < 0) + return; +#define ADD_INT(NAME) if (PyModule_AddIntConstant(m, #NAME, NAME) < 0) return + ADD_INT(DEBUG_STATS); + ADD_INT(DEBUG_COLLECTABLE); + ADD_INT(DEBUG_UNCOLLECTABLE); + ADD_INT(DEBUG_INSTANCES); + ADD_INT(DEBUG_OBJECTS); + ADD_INT(DEBUG_SAVEALL); + ADD_INT(DEBUG_LEAK); +#undef ADD_INT +} + +/* API to invoke gc.collect() from C */ +long +PyGC_Collect(void) +{ + long n; + + if (collecting) + n = 0; /* already collecting, don't do anything */ + else { + collecting = 1; + n = collect(NUM_GENERATIONS - 1); + collecting = 0; + } + + return n; +} + +/* for debugging */ +void +_PyGC_Dump(PyGC_Head *g) +{ + _PyObject_Dump(FROM_GC(g)); +} + +/* extension modules might be compiled with GC support so these + functions must always be available */ + +#undef PyObject_GC_Track +#undef PyObject_GC_UnTrack +#undef PyObject_GC_Del +#undef _PyObject_GC_Malloc + +void +PyObject_GC_Track(void *op) +{ + _PyObject_GC_TRACK(op); +} + +/* for binary compatibility with 2.2 */ +void +_PyObject_GC_Track(PyObject *op) +{ + PyObject_GC_Track(op); +} + +void +PyObject_GC_UnTrack(void *op) +{ + /* Obscure: the Py_TRASHCAN mechanism requires that we be able to + * call PyObject_GC_UnTrack twice on an object. + */ + if (IS_TRACKED(op)) + _PyObject_GC_UNTRACK(op); +} + +/* for binary compatibility with 2.2 */ +void +_PyObject_GC_UnTrack(PyObject *op) +{ + PyObject_GC_UnTrack(op); +} + +PyObject * +_PyObject_GC_Malloc(size_t basicsize) +{ + PyObject *op; + PyGC_Head *g = PyObject_MALLOC(sizeof(PyGC_Head) + basicsize); + if (g == NULL) + return PyErr_NoMemory(); + g->gc.gc_refs = GC_UNTRACKED; + generations[0].count++; /* number of allocated GC objects */ + if (generations[0].count > generations[0].threshold && + enabled && + generations[0].threshold && + !collecting && + !PyErr_Occurred()) { + collecting = 1; + collect_generations(); + collecting = 0; + } + op = FROM_GC(g); + return op; +} + +PyObject * +_PyObject_GC_New(PyTypeObject *tp) +{ + PyObject *op = _PyObject_GC_Malloc(_PyObject_SIZE(tp)); + if (op != NULL) + op = PyObject_INIT(op, tp); + return op; +} + +PyVarObject * +_PyObject_GC_NewVar(PyTypeObject *tp, int nitems) +{ + const size_t size = _PyObject_VAR_SIZE(tp, nitems); + PyVarObject *op = (PyVarObject *) _PyObject_GC_Malloc(size); + if (op != NULL) + op = PyObject_INIT_VAR(op, tp, nitems); + return op; +} + +PyVarObject * +_PyObject_GC_Resize(PyVarObject *op, int nitems) +{ + const size_t basicsize = _PyObject_VAR_SIZE(op->ob_type, nitems); + PyGC_Head *g = AS_GC(op); + g = PyObject_REALLOC(g, sizeof(PyGC_Head) + basicsize); + if (g == NULL) + return (PyVarObject *)PyErr_NoMemory(); + op = (PyVarObject *) FROM_GC(g); + op->ob_size = nitems; + return op; +} + +void +PyObject_GC_Del(void *op) +{ + PyGC_Head *g = AS_GC(op); + if (IS_TRACKED(op)) + gc_list_remove(g); + if (generations[0].count > 0) { + generations[0].count--; + } + PyObject_FREE(g); +} + +/* for binary compatibility with 2.2 */ +#undef _PyObject_GC_Del +void +_PyObject_GC_Del(PyObject *op) +{ + PyObject_GC_Del(op); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gdbmmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gdbmmodule.c new file mode 100644 index 00000000..e4bbd1f1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/gdbmmodule.c @@ -0,0 +1,523 @@ + +/* DBM module using dictionary interface */ +/* Author: Anthony Baxter, after dbmmodule.c */ +/* Doc strings: Mitch Chapman */ + + +#include "Python.h" + +#include +#include +#include +#include "gdbm.h" + +#if defined(WIN32) && !defined(__CYGWIN__) +#include "gdbmerrno.h" +extern const char * gdbm_strerror(gdbm_error); +#endif + +PyDoc_STRVAR(gdbmmodule__doc__, +"This module provides an interface to the GNU DBM (GDBM) library.\n\ +\n\ +This module is quite similar to the dbm module, but uses GDBM instead to\n\ +provide some additional functionality. Please note that the file formats\n\ +created by GDBM and dbm are incompatible. \n\ +\n\ +GDBM objects behave like mappings (dictionaries), except that keys and\n\ +values are always strings. Printing a GDBM object doesn't print the\n\ +keys and values, and the items() and values() methods are not\n\ +supported."); + +typedef struct { + PyObject_HEAD + int di_size; /* -1 means recompute */ + GDBM_FILE di_dbm; +} dbmobject; + +static PyTypeObject Dbmtype; + +#define is_dbmobject(v) ((v)->ob_type == &Dbmtype) +#define check_dbmobject_open(v) if ((v)->di_dbm == NULL) \ + { PyErr_SetString(DbmError, "GDBM object has already been closed"); \ + return NULL; } + + + +static PyObject *DbmError; + +PyDoc_STRVAR(gdbm_object__doc__, +"This object represents a GDBM database.\n\ +GDBM objects behave like mappings (dictionaries), except that keys and\n\ +values are always strings. Printing a GDBM object doesn't print the\n\ +keys and values, and the items() and values() methods are not\n\ +supported.\n\ +\n\ +GDBM objects also support additional operations such as firstkey,\n\ +nextkey, reorganize, and sync."); + +static PyObject * +newdbmobject(char *file, int flags, int mode) +{ + dbmobject *dp; + + dp = PyObject_New(dbmobject, &Dbmtype); + if (dp == NULL) + return NULL; + dp->di_size = -1; + errno = 0; + if ((dp->di_dbm = gdbm_open(file, 0, flags, mode, NULL)) == 0) { + if (errno != 0) + PyErr_SetFromErrno(DbmError); + else + PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno)); + Py_DECREF(dp); + return NULL; + } + return (PyObject *)dp; +} + +/* Methods */ + +static void +dbm_dealloc(register dbmobject *dp) +{ + if (dp->di_dbm) + gdbm_close(dp->di_dbm); + PyObject_Del(dp); +} + +static int +dbm_length(dbmobject *dp) +{ + if (dp->di_dbm == NULL) { + PyErr_SetString(DbmError, "GDBM object has already been closed"); + return -1; + } + if (dp->di_size < 0) { + datum key,okey; + int size; + okey.dsize=0; + + size = 0; + for (key=gdbm_firstkey(dp->di_dbm); key.dptr; + key = gdbm_nextkey(dp->di_dbm,okey)) { + size++; + if(okey.dsize) free(okey.dptr); + okey=key; + } + dp->di_size = size; + } + return dp->di_size; +} + +static PyObject * +dbm_subscript(dbmobject *dp, register PyObject *key) +{ + PyObject *v; + datum drec, krec; + + if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize) ) + return NULL; + + if (dp->di_dbm == NULL) { + PyErr_SetString(DbmError, + "GDBM object has already been closed"); + return NULL; + } + drec = gdbm_fetch(dp->di_dbm, krec); + if (drec.dptr == 0) { + PyErr_SetString(PyExc_KeyError, + PyString_AS_STRING((PyStringObject *)key)); + return NULL; + } + v = PyString_FromStringAndSize(drec.dptr, drec.dsize); + free(drec.dptr); + return v; +} + +static int +dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) +{ + datum krec, drec; + + if (!PyArg_Parse(v, "s#", &krec.dptr, &krec.dsize) ) { + PyErr_SetString(PyExc_TypeError, + "gdbm mappings have string indices only"); + return -1; + } + if (dp->di_dbm == NULL) { + PyErr_SetString(DbmError, + "GDBM object has already been closed"); + return -1; + } + dp->di_size = -1; + if (w == NULL) { + if (gdbm_delete(dp->di_dbm, krec) < 0) { + PyErr_SetString(PyExc_KeyError, + PyString_AS_STRING((PyStringObject *)v)); + return -1; + } + } + else { + if (!PyArg_Parse(w, "s#", &drec.dptr, &drec.dsize)) { + PyErr_SetString(PyExc_TypeError, + "gdbm mappings have string elements only"); + return -1; + } + errno = 0; + if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) { + if (errno != 0) + PyErr_SetFromErrno(DbmError); + else + PyErr_SetString(DbmError, + gdbm_strerror(gdbm_errno)); + return -1; + } + } + return 0; +} + +static PyMappingMethods dbm_as_mapping = { + (inquiry)dbm_length, /*mp_length*/ + (binaryfunc)dbm_subscript, /*mp_subscript*/ + (objobjargproc)dbm_ass_sub, /*mp_ass_subscript*/ +}; + +PyDoc_STRVAR(dbm_close__doc__, +"close() -> None\n\ +Closes the database."); + +static PyObject * +dbm_close(register dbmobject *dp, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":close")) + return NULL; + if (dp->di_dbm) + gdbm_close(dp->di_dbm); + dp->di_dbm = NULL; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(dbm_keys__doc__, +"keys() -> list_of_keys\n\ +Get a list of all keys in the database."); + +static PyObject * +dbm_keys(register dbmobject *dp, PyObject *args) +{ + register PyObject *v, *item; + datum key, nextkey; + int err; + + if (dp == NULL || !is_dbmobject(dp)) { + PyErr_BadInternalCall(); + return NULL; + } + if (!PyArg_ParseTuple(args, ":keys")) + return NULL; + + check_dbmobject_open(dp); + + v = PyList_New(0); + if (v == NULL) + return NULL; + + key = gdbm_firstkey(dp->di_dbm); + while (key.dptr) { + item = PyString_FromStringAndSize(key.dptr, key.dsize); + if (item == NULL) { + free(key.dptr); + Py_DECREF(v); + return NULL; + } + err = PyList_Append(v, item); + Py_DECREF(item); + if (err != 0) { + free(key.dptr); + Py_DECREF(v); + return NULL; + } + nextkey = gdbm_nextkey(dp->di_dbm, key); + free(key.dptr); + key = nextkey; + } + return v; +} + +PyDoc_STRVAR(dbm_has_key__doc__, +"has_key(key) -> boolean\n\ +Find out whether or not the database contains a given key."); + +static PyObject * +dbm_has_key(register dbmobject *dp, PyObject *args) +{ + datum key; + + if (!PyArg_ParseTuple(args, "s#:has_key", &key.dptr, &key.dsize)) + return NULL; + check_dbmobject_open(dp); + return PyInt_FromLong((long) gdbm_exists(dp->di_dbm, key)); +} + +PyDoc_STRVAR(dbm_firstkey__doc__, +"firstkey() -> key\n\ +It's possible to loop over every key in the database using this method\n\ +and the nextkey() method. The traversal is ordered by GDBM's internal\n\ +hash values, and won't be sorted by the key values. This method\n\ +returns the starting key."); + +static PyObject * +dbm_firstkey(register dbmobject *dp, PyObject *args) +{ + register PyObject *v; + datum key; + + if (!PyArg_ParseTuple(args, ":firstkey")) + return NULL; + check_dbmobject_open(dp); + key = gdbm_firstkey(dp->di_dbm); + if (key.dptr) { + v = PyString_FromStringAndSize(key.dptr, key.dsize); + free(key.dptr); + return v; + } + else { + Py_INCREF(Py_None); + return Py_None; + } +} + +PyDoc_STRVAR(dbm_nextkey__doc__, +"nextkey(key) -> next_key\n\ +Returns the key that follows key in the traversal.\n\ +The following code prints every key in the database db, without having\n\ +to create a list in memory that contains them all:\n\ +\n\ + k = db.firstkey()\n\ + while k != None:\n\ + print k\n\ + k = db.nextkey(k)"); + +static PyObject * +dbm_nextkey(register dbmobject *dp, PyObject *args) +{ + register PyObject *v; + datum key, nextkey; + + if (!PyArg_ParseTuple(args, "s#:nextkey", &key.dptr, &key.dsize)) + return NULL; + check_dbmobject_open(dp); + nextkey = gdbm_nextkey(dp->di_dbm, key); + if (nextkey.dptr) { + v = PyString_FromStringAndSize(nextkey.dptr, nextkey.dsize); + free(nextkey.dptr); + return v; + } + else { + Py_INCREF(Py_None); + return Py_None; + } +} + +PyDoc_STRVAR(dbm_reorganize__doc__, +"reorganize() -> None\n\ +If you have carried out a lot of deletions and would like to shrink\n\ +the space used by the GDBM file, this routine will reorganize the\n\ +database. GDBM will not shorten the length of a database file except\n\ +by using this reorganization; otherwise, deleted file space will be\n\ +kept and reused as new (key,value) pairs are added."); + +static PyObject * +dbm_reorganize(register dbmobject *dp, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":reorganize")) + return NULL; + check_dbmobject_open(dp); + errno = 0; + if (gdbm_reorganize(dp->di_dbm) < 0) { + if (errno != 0) + PyErr_SetFromErrno(DbmError); + else + PyErr_SetString(DbmError, gdbm_strerror(gdbm_errno)); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(dbm_sync__doc__, +"sync() -> None\n\ +When the database has been opened in fast mode, this method forces\n\ +any unwritten data to be written to the disk."); + +static PyObject * +dbm_sync(register dbmobject *dp, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":sync")) + return NULL; + check_dbmobject_open(dp); + gdbm_sync(dp->di_dbm); + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef dbm_methods[] = { + {"close", (PyCFunction)dbm_close, METH_VARARGS, dbm_close__doc__}, + {"keys", (PyCFunction)dbm_keys, METH_VARARGS, dbm_keys__doc__}, + {"has_key", (PyCFunction)dbm_has_key, METH_VARARGS, dbm_has_key__doc__}, + {"firstkey", (PyCFunction)dbm_firstkey,METH_VARARGS, dbm_firstkey__doc__}, + {"nextkey", (PyCFunction)dbm_nextkey, METH_VARARGS, dbm_nextkey__doc__}, + {"reorganize",(PyCFunction)dbm_reorganize,METH_VARARGS, dbm_reorganize__doc__}, + {"sync", (PyCFunction)dbm_sync, METH_VARARGS, dbm_sync__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +dbm_getattr(dbmobject *dp, char *name) +{ + return Py_FindMethod(dbm_methods, (PyObject *)dp, name); +} + +static PyTypeObject Dbmtype = { + PyObject_HEAD_INIT(0) + 0, + "gdbm.gdbm", + sizeof(dbmobject), + 0, + (destructor)dbm_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)dbm_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &dbm_as_mapping, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + 0, /*tp_xxx4*/ + gdbm_object__doc__, /*tp_doc*/ +}; + +/* ----------------------------------------------------------------- */ + +PyDoc_STRVAR(dbmopen__doc__, +"open(filename, [flags, [mode]]) -> dbm_object\n\ +Open a dbm database and return a dbm object. The filename argument is\n\ +the name of the database file.\n\ +\n\ +The optional flags argument can be 'r' (to open an existing database\n\ +for reading only -- default), 'w' (to open an existing database for\n\ +reading and writing), 'c' (which creates the database if it doesn't\n\ +exist), or 'n' (which always creates a new empty database).\n\ +\n\ +Some versions of gdbm support additional flags which must be\n\ +appended to one of the flags described above. The module constant\n\ +'open_flags' is a string of valid additional flags. The 'f' flag\n\ +opens the database in fast mode; altered data will not automatically\n\ +be written to the disk after every change. This results in faster\n\ +writes to the database, but may result in an inconsistent database\n\ +if the program crashes while the database is still open. Use the\n\ +sync() method to force any unwritten data to be written to the disk.\n\ +The 's' flag causes all database operations to be synchronized to\n\ +disk. The 'u' flag disables locking of the database file.\n\ +\n\ +The optional mode argument is the Unix mode of the file, used only\n\ +when the database has to be created. It defaults to octal 0666. "); + +static PyObject * +dbmopen(PyObject *self, PyObject *args) +{ + char *name; + char *flags = "r "; + int iflags; + int mode = 0666; + + if (!PyArg_ParseTuple(args, "s|si:open", &name, &flags, &mode)) + return NULL; + switch (flags[0]) { + case 'r': + iflags = GDBM_READER; + break; + case 'w': + iflags = GDBM_WRITER; + break; + case 'c': + iflags = GDBM_WRCREAT; + break; + case 'n': + iflags = GDBM_NEWDB; + break; + default: + PyErr_SetString(DbmError, + "First flag must be one of 'r', 'w', 'c' or 'n'"); + return NULL; + } + for (flags++; *flags != '\0'; flags++) { + char buf[40]; + switch (*flags) { +#ifdef GDBM_FAST + case 'f': + iflags |= GDBM_FAST; + break; +#endif +#ifdef GDBM_SYNC + case 's': + iflags |= GDBM_SYNC; + break; +#endif +#ifdef GDBM_NOLOCK + case 'u': + iflags |= GDBM_NOLOCK; + break; +#endif + default: + PyOS_snprintf(buf, sizeof(buf), "Flag '%c' is not supported.", + *flags); + PyErr_SetString(DbmError, buf); + return NULL; + } + } + + return newdbmobject(name, iflags, mode); +} + +static char dbmmodule_open_flags[] = "rwcn" +#ifdef GDBM_FAST + "f" +#endif +#ifdef GDBM_SYNC + "s" +#endif +#ifdef GDBM_NOLOCK + "u" +#endif + ; + +static PyMethodDef dbmmodule_methods[] = { + { "open", (PyCFunction)dbmopen, METH_VARARGS, dbmopen__doc__}, + { 0, 0 }, +}; + +PyMODINIT_FUNC +initgdbm(void) { + PyObject *m, *d, *s; + + Dbmtype.ob_type = &PyType_Type; + m = Py_InitModule4("gdbm", dbmmodule_methods, + gdbmmodule__doc__, (PyObject *)NULL, + PYTHON_API_VERSION); + d = PyModule_GetDict(m); + DbmError = PyErr_NewException("gdbm.error", NULL, NULL); + if (DbmError != NULL) { + PyDict_SetItemString(d, "error", DbmError); + s = PyString_FromString(dbmmodule_open_flags); + PyDict_SetItemString(d, "open_flags", s); + Py_DECREF(s); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getaddrinfo.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getaddrinfo.c new file mode 100644 index 00000000..438dd6b5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getaddrinfo.c @@ -0,0 +1,638 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * GAI_ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR GAI_ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON GAI_ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN GAI_ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * "#ifdef FAITH" part is local hack for supporting IPv4-v6 translator. + * + * Issues to be discussed: + * - Thread safe-ness must be checked. + * - Return values. There are nonstandard return values defined and used + * in the source code. This is because RFC2133 is silent about which error + * code must be returned for which situation. + * - PF_UNSPEC case would be handled in getipnodebyname() with the AI_ALL flag. + */ + +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "addrinfo.h" +#endif + +#if defined(__KAME__) && defined(ENABLE_IPV6) +# define FAITH +#endif + +#define SUCCESS 0 +#define GAI_ANY 0 +#define YES 1 +#define NO 0 + +#ifdef FAITH +static int translate = NO; +static struct in6_addr faith_prefix = IN6ADDR_GAI_ANY_INIT; +#endif + +static const char in_addrany[] = { 0, 0, 0, 0 }; +static const char in6_addrany[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const char in_loopback[] = { 127, 0, 0, 1 }; +static const char in6_loopback[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +}; + +struct sockinet { + u_char si_len; + u_char si_family; + u_short si_port; +}; + +static struct gai_afd { + int a_af; + int a_addrlen; + int a_socklen; + int a_off; + const char *a_addrany; + const char *a_loopback; +} gai_afdl [] = { +#ifdef ENABLE_IPV6 +#define N_INET6 0 + {PF_INET6, sizeof(struct in6_addr), + sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr), + in6_addrany, in6_loopback}, +#define N_INET 1 +#else +#define N_INET 0 +#endif + {PF_INET, sizeof(struct in_addr), + sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr), + in_addrany, in_loopback}, + {0, 0, 0, 0, NULL, NULL}, +}; + +#ifdef ENABLE_IPV6 +#define PTON_MAX 16 +#else +#define PTON_MAX 4 +#endif + +#ifndef IN_MULTICAST +#define IN_MULTICAST(i) (((i) & 0xf0000000U) == 0xe0000000U) +#endif + +#ifndef IN_EXPERIMENTAL +#define IN_EXPERIMENTAL(i) (((i) & 0xe0000000U) == 0xe0000000U) +#endif + +#ifndef IN_LOOPBACKNET +#define IN_LOOPBACKNET 127 +#endif + +static int get_name Py_PROTO((const char *, struct gai_afd *, + struct addrinfo **, char *, struct addrinfo *, + int)); +static int get_addr Py_PROTO((const char *, int, struct addrinfo **, + struct addrinfo *, int)); +static int str_isnumber Py_PROTO((const char *)); + +static char *ai_errlist[] = { + "success.", + "address family for hostname not supported.", /* EAI_ADDRFAMILY */ + "temporary failure in name resolution.", /* EAI_AGAIN */ + "invalid value for ai_flags.", /* EAI_BADFLAGS */ + "non-recoverable failure in name resolution.", /* EAI_FAIL */ + "ai_family not supported.", /* EAI_FAMILY */ + "memory allocation failure.", /* EAI_MEMORY */ + "no address associated with hostname.", /* EAI_NODATA */ + "hostname nor servname provided, or not known.",/* EAI_NONAME */ + "servname not supported for ai_socktype.", /* EAI_SERVICE */ + "ai_socktype not supported.", /* EAI_SOCKTYPE */ + "system error returned in errno.", /* EAI_SYSTEM */ + "invalid value for hints.", /* EAI_BADHINTS */ + "resolved protocol is unknown.", /* EAI_PROTOCOL */ + "unknown error.", /* EAI_MAX */ +}; + +#define GET_CANONNAME(ai, str) \ +if (pai->ai_flags & AI_CANONNAME) {\ + if (((ai)->ai_canonname = (char *)malloc(strlen(str) + 1)) != NULL) {\ + strcpy((ai)->ai_canonname, (str));\ + } else {\ + error = EAI_MEMORY;\ + goto free;\ + }\ +} + +#ifdef HAVE_SOCKADDR_SA_LEN +#define GET_AI(ai, gai_afd, addr, port) {\ + char *p;\ + if (((ai) = (struct addrinfo *)malloc(sizeof(struct addrinfo) +\ + ((gai_afd)->a_socklen)))\ + == NULL) goto free;\ + memcpy(ai, pai, sizeof(struct addrinfo));\ + (ai)->ai_addr = (struct sockaddr *)((ai) + 1);\ + memset((ai)->ai_addr, 0, (gai_afd)->a_socklen);\ + (ai)->ai_addr->sa_len = (ai)->ai_addrlen = (gai_afd)->a_socklen;\ + (ai)->ai_addr->sa_family = (ai)->ai_family = (gai_afd)->a_af;\ + ((struct sockinet *)(ai)->ai_addr)->si_port = port;\ + p = (char *)((ai)->ai_addr);\ + memcpy(p + (gai_afd)->a_off, (addr), (gai_afd)->a_addrlen);\ +} +#else +#define GET_AI(ai, gai_afd, addr, port) {\ + char *p;\ + if (((ai) = (struct addrinfo *)malloc(sizeof(struct addrinfo) +\ + ((gai_afd)->a_socklen)))\ + == NULL) goto free;\ + memcpy(ai, pai, sizeof(struct addrinfo));\ + (ai)->ai_addr = (struct sockaddr *)((ai) + 1);\ + memset((ai)->ai_addr, 0, (gai_afd)->a_socklen);\ + (ai)->ai_addrlen = (gai_afd)->a_socklen;\ + (ai)->ai_addr->sa_family = (ai)->ai_family = (gai_afd)->a_af;\ + ((struct sockinet *)(ai)->ai_addr)->si_port = port;\ + p = (char *)((ai)->ai_addr);\ + memcpy(p + (gai_afd)->a_off, (addr), (gai_afd)->a_addrlen);\ +} +#endif + +#define ERR(err) { error = (err); goto bad; } + +char * +gai_strerror(int ecode) +{ + if (ecode < 0 || ecode > EAI_MAX) + ecode = EAI_MAX; + return ai_errlist[ecode]; +} + +void +freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + do { + next = ai->ai_next; + if (ai->ai_canonname) + free(ai->ai_canonname); + /* no need to free(ai->ai_addr) */ + free(ai); + } while ((ai = next) != NULL); +} + +static int +str_isnumber(const char *p) +{ + unsigned char *q = (unsigned char *)p; + while (*q) { + if (! isdigit(*q)) + return NO; + q++; + } + return YES; +} + +int +getaddrinfo(const char*hostname, const char*servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct addrinfo sentinel; + struct addrinfo *top = NULL; + struct addrinfo *cur; + int i, error = 0; + char pton[PTON_MAX]; + struct addrinfo ai; + struct addrinfo *pai; + u_short port; + +#ifdef FAITH + static int firsttime = 1; + + if (firsttime) { + /* translator hack */ + { + char *q = getenv("GAI"); + if (q && inet_pton(AF_INET6, q, &faith_prefix) == 1) + translate = YES; + } + firsttime = 0; + } +#endif + + /* initialize file static vars */ + sentinel.ai_next = NULL; + cur = &sentinel; + pai = &ai; + pai->ai_flags = 0; + pai->ai_family = PF_UNSPEC; + pai->ai_socktype = GAI_ANY; + pai->ai_protocol = GAI_ANY; + pai->ai_addrlen = 0; + pai->ai_canonname = NULL; + pai->ai_addr = NULL; + pai->ai_next = NULL; + port = GAI_ANY; + + if (hostname == NULL && servname == NULL) + return EAI_NONAME; + if (hints) { + /* error check for hints */ + if (hints->ai_addrlen || hints->ai_canonname || + hints->ai_addr || hints->ai_next) + ERR(EAI_BADHINTS); /* xxx */ + if (hints->ai_flags & ~AI_MASK) + ERR(EAI_BADFLAGS); + switch (hints->ai_family) { + case PF_UNSPEC: + case PF_INET: +#ifdef ENABLE_IPV6 + case PF_INET6: +#endif + break; + default: + ERR(EAI_FAMILY); + } + memcpy(pai, hints, sizeof(*pai)); + switch (pai->ai_socktype) { + case GAI_ANY: + switch (pai->ai_protocol) { + case GAI_ANY: + break; + case IPPROTO_UDP: + pai->ai_socktype = SOCK_DGRAM; + break; + case IPPROTO_TCP: + pai->ai_socktype = SOCK_STREAM; + break; + default: + pai->ai_socktype = SOCK_RAW; + break; + } + break; + case SOCK_RAW: + break; + case SOCK_DGRAM: + if (pai->ai_protocol != IPPROTO_UDP && + pai->ai_protocol != GAI_ANY) + ERR(EAI_BADHINTS); /*xxx*/ + pai->ai_protocol = IPPROTO_UDP; + break; + case SOCK_STREAM: + if (pai->ai_protocol != IPPROTO_TCP && + pai->ai_protocol != GAI_ANY) + ERR(EAI_BADHINTS); /*xxx*/ + pai->ai_protocol = IPPROTO_TCP; + break; + default: + ERR(EAI_SOCKTYPE); + /* unreachable */ + } + } + + /* + * service port + */ + if (servname) { + if (str_isnumber(servname)) { + if (pai->ai_socktype == GAI_ANY) { + /* caller accept *GAI_ANY* socktype */ + pai->ai_socktype = SOCK_DGRAM; + pai->ai_protocol = IPPROTO_UDP; + } + port = htons((u_short)atoi(servname)); + } else { + struct servent *sp; + char *proto; + + proto = NULL; + switch (pai->ai_socktype) { + case GAI_ANY: + proto = NULL; + break; + case SOCK_DGRAM: + proto = "udp"; + break; + case SOCK_STREAM: + proto = "tcp"; + break; + default: + fprintf(stderr, "panic!\n"); + break; + } + if ((sp = getservbyname(servname, proto)) == NULL) + ERR(EAI_SERVICE); + port = sp->s_port; + if (pai->ai_socktype == GAI_ANY) { + if (strcmp(sp->s_proto, "udp") == 0) { + pai->ai_socktype = SOCK_DGRAM; + pai->ai_protocol = IPPROTO_UDP; + } else if (strcmp(sp->s_proto, "tcp") == 0) { + pai->ai_socktype = SOCK_STREAM; + pai->ai_protocol = IPPROTO_TCP; + } else + ERR(EAI_PROTOCOL); /*xxx*/ + } + } + } + + /* + * hostname == NULL. + * passive socket -> anyaddr (0.0.0.0 or ::) + * non-passive socket -> localhost (127.0.0.1 or ::1) + */ + if (hostname == NULL) { + struct gai_afd *gai_afd; + + for (gai_afd = &gai_afdl[0]; gai_afd->a_af; gai_afd++) { + if (!(pai->ai_family == PF_UNSPEC + || pai->ai_family == gai_afd->a_af)) { + continue; + } + + if (pai->ai_flags & AI_PASSIVE) { + GET_AI(cur->ai_next, gai_afd, gai_afd->a_addrany, port); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "anyaddr"); + */ + } else { + GET_AI(cur->ai_next, gai_afd, gai_afd->a_loopback, + port); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "localhost"); + */ + } + cur = cur->ai_next; + } + top = sentinel.ai_next; + if (top) + goto good; + else + ERR(EAI_FAMILY); + } + + /* hostname as numeric name */ + for (i = 0; gai_afdl[i].a_af; i++) { + if (inet_pton(gai_afdl[i].a_af, hostname, pton)) { + u_long v4a; +#ifdef ENABLE_IPV6 + u_char pfx; +#endif + + switch (gai_afdl[i].a_af) { + case AF_INET: + v4a = ((struct in_addr *)pton)->s_addr; + v4a = ntohl(v4a); + if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) + pai->ai_flags &= ~AI_CANONNAME; + v4a >>= IN_CLASSA_NSHIFT; + if (v4a == 0 || v4a == IN_LOOPBACKNET) + pai->ai_flags &= ~AI_CANONNAME; + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + pfx = ((struct in6_addr *)pton)->s6_addr8[0]; + if (pfx == 0 || pfx == 0xfe || pfx == 0xff) + pai->ai_flags &= ~AI_CANONNAME; + break; +#endif + } + + if (pai->ai_family == gai_afdl[i].a_af || + pai->ai_family == PF_UNSPEC) { + if (! (pai->ai_flags & AI_CANONNAME)) { + GET_AI(top, &gai_afdl[i], pton, port); + goto good; + } + /* + * if AI_CANONNAME and if reverse lookup + * fail, return ai anyway to pacify + * calling application. + * + * XXX getaddrinfo() is a name->address + * translation function, and it looks strange + * that we do addr->name translation here. + */ + get_name(pton, &gai_afdl[i], &top, pton, pai, port); + goto good; + } else + ERR(EAI_FAMILY); /*xxx*/ + } + } + + if (pai->ai_flags & AI_NUMERICHOST) + ERR(EAI_NONAME); + + /* hostname as alphabetical name */ + error = get_addr(hostname, pai->ai_family, &top, pai, port); + if (error == 0) { + if (top) { + good: + *res = top; + return SUCCESS; + } else + error = EAI_FAIL; + } + free: + if (top) + freeaddrinfo(top); + bad: + *res = NULL; + return error; +} + +static int +get_name(addr, gai_afd, res, numaddr, pai, port0) + const char *addr; + struct gai_afd *gai_afd; + struct addrinfo **res; + char *numaddr; + struct addrinfo *pai; + int port0; +{ + u_short port = port0 & 0xffff; + struct hostent *hp; + struct addrinfo *cur; + int error = 0; +#ifdef ENABLE_IPV6 + int h_error; +#endif + +#ifdef ENABLE_IPV6 + hp = getipnodebyaddr(addr, gai_afd->a_addrlen, gai_afd->a_af, &h_error); +#else + hp = gethostbyaddr(addr, gai_afd->a_addrlen, AF_INET); +#endif + if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) { + GET_AI(cur, gai_afd, hp->h_addr_list[0], port); + GET_CANONNAME(cur, hp->h_name); + } else + GET_AI(cur, gai_afd, numaddr, port); + +#ifdef ENABLE_IPV6 + if (hp) + freehostent(hp); +#endif + *res = cur; + return SUCCESS; + free: + if (cur) + freeaddrinfo(cur); +#ifdef ENABLE_IPV6 + if (hp) + freehostent(hp); +#endif + /* bad: */ + *res = NULL; + return error; +} + +static int +get_addr(hostname, af, res, pai, port0) + const char *hostname; + int af; + struct addrinfo **res; + struct addrinfo *pai; + int port0; +{ + u_short port = port0 & 0xffff; + struct addrinfo sentinel; + struct hostent *hp; + struct addrinfo *top, *cur; + struct gai_afd *gai_afd; + int i, error = 0, h_error; + char *ap; + + top = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; +#ifdef ENABLE_IPV6 + if (af == AF_UNSPEC) { + hp = getipnodebyname(hostname, AF_INET6, + AI_ADDRCONFIG|AI_ALL|AI_V4MAPPED, &h_error); + } else + hp = getipnodebyname(hostname, af, AI_ADDRCONFIG, &h_error); +#else + hp = gethostbyname(hostname); + h_error = h_errno; +#endif + if (hp == NULL) { + switch (h_error) { + case HOST_NOT_FOUND: + case NO_DATA: + error = EAI_NODATA; + break; + case TRY_AGAIN: + error = EAI_AGAIN; + break; + case NO_RECOVERY: + default: + error = EAI_FAIL; + break; + } + goto free; + } + + if ((hp->h_name == NULL) || (hp->h_name[0] == 0) || + (hp->h_addr_list[0] == NULL)) { + error = EAI_FAIL; + goto free; + } + + for (i = 0; (ap = hp->h_addr_list[i]) != NULL; i++) { + switch (af) { +#ifdef ENABLE_IPV6 + case AF_INET6: + gai_afd = &gai_afdl[N_INET6]; + break; +#endif +#ifndef ENABLE_IPV6 + default: /* AF_UNSPEC */ +#endif + case AF_INET: + gai_afd = &gai_afdl[N_INET]; + break; +#ifdef ENABLE_IPV6 + default: /* AF_UNSPEC */ + if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) { + ap += sizeof(struct in6_addr) - + sizeof(struct in_addr); + gai_afd = &gai_afdl[N_INET]; + } else + gai_afd = &gai_afdl[N_INET6]; + break; +#endif + } +#ifdef FAITH + if (translate && gai_afd->a_af == AF_INET) { + struct in6_addr *in6; + + GET_AI(cur->ai_next, &gai_afdl[N_INET6], ap, port); + in6 = &((struct sockaddr_in6 *)cur->ai_next->ai_addr)->sin6_addr; + memcpy(&in6->s6_addr32[0], &faith_prefix, + sizeof(struct in6_addr) - sizeof(struct in_addr)); + memcpy(&in6->s6_addr32[3], ap, sizeof(struct in_addr)); + } else +#endif /* FAITH */ + GET_AI(cur->ai_next, gai_afd, ap, port); + if (cur == &sentinel) { + top = cur->ai_next; + GET_CANONNAME(top, hp->h_name); + } + cur = cur->ai_next; + } +#ifdef ENABLE_IPV6 + freehostent(hp); +#endif + *res = top; + return SUCCESS; + free: + if (top) + freeaddrinfo(top); +#ifdef ENABLE_IPV6 + if (hp) + freehostent(hp); +#endif +/* bad: */ + *res = NULL; + return error; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getbuildinfo.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getbuildinfo.c new file mode 100644 index 00000000..0c61ab2f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getbuildinfo.c @@ -0,0 +1,38 @@ +#include "Python.h" + +#ifdef macintosh +#include "macbuildno.h" +#endif + +#ifndef DONT_HAVE_STDIO_H +#include +#endif + +#ifndef DATE +#ifdef __DATE__ +#define DATE __DATE__ +#else +#define DATE "xx/xx/xx" +#endif +#endif + +#ifndef TIME +#ifdef __TIME__ +#define TIME __TIME__ +#else +#define TIME "xx:xx:xx" +#endif +#endif + +#ifndef BUILD +#define BUILD 0 +#endif + +const char * +Py_GetBuildInfo(void) +{ + static char buildinfo[50]; + PyOS_snprintf(buildinfo, sizeof(buildinfo), + "#%d, %.20s, %.9s", BUILD, DATE, TIME); + return buildinfo; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getnameinfo.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getnameinfo.c new file mode 100644 index 00000000..af947508 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getnameinfo.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Issues to be discussed: + * - Thread safe-ness must be checked + * - Return values. There seems to be no standard for return value (RFC2133) + * but INRIA implementation returns EAI_xxx defined for getaddrinfo(). + */ + +#if 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "addrinfo.h" +#endif + +#define SUCCESS 0 +#define YES 1 +#define NO 0 + +static struct gni_afd { + int a_af; + int a_addrlen; + int a_socklen; + int a_off; +} gni_afdl [] = { +#ifdef ENABLE_IPV6 + {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr)}, +#endif + {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr)}, + {0, 0, 0}, +}; + +struct gni_sockinet { + u_char si_len; + u_char si_family; + u_short si_port; +}; + +#define ENI_NOSOCKET 0 +#define ENI_NOSERVNAME 1 +#define ENI_NOHOSTNAME 2 +#define ENI_MEMORY 3 +#define ENI_SYSTEM 4 +#define ENI_FAMILY 5 +#define ENI_SALEN 6 + +/* forward declaration to make gcc happy */ +int getnameinfo Py_PROTO((const struct sockaddr *, size_t, char *, size_t, + char *, size_t, int)); + +int +getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) + const struct sockaddr *sa; + size_t salen; + char *host; + size_t hostlen; + char *serv; + size_t servlen; + int flags; +{ + struct gni_afd *gni_afd; + struct servent *sp; + struct hostent *hp; + u_short port; + int family, len, i; + char *addr, *p; + u_long v4a; +#ifdef ENABLE_IPV6 + u_char pfx; +#endif + int h_error; + char numserv[512]; + char numaddr[512]; + + if (sa == NULL) + return ENI_NOSOCKET; + +#ifdef HAVE_SOCKADDR_SA_LEN + len = sa->sa_len; + if (len != salen) return ENI_SALEN; +#else + len = salen; +#endif + + family = sa->sa_family; + for (i = 0; gni_afdl[i].a_af; i++) + if (gni_afdl[i].a_af == family) { + gni_afd = &gni_afdl[i]; + goto found; + } + return ENI_FAMILY; + + found: + if (len != gni_afd->a_socklen) return ENI_SALEN; + + port = ((struct gni_sockinet *)sa)->si_port; /* network byte order */ + addr = (char *)sa + gni_afd->a_off; + + if (serv == NULL || servlen == 0) { + /* what we should do? */ + } else if (flags & NI_NUMERICSERV) { + sprintf(numserv, "%d", ntohs(port)); + if (strlen(numserv) > servlen) + return ENI_MEMORY; + strcpy(serv, numserv); + } else { + sp = getservbyport(port, (flags & NI_DGRAM) ? "udp" : "tcp"); + if (sp) { + if (strlen(sp->s_name) > servlen) + return ENI_MEMORY; + strcpy(serv, sp->s_name); + } else + return ENI_NOSERVNAME; + } + + switch (sa->sa_family) { + case AF_INET: + v4a = ((struct sockaddr_in *)sa)->sin_addr.s_addr; + if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) + flags |= NI_NUMERICHOST; + v4a >>= IN_CLASSA_NSHIFT; + if (v4a == 0 || v4a == IN_LOOPBACKNET) + flags |= NI_NUMERICHOST; + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr8[0]; + if (pfx == 0 || pfx == 0xfe || pfx == 0xff) + flags |= NI_NUMERICHOST; + break; +#endif + } + if (host == NULL || hostlen == 0) { + /* what should we do? */ + } else if (flags & NI_NUMERICHOST) { + if (inet_ntop(gni_afd->a_af, addr, numaddr, sizeof(numaddr)) + == NULL) + return ENI_SYSTEM; + if (strlen(numaddr) > hostlen) + return ENI_MEMORY; + strcpy(host, numaddr); + } else { +#ifdef ENABLE_IPV6 + hp = getipnodebyaddr(addr, gni_afd->a_addrlen, gni_afd->a_af, &h_error); +#else + hp = gethostbyaddr(addr, gni_afd->a_addrlen, gni_afd->a_af); + h_error = h_errno; +#endif + + if (hp) { + if (flags & NI_NOFQDN) { + p = strchr(hp->h_name, '.'); + if (p) *p = '\0'; + } + if (strlen(hp->h_name) > hostlen) { +#ifdef ENABLE_IPV6 + freehostent(hp); +#endif + return ENI_MEMORY; + } + strcpy(host, hp->h_name); +#ifdef ENABLE_IPV6 + freehostent(hp); +#endif + } else { + if (flags & NI_NAMEREQD) + return ENI_NOHOSTNAME; + if (inet_ntop(gni_afd->a_af, addr, numaddr, sizeof(numaddr)) + == NULL) + return ENI_NOHOSTNAME; + if (strlen(numaddr) > hostlen) + return ENI_MEMORY; + strcpy(host, numaddr); + } + } + return SUCCESS; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getpath.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getpath.c new file mode 100644 index 00000000..c1c3e630 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/getpath.c @@ -0,0 +1,650 @@ +/* Return the initial module search path. */ + +#include "Python.h" +#include "osdefs.h" + +#include +#include + +#ifdef WITH_NEXT_FRAMEWORK +#include +#endif + +/* Search in some common locations for the associated Python libraries. + * + * Two directories must be found, the platform independent directory + * (prefix), containing the common .py and .pyc files, and the platform + * dependent directory (exec_prefix), containing the shared library + * modules. Note that prefix and exec_prefix can be the same directory, + * but for some installations, they are different. + * + * Py_GetPath() carries out separate searches for prefix and exec_prefix. + * Each search tries a number of different locations until a ``landmark'' + * file or directory is found. If no prefix or exec_prefix is found, a + * warning message is issued and the preprocessor defined PREFIX and + * EXEC_PREFIX are used (even though they will not work); python carries on + * as best as is possible, but most imports will fail. + * + * Before any searches are done, the location of the executable is + * determined. If argv[0] has one or more slashs in it, it is used + * unchanged. Otherwise, it must have been invoked from the shell's path, + * so we search $PATH for the named executable and use that. If the + * executable was not found on $PATH (or there was no $PATH environment + * variable), the original argv[0] string is used. + * + * Next, the executable location is examined to see if it is a symbolic + * link. If so, the link is chased (correctly interpreting a relative + * pathname if one is found) and the directory of the link target is used. + * + * Finally, argv0_path is set to the directory containing the executable + * (i.e. the last component is stripped). + * + * With argv0_path in hand, we perform a number of steps. The same steps + * are performed for prefix and for exec_prefix, but with a different + * landmark. + * + * Step 1. Are we running python out of the build directory? This is + * checked by looking for a different kind of landmark relative to + * argv0_path. For prefix, the landmark's path is derived from the VPATH + * preprocessor variable (taking into account that its value is almost, but + * not quite, what we need). For exec_prefix, the landmark is + * Modules/Setup. If the landmark is found, we're done. + * + * For the remaining steps, the prefix landmark will always be + * lib/python$VERSION/os.py and the exec_prefix will always be + * lib/python$VERSION/lib-dynload, where $VERSION is Python's version + * number as supplied by the Makefile. Note that this means that no more + * build directory checking is performed; if the first step did not find + * the landmarks, the assumption is that python is running from an + * installed setup. + * + * Step 2. See if the $PYTHONHOME environment variable points to the + * installed location of the Python libraries. If $PYTHONHOME is set, then + * it points to prefix and exec_prefix. $PYTHONHOME can be a single + * directory, which is used for both, or the prefix and exec_prefix + * directories separated by a colon. + * + * Step 3. Try to find prefix and exec_prefix relative to argv0_path, + * backtracking up the path until it is exhausted. This is the most common + * step to succeed. Note that if prefix and exec_prefix are different, + * exec_prefix is more likely to be found; however if exec_prefix is a + * subdirectory of prefix, both will be found. + * + * Step 4. Search the directories pointed to by the preprocessor variables + * PREFIX and EXEC_PREFIX. These are supplied by the Makefile but can be + * passed in as options to the configure script. + * + * That's it! + * + * Well, almost. Once we have determined prefix and exec_prefix, the + * preprocessor variable PYTHONPATH is used to construct a path. Each + * relative path on PYTHONPATH is prefixed with prefix. Then the directory + * containing the shared library modules is appended. The environment + * variable $PYTHONPATH is inserted in front of it all. Finally, the + * prefix and exec_prefix globals are tweaked so they reflect the values + * expected by other code, by stripping the "lib/python$VERSION/..." stuff + * off. If either points to the build directory, the globals are reset to + * the corresponding preprocessor variables (so sys.prefix will reflect the + * installation location, even though sys.path points into the build + * directory). This seems to make more sense given that currently the only + * known use of sys.prefix and sys.exec_prefix is for the ILU installation + * process to find the installed Python tree. + */ + +#ifndef VERSION +#if defined(__VMS) +#define VERSION "2_1" +#else +#define VERSION "2.1" +#endif +#endif + +#ifndef VPATH +#define VPATH "." +#endif + +#ifndef PREFIX +#define PREFIX "/usr/local" +#endif + +#ifndef EXEC_PREFIX +#define EXEC_PREFIX PREFIX +#endif + +#ifndef PYTHONPATH +#define PYTHONPATH PREFIX "/lib/python" VERSION ":" \ + EXEC_PREFIX "/lib/python" VERSION "/lib-dynload" +#endif + +#ifndef LANDMARK +#define LANDMARK "os.py" +#endif + +static char prefix[MAXPATHLEN+1]; +static char exec_prefix[MAXPATHLEN+1]; +static char progpath[MAXPATHLEN+1]; +static char *module_search_path = NULL; +static char lib_python[] = "lib/python" VERSION; + +static void +reduce(char *dir) +{ + size_t i = strlen(dir); + while (i > 0 && dir[i] != SEP) + --i; + dir[i] = '\0'; +} + + +static int +isfile(char *filename) /* Is file, not directory */ +{ + struct stat buf; + if (stat(filename, &buf) != 0) + return 0; + if (!S_ISREG(buf.st_mode)) + return 0; + return 1; +} + + +static int +ismodule(char *filename) /* Is module -- check for .pyc/.pyo too */ +{ + if (isfile(filename)) + return 1; + + /* Check for the compiled version of prefix. */ + if (strlen(filename) < MAXPATHLEN) { + strcat(filename, Py_OptimizeFlag ? "o" : "c"); + if (isfile(filename)) + return 1; + } + return 0; +} + + +static int +isxfile(char *filename) /* Is executable file */ +{ + struct stat buf; + if (stat(filename, &buf) != 0) + return 0; + if (!S_ISREG(buf.st_mode)) + return 0; + if ((buf.st_mode & 0111) == 0) + return 0; + return 1; +} + + +static int +isdir(char *filename) /* Is directory */ +{ + struct stat buf; + if (stat(filename, &buf) != 0) + return 0; + if (!S_ISDIR(buf.st_mode)) + return 0; + return 1; +} + + +/* joinpath requires that any buffer argument passed to it has at + least MAXPATHLEN + 1 bytes allocated. If this requirement is met, + it guarantees that it will never overflow the buffer. If stuff + is too long, buffer will contain a truncated copy of stuff. +*/ +static void +joinpath(char *buffer, char *stuff) +{ + size_t n, k; + if (stuff[0] == SEP) + n = 0; + else { + n = strlen(buffer); + if (n > 0 && buffer[n-1] != SEP && n < MAXPATHLEN) + buffer[n++] = SEP; + } + k = strlen(stuff); + if (n + k > MAXPATHLEN) + k = MAXPATHLEN - n; + strncpy(buffer+n, stuff, k); + buffer[n+k] = '\0'; +} + +/* copy_absolute requires that path be allocated at least + MAXPATHLEN + 1 bytes and that p be no more than MAXPATHLEN bytes. */ +static void +copy_absolute(char *path, char *p) +{ + if (p[0] == SEP) + strcpy(path, p); + else { + getcwd(path, MAXPATHLEN); + if (p[0] == '.' && p[1] == SEP) + p += 2; + joinpath(path, p); + } +} + +/* absolutize() requires that path be allocated at least MAXPATHLEN+1 bytes. */ +static void +absolutize(char *path) +{ + char buffer[MAXPATHLEN + 1]; + + if (path[0] == SEP) + return; + copy_absolute(buffer, path); + strcpy(path, buffer); +} + +/* search_for_prefix requires that argv0_path be no more than MAXPATHLEN + bytes long. +*/ +static int +search_for_prefix(char *argv0_path, char *home) +{ + size_t n; + char *vpath; + + /* If PYTHONHOME is set, we believe it unconditionally */ + if (home) { + char *delim; + strncpy(prefix, home, MAXPATHLEN); + delim = strchr(prefix, DELIM); + if (delim) + *delim = '\0'; + joinpath(prefix, lib_python); + joinpath(prefix, LANDMARK); + return 1; + } + + /* Check to see if argv[0] is in the build directory */ + strcpy(prefix, argv0_path); + joinpath(prefix, "Modules/Setup"); + if (isfile(prefix)) { + /* Check VPATH to see if argv0_path is in the build directory. */ + vpath = VPATH; + strcpy(prefix, argv0_path); + joinpath(prefix, vpath); + joinpath(prefix, "Lib"); + joinpath(prefix, LANDMARK); + if (ismodule(prefix)) + return -1; + } + + /* Search from argv0_path, until root is found */ + copy_absolute(prefix, argv0_path); + do { + n = strlen(prefix); + joinpath(prefix, lib_python); + joinpath(prefix, LANDMARK); + if (ismodule(prefix)) + return 1; + prefix[n] = '\0'; + reduce(prefix); + } while (prefix[0]); + + /* Look at configure's PREFIX */ + strncpy(prefix, PREFIX, MAXPATHLEN); + joinpath(prefix, lib_python); + joinpath(prefix, LANDMARK); + if (ismodule(prefix)) + return 1; + + /* Fail */ + return 0; +} + + +/* search_for_exec_prefix requires that argv0_path be no more than + MAXPATHLEN bytes long. +*/ +static int +search_for_exec_prefix(char *argv0_path, char *home) +{ + size_t n; + + /* If PYTHONHOME is set, we believe it unconditionally */ + if (home) { + char *delim; + delim = strchr(home, DELIM); + if (delim) + strncpy(exec_prefix, delim+1, MAXPATHLEN); + else + strncpy(exec_prefix, home, MAXPATHLEN); + joinpath(exec_prefix, lib_python); + joinpath(exec_prefix, "lib-dynload"); + return 1; + } + + /* Check to see if argv[0] is in the build directory */ + strcpy(exec_prefix, argv0_path); + joinpath(exec_prefix, "Modules/Setup"); + if (isfile(exec_prefix)) { + reduce(exec_prefix); + return -1; + } + + /* Search from argv0_path, until root is found */ + copy_absolute(exec_prefix, argv0_path); + do { + n = strlen(exec_prefix); + joinpath(exec_prefix, lib_python); + joinpath(exec_prefix, "lib-dynload"); + if (isdir(exec_prefix)) + return 1; + exec_prefix[n] = '\0'; + reduce(exec_prefix); + } while (exec_prefix[0]); + + /* Look at configure's EXEC_PREFIX */ + strncpy(exec_prefix, EXEC_PREFIX, MAXPATHLEN); + joinpath(exec_prefix, lib_python); + joinpath(exec_prefix, "lib-dynload"); + if (isdir(exec_prefix)) + return 1; + + /* Fail */ + return 0; +} + + +static void +calculate_path(void) +{ + extern char *Py_GetProgramName(void); + + static char delimiter[2] = {DELIM, '\0'}; + static char separator[2] = {SEP, '\0'}; + char *pythonpath = PYTHONPATH; + char *rtpypath = Py_GETENV("PYTHONPATH"); + char *home = Py_GetPythonHome(); + char *path = getenv("PATH"); + char *prog = Py_GetProgramName(); + char argv0_path[MAXPATHLEN+1]; + char zip_path[MAXPATHLEN+1]; + int pfound, efound; /* 1 if found; -1 if found build directory */ + char *buf; + size_t bufsz; + size_t prefixsz; + char *defpath = pythonpath; +#ifdef WITH_NEXT_FRAMEWORK + NSModule pythonModule; +#endif + + /* If there is no slash in the argv0 path, then we have to + * assume python is on the user's $PATH, since there's no + * other way to find a directory to start the search from. If + * $PATH isn't exported, you lose. + */ + if (strchr(prog, SEP)) + strncpy(progpath, prog, MAXPATHLEN); + else if (path) { + while (1) { + char *delim = strchr(path, DELIM); + + if (delim) { + size_t len = delim - path; + if (len > MAXPATHLEN) + len = MAXPATHLEN; + strncpy(progpath, path, len); + *(progpath + len) = '\0'; + } + else + strncpy(progpath, path, MAXPATHLEN); + + joinpath(progpath, prog); + if (isxfile(progpath)) + break; + + if (!delim) { + progpath[0] = '\0'; + break; + } + path = delim + 1; + } + } + else + progpath[0] = '\0'; + if (progpath[0] != SEP) + absolutize(progpath); + strncpy(argv0_path, progpath, MAXPATHLEN); + argv0_path[MAXPATHLEN] = '\0'; + +#ifdef WITH_NEXT_FRAMEWORK + /* On Mac OS X we have a special case if we're running from a framework. + ** This is because the python home should be set relative to the library, + ** which is in the framework, not relative to the executable, which may + ** be outside of the framework. Except when we're in the build directory... + */ + pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize")); + /* Use dylib functions to find out where the framework was loaded from */ + buf = (char *)NSLibraryNameForModule(pythonModule); + if (buf != NULL) { + /* We're in a framework. */ + /* See if we might be in the build directory. The framework in the + ** build directory is incomplete, it only has the .dylib and a few + ** needed symlinks, it doesn't have the Lib directories and such. + ** If we're running with the framework from the build directory we must + ** be running the interpreter in the build directory, so we use the + ** build-directory-specific logic to find Lib and such. + */ + strncpy(argv0_path, buf, MAXPATHLEN); + reduce(argv0_path); + joinpath(argv0_path, lib_python); + joinpath(argv0_path, LANDMARK); + if (!ismodule(argv0_path)) { + /* We are in the build directory so use the name of the + executable - we know that the absolute path is passed */ + strncpy(argv0_path, prog, MAXPATHLEN); + } + else { + /* Use the location of the library as the progpath */ + strncpy(argv0_path, buf, MAXPATHLEN); + } + } +#endif + +#if HAVE_READLINK + { + char tmpbuffer[MAXPATHLEN+1]; + int linklen = readlink(progpath, tmpbuffer, MAXPATHLEN); + while (linklen != -1) { + /* It's not null terminated! */ + tmpbuffer[linklen] = '\0'; + if (tmpbuffer[0] == SEP) + /* tmpbuffer should never be longer than MAXPATHLEN, + but extra check does not hurt */ + strncpy(argv0_path, tmpbuffer, MAXPATHLEN); + else { + /* Interpret relative to progpath */ + reduce(argv0_path); + joinpath(argv0_path, tmpbuffer); + } + linklen = readlink(argv0_path, tmpbuffer, MAXPATHLEN); + } + } +#endif /* HAVE_READLINK */ + + reduce(argv0_path); + /* At this point, argv0_path is guaranteed to be less than + MAXPATHLEN bytes long. + */ + + if (!(pfound = search_for_prefix(argv0_path, home))) { + if (!Py_FrozenFlag) + fprintf(stderr, + "Could not find platform independent libraries \n"); + strncpy(prefix, PREFIX, MAXPATHLEN); + joinpath(prefix, lib_python); + } + else + reduce(prefix); + + strncpy(zip_path, prefix, MAXPATHLEN); + zip_path[MAXPATHLEN] = '\0'; + if (pfound > 0) { /* Use the reduced prefix returned by Py_GetPrefix() */ + reduce(zip_path); + reduce(zip_path); + } + else + strncpy(zip_path, PREFIX, MAXPATHLEN); + joinpath(zip_path, "lib/python00.zip"); + bufsz = strlen(zip_path); /* Replace "00" with version */ + zip_path[bufsz - 6] = VERSION[0]; + zip_path[bufsz - 5] = VERSION[2]; + + if (!(efound = search_for_exec_prefix(argv0_path, home))) { + if (!Py_FrozenFlag) + fprintf(stderr, + "Could not find platform dependent libraries \n"); + strncpy(exec_prefix, EXEC_PREFIX, MAXPATHLEN); + joinpath(exec_prefix, "lib/lib-dynload"); + } + /* If we found EXEC_PREFIX do *not* reduce it! (Yet.) */ + + if ((!pfound || !efound) && !Py_FrozenFlag) + fprintf(stderr, + "Consider setting $PYTHONHOME to [:]\n"); + + /* Calculate size of return buffer. + */ + bufsz = 0; + + if (rtpypath) + bufsz += strlen(rtpypath) + 1; + + prefixsz = strlen(prefix) + 1; + + while (1) { + char *delim = strchr(defpath, DELIM); + + if (defpath[0] != SEP) + /* Paths are relative to prefix */ + bufsz += prefixsz; + + if (delim) + bufsz += delim - defpath + 1; + else { + bufsz += strlen(defpath) + 1; + break; + } + defpath = delim + 1; + } + + bufsz += strlen(zip_path) + 1; + bufsz += strlen(exec_prefix) + 1; + + /* This is the only malloc call in this file */ + buf = PyMem_Malloc(bufsz); + + if (buf == NULL) { + /* We can't exit, so print a warning and limp along */ + fprintf(stderr, "Not enough memory for dynamic PYTHONPATH.\n"); + fprintf(stderr, "Using default static PYTHONPATH.\n"); + module_search_path = PYTHONPATH; + } + else { + /* Run-time value of $PYTHONPATH goes first */ + if (rtpypath) { + strcpy(buf, rtpypath); + strcat(buf, delimiter); + } + else + buf[0] = '\0'; + + /* Next is the default zip path */ + strcat(buf, zip_path); + strcat(buf, delimiter); + + /* Next goes merge of compile-time $PYTHONPATH with + * dynamically located prefix. + */ + defpath = pythonpath; + while (1) { + char *delim = strchr(defpath, DELIM); + + if (defpath[0] != SEP) { + strcat(buf, prefix); + strcat(buf, separator); + } + + if (delim) { + size_t len = delim - defpath + 1; + size_t end = strlen(buf) + len; + strncat(buf, defpath, len); + *(buf + end) = '\0'; + } + else { + strcat(buf, defpath); + break; + } + defpath = delim + 1; + } + strcat(buf, delimiter); + + /* Finally, on goes the directory for dynamic-load modules */ + strcat(buf, exec_prefix); + + /* And publish the results */ + module_search_path = buf; + } + + /* Reduce prefix and exec_prefix to their essence, + * e.g. /usr/local/lib/python1.5 is reduced to /usr/local. + * If we're loading relative to the build directory, + * return the compiled-in defaults instead. + */ + if (pfound > 0) { + reduce(prefix); + reduce(prefix); + } + else + strncpy(prefix, PREFIX, MAXPATHLEN); + + if (efound > 0) { + reduce(exec_prefix); + reduce(exec_prefix); + reduce(exec_prefix); + } + else + strncpy(exec_prefix, EXEC_PREFIX, MAXPATHLEN); +} + + +/* External interface */ + +char * +Py_GetPath(void) +{ + if (!module_search_path) + calculate_path(); + return module_search_path; +} + +char * +Py_GetPrefix(void) +{ + if (!module_search_path) + calculate_path(); + return prefix; +} + +char * +Py_GetExecPrefix(void) +{ + if (!module_search_path) + calculate_path(); + return exec_prefix; +} + +char * +Py_GetProgramFullPath(void) +{ + if (!module_search_path) + calculate_path(); + return progpath; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/glmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/glmodule.c new file mode 100644 index 00000000..6bee1156 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/glmodule.c @@ -0,0 +1,7628 @@ + +/* +Input used to generate the Python module "glmodule.c". +The stub generator is a Python script called "cgen.py". + +Each definition must be contained on one line: + + + + can be: void, short, long (XXX maybe others?) + + can be: char, string, short, float, long, or double + string indicates a null terminated string; + if is char and begins with a *, the * is stripped + and is changed into string + + has the form or [] + where can be + s: arg is sent + r: arg is received (arg is a pointer) + and can be (N and I are numbers): + N + argI + retval + N*argI + N*I + N*retval + In the case where the subscript consists of two parts + separated by *, the first part is the width of the matrix, and + the second part is the length of the matrix. This order is + opposite from the order used in C to declare a two-dimensional + matrix. +*/ + +/* + * An attempt has been made to make this module switch threads on qread + * calls. It is far from safe, though. + */ + +#include +#include + +#ifdef __sgi +extern int devport(); +extern int textwritemask(); +extern int pagewritemask(); +extern int gewrite(); +extern int gettp(); +#endif + +#include "Python.h" +#include "cgensupport.h" + +/* +Some stubs are too complicated for the stub generator. +We can include manually written versions of them here. +A line starting with '%' gives the name of the function so the stub +generator can include it in the table of functions. +*/ + + +static PyObject * +gl_qread(PyObject *self, PyObject *args) +{ + long retval; + short arg1 ; + Py_BEGIN_ALLOW_THREADS + retval = qread( & arg1 ); + Py_END_ALLOW_THREADS + { PyObject *v = PyTuple_New( 2 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewlongobject(retval)); + PyTuple_SetItem(v, 1, mknewshortobject(arg1)); + return v; + } +} + + +/* +varray -- an array of v.. calls. +The argument is an array (maybe list or tuple) of points. +Each point must be a tuple or list of coordinates (x, y, z). +The points may be 2- or 3-dimensional but must all have the +same dimension. Float and int values may be mixed however. +The points are always converted to 3D double precision points +by assuming z=0.0 if necessary (as indicated in the man page), +and for each point v3d() is called. +*/ + + +static PyObject * +gl_varray(PyObject *self, PyObject *args) +{ + PyObject *v, *w=NULL; + int i, n, width; + double vec[3]; + PyObject * (*getitem)(PyObject *, int); + + if (!PyArg_GetObject(args, 1, 0, &v)) + return NULL; + + if (PyList_Check(v)) { + n = PyList_Size(v); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(v)) { + n = PyTuple_Size(v); + getitem = PyTuple_GetItem; + } + else { + PyErr_BadArgument(); + return NULL; + } + + if (n == 0) { + Py_INCREF(Py_None); + return Py_None; + } + if (n > 0) + w = (*getitem)(v, 0); + + width = 0; + if (w == NULL) { + } + else if (PyList_Check(w)) { + width = PyList_Size(w); + } + else if (PyTuple_Check(w)) { + width = PyTuple_Size(w); + } + + switch (width) { + case 2: + vec[2] = 0.0; + /* Fall through */ + case 3: + break; + default: + PyErr_BadArgument(); + return NULL; + } + + for (i = 0; i < n; i++) { + w = (*getitem)(v, i); + if (!PyArg_GetDoubleArray(w, 1, 0, width, vec)) + return NULL; + v3d(vec); + } + + Py_INCREF(Py_None); + return Py_None; +} + +/* +vnarray, nvarray -- an array of n3f and v3f calls. +The argument is an array (list or tuple) of pairs of points and normals. +Each pair is a tuple (NOT a list) of a point and a normal for that point. +Each point or normal must be a tuple (NOT a list) of coordinates (x, y, z). +Three coordinates must be given. Float and int values may be mixed. +For each pair, n3f() is called for the normal, and then v3f() is called +for the vector. + +vnarray and nvarray differ only in the order of the vector and normal in +the pair: vnarray expects (v, n) while nvarray expects (n, v). +*/ + +static PyObject *gen_nvarray(); /* Forward */ + + +static PyObject * +gl_nvarray(PyObject *self, PyObject *args) +{ + return gen_nvarray(args, 0); +} + + +static PyObject * +gl_vnarray(PyObject *self, PyObject *args) +{ + return gen_nvarray(args, 1); +} + +/* Generic, internal version of {nv,nv}array: inorm indicates the + argument order, 0: normal first, 1: vector first. */ + +static PyObject * +gen_nvarray(PyObject *args, int inorm) +{ + PyObject *v, *w, *wnorm, *wvec; + int i, n; + float norm[3], vec[3]; + PyObject * (*getitem)(PyObject *, int); + + if (!PyArg_GetObject(args, 1, 0, &v)) + return NULL; + + if (PyList_Check(v)) { + n = PyList_Size(v); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(v)) { + n = PyTuple_Size(v); + getitem = PyTuple_GetItem; + } + else { + PyErr_BadArgument(); + return NULL; + } + + for (i = 0; i < n; i++) { + w = (*getitem)(v, i); + if (!PyTuple_Check(w) || PyTuple_Size(w) != 2) { + PyErr_BadArgument(); + return NULL; + } + wnorm = PyTuple_GetItem(w, inorm); + wvec = PyTuple_GetItem(w, 1 - inorm); + if (!PyArg_GetFloatArray(wnorm, 1, 0, 3, norm) || + !PyArg_GetFloatArray(wvec, 1, 0, 3, vec)) + return NULL; + n3f(norm); + v3f(vec); + } + + Py_INCREF(Py_None); + return Py_None; +} + +/* nurbssurface(s_knots[], t_knots[], ctl[][], s_order, t_order, type). + The dimensions of ctl[] are computed as follows: + [len(s_knots) - s_order], [len(t_knots) - t_order] +*/ + + +static PyObject * +gl_nurbssurface(PyObject *self, PyObject *args) +{ + long arg1 ; + double * arg2 ; + long arg3 ; + double * arg4 ; + double *arg5 ; + long arg6 ; + long arg7 ; + long arg8 ; + long ncoords; + long s_byte_stride, t_byte_stride; + long s_nctl, t_nctl; + long s, t; + PyObject *v, *w, *pt; + double *pnext; + if (!PyArg_GetLongArraySize(args, 6, 0, &arg1)) + return NULL; + if ((arg2 = PyMem_NEW(double, arg1 )) == NULL) { + return PyErr_NoMemory(); + } + if (!PyArg_GetDoubleArray(args, 6, 0, arg1 , arg2)) + return NULL; + if (!PyArg_GetLongArraySize(args, 6, 1, &arg3)) + return NULL; + if ((arg4 = PyMem_NEW(double, arg3 )) == NULL) { + return PyErr_NoMemory(); + } + if (!PyArg_GetDoubleArray(args, 6, 1, arg3 , arg4)) + return NULL; + if (!PyArg_GetLong(args, 6, 3, &arg6)) + return NULL; + if (!PyArg_GetLong(args, 6, 4, &arg7)) + return NULL; + if (!PyArg_GetLong(args, 6, 5, &arg8)) + return NULL; + if (arg8 == N_XYZ) + ncoords = 3; + else if (arg8 == N_XYZW) + ncoords = 4; + else { + PyErr_BadArgument(); + return NULL; + } + s_nctl = arg1 - arg6; + t_nctl = arg3 - arg7; + if (!PyArg_GetObject(args, 6, 2, &v)) + return NULL; + if (!PyList_Check(v) || PyList_Size(v) != s_nctl) { + PyErr_BadArgument(); + return NULL; + } + if ((arg5 = PyMem_NEW(double, s_nctl*t_nctl*ncoords )) == NULL) { + return PyErr_NoMemory(); + } + pnext = arg5; + for (s = 0; s < s_nctl; s++) { + w = PyList_GetItem(v, s); + if (w == NULL || !PyList_Check(w) || + PyList_Size(w) != t_nctl) { + PyErr_BadArgument(); + return NULL; + } + for (t = 0; t < t_nctl; t++) { + pt = PyList_GetItem(w, t); + if (!PyArg_GetDoubleArray(pt, 1, 0, ncoords, pnext)) + return NULL; + pnext += ncoords; + } + } + s_byte_stride = sizeof(double) * ncoords; + t_byte_stride = s_byte_stride * s_nctl; + nurbssurface( arg1 , arg2 , arg3 , arg4 , + s_byte_stride , t_byte_stride , arg5 , arg6 , arg7 , arg8 ); + PyMem_DEL(arg2); + PyMem_DEL(arg4); + PyMem_DEL(arg5); + Py_INCREF(Py_None); + return Py_None; +} + +/* nurbscurve(knots, ctlpoints, order, type). + The length of ctlpoints is len(knots)-order. */ + + +static PyObject * +gl_nurbscurve(PyObject *self, PyObject *args) +{ + long arg1 ; + double * arg2 ; + long arg3 ; + double * arg4 ; + long arg5 ; + long arg6 ; + int ncoords, npoints; + int i; + PyObject *v; + double *pnext; + if (!PyArg_GetLongArraySize(args, 4, 0, &arg1)) + return NULL; + if ((arg2 = PyMem_NEW(double, arg1 )) == NULL) { + return PyErr_NoMemory(); + } + if (!PyArg_GetDoubleArray(args, 4, 0, arg1 , arg2)) + return NULL; + if (!PyArg_GetLong(args, 4, 2, &arg5)) + return NULL; + if (!PyArg_GetLong(args, 4, 3, &arg6)) + return NULL; + if (arg6 == N_ST) + ncoords = 2; + else if (arg6 == N_STW) + ncoords = 3; + else { + PyErr_BadArgument(); + return NULL; + } + npoints = arg1 - arg5; + if (!PyArg_GetObject(args, 4, 1, &v)) + return NULL; + if (!PyList_Check(v) || PyList_Size(v) != npoints) { + PyErr_BadArgument(); + return NULL; + } + if ((arg4 = PyMem_NEW(double, npoints*ncoords )) == NULL) { + return PyErr_NoMemory(); + } + pnext = arg4; + for (i = 0; i < npoints; i++) { + if (!PyArg_GetDoubleArray(PyList_GetItem(v, i), 1, 0, ncoords, pnext)) + return NULL; + pnext += ncoords; + } + arg3 = (sizeof(double)) * ncoords; + nurbscurve( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 ); + PyMem_DEL(arg2); + PyMem_DEL(arg4); + Py_INCREF(Py_None); + return Py_None; +} + +/* pwlcurve(points, type). + Points is a list of points. Type must be N_ST. */ + + +static PyObject * +gl_pwlcurve(PyObject *self, PyObject *args) +{ + PyObject *v; + long type; + double *data, *pnext; + long npoints, ncoords; + int i; + if (!PyArg_GetObject(args, 2, 0, &v)) + return NULL; + if (!PyArg_GetLong(args, 2, 1, &type)) + return NULL; + if (!PyList_Check(v)) { + PyErr_BadArgument(); + return NULL; + } + npoints = PyList_Size(v); + if (type == N_ST) + ncoords = 2; + else { + PyErr_BadArgument(); + return NULL; + } + if ((data = PyMem_NEW(double, npoints*ncoords)) == NULL) { + return PyErr_NoMemory(); + } + pnext = data; + for (i = 0; i < npoints; i++) { + if (!PyArg_GetDoubleArray(PyList_GetItem(v, i), 1, 0, ncoords, pnext)) + return NULL; + pnext += ncoords; + } + pwlcurve(npoints, data, sizeof(double)*ncoords, type); + PyMem_DEL(data); + Py_INCREF(Py_None); + return Py_None; +} + + +/* Picking and Selecting */ + +static short *pickbuffer = NULL; +static long pickbuffersize; + +static PyObject * +pick_select(PyObject *args, void (*func)()) +{ + if (!PyArg_GetLong(args, 1, 0, &pickbuffersize)) + return NULL; + if (pickbuffer != NULL) { + PyErr_SetString(PyExc_RuntimeError, + "pick/gselect: already picking/selecting"); + return NULL; + } + if ((pickbuffer = PyMem_NEW(short, pickbuffersize)) == NULL) { + return PyErr_NoMemory(); + } + (*func)(pickbuffer, pickbuffersize); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +endpick_select(long (*func)()) +{ + PyObject *v, *w; + int i, nhits, n; + if (pickbuffer == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "endpick/endselect: not in pick/select mode"); + return NULL; + } + nhits = (*func)(pickbuffer); + if (nhits < 0) { + nhits = -nhits; /* How to report buffer overflow otherwise? */ + } + /* Scan the buffer to see how many integers */ + n = 0; + for (; nhits > 0; nhits--) { + n += 1 + pickbuffer[n]; + } + v = PyList_New(n); + if (v == NULL) + return NULL; + /* XXX Could do it nicer and interpret the data structure here, + returning a list of lists. But this can be done in Python... */ + for (i = 0; i < n; i++) { + w = PyInt_FromLong((long)pickbuffer[i]); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(v, i, w); + } + PyMem_DEL(pickbuffer); + pickbuffer = NULL; + return v; +} + +extern void pick(), gselect(); +extern long endpick(), endselect(); + +static PyObject *gl_pick(PyObject *self, PyObject *args) +{ + return pick_select(args, pick); +} + +static PyObject *gl_endpick(PyObject *self) +{ + return endpick_select(endpick); +} + +static PyObject *gl_gselect(PyObject *self, PyObject *args) +{ + return pick_select(args, gselect); +} + +static PyObject *gl_endselect(PyObject *self) +{ + return endpick_select(endselect); +} + + +/* XXX The generator botches this one. Here's a quick hack to fix it. */ + +/* XXX The generator botches this one. Here's a quick hack to fix it. */ + + +static PyObject * +gl_getmatrix(PyObject *self, PyObject *args) +{ + Matrix arg1; + PyObject *v, *w; + int i, j; + getmatrix( arg1 ); + v = PyList_New(16); + if (v == NULL) { + return PyErr_NoMemory(); + } + for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) { + w = mknewfloatobject(arg1[i][j]); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(v, i*4+j, w); + } + return v; +} + +/* Here's an alternate version that returns a 4x4 matrix instead of + a vector. Unfortunately it is incompatible with loadmatrix and + multmatrix... */ + + +static PyObject * +gl_altgetmatrix(PyObject *self, PyObject *args) +{ + Matrix arg1; + PyObject *v, *w; + int i, j; + getmatrix( arg1 ); + v = PyList_New(4); + if (v == NULL) { + return NULL; + } + for (i = 0; i < 4; i++) { + w = PyList_New(4); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(v, i, w); + } + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + w = mknewfloatobject(arg1[i][j]); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(PyList_GetItem(v, i), j, w); + } + } + return v; +} + + +static PyObject * +gl_lrectwrite(PyObject *self, PyObject *args) +{ + short x1 ; + short y1 ; + short x2 ; + short y2 ; + string parray ; + PyObject *s; +#if 0 + int pixcount; +#endif + if (!PyArg_GetShort(args, 5, 0, &x1)) + return NULL; + if (!PyArg_GetShort(args, 5, 1, &y1)) + return NULL; + if (!PyArg_GetShort(args, 5, 2, &x2)) + return NULL; + if (!PyArg_GetShort(args, 5, 3, &y2)) + return NULL; + if (!PyArg_GetString(args, 5, 4, &parray)) + return NULL; + if (!PyArg_GetObject(args, 5, 4, &s)) + return NULL; +#if 0 +/* Don't check this, it breaks experiments with pixmode(PM_SIZE, ...) */ + pixcount = (long)(x2+1-x1) * (long)(y2+1-y1); + if (!PyString_Check(s) || PyString_Size(s) != pixcount*sizeof(long)) { + PyErr_SetString(PyExc_RuntimeError, + "string arg to lrectwrite has wrong size"); + return NULL; + } +#endif + lrectwrite( x1 , y1 , x2 , y2 , (unsigned long *) parray ); + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +gl_lrectread(PyObject *self, PyObject *args) +{ + short x1 ; + short y1 ; + short x2 ; + short y2 ; + PyObject *parray; + int pixcount; + if (!PyArg_GetShort(args, 4, 0, &x1)) + return NULL; + if (!PyArg_GetShort(args, 4, 1, &y1)) + return NULL; + if (!PyArg_GetShort(args, 4, 2, &x2)) + return NULL; + if (!PyArg_GetShort(args, 4, 3, &y2)) + return NULL; + pixcount = (long)(x2+1-x1) * (long)(y2+1-y1); + parray = PyString_FromStringAndSize((char *)NULL, pixcount*sizeof(long)); + if (parray == NULL) + return NULL; /* No memory */ + lrectread(x1, y1, x2, y2, (unsigned long *) PyString_AsString(parray)); + return parray; +} + + +static PyObject * +gl_readdisplay(PyObject *self, PyObject *args) +{ + short x1, y1, x2, y2; + unsigned long *parray, hints; + long size, size_ret; + PyObject *rv; + + if ( !PyArg_Parse(args, "hhhhl", &x1, &y1, &x2, &y2, &hints) ) + return 0; + size = (long)(x2+1-x1) * (long)(y2+1-y1); + rv = PyString_FromStringAndSize((char *)NULL, size*sizeof(long)); + if ( rv == NULL ) + return NULL; + parray = (unsigned long *)PyString_AsString(rv); + size_ret = readdisplay(x1, y1, x2, y2, parray, hints); + if ( size_ret != size ) { + printf("gl_readdisplay: got %ld pixels, expected %ld\n", + size_ret, size); + PyErr_SetString(PyExc_RuntimeError, "readdisplay returned unexpected length"); + return NULL; + } + return rv; +} + +/* Desperately needed, here are tools to compress and decompress + the data manipulated by lrectread/lrectwrite. + + gl.packrect(width, height, packfactor, bigdata) --> smalldata + makes 'bigdata' 4*(packfactor**2) times smaller by: + - turning it into B/W (a factor 4) + - replacing squares of size pacfactor by one + representative + + gl.unpackrect(width, height, packfactor, smalldata) --> bigdata + is the inverse; the numeric arguments must be *the same*. + + Both work best if width and height are multiples of packfactor + (in fact unpackrect will leave garbage bytes). +*/ + + +static PyObject * +gl_packrect(PyObject *self, PyObject *args) +{ + long width, height, packfactor; + char *s; + PyObject *unpacked, *packed; + int pixcount, packedcount, x, y, r, g, b; + unsigned long pixel; + unsigned char *p; + unsigned long *parray; + if (!PyArg_GetLong(args, 4, 0, &width)) + return NULL; + if (!PyArg_GetLong(args, 4, 1, &height)) + return NULL; + if (!PyArg_GetLong(args, 4, 2, &packfactor)) + return NULL; + if (!PyArg_GetString(args, 4, 3, &s)) /* For type checking only */ + return NULL; + if (!PyArg_GetObject(args, 4, 3, &unpacked)) + return NULL; + if (width <= 0 || height <= 0 || packfactor <= 0) { + PyErr_SetString(PyExc_RuntimeError, "packrect args must be > 0"); + return NULL; + } + pixcount = width*height; + packedcount = ((width+packfactor-1)/packfactor) * + ((height+packfactor-1)/packfactor); + if (PyString_Size(unpacked) != pixcount*sizeof(long)) { + PyErr_SetString(PyExc_RuntimeError, + "string arg to packrect has wrong size"); + return NULL; + } + packed = PyString_FromStringAndSize((char *)NULL, packedcount); + if (packed == NULL) + return NULL; + parray = (unsigned long *) PyString_AsString(unpacked); + p = (unsigned char *) PyString_AsString(packed); + for (y = 0; y < height; y += packfactor, parray += packfactor*width) { + for (x = 0; x < width; x += packfactor) { + pixel = parray[x]; + r = pixel & 0xff; + g = (pixel >> 8) & 0xff; + b = (pixel >> 16) & 0xff; + *p++ = (30*r+59*g+11*b) / 100; + } + } + return packed; +} + + +static unsigned long unpacktab[256]; +static int unpacktab_inited = 0; + +static PyObject * +gl_unpackrect(PyObject *self, PyObject *args) +{ + long width, height, packfactor; + char *s; + PyObject *unpacked, *packed; + int pixcount, packedcount; + register unsigned char *p; + register unsigned long *parray; + if (!unpacktab_inited) { + register int white; + for (white = 256; --white >= 0; ) + unpacktab[white] = white * 0x010101L; + unpacktab_inited++; + } + if (!PyArg_GetLong(args, 4, 0, &width)) + return NULL; + if (!PyArg_GetLong(args, 4, 1, &height)) + return NULL; + if (!PyArg_GetLong(args, 4, 2, &packfactor)) + return NULL; + if (!PyArg_GetString(args, 4, 3, &s)) /* For type checking only */ + return NULL; + if (!PyArg_GetObject(args, 4, 3, &packed)) + return NULL; + if (width <= 0 || height <= 0 || packfactor <= 0) { + PyErr_SetString(PyExc_RuntimeError, "packrect args must be > 0"); + return NULL; + } + pixcount = width*height; + packedcount = ((width+packfactor-1)/packfactor) * + ((height+packfactor-1)/packfactor); + if (PyString_Size(packed) != packedcount) { + PyErr_SetString(PyExc_RuntimeError, + "string arg to unpackrect has wrong size"); + return NULL; + } + unpacked = PyString_FromStringAndSize((char *)NULL, pixcount*sizeof(long)); + if (unpacked == NULL) + return NULL; + parray = (unsigned long *) PyString_AsString(unpacked); + p = (unsigned char *) PyString_AsString(packed); + if (packfactor == 1 && width*height > 0) { + /* Just expand bytes to longs */ + register int x = width * height; + do { + *parray++ = unpacktab[*p++]; + } while (--x >= 0); + } + else { + register int y; + for (y = 0; y < height-packfactor+1; + y += packfactor, parray += packfactor*width) { + register int x; + for (x = 0; x < width-packfactor+1; x += packfactor) { + register unsigned long pixel = unpacktab[*p++]; + register int i; + for (i = packfactor*width; (i-=width) >= 0;) { + register int j; + for (j = packfactor; --j >= 0; ) + parray[i+x+j] = pixel; + } + } + } + } + return unpacked; +} + +static PyObject * +gl_gversion(PyObject *self, PyObject *args) +{ + char buf[20]; + gversion(buf); + return PyString_FromString(buf); +} + + +/* void clear - Manual because of clash with termcap */ +static PyObject * +gl_clear(PyObject *self, PyObject *args) +{ + __GLclear( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* End of manually written stubs */ + + +/* long getshade */ + +static PyObject * +gl_getshade(PyObject *self, PyObject *args) +{ + long retval; + retval = getshade( ); + return mknewlongobject(retval); +} + +/* void devport short s long s */ + +static PyObject * +gl_devport(PyObject *self, PyObject *args) +{ + short arg1 ; + long arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + devport( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rdr2i long s long s */ + +static PyObject * +gl_rdr2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + rdr2i( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rectfs short s short s short s short s */ + +static PyObject * +gl_rectfs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + rectfs( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rects short s short s short s short s */ + +static PyObject * +gl_rects(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + rects( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rmv2i long s long s */ + +static PyObject * +gl_rmv2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + rmv2i( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void noport */ + +static PyObject * +gl_noport(PyObject *self, PyObject *args) +{ + noport( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void popviewport */ + +static PyObject * +gl_popviewport(PyObject *self, PyObject *args) +{ + popviewport( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void clearhitcode */ + +static PyObject * +gl_clearhitcode(PyObject *self, PyObject *args) +{ + clearhitcode( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void closeobj */ + +static PyObject * +gl_closeobj(PyObject *self, PyObject *args) +{ + closeobj( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void cursoff */ + +static PyObject * +gl_cursoff(PyObject *self, PyObject *args) +{ + cursoff( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void curson */ + +static PyObject * +gl_curson(PyObject *self, PyObject *args) +{ + curson( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void doublebuffer */ + +static PyObject * +gl_doublebuffer(PyObject *self, PyObject *args) +{ + doublebuffer( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void finish */ + +static PyObject * +gl_finish(PyObject *self, PyObject *args) +{ + finish( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void gconfig */ + +static PyObject * +gl_gconfig(PyObject *self, PyObject *args) +{ + gconfig( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void ginit */ + +static PyObject * +gl_ginit(PyObject *self, PyObject *args) +{ + ginit( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void greset */ + +static PyObject * +gl_greset(PyObject *self, PyObject *args) +{ + greset( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void multimap */ + +static PyObject * +gl_multimap(PyObject *self, PyObject *args) +{ + multimap( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void onemap */ + +static PyObject * +gl_onemap(PyObject *self, PyObject *args) +{ + onemap( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void popattributes */ + +static PyObject * +gl_popattributes(PyObject *self, PyObject *args) +{ + popattributes( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void popmatrix */ + +static PyObject * +gl_popmatrix(PyObject *self, PyObject *args) +{ + popmatrix( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pushattributes */ + +static PyObject * +gl_pushattributes(PyObject *self, PyObject *args) +{ + pushattributes( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pushmatrix */ + +static PyObject * +gl_pushmatrix(PyObject *self, PyObject *args) +{ + pushmatrix( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pushviewport */ + +static PyObject * +gl_pushviewport(PyObject *self, PyObject *args) +{ + pushviewport( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void qreset */ + +static PyObject * +gl_qreset(PyObject *self, PyObject *args) +{ + qreset( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void RGBmode */ + +static PyObject * +gl_RGBmode(PyObject *self, PyObject *args) +{ + RGBmode( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void singlebuffer */ + +static PyObject * +gl_singlebuffer(PyObject *self, PyObject *args) +{ + singlebuffer( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void swapbuffers */ + +static PyObject * +gl_swapbuffers(PyObject *self, PyObject *args) +{ + swapbuffers( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void gsync */ + +static PyObject * +gl_gsync(PyObject *self, PyObject *args) +{ + gsync( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void gflush */ + +static PyObject * +gl_gflush(PyObject *self, PyObject *args) +{ + gflush( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void tpon */ + +static PyObject * +gl_tpon(PyObject *self, PyObject *args) +{ + tpon( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void tpoff */ + +static PyObject * +gl_tpoff(PyObject *self, PyObject *args) +{ + tpoff( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void clkon */ + +static PyObject * +gl_clkon(PyObject *self, PyObject *args) +{ + clkon( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void clkoff */ + +static PyObject * +gl_clkoff(PyObject *self, PyObject *args) +{ + clkoff( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void ringbell */ + +static PyObject * +gl_ringbell(PyObject *self, PyObject *args) +{ + ringbell( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void gbegin */ + +static PyObject * +gl_gbegin(PyObject *self, PyObject *args) +{ + gbegin( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void textinit */ + +static PyObject * +gl_textinit(PyObject *self, PyObject *args) +{ + textinit( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void initnames */ + +static PyObject * +gl_initnames(PyObject *self, PyObject *args) +{ + initnames( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pclos */ + +static PyObject * +gl_pclos(PyObject *self, PyObject *args) +{ + pclos( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void popname */ + +static PyObject * +gl_popname(PyObject *self, PyObject *args) +{ + popname( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void spclos */ + +static PyObject * +gl_spclos(PyObject *self, PyObject *args) +{ + spclos( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void zclear */ + +static PyObject * +gl_zclear(PyObject *self, PyObject *args) +{ + zclear( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void screenspace */ + +static PyObject * +gl_screenspace(PyObject *self, PyObject *args) +{ + screenspace( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void reshapeviewport */ + +static PyObject * +gl_reshapeviewport(PyObject *self, PyObject *args) +{ + reshapeviewport( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void winpush */ + +static PyObject * +gl_winpush(PyObject *self, PyObject *args) +{ + winpush( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void winpop */ + +static PyObject * +gl_winpop(PyObject *self, PyObject *args) +{ + winpop( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void foreground */ + +static PyObject * +gl_foreground(PyObject *self, PyObject *args) +{ + foreground( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void endfullscrn */ + +static PyObject * +gl_endfullscrn(PyObject *self, PyObject *args) +{ + endfullscrn( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void endpupmode */ + +static PyObject * +gl_endpupmode(PyObject *self, PyObject *args) +{ + endpupmode( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void fullscrn */ + +static PyObject * +gl_fullscrn(PyObject *self, PyObject *args) +{ + fullscrn( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pupmode */ + +static PyObject * +gl_pupmode(PyObject *self, PyObject *args) +{ + pupmode( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void winconstraints */ + +static PyObject * +gl_winconstraints(PyObject *self, PyObject *args) +{ + winconstraints( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pagecolor short s */ + +static PyObject * +gl_pagecolor(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + pagecolor( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void textcolor short s */ + +static PyObject * +gl_textcolor(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + textcolor( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void color short s */ + +static PyObject * +gl_color(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + color( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void curveit short s */ + +static PyObject * +gl_curveit(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + curveit( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void font short s */ + +static PyObject * +gl_font(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + font( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void linewidth short s */ + +static PyObject * +gl_linewidth(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + linewidth( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setlinestyle short s */ + +static PyObject * +gl_setlinestyle(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + setlinestyle( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setmap short s */ + +static PyObject * +gl_setmap(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + setmap( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void swapinterval short s */ + +static PyObject * +gl_swapinterval(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + swapinterval( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void writemask short s */ + +static PyObject * +gl_writemask(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + writemask( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void textwritemask short s */ + +static PyObject * +gl_textwritemask(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + textwritemask( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void qdevice short s */ + +static PyObject * +gl_qdevice(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + qdevice( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void unqdevice short s */ + +static PyObject * +gl_unqdevice(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + unqdevice( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void curvebasis short s */ + +static PyObject * +gl_curvebasis(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + curvebasis( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void curveprecision short s */ + +static PyObject * +gl_curveprecision(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + curveprecision( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void loadname short s */ + +static PyObject * +gl_loadname(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + loadname( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void passthrough short s */ + +static PyObject * +gl_passthrough(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + passthrough( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pushname short s */ + +static PyObject * +gl_pushname(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + pushname( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setmonitor short s */ + +static PyObject * +gl_setmonitor(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + setmonitor( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setshade short s */ + +static PyObject * +gl_setshade(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + setshade( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setpattern short s */ + +static PyObject * +gl_setpattern(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + setpattern( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pagewritemask short s */ + +static PyObject * +gl_pagewritemask(PyObject *self, PyObject *args) +{ + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + pagewritemask( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void callobj long s */ + +static PyObject * +gl_callobj(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + callobj( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void delobj long s */ + +static PyObject * +gl_delobj(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + delobj( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void editobj long s */ + +static PyObject * +gl_editobj(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + editobj( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void makeobj long s */ + +static PyObject * +gl_makeobj(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + makeobj( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void maketag long s */ + +static PyObject * +gl_maketag(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + maketag( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void chunksize long s */ + +static PyObject * +gl_chunksize(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + chunksize( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void compactify long s */ + +static PyObject * +gl_compactify(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + compactify( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void deltag long s */ + +static PyObject * +gl_deltag(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + deltag( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void lsrepeat long s */ + +static PyObject * +gl_lsrepeat(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + lsrepeat( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void objinsert long s */ + +static PyObject * +gl_objinsert(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + objinsert( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void objreplace long s */ + +static PyObject * +gl_objreplace(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + objreplace( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void winclose long s */ + +static PyObject * +gl_winclose(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + winclose( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void blanktime long s */ + +static PyObject * +gl_blanktime(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + blanktime( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void freepup long s */ + +static PyObject * +gl_freepup(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + freepup( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void backbuffer long s */ + +static PyObject * +gl_backbuffer(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + backbuffer( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void frontbuffer long s */ + +static PyObject * +gl_frontbuffer(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + frontbuffer( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void lsbackup long s */ + +static PyObject * +gl_lsbackup(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + lsbackup( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void resetls long s */ + +static PyObject * +gl_resetls(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + resetls( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void lampon long s */ + +static PyObject * +gl_lampon(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + lampon( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void lampoff long s */ + +static PyObject * +gl_lampoff(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + lampoff( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setbell long s */ + +static PyObject * +gl_setbell(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + setbell( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void blankscreen long s */ + +static PyObject * +gl_blankscreen(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + blankscreen( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void depthcue long s */ + +static PyObject * +gl_depthcue(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + depthcue( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void zbuffer long s */ + +static PyObject * +gl_zbuffer(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + zbuffer( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void backface long s */ + +static PyObject * +gl_backface(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + backface( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void cmov2i long s long s */ + +static PyObject * +gl_cmov2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + cmov2i( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void draw2i long s long s */ + +static PyObject * +gl_draw2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + draw2i( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void move2i long s long s */ + +static PyObject * +gl_move2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + move2i( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pnt2i long s long s */ + +static PyObject * +gl_pnt2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + pnt2i( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void patchbasis long s long s */ + +static PyObject * +gl_patchbasis(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + patchbasis( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void patchprecision long s long s */ + +static PyObject * +gl_patchprecision(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + patchprecision( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pdr2i long s long s */ + +static PyObject * +gl_pdr2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + pdr2i( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pmv2i long s long s */ + +static PyObject * +gl_pmv2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + pmv2i( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpdr2i long s long s */ + +static PyObject * +gl_rpdr2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + rpdr2i( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpmv2i long s long s */ + +static PyObject * +gl_rpmv2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + rpmv2i( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void xfpt2i long s long s */ + +static PyObject * +gl_xfpt2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + xfpt2i( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void objdelete long s long s */ + +static PyObject * +gl_objdelete(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + objdelete( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void patchcurves long s long s */ + +static PyObject * +gl_patchcurves(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + patchcurves( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void minsize long s long s */ + +static PyObject * +gl_minsize(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + minsize( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void maxsize long s long s */ + +static PyObject * +gl_maxsize(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + maxsize( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void keepaspect long s long s */ + +static PyObject * +gl_keepaspect(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + keepaspect( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void prefsize long s long s */ + +static PyObject * +gl_prefsize(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + prefsize( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void stepunit long s long s */ + +static PyObject * +gl_stepunit(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + stepunit( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void fudge long s long s */ + +static PyObject * +gl_fudge(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + fudge( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void winmove long s long s */ + +static PyObject * +gl_winmove(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + winmove( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void attachcursor short s short s */ + +static PyObject * +gl_attachcursor(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + attachcursor( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void deflinestyle short s short s */ + +static PyObject * +gl_deflinestyle(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + deflinestyle( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void noise short s short s */ + +static PyObject * +gl_noise(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + noise( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void picksize short s short s */ + +static PyObject * +gl_picksize(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + picksize( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void qenter short s short s */ + +static PyObject * +gl_qenter(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + qenter( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setdepth short s short s */ + +static PyObject * +gl_setdepth(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + setdepth( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void cmov2s short s short s */ + +static PyObject * +gl_cmov2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + cmov2s( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void draw2s short s short s */ + +static PyObject * +gl_draw2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + draw2s( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void move2s short s short s */ + +static PyObject * +gl_move2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + move2s( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pdr2s short s short s */ + +static PyObject * +gl_pdr2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + pdr2s( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pmv2s short s short s */ + +static PyObject * +gl_pmv2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + pmv2s( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pnt2s short s short s */ + +static PyObject * +gl_pnt2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + pnt2s( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rdr2s short s short s */ + +static PyObject * +gl_rdr2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + rdr2s( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rmv2s short s short s */ + +static PyObject * +gl_rmv2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + rmv2s( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpdr2s short s short s */ + +static PyObject * +gl_rpdr2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + rpdr2s( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpmv2s short s short s */ + +static PyObject * +gl_rpmv2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + rpmv2s( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void xfpt2s short s short s */ + +static PyObject * +gl_xfpt2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + xfpt2s( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void cmov2 float s float s */ + +static PyObject * +gl_cmov2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + cmov2( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void draw2 float s float s */ + +static PyObject * +gl_draw2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + draw2( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void move2 float s float s */ + +static PyObject * +gl_move2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + move2( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pnt2 float s float s */ + +static PyObject * +gl_pnt2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + pnt2( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pdr2 float s float s */ + +static PyObject * +gl_pdr2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + pdr2( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pmv2 float s float s */ + +static PyObject * +gl_pmv2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + pmv2( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rdr2 float s float s */ + +static PyObject * +gl_rdr2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + rdr2( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rmv2 float s float s */ + +static PyObject * +gl_rmv2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + rmv2( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpdr2 float s float s */ + +static PyObject * +gl_rpdr2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + rpdr2( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpmv2 float s float s */ + +static PyObject * +gl_rpmv2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + rpmv2( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void xfpt2 float s float s */ + +static PyObject * +gl_xfpt2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + xfpt2( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void loadmatrix float s[4*4] */ + +static PyObject * +gl_loadmatrix(PyObject *self, PyObject *args) +{ + float arg1 [ 4 ] [ 4 ] ; + if (!getifloatarray(args, 1, 0, 4 * 4 , (float *) arg1)) + return NULL; + loadmatrix( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void multmatrix float s[4*4] */ + +static PyObject * +gl_multmatrix(PyObject *self, PyObject *args) +{ + float arg1 [ 4 ] [ 4 ] ; + if (!getifloatarray(args, 1, 0, 4 * 4 , (float *) arg1)) + return NULL; + multmatrix( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void crv float s[3*4] */ + +static PyObject * +gl_crv(PyObject *self, PyObject *args) +{ + float arg1 [ 4 ] [ 3 ] ; + if (!getifloatarray(args, 1, 0, 3 * 4 , (float *) arg1)) + return NULL; + crv( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rcrv float s[4*4] */ + +static PyObject * +gl_rcrv(PyObject *self, PyObject *args) +{ + float arg1 [ 4 ] [ 4 ] ; + if (!getifloatarray(args, 1, 0, 4 * 4 , (float *) arg1)) + return NULL; + rcrv( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void addtopup long s char *s long s */ + +static PyObject * +gl_addtopup(PyObject *self, PyObject *args) +{ + long arg1 ; + string arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getistringarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + addtopup( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void charstr char *s */ + +static PyObject * +gl_charstr(PyObject *self, PyObject *args) +{ + string arg1 ; + if (!getistringarg(args, 1, 0, &arg1)) + return NULL; + charstr( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void getport char *s */ + +static PyObject * +gl_getport(PyObject *self, PyObject *args) +{ + string arg1 ; + if (!getistringarg(args, 1, 0, &arg1)) + return NULL; + getport( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* long strwidth char *s */ + +static PyObject * +gl_strwidth(PyObject *self, PyObject *args) +{ + long retval; + string arg1 ; + if (!getistringarg(args, 1, 0, &arg1)) + return NULL; + retval = strwidth( arg1 ); + return mknewlongobject(retval); +} + +/* long winopen char *s */ + +static PyObject * +gl_winopen(PyObject *self, PyObject *args) +{ + long retval; + string arg1 ; + if (!getistringarg(args, 1, 0, &arg1)) + return NULL; + retval = winopen( arg1 ); + return mknewlongobject(retval); +} + +/* void wintitle char *s */ + +static PyObject * +gl_wintitle(PyObject *self, PyObject *args) +{ + string arg1 ; + if (!getistringarg(args, 1, 0, &arg1)) + return NULL; + wintitle( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void polf long s float s[3*arg1] */ + +static PyObject * +gl_polf(PyObject *self, PyObject *args) +{ + long arg1 ; + float (* arg2) [ 3 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 3; + if ((arg2 = (float(*)[3]) PyMem_NEW(float , 3 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getifloatarray(args, 1, 0, 3 * arg1 , (float *) arg2)) + return NULL; + polf( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void polf2 long s float s[2*arg1] */ + +static PyObject * +gl_polf2(PyObject *self, PyObject *args) +{ + long arg1 ; + float (* arg2) [ 2 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 2; + if ((arg2 = (float(*)[2]) PyMem_NEW(float , 2 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getifloatarray(args, 1, 0, 2 * arg1 , (float *) arg2)) + return NULL; + polf2( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void poly long s float s[3*arg1] */ + +static PyObject * +gl_poly(PyObject *self, PyObject *args) +{ + long arg1 ; + float (* arg2) [ 3 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 3; + if ((arg2 = (float(*)[3]) PyMem_NEW(float , 3 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getifloatarray(args, 1, 0, 3 * arg1 , (float *) arg2)) + return NULL; + poly( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void poly2 long s float s[2*arg1] */ + +static PyObject * +gl_poly2(PyObject *self, PyObject *args) +{ + long arg1 ; + float (* arg2) [ 2 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 2; + if ((arg2 = (float(*)[2]) PyMem_NEW(float , 2 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getifloatarray(args, 1, 0, 2 * arg1 , (float *) arg2)) + return NULL; + poly2( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void crvn long s float s[3*arg1] */ + +static PyObject * +gl_crvn(PyObject *self, PyObject *args) +{ + long arg1 ; + float (* arg2) [ 3 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 3; + if ((arg2 = (float(*)[3]) PyMem_NEW(float , 3 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getifloatarray(args, 1, 0, 3 * arg1 , (float *) arg2)) + return NULL; + crvn( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rcrvn long s float s[4*arg1] */ + +static PyObject * +gl_rcrvn(PyObject *self, PyObject *args) +{ + long arg1 ; + float (* arg2) [ 4 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 4; + if ((arg2 = (float(*)[4]) PyMem_NEW(float , 4 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getifloatarray(args, 1, 0, 4 * arg1 , (float *) arg2)) + return NULL; + rcrvn( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void polf2i long s long s[2*arg1] */ + +static PyObject * +gl_polf2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long (* arg2) [ 2 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 2; + if ((arg2 = (long(*)[2]) PyMem_NEW(long , 2 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getilongarray(args, 1, 0, 2 * arg1 , (long *) arg2)) + return NULL; + polf2i( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void polfi long s long s[3*arg1] */ + +static PyObject * +gl_polfi(PyObject *self, PyObject *args) +{ + long arg1 ; + long (* arg2) [ 3 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 3; + if ((arg2 = (long(*)[3]) PyMem_NEW(long , 3 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getilongarray(args, 1, 0, 3 * arg1 , (long *) arg2)) + return NULL; + polfi( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void poly2i long s long s[2*arg1] */ + +static PyObject * +gl_poly2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long (* arg2) [ 2 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 2; + if ((arg2 = (long(*)[2]) PyMem_NEW(long , 2 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getilongarray(args, 1, 0, 2 * arg1 , (long *) arg2)) + return NULL; + poly2i( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void polyi long s long s[3*arg1] */ + +static PyObject * +gl_polyi(PyObject *self, PyObject *args) +{ + long arg1 ; + long (* arg2) [ 3 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 3; + if ((arg2 = (long(*)[3]) PyMem_NEW(long , 3 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getilongarray(args, 1, 0, 3 * arg1 , (long *) arg2)) + return NULL; + polyi( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void polf2s long s short s[2*arg1] */ + +static PyObject * +gl_polf2s(PyObject *self, PyObject *args) +{ + long arg1 ; + short (* arg2) [ 2 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 2; + if ((arg2 = (short(*)[2]) PyMem_NEW(short , 2 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 1, 0, 2 * arg1 , (short *) arg2)) + return NULL; + polf2s( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void polfs long s short s[3*arg1] */ + +static PyObject * +gl_polfs(PyObject *self, PyObject *args) +{ + long arg1 ; + short (* arg2) [ 3 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 3; + if ((arg2 = (short(*)[3]) PyMem_NEW(short , 3 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 1, 0, 3 * arg1 , (short *) arg2)) + return NULL; + polfs( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void polys long s short s[3*arg1] */ + +static PyObject * +gl_polys(PyObject *self, PyObject *args) +{ + long arg1 ; + short (* arg2) [ 3 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 3; + if ((arg2 = (short(*)[3]) PyMem_NEW(short , 3 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 1, 0, 3 * arg1 , (short *) arg2)) + return NULL; + polys( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void poly2s long s short s[2*arg1] */ + +static PyObject * +gl_poly2s(PyObject *self, PyObject *args) +{ + long arg1 ; + short (* arg2) [ 2 ] ; + if (!getilongarraysize(args, 1, 0, &arg1)) + return NULL; + arg1 = arg1 / 2; + if ((arg2 = (short(*)[2]) PyMem_NEW(short , 2 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 1, 0, 2 * arg1 , (short *) arg2)) + return NULL; + poly2s( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void defcursor short s u_short s[128] */ + +static PyObject * +gl_defcursor(PyObject *self, PyObject *args) +{ + short arg1 ; + unsigned short arg2 [ 128 ] ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarray(args, 2, 1, 128 , (short *) arg2)) + return NULL; + defcursor( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void writepixels short s u_short s[arg1] */ + +static PyObject * +gl_writepixels(PyObject *self, PyObject *args) +{ + short arg1 ; + unsigned short * arg2 ; + if (!getishortarraysize(args, 1, 0, &arg1)) + return NULL; + if ((arg2 = PyMem_NEW(unsigned short , arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 1, 0, arg1 , (short *) arg2)) + return NULL; + writepixels( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void defbasis long s float s[4*4] */ + +static PyObject * +gl_defbasis(PyObject *self, PyObject *args) +{ + long arg1 ; + float arg2 [ 4 ] [ 4 ] ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarray(args, 2, 1, 4 * 4 , (float *) arg2)) + return NULL; + defbasis( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void gewrite short s short s[arg1] */ + +static PyObject * +gl_gewrite(PyObject *self, PyObject *args) +{ + short arg1 ; + short * arg2 ; + if (!getishortarraysize(args, 1, 0, &arg1)) + return NULL; + if ((arg2 = PyMem_NEW(short , arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 1, 0, arg1 , arg2)) + return NULL; + gewrite( arg1 , arg2 ); + PyMem_DEL(arg2); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rotate short s char s */ + +static PyObject * +gl_rotate(PyObject *self, PyObject *args) +{ + short arg1 ; + char arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getichararg(args, 2, 1, &arg2)) + return NULL; + rotate( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rot float s char s */ + +static PyObject * +gl_rot(PyObject *self, PyObject *args) +{ + float arg1 ; + char arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getichararg(args, 2, 1, &arg2)) + return NULL; + rot( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void circfi long s long s long s */ + +static PyObject * +gl_circfi(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + circfi( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void circi long s long s long s */ + +static PyObject * +gl_circi(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + circi( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void cmovi long s long s long s */ + +static PyObject * +gl_cmovi(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + cmovi( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void drawi long s long s long s */ + +static PyObject * +gl_drawi(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + drawi( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void movei long s long s long s */ + +static PyObject * +gl_movei(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + movei( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pnti long s long s long s */ + +static PyObject * +gl_pnti(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + pnti( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void newtag long s long s long s */ + +static PyObject * +gl_newtag(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + newtag( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pdri long s long s long s */ + +static PyObject * +gl_pdri(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + pdri( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pmvi long s long s long s */ + +static PyObject * +gl_pmvi(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + pmvi( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rdri long s long s long s */ + +static PyObject * +gl_rdri(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + rdri( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rmvi long s long s long s */ + +static PyObject * +gl_rmvi(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + rmvi( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpdri long s long s long s */ + +static PyObject * +gl_rpdri(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + rpdri( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpmvi long s long s long s */ + +static PyObject * +gl_rpmvi(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + rpmvi( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void xfpti long s long s long s */ + +static PyObject * +gl_xfpti(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + xfpti( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void circ float s float s float s */ + +static PyObject * +gl_circ(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + circ( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void circf float s float s float s */ + +static PyObject * +gl_circf(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + circf( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void cmov float s float s float s */ + +static PyObject * +gl_cmov(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + cmov( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void draw float s float s float s */ + +static PyObject * +gl_draw(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + draw( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void move float s float s float s */ + +static PyObject * +gl_move(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + move( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pnt float s float s float s */ + +static PyObject * +gl_pnt(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + pnt( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void scale float s float s float s */ + +static PyObject * +gl_scale(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + scale( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void translate float s float s float s */ + +static PyObject * +gl_translate(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + translate( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pdr float s float s float s */ + +static PyObject * +gl_pdr(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + pdr( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pmv float s float s float s */ + +static PyObject * +gl_pmv(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + pmv( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rdr float s float s float s */ + +static PyObject * +gl_rdr(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + rdr( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rmv float s float s float s */ + +static PyObject * +gl_rmv(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + rmv( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpdr float s float s float s */ + +static PyObject * +gl_rpdr(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + rpdr( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpmv float s float s float s */ + +static PyObject * +gl_rpmv(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + rpmv( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void xfpt float s float s float s */ + +static PyObject * +gl_xfpt(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + if (!getifloatarg(args, 3, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 3, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 3, 2, &arg3)) + return NULL; + xfpt( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void RGBcolor short s short s short s */ + +static PyObject * +gl_RGBcolor(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + RGBcolor( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void RGBwritemask short s short s short s */ + +static PyObject * +gl_RGBwritemask(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + RGBwritemask( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setcursor short s short s short s */ + +static PyObject * +gl_setcursor(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + setcursor( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void tie short s short s short s */ + +static PyObject * +gl_tie(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + tie( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void circfs short s short s short s */ + +static PyObject * +gl_circfs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + circfs( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void circs short s short s short s */ + +static PyObject * +gl_circs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + circs( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void cmovs short s short s short s */ + +static PyObject * +gl_cmovs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + cmovs( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void draws short s short s short s */ + +static PyObject * +gl_draws(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + draws( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void moves short s short s short s */ + +static PyObject * +gl_moves(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + moves( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pdrs short s short s short s */ + +static PyObject * +gl_pdrs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + pdrs( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pmvs short s short s short s */ + +static PyObject * +gl_pmvs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + pmvs( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pnts short s short s short s */ + +static PyObject * +gl_pnts(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + pnts( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rdrs short s short s short s */ + +static PyObject * +gl_rdrs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + rdrs( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rmvs short s short s short s */ + +static PyObject * +gl_rmvs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + rmvs( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpdrs short s short s short s */ + +static PyObject * +gl_rpdrs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + rpdrs( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpmvs short s short s short s */ + +static PyObject * +gl_rpmvs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + rpmvs( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void xfpts short s short s short s */ + +static PyObject * +gl_xfpts(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + xfpts( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void curorigin short s short s short s */ + +static PyObject * +gl_curorigin(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + curorigin( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void cyclemap short s short s short s */ + +static PyObject * +gl_cyclemap(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + if (!getishortarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + cyclemap( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void patch float s[4*4] float s[4*4] float s[4*4] */ + +static PyObject * +gl_patch(PyObject *self, PyObject *args) +{ + float arg1 [ 4 ] [ 4 ] ; + float arg2 [ 4 ] [ 4 ] ; + float arg3 [ 4 ] [ 4 ] ; + if (!getifloatarray(args, 3, 0, 4 * 4 , (float *) arg1)) + return NULL; + if (!getifloatarray(args, 3, 1, 4 * 4 , (float *) arg2)) + return NULL; + if (!getifloatarray(args, 3, 2, 4 * 4 , (float *) arg3)) + return NULL; + patch( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void splf long s float s[3*arg1] u_short s[arg1] */ + +static PyObject * +gl_splf(PyObject *self, PyObject *args) +{ + long arg1 ; + float (* arg2) [ 3 ] ; + unsigned short * arg3 ; + if (!getilongarraysize(args, 2, 0, &arg1)) + return NULL; + arg1 = arg1 / 3; + if ((arg2 = (float(*)[3]) PyMem_NEW(float , 3 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getifloatarray(args, 2, 0, 3 * arg1 , (float *) arg2)) + return NULL; + if ((arg3 = PyMem_NEW(unsigned short , arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 2, 1, arg1 , (short *) arg3)) + return NULL; + splf( arg1 , arg2 , arg3 ); + PyMem_DEL(arg2); + PyMem_DEL(arg3); + Py_INCREF(Py_None); + return Py_None; +} + +/* void splf2 long s float s[2*arg1] u_short s[arg1] */ + +static PyObject * +gl_splf2(PyObject *self, PyObject *args) +{ + long arg1 ; + float (* arg2) [ 2 ] ; + unsigned short * arg3 ; + if (!getilongarraysize(args, 2, 0, &arg1)) + return NULL; + arg1 = arg1 / 2; + if ((arg2 = (float(*)[2]) PyMem_NEW(float , 2 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getifloatarray(args, 2, 0, 2 * arg1 , (float *) arg2)) + return NULL; + if ((arg3 = PyMem_NEW(unsigned short , arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 2, 1, arg1 , (short *) arg3)) + return NULL; + splf2( arg1 , arg2 , arg3 ); + PyMem_DEL(arg2); + PyMem_DEL(arg3); + Py_INCREF(Py_None); + return Py_None; +} + +/* void splfi long s long s[3*arg1] u_short s[arg1] */ + +static PyObject * +gl_splfi(PyObject *self, PyObject *args) +{ + long arg1 ; + long (* arg2) [ 3 ] ; + unsigned short * arg3 ; + if (!getilongarraysize(args, 2, 0, &arg1)) + return NULL; + arg1 = arg1 / 3; + if ((arg2 = (long(*)[3]) PyMem_NEW(long , 3 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getilongarray(args, 2, 0, 3 * arg1 , (long *) arg2)) + return NULL; + if ((arg3 = PyMem_NEW(unsigned short , arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 2, 1, arg1 , (short *) arg3)) + return NULL; + splfi( arg1 , arg2 , arg3 ); + PyMem_DEL(arg2); + PyMem_DEL(arg3); + Py_INCREF(Py_None); + return Py_None; +} + +/* void splf2i long s long s[2*arg1] u_short s[arg1] */ + +static PyObject * +gl_splf2i(PyObject *self, PyObject *args) +{ + long arg1 ; + long (* arg2) [ 2 ] ; + unsigned short * arg3 ; + if (!getilongarraysize(args, 2, 0, &arg1)) + return NULL; + arg1 = arg1 / 2; + if ((arg2 = (long(*)[2]) PyMem_NEW(long , 2 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getilongarray(args, 2, 0, 2 * arg1 , (long *) arg2)) + return NULL; + if ((arg3 = PyMem_NEW(unsigned short , arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 2, 1, arg1 , (short *) arg3)) + return NULL; + splf2i( arg1 , arg2 , arg3 ); + PyMem_DEL(arg2); + PyMem_DEL(arg3); + Py_INCREF(Py_None); + return Py_None; +} + +/* void splfs long s short s[3*arg1] u_short s[arg1] */ + +static PyObject * +gl_splfs(PyObject *self, PyObject *args) +{ + long arg1 ; + short (* arg2) [ 3 ] ; + unsigned short * arg3 ; + if (!getilongarraysize(args, 2, 0, &arg1)) + return NULL; + arg1 = arg1 / 3; + if ((arg2 = (short(*)[3]) PyMem_NEW(short , 3 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 2, 0, 3 * arg1 , (short *) arg2)) + return NULL; + if ((arg3 = PyMem_NEW(unsigned short , arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 2, 1, arg1 , (short *) arg3)) + return NULL; + splfs( arg1 , arg2 , arg3 ); + PyMem_DEL(arg2); + PyMem_DEL(arg3); + Py_INCREF(Py_None); + return Py_None; +} + +/* void splf2s long s short s[2*arg1] u_short s[arg1] */ + +static PyObject * +gl_splf2s(PyObject *self, PyObject *args) +{ + long arg1 ; + short (* arg2) [ 2 ] ; + unsigned short * arg3 ; + if (!getilongarraysize(args, 2, 0, &arg1)) + return NULL; + arg1 = arg1 / 2; + if ((arg2 = (short(*)[2]) PyMem_NEW(short , 2 * arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 2, 0, 2 * arg1 , (short *) arg2)) + return NULL; + if ((arg3 = PyMem_NEW(unsigned short , arg1 )) == NULL) + return PyErr_NoMemory(); + if (!getishortarray(args, 2, 1, arg1 , (short *) arg3)) + return NULL; + splf2s( arg1 , arg2 , arg3 ); + PyMem_DEL(arg2); + PyMem_DEL(arg3); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rpatch float s[4*4] float s[4*4] float s[4*4] float s[4*4] */ + +static PyObject * +gl_rpatch(PyObject *self, PyObject *args) +{ + float arg1 [ 4 ] [ 4 ] ; + float arg2 [ 4 ] [ 4 ] ; + float arg3 [ 4 ] [ 4 ] ; + float arg4 [ 4 ] [ 4 ] ; + if (!getifloatarray(args, 4, 0, 4 * 4 , (float *) arg1)) + return NULL; + if (!getifloatarray(args, 4, 1, 4 * 4 , (float *) arg2)) + return NULL; + if (!getifloatarray(args, 4, 2, 4 * 4 , (float *) arg3)) + return NULL; + if (!getifloatarray(args, 4, 3, 4 * 4 , (float *) arg4)) + return NULL; + rpatch( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void ortho2 float s float s float s float s */ + +static PyObject * +gl_ortho2(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + float arg4 ; + if (!getifloatarg(args, 4, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 4, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 4, 2, &arg3)) + return NULL; + if (!getifloatarg(args, 4, 3, &arg4)) + return NULL; + ortho2( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rect float s float s float s float s */ + +static PyObject * +gl_rect(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + float arg4 ; + if (!getifloatarg(args, 4, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 4, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 4, 2, &arg3)) + return NULL; + if (!getifloatarg(args, 4, 3, &arg4)) + return NULL; + rect( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rectf float s float s float s float s */ + +static PyObject * +gl_rectf(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + float arg4 ; + if (!getifloatarg(args, 4, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 4, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 4, 2, &arg3)) + return NULL; + if (!getifloatarg(args, 4, 3, &arg4)) + return NULL; + rectf( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void xfpt4 float s float s float s float s */ + +static PyObject * +gl_xfpt4(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + float arg4 ; + if (!getifloatarg(args, 4, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 4, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 4, 2, &arg3)) + return NULL; + if (!getifloatarg(args, 4, 3, &arg4)) + return NULL; + xfpt4( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void textport short s short s short s short s */ + +static PyObject * +gl_textport(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + textport( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void mapcolor short s short s short s short s */ + +static PyObject * +gl_mapcolor(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + mapcolor( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void scrmask short s short s short s short s */ + +static PyObject * +gl_scrmask(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + scrmask( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setvaluator short s short s short s short s */ + +static PyObject * +gl_setvaluator(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + setvaluator( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void viewport short s short s short s short s */ + +static PyObject * +gl_viewport(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + viewport( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void shaderange short s short s short s short s */ + +static PyObject * +gl_shaderange(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + shaderange( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void xfpt4s short s short s short s short s */ + +static PyObject * +gl_xfpt4s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + xfpt4s( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rectfi long s long s long s long s */ + +static PyObject * +gl_rectfi(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + long arg4 ; + if (!getilongarg(args, 4, 0, &arg1)) + return NULL; + if (!getilongarg(args, 4, 1, &arg2)) + return NULL; + if (!getilongarg(args, 4, 2, &arg3)) + return NULL; + if (!getilongarg(args, 4, 3, &arg4)) + return NULL; + rectfi( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void recti long s long s long s long s */ + +static PyObject * +gl_recti(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + long arg4 ; + if (!getilongarg(args, 4, 0, &arg1)) + return NULL; + if (!getilongarg(args, 4, 1, &arg2)) + return NULL; + if (!getilongarg(args, 4, 2, &arg3)) + return NULL; + if (!getilongarg(args, 4, 3, &arg4)) + return NULL; + recti( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void xfpt4i long s long s long s long s */ + +static PyObject * +gl_xfpt4i(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + long arg4 ; + if (!getilongarg(args, 4, 0, &arg1)) + return NULL; + if (!getilongarg(args, 4, 1, &arg2)) + return NULL; + if (!getilongarg(args, 4, 2, &arg3)) + return NULL; + if (!getilongarg(args, 4, 3, &arg4)) + return NULL; + xfpt4i( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void prefposition long s long s long s long s */ + +static PyObject * +gl_prefposition(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + long arg4 ; + if (!getilongarg(args, 4, 0, &arg1)) + return NULL; + if (!getilongarg(args, 4, 1, &arg2)) + return NULL; + if (!getilongarg(args, 4, 2, &arg3)) + return NULL; + if (!getilongarg(args, 4, 3, &arg4)) + return NULL; + prefposition( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void arc float s float s float s short s short s */ + +static PyObject * +gl_arc(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + short arg4 ; + short arg5 ; + if (!getifloatarg(args, 5, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 5, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 5, 2, &arg3)) + return NULL; + if (!getishortarg(args, 5, 3, &arg4)) + return NULL; + if (!getishortarg(args, 5, 4, &arg5)) + return NULL; + arc( arg1 , arg2 , arg3 , arg4 , arg5 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void arcf float s float s float s short s short s */ + +static PyObject * +gl_arcf(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + short arg4 ; + short arg5 ; + if (!getifloatarg(args, 5, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 5, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 5, 2, &arg3)) + return NULL; + if (!getishortarg(args, 5, 3, &arg4)) + return NULL; + if (!getishortarg(args, 5, 4, &arg5)) + return NULL; + arcf( arg1 , arg2 , arg3 , arg4 , arg5 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void arcfi long s long s long s short s short s */ + +static PyObject * +gl_arcfi(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + short arg4 ; + short arg5 ; + if (!getilongarg(args, 5, 0, &arg1)) + return NULL; + if (!getilongarg(args, 5, 1, &arg2)) + return NULL; + if (!getilongarg(args, 5, 2, &arg3)) + return NULL; + if (!getishortarg(args, 5, 3, &arg4)) + return NULL; + if (!getishortarg(args, 5, 4, &arg5)) + return NULL; + arcfi( arg1 , arg2 , arg3 , arg4 , arg5 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void arci long s long s long s short s short s */ + +static PyObject * +gl_arci(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + short arg4 ; + short arg5 ; + if (!getilongarg(args, 5, 0, &arg1)) + return NULL; + if (!getilongarg(args, 5, 1, &arg2)) + return NULL; + if (!getilongarg(args, 5, 2, &arg3)) + return NULL; + if (!getishortarg(args, 5, 3, &arg4)) + return NULL; + if (!getishortarg(args, 5, 4, &arg5)) + return NULL; + arci( arg1 , arg2 , arg3 , arg4 , arg5 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void bbox2 short s short s float s float s float s float s */ + +static PyObject * +gl_bbox2(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + float arg3 ; + float arg4 ; + float arg5 ; + float arg6 ; + if (!getishortarg(args, 6, 0, &arg1)) + return NULL; + if (!getishortarg(args, 6, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 6, 2, &arg3)) + return NULL; + if (!getifloatarg(args, 6, 3, &arg4)) + return NULL; + if (!getifloatarg(args, 6, 4, &arg5)) + return NULL; + if (!getifloatarg(args, 6, 5, &arg6)) + return NULL; + bbox2( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void bbox2i short s short s long s long s long s long s */ + +static PyObject * +gl_bbox2i(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + long arg3 ; + long arg4 ; + long arg5 ; + long arg6 ; + if (!getishortarg(args, 6, 0, &arg1)) + return NULL; + if (!getishortarg(args, 6, 1, &arg2)) + return NULL; + if (!getilongarg(args, 6, 2, &arg3)) + return NULL; + if (!getilongarg(args, 6, 3, &arg4)) + return NULL; + if (!getilongarg(args, 6, 4, &arg5)) + return NULL; + if (!getilongarg(args, 6, 5, &arg6)) + return NULL; + bbox2i( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void bbox2s short s short s short s short s short s short s */ + +static PyObject * +gl_bbox2s(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + short arg5 ; + short arg6 ; + if (!getishortarg(args, 6, 0, &arg1)) + return NULL; + if (!getishortarg(args, 6, 1, &arg2)) + return NULL; + if (!getishortarg(args, 6, 2, &arg3)) + return NULL; + if (!getishortarg(args, 6, 3, &arg4)) + return NULL; + if (!getishortarg(args, 6, 4, &arg5)) + return NULL; + if (!getishortarg(args, 6, 5, &arg6)) + return NULL; + bbox2s( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void blink short s short s short s short s short s */ + +static PyObject * +gl_blink(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + short arg5 ; + if (!getishortarg(args, 5, 0, &arg1)) + return NULL; + if (!getishortarg(args, 5, 1, &arg2)) + return NULL; + if (!getishortarg(args, 5, 2, &arg3)) + return NULL; + if (!getishortarg(args, 5, 3, &arg4)) + return NULL; + if (!getishortarg(args, 5, 4, &arg5)) + return NULL; + blink( arg1 , arg2 , arg3 , arg4 , arg5 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void ortho float s float s float s float s float s float s */ + +static PyObject * +gl_ortho(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + float arg4 ; + float arg5 ; + float arg6 ; + if (!getifloatarg(args, 6, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 6, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 6, 2, &arg3)) + return NULL; + if (!getifloatarg(args, 6, 3, &arg4)) + return NULL; + if (!getifloatarg(args, 6, 4, &arg5)) + return NULL; + if (!getifloatarg(args, 6, 5, &arg6)) + return NULL; + ortho( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void window float s float s float s float s float s float s */ + +static PyObject * +gl_window(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + float arg4 ; + float arg5 ; + float arg6 ; + if (!getifloatarg(args, 6, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 6, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 6, 2, &arg3)) + return NULL; + if (!getifloatarg(args, 6, 3, &arg4)) + return NULL; + if (!getifloatarg(args, 6, 4, &arg5)) + return NULL; + if (!getifloatarg(args, 6, 5, &arg6)) + return NULL; + window( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void lookat float s float s float s float s float s float s short s */ + +static PyObject * +gl_lookat(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + float arg4 ; + float arg5 ; + float arg6 ; + short arg7 ; + if (!getifloatarg(args, 7, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 7, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 7, 2, &arg3)) + return NULL; + if (!getifloatarg(args, 7, 3, &arg4)) + return NULL; + if (!getifloatarg(args, 7, 4, &arg5)) + return NULL; + if (!getifloatarg(args, 7, 5, &arg6)) + return NULL; + if (!getishortarg(args, 7, 6, &arg7)) + return NULL; + lookat( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 , arg7 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void perspective short s float s float s float s */ + +static PyObject * +gl_perspective(PyObject *self, PyObject *args) +{ + short arg1 ; + float arg2 ; + float arg3 ; + float arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 4, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 4, 2, &arg3)) + return NULL; + if (!getifloatarg(args, 4, 3, &arg4)) + return NULL; + perspective( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void polarview float s short s short s short s */ + +static PyObject * +gl_polarview(PyObject *self, PyObject *args) +{ + float arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getifloatarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + polarview( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void arcfs short s short s short s short s short s */ + +static PyObject * +gl_arcfs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + short arg5 ; + if (!getishortarg(args, 5, 0, &arg1)) + return NULL; + if (!getishortarg(args, 5, 1, &arg2)) + return NULL; + if (!getishortarg(args, 5, 2, &arg3)) + return NULL; + if (!getishortarg(args, 5, 3, &arg4)) + return NULL; + if (!getishortarg(args, 5, 4, &arg5)) + return NULL; + arcfs( arg1 , arg2 , arg3 , arg4 , arg5 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void arcs short s short s short s short s short s */ + +static PyObject * +gl_arcs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + short arg5 ; + if (!getishortarg(args, 5, 0, &arg1)) + return NULL; + if (!getishortarg(args, 5, 1, &arg2)) + return NULL; + if (!getishortarg(args, 5, 2, &arg3)) + return NULL; + if (!getishortarg(args, 5, 3, &arg4)) + return NULL; + if (!getishortarg(args, 5, 4, &arg5)) + return NULL; + arcs( arg1 , arg2 , arg3 , arg4 , arg5 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rectcopy short s short s short s short s short s short s */ + +static PyObject * +gl_rectcopy(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + short arg5 ; + short arg6 ; + if (!getishortarg(args, 6, 0, &arg1)) + return NULL; + if (!getishortarg(args, 6, 1, &arg2)) + return NULL; + if (!getishortarg(args, 6, 2, &arg3)) + return NULL; + if (!getishortarg(args, 6, 3, &arg4)) + return NULL; + if (!getishortarg(args, 6, 4, &arg5)) + return NULL; + if (!getishortarg(args, 6, 5, &arg6)) + return NULL; + rectcopy( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void RGBcursor short s short s short s short s short s short s short s */ + +static PyObject * +gl_RGBcursor(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + short arg5 ; + short arg6 ; + short arg7 ; + if (!getishortarg(args, 7, 0, &arg1)) + return NULL; + if (!getishortarg(args, 7, 1, &arg2)) + return NULL; + if (!getishortarg(args, 7, 2, &arg3)) + return NULL; + if (!getishortarg(args, 7, 3, &arg4)) + return NULL; + if (!getishortarg(args, 7, 4, &arg5)) + return NULL; + if (!getishortarg(args, 7, 5, &arg6)) + return NULL; + if (!getishortarg(args, 7, 6, &arg7)) + return NULL; + RGBcursor( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 , arg7 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* long getbutton short s */ + +static PyObject * +gl_getbutton(PyObject *self, PyObject *args) +{ + long retval; + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + retval = getbutton( arg1 ); + return mknewlongobject(retval); +} + +/* long getcmmode */ + +static PyObject * +gl_getcmmode(PyObject *self, PyObject *args) +{ + long retval; + retval = getcmmode( ); + return mknewlongobject(retval); +} + +/* long getlsbackup */ + +static PyObject * +gl_getlsbackup(PyObject *self, PyObject *args) +{ + long retval; + retval = getlsbackup( ); + return mknewlongobject(retval); +} + +/* long getresetls */ + +static PyObject * +gl_getresetls(PyObject *self, PyObject *args) +{ + long retval; + retval = getresetls( ); + return mknewlongobject(retval); +} + +/* long getdcm */ + +static PyObject * +gl_getdcm(PyObject *self, PyObject *args) +{ + long retval; + retval = getdcm( ); + return mknewlongobject(retval); +} + +/* long getzbuffer */ + +static PyObject * +gl_getzbuffer(PyObject *self, PyObject *args) +{ + long retval; + retval = getzbuffer( ); + return mknewlongobject(retval); +} + +/* long ismex */ + +static PyObject * +gl_ismex(PyObject *self, PyObject *args) +{ + long retval; + retval = ismex( ); + return mknewlongobject(retval); +} + +/* long isobj long s */ + +static PyObject * +gl_isobj(PyObject *self, PyObject *args) +{ + long retval; + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + retval = isobj( arg1 ); + return mknewlongobject(retval); +} + +/* long isqueued short s */ + +static PyObject * +gl_isqueued(PyObject *self, PyObject *args) +{ + long retval; + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + retval = isqueued( arg1 ); + return mknewlongobject(retval); +} + +/* long istag long s */ + +static PyObject * +gl_istag(PyObject *self, PyObject *args) +{ + long retval; + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + retval = istag( arg1 ); + return mknewlongobject(retval); +} + +/* long genobj */ + +static PyObject * +gl_genobj(PyObject *self, PyObject *args) +{ + long retval; + retval = genobj( ); + return mknewlongobject(retval); +} + +/* long gentag */ + +static PyObject * +gl_gentag(PyObject *self, PyObject *args) +{ + long retval; + retval = gentag( ); + return mknewlongobject(retval); +} + +/* long getbuffer */ + +static PyObject * +gl_getbuffer(PyObject *self, PyObject *args) +{ + long retval; + retval = getbuffer( ); + return mknewlongobject(retval); +} + +/* long getcolor */ + +static PyObject * +gl_getcolor(PyObject *self, PyObject *args) +{ + long retval; + retval = getcolor( ); + return mknewlongobject(retval); +} + +/* long getdisplaymode */ + +static PyObject * +gl_getdisplaymode(PyObject *self, PyObject *args) +{ + long retval; + retval = getdisplaymode( ); + return mknewlongobject(retval); +} + +/* long getfont */ + +static PyObject * +gl_getfont(PyObject *self, PyObject *args) +{ + long retval; + retval = getfont( ); + return mknewlongobject(retval); +} + +/* long getheight */ + +static PyObject * +gl_getheight(PyObject *self, PyObject *args) +{ + long retval; + retval = getheight( ); + return mknewlongobject(retval); +} + +/* long gethitcode */ + +static PyObject * +gl_gethitcode(PyObject *self, PyObject *args) +{ + long retval; + retval = gethitcode( ); + return mknewlongobject(retval); +} + +/* long getlstyle */ + +static PyObject * +gl_getlstyle(PyObject *self, PyObject *args) +{ + long retval; + retval = getlstyle( ); + return mknewlongobject(retval); +} + +/* long getlwidth */ + +static PyObject * +gl_getlwidth(PyObject *self, PyObject *args) +{ + long retval; + retval = getlwidth( ); + return mknewlongobject(retval); +} + +/* long getmap */ + +static PyObject * +gl_getmap(PyObject *self, PyObject *args) +{ + long retval; + retval = getmap( ); + return mknewlongobject(retval); +} + +/* long getplanes */ + +static PyObject * +gl_getplanes(PyObject *self, PyObject *args) +{ + long retval; + retval = getplanes( ); + return mknewlongobject(retval); +} + +/* long getwritemask */ + +static PyObject * +gl_getwritemask(PyObject *self, PyObject *args) +{ + long retval; + retval = getwritemask( ); + return mknewlongobject(retval); +} + +/* long qtest */ + +static PyObject * +gl_qtest(PyObject *self, PyObject *args) +{ + long retval; + retval = qtest( ); + return mknewlongobject(retval); +} + +/* long getlsrepeat */ + +static PyObject * +gl_getlsrepeat(PyObject *self, PyObject *args) +{ + long retval; + retval = getlsrepeat( ); + return mknewlongobject(retval); +} + +/* long getmonitor */ + +static PyObject * +gl_getmonitor(PyObject *self, PyObject *args) +{ + long retval; + retval = getmonitor( ); + return mknewlongobject(retval); +} + +/* long getopenobj */ + +static PyObject * +gl_getopenobj(PyObject *self, PyObject *args) +{ + long retval; + retval = getopenobj( ); + return mknewlongobject(retval); +} + +/* long getpattern */ + +static PyObject * +gl_getpattern(PyObject *self, PyObject *args) +{ + long retval; + retval = getpattern( ); + return mknewlongobject(retval); +} + +/* long winget */ + +static PyObject * +gl_winget(PyObject *self, PyObject *args) +{ + long retval; + retval = winget( ); + return mknewlongobject(retval); +} + +/* long winattach */ + +static PyObject * +gl_winattach(PyObject *self, PyObject *args) +{ + long retval; + retval = winattach( ); + return mknewlongobject(retval); +} + +/* long getothermonitor */ + +static PyObject * +gl_getothermonitor(PyObject *self, PyObject *args) +{ + long retval; + retval = getothermonitor( ); + return mknewlongobject(retval); +} + +/* long newpup */ + +static PyObject * +gl_newpup(PyObject *self, PyObject *args) +{ + long retval; + retval = newpup( ); + return mknewlongobject(retval); +} + +/* long getvaluator short s */ + +static PyObject * +gl_getvaluator(PyObject *self, PyObject *args) +{ + long retval; + short arg1 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + retval = getvaluator( arg1 ); + return mknewlongobject(retval); +} + +/* void winset long s */ + +static PyObject * +gl_winset(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + winset( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* long dopup long s */ + +static PyObject * +gl_dopup(PyObject *self, PyObject *args) +{ + long retval; + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + retval = dopup( arg1 ); + return mknewlongobject(retval); +} + +/* void getdepth short r short r */ + +static PyObject * +gl_getdepth(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + getdepth( & arg1 , & arg2 ); + { PyObject *v = PyTuple_New( 2 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewshortobject(arg1)); + PyTuple_SetItem(v, 1, mknewshortobject(arg2)); + return v; + } +} + +/* void getcpos short r short r */ + +static PyObject * +gl_getcpos(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + getcpos( & arg1 , & arg2 ); + { PyObject *v = PyTuple_New( 2 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewshortobject(arg1)); + PyTuple_SetItem(v, 1, mknewshortobject(arg2)); + return v; + } +} + +/* void getsize long r long r */ + +static PyObject * +gl_getsize(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + getsize( & arg1 , & arg2 ); + { PyObject *v = PyTuple_New( 2 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewlongobject(arg1)); + PyTuple_SetItem(v, 1, mknewlongobject(arg2)); + return v; + } +} + +/* void getorigin long r long r */ + +static PyObject * +gl_getorigin(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + getorigin( & arg1 , & arg2 ); + { PyObject *v = PyTuple_New( 2 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewlongobject(arg1)); + PyTuple_SetItem(v, 1, mknewlongobject(arg2)); + return v; + } +} + +/* void getviewport short r short r short r short r */ + +static PyObject * +gl_getviewport(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + getviewport( & arg1 , & arg2 , & arg3 , & arg4 ); + { PyObject *v = PyTuple_New( 4 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewshortobject(arg1)); + PyTuple_SetItem(v, 1, mknewshortobject(arg2)); + PyTuple_SetItem(v, 2, mknewshortobject(arg3)); + PyTuple_SetItem(v, 3, mknewshortobject(arg4)); + return v; + } +} + +/* void gettp short r short r short r short r */ + +static PyObject * +gl_gettp(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + gettp( & arg1 , & arg2 , & arg3 , & arg4 ); + { PyObject *v = PyTuple_New( 4 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewshortobject(arg1)); + PyTuple_SetItem(v, 1, mknewshortobject(arg2)); + PyTuple_SetItem(v, 2, mknewshortobject(arg3)); + PyTuple_SetItem(v, 3, mknewshortobject(arg4)); + return v; + } +} + +/* void getgpos float r float r float r float r */ + +static PyObject * +gl_getgpos(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + float arg4 ; + getgpos( & arg1 , & arg2 , & arg3 , & arg4 ); + { PyObject *v = PyTuple_New( 4 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewfloatobject(arg1)); + PyTuple_SetItem(v, 1, mknewfloatobject(arg2)); + PyTuple_SetItem(v, 2, mknewfloatobject(arg3)); + PyTuple_SetItem(v, 3, mknewfloatobject(arg4)); + return v; + } +} + +/* void winposition long s long s long s long s */ + +static PyObject * +gl_winposition(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + long arg4 ; + if (!getilongarg(args, 4, 0, &arg1)) + return NULL; + if (!getilongarg(args, 4, 1, &arg2)) + return NULL; + if (!getilongarg(args, 4, 2, &arg3)) + return NULL; + if (!getilongarg(args, 4, 3, &arg4)) + return NULL; + winposition( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void gRGBcolor short r short r short r */ + +static PyObject * +gl_gRGBcolor(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + gRGBcolor( & arg1 , & arg2 , & arg3 ); + { PyObject *v = PyTuple_New( 3 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewshortobject(arg1)); + PyTuple_SetItem(v, 1, mknewshortobject(arg2)); + PyTuple_SetItem(v, 2, mknewshortobject(arg3)); + return v; + } +} + +/* void gRGBmask short r short r short r */ + +static PyObject * +gl_gRGBmask(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + gRGBmask( & arg1 , & arg2 , & arg3 ); + { PyObject *v = PyTuple_New( 3 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewshortobject(arg1)); + PyTuple_SetItem(v, 1, mknewshortobject(arg2)); + PyTuple_SetItem(v, 2, mknewshortobject(arg3)); + return v; + } +} + +/* void getscrmask short r short r short r short r */ + +static PyObject * +gl_getscrmask(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + getscrmask( & arg1 , & arg2 , & arg3 , & arg4 ); + { PyObject *v = PyTuple_New( 4 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewshortobject(arg1)); + PyTuple_SetItem(v, 1, mknewshortobject(arg2)); + PyTuple_SetItem(v, 2, mknewshortobject(arg3)); + PyTuple_SetItem(v, 3, mknewshortobject(arg4)); + return v; + } +} + +/* void getmcolor short s short r short r short r */ + +static PyObject * +gl_getmcolor(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 1, 0, &arg1)) + return NULL; + getmcolor( arg1 , & arg2 , & arg3 , & arg4 ); + { PyObject *v = PyTuple_New( 3 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewshortobject(arg2)); + PyTuple_SetItem(v, 1, mknewshortobject(arg3)); + PyTuple_SetItem(v, 2, mknewshortobject(arg4)); + return v; + } +} + +/* void mapw long s short s short s float r float r float r float r float r float r */ + +static PyObject * +gl_mapw(PyObject *self, PyObject *args) +{ + long arg1 ; + short arg2 ; + short arg3 ; + float arg4 ; + float arg5 ; + float arg6 ; + float arg7 ; + float arg8 ; + float arg9 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + mapw( arg1 , arg2 , arg3 , & arg4 , & arg5 , & arg6 , & arg7 , & arg8 , & arg9 ); + { PyObject *v = PyTuple_New( 6 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewfloatobject(arg4)); + PyTuple_SetItem(v, 1, mknewfloatobject(arg5)); + PyTuple_SetItem(v, 2, mknewfloatobject(arg6)); + PyTuple_SetItem(v, 3, mknewfloatobject(arg7)); + PyTuple_SetItem(v, 4, mknewfloatobject(arg8)); + PyTuple_SetItem(v, 5, mknewfloatobject(arg9)); + return v; + } +} + +/* void mapw2 long s short s short s float r float r */ + +static PyObject * +gl_mapw2(PyObject *self, PyObject *args) +{ + long arg1 ; + short arg2 ; + short arg3 ; + float arg4 ; + float arg5 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getishortarg(args, 3, 1, &arg2)) + return NULL; + if (!getishortarg(args, 3, 2, &arg3)) + return NULL; + mapw2( arg1 , arg2 , arg3 , & arg4 , & arg5 ); + { PyObject *v = PyTuple_New( 2 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewfloatobject(arg4)); + PyTuple_SetItem(v, 1, mknewfloatobject(arg5)); + return v; + } +} + +/* void getcursor short r u_short r u_short r long r */ + +static PyObject * +gl_getcursor(PyObject *self, PyObject *args) +{ + short arg1 ; + unsigned short arg2 ; + unsigned short arg3 ; + long arg4 ; + getcursor( & arg1 , & arg2 , & arg3 , & arg4 ); + { PyObject *v = PyTuple_New( 4 ); + if (v == NULL) return NULL; + PyTuple_SetItem(v, 0, mknewshortobject(arg1)); + PyTuple_SetItem(v, 1, mknewshortobject((short) arg2)); + PyTuple_SetItem(v, 2, mknewshortobject((short) arg3)); + PyTuple_SetItem(v, 3, mknewlongobject(arg4)); + return v; + } +} + +/* void cmode */ + +static PyObject * +gl_cmode(PyObject *self, PyObject *args) +{ + cmode( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void concave long s */ + +static PyObject * +gl_concave(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + concave( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void curstype long s */ + +static PyObject * +gl_curstype(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + curstype( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void drawmode long s */ + +static PyObject * +gl_drawmode(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + drawmode( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void gammaramp short s[256] short s[256] short s[256] */ + +static PyObject * +gl_gammaramp(PyObject *self, PyObject *args) +{ + short arg1 [ 256 ] ; + short arg2 [ 256 ] ; + short arg3 [ 256 ] ; + if (!getishortarray(args, 3, 0, 256 , arg1)) + return NULL; + if (!getishortarray(args, 3, 1, 256 , arg2)) + return NULL; + if (!getishortarray(args, 3, 2, 256 , arg3)) + return NULL; + gammaramp( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* long getbackface */ + +static PyObject * +gl_getbackface(PyObject *self, PyObject *args) +{ + long retval; + retval = getbackface( ); + return mknewlongobject(retval); +} + +/* long getdescender */ + +static PyObject * +gl_getdescender(PyObject *self, PyObject *args) +{ + long retval; + retval = getdescender( ); + return mknewlongobject(retval); +} + +/* long getdrawmode */ + +static PyObject * +gl_getdrawmode(PyObject *self, PyObject *args) +{ + long retval; + retval = getdrawmode( ); + return mknewlongobject(retval); +} + +/* long getmmode */ + +static PyObject * +gl_getmmode(PyObject *self, PyObject *args) +{ + long retval; + retval = getmmode( ); + return mknewlongobject(retval); +} + +/* long getsm */ + +static PyObject * +gl_getsm(PyObject *self, PyObject *args) +{ + long retval; + retval = getsm( ); + return mknewlongobject(retval); +} + +/* long getvideo long s */ + +static PyObject * +gl_getvideo(PyObject *self, PyObject *args) +{ + long retval; + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + retval = getvideo( arg1 ); + return mknewlongobject(retval); +} + +/* void imakebackground */ + +static PyObject * +gl_imakebackground(PyObject *self, PyObject *args) +{ + imakebackground( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void lmbind short s short s */ + +static PyObject * +gl_lmbind(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + if (!getishortarg(args, 2, 0, &arg1)) + return NULL; + if (!getishortarg(args, 2, 1, &arg2)) + return NULL; + lmbind( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void lmdef long s long s long s float s[arg3] */ + +static PyObject * +gl_lmdef(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + float * arg4 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarraysize(args, 3, 2, &arg3)) + return NULL; + if ((arg4 = PyMem_NEW(float , arg3 )) == NULL) + return PyErr_NoMemory(); + if (!getifloatarray(args, 3, 2, arg3 , arg4)) + return NULL; + lmdef( arg1 , arg2 , arg3 , arg4 ); + PyMem_DEL(arg4); + Py_INCREF(Py_None); + return Py_None; +} + +/* void mmode long s */ + +static PyObject * +gl_mmode(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + mmode( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void normal float s[3] */ + +static PyObject * +gl_normal(PyObject *self, PyObject *args) +{ + float arg1 [ 3 ] ; + if (!getifloatarray(args, 1, 0, 3 , arg1)) + return NULL; + normal( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void overlay long s */ + +static PyObject * +gl_overlay(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + overlay( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void RGBrange short s short s short s short s short s short s short s short s */ + +static PyObject * +gl_RGBrange(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + short arg5 ; + short arg6 ; + short arg7 ; + short arg8 ; + if (!getishortarg(args, 8, 0, &arg1)) + return NULL; + if (!getishortarg(args, 8, 1, &arg2)) + return NULL; + if (!getishortarg(args, 8, 2, &arg3)) + return NULL; + if (!getishortarg(args, 8, 3, &arg4)) + return NULL; + if (!getishortarg(args, 8, 4, &arg5)) + return NULL; + if (!getishortarg(args, 8, 5, &arg6)) + return NULL; + if (!getishortarg(args, 8, 6, &arg7)) + return NULL; + if (!getishortarg(args, 8, 7, &arg8)) + return NULL; + RGBrange( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 , arg7 , arg8 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setvideo long s long s */ + +static PyObject * +gl_setvideo(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + setvideo( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void shademodel long s */ + +static PyObject * +gl_shademodel(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + shademodel( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void underlay long s */ + +static PyObject * +gl_underlay(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + underlay( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void bgnclosedline */ + +static PyObject * +gl_bgnclosedline(PyObject *self, PyObject *args) +{ + bgnclosedline( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void bgnline */ + +static PyObject * +gl_bgnline(PyObject *self, PyObject *args) +{ + bgnline( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void bgnpoint */ + +static PyObject * +gl_bgnpoint(PyObject *self, PyObject *args) +{ + bgnpoint( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void bgnpolygon */ + +static PyObject * +gl_bgnpolygon(PyObject *self, PyObject *args) +{ + bgnpolygon( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void bgnsurface */ + +static PyObject * +gl_bgnsurface(PyObject *self, PyObject *args) +{ + bgnsurface( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void bgntmesh */ + +static PyObject * +gl_bgntmesh(PyObject *self, PyObject *args) +{ + bgntmesh( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void bgntrim */ + +static PyObject * +gl_bgntrim(PyObject *self, PyObject *args) +{ + bgntrim( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void endclosedline */ + +static PyObject * +gl_endclosedline(PyObject *self, PyObject *args) +{ + endclosedline( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void endline */ + +static PyObject * +gl_endline(PyObject *self, PyObject *args) +{ + endline( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void endpoint */ + +static PyObject * +gl_endpoint(PyObject *self, PyObject *args) +{ + endpoint( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void endpolygon */ + +static PyObject * +gl_endpolygon(PyObject *self, PyObject *args) +{ + endpolygon( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void endsurface */ + +static PyObject * +gl_endsurface(PyObject *self, PyObject *args) +{ + endsurface( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void endtmesh */ + +static PyObject * +gl_endtmesh(PyObject *self, PyObject *args) +{ + endtmesh( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void endtrim */ + +static PyObject * +gl_endtrim(PyObject *self, PyObject *args) +{ + endtrim( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void blendfunction long s long s */ + +static PyObject * +gl_blendfunction(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + blendfunction( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void c3f float s[3] */ + +static PyObject * +gl_c3f(PyObject *self, PyObject *args) +{ + float arg1 [ 3 ] ; + if (!getifloatarray(args, 1, 0, 3 , arg1)) + return NULL; + c3f( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void c3i long s[3] */ + +static PyObject * +gl_c3i(PyObject *self, PyObject *args) +{ + long arg1 [ 3 ] ; + if (!getilongarray(args, 1, 0, 3 , arg1)) + return NULL; + c3i( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void c3s short s[3] */ + +static PyObject * +gl_c3s(PyObject *self, PyObject *args) +{ + short arg1 [ 3 ] ; + if (!getishortarray(args, 1, 0, 3 , arg1)) + return NULL; + c3s( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void c4f float s[4] */ + +static PyObject * +gl_c4f(PyObject *self, PyObject *args) +{ + float arg1 [ 4 ] ; + if (!getifloatarray(args, 1, 0, 4 , arg1)) + return NULL; + c4f( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void c4i long s[4] */ + +static PyObject * +gl_c4i(PyObject *self, PyObject *args) +{ + long arg1 [ 4 ] ; + if (!getilongarray(args, 1, 0, 4 , arg1)) + return NULL; + c4i( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void c4s short s[4] */ + +static PyObject * +gl_c4s(PyObject *self, PyObject *args) +{ + short arg1 [ 4 ] ; + if (!getishortarray(args, 1, 0, 4 , arg1)) + return NULL; + c4s( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void colorf float s */ + +static PyObject * +gl_colorf(PyObject *self, PyObject *args) +{ + float arg1 ; + if (!getifloatarg(args, 1, 0, &arg1)) + return NULL; + colorf( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void cpack long s */ + +static PyObject * +gl_cpack(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + cpack( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void czclear long s long s */ + +static PyObject * +gl_czclear(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + czclear( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void dglclose long s */ + +static PyObject * +gl_dglclose(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + dglclose( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* long dglopen char *s long s */ + +static PyObject * +gl_dglopen(PyObject *self, PyObject *args) +{ + long retval; + string arg1 ; + long arg2 ; + if (!getistringarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + retval = dglopen( arg1 , arg2 ); + return mknewlongobject(retval); +} + +/* long getgdesc long s */ + +static PyObject * +gl_getgdesc(PyObject *self, PyObject *args) +{ + long retval; + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + retval = getgdesc( arg1 ); + return mknewlongobject(retval); +} + +/* void getnurbsproperty long s float r */ + +static PyObject * +gl_getnurbsproperty(PyObject *self, PyObject *args) +{ + long arg1 ; + float arg2 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + getnurbsproperty( arg1 , & arg2 ); + return mknewfloatobject(arg2); +} + +/* void glcompat long s long s */ + +static PyObject * +gl_glcompat(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + glcompat( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void iconsize long s long s */ + +static PyObject * +gl_iconsize(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + iconsize( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void icontitle char *s */ + +static PyObject * +gl_icontitle(PyObject *self, PyObject *args) +{ + string arg1 ; + if (!getistringarg(args, 1, 0, &arg1)) + return NULL; + icontitle( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void lRGBrange short s short s short s short s short s short s long s long s */ + +static PyObject * +gl_lRGBrange(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + short arg5 ; + short arg6 ; + long arg7 ; + long arg8 ; + if (!getishortarg(args, 8, 0, &arg1)) + return NULL; + if (!getishortarg(args, 8, 1, &arg2)) + return NULL; + if (!getishortarg(args, 8, 2, &arg3)) + return NULL; + if (!getishortarg(args, 8, 3, &arg4)) + return NULL; + if (!getishortarg(args, 8, 4, &arg5)) + return NULL; + if (!getishortarg(args, 8, 5, &arg6)) + return NULL; + if (!getilongarg(args, 8, 6, &arg7)) + return NULL; + if (!getilongarg(args, 8, 7, &arg8)) + return NULL; + lRGBrange( arg1 , arg2 , arg3 , arg4 , arg5 , arg6 , arg7 , arg8 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void linesmooth long s */ + +static PyObject * +gl_linesmooth(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + linesmooth( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void lmcolor long s */ + +static PyObject * +gl_lmcolor(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + lmcolor( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void logicop long s */ + +static PyObject * +gl_logicop(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + logicop( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void lsetdepth long s long s */ + +static PyObject * +gl_lsetdepth(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + lsetdepth( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void lshaderange short s short s long s long s */ + +static PyObject * +gl_lshaderange(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + long arg3 ; + long arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getilongarg(args, 4, 2, &arg3)) + return NULL; + if (!getilongarg(args, 4, 3, &arg4)) + return NULL; + lshaderange( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void n3f float s[3] */ + +static PyObject * +gl_n3f(PyObject *self, PyObject *args) +{ + float arg1 [ 3 ] ; + if (!getifloatarray(args, 1, 0, 3 , arg1)) + return NULL; + n3f( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void noborder */ + +static PyObject * +gl_noborder(PyObject *self, PyObject *args) +{ + noborder( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pntsmooth long s */ + +static PyObject * +gl_pntsmooth(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + pntsmooth( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void readsource long s */ + +static PyObject * +gl_readsource(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + readsource( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void rectzoom float s float s */ + +static PyObject * +gl_rectzoom(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + if (!getifloatarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + rectzoom( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void sbox float s float s float s float s */ + +static PyObject * +gl_sbox(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + float arg4 ; + if (!getifloatarg(args, 4, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 4, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 4, 2, &arg3)) + return NULL; + if (!getifloatarg(args, 4, 3, &arg4)) + return NULL; + sbox( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void sboxi long s long s long s long s */ + +static PyObject * +gl_sboxi(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + long arg4 ; + if (!getilongarg(args, 4, 0, &arg1)) + return NULL; + if (!getilongarg(args, 4, 1, &arg2)) + return NULL; + if (!getilongarg(args, 4, 2, &arg3)) + return NULL; + if (!getilongarg(args, 4, 3, &arg4)) + return NULL; + sboxi( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void sboxs short s short s short s short s */ + +static PyObject * +gl_sboxs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + sboxs( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void sboxf float s float s float s float s */ + +static PyObject * +gl_sboxf(PyObject *self, PyObject *args) +{ + float arg1 ; + float arg2 ; + float arg3 ; + float arg4 ; + if (!getifloatarg(args, 4, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 4, 1, &arg2)) + return NULL; + if (!getifloatarg(args, 4, 2, &arg3)) + return NULL; + if (!getifloatarg(args, 4, 3, &arg4)) + return NULL; + sboxf( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void sboxfi long s long s long s long s */ + +static PyObject * +gl_sboxfi(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + long arg4 ; + if (!getilongarg(args, 4, 0, &arg1)) + return NULL; + if (!getilongarg(args, 4, 1, &arg2)) + return NULL; + if (!getilongarg(args, 4, 2, &arg3)) + return NULL; + if (!getilongarg(args, 4, 3, &arg4)) + return NULL; + sboxfi( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void sboxfs short s short s short s short s */ + +static PyObject * +gl_sboxfs(PyObject *self, PyObject *args) +{ + short arg1 ; + short arg2 ; + short arg3 ; + short arg4 ; + if (!getishortarg(args, 4, 0, &arg1)) + return NULL; + if (!getishortarg(args, 4, 1, &arg2)) + return NULL; + if (!getishortarg(args, 4, 2, &arg3)) + return NULL; + if (!getishortarg(args, 4, 3, &arg4)) + return NULL; + sboxfs( arg1 , arg2 , arg3 , arg4 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setnurbsproperty long s float s */ + +static PyObject * +gl_setnurbsproperty(PyObject *self, PyObject *args) +{ + long arg1 ; + float arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getifloatarg(args, 2, 1, &arg2)) + return NULL; + setnurbsproperty( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void setpup long s long s long s */ + +static PyObject * +gl_setpup(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + long arg3 ; + if (!getilongarg(args, 3, 0, &arg1)) + return NULL; + if (!getilongarg(args, 3, 1, &arg2)) + return NULL; + if (!getilongarg(args, 3, 2, &arg3)) + return NULL; + setpup( arg1 , arg2 , arg3 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void smoothline long s */ + +static PyObject * +gl_smoothline(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + smoothline( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void subpixel long s */ + +static PyObject * +gl_subpixel(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + subpixel( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void swaptmesh */ + +static PyObject * +gl_swaptmesh(PyObject *self, PyObject *args) +{ + swaptmesh( ); + Py_INCREF(Py_None); + return Py_None; +} + +/* long swinopen long s */ + +static PyObject * +gl_swinopen(PyObject *self, PyObject *args) +{ + long retval; + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + retval = swinopen( arg1 ); + return mknewlongobject(retval); +} + +/* void v2f float s[2] */ + +static PyObject * +gl_v2f(PyObject *self, PyObject *args) +{ + float arg1 [ 2 ] ; + if (!getifloatarray(args, 1, 0, 2 , arg1)) + return NULL; + v2f( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void v2i long s[2] */ + +static PyObject * +gl_v2i(PyObject *self, PyObject *args) +{ + long arg1 [ 2 ] ; + if (!getilongarray(args, 1, 0, 2 , arg1)) + return NULL; + v2i( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void v2s short s[2] */ + +static PyObject * +gl_v2s(PyObject *self, PyObject *args) +{ + short arg1 [ 2 ] ; + if (!getishortarray(args, 1, 0, 2 , arg1)) + return NULL; + v2s( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void v3f float s[3] */ + +static PyObject * +gl_v3f(PyObject *self, PyObject *args) +{ + float arg1 [ 3 ] ; + if (!getifloatarray(args, 1, 0, 3 , arg1)) + return NULL; + v3f( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void v3i long s[3] */ + +static PyObject * +gl_v3i(PyObject *self, PyObject *args) +{ + long arg1 [ 3 ] ; + if (!getilongarray(args, 1, 0, 3 , arg1)) + return NULL; + v3i( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void v3s short s[3] */ + +static PyObject * +gl_v3s(PyObject *self, PyObject *args) +{ + short arg1 [ 3 ] ; + if (!getishortarray(args, 1, 0, 3 , arg1)) + return NULL; + v3s( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void v4f float s[4] */ + +static PyObject * +gl_v4f(PyObject *self, PyObject *args) +{ + float arg1 [ 4 ] ; + if (!getifloatarray(args, 1, 0, 4 , arg1)) + return NULL; + v4f( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void v4i long s[4] */ + +static PyObject * +gl_v4i(PyObject *self, PyObject *args) +{ + long arg1 [ 4 ] ; + if (!getilongarray(args, 1, 0, 4 , arg1)) + return NULL; + v4i( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void v4s short s[4] */ + +static PyObject * +gl_v4s(PyObject *self, PyObject *args) +{ + short arg1 [ 4 ] ; + if (!getishortarray(args, 1, 0, 4 , arg1)) + return NULL; + v4s( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void videocmd long s */ + +static PyObject * +gl_videocmd(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + videocmd( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* long windepth long s */ + +static PyObject * +gl_windepth(PyObject *self, PyObject *args) +{ + long retval; + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + retval = windepth( arg1 ); + return mknewlongobject(retval); +} + +/* void wmpack long s */ + +static PyObject * +gl_wmpack(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + wmpack( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void zdraw long s */ + +static PyObject * +gl_zdraw(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + zdraw( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void zfunction long s */ + +static PyObject * +gl_zfunction(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + zfunction( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void zsource long s */ + +static PyObject * +gl_zsource(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + zsource( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void zwritemask long s */ + +static PyObject * +gl_zwritemask(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + zwritemask( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void v2d double s[2] */ + +static PyObject * +gl_v2d(PyObject *self, PyObject *args) +{ + double arg1 [ 2 ] ; + if (!getidoublearray(args, 1, 0, 2 , arg1)) + return NULL; + v2d( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void v3d double s[3] */ + +static PyObject * +gl_v3d(PyObject *self, PyObject *args) +{ + double arg1 [ 3 ] ; + if (!getidoublearray(args, 1, 0, 3 , arg1)) + return NULL; + v3d( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void v4d double s[4] */ + +static PyObject * +gl_v4d(PyObject *self, PyObject *args) +{ + double arg1 [ 4 ] ; + if (!getidoublearray(args, 1, 0, 4 , arg1)) + return NULL; + v4d( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* void pixmode long s long s */ + +static PyObject * +gl_pixmode(PyObject *self, PyObject *args) +{ + long arg1 ; + long arg2 ; + if (!getilongarg(args, 2, 0, &arg1)) + return NULL; + if (!getilongarg(args, 2, 1, &arg2)) + return NULL; + pixmode( arg1 , arg2 ); + Py_INCREF(Py_None); + return Py_None; +} + +/* long qgetfd */ + +static PyObject * +gl_qgetfd(PyObject *self, PyObject *args) +{ + long retval; + retval = qgetfd( ); + return mknewlongobject(retval); +} + +/* void dither long s */ + +static PyObject * +gl_dither(PyObject *self, PyObject *args) +{ + long arg1 ; + if (!getilongarg(args, 1, 0, &arg1)) + return NULL; + dither( arg1 ); + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef gl_methods[] = { + {"qread", gl_qread, METH_OLDARGS}, + {"varray", gl_varray, METH_OLDARGS}, + {"nvarray", gl_nvarray, METH_OLDARGS}, + {"vnarray", gl_vnarray, METH_OLDARGS}, + {"nurbssurface", gl_nurbssurface, METH_OLDARGS}, + {"nurbscurve", gl_nurbscurve, METH_OLDARGS}, + {"pwlcurve", gl_pwlcurve, METH_OLDARGS}, + {"pick", gl_pick, METH_OLDARGS}, + {"endpick", gl_endpick, METH_NOARGS}, + {"gselect", gl_gselect, METH_OLDARGS}, + {"endselect", gl_endselect, METH_NOARGS}, + {"getmatrix", gl_getmatrix, METH_OLDARGS}, + {"altgetmatrix", gl_altgetmatrix, METH_OLDARGS}, + {"lrectwrite", gl_lrectwrite, METH_OLDARGS}, + {"lrectread", gl_lrectread, METH_OLDARGS}, + {"readdisplay", gl_readdisplay, METH_OLDARGS}, + {"packrect", gl_packrect, METH_OLDARGS}, + {"unpackrect", gl_unpackrect, METH_OLDARGS}, + {"gversion", gl_gversion, METH_OLDARGS}, + {"clear", gl_clear, METH_OLDARGS}, + {"getshade", gl_getshade, METH_OLDARGS}, + {"devport", gl_devport, METH_OLDARGS}, + {"rdr2i", gl_rdr2i, METH_OLDARGS}, + {"rectfs", gl_rectfs, METH_OLDARGS}, + {"rects", gl_rects, METH_OLDARGS}, + {"rmv2i", gl_rmv2i, METH_OLDARGS}, + {"noport", gl_noport, METH_OLDARGS}, + {"popviewport", gl_popviewport, METH_OLDARGS}, + {"clearhitcode", gl_clearhitcode, METH_OLDARGS}, + {"closeobj", gl_closeobj, METH_OLDARGS}, + {"cursoff", gl_cursoff, METH_OLDARGS}, + {"curson", gl_curson, METH_OLDARGS}, + {"doublebuffer", gl_doublebuffer, METH_OLDARGS}, + {"finish", gl_finish, METH_OLDARGS}, + {"gconfig", gl_gconfig, METH_OLDARGS}, + {"ginit", gl_ginit, METH_OLDARGS}, + {"greset", gl_greset, METH_OLDARGS}, + {"multimap", gl_multimap, METH_OLDARGS}, + {"onemap", gl_onemap, METH_OLDARGS}, + {"popattributes", gl_popattributes, METH_OLDARGS}, + {"popmatrix", gl_popmatrix, METH_OLDARGS}, + {"pushattributes", gl_pushattributes,METH_OLDARGS}, + {"pushmatrix", gl_pushmatrix, METH_OLDARGS}, + {"pushviewport", gl_pushviewport, METH_OLDARGS}, + {"qreset", gl_qreset, METH_OLDARGS}, + {"RGBmode", gl_RGBmode, METH_OLDARGS}, + {"singlebuffer", gl_singlebuffer, METH_OLDARGS}, + {"swapbuffers", gl_swapbuffers, METH_OLDARGS}, + {"gsync", gl_gsync, METH_OLDARGS}, + {"gflush", gl_gflush, METH_OLDARGS}, + {"tpon", gl_tpon, METH_OLDARGS}, + {"tpoff", gl_tpoff, METH_OLDARGS}, + {"clkon", gl_clkon, METH_OLDARGS}, + {"clkoff", gl_clkoff, METH_OLDARGS}, + {"ringbell", gl_ringbell, METH_OLDARGS}, + {"gbegin", gl_gbegin, METH_OLDARGS}, + {"textinit", gl_textinit, METH_OLDARGS}, + {"initnames", gl_initnames, METH_OLDARGS}, + {"pclos", gl_pclos, METH_OLDARGS}, + {"popname", gl_popname, METH_OLDARGS}, + {"spclos", gl_spclos, METH_OLDARGS}, + {"zclear", gl_zclear, METH_OLDARGS}, + {"screenspace", gl_screenspace, METH_OLDARGS}, + {"reshapeviewport", gl_reshapeviewport, METH_OLDARGS}, + {"winpush", gl_winpush, METH_OLDARGS}, + {"winpop", gl_winpop, METH_OLDARGS}, + {"foreground", gl_foreground, METH_OLDARGS}, + {"endfullscrn", gl_endfullscrn, METH_OLDARGS}, + {"endpupmode", gl_endpupmode, METH_OLDARGS}, + {"fullscrn", gl_fullscrn, METH_OLDARGS}, + {"pupmode", gl_pupmode, METH_OLDARGS}, + {"winconstraints", gl_winconstraints, METH_OLDARGS}, + {"pagecolor", gl_pagecolor, METH_OLDARGS}, + {"textcolor", gl_textcolor, METH_OLDARGS}, + {"color", gl_color, METH_OLDARGS}, + {"curveit", gl_curveit, METH_OLDARGS}, + {"font", gl_font, METH_OLDARGS}, + {"linewidth", gl_linewidth, METH_OLDARGS}, + {"setlinestyle", gl_setlinestyle, METH_OLDARGS}, + {"setmap", gl_setmap, METH_OLDARGS}, + {"swapinterval", gl_swapinterval, METH_OLDARGS}, + {"writemask", gl_writemask, METH_OLDARGS}, + {"textwritemask", gl_textwritemask, METH_OLDARGS}, + {"qdevice", gl_qdevice, METH_OLDARGS}, + {"unqdevice", gl_unqdevice, METH_OLDARGS}, + {"curvebasis", gl_curvebasis, METH_OLDARGS}, + {"curveprecision", gl_curveprecision,METH_OLDARGS}, + {"loadname", gl_loadname, METH_OLDARGS}, + {"passthrough", gl_passthrough, METH_OLDARGS}, + {"pushname", gl_pushname, METH_OLDARGS}, + {"setmonitor", gl_setmonitor, METH_OLDARGS}, + {"setshade", gl_setshade, METH_OLDARGS}, + {"setpattern", gl_setpattern, METH_OLDARGS}, + {"pagewritemask", gl_pagewritemask, METH_OLDARGS}, + {"callobj", gl_callobj, METH_OLDARGS}, + {"delobj", gl_delobj, METH_OLDARGS}, + {"editobj", gl_editobj, METH_OLDARGS}, + {"makeobj", gl_makeobj, METH_OLDARGS}, + {"maketag", gl_maketag, METH_OLDARGS}, + {"chunksize", gl_chunksize, METH_OLDARGS}, + {"compactify", gl_compactify, METH_OLDARGS}, + {"deltag", gl_deltag, METH_OLDARGS}, + {"lsrepeat", gl_lsrepeat, METH_OLDARGS}, + {"objinsert", gl_objinsert, METH_OLDARGS}, + {"objreplace", gl_objreplace, METH_OLDARGS}, + {"winclose", gl_winclose, METH_OLDARGS}, + {"blanktime", gl_blanktime, METH_OLDARGS}, + {"freepup", gl_freepup, METH_OLDARGS}, + {"backbuffer", gl_backbuffer, METH_OLDARGS}, + {"frontbuffer", gl_frontbuffer, METH_OLDARGS}, + {"lsbackup", gl_lsbackup, METH_OLDARGS}, + {"resetls", gl_resetls, METH_OLDARGS}, + {"lampon", gl_lampon, METH_OLDARGS}, + {"lampoff", gl_lampoff, METH_OLDARGS}, + {"setbell", gl_setbell, METH_OLDARGS}, + {"blankscreen", gl_blankscreen, METH_OLDARGS}, + {"depthcue", gl_depthcue, METH_OLDARGS}, + {"zbuffer", gl_zbuffer, METH_OLDARGS}, + {"backface", gl_backface, METH_OLDARGS}, + {"cmov2i", gl_cmov2i, METH_OLDARGS}, + {"draw2i", gl_draw2i, METH_OLDARGS}, + {"move2i", gl_move2i, METH_OLDARGS}, + {"pnt2i", gl_pnt2i, METH_OLDARGS}, + {"patchbasis", gl_patchbasis, METH_OLDARGS}, + {"patchprecision", gl_patchprecision, METH_OLDARGS}, + {"pdr2i", gl_pdr2i, METH_OLDARGS}, + {"pmv2i", gl_pmv2i, METH_OLDARGS}, + {"rpdr2i", gl_rpdr2i, METH_OLDARGS}, + {"rpmv2i", gl_rpmv2i, METH_OLDARGS}, + {"xfpt2i", gl_xfpt2i, METH_OLDARGS}, + {"objdelete", gl_objdelete, METH_OLDARGS}, + {"patchcurves", gl_patchcurves, METH_OLDARGS}, + {"minsize", gl_minsize, METH_OLDARGS}, + {"maxsize", gl_maxsize, METH_OLDARGS}, + {"keepaspect", gl_keepaspect, METH_OLDARGS}, + {"prefsize", gl_prefsize, METH_OLDARGS}, + {"stepunit", gl_stepunit, METH_OLDARGS}, + {"fudge", gl_fudge, METH_OLDARGS}, + {"winmove", gl_winmove, METH_OLDARGS}, + {"attachcursor", gl_attachcursor, METH_OLDARGS}, + {"deflinestyle", gl_deflinestyle, METH_OLDARGS}, + {"noise", gl_noise, METH_OLDARGS}, + {"picksize", gl_picksize, METH_OLDARGS}, + {"qenter", gl_qenter, METH_OLDARGS}, + {"setdepth", gl_setdepth, METH_OLDARGS}, + {"cmov2s", gl_cmov2s, METH_OLDARGS}, + {"draw2s", gl_draw2s, METH_OLDARGS}, + {"move2s", gl_move2s, METH_OLDARGS}, + {"pdr2s", gl_pdr2s, METH_OLDARGS}, + {"pmv2s", gl_pmv2s, METH_OLDARGS}, + {"pnt2s", gl_pnt2s, METH_OLDARGS}, + {"rdr2s", gl_rdr2s, METH_OLDARGS}, + {"rmv2s", gl_rmv2s, METH_OLDARGS}, + {"rpdr2s", gl_rpdr2s, METH_OLDARGS}, + {"rpmv2s", gl_rpmv2s, METH_OLDARGS}, + {"xfpt2s", gl_xfpt2s, METH_OLDARGS}, + {"cmov2", gl_cmov2, METH_OLDARGS}, + {"draw2", gl_draw2, METH_OLDARGS}, + {"move2", gl_move2, METH_OLDARGS}, + {"pnt2", gl_pnt2, METH_OLDARGS}, + {"pdr2", gl_pdr2, METH_OLDARGS}, + {"pmv2", gl_pmv2, METH_OLDARGS}, + {"rdr2", gl_rdr2, METH_OLDARGS}, + {"rmv2", gl_rmv2, METH_OLDARGS}, + {"rpdr2", gl_rpdr2, METH_OLDARGS}, + {"rpmv2", gl_rpmv2, METH_OLDARGS}, + {"xfpt2", gl_xfpt2, METH_OLDARGS}, + {"loadmatrix", gl_loadmatrix, METH_OLDARGS}, + {"multmatrix", gl_multmatrix, METH_OLDARGS}, + {"crv", gl_crv, METH_OLDARGS}, + {"rcrv", gl_rcrv, METH_OLDARGS}, + {"addtopup", gl_addtopup, METH_OLDARGS}, + {"charstr", gl_charstr, METH_OLDARGS}, + {"getport", gl_getport, METH_OLDARGS}, + {"strwidth", gl_strwidth, METH_OLDARGS}, + {"winopen", gl_winopen, METH_OLDARGS}, + {"wintitle", gl_wintitle, METH_OLDARGS}, + {"polf", gl_polf, METH_OLDARGS}, + {"polf2", gl_polf2, METH_OLDARGS}, + {"poly", gl_poly, METH_OLDARGS}, + {"poly2", gl_poly2, METH_OLDARGS}, + {"crvn", gl_crvn, METH_OLDARGS}, + {"rcrvn", gl_rcrvn, METH_OLDARGS}, + {"polf2i", gl_polf2i, METH_OLDARGS}, + {"polfi", gl_polfi, METH_OLDARGS}, + {"poly2i", gl_poly2i, METH_OLDARGS}, + {"polyi", gl_polyi, METH_OLDARGS}, + {"polf2s", gl_polf2s, METH_OLDARGS}, + {"polfs", gl_polfs, METH_OLDARGS}, + {"polys", gl_polys, METH_OLDARGS}, + {"poly2s", gl_poly2s, METH_OLDARGS}, + {"defcursor", gl_defcursor, METH_OLDARGS}, + {"writepixels", gl_writepixels, METH_OLDARGS}, + {"defbasis", gl_defbasis, METH_OLDARGS}, + {"gewrite", gl_gewrite, METH_OLDARGS}, + {"rotate", gl_rotate, METH_OLDARGS}, + {"rot", gl_rot, METH_OLDARGS}, + {"circfi", gl_circfi, METH_OLDARGS}, + {"circi", gl_circi, METH_OLDARGS}, + {"cmovi", gl_cmovi, METH_OLDARGS}, + {"drawi", gl_drawi, METH_OLDARGS}, + {"movei", gl_movei, METH_OLDARGS}, + {"pnti", gl_pnti, METH_OLDARGS}, + {"newtag", gl_newtag, METH_OLDARGS}, + {"pdri", gl_pdri, METH_OLDARGS}, + {"pmvi", gl_pmvi, METH_OLDARGS}, + {"rdri", gl_rdri, METH_OLDARGS}, + {"rmvi", gl_rmvi, METH_OLDARGS}, + {"rpdri", gl_rpdri, METH_OLDARGS}, + {"rpmvi", gl_rpmvi, METH_OLDARGS}, + {"xfpti", gl_xfpti, METH_OLDARGS}, + {"circ", gl_circ, METH_OLDARGS}, + {"circf", gl_circf, METH_OLDARGS}, + {"cmov", gl_cmov, METH_OLDARGS}, + {"draw", gl_draw, METH_OLDARGS}, + {"move", gl_move, METH_OLDARGS}, + {"pnt", gl_pnt, METH_OLDARGS}, + {"scale", gl_scale, METH_OLDARGS}, + {"translate", gl_translate, METH_OLDARGS}, + {"pdr", gl_pdr, METH_OLDARGS}, + {"pmv", gl_pmv, METH_OLDARGS}, + {"rdr", gl_rdr, METH_OLDARGS}, + {"rmv", gl_rmv, METH_OLDARGS}, + {"rpdr", gl_rpdr, METH_OLDARGS}, + {"rpmv", gl_rpmv, METH_OLDARGS}, + {"xfpt", gl_xfpt, METH_OLDARGS}, + {"RGBcolor", gl_RGBcolor, METH_OLDARGS}, + {"RGBwritemask", gl_RGBwritemask, METH_OLDARGS}, + {"setcursor", gl_setcursor, METH_OLDARGS}, + {"tie", gl_tie, METH_OLDARGS}, + {"circfs", gl_circfs, METH_OLDARGS}, + {"circs", gl_circs, METH_OLDARGS}, + {"cmovs", gl_cmovs, METH_OLDARGS}, + {"draws", gl_draws, METH_OLDARGS}, + {"moves", gl_moves, METH_OLDARGS}, + {"pdrs", gl_pdrs, METH_OLDARGS}, + {"pmvs", gl_pmvs, METH_OLDARGS}, + {"pnts", gl_pnts, METH_OLDARGS}, + {"rdrs", gl_rdrs, METH_OLDARGS}, + {"rmvs", gl_rmvs, METH_OLDARGS}, + {"rpdrs", gl_rpdrs, METH_OLDARGS}, + {"rpmvs", gl_rpmvs, METH_OLDARGS}, + {"xfpts", gl_xfpts, METH_OLDARGS}, + {"curorigin", gl_curorigin, METH_OLDARGS}, + {"cyclemap", gl_cyclemap, METH_OLDARGS}, + {"patch", gl_patch, METH_OLDARGS}, + {"splf", gl_splf, METH_OLDARGS}, + {"splf2", gl_splf2, METH_OLDARGS}, + {"splfi", gl_splfi, METH_OLDARGS}, + {"splf2i", gl_splf2i, METH_OLDARGS}, + {"splfs", gl_splfs, METH_OLDARGS}, + {"splf2s", gl_splf2s, METH_OLDARGS}, + {"rpatch", gl_rpatch, METH_OLDARGS}, + {"ortho2", gl_ortho2, METH_OLDARGS}, + {"rect", gl_rect, METH_OLDARGS}, + {"rectf", gl_rectf, METH_OLDARGS}, + {"xfpt4", gl_xfpt4, METH_OLDARGS}, + {"textport", gl_textport, METH_OLDARGS}, + {"mapcolor", gl_mapcolor, METH_OLDARGS}, + {"scrmask", gl_scrmask, METH_OLDARGS}, + {"setvaluator", gl_setvaluator, METH_OLDARGS}, + {"viewport", gl_viewport, METH_OLDARGS}, + {"shaderange", gl_shaderange, METH_OLDARGS}, + {"xfpt4s", gl_xfpt4s, METH_OLDARGS}, + {"rectfi", gl_rectfi, METH_OLDARGS}, + {"recti", gl_recti, METH_OLDARGS}, + {"xfpt4i", gl_xfpt4i, METH_OLDARGS}, + {"prefposition", gl_prefposition, METH_OLDARGS}, + {"arc", gl_arc, METH_OLDARGS}, + {"arcf", gl_arcf, METH_OLDARGS}, + {"arcfi", gl_arcfi, METH_OLDARGS}, + {"arci", gl_arci, METH_OLDARGS}, + {"bbox2", gl_bbox2, METH_OLDARGS}, + {"bbox2i", gl_bbox2i, METH_OLDARGS}, + {"bbox2s", gl_bbox2s, METH_OLDARGS}, + {"blink", gl_blink, METH_OLDARGS}, + {"ortho", gl_ortho, METH_OLDARGS}, + {"window", gl_window, METH_OLDARGS}, + {"lookat", gl_lookat, METH_OLDARGS}, + {"perspective", gl_perspective, METH_OLDARGS}, + {"polarview", gl_polarview, METH_OLDARGS}, + {"arcfs", gl_arcfs, METH_OLDARGS}, + {"arcs", gl_arcs, METH_OLDARGS}, + {"rectcopy", gl_rectcopy, METH_OLDARGS}, + {"RGBcursor", gl_RGBcursor, METH_OLDARGS}, + {"getbutton", gl_getbutton, METH_OLDARGS}, + {"getcmmode", gl_getcmmode, METH_OLDARGS}, + {"getlsbackup", gl_getlsbackup, METH_OLDARGS}, + {"getresetls", gl_getresetls, METH_OLDARGS}, + {"getdcm", gl_getdcm, METH_OLDARGS}, + {"getzbuffer", gl_getzbuffer, METH_OLDARGS}, + {"ismex", gl_ismex, METH_OLDARGS}, + {"isobj", gl_isobj, METH_OLDARGS}, + {"isqueued", gl_isqueued, METH_OLDARGS}, + {"istag", gl_istag, METH_OLDARGS}, + {"genobj", gl_genobj, METH_OLDARGS}, + {"gentag", gl_gentag, METH_OLDARGS}, + {"getbuffer", gl_getbuffer, METH_OLDARGS}, + {"getcolor", gl_getcolor, METH_OLDARGS}, + {"getdisplaymode", gl_getdisplaymode, METH_OLDARGS}, + {"getfont", gl_getfont, METH_OLDARGS}, + {"getheight", gl_getheight, METH_OLDARGS}, + {"gethitcode", gl_gethitcode, METH_OLDARGS}, + {"getlstyle", gl_getlstyle, METH_OLDARGS}, + {"getlwidth", gl_getlwidth, METH_OLDARGS}, + {"getmap", gl_getmap, METH_OLDARGS}, + {"getplanes", gl_getplanes, METH_OLDARGS}, + {"getwritemask", gl_getwritemask, METH_OLDARGS}, + {"qtest", gl_qtest, METH_OLDARGS}, + {"getlsrepeat", gl_getlsrepeat, METH_OLDARGS}, + {"getmonitor", gl_getmonitor, METH_OLDARGS}, + {"getopenobj", gl_getopenobj, METH_OLDARGS}, + {"getpattern", gl_getpattern, METH_OLDARGS}, + {"winget", gl_winget, METH_OLDARGS}, + {"winattach", gl_winattach, METH_OLDARGS}, + {"getothermonitor", gl_getothermonitor, METH_OLDARGS}, + {"newpup", gl_newpup, METH_OLDARGS}, + {"getvaluator", gl_getvaluator, METH_OLDARGS}, + {"winset", gl_winset, METH_OLDARGS}, + {"dopup", gl_dopup, METH_OLDARGS}, + {"getdepth", gl_getdepth, METH_OLDARGS}, + {"getcpos", gl_getcpos, METH_OLDARGS}, + {"getsize", gl_getsize, METH_OLDARGS}, + {"getorigin", gl_getorigin, METH_OLDARGS}, + {"getviewport", gl_getviewport, METH_OLDARGS}, + {"gettp", gl_gettp, METH_OLDARGS}, + {"getgpos", gl_getgpos, METH_OLDARGS}, + {"winposition", gl_winposition, METH_OLDARGS}, + {"gRGBcolor", gl_gRGBcolor, METH_OLDARGS}, + {"gRGBmask", gl_gRGBmask, METH_OLDARGS}, + {"getscrmask", gl_getscrmask, METH_OLDARGS}, + {"getmcolor", gl_getmcolor, METH_OLDARGS}, + {"mapw", gl_mapw, METH_OLDARGS}, + {"mapw2", gl_mapw2, METH_OLDARGS}, + {"getcursor", gl_getcursor, METH_OLDARGS}, + {"cmode", gl_cmode, METH_OLDARGS}, + {"concave", gl_concave, METH_OLDARGS}, + {"curstype", gl_curstype, METH_OLDARGS}, + {"drawmode", gl_drawmode, METH_OLDARGS}, + {"gammaramp", gl_gammaramp, METH_OLDARGS}, + {"getbackface", gl_getbackface, METH_OLDARGS}, + {"getdescender", gl_getdescender, METH_OLDARGS}, + {"getdrawmode", gl_getdrawmode, METH_OLDARGS}, + {"getmmode", gl_getmmode, METH_OLDARGS}, + {"getsm", gl_getsm, METH_OLDARGS}, + {"getvideo", gl_getvideo, METH_OLDARGS}, + {"imakebackground", gl_imakebackground, METH_OLDARGS}, + {"lmbind", gl_lmbind, METH_OLDARGS}, + {"lmdef", gl_lmdef, METH_OLDARGS}, + {"mmode", gl_mmode, METH_OLDARGS}, + {"normal", gl_normal, METH_OLDARGS}, + {"overlay", gl_overlay, METH_OLDARGS}, + {"RGBrange", gl_RGBrange, METH_OLDARGS}, + {"setvideo", gl_setvideo, METH_OLDARGS}, + {"shademodel", gl_shademodel, METH_OLDARGS}, + {"underlay", gl_underlay, METH_OLDARGS}, + {"bgnclosedline", gl_bgnclosedline, METH_OLDARGS}, + {"bgnline", gl_bgnline, METH_OLDARGS}, + {"bgnpoint", gl_bgnpoint, METH_OLDARGS}, + {"bgnpolygon", gl_bgnpolygon, METH_OLDARGS}, + {"bgnsurface", gl_bgnsurface, METH_OLDARGS}, + {"bgntmesh", gl_bgntmesh, METH_OLDARGS}, + {"bgntrim", gl_bgntrim, METH_OLDARGS}, + {"endclosedline", gl_endclosedline, METH_OLDARGS}, + {"endline", gl_endline, METH_OLDARGS}, + {"endpoint", gl_endpoint, METH_OLDARGS}, + {"endpolygon", gl_endpolygon, METH_OLDARGS}, + {"endsurface", gl_endsurface, METH_OLDARGS}, + {"endtmesh", gl_endtmesh, METH_OLDARGS}, + {"endtrim", gl_endtrim, METH_OLDARGS}, + {"blendfunction", gl_blendfunction, METH_OLDARGS}, + {"c3f", gl_c3f, METH_OLDARGS}, + {"c3i", gl_c3i, METH_OLDARGS}, + {"c3s", gl_c3s, METH_OLDARGS}, + {"c4f", gl_c4f, METH_OLDARGS}, + {"c4i", gl_c4i, METH_OLDARGS}, + {"c4s", gl_c4s, METH_OLDARGS}, + {"colorf", gl_colorf, METH_OLDARGS}, + {"cpack", gl_cpack, METH_OLDARGS}, + {"czclear", gl_czclear, METH_OLDARGS}, + {"dglclose", gl_dglclose, METH_OLDARGS}, + {"dglopen", gl_dglopen, METH_OLDARGS}, + {"getgdesc", gl_getgdesc, METH_OLDARGS}, + {"getnurbsproperty", gl_getnurbsproperty, METH_OLDARGS}, + {"glcompat", gl_glcompat, METH_OLDARGS}, + {"iconsize", gl_iconsize, METH_OLDARGS}, + {"icontitle", gl_icontitle, METH_OLDARGS}, + {"lRGBrange", gl_lRGBrange, METH_OLDARGS}, + {"linesmooth", gl_linesmooth, METH_OLDARGS}, + {"lmcolor", gl_lmcolor, METH_OLDARGS}, + {"logicop", gl_logicop, METH_OLDARGS}, + {"lsetdepth", gl_lsetdepth, METH_OLDARGS}, + {"lshaderange", gl_lshaderange, METH_OLDARGS}, + {"n3f", gl_n3f, METH_OLDARGS}, + {"noborder", gl_noborder, METH_OLDARGS}, + {"pntsmooth", gl_pntsmooth, METH_OLDARGS}, + {"readsource", gl_readsource, METH_OLDARGS}, + {"rectzoom", gl_rectzoom, METH_OLDARGS}, + {"sbox", gl_sbox, METH_OLDARGS}, + {"sboxi", gl_sboxi, METH_OLDARGS}, + {"sboxs", gl_sboxs, METH_OLDARGS}, + {"sboxf", gl_sboxf, METH_OLDARGS}, + {"sboxfi", gl_sboxfi, METH_OLDARGS}, + {"sboxfs", gl_sboxfs, METH_OLDARGS}, + {"setnurbsproperty", gl_setnurbsproperty, METH_OLDARGS}, + {"setpup", gl_setpup, METH_OLDARGS}, + {"smoothline", gl_smoothline, METH_OLDARGS}, + {"subpixel", gl_subpixel, METH_OLDARGS}, + {"swaptmesh", gl_swaptmesh, METH_OLDARGS}, + {"swinopen", gl_swinopen, METH_OLDARGS}, + {"v2f", gl_v2f, METH_OLDARGS}, + {"v2i", gl_v2i, METH_OLDARGS}, + {"v2s", gl_v2s, METH_OLDARGS}, + {"v3f", gl_v3f, METH_OLDARGS}, + {"v3i", gl_v3i, METH_OLDARGS}, + {"v3s", gl_v3s, METH_OLDARGS}, + {"v4f", gl_v4f, METH_OLDARGS}, + {"v4i", gl_v4i, METH_OLDARGS}, + {"v4s", gl_v4s, METH_OLDARGS}, + {"videocmd", gl_videocmd, METH_OLDARGS}, + {"windepth", gl_windepth, METH_OLDARGS}, + {"wmpack", gl_wmpack, METH_OLDARGS}, + {"zdraw", gl_zdraw, METH_OLDARGS}, + {"zfunction", gl_zfunction, METH_OLDARGS}, + {"zsource", gl_zsource, METH_OLDARGS}, + {"zwritemask", gl_zwritemask, METH_OLDARGS}, + {"v2d", gl_v2d, METH_OLDARGS}, + {"v3d", gl_v3d, METH_OLDARGS}, + {"v4d", gl_v4d, METH_OLDARGS}, + {"pixmode", gl_pixmode, METH_OLDARGS}, + {"qgetfd", gl_qgetfd, METH_OLDARGS}, + {"dither", gl_dither, METH_OLDARGS}, + {NULL, NULL} /* Sentinel */ +}; + +void +initgl(void) +{ + (void) Py_InitModule("gl", gl_methods); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/grpmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/grpmodule.c new file mode 100644 index 00000000..576b8c68 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/grpmodule.c @@ -0,0 +1,177 @@ + +/* UNIX group file access module */ + +#include "Python.h" +#include "structseq.h" + +#include +#include + +static PyStructSequence_Field struct_group_type_fields[] = { + {"gr_name", "group name"}, + {"gr_passwd", "password"}, + {"gr_gid", "group id"}, + {"gr_mem", "group memebers"}, + {0} +}; + +PyDoc_STRVAR(struct_group__doc__, +"grp.struct_group: Results from getgr*() routines.\n\n\ +This object may be accessed either as a tuple of\n\ + (gr_name,gr_passwd,gr_gid,gr_mem)\n\ +or via the object attributes as named in the above tuple.\n"); + +static PyStructSequence_Desc struct_group_type_desc = { + "grp.struct_group", + struct_group__doc__, + struct_group_type_fields, + 4, +}; + + +static PyTypeObject StructGrpType; + +static PyObject * +mkgrent(struct group *p) +{ + int setIndex = 0; + PyObject *v = PyStructSequence_New(&StructGrpType), *w; + char **member; + + if (v == NULL) + return NULL; + + if ((w = PyList_New(0)) == NULL) { + Py_DECREF(v); + return NULL; + } + for (member = p->gr_mem; *member != NULL; member++) { + PyObject *x = PyString_FromString(*member); + if (x == NULL || PyList_Append(w, x) != 0) { + Py_XDECREF(x); + Py_DECREF(w); + Py_DECREF(v); + return NULL; + } + Py_DECREF(x); + } + +#define SET(i,val) PyStructSequence_SET_ITEM(v, i, val) + SET(setIndex++, PyString_FromString(p->gr_name)); +#ifdef __VMS + SET(setIndex++, Py_None); + Py_INCREF(Py_None); +#else + if (p->gr_passwd) + SET(setIndex++, PyString_FromString(p->gr_passwd)); + else { + SET(setIndex++, Py_None); + Py_INCREF(Py_None); + } +#endif + SET(setIndex++, PyInt_FromLong((long) p->gr_gid)); + SET(setIndex++, w); +#undef SET + + if (PyErr_Occurred()) { + Py_DECREF(v); + Py_DECREF(w); + return NULL; + } + + return v; +} + +static PyObject * +grp_getgrgid(PyObject *self, PyObject *args) +{ + int gid; + struct group *p; + if (!PyArg_ParseTuple(args, "i:getgrgid", &gid)) + return NULL; + if ((p = getgrgid(gid)) == NULL) { + PyErr_SetString(PyExc_KeyError, "getgrgid(): gid not found"); + return NULL; + } + return mkgrent(p); +} + +static PyObject * +grp_getgrnam(PyObject *self, PyObject *args) +{ + char *name; + struct group *p; + if (!PyArg_ParseTuple(args, "s:getgrnam", &name)) + return NULL; + if ((p = getgrnam(name)) == NULL) { + PyErr_SetString(PyExc_KeyError, "getgrnam(): name not found"); + return NULL; + } + return mkgrent(p); +} + +static PyObject * +grp_getgrall(PyObject *self, PyObject *args) +{ + PyObject *d; + struct group *p; + + if (!PyArg_ParseTuple(args, ":getgrall")) + return NULL; + if ((d = PyList_New(0)) == NULL) + return NULL; + setgrent(); + while ((p = getgrent()) != NULL) { + PyObject *v = mkgrent(p); + if (v == NULL || PyList_Append(d, v) != 0) { + Py_XDECREF(v); + Py_DECREF(d); + return NULL; + } + Py_DECREF(v); + } + endgrent(); + return d; +} + +static PyMethodDef grp_methods[] = { + {"getgrgid", grp_getgrgid, METH_VARARGS, + "getgrgid(id) -> tuple\n\ +Return the group database entry for the given numeric group ID. If\n\ +id is not valid, raise KeyError."}, + {"getgrnam", grp_getgrnam, METH_VARARGS, + "getgrnam(name) -> tuple\n\ +Return the group database entry for the given group name. If\n\ +name is not valid, raise KeyError."}, + {"getgrall", grp_getgrall, METH_VARARGS, + "getgrall() -> list of tuples\n\ +Return a list of all available group entries, in arbitrary order."}, + {NULL, NULL} /* sentinel */ +}; + +PyDoc_STRVAR(grp__doc__, +"Access to the Unix group database.\n\ +\n\ +Group entries are reported as 4-tuples containing the following fields\n\ +from the group database, in order:\n\ +\n\ + name - name of the group\n\ + passwd - group password (encrypted); often empty\n\ + gid - numeric ID of the group\n\ + mem - list of members\n\ +\n\ +The gid is an integer, name and password are strings. (Note that most\n\ +users are not explicitly listed as members of the groups they are in\n\ +according to the password database. Check both databases to get\n\ +complete membership information.)"); + + +PyMODINIT_FUNC +initgrp(void) +{ + PyObject *m, *d; + m = Py_InitModule3("grp", grp_methods, grp__doc__); + d = PyModule_GetDict(m); + PyStructSequence_InitType(&StructGrpType, &struct_group_type_desc); + PyDict_SetItemString(d, "struct_group", (PyObject *) &StructGrpType); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/imageop.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/imageop.c new file mode 100644 index 00000000..dd103ee4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/imageop.c @@ -0,0 +1,708 @@ + +/* imageopmodule - Various operations on pictures */ + +#ifdef sun +#define signed +#endif + +#include "Python.h" + +#if SIZEOF_INT == 4 +typedef int Py_Int32; +typedef unsigned int Py_UInt32; +#else +#if SIZEOF_LONG == 4 +typedef long Py_Int32; +typedef unsigned long Py_UInt32; +#else +#error "No 4-byte integral type" +#endif +#endif + +#define CHARP(cp, xmax, x, y) ((char *)(cp+y*xmax+x)) +#define SHORTP(cp, xmax, x, y) ((short *)(cp+2*(y*xmax+x))) +#define LONGP(cp, xmax, x, y) ((Py_Int32 *)(cp+4*(y*xmax+x))) + +static PyObject *ImageopError; + +static PyObject * +imageop_crop(PyObject *self, PyObject *args) +{ + char *cp, *ncp; + short *nsp; + Py_Int32 *nlp; + int len, size, x, y, newx1, newx2, newy1, newy2; + int ix, iy, xstep, ystep; + PyObject *rv; + + if ( !PyArg_ParseTuple(args, "s#iiiiiii", &cp, &len, &size, &x, &y, + &newx1, &newy1, &newx2, &newy2) ) + return 0; + + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(ImageopError, "Size should be 1, 2 or 4"); + return 0; + } + if ( len != size*x*y ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + xstep = (newx1 < newx2)? 1 : -1; + ystep = (newy1 < newy2)? 1 : -1; + + rv = PyString_FromStringAndSize(NULL, + (abs(newx2-newx1)+1)*(abs(newy2-newy1)+1)*size); + if ( rv == 0 ) + return 0; + ncp = (char *)PyString_AsString(rv); + nsp = (short *)ncp; + nlp = (Py_Int32 *)ncp; + newy2 += ystep; + newx2 += xstep; + for( iy = newy1; iy != newy2; iy+=ystep ) { + for ( ix = newx1; ix != newx2; ix+=xstep ) { + if ( iy < 0 || iy >= y || ix < 0 || ix >= x ) { + if ( size == 1 ) + *ncp++ = 0; + else + *nlp++ = 0; + } else { + if ( size == 1 ) + *ncp++ = *CHARP(cp, x, ix, iy); + else if ( size == 2 ) + *nsp++ = *SHORTP(cp, x, ix, iy); + else + *nlp++ = *LONGP(cp, x, ix, iy); + } + } + } + return rv; +} + +static PyObject * +imageop_scale(PyObject *self, PyObject *args) +{ + char *cp, *ncp; + short *nsp; + Py_Int32 *nlp; + int len, size, x, y, newx, newy; + int ix, iy; + int oix, oiy; + PyObject *rv; + + if ( !PyArg_ParseTuple(args, "s#iiiii", + &cp, &len, &size, &x, &y, &newx, &newy) ) + return 0; + + if ( size != 1 && size != 2 && size != 4 ) { + PyErr_SetString(ImageopError, "Size should be 1, 2 or 4"); + return 0; + } + if ( len != size*x*y ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, newx*newy*size); + if ( rv == 0 ) + return 0; + ncp = (char *)PyString_AsString(rv); + nsp = (short *)ncp; + nlp = (Py_Int32 *)ncp; + for( iy = 0; iy < newy; iy++ ) { + for ( ix = 0; ix < newx; ix++ ) { + oix = ix * x / newx; + oiy = iy * y / newy; + if ( size == 1 ) + *ncp++ = *CHARP(cp, x, oix, oiy); + else if ( size == 2 ) + *nsp++ = *SHORTP(cp, x, oix, oiy); + else + *nlp++ = *LONGP(cp, x, oix, oiy); + } + } + return rv; +} + +/* Note: this routine can use a bit of optimizing */ + +static PyObject * +imageop_tovideo(PyObject *self, PyObject *args) +{ + int maxx, maxy, x, y, len; + int i; + unsigned char *cp, *ncp; + int width; + PyObject *rv; + + + if ( !PyArg_ParseTuple(args, "s#iii", &cp, &len, &width, &maxx, &maxy) ) + return 0; + + if ( width != 1 && width != 4 ) { + PyErr_SetString(ImageopError, "Size should be 1 or 4"); + return 0; + } + if ( maxx*maxy*width != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, len); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + if ( width == 1 ) { + memcpy(ncp, cp, maxx); /* Copy first line */ + ncp += maxx; + for (y=1; y> 1; + } + } + } else { + memcpy(ncp, cp, maxx*4); /* Copy first line */ + ncp += maxx*4; + for (y=1; y> 1; + i++; + *ncp++ = ((int)cp[i] + (int)cp[i-4*maxx]) >> 1; + i++; + *ncp++ = ((int)cp[i] + (int)cp[i-4*maxx]) >> 1; + } + } + } + return rv; +} + +static PyObject * +imageop_grey2mono(PyObject *self, PyObject *args) +{ + int tres, x, y, len; + unsigned char *cp, *ncp; + unsigned char ovalue; + PyObject *rv; + int i, bit; + + + if ( !PyArg_ParseTuple(args, "s#iii", &cp, &len, &x, &y, &tres) ) + return 0; + + if ( x*y != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, (len+7)/8); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + bit = 0x80; + ovalue = 0; + for ( i=0; i < len; i++ ) { + if ( (int)cp[i] > tres ) + ovalue |= bit; + bit >>= 1; + if ( bit == 0 ) { + *ncp++ = ovalue; + bit = 0x80; + ovalue = 0; + } + } + if ( bit != 0x80 ) + *ncp++ = ovalue; + return rv; +} + +static PyObject * +imageop_grey2grey4(PyObject *self, PyObject *args) +{ + int x, y, len; + unsigned char *cp, *ncp; + unsigned char ovalue; + PyObject *rv; + int i; + int pos; + + + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + + if ( x*y != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, (len+1)/2); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + pos = 0; + ovalue = 0; + for ( i=0; i < len; i++ ) { + ovalue |= ((int)cp[i] & 0xf0) >> pos; + pos += 4; + if ( pos == 8 ) { + *ncp++ = ovalue; + ovalue = 0; + pos = 0; + } + } + if ( pos != 0 ) + *ncp++ = ovalue; + return rv; +} + +static PyObject * +imageop_grey2grey2(PyObject *self, PyObject *args) +{ + int x, y, len; + unsigned char *cp, *ncp; + unsigned char ovalue; + PyObject *rv; + int i; + int pos; + + + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + + if ( x*y != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, (len+3)/4); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + pos = 0; + ovalue = 0; + for ( i=0; i < len; i++ ) { + ovalue |= ((int)cp[i] & 0xc0) >> pos; + pos += 2; + if ( pos == 8 ) { + *ncp++ = ovalue; + ovalue = 0; + pos = 0; + } + } + if ( pos != 0 ) + *ncp++ = ovalue; + return rv; +} + +static PyObject * +imageop_dither2mono(PyObject *self, PyObject *args) +{ + int sum, x, y, len; + unsigned char *cp, *ncp; + unsigned char ovalue; + PyObject *rv; + int i, bit; + + + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + + if ( x*y != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, (len+7)/8); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + bit = 0x80; + ovalue = 0; + sum = 0; + for ( i=0; i < len; i++ ) { + sum += cp[i]; + if ( sum >= 256 ) { + sum -= 256; + ovalue |= bit; + } + bit >>= 1; + if ( bit == 0 ) { + *ncp++ = ovalue; + bit = 0x80; + ovalue = 0; + } + } + if ( bit != 0x80 ) + *ncp++ = ovalue; + return rv; +} + +static PyObject * +imageop_dither2grey2(PyObject *self, PyObject *args) +{ + int x, y, len; + unsigned char *cp, *ncp; + unsigned char ovalue; + PyObject *rv; + int i; + int pos; + int sum = 0, nvalue; + + + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + + if ( x*y != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, (len+3)/4); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + pos = 1; + ovalue = 0; + for ( i=0; i < len; i++ ) { + sum += cp[i]; + nvalue = sum & 0x180; + sum -= nvalue; + ovalue |= nvalue >> pos; + pos += 2; + if ( pos == 9 ) { + *ncp++ = ovalue; + ovalue = 0; + pos = 1; + } + } + if ( pos != 0 ) + *ncp++ = ovalue; + return rv; +} + +static PyObject * +imageop_mono2grey(PyObject *self, PyObject *args) +{ + int v0, v1, x, y, len, nlen; + unsigned char *cp, *ncp; + PyObject *rv; + int i, bit; + + if ( !PyArg_ParseTuple(args, "s#iiii", &cp, &len, &x, &y, &v0, &v1) ) + return 0; + + nlen = x*y; + if ( (nlen+7)/8 != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, nlen); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + bit = 0x80; + for ( i=0; i < nlen; i++ ) { + if ( *cp & bit ) + *ncp++ = v1; + else + *ncp++ = v0; + bit >>= 1; + if ( bit == 0 ) { + bit = 0x80; + cp++; + } + } + return rv; +} + +static PyObject * +imageop_grey22grey(PyObject *self, PyObject *args) +{ + int x, y, len, nlen; + unsigned char *cp, *ncp; + PyObject *rv; + int i, pos, value = 0, nvalue; + + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + + nlen = x*y; + if ( (nlen+3)/4 != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, nlen); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + pos = 0; + for ( i=0; i < nlen; i++ ) { + if ( pos == 0 ) { + value = *cp++; + pos = 8; + } + pos -= 2; + nvalue = (value >> pos) & 0x03; + *ncp++ = nvalue | (nvalue << 2) | + (nvalue << 4) | (nvalue << 6); + } + return rv; +} + +static PyObject * +imageop_grey42grey(PyObject *self, PyObject *args) +{ + int x, y, len, nlen; + unsigned char *cp, *ncp; + PyObject *rv; + int i, pos, value = 0, nvalue; + + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + + nlen = x*y; + if ( (nlen+1)/2 != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, nlen); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + pos = 0; + for ( i=0; i < nlen; i++ ) { + if ( pos == 0 ) { + value = *cp++; + pos = 8; + } + pos -= 4; + nvalue = (value >> pos) & 0x0f; + *ncp++ = nvalue | (nvalue << 4); + } + return rv; +} + +static PyObject * +imageop_rgb2rgb8(PyObject *self, PyObject *args) +{ + int x, y, len, nlen; + Py_UInt32 *cp; + unsigned char *ncp; + PyObject *rv; + int i, r, g, b; + Py_UInt32 value, nvalue; + + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + + nlen = x*y; + if ( nlen*4 != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, nlen); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + for ( i=0; i < nlen; i++ ) { + /* Bits in source: aaaaaaaa BBbbbbbb GGGggggg RRRrrrrr */ + value = *cp++; +#if 0 + r = (value >> 5) & 7; + g = (value >> 13) & 7; + b = (value >> 22) & 3; +#else + r = (int) ((value & 0xff) / 255. * 7. + .5); + g = (int) (((value >> 8) & 0xff) / 255. * 7. + .5); + b = (int) (((value >> 16) & 0xff) / 255. * 3. + .5); +#endif + nvalue = (r<<5) | (b<<3) | g; + *ncp++ = (unsigned char)nvalue; + } + return rv; +} + +static PyObject * +imageop_rgb82rgb(PyObject *self, PyObject *args) +{ + int x, y, len, nlen; + unsigned char *cp; + Py_UInt32 *ncp; + PyObject *rv; + int i, r, g, b; + Py_UInt32 value, nvalue; + + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + + nlen = x*y; + if ( nlen != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, nlen*4); + if ( rv == 0 ) + return 0; + ncp = (Py_UInt32 *)PyString_AsString(rv); + + for ( i=0; i < nlen; i++ ) { + /* Bits in source: RRRBBGGG + ** Red and Green are multiplied by 36.5, Blue by 85 + */ + value = *cp++; + r = (value >> 5) & 7; + g = (value ) & 7; + b = (value >> 3) & 3; + r = (r<<5) | (r<<3) | (r>>1); + g = (g<<5) | (g<<3) | (g>>1); + b = (b<<6) | (b<<4) | (b<<2) | b; + nvalue = r | (g<<8) | (b<<16); + *ncp++ = nvalue; + } + return rv; +} + +static PyObject * +imageop_rgb2grey(PyObject *self, PyObject *args) +{ + int x, y, len, nlen; + Py_UInt32 *cp; + unsigned char *ncp; + PyObject *rv; + int i, r, g, b; + Py_UInt32 value, nvalue; + + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + + nlen = x*y; + if ( nlen*4 != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, nlen); + if ( rv == 0 ) + return 0; + ncp = (unsigned char *)PyString_AsString(rv); + + for ( i=0; i < nlen; i++ ) { + value = *cp++; + r = (value ) & 0xff; + g = (value >> 8) & 0xff; + b = (value >> 16) & 0xff; + nvalue = (int)(0.30*r + 0.59*g + 0.11*b); + if ( nvalue > 255 ) nvalue = 255; + *ncp++ = (unsigned char)nvalue; + } + return rv; +} + +static PyObject * +imageop_grey2rgb(PyObject *self, PyObject *args) +{ + int x, y, len, nlen; + unsigned char *cp; + Py_UInt32 *ncp; + PyObject *rv; + int i; + Py_UInt32 value; + + if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) ) + return 0; + + nlen = x*y; + if ( nlen != len ) { + PyErr_SetString(ImageopError, "String has incorrect length"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, nlen*4); + if ( rv == 0 ) + return 0; + ncp = (Py_UInt32 *)PyString_AsString(rv); + + for ( i=0; i < nlen; i++ ) { + value = *cp++; + *ncp++ = value | (value << 8 ) | (value << 16); + } + return rv; +} + +/* +static object * +imageop_mul(object *self, object *args) +{ + char *cp, *ncp; + int len, size, x, y; + object *rv; + int i; + + if ( !getargs(args, "(s#iii)", &cp, &len, &size, &x, &y) ) + return 0; + + if ( size != 1 && size != 4 ) { + err_setstr(ImageopError, "Size should be 1 or 4"); + return 0; + } + if ( len != size*x*y ) { + err_setstr(ImageopError, "String has incorrect length"); + return 0; + } + + rv = newsizedstringobject(NULL, XXXX); + if ( rv == 0 ) + return 0; + ncp = (char *)getstringvalue(rv); + + + for ( i=0; i < len; i += size ) { + } + return rv; +} +*/ + +static PyMethodDef imageop_methods[] = { + { "crop", imageop_crop, METH_VARARGS }, + { "scale", imageop_scale, METH_VARARGS }, + { "grey2mono", imageop_grey2mono, METH_VARARGS }, + { "grey2grey2", imageop_grey2grey2, METH_VARARGS }, + { "grey2grey4", imageop_grey2grey4, METH_VARARGS }, + { "dither2mono", imageop_dither2mono, METH_VARARGS }, + { "dither2grey2", imageop_dither2grey2, METH_VARARGS }, + { "mono2grey", imageop_mono2grey, METH_VARARGS }, + { "grey22grey", imageop_grey22grey, METH_VARARGS }, + { "grey42grey", imageop_grey42grey, METH_VARARGS }, + { "tovideo", imageop_tovideo, METH_VARARGS }, + { "rgb2rgb8", imageop_rgb2rgb8, METH_VARARGS }, + { "rgb82rgb", imageop_rgb82rgb, METH_VARARGS }, + { "rgb2grey", imageop_rgb2grey, METH_VARARGS }, + { "grey2rgb", imageop_grey2rgb, METH_VARARGS }, + { 0, 0 } +}; + + +PyMODINIT_FUNC +initimageop(void) +{ + PyObject *m, *d; + m = Py_InitModule("imageop", imageop_methods); + d = PyModule_GetDict(m); + ImageopError = PyErr_NewException("imageop.error", NULL, NULL); + if (ImageopError != NULL) + PyDict_SetItemString(d, "error", ImageopError); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/imgfile.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/imgfile.c new file mode 100644 index 00000000..d68af005 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/imgfile.c @@ -0,0 +1,502 @@ + +/* IMGFILE module - Interface to sgi libimage */ + +/* XXX This module should be done better at some point. It should return +** an object of image file class, and have routines to manipulate these +** image files in a neater way (so you can get rgb images off a greyscale +** file, for instance, or do a straight display without having to get the +** image bits into python, etc). +** +** Warning: this module is very non-reentrant (esp. the readscaled stuff) +*/ + +#include "Python.h" + +#include + +#include "/usr/people/4Dgifts/iristools/include/izoom.h" + +/* Bunch of missing extern decls; keep gcc -Wall happy... */ +extern void i_seterror(); +extern void iclose(); +extern void filterzoom(); +extern void putrow(); +extern void getrow(); + +static PyObject * ImgfileError; /* Exception we raise for various trouble */ + +static int top_to_bottom; /* True if we want top-to-bottom images */ + +/* The image library does not always call the error hander :-(, + therefore we have a global variable indicating that it was called. + It is cleared by imgfile_open(). */ + +static int error_called; + + +/* The error handler */ + +static void +imgfile_error(char *str) +{ + PyErr_SetString(ImgfileError, str); + error_called = 1; + return; /* To imglib, which will return a failure indicator */ +} + + +/* Open an image file and return a pointer to it. + Make sure we raise an exception if we fail. */ + +static IMAGE * +imgfile_open(char *fname) +{ + IMAGE *image; + i_seterror(imgfile_error); + error_called = 0; + errno = 0; + if ( (image = iopen(fname, "r")) == NULL ) { + /* Error may already be set by imgfile_error */ + if ( !error_called ) { + if (errno) + PyErr_SetFromErrno(ImgfileError); + else + PyErr_SetString(ImgfileError, + "Can't open image file"); + } + return NULL; + } + return image; +} + +static PyObject * +imgfile_ttob(PyObject *self, PyObject *args) +{ + int newval; + PyObject *rv; + + if (!PyArg_ParseTuple(args, "i:ttob", &newval)) + return NULL; + rv = PyInt_FromLong(top_to_bottom); + top_to_bottom = newval; + return rv; +} + +static PyObject * +imgfile_read(PyObject *self, PyObject *args) +{ + char *fname; + PyObject *rv; + int xsize, ysize, zsize; + char *cdatap; + long *idatap; + static short rs[8192], gs[8192], bs[8192]; + int x, y; + IMAGE *image; + int yfirst, ylast, ystep; + + if ( !PyArg_ParseTuple(args, "s:read", &fname) ) + return NULL; + + if ( (image = imgfile_open(fname)) == NULL ) + return NULL; + + if ( image->colormap != CM_NORMAL ) { + iclose(image); + PyErr_SetString(ImgfileError, + "Can only handle CM_NORMAL images"); + return NULL; + } + if ( BPP(image->type) != 1 ) { + iclose(image); + PyErr_SetString(ImgfileError, + "Can't handle imgfiles with bpp!=1"); + return NULL; + } + xsize = image->xsize; + ysize = image->ysize; + zsize = image->zsize; + if ( zsize != 1 && zsize != 3) { + iclose(image); + PyErr_SetString(ImgfileError, + "Can only handle 1 or 3 byte pixels"); + return NULL; + } + if ( xsize > 8192 ) { + iclose(image); + PyErr_SetString(ImgfileError, + "Can't handle image with > 8192 columns"); + return NULL; + } + + if ( zsize == 3 ) zsize = 4; + rv = PyString_FromStringAndSize((char *)NULL, xsize*ysize*zsize); + if ( rv == NULL ) { + iclose(image); + return NULL; + } + cdatap = PyString_AsString(rv); + idatap = (long *)cdatap; + + if (top_to_bottom) { + yfirst = ysize-1; + ylast = -1; + ystep = -1; + } else { + yfirst = 0; + ylast = ysize; + ystep = 1; + } + for ( y=yfirst; y != ylast && !error_called; y += ystep ) { + if ( zsize == 1 ) { + getrow(image, rs, y, 0); + for(x=0; x= 4; + if ( !PyArg_ParseTuple(args, "sii|sd", + &fname, &xwtd, &ywtd, &filter, &blur) ) + return NULL; + + /* + ** Check parameters, open file and check type, rows, etc. + */ + if ( extended ) { + if ( strcmp(filter, "impulse") == 0 ) + fmode = IMPULSE; + else if ( strcmp( filter, "box") == 0 ) + fmode = BOX; + else if ( strcmp( filter, "triangle") == 0 ) + fmode = TRIANGLE; + else if ( strcmp( filter, "quadratic") == 0 ) + fmode = QUADRATIC; + else if ( strcmp( filter, "gaussian") == 0 ) + fmode = GAUSSIAN; + else { + PyErr_SetString(ImgfileError, "Unknown filter type"); + return NULL; + } + } + + if ( (image = imgfile_open(fname)) == NULL ) + return NULL; + + if ( image->colormap != CM_NORMAL ) { + iclose(image); + PyErr_SetString(ImgfileError, + "Can only handle CM_NORMAL images"); + return NULL; + } + if ( BPP(image->type) != 1 ) { + iclose(image); + PyErr_SetString(ImgfileError, + "Can't handle imgfiles with bpp!=1"); + return NULL; + } + xsize = image->xsize; + ysize = image->ysize; + zsize = image->zsize; + if ( zsize != 1 && zsize != 3) { + iclose(image); + PyErr_SetString(ImgfileError, + "Can only handle 1 or 3 byte pixels"); + return NULL; + } + if ( xsize > 8192 ) { + iclose(image); + PyErr_SetString(ImgfileError, + "Can't handle image with > 8192 columns"); + return NULL; + } + + if ( zsize == 3 ) zsize = 4; + rv = PyString_FromStringAndSize(NULL, xwtd*ywtd*zsize); + if ( rv == NULL ) { + iclose(image); + return NULL; + } + PyFPE_START_PROTECT("readscaled", return 0) + xfac = (float)xsize/(float)xwtd; + yfac = (float)ysize/(float)ywtd; + PyFPE_END_PROTECT(yfac) + cdatap = PyString_AsString(rv); + idatap = (long *)cdatap; + + if ( extended ) { + xscale(image, xsize, ysize, zsize, + idatap, xwtd, ywtd, fmode, blur); + } else { + if (top_to_bottom) { + yfirst = ywtd-1; + ylast = -1; + ystep = -1; + } else { + yfirst = 0; + ylast = ywtd; + ystep = 1; + } + for ( y=yfirst; y != ylast && !error_called; y += ystep ) { + yorig = (int)(y*yfac); + if ( zsize == 1 ) { + getrow(image, rs, yorig, 0); + for(x=0; xxsize, image->ysize, image->zsize); + iclose(image); + return rv; +} + +static PyObject * +imgfile_write(PyObject *self, PyObject *args) +{ + IMAGE *image; + char *fname; + int xsize, ysize, zsize, len; + char *cdatap; + long *idatap; + short rs[8192], gs[8192], bs[8192]; + short r, g, b; + long rgb; + int x, y; + int yfirst, ylast, ystep; + + + if ( !PyArg_ParseTuple(args, "ss#iii:write", + &fname, &cdatap, &len, &xsize, &ysize, &zsize) ) + return NULL; + + if ( zsize != 1 && zsize != 3 ) { + PyErr_SetString(ImgfileError, + "Can only handle 1 or 3 byte pixels"); + return NULL; + } + if ( len != xsize * ysize * (zsize == 1 ? 1 : 4) ) { + PyErr_SetString(ImgfileError, "Data does not match sizes"); + return NULL; + } + if ( xsize > 8192 ) { + PyErr_SetString(ImgfileError, + "Can't handle image with > 8192 columns"); + return NULL; + } + + error_called = 0; + errno = 0; + image =iopen(fname, "w", RLE(1), 3, xsize, ysize, zsize); + if ( image == 0 ) { + if ( ! error_called ) { + if (errno) + PyErr_SetFromErrno(ImgfileError); + else + PyErr_SetString(ImgfileError, + "Can't create image file"); + } + return NULL; + } + + idatap = (long *)cdatap; + + if (top_to_bottom) { + yfirst = ysize-1; + ylast = -1; + ystep = -1; + } else { + yfirst = 0; + ylast = ysize; + ystep = 1; + } + for ( y=yfirst; y != ylast && !error_called; y += ystep ) { + if ( zsize == 1 ) { + for( x=0; x> 8 ) & 0xff; + b = (rgb >> 16 ) & 0xff; + rs[x] = r; + gs[x] = g; + bs[x] = b; + } + putrow(image, rs, y, 0); + putrow(image, gs, y, 1); + putrow(image, bs, y, 2); + } + } + iclose(image); + if ( error_called ) + return NULL; + Py_INCREF(Py_None); + return Py_None; + +} + + +static PyMethodDef imgfile_methods[] = { + { "getsizes", imgfile_getsizes, METH_VARARGS }, + { "read", imgfile_read, METH_VARARGS }, + { "readscaled", imgfile_readscaled, METH_VARARGS}, + { "write", imgfile_write, METH_VARARGS }, + { "ttob", imgfile_ttob, METH_VARARGS }, + { NULL, NULL } /* Sentinel */ +}; + + +void +initimgfile(void) +{ + PyObject *m, *d; + m = Py_InitModule("imgfile", imgfile_methods); + d = PyModule_GetDict(m); + ImgfileError = PyErr_NewException("imgfile.error", NULL, NULL); + if (ImgfileError != NULL) + PyDict_SetItemString(d, "error", ImgfileError); +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/itertoolsmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/itertoolsmodule.c new file mode 100644 index 00000000..3a12c2a4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/itertoolsmodule.c @@ -0,0 +1,1852 @@ + +#include "Python.h" + +/* Itertools module written and maintained + by Raymond D. Hettinger + Copyright (c) 2003 Python Software Foundation. + All rights reserved. +*/ + +/* cycle object **********************************************************/ + +typedef struct { + PyObject_HEAD + PyObject *it; + PyObject *saved; + int firstpass; +} cycleobject; + +static PyTypeObject cycle_type; + +static PyObject * +cycle_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *it; + PyObject *iterable; + PyObject *saved; + cycleobject *lz; + + if (!PyArg_UnpackTuple(args, "cycle", 1, 1, &iterable)) + return NULL; + + /* Get iterator. */ + it = PyObject_GetIter(iterable); + if (it == NULL) + return NULL; + + saved = PyList_New(0); + if (saved == NULL) { + Py_DECREF(it); + return NULL; + } + + /* create cycleobject structure */ + lz = (cycleobject *)type->tp_alloc(type, 0); + if (lz == NULL) { + Py_DECREF(it); + Py_DECREF(saved); + return NULL; + } + lz->it = it; + lz->saved = saved; + lz->firstpass = 0; + + return (PyObject *)lz; +} + +static void +cycle_dealloc(cycleobject *lz) +{ + PyObject_GC_UnTrack(lz); + Py_XDECREF(lz->saved); + Py_XDECREF(lz->it); + lz->ob_type->tp_free(lz); +} + +static int +cycle_traverse(cycleobject *lz, visitproc visit, void *arg) +{ + int err; + + if (lz->it) { + err = visit(lz->it, arg); + if (err) + return err; + } + if (lz->saved) { + err = visit(lz->saved, arg); + if (err) + return err; + } + return 0; +} + +static PyObject * +cycle_next(cycleobject *lz) +{ + PyObject *item; + PyObject *it; + + while (1) { + item = PyIter_Next(lz->it); + if (item != NULL) { + if (!lz->firstpass) + PyList_Append(lz->saved, item); + return item; + } + if (PyList_Size(lz->saved) == 0) + return NULL; + it = PyObject_GetIter(lz->saved); + if (it == NULL) + return NULL; + Py_DECREF(lz->it); + lz->it = it; + lz->firstpass = 1; + } +} + +PyDoc_STRVAR(cycle_doc, +"cycle(iterable) --> cycle object\n\ +\n\ +Return elements from the iterable until it is exhausted.\n\ +Then repeat the sequence indefinitely."); + +static PyTypeObject cycle_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.cycle", /* tp_name */ + sizeof(cycleobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)cycle_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + cycle_doc, /* tp_doc */ + (traverseproc)cycle_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)cycle_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + cycle_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* dropwhile object **********************************************************/ + +typedef struct { + PyObject_HEAD + PyObject *func; + PyObject *it; + long start; +} dropwhileobject; + +static PyTypeObject dropwhile_type; + +static PyObject * +dropwhile_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *func, *seq; + PyObject *it; + dropwhileobject *lz; + + if (!PyArg_UnpackTuple(args, "dropwhile", 2, 2, &func, &seq)) + return NULL; + + /* Get iterator. */ + it = PyObject_GetIter(seq); + if (it == NULL) + return NULL; + + /* create dropwhileobject structure */ + lz = (dropwhileobject *)type->tp_alloc(type, 0); + if (lz == NULL) { + Py_DECREF(it); + return NULL; + } + Py_INCREF(func); + lz->func = func; + lz->it = it; + lz->start = 0; + + return (PyObject *)lz; +} + +static void +dropwhile_dealloc(dropwhileobject *lz) +{ + PyObject_GC_UnTrack(lz); + Py_XDECREF(lz->func); + Py_XDECREF(lz->it); + lz->ob_type->tp_free(lz); +} + +static int +dropwhile_traverse(dropwhileobject *lz, visitproc visit, void *arg) +{ + int err; + + if (lz->it) { + err = visit(lz->it, arg); + if (err) + return err; + } + if (lz->func) { + err = visit(lz->func, arg); + if (err) + return err; + } + return 0; +} + +static PyObject * +dropwhile_next(dropwhileobject *lz) +{ + PyObject *item, *good; + PyObject *it = lz->it; + long ok; + + for (;;) { + assert(PyIter_Check(it)); + item = (*it->ob_type->tp_iternext)(it); + if (item == NULL) + return NULL; + if (lz->start == 1) + return item; + + good = PyObject_CallFunctionObjArgs(lz->func, item, NULL); + if (good == NULL) { + Py_DECREF(item); + return NULL; + } + ok = PyObject_IsTrue(good); + Py_DECREF(good); + if (!ok) { + lz->start = 1; + return item; + } + Py_DECREF(item); + } +} + +PyDoc_STRVAR(dropwhile_doc, +"dropwhile(predicate, iterable) --> dropwhile object\n\ +\n\ +Drop items from the iterable while predicate(item) is true.\n\ +Afterwards, return every element until the iterable is exhausted."); + +static PyTypeObject dropwhile_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.dropwhile", /* tp_name */ + sizeof(dropwhileobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dropwhile_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + dropwhile_doc, /* tp_doc */ + (traverseproc)dropwhile_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)dropwhile_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + dropwhile_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* takewhile object **********************************************************/ + +typedef struct { + PyObject_HEAD + PyObject *func; + PyObject *it; + long stop; +} takewhileobject; + +static PyTypeObject takewhile_type; + +static PyObject * +takewhile_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *func, *seq; + PyObject *it; + takewhileobject *lz; + + if (!PyArg_UnpackTuple(args, "takewhile", 2, 2, &func, &seq)) + return NULL; + + /* Get iterator. */ + it = PyObject_GetIter(seq); + if (it == NULL) + return NULL; + + /* create takewhileobject structure */ + lz = (takewhileobject *)type->tp_alloc(type, 0); + if (lz == NULL) { + Py_DECREF(it); + return NULL; + } + Py_INCREF(func); + lz->func = func; + lz->it = it; + lz->stop = 0; + + return (PyObject *)lz; +} + +static void +takewhile_dealloc(takewhileobject *lz) +{ + PyObject_GC_UnTrack(lz); + Py_XDECREF(lz->func); + Py_XDECREF(lz->it); + lz->ob_type->tp_free(lz); +} + +static int +takewhile_traverse(takewhileobject *lz, visitproc visit, void *arg) +{ + int err; + + if (lz->it) { + err = visit(lz->it, arg); + if (err) + return err; + } + if (lz->func) { + err = visit(lz->func, arg); + if (err) + return err; + } + return 0; +} + +static PyObject * +takewhile_next(takewhileobject *lz) +{ + PyObject *item, *good; + PyObject *it = lz->it; + long ok; + + if (lz->stop == 1) + return NULL; + + assert(PyIter_Check(it)); + item = (*it->ob_type->tp_iternext)(it); + if (item == NULL) + return NULL; + + good = PyObject_CallFunctionObjArgs(lz->func, item, NULL); + if (good == NULL) { + Py_DECREF(item); + return NULL; + } + ok = PyObject_IsTrue(good); + Py_DECREF(good); + if (ok) + return item; + Py_DECREF(item); + lz->stop = 1; + return NULL; +} + +PyDoc_STRVAR(takewhile_doc, +"takewhile(predicate, iterable) --> takewhile object\n\ +\n\ +Return successive entries from an iterable as long as the \n\ +predicate evaluates to true for each entry."); + +static PyTypeObject takewhile_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.takewhile", /* tp_name */ + sizeof(takewhileobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)takewhile_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + takewhile_doc, /* tp_doc */ + (traverseproc)takewhile_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)takewhile_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + takewhile_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* islice object ************************************************************/ + +typedef struct { + PyObject_HEAD + PyObject *it; + long next; + long stop; + long step; + long cnt; +} isliceobject; + +static PyTypeObject islice_type; + +static PyObject * +islice_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *seq; + long start=0, stop=-1, step=1; + PyObject *it, *a1=NULL, *a2=NULL; + int numargs; + isliceobject *lz; + + numargs = PyTuple_Size(args); + if (!PyArg_ParseTuple(args, "OO|Ol:islice", &seq, &a1, &a2, &step)) + return NULL; + + if (numargs == 2) { + if (a1 != Py_None) { + stop = PyInt_AsLong(a1); + if (stop == -1) { + if (PyErr_Occurred()) + PyErr_Clear(); + PyErr_SetString(PyExc_ValueError, + "Stop argument must be an integer or None."); + return NULL; + } + } + } else { + start = PyInt_AsLong(a1); + if (start == -1 && PyErr_Occurred()) { + PyErr_Clear(); + PyErr_SetString(PyExc_ValueError, + "Start argument must be an integer."); + return NULL; + } + if (a2 != Py_None) { + stop = PyInt_AsLong(a2); + if (stop == -1) { + if (PyErr_Occurred()) + PyErr_Clear(); + PyErr_SetString(PyExc_ValueError, + "Stop argument must be an integer or None."); + return NULL; + } + } + } + + if (start<0 || stop<-1) { + PyErr_SetString(PyExc_ValueError, + "Indices for islice() must be positive."); + return NULL; + } + + if (step<1) { + PyErr_SetString(PyExc_ValueError, + "Step must be one or larger for islice()."); + return NULL; + } + + /* Get iterator. */ + it = PyObject_GetIter(seq); + if (it == NULL) + return NULL; + + /* create isliceobject structure */ + lz = (isliceobject *)type->tp_alloc(type, 0); + if (lz == NULL) { + Py_DECREF(it); + return NULL; + } + lz->it = it; + lz->next = start; + lz->stop = stop; + lz->step = step; + lz->cnt = 0L; + + return (PyObject *)lz; +} + +static void +islice_dealloc(isliceobject *lz) +{ + PyObject_GC_UnTrack(lz); + Py_XDECREF(lz->it); + lz->ob_type->tp_free(lz); +} + +static int +islice_traverse(isliceobject *lz, visitproc visit, void *arg) +{ + if (lz->it) + return visit(lz->it, arg); + return 0; +} + +static PyObject * +islice_next(isliceobject *lz) +{ + PyObject *item; + PyObject *it = lz->it; + long oldnext; + + while (lz->cnt < lz->next) { + assert(PyIter_Check(it)); + item = (*it->ob_type->tp_iternext)(it); + if (item == NULL) + return NULL; + Py_DECREF(item); + lz->cnt++; + } + if (lz->stop != -1 && lz->cnt >= lz->stop) + return NULL; + assert(PyIter_Check(it)); + item = (*it->ob_type->tp_iternext)(it); + if (item == NULL) + return NULL; + lz->cnt++; + oldnext = lz->next; + lz->next += lz->step; + if (lz->next < oldnext) /* Check for overflow */ + lz->next = lz->stop; + return item; +} + +PyDoc_STRVAR(islice_doc, +"islice(iterable, [start,] stop [, step]) --> islice object\n\ +\n\ +Return an iterator whose next() method returns selected values from an\n\ +iterable. If start is specified, will skip all preceding elements;\n\ +otherwise, start defaults to zero. Step defaults to one. If\n\ +specified as another value, step determines how many values are \n\ +skipped between successive calls. Works like a slice() on a list\n\ +but returns an iterator."); + +static PyTypeObject islice_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.islice", /* tp_name */ + sizeof(isliceobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)islice_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + islice_doc, /* tp_doc */ + (traverseproc)islice_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)islice_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + islice_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* starmap object ************************************************************/ + +typedef struct { + PyObject_HEAD + PyObject *func; + PyObject *it; +} starmapobject; + +static PyTypeObject starmap_type; + +static PyObject * +starmap_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *func, *seq; + PyObject *it; + starmapobject *lz; + + if (!PyArg_UnpackTuple(args, "starmap", 2, 2, &func, &seq)) + return NULL; + + /* Get iterator. */ + it = PyObject_GetIter(seq); + if (it == NULL) + return NULL; + + /* create starmapobject structure */ + lz = (starmapobject *)type->tp_alloc(type, 0); + if (lz == NULL) { + Py_DECREF(it); + return NULL; + } + Py_INCREF(func); + lz->func = func; + lz->it = it; + + return (PyObject *)lz; +} + +static void +starmap_dealloc(starmapobject *lz) +{ + PyObject_GC_UnTrack(lz); + Py_XDECREF(lz->func); + Py_XDECREF(lz->it); + lz->ob_type->tp_free(lz); +} + +static int +starmap_traverse(starmapobject *lz, visitproc visit, void *arg) +{ + int err; + + if (lz->it) { + err = visit(lz->it, arg); + if (err) + return err; + } + if (lz->func) { + err = visit(lz->func, arg); + if (err) + return err; + } + return 0; +} + +static PyObject * +starmap_next(starmapobject *lz) +{ + PyObject *args; + PyObject *result; + PyObject *it = lz->it; + + assert(PyIter_Check(it)); + args = (*it->ob_type->tp_iternext)(it); + if (args == NULL) + return NULL; + if (!PyTuple_CheckExact(args)) { + Py_DECREF(args); + PyErr_SetString(PyExc_TypeError, + "iterator must return a tuple"); + return NULL; + } + result = PyObject_Call(lz->func, args, NULL); + Py_DECREF(args); + return result; +} + +PyDoc_STRVAR(starmap_doc, +"starmap(function, sequence) --> starmap object\n\ +\n\ +Return an iterator whose values are returned from the function evaluated\n\ +with a argument tuple taken from the given sequence."); + +static PyTypeObject starmap_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.starmap", /* tp_name */ + sizeof(starmapobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)starmap_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + starmap_doc, /* tp_doc */ + (traverseproc)starmap_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)starmap_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + starmap_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* imap object ************************************************************/ + +typedef struct { + PyObject_HEAD + PyObject *iters; + PyObject *func; +} imapobject; + +static PyTypeObject imap_type; + +static PyObject * +imap_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *it, *iters, *func; + imapobject *lz; + int numargs, i; + + numargs = PyTuple_Size(args); + if (numargs < 2) { + PyErr_SetString(PyExc_TypeError, + "imap() must have at least two arguments."); + return NULL; + } + + iters = PyTuple_New(numargs-1); + if (iters == NULL) + return NULL; + + for (i=1 ; itp_alloc(type, 0); + if (lz == NULL) { + Py_DECREF(iters); + return NULL; + } + lz->iters = iters; + func = PyTuple_GET_ITEM(args, 0); + Py_INCREF(func); + lz->func = func; + + return (PyObject *)lz; +} + +static void +imap_dealloc(imapobject *lz) +{ + PyObject_GC_UnTrack(lz); + Py_XDECREF(lz->iters); + Py_XDECREF(lz->func); + lz->ob_type->tp_free(lz); +} + +static int +imap_traverse(imapobject *lz, visitproc visit, void *arg) +{ + int err; + + if (lz->iters) { + err = visit(lz->iters, arg); + if (err) + return err; + } + if (lz->func) { + err = visit(lz->func, arg); + if (err) + return err; + } + return 0; +} + +/* +imap() is an iterator version of __builtins__.map() except that it does +not have the None fill-in feature. That was intentionally left out for +the following reasons: + + 1) Itertools are designed to be easily combined and chained together. + Having all tools stop with the shortest input is a unifying principle + that makes it easier to combine finite iterators (supplying data) with + infinite iterators like count() and repeat() (for supplying sequential + or constant arguments to a function). + + 2) In typical use cases for combining itertools, having one finite data + supplier run out before another is likely to be an error condition which + should not pass silently by automatically supplying None. + + 3) The use cases for automatic None fill-in are rare -- not many functions + do something useful when a parameter suddenly switches type and becomes + None. + + 4) If a need does arise, it can be met by __builtins__.map() or by + writing: chain(iterable, repeat(None)). + + 5) Similar toolsets in Haskell and SML do not have automatic None fill-in. +*/ + +static PyObject * +imap_next(imapobject *lz) +{ + PyObject *val; + PyObject *argtuple; + PyObject *result; + int numargs, i; + + numargs = PyTuple_Size(lz->iters); + argtuple = PyTuple_New(numargs); + if (argtuple == NULL) + return NULL; + + for (i=0 ; iiters, i)); + if (val == NULL) { + Py_DECREF(argtuple); + return NULL; + } + PyTuple_SET_ITEM(argtuple, i, val); + } + if (lz->func == Py_None) + return argtuple; + result = PyObject_Call(lz->func, argtuple, NULL); + Py_DECREF(argtuple); + return result; +} + +PyDoc_STRVAR(imap_doc, +"imap(func, *iterables) --> imap object\n\ +\n\ +Make an iterator that computes the function using arguments from\n\ +each of the iterables. Like map() except that it returns\n\ +an iterator instead of a list and that it stops when the shortest\n\ +iterable is exhausted instead of filling in None for shorter\n\ +iterables."); + +static PyTypeObject imap_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.imap", /* tp_name */ + sizeof(imapobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)imap_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + imap_doc, /* tp_doc */ + (traverseproc)imap_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)imap_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + imap_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* chain object ************************************************************/ + +typedef struct { + PyObject_HEAD + long tuplesize; + long iternum; /* which iterator is active */ + PyObject *ittuple; /* tuple of iterators */ +} chainobject; + +static PyTypeObject chain_type; + +static PyObject * +chain_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + chainobject *lz; + int tuplesize = PySequence_Length(args); + int i; + PyObject *ittuple; + + /* obtain iterators */ + assert(PyTuple_Check(args)); + ittuple = PyTuple_New(tuplesize); + if(ittuple == NULL) + return NULL; + for (i=0; i < tuplesize; ++i) { + PyObject *item = PyTuple_GET_ITEM(args, i); + PyObject *it = PyObject_GetIter(item); + if (it == NULL) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + PyErr_Format(PyExc_TypeError, + "chain argument #%d must support iteration", + i+1); + Py_DECREF(ittuple); + return NULL; + } + PyTuple_SET_ITEM(ittuple, i, it); + } + + /* create chainobject structure */ + lz = (chainobject *)type->tp_alloc(type, 0); + if (lz == NULL) { + Py_DECREF(ittuple); + return NULL; + } + + lz->ittuple = ittuple; + lz->iternum = 0; + lz->tuplesize = tuplesize; + + return (PyObject *)lz; +} + +static void +chain_dealloc(chainobject *lz) +{ + PyObject_GC_UnTrack(lz); + Py_XDECREF(lz->ittuple); + lz->ob_type->tp_free(lz); +} + +static int +chain_traverse(chainobject *lz, visitproc visit, void *arg) +{ + if (lz->ittuple) + return visit(lz->ittuple, arg); + return 0; +} + +static PyObject * +chain_next(chainobject *lz) +{ + PyObject *it; + PyObject *item; + + while (lz->iternum < lz->tuplesize) { + it = PyTuple_GET_ITEM(lz->ittuple, lz->iternum); + item = PyIter_Next(it); + if (item != NULL) + return item; + lz->iternum++; + } + return NULL; +} + +PyDoc_STRVAR(chain_doc, +"chain(*iterables) --> chain object\n\ +\n\ +Return a chain object whose .next() method returns elements from the\n\ +first iterable until it is exhausted, then elements from the next\n\ +iterable, until all of the iterables are exhausted."); + +static PyTypeObject chain_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.chain", /* tp_name */ + sizeof(chainobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)chain_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + chain_doc, /* tp_doc */ + (traverseproc)chain_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)chain_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + chain_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* ifilter object ************************************************************/ + +typedef struct { + PyObject_HEAD + PyObject *func; + PyObject *it; +} ifilterobject; + +static PyTypeObject ifilter_type; + +static PyObject * +ifilter_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *func, *seq; + PyObject *it; + ifilterobject *lz; + + if (!PyArg_UnpackTuple(args, "ifilter", 2, 2, &func, &seq)) + return NULL; + + /* Get iterator. */ + it = PyObject_GetIter(seq); + if (it == NULL) + return NULL; + + /* create ifilterobject structure */ + lz = (ifilterobject *)type->tp_alloc(type, 0); + if (lz == NULL) { + Py_DECREF(it); + return NULL; + } + Py_INCREF(func); + lz->func = func; + lz->it = it; + + return (PyObject *)lz; +} + +static void +ifilter_dealloc(ifilterobject *lz) +{ + PyObject_GC_UnTrack(lz); + Py_XDECREF(lz->func); + Py_XDECREF(lz->it); + lz->ob_type->tp_free(lz); +} + +static int +ifilter_traverse(ifilterobject *lz, visitproc visit, void *arg) +{ + int err; + + if (lz->it) { + err = visit(lz->it, arg); + if (err) + return err; + } + if (lz->func) { + err = visit(lz->func, arg); + if (err) + return err; + } + return 0; +} + +static PyObject * +ifilter_next(ifilterobject *lz) +{ + PyObject *item; + PyObject *it = lz->it; + long ok; + + for (;;) { + assert(PyIter_Check(it)); + item = (*it->ob_type->tp_iternext)(it); + if (item == NULL) + return NULL; + + if (lz->func == Py_None) { + ok = PyObject_IsTrue(item); + } else { + PyObject *good; + good = PyObject_CallFunctionObjArgs(lz->func, + item, NULL); + if (good == NULL) { + Py_DECREF(item); + return NULL; + } + ok = PyObject_IsTrue(good); + Py_DECREF(good); + } + if (ok) + return item; + Py_DECREF(item); + } +} + +PyDoc_STRVAR(ifilter_doc, +"ifilter(function or None, sequence) --> ifilter object\n\ +\n\ +Return those items of sequence for which function(item) is true.\n\ +If function is None, return the items that are true."); + +static PyTypeObject ifilter_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.ifilter", /* tp_name */ + sizeof(ifilterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)ifilter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + ifilter_doc, /* tp_doc */ + (traverseproc)ifilter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)ifilter_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + ifilter_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* ifilterfalse object ************************************************************/ + +typedef struct { + PyObject_HEAD + PyObject *func; + PyObject *it; +} ifilterfalseobject; + +static PyTypeObject ifilterfalse_type; + +static PyObject * +ifilterfalse_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *func, *seq; + PyObject *it; + ifilterfalseobject *lz; + + if (!PyArg_UnpackTuple(args, "ifilterfalse", 2, 2, &func, &seq)) + return NULL; + + /* Get iterator. */ + it = PyObject_GetIter(seq); + if (it == NULL) + return NULL; + + /* create ifilterfalseobject structure */ + lz = (ifilterfalseobject *)type->tp_alloc(type, 0); + if (lz == NULL) { + Py_DECREF(it); + return NULL; + } + Py_INCREF(func); + lz->func = func; + lz->it = it; + + return (PyObject *)lz; +} + +static void +ifilterfalse_dealloc(ifilterfalseobject *lz) +{ + PyObject_GC_UnTrack(lz); + Py_XDECREF(lz->func); + Py_XDECREF(lz->it); + lz->ob_type->tp_free(lz); +} + +static int +ifilterfalse_traverse(ifilterfalseobject *lz, visitproc visit, void *arg) +{ + int err; + + if (lz->it) { + err = visit(lz->it, arg); + if (err) + return err; + } + if (lz->func) { + err = visit(lz->func, arg); + if (err) + return err; + } + return 0; +} + +static PyObject * +ifilterfalse_next(ifilterfalseobject *lz) +{ + PyObject *item; + PyObject *it = lz->it; + long ok; + + for (;;) { + assert(PyIter_Check(it)); + item = (*it->ob_type->tp_iternext)(it); + if (item == NULL) + return NULL; + + if (lz->func == Py_None) { + ok = PyObject_IsTrue(item); + } else { + PyObject *good; + good = PyObject_CallFunctionObjArgs(lz->func, + item, NULL); + if (good == NULL) { + Py_DECREF(item); + return NULL; + } + ok = PyObject_IsTrue(good); + Py_DECREF(good); + } + if (!ok) + return item; + Py_DECREF(item); + } +} + +PyDoc_STRVAR(ifilterfalse_doc, +"ifilterfalse(function or None, sequence) --> ifilterfalse object\n\ +\n\ +Return those items of sequence for which function(item) is false.\n\ +If function is None, return the items that are false."); + +static PyTypeObject ifilterfalse_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.ifilterfalse", /* tp_name */ + sizeof(ifilterfalseobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)ifilterfalse_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + ifilterfalse_doc, /* tp_doc */ + (traverseproc)ifilterfalse_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)ifilterfalse_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + ifilterfalse_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* count object ************************************************************/ + +typedef struct { + PyObject_HEAD + long cnt; +} countobject; + +static PyTypeObject count_type; + +static PyObject * +count_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + countobject *lz; + long cnt = 0; + + if (!PyArg_ParseTuple(args, "|l:count", &cnt)) + return NULL; + + /* create countobject structure */ + lz = (countobject *)PyObject_New(countobject, &count_type); + if (lz == NULL) + return NULL; + lz->cnt = cnt; + + return (PyObject *)lz; +} + +static PyObject * +count_next(countobject *lz) +{ + return PyInt_FromLong(lz->cnt++); +} + +PyDoc_STRVAR(count_doc, +"count([firstval]) --> count object\n\ +\n\ +Return a count object whose .next() method returns consecutive\n\ +integers starting from zero or, if specified, from firstval."); + +static PyTypeObject count_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.count", /* tp_name */ + sizeof(countobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)PyObject_Del, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + count_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)count_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + count_new, /* tp_new */ +}; + + +/* izip object ************************************************************/ + +#include "Python.h" + +typedef struct { + PyObject_HEAD + long tuplesize; + PyObject *ittuple; /* tuple of iterators */ + PyObject *result; +} izipobject; + +static PyTypeObject izip_type; + +static PyObject * +izip_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + izipobject *lz; + int i; + PyObject *ittuple; /* tuple of iterators */ + PyObject *result; + int tuplesize = PySequence_Length(args); + + /* args must be a tuple */ + assert(PyTuple_Check(args)); + + /* obtain iterators */ + ittuple = PyTuple_New(tuplesize); + if(ittuple == NULL) + return NULL; + for (i=0; i < tuplesize; ++i) { + PyObject *item = PyTuple_GET_ITEM(args, i); + PyObject *it = PyObject_GetIter(item); + if (it == NULL) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + PyErr_Format(PyExc_TypeError, + "izip argument #%d must support iteration", + i+1); + Py_DECREF(ittuple); + return NULL; + } + PyTuple_SET_ITEM(ittuple, i, it); + } + + /* create a result holder */ + result = PyTuple_New(tuplesize); + if (result == NULL) { + Py_DECREF(ittuple); + return NULL; + } + for (i=0 ; i < tuplesize ; i++) { + Py_INCREF(Py_None); + PyTuple_SET_ITEM(result, i, Py_None); + } + + /* create izipobject structure */ + lz = (izipobject *)type->tp_alloc(type, 0); + if (lz == NULL) { + Py_DECREF(ittuple); + Py_DECREF(result); + return NULL; + } + lz->ittuple = ittuple; + lz->tuplesize = tuplesize; + lz->result = result; + + return (PyObject *)lz; +} + +static void +izip_dealloc(izipobject *lz) +{ + PyObject_GC_UnTrack(lz); + Py_XDECREF(lz->ittuple); + Py_XDECREF(lz->result); + lz->ob_type->tp_free(lz); +} + +static int +izip_traverse(izipobject *lz, visitproc visit, void *arg) +{ + int err; + + if (lz->ittuple) { + err = visit(lz->ittuple, arg); + if (err) + return err; + } + if (lz->result) { + err = visit(lz->result, arg); + if (err) + return err; + } + return 0; +} + +static PyObject * +izip_next(izipobject *lz) +{ + int i; + long tuplesize = lz->tuplesize; + PyObject *result = lz->result; + PyObject *it; + PyObject *item; + PyObject *olditem; + + if (tuplesize == 0) + return NULL; + if (result->ob_refcnt == 1) { + Py_INCREF(result); + for (i=0 ; i < tuplesize ; i++) { + it = PyTuple_GET_ITEM(lz->ittuple, i); + assert(PyIter_Check(it)); + item = (*it->ob_type->tp_iternext)(it); + if (item == NULL) { + Py_DECREF(result); + return NULL; + } + olditem = PyTuple_GET_ITEM(result, i); + PyTuple_SET_ITEM(result, i, item); + Py_DECREF(olditem); + } + } else { + result = PyTuple_New(tuplesize); + if (result == NULL) + return NULL; + for (i=0 ; i < tuplesize ; i++) { + it = PyTuple_GET_ITEM(lz->ittuple, i); + assert(PyIter_Check(it)); + item = (*it->ob_type->tp_iternext)(it); + if (item == NULL) { + Py_DECREF(result); + return NULL; + } + PyTuple_SET_ITEM(result, i, item); + } + } + return result; +} + +PyDoc_STRVAR(izip_doc, +"izip(iter1 [,iter2 [...]]) --> izip object\n\ +\n\ +Return a izip object whose .next() method returns a tuple where\n\ +the i-th element comes from the i-th iterable argument. The .next()\n\ +method continues until the shortest iterable in the argument sequence\n\ +is exhausted and then it raises StopIteration. Works like the zip()\n\ +function but consumes less memory by returning an iterator instead of\n\ +a list."); + +static PyTypeObject izip_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.izip", /* tp_name */ + sizeof(izipobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)izip_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + izip_doc, /* tp_doc */ + (traverseproc)izip_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)izip_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + izip_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* repeat object ************************************************************/ + +typedef struct { + PyObject_HEAD + PyObject *element; + long cnt; +} repeatobject; + +static PyTypeObject repeat_type; + +static PyObject * +repeat_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + repeatobject *ro; + PyObject *element; + long cnt = -1; + + if (!PyArg_ParseTuple(args, "O|l:repeat", &element, &cnt)) + return NULL; + + if (PyTuple_Size(args) == 2 && cnt < 0) + cnt = 0; + + ro = (repeatobject *)type->tp_alloc(type, 0); + if (ro == NULL) + return NULL; + Py_INCREF(element); + ro->element = element; + ro->cnt = cnt; + return (PyObject *)ro; +} + +static void +repeat_dealloc(repeatobject *ro) +{ + PyObject_GC_UnTrack(ro); + Py_XDECREF(ro->element); + ro->ob_type->tp_free(ro); +} + +static int +repeat_traverse(repeatobject *ro, visitproc visit, void *arg) +{ + if (ro->element) + return visit(ro->element, arg); + return 0; +} + +static PyObject * +repeat_next(repeatobject *ro) +{ + if (ro->cnt == 0) + return NULL; + if (ro->cnt > 0) + ro->cnt--; + Py_INCREF(ro->element); + return ro->element; +} + +PyDoc_STRVAR(repeat_doc, +"repeat(element [,times]) -> create an iterator which returns the element\n\ +for the specified number of times. If not specified, returns the element\n\ +endlessly."); + +static PyTypeObject repeat_type = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "itertools.repeat", /* tp_name */ + sizeof(repeatobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)repeat_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + repeat_doc, /* tp_doc */ + (traverseproc)repeat_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)repeat_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + repeat_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* module level code ********************************************************/ + +PyDoc_STRVAR(module_doc, +"Functional tools for creating and using iterators.\n\ +\n\ +Infinite iterators:\n\ +count([n]) --> n, n+1, n+2, ...\n\ +cycle(p) --> p0, p1, ... plast, p0, p1, ...\n\ +repeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times\n\ +\n\ +Iterators terminating on the shortest input sequence:\n\ +izip(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ... \n\ +ifilter(pred, seq) --> elements of seq where pred(elem) is True\n\ +ifilterfalse(pred, seq) --> elements of seq where pred(elem) is False\n\ +islice(seq, [start,] stop [, step]) --> elements from\n\ + seq[start:stop:step]\n\ +imap(fun, p, q, ...) --> fun(p0, q0), fun(p1, q1), ...\n\ +starmap(fun, seq) --> fun(*seq[0]), fun(*seq[1]), ...\n\ +chain(p, q, ...) --> p0, p1, ... plast, q0, q1, ... \n\ +takewhile(pred, seq) --> seq[0], seq[1], until pred fails\n\ +dropwhile(pred, seq) --> seq[n], seq[n+1], starting when pred fails\n\ +"); + + +PyMODINIT_FUNC +inititertools(void) +{ + int i; + PyObject *m; + char *name; + PyTypeObject *typelist[] = { + &cycle_type, + &dropwhile_type, + &takewhile_type, + &islice_type, + &starmap_type, + &imap_type, + &chain_type, + &ifilter_type, + &ifilterfalse_type, + &count_type, + &izip_type, + &repeat_type, + NULL + }; + + m = Py_InitModule3("itertools", NULL, module_doc); + + for (i=0 ; typelist[i] != NULL ; i++) { + if (PyType_Ready(typelist[i]) < 0) + return; + name = strchr(typelist[i]->tp_name, '.'); + assert (name != NULL); + Py_INCREF(typelist[i]); + PyModule_AddObject(m, name+1, (PyObject *)typelist[i]); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ld_so_aix b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ld_so_aix new file mode 100644 index 00000000..0a2a599b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ld_so_aix @@ -0,0 +1,183 @@ +#!/bin/sh +# +# ======================================================================== +# FILE: ld_so_aix +# TYPE: executable, uses makexp_aix +# SYSTEM: AIX +# +# DESCRIPTION: Creates a shareable .o from a set of pre-compiled +# (unshared) .o files +# +# USAGE: ld_so_aix [CC] [arguments] +# +# ARGUMENTS: Same as for "ld". The following arguments are processed +# or supplied by this script (those marked with an asterisk +# can be overriden from command line): +# +# Argument Default value +# (*) -o [OutputFileName] -o shr.o +# (*) -e [EntryPointLabel] -e init[OutputBaseName] +# (*) -bE:[ExportFile] -bE:[OutputBaseName].exp +# (*) -bI:[ImportFile] -bI:./python.exp +# -bM:[ModuleType] -bM:SRE +# -bhalt:[Number] -bhalt:4 +# -T[Number] -T512 +# -H[Number] -H512 +# -lm +# +# The compiler specific ("-lc" or "-lc_r", "-lpthreads",...) +# arguments will be automatically passed to "ld" according +# to the CC command provided as a first argument to this +# script. Usually, the same CC command was used to produce +# the pre-compiled .o file(s). +# +# NOTES: 1. Since "ld_so_aix" was originally written for building +# shared modules for the Python interpreter, the -e and +# -bI default values match Python's conventions. In +# Python, the entry point for a shared module is based +# on the module's name (e.g., the "mathmodule" will +# expect an entry point of "initmath"). +# 2. The script accepts multiple .o or .a input files and +# creates a single (shared) output file. The export list +# that is created is based on the output file's basename +# with the suffix ".exp". +# 3. The resulting shared object file is left in the +# current directory. +# 4. Uncommenting the "echo" lines gives detailed output +# about the commands executed in the script. +# +# +# HISTORY: Oct-1996 -- Support added for multiple .o files -- +# -- and optional arguments processing. -- +# Chris Myers (myers@tc.cornell.edu), Keith Kwok +# (kkwok@tc.cornell.edu) and Vladimir Marangozov +# +# Aug-6-1996 -- Take care of the compiler specific -- +# -- args by leaving CC to invoke "ld". -- +# Vladimir Marangozov +# +# Jul-1-1996 -- Make sure to use /usr/ccs/bin/ld -- +# -- Use makexp_aix for the export list. -- +# Vladimir Marangozov (Vladimir.Marangozov@imag.fr) +# +# Manus Hand (mhand@csn.net) -- Initial code -- 6/24/96 +# ======================================================================== +# + +usage="Usage: ld_so_aix [CC command] [ld arguments]" +if test ! -n "$*"; then + echo $usage; exit 2 +fi + +makexp=`dirname $0`/makexp_aix + +# Check for existence of compiler. +CC=$1; shift +whichcc=`which $CC` + +if test ! -x "$whichcc"; then + echo "ld_so_aix: Compiler '$CC' not found; exiting." + exit 2 +fi + +if test ! -n "$*"; then + echo $usage; exit 2 +fi + +# Default import file for Python +# Can be overriden by providing a -bI: argument. +impfile="./python.exp" + +# Parse arguments +while test -n "$1" +do + case "$1" in + -e | -Wl,-e) + if test -z "$2"; then + echo "ld_so_aix: The -e flag needs a parameter; exiting."; exit 2 + else + shift; entry=$1 + fi + ;; + -e* | -Wl,-e*) + entry=`echo $1 | sed -e "s/-Wl,//" -e "s/-e//"` + ;; + -o) + if test -z "$2"; then + echo "ld_so_aix: The -o flag needs a parameter; exiting."; exit 2 + else + shift; objfile=$1 + fi + ;; + -o*) + objfile=`echo $1 | sed "s/-o//"` + ;; + -bI:* | -Wl,-bI:*) + impfile=`echo $1 | sed -e "s/-Wl,//" -e "s/-bI://"` + ;; + -bE:* | -Wl,-bE:*) + expfile=`echo $1 | sed -e "s/-Wl,//" -e "s/-bE://"` + ;; + *.o | *.a) + objs="$objs $1" + args="$args $1" + ;; + -bM:* | -Wl,-bM:* | -H* | -Wl,-H* | -T* | -Wl,-T* | -lm) + ;; + *) + args="$args $1" + ;; + esac + shift +done + + +if test -z "$objs"; then + echo "ld_so_aix: No input files; exiting." + exit 2 +elif test ! -r "$impfile"; then + echo "ld_so_aix: Import file '$impfile' not found or not readable; exiting." + exit 2 +fi + +# If -o wasn't specified, assume "-o shr.o" +if test -z "$objfile"; then + objfile=shr.o +fi + +filename=`basename $objfile | sed "s/\.[^.]*$//"` + +# If -bE: wasn't specified, assume "-bE:$filename.exp" +if test -z "$expfile"; then + expfile="$filename.exp" +fi + +# Default entry symbol for Python modules = init[modulename] +# Can be overriden by providing a -e argument. +if test -z "$entry"; then + entry=init`echo $filename | sed "s/module.*//"` +fi + +#echo "ld_so_aix: Debug info section" +#echo " -> output file : $objfile" +#echo " -> import file : $impfile" +#echo " -> export file : $expfile" +#echo " -> entry point : $entry" +#echo " -> object files: $objs" +#echo " -> CC arguments: $args" + +CCOPT="-Wl,-e$entry -Wl,-bE:$expfile -Wl,-bI:$impfile -Wl,-bhalt:4" +CCOPT="$CCOPT -Wl,-bM:SRE -Wl,-T512 -Wl,-H512 -lm -o $objfile" +CCARGS="$args" + +# Export list generation. +#echo $makexp $expfile "$objfile" $objs +$makexp $expfile "$objfile" $objs + +# Perform the link. +#echo $CC $CCOPT $CCARGS +$CC $CCOPT $CCARGS + +# Delete the module's export list file. +# Comment this line if you need it. +rm -f $expfile diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ld_so_beos b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ld_so_beos new file mode 100644 index 00000000..a252c4e8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ld_so_beos @@ -0,0 +1,78 @@ +#! /bin/sh +# +# linkmodule for Python +# Chris Herborth (chrish@qnx.com) +# +# This is covered by the same copyright/licensing terms as the rest of +# Python. +# +# Shell script to build shared library versions of the modules; since +# the change to the *ahem* "proper" import/export mechanism, this script +# is much simpler. It handles PowerPC and x86, too. +# +# This is called by the Modules/Makefile as $(LDSHARED): +# +# $(LDSHARED) foomodule.o -o foomodule$(SO) +# +# Could also be called as: +# +# $(LDSHARED) readline.o -L/boot/home/config/lib -lreadline -ltermcap \ +# -o readline$(SO) +# +# so we need to preserve the arguments, sort of. + +# Make sure we got reasonable arguments. +TARGET="" +ARGS="" + +while [ "$#" != "0" ]; do + case "$1" in + -o) TARGET="$2"; shift; shift;; + *) ARGS="$ARGS $1"; shift;; + esac +done + +if [ "$TARGET" = "" ] ; then + echo "Usage:" + echo + echo " $0 [args] -o foomodule.so [args] foomodule.o [args]" + echo + echo "Where:" + echo + echo " [args] normal compiler arguments" + exit 1 +fi + +# The shared libraries and glue objects we need to link against; these +# libs are overkill for most of the standard modules, but it makes life +# in this shell script easier. +LIBS="-lbe -lnet -lroot" + +case $BE_HOST_CPU in + ppc) + # Boy, do we need a lot of crap... + GLUE_LOC=/boot/develop/lib/ppc + GLUE="${GLUE_LOC}/glue-noinit.a ${GLUE_LOC}/init_term_dyn.o" + case $(uname -r) in + 4.0*) CC="mwcc -xms -export pragma -nodup" ;; + *) CC="mwcc -shared -export pragma -nodup" ;; + esac + ;; + + x86) + # We don't need as much crap here... + GLUE="" + CC="gcc -nostart -Wl,-soname=${TARGET}" + ;; + + *) + # What the?!? + echo "$0 doesn't support $BE_HOST_CPU systems..." + echo "You're on your own... I'd be surprised if this works." + GLUE="" + CC="cc" + ;; +esac + +# Now link that shared lib... +$CC -o $TARGET $ARGS $GLUE $LIBS diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/linuxaudiodev.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/linuxaudiodev.c new file mode 100644 index 00000000..8cfab357 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/linuxaudiodev.c @@ -0,0 +1,519 @@ +/* Hey Emacs, this is -*-C-*- + ****************************************************************************** + * linuxaudiodev.c -- Linux audio device for python. + * + * Author : Peter Bosch + * Created On : Thu Mar 2 21:10:33 2000 + * Status : Unknown, Use with caution! + * + * Unless other notices are present in any part of this file + * explicitly claiming copyrights for other people and/or + * organizations, the contents of this file is fully copyright + * (C) 2000 Peter Bosch, all rights reserved. + ****************************************************************************** + */ + +#include "Python.h" +#include "structmember.h" + +#ifdef HAVE_FCNTL_H +#include +#else +#define O_RDONLY 00 +#define O_WRONLY 01 +#endif + + +#include +#if defined(linux) +#include + +typedef unsigned long uint32_t; + +#elif defined(__FreeBSD__) +#include + +#ifndef SNDCTL_DSP_CHANNELS +#define SNDCTL_DSP_CHANNELS SOUND_PCM_WRITE_CHANNELS +#endif + +#endif + +typedef struct { + PyObject_HEAD; + int x_fd; /* The open file */ + int x_mode; /* file mode */ + int x_icount; /* Input count */ + int x_ocount; /* Output count */ + uint32_t x_afmts; /* Audio formats supported by hardware*/ +} lad_t; + +/* XXX several format defined in soundcard.h are not supported, + including _NE (native endian) options and S32 options +*/ + +static struct { + int a_bps; + uint32_t a_fmt; + char *a_name; +} audio_types[] = { + { 8, AFMT_MU_LAW, "logarithmic mu-law 8-bit audio" }, + { 8, AFMT_A_LAW, "logarithmic A-law 8-bit audio" }, + { 8, AFMT_U8, "linear unsigned 8-bit audio" }, + { 8, AFMT_S8, "linear signed 8-bit audio" }, + { 16, AFMT_U16_BE, "linear unsigned 16-bit big-endian audio" }, + { 16, AFMT_U16_LE, "linear unsigned 16-bit little-endian audio" }, + { 16, AFMT_S16_BE, "linear signed 16-bit big-endian audio" }, + { 16, AFMT_S16_LE, "linear signed 16-bit little-endian audio" }, + { 16, AFMT_S16_NE, "linear signed 16-bit native-endian audio" }, +}; + +static int n_audio_types = sizeof(audio_types) / sizeof(audio_types[0]); + +static PyTypeObject Ladtype; + +static PyObject *LinuxAudioError; + +static lad_t * +newladobject(PyObject *arg) +{ + lad_t *xp; + int fd, afmts, imode; + char *basedev = NULL; + char *mode = NULL; + + /* Two ways to call linuxaudiodev.open(): + open(device, mode) (for consistency with builtin open()) + open(mode) (for backwards compatibility) + because the *first* argument is optional, parsing args is + a wee bit tricky. */ + if (!PyArg_ParseTuple(arg, "s|s:open", &basedev, &mode)) + return NULL; + if (mode == NULL) { /* only one arg supplied */ + mode = basedev; + basedev = NULL; + } + + if (strcmp(mode, "r") == 0) + imode = O_RDONLY; + else if (strcmp(mode, "w") == 0) + imode = O_WRONLY; + else { + PyErr_SetString(LinuxAudioError, "mode should be 'r' or 'w'"); + return NULL; + } + + /* Open the correct device. The base device name comes from the + * AUDIODEV environment variable first, then /dev/dsp. The + * control device tacks "ctl" onto the base device name. + * + * Note that the only difference between /dev/audio and /dev/dsp + * is that the former uses logarithmic mu-law encoding and the + * latter uses 8-bit unsigned encoding. + */ + + if (basedev == NULL) { /* called with one arg */ + basedev = getenv("AUDIODEV"); + if (basedev == NULL) /* $AUDIODEV not set */ + basedev = "/dev/dsp"; + } + + if ((fd = open(basedev, imode)) == -1) { + PyErr_SetFromErrnoWithFilename(LinuxAudioError, basedev); + return NULL; + } + if (imode == O_WRONLY && ioctl(fd, SNDCTL_DSP_NONBLOCK, NULL) == -1) { + PyErr_SetFromErrnoWithFilename(LinuxAudioError, basedev); + return NULL; + } + if (ioctl(fd, SNDCTL_DSP_GETFMTS, &afmts) == -1) { + PyErr_SetFromErrnoWithFilename(LinuxAudioError, basedev); + return NULL; + } + /* Create and initialize the object */ + if ((xp = PyObject_New(lad_t, &Ladtype)) == NULL) { + close(fd); + return NULL; + } + xp->x_fd = fd; + xp->x_mode = imode; + xp->x_icount = xp->x_ocount = 0; + xp->x_afmts = afmts; + return xp; +} + +static void +lad_dealloc(lad_t *xp) +{ + /* if already closed, don't reclose it */ + if (xp->x_fd != -1) + close(xp->x_fd); + PyObject_Del(xp); +} + +static PyObject * +lad_read(lad_t *self, PyObject *args) +{ + int size, count; + char *cp; + PyObject *rv; + + if (!PyArg_ParseTuple(args, "i:read", &size)) + return NULL; + rv = PyString_FromStringAndSize(NULL, size); + if (rv == NULL) + return NULL; + cp = PyString_AS_STRING(rv); + if ((count = read(self->x_fd, cp, size)) < 0) { + PyErr_SetFromErrno(LinuxAudioError); + Py_DECREF(rv); + return NULL; + } + self->x_icount += count; + _PyString_Resize(&rv, count); + return rv; +} + +static PyObject * +lad_write(lad_t *self, PyObject *args) +{ + char *cp; + int rv, size; + fd_set write_set_fds; + struct timeval tv; + int select_retval; + + if (!PyArg_ParseTuple(args, "s#:write", &cp, &size)) + return NULL; + + /* use select to wait for audio device to be available */ + FD_ZERO(&write_set_fds); + FD_SET(self->x_fd, &write_set_fds); + tv.tv_sec = 4; /* timeout values */ + tv.tv_usec = 0; + + while (size > 0) { + select_retval = select(self->x_fd+1, NULL, &write_set_fds, NULL, &tv); + tv.tv_sec = 1; tv.tv_usec = 0; /* willing to wait this long next time*/ + if (select_retval) { + if ((rv = write(self->x_fd, cp, size)) == -1) { + if (errno != EAGAIN) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } else { + errno = 0; /* EAGAIN: buffer is full, try again */ + } + } else { + self->x_ocount += rv; + size -= rv; + cp += rv; + } + } else { + /* printf("Not able to write to linux audio device within %ld seconds\n", tv.tv_sec); */ + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +lad_close(lad_t *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":close")) + return NULL; + + if (self->x_fd >= 0) { + close(self->x_fd); + self->x_fd = -1; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +lad_fileno(lad_t *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":fileno")) + return NULL; + return PyInt_FromLong(self->x_fd); +} + +static PyObject * +lad_setparameters(lad_t *self, PyObject *args) +{ + int rate, ssize, nchannels, n, fmt, emulate=0; + + if (!PyArg_ParseTuple(args, "iiii|i:setparameters", + &rate, &ssize, &nchannels, &fmt, &emulate)) + return NULL; + + if (rate < 0) { + PyErr_Format(PyExc_ValueError, "expected rate >= 0, not %d", + rate); + return NULL; + } + if (ssize < 0) { + PyErr_Format(PyExc_ValueError, "expected sample size >= 0, not %d", + ssize); + return NULL; + } + if (nchannels != 1 && nchannels != 2) { + PyErr_Format(PyExc_ValueError, "nchannels must be 1 or 2, not %d", + nchannels); + return NULL; + } + + for (n = 0; n < n_audio_types; n++) + if (fmt == audio_types[n].a_fmt) + break; + if (n == n_audio_types) { + PyErr_Format(PyExc_ValueError, "unknown audio encoding: %d", fmt); + return NULL; + } + if (audio_types[n].a_bps != ssize) { + PyErr_Format(PyExc_ValueError, + "for %s, expected sample size %d, not %d", + audio_types[n].a_name, audio_types[n].a_bps, ssize); + return NULL; + } + + if (emulate == 0) { + if ((self->x_afmts & audio_types[n].a_fmt) == 0) { + PyErr_Format(PyExc_ValueError, + "%s format not supported by device", + audio_types[n].a_name); + return NULL; + } + } + if (ioctl(self->x_fd, SNDCTL_DSP_SETFMT, + &audio_types[n].a_fmt) == -1) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + if (ioctl(self->x_fd, SNDCTL_DSP_CHANNELS, &nchannels) == -1) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + if (ioctl(self->x_fd, SNDCTL_DSP_SPEED, &rate) == -1) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static int +_ssize(lad_t *self, int *nchannels, int *ssize) +{ + int fmt; + + fmt = 0; + if (ioctl(self->x_fd, SNDCTL_DSP_SETFMT, &fmt) < 0) + return -errno; + + switch (fmt) { + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + *ssize = sizeof(char); + break; + case AFMT_S16_LE: + case AFMT_S16_BE: + case AFMT_U16_LE: + case AFMT_U16_BE: + *ssize = sizeof(short); + break; + case AFMT_MPEG: + case AFMT_IMA_ADPCM: + default: + return -EOPNOTSUPP; + } + *nchannels = 0; + if (ioctl(self->x_fd, SNDCTL_DSP_CHANNELS, nchannels) < 0) + return -errno; + return 0; +} + + +/* bufsize returns the size of the hardware audio buffer in number + of samples */ +static PyObject * +lad_bufsize(lad_t *self, PyObject *args) +{ + audio_buf_info ai; + int nchannels, ssize; + + if (!PyArg_ParseTuple(args, ":bufsize")) return NULL; + + if (_ssize(self, &nchannels, &ssize) < 0) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + if (ioctl(self->x_fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + return PyInt_FromLong((ai.fragstotal * ai.fragsize) / (nchannels * ssize)); +} + +/* obufcount returns the number of samples that are available in the + hardware for playing */ +static PyObject * +lad_obufcount(lad_t *self, PyObject *args) +{ + audio_buf_info ai; + int nchannels, ssize; + + if (!PyArg_ParseTuple(args, ":obufcount")) + return NULL; + + if (_ssize(self, &nchannels, &ssize) < 0) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + if (ioctl(self->x_fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + return PyInt_FromLong((ai.fragstotal * ai.fragsize - ai.bytes) / + (ssize * nchannels)); +} + +/* obufcount returns the number of samples that can be played without + blocking */ +static PyObject * +lad_obuffree(lad_t *self, PyObject *args) +{ + audio_buf_info ai; + int nchannels, ssize; + + if (!PyArg_ParseTuple(args, ":obuffree")) + return NULL; + + if (_ssize(self, &nchannels, &ssize) < 0) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + if (ioctl(self->x_fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + return PyInt_FromLong(ai.bytes / (ssize * nchannels)); +} + +/* Flush the device */ +static PyObject * +lad_flush(lad_t *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":flush")) return NULL; + + if (ioctl(self->x_fd, SNDCTL_DSP_SYNC, NULL) == -1) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +lad_getptr(lad_t *self, PyObject *args) +{ + count_info info; + int req; + + if (!PyArg_ParseTuple(args, ":getptr")) + return NULL; + + if (self->x_mode == O_RDONLY) + req = SNDCTL_DSP_GETIPTR; + else + req = SNDCTL_DSP_GETOPTR; + if (ioctl(self->x_fd, req, &info) == -1) { + PyErr_SetFromErrno(LinuxAudioError); + return NULL; + } + return Py_BuildValue("iii", info.bytes, info.blocks, info.ptr); +} + +static PyMethodDef lad_methods[] = { + { "read", (PyCFunction)lad_read, METH_VARARGS }, + { "write", (PyCFunction)lad_write, METH_VARARGS }, + { "setparameters", (PyCFunction)lad_setparameters, METH_VARARGS }, + { "bufsize", (PyCFunction)lad_bufsize, METH_VARARGS }, + { "obufcount", (PyCFunction)lad_obufcount, METH_VARARGS }, + { "obuffree", (PyCFunction)lad_obuffree, METH_VARARGS }, + { "flush", (PyCFunction)lad_flush, METH_VARARGS }, + { "close", (PyCFunction)lad_close, METH_VARARGS }, + { "fileno", (PyCFunction)lad_fileno, METH_VARARGS }, + { "getptr", (PyCFunction)lad_getptr, METH_VARARGS }, + { NULL, NULL} /* sentinel */ +}; + +static PyObject * +lad_getattr(lad_t *xp, char *name) +{ + return Py_FindMethod(lad_methods, (PyObject *)xp, name); +} + +static PyTypeObject Ladtype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "linuxaudiodev.linux_audio_device", /*tp_name*/ + sizeof(lad_t), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)lad_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)lad_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + +static PyObject * +ladopen(PyObject *self, PyObject *args) +{ + return (PyObject *)newladobject(args); +} + +static PyMethodDef linuxaudiodev_methods[] = { + { "open", ladopen, METH_VARARGS }, + { 0, 0 }, +}; + +void +initlinuxaudiodev(void) +{ + PyObject *m; + + m = Py_InitModule("linuxaudiodev", linuxaudiodev_methods); + + LinuxAudioError = PyErr_NewException("linuxaudiodev.error", NULL, NULL); + if (LinuxAudioError) + PyModule_AddObject(m, "error", LinuxAudioError); + + if (PyModule_AddIntConstant(m, "AFMT_MU_LAW", (long)AFMT_MU_LAW) == -1) + return; + if (PyModule_AddIntConstant(m, "AFMT_A_LAW", (long)AFMT_A_LAW) == -1) + return; + if (PyModule_AddIntConstant(m, "AFMT_U8", (long)AFMT_U8) == -1) + return; + if (PyModule_AddIntConstant(m, "AFMT_S8", (long)AFMT_S8) == -1) + return; + if (PyModule_AddIntConstant(m, "AFMT_U16_BE", (long)AFMT_U16_BE) == -1) + return; + if (PyModule_AddIntConstant(m, "AFMT_U16_LE", (long)AFMT_U16_LE) == -1) + return; + if (PyModule_AddIntConstant(m, "AFMT_S16_BE", (long)AFMT_S16_BE) == -1) + return; + if (PyModule_AddIntConstant(m, "AFMT_S16_LE", (long)AFMT_S16_LE) == -1) + return; + if (PyModule_AddIntConstant(m, "AFMT_S16_NE", (long)AFMT_S16_NE) == -1) + return; + + return; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/main.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/main.c new file mode 100644 index 00000000..1175f01b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/main.c @@ -0,0 +1,466 @@ +/* Python interpreter main program */ + +#include "Python.h" +#include "osdefs.h" +#include "compile.h" /* For CO_FUTURE_DIVISION */ + +#ifdef __VMS +#include +#endif + +#if defined(MS_WINDOWS) || defined(__CYGWIN__) +#include +#endif + +#if (defined(PYOS_OS2) && !defined(PYCC_GCC)) || defined(MS_WINDOWS) +#define PYTHONHOMEHELP "\\lib" +#else +#if defined(PYOS_OS2) && defined(PYCC_GCC) +#define PYTHONHOMEHELP "/Lib" +#else +#define PYTHONHOMEHELP "/pythonX.X" +#endif +#endif + +#include "pygetopt.h" + +#define COPYRIGHT \ + "Type \"help\", \"copyright\", \"credits\" or \"license\" " \ + "for more information." + +/* For Py_GetArgcArgv(); set by main() */ +static char **orig_argv; +static int orig_argc; + +/* command line options */ +#define BASE_OPTS "c:dEhiOQ:StuUvVW:xX" + +#ifndef RISCOS +#define PROGRAM_OPTS BASE_OPTS +#else /*RISCOS*/ +/* extra option saying that we are running under a special task window + frontend; especially my_readline will behave different */ +#define PROGRAM_OPTS BASE_OPTS "w" +/* corresponding flag */ +extern int Py_RISCOSWimpFlag; +#endif /*RISCOS*/ + +/* Short usage message (with %s for argv0) */ +static char *usage_line = +"usage: %s [option] ... [-c cmd | file | -] [arg] ...\n"; + +/* Long usage message, split into parts < 512 bytes */ +static char *usage_1 = "\ +Options and arguments (and corresponding environment variables):\n\ +-c cmd : program passed in as string (terminates option list)\n\ +-d : debug output from parser (also PYTHONDEBUG=x)\n\ +-E : ignore environment variables (such as PYTHONPATH)\n\ +-h : print this help message and exit\n\ +-i : inspect interactively after running script, (also PYTHONINSPECT=x)\n\ + and force prompts, even if stdin does not appear to be a terminal\n\ +"; +static char *usage_2 = "\ +-O : optimize generated bytecode (a tad; also PYTHONOPTIMIZE=x)\n\ +-OO : remove doc-strings in addition to the -O optimizations\n\ +-Q arg : division options: -Qold (default), -Qwarn, -Qwarnall, -Qnew\n\ +-S : don't imply 'import site' on initialization\n\ +-t : issue warnings about inconsistent tab usage (-tt: issue errors)\n\ +-u : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)\n\ + see man page for details on internal buffering relating to '-u'\n\ +"; +static char *usage_3 = "\ +-v : verbose (trace import statements) (also PYTHONVERBOSE=x)\n\ +-V : print the Python version number and exit\n\ +-W arg : warning control (arg is action:message:category:module:lineno)\n\ +-x : skip first line of source, allowing use of non-Unix forms of #!cmd\n\ +file : program read from script file\n\ +- : program read from stdin (default; interactive mode if a tty)\n\ +"; +static char *usage_4 = "\ +arg ...: arguments passed to program in sys.argv[1:]\n\ +Other environment variables:\n\ +PYTHONSTARTUP: file executed on interactive startup (no default)\n\ +PYTHONPATH : '%c'-separated list of directories prefixed to the\n\ + default module search path. The result is sys.path.\n\ +PYTHONHOME : alternate directory (or %c).\n\ + The default module search path uses %s.\n\ +PYTHONCASEOK : ignore case in 'import' statements (Windows).\n\ +"; + + +static int +usage(int exitcode, char* program) +{ + FILE *f = exitcode ? stderr : stdout; + + fprintf(f, usage_line, program); + if (exitcode) + fprintf(f, "Try `python -h' for more information.\n"); + else { + fprintf(f, usage_1); + fprintf(f, usage_2); + fprintf(f, usage_3); + fprintf(f, usage_4, DELIM, DELIM, PYTHONHOMEHELP); + } +#if defined(__VMS) + if (exitcode == 0) { + /* suppress 'error' message */ + return 1; + } + else { + /* STS$M_INHIB_MSG + SS$_ABORT */ + return 0x1000002c; + } +#else + return exitcode; +#endif + /*NOTREACHED*/ +} + + +/* Main program */ + +int +Py_Main(int argc, char **argv) +{ + int c; + int sts; + char *command = NULL; + char *filename = NULL; + FILE *fp = stdin; + char *p; + int inspect = 0; + int unbuffered = 0; + int skipfirstline = 0; + int stdin_is_interactive = 0; + int help = 0; + int version = 0; + int saw_inspect_flag = 0; + int saw_unbuffered_flag = 0; + PyCompilerFlags cf; + + cf.cf_flags = 0; + + orig_argc = argc; /* For Py_GetArgcArgv() */ + orig_argv = argv; + +#ifdef RISCOS + Py_RISCOSWimpFlag = 0; +#endif + + PySys_ResetWarnOptions(); + + while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) { + if (c == 'c') { + /* -c is the last option; following arguments + that look like options are left for the + command to interpret. */ + command = malloc(strlen(_PyOS_optarg) + 2); + if (command == NULL) + Py_FatalError( + "not enough memory to copy -c argument"); + strcpy(command, _PyOS_optarg); + strcat(command, "\n"); + break; + } + + switch (c) { + + case 'd': + Py_DebugFlag++; + break; + + case 'Q': + if (strcmp(_PyOS_optarg, "old") == 0) { + Py_DivisionWarningFlag = 0; + break; + } + if (strcmp(_PyOS_optarg, "warn") == 0) { + Py_DivisionWarningFlag = 1; + break; + } + if (strcmp(_PyOS_optarg, "warnall") == 0) { + Py_DivisionWarningFlag = 2; + break; + } + if (strcmp(_PyOS_optarg, "new") == 0) { + /* This only affects __main__ */ + cf.cf_flags |= CO_FUTURE_DIVISION; + /* And this tells the eval loop to treat + BINARY_DIVIDE as BINARY_TRUE_DIVIDE */ + _Py_QnewFlag = 1; + break; + } + fprintf(stderr, + "-Q option should be `-Qold', " + "`-Qwarn', `-Qwarnall', or `-Qnew' only\n"); + return usage(2, argv[0]); + /* NOTREACHED */ + + case 'i': + inspect++; + saw_inspect_flag = 1; + Py_InteractiveFlag++; + break; + + case 'O': + Py_OptimizeFlag++; + break; + + case 'S': + Py_NoSiteFlag++; + break; + + case 'E': + Py_IgnoreEnvironmentFlag++; + break; + + case 't': + Py_TabcheckFlag++; + break; + + case 'u': + unbuffered++; + saw_unbuffered_flag = 1; + break; + + case 'v': + Py_VerboseFlag++; + break; + +#ifdef RISCOS + case 'w': + Py_RISCOSWimpFlag = 1; + break; +#endif + + case 'x': + skipfirstline = 1; + break; + + case 'U': + Py_UnicodeFlag++; + break; + case 'h': + help++; + break; + case 'V': + version++; + break; + + case 'W': + PySys_AddWarnOption(_PyOS_optarg); + break; + + /* This space reserved for other options */ + + default: + return usage(2, argv[0]); + /*NOTREACHED*/ + + } + } + + if (help) + return usage(0, argv[0]); + + if (version) { + fprintf(stderr, "Python %s\n", PY_VERSION); + return 0; + } + + if (!saw_inspect_flag && + (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') + inspect = 1; + if (!saw_unbuffered_flag && + (p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0') + unbuffered = 1; + + if (command == NULL && _PyOS_optind < argc && + strcmp(argv[_PyOS_optind], "-") != 0) + { +#ifdef __VMS + filename = decc$translate_vms(argv[_PyOS_optind]); + if (filename == (char *)0 || filename == (char *)-1) + filename = argv[_PyOS_optind]; + +#else + filename = argv[_PyOS_optind]; +#endif + if (filename != NULL) { + if ((fp = fopen(filename, "r")) == NULL) { + fprintf(stderr, "%s: can't open file '%s'\n", + argv[0], filename); + return 2; + } + else if (skipfirstline) { + int ch; + /* Push back first newline so line numbers + remain the same */ + while ((ch = getc(fp)) != EOF) { + if (ch == '\n') { + (void)ungetc(ch, fp); + break; + } + } + } + } + } + + stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0); + + if (unbuffered) { +#if defined(MS_WINDOWS) || defined(__CYGWIN__) + _setmode(fileno(stdin), O_BINARY); + _setmode(fileno(stdout), O_BINARY); +#endif +#ifndef MPW +#ifdef HAVE_SETVBUF + setvbuf(stdin, (char *)NULL, _IONBF, BUFSIZ); + setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ); + setvbuf(stderr, (char *)NULL, _IONBF, BUFSIZ); +#else /* !HAVE_SETVBUF */ + setbuf(stdin, (char *)NULL); + setbuf(stdout, (char *)NULL); + setbuf(stderr, (char *)NULL); +#endif /* !HAVE_SETVBUF */ +#else /* MPW */ + /* On MPW (3.2) unbuffered seems to hang */ + setvbuf(stdin, (char *)NULL, _IOLBF, BUFSIZ); + setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ); + setvbuf(stderr, (char *)NULL, _IOLBF, BUFSIZ); +#endif /* MPW */ + } + else if (Py_InteractiveFlag) { +#ifdef MS_WINDOWS + /* Doesn't have to have line-buffered -- use unbuffered */ + /* Any set[v]buf(stdin, ...) screws up Tkinter :-( */ + setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ); +#else /* !MS_WINDOWS */ +#ifdef HAVE_SETVBUF + setvbuf(stdin, (char *)NULL, _IOLBF, BUFSIZ); + setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ); +#endif /* HAVE_SETVBUF */ +#endif /* !MS_WINDOWS */ + /* Leave stderr alone - it should be unbuffered anyway. */ + } +#ifdef __VMS + else { + setvbuf (stdout, (char *)NULL, _IOLBF, BUFSIZ); + } +#endif /* __VMS */ + +#ifdef __APPLE__ + /* On MacOS X, when the Python interpreter is embedded in an + application bundle, it gets executed by a bootstrapping script + that does os.execve() with an argv[0] that's different from the + actual Python executable. This is needed to keep the Finder happy, + or rather, to work around Apple's overly strict requirements of + the process name. However, we still need a usable sys.executable, + so the actual executable path is passed in an environment variable. + See Lib/plat-mac/bundlebuiler.py for details about the bootstrap + script. */ + if ((p = Py_GETENV("PYTHONEXECUTABLE")) && *p != '\0') + Py_SetProgramName(p); + else + Py_SetProgramName(argv[0]); +#else + Py_SetProgramName(argv[0]); +#endif + Py_Initialize(); + + if (Py_VerboseFlag || + (command == NULL && filename == NULL && stdin_is_interactive)) { + fprintf(stderr, "Python %s on %s\n", + Py_GetVersion(), Py_GetPlatform()); + if (!Py_NoSiteFlag) + fprintf(stderr, "%s\n", COPYRIGHT); + } + + if (command != NULL) { + /* Backup _PyOS_optind and force sys.argv[0] = '-c' */ + _PyOS_optind--; + argv[_PyOS_optind] = "-c"; + } + + PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind); + + if ((inspect || (command == NULL && filename == NULL)) && + isatty(fileno(stdin))) { + PyObject *v; + v = PyImport_ImportModule("readline"); + if (v == NULL) + PyErr_Clear(); + else + Py_DECREF(v); + } + + if (command) { + sts = PyRun_SimpleStringFlags(command, &cf) != 0; + free(command); + } + else { + if (filename == NULL && stdin_is_interactive) { + char *startup = Py_GETENV("PYTHONSTARTUP"); + if (startup != NULL && startup[0] != '\0') { + FILE *fp = fopen(startup, "r"); + if (fp != NULL) { + (void) PyRun_SimpleFile(fp, startup); + PyErr_Clear(); + fclose(fp); + } + } + } + /* XXX */ + sts = PyRun_AnyFileExFlags( + fp, + filename == NULL ? "" : filename, + filename != NULL, &cf) != 0; + } + + /* Check this environment variable at the end, to give programs the + * opportunity to set it from Python. + */ + if (!saw_inspect_flag && + (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') + { + inspect = 1; + } + + if (inspect && stdin_is_interactive && + (filename != NULL || command != NULL)) + /* XXX */ + sts = PyRun_AnyFileFlags(stdin, "", &cf) != 0; + + Py_Finalize(); +#ifdef RISCOS + if (Py_RISCOSWimpFlag) + fprintf(stderr, "\x0cq\x0c"); /* make frontend quit */ +#endif + +#ifdef __INSURE__ + /* Insure++ is a memory analysis tool that aids in discovering + * memory leaks and other memory problems. On Python exit, the + * interned string dictionary is flagged as being in use at exit + * (which it is). Under normal circumstances, this is fine because + * the memory will be automatically reclaimed by the system. Under + * memory debugging, it's a huge source of useless noise, so we + * trade off slower shutdown for less distraction in the memory + * reports. -baw + */ + _Py_ReleaseInternedStrings(); +#endif /* __INSURE__ */ + + return sts; +} + + +/* Make the *original* argc/argv available to other modules. + This is rare, but it is needed by the secureware extension. */ + +void +Py_GetArgcArgv(int *argc, char ***argv) +{ + *argc = orig_argc; + *argv = orig_argv; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/makesetup b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/makesetup new file mode 100644 index 00000000..50edc232 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/makesetup @@ -0,0 +1,297 @@ +#! /bin/sh + +# Convert templates into Makefile and config.c, based on the module +# definitions found in the file Setup. +# +# Usage: makesetup [-s dir] [-c file] [-m file] [Setup] ... [-n [Setup] ...] +# +# Options: +# -s directory: alternative source directory (default .) +# -l directory: library source directory (default derived from $0) +# -c file: alternative config.c template (default $libdir/config.c.in) +# -c -: don't write config.c +# -m file: alternative Makefile template (default ./Makefile.pre) +# -m -: don't write Makefile +# +# Remaining arguments are one or more Setup files (default ./Setup). +# Setup files after a -n option are used for their variables, modules +# and libraries but not for their .o files. +# +# See Setup.dist for a description of the format of the Setup file. +# +# The following edits are made: +# +# Copying config.c.in to config.c: +# - insert an identifying comment at the start +# - for each mentioned in Setup before *noconfig*: +# + insert 'extern void init(void);' before MARKER 1 +# + insert '{"", initmodule},' before MARKER 2 +# +# Copying Makefile.pre to Makefile: +# - insert an identifying comment at the start +# - replace _MODOBJS_ by the list of objects from Setup (except for +# Setup files after a -n option) +# - replace _MODLIBS_ by the list of libraries from Setup +# - for each object file mentioned in Setup, append a rule +# '.o: .c; ' to the end of the Makefile +# - for each module mentioned in Setup, append a rule +# which creates a shared library version to the end of the Makefile +# - for each variable definition found in Setup, insert the definition +# before the comment 'Definitions added by makesetup' + +# Loop over command line options +usage=' +usage: makesetup [-s srcdir] [-l libdir] [-c config.c.in] [-m Makefile.pre] + [Setup] ... [-n [Setup] ...]' +srcdir='.' +libdir='' +config='' +makepre='' +noobjects='' +doconfig=yes +while : +do + case $1 in + -s) shift; srcdir=$1; shift;; + -l) shift; libdir=$1; shift;; + -c) shift; config=$1; shift;; + -m) shift; makepre=$1; shift;; + --) shift; break;; + -n) noobjects=yes;; + -*) echo "$usage" 1>&2; exit 2;; + *) break;; + esac +done + +# Set default libdir and config if not set by command line +# (Not all systems have dirname) +case $libdir in +'') case $0 in + */*) libdir=`echo $0 | sed 's,/[^/]*$,,'`;; + *) libdir=.;; + esac;; +esac +case $config in +'') config=$libdir/config.c.in;; +esac +case $makepre in +'') makepre=Makefile.pre;; +esac + +# Newline for sed i and a commands +NL='\ +' + +# Setup to link with extra libraries when makeing shared extensions. +# Currently, only Cygwin needs this baggage. +case `uname -s` in +CYGWIN*) if test $libdir = . + then + ExtraLibDir=. + else + ExtraLibDir='$(LIBPL)' + fi + ExtraLibs="-L$ExtraLibDir -lpython\$(VERSION)";; +esac + +# Main loop +for i in ${*-Setup} +do + case $i in + -n) echo '*noobjects*';; + *) echo '*doconfig*'; cat "$i";; + esac +done | +sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | +( + rulesf="@rules.$$" + trap 'rm -f $rulesf' 0 1 2 3 + echo " +# Rules appended by makedepend +" >$rulesf + DEFS= + MODS= + SHAREDMODS= + OBJS= + LIBS= + LOCALLIBS= + BASELIBS= + while read line + do + # to handle backslashes for sh's that don't automatically + # continue a read when the last char is a backslash + while echo $line | grep '\\$' > /dev/null + do + read extraline + line=`echo $line| sed s/.$//`$extraline + done + + # Output DEFS in reverse order so first definition overrides + case $line in + *=*) DEFS="$line$NL$DEFS"; continue;; + 'include '*) DEFS="$line$NL$DEFS"; continue;; + '*noobjects*') + case $noobjects in + yes) ;; + *) LOCALLIBS=$LIBS; LIBS=;; + esac + noobjects=yes; + continue;; + '*doconfig*') doconfig=yes; continue;; + '*static*') doconfig=yes; continue;; + '*noconfig*') doconfig=no; continue;; + '*shared*') doconfig=no; continue;; + esac + srcs= + cpps= + libs= + mods= + skip= + for arg in $line + do + case $skip in + libs) libs="$libs $arg"; skip=; continue;; + cpps) cpps="$cpps $arg"; skip=; continue;; + srcs) srcs="$srcs $arg"; skip=; continue;; + esac + case $arg in + -framework) libs="$libs $arg"; skip=libs; + # OSX/OSXS/Darwin framework link cmd + ;; + -[IDUCfF]*) cpps="$cpps $arg";; + -Xcompiler) skip=cpps;; + -Xlinker) libs="$libs $arg"; skip=libs;; + -rpath) libs="$libs $arg"; skip=libs;; + --rpath) libs="$libs $arg"; skip=libs;; + -[A-Zl]*) libs="$libs $arg";; + *.a) libs="$libs $arg";; + *.so) libs="$libs $arg";; + *.sl) libs="$libs $arg";; + /*.o) libs="$libs $arg";; + *.def) libs="$libs $arg";; + *.o) srcs="$srcs `basename $arg .o`.c";; + *.[cC]) srcs="$srcs $arg";; + *.m) srcs="$srcs $arg";; # Objective-C src + *.cc) srcs="$srcs $arg";; + *.c++) srcs="$srcs $arg";; + *.cxx) srcs="$srcs $arg";; + *.cpp) srcs="$srcs $arg";; + \$*) libs="$libs $arg" + cpps="$cpps $arg";; + *.*) echo 1>&2 "bad word $arg in $line" + exit 1;; + -u) skip=libs; libs="$libs -u";; + [a-zA-Z_]*) mods="$mods $arg";; + *) echo 1>&2 "bad word $arg in $line" + exit 1;; + esac + done + case $doconfig in + yes) + LIBS="$LIBS $libs" + MODS="$MODS $mods" + ;; + esac + case $noobjects in + yes) continue;; + esac + objs='' + for src in $srcs + do + case $src in + *.c) obj=`basename $src .c`.o; cc='$(CC)';; + *.cc) obj=`basename $src .cc`.o; cc='$(CXX)';; + *.c++) obj=`basename $src .c++`.o; cc='$(CXX)';; + *.C) obj=`basename $src .C`.o; cc='$(CXX)';; + *.cxx) obj=`basename $src .cxx`.o; cc='$(CXX)';; + *.cpp) obj=`basename $src .cpp`.o; cc='$(CXX)';; + *.m) obj=`basename $src .m`.o; cc='$(CC)';; # Obj-C + *) continue;; + esac + obj="$srcdir/$obj" + objs="$objs $obj" + case $src in + glmodule.c) ;; + /*) ;; + \$*) ;; + *) src='$(srcdir)/'"$srcdir/$src";; + esac + case $doconfig in + no) cc="$cc \$(CCSHARED) \$(CFLAGS) \$(CPPFLAGS)";; + *) + cc="$cc \$(PY_CFLAGS)";; + esac + rule="$obj: $src; $cc $cpps -c $src -o $obj" + echo "$rule" >>$rulesf + done + case $doconfig in + yes) OBJS="$OBJS $objs";; + esac + for mod in $mods + do + case $objs in + *$mod.o*) base=$mod;; + *) base=${mod}module;; + esac + file="$srcdir/$base\$(SO)" + case $doconfig in + no) SHAREDMODS="$SHAREDMODS $file";; + esac + rule="$file: $objs" + rule="$rule; \$(LDSHARED) $objs $libs $ExtraLibs -o $file" + echo "$rule" >>$rulesf + done + done + + case $SHAREDMODS in + '') ;; + *) DEFS="SHAREDMODS=$SHAREDMODS$NL$DEFS";; + esac + + case $noobjects in + yes) BASELIBS=$LIBS;; + *) LOCALLIBS=$LIBS;; + esac + LIBS='$(LOCALMODLIBS) $(BASEMODLIBS)' + DEFS="BASEMODLIBS=$BASELIBS$NL$DEFS" + DEFS="LOCALMODLIBS=$LOCALLIBS$NL$DEFS" + + EXTDECLS= + INITBITS= + for mod in $MODS + do + EXTDECLS="${EXTDECLS}extern void init$mod(void);$NL" + INITBITS="${INITBITS} {\"$mod\", init$mod},$NL" + done + + + case $config in + -) ;; + *) sed -e " + 1i$NL/* Generated automatically from $config by makesetup. */ + /MARKER 1/i$NL$EXTDECLS + + /MARKER 2/i$NL$INITBITS + + " $config >config.c + ;; + esac + + case $makepre in + -) ;; + *) sedf="@sed.in.$$" + trap 'rm -f $sedf' 0 1 2 3 + echo "1i\\" >$sedf + str="# Generated automatically from $makepre by makesetup." + echo "$str" >>$sedf + echo "s%_MODOBJS_%$OBJS%" >>$sedf + echo "s%_MODLIBS_%$LIBS%" >>$sedf + echo "/Definitions added by makesetup/a$NL$NL$DEFS" >>$sedf + sed -f $sedf $makepre >Makefile + cat $rulesf >>Makefile + rm -f $sedf + ;; + esac + + rm -f $rulesf +) diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/makexp_aix b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/makexp_aix new file mode 100644 index 00000000..10f680a9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/makexp_aix @@ -0,0 +1,75 @@ +#!/bin/sh +# +# =========================================================================== +# FILE: makexp_aix +# TYPE: standalone executable +# SYSTEM: AIX 3.2.5 and AIX 4 +# +# DESCRIPTION: This script creates an export list of ALL global symbols +# from a list of object or archive files. +# +# USAGE: makexp_aix "" ... +# +# where: +# is the target export list filename. +# is the path/file string to be appended +# after the "#!" symbols in the first line of the +# export file. Passing "" means deferred resolution. +# is an object (.o) or an archive file (.a). +# +# HISTORY: +# 3-Apr-1998 -- remove C++ entries of the form Class::method +# Vladimir Marangozov +# +# 1-Jul-1996 -- added header information +# Vladimir Marangozov +# +# 28-Jun-1996 -- initial code +# Vladimir Marangozov (Vladimir.Marangozov@imag.fr) +# ========================================================================== + +# Variables +expFileName=$1 +toAppendStr=$2 +shift; shift; +inputFiles=$* +automsg="Generated automatically by makexp_aix" +notemsg="NOTE: lists _all_ global symbols defined in the above file(s)." +curwdir=`pwd` + +# Create the export file and setup the header info +echo "#!"$toAppendStr > $expFileName +echo "*" >> $expFileName +echo "* $automsg (`date -u`)" >> $expFileName +echo "*" >> $expFileName +echo "* Base Directory: $curwdir" >> $expFileName +echo "* Input File(s) : $inputFiles" >> $expFileName +echo "*" >> $expFileName +echo "* $notemsg" >> $expFileName +echo "*" >> $expFileName + +# Extract the symbol list using 'nm' which produces quite +# different output under AIX 4 than under AIX 3.2.5. +# The following handles both versions by using a common flagset. +# Here are some hidden tricks: +# 1. Use /usr/ccs/bin/nm. Relevant to AIX 3.2.5 which has +# another version under /usr/ucb/bin/nm. +# 2. Use the -B flag to have a standard BSD representation +# of the symbol list on both AIX 3.2.5 and AIX 4. The "-B" +# flag is missing in the AIX 3.2.5 online usage help of 'nm'. +# 3. Use the -x flag to have a hex representation of the symbol +# values. This fills the leading whitespaces on AIX 4, +# thus simplifying the sed statement. +# 4. Eliminate all entries except those with either "B", "D" +# or "T" key letters. We are interested only in the global +# (extern) BSS, DATA and TEXT symbols. With the same statement +# we eliminate object member lines relevant to AIX 4. +# 5. Eliminate entries containing a dot. We can have a dot only +# as a symbol prefix, but such symbols are undefined externs. +# 6. Eliminate everything including the key letter, so that we're +# left with just the symbol name. +# 7. Eliminate all entries containing two colons, like Class::method +# +/usr/ccs/bin/nm -Bex $inputFiles \ +| sed -e '/ [^BDT] /d' -e '/\./d' -e 's/.* [BDT] //' -e '/::/d' \ +| sort | uniq >> $expFileName diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mathmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mathmodule.c new file mode 100644 index 00000000..251db130 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mathmodule.c @@ -0,0 +1,391 @@ +/* Math module -- standard C math library functions, pi and e */ + +#include "Python.h" +#include "longintrepr.h" + +#ifndef _MSC_VER +#ifndef __STDC__ +extern double fmod (double, double); +extern double frexp (double, int *); +extern double ldexp (double, int); +extern double modf (double, double *); +#endif /* __STDC__ */ +#endif /* _MSC_VER */ + +/* Call is_error when errno != 0, and where x is the result libm + * returned. is_error will usually set up an exception and return + * true (1), but may return false (0) without setting up an exception. + */ +static int +is_error(double x) +{ + int result = 1; /* presumption of guilt */ + assert(errno); /* non-zero errno is a precondition for calling */ + if (errno == EDOM) + PyErr_SetString(PyExc_ValueError, "math domain error"); + + else if (errno == ERANGE) { + /* ANSI C generally requires libm functions to set ERANGE + * on overflow, but also generally *allows* them to set + * ERANGE on underflow too. There's no consistency about + * the latter across platforms. + * Alas, C99 never requires that errno be set. + * Here we suppress the underflow errors (libm functions + * should return a zero on underflow, and +- HUGE_VAL on + * overflow, so testing the result for zero suffices to + * distinguish the cases). + */ + if (x) + PyErr_SetString(PyExc_OverflowError, + "math range error"); + else + result = 0; + } + else + /* Unexpected math error */ + PyErr_SetFromErrno(PyExc_ValueError); + return result; +} + +static PyObject * +math_1(PyObject *args, double (*func) (double), char *argsfmt) +{ + double x; + if (! PyArg_ParseTuple(args, argsfmt, &x)) + return NULL; + errno = 0; + PyFPE_START_PROTECT("in math_1", return 0) + x = (*func)(x); + PyFPE_END_PROTECT(x) + Py_SET_ERANGE_IF_OVERFLOW(x); + if (errno && is_error(x)) + return NULL; + else + return PyFloat_FromDouble(x); +} + +static PyObject * +math_2(PyObject *args, double (*func) (double, double), char *argsfmt) +{ + double x, y; + if (! PyArg_ParseTuple(args, argsfmt, &x, &y)) + return NULL; + errno = 0; + PyFPE_START_PROTECT("in math_2", return 0) + x = (*func)(x, y); + PyFPE_END_PROTECT(x) + Py_SET_ERANGE_IF_OVERFLOW(x); + if (errno && is_error(x)) + return NULL; + else + return PyFloat_FromDouble(x); +} + +#define FUNC1(funcname, func, docstring) \ + static PyObject * math_##funcname(PyObject *self, PyObject *args) { \ + return math_1(args, func, "d:" #funcname); \ + }\ + PyDoc_STRVAR(math_##funcname##_doc, docstring); + +#define FUNC2(funcname, func, docstring) \ + static PyObject * math_##funcname(PyObject *self, PyObject *args) { \ + return math_2(args, func, "dd:" #funcname); \ + }\ + PyDoc_STRVAR(math_##funcname##_doc, docstring); + +FUNC1(acos, acos, + "acos(x)\n\nReturn the arc cosine (measured in radians) of x.") +FUNC1(asin, asin, + "asin(x)\n\nReturn the arc sine (measured in radians) of x.") +FUNC1(atan, atan, + "atan(x)\n\nReturn the arc tangent (measured in radians) of x.") +FUNC2(atan2, atan2, + "atan2(y, x)\n\nReturn the arc tangent (measured in radians) of y/x.\n" + "Unlike atan(y/x), the signs of both x and y are considered.") +FUNC1(ceil, ceil, + "ceil(x)\n\nReturn the ceiling of x as a float.\n" + "This is the smallest integral value >= x.") +FUNC1(cos, cos, + "cos(x)\n\nReturn the cosine of x (measured in radians).") +FUNC1(cosh, cosh, + "cosh(x)\n\nReturn the hyperbolic cosine of x.") +FUNC1(exp, exp, + "exp(x)\n\nReturn e raised to the power of x.") +FUNC1(fabs, fabs, + "fabs(x)\n\nReturn the absolute value of the float x.") +FUNC1(floor, floor, + "floor(x)\n\nReturn the floor of x as a float.\n" + "This is the largest integral value <= x.") +FUNC2(fmod, fmod, + "fmod(x,y)\n\nReturn fmod(x, y), according to platform C." + " x % y may differ.") +FUNC2(hypot, hypot, + "hypot(x,y)\n\nReturn the Euclidean distance, sqrt(x*x + y*y).") +#ifdef MPW_3_1 /* This hack is needed for MPW 3.1 but not for 3.2 ... */ +FUNC2(pow, power, + "pow(x,y)\n\nReturn x**y (x to the power of y).") +#else +FUNC2(pow, pow, + "pow(x,y)\n\nReturn x**y (x to the power of y).") +#endif +FUNC1(sin, sin, + "sin(x)\n\nReturn the sine of x (measured in radians).") +FUNC1(sinh, sinh, + "sinh(x)\n\nReturn the hyperbolic sine of x.") +FUNC1(sqrt, sqrt, + "sqrt(x)\n\nReturn the square root of x.") +FUNC1(tan, tan, + "tan(x)\n\nReturn the tangent of x (measured in radians).") +FUNC1(tanh, tanh, + "tanh(x)\n\nReturn the hyperbolic tangent of x.") + +static PyObject * +math_frexp(PyObject *self, PyObject *args) +{ + double x; + int i; + if (! PyArg_ParseTuple(args, "d:frexp", &x)) + return NULL; + errno = 0; + x = frexp(x, &i); + Py_SET_ERANGE_IF_OVERFLOW(x); + if (errno && is_error(x)) + return NULL; + else + return Py_BuildValue("(di)", x, i); +} + +PyDoc_STRVAR(math_frexp_doc, +"frexp(x)\n" +"\n" +"Return the mantissa and exponent of x, as pair (m, e).\n" +"m is a float and e is an int, such that x = m * 2.**e.\n" +"If x is 0, m and e are both 0. Else 0.5 <= abs(m) < 1.0."); + +static PyObject * +math_ldexp(PyObject *self, PyObject *args) +{ + double x; + int exp; + if (! PyArg_ParseTuple(args, "di:ldexp", &x, &exp)) + return NULL; + errno = 0; + PyFPE_START_PROTECT("ldexp", return 0) + x = ldexp(x, exp); + PyFPE_END_PROTECT(x) + Py_SET_ERANGE_IF_OVERFLOW(x); + if (errno && is_error(x)) + return NULL; + else + return PyFloat_FromDouble(x); +} + +PyDoc_STRVAR(math_ldexp_doc, +"ldexp(x, i) -> x * (2**i)"); + +static PyObject * +math_modf(PyObject *self, PyObject *args) +{ + double x, y; + if (! PyArg_ParseTuple(args, "d:modf", &x)) + return NULL; + errno = 0; +#ifdef MPW /* MPW C modf expects pointer to extended as second argument */ + { + extended e; + x = modf(x, &e); + y = e; + } +#else + x = modf(x, &y); +#endif + Py_SET_ERANGE_IF_OVERFLOW(x); + if (errno && is_error(x)) + return NULL; + else + return Py_BuildValue("(dd)", x, y); +} + +PyDoc_STRVAR(math_modf_doc, +"modf(x)\n" +"\n" +"Return the fractional and integer parts of x. Both results carry the sign\n" +"of x. The integer part is returned as a real."); + +/* A decent logarithm is easy to compute even for huge longs, but libm can't + do that by itself -- loghelper can. func is log or log10, and name is + "log" or "log10". Note that overflow isn't possible: a long can contain + no more than INT_MAX * SHIFT bits, so has value certainly less than + 2**(2**64 * 2**16) == 2**2**80, and log2 of that is 2**80, which is + small enough to fit in an IEEE single. log and log10 are even smaller. +*/ + +static PyObject* +loghelper(PyObject* args, double (*func)(double), char *format, PyObject *arg) +{ + /* If it is long, do it ourselves. */ + if (PyLong_Check(arg)) { + double x; + int e; + x = _PyLong_AsScaledDouble(arg, &e); + if (x <= 0.0) { + PyErr_SetString(PyExc_ValueError, + "math domain error"); + return NULL; + } + /* Value is ~= x * 2**(e*SHIFT), so the log ~= + log(x) + log(2) * e * SHIFT. + CAUTION: e*SHIFT may overflow using int arithmetic, + so force use of double. */ + x = func(x) + (e * (double)SHIFT) * func(2.0); + return PyFloat_FromDouble(x); + } + + /* Else let libm handle it by itself. */ + return math_1(args, func, format); +} + +static PyObject * +math_log(PyObject *self, PyObject *args) +{ + PyObject *arg; + PyObject *base = NULL; + PyObject *num, *den; + PyObject *ans; + PyObject *newargs; + + if (!PyArg_UnpackTuple(args, "log", 1, 2, &arg, &base)) + return NULL; + if (base == NULL) + return loghelper(args, log, "d:log", arg); + + newargs = PyTuple_New(1); + if (newargs == NULL) + return NULL; + Py_INCREF(arg); + PyTuple_SET_ITEM(newargs, 0, arg); + num = loghelper(newargs, log, "d:log", arg); + Py_DECREF(newargs); + if (num == NULL) + return NULL; + + newargs = PyTuple_New(1); + if (newargs == NULL) { + Py_DECREF(num); + return NULL; + } + Py_INCREF(base); + PyTuple_SET_ITEM(newargs, 0, base); + den = loghelper(newargs, log, "d:log", base); + Py_DECREF(newargs); + if (den == NULL) { + Py_DECREF(num); + return NULL; + } + + ans = PyNumber_Divide(num, den); + Py_DECREF(num); + Py_DECREF(den); + return ans; +} + +PyDoc_STRVAR(math_log_doc, +"log(x[, base]) -> the logarithm of x to the given base.\n\ +If the base not specified, returns the natural logarithm (base e) of x."); + +static PyObject * +math_log10(PyObject *self, PyObject *args) +{ + PyObject *arg; + + if (!PyArg_UnpackTuple(args, "log10", 1, 1, &arg)) + return NULL; + return loghelper(args, log10, "d:log10", arg); +} + +PyDoc_STRVAR(math_log10_doc, +"log10(x) -> the base 10 logarithm of x."); + +static const double degToRad = 3.141592653589793238462643383 / 180.0; + +static PyObject * +math_degrees(PyObject *self, PyObject *args) +{ + double x; + if (! PyArg_ParseTuple(args, "d:degrees", &x)) + return NULL; + return PyFloat_FromDouble(x / degToRad); +} + +PyDoc_STRVAR(math_degrees_doc, +"degrees(x) -> converts angle x from radians to degrees"); + +static PyObject * +math_radians(PyObject *self, PyObject *args) +{ + double x; + if (! PyArg_ParseTuple(args, "d:radians", &x)) + return NULL; + return PyFloat_FromDouble(x * degToRad); +} + +PyDoc_STRVAR(math_radians_doc, +"radians(x) -> converts angle x from degrees to radians"); + +static PyMethodDef math_methods[] = { + {"acos", math_acos, METH_VARARGS, math_acos_doc}, + {"asin", math_asin, METH_VARARGS, math_asin_doc}, + {"atan", math_atan, METH_VARARGS, math_atan_doc}, + {"atan2", math_atan2, METH_VARARGS, math_atan2_doc}, + {"ceil", math_ceil, METH_VARARGS, math_ceil_doc}, + {"cos", math_cos, METH_VARARGS, math_cos_doc}, + {"cosh", math_cosh, METH_VARARGS, math_cosh_doc}, + {"degrees", math_degrees, METH_VARARGS, math_degrees_doc}, + {"exp", math_exp, METH_VARARGS, math_exp_doc}, + {"fabs", math_fabs, METH_VARARGS, math_fabs_doc}, + {"floor", math_floor, METH_VARARGS, math_floor_doc}, + {"fmod", math_fmod, METH_VARARGS, math_fmod_doc}, + {"frexp", math_frexp, METH_VARARGS, math_frexp_doc}, + {"hypot", math_hypot, METH_VARARGS, math_hypot_doc}, + {"ldexp", math_ldexp, METH_VARARGS, math_ldexp_doc}, + {"log", math_log, METH_VARARGS, math_log_doc}, + {"log10", math_log10, METH_VARARGS, math_log10_doc}, + {"modf", math_modf, METH_VARARGS, math_modf_doc}, + {"pow", math_pow, METH_VARARGS, math_pow_doc}, + {"radians", math_radians, METH_VARARGS, math_radians_doc}, + {"sin", math_sin, METH_VARARGS, math_sin_doc}, + {"sinh", math_sinh, METH_VARARGS, math_sinh_doc}, + {"sqrt", math_sqrt, METH_VARARGS, math_sqrt_doc}, + {"tan", math_tan, METH_VARARGS, math_tan_doc}, + {"tanh", math_tanh, METH_VARARGS, math_tanh_doc}, + {NULL, NULL} /* sentinel */ +}; + + +PyDoc_STRVAR(module_doc, +"This module is always available. It provides access to the\n" +"mathematical functions defined by the C standard."); + +PyMODINIT_FUNC +initmath(void) +{ + PyObject *m, *d, *v; + + m = Py_InitModule3("math", math_methods, module_doc); + d = PyModule_GetDict(m); + + if (!(v = PyFloat_FromDouble(atan(1.0) * 4.0))) + goto finally; + if (PyDict_SetItemString(d, "pi", v) < 0) + goto finally; + Py_DECREF(v); + + if (!(v = PyFloat_FromDouble(exp(1.0)))) + goto finally; + if (PyDict_SetItemString(d, "e", v) < 0) + goto finally; + Py_DECREF(v); + + finally: + return; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5.h new file mode 100644 index 00000000..a2cee425 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5.h @@ -0,0 +1,62 @@ +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* ========== include global.h ========== */ +/* GLOBAL.H - RSAREF types and constants + */ + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT4 defines a four byte word */ +#if SIZEOF_LONG == 4 +typedef unsigned long int UINT4; +#elif SIZEOF_SHORT == 4 +typedef unsigned short int UINT4; +#elif INT_MAX == 2147483647 +typedef unsigned int UINT4; +#else +#error "Can't find a 4-byte integral type" +#endif + +/* ========== End global.h; continue md5.h ========== */ + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +/* Rename all exported symbols to avoid conflicts with similarly named + symbols in some systems' standard C libraries... */ + +#define MD5Init _Py_MD5Init +#define MD5Update _Py_MD5Update +#define MD5Final _Py_MD5Final + +void MD5Init(MD5_CTX *); +void MD5Update(MD5_CTX *, unsigned char *, unsigned int); +void MD5Final(unsigned char [16], MD5_CTX *); diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5c.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5c.c new file mode 100644 index 00000000..02919367 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5c.c @@ -0,0 +1,290 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#include "Python.h" +#include "md5.h" + +/* Constants for MD5Transform routine. */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform(UINT4[4], unsigned char[64]); +static void Encode(unsigned char *, UINT4 *, unsigned int); +static void Decode(UINT4 *, unsigned char *, unsigned int); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + + +/* MD5 initialization. Begins an MD5 operation, writing a new context. */ +void +MD5Init(MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void +MD5Update(MD5_CTX *context, unsigned char *input, unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) { + memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy((POINTER)&context->buffer[index], + (POINTER)&input[i], inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + message digest and zeroing the context. + */ +void +MD5Final(unsigned char digest[16], MD5_CTX *context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update(context, bits, 8); + + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* Zeroize sensitive information. */ + memset((POINTER)context, 0, sizeof (*context)); +} + + +/* MD5 basic transformation. Transforms state based on block. */ +static void +MD5Transform(UINT4 state[4], unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF(b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF(b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF(a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF(d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG(a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG(b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH(a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH(a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II(a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II(d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II(b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II(d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II(b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II(a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + memset((POINTER)x, 0, sizeof (x)); +} + + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void +Encode(unsigned char *output, UINT4 *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void +Decode(UINT4 *output, unsigned char *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5module.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5module.c new file mode 100644 index 00000000..587fefe3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/md5module.c @@ -0,0 +1,268 @@ + +/* MD5 module */ + +/* This module provides an interface to the RSA Data Security, + Inc. MD5 Message-Digest Algorithm, described in RFC 1321. + It requires the files md5c.c and md5.h (which are slightly changed + from the versions in the RFC to avoid the "global.h" file.) */ + + +/* MD5 objects */ + +#include "Python.h" +#include "md5.h" + +typedef struct { + PyObject_HEAD + MD5_CTX md5; /* the context holder */ +} md5object; + +static PyTypeObject MD5type; + +#define is_md5object(v) ((v)->ob_type == &MD5type) + +static md5object * +newmd5object(void) +{ + md5object *md5p; + + md5p = PyObject_New(md5object, &MD5type); + if (md5p == NULL) + return NULL; + + MD5Init(&md5p->md5); /* actual initialisation */ + return md5p; +} + + +/* MD5 methods */ + +static void +md5_dealloc(md5object *md5p) +{ + PyObject_Del(md5p); +} + + +/* MD5 methods-as-attributes */ + +static PyObject * +md5_update(md5object *self, PyObject *args) +{ + unsigned char *cp; + int len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + MD5Update(&self->md5, cp, len); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(update_doc, +"update (arg)\n\ +\n\ +Update the md5 object with the string arg. Repeated calls are\n\ +equivalent to a single call with the concatenation of all the\n\ +arguments."); + + +static PyObject * +md5_digest(md5object *self) +{ + MD5_CTX mdContext; + unsigned char aDigest[16]; + + /* make a temporary copy, and perform the final */ + mdContext = self->md5; + MD5Final(aDigest, &mdContext); + + return PyString_FromStringAndSize((char *)aDigest, 16); +} + +PyDoc_STRVAR(digest_doc, +"digest() -> string\n\ +\n\ +Return the digest of the strings passed to the update() method so\n\ +far. This is a 16-byte string which may contain non-ASCII characters,\n\ +including null bytes."); + + +static PyObject * +md5_hexdigest(md5object *self) +{ + MD5_CTX mdContext; + unsigned char digest[16]; + unsigned char hexdigest[32]; + int i, j; + + /* make a temporary copy, and perform the final */ + mdContext = self->md5; + MD5Final(digest, &mdContext); + + /* Make hex version of the digest */ + for(i=j=0; i<16; i++) { + char c; + c = (digest[i] >> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hexdigest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hexdigest[j++] = c; + } + return PyString_FromStringAndSize((char*)hexdigest, 32); +} + + +PyDoc_STRVAR(hexdigest_doc, +"hexdigest() -> string\n\ +\n\ +Like digest(), but returns the digest as a string of hexadecimal digits."); + + +static PyObject * +md5_copy(md5object *self) +{ + md5object *md5p; + + if ((md5p = newmd5object()) == NULL) + return NULL; + + md5p->md5 = self->md5; + + return (PyObject *)md5p; +} + +PyDoc_STRVAR(copy_doc, +"copy() -> md5 object\n\ +\n\ +Return a copy (``clone'') of the md5 object."); + + +static PyMethodDef md5_methods[] = { + {"update", (PyCFunction)md5_update, METH_VARARGS, update_doc}, + {"digest", (PyCFunction)md5_digest, METH_NOARGS, digest_doc}, + {"hexdigest", (PyCFunction)md5_hexdigest, METH_NOARGS, hexdigest_doc}, + {"copy", (PyCFunction)md5_copy, METH_NOARGS, copy_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +md5_getattr(md5object *self, char *name) +{ + if (strcmp(name, "digest_size") == 0) { + return PyInt_FromLong(16); + } + + return Py_FindMethod(md5_methods, (PyObject *)self, name); +} + +PyDoc_STRVAR(module_doc, +"This module implements the interface to RSA's MD5 message digest\n\ +algorithm (see also Internet RFC 1321). Its use is quite\n\ +straightforward: use the new() to create an md5 object. You can now\n\ +feed this object with arbitrary strings using the update() method, and\n\ +at any point you can ask it for the digest (a strong kind of 128-bit\n\ +checksum, a.k.a. ``fingerprint'') of the concatenation of the strings\n\ +fed to it so far using the digest() method.\n\ +\n\ +Functions:\n\ +\n\ +new([arg]) -- return a new md5 object, initialized with arg if provided\n\ +md5([arg]) -- DEPRECATED, same as new, but for compatibility\n\ +\n\ +Special Objects:\n\ +\n\ +MD5Type -- type object for md5 objects"); + +PyDoc_STRVAR(md5type_doc, +"An md5 represents the object used to calculate the MD5 checksum of a\n\ +string of information.\n\ +\n\ +Methods:\n\ +\n\ +update() -- updates the current digest with an additional string\n\ +digest() -- return the current digest value\n\ +hexdigest() -- return the current digest as a string of hexadecimal digits\n\ +copy() -- return a copy of the current md5 object"); + +static PyTypeObject MD5type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "md5.md5", /*tp_name*/ + sizeof(md5object), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)md5_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)md5_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + 0, /*tp_xxx4*/ + md5type_doc, /*tp_doc*/ +}; + + +/* MD5 functions */ + +static PyObject * +MD5_new(PyObject *self, PyObject *args) +{ + md5object *md5p; + unsigned char *cp = NULL; + int len = 0; + + if (!PyArg_ParseTuple(args, "|s#:new", &cp, &len)) + return NULL; + + if ((md5p = newmd5object()) == NULL) + return NULL; + + if (cp) + MD5Update(&md5p->md5, cp, len); + + return (PyObject *)md5p; +} + +PyDoc_STRVAR(new_doc, +"new([arg]) -> md5 object\n\ +\n\ +Return a new md5 object. If arg is present, the method call update(arg)\n\ +is made."); + + +/* List of functions exported by this module */ + +static PyMethodDef md5_functions[] = { + {"new", (PyCFunction)MD5_new, METH_VARARGS, new_doc}, + {"md5", (PyCFunction)MD5_new, METH_VARARGS, new_doc}, /* Backward compatibility */ + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +PyMODINIT_FUNC +initmd5(void) +{ + PyObject *m, *d; + + MD5type.ob_type = &PyType_Type; + m = Py_InitModule3("md5", md5_functions, module_doc); + d = PyModule_GetDict(m); + PyDict_SetItemString(d, "MD5Type", (PyObject *)&MD5type); + PyModule_AddIntConstant(m, "digest_size", 16); + /* No need to check the error here, the caller will do that */ +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mmapmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mmapmodule.c new file mode 100644 index 00000000..df3773aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mmapmodule.c @@ -0,0 +1,1124 @@ +/* + / Author: Sam Rushing + / Hacked for Unix by AMK + / $Id: mmapmodule.c,v 2.46 2003/07/15 12:37:46 akuchling Exp $ + + / mmapmodule.cpp -- map a view of a file into memory + / + / todo: need permission flags, perhaps a 'chsize' analog + / not all functions check range yet!!! + / + / + / Note: This module currently only deals with 32-bit file + / sizes. + / + / This version of mmapmodule.c has been changed significantly + / from the original mmapfile.c on which it was based. + / The original version of mmapfile is maintained by Sam at + / ftp://squirl.nightmare.com/pub/python/python-ext. +*/ + +#include + +#ifndef MS_WINDOWS +#define UNIX +#endif + +#ifdef MS_WINDOWS +#include +static int +my_getpagesize(void) +{ + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwPageSize; +} +#endif + +#ifdef UNIX +#include +#include + +#ifndef MS_SYNC +/* This is missing e.g. on SunOS 4.1.4 */ +#define MS_SYNC 0 +#endif + +#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) +static int +my_getpagesize(void) +{ + return sysconf(_SC_PAGESIZE); +} +#else +#define my_getpagesize getpagesize +#endif + +#endif /* UNIX */ + +#include +#include + +static PyObject *mmap_module_error; + +typedef enum +{ + ACCESS_DEFAULT, + ACCESS_READ, + ACCESS_WRITE, + ACCESS_COPY +} access_mode; + +typedef struct { + PyObject_HEAD + char * data; + size_t size; + size_t pos; + +#ifdef MS_WINDOWS + HANDLE map_handle; + HANDLE file_handle; + char * tagname; +#endif + +#ifdef UNIX + int fd; +#endif + + access_mode access; +} mmap_object; + + +static void +mmap_object_dealloc(mmap_object *m_obj) +{ +#ifdef MS_WINDOWS + if (m_obj->data != NULL) + UnmapViewOfFile (m_obj->data); + if (m_obj->map_handle != INVALID_HANDLE_VALUE) + CloseHandle (m_obj->map_handle); + if (m_obj->file_handle != INVALID_HANDLE_VALUE) + CloseHandle (m_obj->file_handle); + if (m_obj->tagname) + PyMem_Free(m_obj->tagname); +#endif /* MS_WINDOWS */ + +#ifdef UNIX + if (m_obj->data!=NULL) { + msync(m_obj->data, m_obj->size, MS_SYNC); + munmap(m_obj->data, m_obj->size); + } +#endif /* UNIX */ + + PyObject_Del(m_obj); +} + +static PyObject * +mmap_close_method(mmap_object *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":close")) + return NULL; +#ifdef MS_WINDOWS + /* For each resource we maintain, we need to check + the value is valid, and if so, free the resource + and set the member value to an invalid value so + the dealloc does not attempt to resource clearing + again. + TODO - should we check for errors in the close operations??? + */ + if (self->data != NULL) { + UnmapViewOfFile (self->data); + self->data = NULL; + } + if (self->map_handle != INVALID_HANDLE_VALUE) { + CloseHandle (self->map_handle); + self->map_handle = INVALID_HANDLE_VALUE; + } + if (self->file_handle != INVALID_HANDLE_VALUE) { + CloseHandle (self->file_handle); + self->file_handle = INVALID_HANDLE_VALUE; + } +#endif /* MS_WINDOWS */ + +#ifdef UNIX + if (self->data != NULL) { + munmap(self->data, self->size); + self->data = NULL; + } +#endif + + Py_INCREF (Py_None); + return (Py_None); +} + +#ifdef MS_WINDOWS +#define CHECK_VALID(err) \ +do { \ + if (self->map_handle == INVALID_HANDLE_VALUE) { \ + PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \ + return err; \ + } \ +} while (0) +#endif /* MS_WINDOWS */ + +#ifdef UNIX +#define CHECK_VALID(err) \ +do { \ + if (self->data == NULL) { \ + PyErr_SetString (PyExc_ValueError, "mmap closed or invalid"); \ + return err; \ + } \ +} while (0) +#endif /* UNIX */ + +static PyObject * +mmap_read_byte_method(mmap_object *self, + PyObject *args) +{ + CHECK_VALID(NULL); + if (!PyArg_ParseTuple(args, ":read_byte")) + return NULL; + if (self->pos < self->size) { + char value = self->data[self->pos]; + self->pos += 1; + return Py_BuildValue("c", value); + } else { + PyErr_SetString (PyExc_ValueError, "read byte out of range"); + return NULL; + } +} + +static PyObject * +mmap_read_line_method(mmap_object *self, + PyObject *args) +{ + char *start = self->data+self->pos; + char *eof = self->data+self->size; + char *eol; + PyObject *result; + + CHECK_VALID(NULL); + if (!PyArg_ParseTuple(args, ":readline")) + return NULL; + + eol = memchr(start, '\n', self->size - self->pos); + if (!eol) + eol = eof; + else + ++eol; /* we're interested in the position after the + newline. */ + result = PyString_FromStringAndSize(start, (eol - start)); + self->pos += (eol - start); + return (result); +} + +static PyObject * +mmap_read_method(mmap_object *self, + PyObject *args) +{ + long num_bytes; + PyObject *result; + + CHECK_VALID(NULL); + if (!PyArg_ParseTuple(args, "l:read", &num_bytes)) + return(NULL); + + /* silently 'adjust' out-of-range requests */ + if ((self->pos + num_bytes) > self->size) { + num_bytes -= (self->pos+num_bytes) - self->size; + } + result = Py_BuildValue("s#", self->data+self->pos, num_bytes); + self->pos += num_bytes; + return (result); +} + +static PyObject * +mmap_find_method(mmap_object *self, + PyObject *args) +{ + long start = self->pos; + char *needle; + int len; + + CHECK_VALID(NULL); + if (!PyArg_ParseTuple (args, "s#|l:find", &needle, &len, &start)) { + return NULL; + } else { + char *p; + char *e = self->data + self->size; + + if (start < 0) + start += self->size; + if (start < 0) + start = 0; + else if ((size_t)start > self->size) + start = self->size; + + for (p = self->data + start; p + len <= e; ++p) { + int i; + for (i = 0; i < len && needle[i] == p[i]; ++i) + /* nothing */; + if (i == len) { + return Py_BuildValue ( + "l", + (long) (p - self->data)); + } + } + return Py_BuildValue ("l", (long) -1); + } +} + +static int +is_writeable(mmap_object *self) +{ + if (self->access != ACCESS_READ) + return 1; + PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map."); + return 0; +} + +static int +is_resizeable(mmap_object *self) +{ + if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT)) + return 1; + PyErr_Format(PyExc_TypeError, + "mmap can't resize a readonly or copy-on-write memory map."); + return 0; +} + + +static PyObject * +mmap_write_method(mmap_object *self, + PyObject *args) +{ + int length; + char *data; + + CHECK_VALID(NULL); + if (!PyArg_ParseTuple (args, "s#:write", &data, &length)) + return(NULL); + + if (!is_writeable(self)) + return NULL; + + if ((self->pos + length) > self->size) { + PyErr_SetString (PyExc_ValueError, "data out of range"); + return NULL; + } + memcpy (self->data+self->pos, data, length); + self->pos = self->pos+length; + Py_INCREF (Py_None); + return (Py_None); +} + +static PyObject * +mmap_write_byte_method(mmap_object *self, + PyObject *args) +{ + char value; + + CHECK_VALID(NULL); + if (!PyArg_ParseTuple (args, "c:write_byte", &value)) + return(NULL); + + if (!is_writeable(self)) + return NULL; + *(self->data+self->pos) = value; + self->pos += 1; + Py_INCREF (Py_None); + return (Py_None); +} + +static PyObject * +mmap_size_method(mmap_object *self, + PyObject *args) +{ + CHECK_VALID(NULL); + if (!PyArg_ParseTuple(args, ":size")) + return NULL; + +#ifdef MS_WINDOWS + if (self->file_handle != INVALID_HANDLE_VALUE) { + return (Py_BuildValue ( + "l", (long) + GetFileSize (self->file_handle, NULL))); + } else { + return (Py_BuildValue ("l", (long) self->size) ); + } +#endif /* MS_WINDOWS */ + +#ifdef UNIX + { + struct stat buf; + if (-1 == fstat(self->fd, &buf)) { + PyErr_SetFromErrno(mmap_module_error); + return NULL; + } + return (Py_BuildValue ("l", (long) buf.st_size) ); + } +#endif /* UNIX */ +} + +/* This assumes that you want the entire file mapped, + / and when recreating the map will make the new file + / have the new size + / + / Is this really necessary? This could easily be done + / from python by just closing and re-opening with the + / new size? + */ + +static PyObject * +mmap_resize_method(mmap_object *self, + PyObject *args) +{ + unsigned long new_size; + CHECK_VALID(NULL); + if (!PyArg_ParseTuple (args, "l:resize", &new_size) || + !is_resizeable(self)) { + return NULL; +#ifdef MS_WINDOWS + } else { + DWORD dwErrCode = 0; + /* First, unmap the file view */ + UnmapViewOfFile (self->data); + /* Close the mapping object */ + CloseHandle (self->map_handle); + /* Move to the desired EOF position */ + SetFilePointer (self->file_handle, + new_size, NULL, FILE_BEGIN); + /* Change the size of the file */ + SetEndOfFile (self->file_handle); + /* Create another mapping object and remap the file view */ + self->map_handle = CreateFileMapping ( + self->file_handle, + NULL, + PAGE_READWRITE, + 0, + new_size, + self->tagname); + if (self->map_handle != NULL) { + self->data = (char *) MapViewOfFile (self->map_handle, + FILE_MAP_WRITE, + 0, + 0, + 0); + if (self->data != NULL) { + self->size = new_size; + Py_INCREF (Py_None); + return Py_None; + } else { + dwErrCode = GetLastError(); + } + } else { + dwErrCode = GetLastError(); + } + PyErr_SetFromWindowsErr(dwErrCode); + return (NULL); +#endif /* MS_WINDOWS */ + +#ifdef UNIX +#ifndef HAVE_MREMAP + } else { + PyErr_SetString(PyExc_SystemError, + "mmap: resizing not available--no mremap()"); + return NULL; +#else + } else { + void *newmap; + +#ifdef MREMAP_MAYMOVE + newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE); +#else + newmap = mremap(self->data, self->size, new_size, 0); +#endif + if (newmap == (void *)-1) + { + PyErr_SetFromErrno(mmap_module_error); + return NULL; + } + self->data = newmap; + self->size = new_size; + Py_INCREF(Py_None); + return Py_None; +#endif /* HAVE_MREMAP */ +#endif /* UNIX */ + } +} + +static PyObject * +mmap_tell_method(mmap_object *self, PyObject *args) +{ + CHECK_VALID(NULL); + if (!PyArg_ParseTuple(args, ":tell")) + return NULL; + return (Py_BuildValue ("l", (long) self->pos) ); +} + +static PyObject * +mmap_flush_method(mmap_object *self, PyObject *args) +{ + size_t offset = 0; + size_t size = self->size; + CHECK_VALID(NULL); + if (!PyArg_ParseTuple (args, "|ll:flush", &offset, &size)) { + return NULL; + } else if ((offset + size) > self->size) { + PyErr_SetString (PyExc_ValueError, + "flush values out of range"); + return NULL; + } else { +#ifdef MS_WINDOWS + return (Py_BuildValue("l", (long) + FlushViewOfFile(self->data+offset, size))); +#endif /* MS_WINDOWS */ +#ifdef UNIX + /* XXX semantics of return value? */ + /* XXX flags for msync? */ + if (-1 == msync(self->data + offset, size, + MS_SYNC)) + { + PyErr_SetFromErrno(mmap_module_error); + return NULL; + } + return Py_BuildValue ("l", (long) 0); +#endif /* UNIX */ + } +} + +static PyObject * +mmap_seek_method(mmap_object *self, PyObject *args) +{ + int dist; + int how=0; + CHECK_VALID(NULL); + if (!PyArg_ParseTuple (args, "i|i:seek", &dist, &how)) { + return(NULL); + } else { + size_t where; + switch (how) { + case 0: /* relative to start */ + if (dist < 0) + goto onoutofrange; + where = dist; + break; + case 1: /* relative to current position */ + if ((int)self->pos + dist < 0) + goto onoutofrange; + where = self->pos + dist; + break; + case 2: /* relative to end */ + if ((int)self->size + dist < 0) + goto onoutofrange; + where = self->size + dist; + break; + default: + PyErr_SetString (PyExc_ValueError, + "unknown seek type"); + return NULL; + } + if (where > self->size) + goto onoutofrange; + self->pos = where; + Py_INCREF (Py_None); + return (Py_None); + } + + onoutofrange: + PyErr_SetString (PyExc_ValueError, "seek out of range"); + return NULL; +} + +static PyObject * +mmap_move_method(mmap_object *self, PyObject *args) +{ + unsigned long dest, src, count; + CHECK_VALID(NULL); + if (!PyArg_ParseTuple (args, "iii:move", &dest, &src, &count) || + !is_writeable(self)) { + return NULL; + } else { + /* bounds check the values */ + if (/* end of source after end of data?? */ + ((src+count) > self->size) + /* dest will fit? */ + || (dest+count > self->size)) { + PyErr_SetString (PyExc_ValueError, + "source or destination out of range"); + return NULL; + } else { + memmove (self->data+dest, self->data+src, count); + Py_INCREF (Py_None); + return Py_None; + } + } +} + +static struct PyMethodDef mmap_object_methods[] = { + {"close", (PyCFunction) mmap_close_method, METH_VARARGS}, + {"find", (PyCFunction) mmap_find_method, METH_VARARGS}, + {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS}, + {"move", (PyCFunction) mmap_move_method, METH_VARARGS}, + {"read", (PyCFunction) mmap_read_method, METH_VARARGS}, + {"read_byte", (PyCFunction) mmap_read_byte_method, METH_VARARGS}, + {"readline", (PyCFunction) mmap_read_line_method, METH_VARARGS}, + {"resize", (PyCFunction) mmap_resize_method, METH_VARARGS}, + {"seek", (PyCFunction) mmap_seek_method, METH_VARARGS}, + {"size", (PyCFunction) mmap_size_method, METH_VARARGS}, + {"tell", (PyCFunction) mmap_tell_method, METH_VARARGS}, + {"write", (PyCFunction) mmap_write_method, METH_VARARGS}, + {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +/* Functions for treating an mmap'ed file as a buffer */ + +static int +mmap_buffer_getreadbuf(mmap_object *self, int index, const void **ptr) +{ + CHECK_VALID(-1); + if ( index != 0 ) { + PyErr_SetString(PyExc_SystemError, + "Accessing non-existent mmap segment"); + return -1; + } + *ptr = self->data; + return self->size; +} + +static int +mmap_buffer_getwritebuf(mmap_object *self, int index, const void **ptr) +{ + CHECK_VALID(-1); + if ( index != 0 ) { + PyErr_SetString(PyExc_SystemError, + "Accessing non-existent mmap segment"); + return -1; + } + if (!is_writeable(self)) + return -1; + *ptr = self->data; + return self->size; +} + +static int +mmap_buffer_getsegcount(mmap_object *self, int *lenp) +{ + CHECK_VALID(-1); + if (lenp) + *lenp = self->size; + return 1; +} + +static int +mmap_buffer_getcharbuffer(mmap_object *self, int index, const void **ptr) +{ + if ( index != 0 ) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent buffer segment"); + return -1; + } + *ptr = (const char *)self->data; + return self->size; +} + +static PyObject * +mmap_object_getattr(mmap_object *self, char *name) +{ + return Py_FindMethod (mmap_object_methods, (PyObject *)self, name); +} + +static int +mmap_length(mmap_object *self) +{ + CHECK_VALID(-1); + return self->size; +} + +static PyObject * +mmap_item(mmap_object *self, int i) +{ + CHECK_VALID(NULL); + if (i < 0 || (size_t)i >= self->size) { + PyErr_SetString(PyExc_IndexError, "mmap index out of range"); + return NULL; + } + return PyString_FromStringAndSize(self->data + i, 1); +} + +static PyObject * +mmap_slice(mmap_object *self, int ilow, int ihigh) +{ + CHECK_VALID(NULL); + if (ilow < 0) + ilow = 0; + else if ((size_t)ilow > self->size) + ilow = self->size; + if (ihigh < 0) + ihigh = 0; + if (ihigh < ilow) + ihigh = ilow; + else if ((size_t)ihigh > self->size) + ihigh = self->size; + + return PyString_FromStringAndSize(self->data + ilow, ihigh-ilow); +} + +static PyObject * +mmap_concat(mmap_object *self, PyObject *bb) +{ + CHECK_VALID(NULL); + PyErr_SetString(PyExc_SystemError, + "mmaps don't support concatenation"); + return NULL; +} + +static PyObject * +mmap_repeat(mmap_object *self, int n) +{ + CHECK_VALID(NULL); + PyErr_SetString(PyExc_SystemError, + "mmaps don't support repeat operation"); + return NULL; +} + +static int +mmap_ass_slice(mmap_object *self, int ilow, int ihigh, PyObject *v) +{ + const char *buf; + + CHECK_VALID(-1); + if (ilow < 0) + ilow = 0; + else if ((size_t)ilow > self->size) + ilow = self->size; + if (ihigh < 0) + ihigh = 0; + if (ihigh < ilow) + ihigh = ilow; + else if ((size_t)ihigh > self->size) + ihigh = self->size; + + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, + "mmap object doesn't support slice deletion"); + return -1; + } + if (! (PyString_Check(v)) ) { + PyErr_SetString(PyExc_IndexError, + "mmap slice assignment must be a string"); + return -1; + } + if ( PyString_Size(v) != (ihigh - ilow) ) { + PyErr_SetString(PyExc_IndexError, + "mmap slice assignment is wrong size"); + return -1; + } + if (!is_writeable(self)) + return -1; + buf = PyString_AsString(v); + memcpy(self->data + ilow, buf, ihigh-ilow); + return 0; +} + +static int +mmap_ass_item(mmap_object *self, int i, PyObject *v) +{ + const char *buf; + + CHECK_VALID(-1); + if (i < 0 || (size_t)i >= self->size) { + PyErr_SetString(PyExc_IndexError, "mmap index out of range"); + return -1; + } + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, + "mmap object doesn't support item deletion"); + return -1; + } + if (! (PyString_Check(v) && PyString_Size(v)==1) ) { + PyErr_SetString(PyExc_IndexError, + "mmap assignment must be single-character string"); + return -1; + } + if (!is_writeable(self)) + return -1; + buf = PyString_AsString(v); + self->data[i] = buf[0]; + return 0; +} + +static PySequenceMethods mmap_as_sequence = { + (inquiry)mmap_length, /*sq_length*/ + (binaryfunc)mmap_concat, /*sq_concat*/ + (intargfunc)mmap_repeat, /*sq_repeat*/ + (intargfunc)mmap_item, /*sq_item*/ + (intintargfunc)mmap_slice, /*sq_slice*/ + (intobjargproc)mmap_ass_item, /*sq_ass_item*/ + (intintobjargproc)mmap_ass_slice, /*sq_ass_slice*/ +}; + +static PyBufferProcs mmap_as_buffer = { + (getreadbufferproc)mmap_buffer_getreadbuf, + (getwritebufferproc)mmap_buffer_getwritebuf, + (getsegcountproc)mmap_buffer_getsegcount, + (getcharbufferproc)mmap_buffer_getcharbuffer, +}; + +static PyTypeObject mmap_object_type = { + PyObject_HEAD_INIT(0) /* patched in module init */ + 0, /* ob_size */ + "mmap.mmap", /* tp_name */ + sizeof(mmap_object), /* tp_size */ + 0, /* tp_itemsize */ + /* methods */ + (destructor) mmap_object_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc) mmap_object_getattr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &mmap_as_sequence, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + &mmap_as_buffer, /*tp_as_buffer*/ + Py_TPFLAGS_HAVE_GETCHARBUFFER, /*tp_flags*/ + 0, /*tp_doc*/ +}; + + +/* extract the map size from the given PyObject + + The map size is restricted to [0, INT_MAX] because this is the current + Python limitation on object sizes. Although the mmap object *could* handle + a larger map size, there is no point because all the useful operations + (len(), slicing(), sequence indexing) are limited by a C int. + + Returns -1 on error, with an appropriate Python exception raised. On + success, the map size is returned. */ +static int +_GetMapSize(PyObject *o) +{ + if (PyInt_Check(o)) { + long i = PyInt_AsLong(o); + if (PyErr_Occurred()) + return -1; + if (i < 0) + goto onnegoverflow; + if (i > INT_MAX) + goto onposoverflow; + return (int)i; + } + else if (PyLong_Check(o)) { + long i = PyLong_AsLong(o); + if (PyErr_Occurred()) { + /* yes negative overflow is mistaken for positive overflow + but not worth the trouble to check sign of 'i' */ + if (PyErr_ExceptionMatches(PyExc_OverflowError)) + goto onposoverflow; + else + return -1; + } + if (i < 0) + goto onnegoverflow; + if (i > INT_MAX) + goto onposoverflow; + return (int)i; + } + else { + PyErr_SetString(PyExc_TypeError, + "map size must be an integral value"); + return -1; + } + + onnegoverflow: + PyErr_SetString(PyExc_OverflowError, + "memory mapped size must be positive"); + return -1; + + onposoverflow: + PyErr_SetString(PyExc_OverflowError, + "memory mapped size is too large (limited by C int)"); + return -1; +} + +#ifdef UNIX +static PyObject * +new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict) +{ +#ifdef HAVE_FSTAT + struct stat st; +#endif + mmap_object *m_obj; + PyObject *map_size_obj = NULL; + int map_size; + int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ; + access_mode access = ACCESS_DEFAULT; + char *keywords[] = {"fileno", "length", + "flags", "prot", + "access", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iii", keywords, + &fd, &map_size_obj, &flags, &prot, &access)) + return NULL; + map_size = _GetMapSize(map_size_obj); + if (map_size < 0) + return NULL; + + if ((access != ACCESS_DEFAULT) && + ((flags != MAP_SHARED) || ( prot != (PROT_WRITE | PROT_READ)))) + return PyErr_Format(PyExc_ValueError, + "mmap can't specify both access and flags, prot."); + switch(access) { + case ACCESS_READ: + flags = MAP_SHARED; + prot = PROT_READ; + break; + case ACCESS_WRITE: + flags = MAP_SHARED; + prot = PROT_READ | PROT_WRITE; + break; + case ACCESS_COPY: + flags = MAP_PRIVATE; + prot = PROT_READ | PROT_WRITE; + break; + case ACCESS_DEFAULT: + /* use the specified or default values of flags and prot */ + break; + default: + return PyErr_Format(PyExc_ValueError, + "mmap invalid access parameter."); + } + +#ifdef HAVE_FSTAT +# ifdef __VMS + /* on OpenVMS we must ensure that all bytes are written to the file */ + fsync(fd); +# endif + if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && + (size_t)map_size > st.st_size) { + PyErr_SetString(PyExc_ValueError, + "mmap length is greater than file size"); + return NULL; + } +#endif + m_obj = PyObject_New (mmap_object, &mmap_object_type); + if (m_obj == NULL) {return NULL;} + m_obj->size = (size_t) map_size; + m_obj->pos = (size_t) 0; + m_obj->fd = fd; + m_obj->data = mmap(NULL, map_size, + prot, flags, + fd, 0); + if (m_obj->data == (char *)-1) { + Py_DECREF(m_obj); + PyErr_SetFromErrno(mmap_module_error); + return NULL; + } + m_obj->access = access; + return (PyObject *)m_obj; +} +#endif /* UNIX */ + +#ifdef MS_WINDOWS +static PyObject * +new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict) +{ + mmap_object *m_obj; + PyObject *map_size_obj = NULL; + int map_size; + char *tagname = ""; + DWORD dwErr = 0; + int fileno; + HANDLE fh = 0; + access_mode access = ACCESS_DEFAULT; + DWORD flProtect, dwDesiredAccess; + char *keywords[] = { "fileno", "length", + "tagname", + "access", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|zi", keywords, + &fileno, &map_size_obj, + &tagname, &access)) { + return NULL; + } + + switch(access) { + case ACCESS_READ: + flProtect = PAGE_READONLY; + dwDesiredAccess = FILE_MAP_READ; + break; + case ACCESS_DEFAULT: case ACCESS_WRITE: + flProtect = PAGE_READWRITE; + dwDesiredAccess = FILE_MAP_WRITE; + break; + case ACCESS_COPY: + flProtect = PAGE_WRITECOPY; + dwDesiredAccess = FILE_MAP_COPY; + break; + default: + return PyErr_Format(PyExc_ValueError, + "mmap invalid access parameter."); + } + + map_size = _GetMapSize(map_size_obj); + if (map_size < 0) + return NULL; + + /* if an actual filename has been specified */ + if (fileno != 0) { + fh = (HANDLE)_get_osfhandle(fileno); + if (fh==(HANDLE)-1) { + PyErr_SetFromErrno(mmap_module_error); + return NULL; + } + /* Win9x appears to need us seeked to zero */ + lseek(fileno, 0, SEEK_SET); + } + + m_obj = PyObject_New (mmap_object, &mmap_object_type); + if (m_obj==NULL) + return NULL; + /* Set every field to an invalid marker, so we can safely + destruct the object in the face of failure */ + m_obj->data = NULL; + m_obj->file_handle = INVALID_HANDLE_VALUE; + m_obj->map_handle = INVALID_HANDLE_VALUE; + m_obj->tagname = NULL; + + if (fh) { + /* It is necessary to duplicate the handle, so the + Python code can close it on us */ + if (!DuplicateHandle( + GetCurrentProcess(), /* source process handle */ + fh, /* handle to be duplicated */ + GetCurrentProcess(), /* target proc handle */ + (LPHANDLE)&m_obj->file_handle, /* result */ + 0, /* access - ignored due to options value */ + FALSE, /* inherited by child processes? */ + DUPLICATE_SAME_ACCESS)) { /* options */ + dwErr = GetLastError(); + Py_DECREF(m_obj); + PyErr_SetFromWindowsErr(dwErr); + return NULL; + } + if (!map_size) { + m_obj->size = GetFileSize (fh, NULL); + } else { + m_obj->size = map_size; + } + } + else { + m_obj->size = map_size; + } + + /* set the initial position */ + m_obj->pos = (size_t) 0; + + /* set the tag name */ + if (tagname != NULL && *tagname != '\0') { + m_obj->tagname = PyMem_Malloc(strlen(tagname)+1); + if (m_obj->tagname == NULL) { + PyErr_NoMemory(); + Py_DECREF(m_obj); + return NULL; + } + strcpy(m_obj->tagname, tagname); + } + else + m_obj->tagname = NULL; + + m_obj->access = access; + m_obj->map_handle = CreateFileMapping (m_obj->file_handle, + NULL, + flProtect, + 0, + m_obj->size, + m_obj->tagname); + if (m_obj->map_handle != NULL) { + m_obj->data = (char *) MapViewOfFile (m_obj->map_handle, + dwDesiredAccess, + 0, + 0, + 0); + if (m_obj->data != NULL) { + return ((PyObject *) m_obj); + } else { + dwErr = GetLastError(); + } + } else { + dwErr = GetLastError(); + } + Py_DECREF(m_obj); + PyErr_SetFromWindowsErr(dwErr); + return (NULL); +} +#endif /* MS_WINDOWS */ + +/* List of functions exported by this module */ +static struct PyMethodDef mmap_functions[] = { + {"mmap", (PyCFunction) new_mmap_object, + METH_VARARGS|METH_KEYWORDS}, + {NULL, NULL} /* Sentinel */ +}; + +PyMODINIT_FUNC + initmmap(void) +{ + PyObject *dict, *module; + + /* Patch the object type */ + mmap_object_type.ob_type = &PyType_Type; + + module = Py_InitModule ("mmap", mmap_functions); + dict = PyModule_GetDict (module); + mmap_module_error = PyExc_EnvironmentError; + Py_INCREF(mmap_module_error); + PyDict_SetItemString (dict, "error", mmap_module_error); +#ifdef PROT_EXEC + PyDict_SetItemString (dict, "PROT_EXEC", PyInt_FromLong(PROT_EXEC) ); +#endif +#ifdef PROT_READ + PyDict_SetItemString (dict, "PROT_READ", PyInt_FromLong(PROT_READ) ); +#endif +#ifdef PROT_WRITE + PyDict_SetItemString (dict, "PROT_WRITE", PyInt_FromLong(PROT_WRITE) ); +#endif + +#ifdef MAP_SHARED + PyDict_SetItemString (dict, "MAP_SHARED", PyInt_FromLong(MAP_SHARED) ); +#endif +#ifdef MAP_PRIVATE + PyDict_SetItemString (dict, "MAP_PRIVATE", + PyInt_FromLong(MAP_PRIVATE) ); +#endif +#ifdef MAP_DENYWRITE + PyDict_SetItemString (dict, "MAP_DENYWRITE", + PyInt_FromLong(MAP_DENYWRITE) ); +#endif +#ifdef MAP_EXECUTABLE + PyDict_SetItemString (dict, "MAP_EXECUTABLE", + PyInt_FromLong(MAP_EXECUTABLE) ); +#endif +#ifdef MAP_ANON + PyDict_SetItemString (dict, "MAP_ANON", PyInt_FromLong(MAP_ANON) ); + PyDict_SetItemString (dict, "MAP_ANONYMOUS", + PyInt_FromLong(MAP_ANON) ); +#endif + + PyDict_SetItemString (dict, "PAGESIZE", + PyInt_FromLong( (long)my_getpagesize() ) ); + + PyDict_SetItemString (dict, "ACCESS_READ", + PyInt_FromLong(ACCESS_READ)); + PyDict_SetItemString (dict, "ACCESS_WRITE", + PyInt_FromLong(ACCESS_WRITE)); + PyDict_SetItemString (dict, "ACCESS_COPY", + PyInt_FromLong(ACCESS_COPY)); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mpzmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mpzmodule.c new file mode 100644 index 00000000..95e2ca63 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/mpzmodule.c @@ -0,0 +1,1690 @@ + +/* MPZ module */ + +/* This module provides an interface to an alternate Multi-Precision + library, GNU MP in this case */ + +/* XXX note: everywhere where mpz_size is called, + sizeof (limb) == sizeof (long) has been assumed. */ + + +/* MPZ objects */ + +#include "Python.h" + +#include /* For size_t */ + +/* +** These are the cpp-flags used in this file... +** +** +** MPZ_MDIV_BUG works around the mpz_m{div,mod,...} routines. +** This bug has been fixed in a later release of +** GMP. +** +** MPZ_GET_STR_BUG mpz_get_str corrupts memory, seems to be fixed +** in a later release +** +** MPZ_DEBUG generates a bunch of diagnostic messages +** +** MPZ_SPARE_MALLOC if set, results in extra code that tries to +** minimize the creation of extra objects. +** +** MPZ_TEST_DIV extra diagnostic output on stderr, when division +** routines are involved +** +** MPZ_LIB_DOES_CHECKING if set, assumes that mpz library doesn't call +** alloca with arg < 0 (when casted to a signed +** integral type). +** +** MPZ_CONVERSIONS_AS_METHODS if set, presents the conversions as +** methods. e.g., `mpz(5).long() == 5L' +** Later, Guido provided an interface to the +** standard functions. So this flag has no been +** cleared, and `long(mpz(5)) == 5L' +** +** MP_TEST_ALLOC If set, you would discover why MPZ_GET_STR_BUG +** is needed +** +** MAKEDUMMYINT Must be set if dynamic linking will be used +*/ + + +/* +** IMHO, mpz_m{div,mod,divmod}() do the wrong things when the denominator < 0 +** This has been fixed with gmp release 2.0 +*/ +/*#define MPZ_MDIV_BUG fixed the (for me) nexessary parts in libgmp.a */ +/* +** IMO, mpz_get_str() assumes a bit too large target space, if he doesn't +** allocate it himself +*/ + +#include "gmp.h" + +#if __GNU_MP__ + 0 >= 2 +#define GMP2 +#define BITS_PER_MP_LIMB mp_bits_per_limb +#else +#define MPZ_GET_STR_BUG +#include "gmp-mparam.h" +#endif + +typedef struct { + PyObject_HEAD + MP_INT mpz; /* the actual number */ +} mpzobject; + +static PyTypeObject MPZtype; + +#define is_mpzobject(v) ((v)->ob_type == &MPZtype) + +static const char initialiser_name[] = "mpz"; + +/* #define MPZ_DEBUG */ + +static mpzobject * +newmpzobject(void) +{ + mpzobject *mpzp; + + +#ifdef MPZ_DEBUG + fputs( "mpz_object() called...\n", stderr ); +#endif /* def MPZ_DEBUG */ + mpzp = PyObject_New(mpzobject, &MPZtype); + if (mpzp == NULL) + return NULL; + + mpz_init(&mpzp->mpz); /* actual initialisation */ + return mpzp; +} /* newmpzobject() */ + +#ifdef MPZ_GET_STR_BUG +#include "longlong.h" +#endif /* def MPZ_GET_STR_BUG */ + +static PyObject * +mpz_format(PyObject *objp, int base, unsigned char withname) +{ + mpzobject *mpzp = (mpzobject *)objp; + PyStringObject *strobjp; + size_t i; + int cmpres; + int taglong; + char *cp; + char prefix[5], *tcp; + + + tcp = &prefix[0]; + + if (mpzp == NULL || !is_mpzobject(mpzp)) { + PyErr_BadInternalCall(); + return NULL; + } + + assert(base >= 2 && base <= 36); + + if (withname) + i = strlen(initialiser_name) + 2; /* e.g. 'mpz(' + ')' */ + else + i = 0; + + if ((cmpres = mpz_cmp_si(&mpzp->mpz, 0L)) == 0) + base = 10; /* '0' in every base, right */ + else if (cmpres < 0) { + *tcp++ = '-'; + i += 1; /* space to hold '-' */ + } + +#ifdef MPZ_DEBUG + fprintf(stderr, "mpz_format: mpz_sizeinbase %d\n", + (int)mpz_sizeinbase(&mpzp->mpz, base)); +#endif /* def MPZ_DEBUG */ +#ifdef MPZ_GET_STR_BUG +#ifdef GMP2 + i += ((size_t) abs(mpzp->mpz._mp_size) * BITS_PER_MP_LIMB + * __mp_bases[base].chars_per_bit_exactly) + 1; +#else + i += ((size_t) abs(mpzp->mpz.size) * BITS_PER_MP_LIMB + * __mp_bases[base].chars_per_bit_exactly) + 1; +#endif +#else /* def MPZ_GET_STR_BUG */ + i += (int)mpz_sizeinbase(&mpzp->mpz, base); +#endif /* def MPZ_GET_STR_BUG else */ + + if (base == 16) { + *tcp++ = '0'; + *tcp++ = 'x'; + i += 2; /* space to hold '0x' */ + } + else if (base == 8) { + *tcp++ = '0'; + i += 1; /* space to hold the extra '0' */ + } + else if (base > 10) { + *tcp++ = '0' + base / 10; + *tcp++ = '0' + base % 10; + *tcp++ = '#'; + i += 3; /* space to hold e.g. '12#' */ + } + else if (base < 10) { + *tcp++ = '0' + base; + *tcp++ = '#'; + i += 2; /* space to hold e.g. '6#' */ + } + + /* + ** the following code looks if we need a 'L' attached to the number + ** it will also attach an 'L' to the value -0x80000000 + */ + taglong = 0; + if (mpz_size(&mpzp->mpz) > 1 + || (long)mpz_get_ui(&mpzp->mpz) < 0L) { + taglong = 1; + i += 1; /* space to hold 'L' */ + } + +#ifdef MPZ_DEBUG + fprintf(stderr, "mpz_format: requesting string size %d\n", i); +#endif /* def MPZ_DEBUG */ + if ((strobjp = + (PyStringObject *)PyString_FromStringAndSize((char *)0, i)) + == NULL) + return NULL; + + /* get the beginning of the string memory and start copying things */ + cp = PyString_AS_STRING(strobjp); + if (withname) { + strcpy(cp, initialiser_name); + cp += strlen(initialiser_name); + *cp++ = '('; /*')'*/ + } + + /* copy the already prepared prefix; e.g. sign and base indicator */ + *tcp = '\0'; + strcpy(cp, prefix); + cp += tcp - prefix; + + /* since' we have the sign already, let the lib think it's a positive + number */ + if (cmpres < 0) + mpz_neg(&mpzp->mpz,&mpzp->mpz); /* hack Hack HAck HACk HACK */ + (void)mpz_get_str(cp, base, &mpzp->mpz); + if (cmpres < 0) + mpz_neg(&mpzp->mpz,&mpzp->mpz); /* hack Hack HAck HACk HACK */ +#ifdef MPZ_DEBUG + fprintf(stderr, "mpz_format: base (ultim) %d, mpz_get_str: %s\n", + base, cp); +#endif /* def MPZ_DEBUG */ + cp += strlen(cp); + + if (taglong) + *cp++ = 'L'; + if (withname) + *cp++ = /*'('*/ ')'; + + *cp = '\0'; + +#ifdef MPZ_DEBUG + fprintf(stderr, + "mpz_format: cp (str end) %p, begin %p, diff %d, i %d\n", + cp, PyString_AS_STRING(strobjp), + cp - PyString_AS_STRING(strobjp), i); +#endif /* def MPZ_DEBUG */ + assert(cp - PyString_AS_STRING(strobjp) <= i); + + if (cp - PyString_AS_STRING(strobjp) != i) { + strobjp->ob_size -= i - (cp - PyString_AS_STRING(strobjp)); + } + + return (PyObject *)strobjp; +} /* mpz_format() */ + +/* MPZ methods */ + +static void +mpz_dealloc(mpzobject *mpzp) +{ +#ifdef MPZ_DEBUG + fputs( "mpz_dealloc() called...\n", stderr ); +#endif /* def MPZ_DEBUG */ + mpz_clear(&mpzp->mpz); + PyObject_Del(mpzp); +} /* mpz_dealloc() */ + + +/* pointers to frequently used values 0, 1 and -1 */ +static mpzobject *mpz_value_zero, *mpz_value_one, *mpz_value_mone; + +static int +mpz_compare(mpzobject *a, mpzobject *b) +{ + int cmpres; + + + /* guido sez it's better to return -1, 0 or 1 */ + return (cmpres = mpz_cmp( &a->mpz, &b->mpz )) == 0 ? 0 + : cmpres > 0 ? 1 : -1; +} /* mpz_compare() */ + +static PyObject * +mpz_addition(mpzobject *a, mpzobject *b) +{ + mpzobject *z; + + +#ifdef MPZ_SPARE_MALLOC + if (mpz_cmp_ui(&a->mpz, (unsigned long int)0) == 0) { + Py_INCREF(b); + return (PyObject *)b; + } + + if (mpz_cmp_ui(&b->mpz, (unsigned long int)0) == 0) { + Py_INCREF(a); + return (PyObject *)a; + } +#endif /* def MPZ_SPARE_MALLOC */ + + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_add(&z->mpz, &a->mpz, &b->mpz); + return (PyObject *)z; +} /* mpz_addition() */ + +static PyObject * +mpz_substract(mpzobject *a, mpzobject *b) +{ + mpzobject *z; + + +#ifdef MPZ_SPARE_MALLOC + if (mpz_cmp_ui(&b->mpz, (unsigned long int)0) == 0) { + Py_INCREF(a); + return (PyObject *)a; + } +#endif /* MPZ_SPARE_MALLOC */ + + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_sub(&z->mpz, &a->mpz, &b->mpz); + return (PyObject *)z; +} /* mpz_substract() */ + +static PyObject * +mpz_multiply(mpzobject *a, mpzobject *b) +{ +#ifdef MPZ_SPARE_MALLOC + int cmpres; +#endif /* def MPZ_SPARE_MALLOC */ + mpzobject *z; + + +#ifdef MPZ_SPARE_MALLOC + if ((cmpres = mpz_cmp_ui(&a->mpz, (unsigned long int)0)) == 0) { + Py_INCREF(mpz_value_zero); + return (PyObject *)mpz_value_zero; + } + if (cmpres > 0 && mpz_cmp_ui(&a->mpz, (unsigned long int)1) == 0) { + Py_INCREF(b); + return (PyObject *)b; + } + + if ((cmpres = mpz_cmp_ui(&b->mpz, (unsigned long_int)0)) == 0) { + Py_INCREF(mpz_value_zero); + return (PyObject *)mpz_value_zero; + } + if (cmpres > 0 && mpz_cmp_ui(&b->mpz, (unsigned long int)1) == 0) { + Py_INCREF(a); + return (PyObject *)a; + } +#endif /* MPZ_SPARE_MALLOC */ + + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_mul( &z->mpz, &a->mpz, &b->mpz ); + return (PyObject *)z; + +} /* mpz_multiply() */ + +static PyObject * +mpz_divide(mpzobject *a, mpzobject *b) +{ +#ifdef MPZ_SPARE_MALLOC + int cmpres; +#endif /* def MPZ_SPARE_MALLOC */ + mpzobject *z; + + + if (( +#ifdef MPZ_SPARE_MALLOC + cmpres = +#endif /* def MPZ_SPARE_MALLOC */ + mpz_cmp_ui(&b->mpz, (unsigned long int)0)) == 0) { + PyErr_SetString(PyExc_ZeroDivisionError, "mpz./ by zero"); + return NULL; + } +#ifdef MPZ_SPARE_MALLOC + if (cmpres > 0 && mpz_cmp_ui(&b->mpz(unsigned long int)1) == 0) { + Py_INCREF(a); + return (PyObject *)a; + } +#endif /* def MPZ_SPARE_MALLOC */ + + if ((z = newmpzobject()) == NULL) + return NULL; + +#ifdef MPZ_TEST_DIV + fputs("mpz_divide: div result", stderr); + mpz_div(&z->mpz, &a->mpz, &b->mpz); + mpz_out_str(stderr, 10, &z->mpz); + putc('\n', stderr); +#endif /* def MPZ_TEST_DIV */ +#ifdef MPZ_MDIV_BUG + if ((mpz_cmp_ui(&a->mpz, (unsigned long int)0) < 0) + != (mpz_cmp_ui(&b->mpz, (unsigned long int)0) < 0)) { + /* + ** numerator has other sign than denominator: we have + ** to look at the remainder for a correction, since mpz_mdiv + ** also calls mpz_divmod, I can as well do it myself + */ + MP_INT tmpmpz; + + + mpz_init(&tmpmpz); + mpz_divmod(&z->mpz, &tmpmpz, &a->mpz, &b->mpz); + + if (mpz_cmp_ui(&tmpmpz, (unsigned long int)0) != 0) + mpz_sub_ui(&z->mpz, &z->mpz, (unsigned long int)1); + + mpz_clear(&tmpmpz); + } + else + mpz_div(&z->mpz, &a->mpz, &b->mpz); + /* the ``naive'' implementation does it right for operands + having the same sign */ + +#else /* def MPZ_MDIV_BUG */ + mpz_mdiv(&z->mpz, &a->mpz, &b->mpz); +#endif /* def MPZ_MDIV_BUG else */ +#ifdef MPZ_TEST_DIV + fputs("mpz_divide: mdiv result", stderr); + mpz_out_str(stderr, 10, &z->mpz); + putc('\n', stderr); +#endif /* def MPZ_TEST_DIV */ + return (PyObject *)z; + +} /* mpz_divide() */ + +static PyObject * +mpz_remainder(mpzobject *a, mpzobject *b) +{ +#ifdef MPZ_SPARE_MALLOC + int cmpres; +#endif /* def MPZ_SPARE_MALLOC */ + mpzobject *z; + + + if (( +#ifdef MPZ_SPARE_MALLOC + cmpres = +#endif /* def MPZ_SPARE_MALLOC */ + mpz_cmp_ui(&b->mpz, (unsigned long int)0)) == 0) { + PyErr_SetString(PyExc_ZeroDivisionError, "mpz.% by zero"); + return NULL; + } +#ifdef MPZ_SPARE_MALLOC + if (cmpres > 0) { + if ((cmpres = mpz_cmp_ui(&b->mpz, (unsigned long int)2)) == 0) + { + Py_INCREF(mpz_value_one); + return (PyObject *)mpz_value_one; + } + if (cmpres < 0) { + /* b must be 1 now */ + Py_INCREF(mpz_value_zero); + return (PyObject *)mpz_value_zero; + } + } +#endif /* def MPZ_SPARE_MALLOC */ + + if ((z = newmpzobject()) == NULL) + return NULL; + +#ifdef MPZ_TEST_DIV + fputs("mpz_remain: mod result", stderr); + mpz_mod(&z->mpz, &a->mpz, &b->mpz); + mpz_out_str(stderr, 10, &z->mpz); + putc('\n', stderr); +#endif /* def MPZ_TEST_DIV */ +#ifdef MPZ_MDIV_BUG + + /* the ``naive'' implementation does it right for operands + having the same sign */ + mpz_mod(&z->mpz, &a->mpz, &b->mpz); + + /* assumption: z, a and b all point to different locations */ + if ((mpz_cmp_ui(&a->mpz, (unsigned long int)0) < 0) + != (mpz_cmp_ui(&b->mpz, (unsigned long int)0) < 0) + && mpz_cmp_ui(&z->mpz, (unsigned long int)0) != 0) + mpz_add(&z->mpz, &z->mpz, &b->mpz); + /* + ** numerator has other sign than denominator: we have + ** to look at the remainder for a correction, since mpz_mdiv + ** also calls mpz_divmod, I can as well do it myself + */ +#else /* def MPZ_MDIV_BUG */ + mpz_mmod(&z->mpz, &a->mpz, &b->mpz); +#endif /* def MPZ_MDIV_BUG else */ +#ifdef MPZ_TEST_DIV + fputs("mpz_remain: mmod result", stderr); + mpz_out_str(stderr, 10, &z->mpz); + putc('\n', stderr); +#endif /* def MPZ_TEST_DIV */ + return (PyObject *)z; + +} /* mpz_remainder() */ + +static PyObject * +mpz_div_and_mod(mpzobject *a, mpzobject *b) +{ + PyObject *z = NULL; + mpzobject *x = NULL, *y = NULL; + + + if (mpz_cmp_ui(&b->mpz, (unsigned long int)0) == 0) { + PyErr_SetString(PyExc_ZeroDivisionError, "mpz.divmod by zero"); + return NULL; + } + + if ((z = PyTuple_New(2)) == NULL + || (x = newmpzobject()) == NULL + || (y = newmpzobject()) == NULL) { + Py_XDECREF(z); + Py_XDECREF(x); + Py_XDECREF(y); + return NULL; + } + +#ifdef MPZ_TEST_DIV + fputs("mpz_divmod: dm result", stderr); + mpz_divmod(&x->mpz, &y->mpz, &a->mpz, &b->mpz); + mpz_out_str(stderr, 10, &x->mpz); + putc('\n', stderr); + mpz_out_str(stderr, 10, &y->mpz); + putc('\n', stderr); +#endif /* def MPZ_TEST_DIV */ +#ifdef MPZ_MDIV_BUG + mpz_divmod(&x->mpz, &y->mpz, &a->mpz, &b->mpz); + if ((mpz_cmp_ui(&a->mpz, (unsigned long int)0) < 0) + != (mpz_cmp_ui(&b->mpz, (unsigned long int)0) < 0) + && mpz_cmp_ui(&y->mpz, (unsigned long int)0) != 0) { + /* + ** numerator has other sign than denominator: we have + ** to look at the remainder for a correction. + */ + mpz_add(&y->mpz, &y->mpz, &b->mpz); + mpz_sub_ui(&x->mpz, &x->mpz, (unsigned long int)1); + } +#else /* def MPZ_MDIV_BUG */ + mpz_mdivmod( &x->mpz, &y->mpz, &a->mpz, &b->mpz ); +#endif /* def MPZ_MDIV_BUG else */ +#ifdef MPZ_TEST_DIV + fputs("mpz_divmod: mdm result", stderr); + mpz_out_str(stderr, 10, &x->mpz); + putc('\n', stderr); + mpz_out_str(stderr, 10, &y->mpz); + putc('\n', stderr); +#endif /* def MPZ_TEST_DIV */ + + (void)PyTuple_SetItem(z, 0, (PyObject *)x); + (void)PyTuple_SetItem(z, 1, (PyObject *)y); + + return z; +} /* mpz_div_and_mod() */ + +static PyObject * +mpz_power(mpzobject *a, mpzobject *b, mpzobject *m) +{ + mpzobject *z; + int cmpres; + + if ((PyObject *)m != Py_None) { + mpzobject *z2; + Py_INCREF(Py_None); + z=(mpzobject *)mpz_power(a, b, (mpzobject *)Py_None); + Py_DECREF(Py_None); + if (z==NULL) return((PyObject *)z); + z2=(mpzobject *)mpz_remainder(z, m); + Py_DECREF(z); + return((PyObject *)z2); + } + + if ((cmpres = mpz_cmp_ui(&b->mpz, (unsigned long int)0)) == 0) { + /* the gnu-mp lib sets pow(0,0) to 0, we to 1 */ + + Py_INCREF(mpz_value_one); + return (PyObject *)mpz_value_one; + } + + if (cmpres < 0) { + PyErr_SetString(PyExc_ValueError, + "mpz.pow to negative exponent"); + return NULL; + } + + if ((cmpres = mpz_cmp_ui(&a->mpz, (unsigned long int)0)) == 0) { + /* the base is 0 */ + + Py_INCREF(mpz_value_zero); + return (PyObject *)mpz_value_zero; + } + else if (cmpres > 0 + && mpz_cmp_ui(&a->mpz, (unsigned long int)1) == 0) { + /* the base is 1 */ + + Py_INCREF(mpz_value_one); + return (PyObject *)mpz_value_one; + } + else if (cmpres < 0 + && mpz_cmp_si(&a->mpz, (long int)-1) == 0) { + + MP_INT tmpmpz; + /* the base is -1: pow(-1, any) == 1,-1 for even,uneven b */ + /* XXX this code needs to be optimized: what's better? + mpz_mmod_ui or mpz_mod_2exp, I choose for the latter + for *un*obvious reasons */ + + /* is the exponent even? */ + mpz_init(&tmpmpz); + + /* look to the remainder after a division by (1 << 1) */ + mpz_mod_2exp(&tmpmpz, &b->mpz, (unsigned long int)1); + + if (mpz_cmp_ui(&tmpmpz, (unsigned int)0) == 0) { + mpz_clear(&tmpmpz); + Py_INCREF(mpz_value_one); + return (PyObject *)mpz_value_one; + } + mpz_clear(&tmpmpz); + Py_INCREF(mpz_value_mone); + return (PyObject *)mpz_value_mone; + } + +#ifdef MPZ_LIB_DOES_CHECKING + /* check if it's doable: sizeof(exp) > sizeof(long) && + abs(base) > 1 ?? --> No Way */ + if (mpz_size(&b->mpz) > 1) + return (PyObject *)PyErr_NoMemory(); +#else /* def MPZ_LIB_DOES_CHECKING */ + /* wet finger method */ + if (mpz_cmp_ui(&b->mpz, (unsigned long int)0x10000) >= 0) { + PyErr_SetString(PyExc_ValueError, + "mpz.pow outrageous exponent"); + return NULL; + } +#endif /* def MPZ_LIB_DOES_CHECKING else */ + + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_pow_ui(&z->mpz, &a->mpz, mpz_get_ui(&b->mpz)); + + return (PyObject *)z; +} /* mpz_power() */ + + +static PyObject * +mpz_negative(mpzobject *v) +{ + mpzobject *z; + + +#ifdef MPZ_SPARE_MALLOC + if (mpz_cmp_ui(&v->mpz, (unsigned long int)0) == 0) { + /* -0 == 0 */ + Py_INCREF(v); + return (PyObject *)v; + } +#endif /* def MPZ_SPARE_MALLOC */ + + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_neg(&z->mpz, &v->mpz); + return (PyObject *)z; +} /* mpz_negative() */ + + +static PyObject * +mpz_positive(mpzobject *v) +{ + Py_INCREF(v); + return (PyObject *)v; +} /* mpz_positive() */ + + +static PyObject * +mpz_absolute(mpzobject *v) +{ + mpzobject *z; + + + if (mpz_cmp_ui(&v->mpz, (unsigned long int)0) >= 0) { + Py_INCREF(v); + return (PyObject *)v; + } + + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_neg(&z->mpz, &v->mpz); + return (PyObject *)z; +} /* mpz_absolute() */ + +static int +mpz_nonzero(mpzobject *v) +{ + return mpz_cmp_ui(&v->mpz, (unsigned long int)0) != 0; +} /* mpz_nonzero() */ + +static PyObject * +py_mpz_invert(mpzobject *v) +{ + mpzobject *z; + + + /* I think mpz_com does exactly what needed */ + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_com(&z->mpz, &v->mpz); + return (PyObject *)z; +} /* py_mpz_invert() */ + +static PyObject * +mpz_lshift(mpzobject *a, mpzobject *b) +{ + int cmpres; + mpzobject *z; + + + if ((cmpres = mpz_cmp_ui(&b->mpz, (unsigned long int)0)) == 0) { + /* a << 0 == a */ + Py_INCREF(a); + return (PyObject *)a; + } + + if (cmpres < 0) { + PyErr_SetString(PyExc_ValueError, + "mpz.<< negative shift count"); + return NULL; + } + +#ifdef MPZ_LIB_DOES_CHECKING + if (mpz_size(&b->mpz) > 1) + return (PyObject *)PyErr_NoMemory(); +#else /* def MPZ_LIB_DOES_CHECKING */ + /* wet finger method */ + if (mpz_cmp_ui(&b->mpz, (unsigned long int)0x10000) >= 0) { + PyErr_SetString(PyExc_ValueError, + "mpz.<< outrageous shift count"); + return NULL; + } +#endif /* def MPZ_LIB_DOES_CHECKING else */ + + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_mul_2exp(&z->mpz, &a->mpz, mpz_get_ui(&b->mpz)); + return (PyObject *)z; +} /* mpz_lshift() */ + +static PyObject * +mpz_rshift(mpzobject *a, mpzobject *b) +{ + int cmpres; + mpzobject *z; + + + if ((cmpres = mpz_cmp_ui(&b->mpz, (unsigned long int)0)) == 0) { + /* a >> 0 == a */ + Py_INCREF(a); + return (PyObject *)a; + } + + if (cmpres < 0) { + PyErr_SetString(PyExc_ValueError, + "mpz.>> negative shift count"); + return NULL; + } + + if (mpz_size(&b->mpz) > 1) + return (PyObject *)PyErr_NoMemory(); + + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_div_2exp(&z->mpz, &a->mpz, mpz_get_ui(&b->mpz)); + return (PyObject *)z; +} /* mpz_rshift() */ + +static PyObject * +mpz_andfunc(mpzobject *a, mpzobject *b) +{ + mpzobject *z; + + + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_and(&z->mpz, &a->mpz, &b->mpz); + return (PyObject *)z; +} /* mpz_andfunc() */ + +/* hack Hack HAck HACk HACK, XXX this code is dead slow */ +void +mpz_xor(MP_INT *res, const MP_INT *op1, const MP_INT *op2) +{ + MP_INT tmpmpz; + + mpz_init(&tmpmpz); + + mpz_and(res, op1, op2); + mpz_com(&tmpmpz, res); + mpz_ior(res, op1, op2); + mpz_and(res, res, &tmpmpz); + + mpz_clear(&tmpmpz); +} /* mpz_xor() HACK */ + +static PyObject * +mpz_xorfunc(mpzobject *a, mpzobject *b) +{ + mpzobject *z; + + + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_xor(&z->mpz, &a->mpz, &b->mpz); + return (PyObject *)z; +} /* mpz_xorfunc() */ + +static PyObject * +mpz_orfunc(mpzobject *a, mpzobject *b) +{ + mpzobject *z; + + + if ((z = newmpzobject()) == NULL) + return NULL; + + mpz_ior(&z->mpz, &a->mpz, &b->mpz); + return (PyObject *)z; +} /* mpz_orfunc() */ + +/* MPZ initialisation */ + +#include "longintrepr.h" + +static PyObject * +MPZ_mpz(PyObject *self, PyObject *args) +{ + mpzobject *mpzp; + + +#ifdef MPZ_DEBUG + fputs("MPZ_mpz() called...\n", stderr); +#endif /* def MPZ_DEBUG */ + + /* at least we know it's some object */ + /* note DON't Py_DECREF args */ + + if (PyInt_Check(args)) { + long lval = PyInt_AS_LONG(args); + if (lval == (long)0) { + Py_INCREF(mpz_value_zero); + mpzp = mpz_value_zero; + } + else if (lval == (long)1) { + Py_INCREF(mpz_value_one); + mpzp = mpz_value_one; + } + else if ((mpzp = newmpzobject()) == NULL) + return NULL; + else mpz_set_si(&mpzp->mpz, lval); + } + else if (PyLong_Check(args)) { + MP_INT mplongdigit; + int i; + unsigned char isnegative; + + + if ((mpzp = newmpzobject()) == NULL) + return NULL; + + mpz_set_si(&mpzp->mpz, 0L); + mpz_init(&mplongdigit); + + /* how we're gonna handle this? */ + if ((isnegative = + ((i = ((PyLongObject *)args)->ob_size) < 0) )) + i = -i; + + while (i--) { + mpz_set_ui(&mplongdigit, + (unsigned long) + ((PyLongObject *)args)->ob_digit[i]); + mpz_mul_2exp(&mplongdigit,&mplongdigit, + (unsigned long int)i * SHIFT); + mpz_ior(&mpzp->mpz, &mpzp->mpz, &mplongdigit); + } + + if (isnegative) + mpz_neg(&mpzp->mpz, &mpzp->mpz); + + /* get rid of allocation for tmp variable */ + mpz_clear(&mplongdigit); + } + else if (PyString_Check(args)) { + unsigned char *cp = (unsigned char *)PyString_AS_STRING(args); + int len = PyString_GET_SIZE(args); + MP_INT mplongdigit; + + if ((mpzp = newmpzobject()) == NULL) + return NULL; + + mpz_set_si(&mpzp->mpz, 0L); + mpz_init(&mplongdigit); + + /* let's do it the same way as with the long conversion: + without thinking how it can be faster (-: :-) */ + + cp += len; + while (len--) { + mpz_set_ui(&mplongdigit, (unsigned long)*--cp ); + mpz_mul_2exp(&mplongdigit,&mplongdigit, + (unsigned long int)len * 8); + mpz_ior(&mpzp->mpz, &mpzp->mpz, &mplongdigit); + } + + /* get rid of allocation for tmp variable */ + mpz_clear(&mplongdigit); + } + else if (is_mpzobject(args)) { + Py_INCREF(args); + mpzp = (mpzobject *)args; + } + else { + PyErr_SetString(PyExc_TypeError, +"mpz.mpz() expects integer, long, string or mpz object argument"); + return NULL; + } + + +#ifdef MPZ_DEBUG + fputs("MPZ_mpz: created mpz=", stderr); + mpz_out_str(stderr, 10, &mpzp->mpz); + putc('\n', stderr); +#endif /* def MPZ_DEBUG */ + return (PyObject *)mpzp; +} /* MPZ_mpz() */ + +static mpzobject * +mpz_mpzcoerce(PyObject *z) +{ + /* shortcut: 9 out of 10 times the type is already ok */ + if (is_mpzobject(z)) { + Py_INCREF(z); + return (mpzobject *)z; /* coercion succeeded */ + } + + /* what types do we accept?: intobjects and longobjects */ + if (PyInt_Check(z) || PyLong_Check(z)) + return (mpzobject *)MPZ_mpz((PyObject *)NULL, z); + + PyErr_SetString(PyExc_TypeError, + "number coercion (to mpzobject) failed"); + return NULL; +} /* mpz_mpzcoerce() */ + +/* Forward */ +static void mpz_divm(MP_INT *res, const MP_INT *num, + const MP_INT *den, const MP_INT *mod); + +static PyObject * +MPZ_powm(PyObject *self, PyObject *args) +{ + PyObject *base, *exp, *mod; + mpzobject *mpzbase = NULL, *mpzexp = NULL, *mpzmod = NULL; + mpzobject *z = NULL; + int tstres; + + + if (!PyArg_ParseTuple(args, "OOO", &base, &exp, &mod)) + return NULL; + + if ((mpzbase = mpz_mpzcoerce(base)) == NULL + || (mpzexp = mpz_mpzcoerce(exp)) == NULL + || (mpzmod = mpz_mpzcoerce(mod)) == NULL + || (z = newmpzobject()) == NULL) { + Py_XDECREF(mpzbase); + Py_XDECREF(mpzexp); + Py_XDECREF(mpzmod); + Py_XDECREF(z); + return NULL; + } + + if ((tstres=mpz_cmp_ui(&mpzexp->mpz, (unsigned long int)0)) == 0) { + Py_DECREF(mpzbase); + Py_DECREF(mpzexp); + Py_DECREF(mpzmod); + Py_DECREF(z); + Py_INCREF(mpz_value_one); + return (PyObject *)mpz_value_one; + } + + if (mpz_cmp_ui(&mpzmod->mpz, 0) == 0) { + Py_DECREF(mpzbase); + Py_DECREF(mpzexp); + Py_DECREF(mpzmod); + Py_DECREF(z); + PyErr_SetString(PyExc_ValueError, "modulus cannot be 0"); + return NULL; + } + + if (tstres < 0) { + MP_INT absexp; + /* negative exp */ + + mpz_init_set(&absexp, &mpzexp->mpz); + mpz_abs(&absexp, &absexp); + mpz_powm(&z->mpz, &mpzbase->mpz, &absexp, &mpzmod->mpz); + + mpz_divm(&z->mpz, &mpz_value_one->mpz, &z->mpz, &mpzmod->mpz); + + mpz_clear(&absexp); + } + else { + mpz_powm(&z->mpz, &mpzbase->mpz, &mpzexp->mpz, &mpzmod->mpz); + } + + Py_DECREF(mpzbase); + Py_DECREF(mpzexp); + Py_DECREF(mpzmod); + + return (PyObject *)z; +} /* MPZ_powm() */ + + +static PyObject * +MPZ_gcd(PyObject *self, PyObject *args) +{ + PyObject *op1, *op2; + mpzobject *mpzop1 = NULL, *mpzop2 = NULL; + mpzobject *z; + + + if (!PyArg_ParseTuple(args, "OO", &op1, &op2)) + return NULL; + + if ((mpzop1 = mpz_mpzcoerce(op1)) == NULL + || (mpzop2 = mpz_mpzcoerce(op2)) == NULL + || (z = newmpzobject()) == NULL) { + Py_XDECREF(mpzop1); + Py_XDECREF(mpzop2); + return NULL; + } + + /* ok, we have three mpzobjects, and an initialised result holder */ + mpz_gcd(&z->mpz, &mpzop1->mpz, &mpzop2->mpz); + + Py_DECREF(mpzop1); + Py_DECREF(mpzop2); + + return (PyObject *)z; +} /* MPZ_gcd() */ + + +static PyObject * +MPZ_gcdext(PyObject *self, PyObject *args) +{ + PyObject *op1, *op2, *z = NULL; + mpzobject *mpzop1 = NULL, *mpzop2 = NULL; + mpzobject *g = NULL, *s = NULL, *t = NULL; + + + if (!PyArg_ParseTuple(args, "OO", &op1, &op2)) + return NULL; + + if ((mpzop1 = mpz_mpzcoerce(op1)) == NULL + || (mpzop2 = mpz_mpzcoerce(op2)) == NULL + || (z = PyTuple_New(3)) == NULL + || (g = newmpzobject()) == NULL + || (s = newmpzobject()) == NULL + || (t = newmpzobject()) == NULL) { + Py_XDECREF(mpzop1); + Py_XDECREF(mpzop2); + Py_XDECREF(z); + Py_XDECREF(g); + Py_XDECREF(s); + /*Py_XDECREF(t);*/ + return NULL; + } + + mpz_gcdext(&g->mpz, &s->mpz, &t->mpz, &mpzop1->mpz, &mpzop2->mpz); + + Py_DECREF(mpzop1); + Py_DECREF(mpzop2); + + (void)PyTuple_SetItem(z, 0, (PyObject *)g); + (void)PyTuple_SetItem(z, 1, (PyObject *)s); + (void)PyTuple_SetItem(z, 2, (PyObject *)t); + + return (PyObject *)z; +} /* MPZ_gcdext() */ + + +static PyObject * +MPZ_sqrt(PyObject *self, PyObject *args) +{ + mpzobject *mpzop = NULL; + mpzobject *z; + + + if ((mpzop = mpz_mpzcoerce(args)) == NULL + || (z = newmpzobject()) == NULL) { + Py_XDECREF(mpzop); + return NULL; + } + + mpz_sqrt(&z->mpz, &mpzop->mpz); + + Py_DECREF(mpzop); + + return (PyObject *)z; +} /* MPZ_sqrt() */ + + +static PyObject * +MPZ_sqrtrem(PyObject *self, PyObject *args) +{ + PyObject *z = NULL; + mpzobject *mpzop = NULL; + mpzobject *root = NULL, *rem = NULL; + + if ((mpzop = mpz_mpzcoerce(args)) == NULL + || (z = PyTuple_New(2)) == NULL + || (root = newmpzobject()) == NULL + || (rem = newmpzobject()) == NULL) { + Py_XDECREF(mpzop); + Py_XDECREF(z); + Py_XDECREF(root); + /*Py_XDECREF(rem);*/ + return NULL; + } + + mpz_sqrtrem(&root->mpz, &rem->mpz, &mpzop->mpz); + + Py_DECREF(mpzop); + + (void)PyTuple_SetItem(z, 0, (PyObject *)root); + (void)PyTuple_SetItem(z, 1, (PyObject *)rem); + + return (PyObject *)z; +} /* MPZ_sqrtrem() */ + + +static void +mpz_divm(MP_INT *res, const MP_INT *num, const MP_INT *den, const MP_INT *mod) +{ + MP_INT s0, s1, q, r, x, d0, d1; + + mpz_init_set(&s0, num); + mpz_init_set_ui(&s1, 0); + mpz_init(&q); + mpz_init(&r); + mpz_init(&x); + mpz_init_set(&d0, den); + mpz_init_set(&d1, mod); + +#ifdef GMP2 + while (d1._mp_size != 0) { +#else + while (d1.size != 0) { +#endif + mpz_divmod(&q, &r, &d0, &d1); + mpz_set(&d0, &d1); + mpz_set(&d1, &r); + + mpz_mul(&x, &s1, &q); + mpz_sub(&x, &s0, &x); + mpz_set(&s0, &s1); + mpz_set(&s1, &x); + } + +#ifdef GMP2 + if (d0._mp_size != 1 || d0._mp_d[0] != 1) + res->_mp_size = 0; /* trouble: the gcd != 1; set s to zero */ +#else + if (d0.size != 1 || d0.d[0] != 1) + res->size = 0; /* trouble: the gcd != 1; set s to zero */ +#endif + else { +#ifdef MPZ_MDIV_BUG + /* watch out here! first check the signs, and then perform + the mpz_mod() since mod could point to res */ + if ((s0.size < 0) != (mod->size < 0)) { + mpz_mod(res, &s0, mod); + + if (res->size) + mpz_add(res, res, mod); + } + else + mpz_mod(res, &s0, mod); + +#else /* def MPZ_MDIV_BUG */ + mpz_mmod(res, &s0, mod); +#endif /* def MPZ_MDIV_BUG else */ + } + + mpz_clear(&s0); + mpz_clear(&s1); + mpz_clear(&q); + mpz_clear(&r); + mpz_clear(&x); + mpz_clear(&d0); + mpz_clear(&d1); +} /* mpz_divm() */ + + +static PyObject * +MPZ_divm(PyObject *self, PyObject *args) +{ + PyObject *num, *den, *mod; + mpzobject *mpznum, *mpzden = NULL, *mpzmod = NULL; + mpzobject *z = NULL; + + + if (!PyArg_ParseTuple(args, "OOO", &num, &den, &mod)) + return NULL; + + if ((mpznum = mpz_mpzcoerce(num)) == NULL + || (mpzden = mpz_mpzcoerce(den)) == NULL + || (mpzmod = mpz_mpzcoerce(mod)) == NULL + || (z = newmpzobject()) == NULL ) { + Py_XDECREF(mpznum); + Py_XDECREF(mpzden); + Py_XDECREF(mpzmod); + return NULL; + } + + mpz_divm(&z->mpz, &mpznum->mpz, &mpzden->mpz, &mpzmod->mpz); + + Py_DECREF(mpznum); + Py_DECREF(mpzden); + Py_DECREF(mpzmod); + + if (mpz_cmp_ui(&z->mpz, (unsigned long int)0) == 0) { + Py_DECREF(z); + PyErr_SetString(PyExc_ValueError, + "gcd(den, mod) != 1 or num == 0"); + return NULL; + } + + return (PyObject *)z; +} /* MPZ_divm() */ + + +static PyObject * +mpz_int(mpzobject *self) +{ + long sli; + + + if (mpz_size(&self->mpz) > 1 + || (sli = (long)mpz_get_ui(&self->mpz)) < (long)0 ) { + PyErr_SetString(PyExc_ValueError, + "mpz.int() arg too long to convert"); + return NULL; + } + + if (mpz_cmp_ui(&self->mpz, (unsigned long)0) < 0) + sli = -sli; + + return PyInt_FromLong(sli); +} /* mpz_int() */ + +static PyObject * +mpz_long(mpzobject *self) +{ + int i, isnegative; + unsigned long int uli; + PyLongObject *longobjp; + int ldcount; + int bitpointer, newbitpointer; + MP_INT mpzscratch; + + + /* determine length of python-long to be allocated */ + if ((longobjp = _PyLong_New(i = (int) + ((mpz_size(&self->mpz) * BITS_PER_MP_LIMB + + SHIFT - 1) / + SHIFT))) == NULL) + return NULL; + + /* determine sign, and copy self to scratch var */ + mpz_init_set(&mpzscratch, &self->mpz); + if ((isnegative = (mpz_cmp_ui(&self->mpz, (unsigned long int)0) < 0))) + mpz_neg(&mpzscratch, &mpzscratch); + + /* let those bits come, let those bits go, + e.g. dismantle mpzscratch, build PyLongObject */ + + bitpointer = 0; /* the number of valid bits in stock */ + newbitpointer = 0; + ldcount = 0; /* the python-long limb counter */ + uli = (unsigned long int)0; + while (i--) { + longobjp->ob_digit[ldcount] = uli & MASK; + + /* check if we've had enough bits for this digit */ + if (bitpointer < SHIFT) { + uli = mpz_get_ui(&mpzscratch); + longobjp->ob_digit[ldcount] |= + (uli << bitpointer) & MASK; + uli >>= SHIFT-bitpointer; + bitpointer += BITS_PER_MP_LIMB; + mpz_div_2exp(&mpzscratch, &mpzscratch, + BITS_PER_MP_LIMB); + } + else + uli >>= SHIFT; + bitpointer -= SHIFT; + ldcount++; + } + + assert(mpz_cmp_ui(&mpzscratch, (unsigned long int)0) == 0); + mpz_clear(&mpzscratch); + assert(ldcount <= longobjp->ob_size); + + /* long_normalize() is file-static */ + /* longobjp = long_normalize(longobjp); */ + while (ldcount > 0 && longobjp->ob_digit[ldcount-1] == 0) + ldcount--; + longobjp->ob_size = ldcount; + + + if (isnegative) + longobjp->ob_size = -longobjp->ob_size; + + return (PyObject *)longobjp; + +} /* mpz_long() */ + + +/* I would have avoided pow() anyways, so ... */ +static const double multiplier = 256.0 * 256.0 * 256.0 * 256.0; + +static PyObject * +mpz_float(mpzobject *self) +{ + int i, isnegative; + double x; + double mulstate; + MP_INT mpzscratch; + + + i = (int)mpz_size(&self->mpz); + + /* determine sign, and copy abs(self) to scratch var */ + if ((isnegative = (mpz_cmp_ui(&self->mpz, (unsigned long int)0) < 0))) + { + mpz_init(&mpzscratch); + mpz_neg(&mpzscratch, &self->mpz); + } + else + mpz_init_set(&mpzscratch, &self->mpz); + + /* let those bits come, let those bits go, + e.g. dismantle mpzscratch, build PyFloatObject */ + + /* Can this overflow? Dunno, protect against that possibility. */ + PyFPE_START_PROTECT("mpz_float", return 0) + x = 0.0; + mulstate = 1.0; + while (i--) { + x += mulstate * mpz_get_ui(&mpzscratch); + mulstate *= multiplier; + mpz_div_2exp(&mpzscratch, &mpzscratch, BITS_PER_MP_LIMB); + } + PyFPE_END_PROTECT(mulstate) + + assert(mpz_cmp_ui(&mpzscratch, (unsigned long int)0) == 0); + mpz_clear(&mpzscratch); + + if (isnegative) + x = -x; + + return PyFloat_FromDouble(x); + +} /* mpz_float() */ + +static PyObject * +mpz_hex(mpzobject *self) +{ + return mpz_format((PyObject *)self, 16, (unsigned char)1); +} /* mpz_hex() */ + +static PyObject * +mpz_oct(mpzobject *self) +{ + return mpz_format((PyObject *)self, 8, (unsigned char)1); +} /* mpz_oct() */ + +static PyObject * +mpz_binary(mpzobject *self) +{ + int size; + PyStringObject *strobjp; + char *cp; + MP_INT mp; + unsigned long ldigit; + + if (mpz_cmp_ui(&self->mpz, (unsigned long int)0) < 0) { + PyErr_SetString(PyExc_ValueError, + "mpz.binary() arg must be >= 0"); + return NULL; + } + + mpz_init_set(&mp, &self->mpz); + size = (int)mpz_size(&mp); + + if ((strobjp = (PyStringObject *) + PyString_FromStringAndSize( + (char *)0, size * sizeof (unsigned long int))) == NULL) + return NULL; + + /* get the beginning of the string memory and start copying things */ + cp = PyString_AS_STRING(strobjp); + + /* this has been programmed using a (fairly) decent lib-i/f it could + be must faster if we looked into the GMP lib */ + while (size--) { + ldigit = mpz_get_ui(&mp); + mpz_div_2exp(&mp, &mp, BITS_PER_MP_LIMB); + *cp++ = (unsigned char)(ldigit & 0xFF); + *cp++ = (unsigned char)((ldigit >>= 8) & 0xFF); + *cp++ = (unsigned char)((ldigit >>= 8) & 0xFF); + *cp++ = (unsigned char)((ldigit >>= 8) & 0xFF); + if (sizeof(ldigit) == 8 && BITS_PER_MP_LIMB == 64) { + *cp++ = (unsigned char)((ldigit >>= 8) & 0xFF); + *cp++ = (unsigned char)((ldigit >>= 8) & 0xFF); + *cp++ = (unsigned char)((ldigit >>= 8) & 0xFF); + *cp++ = (unsigned char)((ldigit >>= 8) & 0xFF); + } + } + + while (strobjp->ob_size && !*--cp) + strobjp->ob_size--; + + return (PyObject *)strobjp; +} /* mpz_binary() */ + + +static PyMethodDef mpz_methods[] = { +#ifdef MPZ_CONVERSIONS_AS_METHODS + {"int", mpz_int, METH_NOARGS}, + {"long", mpz_long, METH_NOARGS}, + {"float", mpz_float, METH_NOARGS}, + {"hex", mpz_hex, METH_NOARGS}, + {"oct", mpz_oct, METH_NOARGS}, +#endif /* def MPZ_CONVERSIONS_AS_METHODS */ + {"binary", (PyCFunction)mpz_binary, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +mpz_getattr(mpzobject *self, char *name) +{ + return Py_FindMethod(mpz_methods, (PyObject *)self, name); +} /* mpz_getattr() */ + + +static int +mpz_coerce(PyObject **pv, PyObject **pw) +{ + PyObject *z; + +#ifdef MPZ_DEBUG + fputs("mpz_coerce() called...\n", stderr); +#endif /* def MPZ_DEBUG */ + + assert(is_mpzobject(*pv)); + + /* always convert other arg to mpz value, except for floats */ + if (!PyFloat_Check(*pw)) { + if ((z = (PyObject *)mpz_mpzcoerce(*pw)) == NULL) + return -1; /* -1: an error always has been set */ + + Py_INCREF(*pv); + *pw = z; + } + else { + if ((z = mpz_float((mpzobject *)(*pv))) == NULL) + return -1; + + Py_INCREF(*pw); + *pv = z; + } + return 0; /* coercion succeeded */ + +} /* mpz_coerce() */ + + +static PyObject * +mpz_repr(PyObject *v) +{ + return mpz_format(v, 10, (unsigned char)1); +} /* mpz_repr() */ + + + +#define UF (unaryfunc) +#define BF (binaryfunc) +#define TF (ternaryfunc) +#define IF (inquiry) +#define CF (coercion) + +static PyNumberMethods mpz_as_number = { + BF mpz_addition, /*nb_add*/ + BF mpz_substract, /*nb_subtract*/ + BF mpz_multiply, /*nb_multiply*/ + BF mpz_divide, /*nb_divide*/ + BF mpz_remainder, /*nb_remainder*/ + BF mpz_div_and_mod, /*nb_divmod*/ + TF mpz_power, /*nb_power*/ + UF mpz_negative, /*nb_negative*/ + UF mpz_positive, /*tp_positive*/ + UF mpz_absolute, /*tp_absolute*/ + IF mpz_nonzero, /*tp_nonzero*/ + UF py_mpz_invert, /*nb_invert*/ + BF mpz_lshift, /*nb_lshift*/ + BF mpz_rshift, /*nb_rshift*/ + BF mpz_andfunc, /*nb_and*/ + BF mpz_xorfunc, /*nb_xor*/ + BF mpz_orfunc, /*nb_or*/ + CF mpz_coerce, /*nb_coerce*/ +#ifndef MPZ_CONVERSIONS_AS_METHODS + UF mpz_int, /*nb_int*/ + UF mpz_long, /*nb_long*/ + UF mpz_float, /*nb_float*/ + UF mpz_oct, /*nb_oct*/ + UF mpz_hex, /*nb_hex*/ +#endif /* ndef MPZ_CONVERSIONS_AS_METHODS */ +}; + +static PyTypeObject MPZtype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "mpz.mpz", /*tp_name*/ + sizeof(mpzobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)mpz_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)mpz_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + (cmpfunc)mpz_compare, /*tp_compare*/ + (reprfunc)mpz_repr, /*tp_repr*/ + &mpz_as_number, /*tp_as_number*/ +}; + +/* List of functions exported by this module */ + +static PyMethodDef mpz_functions[] = { +#if 0 + {initialiser_name, MPZ_mpz, METH_O}, +#else /* 0 */ + /* until guido ``fixes'' struct PyMethodDef */ + {(char *)initialiser_name, MPZ_mpz, METH_O}, +#endif /* 0 else */ + {"powm", MPZ_powm, METH_VARARGS}, + {"gcd", MPZ_gcd, METH_VARARGS}, + {"gcdext", MPZ_gcdext, METH_VARARGS}, + {"sqrt", MPZ_sqrt, METH_O}, + {"sqrtrem", MPZ_sqrtrem, METH_O}, + {"divm", MPZ_divm, METH_VARARGS}, + {NULL, NULL} /* Sentinel */ +}; + + +/* #define MP_TEST_ALLOC */ + +#ifdef MP_TEST_ALLOC +#define MP_TEST_SIZE 4 +static const char mp_test_magic[MP_TEST_SIZE] = {'\xAA','\xAA','\xAA','\xAA'}; +static mp_test_error(int *location) +{ + /* assumptions: *alloc returns address divisible by 4, + mpz_* routines allocate in chunks divisible by four */ + fprintf(stderr, "MP_TEST_ERROR: location holds 0x%08d\n", *location ); + Py_FatalError("MP_TEST_ERROR"); +} /* static mp_test_error() */ +#define MP_EXTRA_ALLOC(size) ((size) + MP_TEST_SIZE) +#define MP_SET_TEST(basep,size) (void)memcpy( ((char *)(basep))+(size), mp_test_magic, MP_TEST_SIZE) +#define MP_DO_TEST(basep,size) if ( !memcmp( ((char *)(basep))+(size), mp_test_magic, MP_TEST_SIZE ) ) \ + ; \ + else \ + mp_test_error((int *)((char *)(basep) + size)) +#else /* def MP_TEST_ALLOC */ +#define MP_EXTRA_ALLOC(size) (size) +#define MP_SET_TEST(basep,size) +#define MP_DO_TEST(basep,size) +#endif /* def MP_TEST_ALLOC else */ + +void *mp_allocate(size_t alloc_size) +{ + void *res; + +#ifdef MPZ_DEBUG + fprintf(stderr, "mp_allocate : size %ld\n", + alloc_size); +#endif /* def MPZ_DEBUG */ + + if ( (res = malloc(MP_EXTRA_ALLOC(alloc_size))) == NULL ) + Py_FatalError("mp_allocate failure"); + +#ifdef MPZ_DEBUG + fprintf(stderr, "mp_allocate : address %08p\n", res); +#endif /* def MPZ_DEBUG */ + + MP_SET_TEST(res,alloc_size); + + return res; +} /* mp_allocate() */ + + +void *mp_reallocate(void *ptr, size_t old_size, size_t new_size) +{ + void *res; + +#ifdef MPZ_DEBUG + fprintf(stderr, "mp_reallocate: old address %08p, old size %ld\n", + ptr, old_size); +#endif /* def MPZ_DEBUG */ + + MP_DO_TEST(ptr, old_size); + + if ( (res = realloc(ptr, MP_EXTRA_ALLOC(new_size))) == NULL ) + Py_FatalError("mp_reallocate failure"); + +#ifdef MPZ_DEBUG + fprintf(stderr, "mp_reallocate: new address %08p, new size %ld\n", + res, new_size); +#endif /* def MPZ_DEBUG */ + + MP_SET_TEST(res, new_size); + + return res; +} /* mp_reallocate() */ + + +void mp_free(void *ptr, size_t size) +{ + +#ifdef MPZ_DEBUG + fprintf(stderr, "mp_free : old address %08p, old size %ld\n", + ptr, size); +#endif /* def MPZ_DEBUG */ + + MP_DO_TEST(ptr, size); + free(ptr); +} /* mp_free() */ + + + +/* Initialize this module. */ + +PyMODINIT_FUNC +initmpz(void) +{ + PyObject *module; + PyObject *dict; + +#ifdef MPZ_DEBUG + fputs( "initmpz() called...\n", stderr ); +#endif /* def MPZ_DEBUG */ + + mp_set_memory_functions( mp_allocate, mp_reallocate, mp_free ); + MPZtype.ob_type = &PyType_Type; + module = Py_InitModule("mpz", mpz_functions); + + /* create some frequently used constants */ + if ((mpz_value_zero = newmpzobject()) == NULL) + goto finally; + mpz_set_ui(&mpz_value_zero->mpz, (unsigned long int)0); + + if ((mpz_value_one = newmpzobject()) == NULL) + goto finally; + mpz_set_ui(&mpz_value_one->mpz, (unsigned long int)1); + + if ((mpz_value_mone = newmpzobject()) == NULL) + goto finally; + mpz_set_si(&mpz_value_mone->mpz, (long)-1); + + dict = PyModule_GetDict(module); + if (dict != NULL) { + PyDict_SetItemString(dict, "MPZType", (PyObject*)&MPZtype); + } + finally: + return; +} /* initmpz() */ + +#ifdef MAKEDUMMYINT +int _mpz_dummy_int; /* XXX otherwise, we're .bss-less (DYNLOAD->Jack?) */ +#endif /* def MAKEDUMMYINT */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/nismodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/nismodule.c new file mode 100644 index 00000000..adbd852e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/nismodule.c @@ -0,0 +1,382 @@ +/*********************************************************** + Written by: + Fred Gansevles + B&O group, + Faculteit der Informatica, + Universiteit Twente, + Enschede, + the Netherlands. +******************************************************************/ + +/* NIS module implementation */ + +#include "Python.h" + +#include +#include +#include +#include +#include + +#ifdef __sgi +/* This is missing from rpcsvc/ypclnt.h */ +extern int yp_get_default_domain(char **); +#endif + +static PyObject *NisError; + +static PyObject * +nis_error (int err) +{ + PyErr_SetString(NisError, yperr_string(err)); + return NULL; +} + +static struct nis_map { + char *alias; + char *map; + int fix; +} aliases [] = { + {"passwd", "passwd.byname", 0}, + {"group", "group.byname", 0}, + {"networks", "networks.byaddr", 0}, + {"hosts", "hosts.byname", 0}, + {"protocols", "protocols.bynumber", 0}, + {"services", "services.byname", 0}, + {"aliases", "mail.aliases", 1}, /* created with 'makedbm -a' */ + {"ethers", "ethers.byname", 0}, + {0L, 0L, 0} +}; + +static char * +nis_mapname (char *map, int *pfix) +{ + int i; + + *pfix = 0; + for (i=0; aliases[i].alias != 0L; i++) { + if (!strcmp (aliases[i].alias, map)) { + *pfix = aliases[i].fix; + return aliases[i].map; + } + if (!strcmp (aliases[i].map, map)) { + *pfix = aliases[i].fix; + return aliases[i].map; + } + } + + return map; +} + +typedef int (*foreachfunc)(int, char *, int, char *, int, char *); + +struct ypcallback_data { + PyObject *dict; + int fix; +}; + +static int +nis_foreach (int instatus, char *inkey, int inkeylen, char *inval, + int invallen, struct ypcallback_data *indata) +{ + if (instatus == YP_TRUE) { + PyObject *key; + PyObject *val; + int err; + + if (indata->fix) { + if (inkeylen > 0 && inkey[inkeylen-1] == '\0') + inkeylen--; + if (invallen > 0 && inval[invallen-1] == '\0') + invallen--; + } + key = PyString_FromStringAndSize(inkey, inkeylen); + val = PyString_FromStringAndSize(inval, invallen); + if (key == NULL || val == NULL) { + /* XXX error -- don't know how to handle */ + PyErr_Clear(); + Py_XDECREF(key); + Py_XDECREF(val); + return 1; + } + err = PyDict_SetItem(indata->dict, key, val); + Py_DECREF(key); + Py_DECREF(val); + if (err != 0) { + PyErr_Clear(); + return 1; + } + return 0; + } + return 1; +} + +static PyObject * +nis_match (PyObject *self, PyObject *args) +{ + char *match; + char *domain; + int keylen, len; + char *key, *map; + int err; + PyObject *res; + int fix; + + if (!PyArg_ParseTuple(args, "t#s:match", &key, &keylen, &map)) + return NULL; + if ((err = yp_get_default_domain(&domain)) != 0) + return nis_error(err); + map = nis_mapname (map, &fix); + if (fix) + keylen++; + Py_BEGIN_ALLOW_THREADS + err = yp_match (domain, map, key, keylen, &match, &len); + Py_END_ALLOW_THREADS + if (fix) + len--; + if (err != 0) + return nis_error(err); + res = PyString_FromStringAndSize (match, len); + free (match); + return res; +} + +static PyObject * +nis_cat (PyObject *self, PyObject *args) +{ + char *domain; + char *map; + struct ypall_callback cb; + struct ypcallback_data data; + PyObject *dict; + int err; + + if (!PyArg_ParseTuple(args, "s:cat", &map)) + return NULL; + if ((err = yp_get_default_domain(&domain)) != 0) + return nis_error(err); + dict = PyDict_New (); + if (dict == NULL) + return NULL; + cb.foreach = (foreachfunc)nis_foreach; + data.dict = dict; + map = nis_mapname (map, &data.fix); + cb.data = (char *)&data; + Py_BEGIN_ALLOW_THREADS + err = yp_all (domain, map, &cb); + Py_END_ALLOW_THREADS + if (err != 0) { + Py_DECREF(dict); + return nis_error(err); + } + return dict; +} + +/* These should be u_long on Sun h/w but not on 64-bit h/w. + This is not portable to machines with 16-bit ints and no prototypes */ +#ifndef YPPROC_MAPLIST +#define YPPROC_MAPLIST 11 +#endif +#ifndef YPPROG +#define YPPROG 100004 +#endif +#ifndef YPVERS +#define YPVERS 2 +#endif + +typedef char *domainname; +typedef char *mapname; + +enum nisstat { + NIS_TRUE = 1, + NIS_NOMORE = 2, + NIS_FALSE = 0, + NIS_NOMAP = -1, + NIS_NODOM = -2, + NIS_NOKEY = -3, + NIS_BADOP = -4, + NIS_BADDB = -5, + NIS_YPERR = -6, + NIS_BADARGS = -7, + NIS_VERS = -8 +}; +typedef enum nisstat nisstat; + +struct nismaplist { + mapname map; + struct nismaplist *next; +}; +typedef struct nismaplist nismaplist; + +struct nisresp_maplist { + nisstat stat; + nismaplist *maps; +}; +typedef struct nisresp_maplist nisresp_maplist; + +static struct timeval TIMEOUT = { 25, 0 }; + +static +bool_t +nis_xdr_domainname(XDR *xdrs, domainname *objp) +{ + if (!xdr_string(xdrs, objp, YPMAXDOMAIN)) { + return (FALSE); + } + return (TRUE); +} + +static +bool_t +nis_xdr_mapname(XDR *xdrs, mapname *objp) +{ + if (!xdr_string(xdrs, objp, YPMAXMAP)) { + return (FALSE); + } + return (TRUE); +} + +static +bool_t +nis_xdr_ypmaplist(XDR *xdrs, nismaplist *objp) +{ + if (!nis_xdr_mapname(xdrs, &objp->map)) { + return (FALSE); + } + if (!xdr_pointer(xdrs, (char **)&objp->next, + sizeof(nismaplist), (xdrproc_t)nis_xdr_ypmaplist)) + { + return (FALSE); + } + return (TRUE); +} + +static +bool_t +nis_xdr_ypstat(XDR *xdrs, nisstat *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) { + return (FALSE); + } + return (TRUE); +} + + +static +bool_t +nis_xdr_ypresp_maplist(XDR *xdrs, nisresp_maplist *objp) +{ + if (!nis_xdr_ypstat(xdrs, &objp->stat)) { + return (FALSE); + } + if (!xdr_pointer(xdrs, (char **)&objp->maps, + sizeof(nismaplist), (xdrproc_t)nis_xdr_ypmaplist)) + { + return (FALSE); + } + return (TRUE); +} + + +static +nisresp_maplist * +nisproc_maplist_2(domainname *argp, CLIENT *clnt) +{ + static nisresp_maplist res; + + memset(&res, 0, sizeof(res)); + if (clnt_call(clnt, YPPROC_MAPLIST, + (xdrproc_t)nis_xdr_domainname, (caddr_t)argp, + (xdrproc_t)nis_xdr_ypresp_maplist, (caddr_t)&res, + TIMEOUT) != RPC_SUCCESS) + { + return (NULL); + } + return (&res); +} + +static +nismaplist * +nis_maplist (void) +{ + nisresp_maplist *list; + char *dom; + CLIENT *cl; + char *server = NULL; + int mapi = 0; + int err; + + if ((err = yp_get_default_domain (&dom)) != 0) { + nis_error(err); + return NULL; + } + + while (!server && aliases[mapi].map != 0L) { + yp_master (dom, aliases[mapi].map, &server); + mapi++; + } + if (!server) { + PyErr_SetString(NisError, "No NIS master found for any map"); + return NULL; + } + cl = clnt_create(server, YPPROG, YPVERS, "tcp"); + if (cl == NULL) { + PyErr_SetString(NisError, clnt_spcreateerror(server)); + goto finally; + } + list = nisproc_maplist_2 (&dom, cl); + clnt_destroy(cl); + if (list == NULL) + goto finally; + if (list->stat != NIS_TRUE) + goto finally; + + free(server); + return list->maps; + + finally: + free(server); + return NULL; +} + +static PyObject * +nis_maps (PyObject *self) +{ + nismaplist *maps; + PyObject *list; + + if ((maps = nis_maplist ()) == NULL) + return NULL; + if ((list = PyList_New(0)) == NULL) + return NULL; + for (maps = maps; maps; maps = maps->next) { + PyObject *str = PyString_FromString(maps->map); + if (!str || PyList_Append(list, str) < 0) + { + Py_DECREF(list); + list = NULL; + break; + } + Py_DECREF(str); + } + /* XXX Shouldn't we free the list of maps now? */ + return list; +} + +static PyMethodDef nis_methods[] = { + {"match", nis_match, METH_VARARGS}, + {"cat", nis_cat, METH_VARARGS}, + {"maps", (PyCFunction)nis_maps, METH_NOARGS}, + {NULL, NULL} /* Sentinel */ +}; + +void +initnis (void) +{ + PyObject *m, *d; + m = Py_InitModule("nis", nis_methods); + d = PyModule_GetDict(m); + NisError = PyErr_NewException("nis.error", NULL, NULL); + if (NisError != NULL) + PyDict_SetItemString(d, "error", NisError); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/operator.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/operator.c new file mode 100644 index 00000000..c12de483 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/operator.c @@ -0,0 +1,264 @@ + +#include "Python.h" + +PyDoc_STRVAR(operator_doc, +"Operator interface.\n\ +\n\ +This module exports a set of functions implemented in C corresponding\n\ +to the intrinsic operators of Python. For example, operator.add(x, y)\n\ +is equivalent to the expression x+y. The function names are those\n\ +used for special class methods; variants without leading and trailing\n\ +'__' are also provided for convenience."); + +#define spam1(OP,AOP) static PyObject *OP(PyObject *s, PyObject *a1) { \ + return AOP(a1); } + +#define spam2(OP,AOP) static PyObject *OP(PyObject *s, PyObject *a) { \ + PyObject *a1, *a2; \ + if(! PyArg_UnpackTuple(a,#OP,2,2,&a1,&a2)) return NULL; \ + return AOP(a1,a2); } + +#define spamoi(OP,AOP) static PyObject *OP(PyObject *s, PyObject *a) { \ + PyObject *a1; int a2; \ + if(! PyArg_ParseTuple(a,"Oi:" #OP,&a1,&a2)) return NULL; \ + return AOP(a1,a2); } + +#define spam2n(OP,AOP) static PyObject *OP(PyObject *s, PyObject *a) { \ + PyObject *a1, *a2; \ + if(! PyArg_UnpackTuple(a,#OP,2,2,&a1,&a2)) return NULL; \ + if(-1 == AOP(a1,a2)) return NULL; \ + Py_INCREF(Py_None); \ + return Py_None; } + +#define spam3n(OP,AOP) static PyObject *OP(PyObject *s, PyObject *a) { \ + PyObject *a1, *a2, *a3; \ + if(! PyArg_UnpackTuple(a,#OP,3,3,&a1,&a2,&a3)) return NULL; \ + if(-1 == AOP(a1,a2,a3)) return NULL; \ + Py_INCREF(Py_None); \ + return Py_None; } + +#define spami(OP,AOP) static PyObject *OP(PyObject *s, PyObject *a1) { \ + long r; \ + if(-1 == (r=AOP(a1))) return NULL; \ + return PyBool_FromLong(r); } + +#define spami2(OP,AOP) static PyObject *OP(PyObject *s, PyObject *a) { \ + PyObject *a1, *a2; long r; \ + if(! PyArg_UnpackTuple(a,#OP,2,2,&a1,&a2)) return NULL; \ + if(-1 == (r=AOP(a1,a2))) return NULL; \ + return PyInt_FromLong(r); } + +#define spami2b(OP,AOP) static PyObject *OP(PyObject *s, PyObject *a) { \ + PyObject *a1, *a2; long r; \ + if(! PyArg_UnpackTuple(a,#OP,2,2,&a1,&a2)) return NULL; \ + if(-1 == (r=AOP(a1,a2))) return NULL; \ + return PyBool_FromLong(r); } + +#define spamrc(OP,A) static PyObject *OP(PyObject *s, PyObject *a) { \ + PyObject *a1, *a2; \ + if(! PyArg_UnpackTuple(a,#OP,2,2,&a1,&a2)) return NULL; \ + return PyObject_RichCompare(a1,a2,A); } + +spami(isCallable , PyCallable_Check) +spami(isNumberType , PyNumber_Check) +spami(truth , PyObject_IsTrue) +spam2(op_add , PyNumber_Add) +spam2(op_sub , PyNumber_Subtract) +spam2(op_mul , PyNumber_Multiply) +spam2(op_div , PyNumber_Divide) +spam2(op_floordiv , PyNumber_FloorDivide) +spam2(op_truediv , PyNumber_TrueDivide) +spam2(op_mod , PyNumber_Remainder) +spam1(op_neg , PyNumber_Negative) +spam1(op_pos , PyNumber_Positive) +spam1(op_abs , PyNumber_Absolute) +spam1(op_inv , PyNumber_Invert) +spam1(op_invert , PyNumber_Invert) +spam2(op_lshift , PyNumber_Lshift) +spam2(op_rshift , PyNumber_Rshift) +spami(op_not_ , PyObject_Not) +spam2(op_and_ , PyNumber_And) +spam2(op_xor , PyNumber_Xor) +spam2(op_or_ , PyNumber_Or) +spami(isSequenceType , PySequence_Check) +spam2(op_concat , PySequence_Concat) +spamoi(op_repeat , PySequence_Repeat) +spami2b(op_contains , PySequence_Contains) +spami2b(sequenceIncludes, PySequence_Contains) +spami2(indexOf , PySequence_Index) +spami2(countOf , PySequence_Count) +spami(isMappingType , PyMapping_Check) +spam2(op_getitem , PyObject_GetItem) +spam2n(op_delitem , PyObject_DelItem) +spam3n(op_setitem , PyObject_SetItem) +spamrc(op_lt , Py_LT) +spamrc(op_le , Py_LE) +spamrc(op_eq , Py_EQ) +spamrc(op_ne , Py_NE) +spamrc(op_gt , Py_GT) +spamrc(op_ge , Py_GE) + +static PyObject* +op_pow(PyObject *s, PyObject *a) +{ + PyObject *a1, *a2; + if (PyArg_UnpackTuple(a,"pow", 2, 2, &a1, &a2)) + return PyNumber_Power(a1, a2, Py_None); + return NULL; +} + +static PyObject* +is_(PyObject *s, PyObject *a) +{ + PyObject *a1, *a2, *result = NULL; + if (PyArg_UnpackTuple(a,"is_", 2, 2, &a1, &a2)) { + result = (a1 == a2) ? Py_True : Py_False; + Py_INCREF(result); + } + return result; +} + +static PyObject* +is_not(PyObject *s, PyObject *a) +{ + PyObject *a1, *a2, *result = NULL; + if (PyArg_UnpackTuple(a,"is_not", 2, 2, &a1, &a2)) { + result = (a1 != a2) ? Py_True : Py_False; + Py_INCREF(result); + } + return result; +} + +static PyObject* +op_getslice(PyObject *s, PyObject *a) +{ + PyObject *a1; + int a2,a3; + + if (!PyArg_ParseTuple(a,"Oii:getslice",&a1,&a2,&a3)) + return NULL; + return PySequence_GetSlice(a1,a2,a3); +} + +static PyObject* +op_setslice(PyObject *s, PyObject *a) +{ + PyObject *a1, *a4; + int a2,a3; + + if (!PyArg_ParseTuple(a,"OiiO:setslice",&a1,&a2,&a3,&a4)) + return NULL; + + if (-1 == PySequence_SetSlice(a1,a2,a3,a4)) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +op_delslice(PyObject *s, PyObject *a) +{ + PyObject *a1; + int a2,a3; + + if(! PyArg_ParseTuple(a,"Oii:delslice",&a1,&a2,&a3)) + return NULL; + + if (-1 == PySequence_DelSlice(a1,a2,a3)) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +#undef spam1 +#undef spam2 +#undef spam1o +#undef spam1o +#define spam1(OP,DOC) {#OP, OP, METH_VARARGS, PyDoc_STR(DOC)}, +#define spam2(OP,ALTOP,DOC) {#OP, op_##OP, METH_VARARGS, DOC}, \ + {#ALTOP, op_##OP, METH_VARARGS, PyDoc_STR(DOC)}, +#define spam1o(OP,DOC) {#OP, OP, METH_O, PyDoc_STR(DOC)}, +#define spam2o(OP,ALTOP,DOC) {#OP, op_##OP, METH_O, DOC}, \ + {#ALTOP, op_##OP, METH_O, PyDoc_STR(DOC)}, + +static struct PyMethodDef operator_methods[] = { + +spam1o(isCallable, + "isCallable(a) -- Same as callable(a).") +spam1o(isNumberType, + "isNumberType(a) -- Return True if a has a numeric type, False otherwise.") +spam1o(isSequenceType, + "isSequenceType(a) -- Return True if a has a sequence type, False otherwise.") +spam1o(truth, + "truth(a) -- Return True if a is true, False otherwise.") +spam2(contains,__contains__, + "contains(a, b) -- Same as b in a (note reversed operands).") +spam1(sequenceIncludes, + "sequenceIncludes(a, b) -- Same as b in a (note reversed operands; deprecated).") +spam1(indexOf, + "indexOf(a, b) -- Return the first index of b in a.") +spam1(countOf, + "countOf(a, b) -- Return the number of times b occurs in a.") +spam1o(isMappingType, + "isMappingType(a) -- Return True if a has a mapping type, False otherwise.") + +spam1(is_, "is_(a, b) -- Same as a is b.") +spam1(is_not, "is_not(a, b) -- Same as a is not b.") +spam2(add,__add__, "add(a, b) -- Same as a + b.") +spam2(sub,__sub__, "sub(a, b) -- Same as a - b.") +spam2(mul,__mul__, "mul(a, b) -- Same as a * b.") +spam2(div,__div__, "div(a, b) -- Same as a / b when __future__.division is not in effect.") +spam2(floordiv,__floordiv__, "floordiv(a, b) -- Same as a // b.") +spam2(truediv,__truediv__, "truediv(a, b) -- Same as a / b when __future__.division is in effect.") +spam2(mod,__mod__, "mod(a, b) -- Same as a % b.") +spam2o(neg,__neg__, "neg(a) -- Same as -a.") +spam2o(pos,__pos__, "pos(a) -- Same as +a.") +spam2o(abs,__abs__, "abs(a) -- Same as abs(a).") +spam2o(inv,__inv__, "inv(a) -- Same as ~a.") +spam2o(invert,__invert__, "invert(a) -- Same as ~a.") +spam2(lshift,__lshift__, "lshift(a, b) -- Same as a << b.") +spam2(rshift,__rshift__, "rshift(a, b) -- Same as a >> b.") +spam2o(not_,__not__, "not_(a) -- Same as not a.") +spam2(and_,__and__, "and_(a, b) -- Same as a & b.") +spam2(xor,__xor__, "xor(a, b) -- Same as a ^ b.") +spam2(or_,__or__, "or_(a, b) -- Same as a | b.") +spam2(concat,__concat__, + "concat(a, b) -- Same as a + b, for a and b sequences.") +spam2(repeat,__repeat__, + "repeat(a, b) -- Return a * b, where a is a sequence, and b is an integer.") +spam2(getitem,__getitem__, + "getitem(a, b) -- Same as a[b].") +spam2(setitem,__setitem__, + "setitem(a, b, c) -- Same as a[b] = c.") +spam2(delitem,__delitem__, + "delitem(a, b) -- Same as del a[b].") +spam2(pow,__pow__, "pow(a, b) -- Same as a**b.") +spam2(getslice,__getslice__, + "getslice(a, b, c) -- Same as a[b:c].") +spam2(setslice,__setslice__, +"setslice(a, b, c, d) -- Same as a[b:c] = d.") +spam2(delslice,__delslice__, +"delslice(a, b, c) -- Same as del a[b:c].") +spam2(lt,__lt__, "lt(a, b) -- Same as ab.") +spam2(ge,__ge__, "ge(a, b) -- Same as a>=b.") + + {NULL, NULL} /* sentinel */ + +}; + + +/* Initialization function for the module (*must* be called initoperator) */ + +PyMODINIT_FUNC +initoperator(void) +{ + /* Create the module and add the functions */ + Py_InitModule4("operator", operator_methods, operator_doc, + (PyObject*)NULL, PYTHON_API_VERSION); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ossaudiodev.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ossaudiodev.c new file mode 100644 index 00000000..06215231 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/ossaudiodev.c @@ -0,0 +1,1094 @@ +/* + * ossaudiodev -- Python interface to the OSS (Open Sound System) API. + * This is the standard audio API for Linux and some + * flavours of BSD [XXX which ones?]; it is also available + * for a wide range of commercial Unices. + * + * Originally written by Peter Bosch, March 2000, as linuxaudiodev. + * + * Renamed to ossaudiodev and rearranged/revised/hacked up + * by Greg Ward , November 2002. + * Mixer interface by Nicholas FitzRoy-Dale , Dec 2002. + * + * (c) 2000 Peter Bosch. All Rights Reserved. + * (c) 2002 Gregory P. Ward. All Rights Reserved. + * (c) 2002 Python Software Foundation. All Rights Reserved. + * + * XXX need a license statement + * + * $Id: ossaudiodev.c,v 1.34 2003/06/02 14:15:34 gvanrossum Exp $ + */ + +#include "Python.h" +#include "structmember.h" + +#ifdef HAVE_FCNTL_H +#include +#else +#define O_RDONLY 00 +#define O_WRONLY 01 +#endif + +#include +#include + +#if defined(linux) + +typedef unsigned long uint32_t; + +#elif defined(__FreeBSD__) + +# ifndef SNDCTL_DSP_CHANNELS +# define SNDCTL_DSP_CHANNELS SOUND_PCM_WRITE_CHANNELS +# endif + +#endif + +typedef struct { + PyObject_HEAD; + int fd; /* The open file */ + int mode; /* file mode */ + int icount; /* Input count */ + int ocount; /* Output count */ + uint32_t afmts; /* Audio formats supported by hardware */ +} oss_audio_t; + +typedef struct { + PyObject_HEAD; + int fd; /* The open mixer device */ +} oss_mixer_t; + + +static PyTypeObject OSSAudioType; +static PyTypeObject OSSMixerType; + +static PyObject *OSSAudioError; + + +/* ---------------------------------------------------------------------- + * DSP object initialization/deallocation + */ + +static oss_audio_t * +newossobject(PyObject *arg) +{ + oss_audio_t *self; + int fd, afmts, imode; + char *basedev = NULL; + char *mode = NULL; + + /* Two ways to call open(): + open(device, mode) (for consistency with builtin open()) + open(mode) (for backwards compatibility) + because the *first* argument is optional, parsing args is + a wee bit tricky. */ + if (!PyArg_ParseTuple(arg, "s|s:open", &basedev, &mode)) + return NULL; + if (mode == NULL) { /* only one arg supplied */ + mode = basedev; + basedev = NULL; + } + + if (strcmp(mode, "r") == 0) + imode = O_RDONLY; + else if (strcmp(mode, "w") == 0) + imode = O_WRONLY; + else if (strcmp(mode, "rw") == 0) + imode = O_RDWR; + else { + PyErr_SetString(OSSAudioError, "mode must be 'r', 'w', or 'rw'"); + return NULL; + } + + /* Open the correct device: either the 'device' argument, + or the AUDIODEV environment variable, or "/dev/dsp". */ + if (basedev == NULL) { /* called with one arg */ + basedev = getenv("AUDIODEV"); + if (basedev == NULL) /* $AUDIODEV not set */ + basedev = "/dev/dsp"; + } + + /* Open with O_NONBLOCK to avoid hanging on devices that only allow + one open at a time. This does *not* affect later I/O; OSS + provides a special ioctl() for non-blocking read/write, which is + exposed via oss_nonblock() below. */ + if ((fd = open(basedev, imode|O_NONBLOCK)) == -1) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev); + return NULL; + } + + /* And (try to) put it back in blocking mode so we get the + expected write() semantics. */ + if (fcntl(fd, F_SETFL, 0) == -1) { + close(fd); + PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev); + return NULL; + } + + if (ioctl(fd, SNDCTL_DSP_GETFMTS, &afmts) == -1) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev); + return NULL; + } + /* Create and initialize the object */ + if ((self = PyObject_New(oss_audio_t, &OSSAudioType)) == NULL) { + close(fd); + return NULL; + } + self->fd = fd; + self->mode = imode; + self->icount = self->ocount = 0; + self->afmts = afmts; + return self; +} + +static void +oss_dealloc(oss_audio_t *self) +{ + /* if already closed, don't reclose it */ + if (self->fd != -1) + close(self->fd); + PyObject_Del(self); +} + + +/* ---------------------------------------------------------------------- + * Mixer object initialization/deallocation + */ + +static oss_mixer_t * +newossmixerobject(PyObject *arg) +{ + char *basedev = NULL; + int fd; + oss_mixer_t *self; + + if (!PyArg_ParseTuple(arg, "|s", &basedev)) { + return NULL; + } + + if (basedev == NULL) { + basedev = getenv("MIXERDEV"); + if (basedev == NULL) /* MIXERDEV not set */ + basedev = "/dev/mixer"; + } + + if ((fd = open(basedev, O_RDWR)) == -1) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, basedev); + return NULL; + } + + if ((self = PyObject_New(oss_mixer_t, &OSSMixerType)) == NULL) { + close(fd); + return NULL; + } + + self->fd = fd; + + return self; +} + +static void +oss_mixer_dealloc(oss_mixer_t *self) +{ + /* if already closed, don't reclose it */ + if (self->fd != -1) + close(self->fd); + PyObject_Del(self); +} + + +/* Methods to wrap the OSS ioctls. The calling convention is pretty + simple: + nonblock() -> ioctl(fd, SNDCTL_DSP_NONBLOCK) + fmt = setfmt(fmt) -> ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) + etc. +*/ + + +/* ---------------------------------------------------------------------- + * Helper functions + */ + +/* _do_ioctl_1() is a private helper function used for the OSS ioctls -- + SNDCTL_DSP_{SETFMT,CHANNELS,SPEED} -- that that are called from C + like this: + ioctl(fd, SNDCTL_DSP_cmd, &arg) + + where arg is the value to set, and on return the driver sets arg to + the value that was actually set. Mapping this to Python is obvious: + arg = dsp.xxx(arg) +*/ +static PyObject * +_do_ioctl_1(int fd, PyObject *args, char *fname, int cmd) +{ + char argfmt[33] = "i:"; + int arg; + + assert(strlen(fname) <= 30); + strcat(argfmt, fname); + if (!PyArg_ParseTuple(args, argfmt, &arg)) + return NULL; + + if (ioctl(fd, cmd, &arg) == -1) + return PyErr_SetFromErrno(PyExc_IOError); + return PyInt_FromLong(arg); +} + + +/* _do_ioctl_1_internal() is a wrapper for ioctls that take no inputs + but return an output -- ie. we need to pass a pointer to a local C + variable so the driver can write its output there, but from Python + all we see is the return value. For example, + SOUND_MIXER_READ_DEVMASK returns a bitmask of available mixer + devices, but does not use the value of the parameter passed-in in any + way. +*/ +static PyObject * +_do_ioctl_1_internal(int fd, PyObject *args, char *fname, int cmd) +{ + char argfmt[32] = ":"; + int arg = 0; + + assert(strlen(fname) <= 30); + strcat(argfmt, fname); + if (!PyArg_ParseTuple(args, argfmt, &arg)) + return NULL; + + if (ioctl(fd, cmd, &arg) == -1) + return PyErr_SetFromErrno(PyExc_IOError); + return PyInt_FromLong(arg); +} + + + +/* _do_ioctl_0() is a private helper for the no-argument ioctls: + SNDCTL_DSP_{SYNC,RESET,POST}. */ +static PyObject * +_do_ioctl_0(int fd, PyObject *args, char *fname, int cmd) +{ + char argfmt[32] = ":"; + int rv; + + assert(strlen(fname) <= 30); + strcat(argfmt, fname); + if (!PyArg_ParseTuple(args, argfmt)) + return NULL; + + /* According to hannu@opensound.com, all three of the ioctls that + use this function can block, so release the GIL. This is + especially important for SYNC, which can block for several + seconds. */ + Py_BEGIN_ALLOW_THREADS + rv = ioctl(fd, cmd, 0); + Py_END_ALLOW_THREADS + + if (rv == -1) + return PyErr_SetFromErrno(PyExc_IOError); + Py_INCREF(Py_None); + return Py_None; +} + + +/* ---------------------------------------------------------------------- + * Methods of DSP objects (OSSAudioType) + */ + +static PyObject * +oss_nonblock(oss_audio_t *self, PyObject *args) +{ + /* Hmmm: it doesn't appear to be possible to return to blocking + mode once we're in non-blocking mode! */ + if (!PyArg_ParseTuple(args, ":nonblock")) + return NULL; + if (ioctl(self->fd, SNDCTL_DSP_NONBLOCK, NULL) == -1) + return PyErr_SetFromErrno(PyExc_IOError); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +oss_setfmt(oss_audio_t *self, PyObject *args) +{ + return _do_ioctl_1(self->fd, args, "setfmt", SNDCTL_DSP_SETFMT); +} + +static PyObject * +oss_getfmts(oss_audio_t *self, PyObject *args) +{ + int mask; + if (!PyArg_ParseTuple(args, ":getfmts")) + return NULL; + if (ioctl(self->fd, SNDCTL_DSP_GETFMTS, &mask) == -1) + return PyErr_SetFromErrno(PyExc_IOError); + return PyInt_FromLong(mask); +} + +static PyObject * +oss_channels(oss_audio_t *self, PyObject *args) +{ + return _do_ioctl_1(self->fd, args, "channels", SNDCTL_DSP_CHANNELS); +} + +static PyObject * +oss_speed(oss_audio_t *self, PyObject *args) +{ + return _do_ioctl_1(self->fd, args, "speed", SNDCTL_DSP_SPEED); +} + +static PyObject * +oss_sync(oss_audio_t *self, PyObject *args) +{ + return _do_ioctl_0(self->fd, args, "sync", SNDCTL_DSP_SYNC); +} + +static PyObject * +oss_reset(oss_audio_t *self, PyObject *args) +{ + return _do_ioctl_0(self->fd, args, "reset", SNDCTL_DSP_RESET); +} + +static PyObject * +oss_post(oss_audio_t *self, PyObject *args) +{ + return _do_ioctl_0(self->fd, args, "post", SNDCTL_DSP_POST); +} + + +/* Regular file methods: read(), write(), close(), etc. as well + as one convenience method, writeall(). */ + +static PyObject * +oss_read(oss_audio_t *self, PyObject *args) +{ + int size, count; + char *cp; + PyObject *rv; + + if (!PyArg_ParseTuple(args, "i:read", &size)) + return NULL; + rv = PyString_FromStringAndSize(NULL, size); + if (rv == NULL) + return NULL; + cp = PyString_AS_STRING(rv); + + Py_BEGIN_ALLOW_THREADS + count = read(self->fd, cp, size); + Py_END_ALLOW_THREADS + + if (count < 0) { + PyErr_SetFromErrno(PyExc_IOError); + Py_DECREF(rv); + return NULL; + } + self->icount += count; + _PyString_Resize(&rv, count); + return rv; +} + +static PyObject * +oss_write(oss_audio_t *self, PyObject *args) +{ + char *cp; + int rv, size; + + if (!PyArg_ParseTuple(args, "s#:write", &cp, &size)) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + rv = write(self->fd, cp, size); + Py_END_ALLOW_THREADS + + if (rv == -1) { + return PyErr_SetFromErrno(PyExc_IOError); + } else { + self->ocount += rv; + } + return PyInt_FromLong(rv); +} + +static PyObject * +oss_writeall(oss_audio_t *self, PyObject *args) +{ + char *cp; + int rv, size; + fd_set write_set_fds; + int select_rv; + + /* NB. writeall() is only useful in non-blocking mode: according to + Guenter Geiger on the linux-audio-dev list + (http://eca.cx/lad/2002/11/0380.html), OSS guarantees that + write() in blocking mode consumes the whole buffer. In blocking + mode, the behaviour of write() and writeall() from Python is + indistinguishable. */ + + if (!PyArg_ParseTuple(args, "s#:write", &cp, &size)) + return NULL; + + /* use select to wait for audio device to be available */ + FD_ZERO(&write_set_fds); + FD_SET(self->fd, &write_set_fds); + + while (size > 0) { + Py_BEGIN_ALLOW_THREADS + select_rv = select(self->fd+1, NULL, &write_set_fds, NULL, NULL); + Py_END_ALLOW_THREADS + assert(select_rv != 0); /* no timeout, can't expire */ + if (select_rv == -1) + return PyErr_SetFromErrno(PyExc_IOError); + + Py_BEGIN_ALLOW_THREADS + rv = write(self->fd, cp, size); + Py_END_ALLOW_THREADS + if (rv == -1) { + if (errno == EAGAIN) { /* buffer is full, try again */ + errno = 0; + continue; + } else /* it's a real error */ + return PyErr_SetFromErrno(PyExc_IOError); + } else { /* wrote rv bytes */ + self->ocount += rv; + size -= rv; + cp += rv; + } + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +oss_close(oss_audio_t *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":close")) + return NULL; + + if (self->fd >= 0) { + Py_BEGIN_ALLOW_THREADS + close(self->fd); + Py_END_ALLOW_THREADS + self->fd = -1; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +oss_fileno(oss_audio_t *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":fileno")) + return NULL; + return PyInt_FromLong(self->fd); +} + + +/* Convenience methods: these generally wrap a couple of ioctls into one + common task. */ + +static PyObject * +oss_setparameters(oss_audio_t *self, PyObject *args) +{ + int wanted_fmt, wanted_channels, wanted_rate, strict=0; + int fmt, channels, rate; + PyObject * rv; /* return tuple (fmt, channels, rate) */ + + if (!PyArg_ParseTuple(args, "iii|i:setparameters", + &wanted_fmt, &wanted_channels, &wanted_rate, + &strict)) + return NULL; + + fmt = wanted_fmt; + if (ioctl(self->fd, SNDCTL_DSP_SETFMT, &fmt) == -1) { + return PyErr_SetFromErrno(PyExc_IOError); + } + if (strict && fmt != wanted_fmt) { + return PyErr_Format + (OSSAudioError, + "unable to set requested format (wanted %d, got %d)", + wanted_fmt, fmt); + } + + channels = wanted_channels; + if (ioctl(self->fd, SNDCTL_DSP_CHANNELS, &channels) == -1) { + return PyErr_SetFromErrno(PyExc_IOError); + } + if (strict && channels != wanted_channels) { + return PyErr_Format + (OSSAudioError, + "unable to set requested channels (wanted %d, got %d)", + wanted_channels, channels); + } + + rate = wanted_rate; + if (ioctl(self->fd, SNDCTL_DSP_SPEED, &rate) == -1) { + return PyErr_SetFromErrno(PyExc_IOError); + } + if (strict && rate != wanted_rate) { + return PyErr_Format + (OSSAudioError, + "unable to set requested rate (wanted %d, got %d)", + wanted_rate, rate); + } + + /* Construct the return value: a (fmt, channels, rate) tuple that + tells what the audio hardware was actually set to. */ + rv = PyTuple_New(3); + if (rv == NULL) + return NULL; + PyTuple_SET_ITEM(rv, 0, PyInt_FromLong(fmt)); + PyTuple_SET_ITEM(rv, 1, PyInt_FromLong(channels)); + PyTuple_SET_ITEM(rv, 2, PyInt_FromLong(rate)); + return rv; +} + +static int +_ssize(oss_audio_t *self, int *nchannels, int *ssize) +{ + int fmt; + + fmt = 0; + if (ioctl(self->fd, SNDCTL_DSP_SETFMT, &fmt) < 0) + return -errno; + + switch (fmt) { + case AFMT_MU_LAW: + case AFMT_A_LAW: + case AFMT_U8: + case AFMT_S8: + *ssize = 1; /* 8 bit formats: 1 byte */ + break; + case AFMT_S16_LE: + case AFMT_S16_BE: + case AFMT_U16_LE: + case AFMT_U16_BE: + *ssize = 2; /* 16 bit formats: 2 byte */ + break; + case AFMT_MPEG: + case AFMT_IMA_ADPCM: + default: + return -EOPNOTSUPP; + } + *nchannels = 0; + if (ioctl(self->fd, SNDCTL_DSP_CHANNELS, nchannels) < 0) + return -errno; + return 0; +} + + +/* bufsize returns the size of the hardware audio buffer in number + of samples */ +static PyObject * +oss_bufsize(oss_audio_t *self, PyObject *args) +{ + audio_buf_info ai; + int nchannels, ssize; + + if (!PyArg_ParseTuple(args, ":bufsize")) return NULL; + + if (_ssize(self, &nchannels, &ssize) < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + if (ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return PyInt_FromLong((ai.fragstotal * ai.fragsize) / (nchannels * ssize)); +} + +/* obufcount returns the number of samples that are available in the + hardware for playing */ +static PyObject * +oss_obufcount(oss_audio_t *self, PyObject *args) +{ + audio_buf_info ai; + int nchannels, ssize; + + if (!PyArg_ParseTuple(args, ":obufcount")) + return NULL; + + if (_ssize(self, &nchannels, &ssize) < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + if (ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return PyInt_FromLong((ai.fragstotal * ai.fragsize - ai.bytes) / + (ssize * nchannels)); +} + +/* obufcount returns the number of samples that can be played without + blocking */ +static PyObject * +oss_obuffree(oss_audio_t *self, PyObject *args) +{ + audio_buf_info ai; + int nchannels, ssize; + + if (!PyArg_ParseTuple(args, ":obuffree")) + return NULL; + + if (_ssize(self, &nchannels, &ssize) < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + if (ioctl(self->fd, SNDCTL_DSP_GETOSPACE, &ai) < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return PyInt_FromLong(ai.bytes / (ssize * nchannels)); +} + +static PyObject * +oss_getptr(oss_audio_t *self, PyObject *args) +{ + count_info info; + int req; + + if (!PyArg_ParseTuple(args, ":getptr")) + return NULL; + + if (self->mode == O_RDONLY) + req = SNDCTL_DSP_GETIPTR; + else + req = SNDCTL_DSP_GETOPTR; + if (ioctl(self->fd, req, &info) == -1) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return Py_BuildValue("iii", info.bytes, info.blocks, info.ptr); +} + + +/* ---------------------------------------------------------------------- + * Methods of mixer objects (OSSMixerType) + */ + +static PyObject * +oss_mixer_close(oss_mixer_t *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":close")) + return NULL; + + if (self->fd >= 0) { + close(self->fd); + self->fd = -1; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +oss_mixer_fileno(oss_mixer_t *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":fileno")) + return NULL; + return PyInt_FromLong(self->fd); +} + +/* Simple mixer interface methods */ + +static PyObject * +oss_mixer_controls(oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->fd, args, "controls", + SOUND_MIXER_READ_DEVMASK); +} + +static PyObject * +oss_mixer_stereocontrols(oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->fd, args, "stereocontrols", + SOUND_MIXER_READ_STEREODEVS); +} + +static PyObject * +oss_mixer_reccontrols(oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->fd, args, "reccontrols", + SOUND_MIXER_READ_RECMASK); +} + +static PyObject * +oss_mixer_get(oss_mixer_t *self, PyObject *args) +{ + int channel, volume; + + /* Can't use _do_ioctl_1 because of encoded arg thingy. */ + if (!PyArg_ParseTuple(args, "i:get", &channel)) + return NULL; + + if (channel < 0 || channel > SOUND_MIXER_NRDEVICES) { + PyErr_SetString(OSSAudioError, "Invalid mixer channel specified."); + return NULL; + } + + if (ioctl(self->fd, MIXER_READ(channel), &volume) == -1) + return PyErr_SetFromErrno(PyExc_IOError); + + return Py_BuildValue("(ii)", volume & 0xff, (volume & 0xff00) >> 8); +} + +static PyObject * +oss_mixer_set(oss_mixer_t *self, PyObject *args) +{ + int channel, volume, leftVol, rightVol; + + /* Can't use _do_ioctl_1 because of encoded arg thingy. */ + if (!PyArg_ParseTuple(args, "i(ii):set", &channel, &leftVol, &rightVol)) + return NULL; + + if (channel < 0 || channel > SOUND_MIXER_NRDEVICES) { + PyErr_SetString(OSSAudioError, "Invalid mixer channel specified."); + return NULL; + } + + if (leftVol < 0 || rightVol < 0 || leftVol > 100 || rightVol > 100) { + PyErr_SetString(OSSAudioError, "Volumes must be between 0 and 100."); + return NULL; + } + + volume = (rightVol << 8) | leftVol; + + if (ioctl(self->fd, MIXER_WRITE(channel), &volume) == -1) + return PyErr_SetFromErrno(PyExc_IOError); + + return Py_BuildValue("(ii)", volume & 0xff, (volume & 0xff00) >> 8); +} + +static PyObject * +oss_mixer_get_recsrc(oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1_internal(self->fd, args, "get_recsrc", + SOUND_MIXER_READ_RECSRC); +} + +static PyObject * +oss_mixer_set_recsrc(oss_mixer_t *self, PyObject *args) +{ + return _do_ioctl_1(self->fd, args, "set_recsrc", + SOUND_MIXER_WRITE_RECSRC); +} + + +/* ---------------------------------------------------------------------- + * Method tables and other bureaucracy + */ + +static PyMethodDef oss_methods[] = { + /* Regular file methods */ + { "read", (PyCFunction)oss_read, METH_VARARGS }, + { "write", (PyCFunction)oss_write, METH_VARARGS }, + { "writeall", (PyCFunction)oss_writeall, METH_VARARGS }, + { "close", (PyCFunction)oss_close, METH_VARARGS }, + { "fileno", (PyCFunction)oss_fileno, METH_VARARGS }, + + /* Simple ioctl wrappers */ + { "nonblock", (PyCFunction)oss_nonblock, METH_VARARGS }, + { "setfmt", (PyCFunction)oss_setfmt, METH_VARARGS }, + { "getfmts", (PyCFunction)oss_getfmts, METH_VARARGS }, + { "channels", (PyCFunction)oss_channels, METH_VARARGS }, + { "speed", (PyCFunction)oss_speed, METH_VARARGS }, + { "sync", (PyCFunction)oss_sync, METH_VARARGS }, + { "reset", (PyCFunction)oss_reset, METH_VARARGS }, + { "post", (PyCFunction)oss_post, METH_VARARGS }, + + /* Convenience methods -- wrap a couple of ioctls together */ + { "setparameters", (PyCFunction)oss_setparameters, METH_VARARGS }, + { "bufsize", (PyCFunction)oss_bufsize, METH_VARARGS }, + { "obufcount", (PyCFunction)oss_obufcount, METH_VARARGS }, + { "obuffree", (PyCFunction)oss_obuffree, METH_VARARGS }, + { "getptr", (PyCFunction)oss_getptr, METH_VARARGS }, + + /* Aliases for backwards compatibility */ + { "flush", (PyCFunction)oss_sync, METH_VARARGS }, + + { NULL, NULL} /* sentinel */ +}; + +static PyMethodDef oss_mixer_methods[] = { + /* Regular file method - OSS mixers are ioctl-only interface */ + { "close", (PyCFunction)oss_mixer_close, METH_VARARGS }, + { "fileno", (PyCFunction)oss_mixer_fileno, METH_VARARGS }, + + /* Simple ioctl wrappers */ + { "controls", (PyCFunction)oss_mixer_controls, METH_VARARGS }, + { "stereocontrols", (PyCFunction)oss_mixer_stereocontrols, METH_VARARGS}, + { "reccontrols", (PyCFunction)oss_mixer_reccontrols, METH_VARARGS}, + { "get", (PyCFunction)oss_mixer_get, METH_VARARGS }, + { "set", (PyCFunction)oss_mixer_set, METH_VARARGS }, + { "get_recsrc", (PyCFunction)oss_mixer_get_recsrc, METH_VARARGS }, + { "set_recsrc", (PyCFunction)oss_mixer_set_recsrc, METH_VARARGS }, + + { NULL, NULL} +}; + +static PyObject * +oss_getattr(oss_audio_t *self, char *name) +{ + return Py_FindMethod(oss_methods, (PyObject *)self, name); +} + +static PyObject * +oss_mixer_getattr(oss_mixer_t *self, char *name) +{ + return Py_FindMethod(oss_mixer_methods, (PyObject *)self, name); +} + +static PyTypeObject OSSAudioType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "ossaudiodev.oss_audio_device", /*tp_name*/ + sizeof(oss_audio_t), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)oss_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)oss_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + +static PyTypeObject OSSMixerType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "ossaudiodev.oss_mixer_device", /*tp_name*/ + sizeof(oss_mixer_t), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)oss_mixer_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)oss_mixer_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + + +static PyObject * +ossopen(PyObject *self, PyObject *args) +{ + return (PyObject *)newossobject(args); +} + +static PyObject * +ossopenmixer(PyObject *self, PyObject *args) +{ + return (PyObject *)newossmixerobject(args); +} + +static PyMethodDef ossaudiodev_methods[] = { + { "open", ossopen, METH_VARARGS }, + { "openmixer", ossopenmixer, METH_VARARGS }, + { 0, 0 }, +}; + + +#define _EXPORT_INT(mod, name) \ + if (PyModule_AddIntConstant(mod, #name, (long) (name)) == -1) return; + + +static char *control_labels[] = SOUND_DEVICE_LABELS; +static char *control_names[] = SOUND_DEVICE_NAMES; + + +static int +build_namelists (PyObject *module) +{ + PyObject *labels; + PyObject *names; + PyObject *s; + int num_controls; + int i; + + num_controls = sizeof(control_labels) / sizeof(control_labels[0]); + assert(num_controls == sizeof(control_names) / sizeof(control_names[0])); + + labels = PyList_New(num_controls); + names = PyList_New(num_controls); + for (i = 0; i < num_controls; i++) { + s = PyString_FromString(control_labels[i]); + if (s == NULL) + return -1; + PyList_SET_ITEM(labels, i, s); + + s = PyString_FromString(control_names[i]); + if (s == NULL) + return -1; + PyList_SET_ITEM(names, i, s); + } + + if (PyModule_AddObject(module, "control_labels", labels) == -1) + return -1; + if (PyModule_AddObject(module, "control_names", names) == -1) + return -1; + + return 0; +} + + +void +initossaudiodev(void) +{ + PyObject *m; + + m = Py_InitModule("ossaudiodev", ossaudiodev_methods); + + OSSAudioError = PyErr_NewException("ossaudiodev.OSSAudioError", + NULL, NULL); + if (OSSAudioError) { + /* Each call to PyModule_AddObject decrefs it; compensate: */ + Py_INCREF(OSSAudioError); + Py_INCREF(OSSAudioError); + PyModule_AddObject(m, "error", OSSAudioError); + PyModule_AddObject(m, "OSSAudioError", OSSAudioError); + } + + /* Build 'control_labels' and 'control_names' lists and add them + to the module. */ + if (build_namelists(m) == -1) /* XXX what to do here? */ + return; + + /* Expose the audio format numbers -- essential! */ + _EXPORT_INT(m, AFMT_QUERY); + _EXPORT_INT(m, AFMT_MU_LAW); + _EXPORT_INT(m, AFMT_A_LAW); + _EXPORT_INT(m, AFMT_IMA_ADPCM); + _EXPORT_INT(m, AFMT_U8); + _EXPORT_INT(m, AFMT_S16_LE); + _EXPORT_INT(m, AFMT_S16_BE); + _EXPORT_INT(m, AFMT_S8); + _EXPORT_INT(m, AFMT_U16_LE); + _EXPORT_INT(m, AFMT_U16_BE); + _EXPORT_INT(m, AFMT_MPEG); +#ifdef AFMT_AC3 + _EXPORT_INT(m, AFMT_AC3); +#endif +#ifdef AFMT_S16_NE + _EXPORT_INT(m, AFMT_S16_NE); +#endif + + /* Expose the sound mixer device numbers. */ + _EXPORT_INT(m, SOUND_MIXER_NRDEVICES); + _EXPORT_INT(m, SOUND_MIXER_VOLUME); + _EXPORT_INT(m, SOUND_MIXER_BASS); + _EXPORT_INT(m, SOUND_MIXER_TREBLE); + _EXPORT_INT(m, SOUND_MIXER_SYNTH); + _EXPORT_INT(m, SOUND_MIXER_PCM); + _EXPORT_INT(m, SOUND_MIXER_SPEAKER); + _EXPORT_INT(m, SOUND_MIXER_LINE); + _EXPORT_INT(m, SOUND_MIXER_MIC); + _EXPORT_INT(m, SOUND_MIXER_CD); + _EXPORT_INT(m, SOUND_MIXER_IMIX); + _EXPORT_INT(m, SOUND_MIXER_ALTPCM); + _EXPORT_INT(m, SOUND_MIXER_RECLEV); + _EXPORT_INT(m, SOUND_MIXER_IGAIN); + _EXPORT_INT(m, SOUND_MIXER_OGAIN); + _EXPORT_INT(m, SOUND_MIXER_LINE1); + _EXPORT_INT(m, SOUND_MIXER_LINE2); + _EXPORT_INT(m, SOUND_MIXER_LINE3); + _EXPORT_INT(m, SOUND_MIXER_DIGITAL1); + _EXPORT_INT(m, SOUND_MIXER_DIGITAL2); + _EXPORT_INT(m, SOUND_MIXER_DIGITAL3); + _EXPORT_INT(m, SOUND_MIXER_PHONEIN); + _EXPORT_INT(m, SOUND_MIXER_PHONEOUT); + _EXPORT_INT(m, SOUND_MIXER_VIDEO); + _EXPORT_INT(m, SOUND_MIXER_RADIO); + _EXPORT_INT(m, SOUND_MIXER_MONITOR); + + /* Expose all the ioctl numbers for masochists who like to do this + stuff directly. */ + _EXPORT_INT(m, SNDCTL_COPR_HALT); + _EXPORT_INT(m, SNDCTL_COPR_LOAD); + _EXPORT_INT(m, SNDCTL_COPR_RCODE); + _EXPORT_INT(m, SNDCTL_COPR_RCVMSG); + _EXPORT_INT(m, SNDCTL_COPR_RDATA); + _EXPORT_INT(m, SNDCTL_COPR_RESET); + _EXPORT_INT(m, SNDCTL_COPR_RUN); + _EXPORT_INT(m, SNDCTL_COPR_SENDMSG); + _EXPORT_INT(m, SNDCTL_COPR_WCODE); + _EXPORT_INT(m, SNDCTL_COPR_WDATA); +#ifdef SNDCTL_DSP_BIND_CHANNEL + _EXPORT_INT(m, SNDCTL_DSP_BIND_CHANNEL); +#endif + _EXPORT_INT(m, SNDCTL_DSP_CHANNELS); + _EXPORT_INT(m, SNDCTL_DSP_GETBLKSIZE); + _EXPORT_INT(m, SNDCTL_DSP_GETCAPS); +#ifdef SNDCTL_DSP_GETCHANNELMASK + _EXPORT_INT(m, SNDCTL_DSP_GETCHANNELMASK); +#endif + _EXPORT_INT(m, SNDCTL_DSP_GETFMTS); + _EXPORT_INT(m, SNDCTL_DSP_GETIPTR); + _EXPORT_INT(m, SNDCTL_DSP_GETISPACE); + _EXPORT_INT(m, SNDCTL_DSP_GETODELAY); + _EXPORT_INT(m, SNDCTL_DSP_GETOPTR); + _EXPORT_INT(m, SNDCTL_DSP_GETOSPACE); +#ifdef SNDCTL_DSP_GETSPDIF + _EXPORT_INT(m, SNDCTL_DSP_GETSPDIF); +#endif + _EXPORT_INT(m, SNDCTL_DSP_GETTRIGGER); + _EXPORT_INT(m, SNDCTL_DSP_MAPINBUF); + _EXPORT_INT(m, SNDCTL_DSP_MAPOUTBUF); + _EXPORT_INT(m, SNDCTL_DSP_NONBLOCK); + _EXPORT_INT(m, SNDCTL_DSP_POST); +#ifdef SNDCTL_DSP_PROFILE + _EXPORT_INT(m, SNDCTL_DSP_PROFILE); +#endif + _EXPORT_INT(m, SNDCTL_DSP_RESET); + _EXPORT_INT(m, SNDCTL_DSP_SAMPLESIZE); + _EXPORT_INT(m, SNDCTL_DSP_SETDUPLEX); + _EXPORT_INT(m, SNDCTL_DSP_SETFMT); + _EXPORT_INT(m, SNDCTL_DSP_SETFRAGMENT); +#ifdef SNDCTL_DSP_SETSPDIF + _EXPORT_INT(m, SNDCTL_DSP_SETSPDIF); +#endif + _EXPORT_INT(m, SNDCTL_DSP_SETSYNCRO); + _EXPORT_INT(m, SNDCTL_DSP_SETTRIGGER); + _EXPORT_INT(m, SNDCTL_DSP_SPEED); + _EXPORT_INT(m, SNDCTL_DSP_STEREO); + _EXPORT_INT(m, SNDCTL_DSP_SUBDIVIDE); + _EXPORT_INT(m, SNDCTL_DSP_SYNC); + _EXPORT_INT(m, SNDCTL_FM_4OP_ENABLE); + _EXPORT_INT(m, SNDCTL_FM_LOAD_INSTR); + _EXPORT_INT(m, SNDCTL_MIDI_INFO); + _EXPORT_INT(m, SNDCTL_MIDI_MPUCMD); + _EXPORT_INT(m, SNDCTL_MIDI_MPUMODE); + _EXPORT_INT(m, SNDCTL_MIDI_PRETIME); + _EXPORT_INT(m, SNDCTL_SEQ_CTRLRATE); + _EXPORT_INT(m, SNDCTL_SEQ_GETINCOUNT); + _EXPORT_INT(m, SNDCTL_SEQ_GETOUTCOUNT); +#ifdef SNDCTL_SEQ_GETTIME + _EXPORT_INT(m, SNDCTL_SEQ_GETTIME); +#endif + _EXPORT_INT(m, SNDCTL_SEQ_NRMIDIS); + _EXPORT_INT(m, SNDCTL_SEQ_NRSYNTHS); + _EXPORT_INT(m, SNDCTL_SEQ_OUTOFBAND); + _EXPORT_INT(m, SNDCTL_SEQ_PANIC); + _EXPORT_INT(m, SNDCTL_SEQ_PERCMODE); + _EXPORT_INT(m, SNDCTL_SEQ_RESET); + _EXPORT_INT(m, SNDCTL_SEQ_RESETSAMPLES); + _EXPORT_INT(m, SNDCTL_SEQ_SYNC); + _EXPORT_INT(m, SNDCTL_SEQ_TESTMIDI); + _EXPORT_INT(m, SNDCTL_SEQ_THRESHOLD); +#ifdef SNDCTL_SYNTH_CONTROL + _EXPORT_INT(m, SNDCTL_SYNTH_CONTROL); +#endif +#ifdef SNDCTL_SYNTH_ID + _EXPORT_INT(m, SNDCTL_SYNTH_ID); +#endif + _EXPORT_INT(m, SNDCTL_SYNTH_INFO); + _EXPORT_INT(m, SNDCTL_SYNTH_MEMAVL); +#ifdef SNDCTL_SYNTH_REMOVESAMPLE + _EXPORT_INT(m, SNDCTL_SYNTH_REMOVESAMPLE); +#endif + _EXPORT_INT(m, SNDCTL_TMR_CONTINUE); + _EXPORT_INT(m, SNDCTL_TMR_METRONOME); + _EXPORT_INT(m, SNDCTL_TMR_SELECT); + _EXPORT_INT(m, SNDCTL_TMR_SOURCE); + _EXPORT_INT(m, SNDCTL_TMR_START); + _EXPORT_INT(m, SNDCTL_TMR_STOP); + _EXPORT_INT(m, SNDCTL_TMR_TEMPO); + _EXPORT_INT(m, SNDCTL_TMR_TIMEBASE); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/parsermodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/parsermodule.c new file mode 100644 index 00000000..4edbeadd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/parsermodule.c @@ -0,0 +1,2957 @@ +/* parsermodule.c + * + * Copyright 1995-1996 by Fred L. Drake, Jr. and Virginia Polytechnic + * Institute and State University, Blacksburg, Virginia, USA. + * Portions copyright 1991-1995 by Stichting Mathematisch Centrum, + * Amsterdam, The Netherlands. Copying is permitted under the terms + * associated with the main Python distribution, with the additional + * restriction that this additional notice be included and maintained + * on all distributed copies. + * + * This module serves to replace the original parser module written + * by Guido. The functionality is not matched precisely, but the + * original may be implemented on top of this. This is desirable + * since the source of the text to be parsed is now divorced from + * this interface. + * + * Unlike the prior interface, the ability to give a parse tree + * produced by Python code as a tuple to the compiler is enabled by + * this module. See the documentation for more details. + * + * I've added some annotations that help with the lint code-checking + * program, but they're not complete by a long shot. The real errors + * that lint detects are gone, but there are still warnings with + * Py_[X]DECREF() and Py_[X]INCREF() macros. The lint annotations + * look like "NOTE(...)". + */ + +#include "Python.h" /* general Python API */ +#include "graminit.h" /* symbols defined in the grammar */ +#include "node.h" /* internal parser structure */ +#include "errcode.h" /* error codes for PyNode_*() */ +#include "token.h" /* token definitions */ + /* ISTERMINAL() / ISNONTERMINAL() */ +#include "compile.h" /* PyNode_Compile() */ + +#ifdef lint +#include +#else +#define NOTE(x) +#endif + +#ifdef macintosh +char *strdup(char *); +#endif + +/* String constants used to initialize module attributes. + * + */ +static char parser_copyright_string[] = +"Copyright 1995-1996 by Virginia Polytechnic Institute & State\n\ +University, Blacksburg, Virginia, USA, and Fred L. Drake, Jr., Reston,\n\ +Virginia, USA. Portions copyright 1991-1995 by Stichting Mathematisch\n\ +Centrum, Amsterdam, The Netherlands."; + + +PyDoc_STRVAR(parser_doc_string, +"This is an interface to Python's internal parser."); + +static char parser_version_string[] = "0.5"; + + +typedef PyObject* (*SeqMaker) (int length); +typedef int (*SeqInserter) (PyObject* sequence, + int index, + PyObject* element); + +/* The function below is copyrighted by Stichting Mathematisch Centrum. The + * original copyright statement is included below, and continues to apply + * in full to the function immediately following. All other material is + * original, copyrighted by Fred L. Drake, Jr. and Virginia Polytechnic + * Institute and State University. Changes were made to comply with the + * new naming conventions. Added arguments to provide support for creating + * lists as well as tuples, and optionally including the line numbers. + */ + + +static PyObject* +node2tuple(node *n, /* node to convert */ + SeqMaker mkseq, /* create sequence */ + SeqInserter addelem, /* func. to add elem. in seq. */ + int lineno) /* include line numbers? */ +{ + if (n == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + if (ISNONTERMINAL(TYPE(n))) { + int i; + PyObject *v; + PyObject *w; + + v = mkseq(1 + NCH(n) + (TYPE(n) == encoding_decl)); + if (v == NULL) + return (v); + w = PyInt_FromLong(TYPE(n)); + if (w == NULL) { + Py_DECREF(v); + return ((PyObject*) NULL); + } + (void) addelem(v, 0, w); + for (i = 0; i < NCH(n); i++) { + w = node2tuple(CHILD(n, i), mkseq, addelem, lineno); + if (w == NULL) { + Py_DECREF(v); + return ((PyObject*) NULL); + } + (void) addelem(v, i+1, w); + } + + if (TYPE(n) == encoding_decl) + (void) addelem(v, i+1, PyString_FromString(STR(n))); + return (v); + } + else if (ISTERMINAL(TYPE(n))) { + PyObject *result = mkseq(2 + lineno); + if (result != NULL) { + (void) addelem(result, 0, PyInt_FromLong(TYPE(n))); + (void) addelem(result, 1, PyString_FromString(STR(n))); + if (lineno == 1) + (void) addelem(result, 2, PyInt_FromLong(n->n_lineno)); + } + return (result); + } + else { + PyErr_SetString(PyExc_SystemError, + "unrecognized parse tree node type"); + return ((PyObject*) NULL); + } +} +/* + * End of material copyrighted by Stichting Mathematisch Centrum. + */ + + + +/* There are two types of intermediate objects we're interested in: + * 'eval' and 'exec' types. These constants can be used in the st_type + * field of the object type to identify which any given object represents. + * These should probably go in an external header to allow other extensions + * to use them, but then, we really should be using C++ too. ;-) + */ + +#define PyST_EXPR 1 +#define PyST_SUITE 2 + + +/* These are the internal objects and definitions required to implement the + * ST type. Most of the internal names are more reminiscent of the 'old' + * naming style, but the code uses the new naming convention. + */ + +static PyObject* +parser_error = 0; + + +typedef struct { + PyObject_HEAD /* standard object header */ + node* st_node; /* the node* returned by the parser */ + int st_type; /* EXPR or SUITE ? */ +} PyST_Object; + + +static void parser_free(PyST_Object *st); +static int parser_compare(PyST_Object *left, PyST_Object *right); +static PyObject *parser_getattr(PyObject *self, char *name); + + +static +PyTypeObject PyST_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "parser.st", /* tp_name */ + (int) sizeof(PyST_Object), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)parser_free, /* tp_dealloc */ + 0, /* tp_print */ + parser_getattr, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)parser_compare, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + + /* Functions to access object as input/output buffer */ + 0, /* tp_as_buffer */ + + Py_TPFLAGS_DEFAULT, /* tp_flags */ + + /* __doc__ */ + "Intermediate representation of a Python parse tree." +}; /* PyST_Type */ + + +static int +parser_compare_nodes(node *left, node *right) +{ + int j; + + if (TYPE(left) < TYPE(right)) + return (-1); + + if (TYPE(right) < TYPE(left)) + return (1); + + if (ISTERMINAL(TYPE(left))) + return (strcmp(STR(left), STR(right))); + + if (NCH(left) < NCH(right)) + return (-1); + + if (NCH(right) < NCH(left)) + return (1); + + for (j = 0; j < NCH(left); ++j) { + int v = parser_compare_nodes(CHILD(left, j), CHILD(right, j)); + + if (v != 0) + return (v); + } + return (0); +} + + +/* int parser_compare(PyST_Object* left, PyST_Object* right) + * + * Comparison function used by the Python operators ==, !=, <, >, <=, >= + * This really just wraps a call to parser_compare_nodes() with some easy + * checks and protection code. + * + */ +static int +parser_compare(PyST_Object *left, PyST_Object *right) +{ + if (left == right) + return (0); + + if ((left == 0) || (right == 0)) + return (-1); + + return (parser_compare_nodes(left->st_node, right->st_node)); +} + + +/* parser_newstobject(node* st) + * + * Allocates a new Python object representing an ST. This is simply the + * 'wrapper' object that holds a node* and allows it to be passed around in + * Python code. + * + */ +static PyObject* +parser_newstobject(node *st, int type) +{ + PyST_Object* o = PyObject_New(PyST_Object, &PyST_Type); + + if (o != 0) { + o->st_node = st; + o->st_type = type; + } + else { + PyNode_Free(st); + } + return ((PyObject*)o); +} + + +/* void parser_free(PyST_Object* st) + * + * This is called by a del statement that reduces the reference count to 0. + * + */ +static void +parser_free(PyST_Object *st) +{ + PyNode_Free(st->st_node); + PyObject_Del(st); +} + + +/* parser_st2tuple(PyObject* self, PyObject* args, PyObject* kw) + * + * This provides conversion from a node* to a tuple object that can be + * returned to the Python-level caller. The ST object is not modified. + * + */ +static PyObject* +parser_st2tuple(PyST_Object *self, PyObject *args, PyObject *kw) +{ + PyObject *line_option = 0; + PyObject *res = 0; + int ok; + + static char *keywords[] = {"ast", "line_info", NULL}; + + if (self == NULL) { + ok = PyArg_ParseTupleAndKeywords(args, kw, "O!|O:st2tuple", keywords, + &PyST_Type, &self, &line_option); + } + else + ok = PyArg_ParseTupleAndKeywords(args, kw, "|O:totuple", &keywords[1], + &line_option); + if (ok != 0) { + int lineno = 0; + if (line_option != NULL) { + lineno = (PyObject_IsTrue(line_option) != 0) ? 1 : 0; + } + /* + * Convert ST into a tuple representation. Use Guido's function, + * since it's known to work already. + */ + res = node2tuple(((PyST_Object*)self)->st_node, + PyTuple_New, PyTuple_SetItem, lineno); + } + return (res); +} + + +/* parser_st2list(PyObject* self, PyObject* args, PyObject* kw) + * + * This provides conversion from a node* to a list object that can be + * returned to the Python-level caller. The ST object is not modified. + * + */ +static PyObject* +parser_st2list(PyST_Object *self, PyObject *args, PyObject *kw) +{ + PyObject *line_option = 0; + PyObject *res = 0; + int ok; + + static char *keywords[] = {"ast", "line_info", NULL}; + + if (self == NULL) + ok = PyArg_ParseTupleAndKeywords(args, kw, "O!|O:st2list", keywords, + &PyST_Type, &self, &line_option); + else + ok = PyArg_ParseTupleAndKeywords(args, kw, "|O:tolist", &keywords[1], + &line_option); + if (ok) { + int lineno = 0; + if (line_option != 0) { + lineno = PyObject_IsTrue(line_option) ? 1 : 0; + } + /* + * Convert ST into a tuple representation. Use Guido's function, + * since it's known to work already. + */ + res = node2tuple(self->st_node, + PyList_New, PyList_SetItem, lineno); + } + return (res); +} + + +/* parser_compilest(PyObject* self, PyObject* args) + * + * This function creates code objects from the parse tree represented by + * the passed-in data object. An optional file name is passed in as well. + * + */ +static PyObject* +parser_compilest(PyST_Object *self, PyObject *args, PyObject *kw) +{ + PyObject* res = 0; + char* str = ""; + int ok; + + static char *keywords[] = {"ast", "filename", NULL}; + + if (self == NULL) + ok = PyArg_ParseTupleAndKeywords(args, kw, "O!|s:compilest", keywords, + &PyST_Type, &self, &str); + else + ok = PyArg_ParseTupleAndKeywords(args, kw, "|s:compile", &keywords[1], + &str); + + if (ok) + res = (PyObject *)PyNode_Compile(self->st_node, str); + + return (res); +} + + +/* PyObject* parser_isexpr(PyObject* self, PyObject* args) + * PyObject* parser_issuite(PyObject* self, PyObject* args) + * + * Checks the passed-in ST object to determine if it is an expression or + * a statement suite, respectively. The return is a Python truth value. + * + */ +static PyObject* +parser_isexpr(PyST_Object *self, PyObject *args, PyObject *kw) +{ + PyObject* res = 0; + int ok; + + static char *keywords[] = {"ast", NULL}; + + if (self == NULL) + ok = PyArg_ParseTupleAndKeywords(args, kw, "O!:isexpr", keywords, + &PyST_Type, &self); + else + ok = PyArg_ParseTupleAndKeywords(args, kw, ":isexpr", &keywords[1]); + + if (ok) { + /* Check to see if the ST represents an expression or not. */ + res = (self->st_type == PyST_EXPR) ? Py_True : Py_False; + Py_INCREF(res); + } + return (res); +} + + +static PyObject* +parser_issuite(PyST_Object *self, PyObject *args, PyObject *kw) +{ + PyObject* res = 0; + int ok; + + static char *keywords[] = {"ast", NULL}; + + if (self == NULL) + ok = PyArg_ParseTupleAndKeywords(args, kw, "O!:issuite", keywords, + &PyST_Type, &self); + else + ok = PyArg_ParseTupleAndKeywords(args, kw, ":issuite", &keywords[1]); + + if (ok) { + /* Check to see if the ST represents an expression or not. */ + res = (self->st_type == PyST_EXPR) ? Py_False : Py_True; + Py_INCREF(res); + } + return (res); +} + + +#define PUBLIC_METHOD_TYPE (METH_VARARGS|METH_KEYWORDS) + +static PyMethodDef +parser_methods[] = { + {"compile", (PyCFunction)parser_compilest, PUBLIC_METHOD_TYPE, + PyDoc_STR("Compile this ST object into a code object.")}, + {"isexpr", (PyCFunction)parser_isexpr, PUBLIC_METHOD_TYPE, + PyDoc_STR("Determines if this ST object was created from an expression.")}, + {"issuite", (PyCFunction)parser_issuite, PUBLIC_METHOD_TYPE, + PyDoc_STR("Determines if this ST object was created from a suite.")}, + {"tolist", (PyCFunction)parser_st2list, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates a list-tree representation of this ST.")}, + {"totuple", (PyCFunction)parser_st2tuple, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates a tuple-tree representation of this ST.")}, + + {NULL, NULL, 0, NULL} +}; + + +static PyObject* +parser_getattr(PyObject *self, char *name) +{ + return (Py_FindMethod(parser_methods, self, name)); +} + + +/* err_string(char* message) + * + * Sets the error string for an exception of type ParserError. + * + */ +static void +err_string(char *message) +{ + PyErr_SetString(parser_error, message); +} + + +/* PyObject* parser_do_parse(PyObject* args, int type) + * + * Internal function to actually execute the parse and return the result if + * successful or set an exception if not. + * + */ +static PyObject* +parser_do_parse(PyObject *args, PyObject *kw, char *argspec, int type) +{ + char* string = 0; + PyObject* res = 0; + + static char *keywords[] = {"source", NULL}; + + if (PyArg_ParseTupleAndKeywords(args, kw, argspec, keywords, &string)) { + node* n = PyParser_SimpleParseString(string, + (type == PyST_EXPR) + ? eval_input : file_input); + + if (n) + res = parser_newstobject(n, type); + } + return (res); +} + + +/* PyObject* parser_expr(PyObject* self, PyObject* args) + * PyObject* parser_suite(PyObject* self, PyObject* args) + * + * External interfaces to the parser itself. Which is called determines if + * the parser attempts to recognize an expression ('eval' form) or statement + * suite ('exec' form). The real work is done by parser_do_parse() above. + * + */ +static PyObject* +parser_expr(PyST_Object *self, PyObject *args, PyObject *kw) +{ + NOTE(ARGUNUSED(self)) + return (parser_do_parse(args, kw, "s:expr", PyST_EXPR)); +} + + +static PyObject* +parser_suite(PyST_Object *self, PyObject *args, PyObject *kw) +{ + NOTE(ARGUNUSED(self)) + return (parser_do_parse(args, kw, "s:suite", PyST_SUITE)); +} + + + +/* This is the messy part of the code. Conversion from a tuple to an ST + * object requires that the input tuple be valid without having to rely on + * catching an exception from the compiler. This is done to allow the + * compiler itself to remain fast, since most of its input will come from + * the parser directly, and therefore be known to be syntactically correct. + * This validation is done to ensure that we don't core dump the compile + * phase, returning an exception instead. + * + * Two aspects can be broken out in this code: creating a node tree from + * the tuple passed in, and verifying that it is indeed valid. It may be + * advantageous to expand the number of ST types to include funcdefs and + * lambdadefs to take advantage of the optimizer, recognizing those STs + * here. They are not necessary, and not quite as useful in a raw form. + * For now, let's get expressions and suites working reliably. + */ + + +static node* build_node_tree(PyObject *tuple); +static int validate_expr_tree(node *tree); +static int validate_file_input(node *tree); +static int validate_encoding_decl(node *tree); + +/* PyObject* parser_tuple2st(PyObject* self, PyObject* args) + * + * This is the public function, called from the Python code. It receives a + * single tuple object from the caller, and creates an ST object if the + * tuple can be validated. It does this by checking the first code of the + * tuple, and, if acceptable, builds the internal representation. If this + * step succeeds, the internal representation is validated as fully as + * possible with the various validate_*() routines defined below. + * + * This function must be changed if support is to be added for PyST_FRAGMENT + * ST objects. + * + */ +static PyObject* +parser_tuple2st(PyST_Object *self, PyObject *args, PyObject *kw) +{ + NOTE(ARGUNUSED(self)) + PyObject *st = 0; + PyObject *tuple; + node *tree; + + static char *keywords[] = {"sequence", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "O:sequence2st", keywords, + &tuple)) + return (0); + if (!PySequence_Check(tuple)) { + PyErr_SetString(PyExc_ValueError, + "sequence2st() requires a single sequence argument"); + return (0); + } + /* + * Convert the tree to the internal form before checking it. + */ + tree = build_node_tree(tuple); + if (tree != 0) { + int start_sym = TYPE(tree); + if (start_sym == eval_input) { + /* Might be an eval form. */ + if (validate_expr_tree(tree)) + st = parser_newstobject(tree, PyST_EXPR); + else + PyNode_Free(tree); + } + else if (start_sym == file_input) { + /* This looks like an exec form so far. */ + if (validate_file_input(tree)) + st = parser_newstobject(tree, PyST_SUITE); + else + PyNode_Free(tree); + } + else if (start_sym == encoding_decl) { + /* This looks like an encoding_decl so far. */ + if (validate_encoding_decl(tree)) + st = parser_newstobject(tree, PyST_SUITE); + else + PyNode_Free(tree); + } + else { + /* This is a fragment, at best. */ + PyNode_Free(tree); + err_string("parse tree does not use a valid start symbol"); + } + } + /* Make sure we throw an exception on all errors. We should never + * get this, but we'd do well to be sure something is done. + */ + if (st == NULL && !PyErr_Occurred()) + err_string("unspecified ST error occurred"); + + return st; +} + + +/* node* build_node_children() + * + * Iterate across the children of the current non-terminal node and build + * their structures. If successful, return the root of this portion of + * the tree, otherwise, 0. Any required exception will be specified already, + * and no memory will have been deallocated. + * + */ +static node* +build_node_children(PyObject *tuple, node *root, int *line_num) +{ + int len = PyObject_Size(tuple); + int i, err; + + for (i = 1; i < len; ++i) { + /* elem must always be a sequence, however simple */ + PyObject* elem = PySequence_GetItem(tuple, i); + int ok = elem != NULL; + long type = 0; + char *strn = 0; + + if (ok) + ok = PySequence_Check(elem); + if (ok) { + PyObject *temp = PySequence_GetItem(elem, 0); + if (temp == NULL) + ok = 0; + else { + ok = PyInt_Check(temp); + if (ok) + type = PyInt_AS_LONG(temp); + Py_DECREF(temp); + } + } + if (!ok) { + PyErr_SetObject(parser_error, + Py_BuildValue("os", elem, + "Illegal node construct.")); + Py_XDECREF(elem); + return (0); + } + if (ISTERMINAL(type)) { + int len = PyObject_Size(elem); + PyObject *temp; + + if ((len != 2) && (len != 3)) { + err_string("terminal nodes must have 2 or 3 entries"); + return 0; + } + temp = PySequence_GetItem(elem, 1); + if (temp == NULL) + return 0; + if (!PyString_Check(temp)) { + PyErr_Format(parser_error, + "second item in terminal node must be a string," + " found %s", + temp->ob_type->tp_name); + Py_DECREF(temp); + return 0; + } + if (len == 3) { + PyObject *o = PySequence_GetItem(elem, 2); + if (o != NULL) { + if (PyInt_Check(o)) + *line_num = PyInt_AS_LONG(o); + else { + PyErr_Format(parser_error, + "third item in terminal node must be an" + " integer, found %s", + temp->ob_type->tp_name); + Py_DECREF(o); + Py_DECREF(temp); + return 0; + } + Py_DECREF(o); + } + } + len = PyString_GET_SIZE(temp) + 1; + strn = (char *)PyMem_MALLOC(len); + if (strn != NULL) + (void) memcpy(strn, PyString_AS_STRING(temp), len); + Py_DECREF(temp); + } + else if (!ISNONTERMINAL(type)) { + /* + * It has to be one or the other; this is an error. + * Throw an exception. + */ + PyErr_SetObject(parser_error, + Py_BuildValue("os", elem, "unknown node type.")); + Py_XDECREF(elem); + return (0); + } + err = PyNode_AddChild(root, type, strn, *line_num); + if (err == E_NOMEM) { + PyMem_DEL(strn); + return (node *) PyErr_NoMemory(); + } + if (err == E_OVERFLOW) { + PyMem_DEL(strn); + PyErr_SetString(PyExc_ValueError, + "unsupported number of child nodes"); + return NULL; + } + + if (ISNONTERMINAL(type)) { + node* new_child = CHILD(root, i - 1); + + if (new_child != build_node_children(elem, new_child, line_num)) { + Py_XDECREF(elem); + return (0); + } + } + else if (type == NEWLINE) { /* It's true: we increment the */ + ++(*line_num); /* line number *after* the newline! */ + } + Py_XDECREF(elem); + } + return (root); +} + + +static node* +build_node_tree(PyObject *tuple) +{ + node* res = 0; + PyObject *temp = PySequence_GetItem(tuple, 0); + long num = -1; + + if (temp != NULL) + num = PyInt_AsLong(temp); + Py_XDECREF(temp); + if (ISTERMINAL(num)) { + /* + * The tuple is simple, but it doesn't start with a start symbol. + * Throw an exception now and be done with it. + */ + tuple = Py_BuildValue("os", tuple, + "Illegal syntax-tree; cannot start with terminal symbol."); + PyErr_SetObject(parser_error, tuple); + } + else if (ISNONTERMINAL(num)) { + /* + * Not efficient, but that can be handled later. + */ + int line_num = 0; + PyObject *encoding = NULL; + + if (num == encoding_decl) { + encoding = PySequence_GetItem(tuple, 2); + /* tuple isn't borrowed anymore here, need to DECREF */ + tuple = PySequence_GetSlice(tuple, 0, 2); + } + res = PyNode_New(num); + if (res != NULL) { + if (res != build_node_children(tuple, res, &line_num)) { + PyNode_Free(res); + res = NULL; + } + if (res && encoding) { + int len; + len = PyString_GET_SIZE(encoding) + 1; + res->n_str = (char *)PyMem_MALLOC(len); + if (res->n_str != NULL) + (void) memcpy(res->n_str, PyString_AS_STRING(encoding), len); + Py_DECREF(encoding); + Py_DECREF(tuple); + } + } + } + else + /* The tuple is illegal -- if the number is neither TERMINAL nor + * NONTERMINAL, we can't use it. Not sure the implementation + * allows this condition, but the API doesn't preclude it. + */ + PyErr_SetObject(parser_error, + Py_BuildValue("os", tuple, + "Illegal component tuple.")); + + return (res); +} + + +/* + * Validation routines used within the validation section: + */ +static int validate_terminal(node *terminal, int type, char *string); + +#define validate_ampersand(ch) validate_terminal(ch, AMPER, "&") +#define validate_circumflex(ch) validate_terminal(ch, CIRCUMFLEX, "^") +#define validate_colon(ch) validate_terminal(ch, COLON, ":") +#define validate_comma(ch) validate_terminal(ch, COMMA, ",") +#define validate_dedent(ch) validate_terminal(ch, DEDENT, "") +#define validate_equal(ch) validate_terminal(ch, EQUAL, "=") +#define validate_indent(ch) validate_terminal(ch, INDENT, (char*)NULL) +#define validate_lparen(ch) validate_terminal(ch, LPAR, "(") +#define validate_newline(ch) validate_terminal(ch, NEWLINE, (char*)NULL) +#define validate_rparen(ch) validate_terminal(ch, RPAR, ")") +#define validate_semi(ch) validate_terminal(ch, SEMI, ";") +#define validate_star(ch) validate_terminal(ch, STAR, "*") +#define validate_vbar(ch) validate_terminal(ch, VBAR, "|") +#define validate_doublestar(ch) validate_terminal(ch, DOUBLESTAR, "**") +#define validate_dot(ch) validate_terminal(ch, DOT, ".") +#define validate_name(ch, str) validate_terminal(ch, NAME, str) + +#define VALIDATER(n) static int validate_##n(node *tree) + +VALIDATER(node); VALIDATER(small_stmt); +VALIDATER(class); VALIDATER(node); +VALIDATER(parameters); VALIDATER(suite); +VALIDATER(testlist); VALIDATER(varargslist); +VALIDATER(fpdef); VALIDATER(fplist); +VALIDATER(stmt); VALIDATER(simple_stmt); +VALIDATER(expr_stmt); VALIDATER(power); +VALIDATER(print_stmt); VALIDATER(del_stmt); +VALIDATER(return_stmt); VALIDATER(list_iter); +VALIDATER(raise_stmt); VALIDATER(import_stmt); +VALIDATER(global_stmt); VALIDATER(list_if); +VALIDATER(assert_stmt); VALIDATER(list_for); +VALIDATER(exec_stmt); VALIDATER(compound_stmt); +VALIDATER(while); VALIDATER(for); +VALIDATER(try); VALIDATER(except_clause); +VALIDATER(test); VALIDATER(and_test); +VALIDATER(not_test); VALIDATER(comparison); +VALIDATER(comp_op); VALIDATER(expr); +VALIDATER(xor_expr); VALIDATER(and_expr); +VALIDATER(shift_expr); VALIDATER(arith_expr); +VALIDATER(term); VALIDATER(factor); +VALIDATER(atom); VALIDATER(lambdef); +VALIDATER(trailer); VALIDATER(subscript); +VALIDATER(subscriptlist); VALIDATER(sliceop); +VALIDATER(exprlist); VALIDATER(dictmaker); +VALIDATER(arglist); VALIDATER(argument); +VALIDATER(listmaker); VALIDATER(yield_stmt); +VALIDATER(testlist1); + +#undef VALIDATER + +#define is_even(n) (((n) & 1) == 0) +#define is_odd(n) (((n) & 1) == 1) + + +static int +validate_ntype(node *n, int t) +{ + if (TYPE(n) != t) { + PyErr_Format(parser_error, "Expected node type %d, got %d.", + t, TYPE(n)); + return 0; + } + return 1; +} + + +/* Verifies that the number of child nodes is exactly 'num', raising + * an exception if it isn't. The exception message does not indicate + * the exact number of nodes, allowing this to be used to raise the + * "right" exception when the wrong number of nodes is present in a + * specific variant of a statement's syntax. This is commonly used + * in that fashion. + */ +static int +validate_numnodes(node *n, int num, const char *const name) +{ + if (NCH(n) != num) { + PyErr_Format(parser_error, + "Illegal number of children for %s node.", name); + return 0; + } + return 1; +} + + +static int +validate_terminal(node *terminal, int type, char *string) +{ + int res = (validate_ntype(terminal, type) + && ((string == 0) || (strcmp(string, STR(terminal)) == 0))); + + if (!res && !PyErr_Occurred()) { + PyErr_Format(parser_error, + "Illegal terminal: expected \"%s\"", string); + } + return (res); +} + + +/* X (',' X) [','] + */ +static int +validate_repeating_list(node *tree, int ntype, int (*vfunc)(node *), + const char *const name) +{ + int nch = NCH(tree); + int res = (nch && validate_ntype(tree, ntype) + && vfunc(CHILD(tree, 0))); + + if (!res && !PyErr_Occurred()) + (void) validate_numnodes(tree, 1, name); + else { + if (is_even(nch)) + res = validate_comma(CHILD(tree, --nch)); + if (res && nch > 1) { + int pos = 1; + for ( ; res && pos < nch; pos += 2) + res = (validate_comma(CHILD(tree, pos)) + && vfunc(CHILD(tree, pos + 1))); + } + } + return (res); +} + + +/* validate_class() + * + * classdef: + * 'class' NAME ['(' testlist ')'] ':' suite + */ +static int +validate_class(node *tree) +{ + int nch = NCH(tree); + int res = validate_ntype(tree, classdef) && ((nch == 4) || (nch == 7)); + + if (res) { + res = (validate_name(CHILD(tree, 0), "class") + && validate_ntype(CHILD(tree, 1), NAME) + && validate_colon(CHILD(tree, nch - 2)) + && validate_suite(CHILD(tree, nch - 1))); + } + else + (void) validate_numnodes(tree, 4, "class"); + if (res && (nch == 7)) { + res = (validate_lparen(CHILD(tree, 2)) + && validate_testlist(CHILD(tree, 3)) + && validate_rparen(CHILD(tree, 4))); + } + return (res); +} + + +/* if_stmt: + * 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] + */ +static int +validate_if(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, if_stmt) + && (nch >= 4) + && validate_name(CHILD(tree, 0), "if") + && validate_test(CHILD(tree, 1)) + && validate_colon(CHILD(tree, 2)) + && validate_suite(CHILD(tree, 3))); + + if (res && ((nch % 4) == 3)) { + /* ... 'else' ':' suite */ + res = (validate_name(CHILD(tree, nch - 3), "else") + && validate_colon(CHILD(tree, nch - 2)) + && validate_suite(CHILD(tree, nch - 1))); + nch -= 3; + } + else if (!res && !PyErr_Occurred()) + (void) validate_numnodes(tree, 4, "if"); + if ((nch % 4) != 0) + /* Will catch the case for nch < 4 */ + res = validate_numnodes(tree, 0, "if"); + else if (res && (nch > 4)) { + /* ... ('elif' test ':' suite)+ ... */ + int j = 4; + while ((j < nch) && res) { + res = (validate_name(CHILD(tree, j), "elif") + && validate_colon(CHILD(tree, j + 2)) + && validate_test(CHILD(tree, j + 1)) + && validate_suite(CHILD(tree, j + 3))); + j += 4; + } + } + return (res); +} + + +/* parameters: + * '(' [varargslist] ')' + * + */ +static int +validate_parameters(node *tree) +{ + int nch = NCH(tree); + int res = validate_ntype(tree, parameters) && ((nch == 2) || (nch == 3)); + + if (res) { + res = (validate_lparen(CHILD(tree, 0)) + && validate_rparen(CHILD(tree, nch - 1))); + if (res && (nch == 3)) + res = validate_varargslist(CHILD(tree, 1)); + } + else { + (void) validate_numnodes(tree, 2, "parameters"); + } + return (res); +} + + +/* validate_suite() + * + * suite: + * simple_stmt + * | NEWLINE INDENT stmt+ DEDENT + */ +static int +validate_suite(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, suite) && ((nch == 1) || (nch >= 4))); + + if (res && (nch == 1)) + res = validate_simple_stmt(CHILD(tree, 0)); + else if (res) { + /* NEWLINE INDENT stmt+ DEDENT */ + res = (validate_newline(CHILD(tree, 0)) + && validate_indent(CHILD(tree, 1)) + && validate_stmt(CHILD(tree, 2)) + && validate_dedent(CHILD(tree, nch - 1))); + + if (res && (nch > 4)) { + int i = 3; + --nch; /* forget the DEDENT */ + for ( ; res && (i < nch); ++i) + res = validate_stmt(CHILD(tree, i)); + } + else if (nch < 4) + res = validate_numnodes(tree, 4, "suite"); + } + return (res); +} + + +static int +validate_testlist(node *tree) +{ + return (validate_repeating_list(tree, testlist, + validate_test, "testlist")); +} + + +static int +validate_testlist1(node *tree) +{ + return (validate_repeating_list(tree, testlist1, + validate_test, "testlist1")); +} + + +static int +validate_testlist_safe(node *tree) +{ + return (validate_repeating_list(tree, testlist_safe, + validate_test, "testlist_safe")); +} + + +/* '*' NAME [',' '**' NAME] | '**' NAME + */ +static int +validate_varargslist_trailer(node *tree, int start) +{ + int nch = NCH(tree); + int res = 0; + int sym; + + if (nch <= start) { + err_string("expected variable argument trailer for varargslist"); + return 0; + } + sym = TYPE(CHILD(tree, start)); + if (sym == STAR) { + /* + * ('*' NAME [',' '**' NAME] + */ + if (nch-start == 2) + res = validate_name(CHILD(tree, start+1), NULL); + else if (nch-start == 5) + res = (validate_name(CHILD(tree, start+1), NULL) + && validate_comma(CHILD(tree, start+2)) + && validate_doublestar(CHILD(tree, start+3)) + && validate_name(CHILD(tree, start+4), NULL)); + } + else if (sym == DOUBLESTAR) { + /* + * '**' NAME + */ + if (nch-start == 2) + res = validate_name(CHILD(tree, start+1), NULL); + } + if (!res) + err_string("illegal variable argument trailer for varargslist"); + return res; +} + + +/* validate_varargslist() + * + * varargslist: + * (fpdef ['=' test] ',')* + * ('*' NAME [',' '**' NAME] + * | '**' NAME) + * | fpdef ['=' test] (',' fpdef ['=' test])* [','] + * + */ +static int +validate_varargslist(node *tree) +{ + int nch = NCH(tree); + int res = validate_ntype(tree, varargslist) && (nch != 0); + int sym; + + if (!res) + return 0; + if (nch < 1) { + err_string("varargslist missing child nodes"); + return 0; + } + sym = TYPE(CHILD(tree, 0)); + if (sym == STAR || sym == DOUBLESTAR) + /* whole thing matches: + * '*' NAME [',' '**' NAME] | '**' NAME + */ + res = validate_varargslist_trailer(tree, 0); + else if (sym == fpdef) { + int i = 0; + + sym = TYPE(CHILD(tree, nch-1)); + if (sym == NAME) { + /* + * (fpdef ['=' test] ',')+ + * ('*' NAME [',' '**' NAME] + * | '**' NAME) + */ + /* skip over (fpdef ['=' test] ',')+ */ + while (res && (i+2 <= nch)) { + res = validate_fpdef(CHILD(tree, i)); + ++i; + if (res && TYPE(CHILD(tree, i)) == EQUAL && (i+2 <= nch)) { + res = (validate_equal(CHILD(tree, i)) + && validate_test(CHILD(tree, i+1))); + if (res) + i += 2; + } + if (res && i < nch) { + res = validate_comma(CHILD(tree, i)); + ++i; + if (res && i < nch + && (TYPE(CHILD(tree, i)) == DOUBLESTAR + || TYPE(CHILD(tree, i)) == STAR)) + break; + } + } + /* ... '*' NAME [',' '**' NAME] | '**' NAME + * i --^^^ + */ + if (res) + res = validate_varargslist_trailer(tree, i); + } + else { + /* + * fpdef ['=' test] (',' fpdef ['=' test])* [','] + */ + /* strip trailing comma node */ + if (sym == COMMA) { + res = validate_comma(CHILD(tree, nch-1)); + if (!res) + return 0; + --nch; + } + /* + * fpdef ['=' test] (',' fpdef ['=' test])* + */ + res = validate_fpdef(CHILD(tree, 0)); + ++i; + if (res && (i+2 <= nch) && TYPE(CHILD(tree, i)) == EQUAL) { + res = (validate_equal(CHILD(tree, i)) + && validate_test(CHILD(tree, i+1))); + i += 2; + } + /* + * ... (',' fpdef ['=' test])* + * i ---^^^ + */ + while (res && (nch - i) >= 2) { + res = (validate_comma(CHILD(tree, i)) + && validate_fpdef(CHILD(tree, i+1))); + i += 2; + if (res && (nch - i) >= 2 && TYPE(CHILD(tree, i)) == EQUAL) { + res = (validate_equal(CHILD(tree, i)) + && validate_test(CHILD(tree, i+1))); + i += 2; + } + } + if (res && nch - i != 0) { + res = 0; + err_string("illegal formation for varargslist"); + } + } + } + return res; +} + + +/* list_iter: list_for | list_if + */ +static int +validate_list_iter(node *tree) +{ + int res = (validate_ntype(tree, list_iter) + && validate_numnodes(tree, 1, "list_iter")); + if (res && TYPE(CHILD(tree, 0)) == list_for) + res = validate_list_for(CHILD(tree, 0)); + else + res = validate_list_if(CHILD(tree, 0)); + + return res; +} + +/* list_for: 'for' exprlist 'in' testlist [list_iter] + */ +static int +validate_list_for(node *tree) +{ + int nch = NCH(tree); + int res; + + if (nch == 5) + res = validate_list_iter(CHILD(tree, 4)); + else + res = validate_numnodes(tree, 4, "list_for"); + + if (res) + res = (validate_name(CHILD(tree, 0), "for") + && validate_exprlist(CHILD(tree, 1)) + && validate_name(CHILD(tree, 2), "in") + && validate_testlist_safe(CHILD(tree, 3))); + + return res; +} + +/* list_if: 'if' test [list_iter] + */ +static int +validate_list_if(node *tree) +{ + int nch = NCH(tree); + int res; + + if (nch == 3) + res = validate_list_iter(CHILD(tree, 2)); + else + res = validate_numnodes(tree, 2, "list_if"); + + if (res) + res = (validate_name(CHILD(tree, 0), "if") + && validate_test(CHILD(tree, 1))); + + return res; +} + + +/* validate_fpdef() + * + * fpdef: + * NAME + * | '(' fplist ')' + */ +static int +validate_fpdef(node *tree) +{ + int nch = NCH(tree); + int res = validate_ntype(tree, fpdef); + + if (res) { + if (nch == 1) + res = validate_ntype(CHILD(tree, 0), NAME); + else if (nch == 3) + res = (validate_lparen(CHILD(tree, 0)) + && validate_fplist(CHILD(tree, 1)) + && validate_rparen(CHILD(tree, 2))); + else + res = validate_numnodes(tree, 1, "fpdef"); + } + return (res); +} + + +static int +validate_fplist(node *tree) +{ + return (validate_repeating_list(tree, fplist, + validate_fpdef, "fplist")); +} + + +/* simple_stmt | compound_stmt + * + */ +static int +validate_stmt(node *tree) +{ + int res = (validate_ntype(tree, stmt) + && validate_numnodes(tree, 1, "stmt")); + + if (res) { + tree = CHILD(tree, 0); + + if (TYPE(tree) == simple_stmt) + res = validate_simple_stmt(tree); + else + res = validate_compound_stmt(tree); + } + return (res); +} + + +/* small_stmt (';' small_stmt)* [';'] NEWLINE + * + */ +static int +validate_simple_stmt(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, simple_stmt) + && (nch >= 2) + && validate_small_stmt(CHILD(tree, 0)) + && validate_newline(CHILD(tree, nch - 1))); + + if (nch < 2) + res = validate_numnodes(tree, 2, "simple_stmt"); + --nch; /* forget the NEWLINE */ + if (res && is_even(nch)) + res = validate_semi(CHILD(tree, --nch)); + if (res && (nch > 2)) { + int i; + + for (i = 1; res && (i < nch); i += 2) + res = (validate_semi(CHILD(tree, i)) + && validate_small_stmt(CHILD(tree, i + 1))); + } + return (res); +} + + +static int +validate_small_stmt(node *tree) +{ + int nch = NCH(tree); + int res = validate_numnodes(tree, 1, "small_stmt"); + + if (res) { + int ntype = TYPE(CHILD(tree, 0)); + + if ( (ntype == expr_stmt) + || (ntype == print_stmt) + || (ntype == del_stmt) + || (ntype == pass_stmt) + || (ntype == flow_stmt) + || (ntype == import_stmt) + || (ntype == global_stmt) + || (ntype == assert_stmt) + || (ntype == exec_stmt)) + res = validate_node(CHILD(tree, 0)); + else { + res = 0; + err_string("illegal small_stmt child type"); + } + } + else if (nch == 1) { + res = 0; + PyErr_Format(parser_error, + "Unrecognized child node of small_stmt: %d.", + TYPE(CHILD(tree, 0))); + } + return (res); +} + + +/* compound_stmt: + * if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef + */ +static int +validate_compound_stmt(node *tree) +{ + int res = (validate_ntype(tree, compound_stmt) + && validate_numnodes(tree, 1, "compound_stmt")); + int ntype; + + if (!res) + return (0); + + tree = CHILD(tree, 0); + ntype = TYPE(tree); + if ( (ntype == if_stmt) + || (ntype == while_stmt) + || (ntype == for_stmt) + || (ntype == try_stmt) + || (ntype == funcdef) + || (ntype == classdef)) + res = validate_node(tree); + else { + res = 0; + PyErr_Format(parser_error, + "Illegal compound statement type: %d.", TYPE(tree)); + } + return (res); +} + + +static int +validate_expr_stmt(node *tree) +{ + int j; + int nch = NCH(tree); + int res = (validate_ntype(tree, expr_stmt) + && is_odd(nch) + && validate_testlist(CHILD(tree, 0))); + + if (res && nch == 3 + && TYPE(CHILD(tree, 1)) == augassign) { + res = (validate_numnodes(CHILD(tree, 1), 1, "augassign") + && validate_testlist(CHILD(tree, 2))); + + if (res) { + char *s = STR(CHILD(CHILD(tree, 1), 0)); + + res = (strcmp(s, "+=") == 0 + || strcmp(s, "-=") == 0 + || strcmp(s, "*=") == 0 + || strcmp(s, "/=") == 0 + || strcmp(s, "//=") == 0 + || strcmp(s, "%=") == 0 + || strcmp(s, "&=") == 0 + || strcmp(s, "|=") == 0 + || strcmp(s, "^=") == 0 + || strcmp(s, "<<=") == 0 + || strcmp(s, ">>=") == 0 + || strcmp(s, "**=") == 0); + if (!res) + err_string("illegal augmmented assignment operator"); + } + } + else { + for (j = 1; res && (j < nch); j += 2) + res = (validate_equal(CHILD(tree, j)) + && validate_testlist(CHILD(tree, j + 1))); + } + return (res); +} + + +/* print_stmt: + * + * 'print' ( [ test (',' test)* [','] ] + * | '>>' test [ (',' test)+ [','] ] ) + */ +static int +validate_print_stmt(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, print_stmt) + && (nch > 0) + && validate_name(CHILD(tree, 0), "print")); + + if (res && nch > 1) { + int sym = TYPE(CHILD(tree, 1)); + int i = 1; + int allow_trailing_comma = 1; + + if (sym == test) + res = validate_test(CHILD(tree, i++)); + else { + if (nch < 3) + res = validate_numnodes(tree, 3, "print_stmt"); + else { + res = (validate_ntype(CHILD(tree, i), RIGHTSHIFT) + && validate_test(CHILD(tree, i+1))); + i += 2; + allow_trailing_comma = 0; + } + } + if (res) { + /* ... (',' test)* [','] */ + while (res && i+2 <= nch) { + res = (validate_comma(CHILD(tree, i)) + && validate_test(CHILD(tree, i+1))); + allow_trailing_comma = 1; + i += 2; + } + if (res && !allow_trailing_comma) + res = validate_numnodes(tree, i, "print_stmt"); + else if (res && i < nch) + res = validate_comma(CHILD(tree, i)); + } + } + return (res); +} + + +static int +validate_del_stmt(node *tree) +{ + return (validate_numnodes(tree, 2, "del_stmt") + && validate_name(CHILD(tree, 0), "del") + && validate_exprlist(CHILD(tree, 1))); +} + + +static int +validate_return_stmt(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, return_stmt) + && ((nch == 1) || (nch == 2)) + && validate_name(CHILD(tree, 0), "return")); + + if (res && (nch == 2)) + res = validate_testlist(CHILD(tree, 1)); + + return (res); +} + + +static int +validate_raise_stmt(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, raise_stmt) + && ((nch == 1) || (nch == 2) || (nch == 4) || (nch == 6))); + + if (res) { + res = validate_name(CHILD(tree, 0), "raise"); + if (res && (nch >= 2)) + res = validate_test(CHILD(tree, 1)); + if (res && nch > 2) { + res = (validate_comma(CHILD(tree, 2)) + && validate_test(CHILD(tree, 3))); + if (res && (nch > 4)) + res = (validate_comma(CHILD(tree, 4)) + && validate_test(CHILD(tree, 5))); + } + } + else + (void) validate_numnodes(tree, 2, "raise"); + if (res && (nch == 4)) + res = (validate_comma(CHILD(tree, 2)) + && validate_test(CHILD(tree, 3))); + + return (res); +} + + +/* yield_stmt: 'yield' testlist + */ +static int +validate_yield_stmt(node *tree) +{ + return (validate_ntype(tree, yield_stmt) + && validate_numnodes(tree, 2, "yield_stmt") + && validate_name(CHILD(tree, 0), "yield") + && validate_testlist(CHILD(tree, 1))); +} + + +static int +validate_import_as_name(node *tree) +{ + int nch = NCH(tree); + int ok = validate_ntype(tree, import_as_name); + + if (ok) { + if (nch == 1) + ok = validate_name(CHILD(tree, 0), NULL); + else if (nch == 3) + ok = (validate_name(CHILD(tree, 0), NULL) + && validate_name(CHILD(tree, 1), "as") + && validate_name(CHILD(tree, 2), NULL)); + else + ok = validate_numnodes(tree, 3, "import_as_name"); + } + return ok; +} + + +/* dotted_name: NAME ("." NAME)* + */ +static int +validate_dotted_name(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, dotted_name) + && is_odd(nch) + && validate_name(CHILD(tree, 0), NULL)); + int i; + + for (i = 1; res && (i < nch); i += 2) { + res = (validate_dot(CHILD(tree, i)) + && validate_name(CHILD(tree, i+1), NULL)); + } + return res; +} + + +/* dotted_as_name: dotted_name [NAME NAME] + */ +static int +validate_dotted_as_name(node *tree) +{ + int nch = NCH(tree); + int res = validate_ntype(tree, dotted_as_name); + + if (res) { + if (nch == 1) + res = validate_dotted_name(CHILD(tree, 0)); + else if (nch == 3) + res = (validate_dotted_name(CHILD(tree, 0)) + && validate_name(CHILD(tree, 1), "as") + && validate_name(CHILD(tree, 2), NULL)); + else { + res = 0; + err_string("illegal number of children for dotted_as_name"); + } + } + return res; +} + + +/* import_stmt: + * + * 'import' dotted_as_name (',' dotted_as_name)* + * | 'from' dotted_name 'import' ('*' | import_as_name (',' import_as_name)*) + */ +static int +validate_import_stmt(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, import_stmt) + && (nch >= 2) && is_even(nch) + && validate_ntype(CHILD(tree, 0), NAME)); + + if (res && (strcmp(STR(CHILD(tree, 0)), "import") == 0)) { + int j; + + res = validate_dotted_as_name(CHILD(tree, 1)); + for (j = 2; res && (j < nch); j += 2) + res = (validate_comma(CHILD(tree, j)) + && validate_dotted_as_name(CHILD(tree, j + 1))); + } + else if (res && (res = validate_name(CHILD(tree, 0), "from"))) { + res = ((nch >= 4) && is_even(nch) + && validate_dotted_name(CHILD(tree, 1)) + && validate_name(CHILD(tree, 2), "import")); + if (nch == 4) { + if (TYPE(CHILD(tree, 3)) == import_as_name) + res = validate_import_as_name(CHILD(tree, 3)); + else + res = validate_star(CHILD(tree, 3)); + } + else { + /* 'from' dotted_name 'import' import_as_name + * (',' import_as_name)+ + */ + int j; + res = validate_import_as_name(CHILD(tree, 3)); + for (j = 4; res && (j < nch); j += 2) + res = (validate_comma(CHILD(tree, j)) + && validate_import_as_name(CHILD(tree, j + 1))); + } + } + else + res = 0; + + return (res); +} + + +static int +validate_global_stmt(node *tree) +{ + int j; + int nch = NCH(tree); + int res = (validate_ntype(tree, global_stmt) + && is_even(nch) && (nch >= 2)); + + if (!res && !PyErr_Occurred()) + err_string("illegal global statement"); + + if (res) + res = (validate_name(CHILD(tree, 0), "global") + && validate_ntype(CHILD(tree, 1), NAME)); + for (j = 2; res && (j < nch); j += 2) + res = (validate_comma(CHILD(tree, j)) + && validate_ntype(CHILD(tree, j + 1), NAME)); + + return (res); +} + + +/* exec_stmt: + * + * 'exec' expr ['in' test [',' test]] + */ +static int +validate_exec_stmt(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, exec_stmt) + && ((nch == 2) || (nch == 4) || (nch == 6)) + && validate_name(CHILD(tree, 0), "exec") + && validate_expr(CHILD(tree, 1))); + + if (!res && !PyErr_Occurred()) + err_string("illegal exec statement"); + if (res && (nch > 2)) + res = (validate_name(CHILD(tree, 2), "in") + && validate_test(CHILD(tree, 3))); + if (res && (nch == 6)) + res = (validate_comma(CHILD(tree, 4)) + && validate_test(CHILD(tree, 5))); + + return (res); +} + + +/* assert_stmt: + * + * 'assert' test [',' test] + */ +static int +validate_assert_stmt(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, assert_stmt) + && ((nch == 2) || (nch == 4)) + && (validate_name(CHILD(tree, 0), "assert")) + && validate_test(CHILD(tree, 1))); + + if (!res && !PyErr_Occurred()) + err_string("illegal assert statement"); + if (res && (nch > 2)) + res = (validate_comma(CHILD(tree, 2)) + && validate_test(CHILD(tree, 3))); + + return (res); +} + + +static int +validate_while(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, while_stmt) + && ((nch == 4) || (nch == 7)) + && validate_name(CHILD(tree, 0), "while") + && validate_test(CHILD(tree, 1)) + && validate_colon(CHILD(tree, 2)) + && validate_suite(CHILD(tree, 3))); + + if (res && (nch == 7)) + res = (validate_name(CHILD(tree, 4), "else") + && validate_colon(CHILD(tree, 5)) + && validate_suite(CHILD(tree, 6))); + + return (res); +} + + +static int +validate_for(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, for_stmt) + && ((nch == 6) || (nch == 9)) + && validate_name(CHILD(tree, 0), "for") + && validate_exprlist(CHILD(tree, 1)) + && validate_name(CHILD(tree, 2), "in") + && validate_testlist(CHILD(tree, 3)) + && validate_colon(CHILD(tree, 4)) + && validate_suite(CHILD(tree, 5))); + + if (res && (nch == 9)) + res = (validate_name(CHILD(tree, 6), "else") + && validate_colon(CHILD(tree, 7)) + && validate_suite(CHILD(tree, 8))); + + return (res); +} + + +/* try_stmt: + * 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite] + * | 'try' ':' suite 'finally' ':' suite + * + */ +static int +validate_try(node *tree) +{ + int nch = NCH(tree); + int pos = 3; + int res = (validate_ntype(tree, try_stmt) + && (nch >= 6) && ((nch % 3) == 0)); + + if (res) + res = (validate_name(CHILD(tree, 0), "try") + && validate_colon(CHILD(tree, 1)) + && validate_suite(CHILD(tree, 2)) + && validate_colon(CHILD(tree, nch - 2)) + && validate_suite(CHILD(tree, nch - 1))); + else if (!PyErr_Occurred()) { + const char* name = "except"; + if (TYPE(CHILD(tree, nch - 3)) != except_clause) + name = STR(CHILD(tree, nch - 3)); + + PyErr_Format(parser_error, + "Illegal number of children for try/%s node.", name); + } + /* Skip past except_clause sections: */ + while (res && (TYPE(CHILD(tree, pos)) == except_clause)) { + res = (validate_except_clause(CHILD(tree, pos)) + && validate_colon(CHILD(tree, pos + 1)) + && validate_suite(CHILD(tree, pos + 2))); + pos += 3; + } + if (res && (pos < nch)) { + res = validate_ntype(CHILD(tree, pos), NAME); + if (res && (strcmp(STR(CHILD(tree, pos)), "finally") == 0)) + res = (validate_numnodes(tree, 6, "try/finally") + && validate_colon(CHILD(tree, 4)) + && validate_suite(CHILD(tree, 5))); + else if (res) { + if (nch == (pos + 3)) { + res = ((strcmp(STR(CHILD(tree, pos)), "except") == 0) + || (strcmp(STR(CHILD(tree, pos)), "else") == 0)); + if (!res) + err_string("illegal trailing triple in try statement"); + } + else if (nch == (pos + 6)) { + res = (validate_name(CHILD(tree, pos), "except") + && validate_colon(CHILD(tree, pos + 1)) + && validate_suite(CHILD(tree, pos + 2)) + && validate_name(CHILD(tree, pos + 3), "else")); + } + else + res = validate_numnodes(tree, pos + 3, "try/except"); + } + } + return (res); +} + + +static int +validate_except_clause(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, except_clause) + && ((nch == 1) || (nch == 2) || (nch == 4)) + && validate_name(CHILD(tree, 0), "except")); + + if (res && (nch > 1)) + res = validate_test(CHILD(tree, 1)); + if (res && (nch == 4)) + res = (validate_comma(CHILD(tree, 2)) + && validate_test(CHILD(tree, 3))); + + return (res); +} + + +static int +validate_test(node *tree) +{ + int nch = NCH(tree); + int res = validate_ntype(tree, test) && is_odd(nch); + + if (res && (TYPE(CHILD(tree, 0)) == lambdef)) + res = ((nch == 1) + && validate_lambdef(CHILD(tree, 0))); + else if (res) { + int pos; + res = validate_and_test(CHILD(tree, 0)); + for (pos = 1; res && (pos < nch); pos += 2) + res = (validate_name(CHILD(tree, pos), "or") + && validate_and_test(CHILD(tree, pos + 1))); + } + return (res); +} + + +static int +validate_and_test(node *tree) +{ + int pos; + int nch = NCH(tree); + int res = (validate_ntype(tree, and_test) + && is_odd(nch) + && validate_not_test(CHILD(tree, 0))); + + for (pos = 1; res && (pos < nch); pos += 2) + res = (validate_name(CHILD(tree, pos), "and") + && validate_not_test(CHILD(tree, 0))); + + return (res); +} + + +static int +validate_not_test(node *tree) +{ + int nch = NCH(tree); + int res = validate_ntype(tree, not_test) && ((nch == 1) || (nch == 2)); + + if (res) { + if (nch == 2) + res = (validate_name(CHILD(tree, 0), "not") + && validate_not_test(CHILD(tree, 1))); + else if (nch == 1) + res = validate_comparison(CHILD(tree, 0)); + } + return (res); +} + + +static int +validate_comparison(node *tree) +{ + int pos; + int nch = NCH(tree); + int res = (validate_ntype(tree, comparison) + && is_odd(nch) + && validate_expr(CHILD(tree, 0))); + + for (pos = 1; res && (pos < nch); pos += 2) + res = (validate_comp_op(CHILD(tree, pos)) + && validate_expr(CHILD(tree, pos + 1))); + + return (res); +} + + +static int +validate_comp_op(node *tree) +{ + int res = 0; + int nch = NCH(tree); + + if (!validate_ntype(tree, comp_op)) + return (0); + if (nch == 1) { + /* + * Only child will be a terminal with a well-defined symbolic name + * or a NAME with a string of either 'is' or 'in' + */ + tree = CHILD(tree, 0); + switch (TYPE(tree)) { + case LESS: + case GREATER: + case EQEQUAL: + case EQUAL: + case LESSEQUAL: + case GREATEREQUAL: + case NOTEQUAL: + res = 1; + break; + case NAME: + res = ((strcmp(STR(tree), "in") == 0) + || (strcmp(STR(tree), "is") == 0)); + if (!res) { + PyErr_Format(parser_error, + "illegal operator '%s'", STR(tree)); + } + break; + default: + err_string("illegal comparison operator type"); + break; + } + } + else if ((res = validate_numnodes(tree, 2, "comp_op")) != 0) { + res = (validate_ntype(CHILD(tree, 0), NAME) + && validate_ntype(CHILD(tree, 1), NAME) + && (((strcmp(STR(CHILD(tree, 0)), "is") == 0) + && (strcmp(STR(CHILD(tree, 1)), "not") == 0)) + || ((strcmp(STR(CHILD(tree, 0)), "not") == 0) + && (strcmp(STR(CHILD(tree, 1)), "in") == 0)))); + if (!res && !PyErr_Occurred()) + err_string("unknown comparison operator"); + } + return (res); +} + + +static int +validate_expr(node *tree) +{ + int j; + int nch = NCH(tree); + int res = (validate_ntype(tree, expr) + && is_odd(nch) + && validate_xor_expr(CHILD(tree, 0))); + + for (j = 2; res && (j < nch); j += 2) + res = (validate_xor_expr(CHILD(tree, j)) + && validate_vbar(CHILD(tree, j - 1))); + + return (res); +} + + +static int +validate_xor_expr(node *tree) +{ + int j; + int nch = NCH(tree); + int res = (validate_ntype(tree, xor_expr) + && is_odd(nch) + && validate_and_expr(CHILD(tree, 0))); + + for (j = 2; res && (j < nch); j += 2) + res = (validate_circumflex(CHILD(tree, j - 1)) + && validate_and_expr(CHILD(tree, j))); + + return (res); +} + + +static int +validate_and_expr(node *tree) +{ + int pos; + int nch = NCH(tree); + int res = (validate_ntype(tree, and_expr) + && is_odd(nch) + && validate_shift_expr(CHILD(tree, 0))); + + for (pos = 1; res && (pos < nch); pos += 2) + res = (validate_ampersand(CHILD(tree, pos)) + && validate_shift_expr(CHILD(tree, pos + 1))); + + return (res); +} + + +static int +validate_chain_two_ops(node *tree, int (*termvalid)(node *), int op1, int op2) + { + int pos = 1; + int nch = NCH(tree); + int res = (is_odd(nch) + && (*termvalid)(CHILD(tree, 0))); + + for ( ; res && (pos < nch); pos += 2) { + if (TYPE(CHILD(tree, pos)) != op1) + res = validate_ntype(CHILD(tree, pos), op2); + if (res) + res = (*termvalid)(CHILD(tree, pos + 1)); + } + return (res); +} + + +static int +validate_shift_expr(node *tree) +{ + return (validate_ntype(tree, shift_expr) + && validate_chain_two_ops(tree, validate_arith_expr, + LEFTSHIFT, RIGHTSHIFT)); +} + + +static int +validate_arith_expr(node *tree) +{ + return (validate_ntype(tree, arith_expr) + && validate_chain_two_ops(tree, validate_term, PLUS, MINUS)); +} + + +static int +validate_term(node *tree) +{ + int pos = 1; + int nch = NCH(tree); + int res = (validate_ntype(tree, term) + && is_odd(nch) + && validate_factor(CHILD(tree, 0))); + + for ( ; res && (pos < nch); pos += 2) + res = (((TYPE(CHILD(tree, pos)) == STAR) + || (TYPE(CHILD(tree, pos)) == SLASH) + || (TYPE(CHILD(tree, pos)) == DOUBLESLASH) + || (TYPE(CHILD(tree, pos)) == PERCENT)) + && validate_factor(CHILD(tree, pos + 1))); + + return (res); +} + + +/* factor: + * + * factor: ('+'|'-'|'~') factor | power + */ +static int +validate_factor(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, factor) + && (((nch == 2) + && ((TYPE(CHILD(tree, 0)) == PLUS) + || (TYPE(CHILD(tree, 0)) == MINUS) + || (TYPE(CHILD(tree, 0)) == TILDE)) + && validate_factor(CHILD(tree, 1))) + || ((nch == 1) + && validate_power(CHILD(tree, 0))))); + return (res); +} + + +/* power: + * + * power: atom trailer* ('**' factor)* + */ +static int +validate_power(node *tree) +{ + int pos = 1; + int nch = NCH(tree); + int res = (validate_ntype(tree, power) && (nch >= 1) + && validate_atom(CHILD(tree, 0))); + + while (res && (pos < nch) && (TYPE(CHILD(tree, pos)) == trailer)) + res = validate_trailer(CHILD(tree, pos++)); + if (res && (pos < nch)) { + if (!is_even(nch - pos)) { + err_string("illegal number of nodes for 'power'"); + return (0); + } + for ( ; res && (pos < (nch - 1)); pos += 2) + res = (validate_doublestar(CHILD(tree, pos)) + && validate_factor(CHILD(tree, pos + 1))); + } + return (res); +} + + +static int +validate_atom(node *tree) +{ + int pos; + int nch = NCH(tree); + int res = validate_ntype(tree, atom); + + if (res && nch < 1) + res = validate_numnodes(tree, nch+1, "atom"); + if (res) { + switch (TYPE(CHILD(tree, 0))) { + case LPAR: + res = ((nch <= 3) + && (validate_rparen(CHILD(tree, nch - 1)))); + + if (res && (nch == 3)) + res = validate_testlist(CHILD(tree, 1)); + break; + case LSQB: + if (nch == 2) + res = validate_ntype(CHILD(tree, 1), RSQB); + else if (nch == 3) + res = (validate_listmaker(CHILD(tree, 1)) + && validate_ntype(CHILD(tree, 2), RSQB)); + else { + res = 0; + err_string("illegal list display atom"); + } + break; + case LBRACE: + res = ((nch <= 3) + && validate_ntype(CHILD(tree, nch - 1), RBRACE)); + + if (res && (nch == 3)) + res = validate_dictmaker(CHILD(tree, 1)); + break; + case BACKQUOTE: + res = ((nch == 3) + && validate_testlist1(CHILD(tree, 1)) + && validate_ntype(CHILD(tree, 2), BACKQUOTE)); + break; + case NAME: + case NUMBER: + res = (nch == 1); + break; + case STRING: + for (pos = 1; res && (pos < nch); ++pos) + res = validate_ntype(CHILD(tree, pos), STRING); + break; + default: + res = 0; + break; + } + } + return (res); +} + + +/* listmaker: + * test ( list_for | (',' test)* [','] ) + */ +static int +validate_listmaker(node *tree) +{ + int nch = NCH(tree); + int ok = nch; + + if (nch == 0) + err_string("missing child nodes of listmaker"); + else + ok = validate_test(CHILD(tree, 0)); + + /* + * list_iter | (',' test)* [','] + */ + if (nch == 2 && TYPE(CHILD(tree, 1)) == list_for) + ok = validate_list_for(CHILD(tree, 1)); + else { + /* (',' test)* [','] */ + int i = 1; + while (ok && nch - i >= 2) { + ok = (validate_comma(CHILD(tree, i)) + && validate_test(CHILD(tree, i+1))); + i += 2; + } + if (ok && i == nch-1) + ok = validate_comma(CHILD(tree, i)); + else if (i != nch) { + ok = 0; + err_string("illegal trailing nodes for listmaker"); + } + } + return ok; +} + + +/* funcdef: + * 'def' NAME parameters ':' suite + * + */ +static int +validate_funcdef(node *tree) +{ + return (validate_ntype(tree, funcdef) + && validate_numnodes(tree, 5, "funcdef") + && validate_name(CHILD(tree, 0), "def") + && validate_ntype(CHILD(tree, 1), NAME) + && validate_colon(CHILD(tree, 3)) + && validate_parameters(CHILD(tree, 2)) + && validate_suite(CHILD(tree, 4))); +} + + +static int +validate_lambdef(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, lambdef) + && ((nch == 3) || (nch == 4)) + && validate_name(CHILD(tree, 0), "lambda") + && validate_colon(CHILD(tree, nch - 2)) + && validate_test(CHILD(tree, nch - 1))); + + if (res && (nch == 4)) + res = validate_varargslist(CHILD(tree, 1)); + else if (!res && !PyErr_Occurred()) + (void) validate_numnodes(tree, 3, "lambdef"); + + return (res); +} + + +/* arglist: + * + * (argument ',')* (argument [','] | '*' test [',' '**' test] | '**' test) + */ +static int +validate_arglist(node *tree) +{ + int nch = NCH(tree); + int i = 0; + int ok = 1; + + if (nch <= 0) + /* raise the right error from having an invalid number of children */ + return validate_numnodes(tree, nch + 1, "arglist"); + + while (ok && nch-i >= 2) { + /* skip leading (argument ',') */ + ok = (validate_argument(CHILD(tree, i)) + && validate_comma(CHILD(tree, i+1))); + if (ok) + i += 2; + else + PyErr_Clear(); + } + ok = 1; + if (nch-i > 0) { + /* + * argument | '*' test [',' '**' test] | '**' test + */ + int sym = TYPE(CHILD(tree, i)); + + if (sym == argument) { + ok = validate_argument(CHILD(tree, i)); + if (ok && i+1 != nch) { + err_string("illegal arglist specification" + " (extra stuff on end)"); + ok = 0; + } + } + else if (sym == STAR) { + ok = validate_star(CHILD(tree, i)); + if (ok && (nch-i == 2)) + ok = validate_test(CHILD(tree, i+1)); + else if (ok && (nch-i == 5)) + ok = (validate_test(CHILD(tree, i+1)) + && validate_comma(CHILD(tree, i+2)) + && validate_doublestar(CHILD(tree, i+3)) + && validate_test(CHILD(tree, i+4))); + else { + err_string("illegal use of '*' in arglist"); + ok = 0; + } + } + else if (sym == DOUBLESTAR) { + if (nch-i == 2) + ok = (validate_doublestar(CHILD(tree, i)) + && validate_test(CHILD(tree, i+1))); + else { + err_string("illegal use of '**' in arglist"); + ok = 0; + } + } + else { + err_string("illegal arglist specification"); + ok = 0; + } + } + return (ok); +} + + + +/* argument: + * + * [test '='] test + */ +static int +validate_argument(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, argument) + && ((nch == 1) || (nch == 3)) + && validate_test(CHILD(tree, 0))); + + if (res && (nch == 3)) + res = (validate_equal(CHILD(tree, 1)) + && validate_test(CHILD(tree, 2))); + + return (res); +} + + + +/* trailer: + * + * '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME + */ +static int +validate_trailer(node *tree) +{ + int nch = NCH(tree); + int res = validate_ntype(tree, trailer) && ((nch == 2) || (nch == 3)); + + if (res) { + switch (TYPE(CHILD(tree, 0))) { + case LPAR: + res = validate_rparen(CHILD(tree, nch - 1)); + if (res && (nch == 3)) + res = validate_arglist(CHILD(tree, 1)); + break; + case LSQB: + res = (validate_numnodes(tree, 3, "trailer") + && validate_subscriptlist(CHILD(tree, 1)) + && validate_ntype(CHILD(tree, 2), RSQB)); + break; + case DOT: + res = (validate_numnodes(tree, 2, "trailer") + && validate_ntype(CHILD(tree, 1), NAME)); + break; + default: + res = 0; + break; + } + } + else { + (void) validate_numnodes(tree, 2, "trailer"); + } + return (res); +} + + +/* subscriptlist: + * + * subscript (',' subscript)* [','] + */ +static int +validate_subscriptlist(node *tree) +{ + return (validate_repeating_list(tree, subscriptlist, + validate_subscript, "subscriptlist")); +} + + +/* subscript: + * + * '.' '.' '.' | test | [test] ':' [test] [sliceop] + */ +static int +validate_subscript(node *tree) +{ + int offset = 0; + int nch = NCH(tree); + int res = validate_ntype(tree, subscript) && (nch >= 1) && (nch <= 4); + + if (!res) { + if (!PyErr_Occurred()) + err_string("invalid number of arguments for subscript node"); + return (0); + } + if (TYPE(CHILD(tree, 0)) == DOT) + /* take care of ('.' '.' '.') possibility */ + return (validate_numnodes(tree, 3, "subscript") + && validate_dot(CHILD(tree, 0)) + && validate_dot(CHILD(tree, 1)) + && validate_dot(CHILD(tree, 2))); + if (nch == 1) { + if (TYPE(CHILD(tree, 0)) == test) + res = validate_test(CHILD(tree, 0)); + else + res = validate_colon(CHILD(tree, 0)); + return (res); + } + /* Must be [test] ':' [test] [sliceop], + * but at least one of the optional components will + * be present, but we don't know which yet. + */ + if ((TYPE(CHILD(tree, 0)) != COLON) || (nch == 4)) { + res = validate_test(CHILD(tree, 0)); + offset = 1; + } + if (res) + res = validate_colon(CHILD(tree, offset)); + if (res) { + int rem = nch - ++offset; + if (rem) { + if (TYPE(CHILD(tree, offset)) == test) { + res = validate_test(CHILD(tree, offset)); + ++offset; + --rem; + } + if (res && rem) + res = validate_sliceop(CHILD(tree, offset)); + } + } + return (res); +} + + +static int +validate_sliceop(node *tree) +{ + int nch = NCH(tree); + int res = ((nch == 1) || validate_numnodes(tree, 2, "sliceop")) + && validate_ntype(tree, sliceop); + if (!res && !PyErr_Occurred()) { + res = validate_numnodes(tree, 1, "sliceop"); + } + if (res) + res = validate_colon(CHILD(tree, 0)); + if (res && (nch == 2)) + res = validate_test(CHILD(tree, 1)); + + return (res); +} + + +static int +validate_exprlist(node *tree) +{ + return (validate_repeating_list(tree, exprlist, + validate_expr, "exprlist")); +} + + +static int +validate_dictmaker(node *tree) +{ + int nch = NCH(tree); + int res = (validate_ntype(tree, dictmaker) + && (nch >= 3) + && validate_test(CHILD(tree, 0)) + && validate_colon(CHILD(tree, 1)) + && validate_test(CHILD(tree, 2))); + + if (res && ((nch % 4) == 0)) + res = validate_comma(CHILD(tree, --nch)); + else if (res) + res = ((nch % 4) == 3); + + if (res && (nch > 3)) { + int pos = 3; + /* ( ',' test ':' test )* */ + while (res && (pos < nch)) { + res = (validate_comma(CHILD(tree, pos)) + && validate_test(CHILD(tree, pos + 1)) + && validate_colon(CHILD(tree, pos + 2)) + && validate_test(CHILD(tree, pos + 3))); + pos += 4; + } + } + return (res); +} + + +static int +validate_eval_input(node *tree) +{ + int pos; + int nch = NCH(tree); + int res = (validate_ntype(tree, eval_input) + && (nch >= 2) + && validate_testlist(CHILD(tree, 0)) + && validate_ntype(CHILD(tree, nch - 1), ENDMARKER)); + + for (pos = 1; res && (pos < (nch - 1)); ++pos) + res = validate_ntype(CHILD(tree, pos), NEWLINE); + + return (res); +} + + +static int +validate_node(node *tree) +{ + int nch = 0; /* num. children on current node */ + int res = 1; /* result value */ + node* next = 0; /* node to process after this one */ + + while (res && (tree != 0)) { + nch = NCH(tree); + next = 0; + switch (TYPE(tree)) { + /* + * Definition nodes. + */ + case funcdef: + res = validate_funcdef(tree); + break; + case classdef: + res = validate_class(tree); + break; + /* + * "Trivial" parse tree nodes. + * (Why did I call these trivial?) + */ + case stmt: + res = validate_stmt(tree); + break; + case small_stmt: + /* + * expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt + * | import_stmt | global_stmt | exec_stmt | assert_stmt + */ + res = validate_small_stmt(tree); + break; + case flow_stmt: + res = (validate_numnodes(tree, 1, "flow_stmt") + && ((TYPE(CHILD(tree, 0)) == break_stmt) + || (TYPE(CHILD(tree, 0)) == continue_stmt) + || (TYPE(CHILD(tree, 0)) == yield_stmt) + || (TYPE(CHILD(tree, 0)) == return_stmt) + || (TYPE(CHILD(tree, 0)) == raise_stmt))); + if (res) + next = CHILD(tree, 0); + else if (nch == 1) + err_string("illegal flow_stmt type"); + break; + case yield_stmt: + res = validate_yield_stmt(tree); + break; + /* + * Compound statements. + */ + case simple_stmt: + res = validate_simple_stmt(tree); + break; + case compound_stmt: + res = validate_compound_stmt(tree); + break; + /* + * Fundamental statements. + */ + case expr_stmt: + res = validate_expr_stmt(tree); + break; + case print_stmt: + res = validate_print_stmt(tree); + break; + case del_stmt: + res = validate_del_stmt(tree); + break; + case pass_stmt: + res = (validate_numnodes(tree, 1, "pass") + && validate_name(CHILD(tree, 0), "pass")); + break; + case break_stmt: + res = (validate_numnodes(tree, 1, "break") + && validate_name(CHILD(tree, 0), "break")); + break; + case continue_stmt: + res = (validate_numnodes(tree, 1, "continue") + && validate_name(CHILD(tree, 0), "continue")); + break; + case return_stmt: + res = validate_return_stmt(tree); + break; + case raise_stmt: + res = validate_raise_stmt(tree); + break; + case import_stmt: + res = validate_import_stmt(tree); + break; + case global_stmt: + res = validate_global_stmt(tree); + break; + case exec_stmt: + res = validate_exec_stmt(tree); + break; + case assert_stmt: + res = validate_assert_stmt(tree); + break; + case if_stmt: + res = validate_if(tree); + break; + case while_stmt: + res = validate_while(tree); + break; + case for_stmt: + res = validate_for(tree); + break; + case try_stmt: + res = validate_try(tree); + break; + case suite: + res = validate_suite(tree); + break; + /* + * Expression nodes. + */ + case testlist: + res = validate_testlist(tree); + break; + case testlist1: + res = validate_testlist1(tree); + break; + case test: + res = validate_test(tree); + break; + case and_test: + res = validate_and_test(tree); + break; + case not_test: + res = validate_not_test(tree); + break; + case comparison: + res = validate_comparison(tree); + break; + case exprlist: + res = validate_exprlist(tree); + break; + case comp_op: + res = validate_comp_op(tree); + break; + case expr: + res = validate_expr(tree); + break; + case xor_expr: + res = validate_xor_expr(tree); + break; + case and_expr: + res = validate_and_expr(tree); + break; + case shift_expr: + res = validate_shift_expr(tree); + break; + case arith_expr: + res = validate_arith_expr(tree); + break; + case term: + res = validate_term(tree); + break; + case factor: + res = validate_factor(tree); + break; + case power: + res = validate_power(tree); + break; + case atom: + res = validate_atom(tree); + break; + + default: + /* Hopefully never reached! */ + err_string("unrecognized node type"); + res = 0; + break; + } + tree = next; + } + return (res); +} + + +static int +validate_expr_tree(node *tree) +{ + int res = validate_eval_input(tree); + + if (!res && !PyErr_Occurred()) + err_string("could not validate expression tuple"); + + return (res); +} + + +/* file_input: + * (NEWLINE | stmt)* ENDMARKER + */ +static int +validate_file_input(node *tree) +{ + int j; + int nch = NCH(tree) - 1; + int res = ((nch >= 0) + && validate_ntype(CHILD(tree, nch), ENDMARKER)); + + for (j = 0; res && (j < nch); ++j) { + if (TYPE(CHILD(tree, j)) == stmt) + res = validate_stmt(CHILD(tree, j)); + else + res = validate_newline(CHILD(tree, j)); + } + /* This stays in to prevent any internal failures from getting to the + * user. Hopefully, this won't be needed. If a user reports getting + * this, we have some debugging to do. + */ + if (!res && !PyErr_Occurred()) + err_string("VALIDATION FAILURE: report this to the maintainer!"); + + return (res); +} + +static int +validate_encoding_decl(node *tree) +{ + int nch = NCH(tree); + int res = ((nch == 1) + && validate_file_input(CHILD(tree, 0))); + + if (!res && !PyErr_Occurred()) + err_string("Error Parsing encoding_decl"); + + return res; +} + +static PyObject* +pickle_constructor = NULL; + + +static PyObject* +parser__pickler(PyObject *self, PyObject *args) +{ + NOTE(ARGUNUSED(self)) + PyObject *result = NULL; + PyObject *st = NULL; + PyObject *empty_dict = NULL; + + if (PyArg_ParseTuple(args, "O!:_pickler", &PyST_Type, &st)) { + PyObject *newargs; + PyObject *tuple; + + if ((empty_dict = PyDict_New()) == NULL) + goto finally; + if ((newargs = Py_BuildValue("Oi", st, 1)) == NULL) + goto finally; + tuple = parser_st2tuple((PyST_Object*)NULL, newargs, empty_dict); + if (tuple != NULL) { + result = Py_BuildValue("O(O)", pickle_constructor, tuple); + Py_DECREF(tuple); + } + Py_DECREF(empty_dict); + Py_DECREF(newargs); + } + finally: + Py_XDECREF(empty_dict); + + return (result); +} + + +/* Functions exported by this module. Most of this should probably + * be converted into an ST object with methods, but that is better + * done directly in Python, allowing subclasses to be created directly. + * We'd really have to write a wrapper around it all anyway to allow + * inheritance. + */ +static PyMethodDef parser_functions[] = { + {"ast2tuple", (PyCFunction)parser_st2tuple, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates a tuple-tree representation of an ST.")}, + {"ast2list", (PyCFunction)parser_st2list, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates a list-tree representation of an ST.")}, + {"compileast", (PyCFunction)parser_compilest, PUBLIC_METHOD_TYPE, + PyDoc_STR("Compiles an ST object into a code object.")}, + {"compilest", (PyCFunction)parser_compilest, PUBLIC_METHOD_TYPE, + PyDoc_STR("Compiles an ST object into a code object.")}, + {"expr", (PyCFunction)parser_expr, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates an ST object from an expression.")}, + {"isexpr", (PyCFunction)parser_isexpr, PUBLIC_METHOD_TYPE, + PyDoc_STR("Determines if an ST object was created from an expression.")}, + {"issuite", (PyCFunction)parser_issuite, PUBLIC_METHOD_TYPE, + PyDoc_STR("Determines if an ST object was created from a suite.")}, + {"suite", (PyCFunction)parser_suite, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates an ST object from a suite.")}, + {"sequence2ast", (PyCFunction)parser_tuple2st, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates an ST object from a tree representation.")}, + {"sequence2st", (PyCFunction)parser_tuple2st, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates an ST object from a tree representation.")}, + {"st2tuple", (PyCFunction)parser_st2tuple, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates a tuple-tree representation of an ST.")}, + {"st2list", (PyCFunction)parser_st2list, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates a list-tree representation of an ST.")}, + {"tuple2ast", (PyCFunction)parser_tuple2st, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates an ST object from a tree representation.")}, + {"tuple2st", (PyCFunction)parser_tuple2st, PUBLIC_METHOD_TYPE, + PyDoc_STR("Creates an ST object from a tree representation.")}, + + /* private stuff: support pickle module */ + {"_pickler", (PyCFunction)parser__pickler, METH_VARARGS, + PyDoc_STR("Returns the pickle magic to allow ST objects to be pickled.")}, + + {NULL, NULL, 0, NULL} + }; + + +PyMODINIT_FUNC initparser(void); /* supply a prototype */ + +PyMODINIT_FUNC +initparser(void) +{ + PyObject *module, *copyreg; + + PyST_Type.ob_type = &PyType_Type; + module = Py_InitModule("parser", parser_functions); + + if (parser_error == 0) + parser_error = PyErr_NewException("parser.ParserError", NULL, NULL); + + if (parser_error == 0) + /* caller will check PyErr_Occurred() */ + return; + /* CAUTION: The code next used to skip bumping the refcount on + * parser_error. That's a disaster if initparser() gets called more + * than once. By incref'ing, we ensure that each module dict that + * gets created owns its reference to the shared parser_error object, + * and the file static parser_error vrbl owns a reference too. + */ + Py_INCREF(parser_error); + if (PyModule_AddObject(module, "ParserError", parser_error) != 0) + return; + + Py_INCREF(&PyST_Type); + PyModule_AddObject(module, "ASTType", (PyObject*)&PyST_Type); + Py_INCREF(&PyST_Type); + PyModule_AddObject(module, "STType", (PyObject*)&PyST_Type); + + PyModule_AddStringConstant(module, "__copyright__", + parser_copyright_string); + PyModule_AddStringConstant(module, "__doc__", + parser_doc_string); + PyModule_AddStringConstant(module, "__version__", + parser_version_string); + + /* Register to support pickling. + * If this fails, the import of this module will fail because an + * exception will be raised here; should we clear the exception? + */ + copyreg = PyImport_ImportModule("copy_reg"); + if (copyreg != NULL) { + PyObject *func, *pickler; + + func = PyObject_GetAttrString(copyreg, "pickle"); + pickle_constructor = PyObject_GetAttrString(module, "sequence2st"); + pickler = PyObject_GetAttrString(module, "_pickler"); + Py_XINCREF(pickle_constructor); + if ((func != NULL) && (pickle_constructor != NULL) + && (pickler != NULL)) { + PyObject *res; + + res = PyObject_CallFunction(func, "OOO", &PyST_Type, pickler, + pickle_constructor); + Py_XDECREF(res); + } + Py_XDECREF(func); + Py_XDECREF(pickle_constructor); + Py_XDECREF(pickler); + Py_DECREF(copyreg); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcre-int.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcre-int.h new file mode 100644 index 00000000..f6f7e0f8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcre-int.h @@ -0,0 +1,303 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + + +#define PCRE_VERSION "1.09 28-Apr-1998" + + +/* This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1998 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. +----------------------------------------------------------------------------- +*/ + +/* This header contains definitions that are shared between the different +modules, but which are not relevant to the outside. */ + + +/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(), +define a macro for memmove() if USE_BCOPY is defined. */ + +#ifdef USE_BCOPY +#undef memmove /* some systems may have a macro */ +#define memmove(a, b, c) bcopy(b, a, c) +#endif + +/* Standard C headers plus the external interface definition */ + +#include +#include +#include +#include +#include +#include +#include +#include "pcre.h" + +/* In case there is no definition of offsetof() provided - though any proper +Standard C system should have one. */ + +#ifndef offsetof +#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field)) +#endif + +/* Private options flags start at the most significant end of the two bytes. +The public options defined in pcre.h start at the least significant end. Make +sure they don't overlap! */ + +#define PCRE_FIRSTSET 0x8000 /* first_char is set */ +#define PCRE_STARTLINE 0x4000 /* start after \n for multiline */ +#define PCRE_COMPILED_CASELESS 0x2000 /* like it says */ + +/* Options for the "extra" block produced by pcre_study(). */ + +#define PCRE_STUDY_CASELESS 0x01 /* study was caseless */ +#define PCRE_STUDY_MAPPED 0x02 /* a map of starting chars exists */ + +/* Masks for identifying the public options: all permitted at compile time, +only some permitted at run or study time. */ + +#ifdef FOR_PYTHON +#define PUBLIC_OPTIONS \ + (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ + PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY| \ + PCRE_LOCALE) +#else +#define PUBLIC_OPTIONS \ + (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ + PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY) +#endif +#define PUBLIC_EXEC_OPTIONS \ + (PCRE_CASELESS|PCRE_ANCHORED|PCRE_MULTILINE|PCRE_NOTBOL|PCRE_NOTEOL| \ + PCRE_DOTALL|PCRE_DOLLAR_ENDONLY) + +#define PUBLIC_STUDY_OPTIONS (PCRE_CASELESS) + +/* Magic number to provide a small check against being handed junk. */ + +#define MAGIC_NUMBER 0x50435245 /* 'PCRE' */ + +/* Miscellaneous definitions */ + +typedef int BOOL; + +#define FALSE 0 +#define TRUE 1 + +/* These are escaped items that aren't just an encoding of a particular data +value such as \n. They must have non-zero values, as check_escape() returns +their negation. Also, they must appear in the same order as in the opcode +definitions below, up to ESC_Z. The final one must be ESC_REF as subsequent +values are used for \1, \2, \3, etc. There is a test in the code for an escape +greater than ESC_b and less than ESC_X to detect the types that may be +repeated. If any new escapes are put in-between that don't consume a character, +that code will have to change. */ + +enum { ESC_A = 1, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ESC_w, + + /* These are not Perl escapes, so can't appear in the */ + ESC_X, /* simple table-lookup because they must be conditional */ + /* on PCRE_EXTRA. */ + ESC_Z, + ESC_REF }; + +/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets +that extract substrings. Starting from 1 (i.e. after OP_END), the values up to +OP_EOD must correspond in order to the list of escapes immediately above. */ + +enum { + OP_END, /* End of pattern */ + + /* Values corresponding to backslashed metacharacters */ + + OP_SOD, /* Start of data: \A */ + OP_NOT_WORD_BOUNDARY, /* \B */ + OP_WORD_BOUNDARY, /* \b */ + OP_NOT_DIGIT, /* \D */ + OP_DIGIT, /* \d */ + OP_NOT_WHITESPACE, /* \S */ + OP_WHITESPACE, /* \s */ + OP_NOT_WORDCHAR, /* \W */ + OP_WORDCHAR, /* \w */ + OP_CUT, /* The analogue of Prolog's "cut" operation (extension) */ + OP_EOD, /* End of data: \Z. */ + + OP_NOT_WORD_BOUNDARY_L, /* localized \B */ + OP_WORD_BOUNDARY_L, /* localized \b */ + OP_NOT_WORDCHAR_L, /* localized \W */ + OP_WORDCHAR_L, /* localized \w */ + + OP_CIRC, /* Start of line - varies with multiline switch */ + OP_DOLL, /* End of line - varies with multiline switch */ + OP_ANY, /* Match any character */ + OP_CHARS, /* Match string of characters */ + OP_NOT, /* Match anything but the following char */ + + OP_STAR, /* The maximizing and minimizing versions of */ + OP_MINSTAR, /* all these opcodes must come in pairs, with */ + OP_PLUS, /* the minimizing one second. */ + OP_MINPLUS, /* This first set applies to single characters */ + OP_QUERY, + OP_MINQUERY, + OP_UPTO, /* From 0 to n matches */ + OP_MINUPTO, + OP_EXACT, /* Exactly n matches */ + + OP_NOTSTAR, /* The maximizing and minimizing versions of */ + OP_NOTMINSTAR, /* all these opcodes must come in pairs, with */ + OP_NOTPLUS, /* the minimizing one second. */ + OP_NOTMINPLUS, /* This first set applies to "not" single characters */ + OP_NOTQUERY, + OP_NOTMINQUERY, + OP_NOTUPTO, /* From 0 to n matches */ + OP_NOTMINUPTO, + OP_NOTEXACT, /* Exactly n matches */ + + OP_TYPESTAR, /* The maximizing and minimizing versions of */ + OP_TYPEMINSTAR, /* all these opcodes must come in pairs, with */ + OP_TYPEPLUS, /* the minimizing one second. These codes must */ + OP_TYPEMINPLUS, /* be in exactly the same order as those above. */ + OP_TYPEQUERY, /* This set applies to character types such as \d */ + OP_TYPEMINQUERY, + OP_TYPEUPTO, /* From 0 to n matches */ + OP_TYPEMINUPTO, + OP_TYPEEXACT, /* Exactly n matches */ + + OP_CRSTAR, /* The maximizing and minimizing versions of */ + OP_CRMINSTAR, /* all these opcodes must come in pairs, with */ + OP_CRPLUS, /* the minimizing one second. These codes must */ + OP_CRMINPLUS, /* be in exactly the same order as those above. */ + OP_CRQUERY, /* These are for character classes and back refs */ + OP_CRMINQUERY, + OP_CRRANGE, /* These are different to the three seta above. */ + OP_CRMINRANGE, + + OP_CLASS, /* Match a character class */ + OP_NEGCLASS, /* Match a character class, specified negatively */ + OP_CLASS_L, /* Match a character class */ + OP_REF, /* Match a back reference */ + + OP_ALT, /* Start of alternation */ + OP_KET, /* End of group that doesn't have an unbounded repeat */ + OP_KETRMAX, /* These two must remain together and in this */ + OP_KETRMIN, /* order. They are for groups the repeat for ever. */ + + OP_ASSERT, + OP_ASSERT_NOT, + OP_ONCE, /* Once matched, don't back up into the subpattern */ + + OP_BRAZERO, /* These two must remain together and in this */ + OP_BRAMINZERO, /* order. */ + + OP_BRA /* This and greater values are used for brackets that + extract substrings. */ +}; + +/* The highest extraction number. This is limited by the number of opcodes +left after OP_BRA, i.e. 255 - OP_BRA. We actually set it somewhat lower. */ + +#define EXTRACT_MAX 99 + +/* The texts of compile-time error messages are defined as macros here so that +they can be accessed by the POSIX wrapper and converted into error codes. Yes, +I could have used error codes in the first place, but didn't feel like changing +just to accommodate the POSIX wrapper. */ + +#define ERR1 "\\ at end of pattern" +#define ERR2 "\\c at end of pattern" +#define ERR3 "unrecognized character follows \\" +#define ERR4 "numbers out of order in {} quantifier" +#define ERR5 "number too big in {} quantifier" +#define ERR6 "missing terminating ] for character class" +#define ERR7 "invalid escape sequence in character class" +#define ERR8 "range out of order in character class" +#define ERR9 "nothing to repeat" +#define ERR10 "operand of unlimited repeat could match the empty string" +#define ERR11 "internal error: unexpected repeat" +#define ERR12 "unrecognized character after (?" +#define ERR13 "too many capturing parenthesized sub-patterns" +#define ERR14 "missing )" +#define ERR15 "back reference to non-existent subpattern" +#define ERR16 "erroffset passed as NULL" +#define ERR17 "unknown option bit(s) set" +#define ERR18 "missing ) after comment" +#define ERR19 "too many sets of parentheses" +#define ERR20 "regular expression too large" +#define ERR21 "failed to get memory" +#define ERR22 "unmatched brackets" +#define ERR23 "internal error: code overflow" + +/* All character handling must be done as unsigned characters. Otherwise there +are problems with top-bit-set characters and functions such as isspace(). +However, we leave the interface to the outside world as char *, because that +should make things easier for callers. We define a short type for unsigned char +to save lots of typing. I tried "uchar", but it causes problems on Digital +Unix, where it is defined in sys/types, so use "uschar" instead. */ + +typedef unsigned char uschar; + +/* The real format of the start of the pcre block; the actual code vector +runs on as long as necessary after the end. */ + +typedef struct real_pcre { + unsigned int magic_number; + unsigned short int options; + unsigned char top_bracket; + unsigned char top_backref; + unsigned char first_char; + unsigned char code[1]; +} real_pcre; + +/* The real format of the extra block returned by pcre_study(). */ + +typedef struct real_pcre_extra { + unsigned char options; + unsigned char start_bits[32]; +} real_pcre_extra; + +/* Global tables from chartables.c */ + +extern uschar pcre_lcc[]; +extern uschar pcre_fcc[]; +extern uschar pcre_cbits[]; +extern uschar pcre_ctypes[]; + +/* Bit definitions for entries in pcre_ctypes[]. */ + +#define ctype_space 0x01 +#define ctype_letter 0x02 +#define ctype_digit 0x04 +#define ctype_xdigit 0x08 +#define ctype_word 0x10 /* alphameric or '_' */ +#define ctype_odigit 0x20 /* octal digit */ +#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ + +/* Offsets for the bitmap tables */ + +#define cbit_digit 0 +#define cbit_letter 32 +#define cbit_word 64 +#define cbit_space 96 +#define cbit_length 128 /* Length of the cbits table */ + +/* End of internal.h */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcre.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcre.h new file mode 100644 index 00000000..1f0eb84a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcre.h @@ -0,0 +1,84 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* Copyright (c) 1998 University of Cambridge */ + +#ifndef _PCRE_H +#define _PCRE_H + +#ifdef FOR_PYTHON +#include "Python.h" +#endif + +/* Have to include stdlib.h in order to ensure that size_t is defined; +it is needed here for malloc. */ + +#ifndef DONT_HAVE_SYS_TYPES_H +#include +#endif +#include + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options */ + +#define PCRE_CASELESS 0x0001 +#define PCRE_EXTENDED 0x0002 +#define PCRE_ANCHORED 0x0004 +#define PCRE_MULTILINE 0x0008 +#define PCRE_DOTALL 0x0010 +#define PCRE_DOLLAR_ENDONLY 0x0020 +#define PCRE_EXTRA 0x0040 +#define PCRE_NOTBOL 0x0080 +#define PCRE_NOTEOL 0x0100 +#define PCRE_UNGREEDY 0x0400 +#ifdef FOR_PYTHON +#define PCRE_LOCALE 0x0200 +#endif + +/* Exec-time error codes */ + +#define PCRE_ERROR_NOMATCH (-1) +#define PCRE_ERROR_BADREF (-2) +#define PCRE_ERROR_NULL (-3) +#define PCRE_ERROR_BADOPTION (-4) +#define PCRE_ERROR_BADMAGIC (-5) +#define PCRE_ERROR_UNKNOWN_NODE (-6) +#define PCRE_ERROR_NOMEMORY (-7) + +/* Types */ + +typedef void pcre; +typedef void pcre_extra; + +/* Store get and free functions. These can be set to alternative malloc/free +functions if required. */ + +extern void *(*pcre_malloc)(size_t); +extern void (*pcre_free)(void *); + +/* Functions */ + +#ifdef FOR_PYTHON +extern pcre *pcre_compile(const char *, int, const char **, int *, PyObject *); +extern int pcre_exec(const pcre *, const pcre_extra *, const char *, + int, int, int, int *, int); +#else +extern pcre *pcre_compile(const char *, int, const char **, int *); +extern int pcre_exec(const pcre *, const pcre_extra *, const char *, + int, int, int *, int); +#endif +extern int pcre_info(const pcre *, int *, int *); +extern pcre_extra *pcre_study(const pcre *, int, const char **); +extern const char *pcre_version(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* End of pcre.h */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcremodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcremodule.c new file mode 100644 index 00000000..b628830d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pcremodule.c @@ -0,0 +1,636 @@ +/* Pcre objects */ + +#include "Python.h" + +#ifndef Py_eval_input +/* For Python 1.4, graminit.h has to be explicitly included */ +#include "graminit.h" +#define Py_eval_input eval_input +#endif + +#ifndef FOR_PYTHON +#define FOR_PYTHON +#endif + +#include "pcre.h" +#include "pcre-int.h" + +static PyObject *ErrorObject; + +typedef struct { + PyObject_HEAD + pcre *regex; + pcre_extra *regex_extra; + int num_groups; +} PcreObject; + +static PyTypeObject Pcre_Type; + +#define PcreObject_Check(v) ((v)->ob_type == &Pcre_Type) +#define NORMAL 0 +#define CHARCLASS 1 +#define REPLACEMENT 2 + +#define CHAR 0 +#define MEMORY_REFERENCE 1 +#define SYNTAX 2 +#define NOT_SYNTAX 3 +#define SET 4 +#define WORD_BOUNDARY 5 +#define NOT_WORD_BOUNDARY 6 +#define BEGINNING_OF_BUFFER 7 +#define END_OF_BUFFER 8 +#define STRING 9 + +static PcreObject * +newPcreObject(PyObject *args) +{ + PcreObject *self; + self = PyObject_New(PcreObject, &Pcre_Type); + if (self == NULL) + return NULL; + self->regex = NULL; + self->regex_extra = NULL; + return self; +} + +/* Pcre methods */ + +static void +PyPcre_dealloc(PcreObject *self) +{ + if (self->regex) (pcre_free)(self->regex); + if (self->regex_extra) (pcre_free)(self->regex_extra); + PyObject_Del(self); +} + + +static PyObject * +PyPcre_exec(PcreObject *self, PyObject *args) +{ + char *string; + int stringlen, pos = 0, options=0, endpos = -1, i, count; + int offsets[100*2]; + PyObject *list; + + if (!PyArg_ParseTuple(args, "t#|iii:match", &string, &stringlen, + &pos, &endpos, &options)) + return NULL; + if (endpos == -1) {endpos = stringlen;} + count = pcre_exec(self->regex, self->regex_extra, + string, endpos, pos, options, + offsets, sizeof(offsets)/sizeof(int) ); + /* If an error occurred during the match, and an exception was raised, + just return NULL and leave the exception alone. The most likely + problem to cause this would be running out of memory for + the failure stack. */ + if (PyErr_Occurred()) + { + return NULL; + } + if (count==PCRE_ERROR_NOMATCH) {Py_INCREF(Py_None); return Py_None;} + if (count<0) + { + PyObject *errval = Py_BuildValue("si", "Regex execution error", count); + PyErr_SetObject(ErrorObject, errval); + Py_XDECREF(errval); + return NULL; + } + + list=PyList_New(self->num_groups+1); + if (list==NULL) return NULL; + for(i=0; i<=self->num_groups; i++) + { + PyObject *v; + int start=offsets[i*2], end=offsets[i*2+1]; + /* If the group wasn't affected by the match, return -1, -1 */ + if (start<0 || count<=i) + {start=end=-1;} + v=Py_BuildValue("ii", start, end); + if (v==NULL) {Py_DECREF(list); return NULL;} + PyList_SetItem(list, i, v); + } + return list; +} + +static PyMethodDef Pcre_methods[] = { + {"match", (PyCFunction)PyPcre_exec, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +PyPcre_getattr(PcreObject *self, char *name) +{ + return Py_FindMethod(Pcre_methods, (PyObject *)self, name); +} + + +static PyTypeObject Pcre_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "pcre.Pcre", /*tp_name*/ + sizeof(PcreObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyPcre_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)PyPcre_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; +/* --------------------------------------------------------------------- */ + +static PyObject * +PyPcre_compile(PyObject *self, PyObject *args) +{ + PcreObject *rv; + PyObject *dictionary; + char *pattern; + const char *error; + + int options, erroroffset; + if (!PyArg_ParseTuple(args, "siO!:pcre_compile", &pattern, &options, + &PyDict_Type, &dictionary)) + return NULL; + rv = newPcreObject(args); + if ( rv == NULL ) + return NULL; + + rv->regex = pcre_compile((char*)pattern, options, + &error, &erroroffset, dictionary); + if (rv->regex==NULL) + { + Py_DECREF(rv); + if (!PyErr_Occurred()) + { + PyObject *errval = Py_BuildValue("si", error, erroroffset); + PyErr_SetObject(ErrorObject, errval); + Py_XDECREF(errval); + } + return NULL; + } + rv->regex_extra=pcre_study(rv->regex, 0, &error); + if (rv->regex_extra==NULL && error!=NULL) + { + PyObject *errval = Py_BuildValue("si", error, 0); + Py_DECREF(rv); + PyErr_SetObject(ErrorObject, errval); + Py_XDECREF(errval); + return NULL; + } + rv->num_groups = pcre_info(rv->regex, NULL, NULL); + if (rv->num_groups<0) + { + PyObject *errval = Py_BuildValue("si", error, rv->num_groups); + PyErr_SetObject(ErrorObject, errval); + Py_XDECREF(errval); + Py_DECREF(rv); + return NULL; + } + return (PyObject *)rv; +} + +static PyObject * +PyPcre_expand_escape(unsigned char *pattern, int pattern_len, + int *indexptr, int *typeptr) +{ + unsigned char c; + int index = *indexptr; + + if (pattern_len<=index) + { + PyErr_SetString(ErrorObject, "escape ends too soon"); + return NULL; + } + c=pattern[index]; index++; + *typeptr=CHAR; + + switch (c) + { + case('t'): + *indexptr=index; + return Py_BuildValue("c", (char)9); + case('n'): + *indexptr = index; + return Py_BuildValue("c", (char)10); + case('v'): + *indexptr = index; + return Py_BuildValue("c", (char)11); + case('r'): + *indexptr = index; + return Py_BuildValue("c", (char)13); + case('f'): + *indexptr = index; + return Py_BuildValue("c", (char)12); + case('a'): + *indexptr = index; + return Py_BuildValue("c", (char)7); + case('b'): + *indexptr=index; + return Py_BuildValue("c", (char)8); + case('\\'): + *indexptr=index; + return Py_BuildValue("c", '\\'); + + case('x'): + { + int x, ch, end; + + x = 0; end = index; + while ( (end starting with digit"); + return NULL; + } + else {group_num = group_num * 10 + pattern[i] - '0';} + if (!(pcre_ctypes[pattern[i]] & ctype_word) ) + { + /* XXX should include the text of the reference */ + PyErr_SetString(ErrorObject, "illegal symbolic reference"); + return NULL; + } + } + + *typeptr = MEMORY_REFERENCE; + *indexptr = end+1; + /* If it's a number, return the integer value of the group */ + if (is_number) return Py_BuildValue("i", group_num); + /* Otherwise, return a string containing the group name */ + return Py_BuildValue("s#", pattern+index, end-index); + } + + case('0'): + { + /* \0 always indicates an octal escape, so we consume up to 3 + characters, as long as they're all octal digits */ + int octval=0, i; + index--; + for(i=index; + i<=index+2 && i255) + { + PyErr_SetString(ErrorObject, "octal value out of range"); + return NULL; + } + *indexptr = i; + return Py_BuildValue("c", (unsigned char)octval); + } + + case('1'): case('2'): case('3'): case('4'): + case('5'): case('6'): case('7'): case('8'): + case('9'): + { + /* Handle \?, where ? is from 1 through 9 */ + int value=0; + index--; + /* If it's at least a two-digit reference, like \34, it might + either be a 3-digit octal escape (\123) or a 2-digit + decimal memory reference (\34) */ + + if ( (index+1) 255) + { + PyErr_SetString(ErrorObject, "octal value out of range"); + return NULL; + } + *indexptr = index+3; + return Py_BuildValue("c", (unsigned char)value); + } + else + { + /* 2-digit form, so it's a memory reference */ + value= 10*(pattern[index ]-'0') + + (pattern[index+1]-'0'); + if (value<1 || EXTRACT_MAX<=value) + { + PyErr_SetString(ErrorObject, "memory reference out of range"); + return NULL; + } + *typeptr = MEMORY_REFERENCE; + *indexptr = index+2; + return Py_BuildValue("i", value); + } + } + else + { + /* Single-digit form, like \2, so it's a memory reference */ + *typeptr = MEMORY_REFERENCE; + *indexptr = index+1; + return Py_BuildValue("i", pattern[index]-'0'); + } + } + + default: + /* It's some unknown escape like \s, so return a string containing + \s */ + *typeptr = STRING; + *indexptr = index; + return Py_BuildValue("s#", pattern+index-2, 2); + } +} + +static PyObject * +PyPcre_expand(PyObject *self, PyObject *args) +{ + PyObject *results, *match_obj; + PyObject *repl_obj, *newstring; + unsigned char *repl; + int size, total_len, i, start, pos; + + if (!PyArg_ParseTuple(args, "OS:pcre_expand", &match_obj, &repl_obj)) + return NULL; + + repl=(unsigned char *)PyString_AsString(repl_obj); + size=PyString_Size(repl_obj); + results=PyList_New(0); + if (results==NULL) return NULL; + for(start=total_len=i=0; i +#endif /* defined(__VMS) */ + +PyDoc_STRVAR(posix__doc__, +"This module provides access to operating system functionality that is\n\ +standardized by the C Standard and the POSIX standard (a thinly\n\ +disguised Unix interface). Refer to the library manual and\n\ +corresponding Unix manual entries for more information on calls."); + +#ifndef Py_USING_UNICODE +/* This is used in signatures of functions. */ +#define Py_UNICODE void +#endif + +#if defined(PYOS_OS2) +#define INCL_DOS +#define INCL_DOSERRORS +#define INCL_DOSPROCESS +#define INCL_NOPMAPI +#include +#if defined(PYCC_GCC) +#include +#include +#include +#include +#include "osdefs.h" +#endif +#endif + +#include +#include + +#ifdef HAVE_SYS_WAIT_H +#include /* For WNOHANG */ +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#ifdef HAVE_GRP_H +#include +#endif + +#ifdef HAVE_SYSEXITS_H +#include +#endif /* HAVE_SYSEXITS_H */ + +/* Various compilers have only certain posix functions */ +/* XXX Gosh I wish these were all moved into pyconfig.h */ +#if defined(PYCC_VACPP) && defined(PYOS_OS2) +#include +#else +#if defined(__WATCOMC__) && !defined(__QNX__) /* Watcom compiler */ +#define HAVE_GETCWD 1 +#define HAVE_OPENDIR 1 +#define HAVE_SYSTEM 1 +#if defined(__OS2__) +#define HAVE_EXECV 1 +#define HAVE_WAIT 1 +#endif +#include +#else +#ifdef __BORLANDC__ /* Borland compiler */ +#define HAVE_EXECV 1 +#define HAVE_GETCWD 1 +#define HAVE_OPENDIR 1 +#define HAVE_PIPE 1 +#define HAVE_POPEN 1 +#define HAVE_SYSTEM 1 +#define HAVE_WAIT 1 +#else +#ifdef _MSC_VER /* Microsoft compiler */ +#ifndef MS_XBOX +# define HAVE_GETCWD 1 +# define HAVE_SPAWNV 1 +# define HAVE_EXECV 1 +# define HAVE_SYSTEM 1 +# define HAVE_CWAIT 1 +#endif +#define HAVE_PIPE 1 +#define HAVE_POPEN 1 +#define HAVE_FSYNC 1 +#define fsync _commit +#else +#if defined(PYOS_OS2) && defined(PYCC_GCC) || defined(__VMS) +/* Everything needed is defined in PC/os2emx/pyconfig.h or vms/pyconfig.h */ +#else /* all other compilers */ +/* Unix functions that the configure script doesn't check for */ +#define HAVE_EXECV 1 +#define HAVE_FORK 1 +#if defined(__USLC__) && defined(__SCO_VERSION__) /* SCO UDK Compiler */ +#define HAVE_FORK1 1 +#endif +#define HAVE_GETCWD 1 +#define HAVE_GETEGID 1 +#define HAVE_GETEUID 1 +#define HAVE_GETGID 1 +#define HAVE_GETPPID 1 +#define HAVE_GETUID 1 +#define HAVE_KILL 1 +#define HAVE_OPENDIR 1 +#define HAVE_PIPE 1 +#define HAVE_POPEN 1 +#define HAVE_SYSTEM 1 +#define HAVE_WAIT 1 +#define HAVE_TTYNAME 1 +#endif /* PYOS_OS2 && PYCC_GCC && __VMS */ +#endif /* _MSC_VER */ +#endif /* __BORLANDC__ */ +#endif /* ! __WATCOMC__ || __QNX__ */ +#endif /* ! __IBMC__ */ + +#ifndef _MSC_VER + +#if defined(sun) && !defined(__SVR4) +/* SunOS 4.1.4 doesn't have prototypes for these: */ +extern int rename(const char *, const char *); +extern int pclose(FILE *); +extern int fclose(FILE *); +extern int fsync(int); +extern int lstat(const char *, struct stat *); +extern int symlink(const char *, const char *); +#endif + +#if defined(__sgi)&&_COMPILER_VERSION>=700 +/* declare ctermid_r if compiling with MIPSPro 7.x in ANSI C mode + (default) */ +extern char *ctermid_r(char *); +#endif + +#ifndef HAVE_UNISTD_H +#if defined(PYCC_VACPP) +extern int mkdir(char *); +#else +#if ( defined(__WATCOMC__) || defined(_MSC_VER) ) && !defined(__QNX__) +extern int mkdir(const char *); +#else +extern int mkdir(const char *, mode_t); +#endif +#endif +#if defined(__IBMC__) || defined(__IBMCPP__) +extern int chdir(char *); +extern int rmdir(char *); +#else +extern int chdir(const char *); +extern int rmdir(const char *); +#endif +#ifdef __BORLANDC__ +extern int chmod(const char *, int); +#else +extern int chmod(const char *, mode_t); +#endif +extern int chown(const char *, uid_t, gid_t); +extern char *getcwd(char *, int); +extern char *strerror(int); +extern int link(const char *, const char *); +extern int rename(const char *, const char *); +extern int stat(const char *, struct stat *); +extern int unlink(const char *); +extern int pclose(FILE *); +#ifdef HAVE_SYMLINK +extern int symlink(const char *, const char *); +#endif /* HAVE_SYMLINK */ +#ifdef HAVE_LSTAT +extern int lstat(const char *, struct stat *); +#endif /* HAVE_LSTAT */ +#endif /* !HAVE_UNISTD_H */ + +#endif /* !_MSC_VER */ + +#ifdef HAVE_UTIME_H +#include +#endif /* HAVE_UTIME_H */ + +#ifdef HAVE_SYS_UTIME_H +#include +#define HAVE_UTIME_H /* pretend we do for the rest of this file */ +#endif /* HAVE_SYS_UTIME_H */ + +#ifdef HAVE_SYS_TIMES_H +#include +#endif /* HAVE_SYS_TIMES_H */ + +#ifdef HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#ifdef HAVE_SYS_UTSNAME_H +#include +#endif /* HAVE_SYS_UTSNAME_H */ + +#ifdef HAVE_DIRENT_H +#include +#define NAMLEN(dirent) strlen((dirent)->d_name) +#else +#if defined(__WATCOMC__) && !defined(__QNX__) +#include +#define NAMLEN(dirent) strlen((dirent)->d_name) +#else +#define dirent direct +#define NAMLEN(dirent) (dirent)->d_namlen +#endif +#ifdef HAVE_SYS_NDIR_H +#include +#endif +#ifdef HAVE_SYS_DIR_H +#include +#endif +#ifdef HAVE_NDIR_H +#include +#endif +#endif + +#ifdef _MSC_VER +#include +#include +#include +#include "osdefs.h" +#define WIN32_LEAN_AND_MEAN +#ifdef MS_XBOX +#include +#else +#include +#include /* for ShellExecute() */ +#endif +#define popen _popen +#define pclose _pclose +#endif /* _MSC_VER */ + +#if defined(PYCC_VACPP) && defined(PYOS_OS2) +#include +#endif /* OS2 */ + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif /* MAXPATHLEN */ + +#ifdef UNION_WAIT +/* Emulate some macros on systems that have a union instead of macros */ + +#ifndef WIFEXITED +#define WIFEXITED(u_wait) (!(u_wait).w_termsig && !(u_wait).w_coredump) +#endif + +#ifndef WEXITSTATUS +#define WEXITSTATUS(u_wait) (WIFEXITED(u_wait)?((u_wait).w_retcode):-1) +#endif + +#ifndef WTERMSIG +#define WTERMSIG(u_wait) ((u_wait).w_termsig) +#endif + +#endif /* UNION_WAIT */ + +/* Don't use the "_r" form if we don't need it (also, won't have a + prototype for it, at least on Solaris -- maybe others as well?). */ +#if defined(HAVE_CTERMID_R) && defined(WITH_THREAD) +#define USE_CTERMID_R +#endif + +#if defined(HAVE_TMPNAM_R) && defined(WITH_THREAD) +#define USE_TMPNAM_R +#endif + +/* choose the appropriate stat and fstat functions and return structs */ +#undef STAT +#if defined(MS_WIN64) || defined(MS_WINDOWS) +# define STAT _stati64 +# define FSTAT _fstati64 +# define STRUCT_STAT struct _stati64 +#else +# define STAT stat +# define FSTAT fstat +# define STRUCT_STAT struct stat +#endif + +#if defined(MAJOR_IN_MKDEV) +#include +#else +#if defined(MAJOR_IN_SYSMACROS) +#include +#endif +#if defined(HAVE_MKNOD) && defined(HAVE_SYS_MKDEV_H) +#include +#endif +#endif + +/* Return a dictionary corresponding to the POSIX environment table */ +#ifdef WITH_NEXT_FRAMEWORK +/* On Darwin/MacOSX a shared library or framework has no access to +** environ directly, we must obtain it with _NSGetEnviron(). +*/ +#include +static char **environ; +#elif !defined(_MSC_VER) && ( !defined(__WATCOMC__) || defined(__QNX__) ) +extern char **environ; +#elif defined(MS_XBOX) +static char **environ = NULL; +#endif /* !_MSC_VER */ + +static PyObject * +convertenviron(void) +{ + PyObject *d; + char **e; + d = PyDict_New(); + if (d == NULL) + return NULL; +#ifdef WITH_NEXT_FRAMEWORK + if (environ == NULL) + environ = *_NSGetEnviron(); +#endif + if (environ == NULL) + return d; + /* This part ignores errors */ + for (e = environ; *e != NULL; e++) { + PyObject *k; + PyObject *v; + char *p = strchr(*e, '='); + if (p == NULL) + continue; + k = PyString_FromStringAndSize(*e, (int)(p-*e)); + if (k == NULL) { + PyErr_Clear(); + continue; + } + v = PyString_FromString(p+1); + if (v == NULL) { + PyErr_Clear(); + Py_DECREF(k); + continue; + } + if (PyDict_GetItem(d, k) == NULL) { + if (PyDict_SetItem(d, k, v) != 0) + PyErr_Clear(); + } + Py_DECREF(k); + Py_DECREF(v); + } +#if defined(PYOS_OS2) + { + APIRET rc; + char buffer[1024]; /* OS/2 Provides a Documented Max of 1024 Chars */ + + rc = DosQueryExtLIBPATH(buffer, BEGIN_LIBPATH); + if (rc == NO_ERROR) { /* (not a type, envname is NOT 'BEGIN_LIBPATH') */ + PyObject *v = PyString_FromString(buffer); + PyDict_SetItemString(d, "BEGINLIBPATH", v); + Py_DECREF(v); + } + rc = DosQueryExtLIBPATH(buffer, END_LIBPATH); + if (rc == NO_ERROR) { /* (not a typo, envname is NOT 'END_LIBPATH') */ + PyObject *v = PyString_FromString(buffer); + PyDict_SetItemString(d, "ENDLIBPATH", v); + Py_DECREF(v); + } + } +#endif + return d; +} + + +/* Set a POSIX-specific error from errno, and return NULL */ + +static PyObject * +posix_error(void) +{ + return PyErr_SetFromErrno(PyExc_OSError); +} +static PyObject * +posix_error_with_filename(char* name) +{ + return PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); +} + +#ifdef Py_WIN_WIDE_FILENAMES +static PyObject * +posix_error_with_unicode_filename(Py_UNICODE* name) +{ + return PyErr_SetFromErrnoWithUnicodeFilename(PyExc_OSError, name); +} +#endif /* Py_WIN_WIDE_FILENAMES */ + + +static PyObject * +posix_error_with_allocated_filename(char* name) +{ + PyObject *rc = PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); + PyMem_Free(name); + return rc; +} + +#ifdef MS_WINDOWS +static PyObject * +win32_error(char* function, char* filename) +{ + /* XXX We should pass the function name along in the future. + (_winreg.c also wants to pass the function name.) + This would however require an additional param to the + Windows error object, which is non-trivial. + */ + errno = GetLastError(); + if (filename) + return PyErr_SetFromWindowsErrWithFilename(errno, filename); + else + return PyErr_SetFromWindowsErr(errno); +} + +#ifdef Py_WIN_WIDE_FILENAMES +static PyObject * +win32_error_unicode(char* function, Py_UNICODE* filename) +{ + /* XXX - see win32_error for comments on 'function' */ + errno = GetLastError(); + if (filename) + return PyErr_SetFromWindowsErrWithUnicodeFilename(errno, filename); + else + return PyErr_SetFromWindowsErr(errno); +} + +static PyObject *_PyUnicode_FromFileSystemEncodedObject(register PyObject *obj) +{ + /* XXX Perhaps we should make this API an alias of + PyObject_Unicode() instead ?! */ + if (PyUnicode_CheckExact(obj)) { + Py_INCREF(obj); + return obj; + } + if (PyUnicode_Check(obj)) { + /* For a Unicode subtype that's not a Unicode object, + return a true Unicode object with the same data. */ + return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(obj), + PyUnicode_GET_SIZE(obj)); + } + return PyUnicode_FromEncodedObject(obj, + Py_FileSystemDefaultEncoding, + "strict"); +} + +#endif /* Py_WIN_WIDE_FILENAMES */ + +#endif + +#if defined(PYOS_OS2) +/********************************************************************** + * Helper Function to Trim and Format OS/2 Messages + **********************************************************************/ + static void +os2_formatmsg(char *msgbuf, int msglen, char *reason) +{ + msgbuf[msglen] = '\0'; /* OS/2 Doesn't Guarantee a Terminator */ + + if (strlen(msgbuf) > 0) { /* If Non-Empty Msg, Trim CRLF */ + char *lastc = &msgbuf[ strlen(msgbuf)-1 ]; + + while (lastc > msgbuf && isspace(*lastc)) + *lastc-- = '\0'; /* Trim Trailing Whitespace (CRLF) */ + } + + /* Add Optional Reason Text */ + if (reason) { + strcat(msgbuf, " : "); + strcat(msgbuf, reason); + } +} + +/********************************************************************** + * Decode an OS/2 Operating System Error Code + * + * A convenience function to lookup an OS/2 error code and return a + * text message we can use to raise a Python exception. + * + * Notes: + * The messages for errors returned from the OS/2 kernel reside in + * the file OSO001.MSG in the \OS2 directory hierarchy. + * + **********************************************************************/ + static char * +os2_strerror(char *msgbuf, int msgbuflen, int errorcode, char *reason) +{ + APIRET rc; + ULONG msglen; + + /* Retrieve Kernel-Related Error Message from OSO001.MSG File */ + Py_BEGIN_ALLOW_THREADS + rc = DosGetMessage(NULL, 0, msgbuf, msgbuflen, + errorcode, "oso001.msg", &msglen); + Py_END_ALLOW_THREADS + + if (rc == NO_ERROR) + os2_formatmsg(msgbuf, msglen, reason); + else + PyOS_snprintf(msgbuf, msgbuflen, + "unknown OS error #%d", errorcode); + + return msgbuf; +} + +/* Set an OS/2-specific error and return NULL. OS/2 kernel + errors are not in a global variable e.g. 'errno' nor are + they congruent with posix error numbers. */ + +static PyObject * os2_error(int code) +{ + char text[1024]; + PyObject *v; + + os2_strerror(text, sizeof(text), code, ""); + + v = Py_BuildValue("(is)", code, text); + if (v != NULL) { + PyErr_SetObject(PyExc_OSError, v); + Py_DECREF(v); + } + return NULL; /* Signal to Python that an Exception is Pending */ +} + +#endif /* OS2 */ + +/* POSIX generic methods */ + +static PyObject * +posix_fildes(PyObject *fdobj, int (*func)(int)) +{ + int fd; + int res; + fd = PyObject_AsFileDescriptor(fdobj); + if (fd < 0) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = (*func)(fd); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef Py_WIN_WIDE_FILENAMES +static int +unicode_file_names(void) +{ + static int canusewide = -1; + if (canusewide == -1) { + /* As per doc for ::GetVersion(), this is the correct test for + the Windows NT family. */ + canusewide = (GetVersion() < 0x80000000) ? 1 : 0; + } + return canusewide; +} +#endif + +static PyObject * +posix_1str(PyObject *args, char *format, int (*func)(const char*), + char *wformat, int (*wfunc)(const Py_UNICODE*)) +{ + char *path1 = NULL; + int res; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, wformat, &po)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + res = (*wfunc)(PyUnicode_AS_UNICODE(po)); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_unicode_filename(PyUnicode_AS_UNICODE(po)); + Py_INCREF(Py_None); + return Py_None; + } + /* Drop the argument parsing error as narrow + strings are also valid. */ + PyErr_Clear(); + } +#else + /* Platforms that don't support Unicode filenames + shouldn't be passing these extra params */ + assert(wformat==NULL && wfunc == NULL); +#endif + + if (!PyArg_ParseTuple(args, format, + Py_FileSystemDefaultEncoding, &path1)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = (*func)(path1); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path1); + PyMem_Free(path1); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +posix_2str(PyObject *args, + char *format, + int (*func)(const char *, const char *), + char *wformat, + int (*wfunc)(const Py_UNICODE *, const Py_UNICODE *)) +{ + char *path1 = NULL, *path2 = NULL; + int res; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyObject *po1; + PyObject *po2; + if (PyArg_ParseTuple(args, wformat, &po1, &po2)) { + if (PyUnicode_Check(po1) || PyUnicode_Check(po2)) { + PyObject *wpath1; + PyObject *wpath2; + wpath1 = _PyUnicode_FromFileSystemEncodedObject(po1); + wpath2 = _PyUnicode_FromFileSystemEncodedObject(po2); + if (!wpath1 || !wpath2) { + Py_XDECREF(wpath1); + Py_XDECREF(wpath2); + return NULL; + } + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + res = (*wfunc)(PyUnicode_AS_UNICODE(wpath1), + PyUnicode_AS_UNICODE(wpath2)); + Py_END_ALLOW_THREADS + Py_XDECREF(wpath1); + Py_XDECREF(wpath2); + if (res != 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; + } + /* Else flow through as neither is Unicode. */ + } + /* Drop the argument parsing error as narrow + strings are also valid. */ + PyErr_Clear(); + } +#else + /* Platforms that don't support Unicode filenames + shouldn't be passing these extra params */ + assert(wformat==NULL && wfunc == NULL); +#endif + + if (!PyArg_ParseTuple(args, format, + Py_FileSystemDefaultEncoding, &path1, + Py_FileSystemDefaultEncoding, &path2)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = (*func)(path1, path2); + Py_END_ALLOW_THREADS + PyMem_Free(path1); + PyMem_Free(path2); + if (res != 0) + /* XXX how to report both path1 and path2??? */ + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(stat_result__doc__, +"stat_result: Result from stat or lstat.\n\n\ +This object may be accessed either as a tuple of\n\ + (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)\n\ +or via the attributes st_mode, st_ino, st_dev, st_nlink, st_uid, and so on.\n\ +\n\ +Posix/windows: If your platform supports st_blksize, st_blocks, or st_rdev,\n\ +they are available as attributes only.\n\ +\n\ +See os.stat for more information."); + +static PyStructSequence_Field stat_result_fields[] = { + {"st_mode", "protection bits"}, + {"st_ino", "inode"}, + {"st_dev", "device"}, + {"st_nlink", "number of hard links"}, + {"st_uid", "user ID of owner"}, + {"st_gid", "group ID of owner"}, + {"st_size", "total size, in bytes"}, + /* The NULL is replaced with PyStructSequence_UnnamedField later. */ + {NULL, "integer time of last access"}, + {NULL, "integer time of last modification"}, + {NULL, "integer time of last change"}, + {"st_atime", "time of last access"}, + {"st_mtime", "time of last modification"}, + {"st_ctime", "time of last change"}, +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + {"st_blksize", "blocksize for filesystem I/O"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + {"st_blocks", "number of blocks allocated"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + {"st_rdev", "device type (if inode device)"}, +#endif + {0} +}; + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE +#define ST_BLKSIZE_IDX 13 +#else +#define ST_BLKSIZE_IDX 12 +#endif + +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS +#define ST_BLOCKS_IDX (ST_BLKSIZE_IDX+1) +#else +#define ST_BLOCKS_IDX ST_BLKSIZE_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_RDEV +#define ST_RDEV_IDX (ST_BLOCKS_IDX+1) +#else +#define ST_RDEV_IDX ST_BLOCKS_IDX +#endif + +static PyStructSequence_Desc stat_result_desc = { + "stat_result", /* name */ + stat_result__doc__, /* doc */ + stat_result_fields, + 10 +}; + +PyDoc_STRVAR(statvfs_result__doc__, +"statvfs_result: Result from statvfs or fstatvfs.\n\n\ +This object may be accessed either as a tuple of\n\ + (bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flag, namemax),\n\ +or via the attributes f_bsize, f_frsize, f_blocks, f_bfree, and so on.\n\ +\n\ +See os.statvfs for more information."); + +static PyStructSequence_Field statvfs_result_fields[] = { + {"f_bsize", }, + {"f_frsize", }, + {"f_blocks", }, + {"f_bfree", }, + {"f_bavail", }, + {"f_files", }, + {"f_ffree", }, + {"f_favail", }, + {"f_flag", }, + {"f_namemax",}, + {0} +}; + +static PyStructSequence_Desc statvfs_result_desc = { + "statvfs_result", /* name */ + statvfs_result__doc__, /* doc */ + statvfs_result_fields, + 10 +}; + +static PyTypeObject StatResultType; +static PyTypeObject StatVFSResultType; +static newfunc structseq_new; + +static PyObject * +statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyStructSequence *result; + int i; + + result = (PyStructSequence*)structseq_new(type, args, kwds); + if (!result) + return NULL; + /* If we have been initialized from a tuple, + st_?time might be set to None. Initialize it + from the int slots. */ + for (i = 7; i <= 9; i++) { + if (result->ob_item[i+3] == Py_None) { + Py_DECREF(Py_None); + Py_INCREF(result->ob_item[i]); + result->ob_item[i+3] = result->ob_item[i]; + } + } + return (PyObject*)result; +} + + + +/* If true, st_?time is float. */ +static int _stat_float_times = 0; + +PyDoc_STRVAR(stat_float_times__doc__, +"stat_float_times([newval]) -> oldval\n\n\ +Determine whether os.[lf]stat represents time stamps as float objects.\n\ +If newval is True, future calls to stat() return floats, if it is False,\n\ +future calls return ints. \n\ +If newval is omitted, return the current setting.\n"); + +static PyObject* +stat_float_times(PyObject* self, PyObject *args) +{ + int newval = -1; + if (!PyArg_ParseTuple(args, "|i:stat_float_times", &newval)) + return NULL; + if (newval == -1) + /* Return old value */ + return PyBool_FromLong(_stat_float_times); + _stat_float_times = newval; + Py_INCREF(Py_None); + return Py_None; +} + +static void +fill_time(PyObject *v, int index, time_t sec, unsigned long nsec) +{ + PyObject *fval,*ival; +#if SIZEOF_TIME_T > SIZEOF_LONG + ival = PyLong_FromLongLong((PY_LONG_LONG)sec); +#else + ival = PyInt_FromLong((long)sec); +#endif + if (_stat_float_times) { + fval = PyFloat_FromDouble(sec + 1e-9*nsec); + } else { + fval = ival; + Py_INCREF(fval); + } + PyStructSequence_SET_ITEM(v, index, ival); + PyStructSequence_SET_ITEM(v, index+3, fval); +} + +/* pack a system stat C structure into the Python stat tuple + (used by posix_stat() and posix_fstat()) */ +static PyObject* +_pystat_fromstructstat(STRUCT_STAT st) +{ + unsigned long ansec, mnsec, cnsec; + PyObject *v = PyStructSequence_New(&StatResultType); + if (v == NULL) + return NULL; + + PyStructSequence_SET_ITEM(v, 0, PyInt_FromLong((long)st.st_mode)); +#ifdef HAVE_LARGEFILE_SUPPORT + PyStructSequence_SET_ITEM(v, 1, + PyLong_FromLongLong((PY_LONG_LONG)st.st_ino)); +#else + PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long)st.st_ino)); +#endif +#if defined(HAVE_LONG_LONG) && !defined(MS_WINDOWS) + PyStructSequence_SET_ITEM(v, 2, + PyLong_FromLongLong((PY_LONG_LONG)st.st_dev)); +#else + PyStructSequence_SET_ITEM(v, 2, PyInt_FromLong((long)st.st_dev)); +#endif + PyStructSequence_SET_ITEM(v, 3, PyInt_FromLong((long)st.st_nlink)); + PyStructSequence_SET_ITEM(v, 4, PyInt_FromLong((long)st.st_uid)); + PyStructSequence_SET_ITEM(v, 5, PyInt_FromLong((long)st.st_gid)); +#ifdef HAVE_LARGEFILE_SUPPORT + PyStructSequence_SET_ITEM(v, 6, + PyLong_FromLongLong((PY_LONG_LONG)st.st_size)); +#else + PyStructSequence_SET_ITEM(v, 6, PyInt_FromLong(st.st_size)); +#endif + +#ifdef HAVE_STAT_TV_NSEC + ansec = st.st_atim.tv_nsec; + mnsec = st.st_mtim.tv_nsec; + cnsec = st.st_ctim.tv_nsec; +#else + ansec = mnsec = cnsec = 0; +#endif + fill_time(v, 7, st.st_atime, ansec); + fill_time(v, 8, st.st_mtime, mnsec); + fill_time(v, 9, st.st_ctime, cnsec); + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + PyStructSequence_SET_ITEM(v, ST_BLKSIZE_IDX, + PyInt_FromLong((long)st.st_blksize)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + PyStructSequence_SET_ITEM(v, ST_BLOCKS_IDX, + PyInt_FromLong((long)st.st_blocks)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + PyStructSequence_SET_ITEM(v, ST_RDEV_IDX, + PyInt_FromLong((long)st.st_rdev)); +#endif + + if (PyErr_Occurred()) { + Py_DECREF(v); + return NULL; + } + + return v; +} + +static PyObject * +posix_do_stat(PyObject *self, PyObject *args, + char *format, +#ifdef __VMS + int (*statfunc)(const char *, STRUCT_STAT *, ...), +#else + int (*statfunc)(const char *, STRUCT_STAT *), +#endif + char *wformat, + int (*wstatfunc)(const Py_UNICODE *, STRUCT_STAT *)) +{ + STRUCT_STAT st; + char *path = NULL; /* pass this to stat; do not free() it */ + char *pathfree = NULL; /* this memory must be free'd */ + int res; + +#ifdef MS_WINDOWS + int pathlen; + char pathcopy[MAX_PATH]; +#endif /* MS_WINDOWS */ + + +#ifdef Py_WIN_WIDE_FILENAMES + /* If on wide-character-capable OS see if argument + is Unicode and if so use wide API. */ + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, wformat, &po)) { + Py_UNICODE wpath[MAX_PATH+1]; + pathlen = wcslen(PyUnicode_AS_UNICODE(po)); + /* the library call can blow up if the file name is too long! */ + if (pathlen > MAX_PATH) { + errno = ENAMETOOLONG; + return posix_error(); + } + wcscpy(wpath, PyUnicode_AS_UNICODE(po)); + /* Remove trailing slash or backslash, unless it's the current + drive root (/ or \) or a specific drive's root (like c:\ or c:/). + */ + if (pathlen > 0 && + (wpath[pathlen-1]== L'\\' || wpath[pathlen-1] == L'/')) { + /* It does end with a slash -- exempt the root drive cases. */ + /* XXX UNC root drives should also be exempted? */ + if (pathlen == 1 || (pathlen == 3 && wpath[1] == L':')) + /* leave it alone */; + else { + /* nuke the trailing backslash */ + wpath[pathlen-1] = L'\0'; + } + } + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE result OK without + thread lock as it is a simple dereference. */ + res = wstatfunc(wpath, &st); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error_with_unicode_filename(wpath); + return _pystat_fromstructstat(st); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, format, + Py_FileSystemDefaultEncoding, &path)) + return NULL; + pathfree = path; + +#ifdef MS_WINDOWS + pathlen = strlen(path); + /* the library call can blow up if the file name is too long! */ + if (pathlen > MAX_PATH) { + PyMem_Free(pathfree); + errno = ENAMETOOLONG; + return posix_error(); + } + + /* Remove trailing slash or backslash, unless it's the current + drive root (/ or \) or a specific drive's root (like c:\ or c:/). + */ + if (pathlen > 0 && + (path[pathlen-1]== '\\' || path[pathlen-1] == '/')) { + /* It does end with a slash -- exempt the root drive cases. */ + /* XXX UNC root drives should also be exempted? */ + if (pathlen == 1 || (pathlen == 3 && path[1] == ':')) + /* leave it alone */; + else { + /* nuke the trailing backslash */ + strncpy(pathcopy, path, pathlen); + pathcopy[pathlen-1] = '\0'; + path = pathcopy; + } + } +#endif /* MS_WINDOWS */ + + Py_BEGIN_ALLOW_THREADS + res = (*statfunc)(path, &st); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error_with_allocated_filename(pathfree); + + PyMem_Free(pathfree); + return _pystat_fromstructstat(st); +} + + +/* POSIX methods */ + +PyDoc_STRVAR(posix_access__doc__, +"access(path, mode) -> 1 if granted, 0 otherwise\n\n\ +Use the real uid/gid to test for access to a path. Note that most\n\ +operations will use the effective uid/gid, therefore this routine can\n\ +be used in a suid/sgid environment to test if the invoking user has the\n\ +specified access to the path. The mode argument can be F_OK to test\n\ +existence, or the inclusive-OR of R_OK, W_OK, and X_OK."); + +static PyObject * +posix_access(PyObject *self, PyObject *args) +{ + char *path; + int mode; + int res; + + if (!PyArg_ParseTuple(args, "si:access", &path, &mode)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = access(path, mode); + Py_END_ALLOW_THREADS + return(PyBool_FromLong(res == 0)); +} + +#ifndef F_OK +#define F_OK 0 +#endif +#ifndef R_OK +#define R_OK 4 +#endif +#ifndef W_OK +#define W_OK 2 +#endif +#ifndef X_OK +#define X_OK 1 +#endif + +#ifdef HAVE_TTYNAME +PyDoc_STRVAR(posix_ttyname__doc__, +"ttyname(fd) -> string\n\n\ +Return the name of the terminal device connected to 'fd'."); + +static PyObject * +posix_ttyname(PyObject *self, PyObject *args) +{ + int id; + char *ret; + + if (!PyArg_ParseTuple(args, "i:ttyname", &id)) + return NULL; + +#if defined(__VMS) + /* file descriptor 0 only, the default input device (stdin) */ + if (id == 0) { + ret = ttyname(); + } + else { + ret = NULL; + } +#else + ret = ttyname(id); +#endif + if (ret == NULL) + return(posix_error()); + return(PyString_FromString(ret)); +} +#endif + +#ifdef HAVE_CTERMID +PyDoc_STRVAR(posix_ctermid__doc__, +"ctermid() -> string\n\n\ +Return the name of the controlling terminal for this process."); + +static PyObject * +posix_ctermid(PyObject *self, PyObject *noargs) +{ + char *ret; + char buffer[L_ctermid]; + +#ifdef USE_CTERMID_R + ret = ctermid_r(buffer); +#else + ret = ctermid(buffer); +#endif + if (ret == NULL) + return(posix_error()); + return(PyString_FromString(buffer)); +} +#endif + +PyDoc_STRVAR(posix_chdir__doc__, +"chdir(path)\n\n\ +Change the current working directory to the specified path."); + +static PyObject * +posix_chdir(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS +# ifdef MS_XBOX + Py_INCREF(Py_None); + return Py_None; +# else + return posix_1str(args, "et:chdir", chdir, "U:chdir", _wchdir); +# endif +#elif defined(PYOS_OS2) && defined(PYCC_GCC) + return posix_1str(args, "et:chdir", _chdir2, NULL, NULL); +#elif defined(__VMS) + return posix_1str(args, "et:chdir", (int (*)(const char *))chdir, + NULL, NULL); +#else + return posix_1str(args, "et:chdir", chdir, NULL, NULL); +#endif +} + +#ifdef HAVE_FCHDIR +PyDoc_STRVAR(posix_fchdir__doc__, +"fchdir(fildes)\n\n\ +Change to the directory of the given file descriptor. fildes must be\n\ +opened on a directory, not a file."); + +static PyObject * +posix_fchdir(PyObject *self, PyObject *fdobj) +{ + return posix_fildes(fdobj, fchdir); +} +#endif /* HAVE_FCHDIR */ + + +PyDoc_STRVAR(posix_chmod__doc__, +"chmod(path, mode)\n\n\ +Change the access permissions of a file."); + +static PyObject * +posix_chmod(PyObject *self, PyObject *args) +{ + char *path = NULL; + int i; + int res; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "Ui|:chmod", &po, &i)) { + Py_BEGIN_ALLOW_THREADS + res = _wchmod(PyUnicode_AS_UNICODE(po), i); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_unicode_filename( + PyUnicode_AS_UNICODE(po)); + Py_INCREF(Py_None); + return Py_None; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif /* Py_WIN_WIDE_FILENAMES */ + if (!PyArg_ParseTuple(args, "eti:chmod", Py_FileSystemDefaultEncoding, + &path, &i)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = chmod(path, i); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} + + +#ifdef HAVE_CHROOT +PyDoc_STRVAR(posix_chroot__doc__, +"chroot(path)\n\n\ +Change root directory to path."); + +static PyObject * +posix_chroot(PyObject *self, PyObject *args) +{ + return posix_1str(args, "et:chroot", chroot, NULL, NULL); +} +#endif + +#ifdef HAVE_FSYNC +PyDoc_STRVAR(posix_fsync__doc__, +"fsync(fildes)\n\n\ +force write of file with filedescriptor to disk."); + +static PyObject * +posix_fsync(PyObject *self, PyObject *fdobj) +{ + return posix_fildes(fdobj, fsync); +} +#endif /* HAVE_FSYNC */ + +#ifdef HAVE_FDATASYNC + +#ifdef __hpux +extern int fdatasync(int); /* On HP-UX, in libc but not in unistd.h */ +#endif + +PyDoc_STRVAR(posix_fdatasync__doc__, +"fdatasync(fildes)\n\n\ +force write of file with filedescriptor to disk.\n\ + does not force update of metadata."); + +static PyObject * +posix_fdatasync(PyObject *self, PyObject *fdobj) +{ + return posix_fildes(fdobj, fdatasync); +} +#endif /* HAVE_FDATASYNC */ + + +#ifdef HAVE_CHOWN +PyDoc_STRVAR(posix_chown__doc__, +"chown(path, uid, gid)\n\n\ +Change the owner and group id of path to the numeric uid and gid."); + +static PyObject * +posix_chown(PyObject *self, PyObject *args) +{ + char *path = NULL; + int uid, gid; + int res; + if (!PyArg_ParseTuple(args, "etii:chown", + Py_FileSystemDefaultEncoding, &path, + &uid, &gid)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = chown(path, (uid_t) uid, (gid_t) gid); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_CHOWN */ + +#ifdef HAVE_LCHOWN +PyDoc_STRVAR(posix_lchown__doc__, +"lchown(path, uid, gid)\n\n\ +Change the owner and group id of path to the numeric uid and gid.\n\ +This function will not follow symbolic links."); + +static PyObject * +posix_lchown(PyObject *self, PyObject *args) +{ + char *path = NULL; + int uid, gid; + int res; + if (!PyArg_ParseTuple(args, "etii:lchown", + Py_FileSystemDefaultEncoding, &path, + &uid, &gid)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = lchown(path, (uid_t) uid, (gid_t) gid); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_LCHOWN */ + + +#ifdef HAVE_GETCWD +PyDoc_STRVAR(posix_getcwd__doc__, +"getcwd() -> path\n\n\ +Return a string representing the current working directory."); + +static PyObject * +posix_getcwd(PyObject *self, PyObject *noargs) +{ + char buf[1026]; + char *res; + + Py_BEGIN_ALLOW_THREADS +#if defined(PYOS_OS2) && defined(PYCC_GCC) + res = _getcwd2(buf, sizeof buf); +#else + res = getcwd(buf, sizeof buf); +#endif + Py_END_ALLOW_THREADS + if (res == NULL) + return posix_error(); + return PyString_FromString(buf); +} + +#ifdef Py_USING_UNICODE +PyDoc_STRVAR(posix_getcwdu__doc__, +"getcwdu() -> path\n\n\ +Return a unicode string representing the current working directory."); + +static PyObject * +posix_getcwdu(PyObject *self, PyObject *noargs) +{ + char buf[1026]; + char *res; + +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + wchar_t *wres; + wchar_t wbuf[1026]; + Py_BEGIN_ALLOW_THREADS + wres = _wgetcwd(wbuf, sizeof wbuf/ sizeof wbuf[0]); + Py_END_ALLOW_THREADS + if (wres == NULL) + return posix_error(); + return PyUnicode_FromWideChar(wbuf, wcslen(wbuf)); + } +#endif + + Py_BEGIN_ALLOW_THREADS +#if defined(PYOS_OS2) && defined(PYCC_GCC) + res = _getcwd2(buf, sizeof buf); +#else + res = getcwd(buf, sizeof buf); +#endif + Py_END_ALLOW_THREADS + if (res == NULL) + return posix_error(); + return PyUnicode_Decode(buf, strlen(buf), Py_FileSystemDefaultEncoding,"strict"); +} +#endif +#endif + + +#ifdef HAVE_LINK +PyDoc_STRVAR(posix_link__doc__, +"link(src, dst)\n\n\ +Create a hard link to a file."); + +static PyObject * +posix_link(PyObject *self, PyObject *args) +{ + return posix_2str(args, "etet:link", link, NULL, NULL); +} +#endif /* HAVE_LINK */ + + +PyDoc_STRVAR(posix_listdir__doc__, +"listdir(path) -> list_of_strings\n\n\ +Return a list containing the names of the entries in the directory.\n\ +\n\ + path: path of directory to list\n\ +\n\ +The list is in arbitrary order. It does not include the special\n\ +entries '.' and '..' even if they are present in the directory."); + +static PyObject * +posix_listdir(PyObject *self, PyObject *args) +{ + /* XXX Should redo this putting the (now four) versions of opendir + in separate files instead of having them all here... */ +#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR) + + PyObject *d, *v; + HANDLE hFindFile; + WIN32_FIND_DATA FileData; + /* MAX_PATH characters could mean a bigger encoded string */ + char namebuf[MAX_PATH*2+5]; + char *bufptr = namebuf; + int len = sizeof(namebuf)/sizeof(namebuf[0]); + +#ifdef Py_WIN_WIDE_FILENAMES + /* If on wide-character-capable OS see if argument + is Unicode and if so use wide API. */ + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U:listdir", &po)) { + WIN32_FIND_DATAW wFileData; + Py_UNICODE wnamebuf[MAX_PATH*2+5]; + Py_UNICODE wch; + wcsncpy(wnamebuf, PyUnicode_AS_UNICODE(po), MAX_PATH); + wnamebuf[MAX_PATH] = L'\0'; + len = wcslen(wnamebuf); + wch = (len > 0) ? wnamebuf[len-1] : L'\0'; + if (wch != L'/' && wch != L'\\' && wch != L':') + wnamebuf[len++] = L'/'; + wcscpy(wnamebuf + len, L"*.*"); + if ((d = PyList_New(0)) == NULL) + return NULL; + hFindFile = FindFirstFileW(wnamebuf, &wFileData); + if (hFindFile == INVALID_HANDLE_VALUE) { + errno = GetLastError(); + if (errno == ERROR_FILE_NOT_FOUND) { + return d; + } + Py_DECREF(d); + return win32_error_unicode("FindFirstFileW", wnamebuf); + } + do { + if (wFileData.cFileName[0] == L'.' && + (wFileData.cFileName[1] == L'\0' || + wFileData.cFileName[1] == L'.' && + wFileData.cFileName[2] == L'\0')) + continue; + v = PyUnicode_FromUnicode(wFileData.cFileName, wcslen(wFileData.cFileName)); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); + } while (FindNextFileW(hFindFile, &wFileData) == TRUE); + + if (FindClose(hFindFile) == FALSE) { + Py_DECREF(d); + return win32_error_unicode("FindClose", wnamebuf); + } + return d; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, "et#:listdir", + Py_FileSystemDefaultEncoding, &bufptr, &len)) + return NULL; + if (len > 0) { + char ch = namebuf[len-1]; + if (ch != SEP && ch != ALTSEP && ch != ':') + namebuf[len++] = '/'; + } + strcpy(namebuf + len, "*.*"); + + if ((d = PyList_New(0)) == NULL) + return NULL; + + hFindFile = FindFirstFile(namebuf, &FileData); + if (hFindFile == INVALID_HANDLE_VALUE) { + errno = GetLastError(); + if (errno == ERROR_FILE_NOT_FOUND) + return d; + Py_DECREF(d); + return win32_error("FindFirstFile", namebuf); + } + do { + if (FileData.cFileName[0] == '.' && + (FileData.cFileName[1] == '\0' || + FileData.cFileName[1] == '.' && + FileData.cFileName[2] == '\0')) + continue; + v = PyString_FromString(FileData.cFileName); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); + } while (FindNextFile(hFindFile, &FileData) == TRUE); + + if (FindClose(hFindFile) == FALSE) { + Py_DECREF(d); + return win32_error("FindClose", namebuf); + } + + return d; + +#elif defined(PYOS_OS2) + +#ifndef MAX_PATH +#define MAX_PATH CCHMAXPATH +#endif + char *name, *pt; + int len; + PyObject *d, *v; + char namebuf[MAX_PATH+5]; + HDIR hdir = 1; + ULONG srchcnt = 1; + FILEFINDBUF3 ep; + APIRET rc; + + if (!PyArg_ParseTuple(args, "t#:listdir", &name, &len)) + return NULL; + if (len >= MAX_PATH) { + PyErr_SetString(PyExc_ValueError, "path too long"); + return NULL; + } + strcpy(namebuf, name); + for (pt = namebuf; *pt; pt++) + if (*pt == ALTSEP) + *pt = SEP; + if (namebuf[len-1] != SEP) + namebuf[len++] = SEP; + strcpy(namebuf + len, "*.*"); + + if ((d = PyList_New(0)) == NULL) + return NULL; + + rc = DosFindFirst(namebuf, /* Wildcard Pattern to Match */ + &hdir, /* Handle to Use While Search Directory */ + FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_DIRECTORY, + &ep, sizeof(ep), /* Structure to Receive Directory Entry */ + &srchcnt, /* Max and Actual Count of Entries Per Iteration */ + FIL_STANDARD); /* Format of Entry (EAs or Not) */ + + if (rc != NO_ERROR) { + errno = ENOENT; + return posix_error_with_filename(name); + } + + if (srchcnt > 0) { /* If Directory is NOT Totally Empty, */ + do { + if (ep.achName[0] == '.' + && (ep.achName[1] == '\0' || (ep.achName[1] == '.' && ep.achName[2] == '\0'))) + continue; /* Skip Over "." and ".." Names */ + + strcpy(namebuf, ep.achName); + + /* Leave Case of Name Alone -- In Native Form */ + /* (Removed Forced Lowercasing Code) */ + + v = PyString_FromString(namebuf); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); + } while (DosFindNext(hdir, &ep, sizeof(ep), &srchcnt) == NO_ERROR && srchcnt > 0); + } + + return d; +#else + + char *name = NULL; + PyObject *d, *v; + DIR *dirp; + struct dirent *ep; + int arg_is_unicode = 1; + + if (!PyArg_ParseTuple(args, "U:listdir", &v)) { + arg_is_unicode = 0; + PyErr_Clear(); + } + if (!PyArg_ParseTuple(args, "et:listdir", Py_FileSystemDefaultEncoding, &name)) + return NULL; + if ((dirp = opendir(name)) == NULL) { + return posix_error_with_allocated_filename(name); + } + if ((d = PyList_New(0)) == NULL) { + closedir(dirp); + PyMem_Free(name); + return NULL; + } + while ((ep = readdir(dirp)) != NULL) { + if (ep->d_name[0] == '.' && + (NAMLEN(ep) == 1 || + (ep->d_name[1] == '.' && NAMLEN(ep) == 2))) + continue; + v = PyString_FromStringAndSize(ep->d_name, NAMLEN(ep)); + if (v == NULL) { + Py_DECREF(d); + d = NULL; + break; + } +#ifdef Py_USING_UNICODE + if (arg_is_unicode) { + PyObject *w; + + w = PyUnicode_FromEncodedObject(v, + Py_FileSystemDefaultEncoding, + "strict"); + if (w != NULL) { + Py_DECREF(v); + v = w; + } + else { + /* fall back to the original byte string, as + discussed in patch #683592 */ + PyErr_Clear(); + } + } +#endif + if (PyList_Append(d, v) != 0) { + Py_DECREF(v); + Py_DECREF(d); + d = NULL; + break; + } + Py_DECREF(v); + } + closedir(dirp); + PyMem_Free(name); + + return d; + +#endif /* which OS */ +} /* end of posix_listdir */ + +#if defined(MS_WINDOWS) && !defined(MS_XBOX) +/* A helper function for abspath on win32 */ +static PyObject * +posix__getfullpathname(PyObject *self, PyObject *args) +{ + /* assume encoded strings wont more than double no of chars */ + char inbuf[MAX_PATH*2]; + char *inbufp = inbuf; + int insize = sizeof(inbuf)/sizeof(inbuf[0]); + char outbuf[MAX_PATH*2]; + char *temp; +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U|:_getfullpathname", &po)) { + Py_UNICODE woutbuf[MAX_PATH*2]; + Py_UNICODE *wtemp; + if (!GetFullPathNameW(PyUnicode_AS_UNICODE(po), + sizeof(woutbuf)/sizeof(woutbuf[0]), + woutbuf, &wtemp)) + return win32_error("GetFullPathName", ""); + return PyUnicode_FromUnicode(woutbuf, wcslen(woutbuf)); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + if (!PyArg_ParseTuple (args, "et#:_getfullpathname", + Py_FileSystemDefaultEncoding, &inbufp, + &insize)) + return NULL; + if (!GetFullPathName(inbuf, sizeof(outbuf)/sizeof(outbuf[0]), + outbuf, &temp)) + return win32_error("GetFullPathName", inbuf); + return PyString_FromString(outbuf); +} /* end of posix__getfullpathname */ +#endif /* MS_WINDOWS */ + +PyDoc_STRVAR(posix_mkdir__doc__, +"mkdir(path [, mode=0777])\n\n\ +Create a directory."); + +static PyObject * +posix_mkdir(PyObject *self, PyObject *args) +{ + int res; + char *path = NULL; + int mode = 0777; + +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "U|i:mkdir", &po, &mode)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread lock as + it is a simple dereference. */ + res = _wmkdir(PyUnicode_AS_UNICODE(po)); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, "et|i:mkdir", + Py_FileSystemDefaultEncoding, &path, &mode)) + return NULL; + Py_BEGIN_ALLOW_THREADS +#if ( defined(__WATCOMC__) || defined(_MSC_VER) || defined(PYCC_VACPP) ) && !defined(__QNX__) + res = mkdir(path); +#else + res = mkdir(path, mode); +#endif + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} + + +#ifdef HAVE_NICE +#if defined(HAVE_BROKEN_NICE) && defined(HAVE_SYS_RESOURCE_H) +#if defined(HAVE_GETPRIORITY) && !defined(PRIO_PROCESS) +#include +#endif +#endif + +PyDoc_STRVAR(posix_nice__doc__, +"nice(inc) -> new_priority\n\n\ +Decrease the priority of process by inc and return the new priority."); + +static PyObject * +posix_nice(PyObject *self, PyObject *args) +{ + int increment, value; + + if (!PyArg_ParseTuple(args, "i:nice", &increment)) + return NULL; + + /* There are two flavours of 'nice': one that returns the new + priority (as required by almost all standards out there) and the + Linux/FreeBSD/BSDI one, which returns '0' on success and advices + the use of getpriority() to get the new priority. + + If we are of the nice family that returns the new priority, we + need to clear errno before the call, and check if errno is filled + before calling posix_error() on a returnvalue of -1, because the + -1 may be the actual new priority! */ + + errno = 0; + value = nice(increment); +#if defined(HAVE_BROKEN_NICE) && defined(HAVE_GETPRIORITY) + if (value == 0) + value = getpriority(PRIO_PROCESS, 0); +#endif + if (value == -1 && errno != 0) + /* either nice() or getpriority() returned an error */ + return posix_error(); + return PyInt_FromLong((long) value); +} +#endif /* HAVE_NICE */ + + +PyDoc_STRVAR(posix_rename__doc__, +"rename(old, new)\n\n\ +Rename a file or directory."); + +static PyObject * +posix_rename(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_2str(args, "etet:rename", rename, "OO:rename", _wrename); +#else + return posix_2str(args, "etet:rename", rename, NULL, NULL); +#endif +} + + +PyDoc_STRVAR(posix_rmdir__doc__, +"rmdir(path)\n\n\ +Remove a directory."); + +static PyObject * +posix_rmdir(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_1str(args, "et:rmdir", rmdir, "U:rmdir", _wrmdir); +#else + return posix_1str(args, "et:rmdir", rmdir, NULL, NULL); +#endif +} + + +PyDoc_STRVAR(posix_stat__doc__, +"stat(path) -> stat result\n\n\ +Perform a stat system call on the given path."); + +static PyObject * +posix_stat(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_do_stat(self, args, "et:stat", STAT, "U:stat", _wstati64); +#else + return posix_do_stat(self, args, "et:stat", STAT, NULL, NULL); +#endif +} + + +#ifdef HAVE_SYSTEM +PyDoc_STRVAR(posix_system__doc__, +"system(command) -> exit_status\n\n\ +Execute the command (a string) in a subshell."); + +static PyObject * +posix_system(PyObject *self, PyObject *args) +{ + char *command; + long sts; + if (!PyArg_ParseTuple(args, "s:system", &command)) + return NULL; + Py_BEGIN_ALLOW_THREADS + sts = system(command); + Py_END_ALLOW_THREADS + return PyInt_FromLong(sts); +} +#endif + + +PyDoc_STRVAR(posix_umask__doc__, +"umask(new_mask) -> old_mask\n\n\ +Set the current numeric umask and return the previous umask."); + +static PyObject * +posix_umask(PyObject *self, PyObject *args) +{ + int i; + if (!PyArg_ParseTuple(args, "i:umask", &i)) + return NULL; + i = (int)umask(i); + if (i < 0) + return posix_error(); + return PyInt_FromLong((long)i); +} + + +PyDoc_STRVAR(posix_unlink__doc__, +"unlink(path)\n\n\ +Remove a file (same as remove(path))."); + +PyDoc_STRVAR(posix_remove__doc__, +"remove(path)\n\n\ +Remove a file (same as unlink(path))."); + +static PyObject * +posix_unlink(PyObject *self, PyObject *args) +{ +#ifdef MS_WINDOWS + return posix_1str(args, "et:remove", unlink, "U:remove", _wunlink); +#else + return posix_1str(args, "et:remove", unlink, NULL, NULL); +#endif +} + + +#ifdef HAVE_UNAME +PyDoc_STRVAR(posix_uname__doc__, +"uname() -> (sysname, nodename, release, version, machine)\n\n\ +Return a tuple identifying the current operating system."); + +static PyObject * +posix_uname(PyObject *self, PyObject *noargs) +{ + struct utsname u; + int res; + + Py_BEGIN_ALLOW_THREADS + res = uname(&u); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + return Py_BuildValue("(sssss)", + u.sysname, + u.nodename, + u.release, + u.version, + u.machine); +} +#endif /* HAVE_UNAME */ + +static int +extract_time(PyObject *t, long* sec, long* usec) +{ + long intval; + if (PyFloat_Check(t)) { + double tval = PyFloat_AsDouble(t); + PyObject *intobj = t->ob_type->tp_as_number->nb_int(t); + if (!intobj) + return -1; + intval = PyInt_AsLong(intobj); + Py_DECREF(intobj); + *sec = intval; + *usec = (long)((tval - intval) * 1e6); /* can't exceed 1000000 */ + if (*usec < 0) + /* If rounding gave us a negative number, + truncate. */ + *usec = 0; + return 0; + } + intval = PyInt_AsLong(t); + if (intval == -1 && PyErr_Occurred()) + return -1; + *sec = intval; + *usec = 0; + return 0; +} + +PyDoc_STRVAR(posix_utime__doc__, +"utime(path, (atime, utime))\n\ +utime(path, None)\n\n\ +Set the access and modified time of the file to the given values. If the\n\ +second form is used, set the access and modified times to the current time."); + +static PyObject * +posix_utime(PyObject *self, PyObject *args) +{ + char *path; + long atime, mtime, ausec, musec; + int res; + PyObject* arg; + +#if defined(HAVE_UTIMES) + struct timeval buf[2]; +#define ATIME buf[0].tv_sec +#define MTIME buf[1].tv_sec +#elif defined(HAVE_UTIME_H) +/* XXX should define struct utimbuf instead, above */ + struct utimbuf buf; +#define ATIME buf.actime +#define MTIME buf.modtime +#define UTIME_ARG &buf +#else /* HAVE_UTIMES */ + time_t buf[2]; +#define ATIME buf[0] +#define MTIME buf[1] +#define UTIME_ARG buf +#endif /* HAVE_UTIMES */ + + int have_unicode_filename = 0; +#ifdef Py_WIN_WIDE_FILENAMES + PyUnicodeObject *obwpath; + wchar_t *wpath; + if (unicode_file_names()) { + if (PyArg_ParseTuple(args, "UO|:utime", &obwpath, &arg)) { + wpath = PyUnicode_AS_UNICODE(obwpath); + have_unicode_filename = 1; + } else + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif /* Py_WIN_WIDE_FILENAMES */ + + if (!have_unicode_filename && \ + !PyArg_ParseTuple(args, "sO:utime", &path, &arg)) + return NULL; + if (arg == Py_None) { + /* optional time values not given */ + Py_BEGIN_ALLOW_THREADS +#ifdef Py_WIN_WIDE_FILENAMES + if (have_unicode_filename) + res = _wutime(wpath, NULL); + else +#endif /* Py_WIN_WIDE_FILENAMES */ + res = utime(path, NULL); + Py_END_ALLOW_THREADS + } + else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { + PyErr_SetString(PyExc_TypeError, + "utime() arg 2 must be a tuple (atime, mtime)"); + return NULL; + } + else { + if (extract_time(PyTuple_GET_ITEM(arg, 0), + &atime, &ausec) == -1) + return NULL; + if (extract_time(PyTuple_GET_ITEM(arg, 1), + &mtime, &musec) == -1) + return NULL; + ATIME = atime; + MTIME = mtime; +#ifdef HAVE_UTIMES + buf[0].tv_usec = ausec; + buf[1].tv_usec = musec; + Py_BEGIN_ALLOW_THREADS + res = utimes(path, buf); + Py_END_ALLOW_THREADS +#else + Py_BEGIN_ALLOW_THREADS +#ifdef Py_WIN_WIDE_FILENAMES + if (have_unicode_filename) + /* utime is OK with utimbuf, but _wutime insists + on _utimbuf (the msvc headers assert the + underscore version is ansi) */ + res = _wutime(wpath, (struct _utimbuf *)UTIME_ARG); + else +#endif /* Py_WIN_WIDE_FILENAMES */ + res = utime(path, UTIME_ARG); + Py_END_ALLOW_THREADS +#endif /* HAVE_UTIMES */ + } + if (res < 0) + return posix_error_with_filename(path); + Py_INCREF(Py_None); + return Py_None; +#undef UTIME_ARG +#undef ATIME +#undef MTIME +} + + +/* Process operations */ + +PyDoc_STRVAR(posix__exit__doc__, +"_exit(status)\n\n\ +Exit to the system with specified status, without normal exit processing."); + +static PyObject * +posix__exit(PyObject *self, PyObject *args) +{ + int sts; + if (!PyArg_ParseTuple(args, "i:_exit", &sts)) + return NULL; + _exit(sts); + return NULL; /* Make gcc -Wall happy */ +} + +#if defined(HAVE_EXECV) || defined(HAVE_SPAWNV) +static void +free_string_array(char **array, int count) +{ + int i; + for (i = 0; i < count; i++) + PyMem_Free(array[i]); + PyMem_DEL(array); +} +#endif + + +#ifdef HAVE_EXECV +PyDoc_STRVAR(posix_execv__doc__, +"execv(path, args)\n\n\ +Execute an executable path with arguments, replacing current process.\n\ +\n\ + path: path of executable file\n\ + args: tuple or list of strings"); + +static PyObject * +posix_execv(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv; + char **argvlist; + int i, argc; + PyObject *(*getitem)(PyObject *, int); + + /* execv has two arguments: (path, argv), where + argv is a list or tuple of strings. */ + + if (!PyArg_ParseTuple(args, "etO:execv", + Py_FileSystemDefaultEncoding, + &path, &argv)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, "execv() arg 2 must be a tuple or list"); + PyMem_Free(path); + return NULL; + } + + if (argc == 0) { + PyErr_SetString(PyExc_ValueError, "execv() arg 2 must not be empty"); + PyMem_Free(path); + return NULL; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyMem_Free(path); + return PyErr_NoMemory(); + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), "et", + Py_FileSystemDefaultEncoding, + &argvlist[i])) { + free_string_array(argvlist, i); + PyErr_SetString(PyExc_TypeError, + "execv() arg 2 must contain only strings"); + PyMem_Free(path); + return NULL; + + } + } + argvlist[argc] = NULL; + +#ifdef BAD_EXEC_PROTOTYPES + execv(path, (const char **) argvlist); +#else /* BAD_EXEC_PROTOTYPES */ + execv(path, argvlist); +#endif /* BAD_EXEC_PROTOTYPES */ + + /* If we get here it's definitely an error */ + + free_string_array(argvlist, argc); + PyMem_Free(path); + return posix_error(); +} + + +PyDoc_STRVAR(posix_execve__doc__, +"execve(path, args, env)\n\n\ +Execute a path with arguments and environment, replacing current process.\n\ +\n\ + path: path of executable file\n\ + args: tuple or list of arguments\n\ + env: dictionary of strings mapping to strings"); + +static PyObject * +posix_execve(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv, *env; + char **argvlist; + char **envlist; + PyObject *key, *val, *keys=NULL, *vals=NULL; + int i, pos, argc, envc; + PyObject *(*getitem)(PyObject *, int); + int lastarg = 0; + + /* execve has three arguments: (path, argv, env), where + argv is a list or tuple of strings and env is a dictionary + like posix.environ. */ + + if (!PyArg_ParseTuple(args, "etOO:execve", + Py_FileSystemDefaultEncoding, + &path, &argv, &env)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "execve() arg 2 must be a tuple or list"); + goto fail_0; + } + if (!PyMapping_Check(env)) { + PyErr_SetString(PyExc_TypeError, + "execve() arg 3 must be a mapping object"); + goto fail_0; + } + + if (argc == 0) { + PyErr_SetString(PyExc_ValueError, + "execve() arg 2 must not be empty"); + goto fail_0; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyErr_NoMemory(); + goto fail_0; + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), + "et;execve() arg 2 must contain only strings", + Py_FileSystemDefaultEncoding, + &argvlist[i])) + { + lastarg = i; + goto fail_1; + } + } + lastarg = argc; + argvlist[argc] = NULL; + + i = PyMapping_Size(env); + if (i < 0) + goto fail_1; + envlist = PyMem_NEW(char *, i + 1); + if (envlist == NULL) { + PyErr_NoMemory(); + goto fail_1; + } + envc = 0; + keys = PyMapping_Keys(env); + vals = PyMapping_Values(env); + if (!keys || !vals) + goto fail_2; + if (!PyList_Check(keys) || !PyList_Check(vals)) { + PyErr_SetString(PyExc_TypeError, + "execve(): env.keys() or env.values() is not a list"); + goto fail_2; + } + + for (pos = 0; pos < i; pos++) { + char *p, *k, *v; + size_t len; + + key = PyList_GetItem(keys, pos); + val = PyList_GetItem(vals, pos); + if (!key || !val) + goto fail_2; + + if (!PyArg_Parse( + key, + "s;execve() arg 3 contains a non-string key", + &k) || + !PyArg_Parse( + val, + "s;execve() arg 3 contains a non-string value", + &v)) + { + goto fail_2; + } + +#if defined(PYOS_OS2) + /* Omit Pseudo-Env Vars that Would Confuse Programs if Passed On */ + if (stricmp(k, "BEGINLIBPATH") != 0 && stricmp(k, "ENDLIBPATH") != 0) { +#endif + len = PyString_Size(key) + PyString_Size(val) + 2; + p = PyMem_NEW(char, len); + if (p == NULL) { + PyErr_NoMemory(); + goto fail_2; + } + PyOS_snprintf(p, len, "%s=%s", k, v); + envlist[envc++] = p; +#if defined(PYOS_OS2) + } +#endif + } + envlist[envc] = 0; + + +#ifdef BAD_EXEC_PROTOTYPES + execve(path, (const char **)argvlist, envlist); +#else /* BAD_EXEC_PROTOTYPES */ + execve(path, argvlist, envlist); +#endif /* BAD_EXEC_PROTOTYPES */ + + /* If we get here it's definitely an error */ + + (void) posix_error(); + + fail_2: + while (--envc >= 0) + PyMem_DEL(envlist[envc]); + PyMem_DEL(envlist); + fail_1: + free_string_array(argvlist, lastarg); + Py_XDECREF(vals); + Py_XDECREF(keys); + fail_0: + PyMem_Free(path); + return NULL; +} +#endif /* HAVE_EXECV */ + + +#ifdef HAVE_SPAWNV +PyDoc_STRVAR(posix_spawnv__doc__, +"spawnv(mode, path, args)\n\n\ +Execute the program 'path' in a new process.\n\ +\n\ + mode: mode of process creation\n\ + path: path of executable file\n\ + args: tuple or list of strings"); + +static PyObject * +posix_spawnv(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv; + char **argvlist; + int mode, i, argc; + Py_intptr_t spawnval; + PyObject *(*getitem)(PyObject *, int); + + /* spawnv has three arguments: (mode, path, argv), where + argv is a list or tuple of strings. */ + + if (!PyArg_ParseTuple(args, "ietO:spawnv", &mode, + Py_FileSystemDefaultEncoding, + &path, &argv)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnv() arg 2 must be a tuple or list"); + PyMem_Free(path); + return NULL; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyMem_Free(path); + return PyErr_NoMemory(); + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), "et", + Py_FileSystemDefaultEncoding, + &argvlist[i])) { + free_string_array(argvlist, i); + PyErr_SetString( + PyExc_TypeError, + "spawnv() arg 2 must contain only strings"); + PyMem_Free(path); + return NULL; + } + } + argvlist[argc] = NULL; + +#if defined(PYOS_OS2) && defined(PYCC_GCC) + Py_BEGIN_ALLOW_THREADS + spawnval = spawnv(mode, path, argvlist); + Py_END_ALLOW_THREADS +#else + if (mode == _OLD_P_OVERLAY) + mode = _P_OVERLAY; + + Py_BEGIN_ALLOW_THREADS + spawnval = _spawnv(mode, path, argvlist); + Py_END_ALLOW_THREADS +#endif + + free_string_array(argvlist, argc); + PyMem_Free(path); + + if (spawnval == -1) + return posix_error(); + else +#if SIZEOF_LONG == SIZEOF_VOID_P + return Py_BuildValue("l", (long) spawnval); +#else + return Py_BuildValue("L", (PY_LONG_LONG) spawnval); +#endif +} + + +PyDoc_STRVAR(posix_spawnve__doc__, +"spawnve(mode, path, args, env)\n\n\ +Execute the program 'path' in a new process.\n\ +\n\ + mode: mode of process creation\n\ + path: path of executable file\n\ + args: tuple or list of arguments\n\ + env: dictionary of strings mapping to strings"); + +static PyObject * +posix_spawnve(PyObject *self, PyObject *args) +{ + char *path; + PyObject *argv, *env; + char **argvlist; + char **envlist; + PyObject *key, *val, *keys=NULL, *vals=NULL, *res=NULL; + int mode, i, pos, argc, envc; + Py_intptr_t spawnval; + PyObject *(*getitem)(PyObject *, int); + int lastarg = 0; + + /* spawnve has four arguments: (mode, path, argv, env), where + argv is a list or tuple of strings and env is a dictionary + like posix.environ. */ + + if (!PyArg_ParseTuple(args, "ietOO:spawnve", &mode, + Py_FileSystemDefaultEncoding, + &path, &argv, &env)) + return NULL; + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnve() arg 2 must be a tuple or list"); + goto fail_0; + } + if (!PyMapping_Check(env)) { + PyErr_SetString(PyExc_TypeError, + "spawnve() arg 3 must be a mapping object"); + goto fail_0; + } + + argvlist = PyMem_NEW(char *, argc+1); + if (argvlist == NULL) { + PyErr_NoMemory(); + goto fail_0; + } + for (i = 0; i < argc; i++) { + if (!PyArg_Parse((*getitem)(argv, i), + "et;spawnve() arg 2 must contain only strings", + Py_FileSystemDefaultEncoding, + &argvlist[i])) + { + lastarg = i; + goto fail_1; + } + } + lastarg = argc; + argvlist[argc] = NULL; + + i = PyMapping_Size(env); + if (i < 0) + goto fail_1; + envlist = PyMem_NEW(char *, i + 1); + if (envlist == NULL) { + PyErr_NoMemory(); + goto fail_1; + } + envc = 0; + keys = PyMapping_Keys(env); + vals = PyMapping_Values(env); + if (!keys || !vals) + goto fail_2; + if (!PyList_Check(keys) || !PyList_Check(vals)) { + PyErr_SetString(PyExc_TypeError, + "spawnve(): env.keys() or env.values() is not a list"); + goto fail_2; + } + + for (pos = 0; pos < i; pos++) { + char *p, *k, *v; + size_t len; + + key = PyList_GetItem(keys, pos); + val = PyList_GetItem(vals, pos); + if (!key || !val) + goto fail_2; + + if (!PyArg_Parse( + key, + "s;spawnve() arg 3 contains a non-string key", + &k) || + !PyArg_Parse( + val, + "s;spawnve() arg 3 contains a non-string value", + &v)) + { + goto fail_2; + } + len = PyString_Size(key) + PyString_Size(val) + 2; + p = PyMem_NEW(char, len); + if (p == NULL) { + PyErr_NoMemory(); + goto fail_2; + } + PyOS_snprintf(p, len, "%s=%s", k, v); + envlist[envc++] = p; + } + envlist[envc] = 0; + +#if defined(PYOS_OS2) && defined(PYCC_GCC) + Py_BEGIN_ALLOW_THREADS + spawnval = spawnve(mode, path, argvlist, envlist); + Py_END_ALLOW_THREADS +#else + if (mode == _OLD_P_OVERLAY) + mode = _P_OVERLAY; + + Py_BEGIN_ALLOW_THREADS + spawnval = _spawnve(mode, path, argvlist, envlist); + Py_END_ALLOW_THREADS +#endif + + if (spawnval == -1) + (void) posix_error(); + else +#if SIZEOF_LONG == SIZEOF_VOID_P + res = Py_BuildValue("l", (long) spawnval); +#else + res = Py_BuildValue("L", (PY_LONG_LONG) spawnval); +#endif + + fail_2: + while (--envc >= 0) + PyMem_DEL(envlist[envc]); + PyMem_DEL(envlist); + fail_1: + free_string_array(argvlist, lastarg); + Py_XDECREF(vals); + Py_XDECREF(keys); + fail_0: + PyMem_Free(path); + return res; +} +#endif /* HAVE_SPAWNV */ + + +#ifdef HAVE_FORK1 +PyDoc_STRVAR(posix_fork1__doc__, +"fork1() -> pid\n\n\ +Fork a child process with a single multiplexed (i.e., not bound) thread.\n\ +\n\ +Return 0 to child process and PID of child to parent process."); + +static PyObject * +posix_fork1(PyObject *self, PyObject *noargs) +{ + int pid = fork1(); + if (pid == -1) + return posix_error(); + PyOS_AfterFork(); + return PyInt_FromLong((long)pid); +} +#endif + + +#ifdef HAVE_FORK +PyDoc_STRVAR(posix_fork__doc__, +"fork() -> pid\n\n\ +Fork a child process.\n\ +Return 0 to child process and PID of child to parent process."); + +static PyObject * +posix_fork(PyObject *self, PyObject *noargs) +{ + int pid = fork(); + if (pid == -1) + return posix_error(); + if (pid == 0) + PyOS_AfterFork(); + return PyInt_FromLong((long)pid); +} +#endif + +/* AIX uses /dev/ptc but is otherwise the same as /dev/ptmx */ +/* IRIX has both /dev/ptc and /dev/ptmx, use ptmx */ +#if defined(HAVE_DEV_PTC) && !defined(HAVE_DEV_PTMX) +#define DEV_PTY_FILE "/dev/ptc" +#define HAVE_DEV_PTMX +#else +#define DEV_PTY_FILE "/dev/ptmx" +#endif + +#if defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_DEV_PTMX) +#ifdef HAVE_PTY_H +#include +#else +#ifdef HAVE_LIBUTIL_H +#include +#endif /* HAVE_LIBUTIL_H */ +#endif /* HAVE_PTY_H */ +#ifdef HAVE_STROPTS_H +#include +#endif +#endif /* defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_DEV_PTMX */ + +#if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) +PyDoc_STRVAR(posix_openpty__doc__, +"openpty() -> (master_fd, slave_fd)\n\n\ +Open a pseudo-terminal, returning open fd's for both master and slave end.\n"); + +static PyObject * +posix_openpty(PyObject *self, PyObject *noargs) +{ + int master_fd, slave_fd; +#ifndef HAVE_OPENPTY + char * slave_name; +#endif +#if defined(HAVE_DEV_PTMX) && !defined(HAVE_OPENPTY) && !defined(HAVE__GETPTY) + PyOS_sighandler_t sig_saved; +#ifdef sun + extern char *ptsname(); +#endif +#endif + +#ifdef HAVE_OPENPTY + if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0) + return posix_error(); +#elif defined(HAVE__GETPTY) + slave_name = _getpty(&master_fd, O_RDWR, 0666, 0); + if (slave_name == NULL) + return posix_error(); + + slave_fd = open(slave_name, O_RDWR); + if (slave_fd < 0) + return posix_error(); +#else + master_fd = open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */ + if (master_fd < 0) + return posix_error(); + sig_saved = signal(SIGCHLD, SIG_DFL); + /* change permission of slave */ + if (grantpt(master_fd) < 0) { + signal(SIGCHLD, sig_saved); + return posix_error(); + } + /* unlock slave */ + if (unlockpt(master_fd) < 0) { + signal(SIGCHLD, sig_saved); + return posix_error(); + } + signal(SIGCHLD, sig_saved); + slave_name = ptsname(master_fd); /* get name of slave */ + if (slave_name == NULL) + return posix_error(); + slave_fd = open(slave_name, O_RDWR | O_NOCTTY); /* open slave */ + if (slave_fd < 0) + return posix_error(); +#if !defined(__CYGWIN__) && !defined(HAVE_DEV_PTC) + ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */ + ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */ +#ifndef __hpux + ioctl(slave_fd, I_PUSH, "ttcompat"); /* push ttcompat */ +#endif /* __hpux */ +#endif /* HAVE_CYGWIN */ +#endif /* HAVE_OPENPTY */ + + return Py_BuildValue("(ii)", master_fd, slave_fd); + +} +#endif /* defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) */ + +#ifdef HAVE_FORKPTY +PyDoc_STRVAR(posix_forkpty__doc__, +"forkpty() -> (pid, master_fd)\n\n\ +Fork a new process with a new pseudo-terminal as controlling tty.\n\n\ +Like fork(), return 0 as pid to child process, and PID of child to parent.\n\ +To both, return fd of newly opened pseudo-terminal.\n"); + +static PyObject * +posix_forkpty(PyObject *self, PyObject *noargs) +{ + int master_fd, pid; + + pid = forkpty(&master_fd, NULL, NULL, NULL); + if (pid == -1) + return posix_error(); + if (pid == 0) + PyOS_AfterFork(); + return Py_BuildValue("(ii)", pid, master_fd); +} +#endif + +#ifdef HAVE_GETEGID +PyDoc_STRVAR(posix_getegid__doc__, +"getegid() -> egid\n\n\ +Return the current process's effective group id."); + +static PyObject * +posix_getegid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getegid()); +} +#endif + + +#ifdef HAVE_GETEUID +PyDoc_STRVAR(posix_geteuid__doc__, +"geteuid() -> euid\n\n\ +Return the current process's effective user id."); + +static PyObject * +posix_geteuid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)geteuid()); +} +#endif + + +#ifdef HAVE_GETGID +PyDoc_STRVAR(posix_getgid__doc__, +"getgid() -> gid\n\n\ +Return the current process's group id."); + +static PyObject * +posix_getgid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getgid()); +} +#endif + + +PyDoc_STRVAR(posix_getpid__doc__, +"getpid() -> pid\n\n\ +Return the current process id"); + +static PyObject * +posix_getpid(PyObject *self, PyObject *noargs) +{ +#ifdef MS_XBOX + return PyInt_FromLong(0); +#else + return PyInt_FromLong((long)getpid()); +#endif +} + + +#ifdef HAVE_GETGROUPS +PyDoc_STRVAR(posix_getgroups__doc__, +"getgroups() -> list of group IDs\n\n\ +Return list of supplemental group IDs for the process."); + +static PyObject * +posix_getgroups(PyObject *self, PyObject *noargs) +{ + PyObject *result = NULL; + +#ifdef NGROUPS_MAX +#define MAX_GROUPS NGROUPS_MAX +#else + /* defined to be 16 on Solaris7, so this should be a small number */ +#define MAX_GROUPS 64 +#endif + gid_t grouplist[MAX_GROUPS]; + int n; + + n = getgroups(MAX_GROUPS, grouplist); + if (n < 0) + posix_error(); + else { + result = PyList_New(n); + if (result != NULL) { + int i; + for (i = 0; i < n; ++i) { + PyObject *o = PyInt_FromLong((long)grouplist[i]); + if (o == NULL) { + Py_DECREF(result); + result = NULL; + break; + } + PyList_SET_ITEM(result, i, o); + } + } + } + + return result; +} +#endif + +#ifdef HAVE_GETPGID +PyDoc_STRVAR(posix_getpgid__doc__, +"getpgid(pid) -> pgid\n\n\ +Call the system call getpgid()."); + +static PyObject * +posix_getpgid(PyObject *self, PyObject *args) +{ + int pid, pgid; + if (!PyArg_ParseTuple(args, "i:getpgid", &pid)) + return NULL; + pgid = getpgid(pid); + if (pgid < 0) + return posix_error(); + return PyInt_FromLong((long)pgid); +} +#endif /* HAVE_GETPGID */ + + +#ifdef HAVE_GETPGRP +PyDoc_STRVAR(posix_getpgrp__doc__, +"getpgrp() -> pgrp\n\n\ +Return the current process group id."); + +static PyObject * +posix_getpgrp(PyObject *self, PyObject *noargs) +{ +#ifdef GETPGRP_HAVE_ARG + return PyInt_FromLong((long)getpgrp(0)); +#else /* GETPGRP_HAVE_ARG */ + return PyInt_FromLong((long)getpgrp()); +#endif /* GETPGRP_HAVE_ARG */ +} +#endif /* HAVE_GETPGRP */ + + +#ifdef HAVE_SETPGRP +PyDoc_STRVAR(posix_setpgrp__doc__, +"setpgrp()\n\n\ +Make this process a session leader."); + +static PyObject * +posix_setpgrp(PyObject *self, PyObject *noargs) +{ +#ifdef SETPGRP_HAVE_ARG + if (setpgrp(0, 0) < 0) +#else /* SETPGRP_HAVE_ARG */ + if (setpgrp() < 0) +#endif /* SETPGRP_HAVE_ARG */ + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + +#endif /* HAVE_SETPGRP */ + +#ifdef HAVE_GETPPID +PyDoc_STRVAR(posix_getppid__doc__, +"getppid() -> ppid\n\n\ +Return the parent's process id."); + +static PyObject * +posix_getppid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getppid()); +} +#endif + + +#ifdef HAVE_GETLOGIN +PyDoc_STRVAR(posix_getlogin__doc__, +"getlogin() -> string\n\n\ +Return the actual login name."); + +static PyObject * +posix_getlogin(PyObject *self, PyObject *noargs) +{ + PyObject *result = NULL; + char *name; + int old_errno = errno; + + errno = 0; + name = getlogin(); + if (name == NULL) { + if (errno) + posix_error(); + else + PyErr_SetString(PyExc_OSError, + "unable to determine login name"); + } + else + result = PyString_FromString(name); + errno = old_errno; + + return result; +} +#endif + +#ifdef HAVE_GETUID +PyDoc_STRVAR(posix_getuid__doc__, +"getuid() -> uid\n\n\ +Return the current process's user id."); + +static PyObject * +posix_getuid(PyObject *self, PyObject *noargs) +{ + return PyInt_FromLong((long)getuid()); +} +#endif + + +#ifdef HAVE_KILL +PyDoc_STRVAR(posix_kill__doc__, +"kill(pid, sig)\n\n\ +Kill a process with a signal."); + +static PyObject * +posix_kill(PyObject *self, PyObject *args) +{ + int pid, sig; + if (!PyArg_ParseTuple(args, "ii:kill", &pid, &sig)) + return NULL; +#if defined(PYOS_OS2) && !defined(PYCC_GCC) + if (sig == XCPT_SIGNAL_INTR || sig == XCPT_SIGNAL_BREAK) { + APIRET rc; + if ((rc = DosSendSignalException(pid, sig)) != NO_ERROR) + return os2_error(rc); + + } else if (sig == XCPT_SIGNAL_KILLPROC) { + APIRET rc; + if ((rc = DosKillProcess(DKP_PROCESS, pid)) != NO_ERROR) + return os2_error(rc); + + } else + return NULL; /* Unrecognized Signal Requested */ +#else + if (kill(pid, sig) == -1) + return posix_error(); +#endif + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_KILLPG +PyDoc_STRVAR(posix_killpg__doc__, +"killpg(pgid, sig)\n\n\ +Kill a process group with a signal."); + +static PyObject * +posix_killpg(PyObject *self, PyObject *args) +{ + int pgid, sig; + if (!PyArg_ParseTuple(args, "ii:killpg", &pgid, &sig)) + return NULL; + if (killpg(pgid, sig) == -1) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_PLOCK + +#ifdef HAVE_SYS_LOCK_H +#include +#endif + +PyDoc_STRVAR(posix_plock__doc__, +"plock(op)\n\n\ +Lock program segments into memory."); + +static PyObject * +posix_plock(PyObject *self, PyObject *args) +{ + int op; + if (!PyArg_ParseTuple(args, "i:plock", &op)) + return NULL; + if (plock(op) == -1) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + + +#ifdef HAVE_POPEN +PyDoc_STRVAR(posix_popen__doc__, +"popen(command [, mode='r' [, bufsize]]) -> pipe\n\n\ +Open a pipe to/from a command returning a file object."); + +#if defined(PYOS_OS2) +#if defined(PYCC_VACPP) +static int +async_system(const char *command) +{ + char *p, errormsg[256], args[1024]; + RESULTCODES rcodes; + APIRET rc; + char *shell = Py_GETENV("COMSPEC"); + if (!shell) + shell = "cmd"; + + strcpy(args, shell); + p = &args[ strlen(args)+1 ]; + strcpy(p, "/c "); + strcat(p, command); + p += strlen(p) + 1; + *p = '\0'; + + rc = DosExecPgm(errormsg, sizeof(errormsg), + EXEC_ASYNC, /* Execute Async w/o Wait for Results */ + args, + NULL, /* Inherit Parent's Environment */ + &rcodes, shell); + return rc; +} + +static FILE * +popen(const char *command, const char *mode, int pipesize, int *err) +{ + HFILE rhan, whan; + FILE *retfd = NULL; + APIRET rc = DosCreatePipe(&rhan, &whan, pipesize); + + if (rc != NO_ERROR) { + *err = rc; + return NULL; /* ERROR - Unable to Create Anon Pipe */ + } + + if (strchr(mode, 'r') != NULL) { /* Treat Command as a Data Source */ + int oldfd = dup(1); /* Save STDOUT Handle in Another Handle */ + + DosEnterCritSec(); /* Stop Other Threads While Changing Handles */ + close(1); /* Make STDOUT Available for Reallocation */ + + if (dup2(whan, 1) == 0) { /* Connect STDOUT to Pipe Write Side */ + DosClose(whan); /* Close Now-Unused Pipe Write Handle */ + + rc = async_system(command); + } + + dup2(oldfd, 1); /* Reconnect STDOUT to Original Handle */ + DosExitCritSec(); /* Now Allow Other Threads to Run */ + + if (rc == NO_ERROR) + retfd = fdopen(rhan, mode); /* And Return Pipe Read Handle */ + + close(oldfd); /* And Close Saved STDOUT Handle */ + return retfd; /* Return fd of Pipe or NULL if Error */ + + } else if (strchr(mode, 'w')) { /* Treat Command as a Data Sink */ + int oldfd = dup(0); /* Save STDIN Handle in Another Handle */ + + DosEnterCritSec(); /* Stop Other Threads While Changing Handles */ + close(0); /* Make STDIN Available for Reallocation */ + + if (dup2(rhan, 0) == 0) { /* Connect STDIN to Pipe Read Side */ + DosClose(rhan); /* Close Now-Unused Pipe Read Handle */ + + rc = async_system(command); + } + + dup2(oldfd, 0); /* Reconnect STDIN to Original Handle */ + DosExitCritSec(); /* Now Allow Other Threads to Run */ + + if (rc == NO_ERROR) + retfd = fdopen(whan, mode); /* And Return Pipe Write Handle */ + + close(oldfd); /* And Close Saved STDIN Handle */ + return retfd; /* Return fd of Pipe or NULL if Error */ + + } else { + *err = ERROR_INVALID_ACCESS; + return NULL; /* ERROR - Invalid Mode (Neither Read nor Write) */ + } +} + +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + char *name; + char *mode = "r"; + int err, bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "s|si:popen", &name, &mode, &bufsize)) + return NULL; + Py_BEGIN_ALLOW_THREADS + fp = popen(name, mode, (bufsize > 0) ? bufsize : 4096, &err); + Py_END_ALLOW_THREADS + if (fp == NULL) + return os2_error(err); + + f = PyFile_FromFile(fp, name, mode, fclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +#elif defined(PYCC_GCC) + +/* standard posix version of popen() support */ +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + char *name; + char *mode = "r"; + int bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "s|si:popen", &name, &mode, &bufsize)) + return NULL; + Py_BEGIN_ALLOW_THREADS + fp = popen(name, mode); + Py_END_ALLOW_THREADS + if (fp == NULL) + return posix_error(); + f = PyFile_FromFile(fp, name, mode, pclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +/* fork() under OS/2 has lots'o'warts + * EMX supports pipe() and spawn*() so we can synthesize popen[234]() + * most of this code is a ripoff of the win32 code, but using the + * capabilities of EMX's C library routines + */ + +/* These tell _PyPopen() whether to return 1, 2, or 3 file objects. */ +#define POPEN_1 1 +#define POPEN_2 2 +#define POPEN_3 3 +#define POPEN_4 4 + +static PyObject *_PyPopen(char *, int, int, int); +static int _PyPclose(FILE *file); + +/* + * Internal dictionary mapping popen* file pointers to process handles, + * for use when retrieving the process exit code. See _PyPclose() below + * for more information on this dictionary's use. + */ +static PyObject *_PyPopenProcs = NULL; + +/* os2emx version of popen2() + * + * The result of this function is a pipe (file) connected to the + * process's stdin, and a pipe connected to the process's stdout. + */ + +static PyObject * +os2emx_popen2(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm=0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen2", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'"); + return NULL; + } else + tm = O_BINARY; + + f = _PyPopen(cmdstring, tm, POPEN_2, bufsize); + + return f; +} + +/* + * Variation on os2emx.popen2 + * + * The result of this function is 3 pipes - the process's stdin, + * stdout and stderr + */ + +static PyObject * +os2emx_popen3(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen3", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'"); + return NULL; + } else + tm = O_BINARY; + + f = _PyPopen(cmdstring, tm, POPEN_3, bufsize); + + return f; +} + +/* + * Variation on os2emx.popen2 + * + * The result of this function is 2 pipes - the processes stdin, + * and stdout+stderr combined as a single pipe. + */ + +static PyObject * +os2emx_popen4(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen4", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'"); + return NULL; + } else + tm = O_BINARY; + + f = _PyPopen(cmdstring, tm, POPEN_4, bufsize); + + return f; +} + +/* a couple of structures for convenient handling of multiple + * file handles and pipes + */ +struct file_ref +{ + int handle; + int flags; +}; + +struct pipe_ref +{ + int rd; + int wr; +}; + +/* The following code is derived from the win32 code */ + +static PyObject * +_PyPopen(char *cmdstring, int mode, int n, int bufsize) +{ + struct file_ref stdio[3]; + struct pipe_ref p_fd[3]; + FILE *p_s[3]; + int file_count, i, pipe_err, pipe_pid; + char *shell, *sh_name, *opt, *rd_mode, *wr_mode; + PyObject *f, *p_f[3]; + + /* file modes for subsequent fdopen's on pipe handles */ + if (mode == O_TEXT) + { + rd_mode = "rt"; + wr_mode = "wt"; + } + else + { + rd_mode = "rb"; + wr_mode = "wb"; + } + + /* prepare shell references */ + if ((shell = Py_GETENV("EMXSHELL")) == NULL) + if ((shell = Py_GETENV("COMSPEC")) == NULL) + { + errno = ENOENT; + return posix_error(); + } + + sh_name = _getname(shell); + if (stricmp(sh_name, "cmd.exe") == 0 || stricmp(sh_name, "4os2.exe") == 0) + opt = "/c"; + else + opt = "-c"; + + /* save current stdio fds + their flags, and set not inheritable */ + i = pipe_err = 0; + while (pipe_err >= 0 && i < 3) + { + pipe_err = stdio[i].handle = dup(i); + stdio[i].flags = fcntl(i, F_GETFD, 0); + fcntl(stdio[i].handle, F_SETFD, stdio[i].flags | FD_CLOEXEC); + i++; + } + if (pipe_err < 0) + { + /* didn't get them all saved - clean up and bail out */ + int saved_err = errno; + while (i-- > 0) + { + close(stdio[i].handle); + } + errno = saved_err; + return posix_error(); + } + + /* create pipe ends */ + file_count = 2; + if (n == POPEN_3) + file_count = 3; + i = pipe_err = 0; + while ((pipe_err == 0) && (i < file_count)) + pipe_err = pipe((int *)&p_fd[i++]); + if (pipe_err < 0) + { + /* didn't get them all made - clean up and bail out */ + while (i-- > 0) + { + close(p_fd[i].wr); + close(p_fd[i].rd); + } + errno = EPIPE; + return posix_error(); + } + + /* change the actual standard IO streams over temporarily, + * making the retained pipe ends non-inheritable + */ + pipe_err = 0; + + /* - stdin */ + if (dup2(p_fd[0].rd, 0) == 0) + { + close(p_fd[0].rd); + i = fcntl(p_fd[0].wr, F_GETFD, 0); + fcntl(p_fd[0].wr, F_SETFD, i | FD_CLOEXEC); + if ((p_s[0] = fdopen(p_fd[0].wr, wr_mode)) == NULL) + { + close(p_fd[0].wr); + pipe_err = -1; + } + } + else + { + pipe_err = -1; + } + + /* - stdout */ + if (pipe_err == 0) + { + if (dup2(p_fd[1].wr, 1) == 1) + { + close(p_fd[1].wr); + i = fcntl(p_fd[1].rd, F_GETFD, 0); + fcntl(p_fd[1].rd, F_SETFD, i | FD_CLOEXEC); + if ((p_s[1] = fdopen(p_fd[1].rd, rd_mode)) == NULL) + { + close(p_fd[1].rd); + pipe_err = -1; + } + } + else + { + pipe_err = -1; + } + } + + /* - stderr, as required */ + if (pipe_err == 0) + switch (n) + { + case POPEN_3: + { + if (dup2(p_fd[2].wr, 2) == 2) + { + close(p_fd[2].wr); + i = fcntl(p_fd[2].rd, F_GETFD, 0); + fcntl(p_fd[2].rd, F_SETFD, i | FD_CLOEXEC); + if ((p_s[2] = fdopen(p_fd[2].rd, rd_mode)) == NULL) + { + close(p_fd[2].rd); + pipe_err = -1; + } + } + else + { + pipe_err = -1; + } + break; + } + + case POPEN_4: + { + if (dup2(1, 2) != 2) + { + pipe_err = -1; + } + break; + } + } + + /* spawn the child process */ + if (pipe_err == 0) + { + pipe_pid = spawnlp(P_NOWAIT, shell, shell, opt, cmdstring, (char *)0); + if (pipe_pid == -1) + { + pipe_err = -1; + } + else + { + /* save the PID into the FILE structure + * NOTE: this implementation doesn't actually + * take advantage of this, but do it for + * completeness - AIM Apr01 + */ + for (i = 0; i < file_count; i++) + p_s[i]->_pid = pipe_pid; + } + } + + /* reset standard IO to normal */ + for (i = 0; i < 3; i++) + { + dup2(stdio[i].handle, i); + fcntl(i, F_SETFD, stdio[i].flags); + close(stdio[i].handle); + } + + /* if any remnant problems, clean up and bail out */ + if (pipe_err < 0) + { + for (i = 0; i < 3; i++) + { + close(p_fd[i].rd); + close(p_fd[i].wr); + } + errno = EPIPE; + return posix_error_with_filename(cmdstring); + } + + /* build tuple of file objects to return */ + if ((p_f[0] = PyFile_FromFile(p_s[0], cmdstring, wr_mode, _PyPclose)) != NULL) + PyFile_SetBufSize(p_f[0], bufsize); + if ((p_f[1] = PyFile_FromFile(p_s[1], cmdstring, rd_mode, _PyPclose)) != NULL) + PyFile_SetBufSize(p_f[1], bufsize); + if (n == POPEN_3) + { + if ((p_f[2] = PyFile_FromFile(p_s[2], cmdstring, rd_mode, _PyPclose)) != NULL) + PyFile_SetBufSize(p_f[0], bufsize); + f = Py_BuildValue("OOO", p_f[0], p_f[1], p_f[2]); + } + else + f = Py_BuildValue("OO", p_f[0], p_f[1]); + + /* + * Insert the files we've created into the process dictionary + * all referencing the list with the process handle and the + * initial number of files (see description below in _PyPclose). + * Since if _PyPclose later tried to wait on a process when all + * handles weren't closed, it could create a deadlock with the + * child, we spend some energy here to try to ensure that we + * either insert all file handles into the dictionary or none + * at all. It's a little clumsy with the various popen modes + * and variable number of files involved. + */ + if (!_PyPopenProcs) + { + _PyPopenProcs = PyDict_New(); + } + + if (_PyPopenProcs) + { + PyObject *procObj, *pidObj, *intObj, *fileObj[3]; + int ins_rc[3]; + + fileObj[0] = fileObj[1] = fileObj[2] = NULL; + ins_rc[0] = ins_rc[1] = ins_rc[2] = 0; + + procObj = PyList_New(2); + pidObj = PyInt_FromLong((long) pipe_pid); + intObj = PyInt_FromLong((long) file_count); + + if (procObj && pidObj && intObj) + { + PyList_SetItem(procObj, 0, pidObj); + PyList_SetItem(procObj, 1, intObj); + + fileObj[0] = PyLong_FromVoidPtr(p_s[0]); + if (fileObj[0]) + { + ins_rc[0] = PyDict_SetItem(_PyPopenProcs, + fileObj[0], + procObj); + } + fileObj[1] = PyLong_FromVoidPtr(p_s[1]); + if (fileObj[1]) + { + ins_rc[1] = PyDict_SetItem(_PyPopenProcs, + fileObj[1], + procObj); + } + if (file_count >= 3) + { + fileObj[2] = PyLong_FromVoidPtr(p_s[2]); + if (fileObj[2]) + { + ins_rc[2] = PyDict_SetItem(_PyPopenProcs, + fileObj[2], + procObj); + } + } + + if (ins_rc[0] < 0 || !fileObj[0] || + ins_rc[1] < 0 || (file_count > 1 && !fileObj[1]) || + ins_rc[2] < 0 || (file_count > 2 && !fileObj[2])) + { + /* Something failed - remove any dictionary + * entries that did make it. + */ + if (!ins_rc[0] && fileObj[0]) + { + PyDict_DelItem(_PyPopenProcs, + fileObj[0]); + } + if (!ins_rc[1] && fileObj[1]) + { + PyDict_DelItem(_PyPopenProcs, + fileObj[1]); + } + if (!ins_rc[2] && fileObj[2]) + { + PyDict_DelItem(_PyPopenProcs, + fileObj[2]); + } + } + } + + /* + * Clean up our localized references for the dictionary keys + * and value since PyDict_SetItem will Py_INCREF any copies + * that got placed in the dictionary. + */ + Py_XDECREF(procObj); + Py_XDECREF(fileObj[0]); + Py_XDECREF(fileObj[1]); + Py_XDECREF(fileObj[2]); + } + + /* Child is launched. */ + return f; +} + +/* + * Wrapper for fclose() to use for popen* files, so we can retrieve the + * exit code for the child process and return as a result of the close. + * + * This function uses the _PyPopenProcs dictionary in order to map the + * input file pointer to information about the process that was + * originally created by the popen* call that created the file pointer. + * The dictionary uses the file pointer as a key (with one entry + * inserted for each file returned by the original popen* call) and a + * single list object as the value for all files from a single call. + * The list object contains the Win32 process handle at [0], and a file + * count at [1], which is initialized to the total number of file + * handles using that list. + * + * This function closes whichever handle it is passed, and decrements + * the file count in the dictionary for the process handle pointed to + * by this file. On the last close (when the file count reaches zero), + * this function will wait for the child process and then return its + * exit code as the result of the close() operation. This permits the + * files to be closed in any order - it is always the close() of the + * final handle that will return the exit code. + * + * NOTE: This function is currently called with the GIL released. + * hence we use the GILState API to manage our state. + */ + +static int _PyPclose(FILE *file) +{ + int result; + int exit_code; + int pipe_pid; + PyObject *procObj, *pidObj, *intObj, *fileObj; + int file_count; +#ifdef WITH_THREAD + PyGILState_STATE state; +#endif + + /* Close the file handle first, to ensure it can't block the + * child from exiting if it's the last handle. + */ + result = fclose(file); + +#ifdef WITH_THREAD + state = PyGILState_Ensure(); +#endif + if (_PyPopenProcs) + { + if ((fileObj = PyLong_FromVoidPtr(file)) != NULL && + (procObj = PyDict_GetItem(_PyPopenProcs, + fileObj)) != NULL && + (pidObj = PyList_GetItem(procObj,0)) != NULL && + (intObj = PyList_GetItem(procObj,1)) != NULL) + { + pipe_pid = (int) PyInt_AsLong(pidObj); + file_count = (int) PyInt_AsLong(intObj); + + if (file_count > 1) + { + /* Still other files referencing process */ + file_count--; + PyList_SetItem(procObj,1, + PyInt_FromLong((long) file_count)); + } + else + { + /* Last file for this process */ + if (result != EOF && + waitpid(pipe_pid, &exit_code, 0) == pipe_pid) + { + /* extract exit status */ + if (WIFEXITED(exit_code)) + { + result = WEXITSTATUS(exit_code); + } + else + { + errno = EPIPE; + result = -1; + } + } + else + { + /* Indicate failure - this will cause the file object + * to raise an I/O error and translate the last + * error code from errno. We do have a problem with + * last errors that overlap the normal errno table, + * but that's a consistent problem with the file object. + */ + result = -1; + } + } + + /* Remove this file pointer from dictionary */ + PyDict_DelItem(_PyPopenProcs, fileObj); + + if (PyDict_Size(_PyPopenProcs) == 0) + { + Py_DECREF(_PyPopenProcs); + _PyPopenProcs = NULL; + } + + } /* if object retrieval ok */ + + Py_XDECREF(fileObj); + } /* if _PyPopenProcs */ + +#ifdef WITH_THREAD + PyGILState_Release(state); +#endif + return result; +} + +#endif /* PYCC_??? */ + +#elif defined(MS_WINDOWS) + +/* + * Portable 'popen' replacement for Win32. + * + * Written by Bill Tutt . Minor tweaks + * and 2.0 integration by Fredrik Lundh + * Return code handling by David Bolen . + */ + +#include +#include +#include + +/* These tell _PyPopen() wether to return 1, 2, or 3 file objects. */ +#define POPEN_1 1 +#define POPEN_2 2 +#define POPEN_3 3 +#define POPEN_4 4 + +static PyObject *_PyPopen(char *, int, int); +static int _PyPclose(FILE *file); + +/* + * Internal dictionary mapping popen* file pointers to process handles, + * for use when retrieving the process exit code. See _PyPclose() below + * for more information on this dictionary's use. + */ +static PyObject *_PyPopenProcs = NULL; + + +/* popen that works from a GUI. + * + * The result of this function is a pipe (file) connected to the + * processes stdin or stdout, depending on the requested mode. + */ + +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "r"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 'r') + tm = _O_RDONLY; + else if (*mode != 'w') { + PyErr_SetString(PyExc_ValueError, "popen() arg 2 must be 'r' or 'w'"); + return NULL; + } else + tm = _O_WRONLY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen() arg 3 must be -1"); + return NULL; + } + + if (*(mode+1) == 't') + f = _PyPopen(cmdstring, tm | _O_TEXT, POPEN_1); + else if (*(mode+1) == 'b') + f = _PyPopen(cmdstring, tm | _O_BINARY, POPEN_1); + else + f = _PyPopen(cmdstring, tm | _O_TEXT, POPEN_1); + + return f; +} + +/* Variation on win32pipe.popen + * + * The result of this function is a pipe (file) connected to the + * process's stdin, and a pipe connected to the process's stdout. + */ + +static PyObject * +win32_popen2(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm=0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen2", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = _O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "popen2() arg 2 must be 't' or 'b'"); + return NULL; + } else + tm = _O_BINARY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen2() arg 3 must be -1"); + return NULL; + } + + f = _PyPopen(cmdstring, tm, POPEN_2); + + return f; +} + +/* + * Variation on + * + * The result of this function is 3 pipes - the process's stdin, + * stdout and stderr + */ + +static PyObject * +win32_popen3(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen3", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = _O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "popen3() arg 2 must be 't' or 'b'"); + return NULL; + } else + tm = _O_BINARY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen3() arg 3 must be -1"); + return NULL; + } + + f = _PyPopen(cmdstring, tm, POPEN_3); + + return f; +} + +/* + * Variation on win32pipe.popen + * + * The result of this function is 2 pipes - the processes stdin, + * and stdout+stderr combined as a single pipe. + */ + +static PyObject * +win32_popen4(PyObject *self, PyObject *args) +{ + PyObject *f; + int tm = 0; + + char *cmdstring; + char *mode = "t"; + int bufsize = -1; + if (!PyArg_ParseTuple(args, "s|si:popen4", &cmdstring, &mode, &bufsize)) + return NULL; + + if (*mode == 't') + tm = _O_TEXT; + else if (*mode != 'b') { + PyErr_SetString(PyExc_ValueError, "popen4() arg 2 must be 't' or 'b'"); + return NULL; + } else + tm = _O_BINARY; + + if (bufsize != -1) { + PyErr_SetString(PyExc_ValueError, "popen4() arg 3 must be -1"); + return NULL; + } + + f = _PyPopen(cmdstring, tm, POPEN_4); + + return f; +} + +static BOOL +_PyPopenCreateProcess(char *cmdstring, + HANDLE hStdin, + HANDLE hStdout, + HANDLE hStderr, + HANDLE *hProcess) +{ +#ifndef MS_XBOX + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + DWORD dwProcessFlags = 0; /* no NEW_CONSOLE by default for Ctrl+C handling */ + char *s1,*s2, *s3 = " /c "; + const char *szConsoleSpawn = "w9xpopen.exe"; + int i; + int x; + + if (i = GetEnvironmentVariable("COMSPEC",NULL,0)) { + char *comshell; + + s1 = (char *)alloca(i); + if (!(x = GetEnvironmentVariable("COMSPEC", s1, i))) + return x; + + /* Explicitly check if we are using COMMAND.COM. If we are + * then use the w9xpopen hack. + */ + comshell = s1 + x; + while (comshell >= s1 && *comshell != '\\') + --comshell; + ++comshell; + + if (GetVersion() < 0x80000000 && + _stricmp(comshell, "command.com") != 0) { + /* NT/2000 and not using command.com. */ + x = i + strlen(s3) + strlen(cmdstring) + 1; + s2 = (char *)alloca(x); + ZeroMemory(s2, x); + PyOS_snprintf(s2, x, "%s%s%s", s1, s3, cmdstring); + } + else { + /* + * Oh gag, we're on Win9x or using COMMAND.COM. Use + * the workaround listed in KB: Q150956 + */ + char modulepath[_MAX_PATH]; + struct stat statinfo; + GetModuleFileName(NULL, modulepath, sizeof(modulepath)); + for (i = x = 0; modulepath[i]; i++) + if (modulepath[i] == SEP) + x = i+1; + modulepath[x] = '\0'; + /* Create the full-name to w9xpopen, so we can test it exists */ + strncat(modulepath, + szConsoleSpawn, + (sizeof(modulepath)/sizeof(modulepath[0])) + -strlen(modulepath)); + if (stat(modulepath, &statinfo) != 0) { + /* Eeek - file-not-found - possibly an embedding + situation - see if we can locate it in sys.prefix + */ + strncpy(modulepath, + Py_GetExecPrefix(), + sizeof(modulepath)/sizeof(modulepath[0])); + if (modulepath[strlen(modulepath)-1] != '\\') + strcat(modulepath, "\\"); + strncat(modulepath, + szConsoleSpawn, + (sizeof(modulepath)/sizeof(modulepath[0])) + -strlen(modulepath)); + /* No where else to look - raise an easily identifiable + error, rather than leaving Windows to report + "file not found" - as the user is probably blissfully + unaware this shim EXE is used, and it will confuse them. + (well, it confused me for a while ;-) + */ + if (stat(modulepath, &statinfo) != 0) { + PyErr_Format(PyExc_RuntimeError, + "Can not locate '%s' which is needed " + "for popen to work with your shell " + "or platform.", + szConsoleSpawn); + return FALSE; + } + } + x = i + strlen(s3) + strlen(cmdstring) + 1 + + strlen(modulepath) + + strlen(szConsoleSpawn) + 1; + + s2 = (char *)alloca(x); + ZeroMemory(s2, x); + /* To maintain correct argument passing semantics, + we pass the command-line as it stands, and allow + quoting to be applied. w9xpopen.exe will then + use its argv vector, and re-quote the necessary + args for the ultimate child process. + */ + PyOS_snprintf( + s2, x, + "\"%s\" %s%s%s", + modulepath, + s1, + s3, + cmdstring); + /* Not passing CREATE_NEW_CONSOLE has been known to + cause random failures on win9x. Specifically a + dialog: + "Your program accessed mem currently in use at xxx" + and a hopeful warning about the stability of your + system. + Cost is Ctrl+C wont kill children, but anyone + who cares can have a go! + */ + dwProcessFlags |= CREATE_NEW_CONSOLE; + } + } + + /* Could be an else here to try cmd.exe / command.com in the path + Now we'll just error out.. */ + else { + PyErr_SetString(PyExc_RuntimeError, + "Cannot locate a COMSPEC environment variable to " + "use as the shell"); + return FALSE; + } + + ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + siStartInfo.hStdInput = hStdin; + siStartInfo.hStdOutput = hStdout; + siStartInfo.hStdError = hStderr; + siStartInfo.wShowWindow = SW_HIDE; + + if (CreateProcess(NULL, + s2, + NULL, + NULL, + TRUE, + dwProcessFlags, + NULL, + NULL, + &siStartInfo, + &piProcInfo) ) { + /* Close the handles now so anyone waiting is woken. */ + CloseHandle(piProcInfo.hThread); + + /* Return process handle */ + *hProcess = piProcInfo.hProcess; + return TRUE; + } + win32_error("CreateProcess", s2); +#endif + return FALSE; +} + +/* The following code is based off of KB: Q190351 */ + +static PyObject * +_PyPopen(char *cmdstring, int mode, int n) +{ +#ifndef MS_XBOX + HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr, + hChildStderrRd, hChildStderrWr, hChildStdinWrDup, hChildStdoutRdDup, + hChildStderrRdDup, hProcess; /* hChildStdoutWrDup; */ + + SECURITY_ATTRIBUTES saAttr; + BOOL fSuccess; + int fd1, fd2, fd3; + FILE *f1, *f2, *f3; + long file_count; + PyObject *f; + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) + return win32_error("CreatePipe", NULL); + + /* Create new output read handle and the input write handle. Set + * the inheritance properties to FALSE. Otherwise, the child inherits + * these handles; resulting in non-closeable handles to the pipes + * being created. */ + fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr, + GetCurrentProcess(), &hChildStdinWrDup, 0, + FALSE, + DUPLICATE_SAME_ACCESS); + if (!fSuccess) + return win32_error("DuplicateHandle", NULL); + + /* Close the inheritable version of ChildStdin + that we're using. */ + CloseHandle(hChildStdinWr); + + if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) + return win32_error("CreatePipe", NULL); + + fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, + GetCurrentProcess(), &hChildStdoutRdDup, 0, + FALSE, DUPLICATE_SAME_ACCESS); + if (!fSuccess) + return win32_error("DuplicateHandle", NULL); + + /* Close the inheritable version of ChildStdout + that we're using. */ + CloseHandle(hChildStdoutRd); + + if (n != POPEN_4) { + if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0)) + return win32_error("CreatePipe", NULL); + fSuccess = DuplicateHandle(GetCurrentProcess(), + hChildStderrRd, + GetCurrentProcess(), + &hChildStderrRdDup, 0, + FALSE, DUPLICATE_SAME_ACCESS); + if (!fSuccess) + return win32_error("DuplicateHandle", NULL); + /* Close the inheritable version of ChildStdErr that we're using. */ + CloseHandle(hChildStderrRd); + } + + switch (n) { + case POPEN_1: + switch (mode & (_O_RDONLY | _O_TEXT | _O_BINARY | _O_WRONLY)) { + case _O_WRONLY | _O_TEXT: + /* Case for writing to child Stdin in text mode. */ + fd1 = _open_osfhandle((long)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, "w"); + f = PyFile_FromFile(f1, cmdstring, "w", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdoutRdDup); + CloseHandle(hChildStderrRdDup); + break; + + case _O_RDONLY | _O_TEXT: + /* Case for reading from child Stdout in text mode. */ + fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode); + f1 = _fdopen(fd1, "r"); + f = PyFile_FromFile(f1, cmdstring, "r", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdinWrDup); + CloseHandle(hChildStderrRdDup); + break; + + case _O_RDONLY | _O_BINARY: + /* Case for readinig from child Stdout in binary mode. */ + fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode); + f1 = _fdopen(fd1, "rb"); + f = PyFile_FromFile(f1, cmdstring, "rb", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdinWrDup); + CloseHandle(hChildStderrRdDup); + break; + + case _O_WRONLY | _O_BINARY: + /* Case for writing to child Stdin in binary mode. */ + fd1 = _open_osfhandle((long)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, "wb"); + f = PyFile_FromFile(f1, cmdstring, "wb", _PyPclose); + PyFile_SetBufSize(f, 0); + /* We don't care about these pipes anymore, so close them. */ + CloseHandle(hChildStdoutRdDup); + CloseHandle(hChildStderrRdDup); + break; + } + file_count = 1; + break; + + case POPEN_2: + case POPEN_4: + { + char *m1, *m2; + PyObject *p1, *p2; + + if (mode & _O_TEXT) { + m1 = "r"; + m2 = "w"; + } else { + m1 = "rb"; + m2 = "wb"; + } + + fd1 = _open_osfhandle((long)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, m2); + fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode); + f2 = _fdopen(fd2, m1); + p1 = PyFile_FromFile(f1, cmdstring, m2, _PyPclose); + PyFile_SetBufSize(p1, 0); + p2 = PyFile_FromFile(f2, cmdstring, m1, _PyPclose); + PyFile_SetBufSize(p2, 0); + + if (n != 4) + CloseHandle(hChildStderrRdDup); + + f = Py_BuildValue("OO",p1,p2); + Py_XDECREF(p1); + Py_XDECREF(p2); + file_count = 2; + break; + } + + case POPEN_3: + { + char *m1, *m2; + PyObject *p1, *p2, *p3; + + if (mode & _O_TEXT) { + m1 = "r"; + m2 = "w"; + } else { + m1 = "rb"; + m2 = "wb"; + } + + fd1 = _open_osfhandle((long)hChildStdinWrDup, mode); + f1 = _fdopen(fd1, m2); + fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode); + f2 = _fdopen(fd2, m1); + fd3 = _open_osfhandle((long)hChildStderrRdDup, mode); + f3 = _fdopen(fd3, m1); + p1 = PyFile_FromFile(f1, cmdstring, m2, _PyPclose); + p2 = PyFile_FromFile(f2, cmdstring, m1, _PyPclose); + p3 = PyFile_FromFile(f3, cmdstring, m1, _PyPclose); + PyFile_SetBufSize(p1, 0); + PyFile_SetBufSize(p2, 0); + PyFile_SetBufSize(p3, 0); + f = Py_BuildValue("OOO",p1,p2,p3); + Py_XDECREF(p1); + Py_XDECREF(p2); + Py_XDECREF(p3); + file_count = 3; + break; + } + } + + if (n == POPEN_4) { + if (!_PyPopenCreateProcess(cmdstring, + hChildStdinRd, + hChildStdoutWr, + hChildStdoutWr, + &hProcess)) + return NULL; + } + else { + if (!_PyPopenCreateProcess(cmdstring, + hChildStdinRd, + hChildStdoutWr, + hChildStderrWr, + &hProcess)) + return NULL; + } + + /* + * Insert the files we've created into the process dictionary + * all referencing the list with the process handle and the + * initial number of files (see description below in _PyPclose). + * Since if _PyPclose later tried to wait on a process when all + * handles weren't closed, it could create a deadlock with the + * child, we spend some energy here to try to ensure that we + * either insert all file handles into the dictionary or none + * at all. It's a little clumsy with the various popen modes + * and variable number of files involved. + */ + if (!_PyPopenProcs) { + _PyPopenProcs = PyDict_New(); + } + + if (_PyPopenProcs) { + PyObject *procObj, *hProcessObj, *intObj, *fileObj[3]; + int ins_rc[3]; + + fileObj[0] = fileObj[1] = fileObj[2] = NULL; + ins_rc[0] = ins_rc[1] = ins_rc[2] = 0; + + procObj = PyList_New(2); + hProcessObj = PyLong_FromVoidPtr(hProcess); + intObj = PyInt_FromLong(file_count); + + if (procObj && hProcessObj && intObj) { + PyList_SetItem(procObj,0,hProcessObj); + PyList_SetItem(procObj,1,intObj); + + fileObj[0] = PyLong_FromVoidPtr(f1); + if (fileObj[0]) { + ins_rc[0] = PyDict_SetItem(_PyPopenProcs, + fileObj[0], + procObj); + } + if (file_count >= 2) { + fileObj[1] = PyLong_FromVoidPtr(f2); + if (fileObj[1]) { + ins_rc[1] = PyDict_SetItem(_PyPopenProcs, + fileObj[1], + procObj); + } + } + if (file_count >= 3) { + fileObj[2] = PyLong_FromVoidPtr(f3); + if (fileObj[2]) { + ins_rc[2] = PyDict_SetItem(_PyPopenProcs, + fileObj[2], + procObj); + } + } + + if (ins_rc[0] < 0 || !fileObj[0] || + ins_rc[1] < 0 || (file_count > 1 && !fileObj[1]) || + ins_rc[2] < 0 || (file_count > 2 && !fileObj[2])) { + /* Something failed - remove any dictionary + * entries that did make it. + */ + if (!ins_rc[0] && fileObj[0]) { + PyDict_DelItem(_PyPopenProcs, + fileObj[0]); + } + if (!ins_rc[1] && fileObj[1]) { + PyDict_DelItem(_PyPopenProcs, + fileObj[1]); + } + if (!ins_rc[2] && fileObj[2]) { + PyDict_DelItem(_PyPopenProcs, + fileObj[2]); + } + } + } + + /* + * Clean up our localized references for the dictionary keys + * and value since PyDict_SetItem will Py_INCREF any copies + * that got placed in the dictionary. + */ + Py_XDECREF(procObj); + Py_XDECREF(fileObj[0]); + Py_XDECREF(fileObj[1]); + Py_XDECREF(fileObj[2]); + } + + /* Child is launched. Close the parents copy of those pipe + * handles that only the child should have open. You need to + * make sure that no handles to the write end of the output pipe + * are maintained in this process or else the pipe will not close + * when the child process exits and the ReadFile will hang. */ + + if (!CloseHandle(hChildStdinRd)) + return win32_error("CloseHandle", NULL); + + if (!CloseHandle(hChildStdoutWr)) + return win32_error("CloseHandle", NULL); + + if ((n != 4) && (!CloseHandle(hChildStderrWr))) + return win32_error("CloseHandle", NULL); + + return f; +#else + return 0; +#endif +} + +/* + * Wrapper for fclose() to use for popen* files, so we can retrieve the + * exit code for the child process and return as a result of the close. + * + * This function uses the _PyPopenProcs dictionary in order to map the + * input file pointer to information about the process that was + * originally created by the popen* call that created the file pointer. + * The dictionary uses the file pointer as a key (with one entry + * inserted for each file returned by the original popen* call) and a + * single list object as the value for all files from a single call. + * The list object contains the Win32 process handle at [0], and a file + * count at [1], which is initialized to the total number of file + * handles using that list. + * + * This function closes whichever handle it is passed, and decrements + * the file count in the dictionary for the process handle pointed to + * by this file. On the last close (when the file count reaches zero), + * this function will wait for the child process and then return its + * exit code as the result of the close() operation. This permits the + * files to be closed in any order - it is always the close() of the + * final handle that will return the exit code. + * + * NOTE: This function is currently called with the GIL released. + * hence we use the GILState API to manage our state. + */ + +static int _PyPclose(FILE *file) +{ + int result; + DWORD exit_code; + HANDLE hProcess; + PyObject *procObj, *hProcessObj, *intObj, *fileObj; + long file_count; +#ifdef WITH_THREAD + PyGILState_STATE state; +#endif + + /* Close the file handle first, to ensure it can't block the + * child from exiting if it's the last handle. + */ + result = fclose(file); +#ifdef WITH_THREAD + state = PyGILState_Ensure(); +#endif + if (_PyPopenProcs) { + if ((fileObj = PyLong_FromVoidPtr(file)) != NULL && + (procObj = PyDict_GetItem(_PyPopenProcs, + fileObj)) != NULL && + (hProcessObj = PyList_GetItem(procObj,0)) != NULL && + (intObj = PyList_GetItem(procObj,1)) != NULL) { + + hProcess = PyLong_AsVoidPtr(hProcessObj); + file_count = PyInt_AsLong(intObj); + + if (file_count > 1) { + /* Still other files referencing process */ + file_count--; + PyList_SetItem(procObj,1, + PyInt_FromLong(file_count)); + } else { + /* Last file for this process */ +#ifndef MS_XBOX + if (result != EOF && + WaitForSingleObject(hProcess, INFINITE) != WAIT_FAILED && + GetExitCodeProcess(hProcess, &exit_code)) { + /* Possible truncation here in 16-bit environments, but + * real exit codes are just the lower byte in any event. + */ + result = exit_code; +#else + if (result != EOF && + WaitForSingleObject(hProcess, INFINITE) != WAIT_FAILED) { + /* Possible truncation here in 16-bit environments, but + * real exit codes are just the lower byte in any event. + */ + result = 0; +#endif + } else { + /* Indicate failure - this will cause the file object + * to raise an I/O error and translate the last Win32 + * error code from errno. We do have a problem with + * last errors that overlap the normal errno table, + * but that's a consistent problem with the file object. + */ + if (result != EOF) { + /* If the error wasn't from the fclose(), then + * set errno for the file object error handling. + */ + errno = GetLastError(); + } + result = -1; + } + + /* Free up the native handle at this point */ + CloseHandle(hProcess); + } + + /* Remove this file pointer from dictionary */ + PyDict_DelItem(_PyPopenProcs, fileObj); + + if (PyDict_Size(_PyPopenProcs) == 0) { + Py_DECREF(_PyPopenProcs); + _PyPopenProcs = NULL; + } + + } /* if object retrieval ok */ + + Py_XDECREF(fileObj); + } /* if _PyPopenProcs */ + +#ifdef WITH_THREAD + PyGILState_Release(state); +#endif + return result; +} + +#else /* which OS? */ +static PyObject * +posix_popen(PyObject *self, PyObject *args) +{ + char *name; + char *mode = "r"; + int bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "s|si:popen", &name, &mode, &bufsize)) + return NULL; + /* Strip mode of binary or text modifiers */ + if (strcmp(mode, "rb") == 0 || strcmp(mode, "rt") == 0) + mode = "r"; + else if (strcmp(mode, "wb") == 0 || strcmp(mode, "wt") == 0) + mode = "w"; + Py_BEGIN_ALLOW_THREADS + fp = popen(name, mode); + Py_END_ALLOW_THREADS + if (fp == NULL) + return posix_error(); + f = PyFile_FromFile(fp, name, mode, pclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +#endif /* PYOS_??? */ +#endif /* HAVE_POPEN */ + + +#ifdef HAVE_SETUID +PyDoc_STRVAR(posix_setuid__doc__, +"setuid(uid)\n\n\ +Set the current process's user id."); + +static PyObject * +posix_setuid(PyObject *self, PyObject *args) +{ + int uid; + if (!PyArg_ParseTuple(args, "i:setuid", &uid)) + return NULL; + if (setuid(uid) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETUID */ + + +#ifdef HAVE_SETEUID +PyDoc_STRVAR(posix_seteuid__doc__, +"seteuid(uid)\n\n\ +Set the current process's effective user id."); + +static PyObject * +posix_seteuid (PyObject *self, PyObject *args) +{ + int euid; + if (!PyArg_ParseTuple(args, "i", &euid)) { + return NULL; + } else if (seteuid(euid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETEUID */ + +#ifdef HAVE_SETEGID +PyDoc_STRVAR(posix_setegid__doc__, +"setegid(gid)\n\n\ +Set the current process's effective group id."); + +static PyObject * +posix_setegid (PyObject *self, PyObject *args) +{ + int egid; + if (!PyArg_ParseTuple(args, "i", &egid)) { + return NULL; + } else if (setegid(egid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETEGID */ + +#ifdef HAVE_SETREUID +PyDoc_STRVAR(posix_setreuid__doc__, +"seteuid(ruid, euid)\n\n\ +Set the current process's real and effective user ids."); + +static PyObject * +posix_setreuid (PyObject *self, PyObject *args) +{ + int ruid, euid; + if (!PyArg_ParseTuple(args, "ii", &ruid, &euid)) { + return NULL; + } else if (setreuid(ruid, euid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETREUID */ + +#ifdef HAVE_SETREGID +PyDoc_STRVAR(posix_setregid__doc__, +"setegid(rgid, egid)\n\n\ +Set the current process's real and effective group ids."); + +static PyObject * +posix_setregid (PyObject *self, PyObject *args) +{ + int rgid, egid; + if (!PyArg_ParseTuple(args, "ii", &rgid, &egid)) { + return NULL; + } else if (setregid(rgid, egid) < 0) { + return posix_error(); + } else { + Py_INCREF(Py_None); + return Py_None; + } +} +#endif /* HAVE_SETREGID */ + +#ifdef HAVE_SETGID +PyDoc_STRVAR(posix_setgid__doc__, +"setgid(gid)\n\n\ +Set the current process's group id."); + +static PyObject * +posix_setgid(PyObject *self, PyObject *args) +{ + int gid; + if (!PyArg_ParseTuple(args, "i:setgid", &gid)) + return NULL; + if (setgid(gid) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETGID */ + +#ifdef HAVE_SETGROUPS +PyDoc_STRVAR(posix_setgroups__doc__, +"setgroups(list)\n\n\ +Set the groups of the current process to list."); + +static PyObject * +posix_setgroups(PyObject *self, PyObject *args) +{ + PyObject *groups; + int i, len; + gid_t grouplist[MAX_GROUPS]; + + if (!PyArg_ParseTuple(args, "O:setgid", &groups)) + return NULL; + if (!PySequence_Check(groups)) { + PyErr_SetString(PyExc_TypeError, "setgroups argument must be a sequence"); + return NULL; + } + len = PySequence_Size(groups); + if (len > MAX_GROUPS) { + PyErr_SetString(PyExc_ValueError, "too many groups"); + return NULL; + } + for(i = 0; i < len; i++) { + PyObject *elem; + elem = PySequence_GetItem(groups, i); + if (!elem) + return NULL; + if (!PyInt_Check(elem)) { + PyErr_SetString(PyExc_TypeError, + "groups must be integers"); + Py_DECREF(elem); + return NULL; + } + /* XXX: check that value fits into gid_t. */ + grouplist[i] = PyInt_AsLong(elem); + Py_DECREF(elem); + } + + if (setgroups(len, grouplist) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETGROUPS */ + +#ifdef HAVE_WAITPID +PyDoc_STRVAR(posix_waitpid__doc__, +"waitpid(pid, options) -> (pid, status)\n\n\ +Wait for completion of a given child process."); + +static PyObject * +posix_waitpid(PyObject *self, PyObject *args) +{ + int pid, options; +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "ii:waitpid", &pid, &options)) + return NULL; + Py_BEGIN_ALLOW_THREADS + pid = waitpid(pid, &status, options); + Py_END_ALLOW_THREADS + if (pid == -1) + return posix_error(); + else + return Py_BuildValue("ii", pid, status_i); +} + +#elif defined(HAVE_CWAIT) + +/* MS C has a variant of waitpid() that's usable for most purposes. */ +PyDoc_STRVAR(posix_waitpid__doc__, +"waitpid(pid, options) -> (pid, status << 8)\n\n" +"Wait for completion of a given process. options is ignored on Windows."); + +static PyObject * +posix_waitpid(PyObject *self, PyObject *args) +{ + int pid, options; + int status; + + if (!PyArg_ParseTuple(args, "ii:waitpid", &pid, &options)) + return NULL; + Py_BEGIN_ALLOW_THREADS + pid = _cwait(&status, pid, options); + Py_END_ALLOW_THREADS + if (pid == -1) + return posix_error(); + else + /* shift the status left a byte so this is more like the + POSIX waitpid */ + return Py_BuildValue("ii", pid, status << 8); +} +#endif /* HAVE_WAITPID || HAVE_CWAIT */ + +#ifdef HAVE_WAIT +PyDoc_STRVAR(posix_wait__doc__, +"wait() -> (pid, status)\n\n\ +Wait for completion of a child process."); + +static PyObject * +posix_wait(PyObject *self, PyObject *noargs) +{ + int pid; +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + + status_i = 0; + Py_BEGIN_ALLOW_THREADS + pid = wait(&status); + Py_END_ALLOW_THREADS + if (pid == -1) + return posix_error(); + else + return Py_BuildValue("ii", pid, status_i); +#undef status_i +} +#endif + + +PyDoc_STRVAR(posix_lstat__doc__, +"lstat(path) -> stat result\n\n\ +Like stat(path), but do not follow symbolic links."); + +static PyObject * +posix_lstat(PyObject *self, PyObject *args) +{ +#ifdef HAVE_LSTAT + return posix_do_stat(self, args, "et:lstat", lstat, NULL, NULL); +#else /* !HAVE_LSTAT */ +#ifdef MS_WINDOWS + return posix_do_stat(self, args, "et:lstat", STAT, "U:lstat", _wstati64); +#else + return posix_do_stat(self, args, "et:lstat", STAT, NULL, NULL); +#endif +#endif /* !HAVE_LSTAT */ +} + + +#ifdef HAVE_READLINK +PyDoc_STRVAR(posix_readlink__doc__, +"readlink(path) -> path\n\n\ +Return a string representing the path to which the symbolic link points."); + +static PyObject * +posix_readlink(PyObject *self, PyObject *args) +{ + char buf[MAXPATHLEN]; + char *path; + int n; + if (!PyArg_ParseTuple(args, "s:readlink", &path)) + return NULL; + Py_BEGIN_ALLOW_THREADS + n = readlink(path, buf, (int) sizeof buf); + Py_END_ALLOW_THREADS + if (n < 0) + return posix_error_with_filename(path); + return PyString_FromStringAndSize(buf, n); +} +#endif /* HAVE_READLINK */ + + +#ifdef HAVE_SYMLINK +PyDoc_STRVAR(posix_symlink__doc__, +"symlink(src, dst)\n\n\ +Create a symbolic link pointing to src named dst."); + +static PyObject * +posix_symlink(PyObject *self, PyObject *args) +{ + return posix_2str(args, "etet:symlink", symlink, NULL, NULL); +} +#endif /* HAVE_SYMLINK */ + + +#ifdef HAVE_TIMES +#ifndef HZ +#define HZ 60 /* Universal constant :-) */ +#endif /* HZ */ + +#if defined(PYCC_VACPP) && defined(PYOS_OS2) +static long +system_uptime(void) +{ + ULONG value = 0; + + Py_BEGIN_ALLOW_THREADS + DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &value, sizeof(value)); + Py_END_ALLOW_THREADS + + return value; +} + +static PyObject * +posix_times(PyObject *self, PyObject *noargs) +{ + /* Currently Only Uptime is Provided -- Others Later */ + return Py_BuildValue("ddddd", + (double)0 /* t.tms_utime / HZ */, + (double)0 /* t.tms_stime / HZ */, + (double)0 /* t.tms_cutime / HZ */, + (double)0 /* t.tms_cstime / HZ */, + (double)system_uptime() / 1000); +} +#else /* not OS2 */ +static PyObject * +posix_times(PyObject *self, PyObject *noargs) +{ + struct tms t; + clock_t c; + errno = 0; + c = times(&t); + if (c == (clock_t) -1) + return posix_error(); + return Py_BuildValue("ddddd", + (double)t.tms_utime / HZ, + (double)t.tms_stime / HZ, + (double)t.tms_cutime / HZ, + (double)t.tms_cstime / HZ, + (double)c / HZ); +} +#endif /* not OS2 */ +#endif /* HAVE_TIMES */ + + +#if defined(MS_WINDOWS) && !defined(MS_XBOX) +#define HAVE_TIMES /* so the method table will pick it up */ +static PyObject * +posix_times(PyObject *self, PyObject *noargs) +{ + FILETIME create, exit, kernel, user; + HANDLE hProc; + hProc = GetCurrentProcess(); + GetProcessTimes(hProc, &create, &exit, &kernel, &user); + /* The fields of a FILETIME structure are the hi and lo part + of a 64-bit value expressed in 100 nanosecond units. + 1e7 is one second in such units; 1e-7 the inverse. + 429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7. + */ + return Py_BuildValue( + "ddddd", + (double)(kernel.dwHighDateTime*429.4967296 + + kernel.dwLowDateTime*1e-7), + (double)(user.dwHighDateTime*429.4967296 + + user.dwLowDateTime*1e-7), + (double)0, + (double)0, + (double)0); +} +#endif /* MS_WINDOWS */ + +#ifdef HAVE_TIMES +PyDoc_STRVAR(posix_times__doc__, +"times() -> (utime, stime, cutime, cstime, elapsed_time)\n\n\ +Return a tuple of floating point numbers indicating process times."); +#endif + + +#ifdef HAVE_SETSID +PyDoc_STRVAR(posix_setsid__doc__, +"setsid()\n\n\ +Call the system call setsid()."); + +static PyObject * +posix_setsid(PyObject *self, PyObject *noargs) +{ + if (setsid() < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETSID */ + +#ifdef HAVE_SETPGID +PyDoc_STRVAR(posix_setpgid__doc__, +"setpgid(pid, pgrp)\n\n\ +Call the system call setpgid()."); + +static PyObject * +posix_setpgid(PyObject *self, PyObject *args) +{ + int pid, pgrp; + if (!PyArg_ParseTuple(args, "ii:setpgid", &pid, &pgrp)) + return NULL; + if (setpgid(pid, pgrp) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_SETPGID */ + + +#ifdef HAVE_TCGETPGRP +PyDoc_STRVAR(posix_tcgetpgrp__doc__, +"tcgetpgrp(fd) -> pgid\n\n\ +Return the process group associated with the terminal given by a fd."); + +static PyObject * +posix_tcgetpgrp(PyObject *self, PyObject *args) +{ + int fd, pgid; + if (!PyArg_ParseTuple(args, "i:tcgetpgrp", &fd)) + return NULL; + pgid = tcgetpgrp(fd); + if (pgid < 0) + return posix_error(); + return PyInt_FromLong((long)pgid); +} +#endif /* HAVE_TCGETPGRP */ + + +#ifdef HAVE_TCSETPGRP +PyDoc_STRVAR(posix_tcsetpgrp__doc__, +"tcsetpgrp(fd, pgid)\n\n\ +Set the process group associated with the terminal given by a fd."); + +static PyObject * +posix_tcsetpgrp(PyObject *self, PyObject *args) +{ + int fd, pgid; + if (!PyArg_ParseTuple(args, "ii:tcsetpgrp", &fd, &pgid)) + return NULL; + if (tcsetpgrp(fd, pgid) < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAVE_TCSETPGRP */ + +/* Functions acting on file descriptors */ + +PyDoc_STRVAR(posix_open__doc__, +"open(filename, flag [, mode=0777]) -> fd\n\n\ +Open a file (for low level IO)."); + +static PyObject * +posix_open(PyObject *self, PyObject *args) +{ + char *file = NULL; + int flag; + int mode = 0777; + int fd; + +#ifdef Py_WIN_WIDE_FILENAMES + if (unicode_file_names()) { + PyUnicodeObject *po; + if (PyArg_ParseTuple(args, "Ui|i:mkdir", &po, &flag, &mode)) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + fd = _wopen(PyUnicode_AS_UNICODE(po), flag, mode); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error(); + return PyInt_FromLong((long)fd); + } + /* Drop the argument parsing error as narrow strings + are also valid. */ + PyErr_Clear(); + } +#endif + + if (!PyArg_ParseTuple(args, "eti|i", + Py_FileSystemDefaultEncoding, &file, + &flag, &mode)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + fd = open(file, flag, mode); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error_with_allocated_filename(file); + PyMem_Free(file); + return PyInt_FromLong((long)fd); +} + + +PyDoc_STRVAR(posix_close__doc__, +"close(fd)\n\n\ +Close a file descriptor (for low level IO)."); + +static PyObject * +posix_close(PyObject *self, PyObject *args) +{ + int fd, res; + if (!PyArg_ParseTuple(args, "i:close", &fd)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = close(fd); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(posix_dup__doc__, +"dup(fd) -> fd2\n\n\ +Return a duplicate of a file descriptor."); + +static PyObject * +posix_dup(PyObject *self, PyObject *args) +{ + int fd; + if (!PyArg_ParseTuple(args, "i:dup", &fd)) + return NULL; + Py_BEGIN_ALLOW_THREADS + fd = dup(fd); + Py_END_ALLOW_THREADS + if (fd < 0) + return posix_error(); + return PyInt_FromLong((long)fd); +} + + +PyDoc_STRVAR(posix_dup2__doc__, +"dup2(fd, fd2)\n\n\ +Duplicate file descriptor."); + +static PyObject * +posix_dup2(PyObject *self, PyObject *args) +{ + int fd, fd2, res; + if (!PyArg_ParseTuple(args, "ii:dup2", &fd, &fd2)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = dup2(fd, fd2); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} + + +PyDoc_STRVAR(posix_lseek__doc__, +"lseek(fd, pos, how) -> newpos\n\n\ +Set the current position of a file descriptor."); + +static PyObject * +posix_lseek(PyObject *self, PyObject *args) +{ + int fd, how; +#if defined(MS_WIN64) || defined(MS_WINDOWS) + PY_LONG_LONG pos, res; +#else + off_t pos, res; +#endif + PyObject *posobj; + if (!PyArg_ParseTuple(args, "iOi:lseek", &fd, &posobj, &how)) + return NULL; +#ifdef SEEK_SET + /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */ + switch (how) { + case 0: how = SEEK_SET; break; + case 1: how = SEEK_CUR; break; + case 2: how = SEEK_END; break; + } +#endif /* SEEK_END */ + +#if !defined(HAVE_LARGEFILE_SUPPORT) + pos = PyInt_AsLong(posobj); +#else + pos = PyLong_Check(posobj) ? + PyLong_AsLongLong(posobj) : PyInt_AsLong(posobj); +#endif + if (PyErr_Occurred()) + return NULL; + + Py_BEGIN_ALLOW_THREADS +#if defined(MS_WIN64) || defined(MS_WINDOWS) + res = _lseeki64(fd, pos, how); +#else + res = lseek(fd, pos, how); +#endif + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + +#if !defined(HAVE_LARGEFILE_SUPPORT) + return PyInt_FromLong(res); +#else + return PyLong_FromLongLong(res); +#endif +} + + +PyDoc_STRVAR(posix_read__doc__, +"read(fd, buffersize) -> string\n\n\ +Read a file descriptor."); + +static PyObject * +posix_read(PyObject *self, PyObject *args) +{ + int fd, size, n; + PyObject *buffer; + if (!PyArg_ParseTuple(args, "ii:read", &fd, &size)) + return NULL; + buffer = PyString_FromStringAndSize((char *)NULL, size); + if (buffer == NULL) + return NULL; + Py_BEGIN_ALLOW_THREADS + n = read(fd, PyString_AsString(buffer), size); + Py_END_ALLOW_THREADS + if (n < 0) { + Py_DECREF(buffer); + return posix_error(); + } + if (n != size) + _PyString_Resize(&buffer, n); + return buffer; +} + + +PyDoc_STRVAR(posix_write__doc__, +"write(fd, string) -> byteswritten\n\n\ +Write a string to a file descriptor."); + +static PyObject * +posix_write(PyObject *self, PyObject *args) +{ + int fd, size; + char *buffer; + if (!PyArg_ParseTuple(args, "is#:write", &fd, &buffer, &size)) + return NULL; + Py_BEGIN_ALLOW_THREADS + size = write(fd, buffer, size); + Py_END_ALLOW_THREADS + if (size < 0) + return posix_error(); + return PyInt_FromLong((long)size); +} + + +PyDoc_STRVAR(posix_fstat__doc__, +"fstat(fd) -> stat result\n\n\ +Like stat(), but for an open file descriptor."); + +static PyObject * +posix_fstat(PyObject *self, PyObject *args) +{ + int fd; + STRUCT_STAT st; + int res; + if (!PyArg_ParseTuple(args, "i:fstat", &fd)) + return NULL; +#ifdef __VMS + /* on OpenVMS we must ensure that all bytes are written to the file */ + fsync(fd); +#endif + Py_BEGIN_ALLOW_THREADS + res = FSTAT(fd, &st); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error(); + + return _pystat_fromstructstat(st); +} + + +PyDoc_STRVAR(posix_fdopen__doc__, +"fdopen(fd [, mode='r' [, bufsize]]) -> file_object\n\n\ +Return an open file object connected to a file descriptor."); + +static PyObject * +posix_fdopen(PyObject *self, PyObject *args) +{ + int fd; + char *mode = "r"; + int bufsize = -1; + FILE *fp; + PyObject *f; + if (!PyArg_ParseTuple(args, "i|si", &fd, &mode, &bufsize)) + return NULL; + + if (mode[0] != 'r' && mode[0] != 'w' && mode[0] != 'a') { + PyErr_Format(PyExc_ValueError, + "invalid file mode '%s'", mode); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + fp = fdopen(fd, mode); + Py_END_ALLOW_THREADS + if (fp == NULL) + return posix_error(); + f = PyFile_FromFile(fp, "", mode, fclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +PyDoc_STRVAR(posix_isatty__doc__, +"isatty(fd) -> bool\n\n\ +Return True if the file descriptor 'fd' is an open file descriptor\n\ +connected to the slave end of a terminal."); + +static PyObject * +posix_isatty(PyObject *self, PyObject *args) +{ + int fd; + if (!PyArg_ParseTuple(args, "i:isatty", &fd)) + return NULL; + return PyBool_FromLong(isatty(fd)); +} + +#ifdef HAVE_PIPE +PyDoc_STRVAR(posix_pipe__doc__, +"pipe() -> (read_end, write_end)\n\n\ +Create a pipe."); + +static PyObject * +posix_pipe(PyObject *self, PyObject *noargs) +{ +#if defined(PYOS_OS2) + HFILE read, write; + APIRET rc; + + Py_BEGIN_ALLOW_THREADS + rc = DosCreatePipe( &read, &write, 4096); + Py_END_ALLOW_THREADS + if (rc != NO_ERROR) + return os2_error(rc); + + return Py_BuildValue("(ii)", read, write); +#else +#if !defined(MS_WINDOWS) + int fds[2]; + int res; + Py_BEGIN_ALLOW_THREADS + res = pipe(fds); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error(); + return Py_BuildValue("(ii)", fds[0], fds[1]); +#else /* MS_WINDOWS */ +#ifndef MS_XBOX + HANDLE read, write; + int read_fd, write_fd; + BOOL ok; + Py_BEGIN_ALLOW_THREADS + ok = CreatePipe(&read, &write, NULL, 0); + Py_END_ALLOW_THREADS + if (!ok) + return win32_error("CreatePipe", NULL); + read_fd = _open_osfhandle((Py_intptr_t)read, 0); + write_fd = _open_osfhandle((Py_intptr_t)write, 1); + return Py_BuildValue("(ii)", read_fd, write_fd); +#else + return Py_BuildValue("(ii)", 0, 0); +#endif /* MS_XBOX */ +#endif /* MS_WINDOWS */ +#endif +} +#endif /* HAVE_PIPE */ + + +#ifdef HAVE_MKFIFO +PyDoc_STRVAR(posix_mkfifo__doc__, +"mkfifo(filename [, mode=0666])\n\n\ +Create a FIFO (a POSIX named pipe)."); + +static PyObject * +posix_mkfifo(PyObject *self, PyObject *args) +{ + char *filename; + int mode = 0666; + int res; + if (!PyArg_ParseTuple(args, "s|i:mkfifo", &filename, &mode)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = mkfifo(filename, mode); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + + +#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) +PyDoc_STRVAR(posix_mknod__doc__, +"mknod(filename [, mode=0600, device])\n\n\ +Create a filesystem node (file, device special file or named pipe)\n\ +named filename. mode specifies both the permissions to use and the\n\ +type of node to be created, being combined (bitwise OR) with one of\n\ +S_IFREG, S_IFCHR, S_IFBLK, and S_IFIFO. For S_IFCHR and S_IFBLK,\n\ +device defines the newly created device special file (probably using\n\ +os.makedev()), otherwise it is ignored."); + + +static PyObject * +posix_mknod(PyObject *self, PyObject *args) +{ + char *filename; + int mode = 0600; + int device = 0; + int res; + if (!PyArg_ParseTuple(args, "s|ii:mknod", &filename, &mode, &device)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = mknod(filename, mode, device); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_DEVICE_MACROS +PyDoc_STRVAR(posix_major__doc__, +"major(device) -> major number\n\ +Extracts a device major number from a raw device number."); + +static PyObject * +posix_major(PyObject *self, PyObject *args) +{ + int device; + if (!PyArg_ParseTuple(args, "i:major", &device)) + return NULL; + return PyInt_FromLong((long)major(device)); +} + +PyDoc_STRVAR(posix_minor__doc__, +"minor(device) -> minor number\n\ +Extracts a device minor number from a raw device number."); + +static PyObject * +posix_minor(PyObject *self, PyObject *args) +{ + int device; + if (!PyArg_ParseTuple(args, "i:minor", &device)) + return NULL; + return PyInt_FromLong((long)minor(device)); +} + +PyDoc_STRVAR(posix_makedev__doc__, +"makedev(major, minor) -> device number\n\ +Composes a raw device number from the major and minor device numbers."); + +static PyObject * +posix_makedev(PyObject *self, PyObject *args) +{ + int major, minor; + if (!PyArg_ParseTuple(args, "ii:makedev", &major, &minor)) + return NULL; + return PyInt_FromLong((long)makedev(major, minor)); +} +#endif /* device macros */ + + +#ifdef HAVE_FTRUNCATE +PyDoc_STRVAR(posix_ftruncate__doc__, +"ftruncate(fd, length)\n\n\ +Truncate a file to a specified length."); + +static PyObject * +posix_ftruncate(PyObject *self, PyObject *args) +{ + int fd; + off_t length; + int res; + PyObject *lenobj; + + if (!PyArg_ParseTuple(args, "iO:ftruncate", &fd, &lenobj)) + return NULL; + +#if !defined(HAVE_LARGEFILE_SUPPORT) + length = PyInt_AsLong(lenobj); +#else + length = PyLong_Check(lenobj) ? + PyLong_AsLongLong(lenobj) : PyInt_AsLong(lenobj); +#endif + if (PyErr_Occurred()) + return NULL; + + Py_BEGIN_ALLOW_THREADS + res = ftruncate(fd, length); + Py_END_ALLOW_THREADS + if (res < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_PUTENV +PyDoc_STRVAR(posix_putenv__doc__, +"putenv(key, value)\n\n\ +Change or add an environment variable."); + +/* Save putenv() parameters as values here, so we can collect them when they + * get re-set with another call for the same key. */ +static PyObject *posix_putenv_garbage; + +static PyObject * +posix_putenv(PyObject *self, PyObject *args) +{ + char *s1, *s2; + char *new; + PyObject *newstr; + size_t len; + + if (!PyArg_ParseTuple(args, "ss:putenv", &s1, &s2)) + return NULL; + +#if defined(PYOS_OS2) + if (stricmp(s1, "BEGINLIBPATH") == 0) { + APIRET rc; + + rc = DosSetExtLIBPATH(s2, BEGIN_LIBPATH); + if (rc != NO_ERROR) + return os2_error(rc); + + } else if (stricmp(s1, "ENDLIBPATH") == 0) { + APIRET rc; + + rc = DosSetExtLIBPATH(s2, END_LIBPATH); + if (rc != NO_ERROR) + return os2_error(rc); + } else { +#endif + + /* XXX This can leak memory -- not easy to fix :-( */ + len = strlen(s1) + strlen(s2) + 2; + /* len includes space for a trailing \0; the size arg to + PyString_FromStringAndSize does not count that */ + newstr = PyString_FromStringAndSize(NULL, (int)len - 1); + if (newstr == NULL) + return PyErr_NoMemory(); + new = PyString_AS_STRING(newstr); + PyOS_snprintf(new, len, "%s=%s", s1, s2); + if (putenv(new)) { + Py_DECREF(newstr); + posix_error(); + return NULL; + } + /* Install the first arg and newstr in posix_putenv_garbage; + * this will cause previous value to be collected. This has to + * happen after the real putenv() call because the old value + * was still accessible until then. */ + if (PyDict_SetItem(posix_putenv_garbage, + PyTuple_GET_ITEM(args, 0), newstr)) { + /* really not much we can do; just leak */ + PyErr_Clear(); + } + else { + Py_DECREF(newstr); + } + +#if defined(PYOS_OS2) + } +#endif + Py_INCREF(Py_None); + return Py_None; +} +#endif /* putenv */ + +#ifdef HAVE_UNSETENV +PyDoc_STRVAR(posix_unsetenv__doc__, +"unsetenv(key)\n\n\ +Delete an environment variable."); + +static PyObject * +posix_unsetenv(PyObject *self, PyObject *args) +{ + char *s1; + + if (!PyArg_ParseTuple(args, "s:unsetenv", &s1)) + return NULL; + + unsetenv(s1); + + /* Remove the key from posix_putenv_garbage; + * this will cause it to be collected. This has to + * happen after the real unsetenv() call because the + * old value was still accessible until then. + */ + if (PyDict_DelItem(posix_putenv_garbage, + PyTuple_GET_ITEM(args, 0))) { + /* really not much we can do; just leak */ + PyErr_Clear(); + } + + Py_INCREF(Py_None); + return Py_None; +} +#endif /* unsetenv */ + +#ifdef HAVE_STRERROR +PyDoc_STRVAR(posix_strerror__doc__, +"strerror(code) -> string\n\n\ +Translate an error code to a message string."); + +static PyObject * +posix_strerror(PyObject *self, PyObject *args) +{ + int code; + char *message; + if (!PyArg_ParseTuple(args, "i:strerror", &code)) + return NULL; + message = strerror(code); + if (message == NULL) { + PyErr_SetString(PyExc_ValueError, + "strerror() argument out of range"); + return NULL; + } + return PyString_FromString(message); +} +#endif /* strerror */ + + +#ifdef HAVE_SYS_WAIT_H + +#ifdef WCOREDUMP +PyDoc_STRVAR(posix_WCOREDUMP__doc__, +"WCOREDUMP(status) -> bool\n\n\ +Return True if the process returning 'status' was dumped to a core file."); + +static PyObject * +posix_WCOREDUMP(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WCOREDUMP", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WCOREDUMP(status)); +#undef status_i +} +#endif /* WCOREDUMP */ + +#ifdef WIFCONTINUED +PyDoc_STRVAR(posix_WIFCONTINUED__doc__, +"WIFCONTINUED(status) -> bool\n\n\ +Return True if the process returning 'status' was continued from a\n\ +job control stop."); + +static PyObject * +posix_WIFCONTINUED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WCONTINUED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFCONTINUED(status)); +#undef status_i +} +#endif /* WIFCONTINUED */ + +#ifdef WIFSTOPPED +PyDoc_STRVAR(posix_WIFSTOPPED__doc__, +"WIFSTOPPED(status) -> bool\n\n\ +Return True if the process returning 'status' was stopped."); + +static PyObject * +posix_WIFSTOPPED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WIFSTOPPED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFSTOPPED(status)); +#undef status_i +} +#endif /* WIFSTOPPED */ + +#ifdef WIFSIGNALED +PyDoc_STRVAR(posix_WIFSIGNALED__doc__, +"WIFSIGNALED(status) -> bool\n\n\ +Return True if the process returning 'status' was terminated by a signal."); + +static PyObject * +posix_WIFSIGNALED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WIFSIGNALED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFSIGNALED(status)); +#undef status_i +} +#endif /* WIFSIGNALED */ + +#ifdef WIFEXITED +PyDoc_STRVAR(posix_WIFEXITED__doc__, +"WIFEXITED(status) -> bool\n\n\ +Return true if the process returning 'status' exited using the exit()\n\ +system call."); + +static PyObject * +posix_WIFEXITED(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WIFEXITED", &status_i)) + { + return NULL; + } + + return PyBool_FromLong(WIFEXITED(status)); +#undef status_i +} +#endif /* WIFEXITED */ + +#ifdef WEXITSTATUS +PyDoc_STRVAR(posix_WEXITSTATUS__doc__, +"WEXITSTATUS(status) -> integer\n\n\ +Return the process return code from 'status'."); + +static PyObject * +posix_WEXITSTATUS(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WEXITSTATUS", &status_i)) + { + return NULL; + } + + return Py_BuildValue("i", WEXITSTATUS(status)); +#undef status_i +} +#endif /* WEXITSTATUS */ + +#ifdef WTERMSIG +PyDoc_STRVAR(posix_WTERMSIG__doc__, +"WTERMSIG(status) -> integer\n\n\ +Return the signal that terminated the process that provided the 'status'\n\ +value."); + +static PyObject * +posix_WTERMSIG(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WTERMSIG", &status_i)) + { + return NULL; + } + + return Py_BuildValue("i", WTERMSIG(status)); +#undef status_i +} +#endif /* WTERMSIG */ + +#ifdef WSTOPSIG +PyDoc_STRVAR(posix_WSTOPSIG__doc__, +"WSTOPSIG(status) -> integer\n\n\ +Return the signal that stopped the process that provided\n\ +the 'status' value."); + +static PyObject * +posix_WSTOPSIG(PyObject *self, PyObject *args) +{ +#ifdef UNION_WAIT + union wait status; +#define status_i (status.w_status) +#else + int status; +#define status_i status +#endif + status_i = 0; + + if (!PyArg_ParseTuple(args, "i:WSTOPSIG", &status_i)) + { + return NULL; + } + + return Py_BuildValue("i", WSTOPSIG(status)); +#undef status_i +} +#endif /* WSTOPSIG */ + +#endif /* HAVE_SYS_WAIT_H */ + + +#if defined(HAVE_FSTATVFS) +#ifdef _SCO_DS +/* SCO OpenServer 5.0 and later requires _SVID3 before it reveals the + needed definitions in sys/statvfs.h */ +#define _SVID3 +#endif +#include + +static PyObject* +_pystatvfs_fromstructstatvfs(struct statvfs st) { + PyObject *v = PyStructSequence_New(&StatVFSResultType); + if (v == NULL) + return NULL; + +#if !defined(HAVE_LARGEFILE_SUPPORT) + PyStructSequence_SET_ITEM(v, 0, PyInt_FromLong((long) st.f_bsize)); + PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long) st.f_frsize)); + PyStructSequence_SET_ITEM(v, 2, PyInt_FromLong((long) st.f_blocks)); + PyStructSequence_SET_ITEM(v, 3, PyInt_FromLong((long) st.f_bfree)); + PyStructSequence_SET_ITEM(v, 4, PyInt_FromLong((long) st.f_bavail)); + PyStructSequence_SET_ITEM(v, 5, PyInt_FromLong((long) st.f_files)); + PyStructSequence_SET_ITEM(v, 6, PyInt_FromLong((long) st.f_ffree)); + PyStructSequence_SET_ITEM(v, 7, PyInt_FromLong((long) st.f_favail)); + PyStructSequence_SET_ITEM(v, 8, PyInt_FromLong((long) st.f_flag)); + PyStructSequence_SET_ITEM(v, 9, PyInt_FromLong((long) st.f_namemax)); +#else + PyStructSequence_SET_ITEM(v, 0, PyInt_FromLong((long) st.f_bsize)); + PyStructSequence_SET_ITEM(v, 1, PyInt_FromLong((long) st.f_frsize)); + PyStructSequence_SET_ITEM(v, 2, + PyLong_FromLongLong((PY_LONG_LONG) st.f_blocks)); + PyStructSequence_SET_ITEM(v, 3, + PyLong_FromLongLong((PY_LONG_LONG) st.f_bfree)); + PyStructSequence_SET_ITEM(v, 4, + PyLong_FromLongLong((PY_LONG_LONG) st.f_bavail)); + PyStructSequence_SET_ITEM(v, 5, + PyLong_FromLongLong((PY_LONG_LONG) st.f_files)); + PyStructSequence_SET_ITEM(v, 6, + PyLong_FromLongLong((PY_LONG_LONG) st.f_ffree)); + PyStructSequence_SET_ITEM(v, 7, + PyLong_FromLongLong((PY_LONG_LONG) st.f_favail)); + PyStructSequence_SET_ITEM(v, 8, PyInt_FromLong((long) st.f_flag)); + PyStructSequence_SET_ITEM(v, 9, PyInt_FromLong((long) st.f_namemax)); +#endif + + return v; +} + +PyDoc_STRVAR(posix_fstatvfs__doc__, +"fstatvfs(fd) -> statvfs result\n\n\ +Perform an fstatvfs system call on the given fd."); + +static PyObject * +posix_fstatvfs(PyObject *self, PyObject *args) +{ + int fd, res; + struct statvfs st; + + if (!PyArg_ParseTuple(args, "i:fstatvfs", &fd)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = fstatvfs(fd, &st); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error(); + + return _pystatvfs_fromstructstatvfs(st); +} +#endif /* HAVE_FSTATVFS */ + + +#if defined(HAVE_STATVFS) +#include + +PyDoc_STRVAR(posix_statvfs__doc__, +"statvfs(path) -> statvfs result\n\n\ +Perform a statvfs system call on the given path."); + +static PyObject * +posix_statvfs(PyObject *self, PyObject *args) +{ + char *path; + int res; + struct statvfs st; + if (!PyArg_ParseTuple(args, "s:statvfs", &path)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = statvfs(path, &st); + Py_END_ALLOW_THREADS + if (res != 0) + return posix_error_with_filename(path); + + return _pystatvfs_fromstructstatvfs(st); +} +#endif /* HAVE_STATVFS */ + + +#ifdef HAVE_TEMPNAM +PyDoc_STRVAR(posix_tempnam__doc__, +"tempnam([dir[, prefix]]) -> string\n\n\ +Return a unique name for a temporary file.\n\ +The directory and a prefix may be specified as strings; they may be omitted\n\ +or None if not needed."); + +static PyObject * +posix_tempnam(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + char *dir = NULL; + char *pfx = NULL; + char *name; + + if (!PyArg_ParseTuple(args, "|zz:tempnam", &dir, &pfx)) + return NULL; + + if (PyErr_Warn(PyExc_RuntimeWarning, + "tempnam is a potential security risk to your program") < 0) + return NULL; + +#ifdef MS_WINDOWS + name = _tempnam(dir, pfx); +#else + name = tempnam(dir, pfx); +#endif + if (name == NULL) + return PyErr_NoMemory(); + result = PyString_FromString(name); + free(name); + return result; +} +#endif + + +#ifdef HAVE_TMPFILE +PyDoc_STRVAR(posix_tmpfile__doc__, +"tmpfile() -> file object\n\n\ +Create a temporary file with no directory entries."); + +static PyObject * +posix_tmpfile(PyObject *self, PyObject *noargs) +{ + FILE *fp; + + fp = tmpfile(); + if (fp == NULL) + return posix_error(); + return PyFile_FromFile(fp, "", "w+b", fclose); +} +#endif + + +#ifdef HAVE_TMPNAM +PyDoc_STRVAR(posix_tmpnam__doc__, +"tmpnam() -> string\n\n\ +Return a unique name for a temporary file."); + +static PyObject * +posix_tmpnam(PyObject *self, PyObject *noargs) +{ + char buffer[L_tmpnam]; + char *name; + + if (PyErr_Warn(PyExc_RuntimeWarning, + "tmpnam is a potential security risk to your program") < 0) + return NULL; + +#ifdef USE_TMPNAM_R + name = tmpnam_r(buffer); +#else + name = tmpnam(buffer); +#endif + if (name == NULL) { + PyErr_SetObject(PyExc_OSError, + Py_BuildValue("is", 0, +#ifdef USE_TMPNAM_R + "unexpected NULL from tmpnam_r" +#else + "unexpected NULL from tmpnam" +#endif + )); + return NULL; + } + return PyString_FromString(buffer); +} +#endif + + +/* This is used for fpathconf(), pathconf(), confstr() and sysconf(). + * It maps strings representing configuration variable names to + * integer values, allowing those functions to be called with the + * magic names instead of polluting the module's namespace with tons of + * rarely-used constants. There are three separate tables that use + * these definitions. + * + * This code is always included, even if none of the interfaces that + * need it are included. The #if hackery needed to avoid it would be + * sufficiently pervasive that it's not worth the loss of readability. + */ +struct constdef { + char *name; + long value; +}; + +static int +conv_confname(PyObject *arg, int *valuep, struct constdef *table, + size_t tablesize) +{ + if (PyInt_Check(arg)) { + *valuep = PyInt_AS_LONG(arg); + return 1; + } + if (PyString_Check(arg)) { + /* look up the value in the table using a binary search */ + size_t lo = 0; + size_t mid; + size_t hi = tablesize; + int cmp; + char *confname = PyString_AS_STRING(arg); + while (lo < hi) { + mid = (lo + hi) / 2; + cmp = strcmp(confname, table[mid].name); + if (cmp < 0) + hi = mid; + else if (cmp > 0) + lo = mid + 1; + else { + *valuep = table[mid].value; + return 1; + } + } + PyErr_SetString(PyExc_ValueError, "unrecognized configuration name"); + } + else + PyErr_SetString(PyExc_TypeError, + "configuration names must be strings or integers"); + return 0; +} + + +#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF) +static struct constdef posix_constants_pathconf[] = { +#ifdef _PC_ABI_AIO_XFER_MAX + {"PC_ABI_AIO_XFER_MAX", _PC_ABI_AIO_XFER_MAX}, +#endif +#ifdef _PC_ABI_ASYNC_IO + {"PC_ABI_ASYNC_IO", _PC_ABI_ASYNC_IO}, +#endif +#ifdef _PC_ASYNC_IO + {"PC_ASYNC_IO", _PC_ASYNC_IO}, +#endif +#ifdef _PC_CHOWN_RESTRICTED + {"PC_CHOWN_RESTRICTED", _PC_CHOWN_RESTRICTED}, +#endif +#ifdef _PC_FILESIZEBITS + {"PC_FILESIZEBITS", _PC_FILESIZEBITS}, +#endif +#ifdef _PC_LAST + {"PC_LAST", _PC_LAST}, +#endif +#ifdef _PC_LINK_MAX + {"PC_LINK_MAX", _PC_LINK_MAX}, +#endif +#ifdef _PC_MAX_CANON + {"PC_MAX_CANON", _PC_MAX_CANON}, +#endif +#ifdef _PC_MAX_INPUT + {"PC_MAX_INPUT", _PC_MAX_INPUT}, +#endif +#ifdef _PC_NAME_MAX + {"PC_NAME_MAX", _PC_NAME_MAX}, +#endif +#ifdef _PC_NO_TRUNC + {"PC_NO_TRUNC", _PC_NO_TRUNC}, +#endif +#ifdef _PC_PATH_MAX + {"PC_PATH_MAX", _PC_PATH_MAX}, +#endif +#ifdef _PC_PIPE_BUF + {"PC_PIPE_BUF", _PC_PIPE_BUF}, +#endif +#ifdef _PC_PRIO_IO + {"PC_PRIO_IO", _PC_PRIO_IO}, +#endif +#ifdef _PC_SOCK_MAXBUF + {"PC_SOCK_MAXBUF", _PC_SOCK_MAXBUF}, +#endif +#ifdef _PC_SYNC_IO + {"PC_SYNC_IO", _PC_SYNC_IO}, +#endif +#ifdef _PC_VDISABLE + {"PC_VDISABLE", _PC_VDISABLE}, +#endif +}; + +static int +conv_path_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_pathconf, + sizeof(posix_constants_pathconf) + / sizeof(struct constdef)); +} +#endif + +#ifdef HAVE_FPATHCONF +PyDoc_STRVAR(posix_fpathconf__doc__, +"fpathconf(fd, name) -> integer\n\n\ +Return the configuration limit name for the file descriptor fd.\n\ +If there is no limit, return -1."); + +static PyObject * +posix_fpathconf(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name, fd; + + if (PyArg_ParseTuple(args, "iO&:fpathconf", &fd, + conv_path_confname, &name)) { + long limit; + + errno = 0; + limit = fpathconf(fd, name); + if (limit == -1 && errno != 0) + posix_error(); + else + result = PyInt_FromLong(limit); + } + return result; +} +#endif + + +#ifdef HAVE_PATHCONF +PyDoc_STRVAR(posix_pathconf__doc__, +"pathconf(path, name) -> integer\n\n\ +Return the configuration limit name for the file or directory path.\n\ +If there is no limit, return -1."); + +static PyObject * +posix_pathconf(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name; + char *path; + + if (PyArg_ParseTuple(args, "sO&:pathconf", &path, + conv_path_confname, &name)) { + long limit; + + errno = 0; + limit = pathconf(path, name); + if (limit == -1 && errno != 0) { + if (errno == EINVAL) + /* could be a path or name problem */ + posix_error(); + else + posix_error_with_filename(path); + } + else + result = PyInt_FromLong(limit); + } + return result; +} +#endif + +#ifdef HAVE_CONFSTR +static struct constdef posix_constants_confstr[] = { +#ifdef _CS_ARCHITECTURE + {"CS_ARCHITECTURE", _CS_ARCHITECTURE}, +#endif +#ifdef _CS_HOSTNAME + {"CS_HOSTNAME", _CS_HOSTNAME}, +#endif +#ifdef _CS_HW_PROVIDER + {"CS_HW_PROVIDER", _CS_HW_PROVIDER}, +#endif +#ifdef _CS_HW_SERIAL + {"CS_HW_SERIAL", _CS_HW_SERIAL}, +#endif +#ifdef _CS_INITTAB_NAME + {"CS_INITTAB_NAME", _CS_INITTAB_NAME}, +#endif +#ifdef _CS_LFS64_CFLAGS + {"CS_LFS64_CFLAGS", _CS_LFS64_CFLAGS}, +#endif +#ifdef _CS_LFS64_LDFLAGS + {"CS_LFS64_LDFLAGS", _CS_LFS64_LDFLAGS}, +#endif +#ifdef _CS_LFS64_LIBS + {"CS_LFS64_LIBS", _CS_LFS64_LIBS}, +#endif +#ifdef _CS_LFS64_LINTFLAGS + {"CS_LFS64_LINTFLAGS", _CS_LFS64_LINTFLAGS}, +#endif +#ifdef _CS_LFS_CFLAGS + {"CS_LFS_CFLAGS", _CS_LFS_CFLAGS}, +#endif +#ifdef _CS_LFS_LDFLAGS + {"CS_LFS_LDFLAGS", _CS_LFS_LDFLAGS}, +#endif +#ifdef _CS_LFS_LIBS + {"CS_LFS_LIBS", _CS_LFS_LIBS}, +#endif +#ifdef _CS_LFS_LINTFLAGS + {"CS_LFS_LINTFLAGS", _CS_LFS_LINTFLAGS}, +#endif +#ifdef _CS_MACHINE + {"CS_MACHINE", _CS_MACHINE}, +#endif +#ifdef _CS_PATH + {"CS_PATH", _CS_PATH}, +#endif +#ifdef _CS_RELEASE + {"CS_RELEASE", _CS_RELEASE}, +#endif +#ifdef _CS_SRPC_DOMAIN + {"CS_SRPC_DOMAIN", _CS_SRPC_DOMAIN}, +#endif +#ifdef _CS_SYSNAME + {"CS_SYSNAME", _CS_SYSNAME}, +#endif +#ifdef _CS_VERSION + {"CS_VERSION", _CS_VERSION}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_CFLAGS + {"CS_XBS5_ILP32_OFF32_CFLAGS", _CS_XBS5_ILP32_OFF32_CFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LDFLAGS + {"CS_XBS5_ILP32_OFF32_LDFLAGS", _CS_XBS5_ILP32_OFF32_LDFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LIBS + {"CS_XBS5_ILP32_OFF32_LIBS", _CS_XBS5_ILP32_OFF32_LIBS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LINTFLAGS + {"CS_XBS5_ILP32_OFF32_LINTFLAGS", _CS_XBS5_ILP32_OFF32_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_CFLAGS + {"CS_XBS5_ILP32_OFFBIG_CFLAGS", _CS_XBS5_ILP32_OFFBIG_CFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LDFLAGS + {"CS_XBS5_ILP32_OFFBIG_LDFLAGS", _CS_XBS5_ILP32_OFFBIG_LDFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LIBS + {"CS_XBS5_ILP32_OFFBIG_LIBS", _CS_XBS5_ILP32_OFFBIG_LIBS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LINTFLAGS + {"CS_XBS5_ILP32_OFFBIG_LINTFLAGS", _CS_XBS5_ILP32_OFFBIG_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_CFLAGS + {"CS_XBS5_LP64_OFF64_CFLAGS", _CS_XBS5_LP64_OFF64_CFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LDFLAGS + {"CS_XBS5_LP64_OFF64_LDFLAGS", _CS_XBS5_LP64_OFF64_LDFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LIBS + {"CS_XBS5_LP64_OFF64_LIBS", _CS_XBS5_LP64_OFF64_LIBS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LINTFLAGS + {"CS_XBS5_LP64_OFF64_LINTFLAGS", _CS_XBS5_LP64_OFF64_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_CFLAGS + {"CS_XBS5_LPBIG_OFFBIG_CFLAGS", _CS_XBS5_LPBIG_OFFBIG_CFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LDFLAGS + {"CS_XBS5_LPBIG_OFFBIG_LDFLAGS", _CS_XBS5_LPBIG_OFFBIG_LDFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LIBS + {"CS_XBS5_LPBIG_OFFBIG_LIBS", _CS_XBS5_LPBIG_OFFBIG_LIBS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS + {"CS_XBS5_LPBIG_OFFBIG_LINTFLAGS", _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS}, +#endif +#ifdef _MIPS_CS_AVAIL_PROCESSORS + {"MIPS_CS_AVAIL_PROCESSORS", _MIPS_CS_AVAIL_PROCESSORS}, +#endif +#ifdef _MIPS_CS_BASE + {"MIPS_CS_BASE", _MIPS_CS_BASE}, +#endif +#ifdef _MIPS_CS_HOSTID + {"MIPS_CS_HOSTID", _MIPS_CS_HOSTID}, +#endif +#ifdef _MIPS_CS_HW_NAME + {"MIPS_CS_HW_NAME", _MIPS_CS_HW_NAME}, +#endif +#ifdef _MIPS_CS_NUM_PROCESSORS + {"MIPS_CS_NUM_PROCESSORS", _MIPS_CS_NUM_PROCESSORS}, +#endif +#ifdef _MIPS_CS_OSREL_MAJ + {"MIPS_CS_OSREL_MAJ", _MIPS_CS_OSREL_MAJ}, +#endif +#ifdef _MIPS_CS_OSREL_MIN + {"MIPS_CS_OSREL_MIN", _MIPS_CS_OSREL_MIN}, +#endif +#ifdef _MIPS_CS_OSREL_PATCH + {"MIPS_CS_OSREL_PATCH", _MIPS_CS_OSREL_PATCH}, +#endif +#ifdef _MIPS_CS_OS_NAME + {"MIPS_CS_OS_NAME", _MIPS_CS_OS_NAME}, +#endif +#ifdef _MIPS_CS_OS_PROVIDER + {"MIPS_CS_OS_PROVIDER", _MIPS_CS_OS_PROVIDER}, +#endif +#ifdef _MIPS_CS_PROCESSORS + {"MIPS_CS_PROCESSORS", _MIPS_CS_PROCESSORS}, +#endif +#ifdef _MIPS_CS_SERIAL + {"MIPS_CS_SERIAL", _MIPS_CS_SERIAL}, +#endif +#ifdef _MIPS_CS_VENDOR + {"MIPS_CS_VENDOR", _MIPS_CS_VENDOR}, +#endif +}; + +static int +conv_confstr_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_confstr, + sizeof(posix_constants_confstr) + / sizeof(struct constdef)); +} + +PyDoc_STRVAR(posix_confstr__doc__, +"confstr(name) -> string\n\n\ +Return a string-valued system configuration variable."); + +static PyObject * +posix_confstr(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name; + char buffer[64]; + + if (PyArg_ParseTuple(args, "O&:confstr", conv_confstr_confname, &name)) { + int len = confstr(name, buffer, sizeof(buffer)); + + errno = 0; + if (len == 0) { + if (errno != 0) + posix_error(); + else + result = PyString_FromString(""); + } + else { + if (len >= sizeof(buffer)) { + result = PyString_FromStringAndSize(NULL, len); + if (result != NULL) + confstr(name, PyString_AS_STRING(result), len+1); + } + else + result = PyString_FromString(buffer); + } + } + return result; +} +#endif + + +#ifdef HAVE_SYSCONF +static struct constdef posix_constants_sysconf[] = { +#ifdef _SC_2_CHAR_TERM + {"SC_2_CHAR_TERM", _SC_2_CHAR_TERM}, +#endif +#ifdef _SC_2_C_BIND + {"SC_2_C_BIND", _SC_2_C_BIND}, +#endif +#ifdef _SC_2_C_DEV + {"SC_2_C_DEV", _SC_2_C_DEV}, +#endif +#ifdef _SC_2_C_VERSION + {"SC_2_C_VERSION", _SC_2_C_VERSION}, +#endif +#ifdef _SC_2_FORT_DEV + {"SC_2_FORT_DEV", _SC_2_FORT_DEV}, +#endif +#ifdef _SC_2_FORT_RUN + {"SC_2_FORT_RUN", _SC_2_FORT_RUN}, +#endif +#ifdef _SC_2_LOCALEDEF + {"SC_2_LOCALEDEF", _SC_2_LOCALEDEF}, +#endif +#ifdef _SC_2_SW_DEV + {"SC_2_SW_DEV", _SC_2_SW_DEV}, +#endif +#ifdef _SC_2_UPE + {"SC_2_UPE", _SC_2_UPE}, +#endif +#ifdef _SC_2_VERSION + {"SC_2_VERSION", _SC_2_VERSION}, +#endif +#ifdef _SC_ABI_ASYNCHRONOUS_IO + {"SC_ABI_ASYNCHRONOUS_IO", _SC_ABI_ASYNCHRONOUS_IO}, +#endif +#ifdef _SC_ACL + {"SC_ACL", _SC_ACL}, +#endif +#ifdef _SC_AIO_LISTIO_MAX + {"SC_AIO_LISTIO_MAX", _SC_AIO_LISTIO_MAX}, +#endif +#ifdef _SC_AIO_MAX + {"SC_AIO_MAX", _SC_AIO_MAX}, +#endif +#ifdef _SC_AIO_PRIO_DELTA_MAX + {"SC_AIO_PRIO_DELTA_MAX", _SC_AIO_PRIO_DELTA_MAX}, +#endif +#ifdef _SC_ARG_MAX + {"SC_ARG_MAX", _SC_ARG_MAX}, +#endif +#ifdef _SC_ASYNCHRONOUS_IO + {"SC_ASYNCHRONOUS_IO", _SC_ASYNCHRONOUS_IO}, +#endif +#ifdef _SC_ATEXIT_MAX + {"SC_ATEXIT_MAX", _SC_ATEXIT_MAX}, +#endif +#ifdef _SC_AUDIT + {"SC_AUDIT", _SC_AUDIT}, +#endif +#ifdef _SC_AVPHYS_PAGES + {"SC_AVPHYS_PAGES", _SC_AVPHYS_PAGES}, +#endif +#ifdef _SC_BC_BASE_MAX + {"SC_BC_BASE_MAX", _SC_BC_BASE_MAX}, +#endif +#ifdef _SC_BC_DIM_MAX + {"SC_BC_DIM_MAX", _SC_BC_DIM_MAX}, +#endif +#ifdef _SC_BC_SCALE_MAX + {"SC_BC_SCALE_MAX", _SC_BC_SCALE_MAX}, +#endif +#ifdef _SC_BC_STRING_MAX + {"SC_BC_STRING_MAX", _SC_BC_STRING_MAX}, +#endif +#ifdef _SC_CAP + {"SC_CAP", _SC_CAP}, +#endif +#ifdef _SC_CHARCLASS_NAME_MAX + {"SC_CHARCLASS_NAME_MAX", _SC_CHARCLASS_NAME_MAX}, +#endif +#ifdef _SC_CHAR_BIT + {"SC_CHAR_BIT", _SC_CHAR_BIT}, +#endif +#ifdef _SC_CHAR_MAX + {"SC_CHAR_MAX", _SC_CHAR_MAX}, +#endif +#ifdef _SC_CHAR_MIN + {"SC_CHAR_MIN", _SC_CHAR_MIN}, +#endif +#ifdef _SC_CHILD_MAX + {"SC_CHILD_MAX", _SC_CHILD_MAX}, +#endif +#ifdef _SC_CLK_TCK + {"SC_CLK_TCK", _SC_CLK_TCK}, +#endif +#ifdef _SC_COHER_BLKSZ + {"SC_COHER_BLKSZ", _SC_COHER_BLKSZ}, +#endif +#ifdef _SC_COLL_WEIGHTS_MAX + {"SC_COLL_WEIGHTS_MAX", _SC_COLL_WEIGHTS_MAX}, +#endif +#ifdef _SC_DCACHE_ASSOC + {"SC_DCACHE_ASSOC", _SC_DCACHE_ASSOC}, +#endif +#ifdef _SC_DCACHE_BLKSZ + {"SC_DCACHE_BLKSZ", _SC_DCACHE_BLKSZ}, +#endif +#ifdef _SC_DCACHE_LINESZ + {"SC_DCACHE_LINESZ", _SC_DCACHE_LINESZ}, +#endif +#ifdef _SC_DCACHE_SZ + {"SC_DCACHE_SZ", _SC_DCACHE_SZ}, +#endif +#ifdef _SC_DCACHE_TBLKSZ + {"SC_DCACHE_TBLKSZ", _SC_DCACHE_TBLKSZ}, +#endif +#ifdef _SC_DELAYTIMER_MAX + {"SC_DELAYTIMER_MAX", _SC_DELAYTIMER_MAX}, +#endif +#ifdef _SC_EQUIV_CLASS_MAX + {"SC_EQUIV_CLASS_MAX", _SC_EQUIV_CLASS_MAX}, +#endif +#ifdef _SC_EXPR_NEST_MAX + {"SC_EXPR_NEST_MAX", _SC_EXPR_NEST_MAX}, +#endif +#ifdef _SC_FSYNC + {"SC_FSYNC", _SC_FSYNC}, +#endif +#ifdef _SC_GETGR_R_SIZE_MAX + {"SC_GETGR_R_SIZE_MAX", _SC_GETGR_R_SIZE_MAX}, +#endif +#ifdef _SC_GETPW_R_SIZE_MAX + {"SC_GETPW_R_SIZE_MAX", _SC_GETPW_R_SIZE_MAX}, +#endif +#ifdef _SC_ICACHE_ASSOC + {"SC_ICACHE_ASSOC", _SC_ICACHE_ASSOC}, +#endif +#ifdef _SC_ICACHE_BLKSZ + {"SC_ICACHE_BLKSZ", _SC_ICACHE_BLKSZ}, +#endif +#ifdef _SC_ICACHE_LINESZ + {"SC_ICACHE_LINESZ", _SC_ICACHE_LINESZ}, +#endif +#ifdef _SC_ICACHE_SZ + {"SC_ICACHE_SZ", _SC_ICACHE_SZ}, +#endif +#ifdef _SC_INF + {"SC_INF", _SC_INF}, +#endif +#ifdef _SC_INT_MAX + {"SC_INT_MAX", _SC_INT_MAX}, +#endif +#ifdef _SC_INT_MIN + {"SC_INT_MIN", _SC_INT_MIN}, +#endif +#ifdef _SC_IOV_MAX + {"SC_IOV_MAX", _SC_IOV_MAX}, +#endif +#ifdef _SC_IP_SECOPTS + {"SC_IP_SECOPTS", _SC_IP_SECOPTS}, +#endif +#ifdef _SC_JOB_CONTROL + {"SC_JOB_CONTROL", _SC_JOB_CONTROL}, +#endif +#ifdef _SC_KERN_POINTERS + {"SC_KERN_POINTERS", _SC_KERN_POINTERS}, +#endif +#ifdef _SC_KERN_SIM + {"SC_KERN_SIM", _SC_KERN_SIM}, +#endif +#ifdef _SC_LINE_MAX + {"SC_LINE_MAX", _SC_LINE_MAX}, +#endif +#ifdef _SC_LOGIN_NAME_MAX + {"SC_LOGIN_NAME_MAX", _SC_LOGIN_NAME_MAX}, +#endif +#ifdef _SC_LOGNAME_MAX + {"SC_LOGNAME_MAX", _SC_LOGNAME_MAX}, +#endif +#ifdef _SC_LONG_BIT + {"SC_LONG_BIT", _SC_LONG_BIT}, +#endif +#ifdef _SC_MAC + {"SC_MAC", _SC_MAC}, +#endif +#ifdef _SC_MAPPED_FILES + {"SC_MAPPED_FILES", _SC_MAPPED_FILES}, +#endif +#ifdef _SC_MAXPID + {"SC_MAXPID", _SC_MAXPID}, +#endif +#ifdef _SC_MB_LEN_MAX + {"SC_MB_LEN_MAX", _SC_MB_LEN_MAX}, +#endif +#ifdef _SC_MEMLOCK + {"SC_MEMLOCK", _SC_MEMLOCK}, +#endif +#ifdef _SC_MEMLOCK_RANGE + {"SC_MEMLOCK_RANGE", _SC_MEMLOCK_RANGE}, +#endif +#ifdef _SC_MEMORY_PROTECTION + {"SC_MEMORY_PROTECTION", _SC_MEMORY_PROTECTION}, +#endif +#ifdef _SC_MESSAGE_PASSING + {"SC_MESSAGE_PASSING", _SC_MESSAGE_PASSING}, +#endif +#ifdef _SC_MMAP_FIXED_ALIGNMENT + {"SC_MMAP_FIXED_ALIGNMENT", _SC_MMAP_FIXED_ALIGNMENT}, +#endif +#ifdef _SC_MQ_OPEN_MAX + {"SC_MQ_OPEN_MAX", _SC_MQ_OPEN_MAX}, +#endif +#ifdef _SC_MQ_PRIO_MAX + {"SC_MQ_PRIO_MAX", _SC_MQ_PRIO_MAX}, +#endif +#ifdef _SC_NACLS_MAX + {"SC_NACLS_MAX", _SC_NACLS_MAX}, +#endif +#ifdef _SC_NGROUPS_MAX + {"SC_NGROUPS_MAX", _SC_NGROUPS_MAX}, +#endif +#ifdef _SC_NL_ARGMAX + {"SC_NL_ARGMAX", _SC_NL_ARGMAX}, +#endif +#ifdef _SC_NL_LANGMAX + {"SC_NL_LANGMAX", _SC_NL_LANGMAX}, +#endif +#ifdef _SC_NL_MSGMAX + {"SC_NL_MSGMAX", _SC_NL_MSGMAX}, +#endif +#ifdef _SC_NL_NMAX + {"SC_NL_NMAX", _SC_NL_NMAX}, +#endif +#ifdef _SC_NL_SETMAX + {"SC_NL_SETMAX", _SC_NL_SETMAX}, +#endif +#ifdef _SC_NL_TEXTMAX + {"SC_NL_TEXTMAX", _SC_NL_TEXTMAX}, +#endif +#ifdef _SC_NPROCESSORS_CONF + {"SC_NPROCESSORS_CONF", _SC_NPROCESSORS_CONF}, +#endif +#ifdef _SC_NPROCESSORS_ONLN + {"SC_NPROCESSORS_ONLN", _SC_NPROCESSORS_ONLN}, +#endif +#ifdef _SC_NPROC_CONF + {"SC_NPROC_CONF", _SC_NPROC_CONF}, +#endif +#ifdef _SC_NPROC_ONLN + {"SC_NPROC_ONLN", _SC_NPROC_ONLN}, +#endif +#ifdef _SC_NZERO + {"SC_NZERO", _SC_NZERO}, +#endif +#ifdef _SC_OPEN_MAX + {"SC_OPEN_MAX", _SC_OPEN_MAX}, +#endif +#ifdef _SC_PAGESIZE + {"SC_PAGESIZE", _SC_PAGESIZE}, +#endif +#ifdef _SC_PAGE_SIZE + {"SC_PAGE_SIZE", _SC_PAGE_SIZE}, +#endif +#ifdef _SC_PASS_MAX + {"SC_PASS_MAX", _SC_PASS_MAX}, +#endif +#ifdef _SC_PHYS_PAGES + {"SC_PHYS_PAGES", _SC_PHYS_PAGES}, +#endif +#ifdef _SC_PII + {"SC_PII", _SC_PII}, +#endif +#ifdef _SC_PII_INTERNET + {"SC_PII_INTERNET", _SC_PII_INTERNET}, +#endif +#ifdef _SC_PII_INTERNET_DGRAM + {"SC_PII_INTERNET_DGRAM", _SC_PII_INTERNET_DGRAM}, +#endif +#ifdef _SC_PII_INTERNET_STREAM + {"SC_PII_INTERNET_STREAM", _SC_PII_INTERNET_STREAM}, +#endif +#ifdef _SC_PII_OSI + {"SC_PII_OSI", _SC_PII_OSI}, +#endif +#ifdef _SC_PII_OSI_CLTS + {"SC_PII_OSI_CLTS", _SC_PII_OSI_CLTS}, +#endif +#ifdef _SC_PII_OSI_COTS + {"SC_PII_OSI_COTS", _SC_PII_OSI_COTS}, +#endif +#ifdef _SC_PII_OSI_M + {"SC_PII_OSI_M", _SC_PII_OSI_M}, +#endif +#ifdef _SC_PII_SOCKET + {"SC_PII_SOCKET", _SC_PII_SOCKET}, +#endif +#ifdef _SC_PII_XTI + {"SC_PII_XTI", _SC_PII_XTI}, +#endif +#ifdef _SC_POLL + {"SC_POLL", _SC_POLL}, +#endif +#ifdef _SC_PRIORITIZED_IO + {"SC_PRIORITIZED_IO", _SC_PRIORITIZED_IO}, +#endif +#ifdef _SC_PRIORITY_SCHEDULING + {"SC_PRIORITY_SCHEDULING", _SC_PRIORITY_SCHEDULING}, +#endif +#ifdef _SC_REALTIME_SIGNALS + {"SC_REALTIME_SIGNALS", _SC_REALTIME_SIGNALS}, +#endif +#ifdef _SC_RE_DUP_MAX + {"SC_RE_DUP_MAX", _SC_RE_DUP_MAX}, +#endif +#ifdef _SC_RTSIG_MAX + {"SC_RTSIG_MAX", _SC_RTSIG_MAX}, +#endif +#ifdef _SC_SAVED_IDS + {"SC_SAVED_IDS", _SC_SAVED_IDS}, +#endif +#ifdef _SC_SCHAR_MAX + {"SC_SCHAR_MAX", _SC_SCHAR_MAX}, +#endif +#ifdef _SC_SCHAR_MIN + {"SC_SCHAR_MIN", _SC_SCHAR_MIN}, +#endif +#ifdef _SC_SELECT + {"SC_SELECT", _SC_SELECT}, +#endif +#ifdef _SC_SEMAPHORES + {"SC_SEMAPHORES", _SC_SEMAPHORES}, +#endif +#ifdef _SC_SEM_NSEMS_MAX + {"SC_SEM_NSEMS_MAX", _SC_SEM_NSEMS_MAX}, +#endif +#ifdef _SC_SEM_VALUE_MAX + {"SC_SEM_VALUE_MAX", _SC_SEM_VALUE_MAX}, +#endif +#ifdef _SC_SHARED_MEMORY_OBJECTS + {"SC_SHARED_MEMORY_OBJECTS", _SC_SHARED_MEMORY_OBJECTS}, +#endif +#ifdef _SC_SHRT_MAX + {"SC_SHRT_MAX", _SC_SHRT_MAX}, +#endif +#ifdef _SC_SHRT_MIN + {"SC_SHRT_MIN", _SC_SHRT_MIN}, +#endif +#ifdef _SC_SIGQUEUE_MAX + {"SC_SIGQUEUE_MAX", _SC_SIGQUEUE_MAX}, +#endif +#ifdef _SC_SIGRT_MAX + {"SC_SIGRT_MAX", _SC_SIGRT_MAX}, +#endif +#ifdef _SC_SIGRT_MIN + {"SC_SIGRT_MIN", _SC_SIGRT_MIN}, +#endif +#ifdef _SC_SOFTPOWER + {"SC_SOFTPOWER", _SC_SOFTPOWER}, +#endif +#ifdef _SC_SPLIT_CACHE + {"SC_SPLIT_CACHE", _SC_SPLIT_CACHE}, +#endif +#ifdef _SC_SSIZE_MAX + {"SC_SSIZE_MAX", _SC_SSIZE_MAX}, +#endif +#ifdef _SC_STACK_PROT + {"SC_STACK_PROT", _SC_STACK_PROT}, +#endif +#ifdef _SC_STREAM_MAX + {"SC_STREAM_MAX", _SC_STREAM_MAX}, +#endif +#ifdef _SC_SYNCHRONIZED_IO + {"SC_SYNCHRONIZED_IO", _SC_SYNCHRONIZED_IO}, +#endif +#ifdef _SC_THREADS + {"SC_THREADS", _SC_THREADS}, +#endif +#ifdef _SC_THREAD_ATTR_STACKADDR + {"SC_THREAD_ATTR_STACKADDR", _SC_THREAD_ATTR_STACKADDR}, +#endif +#ifdef _SC_THREAD_ATTR_STACKSIZE + {"SC_THREAD_ATTR_STACKSIZE", _SC_THREAD_ATTR_STACKSIZE}, +#endif +#ifdef _SC_THREAD_DESTRUCTOR_ITERATIONS + {"SC_THREAD_DESTRUCTOR_ITERATIONS", _SC_THREAD_DESTRUCTOR_ITERATIONS}, +#endif +#ifdef _SC_THREAD_KEYS_MAX + {"SC_THREAD_KEYS_MAX", _SC_THREAD_KEYS_MAX}, +#endif +#ifdef _SC_THREAD_PRIORITY_SCHEDULING + {"SC_THREAD_PRIORITY_SCHEDULING", _SC_THREAD_PRIORITY_SCHEDULING}, +#endif +#ifdef _SC_THREAD_PRIO_INHERIT + {"SC_THREAD_PRIO_INHERIT", _SC_THREAD_PRIO_INHERIT}, +#endif +#ifdef _SC_THREAD_PRIO_PROTECT + {"SC_THREAD_PRIO_PROTECT", _SC_THREAD_PRIO_PROTECT}, +#endif +#ifdef _SC_THREAD_PROCESS_SHARED + {"SC_THREAD_PROCESS_SHARED", _SC_THREAD_PROCESS_SHARED}, +#endif +#ifdef _SC_THREAD_SAFE_FUNCTIONS + {"SC_THREAD_SAFE_FUNCTIONS", _SC_THREAD_SAFE_FUNCTIONS}, +#endif +#ifdef _SC_THREAD_STACK_MIN + {"SC_THREAD_STACK_MIN", _SC_THREAD_STACK_MIN}, +#endif +#ifdef _SC_THREAD_THREADS_MAX + {"SC_THREAD_THREADS_MAX", _SC_THREAD_THREADS_MAX}, +#endif +#ifdef _SC_TIMERS + {"SC_TIMERS", _SC_TIMERS}, +#endif +#ifdef _SC_TIMER_MAX + {"SC_TIMER_MAX", _SC_TIMER_MAX}, +#endif +#ifdef _SC_TTY_NAME_MAX + {"SC_TTY_NAME_MAX", _SC_TTY_NAME_MAX}, +#endif +#ifdef _SC_TZNAME_MAX + {"SC_TZNAME_MAX", _SC_TZNAME_MAX}, +#endif +#ifdef _SC_T_IOV_MAX + {"SC_T_IOV_MAX", _SC_T_IOV_MAX}, +#endif +#ifdef _SC_UCHAR_MAX + {"SC_UCHAR_MAX", _SC_UCHAR_MAX}, +#endif +#ifdef _SC_UINT_MAX + {"SC_UINT_MAX", _SC_UINT_MAX}, +#endif +#ifdef _SC_UIO_MAXIOV + {"SC_UIO_MAXIOV", _SC_UIO_MAXIOV}, +#endif +#ifdef _SC_ULONG_MAX + {"SC_ULONG_MAX", _SC_ULONG_MAX}, +#endif +#ifdef _SC_USHRT_MAX + {"SC_USHRT_MAX", _SC_USHRT_MAX}, +#endif +#ifdef _SC_VERSION + {"SC_VERSION", _SC_VERSION}, +#endif +#ifdef _SC_WORD_BIT + {"SC_WORD_BIT", _SC_WORD_BIT}, +#endif +#ifdef _SC_XBS5_ILP32_OFF32 + {"SC_XBS5_ILP32_OFF32", _SC_XBS5_ILP32_OFF32}, +#endif +#ifdef _SC_XBS5_ILP32_OFFBIG + {"SC_XBS5_ILP32_OFFBIG", _SC_XBS5_ILP32_OFFBIG}, +#endif +#ifdef _SC_XBS5_LP64_OFF64 + {"SC_XBS5_LP64_OFF64", _SC_XBS5_LP64_OFF64}, +#endif +#ifdef _SC_XBS5_LPBIG_OFFBIG + {"SC_XBS5_LPBIG_OFFBIG", _SC_XBS5_LPBIG_OFFBIG}, +#endif +#ifdef _SC_XOPEN_CRYPT + {"SC_XOPEN_CRYPT", _SC_XOPEN_CRYPT}, +#endif +#ifdef _SC_XOPEN_ENH_I18N + {"SC_XOPEN_ENH_I18N", _SC_XOPEN_ENH_I18N}, +#endif +#ifdef _SC_XOPEN_LEGACY + {"SC_XOPEN_LEGACY", _SC_XOPEN_LEGACY}, +#endif +#ifdef _SC_XOPEN_REALTIME + {"SC_XOPEN_REALTIME", _SC_XOPEN_REALTIME}, +#endif +#ifdef _SC_XOPEN_REALTIME_THREADS + {"SC_XOPEN_REALTIME_THREADS", _SC_XOPEN_REALTIME_THREADS}, +#endif +#ifdef _SC_XOPEN_SHM + {"SC_XOPEN_SHM", _SC_XOPEN_SHM}, +#endif +#ifdef _SC_XOPEN_UNIX + {"SC_XOPEN_UNIX", _SC_XOPEN_UNIX}, +#endif +#ifdef _SC_XOPEN_VERSION + {"SC_XOPEN_VERSION", _SC_XOPEN_VERSION}, +#endif +#ifdef _SC_XOPEN_XCU_VERSION + {"SC_XOPEN_XCU_VERSION", _SC_XOPEN_XCU_VERSION}, +#endif +#ifdef _SC_XOPEN_XPG2 + {"SC_XOPEN_XPG2", _SC_XOPEN_XPG2}, +#endif +#ifdef _SC_XOPEN_XPG3 + {"SC_XOPEN_XPG3", _SC_XOPEN_XPG3}, +#endif +#ifdef _SC_XOPEN_XPG4 + {"SC_XOPEN_XPG4", _SC_XOPEN_XPG4}, +#endif +}; + +static int +conv_sysconf_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_sysconf, + sizeof(posix_constants_sysconf) + / sizeof(struct constdef)); +} + +PyDoc_STRVAR(posix_sysconf__doc__, +"sysconf(name) -> integer\n\n\ +Return an integer-valued system configuration variable."); + +static PyObject * +posix_sysconf(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int name; + + if (PyArg_ParseTuple(args, "O&:sysconf", conv_sysconf_confname, &name)) { + int value; + + errno = 0; + value = sysconf(name); + if (value == -1 && errno != 0) + posix_error(); + else + result = PyInt_FromLong(value); + } + return result; +} +#endif + + +/* This code is used to ensure that the tables of configuration value names + * are in sorted order as required by conv_confname(), and also to build the + * the exported dictionaries that are used to publish information about the + * names available on the host platform. + * + * Sorting the table at runtime ensures that the table is properly ordered + * when used, even for platforms we're not able to test on. It also makes + * it easier to add additional entries to the tables. + */ + +static int +cmp_constdefs(const void *v1, const void *v2) +{ + const struct constdef *c1 = + (const struct constdef *) v1; + const struct constdef *c2 = + (const struct constdef *) v2; + + return strcmp(c1->name, c2->name); +} + +static int +setup_confname_table(struct constdef *table, size_t tablesize, + char *tablename, PyObject *module) +{ + PyObject *d = NULL; + size_t i; + + qsort(table, tablesize, sizeof(struct constdef), cmp_constdefs); + d = PyDict_New(); + if (d == NULL) + return -1; + + for (i=0; i < tablesize; ++i) { + PyObject *o = PyInt_FromLong(table[i].value); + if (o == NULL || PyDict_SetItemString(d, table[i].name, o) == -1) { + Py_XDECREF(o); + Py_DECREF(d); + return -1; + } + Py_DECREF(o); + } + return PyModule_AddObject(module, tablename, d); +} + +/* Return -1 on failure, 0 on success. */ +static int +setup_confname_tables(PyObject *module) +{ +#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF) + if (setup_confname_table(posix_constants_pathconf, + sizeof(posix_constants_pathconf) + / sizeof(struct constdef), + "pathconf_names", module)) + return -1; +#endif +#ifdef HAVE_CONFSTR + if (setup_confname_table(posix_constants_confstr, + sizeof(posix_constants_confstr) + / sizeof(struct constdef), + "confstr_names", module)) + return -1; +#endif +#ifdef HAVE_SYSCONF + if (setup_confname_table(posix_constants_sysconf, + sizeof(posix_constants_sysconf) + / sizeof(struct constdef), + "sysconf_names", module)) + return -1; +#endif + return 0; +} + + +PyDoc_STRVAR(posix_abort__doc__, +"abort() -> does not return!\n\n\ +Abort the interpreter immediately. This 'dumps core' or otherwise fails\n\ +in the hardest way possible on the hosting operating system."); + +static PyObject * +posix_abort(PyObject *self, PyObject *noargs) +{ + abort(); + /*NOTREACHED*/ + Py_FatalError("abort() called from Python code didn't abort!"); + return NULL; +} + +#if defined(MS_WINDOWS) && !defined(MS_XBOX) +PyDoc_STRVAR(win32_startfile__doc__, +"startfile(filepath) - Start a file with its associated application.\n\ +\n\ +This acts like double-clicking the file in Explorer, or giving the file\n\ +name as an argument to the DOS \"start\" command: the file is opened\n\ +with whatever application (if any) its extension is associated.\n\ +\n\ +startfile returns as soon as the associated application is launched.\n\ +There is no option to wait for the application to close, and no way\n\ +to retrieve the application's exit status.\n\ +\n\ +The filepath is relative to the current directory. If you want to use\n\ +an absolute path, make sure the first character is not a slash (\"/\");\n\ +the underlying Win32 ShellExecute function doesn't work if it is."); + +static PyObject * +win32_startfile(PyObject *self, PyObject *args) +{ + char *filepath; + HINSTANCE rc; + if (!PyArg_ParseTuple(args, "s:startfile", &filepath)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rc = ShellExecute((HWND)0, NULL, filepath, NULL, NULL, SW_SHOWNORMAL); + Py_END_ALLOW_THREADS + if (rc <= (HINSTANCE)32) + return win32_error("startfile", filepath); + Py_INCREF(Py_None); + return Py_None; +} +#endif + +#ifdef HAVE_GETLOADAVG +PyDoc_STRVAR(posix_getloadavg__doc__, +"getloadavg() -> (float, float, float)\n\n\ +Return the number of processes in the system run queue averaged over\n\ +the last 1, 5, and 15 minutes or raises OSError if the load average\n\ +was unobtainable"); + +static PyObject * +posix_getloadavg(PyObject *self, PyObject *noargs) +{ + double loadavg[3]; + if (getloadavg(loadavg, 3)!=3) { + PyErr_SetString(PyExc_OSError, "Load averages are unobtainable"); + return NULL; + } else + return Py_BuildValue("ddd", loadavg[0], loadavg[1], loadavg[2]); +} +#endif + + +static PyMethodDef posix_methods[] = { + {"access", posix_access, METH_VARARGS, posix_access__doc__}, +#ifdef HAVE_TTYNAME + {"ttyname", posix_ttyname, METH_VARARGS, posix_ttyname__doc__}, +#endif + {"chdir", posix_chdir, METH_VARARGS, posix_chdir__doc__}, + {"chmod", posix_chmod, METH_VARARGS, posix_chmod__doc__}, +#ifdef HAVE_CHOWN + {"chown", posix_chown, METH_VARARGS, posix_chown__doc__}, +#endif /* HAVE_CHOWN */ +#ifdef HAVE_LCHOWN + {"lchown", posix_lchown, METH_VARARGS, posix_lchown__doc__}, +#endif /* HAVE_LCHOWN */ +#ifdef HAVE_CHROOT + {"chroot", posix_chroot, METH_VARARGS, posix_chroot__doc__}, +#endif +#ifdef HAVE_CTERMID + {"ctermid", posix_ctermid, METH_NOARGS, posix_ctermid__doc__}, +#endif +#ifdef HAVE_GETCWD + {"getcwd", posix_getcwd, METH_NOARGS, posix_getcwd__doc__}, +#ifdef Py_USING_UNICODE + {"getcwdu", posix_getcwdu, METH_NOARGS, posix_getcwdu__doc__}, +#endif +#endif +#ifdef HAVE_LINK + {"link", posix_link, METH_VARARGS, posix_link__doc__}, +#endif /* HAVE_LINK */ + {"listdir", posix_listdir, METH_VARARGS, posix_listdir__doc__}, + {"lstat", posix_lstat, METH_VARARGS, posix_lstat__doc__}, + {"mkdir", posix_mkdir, METH_VARARGS, posix_mkdir__doc__}, +#ifdef HAVE_NICE + {"nice", posix_nice, METH_VARARGS, posix_nice__doc__}, +#endif /* HAVE_NICE */ +#ifdef HAVE_READLINK + {"readlink", posix_readlink, METH_VARARGS, posix_readlink__doc__}, +#endif /* HAVE_READLINK */ + {"rename", posix_rename, METH_VARARGS, posix_rename__doc__}, + {"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__}, + {"stat", posix_stat, METH_VARARGS, posix_stat__doc__}, + {"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__}, +#ifdef HAVE_SYMLINK + {"symlink", posix_symlink, METH_VARARGS, posix_symlink__doc__}, +#endif /* HAVE_SYMLINK */ +#ifdef HAVE_SYSTEM + {"system", posix_system, METH_VARARGS, posix_system__doc__}, +#endif + {"umask", posix_umask, METH_VARARGS, posix_umask__doc__}, +#ifdef HAVE_UNAME + {"uname", posix_uname, METH_NOARGS, posix_uname__doc__}, +#endif /* HAVE_UNAME */ + {"unlink", posix_unlink, METH_VARARGS, posix_unlink__doc__}, + {"remove", posix_unlink, METH_VARARGS, posix_remove__doc__}, + {"utime", posix_utime, METH_VARARGS, posix_utime__doc__}, +#ifdef HAVE_TIMES + {"times", posix_times, METH_NOARGS, posix_times__doc__}, +#endif /* HAVE_TIMES */ + {"_exit", posix__exit, METH_VARARGS, posix__exit__doc__}, +#ifdef HAVE_EXECV + {"execv", posix_execv, METH_VARARGS, posix_execv__doc__}, + {"execve", posix_execve, METH_VARARGS, posix_execve__doc__}, +#endif /* HAVE_EXECV */ +#ifdef HAVE_SPAWNV + {"spawnv", posix_spawnv, METH_VARARGS, posix_spawnv__doc__}, + {"spawnve", posix_spawnve, METH_VARARGS, posix_spawnve__doc__}, +#endif /* HAVE_SPAWNV */ +#ifdef HAVE_FORK1 + {"fork1", posix_fork1, METH_NOARGS, posix_fork1__doc__}, +#endif /* HAVE_FORK1 */ +#ifdef HAVE_FORK + {"fork", posix_fork, METH_NOARGS, posix_fork__doc__}, +#endif /* HAVE_FORK */ +#if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) + {"openpty", posix_openpty, METH_NOARGS, posix_openpty__doc__}, +#endif /* HAVE_OPENPTY || HAVE__GETPTY || HAVE_DEV_PTMX */ +#ifdef HAVE_FORKPTY + {"forkpty", posix_forkpty, METH_NOARGS, posix_forkpty__doc__}, +#endif /* HAVE_FORKPTY */ +#ifdef HAVE_GETEGID + {"getegid", posix_getegid, METH_NOARGS, posix_getegid__doc__}, +#endif /* HAVE_GETEGID */ +#ifdef HAVE_GETEUID + {"geteuid", posix_geteuid, METH_NOARGS, posix_geteuid__doc__}, +#endif /* HAVE_GETEUID */ +#ifdef HAVE_GETGID + {"getgid", posix_getgid, METH_NOARGS, posix_getgid__doc__}, +#endif /* HAVE_GETGID */ +#ifdef HAVE_GETGROUPS + {"getgroups", posix_getgroups, METH_NOARGS, posix_getgroups__doc__}, +#endif + {"getpid", posix_getpid, METH_NOARGS, posix_getpid__doc__}, +#ifdef HAVE_GETPGRP + {"getpgrp", posix_getpgrp, METH_NOARGS, posix_getpgrp__doc__}, +#endif /* HAVE_GETPGRP */ +#ifdef HAVE_GETPPID + {"getppid", posix_getppid, METH_NOARGS, posix_getppid__doc__}, +#endif /* HAVE_GETPPID */ +#ifdef HAVE_GETUID + {"getuid", posix_getuid, METH_NOARGS, posix_getuid__doc__}, +#endif /* HAVE_GETUID */ +#ifdef HAVE_GETLOGIN + {"getlogin", posix_getlogin, METH_NOARGS, posix_getlogin__doc__}, +#endif +#ifdef HAVE_KILL + {"kill", posix_kill, METH_VARARGS, posix_kill__doc__}, +#endif /* HAVE_KILL */ +#ifdef HAVE_KILLPG + {"killpg", posix_killpg, METH_VARARGS, posix_killpg__doc__}, +#endif /* HAVE_KILLPG */ +#ifdef HAVE_PLOCK + {"plock", posix_plock, METH_VARARGS, posix_plock__doc__}, +#endif /* HAVE_PLOCK */ +#ifdef HAVE_POPEN + {"popen", posix_popen, METH_VARARGS, posix_popen__doc__}, +#ifdef MS_WINDOWS + {"popen2", win32_popen2, METH_VARARGS}, + {"popen3", win32_popen3, METH_VARARGS}, + {"popen4", win32_popen4, METH_VARARGS}, +#ifndef MS_XBOX + {"startfile", win32_startfile, METH_VARARGS, win32_startfile__doc__}, +#endif +#else +#if defined(PYOS_OS2) && defined(PYCC_GCC) + {"popen2", os2emx_popen2, METH_VARARGS}, + {"popen3", os2emx_popen3, METH_VARARGS}, + {"popen4", os2emx_popen4, METH_VARARGS}, +#endif +#endif +#endif /* HAVE_POPEN */ +#ifdef HAVE_SETUID + {"setuid", posix_setuid, METH_VARARGS, posix_setuid__doc__}, +#endif /* HAVE_SETUID */ +#ifdef HAVE_SETEUID + {"seteuid", posix_seteuid, METH_VARARGS, posix_seteuid__doc__}, +#endif /* HAVE_SETEUID */ +#ifdef HAVE_SETEGID + {"setegid", posix_setegid, METH_VARARGS, posix_setegid__doc__}, +#endif /* HAVE_SETEGID */ +#ifdef HAVE_SETREUID + {"setreuid", posix_setreuid, METH_VARARGS, posix_setreuid__doc__}, +#endif /* HAVE_SETREUID */ +#ifdef HAVE_SETREGID + {"setregid", posix_setregid, METH_VARARGS, posix_setregid__doc__}, +#endif /* HAVE_SETREGID */ +#ifdef HAVE_SETGID + {"setgid", posix_setgid, METH_VARARGS, posix_setgid__doc__}, +#endif /* HAVE_SETGID */ +#ifdef HAVE_SETGROUPS + {"setgroups", posix_setgroups, METH_VARARGS, posix_setgroups__doc__}, +#endif /* HAVE_SETGROUPS */ +#ifdef HAVE_GETPGID + {"getpgid", posix_getpgid, METH_VARARGS, posix_getpgid__doc__}, +#endif /* HAVE_GETPGID */ +#ifdef HAVE_SETPGRP + {"setpgrp", posix_setpgrp, METH_NOARGS, posix_setpgrp__doc__}, +#endif /* HAVE_SETPGRP */ +#ifdef HAVE_WAIT + {"wait", posix_wait, METH_NOARGS, posix_wait__doc__}, +#endif /* HAVE_WAIT */ +#if defined(HAVE_WAITPID) || defined(HAVE_CWAIT) + {"waitpid", posix_waitpid, METH_VARARGS, posix_waitpid__doc__}, +#endif /* HAVE_WAITPID */ +#ifdef HAVE_SETSID + {"setsid", posix_setsid, METH_NOARGS, posix_setsid__doc__}, +#endif /* HAVE_SETSID */ +#ifdef HAVE_SETPGID + {"setpgid", posix_setpgid, METH_VARARGS, posix_setpgid__doc__}, +#endif /* HAVE_SETPGID */ +#ifdef HAVE_TCGETPGRP + {"tcgetpgrp", posix_tcgetpgrp, METH_VARARGS, posix_tcgetpgrp__doc__}, +#endif /* HAVE_TCGETPGRP */ +#ifdef HAVE_TCSETPGRP + {"tcsetpgrp", posix_tcsetpgrp, METH_VARARGS, posix_tcsetpgrp__doc__}, +#endif /* HAVE_TCSETPGRP */ + {"open", posix_open, METH_VARARGS, posix_open__doc__}, + {"close", posix_close, METH_VARARGS, posix_close__doc__}, + {"dup", posix_dup, METH_VARARGS, posix_dup__doc__}, + {"dup2", posix_dup2, METH_VARARGS, posix_dup2__doc__}, + {"lseek", posix_lseek, METH_VARARGS, posix_lseek__doc__}, + {"read", posix_read, METH_VARARGS, posix_read__doc__}, + {"write", posix_write, METH_VARARGS, posix_write__doc__}, + {"fstat", posix_fstat, METH_VARARGS, posix_fstat__doc__}, + {"fdopen", posix_fdopen, METH_VARARGS, posix_fdopen__doc__}, + {"isatty", posix_isatty, METH_VARARGS, posix_isatty__doc__}, +#ifdef HAVE_PIPE + {"pipe", posix_pipe, METH_NOARGS, posix_pipe__doc__}, +#endif +#ifdef HAVE_MKFIFO + {"mkfifo", posix_mkfifo, METH_VARARGS, posix_mkfifo__doc__}, +#endif +#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) + {"mknod", posix_mknod, METH_VARARGS, posix_mknod__doc__}, +#endif +#ifdef HAVE_DEVICE_MACROS + {"major", posix_major, METH_VARARGS, posix_major__doc__}, + {"minor", posix_minor, METH_VARARGS, posix_minor__doc__}, + {"makedev", posix_makedev, METH_VARARGS, posix_makedev__doc__}, +#endif +#ifdef HAVE_FTRUNCATE + {"ftruncate", posix_ftruncate, METH_VARARGS, posix_ftruncate__doc__}, +#endif +#ifdef HAVE_PUTENV + {"putenv", posix_putenv, METH_VARARGS, posix_putenv__doc__}, +#endif +#ifdef HAVE_UNSETENV + {"unsetenv", posix_unsetenv, METH_VARARGS, posix_unsetenv__doc__}, +#endif +#ifdef HAVE_STRERROR + {"strerror", posix_strerror, METH_VARARGS, posix_strerror__doc__}, +#endif +#ifdef HAVE_FCHDIR + {"fchdir", posix_fchdir, METH_O, posix_fchdir__doc__}, +#endif +#ifdef HAVE_FSYNC + {"fsync", posix_fsync, METH_O, posix_fsync__doc__}, +#endif +#ifdef HAVE_FDATASYNC + {"fdatasync", posix_fdatasync, METH_O, posix_fdatasync__doc__}, +#endif +#ifdef HAVE_SYS_WAIT_H +#ifdef WCOREDUMP + {"WCOREDUMP", posix_WCOREDUMP, METH_VARARGS, posix_WCOREDUMP__doc__}, +#endif /* WCOREDUMP */ +#ifdef WIFCONTINUED + {"WIFCONTINUED",posix_WIFCONTINUED, METH_VARARGS, posix_WIFCONTINUED__doc__}, +#endif /* WIFCONTINUED */ +#ifdef WIFSTOPPED + {"WIFSTOPPED", posix_WIFSTOPPED, METH_VARARGS, posix_WIFSTOPPED__doc__}, +#endif /* WIFSTOPPED */ +#ifdef WIFSIGNALED + {"WIFSIGNALED", posix_WIFSIGNALED, METH_VARARGS, posix_WIFSIGNALED__doc__}, +#endif /* WIFSIGNALED */ +#ifdef WIFEXITED + {"WIFEXITED", posix_WIFEXITED, METH_VARARGS, posix_WIFEXITED__doc__}, +#endif /* WIFEXITED */ +#ifdef WEXITSTATUS + {"WEXITSTATUS", posix_WEXITSTATUS, METH_VARARGS, posix_WEXITSTATUS__doc__}, +#endif /* WEXITSTATUS */ +#ifdef WTERMSIG + {"WTERMSIG", posix_WTERMSIG, METH_VARARGS, posix_WTERMSIG__doc__}, +#endif /* WTERMSIG */ +#ifdef WSTOPSIG + {"WSTOPSIG", posix_WSTOPSIG, METH_VARARGS, posix_WSTOPSIG__doc__}, +#endif /* WSTOPSIG */ +#endif /* HAVE_SYS_WAIT_H */ +#ifdef HAVE_FSTATVFS + {"fstatvfs", posix_fstatvfs, METH_VARARGS, posix_fstatvfs__doc__}, +#endif +#ifdef HAVE_STATVFS + {"statvfs", posix_statvfs, METH_VARARGS, posix_statvfs__doc__}, +#endif +#ifdef HAVE_TMPFILE + {"tmpfile", posix_tmpfile, METH_NOARGS, posix_tmpfile__doc__}, +#endif +#ifdef HAVE_TEMPNAM + {"tempnam", posix_tempnam, METH_VARARGS, posix_tempnam__doc__}, +#endif +#ifdef HAVE_TMPNAM + {"tmpnam", posix_tmpnam, METH_NOARGS, posix_tmpnam__doc__}, +#endif +#ifdef HAVE_CONFSTR + {"confstr", posix_confstr, METH_VARARGS, posix_confstr__doc__}, +#endif +#ifdef HAVE_SYSCONF + {"sysconf", posix_sysconf, METH_VARARGS, posix_sysconf__doc__}, +#endif +#ifdef HAVE_FPATHCONF + {"fpathconf", posix_fpathconf, METH_VARARGS, posix_fpathconf__doc__}, +#endif +#ifdef HAVE_PATHCONF + {"pathconf", posix_pathconf, METH_VARARGS, posix_pathconf__doc__}, +#endif + {"abort", posix_abort, METH_NOARGS, posix_abort__doc__}, +#if defined(MS_WINDOWS) && !defined(MS_XBOX) + {"_getfullpathname", posix__getfullpathname, METH_VARARGS, NULL}, +#endif +#ifdef HAVE_GETLOADAVG + {"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__}, +#endif + {NULL, NULL} /* Sentinel */ +}; + + +static int +ins(PyObject *module, char *symbol, long value) +{ + return PyModule_AddIntConstant(module, symbol, value); +} + +#if defined(PYOS_OS2) +/* Insert Platform-Specific Constant Values (Strings & Numbers) of Common Use */ +static int insertvalues(PyObject *module) +{ + APIRET rc; + ULONG values[QSV_MAX+1]; + PyObject *v; + char *ver, tmp[50]; + + Py_BEGIN_ALLOW_THREADS + rc = DosQuerySysInfo(1L, QSV_MAX, &values[1], sizeof(ULONG) * QSV_MAX); + Py_END_ALLOW_THREADS + + if (rc != NO_ERROR) { + os2_error(rc); + return -1; + } + + if (ins(module, "meminstalled", values[QSV_TOTPHYSMEM])) return -1; + if (ins(module, "memkernel", values[QSV_TOTRESMEM])) return -1; + if (ins(module, "memvirtual", values[QSV_TOTAVAILMEM])) return -1; + if (ins(module, "maxpathlen", values[QSV_MAX_PATH_LENGTH])) return -1; + if (ins(module, "maxnamelen", values[QSV_MAX_COMP_LENGTH])) return -1; + if (ins(module, "revision", values[QSV_VERSION_REVISION])) return -1; + if (ins(module, "timeslice", values[QSV_MIN_SLICE])) return -1; + + switch (values[QSV_VERSION_MINOR]) { + case 0: ver = "2.00"; break; + case 10: ver = "2.10"; break; + case 11: ver = "2.11"; break; + case 30: ver = "3.00"; break; + case 40: ver = "4.00"; break; + case 50: ver = "5.00"; break; + default: + PyOS_snprintf(tmp, sizeof(tmp), + "%d-%d", values[QSV_VERSION_MAJOR], + values[QSV_VERSION_MINOR]); + ver = &tmp[0]; + } + + /* Add Indicator of the Version of the Operating System */ + if (PyModule_AddStringConstant(module, "version", tmp) < 0) + return -1; + + /* Add Indicator of Which Drive was Used to Boot the System */ + tmp[0] = 'A' + values[QSV_BOOT_DRIVE] - 1; + tmp[1] = ':'; + tmp[2] = '\0'; + + return PyModule_AddStringConstant(module, "bootdrive", tmp); +} +#endif + +static int +all_ins(PyObject *d) +{ +#ifdef F_OK + if (ins(d, "F_OK", (long)F_OK)) return -1; +#endif +#ifdef R_OK + if (ins(d, "R_OK", (long)R_OK)) return -1; +#endif +#ifdef W_OK + if (ins(d, "W_OK", (long)W_OK)) return -1; +#endif +#ifdef X_OK + if (ins(d, "X_OK", (long)X_OK)) return -1; +#endif +#ifdef NGROUPS_MAX + if (ins(d, "NGROUPS_MAX", (long)NGROUPS_MAX)) return -1; +#endif +#ifdef TMP_MAX + if (ins(d, "TMP_MAX", (long)TMP_MAX)) return -1; +#endif +#ifdef WCONTINUED + if (ins(d, "WCONTINUED", (long)WCONTINUED)) return -1; +#endif +#ifdef WNOHANG + if (ins(d, "WNOHANG", (long)WNOHANG)) return -1; +#endif +#ifdef WUNTRACED + if (ins(d, "WUNTRACED", (long)WUNTRACED)) return -1; +#endif +#ifdef O_RDONLY + if (ins(d, "O_RDONLY", (long)O_RDONLY)) return -1; +#endif +#ifdef O_WRONLY + if (ins(d, "O_WRONLY", (long)O_WRONLY)) return -1; +#endif +#ifdef O_RDWR + if (ins(d, "O_RDWR", (long)O_RDWR)) return -1; +#endif +#ifdef O_NDELAY + if (ins(d, "O_NDELAY", (long)O_NDELAY)) return -1; +#endif +#ifdef O_NONBLOCK + if (ins(d, "O_NONBLOCK", (long)O_NONBLOCK)) return -1; +#endif +#ifdef O_APPEND + if (ins(d, "O_APPEND", (long)O_APPEND)) return -1; +#endif +#ifdef O_DSYNC + if (ins(d, "O_DSYNC", (long)O_DSYNC)) return -1; +#endif +#ifdef O_RSYNC + if (ins(d, "O_RSYNC", (long)O_RSYNC)) return -1; +#endif +#ifdef O_SYNC + if (ins(d, "O_SYNC", (long)O_SYNC)) return -1; +#endif +#ifdef O_NOCTTY + if (ins(d, "O_NOCTTY", (long)O_NOCTTY)) return -1; +#endif +#ifdef O_CREAT + if (ins(d, "O_CREAT", (long)O_CREAT)) return -1; +#endif +#ifdef O_EXCL + if (ins(d, "O_EXCL", (long)O_EXCL)) return -1; +#endif +#ifdef O_TRUNC + if (ins(d, "O_TRUNC", (long)O_TRUNC)) return -1; +#endif +#ifdef O_BINARY + if (ins(d, "O_BINARY", (long)O_BINARY)) return -1; +#endif +#ifdef O_TEXT + if (ins(d, "O_TEXT", (long)O_TEXT)) return -1; +#endif +#ifdef O_LARGEFILE + if (ins(d, "O_LARGEFILE", (long)O_LARGEFILE)) return -1; +#endif + +/* MS Windows */ +#ifdef O_NOINHERIT + /* Don't inherit in child processes. */ + if (ins(d, "O_NOINHERIT", (long)O_NOINHERIT)) return -1; +#endif +#ifdef _O_SHORT_LIVED + /* Optimize for short life (keep in memory). */ + /* MS forgot to define this one with a non-underscore form too. */ + if (ins(d, "O_SHORT_LIVED", (long)_O_SHORT_LIVED)) return -1; +#endif +#ifdef O_TEMPORARY + /* Automatically delete when last handle is closed. */ + if (ins(d, "O_TEMPORARY", (long)O_TEMPORARY)) return -1; +#endif +#ifdef O_RANDOM + /* Optimize for random access. */ + if (ins(d, "O_RANDOM", (long)O_RANDOM)) return -1; +#endif +#ifdef O_SEQUENTIAL + /* Optimize for sequential access. */ + if (ins(d, "O_SEQUENTIAL", (long)O_SEQUENTIAL)) return -1; +#endif + +/* GNU extensions. */ +#ifdef O_DIRECT + /* Direct disk access. */ + if (ins(d, "O_DIRECT", (long)O_DIRECT)) return -1; +#endif +#ifdef O_DIRECTORY + /* Must be a directory. */ + if (ins(d, "O_DIRECTORY", (long)O_DIRECTORY)) return -1; +#endif +#ifdef O_NOFOLLOW + /* Do not follow links. */ + if (ins(d, "O_NOFOLLOW", (long)O_NOFOLLOW)) return -1; +#endif + + /* These come from sysexits.h */ +#ifdef EX_OK + if (ins(d, "EX_OK", (long)EX_OK)) return -1; +#endif /* EX_OK */ +#ifdef EX_USAGE + if (ins(d, "EX_USAGE", (long)EX_USAGE)) return -1; +#endif /* EX_USAGE */ +#ifdef EX_DATAERR + if (ins(d, "EX_DATAERR", (long)EX_DATAERR)) return -1; +#endif /* EX_DATAERR */ +#ifdef EX_NOINPUT + if (ins(d, "EX_NOINPUT", (long)EX_NOINPUT)) return -1; +#endif /* EX_NOINPUT */ +#ifdef EX_NOUSER + if (ins(d, "EX_NOUSER", (long)EX_NOUSER)) return -1; +#endif /* EX_NOUSER */ +#ifdef EX_NOHOST + if (ins(d, "EX_NOHOST", (long)EX_NOHOST)) return -1; +#endif /* EX_NOHOST */ +#ifdef EX_UNAVAILABLE + if (ins(d, "EX_UNAVAILABLE", (long)EX_UNAVAILABLE)) return -1; +#endif /* EX_UNAVAILABLE */ +#ifdef EX_SOFTWARE + if (ins(d, "EX_SOFTWARE", (long)EX_SOFTWARE)) return -1; +#endif /* EX_SOFTWARE */ +#ifdef EX_OSERR + if (ins(d, "EX_OSERR", (long)EX_OSERR)) return -1; +#endif /* EX_OSERR */ +#ifdef EX_OSFILE + if (ins(d, "EX_OSFILE", (long)EX_OSFILE)) return -1; +#endif /* EX_OSFILE */ +#ifdef EX_CANTCREAT + if (ins(d, "EX_CANTCREAT", (long)EX_CANTCREAT)) return -1; +#endif /* EX_CANTCREAT */ +#ifdef EX_IOERR + if (ins(d, "EX_IOERR", (long)EX_IOERR)) return -1; +#endif /* EX_IOERR */ +#ifdef EX_TEMPFAIL + if (ins(d, "EX_TEMPFAIL", (long)EX_TEMPFAIL)) return -1; +#endif /* EX_TEMPFAIL */ +#ifdef EX_PROTOCOL + if (ins(d, "EX_PROTOCOL", (long)EX_PROTOCOL)) return -1; +#endif /* EX_PROTOCOL */ +#ifdef EX_NOPERM + if (ins(d, "EX_NOPERM", (long)EX_NOPERM)) return -1; +#endif /* EX_NOPERM */ +#ifdef EX_CONFIG + if (ins(d, "EX_CONFIG", (long)EX_CONFIG)) return -1; +#endif /* EX_CONFIG */ +#ifdef EX_NOTFOUND + if (ins(d, "EX_NOTFOUND", (long)EX_NOTFOUND)) return -1; +#endif /* EX_NOTFOUND */ + +#ifdef HAVE_SPAWNV +#if defined(PYOS_OS2) && defined(PYCC_GCC) + if (ins(d, "P_WAIT", (long)P_WAIT)) return -1; + if (ins(d, "P_NOWAIT", (long)P_NOWAIT)) return -1; + if (ins(d, "P_OVERLAY", (long)P_OVERLAY)) return -1; + if (ins(d, "P_DEBUG", (long)P_DEBUG)) return -1; + if (ins(d, "P_SESSION", (long)P_SESSION)) return -1; + if (ins(d, "P_DETACH", (long)P_DETACH)) return -1; + if (ins(d, "P_PM", (long)P_PM)) return -1; + if (ins(d, "P_DEFAULT", (long)P_DEFAULT)) return -1; + if (ins(d, "P_MINIMIZE", (long)P_MINIMIZE)) return -1; + if (ins(d, "P_MAXIMIZE", (long)P_MAXIMIZE)) return -1; + if (ins(d, "P_FULLSCREEN", (long)P_FULLSCREEN)) return -1; + if (ins(d, "P_WINDOWED", (long)P_WINDOWED)) return -1; + if (ins(d, "P_FOREGROUND", (long)P_FOREGROUND)) return -1; + if (ins(d, "P_BACKGROUND", (long)P_BACKGROUND)) return -1; + if (ins(d, "P_NOCLOSE", (long)P_NOCLOSE)) return -1; + if (ins(d, "P_NOSESSION", (long)P_NOSESSION)) return -1; + if (ins(d, "P_QUOTE", (long)P_QUOTE)) return -1; + if (ins(d, "P_TILDE", (long)P_TILDE)) return -1; + if (ins(d, "P_UNRELATED", (long)P_UNRELATED)) return -1; + if (ins(d, "P_DEBUGDESC", (long)P_DEBUGDESC)) return -1; +#else + if (ins(d, "P_WAIT", (long)_P_WAIT)) return -1; + if (ins(d, "P_NOWAIT", (long)_P_NOWAIT)) return -1; + if (ins(d, "P_OVERLAY", (long)_OLD_P_OVERLAY)) return -1; + if (ins(d, "P_NOWAITO", (long)_P_NOWAITO)) return -1; + if (ins(d, "P_DETACH", (long)_P_DETACH)) return -1; +#endif +#endif + +#if defined(PYOS_OS2) + if (insertvalues(d)) return -1; +#endif + return 0; +} + + +#if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__)) && !defined(__QNX__) +#define INITFUNC initnt +#define MODNAME "nt" + +#elif defined(PYOS_OS2) +#define INITFUNC initos2 +#define MODNAME "os2" + +#else +#define INITFUNC initposix +#define MODNAME "posix" +#endif + +PyMODINIT_FUNC +INITFUNC(void) +{ + PyObject *m, *v; + + m = Py_InitModule3(MODNAME, + posix_methods, + posix__doc__); + + /* Initialize environ dictionary */ + v = convertenviron(); + Py_XINCREF(v); + if (v == NULL || PyModule_AddObject(m, "environ", v) != 0) + return; + Py_DECREF(v); + + if (all_ins(m)) + return; + + if (setup_confname_tables(m)) + return; + + Py_INCREF(PyExc_OSError); + PyModule_AddObject(m, "error", PyExc_OSError); + +#ifdef HAVE_PUTENV + if (posix_putenv_garbage == NULL) + posix_putenv_garbage = PyDict_New(); +#endif + + stat_result_desc.name = MODNAME ".stat_result"; + stat_result_desc.fields[7].name = PyStructSequence_UnnamedField; + stat_result_desc.fields[8].name = PyStructSequence_UnnamedField; + stat_result_desc.fields[9].name = PyStructSequence_UnnamedField; + PyStructSequence_InitType(&StatResultType, &stat_result_desc); + structseq_new = StatResultType.tp_new; + StatResultType.tp_new = statresult_new; + Py_INCREF((PyObject*) &StatResultType); + PyModule_AddObject(m, "stat_result", (PyObject*) &StatResultType); + + statvfs_result_desc.name = MODNAME ".statvfs_result"; + PyStructSequence_InitType(&StatVFSResultType, &statvfs_result_desc); + Py_INCREF((PyObject*) &StatVFSResultType); + PyModule_AddObject(m, "statvfs_result", + (PyObject*) &StatVFSResultType); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/puremodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/puremodule.c new file mode 100644 index 00000000..e0d14655 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/puremodule.c @@ -0,0 +1,986 @@ +/* This module exports the C API to such Pure Software Inc. (tm) (now + * called Pure Atria Corporation) products as Purify (tm) and Quantify + * (tm). Other packages could be added, but I didn't have those products + * and thus lack the API documentation. + * + * Currently supported: Quantify 2.x, Purify 3.x + * + * You need to decide which products you want to incorporate into the + * module when you compile this file. The way to do this is to edit + * /Modules/Setup to pass the appropriate flags to the compiler. + * -DWITH_PURIFY compiles in the Purify support, and -DWITH_QUANTIFY + * compiles in the Quantify support. -DWITH_ALL_PURE compiles in both. + * You can also build a Purify'd or Quantify'd interpreter by passing in + * the LINKCC variable to make. E.g. if you want to build a Purify'd + * interpreter and are using gcc, build Python with this command: + * + * make LINKCC='purify gcc' + * + * It would be nice (and probably easy) to provide this file as a shared + * library, however since it doesn't appear that Pure gives us shared + * libraries of the stubs, it doesn't really matter. For now, you have to + * link this file in statically. + * + * Major bogosity. The purify.h header file exports purify_exit(), but + * guess what? It is not defined in the libpurify_stubs.a file! I tried + * to fake one here, hoping the Pure linker would Do The Right Thing when + * instrumented for Purify, but it doesn't seem to, so I don't export + * purify_exit() to the Python layer. In Python you should raise a + * SystemExit exception anyway. + * + * The actual purify.h and quantify.h files which embody the APIs are + * copyrighted by Pure Software, Inc. and are only attainable through them. + * This module assumes you have legally installed licenses of their + * software. Contact them on the Web via + * + * Author: Barry Warsaw + * + */ + +#include "Python.h" + +#if defined(WITH_PURIFY) || defined(WITH_ALL_PURE) +# include +# define HAS_PURIFY_EXIT 0 /* See note at top of file */ +# define PURE_PURIFY_VERSION 3 /* not provided by purify.h */ +#endif +#if defined(WITH_QUANTIFY) || defined(WITH_ALL_PURE) +# include +# define PURE_QUANTIFY_VERSION 2 /* not provided by quantify.h */ +#endif +#if defined(PURIFY_H) || defined(QUANTIFY_H) +# define COMMON_PURE_FUNCTIONS +#endif /* PURIFY_H || QUANTIFY_H */ + +typedef int (*VoidArgFunc)(void); +typedef int (*StringArgFunc)(char*); +typedef int (*PrintfishFunc)(const char*, ...); +typedef int (*StringIntArgFunc)(const char*, int); + + + +static PyObject* +call_voidarg_function(VoidArgFunc func, PyObject *self, PyObject *args) +{ + int status; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + + status = func(); + return Py_BuildValue("i", status); +} + +static PyObject* +call_stringarg_function(StringArgFunc func, PyObject *self, PyObject *args) +{ + int status; + char* stringarg; + + if (!PyArg_ParseTuple(args, "s", &stringarg)) + return NULL; + + status = func(stringarg); + return Py_BuildValue("i", status); +} + +static PyObject* +call_stringorint_function(StringArgFunc func, PyObject *self, PyObject *args) +{ + int status; + int intarg; + char* stringarg; + + /* according to the quantify.h file, the argument to + * quantify_*_recording_system_call can be an integer or a string, + * but the functions are prototyped as taking a single char* + * argument. Yikes! + */ + if (PyArg_ParseTuple(args, "i", &intarg)) + /* func is prototyped as int(*)(char*) + * better shut up the compiler + */ + status = func((char*)intarg); + + else { + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "s", &stringarg)) + return NULL; + else + status = func(stringarg); + } + return Py_BuildValue("i", status); +} + +static PyObject* +call_printfish_function(PrintfishFunc func, PyObject *self, PyObject *args) +{ + /* we support the printf() style vararg functions by requiring the + * formatting be done in Python. At the C level we pass just a string + * to the printf() style function. + */ + int status; + char* argstring; + + if (!PyArg_ParseTuple(args, "s", &argstring)) + return NULL; + + status = func("%s", argstring); + return Py_BuildValue("i", status); +} + +static PyObject* +call_intasaddr_function(StringArgFunc func, PyObject *self, PyObject *args) +{ + long memrep; + int id; + + if (!PyArg_ParseTuple(args, "l", &memrep)) + return NULL; + + id = func((char*)memrep); + return Py_BuildValue("i", id); +} + +static PyObject* +call_stringandint_function(StringIntArgFunc func, PyObject *self, + PyObject *args) +{ + long srcrep; + int size; + int status; + + if (!PyArg_ParseTuple(args, "li", &srcrep, &size)) + return NULL; + + status = func((char*)srcrep, size); + return Py_BuildValue("i", status); +} + + + +/* functions common to all products + * + * N.B. These printf() style functions are a bit of a kludge. Since the + * API doesn't provide vprintf versions of them, we can't call them + * directly. They don't support all the standard printf % modifiers + * anyway. The way to use these is to use Python's % string operator to do + * the formatting. By the time these functions get the thing to print, + * it's already a string, and they just use "%s" as the format string. + */ + +#ifdef COMMON_PURE_FUNCTIONS + +static PyObject* +pure_pure_logfile_printf(PyObject* self, PyObject* args) +{ + return call_printfish_function(pure_logfile_printf, self, args); +} + +static PyObject* +pure_pure_printf(PyObject* self, PyObject* args) +{ + return call_printfish_function(pure_printf, self, args); +} + +static PyObject* +pure_pure_printf_with_banner(PyObject* self, PyObject* args) +{ + return call_printfish_function(pure_printf_with_banner, self, args); +} + + +#endif /* COMMON_PURE_FUNCTIONS */ + + + +/* Purify functions + * + * N.B. There are some interfaces described in the purify.h file that are + * not described in the manual. + * + * Unsigned longs purify_report_{address,number,type,result} are not + * accessible from the Python layer since they seem mostly useful when + * purify_stop_here() is called by the (C) debugger. The same is true of + * the purify_stop_here_internal() function so it isn't exported either. + * And purify_stop_here() should never be called directly. + * + * The header file says purify_{new,all,clear_new}_reports() are obsolete + * so they aren't exported. + * + * None of the custom dynamic loader functions are exported. + * + * purify_unsafe_memcpy() isn't exported. + * + * purify_{start,size}_of_block() aren't exported. + * + * The manual that I have says that the prototype for the second argument + * to purify_map_pool is: + * + * void (*fn)(char*) + * + * but the purify.h file declares it as: + * + * void (*fn)(char*, int, void*) + * + * and does not explain what the other arguments are for. I support the + * latter but I don't know if I do it right or usefully. + * + * The header file says that purify_describe() returns a char* which is the + * pointer passed to it. The manual says it returns an int, but I believe + * that is a typo. + */ +#ifdef PURIFY_H + +static PyObject* +pure_purify_all_inuse(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_all_inuse, self, args); +} +static PyObject* +pure_purify_all_leaks(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_all_leaks, self, args); +} +static PyObject* +pure_purify_new_inuse(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_new_inuse, self, args); +} +static PyObject* +pure_purify_new_leaks(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_new_leaks, self, args); +} +static PyObject* +pure_purify_clear_inuse(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_clear_inuse, self, args); +} +static PyObject* +pure_purify_clear_leaks(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_clear_leaks, self, args); +} +static PyObject* +pure_purify_all_fds_inuse(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_all_fds_inuse, self, args); +} +static PyObject* +pure_purify_new_fds_inuse(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_new_fds_inuse, self, args); +} +static PyObject* +pure_purify_printf_with_call_chain(PyObject *self, PyObject *args) +{ + return call_printfish_function(purify_printf_with_call_chain, + self, args); +} +static PyObject* +pure_purify_set_pool_id(PyObject *self, PyObject *args) +{ + long memrep; + int id; + + if (!PyArg_ParseTuple(args, "li:purify_set_pool_id", &memrep, &id)) + return NULL; + + purify_set_pool_id((char*)memrep, id); + Py_INCREF(Py_None); + return Py_None; +} +static PyObject* +pure_purify_get_pool_id(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_get_pool_id, self, args); +} +static PyObject* +pure_purify_set_user_data(PyObject *self, PyObject *args) +{ + long memrep; + long datarep; + + if (!PyArg_ParseTuple(args, "ll:purify_set_user_data", &memrep, &datarep)) + return NULL; + + purify_set_user_data((char*)memrep, (void*)datarep); + Py_INCREF(Py_None); + return Py_None; +} +static PyObject* +pure_purify_get_user_data(PyObject *self, PyObject *args) +{ + /* can't use call_intasaddr_function() since purify_get_user_data() + * returns a void* + */ + long memrep; + void* data; + + if (!PyArg_ParseTuple(args, "l:purify_get_user_data", &memrep)) + return NULL; + + data = purify_get_user_data((char*)memrep); + return Py_BuildValue("l", (long)data); +} + + +/* this global variable is shared by both mapping functions: + * pure_purify_map_pool() and pure_purify_map_pool_id(). Since they cache + * this variable it should be safe in the face of recursion or cross + * calling. + * + * Further note that the prototype for the callback function is wrong in + * the Purify manual. The manual says the function takes a single char*, + * but the header file says it takes an additional int and void*. I have + * no idea what these are for! + */ +static PyObject* MapCallable = NULL; + +static void +map_pool_callback(char* mem, int user_size, void *user_aux_data) +{ + long memrep = (long)mem; + long user_aux_data_rep = (long)user_aux_data; + PyObject* result; + PyObject* memobj = Py_BuildValue("lil", memrep, user_size, + user_aux_data_rep); + + if (memobj == NULL) + return; + + result = PyEval_CallObject(MapCallable, memobj); + Py_DECREF(result); + Py_DECREF(memobj); +} + +static PyObject* +pure_purify_map_pool(PyObject *self, PyObject *args) +{ + /* cache global variable in case of recursion */ + PyObject* saved_callable = MapCallable; + PyObject* arg_callable; + int id; + + if (!PyArg_ParseTuple(args, "iO:purify_map_pool", &id, &arg_callable)) + return NULL; + + if (!PyCallable_Check(arg_callable)) { + PyErr_SetString(PyExc_TypeError, + "Second argument must be callable"); + return NULL; + } + MapCallable = arg_callable; + purify_map_pool(id, map_pool_callback); + MapCallable = saved_callable; + + Py_INCREF(Py_None); + return Py_None; +} + +static void +PurifyMapPoolIdCallback(int id) +{ + PyObject* result; + PyObject* intobj = Py_BuildValue("i", id); + + if (intobj == NULL) + return; + + result = PyEval_CallObject(MapCallable, intobj); + Py_DECREF(result); + Py_DECREF(intobj); +} + +static PyObject* +pure_purify_map_pool_id(PyObject *self, PyObject *args) +{ + /* cache global variable in case of recursion */ + PyObject* saved_callable = MapCallable; + PyObject* arg_callable; + + if (!PyArg_ParseTuple(args, "O:purify_map_pool_id", &arg_callable)) + return NULL; + + if (!PyCallable_Check(arg_callable)) { + PyErr_SetString(PyExc_TypeError, "Argument must be callable."); + return NULL; + } + + MapCallable = arg_callable; + purify_map_pool_id(PurifyMapPoolIdCallback); + MapCallable = saved_callable; + + Py_INCREF(Py_None); + return Py_None; +} + + + +static PyObject* +pure_purify_new_messages(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_new_messages, self, args); +} +static PyObject* +pure_purify_all_messages(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_all_messages, self, args); +} +static PyObject* +pure_purify_clear_messages(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_clear_messages, self, args); +} +static PyObject* +pure_purify_clear_new_messages(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_clear_new_messages, self, args); +} +static PyObject* +pure_purify_start_batch(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_start_batch, self, args); +} +static PyObject* +pure_purify_start_batch_show_first(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_start_batch_show_first, + self, args); +} +static PyObject* +pure_purify_stop_batch(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_stop_batch, self, args); +} +static PyObject* +pure_purify_name_thread(PyObject *self, PyObject *args) +{ + /* can't strictly use call_stringarg_function since + * purify_name_thread takes a const char*, not a char* + */ + int status; + char* stringarg; + + if (!PyArg_ParseTuple(args, "s:purify_name_thread", &stringarg)) + return NULL; + + status = purify_name_thread(stringarg); + return Py_BuildValue("i", status); +} +static PyObject* +pure_purify_watch(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch, self, args); +} +static PyObject* +pure_purify_watch_1(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_1, self, args); +} +static PyObject* +pure_purify_watch_2(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_2, self, args); +} +static PyObject* +pure_purify_watch_4(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_4, self, args); +} +static PyObject* +pure_purify_watch_8(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_8, self, args); +} +static PyObject* +pure_purify_watch_w_1(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_w_1, self, args); +} +static PyObject* +pure_purify_watch_w_2(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_w_2, self, args); +} +static PyObject* +pure_purify_watch_w_4(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_w_4, self, args); +} +static PyObject* +pure_purify_watch_w_8(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_w_8, self, args); +} +static PyObject* +pure_purify_watch_r_1(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_r_1, self, args); +} +static PyObject* +pure_purify_watch_r_2(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_r_2, self, args); +} +static PyObject* +pure_purify_watch_r_4(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_r_4, self, args); +} +static PyObject* +pure_purify_watch_r_8(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_r_8, self, args); +} +static PyObject* +pure_purify_watch_rw_1(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_rw_1, self, args); +} +static PyObject* +pure_purify_watch_rw_2(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_rw_2, self, args); +} +static PyObject* +pure_purify_watch_rw_4(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_rw_4, self, args); +} +static PyObject* +pure_purify_watch_rw_8(PyObject *self, PyObject *args) +{ + return call_intasaddr_function(purify_watch_rw_8, self, args); +} + +static PyObject* +pure_purify_watch_n(PyObject *self, PyObject *args) +{ + long addrrep; + unsigned int size; + char* type; + int status; + + if (!PyArg_ParseTuple(args, "lis:purify_watch_n", &addrrep, &size, &type)) + return NULL; + + status = purify_watch_n((char*)addrrep, size, type); + return Py_BuildValue("i", status); +} + +static PyObject* +pure_purify_watch_info(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_watch_info, self, args); +} + +static PyObject* +pure_purify_watch_remove(PyObject *self, PyObject *args) +{ + int watchno; + int status; + + if (!PyArg_ParseTuple(args, "i:purify_watch_remove", &watchno)) + return NULL; + + status = purify_watch_remove(watchno); + return Py_BuildValue("i", status); +} + +static PyObject* +pure_purify_watch_remove_all(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_watch_remove_all, self, args); +} +static PyObject* +pure_purify_describe(PyObject *self, PyObject *args) +{ + long addrrep; + char* rtn; + + if (!PyArg_ParseTuple(args, "l:purify_describe", &addrrep)) + return NULL; + + rtn = purify_describe((char*)addrrep); + return Py_BuildValue("l", (long)rtn); +} + +static PyObject* +pure_purify_what_colors(PyObject *self, PyObject *args) +{ + long addrrep; + unsigned int size; + int status; + + if (!PyArg_ParseTuple(args, "li:purify_what_colors", &addrrep, &size)) + return NULL; + + status = purify_what_colors((char*)addrrep, size); + return Py_BuildValue("i", status); +} + +static PyObject* +pure_purify_is_running(PyObject *self, PyObject *args) +{ + return call_voidarg_function(purify_is_running, self, args); +} + +static PyObject* +pure_purify_assert_is_readable(PyObject *self, PyObject *args) +{ + return call_stringandint_function(purify_assert_is_readable, + self, args); +} +static PyObject* +pure_purify_assert_is_writable(PyObject *self, PyObject *args) +{ + return call_stringandint_function(purify_assert_is_writable, + self, args); +} + +#if HAS_PURIFY_EXIT + +/* I wish I could include this, but I can't. See the notes at the top of + * the file. + */ + +static PyObject* +pure_purify_exit(PyObject *self, PyObject *args) +{ + int status; + + if (!PyArg_ParseTuple(args, "i:purify_exit", &status)) + return NULL; + + /* purify_exit doesn't always act like exit(). See the manual */ + purify_exit(status); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* HAS_PURIFY_EXIT */ + +#endif /* PURIFY_H */ + + + +/* Quantify functions + * + * N.B. Some of these functions are only described in the quantify.h file, + * not in the version of the hardcopy manual that I had. If you're not + * sure what some of these do, check the header file, it is documented + * fairly well. + * + * None of the custom dynamic loader functions are exported. + * + */ +#ifdef QUANTIFY_H + +static PyObject* +pure_quantify_is_running(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_is_running, self, args); +} +static PyObject* +pure_quantify_help(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_help, self, args); +} +static PyObject* +pure_quantify_print_recording_state(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_print_recording_state, + self, args); +} +static PyObject* +pure_quantify_start_recording_data(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_start_recording_data, + self, args); +} +static PyObject* +pure_quantify_stop_recording_data(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_stop_recording_data, self, args); +} +static PyObject* +pure_quantify_is_recording_data(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_is_recording_data, self, args); +} +static PyObject* +pure_quantify_start_recording_system_calls(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_start_recording_system_calls, + self, args); +} +static PyObject* +pure_quantify_stop_recording_system_calls(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_stop_recording_system_calls, + self, args); +} +static PyObject* +pure_quantify_is_recording_system_calls(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_is_recording_system_calls, + self, args); +} +static PyObject* +pure_quantify_start_recording_system_call(PyObject *self, PyObject *args) +{ + return call_stringorint_function(quantify_start_recording_system_call, + self, args); +} +static PyObject* +pure_quantify_stop_recording_system_call(PyObject *self, PyObject *args) +{ + return call_stringorint_function(quantify_stop_recording_system_call, + self, args); +} +static PyObject* +pure_quantify_is_recording_system_call(PyObject *self, PyObject *args) +{ + return call_stringorint_function(quantify_is_recording_system_call, + self, args); +} +static PyObject* +pure_quantify_start_recording_dynamic_library_data(PyObject *self, PyObject *args) +{ + return call_voidarg_function( + quantify_start_recording_dynamic_library_data, + self, args); +} +static PyObject* +pure_quantify_stop_recording_dynamic_library_data(PyObject *self, PyObject *args) +{ + return call_voidarg_function( + quantify_stop_recording_dynamic_library_data, + self, args); +} +static PyObject* +pure_quantify_is_recording_dynamic_library_data(PyObject *self, PyObject *args) +{ + return call_voidarg_function( + quantify_is_recording_dynamic_library_data, + self, args); +} +static PyObject* +pure_quantify_start_recording_register_window_traps(PyObject *self, PyObject *args) +{ + return call_voidarg_function( + quantify_start_recording_register_window_traps, + self, args); +} +static PyObject* +pure_quantify_stop_recording_register_window_traps(PyObject *self, PyObject *args) +{ + return call_voidarg_function( + quantify_stop_recording_register_window_traps, + self, args); +} +static PyObject* +pure_quantify_is_recording_register_window_traps(PyObject *self, PyObject *args) +{ + return call_voidarg_function( + quantify_is_recording_register_window_traps, + self, args); +} +static PyObject* +pure_quantify_disable_recording_data(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_disable_recording_data, + self, args); +} +static PyObject* +pure_quantify_clear_data(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_clear_data, self, args); +} +static PyObject* +pure_quantify_save_data(PyObject *self, PyObject *args) +{ + return call_voidarg_function(quantify_save_data, self, args); +} +static PyObject* +pure_quantify_save_data_to_file(PyObject *self, PyObject *args) +{ + return call_stringarg_function(quantify_save_data_to_file, self, args); +} +static PyObject* +pure_quantify_add_annotation(PyObject *self, PyObject *args) +{ + return call_stringarg_function(quantify_add_annotation, self, args); +} + +#endif /* QUANTIFY_H */ + + + +/* external interface + */ +static struct PyMethodDef +pure_methods[] = { +#ifdef COMMON_PURE_FUNCTIONS + {"pure_logfile_printf", pure_pure_logfile_printf, METH_VARARGS}, + {"pure_printf", pure_pure_printf, METH_VARARGS}, + {"pure_printf_with_banner", pure_pure_printf_with_banner, METH_VARARGS}, +#endif /* COMMON_PURE_FUNCTIONS */ +#ifdef PURIFY_H + {"purify_all_inuse", pure_purify_all_inuse, METH_VARARGS}, + {"purify_all_leaks", pure_purify_all_leaks, METH_VARARGS}, + {"purify_new_inuse", pure_purify_new_inuse, METH_VARARGS}, + {"purify_new_leaks", pure_purify_new_leaks, METH_VARARGS}, + {"purify_clear_inuse", pure_purify_clear_inuse, METH_VARARGS}, + {"purify_clear_leaks", pure_purify_clear_leaks, METH_VARARGS}, + {"purify_all_fds_inuse", pure_purify_all_fds_inuse, METH_VARARGS}, + {"purify_new_fds_inuse", pure_purify_new_fds_inuse, METH_VARARGS}, + /* see purify.h */ + {"purify_logfile_printf", pure_pure_logfile_printf, METH_VARARGS}, + {"purify_printf", pure_pure_printf, METH_VARARGS}, + {"purify_printf_with_banner", pure_pure_printf_with_banner, METH_VARARGS}, + /**/ + {"purify_printf_with_call_chain", pure_purify_printf_with_call_chain, METH_VARARGS}, + {"purify_set_pool_id", pure_purify_set_pool_id, METH_VARARGS}, + {"purify_get_pool_id", pure_purify_get_pool_id, METH_VARARGS}, + {"purify_set_user_data", pure_purify_set_user_data, METH_VARARGS}, + {"purify_get_user_data", pure_purify_get_user_data, METH_VARARGS}, + {"purify_map_pool", pure_purify_map_pool, METH_VARARGS}, + {"purify_map_pool_id", pure_purify_map_pool_id, METH_VARARGS}, + {"purify_new_messages", pure_purify_new_messages, METH_VARARGS}, + {"purify_all_messages", pure_purify_all_messages, METH_VARARGS}, + {"purify_clear_messages", pure_purify_clear_messages, METH_VARARGS}, + {"purify_clear_new_messages", pure_purify_clear_new_messages, METH_VARARGS}, + {"purify_start_batch", pure_purify_start_batch, METH_VARARGS}, + {"purify_start_batch_show_first", pure_purify_start_batch_show_first, METH_VARARGS}, + {"purify_stop_batch", pure_purify_stop_batch, METH_VARARGS}, + {"purify_name_thread", pure_purify_name_thread, METH_VARARGS}, + {"purify_watch", pure_purify_watch, METH_VARARGS}, + {"purify_watch_1", pure_purify_watch_1, METH_VARARGS}, + {"purify_watch_2", pure_purify_watch_2, METH_VARARGS}, + {"purify_watch_4", pure_purify_watch_4, METH_VARARGS}, + {"purify_watch_8", pure_purify_watch_8, METH_VARARGS}, + {"purify_watch_w_1", pure_purify_watch_w_1, METH_VARARGS}, + {"purify_watch_w_2", pure_purify_watch_w_2, METH_VARARGS}, + {"purify_watch_w_4", pure_purify_watch_w_4, METH_VARARGS}, + {"purify_watch_w_8", pure_purify_watch_w_8, METH_VARARGS}, + {"purify_watch_r_1", pure_purify_watch_r_1, METH_VARARGS}, + {"purify_watch_r_2", pure_purify_watch_r_2, METH_VARARGS}, + {"purify_watch_r_4", pure_purify_watch_r_4, METH_VARARGS}, + {"purify_watch_r_8", pure_purify_watch_r_8, METH_VARARGS}, + {"purify_watch_rw_1", pure_purify_watch_rw_1, METH_VARARGS}, + {"purify_watch_rw_2", pure_purify_watch_rw_2, METH_VARARGS}, + {"purify_watch_rw_4", pure_purify_watch_rw_4, METH_VARARGS}, + {"purify_watch_rw_8", pure_purify_watch_rw_8, METH_VARARGS}, + {"purify_watch_n", pure_purify_watch_n, METH_VARARGS}, + {"purify_watch_info", pure_purify_watch_info, METH_VARARGS}, + {"purify_watch_remove", pure_purify_watch_remove, METH_VARARGS}, + {"purify_watch_remove_all", pure_purify_watch_remove_all, METH_VARARGS}, + {"purify_describe", pure_purify_describe, METH_VARARGS}, + {"purify_what_colors", pure_purify_what_colors, METH_VARARGS}, + {"purify_is_running", pure_purify_is_running, METH_VARARGS}, + {"purify_assert_is_readable", pure_purify_assert_is_readable, METH_VARARGS}, + {"purify_assert_is_writable", pure_purify_assert_is_writable, METH_VARARGS}, +#if HAS_PURIFY_EXIT + /* I wish I could include this, but I can't. See the notes at the + * top of the file. + */ + {"purify_exit", pure_purify_exit, METH_VARARGS}, +#endif /* HAS_PURIFY_EXIT */ +#endif /* PURIFY_H */ +#ifdef QUANTIFY_H + {"quantify_is_running", pure_quantify_is_running, METH_VARARGS}, + {"quantify_help", pure_quantify_help, METH_VARARGS}, + {"quantify_print_recording_state", pure_quantify_print_recording_state, METH_VARARGS}, + {"quantify_start_recording_data", pure_quantify_start_recording_data, METH_VARARGS}, + {"quantify_stop_recording_data", pure_quantify_stop_recording_data, METH_VARARGS}, + {"quantify_is_recording_data", pure_quantify_is_recording_data, METH_VARARGS}, + {"quantify_start_recording_system_calls", + pure_quantify_start_recording_system_calls, METH_VARARGS}, + {"quantify_stop_recording_system_calls", + pure_quantify_stop_recording_system_calls, METH_VARARGS}, + {"quantify_is_recording_system_calls", + pure_quantify_is_recording_system_calls, METH_VARARGS}, + {"quantify_start_recording_system_call", + pure_quantify_start_recording_system_call, METH_VARARGS}, + {"quantify_stop_recording_system_call", + pure_quantify_stop_recording_system_call, METH_VARARGS}, + {"quantify_is_recording_system_call", + pure_quantify_is_recording_system_call, METH_VARARGS}, + {"quantify_start_recording_dynamic_library_data", + pure_quantify_start_recording_dynamic_library_data, METH_VARARGS}, + {"quantify_stop_recording_dynamic_library_data", + pure_quantify_stop_recording_dynamic_library_data, METH_VARARGS}, + {"quantify_is_recording_dynamic_library_data", + pure_quantify_is_recording_dynamic_library_data, METH_VARARGS}, + {"quantify_start_recording_register_window_traps", + pure_quantify_start_recording_register_window_traps, METH_VARARGS}, + {"quantify_stop_recording_register_window_traps", + pure_quantify_stop_recording_register_window_traps, METH_VARARGS}, + {"quantify_is_recording_register_window_traps", + pure_quantify_is_recording_register_window_traps, METH_VARARGS}, + {"quantify_disable_recording_data", + pure_quantify_disable_recording_data, METH_VARARGS}, + {"quantify_clear_data", pure_quantify_clear_data, METH_VARARGS}, + {"quantify_save_data", pure_quantify_save_data, METH_VARARGS}, + {"quantify_save_data_to_file", pure_quantify_save_data_to_file, METH_VARARGS}, + {"quantify_add_annotation", pure_quantify_add_annotation, METH_VARARGS}, +#endif /* QUANTIFY_H */ + {NULL, NULL} /* sentinel */ +}; + + + +static void +ins(d, name, val) + PyObject *d; + char* name; + long val; +{ + PyObject *v = PyInt_FromLong(val); + if (v) { + (void)PyDict_SetItemString(d, name, v); + Py_DECREF(v); + } +} + + +void +initpure() +{ + PyObject *m, *d; + + m = Py_InitModule("pure", pure_methods); + d = PyModule_GetDict(m); + + /* this is bogus because we should be able to find this information + * out from the header files. Pure's current versions don't + * include this information! + */ +#ifdef PURE_PURIFY_VERSION + ins(d, "PURIFY_VERSION", PURE_PURIFY_VERSION); +#else + PyDict_SetItemString(d, "PURIFY_VERSION", Py_None); +#endif + + /* these aren't terribly useful because purify_exit() isn't + * exported correctly. See the note at the top of the file. + */ +#ifdef PURIFY_EXIT_ERRORS + ins(d, "PURIFY_EXIT_ERRORS", PURIFY_EXIT_ERRORS); +#endif +#ifdef PURIFY_EXIT_LEAKS + ins(d, "PURIFY_EXIT_LEAKS", PURIFY_EXIT_LEAKS); +#endif +#ifdef PURIFY_EXIT_PLEAKS + ins(d, "PURIFY_EXIT_PLEAKS", PURIFY_EXIT_PLEAKS); +#endif + + +#ifdef PURE_QUANTIFY_VERSION + ins(d, "QUANTIFY_VERSION", PURE_QUANTIFY_VERSION); +#else + PyDict_SetItemString(d, "QUANTIFY_VERSION", Py_None); +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pwdmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pwdmodule.c new file mode 100644 index 00000000..2a306252 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pwdmodule.c @@ -0,0 +1,188 @@ + +/* UNIX password file access module */ + +#include "Python.h" +#include "structseq.h" + +#include +#include + +static PyStructSequence_Field struct_pwd_type_fields[] = { + {"pw_name", "user name"}, + {"pw_passwd", "password"}, + {"pw_uid", "user id"}, + {"pw_gid", "group id"}, + {"pw_gecos", "real name"}, + {"pw_dir", "home directory"}, + {"pw_shell", "shell program"}, + {0} +}; + +PyDoc_STRVAR(struct_passwd__doc__, +"pwd.struct_passwd: Results from getpw*() routines.\n\n\ +This object may be accessed either as a tuple of\n\ + (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell)\n\ +or via the object attributes as named in the above tuple."); + +static PyStructSequence_Desc struct_pwd_type_desc = { + "pwd.struct_passwd", + struct_passwd__doc__, + struct_pwd_type_fields, + 7, +}; + +PyDoc_STRVAR(pwd__doc__, +"This module provides access to the Unix password database.\n\ +It is available on all Unix versions.\n\ +\n\ +Password database entries are reported as 7-tuples containing the following\n\ +items from the password database (see `'), in order:\n\ +pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell.\n\ +The uid and gid items are integers, all others are strings. An\n\ +exception is raised if the entry asked for cannot be found."); + + +static PyTypeObject StructPwdType; + +static void +sets(PyObject *v, int i, char* val) +{ + if (val) + PyStructSequence_SET_ITEM(v, i, PyString_FromString(val)); + else { + PyStructSequence_SET_ITEM(v, i, Py_None); + Py_INCREF(Py_None); + } +} + +static PyObject * +mkpwent(struct passwd *p) +{ + int setIndex = 0; + PyObject *v = PyStructSequence_New(&StructPwdType); + if (v == NULL) + return NULL; + +#define SETI(i,val) PyStructSequence_SET_ITEM(v, i, PyInt_FromLong((long) val)) +#define SETS(i,val) sets(v, i, val) + + SETS(setIndex++, p->pw_name); +#ifdef __VMS + SETS(setIndex++, ""); +#else + SETS(setIndex++, p->pw_passwd); +#endif + SETI(setIndex++, p->pw_uid); + SETI(setIndex++, p->pw_gid); +#ifdef __VMS + SETS(setIndex++, ""); +#else + SETS(setIndex++, p->pw_gecos); +#endif + SETS(setIndex++, p->pw_dir); + SETS(setIndex++, p->pw_shell); + +#undef SETS +#undef SETI + + if (PyErr_Occurred()) { + Py_XDECREF(v); + return NULL; + } + + return v; +} + +PyDoc_STRVAR(pwd_getpwuid__doc__, +"getpwuid(uid) -> (pw_name,pw_passwd,pw_uid,\n\ + pw_gid,pw_gecos,pw_dir,pw_shell)\n\ +Return the password database entry for the given numeric user ID.\n\ +See pwd.__doc__ for more on password database entries."); + +static PyObject * +pwd_getpwuid(PyObject *self, PyObject *args) +{ + int uid; + struct passwd *p; + if (!PyArg_ParseTuple(args, "i:getpwuid", &uid)) + return NULL; + if ((p = getpwuid(uid)) == NULL) { + PyErr_SetString(PyExc_KeyError, "getpwuid(): uid not found"); + return NULL; + } + return mkpwent(p); +} + +PyDoc_STRVAR(pwd_getpwnam__doc__, +"getpwnam(name) -> (pw_name,pw_passwd,pw_uid,\n\ + pw_gid,pw_gecos,pw_dir,pw_shell)\n\ +Return the password database entry for the given user name.\n\ +See pwd.__doc__ for more on password database entries."); + +static PyObject * +pwd_getpwnam(PyObject *self, PyObject *args) +{ + char *name; + struct passwd *p; + if (!PyArg_ParseTuple(args, "s:getpwnam", &name)) + return NULL; + if ((p = getpwnam(name)) == NULL) { + PyErr_SetString(PyExc_KeyError, "getpwnam(): name not found"); + return NULL; + } + return mkpwent(p); +} + +#ifdef HAVE_GETPWENT +PyDoc_STRVAR(pwd_getpwall__doc__, +"getpwall() -> list_of_entries\n\ +Return a list of all available password database entries, \ +in arbitrary order.\n\ +See pwd.__doc__ for more on password database entries."); + +static PyObject * +pwd_getpwall(PyObject *self) +{ + PyObject *d; + struct passwd *p; + if ((d = PyList_New(0)) == NULL) + return NULL; +#if defined(PYOS_OS2) && defined(PYCC_GCC) + if ((p = getpwuid(0)) != NULL) { +#else + setpwent(); + while ((p = getpwent()) != NULL) { +#endif + PyObject *v = mkpwent(p); + if (v == NULL || PyList_Append(d, v) != 0) { + Py_XDECREF(v); + Py_DECREF(d); + return NULL; + } + Py_DECREF(v); + } + endpwent(); + return d; +} +#endif + +static PyMethodDef pwd_methods[] = { + {"getpwuid", pwd_getpwuid, METH_VARARGS, pwd_getpwuid__doc__}, + {"getpwnam", pwd_getpwnam, METH_VARARGS, pwd_getpwnam__doc__}, +#ifdef HAVE_GETPWENT + {"getpwall", (PyCFunction)pwd_getpwall, + METH_NOARGS, pwd_getpwall__doc__}, +#endif + {NULL, NULL} /* sentinel */ +}; + +PyMODINIT_FUNC +initpwd(void) +{ + PyObject *m; + m = Py_InitModule3("pwd", pwd_methods, pwd__doc__); + + PyStructSequence_InitType(&StructPwdType, &struct_pwd_type_desc); + Py_INCREF((PyObject *) &StructPwdType); + PyModule_AddObject(m, "struct_pwent", (PyObject *) &StructPwdType); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pyexpat.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pyexpat.c new file mode 100644 index 00000000..73b9f2d4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pyexpat.c @@ -0,0 +1,2057 @@ +#include "Python.h" +#include + +#include "compile.h" +#include "frameobject.h" +#include "expat.h" + +#define XML_COMBINED_VERSION (10000*XML_MAJOR_VERSION+100*XML_MINOR_VERSION+XML_MICRO_VERSION) + +#ifndef PyDoc_STRVAR + +/* + * fdrake says: + * Don't change the PyDoc_STR macro definition to (str), because + * '''the parentheses cause compile failures + * ("non-constant static initializer" or something like that) + * on some platforms (Irix?)''' + */ +#define PyDoc_STR(str) str +#define PyDoc_VAR(name) static char name[] +#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str) +#endif + +#if (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 2) +/* In Python 2.0 and 2.1, disabling Unicode was not possible. */ +#define Py_USING_UNICODE +#else +#define FIX_TRACE +#endif + +enum HandlerTypes { + StartElement, + EndElement, + ProcessingInstruction, + CharacterData, + UnparsedEntityDecl, + NotationDecl, + StartNamespaceDecl, + EndNamespaceDecl, + Comment, + StartCdataSection, + EndCdataSection, + Default, + DefaultHandlerExpand, + NotStandalone, + ExternalEntityRef, + StartDoctypeDecl, + EndDoctypeDecl, + EntityDecl, + XmlDecl, + ElementDecl, + AttlistDecl, +#if XML_COMBINED_VERSION >= 19504 + SkippedEntity, +#endif + _DummyDecl +}; + +static PyObject *ErrorObject; + +/* ----------------------------------------------------- */ + +/* Declarations for objects of type xmlparser */ + +typedef struct { + PyObject_HEAD + + XML_Parser itself; + int returns_unicode; /* True if Unicode strings are returned; + if false, UTF-8 strings are returned */ + int ordered_attributes; /* Return attributes as a list. */ + int specified_attributes; /* Report only specified attributes. */ + int in_callback; /* Is a callback active? */ + int ns_prefixes; /* Namespace-triplets mode? */ + XML_Char *buffer; /* Buffer used when accumulating characters */ + /* NULL if not enabled */ + int buffer_size; /* Size of buffer, in XML_Char units */ + int buffer_used; /* Buffer units in use */ + PyObject *intern; /* Dictionary to intern strings */ + PyObject **handlers; +} xmlparseobject; + +#define CHARACTER_DATA_BUFFER_SIZE 8192 + +static PyTypeObject Xmlparsetype; + +typedef void (*xmlhandlersetter)(XML_Parser self, void *meth); +typedef void* xmlhandler; + +struct HandlerInfo { + const char *name; + xmlhandlersetter setter; + xmlhandler handler; + PyCodeObject *tb_code; + PyObject *nameobj; +}; + +static struct HandlerInfo handler_info[64]; + +/* Set an integer attribute on the error object; return true on success, + * false on an exception. + */ +static int +set_error_attr(PyObject *err, char *name, int value) +{ + PyObject *v = PyInt_FromLong(value); + + if (v != NULL && PyObject_SetAttrString(err, name, v) == -1) { + Py_DECREF(v); + return 0; + } + return 1; +} + +/* Build and set an Expat exception, including positioning + * information. Always returns NULL. + */ +static PyObject * +set_error(xmlparseobject *self, enum XML_Error code) +{ + PyObject *err; + char buffer[256]; + XML_Parser parser = self->itself; + int lineno = XML_GetErrorLineNumber(parser); + int column = XML_GetErrorColumnNumber(parser); + + /* There is no risk of overflowing this buffer, since + even for 64-bit integers, there is sufficient space. */ + sprintf(buffer, "%.200s: line %i, column %i", + XML_ErrorString(code), lineno, column); + err = PyObject_CallFunction(ErrorObject, "s", buffer); + if ( err != NULL + && set_error_attr(err, "code", code) + && set_error_attr(err, "offset", column) + && set_error_attr(err, "lineno", lineno)) { + PyErr_SetObject(ErrorObject, err); + } + return NULL; +} + +static int +have_handler(xmlparseobject *self, int type) +{ + PyObject *handler = self->handlers[type]; + return handler != NULL; +} + +static PyObject * +get_handler_name(struct HandlerInfo *hinfo) +{ + PyObject *name = hinfo->nameobj; + if (name == NULL) { + name = PyString_FromString(hinfo->name); + hinfo->nameobj = name; + } + Py_XINCREF(name); + return name; +} + + +#ifdef Py_USING_UNICODE +/* Convert a string of XML_Chars into a Unicode string. + Returns None if str is a null pointer. */ + +static PyObject * +conv_string_to_unicode(const XML_Char *str) +{ + /* XXX currently this code assumes that XML_Char is 8-bit, + and hence in UTF-8. */ + /* UTF-8 from Expat, Unicode desired */ + if (str == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyUnicode_DecodeUTF8(str, strlen(str), "strict"); +} + +static PyObject * +conv_string_len_to_unicode(const XML_Char *str, int len) +{ + /* XXX currently this code assumes that XML_Char is 8-bit, + and hence in UTF-8. */ + /* UTF-8 from Expat, Unicode desired */ + if (str == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyUnicode_DecodeUTF8((const char *)str, len, "strict"); +} +#endif + +/* Convert a string of XML_Chars into an 8-bit Python string. + Returns None if str is a null pointer. */ + +static PyObject * +conv_string_to_utf8(const XML_Char *str) +{ + /* XXX currently this code assumes that XML_Char is 8-bit, + and hence in UTF-8. */ + /* UTF-8 from Expat, UTF-8 desired */ + if (str == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString(str); +} + +static PyObject * +conv_string_len_to_utf8(const XML_Char *str, int len) +{ + /* XXX currently this code assumes that XML_Char is 8-bit, + and hence in UTF-8. */ + /* UTF-8 from Expat, UTF-8 desired */ + if (str == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromStringAndSize((const char *)str, len); +} + +/* Callback routines */ + +static void clear_handlers(xmlparseobject *self, int initial); + +/* This handler is used when an error has been detected, in the hope + that actual parsing can be terminated early. This will only help + if an external entity reference is encountered. */ +static int +error_external_entity_ref_handler(XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId) +{ + return 0; +} + +static void +flag_error(xmlparseobject *self) +{ + clear_handlers(self, 0); + XML_SetExternalEntityRefHandler(self->itself, + error_external_entity_ref_handler); +} + +static PyCodeObject* +getcode(enum HandlerTypes slot, char* func_name, int lineno) +{ + PyObject *code = NULL; + PyObject *name = NULL; + PyObject *nulltuple = NULL; + PyObject *filename = NULL; + + if (handler_info[slot].tb_code == NULL) { + code = PyString_FromString(""); + if (code == NULL) + goto failed; + name = PyString_FromString(func_name); + if (name == NULL) + goto failed; + nulltuple = PyTuple_New(0); + if (nulltuple == NULL) + goto failed; + filename = PyString_FromString(__FILE__); + handler_info[slot].tb_code = + PyCode_New(0, /* argcount */ + 0, /* nlocals */ + 0, /* stacksize */ + 0, /* flags */ + code, /* code */ + nulltuple, /* consts */ + nulltuple, /* names */ + nulltuple, /* varnames */ +#if PYTHON_API_VERSION >= 1010 + nulltuple, /* freevars */ + nulltuple, /* cellvars */ +#endif + filename, /* filename */ + name, /* name */ + lineno, /* firstlineno */ + code /* lnotab */ + ); + if (handler_info[slot].tb_code == NULL) + goto failed; + Py_DECREF(code); + Py_DECREF(nulltuple); + Py_DECREF(filename); + Py_DECREF(name); + } + return handler_info[slot].tb_code; + failed: + Py_XDECREF(code); + Py_XDECREF(name); + return NULL; +} + +#ifdef FIX_TRACE +static int +trace_frame(PyThreadState *tstate, PyFrameObject *f, int code, PyObject *val) +{ + int result = 0; + if (!tstate->use_tracing || tstate->tracing) + return 0; + if (tstate->c_profilefunc != NULL) { + tstate->tracing++; + result = tstate->c_profilefunc(tstate->c_profileobj, + f, code , val); + tstate->use_tracing = ((tstate->c_tracefunc != NULL) + || (tstate->c_profilefunc != NULL)); + tstate->tracing--; + if (result) + return result; + } + if (tstate->c_tracefunc != NULL) { + tstate->tracing++; + result = tstate->c_tracefunc(tstate->c_traceobj, + f, code , val); + tstate->use_tracing = ((tstate->c_tracefunc != NULL) + || (tstate->c_profilefunc != NULL)); + tstate->tracing--; + } + return result; +} + +static int +trace_frame_exc(PyThreadState *tstate, PyFrameObject *f) +{ + PyObject *type, *value, *traceback, *arg; + int err; + + if (tstate->c_tracefunc == NULL) + return 0; + + PyErr_Fetch(&type, &value, &traceback); + if (value == NULL) { + value = Py_None; + Py_INCREF(value); + } + arg = Py_BuildValue("(OOO)", type, value, traceback); + if (arg == NULL) { + PyErr_Restore(type, value, traceback); + return 0; + } + err = trace_frame(tstate, f, PyTrace_EXCEPTION, arg); + Py_DECREF(arg); + if (err == 0) + PyErr_Restore(type, value, traceback); + else { + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); + } + return err; +} +#endif + +static PyObject* +call_with_frame(PyCodeObject *c, PyObject* func, PyObject* args) +{ + PyThreadState *tstate = PyThreadState_GET(); + PyFrameObject *f; + PyObject *res; + + if (c == NULL) + return NULL; + + f = PyFrame_New(tstate, c, PyEval_GetGlobals(), NULL); + if (f == NULL) + return NULL; + tstate->frame = f; +#ifdef FIX_TRACE + if (trace_frame(tstate, f, PyTrace_CALL, Py_None) < 0) { + return NULL; + } +#endif + res = PyEval_CallObject(func, args); + if (res == NULL) { + if (tstate->curexc_traceback == NULL) + PyTraceBack_Here(f); +#ifdef FIX_TRACE + if (trace_frame_exc(tstate, f) < 0) { + return NULL; + } + } + else { + if (trace_frame(tstate, f, PyTrace_RETURN, res) < 0) { + Py_XDECREF(res); + res = NULL; + } + } +#else + } +#endif + tstate->frame = f->f_back; + Py_DECREF(f); + return res; +} + +#ifndef Py_USING_UNICODE +#define STRING_CONV_FUNC conv_string_to_utf8 +#else +/* Python 2.0 and later versions, when built with Unicode support */ +#define STRING_CONV_FUNC (self->returns_unicode \ + ? conv_string_to_unicode : conv_string_to_utf8) +#endif + +static PyObject* +string_intern(xmlparseobject *self, const char* str) +{ + PyObject *result = STRING_CONV_FUNC(str); + PyObject *value; + if (!self->intern) + return result; + value = PyDict_GetItem(self->intern, result); + if (!value) { + if (PyDict_SetItem(self->intern, result, result) == 0) + return result; + else + return NULL; + } + Py_INCREF(value); + Py_DECREF(result); + return value; +} + +/* Return 0 on success, -1 on exception. + * flag_error() will be called before return if needed. + */ +static int +call_character_handler(xmlparseobject *self, const XML_Char *buffer, int len) +{ + PyObject *args; + PyObject *temp; + + args = PyTuple_New(1); + if (args == NULL) + return -1; +#ifdef Py_USING_UNICODE + temp = (self->returns_unicode + ? conv_string_len_to_unicode(buffer, len) + : conv_string_len_to_utf8(buffer, len)); +#else + temp = conv_string_len_to_utf8(buffer, len); +#endif + if (temp == NULL) { + Py_DECREF(args); + flag_error(self); + return -1; + } + PyTuple_SET_ITEM(args, 0, temp); + /* temp is now a borrowed reference; consider it unused. */ + self->in_callback = 1; + temp = call_with_frame(getcode(CharacterData, "CharacterData", __LINE__), + self->handlers[CharacterData], args); + /* temp is an owned reference again, or NULL */ + self->in_callback = 0; + Py_DECREF(args); + if (temp == NULL) { + flag_error(self); + return -1; + } + Py_DECREF(temp); + return 0; +} + +static int +flush_character_buffer(xmlparseobject *self) +{ + int rc; + if (self->buffer == NULL || self->buffer_used == 0) + return 0; + rc = call_character_handler(self, self->buffer, self->buffer_used); + self->buffer_used = 0; + return rc; +} + +static void +my_CharacterDataHandler(void *userData, const XML_Char *data, int len) +{ + xmlparseobject *self = (xmlparseobject *) userData; + if (self->buffer == NULL) + call_character_handler(self, data, len); + else { + if ((self->buffer_used + len) > self->buffer_size) { + if (flush_character_buffer(self) < 0) + return; + /* handler might have changed; drop the rest on the floor + * if there isn't a handler anymore + */ + if (!have_handler(self, CharacterData)) + return; + } + if (len > self->buffer_size) { + call_character_handler(self, data, len); + self->buffer_used = 0; + } + else { + memcpy(self->buffer + self->buffer_used, + data, len * sizeof(XML_Char)); + self->buffer_used += len; + } + } +} + +static void +my_StartElementHandler(void *userData, + const XML_Char *name, const XML_Char *atts[]) +{ + xmlparseobject *self = (xmlparseobject *)userData; + + if (have_handler(self, StartElement)) { + PyObject *container, *rv, *args; + int i, max; + + if (flush_character_buffer(self) < 0) + return; + /* Set max to the number of slots filled in atts[]; max/2 is + * the number of attributes we need to process. + */ + if (self->specified_attributes) { + max = XML_GetSpecifiedAttributeCount(self->itself); + } + else { + max = 0; + while (atts[max] != NULL) + max += 2; + } + /* Build the container. */ + if (self->ordered_attributes) + container = PyList_New(max); + else + container = PyDict_New(); + if (container == NULL) { + flag_error(self); + return; + } + for (i = 0; i < max; i += 2) { + PyObject *n = string_intern(self, (XML_Char *) atts[i]); + PyObject *v; + if (n == NULL) { + flag_error(self); + Py_DECREF(container); + return; + } + v = STRING_CONV_FUNC((XML_Char *) atts[i+1]); + if (v == NULL) { + flag_error(self); + Py_DECREF(container); + Py_DECREF(n); + return; + } + if (self->ordered_attributes) { + PyList_SET_ITEM(container, i, n); + PyList_SET_ITEM(container, i+1, v); + } + else if (PyDict_SetItem(container, n, v)) { + flag_error(self); + Py_DECREF(n); + Py_DECREF(v); + return; + } + else { + Py_DECREF(n); + Py_DECREF(v); + } + } + args = Py_BuildValue("(NN)", string_intern(self, name), container); + if (args == NULL) { + Py_DECREF(container); + return; + } + /* Container is now a borrowed reference; ignore it. */ + self->in_callback = 1; + rv = call_with_frame(getcode(StartElement, "StartElement", __LINE__), + self->handlers[StartElement], args); + self->in_callback = 0; + Py_DECREF(args); + if (rv == NULL) { + flag_error(self); + return; + } + Py_DECREF(rv); + } +} + +#define RC_HANDLER(RC, NAME, PARAMS, INIT, PARAM_FORMAT, CONVERSION, \ + RETURN, GETUSERDATA) \ +static RC \ +my_##NAME##Handler PARAMS {\ + xmlparseobject *self = GETUSERDATA ; \ + PyObject *args = NULL; \ + PyObject *rv = NULL; \ + INIT \ +\ + if (have_handler(self, NAME)) { \ + if (flush_character_buffer(self) < 0) \ + return RETURN; \ + args = Py_BuildValue PARAM_FORMAT ;\ + if (!args) { flag_error(self); return RETURN;} \ + self->in_callback = 1; \ + rv = call_with_frame(getcode(NAME,#NAME,__LINE__), \ + self->handlers[NAME], args); \ + self->in_callback = 0; \ + Py_DECREF(args); \ + if (rv == NULL) { \ + flag_error(self); \ + return RETURN; \ + } \ + CONVERSION \ + Py_DECREF(rv); \ + } \ + return RETURN; \ +} + +#define VOID_HANDLER(NAME, PARAMS, PARAM_FORMAT) \ + RC_HANDLER(void, NAME, PARAMS, ;, PARAM_FORMAT, ;, ;,\ + (xmlparseobject *)userData) + +#define INT_HANDLER(NAME, PARAMS, PARAM_FORMAT)\ + RC_HANDLER(int, NAME, PARAMS, int rc=0;, PARAM_FORMAT, \ + rc = PyInt_AsLong(rv);, rc, \ + (xmlparseobject *)userData) + +VOID_HANDLER(EndElement, + (void *userData, const XML_Char *name), + ("(N)", string_intern(self, name))) + +VOID_HANDLER(ProcessingInstruction, + (void *userData, + const XML_Char *target, + const XML_Char *data), + ("(NO&)", string_intern(self, target), STRING_CONV_FUNC,data)) + +VOID_HANDLER(UnparsedEntityDecl, + (void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName), + ("(NNNNN)", + string_intern(self, entityName), string_intern(self, base), + string_intern(self, systemId), string_intern(self, publicId), + string_intern(self, notationName))) + +#ifndef Py_USING_UNICODE +VOID_HANDLER(EntityDecl, + (void *userData, + const XML_Char *entityName, + int is_parameter_entity, + const XML_Char *value, + int value_length, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName), + ("NiNNNNN", + string_intern(self, entityName), is_parameter_entity, + conv_string_len_to_utf8(value, value_length), + string_intern(self, base), string_intern(self, systemId), + string_intern(self, publicId), + string_intern(self, notationName))) +#else +VOID_HANDLER(EntityDecl, + (void *userData, + const XML_Char *entityName, + int is_parameter_entity, + const XML_Char *value, + int value_length, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName), + ("NiNNNNN", + string_intern(self, entityName), is_parameter_entity, + (self->returns_unicode + ? conv_string_len_to_unicode(value, value_length) + : conv_string_len_to_utf8(value, value_length)), + string_intern(self, base), string_intern(self, systemId), + string_intern(self, publicId), + string_intern(self, notationName))) +#endif + +VOID_HANDLER(XmlDecl, + (void *userData, + const XML_Char *version, + const XML_Char *encoding, + int standalone), + ("(O&O&i)", + STRING_CONV_FUNC,version, STRING_CONV_FUNC,encoding, + standalone)) + +static PyObject * +conv_content_model(XML_Content * const model, + PyObject *(*conv_string)(const XML_Char *)) +{ + PyObject *result = NULL; + PyObject *children = PyTuple_New(model->numchildren); + int i; + + if (children != NULL) { + assert(model->numchildren < INT_MAX); + for (i = 0; i < (int)model->numchildren; ++i) { + PyObject *child = conv_content_model(&model->children[i], + conv_string); + if (child == NULL) { + Py_XDECREF(children); + return NULL; + } + PyTuple_SET_ITEM(children, i, child); + } + result = Py_BuildValue("(iiO&N)", + model->type, model->quant, + conv_string,model->name, children); + } + return result; +} + +static void +my_ElementDeclHandler(void *userData, + const XML_Char *name, + XML_Content *model) +{ + xmlparseobject *self = (xmlparseobject *)userData; + PyObject *args = NULL; + + if (have_handler(self, ElementDecl)) { + PyObject *rv = NULL; + PyObject *modelobj, *nameobj; + + if (flush_character_buffer(self) < 0) + goto finally; +#ifdef Py_USING_UNICODE + modelobj = conv_content_model(model, + (self->returns_unicode + ? conv_string_to_unicode + : conv_string_to_utf8)); +#else + modelobj = conv_content_model(model, conv_string_to_utf8); +#endif + if (modelobj == NULL) { + flag_error(self); + goto finally; + } + nameobj = string_intern(self, name); + if (nameobj == NULL) { + Py_DECREF(modelobj); + flag_error(self); + goto finally; + } + args = Py_BuildValue("NN", string_intern(self, name), modelobj); + if (args == NULL) { + Py_DECREF(modelobj); + flag_error(self); + goto finally; + } + self->in_callback = 1; + rv = call_with_frame(getcode(ElementDecl, "ElementDecl", __LINE__), + self->handlers[ElementDecl], args); + self->in_callback = 0; + if (rv == NULL) { + flag_error(self); + goto finally; + } + Py_DECREF(rv); + } + finally: + Py_XDECREF(args); + XML_FreeContentModel(self->itself, model); + return; +} + +VOID_HANDLER(AttlistDecl, + (void *userData, + const XML_Char *elname, + const XML_Char *attname, + const XML_Char *att_type, + const XML_Char *dflt, + int isrequired), + ("(NNO&O&i)", + string_intern(self, elname), string_intern(self, attname), + STRING_CONV_FUNC,att_type, STRING_CONV_FUNC,dflt, + isrequired)) + +#if XML_COMBINED_VERSION >= 19504 +VOID_HANDLER(SkippedEntity, + (void *userData, + const XML_Char *entityName, + int is_parameter_entity), + ("Ni", + string_intern(self, entityName), is_parameter_entity)) +#endif + +VOID_HANDLER(NotationDecl, + (void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId), + ("(NNNN)", + string_intern(self, notationName), string_intern(self, base), + string_intern(self, systemId), string_intern(self, publicId))) + +VOID_HANDLER(StartNamespaceDecl, + (void *userData, + const XML_Char *prefix, + const XML_Char *uri), + ("(NN)", + string_intern(self, prefix), string_intern(self, uri))) + +VOID_HANDLER(EndNamespaceDecl, + (void *userData, + const XML_Char *prefix), + ("(N)", string_intern(self, prefix))) + +VOID_HANDLER(Comment, + (void *userData, const XML_Char *data), + ("(O&)", STRING_CONV_FUNC,data)) + +VOID_HANDLER(StartCdataSection, + (void *userData), + ("()")) + +VOID_HANDLER(EndCdataSection, + (void *userData), + ("()")) + +#ifndef Py_USING_UNICODE +VOID_HANDLER(Default, + (void *userData, const XML_Char *s, int len), + ("(N)", conv_string_len_to_utf8(s,len))) + +VOID_HANDLER(DefaultHandlerExpand, + (void *userData, const XML_Char *s, int len), + ("(N)", conv_string_len_to_utf8(s,len))) +#else +VOID_HANDLER(Default, + (void *userData, const XML_Char *s, int len), + ("(N)", (self->returns_unicode + ? conv_string_len_to_unicode(s,len) + : conv_string_len_to_utf8(s,len)))) + +VOID_HANDLER(DefaultHandlerExpand, + (void *userData, const XML_Char *s, int len), + ("(N)", (self->returns_unicode + ? conv_string_len_to_unicode(s,len) + : conv_string_len_to_utf8(s,len)))) +#endif + +INT_HANDLER(NotStandalone, + (void *userData), + ("()")) + +RC_HANDLER(int, ExternalEntityRef, + (XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId), + int rc=0;, + ("(O&NNN)", + STRING_CONV_FUNC,context, string_intern(self, base), + string_intern(self, systemId), string_intern(self, publicId)), + rc = PyInt_AsLong(rv);, rc, + XML_GetUserData(parser)) + +/* XXX UnknownEncodingHandler */ + +VOID_HANDLER(StartDoctypeDecl, + (void *userData, const XML_Char *doctypeName, + const XML_Char *sysid, const XML_Char *pubid, + int has_internal_subset), + ("(NNNi)", string_intern(self, doctypeName), + string_intern(self, sysid), string_intern(self, pubid), + has_internal_subset)) + +VOID_HANDLER(EndDoctypeDecl, (void *userData), ("()")) + +/* ---------------------------------------------------------------- */ + +static PyObject * +get_parse_result(xmlparseobject *self, int rv) +{ + if (PyErr_Occurred()) { + return NULL; + } + if (rv == 0) { + return set_error(self, XML_GetErrorCode(self->itself)); + } + if (flush_character_buffer(self) < 0) { + return NULL; + } + return PyInt_FromLong(rv); +} + +PyDoc_STRVAR(xmlparse_Parse__doc__, +"Parse(data[, isfinal])\n\ +Parse XML data. `isfinal' should be true at end of input."); + +static PyObject * +xmlparse_Parse(xmlparseobject *self, PyObject *args) +{ + char *s; + int slen; + int isFinal = 0; + + if (!PyArg_ParseTuple(args, "s#|i:Parse", &s, &slen, &isFinal)) + return NULL; + + return get_parse_result(self, XML_Parse(self->itself, s, slen, isFinal)); +} + +/* File reading copied from cPickle */ + +#define BUF_SIZE 2048 + +static int +readinst(char *buf, int buf_size, PyObject *meth) +{ + PyObject *arg = NULL; + PyObject *bytes = NULL; + PyObject *str = NULL; + int len = -1; + + if ((bytes = PyInt_FromLong(buf_size)) == NULL) + goto finally; + + if ((arg = PyTuple_New(1)) == NULL) { + Py_DECREF(bytes); + goto finally; + } + + PyTuple_SET_ITEM(arg, 0, bytes); + + if ((str = PyObject_Call(meth, arg, NULL)) == NULL) + goto finally; + + /* XXX what to do if it returns a Unicode string? */ + if (!PyString_Check(str)) { + PyErr_Format(PyExc_TypeError, + "read() did not return a string object (type=%.400s)", + str->ob_type->tp_name); + goto finally; + } + len = PyString_GET_SIZE(str); + if (len > buf_size) { + PyErr_Format(PyExc_ValueError, + "read() returned too much data: " + "%i bytes requested, %i returned", + buf_size, len); + goto finally; + } + memcpy(buf, PyString_AsString(str), len); +finally: + Py_XDECREF(arg); + Py_XDECREF(str); + return len; +} + +PyDoc_STRVAR(xmlparse_ParseFile__doc__, +"ParseFile(file)\n\ +Parse XML data from file-like object."); + +static PyObject * +xmlparse_ParseFile(xmlparseobject *self, PyObject *args) +{ + int rv = 1; + PyObject *f; + FILE *fp; + PyObject *readmethod = NULL; + + if (!PyArg_ParseTuple(args, "O:ParseFile", &f)) + return NULL; + + if (PyFile_Check(f)) { + fp = PyFile_AsFile(f); + } + else{ + fp = NULL; + readmethod = PyObject_GetAttrString(f, "read"); + if (readmethod == NULL) { + PyErr_Clear(); + PyErr_SetString(PyExc_TypeError, + "argument must have 'read' attribute"); + return NULL; + } + } + for (;;) { + int bytes_read; + void *buf = XML_GetBuffer(self->itself, BUF_SIZE); + if (buf == NULL) { + Py_XDECREF(readmethod); + return PyErr_NoMemory(); + } + + if (fp) { + bytes_read = fread(buf, sizeof(char), BUF_SIZE, fp); + if (bytes_read < 0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + } + else { + bytes_read = readinst(buf, BUF_SIZE, readmethod); + if (bytes_read < 0) { + Py_DECREF(readmethod); + return NULL; + } + } + rv = XML_ParseBuffer(self->itself, bytes_read, bytes_read == 0); + if (PyErr_Occurred()) { + Py_XDECREF(readmethod); + return NULL; + } + + if (!rv || bytes_read == 0) + break; + } + Py_XDECREF(readmethod); + return get_parse_result(self, rv); +} + +PyDoc_STRVAR(xmlparse_SetBase__doc__, +"SetBase(base_url)\n\ +Set the base URL for the parser."); + +static PyObject * +xmlparse_SetBase(xmlparseobject *self, PyObject *args) +{ + char *base; + + if (!PyArg_ParseTuple(args, "s:SetBase", &base)) + return NULL; + if (!XML_SetBase(self->itself, base)) { + return PyErr_NoMemory(); + } + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(xmlparse_GetBase__doc__, +"GetBase() -> url\n\ +Return base URL string for the parser."); + +static PyObject * +xmlparse_GetBase(xmlparseobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":GetBase")) + return NULL; + + return Py_BuildValue("z", XML_GetBase(self->itself)); +} + +PyDoc_STRVAR(xmlparse_GetInputContext__doc__, +"GetInputContext() -> string\n\ +Return the untranslated text of the input that caused the current event.\n\ +If the event was generated by a large amount of text (such as a start tag\n\ +for an element with many attributes), not all of the text may be available."); + +static PyObject * +xmlparse_GetInputContext(xmlparseobject *self, PyObject *args) +{ + PyObject *result = NULL; + + if (PyArg_ParseTuple(args, ":GetInputContext")) { + if (self->in_callback) { + int offset, size; + const char *buffer + = XML_GetInputContext(self->itself, &offset, &size); + + if (buffer != NULL) + result = PyString_FromStringAndSize(buffer + offset, size); + else { + result = Py_None; + Py_INCREF(result); + } + } + else { + result = Py_None; + Py_INCREF(result); + } + } + return result; +} + +PyDoc_STRVAR(xmlparse_ExternalEntityParserCreate__doc__, +"ExternalEntityParserCreate(context[, encoding])\n\ +Create a parser for parsing an external entity based on the\n\ +information passed to the ExternalEntityRefHandler."); + +static PyObject * +xmlparse_ExternalEntityParserCreate(xmlparseobject *self, PyObject *args) +{ + char *context; + char *encoding = NULL; + xmlparseobject *new_parser; + int i; + + if (!PyArg_ParseTuple(args, "z|s:ExternalEntityParserCreate", + &context, &encoding)) { + return NULL; + } + +#ifndef Py_TPFLAGS_HAVE_GC + /* Python versions 2.0 and 2.1 */ + new_parser = PyObject_New(xmlparseobject, &Xmlparsetype); +#else + /* Python versions 2.2 and later */ + new_parser = PyObject_GC_New(xmlparseobject, &Xmlparsetype); +#endif + + if (new_parser == NULL) + return NULL; + new_parser->buffer_size = self->buffer_size; + new_parser->buffer_used = 0; + if (self->buffer != NULL) { + new_parser->buffer = malloc(new_parser->buffer_size); + if (new_parser->buffer == NULL) { +#ifndef Py_TPFLAGS_HAVE_GC + /* Code for versions 2.0 and 2.1 */ + PyObject_Del(new_parser); +#else + /* Code for versions 2.2 and later. */ + PyObject_GC_Del(new_parser); +#endif + return PyErr_NoMemory(); + } + } + else + new_parser->buffer = NULL; + new_parser->returns_unicode = self->returns_unicode; + new_parser->ordered_attributes = self->ordered_attributes; + new_parser->specified_attributes = self->specified_attributes; + new_parser->in_callback = 0; + new_parser->ns_prefixes = self->ns_prefixes; + new_parser->itself = XML_ExternalEntityParserCreate(self->itself, context, + encoding); + new_parser->handlers = 0; + new_parser->intern = self->intern; + Py_XINCREF(new_parser->intern); +#ifdef Py_TPFLAGS_HAVE_GC + PyObject_GC_Track(new_parser); +#else + PyObject_GC_Init(new_parser); +#endif + + if (!new_parser->itself) { + Py_DECREF(new_parser); + return PyErr_NoMemory(); + } + + XML_SetUserData(new_parser->itself, (void *)new_parser); + + /* allocate and clear handlers first */ + for (i = 0; handler_info[i].name != NULL; i++) + /* do nothing */; + + new_parser->handlers = malloc(sizeof(PyObject *) * i); + if (!new_parser->handlers) { + Py_DECREF(new_parser); + return PyErr_NoMemory(); + } + clear_handlers(new_parser, 1); + + /* then copy handlers from self */ + for (i = 0; handler_info[i].name != NULL; i++) { + PyObject *handler = self->handlers[i]; + if (handler != NULL) { + Py_INCREF(handler); + new_parser->handlers[i] = handler; + handler_info[i].setter(new_parser->itself, + handler_info[i].handler); + } + } + return (PyObject *)new_parser; +} + +PyDoc_STRVAR(xmlparse_SetParamEntityParsing__doc__, +"SetParamEntityParsing(flag) -> success\n\ +Controls parsing of parameter entities (including the external DTD\n\ +subset). Possible flag values are XML_PARAM_ENTITY_PARSING_NEVER,\n\ +XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE and\n\ +XML_PARAM_ENTITY_PARSING_ALWAYS. Returns true if setting the flag\n\ +was successful."); + +static PyObject* +xmlparse_SetParamEntityParsing(xmlparseobject *p, PyObject* args) +{ + int flag; + if (!PyArg_ParseTuple(args, "i", &flag)) + return NULL; + flag = XML_SetParamEntityParsing(p->itself, flag); + return PyInt_FromLong(flag); +} + + +#if XML_COMBINED_VERSION >= 19505 +PyDoc_STRVAR(xmlparse_UseForeignDTD__doc__, +"UseForeignDTD([flag])\n\ +Allows the application to provide an artificial external subset if one is\n\ +not specified as part of the document instance. This readily allows the\n\ +use of a 'default' document type controlled by the application, while still\n\ +getting the advantage of providing document type information to the parser.\n\ +'flag' defaults to True if not provided."); + +static PyObject * +xmlparse_UseForeignDTD(xmlparseobject *self, PyObject *args) +{ + PyObject *flagobj = NULL; + XML_Bool flag = XML_TRUE; + enum XML_Error rc; + if (!PyArg_ParseTuple(args, "|O:UseForeignDTD", &flagobj)) + return NULL; + if (flagobj != NULL) + flag = PyObject_IsTrue(flagobj) ? XML_TRUE : XML_FALSE; + rc = XML_UseForeignDTD(self->itself, flag); + if (rc != XML_ERROR_NONE) { + return set_error(self, rc); + } + Py_INCREF(Py_None); + return Py_None; +} +#endif + +static struct PyMethodDef xmlparse_methods[] = { + {"Parse", (PyCFunction)xmlparse_Parse, + METH_VARARGS, xmlparse_Parse__doc__}, + {"ParseFile", (PyCFunction)xmlparse_ParseFile, + METH_VARARGS, xmlparse_ParseFile__doc__}, + {"SetBase", (PyCFunction)xmlparse_SetBase, + METH_VARARGS, xmlparse_SetBase__doc__}, + {"GetBase", (PyCFunction)xmlparse_GetBase, + METH_VARARGS, xmlparse_GetBase__doc__}, + {"ExternalEntityParserCreate", (PyCFunction)xmlparse_ExternalEntityParserCreate, + METH_VARARGS, xmlparse_ExternalEntityParserCreate__doc__}, + {"SetParamEntityParsing", (PyCFunction)xmlparse_SetParamEntityParsing, + METH_VARARGS, xmlparse_SetParamEntityParsing__doc__}, + {"GetInputContext", (PyCFunction)xmlparse_GetInputContext, + METH_VARARGS, xmlparse_GetInputContext__doc__}, +#if XML_COMBINED_VERSION >= 19505 + {"UseForeignDTD", (PyCFunction)xmlparse_UseForeignDTD, + METH_VARARGS, xmlparse_UseForeignDTD__doc__}, +#endif + {NULL, NULL} /* sentinel */ +}; + +/* ---------- */ + + +#ifdef Py_USING_UNICODE + +/* pyexpat international encoding support. + Make it as simple as possible. +*/ + +static char template_buffer[257]; +PyObject *template_string = NULL; + +static void +init_template_buffer(void) +{ + int i; + for (i = 0; i < 256; i++) { + template_buffer[i] = i; + } + template_buffer[256] = 0; +} + +static int +PyUnknownEncodingHandler(void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info) +{ + PyUnicodeObject *_u_string = NULL; + int result = 0; + int i; + + /* Yes, supports only 8bit encodings */ + _u_string = (PyUnicodeObject *) + PyUnicode_Decode(template_buffer, 256, name, "replace"); + + if (_u_string == NULL) + return result; + + for (i = 0; i < 256; i++) { + /* Stupid to access directly, but fast */ + Py_UNICODE c = _u_string->str[i]; + if (c == Py_UNICODE_REPLACEMENT_CHARACTER) + info->map[i] = -1; + else + info->map[i] = c; + } + info->data = NULL; + info->convert = NULL; + info->release = NULL; + result = 1; + Py_DECREF(_u_string); + return result; +} + +#endif + +static PyObject * +newxmlparseobject(char *encoding, char *namespace_separator, PyObject *intern) +{ + int i; + xmlparseobject *self; + +#ifdef Py_TPFLAGS_HAVE_GC + /* Code for versions 2.2 and later */ + self = PyObject_GC_New(xmlparseobject, &Xmlparsetype); +#else + self = PyObject_New(xmlparseobject, &Xmlparsetype); +#endif + if (self == NULL) + return NULL; + +#ifdef Py_USING_UNICODE + self->returns_unicode = 1; +#else + self->returns_unicode = 0; +#endif + + self->buffer = NULL; + self->buffer_size = CHARACTER_DATA_BUFFER_SIZE; + self->buffer_used = 0; + self->ordered_attributes = 0; + self->specified_attributes = 0; + self->in_callback = 0; + self->ns_prefixes = 0; + self->handlers = NULL; + if (namespace_separator != NULL) { + self->itself = XML_ParserCreateNS(encoding, *namespace_separator); + } + else { + self->itself = XML_ParserCreate(encoding); + } + self->intern = intern; + Py_XINCREF(self->intern); +#ifdef Py_TPFLAGS_HAVE_GC + PyObject_GC_Track(self); +#else + PyObject_GC_Init(self); +#endif + if (self->itself == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "XML_ParserCreate failed"); + Py_DECREF(self); + return NULL; + } + XML_SetUserData(self->itself, (void *)self); +#ifdef Py_USING_UNICODE + XML_SetUnknownEncodingHandler(self->itself, + (XML_UnknownEncodingHandler) PyUnknownEncodingHandler, NULL); +#endif + + for (i = 0; handler_info[i].name != NULL; i++) + /* do nothing */; + + self->handlers = malloc(sizeof(PyObject *) * i); + if (!self->handlers) { + Py_DECREF(self); + return PyErr_NoMemory(); + } + clear_handlers(self, 1); + + return (PyObject*)self; +} + + +static void +xmlparse_dealloc(xmlparseobject *self) +{ + int i; +#ifdef Py_TPFLAGS_HAVE_GC + PyObject_GC_UnTrack(self); +#else + PyObject_GC_Fini(self); +#endif + if (self->itself != NULL) + XML_ParserFree(self->itself); + self->itself = NULL; + + if (self->handlers != NULL) { + PyObject *temp; + for (i = 0; handler_info[i].name != NULL; i++) { + temp = self->handlers[i]; + self->handlers[i] = NULL; + Py_XDECREF(temp); + } + free(self->handlers); + self->handlers = NULL; + } + if (self->buffer != NULL) { + free(self->buffer); + self->buffer = NULL; + } + Py_XDECREF(self->intern); +#ifndef Py_TPFLAGS_HAVE_GC + /* Code for versions 2.0 and 2.1 */ + PyObject_Del(self); +#else + /* Code for versions 2.2 and later. */ + PyObject_GC_Del(self); +#endif +} + +static int +handlername2int(const char *name) +{ + int i; + for (i = 0; handler_info[i].name != NULL; i++) { + if (strcmp(name, handler_info[i].name) == 0) { + return i; + } + } + return -1; +} + +static PyObject * +get_pybool(int istrue) +{ + PyObject *result = istrue ? Py_True : Py_False; + Py_INCREF(result); + return result; +} + +static PyObject * +xmlparse_getattr(xmlparseobject *self, char *name) +{ + int handlernum = handlername2int(name); + + if (handlernum != -1) { + PyObject *result = self->handlers[handlernum]; + if (result == NULL) + result = Py_None; + Py_INCREF(result); + return result; + } + if (name[0] == 'E') { + if (strcmp(name, "ErrorCode") == 0) + return PyInt_FromLong((long) + XML_GetErrorCode(self->itself)); + if (strcmp(name, "ErrorLineNumber") == 0) + return PyInt_FromLong((long) + XML_GetErrorLineNumber(self->itself)); + if (strcmp(name, "ErrorColumnNumber") == 0) + return PyInt_FromLong((long) + XML_GetErrorColumnNumber(self->itself)); + if (strcmp(name, "ErrorByteIndex") == 0) + return PyInt_FromLong((long) + XML_GetErrorByteIndex(self->itself)); + } + if (name[0] == 'b') { + if (strcmp(name, "buffer_size") == 0) + return PyInt_FromLong((long) self->buffer_size); + if (strcmp(name, "buffer_text") == 0) + return get_pybool(self->buffer != NULL); + if (strcmp(name, "buffer_used") == 0) + return PyInt_FromLong((long) self->buffer_used); + } + if (strcmp(name, "namespace_prefixes") == 0) + return get_pybool(self->ns_prefixes); + if (strcmp(name, "ordered_attributes") == 0) + return get_pybool(self->ordered_attributes); + if (strcmp(name, "returns_unicode") == 0) + return get_pybool((long) self->returns_unicode); + if (strcmp(name, "specified_attributes") == 0) + return get_pybool((long) self->specified_attributes); + if (strcmp(name, "intern") == 0) { + if (self->intern == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + else { + Py_INCREF(self->intern); + return self->intern; + } + } + +#define APPEND(list, str) \ + do { \ + PyObject *o = PyString_FromString(str); \ + if (o != NULL) \ + PyList_Append(list, o); \ + Py_XDECREF(o); \ + } while (0) + + if (strcmp(name, "__members__") == 0) { + int i; + PyObject *rc = PyList_New(0); + for (i = 0; handler_info[i].name != NULL; i++) { + PyObject *o = get_handler_name(&handler_info[i]); + if (o != NULL) + PyList_Append(rc, o); + Py_XDECREF(o); + } + APPEND(rc, "ErrorCode"); + APPEND(rc, "ErrorLineNumber"); + APPEND(rc, "ErrorColumnNumber"); + APPEND(rc, "ErrorByteIndex"); + APPEND(rc, "buffer_size"); + APPEND(rc, "buffer_text"); + APPEND(rc, "buffer_used"); + APPEND(rc, "namespace_prefixes"); + APPEND(rc, "ordered_attributes"); + APPEND(rc, "returns_unicode"); + APPEND(rc, "specified_attributes"); + APPEND(rc, "intern"); + +#undef APPEND + return rc; + } + return Py_FindMethod(xmlparse_methods, (PyObject *)self, name); +} + +static int +sethandler(xmlparseobject *self, const char *name, PyObject* v) +{ + int handlernum = handlername2int(name); + if (handlernum >= 0) { + xmlhandler c_handler = NULL; + PyObject *temp = self->handlers[handlernum]; + + if (v == Py_None) + v = NULL; + else if (v != NULL) { + Py_INCREF(v); + c_handler = handler_info[handlernum].handler; + } + self->handlers[handlernum] = v; + Py_XDECREF(temp); + handler_info[handlernum].setter(self->itself, c_handler); + return 1; + } + return 0; +} + +static int +xmlparse_setattr(xmlparseobject *self, char *name, PyObject *v) +{ + /* Set attribute 'name' to value 'v'. v==NULL means delete */ + if (v == NULL) { + PyErr_SetString(PyExc_RuntimeError, "Cannot delete attribute"); + return -1; + } + if (strcmp(name, "buffer_text") == 0) { + if (PyObject_IsTrue(v)) { + if (self->buffer == NULL) { + self->buffer = malloc(self->buffer_size); + if (self->buffer == NULL) { + PyErr_NoMemory(); + return -1; + } + self->buffer_used = 0; + } + } + else if (self->buffer != NULL) { + if (flush_character_buffer(self) < 0) + return -1; + free(self->buffer); + self->buffer = NULL; + } + return 0; + } + if (strcmp(name, "namespace_prefixes") == 0) { + if (PyObject_IsTrue(v)) + self->ns_prefixes = 1; + else + self->ns_prefixes = 0; + XML_SetReturnNSTriplet(self->itself, self->ns_prefixes); + return 0; + } + if (strcmp(name, "ordered_attributes") == 0) { + if (PyObject_IsTrue(v)) + self->ordered_attributes = 1; + else + self->ordered_attributes = 0; + return 0; + } + if (strcmp(name, "returns_unicode") == 0) { + if (PyObject_IsTrue(v)) { +#ifndef Py_USING_UNICODE + PyErr_SetString(PyExc_ValueError, + "Unicode support not available"); + return -1; +#else + self->returns_unicode = 1; +#endif + } + else + self->returns_unicode = 0; + return 0; + } + if (strcmp(name, "specified_attributes") == 0) { + if (PyObject_IsTrue(v)) + self->specified_attributes = 1; + else + self->specified_attributes = 0; + return 0; + } + if (strcmp(name, "CharacterDataHandler") == 0) { + /* If we're changing the character data handler, flush all + * cached data with the old handler. Not sure there's a + * "right" thing to do, though, but this probably won't + * happen. + */ + if (flush_character_buffer(self) < 0) + return -1; + } + if (sethandler(self, name, v)) { + return 0; + } + PyErr_SetString(PyExc_AttributeError, name); + return -1; +} + +#ifdef WITH_CYCLE_GC +static int +xmlparse_traverse(xmlparseobject *op, visitproc visit, void *arg) +{ + int i, err; + for (i = 0; handler_info[i].name != NULL; i++) { + if (!op->handlers[i]) + continue; + err = visit(op->handlers[i], arg); + if (err) + return err; + } + return 0; +} + +static int +xmlparse_clear(xmlparseobject *op) +{ + clear_handlers(op, 0); + Py_XDECREF(op->intern); + op->intern = 0; + return 0; +} +#endif + +PyDoc_STRVAR(Xmlparsetype__doc__, "XML parser"); + +static PyTypeObject Xmlparsetype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "pyexpat.xmlparser", /*tp_name*/ + sizeof(xmlparseobject) + PyGC_HEAD_SIZE,/*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)xmlparse_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)xmlparse_getattr, /*tp_getattr*/ + (setattrfunc)xmlparse_setattr, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ +#ifdef Py_TPFLAGS_HAVE_GC + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ +#else + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/ +#endif + Xmlparsetype__doc__, /* tp_doc - Documentation string */ +#ifdef WITH_CYCLE_GC + (traverseproc)xmlparse_traverse, /* tp_traverse */ + (inquiry)xmlparse_clear /* tp_clear */ +#else + 0, 0 +#endif +}; + +/* End of code for xmlparser objects */ +/* -------------------------------------------------------- */ + +PyDoc_STRVAR(pyexpat_ParserCreate__doc__, +"ParserCreate([encoding[, namespace_separator]]) -> parser\n\ +Return a new XML parser object."); + +static PyObject * +pyexpat_ParserCreate(PyObject *notused, PyObject *args, PyObject *kw) +{ + char *encoding = NULL; + char *namespace_separator = NULL; + PyObject *intern = NULL; + PyObject *result; + int intern_decref = 0; + static char *kwlist[] = {"encoding", "namespace_separator", + "intern", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "|zzO:ParserCreate", kwlist, + &encoding, &namespace_separator, &intern)) + return NULL; + if (namespace_separator != NULL + && strlen(namespace_separator) > 1) { + PyErr_SetString(PyExc_ValueError, + "namespace_separator must be at most one" + " character, omitted, or None"); + return NULL; + } + /* Explicitly passing None means no interning is desired. + Not passing anything means that a new dictionary is used. */ + if (intern == Py_None) + intern = NULL; + else if (intern == NULL) { + intern = PyDict_New(); + if (!intern) + return NULL; + intern_decref = 1; + } + else if (!PyDict_Check(intern)) { + PyErr_SetString(PyExc_TypeError, "intern must be a dictionary"); + return NULL; + } + + result = newxmlparseobject(encoding, namespace_separator, intern); + if (intern_decref) { + Py_DECREF(intern); + } + return result; +} + +PyDoc_STRVAR(pyexpat_ErrorString__doc__, +"ErrorString(errno) -> string\n\ +Returns string error for given number."); + +static PyObject * +pyexpat_ErrorString(PyObject *self, PyObject *args) +{ + long code = 0; + + if (!PyArg_ParseTuple(args, "l:ErrorString", &code)) + return NULL; + return Py_BuildValue("z", XML_ErrorString((int)code)); +} + +/* List of methods defined in the module */ + +static struct PyMethodDef pyexpat_methods[] = { + {"ParserCreate", (PyCFunction)pyexpat_ParserCreate, + METH_VARARGS|METH_KEYWORDS, pyexpat_ParserCreate__doc__}, + {"ErrorString", (PyCFunction)pyexpat_ErrorString, + METH_VARARGS, pyexpat_ErrorString__doc__}, + + {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ +}; + +/* Module docstring */ + +PyDoc_STRVAR(pyexpat_module_documentation, +"Python wrapper for Expat parser."); + +/* Return a Python string that represents the version number without the + * extra cruft added by revision control, even if the right options were + * given to the "cvs export" command to make it not include the extra + * cruft. + */ +static PyObject * +get_version_string(void) +{ + static char *rcsid = "$Revision: 2.83 $"; + char *rev = rcsid; + int i = 0; + + while (!isdigit((int)*rev)) + ++rev; + while (rev[i] != ' ' && rev[i] != '\0') + ++i; + + return PyString_FromStringAndSize(rev, i); +} + +/* Initialization function for the module */ + +#ifndef MODULE_NAME +#define MODULE_NAME "pyexpat" +#endif + +#ifndef MODULE_INITFUNC +#define MODULE_INITFUNC initpyexpat +#endif + +#ifndef PyMODINIT_FUNC +# ifdef MS_WINDOWS +# define PyMODINIT_FUNC __declspec(dllexport) void +# else +# define PyMODINIT_FUNC void +# endif +#endif + +PyMODINIT_FUNC MODULE_INITFUNC(void); /* avoid compiler warnings */ + +PyMODINIT_FUNC +MODULE_INITFUNC(void) +{ + PyObject *m, *d; + PyObject *errmod_name = PyString_FromString(MODULE_NAME ".errors"); + PyObject *errors_module; + PyObject *modelmod_name; + PyObject *model_module; + PyObject *sys_modules; + + if (errmod_name == NULL) + return; + modelmod_name = PyString_FromString(MODULE_NAME ".model"); + if (modelmod_name == NULL) + return; + + Xmlparsetype.ob_type = &PyType_Type; + + /* Create the module and add the functions */ + m = Py_InitModule3(MODULE_NAME, pyexpat_methods, + pyexpat_module_documentation); + + /* Add some symbolic constants to the module */ + if (ErrorObject == NULL) { + ErrorObject = PyErr_NewException("xml.parsers.expat.ExpatError", + NULL, NULL); + if (ErrorObject == NULL) + return; + } + Py_INCREF(ErrorObject); + PyModule_AddObject(m, "error", ErrorObject); + Py_INCREF(ErrorObject); + PyModule_AddObject(m, "ExpatError", ErrorObject); + Py_INCREF(&Xmlparsetype); + PyModule_AddObject(m, "XMLParserType", (PyObject *) &Xmlparsetype); + + PyModule_AddObject(m, "__version__", get_version_string()); + PyModule_AddStringConstant(m, "EXPAT_VERSION", + (char *) XML_ExpatVersion()); + { + XML_Expat_Version info = XML_ExpatVersionInfo(); + PyModule_AddObject(m, "version_info", + Py_BuildValue("(iii)", info.major, + info.minor, info.micro)); + } +#ifdef Py_USING_UNICODE + init_template_buffer(); +#endif + /* XXX When Expat supports some way of figuring out how it was + compiled, this should check and set native_encoding + appropriately. + */ + PyModule_AddStringConstant(m, "native_encoding", "UTF-8"); + + sys_modules = PySys_GetObject("modules"); + d = PyModule_GetDict(m); + errors_module = PyDict_GetItem(d, errmod_name); + if (errors_module == NULL) { + errors_module = PyModule_New(MODULE_NAME ".errors"); + if (errors_module != NULL) { + PyDict_SetItem(sys_modules, errmod_name, errors_module); + /* gives away the reference to errors_module */ + PyModule_AddObject(m, "errors", errors_module); + } + } + Py_DECREF(errmod_name); + model_module = PyDict_GetItem(d, modelmod_name); + if (model_module == NULL) { + model_module = PyModule_New(MODULE_NAME ".model"); + if (model_module != NULL) { + PyDict_SetItem(sys_modules, modelmod_name, model_module); + /* gives away the reference to model_module */ + PyModule_AddObject(m, "model", model_module); + } + } + Py_DECREF(modelmod_name); + if (errors_module == NULL || model_module == NULL) + /* Don't core dump later! */ + return; + +#if XML_COMBINED_VERSION > 19505 + { + const XML_Feature *features = XML_GetFeatureList(); + PyObject *list = PyList_New(0); + if (list == NULL) + /* just ignore it */ + PyErr_Clear(); + else { + int i = 0; + for (; features[i].feature != XML_FEATURE_END; ++i) { + int ok; + PyObject *item = Py_BuildValue("si", features[i].name, + features[i].value); + if (item == NULL) { + Py_DECREF(list); + list = NULL; + break; + } + ok = PyList_Append(list, item); + Py_DECREF(item); + if (ok < 0) { + PyErr_Clear(); + break; + } + } + if (list != NULL) + PyModule_AddObject(m, "features", list); + } + } +#endif + +#define MYCONST(name) \ + PyModule_AddStringConstant(errors_module, #name, \ + (char*)XML_ErrorString(name)) + + MYCONST(XML_ERROR_NO_MEMORY); + MYCONST(XML_ERROR_SYNTAX); + MYCONST(XML_ERROR_NO_ELEMENTS); + MYCONST(XML_ERROR_INVALID_TOKEN); + MYCONST(XML_ERROR_UNCLOSED_TOKEN); + MYCONST(XML_ERROR_PARTIAL_CHAR); + MYCONST(XML_ERROR_TAG_MISMATCH); + MYCONST(XML_ERROR_DUPLICATE_ATTRIBUTE); + MYCONST(XML_ERROR_JUNK_AFTER_DOC_ELEMENT); + MYCONST(XML_ERROR_PARAM_ENTITY_REF); + MYCONST(XML_ERROR_UNDEFINED_ENTITY); + MYCONST(XML_ERROR_RECURSIVE_ENTITY_REF); + MYCONST(XML_ERROR_ASYNC_ENTITY); + MYCONST(XML_ERROR_BAD_CHAR_REF); + MYCONST(XML_ERROR_BINARY_ENTITY_REF); + MYCONST(XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF); + MYCONST(XML_ERROR_MISPLACED_XML_PI); + MYCONST(XML_ERROR_UNKNOWN_ENCODING); + MYCONST(XML_ERROR_INCORRECT_ENCODING); + MYCONST(XML_ERROR_UNCLOSED_CDATA_SECTION); + MYCONST(XML_ERROR_EXTERNAL_ENTITY_HANDLING); + MYCONST(XML_ERROR_NOT_STANDALONE); + + PyModule_AddStringConstant(errors_module, "__doc__", + "Constants used to describe error conditions."); + +#undef MYCONST + +#define MYCONST(c) PyModule_AddIntConstant(m, #c, c) + MYCONST(XML_PARAM_ENTITY_PARSING_NEVER); + MYCONST(XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE); + MYCONST(XML_PARAM_ENTITY_PARSING_ALWAYS); +#undef MYCONST + +#define MYCONST(c) PyModule_AddIntConstant(model_module, #c, c) + PyModule_AddStringConstant(model_module, "__doc__", + "Constants used to interpret content model information."); + + MYCONST(XML_CTYPE_EMPTY); + MYCONST(XML_CTYPE_ANY); + MYCONST(XML_CTYPE_MIXED); + MYCONST(XML_CTYPE_NAME); + MYCONST(XML_CTYPE_CHOICE); + MYCONST(XML_CTYPE_SEQ); + + MYCONST(XML_CQUANT_NONE); + MYCONST(XML_CQUANT_OPT); + MYCONST(XML_CQUANT_REP); + MYCONST(XML_CQUANT_PLUS); +#undef MYCONST +} + +static void +clear_handlers(xmlparseobject *self, int initial) +{ + int i = 0; + PyObject *temp; + + for (; handler_info[i].name != NULL; i++) { + if (initial) + self->handlers[i] = NULL; + else { + temp = self->handlers[i]; + self->handlers[i] = NULL; + Py_XDECREF(temp); + handler_info[i].setter(self->itself, NULL); + } + } +} + +static struct HandlerInfo handler_info[] = { + {"StartElementHandler", + (xmlhandlersetter)XML_SetStartElementHandler, + (xmlhandler)my_StartElementHandler}, + {"EndElementHandler", + (xmlhandlersetter)XML_SetEndElementHandler, + (xmlhandler)my_EndElementHandler}, + {"ProcessingInstructionHandler", + (xmlhandlersetter)XML_SetProcessingInstructionHandler, + (xmlhandler)my_ProcessingInstructionHandler}, + {"CharacterDataHandler", + (xmlhandlersetter)XML_SetCharacterDataHandler, + (xmlhandler)my_CharacterDataHandler}, + {"UnparsedEntityDeclHandler", + (xmlhandlersetter)XML_SetUnparsedEntityDeclHandler, + (xmlhandler)my_UnparsedEntityDeclHandler}, + {"NotationDeclHandler", + (xmlhandlersetter)XML_SetNotationDeclHandler, + (xmlhandler)my_NotationDeclHandler}, + {"StartNamespaceDeclHandler", + (xmlhandlersetter)XML_SetStartNamespaceDeclHandler, + (xmlhandler)my_StartNamespaceDeclHandler}, + {"EndNamespaceDeclHandler", + (xmlhandlersetter)XML_SetEndNamespaceDeclHandler, + (xmlhandler)my_EndNamespaceDeclHandler}, + {"CommentHandler", + (xmlhandlersetter)XML_SetCommentHandler, + (xmlhandler)my_CommentHandler}, + {"StartCdataSectionHandler", + (xmlhandlersetter)XML_SetStartCdataSectionHandler, + (xmlhandler)my_StartCdataSectionHandler}, + {"EndCdataSectionHandler", + (xmlhandlersetter)XML_SetEndCdataSectionHandler, + (xmlhandler)my_EndCdataSectionHandler}, + {"DefaultHandler", + (xmlhandlersetter)XML_SetDefaultHandler, + (xmlhandler)my_DefaultHandler}, + {"DefaultHandlerExpand", + (xmlhandlersetter)XML_SetDefaultHandlerExpand, + (xmlhandler)my_DefaultHandlerExpandHandler}, + {"NotStandaloneHandler", + (xmlhandlersetter)XML_SetNotStandaloneHandler, + (xmlhandler)my_NotStandaloneHandler}, + {"ExternalEntityRefHandler", + (xmlhandlersetter)XML_SetExternalEntityRefHandler, + (xmlhandler)my_ExternalEntityRefHandler}, + {"StartDoctypeDeclHandler", + (xmlhandlersetter)XML_SetStartDoctypeDeclHandler, + (xmlhandler)my_StartDoctypeDeclHandler}, + {"EndDoctypeDeclHandler", + (xmlhandlersetter)XML_SetEndDoctypeDeclHandler, + (xmlhandler)my_EndDoctypeDeclHandler}, + {"EntityDeclHandler", + (xmlhandlersetter)XML_SetEntityDeclHandler, + (xmlhandler)my_EntityDeclHandler}, + {"XmlDeclHandler", + (xmlhandlersetter)XML_SetXmlDeclHandler, + (xmlhandler)my_XmlDeclHandler}, + {"ElementDeclHandler", + (xmlhandlersetter)XML_SetElementDeclHandler, + (xmlhandler)my_ElementDeclHandler}, + {"AttlistDeclHandler", + (xmlhandlersetter)XML_SetAttlistDeclHandler, + (xmlhandler)my_AttlistDeclHandler}, +#if XML_COMBINED_VERSION >= 19504 + {"SkippedEntityHandler", + (xmlhandlersetter)XML_SetSkippedEntityHandler, + (xmlhandler)my_SkippedEntityHandler}, +#endif + + {NULL, NULL, NULL} /* sentinel */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pypcre.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pypcre.c new file mode 100644 index 00000000..5c4b5e16 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/pypcre.c @@ -0,0 +1,4756 @@ + +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* DO NOT EDIT THIS FILE! */ + +/* This file is automatically written by the merge-files.py script +included with the PCRE distribution for Python; it's produced from +several C files, and code is removed in the process. If you want to +modify the code or track down bugs, it will be much easier to work +with the code in its original, multiple-file form. Don't edit this +file by hand, or submit patches to it. + +The Python-specific PCRE distribution can be retrieved from + http://starship.skyport.net/crew/amk/regex/ + +The unmodified original PCRE distribution is available at +ftp://ftp.cus.cam.ac.uk/pub/software/programs/pcre/, and is originally +written by: Philip Hazel + +Extensively modified by the Python String-SIG: +Send bug reports to: +(They'll figure out if it's a bug in PCRE or in the Python-specific +changes.) + + Copyright (c) 1997 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. +----------------------------------------------------------------------------- +*/ + + +#define FOR_PYTHON +#include "Python.h" +#include "pcre-int.h" +#include +#include "graminit.h" + +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This file is automatically written by the makechartables auxiliary +program. If you edit it by hand, you might like to edit the Makefile to +prevent its ever being regenerated. */ + +/* This table is a lower casing table. */ + +unsigned char pcre_lcc[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255 }; + +/* This table is a case flipping table. */ + +unsigned char pcre_fcc[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255 }; + +/* This table contains bit maps for digits, letters, 'word' chars, and +white space. Each map is 32 bytes long and the bits run from the least +significant end of each byte. */ + +unsigned char pcre_cbits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfe,0xff,0xff,0x07,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + +/* This table identifies various classes of character by individual bits: + 0x01 white space character + 0x02 letter + 0x04 decimal digit + 0x08 hexadecimal digit + 0x10 alphanumeric or '_' + 0x80 regular expression metacharacter or binary zero +*/ + +unsigned char pcre_ctypes[] = { + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ + 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ + 0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c, /* 0 - 7 */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ + 0x12,0x12,0x12,0x80,0x00,0x00,0x80,0x10, /* X - _ */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ + +/* End of chartables.c */ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1998 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. +----------------------------------------------------------------------------- +*/ + + +/* Include the internals header, which itself includes Standard C headers plus +the external pcre header. */ + + + + +/************************************************* +* Create bitmap of starting chars * +*************************************************/ + +/* This function scans a compiled unanchored expression and attempts to build a +bitmap of the set of initial characters. If it can't, it returns FALSE. As time +goes by, we may be able to get more clever at doing this. + +Arguments: + code points to an expression + start_bits points to a 32-byte table, initialized to 0 + +Returns: TRUE if table built, FALSE otherwise +*/ + +static BOOL +set_start_bits(const uschar *code, uschar *start_bits) +{ +register int c; +volatile int dummy; + +do + { + const uschar *tcode = code + 3; + BOOL try_next = TRUE; + + while (try_next) + { + try_next = FALSE; + + if ((int)*tcode >= OP_BRA || *tcode == OP_ASSERT) + { + if (!set_start_bits(tcode, start_bits)) return FALSE; + } + + else switch(*tcode) + { + default: + return FALSE; + + /* BRAZERO does the bracket, but carries on. */ + + case OP_BRAZERO: + case OP_BRAMINZERO: + if (!set_start_bits(++tcode, start_bits)) return FALSE; + dummy = 1; + do tcode += (tcode[1] << 8) + tcode[2]; while (*tcode == OP_ALT); + tcode += 3; + try_next = TRUE; + break; + + /* Single-char * or ? sets the bit and tries the next item */ + + case OP_STAR: + case OP_MINSTAR: + case OP_QUERY: + case OP_MINQUERY: + start_bits[tcode[1]/8] |= (1 << (tcode[1]&7)); + tcode += 2; + try_next = TRUE; + break; + + /* Single-char upto sets the bit and tries the next */ + + case OP_UPTO: + case OP_MINUPTO: + start_bits[tcode[3]/8] |= (1 << (tcode[3]&7)); + tcode += 4; + try_next = TRUE; + break; + + /* At least one single char sets the bit and stops */ + + case OP_EXACT: /* Fall through */ + tcode++; + + case OP_CHARS: /* Fall through */ + tcode++; + + case OP_PLUS: + case OP_MINPLUS: + start_bits[tcode[1]/8] |= (1 << (tcode[1]&7)); + break; + + /* Single character type sets the bits and stops */ + + case OP_NOT_DIGIT: + for (c = 0; c < 32; c++) start_bits[c] |= ~pcre_cbits[c+cbit_digit]; + break; + + case OP_DIGIT: + for (c = 0; c < 32; c++) start_bits[c] |= pcre_cbits[c+cbit_digit]; + break; + + case OP_NOT_WHITESPACE: + for (c = 0; c < 32; c++) start_bits[c] |= ~pcre_cbits[c+cbit_space]; + break; + + case OP_WHITESPACE: + for (c = 0; c < 32; c++) start_bits[c] |= pcre_cbits[c+cbit_space]; + break; + + case OP_NOT_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= ~(pcre_cbits[c] | pcre_cbits[c+cbit_word]); + break; + + case OP_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= (pcre_cbits[c] | pcre_cbits[c+cbit_word]); + break; + + /* One or more character type fudges the pointer and restarts, knowing + it will hit a single character type and stop there. */ + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + tcode++; + try_next = TRUE; + break; + + case OP_TYPEEXACT: + tcode += 3; + try_next = TRUE; + break; + + /* Zero or more repeats of character types set the bits and then + try again. */ + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + tcode += 2; /* Fall through */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + switch(tcode[1]) + { + case OP_NOT_DIGIT: + for (c = 0; c < 32; c++) start_bits[c] |= ~pcre_cbits[c+cbit_digit]; + break; + + case OP_DIGIT: + for (c = 0; c < 32; c++) start_bits[c] |= pcre_cbits[c+cbit_digit]; + break; + + case OP_NOT_WHITESPACE: + for (c = 0; c < 32; c++) start_bits[c] |= ~pcre_cbits[c+cbit_space]; + break; + + case OP_WHITESPACE: + for (c = 0; c < 32; c++) start_bits[c] |= pcre_cbits[c+cbit_space]; + break; + + case OP_NOT_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= ~(pcre_cbits[c] | pcre_cbits[c+cbit_word]); + break; + + case OP_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= (pcre_cbits[c] | pcre_cbits[c+cbit_word]); + break; + } + + tcode += 2; + try_next = TRUE; + break; + + /* Character class: set the bits and either carry on or not, + according to the repeat count. */ + + case OP_CLASS: + case OP_NEGCLASS: + { + tcode++; + for (c = 0; c < 32; c++) start_bits[c] |= tcode[c]; + tcode += 32; + switch (*tcode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + tcode++; + try_next = TRUE; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (((tcode[1] << 8) + tcode[2]) == 0) + { + tcode += 5; + try_next = TRUE; + } + break; + } + } + break; /* End of class handling */ + + } /* End of switch */ + } /* End of try_next loop */ + + code += (code[1] << 8) + code[2]; /* Advance to next branch */ + } +while (*code == OP_ALT); +return TRUE; +} + + + +/************************************************* +* Study a compiled expression * +*************************************************/ + +/* This function is handed a compiled expression that it must study to produce +information that will speed up the matching. It returns a pcre_extra block +which then gets handed back to pcre_exec(). + +Arguments: + re points to the compiled expression + options contains option bits + errorptr points to where to place error messages; + set NULL unless error + +Returns: pointer to a pcre_extra block, + NULL on error or if no optimization possible +*/ + +pcre_extra * +pcre_study(const pcre *external_re, int options, const char **errorptr) +{ +BOOL caseless; +uschar start_bits[32]; +real_pcre_extra *extra; +const real_pcre *re = (const real_pcre *)external_re; + +*errorptr = NULL; + +if (re == NULL || re->magic_number != MAGIC_NUMBER) + { + *errorptr = "argument is not a compiled regular expression"; + return NULL; + } + +if ((options & ~PUBLIC_STUDY_OPTIONS) != 0) + { + *errorptr = "unknown or incorrect option bit(s) set"; + return NULL; + } + +/* Caseless can either be from the compiled regex or from options. */ + +caseless = ((re->options | options) & PCRE_CASELESS) != 0; + +/* For an anchored pattern, or an unanchored pattern that has a first char, or a +multiline pattern that matches only at "line starts", no further processing at +present. */ + +if ((re->options & (PCRE_ANCHORED|PCRE_FIRSTSET|PCRE_STARTLINE)) != 0) + return NULL; + +/* See if we can find a fixed set of initial characters for the pattern. */ + +memset(start_bits, 0, 32 * sizeof(uschar)); +if (!set_start_bits(re->code, start_bits)) return NULL; + +/* If this studying is caseless, scan the created bit map and duplicate the +bits for any letters. */ + +if (caseless) + { + register int c; + for (c = 0; c < 256; c++) + { + if ((start_bits[c/8] & (1 << (c&7))) != 0 && + (pcre_ctypes[c] & ctype_letter) != 0) + { + int d = pcre_fcc[c]; + start_bits[d/8] |= (1 << (d&7)); + } + } + } + +/* Get an "extra" block and put the information therein. */ + +extra = (real_pcre_extra *)(pcre_malloc)(sizeof(real_pcre_extra)); + +if (extra == NULL) + { + *errorptr = "failed to get memory"; + return NULL; + } + +extra->options = PCRE_STUDY_MAPPED | (caseless? PCRE_STUDY_CASELESS : 0); +memcpy(extra->start_bits, start_bits, sizeof(start_bits)); + +return (pcre_extra *)extra; +} + +/* End of study.c */ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +This is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. See +the file Tech.Notes for some information on the internals. + +Written by: Philip Hazel + + Copyright (c) 1998 University of Cambridge + +----------------------------------------------------------------------------- +Permission is granted to anyone to use this software for any purpose on any +computer system, and to redistribute it freely, subject to the following +restrictions: + +1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. +----------------------------------------------------------------------------- +*/ + + +/* Define DEBUG to get debugging output on stdout. */ + +/* #define DEBUG */ + +/* Use a macro for debugging printing, 'cause that eliminates the use +of #ifdef inline, and there are *still* stupid compilers about that don't like +indented pre-processor statements. I suppose it's only been 10 years... */ + +#undef DPRINTF +#ifdef DEBUG +#define DPRINTF(p) printf p +#else +#define DPRINTF(p) /*nothing*/ +#endif + +/* Include the internals header, which itself includes Standard C headers plus +the external pcre header. */ + + + + +#ifndef Py_eval_input +/* For Python 1.4, graminit.h has to be explicitly included */ +#define Py_eval_input eval_input + +#endif /* FOR_PYTHON */ + +/* Allow compilation as C++ source code, should anybody want to do that. */ + +#ifdef __cplusplus +#define class pcre_class +#endif + + +/* Min and max values for the common repeats; for the maxima, 0 => infinity */ + +static const char rep_min[] = { 0, 0, 1, 1, 0, 0 }; +static const char rep_max[] = { 0, 0, 0, 0, 1, 1 }; + +/* Text forms of OP_ values and things, for debugging (not all used) */ + +#ifdef DEBUG +static const char *OP_names[] = { + "End", "\\A", "\\B", "\\b", "\\D", "\\d", + "\\S", "\\s", "\\W", "\\w", "Cut", "\\Z", + "localized \\B", "localized \\b", "localized \\W", "localized \\w", + "^", "$", "Any", "chars", + "not", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", + "*", "*?", "+", "+?", "?", "??", "{", "{", + "class", "negclass", "classL", "Ref", + "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", "Once", + "Brazero", "Braminzero", "Bra" +}; +#endif + +/* Table for handling escaped characters in the range '0'-'z'. Positive returns +are simple data values; negative values are for special things like \d and so +on. Zero means further processing is needed (for things like \x), or the escape +is invalid. */ + +static const short int escapes[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ + 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */ + '@', -ESC_A, -ESC_B, 0, -ESC_D, 0, 0, 0, /* @ - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, -ESC_S, 0, 0, 0, -ESC_W, /* P - W */ + 0, 0, -ESC_Z, '[', '\\', ']', '^', '_', /* X - _ */ + '`', 7, -ESC_b, 0, -ESC_d, 0, '\f', 0, /* ` - g */ + 0, 0, 0, 0, 0, 0, '\n', 0, /* h - o */ + 0, 0, '\r', -ESC_s, '\t', 0, '\v', -ESC_w, /* p - w */ + 0, 0, 0 /* x - z */ +}; + +/* Definition to allow mutual recursion */ + +static BOOL +compile_regex(int, int *, uschar **, const uschar **, const char **, + PyObject *); + +/* Structure for passing "static" information around between the functions +doing the matching, so that they are thread-safe. */ + +typedef struct match_data { + int errorcode; /* As it says */ + int *offset_vector; /* Offset vector */ + int offset_end; /* One past the end */ + BOOL offset_overflow; /* Set if too many extractions */ + BOOL caseless; /* Case-independent flag */ + BOOL runtime_caseless; /* Caseless forced at run time */ + BOOL multiline; /* Multiline flag */ + BOOL notbol; /* NOTBOL flag */ + BOOL noteol; /* NOTEOL flag */ + BOOL dotall; /* Dot matches any char */ + BOOL endonly; /* Dollar not before final \n */ + const uschar *start_subject; /* Start of the subject string */ + const uschar *end_subject; /* End of the subject string */ + jmp_buf fail_env; /* Environment for longjump() break out */ + const uschar *end_match_ptr; /* Subject position at end match */ + int end_offset_top; /* Highwater mark at end of match */ + jmp_buf error_env; /* For longjmp() if an error occurs deep inside a + matching operation */ + int length; /* Length of the allocated stacks */ + int point; /* Point to add next item pushed onto stacks */ + /* Pointers to the 6 stacks */ + int *off_num, *offset_top, *r1, *r2; + const uschar **eptr, **ecode; +} match_data; + + + +/************************************************* +* Global variables * +*************************************************/ + +/* PCRE is thread-clean and doesn't use any global variables in the normal +sense. However, it calls memory allocation and free functions via the two +indirections below, which are can be changed by the caller, but are shared +between all threads. */ + +void *(*pcre_malloc)(size_t) = malloc; +void (*pcre_free)(void *) = free; + + + + +/************************************************* +* Return version string * +*************************************************/ + +const char * +pcre_version(void) +{ +return PCRE_VERSION; +} + + + + +/************************************************* +* Return info about a compiled pattern * +*************************************************/ + +/* This function picks potentially useful data out of the private +structure. + +Arguments: + external_re points to compiled code + optptr where to pass back the options + first_char where to pass back the first character, + or -1 if multiline and all branches start ^, + or -2 otherwise + +Returns: number of identifying extraction brackets + or negative values on error +*/ + +int +pcre_info(const pcre *external_re, int *optptr, int *first_char) +{ +const real_pcre *re = (real_pcre *)external_re; +if (re == NULL) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; +if (optptr != NULL) *optptr = (re->options & PUBLIC_OPTIONS); +if (first_char != NULL) + *first_char = ((re->options & PCRE_FIRSTSET) != 0)? re->first_char : + ((re->options & PCRE_STARTLINE) != 0)? -1 : -2; +return re->top_bracket; +} + + + + +#ifdef DEBUG +/************************************************* +* Debugging function to print chars * +*************************************************/ + +/* Print a sequence of chars in printable format, stopping at the end of the +subject if the requested. + +Arguments: + p points to characters + length number to print + is_subject TRUE if printing from within md->start_subject + md pointer to matching data block, if is_subject is TRUE + +Returns: nothing +*/ + +static void +pchars(const uschar *p, int length, BOOL is_subject, match_data *md) +{ +int c; +if (is_subject && length > md->end_subject - p) length = md->end_subject - p; +while (length-- > 0) + if (isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c); +} +#endif + + + + +/************************************************* +* Check subpattern for empty operand * +*************************************************/ + +/* This function checks a bracketed subpattern to see if any of the paths +through it could match an empty string. This is used to diagnose an error if +such a subpattern is followed by a quantifier with an unlimited upper bound. + +Argument: + code points to the opening bracket + +Returns: TRUE or FALSE +*/ + +static BOOL +could_be_empty(uschar *code) +{ +do { + uschar *cc = code + 3; + + /* Scan along the opcodes for this branch; as soon as we find something + that matches a non-empty string, break out and advance to test the next + branch. If we get to the end of the branch, return TRUE for the whole + sub-expression. */ + + for (;;) + { + /* Test an embedded subpattern; if it could not be empty, break the + loop. Otherwise carry on in the branch. */ + + if ((int)(*cc) >= OP_BRA || (int)(*cc) == OP_ONCE) + { + if (!could_be_empty(cc)) break; + do cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT); + cc += 3; + } + + else switch (*cc) + { + /* Reached end of a branch: the subpattern may match the empty string */ + + case OP_ALT: + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + return TRUE; + + /* Skip over entire bracket groups with zero lower bound */ + + case OP_BRAZERO: + case OP_BRAMINZERO: + cc++; + /* Fall through */ + + /* Skip over assertive subpatterns */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + do cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT); + cc += 3; + break; + + /* Skip over things that don't match chars */ + + case OP_SOD: + case OP_EOD: + case OP_CIRC: + case OP_DOLL: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY_L: + case OP_WORD_BOUNDARY_L: + cc++; + break; + + /* Skip over simple repeats with zero lower bound */ + + case OP_STAR: + case OP_MINSTAR: + case OP_QUERY: + case OP_MINQUERY: + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + cc += 2; + break; + + /* Skip over UPTOs (lower bound is zero) */ + + case OP_UPTO: + case OP_MINUPTO: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + cc += 4; + break; + + /* Check a class or a back reference for a zero minimum */ + + case OP_CLASS: + case OP_NEGCLASS: + case OP_REF: + case OP_CLASS_L: + switch(*cc) + { + case (OP_REF): cc += 2; break; + case (OP_CLASS): case (OP_NEGCLASS): cc += 1+32; break; + case (OP_CLASS_L): cc += 1+1+32; break; + } + + switch (*cc) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + cc++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if ((cc[1] << 8) + cc[2] != 0) goto NEXT_BRANCH; + cc += 3; + break; + + default: + goto NEXT_BRANCH; + } + break; + + /* Anything else matches at least one character */ + + default: + goto NEXT_BRANCH; + } + } + + NEXT_BRANCH: + code += (code[1] << 8) + code[2]; + } +while (*code == OP_ALT); + +/* No branches match the empty string */ + +return FALSE; +} + +/* Determine the length of a group ID in an expression like + (?P...) +Arguments: + ptr pattern position pointer (say that 3 times fast) + finalchar the character that will mark the end of the ID + errorptr points to the pointer to the error message +*/ + +static int +get_group_id(const uschar *ptr, char finalchar, const char **errorptr) +{ + const uschar *start = ptr; + + /* If the first character is not in \w, or is in \w but is a digit, + report an error */ + if (!(pcre_ctypes[*ptr] & ctype_word) || + (pcre_ctypes[*ptr++] & ctype_digit)) + { + *errorptr = "(?P identifier must start with a letter or underscore"; + return 0; + } + + /* Increment ptr until we either hit a null byte, the desired + final character, or a non-word character */ + for(; (*ptr != 0) && (*ptr != finalchar) && + (pcre_ctypes[*ptr] & ctype_word); ptr++) + { + /* Empty loop body */ + } + if (*ptr==finalchar) + return ptr-start; + if (*ptr==0) + { + *errorptr = "unterminated (?P identifier"; + return 0; + } + *errorptr = "illegal character in (?P identifier"; + return 0; +} + +/************************************************* +* Handle escapes * +*************************************************/ + +/* This function is called when a \ has been encountered. It either returns a +positive value for a simple escape such as \n, or a negative value which +encodes one of the more complicated things such as \d. On entry, ptr is +pointing at the \. On exit, it is on the final character of the escape +sequence. + +Arguments: + ptrptr points to the pattern position pointer + errorptr points to the pointer to the error message + bracount number of previous extracting brackets + options the options bits + isclass TRUE if inside a character class + +Returns: zero or positive => a data character + negative => a special escape sequence + on error, errorptr is set +*/ + +static int +check_escape(const uschar **ptrptr, const char **errorptr, int bracount, + int options, BOOL isclass) +{ +const uschar *ptr = *ptrptr; +int c = *(++ptr) & 255; /* Ensure > 0 on signed-char systems */ +int i; + +if (c == 0) *errorptr = ERR1; + +/* Digits or letters may have special meaning; all others are literals. */ + +else if (c < '0' || c > 'z') {} + +/* Do an initial lookup in a table. A non-zero result is something that can be +returned immediately. Otherwise further processing may be required. */ + +else if ((i = escapes[c - '0']) != 0) c = i; + +/* Escapes that need further processing, or are illegal. */ + +else + { + + switch (c) + { + /* The handling of escape sequences consisting of a string of digits + starting with one that is not zero is not straightforward. By experiment, + the way Perl works seems to be as follows: + + Outside a character class, the digits are read as a decimal number. If the + number is less than 10, or if there are that many previous extracting + left brackets, then it is a back reference. Otherwise, up to three octal + digits are read to form an escaped byte. Thus \123 is likely to be octal + 123 (cf \0123, which is octal 012 followed by the literal 3). If the octal + value is greater than 377, the least significant 8 bits are taken. Inside a + character class, \ followed by a digit is always an octal number. */ + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + + { + /* PYTHON: Try to compute an octal value for a character */ + for(c=0, i=0; ptr[i]!=0 && i<3; i++) + { + if (( pcre_ctypes[ ptr[i] ] & ctype_odigit) != 0) + c = (c * 8 + ptr[i]-'0') & 255; + else + break; /* Non-octal character--break out of the loop */ + } + /* It's a character if there were exactly 3 octal digits, or if + we're inside a character class and there was at least one + octal digit. */ + if ( (i == 3) || (isclass && i!=0) ) + { + ptr += i-1; + break; + } + c = ptr[0]; /* Restore the first character after the \ */ + c -= '0'; i = 1; + while (i<2 && (pcre_ctypes[ptr[1]] & ctype_digit) != 0) + { + c = c * 10 + ptr[1] - '0'; + ptr++; i++; + } + if (c > 255 - ESC_REF) *errorptr = "back reference too big"; + c = -(ESC_REF + c); + } + break; + + /* \0 always starts an octal number, but we may drop through to here with a + larger first octal digit */ + + case '0': + c -= '0'; + while(i++ < 2 && (pcre_ctypes[ptr[1]] & ctype_digit) != 0 && + ptr[1] != '8' && ptr[1] != '9') + c = (c * 8 + *(++ptr) - '0') & 255; + break; + + /* Special escapes not starting with a digit are straightforward */ + + case 'x': + c = 0; + while ( (pcre_ctypes[ptr[1]] & ctype_xdigit) != 0) + { + ptr++; + c = c * 16 + pcre_lcc[*ptr] - + (((pcre_ctypes[*ptr] & ctype_digit) != 0)? '0' : 'W'); + c &= 255; + } + break; + + + /* PCRE_EXTRA enables extensions to Perl in the matter of escapes. Any + other alphameric following \ is an error if PCRE_EXTRA was set; otherwise, + for Perl compatibility, it is a literal. */ + + default: + if ((options & PCRE_EXTRA) != 0) switch(c) + { + case 'X': + c = -ESC_X; /* This could be a lookup if it ever got into Perl */ + break; + + default: + *errorptr = ERR3; + break; + } + break; + } + } + +*ptrptr = ptr; +return c; +} + + + +/************************************************* +* Check for counted repeat * +*************************************************/ + +/* This function is called when a '{' is encountered in a place where it might +start a quantifier. It looks ahead to see if it really is a quantifier or not. +It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd} +where the ddds are digits. + +Arguments: + p pointer to the first char after '{' + +Returns: TRUE or FALSE +*/ + +static BOOL +is_counted_repeat(const uschar *p) +{ +if ((pcre_ctypes[*p++] & ctype_digit) == 0) return FALSE; +while ((pcre_ctypes[*p] & ctype_digit) != 0) p++; +if (*p == '}') return TRUE; + +if (*p++ != ',') return FALSE; +if (*p == '}') return TRUE; + +if ((pcre_ctypes[*p++] & ctype_digit) == 0) return FALSE; +while ((pcre_ctypes[*p] & ctype_digit) != 0) p++; +return (*p == '}'); +} + + + +/************************************************* +* Read repeat counts * +*************************************************/ + +/* Read an item of the form {n,m} and return the values. This is called only +after is_counted_repeat() has confirmed that a repeat-count quantifier exists, +so the syntax is guaranteed to be correct, but we need to check the values. + +Arguments: + p pointer to first char after '{' + minp pointer to int for min + maxp pointer to int for max + returned as -1 if no max + errorptr points to pointer to error message + +Returns: pointer to '}' on success; + current ptr on error, with errorptr set +*/ + +static const uschar * +read_repeat_counts(const uschar *p, int *minp, int *maxp, const char **errorptr) +{ +int min = 0; +int max = -1; + +while ((pcre_ctypes[*p] & ctype_digit) != 0) min = min * 10 + *p++ - '0'; + +if (*p == '}') max = min; else + { + if (*(++p) != '}') + { + max = 0; + while((pcre_ctypes[*p] & ctype_digit) != 0) max = max * 10 + *p++ - '0'; + if (max < min) + { + *errorptr = ERR4; + return p; + } + } + } + +/* Do paranoid checks, then fill in the required variables, and pass back the +pointer to the terminating '}'. */ + +if (min > 65535 || max > 65535) + *errorptr = ERR5; +else + { + *minp = min; + *maxp = max; + } +return p; +} + + + +/************************************************* +* Compile one branch * +*************************************************/ + +/* Scan the pattern, compiling it into the code vector. + +Arguments: + options the option bits + bracket points to number of brackets used + code points to the pointer to the current code point + ptrptr points to the current pattern pointer + errorptr points to pointer to error message + +Returns: TRUE on success + FALSE, with *errorptr set on error +*/ + +static BOOL +compile_branch(int options, int *brackets, uschar **codeptr, + const uschar **ptrptr, const char **errorptr, PyObject *dictionary) +{ +int repeat_type, op_type; +int repeat_min, repeat_max; +int bravalue, length; +int greedy_default, greedy_non_default; +register int c; +register uschar *code = *codeptr; +const uschar *ptr = *ptrptr; +const uschar *oldptr; +uschar *previous = NULL; +uschar class[32]; +uschar *class_flag; /* Pointer to the single-byte flag for OP_CLASS_L */ + +/* Set up the default and non-default settings for greediness */ + +greedy_default = ((options & PCRE_UNGREEDY) != 0); +greedy_non_default = greedy_default ^ 1; + +/* Switch on next character until the end of the branch */ + +for (;; ptr++) + { + BOOL negate_class; + int class_charcount; + int class_lastchar; + + c = *ptr; + if ((options & PCRE_EXTENDED) != 0) + { + if ((pcre_ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + while ((c = *(++ptr)) != 0 && c != '\n'); + continue; + } + } + + switch(c) + { + /* The branch terminates at end of string, |, or ). */ + + case 0: + case '|': + case ')': + *codeptr = code; + *ptrptr = ptr; + return TRUE; + + /* Handle single-character metacharacters */ + + case '^': + previous = NULL; + *code++ = OP_CIRC; + break; + + case '$': + previous = NULL; + *code++ = OP_DOLL; + break; + + case '.': + previous = code; + *code++ = OP_ANY; + break; + + /* Character classes. These always build a 32-byte bitmap of the permitted + characters, except in the special case where there is only one character. + For negated classes, we build the map as usual, then invert it at the end. + */ + + case '[': + previous = code; + if (options & PCRE_LOCALE) + { + *code++ = OP_CLASS_L; + /* Set the flag for localized classes (like \w) to 0 */ + class_flag = code; + *class_flag = 0; + } + else + { + *code++ = OP_CLASS; + class_flag = NULL; + } + + /* If the first character is '^', set the negation flag, and use a + different opcode. This only matters if caseless matching is specified at + runtime. */ + + if ((c = *(++ptr)) == '^') + { + negate_class = TRUE; + if (*(code-1)==OP_CLASS) *(code-1) = OP_NEGCLASS; + c = *(++ptr); + } + else negate_class = FALSE; + + /* Keep a count of chars so that we can optimize the case of just a single + character. */ + + class_charcount = 0; + class_lastchar = -1; + + /* Initialize the 32-char bit map to all zeros. We have to build the + map in a temporary bit of store, in case the class contains only 1 + character, because in that case the compiled code doesn't use the + bit map. */ + + memset(class, 0, 32 * sizeof(uschar)); + + /* Process characters until ] is reached. By writing this as a "do" it + means that an initial ] is taken as a data character. */ + + do + { + if (c == 0) + { + *errorptr = ERR6; + goto FAILED; + } + + /* Backslash may introduce a single character, or it may introduce one + of the specials, which just set a flag. Escaped items are checked for + validity in the pre-compiling pass. The sequence \b is a special case. + Inside a class (and only there) it is treated as backspace. Elsewhere + it marks a word boundary. Other escapes have preset maps ready to + or into the one we are building. We assume they have more than one + character in them, so set class_count bigger than one. */ + + if (c == '\\') + { + c = check_escape(&ptr, errorptr, *brackets, options, TRUE); + if (-c == ESC_b) c = '\b'; + else if (c < 0) + { + class_charcount = 10; + switch (-c) + { + case ESC_d: + { + for (c = 0; c < 32; c++) class[c] |= pcre_cbits[c+cbit_digit]; + } + continue; + + case ESC_D: + { + for (c = 0; c < 32; c++) class[c] |= ~pcre_cbits[c+cbit_digit]; + } + continue; + + case ESC_w: + if (options & PCRE_LOCALE) + { + *class_flag |= 1; + } + else + { + for (c = 0; c < 32; c++) + class[c] |= (pcre_cbits[c] | pcre_cbits[c+cbit_word]); + } + continue; + + case ESC_W: + if (options & PCRE_LOCALE) + { + *class_flag |= 2; + } + else + { + for (c = 0; c < 32; c++) + class[c] |= ~(pcre_cbits[c] | pcre_cbits[c+cbit_word]); + } + continue; + + case ESC_s: + { + for (c = 0; c < 32; c++) class[c] |= pcre_cbits[c+cbit_space]; + } + continue; + + case ESC_S: + { + for (c = 0; c < 32; c++) class[c] |= ~pcre_cbits[c+cbit_space]; + } + continue; + + default: + *errorptr = ERR7; + goto FAILED; + } + } + /* Fall through if single character */ + } + + /* A single character may be followed by '-' to form a range. However, + Perl does not permit ']' to be the end of the range. A '-' character + here is treated as a literal. */ + + if (ptr[1] == '-' && ptr[2] != ']') + { + int d; + ptr += 2; + d = *ptr; + + if (d == 0) + { + *errorptr = ERR6; + goto FAILED; + } + + /* The second part of a range can be a single-character escape, but + not any of the other escapes. */ + + if (d == '\\') + { + d = check_escape(&ptr, errorptr, *brackets, options, TRUE); + if (d < 0) + { + if (d == -ESC_b) d = '\b'; else + { + *errorptr = ERR7; + goto FAILED; + } + } + } + + if (d < c) + { + *errorptr = ERR8; + goto FAILED; + } + + for (; c <= d; c++) + { + class[c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + int uc = pcre_fcc[c]; /* flip case */ + class[uc/8] |= (1 << (uc&7)); + } + class_charcount++; /* in case a one-char range */ + class_lastchar = c; + } + continue; /* Go get the next char in the class */ + } + + /* Handle a lone single character - we can get here for a normal + non-escape char, or after \ that introduces a single character. */ + + class [c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + c = pcre_fcc[c]; /* flip case */ + class[c/8] |= (1 << (c&7)); + } + class_charcount++; + class_lastchar = c; + } + + /* Loop until ']' reached; the check for end of string happens inside the + loop. This "while" is the end of the "do" above. */ + + while ((c = *(++ptr)) != ']'); + + /* If class_charcount is 1 and class_lastchar is not negative, we saw + precisely one character. This doesn't need the whole 32-byte bit map. + We turn it into a 1-character OP_CHAR if it's positive, or OP_NOT if + it's negative. */ + + if (class_charcount == 1 && class_lastchar >= 0) + { + if (negate_class) + { + code[-1] = OP_NOT; + } + else + { + code[-1] = OP_CHARS; + *code++ = 1; + } + *code++ = class_lastchar; + } + + /* Otherwise, negate the 32-byte map if necessary, and copy it into + the code vector. */ + + else + { + /* If this is a localized opcode, bump the code pointer up */ + if (class_flag) code++; + if (negate_class) + { + if (class_flag) *class_flag = (*class_flag) ^ 63; + for (c = 0; c < 32; c++) code[c] = ~class[c]; + } + else + memcpy(code, class, 32); + code += 32; + } + break; + + /* Various kinds of repeat */ + + case '{': + if (!is_counted_repeat(ptr+1)) goto NORMAL_CHAR; + ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorptr); + if (*errorptr != NULL) goto FAILED; + goto REPEAT; + + case '*': + repeat_min = 0; + repeat_max = -1; + goto REPEAT; + + case '+': + repeat_min = 1; + repeat_max = -1; + goto REPEAT; + + case '?': + repeat_min = 0; + repeat_max = 1; + + REPEAT: + if (previous == NULL) + { + *errorptr = ERR9; + goto FAILED; + } + + /* If the next character is '?' this is a minimizing repeat, by default, + but if PCRE_UNGREEDY is set, it works the other way round. Advance to the + next character. */ + + if (ptr[1] == '?') + { repeat_type = greedy_non_default; ptr++; } + else repeat_type = greedy_default; + + /* If the maximum is zero then the minimum must also be zero; Perl allows + this case, so we do too - by simply omitting the item altogether. */ + + if (repeat_max == 0) code = previous; + + /* If previous was a string of characters, chop off the last one and use it + as the subject of the repeat. If there was only one character, we can + abolish the previous item altogether. */ + + else if (*previous == OP_CHARS) + { + int len = previous[1]; + if (len == 1) + { + c = previous[2]; + code = previous; + } + else + { + c = previous[len+1]; + previous[1]--; + code--; + } + op_type = 0; /* Use single-char op codes */ + goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ + } + + /* If previous was a single negated character ([^a] or similar), we use + one of the special opcodes, replacing it. The code is shared with single- + character repeats by adding a suitable offset into repeat_type. */ + + else if ((int)*previous == OP_NOT) + { + op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */ + c = previous[1]; + code = previous; + goto OUTPUT_SINGLE_REPEAT; + } + + /* If previous was a character type match (\d or similar), abolish it and + create a suitable repeat item. The code is shared with single-character + repeats by adding a suitable offset into repeat_type. */ + + else if ((int)*previous < OP_CIRC || *previous == OP_ANY) + { + op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ + c = *previous; + code = previous; + + OUTPUT_SINGLE_REPEAT: + repeat_type += op_type; /* Combine both values for many cases */ + + /* A minimum of zero is handled either as the special case * or ?, or as + an UPTO, with the maximum given. */ + + if (repeat_min == 0) + { + if (repeat_max == -1) *code++ = OP_STAR + repeat_type; + else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; + else + { + *code++ = OP_UPTO + repeat_type; + *code++ = repeat_max >> 8; + *code++ = (repeat_max & 255); + } + } + + /* The case {1,} is handled as the special case + */ + + else if (repeat_min == 1 && repeat_max == -1) + *code++ = OP_PLUS + repeat_type; + + /* The case {n,n} is just an EXACT, while the general case {n,m} is + handled as an EXACT followed by an UPTO. An EXACT of 1 is optimized. */ + + else + { + if (repeat_min != 1) + { + *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ + *code++ = repeat_min >> 8; + *code++ = (repeat_min & 255); + } + + /* If the minimum is 1 and the previous item was a character string, + we either have to put back the item that got canceled if the string + length was 1, or add the character back onto the end of a longer + string. For a character type nothing need be done; it will just get + put back naturally. Note that the final character is always going to + get added below. */ + + else if (*previous == OP_CHARS) + { + if (code == previous) code += 2; else previous[1]++; + } + + /* For a single negated character we also have to put back the + item that got canceled. */ + + else if (*previous == OP_NOT) code++; + + /* If the maximum is unlimited, insert an OP_STAR. */ + + if (repeat_max < 0) + { + *code++ = c; + *code++ = OP_STAR + repeat_type; + } + + /* Else insert an UPTO if the max is greater than the min. */ + + else if (repeat_max != repeat_min) + { + *code++ = c; + repeat_max -= repeat_min; + *code++ = OP_UPTO + repeat_type; + *code++ = repeat_max >> 8; + *code++ = (repeat_max & 255); + } + } + + /* The character or character type itself comes last in all cases. */ + + *code++ = c; + } + + /* If previous was a character class or a back reference, we put the repeat + stuff after it. */ + + else if (*previous == OP_CLASS || *previous == OP_NEGCLASS || + *previous==OP_CLASS_L || *previous == OP_REF) + { + if (repeat_min == 0 && repeat_max == -1) + *code++ = OP_CRSTAR + repeat_type; + else if (repeat_min == 1 && repeat_max == -1) + *code++ = OP_CRPLUS + repeat_type; + else if (repeat_min == 0 && repeat_max == 1) + *code++ = OP_CRQUERY + repeat_type; + else + { + *code++ = OP_CRRANGE + repeat_type; + *code++ = repeat_min >> 8; + *code++ = repeat_min & 255; + if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */ + *code++ = repeat_max >> 8; + *code++ = repeat_max & 255; + } + } + + /* If previous was a bracket group, we may have to replicate it in certain + cases. If the maximum repeat count is unlimited, check that the bracket + group cannot match the empty string, and diagnose an error if it can. */ + + else if ((int)*previous >= OP_BRA) + { + int i; + int len = code - previous; + + if (repeat_max == -1 && could_be_empty(previous)) + { + *errorptr = ERR10; + goto FAILED; + } + + /* If the minimum is greater than zero, and the maximum is unlimited or + equal to the minimum, the first copy remains where it is, and is + replicated up to the minimum number of times. This case includes the + + repeat, but of course no replication is needed in that case. */ + + if (repeat_min > 0 && (repeat_max == -1 || repeat_max == repeat_min)) + { + for (i = 1; i < repeat_min; i++) + { + memcpy(code, previous, len); + code += len; + } + } + + /* If the minimum is zero, stick BRAZERO in front of the first copy. + Then, if there is a fixed upper limit, replicated up to that many times, + sticking BRAZERO in front of all the optional ones. */ + + else + { + if (repeat_min == 0) + { + memmove(previous+1, previous, len); + code++; + *previous++ = OP_BRAZERO + repeat_type; + } + + for (i = 1; i < repeat_min; i++) + { + memcpy(code, previous, len); + code += len; + } + + for (i = (repeat_min > 0)? repeat_min : 1; i < repeat_max; i++) + { + *code++ = OP_BRAZERO + repeat_type; + memcpy(code, previous, len); + code += len; + } + } + + /* If the maximum is unlimited, set a repeater in the final copy. */ + + if (repeat_max == -1) code[-3] = OP_KETRMAX + repeat_type; + } + + /* Else there's some kind of shambles */ + + else + { + *errorptr = ERR11; + goto FAILED; + } + + /* In all case we no longer have a previous item. */ + + previous = NULL; + break; + + + /* Start of nested bracket sub-expression, or comment or lookahead. + First deal with special things that can come after a bracket; all are + introduced by ?, and the appearance of any of them means that this is not a + referencing group. They were checked for validity in the first pass over + the string, so we don't have to check for syntax errors here. */ + + case '(': + previous = code; /* Only real brackets can be repeated */ + if (*(++ptr) == '?') + { + bravalue = OP_BRA; + + switch (*(++ptr)) + { + case '#': + case 'i': + case 'L': + case 'm': + case 's': + case 'x': + ptr++; + while (*ptr != ')') ptr++; + previous = NULL; + continue; + + case ':': /* Non-extracting bracket */ + ptr++; + break; + + case '=': /* Assertions can't be repeated */ + bravalue = OP_ASSERT; + ptr++; + previous = NULL; + break; + + case '!': + bravalue = OP_ASSERT_NOT; + ptr++; + previous = NULL; + break; + + case ('P'): + ptr++; + if (*ptr=='<') + { + /* (?P...) */ + int idlen; + PyObject *string, *intobj; + + ptr++; + idlen = get_group_id(ptr, '>', errorptr); + if (*errorptr) { + goto FAILED; + } + string = PyString_FromStringAndSize((char*)ptr, idlen); + intobj = PyInt_FromLong( brackets[0] + 1 ); + if (intobj == NULL || string == NULL) + { + Py_XDECREF(string); + Py_XDECREF(intobj); + *errorptr = "exception raised"; + goto FAILED; + } + PyDict_SetItem(dictionary, string, intobj); + Py_DECREF(string); Py_DECREF(intobj); /* XXX DECREF commented out! */ + ptr += idlen+1; /* Point to rest of expression */ + goto do_grouping_bracket; + } + if (*ptr=='=') + { + /* (?P=groupname) */ + int idlen, refnum; + PyObject *string, *intobj; + + ptr++; + idlen = get_group_id(ptr, ')', errorptr); + if (*errorptr) { + goto FAILED; + } + string = PyString_FromStringAndSize((char *)ptr, idlen); + if (string==NULL) { + *errorptr = "exception raised"; + goto FAILED; + } + intobj = PyDict_GetItem(dictionary, string); + if (intobj==NULL) { + Py_DECREF(string); + *errorptr = "?P= group identifier isn't defined"; + goto FAILED; + } + + refnum = PyInt_AsLong(intobj); + Py_DECREF(string); + /* The caller doesn't own the reference to the value + returned from PyDict_GetItem, so intobj is not + DECREF'ed. */ + + *code++ = OP_REF; + *code++ = refnum; + /* The continue will cause the top-level for() loop to + be resumed, so ptr will be immediately incremented. + Therefore, the following line adds just idlen, not + idlen+1 */ + ptr += idlen; + continue; + } + /* The character after ?P is neither < nor =, so + report an error. Add more Python-extensions here. */ + *errorptr="unknown after (?P"; + goto FAILED; + + case '>': /* "Match once" brackets */ + if ((options & PCRE_EXTRA) != 0) /* Not yet standard */ + { + bravalue = OP_ONCE; + ptr++; + previous = NULL; + break; + } + /* Else fall through */ + + default: + *errorptr = ERR12; + goto FAILED; + } + } + + /* Else we have a referencing group */ + + else + { + do_grouping_bracket: + if (++(*brackets) > EXTRACT_MAX) + { + *errorptr = ERR13; + goto FAILED; + } + bravalue = OP_BRA + *brackets; + } + + /* Process nested bracketed re; at end pointer is on the bracket. We copy + code into a non-register variable in order to be able to pass its address + because some compilers complain otherwise. */ + + *code = bravalue; + { + uschar *mcode = code; + if (!compile_regex(options, brackets, &mcode, &ptr, errorptr, dictionary)) + goto FAILED; + code = mcode; + } + + if (*ptr != ')') + { + *errorptr = ERR14; + goto FAILED; + } + break; + + /* Check \ for being a real metacharacter; if not, fall through and handle + it as a data character at the start of a string. Escape items are checked + for validity in the pre-compiling pass. */ + + case '\\': + oldptr = ptr; + c = check_escape(&ptr, errorptr, *brackets, options, FALSE); + + /* Handle metacharacters introduced by \. For ones like \d, the ESC_ values + are arranged to be the negation of the corresponding OP_values. For the + back references, the values are ESC_REF plus the reference number. Only + back references and those types that consume a character may be repeated. + We can test for values between ESC_b and ESC_Z for the latter; this may + have to change if any new ones are ever created. */ + + if (c < 0) + { + if (-c >= ESC_REF) + { + int refnum = -c - ESC_REF; + if (*brackets < refnum) + { + *errorptr = ERR15; + goto FAILED; + } + previous = code; + *code++ = OP_REF; + *code++ = refnum; + } + else + { + previous = (-c > ESC_b && -c < ESC_X)? code : NULL; + if ( (options & PCRE_LOCALE) != 0) + { + switch (c) + { + case (-ESC_b): c = -OP_WORD_BOUNDARY_L; break; + case (-ESC_B): c = -OP_NOT_WORD_BOUNDARY_L; break; + case (-ESC_w): c = -OP_WORDCHAR_L; break; + case (-ESC_W): c = -OP_NOT_WORDCHAR_L; break; + } + } + *code++ = -c; + } + continue; + } + + /* Data character: Reset and fall through */ + + ptr = oldptr; + c = '\\'; + + /* Handle a run of data characters until a metacharacter is encountered. + The first character is guaranteed not to be whitespace or # when the + extended flag is set. */ + + NORMAL_CHAR: + default: + previous = code; + *code = OP_CHARS; + code += 2; + length = 0; + + do + { + if ((options & PCRE_EXTENDED) != 0) + { + if ((pcre_ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + while ((c = *(++ptr)) != 0 && c != '\n'); + if (c == 0) break; + continue; + } + } + + /* Backslash may introduce a data char or a metacharacter. Escaped items + are checked for validity in the pre-compiling pass. Stop the string + before a metaitem. */ + + if (c == '\\') + { + oldptr = ptr; + c = check_escape(&ptr, errorptr, *brackets, options, FALSE); + if (c < 0) { ptr = oldptr; break; } + } + + /* Ordinary character or single-char escape */ + + *code++ = c; + length++; + } + + /* This "while" is the end of the "do" above. */ + + while (length < 255 && (pcre_ctypes[c = *(++ptr)] & ctype_meta) == 0); + + /* Compute the length and set it in the data vector, and advance to + the next state. */ + + previous[1] = length; + if (length < 255) ptr--; + break; + } + } /* end of big loop */ + +/* Control never reaches here by falling through, only by a goto for all the +error states. Pass back the position in the pattern so that it can be displayed +to the user for diagnosing the error. */ + +FAILED: +*ptrptr = ptr; +return FALSE; +} + + + + +/************************************************* +* Compile sequence of alternatives * +*************************************************/ + +/* On entry, ptr is pointing past the bracket character, but on return +it points to the closing bracket, or vertical bar, or end of string. +The code variable is pointing at the byte into which the BRA operator has been +stored. + +Argument: + options the option bits + brackets -> int containing the number of extracting brackets used + codeptr -> the address of the current code pointer + ptrptr -> the address of the current pattern pointer + errorptr -> pointer to error message + +Returns: TRUE on success +*/ + +static BOOL +compile_regex(int options, int *brackets, uschar **codeptr, + const uschar **ptrptr, const char **errorptr, PyObject *dictionary) +{ +const uschar *ptr = *ptrptr; +uschar *code = *codeptr; +uschar *start_bracket = code; + +for (;;) + { + int length; + uschar *last_branch = code; + + code += 3; + if (!compile_branch(options, brackets, &code, &ptr, errorptr, dictionary)) + { + *ptrptr = ptr; + return FALSE; + } + + /* Fill in the length of the last branch */ + + length = code - last_branch; + last_branch[1] = length >> 8; + last_branch[2] = length & 255; + + /* Reached end of expression, either ')' or end of pattern. Insert a + terminating ket and the length of the whole bracketed item, and return, + leaving the pointer at the terminating char. */ + + if (*ptr != '|') + { + length = code - start_bracket; + *code++ = OP_KET; + *code++ = length >> 8; + *code++ = length & 255; + *codeptr = code; + *ptrptr = ptr; + return TRUE; + } + + /* Another branch follows; insert an "or" node and advance the pointer. */ + + *code = OP_ALT; + ptr++; + } +/* Control never reaches here */ +} + + + +/************************************************* +* Check for anchored expression * +*************************************************/ + +/* Try to find out if this is an anchored regular expression. Consider each +alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket +all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then +it's anchored. However, if this is a multiline pattern, then only OP_SOD +counts, since OP_CIRC can match in the middle. + +A branch is also implicitly anchored if it starts with .* because that will try +the rest of the pattern at all possible matching points, so there is no point +trying them again. + +Argument: points to start of expression (the bracket) +Returns: TRUE or FALSE +*/ + +static BOOL +is_anchored(register const uschar *code, BOOL multiline) +{ +do { + int op = (int)code[3]; + if (op >= OP_BRA || op == OP_ASSERT || op == OP_ONCE) + { if (!is_anchored(code+3, multiline)) return FALSE; } + else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR) + { if (code[4] != OP_ANY) return FALSE; } + else if (op != OP_SOD && (multiline || op != OP_CIRC)) return FALSE; + code += (code[1] << 8) + code[2]; + } +while (*code == OP_ALT); +return TRUE; +} + + + +/************************************************* +* Check for start with \n line expression * +*************************************************/ + +/* This is called for multiline expressions to try to find out if every branch +starts with ^ so that "first char" processing can be done to speed things up. + +Argument: points to start of expression (the bracket) +Returns: TRUE or FALSE +*/ + +static BOOL +is_startline(const uschar *code) +{ +do { + if ((int)code[3] >= OP_BRA || code[3] == OP_ASSERT) + { if (!is_startline(code+3)) return FALSE; } + else if (code[3] != OP_CIRC) return FALSE; + code += (code[1] << 8) + code[2]; + } +while (*code == OP_ALT); +return TRUE; +} + + + +/************************************************* +* Check for fixed first char * +*************************************************/ + +/* Try to find out if there is a fixed first character. This is called for +unanchored expressions, as it speeds up their processing quite considerably. +Consider each alternative branch. If they all start with the same char, or with +a bracket all of whose alternatives start with the same char (recurse ad lib), +then we return that char, otherwise -1. + +Argument: points to start of expression (the bracket) +Returns: -1 or the fixed first char +*/ + +static int +find_firstchar(uschar *code) +{ +register int c = -1; +do + { + register int charoffset = 4; + + if ((int)code[3] >= OP_BRA || code[3] == OP_ASSERT) + { + register int d; + if ((d = find_firstchar(code+3)) < 0) return -1; + if (c < 0) c = d; else if (c != d) return -1; + } + + else switch(code[3]) + { + default: + return -1; + + case OP_EXACT: /* Fall through */ + charoffset++; + + case OP_CHARS: /* Fall through */ + charoffset++; + + case OP_PLUS: + case OP_MINPLUS: + if (c < 0) c = code[charoffset]; else if (c != code[charoffset]) return -1; + break; + } + code += (code[1] << 8) + code[2]; + } +while (*code == OP_ALT); +return c; +} + + + +/************************************************* +* Compile a Regular Expression * +*************************************************/ + +/* This function takes a string and returns a pointer to a block of store +holding a compiled version of the expression. + +Arguments: + pattern the regular expression + options various option bits + errorptr pointer to pointer to error text + erroroffset ptr offset in pattern where error was detected + +Returns: pointer to compiled data block, or NULL on error, + with errorptr and erroroffset set +*/ + +pcre * +pcre_compile(const char *pattern, int options, const char **errorptr, + int *erroroffset, PyObject *dictionary) +{ +real_pcre *re; +int spaces = 0; +int length = 3; /* For initial BRA plus length */ +int runlength; +int c, size; +int bracount = 0; +int brastack[200]; +int top_backref = 0; +unsigned int brastackptr = 0; +uschar *code; +const uschar *ptr; + +#ifdef DEBUG +uschar *code_base, *code_end; +#endif + +/* We can't pass back an error message if errorptr is NULL; I guess the best we +can do is just return NULL. */ + +if (errorptr == NULL) return NULL; +*errorptr = NULL; + +/* However, we can give a message for this error */ + +if (erroroffset == NULL) + { + *errorptr = ERR16; + return NULL; + } +*erroroffset = 0; + +if ((options & ~PUBLIC_OPTIONS) != 0) + { + *errorptr = ERR17; + return NULL; + } + +DPRINTF(("------------------------------------------------------------------\n")); +DPRINTF(("%s\n", pattern)); + +/* The first thing to do is to make a pass over the pattern to compute the +amount of store required to hold the compiled code. This does not have to be +perfect as long as errors are overestimates. At the same time we can detect any +internal flag settings. Make an attempt to correct for any counted white space +if an "extended" flag setting appears late in the pattern. We can't be so +clever for #-comments. */ + +ptr = (const uschar *)(pattern - 1); +while ((c = *(++ptr)) != 0) + { + int min, max; + int class_charcount; + + if ((pcre_ctypes[c] & ctype_space) != 0) + { + if ((options & PCRE_EXTENDED) != 0) continue; + spaces++; + } + + if (c == '#' && (options & PCRE_EXTENDED) != 0) + { + while ((c = *(++ptr)) != 0 && c != '\n'); + continue; + } + + switch(c) + { + /* A backslashed item may be an escaped "normal" character or a + character type. For a "normal" character, put the pointers and + character back so that tests for whitespace etc. in the input + are done correctly. */ + + case '\\': + { + const uschar *save_ptr = ptr; + c = check_escape(&ptr, errorptr, bracount, options, FALSE); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if (c >= 0) + { + ptr = save_ptr; + c = '\\'; + goto NORMAL_CHAR; + } + } + length++; + + /* A back reference needs an additional char, plus either one or 5 + bytes for a repeat. We also need to keep the value of the highest + back reference. */ + + if (c <= -ESC_REF) + { + int refnum = -c - ESC_REF; + if (refnum > top_backref) top_backref = refnum; + length++; /* For single back reference */ + if (ptr[1] == '{' && is_counted_repeat(ptr+2)) + { + ptr = read_repeat_counts(ptr+2, &min, &max, errorptr); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + else length += 5; + if (ptr[1] == '?') ptr++; + } + } + continue; + + case '^': + case '.': + case '$': + case '*': /* These repeats won't be after brackets; */ + case '+': /* those are handled separately */ + case '?': + length++; + continue; + + /* This covers the cases of repeats after a single char, metachar, class, + or back reference. */ + + case '{': + if (!is_counted_repeat(ptr+1)) goto NORMAL_CHAR; + ptr = read_repeat_counts(ptr+1, &min, &max, errorptr); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + else + { + length--; /* Uncount the original char or metachar */ + if (min == 1) length++; else if (min > 0) length += 4; + if (max > 0) length += 4; else length += 2; + } + if (ptr[1] == '?') ptr++; + continue; + + /* An alternation contains an offset to the next branch or ket. */ + case '|': + length += 3; + continue; + + /* A character class uses 33 characters. Don't worry about character types + that aren't allowed in classes - they'll get picked up during the compile. + A character class that contains only one character uses 2 or 3 bytes, + depending on whether it is negated or not. Notice this where we can. */ + + case '[': + class_charcount = 0; + if (*(++ptr) == '^') ptr++; + do + { + if (*ptr == '\\') + { + int ch = check_escape(&ptr, errorptr, bracount, options, TRUE); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if (-ch == ESC_b) class_charcount++; else class_charcount = 10; + } + else class_charcount++; + ptr++; + } + while (*ptr != 0 && *ptr != ']'); + + /* Repeats for negated single chars are handled by the general code */ + + if (class_charcount == 1) length += 3; else + { + length += 33; + if (options & PCRE_LOCALE) length++; /* Add a byte for the localization flag */ + + /* A repeat needs either 1 or 5 bytes. */ + + if (*ptr != 0 && ptr[1] == '{' && is_counted_repeat(ptr+2)) + { + ptr = read_repeat_counts(ptr+2, &min, &max, errorptr); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if ((min == 0 && (max == 1 || max == -1)) || + (min == 1 && max == -1)) + length++; + else length += 5; + if (ptr[1] == '?') ptr++; + } + } + continue; + + /* Brackets may be genuine groups or special things */ + + case '(': + + /* Handle special forms of bracket, which all start (? */ + + if (ptr[1] == '?') switch (c = ptr[2]) + { + /* Skip over comments entirely */ + case '#': + ptr += 3; + while (*ptr != 0 && *ptr != ')') ptr++; + if (*ptr == 0) + { + *errorptr = ERR18; + goto PCRE_ERROR_RETURN; + } + continue; + + /* Non-referencing groups and lookaheads just move the pointer on, and + then behave like a non-special bracket, except that they don't increment + the count of extracting brackets. */ + + case ':': + case '=': + case '!': + ptr += 2; + break; + + case ('P'): + { + int idlen; + switch (*ptr++) { + case ('<'): + idlen = get_group_id(ptr++, '>', errorptr); + if (*errorptr) goto PCRE_ERROR_RETURN; + ptr += idlen+1; + break; + case ('='): + idlen = get_group_id(ptr++, ')', errorptr); + if (*errorptr) goto PCRE_ERROR_RETURN; + ptr += idlen+1; + length++; + break; + } + } + break; + + /* Ditto for the "once only" bracket, allowed only if the extra bit + is set. */ + + case '>': + if ((options & PCRE_EXTRA) != 0) + { + ptr += 2; + break; + } + /* Else fall through */ + + /* Else loop setting valid options until ) is met. Anything else is an + error. */ + + default: + ptr += 2; + for (;; ptr++) + { + if ((c = *ptr) == 'i') + { + options |= PCRE_CASELESS; + continue; + } + else if ((c = *ptr) == 'L') + { + options |= PCRE_LOCALE; + continue; + } + else if ((c = *ptr) == 'm') + { + options |= PCRE_MULTILINE; + continue; + } + else if (c == 's') + { + options |= PCRE_DOTALL; + continue; + } + else if (c == 'x') + { + options |= PCRE_EXTENDED; + length -= spaces; /* Already counted spaces */ + continue; + } + else if (c == ')') break; + + *errorptr = ERR12; + goto PCRE_ERROR_RETURN; + } + continue; /* End of this bracket handling */ + } + + /* Extracting brackets must be counted so we can process escapes in a + Perlish way. */ + + else bracount++; + + /* Non-special forms of bracket. Save length for computing whole length + at end if there's a repeat that requires duplication of the group. */ + + if (brastackptr >= sizeof(brastack)/sizeof(int)) + { + *errorptr = ERR19; + goto PCRE_ERROR_RETURN; + } + + brastack[brastackptr++] = length; + length += 3; + continue; + + /* Handle ket. Look for subsequent max/min; for certain sets of values we + have to replicate this bracket up to that many times. If brastackptr is + 0 this is an unmatched bracket which will generate an error, but take care + not to try to access brastack[-1]. */ + + case ')': + length += 3; + { + int minval = 1; + int maxval = 1; + int duplength = (brastackptr > 0)? length - brastack[--brastackptr] : 0; + + /* Leave ptr at the final char; for read_repeat_counts this happens + automatically; for the others we need an increment. */ + + if ((c = ptr[1]) == '{' && is_counted_repeat(ptr+2)) + { + ptr = read_repeat_counts(ptr+2, &minval, &maxval, errorptr); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + } + else if (c == '*') { minval = 0; maxval = -1; ptr++; } + else if (c == '+') { maxval = -1; ptr++; } + else if (c == '?') { minval = 0; ptr++; } + + /* If there is a minimum > 1 we have to replicate up to minval-1 times; + if there is a limited maximum we have to replicate up to maxval-1 times + and allow for a BRAZERO item before each optional copy, as we also have + to do before the first copy if the minimum is zero. */ + + if (minval == 0) length++; + else if (minval > 1) length += (minval - 1) * duplength; + if (maxval > minval) length += (maxval - minval) * (duplength + 1); + } + continue; + + /* Non-special character. For a run of such characters the length required + is the number of characters + 2, except that the maximum run length is 255. + We won't get a skipped space or a non-data escape or the start of a # + comment as the first character, so the length can't be zero. */ + + NORMAL_CHAR: + default: + length += 2; + runlength = 0; + do + { + if ((pcre_ctypes[c] & ctype_space) != 0) + { + if ((options & PCRE_EXTENDED) != 0) continue; + spaces++; + } + + if (c == '#' && (options & PCRE_EXTENDED) != 0) + { + while ((c = *(++ptr)) != 0 && c != '\n'); + continue; + } + + /* Backslash may introduce a data char or a metacharacter; stop the + string before the latter. */ + + if (c == '\\') + { + const uschar *saveptr = ptr; + c = check_escape(&ptr, errorptr, bracount, options, FALSE); + if (*errorptr != NULL) goto PCRE_ERROR_RETURN; + if (c < 0) { ptr = saveptr; break; } + } + + /* Ordinary character or single-char escape */ + + runlength++; + } + + /* This "while" is the end of the "do" above. */ + + while (runlength < 255 && (pcre_ctypes[c = *(++ptr)] & ctype_meta) == 0); + + ptr--; + length += runlength; + continue; + } + } + +length += 4; /* For final KET and END */ + +if (length > 65539) + { + *errorptr = ERR20; + return NULL; + } + +/* Compute the size of data block needed and get it, either from malloc or +externally provided function. We specify "code[0]" in the offsetof() expression +rather than just "code", because it has been reported that one broken compiler +fails on "code" because it is also an independent variable. It should make no +difference to the value of the offsetof(). */ + +size = length + offsetof(real_pcre, code[0]); +re = (real_pcre *)(pcre_malloc)(size+50); + +if (re == NULL) + { + *errorptr = ERR21; + return NULL; + } + +/* Put in the magic number and the options. */ + +re->magic_number = MAGIC_NUMBER; +re->options = options; + +/* Set up a starting, non-extracting bracket, then compile the expression. On +error, *errorptr will be set non-NULL, so we don't need to look at the result +of the function here. */ + +ptr = (const uschar *)pattern; +code = re->code; +*code = OP_BRA; +bracount = 0; +(void)compile_regex(options, &bracount, &code, &ptr, errorptr, dictionary); +re->top_bracket = bracount; +re->top_backref = top_backref; + +/* If not reached end of pattern on success, there's an excess bracket. */ + +if (*errorptr == NULL && *ptr != 0) *errorptr = ERR22; + +/* Fill in the terminating state and check for disastrous overflow, but +if debugging, leave the test till after things are printed out. */ + +*code++ = OP_END; + + +#ifndef DEBUG +if (code - re->code > length) *errorptr = ERR23; +#endif + +/* Failed to compile */ + +if (*errorptr != NULL) + { + (pcre_free)(re); + PCRE_ERROR_RETURN: + *erroroffset = ptr - (const uschar *)pattern; + return NULL; + } + +/* If the anchored option was not passed, set flag if we can determine that it +is anchored by virtue of ^ characters or \A or anything else. Otherwise, see if +we can determine what the first character has to be, because that speeds up +unanchored matches no end. In the case of multiline matches, an alternative is +to set the PCRE_STARTLINE flag if all branches start with ^. */ + +if ((options & PCRE_ANCHORED) == 0) + { + if (is_anchored(re->code, (options & PCRE_MULTILINE) != 0)) + re->options |= PCRE_ANCHORED; + else + { + int ch = find_firstchar(re->code); + if (ch >= 0) + { + re->first_char = ch; + re->options |= PCRE_FIRSTSET; + } + else if (is_startline(re->code)) + re->options |= PCRE_STARTLINE; + } + } + +/* Print out the compiled data for debugging */ + +#ifdef DEBUG + +printf("Length = %d top_bracket = %d top_backref=%d\n", + length, re->top_bracket, re->top_backref); + +if (re->options != 0) + { + printf("%s%s%s%s%s%s%s%s\n", + ((re->options & PCRE_ANCHORED) != 0)? "anchored " : "", + ((re->options & PCRE_CASELESS) != 0)? "caseless " : "", + ((re->options & PCRE_EXTENDED) != 0)? "extended " : "", + ((re->options & PCRE_MULTILINE) != 0)? "multiline " : "", + ((re->options & PCRE_DOTALL) != 0)? "dotall " : "", + ((re->options & PCRE_DOLLAR_ENDONLY) != 0)? "endonly " : "", + ((re->options & PCRE_EXTRA) != 0)? "extra " : "", + ((re->options & PCRE_UNGREEDY) != 0)? "ungreedy " : ""); + } + +if ((re->options & PCRE_FIRSTSET) != 0) + { + if (isprint(re->first_char)) printf("First char = %c\n", re->first_char); + else printf("First char = \\x%02x\n", re->first_char); + } + +code_end = code; +code_base = code = re->code; + +while (code < code_end) + { + int charlength; + + printf("%3d ", code - code_base); + + if (*code >= OP_BRA) + { + printf("%3d Bra %d", (code[1] << 8) + code[2], *code - OP_BRA); + code += 2; + } + + else switch(*code) + { + case OP_CHARS: + charlength = *(++code); + printf("%3d ", charlength); + while (charlength-- > 0) + if (isprint(c = *(++code))) printf("%c", c); else printf("\\x%02x", c); + break; + + case OP_KETRMAX: + case OP_KETRMIN: + case OP_ALT: + case OP_KET: + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ONCE: + printf("%3d %s", (code[1] << 8) + code[2], OP_names[*code]); + code += 2; + break; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + if (*code >= OP_TYPESTAR) + printf(" %s", OP_names[code[1]]); + else if (isprint(c = code[1])) printf(" %c", c); + else printf(" \\x%02x", c); + printf("%s", OP_names[*code++]); + break; + + case OP_EXACT: + case OP_UPTO: + case OP_MINUPTO: + if (isprint(c = code[3])) printf(" %c{", c); + else printf(" \\x%02x{", c); + if (*code != OP_EXACT) printf("0,"); + printf("%d}", (code[1] << 8) + code[2]); + if (*code == OP_MINUPTO) printf("?"); + code += 3; + break; + + case OP_TYPEEXACT: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + printf(" %s{", OP_names[code[3]]); + if (*code != OP_TYPEEXACT) printf(","); + printf("%d}", (code[1] << 8) + code[2]); + if (*code == OP_TYPEMINUPTO) printf("?"); + code += 3; + break; + + case OP_NOT: + if (isprint(c = *(++code))) printf(" [^%c]", c); + else printf(" [^\\x%02x]", c); + break; + + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + if (isprint(c = code[1])) printf(" [^%c]", c); + else printf(" [^\\x%02x]", c); + printf("%s", OP_names[*code++]); + break; + + case OP_NOTEXACT: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + if (isprint(c = code[3])) printf(" [^%c]{", c); + else printf(" [^\\x%02x]{", c); + if (*code != OP_NOTEXACT) printf(","); + printf("%d}", (code[1] << 8) + code[2]); + if (*code == OP_NOTMINUPTO) printf("?"); + code += 3; + break; + + case OP_REF: + printf(" \\%d", *(++code)); + code ++; + goto CLASS_REF_REPEAT; + + case OP_CLASS: + case OP_NEGCLASS: + case OP_CLASS_L: + { + int i, min, max; + + if (*code==OP_CLASS_L) + { + code++; + printf("Locflag = %i ", *code++); + printf(" ["); + } + else + { + if (*code++ == OP_CLASS) printf(" ["); + else printf(" ^["); + } + + + for (i = 0; i < 256; i++) + { + if ((code[i/8] & (1 << (i&7))) != 0) + { + int j; + for (j = i+1; j < 256; j++) + if ((code[j/8] & (1 << (j&7))) == 0) break; + if (i == '-' || i == ']') printf("\\"); + if (isprint(i)) printf("%c", i); else printf("\\x%02x", i); + if (--j > i) + { + printf("-"); + if (j == '-' || j == ']') printf("\\"); + if (isprint(j)) printf("%c", j); else printf("\\x%02x", j); + } + i = j; + } + } + printf("]"); + code += 32; + /* code ++;*/ + + CLASS_REF_REPEAT: + + switch(*code) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + printf("%s", OP_names[*code]); + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + min = (code[1] << 8) + code[2]; + max = (code[3] << 8) + code[4]; + if (max == 0) printf("{%d,}", min); + else printf("{%d,%d}", min, max); + if (*code == OP_CRMINRANGE) printf("?"); + code += 4; + break; + + default: + code--; + } + } + break; + + /* Anything else is just a one-node item */ + + default: + printf(" %s", OP_names[*code]); + break; + } + + code++; + printf("\n"); + } +printf("------------------------------------------------------------------\n"); + +/* This check is done here in the debugging case so that the code that +was compiled can be seen. */ + +if (code - re->code > length) + { + printf("length=%i, code length=%i\n", length, code-re->code); + *errorptr = ERR23; + (pcre_free)(re); + *erroroffset = ptr - (uschar *)pattern; + return NULL; + } +#endif + +return (pcre *)re; +} + + + +/************************************************* +* Match a character type * +*************************************************/ + +/* Not used in all the places it might be as it's sometimes faster +to put the code inline. + +Arguments: + type the character type + c the character + dotall the dotall flag + +Returns: TRUE if character is of the type +*/ + +static BOOL +match_type(int type, int c, BOOL dotall) +{ + +#ifdef DEBUG +if (isprint(c)) printf("matching subject %c against ", c); + else printf("matching subject \\x%02x against ", c); +printf("%s\n", OP_names[type]); +#endif + +switch(type) + { + case OP_ANY: return dotall || c != '\n'; + case OP_NOT_DIGIT: return (pcre_ctypes[c] & ctype_digit) == 0; + case OP_DIGIT: return (pcre_ctypes[c] & ctype_digit) != 0; + case OP_NOT_WHITESPACE: return (pcre_ctypes[c] & ctype_space) == 0; + case OP_WHITESPACE: return (pcre_ctypes[c] & ctype_space) != 0; + case OP_NOT_WORDCHAR: return (pcre_ctypes[c] & ctype_word) == 0; + case OP_WORDCHAR: return (pcre_ctypes[c] & ctype_word) != 0; + case OP_NOT_WORDCHAR_L: return (c!='_' && !isalnum(c)); + case OP_WORDCHAR_L: return (c=='_' || isalnum(c)); + } +return FALSE; +} + + + +/************************************************* +* Match a back-reference * +*************************************************/ + +/* If a back reference hasn't been set, the match fails. + +Arguments: + number reference number + eptr points into the subject + length length to be matched + md points to match data block + +Returns: TRUE if matched +*/ + +static BOOL +match_ref(int number, register const uschar *eptr, int length, match_data *md) +{ +const uschar *p = md->start_subject + md->offset_vector[number]; + +#ifdef DEBUG +if (eptr >= md->end_subject) + printf("matching subject "); +else + { + printf("matching subject "); + pchars(eptr, length, TRUE, md); + } +printf(" against backref "); +pchars(p, length, FALSE, md); +printf("\n"); +#endif + +/* Always fail if not enough characters left */ + +if (length > md->end_subject - p) return FALSE; + +/* Separate the caseless case for speed */ + +if (md->caseless) + { while (length-- > 0) if (pcre_lcc[*p++] != pcre_lcc[*eptr++]) return FALSE; } +else + { while (length-- > 0) if (*p++ != *eptr++) return FALSE; } + +return TRUE; +} + +static int free_stack(match_data *md) +{ +/* Free any stack space that was allocated by the call to match(). */ +if (md->off_num) PyMem_DEL(md->off_num); +if (md->offset_top) PyMem_DEL(md->offset_top); +if (md->r1) PyMem_DEL(md->r1); +if (md->r2) PyMem_DEL(md->r2); +if (md->eptr) PyMem_DEL((char *)md->eptr); +if (md->ecode) PyMem_DEL((char *)md->ecode); +return 0; +} + +static int grow_stack(match_data *md) +{ + if (md->length != 0) + { + md->length = md->length + md->length/2; + } + else + { + int string_len = md->end_subject - md->start_subject + 1; + if (string_len < 80) {md->length = string_len; } + else {md->length = 80;} + } + PyMem_RESIZE(md->offset_top, int, md->length); + /* Can't realloc a pointer-to-const; cast const away. */ + md->eptr = (const uschar **)PyMem_Realloc((void *)md->eptr, + sizeof(uschar *) * md->length); + md->ecode = (const uschar **)PyMem_Realloc((void *)md->ecode, + sizeof(uschar *) * md->length); + PyMem_RESIZE(md->off_num, int, md->length); + PyMem_RESIZE(md->r1, int, md->length); + PyMem_RESIZE(md->r2, int, md->length); + if (md->offset_top == NULL || md->eptr == NULL || md->ecode == NULL || + md->off_num == NULL || md->r1 == NULL || md->r2 == NULL) + { + PyErr_NoMemory(); + longjmp(md->error_env, 1); + } + return 0; +} + + +/************************************************* +* Match from current position * +*************************************************/ + +/* On entry ecode points to the first opcode, and eptr to the first character. + +Arguments: + eptr pointer in subject + ecode position in code + offset_top current top pointer + md pointer to "static" info for the match + +Returns: TRUE if matched +*/ + +static BOOL +match(register const uschar *eptr, register const uschar *ecode, int offset_top, + match_data *md) +{ + int save_stack_position = md->point; +match_loop: + +#define SUCCEED goto succeed +#define FAIL goto fail + +for (;;) + { + int min, max, ctype; + register int i; + register int c; + BOOL minimize = FALSE; + + /* Opening bracket. Check the alternative branches in turn, failing if none + match. We have to set the start offset if required and there is space + in the offset vector so that it is available for subsequent back references + if the bracket matches. However, if the bracket fails, we must put back the + previous value of both offsets in case they were set by a previous copy of + the same bracket. Don't worry about setting the flag for the error case here; + that is handled in the code for KET. */ + + if ((int)*ecode >= OP_BRA) + { + int number = (*ecode - OP_BRA) << 1; + int save_offset1 = 0, save_offset2 = 0; + + DPRINTF(("start bracket %d\n", number/2)); + + if (number > 0 && number < md->offset_end) + { + save_offset1 = md->offset_vector[number]; + save_offset2 = md->offset_vector[number+1]; + md->offset_vector[number] = eptr - md->start_subject; + + DPRINTF(("saving %d %d\n", save_offset1, save_offset2)); + } + + /* Recurse for all the alternatives. */ + + do + { + if (match(eptr, ecode+3, offset_top, md)) SUCCEED; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + + DPRINTF(("bracket %d failed\n", number/2)); + + if (number > 0 && number < md->offset_end) + { + md->offset_vector[number] = save_offset1; + md->offset_vector[number+1] = save_offset2; + } + + FAIL; + } + + /* Other types of node can be handled by a switch */ + + switch(*ecode) + { + case OP_END: + md->end_match_ptr = eptr; /* Record where we ended */ + md->end_offset_top = offset_top; /* and how many extracts were taken */ + SUCCEED; + + /* The equivalent of Prolog's "cut" - if the rest doesn't match, the + whole thing doesn't match, so we have to get out via a longjmp(). */ + + case OP_CUT: + if (match(eptr, ecode+1, offset_top, md)) SUCCEED; + longjmp(md->fail_env, 1); + + /* Assertion brackets. Check the alternative branches in turn - the + matching won't pass the KET for an assertion. If any one branch matches, + the assertion is true. */ + + case OP_ASSERT: + do + { + if (match(eptr, ecode+3, offset_top, md)) break; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + if (*ecode == OP_KET) FAIL; + + /* Continue from after the assertion, updating the offsets high water + mark, since extracts may have been taken during the assertion. */ + + do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); + ecode += 3; + offset_top = md->end_offset_top; + continue; + + /* Negative assertion: all branches must fail to match */ + + case OP_ASSERT_NOT: + do + { + if (match(eptr, ecode+3, offset_top, md)) FAIL; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + ecode += 3; + continue; + + /* "Once" brackets are like assertion brackets except that after a match, + the point in the subject string is not moved back. Thus there can never be + a move back into the brackets. Check the alternative branches in turn - the + matching won't pass the KET for this kind of subpattern. If any one branch + matches, we carry on, leaving the subject pointer. */ + + case OP_ONCE: + do + { + if (match(eptr, ecode+3, offset_top, md)) break; + ecode += (ecode[1] << 8) + ecode[2]; + } + while (*ecode == OP_ALT); + if (*ecode == OP_KET) FAIL; + + /* Continue as from after the assertion, updating the offsets high water + mark, since extracts may have been taken. */ + + do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); + ecode += 3; + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + continue; + + /* An alternation is the end of a branch; scan along to find the end of the + bracketed group and go to there. */ + + case OP_ALT: + do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); + break; + + /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating + that it may occur zero times. It may repeat infinitely, or not at all - + i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper + repeat limits are compiled as a number of copies, with the optional ones + preceded by BRAZERO or BRAMINZERO. */ + + case OP_BRAZERO: + { + const uschar *next = ecode+1; + if (match(eptr, next, offset_top, md)) SUCCEED; + do next += (next[1] << 8) + next[2]; while (*next == OP_ALT); + ecode = next + 3; + } + break; + + case OP_BRAMINZERO: + { + const uschar *next = ecode+1; + do next += (next[1] << 8) + next[2]; while (*next == OP_ALT); + if (match(eptr, next+3, offset_top, md)) SUCCEED; + ecode++; + } + break;; + + /* End of a group, repeated or non-repeating. If we are at the end of + an assertion "group", stop matching and SUCCEED, but record the + current high water mark for use by positive assertions. */ + + case OP_KET: + case OP_KETRMIN: + case OP_KETRMAX: + { + int number; + const uschar *prev = ecode - (ecode[1] << 8) - ecode[2]; + + if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT || *prev == OP_ONCE) + { + md->end_match_ptr = eptr; /* For ONCE */ + md->end_offset_top = offset_top; + SUCCEED; + } + + /* In all other cases we have to check the group number back at the + start and if necessary complete handling an extraction by setting the + final offset and bumping the high water mark. */ + + number = (*prev - OP_BRA) << 1; + + DPRINTF(("end bracket %d\n", number/2)); + + if (number > 0) + { + if (number >= md->offset_end) md->offset_overflow = TRUE; else + { + md->offset_vector[number+1] = eptr - md->start_subject; + if (offset_top <= number) offset_top = number + 2; + } + } + + /* For a non-repeating ket, just advance to the next node and continue at + this level. */ + + if (*ecode == OP_KET) + { + ecode += 3; + break; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. */ + + if (*ecode == OP_KETRMIN) + { + const uschar *ptr; + if (match(eptr, ecode+3, offset_top, md)) goto succeed; + /* Handle alternation inside the BRA...KET; push the additional + alternatives onto the stack */ + ptr=prev; + do { + ptr += (ptr[1]<<8)+ ptr[2]; + if (*ptr==OP_ALT) + { + if (md->length == md->point) + { + grow_stack(md); + } + md->offset_top[md->point] = offset_top; + md->eptr[md->point] = eptr; + md->ecode[md->point] = ptr+3; + md->r1[md->point] = 0; + md->r2[md->point] = 0; + md->off_num[md->point] = 0; + md->point++; + } + } while (*ptr==OP_ALT); + ecode=prev+3; goto match_loop; + } + else /* OP_KETRMAX */ + { + const uschar *ptr; + /*int points_pushed=0;*/ + + /* Push one failure point, that will resume matching at the code after + the KETRMAX opcode. */ + if (md->length == md->point) + { + grow_stack(md); + } + md->offset_top[md->point] = offset_top; + md->eptr[md->point] = eptr; + md->ecode[md->point] = ecode+3; + md->r1[md->point] = md->offset_vector[number]; + md->r2[md->point] = md->offset_vector[number+1]; + md->off_num[md->point] = number; + md->point++; + + md->offset_vector[number] = eptr - md->start_subject; + /* Handle alternation inside the BRA...KET; push each of the + additional alternatives onto the stack */ + ptr=prev; + do { + ptr += (ptr[1]<<8)+ ptr[2]; + if (*ptr==OP_ALT) + { + if (md->length == md->point) + if (md->length == md->point) + { + grow_stack(md); + } + md->offset_top[md->point] = offset_top; + md->eptr[md->point] = eptr; + md->ecode[md->point] = ptr+3; + md->r1[md->point] = 0; + md->r2[md->point] = 0; + md->off_num[md->point] = 0; + md->point++; + /*points_pushed++;*/ + } + } while (*ptr==OP_ALT); + /* Jump to the first (or only) alternative and resume trying to match */ + ecode=prev+3; goto match_loop; + } + } + + /* Start of subject unless notbol, or after internal newline if multiline */ + + case OP_CIRC: + if (md->notbol && eptr == md->start_subject) FAIL; + if (md->multiline) + { + if (eptr != md->start_subject && eptr[-1] != '\n') FAIL; + ecode++; + break; + } + /* ... else fall through */ + + /* Start of subject assertion */ + + case OP_SOD: + if (eptr != md->start_subject) FAIL; + ecode++; + break; + + /* Assert before internal newline if multiline, or before + a terminating newline unless endonly is set, else end of subject unless + noteol is set. */ + + case OP_DOLL: + if (md->noteol && eptr >= md->end_subject) FAIL; + if (md->multiline) + { + if (eptr < md->end_subject && *eptr != '\n') FAIL; + ecode++; + break; + } + else if (!md->endonly) + { + if (eptr < md->end_subject - 1 || + (eptr == md->end_subject - 1 && *eptr != '\n')) FAIL; + ecode++; + break; + } + /* ... else fall through */ + + /* End of subject assertion */ + + case OP_EOD: + if (eptr < md->end_subject) FAIL; + ecode++; + break; + + /* Word boundary assertions */ + + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + { + BOOL prev_is_word = (eptr != md->start_subject) && + ((pcre_ctypes[eptr[-1]] & ctype_word) != 0); + BOOL cur_is_word = (eptr < md->end_subject) && + ((pcre_ctypes[*eptr] & ctype_word) != 0); + if ((*ecode++ == OP_WORD_BOUNDARY)? + cur_is_word == prev_is_word : cur_is_word != prev_is_word) + FAIL; + } + break; + + case OP_NOT_WORD_BOUNDARY_L: + case OP_WORD_BOUNDARY_L: + { + BOOL prev_is_word = (eptr != md->start_subject) && + (isalnum(eptr[-1]) || eptr[-1]=='_'); + BOOL cur_is_word = (eptr < md->end_subject) && + (isalnum(*eptr) || *eptr=='_'); + if ((*ecode++ == OP_WORD_BOUNDARY_L)? + cur_is_word == prev_is_word : cur_is_word != prev_is_word) + FAIL; + } + break; + + + /* Match a single character type; inline for speed */ + + case OP_ANY: + if (!md->dotall && eptr < md->end_subject && *eptr == '\n') FAIL; + if (eptr++ >= md->end_subject) FAIL; + ecode++; + break; + + case OP_NOT_DIGIT: + if (eptr >= md->end_subject || (pcre_ctypes[*eptr++] & ctype_digit) != 0) + FAIL; + ecode++; + break; + + case OP_DIGIT: + if (eptr >= md->end_subject || (pcre_ctypes[*eptr++] & ctype_digit) == 0) + FAIL; + ecode++; + break; + + case OP_NOT_WHITESPACE: + if (eptr >= md->end_subject || (pcre_ctypes[*eptr++] & ctype_space) != 0) + FAIL; + ecode++; + break; + + case OP_WHITESPACE: + if (eptr >= md->end_subject || (pcre_ctypes[*eptr++] & ctype_space) == 0) + FAIL; + ecode++; + break; + + case OP_NOT_WORDCHAR: + if (eptr >= md->end_subject || (pcre_ctypes[*eptr++] & ctype_word) != 0) + FAIL; + ecode++; + break; + + case OP_WORDCHAR: + if (eptr >= md->end_subject || (pcre_ctypes[*eptr++] & ctype_word) == 0) + FAIL; + ecode++; + break; + + case OP_NOT_WORDCHAR_L: + if (eptr >= md->end_subject || (*eptr=='_' || isalnum(*eptr) )) + FAIL; + eptr++; + ecode++; + break; + + case OP_WORDCHAR_L: + if (eptr >= md->end_subject || (*eptr!='_' && !isalnum(*eptr) )) + FAIL; + eptr++; + ecode++; + break; + + /* Match a back reference, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. The code is similar + to that for character classes, but repeated for efficiency. Then obey + similar code to character type repeats - written out again for speed. + However, if the referenced string is the empty string, always treat + it as matched, any number of times (otherwise there could be infinite + loops). */ + + case OP_REF: + { + int length; + int number = ecode[1] << 1; /* Doubled reference number */ + ecode += 2; /* Advance past the item */ + + if (number >= offset_top || md->offset_vector[number] < 0) + { + md->errorcode = PCRE_ERROR_BADREF; + FAIL; + } + + length = md->offset_vector[number+1] - md->offset_vector[number]; + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = (ecode[1] << 8) + ecode[2]; + max = (ecode[3] << 8) + ecode[4]; + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + if (!match_ref(number, eptr, length, md)) FAIL; + eptr += length; + continue; /* With the main loop */ + } + + /* If the length of the reference is zero, just continue with the + main loop. */ + + if (length == 0) continue; + + /* First, ensure the minimum number of matches are present. We get back + the length of the reference string explicitly rather than passing the + address of eptr, so that eptr can be a register variable. */ + + for (i = 1; i <= min; i++) + { + if (!match_ref(number, eptr, length, md)) FAIL; + eptr += length; + } + + /* If min = max, continue at the same level without recursion. + They are not both allowed to be zero. */ + + if (min == max) continue; + + /* If minimizing, keep trying and advancing the pointer */ + + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md)) SUCCEED; + if (i >= max || !match_ref(number, eptr, length, md)) + FAIL; + eptr += length; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest string and work backwards */ + + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (!match_ref(number, eptr, length, md)) break; + eptr += length; + } + while (eptr >= pp) + { + if (match(eptr, ecode, offset_top, md)) SUCCEED; + eptr -= length; + } + FAIL; + } + } + /* Control never gets here */ + + /* Match a character class, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. Then obey similar + code to character type repeats - written out again for speed. If caseless + matching was set at runtime but not at compile time, we have to check both + versions of a character, and we have to behave differently for positive and + negative classes. This is the only time where OP_CLASS and OP_NEGCLASS are + treated differently. */ + + case OP_CLASS: + case OP_NEGCLASS: + { + BOOL nasty_case = *ecode == OP_NEGCLASS && md->runtime_caseless; + const uschar *data = ecode + 1; /* Save for matching */ + ecode += 33; /* Advance past the item */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = (ecode[1] << 8) + ecode[2]; + max = (ecode[3] << 8) + ecode[4]; + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + min = max = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) FAIL; + c = *eptr++; + + /* Either not runtime caseless, or it was a positive class. For + runtime caseless, continue if either case is in the map. */ + + if (!nasty_case) + { + if ((data[c/8] & (1 << (c&7))) != 0) continue; + if (md->runtime_caseless) + { + c = pcre_fcc[c]; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + } + } + + /* Runtime caseless and it was a negative class. Continue only if + both cases are in the map. */ + + else + { + if ((data[c/8] & (1 << (c&7))) == 0) FAIL; + c = pcre_fcc[c]; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + } + + FAIL; + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == max) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md)) SUCCEED; + if (i >= max || eptr >= md->end_subject) FAIL; + c = *eptr++; + + /* Either not runtime caseless, or it was a positive class. For + runtime caseless, continue if either case is in the map. */ + + if (!nasty_case) + { + if ((data[c/8] & (1 << (c&7))) != 0) continue; + if (md->runtime_caseless) + { + c = pcre_fcc[c]; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + } + } + + /* Runtime caseless and it was a negative class. Continue only if + both cases are in the map. */ + + else + { + if ((data[c/8] & (1 << (c&7))) == 0) return FALSE; + c = pcre_fcc[c]; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + } + + FAIL; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + const uschar *pp = eptr; + for (i = min; i < max; eptr++, i++) + { + if (eptr >= md->end_subject) break; + c = *eptr; + + /* Either not runtime caseless, or it was a positive class. For + runtime caseless, continue if either case is in the map. */ + + if (!nasty_case) + { + if ((data[c/8] & (1 << (c&7))) != 0) continue; + if (md->runtime_caseless) + { + c = pcre_fcc[c]; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + } + } + + /* Runtime caseless and it was a negative class. Continue only if + both cases are in the map. */ + + else + { + if ((data[c/8] & (1 << (c&7))) == 0) break; + c = pcre_fcc[c]; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + } + + break; + } + + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md)) SUCCEED; + FAIL; + } + } + /* Control never gets here */ + + /* OP_CLASS_L opcode: handles localized character classes */ + + case OP_CLASS_L: + { + const uschar *data = ecode + 1; /* Save for matching */ + const uschar locale_flag = *data; + ecode++; data++; /* The localization support adds an extra byte */ + + ecode += 33; /* Advance past the item */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = (ecode[1] << 8) + ecode[2]; + max = (ecode[3] << 8) + ecode[4]; + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + if (eptr >= md->end_subject) FAIL; + c = *eptr++; + if ((data[c/8] & (1 << (c&7))) != 0) continue; /* With main loop */ + if ( (locale_flag & 1) && (isalnum(c) || c=='_') ) continue; /* Locale \w */ + if ( (locale_flag & 2) && (!isalnum(c) && c!='_') ) continue; /* Locale \W */ +#if 0 + if ( (locale_flag & 4) && isdigit(c) ) continue; /* Locale \d */ + if ( (locale_flag & 8) && !isdigit(c) ) continue; /* Locale \D */ + if ( (locale_flag & 16) && isspace(c) ) continue; /* Locale \s */ + if ( (locale_flag & 32) && !isspace(c) ) continue; /* Locale \S */ +#endif + + if (md->runtime_caseless) + { + c = pcre_fcc[c]; + if ((data[c/8] & (1 << (c&7))) != 0) continue; /* With main loop */ + + if ( (locale_flag & 1) && (isalnum(c) || c=='_') ) continue; /* Locale \w */ + if ( (locale_flag & 2) && (!isalnum(c) && c!='_') ) continue; /* Locale \W */ + } + FAIL; + } + + /* First, ensure the minimum number of matches are present. */ + + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) FAIL; + c = *eptr++; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + if ( (locale_flag & 1) && (isalnum(c) || c=='_') ) continue; /* Locale \w */ + if ( (locale_flag & 2) && (!isalnum(c) && c!='_') ) continue; /* Locale \W */ + + if (md->runtime_caseless) + { + c = pcre_fcc[c]; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + if ( (locale_flag & 1) && (isalnum(c) || c=='_') ) continue; /* Locale \w */ + if ( (locale_flag & 2) && (!isalnum(c) && c!='_') ) continue; /* Locale \W */ + } + FAIL; + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == max) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md)) SUCCEED; + if (i >= max || eptr >= md->end_subject) FAIL; + c = *eptr++; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + if ( (locale_flag & 1) && (isalnum(c) || c=='_') ) continue; /* Locale \w */ + if ( (locale_flag & 2) && (!isalnum(c) && c!='_') ) continue; /* Locale \W */ + + if (md->runtime_caseless) + { + c = pcre_fcc[c]; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + if ( (locale_flag & 1) && (isalnum(c) || c=='_') ) continue; /* Locale \w */ + if ( (locale_flag & 2) && (!isalnum(c) && c!='_') ) continue; /* Locale \W */ + } + FAIL; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + const uschar *pp = eptr; + for (i = min; i < max; eptr++, i++) + { + if (eptr >= md->end_subject) break; + c = *eptr; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + if ( (locale_flag & 1) && (isalnum(c) || c=='_') ) continue; /* Locale \w */ + if ( (locale_flag & 2) && (!isalnum(c) && c!='_') ) continue; /* Locale \W */ + if (md->runtime_caseless) + { + c = pcre_fcc[c]; + if ((data[c/8] & (1 << (c&7))) != 0) continue; + if ( (locale_flag & 1) && (isalnum(c) || c=='_') ) continue; /* Locale \w */ + if ( (locale_flag & 2) && (!isalnum(c) && c!='_') ) continue; /* Locale \W */ + } + break; + } + + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md)) SUCCEED; + FAIL; + } + } + /* Control never gets here */ + + /* Match a run of characters */ + + case OP_CHARS: + { + register int length = ecode[1]; + ecode += 2; + +#ifdef DEBUG /* Sigh. Some compilers never learn. */ + if (eptr >= md->end_subject) + printf("matching subject against pattern "); + else + { + printf("matching subject "); + pchars(eptr, length, TRUE, md); + printf(" against pattern "); + } + pchars(ecode, length, FALSE, md); + printf("\n"); +#endif + + if (length > md->end_subject - eptr) FAIL; + if (md->caseless) + { + while (length-- > 0) if (pcre_lcc[*ecode++] != pcre_lcc[*eptr++]) FAIL; + } + else + { + while (length-- > 0) if (*ecode++ != *eptr++) FAIL; + } + } + break; + + /* Match a single character repeatedly; different opcodes share code. */ + + case OP_EXACT: + min = max = (ecode[1] << 8) + ecode[2]; + ecode += 3; + goto REPEATCHAR; + + case OP_UPTO: + case OP_MINUPTO: + min = 0; + max = (ecode[1] << 8) + ecode[2]; + minimize = *ecode == OP_MINUPTO; + ecode += 3; + goto REPEATCHAR; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + c = *ecode++ - OP_STAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-character matches. We can give + up quickly if there are fewer than the minimum number of characters left in + the subject. */ + + REPEATCHAR: + if (min > md->end_subject - eptr) FAIL; + c = *ecode++; + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If min = max, continue at the same + level without recursing. Otherwise, if minimizing, keep trying the rest of + the expression and advancing one matching character if failing, up to the + maximum. Alternatively, if maximizing, find the maximum number of + characters and work backwards. */ + + DPRINTF(("matching %c{%d,%d} against subject %.*s\n", c, min, max, + max, eptr)); + + if (md->caseless) + { + c = pcre_lcc[c]; + for (i = 1; i <= min; i++) if (c != pcre_lcc[*eptr++]) FAIL; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md)) SUCCEED; + if (i >= max || eptr >= md->end_subject || c != pcre_lcc[*eptr++]) + FAIL; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c != pcre_lcc[*eptr]) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md)) SUCCEED; + FAIL; + } + /* Control never gets here */ + } + + /* Caseful comparisons */ + + else + { + for (i = 1; i <= min; i++) if (c != *eptr++) FAIL; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md)) SUCCEED; + if (i >= max || eptr >= md->end_subject || c != *eptr++) FAIL; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c != *eptr) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md)) SUCCEED; + FAIL; + } + } + /* Control never gets here */ + + /* Match a negated single character */ + + case OP_NOT: + if (eptr >= md->end_subject) FAIL; + ecode++; + if (md->caseless) + { + if (pcre_lcc[*ecode++] == pcre_lcc[*eptr++]) FAIL; + } + else + { + if (*ecode++ == *eptr++) FAIL; + } + break; + + /* Match a negated single character repeatedly. This is almost a repeat of + the code for a repeated single character, but I haven't found a nice way of + commoning these up that doesn't require a test of the positive/negative + option for each character match. Maybe that wouldn't add very much to the + time taken, but character matching *is* what this is all about... */ + + case OP_NOTEXACT: + min = max = (ecode[1] << 8) + ecode[2]; + ecode += 3; + goto REPEATNOTCHAR; + + case OP_NOTUPTO: + case OP_NOTMINUPTO: + min = 0; + max = (ecode[1] << 8) + ecode[2]; + minimize = *ecode == OP_NOTMINUPTO; + ecode += 3; + goto REPEATNOTCHAR; + + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + c = *ecode++ - OP_NOTSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-character matches. We can give + up quickly if there are fewer than the minimum number of characters left in + the subject. */ + + REPEATNOTCHAR: + if (min > md->end_subject - eptr) FAIL; + c = *ecode++; + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If min = max, continue at the same + level without recursing. Otherwise, if minimizing, keep trying the rest of + the expression and advancing one matching character if failing, up to the + maximum. Alternatively, if maximizing, find the maximum number of + characters and work backwards. */ + + DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", c, min, max, + max, eptr)); + + if (md->caseless) + { + c = pcre_lcc[c]; + for (i = 1; i <= min; i++) if (c == pcre_lcc[*eptr++]) FAIL; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md)) SUCCEED; + if (i >= max || eptr >= md->end_subject || c == pcre_lcc[*eptr++]) + FAIL; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c == pcre_lcc[*eptr]) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md)) SUCCEED; + FAIL; + } + /* Control never gets here */ + } + + /* Caseful comparisons */ + + else + { + for (i = 1; i <= min; i++) if (c == *eptr++) FAIL; + if (min == max) continue; + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md)) SUCCEED; + if (i >= max || eptr >= md->end_subject || c == *eptr++) FAIL; + } + /* Control never gets here */ + } + else + { + const uschar *pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || c == *eptr) break; + eptr++; + } + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md)) SUCCEED; + FAIL; + } + } + /* Control never gets here */ + + /* Match a single character type repeatedly; several different opcodes + share code. This is very similar to the code for single characters, but we + repeat it in the interests of efficiency. */ + + case OP_TYPEEXACT: + min = max = (ecode[1] << 8) + ecode[2]; + minimize = TRUE; + ecode += 3; + goto REPEATTYPE; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + min = 0; + max = (ecode[1] << 8) + ecode[2]; + minimize = *ecode == OP_TYPEMINUPTO; + ecode += 3; + goto REPEATTYPE; + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + c = *ecode++ - OP_TYPESTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single character type matches */ + + REPEATTYPE: + ctype = *ecode++; /* Code for the character type */ + + /* First, ensure the minimum number of matches are present. Use inline + code for maximizing the speed, and do the type test once at the start + (i.e. keep it out of the loop). Also test that there are at least the + minimum number of characters before we start. */ + + if (min > md->end_subject - eptr) FAIL; + if (min > 0) switch(ctype) + { + case OP_ANY: + if (!md->dotall) + { for (i = 1; i <= min; i++) if (*eptr++ == '\n') FAIL; } + else eptr += min; + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= min; i++) + if ((pcre_ctypes[*eptr++] & ctype_digit) != 0) FAIL; + break; + + case OP_DIGIT: + for (i = 1; i <= min; i++) + if ((pcre_ctypes[*eptr++] & ctype_digit) == 0) FAIL; + break; + + case OP_NOT_WHITESPACE: + for (i = 1; i <= min; i++) + if ((pcre_ctypes[*eptr++] & ctype_space) != 0) FAIL; + break; + + case OP_WHITESPACE: + for (i = 1; i <= min; i++) + if ((pcre_ctypes[*eptr++] & ctype_space) == 0) FAIL; + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= min; i++) if ((pcre_ctypes[*eptr++] & ctype_word) != 0) + FAIL; + break; + + case OP_WORDCHAR: + for (i = 1; i <= min; i++) if ((pcre_ctypes[*eptr++] & ctype_word) == 0) + FAIL; + break; + + case OP_NOT_WORDCHAR_L: + for (i = 1; i <= min; i++, eptr++) if (*eptr=='_' || isalnum(*eptr)) + FAIL; + break; + + case OP_WORDCHAR_L: + for (i = 1; i <= min; i++, eptr++) if (*eptr!='_' && !isalnum(*eptr)) + FAIL; + break; + } + + /* If min = max, continue at the same level without recursing */ + + if (min == max) continue; + + /* If minimizing, we have to test the rest of the pattern before each + subsequent match, so inlining isn't much help; just use the function. */ + + if (minimize) + { + for (i = min;; i++) + { + if (match(eptr, ecode, offset_top, md)) SUCCEED; + if (i >= max || eptr >= md->end_subject || + !match_type(ctype, *eptr++, md->dotall)) + FAIL; + } + /* Control never gets here */ + } + + /* If maximizing it is worth using inline code for speed, doing the type + test once at the start (i.e. keep it out of the loop). */ + + else + { + const uschar *pp = eptr; + switch(ctype) + { + case OP_ANY: + if (!md->dotall) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || *eptr == '\n') break; + eptr++; + } + } + else + { + c = max - min; + if (c > md->end_subject - eptr) c = md->end_subject - eptr; + eptr += c; + } + break; + + case OP_NOT_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (pcre_ctypes[*eptr] & ctype_digit) != 0) + break; + eptr++; + } + break; + + case OP_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (pcre_ctypes[*eptr] & ctype_digit) == 0) + break; + eptr++; + } + break; + + case OP_NOT_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (pcre_ctypes[*eptr] & ctype_space) != 0) + break; + eptr++; + } + break; + + case OP_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (pcre_ctypes[*eptr] & ctype_space) == 0) + break; + eptr++; + } + break; + + case OP_NOT_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (pcre_ctypes[*eptr] & ctype_word) != 0) + break; + eptr++; + } + break; + + case OP_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (pcre_ctypes[*eptr] & ctype_word) == 0) + break; + eptr++; + } + break; + case OP_NOT_WORDCHAR_L: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (*eptr=='_' || isalnum(*eptr) ) ) + break; + eptr++; + } + break; + + case OP_WORDCHAR_L: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (*eptr!='_' && !isalnum(*eptr) ) ) + break; + eptr++; + } + break; + } + + while (eptr >= pp) + if (match(eptr--, ecode, offset_top, md)) SUCCEED; + FAIL; + } + /* Control never gets here */ + + /* There's been some horrible disaster. */ + + default: + DPRINTF(("Unknown opcode %d\n", *ecode)); + md->errorcode = PCRE_ERROR_UNKNOWN_NODE; + FAIL; + } + + /* Do not stick any code in here without much thought; it is assumed + that "continue" in the code above comes out to here to repeat the main + loop. */ + + } /* End of main loop */ +/* Control never reaches here */ + +fail: + if (md->point > save_stack_position) + { + /* If there are still points remaining on the stack, pop the next one off */ + int off_num; + + md->point--; + offset_top = md->offset_top[md->point]; + eptr = md->eptr[md->point]; + ecode = md->ecode[md->point]; + off_num = md->off_num[md->point]; + md->offset_vector[off_num] = md->r1[md->point]; + md->offset_vector[off_num+1] = md->r2[md->point]; + goto match_loop; + } + /* Failure, and nothing left on the stack, so end this function call */ + + /* Restore the top of the stack to where it was before this function + call. This lets us use one stack for everything; recursive calls + can push and pop information, and may increase the stack. When + the call returns, the parent function can resume pushing and + popping wherever it was. */ + + md->point = save_stack_position; + return FALSE; + +succeed: + return TRUE; +} + + + +/************************************************* +* Segregate setjmp() * +*************************************************/ + +/* The -Wall option of gcc gives warnings for all local variables when setjmp() +is used, even if the coding conforms to the rules of ANSI C. To avoid this, we +hide it in a separate function. This is called only when PCRE_EXTRA is set, +since it's needed only for the extension \X option, and with any luck, a good +compiler will spot the tail recursion and compile it efficiently. + +Arguments: + eptr pointer in subject + ecode position in code + offset_top current top pointer + md pointer to "static" info for the match + +Returns: TRUE if matched +*/ + +static BOOL +match_with_setjmp(const uschar *eptr, const uschar *ecode, int offset_top, + match_data *match_block) +{ +return setjmp(match_block->fail_env) == 0 && + match(eptr, ecode, offset_top, match_block); +} + + + +/************************************************* +* Execute a Regular Expression * +*************************************************/ + +/* This function applies a compiled re to a subject string and picks out +portions of the string if it matches. Two elements in the vector are set for +each substring: the offsets to the start and end of the substring. + +Arguments: + external_re points to the compiled expression + external_extra points to "hints" from pcre_study() or is NULL + subject points to the subject string + length length of subject string (may contain binary zeros) + options option bits + offsets points to a vector of ints to be filled in with offsets + offsetcount the number of elements in the vector + +Returns: > 0 => success; value is the number of elements filled in + = 0 => success, but offsets is not big enough + -1 => failed to match + < -1 => some kind of unexpected problem +*/ + +int +pcre_exec(const pcre *external_re, const pcre_extra *external_extra, + const char *subject, int length, int start_pos, int options, + int *offsets, int offsetcount) +{ + /* The "volatile" directives are to make gcc -Wall stop complaining + that these variables can be clobbered by the longjmp. Hopefully + they won't cost too much performance. */ +volatile int resetcount, ocount; +volatile int first_char = -1; +const uschar * volatile start_bits = NULL; +const uschar * volatile start_match = (const uschar *)subject + start_pos; +match_data match_block; +const uschar *end_subject; +const real_pcre *re = (const real_pcre *)external_re; +const real_pcre_extra *extra = (const real_pcre_extra *)external_extra; +volatile BOOL using_temporary_offsets = FALSE; +volatile BOOL anchored = ((re->options | options) & PCRE_ANCHORED) != 0; +volatile BOOL startline = (re->options & PCRE_STARTLINE) != 0; + +if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION; + +if (re == NULL || subject == NULL || + (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; + +match_block.start_subject = (const uschar *)subject; +match_block.end_subject = match_block.start_subject + length; +end_subject = match_block.end_subject; + +match_block.caseless = ((re->options | options) & PCRE_CASELESS) != 0; +match_block.runtime_caseless = match_block.caseless && + (re->options & PCRE_CASELESS) == 0; + +match_block.multiline = ((re->options | options) & PCRE_MULTILINE) != 0; +match_block.dotall = ((re->options | options) & PCRE_DOTALL) != 0; +match_block.endonly = ((re->options | options) & PCRE_DOLLAR_ENDONLY) != 0; + +match_block.notbol = (options & PCRE_NOTBOL) != 0; +match_block.noteol = (options & PCRE_NOTEOL) != 0; + +match_block.errorcode = PCRE_ERROR_NOMATCH; /* Default error */ + +/* Set the stack state to empty */ + match_block.off_num = match_block.offset_top = NULL; + match_block.r1 = match_block.r2 = NULL; + match_block.eptr = match_block.ecode = NULL; + match_block.point = match_block.length = 0; + +/* If the expression has got more back references than the offsets supplied can +hold, we get a temporary bit of working store to use during the matching. +Otherwise, we can use the vector supplied, rounding down its size to a multiple +of 2. */ + +ocount = offsetcount & (-2); +if (re->top_backref > 0 && re->top_backref >= ocount/2) + { + ocount = re->top_backref * 2 + 2; + match_block.offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int)); + if (match_block.offset_vector == NULL) return PCRE_ERROR_NOMEMORY; + using_temporary_offsets = TRUE; + DPRINTF(("Got memory to hold back references\n")); + } +else match_block.offset_vector = offsets; + +match_block.offset_end = ocount; +match_block.offset_overflow = FALSE; + +/* Compute the minimum number of offsets that we need to reset each time. Doing +this makes a huge difference to execution time when there aren't many brackets +in the pattern. */ + +resetcount = 2 + re->top_bracket * 2; +if (resetcount > offsetcount) resetcount = ocount; + +/* If MULTILINE is set at exec time but was not set at compile time, and the +anchored flag is set, we must re-check because a setting provoked by ^ in the +pattern is not right in multi-line mode. Calling is_anchored() again here does +the right check, because multiline is now set. If it now yields FALSE, the +expression must have had ^ starting some of its branches. Check to see if +that is true for *all* branches, and if so, set the startline flag. */ + +if (match_block.multiline && anchored && (re->options & PCRE_MULTILINE) == 0 && + !is_anchored(re->code, match_block.multiline)) + { + anchored = FALSE; + if (is_startline(re->code)) startline = TRUE; + } + +/* Set up the first character to match, if available. The first_char value is +never set for an anchored regular expression, but the anchoring may be forced +at run time, so we have to test for anchoring. The first char may be unset for +an unanchored pattern, of course. If there's no first char and the pattern was +studied, the may be a bitmap of possible first characters. However, we can +use this only if the caseless state of the studying was correct. */ + +if (!anchored) + { + if ((re->options & PCRE_FIRSTSET) != 0) + { + first_char = re->first_char; + if (match_block.caseless) first_char = pcre_lcc[first_char]; + } + else + if (!startline && extra != NULL && + (extra->options & PCRE_STUDY_MAPPED) != 0 && + ((extra->options & PCRE_STUDY_CASELESS) != 0) == match_block.caseless) + start_bits = extra->start_bits; + } + +/* Loop for unanchored matches; for anchored regexps the loop runs just once. */ + +do + { + int rc; + register int *iptr = match_block.offset_vector; + register int *iend = iptr + resetcount; + + /* Reset the maximum number of extractions we might see. */ + + while (iptr < iend) *iptr++ = -1; + + /* Advance to a unique first char if possible */ + + if (first_char >= 0) + { + if (match_block.caseless) + while (start_match < end_subject && pcre_lcc[*start_match] != first_char) + start_match++; + else + while (start_match < end_subject && *start_match != first_char) + start_match++; + } + + /* Or to just after \n for a multiline match if possible */ + + else if (startline) + { + if (start_match > match_block.start_subject) + { + while (start_match < end_subject && start_match[-1] != '\n') + start_match++; + } + } + + /* Or to a non-unique first char */ + + else if (start_bits != NULL) + { + while (start_match < end_subject) + { + register int c = *start_match; + if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++; else break; + } + } + +#ifdef DEBUG /* Sigh. Some compilers never learn. */ + printf(">>>> Match against: "); + pchars(start_match, end_subject - start_match, TRUE, &match_block); + printf("\n"); +#endif + + /* When a match occurs, substrings will be set for all internal extractions; + we just need to set up the whole thing as substring 0 before returning. If + there were too many extractions, set the return code to zero. In the case + where we had to get some local store to hold offsets for backreferences, copy + those back references that we can. In this case there need not be overflow + if certain parts of the pattern were not used. + + Before starting the match, we have to set up a longjmp() target to enable + the "cut" operation to fail a match completely without backtracking. This + is done in a separate function to avoid compiler warnings. We need not do + it unless PCRE_EXTRA is set, since only in that case is the "cut" operation + enabled. */ + + /* To handle errors such as running out of memory for the failure + stack, we need to save this location via setjmp(), so + error-handling code can call longjmp() to jump out of deeply-nested code. */ + if (setjmp(match_block.error_env)==0) + { + + if ((re->options & PCRE_EXTRA) != 0) + { + if (!match_with_setjmp(start_match, re->code, 2, &match_block)) + continue; + } + else if (!match(start_match, re->code, 2, &match_block)) continue; + + /* Copy the offset information from temporary store if necessary */ + + if (using_temporary_offsets) + { + if (offsetcount >= 4) + { + memcpy(offsets + 2, match_block.offset_vector + 2, + (offsetcount - 2) * sizeof(int)); + DPRINTF(("Copied offsets from temporary memory\n")); + } + if (match_block.end_offset_top > offsetcount) + match_block.offset_overflow = TRUE; + + DPRINTF(("Freeing temporary memory\n")); + (pcre_free)(match_block.offset_vector); + } + + rc = match_block.offset_overflow? 0 : match_block.end_offset_top/2; + + if (match_block.offset_end < 2) rc = 0; else + { + offsets[0] = start_match - match_block.start_subject; + offsets[1] = match_block.end_match_ptr - match_block.start_subject; + } + + DPRINTF((">>>> returning %d\n", rc)); + free_stack(&match_block); + return rc; + } /* End of (if setjmp(match_block.error_env)...) */ + free_stack(&match_block); + + /* Return an error code; pcremodule.c will preserve the exception */ + if (PyErr_Occurred()) return PCRE_ERROR_NOMEMORY; + } +while (!anchored && + match_block.errorcode == PCRE_ERROR_NOMATCH && + start_match++ < end_subject); + +if (using_temporary_offsets) + { + DPRINTF(("Freeing temporary memory\n")); + (pcre_free)(match_block.offset_vector); + } + +#ifdef DEBUG +printf(">>>> returning %d\n", match_block.errorcode); +#endif + + free_stack(&match_block); + return match_block.errorcode; +} + +/* End of pcre.c */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/python.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/python.c new file mode 100644 index 00000000..a7446e78 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/python.c @@ -0,0 +1,24 @@ +/* Minimal main program -- everything is loaded from the library */ + +#include "Python.h" + +#ifdef __FreeBSD__ +#include +#endif + +int +main(int argc, char **argv) +{ + /* 754 requires that FP exceptions run in "no stop" mode by default, + * and until C vendors implement C99's ways to control FP exceptions, + * Python requires non-stop mode. Alas, some platforms enable FP + * exceptions by default. Here we disable them. + */ +#ifdef __FreeBSD__ + fp_except_t m; + + m = fpgetmask(); + fpsetmask(m & ~FP_X_OFL); +#endif + return Py_Main(argc, argv); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/readline.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/readline.c new file mode 100644 index 00000000..c6d8bfb4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/readline.c @@ -0,0 +1,736 @@ +/* This module makes GNU readline available to Python. It has ideas + * contributed by Lee Busby, LLNL, and William Magro, Cornell Theory + * Center. The completer interface was inspired by Lele Gaifax. + * + * More recently, it was largely rewritten by Guido van Rossum who is + * now maintaining it. + */ + +/* Standard definitions */ +#include "Python.h" +#include +#include +#include + +#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) +/* GNU readline() mistakenly sets the LC_CTYPE locale. + * This is evil. Only the user or the app's main() should do this! + * We must save and restore the locale around the rl_initialize() call. + */ +#define SAVE_LOCALE +#include +#endif + +/* GNU readline definitions */ +#undef HAVE_CONFIG_H /* Else readline/chardefs.h includes strings.h */ +#include +#include + +#ifdef HAVE_RL_COMPLETION_MATCHES +#define completion_matches(x, y) \ + rl_completion_matches((x), ((rl_compentry_func_t *)(y))) +#endif + + +/* Exported function to send one line to readline's init file parser */ + +static PyObject * +parse_and_bind(PyObject *self, PyObject *args) +{ + char *s, *copy; + if (!PyArg_ParseTuple(args, "s:parse_and_bind", &s)) + return NULL; + /* Make a copy -- rl_parse_and_bind() modifies its argument */ + /* Bernard Herzog */ + copy = malloc(1 + strlen(s)); + if (copy == NULL) + return PyErr_NoMemory(); + strcpy(copy, s); + rl_parse_and_bind(copy); + free(copy); /* Free the copy */ + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(doc_parse_and_bind, +"parse_and_bind(string) -> None\n\ +Parse and execute single line of a readline init file."); + + +/* Exported function to parse a readline init file */ + +static PyObject * +read_init_file(PyObject *self, PyObject *args) +{ + char *s = NULL; + if (!PyArg_ParseTuple(args, "|z:read_init_file", &s)) + return NULL; + errno = rl_read_init_file(s); + if (errno) + return PyErr_SetFromErrno(PyExc_IOError); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(doc_read_init_file, +"read_init_file([filename]) -> None\n\ +Parse a readline initialization file.\n\ +The default filename is the last filename used."); + + +/* Exported function to load a readline history file */ + +static PyObject * +read_history_file(PyObject *self, PyObject *args) +{ + char *s = NULL; + if (!PyArg_ParseTuple(args, "|z:read_history_file", &s)) + return NULL; + errno = read_history(s); + if (errno) + return PyErr_SetFromErrno(PyExc_IOError); + Py_INCREF(Py_None); + return Py_None; +} + +static int history_length = -1; /* do not truncate history by default */ +PyDoc_STRVAR(doc_read_history_file, +"read_history_file([filename]) -> None\n\ +Load a readline history file.\n\ +The default filename is ~/.history."); + + +/* Exported function to save a readline history file */ + +static PyObject * +write_history_file(PyObject *self, PyObject *args) +{ + char *s = NULL; + if (!PyArg_ParseTuple(args, "|z:write_history_file", &s)) + return NULL; + errno = write_history(s); + if (!errno && history_length >= 0) + history_truncate_file(s, history_length); + if (errno) + return PyErr_SetFromErrno(PyExc_IOError); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(doc_write_history_file, +"write_history_file([filename]) -> None\n\ +Save a readline history file.\n\ +The default filename is ~/.history."); + + +/* Set history length */ + +static PyObject* +set_history_length(PyObject *self, PyObject *args) +{ + int length = history_length; + if (!PyArg_ParseTuple(args, "i:set_history_length", &length)) + return NULL; + history_length = length; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(set_history_length_doc, +"set_history_length(length) -> None\n\ +set the maximal number of items which will be written to\n\ +the history file. A negative length is used to inhibit\n\ +history truncation."); + + +/* Get history length */ + +static PyObject* +get_history_length(PyObject *self, PyObject *noarg) +{ + return PyInt_FromLong(history_length); +} + +PyDoc_STRVAR(get_history_length_doc, +"get_history_length() -> int\n\ +return the maximum number of items that will be written to\n\ +the history file."); + + +/* Generic hook function setter */ + +static PyObject * +set_hook(const char *funcname, PyObject **hook_var, + PyThreadState **tstate, PyObject *args) +{ + PyObject *function = Py_None; + char buf[80]; + PyOS_snprintf(buf, sizeof(buf), "|O:set_%.50s", funcname); + if (!PyArg_ParseTuple(args, buf, &function)) + return NULL; + if (function == Py_None) { + Py_XDECREF(*hook_var); + *hook_var = NULL; + *tstate = NULL; + } + else if (PyCallable_Check(function)) { + PyObject *tmp = *hook_var; + Py_INCREF(function); + *hook_var = function; + Py_XDECREF(tmp); + *tstate = PyThreadState_Get(); + } + else { + PyOS_snprintf(buf, sizeof(buf), + "set_%.50s(func): argument not callable", + funcname); + PyErr_SetString(PyExc_TypeError, buf); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + + +/* Exported functions to specify hook functions in Python */ + +static PyObject *startup_hook = NULL; +static PyThreadState *startup_hook_tstate = NULL; + +#ifdef HAVE_RL_PRE_INPUT_HOOK +static PyObject *pre_input_hook = NULL; +static PyThreadState *pre_input_hook_tstate = NULL; +#endif + +static PyObject * +set_startup_hook(PyObject *self, PyObject *args) +{ + return set_hook("startup_hook", &startup_hook, + &startup_hook_tstate, args); +} + +PyDoc_STRVAR(doc_set_startup_hook, +"set_startup_hook([function]) -> None\n\ +Set or remove the startup_hook function.\n\ +The function is called with no arguments just\n\ +before readline prints the first prompt."); + + +#ifdef HAVE_RL_PRE_INPUT_HOOK + +/* Set pre-input hook */ + +static PyObject * +set_pre_input_hook(PyObject *self, PyObject *args) +{ + return set_hook("pre_input_hook", &pre_input_hook, + &pre_input_hook_tstate, args); +} + +PyDoc_STRVAR(doc_set_pre_input_hook, +"set_pre_input_hook([function]) -> None\n\ +Set or remove the pre_input_hook function.\n\ +The function is called with no arguments after the first prompt\n\ +has been printed and just before readline starts reading input\n\ +characters."); + +#endif + + +/* Exported function to specify a word completer in Python */ + +static PyObject *completer = NULL; +static PyThreadState *completer_tstate = NULL; + +static PyObject *begidx = NULL; +static PyObject *endidx = NULL; + + +/* Get the beginning index for the scope of the tab-completion */ + +static PyObject * +get_begidx(PyObject *self, PyObject *noarg) +{ + Py_INCREF(begidx); + return begidx; +} + +PyDoc_STRVAR(doc_get_begidx, +"get_begidx() -> int\n\ +get the beginning index of the readline tab-completion scope"); + + +/* Get the ending index for the scope of the tab-completion */ + +static PyObject * +get_endidx(PyObject *self, PyObject *noarg) +{ + Py_INCREF(endidx); + return endidx; +} + +PyDoc_STRVAR(doc_get_endidx, +"get_endidx() -> int\n\ +get the ending index of the readline tab-completion scope"); + + +/* Set the tab-completion word-delimiters that readline uses */ + +static PyObject * +set_completer_delims(PyObject *self, PyObject *args) +{ + char *break_chars; + + if(!PyArg_ParseTuple(args, "s:set_completer_delims", &break_chars)) { + return NULL; + } + free((void*)rl_completer_word_break_characters); + rl_completer_word_break_characters = strdup(break_chars); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(doc_set_completer_delims, +"set_completer_delims(string) -> None\n\ +set the readline word delimiters for tab-completion"); + + +/* Add a line to the history buffer */ + +static PyObject * +py_add_history(PyObject *self, PyObject *args) +{ + char *line; + + if(!PyArg_ParseTuple(args, "s:add_history", &line)) { + return NULL; + } + add_history(line); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(doc_add_history, +"add_history(string) -> None\n\ +add a line to the history buffer"); + + +/* Get the tab-completion word-delimiters that readline uses */ + +static PyObject * +get_completer_delims(PyObject *self, PyObject *noarg) +{ + return PyString_FromString(rl_completer_word_break_characters); +} + +PyDoc_STRVAR(doc_get_completer_delims, +"get_completer_delims() -> string\n\ +get the readline word delimiters for tab-completion"); + + +/* Set the completer function */ + +static PyObject * +set_completer(PyObject *self, PyObject *args) +{ + return set_hook("completer", &completer, &completer_tstate, args); +} + +PyDoc_STRVAR(doc_set_completer, +"set_completer([function]) -> None\n\ +Set or remove the completer function.\n\ +The function is called as function(text, state),\n\ +for state in 0, 1, 2, ..., until it returns a non-string.\n\ +It should return the next possible completion starting with 'text'."); + + +static PyObject * +get_completer(PyObject *self, PyObject *noargs) +{ + if (completer == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + Py_INCREF(completer); + return completer; +} + +PyDoc_STRVAR(doc_get_completer, +"get_completer() -> function\n\ +\n\ +Returns current completer function."); + +/* Exported function to get any element of history */ + +static PyObject * +get_history_item(PyObject *self, PyObject *args) +{ + int idx = 0; + HIST_ENTRY *hist_ent; + + if (!PyArg_ParseTuple(args, "i:index", &idx)) + return NULL; + if ((hist_ent = history_get(idx))) + return PyString_FromString(hist_ent->line); + else { + Py_INCREF(Py_None); + return Py_None; + } +} + +PyDoc_STRVAR(doc_get_history_item, +"get_history_item() -> string\n\ +return the current contents of history item at index."); + + +/* Exported function to get current length of history */ + +static PyObject * +get_current_history_length(PyObject *self, PyObject *noarg) +{ + HISTORY_STATE *hist_st; + + hist_st = history_get_history_state(); + return PyInt_FromLong(hist_st ? (long) hist_st->length : (long) 0); +} + +PyDoc_STRVAR(doc_get_current_history_length, +"get_current_history_length() -> integer\n\ +return the current (not the maximum) length of history."); + + +/* Exported function to read the current line buffer */ + +static PyObject * +get_line_buffer(PyObject *self, PyObject *noarg) +{ + return PyString_FromString(rl_line_buffer); +} + +PyDoc_STRVAR(doc_get_line_buffer, +"get_line_buffer() -> string\n\ +return the current contents of the line buffer."); + + +/* Exported function to insert text into the line buffer */ + +static PyObject * +insert_text(PyObject *self, PyObject *args) +{ + char *s; + if (!PyArg_ParseTuple(args, "s:insert_text", &s)) + return NULL; + rl_insert_text(s); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(doc_insert_text, +"insert_text(string) -> None\n\ +Insert text into the command line."); + + +/* Redisplay the line buffer */ + +static PyObject * +redisplay(PyObject *self, PyObject *noarg) +{ + rl_redisplay(); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(doc_redisplay, +"redisplay() -> None\n\ +Change what's displayed on the screen to reflect the current\n\ +contents of the line buffer."); + + +/* Table of functions exported by the module */ + +static struct PyMethodDef readline_methods[] = +{ + {"parse_and_bind", parse_and_bind, METH_VARARGS, doc_parse_and_bind}, + {"get_line_buffer", get_line_buffer, METH_NOARGS, doc_get_line_buffer}, + {"insert_text", insert_text, METH_VARARGS, doc_insert_text}, + {"redisplay", redisplay, METH_NOARGS, doc_redisplay}, + {"read_init_file", read_init_file, METH_VARARGS, doc_read_init_file}, + {"read_history_file", read_history_file, + METH_VARARGS, doc_read_history_file}, + {"write_history_file", write_history_file, + METH_VARARGS, doc_write_history_file}, + {"get_history_item", get_history_item, + METH_VARARGS, doc_get_history_item}, + {"get_current_history_length", (PyCFunction)get_current_history_length, + METH_NOARGS, doc_get_current_history_length}, + {"set_history_length", set_history_length, + METH_VARARGS, set_history_length_doc}, + {"get_history_length", get_history_length, + METH_NOARGS, get_history_length_doc}, + {"set_completer", set_completer, METH_VARARGS, doc_set_completer}, + {"get_completer", get_completer, METH_NOARGS, doc_get_completer}, + {"get_begidx", get_begidx, METH_NOARGS, doc_get_begidx}, + {"get_endidx", get_endidx, METH_NOARGS, doc_get_endidx}, + + {"set_completer_delims", set_completer_delims, + METH_VARARGS, doc_set_completer_delims}, + {"add_history", py_add_history, METH_VARARGS, doc_add_history}, + {"get_completer_delims", get_completer_delims, + METH_NOARGS, doc_get_completer_delims}, + + {"set_startup_hook", set_startup_hook, + METH_VARARGS, doc_set_startup_hook}, +#ifdef HAVE_RL_PRE_INPUT_HOOK + {"set_pre_input_hook", set_pre_input_hook, + METH_VARARGS, doc_set_pre_input_hook}, +#endif + {0, 0} +}; + + +/* C function to call the Python hooks. */ + +static int +on_hook(PyObject *func, PyThreadState **tstate) +{ + int result = 0; + if (func != NULL) { + PyObject *r; + /* Note that readline is called with the interpreter + lock released! */ + PyEval_RestoreThread(*tstate); + r = PyObject_CallFunction(func, NULL); + if (r == NULL) + goto error; + if (r == Py_None) + result = 0; + else + result = PyInt_AsLong(r); + Py_DECREF(r); + goto done; + error: + PyErr_Clear(); + Py_XDECREF(r); + done: + *tstate = PyEval_SaveThread(); + } + return result; +} + +static int +on_startup_hook(void) +{ + return on_hook(startup_hook, &startup_hook_tstate); +} + +#ifdef HAVE_RL_PRE_INPUT_HOOK +static int +on_pre_input_hook(void) +{ + return on_hook(pre_input_hook, &pre_input_hook_tstate); +} +#endif + + +/* C function to call the Python completer. */ + +static char * +on_completion(char *text, int state) +{ + char *result = NULL; + if (completer != NULL) { + PyObject *r; + /* Note that readline is called with the interpreter + lock released! */ + PyEval_RestoreThread(completer_tstate); + /* Don't use the default filename completion if we + * have a custom completion function... */ + rl_attempted_completion_over = 1; + r = PyObject_CallFunction(completer, "si", text, state); + if (r == NULL) + goto error; + if (r == Py_None) { + result = NULL; + } + else { + char *s = PyString_AsString(r); + if (s == NULL) + goto error; + result = strdup(s); + } + Py_DECREF(r); + goto done; + error: + PyErr_Clear(); + Py_XDECREF(r); + done: + completer_tstate = PyEval_SaveThread(); + } + return result; +} + + +/* A more flexible constructor that saves the "begidx" and "endidx" + * before calling the normal completer */ + +static char ** +flex_complete(char *text, int start, int end) +{ + Py_XDECREF(begidx); + Py_XDECREF(endidx); + begidx = PyInt_FromLong((long) start); + endidx = PyInt_FromLong((long) end); + return completion_matches(text, *on_completion); +} + + +/* Helper to initialize GNU readline properly. */ + +static void +setup_readline(void) +{ +#ifdef SAVE_LOCALE + char *saved_locale = strdup(setlocale(LC_CTYPE, NULL)); +#endif + + using_history(); + + rl_readline_name = "python"; +#if defined(PYOS_OS2) && defined(PYCC_GCC) + /* Allow $if term= in .inputrc to work */ + rl_terminal_name = getenv("TERM"); +#endif + /* Force rebind of TAB to insert-tab */ + rl_bind_key('\t', rl_insert); + /* Bind both ESC-TAB and ESC-ESC to the completion function */ + rl_bind_key_in_map ('\t', rl_complete, emacs_meta_keymap); + rl_bind_key_in_map ('\033', rl_complete, emacs_meta_keymap); + /* Set our hook functions */ + rl_startup_hook = (Function *)on_startup_hook; +#ifdef HAVE_RL_PRE_INPUT_HOOK + rl_pre_input_hook = (Function *)on_pre_input_hook; +#endif + /* Set our completion function */ + rl_attempted_completion_function = (CPPFunction *)flex_complete; + /* Set Python word break characters */ + rl_completer_word_break_characters = + strdup(" \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?"); + /* All nonalphanums except '.' */ +#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER + rl_completion_append_character ='\0'; +#endif + + begidx = PyInt_FromLong(0L); + endidx = PyInt_FromLong(0L); + /* Initialize (allows .inputrc to override) + * + * XXX: A bug in the readline-2.2 library causes a memory leak + * inside this function. Nothing we can do about it. + */ + rl_initialize(); + +#ifdef SAVE_LOCALE + setlocale(LC_CTYPE, saved_locale); /* Restore locale */ + free(saved_locale); +#endif +} + + +/* Interrupt handler */ + +static jmp_buf jbuf; + +/* ARGSUSED */ +static void +onintr(int sig) +{ + longjmp(jbuf, 1); +} + + +/* Wrapper around GNU readline that handles signals differently. */ + +static char * +call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) +{ + size_t n; + char *p, *q; + PyOS_sighandler_t old_inthandler; + + old_inthandler = PyOS_setsig(SIGINT, onintr); + if (setjmp(jbuf)) { +#ifdef HAVE_SIGRELSE + /* This seems necessary on SunOS 4.1 (Rasmus Hahn) */ + sigrelse(SIGINT); +#endif + PyOS_setsig(SIGINT, old_inthandler); + return NULL; + } + rl_event_hook = PyOS_InputHook; + + if (sys_stdin != rl_instream || sys_stdout != rl_outstream) { + rl_instream = sys_stdin; + rl_outstream = sys_stdout; +#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER + rl_prep_terminal (1); +#endif + } + + p = readline(prompt); + PyOS_setsig(SIGINT, old_inthandler); + + /* We must return a buffer allocated with PyMem_Malloc. */ + if (p == NULL) { + p = PyMem_Malloc(1); + if (p != NULL) + *p = '\0'; + return p; + } + n = strlen(p); + if (n > 0) { + char *line; + HISTORY_STATE *state = history_get_history_state(); + if (state->length > 0) + line = history_get(state->length)->line; + else + line = ""; + if (strcmp(p, line)) + add_history(p); + /* the history docs don't say so, but the address of state + changes each time history_get_history_state is called + which makes me think it's freshly malloc'd memory... + on the other hand, the address of the last line stays the + same as long as history isn't extended, so it appears to + be malloc'd but managed by the history package... */ + free(state); + } + /* Copy the malloc'ed buffer into a PyMem_Malloc'ed one and + release the original. */ + q = p; + p = PyMem_Malloc(n+2); + if (p != NULL) { + strncpy(p, q, n); + p[n] = '\n'; + p[n+1] = '\0'; + } + free(q); + return p; +} + + +/* Initialize the module */ + +PyDoc_STRVAR(doc_module, +"Importing this module enables command line editing using GNU readline."); + +PyMODINIT_FUNC +initreadline(void) +{ + PyObject *m; + + m = Py_InitModule4("readline", readline_methods, doc_module, + (PyObject *)NULL, PYTHON_API_VERSION); + + PyOS_ReadlineFunctionPointer = call_readline; + setup_readline(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexmodule.c new file mode 100644 index 00000000..4938737e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexmodule.c @@ -0,0 +1,689 @@ +/* +XXX support range parameter on search +XXX support mstop parameter on search +*/ + + +/* Regular expression objects */ +/* This uses Tatu Ylonen's copyleft-free reimplementation of + GNU regular expressions */ + +#include "Python.h" + +#include + +#include "regexpr.h" + +static PyObject *RegexError; /* Exception */ + +typedef struct { + PyObject_HEAD + struct re_pattern_buffer re_patbuf; /* The compiled expression */ + struct re_registers re_regs; /* The registers from the last match */ + char re_fastmap[256]; /* Storage for fastmap */ + PyObject *re_translate; /* String object for translate table */ + PyObject *re_lastok; /* String object last matched/searched */ + PyObject *re_groupindex; /* Group name to index dictionary */ + PyObject *re_givenpat; /* Pattern with symbolic groups */ + PyObject *re_realpat; /* Pattern without symbolic groups */ +} regexobject; + +/* Regex object methods */ + +static void +reg_dealloc(regexobject *re) +{ + if (re->re_patbuf.buffer) + free(re->re_patbuf.buffer); + Py_XDECREF(re->re_translate); + Py_XDECREF(re->re_lastok); + Py_XDECREF(re->re_groupindex); + Py_XDECREF(re->re_givenpat); + Py_XDECREF(re->re_realpat); + PyObject_Del(re); +} + +static PyObject * +makeresult(struct re_registers *regs) +{ + PyObject *v; + int i; + static PyObject *filler = NULL; + + if (filler == NULL) { + filler = Py_BuildValue("(ii)", -1, -1); + if (filler == NULL) + return NULL; + } + v = PyTuple_New(RE_NREGS); + if (v == NULL) + return NULL; + + for (i = 0; i < RE_NREGS; i++) { + int lo = regs->start[i]; + int hi = regs->end[i]; + PyObject *w; + if (lo == -1 && hi == -1) { + w = filler; + Py_INCREF(w); + } + else + w = Py_BuildValue("(ii)", lo, hi); + if (w == NULL || PyTuple_SetItem(v, i, w) < 0) { + Py_DECREF(v); + return NULL; + } + } + return v; +} + +static PyObject * +regobj_match(regexobject *re, PyObject *args) +{ + PyObject *argstring; + char *buffer; + int size; + int offset = 0; + int result; + + if (!PyArg_ParseTuple(args, "O|i:match", &argstring, &offset)) + return NULL; + if (!PyArg_Parse(argstring, "t#", &buffer, &size)) + return NULL; + + if (offset < 0 || offset > size) { + PyErr_SetString(RegexError, "match offset out of range"); + return NULL; + } + Py_XDECREF(re->re_lastok); + re->re_lastok = NULL; + result = _Py_re_match(&re->re_patbuf, (unsigned char *)buffer, size, offset, + &re->re_regs); + if (result < -1) { + /* Serious failure of some sort; if re_match didn't + set an exception, raise a generic error */ + if (!PyErr_Occurred()) + PyErr_SetString(RegexError, "match failure"); + return NULL; + } + if (result >= 0) { + Py_INCREF(argstring); + re->re_lastok = argstring; + } + return PyInt_FromLong((long)result); /* Length of the match or -1 */ +} + +static PyObject * +regobj_search(regexobject *re, PyObject *args) +{ + PyObject *argstring; + char *buffer; + int size; + int offset = 0; + int range; + int result; + + if (!PyArg_ParseTuple(args, "O|i:search", &argstring, &offset)) + return NULL; + if (!PyArg_Parse(argstring, "t#:search", &buffer, &size)) + return NULL; + + if (offset < 0 || offset > size) { + PyErr_SetString(RegexError, "search offset out of range"); + return NULL; + } + /* NB: In Emacs 18.57, the documentation for re_search[_2] and + the implementation don't match: the documentation states that + |range| positions are tried, while the code tries |range|+1 + positions. It seems more productive to believe the code! */ + range = size - offset; + Py_XDECREF(re->re_lastok); + re->re_lastok = NULL; + result = _Py_re_search(&re->re_patbuf, (unsigned char *)buffer, size, offset, range, + &re->re_regs); + if (result < -1) { + /* Serious failure of some sort; if re_match didn't + set an exception, raise a generic error */ + if (!PyErr_Occurred()) + PyErr_SetString(RegexError, "match failure"); + return NULL; + } + if (result >= 0) { + Py_INCREF(argstring); + re->re_lastok = argstring; + } + return PyInt_FromLong((long)result); /* Position of the match or -1 */ +} + +/* get the group from the regex where index can be a string (group name) or + an integer index [0 .. 99] + */ +static PyObject* +group_from_index(regexobject *re, PyObject *index) +{ + int i, a, b; + char *v; + + if (PyString_Check(index)) + if (re->re_groupindex == NULL || + !(index = PyDict_GetItem(re->re_groupindex, index))) + { + PyErr_SetString(RegexError, + "group() group name doesn't exist"); + return NULL; + } + + i = PyInt_AsLong(index); + if (i == -1 && PyErr_Occurred()) + return NULL; + + if (i < 0 || i >= RE_NREGS) { + PyErr_SetString(RegexError, "group() index out of range"); + return NULL; + } + if (re->re_lastok == NULL) { + PyErr_SetString(RegexError, + "group() only valid after successful match/search"); + return NULL; + } + a = re->re_regs.start[i]; + b = re->re_regs.end[i]; + if (a < 0 || b < 0) { + Py_INCREF(Py_None); + return Py_None; + } + + if (!(v = PyString_AsString(re->re_lastok))) + return NULL; + + return PyString_FromStringAndSize(v+a, b-a); +} + + +static PyObject * +regobj_group(regexobject *re, PyObject *args) +{ + int n = PyTuple_Size(args); + int i; + PyObject *res = NULL; + + if (n < 0) + return NULL; + if (n == 0) { + PyErr_SetString(PyExc_TypeError, "not enough arguments"); + return NULL; + } + if (n == 1) { + /* return value is a single string */ + PyObject *index = PyTuple_GetItem(args, 0); + if (!index) + return NULL; + + return group_from_index(re, index); + } + + /* return value is a tuple */ + if (!(res = PyTuple_New(n))) + return NULL; + + for (i = 0; i < n; i++) { + PyObject *index = PyTuple_GetItem(args, i); + PyObject *group = NULL; + + if (!index) + goto finally; + if (!(group = group_from_index(re, index))) + goto finally; + if (PyTuple_SetItem(res, i, group) < 0) + goto finally; + } + return res; + + finally: + Py_DECREF(res); + return NULL; +} + + +static struct PyMethodDef reg_methods[] = { + {"match", (PyCFunction)regobj_match, METH_VARARGS}, + {"search", (PyCFunction)regobj_search, METH_VARARGS}, + {"group", (PyCFunction)regobj_group, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + + +static char* members[] = { + "last", "regs", "translate", + "groupindex", "realpat", "givenpat", + NULL +}; + + +static PyObject * +regobj_getattr(regexobject *re, char *name) +{ + if (strcmp(name, "regs") == 0) { + if (re->re_lastok == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return makeresult(&re->re_regs); + } + if (strcmp(name, "last") == 0) { + if (re->re_lastok == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + Py_INCREF(re->re_lastok); + return re->re_lastok; + } + if (strcmp(name, "translate") == 0) { + if (re->re_translate == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + Py_INCREF(re->re_translate); + return re->re_translate; + } + if (strcmp(name, "groupindex") == 0) { + if (re->re_groupindex == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + Py_INCREF(re->re_groupindex); + return re->re_groupindex; + } + if (strcmp(name, "realpat") == 0) { + if (re->re_realpat == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + Py_INCREF(re->re_realpat); + return re->re_realpat; + } + if (strcmp(name, "givenpat") == 0) { + if (re->re_givenpat == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + Py_INCREF(re->re_givenpat); + return re->re_givenpat; + } + if (strcmp(name, "__members__") == 0) { + int i = 0; + PyObject *list = NULL; + + /* okay, so it's unlikely this list will change that often. + still, it's easier to change it in just one place. + */ + while (members[i]) + i++; + if (!(list = PyList_New(i))) + return NULL; + + i = 0; + while (members[i]) { + PyObject* v = PyString_FromString(members[i]); + if (!v || PyList_SetItem(list, i, v) < 0) { + Py_DECREF(list); + return NULL; + } + i++; + } + return list; + } + return Py_FindMethod(reg_methods, (PyObject *)re, name); +} + +static PyTypeObject Regextype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "regex.regex", /*tp_name*/ + sizeof(regexobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)reg_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)regobj_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + +/* reference counting invariants: + pattern: borrowed + translate: borrowed + givenpat: borrowed + groupindex: transferred +*/ +static PyObject * +newregexobject(PyObject *pattern, PyObject *translate, PyObject *givenpat, PyObject *groupindex) +{ + regexobject *re; + char *pat; + int size; + + if (!PyArg_Parse(pattern, "t#", &pat, &size)) + return NULL; + + if (translate != NULL && PyString_Size(translate) != 256) { + PyErr_SetString(RegexError, + "translation table must be 256 bytes"); + return NULL; + } + re = PyObject_New(regexobject, &Regextype); + if (re != NULL) { + char *error; + re->re_patbuf.buffer = NULL; + re->re_patbuf.allocated = 0; + re->re_patbuf.fastmap = (unsigned char *)re->re_fastmap; + if (translate) { + re->re_patbuf.translate = (unsigned char *)PyString_AsString(translate); + if (!re->re_patbuf.translate) + goto finally; + Py_INCREF(translate); + } + else + re->re_patbuf.translate = NULL; + re->re_translate = translate; + re->re_lastok = NULL; + re->re_groupindex = groupindex; + Py_INCREF(pattern); + re->re_realpat = pattern; + Py_INCREF(givenpat); + re->re_givenpat = givenpat; + error = _Py_re_compile_pattern((unsigned char *)pat, size, &re->re_patbuf); + if (error != NULL) { + PyErr_SetString(RegexError, error); + goto finally; + } + } + return (PyObject *)re; + finally: + Py_DECREF(re); + return NULL; +} + +static PyObject * +regex_compile(PyObject *self, PyObject *args) +{ + PyObject *pat = NULL; + PyObject *tran = NULL; + + if (!PyArg_ParseTuple(args, "S|S:compile", &pat, &tran)) + return NULL; + return newregexobject(pat, tran, pat, NULL); +} + +static PyObject * +symcomp(PyObject *pattern, PyObject *gdict) +{ + char *opat, *oend, *o, *n, *g, *v; + int group_count = 0; + int sz; + int escaped = 0; + char name_buf[128]; + PyObject *npattern; + int require_escape = re_syntax & RE_NO_BK_PARENS ? 0 : 1; + + if (!(opat = PyString_AsString(pattern))) + return NULL; + + if ((sz = PyString_Size(pattern)) < 0) + return NULL; + + oend = opat + sz; + o = opat; + + if (oend == opat) { + Py_INCREF(pattern); + return pattern; + } + + if (!(npattern = PyString_FromStringAndSize((char*)NULL, sz)) || + !(n = PyString_AsString(npattern))) + return NULL; + + while (o < oend) { + if (*o == '(' && escaped == require_escape) { + char *backtrack; + escaped = 0; + ++group_count; + *n++ = *o; + if (++o >= oend || *o != '<') + continue; + /* *o == '<' */ + if (o+1 < oend && *(o+1) == '>') + continue; + backtrack = o; + g = name_buf; + for (++o; o < oend;) { + if (*o == '>') { + PyObject *group_name = NULL; + PyObject *group_index = NULL; + *g++ = '\0'; + group_name = PyString_FromString(name_buf); + group_index = PyInt_FromLong(group_count); + if (group_name == NULL || + group_index == NULL || + PyDict_SetItem(gdict, group_name, + group_index) != 0) + { + Py_XDECREF(group_name); + Py_XDECREF(group_index); + Py_XDECREF(npattern); + return NULL; + } + Py_DECREF(group_name); + Py_DECREF(group_index); + ++o; /* eat the '>' */ + break; + } + if (!isalnum(Py_CHARMASK(*o)) && *o != '_') { + o = backtrack; + break; + } + *g++ = *o++; + } + } + else if (*o == '[' && !escaped) { + *n++ = *o; + ++o; /* eat the char following '[' */ + *n++ = *o; + while (o < oend && *o != ']') { + ++o; + *n++ = *o; + } + if (o < oend) + ++o; + } + else if (*o == '\\') { + escaped = 1; + *n++ = *o; + ++o; + } + else { + escaped = 0; + *n++ = *o; + ++o; + } + } + + if (!(v = PyString_AsString(npattern))) { + Py_DECREF(npattern); + return NULL; + } + /* _PyString_Resize() decrements npattern on failure */ + _PyString_Resize(&npattern, n - v); + return npattern; + +} + +static PyObject * +regex_symcomp(PyObject *self, PyObject *args) +{ + PyObject *pattern; + PyObject *tran = NULL; + PyObject *gdict = NULL; + PyObject *npattern; + PyObject *retval = NULL; + + if (!PyArg_ParseTuple(args, "S|S:symcomp", &pattern, &tran)) + return NULL; + + gdict = PyDict_New(); + if (gdict == NULL || (npattern = symcomp(pattern, gdict)) == NULL) { + Py_DECREF(gdict); + Py_DECREF(pattern); + return NULL; + } + retval = newregexobject(npattern, tran, pattern, gdict); + Py_DECREF(npattern); + return retval; +} + + +static PyObject *cache_pat; +static PyObject *cache_prog; + +static int +update_cache(PyObject *pat) +{ + PyObject *tuple = Py_BuildValue("(O)", pat); + int status = 0; + + if (!tuple) + return -1; + + if (pat != cache_pat) { + Py_XDECREF(cache_pat); + cache_pat = NULL; + Py_XDECREF(cache_prog); + cache_prog = regex_compile((PyObject *)NULL, tuple); + if (cache_prog == NULL) { + status = -1; + goto finally; + } + cache_pat = pat; + Py_INCREF(cache_pat); + } + finally: + Py_DECREF(tuple); + return status; +} + +static PyObject * +regex_match(PyObject *self, PyObject *args) +{ + PyObject *pat, *string; + PyObject *tuple, *v; + + if (!PyArg_ParseTuple(args, "SS:match", &pat, &string)) + return NULL; + if (update_cache(pat) < 0) + return NULL; + + if (!(tuple = Py_BuildValue("(S)", string))) + return NULL; + v = regobj_match((regexobject *)cache_prog, tuple); + Py_DECREF(tuple); + return v; +} + +static PyObject * +regex_search(PyObject *self, PyObject *args) +{ + PyObject *pat, *string; + PyObject *tuple, *v; + + if (!PyArg_ParseTuple(args, "SS:search", &pat, &string)) + return NULL; + if (update_cache(pat) < 0) + return NULL; + + if (!(tuple = Py_BuildValue("(S)", string))) + return NULL; + v = regobj_search((regexobject *)cache_prog, tuple); + Py_DECREF(tuple); + return v; +} + +static PyObject * +regex_set_syntax(PyObject *self, PyObject *args) +{ + int syntax; + if (!PyArg_ParseTuple(args, "i:set_syntax", &syntax)) + return NULL; + syntax = re_set_syntax(syntax); + /* wipe the global pattern cache */ + Py_XDECREF(cache_pat); + cache_pat = NULL; + Py_XDECREF(cache_prog); + cache_prog = NULL; + return PyInt_FromLong((long)syntax); +} + +static PyObject * +regex_get_syntax(PyObject *self) +{ + return PyInt_FromLong((long)re_syntax); +} + + +static struct PyMethodDef regex_global_methods[] = { + {"compile", regex_compile, METH_VARARGS}, + {"symcomp", regex_symcomp, METH_VARARGS}, + {"match", regex_match, METH_VARARGS}, + {"search", regex_search, METH_VARARGS}, + {"set_syntax", regex_set_syntax, METH_VARARGS}, + {"get_syntax", (PyCFunction)regex_get_syntax, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +PyMODINIT_FUNC +initregex(void) +{ + PyObject *m, *d, *v; + int i; + char *s; + + /* Initialize object type */ + Regextype.ob_type = &PyType_Type; + + m = Py_InitModule("regex", regex_global_methods); + d = PyModule_GetDict(m); + + if (PyErr_Warn(PyExc_DeprecationWarning, + "the regex module is deprecated; " + "please use the re module") < 0) + return; + + /* Initialize regex.error exception */ + v = RegexError = PyErr_NewException("regex.error", NULL, NULL); + if (v == NULL || PyDict_SetItemString(d, "error", v) != 0) + goto finally; + + /* Initialize regex.casefold constant */ + if (!(v = PyString_FromStringAndSize((char *)NULL, 256))) + goto finally; + + if (!(s = PyString_AsString(v))) + goto finally; + + for (i = 0; i < 256; i++) { + if (isupper(i)) + s[i] = tolower(i); + else + s[i] = i; + } + if (PyDict_SetItemString(d, "casefold", v) < 0) + goto finally; + Py_DECREF(v); + + if (!PyErr_Occurred()) + return; + finally: + /* Nothing */ ; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexpr.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexpr.c new file mode 100644 index 00000000..6a9b67a9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexpr.c @@ -0,0 +1,2094 @@ +/* regexpr.c + * + * Author: Tatu Ylonen + * + * Copyright (c) 1991 Tatu Ylonen, Espoo, Finland + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies. + * This software is provided "as is" without express or implied + * warranty. + * + * Created: Thu Sep 26 17:14:05 1991 ylo + * Last modified: Mon Nov 4 17:06:48 1991 ylo + * Ported to Think C: 19 Jan 1992 guido@cwi.nl + * + * This code draws many ideas from the regular expression packages by + * Henry Spencer of the University of Toronto and Richard Stallman of + * the Free Software Foundation. + * + * Emacs-specific code and syntax table code is almost directly borrowed + * from GNU regexp. + * + * Bugs fixed and lots of reorganization by Jeffrey C. Ollie, April + * 1997 Thanks for bug reports and ideas from Andrew Kuchling, Tim + * Peters, Guido van Rossum, Ka-Ping Yee, Sjoerd Mullender, and + * probably one or two others that I'm forgetting. + * + * $Id: regexpr.c,v 1.36 2002/08/07 16:21:51 loewis Exp $ */ + +#include "Python.h" +#include "regexpr.h" + +/* The original code blithely assumed that sizeof(short) == 2. Not + * always true. Original instances of "(short)x" were replaced by + * SHORT(x), where SHORT is #defined below. */ + +#define SHORT(x) ((x) & 0x8000 ? (x) - 0x10000 : (x)) + +/* The stack implementation is taken from an idea by Andrew Kuchling. + * It's a doubly linked list of arrays. The advantages of this over a + * simple linked list are that the number of mallocs required are + * reduced. It also makes it possible to statically allocate enough + * space so that small patterns don't ever need to call malloc. + * + * The advantages over a single array is that is periodically + * realloced when more space is needed is that we avoid ever copying + * the stack. */ + +/* item_t is the basic stack element. Defined as a union of + * structures so that both registers, failure points, and counters can + * be pushed/popped from the stack. There's nothing built into the + * item to keep track of whether a certain stack item is a register, a + * failure point, or a counter. */ + +typedef union item_t +{ + struct + { + int num; + int level; + unsigned char *start; + unsigned char *end; + } reg; + struct + { + int count; + int level; + int phantom; + unsigned char *code; + unsigned char *text; + } fail; + struct + { + int num; + int level; + int count; + } cntr; +} item_t; + +#define STACK_PAGE_SIZE 256 +#define NUM_REGISTERS 256 + +/* A 'page' of stack items. */ + +typedef struct item_page_t +{ + item_t items[STACK_PAGE_SIZE]; + struct item_page_t *prev; + struct item_page_t *next; +} item_page_t; + + +typedef struct match_state +{ + /* The number of registers that have been pushed onto the stack + * since the last failure point. */ + + int count; + + /* Used to control when registers need to be pushed onto the + * stack. */ + + int level; + + /* The number of failure points on the stack. */ + + int point; + + /* Storage for the registers. Each register consists of two + * pointers to characters. So register N is represented as + * start[N] and end[N]. The pointers must be converted to + * offsets from the beginning of the string before returning the + * registers to the calling program. */ + + unsigned char *start[NUM_REGISTERS]; + unsigned char *end[NUM_REGISTERS]; + + /* Keeps track of whether a register has changed recently. */ + + int changed[NUM_REGISTERS]; + + /* Structure to encapsulate the stack. */ + struct + { + /* index into the current page. If index == 0 and you need + * to pop an item, move to the previous page and set index + * = STACK_PAGE_SIZE - 1. Otherwise decrement index to + * push a page. If index == STACK_PAGE_SIZE and you need + * to push a page move to the next page and set index = + * 0. If there is no new next page, allocate a new page + * and link it in. Otherwise, increment index to push a + * page. */ + + int index; + item_page_t *current; /* Pointer to the current page. */ + item_page_t first; /* First page is statically allocated. */ + } stack; +} match_state; + +/* Initialize a state object */ + +/* #define NEW_STATE(state) \ */ +/* memset(&state, 0, (void *)(&state.stack) - (void *)(&state)); \ */ +/* state.stack.current = &state.stack.first; \ */ +/* state.stack.first.prev = NULL; \ */ +/* state.stack.first.next = NULL; \ */ +/* state.stack.index = 0; \ */ +/* state.level = 1 */ + +#define NEW_STATE(state, nregs) \ +{ \ + int i; \ + for (i = 0; i < nregs; i++) \ + { \ + state.start[i] = NULL; \ + state.end[i] = NULL; \ + state.changed[i] = 0; \ + } \ + state.stack.current = &state.stack.first; \ + state.stack.first.prev = NULL; \ + state.stack.first.next = NULL; \ + state.stack.index = 0; \ + state.level = 1; \ + state.count = 0; \ + state.level = 0; \ + state.point = 0; \ +} + +/* Free any memory that might have been malloc'd */ + +#define FREE_STATE(state) \ +while(state.stack.first.next != NULL) \ +{ \ + state.stack.current = state.stack.first.next; \ + state.stack.first.next = state.stack.current->next; \ + free(state.stack.current); \ +} + +/* Discard the top 'count' stack items. */ + +#define STACK_DISCARD(stack, count, on_error) \ +stack.index -= count; \ +while (stack.index < 0) \ +{ \ + if (stack.current->prev == NULL) \ + on_error; \ + stack.current = stack.current->prev; \ + stack.index += STACK_PAGE_SIZE; \ +} + +/* Store a pointer to the previous item on the stack. Used to pop an + * item off of the stack. */ + +#define STACK_PREV(stack, top, on_error) \ +if (stack.index == 0) \ +{ \ + if (stack.current->prev == NULL) \ + on_error; \ + stack.current = stack.current->prev; \ + stack.index = STACK_PAGE_SIZE - 1; \ +} \ +else \ +{ \ + stack.index--; \ +} \ +top = &(stack.current->items[stack.index]) + +/* Store a pointer to the next item on the stack. Used to push an item + * on to the stack. */ + +#define STACK_NEXT(stack, top, on_error) \ +if (stack.index == STACK_PAGE_SIZE) \ +{ \ + if (stack.current->next == NULL) \ + { \ + stack.current->next = (item_page_t *)malloc(sizeof(item_page_t)); \ + if (stack.current->next == NULL) \ + on_error; \ + stack.current->next->prev = stack.current; \ + stack.current->next->next = NULL; \ + } \ + stack.current = stack.current->next; \ + stack.index = 0; \ +} \ +top = &(stack.current->items[stack.index++]) + +/* Store a pointer to the item that is 'count' items back in the + * stack. STACK_BACK(stack, top, 1, on_error) is equivalent to + * STACK_TOP(stack, top, on_error). */ + +#define STACK_BACK(stack, top, count, on_error) \ +{ \ + int index; \ + item_page_t *current; \ + current = stack.current; \ + index = stack.index - (count); \ + while (index < 0) \ + { \ + if (current->prev == NULL) \ + on_error; \ + current = current->prev; \ + index += STACK_PAGE_SIZE; \ + } \ + top = &(current->items[index]); \ +} + +/* Store a pointer to the top item on the stack. Execute the + * 'on_error' code if there are no items on the stack. */ + +#define STACK_TOP(stack, top, on_error) \ +if (stack.index == 0) \ +{ \ + if (stack.current->prev == NULL) \ + on_error; \ + top = &(stack.current->prev->items[STACK_PAGE_SIZE - 1]); \ +} \ +else \ +{ \ + top = &(stack.current->items[stack.index - 1]); \ +} + +/* Test to see if the stack is empty */ + +#define STACK_EMPTY(stack) ((stack.index == 0) && \ + (stack.current->prev == NULL)) + +/* Return the start of register 'reg' */ + +#define GET_REG_START(state, reg) (state.start[reg]) + +/* Return the end of register 'reg' */ + +#define GET_REG_END(state, reg) (state.end[reg]) + +/* Set the start of register 'reg'. If the state of the register needs + * saving, push it on the stack. */ + +#define SET_REG_START(state, reg, text, on_error) \ +if(state.changed[reg] < state.level) \ +{ \ + item_t *item; \ + STACK_NEXT(state.stack, item, on_error); \ + item->reg.num = reg; \ + item->reg.start = state.start[reg]; \ + item->reg.end = state.end[reg]; \ + item->reg.level = state.changed[reg]; \ + state.changed[reg] = state.level; \ + state.count++; \ +} \ +state.start[reg] = text + +/* Set the end of register 'reg'. If the state of the register needs + * saving, push it on the stack. */ + +#define SET_REG_END(state, reg, text, on_error) \ +if(state.changed[reg] < state.level) \ +{ \ + item_t *item; \ + STACK_NEXT(state.stack, item, on_error); \ + item->reg.num = reg; \ + item->reg.start = state.start[reg]; \ + item->reg.end = state.end[reg]; \ + item->reg.level = state.changed[reg]; \ + state.changed[reg] = state.level; \ + state.count++; \ +} \ +state.end[reg] = text + +#define PUSH_FAILURE(state, xcode, xtext, on_error) \ +{ \ + item_t *item; \ + STACK_NEXT(state.stack, item, on_error); \ + item->fail.code = xcode; \ + item->fail.text = xtext; \ + item->fail.count = state.count; \ + item->fail.level = state.level; \ + item->fail.phantom = 0; \ + state.count = 0; \ + state.level++; \ + state.point++; \ +} + +/* Update the last failure point with a new position in the text. */ + +#define UPDATE_FAILURE(state, xtext, on_error) \ +{ \ + item_t *item; \ + STACK_BACK(state.stack, item, state.count + 1, on_error); \ + if (!item->fail.phantom) \ + { \ + item_t *item2; \ + STACK_NEXT(state.stack, item2, on_error); \ + item2->fail.code = item->fail.code; \ + item2->fail.text = xtext; \ + item2->fail.count = state.count; \ + item2->fail.level = state.level; \ + item2->fail.phantom = 1; \ + state.count = 0; \ + state.level++; \ + state.point++; \ + } \ + else \ + { \ + STACK_DISCARD(state.stack, state.count, on_error); \ + STACK_TOP(state.stack, item, on_error); \ + item->fail.text = xtext; \ + state.count = 0; \ + state.level++; \ + } \ +} + +#define POP_FAILURE(state, xcode, xtext, on_empty, on_error) \ +{ \ + item_t *item; \ + do \ + { \ + while(state.count > 0) \ + { \ + STACK_PREV(state.stack, item, on_error); \ + state.start[item->reg.num] = item->reg.start; \ + state.end[item->reg.num] = item->reg.end; \ + state.changed[item->reg.num] = item->reg.level; \ + state.count--; \ + } \ + STACK_PREV(state.stack, item, on_empty); \ + xcode = item->fail.code; \ + xtext = item->fail.text; \ + state.count = item->fail.count; \ + state.level = item->fail.level; \ + state.point--; \ + } \ + while (item->fail.text == NULL); \ +} + +enum regexp_compiled_ops /* opcodes for compiled regexp */ +{ + Cend, /* end of pattern reached */ + Cbol, /* beginning of line */ + Ceol, /* end of line */ + Cset, /* character set. Followed by 32 bytes of set. */ + Cexact, /* followed by a byte to match */ + Canychar, /* matches any character except newline */ + Cstart_memory, /* set register start addr (followed by reg number) */ + Cend_memory, /* set register end addr (followed by reg number) */ + Cmatch_memory, /* match a duplicate of reg contents (regnum follows)*/ + Cjump, /* followed by two bytes (lsb,msb) of displacement. */ + Cstar_jump, /* will change to jump/update_failure_jump at runtime */ + Cfailure_jump, /* jump to addr on failure */ + Cupdate_failure_jump, /* update topmost failure point and jump */ + Cdummy_failure_jump, /* push a dummy failure point and jump */ + Cbegbuf, /* match at beginning of buffer */ + Cendbuf, /* match at end of buffer */ + Cwordbeg, /* match at beginning of word */ + Cwordend, /* match at end of word */ + Cwordbound, /* match if at word boundary */ + Cnotwordbound, /* match if not at word boundary */ + Csyntaxspec, /* matches syntax code (1 byte follows) */ + Cnotsyntaxspec, /* matches if syntax code does not match (1 byte follows) */ + Crepeat1 +}; + +enum regexp_syntax_op /* syntax codes for plain and quoted characters */ +{ + Rend, /* special code for end of regexp */ + Rnormal, /* normal character */ + Ranychar, /* any character except newline */ + Rquote, /* the quote character */ + Rbol, /* match beginning of line */ + Reol, /* match end of line */ + Roptional, /* match preceding expression optionally */ + Rstar, /* match preceding expr zero or more times */ + Rplus, /* match preceding expr one or more times */ + Ror, /* match either of alternatives */ + Ropenpar, /* opening parenthesis */ + Rclosepar, /* closing parenthesis */ + Rmemory, /* match memory register */ + Rextended_memory, /* \vnn to match registers 10-99 */ + Ropenset, /* open set. Internal syntax hard-coded below. */ + /* the following are gnu extensions to "normal" regexp syntax */ + Rbegbuf, /* beginning of buffer */ + Rendbuf, /* end of buffer */ + Rwordchar, /* word character */ + Rnotwordchar, /* not word character */ + Rwordbeg, /* beginning of word */ + Rwordend, /* end of word */ + Rwordbound, /* word bound */ + Rnotwordbound, /* not word bound */ + Rnum_ops +}; + +static int re_compile_initialized = 0; +static int regexp_syntax = 0; +int re_syntax = 0; /* Exported copy of regexp_syntax */ +static unsigned char regexp_plain_ops[256]; +static unsigned char regexp_quoted_ops[256]; +static unsigned char regexp_precedences[Rnum_ops]; +static int regexp_context_indep_ops; +static int regexp_ansi_sequences; + +#define NUM_LEVELS 5 /* number of precedence levels in use */ +#define MAX_NESTING 100 /* max nesting level of operators */ + +#define SYNTAX(ch) re_syntax_table[(unsigned char)(ch)] + +unsigned char re_syntax_table[256]; + +void re_compile_initialize(void) +{ + int a; + + static int syntax_table_inited = 0; + + if (!syntax_table_inited) + { + syntax_table_inited = 1; + memset(re_syntax_table, 0, 256); + for (a = 'a'; a <= 'z'; a++) + re_syntax_table[a] = Sword; + for (a = 'A'; a <= 'Z'; a++) + re_syntax_table[a] = Sword; + for (a = '0'; a <= '9'; a++) + re_syntax_table[a] = Sword | Sdigit | Shexdigit; + for (a = '0'; a <= '7'; a++) + re_syntax_table[a] |= Soctaldigit; + for (a = 'A'; a <= 'F'; a++) + re_syntax_table[a] |= Shexdigit; + for (a = 'a'; a <= 'f'; a++) + re_syntax_table[a] |= Shexdigit; + re_syntax_table['_'] = Sword; + for (a = 9; a <= 13; a++) + re_syntax_table[a] = Swhitespace; + re_syntax_table[' '] = Swhitespace; + } + re_compile_initialized = 1; + for (a = 0; a < 256; a++) + { + regexp_plain_ops[a] = Rnormal; + regexp_quoted_ops[a] = Rnormal; + } + for (a = '0'; a <= '9'; a++) + regexp_quoted_ops[a] = Rmemory; + regexp_plain_ops['\134'] = Rquote; + if (regexp_syntax & RE_NO_BK_PARENS) + { + regexp_plain_ops['('] = Ropenpar; + regexp_plain_ops[')'] = Rclosepar; + } + else + { + regexp_quoted_ops['('] = Ropenpar; + regexp_quoted_ops[')'] = Rclosepar; + } + if (regexp_syntax & RE_NO_BK_VBAR) + regexp_plain_ops['\174'] = Ror; + else + regexp_quoted_ops['\174'] = Ror; + regexp_plain_ops['*'] = Rstar; + if (regexp_syntax & RE_BK_PLUS_QM) + { + regexp_quoted_ops['+'] = Rplus; + regexp_quoted_ops['?'] = Roptional; + } + else + { + regexp_plain_ops['+'] = Rplus; + regexp_plain_ops['?'] = Roptional; + } + if (regexp_syntax & RE_NEWLINE_OR) + regexp_plain_ops['\n'] = Ror; + regexp_plain_ops['\133'] = Ropenset; + regexp_plain_ops['\136'] = Rbol; + regexp_plain_ops['$'] = Reol; + regexp_plain_ops['.'] = Ranychar; + if (!(regexp_syntax & RE_NO_GNU_EXTENSIONS)) + { + regexp_quoted_ops['w'] = Rwordchar; + regexp_quoted_ops['W'] = Rnotwordchar; + regexp_quoted_ops['<'] = Rwordbeg; + regexp_quoted_ops['>'] = Rwordend; + regexp_quoted_ops['b'] = Rwordbound; + regexp_quoted_ops['B'] = Rnotwordbound; + regexp_quoted_ops['`'] = Rbegbuf; + regexp_quoted_ops['\''] = Rendbuf; + } + if (regexp_syntax & RE_ANSI_HEX) + regexp_quoted_ops['v'] = Rextended_memory; + for (a = 0; a < Rnum_ops; a++) + regexp_precedences[a] = 4; + if (regexp_syntax & RE_TIGHT_VBAR) + { + regexp_precedences[Ror] = 3; + regexp_precedences[Rbol] = 2; + regexp_precedences[Reol] = 2; + } + else + { + regexp_precedences[Ror] = 2; + regexp_precedences[Rbol] = 3; + regexp_precedences[Reol] = 3; + } + regexp_precedences[Rclosepar] = 1; + regexp_precedences[Rend] = 0; + regexp_context_indep_ops = (regexp_syntax & RE_CONTEXT_INDEP_OPS) != 0; + regexp_ansi_sequences = (regexp_syntax & RE_ANSI_HEX) != 0; +} + +int re_set_syntax(int syntax) +{ + int ret; + + ret = regexp_syntax; + regexp_syntax = syntax; + re_syntax = syntax; /* Exported copy */ + re_compile_initialize(); + return ret; +} + +static int hex_char_to_decimal(int ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + return 16; +} + +static void re_compile_fastmap_aux(unsigned char *code, int pos, + unsigned char *visited, + unsigned char *can_be_null, + unsigned char *fastmap) +{ + int a; + int b; + int syntaxcode; + + if (visited[pos]) + return; /* we have already been here */ + visited[pos] = 1; + for (;;) + switch (code[pos++]) { + case Cend: + { + *can_be_null = 1; + return; + } + case Cbol: + case Cbegbuf: + case Cendbuf: + case Cwordbeg: + case Cwordend: + case Cwordbound: + case Cnotwordbound: + { + for (a = 0; a < 256; a++) + fastmap[a] = 1; + break; + } + case Csyntaxspec: + { + syntaxcode = code[pos++]; + for (a = 0; a < 256; a++) + if (SYNTAX(a) & syntaxcode) + fastmap[a] = 1; + return; + } + case Cnotsyntaxspec: + { + syntaxcode = code[pos++]; + for (a = 0; a < 256; a++) + if (!(SYNTAX(a) & syntaxcode) ) + fastmap[a] = 1; + return; + } + case Ceol: + { + fastmap['\n'] = 1; + if (*can_be_null == 0) + *can_be_null = 2; /* can match null, but only at end of buffer*/ + return; + } + case Cset: + { + for (a = 0; a < 256/8; a++) + if (code[pos + a] != 0) + for (b = 0; b < 8; b++) + if (code[pos + a] & (1 << b)) + fastmap[(a << 3) + b] = 1; + pos += 256/8; + return; + } + case Cexact: + { + fastmap[(unsigned char)code[pos]] = 1; + return; + } + case Canychar: + { + for (a = 0; a < 256; a++) + if (a != '\n') + fastmap[a] = 1; + return; + } + case Cstart_memory: + case Cend_memory: + { + pos++; + break; + } + case Cmatch_memory: + { + for (a = 0; a < 256; a++) + fastmap[a] = 1; + *can_be_null = 1; + return; + } + case Cjump: + case Cdummy_failure_jump: + case Cupdate_failure_jump: + case Cstar_jump: + { + a = (unsigned char)code[pos++]; + a |= (unsigned char)code[pos++] << 8; + pos += (int)SHORT(a); + if (visited[pos]) + { + /* argh... the regexp contains empty loops. This is not + good, as this may cause a failure stack overflow when + matching. Oh well. */ + /* this path leads nowhere; pursue other paths. */ + return; + } + visited[pos] = 1; + break; + } + case Cfailure_jump: + { + a = (unsigned char)code[pos++]; + a |= (unsigned char)code[pos++] << 8; + a = pos + (int)SHORT(a); + re_compile_fastmap_aux(code, a, visited, can_be_null, fastmap); + break; + } + case Crepeat1: + { + pos += 2; + break; + } + default: + { + PyErr_SetString(PyExc_SystemError, "Unknown regex opcode: memory corrupted?"); + return; + /*NOTREACHED*/ + } + } +} + +static int re_do_compile_fastmap(unsigned char *buffer, int used, int pos, + unsigned char *can_be_null, + unsigned char *fastmap) +{ + unsigned char small_visited[512], *visited; + + if (used <= sizeof(small_visited)) + visited = small_visited; + else + { + visited = malloc(used); + if (!visited) + return 0; + } + *can_be_null = 0; + memset(fastmap, 0, 256); + memset(visited, 0, used); + re_compile_fastmap_aux(buffer, pos, visited, can_be_null, fastmap); + if (visited != small_visited) + free(visited); + return 1; +} + +void re_compile_fastmap(regexp_t bufp) +{ + if (!bufp->fastmap || bufp->fastmap_accurate) + return; + assert(bufp->used > 0); + if (!re_do_compile_fastmap(bufp->buffer, + bufp->used, + 0, + &bufp->can_be_null, + bufp->fastmap)) + return; + if (PyErr_Occurred()) return; + if (bufp->buffer[0] == Cbol) + bufp->anchor = 1; /* begline */ + else + if (bufp->buffer[0] == Cbegbuf) + bufp->anchor = 2; /* begbuf */ + else + bufp->anchor = 0; /* none */ + bufp->fastmap_accurate = 1; +} + +/* + * star is coded as: + * 1: failure_jump 2 + * ... code for operand of star + * star_jump 1 + * 2: ... code after star + * + * We change the star_jump to update_failure_jump if we can determine + * that it is safe to do so; otherwise we change it to an ordinary + * jump. + * + * plus is coded as + * + * jump 2 + * 1: failure_jump 3 + * 2: ... code for operand of plus + * star_jump 1 + * 3: ... code after plus + * + * For star_jump considerations this is processed identically to star. + * + */ + +static int re_optimize_star_jump(regexp_t bufp, unsigned char *code) +{ + unsigned char map[256]; + unsigned char can_be_null; + unsigned char *p1; + unsigned char *p2; + unsigned char ch; + int a; + int b; + int num_instructions = 0; + + a = (unsigned char)*code++; + a |= (unsigned char)*code++ << 8; + a = (int)SHORT(a); + + p1 = code + a + 3; /* skip the failure_jump */ + /* Check that the jump is within the pattern */ + if (p1buffer || bufp->buffer+bufp->usedbuffer, bufp->used, + (int)(p2 - bufp->buffer), + &can_be_null, map)) + goto make_normal_jump; + + /* If we might introduce a new update point inside the + * loop, we can't optimize because then update_jump would + * update a wrong failure point. Thus we have to be + * quite careful here. + */ + + /* loop until we find something that consumes a character */ + loop_p1: + num_instructions++; + switch (*p1++) + { + case Cbol: + case Ceol: + case Cbegbuf: + case Cendbuf: + case Cwordbeg: + case Cwordend: + case Cwordbound: + case Cnotwordbound: + { + goto loop_p1; + } + case Cstart_memory: + case Cend_memory: + { + p1++; + goto loop_p1; + } + case Cexact: + { + ch = (unsigned char)*p1++; + if (map[(int)ch]) + goto make_normal_jump; + break; + } + case Canychar: + { + for (b = 0; b < 256; b++) + if (b != '\n' && map[b]) + goto make_normal_jump; + break; + } + case Cset: + { + for (b = 0; b < 256; b++) + if ((p1[b >> 3] & (1 << (b & 7))) && map[b]) + goto make_normal_jump; + p1 += 256/8; + break; + } + default: + { + goto make_normal_jump; + } + } + /* now we know that we can't backtrack. */ + while (p1 != p2 - 3) + { + num_instructions++; + switch (*p1++) + { + case Cend: + { + return 0; + } + case Cbol: + case Ceol: + case Canychar: + case Cbegbuf: + case Cendbuf: + case Cwordbeg: + case Cwordend: + case Cwordbound: + case Cnotwordbound: + { + break; + } + case Cset: + { + p1 += 256/8; + break; + } + case Cexact: + case Cstart_memory: + case Cend_memory: + case Cmatch_memory: + case Csyntaxspec: + case Cnotsyntaxspec: + { + p1++; + break; + } + case Cjump: + case Cstar_jump: + case Cfailure_jump: + case Cupdate_failure_jump: + case Cdummy_failure_jump: + { + goto make_normal_jump; + } + default: + { + return 0; + } + } + } + + /* make_update_jump: */ + code -= 3; + a += 3; /* jump to after the Cfailure_jump */ + code[0] = Cupdate_failure_jump; + code[1] = a & 0xff; + code[2] = a >> 8; + if (num_instructions > 1) + return 1; + assert(num_instructions == 1); + /* if the only instruction matches a single character, we can do + * better */ + p1 = code + 3 + a; /* start of sole instruction */ + if (*p1 == Cset || *p1 == Cexact || *p1 == Canychar || + *p1 == Csyntaxspec || *p1 == Cnotsyntaxspec) + code[0] = Crepeat1; + return 1; + + make_normal_jump: + code -= 3; + *code = Cjump; + return 1; +} + +static int re_optimize(regexp_t bufp) +{ + unsigned char *code; + + code = bufp->buffer; + + while(1) + { + switch (*code++) + { + case Cend: + { + return 1; + } + case Canychar: + case Cbol: + case Ceol: + case Cbegbuf: + case Cendbuf: + case Cwordbeg: + case Cwordend: + case Cwordbound: + case Cnotwordbound: + { + break; + } + case Cset: + { + code += 256/8; + break; + } + case Cexact: + case Cstart_memory: + case Cend_memory: + case Cmatch_memory: + case Csyntaxspec: + case Cnotsyntaxspec: + { + code++; + break; + } + case Cstar_jump: + { + if (!re_optimize_star_jump(bufp, code)) + { + return 0; + } + /* fall through */ + } + case Cupdate_failure_jump: + case Cjump: + case Cdummy_failure_jump: + case Cfailure_jump: + case Crepeat1: + { + code += 2; + break; + } + default: + { + return 0; + } + } + } +} + +#define NEXTCHAR(var) \ +{ \ + if (pos >= size) \ + goto ends_prematurely; \ + (var) = regex[pos]; \ + pos++; \ +} + +#define ALLOC(amount) \ +{ \ + if (pattern_offset+(amount) > alloc) \ + { \ + alloc += 256 + (amount); \ + pattern = realloc(pattern, alloc); \ + if (!pattern) \ + goto out_of_memory; \ + } \ +} + +#define STORE(ch) pattern[pattern_offset++] = (ch) + +#define CURRENT_LEVEL_START (starts[starts_base + current_level]) + +#define SET_LEVEL_START starts[starts_base + current_level] = pattern_offset + +#define PUSH_LEVEL_STARTS \ +if (starts_base < (MAX_NESTING-1)*NUM_LEVELS) \ + starts_base += NUM_LEVELS; \ +else \ + goto too_complex \ + +#define POP_LEVEL_STARTS starts_base -= NUM_LEVELS + +#define PUT_ADDR(offset,addr) \ +{ \ + int disp = (addr) - (offset) - 2; \ + pattern[(offset)] = disp & 0xff; \ + pattern[(offset)+1] = (disp>>8) & 0xff; \ +} + +#define INSERT_JUMP(pos,type,addr) \ +{ \ + int a, p = (pos), t = (type), ad = (addr); \ + for (a = pattern_offset - 1; a >= p; a--) \ + pattern[a + 3] = pattern[a]; \ + pattern[p] = t; \ + PUT_ADDR(p+1,ad); \ + pattern_offset += 3; \ +} + +#define SETBIT(buf,offset,bit) (buf)[(offset)+(bit)/8] |= (1<<((bit) & 7)) + +#define SET_FIELDS \ +{ \ + bufp->allocated = alloc; \ + bufp->buffer = pattern; \ + bufp->used = pattern_offset; \ +} + +#define GETHEX(var) \ +{ \ + unsigned char gethex_ch, gethex_value; \ + NEXTCHAR(gethex_ch); \ + gethex_value = hex_char_to_decimal(gethex_ch); \ + if (gethex_value == 16) \ + goto hex_error; \ + NEXTCHAR(gethex_ch); \ + gethex_ch = hex_char_to_decimal(gethex_ch); \ + if (gethex_ch == 16) \ + goto hex_error; \ + (var) = gethex_value * 16 + gethex_ch; \ +} + +#define ANSI_TRANSLATE(ch) \ +{ \ + switch (ch) \ + { \ + case 'a': \ + case 'A': \ + { \ + ch = 7; /* audible bell */ \ + break; \ + } \ + case 'b': \ + case 'B': \ + { \ + ch = 8; /* backspace */ \ + break; \ + } \ + case 'f': \ + case 'F': \ + { \ + ch = 12; /* form feed */ \ + break; \ + } \ + case 'n': \ + case 'N': \ + { \ + ch = 10; /* line feed */ \ + break; \ + } \ + case 'r': \ + case 'R': \ + { \ + ch = 13; /* carriage return */ \ + break; \ + } \ + case 't': \ + case 'T': \ + { \ + ch = 9; /* tab */ \ + break; \ + } \ + case 'v': \ + case 'V': \ + { \ + ch = 11; /* vertical tab */ \ + break; \ + } \ + case 'x': /* hex code */ \ + case 'X': \ + { \ + GETHEX(ch); \ + break; \ + } \ + default: \ + { \ + /* other characters passed through */ \ + if (translate) \ + ch = translate[(unsigned char)ch]; \ + break; \ + } \ + } \ +} + +char *re_compile_pattern(unsigned char *regex, int size, regexp_t bufp) +{ + int a; + int pos; + int op; + int current_level; + int level; + int opcode; + int pattern_offset = 0, alloc; + int starts[NUM_LEVELS * MAX_NESTING]; + int starts_base; + int future_jumps[MAX_NESTING]; + int num_jumps; + unsigned char ch = '\0'; + unsigned char *pattern; + unsigned char *translate; + int next_register; + int paren_depth; + int num_open_registers; + int open_registers[RE_NREGS]; + int beginning_context; + + if (!re_compile_initialized) + re_compile_initialize(); + bufp->used = 0; + bufp->fastmap_accurate = 0; + bufp->uses_registers = 1; + bufp->num_registers = 1; + translate = bufp->translate; + pattern = bufp->buffer; + alloc = bufp->allocated; + if (alloc == 0 || pattern == NULL) + { + alloc = 256; + pattern = malloc(alloc); + if (!pattern) + goto out_of_memory; + } + pattern_offset = 0; + starts_base = 0; + num_jumps = 0; + current_level = 0; + SET_LEVEL_START; + num_open_registers = 0; + next_register = 1; + paren_depth = 0; + beginning_context = 1; + op = -1; + /* we use Rend dummy to ensure that pending jumps are updated + (due to low priority of Rend) before exiting the loop. */ + pos = 0; + while (op != Rend) + { + if (pos >= size) + op = Rend; + else + { + NEXTCHAR(ch); + if (translate) + ch = translate[(unsigned char)ch]; + op = regexp_plain_ops[(unsigned char)ch]; + if (op == Rquote) + { + NEXTCHAR(ch); + op = regexp_quoted_ops[(unsigned char)ch]; + if (op == Rnormal && regexp_ansi_sequences) + ANSI_TRANSLATE(ch); + } + } + level = regexp_precedences[op]; + /* printf("ch='%c' op=%d level=%d current_level=%d + curlevstart=%d\n", ch, op, level, current_level, + CURRENT_LEVEL_START); */ + if (level > current_level) + { + for (current_level++; current_level < level; current_level++) + SET_LEVEL_START; + SET_LEVEL_START; + } + else + if (level < current_level) + { + current_level = level; + for (;num_jumps > 0 && + future_jumps[num_jumps-1] >= CURRENT_LEVEL_START; + num_jumps--) + PUT_ADDR(future_jumps[num_jumps-1], pattern_offset); + } + switch (op) + { + case Rend: + { + break; + } + case Rnormal: + { + normal_char: + opcode = Cexact; + store_opcode_and_arg: /* opcode & ch must be set */ + SET_LEVEL_START; + ALLOC(2); + STORE(opcode); + STORE(ch); + break; + } + case Ranychar: + { + opcode = Canychar; + store_opcode: + SET_LEVEL_START; + ALLOC(1); + STORE(opcode); + break; + } + case Rquote: + { + Py_FatalError("Rquote"); + /*NOTREACHED*/ + } + case Rbol: + { + if (!beginning_context) { + if (regexp_context_indep_ops) + goto op_error; + else + goto normal_char; + } + opcode = Cbol; + goto store_opcode; + } + case Reol: + { + if (!((pos >= size) || + ((regexp_syntax & RE_NO_BK_VBAR) ? + (regex[pos] == '\174') : + (pos+1 < size && regex[pos] == '\134' && + regex[pos+1] == '\174')) || + ((regexp_syntax & RE_NO_BK_PARENS)? + (regex[pos] == ')'): + (pos+1 < size && regex[pos] == '\134' && + regex[pos+1] == ')')))) { + if (regexp_context_indep_ops) + goto op_error; + else + goto normal_char; + } + opcode = Ceol; + goto store_opcode; + /* NOTREACHED */ + break; + } + case Roptional: + { + if (beginning_context) { + if (regexp_context_indep_ops) + goto op_error; + else + goto normal_char; + } + if (CURRENT_LEVEL_START == pattern_offset) + break; /* ignore empty patterns for ? */ + ALLOC(3); + INSERT_JUMP(CURRENT_LEVEL_START, Cfailure_jump, + pattern_offset + 3); + break; + } + case Rstar: + case Rplus: + { + if (beginning_context) { + if (regexp_context_indep_ops) + goto op_error; + else + goto normal_char; + } + if (CURRENT_LEVEL_START == pattern_offset) + break; /* ignore empty patterns for + and * */ + ALLOC(9); + INSERT_JUMP(CURRENT_LEVEL_START, Cfailure_jump, + pattern_offset + 6); + INSERT_JUMP(pattern_offset, Cstar_jump, CURRENT_LEVEL_START); + if (op == Rplus) /* jump over initial failure_jump */ + INSERT_JUMP(CURRENT_LEVEL_START, Cdummy_failure_jump, + CURRENT_LEVEL_START + 6); + break; + } + case Ror: + { + ALLOC(6); + INSERT_JUMP(CURRENT_LEVEL_START, Cfailure_jump, + pattern_offset + 6); + if (num_jumps >= MAX_NESTING) + goto too_complex; + STORE(Cjump); + future_jumps[num_jumps++] = pattern_offset; + STORE(0); + STORE(0); + SET_LEVEL_START; + break; + } + case Ropenpar: + { + SET_LEVEL_START; + if (next_register < RE_NREGS) + { + bufp->uses_registers = 1; + ALLOC(2); + STORE(Cstart_memory); + STORE(next_register); + open_registers[num_open_registers++] = next_register; + bufp->num_registers++; + next_register++; + } + paren_depth++; + PUSH_LEVEL_STARTS; + current_level = 0; + SET_LEVEL_START; + break; + } + case Rclosepar: + { + if (paren_depth <= 0) + goto parenthesis_error; + POP_LEVEL_STARTS; + current_level = regexp_precedences[Ropenpar]; + paren_depth--; + if (paren_depth < num_open_registers) + { + bufp->uses_registers = 1; + ALLOC(2); + STORE(Cend_memory); + num_open_registers--; + STORE(open_registers[num_open_registers]); + } + break; + } + case Rmemory: + { + if (ch == '0') + goto bad_match_register; + assert(ch >= '0' && ch <= '9'); + bufp->uses_registers = 1; + opcode = Cmatch_memory; + ch -= '0'; + goto store_opcode_and_arg; + } + case Rextended_memory: + { + NEXTCHAR(ch); + if (ch < '0' || ch > '9') + goto bad_match_register; + NEXTCHAR(a); + if (a < '0' || a > '9') + goto bad_match_register; + ch = 10 * (a - '0') + ch - '0'; + if (ch == 0 || ch >= RE_NREGS) + goto bad_match_register; + bufp->uses_registers = 1; + opcode = Cmatch_memory; + goto store_opcode_and_arg; + } + case Ropenset: + { + int complement; + int prev; + int offset; + int range; + int firstchar; + + SET_LEVEL_START; + ALLOC(1+256/8); + STORE(Cset); + offset = pattern_offset; + for (a = 0; a < 256/8; a++) + STORE(0); + NEXTCHAR(ch); + if (translate) + ch = translate[(unsigned char)ch]; + if (ch == '\136') + { + complement = 1; + NEXTCHAR(ch); + if (translate) + ch = translate[(unsigned char)ch]; + } + else + complement = 0; + prev = -1; + range = 0; + firstchar = 1; + while (ch != '\135' || firstchar) + { + firstchar = 0; + if (regexp_ansi_sequences && ch == '\134') + { + NEXTCHAR(ch); + ANSI_TRANSLATE(ch); + } + if (range) + { + for (a = prev; a <= (int)ch; a++) + SETBIT(pattern, offset, a); + prev = -1; + range = 0; + } + else + if (prev != -1 && ch == '-') + range = 1; + else + { + SETBIT(pattern, offset, ch); + prev = ch; + } + NEXTCHAR(ch); + if (translate) + ch = translate[(unsigned char)ch]; + } + if (range) + SETBIT(pattern, offset, '-'); + if (complement) + { + for (a = 0; a < 256/8; a++) + pattern[offset+a] ^= 0xff; + } + break; + } + case Rbegbuf: + { + opcode = Cbegbuf; + goto store_opcode; + } + case Rendbuf: + { + opcode = Cendbuf; + goto store_opcode; + } + case Rwordchar: + { + opcode = Csyntaxspec; + ch = Sword; + goto store_opcode_and_arg; + } + case Rnotwordchar: + { + opcode = Cnotsyntaxspec; + ch = Sword; + goto store_opcode_and_arg; + } + case Rwordbeg: + { + opcode = Cwordbeg; + goto store_opcode; + } + case Rwordend: + { + opcode = Cwordend; + goto store_opcode; + } + case Rwordbound: + { + opcode = Cwordbound; + goto store_opcode; + } + case Rnotwordbound: + { + opcode = Cnotwordbound; + goto store_opcode; + } + default: + { + abort(); + } + } + beginning_context = (op == Ropenpar || op == Ror); + } + if (starts_base != 0) + goto parenthesis_error; + assert(num_jumps == 0); + ALLOC(1); + STORE(Cend); + SET_FIELDS; + if(!re_optimize(bufp)) + return "Optimization error"; + return NULL; + + op_error: + SET_FIELDS; + return "Badly placed special character"; + + bad_match_register: + SET_FIELDS; + return "Bad match register number"; + + hex_error: + SET_FIELDS; + return "Bad hexadecimal number"; + + parenthesis_error: + SET_FIELDS; + return "Badly placed parenthesis"; + + out_of_memory: + SET_FIELDS; + return "Out of memory"; + + ends_prematurely: + SET_FIELDS; + return "Regular expression ends prematurely"; + + too_complex: + SET_FIELDS; + return "Regular expression too complex"; +} + +#undef CHARAT +#undef NEXTCHAR +#undef GETHEX +#undef ALLOC +#undef STORE +#undef CURRENT_LEVEL_START +#undef SET_LEVEL_START +#undef PUSH_LEVEL_STARTS +#undef POP_LEVEL_STARTS +#undef PUT_ADDR +#undef INSERT_JUMP +#undef SETBIT +#undef SET_FIELDS + +#define PREFETCH if (text == textend) goto fail + +#define NEXTCHAR(var) \ +PREFETCH; \ +var = (unsigned char)*text++; \ +if (translate) \ + var = translate[var] + +int re_match(regexp_t bufp, unsigned char *string, int size, int pos, + regexp_registers_t old_regs) +{ + unsigned char *code; + unsigned char *translate; + unsigned char *text; + unsigned char *textstart; + unsigned char *textend; + int a; + int b; + int ch; + int reg; + int match_end; + unsigned char *regstart; + unsigned char *regend; + int regsize; + match_state state; + + assert(pos >= 0 && size >= 0); + assert(pos <= size); + + text = string + pos; + textstart = string; + textend = string + size; + + code = bufp->buffer; + + translate = bufp->translate; + + NEW_STATE(state, bufp->num_registers); + + continue_matching: + switch (*code++) + { + case Cend: + { + match_end = text - textstart; + if (old_regs) + { + old_regs->start[0] = pos; + old_regs->end[0] = match_end; + if (!bufp->uses_registers) + { + for (a = 1; a < RE_NREGS; a++) + { + old_regs->start[a] = -1; + old_regs->end[a] = -1; + } + } + else + { + for (a = 1; a < bufp->num_registers; a++) + { + if ((GET_REG_START(state, a) == NULL) || + (GET_REG_END(state, a) == NULL)) + { + old_regs->start[a] = -1; + old_regs->end[a] = -1; + continue; + } + old_regs->start[a] = GET_REG_START(state, a) - textstart; + old_regs->end[a] = GET_REG_END(state, a) - textstart; + } + for (; a < RE_NREGS; a++) + { + old_regs->start[a] = -1; + old_regs->end[a] = -1; + } + } + } + FREE_STATE(state); + return match_end - pos; + } + case Cbol: + { + if (text == textstart || text[-1] == '\n') + goto continue_matching; + goto fail; + } + case Ceol: + { + if (text == textend || *text == '\n') + goto continue_matching; + goto fail; + } + case Cset: + { + NEXTCHAR(ch); + if (code[ch/8] & (1<<(ch & 7))) + { + code += 256/8; + goto continue_matching; + } + goto fail; + } + case Cexact: + { + NEXTCHAR(ch); + if (ch != (unsigned char)*code++) + goto fail; + goto continue_matching; + } + case Canychar: + { + NEXTCHAR(ch); + if (ch == '\n') + goto fail; + goto continue_matching; + } + case Cstart_memory: + { + reg = *code++; + SET_REG_START(state, reg, text, goto error); + goto continue_matching; + } + case Cend_memory: + { + reg = *code++; + SET_REG_END(state, reg, text, goto error); + goto continue_matching; + } + case Cmatch_memory: + { + reg = *code++; + regstart = GET_REG_START(state, reg); + regend = GET_REG_END(state, reg); + if ((regstart == NULL) || (regend == NULL)) + goto fail; /* or should we just match nothing? */ + regsize = regend - regstart; + + if (regsize > (textend - text)) + goto fail; + if(translate) + { + for (; regstart < regend; regstart++, text++) + if (translate[*regstart] != translate[*text]) + goto fail; + } + else + for (; regstart < regend; regstart++, text++) + if (*regstart != *text) + goto fail; + goto continue_matching; + } + case Cupdate_failure_jump: + { + UPDATE_FAILURE(state, text, goto error); + /* fall to next case */ + } + /* treat Cstar_jump just like Cjump if it hasn't been optimized */ + case Cstar_jump: + case Cjump: + { + a = (unsigned char)*code++; + a |= (unsigned char)*code++ << 8; + code += (int)SHORT(a); + if (codebuffer || bufp->buffer+bufp->usedbuffer || bufp->buffer+bufp->used < failuredest) { + PyErr_SetString(PyExc_SystemError, "Regex VM jump out of bounds (Cdummy_failure_jump failuredest)"); + FREE_STATE(state); + return -2; + } + PUSH_FAILURE(state, failuredest, NULL, goto error); + code += a; + if (codebuffer || bufp->buffer+bufp->used < code) { + PyErr_SetString(PyExc_SystemError, "Regex VM jump out of bounds (Cdummy_failure_jump code)"); + FREE_STATE(state); + return -2; + } + goto continue_matching; + } + case Cfailure_jump: + { + a = (unsigned char)*code++; + a |= (unsigned char)*code++ << 8; + a = (int)SHORT(a); + if (code+abuffer || bufp->buffer+bufp->used < code+a) { + PyErr_SetString(PyExc_SystemError, "Regex VM jump out of bounds (Cfailure_jump)"); + FREE_STATE(state); + return -2; + } + PUSH_FAILURE(state, code + a, text, goto error); + goto continue_matching; + } + case Crepeat1: + { + unsigned char *pinst; + a = (unsigned char)*code++; + a |= (unsigned char)*code++ << 8; + a = (int)SHORT(a); + pinst = code + a; + if (pinstbuffer || bufp->buffer+bufp->used */ + } + case Cbegbuf: + { + if (text == textstart) + goto continue_matching; + goto fail; + } + case Cendbuf: + { + if (text == textend) + goto continue_matching; + goto fail; + } + case Cwordbeg: + { + if (text == textend) + goto fail; + if (!(SYNTAX(*text) & Sword)) + goto fail; + if (text == textstart) + goto continue_matching; + if (!(SYNTAX(text[-1]) & Sword)) + goto continue_matching; + goto fail; + } + case Cwordend: + { + if (text == textstart) + goto fail; + if (!(SYNTAX(text[-1]) & Sword)) + goto fail; + if (text == textend) + goto continue_matching; + if (!(SYNTAX(*text) & Sword)) + goto continue_matching; + goto fail; + } + case Cwordbound: + { + /* Note: as in gnu regexp, this also matches at the + * beginning and end of buffer. */ + + if (text == textstart || text == textend) + goto continue_matching; + if ((SYNTAX(text[-1]) & Sword) ^ (SYNTAX(*text) & Sword)) + goto continue_matching; + goto fail; + } + case Cnotwordbound: + { + /* Note: as in gnu regexp, this never matches at the + * beginning and end of buffer. */ + if (text == textstart || text == textend) + goto fail; + if (!((SYNTAX(text[-1]) & Sword) ^ (SYNTAX(*text) & Sword))) + goto continue_matching; + goto fail; + } + case Csyntaxspec: + { + NEXTCHAR(ch); + if (!(SYNTAX(ch) & (unsigned char)*code++)) + goto fail; + goto continue_matching; + } + case Cnotsyntaxspec: + { + NEXTCHAR(ch); + if (SYNTAX(ch) & (unsigned char)*code++) + goto fail; + goto continue_matching; + } + default: + { + FREE_STATE(state); + PyErr_SetString(PyExc_SystemError, "Unknown regex opcode: memory corrupted?"); + return -2; + /*NOTREACHED*/ + } + } + + + +#if 0 /* This line is never reached --Guido */ + abort(); +#endif + /* + *NOTREACHED + */ + + /* Using "break;" in the above switch statement is equivalent to "goto fail;" */ + fail: + POP_FAILURE(state, code, text, goto done_matching, goto error); + goto continue_matching; + + done_matching: +/* if(translated != NULL) */ +/* free(translated); */ + FREE_STATE(state); + return -1; + + error: +/* if (translated != NULL) */ +/* free(translated); */ + FREE_STATE(state); + return -2; +} + + +#undef PREFETCH +#undef NEXTCHAR + +int re_search(regexp_t bufp, unsigned char *string, int size, int pos, + int range, regexp_registers_t regs) +{ + unsigned char *fastmap; + unsigned char *translate; + unsigned char *text; + unsigned char *partstart; + unsigned char *partend; + int dir; + int ret; + unsigned char anchor; + + assert(size >= 0 && pos >= 0); + assert(pos + range >= 0 && pos + range <= size); /* Bugfix by ylo */ + + fastmap = bufp->fastmap; + translate = bufp->translate; + if (fastmap && !bufp->fastmap_accurate) { + re_compile_fastmap(bufp); + if (PyErr_Occurred()) return -2; + } + + anchor = bufp->anchor; + if (bufp->can_be_null == 1) /* can_be_null == 2: can match null at eob */ + fastmap = NULL; + + if (range < 0) + { + dir = -1; + range = -range; + } + else + dir = 1; + + if (anchor == 2) { + if (pos != 0) + return -1; + else + range = 0; + } + + for (; range >= 0; range--, pos += dir) + { + if (fastmap) + { + if (dir == 1) + { /* searching forwards */ + + text = string + pos; + partend = string + size; + partstart = text; + if (translate) + while (text != partend && + !fastmap[(unsigned char) translate[(unsigned char)*text]]) + text++; + else + while (text != partend && !fastmap[(unsigned char)*text]) + text++; + pos += text - partstart; + range -= text - partstart; + if (pos == size && bufp->can_be_null == 0) + return -1; + } + else + { /* searching backwards */ + text = string + pos; + partstart = string + pos - range; + partend = text; + if (translate) + while (text != partstart && + !fastmap[(unsigned char) + translate[(unsigned char)*text]]) + text--; + else + while (text != partstart && + !fastmap[(unsigned char)*text]) + text--; + pos -= partend - text; + range -= partend - text; + } + } + if (anchor == 1) + { /* anchored to begline */ + if (pos > 0 && (string[pos - 1] != '\n')) + continue; + } + assert(pos >= 0 && pos <= size); + ret = re_match(bufp, string, size, pos, regs); + if (ret >= 0) + return pos; + if (ret == -2) + return -2; + } + return -1; +} + +/* +** Local Variables: +** mode: c +** c-file-style: "python" +** End: +*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexpr.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexpr.h new file mode 100644 index 00000000..607b1e3d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/regexpr.h @@ -0,0 +1,155 @@ +/* + * -*- mode: c-mode; c-file-style: python -*- + */ + +#ifndef Py_REGEXPR_H +#define Py_REGEXPR_H +#ifdef __cplusplus +extern "C" { +#endif + +/* + * regexpr.h + * + * Author: Tatu Ylonen + * + * Copyright (c) 1991 Tatu Ylonen, Espoo, Finland + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies. This + * software is provided "as is" without express or implied warranty. + * + * Created: Thu Sep 26 17:15:36 1991 ylo + * Last modified: Mon Nov 4 15:49:46 1991 ylo + */ + +/* $Id: regexpr.h,v 1.13 2000/07/16 12:04:31 twouters Exp $ */ + +#ifndef REGEXPR_H +#define REGEXPR_H + +#define RE_NREGS 100 /* number of registers available */ + +typedef struct re_pattern_buffer +{ + unsigned char *buffer; /* compiled pattern */ + int allocated; /* allocated size of compiled pattern */ + int used; /* actual length of compiled pattern */ + unsigned char *fastmap; /* fastmap[ch] is true if ch can start pattern */ + unsigned char *translate; /* translation to apply during compilation/matching */ + unsigned char fastmap_accurate; /* true if fastmap is valid */ + unsigned char can_be_null; /* true if can match empty string */ + unsigned char uses_registers; /* registers are used and need to be initialized */ + int num_registers; /* number of registers used */ + unsigned char anchor; /* anchor: 0=none 1=begline 2=begbuf */ +} *regexp_t; + +typedef struct re_registers +{ + int start[RE_NREGS]; /* start offset of region */ + int end[RE_NREGS]; /* end offset of region */ +} *regexp_registers_t; + +/* bit definitions for syntax */ +#define RE_NO_BK_PARENS 1 /* no quoting for parentheses */ +#define RE_NO_BK_VBAR 2 /* no quoting for vertical bar */ +#define RE_BK_PLUS_QM 4 /* quoting needed for + and ? */ +#define RE_TIGHT_VBAR 8 /* | binds tighter than ^ and $ */ +#define RE_NEWLINE_OR 16 /* treat newline as or */ +#define RE_CONTEXT_INDEP_OPS 32 /* ^$?*+ are special in all contexts */ +#define RE_ANSI_HEX 64 /* ansi sequences (\n etc) and \xhh */ +#define RE_NO_GNU_EXTENSIONS 128 /* no gnu extensions */ + +/* definitions for some common regexp styles */ +#define RE_SYNTAX_AWK (RE_NO_BK_PARENS|RE_NO_BK_VBAR|RE_CONTEXT_INDEP_OPS) +#define RE_SYNTAX_EGREP (RE_SYNTAX_AWK|RE_NEWLINE_OR) +#define RE_SYNTAX_GREP (RE_BK_PLUS_QM|RE_NEWLINE_OR) +#define RE_SYNTAX_EMACS 0 + +#define Sword 1 +#define Swhitespace 2 +#define Sdigit 4 +#define Soctaldigit 8 +#define Shexdigit 16 + +/* Rename all exported symbols to avoid conflicts with similarly named + symbols in some systems' standard C libraries... */ + +#define re_syntax _Py_re_syntax +#define re_syntax_table _Py_re_syntax_table +#define re_compile_initialize _Py_re_compile_initialize +#define re_set_syntax _Py_re_set_syntax +#define re_compile_pattern _Py_re_compile_pattern +#define re_match _Py_re_match +#define re_search _Py_re_search +#define re_compile_fastmap _Py_re_compile_fastmap +#define re_comp _Py_re_comp +#define re_exec _Py_re_exec + +#ifdef HAVE_PROTOTYPES + +extern int re_syntax; +/* This is the actual syntax mask. It was added so that Python could do + * syntax-dependent munging of patterns before compilation. */ + +extern unsigned char re_syntax_table[256]; + +void re_compile_initialize(void); + +int re_set_syntax(int syntax); +/* This sets the syntax to use and returns the previous syntax. The + * syntax is specified by a bit mask of the above defined bits. */ + +char *re_compile_pattern(unsigned char *regex, int regex_size, regexp_t compiled); +/* This compiles the regexp (given in regex and length in regex_size). + * This returns NULL if the regexp compiled successfully, and an error + * message if an error was encountered. The buffer field must be + * initialized to a memory area allocated by malloc (or to NULL) before + * use, and the allocated field must be set to its length (or 0 if + * buffer is NULL). Also, the translate field must be set to point to a + * valid translation table, or NULL if it is not used. */ + +int re_match(regexp_t compiled, unsigned char *string, int size, int pos, + regexp_registers_t old_regs); +/* This tries to match the regexp against the string. This returns the + * length of the matched portion, or -1 if the pattern could not be + * matched and -2 if an error (such as failure stack overflow) is + * encountered. */ + +int re_search(regexp_t compiled, unsigned char *string, int size, int startpos, + int range, regexp_registers_t regs); +/* This searches for a substring matching the regexp. This returns the + * first index at which a match is found. range specifies at how many + * positions to try matching; positive values indicate searching + * forwards, and negative values indicate searching backwards. mstop + * specifies the offset beyond which a match must not go. This returns + * -1 if no match is found, and -2 if an error (such as failure stack + * overflow) is encountered. */ + +void re_compile_fastmap(regexp_t compiled); +/* This computes the fastmap for the regexp. For this to have any effect, + * the calling program must have initialized the fastmap field to point + * to an array of 256 characters. */ + +#else /* HAVE_PROTOTYPES */ + +extern int re_syntax; +extern unsigned char re_syntax_table[256]; +void re_compile_initialize(); +int re_set_syntax(); +char *re_compile_pattern(); +int re_match(); +int re_search(); +void re_compile_fastmap(); + +#endif /* HAVE_PROTOTYPES */ + +#endif /* REGEXPR_H */ + + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_REGEXPR_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/resource.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/resource.c new file mode 100644 index 00000000..c1f4df9d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/resource.c @@ -0,0 +1,316 @@ + +#include "Python.h" +#include "structseq.h" +#include +#include +#include +#include +/* for sysconf */ +#if defined(HAVE_UNISTD_H) +#include +#endif + +/* On some systems, these aren't in any header file. + On others they are, with inconsistent prototypes. + We declare the (default) return type, to shut up gcc -Wall; + but we can't declare the prototype, to avoid errors + when the header files declare it different. + Worse, on some Linuxes, getpagesize() returns a size_t... */ + +#define doubletime(TV) ((double)(TV).tv_sec + (TV).tv_usec * 0.000001) + +static PyObject *ResourceError; + +PyDoc_STRVAR(struct_rusage__doc__, +"struct_rusage: Result from getrusage.\n\n" +"This object may be accessed either as a tuple of\n" +" (utime,stime,maxrss,ixrss,idrss,isrss,minflt,majflt,\n" +" nswap,inblock,oublock,msgsnd,msgrcv,nsignals,nvcsw,nivcsw)\n" +"or via the attributes ru_utime, ru_stime, ru_maxrss, and so on."); + +static PyStructSequence_Field struct_rusage_fields[] = { + {"ru_utime", "user time used"}, + {"ru_stime", "system time used"}, + {"ru_maxrss", "max. resident set size"}, + {"ru_ixrss", "shared memory size"}, + {"ru_idrss", "unshared data size"}, + {"ru_isrss", "unshared stack size"}, + {"ru_minflt", "page faults not requiring I/O"}, + {"ru_majflt", "page faults requiring I/O"}, + {"ru_nswap", "number of swap outs"}, + {"ru_inblock", "block input operations"}, + {"ru_oublock", "block output operations"}, + {"ru_msgsnd", "IPC messages sent"}, + {"ru_msgrcv", "IPC messages received"}, + {"ru_nsignals", "signals received"}, + {"ru_nvcsw", "voluntary context switches"}, + {"ru_nivcsw", "involuntary context switches"}, + {0} +}; + +static PyStructSequence_Desc struct_rusage_desc = { + "resource.struct_rusage", /* name */ + struct_rusage__doc__, /* doc */ + struct_rusage_fields, /* fields */ + 16 /* n_in_sequence */ +}; + +static PyTypeObject StructRUsageType; + +static PyObject * +resource_getrusage(PyObject *self, PyObject *args) +{ + int who; + struct rusage ru; + PyObject *result; + + if (!PyArg_ParseTuple(args, "i:getrusage", &who)) + return NULL; + + if (getrusage(who, &ru) == -1) { + if (errno == EINVAL) { + PyErr_SetString(PyExc_ValueError, + "invalid who parameter"); + return NULL; + } + PyErr_SetFromErrno(ResourceError); + return NULL; + } + + result = PyStructSequence_New(&StructRUsageType); + if (!result) + return NULL; + + PyStructSequence_SET_ITEM(result, 0, + PyFloat_FromDouble(doubletime(ru.ru_utime))); + PyStructSequence_SET_ITEM(result, 1, + PyFloat_FromDouble(doubletime(ru.ru_stime))); + PyStructSequence_SET_ITEM(result, 2, PyInt_FromLong(ru.ru_maxrss)); + PyStructSequence_SET_ITEM(result, 3, PyInt_FromLong(ru.ru_ixrss)); + PyStructSequence_SET_ITEM(result, 4, PyInt_FromLong(ru.ru_idrss)); + PyStructSequence_SET_ITEM(result, 5, PyInt_FromLong(ru.ru_isrss)); + PyStructSequence_SET_ITEM(result, 6, PyInt_FromLong(ru.ru_minflt)); + PyStructSequence_SET_ITEM(result, 7, PyInt_FromLong(ru.ru_majflt)); + PyStructSequence_SET_ITEM(result, 8, PyInt_FromLong(ru.ru_nswap)); + PyStructSequence_SET_ITEM(result, 9, PyInt_FromLong(ru.ru_inblock)); + PyStructSequence_SET_ITEM(result, 10, PyInt_FromLong(ru.ru_oublock)); + PyStructSequence_SET_ITEM(result, 11, PyInt_FromLong(ru.ru_msgsnd)); + PyStructSequence_SET_ITEM(result, 12, PyInt_FromLong(ru.ru_msgrcv)); + PyStructSequence_SET_ITEM(result, 13, PyInt_FromLong(ru.ru_nsignals)); + PyStructSequence_SET_ITEM(result, 14, PyInt_FromLong(ru.ru_nvcsw)); + PyStructSequence_SET_ITEM(result, 15, PyInt_FromLong(ru.ru_nivcsw)); + + if (PyErr_Occurred()) { + Py_DECREF(result); + return NULL; + } + + return result; +} + + +static PyObject * +resource_getrlimit(PyObject *self, PyObject *args) +{ + struct rlimit rl; + int resource; + + if (!PyArg_ParseTuple(args, "i:getrlimit", &resource)) + return NULL; + + if (resource < 0 || resource >= RLIM_NLIMITS) { + PyErr_SetString(PyExc_ValueError, + "invalid resource specified"); + return NULL; + } + + if (getrlimit(resource, &rl) == -1) { + PyErr_SetFromErrno(ResourceError); + return NULL; + } + +#if defined(HAVE_LONG_LONG) + if (sizeof(rl.rlim_cur) > sizeof(long)) { + return Py_BuildValue("LL", + (PY_LONG_LONG) rl.rlim_cur, + (PY_LONG_LONG) rl.rlim_max); + } +#endif + return Py_BuildValue("ll", (long) rl.rlim_cur, (long) rl.rlim_max); +} + +static PyObject * +resource_setrlimit(PyObject *self, PyObject *args) +{ + struct rlimit rl; + int resource; + PyObject *curobj, *maxobj; + + if (!PyArg_ParseTuple(args, "i(OO):setrlimit", + &resource, &curobj, &maxobj)) + return NULL; + + if (resource < 0 || resource >= RLIM_NLIMITS) { + PyErr_SetString(PyExc_ValueError, + "invalid resource specified"); + return NULL; + } + +#if !defined(HAVE_LARGEFILE_SUPPORT) + rl.rlim_cur = PyInt_AsLong(curobj); + if (rl.rlim_cur == -1 && PyErr_Occurred()) + return NULL; + rl.rlim_max = PyInt_AsLong(maxobj); + if (rl.rlim_max == -1 && PyErr_Occurred()) + return NULL; +#else + /* The limits are probably bigger than a long */ + rl.rlim_cur = PyLong_Check(curobj) ? + PyLong_AsLongLong(curobj) : PyInt_AsLong(curobj); + if (rl.rlim_cur == -1 && PyErr_Occurred()) + return NULL; + rl.rlim_max = PyLong_Check(maxobj) ? + PyLong_AsLongLong(maxobj) : PyInt_AsLong(maxobj); + if (rl.rlim_max == -1 && PyErr_Occurred()) + return NULL; +#endif + + rl.rlim_cur = rl.rlim_cur & RLIM_INFINITY; + rl.rlim_max = rl.rlim_max & RLIM_INFINITY; + if (setrlimit(resource, &rl) == -1) { + if (errno == EINVAL) + PyErr_SetString(PyExc_ValueError, + "current limit exceeds maximum limit"); + else if (errno == EPERM) + PyErr_SetString(PyExc_ValueError, + "not allowed to raise maximum limit"); + else + PyErr_SetFromErrno(ResourceError); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +resource_getpagesize(PyObject *self, PyObject *args) +{ + long pagesize = 0; + if (!PyArg_ParseTuple(args, ":getpagesize")) + return NULL; + +#if defined(HAVE_GETPAGESIZE) + pagesize = getpagesize(); +#elif defined(HAVE_SYSCONF) + pagesize = sysconf(_SC_PAGE_SIZE); +#endif + return Py_BuildValue("i", pagesize); + +} + +/* List of functions */ + +static struct PyMethodDef +resource_methods[] = { + {"getrusage", resource_getrusage, METH_VARARGS}, + {"getrlimit", resource_getrlimit, METH_VARARGS}, + {"setrlimit", resource_setrlimit, METH_VARARGS}, + {"getpagesize", resource_getpagesize, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +/* Module initialization */ + +PyMODINIT_FUNC +initresource(void) +{ + PyObject *m, *v; + + /* Create the module and add the functions */ + m = Py_InitModule("resource", resource_methods); + + /* Add some symbolic constants to the module */ + if (ResourceError == NULL) { + ResourceError = PyErr_NewException("resource.error", + NULL, NULL); + } + Py_INCREF(ResourceError); + PyModule_AddObject(m, "error", ResourceError); + PyStructSequence_InitType(&StructRUsageType, &struct_rusage_desc); + PyModule_AddObject(m, "struct_rusage", + (PyObject*) &StructRUsageType); + + /* insert constants */ +#ifdef RLIMIT_CPU + PyModule_AddIntConstant(m, "RLIMIT_CPU", RLIMIT_CPU); +#endif + +#ifdef RLIMIT_FSIZE + PyModule_AddIntConstant(m, "RLIMIT_FSIZE", RLIMIT_FSIZE); +#endif + +#ifdef RLIMIT_DATA + PyModule_AddIntConstant(m, "RLIMIT_DATA", RLIMIT_DATA); +#endif + +#ifdef RLIMIT_STACK + PyModule_AddIntConstant(m, "RLIMIT_STACK", RLIMIT_STACK); +#endif + +#ifdef RLIMIT_CORE + PyModule_AddIntConstant(m, "RLIMIT_CORE", RLIMIT_CORE); +#endif + +#ifdef RLIMIT_NOFILE + PyModule_AddIntConstant(m, "RLIMIT_NOFILE", RLIMIT_NOFILE); +#endif + +#ifdef RLIMIT_OFILE + PyModule_AddIntConstant(m, "RLIMIT_OFILE", RLIMIT_OFILE); +#endif + +#ifdef RLIMIT_VMEM + PyModule_AddIntConstant(m, "RLIMIT_VMEM", RLIMIT_VMEM); +#endif + +#ifdef RLIMIT_AS + PyModule_AddIntConstant(m, "RLIMIT_AS", RLIMIT_AS); +#endif + +#ifdef RLIMIT_RSS + PyModule_AddIntConstant(m, "RLIMIT_RSS", RLIMIT_RSS); +#endif + +#ifdef RLIMIT_NPROC + PyModule_AddIntConstant(m, "RLIMIT_NPROC", RLIMIT_NPROC); +#endif + +#ifdef RLIMIT_MEMLOCK + PyModule_AddIntConstant(m, "RLIMIT_MEMLOCK", RLIMIT_MEMLOCK); +#endif + +#ifdef RUSAGE_SELF + PyModule_AddIntConstant(m, "RUSAGE_SELF", RUSAGE_SELF); +#endif + +#ifdef RUSAGE_CHILDREN + PyModule_AddIntConstant(m, "RUSAGE_CHILDREN", RUSAGE_CHILDREN); +#endif + +#ifdef RUSAGE_BOTH + PyModule_AddIntConstant(m, "RUSAGE_BOTH", RUSAGE_BOTH); +#endif + +#if defined(HAVE_LONG_LONG) + if (sizeof(RLIM_INFINITY) > sizeof(long)) { + v = PyLong_FromLongLong((PY_LONG_LONG) RLIM_INFINITY); + } else +#endif + { + v = PyInt_FromLong((long) RLIM_INFINITY); + } + if (v) { + PyModule_AddObject(m, "RLIM_INFINITY", v); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/rgbimgmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/rgbimgmodule.c new file mode 100644 index 00000000..9349408f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/rgbimgmodule.c @@ -0,0 +1,763 @@ +/* + * fastimg - + * Faster reading and writing of image files. + * + * This code should work on machines with any byte order. + * + * Could someone make this run real fast using multiple processors + * or how about using memory mapped files to speed it up? + * + * Paul Haeberli - 1991 + * + * Changed to return sizes. + * Sjoerd Mullender - 1993 + * Changed to incorporate into Python. + * Sjoerd Mullender - 1993 + */ +#include "Python.h" + +#if SIZEOF_INT == 4 +typedef int Py_Int32; +typedef unsigned int Py_UInt32; +#else +#if SIZEOF_LONG == 4 +typedef long Py_Int32; +typedef unsigned long Py_UInt32; +#else +#error "No 4-byte integral type" +#endif +#endif + +#include + +/* + * from image.h + * + */ +typedef struct { + unsigned short imagic; /* stuff saved on disk . . */ + unsigned short type; + unsigned short dim; + unsigned short xsize; + unsigned short ysize; + unsigned short zsize; + Py_UInt32 min; + Py_UInt32 max; + Py_UInt32 wastebytes; + char name[80]; + Py_UInt32 colormap; + + Py_Int32 file; /* stuff used in core only */ + unsigned short flags; + short dorev; + short x; + short y; + short z; + short cnt; + unsigned short *ptr; + unsigned short *base; + unsigned short *tmpbuf; + Py_UInt32 offset; + Py_UInt32 rleend; /* for rle images */ + Py_UInt32 *rowstart; /* for rle images */ + Py_Int32 *rowsize; /* for rle images */ +} IMAGE; + +#define IMAGIC 0732 + +#define TYPEMASK 0xff00 +#define BPPMASK 0x00ff +#define ITYPE_VERBATIM 0x0000 +#define ITYPE_RLE 0x0100 +#define ISRLE(type) (((type) & 0xff00) == ITYPE_RLE) +#define ISVERBATIM(type) (((type) & 0xff00) == ITYPE_VERBATIM) +#define BPP(type) ((type) & BPPMASK) +#define RLE(bpp) (ITYPE_RLE | (bpp)) +#define VERBATIM(bpp) (ITYPE_VERBATIM | (bpp)) +/* + * end of image.h stuff + * + */ + +#define RINTLUM (79) +#define GINTLUM (156) +#define BINTLUM (21) + +#define ILUM(r,g,b) ((int)(RINTLUM*(r)+GINTLUM*(g)+BINTLUM*(b))>>8) + +#define OFFSET_R 3 /* this is byte order dependent */ +#define OFFSET_G 2 +#define OFFSET_B 1 +#define OFFSET_A 0 + +#define CHANOFFSET(z) (3-(z)) /* this is byte order dependent */ + +static void expandrow(unsigned char *, unsigned char *, int); +static void setalpha(unsigned char *, int); +static void copybw(Py_Int32 *, int); +static void interleaverow(unsigned char*, unsigned char*, int, int); +static int compressrow(unsigned char *, unsigned char *, int, int); +static void lumrow(unsigned char *, unsigned char *, int); + +#ifdef ADD_TAGS +#define TAGLEN (5) +#else +#define TAGLEN (0) +#endif + +static PyObject *ImgfileError; + +static int reverse_order; + +#ifdef ADD_TAGS +/* + * addlongimgtag - + * this is used to extract image data from core dumps. + * + */ +static void +addlongimgtag(Py_UInt32 *dptr, int xsize, int ysize) +{ + dptr = dptr + (xsize * ysize); + dptr[0] = 0x12345678; + dptr[1] = 0x59493333; + dptr[2] = 0x69434222; + dptr[3] = xsize; + dptr[4] = ysize; +} +#endif + +/* + * byte order independent read/write of shorts and longs. + * + */ +static unsigned short +getshort(FILE *inf) +{ + unsigned char buf[2]; + + fread(buf, 2, 1, inf); + return (buf[0] << 8) + (buf[1] << 0); +} + +static Py_UInt32 +getlong(FILE *inf) +{ + unsigned char buf[4]; + + fread(buf, 4, 1, inf); + return (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + (buf[3] << 0); +} + +static void +putshort(FILE *outf, unsigned short val) +{ + unsigned char buf[2]; + + buf[0] = (val >> 8); + buf[1] = (val >> 0); + fwrite(buf, 2, 1, outf); +} + +static int +putlong(FILE *outf, Py_UInt32 val) +{ + unsigned char buf[4]; + + buf[0] = (unsigned char) (val >> 24); + buf[1] = (unsigned char) (val >> 16); + buf[2] = (unsigned char) (val >> 8); + buf[3] = (unsigned char) (val >> 0); + return fwrite(buf, 4, 1, outf); +} + +static void +readheader(FILE *inf, IMAGE *image) +{ + memset(image ,0, sizeof(IMAGE)); + image->imagic = getshort(inf); + image->type = getshort(inf); + image->dim = getshort(inf); + image->xsize = getshort(inf); + image->ysize = getshort(inf); + image->zsize = getshort(inf); +} + +static int +writeheader(FILE *outf, IMAGE *image) +{ + IMAGE t; + + memset(&t, 0, sizeof(IMAGE)); + fwrite(&t, sizeof(IMAGE), 1, outf); + fseek(outf, 0, SEEK_SET); + putshort(outf, image->imagic); + putshort(outf, image->type); + putshort(outf, image->dim); + putshort(outf, image->xsize); + putshort(outf, image->ysize); + putshort(outf, image->zsize); + putlong(outf, image->min); + putlong(outf, image->max); + putlong(outf, 0); + return fwrite("no name", 8, 1, outf); +} + +static int +writetab(FILE *outf, /*unsigned*/ Py_Int32 *tab, int len) +{ + int r = 0; + + while(len) { + r = putlong(outf, *tab++); + len--; + } + return r; +} + +static void +readtab(FILE *inf, /*unsigned*/ Py_Int32 *tab, int len) +{ + while(len) { + *tab++ = getlong(inf); + len--; + } +} + +/* + * sizeofimage - + * return the xsize and ysize of an iris image file. + * + */ +static PyObject * +sizeofimage(PyObject *self, PyObject *args) +{ + char *name; + IMAGE image; + FILE *inf; + + if (!PyArg_ParseTuple(args, "s:sizeofimage", &name)) + return NULL; + + inf = fopen(name, "rb"); + if (!inf) { + PyErr_SetString(ImgfileError, "can't open image file"); + return NULL; + } + readheader(inf, &image); + fclose(inf); + if (image.imagic != IMAGIC) { + PyErr_SetString(ImgfileError, + "bad magic number in image file"); + return NULL; + } + return Py_BuildValue("(ii)", image.xsize, image.ysize); +} + +/* + * longimagedata - + * read in a B/W RGB or RGBA iris image file and return a + * pointer to an array of longs. + * + */ +static PyObject * +longimagedata(PyObject *self, PyObject *args) +{ + char *name; + unsigned char *base, *lptr; + unsigned char *rledat = NULL, *verdat = NULL; + Py_Int32 *starttab = NULL, *lengthtab = NULL; + FILE *inf = NULL; + IMAGE image; + int y, z, tablen; + int xsize, ysize, zsize; + int bpp, rle, cur, badorder; + int rlebuflen; + PyObject *rv = NULL; + + if (!PyArg_ParseTuple(args, "s:longimagedata", &name)) + return NULL; + + inf = fopen(name,"rb"); + if (!inf) { + PyErr_SetString(ImgfileError, "can't open image file"); + return NULL; + } + readheader(inf,&image); + if (image.imagic != IMAGIC) { + PyErr_SetString(ImgfileError, + "bad magic number in image file"); + goto finally; + } + rle = ISRLE(image.type); + bpp = BPP(image.type); + if (bpp != 1) { + PyErr_SetString(ImgfileError, + "image must have 1 byte per pix chan"); + goto finally; + } + xsize = image.xsize; + ysize = image.ysize; + zsize = image.zsize; + if (rle) { + tablen = ysize * zsize * sizeof(Py_Int32); + starttab = (Py_Int32 *)malloc(tablen); + lengthtab = (Py_Int32 *)malloc(tablen); + rlebuflen = (int) (1.05 * xsize +10); + rledat = (unsigned char *)malloc(rlebuflen); + if (!starttab || !lengthtab || !rledat) { + PyErr_NoMemory(); + goto finally; + } + + fseek(inf, 512, SEEK_SET); + readtab(inf, starttab, ysize*zsize); + readtab(inf, lengthtab, ysize*zsize); + + /* check data order */ + cur = 0; + badorder = 0; + for(y = 0; y < ysize; y++) { + for(z = 0; z < zsize; z++) { + if (starttab[y + z * ysize] < cur) { + badorder = 1; + break; + } + cur = starttab[y +z * ysize]; + } + if (badorder) + break; + } + + fseek(inf, 512 + 2 * tablen, SEEK_SET); + cur = 512 + 2 * tablen; + rv = PyString_FromStringAndSize((char *)NULL, + (xsize * ysize + TAGLEN) * sizeof(Py_Int32)); + if (rv == NULL) + goto finally; + + base = (unsigned char *) PyString_AsString(rv); +#ifdef ADD_TAGS + addlongimgtag(base,xsize,ysize); +#endif + if (badorder) { + for (z = 0; z < zsize; z++) { + lptr = base; + if (reverse_order) + lptr += (ysize - 1) * xsize + * sizeof(Py_UInt32); + for (y = 0; y < ysize; y++) { + int idx = y + z * ysize; + if (cur != starttab[idx]) { + fseek(inf,starttab[idx], + SEEK_SET); + cur = starttab[idx]; + } + if (lengthtab[idx] > rlebuflen) { + PyErr_SetString(ImgfileError, + "rlebuf is too small"); + Py_DECREF(rv); + rv = NULL; + goto finally; + } + fread(rledat, lengthtab[idx], 1, inf); + cur += lengthtab[idx]; + expandrow(lptr, rledat, 3-z); + if (reverse_order) + lptr -= xsize + * sizeof(Py_UInt32); + else + lptr += xsize + * sizeof(Py_UInt32); + } + } + } else { + lptr = base; + if (reverse_order) + lptr += (ysize - 1) * xsize + * sizeof(Py_UInt32); + for (y = 0; y < ysize; y++) { + for(z = 0; z < zsize; z++) { + int idx = y + z * ysize; + if (cur != starttab[idx]) { + fseek(inf, starttab[idx], + SEEK_SET); + cur = starttab[idx]; + } + fread(rledat, lengthtab[idx], 1, inf); + cur += lengthtab[idx]; + expandrow(lptr, rledat, 3-z); + } + if (reverse_order) + lptr -= xsize * sizeof(Py_UInt32); + else + lptr += xsize * sizeof(Py_UInt32); + } + } + if (zsize == 3) + setalpha(base, xsize * ysize); + else if (zsize < 3) + copybw((Py_Int32 *) base, xsize * ysize); + } + else { + rv = PyString_FromStringAndSize((char *) 0, + (xsize*ysize+TAGLEN)*sizeof(Py_Int32)); + if (rv == NULL) + goto finally; + + base = (unsigned char *) PyString_AsString(rv); +#ifdef ADD_TAGS + addlongimgtag(base, xsize, ysize); +#endif + verdat = (unsigned char *)malloc(xsize); + fseek(inf, 512, SEEK_SET); + for (z = 0; z < zsize; z++) { + lptr = base; + if (reverse_order) + lptr += (ysize - 1) * xsize + * sizeof(Py_UInt32); + for (y = 0; y < ysize; y++) { + fread(verdat, xsize, 1, inf); + interleaverow(lptr, verdat, 3-z, xsize); + if (reverse_order) + lptr -= xsize * sizeof(Py_UInt32); + else + lptr += xsize * sizeof(Py_UInt32); + } + } + if (zsize == 3) + setalpha(base, xsize * ysize); + else if (zsize < 3) + copybw((Py_Int32 *) base, xsize * ysize); + } + finally: + free(starttab); + free(lengthtab); + free(rledat); + free(verdat); + fclose(inf); + return rv; +} + +/* static utility functions for longimagedata */ + +static void +interleaverow(unsigned char *lptr, unsigned char *cptr, int z, int n) +{ + lptr += z; + while (n--) { + *lptr = *cptr++; + lptr += 4; + } +} + +static void +copybw(Py_Int32 *lptr, int n) +{ + while (n >= 8) { + lptr[0] = 0xff000000 + (0x010101 * (lptr[0] & 0xff)); + lptr[1] = 0xff000000 + (0x010101 * (lptr[1] & 0xff)); + lptr[2] = 0xff000000 + (0x010101 * (lptr[2] & 0xff)); + lptr[3] = 0xff000000 + (0x010101 * (lptr[3] & 0xff)); + lptr[4] = 0xff000000 + (0x010101 * (lptr[4] & 0xff)); + lptr[5] = 0xff000000 + (0x010101 * (lptr[5] & 0xff)); + lptr[6] = 0xff000000 + (0x010101 * (lptr[6] & 0xff)); + lptr[7] = 0xff000000 + (0x010101 * (lptr[7] & 0xff)); + lptr += 8; + n -= 8; + } + while (n--) { + *lptr = 0xff000000 + (0x010101 * (*lptr&0xff)); + lptr++; + } +} + +static void +setalpha(unsigned char *lptr, int n) +{ + while (n >= 8) { + lptr[0 * 4] = 0xff; + lptr[1 * 4] = 0xff; + lptr[2 * 4] = 0xff; + lptr[3 * 4] = 0xff; + lptr[4 * 4] = 0xff; + lptr[5 * 4] = 0xff; + lptr[6 * 4] = 0xff; + lptr[7 * 4] = 0xff; + lptr += 4 * 8; + n -= 8; + } + while (n--) { + *lptr = 0xff; + lptr += 4; + } +} + +static void +expandrow(unsigned char *optr, unsigned char *iptr, int z) +{ + unsigned char pixel, count; + + optr += z; + while (1) { + pixel = *iptr++; + if (!(count = (pixel & 0x7f))) + return; + if (pixel & 0x80) { + while (count >= 8) { + optr[0 * 4] = iptr[0]; + optr[1 * 4] = iptr[1]; + optr[2 * 4] = iptr[2]; + optr[3 * 4] = iptr[3]; + optr[4 * 4] = iptr[4]; + optr[5 * 4] = iptr[5]; + optr[6 * 4] = iptr[6]; + optr[7 * 4] = iptr[7]; + optr += 8 * 4; + iptr += 8; + count -= 8; + } + while (count--) { + *optr = *iptr++; + optr += 4; + } + } + else { + pixel = *iptr++; + while (count >= 8) { + optr[0 * 4] = pixel; + optr[1 * 4] = pixel; + optr[2 * 4] = pixel; + optr[3 * 4] = pixel; + optr[4 * 4] = pixel; + optr[5 * 4] = pixel; + optr[6 * 4] = pixel; + optr[7 * 4] = pixel; + optr += 8 * 4; + count -= 8; + } + while (count--) { + *optr = pixel; + optr += 4; + } + } + } +} + +/* + * longstoimage - + * copy an array of longs to an iris image file. Each long + * represents one pixel. xsize and ysize specify the dimensions of + * the pixel array. zsize specifies what kind of image file to + * write out. if zsize is 1, the luminance of the pixels are + * calculated, and a single channel black and white image is saved. + * If zsize is 3, an RGB image file is saved. If zsize is 4, an + * RGBA image file is saved. + * + */ +static PyObject * +longstoimage(PyObject *self, PyObject *args) +{ + unsigned char *lptr; + char *name; + int xsize, ysize, zsize; + FILE *outf = NULL; + IMAGE image; + int tablen, y, z, pos, len; + Py_Int32 *starttab = NULL, *lengthtab = NULL; + unsigned char *rlebuf = NULL; + unsigned char *lumbuf = NULL; + int rlebuflen, goodwrite; + PyObject *retval = NULL; + + if (!PyArg_ParseTuple(args, "s#iiis:longstoimage", &lptr, &len, + &xsize, &ysize, &zsize, &name)) + return NULL; + + goodwrite = 1; + outf = fopen(name, "wb"); + if (!outf) { + PyErr_SetString(ImgfileError, "can't open output file"); + return NULL; + } + tablen = ysize * zsize * sizeof(Py_Int32); + + starttab = (Py_Int32 *)malloc(tablen); + lengthtab = (Py_Int32 *)malloc(tablen); + rlebuflen = (int) (1.05 * xsize + 10); + rlebuf = (unsigned char *)malloc(rlebuflen); + lumbuf = (unsigned char *)malloc(xsize * sizeof(Py_Int32)); + if (!starttab || !lengthtab || !rlebuf || !lumbuf) { + PyErr_NoMemory(); + goto finally; + } + + memset(&image, 0, sizeof(IMAGE)); + image.imagic = IMAGIC; + image.type = RLE(1); + if (zsize>1) + image.dim = 3; + else + image.dim = 2; + image.xsize = xsize; + image.ysize = ysize; + image.zsize = zsize; + image.min = 0; + image.max = 255; + goodwrite *= writeheader(outf, &image); + pos = 512 + 2 * tablen; + fseek(outf, pos, SEEK_SET); + if (reverse_order) + lptr += (ysize - 1) * xsize * sizeof(Py_UInt32); + for (y = 0; y < ysize; y++) { + for (z = 0; z < zsize; z++) { + if (zsize == 1) { + lumrow(lptr, lumbuf, xsize); + len = compressrow(lumbuf, rlebuf, + CHANOFFSET(z), xsize); + } else { + len = compressrow(lptr, rlebuf, + CHANOFFSET(z), xsize); + } + if(len > rlebuflen) { + PyErr_SetString(ImgfileError, + "rlebuf is too small"); + goto finally; + } + goodwrite *= fwrite(rlebuf, len, 1, outf); + starttab[y + z * ysize] = pos; + lengthtab[y + z * ysize] = len; + pos += len; + } + if (reverse_order) + lptr -= xsize * sizeof(Py_UInt32); + else + lptr += xsize * sizeof(Py_UInt32); + } + + fseek(outf, 512, SEEK_SET); + goodwrite *= writetab(outf, starttab, ysize*zsize); + goodwrite *= writetab(outf, lengthtab, ysize*zsize); + if (goodwrite) { + Py_INCREF(Py_None); + retval = Py_None; + } else + PyErr_SetString(ImgfileError, "not enough space for image"); + + finally: + fclose(outf); + free(starttab); + free(lengthtab); + free(rlebuf); + free(lumbuf); + return retval; +} + +/* static utility functions for longstoimage */ + +static void +lumrow(unsigned char *rgbptr, unsigned char *lumptr, int n) +{ + lumptr += CHANOFFSET(0); + while (n--) { + *lumptr = ILUM(rgbptr[OFFSET_R], + rgbptr[OFFSET_G], + rgbptr[OFFSET_B]); + lumptr += 4; + rgbptr += 4; + } +} + +static int +compressrow(unsigned char *lbuf, unsigned char *rlebuf, int z, int cnt) +{ + unsigned char *iptr, *ibufend, *sptr, *optr; + short todo, cc; + Py_Int32 count; + + lbuf += z; + iptr = lbuf; + ibufend = iptr + cnt * 4; + optr = rlebuf; + + while(iptr < ibufend) { + sptr = iptr; + iptr += 8; + while ((iptr 126 ? 126 : (short)count; + count -= todo; + *optr++ = 0x80 | todo; + while (todo > 8) { + optr[0] = sptr[0 * 4]; + optr[1] = sptr[1 * 4]; + optr[2] = sptr[2 * 4]; + optr[3] = sptr[3 * 4]; + optr[4] = sptr[4 * 4]; + optr[5] = sptr[5 * 4]; + optr[6] = sptr[6 * 4]; + optr[7] = sptr[7 * 4]; + optr += 8; + sptr += 8 * 4; + todo -= 8; + } + while (todo--) { + *optr++ = *sptr; + sptr += 4; + } + } + sptr = iptr; + cc = *iptr; + iptr += 4; + while ((iptr < ibufend) && (*iptr == cc)) + iptr += 4; + count = (iptr - sptr) / 4; + while (count) { + todo = count > 126 ? 126 : (short)count; + count -= todo; + *optr++ = (unsigned char) todo; + *optr++ = (unsigned char) cc; + } + } + *optr++ = 0; + return optr - (unsigned char *)rlebuf; +} + +static PyObject * +ttob(PyObject *self, PyObject *args) +{ + int order, oldorder; + + if (!PyArg_ParseTuple(args, "i:ttob", &order)) + return NULL; + oldorder = reverse_order; + reverse_order = order; + return PyInt_FromLong(oldorder); +} + +static PyMethodDef +rgbimg_methods[] = { + {"sizeofimage", sizeofimage, METH_VARARGS}, + {"longimagedata", longimagedata, METH_VARARGS}, + {"longstoimage", longstoimage, METH_VARARGS}, + {"ttob", ttob, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +PyMODINIT_FUNC +initrgbimg(void) +{ + PyObject *m, *d; + m = Py_InitModule("rgbimg", rgbimg_methods); + d = PyModule_GetDict(m); + ImgfileError = PyErr_NewException("rgbimg.error", NULL, NULL); + if (ImgfileError != NULL) + PyDict_SetItemString(d, "error", ImgfileError); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/rotormodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/rotormodule.c new file mode 100644 index 00000000..988a8085 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/rotormodule.c @@ -0,0 +1,627 @@ +/*********************************************************** +Copyright 1994 by Lance Ellinghouse, +Cathedral City, California Republic, United States of America. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Lance Ellinghouse +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +LANCE ELLINGHOUSE DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL LANCE ELLINGHOUSE BE LIABLE FOR ANY SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* This creates an encryption and decryption engine I am calling + a rotor due to the original design was a hardware rotor with + contacts used in Germany during WWII. + +Rotor Module: + +- rotor.newrotor('key') -> rotorobject (default of 6 rotors) +- rotor.newrotor('key', num_rotors) -> rotorobject + +Rotor Objects: + +- ro.setkey('string') -> None (resets the key as defined in newrotor(). +- ro.encrypt('string') -> encrypted string +- ro.decrypt('encrypted string') -> unencrypted string + +- ro.encryptmore('string') -> encrypted string +- ro.decryptmore('encrypted string') -> unencrypted string + +NOTE: the {en,de}cryptmore() methods use the setup that was + established via the {en,de}crypt calls. They will NOT + re-initalize the rotors unless: 1) They have not been + initialized with {en,de}crypt since the last setkey() call; + 2) {en,de}crypt has not been called for this rotor yet. + +NOTE: you MUST use the SAME key in rotor.newrotor() + if you wish to decrypt an encrypted string. + Also, the encrypted string is NOT 0-127 ASCII. + It is considered BINARY data. + +*/ + +/* Rotor objects */ + +#include "Python.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +typedef struct { + PyObject_HEAD + int seed[3]; + short key[5]; + int isinited; + int size; + int size_mask; + int rotors; + unsigned char *e_rotor; /* [num_rotors][size] */ + unsigned char *d_rotor; /* [num_rotors][size] */ + unsigned char *positions; /* [num_rotors] */ + unsigned char *advances; /* [num_rotors] */ +} Rotorobj; + +static PyTypeObject Rotor_Type; + +#define is_rotor(v) ((v)->ob_type == &Rotor_Type) + + +/* This defines the necessary routines to manage rotor objects */ + +static void +set_seed(Rotorobj *r) +{ + r->seed[0] = r->key[0]; + r->seed[1] = r->key[1]; + r->seed[2] = r->key[2]; + r->isinited = FALSE; +} + +/* Return the next random number in the range [0.0 .. 1.0) */ +static double +r_random(Rotorobj *r) +{ + int x, y, z; + double val, term; + + x = r->seed[0]; + y = r->seed[1]; + z = r->seed[2]; + + x = 171 * (x % 177) - 2 * (x/177); + y = 172 * (y % 176) - 35 * (y/176); + z = 170 * (z % 178) - 63 * (z/178); + + if (x < 0) x = x + 30269; + if (y < 0) y = y + 30307; + if (z < 0) z = z + 30323; + + r->seed[0] = x; + r->seed[1] = y; + r->seed[2] = z; + + term = (double)( + (((double)x)/(double)30269.0) + + (((double)y)/(double)30307.0) + + (((double)z)/(double)30323.0) + ); + val = term - (double)floor((double)term); + + if (val >= 1.0) + val = 0.0; + + return val; +} + +static short +r_rand(Rotorobj *r, short s) +{ + return (short)((short)(r_random(r) * (double)s) % s); +} + +static void +set_key(Rotorobj *r, char *key) +{ + unsigned long k1=995, k2=576, k3=767, k4=671, k5=463; + size_t i; + size_t len = strlen(key); + + for (i = 0; i < len; i++) { + unsigned short ki = Py_CHARMASK(key[i]); + + k1 = (((k1<<3 | k1>>13) + ki) & 65535); + k2 = (((k2<<3 | k2>>13) ^ ki) & 65535); + k3 = (((k3<<3 | k3>>13) - ki) & 65535); + k4 = ((ki - (k4<<3 | k4>>13)) & 65535); + k5 = (((k5<<3 | k5>>13) ^ ~ki) & 65535); + } + r->key[0] = (short)k1; + r->key[1] = (short)(k2|1); + r->key[2] = (short)k3; + r->key[3] = (short)k4; + r->key[4] = (short)k5; + + set_seed(r); +} + + + +/* These define the interface to a rotor object */ +static Rotorobj * +rotorobj_new(int num_rotors, char *key) +{ + Rotorobj *xp; + + xp = PyObject_New(Rotorobj, &Rotor_Type); + if (xp == NULL) + return NULL; + set_key(xp, key); + + xp->size = 256; + xp->size_mask = xp->size - 1; + xp->size_mask = 0; + xp->rotors = num_rotors; + xp->e_rotor = NULL; + xp->d_rotor = NULL; + xp->positions = NULL; + xp->advances = NULL; + + if (!(xp->e_rotor = PyMem_NEW(unsigned char, num_rotors * xp->size))) + goto finally; + if (!(xp->d_rotor = PyMem_NEW(unsigned char, num_rotors * xp->size))) + goto finally; + if (!(xp->positions = PyMem_NEW(unsigned char, num_rotors))) + goto finally; + if (!(xp->advances = PyMem_NEW(unsigned char, num_rotors))) + goto finally; + + return xp; + + finally: + if (xp->e_rotor) + PyMem_DEL(xp->e_rotor); + if (xp->d_rotor) + PyMem_DEL(xp->d_rotor); + if (xp->positions) + PyMem_DEL(xp->positions); + if (xp->advances) + PyMem_DEL(xp->advances); + Py_DECREF(xp); + return (Rotorobj*)PyErr_NoMemory(); +} + + +/* These routines implement the rotor itself */ + +/* Here is a fairly sophisticated {en,de}cryption system. It is based on + the idea of a "rotor" machine. A bunch of rotors, each with a + different permutation of the alphabet, rotate around a different amount + after encrypting one character. The current state of the rotors is + used to encrypt one character. + + The code is smart enough to tell if your alphabet has a number of + characters equal to a power of two. If it does, it uses logical + operations, if not it uses div and mod (both require a division). + + You will need to make two changes to the code 1) convert to c, and + customize for an alphabet of 255 chars 2) add a filter at the begining, + and end, which subtracts one on the way in, and adds one on the way + out. + + You might wish to do some timing studies. Another viable alternative + is to "byte stuff" the encrypted data of a normal (perhaps this one) + encryption routine. + + j' + + */ + +/* Note: the C code here is a fairly straightforward transliteration of a + * rotor implemented in lisp. The original lisp code has been removed from + * this file to for simplification, but I've kept the docstrings as + * comments in front of the functions. + */ + + +/* Set ROTOR to the identity permutation */ +static void +RTR_make_id_rotor(Rotorobj *r, unsigned char *rtr) +{ + register int j; + register int size = r->size; + for (j = 0; j < size; j++) { + rtr[j] = (unsigned char)j; + } +} + + +/* The current set of encryption rotors */ +static void +RTR_e_rotors(Rotorobj *r) +{ + int i; + for (i = 0; i < r->rotors; i++) { + RTR_make_id_rotor(r, &(r->e_rotor[(i*r->size)])); + } +} + +/* The current set of decryption rotors */ +static void +RTR_d_rotors(Rotorobj *r) +{ + register int i, j; + for (i = 0; i < r->rotors; i++) { + for (j = 0; j < r->size; j++) { + r->d_rotor[((i*r->size)+j)] = (unsigned char)j; + } + } +} + +/* The positions of the rotors at this time */ +static void +RTR_positions(Rotorobj *r) +{ + int i; + for (i = 0; i < r->rotors; i++) { + r->positions[i] = 1; + } +} + +/* The number of positions to advance the rotors at a time */ +static void +RTR_advances(Rotorobj *r) +{ + int i; + for (i = 0; i < r->rotors; i++) { + r->advances[i] = 1; + } +} + +/* Permute the E rotor, and make the D rotor its inverse + * see Knuth for explanation of algorithm. + */ +static void +RTR_permute_rotor(Rotorobj *r, unsigned char *e, unsigned char *d) +{ + short i = r->size; + short q; + unsigned char j; + RTR_make_id_rotor(r,e); + while (2 <= i) { + q = r_rand(r,i); + i--; + j = e[q]; + e[q] = (unsigned char)e[i]; + e[i] = (unsigned char)j; + d[j] = (unsigned char)i; + } + e[0] = (unsigned char)e[0]; + d[(e[0])] = (unsigned char)0; +} + +/* Given KEY (a list of 5 16 bit numbers), initialize the rotor machine. + * Set the advancement, position, and permutation of the rotors + */ +static void +RTR_init(Rotorobj *r) +{ + int i; + set_seed(r); + RTR_positions(r); + RTR_advances(r); + RTR_e_rotors(r); + RTR_d_rotors(r); + for (i = 0; i < r->rotors; i++) { + r->positions[i] = (unsigned char) r_rand(r, (short)r->size); + r->advances[i] = (1+(2*(r_rand(r, (short)(r->size/2))))); + RTR_permute_rotor(r, + &(r->e_rotor[(i*r->size)]), + &(r->d_rotor[(i*r->size)])); + } + r->isinited = TRUE; +} + +/* Change the RTR-positions vector, using the RTR-advances vector */ +static void +RTR_advance(Rotorobj *r) +{ + register int i=0, temp=0; + if (r->size_mask) { + while (i < r->rotors) { + temp = r->positions[i] + r->advances[i]; + r->positions[i] = temp & r->size_mask; + if ((temp >= r->size) && (i < (r->rotors - 1))) { + r->positions[(i+1)] = 1 + r->positions[(i+1)]; + } + i++; + } + } else { + while (i < r->rotors) { + temp = r->positions[i] + r->advances[i]; + r->positions[i] = temp%r->size; + if ((temp >= r->size) && (i < (r->rotors - 1))) { + r->positions[(i+1)] = 1 + r->positions[(i+1)]; + } + i++; + } + } +} + +/* Encrypt the character P with the current rotor machine */ +static unsigned char +RTR_e_char(Rotorobj *r, unsigned char p) +{ + register int i=0; + register unsigned char tp=p; + if (r->size_mask) { + while (i < r->rotors) { + tp = r->e_rotor[(i*r->size) + + (((r->positions[i] ^ tp) & + r->size_mask))]; + i++; + } + } else { + while (i < r->rotors) { + tp = r->e_rotor[(i*r->size) + + (((r->positions[i] ^ tp) % + (unsigned int) r->size))]; + i++; + } + } + RTR_advance(r); + return ((unsigned char)tp); +} + +/* Decrypt the character C with the current rotor machine */ +static unsigned char +RTR_d_char(Rotorobj *r, unsigned char c) +{ + register int i = r->rotors - 1; + register unsigned char tc = c; + + if (r->size_mask) { + while (0 <= i) { + tc = (r->positions[i] ^ + r->d_rotor[(i*r->size)+tc]) & r->size_mask; + i--; + } + } else { + while (0 <= i) { + tc = (r->positions[i] ^ + r->d_rotor[(i*r->size)+tc]) % + (unsigned int) r->size; + i--; + } + } + RTR_advance(r); + return(tc); +} + +/* Perform a rotor encryption of the region from BEG to END by KEY */ +static void +RTR_e_region(Rotorobj *r, unsigned char *beg, int len, int doinit) +{ + register int i; + if (doinit || r->isinited == FALSE) + RTR_init(r); + for (i = 0; i < len; i++) { + beg[i] = RTR_e_char(r, beg[i]); + } +} + +/* Perform a rotor decryption of the region from BEG to END by KEY */ +static void +RTR_d_region(Rotorobj *r, unsigned char *beg, int len, int doinit) +{ + register int i; + if (doinit || r->isinited == FALSE) + RTR_init(r); + for (i = 0; i < len; i++) { + beg[i] = RTR_d_char(r, beg[i]); + } +} + + + +/* Rotor methods */ +static void +rotor_dealloc(Rotorobj *xp) +{ + if (xp->e_rotor) + PyMem_DEL(xp->e_rotor); + if (xp->d_rotor) + PyMem_DEL(xp->d_rotor); + if (xp->positions) + PyMem_DEL(xp->positions); + if (xp->advances) + PyMem_DEL(xp->advances); + PyObject_Del(xp); +} + +static PyObject * +rotorobj_encrypt(Rotorobj *self, PyObject *args) +{ + char *string = NULL; + int len = 0; + PyObject *rtn = NULL; + char *tmp; + + if (!PyArg_ParseTuple(args, "s#:encrypt", &string, &len)) + return NULL; + if (!(tmp = PyMem_NEW(char, len+5))) { + PyErr_NoMemory(); + return NULL; + } + memset(tmp, '\0', len+1); + memcpy(tmp, string, len); + RTR_e_region(self, (unsigned char *)tmp, len, TRUE); + rtn = PyString_FromStringAndSize(tmp, len); + PyMem_DEL(tmp); + return(rtn); +} + +static PyObject * +rotorobj_encrypt_more(Rotorobj *self, PyObject *args) +{ + char *string = NULL; + int len = 0; + PyObject *rtn = NULL; + char *tmp; + + if (!PyArg_ParseTuple(args, "s#:encrypt_more", &string, &len)) + return NULL; + if (!(tmp = PyMem_NEW(char, len+5))) { + PyErr_NoMemory(); + return NULL; + } + memset(tmp, '\0', len+1); + memcpy(tmp, string, len); + RTR_e_region(self, (unsigned char *)tmp, len, FALSE); + rtn = PyString_FromStringAndSize(tmp, len); + PyMem_DEL(tmp); + return(rtn); +} + +static PyObject * +rotorobj_decrypt(Rotorobj *self, PyObject *args) +{ + char *string = NULL; + int len = 0; + PyObject *rtn = NULL; + char *tmp; + + if (!PyArg_ParseTuple(args, "s#:decrypt", &string, &len)) + return NULL; + if (!(tmp = PyMem_NEW(char, len+5))) { + PyErr_NoMemory(); + return NULL; + } + memset(tmp, '\0', len+1); + memcpy(tmp, string, len); + RTR_d_region(self, (unsigned char *)tmp, len, TRUE); + rtn = PyString_FromStringAndSize(tmp, len); + PyMem_DEL(tmp); + return(rtn); +} + +static PyObject * +rotorobj_decrypt_more(Rotorobj *self, PyObject *args) +{ + char *string = NULL; + int len = 0; + PyObject *rtn = NULL; + char *tmp; + + if (!PyArg_ParseTuple(args, "s#:decrypt_more", &string, &len)) + return NULL; + if (!(tmp = PyMem_NEW(char, len+5))) { + PyErr_NoMemory(); + return NULL; + } + memset(tmp, '\0', len+1); + memcpy(tmp, string, len); + RTR_d_region(self, (unsigned char *)tmp, len, FALSE); + rtn = PyString_FromStringAndSize(tmp, len); + PyMem_DEL(tmp); + return(rtn); +} + +static PyObject * +rotorobj_setkey(Rotorobj *self, PyObject *args) +{ + char *key; + + if (!PyArg_ParseTuple(args, "s:setkey", &key)) + return NULL; + + set_key(self, key); + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef +rotorobj_methods[] = { + {"encrypt", (PyCFunction)rotorobj_encrypt, METH_VARARGS}, + {"encryptmore", (PyCFunction)rotorobj_encrypt_more, METH_VARARGS}, + {"decrypt", (PyCFunction)rotorobj_decrypt, METH_VARARGS}, + {"decryptmore", (PyCFunction)rotorobj_decrypt_more, METH_VARARGS}, + {"setkey", (PyCFunction)rotorobj_setkey, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +/* Return a rotor object's named attribute. */ +static PyObject * +rotorobj_getattr(Rotorobj *s, char *name) +{ + return Py_FindMethod(rotorobj_methods, (PyObject*)s, name); +} + + +static PyTypeObject Rotor_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "rotor.rotor", /*tp_name*/ + sizeof(Rotorobj), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)rotor_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)rotorobj_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_hash*/ +}; + + +static PyObject * +rotor_rotor(PyObject *self, PyObject *args) +{ + Rotorobj *r; + char *string; + int num_rotors = 6; + + if (!PyArg_ParseTuple(args, "s|i:newrotor", &string, &num_rotors)) + return NULL; + + r = rotorobj_new(num_rotors, string); + return (PyObject *)r; +} + + + +static struct PyMethodDef +rotor_methods[] = { + {"newrotor", rotor_rotor, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +PyMODINIT_FUNC +initrotor(void) +{ + Rotor_Type.ob_type = &PyType_Type; + (void)Py_InitModule("rotor", rotor_methods); + if (PyErr_Warn(PyExc_DeprecationWarning, + "the rotor module uses an insecure algorithm " + "and is deprecated") < 0) + return; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/selectmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/selectmodule.c new file mode 100644 index 00000000..0199f914 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/selectmodule.c @@ -0,0 +1,688 @@ +/* select - Module containing unix select(2) call. + Under Unix, the file descriptors are small integers. + Under Win32, select only exists for sockets, and sockets may + have any value except INVALID_SOCKET. + Under BeOS, we suffer the same dichotomy as Win32; sockets can be anything + >= 0. +*/ + +#include "Python.h" + +/* Windows #defines FD_SETSIZE to 64 if FD_SETSIZE isn't already defined. + 64 is too small (too many people have bumped into that limit). + Here we boost it. + Users who want even more than the boosted limit should #define + FD_SETSIZE higher before this; e.g., via compiler /D switch. +*/ +#if defined(MS_WINDOWS) && !defined(FD_SETSIZE) +#define FD_SETSIZE 512 +#endif + +#if defined(HAVE_POLL_H) +#include +#elif defined(HAVE_SYS_POLL_H) +#include +#endif + +#ifdef __sgi +/* This is missing from unistd.h */ +extern void bzero(void *, int); +#endif + +#ifndef DONT_HAVE_SYS_TYPES_H +#include +#endif + +#if defined(PYOS_OS2) && !defined(PYCC_GCC) +#include +#include +#endif + +#ifdef MS_WINDOWS +#include +#else +#ifdef __BEOS__ +#include +#define SOCKET int +#else +#define SOCKET int +#endif +#endif + + +static PyObject *SelectError; + +/* list of Python objects and their file descriptor */ +typedef struct { + PyObject *obj; /* owned reference */ + SOCKET fd; + int sentinel; /* -1 == sentinel */ +} pylist; + +static void +reap_obj(pylist fd2obj[FD_SETSIZE + 1]) +{ + int i; + for (i = 0; i < FD_SETSIZE + 1 && fd2obj[i].sentinel >= 0; i++) { + Py_XDECREF(fd2obj[i].obj); + fd2obj[i].obj = NULL; + } + fd2obj[0].sentinel = -1; +} + + +/* returns -1 and sets the Python exception if an error occurred, otherwise + returns a number >= 0 +*/ +static int +list2set(PyObject *list, fd_set *set, pylist fd2obj[FD_SETSIZE + 1]) +{ + int i; + int max = -1; + int index = 0; + int len = PyList_Size(list); + PyObject* o = NULL; + + fd2obj[0].obj = (PyObject*)0; /* set list to zero size */ + FD_ZERO(set); + + for (i = 0; i < len; i++) { + SOCKET v; + + /* any intervening fileno() calls could decr this refcnt */ + if (!(o = PyList_GetItem(list, i))) + return -1; + + Py_INCREF(o); + v = PyObject_AsFileDescriptor( o ); + if (v == -1) goto finally; + +#if defined(_MSC_VER) + max = 0; /* not used for Win32 */ +#else /* !_MSC_VER */ + if (v < 0 || v >= FD_SETSIZE) { + PyErr_SetString(PyExc_ValueError, + "filedescriptor out of range in select()"); + goto finally; + } + if (v > max) + max = v; +#endif /* _MSC_VER */ + FD_SET(v, set); + + /* add object and its file descriptor to the list */ + if (index >= FD_SETSIZE) { + PyErr_SetString(PyExc_ValueError, + "too many file descriptors in select()"); + goto finally; + } + fd2obj[index].obj = o; + fd2obj[index].fd = v; + fd2obj[index].sentinel = 0; + fd2obj[++index].sentinel = -1; + } + return max+1; + + finally: + Py_XDECREF(o); + return -1; +} + +/* returns NULL and sets the Python exception if an error occurred */ +static PyObject * +set2list(fd_set *set, pylist fd2obj[FD_SETSIZE + 1]) +{ + int i, j, count=0; + PyObject *list, *o; + SOCKET fd; + + for (j = 0; fd2obj[j].sentinel >= 0; j++) { + if (FD_ISSET(fd2obj[j].fd, set)) + count++; + } + list = PyList_New(count); + if (!list) + return NULL; + + i = 0; + for (j = 0; fd2obj[j].sentinel >= 0; j++) { + fd = fd2obj[j].fd; + if (FD_ISSET(fd, set)) { +#ifndef _MSC_VER + if (fd > FD_SETSIZE) { + PyErr_SetString(PyExc_SystemError, + "filedescriptor out of range returned in select()"); + goto finally; + } +#endif + o = fd2obj[j].obj; + fd2obj[j].obj = NULL; + /* transfer ownership */ + if (PyList_SetItem(list, i, o) < 0) + goto finally; + + i++; + } + } + return list; + finally: + Py_DECREF(list); + return NULL; +} + +#undef SELECT_USES_HEAP +#if FD_SETSIZE > 1024 +#define SELECT_USES_HEAP +#endif /* FD_SETSIZE > 1024 */ + +static PyObject * +select_select(PyObject *self, PyObject *args) +{ +#ifdef SELECT_USES_HEAP + pylist *rfd2obj, *wfd2obj, *efd2obj; +#else /* !SELECT_USES_HEAP */ + /* XXX: All this should probably be implemented as follows: + * - find the highest descriptor we're interested in + * - add one + * - that's the size + * See: Stevens, APitUE, $12.5.1 + */ + pylist rfd2obj[FD_SETSIZE + 1]; + pylist wfd2obj[FD_SETSIZE + 1]; + pylist efd2obj[FD_SETSIZE + 1]; +#endif /* SELECT_USES_HEAP */ + PyObject *ifdlist, *ofdlist, *efdlist; + PyObject *ret = NULL; + PyObject *tout = Py_None; + fd_set ifdset, ofdset, efdset; + double timeout; + struct timeval tv, *tvp; + long seconds; + int imax, omax, emax, max; + int n; + + /* convert arguments */ + if (!PyArg_ParseTuple(args, "OOO|O:select", + &ifdlist, &ofdlist, &efdlist, &tout)) + return NULL; + + if (tout == Py_None) + tvp = (struct timeval *)0; + else if (!PyNumber_Check(tout)) { + PyErr_SetString(PyExc_TypeError, + "timeout must be a float or None"); + return NULL; + } + else { + timeout = PyFloat_AsDouble(tout); + if (timeout == -1 && PyErr_Occurred()) + return NULL; + if (timeout > (double)LONG_MAX) { + PyErr_SetString(PyExc_OverflowError, + "timeout period too long"); + return NULL; + } + seconds = (long)timeout; + timeout = timeout - (double)seconds; + tv.tv_sec = seconds; + tv.tv_usec = (long)(timeout*1000000.0); + tvp = &tv; + } + + /* sanity check first three arguments */ + if (!PyList_Check(ifdlist) || + !PyList_Check(ofdlist) || + !PyList_Check(efdlist)) + { + PyErr_SetString(PyExc_TypeError, + "arguments 1-3 must be lists"); + return NULL; + } + +#ifdef SELECT_USES_HEAP + /* Allocate memory for the lists */ + rfd2obj = PyMem_NEW(pylist, FD_SETSIZE + 1); + wfd2obj = PyMem_NEW(pylist, FD_SETSIZE + 1); + efd2obj = PyMem_NEW(pylist, FD_SETSIZE + 1); + if (rfd2obj == NULL || wfd2obj == NULL || efd2obj == NULL) { + if (rfd2obj) PyMem_DEL(rfd2obj); + if (wfd2obj) PyMem_DEL(wfd2obj); + if (efd2obj) PyMem_DEL(efd2obj); + return PyErr_NoMemory(); + } +#endif /* SELECT_USES_HEAP */ + /* Convert lists to fd_sets, and get maximum fd number + * propagates the Python exception set in list2set() + */ + rfd2obj[0].sentinel = -1; + wfd2obj[0].sentinel = -1; + efd2obj[0].sentinel = -1; + if ((imax=list2set(ifdlist, &ifdset, rfd2obj)) < 0) + goto finally; + if ((omax=list2set(ofdlist, &ofdset, wfd2obj)) < 0) + goto finally; + if ((emax=list2set(efdlist, &efdset, efd2obj)) < 0) + goto finally; + max = imax; + if (omax > max) max = omax; + if (emax > max) max = emax; + + Py_BEGIN_ALLOW_THREADS + n = select(max, &ifdset, &ofdset, &efdset, tvp); + Py_END_ALLOW_THREADS + +#ifdef MS_WINDOWS + if (n == SOCKET_ERROR) { + PyErr_SetExcFromWindowsErr(SelectError, WSAGetLastError()); + } +#else + if (n < 0) { + PyErr_SetFromErrno(SelectError); + } +#endif + else if (n == 0) { + /* optimization */ + ifdlist = PyList_New(0); + if (ifdlist) { + ret = Py_BuildValue("OOO", ifdlist, ifdlist, ifdlist); + Py_DECREF(ifdlist); + } + } + else { + /* any of these three calls can raise an exception. it's more + convenient to test for this after all three calls... but + is that acceptable? + */ + ifdlist = set2list(&ifdset, rfd2obj); + ofdlist = set2list(&ofdset, wfd2obj); + efdlist = set2list(&efdset, efd2obj); + if (PyErr_Occurred()) + ret = NULL; + else + ret = Py_BuildValue("OOO", ifdlist, ofdlist, efdlist); + + Py_DECREF(ifdlist); + Py_DECREF(ofdlist); + Py_DECREF(efdlist); + } + + finally: + reap_obj(rfd2obj); + reap_obj(wfd2obj); + reap_obj(efd2obj); +#ifdef SELECT_USES_HEAP + PyMem_DEL(rfd2obj); + PyMem_DEL(wfd2obj); + PyMem_DEL(efd2obj); +#endif /* SELECT_USES_HEAP */ + return ret; +} + +#ifdef HAVE_POLL +/* + * poll() support + */ + +typedef struct { + PyObject_HEAD + PyObject *dict; + int ufd_uptodate; + int ufd_len; + struct pollfd *ufds; +} pollObject; + +static PyTypeObject poll_Type; + +/* Update the malloc'ed array of pollfds to match the dictionary + contained within a pollObject. Return 1 on success, 0 on an error. +*/ + +static int +update_ufd_array(pollObject *self) +{ + int i, pos; + PyObject *key, *value; + + self->ufd_len = PyDict_Size(self->dict); + PyMem_Resize(self->ufds, struct pollfd, self->ufd_len); + if (self->ufds == NULL) { + PyErr_NoMemory(); + return 0; + } + + i = pos = 0; + while (PyDict_Next(self->dict, &pos, &key, &value)) { + self->ufds[i].fd = PyInt_AsLong(key); + self->ufds[i].events = (short)PyInt_AsLong(value); + i++; + } + self->ufd_uptodate = 1; + return 1; +} + +PyDoc_STRVAR(poll_register_doc, +"register(fd [, eventmask] ) -> None\n\n\ +Register a file descriptor with the polling object.\n\ +fd -- either an integer, or an object with a fileno() method returning an\n\ + int.\n\ +events -- an optional bitmask describing the type of events to check for"); + +static PyObject * +poll_register(pollObject *self, PyObject *args) +{ + PyObject *o, *key, *value; + int fd, events = POLLIN | POLLPRI | POLLOUT; + int err; + + if (!PyArg_ParseTuple(args, "O|i:register", &o, &events)) { + return NULL; + } + + fd = PyObject_AsFileDescriptor(o); + if (fd == -1) return NULL; + + /* Add entry to the internal dictionary: the key is the + file descriptor, and the value is the event mask. */ + key = PyInt_FromLong(fd); + if (key == NULL) + return NULL; + value = PyInt_FromLong(events); + if (value == NULL) { + Py_DECREF(key); + return NULL; + } + err = PyDict_SetItem(self->dict, key, value); + Py_DECREF(key); + Py_DECREF(value); + if (err < 0) + return NULL; + + self->ufd_uptodate = 0; + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(poll_unregister_doc, +"unregister(fd) -> None\n\n\ +Remove a file descriptor being tracked by the polling object."); + +static PyObject * +poll_unregister(pollObject *self, PyObject *args) +{ + PyObject *o, *key; + int fd; + + if (!PyArg_ParseTuple(args, "O:unregister", &o)) { + return NULL; + } + + fd = PyObject_AsFileDescriptor( o ); + if (fd == -1) + return NULL; + + /* Check whether the fd is already in the array */ + key = PyInt_FromLong(fd); + if (key == NULL) + return NULL; + + if (PyDict_DelItem(self->dict, key) == -1) { + Py_DECREF(key); + /* This will simply raise the KeyError set by PyDict_DelItem + if the file descriptor isn't registered. */ + return NULL; + } + + Py_DECREF(key); + self->ufd_uptodate = 0; + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(poll_poll_doc, +"poll( [timeout] ) -> list of (fd, event) 2-tuples\n\n\ +Polls the set of registered file descriptors, returning a list containing \n\ +any descriptors that have events or errors to report."); + +static PyObject * +poll_poll(pollObject *self, PyObject *args) +{ + PyObject *result_list = NULL, *tout = NULL; + int timeout = 0, poll_result, i, j; + PyObject *value = NULL, *num = NULL; + + if (!PyArg_ParseTuple(args, "|O:poll", &tout)) { + return NULL; + } + + /* Check values for timeout */ + if (tout == NULL || tout == Py_None) + timeout = -1; + else if (!PyNumber_Check(tout)) { + PyErr_SetString(PyExc_TypeError, + "timeout must be an integer or None"); + return NULL; + } + else { + tout = PyNumber_Int(tout); + if (!tout) + return NULL; + timeout = PyInt_AsLong(tout); + Py_DECREF(tout); + } + + /* Ensure the ufd array is up to date */ + if (!self->ufd_uptodate) + if (update_ufd_array(self) == 0) + return NULL; + + /* call poll() */ + Py_BEGIN_ALLOW_THREADS; + poll_result = poll(self->ufds, self->ufd_len, timeout); + Py_END_ALLOW_THREADS; + + if (poll_result < 0) { + PyErr_SetFromErrno(SelectError); + return NULL; + } + + /* build the result list */ + + result_list = PyList_New(poll_result); + if (!result_list) + return NULL; + else { + for (i = 0, j = 0; j < poll_result; j++) { + /* skip to the next fired descriptor */ + while (!self->ufds[i].revents) { + i++; + } + /* if we hit a NULL return, set value to NULL + and break out of loop; code at end will + clean up result_list */ + value = PyTuple_New(2); + if (value == NULL) + goto error; + num = PyInt_FromLong(self->ufds[i].fd); + if (num == NULL) { + Py_DECREF(value); + goto error; + } + PyTuple_SET_ITEM(value, 0, num); + + num = PyInt_FromLong(self->ufds[i].revents); + if (num == NULL) { + Py_DECREF(value); + goto error; + } + PyTuple_SET_ITEM(value, 1, num); + if ((PyList_SetItem(result_list, j, value)) == -1) { + Py_DECREF(value); + goto error; + } + i++; + } + } + return result_list; + + error: + Py_DECREF(result_list); + return NULL; +} + +static PyMethodDef poll_methods[] = { + {"register", (PyCFunction)poll_register, + METH_VARARGS, poll_register_doc}, + {"unregister", (PyCFunction)poll_unregister, + METH_VARARGS, poll_unregister_doc}, + {"poll", (PyCFunction)poll_poll, + METH_VARARGS, poll_poll_doc}, + {NULL, NULL} /* sentinel */ +}; + +static pollObject * +newPollObject(void) +{ + pollObject *self; + self = PyObject_New(pollObject, &poll_Type); + if (self == NULL) + return NULL; + /* ufd_uptodate is a Boolean, denoting whether the + array pointed to by ufds matches the contents of the dictionary. */ + self->ufd_uptodate = 0; + self->ufds = NULL; + self->dict = PyDict_New(); + if (self->dict == NULL) { + Py_DECREF(self); + return NULL; + } + return self; +} + +static void +poll_dealloc(pollObject *self) +{ + if (self->ufds != NULL) + PyMem_DEL(self->ufds); + Py_XDECREF(self->dict); + PyObject_Del(self); +} + +static PyObject * +poll_getattr(pollObject *self, char *name) +{ + return Py_FindMethod(poll_methods, (PyObject *)self, name); +} + +static PyTypeObject poll_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "select.poll", /*tp_name*/ + sizeof(pollObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)poll_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)poll_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + +PyDoc_STRVAR(poll_doc, +"Returns a polling object, which supports registering and\n\ +unregistering file descriptors, and then polling them for I/O events."); + +static PyObject * +select_poll(PyObject *self, PyObject *args) +{ + pollObject *rv; + + if (!PyArg_ParseTuple(args, ":poll")) + return NULL; + rv = newPollObject(); + if ( rv == NULL ) + return NULL; + return (PyObject *)rv; +} +#endif /* HAVE_POLL */ + +PyDoc_STRVAR(select_doc, +"select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)\n\ +\n\ +Wait until one or more file descriptors are ready for some kind of I/O.\n\ +The first three arguments are lists of file descriptors to be waited for:\n\ +rlist -- wait until ready for reading\n\ +wlist -- wait until ready for writing\n\ +xlist -- wait for an ``exceptional condition''\n\ +If only one kind of condition is required, pass [] for the other lists.\n\ +A file descriptor is either a socket or file object, or a small integer\n\ +gotten from a fileno() method call on one of those.\n\ +\n\ +The optional 4th argument specifies a timeout in seconds; it may be\n\ +a floating point number to specify fractions of seconds. If it is absent\n\ +or None, the call will never time out.\n\ +\n\ +The return value is a tuple of three lists corresponding to the first three\n\ +arguments; each contains the subset of the corresponding file descriptors\n\ +that are ready.\n\ +\n\ +*** IMPORTANT NOTICE ***\n\ +On Windows, only sockets are supported; on Unix, all file descriptors."); + +static PyMethodDef select_methods[] = { + {"select", select_select, METH_VARARGS, select_doc}, +#ifdef HAVE_POLL + {"poll", select_poll, METH_VARARGS, poll_doc}, +#endif /* HAVE_POLL */ + {0, 0}, /* sentinel */ +}; + +PyDoc_STRVAR(module_doc, +"This module supports asynchronous I/O on multiple file descriptors.\n\ +\n\ +*** IMPORTANT NOTICE ***\n\ +On Windows, only sockets are supported; on Unix, all file descriptors."); + +PyMODINIT_FUNC +initselect(void) +{ + PyObject *m; + m = Py_InitModule3("select", select_methods, module_doc); + + SelectError = PyErr_NewException("select.error", NULL, NULL); + Py_INCREF(SelectError); + PyModule_AddObject(m, "error", SelectError); +#ifdef HAVE_POLL + poll_Type.ob_type = &PyType_Type; + PyModule_AddIntConstant(m, "POLLIN", POLLIN); + PyModule_AddIntConstant(m, "POLLPRI", POLLPRI); + PyModule_AddIntConstant(m, "POLLOUT", POLLOUT); + PyModule_AddIntConstant(m, "POLLERR", POLLERR); + PyModule_AddIntConstant(m, "POLLHUP", POLLHUP); + PyModule_AddIntConstant(m, "POLLNVAL", POLLNVAL); + +#ifdef POLLRDNORM + PyModule_AddIntConstant(m, "POLLRDNORM", POLLRDNORM); +#endif +#ifdef POLLRDBAND + PyModule_AddIntConstant(m, "POLLRDBAND", POLLRDBAND); +#endif +#ifdef POLLWRNORM + PyModule_AddIntConstant(m, "POLLWRNORM", POLLWRNORM); +#endif +#ifdef POLLWRBAND + PyModule_AddIntConstant(m, "POLLWRBAND", POLLWRBAND); +#endif +#ifdef POLLMSG + PyModule_AddIntConstant(m, "POLLMSG", POLLMSG); +#endif +#endif /* HAVE_POLL */ +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sgimodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sgimodule.c new file mode 100644 index 00000000..6923d3c9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sgimodule.c @@ -0,0 +1,55 @@ + +/* SGI module -- random SGI-specific things */ + +#include "Python.h" + +#include +#include +#include + +static PyObject * +sgi_nap(PyObject *self, PyObject *args) +{ + long ticks; + if (!PyArg_ParseTuple(args, "l:nap", &ticks)) + return NULL; + Py_BEGIN_ALLOW_THREADS + sginap(ticks); + Py_END_ALLOW_THREADS + Py_INCREF(Py_None); + return Py_None; +} + +extern char *_getpty(int *, int, mode_t, int); + +static PyObject * +sgi__getpty(PyObject *self, PyObject *args) +{ + int oflag; + int mode; + int nofork; + char *name; + int fildes; + if (!PyArg_ParseTuple(args, "iii:_getpty", &oflag, &mode, &nofork)) + return NULL; + errno = 0; + name = _getpty(&fildes, oflag, (mode_t)mode, nofork); + if (name == NULL) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return Py_BuildValue("(si)", name, fildes); +} + +static PyMethodDef sgi_methods[] = { + {"nap", sgi_nap, METH_VARARGS}, + {"_getpty", sgi__getpty, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + + +void +initsgi(void) +{ + Py_InitModule("sgi", sgi_methods); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/shamodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/shamodule.c new file mode 100644 index 00000000..13394713 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/shamodule.c @@ -0,0 +1,542 @@ +/* SHA module */ + +/* This module provides an interface to NIST's Secure Hash Algorithm */ + +/* See below for information about the original code this module was + based upon. Additional work performed by: + + Andrew Kuchling (amk@amk.ca) + Greg Stein (gstein@lyra.org) +*/ + +/* SHA objects */ + +#include "Python.h" + + +/* Endianness testing and definitions */ +#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\ + if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;} + +#define PCT_LITTLE_ENDIAN 1 +#define PCT_BIG_ENDIAN 0 + +/* Some useful types */ + +typedef unsigned char SHA_BYTE; + +#if SIZEOF_INT == 4 +typedef unsigned int SHA_INT32; /* 32-bit integer */ +#else +/* not defined. compilation will die. */ +#endif + +/* The SHA block size and message digest sizes, in bytes */ + +#define SHA_BLOCKSIZE 64 +#define SHA_DIGESTSIZE 20 + +/* The structure for storing SHS info */ + +typedef struct { + PyObject_HEAD + SHA_INT32 digest[5]; /* Message digest */ + SHA_INT32 count_lo, count_hi; /* 64-bit bit count */ + SHA_BYTE data[SHA_BLOCKSIZE]; /* SHA data buffer */ + int Endianness; + int local; /* unprocessed amount in data */ +} SHAobject; + +/* When run on a little-endian CPU we need to perform byte reversal on an + array of longwords. */ + +static void longReverse(SHA_INT32 *buffer, int byteCount, int Endianness) +{ + SHA_INT32 value; + + if ( Endianness == PCT_BIG_ENDIAN ) + return; + + byteCount /= sizeof(*buffer); + while (byteCount--) { + value = *buffer; + value = ( ( value & 0xFF00FF00L ) >> 8 ) | \ + ( ( value & 0x00FF00FFL ) << 8 ); + *buffer++ = ( value << 16 ) | ( value >> 16 ); + } +} + +static void SHAcopy(SHAobject *src, SHAobject *dest) +{ + dest->Endianness = src->Endianness; + dest->local = src->local; + dest->count_lo = src->count_lo; + dest->count_hi = src->count_hi; + memcpy(dest->digest, src->digest, sizeof(src->digest)); + memcpy(dest->data, src->data, sizeof(src->data)); +} + + +/* ------------------------------------------------------------------------ + * + * This code for the SHA algorithm was noted as public domain. The original + * headers are pasted below. + * + * Several changes have been made to make it more compatible with the + * Python environment and desired interface. + * + */ + +/* NIST Secure Hash Algorithm */ +/* heavily modified by Uwe Hollerbach */ +/* from Peter C. Gutmann's implementation as found in */ +/* Applied Cryptography by Bruce Schneier */ +/* Further modifications to include the "UNRAVEL" stuff, below */ + +/* This code is in the public domain */ + +/* UNRAVEL should be fastest & biggest */ +/* UNROLL_LOOPS should be just as big, but slightly slower */ +/* both undefined should be smallest and slowest */ + +#define UNRAVEL +/* #define UNROLL_LOOPS */ + +/* The SHA f()-functions. The f1 and f3 functions can be optimized to + save one boolean operation each - thanks to Rich Schroeppel, + rcs@cs.arizona.edu for discovering this */ + +/*#define f1(x,y,z) ((x & y) | (~x & z)) // Rounds 0-19 */ +#define f1(x,y,z) (z ^ (x & (y ^ z))) /* Rounds 0-19 */ +#define f2(x,y,z) (x ^ y ^ z) /* Rounds 20-39 */ +/*#define f3(x,y,z) ((x & y) | (x & z) | (y & z)) // Rounds 40-59 */ +#define f3(x,y,z) ((x & y) | (z & (x | y))) /* Rounds 40-59 */ +#define f4(x,y,z) (x ^ y ^ z) /* Rounds 60-79 */ + +/* SHA constants */ + +#define CONST1 0x5a827999L /* Rounds 0-19 */ +#define CONST2 0x6ed9eba1L /* Rounds 20-39 */ +#define CONST3 0x8f1bbcdcL /* Rounds 40-59 */ +#define CONST4 0xca62c1d6L /* Rounds 60-79 */ + +/* 32-bit rotate */ + +#define R32(x,n) ((x << n) | (x >> (32 - n))) + +/* the generic case, for when the overall rotation is not unraveled */ + +#define FG(n) \ + T = R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n; \ + E = D; D = C; C = R32(B,30); B = A; A = T + +/* specific cases, for when the overall rotation is unraveled */ + +#define FA(n) \ + T = R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n; B = R32(B,30) + +#define FB(n) \ + E = R32(T,5) + f##n(A,B,C) + D + *WP++ + CONST##n; A = R32(A,30) + +#define FC(n) \ + D = R32(E,5) + f##n(T,A,B) + C + *WP++ + CONST##n; T = R32(T,30) + +#define FD(n) \ + C = R32(D,5) + f##n(E,T,A) + B + *WP++ + CONST##n; E = R32(E,30) + +#define FE(n) \ + B = R32(C,5) + f##n(D,E,T) + A + *WP++ + CONST##n; D = R32(D,30) + +#define FT(n) \ + A = R32(B,5) + f##n(C,D,E) + T + *WP++ + CONST##n; C = R32(C,30) + +/* do SHA transformation */ + +static void +sha_transform(SHAobject *sha_info) +{ + int i; + SHA_INT32 T, A, B, C, D, E, W[80], *WP; + + memcpy(W, sha_info->data, sizeof(sha_info->data)); + longReverse(W, (int)sizeof(sha_info->data), sha_info->Endianness); + + for (i = 16; i < 80; ++i) { + W[i] = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]; + + /* extra rotation fix */ + W[i] = R32(W[i], 1); + } + A = sha_info->digest[0]; + B = sha_info->digest[1]; + C = sha_info->digest[2]; + D = sha_info->digest[3]; + E = sha_info->digest[4]; + WP = W; +#ifdef UNRAVEL + FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); + FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); + FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); + FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); + FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); + FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); + FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); + FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); + sha_info->digest[0] += E; + sha_info->digest[1] += T; + sha_info->digest[2] += A; + sha_info->digest[3] += B; + sha_info->digest[4] += C; +#else /* !UNRAVEL */ +#ifdef UNROLL_LOOPS + FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); + FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); + FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); + FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); + FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); + FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); + FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); + FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); +#else /* !UNROLL_LOOPS */ + for (i = 0; i < 20; ++i) { FG(1); } + for (i = 20; i < 40; ++i) { FG(2); } + for (i = 40; i < 60; ++i) { FG(3); } + for (i = 60; i < 80; ++i) { FG(4); } +#endif /* !UNROLL_LOOPS */ + sha_info->digest[0] += A; + sha_info->digest[1] += B; + sha_info->digest[2] += C; + sha_info->digest[3] += D; + sha_info->digest[4] += E; +#endif /* !UNRAVEL */ +} + +/* initialize the SHA digest */ + +static void +sha_init(SHAobject *sha_info) +{ + TestEndianness(sha_info->Endianness) + + sha_info->digest[0] = 0x67452301L; + sha_info->digest[1] = 0xefcdab89L; + sha_info->digest[2] = 0x98badcfeL; + sha_info->digest[3] = 0x10325476L; + sha_info->digest[4] = 0xc3d2e1f0L; + sha_info->count_lo = 0L; + sha_info->count_hi = 0L; + sha_info->local = 0; +} + +/* update the SHA digest */ + +static void +sha_update(SHAobject *sha_info, SHA_BYTE *buffer, int count) +{ + int i; + SHA_INT32 clo; + + clo = sha_info->count_lo + ((SHA_INT32) count << 3); + if (clo < sha_info->count_lo) { + ++sha_info->count_hi; + } + sha_info->count_lo = clo; + sha_info->count_hi += (SHA_INT32) count >> 29; + if (sha_info->local) { + i = SHA_BLOCKSIZE - sha_info->local; + if (i > count) { + i = count; + } + memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); + count -= i; + buffer += i; + sha_info->local += i; + if (sha_info->local == SHA_BLOCKSIZE) { + sha_transform(sha_info); + } + else { + return; + } + } + while (count >= SHA_BLOCKSIZE) { + memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); + buffer += SHA_BLOCKSIZE; + count -= SHA_BLOCKSIZE; + sha_transform(sha_info); + } + memcpy(sha_info->data, buffer, count); + sha_info->local = count; +} + +/* finish computing the SHA digest */ + +static void +sha_final(unsigned char digest[20], SHAobject *sha_info) +{ + int count; + SHA_INT32 lo_bit_count, hi_bit_count; + + lo_bit_count = sha_info->count_lo; + hi_bit_count = sha_info->count_hi; + count = (int) ((lo_bit_count >> 3) & 0x3f); + ((SHA_BYTE *) sha_info->data)[count++] = 0x80; + if (count > SHA_BLOCKSIZE - 8) { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - count); + sha_transform(sha_info); + memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 8); + } + else { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - 8 - count); + } + + /* GJS: note that we add the hi/lo in big-endian. sha_transform will + swap these values into host-order. */ + sha_info->data[56] = (hi_bit_count >> 24) & 0xff; + sha_info->data[57] = (hi_bit_count >> 16) & 0xff; + sha_info->data[58] = (hi_bit_count >> 8) & 0xff; + sha_info->data[59] = (hi_bit_count >> 0) & 0xff; + sha_info->data[60] = (lo_bit_count >> 24) & 0xff; + sha_info->data[61] = (lo_bit_count >> 16) & 0xff; + sha_info->data[62] = (lo_bit_count >> 8) & 0xff; + sha_info->data[63] = (lo_bit_count >> 0) & 0xff; + sha_transform(sha_info); + digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); + digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); + digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff); + digest[ 3] = (unsigned char) ((sha_info->digest[0] ) & 0xff); + digest[ 4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); + digest[ 5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); + digest[ 6] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff); + digest[ 7] = (unsigned char) ((sha_info->digest[1] ) & 0xff); + digest[ 8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); + digest[ 9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); + digest[10] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff); + digest[11] = (unsigned char) ((sha_info->digest[2] ) & 0xff); + digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); + digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); + digest[14] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff); + digest[15] = (unsigned char) ((sha_info->digest[3] ) & 0xff); + digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); + digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); + digest[18] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff); + digest[19] = (unsigned char) ((sha_info->digest[4] ) & 0xff); +} + +/* + * End of copied SHA code. + * + * ------------------------------------------------------------------------ + */ + +static PyTypeObject SHAtype; + + +static SHAobject * +newSHAobject(void) +{ + return (SHAobject *)PyObject_New(SHAobject, &SHAtype); +} + +/* Internal methods for a hashing object */ + +static void +SHA_dealloc(PyObject *ptr) +{ + PyObject_Del(ptr); +} + + +/* External methods for a hashing object */ + +PyDoc_STRVAR(SHA_copy__doc__, "Return a copy of the hashing object."); + +static PyObject * +SHA_copy(SHAobject *self, PyObject *args) +{ + SHAobject *newobj; + + if (!PyArg_ParseTuple(args, ":copy")) { + return NULL; + } + if ( (newobj = newSHAobject())==NULL) + return NULL; + + SHAcopy(self, newobj); + return (PyObject *)newobj; +} + +PyDoc_STRVAR(SHA_digest__doc__, +"Return the digest value as a string of binary data."); + +static PyObject * +SHA_digest(SHAobject *self, PyObject *args) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + + if (!PyArg_ParseTuple(args, ":digest")) + return NULL; + + SHAcopy(self, &temp); + sha_final(digest, &temp); + return PyString_FromStringAndSize((const char *)digest, sizeof(digest)); +} + +PyDoc_STRVAR(SHA_hexdigest__doc__, +"Return the digest value as a string of hexadecimal digits."); + +static PyObject * +SHA_hexdigest(SHAobject *self, PyObject *args) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + PyObject *retval; + char *hex_digest; + int i, j; + + if (!PyArg_ParseTuple(args, ":hexdigest")) + return NULL; + + /* Get the raw (binary) digest value */ + SHAcopy(self, &temp); + sha_final(digest, &temp); + + /* Create a new string */ + retval = PyString_FromStringAndSize(NULL, sizeof(digest) * 2); + if (!retval) + return NULL; + hex_digest = PyString_AsString(retval); + if (!hex_digest) { + Py_DECREF(retval); + return NULL; + } + + /* Make hex version of the digest */ + for(i=j=0; i> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + } + return retval; +} + +PyDoc_STRVAR(SHA_update__doc__, +"Update this hashing object's state with the provided string."); + +static PyObject * +SHA_update(SHAobject *self, PyObject *args) +{ + unsigned char *cp; + int len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + sha_update(self, cp, len); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef SHA_methods[] = { + {"copy", (PyCFunction)SHA_copy, METH_VARARGS, SHA_copy__doc__}, + {"digest", (PyCFunction)SHA_digest, METH_VARARGS, SHA_digest__doc__}, + {"hexdigest", (PyCFunction)SHA_hexdigest, METH_VARARGS, SHA_hexdigest__doc__}, + {"update", (PyCFunction)SHA_update, METH_VARARGS, SHA_update__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +SHA_getattr(PyObject *self, char *name) +{ + if (strcmp(name, "blocksize")==0) + return PyInt_FromLong(1); + if (strcmp(name, "digest_size")==0 || strcmp(name, "digestsize")==0) + return PyInt_FromLong(20); + + return Py_FindMethod(SHA_methods, self, name); +} + +static PyTypeObject SHAtype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "sha.SHA", /*tp_name*/ + sizeof(SHAobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + SHA_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + SHA_getattr, /*tp_getattr*/ +}; + + +/* The single module-level function: new() */ + +PyDoc_STRVAR(SHA_new__doc__, +"Return a new SHA hashing object. An optional string argument\n\ +may be provided; if present, this string will be automatically\n\ +hashed."); + +static PyObject * +SHA_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"string", NULL}; + SHAobject *new; + unsigned char *cp = NULL; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, + &cp, &len)) { + return NULL; + } + + if ((new = newSHAobject()) == NULL) + return NULL; + + sha_init(new); + + if (PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + if (cp) + sha_update(new, cp, len); + + return (PyObject *)new; +} + + +/* List of functions exported by this module */ + +static struct PyMethodDef SHA_functions[] = { + {"new", (PyCFunction)SHA_new, METH_VARARGS|METH_KEYWORDS, SHA_new__doc__}, + {"sha", (PyCFunction)SHA_new, METH_VARARGS|METH_KEYWORDS, SHA_new__doc__}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +#define insint(n,v) { PyModule_AddIntConstant(m,n,v); } + +PyMODINIT_FUNC +initsha(void) +{ + PyObject *m; + + SHAtype.ob_type = &PyType_Type; + m = Py_InitModule("sha", SHA_functions); + + /* Add some symbolic constants to the module */ + insint("blocksize", 1); /* For future use, in case some hash + functions require an integral number of + blocks */ + insint("digestsize", 20); + insint("digest_size", 20); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/signalmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/signalmodule.c new file mode 100644 index 00000000..f68180fb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/signalmodule.c @@ -0,0 +1,674 @@ + +/* Signal module -- many thanks to Lance Ellinghaus */ + +/* XXX Signals should be recorded per thread, now we have thread state. */ + +#include "Python.h" +#include "intrcheck.h" + +#ifdef MS_WINDOWS +#include +#endif + +#include + +#ifndef SIG_ERR +#define SIG_ERR ((PyOS_sighandler_t)(-1)) +#endif + +#if defined(PYOS_OS2) && !defined(PYCC_GCC) +#define NSIG 12 +#include +#endif + +#ifndef NSIG +# if defined(_NSIG) +# define NSIG _NSIG /* For BSD/SysV */ +# elif defined(_SIGMAX) +# define NSIG (_SIGMAX + 1) /* For QNX */ +# elif defined(SIGMAX) +# define NSIG (SIGMAX + 1) /* For djgpp */ +# else +# define NSIG 64 /* Use a reasonable default value */ +# endif +#endif + + +/* + NOTES ON THE INTERACTION BETWEEN SIGNALS AND THREADS + + When threads are supported, we want the following semantics: + + - only the main thread can set a signal handler + - any thread can get a signal handler + - signals are only delivered to the main thread + + I.e. we don't support "synchronous signals" like SIGFPE (catching + this doesn't make much sense in Python anyway) nor do we support + signals as a means of inter-thread communication, since not all + thread implementations support that (at least our thread library + doesn't). + + We still have the problem that in some implementations signals + generated by the keyboard (e.g. SIGINT) are delivered to all + threads (e.g. SGI), while in others (e.g. Solaris) such signals are + delivered to one random thread (an intermediate possibility would + be to deliver it to the main thread -- POSIX?). For now, we have + a working implementation that works in all three cases -- the + handler ignores signals if getpid() isn't the same as in the main + thread. XXX This is a hack. + + GNU pth is a user-space threading library, and as such, all threads + run within the same process. In this case, if the currently running + thread is not the main_thread, send the signal to the main_thread. +*/ + +#ifdef MS_XBOX +// No pid on Xbox +#undef WITH_THREAD +#endif + +#ifdef WITH_THREAD +#include /* For pid_t */ +#include "pythread.h" +static long main_thread; +static pid_t main_pid; +#endif + +static struct { + int tripped; + PyObject *func; +} Handlers[NSIG]; + +static int is_tripped = 0; /* Speed up sigcheck() when none tripped */ + +static PyObject *DefaultHandler; +static PyObject *IgnoreHandler; +static PyObject *IntHandler; + +/* On Solaris 8, gcc will produce a warning that the function + declaration is not a prototype. This is caused by the definition of + SIG_DFL as (void (*)())0; the correct declaration would have been + (void (*)(int))0. */ + +static PyOS_sighandler_t old_siginthandler = SIG_DFL; + + +static PyObject * +signal_default_int_handler(PyObject *self, PyObject *args) +{ + PyErr_SetNone(PyExc_KeyboardInterrupt); + return NULL; +} + +PyDoc_STRVAR(default_int_handler_doc, +"default_int_handler(...)\n\ +\n\ +The default handler for SIGINT instated by Python.\n\ +It raises KeyboardInterrupt."); + + +static int +checksignals_witharg(void * unused) +{ + return PyErr_CheckSignals(); +} + +static void +signal_handler(int sig_num) +{ +#ifdef WITH_THREAD +#ifdef WITH_PTH + if (PyThread_get_thread_ident() != main_thread) { + pth_raise(*(pth_t *) main_thread, sig_num); + return; + } +#endif + /* See NOTES section above */ + if (getpid() == main_pid) { +#endif + is_tripped++; + Handlers[sig_num].tripped = 1; + Py_AddPendingCall(checksignals_witharg, NULL); +#ifdef WITH_THREAD + } +#endif +#ifdef SIGCHLD + if (sig_num == SIGCHLD) { + /* To avoid infinite recursion, this signal remains + reset until explicit re-instated. + Don't clear the 'func' field as it is our pointer + to the Python handler... */ + return; + } +#endif +#ifdef HAVE_SIGINTERRUPT + siginterrupt(sig_num, 1); +#endif + PyOS_setsig(sig_num, signal_handler); +} + + +#ifdef HAVE_ALARM +static PyObject * +signal_alarm(PyObject *self, PyObject *args) +{ + int t; + if (!PyArg_ParseTuple(args, "i:alarm", &t)) + return NULL; + /* alarm() returns the number of seconds remaining */ + return PyInt_FromLong((long)alarm(t)); +} + +PyDoc_STRVAR(alarm_doc, +"alarm(seconds)\n\ +\n\ +Arrange for SIGALRM to arrive after the given number of seconds."); +#endif + +#ifdef HAVE_PAUSE +static PyObject * +signal_pause(PyObject *self) +{ + Py_BEGIN_ALLOW_THREADS + (void)pause(); + Py_END_ALLOW_THREADS + /* make sure that any exceptions that got raised are propagated + * back into Python + */ + if (PyErr_CheckSignals()) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} +PyDoc_STRVAR(pause_doc, +"pause()\n\ +\n\ +Wait until a signal arrives."); + +#endif + + +static PyObject * +signal_signal(PyObject *self, PyObject *args) +{ + PyObject *obj; + int sig_num; + PyObject *old_handler; + void (*func)(int); + if (!PyArg_ParseTuple(args, "iO:signal", &sig_num, &obj)) + return NULL; +#ifdef WITH_THREAD + if (PyThread_get_thread_ident() != main_thread) { + PyErr_SetString(PyExc_ValueError, + "signal only works in main thread"); + return NULL; + } +#endif + if (sig_num < 1 || sig_num >= NSIG) { + PyErr_SetString(PyExc_ValueError, + "signal number out of range"); + return NULL; + } + if (obj == IgnoreHandler) + func = SIG_IGN; + else if (obj == DefaultHandler) + func = SIG_DFL; + else if (!PyCallable_Check(obj)) { + PyErr_SetString(PyExc_TypeError, +"signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object"); + return NULL; + } + else + func = signal_handler; +#ifdef HAVE_SIGINTERRUPT + siginterrupt(sig_num, 1); +#endif + if (PyOS_setsig(sig_num, func) == SIG_ERR) { + PyErr_SetFromErrno(PyExc_RuntimeError); + return NULL; + } + old_handler = Handlers[sig_num].func; + Handlers[sig_num].tripped = 0; + Py_INCREF(obj); + Handlers[sig_num].func = obj; + return old_handler; +} + +PyDoc_STRVAR(signal_doc, +"signal(sig, action) -> action\n\ +\n\ +Set the action for the given signal. The action can be SIG_DFL,\n\ +SIG_IGN, or a callable Python object. The previous action is\n\ +returned. See getsignal() for possible return values.\n\ +\n\ +*** IMPORTANT NOTICE ***\n\ +A signal handler function is called with two arguments:\n\ +the first is the signal number, the second is the interrupted stack frame."); + + +static PyObject * +signal_getsignal(PyObject *self, PyObject *args) +{ + int sig_num; + PyObject *old_handler; + if (!PyArg_ParseTuple(args, "i:getsignal", &sig_num)) + return NULL; + if (sig_num < 1 || sig_num >= NSIG) { + PyErr_SetString(PyExc_ValueError, + "signal number out of range"); + return NULL; + } + old_handler = Handlers[sig_num].func; + Py_INCREF(old_handler); + return old_handler; +} + +PyDoc_STRVAR(getsignal_doc, +"getsignal(sig) -> action\n\ +\n\ +Return the current action for the given signal. The return value can be:\n\ +SIG_IGN -- if the signal is being ignored\n\ +SIG_DFL -- if the default action for the signal is in effect\n\ +None -- if an unknown handler is in effect\n\ +anything else -- the callable Python object used as a handler"); + + +/* List of functions defined in the module */ +static PyMethodDef signal_methods[] = { +#ifdef HAVE_ALARM + {"alarm", signal_alarm, METH_VARARGS, alarm_doc}, +#endif + {"signal", signal_signal, METH_VARARGS, signal_doc}, + {"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc}, +#ifdef HAVE_PAUSE + {"pause", (PyCFunction)signal_pause, + METH_NOARGS,pause_doc}, +#endif + {"default_int_handler", signal_default_int_handler, + METH_VARARGS, default_int_handler_doc}, + {NULL, NULL} /* sentinel */ +}; + + +PyDoc_STRVAR(module_doc, +"This module provides mechanisms to use signal handlers in Python.\n\ +\n\ +Functions:\n\ +\n\ +alarm() -- cause SIGALRM after a specified time [Unix only]\n\ +signal() -- set the action for a given signal\n\ +getsignal() -- get the signal action for a given signal\n\ +pause() -- wait until a signal arrives [Unix only]\n\ +default_int_handler() -- default SIGINT handler\n\ +\n\ +Constants:\n\ +\n\ +SIG_DFL -- used to refer to the system default handler\n\ +SIG_IGN -- used to ignore the signal\n\ +NSIG -- number of defined signals\n\ +\n\ +SIGINT, SIGTERM, etc. -- signal numbers\n\ +\n\ +*** IMPORTANT NOTICE ***\n\ +A signal handler function is called with two arguments:\n\ +the first is the signal number, the second is the interrupted stack frame."); + +PyMODINIT_FUNC +initsignal(void) +{ + PyObject *m, *d, *x; + int i; + +#ifdef WITH_THREAD + main_thread = PyThread_get_thread_ident(); + main_pid = getpid(); +#endif + + /* Create the module and add the functions */ + m = Py_InitModule3("signal", signal_methods, module_doc); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + + x = DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL); + if (!x || PyDict_SetItemString(d, "SIG_DFL", x) < 0) + goto finally; + + x = IgnoreHandler = PyLong_FromVoidPtr((void *)SIG_IGN); + if (!x || PyDict_SetItemString(d, "SIG_IGN", x) < 0) + goto finally; + + x = PyInt_FromLong((long)NSIG); + if (!x || PyDict_SetItemString(d, "NSIG", x) < 0) + goto finally; + Py_DECREF(x); + + x = IntHandler = PyDict_GetItemString(d, "default_int_handler"); + if (!x) + goto finally; + Py_INCREF(IntHandler); + + Handlers[0].tripped = 0; + for (i = 1; i < NSIG; i++) { + void (*t)(int); + t = PyOS_getsig(i); + Handlers[i].tripped = 0; + if (t == SIG_DFL) + Handlers[i].func = DefaultHandler; + else if (t == SIG_IGN) + Handlers[i].func = IgnoreHandler; + else + Handlers[i].func = Py_None; /* None of our business */ + Py_INCREF(Handlers[i].func); + } + if (Handlers[SIGINT].func == DefaultHandler) { + /* Install default int handler */ + Py_INCREF(IntHandler); + Py_DECREF(Handlers[SIGINT].func); + Handlers[SIGINT].func = IntHandler; + old_siginthandler = PyOS_setsig(SIGINT, signal_handler); + } + +#ifdef SIGHUP + x = PyInt_FromLong(SIGHUP); + PyDict_SetItemString(d, "SIGHUP", x); + Py_XDECREF(x); +#endif +#ifdef SIGINT + x = PyInt_FromLong(SIGINT); + PyDict_SetItemString(d, "SIGINT", x); + Py_XDECREF(x); +#endif +#ifdef SIGBREAK + x = PyInt_FromLong(SIGBREAK); + PyDict_SetItemString(d, "SIGBREAK", x); + Py_XDECREF(x); +#endif +#ifdef SIGQUIT + x = PyInt_FromLong(SIGQUIT); + PyDict_SetItemString(d, "SIGQUIT", x); + Py_XDECREF(x); +#endif +#ifdef SIGILL + x = PyInt_FromLong(SIGILL); + PyDict_SetItemString(d, "SIGILL", x); + Py_XDECREF(x); +#endif +#ifdef SIGTRAP + x = PyInt_FromLong(SIGTRAP); + PyDict_SetItemString(d, "SIGTRAP", x); + Py_XDECREF(x); +#endif +#ifdef SIGIOT + x = PyInt_FromLong(SIGIOT); + PyDict_SetItemString(d, "SIGIOT", x); + Py_XDECREF(x); +#endif +#ifdef SIGABRT + x = PyInt_FromLong(SIGABRT); + PyDict_SetItemString(d, "SIGABRT", x); + Py_XDECREF(x); +#endif +#ifdef SIGEMT + x = PyInt_FromLong(SIGEMT); + PyDict_SetItemString(d, "SIGEMT", x); + Py_XDECREF(x); +#endif +#ifdef SIGFPE + x = PyInt_FromLong(SIGFPE); + PyDict_SetItemString(d, "SIGFPE", x); + Py_XDECREF(x); +#endif +#ifdef SIGKILL + x = PyInt_FromLong(SIGKILL); + PyDict_SetItemString(d, "SIGKILL", x); + Py_XDECREF(x); +#endif +#ifdef SIGBUS + x = PyInt_FromLong(SIGBUS); + PyDict_SetItemString(d, "SIGBUS", x); + Py_XDECREF(x); +#endif +#ifdef SIGSEGV + x = PyInt_FromLong(SIGSEGV); + PyDict_SetItemString(d, "SIGSEGV", x); + Py_XDECREF(x); +#endif +#ifdef SIGSYS + x = PyInt_FromLong(SIGSYS); + PyDict_SetItemString(d, "SIGSYS", x); + Py_XDECREF(x); +#endif +#ifdef SIGPIPE + x = PyInt_FromLong(SIGPIPE); + PyDict_SetItemString(d, "SIGPIPE", x); + Py_XDECREF(x); +#endif +#ifdef SIGALRM + x = PyInt_FromLong(SIGALRM); + PyDict_SetItemString(d, "SIGALRM", x); + Py_XDECREF(x); +#endif +#ifdef SIGTERM + x = PyInt_FromLong(SIGTERM); + PyDict_SetItemString(d, "SIGTERM", x); + Py_XDECREF(x); +#endif +#ifdef SIGUSR1 + x = PyInt_FromLong(SIGUSR1); + PyDict_SetItemString(d, "SIGUSR1", x); + Py_XDECREF(x); +#endif +#ifdef SIGUSR2 + x = PyInt_FromLong(SIGUSR2); + PyDict_SetItemString(d, "SIGUSR2", x); + Py_XDECREF(x); +#endif +#ifdef SIGCLD + x = PyInt_FromLong(SIGCLD); + PyDict_SetItemString(d, "SIGCLD", x); + Py_XDECREF(x); +#endif +#ifdef SIGCHLD + x = PyInt_FromLong(SIGCHLD); + PyDict_SetItemString(d, "SIGCHLD", x); + Py_XDECREF(x); +#endif +#ifdef SIGPWR + x = PyInt_FromLong(SIGPWR); + PyDict_SetItemString(d, "SIGPWR", x); + Py_XDECREF(x); +#endif +#ifdef SIGIO + x = PyInt_FromLong(SIGIO); + PyDict_SetItemString(d, "SIGIO", x); + Py_XDECREF(x); +#endif +#ifdef SIGURG + x = PyInt_FromLong(SIGURG); + PyDict_SetItemString(d, "SIGURG", x); + Py_XDECREF(x); +#endif +#ifdef SIGWINCH + x = PyInt_FromLong(SIGWINCH); + PyDict_SetItemString(d, "SIGWINCH", x); + Py_XDECREF(x); +#endif +#ifdef SIGPOLL + x = PyInt_FromLong(SIGPOLL); + PyDict_SetItemString(d, "SIGPOLL", x); + Py_XDECREF(x); +#endif +#ifdef SIGSTOP + x = PyInt_FromLong(SIGSTOP); + PyDict_SetItemString(d, "SIGSTOP", x); + Py_XDECREF(x); +#endif +#ifdef SIGTSTP + x = PyInt_FromLong(SIGTSTP); + PyDict_SetItemString(d, "SIGTSTP", x); + Py_XDECREF(x); +#endif +#ifdef SIGCONT + x = PyInt_FromLong(SIGCONT); + PyDict_SetItemString(d, "SIGCONT", x); + Py_XDECREF(x); +#endif +#ifdef SIGTTIN + x = PyInt_FromLong(SIGTTIN); + PyDict_SetItemString(d, "SIGTTIN", x); + Py_XDECREF(x); +#endif +#ifdef SIGTTOU + x = PyInt_FromLong(SIGTTOU); + PyDict_SetItemString(d, "SIGTTOU", x); + Py_XDECREF(x); +#endif +#ifdef SIGVTALRM + x = PyInt_FromLong(SIGVTALRM); + PyDict_SetItemString(d, "SIGVTALRM", x); + Py_XDECREF(x); +#endif +#ifdef SIGPROF + x = PyInt_FromLong(SIGPROF); + PyDict_SetItemString(d, "SIGPROF", x); + Py_XDECREF(x); +#endif +#ifdef SIGXCPU + x = PyInt_FromLong(SIGXCPU); + PyDict_SetItemString(d, "SIGXCPU", x); + Py_XDECREF(x); +#endif +#ifdef SIGXFSZ + x = PyInt_FromLong(SIGXFSZ); + PyDict_SetItemString(d, "SIGXFSZ", x); + Py_XDECREF(x); +#endif +#ifdef SIGINFO + x = PyInt_FromLong(SIGINFO); + PyDict_SetItemString(d, "SIGINFO", x); + Py_XDECREF(x); +#endif + if (!PyErr_Occurred()) + return; + + /* Check for errors */ + finally: + return; +} + +static void +finisignal(void) +{ + int i; + PyObject *func; + + PyOS_setsig(SIGINT, old_siginthandler); + old_siginthandler = SIG_DFL; + + for (i = 1; i < NSIG; i++) { + func = Handlers[i].func; + Handlers[i].tripped = 0; + Handlers[i].func = NULL; + if (i != SIGINT && func != NULL && func != Py_None && + func != DefaultHandler && func != IgnoreHandler) + PyOS_setsig(i, SIG_DFL); + Py_XDECREF(func); + } + + Py_XDECREF(IntHandler); + IntHandler = NULL; + Py_XDECREF(DefaultHandler); + DefaultHandler = NULL; + Py_XDECREF(IgnoreHandler); + IgnoreHandler = NULL; +} + + +/* Declared in pyerrors.h */ +int +PyErr_CheckSignals(void) +{ + int i; + PyObject *f; + + if (!is_tripped) + return 0; +#ifdef WITH_THREAD + if (PyThread_get_thread_ident() != main_thread) + return 0; +#endif + if (!(f = (PyObject *)PyEval_GetFrame())) + f = Py_None; + + for (i = 1; i < NSIG; i++) { + if (Handlers[i].tripped) { + PyObject *result = NULL; + PyObject *arglist = Py_BuildValue("(iO)", i, f); + Handlers[i].tripped = 0; + + if (arglist) { + result = PyEval_CallObject(Handlers[i].func, + arglist); + Py_DECREF(arglist); + } + if (!result) + return -1; + + Py_DECREF(result); + } + } + is_tripped = 0; + return 0; +} + + +/* Replacements for intrcheck.c functionality + * Declared in pyerrors.h + */ +void +PyErr_SetInterrupt(void) +{ + is_tripped++; + Handlers[SIGINT].tripped = 1; + Py_AddPendingCall((int (*)(void *))PyErr_CheckSignals, NULL); +} + +void +PyOS_InitInterrupts(void) +{ + initsignal(); + _PyImport_FixupExtension("signal", "signal"); +} + +void +PyOS_FiniInterrupts(void) +{ + finisignal(); +} + +int +PyOS_InterruptOccurred(void) +{ + if (Handlers[SIGINT].tripped) { +#ifdef WITH_THREAD + if (PyThread_get_thread_ident() != main_thread) + return 0; +#endif + Handlers[SIGINT].tripped = 0; + return 1; + } + return 0; +} + +void +PyOS_AfterFork(void) +{ +#ifdef WITH_THREAD + PyEval_ReInitThreads(); + main_thread = PyThread_get_thread_ident(); + main_pid = getpid(); +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/socketmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/socketmodule.c new file mode 100644 index 00000000..70e9f89f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/socketmodule.c @@ -0,0 +1,4151 @@ +/* Socket module */ + +/* + +This module provides an interface to Berkeley socket IPC. + +Limitations: + +- Only AF_INET, AF_INET6 and AF_UNIX address families are supported in a + portable manner, though AF_PACKET is supported under Linux. +- No read/write operations (use sendall/recv or makefile instead). +- Additional restrictions apply on some non-Unix platforms (compensated + for by socket.py). + +Module interface: + +- socket.error: exception raised for socket specific errors +- socket.gaierror: exception raised for getaddrinfo/getnameinfo errors, + a subclass of socket.error +- socket.herror: exception raised for gethostby* errors, + a subclass of socket.error +- socket.fromfd(fd, family, type[, proto]) --> new socket object (created + from an existing file descriptor) +- socket.gethostbyname(hostname) --> host IP address (string: 'dd.dd.dd.dd') +- socket.gethostbyaddr(IP address) --> (hostname, [alias, ...], [IP addr, ...]) +- socket.gethostname() --> host name (string: 'spam' or 'spam.domain.com') +- socket.getprotobyname(protocolname) --> protocol number +- socket.getservbyname(servicename, protocolname) --> port number +- socket.socket([family[, type [, proto]]]) --> new socket object +- socket.ntohs(16 bit value) --> new int object +- socket.ntohl(32 bit value) --> new int object +- socket.htons(16 bit value) --> new int object +- socket.htonl(32 bit value) --> new int object +- socket.getaddrinfo(host, port [, family, socktype, proto, flags]) + --> List of (family, socktype, proto, canonname, sockaddr) +- socket.getnameinfo(sockaddr, flags) --> (host, port) +- socket.AF_INET, socket.SOCK_STREAM, etc.: constants from +- socket.has_ipv6: boolean value indicating if IPv6 is supported +- socket.inet_aton(IP address) -> 32-bit packed IP representation +- socket.inet_ntoa(packed IP) -> IP address string +- socket.getdefaulttimeout() -> None | float +- socket.setdefaulttimeout(None | float) +- an Internet socket address is a pair (hostname, port) + where hostname can be anything recognized by gethostbyname() + (including the dd.dd.dd.dd notation) and port is in host byte order +- where a hostname is returned, the dd.dd.dd.dd notation is used +- a UNIX domain socket address is a string specifying the pathname +- an AF_PACKET socket address is a tuple containing a string + specifying the ethernet interface and an integer specifying + the Ethernet protocol number to be received. For example: + ("eth0",0x1234). Optional 3rd,4th,5th elements in the tuple + specify packet-type and ha-type/addr -- these are ignored by + networking code, but accepted since they are returned by the + getsockname() method. + +Local naming conventions: + +- names starting with sock_ are socket object methods +- names starting with socket_ are module-level functions +- names starting with PySocket are exported through socketmodule.h + +*/ + +#include "Python.h" + +#undef MAX +#define MAX(x, y) ((x) < (y) ? (y) : (x)) + +/* Socket object documentation */ +PyDoc_STRVAR(sock_doc, +"socket([family[, type[, proto]]]) -> socket object\n\ +\n\ +Open a socket of the given type. The family argument specifies the\n\ +address family; it defaults to AF_INET. The type argument specifies\n\ +whether this is a stream (SOCK_STREAM, this is the default)\n\ +or datagram (SOCK_DGRAM) socket. The protocol argument defaults to 0,\n\ +specifying the default protocol. Keyword arguments are accepted.\n\ +\n\ +A socket object represents one endpoint of a network connection.\n\ +\n\ +Methods of socket objects (keyword arguments not allowed):\n\ +\n\ +accept() -- accept a connection, returning new socket and client address\n\ +bind(addr) -- bind the socket to a local address\n\ +close() -- close the socket\n\ +connect(addr) -- connect the socket to a remote address\n\ +connect_ex(addr) -- connect, return an error code instead of an exception\n\ +dup() -- return a new socket object identical to the current one [*]\n\ +fileno() -- return underlying file descriptor\n\ +getpeername() -- return remote address [*]\n\ +getsockname() -- return local address\n\ +getsockopt(level, optname[, buflen]) -- get socket options\n\ +gettimeout() -- return timeout or None\n\ +listen(n) -- start listening for incoming connections\n\ +makefile([mode, [bufsize]]) -- return a file object for the socket [*]\n\ +recv(buflen[, flags]) -- receive data\n\ +recvfrom(buflen[, flags]) -- receive data and sender's address\n\ +sendall(data[, flags]) -- send all data\n\ +send(data[, flags]) -- send data, may not send all of it\n\ +sendto(data[, flags], addr) -- send data to a given address\n\ +setblocking(0 | 1) -- set or clear the blocking I/O flag\n\ +setsockopt(level, optname, value) -- set socket options\n\ +settimeout(None | float) -- set or clear the timeout\n\ +shutdown(how) -- shut down traffic in one or both directions\n\ +\n\ + [*] not available on all platforms!"); + +/* XXX This is a terrible mess of platform-dependent preprocessor hacks. + I hope some day someone can clean this up please... */ + +/* Hacks for gethostbyname_r(). On some non-Linux platforms, the configure + script doesn't get this right, so we hardcode some platform checks below. + On the other hand, not all Linux versions agree, so there the settings + computed by the configure script are needed! */ + +#ifndef linux +# undef HAVE_GETHOSTBYNAME_R_3_ARG +# undef HAVE_GETHOSTBYNAME_R_5_ARG +# undef HAVE_GETHOSTBYNAME_R_6_ARG +#endif + +#ifndef WITH_THREAD +# undef HAVE_GETHOSTBYNAME_R +#endif + +#ifdef HAVE_GETHOSTBYNAME_R +# if defined(_AIX) || defined(__osf__) +# define HAVE_GETHOSTBYNAME_R_3_ARG +# elif defined(__sun) || defined(__sgi) +# define HAVE_GETHOSTBYNAME_R_5_ARG +# elif defined(linux) +/* Rely on the configure script */ +# else +# undef HAVE_GETHOSTBYNAME_R +# endif +#endif + +#if !defined(HAVE_GETHOSTBYNAME_R) && defined(WITH_THREAD) && \ + !defined(MS_WINDOWS) +# define USE_GETHOSTBYNAME_LOCK +#endif + +/* On systems on which getaddrinfo() is believed to not be thread-safe, + (this includes the getaddrinfo emulation) protect access with a lock. */ +#if defined(WITH_THREAD) && (defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) || !defined(HAVE_GETADDRINFO)) +#define USE_GETADDRINFO_LOCK +#endif + +#ifdef USE_GETADDRINFO_LOCK +#define ACQUIRE_GETADDRINFO_LOCK PyThread_acquire_lock(netdb_lock, 1); +#define RELEASE_GETADDRINFO_LOCK PyThread_release_lock(netdb_lock); +#else +#define ACQUIRE_GETADDRINFO_LOCK +#define RELEASE_GETADDRINFO_LOCK +#endif + +#if defined(USE_GETHOSTBYNAME_LOCK) || defined(USE_GETADDRINFO_LOCK) +# include "pythread.h" +#endif + +#if defined(PYCC_VACPP) +# include +# include +# include +# include +# include +#endif + +#if defined(__VMS) +#if ! defined(_SOCKADDR_LEN) +# ifdef getaddrinfo +# undef getaddrinfo +# endif +# include "TCPIP_IOCTL_ROUTINE" +#else +# include +#endif +#endif + +#if defined(PYOS_OS2) +# define INCL_DOS +# define INCL_DOSERRORS +# define INCL_NOPMAPI +# include +#endif + +#if defined(__sgi) && _COMPILER_VERSION>700 && !_SGIAPI +/* make sure that the reentrant (gethostbyaddr_r etc) + functions are declared correctly if compiling with + MIPSPro 7.x in ANSI C mode (default) */ + +/* XXX Using _SGIAPI is the wrong thing, + but I don't know what the right thing is. */ +#define _SGIAPI 1 + +#define HAVE_INET_PTON +#include +#endif + +/* Irix 6.5 fails to define this variable at all. This is needed + for both GCC and SGI's compiler. I'd say that the SGI headers + are just busted. */ +#if defined(__sgi) && !defined(INET_ADDRSTRLEN) +#define INET_ADDRSTRLEN 16 +#endif + +/* Generic includes */ +#include +#include + +/* Generic socket object definitions and includes */ +#define PySocket_BUILDING_SOCKET +#include "socketmodule.h" + +/* Addressing includes */ + +#ifndef MS_WINDOWS + +/* Non-MS WINDOWS includes */ +# include + +/* Headers needed for inet_ntoa() and inet_addr() */ +# ifdef __BEOS__ +# include +# elif defined(PYOS_OS2) && defined(PYCC_VACPP) +# include +typedef size_t socklen_t; +# else +# include +# endif + +# ifndef RISCOS +# include +# else +# include +# include +# define NO_DUP +int h_errno; /* not used */ +# define INET_ADDRSTRLEN 16 +# endif + +#else + +/* MS_WINDOWS includes */ +# include + +#endif + +#ifdef HAVE_STDDEF_H +# include +#endif + +#ifndef offsetof +# define offsetof(type, member) ((size_t)(&((type *)0)->member)) +#endif + +#ifndef O_NONBLOCK +# define O_NONBLOCK O_NDELAY +#endif + +#include "addrinfo.h" + +#ifndef HAVE_INET_PTON +int inet_pton(int af, const char *src, void *dst); +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +#endif + +#ifdef __APPLE__ +/* On OS X, getaddrinfo returns no error indication of lookup + failure, so we must use the emulation instead of the libinfo + implementation. Unfortunately, performing an autoconf test + for this bug would require DNS access for the machine performing + the configuration, which is not acceptable. Therefore, we + determine the bug just by checking for __APPLE__. If this bug + gets ever fixed, perhaps checking for sys/version.h would be + appropriate, which is 10/0 on the system with the bug. */ +#ifndef HAVE_GETNAMEINFO +/* This bug seems to be fixed in Jaguar. Ths easiest way I could + Find to check for Jaguar is that it has getnameinfo(), which + older releases don't have */ +#undef HAVE_GETADDRINFO +#endif +#endif + +/* I know this is a bad practice, but it is the easiest... */ +#if !defined(HAVE_GETADDRINFO) +/* avoid clashes with the C library definition of the symbol. */ +#define getaddrinfo fake_getaddrinfo +#define gai_strerror fake_gai_strerror +#define freeaddrinfo fake_freeaddrinfo +#include "getaddrinfo.c" +#endif +#if !defined(HAVE_GETNAMEINFO) +#define getnameinfo fake_getnameinfo +#include "getnameinfo.c" +#endif + +#if defined(MS_WINDOWS) || defined(__BEOS__) +/* BeOS suffers from the same socket dichotomy as Win32... - [cjh] */ +/* seem to be a few differences in the API */ +#define SOCKETCLOSE closesocket +#define NO_DUP /* Actually it exists on NT 3.5, but what the heck... */ +#endif + +#ifdef MS_WIN32 +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#define snprintf _snprintf +#endif + +#if defined(PYOS_OS2) && !defined(PYCC_GCC) +#define SOCKETCLOSE soclose +#define NO_DUP /* Sockets are Not Actual File Handles under OS/2 */ +#endif + +#ifndef SOCKETCLOSE +#define SOCKETCLOSE close +#endif + +#ifdef __VMS +/* TCP/IP Services for VMS uses a maximum send/revc buffer length of 65535 */ +#define SEGMENT_SIZE 65535 +#endif + +/* + * Constants for getnameinfo() + */ +#if !defined(NI_MAXHOST) +#define NI_MAXHOST 1025 +#endif +#if !defined(NI_MAXSERV) +#define NI_MAXSERV 32 +#endif + +/* XXX There's a problem here: *static* functions are not supposed to have + a Py prefix (or use CapitalizedWords). Later... */ + +/* Global variable holding the exception type for errors detected + by this module (but not argument type or memory errors, etc.). */ +static PyObject *socket_error; +static PyObject *socket_herror; +static PyObject *socket_gaierror; +static PyObject *socket_timeout; + +#ifdef RISCOS +/* Global variable which is !=0 if Python is running in a RISC OS taskwindow */ +static int taskwindow; +#endif + +/* A forward reference to the socket type object. + The sock_type variable contains pointers to various functions, + some of which call new_sockobject(), which uses sock_type, so + there has to be a circular reference. */ +static PyTypeObject sock_type; + +/* Convenience function to raise an error according to errno + and return a NULL pointer from a function. */ + +static PyObject * +set_error(void) +{ +#ifdef MS_WINDOWS + int err_no = WSAGetLastError(); + static struct { + int no; + const char *msg; + } *msgp, msgs[] = { + {WSAEINTR, "Interrupted system call"}, + {WSAEBADF, "Bad file descriptor"}, + {WSAEACCES, "Permission denied"}, + {WSAEFAULT, "Bad address"}, + {WSAEINVAL, "Invalid argument"}, + {WSAEMFILE, "Too many open files"}, + {WSAEWOULDBLOCK, + "The socket operation could not complete " + "without blocking"}, + {WSAEINPROGRESS, "Operation now in progress"}, + {WSAEALREADY, "Operation already in progress"}, + {WSAENOTSOCK, "Socket operation on non-socket"}, + {WSAEDESTADDRREQ, "Destination address required"}, + {WSAEMSGSIZE, "Message too long"}, + {WSAEPROTOTYPE, "Protocol wrong type for socket"}, + {WSAENOPROTOOPT, "Protocol not available"}, + {WSAEPROTONOSUPPORT, "Protocol not supported"}, + {WSAESOCKTNOSUPPORT, "Socket type not supported"}, + {WSAEOPNOTSUPP, "Operation not supported"}, + {WSAEPFNOSUPPORT, "Protocol family not supported"}, + {WSAEAFNOSUPPORT, "Address family not supported"}, + {WSAEADDRINUSE, "Address already in use"}, + {WSAEADDRNOTAVAIL, "Can't assign requested address"}, + {WSAENETDOWN, "Network is down"}, + {WSAENETUNREACH, "Network is unreachable"}, + {WSAENETRESET, "Network dropped connection on reset"}, + {WSAECONNABORTED, "Software caused connection abort"}, + {WSAECONNRESET, "Connection reset by peer"}, + {WSAENOBUFS, "No buffer space available"}, + {WSAEISCONN, "Socket is already connected"}, + {WSAENOTCONN, "Socket is not connected"}, + {WSAESHUTDOWN, "Can't send after socket shutdown"}, + {WSAETOOMANYREFS, "Too many references: can't splice"}, + {WSAETIMEDOUT, "Operation timed out"}, + {WSAECONNREFUSED, "Connection refused"}, + {WSAELOOP, "Too many levels of symbolic links"}, + {WSAENAMETOOLONG, "File name too long"}, + {WSAEHOSTDOWN, "Host is down"}, + {WSAEHOSTUNREACH, "No route to host"}, + {WSAENOTEMPTY, "Directory not empty"}, + {WSAEPROCLIM, "Too many processes"}, + {WSAEUSERS, "Too many users"}, + {WSAEDQUOT, "Disc quota exceeded"}, + {WSAESTALE, "Stale NFS file handle"}, + {WSAEREMOTE, "Too many levels of remote in path"}, + {WSASYSNOTREADY, "Network subsystem is unvailable"}, + {WSAVERNOTSUPPORTED, "WinSock version is not supported"}, + {WSANOTINITIALISED, + "Successful WSAStartup() not yet performed"}, + {WSAEDISCON, "Graceful shutdown in progress"}, + /* Resolver errors */ + {WSAHOST_NOT_FOUND, "No such host is known"}, + {WSATRY_AGAIN, "Host not found, or server failed"}, + {WSANO_RECOVERY, "Unexpected server error encountered"}, + {WSANO_DATA, "Valid name without requested data"}, + {WSANO_ADDRESS, "No address, look for MX record"}, + {0, NULL} + }; + if (err_no) { + PyObject *v; + const char *msg = "winsock error"; + + for (msgp = msgs; msgp->msg; msgp++) { + if (err_no == msgp->no) { + msg = msgp->msg; + break; + } + } + + v = Py_BuildValue("(is)", err_no, msg); + if (v != NULL) { + PyErr_SetObject(socket_error, v); + Py_DECREF(v); + } + return NULL; + } + else +#endif + +#if defined(PYOS_OS2) && !defined(PYCC_GCC) + if (sock_errno() != NO_ERROR) { + APIRET rc; + ULONG msglen; + char outbuf[100]; + int myerrorcode = sock_errno(); + + /* Retrieve socket-related error message from MPTN.MSG file */ + rc = DosGetMessage(NULL, 0, outbuf, sizeof(outbuf), + myerrorcode - SOCBASEERR + 26, + "mptn.msg", + &msglen); + if (rc == NO_ERROR) { + PyObject *v; + + /* OS/2 doesn't guarantee a terminator */ + outbuf[msglen] = '\0'; + if (strlen(outbuf) > 0) { + /* If non-empty msg, trim CRLF */ + char *lastc = &outbuf[ strlen(outbuf)-1 ]; + while (lastc > outbuf && isspace(*lastc)) { + /* Trim trailing whitespace (CRLF) */ + *lastc-- = '\0'; + } + } + v = Py_BuildValue("(is)", myerrorcode, outbuf); + if (v != NULL) { + PyErr_SetObject(socket_error, v); + Py_DECREF(v); + } + return NULL; + } + } +#endif + +#if defined(RISCOS) + if (_inet_error.errnum != NULL) { + PyObject *v; + v = Py_BuildValue("(is)", errno, _inet_err()); + if (v != NULL) { + PyErr_SetObject(socket_error, v); + Py_DECREF(v); + } + return NULL; + } +#endif + + return PyErr_SetFromErrno(socket_error); +} + + +static PyObject * +set_herror(int h_error) +{ + PyObject *v; + +#ifdef HAVE_HSTRERROR + v = Py_BuildValue("(is)", h_error, (char *)hstrerror(h_error)); +#else + v = Py_BuildValue("(is)", h_error, "host not found"); +#endif + if (v != NULL) { + PyErr_SetObject(socket_herror, v); + Py_DECREF(v); + } + + return NULL; +} + + +static PyObject * +set_gaierror(int error) +{ + PyObject *v; + +#ifdef EAI_SYSTEM + /* EAI_SYSTEM is not available on Windows XP. */ + if (error == EAI_SYSTEM) + return set_error(); +#endif + +#ifdef HAVE_GAI_STRERROR + v = Py_BuildValue("(is)", error, gai_strerror(error)); +#else + v = Py_BuildValue("(is)", error, "getaddrinfo failed"); +#endif + if (v != NULL) { + PyErr_SetObject(socket_gaierror, v); + Py_DECREF(v); + } + + return NULL; +} + +/* Function to perform the setting of socket blocking mode + internally. block = (1 | 0). */ +static int +internal_setblocking(PySocketSockObject *s, int block) +{ +#ifndef RISCOS +#ifndef MS_WINDOWS + int delay_flag; +#endif +#endif + + Py_BEGIN_ALLOW_THREADS +#ifdef __BEOS__ + block = !block; + setsockopt(s->sock_fd, SOL_SOCKET, SO_NONBLOCK, + (void *)(&block), sizeof(int)); +#else +#ifndef RISCOS +#ifndef MS_WINDOWS +#if defined(PYOS_OS2) && !defined(PYCC_GCC) + block = !block; + ioctl(s->sock_fd, FIONBIO, (caddr_t)&block, sizeof(block)); +#elif defined(__VMS) + block = !block; + ioctl(s->sock_fd, FIONBIO, (char *)&block); +#else /* !PYOS_OS2 && !_VMS */ + delay_flag = fcntl(s->sock_fd, F_GETFL, 0); + if (block) + delay_flag &= (~O_NONBLOCK); + else + delay_flag |= O_NONBLOCK; + fcntl(s->sock_fd, F_SETFL, delay_flag); +#endif /* !PYOS_OS2 */ +#else /* MS_WINDOWS */ + block = !block; + ioctlsocket(s->sock_fd, FIONBIO, (u_long*)&block); +#endif /* MS_WINDOWS */ +#else /* RISCOS */ + block = !block; + socketioctl(s->sock_fd, FIONBIO, (u_long*)&block); +#endif /* RISCOS */ +#endif /* __BEOS__ */ + Py_END_ALLOW_THREADS + + /* Since these don't return anything */ + return 1; +} + +/* Do a select() on the socket, if necessary (sock_timeout > 0). + The argument writing indicates the direction. + This does not raise an exception; we'll let our caller do that + after they've reacquired the interpreter lock. + Returns 1 on timeout, 0 otherwise. */ +static int +internal_select(PySocketSockObject *s, int writing) +{ + fd_set fds; + struct timeval tv; + int n; + + /* Nothing to do unless we're in timeout mode (not non-blocking) */ + if (s->sock_timeout <= 0.0) + return 0; + + /* Guard against closed socket */ + if (s->sock_fd < 0) + return 0; + + /* Construct the arguments to select */ + tv.tv_sec = (int)s->sock_timeout; + tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); + FD_ZERO(&fds); + FD_SET(s->sock_fd, &fds); + + /* See if the socket is ready */ + if (writing) + n = select(s->sock_fd+1, NULL, &fds, NULL, &tv); + else + n = select(s->sock_fd+1, &fds, NULL, NULL, &tv); + if (n == 0) + return 1; + return 0; +} + +/* Initialize a new socket object. */ + +static double defaulttimeout = -1.0; /* Default timeout for new sockets */ + +PyMODINIT_FUNC +init_sockobject(PySocketSockObject *s, + SOCKET_T fd, int family, int type, int proto) +{ +#ifdef RISCOS + int block = 1; +#endif + s->sock_fd = fd; + s->sock_family = family; + s->sock_type = type; + s->sock_proto = proto; + s->sock_timeout = defaulttimeout; + + s->errorhandler = &set_error; + + if (defaulttimeout >= 0.0) + internal_setblocking(s, 0); + +#ifdef RISCOS + if (taskwindow) + socketioctl(s->sock_fd, 0x80046679, (u_long*)&block); +#endif +} + + +/* Create a new socket object. + This just creates the object and initializes it. + If the creation fails, return NULL and set an exception (implicit + in NEWOBJ()). */ + +static PySocketSockObject * +new_sockobject(SOCKET_T fd, int family, int type, int proto) +{ + PySocketSockObject *s; + s = (PySocketSockObject *) + PyType_GenericNew(&sock_type, NULL, NULL); + if (s != NULL) + init_sockobject(s, fd, family, type, proto); + return s; +} + + +/* Lock to allow python interpreter to continue, but only allow one + thread to be in gethostbyname or getaddrinfo */ +#if defined(USE_GETHOSTBYNAME_LOCK) || defined(USE_GETADDRINFO_LOCK) +PyThread_type_lock netdb_lock; +#endif + + +/* Convert a string specifying a host name or one of a few symbolic + names to a numeric IP address. This usually calls gethostbyname() + to do the work; the names "" and "" are special. + Return the length (IPv4 should be 4 bytes), or negative if + an error occurred; then an exception is raised. */ + +static int +setipaddr(char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int af) +{ + struct addrinfo hints, *res; + int error; + int d1, d2, d3, d4; + char ch; + + memset((void *) addr_ret, '\0', sizeof(*addr_ret)); + if (name[0] == '\0') { + int siz; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_PASSIVE; + Py_BEGIN_ALLOW_THREADS + ACQUIRE_GETADDRINFO_LOCK + error = getaddrinfo(NULL, "0", &hints, &res); + Py_END_ALLOW_THREADS + /* We assume that those thread-unsafe getaddrinfo() versions + *are* safe regarding their return value, ie. that a + subsequent call to getaddrinfo() does not destroy the + outcome of the first call. */ + RELEASE_GETADDRINFO_LOCK + if (error) { + set_gaierror(error); + return -1; + } + switch (res->ai_family) { + case AF_INET: + siz = 4; + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + siz = 16; + break; +#endif + default: + freeaddrinfo(res); + PyErr_SetString(socket_error, + "unsupported address family"); + return -1; + } + if (res->ai_next) { + freeaddrinfo(res); + PyErr_SetString(socket_error, + "wildcard resolved to multiple address"); + return -1; + } + if (res->ai_addrlen < addr_ret_size) + addr_ret_size = res->ai_addrlen; + memcpy(addr_ret, res->ai_addr, addr_ret_size); + freeaddrinfo(res); + return siz; + } + if (name[0] == '<' && strcmp(name, "") == 0) { + struct sockaddr_in *sin; + if (af != AF_INET && af != AF_UNSPEC) { + PyErr_SetString(socket_error, + "address family mismatched"); + return -1; + } + sin = (struct sockaddr_in *)addr_ret; + memset((void *) sin, '\0', sizeof(*sin)); + sin->sin_family = AF_INET; +#ifdef HAVE_SOCKADDR_SA_LEN + sin->sin_len = sizeof(*sin); +#endif + sin->sin_addr.s_addr = INADDR_BROADCAST; + return sizeof(sin->sin_addr); + } + if (sscanf(name, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 && + 0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 && + 0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) { + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)addr_ret; + sin->sin_addr.s_addr = htonl( + ((long) d1 << 24) | ((long) d2 << 16) | + ((long) d3 << 8) | ((long) d4 << 0)); + sin->sin_family = AF_INET; +#ifdef HAVE_SOCKADDR_SA_LEN + sin->sin_len = sizeof(*sin); +#endif + return 4; + } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + Py_BEGIN_ALLOW_THREADS + ACQUIRE_GETADDRINFO_LOCK + error = getaddrinfo(name, NULL, &hints, &res); +#if defined(__digital__) && defined(__unix__) + if (error == EAI_NONAME && af == AF_UNSPEC) { + /* On Tru64 V5.1, numeric-to-addr conversion fails + if no address family is given. Assume IPv4 for now.*/ + hints.ai_family = AF_INET; + error = getaddrinfo(name, NULL, &hints, &res); + } +#endif + Py_END_ALLOW_THREADS + RELEASE_GETADDRINFO_LOCK /* see comment in setipaddr() */ + if (error) { + set_gaierror(error); + return -1; + } + if (res->ai_addrlen < addr_ret_size) + addr_ret_size = res->ai_addrlen; + memcpy((char *) addr_ret, res->ai_addr, addr_ret_size); + freeaddrinfo(res); + switch (addr_ret->sa_family) { + case AF_INET: + return 4; +#ifdef ENABLE_IPV6 + case AF_INET6: + return 16; +#endif + default: + PyErr_SetString(socket_error, "unknown address family"); + return -1; + } +} + + +/* Create a string object representing an IP address. + This is always a string of the form 'dd.dd.dd.dd' (with variable + size numbers). */ + +static PyObject * +makeipaddr(struct sockaddr *addr, int addrlen) +{ + char buf[NI_MAXHOST]; + int error; + + error = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0, + NI_NUMERICHOST); + if (error) { + set_gaierror(error); + return NULL; + } + return PyString_FromString(buf); +} + + +/* Create an object representing the given socket address, + suitable for passing it back to bind(), connect() etc. + The family field of the sockaddr structure is inspected + to determine what kind of address it really is. */ + +/*ARGSUSED*/ +static PyObject * +makesockaddr(int sockfd, struct sockaddr *addr, int addrlen) +{ + if (addrlen == 0) { + /* No address -- may be recvfrom() from known socket */ + Py_INCREF(Py_None); + return Py_None; + } + +#ifdef __BEOS__ + /* XXX: BeOS version of accept() doesn't set family correctly */ + addr->sa_family = AF_INET; +#endif + + switch (addr->sa_family) { + + case AF_INET: + { + struct sockaddr_in *a; + PyObject *addrobj = makeipaddr(addr, sizeof(*a)); + PyObject *ret = NULL; + if (addrobj) { + a = (struct sockaddr_in *)addr; + ret = Py_BuildValue("Oi", addrobj, ntohs(a->sin_port)); + Py_DECREF(addrobj); + } + return ret; + } + +#if defined(AF_UNIX) && !defined(PYOS_OS2) + case AF_UNIX: + { + struct sockaddr_un *a = (struct sockaddr_un *) addr; + return PyString_FromString(a->sun_path); + } +#endif /* AF_UNIX */ + +#ifdef ENABLE_IPV6 + case AF_INET6: + { + struct sockaddr_in6 *a; + PyObject *addrobj = makeipaddr(addr, sizeof(*a)); + PyObject *ret = NULL; + if (addrobj) { + a = (struct sockaddr_in6 *)addr; + ret = Py_BuildValue("Oiii", + addrobj, + ntohs(a->sin6_port), + a->sin6_flowinfo, + a->sin6_scope_id); + Py_DECREF(addrobj); + } + return ret; + } +#endif + +#ifdef HAVE_NETPACKET_PACKET_H + case AF_PACKET: + { + struct sockaddr_ll *a = (struct sockaddr_ll *)addr; + char *ifname = ""; + struct ifreq ifr; + /* need to look up interface name give index */ + if (a->sll_ifindex) { + ifr.ifr_ifindex = a->sll_ifindex; + if (ioctl(sockfd, SIOCGIFNAME, &ifr) == 0) + ifname = ifr.ifr_name; + } + return Py_BuildValue("shbhs#", + ifname, + ntohs(a->sll_protocol), + a->sll_pkttype, + a->sll_hatype, + a->sll_addr, + a->sll_halen); + } +#endif + + /* More cases here... */ + + default: + /* If we don't know the address family, don't raise an + exception -- return it as a tuple. */ + return Py_BuildValue("is#", + addr->sa_family, + addr->sa_data, + sizeof(addr->sa_data)); + + } +} + + +/* Parse a socket address argument according to the socket object's + address family. Return 1 if the address was in the proper format, + 0 of not. The address is returned through addr_ret, its length + through len_ret. */ + +static int +getsockaddrarg(PySocketSockObject *s, PyObject *args, + struct sockaddr **addr_ret, int *len_ret) +{ + switch (s->sock_family) { + +#if defined(AF_UNIX) && !defined(PYOS_OS2) + case AF_UNIX: + { + struct sockaddr_un* addr; + char *path; + int len; + addr = (struct sockaddr_un*)&(s->sock_addr).un; + if (!PyArg_Parse(args, "t#", &path, &len)) + return 0; + if (len > sizeof addr->sun_path) { + PyErr_SetString(socket_error, + "AF_UNIX path too long"); + return 0; + } + addr->sun_family = s->sock_family; + memcpy(addr->sun_path, path, len); + addr->sun_path[len] = 0; + *addr_ret = (struct sockaddr *) addr; + *len_ret = len + sizeof(*addr) - sizeof(addr->sun_path); + return 1; + } +#endif /* AF_UNIX */ + + case AF_INET: + { + struct sockaddr_in* addr; + char *host; + int port, result; + addr=(struct sockaddr_in*)&(s->sock_addr).in; + if (!PyTuple_Check(args)) { + PyErr_Format( + PyExc_TypeError, + "getsockaddrarg: " + "AF_INET address must be tuple, not %.500s", + args->ob_type->tp_name); + return 0; + } + if (!PyArg_ParseTuple(args, "eti:getsockaddrarg", + "idna", &host, &port)) + return 0; + result = setipaddr(host, (struct sockaddr *)addr, + sizeof(*addr), AF_INET); + PyMem_Free(host); + if (result < 0) + return 0; + addr->sin_family = AF_INET; + addr->sin_port = htons((short)port); + *addr_ret = (struct sockaddr *) addr; + *len_ret = sizeof *addr; + return 1; + } + +#ifdef ENABLE_IPV6 + case AF_INET6: + { + struct sockaddr_in6* addr; + char *host; + int port, flowinfo, scope_id, result; + addr = (struct sockaddr_in6*)&(s->sock_addr).in6; + flowinfo = scope_id = 0; + if (!PyArg_ParseTuple(args, "eti|ii", + "idna", &host, &port, &flowinfo, + &scope_id)) { + return 0; + } + result = setipaddr(host, (struct sockaddr *)addr, + sizeof(*addr), AF_INET6); + PyMem_Free(host); + if (result < 0) + return 0; + addr->sin6_family = s->sock_family; + addr->sin6_port = htons((short)port); + addr->sin6_flowinfo = flowinfo; + addr->sin6_scope_id = scope_id; + *addr_ret = (struct sockaddr *) addr; + *len_ret = sizeof *addr; + return 1; + } +#endif + +#ifdef HAVE_NETPACKET_PACKET_H + case AF_PACKET: + { + struct sockaddr_ll* addr; + struct ifreq ifr; + char *interfaceName; + int protoNumber; + int hatype = 0; + int pkttype = 0; + char *haddr; + + if (!PyArg_ParseTuple(args, "si|iis", &interfaceName, + &protoNumber, &pkttype, &hatype, &haddr)) + return 0; + strncpy(ifr.ifr_name, interfaceName, sizeof(ifr.ifr_name)); + ifr.ifr_name[(sizeof(ifr.ifr_name))-1] = '\0'; + if (ioctl(s->sock_fd, SIOCGIFINDEX, &ifr) < 0) { + s->errorhandler(); + return 0; + } + addr = &(s->sock_addr.ll); + addr->sll_family = AF_PACKET; + addr->sll_protocol = htons((short)protoNumber); + addr->sll_ifindex = ifr.ifr_ifindex; + addr->sll_pkttype = pkttype; + addr->sll_hatype = hatype; + *addr_ret = (struct sockaddr *) addr; + *len_ret = sizeof *addr; + return 1; + } +#endif + + /* More cases here... */ + + default: + PyErr_SetString(socket_error, "getsockaddrarg: bad family"); + return 0; + + } +} + + +/* Get the address length according to the socket object's address family. + Return 1 if the family is known, 0 otherwise. The length is returned + through len_ret. */ + +static int +getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret) +{ + switch (s->sock_family) { + +#if defined(AF_UNIX) && !defined(PYOS_OS2) + case AF_UNIX: + { + *len_ret = sizeof (struct sockaddr_un); + return 1; + } +#endif /* AF_UNIX */ + + case AF_INET: + { + *len_ret = sizeof (struct sockaddr_in); + return 1; + } + +#ifdef ENABLE_IPV6 + case AF_INET6: + { + *len_ret = sizeof (struct sockaddr_in6); + return 1; + } +#endif + +#ifdef HAVE_NETPACKET_PACKET_H + case AF_PACKET: + { + *len_ret = sizeof (struct sockaddr_ll); + return 1; + } +#endif + + /* More cases here... */ + + default: + PyErr_SetString(socket_error, "getsockaddrlen: bad family"); + return 0; + + } +} + + +/* s.accept() method */ + +static PyObject * +sock_accept(PySocketSockObject *s) +{ + char addrbuf[256]; + SOCKET_T newfd; + socklen_t addrlen; + PyObject *sock = NULL; + PyObject *addr = NULL; + PyObject *res = NULL; + int timeout; + + if (!getsockaddrlen(s, &addrlen)) + return NULL; + memset(addrbuf, 0, addrlen); + +#ifdef MS_WINDOWS + newfd = INVALID_SOCKET; +#else + newfd = -1; +#endif + + Py_BEGIN_ALLOW_THREADS + timeout = internal_select(s, 0); + if (!timeout) + newfd = accept(s->sock_fd, (struct sockaddr *) addrbuf, + &addrlen); + Py_END_ALLOW_THREADS + + if (timeout) { + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + +#ifdef MS_WINDOWS + if (newfd == INVALID_SOCKET) +#else + if (newfd < 0) +#endif + return s->errorhandler(); + + /* Create the new object with unspecified family, + to avoid calls to bind() etc. on it. */ + sock = (PyObject *) new_sockobject(newfd, + s->sock_family, + s->sock_type, + s->sock_proto); + + if (sock == NULL) { + SOCKETCLOSE(newfd); + goto finally; + } + addr = makesockaddr(s->sock_fd, (struct sockaddr *)addrbuf, + addrlen); + if (addr == NULL) + goto finally; + + res = Py_BuildValue("OO", sock, addr); + +finally: + Py_XDECREF(sock); + Py_XDECREF(addr); + return res; +} + +PyDoc_STRVAR(accept_doc, +"accept() -> (socket object, address info)\n\ +\n\ +Wait for an incoming connection. Return a new socket representing the\n\ +connection, and the address of the client. For IP sockets, the address\n\ +info is a pair (hostaddr, port)."); + +/* s.setblocking(flag) method. Argument: + False -- non-blocking mode; same as settimeout(0) + True -- blocking mode; same as settimeout(None) +*/ + +static PyObject * +sock_setblocking(PySocketSockObject *s, PyObject *arg) +{ + int block; + + block = PyInt_AsLong(arg); + if (block == -1 && PyErr_Occurred()) + return NULL; + + s->sock_timeout = block ? -1.0 : 0.0; + internal_setblocking(s, block); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(setblocking_doc, +"setblocking(flag)\n\ +\n\ +Set the socket to blocking (flag is true) or non-blocking (false).\n\ +setblocking(True) is equivalent to settimeout(None);\n\ +setblocking(False) is equivalent to settimeout(0.0)."); + +/* s.settimeout(timeout) method. Argument: + None -- no timeout, blocking mode; same as setblocking(True) + 0.0 -- non-blocking mode; same as setblocking(False) + > 0 -- timeout mode; operations time out after timeout seconds + < 0 -- illegal; raises an exception +*/ +static PyObject * +sock_settimeout(PySocketSockObject *s, PyObject *arg) +{ + double timeout; + + if (arg == Py_None) + timeout = -1.0; + else { + timeout = PyFloat_AsDouble(arg); + if (timeout < 0.0) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_ValueError, + "Timeout value out of range"); + return NULL; + } + } + + s->sock_timeout = timeout; + internal_setblocking(s, timeout < 0.0); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(settimeout_doc, +"settimeout(timeout)\n\ +\n\ +Set a timeout on socket operations. 'timeout' can be a float,\n\ +giving in seconds, or None. Setting a timeout of None disables\n\ +the timeout feature and is equivalent to setblocking(1).\n\ +Setting a timeout of zero is the same as setblocking(0)."); + +/* s.gettimeout() method. + Returns the timeout associated with a socket. */ +static PyObject * +sock_gettimeout(PySocketSockObject *s) +{ + if (s->sock_timeout < 0.0) { + Py_INCREF(Py_None); + return Py_None; + } + else + return PyFloat_FromDouble(s->sock_timeout); +} + +PyDoc_STRVAR(gettimeout_doc, +"gettimeout() -> timeout\n\ +\n\ +Returns the timeout in floating seconds associated with socket \n\ +operations. A timeout of None indicates that timeouts on socket \n\ +operations are disabled."); + +#ifdef RISCOS +/* s.sleeptaskw(1 | 0) method */ + +static PyObject * +sock_sleeptaskw(PySocketSockObject *s,PyObject *arg) +{ + int block; + block = PyInt_AsLong(arg); + if (block == -1 && PyErr_Occurred()) + return NULL; + Py_BEGIN_ALLOW_THREADS + socketioctl(s->sock_fd, 0x80046679, (u_long*)&block); + Py_END_ALLOW_THREADS + + Py_INCREF(Py_None); + return Py_None; +} +PyDoc_STRVAR(sleeptaskw_doc, +"sleeptaskw(flag)\n\ +\n\ +Allow sleeps in taskwindows."); +#endif + + +/* s.setsockopt() method. + With an integer third argument, sets an integer option. + With a string third argument, sets an option from a buffer; + use optional built-in module 'struct' to encode the string. */ + +static PyObject * +sock_setsockopt(PySocketSockObject *s, PyObject *args) +{ + int level; + int optname; + int res; + char *buf; + int buflen; + int flag; + + if (PyArg_ParseTuple(args, "iii:setsockopt", + &level, &optname, &flag)) { + buf = (char *) &flag; + buflen = sizeof flag; + } + else { + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "iis#:setsockopt", + &level, &optname, &buf, &buflen)) + return NULL; + } + res = setsockopt(s->sock_fd, level, optname, (void *)buf, buflen); + if (res < 0) + return s->errorhandler(); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(setsockopt_doc, +"setsockopt(level, option, value)\n\ +\n\ +Set a socket option. See the Unix manual for level and option.\n\ +The value argument can either be an integer or a string."); + + +/* s.getsockopt() method. + With two arguments, retrieves an integer option. + With a third integer argument, retrieves a string buffer of that size; + use optional built-in module 'struct' to decode the string. */ + +static PyObject * +sock_getsockopt(PySocketSockObject *s, PyObject *args) +{ + int level; + int optname; + int res; + PyObject *buf; + socklen_t buflen = 0; + +#ifdef __BEOS__ + /* We have incomplete socket support. */ + PyErr_SetString(socket_error, "getsockopt not supported"); + return NULL; +#else + + if (!PyArg_ParseTuple(args, "ii|i:getsockopt", + &level, &optname, &buflen)) + return NULL; + + if (buflen == 0) { + int flag = 0; + socklen_t flagsize = sizeof flag; + res = getsockopt(s->sock_fd, level, optname, + (void *)&flag, &flagsize); + if (res < 0) + return s->errorhandler(); + return PyInt_FromLong(flag); + } +#ifdef __VMS + if (buflen > 1024) { +#else + if (buflen <= 0 || buflen > 1024) { +#endif + PyErr_SetString(socket_error, + "getsockopt buflen out of range"); + return NULL; + } + buf = PyString_FromStringAndSize((char *)NULL, buflen); + if (buf == NULL) + return NULL; + res = getsockopt(s->sock_fd, level, optname, + (void *)PyString_AS_STRING(buf), &buflen); + if (res < 0) { + Py_DECREF(buf); + return s->errorhandler(); + } + _PyString_Resize(&buf, buflen); + return buf; +#endif /* __BEOS__ */ +} + +PyDoc_STRVAR(getsockopt_doc, +"getsockopt(level, option[, buffersize]) -> value\n\ +\n\ +Get a socket option. See the Unix manual for level and option.\n\ +If a nonzero buffersize argument is given, the return value is a\n\ +string of that length; otherwise it is an integer."); + + +/* s.bind(sockaddr) method */ + +static PyObject * +sock_bind(PySocketSockObject *s, PyObject *addro) +{ + struct sockaddr *addr; + int addrlen; + int res; + + if (!getsockaddrarg(s, addro, &addr, &addrlen)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = bind(s->sock_fd, addr, addrlen); + Py_END_ALLOW_THREADS + if (res < 0) + return s->errorhandler(); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(bind_doc, +"bind(address)\n\ +\n\ +Bind the socket to a local address. For IP sockets, the address is a\n\ +pair (host, port); the host must refer to the local host. For raw packet\n\ +sockets the address is a tuple (ifname, proto [,pkttype [,hatype]])"); + + +/* s.close() method. + Set the file descriptor to -1 so operations tried subsequently + will surely fail. */ + +static PyObject * +sock_close(PySocketSockObject *s) +{ + SOCKET_T fd; + + if ((fd = s->sock_fd) != -1) { + s->sock_fd = -1; + Py_BEGIN_ALLOW_THREADS + (void) SOCKETCLOSE(fd); + Py_END_ALLOW_THREADS + } + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(close_doc, +"close()\n\ +\n\ +Close the socket. It cannot be used after this call."); + +static int +internal_connect(PySocketSockObject *s, struct sockaddr *addr, int addrlen, + int *timeoutp) +{ + int res, timeout; + + timeout = 0; + res = connect(s->sock_fd, addr, addrlen); + +#ifdef MS_WINDOWS + + if (s->sock_timeout > 0.0) { + if (res < 0 && WSAGetLastError() == WSAEWOULDBLOCK) { + /* This is a mess. Best solution: trust select */ + fd_set fds; + struct timeval tv; + tv.tv_sec = (int)s->sock_timeout; + tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); + FD_ZERO(&fds); + FD_SET(s->sock_fd, &fds); + res = select(s->sock_fd+1, NULL, &fds, NULL, &tv); + if (res == 0) { + res = WSAEWOULDBLOCK; + timeout = 1; + } else if (res > 0) + res = 0; + /* else if (res < 0) an error occurred */ + } + } + + if (res < 0) + res = WSAGetLastError(); + +#else + + if (s->sock_timeout > 0.0) { + if (res < 0 && errno == EINPROGRESS) { + timeout = internal_select(s, 1); + res = connect(s->sock_fd, addr, addrlen); + if (res < 0 && errno == EISCONN) + res = 0; + } + } + + if (res < 0) + res = errno; + +#endif + *timeoutp = timeout; + + return res; +} + +/* s.connect(sockaddr) method */ + +static PyObject * +sock_connect(PySocketSockObject *s, PyObject *addro) +{ + struct sockaddr *addr; + int addrlen; + int res; + int timeout; + + if (!getsockaddrarg(s, addro, &addr, &addrlen)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + res = internal_connect(s, addr, addrlen, &timeout); + Py_END_ALLOW_THREADS + + if (timeout) { + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + if (res != 0) + return s->errorhandler(); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(connect_doc, +"connect(address)\n\ +\n\ +Connect the socket to a remote address. For IP sockets, the address\n\ +is a pair (host, port)."); + + +/* s.connect_ex(sockaddr) method */ + +static PyObject * +sock_connect_ex(PySocketSockObject *s, PyObject *addro) +{ + struct sockaddr *addr; + int addrlen; + int res; + int timeout; + + if (!getsockaddrarg(s, addro, &addr, &addrlen)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + res = internal_connect(s, addr, addrlen, &timeout); + Py_END_ALLOW_THREADS + + return PyInt_FromLong((long) res); +} + +PyDoc_STRVAR(connect_ex_doc, +"connect_ex(address) -> errno\n\ +\n\ +This is like connect(address), but returns an error code (the errno value)\n\ +instead of raising an exception when an error occurs."); + + +/* s.fileno() method */ + +static PyObject * +sock_fileno(PySocketSockObject *s) +{ +#if SIZEOF_SOCKET_T <= SIZEOF_LONG + return PyInt_FromLong((long) s->sock_fd); +#else + return PyLong_FromLongLong((PY_LONG_LONG)s->sock_fd); +#endif +} + +PyDoc_STRVAR(fileno_doc, +"fileno() -> integer\n\ +\n\ +Return the integer file descriptor of the socket."); + + +#ifndef NO_DUP +/* s.dup() method */ + +static PyObject * +sock_dup(PySocketSockObject *s) +{ + SOCKET_T newfd; + PyObject *sock; + + newfd = dup(s->sock_fd); + if (newfd < 0) + return s->errorhandler(); + sock = (PyObject *) new_sockobject(newfd, + s->sock_family, + s->sock_type, + s->sock_proto); + if (sock == NULL) + SOCKETCLOSE(newfd); + return sock; +} + +PyDoc_STRVAR(dup_doc, +"dup() -> socket object\n\ +\n\ +Return a new socket object connected to the same system resource."); + +#endif + + +/* s.getsockname() method */ + +static PyObject * +sock_getsockname(PySocketSockObject *s) +{ + char addrbuf[256]; + int res; + socklen_t addrlen; + + if (!getsockaddrlen(s, &addrlen)) + return NULL; + memset(addrbuf, 0, addrlen); + Py_BEGIN_ALLOW_THREADS + res = getsockname(s->sock_fd, (struct sockaddr *) addrbuf, &addrlen); + Py_END_ALLOW_THREADS + if (res < 0) + return s->errorhandler(); + return makesockaddr(s->sock_fd, (struct sockaddr *) addrbuf, addrlen); +} + +PyDoc_STRVAR(getsockname_doc, +"getsockname() -> address info\n\ +\n\ +Return the address of the local endpoint. For IP sockets, the address\n\ +info is a pair (hostaddr, port)."); + + +#ifdef HAVE_GETPEERNAME /* Cray APP doesn't have this :-( */ +/* s.getpeername() method */ + +static PyObject * +sock_getpeername(PySocketSockObject *s) +{ + char addrbuf[256]; + int res; + socklen_t addrlen; + + if (!getsockaddrlen(s, &addrlen)) + return NULL; + memset(addrbuf, 0, addrlen); + Py_BEGIN_ALLOW_THREADS + res = getpeername(s->sock_fd, (struct sockaddr *) addrbuf, &addrlen); + Py_END_ALLOW_THREADS + if (res < 0) + return s->errorhandler(); + return makesockaddr(s->sock_fd, (struct sockaddr *) addrbuf, addrlen); +} + +PyDoc_STRVAR(getpeername_doc, +"getpeername() -> address info\n\ +\n\ +Return the address of the remote endpoint. For IP sockets, the address\n\ +info is a pair (hostaddr, port)."); + +#endif /* HAVE_GETPEERNAME */ + + +/* s.listen(n) method */ + +static PyObject * +sock_listen(PySocketSockObject *s, PyObject *arg) +{ + int backlog; + int res; + + backlog = PyInt_AsLong(arg); + if (backlog == -1 && PyErr_Occurred()) + return NULL; + Py_BEGIN_ALLOW_THREADS + if (backlog < 1) + backlog = 1; + res = listen(s->sock_fd, backlog); + Py_END_ALLOW_THREADS + if (res < 0) + return s->errorhandler(); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(listen_doc, +"listen(backlog)\n\ +\n\ +Enable a server to accept connections. The backlog argument must be at\n\ +least 1; it specifies the number of unaccepted connection that the system\n\ +will allow before refusing new connections."); + + +#ifndef NO_DUP +/* s.makefile(mode) method. + Create a new open file object referring to a dupped version of + the socket's file descriptor. (The dup() call is necessary so + that the open file and socket objects may be closed independent + of each other.) + The mode argument specifies 'r' or 'w' passed to fdopen(). */ + +static PyObject * +sock_makefile(PySocketSockObject *s, PyObject *args) +{ + extern int fclose(FILE *); + char *mode = "r"; + int bufsize = -1; +#ifdef MS_WIN32 + Py_intptr_t fd; +#else + int fd; +#endif + FILE *fp; + PyObject *f; +#ifdef __VMS + char *mode_r = "r"; + char *mode_w = "w"; +#endif + + if (!PyArg_ParseTuple(args, "|si:makefile", &mode, &bufsize)) + return NULL; +#ifdef __VMS + if (strcmp(mode,"rb") == 0) { + mode = mode_r; + } + else { + if (strcmp(mode,"wb") == 0) { + mode = mode_w; + } + } +#endif +#ifdef MS_WIN32 + if (((fd = _open_osfhandle(s->sock_fd, _O_BINARY)) < 0) || + ((fd = dup(fd)) < 0) || ((fp = fdopen(fd, mode)) == NULL)) +#else + if ((fd = dup(s->sock_fd)) < 0 || (fp = fdopen(fd, mode)) == NULL) +#endif + { + if (fd >= 0) + SOCKETCLOSE(fd); + return s->errorhandler(); + } +#ifdef USE_GUSI2 + /* Workaround for bug in Metrowerks MSL vs. GUSI I/O library */ + if (strchr(mode, 'b') != NULL) + bufsize = 0; +#endif + f = PyFile_FromFile(fp, "", mode, fclose); + if (f != NULL) + PyFile_SetBufSize(f, bufsize); + return f; +} + +PyDoc_STRVAR(makefile_doc, +"makefile([mode[, buffersize]]) -> file object\n\ +\n\ +Return a regular file object corresponding to the socket.\n\ +The mode and buffersize arguments are as for the built-in open() function."); + +#endif /* NO_DUP */ + + +/* s.recv(nbytes [,flags]) method */ + +static PyObject * +sock_recv(PySocketSockObject *s, PyObject *args) +{ + int len, n = 0, flags = 0, timeout; + PyObject *buf; +#ifdef __VMS + int read_length; + char *read_buf; +#endif + + if (!PyArg_ParseTuple(args, "i|i:recv", &len, &flags)) + return NULL; + + if (len < 0) { + PyErr_SetString(PyExc_ValueError, + "negative buffersize in recv"); + return NULL; + } + + buf = PyString_FromStringAndSize((char *) 0, len); + if (buf == NULL) + return NULL; + +#ifndef __VMS + Py_BEGIN_ALLOW_THREADS + timeout = internal_select(s, 0); + if (!timeout) + n = recv(s->sock_fd, PyString_AS_STRING(buf), len, flags); + Py_END_ALLOW_THREADS + + if (timeout) { + Py_DECREF(buf); + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + if (n < 0) { + Py_DECREF(buf); + return s->errorhandler(); + } + if (n != len) + _PyString_Resize(&buf, n); +#else + read_buf = PyString_AsString(buf); + read_length = len; + while (read_length != 0) { + unsigned int segment; + + segment = read_length /SEGMENT_SIZE; + if (segment != 0) { + segment = SEGMENT_SIZE; + } + else { + segment = read_length; + } + + Py_BEGIN_ALLOW_THREADS + timeout = internal_select(s, 0); + if (!timeout) + n = recv(s->sock_fd, read_buf, segment, flags); + Py_END_ALLOW_THREADS + + if (timeout) { + Py_DECREF(buf); + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + if (n < 0) { + Py_DECREF(buf); + return s->errorhandler(); + } + if (n != read_length) { + read_buf += n; + break; + } + + read_length -= segment; + read_buf += segment; + } + if (_PyString_Resize(&buf, (read_buf - PyString_AsString(buf))) < 0) + { + return NULL; + } +#endif /* !__VMS */ + return buf; +} + +PyDoc_STRVAR(recv_doc, +"recv(buffersize[, flags]) -> data\n\ +\n\ +Receive up to buffersize bytes from the socket. For the optional flags\n\ +argument, see the Unix manual. When no data is available, block until\n\ +at least one byte is available or until the remote end is closed. When\n\ +the remote end is closed and all data is read, return the empty string."); + + +/* s.recvfrom(nbytes [,flags]) method */ + +static PyObject * +sock_recvfrom(PySocketSockObject *s, PyObject *args) +{ + char addrbuf[256]; + PyObject *buf = NULL; + PyObject *addr = NULL; + PyObject *ret = NULL; + int len, n = 0, flags = 0, timeout; + socklen_t addrlen; + + if (!PyArg_ParseTuple(args, "i|i:recvfrom", &len, &flags)) + return NULL; + + if (!getsockaddrlen(s, &addrlen)) + return NULL; + buf = PyString_FromStringAndSize((char *) 0, len); + if (buf == NULL) + return NULL; + + Py_BEGIN_ALLOW_THREADS + memset(addrbuf, 0, addrlen); + timeout = internal_select(s, 0); + if (!timeout) + n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags, +#ifndef MS_WINDOWS +#if defined(PYOS_OS2) && !defined(PYCC_GCC) + (struct sockaddr *)addrbuf, &addrlen +#else + (void *)addrbuf, &addrlen +#endif +#else + (struct sockaddr *)addrbuf, &addrlen +#endif + ); + Py_END_ALLOW_THREADS + + if (timeout) { + Py_DECREF(buf); + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + if (n < 0) { + Py_DECREF(buf); + return s->errorhandler(); + } + + if (n != len && _PyString_Resize(&buf, n) < 0) + return NULL; + + if (!(addr = makesockaddr(s->sock_fd, (struct sockaddr *)addrbuf, + addrlen))) + goto finally; + + ret = Py_BuildValue("OO", buf, addr); + +finally: + Py_XDECREF(addr); + Py_XDECREF(buf); + return ret; +} + +PyDoc_STRVAR(recvfrom_doc, +"recvfrom(buffersize[, flags]) -> (data, address info)\n\ +\n\ +Like recv(buffersize, flags) but also return the sender's address info."); + +/* s.send(data [,flags]) method */ + +static PyObject * +sock_send(PySocketSockObject *s, PyObject *args) +{ + char *buf; + int len, n = 0, flags = 0, timeout; +#ifdef __VMS + int send_length; +#endif + + if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags)) + return NULL; + +#ifndef __VMS + Py_BEGIN_ALLOW_THREADS + timeout = internal_select(s, 1); + if (!timeout) + n = send(s->sock_fd, buf, len, flags); + Py_END_ALLOW_THREADS + + if (timeout) { + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + if (n < 0) + return s->errorhandler(); +#else + /* Divide packet into smaller segments for */ + /* TCP/IP Services for OpenVMS */ + send_length = len; + while (send_length != 0) { + unsigned int segment; + + segment = send_length / SEGMENT_SIZE; + if (segment != 0) { + segment = SEGMENT_SIZE; + } + else { + segment = send_length; + } + Py_BEGIN_ALLOW_THREADS + timeout = internal_select(s, 1); + if (!timeout) + n = send(s->sock_fd, buf, segment, flags); + Py_END_ALLOW_THREADS + if (timeout) { + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + if (n < 0) { + return s->errorhandler(); + } + send_length -= segment; + buf += segment; + } /* end while */ +#endif /* !__VMS */ + return PyInt_FromLong((long)n); +} + +PyDoc_STRVAR(send_doc, +"send(data[, flags]) -> count\n\ +\n\ +Send a data string to the socket. For the optional flags\n\ +argument, see the Unix manual. Return the number of bytes\n\ +sent; this may be less than len(data) if the network is busy."); + + +/* s.sendall(data [,flags]) method */ + +static PyObject * +sock_sendall(PySocketSockObject *s, PyObject *args) +{ + char *buf; + int len, n = 0, flags = 0, timeout; + + if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + do { + timeout = internal_select(s, 1); + if (timeout) + break; + n = send(s->sock_fd, buf, len, flags); + if (n < 0) + break; + buf += n; + len -= n; + } while (len > 0); + Py_END_ALLOW_THREADS + + if (timeout) { + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + if (n < 0) + return s->errorhandler(); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(sendall_doc, +"sendall(data[, flags])\n\ +\n\ +Send a data string to the socket. For the optional flags\n\ +argument, see the Unix manual. This calls send() repeatedly\n\ +until all data is sent. If an error occurs, it's impossible\n\ +to tell how much data has been sent."); + + +/* s.sendto(data, [flags,] sockaddr) method */ + +static PyObject * +sock_sendto(PySocketSockObject *s, PyObject *args) +{ + PyObject *addro; + char *buf; + struct sockaddr *addr; + int addrlen, len, n = 0, flags, timeout; + + flags = 0; + if (!PyArg_ParseTuple(args, "s#O:sendto", &buf, &len, &addro)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "s#iO:sendto", + &buf, &len, &flags, &addro)) + return NULL; + } + + if (!getsockaddrarg(s, addro, &addr, &addrlen)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + timeout = internal_select(s, 1); + if (!timeout) + n = sendto(s->sock_fd, buf, len, flags, addr, addrlen); + Py_END_ALLOW_THREADS + + if (timeout) { + PyErr_SetString(socket_timeout, "timed out"); + return NULL; + } + if (n < 0) + return s->errorhandler(); + return PyInt_FromLong((long)n); +} + +PyDoc_STRVAR(sendto_doc, +"sendto(data[, flags], address) -> count\n\ +\n\ +Like send(data, flags) but allows specifying the destination address.\n\ +For IP sockets, the address is a pair (hostaddr, port)."); + + +/* s.shutdown(how) method */ + +static PyObject * +sock_shutdown(PySocketSockObject *s, PyObject *arg) +{ + int how; + int res; + + how = PyInt_AsLong(arg); + if (how == -1 && PyErr_Occurred()) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = shutdown(s->sock_fd, how); + Py_END_ALLOW_THREADS + if (res < 0) + return s->errorhandler(); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(shutdown_doc, +"shutdown(flag)\n\ +\n\ +Shut down the reading side of the socket (flag == 0), the writing side\n\ +of the socket (flag == 1), or both ends (flag == 2)."); + + +/* List of methods for socket objects */ + +static PyMethodDef sock_methods[] = { + {"accept", (PyCFunction)sock_accept, METH_NOARGS, + accept_doc}, + {"bind", (PyCFunction)sock_bind, METH_O, + bind_doc}, + {"close", (PyCFunction)sock_close, METH_NOARGS, + close_doc}, + {"connect", (PyCFunction)sock_connect, METH_O, + connect_doc}, + {"connect_ex", (PyCFunction)sock_connect_ex, METH_O, + connect_ex_doc}, +#ifndef NO_DUP + {"dup", (PyCFunction)sock_dup, METH_NOARGS, + dup_doc}, +#endif + {"fileno", (PyCFunction)sock_fileno, METH_NOARGS, + fileno_doc}, +#ifdef HAVE_GETPEERNAME + {"getpeername", (PyCFunction)sock_getpeername, + METH_NOARGS, getpeername_doc}, +#endif + {"getsockname", (PyCFunction)sock_getsockname, + METH_NOARGS, getsockname_doc}, + {"getsockopt", (PyCFunction)sock_getsockopt, METH_VARARGS, + getsockopt_doc}, + {"listen", (PyCFunction)sock_listen, METH_O, + listen_doc}, +#ifndef NO_DUP + {"makefile", (PyCFunction)sock_makefile, METH_VARARGS, + makefile_doc}, +#endif + {"recv", (PyCFunction)sock_recv, METH_VARARGS, + recv_doc}, + {"recvfrom", (PyCFunction)sock_recvfrom, METH_VARARGS, + recvfrom_doc}, + {"send", (PyCFunction)sock_send, METH_VARARGS, + send_doc}, + {"sendall", (PyCFunction)sock_sendall, METH_VARARGS, + sendall_doc}, + {"sendto", (PyCFunction)sock_sendto, METH_VARARGS, + sendto_doc}, + {"setblocking", (PyCFunction)sock_setblocking, METH_O, + setblocking_doc}, + {"settimeout", (PyCFunction)sock_settimeout, METH_O, + settimeout_doc}, + {"gettimeout", (PyCFunction)sock_gettimeout, METH_NOARGS, + gettimeout_doc}, + {"setsockopt", (PyCFunction)sock_setsockopt, METH_VARARGS, + setsockopt_doc}, + {"shutdown", (PyCFunction)sock_shutdown, METH_O, + shutdown_doc}, +#ifdef RISCOS + {"sleeptaskw", (PyCFunction)sock_sleeptaskw, METH_O, + sleeptaskw_doc}, +#endif + {NULL, NULL} /* sentinel */ +}; + + +/* Deallocate a socket object in response to the last Py_DECREF(). + First close the file description. */ + +static void +sock_dealloc(PySocketSockObject *s) +{ + if (s->sock_fd != -1) + (void) SOCKETCLOSE(s->sock_fd); + s->ob_type->tp_free((PyObject *)s); +} + + +static PyObject * +sock_repr(PySocketSockObject *s) +{ + char buf[512]; +#if SIZEOF_SOCKET_T > SIZEOF_LONG + if (s->sock_fd > LONG_MAX) { + /* this can occur on Win64, and actually there is a special + ugly printf formatter for decimal pointer length integer + printing, only bother if necessary*/ + PyErr_SetString(PyExc_OverflowError, + "no printf formatter to display " + "the socket descriptor in decimal"); + return NULL; + } +#endif + PyOS_snprintf( + buf, sizeof(buf), + "", + (long)s->sock_fd, s->sock_family, + s->sock_type, + s->sock_proto); + return PyString_FromString(buf); +} + + +/* Create a new, uninitialized socket object. */ + +static PyObject * +sock_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *new; + + new = type->tp_alloc(type, 0); + if (new != NULL) { + ((PySocketSockObject *)new)->sock_fd = -1; + ((PySocketSockObject *)new)->sock_timeout = -1.0; + ((PySocketSockObject *)new)->errorhandler = &set_error; + } + return new; +} + + +/* Initialize a new socket object. */ + +/*ARGSUSED*/ +static int +sock_initobj(PyObject *self, PyObject *args, PyObject *kwds) +{ + PySocketSockObject *s = (PySocketSockObject *)self; + SOCKET_T fd; + int family = AF_INET, type = SOCK_STREAM, proto = 0; + static char *keywords[] = {"family", "type", "proto", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "|iii:socket", keywords, + &family, &type, &proto)) + return -1; + + Py_BEGIN_ALLOW_THREADS + fd = socket(family, type, proto); + Py_END_ALLOW_THREADS + +#ifdef MS_WINDOWS + if (fd == INVALID_SOCKET) +#else + if (fd < 0) +#endif + { + set_error(); + return -1; + } + init_sockobject(s, fd, family, type, proto); + /* From now on, ignore SIGPIPE and let the error checking + do the work. */ +#ifdef SIGPIPE + (void) signal(SIGPIPE, SIG_IGN); +#endif + + return 0; + +} + + +/* Type object for socket objects. */ + +static PyTypeObject sock_type = { + PyObject_HEAD_INIT(0) /* Must fill in type value later */ + 0, /* ob_size */ + "_socket.socket", /* tp_name */ + sizeof(PySocketSockObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)sock_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)sock_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + sock_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + sock_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + sock_initobj, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + sock_new, /* tp_new */ + PyObject_Del, /* tp_free */ +}; + + +/* Python interface to gethostname(). */ + +/*ARGSUSED*/ +static PyObject * +socket_gethostname(PyObject *self, PyObject *args) +{ + char buf[1024]; + int res; + if (!PyArg_ParseTuple(args, ":gethostname")) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = gethostname(buf, (int) sizeof buf - 1); + Py_END_ALLOW_THREADS + if (res < 0) + return set_error(); + buf[sizeof buf - 1] = '\0'; + return PyString_FromString(buf); +} + +PyDoc_STRVAR(gethostname_doc, +"gethostname() -> string\n\ +\n\ +Return the current host name."); + + +/* Python interface to gethostbyname(name). */ + +/*ARGSUSED*/ +static PyObject * +socket_gethostbyname(PyObject *self, PyObject *args) +{ + char *name; +#ifdef ENABLE_IPV6 + struct sockaddr_storage addrbuf; +#else + struct sockaddr_in addrbuf; +#endif + + if (!PyArg_ParseTuple(args, "s:gethostbyname", &name)) + return NULL; + if (setipaddr(name, (struct sockaddr *)&addrbuf, sizeof(addrbuf), AF_INET) < 0) + return NULL; + return makeipaddr((struct sockaddr *)&addrbuf, + sizeof(struct sockaddr_in)); +} + +PyDoc_STRVAR(gethostbyname_doc, +"gethostbyname(host) -> address\n\ +\n\ +Return the IP address (a string of the form '255.255.255.255') for a host."); + + +/* Convenience function common to gethostbyname_ex and gethostbyaddr */ + +static PyObject * +gethost_common(struct hostent *h, struct sockaddr *addr, int alen, int af) +{ + char **pch; + PyObject *rtn_tuple = (PyObject *)NULL; + PyObject *name_list = (PyObject *)NULL; + PyObject *addr_list = (PyObject *)NULL; + PyObject *tmp; + + if (h == NULL) { + /* Let's get real error message to return */ +#ifndef RISCOS + set_herror(h_errno); +#else + PyErr_SetString(socket_error, "host not found"); +#endif + return NULL; + } + + if (h->h_addrtype != af) { +#ifdef HAVE_STRERROR + /* Let's get real error message to return */ + PyErr_SetString(socket_error, + (char *)strerror(EAFNOSUPPORT)); +#else + PyErr_SetString( + socket_error, + "Address family not supported by protocol family"); +#endif + return NULL; + } + + switch (af) { + + case AF_INET: + if (alen < sizeof(struct sockaddr_in)) + return NULL; + break; + +#ifdef ENABLE_IPV6 + case AF_INET6: + if (alen < sizeof(struct sockaddr_in6)) + return NULL; + break; +#endif + + } + + if ((name_list = PyList_New(0)) == NULL) + goto err; + + if ((addr_list = PyList_New(0)) == NULL) + goto err; + + for (pch = h->h_aliases; *pch != NULL; pch++) { + int status; + tmp = PyString_FromString(*pch); + if (tmp == NULL) + goto err; + + status = PyList_Append(name_list, tmp); + Py_DECREF(tmp); + + if (status) + goto err; + } + + for (pch = h->h_addr_list; *pch != NULL; pch++) { + int status; + + switch (af) { + + case AF_INET: + { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = af; +#ifdef HAVE_SOCKADDR_SA_LEN + sin.sin_len = sizeof(sin); +#endif + memcpy(&sin.sin_addr, *pch, sizeof(sin.sin_addr)); + tmp = makeipaddr((struct sockaddr *)&sin, sizeof(sin)); + + if (pch == h->h_addr_list && alen >= sizeof(sin)) + memcpy((char *) addr, &sin, sizeof(sin)); + break; + } + +#ifdef ENABLE_IPV6 + case AF_INET6: + { + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = af; +#ifdef HAVE_SOCKADDR_SA_LEN + sin6.sin6_len = sizeof(sin6); +#endif + memcpy(&sin6.sin6_addr, *pch, sizeof(sin6.sin6_addr)); + tmp = makeipaddr((struct sockaddr *)&sin6, + sizeof(sin6)); + + if (pch == h->h_addr_list && alen >= sizeof(sin6)) + memcpy((char *) addr, &sin6, sizeof(sin6)); + break; + } +#endif + + default: /* can't happen */ + PyErr_SetString(socket_error, + "unsupported address family"); + return NULL; + } + + if (tmp == NULL) + goto err; + + status = PyList_Append(addr_list, tmp); + Py_DECREF(tmp); + + if (status) + goto err; + } + + rtn_tuple = Py_BuildValue("sOO", h->h_name, name_list, addr_list); + + err: + Py_XDECREF(name_list); + Py_XDECREF(addr_list); + return rtn_tuple; +} + + +/* Python interface to gethostbyname_ex(name). */ + +/*ARGSUSED*/ +static PyObject * +socket_gethostbyname_ex(PyObject *self, PyObject *args) +{ + char *name; + struct hostent *h; +#ifdef ENABLE_IPV6 + struct sockaddr_storage addr; +#else + struct sockaddr_in addr; +#endif + struct sockaddr *sa; + PyObject *ret; +#ifdef HAVE_GETHOSTBYNAME_R + struct hostent hp_allocated; +#ifdef HAVE_GETHOSTBYNAME_R_3_ARG + struct hostent_data data; +#else + char buf[16384]; + int buf_len = (sizeof buf) - 1; + int errnop; +#endif +#if defined(HAVE_GETHOSTBYNAME_R_3_ARG) || defined(HAVE_GETHOSTBYNAME_R_6_ARG) + int result; +#endif +#endif /* HAVE_GETHOSTBYNAME_R */ + + if (!PyArg_ParseTuple(args, "s:gethostbyname_ex", &name)) + return NULL; + if (setipaddr(name, (struct sockaddr *)&addr, sizeof(addr), AF_INET) < 0) + return NULL; + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_GETHOSTBYNAME_R +#if defined(HAVE_GETHOSTBYNAME_R_6_ARG) + result = gethostbyname_r(name, &hp_allocated, buf, buf_len, + &h, &errnop); +#elif defined(HAVE_GETHOSTBYNAME_R_5_ARG) + h = gethostbyname_r(name, &hp_allocated, buf, buf_len, &errnop); +#else /* HAVE_GETHOSTBYNAME_R_3_ARG */ + memset((void *) &data, '\0', sizeof(data)); + result = gethostbyname_r(name, &hp_allocated, &data); + h = (result != 0) ? NULL : &hp_allocated; +#endif +#else /* not HAVE_GETHOSTBYNAME_R */ +#ifdef USE_GETHOSTBYNAME_LOCK + PyThread_acquire_lock(netdb_lock, 1); +#endif + h = gethostbyname(name); +#endif /* HAVE_GETHOSTBYNAME_R */ + Py_END_ALLOW_THREADS + /* Some C libraries would require addr.__ss_family instead of + addr.ss_family. + Therefore, we cast the sockaddr_storage into sockaddr to + access sa_family. */ + sa = (struct sockaddr*)&addr; + ret = gethost_common(h, (struct sockaddr *)&addr, sizeof(addr), + sa->sa_family); +#ifdef USE_GETHOSTBYNAME_LOCK + PyThread_release_lock(netdb_lock); +#endif + return ret; +} + +PyDoc_STRVAR(ghbn_ex_doc, +"gethostbyname_ex(host) -> (name, aliaslist, addresslist)\n\ +\n\ +Return the true host name, a list of aliases, and a list of IP addresses,\n\ +for a host. The host argument is a string giving a host name or IP number."); + + +/* Python interface to gethostbyaddr(IP). */ + +/*ARGSUSED*/ +static PyObject * +socket_gethostbyaddr(PyObject *self, PyObject *args) +{ +#ifdef ENABLE_IPV6 + struct sockaddr_storage addr; +#else + struct sockaddr_in addr; +#endif + struct sockaddr *sa = (struct sockaddr *)&addr; + char *ip_num; + struct hostent *h; + PyObject *ret; +#ifdef HAVE_GETHOSTBYNAME_R + struct hostent hp_allocated; +#ifdef HAVE_GETHOSTBYNAME_R_3_ARG + struct hostent_data data; +#else + char buf[16384]; + int buf_len = (sizeof buf) - 1; + int errnop; +#endif +#if defined(HAVE_GETHOSTBYNAME_R_3_ARG) || defined(HAVE_GETHOSTBYNAME_R_6_ARG) + int result; +#endif +#endif /* HAVE_GETHOSTBYNAME_R */ + char *ap; + int al; + int af; + + if (!PyArg_ParseTuple(args, "s:gethostbyaddr", &ip_num)) + return NULL; + af = AF_UNSPEC; + if (setipaddr(ip_num, sa, sizeof(addr), af) < 0) + return NULL; + af = sa->sa_family; + ap = NULL; + al = 0; + switch (af) { + case AF_INET: + ap = (char *)&((struct sockaddr_in *)sa)->sin_addr; + al = sizeof(((struct sockaddr_in *)sa)->sin_addr); + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + ap = (char *)&((struct sockaddr_in6 *)sa)->sin6_addr; + al = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr); + break; +#endif + default: + PyErr_SetString(socket_error, "unsupported address family"); + return NULL; + } + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_GETHOSTBYNAME_R +#if defined(HAVE_GETHOSTBYNAME_R_6_ARG) + result = gethostbyaddr_r(ap, al, af, + &hp_allocated, buf, buf_len, + &h, &errnop); +#elif defined(HAVE_GETHOSTBYNAME_R_5_ARG) + h = gethostbyaddr_r(ap, al, af, + &hp_allocated, buf, buf_len, &errnop); +#else /* HAVE_GETHOSTBYNAME_R_3_ARG */ + memset((void *) &data, '\0', sizeof(data)); + result = gethostbyaddr_r(ap, al, af, &hp_allocated, &data); + h = (result != 0) ? NULL : &hp_allocated; +#endif +#else /* not HAVE_GETHOSTBYNAME_R */ +#ifdef USE_GETHOSTBYNAME_LOCK + PyThread_acquire_lock(netdb_lock, 1); +#endif + h = gethostbyaddr(ap, al, af); +#endif /* HAVE_GETHOSTBYNAME_R */ + Py_END_ALLOW_THREADS + ret = gethost_common(h, (struct sockaddr *)&addr, sizeof(addr), af); +#ifdef USE_GETHOSTBYNAME_LOCK + PyThread_release_lock(netdb_lock); +#endif + return ret; +} + +PyDoc_STRVAR(gethostbyaddr_doc, +"gethostbyaddr(host) -> (name, aliaslist, addresslist)\n\ +\n\ +Return the true host name, a list of aliases, and a list of IP addresses,\n\ +for a host. The host argument is a string giving a host name or IP number."); + + +/* Python interface to getservbyname(name). + This only returns the port number, since the other info is already + known or not useful (like the list of aliases). */ + +/*ARGSUSED*/ +static PyObject * +socket_getservbyname(PyObject *self, PyObject *args) +{ + char *name, *proto; + struct servent *sp; + if (!PyArg_ParseTuple(args, "ss:getservbyname", &name, &proto)) + return NULL; + Py_BEGIN_ALLOW_THREADS + sp = getservbyname(name, proto); + Py_END_ALLOW_THREADS + if (sp == NULL) { + PyErr_SetString(socket_error, "service/proto not found"); + return NULL; + } + return PyInt_FromLong((long) ntohs(sp->s_port)); +} + +PyDoc_STRVAR(getservbyname_doc, +"getservbyname(servicename, protocolname) -> integer\n\ +\n\ +Return a port number from a service name and protocol name.\n\ +The protocol name should be 'tcp' or 'udp'."); + + +/* Python interface to getprotobyname(name). + This only returns the protocol number, since the other info is + already known or not useful (like the list of aliases). */ + +/*ARGSUSED*/ +static PyObject * +socket_getprotobyname(PyObject *self, PyObject *args) +{ + char *name; + struct protoent *sp; +#ifdef __BEOS__ +/* Not available in BeOS yet. - [cjh] */ + PyErr_SetString(socket_error, "getprotobyname not supported"); + return NULL; +#else + if (!PyArg_ParseTuple(args, "s:getprotobyname", &name)) + return NULL; + Py_BEGIN_ALLOW_THREADS + sp = getprotobyname(name); + Py_END_ALLOW_THREADS + if (sp == NULL) { + PyErr_SetString(socket_error, "protocol not found"); + return NULL; + } + return PyInt_FromLong((long) sp->p_proto); +#endif +} + +PyDoc_STRVAR(getprotobyname_doc, +"getprotobyname(name) -> integer\n\ +\n\ +Return the protocol number for the named protocol. (Rarely used.)"); + + +#ifndef NO_DUP +/* Create a socket object from a numeric file description. + Useful e.g. if stdin is a socket. + Additional arguments as for socket(). */ + +/*ARGSUSED*/ +static PyObject * +socket_fromfd(PyObject *self, PyObject *args) +{ + PySocketSockObject *s; + SOCKET_T fd; + int family, type, proto = 0; + if (!PyArg_ParseTuple(args, "iii|i:fromfd", + &fd, &family, &type, &proto)) + return NULL; + /* Dup the fd so it and the socket can be closed independently */ + fd = dup(fd); + if (fd < 0) + return set_error(); + s = new_sockobject(fd, family, type, proto); + /* From now on, ignore SIGPIPE and let the error checking + do the work. */ +#ifdef SIGPIPE + (void) signal(SIGPIPE, SIG_IGN); +#endif + return (PyObject *) s; +} + +PyDoc_STRVAR(fromfd_doc, +"fromfd(fd, family, type[, proto]) -> socket object\n\ +\n\ +Create a socket object from the given file descriptor.\n\ +The remaining arguments are the same as for socket()."); + +#endif /* NO_DUP */ + + +static PyObject * +socket_ntohs(PyObject *self, PyObject *args) +{ + int x1, x2; + + if (!PyArg_ParseTuple(args, "i:ntohs", &x1)) { + return NULL; + } + x2 = (int)ntohs((short)x1); + return PyInt_FromLong(x2); +} + +PyDoc_STRVAR(ntohs_doc, +"ntohs(integer) -> integer\n\ +\n\ +Convert a 16-bit integer from network to host byte order."); + + +static PyObject * +socket_ntohl(PyObject *self, PyObject *arg) +{ + unsigned long x; + + if (PyInt_Check(arg)) { + x = PyInt_AS_LONG(arg); + if (x == (unsigned long) -1 && PyErr_Occurred()) + return NULL; + } + else if (PyLong_Check(arg)) { + x = PyLong_AsUnsignedLong(arg); + if (x == (unsigned long) -1 && PyErr_Occurred()) + return NULL; +#if SIZEOF_LONG > 4 + { + unsigned long y; + /* only want the trailing 32 bits */ + y = x & 0xFFFFFFFFUL; + if (y ^ x) + return PyErr_Format(PyExc_OverflowError, + "long int larger than 32 bits"); + x = y; + } +#endif + } + else + return PyErr_Format(PyExc_TypeError, + "expected int/long, %s found", + arg->ob_type->tp_name); + if (x == (unsigned long) -1 && PyErr_Occurred()) + return NULL; + return PyInt_FromLong(ntohl(x)); +} + +PyDoc_STRVAR(ntohl_doc, +"ntohl(integer) -> integer\n\ +\n\ +Convert a 32-bit integer from network to host byte order."); + + +static PyObject * +socket_htons(PyObject *self, PyObject *args) +{ + unsigned long x1, x2; + + if (!PyArg_ParseTuple(args, "i:htons", &x1)) { + return NULL; + } + x2 = (int)htons((short)x1); + return PyInt_FromLong(x2); +} + +PyDoc_STRVAR(htons_doc, +"htons(integer) -> integer\n\ +\n\ +Convert a 16-bit integer from host to network byte order."); + + +static PyObject * +socket_htonl(PyObject *self, PyObject *arg) +{ + unsigned long x; + + if (PyInt_Check(arg)) { + x = PyInt_AS_LONG(arg); + if (x == (unsigned long) -1 && PyErr_Occurred()) + return NULL; + } + else if (PyLong_Check(arg)) { + x = PyLong_AsUnsignedLong(arg); + if (x == (unsigned long) -1 && PyErr_Occurred()) + return NULL; +#if SIZEOF_LONG > 4 + { + unsigned long y; + /* only want the trailing 32 bits */ + y = x & 0xFFFFFFFFUL; + if (y ^ x) + return PyErr_Format(PyExc_OverflowError, + "long int larger than 32 bits"); + x = y; + } +#endif + } + else + return PyErr_Format(PyExc_TypeError, + "expected int/long, %s found", + arg->ob_type->tp_name); + return PyInt_FromLong(htonl(x)); +} + +PyDoc_STRVAR(htonl_doc, +"htonl(integer) -> integer\n\ +\n\ +Convert a 32-bit integer from host to network byte order."); + +/* socket.inet_aton() and socket.inet_ntoa() functions. */ + +PyDoc_STRVAR(inet_aton_doc, +"inet_aton(string) -> packed 32-bit IP representation\n\ +\n\ +Convert an IP address in string format (123.45.67.89) to the 32-bit packed\n\ +binary format used in low-level network functions."); + +static PyObject* +socket_inet_aton(PyObject *self, PyObject *args) +{ +#ifndef INADDR_NONE +#define INADDR_NONE (-1) +#endif +#ifdef HAVE_INET_ATON + struct in_addr buf; +#else + /* Have to use inet_addr() instead */ + unsigned long packed_addr; +#endif + char *ip_addr; + + if (!PyArg_ParseTuple(args, "s:inet_aton", &ip_addr)) + return NULL; + + +#ifdef HAVE_INET_ATON + if (inet_aton(ip_addr, &buf)) + return PyString_FromStringAndSize((char *)(&buf), + sizeof(buf)); + + PyErr_SetString(socket_error, + "illegal IP address string passed to inet_aton"); + return NULL; + +#else /* ! HAVE_INET_ATON */ + /* XXX Problem here: inet_aton('255.255.255.255') raises + an exception while it should be a valid address. */ + packed_addr = inet_addr(ip_addr); + + if (packed_addr == INADDR_NONE) { /* invalid address */ + PyErr_SetString(socket_error, + "illegal IP address string passed to inet_aton"); + return NULL; + } + return PyString_FromStringAndSize((char *) &packed_addr, + sizeof(packed_addr)); +#endif +} + +PyDoc_STRVAR(inet_ntoa_doc, +"inet_ntoa(packed_ip) -> ip_address_string\n\ +\n\ +Convert an IP address from 32-bit packed binary format to string format"); + +static PyObject* +socket_inet_ntoa(PyObject *self, PyObject *args) +{ + char *packed_str; + int addr_len; + struct in_addr packed_addr; + + if (!PyArg_ParseTuple(args, "s#:inet_ntoa", &packed_str, &addr_len)) { + return NULL; + } + + if (addr_len != sizeof(packed_addr)) { + PyErr_SetString(socket_error, + "packed IP wrong length for inet_ntoa"); + return NULL; + } + + memcpy(&packed_addr, packed_str, addr_len); + + return PyString_FromString(inet_ntoa(packed_addr)); +} + +#ifdef HAVE_INET_PTON + +PyDoc_STRVAR(inet_pton_doc, +"inet_pton(af, ip) -> packed IP address string\n\ +\n\ +Convert an IP address from string format to a packed string suitable\n\ +for use with low-level network functions."); + +static PyObject * +socket_inet_pton(PyObject *self, PyObject *args) +{ + int af; + char* ip; + int retval; +#ifdef ENABLE_IPV6 + char packed[MAX(sizeof(struct in_addr), sizeof(struct in6_addr))]; +#else + char packed[sizeof(struct in_addr)]; +#endif + if (!PyArg_ParseTuple(args, "is:inet_pton", &af, &ip)) { + return NULL; + } + +#ifndef ENABLE_IPV6 + if(af == AF_INET6) { + PyErr_SetString(socket_error, + "can't use AF_INET6, IPv6 is disabled"); + return NULL; + } +#endif + + retval = inet_pton(af, ip, packed); + if (retval < 0) { + PyErr_SetFromErrno(socket_error); + return NULL; + } else if (retval == 0) { + PyErr_SetString(socket_error, + "illegal IP address string passed to inet_pton"); + return NULL; + } else if (af == AF_INET) { + return PyString_FromStringAndSize(packed, + sizeof(struct in_addr)); +#ifdef ENABLE_IPV6 + } else if (af == AF_INET6) { + return PyString_FromStringAndSize(packed, + sizeof(struct in6_addr)); +#endif + } else { + PyErr_SetString(socket_error, "unknown address family"); + return NULL; + } +} + +PyDoc_STRVAR(inet_ntop_doc, +"inet_ntop(af, packed_ip) -> string formatted IP address\n\ +\n\ +Convert a packed IP address of the given family to string format."); + +static PyObject * +socket_inet_ntop(PyObject *self, PyObject *args) +{ + int af; + char* packed; + int len; + const char* retval; +#ifdef ENABLE_IPV6 + char ip[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1]; +#else + char ip[INET_ADDRSTRLEN + 1]; +#endif + + /* Guarantee NUL-termination for PyString_FromString() below */ + memset((void *) &ip[0], '\0', sizeof(ip) + 1); + + if (!PyArg_ParseTuple(args, "is#:inet_ntop", &af, &packed, &len)) { + return NULL; + } + + if (af == AF_INET) { + if (len != sizeof(struct in_addr)) { + PyErr_SetString(PyExc_ValueError, + "invalid length of packed IP address string"); + return NULL; + } +#ifdef ENABLE_IPV6 + } else if (af == AF_INET6) { + if (len != sizeof(struct in6_addr)) { + PyErr_SetString(PyExc_ValueError, + "invalid length of packed IP address string"); + return NULL; + } +#endif + } else { + PyErr_Format(PyExc_ValueError, + "unknown address family %d", af); + return NULL; + } + + retval = inet_ntop(af, packed, ip, sizeof(ip)); + if (!retval) { + PyErr_SetFromErrno(socket_error); + return NULL; + } else { + return PyString_FromString(retval); + } + + /* NOTREACHED */ + PyErr_SetString(PyExc_RuntimeError, "invalid handling of inet_ntop"); + return NULL; +} + +#endif /* HAVE_INET_PTON */ + +/* Python interface to getaddrinfo(host, port). */ + +/*ARGSUSED*/ +static PyObject * +socket_getaddrinfo(PyObject *self, PyObject *args) +{ + struct addrinfo hints, *res; + struct addrinfo *res0 = NULL; + PyObject *hobj = NULL; + PyObject *pobj = (PyObject *)NULL; + char pbuf[30]; + char *hptr, *pptr; + int family, socktype, protocol, flags; + int error; + PyObject *all = (PyObject *)NULL; + PyObject *single = (PyObject *)NULL; + PyObject *idna = NULL; + + family = socktype = protocol = flags = 0; + family = AF_UNSPEC; + if (!PyArg_ParseTuple(args, "OO|iiii:getaddrinfo", + &hobj, &pobj, &family, &socktype, + &protocol, &flags)) { + return NULL; + } + if (hobj == Py_None) { + hptr = NULL; + } else if (PyUnicode_Check(hobj)) { + idna = PyObject_CallMethod(hobj, "encode", "s", "idna"); + if (!idna) + return NULL; + hptr = PyString_AsString(idna); + } else if (PyString_Check(hobj)) { + hptr = PyString_AsString(hobj); + } else { + PyErr_SetString(PyExc_TypeError, + "getaddrinfo() argument 1 must be string or None"); + return NULL; + } + if (PyInt_Check(pobj)) { + PyOS_snprintf(pbuf, sizeof(pbuf), "%ld", PyInt_AsLong(pobj)); + pptr = pbuf; + } else if (PyString_Check(pobj)) { + pptr = PyString_AsString(pobj); + } else if (pobj == Py_None) { + pptr = (char *)NULL; + } else { + PyErr_SetString(socket_error, "Int or String expected"); + goto err; + } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = socktype; + hints.ai_protocol = protocol; + hints.ai_flags = flags; + Py_BEGIN_ALLOW_THREADS + ACQUIRE_GETADDRINFO_LOCK + error = getaddrinfo(hptr, pptr, &hints, &res0); + Py_END_ALLOW_THREADS + RELEASE_GETADDRINFO_LOCK /* see comment in setipaddr() */ + if (error) { + set_gaierror(error); + goto err; + } + + if ((all = PyList_New(0)) == NULL) + goto err; + for (res = res0; res; res = res->ai_next) { + PyObject *addr = + makesockaddr(-1, res->ai_addr, res->ai_addrlen); + if (addr == NULL) + goto err; + single = Py_BuildValue("iiisO", res->ai_family, + res->ai_socktype, res->ai_protocol, + res->ai_canonname ? res->ai_canonname : "", + addr); + Py_DECREF(addr); + if (single == NULL) + goto err; + + if (PyList_Append(all, single)) + goto err; + Py_XDECREF(single); + } + Py_XDECREF(idna); + if (res0) + freeaddrinfo(res0); + return all; + err: + Py_XDECREF(single); + Py_XDECREF(all); + Py_XDECREF(idna); + if (res0) + freeaddrinfo(res0); + return (PyObject *)NULL; +} + +PyDoc_STRVAR(getaddrinfo_doc, +"getaddrinfo(host, port [, family, socktype, proto, flags])\n\ + -> list of (family, socktype, proto, canonname, sockaddr)\n\ +\n\ +Resolve host and port into addrinfo struct."); + +/* Python interface to getnameinfo(sa, flags). */ + +/*ARGSUSED*/ +static PyObject * +socket_getnameinfo(PyObject *self, PyObject *args) +{ + PyObject *sa = (PyObject *)NULL; + int flags; + char *hostp; + int port, flowinfo, scope_id; + char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; + struct addrinfo hints, *res = NULL; + int error; + PyObject *ret = (PyObject *)NULL; + + flags = flowinfo = scope_id = 0; + if (!PyArg_ParseTuple(args, "Oi:getnameinfo", &sa, &flags)) + return NULL; + if (!PyArg_ParseTuple(sa, "si|ii", + &hostp, &port, &flowinfo, &scope_id)) + return NULL; + PyOS_snprintf(pbuf, sizeof(pbuf), "%d", port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /* make numeric port happy */ + Py_BEGIN_ALLOW_THREADS + ACQUIRE_GETADDRINFO_LOCK + error = getaddrinfo(hostp, pbuf, &hints, &res); + Py_END_ALLOW_THREADS + RELEASE_GETADDRINFO_LOCK /* see comment in setipaddr() */ + if (error) { + set_gaierror(error); + goto fail; + } + if (res->ai_next) { + PyErr_SetString(socket_error, + "sockaddr resolved to multiple addresses"); + goto fail; + } + switch (res->ai_family) { + case AF_INET: + { + char *t1; + int t2; + if (PyArg_ParseTuple(sa, "si", &t1, &t2) == 0) { + PyErr_SetString(socket_error, + "IPv4 sockaddr must be 2 tuple"); + goto fail; + } + break; + } +#ifdef ENABLE_IPV6 + case AF_INET6: + { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)res->ai_addr; + sin6->sin6_flowinfo = flowinfo; + sin6->sin6_scope_id = scope_id; + break; + } +#endif + } + error = getnameinfo(res->ai_addr, res->ai_addrlen, + hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), flags); + if (error) { + set_gaierror(error); + goto fail; + } + ret = Py_BuildValue("ss", hbuf, pbuf); + +fail: + if (res) + freeaddrinfo(res); + return ret; +} + +PyDoc_STRVAR(getnameinfo_doc, +"getnameinfo(sockaddr, flags) --> (host, port)\n\ +\n\ +Get host and port for a sockaddr."); + + +/* Python API to getting and setting the default timeout value. */ + +static PyObject * +socket_getdefaulttimeout(PyObject *self) +{ + if (defaulttimeout < 0.0) { + Py_INCREF(Py_None); + return Py_None; + } + else + return PyFloat_FromDouble(defaulttimeout); +} + +PyDoc_STRVAR(getdefaulttimeout_doc, +"getdefaulttimeout() -> timeout\n\ +\n\ +Returns the default timeout in floating seconds for new socket objects.\n\ +A value of None indicates that new socket objects have no timeout.\n\ +When the socket module is first imported, the default is None."); + +static PyObject * +socket_setdefaulttimeout(PyObject *self, PyObject *arg) +{ + double timeout; + + if (arg == Py_None) + timeout = -1.0; + else { + timeout = PyFloat_AsDouble(arg); + if (timeout < 0.0) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_ValueError, + "Timeout value out of range"); + return NULL; + } + } + + defaulttimeout = timeout; + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(setdefaulttimeout_doc, +"setdefaulttimeout(timeout)\n\ +\n\ +Set the default timeout in floating seconds for new socket objects.\n\ +A value of None indicates that new socket objects have no timeout.\n\ +When the socket module is first imported, the default is None."); + + +/* List of functions exported by this module. */ + +static PyMethodDef socket_methods[] = { + {"gethostbyname", socket_gethostbyname, + METH_VARARGS, gethostbyname_doc}, + {"gethostbyname_ex", socket_gethostbyname_ex, + METH_VARARGS, ghbn_ex_doc}, + {"gethostbyaddr", socket_gethostbyaddr, + METH_VARARGS, gethostbyaddr_doc}, + {"gethostname", socket_gethostname, + METH_VARARGS, gethostname_doc}, + {"getservbyname", socket_getservbyname, + METH_VARARGS, getservbyname_doc}, + {"getprotobyname", socket_getprotobyname, + METH_VARARGS,getprotobyname_doc}, +#ifndef NO_DUP + {"fromfd", socket_fromfd, + METH_VARARGS, fromfd_doc}, +#endif + {"ntohs", socket_ntohs, + METH_VARARGS, ntohs_doc}, + {"ntohl", socket_ntohl, + METH_O, ntohl_doc}, + {"htons", socket_htons, + METH_VARARGS, htons_doc}, + {"htonl", socket_htonl, + METH_O, htonl_doc}, + {"inet_aton", socket_inet_aton, + METH_VARARGS, inet_aton_doc}, + {"inet_ntoa", socket_inet_ntoa, + METH_VARARGS, inet_ntoa_doc}, +#ifdef HAVE_INET_PTON + {"inet_pton", socket_inet_pton, + METH_VARARGS, inet_pton_doc}, + {"inet_ntop", socket_inet_ntop, + METH_VARARGS, inet_ntop_doc}, +#endif + {"getaddrinfo", socket_getaddrinfo, + METH_VARARGS, getaddrinfo_doc}, + {"getnameinfo", socket_getnameinfo, + METH_VARARGS, getnameinfo_doc}, + {"getdefaulttimeout", (PyCFunction)socket_getdefaulttimeout, + METH_NOARGS, getdefaulttimeout_doc}, + {"setdefaulttimeout", socket_setdefaulttimeout, + METH_O, setdefaulttimeout_doc}, + {NULL, NULL} /* Sentinel */ +}; + + +#ifdef RISCOS +#define OS_INIT_DEFINED + +static int +os_init(void) +{ + _kernel_swi_regs r; + + r.r[0] = 0; + _kernel_swi(0x43380, &r, &r); + taskwindow = r.r[0]; + + return 1; +} + +#endif /* RISCOS */ + + +#ifdef MS_WINDOWS +#define OS_INIT_DEFINED + +/* Additional initialization and cleanup for Windows */ + +static void +os_cleanup(void) +{ + WSACleanup(); +} + +static int +os_init(void) +{ + WSADATA WSAData; + int ret; + char buf[100]; + ret = WSAStartup(0x0101, &WSAData); + switch (ret) { + case 0: /* No error */ + Py_AtExit(os_cleanup); + return 1; /* Success */ + case WSASYSNOTREADY: + PyErr_SetString(PyExc_ImportError, + "WSAStartup failed: network not ready"); + break; + case WSAVERNOTSUPPORTED: + case WSAEINVAL: + PyErr_SetString( + PyExc_ImportError, + "WSAStartup failed: requested version not supported"); + break; + default: + PyOS_snprintf(buf, sizeof(buf), + "WSAStartup failed: error code %d", ret); + PyErr_SetString(PyExc_ImportError, buf); + break; + } + return 0; /* Failure */ +} + +#endif /* MS_WINDOWS */ + + +#ifdef PYOS_OS2 +#define OS_INIT_DEFINED + +/* Additional initialization for OS/2 */ + +static int +os_init(void) +{ +#ifndef PYCC_GCC + char reason[64]; + int rc = sock_init(); + + if (rc == 0) { + return 1; /* Success */ + } + + PyOS_snprintf(reason, sizeof(reason), + "OS/2 TCP/IP Error# %d", sock_errno()); + PyErr_SetString(PyExc_ImportError, reason); + + return 0; /* Failure */ +#else + /* No need to initialise sockets with GCC/EMX */ + return 1; /* Success */ +#endif +} + +#endif /* PYOS_OS2 */ + + +#ifndef OS_INIT_DEFINED +static int +os_init(void) +{ + return 1; /* Success */ +} +#endif + + +/* C API table - always add new things to the end for binary + compatibility. */ +static +PySocketModule_APIObject PySocketModuleAPI = +{ + &sock_type, +}; + + +/* Initialize the _socket module. + + This module is actually called "_socket", and there's a wrapper + "socket.py" which implements some additional functionality. On some + platforms (e.g. Windows and OS/2), socket.py also implements a + wrapper for the socket type that provides missing functionality such + as makefile(), dup() and fromfd(). The import of "_socket" may fail + with an ImportError exception if os-specific initialization fails. + On Windows, this does WINSOCK initialization. When WINSOCK is + initialized succesfully, a call to WSACleanup() is scheduled to be + made at exit time. +*/ + +PyDoc_STRVAR(socket_doc, +"Implementation module for socket operations.\n\ +\n\ +See the socket module for documentation."); + +PyMODINIT_FUNC +init_socket(void) +{ + PyObject *m, *has_ipv6; + + if (!os_init()) + return; + + sock_type.ob_type = &PyType_Type; + m = Py_InitModule3(PySocket_MODULE_NAME, + socket_methods, + socket_doc); + + socket_error = PyErr_NewException("socket.error", NULL, NULL); + if (socket_error == NULL) + return; + Py_INCREF(socket_error); + PyModule_AddObject(m, "error", socket_error); + socket_herror = PyErr_NewException("socket.herror", + socket_error, NULL); + if (socket_herror == NULL) + return; + Py_INCREF(socket_herror); + PyModule_AddObject(m, "herror", socket_herror); + socket_gaierror = PyErr_NewException("socket.gaierror", socket_error, + NULL); + if (socket_gaierror == NULL) + return; + Py_INCREF(socket_gaierror); + PyModule_AddObject(m, "gaierror", socket_gaierror); + socket_timeout = PyErr_NewException("socket.timeout", + socket_error, NULL); + if (socket_timeout == NULL) + return; + Py_INCREF(socket_timeout); + PyModule_AddObject(m, "timeout", socket_timeout); + Py_INCREF((PyObject *)&sock_type); + if (PyModule_AddObject(m, "SocketType", + (PyObject *)&sock_type) != 0) + return; + Py_INCREF((PyObject *)&sock_type); + if (PyModule_AddObject(m, "socket", + (PyObject *)&sock_type) != 0) + return; + +#ifdef ENABLE_IPV6 + has_ipv6 = Py_True; +#else + has_ipv6 = Py_False; +#endif + Py_INCREF(has_ipv6); + PyModule_AddObject(m, "has_ipv6", has_ipv6); + + /* Export C API */ + if (PyModule_AddObject(m, PySocket_CAPI_NAME, + PyCObject_FromVoidPtr((void *)&PySocketModuleAPI, NULL) + ) != 0) + return; + + /* Address families (we only support AF_INET and AF_UNIX) */ +#ifdef AF_UNSPEC + PyModule_AddIntConstant(m, "AF_UNSPEC", AF_UNSPEC); +#endif + PyModule_AddIntConstant(m, "AF_INET", AF_INET); +#ifdef AF_INET6 + PyModule_AddIntConstant(m, "AF_INET6", AF_INET6); +#endif /* AF_INET6 */ +#if defined(AF_UNIX) && !defined(PYOS_OS2) + PyModule_AddIntConstant(m, "AF_UNIX", AF_UNIX); +#endif /* AF_UNIX */ +#ifdef AF_AX25 + /* Amateur Radio AX.25 */ + PyModule_AddIntConstant(m, "AF_AX25", AF_AX25); +#endif +#ifdef AF_IPX + PyModule_AddIntConstant(m, "AF_IPX", AF_IPX); /* Novell IPX */ +#endif +#ifdef AF_APPLETALK + /* Appletalk DDP */ + PyModule_AddIntConstant(m, "AF_APPLETALK", AF_APPLETALK); +#endif +#ifdef AF_NETROM + /* Amateur radio NetROM */ + PyModule_AddIntConstant(m, "AF_NETROM", AF_NETROM); +#endif +#ifdef AF_BRIDGE + /* Multiprotocol bridge */ + PyModule_AddIntConstant(m, "AF_BRIDGE", AF_BRIDGE); +#endif +#ifdef AF_AAL5 + /* Reserved for Werner's ATM */ + PyModule_AddIntConstant(m, "AF_AAL5", AF_AAL5); +#endif +#ifdef AF_X25 + /* Reserved for X.25 project */ + PyModule_AddIntConstant(m, "AF_X25", AF_X25); +#endif +#ifdef AF_INET6 + PyModule_AddIntConstant(m, "AF_INET6", AF_INET6); /* IP version 6 */ +#endif +#ifdef AF_ROSE + /* Amateur Radio X.25 PLP */ + PyModule_AddIntConstant(m, "AF_ROSE", AF_ROSE); +#endif +#ifdef HAVE_NETPACKET_PACKET_H + PyModule_AddIntConstant(m, "AF_PACKET", AF_PACKET); + PyModule_AddIntConstant(m, "PF_PACKET", PF_PACKET); + PyModule_AddIntConstant(m, "PACKET_HOST", PACKET_HOST); + PyModule_AddIntConstant(m, "PACKET_BROADCAST", PACKET_BROADCAST); + PyModule_AddIntConstant(m, "PACKET_MULTICAST", PACKET_MULTICAST); + PyModule_AddIntConstant(m, "PACKET_OTHERHOST", PACKET_OTHERHOST); + PyModule_AddIntConstant(m, "PACKET_OUTGOING", PACKET_OUTGOING); + PyModule_AddIntConstant(m, "PACKET_LOOPBACK", PACKET_LOOPBACK); + PyModule_AddIntConstant(m, "PACKET_FASTROUTE", PACKET_FASTROUTE); +#endif + + /* Socket types */ + PyModule_AddIntConstant(m, "SOCK_STREAM", SOCK_STREAM); + PyModule_AddIntConstant(m, "SOCK_DGRAM", SOCK_DGRAM); +#ifndef __BEOS__ +/* We have incomplete socket support. */ + PyModule_AddIntConstant(m, "SOCK_RAW", SOCK_RAW); + PyModule_AddIntConstant(m, "SOCK_SEQPACKET", SOCK_SEQPACKET); +#if defined(SOCK_RDM) + PyModule_AddIntConstant(m, "SOCK_RDM", SOCK_RDM); +#endif +#endif + +#ifdef SO_DEBUG + PyModule_AddIntConstant(m, "SO_DEBUG", SO_DEBUG); +#endif +#ifdef SO_ACCEPTCONN + PyModule_AddIntConstant(m, "SO_ACCEPTCONN", SO_ACCEPTCONN); +#endif +#ifdef SO_REUSEADDR + PyModule_AddIntConstant(m, "SO_REUSEADDR", SO_REUSEADDR); +#endif +#ifdef SO_KEEPALIVE + PyModule_AddIntConstant(m, "SO_KEEPALIVE", SO_KEEPALIVE); +#endif +#ifdef SO_DONTROUTE + PyModule_AddIntConstant(m, "SO_DONTROUTE", SO_DONTROUTE); +#endif +#ifdef SO_BROADCAST + PyModule_AddIntConstant(m, "SO_BROADCAST", SO_BROADCAST); +#endif +#ifdef SO_USELOOPBACK + PyModule_AddIntConstant(m, "SO_USELOOPBACK", SO_USELOOPBACK); +#endif +#ifdef SO_LINGER + PyModule_AddIntConstant(m, "SO_LINGER", SO_LINGER); +#endif +#ifdef SO_OOBINLINE + PyModule_AddIntConstant(m, "SO_OOBINLINE", SO_OOBINLINE); +#endif +#ifdef SO_REUSEPORT + PyModule_AddIntConstant(m, "SO_REUSEPORT", SO_REUSEPORT); +#endif +#ifdef SO_SNDBUF + PyModule_AddIntConstant(m, "SO_SNDBUF", SO_SNDBUF); +#endif +#ifdef SO_RCVBUF + PyModule_AddIntConstant(m, "SO_RCVBUF", SO_RCVBUF); +#endif +#ifdef SO_SNDLOWAT + PyModule_AddIntConstant(m, "SO_SNDLOWAT", SO_SNDLOWAT); +#endif +#ifdef SO_RCVLOWAT + PyModule_AddIntConstant(m, "SO_RCVLOWAT", SO_RCVLOWAT); +#endif +#ifdef SO_SNDTIMEO + PyModule_AddIntConstant(m, "SO_SNDTIMEO", SO_SNDTIMEO); +#endif +#ifdef SO_RCVTIMEO + PyModule_AddIntConstant(m, "SO_RCVTIMEO", SO_RCVTIMEO); +#endif +#ifdef SO_ERROR + PyModule_AddIntConstant(m, "SO_ERROR", SO_ERROR); +#endif +#ifdef SO_TYPE + PyModule_AddIntConstant(m, "SO_TYPE", SO_TYPE); +#endif + + /* Maximum number of connections for "listen" */ +#ifdef SOMAXCONN + PyModule_AddIntConstant(m, "SOMAXCONN", SOMAXCONN); +#else + PyModule_AddIntConstant(m, "SOMAXCONN", 5); /* Common value */ +#endif + + /* Flags for send, recv */ +#ifdef MSG_OOB + PyModule_AddIntConstant(m, "MSG_OOB", MSG_OOB); +#endif +#ifdef MSG_PEEK + PyModule_AddIntConstant(m, "MSG_PEEK", MSG_PEEK); +#endif +#ifdef MSG_DONTROUTE + PyModule_AddIntConstant(m, "MSG_DONTROUTE", MSG_DONTROUTE); +#endif +#ifdef MSG_DONTWAIT + PyModule_AddIntConstant(m, "MSG_DONTWAIT", MSG_DONTWAIT); +#endif +#ifdef MSG_EOR + PyModule_AddIntConstant(m, "MSG_EOR", MSG_EOR); +#endif +#ifdef MSG_TRUNC + PyModule_AddIntConstant(m, "MSG_TRUNC", MSG_TRUNC); +#endif +#ifdef MSG_CTRUNC + PyModule_AddIntConstant(m, "MSG_CTRUNC", MSG_CTRUNC); +#endif +#ifdef MSG_WAITALL + PyModule_AddIntConstant(m, "MSG_WAITALL", MSG_WAITALL); +#endif +#ifdef MSG_BTAG + PyModule_AddIntConstant(m, "MSG_BTAG", MSG_BTAG); +#endif +#ifdef MSG_ETAG + PyModule_AddIntConstant(m, "MSG_ETAG", MSG_ETAG); +#endif + + /* Protocol level and numbers, usable for [gs]etsockopt */ +#ifdef SOL_SOCKET + PyModule_AddIntConstant(m, "SOL_SOCKET", SOL_SOCKET); +#endif +#ifdef SOL_IP + PyModule_AddIntConstant(m, "SOL_IP", SOL_IP); +#else + PyModule_AddIntConstant(m, "SOL_IP", 0); +#endif +#ifdef SOL_IPX + PyModule_AddIntConstant(m, "SOL_IPX", SOL_IPX); +#endif +#ifdef SOL_AX25 + PyModule_AddIntConstant(m, "SOL_AX25", SOL_AX25); +#endif +#ifdef SOL_ATALK + PyModule_AddIntConstant(m, "SOL_ATALK", SOL_ATALK); +#endif +#ifdef SOL_NETROM + PyModule_AddIntConstant(m, "SOL_NETROM", SOL_NETROM); +#endif +#ifdef SOL_ROSE + PyModule_AddIntConstant(m, "SOL_ROSE", SOL_ROSE); +#endif +#ifdef SOL_TCP + PyModule_AddIntConstant(m, "SOL_TCP", SOL_TCP); +#else + PyModule_AddIntConstant(m, "SOL_TCP", 6); +#endif +#ifdef SOL_UDP + PyModule_AddIntConstant(m, "SOL_UDP", SOL_UDP); +#else + PyModule_AddIntConstant(m, "SOL_UDP", 17); +#endif +#ifdef IPPROTO_IP + PyModule_AddIntConstant(m, "IPPROTO_IP", IPPROTO_IP); +#else + PyModule_AddIntConstant(m, "IPPROTO_IP", 0); +#endif +#ifdef IPPROTO_HOPOPTS + PyModule_AddIntConstant(m, "IPPROTO_HOPOPTS", IPPROTO_HOPOPTS); +#endif +#ifdef IPPROTO_ICMP + PyModule_AddIntConstant(m, "IPPROTO_ICMP", IPPROTO_ICMP); +#else + PyModule_AddIntConstant(m, "IPPROTO_ICMP", 1); +#endif +#ifdef IPPROTO_IGMP + PyModule_AddIntConstant(m, "IPPROTO_IGMP", IPPROTO_IGMP); +#endif +#ifdef IPPROTO_GGP + PyModule_AddIntConstant(m, "IPPROTO_GGP", IPPROTO_GGP); +#endif +#ifdef IPPROTO_IPV4 + PyModule_AddIntConstant(m, "IPPROTO_IPV4", IPPROTO_IPV4); +#endif +#ifdef IPPROTO_IPV6 + PyModule_AddIntConstant(m, "IPPROTO_IPV6", IPPROTO_IPV6); +#endif +#ifdef IPPROTO_IPIP + PyModule_AddIntConstant(m, "IPPROTO_IPIP", IPPROTO_IPIP); +#endif +#ifdef IPPROTO_TCP + PyModule_AddIntConstant(m, "IPPROTO_TCP", IPPROTO_TCP); +#else + PyModule_AddIntConstant(m, "IPPROTO_TCP", 6); +#endif +#ifdef IPPROTO_EGP + PyModule_AddIntConstant(m, "IPPROTO_EGP", IPPROTO_EGP); +#endif +#ifdef IPPROTO_PUP + PyModule_AddIntConstant(m, "IPPROTO_PUP", IPPROTO_PUP); +#endif +#ifdef IPPROTO_UDP + PyModule_AddIntConstant(m, "IPPROTO_UDP", IPPROTO_UDP); +#else + PyModule_AddIntConstant(m, "IPPROTO_UDP", 17); +#endif +#ifdef IPPROTO_IDP + PyModule_AddIntConstant(m, "IPPROTO_IDP", IPPROTO_IDP); +#endif +#ifdef IPPROTO_HELLO + PyModule_AddIntConstant(m, "IPPROTO_HELLO", IPPROTO_HELLO); +#endif +#ifdef IPPROTO_ND + PyModule_AddIntConstant(m, "IPPROTO_ND", IPPROTO_ND); +#endif +#ifdef IPPROTO_TP + PyModule_AddIntConstant(m, "IPPROTO_TP", IPPROTO_TP); +#endif +#ifdef IPPROTO_IPV6 + PyModule_AddIntConstant(m, "IPPROTO_IPV6", IPPROTO_IPV6); +#endif +#ifdef IPPROTO_ROUTING + PyModule_AddIntConstant(m, "IPPROTO_ROUTING", IPPROTO_ROUTING); +#endif +#ifdef IPPROTO_FRAGMENT + PyModule_AddIntConstant(m, "IPPROTO_FRAGMENT", IPPROTO_FRAGMENT); +#endif +#ifdef IPPROTO_RSVP + PyModule_AddIntConstant(m, "IPPROTO_RSVP", IPPROTO_RSVP); +#endif +#ifdef IPPROTO_GRE + PyModule_AddIntConstant(m, "IPPROTO_GRE", IPPROTO_GRE); +#endif +#ifdef IPPROTO_ESP + PyModule_AddIntConstant(m, "IPPROTO_ESP", IPPROTO_ESP); +#endif +#ifdef IPPROTO_AH + PyModule_AddIntConstant(m, "IPPROTO_AH", IPPROTO_AH); +#endif +#ifdef IPPROTO_MOBILE + PyModule_AddIntConstant(m, "IPPROTO_MOBILE", IPPROTO_MOBILE); +#endif +#ifdef IPPROTO_ICMPV6 + PyModule_AddIntConstant(m, "IPPROTO_ICMPV6", IPPROTO_ICMPV6); +#endif +#ifdef IPPROTO_NONE + PyModule_AddIntConstant(m, "IPPROTO_NONE", IPPROTO_NONE); +#endif +#ifdef IPPROTO_DSTOPTS + PyModule_AddIntConstant(m, "IPPROTO_DSTOPTS", IPPROTO_DSTOPTS); +#endif +#ifdef IPPROTO_XTP + PyModule_AddIntConstant(m, "IPPROTO_XTP", IPPROTO_XTP); +#endif +#ifdef IPPROTO_EON + PyModule_AddIntConstant(m, "IPPROTO_EON", IPPROTO_EON); +#endif +#ifdef IPPROTO_PIM + PyModule_AddIntConstant(m, "IPPROTO_PIM", IPPROTO_PIM); +#endif +#ifdef IPPROTO_IPCOMP + PyModule_AddIntConstant(m, "IPPROTO_IPCOMP", IPPROTO_IPCOMP); +#endif +#ifdef IPPROTO_VRRP + PyModule_AddIntConstant(m, "IPPROTO_VRRP", IPPROTO_VRRP); +#endif +#ifdef IPPROTO_BIP + PyModule_AddIntConstant(m, "IPPROTO_BIP", IPPROTO_BIP); +#endif +/**/ +#ifdef IPPROTO_RAW + PyModule_AddIntConstant(m, "IPPROTO_RAW", IPPROTO_RAW); +#else + PyModule_AddIntConstant(m, "IPPROTO_RAW", 255); +#endif +#ifdef IPPROTO_MAX + PyModule_AddIntConstant(m, "IPPROTO_MAX", IPPROTO_MAX); +#endif + + /* Some port configuration */ +#ifdef IPPORT_RESERVED + PyModule_AddIntConstant(m, "IPPORT_RESERVED", IPPORT_RESERVED); +#else + PyModule_AddIntConstant(m, "IPPORT_RESERVED", 1024); +#endif +#ifdef IPPORT_USERRESERVED + PyModule_AddIntConstant(m, "IPPORT_USERRESERVED", IPPORT_USERRESERVED); +#else + PyModule_AddIntConstant(m, "IPPORT_USERRESERVED", 5000); +#endif + + /* Some reserved IP v.4 addresses */ +#ifdef INADDR_ANY + PyModule_AddIntConstant(m, "INADDR_ANY", INADDR_ANY); +#else + PyModule_AddIntConstant(m, "INADDR_ANY", 0x00000000); +#endif +#ifdef INADDR_BROADCAST + PyModule_AddIntConstant(m, "INADDR_BROADCAST", INADDR_BROADCAST); +#else + PyModule_AddIntConstant(m, "INADDR_BROADCAST", 0xffffffff); +#endif +#ifdef INADDR_LOOPBACK + PyModule_AddIntConstant(m, "INADDR_LOOPBACK", INADDR_LOOPBACK); +#else + PyModule_AddIntConstant(m, "INADDR_LOOPBACK", 0x7F000001); +#endif +#ifdef INADDR_UNSPEC_GROUP + PyModule_AddIntConstant(m, "INADDR_UNSPEC_GROUP", INADDR_UNSPEC_GROUP); +#else + PyModule_AddIntConstant(m, "INADDR_UNSPEC_GROUP", 0xe0000000); +#endif +#ifdef INADDR_ALLHOSTS_GROUP + PyModule_AddIntConstant(m, "INADDR_ALLHOSTS_GROUP", + INADDR_ALLHOSTS_GROUP); +#else + PyModule_AddIntConstant(m, "INADDR_ALLHOSTS_GROUP", 0xe0000001); +#endif +#ifdef INADDR_MAX_LOCAL_GROUP + PyModule_AddIntConstant(m, "INADDR_MAX_LOCAL_GROUP", + INADDR_MAX_LOCAL_GROUP); +#else + PyModule_AddIntConstant(m, "INADDR_MAX_LOCAL_GROUP", 0xe00000ff); +#endif +#ifdef INADDR_NONE + PyModule_AddIntConstant(m, "INADDR_NONE", INADDR_NONE); +#else + PyModule_AddIntConstant(m, "INADDR_NONE", 0xffffffff); +#endif + + /* IPv4 [gs]etsockopt options */ +#ifdef IP_OPTIONS + PyModule_AddIntConstant(m, "IP_OPTIONS", IP_OPTIONS); +#endif +#ifdef IP_HDRINCL + PyModule_AddIntConstant(m, "IP_HDRINCL", IP_HDRINCL); +#endif +#ifdef IP_TOS + PyModule_AddIntConstant(m, "IP_TOS", IP_TOS); +#endif +#ifdef IP_TTL + PyModule_AddIntConstant(m, "IP_TTL", IP_TTL); +#endif +#ifdef IP_RECVOPTS + PyModule_AddIntConstant(m, "IP_RECVOPTS", IP_RECVOPTS); +#endif +#ifdef IP_RECVRETOPTS + PyModule_AddIntConstant(m, "IP_RECVRETOPTS", IP_RECVRETOPTS); +#endif +#ifdef IP_RECVDSTADDR + PyModule_AddIntConstant(m, "IP_RECVDSTADDR", IP_RECVDSTADDR); +#endif +#ifdef IP_RETOPTS + PyModule_AddIntConstant(m, "IP_RETOPTS", IP_RETOPTS); +#endif +#ifdef IP_MULTICAST_IF + PyModule_AddIntConstant(m, "IP_MULTICAST_IF", IP_MULTICAST_IF); +#endif +#ifdef IP_MULTICAST_TTL + PyModule_AddIntConstant(m, "IP_MULTICAST_TTL", IP_MULTICAST_TTL); +#endif +#ifdef IP_MULTICAST_LOOP + PyModule_AddIntConstant(m, "IP_MULTICAST_LOOP", IP_MULTICAST_LOOP); +#endif +#ifdef IP_ADD_MEMBERSHIP + PyModule_AddIntConstant(m, "IP_ADD_MEMBERSHIP", IP_ADD_MEMBERSHIP); +#endif +#ifdef IP_DROP_MEMBERSHIP + PyModule_AddIntConstant(m, "IP_DROP_MEMBERSHIP", IP_DROP_MEMBERSHIP); +#endif +#ifdef IP_DEFAULT_MULTICAST_TTL + PyModule_AddIntConstant(m, "IP_DEFAULT_MULTICAST_TTL", + IP_DEFAULT_MULTICAST_TTL); +#endif +#ifdef IP_DEFAULT_MULTICAST_LOOP + PyModule_AddIntConstant(m, "IP_DEFAULT_MULTICAST_LOOP", + IP_DEFAULT_MULTICAST_LOOP); +#endif +#ifdef IP_MAX_MEMBERSHIPS + PyModule_AddIntConstant(m, "IP_MAX_MEMBERSHIPS", IP_MAX_MEMBERSHIPS); +#endif + + /* IPv6 [gs]etsockopt options, defined in RFC2553 */ +#ifdef IPV6_JOIN_GROUP + PyModule_AddIntConstant(m, "IPV6_JOIN_GROUP", IPV6_JOIN_GROUP); +#endif +#ifdef IPV6_LEAVE_GROUP + PyModule_AddIntConstant(m, "IPV6_LEAVE_GROUP", IPV6_LEAVE_GROUP); +#endif +#ifdef IPV6_MULTICAST_HOPS + PyModule_AddIntConstant(m, "IPV6_MULTICAST_HOPS", IPV6_MULTICAST_HOPS); +#endif +#ifdef IPV6_MULTICAST_IF + PyModule_AddIntConstant(m, "IPV6_MULTICAST_IF", IPV6_MULTICAST_IF); +#endif +#ifdef IPV6_MULTICAST_LOOP + PyModule_AddIntConstant(m, "IPV6_MULTICAST_LOOP", IPV6_MULTICAST_LOOP); +#endif +#ifdef IPV6_UNICAST_HOPS + PyModule_AddIntConstant(m, "IPV6_UNICAST_HOPS", IPV6_UNICAST_HOPS); +#endif + + /* TCP options */ +#ifdef TCP_NODELAY + PyModule_AddIntConstant(m, "TCP_NODELAY", TCP_NODELAY); +#endif +#ifdef TCP_MAXSEG + PyModule_AddIntConstant(m, "TCP_MAXSEG", TCP_MAXSEG); +#endif +#ifdef TCP_CORK + PyModule_AddIntConstant(m, "TCP_CORK", TCP_CORK); +#endif +#ifdef TCP_KEEPIDLE + PyModule_AddIntConstant(m, "TCP_KEEPIDLE", TCP_KEEPIDLE); +#endif +#ifdef TCP_KEEPINTVL + PyModule_AddIntConstant(m, "TCP_KEEPINTVL", TCP_KEEPINTVL); +#endif +#ifdef TCP_KEEPCNT + PyModule_AddIntConstant(m, "TCP_KEEPCNT", TCP_KEEPCNT); +#endif +#ifdef TCP_SYNCNT + PyModule_AddIntConstant(m, "TCP_SYNCNT", TCP_SYNCNT); +#endif +#ifdef TCP_LINGER2 + PyModule_AddIntConstant(m, "TCP_LINGER2", TCP_LINGER2); +#endif +#ifdef TCP_DEFER_ACCEPT + PyModule_AddIntConstant(m, "TCP_DEFER_ACCEPT", TCP_DEFER_ACCEPT); +#endif +#ifdef TCP_WINDOW_CLAMP + PyModule_AddIntConstant(m, "TCP_WINDOW_CLAMP", TCP_WINDOW_CLAMP); +#endif +#ifdef TCP_INFO + PyModule_AddIntConstant(m, "TCP_INFO", TCP_INFO); +#endif +#ifdef TCP_QUICKACK + PyModule_AddIntConstant(m, "TCP_QUICKACK", TCP_QUICKACK); +#endif + + + /* IPX options */ +#ifdef IPX_TYPE + PyModule_AddIntConstant(m, "IPX_TYPE", IPX_TYPE); +#endif + + /* get{addr,name}info parameters */ +#ifdef EAI_ADDRFAMILY + PyModule_AddIntConstant(m, "EAI_ADDRFAMILY", EAI_ADDRFAMILY); +#endif +#ifdef EAI_AGAIN + PyModule_AddIntConstant(m, "EAI_AGAIN", EAI_AGAIN); +#endif +#ifdef EAI_BADFLAGS + PyModule_AddIntConstant(m, "EAI_BADFLAGS", EAI_BADFLAGS); +#endif +#ifdef EAI_FAIL + PyModule_AddIntConstant(m, "EAI_FAIL", EAI_FAIL); +#endif +#ifdef EAI_FAMILY + PyModule_AddIntConstant(m, "EAI_FAMILY", EAI_FAMILY); +#endif +#ifdef EAI_MEMORY + PyModule_AddIntConstant(m, "EAI_MEMORY", EAI_MEMORY); +#endif +#ifdef EAI_NODATA + PyModule_AddIntConstant(m, "EAI_NODATA", EAI_NODATA); +#endif +#ifdef EAI_NONAME + PyModule_AddIntConstant(m, "EAI_NONAME", EAI_NONAME); +#endif +#ifdef EAI_SERVICE + PyModule_AddIntConstant(m, "EAI_SERVICE", EAI_SERVICE); +#endif +#ifdef EAI_SOCKTYPE + PyModule_AddIntConstant(m, "EAI_SOCKTYPE", EAI_SOCKTYPE); +#endif +#ifdef EAI_SYSTEM + PyModule_AddIntConstant(m, "EAI_SYSTEM", EAI_SYSTEM); +#endif +#ifdef EAI_BADHINTS + PyModule_AddIntConstant(m, "EAI_BADHINTS", EAI_BADHINTS); +#endif +#ifdef EAI_PROTOCOL + PyModule_AddIntConstant(m, "EAI_PROTOCOL", EAI_PROTOCOL); +#endif +#ifdef EAI_MAX + PyModule_AddIntConstant(m, "EAI_MAX", EAI_MAX); +#endif +#ifdef AI_PASSIVE + PyModule_AddIntConstant(m, "AI_PASSIVE", AI_PASSIVE); +#endif +#ifdef AI_CANONNAME + PyModule_AddIntConstant(m, "AI_CANONNAME", AI_CANONNAME); +#endif +#ifdef AI_NUMERICHOST + PyModule_AddIntConstant(m, "AI_NUMERICHOST", AI_NUMERICHOST); +#endif +#ifdef AI_MASK + PyModule_AddIntConstant(m, "AI_MASK", AI_MASK); +#endif +#ifdef AI_ALL + PyModule_AddIntConstant(m, "AI_ALL", AI_ALL); +#endif +#ifdef AI_V4MAPPED_CFG + PyModule_AddIntConstant(m, "AI_V4MAPPED_CFG", AI_V4MAPPED_CFG); +#endif +#ifdef AI_ADDRCONFIG + PyModule_AddIntConstant(m, "AI_ADDRCONFIG", AI_ADDRCONFIG); +#endif +#ifdef AI_V4MAPPED + PyModule_AddIntConstant(m, "AI_V4MAPPED", AI_V4MAPPED); +#endif +#ifdef AI_DEFAULT + PyModule_AddIntConstant(m, "AI_DEFAULT", AI_DEFAULT); +#endif +#ifdef NI_MAXHOST + PyModule_AddIntConstant(m, "NI_MAXHOST", NI_MAXHOST); +#endif +#ifdef NI_MAXSERV + PyModule_AddIntConstant(m, "NI_MAXSERV", NI_MAXSERV); +#endif +#ifdef NI_NOFQDN + PyModule_AddIntConstant(m, "NI_NOFQDN", NI_NOFQDN); +#endif +#ifdef NI_NUMERICHOST + PyModule_AddIntConstant(m, "NI_NUMERICHOST", NI_NUMERICHOST); +#endif +#ifdef NI_NAMEREQD + PyModule_AddIntConstant(m, "NI_NAMEREQD", NI_NAMEREQD); +#endif +#ifdef NI_NUMERICSERV + PyModule_AddIntConstant(m, "NI_NUMERICSERV", NI_NUMERICSERV); +#endif +#ifdef NI_DGRAM + PyModule_AddIntConstant(m, "NI_DGRAM", NI_DGRAM); +#endif + + /* Initialize gethostbyname lock */ +#if defined(USE_GETHOSTBYNAME_LOCK) || defined(USE_GETADDRINFO_LOCK) + netdb_lock = PyThread_allocate_lock(); +#endif +} + + +#ifndef HAVE_INET_PTON + +/* Simplistic emulation code for inet_pton that only works for IPv4 */ +/* These are not exposed because they do not set errno properly */ + +int +inet_pton(int af, const char *src, void *dst) +{ + if (af == AF_INET) { + long packed_addr; + packed_addr = inet_addr(src); + if (packed_addr == INADDR_NONE) + return 0; + memcpy(dst, &packed_addr, 4); + return 1; + } + /* Should set errno to EAFNOSUPPORT */ + return -1; +} + +const char * +inet_ntop(int af, const void *src, char *dst, socklen_t size) +{ + if (af == AF_INET) { + struct in_addr packed_addr; + if (size < 16) + /* Should set errno to ENOSPC. */ + return NULL; + memcpy(&packed_addr, src, sizeof(packed_addr)); + return strncpy(dst, inet_ntoa(packed_addr), size); + } + /* Should set errno to EAFNOSUPPORT */ + return NULL; +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/socketmodule.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/socketmodule.h new file mode 100644 index 00000000..cedc8da7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/socketmodule.h @@ -0,0 +1,221 @@ +/* Socket module header file */ + +/* Includes needed for the sockaddr_* symbols below */ +#ifndef MS_WINDOWS +#ifdef __VMS +# include +# else +# include +# endif +# include +# if !(defined(__BEOS__) || defined(__CYGWIN__) || (defined(PYOS_OS2) && defined(PYCC_VACPP))) +# include +# endif + +#else /* MS_WINDOWS */ +#if _MSC_VER >= 1300 +# include +# include +# define HAVE_ADDRINFO +# define HAVE_SOCKADDR_STORAGE +# define HAVE_GETADDRINFO +# define HAVE_GETNAMEINFO +# define ENABLE_IPV6 +#else +# include +#endif +#endif + +#ifdef HAVE_SYS_UN_H +# include +#else +# undef AF_UNIX +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +# include +# include +# include +#endif + +#ifndef Py__SOCKET_H +#define Py__SOCKET_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Python module and C API name */ +#define PySocket_MODULE_NAME "_socket" +#define PySocket_CAPI_NAME "CAPI" + +/* Abstract the socket file descriptor type */ +#ifdef MS_WINDOWS +typedef SOCKET SOCKET_T; +# ifdef MS_WIN64 +# define SIZEOF_SOCKET_T 8 +# else +# define SIZEOF_SOCKET_T 4 +# endif +#else +typedef int SOCKET_T; +# define SIZEOF_SOCKET_T SIZEOF_INT +#endif + +/* The object holding a socket. It holds some extra information, + like the address family, which is used to decode socket address + arguments properly. */ + +typedef struct { + PyObject_HEAD + SOCKET_T sock_fd; /* Socket file descriptor */ + int sock_family; /* Address family, e.g., AF_INET */ + int sock_type; /* Socket type, e.g., SOCK_STREAM */ + int sock_proto; /* Protocol type, usually 0 */ + union sock_addr { + struct sockaddr_in in; +#ifdef AF_UNIX + struct sockaddr_un un; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 in6; + struct sockaddr_storage storage; +#endif +#ifdef HAVE_NETPACKET_PACKET_H + struct sockaddr_ll ll; +#endif + } sock_addr; + PyObject *(*errorhandler)(void); /* Error handler; checks + errno, returns NULL and + sets a Python exception */ + double sock_timeout; /* Operation timeout in seconds; + 0.0 means non-blocking */ +} PySocketSockObject; + +/* --- C API ----------------------------------------------------*/ + +/* Short explanation of what this C API export mechanism does + and how it works: + + The _ssl module needs access to the type object defined in + the _socket module. Since cross-DLL linking introduces a lot of + problems on many platforms, the "trick" is to wrap the + C API of a module in a struct which then gets exported to + other modules via a PyCObject. + + The code in socketmodule.c defines this struct (which currently + only contains the type object reference, but could very + well also include other C APIs needed by other modules) + and exports it as PyCObject via the module dictionary + under the name "CAPI". + + Other modules can now include the socketmodule.h file + which defines the needed C APIs to import and set up + a static copy of this struct in the importing module. + + After initialization, the importing module can then + access the C APIs from the _socket module by simply + referring to the static struct, e.g. + + Load _socket module and its C API; this sets up the global + PySocketModule: + + if (PySocketModule_ImportModuleAndAPI()) + return; + + + Now use the C API as if it were defined in the using + module: + + if (!PyArg_ParseTuple(args, "O!|zz:ssl", + + PySocketModule.Sock_Type, + + (PyObject*)&Sock, + &key_file, &cert_file)) + return NULL; + + Support could easily be extended to export more C APIs/symbols + this way. Currently, only the type object is exported, + other candidates would be socket constructors and socket + access functions. + +*/ + +/* C API for usage by other Python modules */ +typedef struct { + PyTypeObject *Sock_Type; +} PySocketModule_APIObject; + +/* XXX The net effect of the following appears to be to define a function + XXX named PySocketModule_APIObject in _ssl.c. It's unclear why it isn't + XXX defined there directly. + + >>> It's defined here because other modules might also want to use + >>> the C API. + +*/ +#ifndef PySocket_BUILDING_SOCKET + +/* --- C API ----------------------------------------------------*/ + +/* Interfacestructure to C API for other modules. + Call PySocketModule_ImportModuleAndAPI() to initialize this + structure. After that usage is simple: + + if (!PyArg_ParseTuple(args, "O!|zz:ssl", + &PySocketModule.Sock_Type, (PyObject*)&Sock, + &key_file, &cert_file)) + return NULL; + ... +*/ + +static +PySocketModule_APIObject PySocketModule; + +/* You *must* call this before using any of the functions in + PySocketModule and check its outcome; otherwise all accesses will + result in a segfault. Returns 0 on success. */ + +#ifndef DPRINTF +# define DPRINTF if (0) printf +#endif + +static +int PySocketModule_ImportModuleAndAPI(void) +{ + PyObject *mod = 0, *v = 0; + char *apimodule = PySocket_MODULE_NAME; + char *apiname = PySocket_CAPI_NAME; + void *api; + + DPRINTF("Importing the %s C API...\n", apimodule); + mod = PyImport_ImportModule(apimodule); + if (mod == NULL) + goto onError; + DPRINTF(" %s package found\n", apimodule); + v = PyObject_GetAttrString(mod, apiname); + if (v == NULL) + goto onError; + Py_DECREF(mod); + DPRINTF(" API object %s found\n", apiname); + api = PyCObject_AsVoidPtr(v); + if (api == NULL) + goto onError; + Py_DECREF(v); + memcpy(&PySocketModule, api, sizeof(PySocketModule)); + DPRINTF(" API object loaded and initialized.\n"); + return 0; + + onError: + DPRINTF(" not found.\n"); + Py_XDECREF(mod); + Py_XDECREF(v); + return -1; +} + +#endif /* !PySocket_BUILDING_SOCKET */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py__SOCKET_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sre.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sre.h new file mode 100644 index 00000000..61fb0f8f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sre.h @@ -0,0 +1,91 @@ +/* + * Secret Labs' Regular Expression Engine + * + * regular expression matching engine + * + * Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved. + * + * See the _sre.c file for information on usage and redistribution. + */ + +#ifndef SRE_INCLUDED +#define SRE_INCLUDED + +#include "sre_constants.h" + +/* size of a code word (must be unsigned short or larger, and + large enough to hold a Py_UNICODE character) */ +#ifdef Py_UNICODE_WIDE +#define SRE_CODE unsigned long +#else +#define SRE_CODE unsigned short +#endif + +typedef struct { + PyObject_VAR_HEAD + int groups; /* must be first! */ + PyObject* groupindex; + PyObject* indexgroup; + /* compatibility */ + PyObject* pattern; /* pattern source (or None) */ + int flags; /* flags used when compiling pattern source */ + /* pattern code */ + int codesize; + SRE_CODE code[1]; +} PatternObject; + +#define PatternObject_GetCode(o) (((PatternObject*)(o))->code) + +typedef struct { + PyObject_VAR_HEAD + PyObject* string; /* link to the target string (must be first) */ + PyObject* regs; /* cached list of matching spans */ + PatternObject* pattern; /* link to the regex (pattern) object */ + int pos, endpos; /* current target slice */ + int lastindex; /* last index marker seen by the engine (-1 if none) */ + int groups; /* number of groups (start/end marks) */ + int mark[1]; +} MatchObject; + +typedef unsigned int (*SRE_TOLOWER_HOOK)(unsigned int ch); + +/* FIXME: shouldn't be a constant, really... */ +#define SRE_MARK_SIZE 200 + +typedef struct SRE_REPEAT_T { + int count; + SRE_CODE* pattern; /* points to REPEAT operator arguments */ + struct SRE_REPEAT_T *prev; /* points to previous repeat context */ +} SRE_REPEAT; + +typedef struct { + /* string pointers */ + void* ptr; /* current position (also end of current slice) */ + void* beginning; /* start of original string */ + void* start; /* start of current slice */ + void* end; /* end of original string */ + /* attributes for the match object */ + PyObject* string; + int pos, endpos; + /* character size */ + int charsize; + /* registers */ + int lastindex; + int lastmark; + void* mark[SRE_MARK_SIZE]; + /* dynamically allocated stuff */ + void** mark_stack; + int mark_stack_size; + int mark_stack_base; + SRE_REPEAT *repeat; /* current repeat context */ + /* hooks */ + SRE_TOLOWER_HOOK lower; +} SRE_STATE; + +typedef struct { + PyObject_HEAD + PyObject* pattern; + SRE_STATE state; +} ScannerObject; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sre_constants.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sre_constants.h new file mode 100644 index 00000000..0959abe3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sre_constants.h @@ -0,0 +1,85 @@ +/* + * Secret Labs' Regular Expression Engine + * + * regular expression matching engine + * + * NOTE: This file is generated by sre_constants.py. If you need + * to change anything in here, edit sre_constants.py and run it. + * + * Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved. + * + * See the _sre.c file for information on usage and redistribution. + */ + +#define SRE_MAGIC 20030419 +#define SRE_OP_FAILURE 0 +#define SRE_OP_SUCCESS 1 +#define SRE_OP_ANY 2 +#define SRE_OP_ANY_ALL 3 +#define SRE_OP_ASSERT 4 +#define SRE_OP_ASSERT_NOT 5 +#define SRE_OP_AT 6 +#define SRE_OP_BRANCH 7 +#define SRE_OP_CALL 8 +#define SRE_OP_CATEGORY 9 +#define SRE_OP_CHARSET 10 +#define SRE_OP_BIGCHARSET 11 +#define SRE_OP_GROUPREF 12 +#define SRE_OP_GROUPREF_IGNORE 13 +#define SRE_OP_IN 14 +#define SRE_OP_IN_IGNORE 15 +#define SRE_OP_INFO 16 +#define SRE_OP_JUMP 17 +#define SRE_OP_LITERAL 18 +#define SRE_OP_LITERAL_IGNORE 19 +#define SRE_OP_MARK 20 +#define SRE_OP_MAX_UNTIL 21 +#define SRE_OP_MIN_UNTIL 22 +#define SRE_OP_NOT_LITERAL 23 +#define SRE_OP_NOT_LITERAL_IGNORE 24 +#define SRE_OP_NEGATE 25 +#define SRE_OP_RANGE 26 +#define SRE_OP_REPEAT 27 +#define SRE_OP_REPEAT_ONE 28 +#define SRE_OP_SUBPATTERN 29 +#define SRE_OP_MIN_REPEAT_ONE 30 +#define SRE_AT_BEGINNING 0 +#define SRE_AT_BEGINNING_LINE 1 +#define SRE_AT_BEGINNING_STRING 2 +#define SRE_AT_BOUNDARY 3 +#define SRE_AT_NON_BOUNDARY 4 +#define SRE_AT_END 5 +#define SRE_AT_END_LINE 6 +#define SRE_AT_END_STRING 7 +#define SRE_AT_LOC_BOUNDARY 8 +#define SRE_AT_LOC_NON_BOUNDARY 9 +#define SRE_AT_UNI_BOUNDARY 10 +#define SRE_AT_UNI_NON_BOUNDARY 11 +#define SRE_CATEGORY_DIGIT 0 +#define SRE_CATEGORY_NOT_DIGIT 1 +#define SRE_CATEGORY_SPACE 2 +#define SRE_CATEGORY_NOT_SPACE 3 +#define SRE_CATEGORY_WORD 4 +#define SRE_CATEGORY_NOT_WORD 5 +#define SRE_CATEGORY_LINEBREAK 6 +#define SRE_CATEGORY_NOT_LINEBREAK 7 +#define SRE_CATEGORY_LOC_WORD 8 +#define SRE_CATEGORY_LOC_NOT_WORD 9 +#define SRE_CATEGORY_UNI_DIGIT 10 +#define SRE_CATEGORY_UNI_NOT_DIGIT 11 +#define SRE_CATEGORY_UNI_SPACE 12 +#define SRE_CATEGORY_UNI_NOT_SPACE 13 +#define SRE_CATEGORY_UNI_WORD 14 +#define SRE_CATEGORY_UNI_NOT_WORD 15 +#define SRE_CATEGORY_UNI_LINEBREAK 16 +#define SRE_CATEGORY_UNI_NOT_LINEBREAK 17 +#define SRE_FLAG_TEMPLATE 1 +#define SRE_FLAG_IGNORECASE 2 +#define SRE_FLAG_LOCALE 4 +#define SRE_FLAG_MULTILINE 8 +#define SRE_FLAG_DOTALL 16 +#define SRE_FLAG_UNICODE 32 +#define SRE_FLAG_VERBOSE 64 +#define SRE_INFO_PREFIX 1 +#define SRE_INFO_LITERAL 2 +#define SRE_INFO_CHARSET 4 diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/stropmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/stropmodule.c new file mode 100644 index 00000000..2e74c5c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/stropmodule.c @@ -0,0 +1,1244 @@ +/* strop module */ + +#include "Python.h" +#include + +PyDoc_STRVAR(strop_module__doc__, +"Common string manipulations, optimized for speed.\n" +"\n" +"Always use \"import string\" rather than referencing\n" +"this module directly."); + +/* XXX This file assumes that the is*() functions + XXX are defined for all 8-bit characters! */ + +#define WARN if (PyErr_Warn(PyExc_DeprecationWarning, \ + "strop functions are obsolete; use string methods")) \ + return NULL + +/* The lstrip(), rstrip() and strip() functions are implemented + in do_strip(), which uses an additional parameter to indicate what + type of strip should occur. */ + +#define LEFTSTRIP 0 +#define RIGHTSTRIP 1 +#define BOTHSTRIP 2 + + +static PyObject * +split_whitespace(char *s, int len, int maxsplit) +{ + int i = 0, j, err; + int countsplit = 0; + PyObject* item; + PyObject *list = PyList_New(0); + + if (list == NULL) + return NULL; + + while (i < len) { + while (i < len && isspace(Py_CHARMASK(s[i]))) { + i = i+1; + } + j = i; + while (i < len && !isspace(Py_CHARMASK(s[i]))) { + i = i+1; + } + if (j < i) { + item = PyString_FromStringAndSize(s+j, (int)(i-j)); + if (item == NULL) + goto finally; + + err = PyList_Append(list, item); + Py_DECREF(item); + if (err < 0) + goto finally; + + countsplit++; + while (i < len && isspace(Py_CHARMASK(s[i]))) { + i = i+1; + } + if (maxsplit && (countsplit >= maxsplit) && i < len) { + item = PyString_FromStringAndSize( + s+i, (int)(len - i)); + if (item == NULL) + goto finally; + + err = PyList_Append(list, item); + Py_DECREF(item); + if (err < 0) + goto finally; + + i = len; + } + } + } + return list; + finally: + Py_DECREF(list); + return NULL; +} + + +PyDoc_STRVAR(splitfields__doc__, +"split(s [,sep [,maxsplit]]) -> list of strings\n" +"splitfields(s [,sep [,maxsplit]]) -> list of strings\n" +"\n" +"Return a list of the words in the string s, using sep as the\n" +"delimiter string. If maxsplit is nonzero, splits into at most\n" +"maxsplit words. If sep is not specified, any whitespace string\n" +"is a separator. Maxsplit defaults to 0.\n" +"\n" +"(split and splitfields are synonymous)"); + +static PyObject * +strop_splitfields(PyObject *self, PyObject *args) +{ + int len, n, i, j, err; + int splitcount, maxsplit; + char *s, *sub; + PyObject *list, *item; + + WARN; + sub = NULL; + n = 0; + splitcount = 0; + maxsplit = 0; + if (!PyArg_ParseTuple(args, "t#|z#i:split", &s, &len, &sub, &n, &maxsplit)) + return NULL; + if (sub == NULL) + return split_whitespace(s, len, maxsplit); + if (n == 0) { + PyErr_SetString(PyExc_ValueError, "empty separator"); + return NULL; + } + + list = PyList_New(0); + if (list == NULL) + return NULL; + + i = j = 0; + while (i+n <= len) { + if (s[i] == sub[0] && (n == 1 || memcmp(s+i, sub, n) == 0)) { + item = PyString_FromStringAndSize(s+j, (int)(i-j)); + if (item == NULL) + goto fail; + err = PyList_Append(list, item); + Py_DECREF(item); + if (err < 0) + goto fail; + i = j = i + n; + splitcount++; + if (maxsplit && (splitcount >= maxsplit)) + break; + } + else + i++; + } + item = PyString_FromStringAndSize(s+j, (int)(len-j)); + if (item == NULL) + goto fail; + err = PyList_Append(list, item); + Py_DECREF(item); + if (err < 0) + goto fail; + + return list; + + fail: + Py_DECREF(list); + return NULL; +} + + +PyDoc_STRVAR(joinfields__doc__, +"join(list [,sep]) -> string\n" +"joinfields(list [,sep]) -> string\n" +"\n" +"Return a string composed of the words in list, with\n" +"intervening occurrences of sep. Sep defaults to a single\n" +"space.\n" +"\n" +"(join and joinfields are synonymous)"); + +static PyObject * +strop_joinfields(PyObject *self, PyObject *args) +{ + PyObject *seq; + char *sep = NULL; + int seqlen, seplen = 0; + int i, reslen = 0, slen = 0, sz = 100; + PyObject *res = NULL; + char* p = NULL; + intargfunc getitemfunc; + + WARN; + if (!PyArg_ParseTuple(args, "O|t#:join", &seq, &sep, &seplen)) + return NULL; + if (sep == NULL) { + sep = " "; + seplen = 1; + } + + seqlen = PySequence_Size(seq); + if (seqlen < 0 && PyErr_Occurred()) + return NULL; + + if (seqlen == 1) { + /* Optimization if there's only one item */ + PyObject *item = PySequence_GetItem(seq, 0); + if (item && !PyString_Check(item)) { + PyErr_SetString(PyExc_TypeError, + "first argument must be sequence of strings"); + Py_DECREF(item); + return NULL; + } + return item; + } + + if (!(res = PyString_FromStringAndSize((char*)NULL, sz))) + return NULL; + p = PyString_AsString(res); + + /* optimize for lists, since it's the most common case. all others + * (tuples and arbitrary sequences) just use the sequence abstract + * interface. + */ + if (PyList_Check(seq)) { + for (i = 0; i < seqlen; i++) { + PyObject *item = PyList_GET_ITEM(seq, i); + if (!PyString_Check(item)) { + PyErr_SetString(PyExc_TypeError, + "first argument must be sequence of strings"); + Py_DECREF(res); + return NULL; + } + slen = PyString_GET_SIZE(item); + while (reslen + slen + seplen >= sz) { + if (_PyString_Resize(&res, sz * 2) < 0) + return NULL; + sz *= 2; + p = PyString_AsString(res) + reslen; + } + if (i > 0) { + memcpy(p, sep, seplen); + p += seplen; + reslen += seplen; + } + memcpy(p, PyString_AS_STRING(item), slen); + p += slen; + reslen += slen; + } + _PyString_Resize(&res, reslen); + return res; + } + + if (seq->ob_type->tp_as_sequence == NULL || + (getitemfunc = seq->ob_type->tp_as_sequence->sq_item) == NULL) + { + PyErr_SetString(PyExc_TypeError, + "first argument must be a sequence"); + return NULL; + } + /* This is now type safe */ + for (i = 0; i < seqlen; i++) { + PyObject *item = getitemfunc(seq, i); + if (!item || !PyString_Check(item)) { + PyErr_SetString(PyExc_TypeError, + "first argument must be sequence of strings"); + Py_DECREF(res); + Py_XDECREF(item); + return NULL; + } + slen = PyString_GET_SIZE(item); + while (reslen + slen + seplen >= sz) { + if (_PyString_Resize(&res, sz * 2) < 0) { + Py_DECREF(item); + return NULL; + } + sz *= 2; + p = PyString_AsString(res) + reslen; + } + if (i > 0) { + memcpy(p, sep, seplen); + p += seplen; + reslen += seplen; + } + memcpy(p, PyString_AS_STRING(item), slen); + p += slen; + reslen += slen; + Py_DECREF(item); + } + _PyString_Resize(&res, reslen); + return res; +} + + +PyDoc_STRVAR(find__doc__, +"find(s, sub [,start [,end]]) -> in\n" +"\n" +"Return the lowest index in s where substring sub is found,\n" +"such that sub is contained within s[start,end]. Optional\n" +"arguments start and end are interpreted as in slice notation.\n" +"\n" +"Return -1 on failure."); + +static PyObject * +strop_find(PyObject *self, PyObject *args) +{ + char *s, *sub; + int len, n, i = 0, last = INT_MAX; + + WARN; + if (!PyArg_ParseTuple(args, "t#t#|ii:find", &s, &len, &sub, &n, &i, &last)) + return NULL; + + if (last > len) + last = len; + if (last < 0) + last += len; + if (last < 0) + last = 0; + if (i < 0) + i += len; + if (i < 0) + i = 0; + + if (n == 0 && i <= last) + return PyInt_FromLong((long)i); + + last -= n; + for (; i <= last; ++i) + if (s[i] == sub[0] && + (n == 1 || memcmp(&s[i+1], &sub[1], n-1) == 0)) + return PyInt_FromLong((long)i); + + return PyInt_FromLong(-1L); +} + + +PyDoc_STRVAR(rfind__doc__, +"rfind(s, sub [,start [,end]]) -> int\n" +"\n" +"Return the highest index in s where substring sub is found,\n" +"such that sub is contained within s[start,end]. Optional\n" +"arguments start and end are interpreted as in slice notation.\n" +"\n" +"Return -1 on failure."); + +static PyObject * +strop_rfind(PyObject *self, PyObject *args) +{ + char *s, *sub; + int len, n, j; + int i = 0, last = INT_MAX; + + WARN; + if (!PyArg_ParseTuple(args, "t#t#|ii:rfind", &s, &len, &sub, &n, &i, &last)) + return NULL; + + if (last > len) + last = len; + if (last < 0) + last += len; + if (last < 0) + last = 0; + if (i < 0) + i += len; + if (i < 0) + i = 0; + + if (n == 0 && i <= last) + return PyInt_FromLong((long)last); + + for (j = last-n; j >= i; --j) + if (s[j] == sub[0] && + (n == 1 || memcmp(&s[j+1], &sub[1], n-1) == 0)) + return PyInt_FromLong((long)j); + + return PyInt_FromLong(-1L); +} + + +static PyObject * +do_strip(PyObject *args, int striptype) +{ + char *s; + int len, i, j; + + + if (PyString_AsStringAndSize(args, &s, &len)) + return NULL; + + i = 0; + if (striptype != RIGHTSTRIP) { + while (i < len && isspace(Py_CHARMASK(s[i]))) { + i++; + } + } + + j = len; + if (striptype != LEFTSTRIP) { + do { + j--; + } while (j >= i && isspace(Py_CHARMASK(s[j]))); + j++; + } + + if (i == 0 && j == len) { + Py_INCREF(args); + return args; + } + else + return PyString_FromStringAndSize(s+i, j-i); +} + + +PyDoc_STRVAR(strip__doc__, +"strip(s) -> string\n" +"\n" +"Return a copy of the string s with leading and trailing\n" +"whitespace removed."); + +static PyObject * +strop_strip(PyObject *self, PyObject *args) +{ + WARN; + return do_strip(args, BOTHSTRIP); +} + + +PyDoc_STRVAR(lstrip__doc__, +"lstrip(s) -> string\n" +"\n" +"Return a copy of the string s with leading whitespace removed."); + +static PyObject * +strop_lstrip(PyObject *self, PyObject *args) +{ + WARN; + return do_strip(args, LEFTSTRIP); +} + + +PyDoc_STRVAR(rstrip__doc__, +"rstrip(s) -> string\n" +"\n" +"Return a copy of the string s with trailing whitespace removed."); + +static PyObject * +strop_rstrip(PyObject *self, PyObject *args) +{ + WARN; + return do_strip(args, RIGHTSTRIP); +} + + +PyDoc_STRVAR(lower__doc__, +"lower(s) -> string\n" +"\n" +"Return a copy of the string s converted to lowercase."); + +static PyObject * +strop_lower(PyObject *self, PyObject *args) +{ + char *s, *s_new; + int i, n; + PyObject *new; + int changed; + + WARN; + if (PyString_AsStringAndSize(args, &s, &n)) + return NULL; + new = PyString_FromStringAndSize(NULL, n); + if (new == NULL) + return NULL; + s_new = PyString_AsString(new); + changed = 0; + for (i = 0; i < n; i++) { + int c = Py_CHARMASK(*s++); + if (isupper(c)) { + changed = 1; + *s_new = tolower(c); + } else + *s_new = c; + s_new++; + } + if (!changed) { + Py_DECREF(new); + Py_INCREF(args); + return args; + } + return new; +} + + +PyDoc_STRVAR(upper__doc__, +"upper(s) -> string\n" +"\n" +"Return a copy of the string s converted to uppercase."); + +static PyObject * +strop_upper(PyObject *self, PyObject *args) +{ + char *s, *s_new; + int i, n; + PyObject *new; + int changed; + + WARN; + if (PyString_AsStringAndSize(args, &s, &n)) + return NULL; + new = PyString_FromStringAndSize(NULL, n); + if (new == NULL) + return NULL; + s_new = PyString_AsString(new); + changed = 0; + for (i = 0; i < n; i++) { + int c = Py_CHARMASK(*s++); + if (islower(c)) { + changed = 1; + *s_new = toupper(c); + } else + *s_new = c; + s_new++; + } + if (!changed) { + Py_DECREF(new); + Py_INCREF(args); + return args; + } + return new; +} + + +PyDoc_STRVAR(capitalize__doc__, +"capitalize(s) -> string\n" +"\n" +"Return a copy of the string s with only its first character\n" +"capitalized."); + +static PyObject * +strop_capitalize(PyObject *self, PyObject *args) +{ + char *s, *s_new; + int i, n; + PyObject *new; + int changed; + + WARN; + if (PyString_AsStringAndSize(args, &s, &n)) + return NULL; + new = PyString_FromStringAndSize(NULL, n); + if (new == NULL) + return NULL; + s_new = PyString_AsString(new); + changed = 0; + if (0 < n) { + int c = Py_CHARMASK(*s++); + if (islower(c)) { + changed = 1; + *s_new = toupper(c); + } else + *s_new = c; + s_new++; + } + for (i = 1; i < n; i++) { + int c = Py_CHARMASK(*s++); + if (isupper(c)) { + changed = 1; + *s_new = tolower(c); + } else + *s_new = c; + s_new++; + } + if (!changed) { + Py_DECREF(new); + Py_INCREF(args); + return args; + } + return new; +} + + +PyDoc_STRVAR(expandtabs__doc__, +"expandtabs(string, [tabsize]) -> string\n" +"\n" +"Expand tabs in a string, i.e. replace them by one or more spaces,\n" +"depending on the current column and the given tab size (default 8).\n" +"The column number is reset to zero after each newline occurring in the\n" +"string. This doesn't understand other non-printing characters."); + +static PyObject * +strop_expandtabs(PyObject *self, PyObject *args) +{ + /* Original by Fredrik Lundh */ + char* e; + char* p; + char* q; + int i, j; + PyObject* out; + char* string; + int stringlen; + int tabsize = 8; + + WARN; + /* Get arguments */ + if (!PyArg_ParseTuple(args, "s#|i:expandtabs", &string, &stringlen, &tabsize)) + return NULL; + if (tabsize < 1) { + PyErr_SetString(PyExc_ValueError, + "tabsize must be at least 1"); + return NULL; + } + + /* First pass: determine size of output string */ + i = j = 0; /* j: current column; i: total of previous lines */ + e = string + stringlen; + for (p = string; p < e; p++) { + if (*p == '\t') + j += tabsize - (j%tabsize); + else { + j++; + if (*p == '\n') { + i += j; + j = 0; + } + } + } + + /* Second pass: create output string and fill it */ + out = PyString_FromStringAndSize(NULL, i+j); + if (out == NULL) + return NULL; + + i = 0; + q = PyString_AS_STRING(out); + + for (p = string; p < e; p++) { + if (*p == '\t') { + j = tabsize - (i%tabsize); + i += j; + while (j-- > 0) + *q++ = ' '; + } else { + *q++ = *p; + i++; + if (*p == '\n') + i = 0; + } + } + + return out; +} + + +PyDoc_STRVAR(count__doc__, +"count(s, sub[, start[, end]]) -> int\n" +"\n" +"Return the number of occurrences of substring sub in string\n" +"s[start:end]. Optional arguments start and end are\n" +"interpreted as in slice notation."); + +static PyObject * +strop_count(PyObject *self, PyObject *args) +{ + char *s, *sub; + int len, n; + int i = 0, last = INT_MAX; + int m, r; + + WARN; + if (!PyArg_ParseTuple(args, "t#t#|ii:count", &s, &len, &sub, &n, &i, &last)) + return NULL; + if (last > len) + last = len; + if (last < 0) + last += len; + if (last < 0) + last = 0; + if (i < 0) + i += len; + if (i < 0) + i = 0; + m = last + 1 - n; + if (n == 0) + return PyInt_FromLong((long) (m-i)); + + r = 0; + while (i < m) { + if (!memcmp(s+i, sub, n)) { + r++; + i += n; + } else { + i++; + } + } + return PyInt_FromLong((long) r); +} + + +PyDoc_STRVAR(swapcase__doc__, +"swapcase(s) -> string\n" +"\n" +"Return a copy of the string s with upper case characters\n" +"converted to lowercase and vice versa."); + +static PyObject * +strop_swapcase(PyObject *self, PyObject *args) +{ + char *s, *s_new; + int i, n; + PyObject *new; + int changed; + + WARN; + if (PyString_AsStringAndSize(args, &s, &n)) + return NULL; + new = PyString_FromStringAndSize(NULL, n); + if (new == NULL) + return NULL; + s_new = PyString_AsString(new); + changed = 0; + for (i = 0; i < n; i++) { + int c = Py_CHARMASK(*s++); + if (islower(c)) { + changed = 1; + *s_new = toupper(c); + } + else if (isupper(c)) { + changed = 1; + *s_new = tolower(c); + } + else + *s_new = c; + s_new++; + } + if (!changed) { + Py_DECREF(new); + Py_INCREF(args); + return args; + } + return new; +} + + +PyDoc_STRVAR(atoi__doc__, +"atoi(s [,base]) -> int\n" +"\n" +"Return the integer represented by the string s in the given\n" +"base, which defaults to 10. The string s must consist of one\n" +"or more digits, possibly preceded by a sign. If base is 0, it\n" +"is chosen from the leading characters of s, 0 for octal, 0x or\n" +"0X for hexadecimal. If base is 16, a preceding 0x or 0X is\n" +"accepted."); + +static PyObject * +strop_atoi(PyObject *self, PyObject *args) +{ + char *s, *end; + int base = 10; + long x; + char buffer[256]; /* For errors */ + + WARN; + if (!PyArg_ParseTuple(args, "s|i:atoi", &s, &base)) + return NULL; + + if ((base != 0 && base < 2) || base > 36) { + PyErr_SetString(PyExc_ValueError, "invalid base for atoi()"); + return NULL; + } + + while (*s && isspace(Py_CHARMASK(*s))) + s++; + errno = 0; + if (base == 0 && s[0] == '0') + x = (long) PyOS_strtoul(s, &end, base); + else + x = PyOS_strtol(s, &end, base); + if (end == s || !isalnum((int)end[-1])) + goto bad; + while (*end && isspace(Py_CHARMASK(*end))) + end++; + if (*end != '\0') { + bad: + PyOS_snprintf(buffer, sizeof(buffer), + "invalid literal for atoi(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + else if (errno != 0) { + PyOS_snprintf(buffer, sizeof(buffer), + "atoi() literal too large: %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + return PyInt_FromLong(x); +} + + +PyDoc_STRVAR(atol__doc__, +"atol(s [,base]) -> long\n" +"\n" +"Return the long integer represented by the string s in the\n" +"given base, which defaults to 10. The string s must consist\n" +"of one or more digits, possibly preceded by a sign. If base\n" +"is 0, it is chosen from the leading characters of s, 0 for\n" +"octal, 0x or 0X for hexadecimal. If base is 16, a preceding\n" +"0x or 0X is accepted. A trailing L or l is not accepted,\n" +"unless base is 0."); + +static PyObject * +strop_atol(PyObject *self, PyObject *args) +{ + char *s, *end; + int base = 10; + PyObject *x; + char buffer[256]; /* For errors */ + + WARN; + if (!PyArg_ParseTuple(args, "s|i:atol", &s, &base)) + return NULL; + + if ((base != 0 && base < 2) || base > 36) { + PyErr_SetString(PyExc_ValueError, "invalid base for atol()"); + return NULL; + } + + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] == '\0') { + PyErr_SetString(PyExc_ValueError, "empty string for atol()"); + return NULL; + } + x = PyLong_FromString(s, &end, base); + if (x == NULL) + return NULL; + if (base == 0 && (*end == 'l' || *end == 'L')) + end++; + while (*end && isspace(Py_CHARMASK(*end))) + end++; + if (*end != '\0') { + PyOS_snprintf(buffer, sizeof(buffer), + "invalid literal for atol(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + Py_DECREF(x); + return NULL; + } + return x; +} + + +PyDoc_STRVAR(atof__doc__, +"atof(s) -> float\n" +"\n" +"Return the floating point number represented by the string s."); + +static PyObject * +strop_atof(PyObject *self, PyObject *args) +{ + extern double strtod(const char *, char **); + char *s, *end; + double x; + char buffer[256]; /* For errors */ + + WARN; + if (!PyArg_ParseTuple(args, "s:atof", &s)) + return NULL; + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] == '\0') { + PyErr_SetString(PyExc_ValueError, "empty string for atof()"); + return NULL; + } + errno = 0; + PyFPE_START_PROTECT("strop_atof", return 0) + x = strtod(s, &end); + PyFPE_END_PROTECT(x) + while (*end && isspace(Py_CHARMASK(*end))) + end++; + if (*end != '\0') { + PyOS_snprintf(buffer, sizeof(buffer), + "invalid literal for atof(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + else if (errno != 0) { + PyOS_snprintf(buffer, sizeof(buffer), + "atof() literal too large: %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + return PyFloat_FromDouble(x); +} + + +PyDoc_STRVAR(maketrans__doc__, +"maketrans(frm, to) -> string\n" +"\n" +"Return a translation table (a string of 256 bytes long)\n" +"suitable for use in string.translate. The strings frm and to\n" +"must be of the same length."); + +static PyObject * +strop_maketrans(PyObject *self, PyObject *args) +{ + unsigned char *c, *from=NULL, *to=NULL; + int i, fromlen=0, tolen=0; + PyObject *result; + + if (!PyArg_ParseTuple(args, "t#t#:maketrans", &from, &fromlen, &to, &tolen)) + return NULL; + + if (fromlen != tolen) { + PyErr_SetString(PyExc_ValueError, + "maketrans arguments must have same length"); + return NULL; + } + + result = PyString_FromStringAndSize((char *)NULL, 256); + if (result == NULL) + return NULL; + c = (unsigned char *) PyString_AS_STRING((PyStringObject *)result); + for (i = 0; i < 256; i++) + c[i]=(unsigned char)i; + for (i = 0; i < fromlen; i++) + c[from[i]]=to[i]; + + return result; +} + + +PyDoc_STRVAR(translate__doc__, +"translate(s,table [,deletechars]) -> string\n" +"\n" +"Return a copy of the string s, where all characters occurring\n" +"in the optional argument deletechars are removed, and the\n" +"remaining characters have been mapped through the given\n" +"translation table, which must be a string of length 256."); + +static PyObject * +strop_translate(PyObject *self, PyObject *args) +{ + register char *input, *table, *output; + register int i, c, changed = 0; + PyObject *input_obj; + char *table1, *output_start, *del_table=NULL; + int inlen, tablen, dellen = 0; + PyObject *result; + int trans_table[256]; + + WARN; + if (!PyArg_ParseTuple(args, "St#|t#:translate", &input_obj, + &table1, &tablen, &del_table, &dellen)) + return NULL; + if (tablen != 256) { + PyErr_SetString(PyExc_ValueError, + "translation table must be 256 characters long"); + return NULL; + } + + table = table1; + inlen = PyString_Size(input_obj); + result = PyString_FromStringAndSize((char *)NULL, inlen); + if (result == NULL) + return NULL; + output_start = output = PyString_AsString(result); + input = PyString_AsString(input_obj); + + if (dellen == 0) { + /* If no deletions are required, use faster code */ + for (i = inlen; --i >= 0; ) { + c = Py_CHARMASK(*input++); + if (Py_CHARMASK((*output++ = table[c])) != c) + changed = 1; + } + if (changed) + return result; + Py_DECREF(result); + Py_INCREF(input_obj); + return input_obj; + } + + for (i = 0; i < 256; i++) + trans_table[i] = Py_CHARMASK(table[i]); + + for (i = 0; i < dellen; i++) + trans_table[(int) Py_CHARMASK(del_table[i])] = -1; + + for (i = inlen; --i >= 0; ) { + c = Py_CHARMASK(*input++); + if (trans_table[c] != -1) + if (Py_CHARMASK(*output++ = (char)trans_table[c]) == c) + continue; + changed = 1; + } + if (!changed) { + Py_DECREF(result); + Py_INCREF(input_obj); + return input_obj; + } + /* Fix the size of the resulting string */ + if (inlen > 0) + _PyString_Resize(&result, output - output_start); + return result; +} + + +/* What follows is used for implementing replace(). Perry Stoll. */ + +/* + mymemfind + + strstr replacement for arbitrary blocks of memory. + + Locates the first occurrence in the memory pointed to by MEM of the + contents of memory pointed to by PAT. Returns the index into MEM if + found, or -1 if not found. If len of PAT is greater than length of + MEM, the function returns -1. +*/ +static int +mymemfind(const char *mem, int len, const char *pat, int pat_len) +{ + register int ii; + + /* pattern can not occur in the last pat_len-1 chars */ + len -= pat_len; + + for (ii = 0; ii <= len; ii++) { + if (mem[ii] == pat[0] && + (pat_len == 1 || + memcmp(&mem[ii+1], &pat[1], pat_len-1) == 0)) { + return ii; + } + } + return -1; +} + +/* + mymemcnt + + Return the number of distinct times PAT is found in MEM. + meaning mem=1111 and pat==11 returns 2. + mem=11111 and pat==11 also return 2. + */ +static int +mymemcnt(const char *mem, int len, const char *pat, int pat_len) +{ + register int offset = 0; + int nfound = 0; + + while (len >= 0) { + offset = mymemfind(mem, len, pat, pat_len); + if (offset == -1) + break; + mem += offset + pat_len; + len -= offset + pat_len; + nfound++; + } + return nfound; +} + +/* + mymemreplace + + Return a string in which all occurrences of PAT in memory STR are + replaced with SUB. + + If length of PAT is less than length of STR or there are no occurrences + of PAT in STR, then the original string is returned. Otherwise, a new + string is allocated here and returned. + + on return, out_len is: + the length of output string, or + -1 if the input string is returned, or + unchanged if an error occurs (no memory). + + return value is: + the new string allocated locally, or + NULL if an error occurred. +*/ +static char * +mymemreplace(const char *str, int len, /* input string */ + const char *pat, int pat_len, /* pattern string to find */ + const char *sub, int sub_len, /* substitution string */ + int count, /* number of replacements */ + int *out_len) +{ + char *out_s; + char *new_s; + int nfound, offset, new_len; + + if (len == 0 || pat_len > len) + goto return_same; + + /* find length of output string */ + nfound = mymemcnt(str, len, pat, pat_len); + if (count < 0) + count = INT_MAX; + else if (nfound > count) + nfound = count; + if (nfound == 0) + goto return_same; + + new_len = len + nfound*(sub_len - pat_len); + if (new_len == 0) { + /* Have to allocate something for the caller to free(). */ + out_s = (char *)PyMem_MALLOC(1); + if (out_s == NULL) + return NULL; + out_s[0] = '\0'; + } + else { + assert(new_len > 0); + new_s = (char *)PyMem_MALLOC(new_len); + if (new_s == NULL) + return NULL; + out_s = new_s; + + for (; count > 0 && len > 0; --count) { + /* find index of next instance of pattern */ + offset = mymemfind(str, len, pat, pat_len); + if (offset == -1) + break; + + /* copy non matching part of input string */ + memcpy(new_s, str, offset); + str += offset + pat_len; + len -= offset + pat_len; + + /* copy substitute into the output string */ + new_s += offset; + memcpy(new_s, sub, sub_len); + new_s += sub_len; + } + /* copy any remaining values into output string */ + if (len > 0) + memcpy(new_s, str, len); + } + *out_len = new_len; + return out_s; + + return_same: + *out_len = -1; + return (char *)str; /* cast away const */ +} + + +PyDoc_STRVAR(replace__doc__, +"replace (str, old, new[, maxsplit]) -> string\n" +"\n" +"Return a copy of string str with all occurrences of substring\n" +"old replaced by new. If the optional argument maxsplit is\n" +"given, only the first maxsplit occurrences are replaced."); + +static PyObject * +strop_replace(PyObject *self, PyObject *args) +{ + char *str, *pat,*sub,*new_s; + int len,pat_len,sub_len,out_len; + int count = -1; + PyObject *new; + + WARN; + if (!PyArg_ParseTuple(args, "t#t#t#|i:replace", + &str, &len, &pat, &pat_len, &sub, &sub_len, + &count)) + return NULL; + if (pat_len <= 0) { + PyErr_SetString(PyExc_ValueError, "empty pattern string"); + return NULL; + } + /* CAUTION: strop treats a replace count of 0 as infinity, unlke + * current (2.1) string.py and string methods. Preserve this for + * ... well, hard to say for what . + */ + if (count == 0) + count = -1; + new_s = mymemreplace(str,len,pat,pat_len,sub,sub_len,count,&out_len); + if (new_s == NULL) { + PyErr_NoMemory(); + return NULL; + } + if (out_len == -1) { + /* we're returning another reference to the input string */ + new = PyTuple_GetItem(args, 0); + Py_XINCREF(new); + } + else { + new = PyString_FromStringAndSize(new_s, out_len); + PyMem_FREE(new_s); + } + return new; +} + + +/* List of functions defined in the module */ + +static PyMethodDef +strop_methods[] = { + {"atof", strop_atof, METH_VARARGS, atof__doc__}, + {"atoi", strop_atoi, METH_VARARGS, atoi__doc__}, + {"atol", strop_atol, METH_VARARGS, atol__doc__}, + {"capitalize", strop_capitalize, METH_O, capitalize__doc__}, + {"count", strop_count, METH_VARARGS, count__doc__}, + {"expandtabs", strop_expandtabs, METH_VARARGS, expandtabs__doc__}, + {"find", strop_find, METH_VARARGS, find__doc__}, + {"join", strop_joinfields, METH_VARARGS, joinfields__doc__}, + {"joinfields", strop_joinfields, METH_VARARGS, joinfields__doc__}, + {"lstrip", strop_lstrip, METH_O, lstrip__doc__}, + {"lower", strop_lower, METH_O, lower__doc__}, + {"maketrans", strop_maketrans, METH_VARARGS, maketrans__doc__}, + {"replace", strop_replace, METH_VARARGS, replace__doc__}, + {"rfind", strop_rfind, METH_VARARGS, rfind__doc__}, + {"rstrip", strop_rstrip, METH_O, rstrip__doc__}, + {"split", strop_splitfields, METH_VARARGS, splitfields__doc__}, + {"splitfields", strop_splitfields, METH_VARARGS, splitfields__doc__}, + {"strip", strop_strip, METH_O, strip__doc__}, + {"swapcase", strop_swapcase, METH_O, swapcase__doc__}, + {"translate", strop_translate, METH_VARARGS, translate__doc__}, + {"upper", strop_upper, METH_O, upper__doc__}, + {NULL, NULL} /* sentinel */ +}; + + +PyMODINIT_FUNC +initstrop(void) +{ + PyObject *m, *s; + char buf[256]; + int c, n; + m = Py_InitModule4("strop", strop_methods, strop_module__doc__, + (PyObject*)NULL, PYTHON_API_VERSION); + + /* Create 'whitespace' object */ + n = 0; + for (c = 0; c < 256; c++) { + if (isspace(c)) + buf[n++] = c; + } + s = PyString_FromStringAndSize(buf, n); + if (s) + PyModule_AddObject(m, "whitespace", s); + + /* Create 'lowercase' object */ + n = 0; + for (c = 0; c < 256; c++) { + if (islower(c)) + buf[n++] = c; + } + s = PyString_FromStringAndSize(buf, n); + if (s) + PyModule_AddObject(m, "lowercase", s); + + /* Create 'uppercase' object */ + n = 0; + for (c = 0; c < 256; c++) { + if (isupper(c)) + buf[n++] = c; + } + s = PyString_FromStringAndSize(buf, n); + if (s) + PyModule_AddObject(m, "uppercase", s); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/structmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/structmodule.c new file mode 100644 index 00000000..689538f7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/structmodule.c @@ -0,0 +1,1297 @@ +/* struct module -- pack values into and (out of) strings */ + +/* New version supporting byte order, alignment and size options, + character strings, and unsigned numbers */ + +#include "Python.h" +#include + +PyDoc_STRVAR(struct__doc__, +"Functions to convert between Python values and C structs.\n\ +Python strings are used to hold the data representing the C struct\n\ +and also as format strings to describe the layout of data in the C struct.\n\ +\n\ +The optional first format char indicates byte order, size and alignment:\n\ + @: native order, size & alignment (default)\n\ + =: native order, std. size & alignment\n\ + <: little-endian, std. size & alignment\n\ + >: big-endian, std. size & alignment\n\ + !: same as >\n\ +\n\ +The remaining chars indicate types of args and must match exactly;\n\ +these can be preceded by a decimal repeat count:\n\ + x: pad byte (no data); c:char; b:signed byte; B:unsigned byte;\n\ + h:short; H:unsigned short; i:int; I:unsigned int;\n\ + l:long; L:unsigned long; f:float; d:double.\n\ +Special cases (preceding decimal count indicates length):\n\ + s:string (array of char); p: pascal string (with count byte).\n\ +Special case (only available in native format):\n\ + P:an integer type that is wide enough to hold a pointer.\n\ +Special case (not in native mode unless 'long long' in platform C):\n\ + q:long long; Q:unsigned long long\n\ +Whitespace between formats is ignored.\n\ +\n\ +The variable struct.error is an exception raised on errors."); + + +/* Exception */ + +static PyObject *StructError; + + +/* Define various structs to figure out the alignments of types */ + +#ifdef __MWERKS__ +/* +** XXXX We have a problem here. There are no unique alignment rules +** on the PowerPC mac. +*/ +#ifdef __powerc +#pragma options align=mac68k +#endif +#endif /* __MWERKS__ */ + +typedef struct { char c; short x; } st_short; +typedef struct { char c; int x; } st_int; +typedef struct { char c; long x; } st_long; +typedef struct { char c; float x; } st_float; +typedef struct { char c; double x; } st_double; +typedef struct { char c; void *x; } st_void_p; + +#define SHORT_ALIGN (sizeof(st_short) - sizeof(short)) +#define INT_ALIGN (sizeof(st_int) - sizeof(int)) +#define LONG_ALIGN (sizeof(st_long) - sizeof(long)) +#define FLOAT_ALIGN (sizeof(st_float) - sizeof(float)) +#define DOUBLE_ALIGN (sizeof(st_double) - sizeof(double)) +#define VOID_P_ALIGN (sizeof(st_void_p) - sizeof(void *)) + +/* We can't support q and Q in native mode unless the compiler does; + in std mode, they're 8 bytes on all platforms. */ +#ifdef HAVE_LONG_LONG +typedef struct { char c; PY_LONG_LONG x; } s_long_long; +#define LONG_LONG_ALIGN (sizeof(s_long_long) - sizeof(PY_LONG_LONG)) +#endif + +#define STRINGIFY(x) #x + +#ifdef __powerc +#pragma options align=reset +#endif + +/* Helper to get a PyLongObject by hook or by crook. Caller should decref. */ + +static PyObject * +get_pylong(PyObject *v) +{ + PyNumberMethods *m; + + assert(v != NULL); + if (PyInt_Check(v)) + return PyLong_FromLong(PyInt_AS_LONG(v)); + if (PyLong_Check(v)) { + Py_INCREF(v); + return v; + } + m = v->ob_type->tp_as_number; + if (m != NULL && m->nb_long != NULL) { + v = m->nb_long(v); + if (v == NULL) + return NULL; + if (PyLong_Check(v)) + return v; + Py_DECREF(v); + } + PyErr_SetString(StructError, + "cannot convert argument to long"); + return NULL; +} + +/* Helper routine to get a Python integer and raise the appropriate error + if it isn't one */ + +static int +get_long(PyObject *v, long *p) +{ + long x = PyInt_AsLong(v); + if (x == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + PyErr_SetString(StructError, + "required argument is not an integer"); + return -1; + } + *p = x; + return 0; +} + + +/* Same, but handling unsigned long */ + +static int +get_ulong(PyObject *v, unsigned long *p) +{ + if (PyLong_Check(v)) { + unsigned long x = PyLong_AsUnsignedLong(v); + if (x == (unsigned long)(-1) && PyErr_Occurred()) + return -1; + *p = x; + return 0; + } + else { + return get_long(v, (long *)p); + } +} + +#ifdef HAVE_LONG_LONG + +/* Same, but handling native long long. */ + +static int +get_longlong(PyObject *v, PY_LONG_LONG *p) +{ + PY_LONG_LONG x; + + v = get_pylong(v); + if (v == NULL) + return -1; + assert(PyLong_Check(v)); + x = PyLong_AsLongLong(v); + Py_DECREF(v); + if (x == (PY_LONG_LONG)-1 && PyErr_Occurred()) + return -1; + *p = x; + return 0; +} + +/* Same, but handling native unsigned long long. */ + +static int +get_ulonglong(PyObject *v, unsigned PY_LONG_LONG *p) +{ + unsigned PY_LONG_LONG x; + + v = get_pylong(v); + if (v == NULL) + return -1; + assert(PyLong_Check(v)); + x = PyLong_AsUnsignedLongLong(v); + Py_DECREF(v); + if (x == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) + return -1; + *p = x; + return 0; +} + +#endif + +/* Floating point helpers */ + +static PyObject * +unpack_float(const char *p, /* start of 4-byte string */ + int le) /* true for little-endian, false for big-endian */ +{ + double x; + + x = _PyFloat_Unpack4((unsigned char *)p, le); + if (x == -1.0 && PyErr_Occurred()) + return NULL; + return PyFloat_FromDouble(x); +} + +static PyObject * +unpack_double(const char *p, /* start of 8-byte string */ + int le) /* true for little-endian, false for big-endian */ +{ + double x; + + x = _PyFloat_Unpack8((unsigned char *)p, le); + if (x == -1.0 && PyErr_Occurred()) + return NULL; + return PyFloat_FromDouble(x); +} + + +/* The translation function for each format character is table driven */ + +typedef struct _formatdef { + char format; + int size; + int alignment; + PyObject* (*unpack)(const char *, + const struct _formatdef *); + int (*pack)(char *, PyObject *, + const struct _formatdef *); +} formatdef; + +/* A large number of small routines follow, with names of the form + + [bln][up]_TYPE + + [bln] distiguishes among big-endian, little-endian and native. + [pu] distiguishes between pack (to struct) and unpack (from struct). + TYPE is one of char, byte, ubyte, etc. +*/ + +/* Native mode routines. ****************************************************/ +/* NOTE: + In all n[up]_ routines handling types larger than 1 byte, there is + *no* guarantee that the p pointer is properly aligned for each type, + therefore memcpy is called. An intermediate variable is used to + compensate for big-endian architectures. + Normally both the intermediate variable and the memcpy call will be + skipped by C optimisation in little-endian architectures (gcc >= 2.91 + does this). */ + +static PyObject * +nu_char(const char *p, const formatdef *f) +{ + return PyString_FromStringAndSize(p, 1); +} + +static PyObject * +nu_byte(const char *p, const formatdef *f) +{ + return PyInt_FromLong((long) *(signed char *)p); +} + +static PyObject * +nu_ubyte(const char *p, const formatdef *f) +{ + return PyInt_FromLong((long) *(unsigned char *)p); +} + +static PyObject * +nu_short(const char *p, const formatdef *f) +{ + short x; + memcpy((char *)&x, p, sizeof x); + return PyInt_FromLong((long)x); +} + +static PyObject * +nu_ushort(const char *p, const formatdef *f) +{ + unsigned short x; + memcpy((char *)&x, p, sizeof x); + return PyInt_FromLong((long)x); +} + +static PyObject * +nu_int(const char *p, const formatdef *f) +{ + int x; + memcpy((char *)&x, p, sizeof x); + return PyInt_FromLong((long)x); +} + +static PyObject * +nu_uint(const char *p, const formatdef *f) +{ + unsigned int x; + memcpy((char *)&x, p, sizeof x); + return PyLong_FromUnsignedLong((unsigned long)x); +} + +static PyObject * +nu_long(const char *p, const formatdef *f) +{ + long x; + memcpy((char *)&x, p, sizeof x); + return PyInt_FromLong(x); +} + +static PyObject * +nu_ulong(const char *p, const formatdef *f) +{ + unsigned long x; + memcpy((char *)&x, p, sizeof x); + return PyLong_FromUnsignedLong(x); +} + +/* Native mode doesn't support q or Q unless the platform C supports + long long (or, on Windows, __int64). */ + +#ifdef HAVE_LONG_LONG + +static PyObject * +nu_longlong(const char *p, const formatdef *f) +{ + PY_LONG_LONG x; + memcpy((char *)&x, p, sizeof x); + return PyLong_FromLongLong(x); +} + +static PyObject * +nu_ulonglong(const char *p, const formatdef *f) +{ + unsigned PY_LONG_LONG x; + memcpy((char *)&x, p, sizeof x); + return PyLong_FromUnsignedLongLong(x); +} + +#endif + +static PyObject * +nu_float(const char *p, const formatdef *f) +{ + float x; + memcpy((char *)&x, p, sizeof x); + return PyFloat_FromDouble((double)x); +} + +static PyObject * +nu_double(const char *p, const formatdef *f) +{ + double x; + memcpy((char *)&x, p, sizeof x); + return PyFloat_FromDouble(x); +} + +static PyObject * +nu_void_p(const char *p, const formatdef *f) +{ + void *x; + memcpy((char *)&x, p, sizeof x); + return PyLong_FromVoidPtr(x); +} + +static int +np_byte(char *p, PyObject *v, const formatdef *f) +{ + long x; + if (get_long(v, &x) < 0) + return -1; + if (x < -128 || x > 127){ + PyErr_SetString(StructError, + "byte format requires -128<=number<=127"); + return -1; + } + *p = (char)x; + return 0; +} + +static int +np_ubyte(char *p, PyObject *v, const formatdef *f) +{ + long x; + if (get_long(v, &x) < 0) + return -1; + if (x < 0 || x > 255){ + PyErr_SetString(StructError, + "ubyte format requires 0<=number<=255"); + return -1; + } + *p = (char)x; + return 0; +} + +static int +np_char(char *p, PyObject *v, const formatdef *f) +{ + if (!PyString_Check(v) || PyString_Size(v) != 1) { + PyErr_SetString(StructError, + "char format require string of length 1"); + return -1; + } + *p = *PyString_AsString(v); + return 0; +} + +static int +np_short(char *p, PyObject *v, const formatdef *f) +{ + long x; + short y; + if (get_long(v, &x) < 0) + return -1; + if (x < SHRT_MIN || x > SHRT_MAX){ + PyErr_SetString(StructError, + "short format requires " STRINGIFY(SHRT_MIN) + "<=number<=" STRINGIFY(SHRT_MAX)); + return -1; + } + y = (short)x; + memcpy(p, (char *)&y, sizeof y); + return 0; +} + +static int +np_ushort(char *p, PyObject *v, const formatdef *f) +{ + long x; + unsigned short y; + if (get_long(v, &x) < 0) + return -1; + if (x < 0 || x > USHRT_MAX){ + PyErr_SetString(StructError, + "short format requires 0<=number<=" STRINGIFY(USHRT_MAX)); + return -1; + } + y = (unsigned short)x; + memcpy(p, (char *)&y, sizeof y); + return 0; +} + +static int +np_int(char *p, PyObject *v, const formatdef *f) +{ + long x; + int y; + if (get_long(v, &x) < 0) + return -1; + y = (int)x; + memcpy(p, (char *)&y, sizeof y); + return 0; +} + +static int +np_uint(char *p, PyObject *v, const formatdef *f) +{ + unsigned long x; + unsigned int y; + if (get_ulong(v, &x) < 0) + return -1; + y = (unsigned int)x; + memcpy(p, (char *)&y, sizeof y); + return 0; +} + +static int +np_long(char *p, PyObject *v, const formatdef *f) +{ + long x; + if (get_long(v, &x) < 0) + return -1; + memcpy(p, (char *)&x, sizeof x); + return 0; +} + +static int +np_ulong(char *p, PyObject *v, const formatdef *f) +{ + unsigned long x; + if (get_ulong(v, &x) < 0) + return -1; + memcpy(p, (char *)&x, sizeof x); + return 0; +} + +#ifdef HAVE_LONG_LONG + +static int +np_longlong(char *p, PyObject *v, const formatdef *f) +{ + PY_LONG_LONG x; + if (get_longlong(v, &x) < 0) + return -1; + memcpy(p, (char *)&x, sizeof x); + return 0; +} + +static int +np_ulonglong(char *p, PyObject *v, const formatdef *f) +{ + unsigned PY_LONG_LONG x; + if (get_ulonglong(v, &x) < 0) + return -1; + memcpy(p, (char *)&x, sizeof x); + return 0; +} +#endif + +static int +np_float(char *p, PyObject *v, const formatdef *f) +{ + float x = (float)PyFloat_AsDouble(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } + memcpy(p, (char *)&x, sizeof x); + return 0; +} + +static int +np_double(char *p, PyObject *v, const formatdef *f) +{ + double x = PyFloat_AsDouble(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } + memcpy(p, (char *)&x, sizeof(double)); + return 0; +} + +static int +np_void_p(char *p, PyObject *v, const formatdef *f) +{ + void *x = PyLong_AsVoidPtr(v); + if (x == NULL && PyErr_Occurred()) { + /* ### hrm. PyLong_AsVoidPtr raises SystemError */ + if (PyErr_ExceptionMatches(PyExc_TypeError)) + PyErr_SetString(StructError, + "required argument is not an integer"); + return -1; + } + memcpy(p, (char *)&x, sizeof x); + return 0; +} + +static formatdef native_table[] = { + {'x', sizeof(char), 0, NULL}, + {'b', sizeof(char), 0, nu_byte, np_byte}, + {'B', sizeof(char), 0, nu_ubyte, np_ubyte}, + {'c', sizeof(char), 0, nu_char, np_char}, + {'s', sizeof(char), 0, NULL}, + {'p', sizeof(char), 0, NULL}, + {'h', sizeof(short), SHORT_ALIGN, nu_short, np_short}, + {'H', sizeof(short), SHORT_ALIGN, nu_ushort, np_ushort}, + {'i', sizeof(int), INT_ALIGN, nu_int, np_int}, + {'I', sizeof(int), INT_ALIGN, nu_uint, np_uint}, + {'l', sizeof(long), LONG_ALIGN, nu_long, np_long}, + {'L', sizeof(long), LONG_ALIGN, nu_ulong, np_ulong}, + {'f', sizeof(float), FLOAT_ALIGN, nu_float, np_float}, + {'d', sizeof(double), DOUBLE_ALIGN, nu_double, np_double}, + {'P', sizeof(void *), VOID_P_ALIGN, nu_void_p, np_void_p}, +#ifdef HAVE_LONG_LONG + {'q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_longlong, np_longlong}, + {'Q', sizeof(PY_LONG_LONG), LONG_LONG_ALIGN, nu_ulonglong,np_ulonglong}, +#endif + {0} +}; + +/* Big-endian routines. *****************************************************/ + +static PyObject * +bu_int(const char *p, const formatdef *f) +{ + long x = 0; + int i = f->size; + do { + x = (x<<8) | (*p++ & 0xFF); + } while (--i > 0); + /* Extend the sign bit. */ + if (SIZEOF_LONG > f->size) + x |= -(x & (1L << (8*f->size - 1))); + return PyInt_FromLong(x); +} + +static PyObject * +bu_uint(const char *p, const formatdef *f) +{ + unsigned long x = 0; + int i = f->size; + do { + x = (x<<8) | (*p++ & 0xFF); + } while (--i > 0); + if (f->size >= 4) + return PyLong_FromUnsignedLong(x); + else + return PyInt_FromLong((long)x); +} + +static PyObject * +bu_longlong(const char *p, const formatdef *f) +{ + return _PyLong_FromByteArray((const unsigned char *)p, + 8, + 0, /* little-endian */ + 1 /* signed */); +} + +static PyObject * +bu_ulonglong(const char *p, const formatdef *f) +{ + return _PyLong_FromByteArray((const unsigned char *)p, + 8, + 0, /* little-endian */ + 0 /* signed */); +} + +static PyObject * +bu_float(const char *p, const formatdef *f) +{ + return unpack_float(p, 0); +} + +static PyObject * +bu_double(const char *p, const formatdef *f) +{ + return unpack_double(p, 0); +} + +static int +bp_int(char *p, PyObject *v, const formatdef *f) +{ + long x; + int i; + if (get_long(v, &x) < 0) + return -1; + i = f->size; + do { + p[--i] = (char)x; + x >>= 8; + } while (i > 0); + return 0; +} + +static int +bp_uint(char *p, PyObject *v, const formatdef *f) +{ + unsigned long x; + int i; + if (get_ulong(v, &x) < 0) + return -1; + i = f->size; + do { + p[--i] = (char)x; + x >>= 8; + } while (i > 0); + return 0; +} + +static int +bp_longlong(char *p, PyObject *v, const formatdef *f) +{ + int res; + v = get_pylong(v); + if (v == NULL) + return -1; + res = _PyLong_AsByteArray((PyLongObject *)v, + (unsigned char *)p, + 8, + 0, /* little_endian */ + 1 /* signed */); + Py_DECREF(v); + return res; +} + +static int +bp_ulonglong(char *p, PyObject *v, const formatdef *f) +{ + int res; + v = get_pylong(v); + if (v == NULL) + return -1; + res = _PyLong_AsByteArray((PyLongObject *)v, + (unsigned char *)p, + 8, + 0, /* little_endian */ + 0 /* signed */); + Py_DECREF(v); + return res; +} + +static int +bp_float(char *p, PyObject *v, const formatdef *f) +{ + double x = PyFloat_AsDouble(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } + return _PyFloat_Pack4(x, (unsigned char *)p, 0); +} + +static int +bp_double(char *p, PyObject *v, const formatdef *f) +{ + double x = PyFloat_AsDouble(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } + return _PyFloat_Pack8(x, (unsigned char *)p, 0); +} + +static formatdef bigendian_table[] = { + {'x', 1, 0, NULL}, + {'b', 1, 0, bu_int, bp_int}, + {'B', 1, 0, bu_uint, bp_int}, + {'c', 1, 0, nu_char, np_char}, + {'s', 1, 0, NULL}, + {'p', 1, 0, NULL}, + {'h', 2, 0, bu_int, bp_int}, + {'H', 2, 0, bu_uint, bp_uint}, + {'i', 4, 0, bu_int, bp_int}, + {'I', 4, 0, bu_uint, bp_uint}, + {'l', 4, 0, bu_int, bp_int}, + {'L', 4, 0, bu_uint, bp_uint}, + {'q', 8, 0, bu_longlong, bp_longlong}, + {'Q', 8, 0, bu_ulonglong, bp_ulonglong}, + {'f', 4, 0, bu_float, bp_float}, + {'d', 8, 0, bu_double, bp_double}, + {0} +}; + +/* Little-endian routines. *****************************************************/ + +static PyObject * +lu_int(const char *p, const formatdef *f) +{ + long x = 0; + int i = f->size; + do { + x = (x<<8) | (p[--i] & 0xFF); + } while (i > 0); + /* Extend the sign bit. */ + if (SIZEOF_LONG > f->size) + x |= -(x & (1L << (8*f->size - 1))); + return PyInt_FromLong(x); +} + +static PyObject * +lu_uint(const char *p, const formatdef *f) +{ + unsigned long x = 0; + int i = f->size; + do { + x = (x<<8) | (p[--i] & 0xFF); + } while (i > 0); + if (f->size >= 4) + return PyLong_FromUnsignedLong(x); + else + return PyInt_FromLong((long)x); +} + +static PyObject * +lu_longlong(const char *p, const formatdef *f) +{ + return _PyLong_FromByteArray((const unsigned char *)p, + 8, + 1, /* little-endian */ + 1 /* signed */); +} + +static PyObject * +lu_ulonglong(const char *p, const formatdef *f) +{ + return _PyLong_FromByteArray((const unsigned char *)p, + 8, + 1, /* little-endian */ + 0 /* signed */); +} + +static PyObject * +lu_float(const char *p, const formatdef *f) +{ + return unpack_float(p, 1); +} + +static PyObject * +lu_double(const char *p, const formatdef *f) +{ + return unpack_double(p, 1); +} + +static int +lp_int(char *p, PyObject *v, const formatdef *f) +{ + long x; + int i; + if (get_long(v, &x) < 0) + return -1; + i = f->size; + do { + *p++ = (char)x; + x >>= 8; + } while (--i > 0); + return 0; +} + +static int +lp_uint(char *p, PyObject *v, const formatdef *f) +{ + unsigned long x; + int i; + if (get_ulong(v, &x) < 0) + return -1; + i = f->size; + do { + *p++ = (char)x; + x >>= 8; + } while (--i > 0); + return 0; +} + +static int +lp_longlong(char *p, PyObject *v, const formatdef *f) +{ + int res; + v = get_pylong(v); + if (v == NULL) + return -1; + res = _PyLong_AsByteArray((PyLongObject*)v, + (unsigned char *)p, + 8, + 1, /* little_endian */ + 1 /* signed */); + Py_DECREF(v); + return res; +} + +static int +lp_ulonglong(char *p, PyObject *v, const formatdef *f) +{ + int res; + v = get_pylong(v); + if (v == NULL) + return -1; + res = _PyLong_AsByteArray((PyLongObject*)v, + (unsigned char *)p, + 8, + 1, /* little_endian */ + 0 /* signed */); + Py_DECREF(v); + return res; +} + +static int +lp_float(char *p, PyObject *v, const formatdef *f) +{ + double x = PyFloat_AsDouble(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } + return _PyFloat_Pack4(x, (unsigned char *)p, 1); +} + +static int +lp_double(char *p, PyObject *v, const formatdef *f) +{ + double x = PyFloat_AsDouble(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_SetString(StructError, + "required argument is not a float"); + return -1; + } + return _PyFloat_Pack8(x, (unsigned char *)p, 1); +} + +static formatdef lilendian_table[] = { + {'x', 1, 0, NULL}, + {'b', 1, 0, lu_int, lp_int}, + {'B', 1, 0, lu_uint, lp_int}, + {'c', 1, 0, nu_char, np_char}, + {'s', 1, 0, NULL}, + {'p', 1, 0, NULL}, + {'h', 2, 0, lu_int, lp_int}, + {'H', 2, 0, lu_uint, lp_uint}, + {'i', 4, 0, lu_int, lp_int}, + {'I', 4, 0, lu_uint, lp_uint}, + {'l', 4, 0, lu_int, lp_int}, + {'L', 4, 0, lu_uint, lp_uint}, + {'q', 8, 0, lu_longlong, lp_longlong}, + {'Q', 8, 0, lu_ulonglong, lp_ulonglong}, + {'f', 4, 0, lu_float, lp_float}, + {'d', 8, 0, lu_double, lp_double}, + {0} +}; + + +static const formatdef * +whichtable(char **pfmt) +{ + const char *fmt = (*pfmt)++; /* May be backed out of later */ + switch (*fmt) { + case '<': + return lilendian_table; + case '>': + case '!': /* Network byte order is big-endian */ + return bigendian_table; + case '=': { /* Host byte order -- different from native in aligment! */ + int n = 1; + char *p = (char *) &n; + if (*p == 1) + return lilendian_table; + else + return bigendian_table; + } + default: + --*pfmt; /* Back out of pointer increment */ + /* Fall through */ + case '@': + return native_table; + } +} + + +/* Get the table entry for a format code */ + +static const formatdef * +getentry(int c, const formatdef *f) +{ + for (; f->format != '\0'; f++) { + if (f->format == c) { + return f; + } + } + PyErr_SetString(StructError, "bad char in struct format"); + return NULL; +} + + +/* Align a size according to a format code */ + +static int +align(int size, int c, const formatdef *e) +{ + if (e->format == c) { + if (e->alignment) { + size = ((size + e->alignment - 1) + / e->alignment) + * e->alignment; + } + } + return size; +} + + +/* calculate the size of a format string */ + +static int +calcsize(const char *fmt, const formatdef *f) +{ + const formatdef *e; + const char *s; + char c; + int size, num, itemsize, x; + + s = fmt; + size = 0; + while ((c = *s++) != '\0') { + if (isspace((int)c)) + continue; + if ('0' <= c && c <= '9') { + num = c - '0'; + while ('0' <= (c = *s++) && c <= '9') { + x = num*10 + (c - '0'); + if (x/10 != num) { + PyErr_SetString( + StructError, + "overflow in item count"); + return -1; + } + num = x; + } + if (c == '\0') + break; + } + else + num = 1; + + e = getentry(c, f); + if (e == NULL) + return -1; + itemsize = e->size; + size = align(size, c, e); + x = num * itemsize; + size += x; + if (x/itemsize != num || size < 0) { + PyErr_SetString(StructError, + "total struct size too long"); + return -1; + } + } + + return size; +} + + +PyDoc_STRVAR(calcsize__doc__, +"calcsize(fmt) -> int\n\ +Return size of C struct described by format string fmt.\n\ +See struct.__doc__ for more on format strings."); + +static PyObject * +struct_calcsize(PyObject *self, PyObject *args) +{ + char *fmt; + const formatdef *f; + int size; + + if (!PyArg_ParseTuple(args, "s:calcsize", &fmt)) + return NULL; + f = whichtable(&fmt); + size = calcsize(fmt, f); + if (size < 0) + return NULL; + return PyInt_FromLong((long)size); +} + + +PyDoc_STRVAR(pack__doc__, +"pack(fmt, v1, v2, ...) -> string\n\ +Return string containing values v1, v2, ... packed according to fmt.\n\ +See struct.__doc__ for more on format strings."); + +static PyObject * +struct_pack(PyObject *self, PyObject *args) +{ + const formatdef *f, *e; + PyObject *format, *result, *v; + char *fmt; + int size, num; + int i, n; + char *s, *res, *restart, *nres; + char c; + + if (args == NULL || !PyTuple_Check(args) || + (n = PyTuple_Size(args)) < 1) + { + PyErr_SetString(PyExc_TypeError, + "struct.pack requires at least one argument"); + return NULL; + } + format = PyTuple_GetItem(args, 0); + fmt = PyString_AsString(format); + if (!fmt) + return NULL; + f = whichtable(&fmt); + size = calcsize(fmt, f); + if (size < 0) + return NULL; + result = PyString_FromStringAndSize((char *)NULL, size); + if (result == NULL) + return NULL; + + s = fmt; + i = 1; + res = restart = PyString_AsString(result); + + while ((c = *s++) != '\0') { + if (isspace((int)c)) + continue; + if ('0' <= c && c <= '9') { + num = c - '0'; + while ('0' <= (c = *s++) && c <= '9') + num = num*10 + (c - '0'); + if (c == '\0') + break; + } + else + num = 1; + + e = getentry(c, f); + if (e == NULL) + goto fail; + nres = restart + align((int)(res-restart), c, e); + /* Fill padd bytes with zeros */ + while (res < nres) + *res++ = '\0'; + if (num == 0 && c != 's') + continue; + do { + if (c == 'x') { + /* doesn't consume arguments */ + memset(res, '\0', num); + res += num; + break; + } + if (i >= n) { + PyErr_SetString(StructError, + "insufficient arguments to pack"); + goto fail; + } + v = PyTuple_GetItem(args, i++); + if (v == NULL) + goto fail; + if (c == 's') { + /* num is string size, not repeat count */ + int n; + if (!PyString_Check(v)) { + PyErr_SetString(StructError, + "argument for 's' must be a string"); + goto fail; + } + n = PyString_Size(v); + if (n > num) + n = num; + if (n > 0) + memcpy(res, PyString_AsString(v), n); + if (n < num) + memset(res+n, '\0', num-n); + res += num; + break; + } + else if (c == 'p') { + /* num is string size + 1, + to fit in the count byte */ + int n; + num--; /* now num is max string size */ + if (!PyString_Check(v)) { + PyErr_SetString(StructError, + "argument for 'p' must be a string"); + goto fail; + } + n = PyString_Size(v); + if (n > num) + n = num; + if (n > 0) + memcpy(res+1, PyString_AsString(v), n); + if (n < num) + /* no real need, just to be nice */ + memset(res+1+n, '\0', num-n); + if (n > 255) + n = 255; + *res++ = n; /* store the length byte */ + res += num; + break; + } + else { + if (e->pack(res, v, e) < 0) + goto fail; + res += e->size; + } + } while (--num > 0); + } + + if (i < n) { + PyErr_SetString(StructError, + "too many arguments for pack format"); + goto fail; + } + + return result; + + fail: + Py_DECREF(result); + return NULL; +} + + +PyDoc_STRVAR(unpack__doc__, +"unpack(fmt, string) -> (v1, v2, ...)\n\ +Unpack the string, containing packed C structure data, according\n\ +to fmt. Requires len(string)==calcsize(fmt).\n\ +See struct.__doc__ for more on format strings."); + +static PyObject * +struct_unpack(PyObject *self, PyObject *args) +{ + const formatdef *f, *e; + char *str, *start, *fmt, *s; + char c; + int len, size, num; + PyObject *res, *v; + + if (!PyArg_ParseTuple(args, "ss#:unpack", &fmt, &start, &len)) + return NULL; + f = whichtable(&fmt); + size = calcsize(fmt, f); + if (size < 0) + return NULL; + if (size != len) { + PyErr_SetString(StructError, + "unpack str size does not match format"); + return NULL; + } + res = PyList_New(0); + if (res == NULL) + return NULL; + str = start; + s = fmt; + while ((c = *s++) != '\0') { + if (isspace((int)c)) + continue; + if ('0' <= c && c <= '9') { + num = c - '0'; + while ('0' <= (c = *s++) && c <= '9') + num = num*10 + (c - '0'); + if (c == '\0') + break; + } + else + num = 1; + + e = getentry(c, f); + if (e == NULL) + goto fail; + str = start + align((int)(str-start), c, e); + if (num == 0 && c != 's') + continue; + + do { + if (c == 'x') { + str += num; + break; + } + if (c == 's') { + /* num is string size, not repeat count */ + v = PyString_FromStringAndSize(str, num); + if (v == NULL) + goto fail; + str += num; + num = 0; + } + else if (c == 'p') { + /* num is string buffer size, + not repeat count */ + int n = *(unsigned char*)str; + /* first byte (unsigned) is string size */ + if (n >= num) + n = num-1; + v = PyString_FromStringAndSize(str+1, n); + if (v == NULL) + goto fail; + str += num; + num = 0; + } + else { + v = e->unpack(str, e); + if (v == NULL) + goto fail; + str += e->size; + } + if (v == NULL || PyList_Append(res, v) < 0) + goto fail; + Py_DECREF(v); + } while (--num > 0); + } + + v = PyList_AsTuple(res); + Py_DECREF(res); + return v; + + fail: + Py_DECREF(res); + return NULL; +} + + +/* List of functions */ + +static PyMethodDef struct_methods[] = { + {"calcsize", struct_calcsize, METH_VARARGS, calcsize__doc__}, + {"pack", struct_pack, METH_VARARGS, pack__doc__}, + {"unpack", struct_unpack, METH_VARARGS, unpack__doc__}, + {NULL, NULL} /* sentinel */ +}; + + +/* Module initialization */ + +PyMODINIT_FUNC +initstruct(void) +{ + PyObject *m; + + /* Create the module and add the functions */ + m = Py_InitModule4("struct", struct_methods, struct__doc__, + (PyObject*)NULL, PYTHON_API_VERSION); + + /* Add some symbolic constants to the module */ + if (StructError == NULL) { + StructError = PyErr_NewException("struct.error", NULL, NULL); + if (StructError == NULL) + return; + } + Py_INCREF(StructError); + PyModule_AddObject(m, "error", StructError); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sunaudiodev.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sunaudiodev.c new file mode 100644 index 00000000..90eee302 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/sunaudiodev.c @@ -0,0 +1,463 @@ + +/* Sad objects */ + +#include "Python.h" +#include "structmember.h" + +#ifdef HAVE_SYS_AUDIOIO_H +#define SOLARIS +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#include +#include +#ifdef SOLARIS +#include +#else +#include +#endif + +/* #define offsetof(str,mem) ((int)(((str *)0)->mem)) */ + +typedef struct { + PyObject_HEAD + int x_fd; /* The open file */ + int x_icount; /* # samples read */ + int x_ocount; /* # samples written */ + int x_isctl; /* True if control device */ + +} sadobject; + +typedef struct { + PyObject_HEAD + audio_info_t ai; +} sadstatusobject; + +static PyTypeObject Sadtype; +static PyTypeObject Sadstatustype; +static sadstatusobject *sads_alloc(void); /* Forward */ + +static PyObject *SunAudioError; + +#define is_sadobject(v) ((v)->ob_type == &Sadtype) +#define is_sadstatusobject(v) ((v)->ob_type == &Sadstatustype) + + +static sadobject * +newsadobject(PyObject *args) +{ + sadobject *xp; + int fd; + char *mode; + int imode; + char* basedev; + char* ctldev; + char* opendev; + + /* Check arg for r/w/rw */ + if (!PyArg_ParseTuple(args, "s", &mode)) + return NULL; + if (strcmp(mode, "r") == 0) + imode = 0; + else if (strcmp(mode, "w") == 0) + imode = 1; + else if (strcmp(mode, "rw") == 0) + imode = 2; + else if (strcmp(mode, "control") == 0) + imode = -1; + else { + PyErr_SetString(SunAudioError, + "Mode should be one of 'r', 'w', 'rw' or 'control'"); + return NULL; + } + + /* Open the correct device. The base device name comes from the + * AUDIODEV environment variable first, then /dev/audio. The + * control device tacks "ctl" onto the base device name. + */ + basedev = getenv("AUDIODEV"); + if (!basedev) + basedev = "/dev/audio"; + ctldev = PyMem_NEW(char, strlen(basedev) + 4); + if (!ctldev) { + PyErr_NoMemory(); + return NULL; + } + strcpy(ctldev, basedev); + strcat(ctldev, "ctl"); + + if (imode < 0) { + opendev = ctldev; + fd = open(ctldev, 2); + } + else { + opendev = basedev; + fd = open(basedev, imode); + } + if (fd < 0) { + PyErr_SetFromErrnoWithFilename(SunAudioError, opendev); + PyMem_DEL(ctldev); + return NULL; + } + PyMem_DEL(ctldev); + + /* Create and initialize the object */ + xp = PyObject_New(sadobject, &Sadtype); + if (xp == NULL) { + close(fd); + return NULL; + } + xp->x_fd = fd; + xp->x_icount = xp->x_ocount = 0; + xp->x_isctl = (imode < 0); + + return xp; +} + +/* Sad methods */ + +static void +sad_dealloc(sadobject *xp) +{ + close(xp->x_fd); + PyObject_Del(xp); +} + +static PyObject * +sad_read(sadobject *self, PyObject *args) +{ + int size, count; + char *cp; + PyObject *rv; + + if (!PyArg_ParseTuple(args, "i:read", &size)) + return NULL; + rv = PyString_FromStringAndSize(NULL, size); + if (rv == NULL) + return NULL; + + if (!(cp = PyString_AsString(rv))) + goto finally; + + count = read(self->x_fd, cp, size); + if (count < 0) { + PyErr_SetFromErrno(SunAudioError); + goto finally; + } +#if 0 + /* TBD: why print this message if you can handle the condition? + * assume it's debugging info which we can just as well get rid + * of. in any case this message should *not* be using printf! + */ + if (count != size) + printf("sunaudio: funny read rv %d wtd %d\n", count, size); +#endif + self->x_icount += count; + return rv; + + finally: + Py_DECREF(rv); + return NULL; +} + +static PyObject * +sad_write(sadobject *self, PyObject *args) +{ + char *cp; + int count, size; + + if (!PyArg_ParseTuple(args, "s#:write", &cp, &size)) + return NULL; + + count = write(self->x_fd, cp, size); + if (count < 0) { + PyErr_SetFromErrno(SunAudioError); + return NULL; + } +#if 0 + if (count != size) + printf("sunaudio: funny write rv %d wanted %d\n", count, size); +#endif + self->x_ocount += count; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sad_getinfo(sadobject *self) +{ + sadstatusobject *rv; + + if (!(rv = sads_alloc())) + return NULL; + + if (ioctl(self->x_fd, AUDIO_GETINFO, &rv->ai) < 0) { + PyErr_SetFromErrno(SunAudioError); + Py_DECREF(rv); + return NULL; + } + return (PyObject *)rv; +} + +static PyObject * +sad_setinfo(sadobject *self, sadstatusobject *arg) +{ + if (!is_sadstatusobject(arg)) { + PyErr_SetString(PyExc_TypeError, + "Must be sun audio status object"); + return NULL; + } + if (ioctl(self->x_fd, AUDIO_SETINFO, &arg->ai) < 0) { + PyErr_SetFromErrno(SunAudioError); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sad_ibufcount(sadobject *self) +{ + audio_info_t ai; + + if (ioctl(self->x_fd, AUDIO_GETINFO, &ai) < 0) { + PyErr_SetFromErrno(SunAudioError); + return NULL; + } + return PyInt_FromLong(ai.record.samples - self->x_icount); +} + +static PyObject * +sad_obufcount(sadobject *self) +{ + audio_info_t ai; + + if (ioctl(self->x_fd, AUDIO_GETINFO, &ai) < 0) { + PyErr_SetFromErrno(SunAudioError); + return NULL; + } + /* x_ocount is in bytes, whereas play.samples is in frames */ + /* we want frames */ + return PyInt_FromLong(self->x_ocount / (ai.play.channels * + ai.play.precision / 8) - + ai.play.samples); +} + +static PyObject * +sad_drain(sadobject *self) +{ + if (ioctl(self->x_fd, AUDIO_DRAIN, 0) < 0) { + PyErr_SetFromErrno(SunAudioError); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef SOLARIS +static PyObject * +sad_getdev(sadobject *self) +{ + struct audio_device ad; + + if (ioctl(self->x_fd, AUDIO_GETDEV, &ad) < 0) { + PyErr_SetFromErrno(SunAudioError); + return NULL; + } + return Py_BuildValue("(sss)", ad.name, ad.version, ad.config); +} +#endif + +static PyObject * +sad_flush(sadobject *self) +{ + if (ioctl(self->x_fd, I_FLUSH, FLUSHW) < 0) { + PyErr_SetFromErrno(SunAudioError); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sad_close(sadobject *self) +{ + + if (self->x_fd >= 0) { + close(self->x_fd); + self->x_fd = -1; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sad_fileno(sadobject *self) +{ + return PyInt_FromLong(self->x_fd); +} + + +static PyMethodDef sad_methods[] = { + { "read", (PyCFunction)sad_read, METH_VARARGS }, + { "write", (PyCFunction)sad_write, METH_VARARGS }, + { "ibufcount", (PyCFunction)sad_ibufcount, METH_NOARGS }, + { "obufcount", (PyCFunction)sad_obufcount, METH_NOARGS }, +#define CTL_METHODS 4 + { "getinfo", (PyCFunction)sad_getinfo, METH_NOARGS }, + { "setinfo", (PyCFunction)sad_setinfo, METH_O}, + { "drain", (PyCFunction)sad_drain, METH_NOARGS }, + { "flush", (PyCFunction)sad_flush, METH_NOARGS }, +#ifdef SOLARIS + { "getdev", (PyCFunction)sad_getdev, METH_NOARGS }, +#endif + { "close", (PyCFunction)sad_close, METH_NOARGS }, + { "fileno", (PyCFunction)sad_fileno, METH_NOARGS }, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +sad_getattr(sadobject *xp, char *name) +{ + if (xp->x_isctl) + return Py_FindMethod(sad_methods+CTL_METHODS, + (PyObject *)xp, name); + else + return Py_FindMethod(sad_methods, (PyObject *)xp, name); +} + +/* ----------------------------------------------------------------- */ + +static sadstatusobject * +sads_alloc(void) { + return PyObject_New(sadstatusobject, &Sadstatustype); +} + +static void +sads_dealloc(sadstatusobject *xp) +{ + PyMem_DEL(xp); +} + +#define OFF(x) offsetof(audio_info_t,x) +static struct memberlist sads_ml[] = { + { "i_sample_rate", T_UINT, OFF(record.sample_rate) }, + { "i_channels", T_UINT, OFF(record.channels) }, + { "i_precision", T_UINT, OFF(record.precision) }, + { "i_encoding", T_UINT, OFF(record.encoding) }, + { "i_gain", T_UINT, OFF(record.gain) }, + { "i_port", T_UINT, OFF(record.port) }, + { "i_samples", T_UINT, OFF(record.samples) }, + { "i_eof", T_UINT, OFF(record.eof) }, + { "i_pause", T_UBYTE, OFF(record.pause) }, + { "i_error", T_UBYTE, OFF(record.error) }, + { "i_waiting", T_UBYTE, OFF(record.waiting) }, + { "i_open", T_UBYTE, OFF(record.open) , RO}, + { "i_active", T_UBYTE, OFF(record.active) , RO}, +#ifdef SOLARIS + { "i_buffer_size", T_UINT, OFF(record.buffer_size) }, + { "i_balance", T_UBYTE, OFF(record.balance) }, + { "i_avail_ports", T_UINT, OFF(record.avail_ports) }, +#endif + + { "o_sample_rate", T_UINT, OFF(play.sample_rate) }, + { "o_channels", T_UINT, OFF(play.channels) }, + { "o_precision", T_UINT, OFF(play.precision) }, + { "o_encoding", T_UINT, OFF(play.encoding) }, + { "o_gain", T_UINT, OFF(play.gain) }, + { "o_port", T_UINT, OFF(play.port) }, + { "o_samples", T_UINT, OFF(play.samples) }, + { "o_eof", T_UINT, OFF(play.eof) }, + { "o_pause", T_UBYTE, OFF(play.pause) }, + { "o_error", T_UBYTE, OFF(play.error) }, + { "o_waiting", T_UBYTE, OFF(play.waiting) }, + { "o_open", T_UBYTE, OFF(play.open) , RO}, + { "o_active", T_UBYTE, OFF(play.active) , RO}, +#ifdef SOLARIS + { "o_buffer_size", T_UINT, OFF(play.buffer_size) }, + { "o_balance", T_UBYTE, OFF(play.balance) }, + { "o_avail_ports", T_UINT, OFF(play.avail_ports) }, +#endif + + { "monitor_gain", T_UINT, OFF(monitor_gain) }, + { NULL, 0, 0}, +}; + +static PyObject * +sads_getattr(sadstatusobject *xp, char *name) +{ + return PyMember_Get((char *)&xp->ai, sads_ml, name); +} + +static int +sads_setattr(sadstatusobject *xp, char *name, PyObject *v) +{ + + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, + "can't delete sun audio status attributes"); + return -1; + } + return PyMember_Set((char *)&xp->ai, sads_ml, name, v); +} + +/* ------------------------------------------------------------------- */ + + +static PyTypeObject Sadtype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "sunaudiodev.sun_audio_device", /*tp_name*/ + sizeof(sadobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)sad_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)sad_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + +static PyTypeObject Sadstatustype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "sunaudiodev.sun_audio_device_status", /*tp_name*/ + sizeof(sadstatusobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)sads_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)sads_getattr, /*tp_getattr*/ + (setattrfunc)sads_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; +/* ------------------------------------------------------------------- */ + +static PyObject * +sadopen(PyObject *self, PyObject *args) +{ + return (PyObject *)newsadobject(args); +} + +static PyMethodDef sunaudiodev_methods[] = { + { "open", sadopen, METH_VARARGS }, + { 0, 0 }, +}; + +void +initsunaudiodev(void) +{ + PyObject *m, *d; + + m = Py_InitModule("sunaudiodev", sunaudiodev_methods); + d = PyModule_GetDict(m); + SunAudioError = PyErr_NewException("sunaudiodev.error", NULL, NULL); + if (SunAudioError) + PyDict_SetItemString(d, "error", SunAudioError); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/svmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/svmodule.c new file mode 100644 index 00000000..303504dc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/svmodule.c @@ -0,0 +1,964 @@ +/* SV module -- interface to the Indigo video board */ + +/* WARNING! This module is for hardware that we don't have any more, + so it hasn't been tested. It has been converted to the new coding + style, and it is possible that this conversion has broken something + -- user beware! */ + +#include +#include +#include "Python.h" +#include "compile.h" +#include "yuv.h" /* for YUV conversion functions */ + +typedef struct { + PyObject_HEAD + SV_nodeP ob_svideo; + svCaptureInfo ob_info; +} svobject; + +typedef struct { + PyObject_HEAD + void *ob_capture; + int ob_mustunlock; + svCaptureInfo ob_info; + svobject *ob_svideo; +} captureobject; + +static PyObject *SvError; /* exception sv.error */ + +static PyObject *newcaptureobject(svobject *, void *, int); + +/* Set a SV-specific error from svideo_errno and return NULL */ +static PyObject * +sv_error(void) +{ + PyErr_SetString(SvError, svStrerror(svideo_errno)); + return NULL; +} + +static PyObject * +svc_conversion(captureobject *self, PyObject *args, void (*function)(), float factor) +{ + PyObject *output; + int invert; + char* outstr; + + if (!PyArg_Parse(args, "i", &invert)) + return NULL; + + if (!(output = PyString_FromStringAndSize( + NULL, + (int)(self->ob_info.width * self->ob_info.height * factor)))) + { + return NULL; + } + if (!(outstr = PyString_AsString(output))) { + Py_DECREF(output); + return NULL; + } + + (*function)((boolean)invert, self->ob_capture, + outstr, + self->ob_info.width, self->ob_info.height); + + return output; +} + +/* + * 3 functions to convert from Starter Video YUV 4:1:1 format to + * Compression Library 4:2:2 Duplicate Chroma format. + */ +static PyObject * +svc_YUVtoYUV422DC(captureobject *self, PyObject *args) +{ + if (self->ob_info.format != SV_YUV411_FRAMES) { + PyErr_SetString(SvError, "data has bad format"); + return NULL; + } + return svc_conversion(self, args, yuv_sv411_to_cl422dc, 2.0); +} + +static PyObject * +svc_YUVtoYUV422DC_quarter(captureobject *self, PyObject *args) +{ + if (self->ob_info.format != SV_YUV411_FRAMES) { + PyErr_SetString(SvError, "data has bad format"); + return NULL; + } + return svc_conversion(self, args, + yuv_sv411_to_cl422dc_quartersize, 0.5); +} + +static PyObject * +svc_YUVtoYUV422DC_sixteenth(captureobject *self, PyObject *args) +{ + if (self->ob_info.format != SV_YUV411_FRAMES) { + PyErr_SetString(SvError, "data has bad format"); + return NULL; + } + return svc_conversion(self, args, + yuv_sv411_to_cl422dc_sixteenthsize, 0.125); +} + +static PyObject * +svc_YUVtoRGB(captureobject *self, PyObject *args) +{ + switch (self->ob_info.format) { + case SV_YUV411_FRAMES: + case SV_YUV411_FRAMES_AND_BLANKING_BUFFER: + break; + default: + PyErr_SetString(SvError, "data had bad format"); + return NULL; + } + return svc_conversion(self, args, svYUVtoRGB, (float) sizeof(long)); +} + +static PyObject * +svc_RGB8toRGB32(captureobject *self, PyObject *args) +{ + if (self->ob_info.format != SV_RGB8_FRAMES) { + PyErr_SetString(SvError, "data has bad format"); + return NULL; + } + return svc_conversion(self, args, svRGB8toRGB32, (float) sizeof(long)); +} + +static PyObject * +svc_InterleaveFields(captureobject *self, PyObject *args) +{ + if (self->ob_info.format != SV_RGB8_FRAMES) { + PyErr_SetString(SvError, "data has bad format"); + return NULL; + } + return svc_conversion(self, args, svInterleaveFields, 1.0); +} + +static PyObject * +svc_GetFields(captureobject *self, PyObject *args) +{ + PyObject *f1 = NULL; + PyObject *f2 = NULL; + PyObject *ret = NULL; + int fieldsize; + char* obcapture; + + if (self->ob_info.format != SV_RGB8_FRAMES) { + PyErr_SetString(SvError, "data has bad format"); + return NULL; + } + + fieldsize = self->ob_info.width * self->ob_info.height / 2; + obcapture = (char*)self->ob_capture; + + if (!(f1 = PyString_FromStringAndSize(obcapture, fieldsize))) + goto finally; + if (!(f2 = PyString_FromStringAndSize(obcapture + fieldsize, + fieldsize))) + goto finally; + ret = Py_BuildValue("(OO)", f1, f2); + + finally: + Py_XDECREF(f1); + Py_XDECREF(f2); + return ret; +} + +static PyObject * +svc_UnlockCaptureData(captureobject *self, PyObject *args) +{ + if (!PyArg_Parse(args, "")) + return NULL; + + if (!self->ob_mustunlock) { + PyErr_SetString(SvError, "buffer should not be unlocked"); + return NULL; + } + + if (svUnlockCaptureData(self->ob_svideo->ob_svideo, self->ob_capture)) + return sv_error(); + + self->ob_mustunlock = 0; + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef USE_GL +#include + +static PyObject * +svc_lrectwrite(captureobject *self, PyObject *args) +{ + Screencoord x1, x2, y1, y2; + + if (!PyArg_Parse(args, "(hhhh)", &x1, &x2, &y1, &y2)) + return NULL; + + lrectwrite(x1, x2, y1, y2, (unsigned long *) self->ob_capture); + + Py_INCREF(Py_None); + return Py_None; +} +#endif + +static PyObject * +svc_writefile(captureobject *self, PyObject *args) +{ + PyObject *file; + int size; + FILE* fp; + + if (!PyArg_Parse(args, "O", &file)) + return NULL; + + if (!PyFile_Check(file)) { + PyErr_SetString(SvError, "not a file object"); + return NULL; + } + + if (!(fp = PyFile_AsFile(file))) + return NULL; + + size = self->ob_info.width * self->ob_info.height; + + if (fwrite(self->ob_capture, sizeof(long), size, fp) != size) { + PyErr_SetString(SvError, "writing failed"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +svc_FindVisibleRegion(captureobject *self, PyObject *args) +{ + void *visible; + int width; + + if (!PyArg_Parse(args, "")) + return NULL; + + if (svFindVisibleRegion(self->ob_svideo->ob_svideo, + self->ob_capture, &visible, + self->ob_info.width)) + return sv_error(); + + if (visible == NULL) { + PyErr_SetString(SvError, "data in wrong format"); + return NULL; + } + + return newcaptureobject(self->ob_svideo, visible, 0); +} + +static PyMethodDef capture_methods[] = { + {"YUVtoRGB", (PyCFunction)svc_YUVtoRGB, METH_OLDARGS}, + {"RGB8toRGB32", (PyCFunction)svc_RGB8toRGB32, METH_OLDARGS}, + {"InterleaveFields", (PyCFunction)svc_InterleaveFields, METH_OLDARGS}, + {"UnlockCaptureData", (PyCFunction)svc_UnlockCaptureData, METH_OLDARGS}, + {"FindVisibleRegion", (PyCFunction)svc_FindVisibleRegion, METH_OLDARGS}, + {"GetFields", (PyCFunction)svc_GetFields, METH_OLDARGS}, + {"YUVtoYUV422DC", (PyCFunction)svc_YUVtoYUV422DC, METH_OLDARGS}, + {"YUVtoYUV422DC_quarter",(PyCFunction)svc_YUVtoYUV422DC_quarter, METH_OLDARGS}, + {"YUVtoYUV422DC_sixteenth",(PyCFunction)svc_YUVtoYUV422DC_sixteenth, METH_OLDARGS}, +#ifdef USE_GL + {"lrectwrite", (PyCFunction)svc_lrectwrite, METH_OLDARGS}, +#endif + {"writefile", (PyCFunction)svc_writefile, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +static void +capture_dealloc(captureobject *self) +{ + if (self->ob_capture != NULL) { + if (self->ob_mustunlock) + (void)svUnlockCaptureData(self->ob_svideo->ob_svideo, + self->ob_capture); + self->ob_capture = NULL; + Py_DECREF(self->ob_svideo); + self->ob_svideo = NULL; + } + PyObject_Del(self); +} + +static PyObject * +capture_getattr(svobject *self, char *name) +{ + return Py_FindMethod(capture_methods, (PyObject *)self, name); +} + +PyTypeObject Capturetype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "sv.capture", /*tp_name*/ + sizeof(captureobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)capture_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)capture_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + +static PyObject * +newcaptureobject(svobject *self, void *ptr, int mustunlock) +{ + captureobject *p; + + p = PyObject_New(captureobject, &Capturetype); + if (p == NULL) + return NULL; + p->ob_svideo = self; + Py_INCREF(self); + p->ob_capture = ptr; + p->ob_mustunlock = mustunlock; + p->ob_info = self->ob_info; + return (PyObject *) p; +} + +static PyObject * +sv_GetCaptureData(svobject *self, PyObject *args) +{ + void *ptr; + long fieldID; + PyObject *res, *c; + + if (!PyArg_Parse(args, "")) + return NULL; + + if (svGetCaptureData(self->ob_svideo, &ptr, &fieldID)) + return sv_error(); + + if (ptr == NULL) { + PyErr_SetString(SvError, "no data available"); + return NULL; + } + + c = newcaptureobject(self, ptr, 1); + if (c == NULL) + return NULL; + res = Py_BuildValue("(Oi)", c, fieldID); + Py_DECREF(c); + return res; +} + +static PyObject * +sv_BindGLWindow(svobject *self, PyObject *args) +{ + long wid; + int mode; + + if (!PyArg_Parse(args, "(ii)", &wid, &mode)) + return NULL; + + if (svBindGLWindow(self->ob_svideo, wid, mode)) + return sv_error(); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sv_EndContinuousCapture(svobject *self, PyObject *args) +{ + + if (!PyArg_Parse(args, "")) + return NULL; + + if (svEndContinuousCapture(self->ob_svideo)) + return sv_error(); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sv_IsVideoDisplayed(svobject *self, PyObject *args) +{ + int v; + + if (!PyArg_Parse(args, "")) + return NULL; + + v = svIsVideoDisplayed(self->ob_svideo); + if (v == -1) + return sv_error(); + + return PyInt_FromLong((long) v); +} + +static PyObject * +sv_OutputOffset(svobject *self, PyObject *args) +{ + int x_offset; + int y_offset; + + if (!PyArg_Parse(args, "(ii)", &x_offset, &y_offset)) + return NULL; + + if (svOutputOffset(self->ob_svideo, x_offset, y_offset)) + return sv_error(); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sv_PutFrame(svobject *self, PyObject *args) +{ + char *buffer; + + if (!PyArg_Parse(args, "s", &buffer)) + return NULL; + + if (svPutFrame(self->ob_svideo, buffer)) + return sv_error(); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sv_QuerySize(svobject *self, PyObject *args) +{ + int w; + int h; + int rw; + int rh; + + if (!PyArg_Parse(args, "(ii)", &w, &h)) + return NULL; + + if (svQuerySize(self->ob_svideo, w, h, &rw, &rh)) + return sv_error(); + + return Py_BuildValue("(ii)", (long) rw, (long) rh); +} + +static PyObject * +sv_SetSize(svobject *self, PyObject *args) +{ + int w; + int h; + + if (!PyArg_Parse(args, "(ii)", &w, &h)) + return NULL; + + if (svSetSize(self->ob_svideo, w, h)) + return sv_error(); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sv_SetStdDefaults(svobject *self, PyObject *args) +{ + + if (!PyArg_Parse(args, "")) + return NULL; + + if (svSetStdDefaults(self->ob_svideo)) + return sv_error(); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sv_UseExclusive(svobject *self, PyObject *args) +{ + boolean onoff; + int mode; + + if (!PyArg_Parse(args, "(ii)", &onoff, &mode)) + return NULL; + + if (svUseExclusive(self->ob_svideo, onoff, mode)) + return sv_error(); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sv_WindowOffset(svobject *self, PyObject *args) +{ + int x_offset; + int y_offset; + + if (!PyArg_Parse(args, "(ii)", &x_offset, &y_offset)) + return NULL; + + if (svWindowOffset(self->ob_svideo, x_offset, y_offset)) + return sv_error(); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sv_CaptureBurst(svobject *self, PyObject *args) +{ + int bytes, i; + svCaptureInfo info; + void *bitvector = NULL; + PyObject *videodata = NULL; + PyObject *bitvecobj = NULL; + PyObject *res = NULL; + static PyObject *evenitem, *odditem; + + if (!PyArg_Parse(args, "(iiiii)", &info.format, + &info.width, &info.height, + &info.size, &info.samplingrate)) + return NULL; + + switch (info.format) { + case SV_RGB8_FRAMES: + bitvector = malloc(SV_BITVEC_SIZE(info.size)); + break; + case SV_YUV411_FRAMES_AND_BLANKING_BUFFER: + break; + default: + PyErr_SetString(SvError, "illegal format specified"); + return NULL; + } + + if (svQueryCaptureBufferSize(self->ob_svideo, &info, &bytes)) { + res = sv_error(); + goto finally; + } + + if (!(videodata = PyString_FromStringAndSize(NULL, bytes))) + goto finally; + + /* XXX -- need to do something about the bitvector */ + { + char* str = PyString_AsString(videodata); + if (!str) + goto finally; + + if (svCaptureBurst(self->ob_svideo, &info, str, bitvector)) { + res = sv_error(); + goto finally; + } + } + + if (bitvector) { + if (evenitem == NULL) { + if (!(evenitem = PyInt_FromLong(0))) + goto finally; + } + if (odditem == NULL) { + if (!(odditem = PyInt_FromLong(1))) + goto finally; + } + if (!(bitvecobj = PyTuple_New(2 * info.size))) + goto finally; + + for (i = 0; i < 2 * info.size; i++) { + int sts; + + if (SV_GET_FIELD(bitvector, i) == SV_EVEN_FIELD) { + Py_INCREF(evenitem); + sts = PyTuple_SetItem(bitvecobj, i, evenitem); + } else { + Py_INCREF(odditem); + sts = PyTuple_SetItem(bitvecobj, i, odditem); + } + if (sts < 0) + goto finally; + } + } else { + bitvecobj = Py_None; + Py_INCREF(Py_None); + } + + res = Py_BuildValue("((iiiii)OO)", info.format, + info.width, info.height, + info.size, info.samplingrate, + videodata, bitvecobj); + + finally: + if (bitvector) + free(bitvector); + + Py_XDECREF(videodata); + Py_XDECREF(bitvecobj); + return res; +} + +static PyObject * +sv_CaptureOneFrame(svobject *self, PyObject *args) +{ + svCaptureInfo info; + int format, width, height; + int bytes; + PyObject *videodata = NULL; + PyObject *res = NULL; + char *str; + + if (!PyArg_Parse(args, "(iii)", &format, &width, &height)) + return NULL; + + info.format = format; + info.width = width; + info.height = height; + info.size = 0; + info.samplingrate = 0; + if (svQueryCaptureBufferSize(self->ob_svideo, &info, &bytes)) + return sv_error(); + + if (!(videodata = PyString_FromStringAndSize(NULL, bytes))) + return NULL; + + str = PyString_AsString(videodata); + if (!str) + goto finally; + + if (svCaptureOneFrame(self->ob_svideo, format, &width, &height, str)) { + res = sv_error(); + goto finally; + } + + res = Py_BuildValue("(iiO)", width, height, videodata); + + finally: + Py_XDECREF(videodata); + return res; +} + +static PyObject * +sv_InitContinuousCapture(svobject *self, PyObject *args) +{ + svCaptureInfo info; + + if (!PyArg_Parse(args, "(iiiii)", &info.format, + &info.width, &info.height, + &info.size, &info.samplingrate)) + return NULL; + + if (svInitContinuousCapture(self->ob_svideo, &info)) + return sv_error(); + + self->ob_info = info; + + return Py_BuildValue("(iiiii)", info.format, info.width, info.height, + info.size, info.samplingrate); +} + +static PyObject * +sv_LoadMap(svobject *self, PyObject *args) +{ + PyObject *rgb; + PyObject *res = NULL; + rgb_tuple *mapp = NULL; + int maptype; + int i, j; /* indices */ + + if (!PyArg_Parse(args, "(iO)", &maptype, &rgb)) + return NULL; + + if (!PyList_Check(rgb) || PyList_Size(rgb) != 256) { + PyErr_BadArgument(); + return NULL; + } + + if (!(mapp = PyMem_NEW(rgb_tuple, 256))) + return PyErr_NoMemory(); + + for (i = 0; i < 256; i++) { + PyObject* v = PyList_GetItem(rgb, i); + if (!v) + goto finally; + + if (!PyTuple_Check(v) || PyTuple_Size(v) != 3) { + PyErr_BadArgument(); + goto finally; + } + for (j = 0; j < 3; j++) { + PyObject* cell = PyTuple_GetItem(v, j); + if (!cell) + goto finally; + + if (!PyInt_Check(cell)) { + PyErr_BadArgument(); + goto finally; + } + switch (j) { + case 0: mapp[i].red = PyInt_AsLong(cell); break; + case 1: mapp[i].blue = PyInt_AsLong(cell); break; + case 2: mapp[i].green = PyInt_AsLong(cell); break; + } + if (PyErr_Occurred()) + goto finally; + } + } + + if (svLoadMap(self->ob_svideo, maptype, mapp)) { + res = sv_error(); + goto finally; + } + + Py_INCREF(Py_None); + res = Py_None; + + finally: + PyMem_DEL(mapp); + return res; +} + +static PyObject * +sv_CloseVideo(svobject *self, PyObject *args) +{ + if (!PyArg_Parse(args, "")) + return NULL; + + if (svCloseVideo(self->ob_svideo)) + return sv_error(); + + self->ob_svideo = NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +doParams(svobject *self, PyObject *args, + int (*func)(SV_nodeP, long *, int), int modified) +{ + PyObject *list; + PyObject *res = NULL; + long *PVbuffer = NULL; + long length; + int i; + + if (!PyArg_Parse(args, "O", &list)) + return NULL; + + if (!PyList_Check(list)) { + PyErr_BadArgument(); + return NULL; + } + + if ((length = PyList_Size(list)) < 0) + return NULL; + + PVbuffer = PyMem_NEW(long, length); + if (PVbuffer == NULL) + return PyErr_NoMemory(); + + for (i = 0; i < length; i++) { + PyObject *v = PyList_GetItem(list, i); + if (!v) + goto finally; + + if (!PyInt_Check(v)) { + PyErr_BadArgument(); + goto finally; + } + PVbuffer[i] = PyInt_AsLong(v); + /* can't just test the return value, because what if the + value was -1?! + */ + if (PVbuffer[i] == -1 && PyErr_Occurred()) + goto finally; + } + + if ((*func)(self->ob_svideo, PVbuffer, length)) { + res = sv_error(); + goto finally; + } + + if (modified) { + for (i = 0; i < length; i++) { + PyObject* v = PyInt_FromLong(PVbuffer[i]); + if (!v || PyList_SetItem(list, i, v) < 0) + goto finally; + } + } + + Py_INCREF(Py_None); + res = Py_None; + + finally: + PyMem_DEL(PVbuffer); + return res; +} + +static PyObject * +sv_GetParam(PyObject *self, PyObject *args) +{ + return doParams(self, args, svGetParam, 1); +} + +static PyObject * +sv_GetParamRange(PyObject *self, PyObject *args) +{ + return doParams(self, args, svGetParamRange, 1); +} + +static PyObject * +sv_SetParam(PyObject *self, PyObject *args) +{ + return doParams(self, args, svSetParam, 0); +} + +static PyMethodDef svideo_methods[] = { + {"BindGLWindow", (PyCFunction)sv_BindGLWindow, METH_OLDARGS}, + {"EndContinuousCapture",(PyCFunction)sv_EndContinuousCapture, METH_OLDARGS}, + {"IsVideoDisplayed", (PyCFunction)sv_IsVideoDisplayed, METH_OLDARGS}, + {"OutputOffset", (PyCFunction)sv_OutputOffset, METH_OLDARGS}, + {"PutFrame", (PyCFunction)sv_PutFrame, METH_OLDARGS}, + {"QuerySize", (PyCFunction)sv_QuerySize, METH_OLDARGS}, + {"SetSize", (PyCFunction)sv_SetSize, METH_OLDARGS}, + {"SetStdDefaults", (PyCFunction)sv_SetStdDefaults, METH_OLDARGS}, + {"UseExclusive", (PyCFunction)sv_UseExclusive, METH_OLDARGS}, + {"WindowOffset", (PyCFunction)sv_WindowOffset, METH_OLDARGS}, + {"InitContinuousCapture",(PyCFunction)sv_InitContinuousCapture, METH_OLDARGS}, + {"CaptureBurst", (PyCFunction)sv_CaptureBurst, METH_OLDARGS}, + {"CaptureOneFrame", (PyCFunction)sv_CaptureOneFrame, METH_OLDARGS}, + {"GetCaptureData", (PyCFunction)sv_GetCaptureData, METH_OLDARGS}, + {"CloseVideo", (PyCFunction)sv_CloseVideo, METH_OLDARGS}, + {"LoadMap", (PyCFunction)sv_LoadMap, METH_OLDARGS}, + {"GetParam", (PyCFunction)sv_GetParam, METH_OLDARGS}, + {"GetParamRange", (PyCFunction)sv_GetParamRange, METH_OLDARGS}, + {"SetParam", (PyCFunction)sv_SetParam, METH_OLDARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +sv_conversion(PyObject *self, PyObject *args, void (*function)(), + int inputfactor, float factor) +{ + int invert, width, height, inputlength; + char *input, *str; + PyObject *output; + + if (!PyArg_Parse(args, "(is#ii)", &invert, + &input, &inputlength, &width, &height)) + return NULL; + + if (width * height * inputfactor > inputlength) { + PyErr_SetString(SvError, "input buffer not long enough"); + return NULL; + } + + if (!(output = PyString_FromStringAndSize(NULL, + (int)(width * height * factor)))) + return NULL; + + str = PyString_AsString(output); + if (!str) { + Py_DECREF(output); + return NULL; + } + (*function)(invert, input, str, width, height); + + return output; +} + +static PyObject * +sv_InterleaveFields(PyObject *self, PyObject *args) +{ + return sv_conversion(self, args, svInterleaveFields, 1, 1.0); +} + +static PyObject * +sv_RGB8toRGB32(PyObject *self, PyObject *args) +{ + return sv_conversion(self, args, svRGB8toRGB32, 1, (float) sizeof(long)); +} + +static PyObject * +sv_YUVtoRGB(PyObject *self, PyObject *args) +{ + return sv_conversion(self, args, svYUVtoRGB, 2, (float) sizeof(long)); +} + +static void +svideo_dealloc(svobject *self) +{ + if (self->ob_svideo != NULL) + (void) svCloseVideo(self->ob_svideo); + PyObject_Del(self); +} + +static PyObject * +svideo_getattr(svobject *self, char *name) +{ + return Py_FindMethod(svideo_methods, (PyObject *)self, name); +} + +PyTypeObject Svtype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "sv.sv", /*tp_name*/ + sizeof(svobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)svideo_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)svideo_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + +static PyObject * +newsvobject(SV_nodeP svp) +{ + svobject *p; + + p = PyObject_New(svobject, &Svtype); + if (p == NULL) + return NULL; + p->ob_svideo = svp; + p->ob_info.format = 0; + p->ob_info.size = 0; + p->ob_info.width = 0; + p->ob_info.height = 0; + p->ob_info.samplingrate = 0; + return (PyObject *) p; +} + +static PyObject * +sv_OpenVideo(PyObject *self, PyObject *args) +{ + SV_nodeP svp; + + if (!PyArg_Parse(args, "")) + return NULL; + + svp = svOpenVideo(); + if (svp == NULL) + return sv_error(); + + return newsvobject(svp); +} + +static PyMethodDef sv_methods[] = { + {"InterleaveFields", (PyCFunction)sv_InterleaveFields, METH_OLDARGS}, + {"RGB8toRGB32", (PyCFunction)sv_RGB8toRGB32, METH_OLDARGS}, + {"YUVtoRGB", (PyCFunction)sv_YUVtoRGB, METH_OLDARGS}, + {"OpenVideo", (PyCFunction)sv_OpenVideo, METH_OLDARGS}, + {NULL, NULL} /* Sentinel */ +}; + +void +initsv(void) +{ + PyObject *m, *d; + + m = Py_InitModule("sv", sv_methods); + d = PyModule_GetDict(m); + + SvError = PyErr_NewException("sv.error", NULL, NULL); + if (SvError == NULL || PyDict_SetItemString(d, "error", SvError) != 0) + return; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/symtablemodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/symtablemodule.c new file mode 100644 index 00000000..fd613943 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/symtablemodule.c @@ -0,0 +1,79 @@ +#include "Python.h" + +#include "compile.h" +#include "symtable.h" + +static PyObject * +symtable_symtable(PyObject *self, PyObject *args) +{ + struct symtable *st; + PyObject *t; + + char *str; + char *filename; + char *startstr; + int start; + + if (!PyArg_ParseTuple(args, "sss:symtable", &str, &filename, + &startstr)) + return NULL; + if (strcmp(startstr, "exec") == 0) + start = Py_file_input; + else if (strcmp(startstr, "eval") == 0) + start = Py_eval_input; + else if (strcmp(startstr, "single") == 0) + start = Py_single_input; + else { + PyErr_SetString(PyExc_ValueError, + "symtable() arg 3 must be 'exec' or 'eval' or 'single'"); + return NULL; + } + st = Py_SymtableString(str, filename, start); + if (st == NULL) + return NULL; + t = Py_BuildValue("O", st->st_symbols); + PyMem_Free((void *)st->st_future); + PySymtable_Free(st); + return t; +} + +static PyMethodDef symtable_methods[] = { + {"symtable", symtable_symtable, METH_VARARGS, + PyDoc_STR("Return symbol and scope dictionaries" + " used internally by compiler.")}, + {NULL, NULL} /* sentinel */ +}; + +PyMODINIT_FUNC +init_symtable(void) +{ + PyObject *m; + + m = Py_InitModule("_symtable", symtable_methods); + PyModule_AddIntConstant(m, "USE", USE); + PyModule_AddIntConstant(m, "DEF_GLOBAL", DEF_GLOBAL); + PyModule_AddIntConstant(m, "DEF_LOCAL", DEF_LOCAL); + PyModule_AddIntConstant(m, "DEF_PARAM", DEF_PARAM); + PyModule_AddIntConstant(m, "DEF_STAR", DEF_STAR); + PyModule_AddIntConstant(m, "DEF_DOUBLESTAR", DEF_DOUBLESTAR); + PyModule_AddIntConstant(m, "DEF_INTUPLE", DEF_INTUPLE); + PyModule_AddIntConstant(m, "DEF_FREE", DEF_FREE); + PyModule_AddIntConstant(m, "DEF_FREE_GLOBAL", DEF_FREE_GLOBAL); + PyModule_AddIntConstant(m, "DEF_FREE_CLASS", DEF_FREE_CLASS); + PyModule_AddIntConstant(m, "DEF_IMPORT", DEF_IMPORT); + PyModule_AddIntConstant(m, "DEF_BOUND", DEF_BOUND); + + PyModule_AddIntConstant(m, "TYPE_FUNCTION", TYPE_FUNCTION); + PyModule_AddIntConstant(m, "TYPE_CLASS", TYPE_CLASS); + PyModule_AddIntConstant(m, "TYPE_MODULE", TYPE_MODULE); + + PyModule_AddIntConstant(m, "OPT_IMPORT_STAR", OPT_IMPORT_STAR); + PyModule_AddIntConstant(m, "OPT_EXEC", OPT_EXEC); + PyModule_AddIntConstant(m, "OPT_BARE_EXEC", OPT_BARE_EXEC); + + PyModule_AddIntConstant(m, "LOCAL", LOCAL); + PyModule_AddIntConstant(m, "GLOBAL_EXPLICIT", GLOBAL_EXPLICIT); + PyModule_AddIntConstant(m, "GLOBAL_IMPLICIT", GLOBAL_IMPLICIT); + PyModule_AddIntConstant(m, "FREE", FREE); + PyModule_AddIntConstant(m, "CELL", CELL); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/syslogmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/syslogmodule.c new file mode 100644 index 00000000..97b7b1c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/syslogmodule.c @@ -0,0 +1,222 @@ +/*********************************************************** +Copyright 1994 by Lance Ellinghouse, +Cathedral City, California Republic, United States of America. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Lance Ellinghouse +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +LANCE ELLINGHOUSE DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL LANCE ELLINGHOUSE BE LIABLE FOR ANY SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/****************************************************************** + +Revision history: + +1998/04/28 (Sean Reifschneider) + - When facility not specified to syslog() method, use default from openlog() + (This is how it was claimed to work in the documentation) + - Potential resource leak of o_ident, now cleaned up in closelog() + - Minor comment accuracy fix. + +95/06/29 (Steve Clift) + - Changed arg parsing to use PyArg_ParseTuple. + - Added PyErr_Clear() call(s) where needed. + - Fix core dumps if user message contains format specifiers. + - Change openlog arg defaults to match normal syslog behavior. + - Plug memory leak in openlog(). + - Fix setlogmask() to return previous mask value. + +******************************************************************/ + +/* syslog module */ + +#include "Python.h" + +#include + +/* only one instance, only one syslog, so globals should be ok */ +static PyObject *S_ident_o = NULL; /* identifier, held by openlog() */ + + +static PyObject * +syslog_openlog(PyObject * self, PyObject * args) +{ + long logopt = 0; + long facility = LOG_USER; + + + Py_XDECREF(S_ident_o); + if (!PyArg_ParseTuple(args, + "S|ll;ident string [, logoption [, facility]]", + &S_ident_o, &logopt, &facility)) + return NULL; + + /* This is needed because openlog() does NOT make a copy + * and syslog() later uses it.. cannot trash it. + */ + Py_INCREF(S_ident_o); + + openlog(PyString_AsString(S_ident_o), logopt, facility); + + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +syslog_syslog(PyObject * self, PyObject * args) +{ + char *message; + int priority = LOG_INFO; + + if (!PyArg_ParseTuple(args, "is;[priority,] message string", + &priority, &message)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "s;[priority,] message string", + &message)) + return NULL; + } + + syslog(priority, "%s", message); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +syslog_closelog(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":closelog")) + return NULL; + closelog(); + Py_XDECREF(S_ident_o); + S_ident_o = NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +syslog_setlogmask(PyObject *self, PyObject *args) +{ + long maskpri, omaskpri; + + if (!PyArg_ParseTuple(args, "l;mask for priority", &maskpri)) + return NULL; + omaskpri = setlogmask(maskpri); + return PyInt_FromLong(omaskpri); +} + +static PyObject * +syslog_log_mask(PyObject *self, PyObject *args) +{ + long mask; + long pri; + if (!PyArg_ParseTuple(args, "l:LOG_MASK", &pri)) + return NULL; + mask = LOG_MASK(pri); + return PyInt_FromLong(mask); +} + +static PyObject * +syslog_log_upto(PyObject *self, PyObject *args) +{ + long mask; + long pri; + if (!PyArg_ParseTuple(args, "l:LOG_UPTO", &pri)) + return NULL; + mask = LOG_UPTO(pri); + return PyInt_FromLong(mask); +} + +/* List of functions defined in the module */ + +static PyMethodDef syslog_methods[] = { + {"openlog", syslog_openlog, METH_VARARGS}, + {"closelog", syslog_closelog, METH_VARARGS}, + {"syslog", syslog_syslog, METH_VARARGS}, + {"setlogmask", syslog_setlogmask, METH_VARARGS}, + {"LOG_MASK", syslog_log_mask, METH_VARARGS}, + {"LOG_UPTO", syslog_log_upto, METH_VARARGS}, + {NULL, NULL, 0} +}; + +/* Initialization function for the module */ + +PyMODINIT_FUNC +initsyslog(void) +{ + PyObject *m; + + /* Create the module and add the functions */ + m = Py_InitModule("syslog", syslog_methods); + + /* Add some symbolic constants to the module */ + + /* Priorities */ + PyModule_AddIntConstant(m, "LOG_EMERG", LOG_EMERG); + PyModule_AddIntConstant(m, "LOG_ALERT", LOG_ALERT); + PyModule_AddIntConstant(m, "LOG_CRIT", LOG_CRIT); + PyModule_AddIntConstant(m, "LOG_ERR", LOG_ERR); + PyModule_AddIntConstant(m, "LOG_WARNING", LOG_WARNING); + PyModule_AddIntConstant(m, "LOG_NOTICE", LOG_NOTICE); + PyModule_AddIntConstant(m, "LOG_INFO", LOG_INFO); + PyModule_AddIntConstant(m, "LOG_DEBUG", LOG_DEBUG); + + /* openlog() option flags */ + PyModule_AddIntConstant(m, "LOG_PID", LOG_PID); + PyModule_AddIntConstant(m, "LOG_CONS", LOG_CONS); + PyModule_AddIntConstant(m, "LOG_NDELAY", LOG_NDELAY); +#ifdef LOG_NOWAIT + PyModule_AddIntConstant(m, "LOG_NOWAIT", LOG_NOWAIT); +#endif +#ifdef LOG_PERROR + PyModule_AddIntConstant(m, "LOG_PERROR", LOG_PERROR); +#endif + + /* Facilities */ + PyModule_AddIntConstant(m, "LOG_KERN", LOG_KERN); + PyModule_AddIntConstant(m, "LOG_USER", LOG_USER); + PyModule_AddIntConstant(m, "LOG_MAIL", LOG_MAIL); + PyModule_AddIntConstant(m, "LOG_DAEMON", LOG_DAEMON); + PyModule_AddIntConstant(m, "LOG_AUTH", LOG_AUTH); + PyModule_AddIntConstant(m, "LOG_LPR", LOG_LPR); + PyModule_AddIntConstant(m, "LOG_LOCAL0", LOG_LOCAL0); + PyModule_AddIntConstant(m, "LOG_LOCAL1", LOG_LOCAL1); + PyModule_AddIntConstant(m, "LOG_LOCAL2", LOG_LOCAL2); + PyModule_AddIntConstant(m, "LOG_LOCAL3", LOG_LOCAL3); + PyModule_AddIntConstant(m, "LOG_LOCAL4", LOG_LOCAL4); + PyModule_AddIntConstant(m, "LOG_LOCAL5", LOG_LOCAL5); + PyModule_AddIntConstant(m, "LOG_LOCAL6", LOG_LOCAL6); + PyModule_AddIntConstant(m, "LOG_LOCAL7", LOG_LOCAL7); + +#ifndef LOG_SYSLOG +#define LOG_SYSLOG LOG_DAEMON +#endif +#ifndef LOG_NEWS +#define LOG_NEWS LOG_MAIL +#endif +#ifndef LOG_UUCP +#define LOG_UUCP LOG_MAIL +#endif +#ifndef LOG_CRON +#define LOG_CRON LOG_DAEMON +#endif + + PyModule_AddIntConstant(m, "LOG_SYSLOG", LOG_SYSLOG); + PyModule_AddIntConstant(m, "LOG_CRON", LOG_CRON); + PyModule_AddIntConstant(m, "LOG_UUCP", LOG_UUCP); + PyModule_AddIntConstant(m, "LOG_NEWS", LOG_NEWS); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/termios.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/termios.c new file mode 100644 index 00000000..491bbb5b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/termios.c @@ -0,0 +1,924 @@ +/* termiosmodule.c -- POSIX terminal I/O module implementation. */ + +#include "Python.h" + +#define PyInit_termios inittermios + +/* Apparently, on SGI, termios.h won't define CTRL if _XOPEN_SOURCE + is defined, so we define it here. */ +#if defined(__sgi) +#define CTRL(c) ((c)&037) +#endif + +#include +#ifdef __osf__ +/* On OSF, sys/ioctl.h requires that struct termio already be defined, + * so this needs to be included first on that platform. */ +#include +#endif +#include + +/* HP-UX requires that this be included to pick up MDCD, MCTS, MDSR, + * MDTR, MRI, and MRTS (appearantly used internally by some things + * defined as macros; these are not used here directly). + */ +#ifdef HAVE_SYS_MODEM_H +#include +#endif +/* HP-UX requires that this be included to pick up TIOCGPGRP and friends */ +#ifdef HAVE_SYS_BSDTTY_H +#include +#endif + +PyDoc_STRVAR(termios__doc__, +"This module provides an interface to the Posix calls for tty I/O control.\n\ +For a complete description of these calls, see the Posix or Unix manual\n\ +pages. It is only available for those Unix versions that support Posix\n\ +termios style tty I/O control.\n\ +\n\ +All functions in this module take a file descriptor fd as their first\n\ +argument. This can be an integer file descriptor, such as returned by\n\ +sys.stdin.fileno(), or a file object, such as sys.stdin itself."); + +static PyObject *TermiosError; + +static int fdconv(PyObject* obj, void* p) +{ + int fd; + + fd = PyObject_AsFileDescriptor(obj); + if (fd >= 0) { + *(int*)p = fd; + return 1; + } + return 0; +} + +PyDoc_STRVAR(termios_tcgetattr__doc__, +"tcgetattr(fd) -> list_of_attrs\n\ +\n\ +Get the tty attributes for file descriptor fd, as follows:\n\ +[iflag, oflag, cflag, lflag, ispeed, ospeed, cc] where cc is a list\n\ +of the tty special characters (each a string of length 1, except the items\n\ +with indices VMIN and VTIME, which are integers when these fields are\n\ +defined). The interpretation of the flags and the speeds as well as the\n\ +indexing in the cc array must be done using the symbolic constants defined\n\ +in this module."); + +static PyObject * +termios_tcgetattr(PyObject *self, PyObject *args) +{ + int fd; + struct termios mode; + PyObject *cc; + speed_t ispeed, ospeed; + PyObject *v; + int i; + char ch; + + if (!PyArg_ParseTuple(args, "O&:tcgetattr", + fdconv, (void*)&fd)) + return NULL; + + if (tcgetattr(fd, &mode) == -1) + return PyErr_SetFromErrno(TermiosError); + + ispeed = cfgetispeed(&mode); + ospeed = cfgetospeed(&mode); + + cc = PyList_New(NCCS); + if (cc == NULL) + return NULL; + for (i = 0; i < NCCS; i++) { + ch = (char)mode.c_cc[i]; + v = PyString_FromStringAndSize(&ch, 1); + if (v == NULL) + goto err; + PyList_SetItem(cc, i, v); + } + + /* Convert the MIN and TIME slots to integer. On some systems, the + MIN and TIME slots are the same as the EOF and EOL slots. So we + only do this in noncanonical input mode. */ + if ((mode.c_lflag & ICANON) == 0) { + v = PyInt_FromLong((long)mode.c_cc[VMIN]); + if (v == NULL) + goto err; + PyList_SetItem(cc, VMIN, v); + v = PyInt_FromLong((long)mode.c_cc[VTIME]); + if (v == NULL) + goto err; + PyList_SetItem(cc, VTIME, v); + } + + if (!(v = PyList_New(7))) + goto err; + + PyList_SetItem(v, 0, PyInt_FromLong((long)mode.c_iflag)); + PyList_SetItem(v, 1, PyInt_FromLong((long)mode.c_oflag)); + PyList_SetItem(v, 2, PyInt_FromLong((long)mode.c_cflag)); + PyList_SetItem(v, 3, PyInt_FromLong((long)mode.c_lflag)); + PyList_SetItem(v, 4, PyInt_FromLong((long)ispeed)); + PyList_SetItem(v, 5, PyInt_FromLong((long)ospeed)); + PyList_SetItem(v, 6, cc); + if (PyErr_Occurred()){ + Py_DECREF(v); + goto err; + } + return v; + err: + Py_DECREF(cc); + return NULL; +} + +PyDoc_STRVAR(termios_tcsetattr__doc__, +"tcsetattr(fd, when, attributes) -> None\n\ +\n\ +Set the tty attributes for file descriptor fd.\n\ +The attributes to be set are taken from the attributes argument, which\n\ +is a list like the one returned by tcgetattr(). The when argument\n\ +determines when the attributes are changed: termios.TCSANOW to\n\ +change immediately, termios.TCSADRAIN to change after transmitting all\n\ +queued output, or termios.TCSAFLUSH to change after transmitting all\n\ +queued output and discarding all queued input. "); + +static PyObject * +termios_tcsetattr(PyObject *self, PyObject *args) +{ + int fd, when; + struct termios mode; + speed_t ispeed, ospeed; + PyObject *term, *cc, *v; + int i; + + if (!PyArg_ParseTuple(args, "O&iO:tcsetattr", + fdconv, &fd, &when, &term)) + return NULL; + if (!PyList_Check(term) || PyList_Size(term) != 7) { + PyErr_SetString(PyExc_TypeError, + "tcsetattr, arg 3: must be 7 element list"); + return NULL; + } + + /* Get the old mode, in case there are any hidden fields... */ + if (tcgetattr(fd, &mode) == -1) + return PyErr_SetFromErrno(TermiosError); + mode.c_iflag = (tcflag_t) PyInt_AsLong(PyList_GetItem(term, 0)); + mode.c_oflag = (tcflag_t) PyInt_AsLong(PyList_GetItem(term, 1)); + mode.c_cflag = (tcflag_t) PyInt_AsLong(PyList_GetItem(term, 2)); + mode.c_lflag = (tcflag_t) PyInt_AsLong(PyList_GetItem(term, 3)); + ispeed = (speed_t) PyInt_AsLong(PyList_GetItem(term, 4)); + ospeed = (speed_t) PyInt_AsLong(PyList_GetItem(term, 5)); + cc = PyList_GetItem(term, 6); + if (PyErr_Occurred()) + return NULL; + + if (!PyList_Check(cc) || PyList_Size(cc) != NCCS) { + PyErr_Format(PyExc_TypeError, + "tcsetattr: attributes[6] must be %d element list", + NCCS); + return NULL; + } + + for (i = 0; i < NCCS; i++) { + v = PyList_GetItem(cc, i); + + if (PyString_Check(v) && PyString_Size(v) == 1) + mode.c_cc[i] = (cc_t) * PyString_AsString(v); + else if (PyInt_Check(v)) + mode.c_cc[i] = (cc_t) PyInt_AsLong(v); + else { + PyErr_SetString(PyExc_TypeError, + "tcsetattr: elements of attributes must be characters or integers"); + return NULL; + } + } + + if (cfsetispeed(&mode, (speed_t) ispeed) == -1) + return PyErr_SetFromErrno(TermiosError); + if (cfsetospeed(&mode, (speed_t) ospeed) == -1) + return PyErr_SetFromErrno(TermiosError); + if (tcsetattr(fd, when, &mode) == -1) + return PyErr_SetFromErrno(TermiosError); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(termios_tcsendbreak__doc__, +"tcsendbreak(fd, duration) -> None\n\ +\n\ +Send a break on file descriptor fd.\n\ +A zero duration sends a break for 0.25-0.5 seconds; a nonzero duration\n\ +has a system dependent meaning."); + +static PyObject * +termios_tcsendbreak(PyObject *self, PyObject *args) +{ + int fd, duration; + + if (!PyArg_ParseTuple(args, "O&i:tcsendbreak", + fdconv, &fd, &duration)) + return NULL; + if (tcsendbreak(fd, duration) == -1) + return PyErr_SetFromErrno(TermiosError); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(termios_tcdrain__doc__, +"tcdrain(fd) -> None\n\ +\n\ +Wait until all output written to file descriptor fd has been transmitted."); + +static PyObject * +termios_tcdrain(PyObject *self, PyObject *args) +{ + int fd; + + if (!PyArg_ParseTuple(args, "O&:tcdrain", + fdconv, &fd)) + return NULL; + if (tcdrain(fd) == -1) + return PyErr_SetFromErrno(TermiosError); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(termios_tcflush__doc__, +"tcflush(fd, queue) -> None\n\ +\n\ +Discard queued data on file descriptor fd.\n\ +The queue selector specifies which queue: termios.TCIFLUSH for the input\n\ +queue, termios.TCOFLUSH for the output queue, or termios.TCIOFLUSH for\n\ +both queues. "); + +static PyObject * +termios_tcflush(PyObject *self, PyObject *args) +{ + int fd, queue; + + if (!PyArg_ParseTuple(args, "O&i:tcflush", + fdconv, &fd, &queue)) + return NULL; + if (tcflush(fd, queue) == -1) + return PyErr_SetFromErrno(TermiosError); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(termios_tcflow__doc__, +"tcflow(fd, action) -> None\n\ +\n\ +Suspend or resume input or output on file descriptor fd.\n\ +The action argument can be termios.TCOOFF to suspend output,\n\ +termios.TCOON to restart output, termios.TCIOFF to suspend input,\n\ +or termios.TCION to restart input."); + +static PyObject * +termios_tcflow(PyObject *self, PyObject *args) +{ + int fd, action; + + if (!PyArg_ParseTuple(args, "O&i:tcflow", + fdconv, &fd, &action)) + return NULL; + if (tcflow(fd, action) == -1) + return PyErr_SetFromErrno(TermiosError); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef termios_methods[] = +{ + {"tcgetattr", termios_tcgetattr, + METH_VARARGS, termios_tcgetattr__doc__}, + {"tcsetattr", termios_tcsetattr, + METH_VARARGS, termios_tcsetattr__doc__}, + {"tcsendbreak", termios_tcsendbreak, + METH_VARARGS, termios_tcsendbreak__doc__}, + {"tcdrain", termios_tcdrain, + METH_VARARGS, termios_tcdrain__doc__}, + {"tcflush", termios_tcflush, + METH_VARARGS, termios_tcflush__doc__}, + {"tcflow", termios_tcflow, + METH_VARARGS, termios_tcflow__doc__}, + {NULL, NULL} +}; + + +#if defined(VSWTCH) && !defined(VSWTC) +#define VSWTC VSWTCH +#endif + +#if defined(VSWTC) && !defined(VSWTCH) +#define VSWTCH VSWTC +#endif + +static struct constant { + char *name; + long value; +} termios_constants[] = { + /* cfgetospeed(), cfsetospeed() constants */ + {"B0", B0}, + {"B50", B50}, + {"B75", B75}, + {"B110", B110}, + {"B134", B134}, + {"B150", B150}, + {"B200", B200}, + {"B300", B300}, + {"B600", B600}, + {"B1200", B1200}, + {"B1800", B1800}, + {"B2400", B2400}, + {"B4800", B4800}, + {"B9600", B9600}, + {"B19200", B19200}, + {"B38400", B38400}, +#ifdef B57600 + {"B57600", B57600}, +#endif +#ifdef B115200 + {"B115200", B115200}, +#endif +#ifdef B230400 + {"B230400", B230400}, +#endif +#ifdef CBAUDEX + {"CBAUDEX", CBAUDEX}, +#endif + + /* tcsetattr() constants */ + {"TCSANOW", TCSANOW}, + {"TCSADRAIN", TCSADRAIN}, + {"TCSAFLUSH", TCSAFLUSH}, + + /* tcflush() constants */ + {"TCIFLUSH", TCIFLUSH}, + {"TCOFLUSH", TCOFLUSH}, + {"TCIOFLUSH", TCIOFLUSH}, + + /* tcflow() constants */ + {"TCOOFF", TCOOFF}, + {"TCOON", TCOON}, + {"TCIOFF", TCIOFF}, + {"TCION", TCION}, + + /* struct termios.c_iflag constants */ + {"IGNBRK", IGNBRK}, + {"BRKINT", BRKINT}, + {"IGNPAR", IGNPAR}, + {"PARMRK", PARMRK}, + {"INPCK", INPCK}, + {"ISTRIP", ISTRIP}, + {"INLCR", INLCR}, + {"IGNCR", IGNCR}, + {"ICRNL", ICRNL}, +#ifdef IUCLC + {"IUCLC", IUCLC}, +#endif + {"IXON", IXON}, + {"IXANY", IXANY}, + {"IXOFF", IXOFF}, +#ifdef IMAXBEL + {"IMAXBEL", IMAXBEL}, +#endif + + /* struct termios.c_oflag constants */ + {"OPOST", OPOST}, +#ifdef OLCUC + {"OLCUC", OLCUC}, +#endif +#ifdef ONLCR + {"ONLCR", ONLCR}, +#endif +#ifdef OCRNL + {"OCRNL", OCRNL}, +#endif +#ifdef ONOCR + {"ONOCR", ONOCR}, +#endif +#ifdef ONLRET + {"ONLRET", ONLRET}, +#endif +#ifdef OFILL + {"OFILL", OFILL}, +#endif +#ifdef OFDEL + {"OFDEL", OFDEL}, +#endif +#ifdef NLDLY + {"NLDLY", NLDLY}, +#endif +#ifdef CRDLY + {"CRDLY", CRDLY}, +#endif +#ifdef TABDLY + {"TABDLY", TABDLY}, +#endif +#ifdef BSDLY + {"BSDLY", BSDLY}, +#endif +#ifdef VTDLY + {"VTDLY", VTDLY}, +#endif +#ifdef FFDLY + {"FFDLY", FFDLY}, +#endif + + /* struct termios.c_oflag-related values (delay mask) */ +#ifdef NL0 + {"NL0", NL0}, +#endif +#ifdef NL1 + {"NL1", NL1}, +#endif +#ifdef CR0 + {"CR0", CR0}, +#endif +#ifdef CR1 + {"CR1", CR1}, +#endif +#ifdef CR2 + {"CR2", CR2}, +#endif +#ifdef CR3 + {"CR3", CR3}, +#endif +#ifdef TAB0 + {"TAB0", TAB0}, +#endif +#ifdef TAB1 + {"TAB1", TAB1}, +#endif +#ifdef TAB2 + {"TAB2", TAB2}, +#endif +#ifdef TAB3 + {"TAB3", TAB3}, +#endif +#ifdef XTABS + {"XTABS", XTABS}, +#endif +#ifdef BS0 + {"BS0", BS0}, +#endif +#ifdef BS1 + {"BS1", BS1}, +#endif +#ifdef VT0 + {"VT0", VT0}, +#endif +#ifdef VT1 + {"VT1", VT1}, +#endif +#ifdef FF0 + {"FF0", FF0}, +#endif +#ifdef FF1 + {"FF1", FF1}, +#endif + + /* struct termios.c_cflag constants */ + {"CSIZE", CSIZE}, + {"CSTOPB", CSTOPB}, + {"CREAD", CREAD}, + {"PARENB", PARENB}, + {"PARODD", PARODD}, + {"HUPCL", HUPCL}, + {"CLOCAL", CLOCAL}, +#ifdef CIBAUD + {"CIBAUD", CIBAUD}, +#endif +#ifdef CRTSCTS + {"CRTSCTS", (long)CRTSCTS}, +#endif + + /* struct termios.c_cflag-related values (character size) */ + {"CS5", CS5}, + {"CS6", CS6}, + {"CS7", CS7}, + {"CS8", CS8}, + + /* struct termios.c_lflag constants */ + {"ISIG", ISIG}, + {"ICANON", ICANON}, +#ifdef XCASE + {"XCASE", XCASE}, +#endif + {"ECHO", ECHO}, + {"ECHOE", ECHOE}, + {"ECHOK", ECHOK}, + {"ECHONL", ECHONL}, +#ifdef ECHOCTL + {"ECHOCTL", ECHOCTL}, +#endif +#ifdef ECHOPRT + {"ECHOPRT", ECHOPRT}, +#endif +#ifdef ECHOKE + {"ECHOKE", ECHOKE}, +#endif +#ifdef FLUSHO + {"FLUSHO", FLUSHO}, +#endif + {"NOFLSH", NOFLSH}, + {"TOSTOP", TOSTOP}, +#ifdef PENDIN + {"PENDIN", PENDIN}, +#endif + {"IEXTEN", IEXTEN}, + + /* indexes into the control chars array returned by tcgetattr() */ + {"VINTR", VINTR}, + {"VQUIT", VQUIT}, + {"VERASE", VERASE}, + {"VKILL", VKILL}, + {"VEOF", VEOF}, + {"VTIME", VTIME}, + {"VMIN", VMIN}, +#ifdef VSWTC + /* The #defines above ensure that if either is defined, both are, + * but both may be omitted by the system headers. ;-( */ + {"VSWTC", VSWTC}, + {"VSWTCH", VSWTCH}, +#endif + {"VSTART", VSTART}, + {"VSTOP", VSTOP}, + {"VSUSP", VSUSP}, + {"VEOL", VEOL}, +#ifdef VREPRINT + {"VREPRINT", VREPRINT}, +#endif +#ifdef VDISCARD + {"VDISCARD", VDISCARD}, +#endif +#ifdef VWERASE + {"VWERASE", VWERASE}, +#endif +#ifdef VLNEXT + {"VLNEXT", VLNEXT}, +#endif +#ifdef VEOL2 + {"VEOL2", VEOL2}, +#endif + + +#ifdef B460800 + {"B460800", B460800}, +#endif +#ifdef CBAUD + {"CBAUD", CBAUD}, +#endif +#ifdef CDEL + {"CDEL", CDEL}, +#endif +#ifdef CDSUSP + {"CDSUSP", CDSUSP}, +#endif +#ifdef CEOF + {"CEOF", CEOF}, +#endif +#ifdef CEOL + {"CEOL", CEOL}, +#endif +#ifdef CEOL2 + {"CEOL2", CEOL2}, +#endif +#ifdef CEOT + {"CEOT", CEOT}, +#endif +#ifdef CERASE + {"CERASE", CERASE}, +#endif +#ifdef CESC + {"CESC", CESC}, +#endif +#ifdef CFLUSH + {"CFLUSH", CFLUSH}, +#endif +#ifdef CINTR + {"CINTR", CINTR}, +#endif +#ifdef CKILL + {"CKILL", CKILL}, +#endif +#ifdef CLNEXT + {"CLNEXT", CLNEXT}, +#endif +#ifdef CNUL + {"CNUL", CNUL}, +#endif +#ifdef COMMON + {"COMMON", COMMON}, +#endif +#ifdef CQUIT + {"CQUIT", CQUIT}, +#endif +#ifdef CRPRNT + {"CRPRNT", CRPRNT}, +#endif +#ifdef CSTART + {"CSTART", CSTART}, +#endif +#ifdef CSTOP + {"CSTOP", CSTOP}, +#endif +#ifdef CSUSP + {"CSUSP", CSUSP}, +#endif +#ifdef CSWTCH + {"CSWTCH", CSWTCH}, +#endif +#ifdef CWERASE + {"CWERASE", CWERASE}, +#endif +#ifdef EXTA + {"EXTA", EXTA}, +#endif +#ifdef EXTB + {"EXTB", EXTB}, +#endif +#ifdef FIOASYNC + {"FIOASYNC", FIOASYNC}, +#endif +#ifdef FIOCLEX + {"FIOCLEX", FIOCLEX}, +#endif +#ifdef FIONBIO + {"FIONBIO", FIONBIO}, +#endif +#ifdef FIONCLEX + {"FIONCLEX", FIONCLEX}, +#endif +#ifdef FIONREAD + {"FIONREAD", FIONREAD}, +#endif +#ifdef IBSHIFT + {"IBSHIFT", IBSHIFT}, +#endif +#ifdef INIT_C_CC + {"INIT_C_CC", INIT_C_CC}, +#endif +#ifdef IOCSIZE_MASK + {"IOCSIZE_MASK", IOCSIZE_MASK}, +#endif +#ifdef IOCSIZE_SHIFT + {"IOCSIZE_SHIFT", IOCSIZE_SHIFT}, +#endif +#ifdef NCC + {"NCC", NCC}, +#endif +#ifdef NCCS + {"NCCS", NCCS}, +#endif +#ifdef NSWTCH + {"NSWTCH", NSWTCH}, +#endif +#ifdef N_MOUSE + {"N_MOUSE", N_MOUSE}, +#endif +#ifdef N_PPP + {"N_PPP", N_PPP}, +#endif +#ifdef N_SLIP + {"N_SLIP", N_SLIP}, +#endif +#ifdef N_STRIP + {"N_STRIP", N_STRIP}, +#endif +#ifdef N_TTY + {"N_TTY", N_TTY}, +#endif +#ifdef TCFLSH + {"TCFLSH", TCFLSH}, +#endif +#ifdef TCGETA + {"TCGETA", TCGETA}, +#endif +#ifdef TCGETS + {"TCGETS", TCGETS}, +#endif +#ifdef TCSBRK + {"TCSBRK", TCSBRK}, +#endif +#ifdef TCSBRKP + {"TCSBRKP", TCSBRKP}, +#endif +#ifdef TCSETA + {"TCSETA", TCSETA}, +#endif +#ifdef TCSETAF + {"TCSETAF", TCSETAF}, +#endif +#ifdef TCSETAW + {"TCSETAW", TCSETAW}, +#endif +#ifdef TCSETS + {"TCSETS", TCSETS}, +#endif +#ifdef TCSETSF + {"TCSETSF", TCSETSF}, +#endif +#ifdef TCSETSW + {"TCSETSW", TCSETSW}, +#endif +#ifdef TCXONC + {"TCXONC", TCXONC}, +#endif +#ifdef TIOCCONS + {"TIOCCONS", TIOCCONS}, +#endif +#ifdef TIOCEXCL + {"TIOCEXCL", TIOCEXCL}, +#endif +#ifdef TIOCGETD + {"TIOCGETD", TIOCGETD}, +#endif +#ifdef TIOCGICOUNT + {"TIOCGICOUNT", TIOCGICOUNT}, +#endif +#ifdef TIOCGLCKTRMIOS + {"TIOCGLCKTRMIOS", TIOCGLCKTRMIOS}, +#endif +#ifdef TIOCGPGRP + {"TIOCGPGRP", TIOCGPGRP}, +#endif +#ifdef TIOCGSERIAL + {"TIOCGSERIAL", TIOCGSERIAL}, +#endif +#ifdef TIOCGSOFTCAR + {"TIOCGSOFTCAR", TIOCGSOFTCAR}, +#endif +#ifdef TIOCGWINSZ + {"TIOCGWINSZ", TIOCGWINSZ}, +#endif +#ifdef TIOCINQ + {"TIOCINQ", TIOCINQ}, +#endif +#ifdef TIOCLINUX + {"TIOCLINUX", TIOCLINUX}, +#endif +#ifdef TIOCMBIC + {"TIOCMBIC", TIOCMBIC}, +#endif +#ifdef TIOCMBIS + {"TIOCMBIS", TIOCMBIS}, +#endif +#ifdef TIOCMGET + {"TIOCMGET", TIOCMGET}, +#endif +#ifdef TIOCMIWAIT + {"TIOCMIWAIT", TIOCMIWAIT}, +#endif +#ifdef TIOCMSET + {"TIOCMSET", TIOCMSET}, +#endif +#ifdef TIOCM_CAR + {"TIOCM_CAR", TIOCM_CAR}, +#endif +#ifdef TIOCM_CD + {"TIOCM_CD", TIOCM_CD}, +#endif +#ifdef TIOCM_CTS + {"TIOCM_CTS", TIOCM_CTS}, +#endif +#ifdef TIOCM_DSR + {"TIOCM_DSR", TIOCM_DSR}, +#endif +#ifdef TIOCM_DTR + {"TIOCM_DTR", TIOCM_DTR}, +#endif +#ifdef TIOCM_LE + {"TIOCM_LE", TIOCM_LE}, +#endif +#ifdef TIOCM_RI + {"TIOCM_RI", TIOCM_RI}, +#endif +#ifdef TIOCM_RNG + {"TIOCM_RNG", TIOCM_RNG}, +#endif +#ifdef TIOCM_RTS + {"TIOCM_RTS", TIOCM_RTS}, +#endif +#ifdef TIOCM_SR + {"TIOCM_SR", TIOCM_SR}, +#endif +#ifdef TIOCM_ST + {"TIOCM_ST", TIOCM_ST}, +#endif +#ifdef TIOCNOTTY + {"TIOCNOTTY", TIOCNOTTY}, +#endif +#ifdef TIOCNXCL + {"TIOCNXCL", TIOCNXCL}, +#endif +#ifdef TIOCOUTQ + {"TIOCOUTQ", TIOCOUTQ}, +#endif +#ifdef TIOCPKT + {"TIOCPKT", TIOCPKT}, +#endif +#ifdef TIOCPKT_DATA + {"TIOCPKT_DATA", TIOCPKT_DATA}, +#endif +#ifdef TIOCPKT_DOSTOP + {"TIOCPKT_DOSTOP", TIOCPKT_DOSTOP}, +#endif +#ifdef TIOCPKT_FLUSHREAD + {"TIOCPKT_FLUSHREAD", TIOCPKT_FLUSHREAD}, +#endif +#ifdef TIOCPKT_FLUSHWRITE + {"TIOCPKT_FLUSHWRITE", TIOCPKT_FLUSHWRITE}, +#endif +#ifdef TIOCPKT_NOSTOP + {"TIOCPKT_NOSTOP", TIOCPKT_NOSTOP}, +#endif +#ifdef TIOCPKT_START + {"TIOCPKT_START", TIOCPKT_START}, +#endif +#ifdef TIOCPKT_STOP + {"TIOCPKT_STOP", TIOCPKT_STOP}, +#endif +#ifdef TIOCSCTTY + {"TIOCSCTTY", TIOCSCTTY}, +#endif +#ifdef TIOCSERCONFIG + {"TIOCSERCONFIG", TIOCSERCONFIG}, +#endif +#ifdef TIOCSERGETLSR + {"TIOCSERGETLSR", TIOCSERGETLSR}, +#endif +#ifdef TIOCSERGETMULTI + {"TIOCSERGETMULTI", TIOCSERGETMULTI}, +#endif +#ifdef TIOCSERGSTRUCT + {"TIOCSERGSTRUCT", TIOCSERGSTRUCT}, +#endif +#ifdef TIOCSERGWILD + {"TIOCSERGWILD", TIOCSERGWILD}, +#endif +#ifdef TIOCSERSETMULTI + {"TIOCSERSETMULTI", TIOCSERSETMULTI}, +#endif +#ifdef TIOCSERSWILD + {"TIOCSERSWILD", TIOCSERSWILD}, +#endif +#ifdef TIOCSER_TEMT + {"TIOCSER_TEMT", TIOCSER_TEMT}, +#endif +#ifdef TIOCSETD + {"TIOCSETD", TIOCSETD}, +#endif +#ifdef TIOCSLCKTRMIOS + {"TIOCSLCKTRMIOS", TIOCSLCKTRMIOS}, +#endif +#ifdef TIOCSPGRP + {"TIOCSPGRP", TIOCSPGRP}, +#endif +#ifdef TIOCSSERIAL + {"TIOCSSERIAL", TIOCSSERIAL}, +#endif +#ifdef TIOCSSOFTCAR + {"TIOCSSOFTCAR", TIOCSSOFTCAR}, +#endif +#ifdef TIOCSTI + {"TIOCSTI", TIOCSTI}, +#endif +#ifdef TIOCSWINSZ + {"TIOCSWINSZ", TIOCSWINSZ}, +#endif +#ifdef TIOCTTYGSTRUCT + {"TIOCTTYGSTRUCT", TIOCTTYGSTRUCT}, +#endif + + /* sentinel */ + {NULL, 0} +}; + + +PyMODINIT_FUNC +PyInit_termios(void) +{ + PyObject *m; + struct constant *constant = termios_constants; + + m = Py_InitModule4("termios", termios_methods, termios__doc__, + (PyObject *)NULL, PYTHON_API_VERSION); + + if (TermiosError == NULL) { + TermiosError = PyErr_NewException("termios.error", NULL, NULL); + } + Py_INCREF(TermiosError); + PyModule_AddObject(m, "error", TermiosError); + + while (constant->name != NULL) { + PyModule_AddIntConstant(m, constant->name, constant->value); + ++constant; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/testcapi_long.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/testcapi_long.h new file mode 100644 index 00000000..445a7295 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/testcapi_long.h @@ -0,0 +1,166 @@ +/* Poor-man's template. Macros used: + TESTNAME name of the test (like test_long_api_inner) + TYPENAME the signed type (like long) + F_S_TO_PY convert signed to pylong; TYPENAME -> PyObject* + F_PY_TO_S convert pylong to signed; PyObject* -> TYPENAME + F_U_TO_PY convert unsigned to pylong; unsigned TYPENAME -> PyObject* + F_PY_TO_U convert pylong to unsigned; PyObject* -> unsigned TYPENAME +*/ + +static PyObject * +TESTNAME(PyObject *error(const char*)) +{ + const int NBITS = sizeof(TYPENAME) * 8; + unsigned TYPENAME base; + PyObject *pyresult; + int i; + + /* Note: This test lets PyObjects leak if an error is raised. Since + an error should never be raised, leaks are impossible . */ + + /* Test native -> PyLong -> native roundtrip identity. + * Generate all powers of 2, and test them and their negations, + * plus the numbers +-1 off from them. + */ + base = 1; + for (i = 0; + i < NBITS + 1; /* on last, base overflows to 0 */ + ++i, base <<= 1) + { + int j; + for (j = 0; j < 6; ++j) { + TYPENAME in, out; + unsigned TYPENAME uin, uout; + + /* For 0, 1, 2 use base; for 3, 4, 5 use -base */ + uin = j < 3 ? base + : (unsigned TYPENAME)(-(TYPENAME)base); + + /* For 0 & 3, subtract 1. + * For 1 & 4, leave alone. + * For 2 & 5, add 1. + */ + uin += (unsigned TYPENAME)(TYPENAME)(j % 3 - 1); + + pyresult = F_U_TO_PY(uin); + if (pyresult == NULL) + return error( + "unsigned unexpected null result"); + + uout = F_PY_TO_U(pyresult); + if (uout == (unsigned TYPENAME)-1 && PyErr_Occurred()) + return error( + "unsigned unexpected -1 result"); + if (uout != uin) + return error( + "unsigned output != input"); + UNBIND(pyresult); + + in = (TYPENAME)uin; + pyresult = F_S_TO_PY(in); + if (pyresult == NULL) + return error( + "signed unexpected null result"); + + out = F_PY_TO_S(pyresult); + if (out == (TYPENAME)-1 && PyErr_Occurred()) + return error( + "signed unexpected -1 result"); + if (out != in) + return error( + "signed output != input"); + UNBIND(pyresult); + } + } + + /* Overflow tests. The loop above ensured that all limit cases that + * should not overflow don't overflow, so all we need to do here is + * provoke one-over-the-limit cases (not exhaustive, but sharp). + */ + { + PyObject *one, *x, *y; + TYPENAME out; + unsigned TYPENAME uout; + + one = PyLong_FromLong(1); + if (one == NULL) + return error( + "unexpected NULL from PyLong_FromLong"); + + /* Unsigned complains about -1? */ + x = PyNumber_Negative(one); + if (x == NULL) + return error( + "unexpected NULL from PyNumber_Negative"); + + uout = F_PY_TO_U(x); + if (uout != (unsigned TYPENAME)-1 || !PyErr_Occurred()) + return error( + "PyLong_AsUnsignedXXX(-1) didn't complain"); + PyErr_Clear(); + UNBIND(x); + + /* Unsigned complains about 2**NBITS? */ + y = PyLong_FromLong((long)NBITS); + if (y == NULL) + return error( + "unexpected NULL from PyLong_FromLong"); + + x = PyNumber_Lshift(one, y); /* 1L << NBITS, == 2**NBITS */ + UNBIND(y); + if (x == NULL) + return error( + "unexpected NULL from PyNumber_Lshift"); + + uout = F_PY_TO_U(x); + if (uout != (unsigned TYPENAME)-1 || !PyErr_Occurred()) + return error( + "PyLong_AsUnsignedXXX(2**NBITS) didn't " + "complain"); + PyErr_Clear(); + + /* Signed complains about 2**(NBITS-1)? + x still has 2**NBITS. */ + y = PyNumber_Rshift(x, one); /* 2**(NBITS-1) */ + UNBIND(x); + if (y == NULL) + return error( + "unexpected NULL from PyNumber_Rshift"); + + out = F_PY_TO_S(y); + if (out != (TYPENAME)-1 || !PyErr_Occurred()) + return error( + "PyLong_AsXXX(2**(NBITS-1)) didn't " + "complain"); + PyErr_Clear(); + + /* Signed complains about -2**(NBITS-1)-1?; + y still has 2**(NBITS-1). */ + x = PyNumber_Negative(y); /* -(2**(NBITS-1)) */ + UNBIND(y); + if (x == NULL) + return error( + "unexpected NULL from PyNumber_Negative"); + + y = PyNumber_Subtract(x, one); /* -(2**(NBITS-1))-1 */ + UNBIND(x); + if (y == NULL) + return error( + "unexpected NULL from PyNumber_Subtract"); + + out = F_PY_TO_S(y); + if (out != (TYPENAME)-1 || !PyErr_Occurred()) + return error( + "PyLong_AsXXX(-2**(NBITS-1)-1) didn't " + "complain"); + PyErr_Clear(); + UNBIND(y); + + Py_XDECREF(x); + Py_XDECREF(y); + Py_DECREF(one); + } + + Py_INCREF(Py_None); + return Py_None; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/threadmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/threadmodule.c new file mode 100644 index 00000000..8de50795 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/threadmodule.c @@ -0,0 +1,406 @@ + +/* Thread module */ +/* Interface to Sjoerd's portable C thread library */ + +#include "Python.h" + +#ifndef WITH_THREAD +#error "Error! The rest of Python is not compiled with thread support." +#error "Rerun configure, adding a --with-threads option." +#error "Then run `make clean' followed by `make'." +#endif + +#include "pythread.h" + +static PyObject *ThreadError; + + +/* Lock objects */ + +typedef struct { + PyObject_HEAD + PyThread_type_lock lock_lock; +} lockobject; + +static PyTypeObject Locktype; + +static lockobject * +newlockobject(void) +{ + lockobject *self; + self = PyObject_New(lockobject, &Locktype); + if (self == NULL) + return NULL; + self->lock_lock = PyThread_allocate_lock(); + if (self->lock_lock == NULL) { + PyObject_Del(self); + self = NULL; + PyErr_SetString(ThreadError, "can't allocate lock"); + } + return self; +} + +static void +lock_dealloc(lockobject *self) +{ + /* Unlock the lock so it's safe to free it */ + PyThread_acquire_lock(self->lock_lock, 0); + PyThread_release_lock(self->lock_lock); + + PyThread_free_lock(self->lock_lock); + PyObject_Del(self); +} + +static PyObject * +lock_PyThread_acquire_lock(lockobject *self, PyObject *args) +{ + int i = 1; + + if (!PyArg_ParseTuple(args, "|i:acquire", &i)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + i = PyThread_acquire_lock(self->lock_lock, i); + Py_END_ALLOW_THREADS + + if (args == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + else + return PyBool_FromLong((long)i); +} + +PyDoc_STRVAR(acquire_doc, +"acquire([wait]) -> None or bool\n\ +(PyThread_acquire_lock() is an obsolete synonym)\n\ +\n\ +Lock the lock. Without argument, this blocks if the lock is already\n\ +locked (even by the same thread), waiting for another thread to release\n\ +the lock, and return None once the lock is acquired.\n\ +With an argument, this will only block if the argument is true,\n\ +and the return value reflects whether the lock is acquired.\n\ +The blocking operation is not interruptible."); + +static PyObject * +lock_PyThread_release_lock(lockobject *self) +{ + /* Sanity check: the lock must be locked */ + if (PyThread_acquire_lock(self->lock_lock, 0)) { + PyThread_release_lock(self->lock_lock); + PyErr_SetString(ThreadError, "release unlocked lock"); + return NULL; + } + + PyThread_release_lock(self->lock_lock); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(release_doc, +"release()\n\ +(PyThread_release_lock() is an obsolete synonym)\n\ +\n\ +Release the lock, allowing another thread that is blocked waiting for\n\ +the lock to acquire the lock. The lock must be in the locked state,\n\ +but it needn't be locked by the same thread that unlocks it."); + +static PyObject * +lock_locked_lock(lockobject *self) +{ + if (PyThread_acquire_lock(self->lock_lock, 0)) { + PyThread_release_lock(self->lock_lock); + return PyBool_FromLong(0L); + } + return PyBool_FromLong(1L); +} + +PyDoc_STRVAR(locked_doc, +"locked() -> bool\n\ +(locked_lock() is an obsolete synonym)\n\ +\n\ +Return whether the lock is in the locked state."); + +static PyMethodDef lock_methods[] = { + {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock, + METH_VARARGS, acquire_doc}, + {"acquire", (PyCFunction)lock_PyThread_acquire_lock, + METH_VARARGS, acquire_doc}, + {"release_lock", (PyCFunction)lock_PyThread_release_lock, + METH_NOARGS, release_doc}, + {"release", (PyCFunction)lock_PyThread_release_lock, + METH_NOARGS, release_doc}, + {"locked_lock", (PyCFunction)lock_locked_lock, + METH_NOARGS, locked_doc}, + {"locked", (PyCFunction)lock_locked_lock, + METH_NOARGS, locked_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +lock_getattr(lockobject *self, char *name) +{ + return Py_FindMethod(lock_methods, (PyObject *)self, name); +} + +static PyTypeObject Locktype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "thread.lock", /*tp_name*/ + sizeof(lockobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)lock_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)lock_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + + +/* Module functions */ + +struct bootstate { + PyInterpreterState *interp; + PyObject *func; + PyObject *args; + PyObject *keyw; +}; + +static void +t_bootstrap(void *boot_raw) +{ + struct bootstate *boot = (struct bootstate *) boot_raw; + PyThreadState *tstate; + PyObject *res; + + tstate = PyThreadState_New(boot->interp); + PyEval_AcquireThread(tstate); + res = PyEval_CallObjectWithKeywords( + boot->func, boot->args, boot->keyw); + if (res == NULL) { + if (PyErr_ExceptionMatches(PyExc_SystemExit)) + PyErr_Clear(); + else { + PyObject *file; + PySys_WriteStderr( + "Unhandled exception in thread started by "); + file = PySys_GetObject("stderr"); + if (file) + PyFile_WriteObject(boot->func, file, 0); + else + PyObject_Print(boot->func, stderr, 0); + PySys_WriteStderr("\n"); + PyErr_PrintEx(0); + } + } + else + Py_DECREF(res); + Py_DECREF(boot->func); + Py_DECREF(boot->args); + Py_XDECREF(boot->keyw); + PyMem_DEL(boot_raw); + PyThreadState_Clear(tstate); + PyThreadState_DeleteCurrent(); + PyThread_exit_thread(); +} + +static PyObject * +thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) +{ + PyObject *func, *args, *keyw = NULL; + struct bootstate *boot; + long ident; + + if (!PyArg_ParseTuple(fargs, "OO|O:start_new_thread", &func, &args, &keyw)) + return NULL; + if (!PyCallable_Check(func)) { + PyErr_SetString(PyExc_TypeError, + "first arg must be callable"); + return NULL; + } + if (!PyTuple_Check(args)) { + PyErr_SetString(PyExc_TypeError, + "2nd arg must be a tuple"); + return NULL; + } + if (keyw != NULL && !PyDict_Check(keyw)) { + PyErr_SetString(PyExc_TypeError, + "optional 3rd arg must be a dictionary"); + return NULL; + } + boot = PyMem_NEW(struct bootstate, 1); + if (boot == NULL) + return PyErr_NoMemory(); + boot->interp = PyThreadState_Get()->interp; + boot->func = func; + boot->args = args; + boot->keyw = keyw; + Py_INCREF(func); + Py_INCREF(args); + Py_XINCREF(keyw); + PyEval_InitThreads(); /* Start the interpreter's thread-awareness */ + ident = PyThread_start_new_thread(t_bootstrap, (void*) boot); + if (ident == -1) { + PyErr_SetString(ThreadError, "can't start new thread\n"); + Py_DECREF(func); + Py_DECREF(args); + Py_XDECREF(keyw); + PyMem_DEL(boot); + return NULL; + } + return PyInt_FromLong(ident); +} + +PyDoc_STRVAR(start_new_doc, +"start_new_thread(function, args[, kwargs])\n\ +(start_new() is an obsolete synonym)\n\ +\n\ +Start a new thread and return its identifier. The thread will call the\n\ +function with positional arguments from the tuple args and keyword arguments\n\ +taken from the optional dictionary kwargs. The thread exits when the\n\ +function returns; the return value is ignored. The thread will also exit\n\ +when the function raises an unhandled exception; a stack trace will be\n\ +printed unless the exception is SystemExit.\n"); + +static PyObject * +thread_PyThread_exit_thread(PyObject *self) +{ + PyErr_SetNone(PyExc_SystemExit); + return NULL; +} + +PyDoc_STRVAR(exit_doc, +"exit()\n\ +(PyThread_exit_thread() is an obsolete synonym)\n\ +\n\ +This is synonymous to ``raise SystemExit''. It will cause the current\n\ +thread to exit silently unless the exception is caught."); + +static PyObject * +thread_PyThread_interrupt_main(PyObject * self) +{ + PyErr_SetInterrupt(); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(interrupt_doc, +"interrupt_main()\n\ +\n\ +Raise a KeyboardInterrupt in the main thread.\n\ +A subthread can use this function to interrupt the main thread." +); + +#ifndef NO_EXIT_PROG +static PyObject * +thread_PyThread_exit_prog(PyObject *self, PyObject *args) +{ + int sts; + if (!PyArg_ParseTuple(args, "i:exit_prog", &sts)) + return NULL; + Py_Exit(sts); /* Calls PyThread_exit_prog(sts) or _PyThread_exit_prog(sts) */ + for (;;) { } /* Should not be reached */ +} +#endif + +static PyObject * +thread_PyThread_allocate_lock(PyObject *self) +{ + return (PyObject *) newlockobject(); +} + +PyDoc_STRVAR(allocate_doc, +"allocate_lock() -> lock object\n\ +(allocate() is an obsolete synonym)\n\ +\n\ +Create a new lock object. See LockType.__doc__ for information about locks."); + +static PyObject * +thread_get_ident(PyObject *self) +{ + long ident; + ident = PyThread_get_thread_ident(); + if (ident == -1) { + PyErr_SetString(ThreadError, "no current thread ident"); + return NULL; + } + return PyInt_FromLong(ident); +} + +PyDoc_STRVAR(get_ident_doc, +"get_ident() -> integer\n\ +\n\ +Return a non-zero integer that uniquely identifies the current thread\n\ +amongst other threads that exist simultaneously.\n\ +This may be used to identify per-thread resources.\n\ +Even though on some platforms threads identities may appear to be\n\ +allocated consecutive numbers starting at 1, this behavior should not\n\ +be relied upon, and the number should be seen purely as a magic cookie.\n\ +A thread's identity may be reused for another thread after it exits."); + +static PyMethodDef thread_methods[] = { + {"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread, + METH_VARARGS, + start_new_doc}, + {"start_new", (PyCFunction)thread_PyThread_start_new_thread, + METH_VARARGS, + start_new_doc}, + {"allocate_lock", (PyCFunction)thread_PyThread_allocate_lock, + METH_NOARGS, allocate_doc}, + {"allocate", (PyCFunction)thread_PyThread_allocate_lock, + METH_NOARGS, allocate_doc}, + {"exit_thread", (PyCFunction)thread_PyThread_exit_thread, + METH_NOARGS, exit_doc}, + {"exit", (PyCFunction)thread_PyThread_exit_thread, + METH_NOARGS, exit_doc}, + {"interrupt_main", (PyCFunction)thread_PyThread_interrupt_main, + METH_NOARGS, interrupt_doc}, + {"get_ident", (PyCFunction)thread_get_ident, + METH_NOARGS, get_ident_doc}, +#ifndef NO_EXIT_PROG + {"exit_prog", (PyCFunction)thread_PyThread_exit_prog, + METH_VARARGS}, +#endif + {NULL, NULL} /* sentinel */ +}; + + +/* Initialization function */ + +PyDoc_STRVAR(thread_doc, +"This module provides primitive operations to write multi-threaded programs.\n\ +The 'threading' module provides a more convenient interface."); + +PyDoc_STRVAR(lock_doc, +"A lock object is a synchronization primitive. To create a lock,\n\ +call the PyThread_allocate_lock() function. Methods are:\n\ +\n\ +acquire() -- lock the lock, possibly blocking until it can be obtained\n\ +release() -- unlock of the lock\n\ +locked() -- test whether the lock is currently locked\n\ +\n\ +A lock is not owned by the thread that locked it; another thread may\n\ +unlock it. A thread attempting to lock a lock that it has already locked\n\ +will block until another thread unlocks it. Deadlocks may ensue."); + +PyMODINIT_FUNC +initthread(void) +{ + PyObject *m, *d; + + /* Create the module and add the functions */ + m = Py_InitModule3("thread", thread_methods, thread_doc); + + /* Add a symbolic constant */ + d = PyModule_GetDict(m); + ThreadError = PyErr_NewException("thread.error", NULL, NULL); + PyDict_SetItemString(d, "error", ThreadError); + Locktype.tp_doc = lock_doc; + Py_INCREF(&Locktype); + PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype); + + /* Initialize the C thread library */ + PyThread_init_thread(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timemodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timemodule.c new file mode 100644 index 00000000..a0bdce7c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timemodule.c @@ -0,0 +1,944 @@ + +/* Time module */ + +#include "Python.h" +#include "structseq.h" + +#include + +#ifdef macintosh +#include +#include +#else +#include +#endif + +#ifdef QUICKWIN +#include +#endif + +#ifdef HAVE_FTIME +#include +#if !defined(MS_WINDOWS) && !defined(PYOS_OS2) +extern int ftime(struct timeb *); +#endif /* MS_WINDOWS */ +#endif /* HAVE_FTIME */ + +#if defined(__WATCOMC__) && !defined(__QNX__) +#include +#else +#ifdef MS_WINDOWS +#ifdef MS_XBOX +#include +#else +#define WIN32_LEAN_AND_MEAN +#include +#endif +#include "pythread.h" + +/* helper to allow us to interrupt sleep() on Windows*/ +static HANDLE hInterruptEvent = NULL; +static BOOL WINAPI PyCtrlHandler(DWORD dwCtrlType) +{ + SetEvent(hInterruptEvent); + /* allow other default handlers to be called. + Default Python handler will setup the + KeyboardInterrupt exception. + */ + return FALSE; +} +static long main_thread; + + +#if defined(__BORLANDC__) +/* These overrides not needed for Win32 */ +#define timezone _timezone +#define tzname _tzname +#define daylight _daylight +#endif /* __BORLANDC__ */ +#endif /* MS_WINDOWS */ +#endif /* !__WATCOMC__ || __QNX__ */ + +#if defined(MS_WINDOWS) && !defined(MS_WIN64) && !defined(__BORLANDC__) +/* Win32 has better clock replacement + XXX Win64 does not yet, but might when the platform matures. */ +#undef HAVE_CLOCK /* We have our own version down below */ +#endif /* MS_WINDOWS && !MS_WIN64 */ + +#if defined(PYOS_OS2) +#define INCL_DOS +#define INCL_ERRORS +#include +#endif + +#if defined(PYCC_VACPP) +#include +#endif + +#ifdef __BEOS__ +#include +/* For bigtime_t, snooze(). - [cjh] */ +#include +#include +#endif + +#ifdef RISCOS +extern int riscos_sleep(double); +#endif + +/* Forward declarations */ +static int floatsleep(double); +static double floattime(void); + +/* For Y2K check */ +static PyObject *moddict; + +#ifdef macintosh +/* Our own timezone. We have enough information to deduce whether +** DST is on currently, but unfortunately we cannot put it to good +** use because we don't know the rules (and that is needed to have +** localtime() return correct tm_isdst values for times other than +** the current time. So, we cop out and only tell the user the current +** timezone. +*/ +static long timezone; + +static void +initmactimezone(void) +{ + MachineLocation loc; + long delta; + + ReadLocation(&loc); + + if (loc.latitude == 0 && loc.longitude == 0 && loc.u.gmtDelta == 0) + return; + + delta = loc.u.gmtDelta & 0x00FFFFFF; + + if (delta & 0x00800000) + delta |= 0xFF000000; + + timezone = -delta; +} +#endif /* macintosh */ + + +static PyObject * +time_time(PyObject *self, PyObject *args) +{ + double secs; + if (!PyArg_ParseTuple(args, ":time")) + return NULL; + secs = floattime(); + if (secs == 0.0) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + return PyFloat_FromDouble(secs); +} + +PyDoc_STRVAR(time_doc, +"time() -> floating point number\n\ +\n\ +Return the current time in seconds since the Epoch.\n\ +Fractions of a second may be present if the system clock provides them."); + +#ifdef HAVE_CLOCK + +#ifndef CLOCKS_PER_SEC +#ifdef CLK_TCK +#define CLOCKS_PER_SEC CLK_TCK +#else +#define CLOCKS_PER_SEC 1000000 +#endif +#endif + +static PyObject * +time_clock(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":clock")) + return NULL; + return PyFloat_FromDouble(((double)clock()) / CLOCKS_PER_SEC); +} +#endif /* HAVE_CLOCK */ + +#if defined(MS_WINDOWS) && !defined(MS_WIN64) && !defined(__BORLANDC__) +/* Due to Mark Hammond and Tim Peters */ +static PyObject * +time_clock(PyObject *self, PyObject *args) +{ + static LARGE_INTEGER ctrStart; + static double divisor = 0.0; + LARGE_INTEGER now; + double diff; + + if (!PyArg_ParseTuple(args, ":clock")) + return NULL; + + if (divisor == 0.0) { + LARGE_INTEGER freq; + QueryPerformanceCounter(&ctrStart); + if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) { + /* Unlikely to happen - this works on all intel + machines at least! Revert to clock() */ + return PyFloat_FromDouble(clock()); + } + divisor = (double)freq.QuadPart; + } + QueryPerformanceCounter(&now); + diff = (double)(now.QuadPart - ctrStart.QuadPart); + return PyFloat_FromDouble(diff / divisor); +} + +#define HAVE_CLOCK /* So it gets included in the methods */ +#endif /* MS_WINDOWS && !MS_WIN64 */ + +#ifdef HAVE_CLOCK +PyDoc_STRVAR(clock_doc, +"clock() -> floating point number\n\ +\n\ +Return the CPU time or real time since the start of the process or since\n\ +the first call to clock(). This has as much precision as the system\n\ +records."); +#endif + +static PyObject * +time_sleep(PyObject *self, PyObject *args) +{ + double secs; + if (!PyArg_ParseTuple(args, "d:sleep", &secs)) + return NULL; + if (floatsleep(secs) != 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(sleep_doc, +"sleep(seconds)\n\ +\n\ +Delay execution for a given number of seconds. The argument may be\n\ +a floating point number for subsecond precision."); + +static PyStructSequence_Field struct_time_type_fields[] = { + {"tm_year", NULL}, + {"tm_mon", NULL}, + {"tm_mday", NULL}, + {"tm_hour", NULL}, + {"tm_min", NULL}, + {"tm_sec", NULL}, + {"tm_wday", NULL}, + {"tm_yday", NULL}, + {"tm_isdst", NULL}, + {0} +}; + +static PyStructSequence_Desc struct_time_type_desc = { + "time.struct_time", + NULL, + struct_time_type_fields, + 9, +}; + +static PyTypeObject StructTimeType; + +static PyObject * +tmtotuple(struct tm *p) +{ + PyObject *v = PyStructSequence_New(&StructTimeType); + if (v == NULL) + return NULL; + +#define SET(i,val) PyStructSequence_SET_ITEM(v, i, PyInt_FromLong((long) val)) + + SET(0, p->tm_year + 1900); + SET(1, p->tm_mon + 1); /* Want January == 1 */ + SET(2, p->tm_mday); + SET(3, p->tm_hour); + SET(4, p->tm_min); + SET(5, p->tm_sec); + SET(6, (p->tm_wday + 6) % 7); /* Want Monday == 0 */ + SET(7, p->tm_yday + 1); /* Want January, 1 == 1 */ + SET(8, p->tm_isdst); +#undef SET + if (PyErr_Occurred()) { + Py_XDECREF(v); + return NULL; + } + + return v; +} + +static PyObject * +time_convert(time_t when, struct tm * (*function)(const time_t *)) +{ + struct tm *p; + errno = 0; + p = function(&when); + if (p == NULL) { +#ifdef EINVAL + if (errno == 0) + errno = EINVAL; +#endif + return PyErr_SetFromErrno(PyExc_ValueError); + } + return tmtotuple(p); +} + +static PyObject * +time_gmtime(PyObject *self, PyObject *args) +{ + double when; + if (PyTuple_Size(args) == 0) + when = floattime(); + if (!PyArg_ParseTuple(args, "|d:gmtime", &when)) + return NULL; + return time_convert((time_t)when, gmtime); +} + +PyDoc_STRVAR(gmtime_doc, +"gmtime([seconds]) -> (tm_year, tm_mon, tm_day, tm_hour, tm_min,\n\ + tm_sec, tm_wday, tm_yday, tm_isdst)\n\ +\n\ +Convert seconds since the Epoch to a time tuple expressing UTC (a.k.a.\n\ +GMT). When 'seconds' is not passed in, convert the current time instead."); + +static PyObject * +time_localtime(PyObject *self, PyObject *args) +{ + double when; + if (PyTuple_Size(args) == 0) + when = floattime(); + if (!PyArg_ParseTuple(args, "|d:localtime", &when)) + return NULL; + return time_convert((time_t)when, localtime); +} + +PyDoc_STRVAR(localtime_doc, +"localtime([seconds]) -> (tm_year,tm_mon,tm_day,tm_hour,tm_min,tm_sec,tm_wday,tm_yday,tm_isdst)\n\ +\n\ +Convert seconds since the Epoch to a time tuple expressing local time.\n\ +When 'seconds' is not passed in, convert the current time instead."); + +static int +gettmarg(PyObject *args, struct tm *p) +{ + int y; + memset((void *) p, '\0', sizeof(struct tm)); + + if (!PyArg_Parse(args, "(iiiiiiiii)", + &y, + &p->tm_mon, + &p->tm_mday, + &p->tm_hour, + &p->tm_min, + &p->tm_sec, + &p->tm_wday, + &p->tm_yday, + &p->tm_isdst)) + return 0; + if (y < 1900) { + PyObject *accept = PyDict_GetItemString(moddict, + "accept2dyear"); + if (accept == NULL || !PyInt_Check(accept) || + PyInt_AsLong(accept) == 0) { + PyErr_SetString(PyExc_ValueError, + "year >= 1900 required"); + return 0; + } + if (69 <= y && y <= 99) + y += 1900; + else if (0 <= y && y <= 68) + y += 2000; + else { + PyErr_SetString(PyExc_ValueError, + "year out of range"); + return 0; + } + } + p->tm_year = y - 1900; + p->tm_mon--; + p->tm_wday = (p->tm_wday + 1) % 7; + p->tm_yday--; + return 1; +} + +#ifdef HAVE_STRFTIME +static PyObject * +time_strftime(PyObject *self, PyObject *args) +{ + PyObject *tup = NULL; + struct tm buf; + const char *fmt; + size_t fmtlen, buflen; + char *outbuf = 0; + size_t i; + + memset((void *) &buf, '\0', sizeof(buf)); + + if (!PyArg_ParseTuple(args, "s|O:strftime", &fmt, &tup)) + return NULL; + + if (tup == NULL) { + time_t tt = time(NULL); + buf = *localtime(&tt); + } else if (!gettmarg(tup, &buf)) + return NULL; + + fmtlen = strlen(fmt); + + /* I hate these functions that presume you know how big the output + * will be ahead of time... + */ + for (i = 1024; ; i += i) { + outbuf = malloc(i); + if (outbuf == NULL) { + return PyErr_NoMemory(); + } + buflen = strftime(outbuf, i, fmt, &buf); + if (buflen > 0 || i >= 256 * fmtlen) { + /* If the buffer is 256 times as long as the format, + it's probably not failing for lack of room! + More likely, the format yields an empty result, + e.g. an empty format, or %Z when the timezone + is unknown. */ + PyObject *ret; + ret = PyString_FromStringAndSize(outbuf, buflen); + free(outbuf); + return ret; + } + free(outbuf); + } +} + +PyDoc_STRVAR(strftime_doc, +"strftime(format[, tuple]) -> string\n\ +\n\ +Convert a time tuple to a string according to a format specification.\n\ +See the library reference manual for formatting codes. When the time tuple\n\ +is not present, current time as returned by localtime() is used."); +#endif /* HAVE_STRFTIME */ + +static PyObject * +time_strptime(PyObject *self, PyObject *args) +{ + PyObject *strptime_module = PyImport_ImportModule("_strptime"); + PyObject *strptime_result; + + if (!strptime_module) + return NULL; + strptime_result = PyObject_CallMethod(strptime_module, "strptime", "O", args); + Py_DECREF(strptime_module); + return strptime_result; +} + +PyDoc_STRVAR(strptime_doc, +"strptime(string, format) -> struct_time\n\ +\n\ +Parse a string to a time tuple according to a format specification.\n\ +See the library reference manual for formatting codes (same as strftime())."); + + +static PyObject * +time_asctime(PyObject *self, PyObject *args) +{ + PyObject *tup = NULL; + struct tm buf; + char *p; + if (!PyArg_ParseTuple(args, "|O:asctime", &tup)) + return NULL; + if (tup == NULL) { + time_t tt = time(NULL); + buf = *localtime(&tt); + } else if (!gettmarg(tup, &buf)) + return NULL; + p = asctime(&buf); + if (p[24] == '\n') + p[24] = '\0'; + return PyString_FromString(p); +} + +PyDoc_STRVAR(asctime_doc, +"asctime([tuple]) -> string\n\ +\n\ +Convert a time tuple to a string, e.g. 'Sat Jun 06 16:26:11 1998'.\n\ +When the time tuple is not present, current time as returned by localtime()\n\ +is used."); + +static PyObject * +time_ctime(PyObject *self, PyObject *args) +{ + double dt; + time_t tt; + char *p; + + if (PyTuple_Size(args) == 0) + tt = time(NULL); + else { + if (!PyArg_ParseTuple(args, "|d:ctime", &dt)) + return NULL; + tt = (time_t)dt; + } + p = ctime(&tt); + if (p == NULL) { + PyErr_SetString(PyExc_ValueError, "unconvertible time"); + return NULL; + } + if (p[24] == '\n') + p[24] = '\0'; + return PyString_FromString(p); +} + +PyDoc_STRVAR(ctime_doc, +"ctime(seconds) -> string\n\ +\n\ +Convert a time in seconds since the Epoch to a string in local time.\n\ +This is equivalent to asctime(localtime(seconds)). When the time tuple is\n\ +not present, current time as returned by localtime() is used."); + +#ifdef HAVE_MKTIME +static PyObject * +time_mktime(PyObject *self, PyObject *args) +{ + PyObject *tup; + struct tm buf; + time_t tt; + if (!PyArg_ParseTuple(args, "O:mktime", &tup)) + return NULL; + tt = time(&tt); + buf = *localtime(&tt); + if (!gettmarg(tup, &buf)) + return NULL; + tt = mktime(&buf); + if (tt == (time_t)(-1)) { + PyErr_SetString(PyExc_OverflowError, + "mktime argument out of range"); + return NULL; + } + return PyFloat_FromDouble((double)tt); +} + +PyDoc_STRVAR(mktime_doc, +"mktime(tuple) -> floating point number\n\ +\n\ +Convert a time tuple in local time to seconds since the Epoch."); +#endif /* HAVE_MKTIME */ + +#ifdef HAVE_WORKING_TZSET +void inittimezone(PyObject *module); + +static PyObject * +time_tzset(PyObject *self, PyObject *args) +{ + PyObject* m; + + if (!PyArg_ParseTuple(args, ":tzset")) + return NULL; + + m = PyImport_ImportModule("time"); + if (m == NULL) { + return NULL; + } + + tzset(); + + /* Reset timezone, altzone, daylight and tzname */ + inittimezone(m); + Py_DECREF(m); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(tzset_doc, +"tzset(zone)\n\ +\n\ +Initialize, or reinitialize, the local timezone to the value stored in\n\ +os.environ['TZ']. The TZ environment variable should be specified in\n\ +standard Uniz timezone format as documented in the tzset man page\n\ +(eg. 'US/Eastern', 'Europe/Amsterdam'). Unknown timezones will silently\n\ +fall back to UTC. If the TZ environment variable is not set, the local\n\ +timezone is set to the systems best guess of wallclock time.\n\ +Changing the TZ environment variable without calling tzset *may* change\n\ +the local timezone used by methods such as localtime, but this behaviour\n\ +should not be relied on."); +#endif /* HAVE_WORKING_TZSET */ + +void inittimezone(PyObject *m) { + /* This code moved from inittime wholesale to allow calling it from + time_tzset. In the future, some parts of it can be moved back + (for platforms that don't HAVE_WORKING_TZSET, when we know what they + are), and the extranious calls to tzset(3) should be removed. + I havn't done this yet, as I don't want to change this code as + little as possible when introducing the time.tzset and time.tzsetwall + methods. This should simply be a method of doing the following once, + at the top of this function and removing the call to tzset() from + time_tzset(): + + #ifdef HAVE_TZSET + tzset() + #endif + + And I'm lazy and hate C so nyer. + */ +#if defined(HAVE_TZNAME) && !defined(__GLIBC__) && !defined(__CYGWIN__) + tzset(); +#ifdef PYOS_OS2 + PyModule_AddIntConstant(m, "timezone", _timezone); +#else /* !PYOS_OS2 */ + PyModule_AddIntConstant(m, "timezone", timezone); +#endif /* PYOS_OS2 */ +#ifdef HAVE_ALTZONE + PyModule_AddIntConstant(m, "altzone", altzone); +#else +#ifdef PYOS_OS2 + PyModule_AddIntConstant(m, "altzone", _timezone-3600); +#else /* !PYOS_OS2 */ + PyModule_AddIntConstant(m, "altzone", timezone-3600); +#endif /* PYOS_OS2 */ +#endif + PyModule_AddIntConstant(m, "daylight", daylight); + PyModule_AddObject(m, "tzname", + Py_BuildValue("(zz)", tzname[0], tzname[1])); +#else /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/ +#ifdef HAVE_STRUCT_TM_TM_ZONE + { +#define YEAR ((time_t)((365 * 24 + 6) * 3600)) + time_t t; + struct tm *p; + long janzone, julyzone; + char janname[10], julyname[10]; + t = (time((time_t *)0) / YEAR) * YEAR; + p = localtime(&t); + janzone = -p->tm_gmtoff; + strncpy(janname, p->tm_zone ? p->tm_zone : " ", 9); + janname[9] = '\0'; + t += YEAR/2; + p = localtime(&t); + julyzone = -p->tm_gmtoff; + strncpy(julyname, p->tm_zone ? p->tm_zone : " ", 9); + julyname[9] = '\0'; + + if( janzone < julyzone ) { + /* DST is reversed in the southern hemisphere */ + PyModule_AddIntConstant(m, "timezone", julyzone); + PyModule_AddIntConstant(m, "altzone", janzone); + PyModule_AddIntConstant(m, "daylight", + janzone != julyzone); + PyModule_AddObject(m, "tzname", + Py_BuildValue("(zz)", + julyname, janname)); + } else { + PyModule_AddIntConstant(m, "timezone", janzone); + PyModule_AddIntConstant(m, "altzone", julyzone); + PyModule_AddIntConstant(m, "daylight", + janzone != julyzone); + PyModule_AddObject(m, "tzname", + Py_BuildValue("(zz)", + janname, julyname)); + } + } +#else +#ifdef macintosh + /* The only thing we can obtain is the current timezone + ** (and whether dst is currently _active_, but that is not what + ** we're looking for:-( ) + */ + initmactimezone(); + PyModule_AddIntConstant(m, "timezone", timezone); + PyModule_AddIntConstant(m, "altzone", timezone); + PyModule_AddIntConstant(m, "daylight", 0); + PyModule_AddObject(m, "tzname", Py_BuildValue("(zz)", "", "")); +#endif /* macintosh */ +#endif /* HAVE_STRUCT_TM_TM_ZONE */ +#ifdef __CYGWIN__ + tzset(); + PyModule_AddIntConstant(m, "timezone", _timezone); + PyModule_AddIntConstant(m, "altzone", _timezone); + PyModule_AddIntConstant(m, "daylight", _daylight); + PyModule_AddObject(m, "tzname", + Py_BuildValue("(zz)", _tzname[0], _tzname[1])); +#endif /* __CYGWIN__ */ +#endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/ +} + + +static PyMethodDef time_methods[] = { + {"time", time_time, METH_VARARGS, time_doc}, +#ifdef HAVE_CLOCK + {"clock", time_clock, METH_VARARGS, clock_doc}, +#endif + {"sleep", time_sleep, METH_VARARGS, sleep_doc}, + {"gmtime", time_gmtime, METH_VARARGS, gmtime_doc}, + {"localtime", time_localtime, METH_VARARGS, localtime_doc}, + {"asctime", time_asctime, METH_VARARGS, asctime_doc}, + {"ctime", time_ctime, METH_VARARGS, ctime_doc}, +#ifdef HAVE_MKTIME + {"mktime", time_mktime, METH_VARARGS, mktime_doc}, +#endif +#ifdef HAVE_STRFTIME + {"strftime", time_strftime, METH_VARARGS, strftime_doc}, +#endif + {"strptime", time_strptime, METH_VARARGS, strptime_doc}, +#ifdef HAVE_WORKING_TZSET + {"tzset", time_tzset, METH_VARARGS, tzset_doc}, +#endif + {NULL, NULL} /* sentinel */ +}; + + +PyDoc_STRVAR(module_doc, +"This module provides various functions to manipulate time values.\n\ +\n\ +There are two standard representations of time. One is the number\n\ +of seconds since the Epoch, in UTC (a.k.a. GMT). It may be an integer\n\ +or a floating point number (to represent fractions of seconds).\n\ +The Epoch is system-defined; on Unix, it is generally January 1st, 1970.\n\ +The actual value can be retrieved by calling gmtime(0).\n\ +\n\ +The other representation is a tuple of 9 integers giving local time.\n\ +The tuple items are:\n\ + year (four digits, e.g. 1998)\n\ + month (1-12)\n\ + day (1-31)\n\ + hours (0-23)\n\ + minutes (0-59)\n\ + seconds (0-59)\n\ + weekday (0-6, Monday is 0)\n\ + Julian day (day in the year, 1-366)\n\ + DST (Daylight Savings Time) flag (-1, 0 or 1)\n\ +If the DST flag is 0, the time is given in the regular time zone;\n\ +if it is 1, the time is given in the DST time zone;\n\ +if it is -1, mktime() should guess based on the date and time.\n\ +\n\ +Variables:\n\ +\n\ +timezone -- difference in seconds between UTC and local standard time\n\ +altzone -- difference in seconds between UTC and local DST time\n\ +daylight -- whether local time should reflect DST\n\ +tzname -- tuple of (standard time zone name, DST time zone name)\n\ +\n\ +Functions:\n\ +\n\ +time() -- return current time in seconds since the Epoch as a float\n\ +clock() -- return CPU time since process start as a float\n\ +sleep() -- delay for a number of seconds given as a float\n\ +gmtime() -- convert seconds since Epoch to UTC tuple\n\ +localtime() -- convert seconds since Epoch to local time tuple\n\ +asctime() -- convert time tuple to string\n\ +ctime() -- convert time in seconds to string\n\ +mktime() -- convert local time tuple to seconds since Epoch\n\ +strftime() -- convert time tuple to string according to format specification\n\ +strptime() -- parse string to time tuple according to format specification\n\ +tzset() -- change the local timezone"); + + +PyMODINIT_FUNC +inittime(void) +{ + PyObject *m; + char *p; + m = Py_InitModule3("time", time_methods, module_doc); + + /* Accept 2-digit dates unless PYTHONY2K is set and non-empty */ + p = Py_GETENV("PYTHONY2K"); + PyModule_AddIntConstant(m, "accept2dyear", (long) (!p || !*p)); + /* Squirrel away the module's dictionary for the y2k check */ + moddict = PyModule_GetDict(m); + Py_INCREF(moddict); + + /* Set, or reset, module variables like time.timezone */ + inittimezone(m); + +#if defined(MS_WINDOWS) && !defined(MS_XBOX) + /* Helper to allow interrupts for Windows. + If Ctrl+C event delivered while not sleeping + it will be ignored. + */ + main_thread = PyThread_get_thread_ident(); + hInterruptEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + SetConsoleCtrlHandler( PyCtrlHandler, TRUE); +#endif /* MS_WINDOWS */ + PyStructSequence_InitType(&StructTimeType, &struct_time_type_desc); + Py_INCREF(&StructTimeType); + PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType); +} + + +/* Implement floattime() for various platforms */ + +static double +floattime(void) +{ + /* There are three ways to get the time: + (1) gettimeofday() -- resolution in microseconds + (2) ftime() -- resolution in milliseconds + (3) time() -- resolution in seconds + In all cases the return value is a float in seconds. + Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may + fail, so we fall back on ftime() or time(). + Note: clock resolution does not imply clock accuracy! */ +#ifdef HAVE_GETTIMEOFDAY + { + struct timeval t; +#ifdef GETTIMEOFDAY_NO_TZ + if (gettimeofday(&t) == 0) + return (double)t.tv_sec + t.tv_usec*0.000001; +#else /* !GETTIMEOFDAY_NO_TZ */ + if (gettimeofday(&t, (struct timezone *)NULL) == 0) + return (double)t.tv_sec + t.tv_usec*0.000001; +#endif /* !GETTIMEOFDAY_NO_TZ */ + } +#endif /* !HAVE_GETTIMEOFDAY */ + { +#if defined(HAVE_FTIME) + struct timeb t; + ftime(&t); + return (double)t.time + (double)t.millitm * (double)0.001; +#else /* !HAVE_FTIME */ + time_t secs; + time(&secs); + return (double)secs; +#endif /* !HAVE_FTIME */ + } +} + + +/* Implement floatsleep() for various platforms. + When interrupted (or when another error occurs), return -1 and + set an exception; else return 0. */ + +static int +floatsleep(double secs) +{ +/* XXX Should test for MS_WINDOWS first! */ +#if defined(HAVE_SELECT) && !defined(__BEOS__) && !defined(__EMX__) + struct timeval t; + double frac; + frac = fmod(secs, 1.0); + secs = floor(secs); + t.tv_sec = (long)secs; + t.tv_usec = (long)(frac*1000000.0); + Py_BEGIN_ALLOW_THREADS + if (select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t) != 0) { +#ifdef EINTR + if (errno != EINTR) { +#else + if (1) { +#endif + Py_BLOCK_THREADS + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + } + Py_END_ALLOW_THREADS +#elif defined(macintosh) +#define MacTicks (* (long *)0x16A) + long deadline; + deadline = MacTicks + (long)(secs * 60.0); + while (MacTicks < deadline) { + /* XXX Should call some yielding function here */ + if (PyErr_CheckSignals()) + return -1; + } +#elif defined(__WATCOMC__) && !defined(__QNX__) + /* XXX Can't interrupt this sleep */ + Py_BEGIN_ALLOW_THREADS + delay((int)(secs * 1000 + 0.5)); /* delay() uses milliseconds */ + Py_END_ALLOW_THREADS +#elif defined(MS_WINDOWS) + { + double millisecs = secs * 1000.0; + unsigned long ul_millis; + + if (millisecs > (double)ULONG_MAX) { + PyErr_SetString(PyExc_OverflowError, + "sleep length is too large"); + return -1; + } + Py_BEGIN_ALLOW_THREADS + /* Allow sleep(0) to maintain win32 semantics, and as decreed + * by Guido, only the main thread can be interrupted. + */ + ul_millis = (unsigned long)millisecs; + if (ul_millis == 0 || + main_thread != PyThread_get_thread_ident()) + Sleep(ul_millis); + else { + DWORD rc; + ResetEvent(hInterruptEvent); + rc = WaitForSingleObject(hInterruptEvent, ul_millis); + if (rc == WAIT_OBJECT_0) { + /* Yield to make sure real Python signal + * handler called. + */ + Sleep(1); + Py_BLOCK_THREADS + errno = EINTR; + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + } + Py_END_ALLOW_THREADS + } +#elif defined(PYOS_OS2) + /* This Sleep *IS* Interruptable by Exceptions */ + Py_BEGIN_ALLOW_THREADS + if (DosSleep(secs * 1000) != NO_ERROR) { + Py_BLOCK_THREADS + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + Py_END_ALLOW_THREADS +#elif defined(__BEOS__) + /* This sleep *CAN BE* interrupted. */ + { + if( secs <= 0.0 ) { + return; + } + + Py_BEGIN_ALLOW_THREADS + /* BeOS snooze() is in microseconds... */ + if( snooze( (bigtime_t)( secs * 1000.0 * 1000.0 ) ) == B_INTERRUPTED ) { + Py_BLOCK_THREADS + PyErr_SetFromErrno( PyExc_IOError ); + return -1; + } + Py_END_ALLOW_THREADS + } +#elif defined(RISCOS) + if (secs <= 0.0) + return 0; + Py_BEGIN_ALLOW_THREADS + /* This sleep *CAN BE* interrupted. */ + if ( riscos_sleep(secs) ) + return -1; + Py_END_ALLOW_THREADS +#elif defined(PLAN9) + { + double millisecs = secs * 1000.0; + if (millisecs > (double)LONG_MAX) { + PyErr_SetString(PyExc_OverflowError, "sleep length is too large"); + return -1; + } + /* This sleep *CAN BE* interrupted. */ + Py_BEGIN_ALLOW_THREADS + if(sleep((long)millisecs) < 0){ + Py_BLOCK_THREADS + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + Py_END_ALLOW_THREADS + } +#else + /* XXX Can't interrupt this sleep */ + Py_BEGIN_ALLOW_THREADS + sleep((int)secs); + Py_END_ALLOW_THREADS +#endif + + return 0; +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timing.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timing.h new file mode 100644 index 00000000..bef8bcea --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timing.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1993 George V. Neville-Neil + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by George V. Neville-Neil + * 4. The name, George Neville-Neil may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _TIMING_H_ +#define _TIMING_H_ + +#ifdef TIME_WITH_SYS_TIME +#include +#include +#else /* !TIME_WITH_SYS_TIME */ +#ifdef HAVE_SYS_TIME_H +#include +#else /* !HAVE_SYS_TIME_H */ +#include +#endif /* !HAVE_SYS_TIME_H */ +#endif /* !TIME_WITH_SYS_TIME */ + +static struct timeval aftertp, beforetp; + +#define BEGINTIMING gettimeofday(&beforetp, NULL) + +#define ENDTIMING gettimeofday(&aftertp, NULL); \ + if(beforetp.tv_usec > aftertp.tv_usec) \ + { \ + aftertp.tv_usec += 1000000; \ + aftertp.tv_sec--; \ + } + +#define TIMINGUS (((aftertp.tv_sec - beforetp.tv_sec) * 1000000) + \ + (aftertp.tv_usec - beforetp.tv_usec)) + +#define TIMINGMS (((aftertp.tv_sec - beforetp.tv_sec) * 1000) + \ + ((aftertp.tv_usec - beforetp.tv_usec) / 1000)) + +#define TIMINGS ((aftertp.tv_sec - beforetp.tv_sec) + \ + (aftertp.tv_usec - beforetp.tv_usec) / 1000000) + +#endif /* _TIMING_H_ */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timingmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timingmodule.c new file mode 100644 index 00000000..43de9224 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/timingmodule.c @@ -0,0 +1,58 @@ +/* + * Author: George V. Neville-Neil + */ + +#include "Python.h" + +/* Our stuff... */ +#include "timing.h" + +static PyObject * +start_timing(PyObject *self) +{ + Py_INCREF(Py_None); + BEGINTIMING; + return Py_None; +} + +static PyObject * +finish_timing(PyObject *self) +{ + ENDTIMING + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +seconds(PyObject *self) +{ + return PyInt_FromLong(TIMINGS); +} + +static PyObject * +milli(PyObject *self) +{ + return PyInt_FromLong(TIMINGMS); +} + +static PyObject * +micro(PyObject *self) +{ + return PyInt_FromLong(TIMINGUS); +} + + +static PyMethodDef timing_methods[] = { + {"start", (PyCFunction)start_timing, METH_NOARGS}, + {"finish", (PyCFunction)finish_timing, METH_NOARGS}, + {"seconds", (PyCFunction)seconds, METH_NOARGS}, + {"milli", (PyCFunction)milli, METH_NOARGS}, + {"micro", (PyCFunction)micro, METH_NOARGS}, + {NULL, NULL} +}; + + +PyMODINIT_FUNC inittiming(void) +{ + (void)Py_InitModule("timing", timing_methods); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/tkappinit.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/tkappinit.c new file mode 100644 index 00000000..f17f8592 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/tkappinit.c @@ -0,0 +1,139 @@ +/* appinit.c -- Tcl and Tk application initialization. + + The function Tcl_AppInit() below initializes various Tcl packages. + It is called for each Tcl interpreter created by _tkinter.create(). + It needs to be compiled with -DWITH_ flags for each package + that you are statically linking with. You may have to add sections + for packages not yet listed below. + + Note that those packages for which Tcl_StaticPackage() is called with + a NULL first argument are known as "static loadable" packages to + Tcl but not actually initialized. To use these, you have to load + it explicitly, e.g. tkapp.eval("load {} Blt"). + */ + +#include +#include + +int +Tcl_AppInit(Tcl_Interp *interp) +{ + Tk_Window main_window; + +#ifdef TK_AQUA +#ifndef MAX_PATH_LEN +#define MAX_PATH_LEN 1024 +#endif + char tclLibPath[MAX_PATH_LEN], tkLibPath[MAX_PATH_LEN]; + Tcl_Obj* pathPtr; + + /* pre- Tcl_Init code copied from tkMacOSXAppInit.c */ + Tk_MacOSXOpenBundleResources (interp, "com.tcltk.tcllibrary", + tclLibPath, MAX_PATH_LEN, 0); + + if (tclLibPath[0] != '\0') { + Tcl_SetVar(interp, "tcl_library", tclLibPath, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "tclDefaultLibrary", tclLibPath, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "tcl_pkgPath", tclLibPath, TCL_GLOBAL_ONLY); + } + + if (tclLibPath[0] != '\0') { + Tcl_SetVar(interp, "tcl_library", tclLibPath, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "tclDefaultLibrary", tclLibPath, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "tcl_pkgPath", tclLibPath, TCL_GLOBAL_ONLY); + } +#endif + if (Tcl_Init (interp) == TCL_ERROR) + return TCL_ERROR; + +#ifdef TK_AQUA + /* pre- Tk_Init code copied from tkMacOSXAppInit.c */ + Tk_MacOSXOpenBundleResources (interp, "com.tcltk.tklibrary", + tkLibPath, MAX_PATH_LEN, 1); + + if (tclLibPath[0] != '\0') { + pathPtr = Tcl_NewStringObj(tclLibPath, -1); + } else { + Tcl_Obj *pathPtr = TclGetLibraryPath(); + } + + if (tkLibPath[0] != '\0') { + Tcl_Obj *objPtr; + + Tcl_SetVar(interp, "tk_library", tkLibPath, TCL_GLOBAL_ONLY); + objPtr = Tcl_NewStringObj(tkLibPath, -1); + Tcl_ListObjAppendElement(NULL, pathPtr, objPtr); + } + + TclSetLibraryPath(pathPtr); +#endif + + if (Tk_Init (interp) == TCL_ERROR) + return TCL_ERROR; + + main_window = Tk_MainWindow(interp); + +#ifdef TK_AQUA + TkMacOSXInitAppleEvents(interp); + TkMacOSXInitMenus(interp); +#endif + +#ifdef WITH_MOREBUTTONS + { + extern Tcl_CmdProc studButtonCmd; + extern Tcl_CmdProc triButtonCmd; + + Tcl_CreateCommand(interp, "studbutton", studButtonCmd, + (ClientData) main_window, NULL); + Tcl_CreateCommand(interp, "tributton", triButtonCmd, + (ClientData) main_window, NULL); + } +#endif + +#ifdef WITH_PIL /* 0.2b5 and later -- not yet released as of May 14 */ + { + extern void TkImaging_Init(Tcl_Interp *); + TkImaging_Init(interp); + /* XXX TkImaging_Init() doesn't have the right return type */ + /*Tcl_StaticPackage(interp, "Imaging", TkImaging_Init, NULL);*/ + } +#endif + +#ifdef WITH_PIL_OLD /* 0.2b4 and earlier */ + { + extern void TkImaging_Init(void); + /* XXX TkImaging_Init() doesn't have the right prototype */ + /*Tcl_StaticPackage(interp, "Imaging", TkImaging_Init, NULL);*/ + } +#endif + +#ifdef WITH_TIX + { + extern int Tix_Init(Tcl_Interp *interp); + extern int Tix_SafeInit(Tcl_Interp *interp); + Tcl_StaticPackage(NULL, "Tix", Tix_Init, Tix_SafeInit); + } +#endif + +#ifdef WITH_BLT + { + extern int Blt_Init(Tcl_Interp *); + extern int Blt_SafeInit(Tcl_Interp *); + Tcl_StaticPackage(NULL, "Blt", Blt_Init, Blt_SafeInit); + } +#endif + +#ifdef WITH_TOGL + { + /* XXX I've heard rumors that this doesn't work */ + extern int Togl_Init(Tcl_Interp *); + /* XXX Is there no Togl_SafeInit? */ + Tcl_StaticPackage(NULL, "Togl", Togl_Init, NULL); + } +#endif + +#ifdef WITH_XXX + +#endif + return TCL_OK; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodedata.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodedata.c new file mode 100644 index 00000000..06dbd673 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodedata.c @@ -0,0 +1,899 @@ +/* ------------------------------------------------------------------------ + + unicodedata -- Provides access to the Unicode 3.2 data base. + + Data was extracted from the Unicode 3.2 UnicodeData.txt file. + + Written by Marc-Andre Lemburg (mal@lemburg.com). + Modified for Python 2.0 by Fredrik Lundh (fredrik@pythonware.com) + Modified by Martin v. Löwis (martin@v.loewis.de) + + Copyright (c) Corporation for National Research Initiatives. + + ------------------------------------------------------------------------ */ + +#include "Python.h" +#include "ucnhash.h" + +/* character properties */ + +typedef struct { + const unsigned char category; /* index into + _PyUnicode_CategoryNames */ + const unsigned char combining; /* combining class value 0 - 255 */ + const unsigned char bidirectional; /* index into + _PyUnicode_BidirectionalNames */ + const unsigned char mirrored; /* true if mirrored in bidir mode */ +} _PyUnicode_DatabaseRecord; + +/* data file generated by Tools/unicode/makeunicodedata.py */ +#include "unicodedata_db.h" + +static const _PyUnicode_DatabaseRecord* +_getrecord_ex(Py_UCS4 code) +{ + int index; + if (code >= 0x110000) + index = 0; + else { + index = index1[(code>>SHIFT)]; + index = index2[(index<category; + return PyString_FromString(_PyUnicode_CategoryNames[index]); +} + +static PyObject * +unicodedata_bidirectional(PyObject *self, PyObject *args) +{ + PyUnicodeObject *v; + int index; + + if (!PyArg_ParseTuple(args, "O!:bidirectional", + &PyUnicode_Type, &v)) + return NULL; + if (PyUnicode_GET_SIZE(v) != 1) { + PyErr_SetString(PyExc_TypeError, + "need a single Unicode character as parameter"); + return NULL; + } + index = (int) _getrecord(v)->bidirectional; + return PyString_FromString(_PyUnicode_BidirectionalNames[index]); +} + +static PyObject * +unicodedata_combining(PyObject *self, PyObject *args) +{ + PyUnicodeObject *v; + + if (!PyArg_ParseTuple(args, "O!:combining", + &PyUnicode_Type, &v)) + return NULL; + if (PyUnicode_GET_SIZE(v) != 1) { + PyErr_SetString(PyExc_TypeError, + "need a single Unicode character as parameter"); + return NULL; + } + return PyInt_FromLong((int) _getrecord(v)->combining); +} + +static PyObject * +unicodedata_mirrored(PyObject *self, PyObject *args) +{ + PyUnicodeObject *v; + + if (!PyArg_ParseTuple(args, "O!:mirrored", + &PyUnicode_Type, &v)) + return NULL; + if (PyUnicode_GET_SIZE(v) != 1) { + PyErr_SetString(PyExc_TypeError, + "need a single Unicode character as parameter"); + return NULL; + } + return PyInt_FromLong((int) _getrecord(v)->mirrored); +} + +static PyObject * +unicodedata_decomposition(PyObject *self, PyObject *args) +{ + PyUnicodeObject *v; + char decomp[256]; + int code, index, count, i; + + if (!PyArg_ParseTuple(args, "O!:decomposition", + &PyUnicode_Type, &v)) + return NULL; + if (PyUnicode_GET_SIZE(v) != 1) { + PyErr_SetString(PyExc_TypeError, + "need a single Unicode character as parameter"); + return NULL; + } + + code = (int) *PyUnicode_AS_UNICODE(v); + + if (code < 0 || code >= 0x110000) + index = 0; + else { + index = decomp_index1[(code>>DECOMP_SHIFT)]; + index = decomp_index2[(index<> 8; + + /* XXX: could allocate the PyString up front instead + (strlen(prefix) + 5 * count + 1 bytes) */ + + /* copy prefix */ + i = strlen(decomp_prefix[decomp_data[index] & 255]); + memcpy(decomp, decomp_prefix[decomp_data[index] & 255], i); + + while (count-- > 0) { + if (i) + decomp[i++] = ' '; + assert((size_t)i < sizeof(decomp)); + PyOS_snprintf(decomp + i, sizeof(decomp) - i, "%04X", + decomp_data[++index]); + i += strlen(decomp + i); + } + + decomp[i] = '\0'; + + return PyString_FromString(decomp); +} + +void +get_decomp_record(Py_UCS4 code, int *index, int *prefix, int *count) +{ + if (code >= 0x110000) { + *index = 0; + } + else { + *index = decomp_index1[(code>>DECOMP_SHIFT)]; + *index = decomp_index2[(*index<> 8; + *prefix = decomp_data[*index] & 255; + + (*index)++; +} + +#define SBase 0xAC00 +#define LBase 0x1100 +#define VBase 0x1161 +#define TBase 0x11A7 +#define LCount 19 +#define VCount 21 +#define TCount 28 +#define NCount (VCount*TCount) +#define SCount (LCount*NCount) + +static PyObject* +nfd_nfkd(PyObject *input, int k) +{ + PyObject *result; + Py_UNICODE *i, *end, *o; + /* Longest decomposition in Unicode 3.2: U+FDFA */ + Py_UNICODE stack[20]; + int space, stackptr, isize; + int index, prefix, count; + unsigned char prev, cur; + + stackptr = 0; + isize = PyUnicode_GET_SIZE(input); + /* Overallocate atmost 10 characters. */ + space = (isize > 10 ? 10 : isize) + isize; + result = PyUnicode_FromUnicode(NULL, space); + if (!result) + return NULL; + i = PyUnicode_AS_UNICODE(input); + end = i + isize; + o = PyUnicode_AS_UNICODE(result); + + while (i < end) { + stack[stackptr++] = *i++; + while(stackptr) { + Py_UNICODE code = stack[--stackptr]; + /* Hangul Decomposition adds three characters in + a single step, so we need atleast that much room. */ + if (space < 3) { + int newsize = PyString_GET_SIZE(result) + 10; + space += 10; + if (PyUnicode_Resize(&result, newsize) == -1) + return NULL; + o = PyUnicode_AS_UNICODE(result) + newsize - space; + } + /* Hangul Decomposition. */ + if (SBase <= code && code < (SBase+SCount)) { + int SIndex = code - SBase; + int L = LBase + SIndex / NCount; + int V = VBase + (SIndex % NCount) / TCount; + int T = TBase + SIndex % TCount; + *o++ = L; + *o++ = V; + space -= 2; + if (T != TBase) { + *o++ = T; + space --; + } + continue; + } + /* Other decompoistions. */ + get_decomp_record(code, &index, &prefix, &count); + + /* Copy character if it is not decomposable, or has a + compatibility decomposition, but we do NFD. */ + if (!count || (prefix && !k)) { + *o++ = code; + space--; + continue; + } + /* Copy decomposition onto the stack, in reverse + order. */ + while(count) { + code = decomp_data[index + (--count)]; + stack[stackptr++] = code; + } + } + } + + /* Drop overallocation. Cannot fail. */ + PyUnicode_Resize(&result, PyUnicode_GET_SIZE(result) - space); + + /* Sort canonically. */ + i = PyUnicode_AS_UNICODE(result); + prev = _getrecord_ex(*i)->combining; + end = i + PyUnicode_GET_SIZE(result); + for (i++; i < end; i++) { + cur = _getrecord_ex(*i)->combining; + if (prev == 0 || cur == 0 || prev <= cur) { + prev = cur; + continue; + } + /* Non-canonical order. Need to switch *i with previous. */ + o = i - 1; + while (1) { + Py_UNICODE tmp = o[1]; + o[1] = o[0]; + o[0] = tmp; + o--; + if (o < PyUnicode_AS_UNICODE(result)) + break; + prev = _getrecord_ex(*o)->combining; + if (prev == 0 || prev <= cur) + break; + } + prev = _getrecord_ex(*i)->combining; + } + return result; +} + +static int +find_nfc_index(struct reindex* nfc, Py_UNICODE code) +{ + int index; + for (index = 0; nfc[index].start; index++) { + int start = nfc[index].start; + if (code < start) + return -1; + if (code <= start + nfc[index].count) { + int delta = code - start; + return nfc[index].index + delta; + } + } + return -1; +} + +static PyObject* +nfc_nfkc(PyObject *input, int k) +{ + PyObject *result; + Py_UNICODE *i, *i1, *o, *end; + int f,l,index,index1,comb; + Py_UNICODE code; + Py_UNICODE *skipped[20]; + int cskipped = 0; + + result = nfd_nfkd(input, k); + if (!result) + return NULL; + + /* We are going to modify result in-place. + If nfd_nfkd is changed to sometimes return the input, + this code needs to be reviewed. */ + assert(result != input); + + i = PyUnicode_AS_UNICODE(result); + end = i + PyUnicode_GET_SIZE(result); + o = PyUnicode_AS_UNICODE(result); + + again: + while (i < end) { + for (index = 0; index < cskipped; index++) { + if (skipped[index] == i) { + /* *i character is skipped. + Remove from list. */ + skipped[index] = skipped[cskipped-1]; + cskipped--; + i++; + goto again; /* continue while */ + } + } + /* Hangul Composition. We don't need to check for + pairs, since we always have decomposed data. */ + if (LBase <= *i && *i < (LBase+LCount) && + i + 1 < end && + VBase <= i[1] && i[1] <= (VBase+VCount)) { + int LIndex, VIndex; + LIndex = i[0] - LBase; + VIndex = i[1] - VBase; + code = SBase + (LIndex*VCount+VIndex)*TCount; + i+=2; + if (i < end && + TBase <= *i && *i <= (TBase+TCount)) { + code += *i-TBase; + i++; + } + *o++ = code; + continue; + } + + f = find_nfc_index(nfc_first, *i); + if (f == -1) { + *o++ = *i++; + continue; + } + /* Find next unblocked character. */ + i1 = i+1; + comb = 0; + while (i1 < end) { + int comb1 = _getrecord_ex(*i1)->combining; + if (comb1 && comb == comb1) { + /* Character is blocked. */ + i1++; + continue; + } + l = find_nfc_index(nfc_last, *i1); + /* *i1 cannot be combined with *i. If *i1 + is a starter, we don't need to look further. + Otherwise, record the combining class. */ + if (l == -1) { + not_combinable: + if (comb1 == 0) + break; + comb = comb1; + i1++; + continue; + } + index = f*TOTAL_LAST + l; + index1 = comp_index[index >> COMP_SHIFT]; + code = comp_data[(index1<>24) & 0xff)) & 0x00ffffff; + } + return h; +} + +static char *hangul_syllables[][3] = { + { "G", "A", "" }, + { "GG", "AE", "G" }, + { "N", "YA", "GG" }, + { "D", "YAE", "GS" }, + { "DD", "EO", "N", }, + { "R", "E", "NJ" }, + { "M", "YEO", "NH" }, + { "B", "YE", "D" }, + { "BB", "O", "L" }, + { "S", "WA", "LG" }, + { "SS", "WAE", "LM" }, + { "", "OE", "LB" }, + { "J", "YO", "LS" }, + { "JJ", "U", "LT" }, + { "C", "WEO", "LP" }, + { "K", "WE", "LH" }, + { "T", "WI", "M" }, + { "P", "YU", "B" }, + { "H", "EU", "BS" }, + { 0, "YI", "S" }, + { 0, "I", "SS" }, + { 0, 0, "NG" }, + { 0, 0, "J" }, + { 0, 0, "C" }, + { 0, 0, "K" }, + { 0, 0, "T" }, + { 0, 0, "P" }, + { 0, 0, "H" } +}; + +static int +is_unified_ideograph(Py_UCS4 code) +{ + return ( + (0x3400 <= code && code <= 0x4DB5) || /* CJK Ideograph Extension A */ + (0x4E00 <= code && code <= 0x9FA5) || /* CJK Ideograph */ + (0x20000 <= code && code <= 0x2A6D6));/* CJK Ideograph Extension B */ +} + +static int +_getucname(Py_UCS4 code, char* buffer, int buflen) +{ + int offset; + int i; + int word; + unsigned char* w; + + if (SBase <= code && code < SBase+SCount) { + /* Hangul syllable. */ + int SIndex = code - SBase; + int L = SIndex / NCount; + int V = (SIndex % NCount) / TCount; + int T = SIndex % TCount; + + if (buflen < 27) + /* Worst case: HANGUL SYLLABLE <10chars>. */ + return 0; + strcpy(buffer, "HANGUL SYLLABLE "); + buffer += 16; + strcpy(buffer, hangul_syllables[L][0]); + buffer += strlen(hangul_syllables[L][0]); + strcpy(buffer, hangul_syllables[V][1]); + buffer += strlen(hangul_syllables[V][1]); + strcpy(buffer, hangul_syllables[T][2]); + buffer += strlen(hangul_syllables[T][2]); + *buffer = '\0'; + return 1; + } + + if (is_unified_ideograph(code)) { + if (buflen < 28) + /* Worst case: CJK UNIFIED IDEOGRAPH-20000 */ + return 0; + sprintf(buffer, "CJK UNIFIED IDEOGRAPH-%X", code); + return 1; + } + + if (code >= 0x110000) + return 0; + + /* get offset into phrasebook */ + offset = phrasebook_offset1[(code>>phrasebook_shift)]; + offset = phrasebook_offset2[(offset<= 0) { + word = (word << 8) + phrasebook[offset+1]; + offset += 2; + } else + word = phrasebook[offset++]; + if (i) { + if (i > buflen) + return 0; /* buffer overflow */ + buffer[i++] = ' '; + } + /* copy word string from lexicon. the last character in the + word has bit 7 set. the last word in a string ends with + 0x80 */ + w = lexicon + lexicon_offset[word]; + while (*w < 128) { + if (i >= buflen) + return 0; /* buffer overflow */ + buffer[i++] = *w++; + } + if (i >= buflen) + return 0; /* buffer overflow */ + buffer[i++] = *w & 127; + if (*w == 128) + break; /* end of word */ + } + + return 1; +} + +static int +_cmpname(int code, const char* name, int namelen) +{ + /* check if code corresponds to the given name */ + int i; + char buffer[NAME_MAXLEN]; + if (!_getucname(code, buffer, sizeof(buffer))) + return 0; + for (i = 0; i < namelen; i++) { + if (toupper(name[i]) != buffer[i]) + return 0; + } + return buffer[namelen] == '\0'; +} + +static void +find_syllable(const char *str, int *len, int *pos, int count, int column) +{ + int i, len1; + *len = -1; + for (i = 0; i < count; i++) { + char *s = hangul_syllables[i][column]; + len1 = strlen(s); + if (len1 <= *len) + continue; + if (strncmp(str, s, len1) == 0) { + *len = len1; + *pos = i; + } + } + if (*len == -1) { + *len = 0; + *pos = -1; + } +} + +static int +_getcode(const char* name, int namelen, Py_UCS4* code) +{ + unsigned int h, v; + unsigned int mask = code_size-1; + unsigned int i, incr; + + /* Check for hangul syllables. */ + if (strncmp(name, "HANGUL SYLLABLE ", 16) == 0) { + int L, V, T, len; + const char *pos = name + 16; + find_syllable(pos, &len, &L, LCount, 0); + pos += len; + find_syllable(pos, &len, &V, VCount, 1); + pos += len; + find_syllable(pos, &len, &T, TCount, 2); + pos += len; + if (V != -1 && V != -1 && T != -1 && pos-name == namelen) { + *code = SBase + (L*VCount+V)*TCount + T; + return 1; + } + /* Otherwise, it's an illegal syllable name. */ + return 0; + } + + /* Check for unified ideographs. */ + if (strncmp(name, "CJK UNIFIED IDEOGRAPH-", 22) == 0) { + /* Four or five hexdigits must follow. */ + v = 0; + name += 22; + namelen -= 22; + if (namelen != 4 && namelen != 5) + return 0; + while (namelen--) { + v *= 16; + if (*name >= '0' && *name <= '9') + v += *name - '0'; + else if (*name >= 'A' && *name <= 'F') + v += *name - 'A' + 10; + else + return 0; + name++; + } + if (!is_unified_ideograph(v)) + return 0; + *code = v; + return 1; + } + + /* the following is the same as python's dictionary lookup, with + only minor changes. see the makeunicodedata script for more + details */ + + h = (unsigned int) _gethash(name, namelen, code_magic); + i = (~h) & mask; + v = code_hash[i]; + if (!v) + return 0; + if (_cmpname(v, name, namelen)) { + *code = v; + return 1; + } + incr = (h ^ (h >> 3)) & mask; + if (!incr) + incr = mask; + for (;;) { + i = (i + incr) & mask; + v = code_hash[i]; + if (!v) + return 0; + if (_cmpname(v, name, namelen)) { + *code = v; + return 1; + } + incr = incr << 1; + if (incr > mask) + incr = incr ^ code_poly; + } +} + +static const _PyUnicode_Name_CAPI hashAPI = +{ + sizeof(_PyUnicode_Name_CAPI), + _getucname, + _getcode +}; + +/* -------------------------------------------------------------------- */ +/* Python bindings */ + +static PyObject * +unicodedata_name(PyObject* self, PyObject* args) +{ + char name[NAME_MAXLEN]; + + PyUnicodeObject* v; + PyObject* defobj = NULL; + if (!PyArg_ParseTuple(args, "O!|O:name", &PyUnicode_Type, &v, &defobj)) + return NULL; + + if (PyUnicode_GET_SIZE(v) != 1) { + PyErr_SetString(PyExc_TypeError, + "need a single Unicode character as parameter"); + return NULL; + } + + if (!_getucname((Py_UCS4) *PyUnicode_AS_UNICODE(v), + name, sizeof(name))) { + if (defobj == NULL) { + PyErr_SetString(PyExc_ValueError, "no such name"); + return NULL; + } + else { + Py_INCREF(defobj); + return defobj; + } + } + + return Py_BuildValue("s", name); +} + +static PyObject * +unicodedata_lookup(PyObject* self, PyObject* args) +{ + Py_UCS4 code; + Py_UNICODE str[1]; + + char* name; + int namelen; + if (!PyArg_ParseTuple(args, "s#:lookup", &name, &namelen)) + return NULL; + + if (!_getcode(name, namelen, &code)) { + char fmt[] = "undefined character name '%s'"; + char *buf = PyMem_MALLOC(sizeof(fmt) + namelen); + sprintf(buf, fmt, name); + PyErr_SetString(PyExc_KeyError, buf); + PyMem_FREE(buf); + return NULL; + } + + str[0] = (Py_UNICODE) code; + return PyUnicode_FromUnicode(str, 1); +} + +/* XXX Add doc strings. */ + +static PyMethodDef unicodedata_functions[] = { + {"decimal", unicodedata_decimal, METH_VARARGS}, + {"digit", unicodedata_digit, METH_VARARGS}, + {"numeric", unicodedata_numeric, METH_VARARGS}, + {"category", unicodedata_category, METH_VARARGS}, + {"bidirectional", unicodedata_bidirectional, METH_VARARGS}, + {"combining", unicodedata_combining, METH_VARARGS}, + {"mirrored", unicodedata_mirrored, METH_VARARGS}, + {"decomposition",unicodedata_decomposition, METH_VARARGS}, + {"name", unicodedata_name, METH_VARARGS}, + {"lookup", unicodedata_lookup, METH_VARARGS}, + {"normalize", unicodedata_normalize, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +PyDoc_STRVAR(unicodedata_docstring, "unicode character database"); + +PyMODINIT_FUNC +initunicodedata(void) +{ + PyObject *m, *v; + + m = Py_InitModule3( + "unicodedata", unicodedata_functions, unicodedata_docstring); + if (!m) + return; + + PyModule_AddStringConstant(m, "unidata_version", UNIDATA_VERSION); + + /* Export C API */ + v = PyCObject_FromVoidPtr((void *) &hashAPI, NULL); + if (v != NULL) + PyModule_AddObject(m, "ucnhash_CAPI", v); +} + +/* +Local variables: +c-basic-offset: 4 +indent-tabs-mode: nil +End: +*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodedata_db.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodedata_db.h new file mode 100644 index 00000000..b6cba477 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodedata_db.h @@ -0,0 +1,3781 @@ +/* this file was generated by Tools/unicode/makeunicodedata.py 2.2 */ + +#define UNIDATA_VERSION "3.2.0" +/* a list of unique database records */ +const _PyUnicode_DatabaseRecord _PyUnicode_Database_Records[] = { + {0, 0, 0, 0}, + {13, 0, 15, 0}, + {13, 0, 17, 0}, + {13, 0, 16, 0}, + {13, 0, 18, 0}, + {10, 0, 18, 0}, + {26, 0, 19, 0}, + {26, 0, 11, 0}, + {28, 0, 11, 0}, + {22, 0, 19, 1}, + {23, 0, 19, 1}, + {27, 0, 11, 0}, + {26, 0, 13, 0}, + {21, 0, 11, 0}, + {26, 0, 10, 0}, + {7, 0, 9, 0}, + {27, 0, 19, 1}, + {27, 0, 19, 0}, + {1, 0, 1, 0}, + {29, 0, 19, 0}, + {20, 0, 19, 0}, + {2, 0, 1, 0}, + {10, 0, 13, 0}, + {30, 0, 19, 0}, + {24, 0, 19, 1}, + {21, 0, 19, 0}, + {30, 0, 11, 0}, + {9, 0, 9, 0}, + {25, 0, 19, 1}, + {9, 0, 19, 0}, + {19, 0, 1, 0}, + {3, 0, 1, 0}, + {18, 0, 1, 0}, + {4, 230, 14, 0}, + {4, 232, 14, 0}, + {4, 220, 14, 0}, + {4, 216, 14, 0}, + {4, 202, 14, 0}, + {4, 1, 14, 0}, + {4, 240, 14, 0}, + {4, 0, 14, 0}, + {4, 234, 14, 0}, + {4, 233, 14, 0}, + {30, 0, 1, 0}, + {6, 0, 14, 0}, + {26, 0, 1, 0}, + {4, 222, 14, 0}, + {4, 228, 14, 0}, + {4, 10, 14, 0}, + {4, 11, 14, 0}, + {4, 12, 14, 0}, + {4, 13, 14, 0}, + {4, 14, 14, 0}, + {4, 15, 14, 0}, + {4, 16, 14, 0}, + {4, 17, 14, 0}, + {4, 18, 14, 0}, + {4, 19, 14, 0}, + {4, 20, 14, 0}, + {4, 21, 14, 0}, + {4, 22, 14, 0}, + {26, 0, 4, 0}, + {4, 23, 14, 0}, + {4, 24, 14, 0}, + {4, 25, 14, 0}, + {19, 0, 4, 0}, + {26, 0, 5, 0}, + {19, 0, 5, 0}, + {18, 0, 5, 0}, + {4, 27, 14, 0}, + {4, 28, 14, 0}, + {4, 29, 14, 0}, + {4, 30, 14, 0}, + {4, 31, 14, 0}, + {4, 32, 14, 0}, + {4, 33, 14, 0}, + {4, 34, 14, 0}, + {7, 0, 12, 0}, + {26, 0, 12, 0}, + {4, 35, 14, 0}, + {14, 0, 5, 0}, + {30, 0, 5, 0}, + {14, 0, 15, 0}, + {4, 36, 14, 0}, + {5, 0, 1, 0}, + {4, 7, 14, 0}, + {4, 9, 14, 0}, + {7, 0, 1, 0}, + {9, 0, 1, 0}, + {4, 84, 14, 0}, + {4, 91, 14, 0}, + {4, 103, 14, 0}, + {4, 107, 14, 0}, + {4, 118, 14, 0}, + {4, 122, 14, 0}, + {22, 0, 19, 0}, + {23, 0, 19, 0}, + {4, 129, 14, 0}, + {4, 130, 14, 0}, + {4, 132, 14, 0}, + {8, 0, 1, 0}, + {10, 0, 15, 0}, + {14, 0, 1, 0}, + {14, 0, 4, 0}, + {24, 0, 19, 0}, + {25, 0, 19, 0}, + {11, 0, 18, 0}, + {12, 0, 16, 0}, + {14, 0, 2, 0}, + {14, 0, 6, 0}, + {14, 0, 8, 0}, + {14, 0, 3, 0}, + {14, 0, 7, 0}, + {4, 218, 14, 0}, + {4, 224, 14, 0}, + {4, 8, 14, 0}, + {15, 0, 1, 0}, + {16, 0, 1, 0}, + {4, 26, 14, 0}, + {28, 0, 5, 0}, + {5, 216, 1, 0}, + {5, 226, 1, 0}, + {27, 0, 1, 0}, +}; + +/* Reindexing of NFC first characters. */ +#define TOTAL_FIRST 356 +#define TOTAL_LAST 53 +struct reindex{int start;short count,index;}; +struct reindex nfc_first[] = { + { 60, 2, 0}, + { 65, 15, 3}, + { 82, 8, 19}, + { 97, 15, 28}, + { 114, 8, 44}, + { 168, 0, 53}, + { 194, 0, 54}, + { 196, 3, 55}, + { 202, 0, 59}, + { 207, 0, 60}, + { 212, 2, 61}, + { 216, 0, 64}, + { 220, 0, 65}, + { 226, 0, 66}, + { 228, 3, 67}, + { 234, 0, 71}, + { 239, 0, 72}, + { 244, 2, 73}, + { 248, 0, 76}, + { 252, 0, 77}, + { 258, 1, 78}, + { 274, 1, 80}, + { 332, 1, 82}, + { 346, 1, 84}, + { 352, 1, 86}, + { 360, 3, 88}, + { 383, 0, 92}, + { 416, 1, 93}, + { 431, 1, 95}, + { 439, 0, 97}, + { 490, 1, 98}, + { 550, 3, 100}, + { 558, 1, 104}, + { 658, 0, 106}, + { 913, 0, 107}, + { 917, 0, 108}, + { 919, 0, 109}, + { 921, 0, 110}, + { 927, 0, 111}, + { 929, 0, 112}, + { 933, 0, 113}, + { 937, 0, 114}, + { 940, 0, 115}, + { 942, 0, 116}, + { 945, 0, 117}, + { 949, 0, 118}, + { 951, 0, 119}, + { 953, 0, 120}, + { 959, 0, 121}, + { 961, 0, 122}, + { 965, 0, 123}, + { 969, 2, 124}, + { 974, 0, 127}, + { 978, 0, 128}, + { 1030, 0, 129}, + { 1040, 0, 130}, + { 1043, 0, 131}, + { 1045, 3, 132}, + { 1050, 0, 136}, + { 1054, 0, 137}, + { 1059, 0, 138}, + { 1063, 0, 139}, + { 1067, 0, 140}, + { 1069, 0, 141}, + { 1072, 0, 142}, + { 1075, 0, 143}, + { 1077, 3, 144}, + { 1082, 0, 148}, + { 1086, 0, 149}, + { 1091, 0, 150}, + { 1095, 0, 151}, + { 1099, 0, 152}, + { 1101, 0, 153}, + { 1110, 0, 154}, + { 1140, 1, 155}, + { 1240, 1, 157}, + { 1256, 1, 159}, + { 1575, 0, 161}, + { 1608, 0, 162}, + { 1610, 0, 163}, + { 1729, 0, 164}, + { 1746, 0, 165}, + { 1749, 0, 166}, + { 2344, 0, 167}, + { 2352, 0, 168}, + { 2355, 0, 169}, + { 2503, 0, 170}, + { 2887, 0, 171}, + { 2962, 0, 172}, + { 3014, 1, 173}, + { 3142, 0, 175}, + { 3263, 0, 176}, + { 3270, 0, 177}, + { 3274, 0, 178}, + { 3398, 1, 179}, + { 3545, 0, 181}, + { 3548, 0, 182}, + { 4133, 0, 183}, + { 7734, 1, 184}, + { 7770, 1, 186}, + { 7778, 1, 188}, + { 7840, 1, 190}, + { 7864, 1, 192}, + { 7884, 1, 194}, + { 7936, 17, 196}, + { 7960, 1, 214}, + { 7968, 17, 216}, + { 7992, 1, 234}, + { 8000, 1, 236}, + { 8008, 1, 238}, + { 8016, 1, 240}, + { 8025, 0, 242}, + { 8032, 16, 243}, + { 8052, 0, 260}, + { 8060, 0, 261}, + { 8118, 0, 262}, + { 8127, 0, 263}, + { 8134, 0, 264}, + { 8182, 0, 265}, + { 8190, 0, 266}, + { 8592, 0, 267}, + { 8594, 0, 268}, + { 8596, 0, 269}, + { 8656, 0, 270}, + { 8658, 0, 271}, + { 8660, 0, 272}, + { 8707, 0, 273}, + { 8712, 0, 274}, + { 8715, 0, 275}, + { 8739, 0, 276}, + { 8741, 0, 277}, + { 8764, 0, 278}, + { 8771, 0, 279}, + { 8773, 0, 280}, + { 8776, 0, 281}, + { 8781, 0, 282}, + { 8801, 0, 283}, + { 8804, 1, 284}, + { 8818, 1, 286}, + { 8822, 1, 288}, + { 8826, 3, 290}, + { 8834, 1, 294}, + { 8838, 1, 296}, + { 8849, 1, 298}, + { 8866, 0, 300}, + { 8872, 1, 301}, + { 8875, 0, 303}, + { 8882, 3, 304}, + { 12358, 0, 308}, + { 12363, 0, 309}, + { 12365, 0, 310}, + { 12367, 0, 311}, + { 12369, 0, 312}, + { 12371, 0, 313}, + { 12373, 0, 314}, + { 12375, 0, 315}, + { 12377, 0, 316}, + { 12379, 0, 317}, + { 12381, 0, 318}, + { 12383, 0, 319}, + { 12385, 0, 320}, + { 12388, 0, 321}, + { 12390, 0, 322}, + { 12392, 0, 323}, + { 12399, 0, 324}, + { 12402, 0, 325}, + { 12405, 0, 326}, + { 12408, 0, 327}, + { 12411, 0, 328}, + { 12445, 0, 329}, + { 12454, 0, 330}, + { 12459, 0, 331}, + { 12461, 0, 332}, + { 12463, 0, 333}, + { 12465, 0, 334}, + { 12467, 0, 335}, + { 12469, 0, 336}, + { 12471, 0, 337}, + { 12473, 0, 338}, + { 12475, 0, 339}, + { 12477, 0, 340}, + { 12479, 0, 341}, + { 12481, 0, 342}, + { 12484, 0, 343}, + { 12486, 0, 344}, + { 12488, 0, 345}, + { 12495, 0, 346}, + { 12498, 0, 347}, + { 12501, 0, 348}, + { 12504, 0, 349}, + { 12507, 0, 350}, + { 12527, 3, 351}, + { 12541, 0, 355}, + {0,0,0} +}; + +struct reindex nfc_last[] = { + { 768, 4, 0}, + { 774, 6, 5}, + { 783, 0, 12}, + { 785, 0, 13}, + { 787, 1, 14}, + { 795, 0, 16}, + { 803, 5, 17}, + { 813, 1, 23}, + { 816, 1, 25}, + { 824, 0, 27}, + { 834, 0, 28}, + { 837, 0, 29}, + { 1619, 2, 30}, + { 2364, 0, 33}, + { 2494, 0, 34}, + { 2519, 0, 35}, + { 2878, 0, 36}, + { 2902, 1, 37}, + { 3006, 0, 39}, + { 3031, 0, 40}, + { 3158, 0, 41}, + { 3266, 0, 42}, + { 3285, 1, 43}, + { 3390, 0, 45}, + { 3415, 0, 46}, + { 3530, 0, 47}, + { 3535, 0, 48}, + { 3551, 0, 49}, + { 4142, 0, 50}, + { 12441, 1, 51}, + {0,0,0} +}; + +/* string literals */ +const char *_PyUnicode_CategoryNames[] = { + "Cn", + "Lu", + "Ll", + "Lt", + "Mn", + "Mc", + "Me", + "Nd", + "Nl", + "No", + "Zs", + "Zl", + "Zp", + "Cc", + "Cf", + "Cs", + "Co", + "Cn", + "Lm", + "Lo", + "Pc", + "Pd", + "Ps", + "Pe", + "Pi", + "Pf", + "Po", + "Sm", + "Sc", + "Sk", + "So", + NULL +}; +const char *_PyUnicode_BidirectionalNames[] = { + "", + "L", + "LRE", + "LRO", + "R", + "AL", + "RLE", + "RLO", + "PDF", + "EN", + "ES", + "ET", + "AN", + "CS", + "NSM", + "BN", + "B", + "S", + "WS", + "ON", + NULL +}; +static const char *decomp_prefix[] = { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + NULL +}; +/* index tables for the database records */ +#define SHIFT 8 +static unsigned char index1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 8, 8, 8, 8, 8, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 8, 8, 8, 38, 39, 40, 41, 42, 43, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 44, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 45, 21, 21, 21, 21, 46, 8, 8, 8, + 8, 8, 8, 8, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 47, 48, 48, 48, 48, 48, 48, 48, + 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 21, 50, 51, 52, 53, 54, 55, 8, 8, 8, 56, + 57, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 58, 59, 8, 8, 60, 61, 62, 63, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 64, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 21, 21, 65, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 66, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 67, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 67, +}; + +static unsigned char index2[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 4, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 3, 3, 2, 5, 6, 6, 7, 8, 7, 6, 6, 9, 10, 6, 11, 12, 13, 12, + 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 12, 6, 16, 17, 16, 6, 6, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 9, 6, 10, 19, 20, 19, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 9, 17, 10, 17, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 22, 6, 8, 8, 8, 8, 23, + 23, 19, 23, 21, 24, 17, 25, 23, 19, 26, 11, 27, 27, 19, 21, 23, 6, 19, + 27, 21, 28, 29, 29, 29, 6, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 17, 18, 18, 18, 18, 18, + 18, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 17, 21, 21, 21, 21, 21, 21, 21, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 18, 21, 18, 21, 18, 21, + 21, 21, 18, 18, 21, 18, 21, 18, 18, 21, 18, 18, 18, 21, 21, 18, 18, 18, + 18, 21, 18, 18, 21, 18, 18, 18, 21, 21, 21, 18, 18, 21, 18, 18, 21, 18, + 21, 18, 21, 18, 18, 21, 18, 21, 21, 18, 21, 18, 18, 21, 18, 18, 18, 21, + 18, 21, 18, 18, 21, 21, 30, 18, 21, 21, 21, 30, 30, 30, 30, 18, 31, 21, + 18, 31, 21, 18, 31, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, + 18, 21, 18, 21, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 21, 18, 31, 21, 18, 21, 18, 18, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 0, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 0, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 19, 19, 32, 32, 32, 32, 32, 32, 32, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 32, 32, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 32, 32, 32, 32, 32, 19, 19, 19, 19, 19, 19, 19, 19, 19, 32, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 35, + 35, 35, 35, 34, 36, 35, 35, 35, 35, 35, 37, 37, 35, 35, 35, 35, 37, 37, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 38, 38, 38, 38, 38, 35, 35, + 35, 35, 33, 33, 33, 33, 33, 33, 33, 33, 39, 33, 35, 35, 35, 33, 33, 33, + 35, 35, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 42, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 0, 0, 0, 0, 19, 19, + 0, 0, 0, 0, 32, 0, 0, 0, 6, 0, 0, 0, 0, 0, 19, 19, 18, 6, 18, 18, 18, 0, + 18, 0, 18, 18, 21, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 0, 21, 21, 18, 18, + 18, 21, 21, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, + 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 21, 21, 21, 21, 18, 21, 17, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 43, 33, 33, 33, 33, 0, 44, 44, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 0, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 0, 0, + 18, 21, 0, 0, 0, 0, 0, 0, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, + 18, 21, 18, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 32, 45, 45, 45, 45, + 45, 45, 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 0, 45, 25, 0, 0, 0, 0, 0, 0, 35, 33, 33, 33, 33, + 35, 33, 33, 33, 46, 35, 33, 33, 33, 33, 33, 33, 0, 35, 35, 35, 35, 35, + 33, 33, 35, 33, 33, 46, 47, 33, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 0, 58, 59, 60, 61, 62, 61, 63, 64, 61, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 0, 0, 0, 0, 0, 65, 65, 65, 61, + 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 66, 0, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 0, 0, 0, 0, 0, 68, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 69, 70, 71, 72, 73, 74, 75, 76, 33, 33, 35, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 7, 78, 78, 66, 67, + 67, 79, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 66, 67, 33, 33, 33, 33, 33, + 33, 33, 80, 44, 33, 33, 33, 33, 35, 33, 68, 68, 33, 33, 23, 35, 33, 33, + 35, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 67, 67, 67, 81, 81, 0, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 0, 82, 67, 83, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 0, 0, 0, 33, 35, 33, 33, 35, 33, 33, + 35, 35, 35, 33, 35, 35, 33, 35, 33, 33, 33, 35, 33, 35, 33, 35, 33, 35, + 33, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 40, 40, 84, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 0, 0, 85, 30, 84, 84, 84, 40, 40, 40, 40, 40, 40, 40, 40, 84, + 84, 84, 84, 86, 0, 0, 30, 33, 35, 33, 33, 0, 0, 0, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 40, 40, 45, 45, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 84, 84, 0, + 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 30, 30, 0, 0, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, + 30, 30, 30, 30, 30, 30, 30, 0, 30, 0, 0, 0, 30, 30, 30, 30, 0, 0, 85, 0, + 84, 84, 84, 40, 40, 40, 40, 0, 0, 84, 84, 0, 0, 84, 84, 86, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 84, 0, 0, 0, 0, 30, 30, 0, 30, 30, 30, 40, 40, 0, 0, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 30, 30, 8, 8, 88, 88, 88, 88, 88, 88, + 43, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, + 30, 30, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 0, + 30, 30, 0, 30, 30, 0, 0, 85, 0, 84, 84, 84, 40, 40, 0, 0, 0, 0, 40, 40, + 0, 0, 40, 40, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 30, 0, 30, + 0, 0, 0, 0, 0, 0, 0, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 40, 40, 30, + 30, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 40, 84, 0, 30, 30, 30, + 30, 30, 30, 30, 0, 30, 0, 30, 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, + 30, 30, 30, 30, 0, 30, 30, 0, 30, 30, 30, 30, 30, 0, 0, 85, 30, 84, 84, + 84, 40, 40, 40, 40, 40, 0, 40, 40, 84, 0, 84, 84, 86, 0, 0, 30, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 40, 84, 84, 0, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 30, 30, 0, 0, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 0, 0, 30, 30, 30, + 30, 0, 0, 85, 30, 84, 40, 84, 40, 40, 40, 0, 0, 0, 84, 84, 0, 0, 84, 84, + 86, 0, 0, 0, 0, 0, 0, 0, 0, 40, 84, 0, 0, 0, 0, 30, 30, 0, 30, 30, 30, 0, + 0, 0, 0, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 43, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 30, 0, 30, 30, 30, 30, 30, 30, 0, 0, 0, + 30, 30, 30, 0, 30, 30, 30, 30, 0, 0, 0, 30, 30, 0, 30, 0, 30, 30, 0, 0, + 0, 30, 30, 0, 0, 0, 30, 30, 30, 0, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, + 0, 30, 30, 30, 0, 0, 0, 0, 84, 84, 40, 84, 84, 0, 0, 0, 84, 84, 84, 0, + 84, 84, 84, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 87, 87, 87, 87, 87, 87, 87, 87, 87, 88, 88, 88, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 84, 84, 0, 30, 30, 30, 30, 30, + 30, 30, 30, 0, 30, 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 0, 30, 30, 30, 30, 30, 0, 0, 0, 0, 40, 40, 40, 84, + 84, 84, 84, 0, 40, 40, 40, 0, 40, 40, 40, 86, 0, 0, 0, 0, 0, 0, 0, 89, + 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 0, 0, 0, 0, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 84, 84, 0, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, 0, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, 30, 30, + 0, 0, 0, 0, 84, 40, 84, 84, 84, 84, 84, 0, 40, 84, 84, 0, 84, 84, 40, 86, + 0, 0, 0, 0, 0, 0, 0, 84, 84, 0, 0, 0, 0, 0, 0, 0, 30, 0, 30, 30, 0, 0, 0, + 0, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 84, 84, 0, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, + 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, 84, 84, 84, 40, 40, 40, 0, 0, 84, + 84, 84, 0, 84, 84, 84, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 30, 30, 0, 0, 0, 0, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 84, 0, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 0, 0, + 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 86, 0, 0, 0, 0, 84, 84, 84, 40, 40, + 40, 0, 40, 0, 84, 84, 84, 84, 84, 84, 84, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 84, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 40, 30, 30, 40, 40, + 40, 40, 91, 91, 86, 0, 0, 0, 0, 8, 30, 30, 30, 30, 30, 30, 32, 40, 92, + 92, 92, 92, 40, 40, 40, 45, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 45, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 0, 30, 0, 0, 30, 30, 0, + 30, 0, 0, 30, 0, 0, 0, 0, 0, 0, 30, 30, 30, 30, 0, 30, 30, 30, 30, 30, + 30, 30, 0, 30, 30, 30, 0, 30, 0, 30, 0, 0, 30, 30, 0, 30, 30, 30, 30, 40, + 30, 30, 40, 40, 40, 40, 93, 93, 0, 40, 40, 30, 0, 0, 30, 30, 30, 30, 30, + 0, 32, 0, 94, 94, 94, 94, 40, 40, 0, 0, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 0, 0, 30, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 43, 43, 43, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 43, 43, 43, 43, + 43, 35, 35, 43, 43, 43, 43, 43, 43, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 43, 35, 43, 35, 43, 36, 95, + 96, 95, 96, 84, 84, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, 0, 0, 97, 98, + 40, 99, 40, 40, 40, 40, 40, 98, 98, 98, 98, 40, 84, 98, 40, 33, 33, 86, + 45, 33, 33, 30, 30, 30, 30, 0, 0, 0, 0, 40, 40, 40, 40, 40, 40, 40, 40, + 0, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 0, 43, 43, 43, 43, 43, 43, 43, 43, 35, 43, 43, 43, 43, 43, 43, 0, 0, + 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, + 30, 30, 30, 30, 30, 0, 30, 30, 0, 84, 40, 40, 40, 40, 84, 40, 0, 0, 0, + 40, 85, 84, 86, 0, 0, 0, 0, 0, 0, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 45, 45, 45, 45, 45, 45, 30, 30, 30, 30, 30, 30, 84, 84, 40, 40, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 45, 0, 0, 0, 0, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, + 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, + 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, 0, 0, 30, 30, 30, + 30, 30, 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 0, 30, 30, + 30, 30, 0, 0, 30, 30, 30, 30, 30, 30, 30, 0, 30, 0, 30, 30, 30, 30, 0, 0, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 0, 30, 0, 30, 30, 30, 30, 0, 0, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 0, 30, 0, 30, 30, 30, 30, 0, 0, 30, 30, 30, 30, + 30, 30, 30, 0, 30, 0, 30, 30, 30, 30, 0, 0, 30, 30, 30, 30, 30, 30, 30, + 0, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 0, 30, 30, 30, 30, 0, 0, 30, + 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, 0, 0, 45, 45, + 45, 45, 45, 45, 45, 45, 87, 87, 87, 87, 87, 87, 87, 87, 87, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 45, 45, 30, 30, 30, 30, 30, 30, 30, + 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 95, 96, + 0, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 45, 45, 45, 100, 100, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, + 30, 30, 30, 30, 40, 40, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 40, 40, 86, + 45, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 40, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 30, 30, 30, + 0, 40, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 84, 84, 84, 40, 40, 40, 40, 40, + 40, 40, 84, 84, 84, 84, 84, 84, 84, 84, 40, 84, 84, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 86, 40, 45, 45, 45, 32, 45, 45, 45, 8, 30, 0, 0, 0, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 25, 6, 6, 6, 6, 40, 40, + 40, 82, 0, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 0, 0, 0, 0, 0, 0, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 32, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, + 0, 0, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, + 21, 18, 21, 18, 21, 18, 21, 21, 21, 21, 21, 21, 21, 0, 0, 0, 0, 18, 21, + 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, + 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, + 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, + 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, + 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 18, 21, 0, 0, 0, + 0, 0, 0, 21, 21, 21, 21, 21, 21, 21, 21, 18, 18, 18, 18, 18, 18, 18, 18, + 21, 21, 21, 21, 21, 21, 0, 0, 18, 18, 18, 18, 18, 18, 0, 0, 21, 21, 21, + 21, 21, 21, 21, 21, 18, 18, 18, 18, 18, 18, 18, 18, 21, 21, 21, 21, 21, + 21, 21, 21, 18, 18, 18, 18, 18, 18, 18, 18, 21, 21, 21, 21, 21, 21, 0, 0, + 18, 18, 18, 18, 18, 18, 0, 0, 21, 21, 21, 21, 21, 21, 21, 21, 0, 18, 0, + 18, 0, 18, 0, 18, 21, 21, 21, 21, 21, 21, 21, 21, 18, 18, 18, 18, 18, 18, + 18, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 0, 0, 21, + 21, 21, 21, 21, 21, 21, 21, 31, 31, 31, 31, 31, 31, 31, 31, 21, 21, 21, + 21, 21, 21, 21, 21, 31, 31, 31, 31, 31, 31, 31, 31, 21, 21, 21, 21, 21, + 21, 21, 21, 31, 31, 31, 31, 31, 31, 31, 31, 21, 21, 21, 21, 21, 0, 21, + 21, 18, 18, 18, 18, 31, 19, 21, 19, 19, 19, 21, 21, 21, 0, 21, 21, 18, + 18, 18, 18, 31, 19, 19, 19, 21, 21, 21, 21, 0, 0, 21, 21, 18, 18, 18, 18, + 0, 19, 19, 19, 21, 21, 21, 21, 21, 21, 21, 21, 18, 18, 18, 18, 18, 19, + 19, 19, 0, 0, 21, 21, 21, 0, 21, 21, 18, 18, 18, 18, 31, 19, 19, 0, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 101, 82, 82, 102, 103, 25, 25, 25, 25, 25, 25, + 6, 6, 104, 105, 95, 104, 104, 105, 95, 104, 6, 6, 6, 6, 6, 6, 6, 6, 106, + 107, 108, 109, 110, 111, 112, 5, 7, 7, 7, 7, 7, 6, 6, 6, 6, 24, 28, 6, 6, + 6, 6, 20, 20, 6, 6, 6, 17, 9, 10, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 17, 0, + 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 5, 82, 82, 82, 82, 0, 0, 0, 0, 0, 0, 82, + 82, 82, 82, 82, 82, 27, 21, 0, 0, 27, 27, 27, 27, 27, 27, 11, 11, 17, 9, + 10, 21, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 11, 11, 17, 9, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 33, 38, 38, 33, 33, 33, + 33, 38, 38, 38, 33, 33, 44, 44, 44, 44, 33, 44, 44, 44, 38, 38, 33, 35, + 33, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 23, 23, 18, 23, 23, 23, 23, 18, 23, 23, 21, 18, 18, 18, 21, 21, 18, 18, + 18, 21, 23, 18, 23, 23, 23, 18, 18, 18, 18, 18, 23, 23, 23, 23, 23, 23, + 18, 23, 18, 23, 18, 23, 18, 18, 18, 18, 26, 21, 18, 18, 23, 18, 21, 30, + 30, 30, 30, 21, 23, 0, 0, 21, 18, 18, 16, 17, 17, 17, 17, 18, 21, 21, 21, + 21, 23, 17, 0, 0, 0, 0, 0, 0, 0, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 17, 17, 17, 17, 17, 23, 23, 23, 23, 23, 17, 17, 23, 23, 23, + 23, 17, 23, 23, 17, 23, 23, 17, 23, 23, 23, 23, 23, 23, 23, 17, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 17, 17, 23, 23, 17, 23, 17, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 17, 17, 17, 16, 16, 16, + 16, 16, 16, 17, 17, 17, 16, 11, 11, 17, 16, 16, 17, 17, 17, 16, 16, 16, + 16, 17, 16, 16, 16, 16, 17, 16, 17, 16, 17, 17, 17, 17, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 16, + 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 17, 16, 17, 16, + 16, 16, 16, 16, 16, 16, 16, 17, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 17, 17, 16, 16, 16, 16, 17, 17, 17, 17, 17, 16, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 16, 16, 17, 17, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 16, + 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 17, 17, 16, + 16, 17, 17, 17, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 23, 23, 23, 23, 23, 23, 23, + 23, 16, 16, 16, 16, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 16, 16, 23, 23, 23, 23, 23, 23, 23, 9, 10, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 23, 17, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 43, 23, + 23, 23, 23, 23, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 95, 96, 6, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 27, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 0, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 17, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 17, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 17, 17, 17, 17, 17, 17, 17, 17, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 0, 0, + 23, 23, 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 17, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 0, 0, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 0, 23, 23, 23, 23, 0, 0, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 0, 23, 0, 23, 23, 23, 23, 0, 0, 0, 23, 0, + 23, 23, 23, 23, 23, 23, 23, 0, 0, 23, 23, 23, 23, 23, 23, 23, 9, 10, 9, + 10, 9, 10, 9, 10, 9, 10, 9, 10, 9, 10, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 23, 0, 0, 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 0, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 17, 17, 17, 16, 16, 16, 16, 17, 17, 17, 17, 17, 16, 16, + 16, 17, 17, 17, 16, 16, 16, 16, 9, 10, 9, 10, 9, 10, 0, 0, 0, 0, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 9, 10, 9, 10, 9, 10, 9, 10, 9, 10, 9, 10, 9, 10, 9, + 10, 9, 10, 9, 10, 9, 10, 17, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, + 17, 16, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 16, 17, 17, 17, + 16, 17, 17, 17, 17, 16, 16, 16, 16, 16, 17, 16, 16, 17, 17, 9, 10, 9, 10, + 16, 17, 17, 17, 17, 16, 17, 16, 16, 16, 17, 17, 16, 16, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 16, 17, 17, 9, 10, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 16, 16, 16, 17, 17, + 16, 17, 16, 17, 17, 16, 17, 16, 16, 16, 16, 17, 17, 17, 17, 17, 16, 16, + 17, 17, 17, 17, 17, 17, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 17, 17, 17, 17, 16, 16, + 16, 16, 17, 16, 16, 17, 17, 16, 16, 17, 17, 17, 17, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 17, 17, 16, 16, 16, 16, 16, 16, 16, 16, 17, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 16, 17, 16, 17, 17, 17, 16, 16, 16, 16, 16, 17, 17, 17, + 17, 17, 16, 16, 16, 17, 17, 17, 17, 16, 17, 17, 17, 16, 16, 16, 16, 16, + 17, 16, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 0, 0, 0, 0, 5, 6, 6, 6, 23, 32, 30, 100, 9, 10, 9, 10, 9, 10, 9, + 10, 9, 10, 23, 23, 9, 10, 9, 10, 9, 10, 9, 10, 25, 95, 96, 96, 23, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 113, 47, 34, 46, 114, 114, 25, + 32, 32, 32, 32, 32, 23, 23, 100, 100, 100, 32, 30, 6, 23, 23, 0, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 115, 115, 19, 19, + 32, 32, 30, 25, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 20, 32, 32, 32, 30, 0, 0, 0, 0, 0, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, + 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 0, 43, 43, 88, 88, 88, 88, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 0, 0, 0, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 0, 0, 0, 43, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 0, 0, 0, 0, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 0, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 0, 0, 0, 0, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 0, 0, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 0, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21, 21, + 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21, 21, 0, 0, 0, + 0, 0, 65, 118, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 11, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 0, 65, 65, 65, 65, 65, 0, 65, 0, + 65, 65, 0, 65, 65, 0, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 95, 96, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 0, 0, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 119, 0, 0, 0, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 33, 33, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 25, + 25, 20, 20, 95, 96, 95, 96, 95, 96, 95, 96, 95, 96, 95, 96, 95, 96, 95, + 96, 6, 6, 0, 0, 6, 6, 6, 6, 20, 20, 20, 12, 6, 12, 0, 6, 12, 6, 6, 25, + 95, 96, 95, 96, 95, 96, 7, 6, 6, 11, 13, 17, 17, 17, 0, 6, 8, 7, 6, 0, 0, + 0, 0, 67, 67, 67, 67, 67, 0, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 0, 0, 82, + 0, 6, 6, 7, 8, 7, 6, 6, 9, 10, 6, 11, 12, 13, 12, 14, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 12, 6, 16, 17, 16, 6, 6, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 9, 6, 10, 19, 20, 19, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 9, 17, 10, 17, 9, + 10, 6, 9, 10, 6, 20, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 32, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 32, 32, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 0, 0, 0, 30, 30, 30, 30, 30, 30, 0, 0, 30, 30, 30, 30, + 30, 30, 0, 0, 30, 30, 30, 30, 30, 30, 0, 0, 30, 30, 30, 0, 0, 0, 8, 8, + 17, 19, 23, 8, 8, 0, 23, 17, 17, 17, 17, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 82, 82, 82, 23, 23, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 0, 88, 88, 88, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 0, 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 0, 0, 0, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 120, 120, 38, 38, 38, 43, 43, 43, 121, 120, 120, 120, + 120, 120, 82, 82, 82, 82, 82, 82, 82, 82, 35, 35, 35, 35, 35, 35, 35, 35, + 43, 43, 33, 33, 33, 33, 33, 35, 35, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 33, 33, 33, 33, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 21, 21, 21, 21, + 21, 21, 21, 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 18, 0, 18, 18, 0, 0, 18, 0, 0, 18, 18, 0, 0, 18, 18, 18, 18, 0, + 18, 18, 18, 18, 18, 18, 18, 18, 21, 21, 21, 21, 0, 21, 0, 21, 21, 21, 21, + 0, 21, 21, 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 18, 18, 0, 18, 18, 18, + 18, 0, 0, 18, 18, 18, 18, 18, 18, 18, 18, 0, 18, 18, 18, 18, 18, 18, 18, + 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 18, 18, 0, 18, 18, 18, 18, 0, 18, 18, + 18, 18, 18, 0, 18, 0, 0, 0, 18, 18, 18, 18, 18, 18, 18, 0, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 0, 0, 0, 0, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 122, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 122, 21, 21, 21, 21, 21, 21, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 122, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 122, 21, 21, + 21, 21, 21, 21, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 122, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 122, 21, 21, 21, 21, 21, 21, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 122, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 122, 21, 21, 21, 21, 21, 21, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 122, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 122, 21, 21, 21, 21, 21, 21, 0, 0, 0, + 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, 117, 0, 0, +}; + +/* decomposition data */ +static unsigned int decomp_data[] = { + 0, 257, 32, 514, 32, 776, 259, 97, 514, 32, 772, 259, 50, 259, 51, 514, + 32, 769, 258, 956, 514, 32, 807, 259, 49, 259, 111, 772, 49, 8260, 52, + 772, 49, 8260, 50, 772, 51, 8260, 52, 512, 65, 768, 512, 65, 769, 512, + 65, 770, 512, 65, 771, 512, 65, 776, 512, 65, 778, 512, 67, 807, 512, 69, + 768, 512, 69, 769, 512, 69, 770, 512, 69, 776, 512, 73, 768, 512, 73, + 769, 512, 73, 770, 512, 73, 776, 512, 78, 771, 512, 79, 768, 512, 79, + 769, 512, 79, 770, 512, 79, 771, 512, 79, 776, 512, 85, 768, 512, 85, + 769, 512, 85, 770, 512, 85, 776, 512, 89, 769, 512, 97, 768, 512, 97, + 769, 512, 97, 770, 512, 97, 771, 512, 97, 776, 512, 97, 778, 512, 99, + 807, 512, 101, 768, 512, 101, 769, 512, 101, 770, 512, 101, 776, 512, + 105, 768, 512, 105, 769, 512, 105, 770, 512, 105, 776, 512, 110, 771, + 512, 111, 768, 512, 111, 769, 512, 111, 770, 512, 111, 771, 512, 111, + 776, 512, 117, 768, 512, 117, 769, 512, 117, 770, 512, 117, 776, 512, + 121, 769, 512, 121, 776, 512, 65, 772, 512, 97, 772, 512, 65, 774, 512, + 97, 774, 512, 65, 808, 512, 97, 808, 512, 67, 769, 512, 99, 769, 512, 67, + 770, 512, 99, 770, 512, 67, 775, 512, 99, 775, 512, 67, 780, 512, 99, + 780, 512, 68, 780, 512, 100, 780, 512, 69, 772, 512, 101, 772, 512, 69, + 774, 512, 101, 774, 512, 69, 775, 512, 101, 775, 512, 69, 808, 512, 101, + 808, 512, 69, 780, 512, 101, 780, 512, 71, 770, 512, 103, 770, 512, 71, + 774, 512, 103, 774, 512, 71, 775, 512, 103, 775, 512, 71, 807, 512, 103, + 807, 512, 72, 770, 512, 104, 770, 512, 73, 771, 512, 105, 771, 512, 73, + 772, 512, 105, 772, 512, 73, 774, 512, 105, 774, 512, 73, 808, 512, 105, + 808, 512, 73, 775, 514, 73, 74, 514, 105, 106, 512, 74, 770, 512, 106, + 770, 512, 75, 807, 512, 107, 807, 512, 76, 769, 512, 108, 769, 512, 76, + 807, 512, 108, 807, 512, 76, 780, 512, 108, 780, 514, 76, 183, 514, 108, + 183, 512, 78, 769, 512, 110, 769, 512, 78, 807, 512, 110, 807, 512, 78, + 780, 512, 110, 780, 514, 700, 110, 512, 79, 772, 512, 111, 772, 512, 79, + 774, 512, 111, 774, 512, 79, 779, 512, 111, 779, 512, 82, 769, 512, 114, + 769, 512, 82, 807, 512, 114, 807, 512, 82, 780, 512, 114, 780, 512, 83, + 769, 512, 115, 769, 512, 83, 770, 512, 115, 770, 512, 83, 807, 512, 115, + 807, 512, 83, 780, 512, 115, 780, 512, 84, 807, 512, 116, 807, 512, 84, + 780, 512, 116, 780, 512, 85, 771, 512, 117, 771, 512, 85, 772, 512, 117, + 772, 512, 85, 774, 512, 117, 774, 512, 85, 778, 512, 117, 778, 512, 85, + 779, 512, 117, 779, 512, 85, 808, 512, 117, 808, 512, 87, 770, 512, 119, + 770, 512, 89, 770, 512, 121, 770, 512, 89, 776, 512, 90, 769, 512, 122, + 769, 512, 90, 775, 512, 122, 775, 512, 90, 780, 512, 122, 780, 258, 115, + 512, 79, 795, 512, 111, 795, 512, 85, 795, 512, 117, 795, 514, 68, 381, + 514, 68, 382, 514, 100, 382, 514, 76, 74, 514, 76, 106, 514, 108, 106, + 514, 78, 74, 514, 78, 106, 514, 110, 106, 512, 65, 780, 512, 97, 780, + 512, 73, 780, 512, 105, 780, 512, 79, 780, 512, 111, 780, 512, 85, 780, + 512, 117, 780, 512, 220, 772, 512, 252, 772, 512, 220, 769, 512, 252, + 769, 512, 220, 780, 512, 252, 780, 512, 220, 768, 512, 252, 768, 512, + 196, 772, 512, 228, 772, 512, 550, 772, 512, 551, 772, 512, 198, 772, + 512, 230, 772, 512, 71, 780, 512, 103, 780, 512, 75, 780, 512, 107, 780, + 512, 79, 808, 512, 111, 808, 512, 490, 772, 512, 491, 772, 512, 439, 780, + 512, 658, 780, 512, 106, 780, 514, 68, 90, 514, 68, 122, 514, 100, 122, + 512, 71, 769, 512, 103, 769, 512, 78, 768, 512, 110, 768, 512, 197, 769, + 512, 229, 769, 512, 198, 769, 512, 230, 769, 512, 216, 769, 512, 248, + 769, 512, 65, 783, 512, 97, 783, 512, 65, 785, 512, 97, 785, 512, 69, + 783, 512, 101, 783, 512, 69, 785, 512, 101, 785, 512, 73, 783, 512, 105, + 783, 512, 73, 785, 512, 105, 785, 512, 79, 783, 512, 111, 783, 512, 79, + 785, 512, 111, 785, 512, 82, 783, 512, 114, 783, 512, 82, 785, 512, 114, + 785, 512, 85, 783, 512, 117, 783, 512, 85, 785, 512, 117, 785, 512, 83, + 806, 512, 115, 806, 512, 84, 806, 512, 116, 806, 512, 72, 780, 512, 104, + 780, 512, 65, 775, 512, 97, 775, 512, 69, 807, 512, 101, 807, 512, 214, + 772, 512, 246, 772, 512, 213, 772, 512, 245, 772, 512, 79, 775, 512, 111, + 775, 512, 558, 772, 512, 559, 772, 512, 89, 772, 512, 121, 772, 259, 104, + 259, 614, 259, 106, 259, 114, 259, 633, 259, 635, 259, 641, 259, 119, + 259, 121, 514, 32, 774, 514, 32, 775, 514, 32, 778, 514, 32, 808, 514, + 32, 771, 514, 32, 779, 259, 611, 259, 108, 259, 115, 259, 120, 259, 661, + 256, 768, 256, 769, 256, 787, 512, 776, 769, 256, 697, 514, 32, 837, 256, + 59, 514, 32, 769, 512, 168, 769, 512, 913, 769, 256, 183, 512, 917, 769, + 512, 919, 769, 512, 921, 769, 512, 927, 769, 512, 933, 769, 512, 937, + 769, 512, 970, 769, 512, 921, 776, 512, 933, 776, 512, 945, 769, 512, + 949, 769, 512, 951, 769, 512, 953, 769, 512, 971, 769, 512, 953, 776, + 512, 965, 776, 512, 959, 769, 512, 965, 769, 512, 969, 769, 258, 946, + 258, 952, 258, 933, 512, 978, 769, 512, 978, 776, 258, 966, 258, 960, + 258, 954, 258, 961, 258, 962, 258, 920, 258, 949, 512, 1045, 768, 512, + 1045, 776, 512, 1043, 769, 512, 1030, 776, 512, 1050, 769, 512, 1048, + 768, 512, 1059, 774, 512, 1048, 774, 512, 1080, 774, 512, 1077, 768, 512, + 1077, 776, 512, 1075, 769, 512, 1110, 776, 512, 1082, 769, 512, 1080, + 768, 512, 1091, 774, 512, 1140, 783, 512, 1141, 783, 512, 1046, 774, 512, + 1078, 774, 512, 1040, 774, 512, 1072, 774, 512, 1040, 776, 512, 1072, + 776, 512, 1045, 774, 512, 1077, 774, 512, 1240, 776, 512, 1241, 776, 512, + 1046, 776, 512, 1078, 776, 512, 1047, 776, 512, 1079, 776, 512, 1048, + 772, 512, 1080, 772, 512, 1048, 776, 512, 1080, 776, 512, 1054, 776, 512, + 1086, 776, 512, 1256, 776, 512, 1257, 776, 512, 1069, 776, 512, 1101, + 776, 512, 1059, 772, 512, 1091, 772, 512, 1059, 776, 512, 1091, 776, 512, + 1059, 779, 512, 1091, 779, 512, 1063, 776, 512, 1095, 776, 512, 1067, + 776, 512, 1099, 776, 514, 1381, 1410, 512, 1575, 1619, 512, 1575, 1620, + 512, 1608, 1620, 512, 1575, 1621, 512, 1610, 1620, 514, 1575, 1652, 514, + 1608, 1652, 514, 1735, 1652, 514, 1610, 1652, 512, 1749, 1620, 512, 1729, + 1620, 512, 1746, 1620, 512, 2344, 2364, 512, 2352, 2364, 512, 2355, 2364, + 512, 2325, 2364, 512, 2326, 2364, 512, 2327, 2364, 512, 2332, 2364, 512, + 2337, 2364, 512, 2338, 2364, 512, 2347, 2364, 512, 2351, 2364, 512, 2503, + 2494, 512, 2503, 2519, 512, 2465, 2492, 512, 2466, 2492, 512, 2479, 2492, + 512, 2610, 2620, 512, 2616, 2620, 512, 2582, 2620, 512, 2583, 2620, 512, + 2588, 2620, 512, 2603, 2620, 512, 2887, 2902, 512, 2887, 2878, 512, 2887, + 2903, 512, 2849, 2876, 512, 2850, 2876, 512, 2962, 3031, 512, 3014, 3006, + 512, 3015, 3006, 512, 3014, 3031, 512, 3142, 3158, 512, 3263, 3285, 512, + 3270, 3285, 512, 3270, 3286, 512, 3270, 3266, 512, 3274, 3285, 512, 3398, + 3390, 512, 3399, 3390, 512, 3398, 3415, 512, 3545, 3530, 512, 3545, 3535, + 512, 3548, 3530, 512, 3545, 3551, 514, 3661, 3634, 514, 3789, 3762, 514, + 3755, 3737, 514, 3755, 3745, 257, 3851, 512, 3906, 4023, 512, 3916, 4023, + 512, 3921, 4023, 512, 3926, 4023, 512, 3931, 4023, 512, 3904, 4021, 512, + 3953, 3954, 512, 3953, 3956, 512, 4018, 3968, 514, 4018, 3969, 512, 4019, + 3968, 514, 4019, 3969, 512, 3953, 3968, 512, 3986, 4023, 512, 3996, 4023, + 512, 4001, 4023, 512, 4006, 4023, 512, 4011, 4023, 512, 3984, 4021, 512, + 4133, 4142, 512, 65, 805, 512, 97, 805, 512, 66, 775, 512, 98, 775, 512, + 66, 803, 512, 98, 803, 512, 66, 817, 512, 98, 817, 512, 199, 769, 512, + 231, 769, 512, 68, 775, 512, 100, 775, 512, 68, 803, 512, 100, 803, 512, + 68, 817, 512, 100, 817, 512, 68, 807, 512, 100, 807, 512, 68, 813, 512, + 100, 813, 512, 274, 768, 512, 275, 768, 512, 274, 769, 512, 275, 769, + 512, 69, 813, 512, 101, 813, 512, 69, 816, 512, 101, 816, 512, 552, 774, + 512, 553, 774, 512, 70, 775, 512, 102, 775, 512, 71, 772, 512, 103, 772, + 512, 72, 775, 512, 104, 775, 512, 72, 803, 512, 104, 803, 512, 72, 776, + 512, 104, 776, 512, 72, 807, 512, 104, 807, 512, 72, 814, 512, 104, 814, + 512, 73, 816, 512, 105, 816, 512, 207, 769, 512, 239, 769, 512, 75, 769, + 512, 107, 769, 512, 75, 803, 512, 107, 803, 512, 75, 817, 512, 107, 817, + 512, 76, 803, 512, 108, 803, 512, 7734, 772, 512, 7735, 772, 512, 76, + 817, 512, 108, 817, 512, 76, 813, 512, 108, 813, 512, 77, 769, 512, 109, + 769, 512, 77, 775, 512, 109, 775, 512, 77, 803, 512, 109, 803, 512, 78, + 775, 512, 110, 775, 512, 78, 803, 512, 110, 803, 512, 78, 817, 512, 110, + 817, 512, 78, 813, 512, 110, 813, 512, 213, 769, 512, 245, 769, 512, 213, + 776, 512, 245, 776, 512, 332, 768, 512, 333, 768, 512, 332, 769, 512, + 333, 769, 512, 80, 769, 512, 112, 769, 512, 80, 775, 512, 112, 775, 512, + 82, 775, 512, 114, 775, 512, 82, 803, 512, 114, 803, 512, 7770, 772, 512, + 7771, 772, 512, 82, 817, 512, 114, 817, 512, 83, 775, 512, 115, 775, 512, + 83, 803, 512, 115, 803, 512, 346, 775, 512, 347, 775, 512, 352, 775, 512, + 353, 775, 512, 7778, 775, 512, 7779, 775, 512, 84, 775, 512, 116, 775, + 512, 84, 803, 512, 116, 803, 512, 84, 817, 512, 116, 817, 512, 84, 813, + 512, 116, 813, 512, 85, 804, 512, 117, 804, 512, 85, 816, 512, 117, 816, + 512, 85, 813, 512, 117, 813, 512, 360, 769, 512, 361, 769, 512, 362, 776, + 512, 363, 776, 512, 86, 771, 512, 118, 771, 512, 86, 803, 512, 118, 803, + 512, 87, 768, 512, 119, 768, 512, 87, 769, 512, 119, 769, 512, 87, 776, + 512, 119, 776, 512, 87, 775, 512, 119, 775, 512, 87, 803, 512, 119, 803, + 512, 88, 775, 512, 120, 775, 512, 88, 776, 512, 120, 776, 512, 89, 775, + 512, 121, 775, 512, 90, 770, 512, 122, 770, 512, 90, 803, 512, 122, 803, + 512, 90, 817, 512, 122, 817, 512, 104, 817, 512, 116, 776, 512, 119, 778, + 512, 121, 778, 514, 97, 702, 512, 383, 775, 512, 65, 803, 512, 97, 803, + 512, 65, 777, 512, 97, 777, 512, 194, 769, 512, 226, 769, 512, 194, 768, + 512, 226, 768, 512, 194, 777, 512, 226, 777, 512, 194, 771, 512, 226, + 771, 512, 7840, 770, 512, 7841, 770, 512, 258, 769, 512, 259, 769, 512, + 258, 768, 512, 259, 768, 512, 258, 777, 512, 259, 777, 512, 258, 771, + 512, 259, 771, 512, 7840, 774, 512, 7841, 774, 512, 69, 803, 512, 101, + 803, 512, 69, 777, 512, 101, 777, 512, 69, 771, 512, 101, 771, 512, 202, + 769, 512, 234, 769, 512, 202, 768, 512, 234, 768, 512, 202, 777, 512, + 234, 777, 512, 202, 771, 512, 234, 771, 512, 7864, 770, 512, 7865, 770, + 512, 73, 777, 512, 105, 777, 512, 73, 803, 512, 105, 803, 512, 79, 803, + 512, 111, 803, 512, 79, 777, 512, 111, 777, 512, 212, 769, 512, 244, 769, + 512, 212, 768, 512, 244, 768, 512, 212, 777, 512, 244, 777, 512, 212, + 771, 512, 244, 771, 512, 7884, 770, 512, 7885, 770, 512, 416, 769, 512, + 417, 769, 512, 416, 768, 512, 417, 768, 512, 416, 777, 512, 417, 777, + 512, 416, 771, 512, 417, 771, 512, 416, 803, 512, 417, 803, 512, 85, 803, + 512, 117, 803, 512, 85, 777, 512, 117, 777, 512, 431, 769, 512, 432, 769, + 512, 431, 768, 512, 432, 768, 512, 431, 777, 512, 432, 777, 512, 431, + 771, 512, 432, 771, 512, 431, 803, 512, 432, 803, 512, 89, 768, 512, 121, + 768, 512, 89, 803, 512, 121, 803, 512, 89, 777, 512, 121, 777, 512, 89, + 771, 512, 121, 771, 512, 945, 787, 512, 945, 788, 512, 7936, 768, 512, + 7937, 768, 512, 7936, 769, 512, 7937, 769, 512, 7936, 834, 512, 7937, + 834, 512, 913, 787, 512, 913, 788, 512, 7944, 768, 512, 7945, 768, 512, + 7944, 769, 512, 7945, 769, 512, 7944, 834, 512, 7945, 834, 512, 949, 787, + 512, 949, 788, 512, 7952, 768, 512, 7953, 768, 512, 7952, 769, 512, 7953, + 769, 512, 917, 787, 512, 917, 788, 512, 7960, 768, 512, 7961, 768, 512, + 7960, 769, 512, 7961, 769, 512, 951, 787, 512, 951, 788, 512, 7968, 768, + 512, 7969, 768, 512, 7968, 769, 512, 7969, 769, 512, 7968, 834, 512, + 7969, 834, 512, 919, 787, 512, 919, 788, 512, 7976, 768, 512, 7977, 768, + 512, 7976, 769, 512, 7977, 769, 512, 7976, 834, 512, 7977, 834, 512, 953, + 787, 512, 953, 788, 512, 7984, 768, 512, 7985, 768, 512, 7984, 769, 512, + 7985, 769, 512, 7984, 834, 512, 7985, 834, 512, 921, 787, 512, 921, 788, + 512, 7992, 768, 512, 7993, 768, 512, 7992, 769, 512, 7993, 769, 512, + 7992, 834, 512, 7993, 834, 512, 959, 787, 512, 959, 788, 512, 8000, 768, + 512, 8001, 768, 512, 8000, 769, 512, 8001, 769, 512, 927, 787, 512, 927, + 788, 512, 8008, 768, 512, 8009, 768, 512, 8008, 769, 512, 8009, 769, 512, + 965, 787, 512, 965, 788, 512, 8016, 768, 512, 8017, 768, 512, 8016, 769, + 512, 8017, 769, 512, 8016, 834, 512, 8017, 834, 512, 933, 788, 512, 8025, + 768, 512, 8025, 769, 512, 8025, 834, 512, 969, 787, 512, 969, 788, 512, + 8032, 768, 512, 8033, 768, 512, 8032, 769, 512, 8033, 769, 512, 8032, + 834, 512, 8033, 834, 512, 937, 787, 512, 937, 788, 512, 8040, 768, 512, + 8041, 768, 512, 8040, 769, 512, 8041, 769, 512, 8040, 834, 512, 8041, + 834, 512, 945, 768, 256, 940, 512, 949, 768, 256, 941, 512, 951, 768, + 256, 942, 512, 953, 768, 256, 943, 512, 959, 768, 256, 972, 512, 965, + 768, 256, 973, 512, 969, 768, 256, 974, 512, 7936, 837, 512, 7937, 837, + 512, 7938, 837, 512, 7939, 837, 512, 7940, 837, 512, 7941, 837, 512, + 7942, 837, 512, 7943, 837, 512, 7944, 837, 512, 7945, 837, 512, 7946, + 837, 512, 7947, 837, 512, 7948, 837, 512, 7949, 837, 512, 7950, 837, 512, + 7951, 837, 512, 7968, 837, 512, 7969, 837, 512, 7970, 837, 512, 7971, + 837, 512, 7972, 837, 512, 7973, 837, 512, 7974, 837, 512, 7975, 837, 512, + 7976, 837, 512, 7977, 837, 512, 7978, 837, 512, 7979, 837, 512, 7980, + 837, 512, 7981, 837, 512, 7982, 837, 512, 7983, 837, 512, 8032, 837, 512, + 8033, 837, 512, 8034, 837, 512, 8035, 837, 512, 8036, 837, 512, 8037, + 837, 512, 8038, 837, 512, 8039, 837, 512, 8040, 837, 512, 8041, 837, 512, + 8042, 837, 512, 8043, 837, 512, 8044, 837, 512, 8045, 837, 512, 8046, + 837, 512, 8047, 837, 512, 945, 774, 512, 945, 772, 512, 8048, 837, 512, + 945, 837, 512, 940, 837, 512, 945, 834, 512, 8118, 837, 512, 913, 774, + 512, 913, 772, 512, 913, 768, 256, 902, 512, 913, 837, 514, 32, 787, 256, + 953, 514, 32, 787, 514, 32, 834, 512, 168, 834, 512, 8052, 837, 512, 951, + 837, 512, 942, 837, 512, 951, 834, 512, 8134, 837, 512, 917, 768, 256, + 904, 512, 919, 768, 256, 905, 512, 919, 837, 512, 8127, 768, 512, 8127, + 769, 512, 8127, 834, 512, 953, 774, 512, 953, 772, 512, 970, 768, 256, + 912, 512, 953, 834, 512, 970, 834, 512, 921, 774, 512, 921, 772, 512, + 921, 768, 256, 906, 512, 8190, 768, 512, 8190, 769, 512, 8190, 834, 512, + 965, 774, 512, 965, 772, 512, 971, 768, 256, 944, 512, 961, 787, 512, + 961, 788, 512, 965, 834, 512, 971, 834, 512, 933, 774, 512, 933, 772, + 512, 933, 768, 256, 910, 512, 929, 788, 512, 168, 768, 256, 901, 256, 96, + 512, 8060, 837, 512, 969, 837, 512, 974, 837, 512, 969, 834, 512, 8182, + 837, 512, 927, 768, 256, 908, 512, 937, 768, 256, 911, 512, 937, 837, + 256, 180, 514, 32, 788, 256, 8194, 256, 8195, 258, 32, 258, 32, 258, 32, + 258, 32, 258, 32, 257, 32, 258, 32, 258, 32, 258, 32, 257, 8208, 514, 32, + 819, 258, 46, 514, 46, 46, 770, 46, 46, 46, 257, 32, 514, 8242, 8242, + 770, 8242, 8242, 8242, 514, 8245, 8245, 770, 8245, 8245, 8245, 514, 33, + 33, 514, 32, 773, 514, 63, 63, 514, 63, 33, 514, 33, 63, 1026, 8242, + 8242, 8242, 8242, 258, 32, 259, 48, 259, 105, 259, 52, 259, 53, 259, 54, + 259, 55, 259, 56, 259, 57, 259, 43, 259, 8722, 259, 61, 259, 40, 259, 41, + 259, 110, 261, 48, 261, 49, 261, 50, 261, 51, 261, 52, 261, 53, 261, 54, + 261, 55, 261, 56, 261, 57, 261, 43, 261, 8722, 261, 61, 261, 40, 261, 41, + 514, 82, 115, 770, 97, 47, 99, 770, 97, 47, 115, 262, 67, 514, 176, 67, + 770, 99, 47, 111, 770, 99, 47, 117, 258, 400, 514, 176, 70, 262, 103, + 262, 72, 262, 72, 262, 72, 262, 104, 262, 295, 262, 73, 262, 73, 262, 76, + 262, 108, 262, 78, 514, 78, 111, 262, 80, 262, 81, 262, 82, 262, 82, 262, + 82, 515, 83, 77, 770, 84, 69, 76, 515, 84, 77, 262, 90, 256, 937, 262, + 90, 256, 75, 256, 197, 262, 66, 262, 67, 262, 101, 262, 69, 262, 70, 262, + 77, 262, 111, 258, 1488, 258, 1489, 258, 1490, 258, 1491, 262, 105, 262, + 947, 262, 915, 262, 928, 262, 8721, 262, 68, 262, 100, 262, 101, 262, + 105, 262, 106, 772, 49, 8260, 51, 772, 50, 8260, 51, 772, 49, 8260, 53, + 772, 50, 8260, 53, 772, 51, 8260, 53, 772, 52, 8260, 53, 772, 49, 8260, + 54, 772, 53, 8260, 54, 772, 49, 8260, 56, 772, 51, 8260, 56, 772, 53, + 8260, 56, 772, 55, 8260, 56, 516, 49, 8260, 258, 73, 514, 73, 73, 770, + 73, 73, 73, 514, 73, 86, 258, 86, 514, 86, 73, 770, 86, 73, 73, 1026, 86, + 73, 73, 73, 514, 73, 88, 258, 88, 514, 88, 73, 770, 88, 73, 73, 258, 76, + 258, 67, 258, 68, 258, 77, 258, 105, 514, 105, 105, 770, 105, 105, 105, + 514, 105, 118, 258, 118, 514, 118, 105, 770, 118, 105, 105, 1026, 118, + 105, 105, 105, 514, 105, 120, 258, 120, 514, 120, 105, 770, 120, 105, + 105, 258, 108, 258, 99, 258, 100, 258, 109, 512, 8592, 824, 512, 8594, + 824, 512, 8596, 824, 512, 8656, 824, 512, 8660, 824, 512, 8658, 824, 512, + 8707, 824, 512, 8712, 824, 512, 8715, 824, 512, 8739, 824, 512, 8741, + 824, 514, 8747, 8747, 770, 8747, 8747, 8747, 514, 8750, 8750, 770, 8750, + 8750, 8750, 512, 8764, 824, 512, 8771, 824, 512, 8773, 824, 512, 8776, + 824, 512, 61, 824, 512, 8801, 824, 512, 8781, 824, 512, 60, 824, 512, 62, + 824, 512, 8804, 824, 512, 8805, 824, 512, 8818, 824, 512, 8819, 824, 512, + 8822, 824, 512, 8823, 824, 512, 8826, 824, 512, 8827, 824, 512, 8834, + 824, 512, 8835, 824, 512, 8838, 824, 512, 8839, 824, 512, 8866, 824, 512, + 8872, 824, 512, 8873, 824, 512, 8875, 824, 512, 8828, 824, 512, 8829, + 824, 512, 8849, 824, 512, 8850, 824, 512, 8882, 824, 512, 8883, 824, 512, + 8884, 824, 512, 8885, 824, 256, 12296, 256, 12297, 263, 49, 263, 50, 263, + 51, 263, 52, 263, 53, 263, 54, 263, 55, 263, 56, 263, 57, 519, 49, 48, + 519, 49, 49, 519, 49, 50, 519, 49, 51, 519, 49, 52, 519, 49, 53, 519, 49, + 54, 519, 49, 55, 519, 49, 56, 519, 49, 57, 519, 50, 48, 770, 40, 49, 41, + 770, 40, 50, 41, 770, 40, 51, 41, 770, 40, 52, 41, 770, 40, 53, 41, 770, + 40, 54, 41, 770, 40, 55, 41, 770, 40, 56, 41, 770, 40, 57, 41, 1026, 40, + 49, 48, 41, 1026, 40, 49, 49, 41, 1026, 40, 49, 50, 41, 1026, 40, 49, 51, + 41, 1026, 40, 49, 52, 41, 1026, 40, 49, 53, 41, 1026, 40, 49, 54, 41, + 1026, 40, 49, 55, 41, 1026, 40, 49, 56, 41, 1026, 40, 49, 57, 41, 1026, + 40, 50, 48, 41, 514, 49, 46, 514, 50, 46, 514, 51, 46, 514, 52, 46, 514, + 53, 46, 514, 54, 46, 514, 55, 46, 514, 56, 46, 514, 57, 46, 770, 49, 48, + 46, 770, 49, 49, 46, 770, 49, 50, 46, 770, 49, 51, 46, 770, 49, 52, 46, + 770, 49, 53, 46, 770, 49, 54, 46, 770, 49, 55, 46, 770, 49, 56, 46, 770, + 49, 57, 46, 770, 50, 48, 46, 770, 40, 97, 41, 770, 40, 98, 41, 770, 40, + 99, 41, 770, 40, 100, 41, 770, 40, 101, 41, 770, 40, 102, 41, 770, 40, + 103, 41, 770, 40, 104, 41, 770, 40, 105, 41, 770, 40, 106, 41, 770, 40, + 107, 41, 770, 40, 108, 41, 770, 40, 109, 41, 770, 40, 110, 41, 770, 40, + 111, 41, 770, 40, 112, 41, 770, 40, 113, 41, 770, 40, 114, 41, 770, 40, + 115, 41, 770, 40, 116, 41, 770, 40, 117, 41, 770, 40, 118, 41, 770, 40, + 119, 41, 770, 40, 120, 41, 770, 40, 121, 41, 770, 40, 122, 41, 263, 65, + 263, 66, 263, 67, 263, 68, 263, 69, 263, 70, 263, 71, 263, 72, 263, 73, + 263, 74, 263, 75, 263, 76, 263, 77, 263, 78, 263, 79, 263, 80, 263, 81, + 263, 82, 263, 83, 263, 84, 263, 85, 263, 86, 263, 87, 263, 88, 263, 89, + 263, 90, 263, 97, 263, 98, 263, 99, 263, 100, 263, 101, 263, 102, 263, + 103, 263, 104, 263, 105, 263, 106, 263, 107, 263, 108, 263, 109, 263, + 110, 263, 111, 263, 112, 263, 113, 263, 114, 263, 115, 263, 116, 263, + 117, 263, 118, 263, 119, 263, 120, 263, 121, 263, 122, 263, 48, 1026, + 8747, 8747, 8747, 8747, 770, 58, 58, 61, 514, 61, 61, 770, 61, 61, 61, + 512, 10973, 824, 258, 27597, 258, 40863, 258, 19968, 258, 20008, 258, + 20022, 258, 20031, 258, 20057, 258, 20101, 258, 20108, 258, 20128, 258, + 20154, 258, 20799, 258, 20837, 258, 20843, 258, 20866, 258, 20886, 258, + 20907, 258, 20960, 258, 20981, 258, 20992, 258, 21147, 258, 21241, 258, + 21269, 258, 21274, 258, 21304, 258, 21313, 258, 21340, 258, 21353, 258, + 21378, 258, 21430, 258, 21448, 258, 21475, 258, 22231, 258, 22303, 258, + 22763, 258, 22786, 258, 22794, 258, 22805, 258, 22823, 258, 22899, 258, + 23376, 258, 23424, 258, 23544, 258, 23567, 258, 23586, 258, 23608, 258, + 23662, 258, 23665, 258, 24027, 258, 24037, 258, 24049, 258, 24062, 258, + 24178, 258, 24186, 258, 24191, 258, 24308, 258, 24318, 258, 24331, 258, + 24339, 258, 24400, 258, 24417, 258, 24435, 258, 24515, 258, 25096, 258, + 25142, 258, 25163, 258, 25903, 258, 25908, 258, 25991, 258, 26007, 258, + 26020, 258, 26041, 258, 26080, 258, 26085, 258, 26352, 258, 26376, 258, + 26408, 258, 27424, 258, 27490, 258, 27513, 258, 27571, 258, 27595, 258, + 27604, 258, 27611, 258, 27663, 258, 27668, 258, 27700, 258, 28779, 258, + 29226, 258, 29238, 258, 29243, 258, 29247, 258, 29255, 258, 29273, 258, + 29275, 258, 29356, 258, 29572, 258, 29577, 258, 29916, 258, 29926, 258, + 29976, 258, 29983, 258, 29992, 258, 30000, 258, 30091, 258, 30098, 258, + 30326, 258, 30333, 258, 30382, 258, 30399, 258, 30446, 258, 30683, 258, + 30690, 258, 30707, 258, 31034, 258, 31160, 258, 31166, 258, 31348, 258, + 31435, 258, 31481, 258, 31859, 258, 31992, 258, 32566, 258, 32593, 258, + 32650, 258, 32701, 258, 32769, 258, 32780, 258, 32786, 258, 32819, 258, + 32895, 258, 32905, 258, 33251, 258, 33258, 258, 33267, 258, 33276, 258, + 33292, 258, 33307, 258, 33311, 258, 33390, 258, 33394, 258, 33400, 258, + 34381, 258, 34411, 258, 34880, 258, 34892, 258, 34915, 258, 35198, 258, + 35211, 258, 35282, 258, 35328, 258, 35895, 258, 35910, 258, 35925, 258, + 35960, 258, 35997, 258, 36196, 258, 36208, 258, 36275, 258, 36523, 258, + 36554, 258, 36763, 258, 36784, 258, 36789, 258, 37009, 258, 37193, 258, + 37318, 258, 37324, 258, 37329, 258, 38263, 258, 38272, 258, 38428, 258, + 38582, 258, 38585, 258, 38632, 258, 38737, 258, 38750, 258, 38754, 258, + 38761, 258, 38859, 258, 38893, 258, 38899, 258, 38913, 258, 39080, 258, + 39131, 258, 39135, 258, 39318, 258, 39321, 258, 39340, 258, 39592, 258, + 39640, 258, 39647, 258, 39717, 258, 39727, 258, 39730, 258, 39740, 258, + 39770, 258, 40165, 258, 40565, 258, 40575, 258, 40613, 258, 40635, 258, + 40643, 258, 40653, 258, 40657, 258, 40697, 258, 40701, 258, 40718, 258, + 40723, 258, 40736, 258, 40763, 258, 40778, 258, 40786, 258, 40845, 258, + 40860, 258, 40864, 264, 32, 258, 12306, 258, 21313, 258, 21316, 258, + 21317, 512, 12363, 12441, 512, 12365, 12441, 512, 12367, 12441, 512, + 12369, 12441, 512, 12371, 12441, 512, 12373, 12441, 512, 12375, 12441, + 512, 12377, 12441, 512, 12379, 12441, 512, 12381, 12441, 512, 12383, + 12441, 512, 12385, 12441, 512, 12388, 12441, 512, 12390, 12441, 512, + 12392, 12441, 512, 12399, 12441, 512, 12399, 12442, 512, 12402, 12441, + 512, 12402, 12442, 512, 12405, 12441, 512, 12405, 12442, 512, 12408, + 12441, 512, 12408, 12442, 512, 12411, 12441, 512, 12411, 12442, 512, + 12358, 12441, 514, 32, 12441, 514, 32, 12442, 512, 12445, 12441, 521, + 12424, 12426, 512, 12459, 12441, 512, 12461, 12441, 512, 12463, 12441, + 512, 12465, 12441, 512, 12467, 12441, 512, 12469, 12441, 512, 12471, + 12441, 512, 12473, 12441, 512, 12475, 12441, 512, 12477, 12441, 512, + 12479, 12441, 512, 12481, 12441, 512, 12484, 12441, 512, 12486, 12441, + 512, 12488, 12441, 512, 12495, 12441, 512, 12495, 12442, 512, 12498, + 12441, 512, 12498, 12442, 512, 12501, 12441, 512, 12501, 12442, 512, + 12504, 12441, 512, 12504, 12442, 512, 12507, 12441, 512, 12507, 12442, + 512, 12454, 12441, 512, 12527, 12441, 512, 12528, 12441, 512, 12529, + 12441, 512, 12530, 12441, 512, 12541, 12441, 521, 12467, 12488, 258, + 4352, 258, 4353, 258, 4522, 258, 4354, 258, 4524, 258, 4525, 258, 4355, + 258, 4356, 258, 4357, 258, 4528, 258, 4529, 258, 4530, 258, 4531, 258, + 4532, 258, 4533, 258, 4378, 258, 4358, 258, 4359, 258, 4360, 258, 4385, + 258, 4361, 258, 4362, 258, 4363, 258, 4364, 258, 4365, 258, 4366, 258, + 4367, 258, 4368, 258, 4369, 258, 4370, 258, 4449, 258, 4450, 258, 4451, + 258, 4452, 258, 4453, 258, 4454, 258, 4455, 258, 4456, 258, 4457, 258, + 4458, 258, 4459, 258, 4460, 258, 4461, 258, 4462, 258, 4463, 258, 4464, + 258, 4465, 258, 4466, 258, 4467, 258, 4468, 258, 4469, 258, 4448, 258, + 4372, 258, 4373, 258, 4551, 258, 4552, 258, 4556, 258, 4558, 258, 4563, + 258, 4567, 258, 4569, 258, 4380, 258, 4573, 258, 4575, 258, 4381, 258, + 4382, 258, 4384, 258, 4386, 258, 4387, 258, 4391, 258, 4393, 258, 4395, + 258, 4396, 258, 4397, 258, 4398, 258, 4399, 258, 4402, 258, 4406, 258, + 4416, 258, 4423, 258, 4428, 258, 4593, 258, 4594, 258, 4439, 258, 4440, + 258, 4441, 258, 4484, 258, 4485, 258, 4488, 258, 4497, 258, 4498, 258, + 4500, 258, 4510, 258, 4513, 259, 19968, 259, 20108, 259, 19977, 259, + 22235, 259, 19978, 259, 20013, 259, 19979, 259, 30002, 259, 20057, 259, + 19993, 259, 19969, 259, 22825, 259, 22320, 259, 20154, 770, 40, 4352, 41, + 770, 40, 4354, 41, 770, 40, 4355, 41, 770, 40, 4357, 41, 770, 40, 4358, + 41, 770, 40, 4359, 41, 770, 40, 4361, 41, 770, 40, 4363, 41, 770, 40, + 4364, 41, 770, 40, 4366, 41, 770, 40, 4367, 41, 770, 40, 4368, 41, 770, + 40, 4369, 41, 770, 40, 4370, 41, 1026, 40, 4352, 4449, 41, 1026, 40, + 4354, 4449, 41, 1026, 40, 4355, 4449, 41, 1026, 40, 4357, 4449, 41, 1026, + 40, 4358, 4449, 41, 1026, 40, 4359, 4449, 41, 1026, 40, 4361, 4449, 41, + 1026, 40, 4363, 4449, 41, 1026, 40, 4364, 4449, 41, 1026, 40, 4366, 4449, + 41, 1026, 40, 4367, 4449, 41, 1026, 40, 4368, 4449, 41, 1026, 40, 4369, + 4449, 41, 1026, 40, 4370, 4449, 41, 1026, 40, 4364, 4462, 41, 770, 40, + 19968, 41, 770, 40, 20108, 41, 770, 40, 19977, 41, 770, 40, 22235, 41, + 770, 40, 20116, 41, 770, 40, 20845, 41, 770, 40, 19971, 41, 770, 40, + 20843, 41, 770, 40, 20061, 41, 770, 40, 21313, 41, 770, 40, 26376, 41, + 770, 40, 28779, 41, 770, 40, 27700, 41, 770, 40, 26408, 41, 770, 40, + 37329, 41, 770, 40, 22303, 41, 770, 40, 26085, 41, 770, 40, 26666, 41, + 770, 40, 26377, 41, 770, 40, 31038, 41, 770, 40, 21517, 41, 770, 40, + 29305, 41, 770, 40, 36001, 41, 770, 40, 31069, 41, 770, 40, 21172, 41, + 770, 40, 20195, 41, 770, 40, 21628, 41, 770, 40, 23398, 41, 770, 40, + 30435, 41, 770, 40, 20225, 41, 770, 40, 36039, 41, 770, 40, 21332, 41, + 770, 40, 31085, 41, 770, 40, 20241, 41, 770, 40, 33258, 41, 770, 40, + 33267, 41, 519, 50, 49, 519, 50, 50, 519, 50, 51, 519, 50, 52, 519, 50, + 53, 519, 50, 54, 519, 50, 55, 519, 50, 56, 519, 50, 57, 519, 51, 48, 519, + 51, 49, 519, 51, 50, 519, 51, 51, 519, 51, 52, 519, 51, 53, 263, 4352, + 263, 4354, 263, 4355, 263, 4357, 263, 4358, 263, 4359, 263, 4361, 263, + 4363, 263, 4364, 263, 4366, 263, 4367, 263, 4368, 263, 4369, 263, 4370, + 519, 4352, 4449, 519, 4354, 4449, 519, 4355, 4449, 519, 4357, 4449, 519, + 4358, 4449, 519, 4359, 4449, 519, 4361, 4449, 519, 4363, 4449, 519, 4364, + 4449, 519, 4366, 4449, 519, 4367, 4449, 519, 4368, 4449, 519, 4369, 4449, + 519, 4370, 4449, 263, 19968, 263, 20108, 263, 19977, 263, 22235, 263, + 20116, 263, 20845, 263, 19971, 263, 20843, 263, 20061, 263, 21313, 263, + 26376, 263, 28779, 263, 27700, 263, 26408, 263, 37329, 263, 22303, 263, + 26085, 263, 26666, 263, 26377, 263, 31038, 263, 21517, 263, 29305, 263, + 36001, 263, 31069, 263, 21172, 263, 31192, 263, 30007, 263, 22899, 263, + 36969, 263, 20778, 263, 21360, 263, 27880, 263, 38917, 263, 20241, 263, + 20889, 263, 27491, 263, 19978, 263, 20013, 263, 19979, 263, 24038, 263, + 21491, 263, 21307, 263, 23447, 263, 23398, 263, 30435, 263, 20225, 263, + 36039, 263, 21332, 263, 22812, 519, 51, 54, 519, 51, 55, 519, 51, 56, + 519, 51, 57, 519, 52, 48, 519, 52, 49, 519, 52, 50, 519, 52, 51, 519, 52, + 52, 519, 52, 53, 519, 52, 54, 519, 52, 55, 519, 52, 56, 519, 52, 57, 519, + 53, 48, 514, 49, 26376, 514, 50, 26376, 514, 51, 26376, 514, 52, 26376, + 514, 53, 26376, 514, 54, 26376, 514, 55, 26376, 514, 56, 26376, 514, 57, + 26376, 770, 49, 48, 26376, 770, 49, 49, 26376, 770, 49, 50, 26376, 263, + 12450, 263, 12452, 263, 12454, 263, 12456, 263, 12458, 263, 12459, 263, + 12461, 263, 12463, 263, 12465, 263, 12467, 263, 12469, 263, 12471, 263, + 12473, 263, 12475, 263, 12477, 263, 12479, 263, 12481, 263, 12484, 263, + 12486, 263, 12488, 263, 12490, 263, 12491, 263, 12492, 263, 12493, 263, + 12494, 263, 12495, 263, 12498, 263, 12501, 263, 12504, 263, 12507, 263, + 12510, 263, 12511, 263, 12512, 263, 12513, 263, 12514, 263, 12516, 263, + 12518, 263, 12520, 263, 12521, 263, 12522, 263, 12523, 263, 12524, 263, + 12525, 263, 12527, 263, 12528, 263, 12529, 263, 12530, 1034, 12450, + 12497, 12540, 12488, 1034, 12450, 12523, 12501, 12449, 1034, 12450, + 12531, 12506, 12450, 778, 12450, 12540, 12523, 1034, 12452, 12491, 12531, + 12464, 778, 12452, 12531, 12481, 778, 12454, 12457, 12531, 1290, 12456, + 12473, 12463, 12540, 12489, 1034, 12456, 12540, 12459, 12540, 778, 12458, + 12531, 12473, 778, 12458, 12540, 12512, 778, 12459, 12452, 12522, 1034, + 12459, 12521, 12483, 12488, 1034, 12459, 12525, 12522, 12540, 778, 12460, + 12525, 12531, 778, 12460, 12531, 12510, 522, 12462, 12460, 778, 12462, + 12491, 12540, 1034, 12461, 12517, 12522, 12540, 1034, 12462, 12523, + 12480, 12540, 522, 12461, 12525, 1290, 12461, 12525, 12464, 12521, 12512, + 1546, 12461, 12525, 12513, 12540, 12488, 12523, 1290, 12461, 12525, + 12527, 12483, 12488, 778, 12464, 12521, 12512, 1290, 12464, 12521, 12512, + 12488, 12531, 1290, 12463, 12523, 12476, 12452, 12525, 1034, 12463, + 12525, 12540, 12493, 778, 12465, 12540, 12473, 778, 12467, 12523, 12490, + 778, 12467, 12540, 12509, 1034, 12469, 12452, 12463, 12523, 1290, 12469, + 12531, 12481, 12540, 12512, 1034, 12471, 12522, 12531, 12464, 778, 12475, + 12531, 12481, 778, 12475, 12531, 12488, 778, 12480, 12540, 12473, 522, + 12487, 12471, 522, 12489, 12523, 522, 12488, 12531, 522, 12490, 12494, + 778, 12494, 12483, 12488, 778, 12495, 12452, 12484, 1290, 12497, 12540, + 12475, 12531, 12488, 778, 12497, 12540, 12484, 1034, 12496, 12540, 12524, + 12523, 1290, 12500, 12450, 12473, 12488, 12523, 778, 12500, 12463, 12523, + 522, 12500, 12467, 522, 12499, 12523, 1290, 12501, 12449, 12521, 12483, + 12489, 1034, 12501, 12451, 12540, 12488, 1290, 12502, 12483, 12471, + 12455, 12523, 778, 12501, 12521, 12531, 1290, 12504, 12463, 12479, 12540, + 12523, 522, 12506, 12477, 778, 12506, 12491, 12498, 778, 12504, 12523, + 12484, 778, 12506, 12531, 12473, 778, 12506, 12540, 12472, 778, 12505, + 12540, 12479, 1034, 12509, 12452, 12531, 12488, 778, 12508, 12523, 12488, + 522, 12507, 12531, 778, 12509, 12531, 12489, 778, 12507, 12540, 12523, + 778, 12507, 12540, 12531, 1034, 12510, 12452, 12463, 12525, 778, 12510, + 12452, 12523, 778, 12510, 12483, 12495, 778, 12510, 12523, 12463, 1290, + 12510, 12531, 12471, 12519, 12531, 1034, 12511, 12463, 12525, 12531, 522, + 12511, 12522, 1290, 12511, 12522, 12496, 12540, 12523, 522, 12513, 12460, + 1034, 12513, 12460, 12488, 12531, 1034, 12513, 12540, 12488, 12523, 778, + 12516, 12540, 12489, 778, 12516, 12540, 12523, 778, 12518, 12450, 12531, + 1034, 12522, 12483, 12488, 12523, 522, 12522, 12521, 778, 12523, 12500, + 12540, 1034, 12523, 12540, 12502, 12523, 522, 12524, 12512, 1290, 12524, + 12531, 12488, 12466, 12531, 778, 12527, 12483, 12488, 514, 48, 28857, + 514, 49, 28857, 514, 50, 28857, 514, 51, 28857, 514, 52, 28857, 514, 53, + 28857, 514, 54, 28857, 514, 55, 28857, 514, 56, 28857, 514, 57, 28857, + 770, 49, 48, 28857, 770, 49, 49, 28857, 770, 49, 50, 28857, 770, 49, 51, + 28857, 770, 49, 52, 28857, 770, 49, 53, 28857, 770, 49, 54, 28857, 770, + 49, 55, 28857, 770, 49, 56, 28857, 770, 49, 57, 28857, 770, 50, 48, + 28857, 770, 50, 49, 28857, 770, 50, 50, 28857, 770, 50, 51, 28857, 770, + 50, 52, 28857, 778, 104, 80, 97, 522, 100, 97, 522, 65, 85, 778, 98, 97, + 114, 522, 111, 86, 522, 112, 99, 522, 24179, 25104, 522, 26157, 21644, + 522, 22823, 27491, 522, 26126, 27835, 1034, 26666, 24335, 20250, 31038, + 522, 112, 65, 522, 110, 65, 522, 956, 65, 522, 109, 65, 522, 107, 65, + 522, 75, 66, 522, 77, 66, 522, 71, 66, 778, 99, 97, 108, 1034, 107, 99, + 97, 108, 522, 112, 70, 522, 110, 70, 522, 956, 70, 522, 956, 103, 522, + 109, 103, 522, 107, 103, 522, 72, 122, 778, 107, 72, 122, 778, 77, 72, + 122, 778, 71, 72, 122, 778, 84, 72, 122, 522, 956, 8467, 522, 109, 8467, + 522, 100, 8467, 522, 107, 8467, 522, 102, 109, 522, 110, 109, 522, 956, + 109, 522, 109, 109, 522, 99, 109, 522, 107, 109, 778, 109, 109, 178, 778, + 99, 109, 178, 522, 109, 178, 778, 107, 109, 178, 778, 109, 109, 179, 778, + 99, 109, 179, 522, 109, 179, 778, 107, 109, 179, 778, 109, 8725, 115, + 1034, 109, 8725, 115, 178, 522, 80, 97, 778, 107, 80, 97, 778, 77, 80, + 97, 778, 71, 80, 97, 778, 114, 97, 100, 1290, 114, 97, 100, 8725, 115, + 1546, 114, 97, 100, 8725, 115, 178, 522, 112, 115, 522, 110, 115, 522, + 956, 115, 522, 109, 115, 522, 112, 86, 522, 110, 86, 522, 956, 86, 522, + 109, 86, 522, 107, 86, 522, 77, 86, 522, 112, 87, 522, 110, 87, 522, 956, + 87, 522, 109, 87, 522, 107, 87, 522, 77, 87, 522, 107, 937, 522, 77, 937, + 1034, 97, 46, 109, 46, 522, 66, 113, 522, 99, 99, 522, 99, 100, 1034, 67, + 8725, 107, 103, 778, 67, 111, 46, 522, 100, 66, 522, 71, 121, 522, 104, + 97, 522, 72, 80, 522, 105, 110, 522, 75, 75, 522, 75, 77, 522, 107, 116, + 522, 108, 109, 522, 108, 110, 778, 108, 111, 103, 522, 108, 120, 522, + 109, 98, 778, 109, 105, 108, 778, 109, 111, 108, 522, 80, 72, 1034, 112, + 46, 109, 46, 778, 80, 80, 77, 522, 80, 82, 522, 115, 114, 522, 83, 118, + 522, 87, 98, 514, 49, 26085, 514, 50, 26085, 514, 51, 26085, 514, 52, + 26085, 514, 53, 26085, 514, 54, 26085, 514, 55, 26085, 514, 56, 26085, + 514, 57, 26085, 770, 49, 48, 26085, 770, 49, 49, 26085, 770, 49, 50, + 26085, 770, 49, 51, 26085, 770, 49, 52, 26085, 770, 49, 53, 26085, 770, + 49, 54, 26085, 770, 49, 55, 26085, 770, 49, 56, 26085, 770, 49, 57, + 26085, 770, 50, 48, 26085, 770, 50, 49, 26085, 770, 50, 50, 26085, 770, + 50, 51, 26085, 770, 50, 52, 26085, 770, 50, 53, 26085, 770, 50, 54, + 26085, 770, 50, 55, 26085, 770, 50, 56, 26085, 770, 50, 57, 26085, 770, + 51, 48, 26085, 770, 51, 49, 26085, 256, 35912, 256, 26356, 256, 36554, + 256, 36040, 256, 28369, 256, 20018, 256, 21477, 256, 40860, 256, 40860, + 256, 22865, 256, 37329, 256, 21895, 256, 22856, 256, 25078, 256, 30313, + 256, 32645, 256, 34367, 256, 34746, 256, 35064, 256, 37007, 256, 27138, + 256, 27931, 256, 28889, 256, 29662, 256, 33853, 256, 37226, 256, 39409, + 256, 20098, 256, 21365, 256, 27396, 256, 29211, 256, 34349, 256, 40478, + 256, 23888, 256, 28651, 256, 34253, 256, 35172, 256, 25289, 256, 33240, + 256, 34847, 256, 24266, 256, 26391, 256, 28010, 256, 29436, 256, 37070, + 256, 20358, 256, 20919, 256, 21214, 256, 25796, 256, 27347, 256, 29200, + 256, 30439, 256, 32769, 256, 34310, 256, 34396, 256, 36335, 256, 38706, + 256, 39791, 256, 40442, 256, 30860, 256, 31103, 256, 32160, 256, 33737, + 256, 37636, 256, 40575, 256, 35542, 256, 22751, 256, 24324, 256, 31840, + 256, 32894, 256, 29282, 256, 30922, 256, 36034, 256, 38647, 256, 22744, + 256, 23650, 256, 27155, 256, 28122, 256, 28431, 256, 32047, 256, 32311, + 256, 38475, 256, 21202, 256, 32907, 256, 20956, 256, 20940, 256, 31260, + 256, 32190, 256, 33777, 256, 38517, 256, 35712, 256, 25295, 256, 27138, + 256, 35582, 256, 20025, 256, 23527, 256, 24594, 256, 29575, 256, 30064, + 256, 21271, 256, 30971, 256, 20415, 256, 24489, 256, 19981, 256, 27852, + 256, 25976, 256, 32034, 256, 21443, 256, 22622, 256, 30465, 256, 33865, + 256, 35498, 256, 27578, 256, 36784, 256, 27784, 256, 25342, 256, 33509, + 256, 25504, 256, 30053, 256, 20142, 256, 20841, 256, 20937, 256, 26753, + 256, 31975, 256, 33391, 256, 35538, 256, 37327, 256, 21237, 256, 21570, + 256, 22899, 256, 24300, 256, 26053, 256, 28670, 256, 31018, 256, 38317, + 256, 39530, 256, 40599, 256, 40654, 256, 21147, 256, 26310, 256, 27511, + 256, 36706, 256, 24180, 256, 24976, 256, 25088, 256, 25754, 256, 28451, + 256, 29001, 256, 29833, 256, 31178, 256, 32244, 256, 32879, 256, 36646, + 256, 34030, 256, 36899, 256, 37706, 256, 21015, 256, 21155, 256, 21693, + 256, 28872, 256, 35010, 256, 35498, 256, 24265, 256, 24565, 256, 25467, + 256, 27566, 256, 31806, 256, 29557, 256, 20196, 256, 22265, 256, 23527, + 256, 23994, 256, 24604, 256, 29618, 256, 29801, 256, 32666, 256, 32838, + 256, 37428, 256, 38646, 256, 38728, 256, 38936, 256, 20363, 256, 31150, + 256, 37300, 256, 38584, 256, 24801, 256, 20102, 256, 20698, 256, 23534, + 256, 23615, 256, 26009, 256, 27138, 256, 29134, 256, 30274, 256, 34044, + 256, 36988, 256, 40845, 256, 26248, 256, 38446, 256, 21129, 256, 26491, + 256, 26611, 256, 27969, 256, 28316, 256, 29705, 256, 30041, 256, 30827, + 256, 32016, 256, 39006, 256, 20845, 256, 25134, 256, 38520, 256, 20523, + 256, 23833, 256, 28138, 256, 36650, 256, 24459, 256, 24900, 256, 26647, + 256, 29575, 256, 38534, 256, 21033, 256, 21519, 256, 23653, 256, 26131, + 256, 26446, 256, 26792, 256, 27877, 256, 29702, 256, 30178, 256, 32633, + 256, 35023, 256, 35041, 256, 37324, 256, 38626, 256, 21311, 256, 28346, + 256, 21533, 256, 29136, 256, 29848, 256, 34298, 256, 38563, 256, 40023, + 256, 40607, 256, 26519, 256, 28107, 256, 33256, 256, 31435, 256, 31520, + 256, 31890, 256, 29376, 256, 28825, 256, 35672, 256, 20160, 256, 33590, + 256, 21050, 256, 20999, 256, 24230, 256, 25299, 256, 31958, 256, 23429, + 256, 27934, 256, 26292, 256, 36667, 256, 34892, 256, 38477, 256, 35211, + 256, 24275, 256, 20800, 256, 21952, 256, 22618, 256, 26228, 256, 20958, + 256, 29482, 256, 30410, 256, 31036, 256, 31070, 256, 31077, 256, 31119, + 256, 38742, 256, 31934, 256, 32701, 256, 34322, 256, 35576, 256, 36920, + 256, 37117, 256, 39151, 256, 39164, 256, 39208, 256, 40372, 256, 20398, + 256, 20711, 256, 20813, 256, 21193, 256, 21220, 256, 21329, 256, 21917, + 256, 22022, 256, 22120, 256, 22592, 256, 22696, 256, 23652, 256, 23662, + 256, 24724, 256, 24936, 256, 24974, 256, 25074, 256, 25935, 256, 26082, + 256, 26257, 256, 26757, 256, 28023, 256, 28186, 256, 28450, 256, 29038, + 256, 29227, 256, 29730, 256, 30865, 256, 31038, 256, 31049, 256, 31048, + 256, 31056, 256, 31062, 256, 31069, 256, 31117, 256, 31118, 256, 31296, + 256, 31361, 256, 31680, 256, 32244, 256, 32265, 256, 32321, 256, 32626, + 256, 32773, 256, 33261, 256, 33401, 256, 33401, 256, 33879, 256, 35088, + 256, 35222, 256, 35585, 256, 35641, 256, 36051, 256, 36104, 256, 36790, + 256, 36920, 256, 38627, 256, 38911, 256, 38971, 514, 102, 102, 514, 102, + 105, 514, 102, 108, 770, 102, 102, 105, 770, 102, 102, 108, 514, 383, + 116, 514, 115, 116, 514, 1396, 1398, 514, 1396, 1381, 514, 1396, 1387, + 514, 1406, 1398, 514, 1396, 1389, 512, 1497, 1460, 512, 1522, 1463, 262, + 1506, 262, 1488, 262, 1491, 262, 1492, 262, 1499, 262, 1500, 262, 1501, + 262, 1512, 262, 1514, 262, 43, 512, 1513, 1473, 512, 1513, 1474, 512, + 64329, 1473, 512, 64329, 1474, 512, 1488, 1463, 512, 1488, 1464, 512, + 1488, 1468, 512, 1489, 1468, 512, 1490, 1468, 512, 1491, 1468, 512, 1492, + 1468, 512, 1493, 1468, 512, 1494, 1468, 512, 1496, 1468, 512, 1497, 1468, + 512, 1498, 1468, 512, 1499, 1468, 512, 1500, 1468, 512, 1502, 1468, 512, + 1504, 1468, 512, 1505, 1468, 512, 1507, 1468, 512, 1508, 1468, 512, 1510, + 1468, 512, 1511, 1468, 512, 1512, 1468, 512, 1513, 1468, 512, 1514, 1468, + 512, 1493, 1465, 512, 1489, 1471, 512, 1499, 1471, 512, 1508, 1471, 514, + 1488, 1500, 267, 1649, 268, 1649, 267, 1659, 268, 1659, 269, 1659, 270, + 1659, 267, 1662, 268, 1662, 269, 1662, 270, 1662, 267, 1664, 268, 1664, + 269, 1664, 270, 1664, 267, 1658, 268, 1658, 269, 1658, 270, 1658, 267, + 1663, 268, 1663, 269, 1663, 270, 1663, 267, 1657, 268, 1657, 269, 1657, + 270, 1657, 267, 1700, 268, 1700, 269, 1700, 270, 1700, 267, 1702, 268, + 1702, 269, 1702, 270, 1702, 267, 1668, 268, 1668, 269, 1668, 270, 1668, + 267, 1667, 268, 1667, 269, 1667, 270, 1667, 267, 1670, 268, 1670, 269, + 1670, 270, 1670, 267, 1671, 268, 1671, 269, 1671, 270, 1671, 267, 1677, + 268, 1677, 267, 1676, 268, 1676, 267, 1678, 268, 1678, 267, 1672, 268, + 1672, 267, 1688, 268, 1688, 267, 1681, 268, 1681, 267, 1705, 268, 1705, + 269, 1705, 270, 1705, 267, 1711, 268, 1711, 269, 1711, 270, 1711, 267, + 1715, 268, 1715, 269, 1715, 270, 1715, 267, 1713, 268, 1713, 269, 1713, + 270, 1713, 267, 1722, 268, 1722, 267, 1723, 268, 1723, 269, 1723, 270, + 1723, 267, 1728, 268, 1728, 267, 1729, 268, 1729, 269, 1729, 270, 1729, + 267, 1726, 268, 1726, 269, 1726, 270, 1726, 267, 1746, 268, 1746, 267, + 1747, 268, 1747, 267, 1709, 268, 1709, 269, 1709, 270, 1709, 267, 1735, + 268, 1735, 267, 1734, 268, 1734, 267, 1736, 268, 1736, 267, 1655, 267, + 1739, 268, 1739, 267, 1733, 268, 1733, 267, 1737, 268, 1737, 267, 1744, + 268, 1744, 269, 1744, 270, 1744, 269, 1609, 270, 1609, 523, 1574, 1575, + 524, 1574, 1575, 523, 1574, 1749, 524, 1574, 1749, 523, 1574, 1608, 524, + 1574, 1608, 523, 1574, 1735, 524, 1574, 1735, 523, 1574, 1734, 524, 1574, + 1734, 523, 1574, 1736, 524, 1574, 1736, 523, 1574, 1744, 524, 1574, 1744, + 525, 1574, 1744, 523, 1574, 1609, 524, 1574, 1609, 525, 1574, 1609, 267, + 1740, 268, 1740, 269, 1740, 270, 1740, 523, 1574, 1580, 523, 1574, 1581, + 523, 1574, 1605, 523, 1574, 1609, 523, 1574, 1610, 523, 1576, 1580, 523, + 1576, 1581, 523, 1576, 1582, 523, 1576, 1605, 523, 1576, 1609, 523, 1576, + 1610, 523, 1578, 1580, 523, 1578, 1581, 523, 1578, 1582, 523, 1578, 1605, + 523, 1578, 1609, 523, 1578, 1610, 523, 1579, 1580, 523, 1579, 1605, 523, + 1579, 1609, 523, 1579, 1610, 523, 1580, 1581, 523, 1580, 1605, 523, 1581, + 1580, 523, 1581, 1605, 523, 1582, 1580, 523, 1582, 1581, 523, 1582, 1605, + 523, 1587, 1580, 523, 1587, 1581, 523, 1587, 1582, 523, 1587, 1605, 523, + 1589, 1581, 523, 1589, 1605, 523, 1590, 1580, 523, 1590, 1581, 523, 1590, + 1582, 523, 1590, 1605, 523, 1591, 1581, 523, 1591, 1605, 523, 1592, 1605, + 523, 1593, 1580, 523, 1593, 1605, 523, 1594, 1580, 523, 1594, 1605, 523, + 1601, 1580, 523, 1601, 1581, 523, 1601, 1582, 523, 1601, 1605, 523, 1601, + 1609, 523, 1601, 1610, 523, 1602, 1581, 523, 1602, 1605, 523, 1602, 1609, + 523, 1602, 1610, 523, 1603, 1575, 523, 1603, 1580, 523, 1603, 1581, 523, + 1603, 1582, 523, 1603, 1604, 523, 1603, 1605, 523, 1603, 1609, 523, 1603, + 1610, 523, 1604, 1580, 523, 1604, 1581, 523, 1604, 1582, 523, 1604, 1605, + 523, 1604, 1609, 523, 1604, 1610, 523, 1605, 1580, 523, 1605, 1581, 523, + 1605, 1582, 523, 1605, 1605, 523, 1605, 1609, 523, 1605, 1610, 523, 1606, + 1580, 523, 1606, 1581, 523, 1606, 1582, 523, 1606, 1605, 523, 1606, 1609, + 523, 1606, 1610, 523, 1607, 1580, 523, 1607, 1605, 523, 1607, 1609, 523, + 1607, 1610, 523, 1610, 1580, 523, 1610, 1581, 523, 1610, 1582, 523, 1610, + 1605, 523, 1610, 1609, 523, 1610, 1610, 523, 1584, 1648, 523, 1585, 1648, + 523, 1609, 1648, 779, 32, 1612, 1617, 779, 32, 1613, 1617, 779, 32, 1614, + 1617, 779, 32, 1615, 1617, 779, 32, 1616, 1617, 779, 32, 1617, 1648, 524, + 1574, 1585, 524, 1574, 1586, 524, 1574, 1605, 524, 1574, 1606, 524, 1574, + 1609, 524, 1574, 1610, 524, 1576, 1585, 524, 1576, 1586, 524, 1576, 1605, + 524, 1576, 1606, 524, 1576, 1609, 524, 1576, 1610, 524, 1578, 1585, 524, + 1578, 1586, 524, 1578, 1605, 524, 1578, 1606, 524, 1578, 1609, 524, 1578, + 1610, 524, 1579, 1585, 524, 1579, 1586, 524, 1579, 1605, 524, 1579, 1606, + 524, 1579, 1609, 524, 1579, 1610, 524, 1601, 1609, 524, 1601, 1610, 524, + 1602, 1609, 524, 1602, 1610, 524, 1603, 1575, 524, 1603, 1604, 524, 1603, + 1605, 524, 1603, 1609, 524, 1603, 1610, 524, 1604, 1605, 524, 1604, 1609, + 524, 1604, 1610, 524, 1605, 1575, 524, 1605, 1605, 524, 1606, 1585, 524, + 1606, 1586, 524, 1606, 1605, 524, 1606, 1606, 524, 1606, 1609, 524, 1606, + 1610, 524, 1609, 1648, 524, 1610, 1585, 524, 1610, 1586, 524, 1610, 1605, + 524, 1610, 1606, 524, 1610, 1609, 524, 1610, 1610, 525, 1574, 1580, 525, + 1574, 1581, 525, 1574, 1582, 525, 1574, 1605, 525, 1574, 1607, 525, 1576, + 1580, 525, 1576, 1581, 525, 1576, 1582, 525, 1576, 1605, 525, 1576, 1607, + 525, 1578, 1580, 525, 1578, 1581, 525, 1578, 1582, 525, 1578, 1605, 525, + 1578, 1607, 525, 1579, 1605, 525, 1580, 1581, 525, 1580, 1605, 525, 1581, + 1580, 525, 1581, 1605, 525, 1582, 1580, 525, 1582, 1605, 525, 1587, 1580, + 525, 1587, 1581, 525, 1587, 1582, 525, 1587, 1605, 525, 1589, 1581, 525, + 1589, 1582, 525, 1589, 1605, 525, 1590, 1580, 525, 1590, 1581, 525, 1590, + 1582, 525, 1590, 1605, 525, 1591, 1581, 525, 1592, 1605, 525, 1593, 1580, + 525, 1593, 1605, 525, 1594, 1580, 525, 1594, 1605, 525, 1601, 1580, 525, + 1601, 1581, 525, 1601, 1582, 525, 1601, 1605, 525, 1602, 1581, 525, 1602, + 1605, 525, 1603, 1580, 525, 1603, 1581, 525, 1603, 1582, 525, 1603, 1604, + 525, 1603, 1605, 525, 1604, 1580, 525, 1604, 1581, 525, 1604, 1582, 525, + 1604, 1605, 525, 1604, 1607, 525, 1605, 1580, 525, 1605, 1581, 525, 1605, + 1582, 525, 1605, 1605, 525, 1606, 1580, 525, 1606, 1581, 525, 1606, 1582, + 525, 1606, 1605, 525, 1606, 1607, 525, 1607, 1580, 525, 1607, 1605, 525, + 1607, 1648, 525, 1610, 1580, 525, 1610, 1581, 525, 1610, 1582, 525, 1610, + 1605, 525, 1610, 1607, 526, 1574, 1605, 526, 1574, 1607, 526, 1576, 1605, + 526, 1576, 1607, 526, 1578, 1605, 526, 1578, 1607, 526, 1579, 1605, 526, + 1579, 1607, 526, 1587, 1605, 526, 1587, 1607, 526, 1588, 1605, 526, 1588, + 1607, 526, 1603, 1604, 526, 1603, 1605, 526, 1604, 1605, 526, 1606, 1605, + 526, 1606, 1607, 526, 1610, 1605, 526, 1610, 1607, 782, 1600, 1614, 1617, + 782, 1600, 1615, 1617, 782, 1600, 1616, 1617, 523, 1591, 1609, 523, 1591, + 1610, 523, 1593, 1609, 523, 1593, 1610, 523, 1594, 1609, 523, 1594, 1610, + 523, 1587, 1609, 523, 1587, 1610, 523, 1588, 1609, 523, 1588, 1610, 523, + 1581, 1609, 523, 1581, 1610, 523, 1580, 1609, 523, 1580, 1610, 523, 1582, + 1609, 523, 1582, 1610, 523, 1589, 1609, 523, 1589, 1610, 523, 1590, 1609, + 523, 1590, 1610, 523, 1588, 1580, 523, 1588, 1581, 523, 1588, 1582, 523, + 1588, 1605, 523, 1588, 1585, 523, 1587, 1585, 523, 1589, 1585, 523, 1590, + 1585, 524, 1591, 1609, 524, 1591, 1610, 524, 1593, 1609, 524, 1593, 1610, + 524, 1594, 1609, 524, 1594, 1610, 524, 1587, 1609, 524, 1587, 1610, 524, + 1588, 1609, 524, 1588, 1610, 524, 1581, 1609, 524, 1581, 1610, 524, 1580, + 1609, 524, 1580, 1610, 524, 1582, 1609, 524, 1582, 1610, 524, 1589, 1609, + 524, 1589, 1610, 524, 1590, 1609, 524, 1590, 1610, 524, 1588, 1580, 524, + 1588, 1581, 524, 1588, 1582, 524, 1588, 1605, 524, 1588, 1585, 524, 1587, + 1585, 524, 1589, 1585, 524, 1590, 1585, 525, 1588, 1580, 525, 1588, 1581, + 525, 1588, 1582, 525, 1588, 1605, 525, 1587, 1607, 525, 1588, 1607, 525, + 1591, 1605, 526, 1587, 1580, 526, 1587, 1581, 526, 1587, 1582, 526, 1588, + 1580, 526, 1588, 1581, 526, 1588, 1582, 526, 1591, 1605, 526, 1592, 1605, + 524, 1575, 1611, 523, 1575, 1611, 781, 1578, 1580, 1605, 780, 1578, 1581, + 1580, 781, 1578, 1581, 1580, 781, 1578, 1581, 1605, 781, 1578, 1582, + 1605, 781, 1578, 1605, 1580, 781, 1578, 1605, 1581, 781, 1578, 1605, + 1582, 780, 1580, 1605, 1581, 781, 1580, 1605, 1581, 780, 1581, 1605, + 1610, 780, 1581, 1605, 1609, 781, 1587, 1581, 1580, 781, 1587, 1580, + 1581, 780, 1587, 1580, 1609, 780, 1587, 1605, 1581, 781, 1587, 1605, + 1581, 781, 1587, 1605, 1580, 780, 1587, 1605, 1605, 781, 1587, 1605, + 1605, 780, 1589, 1581, 1581, 781, 1589, 1581, 1581, 780, 1589, 1605, + 1605, 780, 1588, 1581, 1605, 781, 1588, 1581, 1605, 780, 1588, 1580, + 1610, 780, 1588, 1605, 1582, 781, 1588, 1605, 1582, 780, 1588, 1605, + 1605, 781, 1588, 1605, 1605, 780, 1590, 1581, 1609, 780, 1590, 1582, + 1605, 781, 1590, 1582, 1605, 780, 1591, 1605, 1581, 781, 1591, 1605, + 1581, 781, 1591, 1605, 1605, 780, 1591, 1605, 1610, 780, 1593, 1580, + 1605, 780, 1593, 1605, 1605, 781, 1593, 1605, 1605, 780, 1593, 1605, + 1609, 780, 1594, 1605, 1605, 780, 1594, 1605, 1610, 780, 1594, 1605, + 1609, 780, 1601, 1582, 1605, 781, 1601, 1582, 1605, 780, 1602, 1605, + 1581, 780, 1602, 1605, 1605, 780, 1604, 1581, 1605, 780, 1604, 1581, + 1610, 780, 1604, 1581, 1609, 781, 1604, 1580, 1580, 780, 1604, 1580, + 1580, 780, 1604, 1582, 1605, 781, 1604, 1582, 1605, 780, 1604, 1605, + 1581, 781, 1604, 1605, 1581, 781, 1605, 1581, 1580, 781, 1605, 1581, + 1605, 780, 1605, 1581, 1610, 781, 1605, 1580, 1581, 781, 1605, 1580, + 1605, 781, 1605, 1582, 1580, 781, 1605, 1582, 1605, 781, 1605, 1580, + 1582, 781, 1607, 1605, 1580, 781, 1607, 1605, 1605, 781, 1606, 1581, + 1605, 780, 1606, 1581, 1609, 780, 1606, 1580, 1605, 781, 1606, 1580, + 1605, 780, 1606, 1580, 1609, 780, 1606, 1605, 1610, 780, 1606, 1605, + 1609, 780, 1610, 1605, 1605, 781, 1610, 1605, 1605, 780, 1576, 1582, + 1610, 780, 1578, 1580, 1610, 780, 1578, 1580, 1609, 780, 1578, 1582, + 1610, 780, 1578, 1582, 1609, 780, 1578, 1605, 1610, 780, 1578, 1605, + 1609, 780, 1580, 1605, 1610, 780, 1580, 1581, 1609, 780, 1580, 1605, + 1609, 780, 1587, 1582, 1609, 780, 1589, 1581, 1610, 780, 1588, 1581, + 1610, 780, 1590, 1581, 1610, 780, 1604, 1580, 1610, 780, 1604, 1605, + 1610, 780, 1610, 1581, 1610, 780, 1610, 1580, 1610, 780, 1610, 1605, + 1610, 780, 1605, 1605, 1610, 780, 1602, 1605, 1610, 780, 1606, 1581, + 1610, 781, 1602, 1605, 1581, 781, 1604, 1581, 1605, 780, 1593, 1605, + 1610, 780, 1603, 1605, 1610, 781, 1606, 1580, 1581, 780, 1605, 1582, + 1610, 781, 1604, 1580, 1605, 780, 1603, 1605, 1605, 780, 1604, 1580, + 1605, 780, 1606, 1580, 1581, 780, 1580, 1581, 1610, 780, 1581, 1580, + 1610, 780, 1605, 1580, 1610, 780, 1601, 1605, 1610, 780, 1576, 1581, + 1610, 781, 1603, 1605, 1605, 781, 1593, 1580, 1605, 781, 1589, 1605, + 1605, 780, 1587, 1582, 1610, 780, 1606, 1580, 1610, 779, 1589, 1604, + 1746, 779, 1602, 1604, 1746, 1035, 1575, 1604, 1604, 1607, 1035, 1575, + 1603, 1576, 1585, 1035, 1605, 1581, 1605, 1583, 1035, 1589, 1604, 1593, + 1605, 1035, 1585, 1587, 1608, 1604, 1035, 1593, 1604, 1610, 1607, 1035, + 1608, 1587, 1604, 1605, 779, 1589, 1604, 1609, 4619, 1589, 1604, 1609, + 32, 1575, 1604, 1604, 1607, 32, 1593, 1604, 1610, 1607, 32, 1608, 1587, + 1604, 1605, 2059, 1580, 1604, 32, 1580, 1604, 1575, 1604, 1607, 1035, + 1585, 1740, 1575, 1604, 265, 8229, 265, 8212, 265, 8211, 265, 95, 265, + 95, 265, 40, 265, 41, 265, 123, 265, 125, 265, 12308, 265, 12309, 265, + 12304, 265, 12305, 265, 12298, 265, 12299, 265, 12296, 265, 12297, 265, + 12300, 265, 12301, 265, 12302, 265, 12303, 258, 8254, 258, 8254, 258, + 8254, 258, 8254, 258, 95, 258, 95, 258, 95, 271, 44, 271, 12289, 271, 46, + 271, 59, 271, 58, 271, 63, 271, 33, 271, 8212, 271, 40, 271, 41, 271, + 123, 271, 125, 271, 12308, 271, 12309, 271, 35, 271, 38, 271, 42, 271, + 43, 271, 45, 271, 60, 271, 62, 271, 61, 271, 92, 271, 36, 271, 37, 271, + 64, 523, 32, 1611, 526, 1600, 1611, 523, 32, 1612, 523, 32, 1613, 523, + 32, 1614, 526, 1600, 1614, 523, 32, 1615, 526, 1600, 1615, 523, 32, 1616, + 526, 1600, 1616, 523, 32, 1617, 526, 1600, 1617, 523, 32, 1618, 526, + 1600, 1618, 267, 1569, 267, 1570, 268, 1570, 267, 1571, 268, 1571, 267, + 1572, 268, 1572, 267, 1573, 268, 1573, 267, 1574, 268, 1574, 269, 1574, + 270, 1574, 267, 1575, 268, 1575, 267, 1576, 268, 1576, 269, 1576, 270, + 1576, 267, 1577, 268, 1577, 267, 1578, 268, 1578, 269, 1578, 270, 1578, + 267, 1579, 268, 1579, 269, 1579, 270, 1579, 267, 1580, 268, 1580, 269, + 1580, 270, 1580, 267, 1581, 268, 1581, 269, 1581, 270, 1581, 267, 1582, + 268, 1582, 269, 1582, 270, 1582, 267, 1583, 268, 1583, 267, 1584, 268, + 1584, 267, 1585, 268, 1585, 267, 1586, 268, 1586, 267, 1587, 268, 1587, + 269, 1587, 270, 1587, 267, 1588, 268, 1588, 269, 1588, 270, 1588, 267, + 1589, 268, 1589, 269, 1589, 270, 1589, 267, 1590, 268, 1590, 269, 1590, + 270, 1590, 267, 1591, 268, 1591, 269, 1591, 270, 1591, 267, 1592, 268, + 1592, 269, 1592, 270, 1592, 267, 1593, 268, 1593, 269, 1593, 270, 1593, + 267, 1594, 268, 1594, 269, 1594, 270, 1594, 267, 1601, 268, 1601, 269, + 1601, 270, 1601, 267, 1602, 268, 1602, 269, 1602, 270, 1602, 267, 1603, + 268, 1603, 269, 1603, 270, 1603, 267, 1604, 268, 1604, 269, 1604, 270, + 1604, 267, 1605, 268, 1605, 269, 1605, 270, 1605, 267, 1606, 268, 1606, + 269, 1606, 270, 1606, 267, 1607, 268, 1607, 269, 1607, 270, 1607, 267, + 1608, 268, 1608, 267, 1609, 268, 1609, 267, 1610, 268, 1610, 269, 1610, + 270, 1610, 523, 1604, 1570, 524, 1604, 1570, 523, 1604, 1571, 524, 1604, + 1571, 523, 1604, 1573, 524, 1604, 1573, 523, 1604, 1575, 524, 1604, 1575, + 264, 33, 264, 34, 264, 35, 264, 36, 264, 37, 264, 38, 264, 39, 264, 40, + 264, 41, 264, 42, 264, 43, 264, 44, 264, 45, 264, 46, 264, 47, 264, 48, + 264, 49, 264, 50, 264, 51, 264, 52, 264, 53, 264, 54, 264, 55, 264, 56, + 264, 57, 264, 58, 264, 59, 264, 60, 264, 61, 264, 62, 264, 63, 264, 64, + 264, 65, 264, 66, 264, 67, 264, 68, 264, 69, 264, 70, 264, 71, 264, 72, + 264, 73, 264, 74, 264, 75, 264, 76, 264, 77, 264, 78, 264, 79, 264, 80, + 264, 81, 264, 82, 264, 83, 264, 84, 264, 85, 264, 86, 264, 87, 264, 88, + 264, 89, 264, 90, 264, 91, 264, 92, 264, 93, 264, 94, 264, 95, 264, 96, + 264, 97, 264, 98, 264, 99, 264, 100, 264, 101, 264, 102, 264, 103, 264, + 104, 264, 105, 264, 106, 264, 107, 264, 108, 264, 109, 264, 110, 264, + 111, 264, 112, 264, 113, 264, 114, 264, 115, 264, 116, 264, 117, 264, + 118, 264, 119, 264, 120, 264, 121, 264, 122, 264, 123, 264, 124, 264, + 125, 264, 126, 264, 10629, 264, 10630, 272, 12290, 272, 12300, 272, + 12301, 272, 12289, 272, 12539, 272, 12530, 272, 12449, 272, 12451, 272, + 12453, 272, 12455, 272, 12457, 272, 12515, 272, 12517, 272, 12519, 272, + 12483, 272, 12540, 272, 12450, 272, 12452, 272, 12454, 272, 12456, 272, + 12458, 272, 12459, 272, 12461, 272, 12463, 272, 12465, 272, 12467, 272, + 12469, 272, 12471, 272, 12473, 272, 12475, 272, 12477, 272, 12479, 272, + 12481, 272, 12484, 272, 12486, 272, 12488, 272, 12490, 272, 12491, 272, + 12492, 272, 12493, 272, 12494, 272, 12495, 272, 12498, 272, 12501, 272, + 12504, 272, 12507, 272, 12510, 272, 12511, 272, 12512, 272, 12513, 272, + 12514, 272, 12516, 272, 12518, 272, 12520, 272, 12521, 272, 12522, 272, + 12523, 272, 12524, 272, 12525, 272, 12527, 272, 12531, 272, 12441, 272, + 12442, 272, 12644, 272, 12593, 272, 12594, 272, 12595, 272, 12596, 272, + 12597, 272, 12598, 272, 12599, 272, 12600, 272, 12601, 272, 12602, 272, + 12603, 272, 12604, 272, 12605, 272, 12606, 272, 12607, 272, 12608, 272, + 12609, 272, 12610, 272, 12611, 272, 12612, 272, 12613, 272, 12614, 272, + 12615, 272, 12616, 272, 12617, 272, 12618, 272, 12619, 272, 12620, 272, + 12621, 272, 12622, 272, 12623, 272, 12624, 272, 12625, 272, 12626, 272, + 12627, 272, 12628, 272, 12629, 272, 12630, 272, 12631, 272, 12632, 272, + 12633, 272, 12634, 272, 12635, 272, 12636, 272, 12637, 272, 12638, 272, + 12639, 272, 12640, 272, 12641, 272, 12642, 272, 12643, 264, 162, 264, + 163, 264, 172, 264, 175, 264, 166, 264, 165, 264, 8361, 272, 9474, 272, + 8592, 272, 8593, 272, 8594, 272, 8595, 272, 9632, 272, 9675, 512, 119127, + 119141, 512, 119128, 119141, 512, 119135, 119150, 512, 119135, 119151, + 512, 119135, 119152, 512, 119135, 119153, 512, 119135, 119154, 512, + 119225, 119141, 512, 119226, 119141, 512, 119227, 119150, 512, 119228, + 119150, 512, 119227, 119151, 512, 119228, 119151, 262, 65, 262, 66, 262, + 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, + 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, + 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, + 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, + 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, + 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, + 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, 262, + 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, + 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, + 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, + 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 105, 262, 106, + 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, + 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, + 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, + 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, + 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, + 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, + 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, + 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, + 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, + 262, 122, 262, 65, 262, 67, 262, 68, 262, 71, 262, 74, 262, 75, 262, 78, + 262, 79, 262, 80, 262, 81, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, + 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 102, + 262, 104, 262, 105, 262, 106, 262, 107, 262, 109, 262, 110, 262, 112, + 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, + 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, + 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, + 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, + 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, + 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, + 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, + 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, + 262, 121, 262, 122, 262, 65, 262, 66, 262, 68, 262, 69, 262, 70, 262, 71, + 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, + 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 97, + 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, + 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, + 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, + 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 68, 262, 69, + 262, 70, 262, 71, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 79, + 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 97, + 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, + 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, + 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, + 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, + 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, + 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, + 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, + 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, + 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, + 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, + 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, + 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, + 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, + 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, + 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, + 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, + 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, + 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, + 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, + 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, + 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, + 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, + 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, + 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, + 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, + 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, + 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, + 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, + 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, + 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, + 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, + 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, + 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, + 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, + 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, + 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, + 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, + 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, + 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, + 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, + 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, + 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, + 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, + 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, + 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 913, 262, + 914, 262, 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, + 921, 262, 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, + 928, 262, 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, + 935, 262, 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, + 948, 262, 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, + 955, 262, 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, + 962, 262, 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, + 969, 262, 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, + 982, 262, 913, 262, 914, 262, 915, 262, 916, 262, 917, 262, 918, 262, + 919, 262, 920, 262, 921, 262, 922, 262, 923, 262, 924, 262, 925, 262, + 926, 262, 927, 262, 928, 262, 929, 262, 1012, 262, 931, 262, 932, 262, + 933, 262, 934, 262, 935, 262, 936, 262, 937, 262, 8711, 262, 945, 262, + 946, 262, 947, 262, 948, 262, 949, 262, 950, 262, 951, 262, 952, 262, + 953, 262, 954, 262, 955, 262, 956, 262, 957, 262, 958, 262, 959, 262, + 960, 262, 961, 262, 962, 262, 963, 262, 964, 262, 965, 262, 966, 262, + 967, 262, 968, 262, 969, 262, 8706, 262, 1013, 262, 977, 262, 1008, 262, + 981, 262, 1009, 262, 982, 262, 913, 262, 914, 262, 915, 262, 916, 262, + 917, 262, 918, 262, 919, 262, 920, 262, 921, 262, 922, 262, 923, 262, + 924, 262, 925, 262, 926, 262, 927, 262, 928, 262, 929, 262, 1012, 262, + 931, 262, 932, 262, 933, 262, 934, 262, 935, 262, 936, 262, 937, 262, + 8711, 262, 945, 262, 946, 262, 947, 262, 948, 262, 949, 262, 950, 262, + 951, 262, 952, 262, 953, 262, 954, 262, 955, 262, 956, 262, 957, 262, + 958, 262, 959, 262, 960, 262, 961, 262, 962, 262, 963, 262, 964, 262, + 965, 262, 966, 262, 967, 262, 968, 262, 969, 262, 8706, 262, 1013, 262, + 977, 262, 1008, 262, 981, 262, 1009, 262, 982, 262, 913, 262, 914, 262, + 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, 921, 262, + 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, 928, 262, + 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, 935, 262, + 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, 948, 262, + 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, 955, 262, + 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, 962, 262, + 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, 969, 262, + 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, 982, 262, + 913, 262, 914, 262, 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, + 920, 262, 921, 262, 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, + 927, 262, 928, 262, 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, + 934, 262, 935, 262, 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, + 947, 262, 948, 262, 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, + 954, 262, 955, 262, 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, + 961, 262, 962, 262, 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, + 968, 262, 969, 262, 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, + 1009, 262, 982, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, + 262, 54, 262, 55, 262, 56, 262, 57, 262, 48, 262, 49, 262, 50, 262, 51, + 262, 52, 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, 262, 48, 262, 49, + 262, 50, 262, 51, 262, 52, 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, + 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, 262, 54, 262, 55, + 262, 56, 262, 57, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, + 262, 54, 262, 55, 262, 56, 262, 57, 256, 20029, 256, 20024, 256, 20033, + 256, 131362, 256, 20320, 256, 20398, 256, 20411, 256, 20482, 256, 20602, + 256, 20633, 256, 20711, 256, 20687, 256, 13470, 256, 132666, 256, 20813, + 256, 20820, 256, 20836, 256, 20855, 256, 132380, 256, 13497, 256, 20839, + 256, 20877, 256, 132427, 256, 20887, 256, 20900, 256, 20172, 256, 20908, + 256, 20917, 256, 168415, 256, 20981, 256, 20995, 256, 13535, 256, 21051, + 256, 21062, 256, 21106, 256, 21111, 256, 13589, 256, 21191, 256, 21193, + 256, 21220, 256, 21242, 256, 21253, 256, 21254, 256, 21271, 256, 21321, + 256, 21329, 256, 21338, 256, 21363, 256, 21373, 256, 21375, 256, 21375, + 256, 21375, 256, 133676, 256, 28784, 256, 21450, 256, 21471, 256, 133987, + 256, 21483, 256, 21489, 256, 21510, 256, 21662, 256, 21560, 256, 21576, + 256, 21608, 256, 21666, 256, 21750, 256, 21776, 256, 21843, 256, 21859, + 256, 21892, 256, 21892, 256, 21913, 256, 21931, 256, 21939, 256, 21954, + 256, 22294, 256, 22022, 256, 22295, 256, 22097, 256, 22132, 256, 20999, + 256, 22766, 256, 22478, 256, 22516, 256, 22541, 256, 22411, 256, 22578, + 256, 22577, 256, 22700, 256, 136420, 256, 22770, 256, 22775, 256, 22790, + 256, 22810, 256, 22818, 256, 22882, 256, 136872, 256, 136938, 256, 23020, + 256, 23067, 256, 23079, 256, 23000, 256, 23142, 256, 14062, 256, 136042, + 256, 23304, 256, 23358, 256, 23358, 256, 137672, 256, 23491, 256, 23512, + 256, 23527, 256, 23539, 256, 138008, 256, 23551, 256, 23558, 256, 24371, + 256, 23586, 256, 14209, 256, 23648, 256, 23662, 256, 23744, 256, 23693, + 256, 138724, 256, 23875, 256, 138726, 256, 23918, 256, 23915, 256, 23932, + 256, 24033, 256, 24034, 256, 14383, 256, 24061, 256, 24104, 256, 24125, + 256, 24169, 256, 14434, 256, 139651, 256, 14460, 256, 24240, 256, 24243, + 256, 24246, 256, 24266, 256, 172946, 256, 24318, 256, 140081, 256, + 140081, 256, 33281, 256, 24354, 256, 24354, 256, 14535, 256, 144056, 256, + 156122, 256, 24418, 256, 24427, 256, 14563, 256, 24474, 256, 24525, 256, + 24535, 256, 24569, 256, 24705, 256, 14650, 256, 14620, 256, 24724, 256, + 141012, 256, 24775, 256, 24904, 256, 24908, 256, 24910, 256, 24908, 256, + 24954, 256, 24974, 256, 25010, 256, 24996, 256, 25007, 256, 25054, 256, + 25074, 256, 25078, 256, 25104, 256, 25115, 256, 25181, 256, 25265, 256, + 25300, 256, 25424, 256, 142092, 256, 25405, 256, 25340, 256, 25448, 256, + 25475, 256, 25572, 256, 142321, 256, 25634, 256, 25541, 256, 25513, 256, + 14894, 256, 25705, 256, 25726, 256, 25757, 256, 25719, 256, 14956, 256, + 25935, 256, 25964, 256, 143370, 256, 26083, 256, 26360, 256, 26185, 256, + 15129, 256, 26257, 256, 15112, 256, 15076, 256, 20882, 256, 20885, 256, + 26368, 256, 26268, 256, 32941, 256, 17369, 256, 26391, 256, 26395, 256, + 26401, 256, 26462, 256, 26451, 256, 144323, 256, 15177, 256, 26618, 256, + 26501, 256, 26706, 256, 26757, 256, 144493, 256, 26766, 256, 26655, 256, + 26900, 256, 15261, 256, 26946, 256, 27043, 256, 27114, 256, 27304, 256, + 145059, 256, 27355, 256, 15384, 256, 27425, 256, 145575, 256, 27476, 256, + 15438, 256, 27506, 256, 27551, 256, 27578, 256, 27579, 256, 146061, 256, + 138507, 256, 146170, 256, 27726, 256, 146620, 256, 27839, 256, 27853, + 256, 27751, 256, 27926, 256, 27966, 256, 28023, 256, 27969, 256, 28009, + 256, 28024, 256, 28037, 256, 146718, 256, 27956, 256, 28207, 256, 28270, + 256, 15667, 256, 28363, 256, 28359, 256, 147153, 256, 28153, 256, 28526, + 256, 147294, 256, 147342, 256, 28614, 256, 28729, 256, 28702, 256, 28699, + 256, 15766, 256, 28746, 256, 28797, 256, 28791, 256, 28845, 256, 132389, + 256, 28997, 256, 148067, 256, 29084, 256, 17323, 256, 29224, 256, 29237, + 256, 29264, 256, 149000, 256, 29312, 256, 29333, 256, 149301, 256, + 149524, 256, 29562, 256, 29579, 256, 16044, 256, 29605, 256, 16056, 256, + 16056, 256, 29767, 256, 29788, 256, 29809, 256, 29829, 256, 29898, 256, + 16155, 256, 29988, 256, 150582, 256, 30014, 256, 150674, 256, 30064, 256, + 139679, 256, 30224, 256, 151457, 256, 151480, 256, 151620, 256, 16380, + 256, 16392, 256, 30452, 256, 151795, 256, 151794, 256, 151833, 256, + 151859, 256, 30494, 256, 30495, 256, 30495, 256, 30538, 256, 16441, 256, + 30603, 256, 16454, 256, 16534, 256, 152605, 256, 30798, 256, 30860, 256, + 30924, 256, 16611, 256, 153126, 256, 31062, 256, 153242, 256, 153285, + 256, 31119, 256, 31211, 256, 16687, 256, 31296, 256, 31306, 256, 31311, + 256, 153980, 256, 154279, 256, 154279, 256, 31406, 256, 16898, 256, + 154539, 256, 31686, 256, 31689, 256, 16935, 256, 154752, 256, 31954, 256, + 17056, 256, 31976, 256, 31971, 256, 32000, 256, 155526, 256, 32099, 256, + 17153, 256, 32199, 256, 32258, 256, 32325, 256, 17204, 256, 156200, 256, + 156231, 256, 17241, 256, 156377, 256, 32634, 256, 156478, 256, 32661, + 256, 32762, 256, 32773, 256, 156890, 256, 156963, 256, 32864, 256, + 157096, 256, 32880, 256, 144223, 256, 17365, 256, 32946, 256, 33027, 256, + 17419, 256, 33086, 256, 23221, 256, 157607, 256, 157621, 256, 144275, + 256, 144284, 256, 33281, 256, 33284, 256, 36766, 256, 17515, 256, 33425, + 256, 33419, 256, 33437, 256, 21171, 256, 33457, 256, 33459, 256, 33469, + 256, 33510, 256, 158524, 256, 33509, 256, 33565, 256, 33635, 256, 33709, + 256, 33571, 256, 33725, 256, 33767, 256, 33879, 256, 33619, 256, 33738, + 256, 33740, 256, 33756, 256, 158774, 256, 159083, 256, 158933, 256, + 17707, 256, 34033, 256, 34035, 256, 34070, 256, 160714, 256, 34148, 256, + 159532, 256, 17757, 256, 17761, 256, 159665, 256, 159954, 256, 17771, + 256, 34384, 256, 34396, 256, 34407, 256, 34409, 256, 34473, 256, 34440, + 256, 34574, 256, 34530, 256, 34681, 256, 34600, 256, 34667, 256, 34694, + 256, 19799, 256, 34785, 256, 34817, 256, 17913, 256, 34912, 256, 34915, + 256, 161383, 256, 35031, 256, 35038, 256, 17973, 256, 35066, 256, 13499, + 256, 161966, 256, 162150, 256, 18110, 256, 18119, 256, 35488, 256, 35565, + 256, 35722, 256, 35925, 256, 162984, 256, 36011, 256, 36033, 256, 36123, + 256, 36215, 256, 163631, 256, 133124, 256, 36299, 256, 36284, 256, 36336, + 256, 133342, 256, 36564, 256, 36664, 256, 165330, 256, 165357, 256, + 37012, 256, 37105, 256, 37137, 256, 165678, 256, 37147, 256, 37432, 256, + 37591, 256, 37592, 256, 37500, 256, 37881, 256, 37909, 256, 166906, 256, + 38283, 256, 18837, 256, 38327, 256, 167287, 256, 18918, 256, 38595, 256, + 23986, 256, 38691, 256, 168261, 256, 168474, 256, 19054, 256, 19062, 256, + 38880, 256, 168970, 256, 19122, 256, 169110, 256, 38923, 256, 38923, 256, + 38953, 256, 169398, 256, 39138, 256, 19251, 256, 39209, 256, 39335, 256, + 39362, 256, 39422, 256, 19406, 256, 170800, 256, 39698, 256, 40000, 256, + 40189, 256, 19662, 256, 19693, 256, 40295, 256, 172238, 256, 19704, 256, + 172293, 256, 172558, 256, 172689, 256, 40635, 256, 19798, 256, 40697, + 256, 40702, 256, 40709, 256, 40719, 256, 40726, 256, 40763, 256, 173568, +}; + +/* index tables for the decomposition data */ +#define DECOMP_SHIFT 8 +static unsigned char decomp_index1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 16, 17, 18, 19, 20, 21, 22, 7, 7, 7, 7, 7, 23, 7, + 7, 7, 24, 25, 26, 27, 28, 29, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 30, 31, 32, 33, 34, 35, 36, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 37, 7, 7, 38, 39, 40, + 41, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 42, 43, 44, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static unsigned short decomp_index2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 3, 0, 6, 0, 0, 0, 0, 8, 0, 0, 11, 13, 15, 18, 0, 0, 20, 23, 25, 0, 27, + 31, 35, 0, 39, 42, 45, 48, 51, 54, 0, 57, 60, 63, 66, 69, 72, 75, 78, 81, + 0, 84, 87, 90, 93, 96, 99, 0, 0, 102, 105, 108, 111, 114, 0, 0, 117, 120, + 123, 126, 129, 132, 0, 135, 138, 141, 144, 147, 150, 153, 156, 159, 0, + 162, 165, 168, 171, 174, 177, 0, 0, 180, 183, 186, 189, 192, 0, 195, 198, + 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231, 234, 237, 240, + 243, 0, 0, 246, 249, 252, 255, 258, 261, 264, 267, 270, 273, 276, 279, + 282, 285, 288, 291, 294, 297, 300, 303, 0, 0, 306, 309, 312, 315, 318, + 321, 324, 327, 330, 0, 333, 336, 339, 342, 345, 348, 0, 351, 354, 357, + 360, 363, 366, 369, 372, 0, 0, 375, 378, 381, 384, 387, 390, 393, 0, 0, + 396, 399, 402, 405, 408, 411, 0, 0, 414, 417, 420, 423, 426, 429, 432, + 435, 438, 441, 444, 447, 450, 453, 456, 459, 462, 465, 0, 0, 468, 471, + 474, 477, 480, 483, 486, 489, 492, 495, 498, 501, 504, 507, 510, 513, + 516, 519, 522, 525, 528, 531, 534, 537, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 539, 542, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 545, 548, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 551, 554, 557, 560, 563, 566, 569, 572, + 575, 578, 581, 584, 587, 590, 593, 596, 599, 602, 605, 608, 611, 614, + 617, 620, 623, 0, 626, 629, 632, 635, 638, 641, 0, 0, 644, 647, 650, 653, + 656, 659, 662, 665, 668, 671, 674, 677, 680, 683, 686, 689, 0, 0, 692, + 695, 698, 701, 704, 707, 710, 713, 716, 719, 722, 725, 728, 731, 734, + 737, 740, 743, 746, 749, 752, 755, 758, 761, 764, 767, 770, 773, 776, + 779, 782, 785, 788, 791, 794, 797, 0, 0, 800, 803, 0, 0, 0, 0, 0, 0, 806, + 809, 812, 815, 818, 821, 824, 827, 830, 833, 836, 839, 842, 845, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 848, 850, 852, 854, 856, 858, 860, 862, 864, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 866, + 869, 872, 875, 878, 881, 0, 0, 884, 886, 888, 890, 892, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 894, 896, 0, 898, 900, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 903, 0, 0, 0, 0, + 0, 905, 0, 0, 0, 908, 0, 0, 0, 0, 0, 910, 913, 916, 919, 921, 924, 927, + 0, 930, 0, 933, 936, 939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 942, 945, 948, 951, 954, 957, 960, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 963, 966, + 969, 972, 975, 0, 978, 980, 982, 984, 987, 990, 992, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 994, 996, 998, 0, + 1000, 1002, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1004, 1007, 0, 1010, 0, 0, 0, + 1013, 0, 0, 0, 0, 1016, 1019, 1022, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1025, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1028, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1031, 1034, 0, 1037, 0, 0, 0, 1040, 0, 0, 0, 0, + 1043, 1046, 1049, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1052, 1055, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1058, 1061, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1064, 1067, 1070, 1073, 0, 0, 1076, 1079, 0, 0, 1082, 1085, + 1088, 1091, 1094, 1097, 0, 0, 1100, 1103, 1106, 1109, 1112, 1115, 0, 0, + 1118, 1121, 1124, 1127, 1130, 1133, 1136, 1139, 1142, 1145, 1148, 1151, + 0, 0, 1154, 1157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1163, 1166, 1169, 1172, + 1175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1178, 1181, 1184, 1187, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1190, 0, 1193, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1199, 0, 0, 0, + 0, 0, 0, 0, 1202, 0, 0, 1205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1208, + 1211, 1214, 1217, 1220, 1223, 1226, 1229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1232, 1235, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1238, 1241, + 0, 1244, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1247, 0, 0, 1250, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1253, 1256, 1259, 0, 0, 1262, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1265, 0, 0, 1268, 1271, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1274, 1277, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1280, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1283, 1286, 1289, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1292, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1295, 0, 0, 0, 0, 0, 0, 1298, 1301, 0, + 1304, 1307, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1310, 1313, 1316, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1319, 0, 1322, 1325, 1328, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1331, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1334, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1337, 1340, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1343, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1345, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1348, 0, 0, 0, 0, 1351, 0, 0, 0, 0, + 1354, 0, 0, 0, 0, 1357, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1360, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1363, 0, 1366, 1369, 1372, 1375, 1378, 0, 0, 0, 0, + 0, 0, 0, 1381, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1384, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1387, 0, 0, 0, 0, 1390, 0, 0, 0, 0, 1393, 0, + 0, 0, 0, 1396, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1399, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1402, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1405, 1408, 1411, 1414, 1417, 1420, 1423, + 1426, 1429, 1432, 1435, 1438, 1441, 1444, 1447, 1450, 1453, 1456, 1459, + 1462, 1465, 1468, 1471, 1474, 1477, 1480, 1483, 1486, 1489, 1492, 1495, + 1498, 1501, 1504, 1507, 1510, 1513, 1516, 1519, 1522, 1525, 1528, 1531, + 1534, 1537, 1540, 1543, 1546, 1549, 1552, 1555, 1558, 1561, 1564, 1567, + 1570, 1573, 1576, 1579, 1582, 1585, 1588, 1591, 1594, 1597, 1600, 1603, + 1606, 1609, 1612, 1615, 1618, 1621, 1624, 1627, 1630, 1633, 1636, 1639, + 1642, 1645, 1648, 1651, 1654, 1657, 1660, 1663, 1666, 1669, 1672, 1675, + 1678, 1681, 1684, 1687, 1690, 1693, 1696, 1699, 1702, 1705, 1708, 1711, + 1714, 1717, 1720, 1723, 1726, 1729, 1732, 1735, 1738, 1741, 1744, 1747, + 1750, 1753, 1756, 1759, 1762, 1765, 1768, 1771, 1774, 1777, 1780, 1783, + 1786, 1789, 1792, 1795, 1798, 1801, 1804, 1807, 1810, 1813, 1816, 1819, + 1822, 1825, 1828, 1831, 1834, 1837, 1840, 1843, 1846, 1849, 1852, 1855, + 1858, 1861, 1864, 1867, 1870, 0, 0, 0, 0, 1873, 1876, 1879, 1882, 1885, + 1888, 1891, 1894, 1897, 1900, 1903, 1906, 1909, 1912, 1915, 1918, 1921, + 1924, 1927, 1930, 1933, 1936, 1939, 1942, 1945, 1948, 1951, 1954, 1957, + 1960, 1963, 1966, 1969, 1972, 1975, 1978, 1981, 1984, 1987, 1990, 1993, + 1996, 1999, 2002, 2005, 2008, 2011, 2014, 2017, 2020, 2023, 2026, 2029, + 2032, 2035, 2038, 2041, 2044, 2047, 2050, 2053, 2056, 2059, 2062, 2065, + 2068, 2071, 2074, 2077, 2080, 2083, 2086, 2089, 2092, 2095, 2098, 2101, + 2104, 2107, 2110, 2113, 2116, 2119, 2122, 2125, 2128, 2131, 2134, 2137, + 2140, 0, 0, 0, 0, 0, 0, 2143, 2146, 2149, 2152, 2155, 2158, 2161, 2164, + 2167, 2170, 2173, 2176, 2179, 2182, 2185, 2188, 2191, 2194, 2197, 2200, + 2203, 2206, 0, 0, 2209, 2212, 2215, 2218, 2221, 2224, 0, 0, 2227, 2230, + 2233, 2236, 2239, 2242, 2245, 2248, 2251, 2254, 2257, 2260, 2263, 2266, + 2269, 2272, 2275, 2278, 2281, 2284, 2287, 2290, 2293, 2296, 2299, 2302, + 2305, 2308, 2311, 2314, 2317, 2320, 2323, 2326, 2329, 2332, 2335, 2338, + 0, 0, 2341, 2344, 2347, 2350, 2353, 2356, 0, 0, 2359, 2362, 2365, 2368, + 2371, 2374, 2377, 2380, 0, 2383, 0, 2386, 0, 2389, 0, 2392, 2395, 2398, + 2401, 2404, 2407, 2410, 2413, 2416, 2419, 2422, 2425, 2428, 2431, 2434, + 2437, 2440, 2443, 2446, 2448, 2451, 2453, 2456, 2458, 2461, 2463, 2466, + 2468, 2471, 2473, 2476, 0, 0, 2478, 2481, 2484, 2487, 2490, 2493, 2496, + 2499, 2502, 2505, 2508, 2511, 2514, 2517, 2520, 2523, 2526, 2529, 2532, + 2535, 2538, 2541, 2544, 2547, 2550, 2553, 2556, 2559, 2562, 2565, 2568, + 2571, 2574, 2577, 2580, 2583, 2586, 2589, 2592, 2595, 2598, 2601, 2604, + 2607, 2610, 2613, 2616, 2619, 2622, 2625, 2628, 2631, 2634, 0, 2637, + 2640, 2643, 2646, 2649, 2652, 2654, 2657, 2660, 2662, 2665, 2668, 2671, + 2674, 2677, 0, 2680, 2683, 2686, 2689, 2691, 2694, 2696, 2699, 2702, + 2705, 2708, 2711, 2714, 2717, 0, 0, 2719, 2722, 2725, 2728, 2731, 2734, + 0, 2736, 2739, 2742, 2745, 2748, 2751, 2754, 2756, 2759, 2762, 2765, + 2768, 2771, 2774, 2777, 2779, 2782, 2785, 2787, 0, 0, 2789, 2792, 2795, + 0, 2798, 2801, 2804, 2807, 2809, 2812, 2814, 2817, 2819, 0, 2822, 2824, + 2826, 2828, 2830, 2832, 2834, 2836, 2838, 2840, 2842, 0, 0, 0, 0, 0, 0, + 2844, 0, 0, 0, 0, 0, 2846, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2849, + 2851, 2854, 0, 0, 0, 0, 0, 0, 0, 0, 2858, 0, 0, 0, 2860, 2863, 0, 2867, + 2870, 0, 0, 0, 0, 2874, 0, 2877, 0, 0, 0, 0, 0, 0, 0, 0, 2880, 2883, + 2886, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2889, 0, 0, 0, 0, 0, 0, 0, + 2894, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2896, 2898, 0, 0, + 2900, 2902, 2904, 2906, 2908, 2910, 2912, 2914, 2916, 2918, 2920, 2922, + 2924, 2926, 2928, 2930, 2932, 2934, 2936, 2938, 2940, 2942, 2944, 2946, + 2948, 2950, 2952, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2954, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2957, 2961, 2965, 2967, 0, 2970, 2974, 2978, 0, 2980, 2983, 2985, 2987, + 2989, 2991, 2993, 2995, 2997, 2999, 3001, 0, 3003, 3005, 0, 0, 3008, + 3010, 3012, 3014, 3016, 0, 0, 3018, 3021, 3025, 0, 3028, 0, 3030, 0, + 3032, 0, 3034, 3036, 3038, 3040, 0, 3042, 3044, 3046, 0, 3048, 3050, + 3052, 3054, 3056, 3058, 3060, 0, 0, 0, 3062, 3064, 3066, 3068, 0, 0, 0, + 0, 3070, 3072, 3074, 3076, 3078, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3080, 3084, + 3088, 3092, 3096, 3100, 3104, 3108, 3112, 3116, 3120, 3124, 3128, 3131, + 3133, 3136, 3140, 3143, 3145, 3148, 3152, 3157, 3160, 3162, 3165, 3169, + 3171, 3173, 3175, 3177, 3179, 3182, 3186, 3189, 3191, 3194, 3198, 3203, + 3206, 3208, 3211, 3215, 3217, 3219, 3221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3223, 3226, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3229, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3232, + 3235, 3238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3241, 0, 0, 0, 0, 3244, 0, 0, 3247, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3250, 0, 3253, + 0, 0, 0, 0, 0, 3256, 3259, 0, 3263, 3266, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 3270, 0, 0, 3273, 0, 0, 3276, 0, 3279, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3282, 0, 3285, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3288, 3291, 3294, 3297, 3300, 0, 0, 3303, 3306, + 0, 0, 3309, 3312, 0, 0, 0, 0, 0, 0, 3315, 3318, 0, 0, 3321, 3324, 0, 0, + 3327, 3330, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3333, 3336, 3339, 3342, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3345, + 3348, 3351, 3354, 0, 0, 0, 0, 0, 0, 3357, 3360, 3363, 3366, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3369, 3371, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3373, 3375, 3377, 3379, 3381, 3383, 3385, 3387, + 3389, 3391, 3394, 3397, 3400, 3403, 3406, 3409, 3412, 3415, 3418, 3421, + 3424, 3428, 3432, 3436, 3440, 3444, 3448, 3452, 3456, 3460, 3465, 3470, + 3475, 3480, 3485, 3490, 3495, 3500, 3505, 3510, 3515, 3518, 3521, 3524, + 3527, 3530, 3533, 3536, 3539, 3542, 3546, 3550, 3554, 3558, 3562, 3566, + 3570, 3574, 3578, 3582, 3586, 3590, 3594, 3598, 3602, 3606, 3610, 3614, + 3618, 3622, 3626, 3630, 3634, 3638, 3642, 3646, 3650, 3654, 3658, 3662, + 3666, 3670, 3674, 3678, 3682, 3686, 3690, 3692, 3694, 3696, 3698, 3700, + 3702, 3704, 3706, 3708, 3710, 3712, 3714, 3716, 3718, 3720, 3722, 3724, + 3726, 3728, 3730, 3732, 3734, 3736, 3738, 3740, 3742, 3744, 3746, 3748, + 3750, 3752, 3754, 3756, 3758, 3760, 3762, 3764, 3766, 3768, 3770, 3772, + 3774, 3776, 3778, 3780, 3782, 3784, 3786, 3788, 3790, 3792, 3794, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3796, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3801, 3805, 3808, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3812, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3815, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3817, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3819, 3821, 3823, 3825, 3827, 3829, 3831, + 3833, 3835, 3837, 3839, 3841, 3843, 3845, 3847, 3849, 3851, 3853, 3855, + 3857, 3859, 3861, 3863, 3865, 3867, 3869, 3871, 3873, 3875, 3877, 3879, + 3881, 3883, 3885, 3887, 3889, 3891, 3893, 3895, 3897, 3899, 3901, 3903, + 3905, 3907, 3909, 3911, 3913, 3915, 3917, 3919, 3921, 3923, 3925, 3927, + 3929, 3931, 3933, 3935, 3937, 3939, 3941, 3943, 3945, 3947, 3949, 3951, + 3953, 3955, 3957, 3959, 3961, 3963, 3965, 3967, 3969, 3971, 3973, 3975, + 3977, 3979, 3981, 3983, 3985, 3987, 3989, 3991, 3993, 3995, 3997, 3999, + 4001, 4003, 4005, 4007, 4009, 4011, 4013, 4015, 4017, 4019, 4021, 4023, + 4025, 4027, 4029, 4031, 4033, 4035, 4037, 4039, 4041, 4043, 4045, 4047, + 4049, 4051, 4053, 4055, 4057, 4059, 4061, 4063, 4065, 4067, 4069, 4071, + 4073, 4075, 4077, 4079, 4081, 4083, 4085, 4087, 4089, 4091, 4093, 4095, + 4097, 4099, 4101, 4103, 4105, 4107, 4109, 4111, 4113, 4115, 4117, 4119, + 4121, 4123, 4125, 4127, 4129, 4131, 4133, 4135, 4137, 4139, 4141, 4143, + 4145, 4147, 4149, 4151, 4153, 4155, 4157, 4159, 4161, 4163, 4165, 4167, + 4169, 4171, 4173, 4175, 4177, 4179, 4181, 4183, 4185, 4187, 4189, 4191, + 4193, 4195, 4197, 4199, 4201, 4203, 4205, 4207, 4209, 4211, 4213, 4215, + 4217, 4219, 4221, 4223, 4225, 4227, 4229, 4231, 4233, 4235, 4237, 4239, + 4241, 4243, 4245, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4249, 0, 4251, 4253, 4255, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4257, 0, 4260, 0, 4263, 0, 4266, 0, 4269, 0, + 4272, 0, 4275, 0, 4278, 0, 4281, 0, 4284, 0, 4287, 0, 4290, 0, 0, 4293, + 0, 4296, 0, 4299, 0, 0, 0, 0, 0, 0, 4302, 4305, 0, 4308, 4311, 0, 4314, + 4317, 0, 4320, 4323, 0, 4326, 4329, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4332, 0, 0, 0, 0, 0, 0, 4335, 4338, 0, + 4341, 4344, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4347, 0, 4350, 0, 4353, + 0, 4356, 0, 4359, 0, 4362, 0, 4365, 0, 4368, 0, 4371, 0, 4374, 0, 4377, + 0, 4380, 0, 0, 4383, 0, 4386, 0, 4389, 0, 0, 0, 0, 0, 0, 4392, 4395, 0, + 4398, 4401, 0, 4404, 4407, 0, 4410, 4413, 0, 4416, 4419, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4422, 0, 0, 4425, + 4428, 4431, 4434, 0, 0, 0, 4437, 4440, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4443, 4445, 4447, 4449, 4451, + 4453, 4455, 4457, 4459, 4461, 4463, 4465, 4467, 4469, 4471, 4473, 4475, + 4477, 4479, 4481, 4483, 4485, 4487, 4489, 4491, 4493, 4495, 4497, 4499, + 4501, 4503, 4505, 4507, 4509, 4511, 4513, 4515, 4517, 4519, 4521, 4523, + 4525, 4527, 4529, 4531, 4533, 4535, 4537, 4539, 4541, 4543, 4545, 4547, + 4549, 4551, 4553, 4555, 4557, 4559, 4561, 4563, 4565, 4567, 4569, 4571, + 4573, 4575, 4577, 4579, 4581, 4583, 4585, 4587, 4589, 4591, 4593, 4595, + 4597, 4599, 4601, 4603, 4605, 4607, 4609, 4611, 4613, 4615, 4617, 4619, + 4621, 4623, 4625, 4627, 4629, 0, 0, 0, 4631, 4633, 4635, 4637, 4639, + 4641, 4643, 4645, 4647, 4649, 4651, 4653, 4655, 4657, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4659, 4663, 4667, + 4671, 4675, 4679, 4683, 4687, 4691, 4695, 4699, 4703, 4707, 4711, 4715, + 4720, 4725, 4730, 4735, 4740, 4745, 4750, 4755, 4760, 4765, 4770, 4775, + 4780, 4785, 0, 0, 0, 4790, 4794, 4798, 4802, 4806, 4810, 4814, 4818, + 4822, 4826, 4830, 4834, 4838, 4842, 4846, 4850, 4854, 4858, 4862, 4866, + 4870, 4874, 4878, 4882, 4886, 4890, 4894, 4898, 4902, 4906, 4910, 4914, + 4918, 4922, 4926, 4930, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4934, + 4937, 4940, 4943, 4946, 4949, 4952, 4955, 4958, 4961, 4964, 4967, 4970, + 4973, 4976, 4979, 4981, 4983, 4985, 4987, 4989, 4991, 4993, 4995, 4997, + 4999, 5001, 5003, 5005, 5007, 5010, 5013, 5016, 5019, 5022, 5025, 5028, + 5031, 5034, 5037, 5040, 5043, 5046, 0, 0, 0, 0, 5049, 5051, 5053, 5055, + 5057, 5059, 5061, 5063, 5065, 5067, 5069, 5071, 5073, 5075, 5077, 5079, + 5081, 5083, 5085, 5087, 5089, 5091, 5093, 5095, 5097, 5099, 5101, 5103, + 5105, 5107, 5109, 5111, 5113, 5115, 5117, 5119, 5121, 5123, 5125, 5127, + 5129, 5131, 5133, 5135, 5137, 5139, 5141, 5143, 5145, 5147, 5150, 5153, + 5156, 5159, 5162, 5165, 5168, 5171, 5174, 5177, 5180, 5183, 5186, 5189, + 5192, 5195, 5198, 5201, 5204, 5207, 5210, 5213, 5216, 5219, 5223, 5227, + 0, 0, 0, 0, 5231, 5233, 5235, 5237, 5239, 5241, 5243, 5245, 5247, 5249, + 5251, 5253, 5255, 5257, 5259, 5261, 5263, 5265, 5267, 5269, 5271, 5273, + 5275, 5277, 5279, 5281, 5283, 5285, 5287, 5289, 5291, 5293, 5295, 5297, + 5299, 5301, 5303, 5305, 5307, 5309, 5311, 5313, 5315, 5317, 5319, 5321, + 5323, 0, 5325, 5330, 5335, 5340, 5344, 5349, 5353, 5357, 5363, 5368, + 5372, 5376, 5380, 5385, 5390, 5394, 5398, 5401, 5405, 5410, 5415, 5418, + 5424, 5431, 5437, 5441, 5447, 5453, 5458, 5462, 5466, 5470, 5475, 5481, + 5486, 5490, 5494, 5498, 5501, 5504, 5507, 5510, 5514, 5518, 5524, 5528, + 5533, 5539, 5543, 5546, 5549, 5555, 5560, 5566, 5570, 5576, 5579, 5583, + 5587, 5591, 5595, 5599, 5604, 5608, 5611, 5615, 5619, 5623, 5628, 5632, + 5636, 5640, 5646, 5651, 5654, 5660, 5663, 5668, 5673, 5677, 5681, 5685, + 5690, 5693, 5697, 5702, 5705, 5711, 5715, 5718, 5721, 5724, 5727, 5730, + 5733, 5736, 5739, 5742, 5745, 5749, 5753, 5757, 5761, 5765, 5769, 5773, + 5777, 5781, 5785, 5789, 5793, 5797, 5801, 5805, 5809, 5812, 5815, 5819, + 5822, 0, 0, 0, 0, 5825, 5828, 5831, 5834, 5837, 5842, 5845, 5848, 5851, + 5854, 5857, 5860, 5863, 5866, 5870, 5875, 5878, 5881, 5884, 5887, 5890, + 5893, 5896, 5900, 5904, 5908, 5912, 5915, 5918, 5921, 5924, 5927, 5930, + 5933, 5936, 5939, 5942, 5946, 5950, 5953, 5957, 5961, 5965, 5968, 5972, + 5976, 5981, 5984, 5988, 5992, 5996, 6000, 6006, 6013, 6016, 6019, 6022, + 6025, 6028, 6031, 6034, 6037, 6040, 6043, 6046, 6049, 6052, 6055, 6058, + 6061, 6064, 6067, 6072, 6075, 6078, 6081, 6086, 6090, 6093, 6096, 6099, + 6102, 6105, 6108, 6111, 6114, 6117, 6120, 6124, 6127, 6130, 6134, 6138, + 6141, 6146, 6150, 6153, 6156, 6159, 0, 0, 6162, 6165, 6168, 6171, 6174, + 6177, 6180, 6183, 6186, 6189, 6193, 6197, 6201, 6205, 6209, 6213, 6217, + 6221, 6225, 6229, 6233, 6237, 6241, 6245, 6249, 6253, 6257, 6261, 6265, + 6269, 6273, 0, 6277, 6279, 6281, 6283, 6285, 6287, 6289, 6291, 6293, + 6295, 6297, 6299, 6301, 6303, 6305, 6307, 6309, 6311, 6313, 6315, 6317, + 6319, 6321, 6323, 6325, 6327, 6329, 6331, 6333, 6335, 6337, 6339, 6341, + 6343, 6345, 6347, 6349, 6351, 6353, 6355, 6357, 6359, 6361, 6363, 6365, + 6367, 6369, 6371, 6373, 6375, 6377, 6379, 6381, 6383, 6385, 6387, 6389, + 6391, 6393, 6395, 6397, 6399, 6401, 6403, 6405, 6407, 6409, 6411, 6413, + 6415, 6417, 6419, 6421, 6423, 6425, 6427, 6429, 6431, 6433, 6435, 6437, + 6439, 6441, 6443, 6445, 6447, 6449, 6451, 6453, 6455, 6457, 6459, 6461, + 6463, 6465, 6467, 6469, 6471, 6473, 6475, 6477, 6479, 6481, 6483, 6485, + 6487, 6489, 6491, 6493, 6495, 6497, 6499, 6501, 6503, 6505, 6507, 6509, + 6511, 6513, 6515, 6517, 6519, 6521, 6523, 6525, 6527, 6529, 6531, 6533, + 6535, 6537, 6539, 6541, 6543, 6545, 6547, 6549, 6551, 6553, 6555, 6557, + 6559, 6561, 6563, 6565, 6567, 6569, 6571, 6573, 6575, 6577, 6579, 6581, + 6583, 6585, 6587, 6589, 6591, 6593, 6595, 6597, 6599, 6601, 6603, 6605, + 6607, 6609, 6611, 6613, 6615, 6617, 6619, 6621, 6623, 6625, 6627, 6629, + 6631, 6633, 6635, 6637, 6639, 6641, 6643, 6645, 6647, 6649, 6651, 6653, + 6655, 6657, 6659, 6661, 6663, 6665, 6667, 6669, 6671, 6673, 6675, 6677, + 6679, 6681, 6683, 6685, 6687, 6689, 6691, 6693, 6695, 6697, 6699, 6701, + 6703, 6705, 6707, 6709, 6711, 6713, 6715, 6717, 6719, 6721, 6723, 6725, + 6727, 6729, 6731, 6733, 6735, 6737, 6739, 6741, 6743, 6745, 6747, 6749, + 6751, 6753, 6755, 6757, 6759, 6761, 6763, 6765, 6767, 6769, 6771, 6773, + 6775, 6777, 6779, 6781, 6783, 6785, 6787, 6789, 6791, 6793, 6795, 6797, + 6799, 6801, 6803, 6805, 6807, 6809, 6811, 6813, 6815, 0, 0, 6817, 0, + 6819, 0, 0, 6821, 6823, 6825, 6827, 6829, 6831, 6833, 6835, 6837, 6839, + 0, 6841, 0, 6843, 0, 0, 6845, 6847, 0, 0, 0, 6849, 6851, 6853, 6855, 0, + 0, 6857, 6859, 6861, 6863, 6865, 6867, 6869, 6871, 6873, 6875, 6877, + 6879, 6881, 6883, 6885, 6887, 6889, 6891, 6893, 6895, 6897, 6899, 6901, + 6903, 6905, 6907, 6909, 6911, 6913, 6915, 6917, 6919, 6921, 6923, 6925, + 6927, 6929, 6931, 6933, 6935, 6937, 6939, 6941, 6943, 6945, 6947, 6949, + 6951, 6953, 6955, 6957, 6959, 6961, 6963, 6965, 6967, 6969, 6971, 6973, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 6975, 6978, 6981, 6984, 6988, 6992, 6995, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 6998, 7001, 7004, 7007, 7010, 0, 0, 0, 0, 0, 7013, + 0, 7016, 7019, 7021, 7023, 7025, 7027, 7029, 7031, 7033, 7035, 7037, + 7039, 7042, 7045, 7048, 7051, 7054, 7057, 7060, 7063, 7066, 7069, 7072, + 7075, 0, 7078, 7081, 7084, 7087, 7090, 0, 7093, 0, 7096, 7099, 0, 7102, + 7105, 0, 7108, 7111, 7114, 7117, 7120, 7123, 7126, 7129, 7132, 7135, + 7138, 7140, 7142, 7144, 7146, 7148, 7150, 7152, 7154, 7156, 7158, 7160, + 7162, 7164, 7166, 7168, 7170, 7172, 7174, 7176, 7178, 7180, 7182, 7184, + 7186, 7188, 7190, 7192, 7194, 7196, 7198, 7200, 7202, 7204, 7206, 7208, + 7210, 7212, 7214, 7216, 7218, 7220, 7222, 7224, 7226, 7228, 7230, 7232, + 7234, 7236, 7238, 7240, 7242, 7244, 7246, 7248, 7250, 7252, 7254, 7256, + 7258, 7260, 7262, 7264, 7266, 7268, 7270, 7272, 7274, 7276, 7278, 7280, + 7282, 7284, 7286, 7288, 7290, 7292, 7294, 7296, 7298, 7300, 7302, 7304, + 7306, 7308, 7310, 7312, 7314, 7316, 7318, 7320, 7322, 7324, 7326, 7328, + 7330, 7332, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7334, 7336, 7338, 7340, 7342, + 7344, 7346, 7348, 7350, 7352, 7354, 7356, 7358, 7360, 7362, 7364, 7366, + 7368, 7370, 7372, 7374, 7376, 7378, 7380, 7383, 7386, 7389, 7392, 7395, + 7398, 7401, 7404, 7407, 7410, 7413, 7416, 7419, 7422, 7425, 7428, 7431, + 7434, 7436, 7438, 7440, 7442, 7445, 7448, 7451, 7454, 7457, 7460, 7463, + 7466, 7469, 7472, 7475, 7478, 7481, 7484, 7487, 7490, 7493, 7496, 7499, + 7502, 7505, 7508, 7511, 7514, 7517, 7520, 7523, 7526, 7529, 7532, 7535, + 7538, 7541, 7544, 7547, 7550, 7553, 7556, 7559, 7562, 7565, 7568, 7571, + 7574, 7577, 7580, 7583, 7586, 7589, 7592, 7595, 7598, 7601, 7604, 7607, + 7610, 7613, 7616, 7619, 7622, 7625, 7628, 7631, 7634, 7637, 7640, 7643, + 7646, 7649, 7652, 7655, 7658, 7661, 7664, 7667, 7670, 7673, 7676, 7679, + 7682, 7685, 7688, 7691, 7694, 7697, 7700, 7703, 7706, 7709, 7712, 7715, + 7718, 7721, 7724, 7728, 7732, 7736, 7740, 7744, 7748, 7751, 7754, 7757, + 7760, 7763, 7766, 7769, 7772, 7775, 7778, 7781, 7784, 7787, 7790, 7793, + 7796, 7799, 7802, 7805, 7808, 7811, 7814, 7817, 7820, 7823, 7826, 7829, + 7832, 7835, 7838, 7841, 7844, 7847, 7850, 7853, 7856, 7859, 7862, 7865, + 7868, 7871, 7874, 7877, 7880, 7883, 7886, 7889, 7892, 7895, 7898, 7901, + 7904, 7907, 7910, 7913, 7916, 7919, 7922, 7925, 7928, 7931, 7934, 7937, + 7940, 7943, 7946, 7949, 7952, 7955, 7958, 7961, 7964, 7967, 7970, 7973, + 7976, 7979, 7982, 7985, 7988, 7991, 7994, 7997, 8000, 8003, 8006, 8009, + 8012, 8015, 8018, 8021, 8024, 8027, 8030, 8033, 8036, 8039, 8042, 8045, + 8048, 8051, 8054, 8057, 8060, 8063, 8066, 8069, 8072, 8075, 8078, 8081, + 8084, 8087, 8090, 8093, 8096, 8099, 8102, 8105, 8108, 8111, 8114, 8117, + 8120, 8123, 8126, 8129, 8132, 8135, 8138, 8141, 8144, 8147, 8150, 8153, + 8156, 8159, 8162, 8165, 8168, 8171, 8174, 8178, 8182, 8186, 8189, 8192, + 8195, 8198, 8201, 8204, 8207, 8210, 8213, 8216, 8219, 8222, 8225, 8228, + 8231, 8234, 8237, 8240, 8243, 8246, 8249, 8252, 8255, 8258, 8261, 8264, + 8267, 8270, 8273, 8276, 8279, 8282, 8285, 8288, 8291, 8294, 8297, 8300, + 8303, 8306, 8309, 8312, 8315, 8318, 8321, 8324, 8327, 8330, 8333, 8336, + 8339, 8342, 8345, 8348, 8351, 8354, 8357, 8360, 8363, 8366, 8369, 8372, + 8375, 8378, 8381, 8384, 8387, 8390, 8393, 8396, 8399, 8402, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8405, 8409, 8413, 8417, 8421, + 8425, 8429, 8433, 8437, 8441, 8445, 8449, 8453, 8457, 8461, 8465, 8469, + 8473, 8477, 8481, 8485, 8489, 8493, 8497, 8501, 8505, 8509, 8513, 8517, + 8521, 8525, 8529, 8533, 8537, 8541, 8545, 8549, 8553, 8557, 8561, 8565, + 8569, 8573, 8577, 8581, 8585, 8589, 8593, 8597, 8601, 8605, 8609, 8613, + 8617, 8621, 8625, 8629, 8633, 8637, 8641, 8645, 8649, 8653, 8657, 0, 0, + 8661, 8665, 8669, 8673, 8677, 8681, 8685, 8689, 8693, 8697, 8701, 8705, + 8709, 8713, 8717, 8721, 8725, 8729, 8733, 8737, 8741, 8745, 8749, 8753, + 8757, 8761, 8765, 8769, 8773, 8777, 8781, 8785, 8789, 8793, 8797, 8801, + 8805, 8809, 8813, 8817, 8821, 8825, 8829, 8833, 8837, 8841, 8845, 8849, + 8853, 8857, 8861, 8865, 8869, 8873, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8877, 8881, 8885, 8890, 8895, 8900, 8905, 8910, 8915, 8920, + 8924, 8943, 8952, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8957, 8959, 8961, 8963, 8965, 8967, 8969, + 8971, 8973, 8975, 8977, 8979, 8981, 8983, 8985, 8987, 8989, 8991, 8993, + 8995, 8997, 0, 0, 0, 0, 8999, 9001, 9003, 9005, 9007, 9009, 9011, 9013, + 9015, 9017, 0, 9019, 9021, 9023, 9025, 9027, 9029, 9031, 9033, 9035, + 9037, 9039, 9041, 9043, 9045, 9047, 9049, 9051, 9053, 9055, 0, 9057, + 9059, 9061, 9063, 0, 0, 0, 0, 9065, 9068, 9071, 0, 9074, 0, 9077, 9080, + 9083, 9086, 9089, 9092, 9095, 9098, 9101, 9104, 9107, 9109, 9111, 9113, + 9115, 9117, 9119, 9121, 9123, 9125, 9127, 9129, 9131, 9133, 9135, 9137, + 9139, 9141, 9143, 9145, 9147, 9149, 9151, 9153, 9155, 9157, 9159, 9161, + 9163, 9165, 9167, 9169, 9171, 9173, 9175, 9177, 9179, 9181, 9183, 9185, + 9187, 9189, 9191, 9193, 9195, 9197, 9199, 9201, 9203, 9205, 9207, 9209, + 9211, 9213, 9215, 9217, 9219, 9221, 9223, 9225, 9227, 9229, 9231, 9233, + 9235, 9237, 9239, 9241, 9243, 9245, 9247, 9249, 9251, 9253, 9255, 9257, + 9259, 9261, 9263, 9265, 9267, 9269, 9271, 9273, 9275, 9277, 9279, 9281, + 9283, 9285, 9287, 9289, 9291, 9293, 9295, 9297, 9299, 9301, 9303, 9305, + 9307, 9309, 9311, 9313, 9315, 9317, 9319, 9321, 9323, 9325, 9327, 9329, + 9331, 9333, 9335, 9337, 9339, 9341, 9344, 9347, 9350, 9353, 9356, 9359, + 9362, 0, 0, 0, 0, 9365, 9367, 9369, 9371, 9373, 9375, 9377, 9379, 9381, + 9383, 9385, 9387, 9389, 9391, 9393, 9395, 9397, 9399, 9401, 9403, 9405, + 9407, 9409, 9411, 9413, 9415, 9417, 9419, 9421, 9423, 9425, 9427, 9429, + 9431, 9433, 9435, 9437, 9439, 9441, 9443, 9445, 9447, 9449, 9451, 9453, + 9455, 9457, 9459, 9461, 9463, 9465, 9467, 9469, 9471, 9473, 9475, 9477, + 9479, 9481, 9483, 9485, 9487, 9489, 9491, 9493, 9495, 9497, 9499, 9501, + 9503, 9505, 9507, 9509, 9511, 9513, 9515, 9517, 9519, 9521, 9523, 9525, + 9527, 9529, 9531, 9533, 9535, 9537, 9539, 9541, 9543, 9545, 9547, 9549, + 9551, 9553, 9555, 9557, 9559, 9561, 9563, 9565, 9567, 9569, 9571, 9573, + 9575, 9577, 9579, 9581, 9583, 9585, 9587, 9589, 9591, 9593, 9595, 9597, + 9599, 9601, 9603, 9605, 9607, 9609, 9611, 9613, 9615, 9617, 9619, 9621, + 9623, 9625, 9627, 9629, 9631, 9633, 9635, 9637, 9639, 9641, 9643, 9645, + 9647, 9649, 9651, 9653, 9655, 9657, 9659, 9661, 9663, 9665, 9667, 9669, + 9671, 9673, 9675, 9677, 9679, 9681, 9683, 9685, 9687, 9689, 9691, 9693, + 9695, 9697, 9699, 9701, 9703, 9705, 9707, 9709, 9711, 9713, 9715, 9717, + 9719, 9721, 9723, 9725, 9727, 9729, 9731, 9733, 9735, 9737, 9739, 9741, + 9743, 0, 0, 0, 9745, 9747, 9749, 9751, 9753, 9755, 0, 0, 9757, 9759, + 9761, 9763, 9765, 9767, 0, 0, 9769, 9771, 9773, 9775, 9777, 9779, 0, 0, + 9781, 9783, 9785, 0, 0, 0, 9787, 9789, 9791, 9793, 9795, 9797, 9799, 0, + 9801, 9803, 9805, 9807, 9809, 9811, 9813, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 9815, 9818, 9821, 9824, 9827, 9830, 9833, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 9836, 9839, 9842, 9845, 9848, 9851, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9854, 9856, 9858, 9860, 9862, 9864, + 9866, 9868, 9870, 9872, 9874, 9876, 9878, 9880, 9882, 9884, 9886, 9888, + 9890, 9892, 9894, 9896, 9898, 9900, 9902, 9904, 9906, 9908, 9910, 9912, + 9914, 9916, 9918, 9920, 9922, 9924, 9926, 9928, 9930, 9932, 9934, 9936, + 9938, 9940, 9942, 9944, 9946, 9948, 9950, 9952, 9954, 9956, 9958, 9960, + 9962, 9964, 9966, 9968, 9970, 9972, 9974, 9976, 9978, 9980, 9982, 9984, + 9986, 9988, 9990, 9992, 9994, 9996, 9998, 10000, 10002, 10004, 10006, + 10008, 10010, 10012, 10014, 10016, 10018, 10020, 10022, 0, 10024, 10026, + 10028, 10030, 10032, 10034, 10036, 10038, 10040, 10042, 10044, 10046, + 10048, 10050, 10052, 10054, 10056, 10058, 10060, 10062, 10064, 10066, + 10068, 10070, 10072, 10074, 10076, 10078, 10080, 10082, 10084, 10086, + 10088, 10090, 10092, 10094, 10096, 10098, 10100, 10102, 10104, 10106, + 10108, 10110, 10112, 10114, 10116, 10118, 10120, 10122, 10124, 10126, + 10128, 10130, 10132, 10134, 10136, 10138, 10140, 10142, 10144, 10146, + 10148, 10150, 10152, 10154, 10156, 10158, 10160, 10162, 10164, 0, 10166, + 10168, 0, 0, 10170, 0, 0, 10172, 10174, 0, 0, 10176, 10178, 10180, 10182, + 0, 10184, 10186, 10188, 10190, 10192, 10194, 10196, 10198, 10200, 10202, + 10204, 10206, 0, 10208, 0, 10210, 10212, 10214, 10216, 0, 10218, 10220, + 0, 10222, 10224, 10226, 10228, 10230, 10232, 10234, 10236, 10238, 10240, + 10242, 10244, 10246, 10248, 10250, 10252, 10254, 10256, 10258, 10260, + 10262, 10264, 10266, 10268, 10270, 10272, 10274, 10276, 10278, 10280, + 10282, 10284, 10286, 10288, 10290, 10292, 10294, 10296, 10298, 10300, + 10302, 10304, 10306, 10308, 10310, 10312, 10314, 10316, 10318, 10320, + 10322, 10324, 10326, 10328, 10330, 10332, 10334, 10336, 10338, 10340, + 10342, 10344, 10346, 10348, 10350, 0, 10352, 10354, 10356, 10358, 0, 0, + 10360, 10362, 10364, 10366, 10368, 10370, 10372, 10374, 0, 10376, 10378, + 10380, 10382, 10384, 10386, 10388, 0, 10390, 10392, 10394, 10396, 10398, + 10400, 10402, 10404, 10406, 10408, 10410, 10412, 10414, 10416, 10418, + 10420, 10422, 10424, 10426, 10428, 10430, 10432, 10434, 10436, 10438, + 10440, 10442, 10444, 0, 10446, 10448, 10450, 10452, 0, 10454, 10456, + 10458, 10460, 10462, 0, 10464, 0, 0, 0, 10466, 10468, 10470, 10472, + 10474, 10476, 10478, 0, 10480, 10482, 10484, 10486, 10488, 10490, 10492, + 10494, 10496, 10498, 10500, 10502, 10504, 10506, 10508, 10510, 10512, + 10514, 10516, 10518, 10520, 10522, 10524, 10526, 10528, 10530, 10532, + 10534, 10536, 10538, 10540, 10542, 10544, 10546, 10548, 10550, 10552, + 10554, 10556, 10558, 10560, 10562, 10564, 10566, 10568, 10570, 10572, + 10574, 10576, 10578, 10580, 10582, 10584, 10586, 10588, 10590, 10592, + 10594, 10596, 10598, 10600, 10602, 10604, 10606, 10608, 10610, 10612, + 10614, 10616, 10618, 10620, 10622, 10624, 10626, 10628, 10630, 10632, + 10634, 10636, 10638, 10640, 10642, 10644, 10646, 10648, 10650, 10652, + 10654, 10656, 10658, 10660, 10662, 10664, 10666, 10668, 10670, 10672, + 10674, 10676, 10678, 10680, 10682, 10684, 10686, 10688, 10690, 10692, + 10694, 10696, 10698, 10700, 10702, 10704, 10706, 10708, 10710, 10712, + 10714, 10716, 10718, 10720, 10722, 10724, 10726, 10728, 10730, 10732, + 10734, 10736, 10738, 10740, 10742, 10744, 10746, 10748, 10750, 10752, + 10754, 10756, 10758, 10760, 10762, 10764, 10766, 10768, 10770, 10772, + 10774, 10776, 10778, 10780, 10782, 10784, 10786, 10788, 10790, 10792, + 10794, 10796, 10798, 10800, 10802, 10804, 10806, 10808, 10810, 10812, + 10814, 10816, 10818, 10820, 10822, 10824, 10826, 10828, 10830, 10832, + 10834, 10836, 10838, 10840, 10842, 10844, 10846, 10848, 10850, 10852, + 10854, 10856, 10858, 10860, 10862, 10864, 10866, 10868, 10870, 10872, + 10874, 10876, 10878, 10880, 10882, 10884, 10886, 10888, 10890, 10892, + 10894, 10896, 10898, 10900, 10902, 10904, 10906, 10908, 10910, 10912, + 10914, 10916, 10918, 10920, 10922, 10924, 10926, 10928, 10930, 10932, + 10934, 10936, 10938, 10940, 10942, 10944, 10946, 10948, 10950, 10952, + 10954, 10956, 10958, 10960, 10962, 10964, 10966, 10968, 10970, 10972, + 10974, 10976, 10978, 10980, 10982, 10984, 10986, 10988, 10990, 10992, + 10994, 10996, 10998, 11000, 11002, 11004, 11006, 11008, 11010, 11012, + 11014, 11016, 11018, 11020, 11022, 11024, 11026, 11028, 11030, 11032, + 11034, 11036, 11038, 11040, 11042, 11044, 11046, 11048, 11050, 11052, + 11054, 11056, 11058, 11060, 11062, 11064, 11066, 11068, 11070, 11072, + 11074, 11076, 11078, 11080, 11082, 11084, 11086, 11088, 11090, 11092, + 11094, 11096, 11098, 11100, 11102, 11104, 11106, 11108, 11110, 11112, + 11114, 11116, 11118, 11120, 11122, 11124, 11126, 11128, 11130, 11132, + 11134, 11136, 11138, 11140, 11142, 11144, 11146, 11148, 11150, 11152, + 11154, 0, 0, 0, 0, 11156, 11158, 11160, 11162, 11164, 11166, 11168, + 11170, 11172, 11174, 11176, 11178, 11180, 11182, 11184, 11186, 11188, + 11190, 11192, 11194, 11196, 11198, 11200, 11202, 11204, 11206, 11208, + 11210, 11212, 11214, 11216, 11218, 11220, 11222, 11224, 11226, 11228, + 11230, 11232, 11234, 11236, 11238, 11240, 11242, 11244, 11246, 11248, + 11250, 11252, 11254, 11256, 11258, 11260, 11262, 11264, 11266, 11268, + 11270, 11272, 11274, 11276, 11278, 11280, 11282, 11284, 11286, 11288, + 11290, 11292, 11294, 11296, 11298, 11300, 11302, 11304, 11306, 11308, + 11310, 11312, 11314, 11316, 11318, 11320, 11322, 11324, 11326, 11328, + 11330, 11332, 11334, 11336, 11338, 11340, 11342, 11344, 11346, 11348, + 11350, 11352, 11354, 11356, 11358, 11360, 11362, 11364, 11366, 11368, + 11370, 11372, 11374, 11376, 11378, 11380, 11382, 11384, 11386, 11388, + 11390, 11392, 11394, 11396, 11398, 11400, 11402, 11404, 11406, 11408, + 11410, 11412, 11414, 11416, 11418, 11420, 11422, 11424, 11426, 11428, + 11430, 11432, 11434, 11436, 11438, 11440, 11442, 11444, 11446, 11448, + 11450, 11452, 11454, 11456, 11458, 11460, 11462, 11464, 11466, 11468, + 11470, 11472, 11474, 11476, 11478, 11480, 11482, 11484, 11486, 11488, + 11490, 11492, 11494, 11496, 11498, 11500, 11502, 11504, 11506, 11508, + 11510, 11512, 11514, 11516, 11518, 11520, 11522, 11524, 11526, 11528, + 11530, 11532, 11534, 11536, 11538, 11540, 11542, 11544, 11546, 11548, + 11550, 11552, 11554, 11556, 11558, 11560, 11562, 11564, 11566, 11568, + 11570, 11572, 11574, 11576, 11578, 11580, 11582, 11584, 11586, 11588, + 11590, 11592, 11594, 11596, 11598, 11600, 11602, 11604, 11606, 11608, + 11610, 11612, 11614, 11616, 11618, 11620, 11622, 11624, 11626, 11628, + 11630, 11632, 11634, 11636, 11638, 11640, 11642, 11644, 11646, 11648, + 11650, 11652, 11654, 11656, 11658, 11660, 11662, 11664, 11666, 11668, + 11670, 11672, 11674, 11676, 11678, 11680, 11682, 11684, 11686, 11688, + 11690, 11692, 11694, 11696, 11698, 11700, 11702, 11704, 11706, 11708, + 11710, 11712, 11714, 11716, 11718, 11720, 11722, 11724, 11726, 11728, + 11730, 11732, 11734, 0, 0, 0, 0, 11736, 11738, 11740, 11742, 11744, + 11746, 11748, 11750, 11752, 11754, 11756, 11758, 11760, 11762, 11764, + 11766, 11768, 11770, 11772, 11774, 11776, 11778, 11780, 11782, 11784, + 11786, 11788, 11790, 11792, 11794, 11796, 11798, 11800, 11802, 11804, + 11806, 11808, 11810, 11812, 11814, 11816, 11818, 11820, 11822, 11824, + 11826, 11828, 11830, 11832, 11834, 11836, 11838, 11840, 11842, 11844, + 11846, 11848, 11850, 11852, 11854, 11856, 11858, 11860, 11862, 11864, + 11866, 11868, 11870, 11872, 11874, 11876, 11878, 11880, 11882, 11884, + 11886, 11888, 11890, 11892, 11894, 11896, 11898, 11900, 11902, 11904, + 11906, 11908, 11910, 11912, 11914, 11916, 11918, 11920, 11922, 11924, + 11926, 11928, 11930, 11932, 11934, 11936, 11938, 11940, 11942, 11944, + 11946, 11948, 11950, 11952, 11954, 11956, 11958, 11960, 11962, 11964, + 11966, 11968, 11970, 11972, 11974, 11976, 11978, 11980, 11982, 11984, + 11986, 11988, 11990, 11992, 11994, 11996, 11998, 12000, 12002, 12004, + 12006, 12008, 12010, 12012, 12014, 12016, 12018, 12020, 12022, 12024, + 12026, 12028, 12030, 12032, 12034, 12036, 12038, 12040, 12042, 12044, + 12046, 12048, 12050, 12052, 12054, 12056, 12058, 12060, 12062, 12064, + 12066, 12068, 12070, 12072, 12074, 12076, 12078, 12080, 12082, 12084, + 12086, 12088, 12090, 12092, 12094, 12096, 12098, 12100, 12102, 12104, + 12106, 12108, 12110, 12112, 12114, 12116, 12118, 12120, 12122, 12124, + 12126, 12128, 12130, 12132, 12134, 12136, 12138, 12140, 12142, 12144, + 12146, 12148, 12150, 12152, 12154, 12156, 12158, 12160, 12162, 12164, + 12166, 12168, 12170, 12172, 12174, 12176, 12178, 12180, 12182, 12184, + 12186, 12188, 12190, 12192, 12194, 12196, 12198, 12200, 12202, 12204, + 12206, 12208, 12210, 12212, 12214, 12216, 12218, 12220, 12222, 12224, + 12226, 12228, 12230, 12232, 12234, 12236, 12238, 12240, 12242, 12244, + 12246, 12248, 12250, 12252, 12254, 12256, 12258, 12260, 12262, 12264, + 12266, 12268, 12270, 12272, 12274, 12276, 12278, 12280, 12282, 12284, + 12286, 12288, 12290, 12292, 12294, 12296, 12298, 12300, 12302, 12304, + 12306, 12308, 12310, 12312, 12314, 12316, 12318, 12320, 12322, 12324, + 12326, 12328, 12330, 12332, 12334, 12336, 12338, 12340, 12342, 12344, + 12346, 12348, 12350, 12352, 12354, 12356, 12358, 12360, 12362, 12364, + 12366, 12368, 12370, 12372, 12374, 12376, 12378, 12380, 12382, 12384, + 12386, 12388, 12390, 12392, 12394, 12396, 12398, 12400, 12402, 12404, + 12406, 12408, 12410, 12412, 12414, 12416, 12418, 12420, 12422, 12424, + 12426, 12428, 12430, 12432, 12434, 12436, 12438, 12440, 12442, 12444, + 12446, 12448, 12450, 12452, 12454, 12456, 12458, 12460, 12462, 12464, + 12466, 12468, 12470, 12472, 12474, 12476, 12478, 12480, 12482, 12484, + 12486, 12488, 12490, 12492, 12494, 12496, 12498, 12500, 12502, 12504, + 12506, 12508, 12510, 12512, 12514, 12516, 12518, 12520, 12522, 12524, + 12526, 12528, 12530, 12532, 12534, 12536, 12538, 12540, 12542, 12544, + 12546, 12548, 12550, 12552, 12554, 12556, 12558, 12560, 12562, 12564, + 12566, 12568, 12570, 12572, 12574, 12576, 12578, 12580, 12582, 12584, + 12586, 12588, 12590, 12592, 12594, 12596, 12598, 12600, 12602, 12604, + 12606, 12608, 12610, 12612, 12614, 12616, 12618, 12620, 12622, 12624, + 12626, 12628, 12630, 12632, 12634, 12636, 12638, 12640, 12642, 12644, + 12646, 12648, 12650, 12652, 12654, 12656, 12658, 12660, 12662, 12664, + 12666, 12668, 12670, 12672, 12674, 12676, 12678, 12680, 12682, 12684, + 12686, 12688, 12690, 12692, 12694, 12696, 12698, 12700, 12702, 12704, + 12706, 12708, 12710, 12712, 12714, 12716, 12718, 12720, 12722, 12724, + 12726, 12728, 12730, 12732, 12734, 12736, 12738, 12740, 12742, 12744, + 12746, 12748, 12750, 12752, 12754, 12756, 12758, 12760, 12762, 12764, + 12766, 12768, 12770, 12772, 12774, 12776, 12778, 12780, 12782, 12784, + 12786, 12788, 12790, 12792, 12794, 12796, 12798, 12800, 12802, 12804, + 12806, 12808, 12810, 12812, 12814, 12816, 12818, 12820, 12822, 12824, + 12826, 12828, 12830, 12832, 12834, 12836, 12838, 12840, 12842, 12844, + 12846, 12848, 12850, 12852, 12854, 12856, 12858, 12860, 12862, 12864, + 12866, 12868, 12870, 12872, 12874, 12876, 12878, 12880, 12882, 12884, + 12886, 12888, 12890, 12892, 12894, 12896, 12898, 12900, 12902, 12904, + 12906, 12908, 12910, 12912, 12914, 12916, 12918, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, +}; + +/* NFC pairs */ +#define COMP_SHIFT 3 +static unsigned short comp_index[] = { + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 4, 5, 6, 7, 0, + 0, 0, 0, 8, 9, 10, 0, 0, 0, 11, 12, 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, 0, + 18, 19, 20, 21, 0, 0, 0, 22, 0, 0, 0, 0, 0, 23, 24, 25, 26, 0, 0, 0, 27, + 28, 29, 30, 0, 0, 31, 32, 33, 34, 35, 0, 0, 36, 0, 0, 0, 0, 0, 0, 37, 38, + 39, 40, 0, 0, 41, 0, 42, 43, 44, 0, 0, 45, 46, 47, 0, 0, 0, 0, 48, 49, + 50, 51, 0, 0, 52, 53, 54, 55, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 58, 59, 60, + 61, 0, 0, 62, 63, 64, 65, 0, 0, 0, 66, 67, 68, 69, 0, 0, 70, 71, 72, 73, + 0, 0, 0, 74, 0, 75, 0, 0, 0, 0, 76, 0, 77, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, + 79, 80, 81, 0, 0, 0, 0, 82, 83, 84, 85, 0, 0, 86, 87, 88, 89, 0, 0, 0, + 90, 0, 91, 92, 0, 0, 93, 94, 95, 96, 0, 0, 0, 0, 97, 98, 99, 0, 0, 0, + 100, 101, 102, 103, 0, 0, 0, 104, 0, 0, 0, 0, 0, 105, 106, 107, 0, 0, 0, + 0, 108, 109, 110, 111, 0, 0, 112, 113, 114, 115, 0, 0, 0, 116, 117, 0, 0, + 0, 0, 118, 0, 119, 120, 121, 0, 0, 122, 123, 124, 125, 0, 0, 0, 126, 0, + 127, 0, 0, 0, 128, 129, 130, 131, 0, 0, 0, 132, 133, 134, 135, 0, 0, 0, + 136, 0, 0, 0, 0, 0, 137, 138, 139, 140, 0, 0, 0, 141, 142, 143, 0, 0, 0, + 0, 144, 145, 146, 147, 0, 0, 148, 149, 150, 151, 0, 0, 0, 152, 0, 153, 0, + 0, 0, 154, 155, 156, 0, 0, 0, 0, 0, 157, 0, 0, 0, 0, 158, 159, 160, 161, + 0, 0, 0, 162, 163, 164, 165, 0, 0, 0, 166, 0, 0, 167, 0, 0, 168, 169, 0, + 0, 0, 0, 0, 170, 0, 0, 0, 0, 0, 0, 171, 0, 0, 0, 0, 0, 172, 173, 0, 0, 0, + 0, 0, 174, 0, 0, 0, 0, 0, 175, 176, 0, 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 0, + 178, 179, 0, 0, 0, 0, 180, 181, 0, 0, 0, 0, 0, 182, 0, 0, 0, 0, 0, 0, + 183, 0, 0, 0, 0, 0, 184, 185, 186, 0, 0, 0, 0, 187, 188, 0, 0, 0, 0, 0, + 189, 0, 0, 0, 0, 0, 190, 0, 0, 0, 0, 0, 0, 191, 0, 0, 0, 0, 0, 192, 0, 0, + 0, 0, 0, 0, 193, 194, 0, 0, 0, 0, 0, 195, 0, 0, 0, 0, 0, 196, 197, 0, 0, + 0, 0, 0, 198, 199, 0, 0, 0, 0, 0, 200, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, + 0, 202, 203, 0, 0, 0, 0, 204, 205, 0, 0, 0, 0, 0, 206, 207, 0, 0, 0, 0, + 0, 208, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, 211, + 212, 0, 0, 0, 0, 0, 0, 213, 0, 0, 0, 0, 0, 214, 0, 0, 0, 0, 0, 0, 215, 0, + 0, 0, 0, 0, 0, 216, 0, 0, 0, 0, 0, 217, 0, 0, 0, 0, 0, 218, 0, 0, 0, 0, + 0, 0, 0, 219, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 0, 221, 0, 0, 0, 0, 0, + 222, 223, 224, 0, 0, 0, 225, 226, 227, 0, 0, 0, 0, 228, 229, 230, 0, 0, + 0, 0, 231, 232, 233, 0, 0, 0, 0, 0, 234, 0, 0, 0, 0, 235, 0, 0, 0, 0, 0, + 0, 236, 0, 0, 0, 0, 0, 0, 237, 0, 0, 0, 0, 0, 238, 0, 0, 0, 0, 0, 0, 239, + 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 241, 0, 0, 0, 0, 0, 0, 242, 0, 0, + 0, 0, 0, 0, 243, 0, 0, 0, 0, 244, 245, 246, 0, 247, 0, 0, 248, 0, 249, 0, + 0, 0, 0, 250, 251, 252, 253, 0, 0, 254, 255, 256, 0, 0, 0, 0, 257, 0, + 258, 0, 0, 0, 0, 0, 259, 0, 0, 0, 0, 260, 261, 262, 0, 0, 0, 0, 263, 0, + 264, 265, 0, 0, 0, 0, 0, 0, 266, 0, 0, 0, 0, 0, 0, 267, 0, 0, 268, 269, + 270, 271, 0, 0, 272, 0, 273, 0, 0, 0, 0, 274, 0, 275, 276, 277, 0, 0, + 278, 279, 0, 280, 0, 0, 281, 0, 282, 0, 0, 0, 0, 0, 0, 283, 0, 0, 0, 284, + 285, 286, 0, 287, 0, 0, 288, 0, 289, 0, 290, 0, 0, 291, 0, 0, 292, 0, 0, + 293, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 295, 0, 0, 296, 0, 0, 0, 0, 0, 0, + 297, 0, 0, 0, 0, 0, 298, 299, 0, 0, 0, 0, 0, 300, 0, 0, 0, 0, 0, 301, + 302, 0, 0, 0, 0, 0, 303, 304, 0, 0, 0, 0, 0, 305, 0, 0, 0, 0, 0, 306, + 307, 0, 0, 0, 0, 0, 308, 0, 0, 0, 0, 0, 0, 309, 0, 0, 0, 0, 0, 310, 311, + 0, 0, 0, 0, 0, 312, 0, 0, 0, 0, 0, 0, 313, 0, 0, 0, 0, 0, 0, 314, 0, 0, + 0, 0, 0, 315, 0, 0, 0, 0, 0, 316, 0, 0, 0, 0, 0, 0, 317, 0, 0, 0, 0, 0, + 0, 318, 0, 0, 0, 0, 0, 0, 319, 0, 0, 0, 0, 320, 321, 0, 0, 0, 0, 0, 322, + 0, 0, 0, 0, 0, 0, 0, 323, 0, 0, 0, 0, 0, 324, 325, 0, 0, 0, 0, 0, 326, 0, + 0, 0, 0, 0, 327, 0, 0, 0, 0, 0, 0, 328, 0, 0, 0, 0, 0, 0, 329, 0, 0, 0, + 0, 0, 0, 330, 0, 0, 0, 0, 0, 0, 331, 0, 0, 0, 0, 0, 332, 0, 0, 0, 0, 0, + 333, 0, 0, 0, 0, 0, 0, 334, 0, 0, 0, 0, 0, 335, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 336, 0, 0, 0, 0, 0, 0, 337, 0, 0, 0, 0, 0, 338, 0, 0, 0, 0, 0, 0, 339, + 0, 0, 0, 0, 0, 0, 340, 0, 0, 0, 0, 0, 341, 0, 0, 0, 0, 0, 0, 342, 0, 0, + 0, 0, 0, 0, 343, 0, 0, 0, 0, 0, 344, 0, 0, 0, 0, 0, 0, 345, 0, 0, 0, 0, + 0, 0, 346, 0, 0, 0, 0, 0, 0, 347, 0, 0, 0, 0, 0, 0, 348, 0, 0, 0, 0, 0, + 349, 0, 0, 0, 0, 0, 0, 350, 0, 0, 0, 0, 0, 0, 351, 0, 0, 0, 0, 0, 352, + 353, 0, 0, 0, 0, 0, 354, 0, 0, 0, 0, 0, 0, 355, 0, 0, 0, 0, 0, 0, 356, 0, + 0, 0, 0, 0, 0, 357, 0, 0, 0, 0, 0, 358, 0, 0, 0, 0, 0, 0, 359, 360, 0, 0, + 0, 0, 0, 0, 361, 0, 0, 0, 0, 0, 362, 0, 0, 0, 0, 0, 0, 363, 0, 0, 0, 0, + 0, 0, 364, 0, 0, 0, 0, 0, 365, 0, 0, 0, 0, 0, 0, 366, 0, 0, 0, 0, 0, 367, + 368, 0, 0, 0, 0, 0, 369, 0, 0, 0, 0, 0, 370, 0, 0, 0, 0, 0, 0, 371, 0, 0, + 0, 0, 0, 0, 372, 0, 0, 0, 0, 0, 373, 0, 0, 0, 374, 0, 0, 375, 0, 0, 376, + 0, 0, 0, 0, 0, 0, 377, 0, 0, 0, 0, 0, 0, 378, 0, 0, 0, 0, 0, 379, 0, 0, + 0, 0, 0, 0, 380, 0, 0, 0, 0, 0, 381, 0, 0, 0, 0, 0, 0, 382, 0, 0, 383, 0, + 0, 0, 384, 0, 0, 385, 0, 0, 386, 0, 0, 0, 0, 0, 0, 387, 0, 0, 0, 0, 0, 0, + 388, 0, 0, 0, 0, 0, 389, 0, 0, 0, 0, 0, 0, 390, 0, 0, 0, 0, 0, 391, 0, 0, + 0, 0, 0, 0, 392, 0, 0, 393, 0, 0, 0, 0, 0, 0, 394, 0, 0, 0, 0, 0, 395, 0, + 0, 0, 0, 0, 0, 396, 0, 0, 0, 0, 0, 0, 397, 0, 0, 398, 0, 0, 399, 0, 0, 0, + 400, 0, 0, 0, 0, 0, 401, 0, 0, 0, 0, 0, 0, 402, 0, 0, 0, 0, 0, 0, 403, 0, + 0, 0, 0, 0, 404, 0, 0, 0, 0, 0, 0, 405, 0, 0, 0, 0, 0, 0, 406, 0, 0, 407, + 0, 0, 408, 0, 0, 409, 0, 0, 0, 410, 0, 0, 0, 0, 0, 411, 0, 0, 0, 0, 0, 0, + 412, 0, 0, 0, 0, 0, 0, 413, 0, 0, 0, 0, 0, 414, 0, 0, 0, 0, 0, 0, 415, 0, + 0, 0, 0, 0, 0, 416, 0, 0, 417, 0, 0, 418, 0, 0, 419, 0, 0, 0, 420, 0, 0, + 421, 0, 0, 422, 0, 0, 423, 424, 0, 0, 425, 0, 0, 426, 0, 0, 0, 0, 0, 0, + 427, 0, 0, 0, 0, 0, 428, 0, 0, 0, 0, 0, 0, 429, 0, 0, 0, 0, 0, 0, 430, 0, + 0, 431, 0, 0, 432, 0, 0, 0, 433, 0, 0, 434, 0, 0, 435, 0, 0, 436, 437, 0, + 0, 438, 0, 0, 439, 0, 0, 0, 440, 0, 0, 0, 0, 0, 441, 0, 0, 0, 0, 0, 0, + 442, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 0, 0, 444, 0, 0, 0, 0, 0, 0, 445, 0, + 0, 0, 0, 0, 446, 0, 0, 447, 448, 0, 0, 449, 0, 0, 450, 0, 0, 0, 451, 0, + 0, 0, 0, 0, 452, 0, 0, 0, 0, 0, 0, 453, 0, 0, 0, 0, 0, 0, 454, 0, 0, 0, + 0, 0, 455, 0, 0, 0, 0, 0, 0, 456, 0, 0, 0, 0, 0, 457, 0, 0, 0, 0, 0, 0, + 458, 0, 0, 0, 0, 0, 0, 459, 0, 0, 0, 0, 0, 460, 0, 0, 0, 0, 0, 0, 461, 0, + 0, 462, 0, 0, 463, 0, 0, 0, 0, 0, 0, 464, 0, 0, 0, 0, 0, 0, 465, 0, 0, + 466, 0, 0, 467, 0, 0, 0, 0, 0, 0, 468, 0, 0, 0, 0, 0, 469, 0, 0, 0, 0, 0, + 0, 470, 0, 0, 0, 0, 0, 0, 471, 0, 0, 0, 0, 0, 472, 0, 0, 0, 0, 0, 0, 473, + 0, 0, 0, 0, 0, 0, 474, 0, 0, 0, 0, 0, 475, 0, 0, 0, 0, 0, 0, 476, 0, 0, + 0, 0, 0, 477, 0, 0, 0, 0, 0, 0, 478, 0, 0, 0, 0, 0, 0, 479, 0, 0, 0, 0, + 0, 480, 0, 0, 0, 0, 0, 0, 481, 0, 0, 0, 0, 0, 0, 482, 0, 0, 0, 0, 0, 483, + 0, 0, 0, 0, 0, 0, 484, 0, 0, 0, 0, 0, 485, 0, 0, 0, 0, 0, 0, 486, 0, 0, + 0, 0, 0, 0, 487, 0, 0, 0, 0, 0, 488, 0, 0, 0, 0, 0, 0, 489, 0, 0, 0, 0, + 0, 0, 490, 0, 0, 0, 0, 0, 491, 0, 0, 0, 0, 0, 0, 492, 0, 0, 0, 0, 0, 493, + 0, 0, 0, 0, 0, 0, 494, 0, 0, 0, 0, 0, 0, 495, 0, 0, 0, 0, 0, 496, 0, 0, + 0, 0, 0, 0, 497, 0, 0, 0, 0, 0, 0, 498, 0, 0, 0, 0, 0, 499, 0, 0, 0, 0, + 0, 0, 500, 0, 0, 0, 0, 0, 501, 0, 0, 0, 0, 0, 0, 502, 0, 0, 0, 0, 0, 0, + 503, 0, 0, 0, 0, 0, 504, 0, 0, 0, 0, 0, 0, 505, 0, 0, 0, 0, 0, 0, 506, 0, + 0, 0, 0, 0, 507, 0, 0, 0, 0, 0, 0, 508, 0, 0, 0, 0, 0, 0, 0, 0, 509, 0, + 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, 0, 511, 0, 0, 0, 0, 0, 512, 0, 0, 0, + 0, 0, 0, 513, 0, 0, 0, 0, 0, 0, 514, 0, 0, 0, 0, 0, 515, 0, 0, 0, 0, 0, + 0, 516, 0, 0, 0, 0, 0, 517, 0, 0, 0, 0, 0, 0, 518, 0, 0, 0, 0, 0, 0, 519, + 0, 0, 0, 0, 0, 520, 0, 0, 0, 0, 0, 0, 521, 0, 0, 0, 0, 0, 0, 522, 0, 0, + 0, 0, 0, 523, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0, 0, 525, 526, 0, 0, 0, 0, + 0, 527, 0, 0, 0, 0, 0, 0, 528, 0, 0, 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, 530, + 0, 0, 0, 0, 0, 0, 531, 0, 0, 0, 0, 0, 532, 0, 0, 0, 0, 0, 0, 533, 0, 0, + 0, 0, 0, 534, 0, 0, 0, 0, 0, 0, 535, 0, 0, 0, 0, 0, 0, 536, 0, 0, 0, 0, + 0, 537, 0, 0, 0, 0, 0, 0, 538, 0, 0, 0, 0, 0, 0, 539, 0, 0, 0, 0, 0, 540, + 0, 0, 0, 0, 0, 0, 541, 0, 0, 0, 0, 0, 542, 0, 0, 0, 0, 0, 0, 543, 0, 0, + 0, 0, 0, 0, 544, 0, 0, 0, 0, 0, 545, 0, 0, 0, 0, 0, 0, 546, 0, 0, 0, 0, + 0, 0, 547, 0, 0, 0, 0, 0, 548, 0, 0, 0, 0, 0, 0, 549, 0, 0, 0, 0, 0, 550, + 551, 0, 0, 0, 0, 0, 552, 0, 0, 0, 0, 0, 0, 553, 0, 0, 0, 0, 0, 554, 0, 0, + 0, 0, 0, 0, 555, 0, 0, 0, 0, 0, 0, 556, 0, 0, 0, 0, 0, 557, 0, 0, 0, 0, + 0, 0, 558, +}; + +static unsigned short comp_data[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8814, 0, 0, 0, 0, 8800, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 8815, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 193, 194, 195, + 256, 258, 550, 196, 7842, 197, 0, 461, 512, 514, 0, 0, 0, 7840, 0, 7680, + 0, 0, 260, 0, 0, 0, 0, 7682, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7684, 0, 0, 0, + 0, 0, 0, 0, 0, 7686, 0, 0, 0, 262, 264, 0, 0, 0, 266, 0, 0, 0, 0, 268, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 199, 0, 0, 0, 0, 0, 7690, 0, 0, 0, 0, 270, 0, 0, + 0, 0, 0, 7692, 0, 0, 0, 7696, 0, 7698, 0, 0, 7694, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 200, 201, 202, 7868, 274, 276, 278, 203, 7866, 0, 0, 282, 516, + 518, 0, 0, 0, 7864, 0, 0, 0, 552, 280, 7704, 0, 7706, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7710, 0, 0, 0, 0, 0, 0, 0, 500, 284, 0, 7712, 286, 288, 0, 0, 0, + 0, 486, 0, 0, 0, 0, 0, 0, 0, 0, 0, 290, 0, 0, 0, 0, 0, 0, 0, 0, 0, 292, + 0, 0, 0, 7714, 7718, 0, 0, 0, 542, 0, 0, 0, 0, 0, 7716, 0, 0, 0, 7720, 0, + 0, 7722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 205, 206, 296, 298, + 300, 304, 207, 7880, 0, 0, 463, 520, 522, 0, 0, 0, 7882, 0, 0, 0, 0, 302, + 0, 0, 7724, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 308, 0, 0, 0, 7728, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 488, 0, 0, 0, 0, 0, 7730, 0, 0, 0, 310, 0, 0, 0, + 0, 7732, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 313, 0, 317, 0, 0, 0, 0, 0, + 7734, 0, 0, 0, 315, 0, 7740, 0, 0, 7738, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7742, 0, 0, 0, 0, 7744, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7746, 0, 0, 0, 504, + 323, 0, 209, 0, 0, 7748, 0, 0, 0, 0, 327, 0, 0, 0, 0, 0, 7750, 0, 0, 0, + 325, 0, 7754, 0, 0, 7752, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 211, 212, + 213, 332, 334, 558, 214, 7886, 0, 336, 465, 524, 526, 0, 0, 416, 7884, 0, + 0, 0, 0, 490, 0, 0, 0, 0, 0, 0, 0, 7764, 0, 0, 0, 0, 7766, 0, 0, 0, 0, 0, + 0, 0, 340, 0, 0, 0, 0, 7768, 0, 0, 0, 0, 344, 528, 530, 0, 0, 0, 7770, 0, + 0, 0, 342, 0, 0, 0, 0, 7774, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 346, 348, + 0, 0, 0, 7776, 0, 0, 0, 0, 352, 0, 0, 0, 0, 0, 7778, 0, 0, 536, 350, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7786, 0, 0, 0, 0, 356, 0, 0, 0, 0, 0, + 7788, 0, 0, 538, 354, 0, 7792, 0, 0, 7790, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217, 218, 219, 360, 362, 364, 0, 220, 7910, 366, 368, 467, 532, 534, 0, + 0, 431, 7908, 7794, 0, 0, 0, 370, 7798, 0, 7796, 0, 0, 0, 0, 0, 0, 7804, + 0, 0, 0, 0, 0, 7806, 0, 0, 0, 7808, 7810, 372, 0, 0, 0, 7814, 7812, 0, + 7816, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7818, 7820, 0, 0, 0, 0, 0, 7922, 221, + 374, 7928, 562, 0, 7822, 376, 7926, 0, 0, 0, 0, 0, 0, 0, 0, 7924, 0, 0, + 0, 0, 377, 7824, 0, 0, 0, 379, 0, 0, 0, 0, 381, 0, 0, 0, 0, 0, 7826, 0, + 0, 0, 0, 0, 0, 0, 0, 7828, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 225, 226, + 227, 257, 259, 551, 228, 7843, 229, 0, 462, 513, 515, 0, 0, 0, 7841, 0, + 7681, 0, 0, 261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7683, 0, 0, 7685, 0, + 0, 0, 0, 0, 0, 0, 0, 7687, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 263, 265, 0, + 0, 0, 267, 0, 0, 0, 0, 269, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, 0, 0, 0, 0, + 0, 7691, 0, 0, 0, 0, 271, 0, 0, 0, 0, 0, 7693, 0, 0, 0, 7697, 0, 7699, 0, + 0, 7695, 0, 0, 232, 233, 234, 7869, 275, 277, 279, 235, 7867, 0, 0, 283, + 517, 519, 0, 0, 0, 7865, 0, 0, 0, 553, 281, 7705, 0, 7707, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7711, 0, 0, 0, 0, 0, 0, 0, 501, 285, 0, 7713, 287, 289, 0, 0, + 0, 0, 487, 0, 0, 0, 0, 0, 0, 0, 0, 0, 291, 0, 293, 0, 0, 0, 7715, 7719, + 0, 0, 0, 543, 0, 0, 0, 0, 0, 7717, 0, 0, 0, 7721, 0, 0, 7723, 0, 7830, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 236, 237, 238, 297, 299, 301, 0, 239, 7881, 0, + 0, 464, 521, 523, 0, 0, 0, 7883, 0, 0, 0, 0, 303, 0, 0, 7725, 0, 0, 0, 0, + 0, 309, 0, 0, 0, 0, 0, 0, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7729, + 0, 489, 0, 0, 0, 0, 0, 7731, 0, 0, 0, 311, 0, 0, 0, 0, 7733, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 314, 0, 0, 0, 0, 0, 0, 0, 0, 0, 318, 0, 0, 0, 0, 0, + 7735, 0, 0, 0, 316, 0, 7741, 0, 0, 7739, 0, 0, 0, 7743, 0, 0, 0, 0, 7745, + 0, 0, 7747, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 505, 324, 0, 241, 0, 0, + 7749, 0, 0, 0, 0, 328, 0, 0, 0, 0, 0, 7751, 0, 0, 0, 326, 0, 7755, 0, 0, + 7753, 0, 0, 242, 243, 244, 245, 333, 335, 559, 246, 7887, 0, 337, 466, + 525, 527, 0, 0, 417, 7885, 0, 0, 0, 0, 491, 0, 0, 0, 0, 0, 0, 0, 7765, 0, + 0, 0, 0, 7767, 0, 0, 0, 0, 0, 0, 0, 341, 0, 0, 0, 0, 7769, 0, 0, 0, 0, + 345, 529, 531, 0, 0, 0, 7771, 0, 0, 0, 343, 0, 0, 0, 0, 7775, 0, 0, 0, + 347, 349, 0, 0, 0, 7777, 0, 0, 0, 0, 353, 0, 0, 0, 0, 0, 7779, 0, 0, 537, + 351, 0, 0, 0, 0, 0, 7787, 7831, 0, 0, 0, 357, 0, 0, 0, 0, 0, 7789, 0, 0, + 539, 355, 0, 7793, 0, 0, 7791, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 249, 250, + 251, 361, 363, 365, 0, 252, 7911, 367, 369, 468, 533, 535, 0, 0, 432, + 7909, 7795, 0, 0, 0, 371, 7799, 0, 7797, 0, 0, 0, 0, 0, 0, 7805, 0, 0, 0, + 0, 0, 7807, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7809, 7811, 373, 0, 0, 0, + 7815, 7813, 0, 7832, 0, 0, 0, 0, 0, 0, 0, 7817, 0, 7819, 7821, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7923, 253, 375, 7929, 563, 0, 7823, 255, + 7927, 7833, 0, 0, 0, 0, 0, 0, 0, 7925, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 378, 7825, 0, 0, 0, 380, 0, 0, 0, 0, 382, 0, 0, 0, 0, 0, 7827, 0, 0, + 0, 0, 0, 0, 0, 0, 7829, 0, 0, 8173, 901, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8129, 0, 0, 0, 0, 0, 0, 0, 0, 7846, 7844, 0, 7850, 0, 0, 0, 0, 7848, 0, + 0, 0, 0, 0, 0, 0, 0, 478, 0, 506, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 508, 0, 0, 482, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7688, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7872, 7870, 0, 7876, 0, 0, 0, 0, 7874, 0, 0, 0, 0, 0, 7726, 0, + 0, 0, 7890, 7888, 0, 7894, 0, 0, 0, 0, 7892, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7756, 0, 0, 556, 0, 0, 7758, 0, 0, 0, 0, 0, 0, 0, 0, 0, 554, + 0, 510, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 475, 471, 0, 0, 469, 0, 0, 0, 0, + 0, 0, 473, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7847, 7845, 0, 7851, 0, 0, 0, 0, + 7849, 0, 0, 0, 0, 0, 0, 0, 0, 479, 0, 0, 0, 0, 0, 0, 0, 0, 0, 507, 0, 0, + 0, 0, 509, 0, 0, 483, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7689, 0, 0, 0, 7873, + 7871, 0, 7877, 0, 0, 0, 0, 7875, 0, 0, 0, 0, 0, 7727, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7891, 7889, 0, 7895, 0, 0, 0, 0, 7893, 0, 0, 0, 0, 0, + 7757, 0, 0, 557, 0, 0, 7759, 0, 0, 0, 0, 0, 0, 0, 0, 0, 555, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 511, 0, 0, 0, 476, 472, 0, 0, 470, 0, 0, 0, 0, 0, 0, 474, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 7856, 7854, 0, 7860, 0, 0, 0, 0, 7858, 0, 0, + 0, 0, 7857, 7855, 0, 7861, 0, 0, 0, 0, 7859, 0, 0, 0, 0, 7700, 7702, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7701, 7703, 0, 0, 0, 7760, 7762, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 7761, 7763, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7780, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7781, 0, 0, 0, 0, 7782, 0, 0, 0, 0, + 7783, 0, 0, 0, 0, 0, 0, 0, 7800, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7801, 0, 0, 7802, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7803, 0, 0, 0, + 7835, 0, 0, 0, 0, 0, 0, 7900, 7898, 0, 7904, 0, 0, 0, 0, 7902, 0, 0, 0, + 0, 0, 0, 0, 0, 7906, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7901, 7899, 0, + 7905, 0, 0, 0, 0, 7903, 0, 0, 0, 0, 0, 0, 0, 0, 7907, 0, 0, 0, 7914, + 7912, 0, 7918, 0, 0, 0, 0, 7916, 0, 0, 0, 0, 0, 0, 0, 0, 7920, 0, 0, 0, + 7915, 7913, 0, 7919, 0, 0, 0, 0, 7917, 0, 0, 0, 0, 0, 0, 0, 0, 7921, 0, + 0, 0, 0, 0, 0, 494, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 492, 0, 0, 0, + 0, 493, 0, 0, 0, 0, 480, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 481, 0, 0, + 0, 0, 0, 7708, 0, 0, 0, 0, 7709, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 560, 0, + 0, 0, 0, 561, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 495, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8122, 902, 0, 0, 8121, 8120, 0, 0, 0, 0, 0, 0, 0, 0, 7944, 7945, 0, + 0, 0, 0, 0, 8124, 0, 0, 0, 0, 0, 0, 0, 8136, 904, 0, 0, 0, 0, 7960, 7961, + 0, 0, 0, 0, 0, 8138, 905, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7976, 7977, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8140, 0, 0, 0, 0, 0, 0, 0, 8154, + 906, 0, 0, 8153, 8152, 0, 938, 0, 0, 0, 0, 0, 0, 7992, 7993, 0, 0, 0, 0, + 0, 8184, 908, 0, 0, 0, 0, 8008, 8009, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8172, 0, 0, 0, 0, 0, 8170, 910, 0, 0, 8169, 8168, 0, 939, 0, 0, 0, 0, 0, + 0, 0, 8025, 0, 0, 0, 0, 0, 8186, 911, 0, 0, 0, 0, 8040, 8041, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8188, 0, 0, 0, 0, 8116, 0, 0, 0, 0, 8132, 0, + 0, 0, 0, 0, 0, 0, 8048, 940, 0, 0, 8113, 8112, 0, 0, 0, 0, 0, 0, 0, 0, + 7936, 7937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8118, 8115, 0, 0, 0, 0, + 0, 0, 0, 8050, 941, 0, 0, 0, 0, 7952, 7953, 0, 0, 0, 0, 0, 8052, 942, 0, + 0, 0, 0, 7968, 7969, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8134, 8131, 0, + 0, 0, 0, 0, 0, 0, 8054, 943, 0, 0, 8145, 8144, 0, 970, 0, 0, 0, 0, 0, 0, + 7984, 7985, 0, 0, 0, 0, 8150, 0, 0, 0, 0, 0, 0, 0, 0, 8056, 972, 0, 0, 0, + 0, 8000, 8001, 0, 0, 0, 8164, 8165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 8058, 973, 0, 0, 8161, 8160, 0, 971, 0, 0, 0, 0, 0, 0, 8016, 8017, 0, + 0, 0, 0, 8166, 0, 0, 0, 0, 0, 0, 0, 0, 8060, 974, 0, 0, 0, 0, 8032, 8033, + 0, 0, 0, 0, 8182, 8179, 0, 0, 0, 0, 0, 0, 0, 8146, 912, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8151, 0, 0, 0, 0, 0, 0, 0, 0, 8162, 944, 0, 0, 8167, 0, 0, 0, + 0, 0, 8180, 0, 0, 0, 0, 0, 0, 0, 0, 979, 0, 0, 0, 0, 0, 980, 0, 0, 0, 0, + 1031, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1232, 0, 1234, 0, 0, 0, 0, 0, 0, + 1027, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1024, 0, 0, 0, 0, 1238, 0, 1025, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1217, 0, 1244, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1246, 0, 0, 0, 0, 0, 1037, 0, 0, 0, 1250, 1049, 0, 1252, 0, 0, + 0, 0, 0, 0, 1036, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1254, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1262, 1038, 0, 1264, 0, 0, 1266, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1268, 0, 0, 0, 0, 1272, 0, 0, 0, 0, 1260, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1233, 0, 1235, 0, 0, 0, 0, 0, 0, 1107, 0, 0, 0, 1104, 0, 0, 0, 0, 1239, + 0, 1105, 0, 0, 1218, 0, 1245, 0, 0, 0, 0, 1247, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1117, 0, 0, 0, 1251, 1081, 0, 1253, 0, 0, 0, 0, 0, 0, + 1116, 0, 0, 1255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1263, 1118, 0, 1265, 0, 0, + 1267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1269, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1273, 0, 0, 0, 0, 1261, 0, 0, 0, 0, 1111, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1142, 0, 0, 0, 0, 1143, 0, 0, 0, 0, 0, 0, 0, 1242, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1243, 0, 0, 0, 0, 1258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1259, 0, 0, 0, 1570, 1571, 1573, 0, 0, 0, 1572, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1574, 0, 0, 0, 0, 1730, 0, 0, 0, 0, 1747, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1728, 0, 0, 0, 0, 0, 0, 2345, 0, 0, 0, 0, 2353, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2356, 0, 0, 0, 0, 0, 2507, 2508, 0, 0, + 0, 0, 0, 2891, 2888, 2892, 0, 0, 0, 0, 0, 0, 2964, 0, 0, 0, 3018, 3020, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3019, 0, 0, 0, 0, 0, 0, 3144, 0, 0, 0, + 0, 0, 0, 3264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3274, 3271, 3272, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3275, 0, 0, 0, 0, 0, 0, 3402, 3404, 0, 0, 0, + 3403, 0, 0, 0, 0, 0, 0, 3546, 3548, 3550, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3549, 0, 0, 0, 0, 0, 0, 0, 4134, 0, 0, 0, 0, 0, 0, 7736, 0, 0, 0, 0, + 7737, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7772, 0, 0, 0, 0, 7773, 0, 0, + 0, 0, 0, 0, 7784, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7785, 7852, 0, 0, + 7862, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7853, 0, 0, 7863, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7878, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7879, 0, 0, 0, 0, 7896, + 0, 0, 0, 0, 7897, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7938, 7940, 0, 0, 7942, + 8064, 0, 0, 0, 0, 0, 0, 0, 7939, 7941, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7943, 8065, 0, 0, 0, 0, 8066, 0, 0, 0, 0, 8067, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8068, 0, 0, 0, 0, 8069, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8070, 0, 0, 0, 0, 8071, 0, 0, 0, 0, 0, 0, 0, 7946, 7948, 0, 0, 7950, + 8072, 0, 0, 0, 0, 0, 0, 0, 7947, 7949, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7951, 8073, 0, 0, 0, 0, 8074, 0, 0, 0, 0, 8075, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8076, 0, 0, 0, 0, 8077, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8078, 0, 0, 0, 0, 8079, 0, 0, 0, 0, 0, 0, 0, 7954, 7956, 0, 0, 0, 7955, + 7957, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7962, 7964, 0, 0, 0, 7963, 7965, + 0, 0, 0, 7970, 7972, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7974, 8080, 0, 0, 0, + 0, 0, 0, 0, 7971, 7973, 0, 0, 7975, 8081, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8082, 0, 0, 0, 0, 8083, 0, 0, 0, 0, 8084, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8085, 0, 0, 0, 0, 8086, 0, 0, 0, 0, 8087, 0, 0, 0, 0, 0, 0, + 0, 7978, 7980, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7982, 8088, 0, 0, 0, 0, 0, + 0, 0, 7979, 7981, 0, 0, 7983, 8089, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8090, 0, 0, 0, 0, 8091, 0, 0, 0, 0, 8092, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8093, 0, 0, 0, 0, 8094, 0, 0, 0, 0, 8095, 0, 0, 0, 0, 0, 0, 0, + 7986, 7988, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7990, 0, 0, 0, 0, 0, 0, 0, 0, + 7987, 7989, 0, 0, 7991, 0, 0, 0, 0, 0, 0, 0, 0, 7994, 7996, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 7998, 0, 0, 0, 0, 0, 0, 0, 0, 7995, 7997, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 7999, 0, 0, 0, 0, 0, 0, 0, 0, 8002, 8004, 0, 0, 0, + 8003, 8005, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8010, 8012, 0, 0, 0, 8011, + 8013, 0, 0, 0, 8018, 8020, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8022, 0, 0, 0, + 0, 0, 0, 0, 0, 8019, 8021, 0, 0, 8023, 0, 0, 0, 0, 0, 0, 0, 0, 8027, + 8029, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8031, 0, 0, 0, 0, 0, 0, 0, 0, 8034, + 8036, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8038, 8096, 0, 0, 0, 0, 0, 0, 0, + 8035, 8037, 0, 0, 8039, 8097, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8098, + 0, 0, 0, 0, 8099, 0, 0, 0, 0, 8100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8101, 0, 0, 0, 0, 8102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8103, 0, 0, + 0, 0, 0, 0, 0, 8042, 8044, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8046, 8104, 0, + 0, 0, 0, 0, 0, 0, 8043, 8045, 0, 0, 8047, 8105, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8106, 0, 0, 0, 0, 8107, 0, 0, 0, 0, 8108, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 8109, 0, 0, 0, 0, 8110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8111, 0, 0, 0, 0, 8114, 0, 0, 0, 0, 8130, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8178, 0, 0, 0, 0, 8119, 0, 0, 0, 0, 0, 0, 0, 8141, 8142, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8143, 0, 0, 0, 0, 0, 8135, 0, 0, 0, 0, 8183, + 0, 0, 0, 0, 0, 0, 0, 8157, 8158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8159, 0, + 0, 0, 8602, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8603, 0, 0, 0, 0, 8622, + 0, 0, 0, 0, 8653, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8655, 0, 0, 0, 0, + 8654, 0, 0, 0, 0, 8708, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8713, 0, 0, + 0, 0, 8716, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8740, 0, 0, 0, 0, 8742, + 0, 0, 0, 0, 8769, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8772, 0, 0, 0, 0, + 8775, 0, 0, 0, 0, 8777, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8813, 0, 0, + 0, 0, 8802, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8816, 0, 0, 0, 0, 8817, + 0, 0, 0, 0, 8820, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8821, 0, 0, 0, 0, + 8824, 0, 0, 0, 0, 8825, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8832, 0, 0, + 0, 0, 8833, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8928, 0, 0, 0, 0, 8929, + 0, 0, 0, 0, 8836, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8837, 0, 0, 0, 0, + 8840, 0, 0, 0, 0, 8841, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8930, 0, 0, + 0, 0, 8931, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8876, 0, 0, 0, 0, 8877, + 0, 0, 0, 0, 8878, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8879, 0, 0, 0, 0, + 8938, 0, 0, 0, 0, 8939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8940, 0, 0, + 0, 0, 8941, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12436, 0, 0, 0, 0, 12364, + 0, 0, 0, 0, 12366, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12368, 0, 0, 0, 0, + 12370, 0, 0, 0, 0, 12372, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12374, 0, + 0, 0, 0, 12376, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12378, 0, 0, 0, 0, + 12380, 0, 0, 0, 0, 12382, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12384, 0, + 0, 0, 0, 12386, 0, 0, 0, 0, 12389, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12391, 0, 0, 0, 0, 12393, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12400, + 12401, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12403, 12404, 0, 0, 0, 12406, + 12407, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12409, 12410, 0, 0, 0, 12412, + 12413, 0, 0, 0, 12446, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12532, 0, 0, + 0, 0, 12460, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12462, 0, 0, 0, 0, + 12464, 0, 0, 0, 0, 12466, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12468, 0, + 0, 0, 0, 12470, 0, 0, 0, 0, 12472, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12474, 0, 0, 0, 0, 12476, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12478, 0, + 0, 0, 0, 12480, 0, 0, 0, 0, 12482, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12485, 0, 0, 0, 0, 12487, 0, 0, 0, 0, 12489, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 12496, 12497, 0, 0, 0, 12499, 12500, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 12502, 12503, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12505, 12506, 0, 0, + 0, 12508, 12509, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12535, 0, 0, 0, 0, + 12536, 0, 0, 0, 0, 12537, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12538, 0, + 0, 0, 0, 12542, 0, +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodename_db.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodename_db.h new file mode 100644 index 00000000..1ec6df96 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/unicodename_db.h @@ -0,0 +1,10289 @@ +/* this file was generated by Tools/unicode/makeunicodedata.py 2.2 */ + +#define NAME_MAXLEN 256 + +/* lexicon */ +static unsigned char lexicon[] = { + 76, 69, 84, 84, 69, 210, 87, 73, 84, 200, 83, 77, 65, 76, 204, 83, 89, + 76, 76, 65, 66, 76, 197, 67, 65, 80, 73, 84, 65, 204, 89, 201, 67, 74, + 203, 77, 65, 84, 72, 69, 77, 65, 84, 73, 67, 65, 204, 76, 65, 84, 73, + 206, 65, 82, 65, 66, 73, 195, 67, 79, 77, 80, 65, 84, 73, 66, 73, 76, 73, + 84, 217, 70, 79, 82, 77, 128, 83, 89, 77, 66, 79, 204, 67, 65, 78, 65, + 68, 73, 65, 206, 83, 89, 76, 76, 65, 66, 73, 67, 211, 66, 79, 76, 196, + 76, 73, 71, 65, 84, 85, 82, 197, 65, 78, 196, 77, 85, 83, 73, 67, 65, + 204, 72, 65, 78, 71, 85, 204, 73, 84, 65, 76, 73, 195, 82, 65, 68, 73, + 67, 65, 204, 83, 65, 78, 83, 45, 83, 69, 82, 73, 198, 69, 84, 72, 73, 79, + 80, 73, 195, 83, 73, 71, 206, 71, 82, 69, 69, 203, 68, 73, 71, 73, 212, + 67, 73, 82, 67, 76, 69, 196, 70, 73, 78, 65, 204, 83, 81, 85, 65, 82, + 197, 67, 89, 82, 73, 76, 76, 73, 195, 66, 82, 65, 73, 76, 76, 197, 80, + 65, 84, 84, 69, 82, 206, 66, 89, 90, 65, 78, 84, 73, 78, 197, 73, 83, 79, + 76, 65, 84, 69, 196, 76, 69, 70, 212, 82, 73, 71, 72, 212, 86, 79, 87, + 69, 204, 75, 65, 84, 65, 75, 65, 78, 193, 75, 65, 78, 71, 88, 201, 84, + 73, 66, 69, 84, 65, 206, 68, 79, 85, 66, 76, 197, 77, 69, 69, 205, 67, + 65, 82, 82, 73, 69, 210, 66, 69, 76, 79, 87, 128, 73, 78, 73, 84, 73, 65, + 204, 65, 66, 79, 86, 69, 128, 67, 79, 77, 66, 73, 78, 73, 78, 199, 68, + 79, 212, 89, 69, 200, 77, 79, 78, 71, 79, 76, 73, 65, 206, 65, 82, 82, + 79, 87, 128, 65, 66, 79, 86, 197, 70, 79, 210, 86, 69, 82, 84, 73, 67, + 65, 204, 66, 79, 216, 83, 73, 71, 78, 128, 87, 72, 73, 84, 197, 65, 82, + 82, 79, 215, 68, 82, 65, 87, 73, 78, 71, 211, 72, 69, 66, 82, 69, 215, + 72, 65, 76, 70, 87, 73, 68, 84, 200, 82, 73, 71, 72, 84, 87, 65, 82, 68, + 211, 65, 128, 77, 65, 82, 75, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 73, 195, 65, 76, 69, 198, 80, 65, 82, 69, 78, 84, 72, 69, 83, 73, 90, 69, + 196, 73, 128, 83, 67, 82, 73, 80, 212, 68, 69, 86, 65, 78, 65, 71, 65, + 82, 201, 70, 85, 76, 76, 87, 73, 68, 84, 200, 75, 72, 77, 69, 210, 85, + 208, 84, 79, 128, 70, 82, 65, 75, 84, 85, 210, 68, 79, 87, 206, 69, 81, + 85, 65, 204, 72, 65, 200, 72, 69, 65, 86, 217, 78, 85, 77, 66, 69, 210, + 85, 128, 84, 65, 199, 66, 76, 65, 67, 203, 65, 82, 77, 69, 78, 73, 65, + 206, 67, 72, 79, 83, 69, 79, 78, 199, 74, 69, 69, 205, 83, 89, 77, 66, + 79, 76, 128, 66, 69, 78, 71, 65, 76, 201, 67, 72, 65, 82, 65, 67, 84, 69, + 210, 72, 73, 82, 65, 71, 65, 78, 193, 87, 69, 83, 84, 45, 67, 82, 69, + 197, 84, 72, 65, 201, 67, 72, 69, 82, 79, 75, 69, 197, 73, 68, 69, 79, + 71, 82, 65, 80, 200, 77, 69, 68, 73, 65, 204, 74, 79, 78, 71, 83, 69, 79, + 78, 199, 82, 85, 78, 73, 195, 71, 69, 79, 82, 71, 73, 65, 206, 75, 65, + 78, 78, 65, 68, 193, 83, 73, 78, 72, 65, 76, 193, 84, 69, 76, 85, 71, + 213, 79, 82, 73, 89, 193, 71, 85, 74, 65, 82, 65, 84, 201, 77, 65, 76, + 65, 89, 65, 76, 65, 205, 77, 89, 65, 78, 77, 65, 210, 207, 66, 82, 65, + 67, 75, 69, 84, 128, 68, 69, 83, 69, 82, 69, 212, 71, 85, 82, 77, 85, 75, + 72, 201, 84, 87, 79, 128, 65, 67, 85, 84, 69, 128, 76, 73, 71, 72, 212, + 83, 89, 82, 73, 65, 195, 68, 79, 85, 66, 76, 69, 45, 83, 84, 82, 85, 67, + 203, 79, 78, 69, 128, 83, 84, 82, 79, 75, 69, 128, 65, 80, 204, 70, 85, + 78, 67, 84, 73, 79, 78, 65, 204, 72, 65, 77, 90, 193, 76, 69, 70, 84, 87, + 65, 82, 68, 211, 84, 69, 76, 69, 71, 82, 65, 80, 200, 72, 79, 79, 75, + 128, 74, 85, 78, 71, 83, 69, 79, 78, 199, 68, 65, 83, 73, 193, 77, 65, + 75, 83, 85, 82, 193, 76, 65, 207, 66, 65, 82, 194, 66, 79, 80, 79, 77, + 79, 70, 207, 82, 128, 72, 65, 76, 198, 79, 198, 80, 83, 73, 76, 201, 84, + 207, 77, 79, 78, 79, 83, 80, 65, 67, 197, 78, 79, 212, 84, 65, 77, 73, + 204, 66, 65, 82, 128, 75, 72, 65, 200, 72, 79, 82, 73, 90, 79, 78, 84, + 65, 204, 77, 79, 68, 73, 70, 73, 69, 210, 76, 79, 87, 69, 210, 68, 73, + 65, 69, 82, 69, 83, 73, 83, 128, 78, 85, 77, 69, 82, 65, 204, 86, 79, 67, + 65, 76, 73, 195, 84, 72, 82, 69, 69, 128, 72, 65, 82, 80, 79, 79, 206, + 85, 80, 80, 69, 210, 67, 73, 82, 67, 85, 77, 70, 76, 69, 216, 71, 82, 65, + 86, 69, 128, 65, 78, 71, 76, 197, 84, 72, 65, 65, 78, 193, 65, 76, 80, + 72, 193, 70, 79, 85, 82, 128, 72, 128, 76, 79, 78, 199, 77, 65, 67, 82, + 79, 78, 128, 77, 65, 82, 203, 70, 73, 86, 69, 128, 83, 73, 88, 128, 79, + 77, 69, 71, 193, 79, 88, 73, 65, 128, 86, 65, 82, 73, 65, 128, 67, 73, + 82, 67, 76, 197, 69, 73, 71, 72, 84, 128, 78, 79, 79, 206, 78, 73, 78, + 69, 128, 83, 69, 86, 69, 78, 128, 84, 73, 76, 68, 69, 128, 89, 128, 69, + 84, 193, 82, 73, 71, 72, 84, 128, 83, 85, 66, 74, 79, 73, 78, 69, 196, + 86, 128, 68, 128, 72, 69, 200, 83, 84, 79, 80, 128, 84, 69, 200, 67, 65, + 82, 79, 78, 128, 71, 128, 71, 82, 69, 65, 84, 69, 82, 45, 84, 72, 65, + 206, 66, 128, 74, 128, 90, 128, 67, 128, 70, 128, 76, 69, 83, 83, 45, 84, + 72, 65, 206, 83, 72, 79, 82, 212, 75, 65, 128, 81, 128, 85, 80, 87, 65, + 82, 68, 211, 89, 80, 79, 71, 69, 71, 82, 65, 77, 77, 69, 78, 73, 128, 66, + 82, 69, 86, 69, 128, 70, 85, 76, 204, 83, 69, 69, 206, 83, 72, 69, 69, + 206, 89, 65, 128, 76, 73, 78, 197, 82, 79, 77, 65, 206, 84, 82, 73, 65, + 78, 71, 76, 69, 128, 68, 79, 87, 78, 87, 65, 82, 68, 211, 77, 65, 128, + 79, 80, 69, 82, 65, 84, 79, 82, 128, 82, 69, 86, 69, 82, 83, 69, 196, 84, + 87, 207, 66, 82, 65, 67, 75, 69, 212, 68, 79, 84, 128, 73, 79, 84, 193, + 84, 73, 76, 68, 197, 65, 67, 67, 69, 78, 212, 66, 76, 65, 67, 75, 128, + 80, 65, 83, 83, 73, 86, 69, 45, 80, 85, 76, 76, 45, 68, 79, 87, 78, 45, + 79, 85, 84, 80, 85, 212, 66, 89, 69, 76, 79, 82, 85, 83, 83, 73, 65, 78, + 45, 85, 75, 82, 65, 73, 78, 73, 65, 206, 67, 69, 79, 78, 71, 67, 72, 73, + 69, 85, 77, 83, 83, 65, 78, 71, 67, 73, 69, 85, 67, 128, 80, 65, 83, 83, + 73, 86, 69, 45, 80, 85, 76, 76, 45, 85, 80, 45, 79, 85, 84, 80, 85, 212, + 65, 78, 84, 73, 67, 76, 79, 67, 75, 87, 73, 83, 69, 45, 82, 79, 84, 65, + 84, 69, 196, 67, 69, 79, 78, 71, 67, 72, 73, 69, 85, 77, 83, 83, 65, 78, + 71, 83, 73, 79, 83, 128, 80, 83, 73, 70, 73, 83, 84, 79, 80, 65, 82, 65, + 75, 65, 76, 69, 83, 77, 65, 128, 82, 73, 69, 85, 76, 45, 75, 65, 80, 89, + 69, 79, 85, 78, 80, 73, 69, 85, 80, 128, 75, 65, 80, 89, 69, 79, 85, 78, + 83, 83, 65, 78, 71, 80, 73, 69, 85, 80, 128, 79, 80, 69, 78, 45, 67, 73, + 82, 67, 85, 73, 84, 45, 79, 85, 84, 80, 85, 212, 67, 69, 79, 78, 71, 67, + 72, 73, 69, 85, 77, 67, 72, 73, 69, 85, 67, 72, 128, 67, 72, 73, 84, 85, + 69, 85, 77, 83, 83, 65, 78, 71, 67, 73, 69, 85, 67, 128, 75, 73, 89, 69, + 79, 75, 45, 83, 73, 79, 83, 45, 75, 73, 89, 69, 79, 75, 128, 82, 73, 69, + 85, 76, 45, 77, 73, 69, 85, 77, 45, 75, 73, 89, 69, 79, 75, 128, 82, 73, + 69, 85, 76, 45, 84, 73, 75, 69, 85, 84, 45, 72, 73, 69, 85, 72, 128, 84, + 82, 79, 77, 73, 75, 79, 80, 65, 82, 65, 75, 65, 76, 69, 83, 77, 65, 128, + 80, 73, 69, 85, 80, 45, 83, 73, 79, 83, 45, 75, 73, 89, 69, 79, 75, 128, + 80, 73, 69, 85, 80, 45, 83, 73, 79, 83, 45, 84, 73, 75, 69, 85, 84, 128, + 82, 73, 69, 85, 76, 45, 75, 73, 89, 69, 79, 75, 45, 83, 73, 79, 83, 128, + 82, 73, 69, 85, 76, 45, 89, 69, 79, 82, 73, 78, 72, 73, 69, 85, 72, 128, + 67, 72, 73, 84, 85, 69, 85, 77, 83, 83, 65, 78, 71, 83, 73, 79, 83, 128, + 73, 69, 85, 78, 71, 45, 83, 83, 65, 78, 71, 75, 73, 89, 69, 79, 75, 128, + 76, 79, 78, 71, 45, 66, 82, 65, 78, 67, 72, 45, 72, 65, 71, 65, 76, 204, + 80, 65, 82, 84, 73, 65, 76, 76, 89, 45, 82, 69, 67, 89, 67, 76, 69, 196, + 82, 73, 69, 85, 76, 45, 80, 73, 69, 85, 80, 45, 72, 73, 69, 85, 72, 128, + 83, 72, 79, 82, 84, 45, 84, 87, 73, 71, 45, 66, 74, 65, 82, 75, 65, 206, + 83, 73, 79, 83, 45, 80, 73, 69, 85, 80, 45, 75, 73, 89, 69, 79, 75, 128, + 75, 65, 84, 65, 75, 65, 78, 65, 45, 72, 73, 82, 65, 71, 65, 78, 193, 82, + 73, 69, 85, 76, 45, 80, 73, 69, 85, 80, 45, 83, 73, 79, 83, 128, 89, 69, + 83, 73, 69, 85, 78, 71, 45, 80, 65, 78, 83, 73, 79, 83, 128, 67, 69, 79, + 78, 71, 67, 72, 73, 69, 85, 77, 67, 73, 69, 85, 67, 128, 77, 65, 82, 67, + 65, 84, 79, 45, 83, 84, 65, 67, 67, 65, 84, 79, 128, 80, 73, 69, 85, 80, + 45, 83, 73, 79, 83, 45, 67, 73, 69, 85, 67, 128, 80, 73, 69, 85, 80, 45, + 83, 73, 79, 83, 45, 80, 73, 69, 85, 80, 128, 82, 73, 69, 85, 76, 45, 77, + 73, 69, 85, 77, 45, 83, 73, 79, 83, 128, 83, 72, 79, 82, 84, 45, 84, 87, + 73, 71, 45, 72, 65, 71, 65, 76, 204, 83, 79, 70, 84, 87, 65, 82, 69, 45, + 70, 85, 78, 67, 84, 73, 79, 206, 84, 82, 79, 77, 73, 75, 79, 80, 83, 73, + 70, 73, 83, 84, 79, 78, 128, 75, 65, 80, 89, 69, 79, 85, 78, 80, 72, 73, + 69, 85, 80, 72, 128, 65, 78, 84, 73, 82, 69, 83, 84, 82, 73, 67, 84, 73, + 79, 78, 128, 65, 67, 67, 69, 78, 84, 45, 83, 84, 65, 67, 67, 65, 84, 79, + 128, 65, 78, 84, 73, 75, 69, 78, 79, 75, 89, 76, 73, 83, 77, 65, 128, 67, + 69, 79, 78, 71, 67, 72, 73, 69, 85, 77, 83, 73, 79, 83, 128, 67, 72, 73, + 69, 85, 67, 72, 45, 75, 72, 73, 69, 85, 75, 72, 128, 67, 72, 73, 84, 85, + 69, 85, 77, 67, 72, 73, 69, 85, 67, 72, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 48, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 48, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 48, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 48, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 48, 52, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 48, 53, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 48, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 48, 55, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 48, 56, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 48, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 48, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 48, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 48, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 48, 68, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 48, 69, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 48, 70, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 49, 48, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 49, 49, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 49, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 49, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 49, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 49, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 49, 54, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 49, 55, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 49, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 49, 57, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 49, 65, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 49, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 49, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 49, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 49, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 49, 70, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 50, 48, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 50, 49, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 50, 50, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 50, 51, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 50, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 50, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 50, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 50, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 50, 56, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 50, 57, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 50, 65, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 50, 66, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 50, 67, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 50, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 50, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 50, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 51, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 51, 49, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 51, 50, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 51, 51, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 51, 52, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 51, 53, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 51, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 51, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 51, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 51, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 51, 65, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 51, 66, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 51, 67, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 51, 68, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 51, 69, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 51, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 52, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 52, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 52, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 52, 51, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 52, 52, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 52, 53, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 52, 54, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 52, 55, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 52, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 52, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 52, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 52, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 52, 67, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 52, 68, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 52, 69, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 52, 70, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 53, 48, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 53, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 53, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 53, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 53, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 53, 53, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 53, 54, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 53, 55, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 53, 56, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 53, 57, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 53, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 53, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 53, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 53, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 53, 69, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 53, 70, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 54, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 54, 49, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 54, 50, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 54, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 54, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 54, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 54, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 54, 55, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 54, 56, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 54, 57, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 54, 65, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 54, 66, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 54, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 54, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 54, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 54, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 55, 48, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 55, 49, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 55, 50, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 55, 51, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 55, 52, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 55, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 55, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 55, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 55, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 55, 57, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 55, 65, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 55, 66, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 55, 67, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 55, 68, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 55, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 55, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 56, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 56, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 56, 50, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 56, 51, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 56, 52, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 56, 53, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 56, 54, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 56, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 56, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 56, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 56, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 56, 66, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 56, 67, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 56, 68, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 56, 69, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 56, 70, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 57, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 57, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 57, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 57, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 57, 52, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 57, 53, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 57, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 57, 55, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 57, 56, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 57, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 57, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 57, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 57, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 57, 68, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 57, 69, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 57, 70, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 65, 48, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 65, 49, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 65, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 65, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 65, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 65, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 65, 54, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 65, 55, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 65, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 65, 57, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 65, 65, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 65, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 65, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 65, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 65, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 65, 70, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 66, 48, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 66, 49, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 66, 50, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 66, 51, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 66, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 66, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 66, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 66, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 66, 56, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 66, 57, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 66, 65, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 66, 66, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 66, 67, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 66, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 66, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 66, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 67, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 67, 49, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 67, 50, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 67, 51, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 67, 52, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 67, 53, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 67, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 67, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 67, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 67, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 67, 65, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 67, 66, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 67, 67, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 67, 68, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 67, 69, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 67, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 68, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 68, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 68, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 68, 51, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 68, 52, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 68, 53, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 68, 54, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 68, 55, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 68, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 68, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 68, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 68, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 68, 67, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 68, 68, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 68, 69, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 68, 70, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 69, 48, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 69, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 69, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 69, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 69, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 69, 53, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 69, 54, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 69, 55, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 69, 56, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 69, 57, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 69, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 69, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 69, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 69, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 69, 69, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 69, 70, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 70, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 70, 49, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 70, 50, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 70, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 70, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 70, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 70, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 70, 55, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 70, 56, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 70, 57, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, 70, 65, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 56, 70, 66, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 56, 70, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 56, 70, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 56, 70, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 56, + 70, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 48, 48, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 48, 49, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 48, 50, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 48, 51, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 48, 52, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 48, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 48, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 48, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 48, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 48, 57, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 48, 65, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 48, 66, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 48, 67, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 48, 68, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 48, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 48, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 49, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 49, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 49, 50, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 49, 51, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 49, 52, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 49, 53, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 49, 54, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 49, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 49, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 49, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 49, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 49, 66, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 49, 67, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 49, 68, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 49, 69, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 49, 70, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 50, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 50, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 50, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 50, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 50, 52, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 50, 53, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 50, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 50, 55, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 50, 56, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 50, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 50, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 50, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 50, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 50, 68, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 50, 69, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 50, 70, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 51, 48, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 51, 49, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 51, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 51, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 51, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 51, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 51, 54, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 51, 55, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 51, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 51, 57, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 51, 65, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 51, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 51, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 51, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 51, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 51, 70, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 52, 48, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 52, 49, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 52, 50, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 52, 51, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 52, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 52, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 52, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 52, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 52, 56, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 52, 57, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 52, 65, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 52, 66, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 52, 67, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 52, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 52, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 52, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 53, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 53, 49, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 53, 50, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 53, 51, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 53, 52, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 53, 53, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 53, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 53, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 53, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 53, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 53, 65, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 53, 66, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 53, 67, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 53, 68, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 53, 69, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 53, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 54, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 54, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 54, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 54, 51, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 54, 52, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 54, 53, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 54, 54, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 54, 55, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 54, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 54, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 54, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 54, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 54, 67, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 54, 68, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 54, 69, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 54, 70, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 55, 48, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 55, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 55, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 55, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 55, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 55, 53, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 55, 54, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 55, 55, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 55, 56, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 55, 57, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 55, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 55, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 55, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 55, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 55, 69, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 55, 70, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 56, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 56, 49, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 56, 50, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 56, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 56, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 56, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 56, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 56, 55, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 56, 56, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 56, 57, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 56, 65, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 56, 66, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 56, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 56, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 56, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 56, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 57, 48, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 57, 49, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 57, 50, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 57, 51, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 57, 52, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 57, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 57, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 57, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 57, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 57, 57, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 57, 65, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 57, 66, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 57, 67, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 57, 68, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 57, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 57, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 65, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 65, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 65, 50, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 65, 51, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 65, 52, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 65, 53, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 65, 54, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 65, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 65, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 65, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 65, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 65, 66, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 65, 67, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 65, 68, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 65, 69, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 65, 70, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 66, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 66, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 66, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 66, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 66, 52, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 66, 53, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 66, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 66, 55, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 66, 56, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 66, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 66, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 66, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 66, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 66, 68, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 66, 69, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 66, 70, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 67, 48, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 67, 49, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 67, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 67, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 67, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 67, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 67, 54, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 67, 55, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 67, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 67, 57, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 67, 65, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 67, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 67, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 67, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 67, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 67, 70, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 68, 48, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 68, 49, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 68, 50, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 68, 51, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 68, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 68, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 68, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 68, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 68, 56, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 68, 57, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 68, 65, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 68, 66, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 68, 67, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 68, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 68, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 68, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 69, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 69, 49, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 69, 50, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 69, 51, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 69, 52, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 69, 53, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 69, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 69, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 69, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 69, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 69, 65, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 69, 66, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 69, 67, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 69, 68, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 69, 69, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 69, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 70, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 70, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 70, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 70, 51, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 70, 52, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 70, 53, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 70, 54, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 57, 70, 55, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 57, 70, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 57, 70, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 57, 70, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, + 70, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 70, 67, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 70, 68, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 70, 69, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 57, 70, 70, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 65, 48, 48, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 65, 48, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 65, 48, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 65, 48, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, + 48, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 48, 53, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 48, 54, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 48, 55, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 48, 56, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 65, 48, 57, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 65, 48, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 65, 48, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 65, 48, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, + 48, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 48, 69, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 48, 70, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 49, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 49, 49, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 65, 49, 50, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 65, 49, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 65, 49, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, + 70, 65, 49, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, + 49, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 49, 55, + 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 49, 56, 128, 73, + 68, 69, 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 49, 57, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 50, 70, 65, 49, 65, 128, 73, 68, 69, 79, 71, + 82, 65, 80, 72, 45, 50, 70, 65, 49, 66, 128, 73, 68, 69, 79, 71, 82, 65, + 80, 72, 45, 50, 70, 65, 49, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 50, 70, 65, 49, 68, 128, 74, 65, 76, 76, 65, 74, 65, 76, 65, 76, 79, + 85, 72, 79, 85, 128, 75, 82, 65, 84, 73, 77, 79, 75, 79, 85, 70, 73, 83, + 77, 65, 128, 75, 82, 65, 84, 73, 77, 79, 89, 80, 79, 82, 82, 79, 79, 78, + 128, 76, 79, 78, 71, 45, 66, 82, 65, 78, 67, 72, 45, 77, 65, 68, 210, 77, + 73, 69, 85, 77, 45, 83, 83, 65, 78, 71, 83, 73, 79, 83, 128, 80, 69, 84, + 65, 83, 84, 79, 75, 79, 85, 70, 73, 83, 77, 65, 128, 80, 73, 69, 85, 80, + 45, 83, 83, 65, 78, 71, 83, 73, 79, 83, 128, 80, 83, 73, 70, 73, 83, 84, + 79, 76, 89, 71, 73, 83, 77, 65, 128, 80, 83, 73, 70, 73, 83, 84, 79, 83, + 89, 78, 65, 71, 77, 65, 128, 82, 73, 69, 85, 76, 45, 83, 83, 65, 78, 71, + 83, 73, 79, 83, 128, 84, 69, 65, 82, 68, 82, 79, 80, 45, 83, 72, 65, 78, + 75, 69, 196, 80, 82, 79, 83, 71, 69, 71, 82, 65, 77, 77, 69, 78, 73, 128, + 84, 69, 65, 82, 68, 82, 79, 80, 45, 83, 80, 79, 75, 69, 196, 66, 76, 65, + 67, 75, 45, 70, 69, 65, 84, 72, 69, 82, 69, 196, 84, 82, 73, 65, 78, 71, + 76, 69, 45, 72, 69, 65, 68, 69, 196, 67, 79, 78, 71, 82, 65, 84, 85, 76, + 65, 84, 73, 79, 78, 128, 72, 73, 71, 72, 45, 82, 69, 86, 69, 82, 83, 69, + 68, 45, 185, 65, 70, 79, 82, 69, 77, 69, 78, 84, 73, 79, 78, 69, 68, 128, + 65, 82, 79, 85, 78, 68, 45, 80, 82, 79, 70, 73, 76, 69, 128, 67, 79, 78, + 67, 65, 86, 69, 45, 80, 79, 73, 78, 84, 69, 196, 71, 79, 82, 71, 79, 83, + 89, 78, 84, 72, 69, 84, 79, 78, 128, 73, 68, 69, 78, 84, 73, 70, 73, 67, + 65, 84, 73, 79, 78, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 48, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 48, 49, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 48, 50, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 48, 51, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 48, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 48, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 48, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 48, 55, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 48, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 48, 57, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 48, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 48, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 48, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 48, 68, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 48, 69, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 48, 70, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 49, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 49, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 49, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 49, 51, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 49, 52, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 49, 53, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 49, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 49, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 49, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 49, 57, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 49, 65, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 49, 66, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 49, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 49, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 49, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 49, 70, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 50, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 50, 49, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 50, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 50, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 50, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 50, 53, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 50, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 50, 55, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 50, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 50, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 50, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 50, 66, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 50, 67, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 50, 68, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 50, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 50, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 51, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 51, 49, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 51, 50, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 51, 51, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 51, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 51, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 51, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 51, 55, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 51, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 51, 57, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 51, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 51, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 51, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 51, 68, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 51, 69, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 51, 70, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 52, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 52, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 52, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 52, 51, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 52, 52, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 52, 53, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 52, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 52, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 52, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 52, 57, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 52, 65, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 52, 66, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 52, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 52, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 52, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 52, 70, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 53, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 53, 49, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 53, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 53, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 53, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 53, 53, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 53, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 53, 55, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 53, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 53, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 53, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 53, 66, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 53, 67, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 53, 68, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 53, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 53, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 54, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 54, 49, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 54, 50, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 54, 51, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 54, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 54, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 54, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 54, 55, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 54, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 54, 57, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 54, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 54, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 54, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 54, 68, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 54, 69, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 54, 70, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 55, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 55, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 55, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 55, 51, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 55, 52, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 55, 53, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 55, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 55, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 55, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 55, 57, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 55, 65, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 55, 66, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 55, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 55, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 55, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 55, 70, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 56, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 56, 49, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 56, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 56, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 56, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 56, 53, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 56, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 56, 55, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 56, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 56, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 56, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 56, 66, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 56, 67, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 56, 68, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 56, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 56, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 57, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 57, 49, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 57, 50, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 57, 51, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 57, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 57, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 57, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 57, 55, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 57, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 57, 57, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 57, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 57, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 57, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 57, 68, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 57, 69, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 57, 70, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 65, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 65, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 65, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 65, 51, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 65, 52, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 65, 53, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 65, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 65, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 65, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 65, 57, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 65, 65, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 65, 66, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 65, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 65, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 65, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 65, 70, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 66, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 66, 49, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 66, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 66, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 66, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 66, 53, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 66, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 66, 55, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 66, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 66, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 66, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 66, 66, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 66, 67, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 66, 68, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 66, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 66, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 67, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 67, 49, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 67, 50, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 67, 51, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 67, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 67, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 67, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 67, 55, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 67, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 67, 57, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 67, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 67, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 67, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 67, 68, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 67, 69, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 67, 70, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 68, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 68, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 68, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 68, 51, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 68, 52, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 68, 53, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 68, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 68, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 68, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 68, 57, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 68, 65, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 68, 66, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 68, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 68, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 68, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 68, 70, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 69, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 69, 49, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 69, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 69, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 69, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 69, 53, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 69, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 69, 55, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 69, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 69, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 69, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 69, 66, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 69, 67, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 69, 68, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 69, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 69, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 70, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 70, 49, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 70, 50, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 70, 51, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 70, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 70, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 70, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 70, 55, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 70, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 70, 57, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 57, 70, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 57, 70, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, + 70, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 70, 68, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 57, 70, 69, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 57, 70, 70, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 48, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 48, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 48, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 48, 51, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 48, 52, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 48, 53, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 48, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 48, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 48, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 48, 57, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 48, 65, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 48, 66, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 48, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 48, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 48, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 48, 70, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 49, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 49, 49, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 49, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 49, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 49, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 49, 53, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 49, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 49, 55, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 49, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 49, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 49, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 49, 66, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 49, 67, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 49, 68, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 49, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 49, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 50, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 50, 49, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 50, 50, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 50, 51, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 50, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 50, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 50, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 50, 55, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 50, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 50, 57, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 50, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 50, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 50, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 50, 68, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 51, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 51, 49, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 51, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 51, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 51, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 51, 53, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 51, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 51, 55, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 51, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 51, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 51, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 51, 66, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 51, 67, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 51, 68, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 51, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 51, 70, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 52, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 52, 49, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 52, 50, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 52, 51, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 52, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 52, 53, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 52, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 52, 55, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 52, 56, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 52, 57, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 52, 65, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 52, 66, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 52, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 52, 68, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 52, 69, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 52, 70, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 53, 48, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 53, 49, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 53, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 53, 51, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 53, 52, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 53, 53, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 53, 54, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 53, 55, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 53, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 53, 57, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 53, 65, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 53, 66, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 53, 67, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 53, 68, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 53, 69, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 53, 70, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 54, 48, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 54, 49, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 54, 50, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 54, 51, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 54, 52, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 54, 53, 128, + 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, 54, 54, 128, 73, 68, 69, + 79, 71, 82, 65, 80, 72, 45, 70, 65, 54, 55, 128, 73, 68, 69, 79, 71, 82, + 65, 80, 72, 45, 70, 65, 54, 56, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, + 45, 70, 65, 54, 57, 128, 73, 68, 69, 79, 71, 82, 65, 80, 72, 45, 70, 65, + 54, 65, 128, 76, 79, 78, 71, 45, 66, 82, 65, 78, 67, 72, 45, 79, 83, 211, + 76, 79, 78, 71, 45, 66, 82, 65, 78, 67, 72, 45, 83, 79, 204, 76, 79, 78, + 71, 45, 66, 82, 65, 78, 67, 72, 45, 89, 82, 128, 77, 85, 76, 84, 73, 80, + 76, 73, 67, 65, 84, 73, 79, 78, 128, 80, 65, 76, 65, 84, 65, 76, 73, 90, + 65, 84, 73, 79, 78, 128, 83, 72, 79, 82, 84, 45, 84, 87, 73, 71, 45, 77, + 65, 68, 210, 83, 72, 79, 82, 84, 45, 84, 87, 73, 71, 45, 78, 65, 85, 196, + 83, 73, 79, 83, 45, 83, 83, 65, 78, 71, 83, 73, 79, 83, 128, 84, 69, 65, + 82, 68, 82, 79, 80, 45, 66, 65, 82, 66, 69, 196, 84, 82, 79, 77, 73, 75, + 79, 76, 89, 71, 73, 83, 77, 65, 128, 84, 82, 79, 77, 73, 75, 79, 83, 89, + 78, 65, 71, 77, 65, 128, 87, 72, 73, 84, 69, 45, 70, 69, 65, 84, 72, 69, + 82, 69, 196, 82, 73, 71, 72, 84, 45, 80, 79, 73, 78, 84, 73, 78, 199, 77, + 85, 76, 84, 73, 80, 76, 73, 67, 65, 84, 73, 79, 206, 82, 73, 71, 72, 84, + 45, 83, 72, 65, 68, 79, 87, 69, 196, 66, 65, 76, 76, 79, 79, 78, 45, 83, + 80, 79, 75, 69, 196, 75, 65, 80, 89, 69, 79, 85, 78, 77, 73, 69, 85, 77, + 128, 82, 73, 69, 85, 76, 45, 80, 72, 73, 69, 85, 80, 72, 128, 82, 73, 69, + 85, 76, 45, 84, 72, 73, 69, 85, 84, 72, 128, 65, 82, 71, 79, 83, 89, 78, + 84, 72, 69, 84, 79, 78, 128, 65, 83, 89, 77, 80, 84, 79, 84, 73, 67, 65, + 76, 76, 217, 77, 73, 69, 85, 77, 45, 80, 65, 78, 83, 73, 79, 83, 128, 78, + 73, 69, 85, 78, 45, 80, 65, 78, 83, 73, 79, 83, 128, 80, 65, 82, 65, 76, + 76, 69, 76, 79, 71, 82, 65, 77, 128, 80, 72, 73, 69, 85, 80, 72, 45, 80, + 73, 69, 85, 80, 128, 80, 73, 69, 85, 80, 45, 80, 72, 73, 69, 85, 80, 72, + 128, 80, 73, 69, 85, 80, 45, 84, 72, 73, 69, 85, 84, 72, 128, 82, 73, 69, + 85, 76, 45, 80, 65, 78, 83, 73, 79, 83, 128, 84, 69, 84, 65, 82, 84, 73, + 77, 79, 82, 73, 79, 78, 128, 84, 73, 75, 69, 85, 84, 45, 75, 73, 89, 69, + 79, 75, 128, 84, 82, 73, 65, 78, 71, 76, 69, 45, 82, 79, 85, 78, 196, 89, + 69, 83, 73, 69, 85, 78, 71, 45, 83, 73, 79, 83, 128, 65, 86, 65, 75, 82, + 65, 72, 65, 83, 65, 78, 89, 65, 128, 66, 79, 84, 84, 79, 77, 45, 76, 73, + 71, 72, 84, 69, 196, 67, 72, 73, 69, 85, 67, 72, 45, 72, 73, 69, 85, 72, + 128, 67, 72, 73, 84, 85, 69, 85, 77, 67, 73, 69, 85, 67, 128, 68, 79, 84, + 83, 45, 49, 50, 51, 52, 53, 54, 55, 56, 128, 73, 69, 85, 78, 71, 45, 67, + 72, 73, 69, 85, 67, 72, 128, 73, 69, 85, 78, 71, 45, 75, 72, 73, 69, 85, + 75, 72, 128, 73, 69, 85, 78, 71, 45, 80, 72, 73, 69, 85, 80, 72, 128, 73, + 69, 85, 78, 71, 45, 84, 72, 73, 69, 85, 84, 72, 128, 75, 65, 80, 89, 69, + 79, 85, 78, 82, 73, 69, 85, 76, 128, 76, 79, 78, 71, 45, 66, 82, 65, 78, + 67, 72, 45, 65, 210, 77, 73, 69, 85, 77, 45, 67, 72, 73, 69, 85, 67, 72, + 128, 78, 73, 69, 85, 78, 45, 84, 72, 73, 69, 85, 84, 72, 128, 80, 69, 82, + 80, 69, 78, 68, 73, 67, 85, 76, 65, 82, 128, 80, 73, 69, 85, 80, 45, 67, + 72, 73, 69, 85, 67, 72, 128, 82, 73, 69, 85, 76, 45, 75, 72, 73, 69, 85, + 75, 72, 128, 83, 72, 79, 82, 84, 45, 84, 87, 73, 71, 45, 79, 83, 211, 83, + 72, 79, 82, 84, 45, 84, 87, 73, 71, 45, 83, 79, 204, 83, 72, 79, 82, 84, + 45, 84, 87, 73, 71, 45, 84, 89, 210, 83, 72, 79, 82, 84, 45, 84, 87, 73, + 71, 45, 89, 82, 128, 83, 84, 65, 67, 67, 65, 84, 73, 83, 83, 73, 77, 79, + 128, 84, 72, 69, 82, 77, 79, 68, 89, 78, 65, 77, 73, 67, 128, 89, 85, 85, + 75, 65, 76, 69, 65, 80, 73, 78, 84, 85, 128, 71, 82, 69, 65, 84, 69, 82, + 45, 84, 72, 65, 78, 128, 65, 78, 84, 73, 67, 76, 79, 67, 75, 87, 73, 83, + 197, 76, 69, 70, 84, 45, 80, 79, 73, 78, 84, 73, 78, 199, 73, 78, 84, 69, + 82, 83, 69, 67, 84, 73, 79, 78, 128, 65, 80, 80, 82, 79, 88, 73, 77, 65, + 84, 69, 76, 217, 68, 73, 70, 70, 69, 82, 69, 78, 84, 73, 65, 76, 128, 68, + 79, 87, 78, 45, 80, 79, 73, 78, 84, 73, 78, 199, 80, 65, 82, 69, 83, 84, + 73, 71, 77, 69, 78, 79, 206, 72, 89, 80, 72, 69, 78, 45, 77, 73, 78, 85, + 83, 128, 67, 79, 78, 67, 65, 86, 69, 45, 83, 73, 68, 69, 196, 76, 69, 70, + 84, 45, 84, 79, 45, 82, 73, 71, 72, 212, 78, 73, 69, 85, 78, 45, 84, 73, + 75, 69, 85, 84, 128, 82, 73, 69, 85, 76, 45, 75, 73, 89, 69, 79, 75, 128, + 82, 73, 71, 72, 84, 45, 84, 79, 45, 76, 69, 70, 212, 68, 73, 77, 73, 78, + 85, 84, 73, 79, 78, 45, 49, 128, 68, 82, 79, 80, 45, 83, 72, 65, 68, 79, + 87, 69, 196, 71, 65, 69, 84, 84, 65, 45, 80, 73, 76, 76, 65, 128, 71, 69, + 79, 77, 69, 84, 82, 73, 67, 65, 76, 76, 217, 73, 69, 85, 78, 71, 45, 75, + 73, 89, 69, 79, 75, 128, 78, 73, 69, 85, 78, 45, 75, 73, 89, 69, 79, 75, + 128, 80, 73, 69, 85, 80, 45, 84, 73, 75, 69, 85, 84, 128, 82, 73, 69, 85, + 76, 45, 84, 73, 75, 69, 85, 84, 128, 84, 72, 73, 82, 84, 89, 45, 83, 69, + 67, 79, 78, 196, 84, 87, 69, 78, 84, 89, 45, 69, 73, 71, 72, 84, 200, 84, + 87, 69, 78, 84, 89, 45, 84, 72, 82, 69, 69, 128, 65, 78, 65, 84, 82, 73, + 67, 72, 73, 83, 77, 65, 128, 67, 72, 73, 84, 85, 69, 85, 77, 83, 73, 79, + 83, 128, 67, 82, 79, 83, 83, 69, 68, 45, 84, 65, 73, 76, 128, 67, 89, 76, + 73, 78, 68, 82, 73, 67, 73, 84, 89, 128, 68, 73, 77, 73, 78, 85, 84, 73, + 79, 78, 45, 50, 128, 68, 73, 77, 73, 78, 85, 84, 73, 79, 78, 45, 51, 128, + 68, 73, 83, 67, 79, 78, 84, 73, 78, 85, 79, 85, 211, 68, 79, 84, 83, 45, + 49, 50, 51, 52, 53, 54, 55, 128, 68, 79, 84, 83, 45, 49, 50, 51, 52, 53, + 54, 56, 128, 68, 79, 84, 83, 45, 49, 50, 51, 52, 53, 55, 56, 128, 68, 79, + 84, 83, 45, 49, 50, 51, 52, 54, 55, 56, 128, 68, 79, 84, 83, 45, 49, 50, + 51, 53, 54, 55, 56, 128, 68, 79, 84, 83, 45, 49, 50, 52, 53, 54, 55, 56, + 128, 68, 79, 84, 83, 45, 49, 51, 52, 53, 54, 55, 56, 128, 68, 79, 84, 83, + 45, 50, 51, 52, 53, 54, 55, 56, 128, 69, 85, 82, 79, 45, 67, 85, 82, 82, + 69, 78, 67, 217, 71, 82, 79, 78, 84, 72, 73, 83, 77, 65, 84, 65, 128, 73, + 67, 69, 76, 65, 78, 68, 73, 67, 45, 89, 82, 128, 73, 69, 85, 78, 71, 45, + 84, 73, 75, 69, 85, 84, 128, 73, 78, 84, 69, 82, 83, 89, 76, 76, 65, 66, + 73, 195, 74, 85, 68, 69, 79, 45, 83, 80, 65, 78, 73, 83, 200, 75, 73, 89, + 69, 79, 75, 45, 82, 73, 69, 85, 76, 128, 77, 73, 78, 85, 83, 45, 79, 82, + 45, 80, 76, 85, 211, 79, 80, 69, 78, 45, 79, 85, 84, 76, 73, 78, 69, 196, + 80, 69, 82, 80, 69, 78, 68, 73, 67, 85, 76, 65, 210, 82, 85, 76, 69, 45, + 68, 69, 76, 65, 89, 69, 68, 128, 83, 72, 79, 82, 84, 45, 84, 87, 73, 71, + 45, 65, 210, 83, 73, 79, 83, 45, 67, 72, 73, 69, 85, 67, 72, 128, 83, 73, + 79, 83, 45, 75, 72, 73, 69, 85, 75, 72, 128, 83, 73, 79, 83, 45, 80, 72, + 73, 69, 85, 80, 72, 128, 83, 73, 79, 83, 45, 84, 72, 73, 69, 85, 84, 72, + 128, 83, 84, 82, 65, 71, 71, 73, 83, 77, 65, 84, 65, 128, 84, 72, 85, 78, + 68, 69, 82, 83, 84, 79, 82, 77, 128, 84, 73, 75, 69, 85, 84, 45, 82, 73, + 69, 85, 76, 128, 84, 82, 65, 78, 83, 77, 73, 83, 83, 73, 79, 78, 128, 84, + 87, 69, 78, 84, 89, 45, 69, 73, 71, 72, 84, 128, 84, 87, 69, 78, 84, 89, + 45, 83, 69, 86, 69, 78, 128, 80, 69, 82, 73, 83, 80, 79, 77, 69, 78, 73, + 128, 67, 45, 83, 73, 77, 80, 76, 73, 70, 73, 69, 196, 80, 82, 69, 83, 69, + 78, 84, 65, 84, 73, 79, 206, 65, 82, 65, 66, 73, 67, 45, 73, 78, 68, 73, + 195, 80, 65, 82, 69, 78, 84, 72, 69, 83, 73, 83, 128, 73, 78, 84, 69, 82, + 83, 69, 67, 84, 73, 79, 206, 67, 65, 78, 68, 82, 65, 66, 73, 78, 68, 85, + 128, 69, 82, 82, 79, 82, 45, 66, 65, 82, 82, 69, 196, 66, 76, 65, 67, 75, + 45, 76, 69, 84, 84, 69, 210, 80, 85, 78, 67, 84, 85, 65, 84, 73, 79, 78, + 128, 65, 80, 80, 82, 79, 88, 73, 77, 65, 84, 69, 128, 67, 65, 78, 84, 73, + 76, 76, 65, 84, 73, 79, 206, 69, 75, 70, 79, 78, 73, 84, 73, 75, 79, 78, + 128, 74, 45, 83, 73, 77, 80, 76, 73, 70, 73, 69, 196, 82, 73, 69, 85, 76, + 45, 72, 73, 69, 85, 72, 128, 83, 79, 85, 84, 72, 45, 83, 76, 65, 86, 69, + 217, 65, 66, 66, 82, 69, 86, 73, 65, 84, 73, 79, 206, 65, 83, 84, 82, 79, + 76, 79, 71, 73, 67, 65, 204, 71, 65, 89, 65, 78, 85, 75, 73, 84, 84, 65, + 128, 77, 73, 69, 85, 77, 45, 80, 73, 69, 85, 80, 128, 78, 73, 69, 85, 78, + 45, 67, 73, 69, 85, 67, 128, 78, 73, 69, 85, 78, 45, 72, 73, 69, 85, 72, + 128, 82, 73, 69, 85, 76, 45, 77, 73, 69, 85, 77, 128, 82, 73, 69, 85, 76, + 45, 80, 73, 69, 85, 80, 128, 83, 69, 77, 73, 67, 73, 82, 67, 85, 76, 65, + 210, 83, 83, 65, 78, 71, 84, 73, 75, 69, 85, 84, 128, 65, 67, 75, 78, 79, + 87, 76, 69, 68, 71, 69, 128, 67, 79, 77, 80, 79, 83, 73, 84, 73, 79, 78, + 128, 73, 78, 84, 69, 82, 83, 69, 67, 84, 73, 78, 199, 80, 73, 69, 85, 80, + 45, 67, 73, 69, 85, 67, 128, 81, 85, 73, 78, 68, 73, 67, 69, 83, 73, 77, + 193, 82, 73, 69, 85, 76, 45, 78, 73, 69, 85, 78, 128, 83, 73, 88, 84, 89, + 45, 70, 79, 85, 82, 84, 200, 84, 82, 73, 84, 73, 77, 79, 82, 73, 79, 78, + 128, 84, 87, 69, 78, 84, 89, 45, 70, 79, 85, 82, 128, 87, 69, 68, 71, 69, + 45, 84, 65, 73, 76, 69, 196, 65, 77, 65, 76, 71, 65, 77, 65, 84, 73, 79, + 206, 65, 80, 80, 76, 73, 67, 65, 84, 73, 79, 78, 128, 65, 85, 71, 77, 69, + 78, 84, 65, 84, 73, 79, 206, 67, 65, 78, 67, 69, 76, 76, 65, 84, 73, 79, + 206, 67, 73, 69, 85, 67, 45, 73, 69, 85, 78, 71, 128, 67, 79, 78, 74, 85, + 78, 67, 84, 73, 79, 78, 128, 67, 79, 78, 84, 82, 65, 67, 84, 73, 79, 78, + 128, 67, 79, 82, 80, 79, 82, 65, 84, 73, 79, 78, 128, 67, 79, 85, 78, 84, + 69, 82, 66, 79, 82, 69, 128, 67, 79, 85, 78, 84, 69, 82, 83, 73, 78, 75, + 128, 68, 69, 67, 82, 69, 83, 67, 69, 78, 68, 79, 128, 68, 69, 78, 79, 77, + 73, 78, 65, 84, 79, 82, 128, 68, 73, 83, 84, 73, 78, 71, 85, 73, 83, 72, + 128, 68, 79, 65, 67, 72, 65, 83, 72, 77, 69, 69, 128, 68, 79, 84, 83, 45, + 49, 50, 51, 52, 53, 54, 128, 68, 79, 84, 83, 45, 49, 50, 51, 52, 53, 55, + 128, 68, 79, 84, 83, 45, 49, 50, 51, 52, 53, 56, 128, 68, 79, 84, 83, 45, + 49, 50, 51, 52, 54, 55, 128, 68, 79, 84, 83, 45, 49, 50, 51, 52, 54, 56, + 128, 68, 79, 84, 83, 45, 49, 50, 51, 52, 55, 56, 128, 68, 79, 84, 83, 45, + 49, 50, 51, 53, 54, 55, 128, 68, 79, 84, 83, 45, 49, 50, 51, 53, 54, 56, + 128, 68, 79, 84, 83, 45, 49, 50, 51, 53, 55, 56, 128, 68, 79, 84, 83, 45, + 49, 50, 51, 54, 55, 56, 128, 68, 79, 84, 83, 45, 49, 50, 52, 53, 54, 55, + 128, 68, 79, 84, 83, 45, 49, 50, 52, 53, 54, 56, 128, 68, 79, 84, 83, 45, + 49, 50, 52, 53, 55, 56, 128, 68, 79, 84, 83, 45, 49, 50, 52, 54, 55, 56, + 128, 68, 79, 84, 83, 45, 49, 50, 53, 54, 55, 56, 128, 68, 79, 84, 83, 45, + 49, 51, 52, 53, 54, 55, 128, 68, 79, 84, 83, 45, 49, 51, 52, 53, 54, 56, + 128, 68, 79, 84, 83, 45, 49, 51, 52, 53, 55, 56, 128, 68, 79, 84, 83, 45, + 49, 51, 52, 54, 55, 56, 128, 68, 79, 84, 83, 45, 49, 51, 53, 54, 55, 56, + 128, 68, 79, 84, 83, 45, 49, 52, 53, 54, 55, 56, 128, 68, 79, 84, 83, 45, + 50, 51, 52, 53, 54, 55, 128, 68, 79, 84, 83, 45, 50, 51, 52, 53, 54, 56, + 128, 68, 79, 84, 83, 45, 50, 51, 52, 53, 55, 56, 128, 68, 79, 84, 83, 45, + 50, 51, 52, 54, 55, 56, 128, 68, 79, 84, 83, 45, 50, 51, 53, 54, 55, 56, + 128, 68, 79, 84, 83, 45, 50, 52, 53, 54, 55, 56, 128, 68, 79, 84, 83, 45, + 51, 52, 53, 54, 55, 56, 128, 68, 79, 85, 66, 76, 69, 45, 69, 78, 68, 69, + 196, 69, 65, 77, 72, 65, 78, 67, 72, 79, 76, 76, 128, 70, 73, 78, 71, 69, + 82, 78, 65, 73, 76, 83, 128, 70, 82, 79, 78, 84, 45, 84, 73, 76, 84, 69, + 196, 72, 65, 85, 80, 84, 83, 84, 73, 77, 77, 69, 128, 72, 73, 69, 85, 72, + 45, 77, 73, 69, 85, 77, 128, 72, 73, 69, 85, 72, 45, 78, 73, 69, 85, 78, + 128, 72, 73, 69, 85, 72, 45, 80, 73, 69, 85, 80, 128, 72, 73, 69, 85, 72, + 45, 82, 73, 69, 85, 76, 128, 73, 69, 85, 78, 71, 45, 67, 73, 69, 85, 67, + 128, 73, 69, 85, 78, 71, 45, 77, 73, 69, 85, 77, 128, 73, 69, 85, 78, 71, + 45, 80, 73, 69, 85, 80, 128, 73, 78, 84, 69, 71, 82, 65, 84, 73, 79, 78, + 128, 73, 78, 84, 69, 82, 67, 65, 76, 65, 84, 69, 128, 73, 78, 84, 69, 82, + 82, 79, 66, 65, 78, 71, 128, 75, 73, 82, 79, 77, 69, 69, 84, 79, 82, 85, + 128, 76, 65, 75, 75, 72, 65, 78, 71, 89, 65, 79, 128, 77, 73, 69, 85, 77, + 45, 72, 73, 69, 85, 72, 128, 77, 73, 69, 85, 77, 45, 82, 73, 69, 85, 76, + 128, 77, 85, 85, 83, 73, 75, 65, 84, 79, 65, 78, 128, 78, 65, 65, 75, 83, + 73, 75, 89, 65, 89, 65, 128, 78, 69, 66, 69, 78, 83, 84, 73, 77, 77, 69, + 128, 78, 73, 69, 85, 78, 45, 80, 73, 69, 85, 80, 128, 78, 79, 78, 45, 66, + 82, 69, 65, 75, 73, 78, 199, 80, 65, 82, 65, 75, 76, 73, 84, 73, 75, 73, + 128, 80, 69, 82, 83, 80, 69, 67, 84, 73, 86, 69, 128, 80, 73, 69, 85, 80, + 45, 78, 73, 69, 85, 78, 128, 80, 73, 69, 85, 80, 45, 82, 73, 69, 85, 76, + 128, 80, 79, 83, 84, 80, 79, 83, 73, 84, 73, 79, 206, 80, 82, 69, 83, 67, + 82, 73, 80, 84, 73, 79, 206, 80, 82, 79, 80, 79, 82, 84, 73, 79, 78, 65, + 204, 82, 73, 71, 72, 84, 45, 83, 72, 65, 68, 69, 196, 82, 73, 78, 70, 79, + 82, 90, 65, 78, 68, 79, 128, 82, 79, 85, 78, 68, 45, 84, 73, 80, 80, 69, + 196, 83, 65, 71, 73, 84, 84, 65, 82, 73, 85, 83, 128, 83, 69, 76, 69, 67, + 84, 79, 82, 45, 49, 48, 128, 83, 69, 76, 69, 67, 84, 79, 82, 45, 49, 49, + 128, 83, 69, 76, 69, 67, 84, 79, 82, 45, 49, 50, 128, 83, 69, 76, 69, 67, + 84, 79, 82, 45, 49, 51, 128, 83, 69, 76, 69, 67, 84, 79, 82, 45, 49, 52, + 128, 83, 69, 76, 69, 67, 84, 79, 82, 45, 49, 53, 128, 83, 69, 76, 69, 67, + 84, 79, 82, 45, 49, 54, 128, 83, 80, 82, 69, 67, 72, 71, 69, 83, 65, 78, + 199, 83, 85, 80, 69, 82, 73, 77, 80, 79, 83, 69, 196, 84, 69, 84, 82, 65, + 70, 79, 78, 73, 65, 83, 128, 84, 72, 65, 78, 84, 72, 65, 75, 72, 65, 84, + 128, 84, 72, 82, 69, 69, 45, 80, 69, 82, 45, 69, 205, 84, 79, 65, 78, 68, + 65, 75, 72, 73, 65, 84, 128, 84, 82, 65, 78, 83, 77, 73, 83, 83, 73, 79, + 206, 84, 87, 69, 78, 84, 89, 45, 70, 73, 86, 69, 128, 84, 87, 69, 78, 84, + 89, 45, 78, 73, 78, 69, 128, 85, 78, 65, 83, 80, 73, 82, 65, 84, 69, 68, + 128, 67, 73, 82, 67, 85, 77, 70, 76, 69, 88, 128, 83, 85, 80, 69, 82, 83, + 67, 82, 73, 80, 212, 72, 79, 82, 73, 90, 79, 78, 84, 65, 76, 128, 73, 78, + 68, 69, 80, 69, 78, 68, 69, 78, 212, 80, 69, 82, 73, 83, 80, 79, 77, 69, + 78, 201, 68, 69, 83, 67, 82, 73, 80, 84, 73, 79, 206, 69, 88, 67, 76, 65, + 77, 65, 84, 73, 79, 206, 80, 65, 82, 69, 78, 84, 72, 69, 83, 73, 211, 68, + 79, 85, 66, 76, 69, 45, 76, 73, 78, 197, 77, 65, 72, 65, 65, 80, 82, 65, + 65, 78, 193, 65, 80, 79, 83, 84, 82, 79, 80, 72, 69, 128, 80, 85, 78, 67, + 84, 85, 65, 84, 73, 79, 206, 85, 80, 45, 80, 79, 73, 78, 84, 73, 78, 199, + 83, 73, 78, 71, 76, 69, 45, 76, 73, 78, 197, 73, 77, 80, 69, 82, 70, 69, + 67, 84, 85, 205, 82, 73, 71, 72, 84, 87, 65, 82, 68, 83, 128, 65, 82, 82, + 79, 87, 45, 84, 65, 73, 76, 128, 68, 79, 65, 67, 72, 65, 83, 72, 77, 69, + 197, 65, 69, 76, 65, 45, 80, 73, 76, 76, 65, 128, 65, 76, 84, 69, 82, 78, + 65, 84, 73, 86, 197, 73, 78, 84, 69, 71, 82, 65, 84, 73, 79, 206, 73, 78, + 84, 69, 82, 76, 73, 78, 69, 65, 210, 79, 80, 69, 78, 45, 72, 69, 65, 68, + 69, 196, 82, 73, 69, 85, 76, 45, 83, 73, 79, 83, 128, 83, 69, 77, 73, 45, + 86, 79, 73, 67, 69, 196, 83, 83, 65, 78, 71, 73, 69, 85, 78, 71, 128, 83, + 85, 80, 82, 65, 76, 73, 78, 69, 65, 210, 65, 69, 68, 65, 45, 80, 73, 76, + 76, 65, 128, 67, 79, 78, 83, 69, 67, 85, 84, 73, 86, 197, 68, 73, 86, 73, + 78, 65, 84, 73, 79, 78, 128, 69, 78, 84, 69, 82, 80, 82, 73, 83, 69, 128, + 73, 77, 80, 69, 82, 70, 69, 67, 84, 65, 128, 77, 79, 78, 79, 70, 79, 78, + 73, 65, 83, 128, 77, 79, 78, 79, 71, 82, 65, 77, 77, 79, 211, 78, 65, 65, + 83, 73, 75, 89, 65, 89, 65, 128, 78, 73, 69, 85, 78, 45, 83, 73, 79, 83, + 128, 79, 86, 69, 82, 76, 65, 80, 80, 73, 78, 199, 80, 65, 82, 65, 75, 65, + 76, 69, 83, 77, 193, 80, 65, 82, 65, 75, 76, 73, 84, 73, 75, 201, 80, 69, + 82, 67, 85, 83, 83, 73, 86, 69, 128, 80, 82, 79, 80, 79, 82, 84, 73, 79, + 78, 128, 82, 69, 67, 84, 65, 78, 71, 85, 76, 65, 210, 82, 69, 67, 84, 73, + 76, 73, 78, 69, 65, 210, 82, 69, 80, 76, 65, 67, 69, 77, 69, 78, 212, 83, + 73, 79, 83, 45, 78, 73, 69, 85, 78, 128, 83, 73, 79, 83, 45, 82, 73, 69, + 85, 76, 128, 83, 83, 65, 78, 71, 72, 73, 69, 85, 72, 128, 83, 83, 65, 78, + 71, 78, 73, 69, 85, 78, 128, 83, 83, 65, 78, 71, 82, 73, 69, 85, 76, 128, + 84, 65, 66, 85, 76, 65, 84, 73, 79, 78, 128, 84, 69, 84, 82, 65, 83, 73, + 77, 79, 85, 128, 84, 72, 69, 77, 65, 84, 73, 83, 77, 79, 211, 84, 87, 69, + 78, 84, 89, 45, 79, 78, 69, 128, 84, 87, 69, 78, 84, 89, 45, 84, 87, 79, + 128, 65, 76, 84, 69, 82, 78, 65, 84, 73, 79, 206, 65, 78, 71, 75, 72, 65, + 78, 75, 72, 85, 128, 65, 78, 84, 73, 75, 69, 78, 79, 77, 65, 128, 65, 78, + 85, 83, 86, 65, 82, 65, 89, 65, 128, 65, 80, 79, 83, 84, 82, 79, 70, 79, + 83, 128, 65, 83, 84, 69, 82, 73, 83, 67, 85, 83, 128, 66, 65, 67, 75, 45, + 84, 73, 76, 84, 69, 196, 66, 65, 82, 73, 89, 79, 79, 83, 65, 78, 128, 66, + 65, 84, 72, 65, 77, 65, 83, 65, 84, 128, 67, 73, 82, 67, 85, 76, 65, 84, + 73, 79, 206, 67, 76, 85, 66, 45, 83, 80, 79, 75, 69, 196, 67, 79, 77, 80, + 76, 69, 77, 69, 78, 84, 128, 67, 79, 77, 80, 79, 83, 73, 84, 73, 79, 206, + 67, 79, 82, 82, 69, 83, 80, 79, 78, 68, 211, 67, 82, 79, 83, 83, 66, 79, + 78, 69, 83, 128, 68, 69, 70, 73, 78, 73, 84, 73, 79, 78, 128, 68, 69, 78, + 79, 77, 73, 78, 65, 84, 79, 210, 68, 73, 82, 69, 67, 84, 73, 79, 78, 65, + 204, 68, 79, 84, 83, 45, 49, 50, 51, 52, 53, 128, 68, 79, 84, 83, 45, 49, + 50, 51, 52, 54, 128, 68, 79, 84, 83, 45, 49, 50, 51, 52, 55, 128, 68, 79, + 84, 83, 45, 49, 50, 51, 52, 56, 128, 68, 79, 84, 83, 45, 49, 50, 51, 53, + 54, 128, 68, 79, 84, 83, 45, 49, 50, 51, 53, 55, 128, 68, 79, 84, 83, 45, + 49, 50, 51, 53, 56, 128, 68, 79, 84, 83, 45, 49, 50, 51, 54, 55, 128, 68, + 79, 84, 83, 45, 49, 50, 51, 54, 56, 128, 68, 79, 84, 83, 45, 49, 50, 51, + 55, 56, 128, 68, 79, 84, 83, 45, 49, 50, 52, 53, 54, 128, 68, 79, 84, 83, + 45, 49, 50, 52, 53, 55, 128, 68, 79, 84, 83, 45, 49, 50, 52, 53, 56, 128, + 68, 79, 84, 83, 45, 49, 50, 52, 54, 55, 128, 68, 79, 84, 83, 45, 49, 50, + 52, 54, 56, 128, 68, 79, 84, 83, 45, 49, 50, 52, 55, 56, 128, 68, 79, 84, + 83, 45, 49, 50, 53, 54, 55, 128, 68, 79, 84, 83, 45, 49, 50, 53, 54, 56, + 128, 68, 79, 84, 83, 45, 49, 50, 53, 55, 56, 128, 68, 79, 84, 83, 45, 49, + 50, 54, 55, 56, 128, 68, 79, 84, 83, 45, 49, 51, 52, 53, 54, 128, 68, 79, + 84, 83, 45, 49, 51, 52, 53, 55, 128, 68, 79, 84, 83, 45, 49, 51, 52, 53, + 56, 128, 68, 79, 84, 83, 45, 49, 51, 52, 54, 55, 128, 68, 79, 84, 83, 45, + 49, 51, 52, 54, 56, 128, 68, 79, 84, 83, 45, 49, 51, 52, 55, 56, 128, 68, + 79, 84, 83, 45, 49, 51, 53, 54, 55, 128, 68, 79, 84, 83, 45, 49, 51, 53, + 54, 56, 128, 68, 79, 84, 83, 45, 49, 51, 53, 55, 56, 128, 68, 79, 84, 83, + 45, 49, 51, 54, 55, 56, 128, 68, 79, 84, 83, 45, 49, 52, 53, 54, 55, 128, + 68, 79, 84, 83, 45, 49, 52, 53, 54, 56, 128, 68, 79, 84, 83, 45, 49, 52, + 53, 55, 56, 128, 68, 79, 84, 83, 45, 49, 52, 54, 55, 56, 128, 68, 79, 84, + 83, 45, 49, 53, 54, 55, 56, 128, 68, 79, 84, 83, 45, 50, 51, 52, 53, 54, + 128, 68, 79, 84, 83, 45, 50, 51, 52, 53, 55, 128, 68, 79, 84, 83, 45, 50, + 51, 52, 53, 56, 128, 68, 79, 84, 83, 45, 50, 51, 52, 54, 55, 128, 68, 79, + 84, 83, 45, 50, 51, 52, 54, 56, 128, 68, 79, 84, 83, 45, 50, 51, 52, 55, + 56, 128, 68, 79, 84, 83, 45, 50, 51, 53, 54, 55, 128, 68, 79, 84, 83, 45, + 50, 51, 53, 54, 56, 128, 68, 79, 84, 83, 45, 50, 51, 53, 55, 56, 128, 68, + 79, 84, 83, 45, 50, 51, 54, 55, 56, 128, 68, 79, 84, 83, 45, 50, 52, 53, + 54, 55, 128, 68, 79, 84, 83, 45, 50, 52, 53, 54, 56, 128, 68, 79, 84, 83, + 45, 50, 52, 53, 55, 56, 128, 68, 79, 84, 83, 45, 50, 52, 54, 55, 56, 128, + 68, 79, 84, 83, 45, 50, 53, 54, 55, 56, 128, 68, 79, 84, 83, 45, 51, 52, + 53, 54, 55, 128, 68, 79, 84, 83, 45, 51, 52, 53, 54, 56, 128, 68, 79, 84, + 83, 45, 51, 52, 53, 55, 56, 128, 68, 79, 84, 83, 45, 51, 52, 54, 55, 56, + 128, 68, 79, 84, 83, 45, 51, 53, 54, 55, 56, 128, 68, 79, 84, 83, 45, 52, + 53, 54, 55, 56, 128, 69, 75, 83, 84, 82, 69, 80, 84, 79, 78, 128, 69, 77, + 66, 82, 79, 73, 68, 69, 82, 89, 128, 69, 81, 85, 73, 65, 78, 71, 85, 76, + 65, 210, 70, 65, 72, 82, 69, 78, 72, 69, 73, 84, 128, 70, 79, 82, 77, 65, + 84, 84, 73, 78, 71, 128, 70, 79, 85, 82, 45, 80, 69, 82, 45, 69, 205, 70, + 79, 85, 82, 45, 83, 84, 82, 73, 78, 199, 72, 66, 65, 83, 65, 45, 69, 83, + 65, 83, 193, 72, 79, 77, 79, 84, 72, 69, 84, 73, 67, 128, 72, 89, 80, 72, + 69, 78, 65, 84, 73, 79, 206, 73, 77, 73, 68, 73, 65, 82, 71, 79, 78, 128, + 73, 77, 73, 70, 84, 72, 79, 82, 79, 78, 128, 73, 78, 70, 79, 82, 77, 65, + 84, 73, 79, 206, 75, 73, 82, 79, 71, 85, 82, 65, 77, 85, 128, 75, 85, 78, + 68, 68, 65, 76, 73, 89, 65, 128, 76, 69, 70, 84, 45, 83, 72, 65, 68, 69, + 196, 77, 69, 77, 66, 69, 82, 83, 72, 73, 80, 128, 78, 65, 78, 71, 77, 79, + 78, 84, 72, 79, 128, 78, 79, 78, 45, 74, 79, 73, 78, 69, 82, 128, 78, 79, + 78, 70, 79, 82, 75, 73, 78, 71, 128, 79, 80, 80, 79, 83, 73, 84, 73, 79, + 78, 128, 80, 65, 76, 65, 84, 65, 76, 73, 90, 69, 196, 80, 82, 79, 74, 69, + 67, 84, 73, 79, 78, 128, 80, 82, 79, 74, 69, 67, 84, 73, 86, 69, 128, 82, + 65, 68, 73, 79, 65, 67, 84, 73, 86, 197, 83, 65, 67, 82, 73, 70, 73, 67, + 73, 65, 204, 83, 65, 76, 76, 65, 76, 76, 65, 72, 79, 213, 83, 69, 76, 69, + 67, 84, 79, 82, 45, 49, 128, 83, 69, 76, 69, 67, 84, 79, 82, 45, 50, 128, + 83, 69, 76, 69, 67, 84, 79, 82, 45, 51, 128, 83, 69, 76, 69, 67, 84, 79, + 82, 45, 52, 128, 83, 69, 76, 69, 67, 84, 79, 82, 45, 53, 128, 83, 69, 76, + 69, 67, 84, 79, 82, 45, 54, 128, 83, 69, 76, 69, 67, 84, 79, 82, 45, 55, + 128, 83, 69, 76, 69, 67, 84, 79, 82, 45, 56, 128, 83, 69, 76, 69, 67, 84, + 79, 82, 45, 57, 128, 83, 72, 65, 76, 83, 72, 69, 76, 69, 84, 128, 83, 73, + 79, 83, 45, 72, 73, 69, 85, 72, 128, 83, 73, 79, 83, 45, 73, 69, 85, 78, + 71, 128, 83, 73, 79, 83, 45, 77, 73, 69, 85, 77, 128, 83, 83, 65, 78, 71, + 65, 82, 65, 69, 65, 128, 83, 85, 66, 80, 85, 78, 67, 84, 73, 83, 128, 83, + 85, 66, 83, 84, 73, 84, 85, 84, 69, 128, 83, 89, 78, 67, 72, 82, 79, 78, + 79, 85, 211, 84, 69, 82, 77, 73, 78, 65, 84, 79, 82, 128, 84, 72, 73, 82, + 84, 89, 45, 79, 78, 69, 128, 84, 79, 80, 45, 76, 73, 71, 72, 84, 69, 196, + 84, 82, 65, 78, 83, 86, 69, 82, 83, 65, 204, 84, 87, 69, 78, 84, 89, 45, + 83, 73, 88, 128, 86, 69, 82, 84, 73, 67, 65, 76, 76, 89, 128, 87, 73, 68, + 69, 45, 72, 69, 65, 68, 69, 196, 76, 69, 83, 83, 45, 84, 72, 65, 78, 128, + 65, 78, 78, 79, 84, 65, 84, 73, 79, 206, 68, 69, 83, 67, 69, 78, 68, 69, + 82, 128, 69, 81, 85, 73, 86, 65, 76, 69, 78, 212, 83, 69, 80, 65, 82, 65, + 84, 79, 82, 128, 65, 76, 80, 65, 80, 82, 65, 65, 78, 193, 65, 82, 82, 79, + 87, 72, 69, 65, 68, 128, 80, 72, 65, 82, 89, 78, 71, 69, 65, 204, 80, 82, + 79, 76, 65, 84, 73, 79, 78, 197, 84, 85, 82, 78, 83, 84, 73, 76, 69, 128, + 84, 87, 79, 45, 72, 69, 65, 68, 69, 196, 68, 79, 87, 78, 87, 65, 82, 68, + 83, 128, 69, 88, 84, 69, 78, 83, 73, 79, 78, 128, 83, 69, 77, 73, 67, 79, + 76, 79, 78, 128, 65, 77, 80, 69, 82, 83, 65, 78, 68, 128, 76, 69, 70, 84, + 87, 65, 82, 68, 83, 128, 76, 69, 78, 84, 73, 67, 85, 76, 65, 210, 67, 79, + 77, 77, 69, 82, 67, 73, 65, 204, 83, 69, 77, 73, 68, 73, 82, 69, 67, 212, + 83, 69, 86, 69, 78, 84, 69, 69, 78, 128, 87, 79, 79, 68, 83, 45, 67, 82, + 69, 197, 66, 65, 67, 75, 83, 76, 65, 83, 72, 128, 68, 73, 65, 76, 89, 84, + 73, 75, 65, 128, 70, 73, 88, 69, 68, 45, 70, 79, 82, 205, 73, 77, 80, 69, + 82, 70, 69, 67, 84, 193, 73, 78, 68, 73, 67, 65, 84, 79, 82, 128, 82, 69, + 67, 84, 65, 78, 71, 76, 69, 128, 69, 78, 67, 76, 79, 83, 85, 82, 69, 128, + 72, 79, 85, 82, 71, 76, 65, 83, 83, 128, 83, 69, 77, 73, 66, 82, 69, 86, + 73, 211, 83, 69, 77, 73, 77, 73, 78, 73, 77, 193, 83, 78, 79, 87, 70, 76, + 65, 75, 69, 128, 84, 82, 73, 65, 78, 71, 85, 76, 65, 210, 65, 80, 79, 83, + 84, 82, 79, 70, 79, 201, 65, 80, 79, 83, 84, 82, 79, 70, 79, 211, 65, 82, + 80, 69, 71, 71, 73, 65, 84, 207, 65, 84, 72, 65, 80, 65, 83, 67, 65, 206, + 67, 69, 78, 84, 82, 69, 76, 73, 78, 197, 67, 72, 65, 82, 65, 67, 84, 69, + 82, 128, 67, 79, 78, 84, 65, 73, 78, 73, 78, 199, 67, 79, 80, 82, 79, 68, + 85, 67, 84, 128, 67, 82, 79, 83, 83, 72, 65, 84, 67, 200, 69, 77, 66, 69, + 68, 68, 73, 78, 71, 128, 70, 73, 78, 65, 78, 67, 73, 65, 76, 128, 70, 82, + 69, 84, 66, 79, 65, 82, 68, 128, 71, 69, 82, 83, 72, 65, 89, 73, 77, 128, + 71, 79, 82, 84, 72, 77, 73, 75, 79, 206, 73, 67, 72, 73, 77, 65, 84, 79, + 83, 128, 75, 72, 65, 75, 65, 83, 83, 73, 65, 206, 80, 65, 65, 45, 80, 73, + 76, 76, 65, 128, 80, 72, 73, 76, 73, 80, 80, 73, 78, 197, 83, 69, 77, 73, + 67, 73, 82, 67, 76, 197, 83, 85, 77, 77, 65, 84, 73, 79, 78, 128, 83, 85, + 80, 69, 82, 86, 73, 83, 69, 128, 84, 69, 76, 69, 80, 72, 79, 78, 69, 128, + 84, 82, 69, 77, 79, 76, 79, 45, 49, 128, 84, 82, 69, 77, 79, 76, 79, 45, + 50, 128, 84, 82, 69, 77, 79, 76, 79, 45, 51, 128, 84, 82, 73, 71, 82, 65, + 77, 77, 79, 211, 65, 65, 66, 65, 65, 70, 73, 76, 73, 128, 65, 76, 45, 76, + 65, 75, 85, 78, 65, 128, 65, 78, 84, 73, 70, 79, 78, 73, 65, 128, 65, 80, + 80, 82, 79, 65, 67, 72, 69, 211, 65, 83, 83, 69, 82, 84, 73, 79, 78, 128, + 65, 84, 84, 69, 78, 84, 73, 79, 78, 128, 66, 65, 67, 75, 83, 80, 65, 67, + 69, 128, 66, 73, 66, 76, 69, 45, 67, 82, 69, 197, 67, 65, 80, 82, 73, 67, + 79, 82, 78, 128, 67, 72, 65, 86, 73, 89, 65, 78, 73, 128, 67, 79, 77, 80, + 76, 69, 84, 69, 68, 128, 67, 79, 80, 89, 82, 73, 71, 72, 84, 128, 68, 69, + 76, 73, 77, 73, 84, 69, 82, 128, 68, 69, 83, 67, 69, 78, 68, 73, 78, 199, + 68, 73, 70, 70, 69, 82, 69, 78, 67, 197, 68, 79, 84, 83, 45, 49, 50, 51, + 52, 128, 68, 79, 84, 83, 45, 49, 50, 51, 53, 128, 68, 79, 84, 83, 45, 49, + 50, 51, 54, 128, 68, 79, 84, 83, 45, 49, 50, 51, 55, 128, 68, 79, 84, 83, + 45, 49, 50, 51, 56, 128, 68, 79, 84, 83, 45, 49, 50, 52, 53, 128, 68, 79, + 84, 83, 45, 49, 50, 52, 54, 128, 68, 79, 84, 83, 45, 49, 50, 52, 55, 128, + 68, 79, 84, 83, 45, 49, 50, 52, 56, 128, 68, 79, 84, 83, 45, 49, 50, 53, + 54, 128, 68, 79, 84, 83, 45, 49, 50, 53, 55, 128, 68, 79, 84, 83, 45, 49, + 50, 53, 56, 128, 68, 79, 84, 83, 45, 49, 50, 54, 55, 128, 68, 79, 84, 83, + 45, 49, 50, 54, 56, 128, 68, 79, 84, 83, 45, 49, 50, 55, 56, 128, 68, 79, + 84, 83, 45, 49, 51, 52, 53, 128, 68, 79, 84, 83, 45, 49, 51, 52, 54, 128, + 68, 79, 84, 83, 45, 49, 51, 52, 55, 128, 68, 79, 84, 83, 45, 49, 51, 52, + 56, 128, 68, 79, 84, 83, 45, 49, 51, 53, 54, 128, 68, 79, 84, 83, 45, 49, + 51, 53, 55, 128, 68, 79, 84, 83, 45, 49, 51, 53, 56, 128, 68, 79, 84, 83, + 45, 49, 51, 54, 55, 128, 68, 79, 84, 83, 45, 49, 51, 54, 56, 128, 68, 79, + 84, 83, 45, 49, 51, 55, 56, 128, 68, 79, 84, 83, 45, 49, 52, 53, 54, 128, + 68, 79, 84, 83, 45, 49, 52, 53, 55, 128, 68, 79, 84, 83, 45, 49, 52, 53, + 56, 128, 68, 79, 84, 83, 45, 49, 52, 54, 55, 128, 68, 79, 84, 83, 45, 49, + 52, 54, 56, 128, 68, 79, 84, 83, 45, 49, 52, 55, 56, 128, 68, 79, 84, 83, + 45, 49, 53, 54, 55, 128, 68, 79, 84, 83, 45, 49, 53, 54, 56, 128, 68, 79, + 84, 83, 45, 49, 53, 55, 56, 128, 68, 79, 84, 83, 45, 49, 54, 55, 56, 128, + 68, 79, 84, 83, 45, 50, 51, 52, 53, 128, 68, 79, 84, 83, 45, 50, 51, 52, + 54, 128, 68, 79, 84, 83, 45, 50, 51, 52, 55, 128, 68, 79, 84, 83, 45, 50, + 51, 52, 56, 128, 68, 79, 84, 83, 45, 50, 51, 53, 54, 128, 68, 79, 84, 83, + 45, 50, 51, 53, 55, 128, 68, 79, 84, 83, 45, 50, 51, 53, 56, 128, 68, 79, + 84, 83, 45, 50, 51, 54, 55, 128, 68, 79, 84, 83, 45, 50, 51, 54, 56, 128, + 68, 79, 84, 83, 45, 50, 51, 55, 56, 128, 68, 79, 84, 83, 45, 50, 52, 53, + 54, 128, 68, 79, 84, 83, 45, 50, 52, 53, 55, 128, 68, 79, 84, 83, 45, 50, + 52, 53, 56, 128, 68, 79, 84, 83, 45, 50, 52, 54, 55, 128, 68, 79, 84, 83, + 45, 50, 52, 54, 56, 128, 68, 79, 84, 83, 45, 50, 52, 55, 56, 128, 68, 79, + 84, 83, 45, 50, 53, 54, 55, 128, 68, 79, 84, 83, 45, 50, 53, 54, 56, 128, + 68, 79, 84, 83, 45, 50, 53, 55, 56, 128, 68, 79, 84, 83, 45, 50, 54, 55, + 56, 128, 68, 79, 84, 83, 45, 51, 52, 53, 54, 128, 68, 79, 84, 83, 45, 51, + 52, 53, 55, 128, 68, 79, 84, 83, 45, 51, 52, 53, 56, 128, 68, 79, 84, 83, + 45, 51, 52, 54, 55, 128, 68, 79, 84, 83, 45, 51, 52, 54, 56, 128, 68, 79, + 84, 83, 45, 51, 52, 55, 56, 128, 68, 79, 84, 83, 45, 51, 53, 54, 55, 128, + 68, 79, 84, 83, 45, 51, 53, 54, 56, 128, 68, 79, 84, 83, 45, 51, 53, 55, + 56, 128, 68, 79, 84, 83, 45, 51, 54, 55, 56, 128, 68, 79, 84, 83, 45, 52, + 53, 54, 55, 128, 68, 79, 84, 83, 45, 52, 53, 54, 56, 128, 68, 79, 84, 83, + 45, 52, 53, 55, 56, 128, 68, 79, 84, 83, 45, 52, 54, 55, 56, 128, 68, 79, + 84, 83, 45, 53, 54, 55, 56, 128, 69, 69, 66, 69, 69, 70, 73, 76, 73, 128, + 69, 78, 65, 82, 77, 79, 78, 73, 79, 211, 69, 78, 68, 79, 70, 79, 78, 79, + 78, 128, 69, 83, 84, 73, 77, 65, 84, 69, 83, 128, 69, 88, 67, 69, 76, 76, + 69, 78, 84, 128, 69, 88, 84, 82, 65, 45, 72, 73, 71, 200, 69, 89, 66, 69, + 89, 70, 73, 76, 73, 128, 70, 82, 73, 67, 65, 84, 73, 86, 69, 128, 71, 78, + 65, 86, 73, 89, 65, 78, 73, 128, 71, 79, 82, 71, 79, 84, 69, 82, 73, 128, + 71, 85, 82, 65, 77, 85, 84, 79, 78, 128, 72, 69, 75, 85, 84, 65, 65, 82, + 85, 128, 72, 79, 77, 79, 84, 72, 69, 84, 73, 195, 72, 89, 83, 84, 69, 82, + 69, 83, 73, 211, 73, 76, 85, 85, 89, 65, 78, 78, 65, 128, 73, 77, 73, 70, + 84, 72, 79, 82, 65, 128, 73, 78, 67, 79, 77, 80, 76, 69, 84, 197, 73, 78, + 67, 82, 69, 77, 69, 78, 84, 128, 73, 78, 68, 85, 83, 84, 82, 73, 65, 204, + 73, 82, 85, 85, 89, 65, 78, 78, 65, 128, 74, 69, 82, 85, 83, 65, 76, 69, + 77, 128, 75, 65, 84, 65, 86, 65, 83, 77, 65, 128, 75, 69, 78, 84, 73, 77, + 65, 84, 65, 128, 75, 73, 82, 79, 87, 65, 84, 84, 79, 128, 75, 82, 65, 84, + 73, 77, 65, 84, 65, 128, 75, 85, 82, 85, 90, 69, 73, 82, 79, 128, 76, 72, + 65, 86, 73, 89, 65, 78, 73, 128, 76, 73, 71, 72, 84, 78, 73, 78, 71, 128, + 77, 65, 73, 84, 65, 73, 75, 72, 85, 128, 77, 65, 84, 69, 82, 73, 65, 76, + 83, 128, 77, 69, 84, 79, 66, 69, 76, 85, 83, 128, 77, 73, 82, 73, 66, 65, + 65, 82, 85, 128, 77, 79, 78, 79, 83, 84, 65, 66, 76, 197, 77, 79, 79, 83, + 69, 45, 67, 82, 69, 197, 78, 73, 71, 71, 65, 72, 73, 84, 65, 128, 79, 65, + 66, 79, 65, 70, 73, 76, 73, 128, 79, 79, 66, 79, 79, 70, 73, 76, 73, 128, + 79, 82, 84, 72, 79, 71, 79, 78, 65, 204, 80, 65, 73, 89, 65, 78, 78, 79, + 73, 128, 80, 65, 82, 65, 71, 82, 65, 80, 72, 128, 80, 73, 65, 83, 85, 84, + 79, 82, 85, 128, 80, 73, 84, 67, 72, 70, 79, 82, 75, 128, 80, 73, 90, 90, + 73, 67, 65, 84, 79, 128, 80, 76, 85, 83, 45, 77, 73, 78, 85, 211, 80, 79, + 82, 82, 69, 67, 84, 85, 83, 128, 80, 82, 79, 84, 79, 86, 65, 82, 89, 211, + 81, 85, 65, 84, 69, 82, 78, 73, 79, 206, 81, 85, 69, 83, 84, 73, 79, 78, + 69, 196, 81, 85, 83, 72, 83, 72, 65, 89, 65, 128, 82, 69, 71, 73, 83, 84, + 69, 82, 69, 196, 82, 69, 76, 65, 84, 73, 79, 78, 65, 204, 82, 69, 80, 82, + 69, 83, 69, 78, 84, 128, 82, 69, 83, 85, 80, 73, 78, 85, 83, 128, 82, 73, + 71, 72, 84, 45, 83, 73, 68, 197, 83, 67, 65, 78, 68, 73, 67, 85, 83, 128, + 83, 69, 80, 84, 69, 77, 66, 69, 82, 128, 83, 72, 65, 86, 73, 89, 65, 78, + 73, 128, 83, 72, 79, 85, 76, 68, 69, 82, 69, 196, 83, 73, 88, 45, 80, 69, + 82, 45, 69, 205, 83, 73, 88, 45, 83, 84, 82, 73, 78, 199, 83, 84, 82, 79, + 75, 69, 45, 49, 48, 128, 83, 84, 82, 79, 75, 69, 45, 49, 49, 128, 83, 85, + 66, 83, 84, 73, 84, 85, 84, 197, 83, 89, 82, 77, 65, 84, 73, 75, 73, 128, + 84, 72, 69, 82, 69, 70, 79, 82, 69, 128, 84, 72, 82, 69, 69, 45, 76, 73, + 78, 197, 84, 82, 73, 70, 79, 76, 73, 65, 84, 197, 84, 82, 73, 70, 79, 78, + 73, 65, 83, 128, 84, 82, 73, 71, 79, 82, 71, 79, 78, 128, 86, 73, 83, 65, + 82, 71, 65, 89, 65, 128, 87, 79, 82, 68, 83, 80, 65, 67, 69, 128, 89, 80, + 79, 75, 82, 73, 83, 73, 83, 128, 85, 78, 68, 69, 82, 66, 65, 82, 128, 81, + 85, 79, 84, 65, 84, 73, 79, 206, 65, 83, 84, 69, 82, 73, 83, 75, 128, 79, + 82, 78, 65, 77, 69, 78, 84, 128, 86, 65, 82, 73, 65, 84, 73, 79, 206, 65, + 82, 67, 72, 65, 73, 79, 78, 128, 68, 73, 65, 69, 82, 69, 83, 73, 211, 66, + 76, 65, 67, 75, 70, 79, 79, 212, 83, 85, 66, 83, 67, 82, 73, 80, 212, 68, + 69, 78, 84, 73, 83, 84, 82, 217, 68, 73, 65, 76, 89, 84, 73, 75, 193, 73, + 78, 84, 69, 71, 82, 65, 76, 128, 86, 69, 82, 84, 73, 67, 65, 76, 128, 82, + 69, 67, 89, 67, 76, 73, 78, 199, 65, 78, 85, 83, 86, 65, 82, 65, 128, 65, + 66, 75, 72, 65, 83, 73, 65, 206, 68, 79, 68, 69, 75, 65, 84, 65, 128, 81, + 85, 65, 68, 82, 65, 78, 84, 128, 81, 85, 65, 68, 82, 85, 80, 76, 197, 68, + 73, 65, 84, 79, 78, 73, 75, 201, 69, 76, 76, 73, 80, 83, 73, 83, 128, 69, + 78, 67, 76, 79, 83, 73, 78, 199, 79, 86, 69, 82, 76, 73, 78, 69, 128, 80, + 76, 65, 83, 84, 73, 67, 83, 128, 82, 69, 84, 82, 79, 70, 76, 69, 216, 73, + 84, 69, 82, 65, 84, 73, 79, 206, 78, 79, 84, 69, 72, 69, 65, 68, 128, 78, + 85, 77, 69, 82, 65, 84, 79, 210, 84, 72, 79, 85, 83, 65, 78, 68, 128, 69, + 73, 71, 72, 84, 69, 69, 78, 128, 70, 79, 85, 82, 84, 69, 69, 78, 128, 78, + 73, 78, 69, 84, 69, 69, 78, 128, 84, 72, 73, 82, 84, 69, 69, 78, 128, 68, + 73, 65, 71, 79, 78, 65, 76, 128, 70, 76, 79, 82, 69, 84, 84, 69, 128, 73, + 68, 69, 78, 84, 73, 67, 65, 204, 75, 69, 78, 84, 73, 77, 65, 84, 193, 80, + 65, 82, 65, 71, 82, 65, 80, 200, 82, 69, 76, 65, 84, 73, 79, 78, 128, 83, + 67, 73, 83, 83, 79, 82, 83, 128, 83, 85, 80, 69, 82, 83, 69, 84, 128, 65, + 86, 65, 71, 82, 65, 72, 65, 128, 68, 68, 65, 89, 65, 78, 78, 65, 128, 68, + 69, 80, 65, 82, 84, 73, 78, 199, 70, 65, 78, 69, 82, 79, 83, 73, 211, 73, + 78, 70, 73, 78, 73, 84, 89, 128, 77, 85, 76, 84, 73, 77, 65, 80, 128, 77, + 85, 85, 82, 68, 72, 65, 74, 193, 80, 65, 82, 65, 76, 76, 69, 76, 128, 80, + 82, 69, 67, 69, 68, 69, 83, 128, 83, 73, 88, 84, 69, 69, 78, 84, 200, 83, + 80, 72, 69, 82, 73, 67, 65, 204, 83, 85, 66, 76, 73, 78, 69, 65, 210, 83, + 85, 67, 67, 69, 69, 68, 83, 128, 83, 85, 77, 77, 65, 84, 73, 79, 206, 84, + 69, 76, 69, 80, 72, 79, 78, 197, 84, 72, 79, 85, 83, 65, 78, 68, 211, 89, + 69, 83, 73, 69, 85, 78, 71, 128, 65, 76, 76, 73, 65, 78, 67, 69, 128, 67, + 79, 78, 83, 84, 65, 78, 84, 128, 68, 73, 70, 79, 78, 73, 65, 83, 128, 68, + 73, 71, 82, 65, 77, 77, 79, 211, 70, 73, 83, 72, 72, 79, 79, 75, 128, 70, + 76, 65, 84, 84, 69, 78, 69, 196, 71, 65, 82, 83, 72, 85, 78, 73, 128, 71, + 76, 73, 83, 83, 65, 78, 68, 207, 71, 82, 69, 71, 79, 82, 73, 65, 206, 73, + 78, 83, 69, 82, 84, 73, 79, 206, 73, 78, 86, 73, 83, 73, 66, 76, 197, 73, + 83, 45, 80, 73, 76, 76, 65, 128, 77, 79, 85, 78, 84, 65, 73, 78, 128, 79, + 86, 69, 82, 82, 73, 68, 69, 128, 79, 89, 82, 65, 78, 73, 83, 77, 193, 80, + 69, 68, 69, 83, 84, 65, 76, 128, 80, 78, 69, 85, 77, 65, 84, 65, 128, 80, + 82, 79, 76, 79, 78, 71, 69, 196, 80, 82, 79, 80, 69, 76, 76, 69, 210, 82, + 69, 83, 79, 85, 82, 67, 69, 128, 82, 69, 86, 69, 82, 83, 69, 68, 128, 83, + 69, 77, 73, 86, 79, 87, 69, 204, 83, 69, 80, 65, 82, 65, 84, 79, 210, 83, + 85, 66, 71, 82, 79, 85, 80, 128, 83, 87, 65, 80, 80, 73, 78, 71, 128, 83, + 89, 77, 77, 69, 84, 82, 73, 195, 84, 82, 73, 83, 73, 77, 79, 85, 128, 84, + 84, 65, 89, 65, 78, 78, 65, 128, 85, 78, 68, 69, 82, 76, 73, 78, 197, 85, + 78, 73, 86, 69, 82, 83, 65, 204, 65, 68, 68, 82, 69, 83, 83, 69, 196, 65, + 69, 69, 89, 65, 78, 78, 65, 128, 65, 73, 82, 80, 76, 65, 78, 69, 128, 65, + 78, 85, 68, 65, 84, 84, 65, 128, 65, 80, 79, 68, 69, 88, 73, 65, 128, 65, + 80, 79, 84, 72, 69, 77, 65, 128, 65, 81, 85, 65, 82, 73, 85, 83, 128, 65, + 82, 65, 69, 65, 45, 69, 79, 128, 65, 82, 71, 79, 84, 69, 82, 73, 128, 65, + 82, 73, 83, 84, 69, 82, 65, 128, 65, 82, 82, 79, 87, 72, 69, 65, 196, 65, + 83, 67, 69, 78, 68, 73, 78, 199, 65, 83, 84, 69, 82, 73, 83, 75, 211, 65, + 83, 84, 69, 82, 73, 83, 77, 128, 66, 65, 67, 75, 83, 76, 65, 83, 200, 66, + 73, 79, 72, 65, 90, 65, 82, 196, 66, 73, 83, 69, 67, 84, 73, 78, 199, 66, + 85, 76, 76, 83, 69, 89, 69, 128, 66, 85, 83, 83, 89, 69, 82, 85, 128, 67, + 65, 68, 85, 67, 69, 85, 83, 128, 67, 65, 85, 76, 68, 82, 79, 78, 128, 67, + 72, 65, 77, 73, 76, 79, 78, 128, 67, 72, 65, 84, 84, 65, 87, 65, 128, 67, + 73, 86, 73, 76, 73, 65, 78, 128, 67, 76, 73, 77, 65, 67, 85, 83, 128, 67, + 79, 78, 71, 82, 85, 69, 78, 212, 67, 79, 78, 74, 85, 71, 65, 84, 197, 67, + 79, 78, 84, 79, 85, 82, 69, 196, 67, 79, 80, 89, 82, 73, 71, 72, 212, 67, + 82, 69, 83, 67, 69, 78, 84, 128, 68, 65, 77, 77, 65, 84, 65, 78, 128, 68, + 65, 86, 73, 89, 65, 78, 73, 128, 68, 69, 67, 69, 77, 66, 69, 82, 128, 68, + 69, 76, 73, 77, 73, 84, 69, 210, 68, 73, 70, 84, 79, 71, 71, 79, 211, 68, + 73, 71, 79, 82, 71, 79, 78, 128, 68, 73, 77, 69, 78, 83, 73, 79, 206, 68, + 79, 84, 83, 45, 49, 50, 51, 128, 68, 79, 84, 83, 45, 49, 50, 52, 128, 68, + 79, 84, 83, 45, 49, 50, 53, 128, 68, 79, 84, 83, 45, 49, 50, 54, 128, 68, + 79, 84, 83, 45, 49, 50, 55, 128, 68, 79, 84, 83, 45, 49, 50, 56, 128, 68, + 79, 84, 83, 45, 49, 51, 52, 128, 68, 79, 84, 83, 45, 49, 51, 53, 128, 68, + 79, 84, 83, 45, 49, 51, 54, 128, 68, 79, 84, 83, 45, 49, 51, 55, 128, 68, + 79, 84, 83, 45, 49, 51, 56, 128, 68, 79, 84, 83, 45, 49, 52, 53, 128, 68, + 79, 84, 83, 45, 49, 52, 54, 128, 68, 79, 84, 83, 45, 49, 52, 55, 128, 68, + 79, 84, 83, 45, 49, 52, 56, 128, 68, 79, 84, 83, 45, 49, 53, 54, 128, 68, + 79, 84, 83, 45, 49, 53, 55, 128, 68, 79, 84, 83, 45, 49, 53, 56, 128, 68, + 79, 84, 83, 45, 49, 54, 55, 128, 68, 79, 84, 83, 45, 49, 54, 56, 128, 68, + 79, 84, 83, 45, 49, 55, 56, 128, 68, 79, 84, 83, 45, 50, 51, 52, 128, 68, + 79, 84, 83, 45, 50, 51, 53, 128, 68, 79, 84, 83, 45, 50, 51, 54, 128, 68, + 79, 84, 83, 45, 50, 51, 55, 128, 68, 79, 84, 83, 45, 50, 51, 56, 128, 68, + 79, 84, 83, 45, 50, 52, 53, 128, 68, 79, 84, 83, 45, 50, 52, 54, 128, 68, + 79, 84, 83, 45, 50, 52, 55, 128, 68, 79, 84, 83, 45, 50, 52, 56, 128, 68, + 79, 84, 83, 45, 50, 53, 54, 128, 68, 79, 84, 83, 45, 50, 53, 55, 128, 68, + 79, 84, 83, 45, 50, 53, 56, 128, 68, 79, 84, 83, 45, 50, 54, 55, 128, 68, + 79, 84, 83, 45, 50, 54, 56, 128, 68, 79, 84, 83, 45, 50, 55, 56, 128, 68, + 79, 84, 83, 45, 51, 52, 53, 128, 68, 79, 84, 83, 45, 51, 52, 54, 128, 68, + 79, 84, 83, 45, 51, 52, 55, 128, 68, 79, 84, 83, 45, 51, 52, 56, 128, 68, + 79, 84, 83, 45, 51, 53, 54, 128, 68, 79, 84, 83, 45, 51, 53, 55, 128, 68, + 79, 84, 83, 45, 51, 53, 56, 128, 68, 79, 84, 83, 45, 51, 54, 55, 128, 68, + 79, 84, 83, 45, 51, 54, 56, 128, 68, 79, 84, 83, 45, 51, 55, 56, 128, 68, + 79, 84, 83, 45, 52, 53, 54, 128, 68, 79, 84, 83, 45, 52, 53, 55, 128, 68, + 79, 84, 83, 45, 52, 53, 56, 128, 68, 79, 84, 83, 45, 52, 54, 55, 128, 68, + 79, 84, 83, 45, 52, 54, 56, 128, 68, 79, 84, 83, 45, 52, 55, 56, 128, 68, + 79, 84, 83, 45, 53, 54, 55, 128, 68, 79, 84, 83, 45, 53, 54, 56, 128, 68, + 79, 84, 83, 45, 53, 55, 56, 128, 68, 79, 84, 83, 45, 54, 55, 56, 128, 68, + 79, 84, 84, 69, 68, 45, 76, 128, 68, 79, 84, 84, 69, 68, 45, 78, 128, 68, + 79, 84, 84, 69, 68, 45, 80, 128, 69, 78, 86, 69, 76, 79, 80, 69, 128, 69, + 80, 69, 71, 69, 82, 77, 65, 128, 69, 83, 84, 73, 77, 65, 84, 69, 196, 69, + 83, 85, 75, 85, 85, 68, 79, 128, 69, 88, 84, 82, 65, 45, 76, 79, 215, 70, + 65, 84, 72, 65, 84, 65, 78, 128, 70, 69, 66, 82, 85, 65, 82, 89, 128, 70, + 69, 83, 84, 73, 86, 65, 76, 128, 70, 73, 71, 85, 82, 69, 45, 49, 128, 70, + 73, 71, 85, 82, 69, 45, 50, 128, 70, 73, 71, 85, 82, 69, 45, 51, 128, 70, + 73, 86, 69, 45, 76, 73, 78, 197, 70, 79, 85, 82, 45, 76, 73, 78, 197, 70, + 82, 65, 71, 77, 69, 78, 84, 128, 70, 82, 65, 71, 82, 65, 78, 84, 128, 70, + 85, 78, 67, 84, 73, 79, 78, 128, 71, 69, 78, 73, 84, 73, 86, 69, 128, 71, + 69, 79, 77, 69, 84, 82, 73, 195, 72, 65, 78, 45, 65, 75, 65, 84, 128, 72, + 65, 82, 77, 79, 78, 73, 67, 128, 72, 69, 82, 77, 73, 84, 73, 65, 206, 72, + 85, 65, 82, 65, 68, 68, 79, 128, 73, 76, 85, 89, 65, 78, 78, 65, 128, 73, + 77, 73, 70, 79, 78, 79, 78, 128, 73, 78, 67, 76, 85, 68, 73, 78, 199, 73, + 78, 67, 82, 69, 65, 83, 69, 211, 73, 82, 85, 89, 65, 78, 78, 65, 128, 74, + 65, 86, 73, 89, 65, 78, 73, 128, 75, 65, 83, 82, 65, 84, 65, 78, 128, 75, + 65, 84, 72, 73, 83, 84, 73, 128, 75, 69, 89, 66, 79, 65, 82, 68, 128, 75, + 79, 78, 84, 69, 86, 77, 65, 128, 75, 82, 69, 77, 65, 83, 84, 73, 128, 76, + 69, 70, 84, 45, 83, 73, 68, 197, 76, 79, 67, 65, 84, 73, 86, 69, 128, 76, + 79, 82, 82, 65, 73, 78, 69, 128, 77, 65, 72, 65, 80, 65, 75, 72, 128, 77, + 65, 73, 77, 65, 76, 65, 73, 128, 77, 65, 73, 89, 65, 77, 79, 75, 128, 77, + 65, 83, 67, 85, 76, 73, 78, 197, 77, 69, 68, 73, 67, 73, 78, 69, 128, 77, + 73, 78, 73, 83, 84, 69, 82, 128, 77, 85, 76, 84, 73, 83, 69, 84, 128, 78, + 73, 75, 72, 65, 72, 73, 84, 128, 78, 79, 82, 84, 72, 87, 69, 83, 212, 78, + 79, 86, 69, 77, 66, 69, 82, 128, 79, 86, 69, 82, 76, 65, 73, 68, 128, 80, + 65, 65, 83, 69, 78, 84, 79, 128, 80, 65, 73, 82, 84, 72, 82, 65, 128, 80, + 65, 76, 79, 67, 72, 75, 65, 128, 80, 65, 77, 85, 68, 80, 79, 68, 128, 80, + 65, 82, 73, 67, 72, 79, 78, 128, 80, 65, 86, 73, 89, 65, 78, 73, 128, 80, + 69, 76, 65, 83, 84, 79, 78, 128, 80, 73, 84, 67, 72, 70, 79, 82, 203, 80, + 79, 82, 82, 69, 67, 84, 85, 211, 80, 82, 79, 70, 79, 85, 78, 68, 128, 80, + 83, 73, 70, 73, 83, 84, 79, 206, 81, 65, 73, 82, 84, 72, 82, 65, 128, 81, + 85, 65, 82, 84, 69, 82, 83, 128, 81, 85, 69, 83, 84, 73, 79, 78, 128, 82, + 69, 67, 79, 82, 68, 69, 82, 128, 82, 69, 67, 79, 82, 68, 73, 78, 199, 82, + 69, 67, 84, 65, 78, 71, 76, 197, 82, 69, 70, 69, 82, 69, 78, 67, 197, 82, + 69, 76, 73, 71, 73, 79, 78, 128, 82, 69, 78, 84, 79, 71, 69, 78, 128, 82, + 69, 83, 80, 79, 78, 83, 69, 128, 82, 73, 71, 72, 84, 72, 65, 78, 196, 82, + 85, 75, 75, 65, 75, 72, 65, 128, 83, 65, 78, 84, 73, 73, 77, 85, 128, 83, + 65, 88, 73, 77, 65, 84, 65, 128, 83, 67, 65, 78, 68, 73, 67, 85, 211, 83, + 67, 79, 82, 80, 73, 85, 83, 128, 83, 69, 77, 73, 67, 79, 76, 79, 206, 83, + 69, 86, 69, 78, 84, 69, 69, 206, 83, 73, 67, 75, 78, 69, 83, 83, 128, 83, + 84, 79, 80, 80, 73, 78, 71, 128, 83, 84, 82, 69, 84, 67, 72, 69, 196, 83, + 84, 82, 79, 75, 69, 45, 49, 128, 83, 84, 82, 79, 75, 69, 45, 50, 128, 83, + 84, 82, 79, 75, 69, 45, 51, 128, 83, 84, 82, 79, 75, 69, 45, 52, 128, 83, + 84, 82, 79, 75, 69, 45, 53, 128, 83, 84, 82, 79, 75, 69, 45, 54, 128, 83, + 84, 82, 79, 75, 69, 45, 55, 128, 83, 84, 82, 79, 75, 69, 45, 56, 128, 83, + 84, 82, 79, 75, 69, 45, 57, 128, 83, 85, 73, 84, 65, 66, 76, 69, 128, 83, + 85, 82, 82, 79, 85, 78, 68, 128, 83, 89, 77, 77, 69, 84, 82, 89, 128, 83, + 89, 78, 68, 69, 83, 77, 79, 211, 84, 65, 86, 73, 89, 65, 78, 73, 128, 84, + 69, 84, 82, 65, 80, 76, 73, 128, 84, 79, 82, 67, 85, 76, 85, 83, 128, 84, + 82, 79, 77, 73, 75, 79, 78, 128, 84, 82, 85, 78, 67, 65, 84, 69, 196, 85, + 73, 76, 76, 69, 65, 78, 78, 128, 85, 77, 66, 82, 69, 76, 76, 65, 128, 85, + 78, 68, 69, 82, 68, 79, 84, 128, 85, 78, 68, 69, 82, 84, 73, 69, 128, 86, + 69, 82, 83, 73, 67, 76, 69, 128, 87, 65, 83, 65, 76, 76, 65, 77, 128, 89, + 65, 77, 65, 75, 75, 65, 78, 128, 89, 80, 79, 75, 82, 73, 83, 73, 211, 90, + 65, 86, 73, 89, 65, 78, 73, 128, 78, 69, 71, 65, 84, 73, 86, 197, 73, 78, + 86, 69, 82, 84, 69, 196, 67, 69, 68, 73, 76, 76, 65, 128, 84, 82, 73, 65, + 78, 71, 76, 197, 78, 79, 84, 69, 72, 69, 65, 196, 83, 85, 80, 69, 82, 83, + 69, 212, 84, 65, 71, 66, 65, 78, 87, 193, 70, 82, 65, 67, 84, 73, 79, + 206, 81, 85, 65, 68, 82, 65, 78, 212, 68, 73, 65, 71, 79, 78, 65, 204, + 81, 85, 69, 83, 84, 73, 79, 206, 85, 80, 83, 73, 76, 79, 78, 128, 77, 65, + 82, 84, 89, 82, 73, 193, 79, 86, 69, 82, 66, 65, 82, 128, 79, 86, 69, 82, + 76, 65, 89, 128, 68, 73, 65, 77, 79, 78, 68, 128, 69, 80, 83, 73, 76, 79, + 78, 128, 72, 65, 78, 71, 90, 72, 79, 213, 73, 78, 84, 69, 71, 82, 65, + 204, 77, 69, 65, 83, 85, 82, 69, 196, 79, 77, 73, 67, 82, 79, 78, 128, + 84, 79, 82, 84, 79, 73, 83, 197, 79, 82, 78, 65, 77, 69, 78, 212, 69, 88, + 84, 69, 78, 68, 69, 196, 72, 65, 82, 80, 79, 79, 78, 128, 80, 82, 69, 67, + 69, 68, 69, 211, 83, 79, 76, 73, 68, 85, 83, 128, 83, 85, 67, 67, 69, 69, + 68, 211, 67, 79, 78, 84, 65, 73, 78, 211, 86, 73, 83, 65, 82, 71, 65, + 128, 67, 82, 79, 83, 83, 73, 78, 199, 72, 85, 78, 68, 82, 69, 68, 128, + 83, 73, 77, 65, 78, 83, 73, 211, 68, 73, 71, 82, 65, 80, 72, 128, 66, 65, + 82, 76, 73, 78, 69, 128, 68, 73, 86, 73, 83, 73, 79, 206, 73, 79, 84, 73, + 70, 73, 69, 196, 80, 65, 82, 65, 76, 76, 69, 204, 83, 73, 88, 84, 69, 69, + 78, 128, 83, 81, 85, 65, 82, 69, 68, 128, 83, 85, 66, 71, 82, 79, 85, + 208, 83, 85, 82, 82, 79, 85, 78, 196, 70, 73, 70, 84, 69, 69, 78, 128, + 79, 80, 69, 82, 65, 84, 79, 210, 79, 82, 73, 71, 73, 78, 65, 204, 68, 73, + 65, 83, 84, 79, 76, 201, 70, 65, 84, 72, 65, 84, 65, 206, 80, 79, 73, 78, + 84, 69, 82, 128, 83, 84, 82, 65, 73, 71, 72, 212, 85, 80, 87, 65, 82, 68, + 83, 128, 66, 65, 89, 65, 78, 78, 65, 128, 67, 72, 82, 79, 78, 79, 78, + 128, 68, 73, 71, 79, 82, 71, 79, 206, 69, 73, 71, 72, 84, 72, 83, 128, + 70, 73, 78, 71, 69, 82, 69, 196, 71, 65, 89, 65, 78, 78, 65, 128, 72, 65, + 82, 75, 76, 69, 65, 206, 74, 65, 89, 65, 78, 78, 65, 128, 75, 79, 82, 79, + 78, 73, 83, 128, 76, 69, 65, 84, 72, 69, 82, 128, 76, 79, 90, 69, 78, 71, + 69, 128, 77, 65, 75, 83, 85, 82, 65, 128, 78, 79, 45, 66, 82, 69, 65, + 203, 80, 73, 78, 87, 72, 69, 69, 204, 82, 69, 80, 69, 65, 84, 69, 196, + 83, 65, 89, 65, 78, 78, 65, 128, 83, 69, 76, 69, 67, 84, 79, 210, 83, 81, + 85, 73, 71, 71, 76, 197, 84, 69, 84, 65, 82, 84, 79, 211, 84, 82, 79, 77, + 73, 75, 79, 206, 65, 67, 84, 73, 86, 65, 84, 197, 65, 67, 84, 85, 65, 76, + 76, 217, 65, 80, 79, 68, 69, 82, 77, 193, 65, 82, 73, 83, 84, 69, 82, + 193, 65, 83, 84, 69, 82, 73, 83, 203, 66, 69, 84, 87, 69, 69, 78, 128, + 66, 73, 76, 65, 66, 73, 65, 204, 67, 65, 89, 65, 78, 78, 65, 128, 67, 69, + 73, 76, 73, 78, 71, 128, 67, 72, 79, 82, 69, 86, 77, 193, 67, 72, 82, 79, + 78, 79, 85, 128, 67, 76, 79, 84, 72, 69, 83, 128, 68, 65, 77, 77, 65, 84, + 65, 206, 68, 69, 89, 84, 69, 82, 79, 211, 68, 73, 71, 65, 77, 77, 65, + 128, 68, 73, 83, 73, 77, 79, 85, 128, 69, 77, 80, 72, 65, 83, 73, 211, + 70, 69, 77, 73, 78, 73, 78, 197, 73, 78, 72, 69, 82, 69, 78, 212, 73, 78, + 84, 69, 82, 73, 79, 210, 73, 90, 72, 73, 84, 83, 65, 128, 75, 65, 83, 82, + 65, 84, 65, 206, 75, 65, 89, 65, 78, 78, 65, 128, 75, 79, 77, 66, 85, 86, + 65, 128, 76, 65, 89, 65, 78, 78, 65, 128, 76, 79, 71, 79, 84, 89, 80, + 197, 77, 85, 76, 84, 73, 83, 69, 212, 78, 65, 89, 65, 78, 78, 65, 128, + 80, 65, 89, 65, 78, 78, 65, 128, 80, 69, 68, 69, 83, 84, 65, 204, 80, 69, + 84, 65, 76, 76, 69, 196, 81, 85, 65, 82, 84, 69, 82, 211, 82, 71, 89, 73, + 78, 71, 83, 128, 83, 69, 77, 73, 83, 79, 70, 212, 83, 69, 77, 75, 65, 84, + 72, 128, 83, 72, 65, 80, 73, 78, 71, 128, 83, 79, 67, 73, 69, 84, 89, + 128, 83, 80, 65, 82, 75, 76, 69, 128, 83, 80, 69, 67, 73, 65, 76, 128, + 83, 84, 65, 78, 68, 65, 82, 196, 83, 84, 82, 79, 75, 69, 83, 128, 84, 72, + 69, 83, 69, 79, 83, 128, 84, 72, 79, 85, 83, 65, 78, 196, 85, 66, 65, 68, + 65, 77, 65, 128, 65, 65, 89, 65, 78, 78, 65, 128, 65, 66, 65, 70, 73, 76, + 73, 128, 65, 69, 89, 65, 78, 78, 65, 128, 65, 73, 89, 65, 78, 78, 65, + 128, 65, 76, 86, 69, 79, 76, 65, 210, 65, 78, 71, 83, 84, 82, 79, 205, + 65, 78, 71, 85, 76, 65, 82, 128, 65, 78, 85, 83, 86, 65, 82, 193, 65, 80, + 79, 84, 72, 69, 83, 128, 65, 82, 65, 69, 65, 45, 73, 128, 65, 82, 65, 69, + 65, 45, 85, 128, 65, 82, 67, 72, 65, 73, 79, 206, 65, 85, 89, 65, 78, 78, + 65, 128, 66, 65, 65, 82, 69, 82, 85, 128, 66, 65, 73, 82, 75, 65, 78, + 128, 66, 65, 82, 82, 69, 75, 72, 128, 66, 69, 67, 65, 85, 83, 69, 128, + 66, 69, 76, 71, 84, 72, 79, 210, 66, 69, 82, 75, 65, 78, 65, 206, 66, 73, + 68, 69, 78, 84, 65, 204, 66, 79, 85, 78, 68, 65, 82, 217, 66, 82, 73, 83, + 84, 76, 69, 128, 67, 65, 69, 83, 85, 82, 65, 128, 67, 65, 80, 73, 84, 65, + 76, 128, 67, 65, 82, 82, 73, 65, 71, 197, 67, 69, 76, 83, 73, 85, 83, + 128, 67, 72, 65, 77, 73, 76, 73, 128, 67, 79, 77, 80, 65, 82, 69, 128, + 67, 79, 78, 83, 84, 65, 78, 212, 67, 79, 82, 78, 69, 82, 83, 128, 67, 79, + 82, 82, 69, 67, 84, 128, 67, 82, 85, 90, 69, 73, 82, 207, 67, 85, 83, 84, + 79, 77, 69, 210, 67, 87, 69, 79, 82, 84, 72, 128, 68, 65, 71, 65, 76, 71, + 65, 128, 68, 69, 89, 84, 69, 82, 79, 213, 68, 73, 65, 77, 69, 84, 69, + 210, 68, 73, 65, 84, 79, 78, 79, 206, 68, 73, 71, 82, 65, 77, 77, 193, + 68, 73, 80, 76, 79, 85, 78, 128, 68, 73, 82, 69, 67, 84, 76, 217, 68, 73, + 86, 73, 68, 69, 83, 128, 68, 79, 84, 83, 45, 49, 50, 128, 68, 79, 84, 83, + 45, 49, 51, 128, 68, 79, 84, 83, 45, 49, 52, 128, 68, 79, 84, 83, 45, 49, + 53, 128, 68, 79, 84, 83, 45, 49, 54, 128, 68, 79, 84, 83, 45, 49, 55, + 128, 68, 79, 84, 83, 45, 49, 56, 128, 68, 79, 84, 83, 45, 50, 51, 128, + 68, 79, 84, 83, 45, 50, 52, 128, 68, 79, 84, 83, 45, 50, 53, 128, 68, 79, + 84, 83, 45, 50, 54, 128, 68, 79, 84, 83, 45, 50, 55, 128, 68, 79, 84, 83, + 45, 50, 56, 128, 68, 79, 84, 83, 45, 51, 52, 128, 68, 79, 84, 83, 45, 51, + 53, 128, 68, 79, 84, 83, 45, 51, 54, 128, 68, 79, 84, 83, 45, 51, 55, + 128, 68, 79, 84, 83, 45, 51, 56, 128, 68, 79, 84, 83, 45, 52, 53, 128, + 68, 79, 84, 83, 45, 52, 54, 128, 68, 79, 84, 83, 45, 52, 55, 128, 68, 79, + 84, 83, 45, 52, 56, 128, 68, 79, 84, 83, 45, 53, 54, 128, 68, 79, 84, 83, + 45, 53, 55, 128, 68, 79, 84, 83, 45, 53, 56, 128, 68, 79, 84, 83, 45, 54, + 55, 128, 68, 79, 84, 83, 45, 54, 56, 128, 68, 79, 84, 83, 45, 55, 56, + 128, 68, 82, 65, 70, 84, 73, 78, 199, 69, 65, 66, 72, 65, 68, 72, 128, + 69, 65, 68, 72, 65, 68, 72, 128, 69, 66, 69, 70, 73, 76, 73, 128, 69, 73, + 71, 72, 84, 69, 69, 206, 69, 76, 65, 70, 82, 79, 78, 128, 69, 76, 69, 67, + 84, 82, 73, 195, 69, 78, 81, 85, 73, 82, 89, 128, 69, 78, 84, 69, 82, 73, + 78, 199, 69, 84, 78, 65, 72, 84, 65, 128, 69, 86, 69, 78, 73, 78, 71, + 128, 70, 65, 89, 65, 78, 78, 65, 128, 70, 69, 65, 84, 72, 69, 82, 128, + 70, 69, 82, 77, 65, 84, 65, 128, 70, 73, 83, 72, 69, 89, 69, 128, 70, 79, + 78, 71, 77, 65, 78, 128, 70, 79, 85, 82, 84, 69, 69, 206, 70, 82, 79, 87, + 78, 73, 78, 199, 71, 73, 82, 85, 68, 65, 65, 128, 71, 82, 65, 80, 72, 69, + 77, 197, 72, 65, 76, 65, 78, 84, 65, 128, 72, 65, 76, 66, 69, 82, 68, + 128, 72, 65, 89, 65, 78, 78, 65, 128, 72, 69, 65, 68, 73, 78, 71, 128, + 73, 45, 65, 82, 65, 69, 65, 128, 73, 66, 73, 70, 73, 76, 73, 128, 73, 67, + 72, 65, 68, 73, 78, 128, 73, 73, 89, 65, 78, 78, 65, 128, 73, 78, 70, 73, + 78, 73, 84, 217, 73, 78, 84, 69, 82, 69, 83, 212, 73, 79, 68, 72, 65, 68, + 72, 128, 74, 65, 78, 85, 65, 82, 89, 128, 74, 65, 80, 65, 78, 69, 83, + 197, 74, 85, 80, 73, 84, 69, 82, 128, 75, 65, 75, 65, 66, 65, 84, 128, + 75, 65, 82, 65, 84, 84, 79, 128, 75, 65, 82, 79, 82, 73, 73, 128, 75, 79, + 78, 84, 69, 86, 77, 193, 75, 79, 79, 77, 85, 85, 84, 128, 75, 85, 82, 79, + 79, 78, 69, 128, 76, 65, 78, 71, 85, 65, 71, 197, 76, 79, 67, 65, 84, 73, + 79, 206, 77, 65, 73, 75, 85, 82, 79, 128, 77, 65, 73, 77, 85, 65, 78, + 128, 77, 65, 78, 83, 89, 79, 78, 128, 77, 65, 82, 66, 85, 84, 65, 128, + 77, 65, 82, 67, 65, 84, 79, 128, 77, 65, 89, 65, 78, 78, 65, 128, 77, 69, + 71, 65, 84, 79, 78, 128, 77, 69, 82, 67, 85, 82, 89, 128, 77, 73, 75, 85, + 82, 79, 78, 128, 77, 73, 76, 76, 73, 79, 78, 211, 77, 79, 72, 65, 77, 77, + 65, 196, 77, 79, 82, 78, 73, 78, 71, 128, 77, 85, 76, 84, 73, 80, 76, + 197, 78, 65, 84, 73, 79, 78, 65, 204, 78, 69, 71, 65, 84, 73, 79, 206, + 78, 69, 80, 84, 85, 78, 69, 128, 78, 69, 87, 76, 73, 78, 69, 128, 78, 71, + 69, 65, 68, 65, 76, 128, 78, 73, 75, 65, 72, 73, 84, 128, 78, 73, 78, 69, + 84, 69, 69, 206, 79, 66, 79, 70, 73, 76, 73, 128, 79, 67, 84, 79, 66, 69, + 82, 128, 79, 78, 69, 45, 76, 73, 78, 197, 79, 78, 69, 83, 69, 76, 70, + 128, 79, 79, 89, 65, 78, 78, 65, 128, 79, 82, 84, 72, 79, 68, 79, 216, + 79, 85, 84, 76, 73, 78, 69, 128, 80, 69, 76, 65, 83, 84, 79, 206, 80, 69, + 84, 65, 83, 77, 65, 128, 80, 69, 84, 65, 83, 84, 73, 128, 80, 72, 73, 78, + 84, 72, 85, 128, 80, 72, 85, 84, 72, 65, 79, 128, 80, 79, 68, 65, 84, 85, + 83, 128, 80, 82, 69, 67, 69, 68, 69, 128, 80, 82, 69, 67, 69, 68, 69, + 196, 80, 82, 69, 86, 73, 79, 85, 211, 80, 82, 73, 86, 65, 84, 69, 128, + 80, 82, 79, 80, 69, 82, 84, 217, 81, 85, 65, 82, 84, 69, 82, 128, 82, 65, + 75, 72, 65, 78, 71, 128, 82, 65, 80, 73, 83, 77, 65, 128, 82, 65, 89, 65, + 78, 78, 65, 128, 82, 69, 65, 72, 77, 85, 75, 128, 82, 73, 84, 84, 79, 82, + 85, 128, 82, 85, 85, 66, 85, 82, 85, 128, 83, 65, 73, 75, 85, 82, 85, + 128, 83, 65, 76, 84, 73, 82, 69, 128, 83, 65, 77, 80, 72, 65, 79, 128, + 83, 65, 78, 89, 79, 79, 71, 193, 83, 67, 72, 79, 76, 65, 82, 128, 83, 67, + 82, 85, 80, 76, 69, 128, 83, 69, 71, 77, 69, 78, 84, 128, 83, 69, 86, 69, + 78, 84, 89, 128, 83, 73, 77, 73, 76, 65, 82, 128, 83, 73, 82, 73, 78, 71, + 85, 128, 83, 73, 88, 45, 76, 73, 78, 197, 83, 78, 79, 87, 77, 65, 78, + 128, 83, 80, 82, 73, 78, 71, 83, 128, 83, 81, 85, 65, 82, 69, 83, 128, + 83, 84, 65, 86, 82, 79, 83, 128, 83, 84, 65, 86, 82, 79, 85, 128, 83, 84, + 82, 73, 67, 84, 76, 217, 83, 85, 66, 74, 69, 67, 84, 128, 83, 85, 67, 67, + 69, 69, 68, 128, 83, 89, 78, 69, 86, 77, 65, 128, 84, 65, 73, 83, 89, 79, + 85, 128, 84, 65, 84, 87, 69, 69, 76, 128, 84, 67, 72, 69, 72, 69, 72, + 128, 84, 69, 83, 83, 65, 82, 79, 206, 84, 69, 83, 83, 69, 82, 65, 128, + 84, 72, 73, 82, 84, 69, 69, 206, 84, 72, 85, 78, 68, 69, 82, 128, 84, 72, + 85, 82, 73, 83, 65, 218, 84, 73, 78, 65, 71, 77, 65, 128, 84, 73, 82, 79, + 78, 73, 65, 206, 84, 79, 82, 67, 85, 76, 85, 211, 84, 82, 73, 73, 83, 65, + 80, 128, 84, 86, 73, 77, 65, 68, 85, 210, 84, 87, 79, 45, 76, 73, 78, + 197, 85, 45, 69, 79, 45, 69, 85, 128, 85, 66, 85, 70, 73, 76, 73, 128, + 86, 65, 89, 65, 78, 78, 65, 128, 86, 73, 69, 87, 68, 65, 84, 193, 86, 73, + 76, 76, 65, 71, 69, 128, 86, 79, 73, 67, 73, 78, 71, 128, 87, 65, 83, 65, + 76, 76, 65, 205, 89, 65, 89, 65, 78, 78, 65, 128, 89, 80, 79, 82, 82, 79, + 73, 128, 85, 80, 83, 73, 76, 79, 206, 67, 73, 82, 67, 76, 69, 128, 68, + 73, 78, 71, 66, 65, 212, 65, 67, 67, 69, 78, 84, 128, 69, 80, 83, 73, 76, + 79, 206, 83, 76, 65, 78, 84, 69, 196, 72, 65, 78, 85, 78, 79, 207, 83, + 81, 85, 65, 82, 69, 128, 68, 65, 71, 69, 83, 72, 128, 84, 65, 71, 65, 76, + 79, 199, 79, 77, 73, 67, 82, 79, 206, 71, 76, 79, 84, 84, 65, 204, 78, + 65, 83, 75, 65, 80, 201, 67, 79, 82, 78, 69, 82, 128, 69, 76, 69, 77, 69, + 78, 212, 66, 85, 76, 76, 69, 84, 128, 79, 71, 79, 78, 69, 75, 128, 75, + 73, 82, 71, 72, 73, 218, 82, 69, 86, 69, 82, 83, 197, 86, 73, 82, 65, 77, + 65, 128, 68, 73, 65, 77, 79, 78, 196, 68, 79, 85, 66, 76, 69, 128, 78, + 69, 73, 84, 72, 69, 210, 81, 85, 65, 82, 84, 69, 210, 83, 73, 77, 73, 76, + 65, 210, 83, 73, 78, 71, 76, 69, 128, 83, 81, 85, 65, 82, 69, 196, 68, + 79, 84, 76, 69, 83, 211, 78, 85, 78, 65, 86, 73, 203, 83, 79, 76, 73, 68, + 85, 211, 84, 72, 45, 67, 82, 69, 197, 84, 82, 73, 71, 82, 65, 205, 65, + 82, 75, 84, 73, 75, 207, 69, 76, 69, 86, 69, 78, 128, 73, 78, 83, 73, 68, + 69, 128, 79, 80, 69, 78, 73, 78, 199, 83, 85, 66, 83, 69, 84, 128, 84, + 87, 69, 76, 86, 69, 128, 84, 87, 69, 78, 84, 89, 128, 69, 73, 71, 72, 84, + 72, 211, 72, 89, 80, 72, 69, 78, 128, 80, 65, 82, 84, 73, 65, 204, 86, + 82, 65, 67, 72, 89, 128, 65, 82, 82, 79, 87, 83, 128, 70, 65, 76, 76, 73, + 78, 199, 80, 69, 82, 67, 69, 78, 212, 84, 72, 82, 79, 85, 71, 200, 67, + 69, 68, 73, 76, 76, 193, 67, 79, 78, 84, 82, 79, 204, 67, 85, 82, 86, 73, + 78, 199, 68, 73, 71, 82, 65, 80, 200, 69, 81, 85, 65, 76, 83, 128, 70, + 73, 76, 76, 69, 82, 128, 73, 78, 86, 69, 82, 83, 197, 75, 69, 78, 84, 73, + 77, 193, 79, 66, 76, 73, 81, 85, 197, 82, 79, 85, 78, 68, 69, 196, 83, + 65, 78, 89, 65, 75, 193, 84, 67, 72, 69, 72, 69, 200, 84, 72, 73, 82, 84, + 89, 128, 84, 79, 80, 66, 65, 82, 128, 84, 85, 82, 84, 76, 69, 128, 89, + 73, 68, 68, 73, 83, 200, 45, 75, 72, 89, 73, 76, 128, 66, 79, 84, 84, 79, + 77, 128, 67, 69, 78, 84, 82, 69, 196, 67, 79, 78, 84, 65, 73, 206, 67, + 79, 78, 84, 79, 85, 210, 68, 65, 78, 84, 65, 74, 193, 68, 73, 86, 73, 68, + 69, 196, 68, 79, 84, 84, 69, 68, 128, 68, 82, 65, 71, 79, 78, 128, 70, + 73, 70, 84, 72, 83, 128, 72, 85, 78, 68, 82, 69, 196, 75, 79, 77, 66, 85, + 86, 193, 75, 82, 65, 84, 73, 77, 193, 76, 69, 65, 68, 69, 82, 128, 77, + 65, 82, 66, 85, 84, 193, 77, 69, 77, 66, 69, 82, 128, 78, 65, 84, 85, 82, + 65, 204, 80, 69, 78, 67, 73, 76, 128, 81, 65, 77, 65, 84, 83, 128, 83, + 75, 76, 73, 82, 79, 206, 83, 84, 73, 71, 77, 65, 128, 83, 89, 78, 65, 71, + 77, 193, 84, 65, 65, 76, 85, 74, 193, 84, 72, 69, 83, 69, 79, 211, 84, + 79, 78, 71, 85, 69, 128, 65, 67, 67, 79, 85, 78, 212, 65, 80, 76, 79, 85, + 78, 128, 65, 82, 67, 72, 65, 73, 195, 66, 65, 76, 85, 68, 65, 128, 66, + 65, 77, 66, 79, 79, 128, 66, 65, 83, 72, 75, 73, 210, 66, 73, 78, 68, 73, + 78, 199, 66, 73, 83, 72, 79, 80, 128, 66, 79, 87, 84, 73, 69, 128, 67, + 69, 78, 84, 82, 69, 128, 67, 72, 73, 69, 85, 67, 200, 67, 76, 85, 83, 84, + 69, 210, 68, 65, 71, 71, 69, 82, 128, 68, 69, 67, 73, 77, 65, 204, 68, + 73, 86, 73, 68, 69, 128, 69, 83, 67, 65, 80, 69, 128, 70, 69, 65, 84, 72, + 69, 210, 70, 76, 69, 88, 85, 83, 128, 71, 65, 78, 71, 73, 65, 128, 71, + 69, 82, 69, 83, 72, 128, 73, 78, 72, 73, 66, 73, 212, 73, 83, 83, 72, 65, + 82, 128, 73, 90, 72, 73, 84, 83, 193, 75, 72, 73, 69, 85, 75, 200, 75, + 76, 65, 83, 77, 65, 128, 75, 78, 73, 71, 72, 84, 128, 75, 79, 82, 65, 78, + 73, 195, 76, 69, 71, 69, 84, 79, 211, 77, 65, 76, 65, 75, 79, 206, 77, + 79, 82, 84, 65, 82, 128, 78, 69, 71, 65, 84, 69, 196, 78, 73, 78, 69, 84, + 89, 128, 78, 79, 84, 67, 72, 69, 196, 79, 82, 68, 73, 78, 65, 204, 80, + 72, 73, 69, 85, 80, 200, 80, 72, 82, 65, 83, 69, 128, 80, 73, 76, 67, 82, + 79, 215, 80, 76, 65, 71, 73, 79, 211, 83, 69, 82, 73, 70, 83, 128, 83, + 72, 65, 80, 69, 83, 128, 83, 73, 88, 84, 69, 69, 206, 83, 76, 79, 80, 73, + 78, 199, 83, 77, 65, 76, 76, 69, 210, 83, 77, 73, 76, 73, 78, 199, 83, + 80, 69, 69, 67, 72, 128, 84, 69, 76, 69, 73, 65, 128, 84, 69, 76, 73, 83, + 72, 193, 84, 69, 83, 83, 69, 82, 193, 84, 72, 73, 69, 85, 84, 200, 84, + 72, 82, 69, 65, 68, 128, 84, 72, 82, 69, 69, 45, 196, 85, 80, 84, 85, 82, + 78, 128, 89, 69, 76, 76, 79, 87, 128, 89, 79, 45, 89, 65, 69, 128, 89, + 85, 45, 89, 69, 79, 128, 65, 70, 82, 73, 67, 65, 206, 65, 73, 72, 86, 85, + 83, 128, 65, 73, 86, 73, 76, 73, 203, 65, 76, 73, 71, 78, 69, 196, 65, + 78, 67, 72, 79, 82, 128, 65, 78, 78, 85, 73, 84, 217, 65, 80, 65, 65, 84, + 79, 128, 65, 82, 65, 69, 65, 69, 128, 65, 82, 82, 73, 86, 69, 128, 65, + 82, 83, 69, 79, 83, 128, 65, 82, 85, 72, 85, 65, 128, 65, 85, 71, 85, 83, + 84, 128, 65, 86, 69, 82, 65, 71, 197, 66, 65, 68, 71, 69, 82, 128, 66, + 65, 73, 77, 65, 73, 128, 66, 65, 78, 84, 79, 67, 128, 66, 65, 82, 82, 69, + 69, 128, 66, 69, 78, 90, 69, 78, 197, 66, 69, 84, 87, 69, 69, 206, 66, + 69, 89, 89, 65, 76, 128, 66, 73, 84, 84, 69, 82, 128, 66, 79, 82, 85, 84, + 79, 128, 66, 82, 65, 78, 67, 72, 128, 66, 82, 69, 86, 73, 83, 128, 66, + 85, 67, 75, 76, 69, 128, 67, 65, 78, 67, 69, 76, 128, 67, 65, 78, 67, 69, + 82, 128, 67, 65, 84, 65, 87, 65, 128, 67, 65, 85, 84, 73, 79, 206, 67, + 72, 69, 86, 82, 79, 206, 67, 76, 69, 70, 45, 49, 128, 67, 76, 69, 70, 45, + 50, 128, 67, 76, 73, 86, 73, 83, 128, 67, 76, 79, 83, 69, 68, 128, 67, + 79, 78, 73, 67, 65, 204, 67, 79, 82, 80, 83, 69, 128, 67, 85, 82, 82, 69, + 78, 212, 68, 65, 65, 68, 72, 85, 128, 68, 65, 76, 65, 84, 72, 128, 68, + 65, 77, 65, 82, 85, 128, 68, 65, 83, 69, 73, 65, 128, 68, 68, 65, 72, 65, + 76, 128, 68, 69, 76, 69, 84, 69, 128, 68, 72, 65, 65, 76, 85, 128, 68, + 72, 65, 82, 77, 65, 128, 68, 73, 69, 83, 73, 83, 128, 68, 73, 80, 80, 69, + 82, 128, 68, 79, 84, 83, 45, 49, 128, 68, 79, 84, 83, 45, 50, 128, 68, + 79, 84, 83, 45, 51, 128, 68, 79, 84, 83, 45, 52, 128, 68, 79, 84, 83, 45, + 53, 128, 68, 79, 84, 83, 45, 54, 128, 68, 79, 84, 83, 45, 55, 128, 68, + 79, 84, 83, 45, 56, 128, 68, 82, 65, 67, 72, 77, 193, 69, 73, 71, 72, 84, + 72, 128, 69, 73, 71, 72, 84, 89, 128, 69, 78, 65, 82, 88, 73, 211, 69, + 88, 67, 69, 83, 83, 128, 69, 88, 73, 83, 84, 83, 128, 70, 65, 67, 69, 45, + 49, 128, 70, 65, 67, 69, 45, 50, 128, 70, 65, 67, 69, 45, 51, 128, 70, + 65, 67, 69, 45, 52, 128, 70, 65, 67, 69, 45, 53, 128, 70, 65, 67, 69, 45, + 54, 128, 70, 65, 84, 72, 69, 82, 128, 70, 69, 77, 65, 76, 69, 128, 70, + 69, 82, 77, 65, 84, 193, 70, 73, 70, 84, 69, 69, 206, 70, 76, 65, 71, 45, + 49, 128, 70, 76, 65, 71, 45, 50, 128, 70, 76, 65, 71, 45, 51, 128, 70, + 76, 65, 71, 45, 52, 128, 70, 76, 65, 71, 45, 53, 128, 70, 79, 82, 67, 69, + 83, 128, 71, 69, 68, 79, 76, 65, 128, 71, 69, 77, 73, 78, 73, 128, 71, + 69, 78, 69, 82, 73, 195, 71, 72, 65, 73, 78, 85, 128, 71, 72, 85, 78, 78, + 65, 128, 72, 69, 65, 86, 69, 78, 128, 72, 69, 73, 83, 69, 73, 128, 72, + 69, 82, 85, 84, 85, 128, 72, 85, 73, 73, 84, 79, 128, 73, 45, 66, 69, 65, + 77, 128, 73, 77, 73, 83, 69, 79, 211, 73, 78, 71, 87, 65, 90, 128, 73, + 78, 73, 78, 71, 85, 128, 73, 78, 83, 69, 67, 84, 128, 75, 65, 78, 84, 65, + 74, 193, 75, 69, 70, 85, 76, 65, 128, 75, 69, 89, 67, 65, 80, 128, 75, + 72, 79, 77, 85, 84, 128, 75, 76, 73, 84, 79, 78, 128, 75, 79, 82, 85, 78, + 65, 128, 75, 89, 85, 82, 73, 73, 128, 76, 65, 77, 65, 68, 72, 128, 76, + 65, 84, 69, 82, 65, 204, 76, 73, 78, 69, 45, 49, 128, 76, 73, 78, 69, 45, + 51, 128, 76, 73, 78, 69, 45, 55, 128, 76, 73, 78, 69, 45, 57, 128, 76, + 73, 78, 75, 73, 78, 199, 76, 79, 90, 69, 78, 71, 197, 77, 65, 76, 84, 69, + 83, 197, 77, 65, 82, 75, 69, 82, 128, 77, 65, 82, 85, 75, 85, 128, 77, + 65, 84, 82, 73, 88, 128, 77, 65, 88, 73, 77, 65, 128, 77, 69, 68, 73, 85, + 77, 128, 77, 69, 71, 65, 76, 73, 128, 77, 69, 82, 75, 72, 65, 128, 77, + 69, 84, 82, 73, 65, 128, 77, 73, 68, 76, 73, 78, 197, 77, 73, 76, 76, 69, + 84, 128, 77, 73, 78, 73, 77, 65, 128, 77, 79, 68, 69, 76, 83, 128, 77, + 79, 84, 72, 69, 82, 128, 77, 85, 81, 68, 65, 77, 128, 78, 65, 85, 84, 72, + 83, 128, 78, 69, 78, 65, 78, 79, 128, 78, 73, 82, 85, 71, 85, 128, 78, + 79, 75, 72, 85, 75, 128, 78, 79, 77, 73, 78, 65, 204, 78, 85, 77, 66, 69, + 82, 128, 78, 85, 78, 65, 86, 85, 212, 79, 77, 65, 76, 79, 78, 128, 79, + 80, 69, 78, 45, 80, 128, 79, 80, 80, 79, 83, 69, 128, 79, 82, 73, 71, 73, + 78, 128, 79, 84, 72, 65, 76, 65, 206, 80, 65, 76, 65, 84, 65, 204, 80, + 65, 76, 85, 84, 65, 128, 80, 65, 83, 72, 84, 65, 128, 80, 69, 78, 73, 72, + 73, 128, 80, 69, 82, 83, 79, 78, 128, 80, 73, 75, 85, 82, 85, 128, 80, + 73, 80, 73, 78, 71, 128, 80, 73, 83, 67, 69, 83, 128, 80, 79, 73, 78, 84, + 79, 128, 80, 82, 69, 67, 69, 68, 197, 80, 82, 69, 70, 65, 67, 197, 80, + 82, 79, 68, 85, 67, 212, 81, 69, 84, 65, 78, 65, 128, 81, 85, 66, 85, 84, + 83, 128, 82, 69, 80, 69, 65, 84, 128, 82, 69, 84, 85, 82, 78, 128, 82, + 85, 78, 79, 85, 84, 128, 83, 65, 65, 68, 72, 85, 128, 83, 65, 74, 68, 65, + 72, 128, 83, 65, 77, 69, 75, 72, 128, 83, 65, 78, 78, 89, 65, 128, 83, + 65, 84, 85, 82, 78, 128, 83, 67, 82, 69, 69, 78, 128, 83, 67, 82, 73, 80, + 84, 128, 83, 69, 65, 71, 85, 76, 204, 83, 69, 67, 79, 78, 68, 128, 83, + 69, 67, 82, 69, 84, 128, 83, 69, 67, 84, 79, 82, 128, 83, 69, 73, 83, 77, + 65, 128, 83, 69, 82, 86, 73, 67, 197, 83, 72, 65, 68, 68, 65, 128, 83, + 72, 65, 75, 84, 73, 128, 83, 72, 69, 69, 78, 85, 128, 83, 72, 85, 70, 70, + 76, 197, 83, 73, 67, 75, 76, 69, 128, 83, 73, 88, 84, 72, 83, 128, 83, + 76, 79, 87, 76, 89, 128, 83, 80, 65, 84, 72, 73, 128, 83, 80, 73, 82, 73, + 84, 128, 83, 80, 82, 79, 85, 84, 128, 83, 84, 65, 86, 82, 79, 211, 83, + 84, 82, 65, 73, 70, 128, 83, 84, 82, 73, 68, 69, 128, 83, 84, 82, 79, 75, + 69, 211, 83, 85, 66, 73, 84, 79, 128, 83, 85, 67, 67, 69, 69, 196, 83, + 85, 82, 70, 65, 67, 197, 83, 89, 78, 65, 70, 73, 128, 83, 89, 79, 85, 87, + 65, 128, 84, 65, 84, 87, 69, 69, 204, 84, 65, 85, 82, 85, 83, 128, 84, + 69, 78, 85, 84, 79, 128, 84, 72, 65, 65, 76, 85, 128, 84, 72, 65, 72, 65, + 78, 128, 84, 72, 73, 82, 68, 83, 128, 84, 72, 73, 85, 84, 72, 128, 84, + 73, 80, 69, 72, 65, 128, 84, 82, 73, 80, 76, 73, 128, 84, 82, 73, 80, 79, + 68, 128, 84, 83, 72, 85, 71, 83, 128, 84, 84, 69, 72, 69, 72, 128, 84, + 85, 82, 66, 65, 78, 128, 85, 80, 82, 73, 71, 72, 212, 85, 82, 65, 78, 85, + 83, 128, 86, 65, 76, 76, 69, 89, 128, 86, 65, 82, 69, 73, 65, 201, 86, + 65, 82, 73, 65, 78, 212, 86, 65, 82, 73, 75, 65, 128, 86, 73, 67, 84, 79, + 82, 217, 86, 73, 82, 73, 65, 77, 128, 86, 73, 83, 65, 82, 71, 193, 87, + 69, 65, 80, 79, 78, 128, 87, 82, 73, 84, 73, 78, 199, 89, 70, 69, 83, 73, + 83, 128, 89, 79, 45, 89, 69, 79, 128, 89, 80, 83, 73, 76, 73, 128, 84, + 82, 73, 80, 76, 197, 84, 85, 82, 78, 69, 196, 69, 81, 85, 65, 76, 211, + 71, 79, 84, 72, 73, 195, 76, 73, 71, 72, 84, 128, 72, 69, 65, 86, 89, + 128, 77, 73, 68, 68, 76, 197, 83, 73, 78, 71, 76, 197, 66, 76, 79, 67, + 75, 128, 77, 65, 78, 67, 72, 213, 84, 79, 78, 79, 83, 128, 70, 84, 72, + 79, 82, 193, 79, 77, 69, 71, 65, 128, 83, 73, 71, 77, 65, 128, 68, 65, + 83, 73, 65, 128, 83, 85, 66, 83, 69, 212, 67, 76, 79, 83, 69, 196, 65, + 76, 80, 72, 65, 128, 66, 79, 84, 84, 79, 205, 77, 69, 68, 73, 85, 205, + 86, 85, 76, 71, 65, 210, 67, 79, 77, 77, 65, 128, 67, 79, 80, 84, 73, + 195, 67, 79, 82, 78, 69, 210, 68, 69, 76, 84, 65, 128, 69, 81, 85, 65, + 76, 128, 73, 67, 72, 79, 83, 128, 83, 65, 89, 73, 83, 201, 87, 72, 73, + 84, 69, 128, 65, 76, 77, 79, 83, 212, 67, 82, 79, 83, 83, 128, 75, 65, + 80, 80, 65, 128, 76, 65, 77, 68, 65, 128, 84, 72, 69, 84, 65, 128, 89, + 45, 67, 82, 69, 197, 66, 69, 83, 73, 68, 197, 67, 69, 78, 84, 82, 197, + 77, 65, 67, 82, 79, 206, 83, 72, 65, 68, 68, 193, 78, 79, 82, 77, 65, + 204, 84, 87, 69, 78, 84, 217, 68, 65, 83, 72, 69, 196, 76, 69, 78, 71, + 84, 200, 80, 82, 73, 77, 69, 128, 84, 72, 73, 82, 84, 217, 85, 78, 73, + 79, 78, 128, 67, 65, 78, 68, 82, 193, 82, 69, 80, 69, 65, 212, 83, 84, + 82, 79, 75, 197, 84, 69, 77, 80, 85, 211, 68, 79, 84, 84, 69, 196, 82, + 73, 83, 73, 78, 199, 82, 84, 65, 71, 83, 128, 68, 73, 69, 83, 73, 211, + 68, 73, 80, 76, 73, 128, 73, 78, 68, 69, 88, 128, 75, 79, 80, 80, 65, + 128, 78, 65, 66, 76, 65, 128, 79, 84, 84, 65, 86, 193, 83, 84, 65, 70, + 70, 128, 89, 70, 69, 83, 73, 211, 66, 65, 76, 76, 79, 212, 66, 65, 82, + 82, 69, 197, 67, 76, 73, 67, 75, 128, 67, 85, 82, 86, 69, 196, 69, 65, + 82, 84, 72, 128, 70, 69, 78, 67, 69, 128, 70, 73, 70, 84, 89, 128, 76, + 69, 73, 77, 77, 193, 76, 73, 84, 84, 76, 197, 78, 69, 83, 84, 69, 196, + 78, 85, 75, 84, 65, 128, 85, 73, 71, 72, 85, 210, 66, 65, 83, 83, 65, + 128, 66, 82, 73, 68, 71, 197, 67, 72, 82, 79, 77, 193, 67, 85, 66, 69, + 68, 128, 68, 69, 71, 82, 69, 197, 68, 69, 86, 73, 67, 197, 68, 79, 76, + 76, 65, 210, 80, 65, 73, 82, 69, 196, 80, 65, 84, 65, 72, 128, 80, 73, + 69, 67, 69, 128, 83, 67, 72, 87, 65, 128, 83, 75, 69, 87, 69, 196, 84, + 73, 77, 69, 83, 128, 84, 84, 69, 72, 69, 200, 87, 65, 84, 69, 82, 128, + 87, 73, 71, 71, 76, 217, 65, 82, 79, 85, 78, 196, 65, 82, 83, 69, 79, + 211, 66, 82, 79, 75, 69, 206, 67, 65, 82, 69, 84, 128, 67, 76, 73, 70, + 70, 128, 68, 65, 71, 69, 83, 200, 70, 76, 79, 82, 65, 204, 72, 69, 65, + 82, 84, 128, 76, 65, 77, 69, 68, 128, 76, 85, 78, 65, 84, 197, 77, 65, + 80, 73, 81, 128, 78, 45, 67, 82, 69, 197, 80, 79, 83, 84, 65, 204, 80, + 84, 72, 65, 72, 193, 83, 67, 72, 69, 77, 193, 83, 69, 71, 79, 76, 128, + 83, 72, 65, 68, 69, 128, 83, 84, 82, 69, 83, 211, 84, 72, 79, 82, 78, + 128, 84, 73, 84, 76, 79, 128, 84, 79, 79, 84, 72, 128, 86, 65, 82, 69, + 73, 193, 90, 73, 71, 90, 65, 199, 90, 81, 65, 80, 72, 193, 65, 76, 65, + 80, 72, 128, 65, 76, 65, 89, 72, 197, 66, 69, 65, 77, 69, 196, 66, 73, + 78, 65, 82, 217, 66, 79, 87, 84, 73, 197, 67, 72, 69, 67, 75, 128, 67, + 76, 79, 84, 72, 128, 67, 85, 82, 86, 69, 128, 68, 65, 76, 69, 84, 128, + 68, 65, 78, 68, 65, 128, 68, 68, 65, 72, 65, 204, 68, 69, 65, 84, 72, + 128, 69, 84, 69, 82, 79, 206, 70, 65, 67, 84, 79, 210, 70, 73, 71, 85, + 82, 197, 70, 76, 79, 79, 82, 128, 70, 79, 82, 84, 89, 128, 71, 65, 80, + 80, 69, 196, 71, 69, 78, 73, 75, 201, 71, 72, 79, 83, 84, 128, 71, 72, + 85, 78, 78, 193, 71, 78, 89, 73, 83, 128, 71, 79, 82, 71, 73, 128, 72, + 65, 77, 90, 65, 128, 72, 73, 82, 73, 81, 128, 72, 79, 76, 65, 77, 128, + 72, 79, 82, 83, 69, 128, 72, 87, 65, 73, 82, 128, 75, 65, 90, 65, 75, + 200, 75, 73, 89, 69, 79, 203, 75, 76, 65, 83, 77, 193, 76, 65, 66, 79, + 82, 128, 76, 65, 82, 71, 69, 210, 77, 69, 84, 65, 76, 128, 78, 79, 84, + 69, 83, 128, 79, 71, 79, 78, 69, 203, 79, 76, 73, 71, 79, 206, 79, 82, + 78, 65, 84, 197, 80, 73, 65, 83, 77, 193, 80, 76, 65, 78, 67, 203, 80, + 79, 73, 78, 84, 128, 80, 82, 79, 84, 79, 211, 81, 85, 69, 69, 78, 128, + 81, 85, 73, 76, 76, 128, 83, 65, 77, 80, 73, 128, 83, 67, 82, 69, 69, + 206, 83, 69, 71, 78, 79, 128, 83, 69, 82, 73, 70, 211, 83, 69, 83, 65, + 77, 197, 83, 72, 65, 82, 80, 128, 83, 72, 67, 72, 65, 128, 83, 72, 69, + 69, 80, 128, 83, 72, 69, 76, 76, 128, 83, 72, 73, 77, 65, 128, 83, 72, + 87, 65, 65, 128, 83, 72, 87, 73, 73, 128, 83, 72, 87, 79, 79, 128, 83, + 73, 71, 78, 83, 128, 83, 73, 78, 68, 72, 201, 83, 77, 65, 76, 76, 128, + 83, 80, 73, 82, 73, 212, 83, 84, 79, 67, 75, 128, 83, 84, 85, 68, 89, + 128, 83, 85, 75, 85, 78, 128, 84, 65, 78, 78, 69, 196, 84, 69, 76, 79, + 85, 211, 84, 72, 87, 65, 65, 128, 84, 73, 71, 69, 82, 128, 84, 73, 75, + 69, 85, 212, 84, 82, 85, 78, 75, 128, 84, 83, 65, 68, 73, 128, 84, 83, + 72, 69, 71, 128, 84, 83, 72, 69, 83, 128, 84, 87, 69, 76, 86, 197, 87, + 72, 69, 65, 84, 128, 89, 79, 45, 89, 65, 128, 89, 85, 45, 89, 69, 128, + 90, 90, 73, 69, 84, 128, 45, 67, 72, 65, 76, 128, 45, 75, 72, 89, 85, + 196, 45, 80, 72, 82, 85, 128, 65, 68, 68, 65, 75, 128, 65, 71, 65, 73, + 78, 128, 65, 72, 83, 68, 65, 128, 65, 76, 73, 70, 85, 128, 65, 77, 79, + 85, 78, 212, 65, 78, 80, 69, 65, 128, 65, 80, 82, 73, 76, 128, 65, 82, + 73, 69, 83, 128, 65, 82, 76, 65, 85, 199, 66, 66, 73, 69, 80, 128, 66, + 66, 73, 69, 84, 128, 66, 66, 73, 69, 88, 128, 66, 66, 85, 79, 80, 128, + 66, 66, 85, 79, 88, 128, 66, 66, 85, 82, 88, 128, 66, 69, 69, 84, 65, + 128, 66, 69, 72, 69, 72, 128, 66, 69, 73, 84, 72, 128, 66, 73, 78, 68, + 73, 128, 66, 73, 82, 71, 65, 128, 66, 76, 65, 78, 75, 128, 66, 76, 79, + 79, 68, 128, 66, 82, 65, 67, 69, 128, 66, 82, 65, 78, 67, 200, 66, 82, + 69, 65, 84, 200, 66, 82, 85, 83, 72, 128, 66, 83, 84, 65, 82, 128, 66, + 85, 76, 76, 69, 212, 67, 65, 77, 78, 85, 195, 67, 65, 78, 67, 69, 204, + 67, 69, 65, 76, 67, 128, 67, 69, 73, 82, 84, 128, 67, 72, 65, 68, 65, + 128, 67, 72, 65, 73, 82, 128, 67, 72, 65, 78, 71, 128, 67, 72, 73, 76, + 68, 128, 67, 72, 73, 78, 71, 128, 67, 72, 79, 75, 69, 128, 67, 72, 85, + 76, 65, 128, 67, 72, 85, 79, 80, 128, 67, 72, 85, 79, 84, 128, 67, 72, + 85, 79, 88, 128, 67, 72, 85, 82, 88, 128, 67, 72, 89, 82, 88, 128, 67, + 76, 79, 85, 68, 128, 67, 79, 69, 78, 71, 128, 67, 79, 76, 79, 82, 128, + 67, 79, 77, 69, 84, 128, 67, 79, 77, 77, 79, 206, 67, 79, 86, 69, 82, + 128, 67, 82, 79, 73, 88, 128, 68, 65, 65, 83, 85, 128, 68, 65, 76, 65, + 84, 200, 68, 65, 77, 77, 65, 128, 68, 65, 82, 71, 65, 128, 68, 65, 86, + 73, 68, 128, 68, 68, 68, 72, 65, 128, 68, 68, 73, 69, 80, 128, 68, 68, + 73, 69, 88, 128, 68, 68, 85, 79, 80, 128, 68, 68, 85, 79, 88, 128, 68, + 68, 85, 82, 88, 128, 68, 69, 76, 69, 84, 197, 68, 73, 86, 73, 68, 197, + 68, 79, 77, 65, 73, 206, 68, 82, 73, 86, 69, 128, 69, 69, 75, 65, 65, + 128, 69, 76, 69, 86, 69, 206, 69, 76, 73, 70, 73, 128, 69, 78, 84, 69, + 82, 128, 69, 79, 76, 72, 88, 128, 69, 85, 45, 69, 85, 128, 69, 88, 73, + 83, 84, 128, 70, 65, 65, 70, 85, 128, 70, 65, 73, 72, 85, 128, 70, 65, + 84, 72, 65, 128, 70, 69, 65, 82, 78, 128, 70, 69, 77, 65, 76, 197, 70, + 72, 84, 79, 82, 193, 70, 73, 69, 76, 68, 128, 70, 73, 70, 84, 72, 128, + 70, 73, 71, 72, 84, 128, 70, 73, 76, 76, 69, 196, 70, 73, 78, 73, 84, + 197, 70, 76, 85, 84, 69, 128, 70, 79, 82, 67, 69, 128, 70, 79, 82, 84, + 69, 128, 70, 82, 69, 78, 67, 200, 70, 82, 79, 87, 78, 128, 71, 65, 65, + 70, 85, 128, 71, 65, 68, 79, 76, 128, 71, 65, 77, 65, 76, 128, 71, 65, + 78, 77, 65, 128, 71, 65, 82, 79, 78, 128, 71, 69, 82, 69, 83, 200, 71, + 69, 82, 77, 65, 206, 71, 71, 73, 69, 80, 128, 71, 71, 73, 69, 88, 128, + 71, 71, 85, 79, 80, 128, 71, 71, 85, 79, 84, 128, 71, 71, 85, 79, 88, + 128, 71, 71, 85, 82, 88, 128, 71, 72, 65, 73, 78, 128, 71, 73, 77, 69, + 76, 128, 71, 73, 78, 73, 73, 128, 71, 76, 69, 73, 67, 200, 71, 82, 65, + 73, 78, 128, 71, 82, 65, 83, 83, 128, 72, 45, 84, 89, 80, 197, 72, 65, + 71, 76, 65, 218, 72, 65, 73, 84, 85, 128, 72, 65, 77, 77, 69, 210, 72, + 65, 78, 68, 83, 128, 72, 69, 65, 86, 69, 206, 72, 73, 68, 73, 78, 199, + 72, 76, 73, 69, 80, 128, 72, 76, 73, 69, 88, 128, 72, 76, 85, 79, 80, + 128, 72, 76, 85, 79, 88, 128, 72, 76, 85, 82, 88, 128, 72, 76, 89, 82, + 88, 128, 72, 77, 73, 69, 80, 128, 72, 77, 73, 69, 88, 128, 72, 77, 85, + 79, 80, 128, 72, 77, 85, 79, 88, 128, 72, 77, 85, 82, 88, 128, 72, 77, + 89, 82, 88, 128, 72, 78, 73, 69, 80, 128, 72, 78, 73, 69, 84, 128, 72, + 78, 73, 69, 88, 128, 72, 78, 85, 79, 88, 128, 72, 79, 79, 82, 85, 128, + 72, 79, 85, 83, 69, 128, 72, 85, 82, 65, 78, 128, 72, 88, 73, 69, 80, + 128, 72, 88, 73, 69, 84, 128, 72, 88, 73, 69, 88, 128, 72, 88, 85, 79, + 80, 128, 72, 88, 85, 79, 84, 128, 72, 88, 85, 79, 88, 128, 72, 89, 80, + 72, 69, 206, 73, 67, 72, 79, 85, 128, 73, 71, 71, 87, 83, 128, 73, 83, + 65, 75, 73, 193, 74, 74, 73, 69, 80, 128, 74, 74, 73, 69, 84, 128, 74, + 74, 73, 69, 88, 128, 74, 74, 85, 79, 80, 128, 74, 74, 85, 79, 88, 128, + 74, 74, 85, 82, 88, 128, 75, 65, 65, 70, 85, 128, 75, 65, 73, 82, 73, + 128, 75, 65, 83, 82, 65, 128, 75, 65, 84, 65, 86, 193, 75, 65, 85, 78, + 65, 128, 75, 69, 69, 83, 85, 128, 75, 69, 72, 69, 72, 128, 75, 69, 76, + 86, 73, 206, 75, 72, 85, 65, 84, 128, 75, 72, 87, 65, 73, 128, 75, 78, + 73, 70, 69, 128, 75, 79, 79, 80, 79, 128, 75, 79, 82, 69, 65, 206, 75, + 85, 83, 77, 65, 128, 75, 88, 87, 65, 65, 128, 75, 88, 87, 69, 69, 128, + 76, 45, 84, 89, 80, 197, 76, 65, 65, 77, 85, 128, 76, 65, 71, 85, 83, + 128, 76, 65, 77, 66, 68, 193, 76, 65, 85, 75, 65, 218, 76, 69, 77, 79, + 73, 128, 76, 73, 66, 82, 65, 128, 76, 73, 77, 73, 84, 128, 76, 79, 78, + 71, 65, 128, 76, 79, 85, 82, 69, 128, 77, 65, 68, 68, 65, 128, 77, 65, + 68, 68, 65, 200, 77, 65, 72, 72, 65, 128, 77, 65, 73, 82, 85, 128, 77, + 65, 78, 78, 65, 128, 77, 65, 78, 78, 65, 218, 77, 65, 81, 65, 70, 128, + 77, 65, 82, 67, 72, 128, 77, 65, 83, 79, 82, 193, 77, 69, 69, 77, 85, + 128, 77, 69, 73, 90, 73, 128, 77, 69, 76, 79, 78, 128, 77, 69, 77, 66, + 69, 210, 77, 69, 82, 75, 72, 193, 77, 69, 84, 69, 71, 128, 77, 69, 90, + 90, 79, 128, 77, 71, 73, 69, 88, 128, 77, 71, 85, 79, 80, 128, 77, 71, + 85, 79, 88, 128, 77, 71, 85, 82, 88, 128, 77, 73, 75, 82, 73, 128, 77, + 73, 75, 82, 79, 206, 77, 79, 68, 85, 76, 207, 77, 79, 85, 78, 68, 128, + 77, 79, 85, 84, 72, 128, 77, 85, 78, 65, 72, 128, 77, 85, 83, 73, 67, + 128, 78, 65, 82, 82, 79, 215, 78, 65, 85, 68, 73, 218, 78, 66, 73, 69, + 80, 128, 78, 66, 73, 69, 88, 128, 78, 66, 85, 82, 88, 128, 78, 66, 89, + 82, 88, 128, 78, 68, 73, 69, 88, 128, 78, 68, 85, 82, 88, 128, 78, 71, + 65, 65, 73, 128, 78, 71, 73, 69, 80, 128, 78, 71, 73, 69, 88, 128, 78, + 71, 79, 69, 72, 128, 78, 71, 85, 79, 84, 128, 78, 71, 85, 79, 88, 128, + 78, 74, 73, 69, 80, 128, 78, 74, 73, 69, 84, 128, 78, 74, 73, 69, 88, + 128, 78, 74, 85, 79, 88, 128, 78, 74, 85, 82, 88, 128, 78, 74, 89, 82, + 88, 128, 78, 78, 71, 65, 65, 128, 78, 78, 71, 73, 73, 128, 78, 78, 71, + 79, 79, 128, 78, 79, 79, 78, 85, 128, 78, 79, 84, 67, 72, 128, 78, 79, + 84, 84, 79, 128, 78, 82, 85, 82, 88, 128, 78, 82, 89, 82, 88, 128, 78, + 85, 77, 69, 82, 207, 78, 89, 73, 69, 80, 128, 78, 89, 73, 69, 84, 128, + 78, 89, 73, 69, 88, 128, 78, 89, 85, 79, 80, 128, 78, 89, 85, 79, 88, + 128, 78, 90, 73, 69, 80, 128, 78, 90, 73, 69, 88, 128, 78, 90, 85, 79, + 88, 128, 78, 90, 85, 82, 88, 128, 78, 90, 89, 82, 88, 128, 79, 66, 74, + 69, 67, 212, 79, 78, 75, 65, 82, 128, 79, 80, 84, 73, 79, 206, 79, 84, + 72, 65, 76, 128, 79, 88, 69, 73, 65, 201, 80, 65, 65, 84, 85, 128, 80, + 65, 83, 69, 81, 128, 80, 65, 83, 85, 81, 128, 80, 65, 84, 65, 75, 128, + 80, 65, 90, 69, 82, 128, 80, 69, 69, 90, 73, 128, 80, 69, 72, 69, 72, + 128, 80, 69, 73, 84, 72, 128, 80, 69, 78, 83, 85, 128, 80, 69, 79, 82, + 84, 200, 80, 69, 82, 84, 72, 207, 80, 69, 83, 69, 84, 193, 80, 72, 78, + 65, 69, 203, 80, 72, 85, 78, 71, 128, 80, 73, 65, 78, 79, 128, 80, 76, + 85, 84, 79, 128, 80, 79, 78, 68, 79, 128, 80, 79, 87, 69, 82, 128, 80, + 82, 73, 78, 84, 128, 80, 82, 79, 79, 70, 128, 80, 82, 79, 86, 69, 128, + 81, 65, 65, 70, 85, 128, 81, 65, 68, 77, 65, 128, 81, 65, 82, 78, 69, + 217, 81, 65, 84, 65, 78, 128, 81, 72, 87, 65, 65, 128, 81, 72, 87, 69, + 69, 128, 82, 45, 67, 82, 69, 197, 82, 65, 73, 68, 65, 128, 82, 65, 83, + 79, 85, 204, 82, 65, 84, 73, 79, 128, 82, 69, 65, 67, 72, 128, 82, 69, + 67, 79, 82, 196, 82, 69, 84, 85, 82, 206, 82, 69, 86, 73, 65, 128, 82, + 69, 86, 77, 65, 128, 82, 72, 79, 84, 73, 195, 82, 73, 86, 69, 82, 128, + 82, 78, 79, 79, 78, 128, 82, 79, 66, 65, 84, 128, 82, 82, 85, 79, 88, + 128, 82, 82, 85, 82, 88, 128, 82, 82, 89, 82, 88, 128, 82, 85, 80, 73, + 73, 128, 82, 87, 65, 72, 65, 128, 83, 65, 68, 72, 69, 128, 83, 65, 77, + 69, 75, 200, 83, 65, 77, 89, 79, 203, 83, 65, 85, 73, 76, 128, 83, 69, + 69, 78, 85, 128, 83, 69, 73, 83, 77, 193, 83, 69, 78, 84, 73, 128, 83, + 72, 69, 69, 78, 128, 83, 72, 69, 81, 69, 204, 83, 72, 69, 86, 65, 128, + 83, 72, 79, 79, 84, 128, 83, 72, 85, 79, 80, 128, 83, 72, 85, 79, 88, + 128, 83, 72, 85, 82, 88, 128, 83, 72, 89, 82, 88, 128, 83, 73, 88, 84, + 72, 128, 83, 73, 88, 84, 89, 128, 83, 76, 65, 86, 69, 128, 83, 76, 73, + 67, 69, 128, 83, 76, 79, 80, 69, 128, 83, 77, 69, 65, 82, 128, 83, 77, + 73, 76, 69, 128, 83, 78, 65, 75, 69, 128, 83, 78, 79, 85, 84, 128, 83, + 79, 85, 78, 68, 128, 83, 79, 87, 73, 76, 207, 83, 80, 69, 65, 82, 128, + 83, 80, 79, 79, 78, 128, 83, 80, 85, 78, 71, 211, 83, 81, 85, 73, 83, + 200, 83, 83, 73, 69, 80, 128, 83, 83, 73, 69, 88, 128, 83, 83, 89, 82, + 88, 128, 83, 84, 65, 78, 68, 128, 83, 84, 65, 82, 75, 128, 83, 84, 69, + 65, 77, 128, 83, 84, 79, 78, 69, 128, 83, 87, 69, 69, 84, 128, 83, 89, + 82, 77, 65, 128, 84, 65, 80, 69, 82, 128, 84, 67, 72, 69, 72, 128, 84, + 69, 73, 87, 83, 128, 84, 69, 86, 73, 82, 128, 84, 72, 73, 82, 68, 128, + 84, 72, 73, 84, 65, 128, 84, 72, 79, 78, 71, 128, 84, 72, 85, 78, 71, + 128, 84, 73, 78, 78, 69, 128, 84, 73, 80, 80, 73, 128, 84, 76, 72, 69, + 69, 128, 84, 82, 65, 67, 75, 128, 84, 82, 73, 84, 79, 211, 84, 83, 69, + 82, 69, 128, 84, 84, 83, 69, 69, 128, 84, 85, 71, 82, 73, 203, 84, 89, + 80, 69, 45, 177, 84, 89, 80, 69, 45, 178, 84, 89, 80, 69, 45, 179, 84, + 89, 80, 69, 45, 180, 84, 89, 80, 69, 45, 181, 84, 89, 80, 69, 45, 182, + 84, 89, 80, 69, 45, 183, 85, 80, 87, 65, 82, 196, 86, 65, 65, 86, 85, + 128, 86, 65, 83, 73, 83, 128, 86, 65, 84, 72, 89, 128, 86, 69, 67, 84, + 79, 210, 86, 73, 82, 71, 65, 128, 86, 73, 82, 71, 79, 128, 86, 79, 76, + 85, 77, 197, 87, 65, 65, 86, 85, 128, 87, 65, 83, 76, 65, 128, 87, 65, + 84, 67, 72, 128, 87, 73, 78, 74, 65, 128, 87, 79, 77, 65, 78, 128, 87, + 82, 69, 65, 84, 200, 87, 82, 79, 78, 71, 128, 89, 65, 45, 89, 79, 128, + 89, 65, 65, 68, 79, 128, 89, 65, 65, 82, 85, 128, 89, 69, 79, 45, 79, + 128, 89, 69, 79, 45, 85, 128, 89, 69, 84, 73, 86, 128, 89, 85, 45, 69, + 79, 128, 90, 65, 82, 81, 65, 128, 90, 65, 89, 73, 78, 128, 90, 72, 85, + 79, 80, 128, 90, 72, 85, 79, 88, 128, 90, 72, 85, 82, 88, 128, 90, 72, + 89, 82, 88, 128, 90, 73, 76, 68, 69, 128, 90, 73, 78, 79, 82, 128, 90, + 89, 71, 79, 83, 128, 90, 90, 73, 69, 80, 128, 90, 90, 73, 69, 88, 128, + 90, 90, 85, 82, 88, 128, 90, 90, 89, 82, 88, 128, 84, 72, 82, 69, 197, + 76, 69, 70, 84, 128, 90, 69, 82, 79, 128, 79, 71, 72, 65, 205, 67, 85, + 82, 76, 217, 78, 79, 82, 84, 200, 66, 85, 72, 73, 196, 80, 79, 73, 78, + 212, 83, 79, 85, 84, 200, 82, 73, 78, 71, 128, 84, 65, 67, 75, 128, 68, + 79, 87, 78, 128, 78, 45, 65, 82, 217, 82, 69, 83, 84, 128, 66, 69, 76, + 79, 215, 68, 65, 83, 72, 128, 71, 72, 65, 73, 206, 73, 79, 84, 65, 128, + 67, 79, 77, 77, 193, 86, 65, 82, 73, 193, 66, 82, 69, 86, 197, 84, 84, + 72, 65, 128, 65, 67, 85, 84, 197, 66, 69, 84, 65, 128, 67, 72, 69, 83, + 211, 71, 82, 65, 86, 197, 83, 72, 69, 76, 204, 84, 72, 69, 84, 193, 90, + 69, 84, 65, 128, 83, 79, 85, 78, 196, 85, 78, 73, 79, 206, 69, 73, 71, + 72, 212, 78, 79, 84, 69, 128, 70, 79, 82, 84, 217, 73, 77, 65, 71, 197, + 80, 76, 85, 83, 128, 65, 71, 79, 71, 201, 68, 79, 84, 83, 128, 69, 77, + 80, 84, 217, 72, 65, 76, 70, 128, 72, 69, 65, 82, 212, 83, 85, 73, 84, + 128, 70, 73, 76, 76, 128, 75, 65, 84, 79, 128, 76, 65, 82, 71, 197, 83, + 72, 65, 68, 128, 65, 76, 69, 70, 128, 67, 65, 82, 69, 212, 67, 85, 82, + 76, 128, 70, 65, 82, 83, 201, 75, 65, 80, 80, 193, 77, 79, 79, 78, 128, + 83, 85, 78, 71, 128, 84, 73, 67, 75, 128, 67, 76, 69, 70, 128, 67, 82, + 79, 83, 211, 70, 65, 67, 69, 128, 70, 73, 82, 69, 128, 77, 65, 68, 68, + 193, 81, 85, 65, 68, 128, 83, 84, 69, 77, 128, 84, 67, 72, 69, 200, 84, + 73, 77, 69, 211, 84, 83, 72, 69, 199, 65, 76, 84, 65, 128, 66, 69, 71, + 73, 206, 66, 69, 72, 69, 200, 67, 72, 69, 69, 128, 67, 82, 79, 80, 128, + 68, 65, 77, 77, 193, 70, 65, 84, 72, 193, 72, 65, 78, 68, 128, 74, 79, + 73, 78, 128, 75, 65, 83, 82, 193, 75, 69, 72, 69, 200, 75, 87, 65, 65, + 128, 78, 71, 79, 69, 200, 80, 69, 72, 69, 200, 82, 65, 70, 69, 128, 82, + 78, 79, 79, 206, 82, 84, 65, 71, 211, 83, 69, 86, 69, 206, 83, 72, 65, + 82, 208, 83, 72, 73, 78, 128, 84, 72, 65, 65, 128, 84, 72, 69, 69, 128, + 86, 65, 78, 69, 128, 87, 65, 86, 69, 128, 65, 76, 76, 79, 128, 66, 73, + 82, 68, 128, 67, 65, 82, 79, 206, 67, 72, 69, 67, 203, 67, 72, 82, 79, + 193, 67, 73, 69, 85, 195, 67, 87, 65, 65, 128, 68, 69, 76, 84, 193, 70, + 79, 79, 84, 128, 71, 82, 65, 83, 211, 72, 65, 84, 65, 198, 75, 69, 84, + 84, 201, 76, 76, 76, 65, 128, 76, 79, 79, 80, 128, 77, 85, 83, 73, 195, + 77, 87, 65, 65, 128, 78, 87, 65, 65, 128, 79, 85, 84, 69, 210, 79, 88, + 69, 73, 193, 80, 69, 68, 65, 204, 80, 79, 76, 69, 128, 80, 82, 73, 77, + 197, 80, 87, 65, 65, 128, 82, 79, 79, 84, 128, 82, 85, 80, 69, 197, 83, + 67, 72, 87, 193, 83, 69, 69, 78, 128, 83, 72, 87, 65, 128, 83, 73, 76, + 75, 128, 83, 84, 65, 82, 212, 83, 87, 65, 65, 128, 84, 72, 73, 73, 128, + 84, 87, 65, 65, 128, 87, 73, 78, 68, 128, 89, 73, 87, 78, 128, 89, 87, + 65, 65, 128, 90, 72, 69, 69, 128, 45, 68, 90, 85, 196, 65, 80, 69, 83, + 207, 65, 82, 71, 73, 128, 66, 66, 85, 84, 128, 66, 69, 65, 84, 128, 66, + 76, 65, 68, 197, 66, 76, 85, 69, 128, 66, 79, 78, 69, 128, 66, 82, 85, + 83, 200, 66, 90, 85, 78, 199, 67, 65, 82, 84, 128, 67, 72, 65, 82, 128, + 67, 72, 73, 78, 128, 67, 85, 79, 80, 128, 67, 85, 82, 86, 197, 67, 87, + 73, 73, 128, 67, 87, 79, 79, 128, 68, 65, 76, 69, 212, 68, 68, 85, 82, + 128, 68, 90, 72, 65, 128, 68, 90, 72, 69, 128, 68, 90, 74, 69, 128, 69, + 82, 65, 83, 197, 70, 69, 69, 68, 128, 70, 73, 82, 83, 212, 70, 73, 83, + 72, 128, 70, 73, 84, 65, 128, 70, 76, 65, 84, 128, 70, 82, 79, 71, 128, + 70, 87, 65, 65, 128, 71, 65, 84, 69, 128, 71, 67, 73, 71, 128, 71, 71, + 79, 80, 128, 71, 71, 85, 79, 128, 71, 72, 65, 68, 128, 71, 72, 65, 78, + 128, 71, 72, 72, 65, 128, 71, 73, 77, 69, 204, 71, 79, 65, 76, 128, 71, + 79, 76, 68, 128, 71, 82, 65, 67, 197, 71, 83, 85, 77, 128, 71, 89, 65, + 83, 128, 71, 89, 79, 78, 128, 72, 65, 86, 69, 128, 72, 66, 65, 83, 193, + 72, 72, 65, 65, 128, 72, 73, 69, 85, 200, 72, 79, 82, 73, 128, 72, 88, + 73, 84, 128, 72, 88, 79, 80, 128, 72, 88, 85, 79, 128, 73, 69, 85, 78, + 199, 74, 65, 68, 69, 128, 74, 69, 69, 77, 128, 74, 72, 65, 78, 128, 74, + 72, 69, 72, 128, 74, 74, 73, 69, 128, 74, 74, 85, 84, 128, 75, 72, 65, + 72, 128, 75, 72, 65, 78, 199, 75, 72, 65, 82, 128, 75, 72, 69, 73, 128, + 75, 72, 72, 65, 128, 75, 78, 73, 70, 197, 75, 83, 83, 65, 128, 75, 87, + 73, 73, 128, 75, 87, 79, 79, 128, 76, 69, 65, 70, 128, 76, 73, 87, 78, + 128, 76, 79, 78, 71, 128, 76, 79, 78, 71, 193, 76, 79, 87, 45, 185, 76, + 87, 65, 65, 128, 76, 87, 73, 73, 128, 76, 87, 79, 79, 128, 77, 69, 65, + 84, 128, 77, 69, 69, 77, 128, 77, 69, 83, 79, 128, 77, 73, 69, 85, 205, + 77, 79, 85, 78, 196, 77, 87, 73, 73, 128, 77, 87, 79, 79, 128, 78, 65, + 77, 69, 128, 78, 65, 78, 65, 128, 78, 66, 73, 69, 128, 78, 73, 69, 85, + 206, 78, 78, 78, 65, 128, 78, 79, 68, 69, 128, 78, 89, 73, 80, 128, 78, + 89, 79, 80, 128, 78, 90, 85, 80, 128, 80, 65, 71, 69, 128, 80, 65, 80, + 69, 210, 80, 65, 87, 78, 128, 80, 72, 65, 82, 128, 80, 73, 69, 85, 208, + 80, 73, 87, 82, 128, 80, 76, 65, 67, 197, 80, 79, 85, 78, 196, 80, 87, + 73, 73, 128, 80, 87, 79, 79, 128, 81, 85, 79, 84, 197, 82, 65, 89, 83, + 128, 82, 66, 65, 83, 193, 82, 73, 69, 85, 204, 82, 73, 83, 72, 128, 82, + 79, 79, 75, 128, 82, 87, 65, 65, 128, 83, 65, 76, 76, 193, 83, 65, 76, + 84, 128, 83, 69, 65, 76, 128, 83, 72, 65, 65, 128, 83, 72, 65, 84, 128, + 83, 72, 69, 69, 128, 83, 72, 69, 73, 128, 83, 72, 72, 65, 128, 83, 72, + 73, 70, 212, 83, 72, 79, 71, 201, 83, 72, 85, 82, 128, 83, 72, 87, 69, + 128, 83, 72, 87, 73, 128, 83, 72, 87, 79, 128, 83, 76, 85, 82, 128, 83, + 77, 65, 83, 200, 83, 78, 79, 85, 212, 83, 80, 65, 68, 197, 83, 81, 85, + 65, 212, 83, 85, 75, 85, 206, 83, 87, 73, 73, 128, 83, 87, 79, 79, 128, + 84, 69, 88, 84, 128, 84, 72, 69, 82, 197, 84, 72, 79, 79, 128, 84, 73, + 77, 69, 128, 84, 73, 87, 78, 128, 84, 76, 72, 65, 128, 84, 76, 72, 69, + 128, 84, 76, 72, 73, 128, 84, 76, 72, 79, 128, 84, 82, 85, 69, 128, 84, + 83, 72, 65, 128, 84, 83, 72, 69, 128, 84, 84, 72, 79, 128, 84, 87, 73, + 73, 128, 84, 87, 79, 79, 128, 85, 78, 68, 69, 210, 87, 65, 76, 75, 128, + 87, 65, 83, 76, 193, 87, 65, 84, 69, 210, 87, 72, 79, 76, 197, 87, 73, + 78, 69, 128, 87, 79, 79, 68, 128, 87, 89, 78, 78, 128, 89, 79, 45, 73, + 128, 89, 79, 71, 72, 128, 89, 85, 45, 73, 128, 89, 87, 73, 73, 128, 89, + 87, 79, 79, 128, 90, 65, 73, 78, 128, 90, 65, 81, 69, 198, 90, 72, 65, + 82, 128, 90, 76, 65, 77, 193, 45, 67, 72, 65, 210, 65, 69, 83, 67, 128, + 65, 72, 83, 65, 128, 65, 73, 76, 77, 128, 65, 73, 78, 78, 128, 65, 75, + 66, 65, 210, 65, 76, 71, 73, 218, 65, 76, 76, 65, 200, 65, 77, 80, 83, + 128, 65, 78, 75, 72, 128, 65, 78, 83, 85, 218, 65, 85, 78, 78, 128, 65, + 89, 65, 72, 128, 66, 65, 72, 84, 128, 66, 65, 82, 83, 128, 66, 65, 83, + 69, 128, 66, 66, 65, 80, 128, 66, 66, 65, 84, 128, 66, 66, 65, 88, 128, + 66, 66, 69, 80, 128, 66, 66, 69, 88, 128, 66, 66, 73, 69, 128, 66, 66, + 73, 80, 128, 66, 66, 73, 84, 128, 66, 66, 73, 88, 128, 66, 66, 79, 80, + 128, 66, 66, 79, 84, 128, 66, 66, 79, 88, 128, 66, 66, 85, 79, 128, 66, + 66, 85, 80, 128, 66, 66, 85, 82, 128, 66, 66, 85, 88, 128, 66, 66, 89, + 80, 128, 66, 66, 89, 84, 128, 66, 66, 89, 88, 128, 66, 67, 65, 68, 128, + 66, 69, 65, 78, 128, 66, 69, 69, 72, 128, 66, 69, 76, 76, 128, 66, 69, + 76, 84, 128, 66, 69, 78, 68, 128, 66, 69, 79, 82, 195, 66, 69, 84, 72, + 128, 66, 73, 82, 85, 128, 66, 76, 65, 78, 203, 66, 79, 65, 84, 128, 66, + 79, 68, 89, 128, 66, 83, 68, 85, 211, 66, 83, 75, 85, 210, 66, 85, 77, + 80, 217, 67, 65, 65, 73, 128, 67, 65, 76, 67, 128, 67, 65, 76, 76, 128, + 67, 65, 80, 79, 128, 67, 65, 86, 69, 128, 67, 72, 65, 65, 128, 67, 72, + 65, 78, 128, 67, 72, 65, 80, 128, 67, 72, 65, 84, 128, 67, 72, 65, 88, + 128, 67, 72, 69, 80, 128, 67, 72, 69, 84, 128, 67, 72, 69, 88, 128, 67, + 72, 79, 69, 128, 67, 72, 79, 80, 128, 67, 72, 79, 84, 128, 67, 72, 79, + 88, 128, 67, 72, 85, 79, 128, 67, 72, 85, 80, 128, 67, 72, 85, 82, 128, + 67, 72, 85, 88, 128, 67, 72, 89, 80, 128, 67, 72, 89, 82, 128, 67, 72, + 89, 84, 128, 67, 72, 89, 88, 128, 67, 73, 69, 80, 128, 67, 73, 69, 84, + 128, 67, 73, 69, 88, 128, 67, 76, 65, 78, 128, 67, 76, 65, 87, 128, 67, + 76, 69, 65, 210, 67, 76, 79, 83, 197, 67, 79, 68, 65, 128, 67, 79, 76, + 76, 128, 67, 79, 80, 89, 128, 67, 85, 79, 88, 128, 67, 85, 82, 88, 128, + 67, 89, 82, 88, 128, 68, 65, 71, 65, 218, 68, 65, 71, 83, 128, 68, 65, + 73, 82, 128, 68, 65, 77, 80, 128, 68, 68, 65, 65, 128, 68, 68, 65, 76, + 128, 68, 68, 65, 80, 128, 68, 68, 65, 84, 128, 68, 68, 65, 88, 128, 68, + 68, 69, 69, 128, 68, 68, 69, 80, 128, 68, 68, 69, 88, 128, 68, 68, 73, + 69, 128, 68, 68, 73, 80, 128, 68, 68, 73, 84, 128, 68, 68, 73, 88, 128, + 68, 68, 79, 80, 128, 68, 68, 79, 84, 128, 68, 68, 79, 88, 128, 68, 68, + 85, 79, 128, 68, 68, 85, 80, 128, 68, 68, 85, 84, 128, 68, 68, 85, 88, + 128, 68, 68, 87, 65, 128, 68, 69, 69, 82, 128, 68, 69, 72, 73, 128, 68, + 69, 75, 65, 128, 68, 69, 83, 73, 128, 68, 73, 80, 76, 201, 68, 73, 83, + 72, 128, 68, 73, 84, 84, 207, 68, 76, 69, 69, 128, 68, 79, 73, 84, 128, + 68, 79, 79, 82, 128, 68, 79, 82, 85, 128, 68, 82, 85, 77, 128, 68, 89, + 69, 72, 128, 68, 90, 69, 69, 128, 69, 65, 82, 84, 200, 69, 72, 87, 65, + 218, 69, 78, 84, 69, 210, 69, 84, 72, 69, 204, 69, 85, 45, 85, 128, 69, + 85, 76, 69, 210, 70, 65, 65, 73, 128, 70, 65, 78, 71, 128, 70, 76, 73, + 80, 128, 70, 79, 82, 77, 211, 70, 82, 65, 78, 195, 70, 85, 82, 88, 128, + 71, 65, 77, 65, 204, 71, 68, 65, 78, 128, 71, 71, 65, 65, 128, 71, 71, + 65, 80, 128, 71, 71, 65, 84, 128, 71, 71, 65, 88, 128, 71, 71, 69, 69, + 128, 71, 71, 69, 80, 128, 71, 71, 69, 84, 128, 71, 71, 69, 88, 128, 71, + 71, 73, 69, 128, 71, 71, 73, 84, 128, 71, 71, 73, 88, 128, 71, 71, 79, + 84, 128, 71, 71, 79, 88, 128, 71, 71, 85, 80, 128, 71, 71, 85, 82, 128, + 71, 71, 85, 84, 128, 71, 71, 85, 88, 128, 71, 72, 69, 69, 128, 71, 73, + 66, 65, 128, 71, 73, 69, 84, 128, 71, 73, 71, 65, 128, 71, 79, 82, 84, + 128, 71, 85, 69, 72, 128, 71, 87, 65, 65, 128, 71, 87, 69, 69, 128, 72, + 65, 69, 71, 204, 72, 65, 71, 76, 128, 72, 69, 77, 80, 128, 72, 69, 84, + 72, 128, 72, 72, 69, 69, 128, 72, 72, 87, 65, 128, 72, 73, 69, 88, 128, + 72, 73, 71, 72, 128, 72, 73, 90, 66, 128, 72, 76, 65, 80, 128, 72, 76, + 65, 84, 128, 72, 76, 65, 88, 128, 72, 76, 69, 80, 128, 72, 76, 69, 88, + 128, 72, 76, 73, 69, 128, 72, 76, 73, 80, 128, 72, 76, 73, 84, 128, 72, + 76, 73, 88, 128, 72, 76, 79, 80, 128, 72, 76, 79, 88, 128, 72, 76, 85, + 79, 128, 72, 76, 85, 80, 128, 72, 76, 85, 82, 128, 72, 76, 85, 84, 128, + 72, 76, 85, 88, 128, 72, 76, 89, 80, 128, 72, 76, 89, 82, 128, 72, 76, + 89, 84, 128, 72, 76, 89, 88, 128, 72, 77, 65, 80, 128, 72, 77, 65, 84, + 128, 72, 77, 65, 88, 128, 72, 77, 73, 69, 128, 72, 77, 73, 80, 128, 72, + 77, 73, 84, 128, 72, 77, 73, 88, 128, 72, 77, 79, 80, 128, 72, 77, 79, + 84, 128, 72, 77, 79, 88, 128, 72, 77, 85, 79, 128, 72, 77, 85, 80, 128, + 72, 77, 85, 82, 128, 72, 77, 85, 84, 128, 72, 77, 85, 88, 128, 72, 77, + 89, 80, 128, 72, 77, 89, 82, 128, 72, 77, 89, 88, 128, 72, 78, 65, 80, + 128, 72, 78, 65, 84, 128, 72, 78, 65, 88, 128, 72, 78, 69, 80, 128, 72, + 78, 69, 88, 128, 72, 78, 73, 69, 128, 72, 78, 73, 80, 128, 72, 78, 73, + 84, 128, 72, 78, 73, 88, 128, 72, 78, 79, 80, 128, 72, 78, 79, 84, 128, + 72, 78, 79, 88, 128, 72, 78, 85, 79, 128, 72, 78, 85, 84, 128, 72, 79, + 79, 78, 128, 72, 88, 65, 80, 128, 72, 88, 65, 84, 128, 72, 88, 65, 88, + 128, 72, 88, 69, 80, 128, 72, 88, 69, 88, 128, 72, 88, 73, 69, 128, 72, + 88, 73, 80, 128, 72, 88, 73, 88, 128, 72, 88, 79, 84, 128, 72, 88, 79, + 88, 128, 73, 45, 69, 85, 128, 73, 45, 89, 65, 128, 73, 68, 76, 69, 128, + 73, 70, 73, 78, 128, 73, 76, 85, 89, 128, 73, 78, 67, 72, 128, 73, 78, + 78, 78, 128, 73, 78, 84, 73, 128, 73, 83, 79, 78, 128, 73, 84, 69, 77, + 128, 73, 85, 74, 65, 128, 74, 69, 82, 65, 206, 74, 74, 69, 69, 128, 74, + 74, 73, 80, 128, 74, 74, 73, 84, 128, 74, 74, 73, 88, 128, 74, 74, 79, + 80, 128, 74, 74, 79, 84, 128, 74, 74, 79, 88, 128, 74, 74, 85, 79, 128, + 74, 74, 85, 80, 128, 74, 74, 85, 82, 128, 74, 74, 85, 88, 128, 74, 74, + 89, 80, 128, 74, 74, 89, 84, 128, 74, 74, 89, 88, 128, 74, 85, 76, 89, + 128, 74, 85, 78, 69, 128, 74, 85, 79, 84, 128, 75, 65, 65, 73, 128, 75, + 65, 80, 72, 128, 75, 67, 65, 76, 128, 75, 72, 65, 65, 128, 75, 72, 65, + 73, 128, 75, 72, 65, 78, 128, 75, 72, 69, 69, 128, 75, 72, 79, 78, 128, + 75, 73, 69, 80, 128, 75, 73, 69, 88, 128, 75, 73, 82, 79, 128, 75, 75, + 69, 69, 128, 75, 79, 77, 66, 213, 75, 79, 84, 79, 128, 75, 85, 79, 80, + 128, 75, 85, 79, 88, 128, 75, 85, 82, 88, 128, 75, 85, 85, 72, 128, 75, + 87, 69, 69, 128, 75, 88, 65, 65, 128, 75, 88, 69, 69, 128, 75, 88, 87, + 65, 128, 75, 88, 87, 69, 128, 75, 88, 87, 73, 128, 76, 65, 65, 73, 128, + 76, 65, 77, 69, 128, 76, 65, 77, 69, 196, 76, 68, 65, 78, 128, 76, 69, + 69, 75, 128, 76, 69, 71, 83, 128, 76, 69, 86, 69, 204, 76, 69, 90, 72, + 128, 76, 72, 65, 65, 128, 76, 72, 73, 73, 128, 76, 72, 79, 79, 128, 76, + 73, 69, 84, 128, 76, 73, 70, 69, 128, 76, 79, 79, 84, 128, 76, 85, 73, + 83, 128, 76, 85, 79, 84, 128, 77, 65, 65, 73, 128, 77, 69, 83, 72, 128, + 77, 69, 83, 73, 128, 77, 71, 65, 80, 128, 77, 71, 65, 84, 128, 77, 71, + 65, 88, 128, 77, 71, 69, 80, 128, 77, 71, 69, 88, 128, 77, 71, 73, 69, + 128, 77, 71, 79, 80, 128, 77, 71, 79, 84, 128, 77, 71, 79, 88, 128, 77, + 71, 85, 79, 128, 77, 71, 85, 80, 128, 77, 71, 85, 82, 128, 77, 71, 85, + 84, 128, 77, 71, 85, 88, 128, 77, 73, 67, 82, 207, 77, 73, 76, 76, 197, + 77, 73, 78, 89, 128, 77, 73, 82, 73, 128, 77, 79, 86, 69, 196, 77, 85, + 73, 78, 128, 77, 85, 76, 84, 201, 77, 85, 79, 84, 128, 78, 65, 65, 73, + 128, 78, 65, 73, 82, 193, 78, 65, 78, 68, 128, 78, 66, 65, 80, 128, 78, + 66, 65, 84, 128, 78, 66, 65, 88, 128, 78, 66, 73, 80, 128, 78, 66, 73, + 84, 128, 78, 66, 73, 88, 128, 78, 66, 79, 80, 128, 78, 66, 79, 84, 128, + 78, 66, 79, 88, 128, 78, 66, 85, 80, 128, 78, 66, 85, 82, 128, 78, 66, + 85, 84, 128, 78, 66, 85, 88, 128, 78, 66, 89, 80, 128, 78, 66, 89, 82, + 128, 78, 66, 89, 84, 128, 78, 66, 89, 88, 128, 78, 68, 65, 80, 128, 78, + 68, 65, 84, 128, 78, 68, 65, 88, 128, 78, 68, 69, 80, 128, 78, 68, 73, + 69, 128, 78, 68, 73, 80, 128, 78, 68, 73, 84, 128, 78, 68, 73, 88, 128, + 78, 68, 79, 80, 128, 78, 68, 79, 84, 128, 78, 68, 79, 88, 128, 78, 68, + 85, 80, 128, 78, 68, 85, 82, 128, 78, 68, 85, 84, 128, 78, 68, 85, 88, + 128, 78, 71, 65, 73, 128, 78, 71, 65, 80, 128, 78, 71, 65, 84, 128, 78, + 71, 65, 88, 128, 78, 71, 69, 80, 128, 78, 71, 69, 88, 128, 78, 71, 73, + 69, 128, 78, 71, 79, 80, 128, 78, 71, 79, 84, 128, 78, 71, 79, 88, 128, + 78, 71, 85, 79, 128, 78, 74, 73, 69, 128, 78, 74, 73, 80, 128, 78, 74, + 73, 84, 128, 78, 74, 73, 88, 128, 78, 74, 79, 80, 128, 78, 74, 79, 84, + 128, 78, 74, 79, 88, 128, 78, 74, 85, 79, 128, 78, 74, 85, 80, 128, 78, + 74, 85, 82, 128, 78, 74, 85, 88, 128, 78, 74, 89, 80, 128, 78, 74, 89, + 82, 128, 78, 74, 89, 84, 128, 78, 74, 89, 88, 128, 78, 78, 71, 65, 128, + 78, 78, 71, 73, 128, 78, 78, 71, 79, 128, 78, 79, 83, 69, 128, 78, 82, + 65, 80, 128, 78, 82, 65, 84, 128, 78, 82, 65, 88, 128, 78, 82, 69, 80, + 128, 78, 82, 69, 84, 128, 78, 82, 69, 88, 128, 78, 82, 79, 80, 128, 78, + 82, 79, 88, 128, 78, 82, 85, 80, 128, 78, 82, 85, 82, 128, 78, 82, 85, + 84, 128, 78, 82, 85, 88, 128, 78, 82, 89, 80, 128, 78, 82, 89, 82, 128, + 78, 82, 89, 84, 128, 78, 82, 89, 88, 128, 78, 85, 76, 76, 128, 78, 85, + 79, 80, 128, 78, 85, 82, 88, 128, 78, 89, 65, 65, 128, 78, 89, 69, 69, + 128, 78, 89, 69, 72, 128, 78, 89, 73, 69, 128, 78, 89, 73, 84, 128, 78, + 89, 73, 88, 128, 78, 89, 79, 84, 128, 78, 89, 79, 88, 128, 78, 89, 85, + 79, 128, 78, 89, 85, 80, 128, 78, 89, 85, 84, 128, 78, 89, 85, 88, 128, + 78, 89, 87, 65, 128, 78, 90, 65, 80, 128, 78, 90, 65, 84, 128, 78, 90, + 65, 88, 128, 78, 90, 69, 88, 128, 78, 90, 73, 69, 128, 78, 90, 73, 80, + 128, 78, 90, 73, 84, 128, 78, 90, 73, 88, 128, 78, 90, 79, 80, 128, 78, + 90, 79, 88, 128, 78, 90, 85, 79, 128, 78, 90, 85, 82, 128, 78, 90, 85, + 88, 128, 78, 90, 89, 80, 128, 78, 90, 89, 82, 128, 78, 90, 89, 84, 128, + 78, 90, 89, 88, 128, 79, 45, 69, 79, 128, 79, 45, 89, 69, 128, 79, 78, + 83, 85, 128, 79, 79, 77, 85, 128, 79, 85, 78, 67, 197, 80, 65, 65, 73, + 128, 80, 65, 68, 77, 193, 80, 65, 82, 65, 128, 80, 69, 65, 67, 197, 80, + 69, 78, 78, 217, 80, 69, 83, 79, 128, 80, 72, 65, 65, 128, 80, 72, 65, + 78, 128, 80, 72, 69, 69, 128, 80, 72, 87, 65, 128, 80, 73, 69, 80, 128, + 80, 73, 69, 88, 128, 80, 73, 75, 79, 128, 80, 76, 79, 87, 128, 80, 82, + 73, 78, 212, 80, 85, 79, 80, 128, 80, 85, 79, 88, 128, 80, 85, 82, 88, + 128, 80, 89, 82, 88, 128, 81, 65, 65, 73, 128, 81, 65, 80, 72, 128, 81, + 72, 65, 65, 128, 81, 72, 69, 69, 128, 81, 72, 87, 65, 128, 81, 72, 87, + 69, 128, 81, 72, 87, 73, 128, 81, 73, 69, 80, 128, 81, 73, 69, 84, 128, + 81, 73, 69, 88, 128, 81, 85, 79, 80, 128, 81, 85, 79, 84, 128, 81, 85, + 79, 88, 128, 81, 85, 82, 88, 128, 81, 85, 85, 86, 128, 81, 87, 65, 65, + 128, 81, 87, 69, 69, 128, 81, 89, 82, 88, 128, 82, 65, 65, 73, 128, 82, + 65, 73, 68, 207, 82, 65, 78, 71, 197, 82, 69, 77, 85, 128, 82, 73, 67, + 69, 128, 82, 73, 69, 76, 128, 82, 73, 82, 65, 128, 82, 82, 65, 88, 128, + 82, 82, 69, 72, 128, 82, 82, 69, 80, 128, 82, 82, 69, 84, 128, 82, 82, + 69, 88, 128, 82, 82, 79, 80, 128, 82, 82, 79, 84, 128, 82, 82, 79, 88, + 128, 82, 82, 85, 79, 128, 82, 82, 85, 80, 128, 82, 82, 85, 82, 128, 82, + 82, 85, 84, 128, 82, 82, 85, 88, 128, 82, 82, 89, 80, 128, 82, 82, 89, + 82, 128, 82, 82, 89, 84, 128, 82, 82, 89, 88, 128, 82, 85, 73, 83, 128, + 82, 85, 76, 69, 128, 82, 85, 79, 80, 128, 82, 85, 83, 73, 128, 83, 65, + 65, 73, 128, 83, 65, 73, 76, 128, 83, 65, 76, 65, 128, 83, 65, 76, 65, + 205, 83, 66, 82, 85, 204, 83, 67, 87, 65, 128, 83, 68, 79, 78, 199, 83, + 72, 65, 80, 128, 83, 72, 65, 88, 128, 83, 72, 69, 80, 128, 83, 72, 69, + 84, 128, 83, 72, 69, 88, 128, 83, 72, 73, 73, 128, 83, 72, 79, 79, 128, + 83, 72, 79, 84, 128, 83, 72, 79, 88, 128, 83, 72, 85, 79, 128, 83, 72, + 85, 80, 128, 83, 72, 85, 84, 128, 83, 72, 85, 88, 128, 83, 72, 89, 80, + 128, 83, 72, 89, 82, 128, 83, 72, 89, 84, 128, 83, 72, 89, 88, 128, 83, + 73, 71, 69, 204, 83, 73, 71, 77, 193, 83, 75, 73, 78, 128, 83, 75, 85, + 76, 204, 83, 75, 87, 65, 128, 83, 80, 79, 84, 128, 83, 80, 87, 65, 128, + 83, 83, 65, 80, 128, 83, 83, 65, 84, 128, 83, 83, 65, 88, 128, 83, 83, + 69, 80, 128, 83, 83, 69, 88, 128, 83, 83, 73, 69, 128, 83, 83, 73, 80, + 128, 83, 83, 73, 84, 128, 83, 83, 73, 88, 128, 83, 83, 79, 80, 128, 83, + 83, 79, 84, 128, 83, 83, 79, 88, 128, 83, 83, 85, 80, 128, 83, 83, 85, + 84, 128, 83, 83, 85, 88, 128, 83, 83, 89, 80, 128, 83, 83, 89, 82, 128, + 83, 83, 89, 84, 128, 83, 83, 89, 88, 128, 83, 84, 65, 78, 128, 83, 84, + 69, 80, 128, 83, 84, 73, 76, 197, 83, 84, 87, 65, 128, 83, 85, 79, 80, + 128, 83, 85, 79, 88, 128, 83, 85, 82, 88, 128, 83, 87, 65, 83, 200, 83, + 90, 65, 65, 128, 83, 90, 69, 69, 128, 83, 90, 87, 65, 128, 84, 65, 65, + 73, 128, 84, 65, 75, 69, 128, 84, 65, 76, 76, 128, 84, 69, 45, 85, 128, + 84, 69, 78, 84, 128, 84, 69, 84, 72, 128, 84, 72, 69, 72, 128, 84, 72, + 69, 77, 193, 84, 72, 73, 82, 196, 84, 72, 85, 82, 211, 84, 72, 87, 65, + 128, 84, 73, 69, 80, 128, 84, 73, 69, 88, 128, 84, 73, 71, 72, 212, 84, + 73, 78, 89, 128, 84, 73, 87, 65, 218, 84, 76, 69, 69, 128, 84, 76, 72, + 85, 128, 84, 79, 84, 65, 204, 84, 82, 65, 68, 197, 84, 82, 69, 69, 128, + 84, 82, 73, 79, 206, 84, 83, 65, 65, 128, 84, 83, 65, 68, 201, 84, 83, + 87, 65, 128, 84, 84, 65, 65, 128, 84, 84, 69, 69, 128, 84, 84, 69, 72, + 128, 84, 84, 72, 69, 128, 84, 84, 72, 73, 128, 84, 84, 83, 65, 128, 84, + 84, 83, 69, 128, 84, 84, 83, 73, 128, 84, 84, 83, 79, 128, 84, 84, 83, + 85, 128, 84, 85, 79, 80, 128, 84, 85, 79, 84, 128, 84, 85, 79, 88, 128, + 84, 85, 82, 88, 128, 84, 90, 65, 65, 128, 84, 90, 69, 69, 128, 85, 45, + 65, 69, 128, 85, 65, 84, 72, 128, 86, 73, 69, 80, 128, 86, 73, 69, 84, + 128, 86, 73, 69, 88, 128, 86, 85, 82, 88, 128, 86, 89, 82, 88, 128, 87, + 65, 69, 78, 128, 87, 65, 76, 76, 128, 87, 69, 83, 84, 128, 87, 79, 82, + 75, 128, 87, 82, 65, 80, 128, 87, 85, 78, 74, 207, 87, 85, 79, 80, 128, + 87, 85, 79, 88, 128, 88, 73, 82, 79, 206, 88, 89, 82, 88, 128, 89, 65, + 45, 79, 128, 89, 65, 65, 73, 128, 89, 65, 78, 71, 128, 89, 69, 82, 65, + 200, 89, 73, 45, 85, 128, 89, 73, 78, 71, 128, 89, 79, 45, 79, 128, 89, + 79, 77, 79, 128, 89, 79, 82, 73, 128, 89, 85, 45, 65, 128, 89, 85, 45, + 69, 128, 89, 85, 45, 85, 128, 89, 85, 65, 78, 128, 89, 85, 68, 72, 128, + 89, 85, 79, 84, 128, 89, 85, 82, 88, 128, 89, 89, 82, 88, 128, 90, 65, + 89, 73, 206, 90, 72, 65, 65, 128, 90, 72, 65, 80, 128, 90, 72, 65, 84, + 128, 90, 72, 65, 88, 128, 90, 72, 69, 80, 128, 90, 72, 69, 84, 128, 90, + 72, 69, 88, 128, 90, 72, 79, 80, 128, 90, 72, 79, 84, 128, 90, 72, 79, + 88, 128, 90, 72, 85, 79, 128, 90, 72, 85, 80, 128, 90, 72, 85, 82, 128, + 90, 72, 85, 84, 128, 90, 72, 85, 88, 128, 90, 72, 87, 65, 128, 90, 72, + 89, 80, 128, 90, 72, 89, 82, 128, 90, 72, 89, 84, 128, 90, 72, 89, 88, + 128, 90, 85, 79, 80, 128, 90, 90, 65, 80, 128, 90, 90, 65, 84, 128, 90, + 90, 65, 88, 128, 90, 90, 69, 80, 128, 90, 90, 69, 88, 128, 90, 90, 73, + 69, 128, 90, 90, 73, 80, 128, 90, 90, 73, 84, 128, 90, 90, 73, 88, 128, + 90, 90, 79, 80, 128, 90, 90, 79, 88, 128, 90, 90, 85, 80, 128, 90, 90, + 85, 82, 128, 90, 90, 85, 88, 128, 90, 90, 89, 80, 128, 90, 90, 89, 82, + 128, 90, 90, 89, 84, 128, 90, 90, 89, 88, 128, 68, 79, 84, 211, 70, 82, + 79, 205, 72, 79, 79, 203, 79, 80, 69, 206, 84, 79, 68, 207, 69, 65, 83, + 212, 84, 79, 78, 197, 72, 79, 85, 210, 83, 73, 66, 197, 72, 79, 82, 206, + 81, 85, 65, 196, 68, 65, 83, 200, 78, 69, 79, 128, 84, 69, 78, 128, 84, + 72, 69, 200, 75, 79, 77, 201, 67, 72, 73, 128, 79, 86, 69, 210, 79, 88, + 73, 193, 70, 79, 85, 210, 80, 72, 73, 128, 80, 83, 73, 128, 82, 72, 79, + 128, 83, 65, 82, 193, 84, 65, 67, 203, 71, 72, 65, 128, 66, 72, 65, 128, + 68, 79, 69, 211, 84, 65, 85, 128, 67, 72, 69, 128, 74, 72, 65, 128, 70, + 73, 86, 197, 82, 82, 65, 128, 87, 73, 68, 197, 82, 68, 69, 204, 83, 72, + 73, 206, 87, 65, 86, 217, 89, 85, 83, 128, 90, 65, 73, 206, 67, 65, 78, + 128, 68, 73, 71, 193, 65, 82, 67, 128, 83, 84, 79, 208, 84, 65, 77, 128, + 68, 90, 69, 128, 71, 72, 69, 128, 71, 79, 65, 204, 71, 84, 69, 210, 75, + 65, 70, 128, 76, 69, 71, 128, 78, 89, 79, 128, 83, 72, 79, 128, 83, 84, + 65, 210, 83, 85, 78, 128, 84, 65, 73, 204, 87, 65, 86, 197, 65, 80, 76, + 201, 66, 69, 69, 200, 67, 72, 79, 128, 67, 76, 69, 198, 68, 74, 69, 128, + 68, 75, 65, 210, 68, 89, 69, 200, 68, 90, 65, 128, 70, 69, 72, 128, 70, + 73, 83, 200, 71, 85, 69, 200, 76, 65, 77, 197, 76, 74, 69, 128, 77, 71, + 79, 128, 77, 85, 67, 200, 78, 65, 77, 197, 78, 74, 69, 128, 78, 79, 87, + 128, 78, 89, 69, 200, 80, 69, 69, 128, 82, 65, 65, 128, 83, 72, 73, 128, + 84, 72, 69, 206, 84, 73, 67, 203, 84, 84, 69, 200, 87, 65, 87, 128, 90, + 69, 82, 207, 66, 69, 69, 128, 66, 79, 87, 128, 66, 90, 72, 201, 67, 72, + 85, 128, 70, 76, 65, 212, 70, 82, 69, 197, 72, 73, 69, 128, 75, 69, 72, + 128, 75, 87, 69, 128, 75, 87, 73, 128, 76, 87, 65, 128, 77, 69, 77, 128, + 77, 69, 78, 128, 77, 87, 65, 128, 78, 65, 65, 128, 78, 85, 78, 128, 78, + 87, 65, 128, 78, 89, 73, 211, 80, 69, 72, 128, 81, 65, 65, 128, 82, 72, + 65, 128, 83, 72, 79, 197, 83, 72, 85, 128, 84, 72, 65, 204, 84, 79, 79, + 128, 86, 69, 69, 128, 87, 65, 69, 128, 87, 65, 76, 203, 87, 69, 79, 128, + 88, 69, 72, 128, 89, 79, 68, 128, 89, 89, 65, 128, 65, 76, 76, 201, 65, + 89, 66, 128, 66, 65, 65, 128, 66, 69, 72, 128, 66, 69, 78, 128, 66, 79, + 76, 212, 67, 65, 65, 128, 67, 73, 80, 128, 67, 76, 85, 194, 67, 79, 79, + 128, 67, 79, 87, 128, 67, 85, 80, 128, 67, 87, 69, 128, 67, 87, 73, 128, + 67, 87, 79, 128, 67, 89, 80, 128, 67, 89, 84, 128, 68, 68, 65, 204, 68, + 68, 69, 128, 68, 68, 73, 128, 68, 68, 85, 128, 68, 69, 73, 128, 68, 76, + 65, 128, 68, 79, 71, 128, 68, 79, 78, 128, 68, 82, 85, 205, 70, 65, 65, + 128, 70, 69, 73, 128, 70, 76, 89, 128, 70, 85, 82, 128, 70, 85, 83, 193, + 71, 65, 78, 128, 71, 65, 89, 128, 71, 71, 65, 128, 71, 71, 69, 128, 71, + 71, 73, 128, 71, 71, 79, 128, 71, 71, 85, 128, 71, 73, 77, 128, 71, 74, + 69, 128, 72, 65, 69, 128, 72, 65, 82, 196, 72, 77, 79, 128, 72, 78, 65, + 128, 73, 83, 79, 206, 74, 73, 76, 128, 74, 74, 73, 128, 74, 74, 79, 128, + 74, 74, 85, 128, 74, 74, 89, 128, 75, 65, 89, 128, 75, 69, 78, 128, 75, + 72, 69, 128, 75, 72, 79, 128, 75, 73, 84, 128, 75, 74, 69, 128, 75, 79, + 79, 128, 75, 83, 73, 128, 75, 87, 79, 128, 76, 65, 65, 128, 76, 65, 83, + 128, 76, 79, 79, 128, 76, 87, 69, 128, 76, 87, 73, 128, 76, 87, 79, 128, + 77, 65, 65, 128, 77, 79, 79, 128, 77, 79, 79, 206, 77, 87, 69, 128, 77, + 87, 73, 128, 77, 87, 79, 128, 78, 65, 82, 128, 78, 69, 69, 128, 78, 71, + 65, 211, 78, 73, 66, 128, 78, 79, 79, 128, 78, 79, 84, 197, 78, 87, 69, + 128, 78, 89, 73, 128, 78, 89, 85, 128, 79, 72, 77, 128, 79, 75, 84, 207, + 79, 78, 78, 128, 80, 65, 65, 128, 80, 65, 82, 128, 80, 65, 82, 212, 80, + 65, 84, 200, 80, 72, 79, 128, 80, 72, 85, 210, 80, 79, 76, 201, 80, 79, + 79, 128, 80, 85, 84, 128, 80, 87, 69, 128, 80, 87, 73, 128, 80, 87, 79, + 128, 80, 89, 84, 128, 81, 65, 73, 128, 81, 65, 82, 128, 81, 73, 73, 128, + 81, 79, 84, 128, 81, 85, 79, 128, 81, 85, 85, 128, 82, 65, 69, 128, 82, + 71, 89, 193, 82, 78, 65, 205, 82, 82, 69, 200, 83, 69, 72, 128, 83, 72, + 65, 196, 83, 72, 89, 128, 83, 73, 79, 211, 83, 74, 69, 128, 83, 79, 79, + 128, 83, 83, 73, 128, 83, 83, 79, 128, 83, 87, 69, 128, 83, 87, 73, 128, + 83, 87, 79, 128, 84, 65, 71, 128, 84, 65, 84, 128, 84, 65, 86, 128, 84, + 74, 69, 128, 84, 76, 65, 128, 84, 76, 73, 128, 84, 76, 85, 128, 84, 82, + 69, 197, 84, 84, 73, 128, 84, 87, 69, 128, 84, 87, 73, 128, 85, 83, 69, + 196, 86, 65, 86, 128, 86, 69, 80, 128, 86, 69, 82, 217, 86, 69, 87, 128, + 86, 73, 78, 128, 86, 79, 85, 128, 86, 85, 82, 128, 88, 65, 78, 128, 89, + 65, 78, 199, 89, 65, 84, 128, 89, 69, 82, 213, 89, 70, 69, 206, 89, 79, + 79, 128, 89, 87, 69, 128, 89, 87, 73, 128, 89, 87, 79, 128, 90, 69, 78, + 128, 90, 72, 73, 128, 90, 72, 79, 128, 90, 72, 85, 128, 90, 79, 84, 128, + 65, 77, 66, 193, 65, 82, 67, 200, 65, 88, 69, 128, 66, 65, 78, 203, 66, + 66, 65, 128, 66, 66, 69, 128, 66, 66, 73, 128, 66, 66, 79, 128, 66, 66, + 85, 128, 66, 66, 89, 128, 66, 67, 65, 196, 66, 69, 76, 204, 66, 69, 76, + 212, 66, 69, 84, 128, 66, 69, 84, 193, 66, 73, 71, 128, 66, 75, 65, 173, + 66, 87, 65, 128, 67, 65, 68, 193, 67, 65, 78, 199, 67, 65, 82, 197, 67, + 65, 84, 128, 67, 65, 88, 128, 67, 69, 69, 128, 67, 69, 78, 128, 67, 69, + 80, 128, 67, 69, 88, 128, 67, 72, 65, 196, 67, 72, 69, 206, 67, 73, 69, + 128, 67, 73, 73, 128, 67, 73, 84, 128, 67, 73, 88, 128, 67, 79, 80, 128, + 67, 79, 84, 128, 67, 79, 88, 128, 67, 85, 66, 197, 67, 85, 79, 128, 67, + 85, 82, 128, 67, 85, 84, 128, 67, 85, 88, 128, 67, 89, 65, 128, 67, 89, + 82, 128, 67, 89, 88, 128, 68, 65, 68, 128, 68, 65, 69, 199, 68, 65, 77, + 208, 68, 65, 82, 203, 68, 69, 75, 128, 68, 69, 76, 128, 68, 69, 90, 200, + 68, 76, 73, 128, 68, 76, 79, 128, 68, 76, 85, 128, 68, 82, 73, 204, 68, + 82, 89, 128, 68, 85, 76, 128, 68, 89, 79, 128, 68, 90, 73, 128, 68, 90, + 79, 128, 68, 90, 85, 128, 69, 73, 83, 128, 69, 75, 83, 128, 69, 78, 78, + 128, 69, 78, 79, 211, 69, 79, 72, 128, 69, 85, 82, 207, 69, 87, 69, 128, + 69, 88, 79, 128, 70, 65, 78, 128, 70, 65, 80, 128, 70, 65, 84, 128, 70, + 65, 88, 128, 70, 69, 69, 128, 70, 69, 69, 196, 70, 69, 72, 213, 70, 69, + 78, 199, 70, 69, 79, 200, 70, 70, 73, 128, 70, 70, 76, 128, 70, 73, 73, + 128, 70, 73, 76, 197, 70, 73, 76, 204, 70, 73, 80, 128, 70, 73, 84, 128, + 70, 73, 88, 128, 70, 79, 79, 128, 70, 79, 80, 128, 70, 79, 88, 128, 70, + 85, 80, 128, 70, 85, 84, 128, 70, 85, 88, 128, 70, 87, 65, 128, 70, 89, + 65, 128, 70, 89, 80, 128, 70, 89, 84, 128, 70, 89, 88, 128, 71, 65, 70, + 128, 71, 65, 82, 128, 71, 67, 65, 206, 71, 69, 66, 207, 71, 69, 84, 193, + 71, 72, 73, 128, 71, 72, 79, 128, 71, 72, 85, 128, 71, 72, 90, 128, 71, + 73, 80, 128, 71, 80, 65, 128, 71, 83, 85, 205, 71, 87, 65, 128, 71, 87, + 69, 128, 71, 87, 73, 128, 71, 89, 70, 213, 72, 65, 73, 210, 72, 69, 76, + 205, 72, 69, 78, 199, 72, 72, 69, 128, 72, 72, 73, 128, 72, 72, 79, 128, + 72, 72, 85, 128, 72, 76, 65, 128, 72, 76, 69, 128, 72, 76, 73, 128, 72, + 76, 79, 128, 72, 76, 85, 128, 72, 76, 89, 128, 72, 77, 65, 128, 72, 77, + 73, 128, 72, 77, 85, 128, 72, 77, 89, 128, 72, 78, 69, 128, 72, 78, 73, + 128, 72, 80, 65, 128, 72, 87, 85, 128, 72, 88, 65, 128, 72, 88, 69, 128, + 72, 88, 73, 128, 72, 88, 79, 128, 73, 45, 65, 128, 73, 45, 79, 128, 73, + 79, 82, 128, 74, 65, 65, 128, 74, 65, 82, 128, 74, 69, 72, 128, 74, 69, + 82, 128, 74, 73, 65, 128, 74, 74, 65, 128, 74, 74, 69, 128, 74, 87, 65, + 128, 75, 65, 72, 128, 75, 65, 73, 128, 75, 65, 80, 128, 75, 65, 85, 206, + 75, 65, 88, 128, 75, 69, 80, 128, 75, 69, 88, 128, 75, 69, 89, 128, 75, + 72, 73, 128, 75, 72, 90, 128, 75, 73, 69, 128, 75, 73, 72, 128, 75, 73, + 73, 128, 75, 73, 80, 128, 75, 73, 88, 128, 75, 75, 65, 128, 75, 75, 69, + 128, 75, 75, 73, 128, 75, 75, 79, 128, 75, 75, 85, 128, 75, 79, 72, 128, + 75, 79, 80, 128, 75, 79, 84, 128, 75, 79, 88, 128, 75, 80, 65, 128, 75, + 82, 65, 128, 75, 85, 79, 128, 75, 85, 80, 128, 75, 85, 82, 128, 75, 85, + 84, 128, 75, 85, 88, 128, 75, 88, 65, 128, 75, 88, 69, 128, 75, 88, 73, + 128, 75, 88, 79, 128, 75, 88, 85, 128, 76, 65, 71, 213, 76, 65, 83, 212, + 76, 65, 90, 217, 76, 69, 79, 128, 76, 72, 65, 199, 76, 73, 68, 128, 76, + 73, 73, 128, 76, 73, 78, 203, 76, 73, 82, 193, 76, 79, 71, 128, 76, 79, + 71, 210, 76, 79, 84, 128, 76, 89, 89, 128, 77, 65, 83, 213, 77, 65, 89, + 128, 77, 67, 72, 213, 77, 68, 85, 206, 77, 69, 84, 193, 77, 69, 88, 128, + 77, 71, 65, 128, 77, 71, 69, 128, 77, 71, 85, 128, 77, 72, 90, 128, 77, + 73, 73, 128, 77, 73, 76, 128, 77, 73, 76, 204, 77, 73, 77, 128, 77, 79, + 76, 128, 77, 80, 65, 128, 77, 89, 65, 128, 77, 89, 84, 128, 78, 65, 71, + 128, 78, 65, 79, 211, 78, 66, 65, 128, 78, 66, 73, 128, 78, 66, 79, 128, + 78, 66, 85, 128, 78, 66, 89, 128, 78, 68, 69, 128, 78, 69, 78, 128, 78, + 69, 84, 128, 78, 69, 88, 212, 78, 71, 71, 128, 78, 74, 73, 128, 78, 74, + 79, 128, 78, 74, 85, 128, 78, 74, 89, 128, 78, 78, 71, 128, 78, 78, 79, + 128, 78, 82, 65, 128, 78, 82, 69, 128, 78, 82, 79, 128, 78, 82, 85, 128, + 78, 82, 89, 128, 78, 85, 76, 204, 78, 85, 80, 128, 78, 85, 82, 128, 78, + 85, 88, 128, 78, 89, 69, 128, 78, 90, 65, 128, 78, 90, 69, 128, 78, 90, + 73, 128, 78, 90, 85, 128, 78, 90, 89, 128, 79, 45, 69, 128, 80, 65, 80, + 128, 80, 65, 84, 128, 80, 65, 88, 128, 80, 72, 85, 128, 80, 73, 69, 128, + 80, 73, 71, 128, 80, 73, 80, 128, 80, 73, 84, 128, 80, 73, 88, 128, 80, + 76, 65, 128, 80, 79, 80, 128, 80, 79, 88, 128, 80, 80, 77, 128, 80, 85, + 79, 128, 80, 85, 80, 128, 80, 85, 82, 128, 80, 85, 88, 128, 80, 89, 80, + 128, 80, 89, 82, 128, 80, 89, 88, 128, 81, 65, 76, 193, 81, 65, 81, 128, + 81, 65, 85, 128, 81, 69, 69, 128, 81, 72, 65, 128, 81, 72, 69, 128, 81, + 72, 73, 128, 81, 72, 79, 128, 81, 72, 85, 128, 81, 73, 69, 128, 81, 73, + 80, 128, 81, 73, 84, 128, 81, 73, 88, 128, 81, 79, 70, 128, 81, 79, 79, + 128, 81, 79, 80, 128, 81, 79, 88, 128, 81, 85, 65, 128, 81, 85, 69, 128, + 81, 85, 73, 128, 81, 85, 75, 128, 81, 85, 80, 128, 81, 85, 82, 128, 81, + 85, 84, 128, 81, 85, 86, 128, 81, 85, 88, 128, 81, 87, 65, 128, 81, 87, + 69, 128, 81, 87, 73, 128, 81, 89, 80, 128, 81, 89, 82, 128, 81, 89, 84, + 128, 81, 89, 88, 128, 82, 65, 68, 128, 82, 65, 77, 211, 82, 69, 73, 196, + 82, 73, 80, 128, 82, 74, 69, 128, 82, 74, 69, 211, 82, 79, 79, 128, 82, + 82, 69, 128, 82, 82, 79, 128, 82, 82, 85, 128, 82, 82, 89, 128, 82, 85, + 65, 128, 82, 85, 78, 128, 82, 87, 65, 128, 82, 89, 65, 128, 82, 89, 89, + 128, 83, 45, 87, 128, 83, 65, 68, 128, 83, 65, 89, 128, 83, 66, 85, 194, + 83, 71, 65, 194, 83, 71, 79, 210, 83, 71, 82, 193, 83, 72, 79, 199, 83, + 73, 73, 128, 83, 73, 78, 197, 83, 75, 87, 128, 83, 78, 65, 208, 83, 83, + 69, 128, 83, 83, 85, 128, 83, 83, 89, 128, 83, 84, 69, 205, 83, 85, 65, + 128, 83, 85, 79, 128, 83, 85, 82, 128, 83, 90, 65, 128, 83, 90, 69, 128, + 83, 90, 73, 128, 83, 90, 79, 128, 83, 90, 85, 128, 84, 65, 79, 128, 84, + 65, 80, 128, 84, 65, 80, 197, 84, 65, 87, 128, 84, 65, 88, 128, 84, 69, + 83, 200, 84, 69, 84, 128, 84, 69, 84, 200, 84, 69, 88, 128, 84, 72, 69, + 211, 84, 72, 73, 206, 84, 72, 90, 128, 84, 73, 73, 128, 84, 73, 80, 128, + 84, 73, 84, 128, 84, 73, 88, 128, 84, 76, 86, 128, 84, 79, 84, 128, 84, + 79, 88, 128, 84, 82, 73, 128, 84, 83, 86, 128, 84, 84, 72, 128, 84, 84, + 85, 128, 84, 85, 79, 128, 84, 85, 80, 128, 84, 85, 82, 128, 84, 85, 84, + 128, 84, 85, 88, 128, 84, 89, 65, 128, 84, 89, 69, 128, 84, 89, 73, 128, + 84, 89, 79, 128, 84, 90, 65, 128, 84, 90, 69, 128, 84, 90, 73, 128, 84, + 90, 79, 128, 84, 90, 85, 128, 85, 69, 69, 128, 85, 78, 68, 207, 85, 78, + 73, 212, 85, 79, 78, 128, 85, 82, 85, 218, 86, 65, 65, 128, 86, 65, 80, + 128, 86, 65, 84, 128, 86, 65, 88, 128, 86, 69, 72, 128, 86, 69, 88, 128, + 86, 73, 69, 128, 86, 73, 80, 128, 86, 73, 84, 128, 86, 73, 88, 128, 86, + 79, 73, 196, 86, 79, 80, 128, 86, 79, 84, 128, 86, 79, 88, 128, 86, 85, + 80, 128, 86, 85, 84, 128, 86, 85, 88, 128, 86, 87, 65, 128, 86, 89, 80, + 128, 86, 89, 82, 128, 86, 89, 84, 128, 86, 89, 88, 128, 87, 65, 80, 128, + 87, 65, 84, 128, 87, 65, 88, 128, 87, 69, 80, 128, 87, 69, 88, 128, 87, + 79, 80, 128, 87, 79, 82, 196, 87, 79, 88, 128, 87, 85, 79, 128, 87, 89, + 78, 206, 88, 79, 82, 128, 88, 89, 80, 128, 88, 89, 82, 128, 88, 89, 84, + 128, 88, 89, 88, 128, 89, 65, 75, 128, 89, 73, 73, 128, 89, 85, 68, 200, + 89, 85, 82, 128, 89, 89, 80, 128, 89, 89, 82, 128, 89, 89, 84, 128, 89, + 89, 88, 128, 90, 65, 72, 128, 90, 72, 89, 128, 90, 76, 65, 128, 90, 82, + 65, 128, 90, 85, 84, 128, 90, 90, 65, 128, 90, 90, 69, 128, 90, 90, 73, + 128, 90, 90, 85, 128, 90, 90, 89, 128, 75, 65, 198, 68, 65, 217, 66, 69, + 200, 68, 65, 196, 83, 65, 196, 70, 69, 200, 81, 65, 198, 84, 65, 200, 69, + 78, 196, 82, 82, 128, 65, 82, 195, 78, 79, 210, 77, 65, 201, 79, 67, 210, + 87, 65, 215, 67, 72, 197, 82, 72, 207, 84, 72, 197, 89, 73, 199, 65, 82, + 205, 66, 85, 212, 67, 79, 128, 67, 85, 205, 78, 69, 207, 71, 65, 198, 75, + 72, 207, 77, 71, 207, 90, 65, 200, 66, 73, 199, 68, 73, 197, 71, 72, 197, + 80, 72, 201, 86, 79, 128, 90, 72, 197, 80, 72, 207, 80, 85, 128, 67, 72, + 207, 77, 69, 206, 78, 69, 212, 80, 69, 200, 81, 73, 128, 84, 69, 206, 84, + 73, 208, 86, 69, 200, 89, 79, 196, 66, 69, 212, 67, 73, 128, 68, 89, 207, + 70, 79, 128, 72, 65, 193, 75, 65, 201, 78, 65, 199, 81, 79, 128, 81, 85, + 128, 82, 65, 196, 83, 73, 206, 83, 73, 216, 86, 65, 214, 86, 73, 128, 45, + 85, 205, 67, 72, 201, 67, 85, 128, 67, 89, 128, 68, 85, 204, 68, 90, 128, + 69, 88, 207, 71, 82, 213, 71, 85, 199, 72, 86, 128, 73, 74, 128, 74, 69, + 200, 74, 79, 212, 75, 69, 217, 75, 71, 128, 75, 75, 128, 76, 74, 128, 77, + 73, 199, 78, 74, 128, 78, 86, 128, 78, 89, 201, 79, 72, 205, 80, 65, 215, + 81, 69, 128, 81, 79, 207, 82, 68, 207, 83, 85, 206, 87, 79, 206, 89, 69, + 206, 65, 78, 207, 66, 65, 199, 66, 69, 206, 66, 79, 215, 66, 81, 128, 67, + 77, 128, 67, 85, 212, 68, 76, 128, 68, 86, 128, 69, 67, 200, 70, 77, 128, + 70, 89, 128, 71, 66, 128, 71, 86, 128, 71, 89, 128, 72, 75, 128, 72, 79, + 212, 72, 80, 128, 73, 83, 211, 73, 85, 128, 73, 89, 128, 75, 66, 128, 75, + 73, 208, 75, 76, 128, 75, 77, 128, 75, 84, 128, 75, 86, 128, 76, 67, 197, + 76, 67, 201, 76, 69, 203, 76, 72, 128, 76, 78, 128, 76, 88, 128, 77, 66, + 128, 77, 69, 205, 77, 71, 128, 77, 72, 128, 77, 73, 196, 77, 76, 128, 77, + 77, 128, 77, 83, 128, 77, 86, 128, 77, 87, 128, 78, 65, 193, 78, 69, 215, + 78, 70, 128, 78, 71, 207, 78, 72, 128, 78, 77, 128, 78, 85, 206, 78, 87, + 128, 78, 89, 196, 79, 86, 128, 80, 67, 128, 80, 69, 211, 80, 70, 128, 80, + 73, 201, 80, 79, 208, 80, 82, 128, 80, 86, 128, 80, 87, 128, 81, 79, 198, + 81, 89, 128, 82, 73, 206, 82, 74, 197, 82, 85, 194, 83, 78, 193, 83, 79, + 198, 83, 82, 128, 83, 87, 128, 84, 65, 214, 84, 69, 197, 84, 69, 212, 84, + 73, 210, 84, 82, 128, 86, 69, 197, 86, 69, 215, 87, 66, 128, 87, 86, 128, + 88, 89, 128, 89, 65, 210, 89, 78, 128, 89, 86, 128, 90, 76, 193, 66, 217, + 77, 213, 65, 197, 89, 213, 65, 213, 68, 218, 90, 197, 75, 205, 67, 205, + 75, 213, 77, 205, 76, 218, 77, 194, 77, 207, 77, 214, 77, 215, 80, 207, + 84, 195, 202, 209, +}; + +static unsigned short lexicon_offset[] = { + 0, 0, 6, 10, 15, 23, 30, 32, 35, 47, 52, 58, 71, 76, 82, 90, 99, 103, + 111, 114, 121, 127, 133, 140, 150, 158, 162, 167, 172, 179, 184, 190, + 198, 205, 212, 221, 229, 233, 238, 243, 251, 257, 264, 270, 274, 281, + 287, 294, 300, 309, 312, 315, 324, 330, 335, 338, 346, 349, 354, 359, + 364, 372, 378, 387, 397, 399, 404, 415, 419, 432, 298, 434, 440, 450, + 459, 464, 466, 469, 467, 476, 480, 485, 488, 493, 499, 501, 504, 509, + 517, 525, 529, 536, 543, 552, 560, 569, 573, 581, 590, 596, 605, 610, + 618, 625, 632, 638, 643, 651, 660, 667, 668, 676, 336, 683, 691, 637, + 695, 701, 706, 250, 712, 725, 729, 736, 739, 749, 754, 763, 772, 777, + 786, 791, 798, 801, 805, 813, 815, 819, 821, 826, 828, 837, 840, 845, 22, + 849, 853, 863, 534, 871, 876, 886, 893, 900, 906, 657, 913, 918, 928, + 884, 934, 939, 945, 950, 955, 957, 961, 968, 972, 977, 979, 74, 981, 986, + 991, 997, 1003, 1009, 1013, 1018, 1024, 1030, 1032, 352, 1035, 1041, + 1050, 1052, 1054, 1057, 674, 1062, 540, 1065, 1071, 539, 1073, 31, 1060, + 1085, 1087, 285, 1089, 1091, 1093, 402, 1095, 1104, 1109, 1112, 1114, + 1121, 1135, 1141, 1145, 1149, 1154, 1157, 1161, 1166, 1175, 1184, 100, + 1187, 1196, 1204, 1207, 1214, 1218, 1222, 1227, 1233, 1239, 1263, 1285, + 1307, 1329, 1350, 1371, 1391, 1411, 1430, 1449, 1468, 1487, 1506, 1525, + 1544, 1563, 1581, 1599, 1617, 1635, 1653, 1671, 1689, 1707, 1725, 1743, + 1761, 1778, 1795, 1812, 1829, 1846, 1863, 1880, 1897, 1914, 1931, 1948, + 1964, 1980, 1996, 2012, 2028, 2044, 2060, 2076, 2092, 2108, 2124, 2140, + 2156, 2172, 2188, 2204, 2220, 2236, 2252, 2268, 2284, 2300, 2316, 2332, + 2348, 2364, 2380, 2396, 2412, 2428, 2444, 2460, 2476, 2492, 2508, 2524, + 2540, 2556, 2572, 2588, 2604, 2620, 2636, 2652, 2668, 2684, 2700, 2716, + 2732, 2748, 2764, 2780, 2796, 2812, 2828, 2844, 2860, 2876, 2892, 2908, + 2924, 2940, 2956, 2972, 2988, 3004, 3020, 3036, 3052, 3068, 3084, 3100, + 3116, 3132, 3148, 3164, 3180, 3196, 3212, 3228, 3244, 3260, 3276, 3292, + 3308, 3324, 3340, 3356, 3372, 3388, 3404, 3420, 3436, 3452, 3468, 3484, + 3500, 3516, 3532, 3548, 3564, 3580, 3596, 3612, 3628, 3644, 3660, 3676, + 3692, 3708, 3724, 3740, 3756, 3772, 3788, 3804, 3820, 3836, 3852, 3868, + 3884, 3900, 3916, 3932, 3948, 3964, 3980, 3996, 4012, 4028, 4044, 4060, + 4076, 4092, 4108, 4124, 4140, 4156, 4172, 4188, 4204, 4220, 4236, 4252, + 4268, 4284, 4300, 4316, 4332, 4348, 4364, 4380, 4396, 4412, 4428, 4444, + 4460, 4476, 4492, 4508, 4524, 4540, 4556, 4572, 4588, 4604, 4620, 4636, + 4652, 4668, 4684, 4700, 4716, 4732, 4748, 4764, 4780, 4796, 4812, 4828, + 4844, 4860, 4876, 4892, 4908, 4924, 4940, 4956, 4972, 4988, 5004, 5020, + 5036, 5052, 5068, 5084, 5100, 5116, 5132, 5148, 5164, 5180, 5196, 5212, + 5228, 5244, 5260, 5276, 5292, 5308, 5324, 5340, 5356, 5372, 5388, 5404, + 5420, 5436, 5452, 5468, 5484, 5500, 5516, 5532, 5548, 5564, 5580, 5596, + 5612, 5628, 5644, 5660, 5676, 5692, 5708, 5724, 5740, 5756, 5772, 5788, + 5804, 5820, 5836, 5852, 5868, 5884, 5900, 5916, 5932, 5948, 5964, 5980, + 5996, 6012, 6028, 6044, 6060, 6076, 6092, 6108, 6124, 6140, 6156, 6172, + 6188, 6204, 6220, 6236, 6252, 6268, 6284, 6300, 6316, 6332, 6348, 6364, + 6380, 6396, 6412, 6428, 6444, 6460, 6476, 6492, 6508, 6524, 6540, 6556, + 6572, 6588, 6604, 6620, 6636, 6652, 6668, 6684, 6700, 6716, 6732, 6748, + 6764, 6780, 6796, 6812, 6828, 6844, 6860, 6876, 6892, 6908, 6924, 6940, + 6956, 6972, 6988, 7004, 7020, 7036, 7052, 7068, 7084, 7100, 7116, 7132, + 7148, 7164, 7180, 7196, 7212, 7228, 7244, 7260, 7276, 7292, 7308, 7324, + 7340, 7356, 7372, 7388, 7404, 7420, 7436, 7452, 7468, 7484, 7500, 7516, + 7532, 7548, 7564, 7580, 7596, 7612, 7628, 7644, 7660, 7676, 7692, 7708, + 7724, 7740, 7756, 7772, 7788, 7804, 7820, 7836, 7852, 7868, 7884, 7900, + 7916, 7932, 7948, 7964, 7980, 7996, 8012, 8028, 8044, 8060, 8076, 8092, + 8108, 8124, 8140, 8156, 8172, 8188, 8204, 8220, 8236, 8252, 8268, 8284, + 8300, 8316, 8332, 8348, 8364, 8380, 8396, 8412, 8428, 8444, 8460, 8476, + 8492, 8508, 8524, 8540, 8556, 8572, 8588, 8604, 8620, 8636, 8652, 8668, + 8684, 8700, 8716, 8732, 8748, 8764, 8780, 8796, 8812, 8828, 8844, 8860, + 8876, 8892, 8908, 8924, 8940, 8956, 8972, 8988, 9004, 9020, 9036, 9052, + 9068, 9084, 9100, 9116, 9132, 9148, 9164, 9180, 9196, 9212, 9228, 9244, + 9260, 9276, 9292, 9308, 9324, 9340, 9356, 9372, 9388, 9404, 9420, 9436, + 9452, 9468, 9484, 9500, 9516, 9532, 9548, 9564, 9580, 9596, 9612, 9628, + 9644, 9660, 9676, 9692, 9708, 9724, 9740, 9756, 9772, 9788, 9804, 9820, + 9836, 9852, 9868, 9884, 9900, 9916, 9932, 9948, 9964, 9980, 9996, 10012, + 10028, 10044, 10060, 10076, 10092, 10108, 10124, 10140, 10156, 10172, + 10188, 10204, 10220, 10236, 10252, 10268, 10284, 10300, 10316, 10332, + 10348, 10364, 10380, 10396, 10412, 10428, 10444, 10460, 10476, 10492, + 10508, 10524, 10540, 10556, 10572, 10588, 10604, 10620, 10636, 10652, + 10668, 10684, 10700, 10716, 10732, 10748, 10764, 10780, 10796, 10812, + 10828, 10844, 10860, 10876, 10892, 10908, 10923, 10938, 10953, 10968, + 10983, 10998, 11013, 11028, 11043, 11058, 11073, 11088, 11103, 11118, + 11133, 11148, 11163, 11178, 11193, 11208, 11223, 11238, 11253, 11268, + 11283, 11298, 11313, 11328, 11343, 11358, 11373, 11388, 11403, 11418, + 11433, 11448, 11463, 11478, 11493, 11508, 11523, 11538, 11553, 11568, + 11583, 11598, 11613, 11628, 11643, 11658, 11673, 11688, 11703, 11718, + 11733, 11748, 11763, 11778, 11793, 11808, 11823, 11838, 11853, 11868, + 11883, 11898, 11913, 11928, 11943, 11958, 11973, 11988, 12003, 12018, + 12033, 12048, 12063, 12078, 12093, 12108, 12123, 12138, 12153, 12168, + 12183, 12198, 12213, 12228, 12243, 12258, 12273, 12288, 12303, 12318, + 12333, 12348, 12363, 12378, 12393, 12408, 12423, 12438, 12453, 12468, + 12483, 12498, 12513, 12528, 12543, 12558, 12573, 12588, 12603, 12618, + 12633, 12648, 12663, 12678, 12693, 12708, 12723, 12738, 12753, 12768, + 12783, 12798, 12813, 12828, 12843, 12858, 12873, 12888, 12903, 12918, + 12933, 12948, 12963, 12978, 12993, 13008, 13023, 13038, 13053, 13068, + 13083, 13098, 13113, 13128, 13143, 13158, 13173, 13188, 13203, 13218, + 13233, 13248, 13263, 13278, 13293, 13308, 13323, 13338, 13353, 13368, + 13383, 13398, 13413, 13428, 13443, 13458, 13473, 13488, 13503, 13518, + 13533, 13548, 13563, 13578, 13593, 13608, 13623, 13638, 13653, 13668, + 13683, 13698, 13713, 13728, 13743, 13758, 13773, 13788, 13803, 13818, + 13833, 13848, 13863, 13878, 13893, 13908, 13923, 13938, 13953, 13968, + 13983, 13998, 14013, 14028, 14043, 14058, 14073, 14088, 14103, 14118, + 14133, 14148, 14163, 14178, 14193, 14208, 14223, 14238, 14253, 14268, + 14283, 14298, 14313, 14328, 14343, 14358, 14373, 14388, 14403, 14418, + 14433, 14448, 14463, 14478, 14493, 14508, 14523, 14538, 14553, 14568, + 14583, 14598, 14613, 14628, 14643, 14658, 14673, 14688, 14703, 14718, + 14733, 14748, 14763, 14778, 14793, 14808, 14823, 14838, 14853, 14868, + 14883, 14898, 14913, 14928, 14943, 14958, 14973, 14988, 15003, 15018, + 15033, 15048, 15063, 15078, 15093, 15108, 15123, 15138, 15153, 15168, + 15183, 15198, 15213, 15228, 15243, 15258, 15273, 15288, 15303, 15318, + 15333, 15348, 15363, 15378, 15393, 15408, 15423, 15438, 15453, 15468, + 15483, 15498, 15513, 15528, 15543, 15558, 15573, 15588, 15603, 15618, + 15633, 15648, 15663, 15678, 15693, 15708, 15723, 15738, 15753, 15768, + 15783, 15798, 15813, 15828, 15843, 15858, 15873, 15888, 15903, 15918, + 15933, 15948, 15963, 15978, 15993, 16008, 16023, 16038, 16053, 16068, + 16083, 16098, 16113, 16128, 16143, 16158, 16173, 16188, 16203, 16218, + 16233, 16248, 16263, 16278, 16293, 16308, 16323, 16338, 16353, 16368, + 16383, 16398, 16413, 16428, 16443, 16458, 16473, 16488, 16503, 16518, + 16533, 16548, 16563, 16578, 16593, 16608, 16623, 16638, 16653, 16668, + 16682, 16696, 16710, 16724, 1397, 16738, 16752, 16766, 16780, 16794, + 16808, 16822, 16836, 16850, 16864, 16878, 16892, 16906, 16920, 16934, + 16948, 16962, 16976, 16990, 17004, 17018, 17032, 1798, 17046, 17060, + 17074, 17088, 17102, 17116, 17130, 17144, 17158, 17172, 17186, 17200, + 17214, 17228, 17242, 17256, 17270, 17283, 17296, 17309, 17322, 17335, + 17348, 17361, 17374, 17387, 17400, 17413, 17426, 17439, 17452, 17465, + 17478, 17491, 17504, 17517, 1748, 17530, 17543, 17556, 17569, 17582, + 17595, 17608, 17621, 17634, 17647, 17660, 17673, 17686, 17699, 17712, + 17725, 17738, 17751, 17764, 17777, 17790, 17803, 17816, 17829, 17842, + 17855, 17868, 1512, 17881, 17894, 17907, 17920, 17933, 17946, 17959, + 17972, 17985, 17998, 18011, 18024, 18037, 18050, 18063, 18076, 18088, + 18100, 18112, 18124, 18136, 18148, 18160, 18172, 18184, 18196, 18208, + 18220, 18232, 18244, 18256, 1659, 18268, 18280, 18292, 1605, 18304, + 18316, 18328, 18340, 18352, 18364, 1494, 1587, 18376, 1623, 18388, 18400, + 18412, 18424, 18436, 18448, 18460, 18472, 18484, 18496, 18508, 18520, + 18532, 18544, 18556, 18568, 18580, 18592, 18604, 18616, 18628, 18640, + 18652, 18664, 18676, 18688, 18700, 18712, 18724, 18736, 18748, 18760, + 18772, 18784, 18796, 18808, 18820, 18832, 18844, 18856, 18868, 18880, + 18892, 18904, 18916, 18928, 18940, 18952, 18964, 18976, 18988, 19000, + 19012, 19024, 19036, 19048, 19060, 19072, 19084, 19096, 19108, 19120, + 19132, 19144, 19156, 19168, 19180, 19192, 19204, 19216, 19228, 19240, + 19252, 19264, 19276, 19288, 1379, 19300, 19312, 1713, 19324, 19336, + 19348, 19360, 19372, 19384, 19396, 19408, 19420, 19432, 19444, 19456, + 19468, 19480, 19492, 19504, 19516, 19528, 19540, 19552, 19564, 19576, + 19588, 19600, 19612, 19624, 19636, 19647, 19658, 19669, 19680, 19691, + 19702, 19713, 19724, 19735, 19746, 19757, 19768, 19779, 19790, 19801, + 19812, 19823, 1784, 19834, 19845, 19856, 19867, 19878, 19889, 19900, + 1869, 1296, 19911, 1419, 19922, 19933, 19944, 19955, 19966, 19977, 1886, + 19988, 19999, 20010, 20021, 20032, 20043, 20054, 20065, 20076, 20087, + 20098, 20109, 1852, 20120, 20131, 20142, 20153, 20164, 20175, 20186, + 20197, 20208, 20219, 20230, 20241, 20252, 20263, 20274, 20285, 20296, + 20307, 20318, 20329, 20340, 20351, 20362, 20373, 20384, 20395, 20406, + 20417, 20428, 20439, 20450, 20461, 20472, 20483, 20494, 20505, 20516, + 20527, 20538, 20549, 20560, 20571, 20582, 20593, 20604, 20615, 20626, + 20637, 20648, 20659, 20670, 20681, 20692, 20703, 20714, 20725, 20736, + 20747, 20758, 20769, 20780, 20791, 20802, 20813, 20824, 20835, 20846, + 20857, 20868, 20879, 20890, 20901, 20912, 20923, 20934, 20945, 20956, + 20967, 20978, 20989, 21000, 21011, 21022, 21033, 21044, 21055, 21066, + 21077, 21088, 21099, 21110, 21121, 21132, 21143, 16937, 21154, 21165, + 21176, 21187, 21198, 21209, 21220, 21231, 21242, 21253, 21264, 21275, + 21286, 21297, 21308, 21319, 21330, 21341, 21352, 21363, 21374, 21385, + 21396, 21407, 21418, 21429, 21440, 21451, 21462, 21473, 21484, 21495, + 21506, 21517, 21528, 21539, 21550, 21561, 21572, 21583, 21594, 21605, + 21615, 21625, 21635, 21645, 21655, 21665, 21675, 21685, 21695, 21705, + 21715, 21725, 21735, 21745, 21755, 21765, 21775, 21785, 21795, 18090, + 21805, 21815, 21825, 21835, 21845, 21855, 21865, 1361, 21875, 21885, + 21895, 21905, 21915, 21925, 21935, 21945, 21955, 21965, 21975, 21985, + 21995, 22005, 22015, 22025, 22035, 22045, 22055, 22065, 22075, 22085, + 22095, 22105, 22115, 22125, 22135, 22145, 22155, 22165, 22175, 22185, + 22195, 22205, 22215, 22225, 22235, 22245, 22255, 22265, 22275, 22285, + 22295, 17676, 22305, 18630, 22315, 22325, 22335, 22345, 22355, 22365, + 22375, 22385, 22395, 22405, 22415, 22425, 22435, 22445, 22455, 22465, + 22475, 22485, 22495, 22505, 22515, 22525, 22535, 22545, 22555, 22565, + 22575, 22585, 22595, 22605, 22615, 22625, 22635, 22645, 22655, 22665, + 22675, 22685, 22695, 22705, 22715, 22725, 22735, 22745, 22755, 22765, + 22775, 22785, 22795, 22805, 22815, 22825, 22835, 22845, 22855, 22865, + 22875, 22885, 22895, 22905, 22915, 22925, 22935, 22945, 22955, 22965, + 22975, 22985, 22995, 23005, 23015, 23025, 23035, 23045, 23055, 23065, + 23075, 23085, 23095, 23105, 23115, 23125, 23135, 23145, 23155, 23165, + 23175, 23185, 23195, 23205, 23215, 23225, 23235, 23245, 23255, 23265, + 23275, 23285, 23295, 23305, 23315, 23325, 23335, 23345, 23355, 23365, + 23375, 23385, 23395, 23405, 23415, 23425, 23435, 23445, 23455, 23465, + 23475, 23485, 23495, 1938, 23505, 23515, 23525, 23535, 23545, 23555, + 23565, 23575, 23585, 23595, 23605, 23615, 23625, 23635, 23645, 23655, + 23665, 23675, 23685, 23695, 23705, 23715, 23725, 23735, 23745, 23755, + 23765, 23774, 23783, 23792, 23801, 23810, 23819, 23828, 23837, 23846, + 23855, 23864, 17287, 23873, 23882, 23891, 23900, 23909, 23918, 23927, + 23936, 23945, 23954, 23963, 23972, 23981, 23990, 23999, 24008, 24017, + 24026, 24035, 24044, 24053, 24062, 24071, 24080, 24089, 24098, 24107, + 24116, 24125, 24134, 24143, 24152, 24161, 24170, 24179, 24188, 24197, + 19792, 24206, 24215, 24224, 24233, 24242, 24251, 24260, 24269, 24278, + 24287, 24296, 24305, 24314, 24323, 24332, 24341, 24350, 24359, 24368, + 24377, 24386, 24395, 24404, 24413, 24422, 19979, 24431, 24440, 24449, + 24458, 24467, 24476, 24485, 24494, 24503, 24512, 24521, 24530, 1276, + 24539, 24548, 24557, 24566, 24575, 24584, 24593, 24602, 24611, 24620, + 24629, 24638, 24647, 24656, 24665, 24674, 24683, 24692, 24701, 24710, + 24719, 24728, 24737, 24746, 24755, 24764, 24773, 24782, 24791, 24800, + 24809, 24818, 24827, 24836, 24845, 24854, 24863, 24872, 24881, 24890, + 24899, 24908, 24917, 24926, 24935, 24944, 24953, 24962, 24971, 24980, + 24989, 24998, 25007, 25016, 25025, 25034, 25043, 25052, 25061, 25070, + 25079, 25088, 25097, 25106, 25115, 25124, 25133, 25142, 25151, 25160, + 25169, 25178, 25187, 25196, 25205, 25214, 25223, 25232, 25241, 25250, + 25259, 25268, 25277, 25286, 25295, 25304, 25313, 25322, 25331, 25340, + 25349, 25358, 25367, 25376, 25385, 25394, 25403, 25412, 25421, 25430, + 25439, 25448, 25457, 25466, 25475, 25484, 25493, 25502, 25511, 25520, + 25529, 25538, 25547, 25556, 25565, 25574, 25583, 25592, 25601, 25610, + 25619, 25628, 25637, 25646, 25655, 25664, 25673, 25682, 25691, 25700, + 10755, 25709, 25718, 25727, 25736, 25745, 25754, 25763, 25772, 25781, + 25790, 25799, 25808, 25817, 25826, 25835, 25844, 25853, 25862, 25871, + 25880, 25889, 25898, 25907, 25916, 25925, 25934, 25943, 25952, 25961, + 25970, 25979, 25988, 25997, 26006, 26015, 26024, 26033, 26042, 26051, + 26060, 26069, 26078, 26087, 26096, 26105, 1837, 26114, 26123, 26132, + 26141, 26150, 26159, 26168, 26177, 26186, 26195, 26204, 26213, 26222, + 26231, 26240, 26249, 26258, 26267, 26276, 26285, 26294, 26303, 26312, + 26321, 26330, 26339, 26348, 26357, 26366, 26375, 26383, 26391, 26399, + 16674, 26407, 26415, 26423, 26431, 26439, 26447, 26455, 26463, 26471, + 21617, 26479, 26487, 26495, 26503, 26511, 26519, 26527, 26535, 22007, + 26543, 26551, 26559, 26567, 26575, 26583, 26591, 26599, 17795, 26607, + 26615, 26623, 26631, 26639, 16702, 26647, 1460, 26655, 26663, 2036, + 17899, 26671, 1956, 26679, 26687, 26695, 26703, 16758, 26711, 26719, + 26727, 18368, 26735, 26743, 26751, 17314, 26759, 26767, 26775, 26783, + 24144, 26791, 26799, 26807, 26815, 26823, 26831, 26839, 26847, 26855, + 26863, 26871, 1804, 26879, 26887, 26895, 26903, 26911, 26919, 26927, + 26935, 26943, 26951, 26959, 26967, 26975, 26983, 26991, 26999, 27007, + 27015, 27023, 27031, 27039, 27047, 27055, 27063, 27071, 27079, 27087, + 27095, 27103, 27111, 27119, 27127, 27135, 27143, 27151, 27159, 27167, + 21847, 27175, 27183, 27191, 27199, 27207, 27215, 27223, 27231, 27239, + 27247, 27255, 24531, 27263, 27271, 27279, 27287, 27295, 27303, 27311, + 27319, 27327, 27335, 27343, 27351, 27359, 27367, 27375, 27383, 27391, + 27399, 27407, 27415, 27423, 27431, 27439, 27447, 27455, 27463, 27471, + 27479, 27487, 27495, 27503, 27511, 27519, 27527, 27535, 27543, 27551, + 27559, 27567, 27575, 21157, 27583, 27591, 27599, 27607, 27615, 27623, + 27631, 27639, 27647, 27655, 27663, 27671, 27679, 27687, 27695, 27703, + 27711, 27719, 27727, 27735, 27743, 27751, 27759, 27767, 27775, 27783, + 27791, 27799, 27807, 27815, 27823, 27831, 27839, 27847, 27855, 27863, + 27871, 24567, 27879, 27887, 27895, 27903, 27911, 27919, 27927, 27935, + 27943, 27951, 27959, 27967, 21256, 27975, 27983, 1923, 27991, 27999, + 28007, 28015, 28023, 28031, 28039, 28047, 28055, 28063, 28071, 28079, + 28087, 28095, 28103, 28111, 28119, 28127, 28135, 28143, 28151, 28159, + 2004, 28167, 28175, 10852, 28183, 28191, 28199, 28207, 28215, 28223, + 19196, 28231, 28239, 28247, 28255, 28263, 28271, 28279, 28287, 28295, + 28303, 28311, 28319, 28327, 28335, 28343, 28351, 28359, 28367, 28375, + 28383, 28391, 28399, 28407, 28415, 28423, 28431, 28439, 19352, 28447, + 28455, 28463, 28471, 28479, 28487, 28495, 28503, 28511, 28519, 1699, + 28527, 28535, 28543, 28551, 28559, 28567, 28575, 28583, 28591, 28599, + 28607, 28615, 28623, 28631, 28639, 28647, 28655, 28663, 28671, 28679, + 28687, 28695, 28703, 28711, 28719, 28727, 28735, 28743, 28751, 28759, + 28767, 28775, 28783, 28791, 28799, 28807, 28815, 28823, 23187, 28831, + 28839, 28847, 28855, 28863, 28871, 28879, 28887, 28894, 28901, 18285, + 28908, 28915, 28922, 28929, 28936, 28943, 28950, 28957, 28964, 28971, + 28978, 28985, 28992, 28999, 29006, 11036, 29013, 29020, 29027, 29034, + 29041, 29048, 29055, 29062, 29069, 29076, 29083, 29090, 29097, 29104, + 29111, 29118, 29125, 29132, 29139, 29146, 29153, 29160, 29167, 1499, + 29174, 1592, 29181, 29188, 29195, 29202, 29209, 29216, 29223, 29230, + 29237, 29244, 29251, 29258, 29265, 29272, 1343, 29279, 29286, 29293, + 29300, 29307, 29314, 29321, 29328, 29335, 29342, 29349, 29356, 29363, + 29370, 29377, 29384, 29391, 29398, 21246, 29405, 29412, 29419, 29426, + 29433, 29440, 29447, 29454, 29461, 29468, 29475, 29482, 29489, 29496, + 29503, 29510, 29517, 29524, 29531, 29538, 29545, 29552, 29559, 29566, + 29573, 23956, 29580, 29587, 29594, 29601, 29608, 29615, 29622, 29629, + 29636, 29643, 29650, 29657, 29664, 29671, 29678, 29685, 29692, 29699, + 29706, 29713, 29720, 29727, 29734, 29741, 29748, 29755, 29762, 29769, + 29776, 29783, 29790, 29797, 29804, 29811, 29818, 29825, 29832, 29839, + 29846, 29853, 29860, 29867, 29874, 29881, 29888, 29895, 29902, 29909, + 29916, 29923, 29930, 29937, 29944, 29951, 29958, 29965, 29972, 24145, + 29979, 29986, 29993, 30000, 30007, 30014, 30021, 30028, 1736, 30035, + 30042, 30049, 30056, 30063, 30070, 30077, 30084, 30091, 30098, 30105, + 30112, 30119, 30126, 30133, 30140, 30147, 30154, 30161, 30168, 30175, + 30182, 30189, 30196, 30203, 30210, 30217, 30224, 30231, 30238, 30245, + 30252, 30259, 30266, 30273, 30280, 30287, 30294, 30301, 30308, 24568, + 30315, 30322, 30329, 30336, 30343, 30350, 30357, 30364, 30371, 30378, + 30385, 30392, 30399, 30406, 30413, 30420, 30427, 30434, 30441, 30448, + 30455, 21191, 30462, 30469, 30476, 30483, 30490, 30497, 30504, 30511, + 30518, 27312, 30525, 30532, 30539, 30546, 30553, 30560, 30567, 30574, + 30581, 30588, 30595, 30602, 30609, 30616, 30623, 30630, 30637, 30644, + 30651, 30658, 30665, 30672, 30679, 30686, 30693, 30700, 30707, 30714, + 30721, 30728, 30735, 30742, 30749, 30756, 30763, 30770, 30777, 23348, + 30784, 30791, 30798, 30805, 30812, 28376, 30819, 30826, 30833, 30840, + 30847, 30854, 30861, 30868, 30875, 30882, 30889, 30896, 26464, 30903, + 30910, 30917, 30924, 30931, 30938, 30945, 30952, 30959, 30966, 30973, + 30980, 30987, 30994, 31001, 18141, 31008, 31015, 31022, 31029, 31036, + 31043, 31050, 31057, 31064, 31071, 24460, 31078, 31085, 31092, 31099, + 19065, 31106, 31113, 31120, 31127, 31134, 31141, 31148, 31155, 31162, + 31169, 31176, 31183, 31190, 31197, 31204, 31211, 31218, 31225, 31232, + 31239, 31246, 24586, 31253, 31260, 23188, 31267, 31274, 31281, 31288, + 31295, 31302, 31309, 31316, 31323, 31330, 31337, 31344, 31351, 31357, + 31363, 31369, 31375, 31381, 31387, 31393, 31399, 31405, 22259, 31411, + 31417, 31423, 31429, 31435, 27049, 31441, 31447, 31345, 31453, 31459, + 21739, 31465, 31471, 31477, 31483, 31489, 31495, 31501, 31507, 31513, + 21819, 31519, 31525, 31531, 31537, 31543, 31549, 31555, 31561, 31567, + 31573, 31579, 31585, 31591, 31597, 31603, 31609, 31615, 31621, 31627, + 17576, 22329, 31633, 31639, 31645, 19905, 1169, 31651, 17381, 31657, + 31663, 1301, 31669, 31675, 1538, 18562, 31681, 31687, 16732, 31693, + 18454, 31699, 1405, 17082, 31705, 31711, 31717, 18166, 31723, 31729, + 31735, 31741, 31747, 31753, 26793, 31759, 31765, 31771, 31777, 21699, + 31783, 31789, 31795, 31801, 31807, 31813, 31819, 31825, 10962, 1044, + 31831, 31837, 31843, 31849, 17563, 31855, 31861, 31867, 31873, 31879, + 31885, 31891, 31897, 31903, 31909, 31915, 31921, 31927, 31933, 31939, + 31945, 31951, 31957, 31963, 31969, 31975, 31981, 31987, 31993, 31999, + 32005, 32011, 32017, 32023, 32029, 32035, 21489, 32041, 32047, 32053, + 32059, 32065, 32071, 32077, 32083, 32089, 32095, 24596, 32101, 32107, + 32113, 32119, 32125, 18466, 32131, 32137, 32143, 32149, 32155, 32161, + 32167, 32173, 32179, 32185, 32191, 32197, 32203, 32209, 32215, 32221, + 32227, 21909, 32233, 32239, 32245, 32251, 32257, 32263, 32269, 32275, + 32281, 32287, 32293, 32299, 32305, 32311, 32317, 32323, 32329, 32335, + 32341, 32347, 32353, 32359, 32365, 32371, 32377, 32383, 32389, 32395, + 32401, 32407, 26216, 32413, 32419, 32425, 32431, 32437, 32443, 32449, + 32455, 32461, 32467, 32473, 32479, 32485, 32491, 32497, 32503, 32509, + 32515, 32521, 32527, 32533, 32539, 32545, 32551, 21159, 32557, 32563, + 32569, 32575, 32581, 32587, 32593, 32599, 32605, 32611, 32617, 32623, + 32629, 32635, 32641, 32647, 32653, 32659, 21899, 32665, 32671, 32677, + 32683, 32689, 32695, 32701, 32707, 32713, 32719, 32725, 32731, 32737, + 32743, 32749, 32755, 32761, 32767, 32773, 32779, 32785, 32791, 32797, + 32803, 32809, 32815, 32821, 30176, 32827, 32833, 32839, 32845, 32851, + 32857, 32863, 32869, 32875, 32881, 32887, 27441, 21789, 32893, 32899, + 32905, 32911, 32917, 32923, 32929, 28817, 32935, 32941, 32947, 32953, + 32959, 32965, 32971, 32977, 32983, 32989, 32995, 33001, 33007, 33013, + 33019, 33025, 33031, 33037, 33043, 33049, 33055, 33061, 33067, 33073, + 33079, 33085, 33091, 33097, 33103, 33109, 33115, 33121, 33127, 33133, + 33139, 33145, 33151, 33157, 33163, 33169, 33175, 33181, 33187, 33193, + 33199, 33205, 33211, 33217, 33223, 33229, 33235, 33241, 33247, 33253, + 33259, 33265, 33271, 33277, 33283, 33289, 33295, 33301, 33307, 33313, + 33319, 33325, 33331, 33337, 33343, 33349, 33355, 33361, 33367, 33373, + 33379, 33385, 33391, 33397, 33403, 33409, 33415, 33421, 33427, 33433, + 33439, 33445, 33451, 33457, 33463, 33469, 33475, 33481, 33487, 33493, + 33499, 33505, 33511, 33517, 33523, 33529, 33535, 33541, 33547, 33553, + 33559, 33565, 33571, 33577, 33583, 33589, 33595, 33601, 33607, 33613, + 33619, 33625, 33631, 33637, 33643, 33649, 33655, 33661, 33667, 33673, + 33679, 33685, 33691, 33697, 33703, 33709, 33715, 33721, 33727, 33733, + 33739, 33745, 33751, 33757, 33763, 33769, 33775, 33781, 33787, 33793, + 33799, 33805, 33811, 29679, 33817, 33823, 33829, 33835, 33841, 33847, + 33853, 33859, 33865, 33871, 33877, 33883, 33889, 33895, 33901, 33907, + 33913, 33919, 33925, 33931, 33937, 33943, 33949, 33955, 33961, 33967, + 33973, 33979, 33985, 33991, 33997, 34003, 34009, 34015, 34021, 34027, + 34033, 34039, 34045, 34051, 34057, 34063, 34069, 34075, 34081, 34087, + 34093, 34099, 34105, 34111, 34117, 34123, 34129, 34135, 34141, 34147, + 34153, 34159, 34165, 34171, 34177, 34183, 34189, 34195, 34201, 34207, + 34213, 34219, 34225, 34231, 34237, 34243, 34249, 34255, 34261, 34267, + 34273, 34279, 34285, 34291, 34297, 34303, 25847, 34309, 34315, 34321, + 34327, 34333, 34339, 34345, 34351, 34357, 34363, 34369, 34375, 34381, + 34387, 34393, 34399, 34405, 34411, 34417, 34423, 10932, 34429, 34435, + 34441, 34447, 34453, 34459, 34465, 34471, 34477, 34483, 34489, 34495, + 18502, 34501, 34507, 31240, 34513, 34519, 24605, 34525, 34531, 34537, + 34543, 34549, 34555, 34561, 34567, 34573, 34579, 34585, 34591, 34597, + 34603, 34609, 34615, 34621, 34627, 34633, 34639, 34645, 34651, 34657, + 34663, 34669, 34675, 34681, 34687, 34693, 34699, 23279, 34705, 34711, + 34717, 34723, 34729, 34735, 34741, 34747, 34753, 34759, 34765, 34771, + 34777, 34783, 34789, 34795, 34801, 34807, 34813, 34819, 34825, 34831, + 34837, 34843, 34849, 34854, 34859, 34864, 34869, 23967, 34874, 32672, + 34879, 34884, 34889, 17629, 34894, 34899, 34904, 34909, 34914, 34919, + 34924, 34929, 34934, 34939, 34944, 34949, 34954, 34959, 23480, 34964, + 34969, 32852, 34974, 34979, 34984, 34989, 34994, 34999, 35004, 35009, + 35014, 31994, 35019, 35024, 35029, 26091, 35034, 35039, 35044, 35049, + 35054, 35059, 35064, 35069, 35074, 35079, 35084, 35089, 35094, 35099, + 35104, 1366, 35109, 35114, 35119, 35124, 35129, 35134, 35139, 35144, + 35149, 35154, 35159, 35164, 35169, 35174, 35179, 35184, 35189, 35194, + 35199, 35204, 35209, 35214, 35219, 35224, 35229, 35234, 35239, 35244, + 35249, 35254, 35259, 35264, 31868, 35269, 35274, 35279, 35284, 382, + 35289, 35294, 35299, 35304, 35309, 35314, 35319, 35324, 1019, 35329, + 35334, 35339, 21670, 35344, 35349, 35354, 31424, 35359, 35364, 35369, + 35374, 35379, 35384, 35389, 35394, 35399, 35404, 35409, 35414, 35419, + 35424, 35429, 35434, 35439, 17278, 35444, 35449, 35454, 35459, 35464, + 35469, 35474, 35479, 35484, 34778, 35489, 30492, 35494, 35499, 35504, + 35509, 35514, 35519, 35524, 35529, 34508, 35534, 17642, 35539, 35544, + 35549, 35554, 32090, 35559, 35564, 35569, 35574, 35579, 35584, 21127, + 35589, 35594, 35599, 35604, 35609, 23460, 35614, 35619, 35624, 35629, + 35634, 35639, 35644, 35649, 35654, 35659, 35664, 35669, 35674, 35679, + 35684, 35689, 35694, 35699, 35704, 35709, 35714, 35719, 35724, 35729, + 35734, 35739, 35744, 35749, 35754, 35759, 35764, 35769, 35774, 35779, + 35784, 35789, 21259, 35794, 35799, 35804, 35809, 35814, 27002, 35819, + 35824, 35829, 35834, 35839, 35844, 35849, 35854, 35859, 35864, 35869, + 35874, 35879, 35884, 35889, 35894, 35899, 35904, 35909, 35914, 34226, + 35919, 35924, 35929, 35934, 35939, 35944, 35949, 35954, 35959, 35964, + 35969, 35974, 35979, 35984, 33152, 35989, 35994, 29638, 35999, 36004, + 36009, 16929, 36014, 36019, 36024, 36029, 28370, 36034, 36039, 36044, + 36049, 36054, 36059, 36064, 29554, 36069, 36074, 36079, 36084, 36089, + 36094, 36099, 36104, 36109, 36114, 36119, 36124, 36129, 36134, 36139, + 36144, 36149, 36154, 36159, 36164, 36169, 36174, 34586, 36179, 36184, + 36189, 29862, 36194, 36199, 36204, 36209, 36214, 36219, 36224, 36229, + 36234, 36239, 24723, 36244, 36249, 36254, 36259, 36264, 36269, 36274, + 36279, 36284, 36289, 23160, 36294, 36299, 36304, 36309, 30450, 36314, + 36319, 36324, 36329, 36334, 36339, 26262, 36344, 36349, 36354, 36359, + 36364, 36369, 36374, 36379, 36384, 36389, 36394, 36399, 36404, 36409, + 36414, 36419, 36424, 36429, 36434, 36439, 36444, 36449, 36454, 36459, + 36464, 36469, 36474, 36479, 36484, 36489, 36494, 36499, 32570, 32576, + 32582, 36504, 36509, 36514, 36519, 36524, 36529, 36534, 32588, 32594, + 32600, 33764, 36539, 36544, 36549, 36554, 36559, 36564, 36569, 36574, + 36579, 36584, 36589, 36594, 36599, 36604, 36609, 36614, 36619, 36624, + 36629, 36634, 36639, 31850, 36644, 36649, 36654, 36659, 36664, 36669, + 36674, 36679, 36684, 36689, 36694, 36699, 36704, 36709, 36714, 36719, + 36724, 36729, 36734, 36739, 36744, 36749, 36754, 36759, 36764, 36769, + 36774, 36779, 36784, 36789, 36794, 36799, 36804, 36809, 36814, 36819, + 36824, 36829, 36834, 36839, 36844, 36849, 36854, 36859, 36864, 32858, + 32864, 36869, 36874, 36879, 36884, 36889, 36894, 36899, 36904, 32876, + 32882, 36909, 36914, 36919, 36924, 36929, 34748, 34754, 36934, 36939, + 36944, 36949, 36954, 36959, 36964, 36969, 36974, 36979, 36984, 36989, + 36994, 36999, 37004, 37009, 37014, 37019, 37024, 37029, 37034, 37039, + 37044, 37049, 37054, 37059, 37064, 37069, 37074, 37079, 33092, 37084, + 33098, 37089, 37094, 16831, 26698, 37099, 33104, 33110, 33116, 33122, + 37104, 37109, 37114, 37119, 32714, 37124, 37129, 37134, 37139, 37144, + 37149, 37154, 37159, 37164, 37169, 37174, 37179, 37184, 37189, 37194, + 37199, 37204, 37209, 37214, 37219, 37224, 37229, 37234, 37239, 37244, + 37249, 37254, 37259, 37264, 37269, 37274, 37279, 37284, 37289, 37294, + 37299, 37304, 37309, 37314, 37319, 37324, 37329, 37334, 37339, 37344, + 37349, 37354, 37359, 37364, 37369, 37374, 37379, 37384, 37389, 37394, + 37399, 37404, 37409, 37414, 37419, 32750, 32756, 32762, 34154, 37424, + 37429, 37434, 37439, 37444, 37449, 37454, 37459, 37464, 37469, 37474, + 37479, 37484, 37489, 37494, 37499, 37504, 37509, 37514, 37519, 37524, + 37529, 33380, 33386, 33392, 37534, 37539, 37544, 37549, 37554, 37559, + 37564, 37569, 37574, 37579, 37584, 37589, 37594, 37599, 37604, 37609, + 33398, 37614, 33404, 33410, 33848, 37619, 37624, 37629, 37634, 37639, + 37644, 37649, 37654, 37659, 37664, 37669, 37674, 37679, 37684, 37689, + 37694, 37699, 37704, 37709, 37714, 37719, 37724, 37729, 37734, 37739, + 1234, 21920, 37744, 37749, 37754, 37759, 37764, 37769, 37774, 37779, + 34562, 37784, 37789, 33206, 37794, 33212, 37799, 37804, 37809, 33218, + 37814, 33224, 33230, 33236, 37819, 30366, 37824, 37829, 37834, 37839, + 37844, 37849, 37854, 37859, 37864, 37869, 37874, 37879, 37884, 37889, + 37894, 37899, 37904, 33242, 33248, 37909, 37914, 37919, 37924, 28194, + 37929, 37934, 33254, 37939, 33260, 33266, 37944, 37949, 37954, 30744, + 37959, 37964, 37969, 37974, 37979, 37984, 37989, 37994, 37999, 38004, + 38009, 38014, 38019, 38024, 38029, 38034, 38039, 38044, 38049, 38054, + 38059, 31682, 38064, 38069, 38074, 38079, 38084, 38089, 38094, 38099, + 38104, 38109, 38114, 33854, 38119, 38124, 38129, 38134, 38139, 38144, + 38149, 33860, 33866, 38154, 38159, 38164, 38169, 33278, 33290, 31622, + 38174, 38179, 38184, 38189, 38194, 38199, 38204, 38209, 38214, 38219, + 38224, 38229, 38234, 38239, 38244, 38249, 38254, 38259, 30961, 38264, + 38269, 38274, 38279, 38284, 38289, 38294, 38299, 38304, 38309, 38314, + 38319, 38324, 38329, 38334, 38339, 38344, 38349, 38354, 33296, 38359, + 38364, 38369, 38374, 38379, 38384, 38389, 38394, 38399, 38404, 38409, + 38414, 38419, 38424, 38429, 38434, 38439, 38444, 38449, 38454, 38459, + 38464, 38469, 38474, 38479, 38484, 38489, 38494, 38499, 38504, 38509, + 38514, 38519, 38524, 38529, 38534, 38539, 38544, 38549, 38554, 38559, + 38564, 38569, 38574, 38579, 38584, 38589, 38594, 38599, 38604, 38609, + 38614, 38619, 38624, 38629, 38634, 38639, 38644, 38649, 38654, 38659, + 38664, 38669, 38674, 38679, 38684, 38689, 38694, 38699, 38704, 38709, + 38714, 38719, 38724, 38729, 38734, 38739, 38744, 38749, 38754, 34112, + 38759, 38764, 38769, 38774, 38779, 38784, 38789, 38794, 38799, 38804, + 38809, 38814, 38819, 38824, 38829, 38834, 38839, 38844, 38849, 38854, + 34238, 33890, 38859, 33896, 38864, 38869, 38874, 38879, 38884, 38889, + 38894, 38899, 38904, 38909, 38914, 38919, 38924, 38929, 38934, 38939, + 38944, 38949, 38954, 38959, 38964, 38969, 38974, 38979, 34448, 34454, + 38984, 38989, 38994, 38999, 39004, 24687, 832, 39009, 39014, 39019, + 39024, 39029, 39034, 39039, 39044, 39049, 39054, 39059, 39064, 39069, + 39074, 39079, 39084, 39089, 39094, 39099, 39104, 39109, 39114, 39119, + 39124, 39129, 39134, 39139, 39144, 39149, 34460, 39154, 39159, 39164, + 39169, 39174, 39179, 39184, 39189, 39194, 33986, 39199, 39204, 39209, + 39214, 39219, 39224, 39229, 39234, 21700, 39239, 39244, 39249, 39254, + 39259, 39264, 39269, 30688, 39274, 39279, 39284, 39289, 39294, 39299, + 39304, 39309, 39314, 39319, 39324, 39329, 39334, 39339, 39344, 39349, + 39354, 39359, 39364, 39369, 39374, 32486, 39379, 31171, 23500, 39384, + 39389, 39394, 39399, 39404, 39409, 39414, 39419, 26882, 39424, 39429, + 39434, 39439, 39444, 33320, 33326, 33332, 39449, 33350, 33500, 33506, + 39454, 39459, 39464, 39469, 39474, 39479, 33908, 33914, 33920, 39484, + 39489, 39494, 39499, 39504, 39509, 39514, 39519, 39524, 33926, 39529, + 33932, 39534, 39539, 39544, 39549, 39554, 39559, 39564, 39569, 39574, + 39579, 39584, 39589, 39594, 39599, 39604, 39609, 39614, 39619, 39624, + 39629, 39634, 39639, 39644, 33938, 33944, 39649, 33950, 33956, 33962, + 39654, 39659, 39664, 39669, 39674, 39679, 39684, 39689, 39694, 39699, + 39704, 39709, 39714, 39719, 39724, 39729, 39734, 39739, 39744, 39748, + 39752, 39756, 39760, 39764, 21841, 17890, 39768, 39772, 36180, 39776, + 21117, 23101, 32331, 39780, 33561, 39784, 39788, 39792, 25822, 39796, + 32967, 39800, 39804, 18300, 39808, 26047, 39812, 39816, 31032, 39820, + 31455, 39824, 39828, 39832, 39836, 31791, 39840, 39844, 23191, 16958, + 39848, 32853, 39852, 31551, 39856, 39860, 39864, 21751, 36425, 39868, + 17487, 39872, 39876, 35570, 39880, 39884, 21238, 27099, 39888, 39892, + 39896, 30745, 39900, 39904, 39310, 39325, 39908, 32787, 28946, 35270, + 37560, 36185, 39912, 39916, 31081, 35575, 19188, 39920, 36500, 39924, + 39928, 39932, 39936, 247, 39940, 39944, 39948, 39952, 39956, 39960, + 26326, 39964, 36910, 39968, 39972, 39976, 39980, 36775, 39984, 39988, + 39992, 39996, 40000, 40004, 40008, 37535, 40012, 40016, 40020, 40024, + 40028, 38260, 40032, 40036, 40040, 40044, 40048, 21971, 34587, 40052, + 27203, 40056, 40060, 40064, 32355, 40068, 38370, 36915, 40072, 40076, + 40080, 40084, 40088, 38890, 29814, 40092, 40096, 33855, 40100, 30437, + 26353, 40104, 39005, 40108, 40112, 36155, 40116, 27971, 40120, 40124, + 40128, 40132, 37400, 40136, 40140, 40144, 40148, 39015, 40152, 38270, + 38765, 40156, 40160, 40164, 39290, 39300, 40168, 1081, 40172, 1058, + 39330, 39335, 39340, 39130, 27139, 40176, 40180, 40184, 40188, 32367, + 40192, 38365, 29877, 29884, 40196, 40200, 32385, 40204, 40208, 40212, + 31249, 40216, 40220, 40224, 36440, 40228, 30542, 1229, 29450, 40232, + 40236, 40240, 40244, 40248, 40252, 40256, 40260, 40264, 40268, 40272, + 40276, 40280, 25615, 40284, 40288, 40292, 40296, 40300, 40304, 34389, + 30920, 24715, 37775, 40308, 40312, 40316, 40320, 40324, 40328, 40332, + 37010, 37015, 40336, 40340, 40344, 40348, 40352, 40356, 40360, 37045, + 40364, 29653, 40368, 34563, 40372, 40376, 36605, 40380, 40384, 40388, + 40392, 40396, 40400, 32913, 40404, 40408, 40412, 40416, 40420, 40424, + 40428, 40432, 40436, 40440, 40444, 36885, 36165, 36170, 37185, 40448, + 40452, 40456, 40460, 37245, 40464, 18672, 40468, 40472, 37295, 40476, + 40480, 40484, 40488, 40492, 40496, 40500, 27075, 40504, 40508, 40512, + 40516, 40520, 40524, 40528, 35670, 40532, 40536, 40540, 40544, 40548, + 19753, 40552, 40556, 40560, 40564, 40568, 40572, 40576, 40580, 40584, + 33609, 40588, 40592, 40596, 40600, 40604, 40608, 40612, 38275, 902, + 40616, 40620, 40624, 39280, 20314, 40628, 24130, 40632, 40636, 40640, + 40644, 40648, 40652, 40656, 40660, 40664, 40668, 39295, 40672, 31839, + 24832, 29709, 40676, 40680, 28427, 40684, 40688, 27459, 40692, 32001, + 40696, 40700, 24076, 40704, 23281, 34197, 40708, 40712, 40716, 40720, + 40724, 40728, 40732, 40736, 40740, 40744, 33507, 32361, 40748, 40752, + 40756, 40760, 40764, 38385, 40768, 38395, 38425, 40772, 40776, 40780, + 39155, 40784, 40788, 40792, 40796, 35580, 40800, 35930, 38485, 39460, + 27371, 33783, 24400, 40804, 26299, 40808, 40812, 40816, 36370, 28123, + 36380, 40820, 40824, 40828, 40832, 40836, 40840, 40844, 40848, 40852, + 36385, 40856, 40860, 36390, 35900, 40864, 36400, 36405, 36410, 40868, + 36415, 36420, 36430, 36435, 35490, 36445, 40872, 36450, 38030, 36455, + 36460, 40876, 23878, 40880, 40884, 40888, 40892, 40896, 40900, 40904, + 40908, 40912, 40916, 29184, 40920, 40924, 40928, 40932, 40936, 40940, + 40944, 40948, 40952, 40956, 40960, 40964, 40968, 40972, 40976, 27995, + 40980, 40984, 28323, 40988, 36760, 40992, 36765, 28843, 36770, 40996, + 41000, 36780, 31683, 41004, 36790, 36795, 36800, 36805, 37485, 41008, + 41012, 41016, 38895, 36810, 36820, 41020, 41024, 41028, 36825, 36830, + 35565, 36835, 36840, 36845, 41032, 41036, 41040, 41044, 41048, 41052, + 41056, 41060, 41064, 25957, 35865, 41068, 41072, 41076, 41080, 41084, + 41088, 41092, 41096, 41100, 41104, 41108, 41112, 41116, 41120, 41124, + 41128, 41132, 41136, 41140, 41144, 41148, 41152, 41156, 41160, 41164, + 41168, 41172, 41176, 41180, 41184, 41188, 41192, 36995, 41196, 37000, + 37005, 41200, 41204, 29590, 37020, 41208, 37025, 41212, 41216, 41220, + 41224, 37030, 41228, 37035, 37040, 35635, 37050, 41232, 29107, 41236, + 35640, 37055, 37060, 37065, 37070, 41240, 41244, 41248, 41252, 35770, + 41256, 36575, 19560, 36585, 24652, 28723, 41260, 41264, 36590, 36595, + 36600, 33585, 41268, 41272, 41276, 41280, 35445, 21227, 25813, 41284, + 41288, 41292, 41296, 41300, 41304, 41308, 41312, 41316, 41320, 41324, + 41328, 25885, 36140, 29555, 36615, 36620, 41332, 36625, 31851, 36075, + 36080, 36085, 41336, 41340, 41344, 41348, 41352, 41356, 41360, 39480, + 34377, 32571, 32493, 32583, 21095, 36310, 41364, 33423, 28763, 39245, + 41368, 41372, 41376, 41380, 41384, 35760, 37540, 37545, 37550, 41388, + 41392, 37555, 37565, 37570, 37575, 37580, 35765, 37585, 41396, 37590, + 38235, 37595, 37600, 41400, 41404, 41408, 25588, 41412, 41416, 37675, + 41420, 672, 41424, 41428, 41432, 20248, 41436, 41440, 41444, 41448, + 41452, 41456, 41460, 41464, 41468, 41472, 41476, 41480, 18228, 41484, + 41488, 41492, 41496, 41500, 41504, 41508, 41512, 41516, 41520, 41524, + 41528, 41532, 41536, 41540, 41544, 26344, 37160, 41548, 35610, 37170, + 41552, 41556, 37175, 29079, 19643, 41560, 36160, 39255, 41564, 41568, + 41572, 36960, 41576, 37195, 37200, 41580, 41584, 37205, 41588, 283, + 37210, 37215, 37220, 36090, 37230, 37235, 37240, 37250, 37255, 41592, + 32979, 24184, 41596, 37265, 37270, 41600, 41604, 41608, 41612, 41616, + 41620, 41624, 41628, 41632, 37275, 41636, 41640, 41644, 41648, 37280, + 33555, 37290, 41652, 37300, 37305, 41656, 37310, 37315, 37320, 30549, + 37330, 41660, 37335, 41664, 37345, 41668, 33723, 41672, 37350, 16589, + 37360, 41676, 41680, 41684, 41688, 41692, 32085, 41696, 32625, 18636, + 18156, 41700, 37365, 41704, 37370, 41708, 26859, 41712, 38255, 28619, + 22221, 37375, 33141, 37380, 37385, 37390, 41716, 41720, 41724, 41728, + 41732, 41736, 37395, 34815, 37405, 41740, 41744, 41748, 41752, 41756, + 41760, 37410, 41764, 41768, 37415, 41772, 41776, 41780, 41784, 41788, + 41792, 41796, 41800, 34749, 34755, 35390, 34539, 30934, 41804, 41808, + 41812, 35480, 41816, 41820, 41824, 34257, 41828, 41832, 41836, 41840, + 41844, 39010, 41848, 41852, 41856, 41860, 41864, 41868, 41872, 41876, + 41880, 41884, 41888, 41892, 41896, 41900, 41904, 41908, 41912, 41916, + 41920, 41924, 41928, 41932, 41936, 41940, 41944, 41948, 41952, 41956, + 41960, 41964, 41968, 41972, 41976, 41980, 41984, 41988, 41992, 41996, + 42000, 42004, 42008, 42012, 42016, 16832, 42020, 38280, 26627, 42024, + 38285, 33081, 38295, 23231, 28139, 42028, 42032, 42036, 42040, 35190, + 38790, 38305, 42044, 42048, 42052, 42056, 42060, 42064, 38800, 38310, + 38315, 38320, 38325, 42068, 42072, 38330, 38335, 38340, 38345, 42076, + 42080, 42084, 28795, 20325, 39030, 42088, 42092, 39035, 39040, 42096, + 42100, 42104, 42108, 39045, 42112, 42116, 39050, 39055, 42120, 42124, + 39065, 39070, 39075, 42128, 42132, 42136, 42140, 42144, 35680, 42148, + 39080, 42152, 39085, 39090, 39095, 39100, 39105, 39110, 42156, 42160, + 42164, 42168, 42172, 42176, 42180, 42184, 42188, 42192, 39305, 39120, + 42196, 42200, 42204, 42208, 42212, 42216, 42220, 42224, 42228, 42232, + 42236, 42240, 1944, 42244, 42248, 42252, 42256, 42260, 42264, 42268, + 42272, 42276, 42280, 42284, 42288, 42292, 42296, 42300, 42304, 42308, + 42312, 42316, 42320, 39505, 36940, 42324, 22211, 42328, 42332, 36345, + 42336, 32589, 32595, 26867, 42340, 27419, 42344, 42348, 42352, 42356, + 42360, 42364, 42368, 42372, 42376, 42380, 42384, 42388, 42392, 42396, + 42400, 42404, 42408, 42412, 42416, 42420, 42424, 42428, 42432, 42436, + 42440, 42444, 42448, 42452, 42456, 42460, 42464, 42468, 37715, 37720, + 37450, 37455, 35720, 37460, 35725, 42472, 37465, 37470, 35730, 37725, + 37730, 37735, 42476, 42480, 42484, 42488, 42492, 38380, 42496, 35920, + 38390, 35925, 38400, 42500, 38405, 38410, 42504, 38415, 38420, 42508, + 42512, 42516, 42520, 42524, 38430, 38435, 38440, 39670, 38445, 42528, + 38450, 38455, 38460, 38465, 42532, 38470, 38475, 42536, 38480, 42540, + 38490, 39165, 38495, 38500, 38505, 38510, 42544, 42548, 42552, 33663, + 42556, 42560, 42564, 24140, 42567, 31540, 17813, 23202, 5561, 22212, + 4793, 28140, 27100, 24761, 36316, 26612, 21692, 42570, 17488, 40609, + 40625, 25760, 42573, 42576, 39857, 5049, 42579, 30452, 25463, 21195, + 5305, 32296, 29353, 34708, 29535, 42582, 39913, 42585, 34732, 40521, + 19754, 903, 42588, 34114, 32170, 42591, 1408, 36076, 42594, 733, 41349, + 30746, 42597, 692, 40517, 21239, 17239, 36606, 25427, 23302, 36081, + 19033, 42600, 1132, 42603, 18613, 698, 1027, 6073, 30844, 38601, 41313, + 726, 36866, 35866, 17267, 27124, 42606, 4857, 42609, 32452, 1021, 19069, + 42612, 23142, 19201, 19974, 32824, 42615, 25688, 42618, 42621, 42624, + 42627, 42630, 35386, 26327, 19309, 30648, 32002, 42633, 1193, 33484, + 34060, 39921, 33664, 5113, 40837, 18637, 42636, 34678, 28620, 20249, + 42639, 42642, 24716, 42645, 42648, 42651, 18157, 21250, 41005, 18397, + 42654, 32164, 1172, 22202, 42657, 34774, 297, 42660, 41337, 42663, 33634, + 30949, 19561, 40833, 5897, 24203, 23252, 20392, 31152, 24401, 40389, + 30193, 42666, 42669, 26420, 41045, 16833, 17280, 40829, 42672, 26964, + 35581, 40393, 40397, 42675, 42678, 18565, 29339, 965, 10745, 42681, + 42684, 42687, 16763, 42690, 29500, 42693, 34648, 42696, 12, 26492, 42699, + 5369, 42702, 39881, 42705, 31636, 28724, 24626, 673, 28820, 42708, 42711, + 42714, 42717, 1215, 42720, 42723, 42726, 18661, 42729, 42732, 42735, + 42738, 41593, 39506, 42741, 19525, 40841, 42744, 42747, 42750, 42753, + 42756, 30473, 29003, 31918, 42759, 6137, 32536, 42762, 42765, 42768, + 42771, 34180, 42774, 42777, 42780, 42783, 42786, 42121, 42789, 21591, + 1163, 42792, 42795, 42798, 42801, 41065, 42804, 23432, 1059, 284, 42807, + 915, 42810, 42813, 42816, 21063, 42819, 42257, 29661, 28524, 18457, + 42822, 41077, 42825, 39546, 16530, 39286, 42828, 24185, 41889, 19549, + 36381, 42831, 42834, 42837, 42840, 21972, 5337, 5353, 1465, 42843, 42846, + 5577, 42849, 42852, 42855, 19644, 6153, 41121, 42858, 42861, 42864, + 41713, 351, 42867, 42870, 42873, 42876, 42879, 41225, 22062, 21228, + 32194, 42882, 21085, 42885, 978, 42888, 40401, 42891, 33652, 42894, + 42897, 42900, 42903, 42906, 42909, 42912, 42915, 42918, 36306, 42921, + 42241, 42924, 664, 42927, 32044, 42930, 42933, 42936, 42939, 42942, + 42945, 42948, 42951, 42954, 41321, 42957, 42960, 42963, 35831, 42966, + 42969, 42972, 32374, 42975, 42978, 42981, 42984, 32596, 42987, 42990, + 42993, 1961, 42996, 41853, 42999, 43002, 36331, 43005, 43008, 36711, + 43011, 43014, 43017, 43020, 43023, 23789, 43026, 43029, 43032, 29969, + 43035, 42137, 43038, 43041, 43044, 32056, 43047, 43050, 17211, 29962, + 35641, 952, 43053, 43056, 31384, 43059, 43062, 41341, 40813, 41541, + 43065, 43068, 43071, 43074, 37776, 43077, 41797, 50, 1147, 43080, 40498, + 23862, 43082, 272, 43084, 34061, 40162, 28976, 35482, 43086, 571, 28906, + 27141, 43088, 43090, 811, 32057, 33707, 307, 911, 357, 43092, 37682, + 24357, 241, 43094, 42829, 43096, 4, 22233, 926, 948, 29116, 43098, 43100, + 19689, 796, 40762, 333, 42706, 710, 88, 41206, 623, 313, 165, 35797, + 42643, 32039, 19722, 31367, 43102, 43104, 28005, 43106, 43108, 43110, + 20008, 35102, 43112, 27541, 41598, 43114, 39746, 474, 1205, 28766, 51, + 98, 5, 14, 308, 171, 102, 70, 57, 9, 34, 273, 363, 804, 149, 348, 42737, + 43116, 465, 43117, +}; + +/* code->name phrasebook */ +#define phrasebook_shift 8 +#define phrasebook_short 231 +static unsigned char phrasebook[] = { + 0, 242, 69, 236, 186, 65, 238, 76, 65, 83, 57, 242, 158, 57, 240, 248, + 57, 237, 124, 236, 190, 36, 236, 47, 37, 236, 47, 238, 77, 249, 4, 57, + 242, 84, 235, 243, 213, 189, 239, 144, 27, 244, 173, 27, 121, 27, 114, + 27, 153, 27, 163, 27, 168, 27, 169, 27, 179, 27, 176, 27, 178, 242, 81, + 237, 123, 206, 57, 242, 61, 57, 196, 57, 239, 126, 65, 237, 127, 253, + 220, 9, 5, 1, 64, 9, 5, 1, 199, 9, 5, 1, 203, 9, 5, 1, 187, 9, 5, 1, 70, + 9, 5, 1, 204, 9, 5, 1, 194, 9, 5, 1, 164, 9, 5, 1, 69, 9, 5, 1, 200, 9, + 5, 1, 205, 9, 5, 1, 148, 9, 5, 1, 171, 9, 5, 1, 183, 9, 5, 1, 78, 9, 5, + 1, 198, 9, 5, 1, 209, 9, 5, 1, 135, 9, 5, 1, 159, 9, 5, 1, 190, 9, 5, 1, + 84, 9, 5, 1, 186, 9, 5, 1, 201, 9, 5, 1, 170, 9, 5, 1, 181, 9, 5, 1, 202, + 36, 30, 110, 240, 219, 239, 144, 37, 30, 110, 157, 240, 203, 253, 113, + 244, 176, 244, 201, 240, 203, 9, 3, 1, 64, 9, 3, 1, 199, 9, 3, 1, 203, 9, + 3, 1, 187, 9, 3, 1, 70, 9, 3, 1, 204, 9, 3, 1, 194, 9, 3, 1, 164, 9, 3, + 1, 69, 9, 3, 1, 200, 9, 3, 1, 205, 9, 3, 1, 148, 9, 3, 1, 171, 9, 3, 1, + 183, 9, 3, 1, 78, 9, 3, 1, 198, 9, 3, 1, 209, 9, 3, 1, 135, 9, 3, 1, 159, + 9, 3, 1, 190, 9, 3, 1, 84, 9, 3, 1, 186, 9, 3, 1, 201, 9, 3, 1, 170, 9, + 3, 1, 181, 9, 3, 1, 202, 36, 244, 175, 110, 55, 244, 176, 37, 244, 175, + 110, 180, 239, 190, 242, 69, 239, 116, 236, 186, 65, 249, 196, 57, 245, + 163, 57, 239, 147, 57, 254, 78, 57, 242, 173, 143, 242, 5, 57, 150, 238, + 195, 57, 239, 216, 241, 71, 237, 136, 235, 237, 42, 160, 238, 76, 65, + 141, 57, 249, 119, 240, 241, 238, 52, 57, 166, 242, 156, 57, 238, 45, 57, + 236, 181, 114, 236, 181, 153, 244, 196, 240, 203, 247, 93, 57, 241, 74, + 57, 242, 65, 227, 239, 117, 236, 181, 121, 239, 47, 241, 71, 237, 136, + 235, 190, 42, 160, 238, 76, 65, 242, 83, 239, 123, 253, 101, 240, 147, + 242, 83, 239, 123, 253, 101, 244, 217, 242, 83, 239, 123, 244, 171, 239, + 67, 239, 116, 239, 126, 65, 9, 5, 1, 119, 2, 158, 9, 5, 1, 119, 2, 116, + 9, 5, 1, 119, 2, 236, 180, 9, 5, 1, 119, 2, 180, 9, 5, 1, 119, 2, 150, 9, + 5, 1, 119, 2, 249, 9, 47, 9, 5, 1, 253, 163, 9, 5, 1, 255, 36, 2, 239, + 117, 9, 5, 1, 144, 2, 158, 9, 5, 1, 144, 2, 116, 9, 5, 1, 144, 2, 236, + 180, 9, 5, 1, 144, 2, 150, 9, 5, 1, 197, 2, 158, 9, 5, 1, 197, 2, 116, 9, + 5, 1, 197, 2, 236, 180, 9, 5, 1, 197, 2, 150, 9, 5, 1, 249, 77, 9, 5, 1, + 255, 28, 2, 180, 9, 5, 1, 109, 2, 158, 9, 5, 1, 109, 2, 116, 9, 5, 1, + 109, 2, 236, 180, 9, 5, 1, 109, 2, 180, 9, 5, 1, 109, 2, 150, 235, 191, + 57, 9, 5, 1, 109, 2, 122, 9, 5, 1, 115, 2, 158, 9, 5, 1, 115, 2, 116, 9, + 5, 1, 115, 2, 236, 180, 9, 5, 1, 115, 2, 150, 9, 5, 1, 255, 35, 2, 116, + 9, 5, 1, 242, 189, 9, 3, 1, 245, 5, 159, 9, 3, 1, 119, 2, 158, 9, 3, 1, + 119, 2, 116, 9, 3, 1, 119, 2, 236, 180, 9, 3, 1, 119, 2, 180, 9, 3, 1, + 119, 2, 150, 9, 3, 1, 119, 2, 249, 9, 47, 9, 3, 1, 253, 163, 9, 3, 1, + 255, 36, 2, 239, 117, 9, 3, 1, 144, 2, 158, 9, 3, 1, 144, 2, 116, 9, 3, + 1, 144, 2, 236, 180, 9, 3, 1, 144, 2, 150, 9, 3, 1, 197, 2, 158, 9, 3, 1, + 197, 2, 116, 9, 3, 1, 197, 2, 236, 180, 9, 3, 1, 197, 2, 150, 9, 3, 1, + 249, 77, 9, 3, 1, 255, 28, 2, 180, 9, 3, 1, 109, 2, 158, 9, 3, 1, 109, 2, + 116, 9, 3, 1, 109, 2, 236, 180, 9, 3, 1, 109, 2, 180, 9, 3, 1, 109, 2, + 150, 239, 156, 57, 9, 3, 1, 109, 2, 122, 9, 3, 1, 115, 2, 158, 9, 3, 1, + 115, 2, 116, 9, 3, 1, 115, 2, 236, 180, 9, 3, 1, 115, 2, 150, 9, 3, 1, + 255, 35, 2, 116, 9, 3, 1, 242, 189, 9, 3, 1, 255, 35, 2, 150, 9, 5, 1, + 119, 2, 166, 9, 3, 1, 119, 2, 166, 9, 5, 1, 119, 2, 212, 9, 3, 1, 119, 2, + 212, 9, 5, 1, 119, 2, 240, 216, 9, 3, 1, 119, 2, 240, 216, 9, 5, 1, 255, + 36, 2, 116, 9, 3, 1, 255, 36, 2, 116, 9, 5, 1, 255, 36, 2, 236, 180, 9, + 3, 1, 255, 36, 2, 236, 180, 9, 5, 1, 255, 36, 2, 49, 47, 9, 3, 1, 255, + 36, 2, 49, 47, 9, 5, 1, 255, 36, 2, 193, 9, 3, 1, 255, 36, 2, 193, 9, 5, + 1, 255, 34, 2, 193, 9, 3, 1, 255, 34, 2, 193, 9, 5, 1, 255, 34, 2, 122, + 9, 3, 1, 255, 34, 2, 122, 9, 5, 1, 144, 2, 166, 9, 3, 1, 144, 2, 166, 9, + 5, 1, 144, 2, 212, 9, 3, 1, 144, 2, 212, 9, 5, 1, 144, 2, 49, 47, 9, 3, + 1, 144, 2, 49, 47, 9, 5, 1, 144, 2, 240, 216, 9, 3, 1, 144, 2, 240, 216, + 9, 5, 1, 144, 2, 193, 9, 3, 1, 144, 2, 193, 9, 5, 1, 255, 32, 2, 236, + 180, 9, 3, 1, 255, 32, 2, 236, 180, 9, 5, 1, 255, 32, 2, 212, 9, 3, 1, + 255, 32, 2, 212, 9, 5, 1, 255, 32, 2, 49, 47, 9, 3, 1, 255, 32, 2, 49, + 47, 9, 5, 1, 255, 32, 2, 239, 117, 9, 3, 1, 255, 32, 2, 239, 117, 9, 5, + 1, 255, 37, 2, 236, 180, 9, 3, 1, 255, 37, 2, 236, 180, 9, 5, 1, 255, 37, + 2, 122, 9, 3, 1, 255, 37, 2, 122, 9, 5, 1, 197, 2, 180, 9, 3, 1, 197, 2, + 180, 9, 5, 1, 197, 2, 166, 9, 3, 1, 197, 2, 166, 9, 5, 1, 197, 2, 212, 9, + 3, 1, 197, 2, 212, 9, 5, 1, 197, 2, 240, 216, 9, 3, 1, 197, 2, 240, 216, + 9, 5, 1, 197, 2, 49, 47, 9, 3, 1, 240, 228, 69, 9, 5, 17, 254, 45, 9, 3, + 17, 254, 45, 9, 5, 1, 255, 45, 2, 236, 180, 9, 3, 1, 255, 45, 2, 236, + 180, 9, 5, 1, 255, 38, 2, 239, 117, 9, 3, 1, 255, 38, 2, 239, 117, 9, 3, + 1, 251, 170, 9, 5, 1, 255, 31, 2, 116, 9, 3, 1, 255, 31, 2, 116, 9, 5, 1, + 255, 31, 2, 239, 117, 9, 3, 1, 255, 31, 2, 239, 117, 9, 5, 1, 255, 31, 2, + 193, 9, 3, 1, 255, 31, 2, 193, 9, 5, 1, 255, 31, 2, 242, 65, 227, 9, 3, + 1, 255, 31, 2, 242, 65, 227, 9, 5, 1, 255, 31, 2, 122, 9, 3, 1, 255, 31, + 2, 122, 9, 5, 1, 255, 28, 2, 116, 9, 3, 1, 255, 28, 2, 116, 9, 5, 1, 255, + 28, 2, 239, 117, 9, 3, 1, 255, 28, 2, 239, 117, 9, 5, 1, 255, 28, 2, 193, + 9, 3, 1, 255, 28, 2, 193, 9, 3, 1, 255, 28, 240, 143, 254, 216, 236, 190, + 9, 5, 1, 249, 66, 9, 3, 1, 249, 66, 9, 5, 1, 109, 2, 166, 9, 3, 1, 109, + 2, 166, 9, 5, 1, 109, 2, 212, 9, 3, 1, 109, 2, 212, 9, 5, 1, 109, 2, 42, + 116, 9, 3, 1, 109, 2, 42, 116, 9, 5, 17, 253, 142, 9, 3, 17, 253, 142, 9, + 5, 1, 255, 30, 2, 116, 9, 3, 1, 255, 30, 2, 116, 9, 5, 1, 255, 30, 2, + 239, 117, 9, 3, 1, 255, 30, 2, 239, 117, 9, 5, 1, 255, 30, 2, 193, 9, 3, + 1, 255, 30, 2, 193, 9, 5, 1, 255, 29, 2, 116, 9, 3, 1, 255, 29, 2, 116, + 9, 5, 1, 255, 29, 2, 236, 180, 9, 3, 1, 255, 29, 2, 236, 180, 9, 5, 1, + 255, 29, 2, 239, 117, 9, 3, 1, 255, 29, 2, 239, 117, 9, 5, 1, 255, 29, 2, + 193, 9, 3, 1, 255, 29, 2, 193, 9, 5, 1, 255, 33, 2, 239, 117, 9, 3, 1, + 255, 33, 2, 239, 117, 9, 5, 1, 255, 33, 2, 193, 9, 3, 1, 255, 33, 2, 193, + 9, 5, 1, 255, 33, 2, 122, 9, 3, 1, 255, 33, 2, 122, 9, 5, 1, 115, 2, 180, + 9, 3, 1, 115, 2, 180, 9, 5, 1, 115, 2, 166, 9, 3, 1, 115, 2, 166, 9, 5, + 1, 115, 2, 212, 9, 3, 1, 115, 2, 212, 9, 5, 1, 115, 2, 249, 9, 47, 9, 3, + 1, 115, 2, 249, 9, 47, 9, 5, 1, 115, 2, 42, 116, 9, 3, 1, 115, 2, 42, + 116, 9, 5, 1, 115, 2, 240, 216, 9, 3, 1, 115, 2, 240, 216, 9, 5, 1, 255, + 40, 2, 236, 180, 9, 3, 1, 255, 40, 2, 236, 180, 9, 5, 1, 255, 35, 2, 236, + 180, 9, 3, 1, 255, 35, 2, 236, 180, 9, 5, 1, 255, 35, 2, 150, 9, 5, 1, + 255, 27, 2, 116, 9, 3, 1, 255, 27, 2, 116, 9, 5, 1, 255, 27, 2, 49, 47, + 9, 3, 1, 255, 27, 2, 49, 47, 9, 5, 1, 255, 27, 2, 193, 9, 3, 1, 255, 27, + 2, 193, 9, 3, 1, 165, 159, 9, 3, 1, 255, 41, 2, 122, 9, 5, 1, 255, 41, 2, + 128, 9, 5, 1, 255, 41, 2, 241, 8, 9, 3, 1, 255, 41, 2, 241, 8, 9, 5, 1, + 249, 5, 169, 9, 3, 1, 249, 5, 169, 9, 5, 1, 249, 0, 78, 9, 5, 1, 255, 36, + 2, 128, 9, 3, 1, 255, 36, 2, 128, 9, 5, 1, 241, 93, 187, 9, 5, 1, 255, + 34, 2, 128, 9, 5, 1, 255, 34, 2, 241, 8, 9, 3, 1, 255, 34, 2, 241, 8, 9, + 3, 1, 242, 60, 242, 87, 9, 5, 1, 224, 70, 9, 5, 1, 242, 164, 9, 5, 1, + 249, 0, 70, 9, 5, 1, 255, 42, 2, 128, 9, 3, 1, 255, 42, 2, 128, 9, 5, 1, + 255, 32, 2, 128, 9, 5, 1, 242, 75, 9, 3, 1, 254, 44, 9, 5, 1, 244, 191, + 9, 5, 1, 197, 2, 122, 9, 5, 1, 255, 38, 2, 128, 9, 3, 1, 255, 38, 2, 128, + 9, 3, 1, 255, 31, 2, 143, 9, 3, 1, 243, 200, 2, 122, 9, 5, 1, 242, 60, + 171, 9, 5, 1, 255, 28, 2, 36, 128, 9, 3, 1, 255, 28, 2, 165, 37, 249, 83, + 9, 5, 1, 109, 2, 242, 65, 180, 9, 5, 1, 109, 2, 244, 210, 9, 3, 1, 109, + 2, 244, 210, 9, 5, 1, 254, 62, 9, 3, 1, 254, 62, 9, 5, 1, 255, 46, 2, + 128, 9, 3, 1, 255, 46, 2, 128, 9, 1, 254, 80, 9, 5, 1, 249, 5, 114, 9, 3, + 1, 249, 5, 114, 9, 5, 1, 249, 67, 9, 1, 224, 254, 38, 245, 28, 9, 3, 1, + 255, 33, 2, 241, 233, 128, 9, 5, 1, 255, 33, 2, 128, 9, 3, 1, 255, 33, 2, + 128, 9, 5, 1, 255, 33, 2, 238, 100, 128, 9, 5, 1, 115, 2, 244, 210, 9, 3, + 1, 115, 2, 244, 210, 9, 5, 1, 239, 127, 9, 5, 1, 255, 44, 2, 128, 9, 5, + 1, 255, 35, 2, 128, 9, 3, 1, 255, 35, 2, 128, 9, 5, 1, 255, 27, 2, 122, + 9, 3, 1, 255, 27, 2, 122, 9, 5, 1, 249, 221, 9, 5, 1, 253, 206, 238, 156, + 9, 3, 1, 253, 206, 238, 156, 9, 3, 1, 253, 206, 2, 244, 182, 9, 1, 225, + 2, 122, 9, 5, 1, 249, 5, 168, 9, 3, 1, 249, 5, 168, 9, 1, 239, 116, 240, + 211, 249, 71, 2, 122, 9, 1, 245, 224, 9, 1, 243, 91, 242, 140, 9, 1, 241, + 197, 242, 140, 9, 1, 239, 249, 242, 140, 9, 1, 238, 100, 242, 140, 9, 5, + 1, 254, 231, 2, 193, 9, 5, 1, 255, 34, 2, 3, 1, 255, 27, 2, 193, 9, 3, 1, + 254, 231, 2, 193, 9, 5, 1, 254, 53, 9, 5, 1, 255, 31, 2, 3, 1, 200, 9, 3, + 1, 254, 53, 9, 5, 1, 254, 57, 9, 5, 1, 255, 28, 2, 3, 1, 200, 9, 3, 1, + 254, 57, 9, 5, 1, 119, 2, 193, 9, 3, 1, 119, 2, 193, 9, 5, 1, 197, 2, + 193, 9, 3, 1, 197, 2, 193, 9, 5, 1, 109, 2, 193, 9, 3, 1, 109, 2, 193, 9, + 5, 1, 115, 2, 193, 9, 3, 1, 115, 2, 193, 9, 5, 1, 115, 2, 238, 81, 18, + 166, 9, 3, 1, 115, 2, 238, 81, 18, 166, 9, 5, 1, 115, 2, 238, 81, 18, + 116, 9, 3, 1, 115, 2, 238, 81, 18, 116, 9, 5, 1, 115, 2, 238, 81, 18, + 193, 9, 3, 1, 115, 2, 238, 81, 18, 193, 9, 5, 1, 115, 2, 238, 81, 18, + 158, 9, 3, 1, 115, 2, 238, 81, 18, 158, 9, 3, 1, 242, 60, 70, 9, 5, 1, + 119, 2, 238, 81, 18, 166, 9, 3, 1, 119, 2, 238, 81, 18, 166, 9, 5, 1, + 119, 2, 49, 53, 18, 166, 9, 3, 1, 119, 2, 49, 53, 18, 166, 9, 5, 1, 254, + 221, 2, 166, 9, 3, 1, 254, 221, 2, 166, 9, 5, 1, 255, 32, 2, 122, 9, 3, + 1, 255, 32, 2, 122, 9, 5, 1, 255, 32, 2, 193, 9, 3, 1, 255, 32, 2, 193, + 9, 5, 1, 255, 38, 2, 193, 9, 3, 1, 255, 38, 2, 193, 9, 5, 1, 109, 2, 240, + 216, 9, 3, 1, 109, 2, 240, 216, 9, 5, 1, 109, 2, 242, 234, 18, 166, 9, 3, + 1, 109, 2, 242, 234, 18, 166, 9, 5, 1, 253, 206, 2, 193, 9, 3, 1, 253, + 206, 2, 193, 9, 3, 1, 255, 45, 2, 193, 9, 5, 1, 254, 35, 9, 5, 1, 255, + 34, 2, 3, 1, 202, 9, 3, 1, 254, 35, 9, 5, 1, 255, 32, 2, 116, 9, 3, 1, + 255, 32, 2, 116, 9, 5, 1, 242, 225, 9, 5, 1, 245, 224, 9, 5, 1, 255, 28, + 2, 158, 9, 3, 1, 255, 28, 2, 158, 9, 5, 1, 119, 2, 249, 9, 53, 18, 116, + 9, 3, 1, 119, 2, 249, 9, 53, 18, 116, 9, 5, 1, 254, 221, 2, 116, 9, 3, 1, + 254, 221, 2, 116, 9, 5, 1, 109, 2, 242, 114, 18, 116, 9, 3, 1, 109, 2, + 242, 114, 18, 116, 9, 5, 1, 119, 2, 42, 158, 9, 3, 1, 119, 2, 42, 158, 9, + 5, 1, 119, 2, 239, 116, 212, 9, 3, 1, 119, 2, 239, 116, 212, 9, 5, 1, + 144, 2, 42, 158, 9, 3, 1, 144, 2, 42, 158, 9, 5, 1, 144, 2, 239, 116, + 212, 9, 3, 1, 144, 2, 239, 116, 212, 9, 5, 1, 197, 2, 42, 158, 9, 3, 1, + 197, 2, 42, 158, 9, 5, 1, 197, 2, 239, 116, 212, 9, 3, 1, 197, 2, 239, + 116, 212, 9, 5, 1, 109, 2, 42, 158, 9, 3, 1, 109, 2, 42, 158, 9, 5, 1, + 109, 2, 239, 116, 212, 9, 3, 1, 109, 2, 239, 116, 212, 9, 5, 1, 255, 30, + 2, 42, 158, 9, 3, 1, 255, 30, 2, 42, 158, 9, 5, 1, 255, 30, 2, 239, 116, + 212, 9, 3, 1, 255, 30, 2, 239, 116, 212, 9, 5, 1, 115, 2, 42, 158, 9, 3, + 1, 115, 2, 42, 158, 9, 5, 1, 115, 2, 239, 116, 212, 9, 3, 1, 115, 2, 239, + 116, 212, 9, 5, 1, 255, 29, 2, 244, 192, 45, 9, 3, 1, 255, 29, 2, 244, + 192, 45, 9, 5, 1, 255, 33, 2, 244, 192, 45, 9, 3, 1, 255, 33, 2, 244, + 192, 45, 9, 5, 1, 245, 227, 9, 3, 1, 245, 227, 9, 5, 1, 255, 37, 2, 193, + 9, 3, 1, 255, 37, 2, 193, 9, 5, 1, 255, 28, 2, 165, 37, 249, 83, 9, 5, 1, + 253, 247, 9, 3, 1, 253, 247, 9, 5, 1, 255, 27, 2, 128, 9, 3, 1, 255, 27, + 2, 128, 9, 5, 1, 119, 2, 49, 47, 9, 3, 1, 119, 2, 49, 47, 9, 5, 1, 144, + 2, 239, 117, 9, 3, 1, 144, 2, 239, 117, 9, 5, 1, 109, 2, 238, 81, 18, + 166, 9, 3, 1, 109, 2, 238, 81, 18, 166, 9, 5, 1, 109, 2, 229, 18, 166, 9, + 3, 1, 109, 2, 229, 18, 166, 9, 5, 1, 109, 2, 49, 47, 9, 3, 1, 109, 2, 49, + 47, 9, 5, 1, 109, 2, 49, 53, 18, 166, 9, 3, 1, 109, 2, 49, 53, 18, 166, + 9, 5, 1, 255, 35, 2, 166, 9, 3, 1, 255, 35, 2, 166, 9, 3, 1, 242, 60, 64, + 9, 3, 1, 242, 79, 9, 3, 1, 242, 60, 242, 79, 9, 3, 1, 255, 41, 2, 128, 9, + 3, 1, 249, 0, 78, 9, 3, 1, 255, 36, 2, 244, 226, 9, 3, 1, 255, 34, 2, + 244, 182, 9, 3, 1, 255, 34, 2, 128, 9, 3, 1, 224, 70, 9, 3, 1, 242, 164, + 9, 3, 1, 245, 41, 2, 128, 9, 3, 1, 249, 0, 70, 9, 3, 1, 224, 249, 0, 70, + 9, 3, 1, 224, 249, 0, 144, 2, 128, 9, 3, 1, 242, 77, 224, 249, 0, 70, 9, + 3, 1, 240, 228, 255, 45, 2, 122, 9, 3, 1, 255, 32, 2, 128, 9, 3, 1, 71, + 194, 9, 1, 3, 5, 194, 9, 3, 1, 242, 75, 9, 3, 1, 252, 117, 244, 210, 9, + 3, 1, 242, 60, 164, 9, 3, 1, 255, 37, 2, 128, 9, 3, 1, 251, 64, 2, 128, + 9, 3, 1, 197, 2, 122, 9, 3, 1, 244, 191, 9, 1, 3, 5, 69, 9, 3, 1, 255, + 31, 2, 242, 65, 180, 9, 3, 1, 255, 31, 2, 246, 21, 9, 3, 1, 255, 31, 2, + 238, 100, 128, 9, 3, 1, 247, 59, 9, 3, 1, 242, 60, 171, 9, 3, 1, 242, 60, + 255, 39, 2, 165, 249, 83, 9, 3, 1, 255, 39, 2, 128, 9, 3, 1, 255, 28, 2, + 36, 128, 9, 3, 1, 255, 28, 2, 238, 100, 128, 9, 1, 3, 5, 183, 9, 3, 1, + 242, 138, 78, 9, 1, 3, 5, 253, 142, 9, 3, 1, 242, 77, 242, 72, 9, 3, 1, + 249, 30, 9, 3, 1, 242, 60, 135, 9, 3, 1, 242, 60, 255, 30, 2, 165, 249, + 83, 9, 3, 1, 242, 60, 255, 30, 2, 128, 9, 3, 1, 255, 30, 2, 165, 249, 83, + 9, 3, 1, 255, 30, 2, 244, 182, 9, 3, 1, 255, 30, 2, 238, 139, 9, 3, 1, + 224, 255, 30, 2, 238, 139, 9, 1, 3, 5, 135, 9, 1, 3, 5, 239, 116, 135, 9, + 3, 1, 255, 29, 2, 128, 9, 3, 1, 249, 67, 9, 3, 1, 240, 228, 255, 45, 2, + 242, 114, 18, 128, 9, 3, 1, 245, 196, 224, 249, 67, 9, 3, 1, 254, 38, 2, + 244, 226, 9, 3, 1, 242, 60, 190, 9, 3, 1, 255, 33, 2, 238, 100, 128, 9, + 3, 1, 115, 143, 9, 3, 1, 239, 127, 9, 3, 1, 255, 44, 2, 128, 9, 3, 1, + 242, 60, 186, 9, 3, 1, 242, 60, 201, 9, 3, 1, 242, 60, 181, 9, 1, 3, 5, + 181, 9, 3, 1, 255, 27, 2, 238, 100, 128, 9, 3, 1, 255, 27, 2, 244, 226, + 9, 3, 1, 249, 221, 9, 3, 1, 253, 206, 2, 244, 226, 9, 1, 240, 211, 189, + 9, 1, 237, 117, 242, 116, 238, 9, 9, 1, 239, 116, 240, 211, 189, 9, 1, + 239, 87, 203, 9, 1, 239, 205, 242, 140, 9, 1, 3, 5, 199, 9, 3, 1, 242, + 77, 249, 0, 70, 9, 1, 3, 5, 255, 32, 2, 128, 9, 1, 3, 5, 164, 9, 3, 1, + 255, 45, 2, 236, 7, 9, 3, 1, 242, 60, 205, 9, 1, 3, 5, 148, 9, 3, 1, 255, + 47, 2, 128, 9, 1, 240, 211, 249, 71, 2, 122, 9, 1, 224, 240, 211, 249, + 71, 2, 122, 9, 3, 1, 254, 231, 239, 152, 9, 3, 1, 250, 216, 239, 152, 9, + 3, 1, 254, 231, 240, 253, 2, 244, 226, 9, 3, 1, 255, 24, 239, 152, 9, 3, + 1, 252, 201, 239, 152, 9, 3, 1, 255, 23, 240, 253, 2, 244, 226, 9, 3, 1, + 251, 2, 239, 152, 9, 3, 1, 255, 11, 239, 152, 9, 3, 1, 255, 12, 239, 152, + 9, 1, 239, 205, 236, 224, 9, 1, 240, 8, 236, 224, 147, 1, 3, 164, 147, 1, + 3, 255, 37, 2, 128, 147, 1, 3, 200, 147, 1, 3, 135, 147, 1, 3, 242, 60, + 135, 147, 1, 3, 242, 60, 255, 30, 2, 128, 147, 1, 3, 5, 239, 116, 135, + 147, 1, 3, 201, 147, 1, 3, 181, 147, 1, 242, 107, 147, 1, 42, 242, 107, + 147, 1, 242, 60, 242, 84, 147, 1, 236, 190, 147, 1, 224, 242, 84, 147, 1, + 37, 136, 244, 183, 147, 1, 36, 136, 244, 183, 147, 1, 240, 211, 189, 147, + 1, 224, 240, 211, 189, 147, 1, 36, 237, 116, 147, 1, 37, 237, 116, 147, + 1, 75, 237, 116, 147, 1, 79, 237, 116, 147, 1, 157, 240, 203, 193, 147, + 1, 55, 244, 176, 147, 1, 166, 147, 1, 244, 196, 240, 203, 147, 1, 244, + 201, 240, 203, 147, 1, 253, 113, 55, 244, 176, 147, 1, 253, 113, 166, + 147, 1, 253, 113, 244, 201, 240, 203, 147, 1, 253, 113, 244, 196, 240, + 203, 147, 1, 237, 144, 242, 81, 147, 1, 136, 237, 144, 242, 81, 147, 1, + 241, 13, 37, 136, 244, 183, 147, 1, 241, 13, 36, 136, 244, 183, 147, 1, + 75, 244, 184, 147, 1, 79, 244, 184, 147, 1, 249, 4, 57, 147, 1, 244, 197, + 57, 212, 49, 47, 249, 9, 47, 240, 216, 3, 180, 42, 244, 196, 240, 203, + 147, 1, 244, 59, 128, 147, 1, 244, 234, 240, 203, 147, 1, 3, 242, 75, + 147, 1, 3, 148, 147, 1, 3, 159, 147, 1, 3, 170, 147, 1, 3, 224, 240, 211, + 189, 147, 1, 238, 7, 249, 5, 143, 147, 1, 249, 10, 249, 5, 143, 147, 1, + 254, 145, 249, 5, 143, 147, 1, 253, 113, 249, 5, 143, 147, 1, 239, 11, + 249, 5, 143, 147, 1, 254, 79, 238, 119, 249, 5, 65, 147, 1, 250, 112, + 238, 119, 249, 5, 65, 147, 1, 240, 195, 147, 1, 236, 179, 147, 1, 42, + 236, 190, 48, 244, 201, 240, 203, 48, 244, 196, 240, 203, 48, 157, 240, + 203, 48, 180, 48, 166, 48, 238, 98, 48, 212, 48, 49, 47, 48, 150, 48, + 248, 255, 47, 48, 249, 9, 47, 48, 42, 244, 196, 240, 203, 48, 193, 48, + 55, 217, 47, 48, 42, 55, 217, 47, 48, 42, 244, 201, 240, 203, 48, 236, + 49, 48, 239, 116, 212, 48, 242, 60, 244, 192, 47, 48, 244, 192, 47, 48, + 224, 244, 192, 47, 48, 244, 192, 53, 184, 48, 244, 201, 230, 45, 48, 244, + 196, 230, 45, 48, 36, 249, 35, 45, 48, 37, 249, 35, 45, 48, 36, 160, 47, + 48, 244, 210, 48, 36, 136, 249, 9, 45, 48, 75, 249, 35, 45, 48, 79, 249, + 35, 45, 48, 249, 4, 25, 45, 48, 244, 197, 25, 45, 48, 237, 80, 248, 255, + 45, 48, 238, 100, 248, 255, 45, 48, 49, 45, 48, 238, 81, 45, 48, 249, 9, + 45, 48, 244, 192, 45, 48, 239, 117, 48, 240, 216, 48, 55, 217, 45, 48, + 242, 153, 45, 48, 239, 116, 42, 250, 138, 45, 48, 245, 15, 45, 48, 157, + 230, 45, 48, 244, 194, 45, 48, 239, 116, 244, 194, 45, 48, 229, 45, 48, + 242, 101, 45, 48, 253, 113, 244, 176, 48, 42, 253, 113, 244, 176, 48, + 229, 239, 131, 48, 207, 242, 114, 239, 131, 48, 165, 242, 114, 239, 131, + 48, 207, 240, 230, 239, 131, 48, 165, 240, 230, 239, 131, 48, 37, 136, + 249, 9, 45, 48, 239, 116, 242, 153, 45, 48, 30, 45, 48, 242, 2, 45, 48, + 255, 43, 47, 48, 55, 180, 48, 42, 238, 98, 48, 244, 201, 249, 5, 65, 48, + 244, 196, 249, 5, 65, 48, 26, 236, 43, 48, 26, 239, 186, 48, 26, 238, 85, + 242, 70, 48, 26, 211, 48, 242, 153, 47, 48, 242, 61, 25, 45, 48, 42, 55, + 217, 45, 48, 36, 160, 45, 48, 141, 229, 47, 48, 238, 14, 47, 48, 242, 93, + 80, 139, 47, 48, 36, 37, 59, 45, 48, 210, 59, 45, 48, 240, 82, 241, 22, + 48, 42, 180, 48, 42, 239, 116, 212, 48, 42, 63, 59, 45, 48, 9, 3, 1, 64, + 48, 9, 3, 1, 70, 48, 9, 3, 1, 69, 48, 9, 3, 1, 78, 48, 9, 3, 1, 84, 48, + 9, 3, 1, 203, 48, 9, 3, 1, 187, 48, 9, 3, 1, 164, 48, 9, 3, 1, 171, 48, + 9, 3, 1, 135, 48, 9, 3, 1, 190, 48, 9, 3, 1, 186, 48, 9, 3, 1, 170, 26, + 151, 57, 26, 149, 151, 57, 26, 211, 26, 239, 126, 65, 26, 242, 70, 26, + 238, 85, 242, 70, 26, 5, 1, 162, 2, 242, 70, 26, 254, 82, 241, 83, 26, 5, + 1, 240, 204, 2, 242, 70, 26, 5, 1, 182, 2, 242, 70, 26, 5, 1, 228, 2, + 242, 70, 26, 5, 1, 240, 210, 2, 242, 70, 26, 5, 1, 240, 199, 2, 242, 70, + 26, 5, 1, 172, 2, 242, 70, 26, 3, 1, 228, 2, 238, 85, 18, 242, 70, 26, 5, + 1, 242, 79, 26, 5, 1, 244, 198, 26, 5, 1, 242, 75, 26, 5, 1, 242, 87, 26, + 5, 1, 239, 133, 26, 5, 1, 244, 204, 26, 5, 1, 249, 42, 26, 5, 1, 242, 97, + 26, 5, 1, 244, 191, 26, 5, 1, 242, 95, 26, 5, 1, 242, 96, 26, 5, 1, 253, + 114, 26, 5, 1, 253, 112, 26, 5, 1, 253, 135, 26, 5, 1, 239, 137, 26, 5, + 1, 253, 116, 26, 5, 1, 249, 32, 26, 5, 1, 242, 73, 26, 5, 1, 249, 43, 26, + 5, 1, 239, 127, 26, 5, 1, 249, 30, 26, 5, 1, 249, 23, 26, 5, 1, 249, 31, + 26, 5, 1, 242, 72, 26, 5, 1, 228, 2, 237, 133, 26, 5, 1, 240, 199, 2, + 237, 133, 26, 3, 1, 162, 2, 242, 70, 26, 3, 1, 240, 204, 2, 242, 70, 26, + 3, 1, 182, 2, 242, 70, 26, 3, 1, 228, 2, 242, 70, 26, 3, 1, 240, 199, 2, + 238, 85, 18, 242, 70, 26, 3, 1, 242, 79, 26, 3, 1, 244, 198, 26, 3, 1, + 242, 75, 26, 3, 1, 242, 87, 26, 3, 1, 239, 133, 26, 3, 1, 244, 204, 26, + 3, 1, 249, 42, 26, 3, 1, 242, 97, 26, 3, 1, 244, 191, 26, 3, 1, 242, 95, + 26, 3, 1, 242, 96, 26, 3, 1, 253, 114, 26, 3, 1, 253, 112, 26, 3, 1, 253, + 135, 26, 3, 1, 239, 137, 26, 3, 1, 253, 116, 26, 3, 1, 249, 32, 26, 3, 1, + 29, 242, 73, 26, 3, 1, 242, 73, 26, 3, 1, 249, 43, 26, 3, 1, 239, 127, + 26, 3, 1, 249, 30, 26, 3, 1, 249, 23, 26, 3, 1, 249, 31, 26, 3, 1, 242, + 72, 26, 3, 1, 228, 2, 237, 133, 26, 3, 1, 240, 199, 2, 237, 133, 26, 3, + 1, 240, 210, 2, 242, 70, 26, 3, 1, 240, 199, 2, 242, 70, 26, 3, 1, 172, + 2, 242, 70, 26, 250, 155, 90, 26, 244, 203, 90, 26, 240, 199, 2, 248, + 255, 90, 26, 240, 199, 2, 244, 196, 18, 248, 255, 90, 26, 240, 199, 2, + 238, 81, 18, 248, 255, 90, 26, 253, 212, 90, 26, 254, 224, 90, 26, 254, + 15, 90, 26, 1, 241, 39, 242, 128, 26, 3, 1, 241, 39, 242, 128, 26, 1, + 241, 32, 26, 3, 1, 241, 32, 26, 1, 239, 213, 26, 3, 1, 239, 213, 26, 1, + 242, 128, 26, 3, 1, 242, 128, 26, 1, 242, 243, 26, 3, 1, 242, 243, 242, + 85, 5, 1, 245, 183, 242, 85, 3, 1, 245, 183, 242, 85, 5, 1, 249, 223, + 242, 85, 3, 1, 249, 223, 242, 85, 5, 1, 245, 123, 242, 85, 3, 1, 245, + 123, 242, 85, 5, 1, 245, 109, 242, 85, 3, 1, 245, 109, 242, 85, 5, 1, + 241, 56, 242, 85, 3, 1, 241, 56, 242, 85, 5, 1, 242, 252, 242, 85, 3, 1, + 242, 252, 242, 85, 5, 1, 249, 213, 242, 85, 3, 1, 249, 213, 26, 244, 228, + 90, 26, 253, 168, 90, 26, 242, 180, 248, 76, 90, 26, 1, 250, 118, 26, 5, + 244, 203, 90, 26, 242, 180, 240, 204, 90, 26, 224, 242, 180, 240, 204, + 90, 31, 5, 1, 254, 233, 2, 158, 31, 5, 1, 254, 46, 31, 5, 1, 249, 97, 31, + 5, 1, 249, 237, 31, 5, 1, 238, 164, 253, 181, 31, 5, 1, 249, 76, 31, 5, + 1, 231, 233, 69, 31, 5, 1, 253, 139, 31, 5, 1, 253, 239, 31, 5, 1, 249, + 106, 31, 5, 1, 249, 111, 31, 5, 1, 245, 212, 31, 5, 1, 250, 2, 31, 5, 1, + 197, 2, 158, 31, 5, 1, 207, 84, 31, 5, 1, 245, 79, 31, 5, 1, 64, 31, 5, + 1, 253, 191, 31, 5, 1, 253, 214, 31, 5, 1, 249, 78, 31, 5, 1, 253, 152, + 31, 5, 1, 253, 181, 31, 5, 1, 249, 74, 31, 5, 1, 253, 189, 31, 5, 1, 69, + 31, 5, 1, 207, 69, 31, 5, 1, 208, 31, 5, 1, 253, 223, 31, 5, 1, 253, 224, + 31, 5, 1, 253, 166, 31, 5, 1, 78, 31, 5, 1, 253, 143, 31, 5, 1, 253, 205, + 31, 5, 1, 253, 225, 31, 5, 1, 253, 151, 31, 5, 1, 84, 31, 5, 1, 253, 222, + 31, 5, 1, 253, 88, 31, 5, 1, 249, 64, 31, 5, 1, 249, 44, 31, 5, 1, 249, + 7, 31, 5, 1, 242, 249, 31, 5, 1, 249, 241, 57, 31, 5, 1, 245, 225, 31, 5, + 1, 249, 119, 57, 31, 5, 1, 70, 31, 5, 1, 253, 124, 31, 5, 1, 216, 31, 3, + 1, 64, 31, 3, 1, 253, 191, 31, 3, 1, 253, 214, 31, 3, 1, 249, 78, 31, 3, + 1, 253, 152, 31, 3, 1, 253, 181, 31, 3, 1, 249, 74, 31, 3, 1, 253, 189, + 31, 3, 1, 69, 31, 3, 1, 207, 69, 31, 3, 1, 208, 31, 3, 1, 253, 223, 31, + 3, 1, 253, 224, 31, 3, 1, 253, 166, 31, 3, 1, 78, 31, 3, 1, 253, 143, 31, + 3, 1, 253, 205, 31, 3, 1, 253, 225, 31, 3, 1, 253, 151, 31, 3, 1, 84, 31, + 3, 1, 253, 222, 31, 3, 1, 253, 88, 31, 3, 1, 249, 64, 31, 3, 1, 249, 44, + 31, 3, 1, 249, 7, 31, 3, 1, 242, 249, 31, 3, 1, 249, 241, 57, 31, 3, 1, + 245, 225, 31, 3, 1, 249, 119, 57, 31, 3, 1, 70, 31, 3, 1, 253, 124, 31, + 3, 1, 216, 31, 3, 1, 254, 233, 2, 158, 31, 3, 1, 254, 46, 31, 3, 1, 249, + 97, 31, 3, 1, 249, 237, 31, 3, 1, 238, 164, 253, 181, 31, 3, 1, 249, 76, + 31, 3, 1, 231, 233, 69, 31, 3, 1, 253, 139, 31, 3, 1, 253, 239, 31, 3, 1, + 249, 106, 31, 3, 1, 249, 111, 31, 3, 1, 245, 212, 31, 3, 1, 250, 2, 31, + 3, 1, 197, 2, 158, 31, 3, 1, 207, 84, 31, 3, 1, 245, 79, 31, 5, 1, 242, + 72, 31, 3, 1, 242, 72, 31, 5, 1, 250, 113, 31, 3, 1, 250, 113, 31, 5, 1, + 239, 157, 70, 31, 3, 1, 239, 157, 70, 31, 5, 1, 242, 147, 249, 58, 31, 3, + 1, 242, 147, 249, 58, 31, 5, 1, 239, 157, 242, 147, 249, 58, 31, 3, 1, + 239, 157, 242, 147, 249, 58, 31, 5, 1, 253, 202, 249, 58, 31, 3, 1, 253, + 202, 249, 58, 31, 5, 1, 239, 157, 253, 202, 249, 58, 31, 3, 1, 239, 157, + 253, 202, 249, 58, 31, 5, 1, 250, 4, 31, 3, 1, 250, 4, 31, 5, 1, 249, 31, + 31, 3, 1, 249, 31, 31, 5, 1, 245, 86, 31, 3, 1, 245, 86, 31, 5, 1, 239, + 219, 31, 3, 1, 239, 219, 31, 5, 1, 241, 60, 2, 42, 244, 201, 240, 203, + 31, 3, 1, 241, 60, 2, 42, 244, 201, 240, 203, 31, 5, 1, 254, 74, 31, 3, + 1, 254, 74, 31, 5, 1, 245, 174, 242, 72, 31, 3, 1, 245, 174, 242, 72, 31, + 5, 1, 172, 2, 242, 190, 31, 3, 1, 172, 2, 242, 190, 31, 5, 1, 254, 17, + 31, 3, 1, 254, 17, 31, 5, 1, 242, 128, 31, 3, 1, 242, 128, 31, 238, 133, + 57, 48, 31, 242, 190, 48, 31, 235, 182, 48, 31, 130, 238, 152, 48, 31, + 138, 238, 152, 48, 31, 241, 21, 238, 133, 57, 48, 31, 240, 117, 57, 31, + 5, 1, 207, 197, 2, 244, 182, 31, 3, 1, 207, 197, 2, 244, 182, 31, 5, 1, + 239, 233, 57, 31, 3, 1, 239, 233, 57, 31, 5, 1, 254, 245, 2, 244, 232, + 31, 3, 1, 254, 245, 2, 244, 232, 31, 5, 1, 253, 208, 2, 241, 89, 31, 3, + 1, 253, 208, 2, 241, 89, 31, 5, 1, 253, 208, 2, 122, 31, 3, 1, 253, 208, + 2, 122, 31, 5, 1, 253, 208, 2, 242, 65, 128, 31, 3, 1, 253, 208, 2, 242, + 65, 128, 31, 5, 1, 253, 217, 2, 237, 112, 31, 3, 1, 253, 217, 2, 237, + 112, 31, 5, 1, 254, 238, 2, 237, 112, 31, 3, 1, 254, 238, 2, 237, 112, + 31, 5, 1, 254, 218, 2, 237, 112, 31, 3, 1, 254, 218, 2, 237, 112, 31, 5, + 1, 254, 218, 2, 55, 122, 31, 3, 1, 254, 218, 2, 55, 122, 31, 5, 1, 254, + 218, 2, 122, 31, 3, 1, 254, 218, 2, 122, 31, 5, 1, 241, 42, 208, 31, 3, + 1, 241, 42, 208, 31, 5, 1, 254, 215, 2, 237, 112, 31, 3, 1, 254, 215, 2, + 237, 112, 31, 5, 17, 254, 215, 249, 78, 31, 3, 17, 254, 215, 249, 78, 31, + 5, 1, 254, 229, 2, 242, 65, 128, 31, 3, 1, 254, 229, 2, 242, 65, 128, 31, + 5, 1, 238, 91, 253, 88, 31, 3, 1, 238, 91, 253, 88, 31, 5, 1, 254, 246, + 2, 237, 112, 31, 3, 1, 254, 246, 2, 237, 112, 31, 5, 1, 254, 237, 2, 237, + 112, 31, 3, 1, 254, 237, 2, 237, 112, 31, 5, 1, 239, 175, 84, 31, 3, 1, + 239, 175, 84, 31, 5, 1, 239, 175, 115, 2, 122, 31, 3, 1, 239, 175, 115, + 2, 122, 31, 5, 1, 254, 248, 2, 237, 112, 31, 3, 1, 254, 248, 2, 237, 112, + 31, 5, 17, 254, 237, 249, 64, 31, 3, 17, 254, 237, 249, 64, 31, 5, 1, + 253, 164, 2, 237, 112, 31, 3, 1, 253, 164, 2, 237, 112, 31, 5, 1, 253, + 164, 2, 55, 122, 31, 3, 1, 253, 164, 2, 55, 122, 31, 5, 1, 245, 184, 31, + 3, 1, 245, 184, 31, 5, 1, 238, 91, 249, 44, 31, 3, 1, 238, 91, 249, 44, + 31, 5, 1, 238, 91, 253, 164, 2, 237, 112, 31, 3, 1, 238, 91, 253, 164, 2, + 237, 112, 31, 1, 239, 57, 31, 5, 1, 253, 217, 2, 212, 31, 3, 1, 253, 217, + 2, 212, 31, 5, 1, 254, 218, 2, 128, 31, 3, 1, 254, 218, 2, 128, 31, 5, 1, + 254, 241, 2, 244, 182, 31, 3, 1, 254, 241, 2, 244, 182, 31, 5, 1, 254, + 215, 2, 128, 31, 3, 1, 254, 215, 2, 128, 31, 5, 1, 254, 215, 2, 244, 182, + 31, 3, 1, 254, 215, 2, 244, 182, 31, 5, 1, 237, 160, 249, 44, 31, 3, 1, + 237, 160, 249, 44, 31, 5, 1, 254, 220, 2, 244, 182, 31, 3, 1, 254, 220, + 2, 244, 182, 31, 5, 1, 119, 2, 212, 31, 3, 1, 119, 2, 212, 31, 5, 1, 119, + 2, 150, 31, 3, 1, 119, 2, 150, 31, 5, 17, 119, 253, 181, 31, 3, 17, 119, + 253, 181, 31, 5, 1, 254, 233, 2, 212, 31, 3, 1, 254, 233, 2, 212, 31, 5, + 1, 242, 164, 31, 3, 1, 242, 164, 31, 5, 1, 245, 41, 2, 150, 31, 3, 1, + 245, 41, 2, 150, 31, 5, 1, 253, 217, 2, 150, 31, 3, 1, 253, 217, 2, 150, + 31, 5, 1, 254, 238, 2, 150, 31, 3, 1, 254, 238, 2, 150, 31, 5, 1, 238, + 91, 249, 76, 31, 3, 1, 238, 91, 249, 76, 31, 5, 1, 197, 2, 166, 31, 3, 1, + 197, 2, 166, 31, 5, 1, 197, 2, 150, 31, 3, 1, 197, 2, 150, 31, 5, 1, 109, + 2, 150, 31, 3, 1, 109, 2, 150, 31, 5, 1, 242, 138, 78, 31, 3, 1, 242, + 138, 78, 31, 5, 1, 242, 138, 109, 2, 150, 31, 3, 1, 242, 138, 109, 2, + 150, 31, 5, 1, 144, 2, 150, 31, 3, 1, 144, 2, 150, 31, 5, 1, 115, 2, 166, + 31, 3, 1, 115, 2, 166, 31, 5, 1, 115, 2, 150, 31, 3, 1, 115, 2, 150, 31, + 5, 1, 115, 2, 42, 116, 31, 3, 1, 115, 2, 42, 116, 31, 5, 1, 253, 164, 2, + 150, 31, 3, 1, 253, 164, 2, 150, 31, 5, 1, 250, 114, 2, 150, 31, 3, 1, + 250, 114, 2, 150, 31, 5, 1, 249, 21, 253, 152, 31, 3, 1, 249, 21, 253, + 152, 31, 5, 1, 249, 21, 249, 97, 31, 3, 1, 249, 21, 249, 97, 31, 5, 1, + 249, 21, 250, 128, 31, 3, 1, 249, 21, 250, 128, 31, 5, 1, 249, 21, 245, + 80, 31, 3, 1, 249, 21, 245, 80, 31, 5, 1, 249, 21, 249, 106, 31, 3, 1, + 249, 21, 249, 106, 31, 5, 1, 249, 21, 249, 111, 31, 3, 1, 249, 21, 249, + 111, 31, 5, 1, 249, 21, 250, 73, 31, 3, 1, 249, 21, 250, 73, 31, 5, 1, + 249, 21, 250, 88, 31, 3, 1, 249, 21, 250, 88, 87, 5, 1, 249, 187, 87, 5, + 1, 249, 191, 87, 5, 1, 249, 236, 87, 5, 1, 253, 93, 87, 5, 1, 249, 133, + 87, 5, 1, 253, 129, 87, 5, 1, 254, 9, 87, 5, 1, 254, 11, 87, 5, 1, 76, + 87, 5, 1, 249, 74, 87, 5, 1, 249, 138, 87, 5, 1, 245, 132, 87, 5, 1, 249, + 179, 87, 5, 1, 253, 115, 87, 5, 1, 249, 254, 87, 5, 1, 253, 140, 87, 5, + 1, 253, 118, 87, 5, 1, 245, 95, 87, 5, 1, 245, 68, 87, 5, 1, 249, 148, + 87, 5, 1, 253, 139, 87, 5, 1, 249, 112, 87, 5, 1, 249, 7, 87, 5, 1, 253, + 215, 87, 5, 1, 249, 11, 87, 5, 1, 249, 155, 87, 5, 1, 245, 117, 87, 5, 1, + 253, 92, 87, 5, 1, 250, 68, 87, 5, 1, 250, 105, 87, 5, 1, 245, 204, 87, + 5, 1, 249, 159, 87, 5, 1, 253, 178, 87, 5, 1, 245, 51, 87, 5, 1, 245, + 161, 87, 5, 1, 249, 140, 87, 5, 1, 254, 60, 87, 5, 1, 249, 101, 87, 147, + 1, 36, 136, 244, 183, 87, 236, 190, 87, 239, 215, 65, 87, 236, 186, 65, + 87, 242, 84, 87, 239, 126, 65, 87, 236, 60, 65, 87, 3, 1, 249, 187, 87, + 3, 1, 249, 191, 87, 3, 1, 249, 236, 87, 3, 1, 253, 93, 87, 3, 1, 249, + 133, 87, 3, 1, 253, 129, 87, 3, 1, 254, 9, 87, 3, 1, 254, 11, 87, 3, 1, + 76, 87, 3, 1, 249, 74, 87, 3, 1, 249, 138, 87, 3, 1, 245, 132, 87, 3, 1, + 249, 179, 87, 3, 1, 253, 115, 87, 3, 1, 249, 254, 87, 3, 1, 253, 140, 87, + 3, 1, 253, 118, 87, 3, 1, 245, 95, 87, 3, 1, 245, 68, 87, 3, 1, 249, 148, + 87, 3, 1, 253, 139, 87, 3, 1, 249, 112, 87, 3, 1, 249, 7, 87, 3, 1, 253, + 215, 87, 3, 1, 249, 11, 87, 3, 1, 249, 155, 87, 3, 1, 245, 117, 87, 3, 1, + 253, 92, 87, 3, 1, 250, 68, 87, 3, 1, 250, 105, 87, 3, 1, 245, 204, 87, + 3, 1, 249, 159, 87, 3, 1, 253, 178, 87, 3, 1, 245, 51, 87, 3, 1, 245, + 161, 87, 3, 1, 249, 140, 87, 3, 1, 254, 60, 87, 3, 1, 249, 101, 87, 3, + 17, 254, 100, 245, 51, 87, 213, 189, 87, 240, 241, 61, 230, 240, 70, 61, + 230, 242, 186, 61, 230, 237, 95, 61, 230, 245, 232, 244, 47, 61, 230, + 245, 232, 243, 121, 61, 230, 242, 37, 61, 230, 244, 57, 61, 230, 244, + 158, 61, 230, 241, 235, 61, 230, 244, 156, 61, 230, 244, 114, 61, 230, + 241, 57, 61, 230, 243, 125, 241, 218, 61, 230, 237, 157, 61, 230, 244, + 46, 247, 238, 61, 230, 241, 84, 241, 173, 61, 230, 244, 27, 61, 230, 243, + 232, 61, 230, 239, 44, 61, 230, 241, 211, 61, 230, 243, 220, 241, 190, + 61, 230, 243, 82, 61, 230, 244, 45, 61, 230, 241, 84, 241, 246, 61, 230, + 248, 189, 254, 88, 248, 196, 61, 230, 252, 56, 61, 230, 246, 250, 61, + 230, 246, 103, 61, 230, 244, 165, 61, 167, 243, 215, 240, 200, 61, 244, + 180, 244, 78, 61, 244, 180, 245, 24, 242, 186, 61, 244, 180, 245, 24, + 242, 162, 61, 244, 180, 245, 24, 241, 30, 61, 244, 180, 242, 222, 61, + 244, 180, 244, 125, 61, 244, 180, 242, 186, 61, 244, 180, 242, 162, 61, + 244, 180, 241, 30, 61, 244, 180, 242, 223, 61, 244, 180, 241, 247, 61, + 244, 180, 242, 176, 112, 242, 181, 61, 244, 180, 243, 221, 61, 236, 191, + 243, 213, 61, 244, 180, 245, 1, 61, 236, 191, 244, 24, 61, 244, 180, 249, + 54, 227, 61, 244, 180, 254, 22, 227, 61, 236, 191, 254, 184, 244, 25, 61, + 167, 156, 227, 61, 1, 244, 224, 61, 1, 250, 154, 61, 1, 243, 134, 61, 1, + 242, 204, 61, 1, 253, 125, 61, 1, 250, 102, 61, 1, 244, 159, 61, 1, 251, + 66, 61, 1, 252, 202, 61, 1, 249, 183, 61, 1, 29, 249, 81, 61, 1, 249, 81, + 61, 1, 242, 179, 61, 1, 29, 249, 147, 61, 1, 249, 147, 61, 1, 29, 249, + 152, 61, 1, 249, 152, 61, 1, 241, 253, 61, 1, 245, 57, 61, 1, 29, 253, + 143, 61, 1, 253, 143, 61, 1, 29, 243, 14, 61, 1, 243, 14, 61, 1, 252, 95, + 61, 1, 245, 170, 61, 1, 245, 6, 61, 1, 250, 86, 61, 17, 241, 10, 42, 250, + 102, 61, 17, 241, 10, 254, 24, 249, 183, 61, 17, 241, 10, 42, 249, 183, + 61, 236, 191, 241, 57, 61, 236, 191, 237, 157, 10, 242, 84, 10, 237, 123, + 10, 239, 126, 65, 10, 1, 242, 221, 10, 1, 67, 2, 244, 237, 47, 10, 1, 67, + 2, 125, 47, 10, 1, 253, 162, 2, 125, 47, 10, 1, 67, 2, 125, 45, 10, 1, + 50, 2, 125, 47, 10, 1, 244, 224, 10, 1, 249, 190, 10, 1, 191, 240, 110, + 10, 1, 252, 199, 10, 1, 248, 122, 10, 1, 245, 115, 10, 1, 251, 56, 10, 1, + 245, 120, 10, 1, 250, 205, 10, 1, 248, 121, 10, 1, 249, 159, 10, 1, 245, + 231, 10, 1, 245, 42, 10, 1, 244, 76, 10, 1, 252, 152, 10, 1, 250, 203, + 10, 1, 250, 82, 10, 1, 253, 58, 10, 1, 250, 135, 10, 1, 243, 133, 10, + 240, 176, 10, 1, 249, 101, 10, 1, 250, 53, 10, 1, 249, 81, 10, 1, 251, + 182, 10, 1, 245, 140, 10, 1, 245, 152, 10, 1, 251, 62, 10, 1, 249, 124, + 10, 1, 67, 239, 189, 10, 1, 249, 91, 10, 239, 12, 10, 238, 197, 10, 239, + 35, 10, 243, 106, 10, 243, 81, 10, 243, 183, 10, 242, 9, 10, 243, 6, 10, + 243, 208, 47, 10, 125, 47, 10, 125, 45, 236, 46, 27, 244, 173, 236, 46, + 27, 121, 236, 46, 27, 114, 236, 46, 27, 153, 236, 46, 27, 163, 236, 46, + 27, 168, 236, 46, 27, 169, 236, 46, 27, 179, 236, 46, 27, 176, 236, 46, + 27, 178, 10, 240, 248, 57, 10, 241, 51, 237, 114, 10, 238, 133, 237, 114, + 10, 249, 48, 240, 218, 244, 178, 10, 1, 240, 228, 249, 190, 10, 1, 240, + 228, 250, 53, 10, 1, 236, 181, 244, 224, 10, 1, 67, 244, 144, 10, 1, 67, + 2, 249, 57, 125, 47, 10, 1, 67, 2, 249, 57, 125, 45, 10, 1, 249, 10, 242, + 221, 10, 1, 249, 10, 125, 244, 224, 10, 1, 249, 10, 125, 249, 124, 10, 1, + 115, 2, 125, 47, 10, 1, 249, 10, 125, 249, 91, 10, 1, 248, 145, 10, 1, + 242, 41, 10, 1, 246, 19, 10, 1, 191, 2, 244, 183, 10, 1, 191, 2, 244, + 171, 248, 253, 53, 237, 121, 10, 1, 249, 155, 10, 1, 244, 112, 10, 1, + 243, 42, 10, 1, 81, 2, 125, 47, 10, 1, 81, 2, 225, 248, 253, 55, 47, 10, + 1, 247, 203, 10, 1, 246, 118, 10, 1, 81, 2, 244, 171, 248, 253, 47, 10, + 1, 244, 111, 10, 1, 240, 177, 10, 1, 246, 83, 10, 1, 253, 180, 2, 244, + 183, 10, 1, 253, 180, 2, 49, 45, 10, 1, 253, 180, 2, 49, 244, 188, 18, 3, + 250, 82, 10, 1, 243, 79, 10, 1, 241, 136, 10, 1, 250, 230, 10, 1, 253, + 180, 2, 244, 171, 248, 253, 53, 237, 121, 10, 1, 253, 180, 2, 249, 28, + 248, 253, 47, 10, 1, 248, 26, 10, 1, 253, 105, 2, 3, 186, 10, 1, 253, + 105, 2, 244, 183, 10, 1, 253, 105, 2, 49, 45, 10, 1, 253, 105, 2, 3, 255, + 44, 45, 10, 1, 253, 105, 2, 49, 244, 188, 18, 49, 47, 10, 1, 253, 105, 2, + 225, 248, 253, 47, 10, 1, 251, 119, 10, 1, 253, 105, 2, 249, 28, 248, + 253, 47, 10, 1, 214, 2, 49, 244, 188, 18, 49, 47, 10, 1, 214, 2, 244, + 171, 248, 253, 45, 10, 1, 214, 2, 244, 171, 248, 253, 244, 188, 18, 244, + 171, 248, 253, 47, 10, 1, 253, 108, 2, 225, 248, 253, 45, 10, 1, 253, + 108, 2, 244, 171, 248, 253, 47, 10, 1, 253, 122, 2, 244, 171, 248, 253, + 47, 10, 1, 253, 117, 2, 244, 171, 248, 253, 47, 10, 1, 240, 228, 249, + 101, 10, 1, 253, 111, 2, 49, 247, 99, 45, 10, 1, 253, 111, 2, 49, 45, 10, + 1, 252, 251, 10, 1, 253, 111, 2, 244, 171, 248, 253, 45, 10, 1, 244, 29, + 10, 1, 253, 120, 2, 49, 47, 10, 1, 253, 120, 2, 244, 171, 248, 253, 47, + 10, 1, 243, 187, 10, 1, 248, 110, 249, 81, 10, 1, 253, 87, 2, 244, 183, + 10, 1, 253, 87, 2, 49, 47, 10, 1, 253, 244, 10, 1, 253, 87, 2, 244, 171, + 248, 253, 45, 10, 1, 251, 23, 10, 1, 253, 194, 2, 244, 183, 10, 1, 243, + 245, 10, 1, 253, 194, 2, 225, 248, 253, 45, 10, 1, 246, 162, 10, 1, 253, + 194, 2, 244, 171, 248, 253, 47, 10, 1, 155, 2, 3, 186, 10, 1, 155, 2, 49, + 47, 10, 1, 155, 2, 244, 171, 248, 253, 47, 10, 1, 155, 2, 244, 171, 248, + 253, 45, 10, 1, 177, 2, 49, 45, 10, 1, 177, 241, 177, 10, 1, 244, 61, 10, + 1, 177, 2, 244, 183, 10, 1, 177, 2, 244, 171, 248, 253, 47, 10, 1, 188, + 236, 97, 10, 1, 244, 240, 2, 49, 47, 10, 1, 188, 2, 50, 47, 10, 1, 188, + 245, 99, 10, 1, 188, 249, 79, 2, 125, 47, 10, 1, 191, 241, 26, 245, 99, + 10, 1, 253, 162, 2, 244, 183, 10, 1, 240, 217, 253, 142, 10, 1, 253, 142, + 10, 1, 84, 10, 1, 253, 124, 10, 1, 240, 217, 253, 124, 10, 1, 253, 162, + 2, 225, 248, 253, 47, 10, 1, 253, 214, 10, 1, 244, 227, 249, 91, 10, 1, + 50, 2, 244, 182, 10, 1, 50, 2, 3, 186, 10, 1, 253, 162, 2, 49, 47, 10, 1, + 70, 10, 1, 50, 2, 244, 171, 248, 253, 45, 10, 1, 50, 241, 110, 10, 1, 50, + 242, 139, 2, 125, 47, 10, 213, 189, 10, 1, 253, 163, 10, 3, 249, 10, 17, + 253, 108, 2, 155, 2, 67, 239, 189, 10, 3, 249, 10, 17, 253, 120, 2, 155, + 2, 67, 239, 189, 10, 3, 249, 10, 43, 46, 12, 10, 3, 249, 10, 155, 244, + 224, 10, 3, 249, 10, 245, 115, 10, 3, 249, 10, 244, 171, 244, 215, 10, 3, + 249, 10, 245, 42, 10, 253, 127, 137, 245, 250, 10, 245, 45, 137, 254, + 181, 254, 241, 246, 178, 10, 3, 249, 10, 241, 4, 244, 173, 10, 3, 249, + 10, 242, 44, 236, 226, 244, 173, 10, 3, 249, 10, 240, 228, 251, 61, 137, + 245, 120, 10, 3, 249, 10, 43, 35, 12, 10, 3, 253, 113, 245, 42, 10, 3, + 249, 10, 243, 207, 10, 3, 249, 124, 10, 3, 249, 91, 10, 3, 249, 10, 249, + 91, 10, 3, 249, 10, 245, 152, 10, 245, 162, 137, 241, 252, 10, 244, 216, + 242, 100, 253, 113, 189, 10, 244, 216, 242, 100, 249, 10, 189, 10, 241, + 4, 249, 10, 249, 71, 2, 243, 113, 241, 46, 10, 3, 253, 113, 245, 140, + 239, 141, 236, 46, 27, 244, 173, 239, 141, 236, 46, 27, 121, 239, 141, + 236, 46, 27, 114, 239, 141, 236, 46, 27, 153, 239, 141, 236, 46, 27, 163, + 239, 141, 236, 46, 27, 168, 239, 141, 236, 46, 27, 169, 239, 141, 236, + 46, 27, 179, 239, 141, 236, 46, 27, 176, 239, 141, 236, 46, 27, 178, 10, + 1, 215, 2, 49, 45, 10, 1, 253, 107, 2, 49, 45, 10, 1, 244, 190, 2, 49, + 45, 10, 25, 243, 1, 237, 124, 10, 25, 243, 1, 236, 156, 249, 148, 118, + 253, 127, 137, 238, 41, 118, 236, 210, 213, 189, 118, 238, 129, 213, 189, + 118, 236, 210, 242, 81, 118, 238, 129, 242, 81, 118, 146, 242, 81, 118, + 244, 214, 242, 166, 244, 172, 118, 244, 214, 242, 166, 184, 118, 236, + 210, 244, 214, 242, 166, 244, 172, 118, 238, 129, 244, 214, 242, 166, + 184, 118, 236, 90, 118, 239, 184, 241, 226, 118, 239, 184, 238, 32, 118, + 239, 184, 236, 245, 118, 236, 60, 65, 118, 1, 242, 195, 118, 1, 236, 181, + 242, 195, 118, 1, 246, 24, 118, 1, 243, 122, 118, 1, 246, 134, 238, 141, + 118, 1, 241, 133, 118, 1, 240, 228, 243, 80, 245, 172, 118, 1, 253, 125, + 118, 1, 249, 124, 118, 1, 245, 231, 118, 1, 246, 173, 118, 1, 248, 120, + 118, 1, 252, 203, 238, 141, 118, 1, 248, 202, 118, 1, 253, 48, 253, 125, + 118, 1, 247, 27, 118, 1, 241, 196, 118, 1, 251, 235, 118, 1, 249, 152, + 118, 1, 239, 234, 118, 1, 29, 239, 234, 118, 1, 70, 118, 1, 253, 143, + 118, 1, 224, 253, 143, 118, 1, 244, 68, 118, 1, 248, 0, 118, 1, 245, 172, + 118, 1, 245, 6, 118, 1, 252, 197, 118, 242, 184, 47, 118, 242, 184, 45, + 118, 242, 184, 241, 18, 118, 242, 194, 47, 118, 242, 194, 45, 118, 242, + 194, 241, 18, 118, 245, 169, 47, 118, 245, 169, 45, 118, 242, 118, 245, + 234, 236, 182, 118, 242, 118, 245, 234, 239, 251, 118, 245, 106, 47, 118, + 245, 106, 45, 118, 237, 65, 241, 18, 118, 245, 82, 47, 118, 245, 82, 45, + 118, 244, 67, 118, 239, 216, 227, 118, 238, 51, 118, 239, 77, 118, 225, + 55, 248, 253, 47, 118, 225, 55, 248, 253, 45, 118, 244, 171, 248, 253, + 47, 118, 244, 171, 248, 253, 45, 118, 241, 2, 217, 47, 118, 241, 2, 217, + 45, 118, 243, 233, 118, 240, 4, 161, 1, 249, 68, 161, 1, 238, 59, 161, 1, + 244, 2, 161, 1, 249, 115, 161, 1, 249, 188, 161, 1, 238, 28, 161, 1, 243, + 181, 161, 1, 243, 28, 161, 1, 244, 136, 161, 1, 243, 216, 161, 1, 243, + 104, 161, 1, 241, 138, 161, 1, 245, 8, 161, 1, 243, 198, 161, 1, 243, + 120, 161, 1, 238, 10, 161, 1, 244, 72, 161, 1, 238, 198, 161, 1, 239, + 114, 161, 1, 239, 101, 161, 1, 249, 180, 161, 1, 239, 60, 161, 1, 239, + 34, 161, 1, 237, 180, 161, 1, 248, 143, 161, 1, 245, 107, 161, 1, 247, + 29, 161, 1, 242, 33, 161, 1, 250, 123, 161, 1, 242, 11, 161, 1, 241, 251, + 161, 1, 241, 132, 161, 1, 76, 161, 1, 253, 190, 161, 1, 245, 241, 161, 1, + 241, 176, 161, 1, 244, 44, 161, 1, 244, 143, 161, 239, 246, 161, 237, + 171, 161, 240, 88, 161, 238, 2, 161, 240, 190, 161, 238, 38, 161, 240, + 63, 161, 238, 8, 161, 240, 128, 161, 238, 37, 161, 243, 6, 161, 1, 249, + 150, 72, 25, 236, 49, 72, 25, 238, 90, 72, 25, 239, 148, 72, 1, 64, 72, + 1, 253, 95, 72, 1, 69, 72, 1, 253, 96, 72, 1, 84, 72, 1, 253, 104, 72, 1, + 152, 135, 72, 1, 152, 148, 72, 1, 242, 110, 70, 72, 1, 207, 70, 72, 1, + 70, 72, 1, 253, 106, 72, 1, 242, 110, 78, 72, 1, 207, 78, 72, 1, 78, 72, + 1, 253, 109, 72, 1, 208, 72, 1, 249, 24, 72, 1, 253, 100, 72, 1, 249, 36, + 72, 1, 249, 13, 72, 1, 253, 115, 72, 1, 249, 11, 72, 1, 253, 118, 72, 1, + 249, 45, 72, 1, 249, 38, 72, 1, 249, 22, 72, 1, 244, 195, 72, 1, 249, 27, + 72, 1, 244, 200, 72, 1, 249, 37, 72, 1, 253, 91, 72, 1, 249, 19, 72, 1, + 253, 93, 72, 1, 249, 40, 72, 1, 253, 94, 72, 1, 245, 150, 72, 1, 253, 90, + 72, 1, 249, 29, 72, 1, 253, 110, 72, 1, 249, 39, 72, 1, 221, 72, 1, 216, + 72, 1, 253, 92, 72, 1, 249, 50, 72, 1, 253, 103, 72, 1, 249, 49, 72, 1, + 245, 27, 72, 1, 253, 161, 72, 1, 249, 7, 72, 1, 249, 34, 72, 1, 253, 97, + 72, 1, 253, 88, 72, 25, 242, 149, 72, 25, 238, 117, 72, 38, 25, 253, 95, + 72, 38, 25, 69, 72, 38, 25, 253, 96, 72, 38, 25, 84, 72, 38, 25, 253, + 104, 72, 38, 25, 152, 135, 72, 38, 25, 152, 253, 130, 72, 38, 25, 242, + 110, 70, 72, 38, 25, 207, 70, 72, 38, 25, 70, 72, 38, 25, 253, 106, 72, + 38, 25, 242, 110, 78, 72, 38, 25, 207, 78, 72, 38, 25, 78, 72, 38, 25, + 253, 109, 72, 25, 240, 220, 72, 253, 245, 72, 242, 188, 25, 242, 43, 72, + 242, 188, 25, 238, 170, 72, 244, 201, 240, 203, 72, 244, 196, 240, 203, + 72, 1, 253, 213, 72, 1, 245, 124, 72, 1, 245, 97, 72, 1, 253, 129, 72, 1, + 243, 84, 72, 1, 249, 160, 72, 1, 253, 153, 72, 1, 249, 184, 72, 1, 152, + 253, 130, 72, 1, 152, 253, 146, 72, 38, 25, 152, 148, 72, 38, 25, 152, + 253, 146, 72, 242, 205, 72, 42, 242, 205, 72, 27, 244, 173, 72, 27, 121, + 72, 27, 114, 72, 27, 153, 72, 27, 163, 72, 27, 168, 72, 27, 169, 72, 27, + 179, 72, 27, 176, 72, 27, 178, 72, 236, 60, 57, 91, 25, 236, 49, 91, 25, + 238, 90, 91, 25, 239, 148, 91, 1, 64, 91, 1, 253, 95, 91, 1, 69, 91, 1, + 253, 96, 91, 1, 84, 91, 1, 253, 104, 91, 1, 152, 135, 91, 1, 152, 148, + 91, 1, 70, 91, 1, 253, 106, 91, 1, 78, 91, 1, 253, 109, 91, 1, 208, 91, + 1, 249, 24, 91, 1, 253, 100, 91, 1, 249, 36, 91, 1, 249, 13, 91, 1, 253, + 115, 91, 1, 249, 11, 91, 1, 253, 118, 91, 1, 249, 45, 91, 1, 249, 38, 91, + 1, 249, 22, 91, 1, 244, 195, 91, 1, 249, 27, 91, 1, 244, 200, 91, 1, 249, + 37, 91, 1, 253, 91, 91, 1, 249, 19, 91, 1, 253, 93, 91, 1, 249, 40, 91, + 1, 253, 94, 91, 1, 253, 90, 91, 1, 249, 29, 91, 1, 253, 110, 91, 1, 249, + 39, 91, 1, 221, 91, 1, 216, 91, 1, 253, 92, 91, 1, 253, 103, 91, 1, 249, + 7, 91, 1, 249, 34, 91, 1, 253, 97, 91, 1, 253, 88, 91, 25, 242, 149, 91, + 38, 25, 253, 95, 91, 38, 25, 69, 91, 38, 25, 253, 96, 91, 38, 25, 84, 91, + 38, 25, 253, 104, 91, 38, 25, 152, 135, 91, 38, 25, 152, 253, 130, 91, + 38, 25, 70, 91, 38, 25, 253, 106, 91, 38, 25, 78, 91, 38, 25, 253, 109, + 91, 25, 240, 220, 91, 254, 230, 242, 106, 65, 91, 1, 249, 50, 91, 1, 249, + 160, 91, 1, 249, 184, 91, 1, 152, 253, 130, 91, 1, 152, 253, 146, 91, 38, + 25, 152, 148, 91, 38, 25, 152, 253, 146, 91, 27, 244, 173, 91, 27, 121, + 91, 27, 114, 91, 27, 153, 91, 27, 163, 91, 27, 168, 91, 27, 169, 91, 27, + 179, 91, 27, 176, 91, 27, 178, 91, 1, 254, 253, 2, 242, 65, 238, 109, 91, + 1, 254, 253, 2, 149, 238, 109, 91, 245, 40, 65, 91, 245, 40, 57, 91, 239, + 147, 238, 103, 121, 91, 239, 147, 238, 103, 114, 91, 239, 147, 238, 103, + 153, 91, 239, 147, 238, 103, 163, 91, 239, 147, 238, 103, 253, 101, 251, + 190, 249, 166, 253, 173, 236, 95, 91, 239, 147, 237, 0, 239, 162, 91, + 241, 59, 113, 25, 243, 44, 113, 1, 64, 113, 1, 253, 95, 113, 1, 69, 113, + 1, 253, 96, 113, 1, 84, 113, 1, 253, 104, 113, 1, 253, 126, 113, 1, 253, + 106, 113, 1, 253, 119, 113, 1, 253, 109, 113, 1, 208, 113, 1, 249, 24, + 113, 1, 253, 100, 113, 1, 249, 36, 113, 1, 249, 13, 113, 1, 253, 115, + 113, 1, 249, 11, 113, 1, 253, 118, 113, 1, 249, 45, 113, 1, 249, 38, 113, + 1, 249, 22, 113, 1, 244, 195, 113, 1, 249, 27, 113, 1, 244, 200, 113, 1, + 249, 37, 113, 1, 253, 91, 113, 1, 249, 19, 113, 1, 253, 93, 113, 1, 249, + 40, 113, 1, 253, 94, 113, 1, 253, 90, 113, 1, 249, 29, 113, 1, 253, 110, + 113, 1, 249, 39, 113, 1, 221, 113, 1, 216, 113, 1, 253, 92, 113, 1, 253, + 103, 113, 1, 249, 49, 113, 1, 253, 161, 113, 1, 249, 7, 113, 1, 253, 97, + 113, 1, 253, 88, 113, 25, 242, 149, 113, 38, 25, 253, 95, 113, 38, 25, + 69, 113, 38, 25, 253, 96, 113, 38, 25, 84, 113, 38, 25, 253, 104, 113, + 38, 25, 253, 126, 113, 38, 25, 253, 106, 113, 38, 25, 253, 119, 113, 38, + 25, 253, 109, 113, 25, 240, 220, 113, 1, 245, 124, 113, 1, 245, 97, 113, + 1, 253, 129, 113, 1, 249, 50, 113, 1, 253, 153, 113, 27, 244, 173, 113, + 27, 121, 113, 27, 114, 113, 27, 153, 113, 27, 163, 113, 27, 168, 113, 27, + 169, 113, 27, 179, 113, 27, 176, 113, 27, 178, 113, 244, 121, 113, 243, + 25, 113, 251, 114, 113, 252, 244, 113, 255, 6, 244, 19, 106, 25, 236, 49, + 106, 25, 238, 90, 106, 25, 239, 148, 106, 1, 64, 106, 1, 253, 95, 106, 1, + 69, 106, 1, 253, 96, 106, 1, 84, 106, 1, 253, 104, 106, 1, 152, 135, 106, + 38, 242, 110, 70, 106, 1, 70, 106, 1, 253, 106, 106, 38, 242, 110, 78, + 106, 1, 78, 106, 1, 253, 109, 106, 1, 208, 106, 1, 249, 24, 106, 1, 253, + 100, 106, 1, 249, 36, 106, 1, 249, 13, 106, 1, 253, 115, 106, 1, 249, 11, + 106, 1, 253, 118, 106, 1, 249, 45, 106, 1, 249, 38, 106, 1, 249, 22, 106, + 1, 244, 195, 106, 1, 249, 27, 106, 1, 244, 200, 106, 1, 249, 37, 106, 1, + 253, 91, 106, 1, 249, 19, 106, 1, 253, 93, 106, 1, 249, 40, 106, 1, 253, + 94, 106, 1, 253, 90, 106, 1, 249, 29, 106, 1, 253, 110, 106, 1, 249, 39, + 106, 1, 221, 106, 1, 216, 106, 1, 253, 92, 106, 1, 253, 103, 106, 1, 249, + 49, 106, 1, 253, 161, 106, 1, 249, 7, 106, 1, 249, 34, 106, 1, 253, 97, + 106, 1, 253, 88, 106, 25, 242, 149, 106, 25, 238, 117, 106, 38, 25, 253, + 95, 106, 38, 25, 69, 106, 38, 25, 253, 96, 106, 38, 25, 84, 106, 38, 25, + 253, 104, 106, 38, 25, 152, 135, 106, 38, 25, 152, 253, 130, 106, 38, 25, + 242, 110, 70, 106, 38, 25, 70, 106, 38, 25, 253, 106, 106, 38, 25, 242, + 110, 78, 106, 38, 25, 78, 106, 38, 25, 253, 109, 106, 25, 240, 220, 106, + 253, 245, 106, 1, 152, 253, 130, 106, 27, 244, 173, 106, 27, 121, 106, + 27, 114, 106, 27, 153, 106, 27, 163, 106, 27, 168, 106, 27, 169, 106, 27, + 179, 106, 27, 176, 106, 27, 178, 105, 25, 236, 49, 105, 25, 238, 90, 105, + 25, 239, 148, 105, 1, 64, 105, 1, 253, 95, 105, 1, 69, 105, 1, 253, 96, + 105, 1, 84, 105, 1, 253, 104, 105, 1, 152, 135, 105, 1, 152, 148, 105, 1, + 70, 105, 1, 253, 106, 105, 1, 78, 105, 1, 253, 109, 105, 1, 208, 105, 1, + 249, 24, 105, 1, 253, 100, 105, 1, 249, 36, 105, 1, 249, 13, 105, 1, 253, + 115, 105, 1, 249, 11, 105, 1, 253, 118, 105, 1, 249, 45, 105, 1, 249, 38, + 105, 1, 249, 22, 105, 1, 244, 195, 105, 1, 249, 27, 105, 1, 244, 200, + 105, 1, 249, 37, 105, 1, 253, 91, 105, 1, 249, 19, 105, 1, 253, 93, 105, + 1, 249, 40, 105, 1, 253, 94, 105, 1, 253, 90, 105, 1, 249, 29, 105, 1, + 253, 110, 105, 1, 249, 39, 105, 1, 221, 105, 1, 216, 105, 1, 253, 92, + 105, 1, 253, 103, 105, 1, 249, 49, 105, 1, 249, 7, 105, 1, 249, 34, 105, + 1, 253, 97, 105, 1, 253, 88, 105, 25, 242, 149, 105, 25, 238, 117, 105, + 38, 25, 253, 95, 105, 38, 25, 69, 105, 38, 25, 253, 96, 105, 38, 25, 84, + 105, 38, 25, 253, 104, 105, 38, 25, 152, 135, 105, 38, 25, 70, 105, 38, + 25, 253, 106, 105, 38, 25, 78, 105, 38, 25, 253, 109, 105, 25, 240, 220, + 105, 254, 227, 242, 106, 65, 105, 254, 230, 242, 106, 65, 105, 1, 249, + 50, 105, 1, 249, 160, 105, 1, 249, 184, 105, 1, 152, 253, 130, 105, 1, + 152, 253, 146, 105, 27, 244, 173, 105, 27, 121, 105, 27, 114, 105, 27, + 153, 105, 27, 163, 105, 27, 168, 105, 27, 169, 105, 27, 179, 105, 27, + 176, 105, 27, 178, 105, 241, 59, 142, 25, 238, 90, 142, 25, 239, 148, + 142, 1, 64, 142, 1, 253, 95, 142, 1, 69, 142, 1, 253, 96, 142, 1, 84, + 142, 1, 253, 104, 142, 1, 70, 142, 1, 253, 126, 142, 1, 253, 106, 142, 1, + 78, 142, 1, 253, 119, 142, 1, 253, 109, 142, 1, 208, 142, 1, 249, 13, + 142, 1, 253, 115, 142, 1, 253, 118, 142, 1, 249, 38, 142, 1, 249, 22, + 142, 1, 249, 37, 142, 1, 253, 91, 142, 1, 253, 94, 142, 1, 245, 150, 142, + 1, 253, 90, 142, 1, 221, 142, 1, 216, 142, 1, 253, 92, 142, 1, 249, 50, + 142, 1, 253, 103, 142, 1, 249, 49, 142, 1, 245, 27, 142, 1, 253, 161, + 142, 1, 249, 34, 142, 1, 253, 97, 142, 1, 253, 88, 142, 38, 25, 253, 95, + 142, 38, 25, 69, 142, 38, 25, 253, 96, 142, 38, 25, 84, 142, 38, 25, 253, + 104, 142, 38, 25, 70, 142, 38, 25, 253, 126, 142, 38, 25, 253, 106, 142, + 38, 25, 78, 142, 38, 25, 253, 119, 142, 38, 25, 253, 109, 142, 25, 240, + 220, 142, 254, 230, 242, 106, 65, 142, 27, 121, 142, 27, 114, 142, 27, + 153, 142, 27, 163, 142, 27, 168, 142, 27, 169, 142, 27, 179, 142, 27, + 176, 142, 27, 178, 142, 83, 249, 18, 142, 83, 253, 101, 239, 150, 142, + 83, 253, 101, 238, 104, 104, 25, 236, 49, 104, 25, 238, 90, 104, 25, 239, + 148, 104, 1, 64, 104, 1, 253, 95, 104, 1, 69, 104, 1, 253, 96, 104, 1, + 84, 104, 1, 253, 104, 104, 1, 152, 135, 104, 1, 152, 148, 104, 1, 70, + 104, 1, 253, 126, 104, 1, 253, 106, 104, 1, 78, 104, 1, 253, 119, 104, 1, + 253, 109, 104, 1, 208, 104, 1, 249, 24, 104, 1, 253, 100, 104, 1, 249, + 36, 104, 1, 249, 13, 104, 1, 253, 115, 104, 1, 249, 11, 104, 1, 253, 118, + 104, 1, 249, 45, 104, 1, 249, 38, 104, 1, 249, 22, 104, 1, 244, 195, 104, + 1, 249, 27, 104, 1, 244, 200, 104, 1, 249, 37, 104, 1, 253, 91, 104, 1, + 249, 19, 104, 1, 253, 93, 104, 1, 249, 40, 104, 1, 253, 94, 104, 1, 253, + 90, 104, 1, 249, 29, 104, 1, 253, 110, 104, 1, 249, 39, 104, 1, 221, 104, + 1, 216, 104, 1, 253, 92, 104, 1, 249, 50, 104, 1, 253, 103, 104, 1, 249, + 49, 104, 1, 253, 161, 104, 1, 249, 7, 104, 1, 249, 34, 104, 1, 253, 97, + 104, 1, 253, 88, 104, 38, 25, 253, 95, 104, 38, 25, 69, 104, 38, 25, 253, + 96, 104, 38, 25, 84, 104, 38, 25, 253, 104, 104, 38, 25, 152, 135, 104, + 38, 25, 152, 253, 130, 104, 38, 25, 70, 104, 38, 25, 253, 126, 104, 38, + 25, 253, 106, 104, 38, 25, 78, 104, 38, 25, 253, 119, 104, 38, 25, 253, + 109, 104, 25, 240, 220, 104, 242, 106, 65, 104, 254, 227, 242, 106, 65, + 104, 1, 152, 253, 130, 104, 1, 152, 253, 146, 104, 27, 244, 173, 104, 27, + 121, 104, 27, 114, 104, 27, 153, 104, 27, 163, 104, 27, 168, 104, 27, + 169, 104, 27, 179, 104, 27, 176, 104, 27, 178, 102, 25, 238, 90, 102, 25, + 239, 148, 102, 1, 64, 102, 1, 253, 95, 102, 1, 69, 102, 1, 253, 96, 102, + 1, 84, 102, 1, 253, 104, 102, 1, 152, 135, 102, 1, 152, 148, 102, 1, 70, + 102, 1, 253, 126, 102, 1, 253, 106, 102, 1, 78, 102, 1, 253, 119, 102, 1, + 253, 109, 102, 1, 208, 102, 1, 249, 24, 102, 1, 253, 100, 102, 1, 249, + 36, 102, 1, 249, 13, 102, 1, 253, 115, 102, 1, 249, 11, 102, 1, 253, 118, + 102, 1, 249, 45, 102, 1, 249, 38, 102, 1, 249, 22, 102, 1, 244, 195, 102, + 1, 249, 27, 102, 1, 244, 200, 102, 1, 249, 37, 102, 1, 253, 91, 102, 1, + 249, 19, 102, 1, 253, 93, 102, 1, 249, 40, 102, 1, 253, 94, 102, 1, 253, + 90, 102, 1, 249, 29, 102, 1, 253, 110, 102, 1, 249, 39, 102, 1, 221, 102, + 1, 216, 102, 1, 253, 92, 102, 1, 249, 50, 102, 1, 253, 103, 102, 1, 249, + 49, 102, 1, 253, 161, 102, 1, 249, 7, 102, 1, 249, 34, 102, 1, 253, 97, + 102, 1, 253, 88, 102, 38, 25, 253, 95, 102, 38, 25, 69, 102, 38, 25, 253, + 96, 102, 38, 25, 84, 102, 38, 25, 253, 104, 102, 38, 25, 152, 135, 102, + 38, 25, 152, 253, 130, 102, 38, 25, 70, 102, 38, 25, 253, 126, 102, 38, + 25, 253, 106, 102, 38, 25, 78, 102, 38, 25, 253, 119, 102, 38, 25, 253, + 109, 102, 25, 240, 220, 102, 242, 106, 65, 102, 254, 227, 242, 106, 65, + 102, 1, 253, 153, 102, 1, 152, 253, 130, 102, 1, 152, 253, 146, 102, 27, + 244, 173, 102, 27, 121, 102, 27, 114, 102, 27, 153, 102, 27, 163, 102, + 27, 168, 102, 27, 169, 102, 27, 179, 102, 27, 176, 102, 27, 178, 107, 25, + 238, 90, 107, 25, 239, 148, 107, 1, 64, 107, 1, 253, 95, 107, 1, 69, 107, + 1, 253, 96, 107, 1, 84, 107, 1, 253, 104, 107, 1, 152, 135, 107, 1, 152, + 148, 107, 1, 70, 107, 1, 253, 126, 107, 1, 253, 106, 107, 1, 78, 107, 1, + 253, 119, 107, 1, 253, 109, 107, 1, 208, 107, 1, 249, 24, 107, 1, 253, + 100, 107, 1, 249, 36, 107, 1, 249, 13, 107, 1, 253, 115, 107, 1, 249, 11, + 107, 1, 253, 118, 107, 1, 249, 45, 107, 1, 249, 38, 107, 1, 249, 22, 107, + 1, 244, 195, 107, 1, 249, 27, 107, 1, 244, 200, 107, 1, 249, 37, 107, 1, + 253, 91, 107, 1, 249, 19, 107, 1, 253, 93, 107, 1, 249, 40, 107, 1, 253, + 94, 107, 1, 253, 90, 107, 1, 249, 29, 107, 1, 253, 110, 107, 1, 249, 39, + 107, 1, 221, 107, 1, 216, 107, 1, 253, 92, 107, 1, 249, 50, 107, 1, 253, + 103, 107, 1, 249, 49, 107, 1, 245, 27, 107, 1, 253, 161, 107, 1, 249, 7, + 107, 1, 249, 34, 107, 1, 253, 97, 107, 1, 253, 88, 107, 38, 25, 253, 95, + 107, 38, 25, 69, 107, 38, 25, 253, 96, 107, 38, 25, 84, 107, 38, 25, 253, + 104, 107, 38, 25, 152, 135, 107, 38, 25, 70, 107, 38, 25, 253, 126, 107, + 38, 25, 253, 106, 107, 38, 25, 78, 107, 38, 25, 253, 119, 107, 38, 25, + 253, 109, 107, 25, 240, 220, 107, 254, 230, 242, 106, 65, 107, 1, 152, + 253, 130, 107, 1, 152, 253, 146, 107, 27, 244, 173, 107, 27, 121, 107, + 27, 114, 107, 27, 153, 107, 27, 163, 107, 27, 168, 107, 27, 169, 107, 27, + 179, 107, 27, 176, 107, 27, 178, 103, 25, 236, 243, 103, 25, 238, 72, + 103, 1, 241, 106, 103, 1, 239, 245, 103, 1, 239, 247, 103, 1, 238, 168, + 103, 1, 241, 188, 103, 1, 240, 90, 103, 1, 242, 46, 103, 1, 240, 191, + 103, 1, 239, 33, 103, 1, 238, 21, 103, 1, 239, 29, 103, 1, 238, 16, 103, + 1, 241, 156, 103, 1, 240, 64, 103, 1, 239, 248, 103, 1, 241, 232, 103, 1, + 240, 132, 103, 1, 240, 1, 103, 1, 237, 115, 239, 221, 103, 1, 236, 189, + 239, 221, 103, 1, 237, 115, 239, 183, 103, 1, 236, 189, 239, 183, 103, 1, + 241, 189, 236, 219, 103, 1, 241, 5, 239, 183, 103, 1, 237, 115, 239, 206, + 103, 1, 236, 189, 239, 206, 103, 1, 237, 115, 239, 185, 103, 1, 236, 189, + 239, 185, 103, 1, 241, 34, 236, 219, 103, 1, 241, 34, 240, 158, 236, 146, + 103, 1, 241, 5, 239, 185, 103, 1, 237, 115, 238, 163, 103, 1, 236, 189, + 238, 163, 103, 1, 237, 115, 238, 118, 103, 1, 236, 189, 238, 118, 103, 1, + 238, 123, 239, 226, 103, 1, 241, 5, 238, 118, 103, 1, 237, 115, 239, 241, + 103, 1, 236, 189, 239, 241, 103, 1, 237, 115, 239, 179, 103, 1, 236, 189, + 239, 179, 103, 1, 241, 16, 239, 226, 103, 1, 241, 5, 239, 179, 103, 1, + 237, 115, 239, 227, 103, 1, 236, 189, 239, 227, 103, 1, 237, 115, 239, + 177, 103, 1, 236, 189, 239, 177, 103, 1, 240, 112, 103, 1, 250, 136, 239, + 177, 103, 1, 240, 197, 103, 1, 240, 150, 103, 1, 241, 16, 239, 223, 103, + 1, 240, 192, 103, 1, 241, 34, 239, 194, 103, 1, 238, 123, 239, 194, 103, + 1, 241, 16, 239, 194, 103, 1, 240, 85, 103, 1, 238, 123, 239, 223, 103, + 1, 240, 72, 103, 25, 237, 172, 103, 38, 25, 236, 199, 103, 38, 25, 245, + 26, 236, 211, 103, 38, 25, 249, 62, 236, 211, 103, 38, 25, 245, 26, 238, + 146, 103, 38, 25, 249, 62, 238, 146, 103, 38, 25, 245, 26, 237, 161, 103, + 38, 25, 249, 62, 237, 161, 103, 38, 25, 235, 251, 103, 38, 25, 239, 222, + 103, 38, 25, 249, 62, 239, 222, 103, 38, 25, 247, 38, 246, 104, 103, 38, + 25, 241, 23, 254, 14, 236, 199, 103, 38, 25, 241, 23, 254, 14, 249, 62, + 236, 199, 103, 38, 25, 241, 23, 254, 14, 236, 62, 103, 38, 25, 236, 62, + 103, 38, 25, 249, 62, 235, 251, 103, 38, 25, 249, 62, 236, 62, 103, 236, + 191, 237, 73, 95, 92, 254, 249, 251, 141, 95, 92, 253, 198, 247, 30, 95, + 92, 253, 198, 243, 189, 95, 92, 253, 198, 243, 190, 95, 92, 253, 198, + 247, 33, 95, 92, 253, 198, 240, 148, 95, 92, 254, 156, 252, 20, 95, 92, + 253, 237, 246, 45, 95, 92, 253, 237, 243, 63, 95, 92, 253, 237, 243, 61, + 95, 92, 254, 225, 253, 159, 95, 92, 253, 237, 246, 52, 95, 92, 255, 0, + 248, 194, 95, 92, 254, 240, 243, 59, 95, 92, 139, 244, 26, 95, 92, 253, + 188, 245, 47, 95, 92, 253, 188, 237, 76, 95, 92, 253, 188, 240, 139, 95, + 92, 254, 243, 252, 12, 95, 92, 254, 240, 250, 212, 95, 92, 139, 252, 194, + 95, 92, 253, 188, 244, 119, 95, 92, 253, 188, 242, 34, 95, 92, 253, 188, + 244, 118, 95, 92, 254, 243, 253, 112, 95, 92, 255, 3, 241, 108, 95, 92, + 255, 20, 252, 70, 95, 92, 253, 230, 244, 36, 95, 92, 254, 232, 253, 153, + 95, 92, 253, 230, 247, 243, 95, 92, 254, 232, 250, 251, 95, 92, 253, 230, + 240, 157, 95, 92, 255, 15, 221, 95, 92, 255, 0, 253, 41, 95, 92, 255, 21, + 252, 137, 95, 92, 253, 170, 95, 92, 254, 234, 245, 131, 95, 92, 253, 229, + 95, 92, 255, 26, 248, 169, 95, 92, 254, 225, 248, 51, 95, 92, 254, 225, + 248, 47, 95, 92, 254, 225, 252, 178, 95, 92, 254, 222, 251, 74, 95, 92, + 254, 234, 243, 65, 95, 92, 109, 249, 75, 95, 92, 254, 222, 241, 222, 95, + 92, 238, 40, 95, 92, 249, 33, 64, 95, 92, 253, 147, 239, 25, 95, 92, 249, + 33, 253, 95, 95, 92, 249, 33, 253, 234, 95, 92, 249, 33, 69, 95, 92, 249, + 33, 253, 96, 95, 92, 249, 33, 253, 253, 95, 92, 249, 33, 252, 236, 95, + 92, 249, 33, 84, 95, 92, 249, 33, 253, 104, 95, 92, 240, 138, 95, 239, + 147, 13, 245, 251, 95, 92, 249, 33, 70, 95, 92, 249, 33, 253, 163, 95, + 92, 249, 33, 78, 95, 92, 249, 33, 254, 227, 240, 108, 95, 92, 249, 33, + 254, 227, 239, 45, 95, 92, 236, 142, 95, 92, 239, 46, 95, 92, 238, 30, + 95, 92, 253, 147, 254, 37, 95, 92, 253, 147, 249, 55, 95, 92, 253, 147, + 252, 216, 95, 92, 253, 147, 238, 189, 95, 92, 236, 173, 95, 92, 239, 51, + 95, 92, 239, 112, 95, 92, 240, 76, 95, 27, 244, 173, 95, 27, 121, 95, 27, + 114, 95, 27, 153, 95, 27, 163, 95, 27, 168, 95, 27, 169, 95, 27, 179, 95, + 27, 176, 95, 27, 178, 95, 92, 236, 241, 95, 92, 241, 192, 132, 1, 253, + 155, 132, 1, 253, 198, 244, 231, 132, 1, 253, 198, 249, 72, 132, 1, 249, + 110, 132, 1, 253, 178, 132, 1, 254, 225, 249, 72, 132, 1, 249, 84, 132, + 1, 253, 193, 132, 1, 76, 132, 1, 253, 188, 244, 231, 132, 1, 253, 188, + 249, 72, 132, 1, 253, 136, 132, 1, 253, 221, 132, 1, 253, 187, 132, 1, + 253, 230, 244, 231, 132, 1, 254, 232, 249, 72, 132, 1, 253, 230, 249, 72, + 132, 1, 254, 232, 244, 231, 132, 1, 253, 141, 132, 1, 253, 123, 132, 1, + 254, 234, 245, 131, 132, 1, 254, 234, 247, 68, 132, 1, 253, 138, 132, 1, + 254, 225, 244, 231, 132, 1, 254, 222, 244, 231, 132, 1, 78, 132, 1, 254, + 222, 249, 72, 132, 238, 96, 132, 38, 25, 64, 132, 38, 25, 253, 147, 249, + 139, 132, 38, 25, 253, 95, 132, 38, 25, 253, 234, 132, 38, 25, 69, 132, + 38, 25, 253, 96, 132, 38, 25, 181, 132, 38, 25, 254, 26, 132, 38, 25, 84, + 132, 38, 25, 253, 104, 132, 38, 25, 253, 147, 251, 165, 132, 238, 157, + 25, 253, 184, 132, 238, 157, 25, 249, 84, 132, 38, 25, 70, 132, 38, 25, + 254, 36, 132, 38, 25, 78, 132, 38, 25, 254, 2, 132, 38, 25, 253, 106, + 132, 254, 249, 253, 103, 132, 249, 5, 253, 147, 254, 37, 132, 249, 5, + 253, 147, 249, 55, 132, 249, 5, 253, 147, 253, 174, 132, 249, 5, 253, + 147, 241, 122, 132, 236, 87, 65, 132, 238, 36, 132, 27, 244, 173, 132, + 27, 121, 132, 27, 114, 132, 27, 153, 132, 27, 163, 132, 27, 168, 132, 27, + 169, 132, 27, 179, 132, 27, 176, 132, 27, 178, 132, 254, 222, 253, 136, + 132, 254, 222, 253, 141, 41, 4, 253, 245, 41, 167, 249, 80, 253, 175, + 253, 199, 239, 105, 64, 41, 167, 249, 80, 253, 175, 253, 199, 254, 28, + 250, 64, 250, 150, 221, 41, 167, 249, 80, 253, 175, 253, 199, 254, 28, + 249, 80, 244, 242, 221, 41, 167, 46, 253, 175, 253, 199, 251, 224, 221, + 41, 167, 241, 48, 253, 175, 253, 199, 252, 160, 221, 41, 167, 244, 225, + 253, 175, 253, 199, 250, 45, 250, 70, 221, 41, 167, 253, 175, 253, 199, + 244, 242, 250, 70, 221, 41, 167, 248, 53, 244, 223, 41, 167, 246, 33, + 253, 175, 249, 107, 41, 167, 250, 162, 252, 163, 253, 175, 249, 107, 41, + 167, 236, 24, 243, 15, 41, 167, 238, 200, 244, 242, 243, 53, 41, 167, + 244, 223, 41, 167, 249, 154, 244, 223, 41, 167, 244, 242, 244, 223, 41, + 167, 249, 154, 244, 242, 244, 223, 41, 167, 254, 179, 250, 185, 244, 98, + 244, 223, 41, 167, 250, 63, 251, 45, 244, 223, 41, 167, 244, 225, 245, + 54, 245, 3, 255, 14, 165, 249, 61, 41, 167, 249, 80, 243, 15, 41, 239, + 224, 25, 250, 184, 242, 121, 41, 239, 224, 25, 251, 192, 242, 121, 41, + 236, 61, 25, 252, 162, 251, 28, 245, 235, 242, 121, 41, 236, 61, 25, 243, + 23, 253, 90, 41, 236, 61, 25, 248, 55, 242, 40, 41, 25, 249, 53, 249, 98, + 245, 92, 41, 25, 249, 53, 249, 98, 242, 219, 41, 25, 249, 53, 249, 98, + 245, 102, 41, 25, 249, 53, 254, 16, 245, 92, 41, 25, 249, 53, 254, 16, + 242, 219, 41, 25, 249, 53, 249, 98, 249, 53, 251, 252, 41, 27, 244, 173, + 41, 27, 121, 41, 27, 114, 41, 27, 153, 41, 27, 163, 41, 27, 168, 41, 27, + 169, 41, 27, 179, 41, 27, 176, 41, 27, 178, 41, 27, 136, 121, 41, 27, + 136, 114, 41, 27, 136, 153, 41, 27, 136, 163, 41, 27, 136, 168, 41, 27, + 136, 169, 41, 27, 136, 179, 41, 27, 136, 176, 41, 27, 136, 178, 41, 27, + 136, 244, 173, 41, 167, 246, 32, 242, 121, 41, 167, 250, 27, 245, 65, + 254, 59, 253, 69, 41, 167, 244, 225, 245, 54, 245, 3, 249, 130, 254, 56, + 249, 61, 41, 167, 250, 27, 245, 65, 252, 161, 242, 121, 41, 167, 253, + 164, 249, 107, 41, 167, 254, 73, 243, 24, 41, 167, 254, 43, 245, 3, 245, + 104, 41, 167, 254, 43, 245, 3, 245, 103, 41, 167, 254, 29, 245, 121, 245, + 104, 41, 167, 254, 29, 245, 121, 245, 103, 41, 25, 254, 208, 243, 16, 41, + 25, 254, 139, 243, 16, 41, 1, 208, 41, 1, 249, 24, 41, 1, 253, 100, 41, + 1, 249, 36, 41, 1, 249, 13, 41, 1, 253, 115, 41, 1, 249, 11, 41, 1, 253, + 118, 41, 1, 249, 38, 41, 1, 249, 22, 41, 1, 244, 195, 41, 1, 249, 27, 41, + 1, 244, 200, 41, 1, 249, 37, 41, 1, 253, 91, 41, 1, 249, 19, 41, 1, 253, + 93, 41, 1, 249, 40, 41, 1, 253, 94, 41, 1, 253, 90, 41, 1, 249, 29, 41, + 1, 253, 110, 41, 1, 249, 39, 41, 1, 221, 41, 1, 249, 56, 41, 1, 245, 211, + 41, 1, 249, 100, 41, 1, 245, 78, 41, 1, 253, 98, 41, 1, 249, 52, 41, 1, + 253, 129, 41, 1, 254, 27, 41, 1, 216, 41, 1, 253, 92, 41, 1, 253, 103, + 41, 1, 249, 7, 41, 1, 249, 34, 41, 1, 253, 97, 41, 1, 253, 88, 41, 1, 64, + 41, 1, 245, 127, 41, 1, 237, 134, 253, 92, 41, 38, 25, 253, 95, 41, 38, + 25, 69, 41, 38, 25, 253, 96, 41, 38, 25, 84, 41, 38, 25, 253, 104, 41, + 38, 25, 152, 135, 41, 38, 25, 152, 253, 130, 41, 38, 25, 152, 148, 41, + 38, 25, 152, 253, 146, 41, 38, 25, 70, 41, 38, 25, 253, 126, 41, 38, 25, + 78, 41, 38, 25, 253, 119, 41, 25, 252, 128, 255, 22, 254, 155, 253, 144, + 41, 25, 250, 64, 246, 17, 41, 38, 25, 224, 69, 41, 38, 25, 224, 253, 96, + 41, 25, 254, 59, 254, 212, 254, 152, 253, 93, 41, 25, 254, 183, 247, 55, + 41, 167, 240, 83, 41, 167, 241, 234, 41, 25, 254, 132, 242, 121, 41, 25, + 250, 112, 242, 121, 41, 25, 254, 131, 254, 73, 249, 61, 41, 25, 251, 223, + 249, 61, 41, 25, 254, 42, 254, 91, 239, 232, 41, 25, 254, 42, 254, 141, + 239, 232, 41, 185, 1, 208, 41, 185, 1, 249, 24, 41, 185, 1, 253, 100, 41, + 185, 1, 249, 36, 41, 185, 1, 249, 13, 41, 185, 1, 253, 115, 41, 185, 1, + 249, 11, 41, 185, 1, 253, 118, 41, 185, 1, 249, 38, 41, 185, 1, 249, 22, + 41, 185, 1, 244, 195, 41, 185, 1, 249, 27, 41, 185, 1, 244, 200, 41, 185, + 1, 249, 37, 41, 185, 1, 253, 91, 41, 185, 1, 249, 19, 41, 185, 1, 253, + 93, 41, 185, 1, 249, 40, 41, 185, 1, 253, 94, 41, 185, 1, 253, 90, 41, + 185, 1, 249, 29, 41, 185, 1, 253, 110, 41, 185, 1, 249, 39, 41, 185, 1, + 221, 41, 185, 1, 249, 56, 41, 185, 1, 245, 211, 41, 185, 1, 249, 100, 41, + 185, 1, 245, 78, 41, 185, 1, 253, 98, 41, 185, 1, 249, 52, 41, 185, 1, + 253, 129, 41, 185, 1, 254, 27, 41, 185, 1, 216, 41, 185, 1, 253, 92, 41, + 185, 1, 253, 103, 41, 185, 1, 249, 7, 41, 185, 1, 249, 34, 41, 185, 1, + 253, 97, 41, 185, 1, 253, 88, 41, 185, 1, 64, 41, 185, 1, 245, 127, 41, + 185, 1, 237, 134, 253, 98, 41, 185, 1, 237, 134, 216, 41, 185, 1, 237, + 134, 253, 92, 41, 254, 250, 254, 254, 249, 24, 41, 254, 250, 254, 254, + 254, 125, 249, 130, 254, 56, 249, 61, 41, 236, 54, 25, 82, 245, 60, 41, + 236, 54, 25, 117, 245, 60, 41, 236, 54, 25, 250, 176, 248, 118, 41, 236, + 54, 25, 252, 157, 243, 22, 41, 13, 250, 228, 253, 192, 41, 13, 254, 69, + 252, 127, 41, 13, 247, 237, 246, 135, 41, 13, 254, 69, 254, 180, 250, 63, + 246, 160, 41, 13, 250, 45, 253, 90, 41, 13, 253, 137, 253, 192, 41, 13, + 253, 137, 254, 239, 249, 154, 241, 11, 41, 13, 253, 137, 254, 239, 251, + 46, 241, 11, 41, 13, 253, 137, 254, 239, 249, 130, 241, 11, 41, 25, 249, + 53, 254, 16, 245, 102, 108, 1, 208, 108, 1, 249, 24, 108, 1, 253, 100, + 108, 1, 249, 36, 108, 1, 249, 13, 108, 1, 253, 115, 108, 1, 249, 11, 108, + 1, 253, 118, 108, 1, 249, 45, 108, 1, 249, 38, 108, 1, 247, 179, 108, 1, + 249, 22, 108, 1, 244, 195, 108, 1, 249, 27, 108, 1, 244, 200, 108, 1, + 249, 37, 108, 1, 253, 91, 108, 1, 249, 19, 108, 1, 253, 93, 108, 1, 249, + 40, 108, 1, 253, 94, 108, 1, 253, 90, 108, 1, 249, 29, 108, 1, 253, 110, + 108, 1, 249, 39, 108, 1, 221, 108, 1, 216, 108, 1, 253, 92, 108, 1, 253, + 103, 108, 1, 253, 98, 108, 1, 253, 97, 108, 1, 253, 88, 108, 1, 249, 49, + 108, 1, 64, 108, 1, 69, 108, 1, 253, 96, 108, 1, 84, 108, 1, 253, 104, + 108, 1, 70, 108, 1, 78, 108, 1, 253, 109, 108, 38, 25, 253, 95, 108, 38, + 25, 69, 108, 38, 25, 253, 96, 108, 38, 25, 84, 108, 38, 25, 253, 104, + 108, 38, 25, 70, 108, 38, 25, 253, 106, 108, 25, 238, 90, 108, 25, 49, + 45, 108, 25, 239, 148, 108, 25, 240, 220, 108, 27, 244, 173, 108, 27, + 121, 108, 27, 114, 108, 27, 153, 108, 27, 163, 108, 27, 168, 108, 27, + 169, 108, 27, 179, 108, 27, 176, 108, 27, 178, 108, 25, 242, 147, 239, + 174, 108, 25, 239, 174, 108, 13, 239, 42, 108, 13, 237, 181, 108, 13, + 234, 68, 108, 13, 239, 23, 108, 1, 249, 7, 108, 1, 249, 34, 108, 1, 152, + 135, 108, 1, 152, 253, 130, 108, 1, 152, 148, 108, 1, 152, 253, 146, 108, + 38, 25, 152, 135, 108, 38, 25, 152, 253, 130, 108, 38, 25, 152, 148, 108, + 38, 25, 152, 253, 146, 101, 5, 1, 253, 235, 101, 5, 1, 249, 189, 101, 5, + 1, 249, 227, 101, 5, 1, 249, 216, 101, 5, 1, 253, 166, 101, 5, 1, 250, + 106, 101, 5, 1, 250, 124, 101, 5, 1, 250, 83, 101, 5, 1, 253, 227, 101, + 5, 1, 249, 139, 101, 5, 1, 250, 7, 101, 5, 1, 249, 146, 101, 5, 1, 250, + 25, 101, 5, 1, 253, 246, 101, 5, 1, 250, 40, 101, 5, 1, 245, 233, 101, 5, + 1, 250, 60, 101, 5, 1, 250, 67, 101, 5, 1, 250, 84, 101, 5, 1, 254, 75, + 101, 5, 1, 245, 159, 101, 5, 1, 245, 122, 101, 5, 1, 245, 96, 101, 5, 1, + 250, 55, 101, 5, 1, 245, 6, 101, 5, 1, 245, 69, 101, 5, 1, 249, 61, 101, + 5, 1, 249, 247, 101, 5, 1, 249, 197, 101, 5, 1, 245, 67, 101, 5, 1, 250, + 111, 101, 5, 1, 245, 116, 101, 5, 1, 249, 239, 101, 5, 1, 253, 125, 101, + 5, 1, 249, 137, 101, 5, 1, 253, 132, 101, 5, 1, 249, 240, 101, 5, 1, 249, + 245, 101, 1, 253, 235, 101, 1, 249, 189, 101, 1, 249, 227, 101, 1, 249, + 216, 101, 1, 253, 166, 101, 1, 250, 106, 101, 1, 250, 124, 101, 1, 250, + 83, 101, 1, 253, 227, 101, 1, 249, 139, 101, 1, 250, 7, 101, 1, 249, 146, + 101, 1, 250, 25, 101, 1, 253, 246, 101, 1, 250, 40, 101, 1, 245, 233, + 101, 1, 250, 60, 101, 1, 250, 67, 101, 1, 250, 84, 101, 1, 254, 75, 101, + 1, 245, 159, 101, 1, 245, 122, 101, 1, 245, 96, 101, 1, 250, 55, 101, 1, + 245, 6, 101, 1, 245, 69, 101, 1, 249, 61, 101, 1, 249, 247, 101, 1, 249, + 197, 101, 1, 245, 67, 101, 1, 250, 111, 101, 1, 245, 116, 101, 1, 249, + 239, 101, 1, 253, 125, 101, 1, 249, 137, 101, 1, 253, 132, 101, 1, 249, + 240, 101, 1, 249, 245, 101, 1, 253, 226, 101, 1, 254, 209, 101, 1, 243, + 98, 101, 238, 113, 237, 114, 20, 88, 240, 242, 20, 88, 236, 59, 20, 88, + 242, 131, 20, 88, 240, 244, 20, 88, 236, 72, 20, 88, 242, 134, 20, 88, + 242, 129, 20, 88, 242, 133, 20, 88, 236, 209, 20, 88, 244, 230, 20, 88, + 237, 138, 20, 88, 242, 126, 20, 88, 242, 122, 20, 88, 236, 207, 20, 88, + 239, 155, 20, 88, 239, 158, 20, 88, 239, 166, 20, 88, 239, 161, 20, 88, + 242, 125, 20, 88, 235, 254, 20, 88, 236, 233, 20, 88, 235, 246, 20, 88, + 236, 148, 20, 88, 235, 208, 20, 88, 236, 79, 20, 88, 236, 234, 20, 88, + 236, 57, 20, 88, 235, 221, 20, 88, 236, 64, 20, 88, 235, 194, 20, 88, + 235, 255, 20, 88, 236, 154, 20, 88, 236, 0, 20, 88, 236, 198, 20, 88, + 231, 248, 20, 88, 231, 249, 20, 88, 232, 9, 20, 88, 234, 57, 20, 88, 232, + 8, 20, 88, 236, 77, 20, 88, 235, 226, 20, 88, 235, 205, 20, 88, 235, 204, + 20, 88, 235, 195, 20, 88, 231, 240, 20, 88, 236, 70, 20, 88, 236, 230, + 20, 88, 236, 71, 20, 88, 236, 231, 20, 88, 237, 98, 20, 88, 236, 206, 20, + 88, 232, 2, 20, 88, 235, 185, 20, 88, 237, 97, 20, 88, 236, 229, 20, 88, + 236, 33, 20, 88, 236, 34, 20, 88, 236, 36, 20, 88, 236, 35, 20, 88, 237, + 96, 20, 88, 236, 6, 20, 88, 231, 252, 20, 88, 232, 18, 20, 88, 231, 237, + 20, 88, 239, 191, 20, 88, 235, 253, 20, 88, 236, 23, 20, 88, 236, 136, + 20, 88, 236, 137, 20, 88, 237, 68, 20, 88, 235, 218, 20, 88, 236, 208, + 20, 88, 236, 135, 20, 88, 235, 216, 20, 88, 235, 220, 20, 88, 235, 219, + 20, 88, 238, 134, 20, 88, 236, 88, 20, 88, 235, 214, 20, 88, 231, 243, + 20, 88, 232, 6, 20, 88, 231, 234, 20, 88, 232, 19, 20, 88, 235, 213, 20, + 88, 232, 20, 20, 88, 231, 242, 20, 88, 235, 203, 20, 88, 232, 14, 20, 88, + 236, 232, 20, 88, 236, 73, 20, 88, 240, 255, 20, 129, 240, 255, 20, 129, + 64, 20, 129, 253, 163, 20, 129, 216, 20, 129, 249, 181, 20, 129, 254, 10, + 20, 129, 70, 20, 129, 249, 182, 20, 129, 253, 200, 20, 129, 78, 20, 129, + 253, 98, 20, 129, 249, 175, 20, 129, 253, 142, 20, 129, 253, 123, 20, + 129, 84, 20, 129, 249, 177, 20, 129, 253, 132, 20, 129, 253, 145, 20, + 129, 253, 124, 20, 129, 254, 12, 20, 129, 253, 139, 20, 129, 69, 20, 129, + 250, 132, 20, 129, 250, 133, 20, 129, 248, 186, 20, 129, 244, 151, 20, + 129, 246, 123, 20, 129, 246, 124, 20, 129, 243, 100, 20, 129, 244, 154, + 20, 129, 244, 155, 20, 129, 247, 231, 20, 129, 252, 53, 20, 129, 247, + 232, 20, 129, 252, 54, 20, 129, 252, 55, 20, 129, 243, 19, 20, 129, 241, + 91, 20, 129, 242, 57, 20, 129, 248, 195, 20, 129, 245, 226, 20, 129, 252, + 234, 20, 129, 248, 159, 20, 129, 240, 189, 20, 129, 248, 160, 20, 129, + 252, 235, 20, 129, 248, 198, 20, 129, 244, 157, 20, 129, 248, 199, 20, + 129, 241, 92, 20, 129, 243, 20, 20, 129, 248, 200, 20, 129, 245, 228, 20, + 129, 246, 126, 20, 129, 243, 102, 20, 129, 248, 190, 20, 129, 251, 104, + 20, 129, 246, 247, 20, 129, 251, 105, 20, 129, 251, 106, 20, 129, 246, + 246, 20, 129, 240, 87, 20, 129, 242, 197, 20, 129, 238, 174, 20, 129, + 239, 255, 20, 129, 239, 254, 20, 129, 237, 99, 20, 99, 240, 242, 20, 99, + 236, 59, 20, 99, 236, 63, 20, 99, 242, 131, 20, 99, 236, 65, 20, 99, 236, + 66, 20, 99, 240, 244, 20, 99, 242, 134, 20, 99, 235, 247, 20, 99, 236, + 67, 20, 99, 236, 68, 20, 99, 236, 204, 20, 99, 235, 197, 20, 99, 235, + 196, 20, 99, 236, 57, 20, 99, 242, 129, 20, 99, 242, 133, 20, 99, 236, + 198, 20, 99, 244, 230, 20, 99, 237, 138, 20, 99, 242, 126, 20, 99, 242, + 122, 20, 99, 239, 155, 20, 99, 239, 158, 20, 99, 239, 166, 20, 99, 239, + 161, 20, 99, 242, 125, 20, 99, 236, 26, 20, 99, 231, 244, 20, 99, 235, + 254, 20, 99, 235, 246, 20, 99, 236, 220, 20, 99, 235, 201, 20, 99, 235, + 224, 20, 99, 235, 208, 20, 99, 236, 39, 20, 99, 231, 250, 20, 99, 236, + 79, 20, 99, 236, 1, 20, 99, 231, 246, 20, 99, 236, 234, 20, 99, 231, 245, + 20, 99, 232, 10, 20, 99, 232, 4, 20, 99, 232, 0, 20, 99, 231, 239, 20, + 99, 234, 60, 20, 99, 235, 206, 20, 99, 235, 227, 20, 99, 231, 251, 20, + 99, 236, 27, 20, 99, 236, 144, 20, 99, 236, 64, 20, 99, 236, 216, 20, 99, + 234, 55, 20, 99, 235, 200, 20, 99, 235, 223, 20, 99, 236, 143, 20, 99, + 235, 194, 20, 99, 236, 155, 20, 99, 235, 204, 20, 99, 236, 153, 20, 99, + 235, 195, 20, 99, 236, 70, 20, 99, 236, 71, 20, 99, 236, 231, 20, 99, + 236, 206, 20, 99, 239, 191, 20, 99, 235, 253, 20, 99, 231, 253, 20, 99, + 236, 208, 20, 99, 235, 217, 20, 99, 238, 134, 20, 99, 235, 210, 20, 99, + 232, 5, 20, 99, 235, 203, 20, 99, 232, 14, 20, 99, 236, 132, 20, 99, 236, + 134, 20, 99, 236, 131, 20, 99, 236, 133, 20, 99, 236, 73, 24, 4, 253, 88, + 24, 4, 253, 197, 24, 4, 253, 154, 24, 4, 249, 68, 24, 4, 249, 242, 24, 4, + 253, 125, 24, 4, 253, 140, 24, 4, 253, 103, 24, 4, 253, 229, 24, 4, 253, + 211, 24, 4, 250, 6, 24, 4, 250, 8, 24, 4, 253, 210, 24, 4, 253, 184, 24, + 4, 249, 145, 24, 4, 251, 68, 24, 4, 251, 72, 24, 4, 251, 70, 24, 4, 245, + 107, 24, 4, 246, 174, 24, 4, 251, 69, 24, 4, 251, 71, 24, 4, 246, 175, + 24, 4, 221, 24, 4, 253, 114, 24, 4, 253, 156, 24, 4, 250, 17, 24, 4, 250, + 18, 24, 4, 253, 167, 24, 4, 253, 141, 24, 4, 249, 149, 24, 4, 252, 189, + 24, 4, 252, 193, 24, 4, 252, 191, 24, 4, 248, 112, 24, 4, 248, 113, 24, + 4, 252, 190, 24, 4, 252, 192, 24, 4, 248, 114, 24, 4, 253, 92, 24, 4, + 253, 170, 24, 4, 253, 169, 24, 4, 249, 115, 24, 4, 250, 62, 24, 4, 253, + 150, 24, 4, 253, 144, 24, 4, 252, 144, 24, 4, 253, 97, 24, 4, 253, 172, + 24, 4, 253, 158, 24, 4, 250, 66, 24, 4, 249, 117, 24, 4, 253, 171, 24, 4, + 253, 159, 24, 4, 249, 163, 24, 4, 249, 7, 24, 4, 249, 162, 24, 4, 249, + 118, 24, 4, 245, 180, 24, 4, 245, 182, 24, 4, 249, 70, 24, 4, 249, 85, + 24, 4, 245, 43, 24, 4, 253, 213, 24, 4, 254, 19, 24, 4, 253, 249, 24, 4, + 249, 157, 24, 4, 252, 85, 24, 4, 254, 67, 24, 4, 254, 18, 24, 4, 252, + 108, 24, 4, 252, 110, 24, 4, 248, 14, 24, 4, 248, 15, 24, 4, 252, 109, + 24, 4, 252, 86, 24, 4, 252, 90, 24, 4, 252, 88, 24, 4, 248, 1, 24, 4, + 248, 2, 24, 4, 252, 87, 24, 4, 252, 89, 24, 4, 248, 3, 24, 4, 248, 5, 24, + 4, 244, 48, 24, 4, 244, 49, 24, 4, 248, 4, 24, 4, 253, 110, 24, 4, 253, + 192, 24, 4, 253, 236, 24, 4, 249, 188, 24, 4, 249, 128, 24, 4, 253, 191, + 24, 4, 253, 221, 24, 4, 250, 169, 24, 4, 253, 161, 24, 4, 253, 255, 24, + 4, 254, 25, 24, 4, 252, 247, 24, 4, 249, 174, 24, 4, 253, 214, 24, 4, + 253, 215, 24, 4, 253, 8, 24, 4, 253, 91, 24, 4, 253, 160, 24, 4, 253, + 174, 24, 4, 250, 80, 24, 4, 249, 164, 24, 4, 253, 151, 24, 4, 76, 24, 4, + 249, 172, 24, 4, 253, 115, 24, 4, 254, 32, 24, 4, 254, 5, 24, 4, 249, + 194, 24, 4, 250, 180, 24, 4, 254, 4, 24, 4, 253, 178, 24, 4, 249, 132, + 24, 4, 254, 204, 24, 4, 254, 206, 24, 4, 253, 135, 24, 4, 253, 23, 24, 4, + 253, 24, 24, 4, 254, 205, 24, 4, 254, 77, 24, 4, 253, 34, 24, 4, 253, 36, + 24, 4, 248, 183, 24, 4, 248, 184, 24, 4, 253, 35, 24, 4, 253, 94, 24, 4, + 253, 112, 24, 4, 253, 148, 24, 4, 249, 150, 24, 4, 250, 26, 24, 4, 253, + 157, 24, 4, 253, 136, 24, 4, 249, 153, 24, 4, 249, 38, 24, 4, 250, 34, + 24, 4, 250, 33, 24, 4, 247, 201, 24, 4, 247, 202, 24, 4, 252, 47, 24, 4, + 249, 84, 24, 4, 247, 213, 24, 4, 240, 211, 64, 24, 4, 240, 211, 84, 24, + 4, 240, 211, 69, 24, 4, 240, 211, 253, 95, 24, 4, 240, 211, 253, 126, 24, + 4, 240, 211, 70, 24, 4, 240, 211, 78, 24, 4, 240, 211, 253, 98, 24, 4, + 208, 24, 4, 253, 183, 24, 4, 253, 182, 24, 4, 249, 252, 24, 4, 251, 146, + 24, 4, 253, 134, 24, 4, 253, 155, 24, 4, 249, 141, 24, 4, 249, 143, 24, + 4, 244, 254, 24, 4, 247, 44, 24, 4, 249, 142, 24, 4, 251, 176, 24, 4, + 251, 180, 24, 4, 251, 178, 24, 4, 247, 45, 24, 4, 247, 46, 24, 4, 251, + 177, 24, 4, 251, 179, 24, 4, 247, 47, 24, 4, 247, 49, 24, 4, 243, 195, + 24, 4, 243, 196, 24, 4, 247, 48, 24, 4, 253, 98, 24, 4, 253, 216, 24, 4, + 253, 145, 24, 4, 249, 123, 24, 4, 250, 109, 24, 4, 253, 132, 24, 4, 253, + 138, 24, 4, 237, 117, 64, 24, 4, 237, 117, 84, 24, 4, 237, 117, 69, 24, + 4, 237, 117, 253, 95, 24, 4, 237, 117, 253, 126, 24, 4, 237, 117, 70, 24, + 4, 237, 117, 78, 24, 4, 253, 129, 24, 4, 253, 233, 24, 4, 253, 218, 24, + 4, 250, 123, 24, 4, 249, 126, 24, 4, 253, 189, 24, 4, 253, 190, 24, 4, + 253, 76, 24, 4, 249, 52, 24, 4, 250, 127, 24, 4, 250, 125, 24, 4, 248, + 209, 24, 4, 245, 53, 24, 4, 249, 74, 24, 4, 250, 126, 24, 4, 248, 224, + 24, 4, 216, 24, 4, 253, 124, 24, 4, 253, 139, 24, 4, 249, 180, 24, 4, + 249, 125, 24, 4, 253, 200, 24, 4, 253, 123, 24, 4, 253, 93, 24, 4, 253, + 204, 24, 4, 253, 165, 24, 4, 250, 202, 24, 4, 249, 96, 24, 4, 253, 152, + 24, 4, 253, 193, 24, 4, 250, 236, 24, 4, 249, 27, 24, 4, 249, 212, 24, 4, + 249, 210, 24, 4, 246, 82, 24, 4, 246, 87, 24, 4, 249, 209, 24, 4, 249, + 211, 24, 4, 246, 101, 24, 4, 253, 118, 24, 4, 253, 241, 24, 4, 253, 228, + 24, 4, 251, 117, 24, 4, 249, 104, 24, 4, 253, 239, 24, 4, 253, 240, 24, + 4, 251, 135, 24, 4, 253, 100, 24, 4, 253, 196, 24, 4, 253, 209, 24, 4, + 249, 136, 24, 4, 249, 229, 24, 4, 253, 207, 24, 4, 253, 195, 24, 4, 251, + 52, 24, 4, 251, 54, 24, 4, 246, 167, 24, 4, 246, 168, 24, 4, 251, 53, 24, + 4, 249, 231, 24, 4, 249, 235, 24, 4, 249, 233, 24, 4, 246, 136, 24, 4, + 246, 140, 24, 4, 249, 232, 24, 4, 249, 234, 24, 4, 249, 19, 24, 4, 250, + 87, 24, 4, 249, 73, 24, 4, 245, 8, 24, 4, 245, 9, 24, 4, 249, 63, 24, 4, + 249, 55, 24, 4, 248, 126, 24, 4, 249, 11, 24, 4, 249, 131, 24, 4, 249, + 23, 24, 4, 246, 44, 24, 4, 244, 246, 24, 4, 249, 44, 24, 4, 249, 94, 24, + 4, 246, 60, 24, 4, 249, 29, 24, 4, 252, 63, 24, 4, 249, 30, 24, 4, 247, + 242, 24, 4, 247, 244, 24, 4, 250, 43, 24, 4, 250, 44, 24, 4, 247, 245, + 24, 4, 249, 56, 24, 4, 249, 171, 24, 4, 249, 169, 24, 4, 248, 140, 24, 4, + 245, 210, 24, 4, 249, 64, 24, 4, 249, 170, 24, 4, 248, 142, 24, 4, 252, + 229, 24, 4, 252, 233, 24, 4, 252, 231, 24, 4, 248, 157, 24, 4, 248, 158, + 24, 4, 252, 230, 24, 4, 252, 232, 24, 4, 253, 153, 24, 4, 254, 41, 24, 4, + 253, 226, 24, 4, 249, 222, 24, 4, 250, 255, 24, 4, 254, 40, 24, 4, 254, + 13, 24, 4, 251, 18, 24, 4, 253, 90, 24, 4, 253, 231, 24, 4, 253, 116, 24, + 4, 250, 39, 24, 4, 249, 114, 24, 4, 253, 143, 24, 4, 253, 187, 24, 4, + 249, 156, 24, 4, 252, 145, 24, 4, 251, 248, 24, 4, 251, 19, 24, 238, 73, + 24, 213, 189, 24, 242, 84, 24, 237, 123, 24, 242, 81, 24, 241, 243, 242, + 81, 24, 239, 126, 65, 24, 238, 113, 237, 114, 24, 27, 121, 24, 27, 114, + 24, 27, 153, 24, 27, 163, 24, 27, 168, 24, 27, 169, 24, 27, 179, 24, 27, + 176, 24, 27, 178, 24, 83, 249, 18, 24, 83, 240, 239, 24, 83, 241, 7, 24, + 83, 242, 213, 24, 83, 242, 144, 24, 83, 244, 85, 24, 83, 240, 162, 24, + 83, 241, 152, 24, 83, 241, 69, 24, 83, 239, 150, 24, 83, 253, 250, 238, + 104, 96, 1, 64, 96, 1, 70, 96, 1, 69, 96, 1, 78, 96, 1, 84, 96, 1, 186, + 96, 1, 253, 100, 96, 1, 208, 96, 1, 253, 207, 96, 1, 253, 209, 96, 1, + 253, 195, 96, 1, 253, 196, 96, 1, 254, 109, 96, 1, 253, 88, 96, 1, 253, + 125, 96, 1, 253, 154, 96, 1, 253, 140, 96, 1, 253, 197, 96, 1, 254, 44, + 96, 1, 253, 103, 96, 1, 253, 210, 96, 1, 253, 211, 96, 1, 253, 184, 96, + 1, 253, 229, 96, 1, 254, 137, 96, 1, 221, 96, 1, 253, 167, 96, 1, 253, + 156, 96, 1, 253, 141, 96, 1, 253, 114, 96, 1, 253, 94, 96, 1, 249, 244, + 96, 1, 251, 253, 96, 1, 253, 157, 96, 1, 253, 148, 96, 1, 253, 136, 96, + 1, 253, 112, 96, 1, 254, 58, 96, 1, 252, 99, 96, 1, 252, 100, 96, 1, 252, + 101, 96, 1, 250, 58, 96, 1, 250, 59, 96, 1, 252, 106, 96, 1, 253, 97, 96, + 1, 159, 96, 1, 253, 171, 96, 1, 253, 158, 96, 1, 253, 159, 96, 1, 253, + 172, 96, 1, 254, 72, 96, 1, 253, 93, 96, 1, 253, 91, 96, 1, 253, 152, 96, + 1, 253, 151, 96, 1, 253, 165, 96, 1, 253, 174, 96, 1, 253, 193, 96, 1, + 253, 204, 96, 1, 254, 99, 96, 1, 249, 214, 96, 1, 250, 89, 96, 1, 250, + 90, 96, 1, 250, 91, 96, 1, 250, 92, 96, 1, 250, 93, 96, 1, 252, 212, 96, + 1, 249, 56, 96, 1, 249, 64, 96, 1, 249, 169, 96, 1, 249, 170, 96, 1, 249, + 171, 96, 1, 252, 217, 96, 1, 253, 98, 96, 1, 253, 132, 96, 1, 253, 145, + 96, 1, 253, 138, 96, 1, 253, 216, 96, 1, 254, 203, 96, 1, 216, 96, 1, + 253, 200, 96, 1, 253, 139, 96, 1, 253, 123, 96, 1, 253, 124, 96, 1, 254, + 210, 14, 15, 70, 14, 15, 250, 134, 14, 15, 69, 14, 15, 253, 96, 14, 15, + 78, 14, 15, 253, 119, 14, 15, 242, 98, 253, 119, 14, 15, 44, 253, 126, + 14, 15, 44, 69, 14, 15, 64, 14, 15, 253, 95, 14, 15, 253, 132, 14, 15, + 94, 253, 132, 14, 15, 253, 145, 14, 15, 94, 253, 145, 14, 15, 250, 110, + 14, 15, 94, 250, 110, 14, 15, 253, 138, 14, 15, 94, 253, 138, 14, 15, + 249, 178, 14, 15, 94, 249, 178, 14, 15, 240, 212, 249, 178, 14, 15, 253, + 98, 14, 15, 94, 253, 98, 14, 15, 249, 123, 14, 15, 94, 249, 123, 14, 15, + 240, 212, 249, 123, 14, 15, 253, 106, 14, 15, 242, 98, 201, 14, 15, 240, + 211, 189, 14, 15, 29, 116, 14, 15, 29, 158, 14, 15, 29, 242, 80, 136, + 244, 183, 14, 15, 29, 253, 121, 136, 244, 183, 14, 15, 29, 37, 136, 244, + 183, 14, 15, 29, 244, 183, 14, 15, 29, 42, 116, 14, 15, 29, 42, 207, 55, + 239, 240, 14, 15, 29, 242, 65, 227, 14, 15, 29, 207, 146, 122, 14, 15, + 29, 244, 212, 14, 15, 29, 79, 244, 184, 14, 15, 253, 166, 14, 15, 253, + 227, 14, 15, 253, 246, 14, 15, 253, 235, 14, 15, 253, 143, 14, 15, 247, + 236, 14, 15, 253, 116, 14, 15, 252, 66, 14, 15, 253, 187, 14, 15, 250, + 47, 14, 15, 242, 98, 250, 47, 14, 15, 44, 249, 242, 14, 15, 44, 253, 154, + 14, 15, 253, 90, 14, 15, 250, 39, 14, 15, 250, 49, 14, 15, 94, 250, 49, + 14, 15, 250, 50, 14, 15, 94, 250, 50, 14, 15, 245, 164, 14, 15, 94, 245, + 164, 14, 15, 250, 51, 14, 15, 94, 250, 51, 14, 15, 245, 165, 14, 15, 94, + 245, 165, 14, 15, 249, 156, 14, 15, 94, 249, 156, 14, 15, 245, 38, 14, + 15, 94, 245, 38, 14, 15, 242, 98, 245, 38, 14, 15, 198, 14, 15, 94, 198, + 14, 15, 44, 164, 14, 15, 253, 151, 14, 15, 248, 115, 14, 15, 253, 174, + 14, 15, 252, 208, 14, 15, 76, 14, 15, 249, 167, 14, 15, 242, 98, 249, + 167, 14, 15, 44, 249, 96, 14, 15, 44, 253, 165, 14, 15, 253, 91, 14, 15, + 250, 80, 14, 15, 250, 99, 14, 15, 94, 250, 99, 14, 15, 250, 100, 14, 15, + 94, 250, 100, 14, 15, 245, 215, 14, 15, 94, 245, 215, 14, 15, 114, 14, + 15, 94, 114, 14, 15, 245, 216, 14, 15, 94, 245, 216, 14, 15, 249, 172, + 14, 15, 94, 249, 172, 14, 15, 245, 49, 14, 15, 94, 245, 49, 14, 15, 240, + 212, 245, 49, 14, 15, 190, 14, 15, 250, 95, 14, 15, 250, 96, 14, 15, 250, + 97, 14, 15, 249, 22, 14, 15, 253, 134, 14, 15, 247, 26, 14, 15, 253, 182, + 14, 15, 251, 156, 14, 15, 253, 155, 14, 15, 250, 3, 14, 15, 242, 98, 250, + 3, 14, 15, 208, 14, 15, 249, 252, 14, 15, 249, 142, 14, 15, 94, 249, 142, + 14, 15, 249, 143, 14, 15, 94, 249, 143, 14, 15, 245, 128, 14, 15, 94, + 245, 128, 14, 15, 250, 5, 14, 15, 94, 250, 5, 14, 15, 245, 129, 14, 15, + 94, 245, 129, 14, 15, 249, 141, 14, 15, 94, 249, 141, 14, 15, 244, 254, + 14, 15, 94, 244, 254, 14, 15, 240, 212, 244, 254, 14, 15, 205, 14, 15, + 254, 52, 14, 15, 236, 58, 249, 140, 14, 15, 236, 58, 251, 155, 14, 15, + 236, 58, 251, 164, 14, 15, 236, 58, 251, 140, 14, 15, 254, 4, 14, 15, + 246, 39, 14, 15, 254, 5, 14, 15, 250, 188, 14, 15, 253, 178, 14, 15, 249, + 200, 14, 15, 242, 98, 249, 200, 14, 15, 253, 115, 14, 15, 249, 194, 14, + 15, 249, 203, 14, 15, 94, 249, 203, 14, 15, 249, 204, 14, 15, 94, 249, + 204, 14, 15, 245, 73, 14, 15, 94, 245, 73, 14, 15, 249, 205, 14, 15, 94, + 249, 205, 14, 15, 245, 74, 14, 15, 94, 245, 74, 14, 15, 249, 132, 14, 15, + 94, 249, 132, 14, 15, 245, 19, 14, 15, 94, 245, 19, 14, 15, 240, 212, + 245, 19, 14, 15, 203, 14, 15, 242, 90, 253, 251, 14, 15, 253, 167, 14, + 15, 247, 75, 14, 15, 253, 156, 14, 15, 251, 232, 14, 15, 253, 141, 14, + 15, 250, 19, 14, 15, 242, 98, 250, 19, 14, 15, 221, 14, 15, 250, 17, 14, + 15, 250, 22, 14, 15, 94, 250, 22, 14, 15, 250, 23, 14, 15, 94, 250, 23, + 14, 15, 245, 144, 14, 15, 94, 245, 144, 14, 15, 250, 24, 14, 15, 94, 250, + 24, 14, 15, 245, 145, 14, 15, 94, 245, 145, 14, 15, 249, 149, 14, 15, 94, + 249, 149, 14, 15, 245, 31, 14, 15, 94, 245, 31, 14, 15, 240, 212, 245, + 31, 14, 15, 171, 14, 15, 94, 171, 14, 15, 254, 144, 14, 15, 237, 148, + 171, 14, 15, 242, 90, 171, 14, 15, 253, 157, 14, 15, 247, 107, 14, 15, + 253, 148, 14, 15, 252, 23, 14, 15, 253, 136, 14, 15, 250, 30, 14, 15, + 242, 98, 250, 30, 14, 15, 253, 94, 14, 15, 249, 150, 14, 15, 250, 32, 14, + 15, 94, 250, 32, 14, 15, 249, 153, 14, 15, 94, 249, 153, 14, 15, 245, 32, + 14, 15, 94, 245, 32, 14, 15, 240, 212, 245, 32, 14, 15, 183, 14, 15, 44, + 253, 244, 14, 15, 254, 157, 14, 15, 253, 210, 14, 15, 247, 50, 14, 15, + 253, 211, 14, 15, 251, 196, 14, 15, 253, 184, 14, 15, 250, 12, 14, 15, + 242, 98, 250, 12, 14, 15, 253, 103, 14, 15, 250, 6, 14, 15, 250, 13, 14, + 15, 94, 250, 13, 14, 15, 250, 14, 14, 15, 94, 250, 14, 14, 15, 245, 137, + 14, 15, 94, 245, 137, 14, 15, 250, 15, 14, 15, 94, 250, 15, 14, 15, 245, + 138, 14, 15, 94, 245, 138, 14, 15, 249, 145, 14, 15, 94, 249, 145, 14, + 15, 245, 136, 14, 15, 94, 245, 136, 14, 15, 148, 14, 15, 94, 148, 14, 15, + 98, 148, 14, 15, 253, 171, 14, 15, 248, 49, 14, 15, 253, 158, 14, 15, + 252, 165, 14, 15, 253, 159, 14, 15, 250, 74, 14, 15, 242, 98, 250, 74, + 14, 15, 253, 97, 14, 15, 250, 66, 14, 15, 250, 77, 14, 15, 94, 250, 77, + 14, 15, 250, 78, 14, 15, 94, 250, 78, 14, 15, 245, 198, 14, 15, 94, 245, + 198, 14, 15, 250, 79, 14, 15, 94, 250, 79, 14, 15, 245, 199, 14, 15, 94, + 245, 199, 14, 15, 249, 163, 14, 15, 94, 249, 163, 14, 15, 245, 46, 14, + 15, 94, 245, 46, 14, 15, 240, 212, 245, 46, 14, 15, 159, 14, 15, 237, + 148, 159, 14, 15, 254, 187, 14, 15, 238, 82, 159, 14, 15, 238, 35, 254, + 182, 14, 15, 240, 212, 252, 169, 14, 15, 240, 212, 252, 151, 14, 15, 240, + 212, 248, 83, 14, 15, 240, 212, 248, 106, 14, 15, 240, 212, 248, 79, 14, + 15, 240, 212, 248, 54, 14, 15, 249, 70, 14, 15, 249, 118, 14, 15, 248, + 61, 14, 15, 249, 85, 14, 15, 248, 62, 14, 15, 249, 7, 14, 15, 245, 180, + 14, 15, 245, 189, 14, 15, 94, 245, 189, 14, 15, 245, 190, 14, 15, 94, + 245, 190, 14, 15, 242, 254, 14, 15, 94, 242, 254, 14, 15, 245, 191, 14, + 15, 94, 245, 191, 14, 15, 242, 255, 14, 15, 94, 242, 255, 14, 15, 245, + 43, 14, 15, 94, 245, 43, 14, 15, 242, 253, 14, 15, 94, 242, 253, 14, 15, + 254, 21, 14, 15, 253, 200, 14, 15, 248, 187, 14, 15, 253, 139, 14, 15, + 253, 43, 14, 15, 253, 123, 14, 15, 250, 117, 14, 15, 242, 98, 250, 117, + 14, 15, 216, 14, 15, 249, 180, 14, 15, 250, 120, 14, 15, 94, 250, 120, + 14, 15, 250, 121, 14, 15, 94, 250, 121, 14, 15, 245, 229, 14, 15, 94, + 245, 229, 14, 15, 250, 122, 14, 15, 94, 250, 122, 14, 15, 245, 230, 14, + 15, 94, 245, 230, 14, 15, 250, 119, 14, 15, 94, 250, 119, 14, 15, 245, + 52, 14, 15, 94, 245, 52, 14, 15, 240, 212, 245, 52, 14, 15, 181, 14, 15, + 237, 178, 181, 14, 15, 94, 181, 14, 15, 242, 90, 253, 139, 14, 15, 253, + 150, 14, 15, 244, 50, 253, 150, 14, 15, 94, 253, 210, 14, 15, 248, 17, + 14, 15, 253, 169, 14, 15, 252, 125, 14, 15, 253, 144, 14, 15, 252, 129, + 14, 15, 94, 253, 184, 14, 15, 253, 92, 14, 15, 249, 115, 14, 15, 94, 253, + 103, 14, 15, 245, 175, 14, 15, 94, 245, 175, 14, 15, 135, 14, 15, 94, + 135, 14, 15, 98, 135, 14, 15, 254, 40, 14, 15, 246, 128, 14, 15, 253, + 226, 14, 15, 251, 6, 14, 15, 254, 13, 14, 15, 251, 12, 14, 15, 253, 153, + 14, 15, 249, 222, 14, 15, 245, 90, 14, 15, 94, 245, 90, 14, 15, 204, 14, + 15, 249, 63, 14, 15, 242, 182, 249, 63, 14, 15, 249, 73, 14, 15, 242, + 182, 249, 73, 14, 15, 245, 48, 14, 15, 242, 182, 245, 48, 14, 15, 249, + 55, 14, 15, 245, 202, 14, 15, 249, 19, 14, 15, 245, 8, 14, 15, 243, 10, + 14, 15, 94, 243, 10, 14, 15, 253, 251, 14, 15, 248, 146, 14, 15, 248, + 147, 14, 15, 245, 213, 14, 15, 244, 195, 14, 15, 252, 218, 14, 15, 252, + 226, 14, 15, 252, 227, 14, 15, 252, 228, 14, 15, 252, 225, 14, 15, 240, + 229, 253, 125, 14, 15, 240, 229, 253, 154, 14, 15, 240, 229, 251, 73, 14, + 15, 240, 229, 253, 140, 14, 15, 240, 229, 251, 89, 14, 15, 240, 229, 253, + 88, 14, 15, 240, 229, 249, 68, 14, 15, 240, 229, 164, 14, 15, 241, 225, + 164, 14, 15, 254, 111, 14, 15, 247, 255, 14, 15, 253, 249, 14, 15, 250, + 56, 14, 15, 254, 18, 14, 15, 252, 96, 14, 15, 253, 213, 14, 15, 249, 157, + 14, 15, 209, 14, 15, 245, 206, 14, 15, 245, 207, 14, 15, 245, 208, 14, + 15, 245, 205, 14, 15, 94, 253, 150, 14, 15, 94, 253, 169, 14, 15, 94, + 253, 144, 14, 15, 94, 253, 92, 14, 15, 243, 242, 14, 15, 252, 19, 14, 15, + 247, 152, 14, 15, 249, 110, 14, 15, 247, 153, 14, 15, 249, 13, 14, 15, + 247, 144, 14, 15, 253, 244, 14, 15, 252, 31, 14, 15, 242, 90, 249, 70, + 14, 15, 242, 90, 249, 118, 14, 15, 242, 90, 249, 85, 14, 15, 242, 90, + 249, 7, 14, 15, 237, 131, 249, 63, 14, 15, 237, 131, 249, 73, 14, 15, + 237, 131, 249, 55, 14, 15, 237, 131, 249, 19, 14, 15, 237, 131, 253, 251, + 14, 15, 250, 9, 14, 15, 247, 62, 14, 15, 250, 10, 14, 15, 247, 63, 14, + 15, 249, 144, 14, 15, 247, 60, 14, 15, 254, 134, 14, 15, 240, 231, 249, + 63, 14, 15, 240, 231, 249, 73, 14, 15, 240, 231, 245, 48, 14, 15, 240, + 231, 249, 55, 14, 15, 240, 231, 245, 202, 14, 15, 240, 231, 249, 19, 14, + 15, 240, 231, 245, 8, 14, 15, 240, 231, 253, 251, 14, 15, 241, 95, 199, + 14, 15, 238, 82, 70, 14, 15, 238, 82, 69, 14, 15, 238, 82, 78, 14, 15, + 238, 82, 64, 14, 15, 238, 82, 253, 132, 14, 15, 238, 82, 253, 145, 14, + 15, 238, 82, 253, 138, 14, 15, 238, 82, 253, 98, 14, 15, 238, 82, 253, + 157, 14, 15, 238, 82, 253, 148, 14, 15, 238, 82, 253, 136, 14, 15, 238, + 82, 253, 94, 14, 15, 238, 82, 253, 134, 14, 15, 238, 82, 253, 182, 14, + 15, 238, 82, 253, 155, 14, 15, 238, 82, 208, 14, 15, 242, 90, 253, 125, + 14, 15, 242, 90, 253, 154, 14, 15, 242, 90, 253, 140, 14, 15, 242, 90, + 253, 88, 14, 15, 44, 251, 36, 14, 15, 44, 251, 35, 14, 15, 44, 249, 78, + 14, 15, 44, 246, 153, 14, 15, 44, 251, 34, 14, 15, 44, 249, 36, 14, 15, + 44, 253, 170, 14, 15, 44, 253, 144, 14, 15, 44, 253, 150, 14, 15, 44, + 250, 62, 14, 15, 44, 253, 169, 14, 15, 44, 253, 92, 14, 15, 44, 253, 216, + 14, 15, 44, 253, 138, 14, 15, 44, 253, 132, 14, 15, 44, 250, 109, 14, 15, + 44, 253, 145, 14, 15, 44, 253, 98, 14, 15, 44, 251, 99, 14, 15, 44, 251, + 98, 14, 15, 44, 251, 96, 14, 15, 44, 246, 235, 14, 15, 44, 251, 97, 14, + 15, 44, 251, 95, 14, 15, 44, 250, 87, 14, 15, 44, 249, 55, 14, 15, 44, + 249, 63, 14, 15, 44, 245, 9, 14, 15, 44, 249, 73, 14, 15, 44, 249, 19, + 14, 15, 44, 252, 219, 14, 15, 44, 250, 97, 14, 15, 44, 250, 95, 14, 15, + 44, 248, 144, 14, 15, 44, 250, 96, 14, 15, 44, 249, 22, 14, 15, 44, 253, + 231, 14, 15, 44, 253, 187, 14, 15, 44, 253, 143, 14, 15, 44, 249, 114, + 14, 15, 44, 253, 116, 14, 15, 44, 253, 90, 14, 15, 44, 198, 14, 15, 44, + 253, 196, 14, 15, 44, 253, 195, 14, 15, 44, 253, 207, 14, 15, 44, 249, + 229, 14, 15, 44, 253, 209, 14, 15, 44, 253, 100, 14, 15, 44, 251, 152, + 14, 15, 44, 250, 0, 14, 15, 44, 249, 255, 14, 15, 44, 247, 32, 14, 15, + 44, 251, 151, 14, 15, 44, 249, 24, 14, 15, 44, 251, 163, 14, 15, 44, 251, + 162, 14, 15, 44, 251, 160, 14, 15, 44, 247, 37, 14, 15, 44, 251, 161, 14, + 15, 44, 251, 159, 14, 15, 44, 254, 51, 14, 15, 44, 253, 112, 14, 15, 44, + 253, 136, 14, 15, 44, 253, 157, 14, 15, 44, 250, 26, 14, 15, 44, 253, + 148, 14, 15, 44, 253, 94, 14, 15, 44, 253, 114, 14, 15, 44, 253, 141, 14, + 15, 44, 253, 167, 14, 15, 44, 250, 18, 14, 15, 44, 253, 156, 14, 15, 44, + 221, 14, 15, 44, 253, 124, 14, 15, 44, 253, 123, 14, 15, 44, 253, 200, + 14, 15, 44, 249, 125, 14, 15, 44, 253, 139, 14, 15, 44, 216, 14, 15, 44, + 253, 241, 14, 15, 242, 90, 253, 241, 14, 15, 44, 253, 240, 14, 15, 44, + 253, 239, 14, 15, 44, 249, 104, 14, 15, 44, 253, 228, 14, 15, 242, 90, + 253, 228, 14, 15, 44, 253, 118, 14, 15, 44, 249, 250, 14, 15, 44, 249, + 249, 14, 15, 44, 251, 127, 14, 15, 44, 247, 5, 14, 15, 44, 249, 248, 14, + 15, 44, 251, 126, 14, 15, 44, 253, 229, 14, 15, 44, 253, 184, 14, 15, 44, + 253, 210, 14, 15, 44, 250, 8, 14, 15, 44, 253, 211, 14, 15, 44, 253, 103, + 14, 15, 44, 250, 224, 14, 15, 44, 250, 223, 14, 15, 44, 250, 221, 14, 15, + 44, 246, 111, 14, 15, 44, 250, 222, 14, 15, 44, 249, 214, 14, 15, 44, + 251, 194, 14, 15, 44, 250, 10, 14, 15, 44, 251, 193, 14, 15, 44, 247, 61, + 14, 15, 44, 250, 9, 14, 15, 44, 249, 144, 14, 15, 44, 248, 134, 14, 15, + 44, 245, 208, 14, 15, 44, 245, 206, 14, 15, 44, 244, 122, 14, 15, 44, + 245, 207, 14, 15, 44, 245, 205, 14, 15, 44, 250, 93, 14, 15, 44, 250, 92, + 14, 15, 44, 250, 90, 14, 15, 44, 248, 133, 14, 15, 44, 250, 91, 14, 15, + 44, 250, 89, 14, 15, 44, 253, 233, 14, 15, 44, 253, 190, 14, 15, 44, 253, + 189, 14, 15, 44, 249, 126, 14, 15, 44, 253, 218, 14, 15, 44, 253, 129, + 14, 15, 44, 202, 14, 15, 44, 46, 202, 14, 15, 44, 250, 240, 14, 15, 44, + 250, 239, 14, 15, 44, 249, 76, 14, 15, 44, 246, 119, 14, 15, 44, 250, + 238, 14, 15, 44, 249, 100, 14, 15, 44, 253, 172, 14, 15, 44, 253, 159, + 14, 15, 44, 253, 171, 14, 15, 44, 249, 117, 14, 15, 44, 253, 158, 14, 15, + 44, 253, 97, 14, 15, 44, 249, 162, 14, 15, 44, 249, 85, 14, 15, 44, 249, + 70, 14, 15, 44, 245, 182, 14, 15, 44, 249, 118, 14, 15, 44, 249, 7, 14, + 15, 44, 254, 21, 14, 15, 44, 249, 171, 14, 15, 44, 249, 170, 14, 15, 44, + 249, 64, 14, 15, 44, 245, 210, 14, 15, 44, 249, 169, 14, 15, 44, 249, 56, + 14, 15, 44, 249, 131, 14, 15, 44, 249, 94, 14, 15, 44, 249, 44, 14, 15, + 44, 244, 246, 14, 15, 44, 249, 23, 14, 15, 44, 249, 11, 14, 15, 44, 248, + 152, 14, 15, 44, 248, 151, 14, 15, 44, 248, 149, 14, 15, 44, 244, 126, + 14, 15, 44, 248, 150, 14, 15, 44, 248, 148, 14, 15, 254, 31, 57, 14, 15, + 213, 189, 14, 15, 250, 54, 14, 15, 247, 145, 14, 15, 247, 177, 14, 15, + 244, 0, 14, 15, 247, 178, 14, 15, 244, 1, 14, 15, 247, 176, 14, 15, 243, + 255, 244, 174, 248, 81, 65, 244, 174, 1, 243, 43, 244, 174, 1, 247, 69, + 244, 174, 1, 243, 107, 244, 174, 1, 248, 50, 244, 174, 1, 247, 160, 244, + 174, 1, 248, 161, 244, 174, 1, 246, 80, 244, 174, 1, 244, 120, 244, 174, + 1, 246, 73, 244, 174, 1, 243, 58, 244, 174, 1, 247, 101, 244, 174, 1, + 246, 159, 244, 174, 1, 240, 125, 244, 174, 1, 242, 22, 244, 174, 1, 248, + 42, 244, 174, 1, 245, 239, 244, 174, 1, 250, 38, 244, 174, 1, 254, 198, + 244, 174, 1, 240, 62, 244, 174, 1, 240, 93, 244, 174, 1, 240, 61, 244, + 174, 1, 253, 186, 244, 174, 1, 239, 106, 244, 174, 1, 246, 249, 244, 174, + 1, 236, 127, 244, 174, 1, 244, 30, 244, 174, 241, 54, 65, 244, 174, 224, + 241, 54, 65, 100, 1, 251, 1, 251, 3, 255, 7, 204, 100, 1, 186, 100, 1, + 252, 245, 255, 25, 84, 100, 1, 254, 80, 100, 1, 181, 100, 1, 201, 100, 1, + 240, 182, 248, 125, 242, 189, 100, 1, 249, 77, 100, 1, 245, 247, 64, 100, + 1, 255, 18, 78, 100, 1, 255, 1, 64, 100, 1, 245, 237, 100, 1, 235, 178, + 78, 100, 1, 235, 228, 78, 100, 1, 78, 100, 1, 253, 142, 100, 1, 253, 246, + 100, 1, 248, 18, 254, 20, 252, 120, 135, 100, 1, 243, 185, 100, 1, 250, + 181, 100, 1, 251, 144, 205, 100, 1, 194, 100, 1, 249, 66, 100, 1, 251, + 29, 251, 55, 194, 100, 1, 251, 25, 100, 1, 248, 175, 253, 22, 201, 100, + 1, 243, 140, 164, 100, 1, 246, 169, 164, 100, 1, 231, 254, 164, 100, 1, + 232, 11, 164, 100, 1, 243, 235, 254, 162, 252, 0, 183, 100, 1, 235, 184, + 183, 100, 1, 239, 5, 100, 1, 251, 115, 255, 10, 254, 118, 69, 100, 1, 70, + 100, 1, 247, 1, 200, 100, 1, 251, 30, 100, 1, 235, 222, 253, 163, 100, 1, + 236, 32, 64, 100, 1, 251, 116, 250, 245, 100, 1, 244, 33, 244, 32, 198, + 100, 1, 245, 243, 243, 101, 100, 1, 244, 94, 159, 100, 1, 248, 75, 235, + 179, 159, 100, 1, 235, 229, 159, 100, 1, 203, 100, 1, 202, 100, 1, 248, + 132, 254, 193, 254, 195, 190, 100, 1, 235, 230, 190, 100, 1, 187, 100, 1, + 240, 7, 246, 23, 241, 115, 199, 100, 1, 232, 1, 199, 100, 1, 239, 6, 100, + 1, 241, 228, 100, 1, 246, 121, 255, 5, 70, 100, 1, 243, 212, 254, 55, + 171, 100, 1, 234, 54, 171, 100, 1, 235, 183, 171, 100, 1, 243, 201, 251, + 181, 251, 203, 148, 100, 1, 239, 4, 100, 1, 241, 185, 100, 1, 251, 111, + 100, 1, 246, 78, 250, 204, 187, 100, 1, 241, 231, 246, 125, 78, 100, 1, + 249, 218, 100, 1, 251, 113, 100, 1, 240, 22, 100, 1, 246, 40, 100, 1, + 243, 57, 100, 1, 248, 103, 100, 1, 235, 180, 100, 1, 235, 231, 100, 1, + 236, 22, 100, 1, 209, 100, 1, 170, 100, 242, 66, 236, 52, 100, 240, 120, + 236, 52, 100, 244, 234, 236, 52, 100, 243, 34, 90, 100, 240, 187, 90, + 100, 240, 6, 90, 240, 209, 1, 64, 240, 209, 1, 69, 240, 209, 1, 84, 240, + 209, 1, 208, 240, 209, 1, 253, 100, 240, 209, 1, 249, 13, 240, 209, 1, + 253, 91, 240, 209, 1, 253, 93, 240, 209, 1, 253, 94, 240, 209, 1, 253, + 90, 240, 209, 1, 253, 110, 240, 209, 1, 221, 240, 209, 1, 216, 240, 209, + 1, 253, 103, 240, 209, 1, 253, 98, 240, 209, 1, 253, 97, 240, 209, 1, + 253, 88, 240, 209, 38, 25, 69, 240, 209, 38, 25, 84, 240, 209, 25, 240, + 220, 240, 206, 1, 64, 240, 206, 1, 69, 240, 206, 1, 84, 240, 206, 1, 208, + 240, 206, 1, 253, 100, 240, 206, 1, 249, 13, 240, 206, 1, 253, 91, 240, + 206, 1, 253, 93, 240, 206, 1, 253, 94, 240, 206, 1, 253, 90, 240, 206, 1, + 253, 110, 240, 206, 1, 221, 240, 206, 1, 216, 240, 206, 1, 253, 92, 240, + 206, 1, 253, 103, 240, 206, 1, 253, 98, 240, 206, 1, 253, 97, 240, 206, + 1, 253, 88, 240, 206, 38, 25, 69, 240, 206, 38, 25, 84, 240, 206, 25, + 239, 58, 237, 162, 242, 66, 236, 52, 237, 162, 42, 236, 52, 244, 179, 1, + 64, 244, 179, 1, 69, 244, 179, 1, 84, 244, 179, 1, 208, 244, 179, 1, 253, + 100, 244, 179, 1, 249, 13, 244, 179, 1, 253, 91, 244, 179, 1, 253, 93, + 244, 179, 1, 253, 94, 244, 179, 1, 253, 90, 244, 179, 1, 253, 110, 244, + 179, 1, 221, 244, 179, 1, 216, 244, 179, 1, 253, 92, 244, 179, 1, 253, + 103, 244, 179, 1, 253, 98, 244, 179, 1, 253, 97, 244, 179, 1, 253, 88, + 244, 179, 38, 25, 69, 244, 179, 38, 25, 84, 239, 122, 1, 64, 239, 122, 1, + 69, 239, 122, 1, 84, 239, 122, 1, 208, 239, 122, 1, 253, 100, 239, 122, + 1, 249, 13, 239, 122, 1, 253, 91, 239, 122, 1, 253, 93, 239, 122, 1, 253, + 94, 239, 122, 1, 253, 90, 239, 122, 1, 253, 110, 239, 122, 1, 221, 239, + 122, 1, 216, 239, 122, 1, 253, 103, 239, 122, 1, 253, 98, 239, 122, 1, + 253, 97, 239, 122, 38, 25, 69, 239, 122, 38, 25, 84, 74, 1, 208, 74, 1, + 249, 24, 74, 1, 253, 155, 74, 1, 250, 0, 74, 1, 249, 110, 74, 1, 253, + 115, 74, 1, 249, 11, 74, 1, 253, 178, 74, 1, 249, 94, 74, 1, 249, 84, 74, + 1, 253, 93, 74, 1, 244, 195, 74, 1, 253, 193, 74, 1, 245, 213, 74, 1, + 252, 32, 74, 1, 253, 91, 74, 1, 249, 19, 74, 1, 76, 74, 1, 249, 55, 74, + 1, 253, 136, 74, 1, 253, 110, 74, 1, 249, 29, 74, 1, 253, 187, 74, 1, + 250, 44, 74, 1, 253, 141, 74, 1, 253, 123, 74, 1, 253, 144, 74, 1, 253, + 184, 74, 1, 253, 215, 74, 1, 249, 7, 74, 1, 250, 76, 74, 1, 253, 97, 74, + 1, 253, 88, 74, 1, 253, 103, 74, 1, 253, 213, 74, 236, 183, 38, 252, 83, + 74, 236, 183, 38, 249, 157, 74, 236, 183, 38, 253, 249, 74, 236, 183, 38, + 250, 56, 74, 236, 183, 38, 254, 19, 74, 236, 183, 38, 252, 102, 74, 236, + 183, 38, 250, 59, 74, 236, 183, 38, 248, 13, 74, 236, 183, 38, 254, 70, + 74, 236, 183, 38, 252, 150, 74, 236, 183, 38, 254, 54, 74, 236, 183, 38, + 251, 216, 74, 236, 183, 38, 254, 67, 74, 236, 183, 38, 250, 54, 74, 236, + 183, 38, 254, 68, 249, 173, 121, 74, 236, 183, 38, 254, 68, 249, 173, + 114, 74, 236, 183, 38, 252, 84, 74, 38, 239, 217, 254, 84, 74, 38, 239, + 217, 253, 95, 74, 38, 25, 253, 95, 74, 38, 25, 69, 74, 38, 25, 253, 96, + 74, 38, 25, 181, 74, 38, 25, 254, 26, 74, 38, 25, 84, 74, 38, 25, 253, + 104, 74, 38, 25, 254, 196, 74, 38, 25, 253, 142, 74, 38, 25, 216, 74, 38, + 25, 253, 181, 74, 38, 25, 70, 74, 38, 25, 253, 163, 74, 38, 25, 253, 106, + 74, 38, 25, 253, 119, 74, 38, 25, 253, 109, 74, 25, 240, 126, 74, 25, + 240, 151, 74, 25, 235, 234, 74, 25, 236, 145, 74, 25, 240, 186, 74, 25, + 241, 109, 74, 25, 244, 62, 74, 25, 236, 175, 74, 25, 240, 97, 74, 25, + 243, 27, 74, 25, 244, 70, 241, 254, 74, 25, 242, 52, 74, 25, 243, 72, 74, + 25, 236, 248, 74, 25, 247, 31, 74, 25, 236, 247, 74, 25, 243, 55, 254, + 169, 247, 43, 74, 25, 254, 133, 249, 167, 74, 25, 241, 113, 74, 25, 244, + 35, 247, 100, 74, 25, 240, 101, 74, 239, 147, 13, 248, 22, 74, 25, 235, + 211, 74, 27, 244, 173, 74, 27, 121, 74, 27, 114, 74, 27, 153, 74, 27, + 163, 74, 27, 168, 74, 27, 169, 74, 27, 179, 74, 27, 176, 74, 27, 178, 51, + 243, 45, 51, 238, 96, 51, 242, 84, 51, 213, 189, 51, 242, 81, 51, 249, + 28, 244, 215, 51, 249, 1, 249, 119, 240, 241, 51, 249, 8, 4, 240, 9, 241, + 205, 51, 242, 68, 242, 84, 51, 242, 68, 213, 189, 51, 241, 221, 51, 249, + 135, 238, 79, 239, 195, 121, 51, 249, 135, 238, 79, 239, 195, 114, 51, + 249, 135, 238, 79, 239, 195, 153, 51, 38, 237, 114, 51, 27, 244, 173, 51, + 27, 121, 51, 27, 114, 51, 27, 153, 51, 27, 163, 51, 27, 168, 51, 27, 169, + 51, 27, 179, 51, 27, 176, 51, 27, 178, 51, 1, 64, 51, 1, 70, 51, 1, 69, + 51, 1, 78, 51, 1, 84, 51, 1, 253, 142, 51, 1, 253, 253, 51, 1, 253, 126, + 51, 1, 253, 94, 51, 1, 249, 75, 51, 1, 253, 110, 51, 1, 253, 90, 51, 1, + 253, 213, 51, 1, 253, 100, 51, 1, 221, 51, 1, 253, 103, 51, 1, 253, 97, + 51, 1, 249, 7, 51, 1, 253, 91, 51, 1, 253, 93, 51, 1, 249, 11, 51, 1, + 253, 118, 51, 1, 216, 51, 1, 253, 92, 51, 1, 253, 98, 51, 1, 253, 153, + 51, 1, 208, 51, 1, 249, 24, 51, 1, 249, 56, 51, 1, 253, 129, 51, 1, 249, + 68, 51, 1, 253, 72, 51, 1, 249, 144, 51, 1, 250, 125, 51, 1, 249, 23, 51, + 1, 249, 1, 165, 38, 57, 51, 1, 249, 1, 70, 51, 1, 249, 1, 69, 51, 1, 249, + 1, 78, 51, 1, 249, 1, 84, 51, 1, 249, 1, 253, 142, 51, 1, 249, 1, 253, + 253, 51, 1, 249, 1, 249, 75, 51, 1, 249, 1, 253, 110, 51, 1, 249, 1, 253, + 90, 51, 1, 249, 1, 253, 213, 51, 1, 249, 1, 253, 100, 51, 1, 249, 1, 221, + 51, 1, 249, 1, 253, 91, 51, 1, 249, 1, 253, 93, 51, 1, 249, 1, 249, 11, + 51, 1, 249, 1, 253, 118, 51, 1, 249, 1, 249, 56, 51, 1, 249, 1, 216, 51, + 1, 249, 1, 253, 98, 51, 1, 249, 1, 208, 51, 1, 249, 1, 249, 136, 51, 1, + 249, 1, 249, 68, 51, 1, 249, 1, 251, 121, 51, 1, 249, 1, 252, 21, 51, 1, + 249, 1, 249, 100, 51, 1, 249, 8, 70, 51, 1, 249, 8, 69, 51, 1, 249, 8, + 254, 122, 51, 1, 249, 8, 253, 253, 51, 1, 249, 8, 84, 51, 1, 249, 8, 249, + 75, 51, 1, 249, 8, 208, 51, 1, 249, 8, 253, 100, 51, 1, 249, 8, 253, 88, + 51, 1, 249, 8, 253, 90, 51, 1, 249, 8, 249, 7, 51, 1, 249, 8, 253, 91, + 51, 1, 249, 8, 253, 93, 51, 1, 249, 8, 253, 118, 51, 1, 249, 8, 253, 153, + 51, 1, 249, 8, 249, 136, 51, 1, 249, 8, 249, 68, 51, 1, 249, 8, 249, 56, + 51, 1, 249, 8, 253, 129, 51, 1, 249, 8, 249, 115, 51, 1, 249, 8, 249, 11, + 51, 1, 249, 8, 249, 52, 51, 1, 242, 68, 69, 51, 1, 242, 68, 208, 51, 1, + 242, 68, 253, 92, 51, 1, 242, 68, 253, 153, 51, 1, 242, 68, 249, 52, 51, + 1, 192, 195, 239, 252, 121, 51, 1, 192, 195, 242, 53, 121, 51, 1, 192, + 195, 241, 134, 51, 1, 192, 195, 239, 244, 51, 1, 192, 195, 239, 116, 239, + 244, 51, 1, 192, 195, 241, 40, 51, 1, 192, 195, 244, 171, 241, 40, 51, 1, + 192, 195, 64, 51, 1, 192, 195, 69, 51, 1, 192, 195, 208, 51, 1, 192, 195, + 249, 13, 51, 1, 192, 195, 253, 115, 51, 1, 192, 195, 249, 22, 51, 1, 192, + 195, 244, 195, 51, 1, 192, 195, 249, 27, 51, 1, 192, 195, 249, 37, 51, 1, + 192, 195, 253, 91, 51, 1, 192, 195, 253, 93, 51, 1, 192, 195, 253, 90, + 51, 1, 192, 195, 249, 29, 51, 1, 192, 195, 249, 34, 51, 1, 192, 195, 249, + 52, 51, 1, 192, 195, 253, 129, 51, 1, 192, 195, 253, 219, 51, 1, 249, 1, + 192, 195, 253, 91, 51, 1, 249, 1, 192, 195, 249, 52, 51, 1, 242, 68, 192, + 195, 249, 36, 51, 1, 242, 68, 192, 195, 249, 13, 51, 1, 242, 68, 192, + 195, 253, 115, 51, 1, 242, 68, 192, 195, 249, 45, 51, 1, 242, 68, 192, + 195, 249, 22, 51, 1, 242, 68, 192, 195, 244, 200, 51, 1, 242, 68, 192, + 195, 253, 91, 51, 1, 242, 68, 192, 195, 249, 40, 51, 1, 242, 68, 192, + 195, 249, 34, 51, 1, 242, 68, 192, 195, 250, 199, 51, 1, 242, 68, 192, + 195, 249, 52, 51, 1, 242, 68, 192, 195, 253, 129, 51, 1, 192, 195, 136, + 84, 51, 1, 192, 195, 136, 216, 51, 1, 242, 68, 192, 195, 249, 39, 51, 1, + 192, 195, 240, 23, 9, 5, 1, 119, 2, 249, 9, 45, 9, 3, 1, 119, 2, 249, 9, + 45, 9, 5, 1, 255, 41, 2, 49, 47, 9, 3, 1, 255, 41, 2, 49, 47, 9, 5, 1, + 255, 41, 2, 49, 45, 9, 3, 1, 255, 41, 2, 49, 45, 9, 5, 1, 255, 41, 2, + 217, 45, 9, 3, 1, 255, 41, 2, 217, 45, 9, 5, 1, 255, 36, 2, 240, 250, 18, + 116, 9, 3, 1, 255, 36, 2, 240, 250, 18, 116, 9, 5, 1, 255, 34, 2, 49, 47, + 9, 3, 1, 255, 34, 2, 49, 47, 9, 5, 1, 255, 34, 2, 49, 45, 9, 3, 1, 255, + 34, 2, 49, 45, 9, 5, 1, 255, 34, 2, 217, 45, 9, 3, 1, 255, 34, 2, 217, + 45, 9, 5, 1, 255, 34, 2, 239, 117, 9, 3, 1, 255, 34, 2, 239, 117, 9, 5, + 1, 255, 34, 2, 157, 45, 9, 3, 1, 255, 34, 2, 157, 45, 9, 5, 1, 144, 2, + 242, 101, 18, 158, 9, 3, 1, 144, 2, 242, 101, 18, 158, 9, 5, 1, 144, 2, + 242, 101, 18, 116, 9, 3, 1, 144, 2, 242, 101, 18, 116, 9, 5, 1, 144, 2, + 157, 45, 9, 3, 1, 144, 2, 157, 45, 9, 5, 1, 144, 2, 229, 45, 9, 3, 1, + 144, 2, 229, 45, 9, 5, 1, 144, 2, 240, 250, 18, 212, 9, 3, 1, 144, 2, + 240, 250, 18, 212, 9, 5, 1, 255, 42, 2, 49, 47, 9, 3, 1, 255, 42, 2, 49, + 47, 9, 5, 1, 255, 32, 2, 166, 9, 3, 1, 255, 32, 2, 166, 9, 5, 1, 255, 37, + 2, 49, 47, 9, 3, 1, 255, 37, 2, 49, 47, 9, 5, 1, 255, 37, 2, 49, 45, 9, + 3, 1, 255, 37, 2, 49, 45, 9, 5, 1, 255, 37, 2, 150, 9, 3, 1, 255, 37, 2, + 150, 9, 5, 1, 255, 37, 2, 239, 117, 9, 3, 1, 255, 37, 2, 239, 117, 9, 5, + 1, 255, 37, 2, 244, 194, 45, 9, 3, 1, 255, 37, 2, 244, 194, 45, 9, 5, 1, + 197, 2, 229, 45, 9, 3, 1, 197, 2, 229, 45, 9, 5, 1, 197, 2, 238, 81, 18, + 116, 9, 3, 1, 197, 2, 238, 81, 18, 116, 9, 5, 1, 255, 38, 2, 116, 9, 3, + 1, 255, 38, 2, 116, 9, 5, 1, 255, 38, 2, 49, 45, 9, 3, 1, 255, 38, 2, 49, + 45, 9, 5, 1, 255, 38, 2, 217, 45, 9, 3, 1, 255, 38, 2, 217, 45, 9, 5, 1, + 255, 31, 2, 49, 45, 9, 3, 1, 255, 31, 2, 49, 45, 9, 5, 1, 255, 31, 2, 49, + 244, 188, 18, 166, 9, 3, 1, 255, 31, 2, 49, 244, 188, 18, 166, 9, 5, 1, + 255, 31, 2, 217, 45, 9, 3, 1, 255, 31, 2, 217, 45, 9, 5, 1, 255, 31, 2, + 157, 45, 9, 3, 1, 255, 31, 2, 157, 45, 9, 5, 1, 255, 39, 2, 116, 9, 3, 1, + 255, 39, 2, 116, 9, 5, 1, 255, 39, 2, 49, 47, 9, 3, 1, 255, 39, 2, 49, + 47, 9, 5, 1, 255, 39, 2, 49, 45, 9, 3, 1, 255, 39, 2, 49, 45, 9, 5, 1, + 255, 28, 2, 49, 47, 9, 3, 1, 255, 28, 2, 49, 47, 9, 5, 1, 255, 28, 2, 49, + 45, 9, 3, 1, 255, 28, 2, 49, 45, 9, 5, 1, 255, 28, 2, 217, 45, 9, 3, 1, + 255, 28, 2, 217, 45, 9, 5, 1, 255, 28, 2, 157, 45, 9, 3, 1, 255, 28, 2, + 157, 45, 9, 5, 1, 109, 2, 229, 18, 116, 9, 3, 1, 109, 2, 229, 18, 116, 9, + 5, 1, 109, 2, 229, 18, 150, 9, 3, 1, 109, 2, 229, 18, 150, 9, 5, 1, 109, + 2, 242, 101, 18, 158, 9, 3, 1, 109, 2, 242, 101, 18, 158, 9, 5, 1, 109, + 2, 242, 101, 18, 116, 9, 3, 1, 109, 2, 242, 101, 18, 116, 9, 5, 1, 255, + 46, 2, 116, 9, 3, 1, 255, 46, 2, 116, 9, 5, 1, 255, 46, 2, 49, 47, 9, 3, + 1, 255, 46, 2, 49, 47, 9, 5, 1, 255, 30, 2, 49, 47, 9, 3, 1, 255, 30, 2, + 49, 47, 9, 5, 1, 255, 30, 2, 49, 45, 9, 3, 1, 255, 30, 2, 49, 45, 9, 5, + 1, 255, 30, 2, 49, 244, 188, 18, 166, 9, 3, 1, 255, 30, 2, 49, 244, 188, + 18, 166, 9, 5, 1, 255, 30, 2, 217, 45, 9, 3, 1, 255, 30, 2, 217, 45, 9, + 5, 1, 255, 29, 2, 49, 47, 9, 3, 1, 255, 29, 2, 49, 47, 9, 5, 1, 255, 29, + 2, 49, 45, 9, 3, 1, 255, 29, 2, 49, 45, 9, 5, 1, 255, 29, 2, 244, 196, + 18, 49, 47, 9, 3, 1, 255, 29, 2, 244, 196, 18, 49, 47, 9, 5, 1, 255, 29, + 2, 245, 15, 18, 49, 47, 9, 3, 1, 255, 29, 2, 245, 15, 18, 49, 47, 9, 5, + 1, 255, 29, 2, 49, 244, 188, 18, 49, 47, 9, 3, 1, 255, 29, 2, 49, 244, + 188, 18, 49, 47, 9, 5, 1, 255, 33, 2, 49, 47, 9, 3, 1, 255, 33, 2, 49, + 47, 9, 5, 1, 255, 33, 2, 49, 45, 9, 3, 1, 255, 33, 2, 49, 45, 9, 5, 1, + 255, 33, 2, 217, 45, 9, 3, 1, 255, 33, 2, 217, 45, 9, 5, 1, 255, 33, 2, + 157, 45, 9, 3, 1, 255, 33, 2, 157, 45, 9, 5, 1, 115, 2, 238, 81, 45, 9, + 3, 1, 115, 2, 238, 81, 45, 9, 5, 1, 115, 2, 229, 45, 9, 3, 1, 115, 2, + 229, 45, 9, 5, 1, 115, 2, 157, 45, 9, 3, 1, 115, 2, 157, 45, 9, 5, 1, + 115, 2, 229, 18, 116, 9, 3, 1, 115, 2, 229, 18, 116, 9, 5, 1, 115, 2, + 242, 101, 18, 150, 9, 3, 1, 115, 2, 242, 101, 18, 150, 9, 5, 1, 255, 44, + 2, 180, 9, 3, 1, 255, 44, 2, 180, 9, 5, 1, 255, 44, 2, 49, 45, 9, 3, 1, + 255, 44, 2, 49, 45, 9, 5, 1, 255, 40, 2, 158, 9, 3, 1, 255, 40, 2, 158, + 9, 5, 1, 255, 40, 2, 116, 9, 3, 1, 255, 40, 2, 116, 9, 5, 1, 255, 40, 2, + 150, 9, 3, 1, 255, 40, 2, 150, 9, 5, 1, 255, 40, 2, 49, 47, 9, 3, 1, 255, + 40, 2, 49, 47, 9, 5, 1, 255, 40, 2, 49, 45, 9, 3, 1, 255, 40, 2, 49, 45, + 9, 5, 1, 255, 43, 2, 49, 47, 9, 3, 1, 255, 43, 2, 49, 47, 9, 5, 1, 255, + 43, 2, 150, 9, 3, 1, 255, 43, 2, 150, 9, 5, 1, 255, 35, 2, 49, 47, 9, 3, + 1, 255, 35, 2, 49, 47, 9, 5, 1, 255, 27, 2, 236, 180, 9, 3, 1, 255, 27, + 2, 236, 180, 9, 5, 1, 255, 27, 2, 49, 45, 9, 3, 1, 255, 27, 2, 49, 45, 9, + 5, 1, 255, 27, 2, 217, 45, 9, 3, 1, 255, 27, 2, 217, 45, 9, 3, 1, 255, + 37, 2, 217, 45, 9, 3, 1, 255, 33, 2, 150, 9, 3, 1, 255, 40, 2, 249, 9, + 47, 9, 3, 1, 255, 35, 2, 249, 9, 47, 9, 3, 1, 119, 2, 37, 136, 244, 183, + 9, 3, 1, 165, 255, 29, 2, 49, 47, 9, 5, 1, 119, 2, 49, 45, 9, 3, 1, 119, + 2, 49, 45, 9, 5, 1, 119, 2, 248, 255, 47, 9, 3, 1, 119, 2, 248, 255, 47, + 9, 5, 1, 119, 2, 157, 18, 116, 9, 3, 1, 119, 2, 157, 18, 116, 9, 5, 1, + 119, 2, 157, 18, 158, 9, 3, 1, 119, 2, 157, 18, 158, 9, 5, 1, 119, 2, + 157, 18, 248, 255, 47, 9, 3, 1, 119, 2, 157, 18, 248, 255, 47, 9, 5, 1, + 119, 2, 157, 18, 180, 9, 3, 1, 119, 2, 157, 18, 180, 9, 5, 1, 119, 2, + 157, 18, 49, 45, 9, 3, 1, 119, 2, 157, 18, 49, 45, 9, 5, 1, 119, 2, 244, + 194, 18, 116, 9, 3, 1, 119, 2, 244, 194, 18, 116, 9, 5, 1, 119, 2, 244, + 194, 18, 158, 9, 3, 1, 119, 2, 244, 194, 18, 158, 9, 5, 1, 119, 2, 244, + 194, 18, 248, 255, 47, 9, 3, 1, 119, 2, 244, 194, 18, 248, 255, 47, 9, 5, + 1, 119, 2, 244, 194, 18, 180, 9, 3, 1, 119, 2, 244, 194, 18, 180, 9, 5, + 1, 119, 2, 244, 194, 18, 49, 45, 9, 3, 1, 119, 2, 244, 194, 18, 49, 45, + 9, 5, 1, 144, 2, 49, 45, 9, 3, 1, 144, 2, 49, 45, 9, 5, 1, 144, 2, 248, + 255, 47, 9, 3, 1, 144, 2, 248, 255, 47, 9, 5, 1, 144, 2, 180, 9, 3, 1, + 144, 2, 180, 9, 5, 1, 144, 2, 157, 18, 116, 9, 3, 1, 144, 2, 157, 18, + 116, 9, 5, 1, 144, 2, 157, 18, 158, 9, 3, 1, 144, 2, 157, 18, 158, 9, 5, + 1, 144, 2, 157, 18, 248, 255, 47, 9, 3, 1, 144, 2, 157, 18, 248, 255, 47, + 9, 5, 1, 144, 2, 157, 18, 180, 9, 3, 1, 144, 2, 157, 18, 180, 9, 5, 1, + 144, 2, 157, 18, 49, 45, 9, 3, 1, 144, 2, 157, 18, 49, 45, 9, 5, 1, 197, + 2, 248, 255, 47, 9, 3, 1, 197, 2, 248, 255, 47, 9, 5, 1, 197, 2, 49, 45, + 9, 3, 1, 197, 2, 49, 45, 9, 5, 1, 109, 2, 49, 45, 9, 3, 1, 109, 2, 49, + 45, 9, 5, 1, 109, 2, 248, 255, 47, 9, 3, 1, 109, 2, 248, 255, 47, 9, 5, + 1, 109, 2, 157, 18, 116, 9, 3, 1, 109, 2, 157, 18, 116, 9, 5, 1, 109, 2, + 157, 18, 158, 9, 3, 1, 109, 2, 157, 18, 158, 9, 5, 1, 109, 2, 157, 18, + 248, 255, 47, 9, 3, 1, 109, 2, 157, 18, 248, 255, 47, 9, 5, 1, 109, 2, + 157, 18, 180, 9, 3, 1, 109, 2, 157, 18, 180, 9, 5, 1, 109, 2, 157, 18, + 49, 45, 9, 3, 1, 109, 2, 157, 18, 49, 45, 9, 5, 1, 109, 2, 249, 12, 18, + 116, 9, 3, 1, 109, 2, 249, 12, 18, 116, 9, 5, 1, 109, 2, 249, 12, 18, + 158, 9, 3, 1, 109, 2, 249, 12, 18, 158, 9, 5, 1, 109, 2, 249, 12, 18, + 248, 255, 47, 9, 3, 1, 109, 2, 249, 12, 18, 248, 255, 47, 9, 5, 1, 109, + 2, 249, 12, 18, 180, 9, 3, 1, 109, 2, 249, 12, 18, 180, 9, 5, 1, 109, 2, + 249, 12, 18, 49, 45, 9, 3, 1, 109, 2, 249, 12, 18, 49, 45, 9, 5, 1, 115, + 2, 49, 45, 9, 3, 1, 115, 2, 49, 45, 9, 5, 1, 115, 2, 248, 255, 47, 9, 3, + 1, 115, 2, 248, 255, 47, 9, 5, 1, 115, 2, 249, 12, 18, 116, 9, 3, 1, 115, + 2, 249, 12, 18, 116, 9, 5, 1, 115, 2, 249, 12, 18, 158, 9, 3, 1, 115, 2, + 249, 12, 18, 158, 9, 5, 1, 115, 2, 249, 12, 18, 248, 255, 47, 9, 3, 1, + 115, 2, 249, 12, 18, 248, 255, 47, 9, 5, 1, 115, 2, 249, 12, 18, 180, 9, + 3, 1, 115, 2, 249, 12, 18, 180, 9, 5, 1, 115, 2, 249, 12, 18, 49, 45, 9, + 3, 1, 115, 2, 249, 12, 18, 49, 45, 9, 5, 1, 255, 35, 2, 158, 9, 3, 1, + 255, 35, 2, 158, 9, 5, 1, 255, 35, 2, 49, 45, 9, 3, 1, 255, 35, 2, 49, + 45, 9, 5, 1, 255, 35, 2, 248, 255, 47, 9, 3, 1, 255, 35, 2, 248, 255, 47, + 9, 5, 1, 255, 35, 2, 180, 9, 3, 1, 255, 35, 2, 180, 26, 3, 1, 162, 2, + 242, 78, 26, 3, 1, 162, 2, 242, 74, 26, 3, 1, 162, 2, 138, 18, 174, 26, + 3, 1, 162, 2, 130, 18, 174, 26, 3, 1, 162, 2, 138, 18, 173, 26, 3, 1, + 162, 2, 130, 18, 173, 26, 3, 1, 162, 2, 138, 18, 236, 43, 26, 3, 1, 162, + 2, 130, 18, 236, 43, 26, 5, 1, 162, 2, 242, 78, 26, 5, 1, 162, 2, 242, + 74, 26, 5, 1, 162, 2, 138, 18, 174, 26, 5, 1, 162, 2, 130, 18, 174, 26, + 5, 1, 162, 2, 138, 18, 173, 26, 5, 1, 162, 2, 130, 18, 173, 26, 5, 1, + 162, 2, 138, 18, 236, 43, 26, 5, 1, 162, 2, 130, 18, 236, 43, 26, 3, 1, + 240, 204, 2, 242, 78, 26, 3, 1, 240, 204, 2, 242, 74, 26, 3, 1, 240, 204, + 2, 138, 18, 174, 26, 3, 1, 240, 204, 2, 130, 18, 174, 26, 3, 1, 240, 204, + 2, 138, 18, 173, 26, 3, 1, 240, 204, 2, 130, 18, 173, 26, 5, 1, 240, 204, + 2, 242, 78, 26, 5, 1, 240, 204, 2, 242, 74, 26, 5, 1, 240, 204, 2, 138, + 18, 174, 26, 5, 1, 240, 204, 2, 130, 18, 174, 26, 5, 1, 240, 204, 2, 138, + 18, 173, 26, 5, 1, 240, 204, 2, 130, 18, 173, 26, 3, 1, 182, 2, 242, 78, + 26, 3, 1, 182, 2, 242, 74, 26, 3, 1, 182, 2, 138, 18, 174, 26, 3, 1, 182, + 2, 130, 18, 174, 26, 3, 1, 182, 2, 138, 18, 173, 26, 3, 1, 182, 2, 130, + 18, 173, 26, 3, 1, 182, 2, 138, 18, 236, 43, 26, 3, 1, 182, 2, 130, 18, + 236, 43, 26, 5, 1, 182, 2, 242, 78, 26, 5, 1, 182, 2, 242, 74, 26, 5, 1, + 182, 2, 138, 18, 174, 26, 5, 1, 182, 2, 130, 18, 174, 26, 5, 1, 182, 2, + 138, 18, 173, 26, 5, 1, 182, 2, 130, 18, 173, 26, 5, 1, 182, 2, 138, 18, + 236, 43, 26, 5, 1, 182, 2, 130, 18, 236, 43, 26, 3, 1, 228, 2, 242, 78, + 26, 3, 1, 228, 2, 242, 74, 26, 3, 1, 228, 2, 138, 18, 174, 26, 3, 1, 228, + 2, 130, 18, 174, 26, 3, 1, 228, 2, 138, 18, 173, 26, 3, 1, 228, 2, 130, + 18, 173, 26, 3, 1, 228, 2, 138, 18, 236, 43, 26, 3, 1, 228, 2, 130, 18, + 236, 43, 26, 5, 1, 228, 2, 242, 78, 26, 5, 1, 228, 2, 242, 74, 26, 5, 1, + 228, 2, 138, 18, 174, 26, 5, 1, 228, 2, 130, 18, 174, 26, 5, 1, 228, 2, + 138, 18, 173, 26, 5, 1, 228, 2, 130, 18, 173, 26, 5, 1, 228, 2, 138, 18, + 236, 43, 26, 5, 1, 228, 2, 130, 18, 236, 43, 26, 3, 1, 240, 210, 2, 242, + 78, 26, 3, 1, 240, 210, 2, 242, 74, 26, 3, 1, 240, 210, 2, 138, 18, 174, + 26, 3, 1, 240, 210, 2, 130, 18, 174, 26, 3, 1, 240, 210, 2, 138, 18, 173, + 26, 3, 1, 240, 210, 2, 130, 18, 173, 26, 5, 1, 240, 210, 2, 242, 78, 26, + 5, 1, 240, 210, 2, 242, 74, 26, 5, 1, 240, 210, 2, 138, 18, 174, 26, 5, + 1, 240, 210, 2, 130, 18, 174, 26, 5, 1, 240, 210, 2, 138, 18, 173, 26, 5, + 1, 240, 210, 2, 130, 18, 173, 26, 3, 1, 240, 199, 2, 242, 78, 26, 3, 1, + 240, 199, 2, 242, 74, 26, 3, 1, 240, 199, 2, 138, 18, 174, 26, 3, 1, 240, + 199, 2, 130, 18, 174, 26, 3, 1, 240, 199, 2, 138, 18, 173, 26, 3, 1, 240, + 199, 2, 130, 18, 173, 26, 3, 1, 240, 199, 2, 138, 18, 236, 43, 26, 3, 1, + 240, 199, 2, 130, 18, 236, 43, 26, 5, 1, 240, 199, 2, 242, 74, 26, 5, 1, + 240, 199, 2, 130, 18, 174, 26, 5, 1, 240, 199, 2, 130, 18, 173, 26, 5, 1, + 240, 199, 2, 130, 18, 236, 43, 26, 3, 1, 172, 2, 242, 78, 26, 3, 1, 172, + 2, 242, 74, 26, 3, 1, 172, 2, 138, 18, 174, 26, 3, 1, 172, 2, 130, 18, + 174, 26, 3, 1, 172, 2, 138, 18, 173, 26, 3, 1, 172, 2, 130, 18, 173, 26, + 3, 1, 172, 2, 138, 18, 236, 43, 26, 3, 1, 172, 2, 130, 18, 236, 43, 26, + 5, 1, 172, 2, 242, 78, 26, 5, 1, 172, 2, 242, 74, 26, 5, 1, 172, 2, 138, + 18, 174, 26, 5, 1, 172, 2, 130, 18, 174, 26, 5, 1, 172, 2, 138, 18, 173, + 26, 5, 1, 172, 2, 130, 18, 173, 26, 5, 1, 172, 2, 138, 18, 236, 43, 26, + 5, 1, 172, 2, 130, 18, 236, 43, 26, 3, 1, 162, 2, 174, 26, 3, 1, 162, 2, + 173, 26, 3, 1, 240, 204, 2, 174, 26, 3, 1, 240, 204, 2, 173, 26, 3, 1, + 182, 2, 174, 26, 3, 1, 182, 2, 173, 26, 3, 1, 228, 2, 174, 26, 3, 1, 228, + 2, 173, 26, 3, 1, 240, 210, 2, 174, 26, 3, 1, 240, 210, 2, 173, 26, 3, 1, + 240, 199, 2, 174, 26, 3, 1, 240, 199, 2, 173, 26, 3, 1, 172, 2, 174, 26, + 3, 1, 172, 2, 173, 26, 3, 1, 162, 2, 138, 18, 211, 26, 3, 1, 162, 2, 130, + 18, 211, 26, 3, 1, 162, 2, 138, 18, 244, 193, 18, 211, 26, 3, 1, 162, 2, + 130, 18, 244, 193, 18, 211, 26, 3, 1, 162, 2, 138, 18, 249, 26, 18, 211, + 26, 3, 1, 162, 2, 130, 18, 249, 26, 18, 211, 26, 3, 1, 162, 2, 138, 18, + 236, 184, 18, 211, 26, 3, 1, 162, 2, 130, 18, 236, 184, 18, 211, 26, 5, + 1, 162, 2, 138, 18, 234, 62, 26, 5, 1, 162, 2, 130, 18, 234, 62, 26, 5, + 1, 162, 2, 138, 18, 244, 193, 18, 234, 62, 26, 5, 1, 162, 2, 130, 18, + 244, 193, 18, 234, 62, 26, 5, 1, 162, 2, 138, 18, 249, 26, 18, 234, 62, + 26, 5, 1, 162, 2, 130, 18, 249, 26, 18, 234, 62, 26, 5, 1, 162, 2, 138, + 18, 236, 184, 18, 234, 62, 26, 5, 1, 162, 2, 130, 18, 236, 184, 18, 234, + 62, 26, 3, 1, 182, 2, 138, 18, 211, 26, 3, 1, 182, 2, 130, 18, 211, 26, + 3, 1, 182, 2, 138, 18, 244, 193, 18, 211, 26, 3, 1, 182, 2, 130, 18, 244, + 193, 18, 211, 26, 3, 1, 182, 2, 138, 18, 249, 26, 18, 211, 26, 3, 1, 182, + 2, 130, 18, 249, 26, 18, 211, 26, 3, 1, 182, 2, 138, 18, 236, 184, 18, + 211, 26, 3, 1, 182, 2, 130, 18, 236, 184, 18, 211, 26, 5, 1, 182, 2, 138, + 18, 234, 62, 26, 5, 1, 182, 2, 130, 18, 234, 62, 26, 5, 1, 182, 2, 138, + 18, 244, 193, 18, 234, 62, 26, 5, 1, 182, 2, 130, 18, 244, 193, 18, 234, + 62, 26, 5, 1, 182, 2, 138, 18, 249, 26, 18, 234, 62, 26, 5, 1, 182, 2, + 130, 18, 249, 26, 18, 234, 62, 26, 5, 1, 182, 2, 138, 18, 236, 184, 18, + 234, 62, 26, 5, 1, 182, 2, 130, 18, 236, 184, 18, 234, 62, 26, 3, 1, 172, + 2, 138, 18, 211, 26, 3, 1, 172, 2, 130, 18, 211, 26, 3, 1, 172, 2, 138, + 18, 244, 193, 18, 211, 26, 3, 1, 172, 2, 130, 18, 244, 193, 18, 211, 26, + 3, 1, 172, 2, 138, 18, 249, 26, 18, 211, 26, 3, 1, 172, 2, 130, 18, 249, + 26, 18, 211, 26, 3, 1, 172, 2, 138, 18, 236, 184, 18, 211, 26, 3, 1, 172, + 2, 130, 18, 236, 184, 18, 211, 26, 5, 1, 172, 2, 138, 18, 234, 62, 26, 5, + 1, 172, 2, 130, 18, 234, 62, 26, 5, 1, 172, 2, 138, 18, 244, 193, 18, + 234, 62, 26, 5, 1, 172, 2, 130, 18, 244, 193, 18, 234, 62, 26, 5, 1, 172, + 2, 138, 18, 249, 26, 18, 234, 62, 26, 5, 1, 172, 2, 130, 18, 249, 26, 18, + 234, 62, 26, 5, 1, 172, 2, 138, 18, 236, 184, 18, 234, 62, 26, 5, 1, 172, + 2, 130, 18, 236, 184, 18, 234, 62, 26, 3, 1, 162, 2, 240, 245, 26, 3, 1, + 162, 2, 166, 26, 3, 1, 162, 2, 244, 193, 18, 211, 26, 3, 1, 162, 2, 211, + 26, 3, 1, 162, 2, 249, 26, 18, 211, 26, 3, 1, 162, 2, 236, 43, 26, 3, 1, + 162, 2, 236, 184, 18, 211, 26, 5, 1, 162, 2, 240, 245, 26, 5, 1, 162, 2, + 166, 26, 5, 1, 162, 2, 174, 26, 5, 1, 162, 2, 173, 26, 5, 1, 162, 2, 234, + 62, 26, 239, 186, 26, 234, 62, 26, 242, 78, 26, 236, 43, 26, 238, 85, 18, + 236, 43, 26, 3, 1, 182, 2, 244, 193, 18, 211, 26, 3, 1, 182, 2, 211, 26, + 3, 1, 182, 2, 249, 26, 18, 211, 26, 3, 1, 182, 2, 236, 43, 26, 3, 1, 182, + 2, 236, 184, 18, 211, 26, 5, 1, 240, 204, 2, 174, 26, 5, 1, 240, 204, 2, + 173, 26, 5, 1, 182, 2, 174, 26, 5, 1, 182, 2, 173, 26, 5, 1, 182, 2, 234, + 62, 26, 138, 18, 174, 26, 138, 18, 173, 26, 138, 18, 236, 43, 26, 3, 1, + 228, 2, 240, 245, 26, 3, 1, 228, 2, 166, 26, 3, 1, 228, 2, 238, 85, 18, + 174, 26, 3, 1, 228, 2, 238, 85, 18, 173, 26, 3, 1, 228, 2, 236, 43, 26, + 3, 1, 228, 2, 238, 85, 18, 236, 43, 26, 5, 1, 228, 2, 240, 245, 26, 5, 1, + 228, 2, 166, 26, 5, 1, 228, 2, 174, 26, 5, 1, 228, 2, 173, 26, 130, 18, + 174, 26, 130, 18, 173, 26, 130, 18, 236, 43, 26, 3, 1, 240, 199, 2, 240, + 245, 26, 3, 1, 240, 199, 2, 166, 26, 3, 1, 240, 199, 2, 238, 85, 18, 174, + 26, 3, 1, 240, 199, 2, 238, 85, 18, 173, 26, 3, 1, 253, 168, 2, 242, 78, + 26, 3, 1, 253, 168, 2, 242, 74, 26, 3, 1, 240, 199, 2, 236, 43, 26, 3, 1, + 240, 199, 2, 238, 85, 18, 236, 43, 26, 5, 1, 240, 199, 2, 240, 245, 26, + 5, 1, 240, 199, 2, 166, 26, 5, 1, 240, 199, 2, 174, 26, 5, 1, 240, 199, + 2, 173, 26, 5, 1, 253, 168, 2, 242, 74, 26, 238, 85, 18, 174, 26, 238, + 85, 18, 173, 26, 174, 26, 3, 1, 172, 2, 244, 193, 18, 211, 26, 3, 1, 172, + 2, 211, 26, 3, 1, 172, 2, 249, 26, 18, 211, 26, 3, 1, 172, 2, 236, 43, + 26, 3, 1, 172, 2, 236, 184, 18, 211, 26, 5, 1, 240, 210, 2, 174, 26, 5, + 1, 240, 210, 2, 173, 26, 5, 1, 172, 2, 174, 26, 5, 1, 172, 2, 173, 26, 5, + 1, 172, 2, 234, 62, 26, 173, 26, 242, 74, 254, 215, 244, 238, 254, 220, + 244, 238, 254, 215, 242, 69, 254, 220, 242, 69, 236, 174, 242, 69, 237, + 63, 242, 69, 238, 61, 242, 69, 242, 211, 242, 69, 236, 191, 242, 69, 252, + 206, 242, 69, 251, 57, 242, 69, 249, 127, 245, 12, 242, 69, 249, 127, + 245, 12, 237, 77, 249, 127, 245, 12, 241, 22, 235, 245, 65, 235, 248, 65, + 240, 241, 236, 149, 240, 241, 242, 211, 244, 189, 254, 215, 244, 189, + 254, 220, 244, 189, 146, 143, 42, 55, 244, 176, 42, 253, 113, 244, 176, + 36, 242, 66, 238, 76, 65, 37, 242, 66, 238, 76, 65, 242, 66, 245, 135, + 238, 76, 65, 242, 66, 234, 67, 238, 76, 65, 36, 42, 238, 76, 65, 37, 42, + 238, 76, 65, 42, 245, 135, 238, 76, 65, 42, 234, 67, 238, 76, 65, 241, + 50, 42, 241, 50, 240, 215, 237, 144, 240, 215, 253, 101, 49, 241, 25, + 225, 49, 241, 25, 146, 238, 96, 237, 67, 242, 239, 217, 237, 114, 238, + 113, 237, 114, 235, 245, 237, 154, 235, 248, 237, 154, 254, 171, 237, 1, + 237, 62, 235, 245, 238, 148, 235, 248, 238, 148, 243, 234, 239, 190, 242, + 69, 254, 66, 247, 96, 57, 254, 66, 253, 250, 239, 243, 57, 242, 107, 42, + 242, 107, 242, 59, 242, 107, 224, 242, 107, 224, 42, 242, 107, 224, 242, + 59, 242, 107, 242, 174, 242, 66, 235, 237, 160, 238, 76, 65, 242, 66, + 235, 190, 160, 238, 76, 65, 239, 72, 65, 42, 236, 186, 65, 236, 140, 238, + 98, 239, 109, 92, 249, 89, 244, 225, 238, 144, 242, 239, 238, 180, 243, + 171, 240, 215, 239, 123, 242, 91, 36, 30, 226, 2, 242, 242, 37, 30, 226, + 2, 242, 242, 42, 239, 126, 65, 239, 126, 236, 186, 65, 236, 186, 239, + 126, 65, 240, 184, 25, 254, 11, 224, 241, 74, 57, 86, 126, 240, 215, 86, + 63, 240, 215, 253, 113, 238, 77, 224, 237, 123, 246, 71, 253, 131, 225, + 238, 179, 241, 96, 237, 108, 237, 127, 244, 197, 57, 238, 94, 242, 107, + 242, 82, 8, 242, 69, 253, 19, 241, 22, 240, 80, 236, 85, 238, 145, 242, + 167, 238, 145, 237, 114, 241, 58, 238, 161, 238, 160, 239, 199, 238, 161, + 238, 160, 241, 58, 10, 249, 3, 239, 235, 239, 199, 10, 249, 3, 239, 235, + 240, 121, 27, 241, 77, 241, 223, 27, 241, 77, 236, 181, 244, 173, 236, + 181, 9, 3, 1, 69, 236, 181, 163, 236, 181, 168, 236, 181, 169, 236, 181, + 179, 236, 181, 176, 236, 181, 178, 236, 181, 249, 4, 57, 236, 181, 242, + 119, 236, 181, 242, 61, 57, 236, 181, 36, 236, 47, 236, 181, 37, 236, 47, + 236, 181, 9, 3, 1, 183, 238, 83, 244, 173, 238, 83, 121, 238, 83, 114, + 238, 83, 153, 238, 83, 163, 238, 83, 168, 238, 83, 169, 238, 83, 179, + 238, 83, 176, 238, 83, 178, 238, 83, 249, 4, 57, 238, 83, 242, 119, 238, + 83, 242, 61, 57, 238, 83, 36, 236, 47, 238, 83, 37, 236, 47, 236, 20, 57, + 244, 214, 57, 240, 20, 57, 243, 118, 246, 132, 57, 251, 199, 57, 251, + 234, 57, 247, 108, 57, 244, 34, 57, 245, 40, 57, 254, 76, 57, 254, 153, + 244, 77, 57, 250, 225, 57, 250, 248, 57, 254, 126, 57, 244, 127, 57, 241, + 150, 57, 243, 126, 247, 240, 57, 252, 62, 57, 48, 36, 154, 47, 48, 37, + 154, 47, 48, 165, 55, 217, 239, 131, 48, 207, 55, 217, 239, 131, 48, 235, + 236, 59, 47, 48, 238, 87, 59, 47, 48, 36, 59, 47, 48, 37, 59, 47, 48, + 249, 9, 239, 131, 48, 238, 87, 249, 9, 239, 131, 48, 235, 236, 249, 9, + 239, 131, 48, 244, 171, 248, 253, 47, 48, 249, 28, 248, 253, 47, 48, 238, + 97, 240, 200, 48, 238, 97, 240, 207, 48, 238, 97, 239, 132, 48, 238, 97, + 175, 237, 132, 48, 36, 37, 59, 47, 48, 238, 97, 242, 0, 48, 238, 97, 241, + 191, 48, 238, 97, 244, 135, 239, 119, 219, 48, 240, 219, 240, 230, 239, + 131, 48, 42, 55, 242, 114, 239, 131, 48, 241, 98, 90, 48, 242, 59, 239, + 108, 48, 249, 51, 242, 153, 47, 48, 126, 59, 239, 131, 241, 37, 253, 128, + 238, 167, 139, 253, 173, 240, 172, 120, 5, 203, 242, 156, 240, 14, 242, + 100, 217, 90, 250, 177, 253, 128, 250, 174, 252, 237, 246, 127, 238, 136, + 240, 160, 242, 156, 237, 61, 71, 3, 194, 71, 5, 164, 236, 51, 5, 164, + 120, 5, 164, 242, 238, 238, 136, 242, 238, 240, 17, 249, 25, 225, 253, + 116, 71, 5, 69, 236, 51, 5, 69, 71, 5, 148, 71, 3, 148, 255, 31, 255, 41, + 253, 99, 90, 120, 5, 183, 244, 7, 57, 244, 205, 239, 70, 237, 183, 71, 5, + 198, 120, 5, 198, 120, 5, 209, 71, 5, 135, 236, 51, 5, 135, 120, 5, 135, + 236, 157, 248, 116, 239, 75, 242, 8, 65, 238, 132, 57, 248, 136, 167, 57, + 239, 110, 120, 5, 202, 247, 235, 57, 254, 61, 57, 239, 116, 254, 61, 57, + 236, 51, 5, 202, 242, 60, 26, 3, 1, 244, 191, 243, 188, 57, 239, 250, 57, + 71, 5, 199, 236, 51, 5, 203, 239, 9, 90, 71, 3, 70, 71, 5, 70, 71, 5, + 204, 242, 60, 5, 204, 71, 5, 171, 71, 3, 78, 67, 90, 254, 3, 90, 245, 98, + 90, 245, 76, 90, 237, 71, 242, 16, 241, 3, 5, 209, 120, 3, 242, 75, 120, + 5, 242, 75, 120, 5, 253, 116, 120, 244, 186, 237, 164, 242, 60, 23, 5, + 194, 242, 60, 23, 5, 148, 224, 23, 5, 148, 242, 60, 23, 5, 181, 120, 21, + 5, 187, 120, 21, 3, 187, 120, 21, 3, 70, 120, 21, 3, 69, 120, 21, 3, 200, + 240, 146, 244, 176, 242, 60, 237, 124, 242, 83, 239, 123, 253, 101, 244, + 116, 242, 83, 239, 123, 225, 242, 35, 242, 83, 239, 123, 253, 101, 243, + 111, 242, 83, 239, 123, 225, 241, 20, 242, 83, 239, 123, 244, 171, 241, + 20, 242, 83, 239, 123, 249, 28, 241, 20, 242, 83, 239, 123, 253, 101, + 244, 84, 242, 83, 239, 123, 249, 48, 242, 14, 242, 83, 239, 123, 253, + 101, 241, 151, 242, 83, 239, 123, 244, 171, 239, 181, 242, 83, 239, 123, + 249, 48, 239, 181, 242, 83, 239, 123, 245, 4, 239, 181, 239, 123, 238, + 103, 121, 218, 151, 121, 218, 151, 114, 218, 151, 153, 218, 151, 163, + 218, 151, 168, 218, 151, 169, 218, 151, 179, 218, 151, 176, 218, 151, + 178, 218, 151, 249, 18, 218, 151, 240, 234, 218, 151, 240, 238, 218, 151, + 242, 144, 218, 151, 253, 101, 239, 150, 218, 151, 249, 48, 239, 150, 218, + 151, 253, 101, 238, 104, 3, 218, 151, 121, 3, 218, 151, 114, 3, 218, 151, + 153, 3, 218, 151, 163, 3, 218, 151, 168, 3, 218, 151, 169, 3, 218, 151, + 179, 3, 218, 151, 176, 3, 218, 151, 178, 3, 218, 151, 249, 18, 3, 218, + 151, 240, 234, 3, 218, 151, 240, 238, 3, 218, 151, 242, 144, 3, 218, 151, + 253, 101, 239, 150, 3, 218, 151, 249, 48, 239, 150, 3, 218, 151, 253, + 101, 238, 104, 218, 151, 253, 101, 239, 243, 255, 36, 187, 218, 151, 249, + 48, 238, 104, 218, 151, 253, 250, 238, 104, 218, 151, 224, 253, 101, 239, + 150, 126, 52, 210, 52, 63, 52, 220, 52, 36, 37, 52, 75, 79, 52, 244, 177, + 249, 17, 52, 244, 177, 249, 2, 52, 244, 181, 249, 2, 52, 244, 181, 249, + 17, 52, 126, 59, 2, 122, 63, 59, 2, 122, 126, 249, 90, 52, 63, 249, 90, + 52, 126, 225, 242, 159, 52, 210, 225, 242, 159, 52, 63, 225, 242, 159, + 52, 220, 225, 242, 159, 52, 126, 59, 2, 244, 182, 63, 59, 2, 244, 182, + 126, 59, 248, 254, 143, 210, 59, 248, 254, 143, 63, 59, 248, 254, 143, + 220, 59, 248, 254, 143, 75, 79, 59, 2, 245, 253, 126, 59, 2, 128, 63, 59, + 2, 128, 126, 59, 2, 245, 28, 63, 59, 2, 245, 28, 36, 37, 249, 90, 52, 36, + 37, 59, 2, 122, 220, 242, 193, 52, 210, 59, 2, 253, 252, 237, 125, 210, + 59, 2, 253, 252, 236, 195, 220, 59, 2, 253, 252, 237, 125, 220, 59, 2, + 253, 252, 236, 195, 63, 59, 2, 242, 86, 237, 121, 220, 59, 2, 242, 86, + 237, 125, 235, 236, 253, 121, 237, 163, 52, 238, 87, 253, 121, 237, 163, + 52, 244, 177, 249, 17, 59, 139, 165, 143, 126, 59, 139, 253, 99, 249, 25, + 63, 59, 139, 143, 235, 236, 249, 0, 175, 52, 238, 87, 249, 0, 175, 52, + 126, 154, 2, 133, 239, 176, 126, 154, 2, 133, 237, 121, 210, 154, 2, 133, + 236, 195, 210, 154, 2, 133, 237, 125, 63, 154, 2, 133, 239, 176, 63, 154, + 2, 133, 237, 121, 220, 154, 2, 133, 236, 195, 220, 154, 2, 133, 237, 125, + 63, 59, 249, 25, 126, 52, 210, 59, 126, 137, 220, 52, 126, 59, 249, 25, + 63, 52, 126, 242, 161, 240, 246, 210, 242, 161, 240, 246, 63, 242, 161, + 240, 246, 220, 242, 161, 240, 246, 126, 154, 249, 25, 63, 239, 142, 63, + 154, 249, 25, 126, 239, 142, 126, 42, 59, 2, 122, 36, 37, 42, 59, 2, 122, + 63, 42, 59, 2, 122, 126, 42, 52, 210, 42, 52, 63, 42, 52, 220, 42, 52, + 36, 37, 42, 52, 75, 79, 42, 52, 244, 177, 249, 17, 42, 52, 244, 177, 249, + 2, 42, 52, 244, 181, 249, 2, 42, 52, 244, 181, 249, 17, 42, 52, 126, 242, + 59, 52, 63, 242, 59, 52, 126, 239, 196, 52, 63, 239, 196, 52, 210, 59, 2, + 42, 122, 220, 59, 2, 42, 122, 126, 242, 105, 52, 210, 242, 105, 52, 63, + 242, 105, 52, 220, 242, 105, 52, 126, 59, 139, 143, 63, 59, 139, 143, + 126, 58, 52, 210, 58, 52, 63, 58, 52, 220, 58, 52, 210, 58, 59, 248, 254, + 143, 210, 58, 59, 254, 236, 238, 150, 210, 58, 59, 254, 236, 239, 228, 2, + 146, 143, 210, 58, 59, 254, 236, 239, 228, 2, 55, 143, 210, 58, 42, 52, + 210, 58, 42, 59, 254, 236, 238, 150, 63, 58, 59, 248, 254, 248, 170, 244, + 177, 249, 17, 59, 139, 240, 213, 244, 181, 249, 2, 59, 139, 240, 213, 75, + 79, 58, 52, 37, 59, 2, 3, 240, 200, 220, 59, 126, 137, 210, 52, 244, 171, + 63, 240, 246, 126, 59, 2, 55, 122, 63, 59, 2, 55, 122, 36, 37, 59, 2, 55, + 122, 126, 59, 2, 42, 55, 122, 63, 59, 2, 42, 55, 122, 36, 37, 59, 2, 42, + 55, 122, 126, 236, 203, 52, 63, 236, 203, 52, 36, 37, 236, 203, 52, 54, + 249, 185, 236, 251, 240, 243, 235, 240, 245, 201, 241, 155, 245, 201, + 249, 41, 141, 243, 103, 244, 216, 250, 69, 238, 19, 242, 130, 240, 214, + 253, 128, 141, 255, 2, 240, 214, 253, 128, 3, 240, 214, 253, 128, 239, + 146, 254, 217, 241, 27, 249, 41, 141, 241, 14, 254, 217, 241, 27, 3, 239, + 146, 254, 217, 241, 27, 253, 127, 137, 244, 42, 244, 186, 239, 138, 244, + 186, 237, 152, 244, 186, 237, 164, 244, 197, 57, 236, 28, 57, 49, 244, + 212, 239, 156, 242, 91, 253, 232, 242, 119, 239, 203, 223, 249, 9, 223, + 243, 54, 223, 30, 245, 39, 250, 194, 245, 39, 242, 214, 245, 39, 236, + 158, 76, 238, 121, 37, 242, 117, 242, 117, 239, 136, 242, 117, 238, 128, + 242, 117, 240, 31, 249, 41, 141, 241, 52, 239, 160, 76, 141, 239, 160, + 76, 240, 202, 249, 46, 240, 202, 253, 186, 235, 238, 242, 109, 238, 86, + 42, 238, 86, 242, 59, 238, 86, 241, 15, 238, 86, 242, 27, 238, 86, 244, + 142, 238, 86, 238, 87, 238, 86, 238, 87, 241, 15, 238, 86, 235, 236, 241, + 15, 238, 86, 238, 67, 240, 5, 244, 53, 236, 225, 49, 242, 119, 241, 154, + 239, 24, 236, 225, 237, 66, 229, 223, 224, 180, 239, 116, 251, 187, 159, + 252, 166, 245, 11, 244, 149, 239, 138, 141, 180, 244, 197, 180, 235, 199, + 80, 76, 141, 235, 199, 80, 76, 235, 239, 80, 76, 235, 239, 253, 177, 141, + 239, 200, 80, 76, 240, 223, 235, 239, 253, 137, 239, 200, 80, 76, 242, + 93, 80, 76, 141, 242, 93, 80, 76, 242, 93, 80, 112, 80, 76, 242, 59, 180, + 254, 1, 80, 76, 237, 113, 76, 235, 252, 237, 113, 76, 237, 187, 239, 204, + 237, 174, 253, 173, 243, 204, 235, 252, 80, 76, 235, 239, 80, 139, 112, + 253, 173, 244, 211, 253, 128, 244, 211, 137, 112, 235, 239, 80, 76, 244, + 214, 240, 254, 242, 61, 242, 81, 249, 9, 254, 214, 80, 76, 249, 9, 80, + 76, 236, 253, 76, 238, 5, 237, 60, 76, 249, 86, 240, 254, 245, 20, 80, + 76, 80, 139, 254, 216, 236, 255, 239, 136, 254, 30, 238, 50, 80, 76, 141, + 80, 76, 238, 111, 76, 141, 238, 111, 76, 240, 171, 237, 113, 76, 206, + 112, 80, 76, 196, 112, 80, 76, 206, 249, 25, 80, 76, 196, 249, 25, 80, + 76, 206, 253, 177, 141, 80, 76, 196, 253, 177, 141, 80, 76, 249, 108, + 237, 110, 249, 108, 235, 235, 239, 204, 141, 237, 113, 76, 141, 237, 110, + 141, 235, 235, 240, 223, 206, 253, 137, 80, 76, 240, 223, 196, 253, 137, + 80, 76, 206, 112, 237, 113, 76, 196, 112, 237, 113, 76, 240, 223, 206, + 253, 137, 237, 113, 76, 240, 223, 196, 253, 137, 237, 113, 76, 206, 112, + 235, 235, 196, 112, 237, 110, 240, 223, 206, 253, 137, 235, 235, 240, + 223, 196, 253, 137, 237, 110, 238, 126, 238, 130, 239, 143, 112, 80, 76, + 239, 145, 112, 80, 76, 239, 143, 112, 237, 113, 76, 239, 145, 112, 237, + 113, 76, 249, 41, 141, 240, 142, 249, 41, 141, 240, 173, 242, 76, 253, + 128, 239, 121, 253, 128, 141, 119, 242, 76, 253, 128, 141, 119, 239, 121, + 253, 128, 242, 76, 137, 112, 80, 76, 239, 121, 137, 112, 80, 76, 240, + 223, 119, 242, 76, 137, 253, 137, 80, 76, 240, 223, 119, 239, 121, 137, + 253, 137, 80, 76, 242, 76, 137, 2, 141, 80, 76, 239, 121, 137, 2, 141, + 80, 76, 239, 50, 239, 225, 235, 181, 239, 225, 242, 109, 30, 244, 211, + 253, 128, 30, 239, 169, 253, 128, 30, 244, 211, 137, 112, 80, 76, 30, + 239, 169, 137, 112, 80, 76, 30, 249, 195, 30, 249, 202, 28, 244, 212, 28, + 242, 119, 28, 242, 167, 28, 239, 156, 242, 91, 28, 49, 223, 28, 249, 9, + 223, 28, 239, 203, 223, 28, 240, 254, 28, 244, 189, 240, 227, 244, 212, + 240, 227, 242, 119, 240, 227, 242, 167, 240, 227, 49, 223, 37, 244, 184, + 36, 244, 184, 79, 244, 184, 75, 244, 184, 237, 175, 241, 216, 245, 209, + 241, 172, 242, 59, 55, 253, 99, 37, 237, 119, 42, 55, 253, 99, 42, 37, + 237, 119, 249, 41, 141, 244, 43, 141, 245, 209, 249, 41, 141, 243, 116, + 241, 68, 42, 55, 253, 99, 42, 37, 237, 119, 239, 143, 245, 217, 238, 114, + 239, 145, 245, 217, 238, 114, 242, 103, 239, 164, 253, 128, 239, 146, + 254, 217, 242, 103, 238, 159, 242, 103, 239, 164, 137, 112, 80, 76, 239, + 146, 254, 217, 242, 103, 239, 164, 112, 80, 76, 239, 169, 253, 128, 244, + 211, 253, 128, 238, 122, 239, 27, 238, 193, 241, 207, 236, 139, 253, 30, + 247, 109, 252, 34, 37, 160, 2, 249, 65, 37, 219, 244, 186, 240, 202, 249, + 46, 244, 186, 240, 202, 253, 186, 244, 186, 235, 238, 244, 186, 242, 109, + 240, 221, 223, 49, 223, 249, 86, 223, 239, 156, 242, 167, 241, 45, 36, + 242, 103, 242, 210, 237, 128, 239, 138, 37, 242, 103, 242, 210, 237, 128, + 239, 138, 36, 237, 128, 239, 138, 37, 237, 128, 239, 138, 224, 229, 240, + 254, 244, 175, 240, 202, 253, 186, 244, 175, 240, 202, 249, 46, 42, 240, + 237, 42, 238, 116, 42, 235, 238, 42, 242, 109, 238, 43, 80, 18, 239, 160, + 76, 206, 2, 227, 196, 2, 227, 250, 104, 249, 108, 237, 110, 250, 104, + 249, 108, 235, 235, 206, 80, 139, 112, 235, 235, 196, 80, 139, 112, 237, + 110, 80, 139, 112, 237, 110, 80, 139, 112, 235, 235, 80, 139, 112, 238, + 126, 80, 139, 112, 238, 130, 249, 41, 141, 241, 242, 112, 242, 88, 249, + 41, 141, 242, 26, 112, 242, 88, 141, 30, 244, 211, 137, 112, 80, 76, 141, + 30, 239, 169, 137, 112, 80, 76, 30, 244, 211, 137, 112, 141, 80, 76, 30, + 239, 169, 137, 112, 141, 80, 76, 206, 253, 177, 141, 237, 113, 76, 196, + 253, 177, 141, 237, 113, 76, 239, 143, 253, 177, 141, 237, 113, 76, 239, + 145, 253, 177, 141, 237, 113, 76, 141, 242, 103, 239, 164, 253, 128, 249, + 41, 141, 241, 14, 254, 217, 242, 103, 238, 159, 141, 242, 103, 239, 164, + 137, 112, 80, 76, 249, 41, 141, 241, 14, 254, 217, 242, 103, 239, 164, + 112, 242, 88, 55, 238, 96, 241, 213, 146, 238, 96, 75, 37, 239, 125, 238, + 96, 79, 37, 239, 125, 238, 96, 240, 214, 137, 2, 165, 146, 122, 240, 214, + 137, 2, 55, 253, 99, 254, 228, 253, 127, 137, 146, 122, 3, 240, 214, 137, + 2, 55, 253, 99, 254, 228, 253, 127, 137, 146, 122, 240, 214, 137, 2, 49, + 47, 240, 214, 137, 2, 239, 130, 3, 240, 214, 137, 2, 239, 130, 240, 214, + 137, 2, 238, 75, 240, 214, 137, 2, 225, 146, 239, 240, 239, 146, 2, 165, + 146, 122, 239, 146, 2, 55, 253, 99, 254, 228, 253, 127, 137, 146, 122, 3, + 239, 146, 2, 55, 253, 99, 254, 228, 253, 127, 137, 146, 122, 239, 146, 2, + 239, 130, 3, 239, 146, 2, 239, 130, 255, 27, 239, 129, 254, 87, 237, 75, + 240, 25, 57, 240, 67, 52, 243, 163, 75, 237, 116, 79, 237, 116, 237, 82, + 236, 152, 249, 57, 244, 176, 36, 239, 207, 37, 239, 207, 36, 242, 212, + 37, 242, 212, 242, 80, 37, 244, 247, 242, 80, 36, 244, 247, 253, 121, 37, + 244, 247, 253, 121, 36, 244, 247, 224, 141, 57, 30, 239, 188, 249, 65, + 240, 161, 242, 6, 238, 132, 239, 69, 240, 141, 237, 136, 240, 193, 240, + 207, 245, 162, 137, 240, 92, 57, 242, 60, 141, 57, 244, 145, 237, 140, + 253, 121, 36, 240, 213, 253, 121, 37, 240, 213, 242, 80, 36, 240, 213, + 242, 80, 37, 240, 213, 253, 121, 136, 238, 86, 242, 80, 136, 238, 86, + 243, 119, 244, 90, 75, 238, 177, 241, 112, 225, 146, 245, 252, 244, 20, + 251, 150, 245, 81, 139, 253, 173, 184, 255, 43, 254, 214, 119, 239, 71, + 249, 47, 239, 37, 235, 237, 160, 110, 235, 190, 160, 110, 245, 81, 139, + 253, 173, 244, 172, 241, 111, 244, 183, 236, 8, 254, 1, 234, 69, 239, 99, + 248, 135, 241, 250, 238, 203, 241, 230, 241, 129, 244, 110, 244, 88, 236, + 92, 236, 93, 123, 124, 13, 241, 183, 123, 124, 13, 244, 99, 244, 238, + 123, 124, 13, 249, 14, 242, 88, 123, 124, 13, 249, 14, 241, 52, 123, 124, + 13, 249, 14, 239, 132, 123, 124, 13, 249, 14, 249, 69, 123, 124, 13, 249, + 14, 240, 200, 123, 124, 13, 175, 242, 150, 123, 124, 13, 175, 249, 69, + 123, 124, 13, 248, 80, 143, 123, 124, 13, 238, 181, 143, 123, 124, 13, + 249, 14, 242, 91, 123, 124, 13, 249, 14, 237, 132, 123, 124, 13, 249, 14, + 237, 110, 123, 124, 13, 249, 14, 235, 235, 123, 124, 13, 126, 245, 10, + 123, 124, 13, 63, 245, 10, 123, 124, 13, 249, 14, 126, 52, 123, 124, 13, + 249, 14, 63, 52, 123, 124, 13, 175, 237, 132, 123, 124, 13, 79, 249, 35, + 238, 75, 123, 124, 13, 245, 20, 242, 150, 123, 124, 13, 249, 14, 79, 242, + 174, 123, 124, 13, 249, 14, 242, 87, 123, 124, 13, 79, 249, 35, 249, 69, + 123, 124, 13, 210, 245, 10, 123, 124, 13, 249, 14, 210, 52, 123, 124, 13, + 75, 249, 35, 239, 130, 123, 124, 13, 254, 6, 242, 150, 123, 124, 13, 249, + 14, 75, 242, 174, 123, 124, 13, 249, 14, 250, 213, 123, 124, 13, 75, 249, + 35, 249, 69, 123, 124, 13, 220, 245, 10, 123, 124, 13, 249, 14, 220, 52, + 123, 124, 13, 245, 166, 238, 75, 123, 124, 13, 245, 20, 238, 75, 123, + 124, 13, 240, 221, 238, 75, 123, 124, 13, 254, 48, 238, 75, 123, 124, 13, + 175, 238, 75, 123, 124, 13, 75, 249, 161, 249, 69, 123, 124, 13, 245, + 166, 244, 238, 123, 124, 13, 175, 244, 178, 123, 124, 13, 249, 14, 242, + 81, 123, 124, 13, 75, 249, 35, 150, 123, 124, 13, 254, 6, 150, 123, 124, + 13, 249, 86, 150, 123, 124, 13, 254, 48, 150, 123, 124, 13, 175, 150, + 123, 124, 13, 79, 249, 161, 242, 150, 123, 124, 13, 36, 249, 161, 242, + 150, 123, 124, 13, 229, 150, 123, 124, 13, 196, 150, 123, 124, 13, 244, + 192, 143, 123, 124, 13, 254, 6, 180, 123, 124, 13, 244, 164, 123, 124, + 13, 248, 105, 180, 123, 124, 13, 239, 82, 238, 75, 123, 124, 13, 249, 14, + 141, 242, 88, 123, 124, 13, 249, 14, 239, 68, 123, 124, 13, 79, 244, 225, + 180, 123, 124, 13, 75, 244, 225, 180, 123, 124, 13, 244, 191, 123, 124, + 13, 249, 32, 123, 124, 13, 242, 72, 123, 124, 13, 162, 238, 75, 123, 124, + 13, 240, 204, 238, 75, 123, 124, 13, 228, 238, 75, 123, 124, 13, 172, + 238, 75, 123, 124, 13, 242, 79, 141, 245, 16, 65, 37, 160, 2, 220, 242, + 193, 52, 238, 60, 249, 0, 249, 47, 250, 151, 90, 55, 217, 2, 242, 65, + 227, 238, 144, 90, 237, 182, 238, 165, 90, 236, 11, 238, 165, 90, 239, + 215, 90, 236, 252, 90, 58, 30, 2, 242, 100, 55, 244, 176, 246, 122, 90, + 236, 200, 254, 49, 90, 251, 63, 90, 28, 146, 253, 99, 2, 244, 3, 28, 239, + 118, 244, 185, 242, 173, 175, 2, 239, 52, 52, 252, 238, 90, 238, 34, 90, + 238, 15, 90, 231, 241, 243, 139, 90, 231, 241, 243, 197, 90, 231, 232, + 90, 231, 235, 90, 243, 92, 241, 131, 13, 249, 3, 114, 232, 12, 90, 123, + 124, 13, 244, 238, 241, 51, 238, 158, 254, 49, 90, 240, 144, 245, 156, + 252, 16, 245, 156, 247, 250, 242, 244, 90, 246, 70, 242, 244, 90, 36, + 236, 187, 156, 128, 36, 236, 187, 237, 122, 36, 236, 187, 149, 128, 37, + 236, 187, 156, 128, 37, 236, 187, 237, 122, 37, 236, 187, 149, 128, 36, + 30, 226, 156, 240, 213, 36, 30, 226, 237, 122, 36, 30, 226, 149, 240, + 213, 37, 30, 226, 156, 240, 213, 37, 30, 226, 237, 122, 37, 30, 226, 149, + 240, 213, 36, 244, 175, 226, 156, 128, 36, 244, 175, 226, 242, 65, 242, + 163, 36, 244, 175, 226, 149, 128, 244, 175, 226, 237, 122, 37, 244, 175, + 226, 156, 128, 37, 244, 175, 226, 242, 65, 242, 163, 37, 244, 175, 226, + 149, 128, 239, 135, 237, 122, 146, 217, 237, 122, 156, 36, 112, 149, 37, + 244, 175, 226, 239, 174, 156, 37, 112, 149, 36, 244, 175, 226, 239, 174, + 238, 131, 249, 168, 238, 131, 241, 12, 253, 121, 30, 110, 242, 80, 30, + 110, 242, 80, 30, 226, 249, 25, 253, 121, 30, 110, 22, 13, 241, 12, 36, + 55, 56, 244, 176, 37, 55, 56, 244, 176, 146, 249, 116, 241, 198, 146, + 249, 116, 241, 199, 146, 249, 116, 241, 200, 146, 249, 116, 241, 201, + 238, 84, 13, 117, 55, 18, 253, 121, 184, 238, 84, 13, 117, 55, 18, 242, + 80, 184, 238, 84, 13, 117, 55, 2, 240, 200, 238, 84, 13, 117, 79, 18, + 146, 2, 240, 200, 238, 84, 13, 117, 75, 18, 146, 2, 240, 200, 238, 84, + 13, 117, 55, 2, 219, 238, 84, 13, 117, 79, 18, 146, 2, 219, 238, 84, 13, + 117, 75, 18, 146, 2, 219, 238, 84, 13, 117, 55, 18, 245, 11, 238, 84, 13, + 117, 79, 18, 146, 2, 245, 11, 238, 84, 13, 117, 75, 18, 146, 2, 245, 11, + 238, 84, 13, 117, 79, 18, 236, 182, 238, 84, 13, 117, 75, 18, 236, 182, + 238, 84, 13, 117, 55, 18, 253, 121, 244, 172, 238, 84, 13, 117, 55, 18, + 242, 80, 244, 172, 30, 245, 22, 244, 56, 90, 13, 54, 247, 197, 13, 54, + 245, 45, 137, 240, 86, 13, 54, 245, 45, 137, 245, 200, 13, 54, 253, 127, + 137, 245, 200, 13, 54, 253, 127, 137, 236, 40, 13, 54, 240, 68, 13, 54, + 236, 74, 13, 54, 246, 20, 13, 54, 237, 177, 13, 54, 146, 236, 235, 13, + 54, 217, 245, 83, 13, 54, 55, 236, 235, 13, 54, 249, 3, 245, 83, 13, 54, + 240, 13, 241, 249, 13, 54, 245, 185, 252, 58, 13, 54, 245, 185, 253, 227, + 13, 54, 250, 210, 251, 197, 241, 53, 13, 54, 242, 157, 240, 251, 121, 13, + 54, 242, 157, 240, 251, 114, 13, 54, 242, 157, 240, 251, 153, 13, 54, + 242, 157, 240, 251, 163, 13, 54, 239, 115, 236, 74, 13, 54, 237, 102, + 246, 248, 13, 54, 253, 127, 137, 236, 176, 242, 67, 13, 54, 241, 120, 13, + 54, 253, 127, 137, 241, 209, 13, 54, 237, 101, 13, 54, 241, 53, 13, 54, + 251, 7, 237, 114, 13, 54, 246, 161, 237, 114, 13, 54, 244, 55, 237, 114, + 13, 54, 252, 239, 237, 114, 13, 54, 242, 69, 13, 54, 241, 137, 246, 29, + 90, 249, 0, 249, 47, 13, 54, 240, 124, 13, 54, 243, 90, 249, 3, 114, 13, + 54, 238, 65, 249, 3, 114, 253, 149, 128, 253, 149, 243, 60, 253, 149, + 245, 88, 253, 149, 239, 116, 245, 88, 253, 149, 250, 152, 241, 119, 253, + 149, 254, 89, 249, 89, 253, 149, 243, 49, 250, 140, 234, 72, 253, 149, + 243, 29, 137, 242, 201, 253, 149, 244, 189, 253, 149, 240, 21, 241, 37, + 241, 224, 253, 149, 42, 237, 132, 28, 27, 121, 28, 27, 114, 28, 27, 153, + 28, 27, 163, 28, 27, 168, 28, 27, 169, 28, 27, 179, 28, 27, 176, 28, 27, + 178, 28, 83, 249, 18, 28, 83, 240, 234, 28, 83, 240, 238, 28, 83, 238, + 108, 28, 83, 238, 106, 28, 83, 239, 167, 28, 83, 239, 162, 28, 83, 237, + 129, 28, 83, 238, 105, 28, 83, 238, 107, 28, 83, 240, 239, 68, 27, 121, + 68, 27, 114, 68, 27, 153, 68, 27, 163, 68, 27, 168, 68, 27, 169, 68, 27, + 179, 68, 27, 176, 68, 27, 178, 68, 83, 249, 18, 68, 83, 240, 234, 68, 83, + 240, 238, 68, 83, 238, 108, 68, 83, 238, 106, 68, 83, 239, 167, 68, 83, + 239, 162, 68, 83, 237, 129, 68, 83, 238, 105, 68, 83, 238, 107, 68, 83, + 240, 239, 27, 253, 101, 213, 189, 27, 225, 213, 189, 27, 244, 171, 213, + 189, 27, 249, 28, 213, 189, 27, 249, 48, 213, 189, 27, 254, 23, 213, 189, + 27, 245, 4, 213, 189, 27, 244, 207, 213, 189, 27, 250, 29, 213, 189, 83, + 253, 250, 213, 189, 83, 243, 97, 213, 189, 83, 243, 17, 213, 189, 83, + 240, 180, 213, 189, 83, 240, 78, 213, 189, 83, 241, 166, 213, 189, 83, + 241, 78, 213, 189, 83, 239, 83, 213, 189, 83, 240, 65, 213, 189, 83, 240, + 127, 213, 189, 83, 242, 104, 213, 189, 68, 9, 3, 1, 64, 68, 9, 3, 1, 199, + 68, 9, 3, 1, 203, 68, 9, 3, 1, 187, 68, 9, 3, 1, 70, 68, 9, 3, 1, 204, + 68, 9, 3, 1, 194, 68, 9, 3, 1, 164, 68, 9, 3, 1, 69, 68, 9, 3, 1, 200, + 68, 9, 3, 1, 205, 68, 9, 3, 1, 148, 68, 9, 3, 1, 171, 68, 9, 3, 1, 183, + 68, 9, 3, 1, 78, 68, 9, 3, 1, 198, 68, 9, 3, 1, 209, 68, 9, 3, 1, 135, + 68, 9, 3, 1, 159, 68, 9, 3, 1, 190, 68, 9, 3, 1, 84, 68, 9, 3, 1, 186, + 68, 9, 3, 1, 201, 68, 9, 3, 1, 170, 68, 9, 3, 1, 181, 68, 9, 3, 1, 202, + 28, 9, 5, 1, 64, 28, 9, 5, 1, 199, 28, 9, 5, 1, 203, 28, 9, 5, 1, 187, + 28, 9, 5, 1, 70, 28, 9, 5, 1, 204, 28, 9, 5, 1, 194, 28, 9, 5, 1, 164, + 28, 9, 5, 1, 69, 28, 9, 5, 1, 200, 28, 9, 5, 1, 205, 28, 9, 5, 1, 148, + 28, 9, 5, 1, 171, 28, 9, 5, 1, 183, 28, 9, 5, 1, 78, 28, 9, 5, 1, 198, + 28, 9, 5, 1, 209, 28, 9, 5, 1, 135, 28, 9, 5, 1, 159, 28, 9, 5, 1, 190, + 28, 9, 5, 1, 84, 28, 9, 5, 1, 186, 28, 9, 5, 1, 201, 28, 9, 5, 1, 170, + 28, 9, 5, 1, 181, 28, 9, 5, 1, 202, 28, 9, 3, 1, 64, 28, 9, 3, 1, 199, + 28, 9, 3, 1, 203, 28, 9, 3, 1, 187, 28, 9, 3, 1, 70, 28, 9, 3, 1, 204, + 28, 9, 3, 1, 194, 28, 9, 3, 1, 164, 28, 9, 3, 1, 69, 28, 9, 3, 1, 200, + 28, 9, 3, 1, 205, 28, 9, 3, 1, 148, 28, 9, 3, 1, 171, 28, 9, 3, 1, 183, + 28, 9, 3, 1, 78, 28, 9, 3, 1, 198, 28, 9, 3, 1, 209, 28, 9, 3, 1, 135, + 28, 9, 3, 1, 159, 28, 9, 3, 1, 190, 28, 9, 3, 1, 84, 28, 9, 3, 1, 186, + 28, 9, 3, 1, 201, 28, 9, 3, 1, 170, 28, 9, 3, 1, 181, 28, 9, 3, 1, 202, + 28, 27, 244, 173, 239, 115, 28, 83, 240, 234, 239, 115, 28, 83, 240, 238, + 239, 115, 28, 83, 238, 108, 239, 115, 28, 83, 238, 106, 239, 115, 28, 83, + 239, 167, 239, 115, 28, 83, 239, 162, 239, 115, 28, 83, 237, 129, 239, + 115, 28, 83, 238, 105, 239, 115, 28, 83, 238, 107, 239, 115, 28, 83, 240, + 239, 42, 28, 27, 121, 42, 28, 27, 114, 42, 28, 27, 153, 42, 28, 27, 163, + 42, 28, 27, 168, 42, 28, 27, 169, 42, 28, 27, 179, 42, 28, 27, 176, 42, + 28, 27, 178, 42, 28, 83, 249, 18, 56, 60, 117, 236, 182, 56, 60, 82, 236, + 182, 56, 60, 117, 238, 88, 56, 60, 82, 238, 88, 56, 60, 117, 242, 59, + 249, 15, 236, 182, 56, 60, 82, 242, 59, 249, 15, 236, 182, 56, 60, 117, + 242, 59, 249, 15, 238, 88, 56, 60, 82, 242, 59, 249, 15, 238, 88, 56, 60, + 117, 238, 94, 249, 15, 236, 182, 56, 60, 82, 238, 94, 249, 15, 236, 182, + 56, 60, 117, 238, 94, 249, 15, 238, 88, 56, 60, 82, 238, 94, 249, 15, + 238, 88, 56, 60, 117, 79, 18, 184, 56, 60, 79, 117, 18, 37, 242, 64, 56, + 60, 79, 82, 18, 37, 242, 63, 56, 60, 82, 79, 18, 184, 56, 60, 117, 79, + 18, 244, 172, 56, 60, 79, 117, 18, 36, 242, 64, 56, 60, 79, 82, 18, 36, + 242, 63, 56, 60, 82, 79, 18, 244, 172, 56, 60, 117, 75, 18, 184, 56, 60, + 75, 117, 18, 37, 242, 64, 56, 60, 75, 82, 18, 37, 242, 63, 56, 60, 82, + 75, 18, 184, 56, 60, 117, 75, 18, 244, 172, 56, 60, 75, 117, 18, 36, 242, + 64, 56, 60, 75, 82, 18, 36, 242, 63, 56, 60, 82, 75, 18, 244, 172, 56, + 60, 117, 55, 18, 184, 56, 60, 55, 117, 18, 37, 242, 64, 56, 60, 75, 82, + 18, 37, 79, 242, 63, 56, 60, 79, 82, 18, 37, 75, 242, 63, 56, 60, 55, 82, + 18, 37, 242, 63, 56, 60, 79, 117, 18, 37, 75, 242, 64, 56, 60, 75, 117, + 18, 37, 79, 242, 64, 56, 60, 82, 55, 18, 184, 56, 60, 117, 55, 18, 244, + 172, 56, 60, 55, 117, 18, 36, 242, 64, 56, 60, 75, 82, 18, 36, 79, 242, + 63, 56, 60, 79, 82, 18, 36, 75, 242, 63, 56, 60, 55, 82, 18, 36, 242, 63, + 56, 60, 79, 117, 18, 36, 75, 242, 64, 56, 60, 75, 117, 18, 36, 79, 242, + 64, 56, 60, 82, 55, 18, 244, 172, 56, 60, 117, 79, 18, 236, 182, 56, 60, + 36, 82, 18, 37, 79, 242, 63, 56, 60, 37, 82, 18, 36, 79, 242, 63, 56, 60, + 79, 117, 18, 146, 242, 64, 56, 60, 79, 82, 18, 146, 242, 63, 56, 60, 37, + 117, 18, 36, 79, 242, 64, 56, 60, 36, 117, 18, 37, 79, 242, 64, 56, 60, + 82, 79, 18, 236, 182, 56, 60, 117, 75, 18, 236, 182, 56, 60, 36, 82, 18, + 37, 75, 242, 63, 56, 60, 37, 82, 18, 36, 75, 242, 63, 56, 60, 75, 117, + 18, 146, 242, 64, 56, 60, 75, 82, 18, 146, 242, 63, 56, 60, 37, 117, 18, + 36, 75, 242, 64, 56, 60, 36, 117, 18, 37, 75, 242, 64, 56, 60, 82, 75, + 18, 236, 182, 56, 60, 117, 55, 18, 236, 182, 56, 60, 36, 82, 18, 37, 55, + 242, 63, 56, 60, 37, 82, 18, 36, 55, 242, 63, 56, 60, 55, 117, 18, 146, + 242, 64, 56, 60, 75, 82, 18, 79, 146, 242, 63, 56, 60, 79, 82, 18, 75, + 146, 242, 63, 56, 60, 55, 82, 18, 146, 242, 63, 56, 60, 36, 75, 82, 18, + 37, 79, 242, 63, 56, 60, 37, 75, 82, 18, 36, 79, 242, 63, 56, 60, 36, 79, + 82, 18, 37, 75, 242, 63, 56, 60, 37, 79, 82, 18, 36, 75, 242, 63, 56, 60, + 79, 117, 18, 75, 146, 242, 64, 56, 60, 75, 117, 18, 79, 146, 242, 64, 56, + 60, 37, 117, 18, 36, 55, 242, 64, 56, 60, 36, 117, 18, 37, 55, 242, 64, + 56, 60, 82, 55, 18, 236, 182, 56, 60, 117, 42, 249, 15, 236, 182, 56, 60, + 82, 42, 249, 15, 236, 182, 56, 60, 117, 42, 249, 15, 238, 88, 56, 60, 82, + 42, 249, 15, 238, 88, 56, 60, 42, 236, 182, 56, 60, 42, 238, 88, 56, 60, + 79, 242, 66, 18, 37, 240, 222, 56, 60, 79, 42, 18, 37, 240, 226, 56, 60, + 42, 79, 18, 184, 56, 60, 79, 242, 66, 18, 36, 240, 222, 56, 60, 79, 42, + 18, 36, 240, 226, 56, 60, 42, 79, 18, 244, 172, 56, 60, 75, 242, 66, 18, + 37, 240, 222, 56, 60, 75, 42, 18, 37, 240, 226, 56, 60, 42, 75, 18, 184, + 56, 60, 75, 242, 66, 18, 36, 240, 222, 56, 60, 75, 42, 18, 36, 240, 226, + 56, 60, 42, 75, 18, 244, 172, 56, 60, 55, 242, 66, 18, 37, 240, 222, 56, + 60, 55, 42, 18, 37, 240, 226, 56, 60, 42, 55, 18, 184, 56, 60, 55, 242, + 66, 18, 36, 240, 222, 56, 60, 55, 42, 18, 36, 240, 226, 56, 60, 42, 55, + 18, 244, 172, 56, 60, 79, 242, 66, 18, 146, 240, 222, 56, 60, 79, 42, 18, + 146, 240, 226, 56, 60, 42, 79, 18, 236, 182, 56, 60, 75, 242, 66, 18, + 146, 240, 222, 56, 60, 75, 42, 18, 146, 240, 226, 56, 60, 42, 75, 18, + 236, 182, 56, 60, 55, 242, 66, 18, 146, 240, 222, 56, 60, 55, 42, 18, + 146, 240, 226, 56, 60, 42, 55, 18, 236, 182, 56, 60, 117, 253, 133, 79, + 18, 184, 56, 60, 117, 253, 133, 79, 18, 244, 172, 56, 60, 117, 253, 133, + 75, 18, 244, 172, 56, 60, 117, 253, 133, 75, 18, 184, 56, 60, 117, 239, + 125, 156, 37, 139, 149, 244, 172, 56, 60, 117, 239, 125, 156, 36, 139, + 149, 184, 56, 60, 117, 239, 125, 242, 94, 56, 60, 117, 244, 172, 56, 60, + 117, 253, 131, 56, 60, 117, 184, 56, 60, 117, 244, 185, 56, 60, 82, 244, + 172, 56, 60, 82, 253, 131, 56, 60, 82, 184, 56, 60, 82, 244, 185, 56, 60, + 117, 36, 18, 82, 184, 56, 60, 117, 75, 18, 82, 244, 185, 56, 60, 82, 36, + 18, 117, 184, 56, 60, 82, 75, 18, 117, 244, 185, 156, 136, 242, 67, 149, + 253, 101, 242, 111, 242, 67, 149, 253, 101, 240, 224, 242, 67, 149, 244, + 171, 240, 240, 242, 67, 149, 136, 242, 67, 149, 249, 48, 240, 240, 242, + 67, 149, 244, 171, 239, 231, 242, 67, 149, 245, 4, 240, 240, 242, 67, + 213, 242, 67, 36, 245, 4, 240, 240, 242, 67, 36, 244, 171, 239, 231, 242, + 67, 36, 249, 48, 240, 240, 242, 67, 36, 136, 242, 67, 36, 244, 171, 240, + 240, 242, 67, 36, 253, 101, 240, 224, 242, 67, 36, 253, 101, 242, 111, + 242, 67, 37, 136, 242, 67, 117, 242, 187, 242, 82, 242, 187, 250, 208, + 242, 187, 156, 253, 101, 242, 111, 242, 67, 37, 253, 101, 242, 111, 242, + 67, 239, 124, 149, 244, 172, 239, 124, 149, 184, 239, 124, 156, 244, 172, + 239, 124, 156, 36, 18, 149, 36, 18, 149, 184, 239, 124, 156, 36, 18, 149, + 184, 239, 124, 156, 36, 18, 156, 37, 18, 149, 244, 172, 239, 124, 156, + 36, 18, 156, 37, 18, 149, 184, 239, 124, 156, 184, 239, 124, 156, 37, 18, + 149, 244, 172, 239, 124, 156, 37, 18, 149, 36, 18, 149, 184, 86, 240, + 207, 58, 240, 207, 58, 30, 2, 241, 4, 240, 18, 58, 30, 237, 151, 86, 3, + 240, 207, 30, 2, 146, 244, 220, 30, 2, 55, 244, 220, 30, 2, 238, 39, 237, + 153, 244, 220, 30, 2, 156, 36, 139, 149, 37, 244, 220, 30, 2, 156, 37, + 139, 149, 36, 244, 220, 30, 2, 239, 125, 237, 153, 244, 220, 86, 3, 240, + 207, 58, 3, 240, 207, 86, 237, 137, 58, 237, 137, 86, 55, 237, 137, 58, + 55, 237, 137, 86, 235, 202, 58, 235, 202, 86, 236, 192, 219, 58, 236, + 192, 219, 86, 236, 192, 3, 219, 58, 236, 192, 3, 219, 86, 235, 190, 219, + 58, 235, 190, 219, 86, 235, 190, 3, 219, 58, 235, 190, 3, 219, 86, 235, + 190, 239, 173, 58, 235, 190, 239, 173, 86, 235, 241, 219, 58, 235, 241, + 219, 86, 235, 241, 3, 219, 58, 235, 241, 3, 219, 86, 235, 237, 219, 58, + 235, 237, 219, 86, 235, 237, 3, 219, 58, 235, 237, 3, 219, 86, 235, 237, + 239, 173, 58, 235, 237, 239, 173, 86, 239, 132, 58, 239, 132, 58, 240, + 221, 237, 151, 86, 3, 239, 132, 240, 75, 239, 188, 58, 240, 200, 242, + 118, 240, 200, 175, 2, 55, 244, 220, 238, 184, 86, 240, 200, 175, 2, 36, + 136, 231, 231, 175, 2, 37, 136, 231, 231, 175, 2, 149, 136, 231, 231, + 175, 2, 156, 136, 231, 231, 175, 2, 156, 37, 239, 124, 231, 231, 175, 2, + 254, 1, 253, 177, 156, 36, 239, 124, 231, 231, 36, 136, 86, 240, 200, 37, + 136, 86, 240, 200, 241, 0, 240, 215, 241, 0, 58, 240, 200, 156, 136, 241, + 0, 58, 240, 200, 149, 136, 241, 0, 58, 240, 200, 156, 36, 239, 124, 239, + 170, 249, 65, 156, 37, 239, 124, 239, 170, 249, 65, 149, 37, 239, 124, + 239, 170, 249, 65, 149, 36, 239, 124, 239, 170, 249, 65, 156, 136, 240, + 200, 149, 136, 240, 200, 86, 149, 37, 219, 86, 149, 36, 219, 86, 156, 36, + 219, 86, 156, 37, 219, 58, 240, 215, 30, 2, 36, 136, 231, 231, 30, 2, 37, + 136, 231, 231, 30, 2, 156, 36, 239, 125, 136, 231, 231, 30, 2, 149, 37, + 239, 125, 136, 231, 231, 58, 30, 2, 55, 238, 183, 244, 176, 58, 236, 192, + 239, 118, 2, 227, 236, 192, 239, 118, 2, 36, 136, 231, 231, 236, 192, + 239, 118, 2, 37, 136, 231, 231, 244, 222, 240, 200, 58, 30, 2, 156, 36, + 238, 93, 58, 30, 2, 149, 36, 238, 93, 58, 30, 2, 149, 37, 238, 93, 58, + 30, 2, 156, 37, 238, 93, 58, 175, 2, 156, 36, 238, 93, 58, 175, 2, 149, + 36, 238, 93, 58, 175, 2, 149, 37, 238, 93, 58, 175, 2, 156, 37, 238, 93, + 156, 36, 219, 156, 37, 219, 149, 36, 219, 58, 242, 82, 240, 207, 86, 242, + 82, 240, 207, 58, 242, 82, 3, 240, 207, 86, 242, 82, 3, 240, 207, 149, + 37, 219, 86, 254, 71, 2, 245, 168, 243, 71, 239, 107, 240, 166, 243, 74, + 86, 244, 178, 58, 244, 178, 238, 29, 236, 38, 249, 87, 238, 178, 245, + 151, 237, 186, 245, 151, 236, 89, 237, 79, 86, 237, 166, 58, 237, 166, + 242, 137, 249, 47, 242, 137, 56, 2, 242, 201, 242, 137, 56, 2, 170, 240, + 156, 58, 245, 186, 242, 163, 86, 245, 186, 242, 163, 224, 241, 3, 242, + 177, 244, 218, 240, 215, 86, 36, 239, 119, 242, 127, 86, 37, 239, 119, + 242, 127, 58, 36, 239, 119, 242, 127, 58, 75, 239, 119, 242, 127, 58, 37, + 239, 119, 242, 127, 58, 79, 239, 119, 242, 127, 248, 78, 18, 236, 254, + 241, 123, 57, 237, 83, 57, 238, 182, 57, 238, 186, 245, 246, 240, 133, + 242, 94, 254, 31, 249, 32, 244, 234, 137, 239, 43, 244, 234, 137, 238, + 22, 249, 86, 18, 238, 196, 244, 227, 90, 254, 81, 242, 10, 243, 142, 18, + 242, 13, 247, 239, 90, 254, 79, 248, 188, 240, 232, 54, 241, 179, 240, + 232, 54, 247, 52, 240, 232, 54, 244, 236, 240, 232, 54, 240, 181, 240, + 232, 54, 245, 50, 240, 232, 54, 242, 169, 240, 232, 54, 238, 147, 240, + 232, 54, 242, 142, 248, 172, 137, 241, 139, 58, 240, 79, 244, 235, 58, + 241, 81, 244, 235, 86, 241, 81, 244, 235, 58, 254, 71, 2, 245, 168, 245, + 84, 240, 224, 244, 229, 251, 184, 240, 224, 244, 229, 240, 115, 243, 108, + 57, 242, 142, 251, 217, 57, 240, 96, 241, 255, 242, 45, 240, 123, 244, + 38, 243, 33, 242, 31, 241, 174, 241, 121, 251, 188, 244, 141, 243, 203, + 239, 81, 236, 162, 237, 179, 238, 173, 241, 240, 58, 244, 199, 245, 125, + 58, 244, 199, 242, 241, 58, 244, 199, 245, 173, 58, 244, 199, 241, 44, + 58, 244, 199, 241, 63, 58, 244, 199, 245, 158, 86, 244, 199, 245, 125, + 86, 244, 199, 242, 241, 86, 244, 199, 245, 173, 86, 244, 199, 241, 44, + 86, 244, 199, 241, 63, 86, 244, 199, 245, 158, 86, 245, 195, 244, 219, + 58, 244, 218, 244, 219, 58, 240, 221, 244, 219, 86, 249, 199, 244, 219, + 58, 245, 195, 244, 219, 86, 244, 218, 244, 219, 86, 240, 221, 244, 219, + 58, 249, 199, 244, 219, 254, 112, 240, 167, 240, 224, 244, 208, 242, 111, + 244, 208, 242, 198, 242, 111, 242, 233, 242, 198, 238, 127, 242, 233, + 245, 30, 249, 134, 57, 245, 30, 241, 28, 57, 245, 30, 245, 5, 57, 249, + 17, 118, 242, 94, 249, 2, 118, 242, 94, 238, 166, 238, 89, 90, 238, 89, + 13, 54, 244, 128, 238, 99, 238, 89, 13, 54, 244, 129, 238, 99, 238, 89, + 13, 54, 244, 130, 238, 99, 238, 89, 13, 54, 244, 131, 238, 99, 238, 89, + 13, 54, 244, 132, 238, 99, 238, 89, 13, 54, 244, 133, 238, 99, 238, 89, + 13, 54, 244, 134, 238, 99, 238, 89, 13, 54, 241, 175, 238, 31, 86, 238, + 166, 238, 89, 90, 240, 152, 245, 157, 90, 231, 255, 245, 157, 90, 253, + 203, 241, 157, 253, 203, 241, 158, 253, 203, 241, 159, 253, 203, 241, + 160, 253, 203, 241, 161, 253, 203, 241, 162, 58, 175, 2, 49, 184, 58, + 175, 2, 225, 244, 215, 86, 175, 2, 58, 49, 184, 86, 175, 2, 225, 58, 244, + 215, 156, 245, 61, 238, 115, 86, 238, 115, 149, 245, 61, 238, 115, 58, + 238, 115, 238, 132, 240, 105, 57, 252, 196, 243, 95, 238, 169, 239, 7, + 242, 51, 244, 250, 242, 55, 244, 250, 149, 37, 241, 29, 241, 29, 156, 37, + 241, 29, 58, 250, 28, 86, 250, 28, 245, 16, 65, 82, 245, 16, 65, 235, + 191, 170, 82, 235, 191, 170, 242, 137, 170, 82, 242, 137, 170, 239, 159, + 26, 242, 94, 82, 26, 242, 94, 249, 0, 242, 100, 242, 94, 82, 249, 0, 242, + 100, 242, 94, 9, 242, 94, 239, 153, 58, 9, 242, 94, 239, 159, 9, 242, 94, + 241, 204, 242, 94, 249, 86, 137, 243, 83, 249, 28, 234, 63, 238, 77, 249, + 28, 235, 193, 238, 77, 82, 249, 28, 235, 193, 238, 77, 249, 28, 236, 250, + 238, 77, 86, 249, 28, 240, 218, 244, 178, 58, 249, 28, 240, 218, 244, + 178, 242, 188, 239, 159, 58, 244, 178, 28, 58, 244, 178, 249, 0, 242, + 100, 86, 244, 178, 86, 242, 100, 58, 244, 178, 239, 159, 86, 244, 178, + 82, 239, 159, 86, 244, 178, 239, 192, 244, 178, 239, 153, 58, 244, 178, + 82, 238, 77, 249, 0, 242, 100, 238, 77, 244, 207, 244, 96, 238, 77, 244, + 207, 240, 218, 86, 244, 178, 244, 207, 240, 218, 239, 192, 244, 178, 254, + 23, 240, 218, 86, 244, 178, 244, 207, 240, 218, 236, 227, 86, 244, 178, + 82, 244, 207, 240, 218, 236, 227, 86, 244, 178, 243, 17, 240, 218, 86, + 244, 178, 241, 78, 240, 218, 238, 77, 234, 63, 238, 77, 249, 0, 242, 100, + 234, 63, 238, 77, 82, 234, 63, 238, 77, 254, 23, 239, 230, 86, 18, 58, + 238, 110, 86, 238, 110, 58, 238, 110, 244, 207, 239, 230, 239, 159, 86, + 238, 110, 28, 249, 0, 242, 100, 244, 207, 240, 218, 244, 178, 82, 234, + 63, 239, 192, 238, 77, 237, 143, 248, 129, 238, 69, 237, 143, 82, 241, + 124, 237, 143, 239, 237, 82, 239, 237, 235, 193, 238, 77, 244, 207, 234, + 63, 238, 154, 238, 77, 82, 244, 207, 234, 63, 238, 154, 238, 77, 239, + 153, 58, 240, 200, 149, 37, 235, 250, 58, 240, 207, 156, 37, 235, 250, + 58, 240, 207, 149, 37, 239, 153, 58, 240, 207, 156, 37, 239, 153, 58, + 240, 207, 86, 240, 221, 244, 197, 58, 170, 117, 55, 143, 242, 82, 55, + 143, 82, 55, 143, 82, 242, 66, 242, 60, 244, 192, 238, 76, 167, 238, 78, + 82, 242, 66, 244, 192, 238, 76, 167, 238, 78, 82, 42, 242, 60, 244, 192, + 238, 76, 167, 238, 78, 82, 42, 244, 192, 238, 76, 167, 238, 78, 242, 141, + 252, 177, 238, 113, 25, 238, 78, 82, 236, 186, 167, 238, 78, 82, 244, + 218, 236, 186, 167, 238, 78, 82, 86, 242, 178, 241, 3, 82, 86, 244, 218, + 240, 215, 242, 177, 242, 178, 241, 3, 242, 177, 244, 218, 240, 215, 242, + 82, 36, 236, 187, 238, 78, 242, 82, 37, 236, 187, 238, 78, 242, 82, 238, + 140, 36, 236, 187, 238, 78, 242, 82, 238, 140, 37, 236, 187, 238, 78, + 242, 82, 235, 237, 160, 226, 238, 78, 242, 82, 235, 190, 160, 226, 238, + 78, 82, 235, 237, 160, 238, 76, 167, 238, 78, 82, 235, 190, 160, 238, 76, + 167, 238, 78, 82, 235, 237, 160, 226, 238, 78, 82, 235, 190, 160, 226, + 238, 78, 117, 36, 239, 139, 244, 202, 226, 238, 78, 117, 37, 239, 139, + 244, 202, 226, 238, 78, 242, 82, 36, 244, 175, 226, 238, 78, 242, 82, 37, + 244, 175, 226, 238, 78, 240, 201, 239, 115, 28, 27, 121, 240, 201, 239, + 115, 28, 27, 114, 240, 201, 239, 115, 28, 27, 153, 240, 201, 239, 115, + 28, 27, 163, 240, 201, 239, 115, 28, 27, 168, 240, 201, 239, 115, 28, 27, + 169, 240, 201, 239, 115, 28, 27, 179, 240, 201, 239, 115, 28, 27, 176, + 240, 201, 239, 115, 28, 27, 178, 240, 201, 239, 115, 28, 83, 249, 18, + 240, 201, 28, 23, 27, 121, 240, 201, 28, 23, 27, 114, 240, 201, 28, 23, + 27, 153, 240, 201, 28, 23, 27, 163, 240, 201, 28, 23, 27, 168, 240, 201, + 28, 23, 27, 169, 240, 201, 28, 23, 27, 179, 240, 201, 28, 23, 27, 176, + 240, 201, 28, 23, 27, 178, 240, 201, 28, 23, 83, 249, 18, 240, 201, 239, + 115, 28, 23, 27, 121, 240, 201, 239, 115, 28, 23, 27, 114, 240, 201, 239, + 115, 28, 23, 27, 153, 240, 201, 239, 115, 28, 23, 27, 163, 240, 201, 239, + 115, 28, 23, 27, 168, 240, 201, 239, 115, 28, 23, 27, 169, 240, 201, 239, + 115, 28, 23, 27, 179, 240, 201, 239, 115, 28, 23, 27, 176, 240, 201, 239, + 115, 28, 23, 27, 178, 240, 201, 239, 115, 28, 23, 83, 249, 18, 82, 237, + 109, 63, 52, 82, 244, 181, 249, 2, 52, 82, 63, 52, 82, 244, 177, 249, 2, + 52, 240, 60, 244, 180, 63, 52, 82, 236, 161, 63, 52, 234, 65, 63, 52, 82, + 234, 65, 63, 52, 242, 105, 234, 65, 63, 52, 82, 242, 105, 234, 65, 63, + 52, 86, 63, 52, 241, 88, 237, 105, 63, 237, 116, 241, 88, 235, 212, 63, + 237, 116, 86, 63, 237, 116, 82, 86, 242, 141, 220, 18, 63, 52, 82, 86, + 242, 141, 210, 18, 63, 52, 245, 196, 86, 63, 52, 82, 234, 70, 86, 63, 52, + 236, 159, 58, 63, 52, 237, 74, 58, 63, 52, 236, 246, 239, 153, 58, 63, + 52, 236, 129, 239, 153, 58, 63, 52, 82, 149, 235, 192, 58, 63, 52, 82, + 156, 235, 192, 58, 63, 52, 241, 70, 149, 235, 192, 58, 63, 52, 241, 70, + 156, 235, 192, 58, 63, 52, 28, 82, 58, 63, 52, 235, 189, 63, 52, 234, 64, + 244, 181, 249, 2, 52, 234, 64, 63, 52, 234, 64, 244, 177, 249, 2, 52, 82, + 234, 64, 244, 181, 249, 2, 52, 82, 234, 64, 63, 52, 82, 234, 64, 244, + 177, 249, 2, 52, 235, 186, 63, 52, 82, 234, 61, 63, 52, 236, 83, 63, 52, + 82, 236, 83, 63, 52, 236, 29, 63, 52, 58, 240, 221, 2, 241, 13, 227, 18, + 2, 227, 240, 214, 137, 240, 236, 239, 176, 149, 37, 242, 86, 2, 227, 156, + 36, 242, 86, 2, 227, 36, 245, 33, 244, 251, 37, 245, 33, 244, 251, 213, + 245, 33, 244, 251, 244, 222, 75, 244, 184, 244, 222, 79, 244, 184, 36, + 18, 37, 42, 237, 119, 36, 18, 37, 244, 184, 36, 238, 122, 165, 37, 244, + 184, 165, 36, 244, 184, 75, 249, 35, 2, 175, 47, 241, 203, 241, 17, 254, + 216, 146, 248, 43, 58, 235, 244, 239, 132, 58, 235, 244, 240, 221, 2, + 126, 244, 232, 58, 235, 244, 240, 221, 2, 63, 244, 232, 58, 30, 2, 126, + 244, 232, 58, 30, 2, 63, 244, 232, 8, 36, 58, 30, 110, 8, 37, 58, 30, + 110, 8, 36, 160, 110, 8, 37, 160, 110, 8, 36, 42, 160, 110, 8, 37, 42, + 160, 110, 210, 238, 94, 52, 220, 238, 94, 52, 235, 236, 242, 215, 175, + 52, 238, 87, 242, 215, 175, 52, 37, 59, 2, 28, 244, 212, 165, 126, 52, + 165, 63, 52, 165, 36, 37, 52, 165, 126, 42, 52, 165, 63, 42, 52, 165, 36, + 37, 42, 52, 165, 126, 59, 248, 254, 143, 165, 63, 59, 248, 254, 143, 165, + 126, 42, 59, 248, 254, 143, 165, 63, 42, 59, 248, 254, 143, 165, 63, 239, + 196, 52, 32, 33, 243, 46, 32, 33, 241, 142, 32, 33, 241, 143, 32, 33, + 240, 32, 32, 33, 241, 144, 32, 33, 240, 33, 32, 33, 240, 39, 32, 33, 238, + 204, 32, 33, 241, 145, 32, 33, 240, 34, 32, 33, 240, 40, 32, 33, 238, + 205, 32, 33, 240, 45, 32, 33, 238, 210, 32, 33, 238, 225, 32, 33, 237, + 188, 32, 33, 241, 146, 32, 33, 240, 35, 32, 33, 240, 41, 32, 33, 238, + 206, 32, 33, 240, 46, 32, 33, 238, 211, 32, 33, 238, 226, 32, 33, 237, + 189, 32, 33, 240, 50, 32, 33, 238, 215, 32, 33, 238, 230, 32, 33, 237, + 193, 32, 33, 238, 240, 32, 33, 237, 203, 32, 33, 237, 223, 32, 33, 237, + 2, 32, 33, 241, 147, 32, 33, 240, 36, 32, 33, 240, 42, 32, 33, 238, 207, + 32, 33, 240, 47, 32, 33, 238, 212, 32, 33, 238, 227, 32, 33, 237, 190, + 32, 33, 240, 51, 32, 33, 238, 216, 32, 33, 238, 231, 32, 33, 237, 194, + 32, 33, 238, 241, 32, 33, 237, 204, 32, 33, 237, 224, 32, 33, 237, 3, 32, + 33, 240, 54, 32, 33, 238, 219, 32, 33, 238, 234, 32, 33, 237, 197, 32, + 33, 238, 244, 32, 33, 237, 207, 32, 33, 237, 227, 32, 33, 237, 6, 32, 33, + 238, 250, 32, 33, 237, 213, 32, 33, 237, 233, 32, 33, 237, 12, 32, 33, + 237, 243, 32, 33, 237, 22, 32, 33, 237, 37, 32, 33, 236, 98, 32, 33, 241, + 148, 32, 33, 240, 37, 32, 33, 240, 43, 32, 33, 238, 208, 32, 33, 240, 48, + 32, 33, 238, 213, 32, 33, 238, 228, 32, 33, 237, 191, 32, 33, 240, 52, + 32, 33, 238, 217, 32, 33, 238, 232, 32, 33, 237, 195, 32, 33, 238, 242, + 32, 33, 237, 205, 32, 33, 237, 225, 32, 33, 237, 4, 32, 33, 240, 55, 32, + 33, 238, 220, 32, 33, 238, 235, 32, 33, 237, 198, 32, 33, 238, 245, 32, + 33, 237, 208, 32, 33, 237, 228, 32, 33, 237, 7, 32, 33, 238, 251, 32, 33, + 237, 214, 32, 33, 237, 234, 32, 33, 237, 13, 32, 33, 237, 244, 32, 33, + 237, 23, 32, 33, 237, 38, 32, 33, 236, 99, 32, 33, 240, 57, 32, 33, 238, + 222, 32, 33, 238, 237, 32, 33, 237, 200, 32, 33, 238, 247, 32, 33, 237, + 210, 32, 33, 237, 230, 32, 33, 237, 9, 32, 33, 238, 253, 32, 33, 237, + 216, 32, 33, 237, 236, 32, 33, 237, 15, 32, 33, 237, 246, 32, 33, 237, + 25, 32, 33, 237, 40, 32, 33, 236, 101, 32, 33, 239, 0, 32, 33, 237, 219, + 32, 33, 237, 239, 32, 33, 237, 18, 32, 33, 237, 249, 32, 33, 237, 28, 32, + 33, 237, 43, 32, 33, 236, 104, 32, 33, 237, 253, 32, 33, 237, 32, 32, 33, + 237, 47, 32, 33, 236, 108, 32, 33, 237, 52, 32, 33, 236, 113, 32, 33, + 236, 119, 32, 33, 236, 12, 32, 33, 241, 149, 32, 33, 240, 38, 32, 33, + 240, 44, 32, 33, 238, 209, 32, 33, 240, 49, 32, 33, 238, 214, 32, 33, + 238, 229, 32, 33, 237, 192, 32, 33, 240, 53, 32, 33, 238, 218, 32, 33, + 238, 233, 32, 33, 237, 196, 32, 33, 238, 243, 32, 33, 237, 206, 32, 33, + 237, 226, 32, 33, 237, 5, 32, 33, 240, 56, 32, 33, 238, 221, 32, 33, 238, + 236, 32, 33, 237, 199, 32, 33, 238, 246, 32, 33, 237, 209, 32, 33, 237, + 229, 32, 33, 237, 8, 32, 33, 238, 252, 32, 33, 237, 215, 32, 33, 237, + 235, 32, 33, 237, 14, 32, 33, 237, 245, 32, 33, 237, 24, 32, 33, 237, 39, + 32, 33, 236, 100, 32, 33, 240, 58, 32, 33, 238, 223, 32, 33, 238, 238, + 32, 33, 237, 201, 32, 33, 238, 248, 32, 33, 237, 211, 32, 33, 237, 231, + 32, 33, 237, 10, 32, 33, 238, 254, 32, 33, 237, 217, 32, 33, 237, 237, + 32, 33, 237, 16, 32, 33, 237, 247, 32, 33, 237, 26, 32, 33, 237, 41, 32, + 33, 236, 102, 32, 33, 239, 1, 32, 33, 237, 220, 32, 33, 237, 240, 32, 33, + 237, 19, 32, 33, 237, 250, 32, 33, 237, 29, 32, 33, 237, 44, 32, 33, 236, + 105, 32, 33, 237, 254, 32, 33, 237, 33, 32, 33, 237, 48, 32, 33, 236, + 109, 32, 33, 237, 53, 32, 33, 236, 114, 32, 33, 236, 120, 32, 33, 236, + 13, 32, 33, 240, 59, 32, 33, 238, 224, 32, 33, 238, 239, 32, 33, 237, + 202, 32, 33, 238, 249, 32, 33, 237, 212, 32, 33, 237, 232, 32, 33, 237, + 11, 32, 33, 238, 255, 32, 33, 237, 218, 32, 33, 237, 238, 32, 33, 237, + 17, 32, 33, 237, 248, 32, 33, 237, 27, 32, 33, 237, 42, 32, 33, 236, 103, + 32, 33, 239, 2, 32, 33, 237, 221, 32, 33, 237, 241, 32, 33, 237, 20, 32, + 33, 237, 251, 32, 33, 237, 30, 32, 33, 237, 45, 32, 33, 236, 106, 32, 33, + 237, 255, 32, 33, 237, 34, 32, 33, 237, 49, 32, 33, 236, 110, 32, 33, + 237, 54, 32, 33, 236, 115, 32, 33, 236, 121, 32, 33, 236, 14, 32, 33, + 239, 3, 32, 33, 237, 222, 32, 33, 237, 242, 32, 33, 237, 21, 32, 33, 237, + 252, 32, 33, 237, 31, 32, 33, 237, 46, 32, 33, 236, 107, 32, 33, 238, 0, + 32, 33, 237, 35, 32, 33, 237, 50, 32, 33, 236, 111, 32, 33, 237, 55, 32, + 33, 236, 116, 32, 33, 236, 122, 32, 33, 236, 15, 32, 33, 238, 1, 32, 33, + 237, 36, 32, 33, 237, 51, 32, 33, 236, 112, 32, 33, 237, 56, 32, 33, 236, + 117, 32, 33, 236, 123, 32, 33, 236, 16, 32, 33, 237, 57, 32, 33, 236, + 118, 32, 33, 236, 124, 32, 33, 236, 17, 32, 33, 236, 125, 32, 33, 236, + 18, 32, 33, 236, 19, 32, 33, 235, 215, 63, 237, 120, 59, 2, 55, 122, 63, + 237, 120, 59, 2, 42, 55, 122, 126, 42, 59, 2, 55, 122, 63, 42, 59, 2, 55, + 122, 36, 37, 42, 59, 2, 55, 122, 63, 237, 120, 59, 248, 254, 143, 126, + 42, 59, 248, 254, 143, 63, 42, 59, 248, 254, 143, 220, 59, 2, 146, 122, + 210, 59, 2, 146, 122, 210, 242, 59, 52, 220, 242, 59, 52, 126, 42, 249, + 15, 52, 63, 42, 249, 15, 52, 126, 242, 59, 249, 15, 52, 63, 242, 59, 249, + 15, 52, 63, 237, 120, 242, 59, 249, 15, 52, 63, 59, 2, 242, 118, 244, + 239, 210, 59, 139, 143, 220, 59, 139, 143, 63, 59, 2, 249, 88, 2, 55, + 122, 63, 59, 2, 249, 88, 2, 42, 55, 122, 63, 237, 120, 59, 2, 244, 182, + 63, 237, 120, 59, 2, 249, 88, 2, 55, 122, 63, 237, 120, 59, 2, 249, 88, + 2, 42, 55, 122, 126, 236, 196, 63, 236, 196, 126, 42, 236, 196, 63, 42, + 236, 196, 126, 59, 139, 86, 239, 132, 63, 59, 139, 86, 239, 132, 126, 59, + 248, 254, 253, 99, 139, 86, 239, 132, 63, 59, 248, 254, 253, 99, 139, 86, + 239, 132, 244, 177, 249, 17, 18, 244, 181, 249, 2, 52, 244, 177, 249, 2, + 18, 244, 181, 249, 17, 52, 244, 177, 249, 17, 59, 2, 128, 244, 177, 249, + 2, 59, 2, 128, 244, 181, 249, 2, 59, 2, 128, 244, 181, 249, 17, 59, 2, + 128, 244, 177, 249, 17, 59, 18, 244, 177, 249, 2, 52, 244, 177, 249, 2, + 59, 18, 244, 181, 249, 2, 52, 244, 181, 249, 2, 59, 18, 244, 181, 249, + 17, 52, 244, 181, 249, 17, 59, 18, 244, 177, 249, 17, 52, 242, 120, 239, + 125, 239, 149, 240, 247, 238, 109, 240, 247, 239, 125, 239, 149, 242, + 120, 238, 109, 244, 181, 249, 2, 59, 239, 149, 244, 177, 249, 2, 52, 244, + 177, 249, 2, 59, 239, 149, 244, 181, 249, 2, 52, 240, 247, 239, 125, 239, + 149, 244, 177, 249, 2, 52, 242, 120, 239, 125, 239, 149, 244, 181, 249, + 2, 52, 244, 177, 249, 2, 59, 239, 149, 244, 177, 249, 17, 52, 244, 177, + 249, 17, 59, 239, 149, 244, 177, 249, 2, 52, 249, 90, 59, 239, 119, 240, + 30, 184, 59, 239, 119, 63, 249, 120, 240, 252, 239, 176, 59, 239, 119, + 63, 249, 120, 240, 252, 237, 121, 59, 239, 119, 220, 249, 120, 240, 252, + 237, 125, 59, 239, 119, 220, 249, 120, 240, 252, 236, 195, 238, 56, 253, + 133, 238, 87, 52, 239, 41, 253, 133, 235, 236, 52, 253, 121, 253, 133, + 235, 236, 52, 242, 80, 253, 133, 235, 236, 52, 253, 121, 253, 133, 238, + 87, 59, 2, 242, 119, 253, 121, 253, 133, 235, 236, 59, 2, 244, 212, 149, + 37, 236, 69, 238, 87, 52, 149, 36, 236, 69, 235, 236, 52, 235, 236, 242, + 77, 175, 52, 238, 87, 242, 77, 175, 52, 63, 59, 53, 207, 126, 52, 126, + 59, 53, 207, 63, 52, 207, 63, 59, 53, 126, 52, 63, 59, 2, 249, 4, 45, + 126, 59, 2, 249, 4, 45, 63, 59, 240, 249, 170, 36, 37, 59, 240, 249, 3, + 240, 200, 210, 237, 120, 59, 248, 254, 3, 240, 200, 36, 133, 75, 37, 133, + 79, 239, 142, 36, 133, 79, 37, 133, 75, 239, 142, 75, 133, 37, 79, 133, + 36, 239, 142, 75, 133, 36, 79, 133, 37, 239, 142, 36, 133, 75, 37, 133, + 75, 239, 142, 75, 133, 37, 79, 133, 37, 239, 142, 36, 133, 79, 37, 133, + 79, 239, 142, 75, 133, 36, 79, 133, 36, 239, 142, 126, 154, 2, 133, 75, + 139, 143, 63, 154, 2, 133, 75, 139, 143, 210, 154, 2, 133, 37, 139, 143, + 220, 154, 2, 133, 37, 139, 143, 126, 154, 2, 133, 79, 139, 143, 63, 154, + 2, 133, 79, 139, 143, 210, 154, 2, 133, 36, 139, 143, 220, 154, 2, 133, + 36, 139, 143, 126, 154, 2, 133, 75, 248, 254, 143, 63, 154, 2, 133, 75, + 248, 254, 143, 210, 154, 2, 133, 37, 248, 254, 143, 220, 154, 2, 133, 37, + 248, 254, 143, 126, 154, 2, 133, 79, 248, 254, 143, 63, 154, 2, 133, 79, + 248, 254, 143, 210, 154, 2, 133, 36, 248, 254, 143, 220, 154, 2, 133, 36, + 248, 254, 143, 126, 154, 2, 133, 75, 53, 126, 154, 2, 133, 244, 185, 210, + 154, 2, 133, 36, 242, 99, 210, 154, 2, 133, 184, 63, 154, 2, 133, 75, 53, + 63, 154, 2, 133, 244, 185, 220, 154, 2, 133, 36, 242, 99, 220, 154, 2, + 133, 184, 126, 154, 2, 133, 75, 53, 63, 154, 2, 133, 253, 131, 126, 154, + 2, 133, 79, 53, 63, 154, 2, 133, 244, 185, 63, 154, 2, 133, 75, 53, 126, + 154, 2, 133, 253, 131, 63, 154, 2, 133, 79, 53, 126, 154, 2, 133, 244, + 185, 126, 154, 2, 133, 75, 53, 165, 244, 189, 126, 154, 2, 133, 79, 244, + 188, 165, 244, 189, 63, 154, 2, 133, 75, 53, 165, 244, 189, 63, 154, 2, + 133, 79, 244, 188, 165, 244, 189, 210, 154, 2, 133, 36, 242, 99, 220, + 154, 2, 133, 184, 220, 154, 2, 133, 36, 242, 99, 210, 154, 2, 133, 184, + 37, 42, 59, 2, 241, 4, 245, 25, 242, 61, 25, 53, 63, 52, 229, 239, 168, + 53, 63, 52, 126, 59, 53, 229, 223, 63, 59, 53, 229, 223, 63, 59, 53, 242, + 93, 80, 76, 206, 53, 126, 52, 126, 59, 240, 249, 237, 110, 196, 53, 63, + 52, 242, 76, 53, 63, 52, 126, 59, 240, 249, 240, 237, 239, 121, 53, 126, + 52, 36, 249, 102, 244, 182, 37, 249, 102, 244, 182, 75, 249, 102, 244, + 182, 79, 249, 102, 244, 182, 242, 59, 55, 253, 99, 237, 185, 255, 27, + 239, 129, 248, 82, 255, 27, 239, 129, 249, 173, 242, 81, 36, 58, 244, + 175, 110, 37, 58, 244, 175, 110, 36, 58, 236, 47, 37, 58, 236, 47, 255, + 27, 239, 129, 36, 244, 211, 110, 255, 27, 239, 129, 37, 244, 211, 110, + 255, 27, 239, 129, 36, 241, 43, 110, 255, 27, 239, 129, 37, 241, 43, 110, + 36, 30, 226, 2, 238, 75, 37, 30, 226, 2, 238, 75, 36, 30, 226, 2, 249, + 121, 254, 214, 253, 121, 240, 213, 37, 30, 226, 2, 249, 121, 254, 214, + 242, 80, 240, 213, 36, 30, 226, 2, 249, 121, 254, 214, 242, 80, 240, 213, + 37, 30, 226, 2, 249, 121, 254, 214, 253, 121, 240, 213, 36, 160, 226, 2, + 227, 37, 160, 226, 2, 227, 36, 253, 133, 206, 110, 37, 253, 133, 196, + 110, 42, 36, 253, 133, 196, 110, 42, 37, 253, 133, 206, 110, 36, 86, 239, + 139, 244, 202, 110, 37, 86, 239, 139, 244, 202, 110, 242, 118, 242, 143, + 55, 242, 193, 244, 176, 239, 136, 160, 240, 236, 244, 172, 37, 160, 242, + 49, 2, 240, 207, 239, 136, 37, 160, 2, 227, 160, 2, 255, 29, 240, 235, + 244, 196, 242, 117, 238, 128, 160, 240, 236, 244, 172, 238, 128, 160, + 240, 236, 253, 131, 242, 60, 242, 117, 224, 242, 117, 160, 2, 238, 75, + 224, 160, 2, 238, 75, 241, 2, 160, 240, 236, 253, 131, 241, 2, 160, 240, + 236, 244, 185, 239, 136, 160, 2, 249, 0, 253, 176, 242, 112, 254, 214, + 59, 239, 119, 75, 18, 184, 239, 136, 160, 2, 249, 0, 253, 176, 242, 112, + 254, 214, 59, 239, 119, 75, 18, 244, 172, 239, 136, 160, 2, 249, 0, 253, + 176, 242, 112, 254, 214, 59, 239, 119, 79, 18, 184, 239, 136, 160, 2, + 249, 0, 253, 176, 242, 112, 254, 214, 59, 239, 119, 79, 18, 244, 172, + 239, 136, 160, 2, 249, 0, 253, 176, 242, 112, 254, 214, 59, 239, 119, 37, + 18, 253, 131, 239, 136, 160, 2, 249, 0, 253, 176, 242, 112, 254, 214, 59, + 239, 119, 36, 18, 253, 131, 239, 136, 160, 2, 249, 0, 253, 176, 242, 112, + 254, 214, 59, 239, 119, 37, 18, 244, 185, 239, 136, 160, 2, 249, 0, 253, + 176, 242, 112, 254, 214, 59, 239, 119, 36, 18, 244, 185, 224, 244, 216, + 250, 69, 244, 216, 253, 232, 2, 239, 130, 244, 216, 253, 232, 2, 3, 175, + 47, 244, 216, 253, 232, 2, 37, 59, 47, 244, 216, 253, 232, 2, 36, 59, 47, + 175, 2, 146, 143, 28, 55, 143, 28, 238, 124, 28, 240, 219, 239, 144, 28, + 235, 225, 175, 241, 17, 254, 216, 146, 253, 99, 18, 253, 121, 136, 241, + 17, 254, 216, 55, 143, 175, 2, 236, 171, 170, 28, 231, 236, 239, 156, 57, + 75, 59, 240, 249, 240, 200, 28, 58, 240, 215, 28, 240, 215, 28, 237, 110, + 28, 235, 235, 175, 2, 3, 175, 139, 253, 173, 184, 175, 2, 225, 146, 242, + 24, 139, 253, 173, 184, 240, 227, 242, 120, 239, 125, 242, 91, 240, 227, + 240, 247, 239, 125, 242, 91, 240, 227, 238, 77, 240, 227, 3, 240, 200, + 240, 227, 240, 207, 225, 242, 160, 240, 168, 239, 118, 2, 49, 47, 239, + 118, 2, 238, 75, 255, 29, 254, 214, 219, 239, 118, 2, 242, 246, 254, 228, + 241, 12, 37, 239, 118, 53, 36, 219, 36, 239, 118, 242, 99, 55, 143, 55, + 253, 99, 242, 99, 37, 219, 242, 200, 2, 36, 136, 231, 231, 242, 200, 2, + 37, 136, 231, 231, 86, 241, 45, 244, 241, 2, 36, 136, 231, 231, 244, 241, + 2, 37, 136, 231, 231, 58, 237, 140, 86, 237, 140, 36, 242, 170, 242, 143, + 37, 242, 170, 242, 143, 36, 42, 242, 170, 242, 143, 37, 42, 242, 170, + 242, 143, 238, 18, 238, 121, 254, 192, 249, 25, 238, 121, 240, 91, 241, + 68, 2, 55, 143, 236, 126, 238, 122, 30, 2, 238, 194, 240, 134, 239, 32, + 254, 85, 242, 12, 239, 138, 242, 61, 25, 18, 240, 205, 238, 124, 242, 61, + 25, 18, 240, 205, 239, 160, 2, 229, 47, 238, 111, 139, 18, 240, 205, 238, + 124, 243, 136, 244, 104, 235, 233, 235, 241, 239, 118, 2, 36, 136, 231, + 231, 235, 241, 239, 118, 2, 37, 136, 231, 231, 86, 240, 221, 2, 79, 52, + 86, 239, 188, 58, 175, 2, 79, 52, 86, 175, 2, 79, 52, 236, 50, 58, 240, + 207, 236, 50, 86, 240, 207, 236, 50, 58, 239, 132, 236, 50, 86, 239, 132, + 236, 50, 58, 240, 200, 236, 50, 86, 240, 200, 236, 31, 240, 219, 240, + 230, 223, 240, 230, 2, 239, 130, 240, 219, 240, 230, 2, 146, 122, 253, + 202, 239, 144, 253, 202, 240, 219, 239, 144, 42, 244, 212, 242, 59, 244, + 212, 235, 237, 242, 141, 160, 110, 235, 190, 242, 141, 160, 110, 248, + 131, 247, 97, 244, 186, 28, 49, 223, 244, 186, 28, 249, 4, 223, 244, 186, + 28, 244, 241, 223, 244, 186, 244, 206, 239, 168, 2, 227, 244, 186, 244, + 206, 239, 168, 2, 244, 212, 244, 186, 30, 236, 48, 223, 244, 186, 30, + 244, 206, 223, 225, 240, 202, 18, 223, 225, 240, 202, 112, 223, 244, 186, + 244, 241, 223, 243, 229, 225, 252, 179, 238, 131, 2, 238, 86, 238, 94, + 239, 135, 223, 243, 114, 250, 41, 238, 86, 239, 135, 2, 42, 122, 239, + 135, 241, 105, 2, 242, 91, 236, 249, 239, 22, 235, 236, 236, 138, 217, + 236, 201, 2, 236, 226, 250, 42, 242, 171, 245, 36, 217, 236, 201, 2, 236, + 69, 250, 42, 242, 171, 245, 36, 217, 236, 201, 141, 239, 31, 253, 173, + 245, 36, 239, 135, 242, 171, 119, 244, 180, 223, 238, 49, 239, 135, 223, + 239, 135, 2, 126, 59, 2, 128, 239, 135, 2, 244, 241, 57, 239, 135, 2, + 235, 238, 239, 135, 2, 242, 109, 239, 135, 2, 239, 130, 239, 135, 2, 238, + 75, 244, 251, 244, 222, 36, 239, 118, 223, 255, 27, 239, 129, 242, 185, + 236, 75, 255, 27, 239, 129, 242, 185, 241, 239, 255, 27, 239, 129, 242, + 185, 237, 81, 249, 4, 25, 2, 3, 175, 47, 249, 4, 25, 2, 157, 230, 47, + 249, 4, 25, 2, 229, 47, 249, 4, 25, 2, 49, 45, 249, 4, 25, 2, 229, 45, + 249, 4, 25, 2, 238, 83, 114, 249, 4, 25, 2, 86, 219, 244, 197, 25, 2, + 244, 192, 47, 244, 197, 25, 2, 49, 45, 244, 197, 25, 2, 240, 247, 244, + 215, 244, 197, 25, 2, 242, 120, 244, 215, 249, 4, 25, 254, 214, 36, 136, + 240, 200, 249, 4, 25, 254, 214, 37, 136, 240, 200, 244, 139, 112, 244, + 234, 239, 138, 235, 191, 25, 2, 49, 47, 235, 191, 25, 2, 238, 75, 237, + 128, 241, 244, 2, 242, 80, 241, 128, 245, 193, 239, 138, 235, 191, 25, + 254, 214, 36, 136, 240, 200, 235, 191, 25, 254, 214, 37, 136, 240, 200, + 28, 235, 191, 25, 2, 157, 240, 203, 235, 191, 25, 254, 214, 42, 240, 200, + 28, 239, 156, 57, 249, 4, 25, 254, 214, 219, 244, 197, 25, 254, 214, 219, + 235, 191, 25, 254, 214, 219, 239, 218, 239, 138, 239, 76, 239, 218, 239, + 138, 255, 27, 239, 129, 238, 53, 236, 75, 236, 84, 112, 237, 152, 236, + 48, 2, 227, 244, 206, 2, 244, 197, 57, 244, 206, 2, 239, 130, 236, 48, 2, + 239, 130, 236, 48, 2, 240, 202, 249, 46, 244, 206, 2, 240, 202, 253, 186, + 244, 206, 53, 235, 238, 236, 48, 53, 242, 109, 244, 206, 53, 253, 99, 53, + 235, 238, 236, 48, 53, 253, 99, 53, 242, 109, 244, 206, 242, 99, 18, 242, + 160, 2, 242, 109, 236, 48, 242, 99, 18, 242, 160, 2, 235, 238, 242, 77, + 244, 206, 2, 241, 76, 242, 77, 236, 48, 2, 241, 76, 42, 30, 235, 238, 42, + 30, 242, 109, 242, 77, 244, 206, 2, 242, 246, 18, 245, 193, 239, 138, + 240, 202, 18, 2, 49, 47, 240, 202, 112, 2, 49, 47, 42, 240, 202, 249, 46, + 42, 240, 202, 253, 186, 225, 236, 76, 240, 202, 249, 46, 225, 236, 76, + 240, 202, 253, 186, 241, 79, 244, 222, 253, 186, 241, 79, 244, 222, 249, + 46, 240, 202, 112, 236, 221, 240, 202, 249, 46, 240, 202, 18, 2, 242, 65, + 244, 239, 240, 202, 112, 2, 242, 65, 244, 239, 240, 202, 18, 2, 146, 244, + 189, 240, 202, 112, 2, 146, 244, 189, 240, 202, 18, 2, 42, 239, 130, 240, + 202, 18, 2, 238, 75, 240, 202, 18, 2, 42, 238, 75, 3, 254, 199, 2, 238, + 75, 240, 202, 112, 2, 42, 239, 130, 240, 202, 112, 2, 42, 238, 75, 255, + 27, 239, 129, 243, 94, 232, 15, 255, 27, 239, 129, 248, 19, 232, 15, 242, + 61, 25, 2, 49, 45, 238, 111, 2, 49, 47, 242, 59, 146, 253, 99, 2, 42, 55, + 122, 242, 59, 146, 253, 99, 2, 242, 59, 55, 122, 229, 239, 168, 2, 49, + 47, 229, 239, 168, 2, 242, 120, 244, 215, 240, 225, 244, 197, 240, 163, + 238, 192, 2, 49, 47, 242, 61, 2, 238, 77, 242, 93, 80, 139, 2, 157, 240, + 203, 235, 239, 80, 112, 80, 76, 242, 61, 25, 53, 249, 4, 57, 249, 4, 25, + 53, 242, 61, 57, 242, 61, 25, 53, 229, 223, 42, 244, 214, 242, 88, 225, + 236, 212, 242, 61, 243, 0, 244, 171, 236, 212, 242, 61, 243, 0, 242, 61, + 25, 2, 225, 248, 253, 53, 18, 225, 248, 253, 45, 237, 113, 2, 249, 28, + 248, 253, 47, 206, 2, 175, 240, 235, 196, 2, 175, 240, 235, 206, 2, 239, + 126, 167, 47, 196, 2, 239, 126, 167, 47, 206, 112, 240, 205, 80, 76, 196, + 112, 240, 205, 80, 76, 206, 112, 240, 205, 80, 139, 2, 49, 240, 235, 196, + 112, 240, 205, 80, 139, 2, 49, 240, 235, 206, 112, 240, 205, 80, 139, 2, + 49, 47, 196, 112, 240, 205, 80, 139, 2, 49, 47, 206, 112, 240, 205, 80, + 139, 2, 49, 53, 184, 196, 112, 240, 205, 80, 139, 2, 49, 53, 244, 172, + 206, 112, 236, 53, 196, 112, 236, 53, 206, 18, 236, 193, 141, 80, 76, + 196, 18, 236, 193, 141, 80, 76, 206, 18, 141, 236, 53, 196, 18, 141, 236, + 53, 206, 53, 236, 188, 80, 53, 235, 235, 196, 53, 236, 188, 80, 53, 237, + 110, 206, 53, 240, 225, 112, 242, 88, 196, 53, 240, 225, 112, 242, 88, + 206, 53, 240, 225, 53, 235, 235, 196, 53, 240, 225, 53, 237, 110, 206, + 53, 196, 53, 236, 188, 242, 88, 196, 53, 206, 53, 236, 188, 242, 88, 206, + 53, 240, 205, 80, 53, 196, 53, 240, 205, 242, 88, 196, 53, 240, 205, 80, + 53, 206, 53, 240, 205, 242, 88, 240, 205, 80, 139, 112, 237, 110, 240, + 205, 80, 139, 112, 235, 235, 240, 205, 80, 139, 112, 206, 2, 49, 240, + 235, 240, 205, 80, 139, 112, 196, 2, 49, 240, 235, 236, 188, 80, 139, + 112, 237, 110, 236, 188, 80, 139, 112, 235, 235, 236, 188, 240, 205, 80, + 139, 112, 237, 110, 236, 188, 240, 205, 80, 139, 112, 235, 235, 240, 225, + 112, 237, 110, 240, 225, 112, 235, 235, 240, 225, 53, 206, 53, 242, 61, + 57, 240, 225, 53, 196, 53, 242, 61, 57, 42, 242, 148, 237, 110, 42, 242, + 148, 235, 235, 42, 242, 148, 206, 2, 238, 75, 196, 236, 221, 237, 110, + 196, 242, 99, 237, 110, 206, 242, 77, 254, 216, 242, 203, 196, 242, 77, + 254, 216, 242, 203, 206, 242, 77, 254, 216, 245, 72, 53, 240, 205, 242, + 88, 196, 242, 77, 254, 216, 245, 72, 53, 240, 205, 242, 88, 241, 80, 245, + 47, 242, 230, 245, 47, 241, 80, 249, 166, 112, 80, 76, 242, 230, 249, + 166, 112, 80, 76, 242, 61, 25, 2, 246, 34, 47, 239, 143, 53, 236, 193, + 242, 61, 57, 239, 145, 53, 236, 193, 242, 61, 57, 239, 143, 53, 236, 193, + 141, 80, 76, 239, 145, 53, 236, 193, 141, 80, 76, 239, 143, 53, 242, 61, + 57, 239, 145, 53, 242, 61, 57, 239, 143, 53, 141, 80, 76, 239, 145, 53, + 141, 80, 76, 239, 143, 53, 242, 93, 80, 76, 239, 145, 53, 242, 93, 80, + 76, 239, 143, 53, 141, 242, 93, 80, 76, 239, 145, 53, 141, 242, 93, 80, + 76, 42, 238, 126, 42, 238, 130, 242, 76, 2, 227, 239, 121, 2, 227, 242, + 76, 2, 249, 4, 25, 45, 239, 121, 2, 249, 4, 25, 45, 242, 76, 2, 235, 191, + 25, 45, 239, 121, 2, 235, 191, 25, 45, 242, 76, 137, 112, 80, 139, 2, 49, + 47, 239, 121, 137, 112, 80, 139, 2, 49, 47, 242, 76, 137, 53, 242, 61, + 57, 239, 121, 137, 53, 242, 61, 57, 242, 76, 137, 53, 229, 223, 239, 121, + 137, 53, 229, 223, 242, 76, 137, 53, 242, 93, 80, 76, 239, 121, 137, 53, + 242, 93, 80, 76, 242, 76, 137, 53, 141, 80, 76, 239, 121, 137, 53, 141, + 80, 76, 30, 36, 249, 0, 56, 223, 30, 37, 249, 0, 56, 223, 242, 77, 240, + 237, 242, 77, 238, 116, 242, 77, 242, 76, 112, 80, 76, 242, 77, 239, 121, + 112, 80, 76, 242, 76, 53, 238, 116, 239, 121, 53, 240, 237, 242, 76, 53, + 240, 237, 239, 121, 53, 238, 116, 239, 121, 242, 99, 240, 237, 239, 121, + 242, 99, 18, 242, 160, 254, 216, 249, 15, 2, 240, 237, 240, 214, 137, + 240, 236, 237, 121, 239, 62, 2, 254, 190, 249, 168, 237, 106, 235, 238, + 240, 77, 237, 78, 207, 36, 244, 184, 207, 79, 244, 184, 207, 75, 244, + 184, 236, 30, 2, 159, 55, 253, 99, 242, 59, 37, 237, 119, 42, 55, 253, + 99, 36, 237, 119, 55, 253, 99, 42, 36, 237, 119, 42, 55, 253, 99, 42, 36, + 237, 119, 165, 249, 15, 248, 254, 36, 243, 219, 137, 42, 238, 88, 207, + 79, 249, 35, 2, 239, 130, 207, 75, 249, 35, 2, 238, 75, 207, 75, 249, 35, + 53, 207, 79, 244, 184, 42, 79, 244, 184, 42, 75, 244, 184, 42, 242, 114, + 141, 57, 224, 42, 242, 114, 141, 57, 249, 41, 141, 243, 93, 2, 224, 240, + 122, 242, 91, 55, 217, 2, 175, 47, 55, 217, 2, 175, 45, 79, 249, 35, 2, + 175, 45, 239, 160, 2, 146, 122, 239, 160, 2, 229, 223, 242, 59, 55, 253, + 99, 242, 199, 238, 114, 242, 59, 55, 253, 99, 2, 146, 122, 242, 59, 244, + 214, 223, 242, 59, 242, 148, 237, 110, 242, 59, 242, 148, 235, 235, 236, + 188, 240, 205, 206, 112, 80, 76, 236, 188, 240, 205, 196, 112, 80, 76, + 242, 59, 240, 230, 242, 199, 238, 114, 244, 222, 242, 59, 55, 253, 99, + 223, 42, 240, 230, 223, 58, 55, 143, 244, 186, 58, 55, 143, 7, 22, 241, + 248, 7, 22, 242, 175, 7, 22, 242, 165, 121, 7, 22, 242, 165, 114, 7, 22, + 242, 165, 153, 7, 22, 241, 237, 7, 22, 249, 47, 7, 22, 243, 7, 7, 22, + 245, 126, 121, 7, 22, 245, 126, 114, 7, 22, 236, 213, 7, 22, 245, 178, 7, + 22, 3, 121, 7, 22, 3, 114, 7, 22, 249, 105, 121, 7, 22, 249, 105, 114, 7, + 22, 249, 105, 153, 7, 22, 249, 105, 163, 7, 22, 244, 91, 7, 22, 241, 87, + 7, 22, 245, 194, 121, 7, 22, 245, 194, 114, 7, 22, 244, 218, 121, 7, 22, + 244, 218, 114, 7, 22, 244, 250, 7, 22, 249, 158, 7, 22, 243, 64, 7, 22, + 249, 87, 7, 22, 244, 229, 7, 22, 242, 207, 7, 22, 241, 217, 7, 22, 238, + 190, 7, 22, 245, 220, 121, 7, 22, 245, 220, 114, 7, 22, 244, 236, 7, 22, + 254, 65, 121, 7, 22, 254, 65, 114, 7, 22, 237, 130, 136, 250, 94, 243, + 13, 7, 22, 249, 201, 7, 22, 249, 215, 7, 22, 245, 114, 7, 22, 249, 192, + 137, 242, 202, 7, 22, 249, 220, 7, 22, 243, 3, 121, 7, 22, 243, 3, 114, + 7, 22, 241, 41, 7, 22, 245, 44, 7, 22, 236, 44, 245, 44, 7, 22, 253, 243, + 121, 7, 22, 253, 243, 114, 7, 22, 253, 243, 153, 7, 22, 253, 243, 163, 7, + 22, 247, 77, 7, 22, 242, 250, 7, 22, 252, 116, 7, 22, 250, 249, 7, 22, + 250, 37, 7, 22, 245, 64, 121, 7, 22, 245, 64, 114, 7, 22, 245, 139, 7, + 22, 241, 67, 7, 22, 245, 23, 121, 7, 22, 245, 23, 114, 7, 22, 245, 23, + 153, 7, 22, 243, 11, 7, 22, 239, 210, 7, 22, 249, 17, 121, 7, 22, 249, + 17, 114, 7, 22, 236, 44, 249, 117, 7, 22, 237, 130, 244, 210, 7, 22, 244, + 210, 7, 22, 236, 44, 241, 82, 7, 22, 236, 44, 242, 251, 7, 22, 245, 22, + 7, 22, 236, 44, 245, 66, 7, 22, 237, 130, 245, 218, 7, 22, 249, 176, 121, + 7, 22, 249, 176, 114, 7, 22, 245, 70, 7, 22, 236, 44, 245, 100, 7, 22, + 165, 121, 7, 22, 165, 114, 7, 22, 236, 44, 245, 133, 7, 22, 236, 44, 245, + 91, 7, 22, 245, 143, 121, 7, 22, 245, 143, 114, 7, 22, 245, 167, 7, 22, + 245, 62, 7, 22, 236, 44, 243, 8, 239, 187, 7, 22, 236, 44, 245, 130, 7, + 22, 236, 44, 245, 50, 7, 22, 236, 44, 249, 224, 7, 22, 254, 8, 121, 7, + 22, 254, 8, 114, 7, 22, 254, 8, 153, 7, 22, 236, 44, 249, 219, 7, 22, + 245, 25, 7, 22, 236, 44, 242, 224, 7, 22, 245, 63, 7, 22, 242, 217, 7, + 22, 236, 44, 245, 85, 7, 22, 236, 44, 245, 14, 7, 22, 236, 44, 245, 177, + 7, 22, 237, 130, 243, 18, 7, 22, 237, 130, 241, 90, 7, 22, 236, 44, 245, + 89, 7, 22, 236, 56, 245, 21, 7, 22, 236, 44, 245, 21, 7, 22, 236, 56, + 242, 191, 7, 22, 236, 44, 242, 191, 7, 22, 236, 56, 241, 19, 7, 22, 236, + 44, 241, 19, 7, 22, 241, 9, 7, 22, 236, 56, 241, 9, 7, 22, 236, 44, 241, + 9, 40, 22, 121, 40, 22, 244, 176, 40, 22, 227, 40, 22, 242, 91, 40, 22, + 242, 3, 40, 22, 128, 40, 22, 114, 40, 22, 251, 195, 40, 22, 249, 146, 40, + 22, 247, 57, 40, 22, 243, 99, 40, 22, 176, 40, 22, 79, 249, 47, 40, 22, + 243, 76, 40, 22, 251, 107, 40, 22, 243, 7, 40, 22, 249, 0, 249, 47, 40, + 22, 243, 191, 40, 22, 244, 40, 40, 22, 248, 174, 40, 22, 244, 97, 40, 22, + 37, 249, 0, 249, 47, 40, 22, 243, 145, 237, 139, 40, 22, 249, 18, 40, 22, + 236, 213, 40, 22, 245, 178, 40, 22, 242, 175, 40, 22, 240, 145, 40, 22, + 243, 26, 40, 22, 243, 231, 40, 22, 237, 139, 40, 22, 242, 142, 40, 22, + 240, 159, 40, 22, 253, 195, 40, 22, 255, 8, 242, 15, 40, 22, 240, 71, 40, + 22, 250, 158, 40, 22, 244, 148, 40, 22, 243, 62, 40, 22, 248, 24, 40, 22, + 246, 251, 40, 22, 243, 2, 40, 22, 247, 53, 40, 22, 241, 130, 40, 22, 242, + 19, 40, 22, 238, 147, 40, 22, 244, 60, 40, 22, 248, 173, 40, 22, 240, + 131, 40, 22, 242, 42, 40, 22, 250, 229, 40, 22, 207, 241, 87, 40, 22, + 242, 118, 242, 175, 40, 22, 165, 242, 23, 40, 22, 225, 243, 143, 40, 22, + 244, 79, 40, 22, 249, 129, 40, 22, 244, 92, 40, 22, 240, 10, 40, 22, 248, + 104, 40, 22, 242, 178, 40, 22, 240, 84, 40, 22, 246, 113, 40, 22, 244, + 250, 40, 22, 241, 117, 40, 22, 249, 158, 40, 22, 242, 1, 40, 22, 241, + 141, 40, 22, 250, 139, 40, 22, 240, 207, 40, 22, 249, 151, 40, 22, 249, + 87, 40, 22, 252, 156, 40, 22, 244, 229, 40, 22, 248, 137, 40, 22, 247, + 51, 40, 22, 189, 40, 22, 242, 207, 40, 22, 242, 54, 40, 22, 254, 240, + 249, 151, 40, 22, 240, 16, 40, 22, 249, 225, 40, 22, 246, 68, 40, 22, + 244, 105, 40, 22, 242, 169, 40, 22, 244, 236, 40, 22, 246, 69, 40, 22, + 241, 163, 40, 22, 42, 170, 40, 22, 136, 250, 94, 243, 13, 40, 22, 244, + 87, 40, 22, 246, 129, 40, 22, 249, 201, 40, 22, 249, 215, 40, 22, 239, + 64, 40, 22, 245, 114, 40, 22, 243, 218, 40, 22, 248, 130, 40, 22, 244, + 107, 40, 22, 247, 67, 40, 22, 252, 246, 40, 22, 243, 110, 40, 22, 249, + 192, 137, 242, 202, 40, 22, 239, 84, 40, 22, 242, 118, 248, 119, 40, 22, + 242, 92, 40, 22, 248, 77, 40, 22, 246, 109, 40, 22, 249, 220, 40, 22, + 244, 95, 40, 22, 52, 40, 22, 244, 106, 40, 22, 242, 18, 40, 22, 244, 123, + 40, 22, 243, 137, 40, 22, 246, 43, 40, 22, 244, 103, 40, 22, 241, 41, 40, + 22, 248, 21, 40, 22, 245, 44, 40, 22, 251, 118, 40, 22, 252, 14, 40, 22, + 242, 250, 40, 22, 240, 73, 40, 22, 250, 37, 40, 22, 249, 46, 40, 22, 247, + 249, 40, 22, 249, 218, 40, 22, 243, 52, 40, 22, 245, 139, 40, 22, 239, + 49, 40, 22, 245, 179, 40, 22, 241, 101, 40, 22, 241, 67, 40, 22, 241, 36, + 40, 22, 241, 229, 40, 22, 246, 30, 40, 22, 239, 86, 40, 22, 243, 73, 40, + 22, 243, 138, 40, 22, 243, 11, 40, 22, 241, 187, 40, 22, 243, 47, 40, 22, + 249, 176, 237, 139, 40, 22, 239, 210, 40, 22, 248, 171, 40, 22, 249, 117, + 40, 22, 244, 210, 40, 22, 241, 82, 40, 22, 242, 47, 40, 22, 246, 18, 40, + 22, 252, 65, 40, 22, 241, 107, 40, 22, 242, 251, 40, 22, 252, 119, 40, + 22, 252, 138, 40, 22, 245, 22, 40, 22, 246, 31, 40, 22, 245, 66, 40, 22, + 241, 114, 40, 22, 240, 119, 40, 22, 245, 218, 40, 22, 245, 70, 40, 22, + 245, 222, 40, 22, 236, 96, 40, 22, 240, 194, 40, 22, 245, 100, 40, 22, + 245, 133, 40, 22, 245, 91, 40, 22, 243, 230, 40, 22, 244, 86, 40, 22, + 207, 244, 109, 245, 14, 40, 22, 245, 167, 40, 22, 245, 62, 40, 22, 244, + 150, 40, 22, 244, 235, 40, 22, 239, 187, 40, 22, 243, 8, 239, 187, 40, + 22, 247, 56, 40, 22, 244, 93, 40, 22, 245, 130, 40, 22, 245, 50, 40, 22, + 249, 224, 40, 22, 249, 219, 40, 22, 245, 25, 40, 22, 239, 21, 40, 22, + 242, 224, 40, 22, 245, 63, 40, 22, 248, 117, 40, 22, 246, 171, 40, 22, + 243, 112, 40, 22, 237, 84, 245, 222, 40, 22, 238, 187, 40, 22, 242, 217, + 40, 22, 245, 85, 40, 22, 245, 14, 40, 22, 245, 177, 40, 22, 246, 102, 40, + 22, 243, 18, 40, 22, 246, 172, 40, 22, 241, 90, 40, 22, 241, 214, 40, 22, + 231, 231, 40, 22, 237, 59, 40, 22, 245, 89, 40, 22, 242, 39, 40, 22, 246, + 115, 40, 22, 250, 61, 40, 22, 247, 180, 40, 22, 245, 21, 40, 22, 242, + 191, 40, 22, 241, 19, 40, 22, 241, 9, 40, 22, 243, 115, 66, 236, 185, 92, + 36, 139, 184, 66, 236, 185, 92, 53, 139, 45, 66, 236, 185, 92, 36, 139, + 242, 65, 18, 184, 66, 236, 185, 92, 53, 139, 242, 65, 18, 45, 66, 236, + 185, 92, 213, 239, 98, 66, 236, 185, 92, 239, 165, 248, 254, 47, 66, 236, + 185, 92, 239, 165, 248, 254, 45, 66, 236, 185, 92, 239, 165, 248, 254, + 244, 172, 66, 236, 185, 92, 239, 165, 248, 254, 156, 244, 172, 66, 236, + 185, 92, 239, 165, 248, 254, 156, 184, 66, 236, 185, 92, 239, 165, 248, + 254, 149, 244, 172, 66, 236, 185, 92, 239, 54, 66, 242, 69, 66, 242, 84, + 66, 213, 189, 246, 110, 65, 240, 95, 238, 20, 239, 239, 90, 66, 238, 101, + 65, 66, 241, 48, 65, 66, 83, 244, 173, 36, 160, 110, 37, 160, 110, 36, + 42, 160, 110, 37, 42, 160, 110, 36, 242, 86, 110, 37, 242, 86, 110, 36, + 58, 242, 86, 110, 37, 58, 242, 86, 110, 36, 86, 237, 126, 110, 37, 86, + 237, 126, 110, 242, 183, 65, 251, 32, 65, 36, 239, 139, 244, 202, 110, + 37, 239, 139, 244, 202, 110, 36, 58, 237, 126, 110, 37, 58, 237, 126, + 110, 36, 58, 239, 139, 244, 202, 110, 37, 58, 239, 139, 244, 202, 110, + 36, 58, 30, 110, 37, 58, 30, 110, 249, 90, 244, 189, 224, 42, 245, 37, + 238, 76, 65, 42, 245, 37, 238, 76, 65, 253, 113, 42, 245, 37, 238, 76, + 65, 242, 183, 167, 244, 235, 239, 134, 151, 121, 239, 134, 151, 114, 239, + 134, 151, 153, 239, 134, 151, 163, 239, 134, 151, 168, 239, 134, 151, + 169, 239, 134, 151, 179, 239, 134, 151, 176, 239, 134, 151, 178, 66, 247, + 58, 249, 5, 65, 66, 242, 120, 249, 5, 65, 66, 238, 119, 249, 5, 65, 66, + 240, 69, 249, 5, 65, 20, 242, 66, 49, 249, 5, 65, 20, 42, 49, 249, 5, 65, + 249, 57, 244, 189, 55, 249, 82, 242, 113, 65, 55, 249, 82, 242, 113, 2, + 242, 116, 244, 205, 65, 55, 249, 82, 242, 113, 167, 156, 244, 217, 55, + 249, 82, 242, 113, 2, 242, 116, 244, 205, 167, 156, 244, 217, 55, 249, + 82, 242, 113, 167, 149, 244, 217, 28, 242, 183, 65, 66, 127, 217, 251, 0, + 238, 158, 90, 239, 134, 151, 249, 18, 239, 134, 151, 240, 239, 239, 134, + 151, 241, 7, 55, 66, 238, 101, 65, 251, 219, 65, 250, 41, 236, 240, 65, + 66, 238, 79, 237, 136, 66, 136, 251, 8, 242, 69, 93, 1, 3, 64, 93, 1, 64, + 93, 1, 3, 69, 93, 1, 69, 93, 1, 3, 84, 93, 1, 84, 93, 1, 3, 70, 93, 1, + 70, 93, 1, 3, 78, 93, 1, 78, 93, 1, 208, 93, 1, 253, 100, 93, 1, 253, + 182, 93, 1, 253, 209, 93, 1, 253, 183, 93, 1, 253, 196, 93, 1, 253, 134, + 93, 1, 253, 207, 93, 1, 253, 155, 93, 1, 253, 195, 93, 1, 253, 97, 93, 1, + 253, 129, 93, 1, 253, 158, 93, 1, 253, 218, 93, 1, 253, 172, 93, 1, 253, + 233, 93, 1, 253, 171, 93, 1, 253, 189, 93, 1, 253, 159, 93, 1, 253, 190, + 93, 1, 253, 91, 93, 1, 253, 93, 93, 1, 253, 174, 93, 1, 253, 165, 93, 1, + 3, 253, 160, 93, 1, 253, 160, 93, 1, 253, 204, 93, 1, 253, 151, 93, 1, + 253, 152, 93, 1, 76, 93, 1, 253, 193, 93, 1, 253, 94, 93, 1, 253, 148, + 93, 1, 253, 112, 93, 1, 253, 157, 93, 1, 253, 136, 93, 1, 253, 88, 93, 1, + 253, 110, 93, 1, 253, 90, 93, 1, 253, 154, 93, 1, 253, 236, 93, 1, 253, + 116, 93, 1, 253, 197, 93, 1, 253, 192, 93, 1, 253, 231, 93, 1, 253, 125, + 93, 1, 253, 191, 93, 1, 253, 143, 93, 1, 253, 140, 93, 1, 253, 221, 93, + 1, 253, 187, 93, 1, 221, 93, 1, 253, 156, 93, 1, 253, 114, 93, 1, 253, + 167, 93, 1, 253, 141, 93, 1, 3, 216, 93, 1, 216, 93, 1, 3, 253, 124, 93, + 1, 253, 124, 93, 1, 3, 253, 123, 93, 1, 253, 123, 93, 1, 253, 92, 93, 1, + 253, 169, 93, 1, 253, 170, 93, 1, 253, 150, 93, 1, 253, 144, 93, 1, 3, + 253, 98, 93, 1, 253, 98, 93, 1, 253, 145, 93, 1, 253, 132, 93, 1, 253, + 138, 93, 1, 183, 93, 1, 253, 255, 93, 1, 3, 208, 93, 1, 3, 253, 134, 48, + 232, 3, 242, 116, 244, 205, 65, 48, 232, 3, 236, 205, 244, 205, 65, 232, + 3, 242, 116, 244, 205, 65, 232, 3, 236, 205, 244, 205, 65, 93, 238, 101, + 65, 93, 242, 116, 238, 101, 65, 93, 240, 253, 248, 197, 232, 3, 42, 240, + 241, 39, 1, 3, 64, 39, 1, 64, 39, 1, 3, 69, 39, 1, 69, 39, 1, 3, 84, 39, + 1, 84, 39, 1, 3, 70, 39, 1, 70, 39, 1, 3, 78, 39, 1, 78, 39, 1, 208, 39, + 1, 253, 100, 39, 1, 253, 182, 39, 1, 253, 209, 39, 1, 253, 183, 39, 1, + 253, 196, 39, 1, 253, 134, 39, 1, 253, 207, 39, 1, 253, 155, 39, 1, 253, + 195, 39, 1, 253, 97, 39, 1, 253, 129, 39, 1, 253, 158, 39, 1, 253, 218, + 39, 1, 253, 172, 39, 1, 253, 233, 39, 1, 253, 171, 39, 1, 253, 189, 39, + 1, 253, 159, 39, 1, 253, 190, 39, 1, 253, 91, 39, 1, 253, 93, 39, 1, 253, + 174, 39, 1, 253, 165, 39, 1, 3, 253, 160, 39, 1, 253, 160, 39, 1, 253, + 204, 39, 1, 253, 151, 39, 1, 253, 152, 39, 1, 76, 39, 1, 253, 193, 39, 1, + 253, 94, 39, 1, 253, 148, 39, 1, 253, 112, 39, 1, 253, 157, 39, 1, 253, + 136, 39, 1, 253, 88, 39, 1, 253, 110, 39, 1, 253, 90, 39, 1, 253, 154, + 39, 1, 253, 236, 39, 1, 253, 116, 39, 1, 253, 197, 39, 1, 253, 192, 39, + 1, 253, 231, 39, 1, 253, 125, 39, 1, 253, 191, 39, 1, 253, 143, 39, 1, + 253, 140, 39, 1, 253, 221, 39, 1, 253, 187, 39, 1, 221, 39, 1, 253, 156, + 39, 1, 253, 114, 39, 1, 253, 167, 39, 1, 253, 141, 39, 1, 3, 216, 39, 1, + 216, 39, 1, 3, 253, 124, 39, 1, 253, 124, 39, 1, 3, 253, 123, 39, 1, 253, + 123, 39, 1, 253, 92, 39, 1, 253, 169, 39, 1, 253, 170, 39, 1, 253, 150, + 39, 1, 253, 144, 39, 1, 3, 253, 98, 39, 1, 253, 98, 39, 1, 253, 145, 39, + 1, 253, 132, 39, 1, 253, 138, 39, 1, 183, 39, 1, 253, 255, 39, 1, 3, 208, + 39, 1, 3, 253, 134, 39, 1, 253, 161, 39, 1, 254, 25, 39, 1, 253, 214, 39, + 1, 253, 215, 39, 242, 65, 227, 232, 3, 238, 153, 244, 205, 65, 39, 238, + 101, 65, 39, 242, 116, 238, 101, 65, 39, 240, 253, 247, 39, 134, 1, 199, + 134, 1, 198, 134, 1, 171, 134, 1, 204, 134, 1, 187, 134, 1, 190, 134, 1, + 183, 134, 1, 148, 134, 1, 194, 134, 1, 205, 134, 1, 164, 134, 1, 200, + 134, 1, 209, 134, 1, 170, 134, 1, 254, 211, 134, 1, 254, 94, 134, 1, 254, + 21, 134, 1, 135, 134, 1, 202, 134, 1, 203, 134, 1, 159, 134, 1, 64, 134, + 1, 78, 134, 1, 70, 134, 1, 254, 9, 134, 1, 253, 106, 134, 1, 254, 36, + 134, 1, 253, 109, 134, 1, 253, 247, 134, 1, 253, 235, 134, 1, 253, 166, + 134, 1, 249, 75, 134, 1, 249, 66, 134, 1, 253, 205, 134, 1, 69, 134, 1, + 84, 134, 1, 254, 120, 134, 1, 186, 134, 1, 253, 244, 134, 1, 254, 108, + 20, 1, 240, 242, 20, 1, 236, 59, 20, 1, 236, 63, 20, 1, 242, 131, 20, 1, + 236, 65, 20, 1, 236, 66, 20, 1, 240, 244, 20, 1, 236, 72, 20, 1, 242, + 134, 20, 1, 235, 247, 20, 1, 236, 67, 20, 1, 236, 68, 20, 1, 236, 204, + 20, 1, 235, 197, 20, 1, 235, 196, 20, 1, 236, 57, 20, 1, 242, 129, 20, 1, + 242, 133, 20, 1, 236, 209, 20, 1, 236, 198, 20, 1, 244, 230, 20, 1, 237, + 138, 20, 1, 242, 126, 20, 1, 242, 122, 20, 1, 236, 207, 20, 1, 239, 155, + 20, 1, 239, 158, 20, 1, 239, 166, 20, 1, 239, 161, 20, 1, 242, 125, 20, + 1, 64, 20, 1, 253, 163, 20, 1, 216, 20, 1, 249, 181, 20, 1, 254, 10, 20, + 1, 70, 20, 1, 249, 182, 20, 1, 253, 200, 20, 1, 78, 20, 1, 253, 98, 20, + 1, 249, 175, 20, 1, 253, 142, 20, 1, 253, 123, 20, 1, 84, 20, 1, 249, + 177, 20, 1, 253, 132, 20, 1, 253, 145, 20, 1, 253, 124, 20, 1, 254, 12, + 20, 1, 253, 139, 20, 1, 69, 20, 240, 255, 20, 1, 236, 233, 20, 1, 235, + 246, 20, 1, 236, 220, 20, 1, 235, 201, 20, 1, 231, 250, 20, 1, 236, 1, + 20, 1, 232, 4, 20, 1, 235, 206, 20, 1, 231, 251, 20, 1, 236, 64, 20, 1, + 236, 216, 20, 1, 235, 200, 20, 1, 235, 194, 20, 1, 235, 255, 20, 1, 236, + 0, 20, 1, 231, 248, 20, 1, 231, 249, 20, 1, 236, 77, 20, 1, 235, 205, 20, + 1, 235, 195, 20, 1, 231, 240, 20, 1, 236, 70, 20, 1, 236, 230, 20, 1, + 236, 71, 20, 1, 236, 206, 20, 1, 236, 229, 20, 1, 239, 191, 20, 1, 236, + 208, 20, 1, 238, 134, 20, 1, 235, 210, 20, 1, 232, 5, 20, 1, 232, 14, 20, + 1, 236, 232, 20, 1, 236, 73, 20, 1, 243, 19, 20, 1, 241, 91, 20, 1, 245, + 226, 20, 1, 241, 92, 20, 1, 243, 20, 20, 1, 245, 228, 20, 1, 242, 197, + 20, 1, 241, 100, 66, 237, 111, 241, 202, 65, 66, 237, 111, 240, 219, 65, + 66, 237, 111, 253, 101, 65, 66, 237, 111, 225, 65, 66, 237, 111, 244, + 171, 65, 66, 237, 111, 249, 28, 65, 66, 237, 111, 253, 121, 65, 66, 237, + 111, 242, 65, 65, 66, 237, 111, 242, 80, 65, 66, 237, 111, 245, 84, 65, + 66, 237, 111, 242, 165, 65, 66, 237, 111, 248, 124, 65, 66, 237, 111, + 242, 214, 65, 66, 237, 111, 243, 144, 65, 66, 237, 111, 246, 120, 65, 66, + 237, 111, 254, 55, 65, 134, 1, 253, 192, 134, 1, 253, 218, 134, 1, 253, + 228, 134, 1, 253, 196, 134, 1, 253, 126, 134, 1, 250, 243, 134, 1, 253, + 119, 134, 1, 250, 38, 134, 1, 254, 117, 134, 1, 250, 137, 134, 1, 251, + 112, 134, 1, 252, 240, 134, 1, 254, 115, 134, 1, 252, 18, 134, 1, 245, + 240, 134, 1, 245, 249, 134, 1, 253, 234, 134, 1, 253, 245, 134, 1, 252, + 57, 134, 1, 246, 252, 134, 29, 1, 198, 134, 29, 1, 190, 134, 29, 1, 205, + 134, 29, 1, 164, 39, 1, 3, 253, 183, 39, 1, 3, 253, 158, 39, 1, 3, 253, + 172, 39, 1, 3, 76, 39, 1, 3, 253, 112, 39, 1, 3, 253, 88, 39, 1, 3, 253, + 154, 39, 1, 3, 253, 197, 39, 1, 3, 253, 125, 39, 1, 3, 253, 140, 39, 1, + 3, 253, 114, 39, 1, 3, 253, 92, 39, 1, 3, 253, 169, 39, 1, 3, 253, 170, + 39, 1, 3, 253, 150, 39, 1, 3, 253, 144, 68, 20, 240, 242, 68, 20, 242, + 131, 68, 20, 240, 244, 68, 20, 242, 134, 68, 20, 242, 129, 68, 20, 242, + 133, 68, 20, 244, 230, 68, 20, 242, 126, 68, 20, 242, 122, 68, 20, 239, + 155, 68, 20, 239, 158, 68, 20, 239, 166, 68, 20, 239, 161, 68, 20, 242, + 125, 68, 20, 242, 227, 64, 68, 20, 245, 149, 64, 68, 20, 243, 12, 64, 68, + 20, 245, 171, 64, 68, 20, 245, 142, 64, 68, 20, 245, 160, 64, 68, 20, + 250, 72, 64, 68, 20, 245, 113, 64, 68, 20, 245, 18, 64, 68, 20, 241, 47, + 64, 68, 20, 241, 61, 64, 68, 20, 241, 86, 64, 68, 20, 241, 72, 64, 68, + 20, 245, 108, 64, 68, 20, 245, 18, 84, 68, 97, 121, 68, 97, 114, 68, 97, + 153, 68, 97, 163, 68, 97, 168, 68, 97, 169, 68, 97, 179, 68, 97, 176, 68, + 97, 178, 68, 97, 249, 18, 68, 97, 244, 229, 68, 97, 244, 236, 68, 97, + 242, 169, 68, 97, 245, 223, 68, 97, 242, 231, 68, 97, 242, 142, 68, 97, + 249, 87, 68, 97, 243, 4, 68, 97, 245, 105, 68, 97, 239, 236, 68, 97, 245, + 146, 68, 97, 239, 238, 68, 97, 237, 155, 68, 97, 234, 66, 68, 97, 242, + 229, 68, 97, 238, 54, 68, 97, 246, 41, 68, 97, 243, 5, 68, 97, 237, 165, + 68, 97, 236, 214, 68, 97, 238, 155, 68, 97, 238, 135, 68, 97, 239, 14, + 68, 97, 244, 187, 68, 97, 245, 179, 68, 97, 244, 54, 28, 83, 242, 104, + 121, 28, 83, 242, 104, 114, 28, 83, 242, 104, 153, 28, 83, 242, 104, 163, + 28, 83, 242, 104, 168, 28, 83, 242, 104, 169, 28, 83, 242, 104, 179, 28, + 83, 242, 104, 176, 28, 83, 242, 104, 178, 28, 83, 241, 7, 28, 83, 242, + 108, 121, 28, 83, 242, 108, 114, 28, 83, 242, 108, 153, 28, 83, 242, 108, + 163, 28, 83, 242, 108, 168, 28, 20, 240, 242, 28, 20, 242, 131, 28, 20, + 240, 244, 28, 20, 242, 134, 28, 20, 242, 129, 28, 20, 242, 133, 28, 20, + 244, 230, 28, 20, 242, 126, 28, 20, 242, 122, 28, 20, 239, 155, 28, 20, + 239, 158, 28, 20, 239, 166, 28, 20, 239, 161, 28, 20, 242, 125, 28, 20, + 242, 227, 64, 28, 20, 245, 149, 64, 28, 20, 243, 12, 64, 28, 20, 245, + 171, 64, 28, 20, 245, 142, 64, 28, 20, 245, 160, 64, 28, 20, 250, 72, 64, + 28, 20, 245, 113, 64, 28, 20, 245, 18, 64, 28, 20, 241, 47, 64, 28, 20, + 241, 61, 64, 28, 20, 241, 86, 64, 28, 20, 241, 72, 64, 28, 20, 245, 108, + 64, 243, 193, 239, 239, 90, 28, 97, 121, 28, 97, 114, 28, 97, 153, 28, + 97, 163, 28, 97, 168, 28, 97, 169, 28, 97, 179, 28, 97, 176, 28, 97, 178, + 28, 97, 249, 18, 28, 97, 244, 229, 28, 97, 244, 236, 28, 97, 242, 169, + 28, 97, 245, 223, 28, 97, 242, 231, 28, 97, 242, 142, 28, 97, 249, 87, + 28, 97, 243, 4, 28, 97, 245, 105, 28, 97, 239, 236, 28, 97, 245, 146, 28, + 97, 239, 238, 28, 97, 237, 155, 28, 97, 234, 66, 28, 97, 242, 229, 28, + 97, 242, 4, 28, 97, 247, 76, 28, 97, 241, 164, 28, 97, 239, 97, 28, 97, + 238, 6, 28, 97, 244, 41, 28, 97, 237, 176, 28, 97, 246, 255, 28, 97, 244, + 187, 28, 97, 246, 74, 28, 97, 240, 19, 28, 97, 246, 177, 28, 97, 241, 46, + 28, 97, 251, 206, 28, 97, 244, 172, 28, 97, 184, 28, 97, 239, 48, 28, 97, + 239, 73, 28, 97, 243, 5, 28, 97, 237, 165, 28, 97, 236, 214, 28, 97, 238, + 155, 28, 97, 238, 135, 28, 97, 243, 248, 28, 83, 242, 108, 169, 28, 83, + 242, 108, 179, 28, 83, 242, 108, 176, 28, 83, 242, 108, 178, 28, 83, 242, + 213, 28, 83, 244, 209, 121, 28, 83, 244, 209, 114, 28, 83, 244, 209, 153, + 28, 83, 244, 209, 163, 28, 83, 244, 209, 168, 28, 83, 244, 209, 169, 28, + 83, 244, 209, 179, 28, 83, 244, 209, 176, 28, 83, 244, 209, 178, 28, 83, + 242, 144, 66, 127, 13, 54, 240, 94, 66, 127, 13, 54, 239, 13, 66, 127, + 13, 54, 243, 214, 66, 127, 13, 54, 243, 31, 66, 127, 13, 54, 251, 222, + 66, 127, 13, 54, 247, 20, 66, 127, 13, 54, 247, 19, 66, 127, 13, 54, 241, + 104, 66, 127, 13, 54, 238, 58, 66, 127, 13, 54, 240, 129, 66, 127, 13, + 54, 239, 53, 66, 127, 13, 54, 238, 199, 28, 39, 64, 28, 39, 69, 28, 39, + 84, 28, 39, 70, 28, 39, 78, 28, 39, 208, 28, 39, 253, 182, 28, 39, 253, + 183, 28, 39, 253, 134, 28, 39, 253, 155, 28, 39, 253, 97, 28, 39, 253, + 158, 28, 39, 253, 172, 28, 39, 253, 171, 28, 39, 253, 159, 28, 39, 253, + 91, 28, 39, 253, 174, 28, 39, 253, 160, 28, 39, 253, 151, 28, 39, 76, 28, + 39, 253, 94, 28, 39, 253, 148, 28, 39, 253, 112, 28, 39, 253, 157, 28, + 39, 253, 136, 28, 39, 253, 88, 28, 39, 253, 154, 28, 39, 253, 197, 28, + 39, 253, 125, 28, 39, 253, 140, 28, 39, 221, 28, 39, 253, 156, 28, 39, + 253, 114, 28, 39, 253, 167, 28, 39, 253, 141, 28, 39, 216, 28, 39, 253, + 124, 28, 39, 253, 123, 28, 39, 253, 92, 28, 39, 253, 169, 28, 39, 253, + 170, 28, 39, 253, 150, 28, 39, 253, 144, 28, 39, 253, 98, 28, 39, 253, + 145, 28, 39, 253, 132, 28, 39, 253, 138, 30, 241, 99, 30, 241, 103, 30, + 243, 30, 30, 245, 236, 30, 241, 186, 30, 246, 253, 30, 252, 241, 30, 239, + 10, 30, 243, 96, 30, 247, 233, 30, 247, 234, 30, 243, 182, 30, 240, 98, + 30, 240, 99, 30, 243, 124, 30, 243, 123, 30, 246, 158, 30, 243, 135, 30, + 241, 195, 30, 240, 81, 30, 247, 36, 30, 237, 72, 30, 236, 141, 30, 238, + 25, 30, 241, 178, 30, 238, 12, 30, 238, 27, 30, 240, 102, 30, 243, 186, + 30, 241, 194, 30, 243, 192, 30, 240, 155, 30, 239, 78, 30, 240, 164, 30, + 244, 74, 30, 244, 75, 30, 243, 78, 30, 246, 105, 30, 246, 114, 30, 252, + 213, 30, 247, 110, 30, 244, 4, 30, 243, 141, 30, 239, 55, 30, 244, 23, + 30, 240, 2, 30, 238, 42, 30, 241, 238, 30, 247, 248, 30, 246, 28, 30, + 239, 28, 30, 241, 182, 30, 238, 185, 30, 243, 164, 30, 238, 13, 30, 247, + 241, 30, 241, 236, 30, 241, 181, 30, 244, 31, 30, 244, 28, 30, 243, 41, + 30, 241, 241, 30, 241, 116, 30, 251, 88, 30, 244, 39, 30, 243, 162, 30, + 246, 231, 30, 240, 107, 30, 243, 210, 30, 243, 209, 30, 241, 206, 30, + 240, 109, 30, 240, 116, 30, 247, 98, 30, 238, 33, 30, 245, 29, 30, 240, + 114, 30, 240, 113, 30, 244, 152, 30, 244, 153, 30, 248, 201, 30, 240, + 153, 30, 248, 23, 30, 244, 66, 30, 240, 154, 30, 248, 20, 30, 239, 74, + 30, 244, 146, 66, 127, 13, 54, 249, 6, 244, 173, 66, 127, 13, 54, 249, 6, + 121, 66, 127, 13, 54, 249, 6, 114, 66, 127, 13, 54, 249, 6, 153, 66, 127, + 13, 54, 249, 6, 163, 66, 127, 13, 54, 249, 6, 168, 66, 127, 13, 54, 249, + 6, 169, 66, 127, 13, 54, 249, 6, 179, 66, 127, 13, 54, 249, 6, 176, 66, + 127, 13, 54, 249, 6, 178, 66, 127, 13, 54, 249, 6, 249, 18, 66, 127, 13, + 54, 249, 6, 240, 234, 66, 127, 13, 54, 249, 6, 240, 238, 66, 127, 13, 54, + 249, 6, 238, 108, 66, 127, 13, 54, 249, 6, 238, 106, 66, 127, 13, 54, + 249, 6, 239, 167, 66, 127, 13, 54, 249, 6, 239, 162, 66, 127, 13, 54, + 249, 6, 237, 129, 66, 127, 13, 54, 249, 6, 238, 105, 66, 127, 13, 54, + 249, 6, 238, 107, 66, 127, 13, 54, 249, 6, 240, 239, 66, 127, 13, 54, + 249, 6, 236, 238, 66, 127, 13, 54, 249, 6, 236, 239, 66, 127, 13, 54, + 249, 6, 236, 4, 66, 127, 13, 54, 249, 6, 236, 82, 30, 251, 93, 30, 253, + 93, 30, 253, 109, 30, 143, 30, 254, 163, 30, 254, 165, 30, 253, 238, 249, + 109, 241, 180, 30, 253, 238, 249, 109, 242, 29, 30, 253, 238, 249, 109, + 240, 175, 30, 253, 238, 249, 109, 243, 217, 30, 236, 91, 30, 255, 19, + 245, 245, 30, 253, 94, 30, 254, 219, 64, 30, 221, 30, 208, 30, 254, 124, + 30, 254, 140, 30, 254, 106, 30, 250, 175, 30, 247, 28, 30, 254, 167, 30, + 254, 154, 30, 254, 219, 204, 30, 254, 219, 194, 30, 254, 143, 30, 254, + 50, 30, 254, 114, 30, 251, 153, 30, 251, 230, 30, 251, 37, 30, 252, 207, + 30, 254, 219, 148, 30, 254, 146, 30, 254, 98, 30, 254, 127, 30, 254, 104, + 30, 254, 158, 30, 254, 219, 171, 30, 254, 147, 30, 254, 95, 30, 254, 128, + 30, 254, 251, 239, 163, 30, 254, 244, 239, 163, 30, 255, 39, 239, 163, + 30, 254, 242, 239, 163, 30, 254, 251, 242, 155, 30, 254, 244, 242, 155, + 30, 255, 39, 242, 155, 30, 254, 242, 242, 155, 30, 255, 39, 249, 25, 159, + 30, 255, 39, 249, 25, 255, 29, 239, 163, 30, 253, 90, 30, 251, 169, 30, + 251, 242, 30, 251, 44, 30, 252, 115, 30, 254, 20, 249, 25, 159, 30, 254, + 20, 249, 25, 255, 29, 239, 163, 30, 254, 173, 30, 254, 159, 30, 254, 219, + 159, 30, 254, 148, 30, 254, 174, 30, 254, 58, 30, 254, 219, 186, 30, 254, + 149, 30, 254, 130, 30, 255, 16, 245, 29, 30, 254, 175, 30, 254, 161, 30, + 254, 219, 201, 30, 254, 150, 30, 254, 52, 30, 255, 17, 245, 29, 30, 255, + 38, 250, 35, 30, 255, 39, 250, 35, 30, 253, 234, 30, 254, 90, 30, 254, + 92, 30, 254, 93, 30, 255, 36, 249, 25, 254, 50, 30, 253, 178, 30, 254, + 97, 30, 254, 110, 30, 253, 88, 30, 254, 113, 30, 253, 227, 30, 254, 51, + 30, 254, 242, 240, 12, 30, 254, 129, 30, 254, 135, 30, 254, 136, 30, 251, + 202, 30, 254, 138, 30, 255, 13, 243, 2, 30, 251, 233, 30, 251, 239, 30, + 254, 168, 30, 254, 170, 30, 252, 74, 30, 254, 172, 30, 254, 185, 30, 254, + 72, 30, 254, 202, 66, 127, 13, 54, 253, 89, 121, 66, 127, 13, 54, 253, + 89, 114, 66, 127, 13, 54, 253, 89, 153, 66, 127, 13, 54, 253, 89, 163, + 66, 127, 13, 54, 253, 89, 168, 66, 127, 13, 54, 253, 89, 169, 66, 127, + 13, 54, 253, 89, 179, 66, 127, 13, 54, 253, 89, 176, 66, 127, 13, 54, + 253, 89, 178, 66, 127, 13, 54, 253, 89, 249, 18, 66, 127, 13, 54, 253, + 89, 240, 234, 66, 127, 13, 54, 253, 89, 240, 238, 66, 127, 13, 54, 253, + 89, 238, 108, 66, 127, 13, 54, 253, 89, 238, 106, 66, 127, 13, 54, 253, + 89, 239, 167, 66, 127, 13, 54, 253, 89, 239, 162, 66, 127, 13, 54, 253, + 89, 237, 129, 66, 127, 13, 54, 253, 89, 238, 105, 66, 127, 13, 54, 253, + 89, 238, 107, 66, 127, 13, 54, 253, 89, 240, 239, 66, 127, 13, 54, 253, + 89, 236, 238, 66, 127, 13, 54, 253, 89, 236, 239, 66, 127, 13, 54, 253, + 89, 236, 4, 66, 127, 13, 54, 253, 89, 236, 82, 66, 127, 13, 54, 253, 89, + 236, 177, 66, 127, 13, 54, 253, 89, 237, 107, 66, 127, 13, 54, 253, 89, + 236, 42, 66, 127, 13, 54, 253, 89, 236, 41, 66, 127, 13, 54, 253, 89, + 236, 178, 66, 127, 13, 54, 253, 89, 241, 7, 66, 127, 13, 54, 253, 89, + 237, 104, 6, 4, 254, 119, 6, 4, 254, 121, 6, 4, 69, 6, 4, 254, 116, 6, 4, + 251, 109, 6, 4, 251, 110, 6, 4, 253, 181, 6, 4, 251, 108, 6, 4, 253, 220, + 6, 4, 254, 86, 6, 4, 64, 6, 4, 254, 83, 6, 4, 252, 243, 6, 4, 254, 197, + 6, 4, 252, 242, 6, 4, 254, 17, 6, 4, 254, 164, 6, 4, 78, 6, 4, 254, 63, + 6, 4, 254, 101, 6, 4, 70, 6, 4, 253, 216, 6, 4, 250, 160, 6, 4, 250, 161, + 6, 4, 253, 236, 6, 4, 250, 159, 6, 4, 246, 26, 6, 4, 246, 27, 6, 4, 250, + 157, 6, 4, 246, 25, 6, 4, 250, 142, 6, 4, 250, 143, 6, 4, 253, 110, 6, 4, + 250, 141, 6, 4, 246, 36, 6, 4, 250, 165, 6, 4, 246, 35, 6, 4, 250, 164, + 6, 4, 249, 47, 6, 4, 253, 221, 6, 4, 250, 163, 6, 4, 250, 156, 6, 4, 253, + 191, 6, 4, 250, 153, 6, 4, 250, 167, 6, 4, 250, 168, 6, 4, 253, 192, 6, + 4, 250, 166, 6, 4, 246, 37, 6, 4, 249, 193, 6, 4, 250, 172, 6, 4, 250, + 173, 6, 4, 254, 30, 6, 4, 250, 170, 6, 4, 246, 38, 6, 4, 250, 171, 6, 4, + 252, 68, 6, 4, 252, 69, 6, 4, 253, 116, 6, 4, 252, 67, 6, 4, 247, 247, 6, + 4, 252, 64, 6, 4, 247, 246, 6, 4, 252, 60, 6, 4, 252, 61, 6, 4, 253, 90, + 6, 4, 252, 59, 6, 4, 247, 252, 6, 4, 252, 75, 6, 4, 247, 251, 6, 4, 252, + 72, 6, 4, 252, 73, 6, 4, 253, 187, 6, 4, 252, 71, 6, 4, 250, 48, 6, 4, + 252, 78, 6, 4, 253, 231, 6, 4, 252, 76, 6, 4, 247, 253, 6, 4, 252, 77, 6, + 4, 250, 52, 6, 4, 252, 81, 6, 4, 254, 176, 6, 4, 252, 79, 6, 4, 247, 254, + 6, 4, 252, 80, 6, 4, 246, 5, 6, 4, 246, 6, 6, 4, 250, 146, 6, 4, 246, 4, + 6, 4, 243, 36, 6, 4, 243, 37, 6, 4, 246, 3, 6, 4, 243, 35, 6, 4, 245, + 255, 6, 4, 246, 0, 6, 4, 250, 144, 6, 4, 245, 254, 6, 4, 243, 39, 6, 4, + 246, 10, 6, 4, 243, 38, 6, 4, 246, 8, 6, 4, 246, 9, 6, 4, 250, 147, 6, 4, + 246, 7, 6, 4, 246, 2, 6, 4, 250, 145, 6, 4, 246, 1, 6, 4, 245, 58, 6, 4, + 246, 13, 6, 4, 250, 148, 6, 4, 246, 11, 6, 4, 243, 40, 6, 4, 246, 12, 6, + 4, 246, 15, 6, 4, 246, 16, 6, 4, 250, 149, 6, 4, 246, 14, 6, 4, 247, 115, + 6, 4, 247, 116, 6, 4, 252, 3, 6, 4, 247, 114, 6, 4, 243, 237, 6, 4, 245, + 148, 6, 4, 243, 236, 6, 4, 247, 112, 6, 4, 247, 113, 6, 4, 252, 2, 6, 4, + 247, 111, 6, 4, 247, 118, 6, 4, 247, 119, 6, 4, 252, 4, 6, 4, 247, 117, + 6, 4, 247, 122, 6, 4, 247, 123, 6, 4, 252, 5, 6, 4, 247, 120, 6, 4, 243, + 238, 6, 4, 247, 121, 6, 4, 247, 126, 6, 4, 247, 127, 6, 4, 252, 6, 6, 4, + 247, 124, 6, 4, 243, 239, 6, 4, 247, 125, 6, 4, 246, 204, 6, 4, 246, 205, + 6, 4, 251, 83, 6, 4, 246, 203, 6, 4, 243, 153, 6, 4, 246, 202, 6, 4, 243, + 152, 6, 4, 246, 200, 6, 4, 246, 201, 6, 4, 251, 82, 6, 4, 246, 199, 6, 4, + 243, 155, 6, 4, 246, 209, 6, 4, 243, 154, 6, 4, 246, 207, 6, 4, 246, 208, + 6, 4, 249, 243, 6, 4, 246, 206, 6, 4, 246, 212, 6, 4, 246, 213, 6, 4, + 251, 84, 6, 4, 246, 210, 6, 4, 243, 156, 6, 4, 246, 211, 6, 4, 246, 216, + 6, 4, 251, 85, 6, 4, 246, 214, 6, 4, 243, 157, 6, 4, 246, 215, 6, 4, 251, + 237, 6, 4, 251, 238, 6, 4, 253, 156, 6, 4, 251, 236, 6, 4, 247, 95, 6, 4, + 251, 231, 6, 4, 247, 94, 6, 4, 251, 220, 6, 4, 251, 221, 6, 4, 221, 6, 4, + 251, 218, 6, 4, 247, 104, 6, 4, 247, 105, 6, 4, 251, 243, 6, 4, 247, 103, + 6, 4, 251, 240, 6, 4, 251, 241, 6, 4, 253, 141, 6, 4, 250, 21, 6, 4, 251, + 226, 6, 4, 253, 167, 6, 4, 251, 246, 6, 4, 251, 247, 6, 4, 253, 114, 6, + 4, 251, 244, 6, 4, 247, 106, 6, 4, 251, 245, 6, 4, 251, 250, 6, 4, 251, + 251, 6, 4, 254, 151, 6, 4, 251, 249, 6, 4, 251, 10, 6, 4, 251, 11, 6, 4, + 253, 226, 6, 4, 251, 9, 6, 4, 250, 253, 6, 4, 250, 254, 6, 4, 253, 153, + 6, 4, 250, 252, 6, 4, 251, 14, 6, 4, 254, 13, 6, 4, 251, 13, 6, 4, 251, + 16, 6, 4, 251, 17, 6, 4, 254, 41, 6, 4, 251, 15, 6, 4, 246, 133, 6, 4, + 249, 225, 6, 4, 251, 21, 6, 4, 251, 22, 6, 4, 254, 105, 6, 4, 251, 20, 6, + 4, 252, 255, 6, 4, 253, 0, 6, 4, 254, 25, 6, 4, 252, 254, 6, 4, 248, 165, + 6, 4, 248, 166, 6, 4, 252, 253, 6, 4, 248, 164, 6, 4, 252, 249, 6, 4, + 252, 250, 6, 4, 253, 161, 6, 4, 252, 248, 6, 4, 253, 3, 6, 4, 253, 4, 6, + 4, 253, 215, 6, 4, 253, 2, 6, 4, 252, 252, 6, 4, 250, 103, 6, 4, 253, 6, + 6, 4, 253, 7, 6, 4, 253, 255, 6, 4, 253, 5, 6, 4, 248, 167, 6, 4, 250, + 108, 6, 4, 253, 11, 6, 4, 253, 12, 6, 4, 254, 201, 6, 4, 253, 9, 6, 4, + 248, 168, 6, 4, 253, 10, 6, 4, 250, 219, 6, 4, 250, 220, 6, 4, 253, 165, + 6, 4, 250, 218, 6, 4, 246, 107, 6, 4, 250, 217, 6, 4, 246, 106, 6, 4, + 250, 209, 6, 4, 250, 211, 6, 4, 253, 93, 6, 4, 250, 207, 6, 4, 246, 116, + 6, 4, 250, 231, 6, 4, 227, 6, 4, 250, 227, 6, 4, 253, 193, 6, 4, 250, + 226, 6, 4, 250, 215, 6, 4, 253, 152, 6, 4, 250, 214, 6, 4, 250, 234, 6, + 4, 250, 235, 6, 4, 253, 204, 6, 4, 250, 232, 6, 4, 246, 117, 6, 4, 250, + 233, 6, 4, 252, 210, 6, 4, 252, 211, 6, 4, 253, 174, 6, 4, 252, 209, 6, + 4, 248, 128, 6, 4, 249, 89, 6, 4, 248, 127, 6, 4, 250, 85, 6, 4, 252, + 198, 6, 4, 253, 91, 6, 4, 252, 195, 6, 4, 248, 154, 6, 4, 248, 155, 6, 4, + 252, 220, 6, 4, 248, 153, 6, 4, 252, 214, 6, 4, 252, 215, 6, 4, 76, 6, 4, + 249, 168, 6, 4, 252, 204, 6, 4, 253, 151, 6, 4, 252, 200, 6, 4, 252, 223, + 6, 4, 252, 224, 6, 4, 253, 160, 6, 4, 252, 221, 6, 4, 248, 156, 6, 4, + 252, 222, 6, 4, 246, 92, 6, 4, 246, 93, 6, 4, 249, 210, 6, 4, 246, 91, 6, + 4, 243, 86, 6, 4, 246, 90, 6, 4, 243, 85, 6, 4, 246, 85, 6, 4, 246, 86, + 6, 4, 249, 27, 6, 4, 246, 84, 6, 4, 243, 88, 6, 4, 246, 97, 6, 4, 243, + 87, 6, 4, 246, 95, 6, 4, 246, 96, 6, 4, 249, 211, 6, 4, 246, 94, 6, 4, + 246, 89, 6, 4, 249, 209, 6, 4, 246, 88, 6, 4, 246, 99, 6, 4, 246, 100, 6, + 4, 249, 212, 6, 4, 246, 98, 6, 4, 243, 89, 6, 4, 245, 77, 6, 4, 247, 135, + 6, 4, 247, 136, 6, 4, 252, 9, 6, 4, 247, 134, 6, 4, 243, 240, 6, 4, 247, + 133, 6, 4, 247, 129, 6, 4, 247, 130, 6, 4, 252, 7, 6, 4, 247, 128, 6, 4, + 247, 138, 6, 4, 247, 139, 6, 4, 252, 10, 6, 4, 247, 137, 6, 4, 247, 132, + 6, 4, 252, 8, 6, 4, 247, 131, 6, 4, 247, 142, 6, 4, 247, 143, 6, 4, 252, + 11, 6, 4, 247, 140, 6, 4, 243, 241, 6, 4, 247, 141, 6, 4, 246, 224, 6, 4, + 246, 225, 6, 4, 251, 87, 6, 4, 246, 223, 6, 4, 243, 159, 6, 4, 243, 160, + 6, 4, 246, 222, 6, 4, 243, 158, 6, 4, 246, 218, 6, 4, 246, 219, 6, 4, + 249, 244, 6, 4, 246, 217, 6, 4, 243, 161, 6, 4, 246, 229, 6, 4, 246, 227, + 6, 4, 246, 228, 6, 4, 246, 226, 6, 4, 246, 221, 6, 4, 251, 86, 6, 4, 246, + 220, 6, 4, 246, 230, 6, 4, 252, 25, 6, 4, 252, 26, 6, 4, 253, 148, 6, 4, + 252, 24, 6, 4, 247, 159, 6, 4, 252, 22, 6, 4, 247, 158, 6, 4, 252, 1, 6, + 4, 253, 94, 6, 4, 251, 255, 6, 4, 247, 199, 6, 4, 252, 42, 6, 4, 247, + 198, 6, 4, 249, 151, 6, 4, 252, 35, 6, 4, 253, 136, 6, 4, 252, 33, 6, 4, + 252, 15, 6, 4, 253, 157, 6, 4, 252, 13, 6, 4, 252, 45, 6, 4, 252, 46, 6, + 4, 253, 112, 6, 4, 252, 43, 6, 4, 247, 200, 6, 4, 252, 44, 6, 4, 246, + 186, 6, 4, 246, 187, 6, 4, 251, 78, 6, 4, 246, 185, 6, 4, 243, 147, 6, 4, + 246, 184, 6, 4, 243, 146, 6, 4, 246, 180, 6, 4, 246, 181, 6, 4, 251, 76, + 6, 4, 246, 179, 6, 4, 243, 149, 6, 4, 246, 190, 6, 4, 243, 148, 6, 4, + 246, 189, 6, 4, 251, 79, 6, 4, 246, 188, 6, 4, 246, 183, 6, 4, 251, 77, + 6, 4, 246, 182, 6, 4, 246, 193, 6, 4, 246, 194, 6, 4, 251, 80, 6, 4, 246, + 191, 6, 4, 243, 150, 6, 4, 246, 192, 6, 4, 246, 197, 6, 4, 246, 198, 6, + 4, 251, 81, 6, 4, 246, 195, 6, 4, 243, 151, 6, 4, 246, 196, 6, 4, 251, + 200, 6, 4, 251, 201, 6, 4, 253, 211, 6, 4, 251, 198, 6, 4, 247, 65, 6, 4, + 247, 66, 6, 4, 250, 11, 6, 4, 247, 64, 6, 4, 251, 185, 6, 4, 251, 186, 6, + 4, 253, 103, 6, 4, 251, 183, 6, 4, 247, 71, 6, 4, 247, 72, 6, 4, 251, + 208, 6, 4, 247, 70, 6, 4, 251, 205, 6, 4, 251, 207, 6, 4, 253, 184, 6, 4, + 251, 204, 6, 4, 251, 191, 6, 4, 253, 210, 6, 4, 251, 189, 6, 4, 251, 211, + 6, 4, 251, 212, 6, 4, 253, 229, 6, 4, 251, 209, 6, 4, 247, 73, 6, 4, 251, + 210, 6, 4, 251, 214, 6, 4, 251, 215, 6, 4, 254, 54, 6, 4, 251, 213, 6, 4, + 247, 74, 6, 4, 250, 16, 6, 4, 251, 40, 6, 4, 251, 41, 6, 4, 253, 209, 6, + 4, 251, 39, 6, 4, 246, 156, 6, 4, 246, 157, 6, 4, 251, 38, 6, 4, 246, + 155, 6, 4, 251, 26, 6, 4, 251, 27, 6, 4, 253, 100, 6, 4, 251, 24, 6, 4, + 246, 164, 6, 4, 246, 165, 6, 4, 251, 47, 6, 4, 246, 163, 6, 4, 249, 238, + 6, 4, 251, 43, 6, 4, 253, 195, 6, 4, 251, 42, 6, 4, 251, 31, 6, 4, 251, + 33, 6, 4, 253, 207, 6, 4, 249, 230, 6, 4, 251, 50, 6, 4, 251, 51, 6, 4, + 253, 196, 6, 4, 251, 48, 6, 4, 246, 166, 6, 4, 251, 49, 6, 4, 250, 1, 6, + 4, 251, 158, 6, 4, 253, 182, 6, 4, 251, 157, 6, 4, 247, 35, 6, 4, 251, + 154, 6, 4, 247, 34, 6, 4, 251, 143, 6, 4, 251, 145, 6, 4, 208, 6, 4, 251, + 142, 6, 4, 247, 41, 6, 4, 251, 171, 6, 4, 247, 40, 6, 4, 251, 167, 6, 4, + 251, 168, 6, 4, 253, 155, 6, 4, 251, 166, 6, 4, 251, 148, 6, 4, 251, 149, + 6, 4, 253, 134, 6, 4, 251, 147, 6, 4, 251, 174, 6, 4, 251, 175, 6, 4, + 253, 183, 6, 4, 251, 172, 6, 4, 247, 42, 6, 4, 251, 173, 6, 4, 246, 145, + 6, 4, 246, 146, 6, 4, 249, 233, 6, 4, 243, 128, 6, 4, 246, 144, 6, 4, + 243, 127, 6, 4, 246, 138, 6, 4, 246, 139, 6, 4, 249, 231, 6, 4, 246, 137, + 6, 4, 243, 130, 6, 4, 243, 131, 6, 4, 245, 94, 6, 4, 243, 129, 6, 4, 246, + 147, 6, 4, 246, 148, 6, 4, 249, 234, 6, 4, 245, 93, 6, 4, 246, 142, 6, 4, + 246, 143, 6, 4, 249, 232, 6, 4, 246, 141, 6, 4, 246, 151, 6, 4, 246, 152, + 6, 4, 249, 235, 6, 4, 246, 149, 6, 4, 243, 132, 6, 4, 246, 150, 6, 4, + 243, 223, 6, 4, 247, 84, 6, 4, 247, 80, 6, 4, 247, 81, 6, 4, 251, 227, 6, + 4, 247, 79, 6, 4, 243, 225, 6, 4, 247, 88, 6, 4, 243, 224, 6, 4, 247, 86, + 6, 4, 247, 87, 6, 4, 249, 107, 6, 4, 247, 85, 6, 4, 247, 83, 6, 4, 251, + 228, 6, 4, 247, 82, 6, 4, 247, 91, 6, 4, 247, 92, 6, 4, 251, 229, 6, 4, + 247, 89, 6, 4, 243, 226, 6, 4, 247, 90, 6, 4, 245, 110, 6, 4, 246, 243, + 6, 4, 251, 102, 6, 4, 246, 242, 6, 4, 243, 166, 6, 4, 243, 167, 6, 4, + 246, 241, 6, 4, 243, 165, 6, 4, 246, 237, 6, 4, 246, 238, 6, 4, 251, 100, + 6, 4, 246, 236, 6, 4, 243, 169, 6, 4, 243, 170, 6, 4, 245, 112, 6, 4, + 243, 168, 6, 4, 246, 244, 6, 4, 246, 245, 6, 4, 251, 103, 6, 4, 245, 111, + 6, 4, 246, 240, 6, 4, 251, 101, 6, 4, 246, 239, 6, 4, 243, 244, 6, 4, + 247, 151, 6, 4, 243, 243, 6, 4, 247, 147, 6, 4, 247, 148, 6, 4, 249, 13, + 6, 4, 247, 146, 6, 4, 243, 246, 6, 4, 243, 247, 6, 4, 247, 157, 6, 4, + 247, 155, 6, 4, 247, 156, 6, 4, 249, 110, 6, 4, 247, 154, 6, 4, 247, 150, + 6, 4, 252, 17, 6, 4, 247, 149, 6, 4, 251, 75, 6, 4, 246, 176, 6, 4, 249, + 137, 6, 4, 251, 59, 6, 4, 251, 60, 6, 4, 253, 88, 6, 4, 251, 58, 6, 4, + 246, 233, 6, 4, 246, 234, 6, 4, 251, 94, 6, 4, 246, 232, 6, 4, 251, 91, + 6, 4, 251, 92, 6, 4, 253, 140, 6, 4, 251, 90, 6, 4, 251, 67, 6, 4, 253, + 125, 6, 4, 251, 65, 6, 4, 253, 14, 6, 4, 253, 15, 6, 4, 253, 98, 6, 4, + 253, 13, 6, 4, 248, 177, 6, 4, 253, 21, 6, 4, 248, 176, 6, 4, 253, 20, 6, + 4, 253, 138, 6, 4, 253, 18, 6, 4, 253, 17, 6, 4, 253, 132, 6, 4, 253, 16, + 6, 4, 253, 67, 6, 4, 253, 68, 6, 4, 253, 218, 6, 4, 253, 66, 6, 4, 248, + 230, 6, 4, 253, 65, 6, 4, 248, 229, 6, 4, 253, 60, 6, 4, 253, 61, 6, 4, + 253, 129, 6, 4, 253, 59, 6, 4, 248, 232, 6, 4, 253, 73, 6, 4, 248, 231, + 6, 4, 250, 129, 6, 4, 253, 71, 6, 4, 253, 190, 6, 4, 253, 70, 6, 4, 253, + 63, 6, 4, 253, 189, 6, 4, 253, 62, 6, 4, 253, 74, 6, 4, 253, 75, 6, 4, + 253, 233, 6, 4, 250, 130, 6, 4, 248, 233, 6, 4, 250, 131, 6, 4, 253, 79, + 6, 4, 253, 80, 6, 4, 254, 213, 6, 4, 253, 77, 6, 4, 248, 234, 6, 4, 253, + 78, 6, 4, 250, 189, 6, 4, 250, 190, 6, 4, 254, 5, 6, 4, 249, 198, 6, 4, + 246, 66, 6, 4, 246, 67, 6, 4, 250, 187, 6, 4, 246, 65, 6, 4, 250, 178, 6, + 4, 250, 179, 6, 4, 253, 115, 6, 4, 249, 195, 6, 4, 246, 75, 6, 4, 250, + 195, 6, 4, 245, 71, 6, 4, 250, 192, 6, 4, 250, 193, 6, 4, 253, 178, 6, 4, + 250, 191, 6, 4, 250, 183, 6, 4, 254, 4, 6, 4, 250, 182, 6, 4, 250, 197, + 6, 4, 250, 198, 6, 4, 254, 32, 6, 4, 249, 202, 6, 4, 246, 76, 6, 4, 250, + 196, 6, 4, 249, 207, 6, 4, 250, 201, 6, 4, 254, 33, 6, 4, 249, 206, 6, 4, + 246, 77, 6, 4, 250, 200, 6, 4, 248, 242, 6, 4, 248, 243, 6, 4, 253, 83, + 6, 4, 248, 241, 6, 4, 243, 21, 6, 4, 244, 168, 6, 4, 248, 240, 6, 4, 244, + 167, 6, 4, 248, 236, 6, 4, 248, 237, 6, 4, 253, 81, 6, 4, 248, 235, 6, 4, + 248, 245, 6, 4, 253, 84, 6, 4, 248, 244, 6, 4, 248, 239, 6, 4, 253, 82, + 6, 4, 248, 238, 6, 4, 248, 248, 6, 4, 253, 85, 6, 4, 248, 246, 6, 4, 244, + 169, 6, 4, 248, 247, 6, 4, 248, 251, 6, 4, 248, 252, 6, 4, 253, 86, 6, 4, + 248, 249, 6, 4, 244, 170, 6, 4, 248, 250, 6, 4, 247, 220, 6, 4, 247, 221, + 6, 4, 252, 50, 6, 4, 247, 219, 6, 4, 244, 14, 6, 4, 247, 218, 6, 4, 244, + 13, 6, 4, 247, 215, 6, 4, 247, 216, 6, 4, 252, 48, 6, 4, 247, 214, 6, 4, + 244, 15, 6, 4, 247, 224, 6, 4, 247, 223, 6, 4, 247, 222, 6, 4, 247, 217, + 6, 4, 252, 49, 6, 4, 247, 226, 6, 4, 252, 51, 6, 4, 245, 155, 6, 4, 244, + 16, 6, 4, 247, 225, 6, 4, 247, 229, 6, 4, 247, 230, 6, 4, 252, 52, 6, 4, + 247, 227, 6, 4, 244, 17, 6, 4, 247, 228, 6, 4, 252, 168, 6, 4, 169, 6, 4, + 253, 158, 6, 4, 252, 167, 6, 4, 248, 74, 6, 4, 252, 164, 6, 4, 248, 73, + 6, 4, 252, 154, 6, 4, 252, 155, 6, 4, 253, 97, 6, 4, 252, 153, 6, 4, 248, + 108, 6, 4, 252, 180, 6, 4, 248, 107, 6, 4, 252, 172, 6, 4, 252, 173, 6, + 4, 253, 159, 6, 4, 252, 171, 6, 4, 252, 159, 6, 4, 253, 171, 6, 4, 252, + 158, 6, 4, 252, 183, 6, 4, 252, 184, 6, 4, 253, 172, 6, 4, 252, 181, 6, + 4, 248, 109, 6, 4, 252, 182, 6, 4, 252, 187, 6, 4, 252, 188, 6, 4, 254, + 188, 6, 4, 252, 185, 6, 4, 248, 111, 6, 4, 252, 186, 6, 4, 248, 91, 6, 4, + 248, 92, 6, 4, 250, 75, 6, 4, 248, 90, 6, 4, 244, 101, 6, 4, 248, 89, 6, + 4, 244, 100, 6, 4, 248, 85, 6, 4, 248, 86, 6, 4, 249, 34, 6, 4, 248, 84, + 6, 4, 248, 94, 6, 4, 248, 95, 6, 4, 250, 76, 6, 4, 248, 93, 6, 4, 248, + 88, 6, 4, 252, 174, 6, 4, 248, 87, 6, 4, 248, 97, 6, 4, 248, 98, 6, 4, + 252, 175, 6, 4, 248, 96, 6, 4, 248, 101, 6, 4, 248, 102, 6, 4, 252, 176, + 6, 4, 248, 99, 6, 4, 244, 102, 6, 4, 248, 100, 6, 4, 248, 211, 6, 4, 248, + 212, 6, 4, 249, 52, 6, 4, 248, 210, 6, 4, 244, 161, 6, 4, 248, 219, 6, 4, + 244, 160, 6, 4, 248, 217, 6, 4, 248, 218, 6, 4, 250, 126, 6, 4, 248, 216, + 6, 4, 248, 214, 6, 4, 248, 215, 6, 4, 249, 74, 6, 4, 248, 213, 6, 4, 248, + 222, 6, 4, 248, 223, 6, 4, 250, 127, 6, 4, 248, 220, 6, 4, 244, 162, 6, + 4, 248, 221, 6, 4, 248, 227, 6, 4, 248, 228, 6, 4, 253, 64, 6, 4, 248, + 225, 6, 4, 244, 163, 6, 4, 248, 226, 6, 4, 246, 47, 6, 4, 246, 48, 6, 4, + 249, 11, 6, 4, 246, 46, 6, 4, 243, 67, 6, 4, 243, 68, 6, 4, 246, 56, 6, + 4, 243, 66, 6, 4, 246, 54, 6, 4, 246, 55, 6, 4, 249, 94, 6, 4, 246, 53, + 6, 4, 246, 50, 6, 4, 246, 51, 6, 4, 249, 44, 6, 4, 246, 49, 6, 4, 246, + 59, 6, 4, 249, 131, 6, 4, 246, 57, 6, 4, 243, 69, 6, 4, 246, 58, 6, 4, + 246, 63, 6, 4, 246, 64, 6, 4, 250, 186, 6, 4, 246, 61, 6, 4, 243, 70, 6, + 4, 246, 62, 6, 4, 248, 25, 6, 4, 249, 50, 6, 4, 244, 63, 6, 4, 248, 33, + 6, 4, 248, 31, 6, 4, 248, 32, 6, 4, 252, 134, 6, 4, 248, 30, 6, 4, 248, + 28, 6, 4, 248, 29, 6, 4, 252, 133, 6, 4, 248, 27, 6, 4, 248, 36, 6, 4, + 248, 37, 6, 4, 252, 135, 6, 4, 248, 34, 6, 4, 244, 64, 6, 4, 248, 35, 6, + 4, 248, 40, 6, 4, 248, 41, 6, 4, 252, 136, 6, 4, 248, 38, 6, 4, 244, 65, + 6, 4, 248, 39, 6, 4, 247, 182, 6, 4, 247, 183, 6, 4, 252, 36, 6, 4, 247, + 181, 6, 4, 247, 188, 6, 4, 252, 38, 6, 4, 247, 187, 6, 4, 247, 185, 6, 4, + 247, 186, 6, 4, 252, 37, 6, 4, 247, 184, 6, 4, 247, 191, 6, 4, 247, 192, + 6, 4, 252, 39, 6, 4, 247, 189, 6, 4, 244, 5, 6, 4, 247, 190, 6, 4, 247, + 195, 6, 4, 247, 196, 6, 4, 252, 40, 6, 4, 247, 193, 6, 4, 244, 6, 6, 4, + 247, 194, 6, 4, 245, 181, 6, 4, 248, 57, 6, 4, 249, 7, 6, 4, 248, 56, 6, + 4, 244, 81, 6, 4, 248, 65, 6, 4, 244, 80, 6, 4, 248, 63, 6, 4, 248, 64, + 6, 4, 249, 85, 6, 4, 245, 187, 6, 4, 248, 59, 6, 4, 248, 60, 6, 4, 249, + 70, 6, 4, 248, 58, 6, 4, 248, 67, 6, 4, 248, 68, 6, 4, 249, 162, 6, 4, + 248, 66, 6, 4, 244, 82, 6, 4, 245, 188, 6, 4, 248, 71, 6, 4, 248, 72, 6, + 4, 250, 71, 6, 4, 248, 69, 6, 4, 244, 83, 6, 4, 248, 70, 6, 4, 250, 61, + 6, 4, 252, 118, 6, 4, 253, 92, 6, 4, 249, 158, 6, 4, 248, 45, 6, 4, 252, + 139, 6, 4, 248, 44, 6, 4, 252, 131, 6, 4, 252, 132, 6, 4, 253, 144, 6, 4, + 252, 130, 6, 4, 252, 123, 6, 4, 253, 150, 6, 4, 252, 121, 6, 4, 252, 142, + 6, 4, 252, 143, 6, 4, 253, 170, 6, 4, 252, 140, 6, 4, 248, 46, 6, 4, 252, + 141, 6, 4, 252, 148, 6, 4, 252, 149, 6, 4, 254, 70, 6, 4, 252, 146, 6, 4, + 248, 48, 6, 4, 252, 147, 6, 4, 251, 124, 6, 4, 251, 125, 6, 4, 253, 228, + 6, 4, 251, 123, 6, 4, 247, 3, 6, 4, 247, 4, 6, 4, 251, 122, 6, 4, 247, 2, + 6, 4, 247, 22, 6, 4, 247, 23, 6, 4, 251, 130, 6, 4, 247, 21, 6, 4, 249, + 69, 6, 4, 251, 129, 6, 4, 253, 240, 6, 4, 251, 128, 6, 4, 251, 133, 6, 4, + 251, 134, 6, 4, 253, 241, 6, 4, 251, 131, 6, 4, 247, 24, 6, 4, 251, 132, + 6, 4, 251, 138, 6, 4, 251, 139, 6, 4, 254, 123, 6, 4, 251, 136, 6, 4, + 247, 25, 6, 4, 251, 137, 6, 4, 252, 93, 6, 4, 252, 94, 6, 4, 253, 249, 6, + 4, 252, 92, 6, 4, 248, 7, 6, 4, 248, 8, 6, 4, 252, 91, 6, 4, 248, 6, 6, + 4, 248, 10, 6, 4, 248, 11, 6, 4, 250, 58, 6, 4, 248, 9, 6, 4, 250, 57, 6, + 4, 252, 98, 6, 4, 254, 18, 6, 4, 252, 97, 6, 4, 252, 105, 6, 4, 252, 107, + 6, 4, 254, 19, 6, 4, 252, 103, 6, 4, 248, 12, 6, 4, 252, 104, 6, 4, 252, + 113, 6, 4, 252, 114, 6, 4, 254, 178, 6, 4, 252, 111, 6, 4, 248, 16, 6, 4, + 252, 112, 6, 4, 247, 7, 6, 4, 247, 8, 6, 4, 249, 248, 6, 4, 247, 6, 6, 4, + 243, 176, 6, 4, 243, 177, 6, 4, 245, 118, 6, 4, 243, 175, 6, 4, 243, 179, + 6, 4, 247, 12, 6, 4, 243, 178, 6, 4, 247, 10, 6, 4, 247, 11, 6, 4, 249, + 249, 6, 4, 247, 9, 6, 4, 245, 119, 6, 4, 247, 15, 6, 4, 249, 250, 6, 4, + 247, 13, 6, 4, 243, 180, 6, 4, 247, 14, 6, 4, 247, 17, 6, 4, 247, 18, 6, + 4, 249, 251, 6, 4, 247, 16, 6, 4, 247, 163, 6, 4, 247, 164, 6, 4, 252, + 27, 6, 4, 247, 162, 6, 4, 243, 250, 6, 4, 243, 251, 6, 4, 247, 161, 6, 4, + 243, 249, 6, 4, 243, 252, 6, 4, 247, 168, 6, 4, 247, 166, 6, 4, 247, 167, + 6, 4, 252, 28, 6, 4, 247, 165, 6, 4, 247, 171, 6, 4, 252, 29, 6, 4, 247, + 169, 6, 4, 243, 253, 6, 4, 247, 170, 6, 4, 247, 174, 6, 4, 247, 175, 6, + 4, 252, 30, 6, 4, 247, 172, 6, 4, 243, 254, 6, 4, 247, 173, 6, 4, 247, + 205, 6, 4, 247, 206, 6, 4, 250, 33, 6, 4, 245, 153, 6, 4, 244, 9, 6, 4, + 244, 10, 6, 4, 247, 204, 6, 4, 244, 8, 6, 4, 244, 12, 6, 4, 247, 209, 6, + 4, 244, 11, 6, 4, 247, 207, 6, 4, 247, 208, 6, 4, 249, 84, 6, 4, 245, + 154, 6, 4, 247, 211, 6, 4, 247, 212, 6, 4, 250, 34, 6, 4, 247, 210, 6, 4, + 253, 27, 6, 4, 253, 28, 6, 4, 253, 135, 6, 4, 253, 26, 6, 4, 248, 179, 6, + 4, 248, 180, 6, 4, 253, 25, 6, 4, 248, 178, 6, 4, 248, 182, 6, 4, 253, + 33, 6, 4, 253, 31, 6, 4, 253, 32, 6, 4, 254, 77, 6, 4, 253, 29, 6, 4, + 253, 39, 6, 4, 253, 40, 6, 4, 254, 207, 6, 4, 253, 37, 6, 4, 248, 185, 6, + 4, 253, 38, 6, 4, 250, 116, 6, 4, 253, 45, 6, 4, 253, 139, 6, 4, 253, 44, + 6, 4, 248, 192, 6, 4, 248, 193, 6, 4, 253, 42, 6, 4, 248, 191, 6, 4, 248, + 204, 6, 4, 248, 205, 6, 4, 253, 49, 6, 4, 248, 203, 6, 4, 250, 118, 6, 4, + 253, 47, 6, 4, 253, 123, 6, 4, 253, 46, 6, 4, 253, 52, 6, 4, 253, 53, 6, + 4, 253, 124, 6, 4, 253, 50, 6, 4, 248, 206, 6, 4, 253, 51, 6, 4, 253, 56, + 6, 4, 253, 57, 6, 4, 254, 26, 6, 4, 253, 54, 6, 4, 248, 207, 6, 4, 253, + 55, 6, 22, 250, 57, 6, 22, 253, 211, 6, 22, 250, 1, 6, 22, 245, 153, 6, + 22, 249, 206, 6, 22, 250, 75, 6, 22, 245, 93, 6, 22, 249, 230, 6, 22, + 253, 156, 6, 22, 245, 110, 6, 22, 250, 16, 6, 22, 245, 58, 6, 22, 250, + 21, 6, 22, 253, 123, 6, 22, 250, 48, 6, 22, 245, 112, 6, 22, 250, 85, 6, + 22, 253, 100, 6, 22, 250, 130, 6, 22, 249, 207, 6, 22, 245, 77, 6, 22, + 249, 193, 6, 22, 245, 94, 6, 22, 245, 154, 6, 22, 253, 160, 6, 22, 254, + 63, 6, 22, 245, 119, 6, 22, 250, 129, 6, 22, 250, 52, 6, 22, 249, 243, 6, + 22, 250, 116, 6, 22, 250, 108, 6, 22, 250, 71, 6, 22, 250, 103, 6, 22, + 253, 129, 6, 22, 253, 240, 6, 22, 245, 155, 6, 22, 249, 251, 6, 22, 249, + 238, 6, 22, 245, 118, 6, 22, 253, 138, 6, 22, 253, 204, 6, 22, 245, 188, + 6, 22, 250, 11, 6, 22, 254, 33, 6, 22, 245, 71, 6, 22, 249, 198, 6, 22, + 245, 111, 6, 22, 245, 181, 6, 22, 250, 131, 6, 22, 245, 187, 6, 22, 249, + 44, 6, 22, 243, 21, 6, 22, 245, 148, 6, 22, 253, 134, 7, 11, 234, 73, 7, + 11, 234, 74, 7, 11, 234, 75, 7, 11, 234, 76, 7, 11, 234, 77, 7, 11, 234, + 78, 7, 11, 234, 79, 7, 11, 234, 80, 7, 11, 234, 81, 7, 11, 234, 82, 7, + 11, 234, 83, 7, 11, 234, 84, 7, 11, 234, 85, 7, 11, 234, 86, 7, 11, 234, + 87, 7, 11, 234, 88, 7, 11, 234, 89, 7, 11, 234, 90, 7, 11, 234, 91, 7, + 11, 234, 92, 7, 11, 234, 93, 7, 11, 234, 94, 7, 11, 234, 95, 7, 11, 234, + 96, 7, 11, 234, 97, 7, 11, 234, 98, 7, 11, 234, 99, 7, 11, 234, 100, 7, + 11, 234, 101, 7, 11, 234, 102, 7, 11, 234, 103, 7, 11, 234, 104, 7, 11, + 234, 105, 7, 11, 234, 106, 7, 11, 234, 107, 7, 11, 234, 108, 7, 11, 234, + 109, 7, 11, 234, 110, 7, 11, 234, 111, 7, 11, 234, 112, 7, 11, 234, 113, + 7, 11, 234, 114, 7, 11, 234, 115, 7, 11, 234, 116, 7, 11, 234, 117, 7, + 11, 234, 118, 7, 11, 234, 119, 7, 11, 234, 120, 7, 11, 234, 121, 7, 11, + 234, 122, 7, 11, 234, 123, 7, 11, 234, 124, 7, 11, 234, 125, 7, 11, 234, + 126, 7, 11, 234, 127, 7, 11, 234, 128, 7, 11, 234, 129, 7, 11, 234, 130, + 7, 11, 234, 131, 7, 11, 234, 132, 7, 11, 234, 133, 7, 11, 234, 134, 7, + 11, 234, 135, 7, 11, 234, 136, 7, 11, 234, 137, 7, 11, 234, 138, 7, 11, + 234, 139, 7, 11, 234, 140, 7, 11, 234, 141, 7, 11, 234, 142, 7, 11, 234, + 143, 7, 11, 234, 144, 7, 11, 234, 145, 7, 11, 234, 146, 7, 11, 234, 147, + 7, 11, 234, 148, 7, 11, 234, 149, 7, 11, 234, 150, 7, 11, 234, 151, 7, + 11, 234, 152, 7, 11, 234, 153, 7, 11, 234, 154, 7, 11, 234, 155, 7, 11, + 234, 156, 7, 11, 234, 157, 7, 11, 234, 158, 7, 11, 234, 159, 7, 11, 234, + 160, 7, 11, 234, 161, 7, 11, 234, 162, 7, 11, 234, 163, 7, 11, 234, 164, + 7, 11, 234, 165, 7, 11, 234, 166, 7, 11, 234, 167, 7, 11, 234, 168, 7, + 11, 234, 169, 7, 11, 234, 170, 7, 11, 234, 171, 7, 11, 234, 172, 7, 11, + 234, 173, 7, 11, 234, 174, 7, 11, 234, 175, 7, 11, 234, 176, 7, 11, 234, + 177, 7, 11, 234, 178, 7, 11, 234, 179, 7, 11, 234, 180, 7, 11, 234, 181, + 7, 11, 234, 182, 7, 11, 234, 183, 7, 11, 234, 184, 7, 11, 234, 185, 7, + 11, 234, 186, 7, 11, 234, 187, 7, 11, 234, 188, 7, 11, 234, 189, 7, 11, + 234, 190, 7, 11, 234, 191, 7, 11, 234, 192, 7, 11, 234, 193, 7, 11, 234, + 194, 7, 11, 234, 195, 7, 11, 234, 196, 7, 11, 234, 197, 7, 11, 234, 198, + 7, 11, 234, 199, 7, 11, 234, 200, 7, 11, 234, 201, 7, 11, 234, 202, 7, + 11, 234, 203, 7, 11, 234, 204, 7, 11, 234, 205, 7, 11, 234, 206, 7, 11, + 234, 207, 7, 11, 234, 208, 7, 11, 234, 209, 7, 11, 234, 210, 7, 11, 234, + 211, 7, 11, 234, 212, 7, 11, 234, 213, 7, 11, 234, 214, 7, 11, 234, 215, + 7, 11, 234, 216, 7, 11, 234, 217, 7, 11, 234, 218, 7, 11, 234, 219, 7, + 11, 234, 220, 7, 11, 234, 221, 7, 11, 234, 222, 7, 11, 234, 223, 7, 11, + 234, 224, 7, 11, 234, 225, 7, 11, 234, 226, 7, 11, 234, 227, 7, 11, 234, + 228, 7, 11, 234, 229, 7, 11, 234, 230, 7, 11, 234, 231, 7, 11, 234, 232, + 7, 11, 234, 233, 7, 11, 234, 234, 7, 11, 234, 235, 7, 11, 234, 236, 7, + 11, 234, 237, 7, 11, 234, 238, 7, 11, 234, 239, 7, 11, 234, 240, 7, 11, + 234, 241, 7, 11, 234, 242, 7, 11, 234, 243, 7, 11, 234, 244, 7, 11, 234, + 245, 7, 11, 234, 246, 7, 11, 234, 247, 7, 11, 234, 248, 7, 11, 234, 249, + 7, 11, 234, 250, 7, 11, 234, 251, 7, 11, 234, 252, 7, 11, 234, 253, 7, + 11, 234, 254, 7, 11, 234, 255, 7, 11, 235, 0, 7, 11, 235, 1, 7, 11, 235, + 2, 7, 11, 235, 3, 7, 11, 235, 4, 7, 11, 235, 5, 7, 11, 235, 6, 7, 11, + 235, 7, 7, 11, 235, 8, 7, 11, 235, 9, 7, 11, 235, 10, 7, 11, 235, 11, 7, + 11, 235, 12, 7, 11, 235, 13, 7, 11, 235, 14, 7, 11, 235, 15, 7, 11, 235, + 16, 7, 11, 235, 17, 7, 11, 235, 18, 7, 11, 235, 19, 7, 11, 235, 20, 7, + 11, 235, 21, 7, 11, 235, 22, 7, 11, 235, 23, 7, 11, 235, 24, 7, 11, 235, + 25, 7, 11, 235, 26, 7, 11, 235, 27, 7, 11, 235, 28, 7, 11, 235, 29, 7, + 11, 235, 30, 7, 11, 235, 31, 7, 11, 235, 32, 7, 11, 235, 33, 7, 11, 235, + 34, 7, 11, 235, 35, 7, 11, 235, 36, 7, 11, 235, 37, 7, 11, 235, 38, 7, + 11, 235, 39, 7, 11, 235, 40, 7, 11, 235, 41, 7, 11, 235, 42, 7, 11, 235, + 43, 7, 11, 235, 44, 7, 11, 235, 45, 7, 11, 235, 46, 7, 11, 235, 47, 7, + 11, 235, 48, 7, 11, 235, 49, 7, 11, 235, 50, 7, 11, 235, 51, 7, 11, 235, + 52, 7, 11, 235, 53, 7, 11, 235, 54, 7, 11, 235, 55, 7, 11, 235, 56, 7, + 11, 235, 57, 7, 11, 235, 58, 7, 11, 235, 59, 7, 11, 235, 60, 7, 11, 235, + 61, 7, 11, 235, 62, 7, 11, 235, 63, 7, 11, 235, 64, 7, 11, 235, 65, 7, + 11, 235, 66, 7, 11, 235, 67, 7, 11, 235, 68, 7, 11, 235, 69, 7, 11, 235, + 70, 7, 11, 235, 71, 7, 11, 235, 72, 7, 11, 235, 73, 7, 11, 235, 74, 7, + 11, 235, 75, 7, 11, 235, 76, 7, 11, 235, 77, 7, 11, 235, 78, 7, 11, 235, + 79, 7, 11, 235, 80, 7, 11, 235, 81, 7, 11, 235, 82, 7, 11, 235, 83, 7, + 11, 235, 84, 7, 11, 235, 85, 7, 11, 235, 86, 7, 11, 235, 87, 7, 11, 235, + 88, 7, 11, 235, 89, 7, 11, 235, 90, 7, 11, 235, 91, 7, 11, 235, 92, 7, + 11, 235, 93, 7, 11, 235, 94, 7, 11, 235, 95, 7, 11, 235, 96, 7, 11, 235, + 97, 7, 11, 235, 98, 7, 11, 235, 99, 7, 11, 235, 100, 7, 11, 235, 101, 7, + 11, 235, 102, 7, 11, 235, 103, 7, 11, 235, 104, 7, 11, 235, 105, 7, 11, + 235, 106, 7, 11, 235, 107, 7, 11, 235, 108, 7, 11, 235, 109, 7, 11, 235, + 110, 7, 11, 235, 111, 7, 11, 235, 112, 7, 11, 235, 113, 7, 11, 235, 114, + 7, 11, 235, 115, 7, 11, 235, 116, 7, 11, 235, 117, 7, 11, 235, 118, 7, + 11, 235, 119, 7, 11, 235, 120, 7, 11, 235, 121, 7, 11, 235, 122, 7, 11, + 235, 123, 7, 11, 235, 124, 7, 11, 235, 125, 7, 11, 235, 126, 7, 11, 235, + 127, 7, 11, 235, 128, 7, 11, 235, 129, 7, 11, 235, 130, 7, 11, 235, 131, + 7, 11, 235, 132, 7, 11, 235, 133, 7, 11, 235, 134, 7, 11, 235, 135, 7, + 11, 235, 136, 7, 11, 235, 137, 7, 11, 235, 138, 7, 11, 235, 139, 7, 11, + 235, 140, 7, 11, 235, 141, 7, 11, 235, 142, 7, 11, 235, 143, 7, 11, 235, + 144, 7, 11, 235, 145, 7, 11, 235, 146, 7, 11, 235, 147, 7, 11, 235, 148, + 7, 11, 235, 149, 7, 11, 235, 150, 7, 11, 235, 151, 7, 11, 235, 152, 7, + 11, 235, 153, 7, 11, 235, 154, 7, 11, 235, 155, 7, 11, 235, 156, 7, 11, + 235, 157, 7, 11, 235, 158, 7, 11, 235, 159, 7, 11, 235, 160, 7, 11, 235, + 161, 7, 11, 235, 162, 7, 11, 235, 163, 7, 11, 235, 164, 7, 11, 235, 165, + 7, 11, 235, 166, 7, 11, 235, 167, 7, 11, 235, 168, 7, 11, 235, 169, 7, + 11, 235, 170, 7, 11, 235, 171, 7, 11, 235, 172, 7, 11, 235, 173, 7, 11, + 235, 174, 7, 11, 235, 175, 7, 11, 235, 176, 7, 11, 235, 177, 9, 3, 17, + 254, 102, 9, 3, 17, 253, 226, 9, 3, 17, 254, 103, 9, 3, 17, 251, 4, 9, 3, + 17, 251, 5, 9, 3, 17, 165, 255, 29, 190, 9, 3, 17, 254, 186, 87, 3, 17, + 253, 242, 249, 112, 87, 3, 17, 253, 242, 249, 133, 87, 3, 17, 253, 242, + 249, 138, 87, 3, 17, 254, 200, 249, 112, 87, 3, 17, 253, 242, 249, 179, + 61, 1, 254, 0, 2, 242, 222, 61, 244, 180, 236, 25, 242, 50, 61, 17, 241, + 10, 254, 0, 254, 0, 242, 162, 61, 1, 236, 200, 245, 57, 61, 1, 249, 51, + 244, 224, 61, 1, 249, 51, 242, 204, 61, 1, 249, 51, 253, 125, 61, 1, 249, + 51, 249, 81, 61, 1, 249, 51, 242, 179, 61, 1, 249, 51, 29, 249, 147, 61, + 1, 249, 51, 245, 170, 61, 1, 249, 51, 250, 86, 61, 1, 236, 200, 249, 4, + 57, 61, 1, 249, 54, 2, 249, 54, 227, 61, 1, 249, 54, 2, 254, 22, 227, 61, + 1, 249, 54, 2, 242, 176, 18, 249, 54, 227, 61, 1, 249, 54, 2, 242, 176, + 18, 254, 22, 227, 61, 1, 67, 2, 242, 162, 61, 1, 67, 2, 241, 30, 61, 1, + 67, 2, 242, 181, 61, 1, 254, 3, 2, 240, 208, 61, 1, 245, 98, 2, 240, 208, + 61, 1, 245, 76, 2, 240, 208, 61, 1, 255, 9, 2, 242, 181, 61, 1, 254, 24, + 2, 240, 208, 61, 1, 248, 208, 2, 240, 208, 61, 1, 254, 191, 2, 240, 208, + 61, 1, 254, 0, 2, 240, 208, 61, 1, 29, 253, 87, 2, 240, 208, 61, 1, 253, + 87, 2, 240, 208, 61, 1, 247, 54, 2, 240, 208, 61, 1, 254, 142, 2, 240, + 208, 61, 1, 254, 160, 2, 240, 208, 61, 1, 244, 69, 2, 240, 208, 61, 1, + 29, 254, 229, 2, 240, 208, 61, 1, 254, 229, 2, 240, 208, 61, 1, 248, 141, + 2, 240, 208, 61, 1, 254, 177, 2, 240, 208, 61, 1, 252, 122, 2, 240, 208, + 61, 1, 249, 54, 2, 240, 208, 61, 1, 254, 189, 2, 240, 208, 61, 1, 254, + 24, 2, 242, 223, 61, 1, 254, 3, 2, 245, 1, 61, 1, 253, 87, 2, 245, 1, 61, + 1, 254, 229, 2, 245, 1, 61, 17, 67, 242, 179, 10, 1, 67, 245, 219, 35, + 12, 10, 1, 67, 245, 219, 29, 12, 10, 1, 249, 93, 35, 12, 10, 1, 249, 93, + 29, 12, 10, 1, 249, 93, 46, 12, 10, 1, 249, 93, 98, 12, 10, 1, 253, 248, + 35, 12, 10, 1, 253, 248, 29, 12, 10, 1, 253, 248, 46, 12, 10, 1, 253, + 248, 98, 12, 10, 1, 244, 245, 35, 12, 10, 1, 244, 245, 29, 12, 10, 1, + 244, 245, 46, 12, 10, 1, 244, 245, 98, 12, 10, 1, 242, 168, 35, 12, 10, + 1, 242, 168, 29, 12, 10, 1, 242, 168, 46, 12, 10, 1, 242, 168, 98, 12, + 10, 1, 245, 7, 35, 12, 10, 1, 245, 7, 29, 12, 10, 1, 245, 7, 46, 12, 10, + 1, 245, 7, 98, 12, 10, 1, 249, 122, 35, 12, 10, 1, 249, 122, 29, 12, 10, + 1, 249, 122, 46, 12, 10, 1, 249, 122, 98, 12, 10, 1, 253, 254, 35, 12, + 10, 1, 253, 254, 29, 12, 10, 1, 253, 254, 46, 12, 10, 1, 253, 254, 98, + 12, 10, 1, 245, 0, 35, 12, 10, 1, 245, 0, 29, 12, 10, 1, 245, 0, 46, 12, + 10, 1, 245, 0, 98, 12, 10, 1, 249, 99, 35, 12, 10, 1, 249, 99, 29, 12, + 10, 1, 249, 99, 46, 12, 10, 1, 249, 99, 98, 12, 10, 1, 249, 113, 35, 12, + 10, 1, 249, 113, 29, 12, 10, 1, 249, 113, 46, 12, 10, 1, 249, 113, 98, + 12, 10, 1, 244, 240, 35, 12, 10, 1, 244, 240, 29, 12, 10, 1, 244, 240, + 46, 12, 10, 1, 244, 240, 98, 12, 10, 1, 241, 6, 35, 12, 10, 1, 241, 6, + 29, 12, 10, 1, 241, 6, 46, 12, 10, 1, 241, 6, 98, 12, 10, 1, 242, 206, + 35, 12, 10, 1, 242, 206, 29, 12, 10, 1, 245, 75, 35, 12, 10, 1, 245, 75, + 29, 12, 10, 1, 254, 34, 35, 12, 10, 1, 254, 34, 29, 12, 10, 1, 249, 208, + 35, 12, 10, 1, 249, 208, 29, 12, 10, 1, 254, 47, 35, 12, 10, 1, 254, 47, + 29, 12, 10, 1, 250, 65, 35, 12, 10, 1, 250, 65, 29, 12, 10, 1, 244, 253, + 35, 12, 10, 1, 244, 253, 29, 12, 10, 1, 244, 253, 46, 12, 10, 1, 244, + 253, 98, 12, 10, 1, 253, 194, 35, 12, 10, 1, 253, 194, 29, 12, 10, 1, + 253, 194, 46, 12, 10, 1, 253, 194, 98, 12, 10, 1, 249, 103, 35, 12, 10, + 1, 249, 103, 29, 12, 10, 1, 249, 103, 46, 12, 10, 1, 249, 103, 98, 12, + 10, 1, 244, 255, 35, 12, 10, 1, 244, 255, 29, 12, 10, 1, 244, 255, 46, + 12, 10, 1, 244, 255, 98, 12, 10, 1, 177, 242, 218, 35, 12, 10, 1, 177, + 242, 218, 29, 12, 10, 1, 245, 2, 35, 12, 10, 1, 245, 2, 29, 12, 10, 1, + 245, 2, 46, 12, 10, 1, 245, 2, 98, 12, 10, 1, 188, 2, 50, 53, 35, 12, 10, + 1, 188, 2, 50, 53, 29, 12, 10, 1, 188, 249, 79, 35, 12, 10, 1, 188, 249, + 79, 29, 12, 10, 1, 188, 249, 79, 46, 12, 10, 1, 188, 249, 79, 98, 12, 10, + 1, 188, 236, 197, 35, 12, 10, 1, 188, 236, 197, 29, 12, 10, 1, 188, 236, + 197, 46, 12, 10, 1, 188, 236, 197, 98, 12, 10, 1, 50, 242, 139, 35, 12, + 10, 1, 50, 242, 139, 29, 12, 10, 1, 50, 242, 139, 2, 125, 53, 35, 12, 10, + 1, 50, 242, 139, 2, 125, 53, 29, 12, 10, 1, 254, 235, 35, 12, 10, 1, 254, + 235, 29, 12, 10, 1, 254, 235, 46, 12, 10, 1, 254, 235, 98, 12, 10, 1, + 115, 35, 12, 10, 1, 115, 29, 12, 10, 1, 254, 223, 35, 12, 10, 1, 254, + 223, 29, 12, 10, 1, 254, 226, 35, 12, 10, 1, 254, 226, 29, 12, 10, 1, + 115, 2, 125, 53, 35, 12, 10, 1, 254, 255, 35, 12, 10, 1, 254, 255, 29, + 12, 10, 1, 240, 217, 254, 223, 35, 12, 10, 1, 240, 217, 254, 223, 29, 12, + 10, 1, 240, 217, 254, 226, 35, 12, 10, 1, 240, 217, 254, 226, 29, 12, 10, + 1, 144, 35, 12, 10, 1, 144, 29, 12, 10, 1, 144, 46, 12, 10, 1, 144, 98, + 12, 10, 1, 242, 151, 242, 226, 240, 217, 67, 131, 46, 12, 10, 1, 242, + 151, 242, 226, 240, 217, 67, 131, 98, 12, 10, 17, 50, 2, 125, 53, 2, 67, + 35, 12, 10, 17, 50, 2, 125, 53, 2, 67, 29, 12, 10, 17, 50, 2, 125, 53, 2, + 254, 221, 35, 12, 10, 17, 50, 2, 125, 53, 2, 254, 221, 29, 12, 10, 17, + 50, 2, 125, 53, 2, 253, 162, 35, 12, 10, 17, 50, 2, 125, 53, 2, 253, 162, + 29, 12, 10, 17, 50, 2, 125, 53, 2, 115, 35, 12, 10, 17, 50, 2, 125, 53, + 2, 115, 29, 12, 10, 17, 50, 2, 125, 53, 2, 254, 223, 35, 12, 10, 17, 50, + 2, 125, 53, 2, 254, 223, 29, 12, 10, 17, 50, 2, 125, 53, 2, 254, 226, 35, + 12, 10, 17, 50, 2, 125, 53, 2, 254, 226, 29, 12, 10, 17, 50, 2, 125, 53, + 2, 144, 35, 12, 10, 17, 50, 2, 125, 53, 2, 144, 29, 12, 10, 17, 50, 2, + 125, 53, 2, 144, 46, 12, 10, 17, 242, 151, 240, 217, 50, 2, 125, 53, 2, + 67, 131, 35, 12, 10, 17, 242, 151, 240, 217, 50, 2, 125, 53, 2, 67, 131, + 29, 12, 10, 17, 242, 151, 240, 217, 50, 2, 125, 53, 2, 67, 131, 46, 12, + 10, 1, 244, 227, 50, 35, 12, 10, 1, 244, 227, 50, 29, 12, 10, 1, 244, + 227, 50, 46, 12, 10, 1, 244, 227, 50, 98, 12, 10, 17, 50, 2, 125, 53, 2, + 89, 35, 12, 10, 17, 50, 2, 125, 53, 2, 81, 35, 12, 10, 17, 50, 2, 125, + 53, 2, 43, 35, 12, 10, 17, 50, 2, 125, 53, 2, 67, 131, 35, 12, 10, 17, + 50, 2, 125, 53, 2, 50, 35, 12, 10, 17, 253, 102, 2, 89, 35, 12, 10, 17, + 253, 102, 2, 81, 35, 12, 10, 17, 253, 102, 2, 145, 35, 12, 10, 17, 253, + 102, 2, 43, 35, 12, 10, 17, 253, 102, 2, 67, 131, 35, 12, 10, 17, 253, + 102, 2, 50, 35, 12, 10, 17, 191, 2, 89, 35, 12, 10, 17, 191, 2, 81, 35, + 12, 10, 17, 191, 2, 145, 35, 12, 10, 17, 191, 2, 43, 35, 12, 10, 17, 191, + 2, 67, 131, 35, 12, 10, 17, 191, 2, 50, 35, 12, 10, 17, 249, 20, 2, 89, + 35, 12, 10, 17, 249, 20, 2, 43, 35, 12, 10, 17, 249, 20, 2, 67, 131, 35, + 12, 10, 17, 249, 20, 2, 50, 35, 12, 10, 17, 89, 2, 81, 35, 12, 10, 17, + 89, 2, 43, 35, 12, 10, 17, 81, 2, 89, 35, 12, 10, 17, 81, 2, 43, 35, 12, + 10, 17, 145, 2, 89, 35, 12, 10, 17, 145, 2, 81, 35, 12, 10, 17, 145, 2, + 43, 35, 12, 10, 17, 214, 2, 89, 35, 12, 10, 17, 214, 2, 81, 35, 12, 10, + 17, 214, 2, 145, 35, 12, 10, 17, 214, 2, 43, 35, 12, 10, 17, 253, 108, 2, + 81, 35, 12, 10, 17, 253, 108, 2, 43, 35, 12, 10, 17, 253, 107, 2, 89, 35, + 12, 10, 17, 253, 107, 2, 81, 35, 12, 10, 17, 253, 107, 2, 145, 35, 12, + 10, 17, 253, 107, 2, 43, 35, 12, 10, 17, 253, 122, 2, 81, 35, 12, 10, 17, + 253, 122, 2, 43, 35, 12, 10, 17, 253, 201, 2, 43, 35, 12, 10, 17, 253, + 117, 2, 89, 35, 12, 10, 17, 253, 117, 2, 43, 35, 12, 10, 17, 244, 190, 2, + 89, 35, 12, 10, 17, 244, 190, 2, 43, 35, 12, 10, 17, 253, 111, 2, 89, 35, + 12, 10, 17, 253, 111, 2, 81, 35, 12, 10, 17, 253, 111, 2, 145, 35, 12, + 10, 17, 253, 111, 2, 43, 35, 12, 10, 17, 253, 111, 2, 67, 131, 35, 12, + 10, 17, 253, 111, 2, 50, 35, 12, 10, 17, 253, 120, 2, 81, 35, 12, 10, 17, + 253, 120, 2, 43, 35, 12, 10, 17, 253, 120, 2, 67, 131, 35, 12, 10, 17, + 253, 120, 2, 50, 35, 12, 10, 17, 253, 87, 2, 67, 35, 12, 10, 17, 253, 87, + 2, 89, 35, 12, 10, 17, 253, 87, 2, 81, 35, 12, 10, 17, 253, 87, 2, 145, + 35, 12, 10, 17, 253, 87, 2, 155, 35, 12, 10, 17, 253, 87, 2, 43, 35, 12, + 10, 17, 253, 87, 2, 67, 131, 35, 12, 10, 17, 253, 87, 2, 50, 35, 12, 10, + 17, 155, 2, 89, 35, 12, 10, 17, 155, 2, 81, 35, 12, 10, 17, 155, 2, 145, + 35, 12, 10, 17, 155, 2, 43, 35, 12, 10, 17, 155, 2, 67, 131, 35, 12, 10, + 17, 155, 2, 50, 35, 12, 10, 17, 43, 2, 89, 35, 12, 10, 17, 43, 2, 81, 35, + 12, 10, 17, 43, 2, 145, 35, 12, 10, 17, 43, 2, 43, 35, 12, 10, 17, 43, 2, + 67, 131, 35, 12, 10, 17, 43, 2, 50, 35, 12, 10, 17, 177, 2, 89, 35, 12, + 10, 17, 177, 2, 81, 35, 12, 10, 17, 177, 2, 145, 35, 12, 10, 17, 177, 2, + 43, 35, 12, 10, 17, 177, 2, 67, 131, 35, 12, 10, 17, 177, 2, 50, 35, 12, + 10, 17, 188, 2, 89, 35, 12, 10, 17, 188, 2, 43, 35, 12, 10, 17, 188, 2, + 67, 131, 35, 12, 10, 17, 188, 2, 50, 35, 12, 10, 17, 50, 2, 89, 35, 12, + 10, 17, 50, 2, 81, 35, 12, 10, 17, 50, 2, 145, 35, 12, 10, 17, 50, 2, 43, + 35, 12, 10, 17, 50, 2, 67, 131, 35, 12, 10, 17, 50, 2, 50, 35, 12, 10, + 17, 249, 165, 2, 236, 181, 67, 35, 12, 10, 17, 253, 105, 2, 236, 181, 67, + 35, 12, 10, 17, 67, 131, 2, 236, 181, 67, 35, 12, 10, 17, 242, 102, 2, + 239, 211, 35, 12, 10, 17, 242, 102, 2, 239, 220, 35, 12, 10, 17, 242, + 102, 2, 244, 249, 35, 12, 10, 17, 242, 102, 2, 244, 248, 35, 12, 10, 17, + 242, 102, 2, 244, 252, 35, 12, 10, 17, 242, 102, 2, 236, 181, 67, 35, 12, + 10, 17, 50, 2, 125, 53, 2, 253, 105, 29, 12, 10, 17, 50, 2, 125, 53, 2, + 249, 59, 29, 12, 10, 17, 50, 2, 125, 53, 2, 43, 29, 12, 10, 17, 50, 2, + 125, 53, 2, 177, 29, 12, 10, 17, 50, 2, 125, 53, 2, 67, 131, 29, 12, 10, + 17, 50, 2, 125, 53, 2, 50, 29, 12, 10, 17, 253, 102, 2, 253, 105, 29, 12, + 10, 17, 253, 102, 2, 249, 59, 29, 12, 10, 17, 253, 102, 2, 43, 29, 12, + 10, 17, 253, 102, 2, 177, 29, 12, 10, 17, 253, 102, 2, 67, 131, 29, 12, + 10, 17, 253, 102, 2, 50, 29, 12, 10, 17, 191, 2, 253, 105, 29, 12, 10, + 17, 191, 2, 249, 59, 29, 12, 10, 17, 191, 2, 43, 29, 12, 10, 17, 191, 2, + 177, 29, 12, 10, 17, 191, 2, 67, 131, 29, 12, 10, 17, 191, 2, 50, 29, 12, + 10, 17, 249, 20, 2, 253, 105, 29, 12, 10, 17, 249, 20, 2, 249, 59, 29, + 12, 10, 17, 249, 20, 2, 43, 29, 12, 10, 17, 249, 20, 2, 177, 29, 12, 10, + 17, 249, 20, 2, 67, 131, 29, 12, 10, 17, 249, 20, 2, 50, 29, 12, 10, 17, + 253, 111, 2, 67, 131, 29, 12, 10, 17, 253, 111, 2, 50, 29, 12, 10, 17, + 253, 120, 2, 67, 131, 29, 12, 10, 17, 253, 120, 2, 50, 29, 12, 10, 17, + 253, 87, 2, 67, 29, 12, 10, 17, 253, 87, 2, 155, 29, 12, 10, 17, 253, 87, + 2, 43, 29, 12, 10, 17, 253, 87, 2, 67, 131, 29, 12, 10, 17, 253, 87, 2, + 50, 29, 12, 10, 17, 155, 2, 43, 29, 12, 10, 17, 155, 2, 67, 131, 29, 12, + 10, 17, 155, 2, 50, 29, 12, 10, 17, 43, 2, 67, 29, 12, 10, 17, 43, 2, 43, + 29, 12, 10, 17, 177, 2, 253, 105, 29, 12, 10, 17, 177, 2, 249, 59, 29, + 12, 10, 17, 177, 2, 43, 29, 12, 10, 17, 177, 2, 177, 29, 12, 10, 17, 177, + 2, 67, 131, 29, 12, 10, 17, 177, 2, 50, 29, 12, 10, 17, 67, 131, 2, 236, + 181, 67, 29, 12, 10, 17, 50, 2, 253, 105, 29, 12, 10, 17, 50, 2, 249, 59, + 29, 12, 10, 17, 50, 2, 43, 29, 12, 10, 17, 50, 2, 177, 29, 12, 10, 17, + 50, 2, 67, 131, 29, 12, 10, 17, 50, 2, 50, 29, 12, 10, 17, 50, 2, 125, + 53, 2, 89, 46, 12, 10, 17, 50, 2, 125, 53, 2, 81, 46, 12, 10, 17, 50, 2, + 125, 53, 2, 145, 46, 12, 10, 17, 50, 2, 125, 53, 2, 43, 46, 12, 10, 17, + 50, 2, 125, 53, 2, 188, 46, 12, 10, 17, 253, 102, 2, 89, 46, 12, 10, 17, + 253, 102, 2, 81, 46, 12, 10, 17, 253, 102, 2, 145, 46, 12, 10, 17, 253, + 102, 2, 43, 46, 12, 10, 17, 253, 102, 2, 188, 46, 12, 10, 17, 191, 2, 89, + 46, 12, 10, 17, 191, 2, 81, 46, 12, 10, 17, 191, 2, 145, 46, 12, 10, 17, + 191, 2, 43, 46, 12, 10, 17, 191, 2, 188, 46, 12, 10, 17, 249, 20, 2, 43, + 46, 12, 10, 17, 89, 2, 81, 46, 12, 10, 17, 89, 2, 43, 46, 12, 10, 17, 81, + 2, 89, 46, 12, 10, 17, 81, 2, 43, 46, 12, 10, 17, 145, 2, 89, 46, 12, 10, + 17, 145, 2, 43, 46, 12, 10, 17, 214, 2, 89, 46, 12, 10, 17, 214, 2, 81, + 46, 12, 10, 17, 214, 2, 145, 46, 12, 10, 17, 214, 2, 43, 46, 12, 10, 17, + 253, 108, 2, 81, 46, 12, 10, 17, 253, 108, 2, 145, 46, 12, 10, 17, 253, + 108, 2, 43, 46, 12, 10, 17, 253, 107, 2, 89, 46, 12, 10, 17, 253, 107, 2, + 81, 46, 12, 10, 17, 253, 107, 2, 145, 46, 12, 10, 17, 253, 107, 2, 43, + 46, 12, 10, 17, 253, 122, 2, 81, 46, 12, 10, 17, 253, 201, 2, 43, 46, 12, + 10, 17, 253, 117, 2, 89, 46, 12, 10, 17, 253, 117, 2, 43, 46, 12, 10, 17, + 244, 190, 2, 89, 46, 12, 10, 17, 244, 190, 2, 43, 46, 12, 10, 17, 253, + 111, 2, 89, 46, 12, 10, 17, 253, 111, 2, 81, 46, 12, 10, 17, 253, 111, 2, + 145, 46, 12, 10, 17, 253, 111, 2, 43, 46, 12, 10, 17, 253, 120, 2, 81, + 46, 12, 10, 17, 253, 120, 2, 43, 46, 12, 10, 17, 253, 87, 2, 89, 46, 12, + 10, 17, 253, 87, 2, 81, 46, 12, 10, 17, 253, 87, 2, 145, 46, 12, 10, 17, + 253, 87, 2, 155, 46, 12, 10, 17, 253, 87, 2, 43, 46, 12, 10, 17, 155, 2, + 89, 46, 12, 10, 17, 155, 2, 81, 46, 12, 10, 17, 155, 2, 145, 46, 12, 10, + 17, 155, 2, 43, 46, 12, 10, 17, 155, 2, 188, 46, 12, 10, 17, 43, 2, 89, + 46, 12, 10, 17, 43, 2, 81, 46, 12, 10, 17, 43, 2, 145, 46, 12, 10, 17, + 43, 2, 43, 46, 12, 10, 17, 177, 2, 89, 46, 12, 10, 17, 177, 2, 81, 46, + 12, 10, 17, 177, 2, 145, 46, 12, 10, 17, 177, 2, 43, 46, 12, 10, 17, 177, + 2, 188, 46, 12, 10, 17, 188, 2, 89, 46, 12, 10, 17, 188, 2, 43, 46, 12, + 10, 17, 188, 2, 236, 181, 67, 46, 12, 10, 17, 50, 2, 89, 46, 12, 10, 17, + 50, 2, 81, 46, 12, 10, 17, 50, 2, 145, 46, 12, 10, 17, 50, 2, 43, 46, 12, + 10, 17, 50, 2, 188, 46, 12, 10, 17, 50, 2, 125, 53, 2, 43, 98, 12, 10, + 17, 50, 2, 125, 53, 2, 188, 98, 12, 10, 17, 253, 102, 2, 43, 98, 12, 10, + 17, 253, 102, 2, 188, 98, 12, 10, 17, 191, 2, 43, 98, 12, 10, 17, 191, 2, + 188, 98, 12, 10, 17, 249, 20, 2, 43, 98, 12, 10, 17, 249, 20, 2, 188, 98, + 12, 10, 17, 214, 2, 43, 98, 12, 10, 17, 214, 2, 188, 98, 12, 10, 17, 215, + 2, 43, 98, 12, 10, 17, 215, 2, 188, 98, 12, 10, 17, 253, 87, 2, 155, 98, + 12, 10, 17, 253, 87, 2, 43, 98, 12, 10, 17, 155, 2, 43, 98, 12, 10, 17, + 177, 2, 43, 98, 12, 10, 17, 177, 2, 188, 98, 12, 10, 17, 50, 2, 43, 98, + 12, 10, 17, 50, 2, 188, 98, 12, 10, 17, 242, 102, 2, 244, 249, 98, 12, + 10, 17, 242, 102, 2, 244, 248, 98, 12, 10, 17, 242, 102, 2, 244, 252, 98, + 12, 10, 17, 253, 122, 2, 67, 131, 35, 12, 10, 17, 253, 122, 2, 50, 35, + 12, 10, 17, 253, 117, 2, 67, 131, 35, 12, 10, 17, 253, 117, 2, 50, 35, + 12, 10, 17, 244, 190, 2, 67, 131, 35, 12, 10, 17, 244, 190, 2, 50, 35, + 12, 10, 17, 214, 2, 67, 131, 35, 12, 10, 17, 214, 2, 50, 35, 12, 10, 17, + 215, 2, 67, 131, 35, 12, 10, 17, 215, 2, 50, 35, 12, 10, 17, 81, 2, 67, + 131, 35, 12, 10, 17, 81, 2, 50, 35, 12, 10, 17, 89, 2, 67, 131, 35, 12, + 10, 17, 89, 2, 50, 35, 12, 10, 17, 145, 2, 67, 131, 35, 12, 10, 17, 145, + 2, 50, 35, 12, 10, 17, 253, 108, 2, 67, 131, 35, 12, 10, 17, 253, 108, 2, + 50, 35, 12, 10, 17, 253, 107, 2, 67, 131, 35, 12, 10, 17, 253, 107, 2, + 50, 35, 12, 10, 17, 215, 2, 89, 35, 12, 10, 17, 215, 2, 81, 35, 12, 10, + 17, 215, 2, 145, 35, 12, 10, 17, 215, 2, 43, 35, 12, 10, 17, 215, 2, 253, + 105, 35, 12, 10, 17, 214, 2, 253, 105, 35, 12, 10, 17, 253, 108, 2, 253, + 105, 35, 12, 10, 17, 253, 107, 2, 253, 105, 35, 12, 10, 17, 253, 122, 2, + 67, 131, 29, 12, 10, 17, 253, 122, 2, 50, 29, 12, 10, 17, 253, 117, 2, + 67, 131, 29, 12, 10, 17, 253, 117, 2, 50, 29, 12, 10, 17, 244, 190, 2, + 67, 131, 29, 12, 10, 17, 244, 190, 2, 50, 29, 12, 10, 17, 214, 2, 67, + 131, 29, 12, 10, 17, 214, 2, 50, 29, 12, 10, 17, 215, 2, 67, 131, 29, 12, + 10, 17, 215, 2, 50, 29, 12, 10, 17, 81, 2, 67, 131, 29, 12, 10, 17, 81, + 2, 50, 29, 12, 10, 17, 89, 2, 67, 131, 29, 12, 10, 17, 89, 2, 50, 29, 12, + 10, 17, 145, 2, 67, 131, 29, 12, 10, 17, 145, 2, 50, 29, 12, 10, 17, 253, + 108, 2, 67, 131, 29, 12, 10, 17, 253, 108, 2, 50, 29, 12, 10, 17, 253, + 107, 2, 67, 131, 29, 12, 10, 17, 253, 107, 2, 50, 29, 12, 10, 17, 215, 2, + 89, 29, 12, 10, 17, 215, 2, 81, 29, 12, 10, 17, 215, 2, 145, 29, 12, 10, + 17, 215, 2, 43, 29, 12, 10, 17, 215, 2, 253, 105, 29, 12, 10, 17, 214, 2, + 253, 105, 29, 12, 10, 17, 253, 108, 2, 253, 105, 29, 12, 10, 17, 253, + 107, 2, 253, 105, 29, 12, 10, 17, 215, 2, 89, 46, 12, 10, 17, 215, 2, 81, + 46, 12, 10, 17, 215, 2, 145, 46, 12, 10, 17, 215, 2, 43, 46, 12, 10, 17, + 214, 2, 188, 46, 12, 10, 17, 215, 2, 188, 46, 12, 10, 17, 253, 122, 2, + 43, 46, 12, 10, 17, 214, 2, 89, 98, 12, 10, 17, 214, 2, 81, 98, 12, 10, + 17, 214, 2, 145, 98, 12, 10, 17, 215, 2, 89, 98, 12, 10, 17, 215, 2, 81, + 98, 12, 10, 17, 215, 2, 145, 98, 12, 10, 17, 253, 122, 2, 43, 98, 12, 10, + 17, 253, 201, 2, 43, 98, 12, 10, 17, 67, 2, 239, 172, 29, 12, 10, 17, 67, + 2, 239, 172, 35, 12, 242, 236, 36, 236, 47, 242, 236, 37, 236, 47, 10, + 17, 191, 2, 89, 2, 43, 46, 12, 10, 17, 191, 2, 81, 2, 89, 29, 12, 10, 17, + 191, 2, 81, 2, 89, 46, 12, 10, 17, 191, 2, 81, 2, 43, 46, 12, 10, 17, + 191, 2, 145, 2, 43, 46, 12, 10, 17, 191, 2, 43, 2, 89, 46, 12, 10, 17, + 191, 2, 43, 2, 81, 46, 12, 10, 17, 191, 2, 43, 2, 145, 46, 12, 10, 17, + 89, 2, 43, 2, 81, 29, 12, 10, 17, 89, 2, 43, 2, 81, 46, 12, 10, 17, 81, + 2, 43, 2, 50, 29, 12, 10, 17, 81, 2, 43, 2, 67, 131, 29, 12, 10, 17, 214, + 2, 81, 2, 89, 46, 12, 10, 17, 214, 2, 89, 2, 81, 46, 12, 10, 17, 214, 2, + 89, 2, 67, 131, 29, 12, 10, 17, 214, 2, 43, 2, 81, 29, 12, 10, 17, 214, + 2, 43, 2, 81, 46, 12, 10, 17, 214, 2, 43, 2, 89, 46, 12, 10, 17, 214, 2, + 43, 2, 43, 29, 12, 10, 17, 214, 2, 43, 2, 43, 46, 12, 10, 17, 253, 108, + 2, 81, 2, 81, 29, 12, 10, 17, 253, 108, 2, 81, 2, 81, 46, 12, 10, 17, + 253, 108, 2, 43, 2, 43, 29, 12, 10, 17, 215, 2, 81, 2, 43, 29, 12, 10, + 17, 215, 2, 81, 2, 43, 46, 12, 10, 17, 215, 2, 89, 2, 50, 29, 12, 10, 17, + 215, 2, 43, 2, 145, 29, 12, 10, 17, 215, 2, 43, 2, 145, 46, 12, 10, 17, + 215, 2, 43, 2, 43, 29, 12, 10, 17, 215, 2, 43, 2, 43, 46, 12, 10, 17, + 253, 107, 2, 81, 2, 67, 131, 29, 12, 10, 17, 253, 107, 2, 145, 2, 43, 29, + 12, 10, 17, 253, 107, 2, 145, 2, 43, 46, 12, 10, 17, 253, 122, 2, 43, 2, + 81, 29, 12, 10, 17, 253, 122, 2, 43, 2, 81, 46, 12, 10, 17, 253, 122, 2, + 43, 2, 43, 46, 12, 10, 17, 253, 122, 2, 43, 2, 50, 29, 12, 10, 17, 253, + 117, 2, 89, 2, 43, 29, 12, 10, 17, 253, 117, 2, 43, 2, 43, 29, 12, 10, + 17, 253, 117, 2, 43, 2, 43, 46, 12, 10, 17, 253, 117, 2, 43, 2, 67, 131, + 29, 12, 10, 17, 244, 190, 2, 43, 2, 43, 29, 12, 10, 17, 244, 190, 2, 43, + 2, 50, 29, 12, 10, 17, 244, 190, 2, 43, 2, 67, 131, 29, 12, 10, 17, 253, + 111, 2, 145, 2, 43, 29, 12, 10, 17, 253, 111, 2, 145, 2, 43, 46, 12, 10, + 17, 253, 120, 2, 43, 2, 81, 29, 12, 10, 17, 253, 120, 2, 43, 2, 43, 29, + 12, 10, 17, 155, 2, 81, 2, 43, 29, 12, 10, 17, 155, 2, 81, 2, 50, 29, 12, + 10, 17, 155, 2, 81, 2, 67, 131, 29, 12, 10, 17, 155, 2, 89, 2, 89, 46, + 12, 10, 17, 155, 2, 89, 2, 89, 29, 12, 10, 17, 155, 2, 145, 2, 43, 29, + 12, 10, 17, 155, 2, 145, 2, 43, 46, 12, 10, 17, 155, 2, 43, 2, 81, 29, + 12, 10, 17, 155, 2, 43, 2, 81, 46, 12, 10, 17, 43, 2, 81, 2, 89, 46, 12, + 10, 17, 43, 2, 81, 2, 43, 46, 12, 10, 17, 43, 2, 81, 2, 50, 29, 12, 10, + 17, 43, 2, 89, 2, 81, 46, 12, 10, 17, 43, 2, 89, 2, 43, 46, 12, 10, 17, + 43, 2, 145, 2, 89, 46, 12, 10, 17, 43, 2, 145, 2, 43, 46, 12, 10, 17, 43, + 2, 89, 2, 145, 46, 12, 10, 17, 188, 2, 43, 2, 89, 46, 12, 10, 17, 188, 2, + 43, 2, 43, 46, 12, 10, 17, 177, 2, 81, 2, 43, 46, 12, 10, 17, 177, 2, 81, + 2, 67, 131, 29, 12, 10, 17, 177, 2, 89, 2, 43, 29, 12, 10, 17, 177, 2, + 89, 2, 43, 46, 12, 10, 17, 177, 2, 89, 2, 67, 131, 29, 12, 10, 17, 177, + 2, 43, 2, 50, 29, 12, 10, 17, 177, 2, 43, 2, 67, 131, 29, 12, 10, 17, 50, + 2, 43, 2, 43, 29, 12, 10, 17, 50, 2, 43, 2, 43, 46, 12, 10, 17, 253, 102, + 2, 145, 2, 50, 29, 12, 10, 17, 191, 2, 89, 2, 50, 29, 12, 10, 17, 191, 2, + 89, 2, 67, 131, 29, 12, 10, 17, 191, 2, 145, 2, 50, 29, 12, 10, 17, 191, + 2, 145, 2, 67, 131, 29, 12, 10, 17, 191, 2, 43, 2, 50, 29, 12, 10, 17, + 191, 2, 43, 2, 67, 131, 29, 12, 10, 17, 89, 2, 43, 2, 50, 29, 12, 10, 17, + 89, 2, 81, 2, 67, 131, 29, 12, 10, 17, 89, 2, 43, 2, 67, 131, 29, 12, 10, + 17, 214, 2, 145, 2, 67, 131, 29, 12, 10, 17, 253, 108, 2, 81, 2, 50, 29, + 12, 10, 17, 215, 2, 81, 2, 50, 29, 12, 10, 17, 253, 107, 2, 81, 2, 50, + 29, 12, 10, 17, 155, 2, 89, 2, 50, 29, 12, 10, 17, 155, 2, 43, 2, 50, 29, + 12, 10, 17, 50, 2, 81, 2, 50, 29, 12, 10, 17, 50, 2, 89, 2, 50, 29, 12, + 10, 17, 50, 2, 43, 2, 50, 29, 12, 10, 17, 43, 2, 43, 2, 50, 29, 12, 10, + 17, 253, 120, 2, 43, 2, 50, 29, 12, 10, 17, 177, 2, 81, 2, 50, 29, 12, + 10, 17, 253, 120, 2, 43, 2, 81, 46, 12, 10, 17, 155, 2, 81, 2, 43, 46, + 12, 10, 17, 253, 117, 2, 43, 2, 50, 29, 12, 10, 17, 253, 87, 2, 43, 2, + 50, 29, 12, 10, 17, 177, 2, 89, 2, 81, 46, 12, 10, 17, 43, 2, 145, 2, 50, + 29, 12, 10, 17, 155, 2, 89, 2, 43, 46, 12, 10, 17, 253, 87, 2, 43, 2, 43, + 29, 12, 10, 17, 155, 2, 89, 2, 43, 29, 12, 10, 17, 177, 2, 89, 2, 81, 29, + 12, 10, 17, 89, 2, 81, 2, 50, 29, 12, 10, 17, 81, 2, 89, 2, 50, 29, 12, + 10, 17, 43, 2, 89, 2, 50, 29, 12, 10, 17, 253, 111, 2, 43, 2, 50, 29, 12, + 10, 17, 253, 102, 2, 81, 2, 50, 29, 12, 10, 17, 253, 87, 2, 43, 2, 43, + 46, 12, 10, 17, 253, 117, 2, 89, 2, 43, 46, 12, 10, 17, 253, 108, 2, 43, + 2, 43, 46, 12, 10, 17, 214, 2, 145, 2, 50, 29, 12, 10, 17, 177, 2, 89, 2, + 50, 29, 12, 10, 17, 245, 176, 250, 101, 254, 217, 241, 64, 249, 71, 25, + 35, 12, 10, 17, 252, 82, 250, 101, 254, 217, 241, 64, 249, 71, 25, 35, + 12, 10, 17, 245, 244, 35, 12, 10, 17, 245, 242, 35, 12, 10, 17, 240, 118, + 35, 12, 10, 17, 248, 52, 35, 12, 10, 17, 244, 52, 35, 12, 10, 17, 242, + 196, 35, 12, 10, 17, 240, 196, 35, 12, 10, 17, 245, 176, 35, 12, 10, 17, + 237, 85, 242, 196, 239, 111, 10, 17, 234, 51, 252, 124, 57, 238, 79, 237, + 86, 238, 79, 237, 87, 238, 79, 237, 88, 238, 79, 237, 89, 238, 79, 237, + 90, 238, 79, 237, 91, 238, 79, 237, 92, 238, 79, 237, 93, 238, 79, 237, + 94, 238, 79, 236, 163, 238, 79, 236, 164, 238, 79, 236, 165, 238, 79, + 236, 166, 238, 79, 236, 167, 238, 79, 236, 168, 238, 79, 236, 169, 48, + 17, 36, 244, 217, 48, 17, 37, 244, 217, 48, 42, 229, 36, 244, 217, 48, + 42, 229, 37, 244, 217, 236, 45, 249, 3, 54, 55, 225, 49, 241, 25, 236, + 45, 249, 3, 54, 55, 254, 220, 244, 189, 236, 45, 249, 3, 54, 55, 254, + 215, 244, 189, 236, 45, 249, 3, 54, 55, 253, 113, 244, 176, 236, 45, 249, + 3, 54, 55, 249, 57, 253, 113, 244, 176, 236, 45, 249, 3, 54, 55, 36, 236, + 47, 236, 45, 249, 3, 54, 55, 37, 236, 47, 236, 45, 249, 3, 54, 55, 36, + 244, 175, 110, 236, 45, 249, 3, 54, 55, 37, 244, 175, 110, 236, 45, 249, + 3, 54, 55, 36, 239, 139, 244, 202, 110, 236, 45, 249, 3, 54, 55, 37, 239, + 139, 244, 202, 110, 236, 45, 249, 3, 54, 55, 36, 86, 237, 126, 110, 236, + 45, 249, 3, 54, 55, 37, 86, 237, 126, 110, 236, 45, 249, 3, 54, 55, 36, + 42, 160, 110, 236, 45, 249, 3, 54, 55, 37, 42, 160, 110, 236, 45, 249, 3, + 54, 55, 36, 160, 110, 236, 45, 249, 3, 54, 55, 37, 160, 110, 236, 45, + 249, 3, 54, 55, 36, 242, 86, 110, 236, 45, 249, 3, 54, 55, 37, 242, 86, + 110, 236, 45, 249, 3, 54, 55, 36, 58, 242, 86, 110, 236, 45, 249, 3, 54, + 55, 37, 58, 242, 86, 110, 242, 247, 227, 58, 242, 247, 227, 242, 105, + 238, 98, 237, 149, 238, 98, 249, 57, 238, 98, 42, 249, 57, 238, 98, 242, + 105, 253, 113, 244, 176, 237, 149, 253, 113, 244, 176, 249, 57, 253, 113, + 244, 176, 3, 242, 84, 3, 66, 242, 84, 3, 213, 189, 3, 237, 123, 3, 242, + 81, 3, 239, 126, 65, 3, 236, 186, 65, 3, 254, 220, 244, 189, 3, 36, 236, + 47, 3, 37, 236, 47, 3, 36, 244, 175, 110, 3, 37, 244, 175, 110, 3, 36, + 239, 139, 244, 202, 110, 3, 37, 239, 139, 244, 202, 110, 3, 83, 57, 3, + 237, 124, 3, 238, 77, 3, 249, 4, 57, 3, 235, 243, 3, 206, 57, 3, 196, 57, + 3, 242, 61, 57, 3, 240, 219, 239, 144, 3, 242, 158, 57, 3, 240, 248, 57, + 3, 237, 127, 253, 220, 10, 239, 172, 35, 12, 10, 242, 30, 2, 239, 172, + 47, 10, 239, 211, 35, 12, 10, 249, 88, 239, 20, 10, 239, 220, 35, 12, 10, + 244, 249, 35, 12, 10, 244, 249, 98, 12, 10, 244, 248, 35, 12, 10, 244, + 248, 98, 12, 10, 244, 252, 35, 12, 10, 244, 252, 98, 12, 10, 242, 102, + 35, 12, 10, 242, 102, 98, 12, 10, 245, 197, 35, 12, 10, 245, 197, 98, 12, + 10, 1, 125, 35, 12, 10, 1, 67, 2, 244, 237, 53, 35, 12, 10, 1, 67, 2, + 244, 237, 53, 29, 12, 10, 1, 67, 2, 125, 53, 35, 12, 10, 1, 67, 2, 125, + 53, 29, 12, 10, 1, 253, 162, 2, 125, 53, 35, 12, 10, 1, 253, 162, 2, 125, + 53, 29, 12, 10, 1, 67, 2, 125, 244, 188, 35, 12, 10, 1, 67, 2, 125, 244, + 188, 29, 12, 10, 1, 50, 2, 125, 53, 35, 12, 10, 1, 50, 2, 125, 53, 29, + 12, 10, 1, 50, 2, 125, 53, 46, 12, 10, 1, 50, 2, 125, 53, 98, 12, 10, 1, + 67, 35, 12, 10, 1, 67, 29, 12, 10, 1, 253, 102, 35, 12, 10, 1, 253, 102, + 29, 12, 10, 1, 253, 102, 46, 12, 10, 1, 253, 102, 98, 12, 10, 1, 191, + 241, 26, 35, 12, 10, 1, 191, 241, 26, 29, 12, 10, 1, 191, 35, 12, 10, 1, + 191, 29, 12, 10, 1, 191, 46, 12, 10, 1, 191, 98, 12, 10, 1, 249, 20, 35, + 12, 10, 1, 249, 20, 29, 12, 10, 1, 249, 20, 46, 12, 10, 1, 249, 20, 98, + 12, 10, 1, 89, 35, 12, 10, 1, 89, 29, 12, 10, 1, 89, 46, 12, 10, 1, 89, + 98, 12, 10, 1, 81, 35, 12, 10, 1, 81, 29, 12, 10, 1, 81, 46, 12, 10, 1, + 81, 98, 12, 10, 1, 145, 35, 12, 10, 1, 145, 29, 12, 10, 1, 145, 46, 12, + 10, 1, 145, 98, 12, 10, 1, 253, 180, 35, 12, 10, 1, 253, 180, 29, 12, 10, + 1, 249, 165, 35, 12, 10, 1, 249, 165, 29, 12, 10, 1, 253, 105, 35, 12, + 10, 1, 253, 105, 29, 12, 10, 1, 249, 59, 35, 12, 10, 1, 249, 59, 29, 12, + 10, 1, 214, 35, 12, 10, 1, 214, 29, 12, 10, 1, 214, 46, 12, 10, 1, 214, + 98, 12, 10, 1, 215, 35, 12, 10, 1, 215, 29, 12, 10, 1, 215, 46, 12, 10, + 1, 215, 98, 12, 10, 1, 253, 108, 35, 12, 10, 1, 253, 108, 29, 12, 10, 1, + 253, 108, 46, 12, 10, 1, 253, 108, 98, 12, 10, 1, 253, 107, 35, 12, 10, + 1, 253, 107, 29, 12, 10, 1, 253, 107, 46, 12, 10, 1, 253, 107, 98, 12, + 10, 1, 253, 122, 35, 12, 10, 1, 253, 122, 29, 12, 10, 1, 253, 122, 46, + 12, 10, 1, 253, 122, 98, 12, 10, 1, 253, 201, 35, 12, 10, 1, 253, 201, + 29, 12, 10, 1, 253, 201, 46, 12, 10, 1, 253, 201, 98, 12, 10, 1, 253, + 117, 35, 12, 10, 1, 253, 117, 29, 12, 10, 1, 253, 117, 46, 12, 10, 1, + 253, 117, 98, 12, 10, 1, 244, 190, 35, 12, 10, 1, 244, 190, 29, 12, 10, + 1, 244, 190, 46, 12, 10, 1, 244, 190, 98, 12, 10, 1, 253, 111, 35, 12, + 10, 1, 253, 111, 29, 12, 10, 1, 253, 111, 46, 12, 10, 1, 253, 111, 98, + 12, 10, 1, 253, 120, 35, 12, 10, 1, 253, 120, 29, 12, 10, 1, 253, 120, + 46, 12, 10, 1, 253, 120, 98, 12, 10, 1, 253, 87, 35, 12, 10, 1, 253, 87, + 29, 12, 10, 1, 253, 87, 46, 12, 10, 1, 253, 87, 98, 12, 10, 1, 155, 35, + 12, 10, 1, 155, 29, 12, 10, 1, 155, 46, 12, 10, 1, 155, 98, 12, 10, 1, + 43, 35, 12, 10, 1, 43, 29, 12, 10, 1, 43, 46, 12, 10, 1, 43, 98, 12, 10, + 1, 177, 35, 12, 10, 1, 177, 29, 12, 10, 1, 177, 46, 12, 10, 1, 177, 98, + 12, 10, 1, 188, 35, 12, 10, 1, 188, 29, 12, 10, 1, 188, 46, 12, 10, 1, + 188, 98, 12, 10, 1, 253, 162, 35, 12, 10, 1, 253, 162, 29, 12, 10, 1, 67, + 131, 35, 12, 10, 1, 67, 131, 29, 12, 10, 1, 50, 35, 12, 10, 1, 50, 29, + 12, 10, 1, 50, 46, 12, 10, 1, 50, 98, 12, 10, 17, 155, 2, 67, 2, 244, + 237, 53, 35, 12, 10, 17, 155, 2, 67, 2, 244, 237, 53, 29, 12, 10, 17, + 155, 2, 67, 2, 125, 53, 35, 12, 10, 17, 155, 2, 67, 2, 125, 53, 29, 12, + 10, 17, 155, 2, 67, 2, 125, 244, 188, 35, 12, 10, 17, 155, 2, 67, 2, 125, + 244, 188, 29, 12, 10, 17, 155, 2, 67, 35, 12, 10, 17, 155, 2, 67, 29, 12, + 249, 127, 245, 12, 239, 190, 242, 69, 73, 236, 186, 65, 73, 238, 76, 65, + 73, 83, 57, 73, 242, 158, 57, 73, 240, 248, 57, 73, 237, 124, 73, 236, + 190, 73, 36, 236, 47, 73, 37, 236, 47, 73, 238, 77, 73, 249, 4, 57, 73, + 242, 84, 73, 235, 243, 73, 213, 189, 73, 239, 144, 73, 27, 244, 173, 73, + 27, 121, 73, 27, 114, 73, 27, 153, 73, 27, 163, 73, 27, 168, 73, 27, 169, + 73, 27, 179, 73, 27, 176, 73, 27, 178, 73, 242, 81, 73, 237, 123, 73, + 206, 57, 73, 242, 61, 57, 73, 196, 57, 73, 239, 126, 65, 73, 237, 127, + 253, 220, 73, 9, 5, 1, 64, 73, 9, 5, 1, 199, 73, 9, 5, 1, 203, 73, 9, 5, + 1, 187, 73, 9, 5, 1, 70, 73, 9, 5, 1, 204, 73, 9, 5, 1, 194, 73, 9, 5, 1, + 164, 73, 9, 5, 1, 69, 73, 9, 5, 1, 200, 73, 9, 5, 1, 205, 73, 9, 5, 1, + 148, 73, 9, 5, 1, 171, 73, 9, 5, 1, 183, 73, 9, 5, 1, 78, 73, 9, 5, 1, + 198, 73, 9, 5, 1, 209, 73, 9, 5, 1, 135, 73, 9, 5, 1, 159, 73, 9, 5, 1, + 190, 73, 9, 5, 1, 84, 73, 9, 5, 1, 186, 73, 9, 5, 1, 201, 73, 9, 5, 1, + 170, 73, 9, 5, 1, 181, 73, 9, 5, 1, 202, 73, 36, 30, 110, 73, 240, 219, + 239, 144, 73, 37, 30, 110, 73, 157, 240, 203, 73, 253, 113, 244, 176, 73, + 244, 201, 240, 203, 73, 9, 3, 1, 64, 73, 9, 3, 1, 199, 73, 9, 3, 1, 203, + 73, 9, 3, 1, 187, 73, 9, 3, 1, 70, 73, 9, 3, 1, 204, 73, 9, 3, 1, 194, + 73, 9, 3, 1, 164, 73, 9, 3, 1, 69, 73, 9, 3, 1, 200, 73, 9, 3, 1, 205, + 73, 9, 3, 1, 148, 73, 9, 3, 1, 171, 73, 9, 3, 1, 183, 73, 9, 3, 1, 78, + 73, 9, 3, 1, 198, 73, 9, 3, 1, 209, 73, 9, 3, 1, 135, 73, 9, 3, 1, 159, + 73, 9, 3, 1, 190, 73, 9, 3, 1, 84, 73, 9, 3, 1, 186, 73, 9, 3, 1, 201, + 73, 9, 3, 1, 170, 73, 9, 3, 1, 181, 73, 9, 3, 1, 202, 73, 36, 244, 175, + 110, 73, 55, 244, 176, 73, 37, 244, 175, 110, 73, 180, 73, 36, 58, 236, + 47, 73, 37, 58, 236, 47, 62, 66, 213, 189, 62, 36, 242, 86, 110, 62, 37, + 242, 86, 110, 62, 66, 242, 84, 62, 39, 242, 65, 227, 62, 39, 1, 253, 138, + 62, 39, 1, 3, 64, 62, 39, 1, 3, 69, 62, 39, 1, 3, 84, 62, 39, 1, 3, 70, + 62, 39, 1, 3, 78, 62, 39, 1, 3, 216, 62, 39, 1, 3, 253, 124, 62, 39, 1, + 3, 253, 123, 62, 39, 1, 3, 253, 160, 62, 232, 3, 238, 153, 244, 205, 65, + 62, 39, 1, 64, 62, 39, 1, 69, 62, 39, 1, 84, 62, 39, 1, 70, 62, 39, 1, + 78, 62, 39, 1, 208, 62, 39, 1, 253, 182, 62, 39, 1, 253, 183, 62, 39, 1, + 253, 134, 62, 39, 1, 253, 155, 62, 39, 1, 253, 97, 62, 39, 1, 253, 158, + 62, 39, 1, 253, 172, 62, 39, 1, 253, 171, 62, 39, 1, 253, 159, 62, 39, 1, + 253, 91, 62, 39, 1, 253, 174, 62, 39, 1, 253, 160, 62, 39, 1, 253, 151, + 62, 39, 1, 76, 62, 39, 1, 253, 94, 62, 39, 1, 253, 148, 62, 39, 1, 253, + 112, 62, 39, 1, 253, 157, 62, 39, 1, 253, 136, 62, 39, 1, 253, 88, 62, + 39, 1, 253, 154, 62, 39, 1, 253, 197, 62, 39, 1, 253, 125, 62, 39, 1, + 253, 140, 62, 39, 1, 221, 62, 39, 1, 253, 156, 62, 39, 1, 253, 114, 62, + 39, 1, 253, 167, 62, 39, 1, 253, 141, 62, 39, 1, 216, 62, 39, 1, 253, + 124, 62, 39, 1, 253, 123, 62, 39, 1, 253, 92, 62, 39, 1, 253, 169, 62, + 39, 1, 253, 170, 62, 39, 1, 253, 150, 62, 39, 1, 253, 144, 62, 39, 1, + 253, 98, 62, 39, 1, 183, 62, 39, 242, 116, 244, 205, 65, 62, 39, 236, + 205, 244, 205, 65, 62, 20, 240, 255, 62, 20, 1, 240, 242, 62, 20, 1, 236, + 59, 62, 20, 1, 236, 63, 62, 20, 1, 242, 131, 62, 20, 1, 236, 65, 62, 20, + 1, 236, 66, 62, 20, 1, 240, 244, 62, 20, 1, 236, 72, 62, 20, 1, 242, 134, + 62, 20, 1, 235, 247, 62, 20, 1, 236, 67, 62, 20, 1, 236, 68, 62, 20, 1, + 236, 204, 62, 20, 1, 235, 197, 62, 20, 1, 235, 196, 62, 20, 1, 236, 57, + 62, 20, 1, 242, 129, 62, 20, 1, 242, 133, 62, 20, 1, 236, 209, 62, 20, 1, + 236, 198, 62, 20, 1, 244, 230, 62, 20, 1, 237, 138, 62, 20, 1, 242, 126, + 62, 20, 1, 242, 122, 62, 20, 1, 236, 207, 62, 20, 1, 239, 155, 62, 20, 1, + 239, 158, 62, 20, 1, 239, 166, 62, 20, 1, 239, 161, 62, 20, 1, 242, 125, + 62, 20, 1, 64, 62, 20, 1, 253, 163, 62, 20, 1, 216, 62, 20, 1, 249, 181, + 62, 20, 1, 254, 10, 62, 20, 1, 70, 62, 20, 1, 249, 182, 62, 20, 1, 253, + 200, 62, 20, 1, 78, 62, 20, 1, 253, 98, 62, 20, 1, 249, 175, 62, 20, 1, + 253, 142, 62, 20, 1, 253, 123, 62, 20, 1, 84, 62, 20, 1, 249, 177, 62, + 20, 1, 253, 132, 62, 20, 1, 253, 145, 62, 20, 1, 253, 124, 62, 20, 1, + 254, 12, 62, 20, 1, 253, 139, 62, 20, 1, 69, 73, 249, 196, 57, 73, 245, + 163, 57, 73, 141, 57, 73, 166, 73, 242, 173, 143, 73, 254, 78, 57, 73, + 254, 76, 57, 62, 246, 131, 117, 238, 88, 62, 126, 52, 62, 210, 52, 62, + 63, 52, 62, 220, 52, 62, 86, 240, 207, 62, 58, 240, 200, 236, 202, 237, + 111, 241, 97, 236, 202, 237, 111, 237, 114, 236, 202, 237, 111, 237, 103, + 244, 18, 236, 228, 237, 150, 236, 228, 237, 150, 222, 21, 1, 64, 222, 21, + 1, 253, 191, 222, 21, 1, 253, 134, 222, 21, 1, 253, 152, 222, 21, 1, 70, + 222, 21, 1, 253, 214, 222, 21, 1, 253, 189, 222, 21, 1, 253, 125, 222, + 21, 1, 249, 63, 222, 21, 1, 69, 222, 21, 1, 208, 222, 21, 1, 253, 223, + 222, 21, 1, 253, 224, 222, 21, 1, 253, 166, 222, 21, 1, 249, 67, 222, 21, + 1, 78, 222, 21, 1, 253, 143, 222, 21, 1, 249, 70, 222, 21, 1, 253, 183, + 222, 21, 1, 253, 205, 222, 21, 1, 253, 225, 222, 21, 1, 253, 151, 222, + 21, 1, 84, 222, 21, 1, 250, 242, 222, 21, 1, 250, 43, 222, 21, 1, 249, + 255, 222, 21, 1, 253, 222, 222, 21, 1, 250, 246, 222, 21, 1, 249, 44, + 222, 21, 1, 253, 96, 222, 21, 1, 253, 104, 222, 21, 151, 121, 222, 21, + 151, 168, 222, 21, 151, 249, 18, 222, 21, 151, 242, 144, 242, 62, 1, 245, + 238, 242, 62, 1, 240, 3, 242, 62, 1, 246, 154, 242, 62, 1, 246, 79, 242, + 62, 1, 241, 94, 242, 62, 1, 239, 66, 242, 62, 1, 247, 0, 242, 62, 1, 246, + 170, 242, 62, 1, 242, 36, 242, 62, 1, 250, 241, 242, 62, 1, 243, 194, + 242, 62, 1, 243, 199, 242, 62, 1, 243, 211, 242, 62, 1, 241, 219, 242, + 62, 1, 251, 120, 242, 62, 1, 248, 162, 242, 62, 1, 239, 56, 242, 62, 1, + 241, 69, 242, 62, 1, 244, 51, 242, 62, 1, 244, 71, 242, 62, 1, 244, 113, + 242, 62, 1, 244, 147, 242, 62, 1, 243, 105, 242, 62, 1, 243, 173, 242, + 62, 1, 242, 225, 242, 62, 1, 244, 21, 242, 62, 1, 250, 29, 239, 150, 111, + 5, 1, 165, 69, 111, 5, 1, 165, 70, 111, 5, 1, 165, 64, 111, 5, 1, 165, + 253, 219, 111, 5, 1, 165, 78, 111, 5, 1, 165, 253, 119, 111, 5, 1, 207, + 69, 111, 5, 1, 207, 70, 111, 5, 1, 207, 64, 111, 5, 1, 207, 253, 219, + 111, 5, 1, 207, 78, 111, 5, 1, 207, 253, 119, 111, 5, 1, 254, 2, 111, 5, + 1, 254, 64, 111, 5, 1, 253, 216, 111, 5, 1, 249, 125, 111, 5, 1, 164, + 111, 5, 1, 249, 114, 111, 5, 1, 249, 128, 111, 5, 1, 249, 164, 111, 5, 1, + 249, 96, 111, 5, 1, 244, 246, 111, 5, 1, 249, 104, 111, 5, 1, 249, 253, + 111, 5, 1, 249, 228, 111, 5, 1, 253, 222, 111, 5, 1, 249, 174, 111, 5, 1, + 249, 77, 111, 5, 1, 245, 9, 111, 5, 1, 253, 225, 111, 5, 1, 249, 126, + 111, 5, 1, 249, 67, 111, 5, 1, 245, 53, 111, 5, 1, 253, 205, 111, 5, 1, + 253, 223, 111, 5, 1, 253, 224, 111, 5, 1, 253, 166, 111, 5, 1, 249, 66, + 111, 3, 1, 165, 69, 111, 3, 1, 165, 70, 111, 3, 1, 165, 64, 111, 3, 1, + 165, 253, 219, 111, 3, 1, 165, 78, 111, 3, 1, 165, 253, 119, 111, 3, 1, + 207, 69, 111, 3, 1, 207, 70, 111, 3, 1, 207, 64, 111, 3, 1, 207, 253, + 219, 111, 3, 1, 207, 78, 111, 3, 1, 207, 253, 119, 111, 3, 1, 254, 2, + 111, 3, 1, 254, 64, 111, 3, 1, 253, 216, 111, 3, 1, 249, 125, 111, 3, 1, + 164, 111, 3, 1, 249, 114, 111, 3, 1, 249, 128, 111, 3, 1, 249, 164, 111, + 3, 1, 249, 96, 111, 3, 1, 244, 246, 111, 3, 1, 249, 104, 111, 3, 1, 249, + 253, 111, 3, 1, 249, 228, 111, 3, 1, 253, 222, 111, 3, 1, 249, 174, 111, + 3, 1, 249, 77, 111, 3, 1, 245, 9, 111, 3, 1, 253, 225, 111, 3, 1, 249, + 126, 111, 3, 1, 249, 67, 111, 3, 1, 245, 53, 111, 3, 1, 253, 205, 111, 3, + 1, 253, 223, 111, 3, 1, 253, 224, 111, 3, 1, 253, 166, 111, 3, 1, 249, + 66, 34, 19, 13, 242, 78, 34, 19, 13, 241, 135, 34, 19, 13, 236, 43, 34, + 19, 13, 245, 34, 236, 55, 34, 19, 13, 245, 34, 242, 124, 34, 19, 13, 242, + 192, 236, 55, 34, 19, 13, 242, 192, 242, 124, 34, 19, 13, 239, 36, 34, + 19, 13, 238, 66, 34, 19, 13, 236, 151, 34, 19, 13, 238, 74, 34, 19, 13, + 239, 113, 242, 124, 34, 19, 13, 239, 40, 34, 19, 13, 245, 55, 236, 55, + 34, 19, 13, 254, 39, 236, 55, 34, 19, 13, 241, 83, 34, 19, 13, 238, 24, + 34, 19, 13, 236, 244, 34, 19, 13, 237, 146, 242, 124, 34, 19, 13, 240, + 174, 34, 19, 13, 244, 117, 34, 19, 13, 242, 235, 238, 80, 34, 19, 13, + 242, 145, 238, 80, 34, 19, 13, 241, 245, 34, 19, 13, 238, 188, 34, 19, + 13, 244, 138, 34, 19, 13, 249, 246, 238, 80, 34, 19, 13, 241, 1, 238, 80, + 34, 19, 13, 238, 112, 238, 80, 34, 19, 13, 239, 79, 34, 19, 13, 239, 59, + 34, 19, 13, 242, 20, 238, 171, 34, 19, 13, 244, 22, 238, 80, 34, 19, 13, + 242, 48, 238, 80, 34, 19, 13, 239, 201, 238, 80, 34, 19, 13, 238, 172, + 34, 19, 13, 241, 62, 34, 19, 13, 244, 58, 34, 19, 13, 242, 237, 238, 80, + 34, 19, 13, 240, 183, 34, 19, 13, 236, 5, 34, 19, 13, 242, 7, 34, 19, 13, + 241, 33, 238, 80, 34, 19, 13, 241, 33, 251, 225, 240, 170, 34, 19, 13, + 238, 149, 238, 80, 34, 19, 13, 244, 115, 34, 19, 13, 243, 202, 34, 19, + 13, 250, 237, 34, 19, 13, 248, 138, 34, 19, 13, 240, 179, 34, 19, 13, + 238, 26, 34, 19, 13, 245, 55, 254, 39, 249, 16, 34, 19, 13, 242, 71, 238, + 80, 34, 19, 13, 238, 17, 34, 19, 13, 239, 198, 238, 80, 34, 19, 13, 243, + 184, 239, 104, 34, 19, 13, 239, 61, 34, 19, 13, 238, 48, 34, 19, 13, 239, + 38, 34, 19, 13, 239, 208, 238, 80, 34, 19, 13, 240, 149, 34, 19, 13, 236, + 222, 238, 80, 34, 19, 13, 236, 223, 238, 80, 34, 19, 13, 240, 89, 34, 19, + 13, 245, 147, 34, 19, 13, 240, 136, 34, 19, 13, 240, 100, 245, 13, 34, + 19, 13, 239, 198, 245, 13, 34, 19, 13, 236, 37, 34, 19, 13, 236, 21, 34, + 19, 13, 249, 246, 249, 16, 34, 19, 13, 242, 235, 249, 16, 34, 19, 13, + 245, 34, 249, 16, 34, 19, 13, 240, 137, 34, 19, 13, 239, 39, 34, 19, 13, + 234, 56, 34, 19, 13, 234, 52, 34, 19, 13, 240, 135, 249, 16, 34, 19, 13, + 238, 112, 253, 185, 249, 60, 34, 19, 13, 241, 1, 253, 185, 249, 60, 34, + 19, 13, 242, 58, 34, 19, 13, 237, 146, 249, 16, 34, 19, 13, 237, 145, + 239, 100, 249, 16, 34, 19, 13, 240, 198, 34, 19, 13, 234, 53, 34, 19, 13, + 240, 66, 34, 19, 13, 240, 15, 34, 19, 13, 243, 228, 246, 254, 34, 19, 13, + 242, 192, 249, 16, 34, 19, 13, 242, 237, 249, 16, 34, 19, 13, 239, 65, + 249, 16, 34, 19, 13, 241, 227, 34, 19, 13, 236, 242, 34, 19, 13, 240, + 106, 34, 19, 13, 236, 223, 249, 16, 34, 19, 13, 236, 222, 249, 16, 34, + 19, 13, 242, 209, 236, 150, 34, 19, 13, 240, 103, 34, 19, 13, 232, 17, + 34, 19, 13, 239, 198, 249, 16, 34, 19, 13, 237, 58, 34, 19, 13, 241, 33, + 249, 16, 34, 19, 13, 244, 108, 34, 19, 13, 239, 208, 249, 16, 34, 19, 13, + 239, 8, 34, 19, 13, 244, 73, 249, 16, 34, 19, 13, 248, 181, 241, 62, 34, + 19, 13, 232, 13, 34, 19, 13, 234, 58, 34, 19, 13, 235, 187, 34, 19, 13, + 231, 247, 34, 19, 13, 231, 238, 34, 19, 13, 235, 188, 34, 19, 13, 234, + 59, 34, 19, 13, 234, 71, 34, 19, 13, 235, 198, 34, 19, 13, 242, 209, 235, + 198, 34, 19, 13, 238, 149, 249, 16, 34, 19, 13, 236, 237, 250, 247, 34, + 19, 13, 236, 237, 250, 250, 34, 19, 13, 248, 123, 241, 38, 34, 19, 13, + 252, 205, 254, 15, 239, 253, 34, 19, 13, 238, 23, 34, 19, 13, 238, 4, 34, + 19, 13, 250, 115, 244, 221, 34, 19, 13, 250, 115, 249, 60, 34, 19, 13, + 240, 169, 34, 19, 13, 242, 228, 249, 60, 34, 19, 13, 246, 108, 238, 80, + 34, 19, 13, 241, 24, 238, 80, 34, 19, 13, 241, 24, 245, 13, 34, 19, 13, + 241, 24, 249, 16, 34, 19, 13, 239, 201, 249, 16, 34, 19, 13, 245, 248, + 34, 19, 13, 242, 124, 34, 19, 13, 242, 38, 34, 19, 13, 239, 102, 34, 19, + 13, 239, 186, 34, 19, 13, 242, 146, 250, 244, 239, 209, 34, 19, 13, 242, + 146, 254, 7, 239, 178, 34, 19, 13, 242, 146, 248, 139, 239, 178, 34, 19, + 13, 242, 146, 240, 178, 239, 178, 34, 19, 13, 242, 146, 241, 184, 239, + 209, 34, 19, 13, 242, 145, 253, 185, 249, 60, 34, 19, 13, 242, 145, 235, + 242, 238, 176, 34, 19, 13, 242, 145, 235, 242, 242, 208, 34, 19, 13, 238, + 202, 34, 19, 13, 239, 180, 235, 242, 239, 202, 244, 221, 34, 19, 13, 239, + 180, 235, 242, 239, 202, 249, 60, 34, 19, 13, 239, 180, 235, 242, 242, + 208, 34, 19, 13, 238, 71, 34, 19, 13, 243, 32, 34, 19, 13, 237, 69, 34, + 19, 13, 240, 26, 34, 19, 13, 244, 213, 250, 46, 245, 56, 34, 19, 13, 244, + 213, 238, 175, 34, 19, 13, 244, 213, 245, 56, 34, 19, 13, 244, 213, 241, + 212, 34, 19, 13, 244, 213, 247, 78, 34, 19, 13, 244, 213, 242, 220, 34, + 19, 13, 244, 213, 238, 11, 34, 19, 13, 244, 213, 250, 46, 242, 220, 34, + 19, 13, 239, 128, 242, 240, 242, 89, 34, 19, 13, 239, 128, 249, 186, 242, + 240, 242, 89, 34, 19, 13, 239, 128, 239, 212, 242, 89, 34, 19, 13, 239, + 128, 249, 186, 239, 212, 242, 89, 34, 19, 13, 239, 128, 244, 124, 242, + 89, 34, 19, 13, 239, 128, 238, 70, 34, 19, 13, 239, 128, 239, 197, 242, + 89, 34, 19, 13, 239, 128, 239, 197, 241, 65, 242, 89, 34, 19, 13, 239, + 128, 241, 65, 242, 89, 34, 19, 13, 239, 128, 241, 75, 242, 89, 34, 19, + 13, 243, 174, 243, 9, 237, 159, 34, 19, 13, 237, 145, 243, 9, 237, 159, + 34, 19, 13, 238, 120, 236, 172, 34, 19, 13, 238, 120, 236, 217, 34, 19, + 13, 238, 120, 238, 137, 34, 19, 13, 239, 128, 248, 163, 242, 89, 34, 19, + 13, 239, 128, 238, 47, 242, 89, 34, 19, 13, 239, 128, 241, 75, 239, 197, + 242, 89, 34, 19, 13, 237, 158, 255, 28, 241, 38, 34, 19, 13, 237, 158, + 255, 28, 240, 29, 34, 19, 13, 241, 153, 254, 15, 242, 71, 250, 107, 34, + 19, 13, 239, 30, 34, 19, 13, 237, 70, 34, 19, 13, 242, 71, 240, 0, 240, + 24, 243, 172, 34, 19, 13, 242, 71, 238, 95, 253, 90, 34, 19, 13, 242, 71, + 238, 95, 245, 147, 34, 19, 13, 242, 71, 251, 254, 242, 89, 34, 19, 13, + 242, 71, 238, 95, 253, 165, 34, 19, 13, 242, 71, 241, 31, 240, 27, 253, + 165, 34, 19, 13, 242, 71, 238, 95, 253, 134, 34, 19, 13, 242, 71, 238, + 95, 253, 190, 34, 19, 13, 242, 71, 238, 95, 254, 252, 244, 221, 34, 19, + 13, 242, 71, 238, 95, 254, 252, 249, 60, 34, 19, 13, 242, 71, 241, 66, + 242, 154, 238, 137, 34, 19, 13, 242, 71, 241, 66, 242, 154, 236, 217, 34, + 19, 13, 243, 109, 241, 31, 242, 154, 244, 137, 34, 19, 13, 242, 71, 241, + 31, 242, 154, 242, 28, 34, 19, 13, 242, 71, 241, 220, 34, 19, 13, 245, + 17, 244, 166, 34, 19, 13, 245, 17, 241, 193, 34, 19, 13, 245, 17, 242, + 17, 34, 19, 13, 242, 71, 197, 242, 136, 235, 207, 34, 19, 13, 242, 71, + 238, 3, 237, 173, 34, 19, 13, 242, 136, 236, 81, 34, 19, 13, 242, 123, + 236, 81, 34, 19, 13, 242, 123, 235, 207, 34, 19, 13, 242, 123, 249, 92, + 254, 7, 238, 92, 34, 19, 13, 242, 123, 236, 218, 241, 85, 238, 92, 34, + 19, 13, 242, 123, 238, 138, 254, 247, 238, 92, 34, 19, 13, 242, 123, 237, + 170, 250, 36, 238, 92, 34, 19, 13, 242, 136, 249, 92, 254, 7, 238, 92, + 34, 19, 13, 242, 136, 236, 218, 241, 85, 238, 92, 34, 19, 13, 242, 136, + 238, 138, 254, 247, 238, 92, 34, 19, 13, 242, 136, 237, 170, 250, 36, + 238, 92, 34, 19, 13, 242, 216, 241, 140, 34, 19, 13, 242, 216, 242, 56, + 34, 19, 13, 239, 171, 249, 92, 243, 227, 34, 19, 13, 239, 171, 249, 92, + 241, 210, 34, 19, 13, 239, 171, 242, 124, 34, 19, 13, 239, 171, 239, 242, + 34, 19, 13, 239, 151, 239, 242, 34, 19, 13, 239, 151, 241, 35, 239, 214, + 34, 19, 13, 239, 151, 241, 35, 238, 162, 34, 19, 13, 239, 151, 241, 35, + 236, 236, 34, 19, 13, 239, 151, 241, 102, 34, 19, 13, 239, 151, 242, 172, + 239, 214, 34, 19, 13, 239, 151, 242, 172, 238, 162, 34, 19, 13, 239, 151, + 242, 172, 236, 236, 34, 19, 13, 240, 28, 254, 107, 34, 19, 13, 238, 201, + 253, 247, 34, 19, 13, 241, 32, 34, 19, 13, 240, 233, 253, 90, 34, 19, 13, + 240, 233, 250, 107, 34, 19, 13, 240, 233, 253, 100, 34, 19, 13, 240, 233, + 253, 165, 34, 19, 13, 240, 233, 253, 134, 34, 19, 13, 240, 233, 253, 190, + 34, 19, 13, 240, 233, 253, 148, 34, 19, 13, 238, 112, 253, 185, 245, 141, + 34, 19, 13, 241, 1, 253, 185, 245, 141, 34, 19, 13, 238, 112, 253, 185, + 244, 221, 34, 19, 13, 241, 1, 253, 185, 244, 221, 34, 19, 13, 242, 228, + 244, 221, 34, 19, 13, 242, 145, 253, 185, 244, 221, 19, 13, 242, 66, 239, + 154, 19, 13, 42, 239, 154, 19, 13, 29, 239, 154, 19, 13, 240, 219, 29, + 239, 154, 19, 13, 242, 105, 239, 154, 19, 13, 207, 239, 154, 19, 13, 36, + 242, 113, 57, 19, 13, 37, 242, 113, 57, 19, 13, 242, 113, 244, 215, 19, + 13, 253, 180, 242, 245, 19, 13, 255, 4, 246, 42, 19, 13, 242, 245, 19, + 13, 246, 72, 19, 13, 239, 193, 239, 15, 19, 13, 239, 193, 239, 16, 19, + 13, 239, 193, 239, 17, 19, 13, 240, 74, 19, 13, 241, 165, 45, 19, 13, + 243, 50, 65, 19, 13, 240, 11, 19, 13, 243, 48, 19, 13, 110, 19, 13, 240, + 130, 242, 135, 19, 13, 240, 188, 242, 135, 19, 13, 238, 68, 242, 135, 19, + 13, 239, 19, 242, 135, 19, 13, 239, 18, 242, 135, 19, 13, 240, 165, 242, + 135, 19, 13, 238, 62, 237, 156, 19, 13, 237, 64, 237, 156, 19, 13, 255, + 32, 244, 233, 19, 13, 255, 32, 249, 95, 242, 132, 244, 243, 19, 13, 255, + 32, 249, 95, 242, 132, 242, 152, 19, 13, 255, 36, 244, 233, 19, 13, 255, + 42, 244, 233, 19, 13, 255, 42, 249, 95, 242, 132, 244, 243, 19, 13, 255, + 42, 249, 95, 242, 132, 242, 152, 19, 13, 249, 217, 241, 125, 19, 13, 249, + 217, 241, 126, 19, 13, 42, 242, 248, 19, 13, 42, 245, 87, 19, 13, 249, + 134, 253, 131, 19, 13, 249, 134, 244, 185, 19, 13, 241, 28, 253, 131, 19, + 13, 241, 28, 244, 185, 19, 13, 245, 5, 253, 131, 19, 13, 245, 5, 244, + 185, 19, 13, 240, 224, 249, 5, 242, 248, 19, 13, 240, 224, 249, 5, 245, + 87, 19, 13, 243, 75, 245, 203, 19, 13, 254, 96, 245, 203, 19, 13, 242, + 132, 244, 243, 19, 13, 242, 132, 242, 152, 19, 13, 236, 78, 244, 243, 19, + 13, 236, 78, 242, 152, 19, 13, 247, 102, 244, 187, 19, 13, 245, 221, 244, + 187, 19, 13, 136, 244, 187, 19, 13, 240, 224, 244, 187, 19, 13, 242, 111, + 244, 187, 19, 13, 238, 127, 244, 187, 19, 13, 236, 2, 244, 187, 19, 13, + 236, 80, 244, 187, 19, 13, 253, 101, 241, 21, 236, 3, 244, 187, 19, 13, + 255, 43, 238, 102, 19, 13, 249, 4, 238, 102, 19, 13, 175, 255, 43, 238, + 102, 19, 13, 30, 239, 120, 242, 92, 19, 13, 30, 239, 120, 231, 231, 19, + 13, 239, 118, 239, 120, 75, 242, 92, 19, 13, 239, 118, 239, 120, 75, 231, + 231, 19, 13, 239, 118, 239, 120, 36, 242, 92, 19, 13, 239, 118, 239, 120, + 36, 231, 231, 19, 13, 239, 118, 239, 120, 37, 242, 92, 19, 13, 239, 118, + 239, 120, 37, 231, 231, 19, 13, 239, 118, 239, 120, 79, 242, 92, 19, 13, + 239, 118, 239, 120, 79, 231, 231, 19, 13, 239, 118, 239, 120, 75, 37, + 242, 92, 19, 13, 239, 118, 239, 120, 75, 37, 231, 231, 19, 13, 250, 20, + 239, 120, 242, 92, 19, 13, 250, 20, 239, 120, 231, 231, 19, 13, 235, 209, + 239, 120, 79, 242, 92, 19, 13, 235, 209, 239, 120, 79, 231, 231, 19, 13, + 236, 187, 238, 102, 19, 13, 253, 1, 238, 102, 19, 13, 239, 120, 231, 231, + 19, 13, 252, 41, 238, 102, 19, 13, 241, 49, 239, 120, 242, 92, 19, 13, + 241, 49, 239, 120, 231, 231, 19, 13, 212, 19, 13, 245, 221, 244, 208, 19, + 13, 136, 244, 208, 19, 13, 240, 224, 244, 208, 19, 13, 242, 111, 244, + 208, 19, 13, 238, 127, 244, 208, 19, 13, 236, 2, 244, 208, 19, 13, 236, + 80, 244, 208, 19, 13, 253, 101, 241, 21, 236, 3, 244, 208, 19, 13, 48, + 244, 239, 19, 13, 48, 236, 170, 244, 239, 19, 13, 48, 237, 167, 19, 13, + 48, 237, 168, 19, 13, 48, 237, 169, 19, 13, 239, 182, 237, 167, 19, 13, + 239, 182, 237, 168, 19, 13, 239, 182, 237, 169, 19, 13, 48, 236, 86, 227, + 19, 13, 48, 241, 167, 19, 13, 48, 241, 168, 19, 13, 48, 241, 169, 19, 13, + 48, 241, 170, 19, 13, 48, 241, 171, 19, 13, 244, 244, 245, 59, 19, 13, + 253, 127, 245, 59, 19, 13, 244, 244, 249, 89, 19, 13, 253, 127, 249, 89, + 19, 13, 244, 244, 245, 192, 19, 13, 253, 127, 245, 192, 19, 13, 244, 244, + 241, 73, 19, 13, 253, 127, 241, 73, 19, 13, 48, 240, 203, 19, 13, 48, + 239, 85, 19, 13, 48, 242, 32, 19, 13, 48, 235, 232, 19, 13, 48, 240, 111, + 19, 13, 48, 232, 7, 19, 13, 48, 232, 16, 19, 13, 48, 243, 206, 19, 13, + 237, 147, 253, 131, 19, 13, 237, 147, 244, 185, 19, 13, 48, 246, 112, 19, + 13, 48, 252, 126, 19, 13, 48, 246, 130, 19, 13, 48, 244, 89, 19, 13, 48, + 246, 22, 19, 13, 48, 42, 241, 36, 19, 13, 48, 242, 59, 241, 36, 19, 13, + 236, 160, 19, 13, 242, 25, 19, 13, 202, 19, 13, 244, 37, 19, 13, 243, + 222, 19, 13, 243, 117, 19, 13, 237, 184, 19, 13, 236, 94, 19, 13, 245, + 101, 250, 31, 242, 91, 19, 13, 245, 101, 250, 31, 254, 243, 242, 91, 19, + 13, 254, 194, 19, 13, 245, 214, 19, 13, 239, 116, 245, 214, 19, 13, 250, + 98, 242, 91, 19, 13, 250, 98, 253, 131, 19, 13, 239, 140, 239, 88, 19, + 13, 239, 140, 239, 89, 19, 13, 239, 140, 239, 90, 19, 13, 239, 140, 239, + 91, 19, 13, 239, 140, 239, 92, 19, 13, 239, 140, 239, 93, 19, 13, 239, + 140, 239, 94, 19, 13, 239, 140, 239, 95, 19, 13, 239, 140, 239, 96, 19, + 13, 239, 140, 238, 63, 19, 13, 239, 140, 238, 64, 19, 13, 236, 130, 19, + 13, 236, 147, 19, 13, 253, 127, 137, 242, 21, 19, 13, 242, 156, 242, 91, + 19, 13, 48, 79, 249, 129, 19, 13, 48, 75, 249, 129, 19, 13, 48, 239, 26, + 19, 13, 48, 252, 170, 238, 44, 19, 13, 245, 35, 65, 19, 13, 245, 35, 75, + 65, 19, 13, 136, 245, 35, 65, 19, 13, 238, 142, 253, 131, 19, 13, 238, + 142, 244, 185, 19, 13, 2, 236, 128, 19, 13, 246, 81, 19, 13, 250, 206, + 249, 185, 19, 13, 241, 208, 19, 13, 243, 205, 19, 13, 241, 118, 19, 13, + 237, 141, 242, 92, 19, 13, 237, 141, 231, 231, 19, 13, 241, 215, 19, 13, + 242, 232, 231, 231, 19, 13, 237, 142, 242, 92, 19, 13, 237, 142, 231, + 231, 19, 13, 249, 226, 242, 92, 19, 13, 249, 226, 231, 231, 19, 13, 245, + 134, 239, 229, 244, 187, 19, 13, 245, 134, 237, 135, 244, 187, 19, 13, + 243, 51, 244, 187, 19, 13, 237, 141, 244, 187, 19, 13, 242, 232, 244, + 187, 19, 13, 237, 142, 244, 187, 19, 13, 242, 115, 238, 125, 253, 179, + 237, 118, 238, 151, 19, 13, 242, 115, 238, 125, 253, 179, 237, 118, 236, + 215, 19, 13, 242, 115, 238, 125, 253, 179, 237, 118, 239, 229, 235, 249, + 19, 13, 242, 115, 236, 194, 253, 179, 237, 118, 238, 151, 19, 13, 242, + 115, 236, 194, 253, 179, 237, 118, 236, 215, 19, 13, 242, 115, 236, 194, + 253, 179, 237, 118, 237, 135, 235, 249, 19, 13, 242, 115, 236, 194, 253, + 179, 237, 118, 237, 135, 236, 9, 19, 13, 242, 115, 236, 194, 253, 179, + 237, 118, 237, 135, 236, 10, 19, 13, 243, 77, 19, 13, 238, 143, 255, 36, + 244, 233, 19, 13, 238, 143, 255, 42, 244, 233, 19, 13, 30, 199, 19, 13, + 244, 140, 19, 13, 240, 140, 19, 13, 241, 127, 19, 13, 238, 57, 19, 13, + 238, 191, 19, 13, 239, 103, 19, 13, 238, 46, 19, 13, 239, 63, 241, 55, + 19, 13, 239, 80, 241, 55, 19, 13, 240, 185, 238, 55, 19, 13, 254, 166, + 237, 100, 8, 16, 5, 64, 8, 16, 5, 199, 8, 16, 5, 203, 8, 16, 5, 187, 8, + 16, 5, 70, 8, 16, 5, 204, 8, 16, 5, 194, 8, 16, 5, 164, 8, 16, 5, 69, 8, + 16, 5, 200, 8, 16, 5, 205, 8, 16, 5, 148, 8, 16, 5, 171, 8, 16, 5, 183, + 8, 16, 5, 78, 8, 16, 5, 198, 8, 16, 5, 209, 8, 16, 5, 135, 8, 16, 5, 159, + 8, 16, 5, 190, 8, 16, 5, 84, 8, 16, 5, 186, 8, 16, 5, 201, 8, 16, 5, 170, + 8, 16, 5, 181, 8, 16, 5, 202, 8, 16, 3, 64, 8, 16, 3, 199, 8, 16, 3, 203, + 8, 16, 3, 187, 8, 16, 3, 70, 8, 16, 3, 204, 8, 16, 3, 194, 8, 16, 3, 164, + 8, 16, 3, 69, 8, 16, 3, 200, 8, 16, 3, 205, 8, 16, 3, 148, 8, 16, 3, 171, + 8, 16, 3, 183, 8, 16, 3, 78, 8, 16, 3, 198, 8, 16, 3, 209, 8, 16, 3, 135, + 8, 16, 3, 159, 8, 16, 3, 190, 8, 16, 3, 84, 8, 16, 3, 186, 8, 16, 3, 201, + 8, 16, 3, 170, 8, 16, 3, 181, 8, 16, 3, 202, 8, 21, 5, 64, 8, 21, 5, 199, + 8, 21, 5, 203, 8, 21, 5, 187, 8, 21, 5, 70, 8, 21, 5, 204, 8, 21, 5, 194, + 8, 21, 5, 164, 8, 21, 5, 69, 8, 21, 5, 200, 8, 21, 5, 205, 8, 21, 5, 148, + 8, 21, 5, 171, 8, 21, 5, 183, 8, 21, 5, 78, 8, 21, 5, 198, 8, 21, 5, 209, + 8, 21, 5, 135, 8, 21, 5, 159, 8, 21, 5, 190, 8, 21, 5, 84, 8, 21, 5, 186, + 8, 21, 5, 201, 8, 21, 5, 170, 8, 21, 5, 181, 8, 21, 5, 202, 8, 21, 3, 64, + 8, 21, 3, 199, 8, 21, 3, 203, 8, 21, 3, 187, 8, 21, 3, 70, 8, 21, 3, 204, + 8, 21, 3, 194, 8, 21, 3, 69, 8, 21, 3, 200, 8, 21, 3, 205, 8, 21, 3, 148, + 8, 21, 3, 171, 8, 21, 3, 183, 8, 21, 3, 78, 8, 21, 3, 198, 8, 21, 3, 209, + 8, 21, 3, 135, 8, 21, 3, 159, 8, 21, 3, 190, 8, 21, 3, 84, 8, 21, 3, 186, + 8, 21, 3, 201, 8, 21, 3, 170, 8, 21, 3, 181, 8, 21, 3, 202, 8, 16, 21, 5, + 64, 8, 16, 21, 5, 199, 8, 16, 21, 5, 203, 8, 16, 21, 5, 187, 8, 16, 21, + 5, 70, 8, 16, 21, 5, 204, 8, 16, 21, 5, 194, 8, 16, 21, 5, 164, 8, 16, + 21, 5, 69, 8, 16, 21, 5, 200, 8, 16, 21, 5, 205, 8, 16, 21, 5, 148, 8, + 16, 21, 5, 171, 8, 16, 21, 5, 183, 8, 16, 21, 5, 78, 8, 16, 21, 5, 198, + 8, 16, 21, 5, 209, 8, 16, 21, 5, 135, 8, 16, 21, 5, 159, 8, 16, 21, 5, + 190, 8, 16, 21, 5, 84, 8, 16, 21, 5, 186, 8, 16, 21, 5, 201, 8, 16, 21, + 5, 170, 8, 16, 21, 5, 181, 8, 16, 21, 5, 202, 8, 16, 21, 3, 64, 8, 16, + 21, 3, 199, 8, 16, 21, 3, 203, 8, 16, 21, 3, 187, 8, 16, 21, 3, 70, 8, + 16, 21, 3, 204, 8, 16, 21, 3, 194, 8, 16, 21, 3, 164, 8, 16, 21, 3, 69, + 8, 16, 21, 3, 200, 8, 16, 21, 3, 205, 8, 16, 21, 3, 148, 8, 16, 21, 3, + 171, 8, 16, 21, 3, 183, 8, 16, 21, 3, 78, 8, 16, 21, 3, 198, 8, 16, 21, + 3, 209, 8, 16, 21, 3, 135, 8, 16, 21, 3, 159, 8, 16, 21, 3, 190, 8, 16, + 21, 3, 84, 8, 16, 21, 3, 186, 8, 16, 21, 3, 201, 8, 16, 21, 3, 170, 8, + 16, 21, 3, 181, 8, 16, 21, 3, 202, 8, 71, 5, 64, 8, 71, 5, 203, 8, 71, 5, + 187, 8, 71, 5, 194, 8, 71, 5, 200, 8, 71, 5, 205, 8, 71, 5, 183, 8, 71, + 5, 78, 8, 71, 5, 198, 8, 71, 5, 209, 8, 71, 5, 159, 8, 71, 5, 190, 8, 71, + 5, 84, 8, 71, 5, 186, 8, 71, 5, 201, 8, 71, 5, 170, 8, 71, 5, 181, 8, 71, + 5, 202, 8, 71, 3, 64, 8, 71, 3, 199, 8, 71, 3, 203, 8, 71, 3, 187, 8, 71, + 3, 204, 8, 71, 3, 164, 8, 71, 3, 69, 8, 71, 3, 200, 8, 71, 3, 205, 8, 71, + 3, 171, 8, 71, 3, 183, 8, 71, 3, 198, 8, 71, 3, 209, 8, 71, 3, 135, 8, + 71, 3, 159, 8, 71, 3, 190, 8, 71, 3, 84, 8, 71, 3, 186, 8, 71, 3, 201, 8, + 71, 3, 170, 8, 71, 3, 181, 8, 71, 3, 202, 8, 16, 71, 5, 64, 8, 16, 71, 5, + 199, 8, 16, 71, 5, 203, 8, 16, 71, 5, 187, 8, 16, 71, 5, 70, 8, 16, 71, + 5, 204, 8, 16, 71, 5, 194, 8, 16, 71, 5, 164, 8, 16, 71, 5, 69, 8, 16, + 71, 5, 200, 8, 16, 71, 5, 205, 8, 16, 71, 5, 148, 8, 16, 71, 5, 171, 8, + 16, 71, 5, 183, 8, 16, 71, 5, 78, 8, 16, 71, 5, 198, 8, 16, 71, 5, 209, + 8, 16, 71, 5, 135, 8, 16, 71, 5, 159, 8, 16, 71, 5, 190, 8, 16, 71, 5, + 84, 8, 16, 71, 5, 186, 8, 16, 71, 5, 201, 8, 16, 71, 5, 170, 8, 16, 71, + 5, 181, 8, 16, 71, 5, 202, 8, 16, 71, 3, 64, 8, 16, 71, 3, 199, 8, 16, + 71, 3, 203, 8, 16, 71, 3, 187, 8, 16, 71, 3, 70, 8, 16, 71, 3, 204, 8, + 16, 71, 3, 194, 8, 16, 71, 3, 164, 8, 16, 71, 3, 69, 8, 16, 71, 3, 200, + 8, 16, 71, 3, 205, 8, 16, 71, 3, 148, 8, 16, 71, 3, 171, 8, 16, 71, 3, + 183, 8, 16, 71, 3, 78, 8, 16, 71, 3, 198, 8, 16, 71, 3, 209, 8, 16, 71, + 3, 135, 8, 16, 71, 3, 159, 8, 16, 71, 3, 190, 8, 16, 71, 3, 84, 8, 16, + 71, 3, 186, 8, 16, 71, 3, 201, 8, 16, 71, 3, 170, 8, 16, 71, 3, 181, 8, + 16, 71, 3, 202, 8, 77, 5, 64, 8, 77, 5, 199, 8, 77, 5, 187, 8, 77, 5, 70, + 8, 77, 5, 204, 8, 77, 5, 194, 8, 77, 5, 200, 8, 77, 5, 205, 8, 77, 5, + 148, 8, 77, 5, 171, 8, 77, 5, 183, 8, 77, 5, 78, 8, 77, 5, 198, 8, 77, 5, + 209, 8, 77, 5, 159, 8, 77, 5, 190, 8, 77, 5, 84, 8, 77, 5, 186, 8, 77, 5, + 201, 8, 77, 5, 170, 8, 77, 5, 181, 8, 77, 3, 64, 8, 77, 3, 199, 8, 77, 3, + 203, 8, 77, 3, 187, 8, 77, 3, 70, 8, 77, 3, 204, 8, 77, 3, 194, 8, 77, 3, + 164, 8, 77, 3, 69, 8, 77, 3, 200, 8, 77, 3, 205, 8, 77, 3, 148, 8, 77, 3, + 171, 8, 77, 3, 183, 8, 77, 3, 78, 8, 77, 3, 198, 8, 77, 3, 209, 8, 77, 3, + 135, 8, 77, 3, 159, 8, 77, 3, 190, 8, 77, 3, 84, 8, 77, 3, 186, 8, 77, 3, + 201, 8, 77, 3, 170, 8, 77, 3, 181, 8, 77, 3, 202, 8, 120, 5, 64, 8, 120, + 5, 199, 8, 120, 5, 187, 8, 120, 5, 70, 8, 120, 5, 204, 8, 120, 5, 194, 8, + 120, 5, 69, 8, 120, 5, 200, 8, 120, 5, 205, 8, 120, 5, 148, 8, 120, 5, + 171, 8, 120, 5, 78, 8, 120, 5, 159, 8, 120, 5, 190, 8, 120, 5, 84, 8, + 120, 5, 186, 8, 120, 5, 201, 8, 120, 5, 170, 8, 120, 5, 181, 8, 120, 3, + 64, 8, 120, 3, 199, 8, 120, 3, 203, 8, 120, 3, 187, 8, 120, 3, 70, 8, + 120, 3, 204, 8, 120, 3, 194, 8, 120, 3, 164, 8, 120, 3, 69, 8, 120, 3, + 200, 8, 120, 3, 205, 8, 120, 3, 148, 8, 120, 3, 171, 8, 120, 3, 183, 8, + 120, 3, 78, 8, 120, 3, 198, 8, 120, 3, 209, 8, 120, 3, 135, 8, 120, 3, + 159, 8, 120, 3, 190, 8, 120, 3, 84, 8, 120, 3, 186, 8, 120, 3, 201, 8, + 120, 3, 170, 8, 120, 3, 181, 8, 120, 3, 202, 8, 16, 77, 5, 64, 8, 16, 77, + 5, 199, 8, 16, 77, 5, 203, 8, 16, 77, 5, 187, 8, 16, 77, 5, 70, 8, 16, + 77, 5, 204, 8, 16, 77, 5, 194, 8, 16, 77, 5, 164, 8, 16, 77, 5, 69, 8, + 16, 77, 5, 200, 8, 16, 77, 5, 205, 8, 16, 77, 5, 148, 8, 16, 77, 5, 171, + 8, 16, 77, 5, 183, 8, 16, 77, 5, 78, 8, 16, 77, 5, 198, 8, 16, 77, 5, + 209, 8, 16, 77, 5, 135, 8, 16, 77, 5, 159, 8, 16, 77, 5, 190, 8, 16, 77, + 5, 84, 8, 16, 77, 5, 186, 8, 16, 77, 5, 201, 8, 16, 77, 5, 170, 8, 16, + 77, 5, 181, 8, 16, 77, 5, 202, 8, 16, 77, 3, 64, 8, 16, 77, 3, 199, 8, + 16, 77, 3, 203, 8, 16, 77, 3, 187, 8, 16, 77, 3, 70, 8, 16, 77, 3, 204, + 8, 16, 77, 3, 194, 8, 16, 77, 3, 164, 8, 16, 77, 3, 69, 8, 16, 77, 3, + 200, 8, 16, 77, 3, 205, 8, 16, 77, 3, 148, 8, 16, 77, 3, 171, 8, 16, 77, + 3, 183, 8, 16, 77, 3, 78, 8, 16, 77, 3, 198, 8, 16, 77, 3, 209, 8, 16, + 77, 3, 135, 8, 16, 77, 3, 159, 8, 16, 77, 3, 190, 8, 16, 77, 3, 84, 8, + 16, 77, 3, 186, 8, 16, 77, 3, 201, 8, 16, 77, 3, 170, 8, 16, 77, 3, 181, + 8, 16, 77, 3, 202, 8, 23, 5, 64, 8, 23, 5, 199, 8, 23, 5, 203, 8, 23, 5, + 187, 8, 23, 5, 70, 8, 23, 5, 204, 8, 23, 5, 194, 8, 23, 5, 164, 8, 23, 5, + 69, 8, 23, 5, 200, 8, 23, 5, 205, 8, 23, 5, 148, 8, 23, 5, 171, 8, 23, 5, + 183, 8, 23, 5, 78, 8, 23, 5, 198, 8, 23, 5, 209, 8, 23, 5, 135, 8, 23, 5, + 159, 8, 23, 5, 190, 8, 23, 5, 84, 8, 23, 5, 186, 8, 23, 5, 201, 8, 23, 5, + 170, 8, 23, 5, 181, 8, 23, 5, 202, 8, 23, 3, 64, 8, 23, 3, 199, 8, 23, 3, + 203, 8, 23, 3, 187, 8, 23, 3, 70, 8, 23, 3, 204, 8, 23, 3, 194, 8, 23, 3, + 164, 8, 23, 3, 69, 8, 23, 3, 200, 8, 23, 3, 205, 8, 23, 3, 148, 8, 23, 3, + 171, 8, 23, 3, 183, 8, 23, 3, 78, 8, 23, 3, 198, 8, 23, 3, 209, 8, 23, 3, + 135, 8, 23, 3, 159, 8, 23, 3, 190, 8, 23, 3, 84, 8, 23, 3, 186, 8, 23, 3, + 201, 8, 23, 3, 170, 8, 23, 3, 181, 8, 23, 3, 202, 8, 23, 16, 5, 64, 8, + 23, 16, 5, 199, 8, 23, 16, 5, 203, 8, 23, 16, 5, 187, 8, 23, 16, 5, 70, + 8, 23, 16, 5, 204, 8, 23, 16, 5, 194, 8, 23, 16, 5, 164, 8, 23, 16, 5, + 69, 8, 23, 16, 5, 200, 8, 23, 16, 5, 205, 8, 23, 16, 5, 148, 8, 23, 16, + 5, 171, 8, 23, 16, 5, 183, 8, 23, 16, 5, 78, 8, 23, 16, 5, 198, 8, 23, + 16, 5, 209, 8, 23, 16, 5, 135, 8, 23, 16, 5, 159, 8, 23, 16, 5, 190, 8, + 23, 16, 5, 84, 8, 23, 16, 5, 186, 8, 23, 16, 5, 201, 8, 23, 16, 5, 170, + 8, 23, 16, 5, 181, 8, 23, 16, 5, 202, 8, 23, 16, 3, 64, 8, 23, 16, 3, + 199, 8, 23, 16, 3, 203, 8, 23, 16, 3, 187, 8, 23, 16, 3, 70, 8, 23, 16, + 3, 204, 8, 23, 16, 3, 194, 8, 23, 16, 3, 164, 8, 23, 16, 3, 69, 8, 23, + 16, 3, 200, 8, 23, 16, 3, 205, 8, 23, 16, 3, 148, 8, 23, 16, 3, 171, 8, + 23, 16, 3, 183, 8, 23, 16, 3, 78, 8, 23, 16, 3, 198, 8, 23, 16, 3, 209, + 8, 23, 16, 3, 135, 8, 23, 16, 3, 159, 8, 23, 16, 3, 190, 8, 23, 16, 3, + 84, 8, 23, 16, 3, 186, 8, 23, 16, 3, 201, 8, 23, 16, 3, 170, 8, 23, 16, + 3, 181, 8, 23, 16, 3, 202, 8, 23, 21, 5, 64, 8, 23, 21, 5, 199, 8, 23, + 21, 5, 203, 8, 23, 21, 5, 187, 8, 23, 21, 5, 70, 8, 23, 21, 5, 204, 8, + 23, 21, 5, 194, 8, 23, 21, 5, 164, 8, 23, 21, 5, 69, 8, 23, 21, 5, 200, + 8, 23, 21, 5, 205, 8, 23, 21, 5, 148, 8, 23, 21, 5, 171, 8, 23, 21, 5, + 183, 8, 23, 21, 5, 78, 8, 23, 21, 5, 198, 8, 23, 21, 5, 209, 8, 23, 21, + 5, 135, 8, 23, 21, 5, 159, 8, 23, 21, 5, 190, 8, 23, 21, 5, 84, 8, 23, + 21, 5, 186, 8, 23, 21, 5, 201, 8, 23, 21, 5, 170, 8, 23, 21, 5, 181, 8, + 23, 21, 5, 202, 8, 23, 21, 3, 64, 8, 23, 21, 3, 199, 8, 23, 21, 3, 203, + 8, 23, 21, 3, 187, 8, 23, 21, 3, 70, 8, 23, 21, 3, 204, 8, 23, 21, 3, + 194, 8, 23, 21, 3, 164, 8, 23, 21, 3, 69, 8, 23, 21, 3, 200, 8, 23, 21, + 3, 205, 8, 23, 21, 3, 148, 8, 23, 21, 3, 171, 8, 23, 21, 3, 183, 8, 23, + 21, 3, 78, 8, 23, 21, 3, 198, 8, 23, 21, 3, 209, 8, 23, 21, 3, 135, 8, + 23, 21, 3, 159, 8, 23, 21, 3, 190, 8, 23, 21, 3, 84, 8, 23, 21, 3, 186, + 8, 23, 21, 3, 201, 8, 23, 21, 3, 170, 8, 23, 21, 3, 181, 8, 23, 21, 3, + 202, 8, 23, 16, 21, 5, 64, 8, 23, 16, 21, 5, 199, 8, 23, 16, 21, 5, 203, + 8, 23, 16, 21, 5, 187, 8, 23, 16, 21, 5, 70, 8, 23, 16, 21, 5, 204, 8, + 23, 16, 21, 5, 194, 8, 23, 16, 21, 5, 164, 8, 23, 16, 21, 5, 69, 8, 23, + 16, 21, 5, 200, 8, 23, 16, 21, 5, 205, 8, 23, 16, 21, 5, 148, 8, 23, 16, + 21, 5, 171, 8, 23, 16, 21, 5, 183, 8, 23, 16, 21, 5, 78, 8, 23, 16, 21, + 5, 198, 8, 23, 16, 21, 5, 209, 8, 23, 16, 21, 5, 135, 8, 23, 16, 21, 5, + 159, 8, 23, 16, 21, 5, 190, 8, 23, 16, 21, 5, 84, 8, 23, 16, 21, 5, 186, + 8, 23, 16, 21, 5, 201, 8, 23, 16, 21, 5, 170, 8, 23, 16, 21, 5, 181, 8, + 23, 16, 21, 5, 202, 8, 23, 16, 21, 3, 64, 8, 23, 16, 21, 3, 199, 8, 23, + 16, 21, 3, 203, 8, 23, 16, 21, 3, 187, 8, 23, 16, 21, 3, 70, 8, 23, 16, + 21, 3, 204, 8, 23, 16, 21, 3, 194, 8, 23, 16, 21, 3, 164, 8, 23, 16, 21, + 3, 69, 8, 23, 16, 21, 3, 200, 8, 23, 16, 21, 3, 205, 8, 23, 16, 21, 3, + 148, 8, 23, 16, 21, 3, 171, 8, 23, 16, 21, 3, 183, 8, 23, 16, 21, 3, 78, + 8, 23, 16, 21, 3, 198, 8, 23, 16, 21, 3, 209, 8, 23, 16, 21, 3, 135, 8, + 23, 16, 21, 3, 159, 8, 23, 16, 21, 3, 190, 8, 23, 16, 21, 3, 84, 8, 23, + 16, 21, 3, 186, 8, 23, 16, 21, 3, 201, 8, 23, 16, 21, 3, 170, 8, 23, 16, + 21, 3, 181, 8, 23, 16, 21, 3, 202, 8, 140, 5, 64, 8, 140, 5, 199, 8, 140, + 5, 203, 8, 140, 5, 187, 8, 140, 5, 70, 8, 140, 5, 204, 8, 140, 5, 194, 8, + 140, 5, 164, 8, 140, 5, 69, 8, 140, 5, 200, 8, 140, 5, 205, 8, 140, 5, + 148, 8, 140, 5, 171, 8, 140, 5, 183, 8, 140, 5, 78, 8, 140, 5, 198, 8, + 140, 5, 209, 8, 140, 5, 135, 8, 140, 5, 159, 8, 140, 5, 190, 8, 140, 5, + 84, 8, 140, 5, 186, 8, 140, 5, 201, 8, 140, 5, 170, 8, 140, 5, 181, 8, + 140, 5, 202, 8, 140, 3, 64, 8, 140, 3, 199, 8, 140, 3, 203, 8, 140, 3, + 187, 8, 140, 3, 70, 8, 140, 3, 204, 8, 140, 3, 194, 8, 140, 3, 164, 8, + 140, 3, 69, 8, 140, 3, 200, 8, 140, 3, 205, 8, 140, 3, 148, 8, 140, 3, + 171, 8, 140, 3, 183, 8, 140, 3, 78, 8, 140, 3, 198, 8, 140, 3, 209, 8, + 140, 3, 135, 8, 140, 3, 159, 8, 140, 3, 190, 8, 140, 3, 84, 8, 140, 3, + 186, 8, 140, 3, 201, 8, 140, 3, 170, 8, 140, 3, 181, 8, 140, 3, 202, 8, + 16, 5, 242, 79, 8, 16, 5, 244, 198, 8, 16, 5, 242, 75, 8, 16, 5, 242, 87, + 8, 16, 5, 239, 133, 8, 16, 5, 244, 204, 8, 16, 5, 249, 42, 8, 16, 5, 242, + 97, 8, 16, 5, 244, 191, 8, 16, 5, 242, 95, 8, 16, 5, 242, 96, 8, 16, 5, + 253, 114, 8, 16, 5, 253, 112, 8, 16, 5, 253, 135, 8, 16, 5, 239, 137, 8, + 16, 5, 253, 116, 8, 16, 5, 249, 32, 8, 16, 5, 244, 203, 90, 8, 16, 5, + 242, 73, 8, 16, 5, 249, 43, 8, 16, 5, 239, 127, 8, 16, 5, 249, 30, 8, 16, + 5, 249, 23, 8, 16, 5, 249, 31, 8, 16, 5, 242, 72, 8, 16, 242, 130, 8, 16, + 3, 242, 79, 8, 16, 3, 244, 198, 8, 16, 3, 242, 75, 8, 16, 3, 242, 87, 8, + 16, 3, 239, 133, 8, 16, 3, 244, 204, 8, 16, 3, 249, 42, 8, 16, 3, 242, + 97, 8, 16, 3, 244, 191, 8, 16, 3, 242, 95, 8, 16, 3, 242, 96, 8, 16, 3, + 253, 114, 8, 16, 3, 253, 112, 8, 16, 3, 253, 135, 8, 16, 3, 239, 137, 8, + 16, 3, 253, 116, 8, 16, 3, 249, 32, 8, 16, 3, 29, 242, 73, 8, 16, 3, 242, + 73, 8, 16, 3, 249, 43, 8, 16, 3, 239, 127, 8, 16, 3, 249, 30, 8, 16, 3, + 249, 23, 8, 16, 3, 249, 31, 8, 16, 3, 242, 72, 8, 16, 240, 243, 235, 240, + 8, 16, 240, 204, 90, 8, 16, 244, 203, 90, 8, 16, 244, 228, 90, 8, 16, + 253, 212, 90, 8, 16, 253, 168, 90, 8, 16, 254, 224, 90, 8, 21, 5, 242, + 79, 8, 21, 5, 244, 198, 8, 21, 5, 242, 75, 8, 21, 5, 242, 87, 8, 21, 5, + 239, 133, 8, 21, 5, 244, 204, 8, 21, 5, 249, 42, 8, 21, 5, 242, 97, 8, + 21, 5, 244, 191, 8, 21, 5, 242, 95, 8, 21, 5, 242, 96, 8, 21, 5, 253, + 114, 8, 21, 5, 253, 112, 8, 21, 5, 253, 135, 8, 21, 5, 239, 137, 8, 21, + 5, 253, 116, 8, 21, 5, 249, 32, 8, 21, 5, 244, 203, 90, 8, 21, 5, 242, + 73, 8, 21, 5, 249, 43, 8, 21, 5, 239, 127, 8, 21, 5, 249, 30, 8, 21, 5, + 249, 23, 8, 21, 5, 249, 31, 8, 21, 5, 242, 72, 8, 21, 242, 130, 8, 21, 3, + 242, 79, 8, 21, 3, 244, 198, 8, 21, 3, 242, 75, 8, 21, 3, 242, 87, 8, 21, + 3, 239, 133, 8, 21, 3, 244, 204, 8, 21, 3, 249, 42, 8, 21, 3, 242, 97, 8, + 21, 3, 244, 191, 8, 21, 3, 242, 95, 8, 21, 3, 242, 96, 8, 21, 3, 253, + 114, 8, 21, 3, 253, 112, 8, 21, 3, 253, 135, 8, 21, 3, 239, 137, 8, 21, + 3, 253, 116, 8, 21, 3, 249, 32, 8, 21, 3, 29, 242, 73, 8, 21, 3, 242, 73, + 8, 21, 3, 249, 43, 8, 21, 3, 239, 127, 8, 21, 3, 249, 30, 8, 21, 3, 249, + 23, 8, 21, 3, 249, 31, 8, 21, 3, 242, 72, 8, 21, 240, 243, 235, 240, 8, + 21, 240, 204, 90, 8, 21, 244, 203, 90, 8, 21, 244, 228, 90, 8, 21, 253, + 212, 90, 8, 21, 253, 168, 90, 8, 21, 254, 224, 90, 8, 16, 21, 5, 242, 79, + 8, 16, 21, 5, 244, 198, 8, 16, 21, 5, 242, 75, 8, 16, 21, 5, 242, 87, 8, + 16, 21, 5, 239, 133, 8, 16, 21, 5, 244, 204, 8, 16, 21, 5, 249, 42, 8, + 16, 21, 5, 242, 97, 8, 16, 21, 5, 244, 191, 8, 16, 21, 5, 242, 95, 8, 16, + 21, 5, 242, 96, 8, 16, 21, 5, 253, 114, 8, 16, 21, 5, 253, 112, 8, 16, + 21, 5, 253, 135, 8, 16, 21, 5, 239, 137, 8, 16, 21, 5, 253, 116, 8, 16, + 21, 5, 249, 32, 8, 16, 21, 5, 244, 203, 90, 8, 16, 21, 5, 242, 73, 8, 16, + 21, 5, 249, 43, 8, 16, 21, 5, 239, 127, 8, 16, 21, 5, 249, 30, 8, 16, 21, + 5, 249, 23, 8, 16, 21, 5, 249, 31, 8, 16, 21, 5, 242, 72, 8, 16, 21, 242, + 130, 8, 16, 21, 3, 242, 79, 8, 16, 21, 3, 244, 198, 8, 16, 21, 3, 242, + 75, 8, 16, 21, 3, 242, 87, 8, 16, 21, 3, 239, 133, 8, 16, 21, 3, 244, + 204, 8, 16, 21, 3, 249, 42, 8, 16, 21, 3, 242, 97, 8, 16, 21, 3, 244, + 191, 8, 16, 21, 3, 242, 95, 8, 16, 21, 3, 242, 96, 8, 16, 21, 3, 253, + 114, 8, 16, 21, 3, 253, 112, 8, 16, 21, 3, 253, 135, 8, 16, 21, 3, 239, + 137, 8, 16, 21, 3, 253, 116, 8, 16, 21, 3, 249, 32, 8, 16, 21, 3, 29, + 242, 73, 8, 16, 21, 3, 242, 73, 8, 16, 21, 3, 249, 43, 8, 16, 21, 3, 239, + 127, 8, 16, 21, 3, 249, 30, 8, 16, 21, 3, 249, 23, 8, 16, 21, 3, 249, 31, + 8, 16, 21, 3, 242, 72, 8, 16, 21, 240, 243, 235, 240, 8, 16, 21, 240, + 204, 90, 8, 16, 21, 244, 203, 90, 8, 16, 21, 244, 228, 90, 8, 16, 21, + 253, 212, 90, 8, 16, 21, 253, 168, 90, 8, 16, 21, 254, 224, 90, 8, 23, + 16, 5, 242, 79, 8, 23, 16, 5, 244, 198, 8, 23, 16, 5, 242, 75, 8, 23, 16, + 5, 242, 87, 8, 23, 16, 5, 239, 133, 8, 23, 16, 5, 244, 204, 8, 23, 16, 5, + 249, 42, 8, 23, 16, 5, 242, 97, 8, 23, 16, 5, 244, 191, 8, 23, 16, 5, + 242, 95, 8, 23, 16, 5, 242, 96, 8, 23, 16, 5, 253, 114, 8, 23, 16, 5, + 253, 112, 8, 23, 16, 5, 253, 135, 8, 23, 16, 5, 239, 137, 8, 23, 16, 5, + 253, 116, 8, 23, 16, 5, 249, 32, 8, 23, 16, 5, 244, 203, 90, 8, 23, 16, + 5, 242, 73, 8, 23, 16, 5, 249, 43, 8, 23, 16, 5, 239, 127, 8, 23, 16, 5, + 249, 30, 8, 23, 16, 5, 249, 23, 8, 23, 16, 5, 249, 31, 8, 23, 16, 5, 242, + 72, 8, 23, 16, 242, 130, 8, 23, 16, 3, 242, 79, 8, 23, 16, 3, 244, 198, + 8, 23, 16, 3, 242, 75, 8, 23, 16, 3, 242, 87, 8, 23, 16, 3, 239, 133, 8, + 23, 16, 3, 244, 204, 8, 23, 16, 3, 249, 42, 8, 23, 16, 3, 242, 97, 8, 23, + 16, 3, 244, 191, 8, 23, 16, 3, 242, 95, 8, 23, 16, 3, 242, 96, 8, 23, 16, + 3, 253, 114, 8, 23, 16, 3, 253, 112, 8, 23, 16, 3, 253, 135, 8, 23, 16, + 3, 239, 137, 8, 23, 16, 3, 253, 116, 8, 23, 16, 3, 249, 32, 8, 23, 16, 3, + 29, 242, 73, 8, 23, 16, 3, 242, 73, 8, 23, 16, 3, 249, 43, 8, 23, 16, 3, + 239, 127, 8, 23, 16, 3, 249, 30, 8, 23, 16, 3, 249, 23, 8, 23, 16, 3, + 249, 31, 8, 23, 16, 3, 242, 72, 8, 23, 16, 240, 243, 235, 240, 8, 23, 16, + 240, 204, 90, 8, 23, 16, 244, 203, 90, 8, 23, 16, 244, 228, 90, 8, 23, + 16, 253, 212, 90, 8, 23, 16, 253, 168, 90, 8, 23, 16, 254, 224, 90, 8, + 23, 16, 21, 5, 242, 79, 8, 23, 16, 21, 5, 244, 198, 8, 23, 16, 21, 5, + 242, 75, 8, 23, 16, 21, 5, 242, 87, 8, 23, 16, 21, 5, 239, 133, 8, 23, + 16, 21, 5, 244, 204, 8, 23, 16, 21, 5, 249, 42, 8, 23, 16, 21, 5, 242, + 97, 8, 23, 16, 21, 5, 244, 191, 8, 23, 16, 21, 5, 242, 95, 8, 23, 16, 21, + 5, 242, 96, 8, 23, 16, 21, 5, 253, 114, 8, 23, 16, 21, 5, 253, 112, 8, + 23, 16, 21, 5, 253, 135, 8, 23, 16, 21, 5, 239, 137, 8, 23, 16, 21, 5, + 253, 116, 8, 23, 16, 21, 5, 249, 32, 8, 23, 16, 21, 5, 244, 203, 90, 8, + 23, 16, 21, 5, 242, 73, 8, 23, 16, 21, 5, 249, 43, 8, 23, 16, 21, 5, 239, + 127, 8, 23, 16, 21, 5, 249, 30, 8, 23, 16, 21, 5, 249, 23, 8, 23, 16, 21, + 5, 249, 31, 8, 23, 16, 21, 5, 242, 72, 8, 23, 16, 21, 242, 130, 8, 23, + 16, 21, 3, 242, 79, 8, 23, 16, 21, 3, 244, 198, 8, 23, 16, 21, 3, 242, + 75, 8, 23, 16, 21, 3, 242, 87, 8, 23, 16, 21, 3, 239, 133, 8, 23, 16, 21, + 3, 244, 204, 8, 23, 16, 21, 3, 249, 42, 8, 23, 16, 21, 3, 242, 97, 8, 23, + 16, 21, 3, 244, 191, 8, 23, 16, 21, 3, 242, 95, 8, 23, 16, 21, 3, 242, + 96, 8, 23, 16, 21, 3, 253, 114, 8, 23, 16, 21, 3, 253, 112, 8, 23, 16, + 21, 3, 253, 135, 8, 23, 16, 21, 3, 239, 137, 8, 23, 16, 21, 3, 253, 116, + 8, 23, 16, 21, 3, 249, 32, 8, 23, 16, 21, 3, 29, 242, 73, 8, 23, 16, 21, + 3, 242, 73, 8, 23, 16, 21, 3, 249, 43, 8, 23, 16, 21, 3, 239, 127, 8, 23, + 16, 21, 3, 249, 30, 8, 23, 16, 21, 3, 249, 23, 8, 23, 16, 21, 3, 249, 31, + 8, 23, 16, 21, 3, 242, 72, 8, 23, 16, 21, 240, 243, 235, 240, 8, 23, 16, + 21, 240, 204, 90, 8, 23, 16, 21, 244, 203, 90, 8, 23, 16, 21, 244, 228, + 90, 8, 23, 16, 21, 253, 212, 90, 8, 23, 16, 21, 253, 168, 90, 8, 23, 16, + 21, 254, 224, 90, 8, 16, 27, 244, 173, 8, 16, 27, 121, 8, 16, 27, 114, 8, + 16, 27, 153, 8, 16, 27, 163, 8, 16, 27, 168, 8, 16, 27, 169, 8, 16, 27, + 179, 8, 16, 27, 176, 8, 16, 27, 178, 8, 120, 27, 244, 173, 8, 120, 27, + 121, 8, 120, 27, 114, 8, 120, 27, 153, 8, 120, 27, 163, 8, 120, 27, 168, + 8, 120, 27, 169, 8, 120, 27, 179, 8, 120, 27, 176, 8, 120, 27, 178, 8, + 23, 27, 244, 173, 8, 23, 27, 121, 8, 23, 27, 114, 8, 23, 27, 153, 8, 23, + 27, 163, 8, 23, 27, 168, 8, 23, 27, 169, 8, 23, 27, 179, 8, 23, 27, 176, + 8, 23, 27, 178, 8, 23, 16, 27, 244, 173, 8, 23, 16, 27, 121, 8, 23, 16, + 27, 114, 8, 23, 16, 27, 153, 8, 23, 16, 27, 163, 8, 23, 16, 27, 168, 8, + 23, 16, 27, 169, 8, 23, 16, 27, 179, 8, 23, 16, 27, 176, 8, 23, 16, 27, + 178, 8, 140, 27, 244, 173, 8, 140, 27, 121, 8, 140, 27, 114, 8, 140, 27, + 153, 8, 140, 27, 163, 8, 140, 27, 168, 8, 140, 27, 169, 8, 140, 27, 179, + 8, 140, 27, 176, 8, 140, 27, 178, 7, 11, 232, 21, 7, 11, 232, 22, 7, 11, + 232, 23, 7, 11, 232, 24, 7, 11, 232, 25, 7, 11, 232, 26, 7, 11, 232, 27, + 7, 11, 232, 28, 7, 11, 232, 29, 7, 11, 232, 30, 7, 11, 232, 31, 7, 11, + 232, 32, 7, 11, 232, 33, 7, 11, 232, 34, 7, 11, 232, 35, 7, 11, 232, 36, + 7, 11, 232, 37, 7, 11, 232, 38, 7, 11, 232, 39, 7, 11, 232, 40, 7, 11, + 232, 41, 7, 11, 232, 42, 7, 11, 232, 43, 7, 11, 232, 44, 7, 11, 232, 45, + 7, 11, 232, 46, 7, 11, 232, 47, 7, 11, 232, 48, 7, 11, 232, 49, 7, 11, + 232, 50, 7, 11, 232, 51, 7, 11, 232, 52, 7, 11, 232, 53, 7, 11, 232, 54, + 7, 11, 232, 55, 7, 11, 232, 56, 7, 11, 232, 57, 7, 11, 232, 58, 7, 11, + 232, 59, 7, 11, 232, 60, 7, 11, 232, 61, 7, 11, 232, 62, 7, 11, 232, 63, + 7, 11, 232, 64, 7, 11, 232, 65, 7, 11, 232, 66, 7, 11, 232, 67, 7, 11, + 232, 68, 7, 11, 232, 69, 7, 11, 232, 70, 7, 11, 232, 71, 7, 11, 232, 72, + 7, 11, 232, 73, 7, 11, 232, 74, 7, 11, 232, 75, 7, 11, 232, 76, 7, 11, + 232, 77, 7, 11, 232, 78, 7, 11, 232, 79, 7, 11, 232, 80, 7, 11, 232, 81, + 7, 11, 232, 82, 7, 11, 232, 83, 7, 11, 232, 84, 7, 11, 232, 85, 7, 11, + 232, 86, 7, 11, 232, 87, 7, 11, 232, 88, 7, 11, 232, 89, 7, 11, 232, 90, + 7, 11, 232, 91, 7, 11, 232, 92, 7, 11, 232, 93, 7, 11, 232, 94, 7, 11, + 232, 95, 7, 11, 232, 96, 7, 11, 232, 97, 7, 11, 232, 98, 7, 11, 232, 99, + 7, 11, 232, 100, 7, 11, 232, 101, 7, 11, 232, 102, 7, 11, 232, 103, 7, + 11, 232, 104, 7, 11, 232, 105, 7, 11, 232, 106, 7, 11, 232, 107, 7, 11, + 232, 108, 7, 11, 232, 109, 7, 11, 232, 110, 7, 11, 232, 111, 7, 11, 232, + 112, 7, 11, 232, 113, 7, 11, 232, 114, 7, 11, 232, 115, 7, 11, 232, 116, + 7, 11, 232, 117, 7, 11, 232, 118, 7, 11, 232, 119, 7, 11, 232, 120, 7, + 11, 232, 121, 7, 11, 232, 122, 7, 11, 232, 123, 7, 11, 232, 124, 7, 11, + 232, 125, 7, 11, 232, 126, 7, 11, 232, 127, 7, 11, 232, 128, 7, 11, 232, + 129, 7, 11, 232, 130, 7, 11, 232, 131, 7, 11, 232, 132, 7, 11, 232, 133, + 7, 11, 232, 134, 7, 11, 232, 135, 7, 11, 232, 136, 7, 11, 232, 137, 7, + 11, 232, 138, 7, 11, 232, 139, 7, 11, 232, 140, 7, 11, 232, 141, 7, 11, + 232, 142, 7, 11, 232, 143, 7, 11, 232, 144, 7, 11, 232, 145, 7, 11, 232, + 146, 7, 11, 232, 147, 7, 11, 232, 148, 7, 11, 232, 149, 7, 11, 232, 150, + 7, 11, 232, 151, 7, 11, 232, 152, 7, 11, 232, 153, 7, 11, 232, 154, 7, + 11, 232, 155, 7, 11, 232, 156, 7, 11, 232, 157, 7, 11, 232, 158, 7, 11, + 232, 159, 7, 11, 232, 160, 7, 11, 232, 161, 7, 11, 232, 162, 7, 11, 232, + 163, 7, 11, 232, 164, 7, 11, 232, 165, 7, 11, 232, 166, 7, 11, 232, 167, + 7, 11, 232, 168, 7, 11, 232, 169, 7, 11, 232, 170, 7, 11, 232, 171, 7, + 11, 232, 172, 7, 11, 232, 173, 7, 11, 232, 174, 7, 11, 232, 175, 7, 11, + 232, 176, 7, 11, 232, 177, 7, 11, 232, 178, 7, 11, 232, 179, 7, 11, 232, + 180, 7, 11, 232, 181, 7, 11, 232, 182, 7, 11, 232, 183, 7, 11, 232, 184, + 7, 11, 232, 185, 7, 11, 232, 186, 7, 11, 232, 187, 7, 11, 232, 188, 7, + 11, 232, 189, 7, 11, 232, 190, 7, 11, 232, 191, 7, 11, 232, 192, 7, 11, + 232, 193, 7, 11, 232, 194, 7, 11, 232, 195, 7, 11, 232, 196, 7, 11, 232, + 197, 7, 11, 232, 198, 7, 11, 232, 199, 7, 11, 232, 200, 7, 11, 232, 201, + 7, 11, 232, 202, 7, 11, 232, 203, 7, 11, 232, 204, 7, 11, 232, 205, 7, + 11, 232, 206, 7, 11, 232, 207, 7, 11, 232, 208, 7, 11, 232, 209, 7, 11, + 232, 210, 7, 11, 232, 211, 7, 11, 232, 212, 7, 11, 232, 213, 7, 11, 232, + 214, 7, 11, 232, 215, 7, 11, 232, 216, 7, 11, 232, 217, 7, 11, 232, 218, + 7, 11, 232, 219, 7, 11, 232, 220, 7, 11, 232, 221, 7, 11, 232, 222, 7, + 11, 232, 223, 7, 11, 232, 224, 7, 11, 232, 225, 7, 11, 232, 226, 7, 11, + 232, 227, 7, 11, 232, 228, 7, 11, 232, 229, 7, 11, 232, 230, 7, 11, 232, + 231, 7, 11, 232, 232, 7, 11, 232, 233, 7, 11, 232, 234, 7, 11, 232, 235, + 7, 11, 232, 236, 7, 11, 232, 237, 7, 11, 232, 238, 7, 11, 232, 239, 7, + 11, 232, 240, 7, 11, 232, 241, 7, 11, 232, 242, 7, 11, 232, 243, 7, 11, + 232, 244, 7, 11, 232, 245, 7, 11, 232, 246, 7, 11, 232, 247, 7, 11, 232, + 248, 7, 11, 232, 249, 7, 11, 232, 250, 7, 11, 232, 251, 7, 11, 232, 252, + 7, 11, 232, 253, 7, 11, 232, 254, 7, 11, 232, 255, 7, 11, 233, 0, 7, 11, + 233, 1, 7, 11, 233, 2, 7, 11, 233, 3, 7, 11, 233, 4, 7, 11, 233, 5, 7, + 11, 233, 6, 7, 11, 233, 7, 7, 11, 233, 8, 7, 11, 233, 9, 7, 11, 233, 10, + 7, 11, 233, 11, 7, 11, 233, 12, 7, 11, 233, 13, 7, 11, 233, 14, 7, 11, + 233, 15, 7, 11, 233, 16, 7, 11, 233, 17, 7, 11, 233, 18, 7, 11, 233, 19, + 7, 11, 233, 20, 7, 11, 233, 21, 7, 11, 233, 22, 7, 11, 233, 23, 7, 11, + 233, 24, 7, 11, 233, 25, 7, 11, 233, 26, 7, 11, 233, 27, 7, 11, 233, 28, + 7, 11, 233, 29, 7, 11, 233, 30, 7, 11, 233, 31, 7, 11, 233, 32, 7, 11, + 233, 33, 7, 11, 233, 34, 7, 11, 233, 35, 7, 11, 233, 36, 7, 11, 233, 37, + 7, 11, 233, 38, 7, 11, 233, 39, 7, 11, 233, 40, 7, 11, 233, 41, 7, 11, + 233, 42, 7, 11, 233, 43, 7, 11, 233, 44, 7, 11, 233, 45, 7, 11, 233, 46, + 7, 11, 233, 47, 7, 11, 233, 48, 7, 11, 233, 49, 7, 11, 233, 50, 7, 11, + 233, 51, 7, 11, 233, 52, 7, 11, 233, 53, 7, 11, 233, 54, 7, 11, 233, 55, + 7, 11, 233, 56, 7, 11, 233, 57, 7, 11, 233, 58, 7, 11, 233, 59, 7, 11, + 233, 60, 7, 11, 233, 61, 7, 11, 233, 62, 7, 11, 233, 63, 7, 11, 233, 64, + 7, 11, 233, 65, 7, 11, 233, 66, 7, 11, 233, 67, 7, 11, 233, 68, 7, 11, + 233, 69, 7, 11, 233, 70, 7, 11, 233, 71, 7, 11, 233, 72, 7, 11, 233, 73, + 7, 11, 233, 74, 7, 11, 233, 75, 7, 11, 233, 76, 7, 11, 233, 77, 7, 11, + 233, 78, 7, 11, 233, 79, 7, 11, 233, 80, 7, 11, 233, 81, 7, 11, 233, 82, + 7, 11, 233, 83, 7, 11, 233, 84, 7, 11, 233, 85, 7, 11, 233, 86, 7, 11, + 233, 87, 7, 11, 233, 88, 7, 11, 233, 89, 7, 11, 233, 90, 7, 11, 233, 91, + 7, 11, 233, 92, 7, 11, 233, 93, 7, 11, 233, 94, 7, 11, 233, 95, 7, 11, + 233, 96, 7, 11, 233, 97, 7, 11, 233, 98, 7, 11, 233, 99, 7, 11, 233, 100, + 7, 11, 233, 101, 7, 11, 233, 102, 7, 11, 233, 103, 7, 11, 233, 104, 7, + 11, 233, 105, 7, 11, 233, 106, 7, 11, 233, 107, 7, 11, 233, 108, 7, 11, + 233, 109, 7, 11, 233, 110, 7, 11, 233, 111, 7, 11, 233, 112, 7, 11, 233, + 113, 7, 11, 233, 114, 7, 11, 233, 115, 7, 11, 233, 116, 7, 11, 233, 117, + 7, 11, 233, 118, 7, 11, 233, 119, 7, 11, 233, 120, 7, 11, 233, 121, 7, + 11, 233, 122, 7, 11, 233, 123, 7, 11, 233, 124, 7, 11, 233, 125, 7, 11, + 233, 126, 7, 11, 233, 127, 7, 11, 233, 128, 7, 11, 233, 129, 7, 11, 233, + 130, 7, 11, 233, 131, 7, 11, 233, 132, 7, 11, 233, 133, 7, 11, 233, 134, + 7, 11, 233, 135, 7, 11, 233, 136, 7, 11, 233, 137, 7, 11, 233, 138, 7, + 11, 233, 139, 7, 11, 233, 140, 7, 11, 233, 141, 7, 11, 233, 142, 7, 11, + 233, 143, 7, 11, 233, 144, 7, 11, 233, 145, 7, 11, 233, 146, 7, 11, 233, + 147, 7, 11, 233, 148, 7, 11, 233, 149, 7, 11, 233, 150, 7, 11, 233, 151, + 7, 11, 233, 152, 7, 11, 233, 153, 7, 11, 233, 154, 7, 11, 233, 155, 7, + 11, 233, 156, 7, 11, 233, 157, 7, 11, 233, 158, 7, 11, 233, 159, 7, 11, + 233, 160, 7, 11, 233, 161, 7, 11, 233, 162, 7, 11, 233, 163, 7, 11, 233, + 164, 7, 11, 233, 165, 7, 11, 233, 166, 7, 11, 233, 167, 7, 11, 233, 168, + 7, 11, 233, 169, 7, 11, 233, 170, 7, 11, 233, 171, 7, 11, 233, 172, 7, + 11, 233, 173, 7, 11, 233, 174, 7, 11, 233, 175, 7, 11, 233, 176, 7, 11, + 233, 177, 7, 11, 233, 178, 7, 11, 233, 179, 7, 11, 233, 180, 7, 11, 233, + 181, 7, 11, 233, 182, 7, 11, 233, 183, 7, 11, 233, 184, 7, 11, 233, 185, + 7, 11, 233, 186, 7, 11, 233, 187, 7, 11, 233, 188, 7, 11, 233, 189, 7, + 11, 233, 190, 7, 11, 233, 191, 7, 11, 233, 192, 7, 11, 233, 193, 7, 11, + 233, 194, 7, 11, 233, 195, 7, 11, 233, 196, 7, 11, 233, 197, 7, 11, 233, + 198, 7, 11, 233, 199, 7, 11, 233, 200, 7, 11, 233, 201, 7, 11, 233, 202, + 7, 11, 233, 203, 7, 11, 233, 204, 7, 11, 233, 205, 7, 11, 233, 206, 7, + 11, 233, 207, 7, 11, 233, 208, 7, 11, 233, 209, 7, 11, 233, 210, 7, 11, + 233, 211, 7, 11, 233, 212, 7, 11, 233, 213, 7, 11, 233, 214, 7, 11, 233, + 215, 7, 11, 233, 216, 7, 11, 233, 217, 7, 11, 233, 218, 7, 11, 233, 219, + 7, 11, 233, 220, 7, 11, 233, 221, 7, 11, 233, 222, 7, 11, 233, 223, 7, + 11, 233, 224, 7, 11, 233, 225, 7, 11, 233, 226, 7, 11, 233, 227, 7, 11, + 233, 228, 7, 11, 233, 229, 7, 11, 233, 230, 7, 11, 233, 231, 7, 11, 233, + 232, 7, 11, 233, 233, 7, 11, 233, 234, 7, 11, 233, 235, 7, 11, 233, 236, + 7, 11, 233, 237, 7, 11, 233, 238, 7, 11, 233, 239, 7, 11, 233, 240, 7, + 11, 233, 241, 7, 11, 233, 242, 7, 11, 233, 243, 7, 11, 233, 244, 7, 11, + 233, 245, 7, 11, 233, 246, 7, 11, 233, 247, 7, 11, 233, 248, 7, 11, 233, + 249, 7, 11, 233, 250, 7, 11, 233, 251, 7, 11, 233, 252, 7, 11, 233, 253, + 7, 11, 233, 254, 7, 11, 233, 255, 7, 11, 234, 0, 7, 11, 234, 1, 7, 11, + 234, 2, 7, 11, 234, 3, 7, 11, 234, 4, 7, 11, 234, 5, 7, 11, 234, 6, 7, + 11, 234, 7, 7, 11, 234, 8, 7, 11, 234, 9, 7, 11, 234, 10, 7, 11, 234, 11, + 7, 11, 234, 12, 7, 11, 234, 13, 7, 11, 234, 14, 7, 11, 234, 15, 7, 11, + 234, 16, 7, 11, 234, 17, 7, 11, 234, 18, 7, 11, 234, 19, 7, 11, 234, 20, + 7, 11, 234, 21, 7, 11, 234, 22, 7, 11, 234, 23, 7, 11, 234, 24, 7, 11, + 234, 25, 7, 11, 234, 26, 7, 11, 234, 27, 7, 11, 234, 28, 7, 11, 234, 29, + 7, 11, 234, 30, 7, 11, 234, 31, 7, 11, 234, 32, 7, 11, 234, 33, 7, 11, + 234, 34, 7, 11, 234, 35, 7, 11, 234, 36, 7, 11, 234, 37, 7, 11, 234, 38, + 7, 11, 234, 39, 7, 11, 234, 40, 7, 11, 234, 41, 7, 11, 234, 42, 7, 11, + 234, 43, 7, 11, 234, 44, 7, 11, 234, 45, 7, 11, 234, 46, 7, 11, 234, 47, + 7, 11, 234, 48, 7, 11, 234, 49, 7, 11, 234, 50, 240, 104, 250, 81, 85, + 242, 69, 85, 236, 186, 65, 85, 238, 76, 65, 85, 83, 57, 85, 242, 158, 57, + 85, 240, 248, 57, 85, 237, 124, 85, 236, 190, 85, 36, 236, 47, 85, 37, + 236, 47, 85, 238, 77, 85, 249, 4, 57, 85, 242, 84, 85, 235, 243, 85, 213, + 189, 85, 239, 144, 85, 27, 244, 173, 85, 27, 121, 85, 27, 114, 85, 27, + 153, 85, 27, 163, 85, 27, 168, 85, 27, 169, 85, 27, 179, 85, 27, 176, 85, + 27, 178, 85, 242, 81, 85, 237, 123, 85, 206, 57, 85, 242, 61, 57, 85, + 196, 57, 85, 239, 126, 65, 85, 237, 127, 253, 220, 85, 9, 5, 1, 64, 85, + 9, 5, 1, 199, 85, 9, 5, 1, 203, 85, 9, 5, 1, 187, 85, 9, 5, 1, 70, 85, 9, + 5, 1, 204, 85, 9, 5, 1, 194, 85, 9, 5, 1, 164, 85, 9, 5, 1, 69, 85, 9, 5, + 1, 200, 85, 9, 5, 1, 205, 85, 9, 5, 1, 148, 85, 9, 5, 1, 171, 85, 9, 5, + 1, 183, 85, 9, 5, 1, 78, 85, 9, 5, 1, 198, 85, 9, 5, 1, 209, 85, 9, 5, 1, + 135, 85, 9, 5, 1, 159, 85, 9, 5, 1, 190, 85, 9, 5, 1, 84, 85, 9, 5, 1, + 186, 85, 9, 5, 1, 201, 85, 9, 5, 1, 170, 85, 9, 5, 1, 181, 85, 9, 5, 1, + 202, 85, 36, 30, 110, 85, 240, 219, 239, 144, 85, 37, 30, 110, 85, 157, + 240, 203, 85, 253, 113, 244, 176, 85, 244, 201, 240, 203, 85, 9, 3, 1, + 64, 85, 9, 3, 1, 199, 85, 9, 3, 1, 203, 85, 9, 3, 1, 187, 85, 9, 3, 1, + 70, 85, 9, 3, 1, 204, 85, 9, 3, 1, 194, 85, 9, 3, 1, 164, 85, 9, 3, 1, + 69, 85, 9, 3, 1, 200, 85, 9, 3, 1, 205, 85, 9, 3, 1, 148, 85, 9, 3, 1, + 171, 85, 9, 3, 1, 183, 85, 9, 3, 1, 78, 85, 9, 3, 1, 198, 85, 9, 3, 1, + 209, 85, 9, 3, 1, 135, 85, 9, 3, 1, 159, 85, 9, 3, 1, 190, 85, 9, 3, 1, + 84, 85, 9, 3, 1, 186, 85, 9, 3, 1, 201, 85, 9, 3, 1, 170, 85, 9, 3, 1, + 181, 85, 9, 3, 1, 202, 85, 36, 244, 175, 110, 85, 55, 244, 176, 85, 37, + 244, 175, 110, 85, 180, 243, 56, 250, 81, +}; + +static unsigned char phrasebook_offset1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 8, 8, 8, 8, 8, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 8, 8, 8, 38, 39, 40, 41, 42, 43, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 44, 45, 46, 47, 48, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 49, 50, + 51, 52, 53, 54, 55, 8, 8, 8, 56, 57, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 58, + 59, 8, 8, 60, 61, 62, 63, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 64, 65, 66, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 67, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, +}; + +static unsigned int phrasebook_offset2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 6, 9, 11, 14, 17, 19, 21, 24, 27, 29, 32, + 34, 36, 38, 40, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 70, + 72, 75, 79, 83, 87, 91, 95, 99, 103, 107, 111, 115, 119, 123, 127, 131, + 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179, 183, 186, + 190, 193, 196, 200, 204, 208, 212, 216, 220, 224, 228, 232, 236, 240, + 244, 248, 252, 256, 260, 264, 268, 272, 276, 280, 284, 288, 292, 296, + 300, 304, 308, 312, 315, 319, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 320, 324, 329, + 332, 335, 338, 341, 344, 347, 348, 351, 357, 364, 366, 370, 373, 374, + 377, 380, 383, 386, 390, 393, 396, 399, 401, 404, 410, 417, 425, 433, + 441, 446, 452, 458, 465, 471, 477, 485, 490, 498, 504, 510, 517, 523, + 529, 535, 542, 548, 553, 560, 566, 572, 579, 585, 591, 594, 600, 606, + 612, 619, 625, 632, 637, 643, 649, 655, 662, 668, 674, 682, 687, 695, + 701, 707, 714, 720, 726, 732, 739, 745, 750, 757, 763, 769, 776, 782, + 788, 791, 797, 803, 809, 816, 822, 829, 834, 841, 847, 853, 859, 865, + 872, 879, 886, 893, 901, 909, 917, 925, 932, 939, 946, 953, 960, 967, + 973, 979, 985, 991, 998, 1005, 1012, 1019, 1025, 1031, 1039, 1047, 1054, + 1061, 1069, 1077, 1085, 1093, 1101, 1109, 1116, 1123, 1129, 1135, 1141, + 1147, 1153, 1159, 1166, 1173, 1180, 1186, 1191, 1196, 1204, 1212, 1220, + 1228, 1233, 1240, 1247, 1255, 1263, 1270, 1277, 1286, 1295, 1302, 1309, + 1316, 1323, 1331, 1339, 1346, 1353, 1364, 1369, 1374, 1380, 1386, 1392, + 1398, 1405, 1412, 1417, 1422, 1429, 1436, 1444, 1452, 1459, 1466, 1473, + 1480, 1488, 1496, 1504, 1512, 1519, 1526, 1534, 1542, 1549, 1556, 1563, + 1570, 1576, 1582, 1588, 1594, 1600, 1606, 1614, 1622, 1629, 1636, 1643, + 1650, 1658, 1666, 1674, 1682, 1689, 1696, 1703, 1711, 1719, 1726, 1733, + 1738, 1745, 1752, 1760, 1768, 1774, 1780, 1786, 1793, 1800, 1806, 1813, + 1821, 1829, 1836, 1841, 1846, 1852, 1859, 1866, 1873, 1878, 1883, 1888, + 1894, 1901, 1908, 1915, 1922, 1928, 1936, 1946, 1954, 1961, 1968, 1973, + 1978, 1985, 1992, 1996, 2002, 2008, 2013, 2020, 2029, 2036, 2043, 2052, + 2059, 2066, 2071, 2078, 2085, 2092, 2099, 2106, 2111, 2118, 2125, 2133, + 2138, 2144, 2150, 2160, 2164, 2170, 2176, 2182, 2188, 2195, 2207, 2214, + 2219, 2228, 2233, 2238, 2247, 2252, 2258, 2264, 2270, 2276, 2282, 2288, + 2294, 2300, 2309, 2318, 2327, 2336, 2345, 2354, 2363, 2372, 2378, 2387, + 2396, 2405, 2414, 2421, 2428, 2435, 2442, 2449, 2456, 2463, 2470, 2477, + 2484, 2493, 2502, 2509, 2516, 2523, 2528, 2537, 2542, 2549, 2556, 2561, + 2566, 2573, 2580, 2590, 2600, 2607, 2614, 2623, 2632, 2639, 2646, 2654, + 2662, 2669, 2676, 2684, 2692, 2699, 2706, 2714, 2722, 2729, 2736, 2744, + 2752, 2760, 2768, 2777, 2786, 2793, 2800, 2808, 2816, 2825, 2834, 2843, + 2852, 2857, 2862, 2869, 2876, 0, 2886, 2891, 2896, 2903, 2910, 2917, + 2924, 2931, 2938, 2947, 2956, 2964, 2972, 2979, 2986, 2995, 3004, 3011, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3018, 3024, 3029, 3036, 3043, 3049, 3057, 3065, 3072, 3077, + 3082, 3089, 3095, 3102, 3111, 3120, 3129, 3136, 3141, 3146, 3151, 3158, + 3164, 3171, 3178, 3184, 3189, 3194, 3203, 3211, 3220, 3225, 3231, 3242, + 3249, 3257, 3266, 3271, 3277, 3283, 3290, 3295, 3301, 3312, 3321, 3330, + 3338, 3346, 3355, 3360, 3367, 3374, 3379, 3391, 3399, 3407, 3413, 3422, + 3427, 3432, 3439, 3445, 3451, 3457, 3462, 3471, 3479, 3484, 3492, 3497, + 3505, 3512, 3517, 3523, 3528, 3536, 3544, 3549, 3557, 3563, 3568, 3575, + 3583, 3592, 3599, 3606, 3616, 3623, 3630, 3640, 3647, 3654, 3661, 3667, + 0, 0, 3673, 3677, 3684, 3688, 3692, 3698, 3707, 3714, 3718, 3722, 3726, + 3731, 3737, 3741, 3746, 3752, 3758, 3763, 3769, 3774, 3779, 3784, 3789, + 3794, 3795, 3800, 3803, 3809, 3815, 3822, 3827, 3835, 3843, 3849, 3856, + 3864, 3872, 3877, 3882, 3887, 3892, 3893, 3895, 3898, 3900, 3902, 3907, + 3912, 3918, 3923, 3927, 3931, 3935, 3942, 3949, 3956, 3963, 3970, 3977, + 3986, 3995, 3999, 4003, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4008, 4013, 4018, 4022, 4024, 4026, 4029, 4031, 4034, 4036, 4040, + 4044, 4050, 4052, 4056, 4061, 4067, 4070, 4074, 4080, 4084, 4089, 4094, + 4099, 4104, 4109, 4114, 4118, 4121, 4127, 4132, 4137, 4142, 4147, 4153, + 4159, 4162, 4166, 4170, 4174, 4177, 4180, 4184, 4188, 4195, 4199, 4203, + 4207, 4213, 4216, 4220, 4225, 4231, 4235, 4241, 4247, 4253, 4259, 4265, + 4271, 4274, 4278, 4282, 4285, 4289, 4295, 4301, 4305, 4309, 4315, 4318, + 4322, 4327, 4332, 4336, 4340, 4344, 4350, 4355, 4359, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4364, 4367, 4372, 4377, 4382, 4387, 4392, + 4397, 4402, 4407, 4412, 4417, 4422, 4427, 4432, 4437, 0, 0, 0, 0, 4442, + 4445, 0, 0, 0, 0, 4449, 0, 0, 0, 4451, 0, 0, 0, 0, 0, 4455, 4458, 4463, + 4470, 4475, 4483, 4490, 0, 4497, 0, 4505, 4513, 4520, 4530, 4535, 4540, + 4545, 4550, 4555, 4560, 4565, 4570, 4575, 4580, 4585, 4590, 4595, 4600, + 4605, 4610, 0, 4615, 4620, 4625, 4630, 4635, 4640, 4645, 4650, 4657, + 4665, 4672, 4680, 4687, 4694, 4705, 4710, 4715, 4720, 4725, 4730, 4735, + 4740, 4745, 4750, 4755, 4760, 4765, 4770, 4775, 4780, 4785, 4790, 4796, + 4801, 4806, 4811, 4816, 4821, 4826, 4831, 4838, 4846, 4854, 4862, 0, + 4869, 4873, 4877, 4884, 4894, 4904, 4908, 4912, 4916, 4922, 4929, 4933, + 4938, 4942, 4947, 4951, 4956, 4960, 4965, 4971, 4977, 4983, 4989, 4995, + 5001, 5007, 5013, 5019, 5025, 5031, 5037, 5043, 5049, 5053, 5057, 5063, + 5067, 5072, 5078, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5085, 5092, 5097, 5102, + 5107, 5114, 5119, 5125, 5130, 5135, 5140, 5145, 5150, 5155, 5161, 5166, + 5171, 5175, 5180, 5185, 5190, 5195, 5200, 5205, 5210, 5214, 5219, 5223, + 5228, 5233, 5238, 5242, 5247, 5252, 5257, 5262, 5266, 5271, 5276, 5281, + 5286, 5291, 5296, 5302, 5307, 5313, 5317, 5322, 5326, 5330, 5335, 5340, + 5345, 5350, 5355, 5360, 5365, 5369, 5374, 5378, 5383, 5388, 5393, 5397, + 5402, 5407, 5412, 5417, 5421, 5426, 5431, 5436, 5441, 5446, 5451, 5457, + 5462, 5468, 5472, 5477, 5481, 5488, 5493, 5498, 5503, 5510, 5515, 5521, + 5526, 5531, 5536, 5541, 5546, 5551, 5557, 5562, 5567, 5572, 5577, 5582, + 5587, 5593, 5599, 5606, 5613, 5622, 5631, 5638, 5645, 5654, 5663, 5668, + 5673, 5678, 5683, 5688, 5693, 5698, 5703, 5714, 5725, 5730, 5735, 5742, + 5749, 5756, 5763, 5768, 5773, 5778, 5783, 5787, 5791, 5795, 5800, 0, + 5805, 5812, 5817, 5825, 5833, 5839, 5845, 5853, 5861, 5869, 5877, 5884, + 5891, 5900, 5909, 5917, 5925, 5933, 5941, 5949, 5957, 5965, 5973, 5980, + 5987, 5993, 5999, 6007, 6015, 6022, 6029, 6038, 6047, 6054, 6061, 6069, + 6077, 6085, 6093, 6099, 6105, 6113, 6121, 6129, 6137, 6144, 6151, 6159, + 6167, 6175, 6183, 6188, 6193, 6200, 6207, 6217, 6227, 6231, 6238, 6245, + 6252, 6259, 6267, 6275, 6282, 6289, 6297, 6305, 6312, 6319, 6327, 0, + 6335, 6341, 6347, 6353, 6359, 6365, 6371, 6378, 6385, 6390, 6395, 6402, + 6409, 6416, 6423, 6430, 6437, 6444, 6451, 6457, 6463, 6469, 6475, 6481, + 6487, 6493, 6499, 6507, 6515, 6521, 6527, 6533, 6539, 6545, 6551, 6558, + 6565, 6572, 0, 0, 6579, 6586, 0, 0, 0, 0, 0, 0, 6593, 6600, 6607, 6614, + 6621, 6628, 6635, 6642, 6649, 6656, 6663, 6670, 6677, 6684, 6691, 6698, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 6705, 6710, 6715, 6720, 6725, 6730, 6735, + 6740, 6745, 6749, 6754, 6759, 6764, 6769, 6774, 6779, 6784, 6789, 6794, + 6799, 6804, 6809, 6814, 6819, 6824, 6829, 6834, 6839, 6844, 6849, 6854, + 6859, 6864, 6869, 6874, 6879, 6884, 6889, 0, 0, 6894, 6901, 6904, 6908, + 6912, 6915, 6919, 0, 6923, 6928, 6933, 6938, 6943, 6948, 6953, 6958, + 6963, 6967, 6972, 6977, 6982, 6987, 6992, 6997, 7002, 7007, 7012, 7017, + 7022, 7027, 7032, 7037, 7042, 7047, 7052, 7057, 7062, 7067, 7072, 7077, + 7082, 7087, 7092, 7097, 7102, 7107, 7112, 0, 7119, 7122, 0, 0, 0, 0, 0, + 0, 7125, 7129, 7133, 7137, 7143, 7149, 7153, 7157, 7161, 7165, 7169, + 7173, 7177, 7183, 7187, 7193, 7199, 0, 7203, 7207, 7211, 7215, 7221, + 7225, 7229, 7235, 7243, 7247, 7251, 7255, 7259, 7265, 7270, 7277, 7284, + 7291, 7296, 7301, 7306, 7311, 7316, 0, 7321, 7326, 7334, 7339, 7344, + 7349, 7354, 7360, 7366, 7373, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7377, + 7381, 7385, 7389, 7393, 7397, 7401, 7405, 7409, 7413, 7417, 7422, 7426, + 7430, 7435, 7439, 7444, 7448, 7452, 7456, 7461, 7465, 7470, 7474, 7478, + 7482, 7486, 0, 0, 0, 0, 0, 7490, 7497, 7505, 7512, 7517, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7522, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7525, 0, 0, 0, 7528, 0, 7532, 7536, 7543, + 7549, 7556, 7562, 7568, 7572, 7576, 7581, 7585, 7589, 7593, 7597, 7601, + 7605, 7609, 7613, 7617, 7621, 7625, 7629, 7633, 7637, 7641, 7645, 0, 0, + 0, 0, 0, 7649, 7652, 7656, 7660, 7664, 7668, 7672, 7676, 7680, 7684, + 7689, 7693, 7696, 7699, 7702, 7705, 7708, 7711, 7714, 7717, 7721, 7724, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7727, 7732, 7736, 7740, 7744, 7748, 7752, + 7756, 7760, 7764, 7768, 7772, 7777, 7782, 7789, 7795, 7801, 7807, 7812, + 7820, 7828, 7834, 7841, 7848, 7854, 7861, 7865, 7869, 7873, 7879, 7890, + 7894, 7898, 7902, 7908, 7917, 7921, 7925, 7934, 7938, 7942, 7946, 7953, + 7960, 7972, 7976, 7980, 7984, 7996, 8006, 8010, 8017, 8024, 8031, 8040, + 8051, 8060, 8064, 8074, 8084, 8093, 8109, 8118, 8128, 8138, 8148, 8154, + 8163, 8170, 8174, 8184, 8188, 8195, 8205, 8209, 8215, 8222, 8229, 8233, + 8243, 8247, 8254, 8258, 8267, 8271, 8281, 8287, 8293, 8302, 8311, 8317, + 8322, 8326, 8332, 8341, 8346, 8353, 8359, 8364, 8372, 8379, 8386, 8392, + 8396, 8399, 8403, 8409, 8418, 8422, 8428, 8434, 8440, 8447, 8450, 8459, + 8464, 8472, 8475, 8479, 8492, 8505, 8512, 8519, 8525, 8533, 8539, 8545, + 8555, 8563, 8573, 8584, 8591, 8597, 8603, 8607, 8611, 8617, 8623, 8629, + 8637, 8645, 8657, 0, 0, 8663, 8670, 8676, 8682, 8688, 8694, 8700, 8706, + 8712, 8718, 8724, 8730, 8737, 8744, 8750, 0, 8758, 8764, 8769, 8774, + 8779, 8784, 8788, 8795, 8801, 8810, 8818, 8821, 8826, 8831, 0, 8836, + 8840, 8844, 8850, 8854, 8858, 8864, 8868, 8876, 8880, 8884, 8888, 8892, + 8896, 8902, 8906, 8912, 8916, 8920, 8924, 8928, 8932, 8937, 8940, 8944, + 8949, 8953, 8957, 8961, 8965, 0, 0, 0, 8969, 8973, 8977, 8982, 8986, + 8990, 8995, 8999, 9003, 9010, 9017, 9021, 9025, 9030, 9034, 9038, 9041, + 9045, 9048, 9051, 9057, 9063, 9069, 9075, 9080, 9085, 9088, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 9091, 9095, 9099, 9103, 9107, 9111, 9115, 9119, 9123, 9127, 9131, + 9135, 9139, 9143, 9147, 9151, 9155, 9159, 9163, 9167, 9171, 9175, 9179, + 9183, 9187, 9191, 9195, 9199, 9203, 9207, 9211, 9215, 9219, 9222, 9226, + 9230, 9234, 9238, 9242, 9245, 9248, 9251, 9254, 9257, 9260, 9263, 9266, + 9269, 9272, 9275, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 9279, 9283, 9287, 0, 9291, 9294, 9298, 9301, 9305, 9308, + 9312, 9316, 9320, 9325, 9329, 9332, 9336, 9341, 9345, 9348, 9352, 9355, + 9359, 9363, 9367, 9371, 9375, 9379, 9383, 9387, 9391, 9395, 9399, 9403, + 9407, 9411, 9415, 9419, 9423, 9427, 9431, 9435, 9439, 9443, 9447, 9451, + 9454, 9457, 9461, 9465, 9469, 9473, 9477, 9481, 9485, 9489, 9493, 0, 0, + 9497, 9501, 9505, 9510, 9514, 9519, 9523, 9528, 9533, 9539, 9545, 9550, + 9554, 9559, 9565, 9570, 9574, 9579, 0, 0, 9583, 9586, 9592, 9598, 9603, + 0, 0, 0, 9608, 9612, 9616, 9620, 9624, 9628, 9632, 9636, 9640, 9645, + 9650, 9655, 9661, 9664, 9668, 9672, 9675, 9678, 9681, 9684, 9687, 9690, + 9693, 9696, 9699, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9703, + 9707, 9711, 0, 9715, 9718, 9722, 9725, 9729, 9732, 9736, 9740, 0, 0, + 9744, 9747, 0, 0, 9751, 9754, 9758, 9761, 9765, 9769, 9773, 9777, 9781, + 9785, 9789, 9793, 9797, 9801, 9805, 9809, 9813, 9817, 9821, 9825, 9829, + 9833, 0, 9837, 9841, 9845, 9849, 9853, 9856, 9859, 0, 9863, 0, 0, 0, + 9867, 9871, 9875, 9879, 0, 0, 9883, 0, 9887, 9892, 9896, 9901, 9905, + 9910, 9915, 0, 0, 9921, 9925, 0, 0, 9930, 9934, 9939, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9943, 0, 0, 0, 0, 9949, 9953, 0, 9957, 9961, 9966, 9971, 9976, + 0, 0, 9982, 9986, 9989, 9992, 9995, 9998, 10001, 10004, 10007, 10010, + 10013, 10022, 10030, 10034, 10038, 10044, 10050, 10056, 10062, 10077, + 10084, 0, 0, 0, 0, 0, 0, 0, 10087, 0, 0, 10091, 10094, 10098, 10101, + 10105, 10108, 0, 0, 0, 0, 10112, 10116, 0, 0, 10120, 10124, 10128, 10131, + 10135, 10139, 10143, 10147, 10151, 10155, 10159, 10163, 10167, 10171, + 10175, 10179, 10183, 10187, 10191, 10195, 10199, 10203, 0, 10207, 10211, + 10215, 10219, 10223, 10226, 10229, 0, 10233, 10237, 0, 10241, 10245, 0, + 10249, 10253, 0, 0, 10257, 0, 10261, 10266, 10270, 10275, 10279, 0, 0, 0, + 0, 10284, 10289, 0, 0, 10294, 10299, 10304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10308, 10312, 10316, 10320, 0, 10324, 0, 0, 0, 0, 0, 0, 0, 10328, + 10332, 10335, 10338, 10341, 10344, 10347, 10350, 10353, 10356, 10359, + 10362, 10365, 10368, 10371, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10376, + 10380, 10384, 0, 10388, 10391, 10395, 10398, 10402, 10405, 10409, 0, + 10413, 0, 10418, 10421, 10425, 0, 10430, 10433, 10437, 10440, 10444, + 10448, 10452, 10456, 10460, 10464, 10468, 10472, 10476, 10480, 10484, + 10488, 10492, 10496, 10500, 10504, 10508, 10512, 0, 10516, 10520, 10524, + 10528, 10532, 10535, 10538, 0, 10542, 10546, 0, 10550, 10554, 10558, + 10562, 10566, 0, 0, 10570, 10574, 10578, 10583, 10587, 10592, 10596, + 10601, 10606, 10612, 0, 10618, 10622, 10627, 0, 10633, 10637, 10642, 0, + 0, 10646, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10649, 0, 0, 0, 0, + 0, 10654, 10658, 10661, 10664, 10667, 10670, 10673, 10676, 10679, 10682, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10685, 10689, 10693, + 0, 10697, 10700, 10704, 10707, 10711, 10714, 10718, 10722, 0, 0, 10726, + 10729, 0, 0, 10733, 10736, 10740, 10743, 10747, 10751, 10755, 10759, + 10763, 10767, 10771, 10775, 10779, 10783, 10787, 10791, 10795, 10799, + 10803, 10807, 10811, 10815, 0, 10819, 10823, 10827, 10831, 10835, 10838, + 10841, 0, 10845, 10849, 0, 0, 10853, 10857, 10861, 10865, 0, 0, 10869, + 10873, 10877, 10882, 10886, 10891, 10895, 10900, 0, 0, 0, 10905, 10909, + 0, 0, 10914, 10918, 10923, 0, 0, 0, 0, 0, 0, 0, 0, 10927, 10933, 0, 0, 0, + 0, 10939, 10943, 0, 10947, 10951, 10956, 0, 0, 0, 0, 10961, 10965, 10968, + 10971, 10974, 10977, 10980, 10983, 10986, 10989, 10992, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10995, 10999, 0, 11003, 11006, 11010, + 11013, 11017, 11020, 0, 0, 0, 11024, 11027, 11031, 0, 11035, 11038, + 11042, 11046, 0, 0, 0, 11049, 11053, 0, 11057, 0, 11061, 11065, 0, 0, 0, + 11069, 11073, 0, 0, 0, 11077, 11081, 11085, 0, 0, 0, 11089, 11092, 11095, + 11099, 11103, 11107, 11111, 11115, 0, 11119, 11123, 11127, 0, 0, 0, 0, + 11131, 11136, 11140, 11145, 11149, 0, 0, 0, 11154, 11158, 11163, 0, + 11168, 11172, 11177, 11182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11186, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11192, 11195, 11198, 11201, 11204, + 11207, 11210, 11213, 11216, 11219, 11223, 11229, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 11235, 11239, 11243, 0, 11247, 11250, 11254, 11257, + 11261, 11264, 11268, 11272, 0, 11276, 11279, 11283, 0, 11287, 11290, + 11294, 11298, 11301, 11305, 11309, 11313, 11317, 11321, 11325, 11329, + 11333, 11337, 11341, 11345, 11349, 11353, 11357, 11361, 11365, 11369, + 11373, 0, 11377, 11381, 11385, 11389, 11393, 11396, 11399, 11403, 11407, + 11411, 0, 11415, 11419, 11423, 11427, 11431, 0, 0, 0, 0, 11435, 11440, + 11444, 11449, 11453, 11458, 11463, 0, 11469, 11473, 11478, 0, 11483, + 11487, 11492, 11497, 0, 0, 0, 0, 0, 0, 0, 11501, 11505, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 11511, 11516, 0, 0, 0, 0, 11521, 11525, 11528, 11531, 11534, + 11537, 11540, 11543, 11546, 11549, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 11552, 11556, 0, 11560, 11563, 11567, 11570, 11574, 11577, + 11581, 11585, 0, 11589, 11592, 11596, 0, 11600, 11603, 11607, 11611, + 11614, 11618, 11622, 11626, 11630, 11634, 11638, 11642, 11646, 11650, + 11654, 11658, 11662, 11666, 11670, 11674, 11678, 11682, 11686, 0, 11690, + 11694, 11698, 11702, 11706, 11709, 11712, 11716, 11720, 11724, 0, 11728, + 11732, 11736, 11740, 11744, 0, 0, 0, 0, 11748, 11753, 11757, 11762, + 11766, 11771, 11776, 0, 11782, 11786, 11791, 0, 11796, 11800, 11805, + 11810, 0, 0, 0, 0, 0, 0, 0, 11814, 11818, 0, 0, 0, 0, 0, 0, 0, 11824, 0, + 11828, 11833, 0, 0, 0, 0, 11838, 11842, 11845, 11848, 11851, 11854, + 11857, 11860, 11863, 11866, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 11869, 11873, 0, 11877, 11880, 11884, 11887, 11891, 11894, + 11898, 11902, 0, 11906, 11909, 11913, 0, 11917, 11920, 11924, 11928, + 11931, 11935, 11939, 11943, 11947, 11951, 11955, 11959, 11963, 11967, + 11971, 11975, 11979, 11983, 11987, 11991, 11995, 11999, 12003, 0, 12007, + 12011, 12015, 12019, 12023, 12026, 12029, 12033, 12037, 12041, 12045, + 12049, 12053, 12057, 12061, 12065, 0, 0, 0, 0, 12069, 12074, 12078, + 12083, 12087, 12092, 0, 0, 12097, 12101, 12106, 0, 12111, 12115, 12120, + 12125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12129, 0, 0, 0, 0, 0, 0, 0, 0, 12135, + 12140, 0, 0, 0, 0, 12145, 12149, 12152, 12155, 12158, 12161, 12164, + 12167, 12170, 12173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 12176, 12180, 0, 12184, 12188, 12192, 12196, 12200, 12204, 12208, + 12212, 12216, 12220, 12224, 12228, 12232, 12236, 12240, 12244, 12248, + 12252, 0, 0, 0, 12256, 12262, 12268, 12274, 12280, 12286, 12292, 12298, + 12304, 12310, 12316, 12322, 12330, 12336, 12342, 12348, 12354, 12360, + 12366, 12372, 12378, 12384, 12390, 12396, 0, 12402, 12408, 12414, 12420, + 12426, 12432, 12436, 12442, 12446, 0, 12450, 0, 0, 12456, 12460, 12466, + 12472, 12478, 12482, 12488, 0, 0, 0, 12492, 0, 0, 0, 0, 12496, 12501, + 12508, 12515, 12522, 12529, 0, 12536, 0, 12543, 12548, 12553, 12560, + 12567, 12576, 12587, 12596, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 12601, 12608, 12615, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12620, + 12626, 12632, 12638, 12644, 12650, 12656, 12662, 12668, 12674, 12680, + 12686, 12692, 12698, 12704, 12709, 12715, 12721, 12727, 12733, 12739, + 12744, 12750, 12756, 12762, 12768, 12774, 12780, 12786, 12792, 12798, + 12804, 12810, 12815, 12821, 12827, 12831, 12837, 12841, 12847, 12853, + 12859, 12865, 12871, 12877, 12882, 12888, 12892, 12897, 12903, 12909, + 12915, 12920, 12926, 12932, 12938, 12943, 12949, 0, 0, 0, 0, 12953, + 12959, 12964, 12970, 12975, 12983, 12991, 12995, 12999, 13003, 13009, + 13015, 13021, 13027, 13031, 13035, 13039, 13043, 13047, 13050, 13053, + 13056, 13059, 13062, 13065, 13068, 13071, 13074, 13078, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 13082, 13086, 0, 13092, 0, 0, 13098, 13102, 0, + 13106, 0, 0, 13112, 0, 0, 0, 0, 0, 0, 13116, 13120, 13123, 13129, 0, + 13135, 13139, 13143, 13147, 13153, 13159, 13165, 0, 13171, 13175, 13179, + 0, 13185, 0, 13191, 0, 0, 13195, 13201, 0, 13207, 13210, 13216, 13219, + 13223, 13230, 13235, 13240, 13244, 13249, 13253, 13258, 13262, 0, 13267, + 13274, 13280, 0, 0, 13286, 13290, 13295, 13299, 13304, 0, 13309, 0, + 13314, 13321, 13328, 13335, 13342, 13346, 0, 0, 13349, 13353, 13356, + 13359, 13362, 13365, 13368, 13371, 13374, 13377, 0, 0, 13380, 13385, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 13390, 13394, 13405, 13420, 13435, 13445, + 13456, 13469, 13480, 13486, 13494, 13504, 13510, 13518, 13522, 13528, + 13534, 13542, 13552, 13560, 13573, 13579, 13587, 13595, 13607, 13615, + 13623, 13631, 13639, 13647, 13655, 13663, 13673, 13677, 13680, 13683, + 13686, 13689, 13692, 13695, 13698, 13701, 13704, 13708, 13712, 13716, + 13720, 13724, 13728, 13732, 13736, 13740, 13745, 13751, 13761, 13775, + 13785, 13791, 13797, 13805, 13813, 13821, 13829, 13835, 13841, 13844, + 13848, 13852, 13856, 13860, 13864, 13868, 0, 13872, 13876, 13880, 13884, + 13888, 13892, 13896, 13900, 13904, 13908, 13912, 13916, 13920, 13924, + 13928, 13932, 13935, 13939, 13943, 13947, 13951, 13955, 13959, 13963, + 13967, 13970, 13974, 13978, 13982, 13986, 13990, 13994, 13997, 14001, 0, + 0, 0, 0, 0, 0, 14007, 14012, 14016, 14021, 14025, 14030, 14035, 14041, + 14046, 14052, 14056, 14061, 14065, 14070, 14080, 14086, 14091, 14097, + 14107, 14113, 14117, 14121, 14127, 14133, 14141, 14147, 14155, 0, 0, 0, + 0, 14163, 14167, 14172, 14177, 14182, 14187, 14192, 14197, 0, 14202, + 14207, 14212, 14217, 14222, 14227, 14232, 14237, 14242, 14247, 14252, + 14257, 14262, 14267, 14272, 14277, 14281, 14286, 14291, 14296, 14301, + 14306, 14311, 14316, 14321, 14325, 14330, 14335, 14340, 14345, 14350, + 14355, 14359, 14364, 14371, 14377, 0, 14384, 14391, 14404, 14411, 14418, + 14426, 14434, 14440, 14446, 14452, 14462, 14468, 14474, 14484, 14494, 0, + 0, 14504, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 14512, 14515, 14519, 14523, 14527, 14531, 14535, 14539, 14543, + 14547, 14551, 14555, 14559, 14563, 14567, 14571, 14575, 14579, 14583, + 14587, 14591, 14595, 14599, 14603, 14607, 14611, 14614, 14617, 14621, + 14625, 14629, 14633, 14637, 14641, 0, 14644, 14647, 14651, 14654, 14658, + 0, 14661, 14664, 0, 14668, 14673, 14677, 14682, 14686, 14691, 14695, 0, + 0, 0, 14700, 14704, 14708, 14712, 0, 0, 0, 0, 0, 0, 14716, 14720, 14723, + 14726, 14729, 14732, 14735, 14738, 14741, 14744, 14747, 14753, 14757, + 14761, 14765, 14769, 14773, 14777, 14781, 14785, 14790, 14794, 14799, + 14804, 14810, 14815, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 14821, 14826, 14831, 14836, 14841, 14846, 14851, 14856, + 14861, 14866, 14871, 14876, 14881, 14886, 14891, 14896, 14901, 14906, + 14911, 14916, 14921, 14926, 14931, 14936, 14941, 14946, 14951, 14956, + 14961, 14966, 14971, 14976, 14981, 14986, 14991, 14996, 15001, 15006, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 15011, 15015, 15019, 15023, 15027, 15031, + 15035, 15039, 15043, 15047, 15051, 15055, 15059, 15063, 15067, 15071, + 15075, 15079, 15083, 15087, 15091, 15095, 15099, 15103, 15107, 15111, + 15115, 15119, 15123, 15127, 15131, 15135, 15139, 15143, 15147, 15151, + 15155, 15159, 15163, 15167, 15171, 0, 0, 15175, 0, 0, 0, 0, 15180, 15184, + 15188, 15192, 15196, 15200, 15204, 15208, 15212, 15216, 15220, 15224, + 15228, 15232, 15236, 15240, 15244, 15248, 15252, 15256, 15260, 15264, + 15268, 15272, 15276, 15280, 15284, 15288, 15292, 15296, 15300, 15304, + 15308, 15312, 15316, 15320, 15324, 15328, 15332, 15336, 15340, 15344, + 15348, 15352, 15356, 15360, 15364, 15368, 15372, 15376, 15380, 15384, + 15388, 15392, 15396, 15400, 15404, 15408, 15412, 15416, 15420, 15424, + 15428, 15432, 15436, 15440, 15444, 15448, 15452, 15456, 15460, 15464, + 15468, 15472, 15476, 15480, 15484, 15488, 15492, 15496, 15500, 15504, + 15508, 15512, 15516, 15520, 15524, 15528, 15532, 15536, 0, 0, 0, 0, 0, + 15540, 15544, 15548, 15551, 15555, 15558, 15562, 15566, 15569, 15573, + 15577, 15580, 15584, 15588, 15592, 15596, 15599, 15603, 15607, 15611, + 15615, 15619, 15623, 15626, 15630, 15634, 15638, 15642, 15646, 15650, + 15654, 15658, 15662, 15666, 15670, 15674, 15678, 15682, 15686, 15690, + 15694, 15698, 15702, 15706, 15710, 15714, 15718, 15722, 15726, 15730, + 15734, 15738, 15742, 15746, 15750, 15754, 15758, 15762, 15766, 15770, + 15774, 15778, 15782, 15786, 15790, 15794, 15798, 15802, 0, 0, 0, 0, 0, + 15806, 15810, 15814, 15818, 15822, 15826, 15830, 15834, 15838, 15842, + 15846, 15850, 15854, 15858, 15862, 15866, 15870, 15874, 15878, 15882, + 15886, 15890, 15894, 15898, 15902, 15906, 15910, 15914, 15918, 15922, + 15926, 15930, 15934, 15938, 15942, 15946, 15950, 15954, 15958, 15962, + 15966, 15970, 15974, 15978, 15982, 15986, 15990, 15994, 15998, 16002, + 16006, 16010, 16014, 16018, 16022, 16026, 16030, 16034, 16038, 16042, + 16046, 16050, 16054, 16058, 16062, 16066, 16070, 16074, 16078, 16082, + 16086, 16090, 16094, 16098, 16102, 16106, 16110, 16114, 16118, 16122, + 16126, 16130, 0, 0, 0, 0, 0, 0, 16134, 16138, 16142, 16146, 16150, 16154, + 16158, 0, 16162, 16166, 16170, 16174, 16178, 16182, 16186, 16190, 16194, + 16198, 16202, 16206, 16210, 16214, 16218, 16222, 16226, 16229, 16233, + 16237, 16241, 16245, 16249, 16253, 16257, 16261, 16265, 16269, 16273, + 16277, 16281, 16285, 16289, 16293, 16297, 16301, 16305, 16309, 16313, + 16317, 16321, 16325, 16329, 16333, 16337, 16341, 16345, 16349, 16353, + 16357, 16361, 16365, 16369, 16373, 16377, 16381, 16385, 16389, 16393, + 16397, 16401, 16405, 16409, 0, 16413, 0, 16417, 16421, 16425, 16429, 0, + 0, 16433, 16437, 16441, 16445, 16449, 16453, 16457, 0, 16461, 0, 16465, + 16469, 16473, 16477, 0, 0, 16481, 16485, 16489, 16493, 16497, 16501, + 16505, 16509, 16513, 16517, 16521, 16525, 16529, 16533, 16537, 16541, + 16545, 16549, 16553, 16557, 16561, 16565, 16569, 16572, 16576, 16580, + 16584, 16588, 16592, 16596, 16600, 16604, 16608, 16612, 16616, 16620, + 16624, 16628, 16632, 0, 16636, 0, 16640, 16644, 16648, 16652, 0, 0, + 16656, 16660, 16664, 16668, 16672, 16676, 16680, 16684, 16688, 16692, + 16696, 16700, 16704, 16708, 16712, 16716, 16720, 16725, 16730, 16735, + 16741, 16747, 16752, 16757, 16763, 16766, 16770, 16774, 16778, 16782, + 16786, 0, 16790, 0, 16794, 16798, 16802, 16806, 0, 0, 16810, 16814, + 16818, 16822, 16826, 16830, 16834, 0, 16838, 0, 16842, 16846, 16850, + 16854, 0, 0, 16858, 16862, 16866, 16870, 16874, 16878, 16882, 0, 16886, + 16891, 16896, 16901, 16907, 16913, 16918, 0, 16923, 16927, 16931, 16935, + 16939, 16943, 16947, 16951, 16955, 16959, 16963, 16967, 16971, 16975, + 16979, 16983, 16987, 16990, 16994, 16998, 17002, 17006, 17010, 0, 17014, + 17018, 17022, 17026, 17030, 17034, 17038, 17042, 17046, 17050, 17054, + 17058, 17062, 17066, 17070, 17074, 17078, 17082, 17086, 17090, 17094, + 17098, 17102, 17106, 17110, 17114, 17118, 17122, 17126, 17130, 17134, 0, + 17138, 0, 17142, 17146, 17150, 17154, 0, 0, 17158, 17162, 17166, 17170, + 17174, 17178, 17182, 0, 17186, 17190, 17194, 17198, 17202, 17206, 17210, + 17214, 17218, 17222, 17226, 17230, 17234, 17238, 17242, 17246, 17250, + 17254, 17258, 17262, 17266, 17270, 17274, 17278, 17282, 17286, 17290, + 17294, 17298, 17302, 17306, 17310, 17314, 17318, 17322, 17326, 17330, + 17334, 17338, 0, 17342, 17346, 17350, 17354, 17358, 17362, 17366, 17370, + 17374, 17378, 17382, 17386, 17390, 17394, 17398, 17402, 17406, 17410, + 17414, 0, 0, 0, 0, 0, 0, 17418, 17421, 17424, 17427, 17430, 17433, 17438, + 17442, 17447, 17450, 17453, 17456, 17459, 17462, 17465, 17468, 17471, + 17474, 17478, 17482, 17486, 17490, 17494, 17498, 17502, 17506, 17510, + 17514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17520, 17523, 17526, 17529, 17532, + 17535, 17538, 17542, 17545, 17549, 17553, 17557, 17561, 17565, 17569, + 17573, 17577, 17581, 17585, 17589, 17593, 17597, 17601, 17605, 17609, + 17613, 17616, 17620, 17624, 17628, 17632, 17636, 17640, 17644, 17648, + 17652, 17656, 17660, 17664, 17668, 17672, 17676, 17680, 17684, 17688, + 17692, 17695, 17699, 17703, 17707, 17711, 17715, 17719, 17723, 17727, + 17731, 17735, 17739, 17743, 17747, 17751, 17755, 17759, 17763, 17767, + 17771, 17775, 17779, 17783, 17787, 17791, 17795, 17799, 17803, 17807, + 17811, 17815, 17819, 17823, 17827, 17830, 17834, 17838, 17842, 17846, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17850, 17853, 17857, 17860, 17864, + 17867, 17871, 17877, 17882, 17886, 17889, 17893, 17897, 17902, 17906, + 17911, 17915, 17920, 17924, 17929, 17933, 17938, 17944, 17948, 17953, + 17957, 17962, 17968, 17972, 17977, 17982, 17986, 17990, 17998, 18006, + 18013, 18018, 18023, 18031, 18037, 18043, 18048, 18054, 18058, 18062, + 18066, 18070, 18074, 18078, 18082, 18086, 18090, 18094, 18100, 18105, + 18110, 18114, 18118, 18122, 18127, 18131, 18136, 18140, 18145, 18149, + 18154, 18158, 18163, 18167, 18172, 18176, 18181, 18187, 18190, 18194, + 18198, 18202, 18206, 18210, 18214, 18217, 18221, 18227, 18232, 18237, + 18241, 18245, 18249, 18254, 18258, 18263, 18267, 18272, 18275, 18279, + 18283, 18288, 18292, 18297, 18301, 18306, 18312, 18315, 18319, 18323, + 18327, 18331, 18335, 18339, 18343, 18347, 18351, 18355, 18361, 18364, + 18368, 18372, 18377, 18381, 18386, 18390, 18395, 18399, 18404, 18408, + 18413, 18417, 18422, 18426, 18431, 18437, 18440, 18444, 18450, 18456, + 18462, 18468, 18472, 18476, 18480, 18484, 18488, 18492, 18498, 18502, + 18506, 18510, 18515, 18519, 18524, 18528, 18533, 18537, 18542, 18546, + 18551, 18555, 18560, 18564, 18569, 18575, 18578, 18584, 18588, 18592, + 18596, 18600, 18604, 18608, 18614, 18617, 18621, 18625, 18630, 18634, + 18639, 18643, 18648, 18652, 18657, 18661, 18666, 18670, 18675, 18679, + 18684, 18690, 18693, 18697, 18701, 18706, 18711, 18715, 18719, 18723, + 18727, 18731, 18735, 18741, 18745, 18749, 18753, 18758, 18762, 18767, + 18771, 18776, 18782, 18785, 18790, 18794, 18798, 18802, 18806, 18810, + 18814, 18818, 18824, 18828, 18832, 18836, 18841, 18845, 18850, 18854, + 18859, 18863, 18868, 18872, 18877, 18881, 18886, 18890, 18895, 18898, + 18902, 18906, 18910, 18914, 18918, 18922, 18926, 18930, 18936, 18940, + 18944, 18948, 18953, 18957, 18962, 18966, 18971, 18975, 18980, 18984, + 18989, 18993, 18998, 19002, 19007, 19013, 19016, 19021, 19025, 19030, + 19036, 19042, 19048, 19054, 19060, 19066, 19072, 19076, 19080, 19084, + 19088, 19092, 19096, 19100, 19104, 19109, 19113, 19118, 19122, 19127, + 19131, 19136, 19140, 19145, 19149, 19154, 19158, 19163, 19167, 19171, + 19175, 19179, 19183, 19187, 19191, 19197, 19200, 19204, 19208, 19213, + 19217, 19222, 19226, 19231, 19235, 19240, 19244, 19249, 19253, 19258, + 19262, 19267, 19273, 19276, 19281, 19285, 19291, 19295, 19301, 19306, + 19310, 19314, 19318, 19322, 19326, 19331, 19335, 19339, 19344, 19348, + 19353, 19356, 19360, 19364, 19368, 19372, 19376, 19380, 19384, 19388, + 19392, 19396, 19400, 19405, 19408, 19412, 19418, 19422, 19428, 19432, + 19438, 19442, 19446, 19450, 19454, 19458, 19463, 19467, 19471, 19475, + 19479, 19483, 19487, 19491, 19495, 19499, 19503, 19509, 19515, 19521, + 19527, 19533, 19539, 19545, 19550, 19555, 19559, 19563, 19567, 19571, + 19575, 19579, 19583, 19587, 19590, 19594, 19598, 19602, 19606, 19611, + 19616, 19621, 19626, 19630, 19634, 19638, 19642, 19646, 19650, 19654, + 19658, 19662, 19668, 19674, 19680, 19686, 19692, 19698, 19704, 19710, + 19716, 19720, 19724, 19728, 19732, 19736, 19740, 19744, 19750, 19756, + 19762, 19768, 19774, 19780, 19786, 19792, 19797, 19802, 19807, 19812, + 19817, 19823, 19829, 19835, 19841, 19847, 19853, 19859, 19865, 19871, + 19877, 19883, 19888, 19894, 19900, 19906, 19912, 19917, 19922, 19927, + 19932, 19937, 19942, 19947, 19952, 19957, 19962, 19967, 19972, 19977, + 19982, 19987, 19992, 19997, 20002, 20007, 20012, 20017, 20022, 20027, + 20032, 20037, 20042, 20047, 20052, 20057, 20062, 20067, 20072, 20077, + 20082, 20087, 20092, 20097, 20102, 20107, 20112, 20117, 20122, 20126, + 20131, 20136, 20141, 20146, 20151, 20156, 20161, 20166, 20171, 20176, + 20181, 20186, 20191, 20196, 20201, 20206, 20211, 20216, 20221, 20226, + 20231, 20236, 20241, 20246, 20251, 20256, 20261, 20266, 20271, 20276, + 20280, 20285, 20290, 20295, 20300, 20305, 20309, 20314, 20320, 20325, + 20330, 20335, 20340, 20346, 20351, 20356, 20361, 20366, 20371, 20376, + 20381, 20386, 20391, 20396, 20401, 20406, 20411, 20416, 20421, 20426, + 20431, 20436, 20441, 20446, 20451, 20456, 20461, 20466, 20471, 20476, + 20481, 20486, 20491, 20496, 20501, 20506, 20511, 20516, 20521, 20526, + 20531, 20536, 20541, 20546, 20551, 20556, 20561, 20565, 20570, 20575, + 20580, 20585, 20590, 20595, 20600, 20605, 20610, 20615, 20620, 20625, + 20630, 20635, 20640, 20645, 20650, 20655, 20660, 20665, 20670, 20675, + 20680, 20685, 20690, 20695, 20700, 20705, 20710, 20715, 20720, 20725, + 20730, 20735, 20740, 20745, 20750, 20755, 20760, 20764, 20768, 20772, + 20776, 20780, 20784, 20788, 20792, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20796, + 20801, 20806, 20811, 20816, 20821, 20826, 20831, 20836, 20841, 20846, + 20851, 20856, 20861, 20866, 20871, 20876, 20881, 20886, 20891, 20896, + 20901, 20906, 20911, 20916, 20921, 20926, 20931, 20936, 0, 0, 0, 20942, + 20951, 20954, 20961, 20965, 20968, 20971, 20979, 20983, 20988, 20993, + 20998, 21002, 21007, 21012, 21015, 21019, 21023, 21032, 21036, 21040, + 21045, 21048, 21052, 21059, 21063, 21070, 21075, 21080, 21085, 21090, + 21099, 21104, 21108, 21117, 21120, 21125, 21129, 21135, 21140, 21146, + 21153, 21159, 21164, 21171, 21176, 21179, 21182, 21191, 21196, 21199, + 21208, 21213, 21217, 21221, 21228, 21235, 21240, 21245, 21254, 21258, + 21262, 21266, 21273, 21280, 21284, 21288, 21292, 21296, 21300, 21304, + 21308, 21312, 21316, 21319, 21322, 21327, 21332, 21337, 21341, 21345, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21349, 21353, 21357, 21361, + 21365, 21370, 21375, 21380, 21385, 21390, 21395, 21400, 21404, 0, 21408, + 21413, 21418, 21423, 21428, 21433, 21438, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 21443, 21447, 21451, 21455, 21459, 21464, 21469, 21474, 21479, 21484, + 21489, 21494, 21498, 21502, 21507, 21512, 21517, 21522, 21527, 21532, + 21537, 21542, 21548, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21553, 21557, 21561, + 21565, 21569, 21574, 21579, 21584, 21589, 21594, 21599, 21604, 21608, + 21612, 21617, 21622, 21627, 21632, 21637, 21642, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 21647, 21651, 21655, 21659, 21663, 21668, 21673, 21678, + 21683, 21688, 21693, 21698, 21702, 0, 21706, 21711, 21716, 0, 21721, + 21726, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21731, 21734, 21738, 21742, + 21746, 21750, 21754, 21758, 21762, 21766, 21770, 21774, 21778, 21782, + 21786, 21790, 21794, 21798, 21801, 21805, 21809, 21813, 21817, 21821, + 21825, 21829, 21833, 21837, 21841, 21845, 21849, 21853, 21857, 21861, + 21865, 21869, 21875, 21881, 21887, 21893, 21899, 21905, 21911, 21917, + 21923, 21929, 21935, 21941, 21947, 21953, 21962, 21971, 21977, 21983, + 21989, 21994, 21998, 22003, 22007, 22012, 22016, 22021, 22026, 22031, + 22035, 22040, 22044, 22049, 22054, 22059, 22064, 22068, 22072, 22076, + 22080, 22084, 22088, 22092, 22096, 22100, 22104, 22110, 22114, 22118, + 22122, 22126, 22130, 22138, 22144, 22148, 22154, 22158, 22164, 0, 0, 0, + 22168, 22172, 22175, 22178, 22181, 22184, 22187, 22190, 22193, 22196, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22199, + 22202, 22205, 22208, 22211, 22214, 22219, 22226, 22234, 22239, 22244, + 22247, 22255, 22263, 22271, 0, 22275, 22279, 22282, 22285, 22288, 22291, + 22294, 22297, 22300, 22303, 0, 0, 0, 0, 0, 0, 22306, 22309, 22312, 22315, + 22318, 22321, 22325, 22329, 22333, 22337, 22341, 22345, 22349, 22353, + 22357, 22360, 22364, 22368, 22372, 22376, 22380, 22384, 22388, 22391, + 22395, 22399, 22403, 22406, 22410, 22414, 22418, 22422, 22426, 22430, + 22434, 22438, 22445, 22450, 22455, 22460, 22465, 22471, 22477, 22483, + 22489, 22495, 22501, 22507, 22512, 22518, 22524, 22530, 22536, 22542, + 22547, 22553, 22558, 22564, 22570, 22576, 22582, 22588, 22593, 22598, + 22604, 22610, 22615, 22621, 22626, 22632, 22638, 22644, 22650, 22656, + 22662, 22668, 22674, 22680, 22686, 22692, 22698, 22704, 22710, 22716, + 22721, 22726, 22732, 22738, 0, 0, 0, 0, 0, 0, 0, 0, 22744, 22751, 22758, + 22764, 22770, 22778, 22784, 22792, 22797, 22802, 22807, 22813, 22819, + 22825, 22831, 22837, 22843, 22849, 22855, 22861, 22867, 22873, 22879, + 22885, 22891, 22899, 22907, 22915, 22923, 22931, 22939, 22947, 22955, + 22963, 22971, 22979, 22987, 22995, 23003, 23009, 23015, 23023, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23029, 23037, 23045, 23053, 23061, + 23069, 23077, 23085, 23093, 23103, 23113, 23121, 23129, 23137, 23145, + 23153, 23161, 23169, 23177, 23185, 23193, 23202, 23211, 23220, 23229, + 23236, 23243, 23250, 23257, 23266, 23275, 23283, 23291, 23298, 23305, + 23313, 23321, 23329, 23337, 23344, 23351, 23359, 23367, 23376, 23385, + 23392, 23399, 23408, 23417, 23424, 23431, 23439, 23447, 23455, 23463, + 23471, 23479, 23490, 23501, 23509, 23517, 23525, 23533, 23540, 23547, + 23555, 23563, 23571, 23579, 23587, 23595, 23603, 23611, 23619, 23627, + 23635, 23643, 23651, 23659, 23667, 23675, 23684, 23693, 23702, 23711, + 23718, 23725, 23733, 23741, 23749, 23757, 23765, 23773, 23784, 23795, + 23803, 23811, 23819, 23827, 23835, 23843, 23854, 23865, 23876, 23887, + 23899, 23911, 23919, 23927, 23935, 23943, 23951, 23959, 23967, 23975, + 23983, 23991, 23998, 24005, 24012, 24019, 24027, 24035, 24044, 24053, + 24060, 24067, 24075, 24083, 24090, 24097, 24104, 24111, 24118, 24125, + 24133, 24141, 24149, 24157, 24165, 24173, 24180, 24187, 24195, 24203, + 24211, 24219, 24227, 24235, 24243, 24251, 24259, 24266, 24275, 24284, + 24293, 0, 0, 0, 0, 24302, 24309, 24316, 24324, 24332, 24340, 24348, + 24356, 24364, 24374, 24384, 24392, 24400, 24409, 24418, 24427, 24436, + 24445, 24454, 24465, 24476, 24485, 24494, 24504, 24514, 24521, 24528, + 24536, 24544, 24550, 24556, 24564, 24572, 24580, 24588, 24598, 24608, + 24616, 24624, 24633, 24642, 24650, 24658, 24665, 24672, 24679, 24686, + 24694, 24702, 24710, 24718, 24726, 24734, 24744, 24754, 24762, 24770, + 24779, 24788, 24797, 24806, 24815, 24824, 24835, 24846, 24855, 24864, + 24874, 24884, 24891, 24898, 24906, 24914, 24923, 24932, 24941, 24950, + 24961, 24972, 24981, 24990, 25000, 25010, 25017, 25024, 25032, 25040, + 25049, 25058, 25065, 0, 0, 0, 0, 0, 0, 25072, 25079, 25086, 25094, 25102, + 25110, 25118, 25127, 25136, 25143, 25150, 25158, 25166, 25174, 25182, + 25191, 25200, 25208, 25216, 25225, 25234, 25243, 0, 0, 25252, 25260, + 25268, 25277, 25286, 25295, 0, 0, 25304, 25311, 25318, 25326, 25334, + 25342, 25350, 25359, 25368, 25375, 25382, 25390, 25398, 25406, 25414, + 25423, 25432, 25439, 25446, 25454, 25462, 25470, 25478, 25487, 25496, + 25503, 25510, 25518, 25526, 25534, 25542, 25551, 25560, 25568, 25576, + 25585, 25594, 25603, 0, 0, 25612, 25620, 25628, 25637, 25646, 25655, 0, + 0, 25664, 25672, 25680, 25689, 25698, 25707, 25716, 25726, 0, 25736, 0, + 25744, 0, 25753, 0, 25762, 25772, 25779, 25786, 25794, 25802, 25810, + 25818, 25827, 25836, 25843, 25850, 25858, 25866, 25874, 25882, 25891, + 25900, 25906, 25912, 25919, 25926, 25932, 25938, 25944, 25950, 25957, + 25964, 25971, 25978, 25984, 0, 0, 25990, 25998, 26006, 26017, 26028, + 26039, 26050, 26061, 26072, 26081, 26090, 26102, 26114, 26126, 26138, + 26150, 26162, 26170, 26178, 26189, 26200, 26211, 26222, 26233, 26244, + 26253, 26262, 26274, 26286, 26298, 26310, 26322, 26334, 26342, 26350, + 26361, 26372, 26383, 26394, 26405, 26416, 26425, 26434, 26446, 26458, + 26470, 26482, 26494, 26506, 26513, 26519, 26528, 26534, 0, 26543, 26550, + 26559, 26566, 26572, 26578, 26584, 26591, 26594, 26597, 26600, 26603, + 26609, 26618, 26624, 0, 26633, 26640, 26649, 26656, 26663, 26669, 26675, + 26682, 26686, 26690, 26695, 26702, 26708, 26717, 0, 0, 26726, 26733, + 26743, 26750, 26756, 26762, 0, 26768, 26772, 26776, 26781, 26789, 26796, + 26806, 26816, 26824, 26832, 26840, 26851, 26859, 26866, 26873, 26880, + 26888, 26893, 26898, 0, 0, 26900, 26909, 26915, 0, 26924, 26931, 26940, + 26947, 26954, 26960, 26966, 26973, 26975, 0, 26978, 26982, 26986, 26990, + 26994, 26998, 27002, 27006, 27010, 27014, 27018, 27022, 27028, 27034, + 27040, 27043, 27046, 27048, 27052, 27056, 27060, 27064, 27066, 27070, + 27075, 27081, 27087, 27094, 27101, 27106, 27111, 27117, 27123, 27125, + 27128, 27130, 27134, 27139, 27143, 27146, 27150, 27153, 27157, 27161, + 27165, 27171, 27175, 27179, 27185, 27190, 27197, 27199, 27202, 27206, + 27209, 27213, 27218, 27220, 27228, 27236, 27239, 27243, 27245, 27247, + 27249, 27252, 27258, 27260, 27264, 27268, 27274, 27280, 27284, 27289, + 27294, 27299, 27303, 27307, 27311, 27315, 27318, 27322, 27329, 0, 0, 0, + 0, 27334, 0, 0, 0, 0, 0, 0, 0, 27338, 27343, 27347, 27351, 27355, 0, 0, + 0, 0, 0, 0, 27359, 27365, 27371, 27378, 27385, 27390, 27395, 27399, 0, 0, + 27405, 27408, 27411, 27414, 27417, 27420, 27423, 27428, 27432, 27437, + 27442, 27447, 27453, 27457, 27460, 27463, 27466, 27469, 27472, 27475, + 27478, 27481, 27484, 27489, 27493, 27498, 27503, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 27508, 27511, 27514, 27517, 27522, 27525, + 27528, 27531, 27534, 27537, 27540, 27545, 27548, 27551, 27554, 27557, + 27560, 27565, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27568, 27572, 27576, 27582, 27588, 27593, + 27598, 27602, 27606, 27611, 27618, 27625, 27631, 27637, 27642, 27647, + 27652, 27658, 27663, 27668, 27673, 27681, 27688, 27695, 27699, 27704, + 27710, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 27715, 27719, 27726, 27729, 27733, 27737, 27741, 27745, 27749, 27751, + 27755, 27758, 27761, 27765, 27768, 27772, 27781, 27784, 27788, 27791, + 27794, 27801, 27804, 27807, 27813, 27816, 27819, 27822, 27825, 27829, + 27832, 27836, 27838, 27841, 27844, 27848, 27850, 27853, 27856, 27859, + 27864, 27868, 27875, 27878, 27881, 27884, 27888, 27891, 27894, 27897, + 27900, 27904, 27907, 27910, 27912, 27915, 27918, 27921, 27925, 0, 0, + 27929, 27933, 27937, 27941, 27946, 27951, 27956, 27960, 27965, 27969, + 27973, 27977, 27981, 27985, 27989, 0, 0, 0, 0, 0, 0, 0, 27993, 28001, + 28008, 28016, 28023, 28031, 28039, 28047, 28055, 28063, 28071, 28079, + 28087, 28092, 28095, 28098, 28101, 28104, 28107, 28110, 28113, 28116, + 28119, 28123, 28127, 28131, 28135, 28141, 28147, 28153, 28157, 28161, + 28165, 28169, 28173, 28177, 28181, 28185, 28189, 28194, 28199, 28204, + 28209, 28216, 28223, 28230, 28239, 28245, 28251, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 28258, 28260, 28262, 28264, 28266, 28269, 28272, 28277, + 28282, 28287, 28292, 28296, 28300, 28304, 28308, 28313, 28318, 28323, + 28328, 28333, 28338, 28343, 28348, 28353, 28358, 28364, 28368, 28372, + 28377, 28382, 28387, 28392, 28396, 28403, 28410, 28417, 28424, 28431, + 28438, 28445, 28452, 28460, 28471, 28477, 28483, 28489, 28495, 28501, + 28507, 28513, 28519, 28525, 28531, 28537, 28543, 28549, 28554, 28559, + 28564, 28569, 28576, 28583, 28588, 28594, 28599, 28602, 28605, 28608, + 28611, 28615, 28619, 28625, 28631, 28637, 28643, 28647, 28651, 28655, + 28659, 28664, 28669, 28673, 28677, 28681, 28685, 28689, 28693, 28696, + 28699, 28702, 28705, 28711, 28718, 28728, 28738, 28742, 28750, 28757, + 28765, 28773, 28777, 28783, 28789, 28794, 28799, 28804, 28810, 28816, + 28822, 28829, 28833, 28837, 28842, 28845, 28847, 28851, 28855, 28862, + 28866, 28868, 28870, 28874, 28881, 28886, 28892, 28901, 28908, 28913, + 28917, 28921, 28925, 28928, 28931, 28934, 28938, 28942, 28945, 28948, + 28951, 28954, 28958, 28962, 28965, 28967, 28970, 28972, 28976, 28980, + 28982, 28987, 28990, 28994, 28998, 29002, 29004, 29006, 29008, 29011, + 29015, 29019, 29023, 29027, 29031, 29037, 29043, 29045, 29047, 29049, + 29051, 29054, 29056, 29060, 29062, 29064, 29066, 29071, 29075, 29079, + 29081, 29084, 29088, 29093, 29097, 29106, 29116, 29120, 29125, 29131, + 29134, 29138, 29141, 29146, 29150, 29156, 29160, 29171, 29179, 29183, + 29187, 29193, 29197, 29200, 29202, 29205, 29209, 29213, 29219, 29223, + 29227, 29230, 29233, 29237, 29242, 29246, 29250, 29255, 29260, 29266, + 29272, 29276, 29280, 29282, 29286, 29289, 29292, 29299, 29306, 29311, + 29316, 29324, 29332, 29336, 29340, 29347, 29354, 29356, 29358, 29363, + 29368, 29374, 29380, 29385, 29390, 29394, 29398, 29404, 29410, 29416, + 29422, 29432, 29442, 29449, 29456, 29458, 29462, 29466, 29471, 29476, + 29483, 29490, 29493, 29496, 29499, 29502, 29505, 29510, 29513, 29517, + 29521, 29524, 29527, 29531, 29535, 29539, 29543, 29546, 29549, 29552, + 29555, 29557, 29559, 29561, 29563, 29571, 29579, 29584, 29587, 29592, + 29602, 29608, 29614, 29620, 29628, 29636, 29647, 29651, 29655, 29657, + 29663, 29665, 29667, 29669, 29671, 29676, 29678, 29684, 29690, 29694, + 29698, 29701, 29703, 29706, 29710, 29712, 29721, 29730, 29735, 29740, + 29744, 29750, 29756, 29759, 29762, 29765, 29768, 29770, 29775, 29778, + 29781, 29787, 29793, 29799, 29805, 29810, 29815, 29820, 29825, 29833, + 29841, 29849, 29857, 29865, 29873, 29880, 29887, 29895, 29903, 29910, + 29921, 29930, 29944, 29947, 29952, 29958, 29964, 29971, 29985, 30000, + 30006, 30012, 30019, 30025, 30033, 30039, 30052, 30066, 30071, 30077, + 30085, 30088, 30091, 30093, 30096, 30099, 30101, 30103, 30107, 30110, + 30113, 30116, 30119, 30124, 30129, 30134, 30139, 30142, 30145, 30147, + 30149, 30151, 30155, 30159, 30163, 30169, 30173, 30175, 30177, 30182, + 30187, 30192, 30197, 30202, 30207, 30209, 30211, 30220, 30224, 30230, + 30239, 30241, 30245, 30249, 30256, 30260, 30262, 30266, 30268, 30272, + 30276, 30280, 30282, 30284, 30286, 30291, 30298, 30305, 30312, 30319, + 30326, 30333, 30339, 30345, 30351, 30357, 30364, 30371, 30378, 30385, + 30391, 30397, 30404, 30411, 30417, 30425, 30432, 30440, 30447, 30455, + 30461, 30468, 30476, 30483, 30491, 30498, 30506, 30512, 30519, 30526, + 30533, 30540, 30547, 30553, 30561, 30568, 30574, 30581, 30588, 30594, + 30600, 30606, 30611, 30619, 30627, 30632, 30637, 30643, 30649, 30654, + 30660, 30667, 30675, 30682, 30689, 30696, 30701, 30706, 30711, 30717, + 30724, 30730, 30736, 30741, 30745, 30752, 30758, 30761, 30767, 30770, + 30775, 30780, 30783, 30786, 30794, 30797, 30802, 30805, 30812, 30817, + 30824, 30827, 30830, 30833, 30838, 30843, 30846, 30849, 30857, 30860, + 30865, 30872, 30876, 30880, 30885, 30890, 30895, 30900, 30905, 30910, + 30915, 30920, 30926, 30931, 30937, 30943, 30948, 30954, 30960, 30968, + 30974, 30979, 30985, 30993, 30999, 31003, 31007, 31017, 31027, 31031, + 31035, 31039, 31043, 31053, 31057, 31062, 31067, 31072, 31077, 31082, + 31087, 31096, 31105, 31113, 31123, 31133, 31140, 31149, 31158, 31166, + 31176, 31186, 31194, 31202, 31212, 31222, 31225, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31228, 31232, 31239, + 31246, 31253, 31260, 31264, 31268, 31272, 31276, 31281, 31286, 31291, + 31297, 31303, 31309, 31315, 31323, 31330, 31337, 31344, 31351, 31357, + 31363, 31372, 31376, 31383, 31387, 31391, 31397, 31403, 31409, 31415, + 31419, 31423, 31426, 31430, 31434, 31441, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31448, 31451, 31455, 31459, + 31465, 31471, 31477, 31485, 31492, 31496, 31504, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31509, 31512, 31515, 31518, 31521, + 31524, 31527, 31530, 31533, 31536, 31540, 31544, 31548, 31552, 31556, + 31560, 31564, 31568, 31572, 31576, 31580, 31583, 31586, 31589, 31592, + 31595, 31598, 31601, 31604, 31607, 31611, 31615, 31619, 31623, 31627, + 31631, 31635, 31639, 31643, 31647, 31651, 31656, 31660, 31665, 31670, + 31675, 31680, 31685, 31690, 31695, 31700, 31705, 31710, 31715, 31720, + 31725, 31730, 31735, 31740, 31745, 31750, 31755, 31760, 31765, 31770, + 31775, 31780, 31785, 31790, 31795, 31800, 31805, 31810, 31815, 31820, + 31825, 31830, 31835, 31840, 31845, 31850, 31855, 31860, 31865, 31870, + 31875, 31880, 31885, 31890, 31895, 31900, 31905, 31910, 31915, 31920, + 31925, 31930, 31935, 31940, 31945, 31950, 31955, 31960, 31965, 31970, + 31975, 31980, 31985, 31990, 31995, 32000, 32005, 32010, 32015, 32020, + 32025, 32030, 32035, 32040, 32045, 32050, 32055, 32060, 32065, 32070, + 32075, 32080, 32085, 32090, 32095, 32100, 32105, 32110, 32115, 32120, + 32125, 32130, 32135, 32140, 32144, 32150, 32156, 32162, 32168, 32174, + 32180, 32186, 32192, 32198, 32204, 32208, 32212, 32216, 32220, 32224, + 32228, 32232, 32236, 32240, 0, 32245, 32250, 32255, 32260, 32265, 32274, + 32283, 32292, 32301, 32310, 32319, 32328, 32337, 32343, 32351, 32359, + 32365, 32372, 32380, 32388, 32395, 32401, 32409, 32417, 32423, 32430, + 32438, 32446, 32453, 32459, 32467, 32476, 32485, 32493, 32502, 32511, + 32517, 32524, 32532, 32541, 32550, 32558, 32567, 32576, 32583, 32590, + 32599, 32608, 32616, 32624, 32633, 32642, 32649, 32656, 32665, 32674, + 32682, 32690, 32699, 32708, 32715, 32722, 32731, 32740, 32748, 32757, + 32766, 32774, 32784, 32794, 32804, 32814, 32823, 32832, 32841, 32850, + 32857, 32865, 32873, 32881, 32889, 32894, 32899, 32908, 32916, 32922, + 32931, 32939, 32946, 32955, 32963, 32969, 32978, 32986, 32993, 33002, + 33010, 33016, 33025, 33033, 33040, 33049, 33057, 33064, 33073, 33081, + 33088, 33097, 33105, 33112, 33120, 33129, 33138, 33146, 33157, 33167, + 33174, 33179, 33184, 33188, 33193, 33198, 33203, 33207, 33212, 33219, + 33227, 33234, 33242, 33246, 33253, 33260, 33267, 33271, 33278, 33285, + 33292, 33295, 33302, 33309, 33316, 33320, 33327, 33334, 33341, 33345, + 33348, 33352, 33356, 33363, 33370, 33375, 33379, 33384, 33394, 33401, + 33412, 33422, 33426, 33434, 33444, 33447, 33450, 33457, 33465, 33470, + 33475, 33483, 33492, 33501, 33509, 33513, 33517, 33520, 33523, 33527, + 33531, 33534, 33537, 33541, 33545, 33550, 33555, 33559, 33563, 33568, + 33573, 33578, 33583, 33587, 33591, 33596, 33601, 33605, 33609, 33614, + 33619, 33624, 33629, 33632, 33635, 33644, 33646, 33648, 33651, 33655, + 33660, 33662, 33665, 33671, 33677, 33683, 33689, 33697, 33709, 33714, + 33719, 33723, 33728, 33735, 33742, 33750, 33758, 33766, 33774, 33778, + 33782, 33786, 33790, 33794, 33798, 33801, 33807, 33813, 33822, 33831, + 33839, 33846, 33855, 33864, 33868, 33875, 33882, 33889, 33896, 33903, + 33910, 33917, 33924, 33927, 33930, 33933, 33938, 33943, 33949, 33955, + 33958, 33964, 33966, 33968, 33970, 33972, 33975, 33978, 33980, 33982, + 33984, 33988, 33992, 33994, 33996, 33999, 34002, 34006, 34012, 34017, 0, + 0, 34019, 34024, 0, 34029, 34038, 34044, 34050, 34056, 34062, 34068, + 34074, 34079, 34082, 34085, 34088, 34090, 34092, 34096, 34100, 34105, + 34110, 34115, 34118, 34122, 34127, 34130, 34134, 34139, 34144, 34149, + 34154, 34159, 34164, 34169, 34174, 34179, 34184, 34189, 34194, 34200, + 34206, 34212, 34214, 34217, 34219, 34222, 34224, 34226, 34228, 34230, + 34232, 34234, 34236, 34238, 34240, 34242, 34244, 34246, 34248, 34250, + 34252, 34254, 34256, 34261, 34266, 34271, 34276, 34281, 34286, 34291, + 34296, 34301, 34306, 34311, 34316, 34321, 34326, 34331, 34336, 34341, + 34346, 34351, 34356, 34360, 34364, 34368, 34374, 34380, 34385, 34390, + 34395, 34400, 34405, 34410, 34418, 34426, 34434, 34442, 34450, 34458, + 34466, 34474, 34480, 34485, 0, 0, 34490, 34494, 34498, 34502, 34506, + 34510, 34514, 34519, 34525, 34531, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34538, 34543, 34546, 34551, 0, 34554, + 34559, 34563, 34565, 0, 0, 34567, 34571, 34575, 34579, 34581, 34585, + 34588, 34591, 34594, 34598, 34601, 34605, 34608, 34612, 34617, 34621, + 34627, 34634, 34637, 34643, 34648, 34652, 34657, 34663, 34669, 34676, + 34682, 34689, 0, 34696, 34703, 34707, 34714, 34720, 34725, 34731, 34735, + 34740, 34743, 34749, 34755, 34762, 34770, 34777, 34786, 34796, 34803, + 34809, 34813, 34821, 34826, 34835, 34838, 34841, 34850, 34861, 34868, + 34870, 34876, 34881, 34883, 34886, 34890, 34898, 0, 34907, 0, 34912, + 34919, 34926, 34933, 0, 0, 0, 34940, 0, 34947, 34950, 34954, 34957, + 34969, 34979, 34990, 0, 0, 34999, 35008, 35014, 35022, 35026, 35034, + 35038, 35046, 35053, 35060, 35069, 35078, 35086, 35094, 35103, 35112, + 35119, 35126, 35135, 35144, 35152, 35160, 35167, 35174, 35181, 35188, + 35195, 35202, 35209, 35216, 35223, 35231, 35237, 35243, 35249, 35255, + 35261, 35267, 35273, 35279, 35285, 35292, 35300, 35308, 35316, 35324, + 35332, 35340, 35348, 35356, 35364, 35373, 0, 0, 0, 35378, 35384, 35387, + 35393, 35399, 35404, 35408, 35413, 35419, 35426, 35429, 35436, 35443, + 35447, 35455, 35463, 35468, 35474, 35479, 35484, 35491, 35498, 35505, + 35512, 0, 35520, 35528, 35533, 35537, 35544, 35548, 35555, 35563, 35568, + 35576, 35580, 35585, 35589, 35594, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 35598, 35605, 35608, 35615, 35621, 35627, 35632, 35637, + 35642, 35647, 35652, 35658, 35663, 35666, 35670, 35674, 35680, 35689, + 35694, 35703, 35712, 35718, 35724, 35729, 35734, 35738, 35742, 35747, 0, + 0, 0, 0, 35752, 35756, 35760, 35766, 35772, 35778, 35781, 35784, 35788, + 35792, 35796, 35801, 35807, 35813, 35820, 35827, 35832, 35836, 35840, + 35844, 35848, 35852, 35856, 35860, 35864, 35868, 35872, 35876, 35880, + 35884, 35888, 35892, 35896, 35900, 35904, 35908, 35912, 35916, 35920, + 35924, 35928, 35932, 35936, 35940, 35944, 35948, 35952, 35956, 35960, + 35964, 35968, 35972, 35976, 35980, 35984, 35988, 35992, 35996, 36000, + 36004, 36008, 36012, 36016, 36020, 36024, 36028, 36032, 36036, 36040, + 36044, 36048, 36052, 36056, 36060, 36064, 36068, 36072, 36076, 36080, + 36084, 36088, 36092, 36096, 36100, 36104, 36108, 36112, 36116, 36120, + 36124, 36128, 36132, 36136, 36140, 36144, 36148, 36152, 36156, 36160, + 36164, 36168, 36172, 36176, 36180, 36184, 36188, 36192, 36196, 36200, + 36204, 36208, 36212, 36216, 36220, 36224, 36228, 36232, 36236, 36240, + 36244, 36248, 36252, 36256, 36260, 36264, 36268, 36272, 36276, 36280, + 36284, 36288, 36292, 36296, 36300, 36304, 36308, 36312, 36316, 36320, + 36324, 36328, 36332, 36336, 36340, 36344, 36348, 36352, 36356, 36360, + 36364, 36368, 36372, 36376, 36380, 36384, 36388, 36392, 36396, 36400, + 36404, 36408, 36412, 36416, 36420, 36424, 36428, 36432, 36436, 36440, + 36444, 36448, 36452, 36456, 36460, 36464, 36468, 36472, 36476, 36480, + 36484, 36488, 36492, 36496, 36500, 36504, 36508, 36512, 36516, 36520, + 36524, 36528, 36532, 36536, 36540, 36544, 36548, 36552, 36556, 36560, + 36564, 36568, 36572, 36576, 36580, 36584, 36588, 36592, 36596, 36600, + 36604, 36608, 36612, 36616, 36620, 36624, 36628, 36632, 36636, 36640, + 36644, 36648, 36652, 36656, 36660, 36664, 36668, 36672, 36676, 36680, + 36684, 36688, 36692, 36696, 36700, 36704, 36708, 36712, 36716, 36720, + 36724, 36728, 36732, 36736, 36740, 36744, 36748, 36752, 36756, 36760, + 36764, 36768, 36772, 36776, 36780, 36784, 36788, 36792, 36796, 36800, + 36804, 36808, 36812, 36816, 36820, 36824, 36828, 36832, 36836, 36840, + 36844, 36848, 36852, 36856, 36863, 36871, 36877, 36883, 36890, 36897, + 36903, 36909, 36914, 36919, 36923, 36927, 36932, 36937, 36943, 36949, + 36957, 36964, 36968, 36972, 36980, 36989, 36996, 37006, 37017, 37020, + 37023, 37027, 37031, 37037, 37043, 37053, 37063, 37073, 37083, 37090, + 37097, 37104, 37111, 37122, 37133, 37144, 37155, 37165, 37175, 37187, + 37199, 37210, 37221, 37233, 37245, 37253, 37263, 37273, 37283, 37293, + 37300, 37307, 37314, 37321, 37331, 37341, 37348, 37355, 37361, 37367, + 37373, 37379, 37385, 37391, 37397, 37402, 37410, 37419, 37427, 37435, + 37443, 37451, 37459, 37467, 37475, 37483, 37490, 37497, 37504, 37511, + 37518, 37525, 37532, 37539, 37547, 37555, 37563, 37571, 37579, 37587, + 37595, 37603, 37615, 37627, 37639, 37651, 37663, 37675, 37687, 37699, + 37708, 37718, 37727, 37737, 37749, 37761, 37769, 37775, 37781, 37786, + 37791, 37798, 37802, 37808, 37812, 37817, 37823, 37828, 37833, 37838, + 37843, 37848, 37855, 37861, 37869, 37874, 37879, 37883, 37887, 37895, + 37903, 37911, 37919, 37925, 37931, 37943, 37955, 37967, 37979, 37984, + 37989, 37994, 37999, 38005, 38011, 38018, 38025, 38029, 38034, 38041, + 38048, 38054, 38060, 38064, 38071, 38078, 38082, 38085, 38089, 38094, + 38101, 38108, 38126, 38145, 38163, 38182, 38201, 38220, 38239, 38258, + 38263, 38270, 38278, 38286, 38294, 38298, 38301, 38304, 38309, 38312, + 38330, 38335, 38341, 38347, 38351, 38354, 38357, 38360, 38368, 38378, + 38386, 38394, 38398, 38403, 38407, 38412, 38417, 38422, 38427, 38436, + 38442, 38449, 38456, 38463, 38470, 38473, 38480, 38487, 38490, 38493, + 38498, 38503, 38509, 38515, 38519, 38525, 38532, 38536, 38542, 38546, + 38550, 38558, 38569, 38577, 38581, 38583, 38592, 38601, 38607, 38610, + 38615, 38620, 38625, 38630, 38635, 38640, 38645, 38650, 38652, 38657, + 38662, 38669, 38673, 38679, 38682, 38686, 38692, 38698, 38700, 38702, + 38707, 38713, 38719, 38727, 38736, 38742, 38748, 38753, 38758, 38763, + 38768, 38773, 38778, 38784, 38789, 38796, 38800, 38804, 38816, 38828, + 38838, 38846, 38851, 38858, 38864, 38869, 38874, 38879, 38884, 38886, + 38892, 38900, 38908, 38916, 38923, 38930, 38936, 38942, 38948, 38955, + 38961, 38968, 38974, 38982, 38990, 38999, 39008, 39015, 39021, 39027, + 39036, 39040, 39049, 39058, 39066, 39074, 39078, 39084, 39090, 39096, + 39100, 39106, 39114, 39119, 39123, 39129, 39134, 39139, 39146, 39153, + 39158, 39163, 39171, 39179, 39189, 39199, 39206, 39213, 39217, 39221, + 39233, 39239, 39245, 39250, 39255, 39262, 39269, 39275, 39281, 39290, + 39298, 39306, 39313, 39320, 39327, 39333, 39340, 39346, 39353, 39360, + 39368, 39376, 39382, 39387, 39396, 39406, 39412, 39420, 39426, 39431, + 39436, 39444, 39450, 39457, 39464, 39470, 39475, 39482, 39490, 39503, + 39511, 39516, 39521, 39527, 39533, 39539, 39545, 39555, 39565, 39574, + 39583, 39593, 39604, 39608, 39612, 39619, 39626, 39631, 39636, 39644, + 39652, 39659, 39666, 39673, 39680, 39688, 39696, 39708, 39720, 39727, + 39734, 39744, 39754, 39761, 39768, 39777, 39786, 39791, 39796, 39804, + 39812, 39817, 39822, 39829, 39834, 39839, 39846, 39853, 39865, 39877, + 39881, 39885, 39892, 39899, 39906, 39914, 39922, 39930, 39938, 39944, + 39950, 39956, 39962, 39969, 39976, 39984, 39992, 39995, 39998, 40002, + 40006, 40013, 40020, 40027, 40034, 40043, 40052, 40059, 40066, 40072, + 40078, 40086, 40094, 40101, 40108, 40114, 40120, 40124, 40128, 40135, + 40142, 40147, 40152, 40157, 40162, 40168, 40182, 40189, 40196, 40200, + 40202, 40204, 40208, 40212, 40216, 40220, 40228, 40235, 40242, 40250, + 40262, 40269, 40276, 40285, 40289, 40293, 40298, 40304, 40315, 40320, + 40325, 40331, 40336, 40341, 40350, 40358, 40363, 40369, 40375, 40383, + 40391, 40399, 40407, 40411, 40414, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40419, 40423, + 40427, 40432, 40437, 40442, 40446, 40450, 40454, 40459, 40464, 40468, + 40472, 40476, 40480, 40485, 40490, 40495, 40500, 40504, 40508, 40513, + 40518, 40523, 40528, 40532, 0, 40536, 40540, 40544, 40548, 40552, 40556, + 40560, 40565, 40570, 40574, 40579, 40584, 40593, 40597, 40601, 40605, + 40612, 40616, 40621, 40626, 40630, 40634, 40640, 40645, 40650, 40655, + 40660, 40664, 40668, 40672, 40676, 40680, 40685, 40690, 40694, 40698, + 40703, 40708, 40713, 40717, 40721, 40726, 40731, 40737, 40743, 40747, + 40753, 40759, 40763, 40769, 40775, 40780, 40785, 40789, 40795, 40799, + 40803, 40809, 40815, 40820, 40825, 40829, 40833, 40841, 40847, 40853, + 40859, 40864, 40869, 40874, 40880, 40884, 40890, 40894, 40898, 40904, + 40910, 40916, 40922, 40928, 40934, 40940, 40946, 40952, 40958, 40964, + 40970, 40974, 40980, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40986, 40989, + 40993, 40996, 41000, 41004, 41007, 41010, 41014, 41018, 41022, 41026, + 41029, 41034, 41038, 41042, 41046, 41052, 41056, 41060, 41064, 41068, + 41075, 41081, 41085, 41089, 41093, 41097, 41101, 41105, 41109, 41113, + 41117, 41121, 41125, 41131, 41135, 41139, 41143, 41147, 41151, 41155, + 41159, 41163, 41167, 41171, 41175, 41179, 41183, 41187, 41191, 41195, + 41200, 41206, 41211, 41216, 41220, 41224, 41228, 41232, 41236, 41240, + 41244, 41248, 41252, 41256, 41260, 41264, 41268, 41272, 41276, 41280, + 41284, 41288, 41292, 41296, 41300, 41303, 41307, 41311, 41317, 41321, + 41325, 41329, 41333, 41337, 41341, 41345, 41349, 41353, 41360, 41364, + 41368, 41372, 41376, 41380, 41384, 41388, 41392, 41396, 41400, 41404, + 41408, 41415, 41419, 41425, 41429, 41433, 41437, 41441, 41445, 41448, + 41452, 41456, 41460, 41464, 41468, 41472, 41476, 41480, 41484, 41488, + 41492, 41496, 41500, 41504, 41508, 41512, 41516, 41520, 41524, 41528, + 41532, 41536, 41540, 41544, 41548, 41552, 41556, 41560, 41564, 41568, + 41572, 41576, 41582, 41586, 41590, 41594, 41598, 41602, 41606, 41610, + 41614, 41618, 41622, 41626, 41630, 41634, 41638, 41642, 41646, 41650, + 41654, 41658, 41662, 41666, 41670, 41674, 41678, 41682, 41686, 41690, + 41697, 41701, 41705, 41709, 41713, 41717, 41723, 41727, 41731, 41735, + 41739, 41743, 41747, 41751, 41755, 41759, 41763, 41767, 41771, 41775, + 41781, 41785, 41789, 41793, 41797, 41801, 41805, 41809, 41813, 41817, + 41821, 41825, 41829, 41833, 41837, 41841, 41845, 41849, 41853, 41857, + 41861, 41865, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 41869, 41876, 41883, 41893, 41903, 41910, 41919, 41928, + 41938, 41949, 41959, 41970, 0, 0, 0, 0, 41976, 41979, 41982, 41985, + 41988, 41995, 41999, 42003, 42007, 42010, 42013, 42017, 42021, 42025, + 42029, 42034, 42039, 42044, 42049, 42052, 42055, 42061, 42067, 42072, + 42077, 42084, 42091, 42095, 42099, 42103, 42110, 42116, 42124, 42129, + 42133, 42137, 42141, 42145, 42149, 42153, 42157, 42161, 42165, 42171, + 42177, 42183, 42189, 42196, 42202, 42206, 42212, 42223, 42232, 42246, + 42255, 42259, 42267, 42272, 42277, 42282, 42287, 42290, 42295, 42300, 0, + 42306, 42310, 42313, 42317, 42320, 42324, 42327, 42331, 42334, 42338, + 42341, 42344, 42348, 42352, 42356, 42360, 42364, 42368, 42372, 42376, + 42380, 42384, 42388, 42392, 42396, 42400, 42404, 42408, 42412, 42416, + 42420, 42424, 42428, 42432, 42436, 42441, 42445, 42449, 42453, 42457, + 42460, 42464, 42468, 42472, 42476, 42480, 42484, 42488, 42492, 42496, + 42500, 42504, 42508, 42512, 42516, 42520, 42524, 42528, 42532, 42536, + 42540, 42544, 42547, 42551, 42555, 42559, 42563, 42567, 42570, 42575, + 42579, 42584, 42588, 42592, 42596, 42600, 42604, 42608, 42613, 42617, + 42621, 42625, 42629, 42632, 42636, 42640, 0, 0, 42645, 42653, 42661, + 42668, 42675, 42679, 42685, 42690, 42695, 42699, 42702, 42706, 42709, + 42713, 42716, 42720, 42723, 42727, 42730, 42733, 42737, 42741, 42745, + 42749, 42753, 42757, 42761, 42765, 42769, 42773, 42777, 42781, 42785, + 42789, 42793, 42797, 42801, 42805, 42809, 42813, 42817, 42821, 42825, + 42830, 42834, 42838, 42842, 42846, 42849, 42853, 42857, 42861, 42865, + 42869, 42873, 42877, 42881, 42885, 42889, 42893, 42897, 42901, 42905, + 42909, 42913, 42917, 42921, 42925, 42929, 42933, 42936, 42940, 42944, + 42948, 42952, 42956, 42959, 42964, 42968, 42973, 42977, 42981, 42985, + 42989, 42993, 42997, 43002, 43006, 43010, 43014, 43018, 43021, 43025, + 43029, 43034, 43038, 43042, 43046, 43050, 43054, 43061, 43065, 43071, 0, + 0, 0, 0, 0, 43076, 43079, 43082, 43085, 43088, 43091, 43094, 43097, + 43100, 43103, 43106, 43109, 43112, 43115, 43118, 43122, 43126, 43130, + 43133, 43136, 43139, 43142, 43145, 43148, 43151, 43155, 43159, 43163, + 43167, 43171, 43175, 43179, 43183, 43187, 43191, 43194, 43197, 43201, + 43204, 43208, 0, 0, 0, 0, 43212, 43216, 43220, 43224, 43228, 43232, + 43236, 43240, 43244, 43248, 43252, 43256, 43260, 43264, 43268, 43272, + 43276, 43280, 43284, 43288, 43292, 43296, 43300, 43304, 43308, 43312, + 43316, 43320, 43324, 43328, 43332, 43335, 43339, 43342, 43346, 43350, + 43353, 43357, 43361, 43364, 43368, 43372, 43376, 43380, 43383, 43387, + 43391, 43395, 43399, 43403, 43407, 43410, 43413, 43417, 43421, 43425, + 43429, 43433, 43437, 43441, 43445, 43449, 43453, 43457, 43461, 43465, + 43469, 43473, 43477, 43481, 43485, 43489, 43493, 43497, 43501, 43505, + 43509, 43513, 43517, 43521, 43525, 43529, 43533, 43537, 43541, 43545, + 43549, 43553, 43557, 43561, 43565, 43569, 43573, 43577, 0, 43581, 43587, + 43593, 43599, 43604, 43610, 43616, 43622, 43628, 43634, 43640, 43646, + 43652, 43658, 43664, 43670, 43676, 43680, 43684, 43688, 43692, 43696, + 43700, 43704, 43708, 43712, 43716, 43720, 43724, 43728, 43732, 43736, + 43740, 43744, 43748, 43752, 43756, 43760, 43764, 43768, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 43772, 43777, 43782, 43787, 43791, 43796, 43801, 43806, 43811, + 43816, 43821, 43826, 43831, 43836, 43841, 43846, 43851, 43855, 43859, + 43863, 43867, 43871, 43875, 43879, 43883, 43887, 43891, 43895, 43899, + 43903, 43907, 43912, 43917, 43922, 43927, 43932, 43937, 43942, 43947, + 43952, 43957, 43962, 43967, 43972, 43977, 0, 0, 0, 43982, 43985, 43988, + 43991, 43994, 43997, 44000, 44003, 44006, 44009, 44013, 44017, 44021, + 44025, 44029, 44033, 44037, 44041, 44045, 44049, 44053, 44057, 44061, + 44065, 44069, 44073, 44077, 44081, 44085, 44089, 44093, 44097, 44101, + 44105, 44109, 44113, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44117, 44122, + 44127, 44132, 44137, 44142, 44147, 44152, 44157, 44162, 44166, 44171, + 44176, 44181, 44186, 44191, 44195, 44199, 44203, 44207, 44211, 44215, + 44219, 44223, 44227, 44231, 44235, 44239, 44243, 44247, 44252, 44257, + 44262, 44267, 44272, 44277, 44282, 44287, 44292, 44297, 44302, 44307, + 44312, 0, 0, 0, 44317, 44322, 44325, 44328, 44331, 44334, 44337, 44340, + 44343, 44346, 44349, 44353, 44357, 44361, 44365, 44369, 44373, 44377, + 44381, 44385, 44389, 44393, 44397, 44401, 44405, 44409, 44413, 44417, + 44421, 44425, 44429, 44433, 44437, 44441, 44445, 44449, 44453, 44457, + 44461, 44465, 44469, 44473, 44476, 44480, 44484, 44488, 44492, 44496, + 44500, 44504, 44508, 44513, 44518, 44523, 44528, 44532, 44537, 44542, + 44547, 44552, 44557, 44562, 44567, 44572, 44577, 44581, 44587, 44593, + 44599, 44605, 44611, 44617, 44623, 44629, 44635, 44641, 44647, 0, 0, 0, + 0, 44653, 44656, 44659, 44662, 44665, 44668, 44671, 44675, 44679, 44683, + 44687, 44691, 44695, 44699, 44703, 44707, 44711, 44715, 44719, 44723, + 44726, 44730, 44734, 44738, 44742, 44746, 44750, 44754, 44758, 44762, + 44766, 44769, 44773, 44777, 44781, 44785, 44788, 44792, 44796, 44800, + 44804, 44808, 44812, 44816, 44820, 44824, 44828, 0, 44832, 44835, 44838, + 44841, 44844, 44847, 44850, 44853, 44856, 44859, 44862, 44865, 44868, + 44871, 44874, 44877, 44880, 44883, 44886, 44889, 44892, 44895, 44898, + 44901, 44904, 44907, 44910, 44913, 44916, 44919, 44922, 44925, 44928, + 44931, 44934, 44937, 44940, 44943, 44946, 44949, 44952, 44955, 44958, + 44961, 44964, 44967, 44970, 44973, 44976, 44979, 44982, 44985, 44988, + 44991, 44994, 44997, 45000, 45003, 45006, 45009, 45012, 45015, 45018, + 45021, 45024, 45027, 45030, 45033, 45036, 45039, 45042, 45045, 45048, + 45051, 45054, 45057, 45060, 45063, 45066, 45069, 45072, 45075, 45078, + 45081, 45084, 45087, 45090, 45093, 45096, 45104, 45111, 45118, 45125, + 45132, 45139, 45146, 45153, 45160, 45167, 45175, 45183, 45191, 45199, + 45207, 45215, 45223, 45231, 45239, 45247, 45255, 45263, 45271, 45279, + 45287, 45290, 45293, 45296, 45298, 45301, 0, 0, 0, 0, 45304, 45311, + 45318, 45325, 45332, 45335, 45340, 45343, 45347, 45349, 45351, 45354, + 45357, 45360, 45363, 45366, 45369, 45372, 45376, 45380, 45383, 45386, + 45389, 45392, 45395, 45398, 45401, 45405, 45408, 45411, 45414, 45417, + 45420, 45424, 45427, 45430, 45433, 45438, 45443, 45448, 45453, 45458, + 45463, 45468, 45473, 45479, 45488, 45491, 45494, 45497, 45500, 45503, + 45509, 45518, 45521, 45524, 45528, 45531, 45534, 45537, 45541, 45544, + 45547, 45552, 45555, 45558, 45562, 45565, 45568, 45573, 45578, 45583, + 45586, 45589, 45592, 45595, 45602, 45605, 45608, 45611, 45614, 45617, + 45620, 45623, 45628, 45631, 45634, 45637, 45640, 45643, 45648, 45651, + 45654, 45657, 45660, 45663, 45666, 45669, 45672, 0, 0, 45675, 45682, + 45689, 45696, 45703, 45710, 45717, 45724, 45731, 45738, 45746, 45754, + 45762, 45770, 45778, 45786, 45794, 45802, 45810, 45818, 45826, 45834, + 45842, 45850, 45858, 45866, 45874, 45882, 45890, 45898, 45906, 0, 45914, + 45918, 45922, 45925, 45929, 45933, 45937, 45941, 45945, 45949, 45953, + 45956, 45960, 45964, 45968, 45972, 45976, 45980, 45983, 45987, 45991, + 45994, 45998, 46002, 46006, 46010, 46014, 46018, 46022, 46026, 46030, + 46034, 46038, 46042, 46046, 46050, 46054, 46058, 46062, 46066, 46070, + 46074, 46078, 46082, 46086, 46090, 46094, 46098, 46102, 46106, 46110, + 46114, 46118, 46122, 46126, 46130, 46134, 46138, 46142, 46146, 46150, + 46154, 46158, 46162, 46166, 46170, 46174, 46178, 46182, 46186, 46190, + 46194, 46198, 46202, 46206, 46210, 46214, 46218, 46222, 46226, 46230, + 46234, 46238, 46242, 46246, 46250, 46254, 46258, 46262, 46266, 46270, + 46274, 46278, 46282, 46286, 46290, 46294, 46298, 46302, 46306, 46310, + 46314, 46318, 46322, 46326, 46330, 46334, 46338, 46342, 46346, 46350, + 46354, 46358, 46362, 46366, 46370, 46374, 46378, 46382, 46386, 46390, + 46394, 46398, 46402, 46406, 46410, 46414, 46418, 46422, 46426, 46430, + 46434, 46438, 46442, 46446, 46450, 46454, 46458, 46462, 46466, 46470, + 46474, 46478, 46482, 46486, 46490, 46494, 46498, 46502, 46506, 46510, + 46514, 46518, 46522, 46526, 46530, 46534, 46538, 46542, 46546, 46550, + 46554, 46558, 46562, 46566, 46570, 46574, 46578, 46582, 46586, 46590, + 46594, 46598, 46602, 46606, 46610, 46614, 46618, 46622, 46626, 46630, + 46634, 46638, 46642, 46645, 46649, 46653, 46657, 46661, 46665, 46669, + 46673, 46677, 46681, 46685, 46689, 46693, 46697, 46701, 46705, 46709, + 46713, 46717, 46721, 46725, 46729, 46733, 46737, 46741, 46745, 46749, + 46753, 46757, 46761, 46765, 46769, 46773, 46777, 46781, 46785, 46789, + 46793, 46797, 46801, 46805, 46809, 46813, 46817, 46821, 46825, 46829, + 46833, 46837, 46841, 46845, 46849, 46853, 46857, 46861, 46865, 46869, + 46873, 46877, 46881, 46885, 46889, 46893, 46897, 46901, 46905, 46909, + 46913, 46917, 46921, 46925, 46929, 46933, 46937, 46941, 46945, 46949, + 46953, 46957, 46961, 46965, 46969, 46973, 46977, 46981, 46985, 46988, + 46992, 46996, 47000, 47004, 47008, 47012, 47016, 47020, 47024, 47028, + 47032, 47036, 47040, 47044, 47048, 47052, 47056, 47060, 47064, 47068, + 47072, 47076, 47080, 47084, 47088, 47092, 47096, 47100, 47104, 47107, + 47111, 47115, 47119, 47123, 47127, 47131, 47135, 47139, 47143, 47147, + 47151, 47155, 47159, 47163, 47167, 47171, 47175, 47179, 47183, 47187, + 47191, 47195, 47199, 47203, 47207, 47211, 47215, 47219, 47223, 47227, + 47231, 47235, 47239, 47243, 47247, 47251, 47255, 47259, 47263, 47267, + 47271, 47275, 47279, 47283, 47287, 47291, 47295, 47299, 47303, 47307, + 47311, 47315, 47319, 47323, 47327, 47331, 47335, 47339, 47343, 47347, + 47351, 47355, 47359, 47363, 47367, 47371, 47375, 47379, 47383, 47387, + 47391, 47395, 47399, 47403, 47407, 47411, 47415, 47419, 47423, 47427, + 47431, 47435, 47439, 47443, 47447, 47451, 47455, 47459, 47463, 47467, + 47471, 47475, 47479, 47483, 47487, 47491, 47495, 47499, 47503, 47507, + 47511, 47515, 47519, 47523, 47527, 47531, 47535, 47539, 47543, 47547, + 47551, 47555, 47559, 47563, 47567, 47571, 47575, 47579, 47583, 47587, + 47591, 47595, 47599, 47603, 47607, 47611, 47615, 47619, 47623, 47627, + 47631, 47635, 47639, 47643, 47647, 47651, 47655, 47659, 47663, 47667, + 47671, 47675, 47679, 47683, 47687, 47691, 47695, 47699, 47703, 47707, + 47711, 47715, 47719, 47723, 47727, 47731, 47735, 47739, 47743, 47747, + 47751, 47755, 47759, 47763, 47767, 47771, 47775, 47779, 47783, 47787, + 47791, 47795, 47799, 47803, 47807, 47811, 47815, 47819, 47823, 47827, + 47831, 47835, 47839, 47843, 47847, 47851, 47855, 47859, 47863, 47867, + 47871, 47875, 47879, 47883, 47887, 47891, 47895, 47899, 47903, 47907, + 47911, 47915, 47919, 47923, 47927, 47931, 47935, 47939, 47943, 47947, + 47951, 47955, 47959, 47962, 47966, 47970, 47974, 47978, 47982, 47986, + 47990, 47994, 47998, 48002, 48006, 48010, 48014, 48018, 48022, 48026, + 48030, 48034, 48038, 48042, 48046, 48050, 48054, 48058, 48062, 48066, + 48070, 48074, 48078, 48082, 48086, 48090, 48094, 48098, 48102, 48106, + 48110, 48114, 48118, 48122, 48126, 48130, 48134, 48138, 48142, 48146, + 48150, 48154, 48158, 48162, 48166, 48170, 48174, 48178, 48182, 48186, + 48190, 48194, 48198, 48202, 48206, 48210, 48214, 48218, 48222, 48226, + 48230, 48234, 48238, 48242, 48246, 48250, 48254, 48258, 48262, 48266, + 48270, 48274, 48278, 48282, 48286, 48290, 48294, 48298, 48302, 48306, + 48310, 48314, 48318, 48322, 48326, 48330, 48334, 48338, 48342, 48346, + 48350, 48354, 48358, 48362, 48366, 48370, 48374, 48378, 48382, 48386, + 48390, 48394, 48398, 48402, 48406, 48410, 48414, 48418, 48422, 48426, + 48430, 48434, 48438, 48442, 48446, 48450, 48454, 48458, 48462, 48466, + 48470, 48474, 48478, 48482, 48486, 48490, 48494, 48498, 48502, 48506, + 48510, 48514, 48518, 48522, 48526, 48530, 48534, 48538, 48542, 48546, + 48550, 48554, 48558, 48562, 48566, 48570, 48574, 48578, 48582, 48586, + 48590, 48594, 48598, 48602, 48606, 48610, 48614, 48618, 48622, 48626, + 48630, 48634, 48638, 48642, 48646, 48650, 48654, 48658, 48662, 48666, + 48670, 48674, 48678, 48682, 48686, 48690, 48694, 48698, 48702, 48706, + 48710, 48714, 48718, 48722, 48726, 48730, 48734, 48738, 48742, 48746, + 48750, 48754, 48758, 48762, 48766, 48770, 48774, 48778, 48782, 48786, + 48790, 48794, 48798, 48802, 48806, 48810, 48814, 48818, 48822, 48826, + 48830, 48834, 48838, 48842, 48846, 48850, 48854, 48858, 48862, 48866, + 48870, 48874, 48878, 48882, 48886, 48890, 48894, 48898, 48902, 48906, + 48910, 48914, 48918, 48922, 48926, 48930, 48934, 48938, 48942, 48946, + 48950, 48954, 48958, 48962, 48966, 48970, 48974, 48978, 48982, 48986, + 48990, 48994, 48998, 49002, 49006, 49010, 49014, 49018, 49021, 49025, + 49029, 49033, 49037, 49041, 49045, 49049, 49053, 49057, 49061, 49065, + 49069, 49073, 49077, 49081, 49085, 49089, 49093, 49097, 49101, 49105, + 49109, 49113, 49117, 49121, 49125, 49129, 49133, 49137, 49141, 49145, + 49149, 49153, 49157, 49161, 49165, 49169, 49173, 49177, 49181, 49185, + 49189, 49193, 49197, 49201, 49205, 49209, 49213, 49217, 49221, 49225, + 49229, 49233, 49237, 49241, 49245, 49249, 49253, 49257, 49261, 49265, + 49269, 49273, 49277, 49281, 49285, 49289, 49293, 49297, 49301, 49305, + 49309, 49313, 49317, 49321, 49325, 49329, 49333, 49337, 49341, 49345, + 49349, 49353, 49357, 49361, 49365, 49369, 49373, 49377, 49381, 49385, + 49389, 49393, 49397, 49401, 49405, 49409, 49413, 49417, 49421, 49425, + 49429, 49433, 49437, 49441, 49445, 49449, 49453, 49457, 49461, 49465, + 49469, 49473, 49477, 49481, 49485, 49489, 49493, 49497, 49501, 49505, + 49509, 49513, 49517, 49521, 49525, 49529, 49533, 49537, 49541, 49545, + 49549, 49553, 49557, 49561, 49565, 49569, 49573, 49577, 49581, 49585, + 49589, 49593, 49597, 49601, 49605, 49609, 49613, 49617, 49621, 49625, + 49629, 49633, 49637, 49641, 49645, 49649, 49653, 49657, 49661, 49665, + 49669, 49673, 49677, 49681, 49685, 49689, 49693, 49697, 49701, 49705, + 49709, 49713, 49717, 49721, 49725, 49729, 49733, 49737, 49741, 49745, + 49749, 49753, 49757, 49761, 49765, 49769, 49773, 49777, 49781, 49785, + 49789, 49793, 49797, 49801, 49805, 49809, 49813, 49817, 49821, 49825, + 49829, 49833, 49837, 49841, 49845, 49849, 49853, 49857, 49861, 49865, + 49869, 49873, 49877, 49881, 49885, 49889, 49893, 49897, 49901, 49905, + 49909, 49913, 49917, 49921, 49925, 49929, 49933, 49937, 49941, 49945, + 49949, 49953, 49957, 49961, 49965, 49969, 49973, 49977, 49981, 49985, + 49989, 49993, 49997, 50001, 50005, 50009, 50013, 50017, 50021, 50025, + 50029, 50033, 50037, 50041, 50045, 50049, 50053, 50057, 50061, 50065, + 50069, 50073, 50077, 50081, 50085, 50089, 50093, 50097, 50101, 50105, + 50109, 50113, 50117, 50121, 50125, 50129, 50133, 50137, 50141, 50145, + 50149, 50153, 50157, 50161, 50165, 50169, 50173, 50177, 50181, 50185, + 50189, 50193, 50197, 50201, 50205, 50209, 50213, 50217, 50221, 50225, + 50229, 50233, 50237, 50241, 50245, 50249, 50253, 50257, 50261, 50265, + 50269, 50273, 50277, 50281, 50285, 50289, 50293, 50297, 50301, 50305, + 50309, 50313, 50317, 50321, 50325, 50329, 50333, 50337, 50341, 50345, + 50349, 50353, 50357, 50361, 50365, 50369, 50373, 50377, 50381, 50385, + 50389, 50393, 50397, 50401, 50405, 50409, 50413, 50417, 50421, 50425, + 50429, 50433, 50437, 50441, 50445, 50449, 50453, 50457, 50461, 50465, + 50469, 50473, 50477, 50481, 50485, 50489, 50493, 50497, 50501, 50505, + 50509, 50513, 50517, 50521, 50525, 50529, 50533, 50537, 50541, 50545, + 50549, 50553, 50557, 50561, 0, 0, 0, 50565, 50569, 50573, 50577, 50581, + 50585, 50589, 50593, 50597, 50601, 50605, 50609, 50613, 50617, 50621, + 50625, 50629, 50633, 50637, 50641, 50645, 50649, 50653, 50657, 50661, + 50665, 50669, 50673, 50677, 50681, 50685, 50689, 50693, 50697, 50701, + 50705, 50709, 50713, 50717, 50721, 50725, 50729, 50733, 50737, 50741, + 50745, 50749, 50753, 50757, 50761, 50765, 50769, 50773, 50777, 50781, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 50785, 50789, 50793, 50797, 50801, 50805, 50809, + 50813, 50817, 50821, 50825, 50829, 50833, 50837, 50841, 50845, 50849, + 50853, 50857, 50861, 50865, 50869, 50873, 50877, 50881, 50885, 50889, + 50893, 50897, 50901, 50905, 50909, 50913, 50917, 50921, 50925, 50929, + 50933, 50937, 50941, 50945, 50949, 50953, 50957, 50961, 50965, 50969, + 50973, 50977, 50981, 50985, 50989, 50993, 50997, 51001, 51005, 51009, + 51013, 51017, 51021, 51025, 51029, 51033, 51037, 51041, 51045, 51049, + 51053, 51057, 51061, 51065, 51069, 51073, 51077, 51081, 51085, 51089, + 51093, 51097, 51101, 51105, 51109, 51113, 51117, 51121, 51125, 51129, + 51133, 51137, 51141, 51145, 51149, 51153, 51157, 51161, 51165, 51169, + 51173, 51177, 51181, 51185, 51189, 51193, 51197, 51201, 51205, 51209, + 51213, 51217, 51221, 51225, 51229, 51233, 51237, 51241, 51245, 51249, + 51253, 51257, 51261, 51265, 51269, 51273, 51277, 51281, 51285, 51289, + 51293, 51297, 51301, 51305, 51309, 51313, 51317, 51321, 51325, 51329, + 51333, 51337, 51341, 51345, 51349, 51353, 51357, 51361, 51365, 51369, + 51373, 51377, 51381, 51385, 51389, 51393, 51397, 51401, 51405, 51409, + 51413, 51417, 51421, 51425, 51429, 51433, 51437, 51441, 51445, 51449, + 51453, 51457, 51461, 51465, 51469, 51473, 51477, 51481, 51485, 51489, + 51493, 51497, 51501, 51505, 51509, 51513, 51517, 51521, 51525, 51529, + 51533, 51537, 51541, 51545, 51549, 51553, 51557, 51561, 51565, 51569, + 51573, 51577, 51581, 51585, 51589, 51593, 51597, 51601, 51605, 51609, + 51613, 51617, 51621, 51625, 51629, 51633, 51637, 51641, 51645, 51649, + 51653, 51657, 51661, 51665, 51669, 51673, 51677, 51681, 51685, 51689, + 51693, 51697, 51701, 51705, 51709, 51713, 51717, 51721, 51725, 51729, + 51733, 51737, 51741, 51745, 51749, 51753, 51757, 51761, 51765, 51769, + 51773, 51777, 51781, 51785, 51789, 51793, 51797, 51801, 51805, 51809, + 51813, 51817, 51821, 51825, 51829, 51833, 51837, 51841, 51845, 51849, + 51853, 51857, 51861, 51865, 51869, 51873, 51877, 51881, 51885, 51889, + 51893, 51897, 51901, 51905, 51909, 51913, 51917, 51921, 51925, 51929, + 51933, 51937, 51941, 51945, 51949, 51953, 51957, 51961, 51965, 51969, + 51973, 51977, 51981, 51985, 51989, 0, 0, 51993, 51997, 52001, 52005, + 52009, 52013, 52017, 52021, 52025, 52029, 52033, 52037, 52041, 52045, + 52049, 52053, 52057, 52061, 52065, 52069, 52073, 52077, 52081, 52085, + 52089, 52093, 52097, 52101, 52105, 52109, 52113, 52117, 52121, 52125, + 52129, 52133, 52137, 52141, 52145, 52149, 52153, 52157, 52161, 52165, + 52169, 52173, 52177, 52181, 52185, 52189, 52193, 52197, 52201, 52205, + 52209, 52213, 52217, 52221, 52225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52229, 52234, 52239, + 52244, 52249, 52254, 52261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52266, + 52273, 52280, 52287, 52294, 0, 0, 0, 0, 0, 52301, 52308, 52315, 52325, + 52331, 52337, 52343, 52349, 52355, 52361, 52368, 52374, 52380, 52387, + 52395, 52403, 52414, 52425, 52431, 52437, 52443, 52450, 52457, 52464, + 52471, 52478, 0, 52485, 52492, 52499, 52507, 52514, 0, 52521, 0, 52528, + 52535, 0, 52542, 52550, 0, 52557, 52564, 52571, 52578, 52585, 52592, + 52599, 52606, 52613, 52620, 52625, 52632, 52639, 52645, 52651, 52657, + 52663, 52669, 52675, 52681, 52687, 52693, 52699, 52705, 52711, 52717, + 52723, 52729, 52735, 52741, 52747, 52753, 52759, 52765, 52771, 52777, + 52783, 52789, 52795, 52801, 52807, 52813, 52819, 52825, 52831, 52837, + 52843, 52849, 52855, 52861, 52867, 52873, 52879, 52885, 52891, 52897, + 52903, 52909, 52915, 52921, 52927, 52933, 52939, 52945, 52951, 52957, + 52963, 52969, 52975, 52981, 52987, 52993, 52999, 53005, 53011, 53017, + 53023, 53029, 53035, 53041, 53047, 53053, 53059, 53065, 53071, 53077, + 53083, 53089, 53095, 53102, 53109, 53115, 53121, 53127, 53133, 53141, + 53149, 53156, 53163, 53170, 53177, 53184, 53191, 53198, 53205, 53212, + 53219, 53229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53239, 53245, 53251, 53257, 53263, + 53268, 53273, 53279, 53285, 53291, 53297, 53305, 53311, 53317, 53325, + 53333, 53341, 53349, 53354, 53359, 53364, 53369, 53381, 53393, 53403, + 53413, 53424, 53435, 53446, 53457, 53467, 53477, 53488, 53499, 53510, + 53521, 53531, 53541, 53551, 53566, 53581, 53596, 53603, 53610, 53617, + 53624, 53634, 53644, 53654, 53665, 53675, 53683, 53691, 53699, 53707, + 53716, 53724, 53731, 53738, 53745, 53752, 53760, 53767, 53775, 53783, + 53792, 53800, 53807, 53814, 53821, 53828, 53835, 53842, 53849, 53856, + 53863, 53870, 53877, 53885, 53893, 53901, 53909, 53917, 53925, 53933, + 53941, 53949, 53957, 53965, 53973, 53981, 53989, 53997, 54005, 54013, + 54022, 54030, 54038, 54046, 54055, 54063, 54071, 54079, 54087, 54095, + 54103, 54111, 54120, 54128, 54135, 54142, 54149, 54156, 54164, 54171, + 54178, 54185, 54192, 54199, 54207, 54214, 54221, 54228, 54235, 54242, + 54250, 54257, 54264, 54271, 54279, 54286, 54293, 54300, 54307, 54314, + 54322, 54329, 54339, 54349, 54359, 54368, 54377, 54386, 54395, 54404, + 54414, 54425, 54436, 54446, 54456, 54467, 54477, 54486, 54495, 54503, + 54511, 54520, 54528, 54536, 54544, 54551, 54558, 54566, 54573, 54582, + 54591, 54599, 54607, 54616, 54624, 54633, 54641, 54650, 54658, 54666, + 54674, 54682, 54691, 54699, 54706, 54714, 54721, 54728, 54735, 54743, + 54751, 54758, 54765, 54773, 54780, 54790, 54798, 54806, 54813, 54820, + 54828, 54835, 54845, 54855, 54865, 54875, 54885, 54893, 54901, 54909, + 54917, 54925, 54932, 54939, 54946, 54953, 54960, 54968, 54975, 54982, + 54989, 54996, 55003, 55010, 55017, 55024, 55031, 55038, 55046, 55054, + 55062, 55070, 55078, 55086, 55094, 55102, 55110, 55118, 55126, 55134, + 55142, 55150, 55158, 55166, 55174, 55182, 55190, 55198, 55206, 55214, + 55222, 55230, 55237, 55244, 55251, 55258, 55265, 55272, 55279, 55286, + 55293, 55300, 55307, 55314, 55321, 55328, 55335, 55342, 55351, 55358, + 55365, 55372, 55379, 55386, 55396, 55406, 55414, 55422, 55429, 55436, + 55444, 55452, 55459, 55466, 55473, 55480, 55488, 55496, 55503, 55510, + 55517, 55524, 55531, 55540, 55549, 55558, 55567, 55575, 55584, 55592, + 55601, 55609, 55617, 55624, 55632, 55639, 55647, 55654, 55662, 55669, + 55677, 55684, 55693, 55701, 55710, 55718, 55725, 55732, 55739, 55746, + 55754, 55762, 55771, 55780, 55789, 55797, 55806, 55814, 55823, 55831, + 55839, 55846, 55854, 55861, 55869, 55876, 55884, 55891, 55899, 55906, + 55915, 55923, 55932, 55940, 55947, 55954, 55961, 55968, 55976, 55984, + 55993, 56002, 56009, 56016, 56023, 56030, 56037, 56044, 56052, 56059, + 56066, 56073, 56080, 56087, 56094, 56102, 56110, 56118, 56126, 56131, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56136, 56145, 56154, 56163, + 56172, 56181, 56190, 56199, 56208, 56217, 56226, 56235, 56245, 56254, + 56263, 56273, 56282, 56291, 56300, 56309, 56318, 56328, 56338, 56348, + 56357, 56366, 56375, 56384, 56393, 56402, 56411, 56422, 56432, 56442, + 56452, 56462, 56472, 56482, 56492, 56502, 56512, 56523, 56533, 56543, + 56554, 56564, 56574, 56584, 56594, 56603, 56612, 56622, 56631, 56640, + 56649, 56658, 56667, 56676, 56685, 56694, 56703, 56712, 56721, 56730, 0, + 0, 56739, 56748, 56757, 56766, 56775, 56785, 56794, 56803, 56813, 56822, + 56832, 56841, 56850, 56860, 56869, 56879, 56888, 56898, 56907, 56917, + 56926, 56936, 56946, 56956, 56966, 56975, 56985, 56994, 57003, 57012, + 57021, 57030, 57039, 57049, 57058, 57068, 57077, 57087, 57097, 57106, + 57115, 57124, 57134, 57143, 57152, 57161, 57170, 57179, 57189, 57199, + 57209, 57219, 57229, 57238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 57247, 57262, 57277, 57283, 57289, 57295, 57301, 57307, 57313, 57319, + 57325, 57333, 57337, 0, 0, 0, 57340, 57344, 57348, 57352, 57356, 57360, + 57364, 57368, 57372, 57376, 57380, 57384, 57388, 57392, 57396, 57400, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57404, 57409, 57414, 57420, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57426, 57436, 57446, 57456, 57466, + 57478, 57487, 57496, 57506, 57516, 57528, 57540, 57551, 57562, 57572, + 57582, 57591, 57600, 57610, 57620, 57631, 57642, 57645, 0, 0, 57649, + 57653, 57657, 57661, 57666, 57672, 57678, 57684, 57687, 57691, 0, 57694, + 57697, 57700, 57704, 57708, 57713, 57717, 57721, 57726, 57731, 57738, + 57745, 57748, 57751, 57754, 57758, 57761, 57764, 57767, 0, 57771, 57776, + 57780, 57784, 0, 0, 0, 0, 57789, 57794, 57801, 57806, 57811, 0, 57816, + 57821, 57826, 57831, 57836, 57841, 57846, 57851, 57856, 57861, 57866, + 57871, 57880, 57889, 57897, 57905, 57914, 57923, 57932, 57941, 57949, + 57957, 57965, 57973, 57978, 57983, 57989, 57995, 58001, 58007, 58014, + 58021, 58026, 58031, 58036, 58041, 58047, 58053, 58059, 58065, 58070, + 58075, 58080, 58085, 58090, 58095, 58100, 58105, 58110, 58115, 58120, + 58125, 58131, 58137, 58143, 58149, 58155, 58161, 58167, 58173, 58178, + 58183, 58188, 58193, 58198, 58203, 58208, 58213, 58219, 58225, 58231, + 58237, 58243, 58249, 58255, 58261, 58267, 58273, 58279, 58285, 58291, + 58297, 58303, 58309, 58315, 58321, 58327, 58333, 58339, 58345, 58351, + 58357, 58363, 58369, 58375, 58381, 58387, 58393, 58399, 58405, 58411, + 58417, 58423, 58429, 58434, 58439, 58444, 58449, 58454, 58459, 58464, + 58469, 58474, 58479, 58484, 58489, 58494, 58499, 58504, 58509, 58515, + 58521, 58527, 58533, 58538, 58543, 58548, 58553, 58564, 58575, 58585, + 58595, 58606, 58617, 58624, 0, 0, 58631, 0, 58639, 58643, 58647, 58650, + 58654, 58658, 58661, 58664, 58668, 58672, 58675, 58679, 58682, 58685, + 58688, 58691, 58695, 58698, 58701, 58704, 58707, 58710, 58713, 58716, + 58719, 58722, 58725, 58728, 58731, 58735, 58738, 58742, 58747, 58752, + 58757, 58762, 58767, 58772, 58777, 58782, 58787, 58792, 58797, 58802, + 58807, 58812, 58817, 58822, 58827, 58832, 58837, 58842, 58847, 58852, + 58857, 58862, 58867, 58872, 58877, 58881, 58886, 58890, 58894, 58899, + 58904, 58909, 58914, 58919, 58924, 58929, 58934, 58939, 58944, 58949, + 58954, 58959, 58964, 58969, 58974, 58979, 58984, 58989, 58994, 58999, + 59004, 59009, 59014, 59019, 59024, 59029, 59034, 59039, 59043, 59048, + 59050, 59055, 59060, 59064, 59069, 59074, 59078, 59083, 59088, 59093, + 59098, 59103, 59108, 59113, 59118, 59124, 59130, 59136, 59144, 59148, + 59152, 59156, 59160, 59164, 59168, 59173, 59178, 59183, 59188, 59193, + 59198, 59203, 59208, 59213, 59218, 59223, 59228, 59233, 59237, 59242, + 59247, 59252, 59257, 59262, 59267, 59272, 59277, 59282, 59287, 59291, + 59296, 59301, 59306, 59311, 59315, 59320, 59325, 59330, 59335, 59340, + 59345, 59350, 59355, 59359, 59366, 59373, 59377, 59382, 59387, 59392, + 59397, 59402, 59407, 59412, 59417, 59422, 59427, 59432, 59437, 59442, + 59447, 59452, 59457, 59462, 59467, 59472, 59477, 59482, 59487, 59492, + 59497, 59502, 59507, 59512, 59517, 59522, 0, 0, 0, 59527, 59531, 59536, + 59540, 59545, 59550, 0, 0, 59554, 59559, 59564, 59568, 59573, 59578, 0, + 0, 59583, 59588, 59592, 59597, 59602, 59607, 0, 0, 59612, 59617, 59622, + 0, 0, 0, 59626, 59630, 59634, 59637, 59639, 59643, 59647, 0, 59651, + 59657, 59660, 59663, 59666, 59669, 59673, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 59677, 59683, 59689, 59695, 59701, 0, 0, 59705, 59709, 59714, 59719, + 59724, 59728, 59733, 59738, 59743, 59748, 59752, 59756, 59761, 59766, + 59771, 59776, 59780, 59785, 59790, 59795, 59800, 59805, 59810, 59814, + 59819, 59824, 59829, 59834, 59839, 59844, 59849, 0, 59854, 59858, 59862, + 59867, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59872, 59877, 59882, 59887, + 59892, 59897, 59902, 59907, 59912, 59917, 59922, 59927, 59932, 59937, + 59942, 59947, 59952, 59957, 59962, 59967, 59972, 59977, 59982, 59987, + 59992, 59997, 60002, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60009, 60014, + 60019, 60024, 60030, 60035, 60041, 60046, 60051, 60056, 60062, 60067, + 60073, 60078, 60083, 60088, 60093, 60097, 60102, 60107, 60112, 60117, + 60122, 60127, 60132, 60137, 60142, 60147, 60152, 60157, 60162, 60167, + 60172, 60177, 60182, 60187, 60192, 60197, 0, 0, 60202, 60207, 60212, + 60217, 60223, 60228, 60234, 60239, 60244, 60249, 60255, 60260, 60266, + 60271, 60276, 60281, 60286, 60290, 60295, 60300, 60305, 60310, 60315, + 60320, 60325, 60330, 60335, 60340, 60345, 60350, 60355, 60360, 60365, + 60370, 60375, 60380, 60385, 60390, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60395, + 60400, 60405, 60410, 60417, 60424, 60431, 60438, 60443, 60448, 60453, + 60458, 60465, 60470, 60477, 60484, 60489, 60494, 60499, 60506, 60511, + 60516, 60523, 60530, 60535, 60540, 60545, 60552, 60559, 60566, 60571, + 60576, 60583, 60590, 60597, 60604, 60609, 60614, 60619, 60626, 60631, + 60636, 60641, 60648, 60657, 60664, 60669, 60674, 60679, 60684, 60689, + 60694, 60703, 60710, 60715, 60722, 60729, 60734, 60739, 60744, 60751, + 60756, 60763, 60770, 60775, 60780, 60785, 60792, 60799, 60804, 60809, + 60816, 60823, 60830, 60835, 60840, 60845, 60850, 60857, 60866, 60875, + 60880, 60887, 60896, 60901, 60906, 60911, 60916, 60923, 60930, 60937, + 60944, 60949, 60954, 60959, 60966, 60973, 60980, 60985, 60990, 60997, + 61002, 61009, 61014, 61021, 61026, 61033, 61040, 61045, 61050, 61055, + 61060, 61065, 61070, 61075, 61080, 61085, 61092, 61099, 61106, 61113, + 61120, 61129, 61134, 61139, 61146, 61153, 61158, 61165, 61172, 61179, + 61186, 61193, 61200, 61205, 61210, 61215, 61220, 61225, 61234, 61243, + 61252, 61261, 61270, 61279, 61288, 61297, 61302, 61313, 61324, 61333, + 61338, 61343, 61348, 61353, 61362, 61369, 61376, 61383, 61390, 61397, + 61404, 61413, 61422, 61433, 61442, 61453, 61462, 61469, 61478, 61489, + 61498, 61507, 61516, 61525, 61532, 61539, 61546, 61555, 61564, 61575, + 61584, 61593, 61604, 61609, 61614, 61625, 61634, 61643, 61652, 61661, + 61672, 61681, 61690, 61701, 61712, 61723, 61734, 61745, 61756, 61763, + 61770, 61777, 61784, 61794, 61803, 61810, 61817, 61824, 61835, 61846, + 61857, 61868, 61879, 61890, 61901, 61912, 61919, 61926, 61935, 61944, + 61951, 61958, 61965, 61974, 61983, 61992, 61999, 62008, 62017, 62026, + 62033, 62040, 62045, 62052, 62059, 62066, 62073, 62080, 62087, 62094, + 62103, 62112, 62121, 62130, 62137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62146, + 62152, 62157, 62162, 62169, 62175, 62180, 62186, 62192, 62198, 62204, + 62210, 62214, 62218, 62224, 62230, 62236, 62240, 62245, 62250, 62254, + 62258, 62261, 62267, 62273, 62279, 62285, 62291, 62297, 62303, 62309, + 62315, 62325, 62335, 62341, 62347, 62357, 62367, 62373, 0, 0, 0, 62379, + 62384, 62389, 62395, 62401, 62407, 62413, 62419, 62425, 62433, 62441, + 62447, 62453, 62459, 62465, 62471, 62477, 62483, 62489, 62494, 62500, + 62506, 62512, 62518, 62524, 62534, 62540, 62546, 62553, 62560, 62567, + 62576, 62585, 62594, 62603, 62612, 62621, 62630, 62639, 62649, 62659, + 62667, 62675, 62684, 62693, 62699, 62705, 62711, 62717, 62725, 62733, + 62736, 62742, 62747, 62753, 62759, 62765, 62771, 62777, 62787, 62792, + 62799, 62804, 62809, 62814, 62820, 62826, 62832, 62838, 62843, 62848, + 62853, 62858, 62863, 62869, 62875, 62881, 62887, 62893, 62899, 62905, + 62911, 62916, 62921, 62926, 62931, 62936, 62941, 62946, 62951, 62957, + 62963, 62968, 62973, 62978, 62983, 62988, 62994, 63001, 63005, 63009, + 63012, 63016, 63020, 63024, 63028, 63032, 63040, 63050, 63054, 63058, + 63064, 63070, 63076, 63082, 63088, 63094, 63100, 63106, 63112, 63118, + 63124, 63130, 63136, 63142, 63146, 63150, 63157, 63163, 63169, 63175, + 63180, 63187, 63192, 63198, 63204, 63210, 63216, 63221, 63225, 63231, + 63235, 63239, 63243, 63249, 63255, 63259, 63265, 63271, 63277, 63283, + 63289, 63297, 63305, 63311, 63317, 63323, 63329, 63341, 63353, 63367, + 63379, 63391, 63405, 63419, 63433, 63437, 63445, 63453, 63457, 63461, + 63465, 63469, 63473, 63477, 63481, 63485, 63491, 63497, 63503, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 63509, 63513, 63517, 63521, 63525, 63529, 63533, + 63537, 63541, 63545, 63549, 63553, 63557, 63561, 63565, 63569, 63573, + 63577, 63581, 63585, 63589, 63593, 63597, 63601, 63605, 63609, 63613, + 63617, 63621, 63625, 63629, 63633, 63637, 63641, 63645, 63649, 63653, + 63657, 63661, 63665, 63669, 63673, 63677, 63681, 63685, 63689, 63693, + 63697, 63701, 63705, 63709, 63713, 63717, 63721, 63725, 63729, 63733, + 63737, 63741, 63745, 63749, 63753, 63757, 63761, 63765, 63769, 63773, + 63777, 63781, 63785, 63789, 63793, 63797, 63801, 63805, 63809, 63813, + 63817, 63821, 63825, 63829, 63833, 63837, 63841, 63845, 0, 63849, 63853, + 63857, 63861, 63865, 63869, 63873, 63877, 63881, 63885, 63889, 63893, + 63897, 63901, 63905, 63909, 63913, 63917, 63921, 63926, 63931, 63936, + 63941, 63946, 63951, 63956, 63961, 63966, 63971, 63976, 63981, 63986, + 63991, 63996, 64001, 64006, 64011, 64016, 64021, 64026, 64031, 64036, + 64041, 64046, 64051, 64056, 64061, 64066, 64071, 64076, 64081, 64086, + 64091, 64096, 64101, 64106, 64111, 64116, 64121, 64126, 64131, 64136, + 64141, 64146, 64151, 64156, 64161, 64166, 64171, 64176, 64181, 0, 64185, + 64189, 0, 0, 64193, 0, 0, 64197, 64201, 0, 0, 64205, 64209, 64213, 64217, + 0, 64221, 64225, 64229, 64233, 64237, 64241, 64245, 64249, 64253, 64257, + 64261, 64265, 0, 64269, 0, 64273, 64277, 64281, 64285, 0, 64289, 64293, + 0, 64297, 64301, 64305, 64309, 64313, 64317, 64321, 64325, 64329, 64333, + 64337, 64341, 64346, 64351, 64356, 64361, 64366, 64371, 64376, 64381, + 64386, 64391, 64396, 64401, 64406, 64411, 64416, 64421, 64426, 64431, + 64436, 64441, 64446, 64451, 64456, 64461, 64466, 64471, 64476, 64481, + 64486, 64491, 64496, 64501, 64506, 64511, 64516, 64521, 64526, 64531, + 64536, 64541, 64546, 64551, 64556, 64561, 64566, 64571, 64576, 64581, + 64586, 64591, 64596, 64601, 64605, 0, 64609, 64613, 64617, 64621, 0, 0, + 64625, 64629, 64633, 64637, 64641, 64645, 64649, 64653, 0, 64657, 64661, + 64665, 64669, 64673, 64677, 64681, 0, 64685, 64689, 64693, 64697, 64701, + 64705, 64709, 64713, 64717, 64721, 64725, 64729, 64733, 64737, 64741, + 64745, 64749, 64753, 64757, 64761, 64765, 64769, 64773, 64777, 64781, + 64785, 64789, 64793, 0, 64797, 64801, 64805, 64809, 0, 64813, 64817, + 64821, 64825, 64829, 0, 64833, 0, 0, 0, 64837, 64841, 64845, 64849, + 64853, 64857, 64861, 0, 64865, 64869, 64873, 64877, 64881, 64885, 64889, + 64893, 64897, 64901, 64905, 64909, 64913, 64917, 64921, 64925, 64929, + 64933, 64937, 64941, 64945, 64949, 64953, 64957, 64961, 64965, 64969, + 64974, 64979, 64984, 64989, 64994, 64999, 65004, 65009, 65014, 65019, + 65024, 65029, 65034, 65039, 65044, 65049, 65054, 65059, 65064, 65069, + 65074, 65079, 65084, 65089, 65094, 65099, 65104, 65109, 65114, 65119, + 65124, 65129, 65134, 65139, 65144, 65149, 65154, 65159, 65164, 65169, + 65174, 65179, 65184, 65189, 65194, 65199, 65204, 65209, 65214, 65219, + 65224, 65229, 65233, 65237, 65241, 65245, 65249, 65253, 65257, 65261, + 65265, 65269, 65273, 65277, 65281, 65285, 65289, 65293, 65297, 65301, + 65305, 65309, 65313, 65317, 65321, 65325, 65329, 65333, 65337, 65341, + 65345, 65349, 65353, 65357, 65361, 65365, 65369, 65373, 65377, 65381, + 65385, 65389, 65393, 65397, 65401, 65405, 65409, 65413, 65417, 65421, + 65425, 65429, 65433, 65437, 65442, 65447, 65452, 65457, 65462, 65467, + 65472, 65477, 65482, 65487, 65492, 65497, 65502, 65507, 65512, 65517, + 65522, 65527, 65532, 65537, 65542, 65547, 65552, 65557, 65562, 65567, + 65572, 65577, 65582, 65587, 65592, 65597, 65602, 65607, 65612, 65617, + 65622, 65627, 65632, 65637, 65642, 65647, 65652, 65657, 65662, 65667, + 65672, 65677, 65682, 65687, 65692, 65697, 65702, 65707, 65712, 65717, + 65722, 65727, 65732, 65737, 65742, 65747, 65752, 65757, 65762, 65767, + 65772, 65777, 65782, 65787, 65792, 65797, 65802, 65807, 65812, 65817, + 65822, 65827, 65832, 65837, 65842, 65847, 65852, 65857, 65862, 65867, + 65872, 65877, 65882, 65887, 65892, 65897, 65902, 65907, 65912, 65917, + 65922, 65927, 65932, 65937, 65942, 65947, 65952, 65957, 65963, 65969, + 65975, 65981, 65987, 65993, 65999, 66005, 66011, 66017, 66023, 66029, + 66035, 66041, 66047, 66053, 66059, 66065, 66071, 66077, 66083, 66089, + 66095, 66101, 66107, 66113, 66119, 66125, 66131, 66137, 66143, 66149, + 66155, 66161, 66167, 66173, 66179, 66185, 66191, 66197, 66203, 66209, + 66215, 66221, 66227, 66233, 66239, 66245, 66251, 66257, 66263, 66269, + 66273, 66277, 66281, 66285, 66289, 66293, 66297, 66301, 66305, 66309, + 66313, 66317, 66321, 66325, 66329, 66333, 66337, 66341, 66345, 66349, + 66353, 66357, 66361, 66365, 66369, 66373, 66377, 66381, 66385, 66389, + 66393, 66397, 66401, 66405, 66409, 66413, 66417, 66421, 66425, 66429, + 66433, 66437, 66441, 66445, 66449, 66453, 66457, 66461, 66465, 66469, + 66473, 0, 0, 0, 0, 66477, 66482, 66487, 66492, 66497, 66502, 66507, + 66512, 66517, 66522, 66527, 66532, 66537, 66542, 66547, 66552, 66557, + 66562, 66568, 66573, 66578, 66583, 66588, 66593, 66598, 66603, 66607, + 66612, 66617, 66622, 66627, 66632, 66637, 66642, 66647, 66652, 66657, + 66662, 66667, 66672, 66677, 66682, 66687, 66692, 66698, 66703, 66708, + 66713, 66718, 66723, 66728, 66733, 66739, 66744, 66749, 66754, 66759, + 66764, 66769, 66774, 66779, 66784, 66789, 66794, 66799, 66804, 66809, + 66814, 66819, 66824, 66829, 66834, 66839, 66844, 66849, 66854, 66860, + 66865, 66870, 66875, 66880, 66885, 66890, 66895, 66899, 66904, 66909, + 66914, 66919, 66924, 66929, 66934, 66939, 66944, 66949, 66954, 66959, + 66964, 66969, 66974, 66979, 66984, 66990, 66995, 67000, 67005, 67010, + 67015, 67020, 67025, 67031, 67036, 67041, 67046, 67051, 67056, 67061, + 67067, 67073, 67079, 67085, 67091, 67097, 67103, 67109, 67115, 67121, + 67127, 67133, 67139, 67145, 67151, 67157, 67163, 67170, 67176, 67182, + 67188, 67194, 67200, 67206, 67212, 67217, 67223, 67229, 67235, 67241, + 67247, 67253, 67259, 67265, 67271, 67277, 67283, 67289, 67295, 67301, + 67307, 67313, 67319, 67326, 67332, 67338, 67344, 67350, 67356, 67362, + 67368, 67375, 67381, 67387, 67393, 67399, 67405, 67411, 67417, 67423, + 67429, 67435, 67441, 67447, 67453, 67459, 67465, 67471, 67477, 67483, + 67489, 67495, 67501, 67507, 67513, 67520, 67526, 67532, 67538, 67544, + 67550, 67556, 67562, 67567, 67573, 67579, 67585, 67591, 67597, 67603, + 67609, 67615, 67621, 67627, 67633, 67639, 67645, 67651, 67657, 67663, + 67669, 67676, 67682, 67688, 67694, 67700, 67706, 67712, 67718, 67725, + 67731, 67737, 67743, 67749, 67755, 67761, 67768, 67775, 67782, 67789, + 67796, 67803, 67810, 67817, 67824, 67831, 67838, 67845, 67852, 67859, + 67866, 67873, 67880, 67888, 67895, 67902, 67909, 67916, 67923, 67930, + 67937, 67943, 67950, 67957, 67964, 67971, 67978, 67985, 67992, 67999, + 68006, 68013, 68020, 68027, 68034, 68041, 68048, 68055, 68062, 68070, + 68077, 68084, 68091, 68098, 68105, 68112, 68119, 68127, 68134, 68141, + 68148, 68155, 68162, 0, 0, 0, 0, 68169, 68174, 68178, 68182, 68186, + 68190, 68194, 68198, 68202, 68206, 68210, 68215, 68219, 68223, 68227, + 68231, 68235, 68239, 68243, 68247, 68251, 68256, 68260, 68264, 68268, + 68272, 68276, 68280, 68284, 68288, 68292, 68298, 68303, 68308, 68313, + 68318, 68323, 68328, 68333, 68338, 68343, 68348, 68352, 68356, 68360, + 68364, 68368, 68372, 68376, 68380, 68384, 68388, 68392, 68396, 68400, + 68404, 68408, 68412, 68416, 68420, 68424, 68428, 68432, 68436, 68440, + 68444, 68448, 68452, 68456, 68460, 68464, 68468, 68472, 68476, 68480, + 68484, 68488, 68492, 68496, 68500, 68504, 68508, 68512, 68516, 68520, + 68524, 68528, 68532, 68536, 68540, 68544, 68548, 68552, 68556, 68560, + 68564, 68568, 68572, 68576, 68580, 68584, 68588, 68592, 68596, 68600, + 68604, 68608, 68612, 68616, 68620, 68624, 68628, 68632, 68636, 68640, + 68644, 68648, 68652, 68656, 68660, 68664, 68668, 68672, 68676, 68680, + 68684, 68688, 68692, 68696, 68700, 68704, 68708, 68712, 68716, 68720, + 68724, 68728, 68732, 68736, 68740, 68744, 68748, 68752, 68756, 68760, + 68764, 68768, 68772, 68776, 68780, 68784, 68788, 68792, 68796, 68800, + 68804, 68808, 68812, 68816, 68820, 68824, 68828, 68832, 68836, 68840, + 68844, 68848, 68852, 68856, 68860, 68864, 68868, 68872, 68876, 68880, + 68884, 68888, 68892, 68896, 68900, 68904, 68908, 68912, 68916, 68920, + 68924, 68928, 68932, 68936, 68940, 68944, 68948, 68952, 68956, 68960, + 68964, 68968, 68972, 68976, 68980, 68984, 68988, 68992, 68996, 69000, + 69004, 69008, 69012, 69016, 69020, 69024, 69028, 69032, 69036, 69040, + 69044, 69048, 69052, 69056, 69060, 69064, 69068, 69072, 69076, 69080, + 69084, 69088, 69092, 69096, 69100, 69104, 69108, 69112, 69116, 69120, + 69124, 69128, 69132, 69136, 69140, 69144, 69148, 69152, 69156, 69160, + 69164, 69168, 69172, 69176, 69180, 69184, 69188, 69192, 69196, 69200, + 69204, 69208, 69212, 69216, 69220, 69224, 69228, 69232, 69236, 69240, + 69244, 69248, 69252, 69256, 69260, 69264, 69268, 69272, 69276, 69280, + 69284, 69288, 69292, 69296, 69300, 69304, 69308, 69312, 69316, 69320, + 69324, 69328, 69332, 69336, 69340, 69344, 69348, 69352, 69356, 69360, + 69364, 69368, 69372, 69376, 69380, 69384, 69388, 69392, 69396, 69400, + 69404, 69408, 69412, 69416, 69420, 69424, 69428, 69432, 69436, 69440, + 69444, 69448, 69452, 69456, 69460, 69464, 69468, 69472, 69476, 69480, + 69484, 69488, 69492, 69496, 69500, 69504, 69508, 69512, 69516, 69520, + 69524, 69528, 69532, 69536, 69540, 69544, 69548, 69552, 69556, 69560, + 69564, 69568, 69572, 69576, 69580, 69584, 69588, 69592, 69596, 69600, + 69604, 69608, 69612, 69616, 69620, 69624, 69628, 69632, 69636, 69640, + 69644, 69648, 69652, 69656, 69660, 69664, 69668, 69672, 69676, 69680, + 69684, 69688, 69692, 69696, 69700, 69704, 69708, 69712, 69716, 69720, + 69724, 69728, 69732, 69736, 69740, 69744, 69748, 69752, 69756, 69760, + 69764, 69768, 69772, 69776, 69780, 69784, 69788, 69792, 69796, 69800, + 69804, 69808, 69812, 69816, 69820, 69824, 69828, 69832, 69836, 69840, + 69844, 69848, 69852, 69856, 69860, 69864, 69868, 69872, 69876, 69880, + 69884, 69888, 69892, 69896, 69900, 69904, 69908, 69912, 69916, 69920, + 69924, 69928, 69932, 69936, 69940, 69944, 69948, 69952, 69956, 69960, + 69964, 69968, 69972, 69976, 69980, 69984, 69988, 69992, 69996, 70000, + 70004, 70008, 70012, 70016, 70020, 70024, 70028, 70032, 70036, 70040, + 70044, 70048, 70052, 70056, 70060, 70064, 70068, 70072, 70076, 70080, + 70084, 70088, 70092, 70096, 70100, 70104, 70108, 70112, 70116, 70120, + 70124, 70128, 70132, 70136, 70140, 70144, 70148, 70152, 70156, 70160, + 70164, 70168, 70172, 70176, 70180, 70184, 70188, 70192, 70196, 70200, + 70204, 70208, 70212, 70216, 70220, 70224, 70228, 70232, 70236, 70240, + 70244, 70248, 70252, 70256, 70260, 70264, 70268, 70272, 70276, 70280, + 70284, 70288, 70292, 70296, 70300, 70304, 70308, 70312, 70316, 70320, + 70324, 70328, 70332, 70336, 70340, 70344, 70348, 70352, 70356, 70360, + 70364, 70368, 70372, 70376, 70380, 70384, 70388, 70392, 70396, 70400, + 70404, 70408, 70412, 70416, 70420, 70424, 70428, 70432, 70436, 70440, + 70444, 70448, 70452, 70456, 70460, 70464, 70468, 70472, 70476, 70480, + 70484, 70488, 70492, 70496, 70500, 70504, 70508, 70512, 70516, 70520, + 70524, 70528, 70532, 70536, 70540, 70544, 70548, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 70552, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70556, 70559, 70563, 70567, 70570, + 70574, 70578, 70581, 70584, 70588, 70592, 70595, 70599, 70602, 70605, + 70608, 70611, 70615, 70618, 70621, 70624, 70627, 70630, 70633, 70636, + 70639, 70642, 70645, 70648, 70651, 70655, 70658, 70662, 70667, 70672, + 70677, 70682, 70687, 70692, 70697, 70702, 70707, 70712, 70717, 70722, + 70727, 70732, 70737, 70742, 70747, 70752, 70757, 70762, 70767, 70772, + 70777, 70782, 70787, 70792, 70797, 70801, 70806, 70810, 70814, 70819, + 70824, 70829, 70834, 70839, 70844, 70849, 70854, 70859, 70864, 70869, + 70874, 70879, 70884, 70889, 70894, 70899, 70904, 70909, 70914, 70919, + 70924, 70929, 70934, 70939, 70944, 70949, 70954, 70959, 70963, 70968, + 70970, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* name->code dictionary */ +static unsigned int code_hash[] = { + 0, 4851, 118860, 0, 0, 66306, 7929, 64584, 9518, 64710, 0, 42166, 0, + 1097, 0, 12064, 41730, 596, 8570, 0, 120216, 8651, 41728, 41721, 41835, + 12995, 41202, 1373, 0, 13110, 5816, 119067, 64810, 1000, 0, 11951, 41140, + 1209, 9717, 0, 0, 1073, 0, 65470, 41138, 8851, 0, 0, 12167, 1115, 8874, + 9794, 194660, 0, 0, 12237, 3966, 41603, 8308, 9290, 120763, 41600, 9231, + 120183, 2959, 1457, 3535, 0, 42179, 63860, 41538, 12927, 8618, 42175, + 3404, 64661, 5148, 41737, 1759, 0, 119974, 0, 118949, 12290, 66577, + 120019, 9860, 12312, 10151, 8205, 0, 5131, 0, 9627, 0, 9834, 3055, 9852, + 1944, 1248, 10148, 63884, 119990, 119991, 12701, 119204, 12235, 603, 0, + 65327, 119998, 65305, 120000, 3350, 66576, 64318, 0, 8154, 3390, 120007, + 41817, 119956, 64603, 66328, 120012, 120013, 3400, 120015, 6041, 65020, + 41899, 119879, 8002, 8562, 4364, 0, 4043, 8712, 119988, 7813, 119117, + 120759, 10124, 11966, 8601, 6069, 10143, 4814, 12041, 1418, 10885, 12673, + 0, 0, 9660, 2764, 13012, 4571, 5704, 0, 119946, 12078, 2970, 5457, 5440, + 8857, 0, 0, 2843, 5355, 41599, 118883, 119175, 5194, 12950, 0, 3486, + 65324, 194602, 10123, 65167, 0, 10717, 64570, 2637, 64629, 8460, 10682, + 8476, 10602, 800, 0, 120583, 194632, 7799, 64930, 0, 8465, 12289, 0, + 2384, 13172, 13119, 8488, 5412, 10906, 1353, 0, 41351, 41823, 5828, 8206, + 120674, 8933, 1601, 9072, 5867, 13302, 12458, 0, 8090, 5418, 12452, 0, + 9483, 3351, 917544, 64510, 10817, 0, 41539, 2750, 0, 556, 41855, 41246, + 0, 12213, 0, 2760, 10620, 0, 12210, 120743, 0, 5498, 9998, 41536, 0, 0, + 9242, 3459, 8997, 0, 0, 194888, 0, 0, 4839, 12604, 0, 4435, 119016, 4975, + 4635, 295, 120026, 195039, 6050, 64898, 0, 7688, 0, 63903, 9036, 63901, + 0, 3971, 118975, 0, 2952, 917618, 6287, 8031, 2725, 63899, 63898, 5482, + 667, 12332, 1177, 6086, 12322, 12069, 5172, 41617, 64102, 7859, 1945, + 64099, 9815, 10453, 63883, 63882, 7997, 8555, 63878, 63877, 8705, 64097, + 64096, 9571, 528, 0, 0, 12132, 41723, 63875, 41578, 63873, 63855, 63854, + 41913, 9056, 0, 6188, 64593, 6155, 10806, 446, 41911, 64065, 41318, + 63850, 63, 64069, 63846, 2972, 9455, 63843, 64064, 63849, 63848, 63847, + 1176, 0, 8302, 9577, 63842, 4178, 13208, 13188, 10948, 10041, 8105, 4333, + 0, 118983, 1105, 4180, 5388, 12094, 120165, 0, 7714, 63890, 63889, 7768, + 5538, 9987, 0, 118932, 1678, 917611, 552, 9560, 64077, 10785, 8996, + 12280, 4471, 119112, 9159, 10171, 63861, 10486, 5540, 63858, 63865, 281, + 63863, 12075, 42041, 0, 5174, 0, 63857, 1388, 3123, 0, 1077, 13272, 8408, + 64704, 194821, 0, 9223, 0, 65318, 0, 194822, 42105, 1116, 13274, 194972, + 3663, 0, 1112, 119122, 8686, 8881, 5334, 42108, 0, 64092, 64091, 9322, 0, + 120595, 64095, 5327, 8111, 63870, 63869, 3478, 63867, 6199, 2903, 0, + 3001, 1158, 8745, 64081, 4741, 63866, 4737, 4370, 4846, 0, 4742, 41335, + 4118, 1797, 64600, 805, 120130, 46, 65101, 8760, 298, 118987, 12212, + 120123, 65174, 63836, 32, 5965, 0, 0, 12225, 3665, 63837, 64793, 65330, + 41336, 4305, 66360, 8083, 0, 119938, 63821, 4412, 63819, 63818, 12244, + 5227, 9047, 12702, 4181, 4752, 63975, 4634, 560, 5643, 8226, 6181, 63812, + 41181, 63810, 63790, 3639, 63815, 10122, 63813, 6047, 7937, 63974, 780, + 206, 0, 4936, 65147, 1098, 63930, 0, 1093, 41729, 3016, 4869, 63932, + 917554, 63929, 3546, 1605, 0, 6182, 0, 65058, 8400, 41533, 63920, 0, + 5471, 2984, 5314, 9287, 5473, 44, 0, 195075, 13169, 5290, 5283, 1695, + 63827, 1088, 5961, 8304, 1084, 1085, 63829, 1083, 10131, 5576, 0, 64405, + 4263, 1092, 4754, 8947, 5252, 0, 65253, 64938, 0, 7908, 120622, 120719, + 120673, 0, 2965, 0, 8808, 0, 1089, 7761, 41641, 42119, 12355, 65204, 940, + 5787, 9992, 63938, 5057, 64679, 12463, 2994, 5054, 41694, 0, 9664, 41026, + 1437, 9399, 658, 3497, 12920, 0, 660, 5060, 666, 9022, 5532, 118951, + 5533, 5059, 4727, 6118, 222, 979, 3884, 12459, 65154, 5773, 978, 120748, + 120690, 41619, 10239, 12465, 0, 194863, 64411, 63946, 1707, 0, 12461, + 63895, 63949, 63948, 63947, 3376, 6038, 63943, 63942, 63894, 65323, 0, + 65508, 7776, 64278, 2379, 8703, 917591, 64668, 801, 8125, 1690, 63919, + 63918, 63917, 2369, 0, 12844, 0, 119235, 5486, 2334, 64893, 4463, 5483, + 10207, 0, 2367, 5484, 63909, 264, 2375, 8060, 6194, 5485, 1844, 119084, + 9061, 5534, 10672, 4502, 0, 253, 0, 1823, 8800, 10746, 64470, 0, 11957, + 6192, 0, 0, 118942, 0, 725, 4550, 13257, 120800, 118944, 12892, 0, 0, + 41775, 8413, 0, 0, 5693, 10397, 0, 13209, 5074, 5073, 0, 8983, 0, 119183, + 66586, 5072, 63982, 6198, 12478, 917609, 196, 66608, 3111, 64725, 4929, + 12445, 0, 0, 0, 66606, 64942, 1076, 0, 1436, 4934, 64415, 41323, 9758, 0, + 12807, 63907, 63906, 4548, 4329, 6113, 4979, 3048, 4423, 41320, 0, 10515, + 6218, 8971, 5071, 0, 3642, 1430, 5070, 10042, 120606, 3987, 5068, 120483, + 3255, 3493, 0, 8905, 10735, 120217, 41635, 3378, 4531, 1245, 9105, 66311, + 4921, 4481, 3771, 194649, 2710, 41693, 0, 41724, 64709, 41682, 41690, + 120790, 4922, 325, 992, 0, 4925, 10914, 0, 9526, 4920, 65262, 948, 10783, + 120208, 4930, 917570, 4462, 194855, 4933, 5339, 6115, 120210, 4928, + 917603, 4457, 0, 65290, 42163, 722, 5684, 8678, 12637, 0, 5689, 8753, + 1509, 120617, 5468, 9511, 195043, 65183, 1672, 6205, 5832, 6310, 5686, 0, + 120818, 0, 0, 0, 50, 0, 41607, 120115, 1679, 120116, 10759, 120113, 0, + 3183, 13259, 4448, 119225, 401, 119139, 120109, 64763, 5761, 342, 8553, + 1151, 8143, 0, 11983, 64384, 624, 120715, 65500, 0, 5078, 12501, 5656, 0, + 5076, 118870, 8812, 0, 41601, 685, 9025, 1524, 8003, 194714, 5539, 8087, + 12971, 120101, 120094, 1252, 0, 194612, 4636, 0, 118985, 8053, 9732, 0, + 5080, 13121, 5036, 5035, 120590, 12277, 119187, 195042, 8074, 275, 13084, + 194594, 8741, 4432, 120610, 5033, 120668, 64605, 4836, 3888, 473, 0, + 8502, 120250, 120681, 1087, 12499, 0, 63844, 12496, 3601, 1922, 194626, + 64965, 65422, 12502, 194624, 12505, 66321, 0, 9489, 0, 3432, 4384, + 917548, 6094, 0, 8815, 13295, 64753, 0, 1676, 1154, 3857, 1205, 5030, 0, + 13100, 12958, 10519, 9622, 0, 64723, 4421, 10592, 0, 495, 0, 10544, 7983, + 118882, 10749, 119835, 8494, 13297, 10979, 41710, 947, 0, 437, 41709, + 10969, 119935, 119934, 9465, 13290, 4795, 119930, 64306, 8826, 120181, + 41714, 120611, 8626, 4590, 4711, 120769, 120195, 2739, 119918, 8044, + 40964, 251, 12686, 7895, 4395, 119927, 119926, 119929, 1779, 8146, + 119922, 41543, 5325, 642, 120753, 8880, 7685, 194653, 0, 6234, 13229, + 625, 8187, 9990, 1113, 194643, 7915, 1104, 120176, 8179, 10655, 0, 9316, + 10980, 2489, 1082, 8150, 1359, 194645, 0, 0, 0, 5042, 5041, 0, 12084, + 8049, 41548, 0, 40962, 0, 0, 4761, 10506, 4766, 1616, 1273, 0, 8795, + 118876, 0, 63957, 9232, 1138, 10483, 12677, 41545, 12881, 3239, 0, 0, + 66614, 194582, 42128, 3484, 64545, 194801, 12620, 8503, 5122, 41527, + 5040, 4924, 194913, 0, 120535, 0, 5039, 41926, 8303, 0, 5038, 0, 10003, + 0, 917543, 120586, 1686, 0, 9359, 66609, 3664, 0, 8238, 64299, 0, 0, + 3863, 126, 4835, 0, 0, 13245, 4309, 7744, 194569, 119902, 194568, 13184, + 0, 0, 12222, 8136, 194987, 711, 1633, 0, 0, 4762, 1103, 194561, 12281, + 4765, 41331, 1006, 13040, 4760, 9461, 8201, 10871, 0, 1102, 5031, 118904, + 194664, 0, 64636, 13042, 337, 194781, 0, 119184, 12279, 1111, 120309, + 194636, 4707, 194635, 5511, 7883, 8822, 7880, 4522, 8255, 5512, 13010, + 119232, 66378, 64313, 194667, 5906, 1119, 120233, 13038, 120639, 2455, + 64734, 13008, 41652, 4385, 12492, 12821, 8714, 64775, 119161, 13009, 160, + 0, 0, 64262, 5052, 64031, 5821, 6186, 41792, 0, 5051, 0, 1429, 64573, + 5050, 302, 388, 12058, 735, 0, 1079, 3867, 5708, 12726, 0, 9117, 5706, + 10679, 5513, 8791, 4005, 0, 5510, 10991, 0, 65458, 2470, 917581, 41399, + 1925, 194805, 917577, 917576, 917571, 5048, 5047, 41532, 10058, 0, + 917569, 9070, 0, 3339, 8089, 1106, 639, 120456, 63967, 3340, 3109, 3653, + 4599, 10799, 917583, 10605, 917585, 1476, 648, 1754, 11001, 3233, 864, + 41782, 10164, 8972, 0, 3530, 9750, 0, 13240, 41781, 5192, 4338, 5046, + 8512, 63770, 13199, 8967, 1236, 5045, 12012, 13189, 7986, 5044, 120556, + 9006, 13128, 5043, 9553, 1590, 63777, 63776, 9669, 12341, 8654, 8402, + 63779, 1583, 4740, 13260, 3586, 13276, 0, 120306, 0, 194661, 41523, + 13296, 517, 12922, 120140, 194658, 41528, 123, 65454, 12393, 63807, + 41997, 10531, 7784, 13271, 1334, 120445, 4479, 1126, 119004, 120663, 0, + 8520, 3925, 0, 8069, 4357, 42154, 489, 120450, 120440, 8848, 63786, 8450, + 120434, 11926, 41557, 1145, 63788, 7910, 63785, 63784, 10456, 8711, 6183, + 8183, 120741, 8928, 0, 7952, 0, 125, 9235, 64861, 0, 12689, 0, 10779, + 10990, 3523, 1074, 13258, 9536, 8477, 0, 4427, 10517, 63757, 7726, 12217, + 41802, 267, 1349, 10713, 1371, 120293, 195070, 2458, 63753, 6201, 41084, + 41074, 4266, 10652, 41612, 41077, 3402, 9050, 3398, 8140, 42084, 6260, + 3391, 41075, 2476, 41956, 11988, 3898, 10625, 10201, 10988, 13017, 63794, + 10367, 12521, 10431, 13014, 13013, 1068, 194806, 12523, 12945, 12524, + 12438, 7950, 10804, 13233, 12082, 4386, 9053, 12473, 2793, 12475, 704, 0, + 6195, 9530, 12238, 12232, 0, 194944, 5681, 12629, 4595, 63760, 792, 0, + 64803, 0, 8742, 0, 64947, 65448, 63744, 12948, 64787, 195002, 63748, + 1693, 63746, 63745, 5055, 0, 4287, 1090, 4902, 1131, 41180, 194721, 4558, + 1816, 9523, 41712, 168, 0, 4898, 64298, 6157, 63775, 4901, 1821, 13191, + 12170, 3500, 3139, 791, 9162, 12485, 10306, 119001, 194945, 0, 64433, + 8354, 10033, 941, 0, 64422, 120185, 0, 8234, 64559, 8228, 8424, 10246, + 194996, 12811, 194998, 3946, 195000, 8057, 0, 673, 194854, 64357, 0, 0, + 9547, 288, 64296, 194976, 2448, 10025, 194981, 2918, 2452, 65300, 41529, + 8729, 64726, 2790, 7845, 3793, 0, 4408, 4122, 0, 41535, 8723, 65178, + 10087, 0, 731, 42109, 12923, 2438, 64855, 65396, 0, 1175, 13256, 1282, + 373, 119172, 5396, 8653, 8557, 7723, 0, 3330, 0, 41952, 0, 5273, 8248, + 5269, 3304, 5202, 2404, 5267, 0, 0, 0, 5277, 12963, 5371, 6189, 4125, + 1826, 12133, 65241, 118873, 8785, 917589, 0, 64643, 9035, 3864, 13185, + 4631, 3879, 118785, 0, 4166, 164, 0, 0, 64484, 0, 10212, 5384, 41882, 0, + 64346, 0, 120098, 0, 41388, 0, 12005, 12666, 41387, 13207, 8706, 5552, + 10172, 700, 5929, 5553, 12978, 194610, 5356, 12906, 8563, 41888, 3180, 0, + 0, 5554, 971, 12344, 8724, 0, 13201, 63874, 0, 2866, 8517, 12446, 13190, + 64632, 120227, 5555, 10045, 12882, 13275, 120672, 41522, 13206, 9143, + 194957, 41525, 120539, 195069, 656, 194614, 65037, 4577, 12229, 8715, 0, + 0, 120261, 4269, 64813, 119163, 41609, 10476, 950, 0, 3932, 41450, 0, 0, + 0, 120014, 11974, 118884, 369, 119096, 41784, 119099, 5097, 194869, 9848, + 0, 10381, 4796, 10317, 3651, 10285, 194616, 10269, 5102, 5101, 120511, + 9064, 8138, 120455, 404, 5100, 1439, 12093, 1247, 8092, 195064, 5099, + 1831, 1441, 4793, 3842, 650, 0, 746, 120784, 195041, 41453, 12018, 9031, + 12182, 120305, 9078, 8545, 4422, 4708, 3799, 3268, 0, 9118, 119127, 2676, + 7750, 4374, 195052, 6190, 1364, 195053, 8038, 195055, 9857, 0, 9858, + 195033, 66585, 12129, 13174, 8481, 12412, 6202, 64380, 10920, 10872, + 2365, 7841, 195038, 5108, 5107, 0, 13210, 6176, 195093, 5541, 41785, + 41171, 12613, 5284, 4372, 207, 0, 4275, 120171, 0, 119147, 0, 12965, 384, + 5103, 10404, 10340, 10702, 0, 488, 13236, 12937, 10017, 9733, 13187, + 10014, 11982, 41373, 13198, 5203, 120517, 13232, 5106, 349, 4863, 41371, + 13194, 41367, 5105, 13133, 12861, 4398, 5104, 5672, 304, 1096, 0, 0, 932, + 12441, 0, 238, 195008, 4318, 10452, 195013, 8032, 13243, 13237, 12719, + 194884, 119059, 64814, 64884, 119872, 10670, 8597, 1178, 64017, 9864, + 13195, 8803, 309, 0, 8151, 10858, 64961, 7722, 12553, 10459, 12568, + 64824, 12549, 66590, 12570, 9712, 41417, 41496, 0, 65165, 4965, 0, 10538, + 0, 41401, 0, 0, 6191, 6261, 0, 0, 11965, 1957, 10420, 982, 2756, 9370, + 2720, 12357, 119260, 2925, 118817, 13056, 3222, 13212, 10116, 41644, + 10105, 10624, 41581, 10834, 118793, 64407, 5242, 41963, 64476, 1694, + 8216, 10814, 0, 7781, 6306, 64568, 0, 120738, 12077, 42057, 41444, 0, + 120325, 64799, 3475, 66566, 2479, 9709, 3632, 120322, 10698, 0, 3648, + 3907, 194963, 194962, 3636, 40979, 2979, 8837, 65229, 1843, 3936, 0, 0, + 41347, 65119, 13235, 3640, 41248, 120579, 4379, 13239, 12692, 7969, + 120120, 66353, 194951, 118908, 120509, 41846, 2529, 734, 10808, 65146, + 42083, 194955, 194954, 42055, 1846, 66367, 12181, 9634, 120310, 9988, + 12991, 1670, 5740, 120317, 10072, 5379, 120318, 41163, 41157, 785, 8236, + 194942, 9027, 63897, 13267, 64383, 64688, 925, 0, 120541, 41773, 41071, + 9586, 120312, 41984, 9217, 6151, 12110, 0, 194901, 64580, 4016, 13265, + 13264, 381, 12936, 6100, 42077, 120768, 5808, 5184, 8200, 12967, 10810, + 5612, 4583, 0, 5860, 120506, 64575, 0, 812, 3615, 65284, 5178, 194929, + 119015, 9825, 5188, 9698, 7814, 194935, 10692, 1166, 64429, 41921, 924, + 9756, 12359, 119258, 0, 2442, 10703, 194940, 194939, 8012, 5674, 0, 0, + 12361, 5677, 0, 0, 40972, 12453, 41920, 5673, 0, 5676, 8542, 12694, + 118978, 2468, 1294, 41294, 3336, 3883, 64388, 1727, 194991, 0, 3605, + 120638, 0, 12034, 8718, 3550, 736, 7806, 4505, 2715, 806, 5826, 41884, + 5813, 0, 65391, 5841, 5837, 64731, 66325, 3105, 2405, 5838, 5796, 0, + 65259, 5793, 5735, 5866, 5797, 1432, 5865, 12143, 7956, 598, 0, 41886, + 2480, 0, 66334, 9037, 5671, 5537, 0, 0, 10932, 0, 1211, 847, 120615, + 9529, 118832, 12318, 194601, 0, 5645, 10622, 41391, 194967, 120033, + 64597, 0, 5650, 120039, 119102, 64864, 194968, 9624, 0, 0, 0, 2748, 3589, + 0, 4035, 10297, 0, 4265, 194969, 3977, 65344, 12051, 836, 5698, 2488, + 194634, 4582, 120713, 5644, 10292, 66627, 8046, 0, 10550, 120081, 65116, + 119206, 120022, 65120, 1374, 64878, 119014, 41013, 10568, 41374, 4030, + 41010, 0, 41015, 120516, 65325, 400, 12597, 120621, 0, 120593, 41375, + 5659, 64827, 4759, 118906, 390, 10266, 41349, 1170, 3473, 7718, 118962, + 1609, 902, 0, 0, 66352, 0, 8122, 5712, 0, 8004, 3861, 9540, 10278, 2554, + 5158, 5714, 41136, 194970, 64351, 807, 0, 194691, 64677, 976, 0, 6146, + 65518, 771, 10954, 41356, 9673, 13168, 0, 41143, 8676, 7904, 5579, 953, + 451, 194585, 5578, 12635, 0, 9724, 0, 0, 9524, 120794, 118789, 1440, + 3379, 10310, 120016, 120722, 471, 0, 0, 3795, 120220, 12586, 10701, 0, + 41060, 10094, 64900, 194959, 10857, 2474, 120640, 9590, 93, 10615, 10213, + 8128, 12551, 10049, 8171, 3544, 0, 6017, 65311, 383, 120223, 13306, + 10533, 7870, 0, 5187, 120711, 1456, 0, 42164, 0, 194702, 5232, 0, 41009, + 2472, 41005, 120699, 8710, 6019, 4256, 120776, 4980, 8860, 9640, 10028, + 12845, 119114, 13182, 65121, 120685, 0, 10631, 65126, 7972, 118928, 8066, + 0, 7900, 8316, 0, 120213, 120555, 0, 120830, 0, 10347, 445, 120566, 0, + 12931, 0, 8330, 0, 0, 0, 64366, 64369, 8814, 3902, 64607, 1770, 0, 12836, + 0, 64552, 0, 4584, 9684, 0, 0, 10866, 0, 1118, 0, 0, 9349, 1081, 120464, + 0, 8162, 9342, 5996, 0, 4903, 64332, 41386, 5162, 41007, 1330, 64486, + 40995, 12209, 12047, 41384, 0, 0, 1848, 4334, 120334, 41975, 64777, + 10674, 5522, 0, 61, 120157, 0, 3633, 917582, 65162, 41234, 12089, 118800, + 9771, 120333, 13251, 41959, 64749, 6262, 2784, 0, 9334, 8126, 0, 64967, + 7975, 441, 194591, 0, 66621, 4884, 40999, 120269, 0, 66314, 6313, 10890, + 0, 119090, 8324, 7855, 2345, 0, 463, 64737, 0, 0, 3117, 5460, 0, 1193, + 10056, 1148, 12396, 13252, 7829, 42173, 0, 7743, 0, 13248, 5499, 120549, + 120557, 9034, 6039, 0, 5663, 119182, 41018, 0, 10338, 2482, 1471, 195027, + 120079, 66370, 12378, 41966, 41970, 0, 12374, 10903, 9592, 0, 911, 2460, + 120499, 11944, 12376, 41032, 40996, 120614, 12380, 5520, 64473, 10869, + 5870, 64670, 13310, 2603, 12326, 539, 0, 65180, 0, 3853, 41327, 64901, + 120796, 0, 10722, 0, 8659, 0, 12474, 0, 5857, 65342, 2478, 119120, 4162, + 7942, 4260, 12953, 119245, 120090, 12470, 0, 0, 2742, 12476, 5439, 10946, + 9101, 12472, 0, 12302, 3018, 12942, 5748, 194895, 10773, 6161, 0, 8796, + 0, 194583, 118986, 8519, 13146, 119954, 42053, 9422, 10333, 2882, 4366, + 0, 12843, 4520, 0, 0, 10648, 0, 4014, 12842, 0, 12015, 13117, 0, 3893, + 66362, 5810, 0, 0, 42147, 64747, 13292, 0, 12938, 10427, 9154, 3844, + 63934, 9755, 1110, 65239, 10892, 8231, 10775, 0, 41968, 783, 12161, 3591, + 41969, 0, 2453, 8518, 3620, 119181, 12443, 4556, 10349, 10413, 0, 41159, + 3202, 119097, 10510, 4382, 0, 0, 10842, 41265, 120088, 8902, 0, 1840, + 41751, 12891, 0, 4883, 285, 4723, 41917, 9788, 4459, 66635, 1634, 41958, + 9155, 240, 9786, 65082, 41919, 8579, 9743, 7981, 13134, 118878, 4508, + 64883, 41999, 120231, 120664, 118885, 63887, 3081, 63886, 120080, 0, 0, + 10445, 41720, 0, 0, 2614, 9024, 64620, 1729, 0, 64289, 65221, 0, 65466, + 64852, 64509, 120235, 63916, 194984, 41203, 0, 41879, 0, 4121, 12957, + 884, 41214, 63879, 4943, 5150, 0, 5278, 7773, 643, 3086, 118912, 64652, + 120068, 58, 0, 6167, 120083, 63872, 8491, 0, 0, 41495, 3624, 0, 0, 64655, + 2721, 9616, 63988, 41955, 41321, 10500, 10440, 9611, 4264, 120077, 0, + 7738, 63986, 63985, 12638, 0, 3435, 3094, 12916, 9754, 66376, 4437, + 41292, 8899, 0, 42058, 9517, 65143, 0, 65360, 0, 119047, 63956, 4306, + 41380, 11995, 63960, 9591, 63958, 10217, 118845, 120657, 120578, 12456, + 2723, 0, 5088, 5086, 0, 0, 7752, 41378, 2880, 0, 0, 2872, 1386, 65034, + 3498, 4378, 65039, 4270, 12392, 65036, 7853, 0, 12101, 5822, 5230, 0, + 710, 0, 12390, 1666, 8161, 371, 12013, 63891, 42092, 119103, 415, 63851, + 63892, 63962, 42096, 5183, 3362, 12377, 7924, 2927, 4324, 63961, 4472, + 1244, 331, 0, 12683, 10662, 64678, 4756, 195017, 119021, 10730, 7691, + 10331, 0, 41964, 6238, 8938, 8628, 6043, 0, 64895, 1604, 9565, 10539, + 120814, 41220, 13032, 120791, 194575, 10032, 8750, 12373, 63828, 11992, + 1351, 0, 8698, 12190, 3622, 1930, 194573, 9621, 12427, 63981, 4967, + 13031, 1966, 2330, 0, 3657, 0, 65202, 6000, 4347, 4416, 42098, 13180, + 10694, 8099, 402, 41916, 13147, 0, 42100, 12429, 9695, 41757, 41281, + 3515, 5170, 65261, 41755, 676, 6259, 41742, 0, 41870, 0, 3536, 0, 41305, + 63902, 6162, 10532, 0, 10113, 41829, 120545, 5159, 12422, 41832, 439, 0, + 194948, 12316, 12481, 2325, 40970, 41830, 194947, 0, 5145, 12486, 65018, + 194723, 5409, 8976, 0, 12336, 4135, 9685, 341, 2727, 4129, 3539, 66616, + 0, 41736, 7913, 5405, 63859, 4131, 41267, 64721, 63871, 4133, 63864, 210, + 4600, 64690, 3254, 4137, 120608, 0, 119062, 0, 0, 4591, 65077, 118982, 0, + 3355, 9508, 3393, 561, 12159, 195, 64261, 3377, 12497, 41269, 0, 13135, + 0, 8368, 119224, 41499, 0, 0, 0, 41498, 0, 1379, 246, 12603, 0, 3788, + 2924, 64587, 12812, 8728, 64906, 119213, 8917, 0, 301, 64765, 3969, + 64964, 9575, 64562, 0, 9652, 0, 64590, 42086, 0, 0, 13163, 0, 41877, + 120443, 3182, 327, 0, 9042, 120298, 0, 42169, 4755, 0, 119882, 13223, + 12431, 8668, 12434, 608, 600, 5999, 1219, 3934, 9494, 0, 0, 1726, 0, + 64686, 8212, 12115, 0, 13160, 7759, 65363, 485, 0, 65291, 9828, 927, + 42102, 194979, 12436, 9351, 7778, 64379, 0, 0, 10126, 1208, 0, 64757, + 9337, 64362, 0, 64535, 120735, 9021, 0, 0, 0, 119237, 5411, 0, 9648, + 64617, 63834, 9150, 63835, 1117, 13037, 2594, 63809, 10691, 12052, 10643, + 41503, 65212, 64536, 2546, 119216, 213, 65309, 10554, 3972, 0, 194678, + 65442, 194677, 12416, 11914, 5452, 8230, 0, 41951, 12418, 42049, 3882, + 8532, 2713, 1573, 9650, 42136, 4596, 66339, 1406, 194682, 40990, 194593, + 12414, 8287, 4143, 194687, 10489, 1143, 4141, 9682, 12415, 1508, 64515, + 8779, 10569, 8725, 120783, 65264, 64487, 119064, 4145, 194761, 194794, + 66613, 0, 8027, 120192, 0, 9550, 0, 0, 194799, 120189, 64070, 10740, + 195020, 64816, 10998, 66333, 12955, 0, 2888, 0, 0, 7715, 3881, 41487, + 12118, 194778, 2878, 5390, 0, 3009, 41476, 41489, 63765, 3007, 1448, + 2975, 10429, 3889, 8521, 5083, 5082, 0, 5235, 803, 194966, 3014, 5081, + 8986, 11002, 10632, 11934, 0, 1332, 64802, 3929, 4597, 65532, 64767, + 9107, 5191, 9288, 9657, 2892, 10577, 6031, 555, 120188, 0, 194927, 12367, + 42170, 13151, 0, 629, 1924, 0, 12037, 0, 5858, 8462, 8005, 12365, 1784, + 1361, 118939, 12369, 7905, 120041, 5077, 194668, 10880, 63927, 5075, + 194973, 9371, 65075, 41193, 11007, 0, 10997, 0, 1342, 0, 0, 3434, 4843, + 4506, 0, 5266, 0, 5272, 4482, 4507, 9578, 63923, 66319, 7979, 64381, + 9831, 64417, 0, 461, 9803, 41972, 4504, 444, 0, 9127, 5276, 64522, 0, + 120179, 0, 64911, 12848, 5177, 41324, 12055, 8722, 120805, 1197, 65512, + 1149, 4114, 409, 4383, 8900, 8948, 7684, 3492, 721, 41191, 9108, 0, 0, + 11954, 119191, 118819, 40963, 3099, 0, 65088, 0, 119834, 12587, 194703, + 0, 12036, 0, 65123, 41576, 8152, 120721, 64428, 12227, 8578, 5995, 12828, + 41575, 2922, 63950, 63944, 120643, 0, 2670, 4167, 0, 65009, 120025, + 65173, 118958, 13023, 938, 0, 0, 0, 9721, 0, 41017, 9606, 12413, 4024, + 41063, 0, 12334, 0, 4153, 11911, 10793, 5250, 12407, 3395, 4404, 0, + 12401, 42007, 5775, 42005, 194739, 119251, 0, 12205, 1344, 8870, 194744, + 4940, 4735, 7683, 1167, 12822, 4983, 0, 63939, 64907, 0, 0, 0, 63896, 0, + 12039, 10559, 11956, 119841, 118892, 9472, 4282, 11929, 0, 12816, 9596, + 0, 12710, 0, 12721, 4101, 0, 0, 5992, 119840, 0, 120280, 1004, 9632, + 120602, 0, 0, 12627, 10953, 0, 6290, 0, 0, 0, 9491, 9686, 5890, 0, 65232, + 12712, 0, 194748, 10718, 13154, 3461, 9139, 64756, 0, 119151, 0, 0, + 13227, 12585, 10565, 119152, 12177, 41708, 12860, 41098, 10015, 10838, + 4900, 10352, 0, 10061, 5903, 4119, 5140, 209, 64002, 41704, 9702, 119100, + 41132, 9245, 13048, 4927, 4138, 41093, 65286, 0, 2410, 993, 194975, + 13054, 12394, 0, 0, 0, 12685, 120011, 119040, 10781, 41230, 0, 0, 1680, + 10507, 118809, 10659, 3600, 13049, 120027, 1336, 41518, 0, 5896, 119838, + 5993, 2819, 64820, 12706, 12966, 41134, 120581, 63915, 0, 8184, 272, + 1363, 8793, 8411, 63908, 41502, 3077, 983, 0, 1512, 0, 1190, 4109, 1335, + 841, 5888, 41358, 9836, 9544, 120820, 41481, 8313, 7832, 118954, 3090, + 2409, 817, 1664, 1850, 120757, 3079, 4731, 10118, 66629, 64541, 12033, + 1255, 12386, 9247, 64350, 66633, 12389, 66610, 0, 41996, 63990, 64936, + 5864, 1147, 63992, 5835, 5328, 66625, 5480, 7858, 41990, 4116, 12391, + 66634, 1094, 194, 12384, 0, 8180, 41686, 12313, 0, 63904, 195018, 6114, + 10898, 0, 64578, 8247, 507, 91, 0, 10695, 0, 12070, 0, 10036, 7857, 6067, + 774, 119829, 2744, 119815, 5994, 12539, 41857, 64321, 8359, 119820, 6028, + 119819, 13167, 0, 7719, 119875, 2486, 7893, 41059, 162, 5436, 0, 119809, + 9687, 64956, 6304, 119078, 6051, 0, 5262, 5904, 0, 12681, 0, 0, 12406, + 12219, 3652, 10537, 0, 10492, 64550, 0, 279, 0, 119978, 64619, 12403, + 1489, 195016, 4132, 4899, 3899, 1007, 42124, 4976, 2343, 4103, 0, 0, + 10750, 1345, 0, 120801, 12859, 8956, 4098, 65267, 5861, 0, 11999, 12151, + 64804, 0, 12645, 5146, 0, 0, 0, 41094, 492, 8685, 12974, 0, 118865, + 41551, 5147, 2582, 0, 64538, 0, 1928, 0, 9594, 5991, 13304, 0, 2527, 0, + 197, 2799, 8241, 0, 119810, 120199, 0, 64958, 0, 5524, 194809, 10138, + 119808, 0, 8897, 119072, 41553, 8357, 4124, 1799, 65371, 42148, 0, 12954, + 194688, 65340, 1123, 963, 2434, 10120, 12405, 195015, 0, 398, 392, 9723, + 7894, 119011, 7945, 64935, 4402, 10896, 12402, 119106, 41880, 8414, + 12408, 120554, 0, 406, 0, 9164, 12411, 0, 4560, 8554, 4961, 0, 1575, + 64682, 5438, 165, 9993, 41467, 63953, 8064, 9093, 9599, 9147, 0, 0, 4987, + 9148, 2399, 4096, 53, 10944, 12368, 65435, 195011, 8178, 64598, 3367, + 12910, 10884, 727, 65272, 0, 5805, 1947, 0, 195022, 42176, 12370, 120397, + 1705, 9331, 8898, 0, 12372, 120642, 195023, 8017, 65287, 8813, 12366, + 10963, 6066, 1329, 4909, 3052, 9220, 120696, 4904, 120274, 10803, 1365, + 9253, 0, 41264, 0, 120712, 0, 119814, 1499, 0, 8055, 0, 8740, 5398, + 63964, 120419, 8924, 0, 5988, 3660, 12017, 64646, 9476, 8788, 1357, + 42113, 0, 3629, 8774, 42114, 0, 3628, 120172, 0, 1933, 3469, 1567, 42116, + 11969, 64809, 2928, 4905, 2487, 4910, 3121, 1804, 3311, 194916, 9114, 0, + 12083, 9315, 4822, 4906, 3852, 2847, 0, 3236, 0, 1251, 7777, 41852, 7951, + 1198, 9132, 0, 12274, 510, 10259, 9865, 0, 4561, 6018, 1398, 0, 12276, + 120683, 41569, 0, 120750, 8167, 12127, 41932, 840, 120300, 2443, 10918, + 10410, 0, 1001, 9241, 1927, 333, 41930, 0, 8144, 8034, 119833, 0, 118828, + 0, 12867, 0, 8260, 7769, 64910, 12621, 65364, 8904, 518, 4764, 0, 41168, + 13204, 4387, 4127, 10530, 65369, 0, 120724, 41044, 0, 0, 9358, 0, 42078, + 5136, 1968, 0, 0, 1337, 10581, 0, 4533, 796, 195001, 0, 0, 12038, 120649, + 12664, 0, 65461, 9798, 6120, 478, 1948, 119007, 10962, 952, 6016, 0, 0, + 9512, 4276, 1206, 3619, 41638, 0, 3843, 8142, 8853, 3361, 41795, 490, + 10715, 3436, 0, 63841, 12817, 9847, 12348, 3930, 12854, 0, 6154, 9551, + 65354, 65346, 784, 65357, 334, 64797, 1453, 65356, 8940, 120329, 8500, + 10428, 10364, 64715, 778, 4317, 10004, 7989, 64676, 3227, 120238, 194654, + 120782, 0, 10855, 13102, 41702, 10309, 9718, 10277, 194958, 120308, + 41624, 5415, 9613, 9001, 4526, 3462, 65215, 64520, 41020, 0, 120042, + 42056, 9759, 64957, 3963, 120304, 8114, 1469, 65445, 65381, 194709, 4988, + 0, 118956, 9598, 904, 352, 0, 1451, 1356, 8453, 4134, 0, 0, 1619, 9703, + 41745, 0, 8575, 119180, 1201, 64732, 12846, 0, 41860, 11919, 64962, + 41550, 5289, 13144, 8511, 9460, 823, 9675, 12305, 5940, 226, 2649, 12387, + 1253, 0, 0, 500, 64521, 9081, 1658, 11936, 64735, 120705, 64660, 63845, + 64784, 9785, 42123, 64783, 194619, 0, 5152, 8935, 41754, 119101, 5304, 0, + 616, 4323, 64666, 4684, 0, 120613, 194912, 65339, 10560, 6048, 4763, + 4112, 118935, 10870, 5260, 9821, 65129, 326, 9681, 4475, 0, 10771, 2876, + 194915, 194833, 6035, 41398, 41192, 9802, 13261, 194880, 453, 41396, + 917564, 13159, 12140, 9572, 65274, 10392, 10328, 40998, 7704, 917542, + 194886, 9800, 4123, 0, 42103, 41000, 7854, 119239, 0, 10977, 64061, + 10344, 9808, 64014, 5394, 4126, 12800, 9521, 9589, 64755, 194917, 4425, + 194897, 10464, 63802, 64769, 1288, 0, 64016, 64024, 12173, 679, 64012, + 194893, 5850, 12049, 118937, 10796, 4474, 10742, 10693, 64006, 1587, + 64005, 0, 120519, 65490, 1369, 12134, 119050, 7927, 0, 1139, 64030, + 64026, 64029, 8970, 64948, 4430, 0, 10774, 4514, 0, 12421, 8194, 0, 1852, + 3057, 65483, 8893, 64032, 12542, 12973, 65341, 120497, 0, 7925, 12423, + 10475, 0, 3496, 1352, 10933, 7707, 9102, 627, 42034, 6158, 8327, 64497, + 0, 6040, 917592, 10129, 64863, 9336, 65451, 5730, 7844, 7798, 64474, + 64259, 1682, 64290, 7820, 119049, 12951, 194906, 7746, 1492, 0, 8288, + 12563, 10728, 5127, 120163, 65509, 5495, 4273, 118922, 9644, 10849, 1833, + 2999, 120612, 64373, 194622, 185, 65085, 6023, 169, 5497, 64611, 8085, 0, + 194850, 194789, 8224, 119010, 1949, 4117, 7847, 120489, 119982, 5321, 0, + 120534, 9313, 2589, 64408, 1689, 7802, 4683, 120502, 120716, 64667, 0, + 1184, 0, 815, 8273, 0, 6049, 120530, 4027, 834, 0, 1803, 64683, 1503, + 8995, 0, 0, 5731, 1381, 2387, 0, 12430, 8289, 10981, 12654, 2881, 65514, + 917600, 9601, 332, 9668, 9766, 5142, 2407, 119221, 0, 6036, 64881, 4026, + 8645, 64789, 2887, 119832, 3526, 6298, 119136, 64475, 4833, 1834, 195095, + 8572, 6021, 10940, 65249, 119848, 8662, 119207, 0, 2652, 10959, 119849, + 10784, 120720, 0, 166, 0, 8635, 9706, 10623, 408, 1828, 0, 13298, 0, + 8531, 8168, 6280, 12324, 8811, 10639, 0, 4832, 64557, 41643, 6279, 12508, + 8713, 10690, 9161, 41645, 1620, 0, 646, 0, 195091, 42129, 609, 119858, + 3472, 8697, 41086, 0, 4343, 6212, 0, 0, 5809, 1950, 239, 119828, 637, + 120048, 41592, 119855, 917539, 120449, 0, 3247, 120754, 12985, 12696, + 119854, 0, 119827, 12929, 10983, 712, 120291, 0, 41567, 0, 0, 0, 119852, + 120793, 119137, 1506, 41565, 0, 4509, 0, 12651, 12216, 64628, 40988, + 11961, 120626, 41727, 7803, 64341, 2396, 42036, 118844, 0, 120264, 355, + 9719, 3886, 9814, 63912, 0, 65444, 996, 42075, 64880, 917578, 65199, + 194810, 8655, 8222, 0, 7939, 10342, 917574, 3178, 917590, 0, 5907, 42071, + 3976, 0, 42161, 0, 5833, 12561, 12555, 5969, 5699, 12562, 12550, 9488, + 40982, 8489, 0, 1488, 0, 13149, 0, 9799, 5265, 66612, 1563, 119091, 9619, + 12464, 0, 917557, 119842, 64508, 5803, 7797, 6070, 10006, 64919, 465, + 6082, 13078, 9692, 194745, 12567, 8116, 795, 0, 7843, 12462, 3607, 12715, + 10046, 9612, 42153, 8218, 9485, 120811, 0, 12468, 8607, 1008, 65322, + 3306, 120321, 65138, 6057, 508, 0, 1766, 119074, 11996, 1820, 4547, 0, + 638, 6083, 120265, 12308, 0, 2305, 0, 0, 9470, 6056, 10878, 65236, 4818, + 6085, 0, 65207, 3915, 41634, 5382, 41639, 0, 6235, 119060, 4028, 1787, + 42180, 41979, 0, 3249, 1768, 1130, 12328, 501, 42016, 10601, 195087, + 917629, 65294, 7742, 0, 13280, 41922, 10747, 118925, 5310, 9475, 0, + 120810, 8959, 5526, 119085, 0, 0, 8568, 119818, 65155, 64939, 5403, 0, + 41703, 64926, 1771, 12460, 8936, 120631, 119023, 0, 10760, 119115, 9158, + 0, 120259, 0, 120582, 5410, 5783, 10365, 8403, 5400, 120526, 120295, + 5027, 9326, 10491, 0, 4831, 120698, 5028, 5587, 0, 0, 5026, 4923, 65086, + 8981, 12382, 8931, 120755, 1415, 8866, 0, 65513, 10461, 12103, 0, 8642, + 5029, 64788, 1580, 3598, 0, 41070, 10053, 0, 0, 120258, 6026, 41515, 0, + 64592, 1716, 1461, 910, 11907, 620, 41001, 3658, 41541, 120107, 120332, + 64758, 5024, 12888, 41003, 118811, 5025, 120767, 41514, 0, 5703, 119124, + 41517, 41504, 41519, 0, 40989, 119160, 5849, 623, 781, 670, 10660, 5769, + 613, 6105, 120774, 477, 1268, 65275, 8906, 592, 1578, 2636, 64404, 10815, + 917602, 8225, 194928, 654, 0, 653, 652, 7721, 647, 7869, 633, 120224, + 42152, 64361, 12480, 6119, 829, 39, 12487, 0, 120529, 0, 12482, 0, 12489, + 9667, 391, 5550, 0, 482, 0, 1203, 0, 1813, 64544, 41311, 9503, 120623, + 2877, 120249, 120758, 1675, 4939, 5315, 0, 66567, 10070, 10595, 65443, + 4576, 0, 64304, 120241, 4277, 40997, 4039, 0, 64472, 368, 13036, 3960, + 65460, 119073, 120247, 120244, 0, 3958, 0, 1849, 194564, 270, 13086, + 10714, 194650, 42064, 11959, 0, 64657, 41608, 3618, 120240, 9069, 6273, + 5156, 364, 9595, 929, 119980, 42035, 707, 41155, 41725, 8691, 0, 224, + 41662, 0, 9332, 4966, 0, 0, 4578, 64513, 3841, 0, 0, 10732, 13074, 9580, + 4972, 9356, 0, 2909, 118847, 1286, 10166, 8682, 12147, 10203, 9608, + 12815, 7730, 11962, 41540, 12507, 1196, 0, 0, 777, 10020, 4375, 41372, + 41924, 525, 12198, 0, 8763, 0, 41628, 533, 11931, 8658, 0, 41520, 2705, + 65010, 13126, 9838, 4377, 8559, 7765, 120234, 63780, 13193, 2701, 119051, + 8679, 5767, 1576, 7735, 9809, 8353, 63747, 41960, 63772, 0, 10889, 1748, + 7757, 65265, 120226, 12803, 0, 2718, 4168, 0, 13308, 63764, 63787, 1179, + 4440, 0, 7694, 363, 8896, 63768, 3485, 12987, 41701, 64908, 120826, 0, + 1591, 42168, 64625, 10192, 0, 119192, 13053, 10013, 5630, 0, 0, 9492, + 10390, 13083, 12833, 5543, 120609, 1640, 12495, 630, 0, 3138, 10996, + 41127, 1043, 0, 12498, 10090, 0, 0, 313, 0, 8615, 120746, 41878, 493, + 41426, 5750, 1717, 9417, 479, 9405, 120771, 0, 9398, 9403, 3520, 8426, + 12490, 64315, 65185, 0, 12493, 5815, 10707, 1002, 12491, 0, 12934, 631, + 120146, 64922, 13161, 41303, 0, 10546, 120147, 118901, 13305, 0, 2797, + 13107, 120095, 306, 714, 3058, 0, 0, 917555, 119961, 194824, 0, 12644, 0, + 0, 194815, 7909, 9157, 4569, 63758, 63805, 0, 63804, 40986, 180, 244, 0, + 12898, 12494, 12674, 8244, 362, 0, 118967, 8037, 0, 0, 120680, 4882, + 5185, 0, 5521, 4885, 5519, 42155, 10302, 4880, 10104, 1027, 1360, 248, + 12424, 10523, 1446, 4319, 41646, 991, 5189, 63754, 10494, 120527, 1722, + 5581, 120151, 470, 118965, 65271, 5523, 0, 64527, 4579, 0, 9549, 12511, + 10549, 12514, 9661, 0, 12000, 9602, 8623, 0, 0, 0, 0, 12512, 41341, + 13041, 6150, 9846, 659, 6098, 0, 1174, 10334, 0, 8311, 12510, 63856, + 13039, 0, 12513, 9284, 12471, 0, 12330, 0, 63853, 0, 2323, 65288, 2319, + 6293, 12477, 0, 2311, 194867, 4415, 237, 6281, 0, 0, 9010, 2309, 7897, + 8173, 64894, 12469, 0, 118979, 1736, 10609, 3894, 12228, 9397, 10987, + 3383, 9396, 9393, 693, 9130, 314, 9389, 6209, 9387, 9388, 4932, 9386, + 9383, 5332, 12204, 9285, 10436, 8185, 41808, 1751, 273, 8165, 13166, + 2313, 65449, 7948, 9236, 8544, 4528, 2584, 6301, 119185, 6289, 10484, + 9463, 0, 9339, 10922, 3757, 3147, 195092, 12420, 10421, 120488, 2310, + 64389, 2326, 9382, 2565, 9380, 9377, 7921, 9375, 9376, 1683, 9374, 2567, + 8596, 12444, 4044, 41274, 12527, 8210, 120756, 8334, 474, 12331, 0, + 42032, 8744, 726, 9839, 120313, 0, 0, 41276, 42030, 10467, 12522, 9835, + 0, 4951, 634, 12275, 10895, 65492, 274, 120236, 1858, 4744, 4746, 0, + 9548, 120596, 403, 120117, 12503, 9610, 8068, 8197, 63996, 699, 120634, + 41665, 1819, 10496, 0, 42182, 0, 13262, 0, 41667, 12506, 194835, 1923, 0, + 12500, 118849, 12509, 64393, 194755, 120692, 10589, 64279, 41047, 2996, + 1937, 194716, 917505, 8084, 4047, 3608, 63840, 65016, 1107, 194718, 9076, + 8862, 120636, 293, 194841, 64766, 64791, 41827, 13222, 65416, 10579, + 8560, 10463, 63994, 118835, 4803, 9043, 1739, 1941, 498, 64471, 1713, 0, + 12529, 8042, 0, 2344, 12528, 6297, 2414, 0, 0, 3231, 0, 12200, 0, 65156, + 12530, 2537, 969, 41429, 12658, 13034, 6165, 13035, 0, 41433, 4719, 469, + 119240, 4363, 5211, 8914, 0, 119948, 1772, 1435, 64876, 2969, 6046, + 64812, 6208, 64101, 5746, 12215, 0, 4931, 1951, 8612, 917599, 9607, 0, + 338, 0, 5061, 10675, 41106, 10767, 1491, 8115, 0, 11941, 41061, 8227, + 8270, 1218, 0, 41993, 41509, 0, 63808, 12889, 120603, 41108, 4486, 41995, + 1075, 1958, 10925, 41992, 41506, 0, 0, 120326, 10257, 0, 10273, 120327, + 7692, 12669, 8008, 120320, 330, 8566, 65083, 9046, 41117, 41126, 12532, + 0, 120114, 3508, 7794, 0, 0, 9645, 65026, 10770, 3669, 3968, 65028, 0, + 13028, 194890, 12537, 194802, 120112, 0, 12536, 2350, 13029, 66320, 0, + 12116, 13030, 118980, 4527, 1588, 12538, 8409, 119026, 10683, 65398, 787, + 9502, 4948, 12484, 4032, 118940, 41654, 65399, 6207, 0, 6117, 65401, + 8412, 65247, 194842, 8734, 644, 9769, 41657, 10149, 3659, 9533, 184, + 9853, 10827, 12488, 65382, 10502, 41556, 12623, 65474, 2354, 0, 8220, + 118856, 6295, 901, 41510, 7953, 118826, 5157, 4020, 63811, 11927, 66584, + 64818, 0, 41687, 64303, 0, 63816, 0, 0, 119868, 65198, 0, 0, 118930, + 64094, 118926, 7877, 2352, 0, 120726, 64576, 13299, 1407, 10911, 0, + 13026, 0, 7941, 0, 8362, 8903, 9777, 0, 10399, 5869, 8636, 0, 1343, 0, + 12649, 9325, 13025, 6283, 64499, 12643, 0, 0, 13027, 8543, 10051, 9216, + 8263, 13019, 41258, 8625, 194754, 13021, 10477, 3136, 8733, 120742, 8315, + 13022, 10196, 64588, 0, 6152, 0, 5477, 917566, 10112, 194763, 13020, + 194765, 8675, 194767, 194766, 119870, 0, 10978, 8029, 6091, 120350, 4485, + 3335, 118886, 3590, 9776, 41397, 66578, 5215, 194750, 3333, 1632, 63900, + 3588, 3342, 9341, 5363, 917559, 12725, 120564, 0, 64076, 223, 64079, + 1611, 13246, 13018, 120319, 63792, 65245, 3337, 1171, 194605, 194587, 0, + 1805, 8772, 41423, 119241, 11945, 8708, 13046, 8838, 425, 4025, 10709, + 41868, 0, 2392, 13047, 4530, 120105, 10617, 1213, 119233, 120103, 797, + 118814, 7888, 13050, 194742, 64387, 4115, 0, 194735, 0, 3277, 8929, 4947, + 41055, 0, 64276, 426, 194724, 13045, 8251, 10136, 7751, 194727, 9726, + 119253, 1224, 12806, 8768, 13044, 0, 1764, 3101, 119817, 8480, 1078, + 9757, 65439, 41057, 120167, 0, 8663, 9312, 4413, 4539, 3787, 42160, 9222, + 0, 9165, 1572, 9092, 12593, 41961, 2346, 12724, 8958, 0, 9646, 3773, + 41825, 1293, 7947, 12003, 120228, 13043, 8056, 2454, 5349, 208, 0, + 120168, 64849, 120525, 8816, 10699, 10840, 0, 7825, 5661, 0, 12595, 3603, + 41109, 2398, 3548, 1157, 64903, 8638, 0, 0, 3115, 0, 0, 118787, 8235, + 4405, 10086, 4876, 0, 0, 119256, 65430, 64493, 6079, 12646, 10764, 8158, + 41561, 41472, 998, 13051, 13105, 3143, 194674, 194673, 41559, 8509, 7882, + 13052, 118948, 5665, 530, 119071, 41986, 0, 12002, 64526, 5742, 5664, + 4692, 8979, 12310, 4007, 63970, 194686, 7896, 1121, 119168, 3382, 63959, + 66373, 13231, 0, 64874, 4732, 6311, 0, 12004, 63976, 8627, 63977, 10110, + 194671, 41705, 0, 42028, 64327, 10509, 2795, 63979, 65308, 0, 0, 6275, + 917573, 41699, 120324, 194655, 3229, 65517, 63952, 41133, 0, 5407, 12823, + 2331, 41678, 42026, 12156, 2336, 119046, 0, 120323, 0, 0, 1921, 120003, + 194642, 0, 822, 65529, 12691, 4284, 194657, 194648, 194659, 12841, 9229, + 10956, 41255, 12607, 5311, 1795, 965, 3521, 10587, 5774, 8325, 0, 65403, + 0, 1854, 10794, 119250, 10057, 6294, 3144, 64780, 5280, 65019, 4344, + 12905, 41610, 6076, 748, 12385, 768, 535, 442, 12375, 194652, 194629, + 10556, 2475, 12388, 4889, 8968, 6071, 3593, 64093, 4804, 2342, 0, 1800, + 0, 4894, 467, 4890, 120803, 194800, 120707, 4893, 8421, 12433, 10666, + 4888, 502, 64080, 64615, 41490, 0, 12043, 10119, 316, 0, 10230, 65191, + 64087, 64924, 64086, 64746, 2332, 4860, 412, 194567, 11997, 12432, 9583, + 8058, 5546, 8019, 917553, 66561, 63750, 12203, 5544, 2355, 8913, 120390, + 4875, 10613, 120381, 12137, 5548, 9344, 6250, 7944, 120395, 13104, 6077, + 12383, 65452, 120405, 0, 3134, 0, 120775, 4669, 0, 0, 0, 3050, 63839, + 10319, 119075, 10383, 118842, 4592, 120694, 10809, 0, 4691, 0, 9345, 621, + 0, 120055, 4918, 10734, 120032, 64631, 0, 7804, 0, 10811, 8457, 10545, + 4914, 10271, 3786, 8886, 4917, 0, 64914, 7923, 3716, 5464, 9996, 118893, + 2361, 7971, 8195, 0, 9566, 7682, 3722, 8086, 41707, 10845, 8319, 2312, + 40977, 10050, 10874, 8305, 8859, 41458, 40980, 65110, 13202, 0, 12582, + 9119, 0, 7920, 41521, 4021, 6288, 7985, 0, 5653, 120063, 10891, 7698, + 5658, 410, 41552, 1802, 120789, 4913, 0, 41659, 41671, 1827, 0, 64396, + 41668, 9077, 2327, 8810, 12959, 120372, 12705, 3860, 10756, 9239, 8821, + 6153, 2867, 120364, 42158, 698, 120359, 8749, 10356, 12698, 64858, 361, + 12641, 845, 194599, 41560, 11970, 4562, 63756, 2926, 194639, 4099, + 194692, 194695, 7936, 194697, 611, 0, 4716, 118891, 0, 120528, 7686, + 120568, 0, 194700, 120543, 118875, 194701, 6291, 5462, 10823, 41669, + 9734, 65455, 9071, 4655, 4151, 0, 0, 0, 839, 42162, 7695, 8769, 65246, + 10737, 119194, 4859, 64467, 65504, 4826, 118998, 41090, 0, 12172, 120387, + 0, 0, 120345, 12576, 7842, 12839, 0, 804, 2699, 0, 10542, 2985, 119222, + 194596, 8271, 10091, 0, 9468, 0, 9827, 64106, 0, 286, 12323, 118830, + 66592, 0, 0, 1425, 35, 119229, 65084, 0, 41210, 64432, 8482, 0, 6090, + 5032, 7812, 10534, 195035, 664, 194633, 5034, 4272, 65211, 40967, 40965, + 42024, 12704, 13294, 66589, 64869, 6032, 120367, 9129, 119867, 0, 120166, + 0, 194813, 5244, 120169, 120170, 41161, 5518, 4174, 10993, 8189, 968, + 120161, 1169, 434, 41437, 66573, 6034, 41164, 64744, 12574, 118867, 0, + 524, 0, 118934, 788, 0, 12679, 64506, 64528, 1663, 10419, 0, 41227, + 120398, 12346, 12855, 64848, 120399, 10415, 41562, 0, 120432, 118850, + 119141, 0, 0, 194662, 959, 8885, 12564, 64333, 120339, 9469, 5195, 5445, + 9355, 64323, 42151, 4644, 8989, 221, 310, 41253, 41564, 8010, 120396, + 4962, 63766, 8855, 10054, 120413, 120618, 0, 9012, 120415, 12088, 41002, + 13215, 120287, 10451, 64260, 374, 120153, 816, 64634, 120148, 120054, + 41934, 3873, 8367, 0, 64608, 4715, 6101, 11987, 41936, 0, 64511, 12723, + 65089, 0, 307, 120416, 9585, 5374, 120178, 1462, 10235, 0, 194670, 0, + 12119, 120498, 13024, 1929, 120426, 12142, 120425, 12236, 41419, 194618, + 120427, 12982, 64374, 5378, 194666, 64295, 41421, 0, 741, 10083, 0, + 120662, 821, 0, 2498, 5800, 10755, 2992, 1760, 8124, 4469, 2324, 828, + 3611, 194865, 0, 1185, 194728, 531, 0, 10628, 194683, 0, 7999, 8204, + 3614, 2827, 9696, 10942, 7713, 2348, 4354, 10904, 4380, 194608, 7833, + 10573, 5320, 41240, 194611, 3000, 10301, 1810, 3673, 5137, 9525, 64569, + 194849, 118861, 0, 120821, 10121, 64940, 194592, 120406, 12824, 13066, + 4748, 7970, 120630, 12608, 194600, 5871, 41160, 9700, 12580, 0, 120777, + 119811, 3967, 7898, 13137, 8775, 64560, 12713, 2963, 9090, 8410, 4454, + 723, 1734, 966, 4449, 0, 64594, 2456, 231, 2320, 194589, 339, 4968, + 194590, 63752, 8075, 1230, 120795, 8047, 3597, 9761, 10584, 41542, 65404, + 1290, 66358, 8352, 0, 5687, 120505, 3840, 1584, 0, 6045, 0, 10498, 9704, + 0, 120338, 119869, 119992, 12311, 8660, 0, 8365, 8643, 0, 0, 4483, 1709, + 64399, 9780, 6080, 13092, 120044, 1746, 6072, 8667, 12121, 194579, 13140, + 194581, 194584, 2531, 4480, 120765, 0, 1226, 1259, 0, 10394, 0, 10897, + 194560, 605, 120164, 641, 5219, 12342, 64100, 41500, 41129, 311, 12283, + 6221, 9075, 120358, 5466, 10877, 118868, 64885, 120737, 4535, 2667, 4271, + 65406, 0, 345, 41410, 10829, 41198, 0, 41407, 64104, 5037, 41131, 1776, + 8422, 41201, 64103, 41508, 4660, 323, 0, 0, 64338, 1295, 0, 4625, 8323, + 4630, 247, 0, 0, 12338, 4651, 2668, 12080, 0, 64574, 11933, 2519, 0, + 41903, 41079, 5053, 194787, 5049, 0, 65459, 706, 7754, 7727, 8738, 4031, + 6278, 0, 9672, 649, 5514, 118920, 0, 10280, 12670, 1013, 41218, 0, 705, + 41591, 8755, 0, 1183, 65252, 8268, 917549, 0, 8157, 9736, 64503, 65418, + 118921, 4747, 0, 194843, 11913, 4718, 0, 10837, 5141, 10614, 0, 7962, + 12211, 9837, 0, 64722, 119008, 5719, 119977, 9773, 119068, 0, 1857, + 119921, 4626, 8464, 8472, 0, 4629, 8499, 6059, 0, 4624, 7818, 8535, + 119914, 65179, 7805, 64805, 64811, 12242, 41011, 194905, 0, 10558, 0, 0, + 0, 8492, 0, 8459, 0, 1788, 1579, 10766, 0, 0, 8048, 9543, 9028, 120522, + 119177, 0, 41455, 1285, 64882, 194620, 10092, 8684, 12640, 6102, 0, 5298, + 12625, 5294, 0, 42013, 3940, 41597, 119917, 0, 9816, 8665, 0, 12073, + 12630, 1653, 64669, 10153, 0, 6166, 118791, 119913, 0, 5292, 0, 0, 1939, + 913, 3970, 64599, 12455, 1793, 0, 120162, 0, 7878, 8211, 65263, 0, 0, + 118910, 0, 119125, 3514, 13219, 9569, 10865, 120645, 5263, 13286, 0, + 5500, 10022, 65387, 118831, 65384, 5322, 980, 66354, 10008, 5324, 0, + 3784, 41614, 64751, 6230, 0, 63885, 10085, 3360, 8098, 41616, 0, 41734, + 10096, 41613, 8072, 120299, 0, 41821, 1249, 7783, 41731, 12032, 8237, 0, + 64899, 12395, 41149, 12818, 120565, 10462, 41150, 194574, 9795, 119057, + 194609, 13213, 0, 0, 41152, 194679, 9249, 12518, 7808, 1829, 194780, + 41811, 4358, 65315, 10831, 0, 0, 0, 64354, 1710, 0, 10168, 120597, 9781, + 49, 194613, 194953, 6258, 8269, 120594, 9741, 0, 5649, 194617, 315, + 12813, 1643, 194615, 12397, 3470, 8884, 65175, 41099, 65314, 120008, + 1378, 65163, 1072, 120647, 118802, 0, 0, 0, 120002, 0, 1080, 120411, + 8787, 194828, 1101, 41618, 0, 8405, 0, 12632, 1086, 10968, 42088, 7680, + 8847, 10805, 120714, 12639, 3380, 8123, 1091, 6121, 7977, 4501, 12665, + 8119, 12998, 66309, 0, 1494, 0, 3127, 0, 64945, 12930, 1394, 119230, 0, + 12363, 5345, 9789, 0, 9527, 120659, 64582, 12977, 12309, 42090, 8022, + 10635, 12939, 12404, 65168, 42003, 2495, 5848, 8726, 5570, 2587, 12410, + 41722, 1012, 8100, 7890, 120296, 0, 10649, 5569, 6229, 1593, 65319, 6063, + 619, 0, 65080, 6053, 194598, 4120, 65337, 64727, 9160, 0, 119214, 0, + 9366, 9016, 42006, 6055, 3870, 4279, 2500, 10757, 1507, 8497, 8602, + 65320, 41991, 65334, 65333, 65332, 65331, 42059, 42061, 9080, 120099, + 9128, 64480, 5571, 3674, 9740, 9121, 4371, 5798, 10408, 42085, 10107, + 4106, 41989, 65313, 42074, 63999, 41228, 0, 10233, 13098, 120423, 41239, + 0, 0, 8182, 0, 119831, 0, 11947, 0, 5847, 1505, 9131, 0, 12606, 12695, + 41988, 41250, 12175, 0, 64075, 120739, 7809, 0, 0, 562, 8120, 120701, 0, + 13033, 64738, 3219, 120532, 10664, 1366, 1037, 194747, 4551, 0, 118945, + 0, 10637, 4568, 549, 1570, 10478, 2835, 12517, 557, 9457, 5952, 64649, + 41056, 12519, 41004, 0, 2825, 66636, 10825, 8079, 2821, 41046, 0, 195065, + 12111, 3927, 13071, 12515, 452, 5271, 5492, 64718, 2831, 10604, 10144, + 41706, 5212, 5493, 41120, 8916, 0, 9747, 12019, 41332, 1618, 12333, + 917584, 1668, 10430, 0, 5853, 1187, 10363, 118990, 12956, 0, 119107, 0, + 3240, 12060, 12194, 120100, 41631, 41065, 5323, 8166, 4557, 0, 2707, + 8309, 0, 65297, 41052, 0, 2697, 8752, 194689, 4912, 2695, 65172, 0, + 119223, 8864, 0, 64798, 10736, 2693, 12125, 41124, 0, 1164, 0, 0, 1035, + 41067, 120219, 7881, 701, 12178, 3489, 0, 12340, 120751, 5248, 12218, + 120538, 6303, 3796, 41123, 0, 3994, 120283, 10457, 9991, 41128, 64485, + 5792, 120282, 64857, 42171, 2855, 7994, 64762, 6104, 0, 0, 9340, 10654, + 1589, 119226, 296, 3246, 7906, 2879, 41981, 41620, 0, 7815, 120797, + 120482, 0, 0, 10585, 12579, 1496, 747, 12708, 942, 2378, 10960, 119830, + 5299, 0, 9320, 5449, 1232, 8139, 6216, 41431, 0, 65373, 5295, 66624, 0, + 1223, 1642, 174, 120824, 12158, 4161, 2374, 120546, 8475, 3212, 66313, + 3211, 194576, 5286, 0, 0, 917546, 9728, 3846, 8070, 5536, 0, 7705, 11942, + 0, 12136, 3309, 0, 66377, 41491, 0, 4986, 12189, 41653, 1280, 1241, + 917537, 4257, 8496, 0, 6220, 9004, 65411, 194580, 41513, 41650, 0, + 194578, 194878, 12914, 120740, 0, 12797, 6078, 10237, 0, 1475, 119118, + 11979, 6084, 118900, 41064, 41062, 9635, 12600, 3256, 41236, 42076, 0, 0, + 119866, 8727, 65304, 64866, 41237, 64073, 64867, 10562, 118947, 65329, + 64071, 10640, 3248, 2613, 119865, 9015, 0, 66568, 3635, 64337, 41651, + 41241, 64944, 3494, 0, 0, 10588, 0, 0, 0, 0, 635, 0, 194797, 0, 65312, + 5447, 0, 0, 64382, 4010, 7984, 8600, 41915, 120139, 4176, 41105, 5812, + 119933, 6232, 0, 0, 194588, 318, 5302, 0, 0, 4335, 3649, 3941, 42145, + 41110, 3634, 64892, 9113, 1954, 12155, 7866, 0, 0, 42146, 120134, 120138, + 120129, 2849, 120128, 0, 7938, 12960, 1761, 4586, 65379, 350, 10930, + 119936, 509, 0, 0, 0, 0, 542, 5133, 41680, 0, 9500, 0, 1514, 64741, 0, + 5453, 65533, 64921, 0, 2496, 8493, 944, 9368, 3890, 12168, 1438, 8817, + 120592, 10818, 41947, 1220, 120828, 63931, 1194, 3242, 1571, 9555, 8598, + 0, 6169, 943, 41946, 2798, 312, 0, 41980, 119025, 120717, 8877, 269, + 3495, 6272, 9617, 1460, 8988, 120660, 4891, 0, 10862, 0, 41119, 41416, 0, + 4173, 0, 194637, 0, 12895, 64955, 41418, 0, 119022, 120286, 41415, 6296, + 9582, 193, 12188, 0, 64680, 41122, 1730, 2457, 4493, 2314, 10469, 1362, + 9822, 7703, 8840, 5807, 0, 120451, 8534, 0, 4426, 0, 0, 120209, 119123, + 7874, 8681, 5220, 120281, 13136, 119825, 2416, 3310, 10972, 118881, 379, + 119215, 13220, 0, 0, 3223, 5517, 1284, 8041, 4549, 120475, 5240, 9811, + 10012, 3096, 120275, 0, 0, 8515, 8688, 12866, 0, 3294, 9501, 0, 1272, + 65485, 64514, 64654, 120035, 65210, 1467, 10158, 10040, 5288, 9519, + 41861, 8132, 64090, 118899, 12193, 66615, 65493, 3215, 0, 7710, 1610, + 120271, 0, 63881, 0, 0, 5181, 5275, 0, 228, 8637, 1501, 120476, 3789, + 5179, 0, 6225, 118927, 0, 1725, 66603, 8196, 9352, 12042, 0, 0, 9537, + 3961, 5762, 1967, 2605, 4500, 64561, 8104, 4981, 917545, 3405, 0, 63876, + 10414, 13001, 8141, 9559, 2600, 41649, 41647, 64851, 0, 3237, 8631, 2545, + 10466, 8541, 0, 0, 41866, 0, 120430, 64517, 10127, 0, 1650, 262, 1637, + 10958, 7901, 3238, 41945, 0, 41941, 3308, 65158, 10860, 8614, 65220, + 41493, 120624, 41943, 10762, 0, 45, 0, 0, 8106, 4128, 10065, 64083, 4494, + 0, 4012, 10395, 0, 9084, 4537, 8737, 64089, 11004, 695, 739, 696, 7912, + 2620, 64398, 195044, 9227, 0, 179, 5098, 691, 738, 2853, 0, 118813, 3868, + 688, 0, 690, 2548, 737, 974, 64084, 119837, 10854, 119839, 10034, 3985, + 8783, 118838, 9362, 0, 0, 4682, 118869, 12809, 8082, 4685, 3158, 10879, + 4389, 4680, 923, 41863, 3851, 292, 13002, 119845, 119844, 3221, 1763, + 64468, 4612, 119851, 119850, 12999, 41219, 12349, 64644, 10782, 3637, + 12996, 0, 11949, 63922, 10594, 3228, 41826, 64624, 0, 10967, 2731, 0, + 9651, 651, 3891, 7696, 0, 2337, 1735, 0, 0, 4177, 195098, 9089, 66312, + 64695, 120580, 64500, 1860, 2654, 0, 1856, 12240, 8599, 195049, 66356, + 118999, 3458, 3208, 12975, 8498, 119121, 8949, 8758, 9450, 194859, 1569, + 63888, 12534, 12124, 7690, 119254, 12533, 917551, 7740, 4543, 41471, 0, + 64674, 0, 0, 0, 11980, 0, 41544, 41689, 63789, 12282, 64909, 13064, + 63893, 64556, 8850, 9238, 0, 8561, 4573, 0, 0, 12791, 120605, 0, 0, + 120744, 8778, 10630, 12900, 0, 10950, 8314, 194936, 12790, 8804, 65092, + 66607, 12792, 120435, 42018, 1744, 12789, 10366, 12317, 41310, 0, 13164, + 10723, 967, 120253, 64546, 12690, 120454, 3257, 0, 9862, 1845, 2974, + 10446, 41848, 0, 278, 10580, 10089, 870, 0, 3499, 8609, 42149, 876, 871, + 877, 6002, 878, 42015, 879, 0, 4563, 65176, 41308, 0, 65306, 867, 9520, + 872, 8646, 868, 873, 0, 0, 869, 874, 195048, 1940, 875, 790, 220, 65193, + 194845, 10678, 10044, 194877, 5429, 13082, 0, 917541, 5707, 10393, 0, + 120267, 42067, 41890, 5433, 10657, 7911, 0, 3742, 9775, 3959, 0, 5425, + 4977, 2467, 5317, 5423, 4611, 120553, 8040, 5069, 9679, 4182, 0, 4676, + 120501, 41073, 4418, 4184, 4628, 10208, 12989, 118784, 917540, 1851, + 12186, 120601, 11908, 120254, 9360, 9083, 0, 41764, 194565, 12837, 8829, + 7711, 64423, 119218, 194777, 120260, 118855, 8809, 64371, 365, 12056, + 41382, 0, 0, 65395, 42080, 195040, 5516, 2845, 7717, 4588, 41717, 63830, + 544, 12045, 2433, 0, 5515, 3352, 0, 64377, 65437, 793, 65194, 0, 305, 0, + 119002, 842, 120576, 8208, 0, 41695, 1647, 118877, 5608, 63824, 917625, + 818, 5337, 917622, 917621, 120531, 9638, 8061, 8735, 12483, 120468, + 13003, 119140, 10973, 66359, 1372, 118858, 917608, 4969, 1254, 917605, + 989, 64257, 118862, 65228, 6060, 0, 4326, 2840, 64601, 13068, 0, 65242, + 3245, 9068, 119069, 949, 0, 0, 6148, 8605, 2651, 0, 0, 0, 0, 0, 65106, + 120418, 0, 0, 41796, 1269, 195028, 63868, 41777, 64372, 5144, 3226, 655, + 120467, 4431, 4331, 8777, 3285, 41834, 5279, 0, 10336, 8312, 0, 12091, + 671, 250, 0, 618, 668, 610, 65195, 0, 1152, 5256, 640, 41229, 12207, + 1067, 255, 3905, 917593, 9493, 120466, 41014, 10795, 0, 0, 120728, 0, + 10653, 41272, 0, 13287, 0, 65166, 9019, 0, 0, 120695, 987, 64410, 5527, + 2768, 10684, 3365, 5135, 0, 12796, 11953, 0, 0, 5139, 346, 119144, 6305, + 12609, 4675, 5168, 5530, 5210, 0, 4627, 8253, 5208, 1136, 65433, 120587, + 5218, 7976, 118864, 65285, 3244, 5529, 0, 0, 0, 5432, 64258, 4041, 8784, + 2357, 0, 5528, 229, 42140, 119884, 0, 0, 119881, 119880, 119197, 4000, + 119877, 119876, 665, 119045, 3206, 7770, 7884, 64853, 0, 118916, 0, 211, + 2509, 7790, 10470, 7861, 3220, 10791, 64050, 450, 8951, 5214, 10432, + 8118, 5450, 10768, 1233, 4661, 5852, 0, 66338, 41865, 1708, 13293, 40985, + 2623, 10927, 1701, 0, 2388, 4698, 41761, 1066, 8361, 4701, 41758, 5444, + 2617, 64889, 8267, 119863, 119089, 0, 0, 120633, 2625, 8801, 3053, 4340, + 120412, 3631, 10955, 7850, 120292, 8416, 917607, 120203, 65507, 194803, + 12660, 8232, 65434, 194807, 194713, 41069, 194808, 0, 12099, 4310, 4336, + 6252, 713, 41068, 7990, 3990, 194811, 65113, 64638, 65243, 13145, 4489, + 194791, 42138, 1030, 5358, 64577, 9513, 10370, 9357, 194764, 1773, 10250, + 10258, 2712, 1635, 7745, 1410, 0, 0, 94, 194965, 120149, 194731, 8908, + 559, 120421, 12862, 0, 10752, 4892, 10876, 64537, 41307, 8732, 120336, + 5777, 1757, 9539, 4696, 2586, 65248, 8945, 8466, 3641, 5419, 41803, + 42062, 0, 0, 120344, 3668, 120823, 8610, 12226, 0, 194949, 2340, 936, + 13289, 64478, 120436, 1459, 0, 10499, 2962, 0, 2321, 1504, 10465, 41312, + 8921, 195025, 120206, 195026, 64525, 41901, 63814, 4113, 2949, 2372, 336, + 194774, 2958, 12152, 5348, 682, 2395, 120061, 13291, 64743, 10593, 1703, + 4013, 194779, 8033, 120064, 65152, 9810, 10198, 4150, 12970, 8318, 41790, + 10109, 41893, 2360, 41794, 12858, 0, 3999, 3777, 118946, 1965, 9796, + 2411, 194950, 799, 0, 10276, 10308, 10372, 63832, 8501, 63833, 2317, + 10260, 41317, 120513, 5417, 0, 10384, 0, 9353, 0, 7753, 2351, 10641, + 64489, 41314, 0, 119812, 0, 119236, 230, 65431, 12009, 0, 4855, 4165, + 8746, 5441, 9654, 10288, 10320, 0, 10596, 0, 0, 4784, 0, 13270, 7786, + 10098, 41147, 194570, 63769, 680, 6274, 10312, 1181, 0, 3174, 13127, 0, + 64822, 41887, 0, 4862, 9735, 120709, 0, 917604, 3914, 41037, 10828, 9065, + 12961, 41039, 119173, 0, 6231, 289, 65302, 4694, 64504, 4690, 0, 118955, + 0, 4693, 65257, 40987, 4667, 4688, 0, 8828, 0, 0, 1246, 3110, 64705, + 12197, 41008, 4749, 0, 0, 921, 218, 0, 1520, 242, 4786, 41700, 8217, + 8932, 64653, 7834, 10088, 0, 0, 64681, 5313, 951, 8888, 64534, 4816, 0, + 0, 4009, 194694, 0, 120562, 41549, 195031, 64860, 119138, 119900, 4689, + 119888, 0, 120158, 119209, 120159, 1646, 120156, 119891, 4040, 194734, + 65118, 119889, 2579, 119905, 3177, 8207, 9099, 4107, 0, 119894, 662, + 120706, 9244, 0, 63781, 10084, 0, 0, 118840, 194858, 41929, 3399, 9851, + 120442, 8739, 9059, 0, 7687, 64637, 8854, 40993, 52, 13241, 0, 0, 120444, + 1777, 9151, 1137, 0, 749, 120809, 120584, 5385, 3978, 917594, 0, 0, 5989, + 0, 10170, 65013, 0, 41685, 64702, 120438, 8425, 41684, 0, 519, 10369, + 64547, 1585, 195030, 10249, 422, 1500, 10305, 986, 0, 3666, 5781, 5599, + 3098, 2494, 120202, 4861, 0, 64334, 194712, 0, 0, 41221, 65102, 8961, + 252, 10243, 10245, 63936, 0, 120452, 194707, 63751, 9478, 2508, 9060, + 917630, 202, 10761, 120747, 1242, 12899, 120447, 194705, 63940, 64533, 0, + 9593, 10543, 2403, 12979, 64609, 0, 65260, 2504, 9784, 41024, 7764, + 64701, 9514, 120825, 5859, 119259, 2858, 8298, 0, 120700, 65478, 9691, + 4971, 12992, 2753, 1936, 0, 8456, 2751, 12662, 2763, 8953, 42104, 10731, + 7774, 4780, 9792, 63991, 0, 194871, 0, 120764, 2856, 10019, 47, 63989, + 2823, 4365, 120629, 0, 3647, 7899, 2602, 8417, 119903, 0, 41135, 120437, + 4033, 118854, 194848, 172, 194720, 212, 41137, 12350, 12320, 118808, + 64623, 0, 8257, 8915, 2759, 945, 3732, 120230, 0, 5344, 194851, 1291, 0, + 9062, 119252, 9531, 13155, 8505, 64479, 12062, 119018, 64703, 65487, 0, + 10900, 41531, 1263, 3720, 12048, 63935, 64292, 41524, 64692, 12652, 6099, + 41534, 0, 63933, 64426, 299, 0, 119892, 63951, 3524, 64785, 8831, 0, + 8674, 3075, 0, 8245, 0, 12624, 0, 1673, 4811, 63928, 5845, 9338, 6243, + 65414, 2581, 4001, 0, 9820, 64098, 12187, 5551, 0, 5984, 0, 195073, 4393, + 10566, 120407, 8680, 0, 194726, 2588, 5422, 0, 0, 3491, 2471, 0, 2883, + 2749, 63921, 0, 10913, 0, 194725, 119134, 675, 120551, 63924, 0, 41287, + 6219, 63926, 0, 41232, 9329, 63925, 41153, 219, 63945, 41330, 692, 65200, + 0, 9240, 3181, 9688, 0, 1222, 66369, 8262, 119155, 64530, 0, 64610, 3092, + 12092, 9615, 64808, 120691, 8013, 0, 0, 195019, 8895, 5253, 0, 5458, 0, + 922, 118805, 0, 65111, 0, 3218, 12618, 63997, 120469, 63831, 8962, 8569, + 9641, 11932, 12202, 3214, 120461, 9604, 12053, 3207, 120465, 63826, + 118941, 64392, 120141, 63825, 2844, 3205, 41974, 41286, 12139, 66588, + 64708, 0, 3358, 2606, 0, 3104, 2608, 0, 1173, 0, 5308, 195007, 290, 0, + 194937, 2862, 2792, 64498, 66371, 378, 2610, 66591, 65079, 0, 65372, 0, + 37, 0, 0, 1814, 120479, 3209, 118843, 0, 10638, 9768, 64648, 0, 66372, 0, + 2591, 2837, 4341, 41403, 64105, 0, 5233, 65270, 64792, 195090, 3570, + 9112, 0, 40991, 9490, 63761, 1685, 595, 64856, 194730, 1292, 6222, + 120142, 3654, 120552, 9637, 0, 2535, 41293, 0, 10656, 194983, 3243, 9014, + 5606, 63762, 538, 11006, 5602, 7807, 8073, 0, 10629, 8203, 0, 3056, 8458, + 41778, 8495, 8762, 10508, 917552, 779, 9818, 64367, 2465, 3463, 8193, 0, + 9730, 8695, 4738, 0, 5811, 12345, 64904, 0, 504, 0, 10899, 8982, 0, 0, 0, + 782, 4867, 10883, 1262, 64771, 732, 3737, 917614, 1548, 0, 120589, 1832, + 5604, 5611, 41141, 0, 4376, 64612, 11991, 3745, 41738, 119928, 1502, 0, + 0, 3869, 11937, 5702, 3655, 1783, 0, 5728, 195086, 13285, 120521, 11918, + 9603, 5724, 5254, 5727, 7724, 0, 119901, 5723, 5129, 194898, 0, 10597, + 9033, 5614, 5893, 6223, 12303, 42073, 0, 120702, 0, 119862, 4792, 0, + 1964, 0, 41950, 12146, 0, 120648, 66570, 195082, 894, 300, 194595, 10037, + 120675, 195085, 0, 9783, 2562, 2607, 64740, 64830, 0, 0, 0, 119861, + 64056, 13062, 64946, 5096, 5095, 2863, 3424, 0, 10454, 118801, 5094, + 10093, 4369, 13156, 12306, 5401, 5093, 119909, 194597, 65251, 5092, 526, + 0, 41295, 5091, 176, 41691, 8985, 4104, 119911, 6285, 1215, 11985, 5744, + 12272, 9832, 0, 3713, 13218, 0, 0, 8980, 118988, 12293, 8844, 13106, + 41505, 42082, 4278, 1737, 8987, 12917, 0, 9074, 917560, 9335, 12850, + 118991, 8113, 10339, 5255, 1786, 661, 0, 5475, 0, 41854, 120620, 0, + 12419, 1160, 1267, 119238, 41217, 0, 10018, 360, 194792, 3621, 64662, + 5863, 3137, 0, 12108, 12928, 41216, 1228, 2616, 119190, 64401, 65234, + 10745, 1714, 3135, 0, 0, 0, 3142, 0, 0, 10819, 917565, 0, 0, 64, 1470, + 194566, 10291, 6227, 2826, 41749, 0, 119864, 6163, 9708, 13250, 0, 42011, + 0, 8603, 12206, 5839, 1702, 1240, 41461, 6286, 194995, 5834, 0, 3858, 0, + 1765, 12086, 42001, 1600, 64309, 0, 0, 8401, 120786, 42014, 9282, 8882, + 118929, 10479, 2570, 2852, 5367, 4601, 194941, 0, 1234, 9678, 13115, + 66310, 12667, 0, 41973, 10147, 12935, 0, 0, 118829, 0, 8163, 41716, + 12727, 194816, 120533, 41289, 0, 13129, 2864, 8977, 602, 10435, 9395, + 41675, 120340, 2765, 64540, 41279, 120414, 119246, 0, 0, 0, 119220, + 10887, 65206, 118963, 64920, 66593, 63914, 12150, 263, 194563, 41288, 0, + 9633, 10886, 119042, 7831, 12067, 0, 0, 0, 8076, 118827, 8290, 8291, 0, + 0, 0, 2596, 10852, 63911, 13113, 0, 42019, 2393, 8766, 9087, 750, 0, + 41574, 10163, 120654, 63913, 10441, 5954, 64931, 4314, 194675, 198, 0, + 730, 41441, 7819, 194826, 0, 13165, 1720, 63905, 8619, 678, 8240, 118960, + 194852, 3751, 0, 0, 4262, 1798, 709, 0, 1354, 10778, 13152, 0, 3892, + 8137, 10449, 0, 0, 41470, 245, 41045, 41719, 41233, 64801, 0, 497, 12100, + 5953, 0, 7796, 41235, 0, 42045, 9804, 8449, 432, 1281, 64355, 65393, + 64339, 10677, 604, 41097, 9120, 1859, 0, 10460, 3425, 0, 0, 2836, 8797, + 8490, 9052, 64888, 0, 2356, 95, 64786, 1738, 0, 0, 2832, 64640, 9670, + 6096, 0, 64918, 65151, 10063, 2822, 12199, 4436, 0, 2566, 11971, 12090, + 64872, 1065, 1331, 0, 0, 2576, 0, 41142, 5090, 5089, 120263, 9505, + 119109, 514, 41692, 319, 2921, 0, 9477, 5772, 12968, 5087, 118822, + 917567, 96, 2580, 0, 10522, 41223, 5085, 1463, 41342, 0, 5293, 0, 0, + 3733, 3772, 13090, 12054, 64496, 41254, 64300, 12575, 13091, 0, 0, 9680, + 0, 0, 41413, 64419, 118953, 0, 0, 118923, 0, 0, 10939, 6106, 0, 41271, + 1132, 0, 4534, 41270, 0, 9224, 195099, 0, 64761, 0, 3671, 8510, 0, 0, + 41275, 0, 0, 10807, 7963, 42012, 0, 0, 65227, 6187, 13109, 3854, 41479, + 13141, 9715, 0, 8258, 0, 4185, 41334, 65148, 8871, 42, 0, 0, 4102, 0, + 9029, 118995, 0, 2353, 6308, 41604, 11958, 2611, 119186, 41021, 0, + 194631, 66336, 8045, 120428, 12946, 4484, 8747, 118976, 12826, 65233, + 5557, 41224, 9737, 13216, 3747, 9467, 5291, 8878, 1691, 41226, 0, 12107, + 10146, 10905, 9086, 64566, 697, 0, 628, 0, 12594, 0, 10468, 4546, 7731, + 65256, 12010, 0, 0, 3805, 0, 64293, 0, 9844, 0, 6307, 118950, 0, 0, + 12166, 64697, 10516, 194706, 10152, 12648, 10354, 0, 9532, 5785, 41309, + 9764, 41316, 120160, 0, 13230, 41299, 5559, 0, 8704, 2397, 5556, 0, + 66368, 13122, 9011, 191, 9630, 41837, 42040, 5506, 0, 0, 64850, 41072, + 12598, 8845, 41577, 194790, 10002, 8889, 119113, 42141, 41570, 41838, + 683, 396, 41580, 12526, 0, 12901, 12351, 65115, 343, 66597, 194680, + 41360, 0, 10481, 4559, 0, 1956, 118857, 0, 0, 1724, 1210, 120066, 9412, + 3739, 6263, 0, 0, 3964, 8984, 38, 8533, 9234, 10947, 65073, 13063, 0, + 1778, 3956, 65091, 42070, 42069, 119846, 8743, 8369, 118807, 10941, + 12467, 0, 5547, 66618, 0, 0, 8175, 0, 284, 8108, 934, 5696, 0, 173, 0, + 8652, 0, 120670, 1750, 0, 4394, 65056, 1807, 9354, 0, 0, 5889, 63783, 0, + 64714, 917624, 0, 12162, 12120, 41087, 1721, 7767, 7891, 120446, 10563, + 2583, 4512, 63973, 2462, 7693, 64294, 10434, 3855, 8107, 41337, 63972, + 4952, 65413, 0, 5504, 41340, 3975, 0, 0, 65420, 12672, 3798, 2703, 0, + 64347, 0, 9774, 41847, 1127, 455, 41095, 3962, 10100, 3483, 41101, 3954, + 41091, 4513, 9104, 3503, 65375, 41298, 1468, 65386, 1864, 41851, 0, + 41446, 2540, 7736, 41080, 41849, 917619, 4320, 3224, 12909, 9705, 64684, + 8604, 195006, 1510, 11978, 6149, 3887, 194882, 1411, 2824, 194708, 10106, + 8770, 1403, 0, 1347, 9631, 8671, 0, 4283, 0, 194785, 8640, 13124, 258, + 1654, 41408, 8858, 0, 42139, 3741, 0, 4042, 4581, 2873, 119029, 0, 0, + 8549, 10861, 0, 41673, 64829, 1733, 4392, 2568, 10786, 63983, 0, 376, + 41486, 9221, 64871, 119907, 8823, 41222, 12857, 6217, 7965, 4896, 0, + 10154, 0, 41350, 8301, 0, 0, 1684, 64501, 10974, 458, 41199, 0, 0, + 195046, 11916, 340, 194980, 12298, 10864, 0, 12288, 0, 4388, 1493, 10521, + 0, 4097, 0, 13080, 65203, 195051, 41642, 6030, 8059, 3210, 13131, 120190, + 917597, 0, 8794, 41278, 41629, 12154, 0, 10043, 64658, 1186, 41571, + 41297, 617, 9464, 0, 3675, 5207, 63955, 5213, 118896, 833, 41348, 41568, + 0, 3253, 63954, 41088, 8630, 6062, 0, 5596, 5545, 194982, 933, 1341, + 9842, 5217, 0, 8942, 64800, 0, 0, 41615, 2635, 0, 194768, 41632, 120152, + 0, 7835, 41622, 9002, 0, 194770, 64558, 0, 9716, 0, 9805, 5990, 900, + 5784, 194775, 9317, 0, 3612, 4011, 64376, 41953, 5389, 7864, 0, 65336, + 2839, 5600, 3903, 0, 10447, 3749, 1207, 9319, 0, 3501, 0, 119142, 4403, + 0, 1124, 5597, 195009, 0, 9321, 4429, 917606, 0, 194572, 1719, 64873, + 546, 9671, 1125, 4399, 9542, 472, 7716, 8452, 5488, 65223, 42025, 0, + 5491, 3602, 8328, 41182, 2604, 41949, 5490, 41183, 5489, 8522, 10287, + 684, 6300, 0, 2854, 0, 4390, 454, 7823, 194784, 195061, 0, 195062, 0, 0, + 64572, 8478, 194788, 2394, 2575, 3415, 3746, 0, 8648, 0, 65421, 65505, + 119092, 11989, 65142, 418, 118810, 917616, 10295, 8249, 10391, 41752, + 4565, 8641, 41449, 2598, 513, 0, 41475, 8656, 0, 1024, 65008, 7961, 0, + 8941, 917563, 4554, 0, 9023, 40973, 194977, 12126, 10964, 0, 63799, 9509, + 0, 1036, 65114, 0, 1723, 0, 9049, 41185, 41579, 2444, 41200, 10705, + 41447, 194795, 65224, 0, 740, 63963, 917613, 118874, 194986, 5300, 10407, + 9459, 0, 0, 195060, 7856, 8121, 10438, 12050, 41698, 2860, 12157, 5238, + 0, 5690, 5743, 10424, 12065, 0, 13095, 0, 0, 8875, 8694, 9506, 13254, + 5575, 12847, 2413, 118917, 0, 962, 12176, 1122, 317, 9040, 119116, 1582, + 0, 1920, 41477, 10173, 827, 10801, 0, 118798, 0, 5223, 496, 10439, 4313, + 5226, 12602, 7860, 120627, 906, 7758, 2842, 10215, 5224, 5487, 798, 5692, + 12801, 8406, 1153, 5695, 41711, 64627, 8054, 12626, 120131, 5691, 287, + 866, 233, 4642, 66574, 0, 0, 0, 65140, 42089, 8830, 9008, 0, 10524, + 41175, 42079, 65423, 119065, 5296, 0, 0, 10663, 0, 3302, 0, 0, 0, 0, 0, + 0, 0, 42093, 3920, 8690, 120036, 0, 12122, 4580, 41967, 6116, 1785, + 41965, 120635, 3021, 42004, 5138, 0, 0, 41998, 41867, 4540, 41179, + 194804, 6200, 63796, 5134, 42021, 322, 4643, 5132, 42010, 194988, 64589, + 5143, 64875, 8790, 0, 194796, 64604, 9365, 8869, 0, 64400, 42060, 0, + 64887, 194814, 64941, 10270, 10286, 10318, 10382, 0, 4110, 0, 41530, + 10929, 64277, 3234, 120607, 13058, 8617, 41982, 6025, 120736, 12805, + 8767, 0, 0, 9597, 41283, 5201, 0, 6215, 12714, 6214, 13101, 65282, + 120490, 65268, 120504, 120045, 194681, 187, 0, 10059, 10511, 4963, 9767, + 789, 1749, 8964, 0, 194577, 320, 41948, 41833, 195047, 3049, 41139, 9787, + 9449, 10081, 10528, 42121, 118894, 0, 7954, 5549, 0, 195071, 8485, 4671, + 1189, 905, 480, 10985, 10240, 10610, 5414, 8647, 1745, 4286, 5421, 5427, + 9554, 0, 0, 65465, 41507, 8806, 42047, 9442, 6213, 9443, 9436, 7867, + 64720, 6236, 42052, 0, 2406, 0, 12851, 4566, 348, 5474, 3801, 3103, + 10406, 5246, 5236, 64395, 0, 5200, 64305, 41739, 41733, 64518, 10931, + 13181, 41402, 395, 5391, 5198, 8786, 9428, 41259, 5196, 0, 2691, 42009, + 5205, 41244, 5562, 0, 118973, 41262, 66364, 64421, 0, 41251, 9126, 435, + 3979, 12014, 12893, 8093, 9079, 3203, 192, 120785, 3385, 41266, 64430, + 5383, 10294, 10326, 0, 5738, 9574, 2666, 0, 5361, 831, 419, 8256, 10716, + 7872, 64583, 194758, 1260, 3149, 5359, 7766, 0, 7914, 5357, 916, 769, + 2624, 5364, 64739, 120599, 5563, 547, 1943, 41188, 5560, 41212, 487, 0, + 4497, 3754, 0, 0, 9039, 10619, 41776, 0, 8716, 120126, 40983, 64072, + 41516, 0, 119044, 0, 41376, 0, 3232, 12185, 0, 120632, 0, 120458, 41889, + 0, 8634, 1161, 41895, 118804, 9701, 8622, 120215, 0, 0, 120588, 669, + 5679, 41362, 120125, 118961, 11921, 42087, 5678, 0, 0, 41364, 460, 0, + 41352, 41361, 195101, 41366, 0, 3356, 6178, 917, 0, 119915, 64068, 7782, + 9044, 4974, 677, 119916, 0, 119932, 41912, 1216, 12504, 11952, 3349, + 195097, 12296, 8927, 4739, 3738, 5802, 194997, 5683, 10368, 0, 491, 0, + 120503, 0, 0, 5682, 6206, 8670, 0, 5680, 917568, 10001, 41881, 118823, + 1449, 10241, 3768, 65255, 3776, 9095, 7741, 12684, 41885, 1046, 0, 5567, + 2717, 4620, 5171, 5564, 64571, 41908, 41786, 5565, 12819, 12578, 194992, + 194771, 5169, 5566, 3465, 64694, 3175, 11904, 120065, 120804, 5176, 5942, + 8468, 4871, 10361, 10425, 119210, 118952, 64729, 1128, 194722, 10548, + 64664, 10647, 9408, 9409, 9410, 457, 3662, 9413, 1934, 9415, 9416, 8802, + 9418, 8909, 9420, 9421, 5897, 9423, 5165, 5126, 41385, 8043, 8950, + 194562, 8955, 3374, 9400, 9401, 9402, 8939, 9404, 3507, 9406, 9407, 0, + 65515, 9499, 10035, 183, 65078, 2631, 0, 10636, 41130, 0, 3996, 120650, + 64675, 1667, 41584, 65486, 41582, 0, 4332, 64825, 10741, 10726, 12912, + 41125, 5899, 8101, 3610, 12085, 41748, 0, 955, 120092, 5340, 5350, 41058, + 5446, 64549, 10875, 64796, 5442, 120424, 0, 9782, 5451, 12896, 3616, 0, + 0, 3874, 7708, 64370, 10859, 0, 10345, 10409, 0, 11909, 120591, 120303, + 41038, 0, 194733, 4447, 8536, 120708, 917586, 0, 194732, 724, 42048, + 1455, 205, 0, 10351, 64618, 0, 4175, 12307, 0, 120380, 939, 41355, 5505, + 119154, 5503, 8021, 0, 119150, 9819, 41357, 8011, 6088, 5507, 12044, 190, + 0, 10026, 4356, 8188, 1191, 0, 4417, 10329, 5476, 8991, 0, 7827, 0, 5829, + 8550, 0, 5592, 2919, 64925, 2675, 5595, 0, 7918, 4367, 0, 0, 5478, 1728, + 5594, 120710, 178, 12972, 5590, 10727, 13067, 118909, 65254, 0, 9731, 0, + 64633, 0, 12113, 13065, 118863, 9252, 12278, 4652, 0, 66563, 194879, 0, + 0, 12887, 10551, 10710, 0, 0, 0, 120570, 41804, 5199, 9497, 1120, 120471, + 8333, 1444, 9486, 120808, 13142, 4538, 194830, 5285, 6177, 5894, 0, + 11910, 13224, 0, 8963, 4034, 13162, 65389, 3334, 64003, 41747, 10708, + 194571, 8677, 120734, 1651, 9350, 8861, 0, 8836, 1142, 0, 4396, 10928, 0, + 8922, 8856, 66611, 4002, 0, 10442, 10676, 3344, 120402, 64963, 10813, + 2592, 12853, 120242, 120243, 3438, 119912, 7871, 120239, 65516, 12321, + 120391, 118890, 120389, 10007, 120246, 9588, 120248, 4700, 66366, 41994, + 120051, 8661, 120365, 66572, 0, 120401, 4973, 5573, 12588, 9629, 40981, + 119873, 118981, 7993, 64328, 42002, 64754, 41766, 8825, 13016, 0, 0, + 10346, 6107, 0, 9243, 2464, 0, 6108, 3372, 335, 6247, 64689, 438, 4510, + 5765, 8721, 119878, 4036, 6092, 12840, 120229, 8876, 10303, 8096, 10284, + 3354, 10268, 0, 9289, 8689, 10316, 3876, 10335, 0, 42044, 0, 0, 0, 8050, + 120030, 0, 64591, 0, 120053, 0, 843, 120495, 194829, 120770, 0, 10117, + 66560, 41902, 118871, 6312, 215, 1963, 118889, 64494, 1953, 9579, 41938, + 1256, 3910, 13015, 6242, 41329, 9662, 41257, 41900, 3366, 10700, 8805, + 1742, 5542, 9333, 8202, 120459, 120232, 41611, 0, 0, 120385, 499, 118846, + 8593, 0, 0, 41169, 1712, 5932, 8097, 41762, 12292, 194685, 11967, 11963, + 65296, 41243, 118957, 5662, 416, 9458, 64687, 0, 0, 194844, 10984, 64386, + 64672, 0, 0, 0, 41172, 0, 66355, 120669, 41937, 194825, 12540, 65446, + 3804, 41760, 5794, 201, 2662, 9419, 64579, 8254, 41726, 10975, 0, 120625, + 65131, 9507, 4108, 3880, 8023, 1200, 12243, 0, 5282, 0, 0, 65032, 5891, + 65031, 3343, 1636, 195057, 65029, 65024, 3896, 195056, 9674, 2947, 99, + 98, 97, 120571, 64414, 13059, 8221, 64085, 3381, 0, 7892, 0, 10777, 0, + 65310, 3913, 0, 0, 64959, 8039, 1265, 4316, 6309, 118815, 12969, 12596, + 66595, 66596, 41939, 5593, 195059, 5998, 9163, 12300, 6061, 64854, 119, + 118, 117, 116, 8930, 122, 121, 120, 111, 110, 109, 108, 115, 114, 113, + 112, 103, 102, 101, 100, 107, 106, 105, 104, 41793, 917572, 534, 120515, + 0, 42027, 12114, 0, 917579, 0, 194698, 6020, 12716, 10561, 10075, 475, + 118888, 13266, 9144, 120383, 917580, 195088, 194741, 10645, 1212, 5079, + 0, 8134, 8483, 2913, 10330, 4908, 1866, 1639, 119189, 0, 8923, 1645, + 12059, 64505, 0, 194873, 0, 4817, 5935, 1250, 0, 8174, 9600, 9856, 9859, + 7916, 9861, 5343, 5258, 4328, 41206, 64794, 10882, 405, 0, 4659, 195045, + 657, 12610, 4970, 4461, 1134, 41170, 1454, 41242, 65130, 4468, 5987, + 195079, 9762, 4456, 5206, 10720, 0, 10480, 41718, 5818, 0, 8264, 10229, + 260, 645, 195054, 10687, 118837, 4821, 4466, 120500, 5824, 984, 0, 10688, + 5851, 5705, 7729, 41406, 10591, 41797, 119983, 65438, 119985, 119984, + 119979, 41404, 1165, 7879, 4451, 13087, 0, 195067, 119987, 119986, 41909, + 118902, 2791, 9363, 9552, 3375, 0, 5900, 12997, 7889, 2722, 0, 13173, + 2381, 12883, 10994, 10529, 12437, 194756, 8644, 63791, 12425, 10661, + 10856, 9614, 0, 41478, 0, 10064, 10901, 10748, 120542, 11005, 4868, + 119162, 1952, 0, 8455, 10082, 0, 8467, 12577, 194760, 5182, 12183, 6145, + 119188, 64929, 4465, 42120, 12135, 5732, 4464, 7728, 3922, 977, 4458, 0, + 195068, 64770, 120772, 3353, 344, 0, 41626, 1395, 64635, 120089, 5776, + 8558, 786, 65153, 0, 64340, 120082, 10202, 120084, 41027, 120086, 10132, + 64413, 120087, 120074, 119119, 120059, 0, 120078, 63862, 41896, 8657, 0, + 8594, 10204, 0, 120477, 120069, 120072, 1399, 119203, 120056, 0, 8852, + 64492, 241, 194759, 4907, 0, 9738, 0, 9727, 7851, 119196, 10951, 4439, 0, + 119199, 195021, 9085, 0, 119200, 9327, 6160, 0, 8650, 64865, 8088, 64933, + 41910, 118872, 65217, 3965, 120050, 0, 0, 13300, 0, 0, 65491, 65145, + 9041, 0, 65017, 10826, 4420, 41263, 10583, 7760, 194798, 0, 0, 120047, + 13217, 8748, 65415, 0, 42159, 9066, 194860, 11993, 0, 2626, 7762, 10902, + 0, 0, 41526, 64285, 10472, 2995, 120704, 12907, 41184, 2371, 0, 10038, + 259, 1009, 0, 2402, 2333, 65011, 0, 12962, 65125, 0, 12417, 65380, 9103, + 12700, 3148, 0, 119145, 7779, 10219, 0, 9479, 6029, 120369, 119910, 9689, + 41261, 0, 8993, 8613, 0, 118989, 3368, 606, 41492, 7697, 10228, 41596, + 4311, 194769, 6027, 120572, 4322, 41661, 7991, 0, 10578, 0, 41465, 41054, + 2735, 41664, 0, 63778, 65273, 1287, 65408, 9348, 120656, 6164, 0, 41273, + 0, 65027, 0, 9576, 0, 3347, 4160, 5154, 0, 3794, 66564, 65219, 11925, + 7709, 9088, 3743, 65099, 1396, 4572, 0, 3847, 0, 65081, 4985, 1615, 672, + 809, 12980, 63806, 0, 65218, 5799, 0, 65072, 1577, 194934, 0, 5928, 4525, + 10658, 120561, 1266, 0, 0, 195024, 12622, 9347, 0, 0, 64424, 41048, 7789, + 773, 63910, 41112, 283, 64416, 66374, 532, 0, 120049, 41115, 3051, 5862, + 3370, 0, 119132, 5443, 3250, 8153, 0, 120278, 9510, 120279, 120493, 9541, + 0, 41066, 64706, 0, 0, 3505, 8707, 9466, 64286, 8537, 120802, 3626, 3471, + 0, 915, 195094, 12990, 120034, 0, 118797, 195074, 0, 41906, 0, 195072, 0, + 64365, 0, 3225, 0, 4433, 5186, 0, 41933, 1443, 4381, 9829, 65124, 10926, + 0, 195076, 64879, 64699, 0, 65476, 0, 0, 10021, 5160, 1387, 65495, 6103, + 120388, 41480, 0, 0, 217, 0, 0, 12466, 10443, 10789, 41158, 41460, 0, 0, + 41483, 195096, 12565, 64287, 10077, 12890, 5931, 195014, 9283, 7700, + 41252, 6042, 65499, 0, 41249, 512, 2990, 0, 0, 65456, 0, 632, 12940, 0, + 41296, 9545, 41291, 5957, 0, 8926, 3511, 41282, 5923, 10400, 10174, + 41456, 64581, 5386, 4274, 5786, 10633, 0, 5056, 119860, 417, 41474, + 120773, 13263, 9812, 5934, 4460, 66583, 119231, 64877, 65410, 64481, 0, + 0, 10937, 0, 120218, 0, 0, 0, 2953, 5819, 1801, 12835, 917627, 0, 194743, + 0, 66375, 8867, 702, 120410, 1237, 10274, 4552, 65447, 119966, 0, 1375, + 12106, 194693, 10264, 1755, 10482, 9228, 10376, 1163, 2951, 7840, 64336, + 64890, 10252, 0, 3384, 120703, 10167, 830, 194656, 65425, 10769, 8451, + 41368, 12520, 9753, 120762, 8944, 0, 0, 10473, 2908, 0, 0, 64902, 10299, + 119165, 12097, 64733, 12952, 4441, 10503, 0, 41430, 9330, 0, 10267, 411, + 10315, 10379, 0, 0, 13281, 10009, 7865, 2730, 10388, 9677, 5428, 118993, + 3364, 64806, 66363, 119179, 118816, 65463, 9535, 216, 10332, 1401, + 119895, 622, 0, 885, 64772, 1602, 4467, 41405, 5768, 0, 12160, 41328, + 484, 65187, 41051, 12071, 9609, 9806, 41497, 3338, 0, 120671, 10411, + 2736, 10255, 10263, 10279, 2794, 8807, 64491, 119896, 4315, 5222, 5381, + 0, 0, 5193, 5125, 5456, 5509, 41177, 0, 9534, 0, 64431, 1603, 3430, 0, + 10298, 0, 0, 981, 41176, 4330, 994, 120536, 1824, 10908, 0, 41681, 41683, + 5921, 194925, 2597, 3957, 5922, 118903, 0, 674, 194971, 0, 2946, 5354, + 5251, 4406, 5307, 3759, 65160, 8364, 5123, 1433, 5281, 5469, 5121, 5924, + 5920, 0, 5130, 64606, 0, 0, 8418, 41420, 1221, 2733, 0, 742, 5216, 2893, + 10772, 65276, 5937, 3468, 2553, 9230, 5939, 3997, 0, 8363, 120677, 2993, + 7772, 3916, 10289, 194932, 1141, 120301, 8159, 718, 10137, 973, 9666, + 120718, 3235, 2415, 5938, 0, 8018, 12448, 0, 10401, 10337, 0, 0, 65390, + 0, 8719, 1202, 0, 64651, 12983, 0, 12165, 119095, 0, 9067, 13116, 8077, + 65388, 0, 8419, 63773, 65419, 63774, 0, 0, 10725, 10433, 0, 0, 1431, + 64519, 66565, 10821, 4359, 12804, 12192, 8229, 1235, 3307, 41928, 0, + 3146, 4544, 9009, 8551, 118820, 1740, 194749, 65469, 985, 2724, 13076, + 120806, 12068, 119949, 515, 10141, 119944, 119945, 63763, 4476, 119146, + 119941, 12655, 8907, 13226, 4589, 4521, 119077, 9141, 64645, 10665, 2741, + 41572, 6197, 1370, 10101, 41573, 194746, 3931, 194924, 0, 6184, 8606, + 3303, 11968, 65475, 9473, 13103, 63771, 8879, 41390, 120600, 4478, + 917588, 41735, 120349, 717, 10754, 4477, 0, 814, 42066, 119962, 63767, + 1780, 41031, 119958, 41392, 819, 10611, 9694, 11955, 119952, 119953, + 41111, 9462, 0, 7788, 12820, 66327, 66580, 0, 118966, 0, 1581, 12650, + 41173, 3346, 430, 64698, 0, 66628, 268, 0, 4945, 0, 4950, 12918, 9456, + 10923, 5936, 0, 5964, 12908, 13081, 308, 0, 12933, 0, 41746, 4949, 0, + 443, 41030, 4944, 5467, 194938, 5926, 1862, 6044, 65392, 8820, 4946, + 194793, 9038, 7887, 0, 7830, 41306, 13093, 2698, 41144, 0, 12072, 41753, + 41914, 41304, 824, 0, 8595, 65225, 119813, 119816, 4673, 41354, 4678, + 13283, 12697, 65059, 12381, 3488, 5933, 5481, 3490, 1199, 65014, 8356, + 12297, 119153, 1955, 12688, 3102, 10474, 4672, 119822, 119821, 5531, + 119823, 119826, 66332, 8835, 4674, 119041, 5831, 0, 64896, 12379, 8025, + 119947, 64542, 1855, 65128, 5472, 64425, 7852, 194993, 119943, 0, 0, + 2745, 5470, 65171, 9124, 119110, 4654, 65289, 291, 0, 120285, 10525, + 4649, 65209, 120284, 12647, 4648, 4640, 64713, 10224, 120429, 6246, + 64950, 7828, 4650, 41464, 0, 119086, 4653, 7822, 120331, 118824, 120330, + 8669, 194921, 10729, 65093, 5778, 6302, 2716, 194606, 12680, 119130, + 1417, 10916, 0, 9452, 8547, 2711, 42165, 120798, 64953, 7992, 64663, + 41907, 4662, 65453, 120408, 9149, 9146, 599, 4641, 11990, 64819, 63782, + 4656, 10130, 41469, 7811, 40994, 12426, 4646, 5967, 865, 3725, 5713, + 5814, 4645, 42033, 120422, 41756, 13132, 64728, 9026, 10833, 64673, 1659, + 919, 41935, 1671, 195089, 3054, 9219, 9744, 1661, 13120, 4622, 119087, + 10140, 9713, 0, 119143, 194961, 9045, 2306, 10485, 118943, 6068, 10612, + 65307, 4617, 120294, 194964, 41462, 4616, 10518, 10423, 10359, 0, 5958, + 0, 9564, 4618, 826, 195083, 4321, 4621, 195084, 41313, 522, 5368, 1808, + 7848, 0, 5366, 12201, 5372, 0, 66632, 0, 4391, 64331, 2696, 120155, + 11003, 4638, 64490, 1790, 66304, 167, 10921, 9791, 0, 9840, 5376, 1835, + 5335, 10313, 41370, 4633, 64320, 10265, 1180, 4632, 118970, 5387, 5333, + 64256, 12903, 41, 5331, 1792, 11928, 64363, 5338, 4637, 0, 5971, 12066, + 120393, 385, 4152, 2585, 0, 10909, 3126, 1427, 194812, 10957, 5970, 3431, + 120394, 10358, 10422, 4758, 0, 1608, 2738, 12707, 10455, 4753, 0, 0, 0, + 6240, 5231, 119013, 12541, 65216, 6248, 0, 2593, 8463, 7810, 119923, + 5229, 4757, 65192, 66581, 2728, 4411, 64563, 65235, 5234, 119924, 194914, + 0, 10066, 9746, 0, 2622, 6033, 13061, 8016, 41196, 8954, 64831, 65189, + 2632, 13228, 10108, 1011, 5574, 1853, 2709, 65139, 5577, 42091, 41165, + 393, 12450, 8965, 41166, 42177, 5316, 0, 171, 5941, 5572, 120062, 5312, + 12531, 5525, 5330, 5319, 64066, 195100, 64647, 8937, 63798, 12454, 12291, + 42132, 12063, 0, 64343, 3230, 0, 10350, 10644, 5209, 297, 5721, 12109, + 8415, 8632, 10102, 119925, 0, 2497, 5720, 960, 1692, 118792, 4610, 8696, + 4292, 64760, 4609, 10512, 4614, 541, 0, 5287, 5309, 2503, 119243, 1762, + 4647, 56, 10743, 5844, 41381, 601, 4613, 10194, 4663, 64469, 4608, 2507, + 13273, 5190, 119963, 63759, 195010, 66357, 8892, 0, 119942, 119931, 2734, + 5782, 420, 64368, 63795, 65105, 10797, 5960, 63797, 8992, 65293, 41238, + 1782, 12814, 63852, 12525, 10686, 41383, 5501, 0, 3650, 119955, 120749, + 359, 4183, 119957, 6239, 41670, 41256, 329, 66582, 12573, 120462, 0, + 9346, 66331, 13244, 119048, 42167, 3767, 5737, 5380, 4865, 0, 1155, + 917538, 5736, 4368, 64724, 63749, 0, 5601, 5739, 41023, 4866, 9985, 7987, + 64406, 1172, 120563, 0, 6253, 0, 8574, 5603, 41666, 4473, 119847, 4870, + 0, 65347, 41799, 65345, 8199, 0, 5347, 119063, 9280, 4864, 10398, 4144, + 0, 120567, 6245, 120478, 2732, 5598, 745, 4555, 5341, 0, 4777, 0, 5351, + 0, 0, 65244, 120729, 195080, 3097, 63817, 5966, 120363, 4778, 0, 10863, + 1660, 4781, 0, 271, 0, 65370, 8577, 65368, 12653, 65366, 10216, 4782, + 10000, 65362, 65361, 11912, 12325, 65358, 8717, 41583, 65355, 4776, + 65353, 65352, 8700, 65350, 65349, 10575, 10426, 0, 120150, 10362, 0, + 1715, 4849, 8242, 9561, 0, 0, 0, 0, 0, 5963, 0, 0, 4916, 4850, 380, 1607, + 466, 4853, 0, 4854, 0, 5164, 41096, 1350, 5124, 64420, 0, 5362, 8471, + 2708, 119131, 7946, 3785, 234, 0, 120481, 41268, 4848, 2530, 41636, 4798, + 1225, 41842, 0, 10458, 0, 8576, 5197, 0, 2704, 4794, 8329, 63823, 8322, + 4797, 66326, 5725, 2694, 2595, 3363, 2439, 65104, 5607, 41089, 303, + 41162, 195037, 2665, 2437, 0, 9817, 4844, 8764, 0, 8934, 0, 0, 4492, 0, + 9843, 2441, 10739, 65090, 1188, 120290, 1100, 2451, 2714, 41081, 2912, 0, + 4937, 119104, 0, 3572, 10023, 12343, 13079, 9248, 0, 9729, 0, 65190, + 119094, 2726, 3107, 0, 4941, 7996, 10995, 9140, 1408, 5261, 0, 41451, + 181, 0, 4942, 63801, 4938, 0, 972, 5259, 9369, 64868, 4142, 5257, 0, 0, + 4964, 5264, 9538, 0, 0, 41225, 0, 63800, 0, 119058, 9482, 4873, 3265, + 1822, 0, 12601, 41078, 3865, 261, 5927, 7791, 0, 0, 0, 10696, 9830, 6073, + 389, 10893, 6255, 6075, 4872, 282, 0, 3125, 9567, 195012, 4878, 5459, + 4874, 0, 9557, 3474, 64774, 120356, 194704, 6081, 9563, 9411, 13139, 0, + 11940, 41744, 0, 10788, 119176, 8751, 10385, 120273, 7816, 9414, 4665, + 12628, 4670, 119871, 41555, 0, 9642, 10912, 958, 119853, 3082, 0, 4666, + 0, 4915, 0, 2891, 5856, 12096, 5163, 4664, 10836, 1817, 120010, 12231, + 41554, 10564, 7763, 13077, 42099, 4400, 9697, 3606, 10275, 8925, 10371, + 10307, 1063, 10227, 120251, 9772, 4541, 6299, 1389, 0, 120257, 9823, + 42081, 12941, 65197, 10520, 120255, 120256, 12301, 120266, 10505, 120268, + 66604, 120262, 66601, 41814, 13282, 66600, 523, 505, 1447, 846, 0, 41813, + 0, 8608, 120537, 65482, 2543, 12163, 3108, 9745, 4529, 120472, 64764, + 118825, 7919, 0, 1641, 119874, 64949, 8966, 10251, 10247, 5908, 715, + 64353, 0, 9453, 1699, 10943, 10763, 0, 118992, 550, 10169, 0, 64385, + 66579, 3766, 120457, 5780, 9504, 9051, 257, 10373, 13153, 12061, 10261, + 10253, 7821, 2599, 9433, 11984, 9156, 5930, 120377, 0, 0, 3128, 4789, + 5067, 5066, 3760, 1718, 9438, 8827, 1146, 5065, 41435, 4352, 0, 2435, + 41839, 5064, 5326, 120453, 3778, 1809, 8873, 7824, 41434, 5062, 1264, + 64817, 41586, 41440, 3764, 8473, 64716, 8469, 3933, 12947, 4564, 12337, + 0, 10375, 0, 120362, 64768, 0, 0, 5225, 0, 42130, 7903, 5151, 0, 0, + 64685, 5626, 2569, 0, 3800, 65424, 0, 0, 5353, 5625, 10894, 954, 64927, + 1010, 41043, 0, 41438, 41439, 120357, 10711, 4593, 120752, 119003, 2590, + 5629, 13309, 10293, 10325, 5632, 10471, 120038, 64759, 42054, 5166, 5628, + 120031, 970, 120029, 4772, 2400, 5627, 118972, 120018, 12885, 3119, + 120021, 10961, 65103, 203, 9986, 0, 64344, 636, 120550, 120652, 64378, + 42111, 64356, 0, 554, 120761, 8320, 64275, 8863, 120520, 42042, 41883, + 63803, 0, 120792, 5694, 7689, 42142, 9323, 4325, 3047, 3937, 175, 195077, + 3169, 64335, 64781, 912, 1243, 4536, 5431, 120005, 0, 6244, 120154, 0, + 3935, 120665, 1129, 0, 11950, 5392, 118859, 7846, 118806, 5397, 0, 12046, + 12599, 3845, 4490, 5395, 0, 5393, 354, 120544, 11977, 0, 8366, 0, 7756, + 3901, 65484, 51, 626, 41602, 5895, 9568, 64057, 456, 0, 8145, 1168, 9251, + 9082, 119964, 64055, 0, 3866, 8818, 41512, 0, 0, 10324, 3918, 5377, 3797, + 1644, 10405, 9658, 4140, 13057, 42029, 42037, 9030, 813, 119973, 41454, + 4146, 195036, 5360, 2466, 236, 195032, 0, 6249, 42117, 5898, 0, 41457, + 119148, 5855, 1969, 4911, 988, 0, 12838, 64483, 0, 10341, 10552, 65479, + 5854, 0, 0, 118933, 119989, 119940, 10416, 11981, 3872, 0, 0, 120725, + 6093, 0, 2838, 119939, 0, 170, 0, 13143, 4169, 118931, 41859, 6058, + 120813, 10553, 1662, 65295, 0, 64342, 5892, 195081, 0, 42106, 66, 65, 68, + 67, 70, 69, 72, 71, 74, 73, 76, 75, 78, 77, 80, 79, 82, 81, 84, 83, 86, + 85, 88, 87, 90, 89, 4736, 10357, 120400, 8170, 1704, 8556, 120661, 9659, + 120403, 1743, 120512, 9556, 9496, 4503, 41977, 9647, 7876, 0, 120575, + 3928, 11948, 65283, 10706, 66562, 66308, 4842, 7771, 0, 9109, 4841, 1289, + 4171, 12008, 6251, 3923, 1490, 2447, 120347, 0, 10907, 5245, 0, 10114, + 64000, 9790, 4845, 8332, 10582, 0, 4840, 5675, 254, 1747, 65429, 4825, + 10626, 8918, 10281, 5716, 64004, 66594, 0, 119019, 0, 8080, 118895, 367, + 1472, 120386, 0, 4829, 64693, 5905, 12339, 8919, 9515, 120384, 194651, + 65266, 0, 4830, 9134, 41365, 64671, 41978, 1412, 4594, 1391, 10536, 7720, + 4824, 7775, 0, 120392, 0, 1960, 3140, 0, 7960, 41836, 41844, 6052, 6064, + 54, 1428, 12214, 0, 6211, 7699, 358, 0, 10557, 65161, 10758, 8223, + 120341, 4261, 12642, 0, 120343, 0, 0, 119053, 120382, 119055, 119054, + 194902, 64554, 10574, 3878, 4017, 12827, 1752, 0, 0, 41118, 3924, 10199, + 0, 64966, 0, 0, 0, 41116, 720, 324, 0, 120684, 12057, 11917, 1464, 41343, + 4721, 7974, 65407, 8957, 0, 64488, 120371, 0, 64041, 195058, 120091, 0, + 4722, 917617, 0, 0, 4725, 9690, 4726, 0, 194956, 119843, 118969, 5204, 0, + 0, 118851, 4015, 3995, 8052, 476, 3714, 10073, 3595, 10232, 10999, 1382, + 120558, 12636, 0, 120404, 1656, 41831, 8130, 8672, 8832, 8720, 3908, + 1452, 13111, 64523, 64067, 0, 8552, 12398, 41845, 3849, 0, 0, 9778, 468, + 612, 42150, 55, 118959, 0, 0, 1674, 120277, 5823, 120276, 1114, 42110, + 540, 120052, 119017, 12516, 41743, 3938, 120057, 65417, 64316, 120060, + 12400, 820, 41741, 6292, 65303, 7955, 64074, 4713, 3359, 7800, 41566, + 65177, 6226, 353, 719, 9656, 9474, 64742, 120043, 4532, 65412, 120046, + 10868, 4717, 2349, 5902, 0, 4712, 9481, 119012, 65400, 3623, 8155, 1195, + 3942, 4714, 9625, 41151, 0, 41589, 12006, 0, 12074, 12409, 0, 4360, + 12964, 9739, 1229, 63793, 0, 0, 0, 8539, 65100, 120508, 4809, 9623, 4788, + 0, 0, 64745, 0, 65405, 120104, 13075, 194866, 5365, 4545, 8901, 8000, + 2492, 4813, 65432, 0, 5925, 4808, 64330, 9649, 41154, 65030, 5128, 4038, + 12718, 4810, 64859, 12794, 64928, 1648, 5435, 3522, 120689, 414, 10236, + 0, 12709, 41359, 120494, 0, 11905, 41082, 119859, 12581, 10374, 5175, + 119857, 0, 10254, 63820, 9751, 10262, 64088, 41363, 3919, 607, 0, 120288, + 9018, 5270, 10314, 10282, 65477, 10378, 64310, 40976, 8265, 7737, 0, + 40975, 5840, 0, 10162, 40978, 0, 8454, 42072, 42038, 387, 119098, 119083, + 0, 2550, 0, 119836, 118971, 41344, 3525, 120297, 0, 64641, 41590, 5619, + 41346, 13157, 375, 12703, 0, 5616, 64943, 64324, 0, 119202, 9454, 5615, + 0, 2315, 0, 1938, 5455, 64752, 808, 5568, 119201, 119198, 1026, 5620, + 194887, 195078, 13150, 5617, 0, 9225, 64639, 12902, 9145, 64595, 1338, + 120352, 119178, 9863, 0, 3084, 64553, 0, 41025, 6037, 0, 3974, 7998, + 10290, 10888, 3083, 10322, 2316, 118821, 64297, 41036, 0, 917615, 0, 0, + 0, 12904, 5373, 194773, 64700, 3762, 1445, 40961, 0, 11986, 0, 40960, 0, + 3780, 12808, 5779, 64952, 10402, 12011, 3906, 9707, 10603, 8326, 0, + 65498, 3763, 194923, 5618, 0, 3779, 194922, 9324, 118852, 63822, 9073, 0, + 64302, 10704, 280, 4787, 0, 917556, 13072, 9999, 0, 0, 9570, 0, 8699, + 2689, 917626, 65426, 0, 42135, 119061, 2551, 40966, 10011, 10200, 3998, + 120448, 120788, 503, 0, 4470, 2690, 118853, 7780, 5369, 41954, 5249, + 1652, 772, 8756, 8310, 65428, 3487, 120585, 3585, 1688, 917610, 119159, + 41822, 194874, 65359, 41904, 9720, 41697, 41319, 13125, 10650, 5836, + 12358, 4668, 4355, 9048, 1465, 10850, 3943, 65025, 41205, 41315, 41488, + 0, 917601, 5352, 12362, 12435, 8839, 41053, 3266, 7785, 12356, 8616, + 12104, 0, 0, 194621, 0, 3638, 5420, 3897, 3216, 0, 2358, 4018, 8633, + 2850, 0, 9639, 0, 0, 0, 64630, 65427, 3542, 120023, 12076, 5303, 8078, + 12676, 64418, 6276, 1706, 0, 41819, 41422, 12943, 65150, 10792, 41484, + 194607, 10847, 41050, 8872, 41824, 13099, 0, 0, 120378, 8504, 10830, 0, + 615, 10668, 10139, 0, 10504, 9779, 3625, 64650, 41409, 0, 41425, 65087, + 41688, 8789, 41427, 4022, 0, 0, 0, 120355, 41424, 917598, 0, 41820, 0, + 65292, 4812, 1261, 0, 3911, 12102, 120727, 1033, 0, 64642, 0, 3904, + 119205, 10514, 3275, 0, 0, 13123, 10846, 0, 118936, 195029, 12138, 10989, + 0, 6233, 10598, 449, 2669, 903, 118997, 2920, 9636, 65240, 10738, 0, + 9367, 593, 41085, 3917, 64622, 41713, 64307, 0, 41448, 3596, 0, 0, 9763, + 64082, 8819, 12347, 124, 12981, 41113, 232, 12234, 120646, 0, 0, 10820, + 194978, 0, 9094, 1769, 41715, 2463, 0, 1064, 13307, 41976, 10115, 64482, + 0, 0, 7862, 7795, 1474, 8516, 4828, 1258, 118994, 0, 0, 0, 9498, 0, 2911, + 120289, 41178, 3939, 64823, 8846, 8943, 12617, 41174, 2650, 4491, 1961, + 41463, 64291, 41167, 1959, 775, 120311, 41732, 41016, 6074, 9618, 194903, + 1511, 3613, 0, 4259, 41436, 3656, 0, 65436, 41019, 12428, 0, 0, 194896, + 8514, 8513, 9054, 1613, 41828, 0, 65531, 0, 118879, 0, 0, 5741, 10145, + 8865, 64551, 120379, 5788, 7917, 0, 66622, 7733, 64359, 9558, 120375, 0, + 120376, 0, 4268, 41247, 120524, 120370, 3871, 194892, 10881, 9111, 10621, + 41696, 65462, 120366, 119111, 120745, 9765, 120368, 0, 0, 42118, 10321, + 65281, 41587, 10949, 0, 42107, 0, 0, 5416, 10802, 195050, 66318, 65298, + 0, 5685, 0, 12633, 7928, 120354, 8094, 41595, 120510, 0, 794, 194907, + 12656, 10355, 64665, 5274, 1665, 41598, 3993, 194909, 64512, 40971, 536, + 189, 12611, 119234, 0, 2859, 4838, 63838, 4834, 2338, 0, 194839, 4837, + 41944, 770, 41452, 811, 1687, 41042, 66620, 120730, 64427, 64326, 40969, + 10526, 3895, 5406, 40968, 1339, 0, 120473, 10193, 3116, 7747, 0, 8020, + 10843, 41012, 12825, 0, 8266, 41006, 12371, 2871, 64614, 41245, 999, + 119129, 64567, 41876, 2663, 64586, 0, 120651, 120687, 10150, 65367, + 64308, 1522, 597, 4775, 10917, 12571, 10448, 12583, 12560, 12558, 12556, + 12584, 1741, 65097, 1227, 9676, 12566, 12569, 12554, 0, 10812, 1586, + 4978, 0, 3078, 1402, 118924, 9391, 40984, 9379, 9372, 394, 3088, 6284, 0, + 41663, 3991, 64391, 0, 9237, 424, 41648, 41208, 0, 9384, 41076, 1830, 0, + 0, 41656, 8246, 120307, 0, 0, 41840, 0, 2377, 41676, 0, 12572, 12552, + 12557, 12559, 5479, 2796, 1003, 2373, 9446, 9447, 9448, 48, 0, 9480, 481, + 2359, 9125, 9439, 9440, 9441, 548, 9153, 9444, 9445, 9430, 9431, 9432, + 397, 9434, 9435, 3984, 9437, 0, 1614, 9424, 9425, 9426, 9427, 1358, 9429, + 428, 9620, 9655, 0, 10982, 9096, 1333, 65170, 407, 12299, 0, 0, 5955, + 194985, 1108, 5804, 11976, 41231, 41466, 64782, 3926, 9057, 64613, 8798, + 0, 0, 1392, 8250, 10952, 5986, 5985, 8065, 41326, 10353, 10417, 0, 0, + 4407, 64524, 4019, 0, 118919, 8448, 8219, 118914, 1812, 12675, 12659, 0, + 194823, 119167, 42172, 42068, 6054, 10697, 2386, 0, 41759, 10642, 3909, + 64585, 10296, 41763, 119171, 42051, 64862, 4164, 1049, 0, 120569, 11943, + 41806, 8709, 10606, 3921, 120637, 64691, 41985, 8994, 1038, 120373, 8470, + 0, 0, 4008, 0, 8773, 10733, 36, 0, 5153, 41805, 13097, 0, 64937, 8736, + 1414, 64495, 9683, 0, 0, 0, 2536, 119951, 66330, 0, 8621, 65157, 12852, + 3031, 120441, 41345, 66317, 182, 66315, 66316, 0, 10210, 120492, 9058, + 366, 0, 120102, 961, 63755, 10848, 4570, 65301, 3106, 0, 41284, 1696, + 41189, 4003, 12105, 0, 5766, 12802, 3264, 8824, 13268, 0, 10936, 63980, + 0, 0, 194604, 120523, 0, 2322, 917561, 65506, 8300, 120374, 917536, + 41285, 3547, 120144, 8112, 0, 41459, 41369, 6089, 13000, 0, 12117, 4170, + 1029, 10540, 12315, 9063, 0, 120666, 744, 0, 12897, 3792, 4926, 917623, + 6065, 3551, 0, 0, 4623, 41186, 41816, 4598, 41818, 12795, 5968, 7922, + 12614, 10851, 8523, 6179, 119066, 6180, 1863, 4710, 0, 5956, 11972, + 41290, 0, 4705, 716, 177, 120831, 4704, 12360, 120270, 64719, 161, 9020, + 120272, 0, 4706, 10646, 0, 120037, 4709, 10680, 8754, 0, 120237, 120245, + 119164, 0, 41377, 9136, 1700, 4401, 41280, 194711, 8974, 4004, 0, 10634, + 41791, 2318, 8506, 66361, 8198, 42022, 1005, 937, 118996, 4734, 2870, + 41277, 12319, 66619, 5404, 4729, 3667, 235, 1384, 4728, 41049, 120420, + 120644, 120017, 8109, 120020, 119920, 4730, 447, 13186, 1513, 4733, 8664, + 63978, 0, 120252, 12911, 9665, 1383, 8565, 2469, 120024, 12663, 6156, + 120417, 0, 0, 4288, 120225, 2674, 13238, 11922, 41145, 41468, 3510, + 13234, 41148, 8683, 5605, 42095, 10497, 12221, 1380, 12314, 41146, + 118964, 13196, 13197, 3512, 120682, 9495, 8103, 0, 5959, 65184, 0, 41563, + 0, 120028, 41925, 13205, 13211, 5801, 41923, 0, 120316, 1283, 120302, + 4779, 7988, 3719, 4006, 3271, 66569, 64711, 8355, 118799, 8842, 0, 64870, + 13070, 0, 3875, 5962, 1095, 120106, 3599, 119149, 5827, 0, 7787, 0, + 65494, 120816, 64565, 0, 4773, 64531, 64034, 0, 0, 12785, 42043, 65467, + 119227, 0, 42046, 9742, 521, 65136, 10800, 41473, 8404, 917595, 483, 0, + 1450, 12986, 928, 0, 65441, 0, 10599, 0, 3989, 10971, 120431, 5771, 9841, + 8843, 12145, 119950, 119959, 194856, 9807, 3769, 41190, 3973, 119105, + 4575, 9573, 7982, 429, 8849, 0, 0, 41771, 1796, 118918, 118968, 194853, + 8164, 41301, 3502, 0, 0, 0, 4919, 10590, 5825, 7755, 0, 0, 64548, 12661, + 1621, 10214, 10418, 41962, 0, 41971, 1409, 12195, 1617, 3112, 10824, + 42101, 1390, 64403, 0, 421, 1756, 5846, 0, 8666, 120132, 0, 120360, + 42174, 3630, 5408, 2817, 1214, 119000, 120124, 10218, 41769, 3168, 0, + 42134, 7957, 2370, 2846, 1056, 119070, 12798, 0, 120314, 1836, 8757, 0, + 12327, 3740, 119028, 5622, 65374, 41765, 2341, 3944, 8484, 8474, 120817, + 120433, 3118, 8461, 41942, 12153, 5621, 12799, 8127, 8975, 9451, 7943, + 13073, 12169, 10618, 681, 0, 703, 120812, 3272, 8781, 12894, 0, 12223, 0, + 6204, 0, 0, 8279, 8776, 64954, 3276, 0, 65222, 4267, 194627, 41325, 0, + 64795, 0, 12171, 10047, 9710, 3262, 0, 0, 0, 42020, 118788, 163, 3877, + 10495, 1655, 5842, 12479, 3122, 0, 7793, 0, 9328, 64352, 10039, 6003, + 42094, 5623, 0, 5717, 3986, 0, 0, 8912, 64555, 41858, 64078, 0, 3627, + 4523, 194836, 12241, 8540, 12993, 8887, 4574, 41040, 2459, 64886, 13060, + 41041, 8946, 10348, 10412, 5718, 0, 10450, 8147, 13221, 66329, 120353, + 3765, 119885, 0, 1606, 120348, 120351, 3093, 119126, 4619, 10600, 119247, + 7712, 0, 4312, 41918, 120337, 10128, 11923, 4023, 41892, 5763, 120335, + 4827, 2401, 12810, 8792, 120346, 4455, 7826, 433, 120342, 0, 2499, 41812, + 12886, 0, 11973, 13089, 4293, 10300, 10161, 10396, 12196, 66322, 66630, + 0, 0, 3010, 5817, 120604, 1458, 3120, 9797, 9643, 0, 4984, 10389, 120111, + 9100, 0, 0, 0, 1061, 4699, 9115, 3509, 0, 486, 4290, 120201, 0, 0, 0, + 1045, 120361, 5631, 10380, 9626, 2380, 0, 0, 120678, 2376, 8486, 0, 9824, + 2335, 4362, 12174, 0, 2366, 1025, 0, 12634, 120760, 0, 41443, 120732, 0, + 64035, 9711, 1523, 0, 5058, 41445, 120058, 0, 8567, 41442, 3988, 0, 0, + 1847, 0, 10403, 8564, 65385, 65076, 65117, 0, 0, 0, 12616, 0, 6256, 0, + 12671, 0, 10206, 118974, 0, 2673, 11960, 5820, 0, 4488, 194740, 7926, 0, + 10444, 42137, 120787, 2754, 9850, 0, 4487, 13192, 41957, 1032, 65530, + 1711, 0, 120067, 3114, 614, 0, 118938, 119261, 0, 926, 120822, 0, 0, 0, + 0, 10832, 0, 1050, 41798, 41035, 0, 9314, 41801, 119088, 120616, 520, + 10437, 12699, 8331, 0, 3091, 41034, 0, 2307, 8360, 10097, 0, 321, 41028, + 64543, 0, 0, 0, 0, 2861, 10360, 10095, 0, 66307, 440, 1861, 13085, 9233, + 0, 64532, 0, 119158, 12123, 0, 3859, 10570, 41660, 8209, 0, 118841, + 10910, 0, 1521, 7875, 41658, 10487, 0, 5760, 13011, 743, 4414, 120766, + 120628, 0, 5243, 9849, 5239, 0, 0, 1405, 5237, 0, 65112, 10103, 5247, + 4769, 42063, 5508, 120829, 5764, 0, 3513, 3008, 9378, 0, 194960, 10125, + 120686, 41103, 9394, 13225, 1397, 120548, 65365, 119093, 4770, 0, 9392, + 8731, 65378, 12079, 120619, 12682, 9122, 0, 4774, 3019, 9997, 12834, 0, + 1099, 10490, 120547, 1340, 9390, 0, 0, 464, 4281, 4768, 9385, 0, 1346, + 9017, 917631, 12087, 64516, 423, 1818, 65144, 0, 8272, 0, 66324, 120676, + 3087, 64960, 10111, 65181, 64707, 0, 9584, 8214, 0, 0, 0, 9106, 118907, + 119248, 5806, 64750, 119195, 8243, 9123, 5709, 0, 265, 64409, 13255, + 12605, 0, 2752, 64626, 0, 1434, 59, 5637, 0, 0, 64897, 0, 41810, 42008, + 66305, 0, 41809, 10283, 41983, 64826, 917596, 1156, 8009, 3305, 3782, + 511, 41843, 0, 1014, 64360, 11906, 194676, 10835, 10157, 0, 1400, 10323, + 10685, 7702, 41211, 10387, 4453, 2440, 3758, 1150, 10547, 5700, 41213, + 118812, 65383, 2339, 64019, 5697, 41156, 63984, 9116, 120480, 0, 462, + 41841, 10493, 3862, 8129, 0, 0, 12864, 42065, 9845, 0, 8261, 5701, 9722, + 9581, 1385, 1426, 120474, 194943, 41872, 0, 41033, 8571, 194870, 13288, + 120108, 5167, 0, 1681, 12184, 1204, 3755, 11935, 7748, 8213, 3286, 8911, + 64712, 10744, 0, 990, 5647, 5726, 64915, 10377, 0, 194989, 5646, 195063, + 13253, 2851, 3945, 120096, 120119, 4373, 0, 64402, 9587, 1789, 5651, + 120097, 3100, 0, 5648, 64748, 118897, 0, 10205, 3545, 8190, 10016, 64616, + 0, 8479, 64312, 120118, 2368, 63993, 4419, 120460, 0, 3439, 1825, 1192, + 119166, 8891, 3080, 118836, 2347, 5430, 1140, 8990, 2848, 10159, 0, + 120212, 249, 0, 66623, 12191, 1815, 0, 890, 8883, 3267, 728, 42144, 995, + 0, 4410, 1041, 10576, 8102, 10099, 10343, 0, 8091, 558, 120110, 12273, 0, + 0, 12112, 0, 41389, 0, 65214, 5375, 10142, 8548, 8215, 3129, 10074, + 12913, 9005, 41856, 13242, 64891, 7725, 11938, 194757, 0, 8624, 5173, + 65348, 527, 0, 41894, 10327, 6277, 10608, 11915, 10010, 0, 3540, 41672, + 835, 2329, 0, 41029, 0, 7849, 12245, 5426, 4258, 63987, 41787, 5424, + 12016, 9699, 0, 5434, 0, 0, 8067, 6144, 0, 10311, 118977, 1404, 3095, + 64917, 120211, 3464, 494, 4819, 0, 65098, 10965, 956, 3672, 13112, 1498, + 194864, 0, 120667, 431, 10029, 65159, 0, 8761, 41537, 13171, 13096, 0, + 65108, 0, 9516, 1044, 5268, 0, 4954, 0, 4450, 120723, 0, 64358, 11946, + 356, 3477, 227, 10488, 13214, 382, 41940, 12295, 0, 66631, 0, 3020, + 120819, 120560, 2541, 0, 12364, 64934, 0, 1057, 957, 9110, 194974, 2743, + 0, 63965, 120463, 9097, 66571, 0, 8782, 3006, 776, 2524, 1592, 8573, 0, + 10924, 65164, 63941, 41593, 4397, 8952, 3856, 118818, 0, 5872, 65377, 0, + 917620, 0, 1698, 0, 64477, 5413, 3953, 1053, 120827, 65094, 11994, 4339, + 1052, 1051, 459, 1060, 0, 194647, 65299, 0, 5228, 0, 7868, 689, 12984, + 4163, 0, 8639, 0, 65226, 65510, 1162, 12130, 2671, 0, 8095, 64375, + 119005, 42178, 4553, 0, 0, 195005, 0, 195004, 0, 195003, 0, 194623, 4567, + 41891, 1926, 0, 119056, 4820, 8110, 10935, 194646, 194665, 5830, 119212, + 1377, 0, 4897, 12932, 9250, 8693, 4438, 0, 0, 1753, 194641, 6147, 917558, + 64621, 8833, 0, 0, 194644, 41428, 64596, 10719, 65440, 194669, 1413, + 194994, 65394, 802, 12141, 0, 5561, 10607, 10671, 2528, 41774, 41379, + 42023, 838, 5669, 120697, 844, 8036, 194891, 256, 0, 5583, 41987, 0, 0, + 5580, 65464, 2923, 10853, 5582, 10048, 0, 13069, 5795, 13158, 119006, + 195066, 6087, 120514, 41322, 12180, 0, 194640, 0, 0, 8894, 5370, 119997, + 194628, 1638, 10966, 120491, 194630, 118848, 5733, 0, 64288, 0, 8172, + 42017, 5729, 10844, 194715, 64054, 9760, 0, 120121, 1238, 200, 0, 1062, + 119993, 194946, 118905, 0, 0, 1070, 9361, 0, 6095, 3394, 0, 3015, 0, 0, + 4037, 0, 119995, 65186, 66626, 7817, 1841, 13183, 12976, 120496, 372, + 1669, 10776, 63937, 7701, 41585, 64397, 119211, 1732, 276, 41862, 2828, + 33, 65326, 41768, 9007, 118796, 41588, 914, 427, 8071, 3538, 3900, 65321, + 41864, 1031, 6257, 65279, 41869, 0, 0, 2328, 917550, 1071, 41400, 194717, + 13249, 10841, 41627, 5301, 1047, 0, 5734, 8960, 120688, 8001, 10651, + 119970, 65012, 9663, 194990, 12304, 41621, 5711, 12921, 12098, 119244, + 9166, 12164, 5710, 119082, 194782, 65213, 12447, 10571, 0, 0, 0, 119108, + 5558, 0, 5715, 10915, 0, 12007, 3670, 2761, 11975, 0, 3074, 5722, 194876, + 8629, 0, 0, 4499, 2757, 4496, 9725, 0, 8910, 10689, 0, 12717, 0, 64730, + 0, 0, 0, 41630, 41640, 120143, 0, 118911, 4280, 13118, 8765, 12784, 7792, + 1393, 0, 8701, 119027, 8487, 8233, 0, 0, 0, 120009, 4495, 12144, 2841, + 12543, 0, 1473, 10992, 64329, 118984, 0, 120006, 9318, 357, 1048, 41100, + 0, 41104, 65457, 8035, 1054, 0, 1040, 65450, 5454, 4434, 1069, 0, 0, 0, + 0, 5084, 65402, 119133, 9693, 12354, 733, 41931, 41677, 41102, 4353, + 41674, 1059, 9218, 1731, 0, 0, 0, 194786, 41679, 8299, 0, 118833, 64390, + 0, 5155, 12786, 12787, 42122, 63998, 0, 41779, 0, 3587, 12131, 41432, + 10986, 66602, 9605, 64807, 12788, 194926, 41767, 3371, 0, 13114, 8771, + 3955, 41022, 0, 1109, 11000, 0, 65351, 9770, 9246, 12230, 64507, 8868, + 399, 65137, 41783, 41772, 64045, 12149, 2755, 551, 0, 10156, 4857, 0, + 4428, 2544, 65074, 0, 0, 0, 917612, 351, 5747, 12179, 194603, 7978, + 41092, 120731, 0, 11924, 0, 10712, 65015, 120733, 563, 64815, 0, 9013, + 5588, 57, 0, 10386, 65269, 119043, 5585, 0, 2549, 694, 0, 0, 5584, 8358, + 64717, 10238, 194919, 10919, 277, 7980, 0, 41815, 194920, 41800, 5589, + 41807, 2664, 12793, 5586, 1574, 10513, 0, 2525, 4852, 5749, 0, 41605, + 64696, 0, 1039, 9801, 10155, 5745, 188, 8135, 0, 10055, 120655, 9055, + 41853, 4858, 5657, 0, 436, 4771, 0, 0, 5654, 4856, 8051, 120799, 0, 0, + 5652, 10945, 0, 0, 0, 3661, 7863, 118834, 0, 41302, 0, 0, 5402, 10234, + 5843, 11939, 5655, 42157, 0, 3157, 1055, 917628, 0, 3504, 0, 0, 10822, + 5149, 41927, 10226, 41871, 0, 3594, 10272, 10304, 40, 12657, 594, 10244, + 386, 10256, 8834, 10816, 118866, 3467, 0, 0, 3331, 946, 10231, 1495, + 8131, 13179, 194752, 9562, 4304, 0, 8160, 0, 194753, 64529, 64656, 63995, + 1348, 12239, 64013, 5666, 13303, 10555, 194729, 119919, 9091, 10798, + 65230, 13269, 10195, 194931, 7732, 41905, 9793, 0, 6097, 5668, 8780, + 4982, 119883, 5670, 63969, 0, 194762, 2672, 3735, 5667, 13138, 120807, + 9484, 10724, 13203, 119024, 65258, 194933, 4361, 9487, 64314, 9286, 1497, + 195034, 1932, 12442, 6193, 3571, 13247, 0, 7973, 119157, 64821, 11964, + 41339, 7873, 63968, 119219, 553, 120653, 194690, 194857, 3604, 0, 4587, + 0, 0, 119020, 65149, 1962, 194861, 194696, 5633, 194862, 66337, 0, 0, + 64905, 12856, 5437, 65208, 10669, 8702, 7964, 63971, 9135, 199, 10976, + 4105, 63880, 194699, 194736, 194710, 12148, 13148, 194684, 0, 9226, + 120439, 12399, 10765, 5634, 4524, 12720, 4724, 0, 8407, 66323, 12224, + 120815, 0, 5221, 64348, 328, 7886, 0, 5448, 5636, 120071, 5329, 0, 5638, + 118839, 7940, 119076, 0, 65182, 5635, 3373, 2986, 118880, 194751, 3437, + 194832, 6203, 9833, 12693, 11920, 8274, 194838, 64394, 1657, 41558, + 194672, 118887, 5639, 2954, 5660, 5640, 65376, 194818, 194817, 194820, + 194819, 5297, 41637, 13284, 6112, 7968, 41625, 194737, 194738, 120507, + 41780, 5642, 0, 194827, 42181, 4342, 42039, 194831, 1677, 0, 4585, 5641, + 8259, 13301, 1058, 2719, 194625, 194638, 917587, 1144, 5868, 0, 10867, + 65169, 13277, 4308, 2539, 0, 65122, 543, 64916, 64736, 2547, 10209, + 119170, 65317, 5399, 65316, 0, 41633, 7902, 64932, 9000, 12233, 0, + 120315, 1865, 0, 5613, 194772, 12994, 65057, 5610, 0, 6228, 4307, 3482, + 42133, 10787, 194840, 2997, 506, 5609, 41194, 12863, 194776, 64412, + 41195, 2412, 8169, 8186, 8841, 9522, 516, 13130, 41197, 0, 34, 64007, + 10030, 5306, 1612, 120187, 120182, 64951, 120184, 12001, 10211, 120177, + 64564, 66365, 120174, 120173, 7749, 120175, 118898, 1758, 413, 10667, + 4677, 120197, 9133, 1935, 120194, 1042, 120196, 64779, 1931, 10248, 6185, + 64776, 1217, 10242, 708, 825, 118913, 0, 12294, 0, 194908, 9138, 2534, + 810, 12631, 194911, 0, 4424, 119255, 5591, 1239, 2364, 0, 917562, 3403, + 119193, 0, 64364, 0, 65250, 10027, 8998, 0, 0, 9152, 194952, 120191, + 2980, 119169, 41850, 931, 3433, 13170, 12615, 1594, 119937, 65397, 0, + 12944, 41623, 8730, 41353, 65409, 119009, 4337, 65188, 41394, 918, + 194899, 935, 7681, 194900, 377, 41393, 12668, 194904, 2477, 64301, 0, 0, + 0, 65201, 9528, 0, 12884, 194881, 7907, 194883, 120186, 194885, 65328, + 10673, 119217, 194889, 119128, 917575, 1781, 5496, 3357, 62, 1649, + 120641, 964, 119242, 65033, 194894, 0, 0, 65035, 194872, 65038, 0, 64602, + 119856, 0, 12220, 12711, 66575, 4542, 194875, 8423, 3348, 448, 120573, + 2991, 9364, 0, 997, 7949, 194918, 12849, 0, 0, 3073, 42000, 9714, 120679, + 4657, 12988, 4658, 65335, 12335, 119228, 65237, 6241, 2818, 4877, 2385, + 5463, 41897, 4172, 10052, 4409, 0, 10873, 12095, 0, 5346, 120328, 0, + 6237, 5461, 118803, 12722, 119999, 40974, 65231, 64828, 12678, 194868, + 8592, 1257, 0, 10970, 2408, 3251, 66617, 3274, 5465, 41501, 2461, 0, 0, + 5342, 8317, 194663, 194999, 3263, 120559, 8673, 194719, 3270, 64539, + 65338, 0, 120518, 0, 0, 5535, 9142, 0, 4289, 8687, 10938, 120658, 118790, + 1182, 2542, 186, 0, 119156, 5770, 529, 65196, 12612, 12949, 10586, 10790, + 10839, 8920, 5241, 41207, 0, 0, 41594, 225, 0, 5688, 41300, 41204, 0, + 118794, 10721, 41209, 9254, 42097, 1794, 41875, 65238, 5624, 266, 120221, + 120222, 41873, 3617, 120040, 41494, 119824, 8420, 13088, 120214, 10225, + 41338, 3734, 7734, 0, 5502, 66605, 4452, 41260, 0, 0, 4511, 5161, 10572, + 0, 42115, 42050, 64349, 41083, 0, 0, 0, 9003, 8192, 0, 5305, 9653, 10616, + 1697, 9546, 0, 194847, 119174, 41482, 65205, 10031, 64063, 12353, 12535, + 8620, 120207, 64058, 8799, 42131, 42031, 64062, 1028, 64060, 64059, 837, + 10567, 119960, 41606, 3176, 64773, 0, 2902, 64043, 64042, 41740, 3609, + 120577, 13200, 832, 64044, 42156, 10076, 64040, 64039, 12919, 1034, 3392, + 10753, 5180, 64033, 41395, 65468, 64038, 64037, 64036, 41898, 4291, + 63966, 64015, 41114, 243, 194930, 0, 6024, 194586, 12128, 194910, 3476, + 8973, 8538, 64011, 64010, 64008, 4285, 4800, 7706, 41750, 0, 2538, 64009, + 204, 0, 4802, 4111, 8239, 9098, 4805, 64001, 214, 7885, 42143, 8321, 0, + 12208, 4767, 9343, 64049, 64048, 0, 1133, 64053, 64052, 64051, 41187, + 8692, 6022, 119052, 10005, 12329, 41333, 0, 43, 1942, 0, 0, 41107, 12619, + 41121, 3885, 92, 64023, 64022, 64021, 64020, 0, 12451, 64025, 41412, + 41485, 12035, 119208, 6254, 10501, 64018, 8890, 12457, 66587, 194837, 0, + 64778, 118915, 194834, 120193, 0, 66637, 7995, 8759, 41411, 13094, 12449, + 8546, 41414, 65109, 3179, 0, 4720, 10165, 0, 119249, 0, 10751, 0, 12915, + 120180, 10535, 0, 0, 0, 6168, 10934, 1946, 294, 41874, 5494, 4639, 0, + 12040, 6196, 4498, 0, 64028, 64027, 41789, 41788, 2960, 118786, 118795, + 8969, 119887, 10197, 0, 119886, 2950, 11998, 6210, 119890, 370, 3549, + 64790, 7801, 4953, 119967, 0, 0, 3297, 10681, 120693, 1135, 194783, 0, + 5063, 3517, 2964, 119257, 0, 2552, 41546, 60, 10627, 8649, 8252, 729, + 120598, 0, 10541, 0, 64923, 41770, 41547, 9032, 0, 0, 119899, 41215, + 119897, 119898, 12832, 119904, 8081, 3761, 3537, 119908, 9137, 119906, + 8999, 65343, 3850, 3466, 4327, 0, 9373, 0, 908, 6282, 8611, 9813, 0, + 41655, 537, 41511, 4179, 8978, 120540, 119135, 1842, 10527, 120409, 9628, + 3848, 12081, 9826, 64502, 1767, 5336, 120200, 64659, 663, 194846, 10780, + 0, 13108, 120574, 120204, 120198, 120205, 347, 42112, 40992, 4100, 920, + 1811, 1355, 7739, 917547, 3592, 10078, 5318, 0, 0, 120073, 0, 6224, + 120470, 9381, 0, 64345, 0, 9281, 3296, 12865, 0, 0, +}; + +#define code_magic 47 +#define code_size 16384 +#define code_poly 16427 diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xreadlinesmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xreadlinesmodule.c new file mode 100644 index 00000000..81ed3049 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xreadlinesmodule.c @@ -0,0 +1,171 @@ +#include "Python.h" + +PyDoc_STRVAR(xreadlines_doc, +"xreadlines(f)\n\ +\n\ +Return an xreadlines object for the file f."); + +typedef struct { + PyObject_HEAD + PyObject *file; + PyObject *lines; + int lineslen; + int lineno; + int abslineno; +} PyXReadlinesObject; + +static PyTypeObject XReadlinesObject_Type; + +static void +xreadlines_dealloc(PyXReadlinesObject *op) +{ + Py_XDECREF(op->file); + Py_XDECREF(op->lines); + PyObject_DEL(op); +} + +/* A larger chunk size doesn't seem to make a difference */ +#define CHUNKSIZE 8192 + +static PyXReadlinesObject * +newreadlinesobject(PyObject *file) +{ + PyXReadlinesObject *op; + op = PyObject_NEW(PyXReadlinesObject, &XReadlinesObject_Type); + if (op == NULL) + return NULL; + Py_XINCREF(file); + op->file = file; + op->lines = NULL; + op->abslineno = op->lineno = op->lineslen = 0; + return op; +} + +static PyObject * +xreadlines(PyObject *self, PyObject *args) +{ + PyObject *file; + PyXReadlinesObject *ret; + + if (!PyArg_ParseTuple(args, "O:xreadlines", &file)) + return NULL; + ret = newreadlinesobject(file); + return (PyObject*)ret; +} + +static PyObject * +xreadlines_common(PyXReadlinesObject *a) +{ + if (a->lineno >= a->lineslen) { + Py_XDECREF(a->lines); + a->lines = PyObject_CallMethod(a->file, "readlines", "(i)", + CHUNKSIZE); + if (a->lines == NULL) + return NULL; + a->lineno = 0; + if ((a->lineslen = PySequence_Size(a->lines)) < 0) + return NULL; + } + a->abslineno++; + return PySequence_GetItem(a->lines, a->lineno++); +} + +static PyObject * +xreadlines_item(PyXReadlinesObject *a, int i) +{ + if (i != a->abslineno) { + PyErr_SetString(PyExc_RuntimeError, + "xreadlines object accessed out of order"); + return NULL; + } + return xreadlines_common(a); +} + +static PyObject * +xreadlines_iternext(PyXReadlinesObject *a) +{ + PyObject *res; + + res = xreadlines_common(a); + if (res == NULL && PyErr_ExceptionMatches(PyExc_IndexError)) + PyErr_Clear(); + return res; +} + +static PyObject * +xreadlines_next(PyXReadlinesObject *a, PyObject *args) +{ + PyObject *res; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + res = xreadlines_common(a); + if (res == NULL && PyErr_ExceptionMatches(PyExc_IndexError)) + PyErr_SetObject(PyExc_StopIteration, Py_None); + return res; +} + +PyDoc_STRVAR(next_doc, "x.next() -> the next line or raise StopIteration"); + +static PyMethodDef xreadlines_methods[] = { + {"next", (PyCFunction)xreadlines_next, METH_VARARGS, next_doc}, + {NULL, NULL} +}; + +static PyObject * +xreadlines_getattr(PyObject *a, char *name) +{ + return Py_FindMethod(xreadlines_methods, a, name); +} + +static PySequenceMethods xreadlines_as_sequence = { + 0, /*sq_length*/ + 0, /*sq_concat*/ + 0, /*sq_repeat*/ + (intargfunc)xreadlines_item, /*sq_item*/ +}; + +static PyTypeObject XReadlinesObject_Type = { + PyObject_HEAD_INIT(NULL) + 0, + "xreadlines.xreadlines", + sizeof(PyXReadlinesObject), + 0, + (destructor)xreadlines_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + xreadlines_getattr, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &xreadlines_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)xreadlines_iternext, /* tp_iternext */ +}; + +static PyMethodDef xreadlines_functions[] = { + {"xreadlines", xreadlines, METH_VARARGS, xreadlines_doc}, + {NULL, NULL} +}; + +PyMODINIT_FUNC +initxreadlines(void) +{ + XReadlinesObject_Type.ob_type = &PyType_Type; + Py_InitModule("xreadlines", xreadlines_functions); + PyErr_Warn(PyExc_DeprecationWarning, + "xreadlines is deprecated; use 'for line in file'."); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xxmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xxmodule.c new file mode 100644 index 00000000..a647bf17 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xxmodule.c @@ -0,0 +1,374 @@ + +/* Use this file as a template to start implementing a module that + also declares object types. All occurrences of 'Xxo' should be changed + to something reasonable for your objects. After that, all other + occurrences of 'xx' should be changed to something reasonable for your + module. If your module is named foo your sourcefile should be named + foomodule.c. + + You will probably want to delete all references to 'x_attr' and add + your own types of attributes instead. Maybe you want to name your + local variables other than 'self'. If your object type is needed in + other files, you'll have to create a file "foobarobject.h"; see + intobject.h for an example. */ + +/* Xxo objects */ + +#include "Python.h" + +static PyObject *ErrorObject; + +typedef struct { + PyObject_HEAD + PyObject *x_attr; /* Attributes dictionary */ +} XxoObject; + +static PyTypeObject Xxo_Type; + +#define XxoObject_Check(v) ((v)->ob_type == &Xxo_Type) + +static XxoObject * +newXxoObject(PyObject *arg) +{ + XxoObject *self; + self = PyObject_New(XxoObject, &Xxo_Type); + if (self == NULL) + return NULL; + self->x_attr = NULL; + return self; +} + +/* Xxo methods */ + +static void +Xxo_dealloc(XxoObject *self) +{ + Py_XDECREF(self->x_attr); + PyObject_Del(self); +} + +static PyObject * +Xxo_demo(XxoObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":demo")) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef Xxo_methods[] = { + {"demo", (PyCFunction)Xxo_demo, METH_VARARGS, + PyDoc_STR("demo() -> None")}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +Xxo_getattr(XxoObject *self, char *name) +{ + if (self->x_attr != NULL) { + PyObject *v = PyDict_GetItemString(self->x_attr, name); + if (v != NULL) { + Py_INCREF(v); + return v; + } + } + return Py_FindMethod(Xxo_methods, (PyObject *)self, name); +} + +static int +Xxo_setattr(XxoObject *self, char *name, PyObject *v) +{ + if (self->x_attr == NULL) { + self->x_attr = PyDict_New(); + if (self->x_attr == NULL) + return -1; + } + if (v == NULL) { + int rv = PyDict_DelItemString(self->x_attr, name); + if (rv < 0) + PyErr_SetString(PyExc_AttributeError, + "delete non-existing Xxo attribute"); + return rv; + } + else + return PyDict_SetItemString(self->x_attr, name, v); +} + +static PyTypeObject Xxo_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "xxmodule.Xxo", /*tp_name*/ + sizeof(XxoObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)Xxo_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)Xxo_getattr, /*tp_getattr*/ + (setattrfunc)Xxo_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; +/* --------------------------------------------------------------------- */ + +/* Function of two integers returning integer */ + +PyDoc_STRVAR(xx_foo_doc, +"foo(i,j)\n\ +\n\ +Return the sum of i and j."); + +static PyObject * +xx_foo(PyObject *self, PyObject *args) +{ + long i, j; + long res; + if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) + return NULL; + res = i+j; /* XXX Do something here */ + return PyInt_FromLong(res); +} + + +/* Function of no arguments returning new Xxo object */ + +static PyObject * +xx_new(PyObject *self, PyObject *args) +{ + XxoObject *rv; + + if (!PyArg_ParseTuple(args, ":new")) + return NULL; + rv = newXxoObject(args); + if (rv == NULL) + return NULL; + return (PyObject *)rv; +} + +/* Example with subtle bug from extensions manual ("Thin Ice"). */ + +static PyObject * +xx_bug(PyObject *self, PyObject *args) +{ + PyObject *list, *item; + + if (!PyArg_ParseTuple(args, "O:bug", &list)) + return NULL; + + item = PyList_GetItem(list, 0); + /* Py_INCREF(item); */ + PyList_SetItem(list, 1, PyInt_FromLong(0L)); + PyObject_Print(item, stdout, 0); + printf("\n"); + /* Py_DECREF(item); */ + + Py_INCREF(Py_None); + return Py_None; +} + +/* Test bad format character */ + +static PyObject * +xx_roj(PyObject *self, PyObject *args) +{ + PyObject *a; + long b; + if (!PyArg_ParseTuple(args, "O#:roj", &a, &b)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + + +/* ---------- */ + +static PyTypeObject Str_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "xxmodule.Str", /*tp_name*/ + 0, /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + &PyString_Type, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +/* ---------- */ + +static PyObject * +null_richcompare(PyObject *self, PyObject *other, int op) +{ + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static PyTypeObject Null_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "xxmodule.Null", /*tp_name*/ + 0, /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + null_richcompare, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + &PyBaseObject_Type, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + PyType_GenericNew, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + + +/* ---------- */ + + +/* List of functions defined in the module */ + +static PyMethodDef xx_methods[] = { + {"roj", xx_roj, METH_VARARGS, + PyDoc_STR("roj(a,b) -> None")}, + {"foo", xx_foo, METH_VARARGS, + xx_foo_doc}, + {"new", xx_new, METH_VARARGS, + PyDoc_STR("new() -> new Xx object")}, + {"bug", xx_bug, METH_VARARGS, + PyDoc_STR("bug(o) -> None")}, + {NULL, NULL} /* sentinel */ +}; + +PyDoc_STRVAR(module_doc, +"This is a template module just for instruction."); + +/* Initialization function for the module (*must* be called initxx) */ + +PyMODINIT_FUNC +initxx(void) +{ + PyObject *m; + + /* Finalize the type object including setting type of the new type + * object; doing it here is required for portability to Windows + * without requiring C++. */ + if (PyType_Ready(&Xxo_Type) < 0) + return; + + /* Create the module and add the functions */ + m = Py_InitModule3("xx", xx_methods, module_doc); + + /* Add some symbolic constants to the module */ + if (ErrorObject == NULL) { + ErrorObject = PyErr_NewException("xx.error", NULL, NULL); + if (ErrorObject == NULL) + return; + } + Py_INCREF(ErrorObject); + PyModule_AddObject(m, "error", ErrorObject); + + /* Add Str */ + if (PyType_Ready(&Str_Type) < 0) + return; + PyModule_AddObject(m, "Str", (PyObject *)&Str_Type); + + /* Add Null */ + if (PyType_Ready(&Null_Type) < 0) + return; + PyModule_AddObject(m, "Null", (PyObject *)&Null_Type); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xxsubtype.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xxsubtype.c new file mode 100644 index 00000000..9d36e46d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/xxsubtype.c @@ -0,0 +1,303 @@ +#include "Python.h" +#include "structmember.h" + +PyDoc_STRVAR(xxsubtype__doc__, +"xxsubtype is an example module showing how to subtype builtin types from C.\n" +"test_descr.py in the standard test suite requires it in order to complete.\n" +"If you don't care about the examples, and don't intend to run the Python\n" +"test suite, you can recompile Python without Modules/xxsubtype.c."); + +/* We link this module statically for convenience. If compiled as a shared + library instead, some compilers don't allow addresses of Python objects + defined in other libraries to be used in static initializers here. The + DEFERRED_ADDRESS macro is used to tag the slots where such addresses + appear; the module init function must fill in the tagged slots at runtime. + The argument is for documentation -- the macro ignores it. +*/ +#define DEFERRED_ADDRESS(ADDR) 0 + +/* spamlist -- a list subtype */ + +typedef struct { + PyListObject list; + int state; +} spamlistobject; + +static PyObject * +spamlist_getstate(spamlistobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":getstate")) + return NULL; + return PyInt_FromLong(self->state); +} + +static PyObject * +spamlist_setstate(spamlistobject *self, PyObject *args) +{ + int state; + + if (!PyArg_ParseTuple(args, "i:setstate", &state)) + return NULL; + self->state = state; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +spamlist_specialmeth(PyObject *self, PyObject *args, PyObject *kw) +{ + PyObject *result = PyTuple_New(3); + + if (result != NULL) { + if (self == NULL) + self = Py_None; + if (kw == NULL) + kw = Py_None; + Py_INCREF(self); + PyTuple_SET_ITEM(result, 0, self); + Py_INCREF(args); + PyTuple_SET_ITEM(result, 1, args); + Py_INCREF(kw); + PyTuple_SET_ITEM(result, 2, kw); + } + return result; +} + +static PyMethodDef spamlist_methods[] = { + {"getstate", (PyCFunction)spamlist_getstate, METH_VARARGS, + PyDoc_STR("getstate() -> state")}, + {"setstate", (PyCFunction)spamlist_setstate, METH_VARARGS, + PyDoc_STR("setstate(state)")}, + /* These entries differ only in the flags; they are used by the tests + in test.test_descr. */ + {"classmeth", (PyCFunction)spamlist_specialmeth, + METH_VARARGS | METH_KEYWORDS | METH_CLASS, + PyDoc_STR("classmeth(*args, **kw)")}, + {"staticmeth", (PyCFunction)spamlist_specialmeth, + METH_VARARGS | METH_KEYWORDS | METH_STATIC, + PyDoc_STR("staticmeth(*args, **kw)")}, + {NULL, NULL}, +}; + +static PyTypeObject spamlist_type; + +static int +spamlist_init(spamlistobject *self, PyObject *args, PyObject *kwds) +{ + if (PyList_Type.tp_init((PyObject *)self, args, kwds) < 0) + return -1; + self->state = 0; + return 0; +} + +static PyObject * +spamlist_state_get(spamlistobject *self) +{ + return PyInt_FromLong(self->state); +} + +static PyGetSetDef spamlist_getsets[] = { + {"state", (getter)spamlist_state_get, NULL, + PyDoc_STR("an int variable for demonstration purposes")}, + {0} +}; + +static PyTypeObject spamlist_type = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "xxsubtype.spamlist", + sizeof(spamlistobject), + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + spamlist_methods, /* tp_methods */ + 0, /* tp_members */ + spamlist_getsets, /* tp_getset */ + DEFERRED_ADDRESS(&PyList_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)spamlist_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +/* spamdict -- a dict subtype */ + +typedef struct { + PyDictObject dict; + int state; +} spamdictobject; + +static PyObject * +spamdict_getstate(spamdictobject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":getstate")) + return NULL; + return PyInt_FromLong(self->state); +} + +static PyObject * +spamdict_setstate(spamdictobject *self, PyObject *args) +{ + int state; + + if (!PyArg_ParseTuple(args, "i:setstate", &state)) + return NULL; + self->state = state; + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef spamdict_methods[] = { + {"getstate", (PyCFunction)spamdict_getstate, METH_VARARGS, + PyDoc_STR("getstate() -> state")}, + {"setstate", (PyCFunction)spamdict_setstate, METH_VARARGS, + PyDoc_STR("setstate(state)")}, + {NULL, NULL}, +}; + +static PyTypeObject spamdict_type; + +static int +spamdict_init(spamdictobject *self, PyObject *args, PyObject *kwds) +{ + if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) + return -1; + self->state = 0; + return 0; +} + +static PyMemberDef spamdict_members[] = { + {"state", T_INT, offsetof(spamdictobject, state), READONLY, + PyDoc_STR("an int variable for demonstration purposes")}, + {0} +}; + +static PyTypeObject spamdict_type = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "xxsubtype.spamdict", + sizeof(spamdictobject), + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + spamdict_methods, /* tp_methods */ + spamdict_members, /* tp_members */ + 0, /* tp_getset */ + DEFERRED_ADDRESS(&PyDict_Type), /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)spamdict_init, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +static PyObject * +spam_bench(PyObject *self, PyObject *args) +{ + PyObject *obj, *name, *res; + int n = 1000; + time_t t0, t1; + + if (!PyArg_ParseTuple(args, "OS|i", &obj, &name, &n)) + return NULL; + t0 = clock(); + while (--n >= 0) { + res = PyObject_GetAttr(obj, name); + if (res == NULL) + return NULL; + Py_DECREF(res); + } + t1 = clock(); + return PyFloat_FromDouble((double)(t1-t0) / CLOCKS_PER_SEC); +} + +static PyMethodDef xxsubtype_functions[] = { + {"bench", spam_bench, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +PyMODINIT_FUNC +initxxsubtype(void) +{ + PyObject *m; + + /* Fill in deferred data addresses. This must be done before + PyType_Ready() is called. Note that PyType_Ready() automatically + initializes the ob.ob_type field to &PyType_Type if it's NULL, + so it's not necessary to fill in ob_type first. */ + spamdict_type.tp_base = &PyDict_Type; + if (PyType_Ready(&spamdict_type) < 0) + return; + + spamlist_type.tp_base = &PyList_Type; + if (PyType_Ready(&spamlist_type) < 0) + return; + + m = Py_InitModule3("xxsubtype", + xxsubtype_functions, + xxsubtype__doc__); + if (m == NULL) + return; + + if (PyType_Ready(&spamlist_type) < 0) + return; + if (PyType_Ready(&spamdict_type) < 0) + return; + + Py_INCREF(&spamlist_type); + if (PyModule_AddObject(m, "spamlist", + (PyObject *) &spamlist_type) < 0) + return; + + Py_INCREF(&spamdict_type); + if (PyModule_AddObject(m, "spamdict", + (PyObject *) &spamdict_type) < 0) + return; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/yuv.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/yuv.h new file mode 100644 index 00000000..fa406344 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/yuv.h @@ -0,0 +1,99 @@ + +#ifndef Py_YUV_H +#define Py_YUV_H +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SVideo YUV 4:1:1 format. + * + * 4 consecutive quadwords describe 8 pixels on 2 lines, as depicted + * below. An array of (width/4) of the below structure describes 2 + * scan lines. + * + * +-------------------+ + * | 00 | 01 | 02 | 03 | . . . + * +-------------------+ + * | 10 | 11 | 12 | 13 | . . . + * +-------------------+ + */ +struct yuv411 { + struct { + unsigned int dummy:8; + unsigned int y0:8; + unsigned int u0:2; + unsigned int v0:2; + unsigned int y1:8; + unsigned int u1:2; + unsigned int v1:2; + } v[4]; +}; + +#define YUV411_Y00(y) (y).v[0].y0 +#define YUV411_Y01(y) (y).v[1].y0 +#define YUV411_Y02(y) (y).v[2].y0 +#define YUV411_Y03(y) (y).v[3].y0 +#define YUV411_Y10(y) (y).v[0].y1 +#define YUV411_Y11(y) (y).v[1].y1 +#define YUV411_Y12(y) (y).v[2].y1 +#define YUV411_Y13(y) (y).v[3].y1 +#define YUV411_U00(y) ((y).v[0].u0<<6|(y).v[1].u0<<4|(y).v[2].u0<<2|(y).v[3].u0) +#define YUV411_U01(y) YUV411_U00(y) +#define YUV411_U02(y) YUV411_U00(y) +#define YUV411_U03(y) YUV411_U00(y) +#define YUV411_U10(y) ((y).v[0].u1<<6|(y).v[1].u1<<4|(y).v[2].u1<<2|(y).v[3].u1) +#define YUV411_U11(y) YUV411_U10(y) +#define YUV411_U12(y) YUV411_U10(y) +#define YUV411_U13(y) YUV411_U10(y) +#define YUV411_V00(y) ((y).v[0].v0<<6|(y).v[1].v0<<4|(y).v[2].v0<<2|(y).v[3].v0) +#define YUV411_V01(y) YUV411_V00(y) +#define YUV411_V02(y) YUV411_V00(y) +#define YUV411_V03(y) YUV411_V00(y) +#define YUV411_V10(y) ((y).v[0].v1<<6|(y).v[1].v1<<4|(y).v[2].v1<<2|(y).v[3].v1) +#define YUV411_V11(y) YUV411_V10(y) +#define YUV411_V12(y) YUV411_V10(y) +#define YUV411_V13(y) YUV411_V10(y) + +/* + * Compression Library YUV 4:2:2 format. + * + * 1 longword describes 2 pixels. + * + * +-------+ + * | 0 | 1 | + * +-------+ + */ +struct yuv422 { + unsigned int u:8; + unsigned int y0:8; + unsigned int v:8; + unsigned int y1:8; +}; +#define YUV422_Y0(y) (y).y0 +#define YUV422_Y1(y) (y).y1 +#define YUV422_U0(y) (y).u +#define YUV422_U1(y) (y).u +#define YUV422_V0(y) (y).v +#define YUV422_V1(y) (y).v + +/* + * Compression library YUV 4:2:2 Duplicate Chroma format. + * + * This is like the previous format, but the U and V values are + * duplicated vertically (and hence there is some redundancy in the + * data). With other words, lines 2*n and 2*n+1 have the same U and V + * values but different Y values. + */ + +/* + * Conversion functions. + */ +void yuv_sv411_to_cl422dc(int, void *, void *, int, int); +void yuv_sv411_to_cl422dc_quartersize(int, void *, void *, int, int); +void yuv_sv411_to_cl422dc_sixteenthsize(int, void *, void *, int, int); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_YUV_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/yuvconvert.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/yuvconvert.c new file mode 100644 index 00000000..f24b35c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/yuvconvert.c @@ -0,0 +1,118 @@ + +#include "yuv.h" + +void +yuv_sv411_to_cl422dc(int invert, void *data, void *yuv, int width, int height) +{ + struct yuv411 *in = data; + struct yuv422 *out_even = yuv; + struct yuv422 *out_odd = out_even + width / 2; + int i, j; /* counters */ + + for (i = height / 2; i--; ) { + for (j = width / 4; j--; ) { + YUV422_Y0(*out_even) = YUV411_Y00(*in); + YUV422_U0(*out_even) = YUV411_U00(*in); + YUV422_V0(*out_even) = YUV411_V00(*in); + YUV422_Y1(*out_even) = YUV411_Y01(*in); + out_even++; + YUV422_Y0(*out_even) = YUV411_Y02(*in); + YUV422_U0(*out_even) = YUV411_U02(*in); + YUV422_V0(*out_even) = YUV411_V02(*in); + YUV422_Y1(*out_even) = YUV411_Y03(*in); + out_even++; + YUV422_Y0(*out_odd) = YUV411_Y10(*in); + YUV422_U0(*out_odd) = YUV411_U10(*in); + YUV422_V0(*out_odd) = YUV411_V10(*in); + YUV422_Y1(*out_odd) = YUV411_Y11(*in); + out_odd++; + YUV422_Y0(*out_odd) = YUV411_Y12(*in); + YUV422_U0(*out_odd) = YUV411_U12(*in); + YUV422_V0(*out_odd) = YUV411_V12(*in); + YUV422_Y1(*out_odd) = YUV411_Y13(*in); + out_odd++; + in++; + } + out_even += width / 2; + out_odd += width / 2; + } +} + +void +yuv_sv411_to_cl422dc_quartersize(int invert, void *data, void *yuv, + int width, int height) +{ + int w4 = width / 4; /* quarter of width is used often */ + struct yuv411 *in_even = data; + struct yuv411 *in_odd = in_even + w4; + struct yuv422 *out_even = yuv; + struct yuv422 *out_odd = out_even + w4; + int i, j; /* counters */ + int u, v; /* U and V values */ + + for (i = height / 4; i--; ) { + for (j = w4; j--; ) { + u = YUV411_U00(*in_even); + v = YUV411_V00(*in_even); + + YUV422_Y0(*out_even) = YUV411_Y00(*in_even); + YUV422_U0(*out_even) = u; + YUV422_V0(*out_even) = v; + YUV422_Y1(*out_even) = YUV411_Y02(*in_even); + + YUV422_Y0(*out_odd) = YUV411_Y10(*in_odd); + YUV422_U0(*out_odd) = u; + YUV422_V0(*out_odd) = v; + YUV422_Y1(*out_odd) = YUV411_Y12(*in_odd); + + in_even++; + in_odd++; + out_even++; + out_odd++; + } + in_even += w4; + in_odd += w4; + out_even += w4; + out_odd += w4; + } +} + +void +yuv_sv411_to_cl422dc_sixteenthsize(int invert, void *data, void *yuv, + int width, int height) +{ + int w4_3 = 3 * width / 4; /* three quarters of width is used often */ + int w8 = width / 8; /* and so is one eighth */ + struct yuv411 *in_even = data; + struct yuv411 *in_odd = in_even + width / 2; + struct yuv422 *out_even = yuv; + struct yuv422 *out_odd = out_even + w8; + int i, j; /* counters */ + int u, v; /* U and V values */ + + for (i = height / 8; i--; ) { + for (j = w8; j--; ) { + u = YUV411_U00(in_even[0]); + v = YUV411_V00(in_even[0]); + + YUV422_Y0(*out_even) = YUV411_Y00(in_even[0]); + YUV422_U0(*out_even) = u; + YUV422_V0(*out_even) = v; + YUV422_Y1(*out_even) = YUV411_Y00(in_even[1]); + + YUV422_Y0(*out_odd) = YUV411_Y00(in_odd[0]); + YUV422_U0(*out_odd) = u; + YUV422_V0(*out_odd) = v; + YUV422_Y1(*out_odd) = YUV411_Y00(in_even[1]); + + in_even += 2; + in_odd += 2; + out_even++; + out_odd++; + } + in_even += w4_3; + in_odd += w4_3; + out_even += w8; + out_odd += w8; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/zipimport.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/zipimport.c new file mode 100644 index 00000000..d2ffaf69 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/zipimport.c @@ -0,0 +1,1188 @@ +#include "Python.h" +#include "structmember.h" +#include "osdefs.h" +#include "marshal.h" +#include "compile.h" +#include + + +#define IS_SOURCE 0x0 +#define IS_BYTECODE 0x1 +#define IS_PACKAGE 0x2 + +struct st_zip_searchorder { + char suffix[14]; + int type; +}; + +/* zip_searchorder defines how we search for a module in the Zip + archive: we first search for a package __init__, then for + non-package .pyc, .pyo and .py entries. The .pyc and .pyo entries + are swapped by initzipimport() if we run in optimized mode. Also, + '/' is replaced by SEP there. */ +static struct st_zip_searchorder zip_searchorder[] = { + {"/__init__.pyc", IS_PACKAGE | IS_BYTECODE}, + {"/__init__.pyo", IS_PACKAGE | IS_BYTECODE}, + {"/__init__.py", IS_PACKAGE | IS_SOURCE}, + {".pyc", IS_BYTECODE}, + {".pyo", IS_BYTECODE}, + {".py", IS_SOURCE}, + {"", 0} +}; + +/* zipimporter object definition and support */ + +typedef struct _zipimporter ZipImporter; + +struct _zipimporter { + PyObject_HEAD + PyObject *archive; /* pathname of the Zip archive */ + PyObject *prefix; /* file prefix: "a/sub/directory/" */ + PyObject *files; /* dict with file info {path: toc_entry} */ +}; + +static PyTypeObject ZipImporter_Type; +static PyObject *ZipImportError; +static PyObject *zip_directory_cache = NULL; + +/* forward decls */ +static PyObject *read_directory(char *archive); +static PyObject *get_data(char *archive, PyObject *toc_entry); +static PyObject *get_module_code(ZipImporter *self, char *fullname, + int *p_ispackage, char **p_modpath); + + +#define ZipImporter_Check(op) PyObject_TypeCheck(op, &ZipImporter_Type) + + +/* zipimporter.__init__ + Split the "subdirectory" from the Zip archive path, lookup a matching + entry in sys.path_importer_cache, fetch the file directory from there + if found, or else read it from the archive. */ +static int +zipimporter_init(ZipImporter *self, PyObject *args, PyObject *kwds) +{ + char *path, *p, *prefix, buf[MAXPATHLEN+2]; + int len; + + if (!PyArg_ParseTuple(args, "s:zipimporter", + &path)) + return -1; + + len = strlen(path); + if (len == 0) { + PyErr_SetString(ZipImportError, "archive path is empty"); + return -1; + } + if (len >= MAXPATHLEN) { + PyErr_SetString(ZipImportError, + "archive path too long"); + return -1; + } + strcpy(buf, path); + +#ifdef ALTSEP + for (p = buf; *p; p++) { + if (*p == ALTSEP) + *p = SEP; + } +#endif + + path = NULL; + prefix = NULL; + for (;;) { +#ifndef RISCOS + struct stat statbuf; + int rv; + + rv = stat(buf, &statbuf); + if (rv == 0) { + /* it exists */ + if (S_ISREG(statbuf.st_mode)) + /* it's a file */ + path = buf; + break; + } +#else + if (object_exists(buf)) { + /* it exists */ + if (isfile(buf)) + /* it's a file */ + path = buf; + break; + } +#endif + /* back up one path element */ + p = strrchr(buf, SEP); + if (prefix != NULL) + *prefix = SEP; + if (p == NULL) + break; + *p = '\0'; + prefix = p; + } + if (path != NULL) { + PyObject *files; + files = PyDict_GetItemString(zip_directory_cache, path); + if (files == NULL) { + files = read_directory(buf); + if (files == NULL) + return -1; + if (PyDict_SetItemString(zip_directory_cache, path, + files) != 0) + return -1; + } + else + Py_INCREF(files); + self->files = files; + } + else { + PyErr_SetString(ZipImportError, "not a Zip file"); + return -1; + } + + if (prefix == NULL) + prefix = ""; + else { + prefix++; + len = strlen(prefix); + if (prefix[len-1] != SEP) { + /* add trailing SEP */ + prefix[len] = SEP; + prefix[len + 1] = '\0'; + } + } + + self->archive = PyString_FromString(buf); + if (self->archive == NULL) + return -1; + + self->prefix = PyString_FromString(prefix); + if (self->prefix == NULL) + return -1; + + return 0; +} + +/* GC support. */ +static int +zipimporter_traverse(PyObject *obj, visitproc visit, void *arg) +{ + ZipImporter *self = (ZipImporter *)obj; + int err; + + if (self->files != NULL) { + err = visit(self->files, arg); + if (err) + return err; + } + return 0; +} + +static void +zipimporter_dealloc(ZipImporter *self) +{ + PyObject_GC_UnTrack(self); + Py_XDECREF(self->archive); + Py_XDECREF(self->prefix); + Py_XDECREF(self->files); + self->ob_type->tp_free((PyObject *)self); +} + +static PyObject * +zipimporter_repr(ZipImporter *self) +{ + char buf[500]; + char *archive = "???"; + char *prefix = ""; + + if (self->archive != NULL && PyString_Check(self->archive)) + archive = PyString_AsString(self->archive); + if (self->prefix != NULL && PyString_Check(self->prefix)) + prefix = PyString_AsString(self->prefix); + if (prefix != NULL && *prefix) + PyOS_snprintf(buf, sizeof(buf), + "", + archive, SEP, prefix); + else + PyOS_snprintf(buf, sizeof(buf), + "", + archive); + return PyString_FromString(buf); +} + +/* return fullname.split(".")[-1] */ +static char * +get_subname(char *fullname) +{ + char *subname = strrchr(fullname, '.'); + if (subname == NULL) + subname = fullname; + else + subname++; + return subname; +} + +/* Given a (sub)modulename, write the potential file path in the + archive (without extension) to the path buffer. Return the + length of the resulting string. */ +static int +make_filename(char *prefix, char *name, char *path) +{ + int len; + char *p; + + len = strlen(prefix); + + /* self.prefix + name [+ SEP + "__init__"] + ".py[co]" */ + if (len + strlen(name) + 13 >= MAXPATHLEN) { + PyErr_SetString(ZipImportError, "path too long"); + return -1; + } + + strcpy(path, prefix); + strcpy(path + len, name); + for (p = path + len; *p; p++) { + if (*p == '.') + *p = SEP; + } + len += strlen(name); + return len; +} + +enum module_info { + MI_ERROR, + MI_NOT_FOUND, + MI_MODULE, + MI_PACKAGE +}; + +/* Return some information about a module. */ +static enum module_info +get_module_info(ZipImporter *self, char *fullname) +{ + char *subname, path[MAXPATHLEN + 1]; + int len; + struct st_zip_searchorder *zso; + + subname = get_subname(fullname); + + len = make_filename(PyString_AsString(self->prefix), subname, path); + if (len < 0) + return MI_ERROR; + + for (zso = zip_searchorder; *zso->suffix; zso++) { + strcpy(path + len, zso->suffix); + if (PyDict_GetItemString(self->files, path) != NULL) { + if (zso->type & IS_PACKAGE) + return MI_PACKAGE; + else + return MI_MODULE; + } + } + return MI_NOT_FOUND; +} + +/* Check whether we can satisfy the import of the module named by + 'fullname'. Return self if we can, None if we can't. */ +static PyObject * +zipimporter_find_module(PyObject *obj, PyObject *args) +{ + ZipImporter *self = (ZipImporter *)obj; + PyObject *path = NULL; + char *fullname; + enum module_info mi; + + if (!PyArg_ParseTuple(args, "s|O:zipimporter.find_module", + &fullname, &path)) + return NULL; + + mi = get_module_info(self, fullname); + if (mi == MI_ERROR) + return NULL; + if (mi == MI_NOT_FOUND) { + Py_INCREF(Py_None); + return Py_None; + } + Py_INCREF(self); + return (PyObject *)self; +} + +/* Load and return the module named by 'fullname'. */ +static PyObject * +zipimporter_load_module(PyObject *obj, PyObject *args) +{ + ZipImporter *self = (ZipImporter *)obj; + PyObject *code, *mod, *dict; + char *fullname, *modpath; + int ispackage; + + if (!PyArg_ParseTuple(args, "s:zipimporter.load_module", + &fullname)) + return NULL; + + code = get_module_code(self, fullname, &ispackage, &modpath); + if (code == NULL) + return NULL; + + mod = PyImport_AddModule(fullname); + if (mod == NULL) { + Py_DECREF(code); + return NULL; + } + dict = PyModule_GetDict(mod); + + /* mod.__loader__ = self */ + if (PyDict_SetItemString(dict, "__loader__", (PyObject *)self) != 0) + goto error; + + if (ispackage) { + /* add __path__ to the module *before* the code gets + executed */ + PyObject *pkgpath, *fullpath; + char *prefix = PyString_AsString(self->prefix); + char *subname = get_subname(fullname); + int err; + + fullpath = PyString_FromFormat("%s%c%s%s", + PyString_AsString(self->archive), + SEP, + *prefix ? prefix : "", + subname); + if (fullpath == NULL) + goto error; + + pkgpath = Py_BuildValue("[O]", fullpath); + Py_DECREF(fullpath); + if (pkgpath == NULL) + goto error; + err = PyDict_SetItemString(dict, "__path__", pkgpath); + Py_DECREF(pkgpath); + if (err != 0) + goto error; + } + mod = PyImport_ExecCodeModuleEx(fullname, code, modpath); + Py_DECREF(code); + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # loaded from Zip %s\n", + fullname, modpath); + return mod; +error: + Py_DECREF(code); + Py_DECREF(mod); + return NULL; +} + +/* Return a bool signifying whether the module is a package or not. */ +static PyObject * +zipimporter_is_package(PyObject *obj, PyObject *args) +{ + ZipImporter *self = (ZipImporter *)obj; + char *fullname; + enum module_info mi; + + if (!PyArg_ParseTuple(args, "s:zipimporter.is_package", + &fullname)) + return NULL; + + mi = get_module_info(self, fullname); + if (mi == MI_ERROR) + return NULL; + if (mi == MI_NOT_FOUND) { + PyErr_Format(ZipImportError, "can't find module '%.200s'", + fullname); + return NULL; + } + return PyBool_FromLong(mi == MI_PACKAGE); +} + +static PyObject * +zipimporter_get_data(PyObject *obj, PyObject *args) +{ + ZipImporter *self = (ZipImporter *)obj; + char *path; +#ifdef ALTSEP + char *p, buf[MAXPATHLEN + 1]; +#endif + PyObject *toc_entry; + int len; + + if (!PyArg_ParseTuple(args, "s:zipimporter.get_data", &path)) + return NULL; + +#ifdef ALTSEP + if (strlen(path) >= MAXPATHLEN) { + PyErr_SetString(ZipImportError, "path too long"); + return NULL; + } + strcpy(buf, path); + for (p = buf; *p; p++) { + if (*p == ALTSEP) + *p = SEP; + } + path = buf; +#endif + len = PyString_Size(self->archive); + if ((size_t)len < strlen(path) && + strncmp(path, PyString_AsString(self->archive), len) == 0 && + path[len] == SEP) { + path = path + len + 1; + } + + toc_entry = PyDict_GetItemString(self->files, path); + if (toc_entry == NULL) { + PyErr_Format(PyExc_IOError, "file not found [%.200s]", + path); + return NULL; + } + return get_data(PyString_AsString(self->archive), toc_entry); +} + +static PyObject * +zipimporter_get_code(PyObject *obj, PyObject *args) +{ + ZipImporter *self = (ZipImporter *)obj; + char *fullname; + + if (!PyArg_ParseTuple(args, "s:zipimporter.get_code", &fullname)) + return NULL; + + return get_module_code(self, fullname, NULL, NULL); +} + +static PyObject * +zipimporter_get_source(PyObject *obj, PyObject *args) +{ + ZipImporter *self = (ZipImporter *)obj; + PyObject *toc_entry; + char *fullname, *subname, path[MAXPATHLEN+1]; + int len; + enum module_info mi; + + if (!PyArg_ParseTuple(args, "s:zipimporter.get_source", &fullname)) + return NULL; + + mi = get_module_info(self, fullname); + if (mi == MI_ERROR) + return NULL; + if (mi == MI_NOT_FOUND) { + PyErr_Format(ZipImportError, "can't find module '%.200s'", + fullname); + return NULL; + } + subname = get_subname(fullname); + + len = make_filename(PyString_AsString(self->prefix), subname, path); + if (len < 0) + return NULL; + + if (mi == MI_PACKAGE) { + path[len] = SEP; + strcpy(path + len + 1, "__init__.py"); + } + else + strcpy(path + len, ".py"); + + toc_entry = PyDict_GetItemString(self->files, path); + if (toc_entry != NULL) + return get_data(PyString_AsString(self->archive), toc_entry); + + /* we have the module, but no source */ + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(doc_find_module, +"find_module(fullname, path=None) -> self or None.\n\ +\n\ +Search for a module specified by 'fullname'. 'fullname' must be the\n\ +fully qualified (dotted) module name. It returns the zipimporter\n\ +instance itself if the module was found, or None if it wasn't.\n\ +The optional 'path' argument is ignored -- it's there for compatibility\n\ +with the importer protocol."); + +PyDoc_STRVAR(doc_load_module, +"load_module(fullname) -> module.\n\ +\n\ +Load the module specified by 'fullname'. 'fullname' must be the\n\ +fully qualified (dotted) module name. It returns the imported\n\ +module, or raises ZipImportError if it wasn't found."); + +PyDoc_STRVAR(doc_get_data, +"get_data(pathname) -> string with file data.\n\ +\n\ +Return the data associated with 'pathname'. Raise IOError if\n\ +the file wasn't found."); + +PyDoc_STRVAR(doc_is_package, +"is_package(fullname) -> bool.\n\ +\n\ +Return True if the module specified by fullname is a package.\n\ +Raise ZipImportError is the module couldn't be found."); + +PyDoc_STRVAR(doc_get_code, +"get_code(fullname) -> code object.\n\ +\n\ +Return the code object for the specified module. Raise ZipImportError\n\ +is the module couldn't be found."); + +PyDoc_STRVAR(doc_get_source, +"get_source(fullname) -> source string.\n\ +\n\ +Return the source code for the specified module. Raise ZipImportError\n\ +is the module couldn't be found, return None if the archive does\n\ +contain the module, but has no source for it."); + +static PyMethodDef zipimporter_methods[] = { + {"find_module", zipimporter_find_module, METH_VARARGS, + doc_find_module}, + {"load_module", zipimporter_load_module, METH_VARARGS, + doc_load_module}, + {"get_data", zipimporter_get_data, METH_VARARGS, + doc_get_data}, + {"get_code", zipimporter_get_code, METH_VARARGS, + doc_get_code}, + {"get_source", zipimporter_get_source, METH_VARARGS, + doc_get_source}, + {"is_package", zipimporter_is_package, METH_VARARGS, + doc_is_package}, + {NULL, NULL} /* sentinel */ +}; + +static PyMemberDef zipimporter_members[] = { + {"archive", T_OBJECT, offsetof(ZipImporter, archive), READONLY}, + {"prefix", T_OBJECT, offsetof(ZipImporter, prefix), READONLY}, + {"_files", T_OBJECT, offsetof(ZipImporter, files), READONLY}, + {NULL} +}; + +PyDoc_STRVAR(zipimporter_doc, +"zipimporter(archivepath) -> zipimporter object\n\ +\n\ +Create a new zipimporter instance. 'archivepath' must be a path to\n\ +a zipfile. ZipImportError is raised if 'archivepath' doesn't point to\n\ +a valid Zip archive."); + +#define DEFERRED_ADDRESS(ADDR) 0 + +static PyTypeObject ZipImporter_Type = { + PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type)) + 0, + "zipimport.zipimporter", + sizeof(ZipImporter), + 0, /* tp_itemsize */ + (destructor)zipimporter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)zipimporter_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_GC, /* tp_flags */ + zipimporter_doc, /* tp_doc */ + zipimporter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + zipimporter_methods, /* tp_methods */ + zipimporter_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)zipimporter_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/* implementation */ + +/* Given a buffer, return the long that is represented by the first + 4 bytes, encoded as little endian. This partially reimplements + marshal.c:r_long() */ +static long +get_long(unsigned char *buf) { + long x; + x = buf[0]; + x |= (long)buf[1] << 8; + x |= (long)buf[2] << 16; + x |= (long)buf[3] << 24; +#if SIZEOF_LONG > 4 + /* Sign extension for 64-bit machines */ + x |= -(x & 0x80000000L); +#endif + return x; +} + +/* + read_directory(archive) -> files dict (new reference) + + Given a path to a Zip archive, build a dict, mapping file names + (local to the archive, using SEP as a separator) to toc entries. + + A toc_entry is a tuple: + + (compress, # compression kind; 0 for uncompressed + data_size, # size of compressed data on disk + file_size, # size of decompressed data + file_offset, # offset of file header from start of archive + time, # mod time of file (in dos format) + date, # mod data of file (in dos format) + crc, # crc checksum of the data + ) + + Directories can be recognized by the trailing SEP in the name, + data_size and file_offset are 0. +*/ +static PyObject * +read_directory(char *archive) +{ + PyObject *files = NULL; + FILE *fp; + long compress, crc, data_size, file_size, file_offset, date, time; + long header_offset, name_size, header_size, header_position; + long i, l, length, count; + char path[MAXPATHLEN + 5]; + char name[MAXPATHLEN + 5]; + char *p, endof_central_dir[22]; + long arc_offset; /* offset from beginning of file to start of zip-archive */ + + if (strlen(archive) > MAXPATHLEN) { + PyErr_SetString(PyExc_OverflowError, + "Zip path name is too long"); + return NULL; + } + strcpy(path, archive); + + fp = fopen(archive, "rb"); + if (fp == NULL) { + PyErr_Format(ZipImportError, "can't open Zip file: " + "'%.200s'", archive); + return NULL; + } + fseek(fp, -22, SEEK_END); + header_position = ftell(fp); + if (fread(endof_central_dir, 1, 22, fp) != 22) { + fclose(fp); + PyErr_Format(ZipImportError, "can't read Zip file: " + "'%.200s'", archive); + return NULL; + } + if (get_long((unsigned char *)endof_central_dir) != 0x06054B50) { + /* Bad: End of Central Dir signature */ + fclose(fp); + PyErr_Format(ZipImportError, "not a Zip file: " + "'%.200s'", archive); + return NULL; + } + + header_size = get_long((unsigned char *)endof_central_dir + 12); + header_offset = get_long((unsigned char *)endof_central_dir + 16); + arc_offset = header_position - header_offset - header_size; + header_offset += arc_offset; + + files = PyDict_New(); + if (files == NULL) + goto error; + + length = (long)strlen(path); + path[length] = SEP; + + /* Start of Central Directory */ + count = 0; + for (;;) { + PyObject *t; + int err; + + fseek(fp, header_offset, 0); /* Start of file header */ + l = PyMarshal_ReadLongFromFile(fp); + if (l != 0x02014B50) + break; /* Bad: Central Dir File Header */ + fseek(fp, header_offset + 10, 0); + compress = PyMarshal_ReadShortFromFile(fp); + time = PyMarshal_ReadShortFromFile(fp); + date = PyMarshal_ReadShortFromFile(fp); + crc = PyMarshal_ReadLongFromFile(fp); + data_size = PyMarshal_ReadLongFromFile(fp); + file_size = PyMarshal_ReadLongFromFile(fp); + name_size = PyMarshal_ReadShortFromFile(fp); + header_size = 46 + name_size + + PyMarshal_ReadShortFromFile(fp) + + PyMarshal_ReadShortFromFile(fp); + fseek(fp, header_offset + 42, 0); + file_offset = PyMarshal_ReadLongFromFile(fp) + arc_offset; + if (name_size > MAXPATHLEN) + name_size = MAXPATHLEN; + + p = name; + for (i = 0; i < name_size; i++) { + *p = (char)getc(fp); + if (*p == '/') + *p = SEP; + p++; + } + *p = 0; /* Add terminating null byte */ + header_offset += header_size; + + strncpy(path + length + 1, name, MAXPATHLEN - length - 1); + + t = Py_BuildValue("siiiiiii", path, compress, data_size, + file_size, file_offset, time, date, crc); + if (t == NULL) + goto error; + err = PyDict_SetItemString(files, name, t); + Py_DECREF(t); + if (err != 0) + goto error; + count++; + } + fclose(fp); + if (Py_VerboseFlag) + PySys_WriteStderr("# zipimport: found %ld names in %s\n", + count, archive); + return files; +error: + fclose(fp); + Py_XDECREF(files); + return NULL; +} + +/* Return the zlib.decompress function object, or NULL if zlib couldn't + be imported. The function is cached when found, so subsequent calls + don't import zlib again. Returns a *borrowed* reference. + XXX This makes zlib.decompress immortal. */ +static PyObject * +get_decompress_func(void) +{ + static PyObject *decompress = NULL; + + if (decompress == NULL) { + PyObject *zlib; + static int importing_zlib = 0; + + if (importing_zlib != 0) + /* Someone has a zlib.py[co] in their Zip file; + let's avoid a stack overflow. */ + return NULL; + importing_zlib = 1; + zlib = PyImport_ImportModule("zlib"); /* import zlib */ + importing_zlib = 0; + if (zlib != NULL) { + decompress = PyObject_GetAttrString(zlib, + "decompress"); + Py_DECREF(zlib); + } + else + PyErr_Clear(); + if (Py_VerboseFlag) + PySys_WriteStderr("# zipimport: zlib %s\n", + zlib != NULL ? "available": "UNAVAILABLE"); + } + return decompress; +} + +/* Given a path to a Zip file and a toc_entry, return the (uncompressed) + data as a new reference. */ +static PyObject * +get_data(char *archive, PyObject *toc_entry) +{ + PyObject *raw_data, *data = NULL, *decompress; + char *buf; + FILE *fp; + int err, bytes_read = 0; + long l; + char *datapath; + long compress, data_size, file_size, file_offset; + long time, date, crc; + + if (!PyArg_ParseTuple(toc_entry, "slllllll", &datapath, &compress, + &data_size, &file_size, &file_offset, &time, + &date, &crc)) { + return NULL; + } + + fp = fopen(archive, "rb"); + if (!fp) { + PyErr_Format(PyExc_IOError, + "zipimport: can not open file %s", archive); + return NULL; + } + + /* Check to make sure the local file header is correct */ + fseek(fp, file_offset, 0); + l = PyMarshal_ReadLongFromFile(fp); + if (l != 0x04034B50) { + /* Bad: Local File Header */ + PyErr_Format(ZipImportError, + "bad local file header in %s", + archive); + fclose(fp); + return NULL; + } + fseek(fp, file_offset + 26, 0); + l = 30 + PyMarshal_ReadShortFromFile(fp) + + PyMarshal_ReadShortFromFile(fp); /* local header size */ + file_offset += l; /* Start of file data */ + + raw_data = PyString_FromStringAndSize((char *)NULL, compress == 0 ? + data_size : data_size + 1); + if (raw_data == NULL) { + fclose(fp); + return NULL; + } + buf = PyString_AsString(raw_data); + + err = fseek(fp, file_offset, 0); + if (err == 0) + bytes_read = fread(buf, 1, data_size, fp); + fclose(fp); + if (err || bytes_read != data_size) { + PyErr_SetString(PyExc_IOError, + "zipimport: can't read data"); + Py_DECREF(raw_data); + return NULL; + } + + if (compress != 0) { + buf[data_size] = 'Z'; /* saw this in zipfile.py */ + data_size++; + } + buf[data_size] = '\0'; + + if (compress == 0) /* data is not compressed */ + return raw_data; + + /* Decompress with zlib */ + decompress = get_decompress_func(); + if (decompress == NULL) { + PyErr_SetString(ZipImportError, + "can't decompress data; " + "zlib not available"); + goto error; + } + data = PyObject_CallFunction(decompress, "Ol", raw_data, -15); +error: + Py_DECREF(raw_data); + return data; +} + +/* Lenient date/time comparison function. The precision of the mtime + in the archive is lower than the mtime stored in a .pyc: we + must allow a difference of at most one second. */ +static int +eq_mtime(time_t t1, time_t t2) +{ + time_t d = t1 - t2; + if (d < 0) + d = -d; + /* dostime only stores even seconds, so be lenient */ + return d <= 1; +} + +/* Given the contents of a .py[co] file in a buffer, unmarshal the data + and return the code object. Return None if it the magic word doesn't + match (we do this instead of raising an exception as we fall back + to .py if available and we don't want to mask other errors). + Returns a new reference. */ +static PyObject * +unmarshal_code(char *pathname, PyObject *data, time_t mtime) +{ + PyObject *code; + char *buf = PyString_AsString(data); + int size = PyString_Size(data); + + if (size <= 9) { + PyErr_SetString(ZipImportError, + "bad pyc data"); + return NULL; + } + + if (get_long((unsigned char *)buf) != PyImport_GetMagicNumber()) { + if (Py_VerboseFlag) + PySys_WriteStderr("# %s has bad magic\n", + pathname); + Py_INCREF(Py_None); + return Py_None; /* signal caller to try alternative */ + } + + if (mtime != 0 && !eq_mtime(get_long((unsigned char *)buf + 4), + mtime)) { + if (Py_VerboseFlag) + PySys_WriteStderr("# %s has bad mtime\n", + pathname); + Py_INCREF(Py_None); + return Py_None; /* signal caller to try alternative */ + } + + code = PyMarshal_ReadObjectFromString(buf + 8, size - 8); + if (code == NULL) + return NULL; + if (!PyCode_Check(code)) { + Py_DECREF(code); + PyErr_Format(PyExc_TypeError, + "compiled module %.200s is not a code object", + pathname); + return NULL; + } + return code; +} + +/* Replace any occurances of "\r\n?" in the input string with "\n". + This converts DOS and Mac line endings to Unix line endings. + Also append a trailing "\n" to be compatible with + PyParser_SimpleParseFile(). Returns a new reference. */ +static PyObject * +normalize_line_endings(PyObject *source) +{ + char *buf, *q, *p = PyString_AsString(source); + PyObject *fixed_source; + + /* one char extra for trailing \n and one for terminating \0 */ + buf = PyMem_Malloc(PyString_Size(source) + 2); + if (buf == NULL) { + PyErr_SetString(PyExc_MemoryError, + "zipimport: no memory to allocate " + "source buffer"); + return NULL; + } + /* replace "\r\n?" by "\n" */ + for (q = buf; *p != '\0'; p++) { + if (*p == '\r') { + *q++ = '\n'; + if (*(p + 1) == '\n') + p++; + } + else + *q++ = *p; + } + *q++ = '\n'; /* add trailing \n */ + *q = '\0'; + fixed_source = PyString_FromString(buf); + PyMem_Free(buf); + return fixed_source; +} + +/* Given a string buffer containing Python source code, compile it + return and return a code object as a new reference. */ +static PyObject * +compile_source(char *pathname, PyObject *source) +{ + PyObject *code, *fixed_source; + + fixed_source = normalize_line_endings(source); + if (fixed_source == NULL) + return NULL; + + code = Py_CompileString(PyString_AsString(fixed_source), pathname, + Py_file_input); + Py_DECREF(fixed_source); + return code; +} + +/* Convert the date/time values found in the Zip archive to a value + that's compatible with the time stamp stored in .pyc files. */ +static time_t +parse_dostime(int dostime, int dosdate) +{ + struct tm stm; + + stm.tm_sec = (dostime & 0x1f) * 2; + stm.tm_min = (dostime >> 5) & 0x3f; + stm.tm_hour = (dostime >> 11) & 0x1f; + stm.tm_mday = dosdate & 0x1f; + stm.tm_mon = ((dosdate >> 5) & 0x0f) - 1; + stm.tm_year = ((dosdate >> 9) & 0x7f) + 80; + stm.tm_isdst = -1; /* wday/yday is ignored */ + + return mktime(&stm); +} + +/* Given a path to a .pyc or .pyo file in the archive, return the + modifictaion time of the matching .py file, or 0 if no source + is available. */ +static time_t +get_mtime_of_source(ZipImporter *self, char *path) +{ + PyObject *toc_entry; + time_t mtime = 0; + int lastchar = strlen(path) - 1; + char savechar = path[lastchar]; + path[lastchar] = '\0'; /* strip 'c' or 'o' from *.py[co] */ + toc_entry = PyDict_GetItemString(self->files, path); + if (toc_entry != NULL && PyTuple_Check(toc_entry) && + PyTuple_Size(toc_entry) == 8) { + /* fetch the time stamp of the .py file for comparison + with an embedded pyc time stamp */ + int time, date; + time = PyInt_AsLong(PyTuple_GetItem(toc_entry, 5)); + date = PyInt_AsLong(PyTuple_GetItem(toc_entry, 6)); + mtime = parse_dostime(time, date); + } + path[lastchar] = savechar; + return mtime; +} + +/* Return the code object for the module named by 'fullname' from the + Zip archive as a new reference. */ +static PyObject * +get_code_from_data(ZipImporter *self, int ispackage, int isbytecode, + time_t mtime, PyObject *toc_entry) +{ + PyObject *data, *code; + char *modpath; + char *archive = PyString_AsString(self->archive); + + if (archive == NULL) + return NULL; + + data = get_data(archive, toc_entry); + if (data == NULL) + return NULL; + + modpath = PyString_AsString(PyTuple_GetItem(toc_entry, 0)); + + if (isbytecode) { + code = unmarshal_code(modpath, data, mtime); + } + else { + code = compile_source(modpath, data); + } + Py_DECREF(data); + return code; +} + +/* Get the code object assoiciated with the module specified by + 'fullname'. */ +static PyObject * +get_module_code(ZipImporter *self, char *fullname, + int *p_ispackage, char **p_modpath) +{ + PyObject *toc_entry; + char *subname, path[MAXPATHLEN + 1]; + int len; + struct st_zip_searchorder *zso; + + subname = get_subname(fullname); + + len = make_filename(PyString_AsString(self->prefix), subname, path); + if (len < 0) + return NULL; + + for (zso = zip_searchorder; *zso->suffix; zso++) { + PyObject *code = NULL; + + strcpy(path + len, zso->suffix); + if (Py_VerboseFlag > 1) + PySys_WriteStderr("# trying %s%c%s\n", + PyString_AsString(self->archive), + SEP, path); + toc_entry = PyDict_GetItemString(self->files, path); + if (toc_entry != NULL) { + time_t mtime = 0; + int ispackage = zso->type & IS_PACKAGE; + int isbytecode = zso->type & IS_BYTECODE; + + if (isbytecode) + mtime = get_mtime_of_source(self, path); + if (p_ispackage != NULL) + *p_ispackage = ispackage; + code = get_code_from_data(self, ispackage, + isbytecode, mtime, + toc_entry); + if (code == Py_None) { + /* bad magic number or non-matching mtime + in byte code, try next */ + Py_DECREF(code); + continue; + } + if (code != NULL && p_modpath != NULL) + *p_modpath = PyString_AsString( + PyTuple_GetItem(toc_entry, 0)); + return code; + } + } + PyErr_Format(ZipImportError, "can't find module '%.200s'", fullname); + return NULL; +} + + +/* Module init */ + +PyDoc_STRVAR(zipimport_doc, +"zipimport provides support for importing Python modules from Zip archives.\n\ +\n\ +This module exports three objects:\n\ +- zipimporter: a class; its constructor takes a path to a Zip archive.\n\ +- ZipImporterError: exception raised by zipimporter objects. It's a\n\ + subclass of ImportError, so it can be caught as ImportError, too.\n\ +- _zip_directory_cache: a dict, mapping archive paths to zip directory\n\ + info dicts, as used in zipimporter._files.\n\ +\n\ +It is usually not needed to use the zipimport module explicitly; it is\n\ +used by the builtin import mechanism for sys.path items that are paths\n\ +to Zip archives."); + +PyMODINIT_FUNC +initzipimport(void) +{ + PyObject *mod; + + if (PyType_Ready(&ZipImporter_Type) < 0) + return; + + /* Correct directory separator */ + zip_searchorder[0].suffix[0] = SEP; + zip_searchorder[1].suffix[0] = SEP; + zip_searchorder[2].suffix[0] = SEP; + if (Py_OptimizeFlag) { + /* Reverse *.pyc and *.pyo */ + struct st_zip_searchorder tmp; + tmp = zip_searchorder[0]; + zip_searchorder[0] = zip_searchorder[1]; + zip_searchorder[1] = tmp; + tmp = zip_searchorder[3]; + zip_searchorder[3] = zip_searchorder[4]; + zip_searchorder[4] = tmp; + } + + mod = Py_InitModule4("zipimport", NULL, zipimport_doc, + NULL, PYTHON_API_VERSION); + + ZipImportError = PyErr_NewException("zipimport.ZipImportError", + PyExc_ImportError, NULL); + if (ZipImportError == NULL) + return; + + Py_INCREF(ZipImportError); + if (PyModule_AddObject(mod, "ZipImportError", + ZipImportError) < 0) + return; + + Py_INCREF(&ZipImporter_Type); + if (PyModule_AddObject(mod, "zipimporter", + (PyObject *)&ZipImporter_Type) < 0) + return; + + zip_directory_cache = PyDict_New(); + if (zip_directory_cache == NULL) + return; + Py_INCREF(zip_directory_cache); + if (PyModule_AddObject(mod, "_zip_directory_cache", + zip_directory_cache) < 0) + return; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/zlibmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/zlibmodule.c new file mode 100644 index 00000000..90ab2f25 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Modules/zlibmodule.c @@ -0,0 +1,907 @@ +/* zlibmodule.c -- gzip-compatible data compression */ +/* See http://www.gzip.org/zlib/ */ + +/* Windows users: read Python's PCbuild\readme.txt */ + + +#include "Python.h" +#include "zlib.h" + +#ifdef WITH_THREAD +#include "pythread.h" + +/* #defs ripped off from _tkinter.c, even though the situation here is much + simpler, because we don't have to worry about waiting for Tcl + events! And, since zlib itself is threadsafe, we don't need to worry + about re-entering zlib functions. + + N.B. + + Since ENTER_ZLIB and LEAVE_ZLIB only need to be called on functions + that modify the components of preexisting de/compress objects, it + could prove to be a performance gain on multiprocessor machines if + there was an de/compress object-specific lock. However, for the + moment the ENTER_ZLIB and LEAVE_ZLIB calls are global for ALL + de/compress objects. + */ + +static PyThread_type_lock zlib_lock = NULL; /* initialized on module load */ + +#define ENTER_ZLIB \ + Py_BEGIN_ALLOW_THREADS \ + PyThread_acquire_lock(zlib_lock, 1); \ + Py_END_ALLOW_THREADS + +#define LEAVE_ZLIB \ + PyThread_release_lock(zlib_lock); + +#else + +#define ENTER_ZLIB +#define LEAVE_ZLIB + +#endif + +/* The following parameters are copied from zutil.h, version 0.95 */ +#define DEFLATED 8 +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#define DEF_WBITS MAX_WBITS + +/* The output buffer will be increased in chunks of DEFAULTALLOC bytes. */ +#define DEFAULTALLOC (16*1024) +#define PyInit_zlib initzlib + +static PyTypeObject Comptype; +static PyTypeObject Decomptype; + +static PyObject *ZlibError; + +typedef struct +{ + PyObject_HEAD + z_stream zst; + PyObject *unused_data; + PyObject *unconsumed_tail; + int is_initialised; +} compobject; + +static void +zlib_error(z_stream zst, int err, char *msg) +{ + if (zst.msg == Z_NULL) + PyErr_Format(ZlibError, "Error %d %s", err, msg); + else + PyErr_Format(ZlibError, "Error %d %s: %.200s", err, msg, zst.msg); +} + +PyDoc_STRVAR(compressobj__doc__, +"compressobj([level]) -- Return a compressor object.\n" +"\n" +"Optional arg level is the compression level, in 1-9."); + +PyDoc_STRVAR(decompressobj__doc__, +"decompressobj([wbits]) -- Return a decompressor object.\n" +"\n" +"Optional arg wbits is the window buffer size."); + +static compobject * +newcompobject(PyTypeObject *type) +{ + compobject *self; + self = PyObject_New(compobject, type); + if (self == NULL) + return NULL; + self->is_initialised = 0; + self->unused_data = PyString_FromString(""); + if (self->unused_data == NULL) { + Py_DECREF(self); + return NULL; + } + self->unconsumed_tail = PyString_FromString(""); + if (self->unconsumed_tail == NULL) { + Py_DECREF(self); + return NULL; + } + return self; +} + +PyDoc_STRVAR(compress__doc__, +"compress(string[, level]) -- Returned compressed string.\n" +"\n" +"Optional arg level is the compression level, in 1-9."); + +static PyObject * +PyZlib_compress(PyObject *self, PyObject *args) +{ + PyObject *ReturnVal = NULL; + Byte *input, *output; + int length, level=Z_DEFAULT_COMPRESSION, err; + z_stream zst; + + /* require Python string object, optional 'level' arg */ + if (!PyArg_ParseTuple(args, "s#|i:compress", &input, &length, &level)) + return NULL; + + zst.avail_out = length + length/1000 + 12 + 1; + + output = (Byte*)malloc(zst.avail_out); + if (output == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Can't allocate memory to compress data"); + return NULL; + } + + /* Past the point of no return. From here on out, we need to make sure + we clean up mallocs & INCREFs. */ + + zst.zalloc = (alloc_func)NULL; + zst.zfree = (free_func)Z_NULL; + zst.next_out = (Byte *)output; + zst.next_in = (Byte *)input; + zst.avail_in = length; + err = deflateInit(&zst, level); + + switch(err) { + case(Z_OK): + break; + case(Z_MEM_ERROR): + PyErr_SetString(PyExc_MemoryError, + "Out of memory while compressing data"); + goto error; + case(Z_STREAM_ERROR): + PyErr_SetString(ZlibError, + "Bad compression level"); + goto error; + default: + deflateEnd(&zst); + zlib_error(zst, err, "while compressing data"); + goto error; + } + + Py_BEGIN_ALLOW_THREADS; + err = deflate(&zst, Z_FINISH); + Py_END_ALLOW_THREADS; + + if (err != Z_STREAM_END) { + zlib_error(zst, err, "while compressing data"); + deflateEnd(&zst); + goto error; + } + + err=deflateEnd(&zst); + if (err == Z_OK) + ReturnVal = PyString_FromStringAndSize((char *)output, + zst.total_out); + else + zlib_error(zst, err, "while finishing compression"); + + error: + free(output); + + return ReturnVal; +} + +PyDoc_STRVAR(decompress__doc__, +"decompress(string[, wbits[, bufsize]]) -- Return decompressed string.\n" +"\n" +"Optional arg wbits is the window buffer size. Optional arg bufsize is\n" +"the initial output buffer size."); + +static PyObject * +PyZlib_decompress(PyObject *self, PyObject *args) +{ + PyObject *result_str; + Byte *input; + int length, err; + int wsize=DEF_WBITS, r_strlen=DEFAULTALLOC; + z_stream zst; + + if (!PyArg_ParseTuple(args, "s#|ii:decompress", + &input, &length, &wsize, &r_strlen)) + return NULL; + + if (r_strlen <= 0) + r_strlen = 1; + + zst.avail_in = length; + zst.avail_out = r_strlen; + + if (!(result_str = PyString_FromStringAndSize(NULL, r_strlen))) + return NULL; + + zst.zalloc = (alloc_func)NULL; + zst.zfree = (free_func)Z_NULL; + zst.next_out = (Byte *)PyString_AS_STRING(result_str); + zst.next_in = (Byte *)input; + err = inflateInit2(&zst, wsize); + + switch(err) { + case(Z_OK): + break; + case(Z_MEM_ERROR): + PyErr_SetString(PyExc_MemoryError, + "Out of memory while decompressing data"); + goto error; + default: + inflateEnd(&zst); + zlib_error(zst, err, "while preparing to decompress data"); + goto error; + } + + do { + Py_BEGIN_ALLOW_THREADS + err=inflate(&zst, Z_FINISH); + Py_END_ALLOW_THREADS + + switch(err) { + case(Z_STREAM_END): + break; + case(Z_BUF_ERROR): + /* + * If there is at least 1 byte of room according to zst.avail_out + * and we get this error, assume that it means zlib cannot + * process the inflate call() due to an error in the data. + */ + if (zst.avail_out > 0) { + PyErr_Format(ZlibError, "Error %i while decompressing data", + err); + inflateEnd(&zst); + goto error; + } + /* fall through */ + case(Z_OK): + /* need more memory */ + if (_PyString_Resize(&result_str, r_strlen << 1) < 0) { + inflateEnd(&zst); + goto error; + } + zst.next_out = (unsigned char *)PyString_AS_STRING(result_str) \ + + r_strlen; + zst.avail_out = r_strlen; + r_strlen = r_strlen << 1; + break; + default: + inflateEnd(&zst); + zlib_error(zst, err, "while decompressing data"); + goto error; + } + } while (err != Z_STREAM_END); + + err = inflateEnd(&zst); + if (err != Z_OK) { + zlib_error(zst, err, "while finishing data decompression"); + goto error; + } + + _PyString_Resize(&result_str, zst.total_out); + return result_str; + + error: + Py_XDECREF(result_str); + return NULL; +} + +static PyObject * +PyZlib_compressobj(PyObject *selfptr, PyObject *args) +{ + compobject *self; + int level=Z_DEFAULT_COMPRESSION, method=DEFLATED; + int wbits=MAX_WBITS, memLevel=DEF_MEM_LEVEL, strategy=0, err; + + if (!PyArg_ParseTuple(args, "|iiiii:compressobj", &level, &method, &wbits, + &memLevel, &strategy)) + return NULL; + + self = newcompobject(&Comptype); + if (self==NULL) + return(NULL); + self->zst.zalloc = (alloc_func)NULL; + self->zst.zfree = (free_func)Z_NULL; + err = deflateInit2(&self->zst, level, method, wbits, memLevel, strategy); + switch(err) { + case (Z_OK): + self->is_initialised = 1; + return (PyObject*)self; + case (Z_MEM_ERROR): + Py_DECREF(self); + PyErr_SetString(PyExc_MemoryError, + "Can't allocate memory for compression object"); + return NULL; + case(Z_STREAM_ERROR): + Py_DECREF(self); + PyErr_SetString(PyExc_ValueError, "Invalid initialization option"); + return NULL; + default: + zlib_error(self->zst, err, "while creating compression object"); + Py_DECREF(self); + return NULL; + } +} + +static PyObject * +PyZlib_decompressobj(PyObject *selfptr, PyObject *args) +{ + int wbits=DEF_WBITS, err; + compobject *self; + if (!PyArg_ParseTuple(args, "|i:decompressobj", &wbits)) + return NULL; + + self = newcompobject(&Decomptype); + if (self == NULL) + return(NULL); + self->zst.zalloc = (alloc_func)NULL; + self->zst.zfree = (free_func)Z_NULL; + err = inflateInit2(&self->zst, wbits); + switch(err) { + case (Z_OK): + self->is_initialised = 1; + return (PyObject*)self; + case(Z_STREAM_ERROR): + Py_DECREF(self); + PyErr_SetString(PyExc_ValueError, "Invalid initialization option"); + return NULL; + case (Z_MEM_ERROR): + Py_DECREF(self); + PyErr_SetString(PyExc_MemoryError, + "Can't allocate memory for decompression object"); + return NULL; + default: + zlib_error(self->zst, err, "while creating decompression object"); + Py_DECREF(self); + return NULL; + } +} + +static void +Comp_dealloc(compobject *self) +{ + if (self->is_initialised) + deflateEnd(&self->zst); + Py_XDECREF(self->unused_data); + Py_XDECREF(self->unconsumed_tail); + PyObject_Del(self); +} + +static void +Decomp_dealloc(compobject *self) +{ + if (self->is_initialised) + inflateEnd(&self->zst); + Py_XDECREF(self->unused_data); + Py_XDECREF(self->unconsumed_tail); + PyObject_Del(self); +} + +PyDoc_STRVAR(comp_compress__doc__, +"compress(data) -- Return a string containing data compressed.\n" +"\n" +"After calling this function, some of the input data may still\n" +"be stored in internal buffers for later processing.\n" +"Call the flush() method to clear these buffers."); + + +static PyObject * +PyZlib_objcompress(compobject *self, PyObject *args) +{ + int err, inplen, length = DEFAULTALLOC; + PyObject *RetVal; + Byte *input; + unsigned long start_total_out; + + if (!PyArg_ParseTuple(args, "s#:compress", &input, &inplen)) + return NULL; + + if (!(RetVal = PyString_FromStringAndSize(NULL, length))) + return NULL; + + ENTER_ZLIB + + start_total_out = self->zst.total_out; + self->zst.avail_in = inplen; + self->zst.next_in = input; + self->zst.avail_out = length; + self->zst.next_out = (unsigned char *)PyString_AS_STRING(RetVal); + + Py_BEGIN_ALLOW_THREADS + err = deflate(&(self->zst), Z_NO_FLUSH); + Py_END_ALLOW_THREADS + + /* while Z_OK and the output buffer is full, there might be more output, + so extend the output buffer and try again */ + while (err == Z_OK && self->zst.avail_out == 0) { + if (_PyString_Resize(&RetVal, length << 1) < 0) + goto error; + self->zst.next_out = (unsigned char *)PyString_AS_STRING(RetVal) \ + + length; + self->zst.avail_out = length; + length = length << 1; + + Py_BEGIN_ALLOW_THREADS + err = deflate(&(self->zst), Z_NO_FLUSH); + Py_END_ALLOW_THREADS + } + /* We will only get Z_BUF_ERROR if the output buffer was full but + there wasn't more output when we tried again, so it is not an error + condition. + */ + + if (err != Z_OK && err != Z_BUF_ERROR) { + zlib_error(self->zst, err, "while compressing"); + Py_DECREF(RetVal); + RetVal = NULL; + goto error; + } + _PyString_Resize(&RetVal, self->zst.total_out - start_total_out); + + error: + LEAVE_ZLIB + return RetVal; +} + +PyDoc_STRVAR(decomp_decompress__doc__, +"decompress(data, max_length) -- Return a string containing the decompressed\n" +"version of the data.\n" +"\n" +"After calling this function, some of the input data may still be stored in\n" +"internal buffers for later processing.\n" +"Call the flush() method to clear these buffers.\n" +"If the max_length parameter is specified then the return value will be\n" +"no longer than max_length. Unconsumed input data will be stored in\n" +"the unconsumed_tail attribute."); + +static PyObject * +PyZlib_objdecompress(compobject *self, PyObject *args) +{ + int err, inplen, old_length, length = DEFAULTALLOC; + int max_length = 0; + PyObject *RetVal; + Byte *input; + unsigned long start_total_out; + + if (!PyArg_ParseTuple(args, "s#|i:decompress", &input, + &inplen, &max_length)) + return NULL; + if (max_length < 0) { + PyErr_SetString(PyExc_ValueError, + "max_length must be greater than zero"); + return NULL; + } + + /* limit amount of data allocated to max_length */ + if (max_length && length > max_length) + length = max_length; + if (!(RetVal = PyString_FromStringAndSize(NULL, length))) + return NULL; + + ENTER_ZLIB + + start_total_out = self->zst.total_out; + self->zst.avail_in = inplen; + self->zst.next_in = input; + self->zst.avail_out = length; + self->zst.next_out = (unsigned char *)PyString_AS_STRING(RetVal); + + Py_BEGIN_ALLOW_THREADS + err = inflate(&(self->zst), Z_SYNC_FLUSH); + Py_END_ALLOW_THREADS + + /* While Z_OK and the output buffer is full, there might be more output. + So extend the output buffer and try again. + */ + while (err == Z_OK && self->zst.avail_out == 0) { + /* If max_length set, don't continue decompressing if we've already + reached the limit. + */ + if (max_length && length >= max_length) + break; + + /* otherwise, ... */ + old_length = length; + length = length << 1; + if (max_length && length > max_length) + length = max_length; + + if (_PyString_Resize(&RetVal, length) < 0) + goto error; + self->zst.next_out = (unsigned char *)PyString_AS_STRING(RetVal) \ + + old_length; + self->zst.avail_out = length - old_length; + + Py_BEGIN_ALLOW_THREADS + err = inflate(&(self->zst), Z_SYNC_FLUSH); + Py_END_ALLOW_THREADS + } + + /* Not all of the compressed data could be accomodated in the output buffer + of specified size. Return the unconsumed tail in an attribute.*/ + if(max_length) { + Py_DECREF(self->unconsumed_tail); + self->unconsumed_tail = PyString_FromStringAndSize((char *)self->zst.next_in, + self->zst.avail_in); + if(!self->unconsumed_tail) { + Py_DECREF(RetVal); + RetVal = NULL; + goto error; + } + } + + /* The end of the compressed data has been reached, so set the + unused_data attribute to a string containing the remainder of the + data in the string. Note that this is also a logical place to call + inflateEnd, but the old behaviour of only calling it on flush() is + preserved. + */ + if (err == Z_STREAM_END) { + Py_XDECREF(self->unused_data); /* Free original empty string */ + self->unused_data = PyString_FromStringAndSize( + (char *)self->zst.next_in, self->zst.avail_in); + if (self->unused_data == NULL) { + Py_DECREF(RetVal); + goto error; + } + /* We will only get Z_BUF_ERROR if the output buffer was full + but there wasn't more output when we tried again, so it is + not an error condition. + */ + } else if (err != Z_OK && err != Z_BUF_ERROR) { + zlib_error(self->zst, err, "while decompressing"); + Py_DECREF(RetVal); + RetVal = NULL; + goto error; + } + + _PyString_Resize(&RetVal, self->zst.total_out - start_total_out); + + error: + LEAVE_ZLIB + + return RetVal; +} + +PyDoc_STRVAR(comp_flush__doc__, +"flush( [mode] ) -- Return a string containing any remaining compressed data.\n" +"\n" +"mode can be one of the constants Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH; the\n" +"default value used when mode is not specified is Z_FINISH.\n" +"If mode == Z_FINISH, the compressor object can no longer be used after\n" +"calling the flush() method. Otherwise, more data can still be compressed."); + +static PyObject * +PyZlib_flush(compobject *self, PyObject *args) +{ + int err, length = DEFAULTALLOC; + PyObject *RetVal; + int flushmode = Z_FINISH; + unsigned long start_total_out; + + if (!PyArg_ParseTuple(args, "|i:flush", &flushmode)) + return NULL; + + /* Flushing with Z_NO_FLUSH is a no-op, so there's no point in + doing any work at all; just return an empty string. */ + if (flushmode == Z_NO_FLUSH) { + return PyString_FromStringAndSize(NULL, 0); + } + + if (!(RetVal = PyString_FromStringAndSize(NULL, length))) + return NULL; + + ENTER_ZLIB + + start_total_out = self->zst.total_out; + self->zst.avail_in = 0; + self->zst.avail_out = length; + self->zst.next_out = (unsigned char *)PyString_AS_STRING(RetVal); + + Py_BEGIN_ALLOW_THREADS + err = deflate(&(self->zst), flushmode); + Py_END_ALLOW_THREADS + + /* while Z_OK and the output buffer is full, there might be more output, + so extend the output buffer and try again */ + while (err == Z_OK && self->zst.avail_out == 0) { + if (_PyString_Resize(&RetVal, length << 1) < 0) + goto error; + self->zst.next_out = (unsigned char *)PyString_AS_STRING(RetVal) \ + + length; + self->zst.avail_out = length; + length = length << 1; + + Py_BEGIN_ALLOW_THREADS + err = deflate(&(self->zst), flushmode); + Py_END_ALLOW_THREADS + } + + /* If flushmode is Z_FINISH, we also have to call deflateEnd() to free + various data structures. Note we should only get Z_STREAM_END when + flushmode is Z_FINISH, but checking both for safety*/ + if (err == Z_STREAM_END && flushmode == Z_FINISH) { + err = deflateEnd(&(self->zst)); + if (err != Z_OK) { + zlib_error(self->zst, err, "from deflateEnd()"); + Py_DECREF(RetVal); + RetVal = NULL; + goto error; + } + else + self->is_initialised = 0; + + /* We will only get Z_BUF_ERROR if the output buffer was full + but there wasn't more output when we tried again, so it is + not an error condition. + */ + } else if (err!=Z_OK && err!=Z_BUF_ERROR) { + zlib_error(self->zst, err, "while flushing"); + Py_DECREF(RetVal); + RetVal = NULL; + goto error; + } + + _PyString_Resize(&RetVal, self->zst.total_out - start_total_out); + + error: + LEAVE_ZLIB + + return RetVal; +} + +PyDoc_STRVAR(decomp_flush__doc__, +"flush() -- Return a string containing any remaining decompressed data.\n" +"\n" +"The decompressor object can no longer be used after this call."); + +static PyObject * +PyZlib_unflush(compobject *self, PyObject *args) +{ + int err, length = DEFAULTALLOC; + PyObject * retval = NULL; + unsigned long start_total_out; + + if (!PyArg_ParseTuple(args, "|i:flush", &length)) + return NULL; + if (!(retval = PyString_FromStringAndSize(NULL, length))) + return NULL; + + + ENTER_ZLIB + + start_total_out = self->zst.total_out; + self->zst.avail_out = length; + self->zst.next_out = (Byte *)PyString_AS_STRING(retval); + + Py_BEGIN_ALLOW_THREADS + err = inflate(&(self->zst), Z_FINISH); + Py_END_ALLOW_THREADS + + /* while Z_OK and the output buffer is full, there might be more output, + so extend the output buffer and try again */ + while ((err == Z_OK || err == Z_BUF_ERROR) && self->zst.avail_out == 0) { + if (_PyString_Resize(&retval, length << 1) < 0) + goto error; + self->zst.next_out = (Byte *)PyString_AS_STRING(retval) + length; + self->zst.avail_out = length; + length = length << 1; + + Py_BEGIN_ALLOW_THREADS + err = inflate(&(self->zst), Z_FINISH); + Py_END_ALLOW_THREADS + } + + /* If flushmode is Z_FINISH, we also have to call deflateEnd() to free + various data structures. Note we should only get Z_STREAM_END when + flushmode is Z_FINISH */ + if (err == Z_STREAM_END) { + err = inflateEnd(&(self->zst)); + self->is_initialised = 0; + if (err != Z_OK) { + zlib_error(self->zst, err, "from inflateEnd()"); + Py_DECREF(retval); + retval = NULL; + goto error; + } + } + _PyString_Resize(&retval, self->zst.total_out - start_total_out); + +error: + + LEAVE_ZLIB + + return retval; +} + +static PyMethodDef comp_methods[] = +{ + {"compress", (binaryfunc)PyZlib_objcompress, METH_VARARGS, + comp_compress__doc__}, + {"flush", (binaryfunc)PyZlib_flush, METH_VARARGS, + comp_flush__doc__}, + {NULL, NULL} +}; + +static PyMethodDef Decomp_methods[] = +{ + {"decompress", (binaryfunc)PyZlib_objdecompress, METH_VARARGS, + decomp_decompress__doc__}, + {"flush", (binaryfunc)PyZlib_unflush, METH_VARARGS, + decomp_flush__doc__}, + {NULL, NULL} +}; + +static PyObject * +Comp_getattr(compobject *self, char *name) +{ + /* No ENTER/LEAVE_ZLIB is necessary because this fn doesn't touch + internal data. */ + + return Py_FindMethod(comp_methods, (PyObject *)self, name); +} + +static PyObject * +Decomp_getattr(compobject *self, char *name) +{ + PyObject * retval; + + ENTER_ZLIB + + if (strcmp(name, "unused_data") == 0) { + Py_INCREF(self->unused_data); + retval = self->unused_data; + } else if (strcmp(name, "unconsumed_tail") == 0) { + Py_INCREF(self->unconsumed_tail); + retval = self->unconsumed_tail; + } else + retval = Py_FindMethod(Decomp_methods, (PyObject *)self, name); + + LEAVE_ZLIB + + return retval; +} + +PyDoc_STRVAR(adler32__doc__, +"adler32(string[, start]) -- Compute an Adler-32 checksum of string.\n" +"\n" +"An optional starting value can be specified. The returned checksum is\n" +"an integer."); + +static PyObject * +PyZlib_adler32(PyObject *self, PyObject *args) +{ + uLong adler32val = adler32(0L, Z_NULL, 0); + Byte *buf; + int len; + + if (!PyArg_ParseTuple(args, "s#|l:adler32", &buf, &len, &adler32val)) + return NULL; + adler32val = adler32(adler32val, buf, len); + return PyInt_FromLong(adler32val); +} + +PyDoc_STRVAR(crc32__doc__, +"crc32(string[, start]) -- Compute a CRC-32 checksum of string.\n" +"\n" +"An optional starting value can be specified. The returned checksum is\n" +"an integer."); + +static PyObject * +PyZlib_crc32(PyObject *self, PyObject *args) +{ + uLong crc32val = crc32(0L, Z_NULL, 0); + Byte *buf; + int len; + if (!PyArg_ParseTuple(args, "s#|l:crc32", &buf, &len, &crc32val)) + return NULL; + crc32val = crc32(crc32val, buf, len); + return PyInt_FromLong(crc32val); +} + + +static PyMethodDef zlib_methods[] = +{ + {"adler32", (PyCFunction)PyZlib_adler32, METH_VARARGS, + adler32__doc__}, + {"compress", (PyCFunction)PyZlib_compress, METH_VARARGS, + compress__doc__}, + {"compressobj", (PyCFunction)PyZlib_compressobj, METH_VARARGS, + compressobj__doc__}, + {"crc32", (PyCFunction)PyZlib_crc32, METH_VARARGS, + crc32__doc__}, + {"decompress", (PyCFunction)PyZlib_decompress, METH_VARARGS, + decompress__doc__}, + {"decompressobj", (PyCFunction)PyZlib_decompressobj, METH_VARARGS, + decompressobj__doc__}, + {NULL, NULL} +}; + +static PyTypeObject Comptype = { + PyObject_HEAD_INIT(0) + 0, + "zlib.Compress", + sizeof(compobject), + 0, + (destructor)Comp_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)Comp_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ +}; + +static PyTypeObject Decomptype = { + PyObject_HEAD_INIT(0) + 0, + "zlib.Decompress", + sizeof(compobject), + 0, + (destructor)Decomp_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)Decomp_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ +}; + +PyDoc_STRVAR(zlib_module_documentation, +"The functions in this module allow compression and decompression using the\n" +"zlib library, which is based on GNU zip.\n" +"\n" +"adler32(string[, start]) -- Compute an Adler-32 checksum.\n" +"compress(string[, level]) -- Compress string, with compression level in 1-9.\n" +"compressobj([level]) -- Return a compressor object.\n" +"crc32(string[, start]) -- Compute a CRC-32 checksum.\n" +"decompress(string,[wbits],[bufsize]) -- Decompresses a compressed string.\n" +"decompressobj([wbits]) -- Return a decompressor object.\n" +"\n" +"'wbits' is window buffer size.\n" +"Compressor objects support compress() and flush() methods; decompressor\n" +"objects support decompress() and flush()."); + +PyMODINIT_FUNC +PyInit_zlib(void) +{ + PyObject *m, *ver; + Comptype.ob_type = &PyType_Type; + Decomptype.ob_type = &PyType_Type; + m = Py_InitModule4("zlib", zlib_methods, + zlib_module_documentation, + (PyObject*)NULL,PYTHON_API_VERSION); + + ZlibError = PyErr_NewException("zlib.error", NULL, NULL); + if (ZlibError != NULL) { + Py_INCREF(ZlibError); + PyModule_AddObject(m, "error", ZlibError); + } + PyModule_AddIntConstant(m, "MAX_WBITS", MAX_WBITS); + PyModule_AddIntConstant(m, "DEFLATED", DEFLATED); + PyModule_AddIntConstant(m, "DEF_MEM_LEVEL", DEF_MEM_LEVEL); + PyModule_AddIntConstant(m, "Z_BEST_SPEED", Z_BEST_SPEED); + PyModule_AddIntConstant(m, "Z_BEST_COMPRESSION", Z_BEST_COMPRESSION); + PyModule_AddIntConstant(m, "Z_DEFAULT_COMPRESSION", Z_DEFAULT_COMPRESSION); + PyModule_AddIntConstant(m, "Z_FILTERED", Z_FILTERED); + PyModule_AddIntConstant(m, "Z_HUFFMAN_ONLY", Z_HUFFMAN_ONLY); + PyModule_AddIntConstant(m, "Z_DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY); + + PyModule_AddIntConstant(m, "Z_FINISH", Z_FINISH); + PyModule_AddIntConstant(m, "Z_NO_FLUSH", Z_NO_FLUSH); + PyModule_AddIntConstant(m, "Z_SYNC_FLUSH", Z_SYNC_FLUSH); + PyModule_AddIntConstant(m, "Z_FULL_FLUSH", Z_FULL_FLUSH); + + ver = PyString_FromString(ZLIB_VERSION); + if (ver != NULL) + PyModule_AddObject(m, "ZLIB_VERSION", ver); + + PyModule_AddStringConstant(m, "__version__", "1.0"); + +#ifdef WITH_THREAD + zlib_lock = PyThread_allocate_lock(); +#endif /* WITH_THREAD */ +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/.cvsignore b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/.cvsignore new file mode 100644 index 00000000..6ea1ddf4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/.cvsignore @@ -0,0 +1,2 @@ +add2lib +Makefile diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/abstract.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/abstract.c new file mode 100644 index 00000000..c9055ac8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/abstract.c @@ -0,0 +1,2192 @@ +/* Abstract Object Interface (many thanks to Jim Fulton) */ + +#include "Python.h" +#include +#include "structmember.h" /* we need the offsetof() macro from there */ +#include "longintrepr.h" + +#define NEW_STYLE_NUMBER(o) PyType_HasFeature((o)->ob_type, \ + Py_TPFLAGS_CHECKTYPES) + +/* Shorthands to return certain errors */ + +static PyObject * +type_error(const char *msg) +{ + PyErr_SetString(PyExc_TypeError, msg); + return NULL; +} + +static PyObject * +null_error(void) +{ + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_SystemError, + "null argument to internal routine"); + return NULL; +} + +/* Operations on any object */ + +int +PyObject_Cmp(PyObject *o1, PyObject *o2, int *result) +{ + int r; + + if (o1 == NULL || o2 == NULL) { + null_error(); + return -1; + } + r = PyObject_Compare(o1, o2); + if (PyErr_Occurred()) + return -1; + *result = r; + return 0; +} + +PyObject * +PyObject_Type(PyObject *o) +{ + PyObject *v; + + if (o == NULL) + return null_error(); + v = (PyObject *)o->ob_type; + Py_INCREF(v); + return v; +} + +int +PyObject_Size(PyObject *o) +{ + PySequenceMethods *m; + + if (o == NULL) { + null_error(); + return -1; + } + + m = o->ob_type->tp_as_sequence; + if (m && m->sq_length) + return m->sq_length(o); + + return PyMapping_Size(o); +} + +#undef PyObject_Length +int +PyObject_Length(PyObject *o) +{ + return PyObject_Size(o); +} +#define PyObject_Length PyObject_Size + +PyObject * +PyObject_GetItem(PyObject *o, PyObject *key) +{ + PyMappingMethods *m; + + if (o == NULL || key == NULL) + return null_error(); + + m = o->ob_type->tp_as_mapping; + if (m && m->mp_subscript) + return m->mp_subscript(o, key); + + if (o->ob_type->tp_as_sequence) { + if (PyInt_Check(key)) + return PySequence_GetItem(o, PyInt_AsLong(key)); + else if (PyLong_Check(key)) { + long key_value = PyLong_AsLong(key); + if (key_value == -1 && PyErr_Occurred()) + return NULL; + return PySequence_GetItem(o, key_value); + } + else if (o->ob_type->tp_as_sequence->sq_item) + return type_error("sequence index must be integer"); + } + + return type_error("unsubscriptable object"); +} + +int +PyObject_SetItem(PyObject *o, PyObject *key, PyObject *value) +{ + PyMappingMethods *m; + + if (o == NULL || key == NULL || value == NULL) { + null_error(); + return -1; + } + m = o->ob_type->tp_as_mapping; + if (m && m->mp_ass_subscript) + return m->mp_ass_subscript(o, key, value); + + if (o->ob_type->tp_as_sequence) { + if (PyInt_Check(key)) + return PySequence_SetItem(o, PyInt_AsLong(key), value); + else if (PyLong_Check(key)) { + long key_value = PyLong_AsLong(key); + if (key_value == -1 && PyErr_Occurred()) + return -1; + return PySequence_SetItem(o, key_value, value); + } + else if (o->ob_type->tp_as_sequence->sq_ass_item) { + type_error("sequence index must be integer"); + return -1; + } + } + + type_error("object does not support item assignment"); + return -1; +} + +int +PyObject_DelItem(PyObject *o, PyObject *key) +{ + PyMappingMethods *m; + + if (o == NULL || key == NULL) { + null_error(); + return -1; + } + m = o->ob_type->tp_as_mapping; + if (m && m->mp_ass_subscript) + return m->mp_ass_subscript(o, key, (PyObject*)NULL); + + if (o->ob_type->tp_as_sequence) { + if (PyInt_Check(key)) + return PySequence_DelItem(o, PyInt_AsLong(key)); + else if (PyLong_Check(key)) { + long key_value = PyLong_AsLong(key); + if (key_value == -1 && PyErr_Occurred()) + return -1; + return PySequence_DelItem(o, key_value); + } + else if (o->ob_type->tp_as_sequence->sq_ass_item) { + type_error("sequence index must be integer"); + return -1; + } + } + + type_error("object does not support item deletion"); + return -1; +} + +int +PyObject_DelItemString(PyObject *o, char *key) +{ + PyObject *okey; + int ret; + + if (o == NULL || key == NULL) { + null_error(); + return -1; + } + okey = PyString_FromString(key); + if (okey == NULL) + return -1; + ret = PyObject_DelItem(o, okey); + Py_DECREF(okey); + return ret; +} + +int PyObject_AsCharBuffer(PyObject *obj, + const char **buffer, + int *buffer_len) +{ + PyBufferProcs *pb; + const char *pp; + int len; + + if (obj == NULL || buffer == NULL || buffer_len == NULL) { + null_error(); + return -1; + } + pb = obj->ob_type->tp_as_buffer; + if (pb == NULL || + pb->bf_getcharbuffer == NULL || + pb->bf_getsegcount == NULL) { + PyErr_SetString(PyExc_TypeError, + "expected a character buffer object"); + return -1; + } + if ((*pb->bf_getsegcount)(obj,NULL) != 1) { + PyErr_SetString(PyExc_TypeError, + "expected a single-segment buffer object"); + return -1; + } + len = (*pb->bf_getcharbuffer)(obj, 0, &pp); + if (len < 0) + return -1; + *buffer = pp; + *buffer_len = len; + return 0; +} + +int +PyObject_CheckReadBuffer(PyObject *obj) +{ + PyBufferProcs *pb = obj->ob_type->tp_as_buffer; + + if (pb == NULL || + pb->bf_getreadbuffer == NULL || + pb->bf_getsegcount == NULL || + (*pb->bf_getsegcount)(obj, NULL) != 1) + return 0; + return 1; +} + +int PyObject_AsReadBuffer(PyObject *obj, + const void **buffer, + int *buffer_len) +{ + PyBufferProcs *pb; + void *pp; + int len; + + if (obj == NULL || buffer == NULL || buffer_len == NULL) { + null_error(); + return -1; + } + pb = obj->ob_type->tp_as_buffer; + if (pb == NULL || + pb->bf_getreadbuffer == NULL || + pb->bf_getsegcount == NULL) { + PyErr_SetString(PyExc_TypeError, + "expected a readable buffer object"); + return -1; + } + if ((*pb->bf_getsegcount)(obj, NULL) != 1) { + PyErr_SetString(PyExc_TypeError, + "expected a single-segment buffer object"); + return -1; + } + len = (*pb->bf_getreadbuffer)(obj, 0, &pp); + if (len < 0) + return -1; + *buffer = pp; + *buffer_len = len; + return 0; +} + +int PyObject_AsWriteBuffer(PyObject *obj, + void **buffer, + int *buffer_len) +{ + PyBufferProcs *pb; + void*pp; + int len; + + if (obj == NULL || buffer == NULL || buffer_len == NULL) { + null_error(); + return -1; + } + pb = obj->ob_type->tp_as_buffer; + if (pb == NULL || + pb->bf_getwritebuffer == NULL || + pb->bf_getsegcount == NULL) { + PyErr_SetString(PyExc_TypeError, + "expected a writeable buffer object"); + return -1; + } + if ((*pb->bf_getsegcount)(obj, NULL) != 1) { + PyErr_SetString(PyExc_TypeError, + "expected a single-segment buffer object"); + return -1; + } + len = (*pb->bf_getwritebuffer)(obj,0,&pp); + if (len < 0) + return -1; + *buffer = pp; + *buffer_len = len; + return 0; +} + +/* Operations on numbers */ + +int +PyNumber_Check(PyObject *o) +{ + return o && o->ob_type->tp_as_number && + (o->ob_type->tp_as_number->nb_int || + o->ob_type->tp_as_number->nb_float); +} + +/* Binary operators */ + +/* New style number protocol support */ + +#define NB_SLOT(x) offsetof(PyNumberMethods, x) +#define NB_BINOP(nb_methods, slot) \ + (*(binaryfunc*)(& ((char*)nb_methods)[slot])) +#define NB_TERNOP(nb_methods, slot) \ + (*(ternaryfunc*)(& ((char*)nb_methods)[slot])) + +/* + Calling scheme used for binary operations: + + v w Action + ------------------------------------------------------------------- + new new w.op(v,w)[*], v.op(v,w), w.op(v,w) + new old v.op(v,w), coerce(v,w), v.op(v,w) + old new w.op(v,w), coerce(v,w), v.op(v,w) + old old coerce(v,w), v.op(v,w) + + [*] only when v->ob_type != w->ob_type && w->ob_type is a subclass of + v->ob_type + + Legend: + ------- + * new == new style number + * old == old style number + * Action indicates the order in which operations are tried until either + a valid result is produced or an error occurs. + + */ + +static PyObject * +binary_op1(PyObject *v, PyObject *w, const int op_slot) +{ + PyObject *x; + binaryfunc slotv = NULL; + binaryfunc slotw = NULL; + + if (v->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(v)) + slotv = NB_BINOP(v->ob_type->tp_as_number, op_slot); + if (w->ob_type != v->ob_type && + w->ob_type->tp_as_number != NULL && NEW_STYLE_NUMBER(w)) { + slotw = NB_BINOP(w->ob_type->tp_as_number, op_slot); + if (slotw == slotv) + slotw = NULL; + } + if (slotv) { + if (slotw && PyType_IsSubtype(w->ob_type, v->ob_type)) { + x = slotw(v, w); + if (x != Py_NotImplemented) + return x; + Py_DECREF(x); /* can't do it */ + slotw = NULL; + } + x = slotv(v, w); + if (x != Py_NotImplemented) + return x; + Py_DECREF(x); /* can't do it */ + } + if (slotw) { + x = slotw(v, w); + if (x != Py_NotImplemented) + return x; + Py_DECREF(x); /* can't do it */ + } + if (!NEW_STYLE_NUMBER(v) || !NEW_STYLE_NUMBER(w)) { + int err = PyNumber_CoerceEx(&v, &w); + if (err < 0) { + return NULL; + } + if (err == 0) { + PyNumberMethods *mv = v->ob_type->tp_as_number; + if (mv) { + binaryfunc slot; + slot = NB_BINOP(mv, op_slot); + if (slot) { + PyObject *x = slot(v, w); + Py_DECREF(v); + Py_DECREF(w); + return x; + } + } + /* CoerceEx incremented the reference counts */ + Py_DECREF(v); + Py_DECREF(w); + } + } + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static PyObject * +binop_type_error(PyObject *v, PyObject *w, const char *op_name) +{ + PyErr_Format(PyExc_TypeError, + "unsupported operand type(s) for %s: '%s' and '%s'", + op_name, + v->ob_type->tp_name, + w->ob_type->tp_name); + return NULL; +} + +static PyObject * +binary_op(PyObject *v, PyObject *w, const int op_slot, const char *op_name) +{ + PyObject *result = binary_op1(v, w, op_slot); + if (result == Py_NotImplemented) { + Py_DECREF(result); + return binop_type_error(v, w, op_name); + } + return result; +} + + +/* + Calling scheme used for ternary operations: + + *** In some cases, w.op is called before v.op; see binary_op1. *** + + v w z Action + ------------------------------------------------------------------- + new new new v.op(v,w,z), w.op(v,w,z), z.op(v,w,z) + new old new v.op(v,w,z), z.op(v,w,z), coerce(v,w,z), v.op(v,w,z) + old new new w.op(v,w,z), z.op(v,w,z), coerce(v,w,z), v.op(v,w,z) + old old new z.op(v,w,z), coerce(v,w,z), v.op(v,w,z) + new new old v.op(v,w,z), w.op(v,w,z), coerce(v,w,z), v.op(v,w,z) + new old old v.op(v,w,z), coerce(v,w,z), v.op(v,w,z) + old new old w.op(v,w,z), coerce(v,w,z), v.op(v,w,z) + old old old coerce(v,w,z), v.op(v,w,z) + + Legend: + ------- + * new == new style number + * old == old style number + * Action indicates the order in which operations are tried until either + a valid result is produced or an error occurs. + * coerce(v,w,z) actually does: coerce(v,w), coerce(v,z), coerce(w,z) and + only if z != Py_None; if z == Py_None, then it is treated as absent + variable and only coerce(v,w) is tried. + + */ + +static PyObject * +ternary_op(PyObject *v, + PyObject *w, + PyObject *z, + const int op_slot, + const char *op_name) +{ + PyNumberMethods *mv, *mw, *mz; + PyObject *x = NULL; + ternaryfunc slotv = NULL; + ternaryfunc slotw = NULL; + ternaryfunc slotz = NULL; + + mv = v->ob_type->tp_as_number; + mw = w->ob_type->tp_as_number; + if (mv != NULL && NEW_STYLE_NUMBER(v)) + slotv = NB_TERNOP(mv, op_slot); + if (w->ob_type != v->ob_type && + mw != NULL && NEW_STYLE_NUMBER(w)) { + slotw = NB_TERNOP(mw, op_slot); + if (slotw == slotv) + slotw = NULL; + } + if (slotv) { + if (slotw && PyType_IsSubtype(w->ob_type, v->ob_type)) { + x = slotw(v, w, z); + if (x != Py_NotImplemented) + return x; + Py_DECREF(x); /* can't do it */ + slotw = NULL; + } + x = slotv(v, w, z); + if (x != Py_NotImplemented) + return x; + Py_DECREF(x); /* can't do it */ + } + if (slotw) { + x = slotw(v, w, z); + if (x != Py_NotImplemented) + return x; + Py_DECREF(x); /* can't do it */ + } + mz = z->ob_type->tp_as_number; + if (mz != NULL && NEW_STYLE_NUMBER(z)) { + slotz = NB_TERNOP(mz, op_slot); + if (slotz == slotv || slotz == slotw) + slotz = NULL; + if (slotz) { + x = slotz(v, w, z); + if (x != Py_NotImplemented) + return x; + Py_DECREF(x); /* can't do it */ + } + } + + if (!NEW_STYLE_NUMBER(v) || !NEW_STYLE_NUMBER(w) || + (z != Py_None && !NEW_STYLE_NUMBER(z))) { + /* we have an old style operand, coerce */ + PyObject *v1, *z1, *w2, *z2; + int c; + + c = PyNumber_Coerce(&v, &w); + if (c != 0) + goto error3; + + /* Special case: if the third argument is None, it is + treated as absent argument and not coerced. */ + if (z == Py_None) { + if (v->ob_type->tp_as_number) { + slotz = NB_TERNOP(v->ob_type->tp_as_number, + op_slot); + if (slotz) + x = slotz(v, w, z); + else + c = -1; + } + else + c = -1; + goto error2; + } + v1 = v; + z1 = z; + c = PyNumber_Coerce(&v1, &z1); + if (c != 0) + goto error2; + w2 = w; + z2 = z1; + c = PyNumber_Coerce(&w2, &z2); + if (c != 0) + goto error1; + + if (v1->ob_type->tp_as_number != NULL) { + slotv = NB_TERNOP(v1->ob_type->tp_as_number, + op_slot); + if (slotv) + x = slotv(v1, w2, z2); + else + c = -1; + } + else + c = -1; + + Py_DECREF(w2); + Py_DECREF(z2); + error1: + Py_DECREF(v1); + Py_DECREF(z1); + error2: + Py_DECREF(v); + Py_DECREF(w); + error3: + if (c >= 0) + return x; + } + + if (z == Py_None) + PyErr_Format( + PyExc_TypeError, + "unsupported operand type(s) for ** or pow(): " + "'%s' and '%s'", + v->ob_type->tp_name, + w->ob_type->tp_name); + else + PyErr_Format( + PyExc_TypeError, + "unsupported operand type(s) for pow(): " + "'%s', '%s', '%s'", + v->ob_type->tp_name, + w->ob_type->tp_name, + z->ob_type->tp_name); + return NULL; +} + +#define BINARY_FUNC(func, op, op_name) \ + PyObject * \ + func(PyObject *v, PyObject *w) { \ + return binary_op(v, w, NB_SLOT(op), op_name); \ + } + +BINARY_FUNC(PyNumber_Or, nb_or, "|") +BINARY_FUNC(PyNumber_Xor, nb_xor, "^") +BINARY_FUNC(PyNumber_And, nb_and, "&") +BINARY_FUNC(PyNumber_Lshift, nb_lshift, "<<") +BINARY_FUNC(PyNumber_Rshift, nb_rshift, ">>") +BINARY_FUNC(PyNumber_Subtract, nb_subtract, "-") +BINARY_FUNC(PyNumber_Divide, nb_divide, "/") +BINARY_FUNC(PyNumber_Divmod, nb_divmod, "divmod()") + +PyObject * +PyNumber_Add(PyObject *v, PyObject *w) +{ + PyObject *result = binary_op1(v, w, NB_SLOT(nb_add)); + if (result == Py_NotImplemented) { + PySequenceMethods *m = v->ob_type->tp_as_sequence; + if (m && m->sq_concat) { + Py_DECREF(result); + result = (*m->sq_concat)(v, w); + } + if (result == Py_NotImplemented) { + Py_DECREF(result); + return binop_type_error(v, w, "+"); + } + } + return result; +} + +static PyObject * +sequence_repeat(intargfunc repeatfunc, PyObject *seq, PyObject *n) +{ + long count; + if (PyInt_Check(n)) { + count = PyInt_AsLong(n); + } + else if (PyLong_Check(n)) { + count = PyLong_AsLong(n); + if (count == -1 && PyErr_Occurred()) + return NULL; + } + else { + return type_error( + "can't multiply sequence to non-int"); + } +#if LONG_MAX != INT_MAX + if (count > INT_MAX) { + PyErr_SetString(PyExc_ValueError, + "sequence repeat count too large"); + return NULL; + } + else if (count < INT_MIN) + count = INT_MIN; + /* XXX Why don't I either + + - set count to -1 whenever it's negative (after all, + sequence repeat usually treats negative numbers + as zero(); or + + - raise an exception when it's less than INT_MIN? + + I'm thinking about a hypothetical use case where some + sequence type might use a negative value as a flag of + some kind. In those cases I don't want to break the + code by mapping all negative values to -1. But I also + don't want to break e.g. []*(-sys.maxint), which is + perfectly safe, returning []. As a compromise, I do + map out-of-range negative values. + */ +#endif + return (*repeatfunc)(seq, (int)count); +} + +PyObject * +PyNumber_Multiply(PyObject *v, PyObject *w) +{ + PyObject *result = binary_op1(v, w, NB_SLOT(nb_multiply)); + if (result == Py_NotImplemented) { + PySequenceMethods *mv = v->ob_type->tp_as_sequence; + PySequenceMethods *mw = w->ob_type->tp_as_sequence; + Py_DECREF(result); + if (mv && mv->sq_repeat) { + return sequence_repeat(mv->sq_repeat, v, w); + } + else if (mw && mw->sq_repeat) { + return sequence_repeat(mw->sq_repeat, w, v); + } + result = binop_type_error(v, w, "*"); + } + return result; +} + +PyObject * +PyNumber_FloorDivide(PyObject *v, PyObject *w) +{ + /* XXX tp_flags test */ + return binary_op(v, w, NB_SLOT(nb_floor_divide), "//"); +} + +PyObject * +PyNumber_TrueDivide(PyObject *v, PyObject *w) +{ + /* XXX tp_flags test */ + return binary_op(v, w, NB_SLOT(nb_true_divide), "/"); +} + +PyObject * +PyNumber_Remainder(PyObject *v, PyObject *w) +{ + return binary_op(v, w, NB_SLOT(nb_remainder), "%"); +} + +PyObject * +PyNumber_Power(PyObject *v, PyObject *w, PyObject *z) +{ + return ternary_op(v, w, z, NB_SLOT(nb_power), "** or pow()"); +} + +/* Binary in-place operators */ + +/* The in-place operators are defined to fall back to the 'normal', + non in-place operations, if the in-place methods are not in place. + + - If the left hand object has the appropriate struct members, and + they are filled, call the appropriate function and return the + result. No coercion is done on the arguments; the left-hand object + is the one the operation is performed on, and it's up to the + function to deal with the right-hand object. + + - Otherwise, in-place modification is not supported. Handle it exactly as + a non in-place operation of the same kind. + + */ + +#define HASINPLACE(t) \ + PyType_HasFeature((t)->ob_type, Py_TPFLAGS_HAVE_INPLACEOPS) + +static PyObject * +binary_iop1(PyObject *v, PyObject *w, const int iop_slot, const int op_slot) +{ + PyNumberMethods *mv = v->ob_type->tp_as_number; + if (mv != NULL && HASINPLACE(v)) { + binaryfunc slot = NB_BINOP(mv, iop_slot); + if (slot) { + PyObject *x = (slot)(v, w); + if (x != Py_NotImplemented) { + return x; + } + Py_DECREF(x); + } + } + return binary_op1(v, w, op_slot); +} + +static PyObject * +binary_iop(PyObject *v, PyObject *w, const int iop_slot, const int op_slot, + const char *op_name) +{ + PyObject *result = binary_iop1(v, w, iop_slot, op_slot); + if (result == Py_NotImplemented) { + Py_DECREF(result); + return binop_type_error(v, w, op_name); + } + return result; +} + +#define INPLACE_BINOP(func, iop, op, op_name) \ + PyObject * \ + func(PyObject *v, PyObject *w) { \ + return binary_iop(v, w, NB_SLOT(iop), NB_SLOT(op), op_name); \ + } + +INPLACE_BINOP(PyNumber_InPlaceOr, nb_inplace_or, nb_or, "|=") +INPLACE_BINOP(PyNumber_InPlaceXor, nb_inplace_xor, nb_xor, "^=") +INPLACE_BINOP(PyNumber_InPlaceAnd, nb_inplace_and, nb_and, "&=") +INPLACE_BINOP(PyNumber_InPlaceLshift, nb_inplace_lshift, nb_lshift, "<<=") +INPLACE_BINOP(PyNumber_InPlaceRshift, nb_inplace_rshift, nb_rshift, ">>=") +INPLACE_BINOP(PyNumber_InPlaceSubtract, nb_inplace_subtract, nb_subtract, "-=") +INPLACE_BINOP(PyNumber_InPlaceDivide, nb_inplace_divide, nb_divide, "/=") + +PyObject * +PyNumber_InPlaceFloorDivide(PyObject *v, PyObject *w) +{ + /* XXX tp_flags test */ + return binary_iop(v, w, NB_SLOT(nb_inplace_floor_divide), + NB_SLOT(nb_floor_divide), "//="); +} + +PyObject * +PyNumber_InPlaceTrueDivide(PyObject *v, PyObject *w) +{ + /* XXX tp_flags test */ + return binary_iop(v, w, NB_SLOT(nb_inplace_true_divide), + NB_SLOT(nb_true_divide), "/="); +} + +PyObject * +PyNumber_InPlaceAdd(PyObject *v, PyObject *w) +{ + PyObject *result = binary_iop1(v, w, NB_SLOT(nb_inplace_add), + NB_SLOT(nb_add)); + if (result == Py_NotImplemented) { + PySequenceMethods *m = v->ob_type->tp_as_sequence; + Py_DECREF(result); + if (m != NULL) { + binaryfunc f = NULL; + if (HASINPLACE(v)) + f = m->sq_inplace_concat; + if (f == NULL) + f = m->sq_concat; + if (f != NULL) + return (*f)(v, w); + } + result = binop_type_error(v, w, "+="); + } + return result; +} + +PyObject * +PyNumber_InPlaceMultiply(PyObject *v, PyObject *w) +{ + PyObject *result = binary_iop1(v, w, NB_SLOT(nb_inplace_multiply), + NB_SLOT(nb_multiply)); + if (result == Py_NotImplemented) { + intargfunc f = NULL; + PySequenceMethods *mv = v->ob_type->tp_as_sequence; + PySequenceMethods *mw = w->ob_type->tp_as_sequence; + Py_DECREF(result); + if (mv != NULL) { + if (HASINPLACE(v)) + f = mv->sq_inplace_repeat; + if (f == NULL) + f = mv->sq_repeat; + if (f != NULL) + return sequence_repeat(f, v, w); + } + else if (mw != NULL) { + /* Note that the right hand operand should not be + * mutated in this case so sq_inplace_repeat is not + * used. */ + if (mw->sq_repeat) + return sequence_repeat(mw->sq_repeat, w, v); + } + result = binop_type_error(v, w, "*="); + } + return result; +} + +PyObject * +PyNumber_InPlaceRemainder(PyObject *v, PyObject *w) +{ + return binary_iop(v, w, NB_SLOT(nb_inplace_remainder), + NB_SLOT(nb_remainder), "%="); +} + +PyObject * +PyNumber_InPlacePower(PyObject *v, PyObject *w, PyObject *z) +{ + if (HASINPLACE(v) && v->ob_type->tp_as_number && + v->ob_type->tp_as_number->nb_inplace_power != NULL) { + return ternary_op(v, w, z, NB_SLOT(nb_inplace_power), "**="); + } + else { + return ternary_op(v, w, z, NB_SLOT(nb_power), "**="); + } +} + + +/* Unary operators and functions */ + +PyObject * +PyNumber_Negative(PyObject *o) +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_negative) + return (*m->nb_negative)(o); + + return type_error("bad operand type for unary -"); +} + +PyObject * +PyNumber_Positive(PyObject *o) +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_positive) + return (*m->nb_positive)(o); + + return type_error("bad operand type for unary +"); +} + +PyObject * +PyNumber_Invert(PyObject *o) +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_invert) + return (*m->nb_invert)(o); + + return type_error("bad operand type for unary ~"); +} + +PyObject * +PyNumber_Absolute(PyObject *o) +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + m = o->ob_type->tp_as_number; + if (m && m->nb_absolute) + return m->nb_absolute(o); + + return type_error("bad operand type for abs()"); +} + +/* Add a check for embedded NULL-bytes in the argument. */ +static PyObject * +int_from_string(const char *s, int len) +{ + char *end; + PyObject *x; + + x = PyInt_FromString((char*)s, &end, 10); + if (x == NULL) + return NULL; + if (end != s + len) { + PyErr_SetString(PyExc_ValueError, + "null byte in argument for int()"); + Py_DECREF(x); + return NULL; + } + return x; +} + +PyObject * +PyNumber_Int(PyObject *o) +{ + PyNumberMethods *m; + const char *buffer; + int buffer_len; + + if (o == NULL) + return null_error(); + if (PyInt_CheckExact(o)) { + Py_INCREF(o); + return o; + } + if (PyInt_Check(o)) { + PyIntObject *io = (PyIntObject*)o; + return PyInt_FromLong(io->ob_ival); + } + if (PyString_Check(o)) + return int_from_string(PyString_AS_STRING(o), + PyString_GET_SIZE(o)); +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(o)) + return PyInt_FromUnicode(PyUnicode_AS_UNICODE(o), + PyUnicode_GET_SIZE(o), + 10); +#endif + m = o->ob_type->tp_as_number; + if (m && m->nb_int) + return m->nb_int(o); + if (!PyObject_AsCharBuffer(o, &buffer, &buffer_len)) + return int_from_string((char*)buffer, buffer_len); + + return type_error("int() argument must be a string or a number"); +} + +/* Add a check for embedded NULL-bytes in the argument. */ +static PyObject * +long_from_string(const char *s, int len) +{ + char *end; + PyObject *x; + + x = PyLong_FromString((char*)s, &end, 10); + if (x == NULL) + return NULL; + if (end != s + len) { + PyErr_SetString(PyExc_ValueError, + "null byte in argument for long()"); + Py_DECREF(x); + return NULL; + } + return x; +} + +PyObject * +PyNumber_Long(PyObject *o) +{ + PyNumberMethods *m; + const char *buffer; + int buffer_len; + + if (o == NULL) + return null_error(); + if (PyLong_CheckExact(o)) { + Py_INCREF(o); + return o; + } + if (PyLong_Check(o)) + return _PyLong_Copy((PyLongObject *)o); + if (PyString_Check(o)) + /* need to do extra error checking that PyLong_FromString() + * doesn't do. In particular long('9.5') must raise an + * exception, not truncate the float. + */ + return long_from_string(PyString_AS_STRING(o), + PyString_GET_SIZE(o)); +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(o)) + /* The above check is done in PyLong_FromUnicode(). */ + return PyLong_FromUnicode(PyUnicode_AS_UNICODE(o), + PyUnicode_GET_SIZE(o), + 10); +#endif + m = o->ob_type->tp_as_number; + if (m && m->nb_long) + return m->nb_long(o); + if (!PyObject_AsCharBuffer(o, &buffer, &buffer_len)) + return long_from_string(buffer, buffer_len); + + return type_error("long() argument must be a string or a number"); +} + +PyObject * +PyNumber_Float(PyObject *o) +{ + PyNumberMethods *m; + + if (o == NULL) + return null_error(); + if (PyFloat_CheckExact(o)) { + Py_INCREF(o); + return o; + } + if (PyFloat_Check(o)) { + PyFloatObject *po = (PyFloatObject *)o; + return PyFloat_FromDouble(po->ob_fval); + } + if (!PyString_Check(o)) { + m = o->ob_type->tp_as_number; + if (m && m->nb_float) + return m->nb_float(o); + } + return PyFloat_FromString(o, NULL); +} + +/* Operations on sequences */ + +int +PySequence_Check(PyObject *s) +{ + return s != NULL && s->ob_type->tp_as_sequence && + s->ob_type->tp_as_sequence->sq_item != NULL; +} + +int +PySequence_Size(PyObject *s) +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_length) + return m->sq_length(s); + + type_error("len() of unsized object"); + return -1; +} + +#undef PySequence_Length +int +PySequence_Length(PyObject *s) +{ + return PySequence_Size(s); +} +#define PySequence_Length PySequence_Size + +PyObject * +PySequence_Concat(PyObject *s, PyObject *o) +{ + PySequenceMethods *m; + + if (s == NULL || o == NULL) + return null_error(); + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_concat) + return m->sq_concat(s, o); + + return type_error("object can't be concatenated"); +} + +PyObject * +PySequence_Repeat(PyObject *o, int count) +{ + PySequenceMethods *m; + + if (o == NULL) + return null_error(); + + m = o->ob_type->tp_as_sequence; + if (m && m->sq_repeat) + return m->sq_repeat(o, count); + + return type_error("object can't be repeated"); +} + +PyObject * +PySequence_InPlaceConcat(PyObject *s, PyObject *o) +{ + PySequenceMethods *m; + + if (s == NULL || o == NULL) + return null_error(); + + m = s->ob_type->tp_as_sequence; + if (m && HASINPLACE(s) && m->sq_inplace_concat) + return m->sq_inplace_concat(s, o); + if (m && m->sq_concat) + return m->sq_concat(s, o); + + return type_error("object can't be concatenated"); +} + +PyObject * +PySequence_InPlaceRepeat(PyObject *o, int count) +{ + PySequenceMethods *m; + + if (o == NULL) + return null_error(); + + m = o->ob_type->tp_as_sequence; + if (m && HASINPLACE(o) && m->sq_inplace_repeat) + return m->sq_inplace_repeat(o, count); + if (m && m->sq_repeat) + return m->sq_repeat(o, count); + + return type_error("object can't be repeated"); +} + +PyObject * +PySequence_GetItem(PyObject *s, int i) +{ + PySequenceMethods *m; + + if (s == NULL) + return null_error(); + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_item) { + if (i < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return NULL; + i += l; + } + } + return m->sq_item(s, i); + } + + return type_error("unindexable object"); +} + +static PyObject * +sliceobj_from_intint(int i, int j) +{ + PyObject *start, *end, *slice; + start = PyInt_FromLong((long)i); + if (!start) + return NULL; + end = PyInt_FromLong((long)j); + if (!end) { + Py_DECREF(start); + return NULL; + } + slice = PySlice_New(start, end, NULL); + Py_DECREF(start); + Py_DECREF(end); + return slice; +} + +PyObject * +PySequence_GetSlice(PyObject *s, int i1, int i2) +{ + PySequenceMethods *m; + PyMappingMethods *mp; + + if (!s) return null_error(); + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_slice) { + if (i1 < 0 || i2 < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return NULL; + if (i1 < 0) + i1 += l; + if (i2 < 0) + i2 += l; + } + } + return m->sq_slice(s, i1, i2); + } else if ((mp = s->ob_type->tp_as_mapping) && mp->mp_subscript) { + PyObject *res; + PyObject *slice = sliceobj_from_intint(i1, i2); + if (!slice) + return NULL; + res = mp->mp_subscript(s, slice); + Py_DECREF(slice); + return res; + } + + return type_error("unsliceable object"); +} + +int +PySequence_SetItem(PyObject *s, int i, PyObject *o) +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_item) { + if (i < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return -1; + i += l; + } + } + return m->sq_ass_item(s, i, o); + } + + type_error("object doesn't support item assignment"); + return -1; +} + +int +PySequence_DelItem(PyObject *s, int i) +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_item) { + if (i < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return -1; + i += l; + } + } + return m->sq_ass_item(s, i, (PyObject *)NULL); + } + + type_error("object doesn't support item deletion"); + return -1; +} + +int +PySequence_SetSlice(PyObject *s, int i1, int i2, PyObject *o) +{ + PySequenceMethods *m; + PyMappingMethods *mp; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_slice) { + if (i1 < 0 || i2 < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return -1; + if (i1 < 0) + i1 += l; + if (i2 < 0) + i2 += l; + } + } + return m->sq_ass_slice(s, i1, i2, o); + } else if ((mp = s->ob_type->tp_as_mapping) && mp->mp_ass_subscript) { + int res; + PyObject *slice = sliceobj_from_intint(i1, i2); + if (!slice) + return -1; + res = mp->mp_ass_subscript(s, slice, o); + Py_DECREF(slice); + return res; + } + + type_error("object doesn't support slice assignment"); + return -1; +} + +int +PySequence_DelSlice(PyObject *s, int i1, int i2) +{ + PySequenceMethods *m; + + if (s == NULL) { + null_error(); + return -1; + } + + m = s->ob_type->tp_as_sequence; + if (m && m->sq_ass_slice) { + if (i1 < 0 || i2 < 0) { + if (m->sq_length) { + int l = (*m->sq_length)(s); + if (l < 0) + return -1; + if (i1 < 0) + i1 += l; + if (i2 < 0) + i2 += l; + } + } + return m->sq_ass_slice(s, i1, i2, (PyObject *)NULL); + } + type_error("object doesn't support slice deletion"); + return -1; +} + +PyObject * +PySequence_Tuple(PyObject *v) +{ + PyObject *it; /* iter(v) */ + int n; /* guess for result tuple size */ + PyObject *result; + int j; + + if (v == NULL) + return null_error(); + + /* Special-case the common tuple and list cases, for efficiency. */ + if (PyTuple_CheckExact(v)) { + /* Note that we can't know whether it's safe to return + a tuple *subclass* instance as-is, hence the restriction + to exact tuples here. In contrast, lists always make + a copy, so there's no need for exactness below. */ + Py_INCREF(v); + return v; + } + if (PyList_Check(v)) + return PyList_AsTuple(v); + + /* Get iterator. */ + it = PyObject_GetIter(v); + if (it == NULL) + return NULL; + + /* Guess result size and allocate space. */ + n = PySequence_Size(v); + if (n < 0) { + PyErr_Clear(); + n = 10; /* arbitrary */ + } + result = PyTuple_New(n); + if (result == NULL) + goto Fail; + + /* Fill the tuple. */ + for (j = 0; ; ++j) { + PyObject *item = PyIter_Next(it); + if (item == NULL) { + if (PyErr_Occurred()) + goto Fail; + break; + } + if (j >= n) { + if (n < 500) + n += 10; + else + n += 100; + if (_PyTuple_Resize(&result, n) != 0) { + Py_DECREF(item); + goto Fail; + } + } + PyTuple_SET_ITEM(result, j, item); + } + + /* Cut tuple back if guess was too large. */ + if (j < n && + _PyTuple_Resize(&result, j) != 0) + goto Fail; + + Py_DECREF(it); + return result; + +Fail: + Py_XDECREF(result); + Py_DECREF(it); + return NULL; +} + +PyObject * +PySequence_List(PyObject *v) +{ + PyObject *it; /* iter(v) */ + PyObject *result; /* result list */ + int n; /* guess for result list size */ + int i; + + if (v == NULL) + return null_error(); + + /* Special-case list(a_list), for speed. */ + if (PyList_Check(v)) + return PyList_GetSlice(v, 0, PyList_GET_SIZE(v)); + + /* Get iterator. There may be some low-level efficiency to be gained + * by caching the tp_iternext slot instead of using PyIter_Next() + * later, but premature optimization is the root etc. + */ + it = PyObject_GetIter(v); + if (it == NULL) + return NULL; + + /* Guess a result list size. */ + n = -1; /* unknown */ + if (PySequence_Check(v) && + v->ob_type->tp_as_sequence->sq_length) { + n = PySequence_Size(v); + if (n < 0) + PyErr_Clear(); + } + if (n < 0) + n = 8; /* arbitrary */ + result = PyList_New(n); + if (result == NULL) { + Py_DECREF(it); + return NULL; + } + + /* Run iterator to exhaustion. */ + for (i = 0; ; i++) { + PyObject *item = PyIter_Next(it); + if (item == NULL) { + if (PyErr_Occurred()) { + Py_DECREF(result); + result = NULL; + } + break; + } + if (i < n) + PyList_SET_ITEM(result, i, item); /* steals ref */ + else { + int status = PyList_Append(result, item); + Py_DECREF(item); /* append creates a new ref */ + if (status < 0) { + Py_DECREF(result); + result = NULL; + break; + } + } + } + + /* Cut back result list if initial guess was too large. */ + if (i < n && result != NULL) { + if (PyList_SetSlice(result, i, n, (PyObject *)NULL) != 0) { + Py_DECREF(result); + result = NULL; + } + } + Py_DECREF(it); + return result; +} + +PyObject * +PySequence_Fast(PyObject *v, const char *m) +{ + if (v == NULL) + return null_error(); + + if (PyList_CheckExact(v) || PyTuple_CheckExact(v)) { + Py_INCREF(v); + return v; + } + + v = PySequence_Tuple(v); + if (v == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) + return type_error(m); + + return v; +} + +/* Iterate over seq. Result depends on the operation: + PY_ITERSEARCH_COUNT: -1 if error, else # of times obj appears in seq. + PY_ITERSEARCH_INDEX: 0-based index of first occurence of obj in seq; + set ValueError and return -1 if none found; also return -1 on error. + Py_ITERSEARCH_CONTAINS: return 1 if obj in seq, else 0; -1 on error. +*/ +int +_PySequence_IterSearch(PyObject *seq, PyObject *obj, int operation) +{ + int n; + int wrapped; /* for PY_ITERSEARCH_INDEX, true iff n wrapped around */ + PyObject *it; /* iter(seq) */ + + if (seq == NULL || obj == NULL) { + null_error(); + return -1; + } + + it = PyObject_GetIter(seq); + if (it == NULL) { + type_error("iterable argument required"); + return -1; + } + + n = wrapped = 0; + for (;;) { + int cmp; + PyObject *item = PyIter_Next(it); + if (item == NULL) { + if (PyErr_Occurred()) + goto Fail; + break; + } + + cmp = PyObject_RichCompareBool(obj, item, Py_EQ); + Py_DECREF(item); + if (cmp < 0) + goto Fail; + if (cmp > 0) { + switch (operation) { + case PY_ITERSEARCH_COUNT: + ++n; + if (n <= 0) { + PyErr_SetString(PyExc_OverflowError, + "count exceeds C int size"); + goto Fail; + } + break; + + case PY_ITERSEARCH_INDEX: + if (wrapped) { + PyErr_SetString(PyExc_OverflowError, + "index exceeds C int size"); + goto Fail; + } + goto Done; + + case PY_ITERSEARCH_CONTAINS: + n = 1; + goto Done; + + default: + assert(!"unknown operation"); + } + } + + if (operation == PY_ITERSEARCH_INDEX) { + ++n; + if (n <= 0) + wrapped = 1; + } + } + + if (operation != PY_ITERSEARCH_INDEX) + goto Done; + + PyErr_SetString(PyExc_ValueError, + "sequence.index(x): x not in sequence"); + /* fall into failure code */ +Fail: + n = -1; + /* fall through */ +Done: + Py_DECREF(it); + return n; + +} + +/* Return # of times o appears in s. */ +int +PySequence_Count(PyObject *s, PyObject *o) +{ + return _PySequence_IterSearch(s, o, PY_ITERSEARCH_COUNT); +} + +/* Return -1 if error; 1 if ob in seq; 0 if ob not in seq. + * Use sq_contains if possible, else defer to _PySequence_IterSearch(). + */ +int +PySequence_Contains(PyObject *seq, PyObject *ob) +{ + if (PyType_HasFeature(seq->ob_type, Py_TPFLAGS_HAVE_SEQUENCE_IN)) { + PySequenceMethods *sqm = seq->ob_type->tp_as_sequence; + if (sqm != NULL && sqm->sq_contains != NULL) + return (*sqm->sq_contains)(seq, ob); + } + return _PySequence_IterSearch(seq, ob, PY_ITERSEARCH_CONTAINS); +} + +/* Backwards compatibility */ +#undef PySequence_In +int +PySequence_In(PyObject *w, PyObject *v) +{ + return PySequence_Contains(w, v); +} + +int +PySequence_Index(PyObject *s, PyObject *o) +{ + return _PySequence_IterSearch(s, o, PY_ITERSEARCH_INDEX); +} + +/* Operations on mappings */ + +int +PyMapping_Check(PyObject *o) +{ + return o && o->ob_type->tp_as_mapping && + o->ob_type->tp_as_mapping->mp_subscript; +} + +int +PyMapping_Size(PyObject *o) +{ + PyMappingMethods *m; + + if (o == NULL) { + null_error(); + return -1; + } + + m = o->ob_type->tp_as_mapping; + if (m && m->mp_length) + return m->mp_length(o); + + type_error("len() of unsized object"); + return -1; +} + +#undef PyMapping_Length +int +PyMapping_Length(PyObject *o) +{ + return PyMapping_Size(o); +} +#define PyMapping_Length PyMapping_Size + +PyObject * +PyMapping_GetItemString(PyObject *o, char *key) +{ + PyObject *okey, *r; + + if (key == NULL) + return null_error(); + + okey = PyString_FromString(key); + if (okey == NULL) + return NULL; + r = PyObject_GetItem(o, okey); + Py_DECREF(okey); + return r; +} + +int +PyMapping_SetItemString(PyObject *o, char *key, PyObject *value) +{ + PyObject *okey; + int r; + + if (key == NULL) { + null_error(); + return -1; + } + + okey = PyString_FromString(key); + if (okey == NULL) + return -1; + r = PyObject_SetItem(o, okey, value); + Py_DECREF(okey); + return r; +} + +int +PyMapping_HasKeyString(PyObject *o, char *key) +{ + PyObject *v; + + v = PyMapping_GetItemString(o, key); + if (v) { + Py_DECREF(v); + return 1; + } + PyErr_Clear(); + return 0; +} + +int +PyMapping_HasKey(PyObject *o, PyObject *key) +{ + PyObject *v; + + v = PyObject_GetItem(o, key); + if (v) { + Py_DECREF(v); + return 1; + } + PyErr_Clear(); + return 0; +} + +/* Operations on callable objects */ + +/* XXX PyCallable_Check() is in object.c */ + +PyObject * +PyObject_CallObject(PyObject *o, PyObject *a) +{ + return PyEval_CallObjectWithKeywords(o, a, NULL); +} + +PyObject * +PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) +{ + ternaryfunc call; + + if ((call = func->ob_type->tp_call) != NULL) { + PyObject *result = (*call)(func, arg, kw); + if (result == NULL && !PyErr_Occurred()) + PyErr_SetString( + PyExc_SystemError, + "NULL result without error in PyObject_Call"); + return result; + } + PyErr_Format(PyExc_TypeError, "'%s' object is not callable", + func->ob_type->tp_name); + return NULL; +} + +PyObject * +PyObject_CallFunction(PyObject *callable, char *format, ...) +{ + va_list va; + PyObject *args, *retval; + + if (callable == NULL) + return null_error(); + + if (format && *format) { + va_start(va, format); + args = Py_VaBuildValue(format, va); + va_end(va); + } + else + args = PyTuple_New(0); + + if (args == NULL) + return NULL; + + if (!PyTuple_Check(args)) { + PyObject *a; + + a = PyTuple_New(1); + if (a == NULL) + return NULL; + if (PyTuple_SetItem(a, 0, args) < 0) + return NULL; + args = a; + } + retval = PyObject_Call(callable, args, NULL); + + Py_DECREF(args); + + return retval; +} + +PyObject * +PyObject_CallMethod(PyObject *o, char *name, char *format, ...) +{ + va_list va; + PyObject *args, *func = 0, *retval; + + if (o == NULL || name == NULL) + return null_error(); + + func = PyObject_GetAttrString(o, name); + if (func == NULL) { + PyErr_SetString(PyExc_AttributeError, name); + return 0; + } + + if (!PyCallable_Check(func)) + return type_error("call of non-callable attribute"); + + if (format && *format) { + va_start(va, format); + args = Py_VaBuildValue(format, va); + va_end(va); + } + else + args = PyTuple_New(0); + + if (!args) + return NULL; + + if (!PyTuple_Check(args)) { + PyObject *a; + + a = PyTuple_New(1); + if (a == NULL) + return NULL; + if (PyTuple_SetItem(a, 0, args) < 0) + return NULL; + args = a; + } + + retval = PyObject_Call(func, args, NULL); + + Py_DECREF(args); + Py_DECREF(func); + + return retval; +} + + +static PyObject * +objargs_mktuple(va_list va) +{ + int i, n = 0; + va_list countva; + PyObject *result, *tmp; + +#ifdef VA_LIST_IS_ARRAY + memcpy(countva, va, sizeof(va_list)); +#else +#ifdef __va_copy + __va_copy(countva, va); +#else + countva = va; +#endif +#endif + + while (((PyObject *)va_arg(countva, PyObject *)) != NULL) + ++n; + result = PyTuple_New(n); + if (result != NULL && n > 0) { + for (i = 0; i < n; ++i) { + tmp = (PyObject *)va_arg(va, PyObject *); + PyTuple_SET_ITEM(result, i, tmp); + Py_INCREF(tmp); + } + } + return result; +} + +PyObject * +PyObject_CallMethodObjArgs(PyObject *callable, PyObject *name, ...) +{ + PyObject *args, *tmp; + va_list vargs; + + if (callable == NULL || name == NULL) + return null_error(); + + callable = PyObject_GetAttr(callable, name); + if (callable == NULL) + return NULL; + + /* count the args */ + va_start(vargs, name); + args = objargs_mktuple(vargs); + va_end(vargs); + if (args == NULL) { + Py_DECREF(callable); + return NULL; + } + tmp = PyObject_Call(callable, args, NULL); + Py_DECREF(args); + Py_DECREF(callable); + + return tmp; +} + +PyObject * +PyObject_CallFunctionObjArgs(PyObject *callable, ...) +{ + PyObject *args, *tmp; + va_list vargs; + + if (callable == NULL) + return null_error(); + + /* count the args */ + va_start(vargs, callable); + args = objargs_mktuple(vargs); + va_end(vargs); + if (args == NULL) + return NULL; + tmp = PyObject_Call(callable, args, NULL); + Py_DECREF(args); + + return tmp; +} + + +/* isinstance(), issubclass() */ + +/* abstract_get_bases() has logically 4 return states, with a sort of 0th + * state that will almost never happen. + * + * 0. creating the __bases__ static string could get a MemoryError + * 1. getattr(cls, '__bases__') could raise an AttributeError + * 2. getattr(cls, '__bases__') could raise some other exception + * 3. getattr(cls, '__bases__') could return a tuple + * 4. getattr(cls, '__bases__') could return something other than a tuple + * + * Only state #3 is a non-error state and only it returns a non-NULL object + * (it returns the retrieved tuple). + * + * Any raised AttributeErrors are masked by clearing the exception and + * returning NULL. If an object other than a tuple comes out of __bases__, + * then again, the return value is NULL. So yes, these two situations + * produce exactly the same results: NULL is returned and no error is set. + * + * If some exception other than AttributeError is raised, then NULL is also + * returned, but the exception is not cleared. That's because we want the + * exception to be propagated along. + * + * Callers are expected to test for PyErr_Occurred() when the return value + * is NULL to decide whether a valid exception should be propagated or not. + * When there's no exception to propagate, it's customary for the caller to + * set a TypeError. + */ +static PyObject * +abstract_get_bases(PyObject *cls) +{ + static PyObject *__bases__ = NULL; + PyObject *bases; + + if (__bases__ == NULL) { + __bases__ = PyString_FromString("__bases__"); + if (__bases__ == NULL) + return NULL; + } + bases = PyObject_GetAttr(cls, __bases__); + if (bases == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_Clear(); + return NULL; + } + if (!PyTuple_Check(bases)) { + Py_DECREF(bases); + return NULL; + } + return bases; +} + + +static int +abstract_issubclass(PyObject *derived, PyObject *cls) +{ + PyObject *bases; + int i, n; + int r = 0; + + + if (derived == cls) + return 1; + + if (PyTuple_Check(cls)) { + /* Not a general sequence -- that opens up the road to + recursion and stack overflow. */ + n = PyTuple_GET_SIZE(cls); + for (i = 0; i < n; i++) { + if (derived == PyTuple_GET_ITEM(cls, i)) + return 1; + } + } + bases = abstract_get_bases(derived); + if (bases == NULL) { + if (PyErr_Occurred()) + return -1; + return 0; + } + n = PyTuple_GET_SIZE(bases); + for (i = 0; i < n; i++) { + r = abstract_issubclass(PyTuple_GET_ITEM(bases, i), cls); + if (r != 0) + break; + } + + Py_DECREF(bases); + + return r; +} + +static int +check_class(PyObject *cls, const char *error) +{ + PyObject *bases = abstract_get_bases(cls); + if (bases == NULL) { + /* Do not mask errors. */ + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_TypeError, error); + return 0; + } + Py_DECREF(bases); + return -1; +} + +int +PyObject_IsInstance(PyObject *inst, PyObject *cls) +{ + PyObject *icls; + static PyObject *__class__ = NULL; + int retval = 0; + + if (__class__ == NULL) { + __class__ = PyString_FromString("__class__"); + if (__class__ == NULL) + return -1; + } + + if (PyClass_Check(cls) && PyInstance_Check(inst)) { + PyObject *inclass = + (PyObject*)((PyInstanceObject*)inst)->in_class; + retval = PyClass_IsSubclass(inclass, cls); + } + else if (PyType_Check(cls)) { + retval = PyObject_TypeCheck(inst, (PyTypeObject *)cls); + if (retval == 0) { + PyObject *c = PyObject_GetAttr(inst, __class__); + if (c == NULL) { + PyErr_Clear(); + } + else { + if (c != (PyObject *)(inst->ob_type) && + PyType_Check(c)) + retval = PyType_IsSubtype( + (PyTypeObject *)c, + (PyTypeObject *)cls); + Py_DECREF(c); + } + } + } + else if (PyTuple_Check(cls)) { + /* Not a general sequence -- that opens up the road to + recursion and stack overflow. */ + int i, n; + + n = PyTuple_GET_SIZE(cls); + for (i = 0; i < n; i++) { + retval = PyObject_IsInstance( + inst, PyTuple_GET_ITEM(cls, i)); + if (retval != 0) + break; + } + } + else { + if (!check_class(cls, + "isinstance() arg 2 must be a class, type," + " or tuple of classes and types")) + return -1; + icls = PyObject_GetAttr(inst, __class__); + if (icls == NULL) { + PyErr_Clear(); + retval = 0; + } + else { + retval = abstract_issubclass(icls, cls); + Py_DECREF(icls); + } + } + + return retval; +} + +int +PyObject_IsSubclass(PyObject *derived, PyObject *cls) +{ + int retval; + + if (!PyClass_Check(derived) || !PyClass_Check(cls)) { + if (!check_class(derived, + "issubclass() arg 1 must be a class")) + return -1; + + if (PyTuple_Check(cls)) { + int i; + int n = PyTuple_GET_SIZE(cls); + for (i = 0; i < n; ++i) { + retval = PyObject_IsSubclass( + derived, PyTuple_GET_ITEM(cls, i)); + if (retval != 0) { + /* either found it, or got an error */ + return retval; + } + } + return 0; + } + else { + if (!check_class(cls, + "issubclass() arg 2 must be a class" + " or tuple of classes")) + return -1; + } + + retval = abstract_issubclass(derived, cls); + } + else { + /* shortcut */ + if (!(retval = (derived == cls))) + retval = PyClass_IsSubclass(derived, cls); + } + + return retval; +} + +PyObject * +PyObject_GetIter(PyObject *o) +{ + PyTypeObject *t = o->ob_type; + getiterfunc f = NULL; + if (PyType_HasFeature(t, Py_TPFLAGS_HAVE_ITER)) + f = t->tp_iter; + if (f == NULL) { + if (PySequence_Check(o)) + return PySeqIter_New(o); + PyErr_SetString(PyExc_TypeError, + "iteration over non-sequence"); + return NULL; + } + else { + PyObject *res = (*f)(o); + if (res != NULL && !PyIter_Check(res)) { + PyErr_Format(PyExc_TypeError, + "iter() returned non-iterator " + "of type '%.100s'", + res->ob_type->tp_name); + Py_DECREF(res); + res = NULL; + } + return res; + } +} + +/* Return next item. + * If an error occurs, return NULL. PyErr_Occurred() will be true. + * If the iteration terminates normally, return NULL and clear the + * PyExc_StopIteration exception (if it was set). PyErr_Occurred() + * will be false. + * Else return the next object. PyErr_Occurred() will be false. + */ +PyObject * +PyIter_Next(PyObject *iter) +{ + PyObject *result; + assert(PyIter_Check(iter)); + result = (*iter->ob_type->tp_iternext)(iter); + if (result == NULL && + PyErr_Occurred() && + PyErr_ExceptionMatches(PyExc_StopIteration)) + PyErr_Clear(); + return result; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/boolobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/boolobject.c new file mode 100644 index 00000000..75165e31 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/boolobject.c @@ -0,0 +1,201 @@ +/* Boolean type, a subtype of int */ + +#include "Python.h" + +/* We need to define bool_print to override int_print */ + +static int +bool_print(PyBoolObject *self, FILE *fp, int flags) +{ + fputs(self->ob_ival == 0 ? "False" : "True", fp); + return 0; +} + +/* We define bool_repr to return "False" or "True" */ + +static PyObject *false_str = NULL; +static PyObject *true_str = NULL; + +static PyObject * +bool_repr(PyBoolObject *self) +{ + PyObject *s; + + if (self->ob_ival) + s = true_str ? true_str : + (true_str = PyString_InternFromString("True")); + else + s = false_str ? false_str : + (false_str = PyString_InternFromString("False")); + Py_XINCREF(s); + return s; +} + +/* Function to return a bool from a C long */ + +PyObject *PyBool_FromLong(long ok) +{ + PyObject *result; + + if (ok) + result = Py_True; + else + result = Py_False; + Py_INCREF(result); + return result; +} + +/* We define bool_new to always return either Py_True or Py_False */ + +static PyObject * +bool_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"x", 0}; + PyObject *x = Py_False; + long ok; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:bool", kwlist, &x)) + return NULL; + ok = PyObject_IsTrue(x); + if (ok < 0) + return NULL; + return PyBool_FromLong(ok); +} + +/* Arithmetic operations redefined to return bool if both args are bool. */ + +static PyObject * +bool_and(PyObject *a, PyObject *b) +{ + if (!PyBool_Check(a) || !PyBool_Check(b)) + return PyInt_Type.tp_as_number->nb_and(a, b); + return PyBool_FromLong( + ((PyBoolObject *)a)->ob_ival & ((PyBoolObject *)b)->ob_ival); +} + +static PyObject * +bool_or(PyObject *a, PyObject *b) +{ + if (!PyBool_Check(a) || !PyBool_Check(b)) + return PyInt_Type.tp_as_number->nb_or(a, b); + return PyBool_FromLong( + ((PyBoolObject *)a)->ob_ival | ((PyBoolObject *)b)->ob_ival); +} + +static PyObject * +bool_xor(PyObject *a, PyObject *b) +{ + if (!PyBool_Check(a) || !PyBool_Check(b)) + return PyInt_Type.tp_as_number->nb_xor(a, b); + return PyBool_FromLong( + ((PyBoolObject *)a)->ob_ival ^ ((PyBoolObject *)b)->ob_ival); +} + +/* Doc string */ + +PyDoc_STRVAR(bool_doc, +"bool(x) -> bool\n\ +\n\ +Returns True when the argument x is true, False otherwise.\n\ +The builtins True and False are the only two instances of the class bool.\n\ +The class bool is a subclass of the class int, and cannot be subclassed."); + +/* Arithmetic methods -- only so we can override &, |, ^. */ + +static PyNumberMethods bool_as_number = { + 0, /* nb_add */ + 0, /* nb_subtract */ + 0, /* nb_multiply */ + 0, /* nb_divide */ + 0, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + 0, /* nb_negative */ + 0, /* nb_positive */ + 0, /* nb_absolute */ + 0, /* nb_nonzero */ + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + (binaryfunc)bool_and, /* nb_and */ + (binaryfunc)bool_xor, /* nb_xor */ + (binaryfunc)bool_or, /* nb_or */ + 0, /* nb_coerce */ + 0, /* nb_int */ + 0, /* nb_long */ + 0, /* nb_float */ + 0, /* nb_oct */ + 0, /* nb_hex */ + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ + 0, /* nb_inplace_divide */ + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + 0, /* nb_floor_divide */ + 0, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +}; + +/* The type object for bool. Note that this cannot be subclassed! */ + +PyTypeObject PyBool_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "bool", + sizeof(PyIntObject), + 0, + 0, /* tp_dealloc */ + (printfunc)bool_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)bool_repr, /* tp_repr */ + &bool_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)bool_repr, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ + bool_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &PyInt_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + bool_new, /* tp_new */ +}; + +/* The objects representing bool values False and True */ + +/* Named Zero for link-level compatibility */ +PyIntObject _Py_ZeroStruct = { + PyObject_HEAD_INIT(&PyBool_Type) + 0 +}; + +PyIntObject _Py_TrueStruct = { + PyObject_HEAD_INIT(&PyBool_Type) + 1 +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/bufferobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/bufferobject.c new file mode 100644 index 00000000..216a7a9c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/bufferobject.c @@ -0,0 +1,568 @@ + +/* Buffer object implementation */ + +#include "Python.h" + + +typedef struct { + PyObject_HEAD + PyObject *b_base; + void *b_ptr; + int b_size; + int b_readonly; + long b_hash; +} PyBufferObject; + + +static PyObject * +_PyBuffer_FromMemory(PyObject *base, void *ptr, int size, int readonly) +{ + PyBufferObject * b; + + if ( size < 0 ) { + PyErr_SetString(PyExc_ValueError, + "size must be zero or positive"); + return NULL; + } + + b = PyObject_NEW(PyBufferObject, &PyBuffer_Type); + if ( b == NULL ) + return NULL; + + Py_XINCREF(base); + b->b_base = base; + b->b_ptr = ptr; + b->b_size = size; + b->b_readonly = readonly; + b->b_hash = -1; + + return (PyObject *) b; +} + +static PyObject * +_PyBuffer_FromObject(PyObject *base, int offset, int size, + getreadbufferproc proc, int readonly) +{ + PyBufferProcs *pb = base->ob_type->tp_as_buffer; + void *p; + int count; + + if ( offset < 0 ) { + PyErr_SetString(PyExc_ValueError, + "offset must be zero or positive"); + return NULL; + } + + if ( (*pb->bf_getsegcount)(base, NULL) != 1 ) + { + PyErr_SetString(PyExc_TypeError, + "single-segment buffer object expected"); + return NULL; + } + if ( (count = (*proc)(base, 0, &p)) < 0 ) + return NULL; + + /* apply constraints to the start/end */ + if ( size == Py_END_OF_BUFFER || size < 0 ) + size = count; + if ( offset > count ) + offset = count; + if ( offset + size > count ) + size = count - offset; + + /* if the base object is another buffer, then "deref" it, + * except if the base of the other buffer is NULL + */ + if ( PyBuffer_Check(base) && (((PyBufferObject *)base)->b_base) ) + base = ((PyBufferObject *)base)->b_base; + + return _PyBuffer_FromMemory(base, (char *)p + offset, size, readonly); +} + + +PyObject * +PyBuffer_FromObject(PyObject *base, int offset, int size) +{ + PyBufferProcs *pb = base->ob_type->tp_as_buffer; + + if ( pb == NULL || + pb->bf_getreadbuffer == NULL || + pb->bf_getsegcount == NULL ) + { + PyErr_SetString(PyExc_TypeError, "buffer object expected"); + return NULL; + } + + return _PyBuffer_FromObject(base, offset, size, + pb->bf_getreadbuffer, 1); +} + +PyObject * +PyBuffer_FromReadWriteObject(PyObject *base, int offset, int size) +{ + PyBufferProcs *pb = base->ob_type->tp_as_buffer; + + if ( pb == NULL || + pb->bf_getwritebuffer == NULL || + pb->bf_getsegcount == NULL ) + { + PyErr_SetString(PyExc_TypeError, "buffer object expected"); + return NULL; + } + + return _PyBuffer_FromObject(base, offset, size, + (getreadbufferproc)pb->bf_getwritebuffer, + 0); +} + +PyObject * +PyBuffer_FromMemory(void *ptr, int size) +{ + return _PyBuffer_FromMemory(NULL, ptr, size, 1); +} + +PyObject * +PyBuffer_FromReadWriteMemory(void *ptr, int size) +{ + return _PyBuffer_FromMemory(NULL, ptr, size, 0); +} + +PyObject * +PyBuffer_New(int size) +{ + PyObject *o; + PyBufferObject * b; + + if (size < 0) { + PyErr_SetString(PyExc_ValueError, + "size must be zero or positive"); + return NULL; + } + /* Inline PyObject_New */ + o = PyObject_MALLOC(sizeof(*b) + size); + if ( o == NULL ) + return PyErr_NoMemory(); + b = (PyBufferObject *) PyObject_INIT(o, &PyBuffer_Type); + + b->b_base = NULL; + b->b_ptr = (void *)(b + 1); + b->b_size = size; + b->b_readonly = 0; + b->b_hash = -1; + + return o; +} + +/* Methods */ + +static PyObject * +buffer_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + PyObject *ob; + int offset = 0; + int size = Py_END_OF_BUFFER; + + if ( !PyArg_ParseTuple(args, "O|ii:buffer", &ob, &offset, &size) ) + return NULL; + return PyBuffer_FromObject(ob, offset, size); +} + +PyDoc_STRVAR(buffer_doc, +"buffer(object [, offset[, size]])\n\ +\n\ +Create a new buffer object which references the given object.\n\ +The buffer will reference a slice of the target object from the\n\ +start of the object (or at the specified offset). The slice will\n\ +extend to the end of the target object (or with the specified size)."); + + +static void +buffer_dealloc(PyBufferObject *self) +{ + Py_XDECREF(self->b_base); + PyObject_DEL(self); +} + +static int +buffer_compare(PyBufferObject *self, PyBufferObject *other) +{ + int len_self = self->b_size; + int len_other = other->b_size; + int min_len = (len_self < len_other) ? len_self : len_other; + int cmp; + if (min_len > 0) { + cmp = memcmp(self->b_ptr, other->b_ptr, min_len); + if (cmp != 0) + return cmp; + } + return (len_self < len_other) ? -1 : (len_self > len_other) ? 1 : 0; +} + +static PyObject * +buffer_repr(PyBufferObject *self) +{ + char *status = self->b_readonly ? "read-only" : "read-write"; + + if ( self->b_base == NULL ) + return PyString_FromFormat("<%s buffer ptr %p, size %d at %p>", + status, + self->b_ptr, + self->b_size, + self); + else + return PyString_FromFormat( + "<%s buffer for %p, ptr %p, size %d at %p>", + status, + self->b_base, + self->b_ptr, + self->b_size, + self); +} + +static long +buffer_hash(PyBufferObject *self) +{ + register int len; + register unsigned char *p; + register long x; + + if ( self->b_hash != -1 ) + return self->b_hash; + + if ( !self->b_readonly ) + { + /* ### use different wording, since this is conditional? */ + PyErr_SetString(PyExc_TypeError, "unhashable type"); + return -1; + } + + len = self->b_size; + p = (unsigned char *) self->b_ptr; + x = *p << 7; + while (--len >= 0) + x = (1000003*x) ^ *p++; + x ^= self->b_size; + if (x == -1) + x = -2; + self->b_hash = x; + return x; +} + +static PyObject * +buffer_str(PyBufferObject *self) +{ + return PyString_FromStringAndSize(self->b_ptr, self->b_size); +} + +/* Sequence methods */ + +static int +buffer_length(PyBufferObject *self) +{ + return self->b_size; +} + +static PyObject * +buffer_concat(PyBufferObject *self, PyObject *other) +{ + PyBufferProcs *pb = other->ob_type->tp_as_buffer; + char *p1; + void *p2; + PyObject *ob; + int count; + + if ( pb == NULL || + pb->bf_getreadbuffer == NULL || + pb->bf_getsegcount == NULL ) + { + PyErr_BadArgument(); + return NULL; + } + if ( (*pb->bf_getsegcount)(other, NULL) != 1 ) + { + /* ### use a different exception type/message? */ + PyErr_SetString(PyExc_TypeError, + "single-segment buffer object expected"); + return NULL; + } + + /* optimize special case */ + if ( self->b_size == 0 ) + { + Py_INCREF(other); + return other; + } + + if ( (count = (*pb->bf_getreadbuffer)(other, 0, &p2)) < 0 ) + return NULL; + + ob = PyString_FromStringAndSize(NULL, self->b_size + count); + p1 = PyString_AS_STRING(ob); + memcpy(p1, self->b_ptr, self->b_size); + memcpy(p1 + self->b_size, p2, count); + + /* there is an extra byte in the string object, so this is safe */ + p1[self->b_size + count] = '\0'; + + return ob; +} + +static PyObject * +buffer_repeat(PyBufferObject *self, int count) +{ + PyObject *ob; + register char *p; + void *ptr = self->b_ptr; + int size = self->b_size; + + if ( count < 0 ) + count = 0; + ob = PyString_FromStringAndSize(NULL, size * count); + if ( ob == NULL ) + return NULL; + + p = PyString_AS_STRING(ob); + while ( count-- ) + { + memcpy(p, ptr, size); + p += size; + } + + /* there is an extra byte in the string object, so this is safe */ + *p = '\0'; + + return ob; +} + +static PyObject * +buffer_item(PyBufferObject *self, int idx) +{ + if ( idx < 0 || idx >= self->b_size ) + { + PyErr_SetString(PyExc_IndexError, "buffer index out of range"); + return NULL; + } + return PyString_FromStringAndSize((char *)self->b_ptr + idx, 1); +} + +static PyObject * +buffer_slice(PyBufferObject *self, int left, int right) +{ + if ( left < 0 ) + left = 0; + if ( right < 0 ) + right = 0; + if ( right > self->b_size ) + right = self->b_size; + if ( right < left ) + right = left; + return PyString_FromStringAndSize((char *)self->b_ptr + left, + right - left); +} + +static int +buffer_ass_item(PyBufferObject *self, int idx, PyObject *other) +{ + PyBufferProcs *pb; + void *p; + int count; + + if ( self->b_readonly ) { + PyErr_SetString(PyExc_TypeError, + "buffer is read-only"); + return -1; + } + + if (idx < 0 || idx >= self->b_size) { + PyErr_SetString(PyExc_IndexError, + "buffer assignment index out of range"); + return -1; + } + + pb = other ? other->ob_type->tp_as_buffer : NULL; + if ( pb == NULL || + pb->bf_getreadbuffer == NULL || + pb->bf_getsegcount == NULL ) + { + PyErr_BadArgument(); + return -1; + } + if ( (*pb->bf_getsegcount)(other, NULL) != 1 ) + { + /* ### use a different exception type/message? */ + PyErr_SetString(PyExc_TypeError, + "single-segment buffer object expected"); + return -1; + } + + if ( (count = (*pb->bf_getreadbuffer)(other, 0, &p)) < 0 ) + return -1; + if ( count != 1 ) { + PyErr_SetString(PyExc_TypeError, + "right operand must be a single byte"); + return -1; + } + + ((char *)self->b_ptr)[idx] = *(char *)p; + return 0; +} + +static int +buffer_ass_slice(PyBufferObject *self, int left, int right, PyObject *other) +{ + PyBufferProcs *pb; + void *p; + int slice_len; + int count; + + if ( self->b_readonly ) { + PyErr_SetString(PyExc_TypeError, + "buffer is read-only"); + return -1; + } + + pb = other ? other->ob_type->tp_as_buffer : NULL; + if ( pb == NULL || + pb->bf_getreadbuffer == NULL || + pb->bf_getsegcount == NULL ) + { + PyErr_BadArgument(); + return -1; + } + if ( (*pb->bf_getsegcount)(other, NULL) != 1 ) + { + /* ### use a different exception type/message? */ + PyErr_SetString(PyExc_TypeError, + "single-segment buffer object expected"); + return -1; + } + if ( (count = (*pb->bf_getreadbuffer)(other, 0, &p)) < 0 ) + return -1; + + if ( left < 0 ) + left = 0; + else if ( left > self->b_size ) + left = self->b_size; + if ( right < left ) + right = left; + else if ( right > self->b_size ) + right = self->b_size; + slice_len = right - left; + + if ( count != slice_len ) { + PyErr_SetString( + PyExc_TypeError, + "right operand length must match slice length"); + return -1; + } + + if ( slice_len ) + memcpy((char *)self->b_ptr + left, p, slice_len); + + return 0; +} + +/* Buffer methods */ + +static int +buffer_getreadbuf(PyBufferObject *self, int idx, void **pp) +{ + if ( idx != 0 ) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent buffer segment"); + return -1; + } + *pp = self->b_ptr; + return self->b_size; +} + +static int +buffer_getwritebuf(PyBufferObject *self, int idx, void **pp) +{ + if ( self->b_readonly ) + { + PyErr_SetString(PyExc_TypeError, "buffer is read-only"); + return -1; + } + return buffer_getreadbuf(self, idx, pp); +} + +static int +buffer_getsegcount(PyBufferObject *self, int *lenp) +{ + if ( lenp ) + *lenp = self->b_size; + return 1; +} + +static int +buffer_getcharbuf(PyBufferObject *self, int idx, const char **pp) +{ + if ( idx != 0 ) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent buffer segment"); + return -1; + } + *pp = (const char *)self->b_ptr; + return self->b_size; +} + + +static PySequenceMethods buffer_as_sequence = { + (inquiry)buffer_length, /*sq_length*/ + (binaryfunc)buffer_concat, /*sq_concat*/ + (intargfunc)buffer_repeat, /*sq_repeat*/ + (intargfunc)buffer_item, /*sq_item*/ + (intintargfunc)buffer_slice, /*sq_slice*/ + (intobjargproc)buffer_ass_item, /*sq_ass_item*/ + (intintobjargproc)buffer_ass_slice, /*sq_ass_slice*/ +}; + +static PyBufferProcs buffer_as_buffer = { + (getreadbufferproc)buffer_getreadbuf, + (getwritebufferproc)buffer_getwritebuf, + (getsegcountproc)buffer_getsegcount, + (getcharbufferproc)buffer_getcharbuf, +}; + +PyTypeObject PyBuffer_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "buffer", + sizeof(PyBufferObject), + 0, + (destructor)buffer_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)buffer_compare, /* tp_compare */ + (reprfunc)buffer_repr, /* tp_repr */ + 0, /* tp_as_number */ + &buffer_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)buffer_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)buffer_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + &buffer_as_buffer, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + buffer_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + buffer_new, /* tp_new */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/cellobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/cellobject.c new file mode 100644 index 00000000..5744ce8b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/cellobject.c @@ -0,0 +1,114 @@ +/* Cell object implementation */ + +#include "Python.h" + +PyObject * +PyCell_New(PyObject *obj) +{ + PyCellObject *op; + + op = (PyCellObject *)PyObject_GC_New(PyCellObject, &PyCell_Type); + op->ob_ref = obj; + Py_XINCREF(obj); + + _PyObject_GC_TRACK(op); + return (PyObject *)op; +} + +PyObject * +PyCell_Get(PyObject *op) +{ + if (!PyCell_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + Py_XINCREF(((PyCellObject*)op)->ob_ref); + return PyCell_GET(op); +} + +int +PyCell_Set(PyObject *op, PyObject *obj) +{ + if (!PyCell_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + Py_XDECREF(((PyCellObject*)op)->ob_ref); + Py_XINCREF(obj); + PyCell_SET(op, obj); + return 0; +} + +static void +cell_dealloc(PyCellObject *op) +{ + _PyObject_GC_UNTRACK(op); + Py_XDECREF(op->ob_ref); + PyObject_GC_Del(op); +} + +static int +cell_compare(PyCellObject *a, PyCellObject *b) +{ + if (a->ob_ref == NULL) { + if (b->ob_ref == NULL) + return 0; + return -1; + } else if (b->ob_ref == NULL) + return 1; + return PyObject_Compare(a->ob_ref, b->ob_ref); +} + +static PyObject * +cell_repr(PyCellObject *op) +{ + if (op->ob_ref == NULL) + return PyString_FromFormat("", op); + + return PyString_FromFormat("", + op, op->ob_ref->ob_type->tp_name, + op->ob_ref); +} + +static int +cell_traverse(PyCellObject *op, visitproc visit, void *arg) +{ + if (op->ob_ref) + return visit(op->ob_ref, arg); + return 0; +} + +static int +cell_clear(PyCellObject *op) +{ + Py_XDECREF(op->ob_ref); + op->ob_ref = NULL; + return 0; +} + +PyTypeObject PyCell_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "cell", + sizeof(PyCellObject), + 0, + (destructor)cell_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)cell_compare, /* tp_compare */ + (reprfunc)cell_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)cell_traverse, /* tp_traverse */ + (inquiry)cell_clear, /* tp_clear */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/classobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/classobject.c new file mode 100644 index 00000000..4d282cfd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/classobject.c @@ -0,0 +1,2516 @@ + +/* Class object implementation */ + +#include "Python.h" +#include "structmember.h" + +#define TP_DESCR_GET(t) \ + (PyType_HasFeature(t, Py_TPFLAGS_HAVE_CLASS) ? (t)->tp_descr_get : NULL) + + +/* Forward */ +static PyObject *class_lookup(PyClassObject *, PyObject *, + PyClassObject **); +static PyObject *instance_getattr1(PyInstanceObject *, PyObject *); +static PyObject *instance_getattr2(PyInstanceObject *, PyObject *); + +static PyObject *getattrstr, *setattrstr, *delattrstr; + + +PyObject * +PyClass_New(PyObject *bases, PyObject *dict, PyObject *name) + /* bases is NULL or tuple of classobjects! */ +{ + PyClassObject *op, *dummy; + static PyObject *docstr, *modstr, *namestr; + if (docstr == NULL) { + docstr= PyString_InternFromString("__doc__"); + if (docstr == NULL) + return NULL; + } + if (modstr == NULL) { + modstr= PyString_InternFromString("__module__"); + if (modstr == NULL) + return NULL; + } + if (namestr == NULL) { + namestr= PyString_InternFromString("__name__"); + if (namestr == NULL) + return NULL; + } + if (name == NULL || !PyString_Check(name)) { + PyErr_SetString(PyExc_TypeError, + "PyClass_New: name must be a string"); + return NULL; + } + if (dict == NULL || !PyDict_Check(dict)) { + PyErr_SetString(PyExc_TypeError, + "PyClass_New: dict must be a dictionary"); + return NULL; + } + if (PyDict_GetItem(dict, docstr) == NULL) { + if (PyDict_SetItem(dict, docstr, Py_None) < 0) + return NULL; + } + if (PyDict_GetItem(dict, modstr) == NULL) { + PyObject *globals = PyEval_GetGlobals(); + if (globals != NULL) { + PyObject *modname = PyDict_GetItem(globals, namestr); + if (modname != NULL) { + if (PyDict_SetItem(dict, modstr, modname) < 0) + return NULL; + } + } + } + if (bases == NULL) { + bases = PyTuple_New(0); + if (bases == NULL) + return NULL; + } + else { + int i, n; + PyObject *base; + if (!PyTuple_Check(bases)) { + PyErr_SetString(PyExc_TypeError, + "PyClass_New: bases must be a tuple"); + return NULL; + } + n = PyTuple_Size(bases); + for (i = 0; i < n; i++) { + base = PyTuple_GET_ITEM(bases, i); + if (!PyClass_Check(base)) { + if (PyCallable_Check( + (PyObject *) base->ob_type)) + return PyObject_CallFunction( + (PyObject *) base->ob_type, + "OOO", + name, + bases, + dict); + PyErr_SetString(PyExc_TypeError, + "PyClass_New: base must be a class"); + return NULL; + } + } + Py_INCREF(bases); + } + op = PyObject_GC_New(PyClassObject, &PyClass_Type); + if (op == NULL) { + Py_DECREF(bases); + return NULL; + } + op->cl_bases = bases; + Py_INCREF(dict); + op->cl_dict = dict; + Py_XINCREF(name); + op->cl_name = name; + if (getattrstr == NULL) { + getattrstr = PyString_InternFromString("__getattr__"); + setattrstr = PyString_InternFromString("__setattr__"); + delattrstr = PyString_InternFromString("__delattr__"); + } + op->cl_getattr = class_lookup(op, getattrstr, &dummy); + op->cl_setattr = class_lookup(op, setattrstr, &dummy); + op->cl_delattr = class_lookup(op, delattrstr, &dummy); + Py_XINCREF(op->cl_getattr); + Py_XINCREF(op->cl_setattr); + Py_XINCREF(op->cl_delattr); + _PyObject_GC_TRACK(op); + return (PyObject *) op; +} + +PyObject * +PyMethod_Function(PyObject *im) +{ + if (!PyMethod_Check(im)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyMethodObject *)im)->im_func; +} + +PyObject * +PyMethod_Self(PyObject *im) +{ + if (!PyMethod_Check(im)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyMethodObject *)im)->im_self; +} + +PyObject * +PyMethod_Class(PyObject *im) +{ + if (!PyMethod_Check(im)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyMethodObject *)im)->im_class; +} + +PyDoc_STRVAR(class_doc, +"classobj(name, bases, dict)\n\ +\n\ +Create a class object. The name must be a string; the second argument\n\ +a tuple of classes, and the third a dictionary."); + +static PyObject * +class_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *name, *bases, *dict; + static char *kwlist[] = {"name", "bases", "dict", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "SOO", kwlist, + &name, &bases, &dict)) + return NULL; + return PyClass_New(bases, dict, name); +} + +/* Class methods */ + +static void +class_dealloc(PyClassObject *op) +{ + _PyObject_GC_UNTRACK(op); + Py_DECREF(op->cl_bases); + Py_DECREF(op->cl_dict); + Py_XDECREF(op->cl_name); + Py_XDECREF(op->cl_getattr); + Py_XDECREF(op->cl_setattr); + Py_XDECREF(op->cl_delattr); + PyObject_GC_Del(op); +} + +static PyObject * +class_lookup(PyClassObject *cp, PyObject *name, PyClassObject **pclass) +{ + int i, n; + PyObject *value = PyDict_GetItem(cp->cl_dict, name); + if (value != NULL) { + *pclass = cp; + return value; + } + n = PyTuple_Size(cp->cl_bases); + for (i = 0; i < n; i++) { + /* XXX What if one of the bases is not a class? */ + PyObject *v = class_lookup( + (PyClassObject *) + PyTuple_GetItem(cp->cl_bases, i), name, pclass); + if (v != NULL) + return v; + } + return NULL; +} + +static PyObject * +class_getattr(register PyClassObject *op, PyObject *name) +{ + register PyObject *v; + register char *sname = PyString_AsString(name); + PyClassObject *class; + descrgetfunc f; + + if (sname[0] == '_' && sname[1] == '_') { + if (strcmp(sname, "__dict__") == 0) { + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, + "class.__dict__ not accessible in restricted mode"); + return NULL; + } + Py_INCREF(op->cl_dict); + return op->cl_dict; + } + if (strcmp(sname, "__bases__") == 0) { + Py_INCREF(op->cl_bases); + return op->cl_bases; + } + if (strcmp(sname, "__name__") == 0) { + if (op->cl_name == NULL) + v = Py_None; + else + v = op->cl_name; + Py_INCREF(v); + return v; + } + } + v = class_lookup(op, name, &class); + if (v == NULL) { + PyErr_Format(PyExc_AttributeError, + "class %.50s has no attribute '%.400s'", + PyString_AS_STRING(op->cl_name), sname); + return NULL; + } + f = TP_DESCR_GET(v->ob_type); + if (f == NULL) + Py_INCREF(v); + else + v = f(v, (PyObject *)NULL, (PyObject *)op); + return v; +} + +static void +set_slot(PyObject **slot, PyObject *v) +{ + PyObject *temp = *slot; + Py_XINCREF(v); + *slot = v; + Py_XDECREF(temp); +} + +static void +set_attr_slots(PyClassObject *c) +{ + PyClassObject *dummy; + + set_slot(&c->cl_getattr, class_lookup(c, getattrstr, &dummy)); + set_slot(&c->cl_setattr, class_lookup(c, setattrstr, &dummy)); + set_slot(&c->cl_delattr, class_lookup(c, delattrstr, &dummy)); +} + +static char * +set_dict(PyClassObject *c, PyObject *v) +{ + if (v == NULL || !PyDict_Check(v)) + return "__dict__ must be a dictionary object"; + set_slot(&c->cl_dict, v); + set_attr_slots(c); + return ""; +} + +static char * +set_bases(PyClassObject *c, PyObject *v) +{ + int i, n; + + if (v == NULL || !PyTuple_Check(v)) + return "__bases__ must be a tuple object"; + n = PyTuple_Size(v); + for (i = 0; i < n; i++) { + PyObject *x = PyTuple_GET_ITEM(v, i); + if (!PyClass_Check(x)) + return "__bases__ items must be classes"; + if (PyClass_IsSubclass(x, (PyObject *)c)) + return "a __bases__ item causes an inheritance cycle"; + } + set_slot(&c->cl_bases, v); + set_attr_slots(c); + return ""; +} + +static char * +set_name(PyClassObject *c, PyObject *v) +{ + if (v == NULL || !PyString_Check(v)) + return "__name__ must be a string object"; + if (strlen(PyString_AS_STRING(v)) != (size_t)PyString_GET_SIZE(v)) + return "__name__ must not contain null bytes"; + set_slot(&c->cl_name, v); + return ""; +} + +static int +class_setattr(PyClassObject *op, PyObject *name, PyObject *v) +{ + char *sname; + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, + "classes are read-only in restricted mode"); + return -1; + } + sname = PyString_AsString(name); + if (sname[0] == '_' && sname[1] == '_') { + int n = PyString_Size(name); + if (sname[n-1] == '_' && sname[n-2] == '_') { + char *err = NULL; + if (strcmp(sname, "__dict__") == 0) + err = set_dict(op, v); + else if (strcmp(sname, "__bases__") == 0) + err = set_bases(op, v); + else if (strcmp(sname, "__name__") == 0) + err = set_name(op, v); + else if (strcmp(sname, "__getattr__") == 0) + set_slot(&op->cl_getattr, v); + else if (strcmp(sname, "__setattr__") == 0) + set_slot(&op->cl_setattr, v); + else if (strcmp(sname, "__delattr__") == 0) + set_slot(&op->cl_delattr, v); + /* For the last three, we fall through to update the + dictionary as well. */ + if (err != NULL) { + if (*err == '\0') + return 0; + PyErr_SetString(PyExc_TypeError, err); + return -1; + } + } + } + if (v == NULL) { + int rv = PyDict_DelItem(op->cl_dict, name); + if (rv < 0) + PyErr_Format(PyExc_AttributeError, + "class %.50s has no attribute '%.400s'", + PyString_AS_STRING(op->cl_name), sname); + return rv; + } + else + return PyDict_SetItem(op->cl_dict, name, v); +} + +static PyObject * +class_repr(PyClassObject *op) +{ + PyObject *mod = PyDict_GetItemString(op->cl_dict, "__module__"); + char *name; + if (op->cl_name == NULL || !PyString_Check(op->cl_name)) + name = "?"; + else + name = PyString_AsString(op->cl_name); + if (mod == NULL || !PyString_Check(mod)) + return PyString_FromFormat("", name, op); + else + return PyString_FromFormat("", + PyString_AsString(mod), + name, op); +} + +static PyObject * +class_str(PyClassObject *op) +{ + PyObject *mod = PyDict_GetItemString(op->cl_dict, "__module__"); + PyObject *name = op->cl_name; + PyObject *res; + int m, n; + + if (name == NULL || !PyString_Check(name)) + return class_repr(op); + if (mod == NULL || !PyString_Check(mod)) { + Py_INCREF(name); + return name; + } + m = PyString_Size(mod); + n = PyString_Size(name); + res = PyString_FromStringAndSize((char *)NULL, m+1+n); + if (res != NULL) { + char *s = PyString_AsString(res); + memcpy(s, PyString_AsString(mod), m); + s += m; + *s++ = '.'; + memcpy(s, PyString_AsString(name), n); + } + return res; +} + +static int +class_traverse(PyClassObject *o, visitproc visit, void *arg) +{ + int err; + if (o->cl_bases) { + err = visit(o->cl_bases, arg); + if (err) + return err; + } + if (o->cl_dict) { + err = visit(o->cl_dict, arg); + if (err) + return err; + } + if (o->cl_name) { + err = visit(o->cl_name, arg); + if (err) + return err; + } + if (o->cl_getattr) { + err = visit(o->cl_getattr, arg); + if (err) + return err; + } + if (o->cl_setattr) { + err = visit(o->cl_setattr, arg); + if (err) + return err; + } + if (o->cl_delattr) { + err = visit(o->cl_delattr, arg); + if (err) + return err; + } + return 0; +} + +PyTypeObject PyClass_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "classobj", + sizeof(PyClassObject), + 0, + (destructor)class_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)class_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + PyInstance_New, /* tp_call */ + (reprfunc)class_str, /* tp_str */ + (getattrofunc)class_getattr, /* tp_getattro */ + (setattrofunc)class_setattr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + class_doc, /* tp_doc */ + (traverseproc)class_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + class_new, /* tp_new */ +}; + +int +PyClass_IsSubclass(PyObject *class, PyObject *base) +{ + int i, n; + PyClassObject *cp; + if (class == base) + return 1; + if (PyTuple_Check(base)) { + n = PyTuple_GET_SIZE(base); + for (i = 0; i < n; i++) { + if (PyClass_IsSubclass(class, PyTuple_GET_ITEM(base, i))) + return 1; + } + return 0; + } + if (class == NULL || !PyClass_Check(class)) + return 0; + cp = (PyClassObject *)class; + n = PyTuple_Size(cp->cl_bases); + for (i = 0; i < n; i++) { + if (PyClass_IsSubclass(PyTuple_GetItem(cp->cl_bases, i), base)) + return 1; + } + return 0; +} + + +/* Instance objects */ + +PyObject * +PyInstance_NewRaw(PyObject *klass, PyObject *dict) +{ + PyInstanceObject *inst; + + if (!PyClass_Check(klass)) { + PyErr_BadInternalCall(); + return NULL; + } + if (dict == NULL) { + dict = PyDict_New(); + if (dict == NULL) + return NULL; + } + else { + if (!PyDict_Check(dict)) { + PyErr_BadInternalCall(); + return NULL; + } + Py_INCREF(dict); + } + inst = PyObject_GC_New(PyInstanceObject, &PyInstance_Type); + if (inst == NULL) { + Py_DECREF(dict); + return NULL; + } + inst->in_weakreflist = NULL; + Py_INCREF(klass); + inst->in_class = (PyClassObject *)klass; + inst->in_dict = dict; + _PyObject_GC_TRACK(inst); + return (PyObject *)inst; +} + +PyObject * +PyInstance_New(PyObject *klass, PyObject *arg, PyObject *kw) +{ + register PyInstanceObject *inst; + PyObject *init; + static PyObject *initstr; + + inst = (PyInstanceObject *) PyInstance_NewRaw(klass, NULL); + if (inst == NULL) + return NULL; + if (initstr == NULL) + initstr = PyString_InternFromString("__init__"); + init = instance_getattr2(inst, initstr); + if (init == NULL) { + if (PyErr_Occurred()) { + Py_DECREF(inst); + return NULL; + } + if ((arg != NULL && (!PyTuple_Check(arg) || + PyTuple_Size(arg) != 0)) + || (kw != NULL && (!PyDict_Check(kw) || + PyDict_Size(kw) != 0))) { + PyErr_SetString(PyExc_TypeError, + "this constructor takes no arguments"); + Py_DECREF(inst); + inst = NULL; + } + } + else { + PyObject *res = PyEval_CallObjectWithKeywords(init, arg, kw); + Py_DECREF(init); + if (res == NULL) { + Py_DECREF(inst); + inst = NULL; + } + else { + if (res != Py_None) { + PyErr_SetString(PyExc_TypeError, + "__init__() should return None"); + Py_DECREF(inst); + inst = NULL; + } + Py_DECREF(res); + } + } + return (PyObject *)inst; +} + +/* Instance methods */ + +PyDoc_STRVAR(instance_doc, +"instance(class[, dict])\n\ +\n\ +Create an instance without calling its __init__() method.\n\ +The class must be a classic class.\n\ +If present, dict must be a dictionary or None."); + +static PyObject * +instance_new(PyTypeObject* type, PyObject* args, PyObject *kw) +{ + PyObject *klass; + PyObject *dict = Py_None; + + if (!PyArg_ParseTuple(args, "O!|O:instance", + &PyClass_Type, &klass, &dict)) + return NULL; + + if (dict == Py_None) + dict = NULL; + else if (!PyDict_Check(dict)) { + PyErr_SetString(PyExc_TypeError, + "instance() second arg must be dictionary or None"); + return NULL; + } + return PyInstance_NewRaw(klass, dict); +} + + +static void +instance_dealloc(register PyInstanceObject *inst) +{ + PyObject *error_type, *error_value, *error_traceback; + PyObject *del; + static PyObject *delstr; + + _PyObject_GC_UNTRACK(inst); + if (inst->in_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) inst); + + /* Temporarily resurrect the object. */ + assert(inst->ob_type == &PyInstance_Type); + assert(inst->ob_refcnt == 0); + inst->ob_refcnt = 1; + + /* Save the current exception, if any. */ + PyErr_Fetch(&error_type, &error_value, &error_traceback); + /* Execute __del__ method, if any. */ + if (delstr == NULL) + delstr = PyString_InternFromString("__del__"); + if ((del = instance_getattr2(inst, delstr)) != NULL) { + PyObject *res = PyEval_CallObject(del, (PyObject *)NULL); + if (res == NULL) + PyErr_WriteUnraisable(del); + else + Py_DECREF(res); + Py_DECREF(del); + } + /* Restore the saved exception. */ + PyErr_Restore(error_type, error_value, error_traceback); + + /* Undo the temporary resurrection; can't use DECREF here, it would + * cause a recursive call. + */ + assert(inst->ob_refcnt > 0); + if (--inst->ob_refcnt == 0) { + Py_DECREF(inst->in_class); + Py_XDECREF(inst->in_dict); + PyObject_GC_Del(inst); + } + else { + int refcnt = inst->ob_refcnt; + /* __del__ resurrected it! Make it look like the original + * Py_DECREF never happened. + */ + _Py_NewReference((PyObject *)inst); + inst->ob_refcnt = refcnt; + _PyObject_GC_TRACK(inst); + /* If Py_REF_DEBUG, the original decref dropped _Py_RefTotal, + * but _Py_NewReference bumped it again, so that's a wash. + * If Py_TRACE_REFS, _Py_NewReference re-added self to the + * object chain, so no more to do there either. + * If COUNT_ALLOCS, the original decref bumped tp_frees, and + * _Py_NewReference bumped tp_allocs: both of those need to + * be undone. + */ +#ifdef COUNT_ALLOCS + --inst->ob_type->tp_frees; + --inst->ob_type->tp_allocs; +#endif + } +} + +static PyObject * +instance_getattr1(register PyInstanceObject *inst, PyObject *name) +{ + register PyObject *v; + register char *sname = PyString_AsString(name); + if (sname[0] == '_' && sname[1] == '_') { + if (strcmp(sname, "__dict__") == 0) { + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, + "instance.__dict__ not accessible in restricted mode"); + return NULL; + } + Py_INCREF(inst->in_dict); + return inst->in_dict; + } + if (strcmp(sname, "__class__") == 0) { + Py_INCREF(inst->in_class); + return (PyObject *)inst->in_class; + } + } + v = instance_getattr2(inst, name); + if (v == NULL && !PyErr_Occurred()) { + PyErr_Format(PyExc_AttributeError, + "%.50s instance has no attribute '%.400s'", + PyString_AS_STRING(inst->in_class->cl_name), sname); + } + return v; +} + +static PyObject * +instance_getattr2(register PyInstanceObject *inst, PyObject *name) +{ + register PyObject *v; + PyClassObject *class; + descrgetfunc f; + + v = PyDict_GetItem(inst->in_dict, name); + if (v != NULL) { + Py_INCREF(v); + return v; + } + v = class_lookup(inst->in_class, name, &class); + if (v != NULL) { + Py_INCREF(v); + f = TP_DESCR_GET(v->ob_type); + if (f != NULL) { + PyObject *w = f(v, (PyObject *)inst, + (PyObject *)(inst->in_class)); + Py_DECREF(v); + v = w; + } + } + return v; +} + +static PyObject * +instance_getattr(register PyInstanceObject *inst, PyObject *name) +{ + register PyObject *func, *res; + res = instance_getattr1(inst, name); + if (res == NULL && (func = inst->in_class->cl_getattr) != NULL) { + PyObject *args; + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + args = Py_BuildValue("(OO)", inst, name); + if (args == NULL) + return NULL; + res = PyEval_CallObject(func, args); + Py_DECREF(args); + } + return res; +} + +/* See classobject.h comments: this only does dict lookups, and is always + * safe to call. + */ +PyObject * +_PyInstance_Lookup(PyObject *pinst, PyObject *name) +{ + PyObject *v; + PyClassObject *class; + PyInstanceObject *inst; /* pinst cast to the right type */ + + assert(PyInstance_Check(pinst)); + inst = (PyInstanceObject *)pinst; + + assert(PyString_Check(name)); + + v = PyDict_GetItem(inst->in_dict, name); + if (v == NULL) + v = class_lookup(inst->in_class, name, &class); + return v; +} + +static int +instance_setattr1(PyInstanceObject *inst, PyObject *name, PyObject *v) +{ + if (v == NULL) { + int rv = PyDict_DelItem(inst->in_dict, name); + if (rv < 0) + PyErr_Format(PyExc_AttributeError, + "%.50s instance has no attribute '%.400s'", + PyString_AS_STRING(inst->in_class->cl_name), + PyString_AS_STRING(name)); + return rv; + } + else + return PyDict_SetItem(inst->in_dict, name, v); +} + +static int +instance_setattr(PyInstanceObject *inst, PyObject *name, PyObject *v) +{ + PyObject *func, *args, *res, *tmp; + char *sname = PyString_AsString(name); + if (sname[0] == '_' && sname[1] == '_') { + int n = PyString_Size(name); + if (sname[n-1] == '_' && sname[n-2] == '_') { + if (strcmp(sname, "__dict__") == 0) { + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, + "__dict__ not accessible in restricted mode"); + return -1; + } + if (v == NULL || !PyDict_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "__dict__ must be set to a dictionary"); + return -1; + } + tmp = inst->in_dict; + Py_INCREF(v); + inst->in_dict = v; + Py_DECREF(tmp); + return 0; + } + if (strcmp(sname, "__class__") == 0) { + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, + "__class__ not accessible in restricted mode"); + return -1; + } + if (v == NULL || !PyClass_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "__class__ must be set to a class"); + return -1; + } + tmp = (PyObject *)(inst->in_class); + Py_INCREF(v); + inst->in_class = (PyClassObject *)v; + Py_DECREF(tmp); + return 0; + } + } + } + if (v == NULL) + func = inst->in_class->cl_delattr; + else + func = inst->in_class->cl_setattr; + if (func == NULL) + return instance_setattr1(inst, name, v); + if (v == NULL) + args = Py_BuildValue("(OO)", inst, name); + else + args = Py_BuildValue("(OOO)", inst, name, v); + if (args == NULL) + return -1; + res = PyEval_CallObject(func, args); + Py_DECREF(args); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static PyObject * +instance_repr(PyInstanceObject *inst) +{ + PyObject *func; + PyObject *res; + static PyObject *reprstr; + + if (reprstr == NULL) + reprstr = PyString_InternFromString("__repr__"); + func = instance_getattr(inst, reprstr); + if (func == NULL) { + PyObject *classname, *mod; + char *cname; + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + classname = inst->in_class->cl_name; + mod = PyDict_GetItemString(inst->in_class->cl_dict, + "__module__"); + if (classname != NULL && PyString_Check(classname)) + cname = PyString_AsString(classname); + else + cname = "?"; + if (mod == NULL || !PyString_Check(mod)) + return PyString_FromFormat("", + cname, inst); + else + return PyString_FromFormat("<%s.%s instance at %p>", + PyString_AsString(mod), + cname, inst); + } + res = PyEval_CallObject(func, (PyObject *)NULL); + Py_DECREF(func); + return res; +} + +static PyObject * +instance_str(PyInstanceObject *inst) +{ + PyObject *func; + PyObject *res; + static PyObject *strstr; + + if (strstr == NULL) + strstr = PyString_InternFromString("__str__"); + func = instance_getattr(inst, strstr); + if (func == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + return instance_repr(inst); + } + res = PyEval_CallObject(func, (PyObject *)NULL); + Py_DECREF(func); + return res; +} + +static long +instance_hash(PyInstanceObject *inst) +{ + PyObject *func; + PyObject *res; + long outcome; + static PyObject *hashstr, *eqstr, *cmpstr; + + if (hashstr == NULL) + hashstr = PyString_InternFromString("__hash__"); + func = instance_getattr(inst, hashstr); + if (func == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; + PyErr_Clear(); + /* If there is no __eq__ and no __cmp__ method, we hash on the + address. If an __eq__ or __cmp__ method exists, there must + be a __hash__. */ + if (eqstr == NULL) + eqstr = PyString_InternFromString("__eq__"); + func = instance_getattr(inst, eqstr); + if (func == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; + PyErr_Clear(); + if (cmpstr == NULL) + cmpstr = PyString_InternFromString("__cmp__"); + func = instance_getattr(inst, cmpstr); + if (func == NULL) { + if (!PyErr_ExceptionMatches( + PyExc_AttributeError)) + return -1; + PyErr_Clear(); + return _Py_HashPointer(inst); + } + } + Py_XDECREF(func); + PyErr_SetString(PyExc_TypeError, "unhashable instance"); + return -1; + } + res = PyEval_CallObject(func, (PyObject *)NULL); + Py_DECREF(func); + if (res == NULL) + return -1; + if (PyInt_Check(res)) { + outcome = PyInt_AsLong(res); + if (outcome == -1) + outcome = -2; + } + else { + PyErr_SetString(PyExc_TypeError, + "__hash__() should return an int"); + outcome = -1; + } + Py_DECREF(res); + return outcome; +} + +static int +instance_traverse(PyInstanceObject *o, visitproc visit, void *arg) +{ + int err; + if (o->in_class) { + err = visit((PyObject *)(o->in_class), arg); + if (err) + return err; + } + if (o->in_dict) { + err = visit(o->in_dict, arg); + if (err) + return err; + } + return 0; +} + +static PyObject *getitemstr, *setitemstr, *delitemstr, *lenstr; +static PyObject *iterstr, *nextstr; + +static int +instance_length(PyInstanceObject *inst) +{ + PyObject *func; + PyObject *res; + int outcome; + + if (lenstr == NULL) + lenstr = PyString_InternFromString("__len__"); + func = instance_getattr(inst, lenstr); + if (func == NULL) + return -1; + res = PyEval_CallObject(func, (PyObject *)NULL); + Py_DECREF(func); + if (res == NULL) + return -1; + if (PyInt_Check(res)) { + outcome = PyInt_AsLong(res); + if (outcome < 0) + PyErr_SetString(PyExc_ValueError, + "__len__() should return >= 0"); + } + else { + PyErr_SetString(PyExc_TypeError, + "__len__() should return an int"); + outcome = -1; + } + Py_DECREF(res); + return outcome; +} + +static PyObject * +instance_subscript(PyInstanceObject *inst, PyObject *key) +{ + PyObject *func; + PyObject *arg; + PyObject *res; + + if (getitemstr == NULL) + getitemstr = PyString_InternFromString("__getitem__"); + func = instance_getattr(inst, getitemstr); + if (func == NULL) + return NULL; + arg = Py_BuildValue("(O)", key); + if (arg == NULL) { + Py_DECREF(func); + return NULL; + } + res = PyEval_CallObject(func, arg); + Py_DECREF(func); + Py_DECREF(arg); + return res; +} + +static int +instance_ass_subscript(PyInstanceObject *inst, PyObject *key, PyObject *value) +{ + PyObject *func; + PyObject *arg; + PyObject *res; + + if (value == NULL) { + if (delitemstr == NULL) + delitemstr = PyString_InternFromString("__delitem__"); + func = instance_getattr(inst, delitemstr); + } + else { + if (setitemstr == NULL) + setitemstr = PyString_InternFromString("__setitem__"); + func = instance_getattr(inst, setitemstr); + } + if (func == NULL) + return -1; + if (value == NULL) + arg = Py_BuildValue("(O)", key); + else + arg = Py_BuildValue("(OO)", key, value); + if (arg == NULL) { + Py_DECREF(func); + return -1; + } + res = PyEval_CallObject(func, arg); + Py_DECREF(func); + Py_DECREF(arg); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static PyMappingMethods instance_as_mapping = { + (inquiry)instance_length, /* mp_length */ + (binaryfunc)instance_subscript, /* mp_subscript */ + (objobjargproc)instance_ass_subscript, /* mp_ass_subscript */ +}; + +static PyObject * +instance_item(PyInstanceObject *inst, int i) +{ + PyObject *func, *arg, *res; + + if (getitemstr == NULL) + getitemstr = PyString_InternFromString("__getitem__"); + func = instance_getattr(inst, getitemstr); + if (func == NULL) + return NULL; + arg = Py_BuildValue("(i)", i); + if (arg == NULL) { + Py_DECREF(func); + return NULL; + } + res = PyEval_CallObject(func, arg); + Py_DECREF(func); + Py_DECREF(arg); + return res; +} + +static PyObject * +sliceobj_from_intint(int i, int j) +{ + PyObject *start, *end, *res; + + start = PyInt_FromLong((long)i); + if (!start) + return NULL; + + end = PyInt_FromLong((long)j); + if (!end) { + Py_DECREF(start); + return NULL; + } + res = PySlice_New(start, end, NULL); + Py_DECREF(start); + Py_DECREF(end); + return res; +} + + +static PyObject * +instance_slice(PyInstanceObject *inst, int i, int j) +{ + PyObject *func, *arg, *res; + static PyObject *getslicestr; + + if (getslicestr == NULL) + getslicestr = PyString_InternFromString("__getslice__"); + func = instance_getattr(inst, getslicestr); + + if (func == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + + if (getitemstr == NULL) + getitemstr = PyString_InternFromString("__getitem__"); + func = instance_getattr(inst, getitemstr); + if (func == NULL) + return NULL; + arg = Py_BuildValue("(N)", sliceobj_from_intint(i, j)); + } else + arg = Py_BuildValue("(ii)", i, j); + + if (arg == NULL) { + Py_DECREF(func); + return NULL; + } + res = PyEval_CallObject(func, arg); + Py_DECREF(func); + Py_DECREF(arg); + return res; +} + +static int +instance_ass_item(PyInstanceObject *inst, int i, PyObject *item) +{ + PyObject *func, *arg, *res; + + if (item == NULL) { + if (delitemstr == NULL) + delitemstr = PyString_InternFromString("__delitem__"); + func = instance_getattr(inst, delitemstr); + } + else { + if (setitemstr == NULL) + setitemstr = PyString_InternFromString("__setitem__"); + func = instance_getattr(inst, setitemstr); + } + if (func == NULL) + return -1; + if (item == NULL) + arg = Py_BuildValue("i", i); + else + arg = Py_BuildValue("(iO)", i, item); + if (arg == NULL) { + Py_DECREF(func); + return -1; + } + res = PyEval_CallObject(func, arg); + Py_DECREF(func); + Py_DECREF(arg); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static int +instance_ass_slice(PyInstanceObject *inst, int i, int j, PyObject *value) +{ + PyObject *func, *arg, *res; + static PyObject *setslicestr, *delslicestr; + + if (value == NULL) { + if (delslicestr == NULL) + delslicestr = + PyString_InternFromString("__delslice__"); + func = instance_getattr(inst, delslicestr); + if (func == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; + PyErr_Clear(); + if (delitemstr == NULL) + delitemstr = + PyString_InternFromString("__delitem__"); + func = instance_getattr(inst, delitemstr); + if (func == NULL) + return -1; + + arg = Py_BuildValue("(N)", + sliceobj_from_intint(i, j)); + } else + arg = Py_BuildValue("(ii)", i, j); + } + else { + if (setslicestr == NULL) + setslicestr = + PyString_InternFromString("__setslice__"); + func = instance_getattr(inst, setslicestr); + if (func == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; + PyErr_Clear(); + if (setitemstr == NULL) + setitemstr = + PyString_InternFromString("__setitem__"); + func = instance_getattr(inst, setitemstr); + if (func == NULL) + return -1; + + arg = Py_BuildValue("(NO)", + sliceobj_from_intint(i, j), value); + } else + arg = Py_BuildValue("(iiO)", i, j, value); + } + if (arg == NULL) { + Py_DECREF(func); + return -1; + } + res = PyEval_CallObject(func, arg); + Py_DECREF(func); + Py_DECREF(arg); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static int +instance_contains(PyInstanceObject *inst, PyObject *member) +{ + static PyObject *__contains__; + PyObject *func; + + /* Try __contains__ first. + * If that can't be done, try iterator-based searching. + */ + + if(__contains__ == NULL) { + __contains__ = PyString_InternFromString("__contains__"); + if(__contains__ == NULL) + return -1; + } + func = instance_getattr(inst, __contains__); + if (func) { + PyObject *res; + int ret; + PyObject *arg = Py_BuildValue("(O)", member); + if(arg == NULL) { + Py_DECREF(func); + return -1; + } + res = PyEval_CallObject(func, arg); + Py_DECREF(func); + Py_DECREF(arg); + if(res == NULL) + return -1; + ret = PyObject_IsTrue(res); + Py_DECREF(res); + return ret; + } + + /* Couldn't find __contains__. */ + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + /* Assume the failure was simply due to that there is no + * __contains__ attribute, and try iterating instead. + */ + PyErr_Clear(); + return _PySequence_IterSearch((PyObject *)inst, member, + PY_ITERSEARCH_CONTAINS); + } + else + return -1; +} + +static PySequenceMethods +instance_as_sequence = { + (inquiry)instance_length, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + (intargfunc)instance_item, /* sq_item */ + (intintargfunc)instance_slice, /* sq_slice */ + (intobjargproc)instance_ass_item, /* sq_ass_item */ + (intintobjargproc)instance_ass_slice, /* sq_ass_slice */ + (objobjproc)instance_contains, /* sq_contains */ +}; + +static PyObject * +generic_unary_op(PyInstanceObject *self, PyObject *methodname) +{ + PyObject *func, *res; + + if ((func = instance_getattr(self, methodname)) == NULL) + return NULL; + res = PyEval_CallObject(func, (PyObject *)NULL); + Py_DECREF(func); + return res; +} + +static PyObject * +generic_binary_op(PyObject *v, PyObject *w, char *opname) +{ + PyObject *result; + PyObject *args; + PyObject *func = PyObject_GetAttrString(v, opname); + if (func == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + args = Py_BuildValue("(O)", w); + if (args == NULL) { + Py_DECREF(func); + return NULL; + } + result = PyEval_CallObject(func, args); + Py_DECREF(args); + Py_DECREF(func); + return result; +} + + +static PyObject *coerce_obj; + +/* Try one half of a binary operator involving a class instance. */ +static PyObject * +half_binop(PyObject *v, PyObject *w, char *opname, binaryfunc thisfunc, + int swapped) +{ + PyObject *args; + PyObject *coercefunc; + PyObject *coerced = NULL; + PyObject *v1; + PyObject *result; + + if (!PyInstance_Check(v)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + if (coerce_obj == NULL) { + coerce_obj = PyString_InternFromString("__coerce__"); + if (coerce_obj == NULL) + return NULL; + } + coercefunc = PyObject_GetAttr(v, coerce_obj); + if (coercefunc == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + return generic_binary_op(v, w, opname); + } + + args = Py_BuildValue("(O)", w); + if (args == NULL) { + Py_DECREF(coercefunc); + return NULL; + } + coerced = PyEval_CallObject(coercefunc, args); + Py_DECREF(args); + Py_DECREF(coercefunc); + if (coerced == NULL) { + return NULL; + } + if (coerced == Py_None || coerced == Py_NotImplemented) { + Py_DECREF(coerced); + return generic_binary_op(v, w, opname); + } + if (!PyTuple_Check(coerced) || PyTuple_Size(coerced) != 2) { + Py_DECREF(coerced); + PyErr_SetString(PyExc_TypeError, + "coercion should return None or 2-tuple"); + return NULL; + } + v1 = PyTuple_GetItem(coerced, 0); + w = PyTuple_GetItem(coerced, 1); + if (v1->ob_type == v->ob_type && PyInstance_Check(v)) { + /* prevent recursion if __coerce__ returns self as the first + * argument */ + result = generic_binary_op(v1, w, opname); + } else { + if (swapped) + result = (thisfunc)(w, v1); + else + result = (thisfunc)(v1, w); + } + Py_DECREF(coerced); + return result; +} + +/* Implement a binary operator involving at least one class instance. */ +static PyObject * +do_binop(PyObject *v, PyObject *w, char *opname, char *ropname, + binaryfunc thisfunc) +{ + PyObject *result = half_binop(v, w, opname, thisfunc, 0); + if (result == Py_NotImplemented) { + Py_DECREF(result); + result = half_binop(w, v, ropname, thisfunc, 1); + } + return result; +} + +static PyObject * +do_binop_inplace(PyObject *v, PyObject *w, char *iopname, char *opname, + char *ropname, binaryfunc thisfunc) +{ + PyObject *result = half_binop(v, w, iopname, thisfunc, 0); + if (result == Py_NotImplemented) { + Py_DECREF(result); + result = do_binop(v, w, opname, ropname, thisfunc); + } + return result; +} + +static int +instance_coerce(PyObject **pv, PyObject **pw) +{ + PyObject *v = *pv; + PyObject *w = *pw; + PyObject *coercefunc; + PyObject *args; + PyObject *coerced; + + if (coerce_obj == NULL) { + coerce_obj = PyString_InternFromString("__coerce__"); + if (coerce_obj == NULL) + return -1; + } + coercefunc = PyObject_GetAttr(v, coerce_obj); + if (coercefunc == NULL) { + /* No __coerce__ method */ + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; + PyErr_Clear(); + return 1; + } + /* Has __coerce__ method: call it */ + args = Py_BuildValue("(O)", w); + if (args == NULL) { + return -1; + } + coerced = PyEval_CallObject(coercefunc, args); + Py_DECREF(args); + Py_DECREF(coercefunc); + if (coerced == NULL) { + /* __coerce__ call raised an exception */ + return -1; + } + if (coerced == Py_None || coerced == Py_NotImplemented) { + /* __coerce__ says "I can't do it" */ + Py_DECREF(coerced); + return 1; + } + if (!PyTuple_Check(coerced) || PyTuple_Size(coerced) != 2) { + /* __coerce__ return value is malformed */ + Py_DECREF(coerced); + PyErr_SetString(PyExc_TypeError, + "coercion should return None or 2-tuple"); + return -1; + } + /* __coerce__ returned two new values */ + *pv = PyTuple_GetItem(coerced, 0); + *pw = PyTuple_GetItem(coerced, 1); + Py_INCREF(*pv); + Py_INCREF(*pw); + Py_DECREF(coerced); + return 0; +} + +#define UNARY(funcname, methodname) \ +static PyObject *funcname(PyInstanceObject *self) { \ + static PyObject *o; \ + if (o == NULL) o = PyString_InternFromString(methodname); \ + return generic_unary_op(self, o); \ +} + +#define BINARY(f, m, n) \ +static PyObject *f(PyObject *v, PyObject *w) { \ + return do_binop(v, w, "__" m "__", "__r" m "__", n); \ +} + +#define BINARY_INPLACE(f, m, n) \ +static PyObject *f(PyObject *v, PyObject *w) { \ + return do_binop_inplace(v, w, "__i" m "__", "__" m "__", \ + "__r" m "__", n); \ +} + +UNARY(instance_neg, "__neg__") +UNARY(instance_pos, "__pos__") +UNARY(instance_abs, "__abs__") + +BINARY(instance_or, "or", PyNumber_Or) +BINARY(instance_and, "and", PyNumber_And) +BINARY(instance_xor, "xor", PyNumber_Xor) +BINARY(instance_lshift, "lshift", PyNumber_Lshift) +BINARY(instance_rshift, "rshift", PyNumber_Rshift) +BINARY(instance_add, "add", PyNumber_Add) +BINARY(instance_sub, "sub", PyNumber_Subtract) +BINARY(instance_mul, "mul", PyNumber_Multiply) +BINARY(instance_div, "div", PyNumber_Divide) +BINARY(instance_mod, "mod", PyNumber_Remainder) +BINARY(instance_divmod, "divmod", PyNumber_Divmod) +BINARY(instance_floordiv, "floordiv", PyNumber_FloorDivide) +BINARY(instance_truediv, "truediv", PyNumber_TrueDivide) + +BINARY_INPLACE(instance_ior, "or", PyNumber_InPlaceOr) +BINARY_INPLACE(instance_ixor, "xor", PyNumber_InPlaceXor) +BINARY_INPLACE(instance_iand, "and", PyNumber_InPlaceAnd) +BINARY_INPLACE(instance_ilshift, "lshift", PyNumber_InPlaceLshift) +BINARY_INPLACE(instance_irshift, "rshift", PyNumber_InPlaceRshift) +BINARY_INPLACE(instance_iadd, "add", PyNumber_InPlaceAdd) +BINARY_INPLACE(instance_isub, "sub", PyNumber_InPlaceSubtract) +BINARY_INPLACE(instance_imul, "mul", PyNumber_InPlaceMultiply) +BINARY_INPLACE(instance_idiv, "div", PyNumber_InPlaceDivide) +BINARY_INPLACE(instance_imod, "mod", PyNumber_InPlaceRemainder) +BINARY_INPLACE(instance_ifloordiv, "floordiv", PyNumber_InPlaceFloorDivide) +BINARY_INPLACE(instance_itruediv, "truediv", PyNumber_InPlaceTrueDivide) + +/* Try a 3-way comparison, returning an int; v is an instance. Return: + -2 for an exception; + -1 if v < w; + 0 if v == w; + 1 if v > w; + 2 if this particular 3-way comparison is not implemented or undefined. +*/ +static int +half_cmp(PyObject *v, PyObject *w) +{ + static PyObject *cmp_obj; + PyObject *args; + PyObject *cmp_func; + PyObject *result; + long l; + + assert(PyInstance_Check(v)); + + if (cmp_obj == NULL) { + cmp_obj = PyString_InternFromString("__cmp__"); + if (cmp_obj == NULL) + return -2; + } + + cmp_func = PyObject_GetAttr(v, cmp_obj); + if (cmp_func == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -2; + PyErr_Clear(); + return 2; + } + + args = Py_BuildValue("(O)", w); + if (args == NULL) { + Py_DECREF(cmp_func); + return -2; + } + + result = PyEval_CallObject(cmp_func, args); + Py_DECREF(args); + Py_DECREF(cmp_func); + + if (result == NULL) + return -2; + + if (result == Py_NotImplemented) { + Py_DECREF(result); + return 2; + } + + l = PyInt_AsLong(result); + Py_DECREF(result); + if (l == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "comparison did not return an int"); + return -2; + } + + return l < 0 ? -1 : l > 0 ? 1 : 0; +} + +/* Try a 3-way comparison, returning an int; either v or w is an instance. + We first try a coercion. Return: + -2 for an exception; + -1 if v < w; + 0 if v == w; + 1 if v > w; + 2 if this particular 3-way comparison is not implemented or undefined. + THIS IS ONLY CALLED FROM object.c! +*/ +static int +instance_compare(PyObject *v, PyObject *w) +{ + int c; + + c = PyNumber_CoerceEx(&v, &w); + if (c < 0) + return -2; + if (c == 0) { + /* If neither is now an instance, use regular comparison */ + if (!PyInstance_Check(v) && !PyInstance_Check(w)) { + c = PyObject_Compare(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (PyErr_Occurred()) + return -2; + return c < 0 ? -1 : c > 0 ? 1 : 0; + } + } + else { + /* The coercion didn't do anything. + Treat this the same as returning v and w unchanged. */ + Py_INCREF(v); + Py_INCREF(w); + } + + if (PyInstance_Check(v)) { + c = half_cmp(v, w); + if (c <= 1) { + Py_DECREF(v); + Py_DECREF(w); + return c; + } + } + if (PyInstance_Check(w)) { + c = half_cmp(w, v); + if (c <= 1) { + Py_DECREF(v); + Py_DECREF(w); + if (c >= -1) + c = -c; + return c; + } + } + Py_DECREF(v); + Py_DECREF(w); + return 2; +} + +static int +instance_nonzero(PyInstanceObject *self) +{ + PyObject *func, *res; + long outcome; + static PyObject *nonzerostr; + + if (nonzerostr == NULL) + nonzerostr = PyString_InternFromString("__nonzero__"); + if ((func = instance_getattr(self, nonzerostr)) == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; + PyErr_Clear(); + if (lenstr == NULL) + lenstr = PyString_InternFromString("__len__"); + if ((func = instance_getattr(self, lenstr)) == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; + PyErr_Clear(); + /* Fall back to the default behavior: + all instances are nonzero */ + return 1; + } + } + res = PyEval_CallObject(func, (PyObject *)NULL); + Py_DECREF(func); + if (res == NULL) + return -1; + if (!PyInt_Check(res)) { + Py_DECREF(res); + PyErr_SetString(PyExc_TypeError, + "__nonzero__ should return an int"); + return -1; + } + outcome = PyInt_AsLong(res); + Py_DECREF(res); + if (outcome < 0) { + PyErr_SetString(PyExc_ValueError, + "__nonzero__ should return >= 0"); + return -1; + } + return outcome > 0; +} + +UNARY(instance_invert, "__invert__") +UNARY(instance_int, "__int__") +UNARY(instance_long, "__long__") +UNARY(instance_float, "__float__") +UNARY(instance_oct, "__oct__") +UNARY(instance_hex, "__hex__") + +static PyObject * +bin_power(PyObject *v, PyObject *w) +{ + return PyNumber_Power(v, w, Py_None); +} + +/* This version is for ternary calls only (z != None) */ +static PyObject * +instance_pow(PyObject *v, PyObject *w, PyObject *z) +{ + if (z == Py_None) { + return do_binop(v, w, "__pow__", "__rpow__", bin_power); + } + else { + PyObject *func; + PyObject *args; + PyObject *result; + + /* XXX Doesn't do coercions... */ + func = PyObject_GetAttrString(v, "__pow__"); + if (func == NULL) + return NULL; + args = Py_BuildValue("(OO)", w, z); + if (args == NULL) { + Py_DECREF(func); + return NULL; + } + result = PyEval_CallObject(func, args); + Py_DECREF(func); + Py_DECREF(args); + return result; + } +} + +static PyObject * +bin_inplace_power(PyObject *v, PyObject *w) +{ + return PyNumber_InPlacePower(v, w, Py_None); +} + + +static PyObject * +instance_ipow(PyObject *v, PyObject *w, PyObject *z) +{ + if (z == Py_None) { + return do_binop_inplace(v, w, "__ipow__", "__pow__", + "__rpow__", bin_inplace_power); + } + else { + /* XXX Doesn't do coercions... */ + PyObject *func; + PyObject *args; + PyObject *result; + + func = PyObject_GetAttrString(v, "__ipow__"); + if (func == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + return instance_pow(v, w, z); + } + args = Py_BuildValue("(OO)", w, z); + if (args == NULL) { + Py_DECREF(func); + return NULL; + } + result = PyEval_CallObject(func, args); + Py_DECREF(func); + Py_DECREF(args); + return result; + } +} + + +/* Map rich comparison operators to their __xx__ namesakes */ +#define NAME_OPS 6 +static PyObject **name_op = NULL; + +static int +init_name_op(void) +{ + int i; + char *_name_op[] = { + "__lt__", + "__le__", + "__eq__", + "__ne__", + "__gt__", + "__ge__", + }; + + name_op = (PyObject **)malloc(sizeof(PyObject *) * NAME_OPS); + if (name_op == NULL) + return -1; + for (i = 0; i < NAME_OPS; ++i) { + name_op[i] = PyString_InternFromString(_name_op[i]); + if (name_op[i] == NULL) + return -1; + } + return 0; +} + +static PyObject * +half_richcompare(PyObject *v, PyObject *w, int op) +{ + PyObject *method; + PyObject *args; + PyObject *res; + + assert(PyInstance_Check(v)); + + if (name_op == NULL) { + if (init_name_op() < 0) + return NULL; + } + /* If the instance doesn't define an __getattr__ method, use + instance_getattr2 directly because it will not set an + exception on failure. */ + if (((PyInstanceObject *)v)->in_class->cl_getattr == NULL) + method = instance_getattr2((PyInstanceObject *)v, + name_op[op]); + else + method = PyObject_GetAttr(v, name_op[op]); + if (method == NULL) { + if (PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + } + res = Py_NotImplemented; + Py_INCREF(res); + return res; + } + + args = Py_BuildValue("(O)", w); + if (args == NULL) { + Py_DECREF(method); + return NULL; + } + + res = PyEval_CallObject(method, args); + Py_DECREF(args); + Py_DECREF(method); + + return res; +} + +/* Map rich comparison operators to their swapped version, e.g. LT --> GT */ +static int swapped_op[] = {Py_GT, Py_GE, Py_EQ, Py_NE, Py_LT, Py_LE}; + +static PyObject * +instance_richcompare(PyObject *v, PyObject *w, int op) +{ + PyObject *res; + + if (PyInstance_Check(v)) { + res = half_richcompare(v, w, op); + if (res != Py_NotImplemented) + return res; + Py_DECREF(res); + } + + if (PyInstance_Check(w)) { + res = half_richcompare(w, v, swapped_op[op]); + if (res != Py_NotImplemented) + return res; + Py_DECREF(res); + } + + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + + +/* Get the iterator */ +static PyObject * +instance_getiter(PyInstanceObject *self) +{ + PyObject *func; + + if (iterstr == NULL) { + iterstr = PyString_InternFromString("__iter__"); + if (iterstr == NULL) + return NULL; + } + if (getitemstr == NULL) { + getitemstr = PyString_InternFromString("__getitem__"); + if (getitemstr == NULL) + return NULL; + } + + if ((func = instance_getattr(self, iterstr)) != NULL) { + PyObject *res = PyEval_CallObject(func, (PyObject *)NULL); + Py_DECREF(func); + if (res != NULL && !PyIter_Check(res)) { + PyErr_Format(PyExc_TypeError, + "__iter__ returned non-iterator " + "of type '%.100s'", + res->ob_type->tp_name); + Py_DECREF(res); + res = NULL; + } + return res; + } + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + if ((func = instance_getattr(self, getitemstr)) == NULL) { + PyErr_SetString(PyExc_TypeError, + "iteration over non-sequence"); + return NULL; + } + Py_DECREF(func); + return PySeqIter_New((PyObject *)self); +} + + +/* Call the iterator's next */ +static PyObject * +instance_iternext(PyInstanceObject *self) +{ + PyObject *func; + + if (nextstr == NULL) + nextstr = PyString_InternFromString("next"); + + if ((func = instance_getattr(self, nextstr)) != NULL) { + PyObject *res = PyEval_CallObject(func, (PyObject *)NULL); + Py_DECREF(func); + if (res != NULL) { + return res; + } + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + PyErr_Clear(); + return NULL; + } + return NULL; + } + PyErr_SetString(PyExc_TypeError, "instance has no next() method"); + return NULL; +} + +static PyObject * +instance_call(PyObject *func, PyObject *arg, PyObject *kw) +{ + PyThreadState *tstate = PyThreadState_GET(); + PyObject *res, *call = PyObject_GetAttrString(func, "__call__"); + if (call == NULL) { + PyInstanceObject *inst = (PyInstanceObject*) func; + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + PyErr_Format(PyExc_AttributeError, + "%.200s instance has no __call__ method", + PyString_AsString(inst->in_class->cl_name)); + return NULL; + } + /* We must check and increment the recursion depth here. Scenario: + class A: + pass + A.__call__ = A() # that's right + a = A() # ok + a() # infinite recursion + This bounces between instance_call() and PyObject_Call() without + ever hitting eval_frame() (which has the main recursion check). */ + if (tstate->recursion_depth++ > Py_GetRecursionLimit()) { + PyErr_SetString(PyExc_RuntimeError, + "maximum __call__ recursion depth exceeded"); + res = NULL; + } + else + res = PyObject_Call(call, arg, kw); + tstate->recursion_depth--; + Py_DECREF(call); + return res; +} + + +static PyNumberMethods instance_as_number = { + (binaryfunc)instance_add, /* nb_add */ + (binaryfunc)instance_sub, /* nb_subtract */ + (binaryfunc)instance_mul, /* nb_multiply */ + (binaryfunc)instance_div, /* nb_divide */ + (binaryfunc)instance_mod, /* nb_remainder */ + (binaryfunc)instance_divmod, /* nb_divmod */ + (ternaryfunc)instance_pow, /* nb_power */ + (unaryfunc)instance_neg, /* nb_negative */ + (unaryfunc)instance_pos, /* nb_positive */ + (unaryfunc)instance_abs, /* nb_absolute */ + (inquiry)instance_nonzero, /* nb_nonzero */ + (unaryfunc)instance_invert, /* nb_invert */ + (binaryfunc)instance_lshift, /* nb_lshift */ + (binaryfunc)instance_rshift, /* nb_rshift */ + (binaryfunc)instance_and, /* nb_and */ + (binaryfunc)instance_xor, /* nb_xor */ + (binaryfunc)instance_or, /* nb_or */ + (coercion)instance_coerce, /* nb_coerce */ + (unaryfunc)instance_int, /* nb_int */ + (unaryfunc)instance_long, /* nb_long */ + (unaryfunc)instance_float, /* nb_float */ + (unaryfunc)instance_oct, /* nb_oct */ + (unaryfunc)instance_hex, /* nb_hex */ + (binaryfunc)instance_iadd, /* nb_inplace_add */ + (binaryfunc)instance_isub, /* nb_inplace_subtract */ + (binaryfunc)instance_imul, /* nb_inplace_multiply */ + (binaryfunc)instance_idiv, /* nb_inplace_divide */ + (binaryfunc)instance_imod, /* nb_inplace_remainder */ + (ternaryfunc)instance_ipow, /* nb_inplace_power */ + (binaryfunc)instance_ilshift, /* nb_inplace_lshift */ + (binaryfunc)instance_irshift, /* nb_inplace_rshift */ + (binaryfunc)instance_iand, /* nb_inplace_and */ + (binaryfunc)instance_ixor, /* nb_inplace_xor */ + (binaryfunc)instance_ior, /* nb_inplace_or */ + (binaryfunc)instance_floordiv, /* nb_floor_divide */ + (binaryfunc)instance_truediv, /* nb_true_divide */ + (binaryfunc)instance_ifloordiv, /* nb_inplace_floor_divide */ + (binaryfunc)instance_itruediv, /* nb_inplace_true_divide */ +}; + +PyTypeObject PyInstance_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "instance", + sizeof(PyInstanceObject), + 0, + (destructor)instance_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + instance_compare, /* tp_compare */ + (reprfunc)instance_repr, /* tp_repr */ + &instance_as_number, /* tp_as_number */ + &instance_as_sequence, /* tp_as_sequence */ + &instance_as_mapping, /* tp_as_mapping */ + (hashfunc)instance_hash, /* tp_hash */ + instance_call, /* tp_call */ + (reprfunc)instance_str, /* tp_str */ + (getattrofunc)instance_getattr, /* tp_getattro */ + (setattrofunc)instance_setattr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_CHECKTYPES,/*tp_flags*/ + instance_doc, /* tp_doc */ + (traverseproc)instance_traverse, /* tp_traverse */ + 0, /* tp_clear */ + instance_richcompare, /* tp_richcompare */ + offsetof(PyInstanceObject, in_weakreflist), /* tp_weaklistoffset */ + (getiterfunc)instance_getiter, /* tp_iter */ + (iternextfunc)instance_iternext, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + instance_new, /* tp_new */ +}; + + +/* Instance method objects are used for two purposes: + (a) as bound instance methods (returned by instancename.methodname) + (b) as unbound methods (returned by ClassName.methodname) + In case (b), im_self is NULL +*/ + +static PyMethodObject *free_list; + +PyObject * +PyMethod_New(PyObject *func, PyObject *self, PyObject *class) +{ + register PyMethodObject *im; + if (!PyCallable_Check(func)) { + PyErr_BadInternalCall(); + return NULL; + } + im = free_list; + if (im != NULL) { + free_list = (PyMethodObject *)(im->im_self); + PyObject_INIT(im, &PyMethod_Type); + } + else { + im = PyObject_GC_New(PyMethodObject, &PyMethod_Type); + if (im == NULL) + return NULL; + } + im->im_weakreflist = NULL; + Py_INCREF(func); + im->im_func = func; + Py_XINCREF(self); + im->im_self = self; + Py_XINCREF(class); + im->im_class = class; + _PyObject_GC_TRACK(im); + return (PyObject *)im; +} + +/* Descriptors for PyMethod attributes */ + +/* im_class, im_func and im_self are stored in the PyMethod object */ + +#define OFF(x) offsetof(PyMethodObject, x) + +static PyMemberDef instancemethod_memberlist[] = { + {"im_class", T_OBJECT, OFF(im_class), READONLY|RESTRICTED, + "the class associated with a method"}, + {"im_func", T_OBJECT, OFF(im_func), READONLY|RESTRICTED, + "the function (or other callable) implementing a method"}, + {"im_self", T_OBJECT, OFF(im_self), READONLY|RESTRICTED, + "the instance to which a method is bound; None for unbound methods"}, + {NULL} /* Sentinel */ +}; + +/* The getattr() implementation for PyMethod objects is similar to + PyObject_GenericGetAttr(), but instead of looking in __dict__ it + asks im_self for the attribute. Then the error handling is a bit + different because we want to preserve the exception raised by the + delegate, unless we have an alternative from our class. */ + +static PyObject * +instancemethod_getattro(PyObject *obj, PyObject *name) +{ + PyMethodObject *im = (PyMethodObject *)obj; + PyTypeObject *tp = obj->ob_type; + PyObject *descr = NULL, *res; + descrgetfunc f = NULL; + + if (PyType_HasFeature(tp, Py_TPFLAGS_HAVE_CLASS)) { + if (tp->tp_dict == NULL) { + if (PyType_Ready(tp) < 0) + return NULL; + } + descr = _PyType_Lookup(tp, name); + } + + f = NULL; + if (descr != NULL) { + f = TP_DESCR_GET(descr->ob_type); + if (f != NULL && PyDescr_IsData(descr)) + return f(descr, obj, (PyObject *)obj->ob_type); + } + + res = PyObject_GetAttr(im->im_func, name); + if (res != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError)) + return res; + + if (f != NULL) { + PyErr_Clear(); + return f(descr, obj, (PyObject *)obj->ob_type); + } + + if (descr != NULL) { + PyErr_Clear(); + Py_INCREF(descr); + return descr; + } + + assert(PyErr_Occurred()); + return NULL; +} + +PyDoc_STRVAR(instancemethod_doc, +"instancemethod(function, instance, class)\n\ +\n\ +Create an instance method object."); + +static PyObject * +instancemethod_new(PyTypeObject* type, PyObject* args, PyObject *kw) +{ + PyObject *func; + PyObject *self; + PyObject *classObj = NULL; + + if (!PyArg_UnpackTuple(args, "instancemethod", 2, 3, + &func, &self, &classObj)) + return NULL; + if (!PyCallable_Check(func)) { + PyErr_SetString(PyExc_TypeError, + "first argument must be callable"); + return NULL; + } + if (self == Py_None) + self = NULL; + return PyMethod_New(func, self, classObj); +} + +static void +instancemethod_dealloc(register PyMethodObject *im) +{ + _PyObject_GC_UNTRACK(im); + if (im->im_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *)im); + Py_DECREF(im->im_func); + Py_XDECREF(im->im_self); + Py_XDECREF(im->im_class); + im->im_self = (PyObject *)free_list; + free_list = im; +} + +static int +instancemethod_compare(PyMethodObject *a, PyMethodObject *b) +{ + if (a->im_self != b->im_self) + return (a->im_self < b->im_self) ? -1 : 1; + return PyObject_Compare(a->im_func, b->im_func); +} + +static PyObject * +instancemethod_repr(PyMethodObject *a) +{ + PyObject *self = a->im_self; + PyObject *func = a->im_func; + PyObject *klass = a->im_class; + PyObject *funcname = NULL, *klassname = NULL, *result = NULL; + char *sfuncname = "?", *sklassname = "?"; + + funcname = PyObject_GetAttrString(func, "__name__"); + if (funcname == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + } + else if (!PyString_Check(funcname)) { + Py_DECREF(funcname); + funcname = NULL; + } + else + sfuncname = PyString_AS_STRING(funcname); + if (klass == NULL) + klassname = NULL; + else { + klassname = PyObject_GetAttrString(klass, "__name__"); + if (klassname == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + } + else if (!PyString_Check(klassname)) { + Py_DECREF(klassname); + klassname = NULL; + } + else + sklassname = PyString_AS_STRING(klassname); + } + if (self == NULL) + result = PyString_FromFormat("", + sklassname, sfuncname); + else { + /* XXX Shouldn't use repr() here! */ + PyObject *selfrepr = PyObject_Repr(self); + if (selfrepr == NULL) + goto fail; + if (!PyString_Check(selfrepr)) { + Py_DECREF(selfrepr); + goto fail; + } + result = PyString_FromFormat("", + sklassname, sfuncname, + PyString_AS_STRING(selfrepr)); + Py_DECREF(selfrepr); + } + fail: + Py_XDECREF(funcname); + Py_XDECREF(klassname); + return result; +} + +static long +instancemethod_hash(PyMethodObject *a) +{ + long x, y; + if (a->im_self == NULL) + x = PyObject_Hash(Py_None); + else + x = PyObject_Hash(a->im_self); + if (x == -1) + return -1; + y = PyObject_Hash(a->im_func); + if (y == -1) + return -1; + return x ^ y; +} + +static int +instancemethod_traverse(PyMethodObject *im, visitproc visit, void *arg) +{ + int err; + if (im->im_func) { + err = visit(im->im_func, arg); + if (err) + return err; + } + if (im->im_self) { + err = visit(im->im_self, arg); + if (err) + return err; + } + if (im->im_class) { + err = visit(im->im_class, arg); + if (err) + return err; + } + return 0; +} + +static void +getclassname(PyObject *class, char *buf, int bufsize) +{ + PyObject *name; + + assert(bufsize > 1); + strcpy(buf, "?"); /* Default outcome */ + if (class == NULL) + return; + name = PyObject_GetAttrString(class, "__name__"); + if (name == NULL) { + /* This function cannot return an exception */ + PyErr_Clear(); + return; + } + if (PyString_Check(name)) { + strncpy(buf, PyString_AS_STRING(name), bufsize); + buf[bufsize-1] = '\0'; + } + Py_DECREF(name); +} + +static void +getinstclassname(PyObject *inst, char *buf, int bufsize) +{ + PyObject *class; + + if (inst == NULL) { + assert(bufsize > 0 && (size_t)bufsize > strlen("nothing")); + strcpy(buf, "nothing"); + return; + } + + class = PyObject_GetAttrString(inst, "__class__"); + if (class == NULL) { + /* This function cannot return an exception */ + PyErr_Clear(); + class = (PyObject *)(inst->ob_type); + Py_INCREF(class); + } + getclassname(class, buf, bufsize); + Py_XDECREF(class); +} + +static PyObject * +instancemethod_call(PyObject *func, PyObject *arg, PyObject *kw) +{ + PyObject *self = PyMethod_GET_SELF(func); + PyObject *class = PyMethod_GET_CLASS(func); + PyObject *result; + + func = PyMethod_GET_FUNCTION(func); + if (self == NULL) { + /* Unbound methods must be called with an instance of + the class (or a derived class) as first argument */ + int ok; + if (PyTuple_Size(arg) >= 1) + self = PyTuple_GET_ITEM(arg, 0); + if (self == NULL) + ok = 0; + else { + ok = PyObject_IsInstance(self, class); + if (ok < 0) + return NULL; + } + if (!ok) { + char clsbuf[256]; + char instbuf[256]; + getclassname(class, clsbuf, sizeof(clsbuf)); + getinstclassname(self, instbuf, sizeof(instbuf)); + PyErr_Format(PyExc_TypeError, + "unbound method %s%s must be called with " + "%s instance as first argument " + "(got %s%s instead)", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + clsbuf, + instbuf, + self == NULL ? "" : " instance"); + return NULL; + } + Py_INCREF(arg); + } + else { + int argcount = PyTuple_Size(arg); + PyObject *newarg = PyTuple_New(argcount + 1); + int i; + if (newarg == NULL) + return NULL; + Py_INCREF(self); + PyTuple_SET_ITEM(newarg, 0, self); + for (i = 0; i < argcount; i++) { + PyObject *v = PyTuple_GET_ITEM(arg, i); + Py_XINCREF(v); + PyTuple_SET_ITEM(newarg, i+1, v); + } + arg = newarg; + } + result = PyObject_Call((PyObject *)func, arg, kw); + Py_DECREF(arg); + return result; +} + +static PyObject * +instancemethod_descr_get(PyObject *meth, PyObject *obj, PyObject *cls) +{ + /* Don't rebind an already bound method, or an unbound method + of a class that's not a base class of cls. */ + + if (PyMethod_GET_SELF(meth) != NULL) { + /* Already bound */ + Py_INCREF(meth); + return meth; + } + /* No, it is an unbound method */ + if (PyMethod_GET_CLASS(meth) != NULL && cls != NULL) { + /* Do subclass test. If it fails, return meth unchanged. */ + int ok = PyObject_IsSubclass(cls, PyMethod_GET_CLASS(meth)); + if (ok < 0) + return NULL; + if (!ok) { + Py_INCREF(meth); + return meth; + } + } + /* Bind it to obj */ + return PyMethod_New(PyMethod_GET_FUNCTION(meth), obj, cls); +} + +PyTypeObject PyMethod_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "instancemethod", + sizeof(PyMethodObject), + 0, + (destructor)instancemethod_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)instancemethod_compare, /* tp_compare */ + (reprfunc)instancemethod_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)instancemethod_hash, /* tp_hash */ + instancemethod_call, /* tp_call */ + 0, /* tp_str */ + (getattrofunc)instancemethod_getattro, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + instancemethod_doc, /* tp_doc */ + (traverseproc)instancemethod_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(PyMethodObject, im_weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + instancemethod_memberlist, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + instancemethod_descr_get, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + instancemethod_new, /* tp_new */ +}; + +/* Clear out the free list */ + +void +PyMethod_Fini(void) +{ + while (free_list) { + PyMethodObject *im = free_list; + free_list = (PyMethodObject *)(im->im_self); + PyObject_GC_Del(im); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/cobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/cobject.c new file mode 100644 index 00000000..6e066722 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/cobject.c @@ -0,0 +1,146 @@ + +/* Wrap void* pointers to be passed between C modules */ + +#include "Python.h" + + +/* Declarations for objects of type PyCObject */ + +typedef void (*destructor1)(void *); +typedef void (*destructor2)(void *, void*); + +typedef struct { + PyObject_HEAD + void *cobject; + void *desc; + void (*destructor)(void *); +} PyCObject; + +PyObject * +PyCObject_FromVoidPtr(void *cobj, void (*destr)(void *)) +{ + PyCObject *self; + + self = PyObject_NEW(PyCObject, &PyCObject_Type); + if (self == NULL) + return NULL; + self->cobject=cobj; + self->destructor=destr; + self->desc=NULL; + + return (PyObject *)self; +} + +PyObject * +PyCObject_FromVoidPtrAndDesc(void *cobj, void *desc, + void (*destr)(void *, void *)) +{ + PyCObject *self; + + if (!desc) { + PyErr_SetString(PyExc_TypeError, + "PyCObject_FromVoidPtrAndDesc called with null" + " description"); + return NULL; + } + self = PyObject_NEW(PyCObject, &PyCObject_Type); + if (self == NULL) + return NULL; + self->cobject = cobj; + self->destructor = (destructor1)destr; + self->desc = desc; + + return (PyObject *)self; +} + +void * +PyCObject_AsVoidPtr(PyObject *self) +{ + if (self) { + if (self->ob_type == &PyCObject_Type) + return ((PyCObject *)self)->cobject; + PyErr_SetString(PyExc_TypeError, + "PyCObject_AsVoidPtr with non-C-object"); + } + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_TypeError, + "PyCObject_AsVoidPtr called with null pointer"); + return NULL; +} + +void * +PyCObject_GetDesc(PyObject *self) +{ + if (self) { + if (self->ob_type == &PyCObject_Type) + return ((PyCObject *)self)->desc; + PyErr_SetString(PyExc_TypeError, + "PyCObject_GetDesc with non-C-object"); + } + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_TypeError, + "PyCObject_GetDesc called with null pointer"); + return NULL; +} + +void * +PyCObject_Import(char *module_name, char *name) +{ + PyObject *m, *c; + void *r = NULL; + + if ((m = PyImport_ImportModule(module_name))) { + if ((c = PyObject_GetAttrString(m,name))) { + r = PyCObject_AsVoidPtr(c); + Py_DECREF(c); + } + Py_DECREF(m); + } + return r; +} + +static void +PyCObject_dealloc(PyCObject *self) +{ + if (self->destructor) { + if(self->desc) + ((destructor2)(self->destructor))(self->cobject, self->desc); + else + (self->destructor)(self->cobject); + } + PyObject_DEL(self); +} + + +PyDoc_STRVAR(PyCObject_Type__doc__, +"C objects to be exported from one extension module to another\n\ +\n\ +C objects are used for communication between extension modules. They\n\ +provide a way for an extension module to export a C interface to other\n\ +extension modules, so that extension modules can use the Python import\n\ +mechanism to link to one another."); + +PyTypeObject PyCObject_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "PyCObject", /*tp_name*/ + sizeof(PyCObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PyCObject_dealloc, /*tp_dealloc*/ + (printfunc)0, /*tp_print*/ + (getattrfunc)0, /*tp_getattr*/ + (setattrfunc)0, /*tp_setattr*/ + (cmpfunc)0, /*tp_compare*/ + (reprfunc)0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)0, /*tp_hash*/ + (ternaryfunc)0, /*tp_call*/ + (reprfunc)0, /*tp_str*/ + + /* Space for future expansion */ + 0L,0L,0L,0L, + PyCObject_Type__doc__ /* Documentation string */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/complexobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/complexobject.c new file mode 100644 index 00000000..35f36b99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/complexobject.c @@ -0,0 +1,1022 @@ + +/* Complex object implementation */ + +/* Borrows heavily from floatobject.c */ + +/* Submitted by Jim Hugunin */ + +#include "Python.h" +#include "structmember.h" + +#ifndef WITHOUT_COMPLEX + +/* Precisions used by repr() and str(), respectively. + + The repr() precision (17 significant decimal digits) is the minimal number + that is guaranteed to have enough precision so that if the number is read + back in the exact same binary value is recreated. This is true for IEEE + floating point by design, and also happens to work for all other modern + hardware. + + The str() precision is chosen so that in most cases, the rounding noise + created by various operations is suppressed, while giving plenty of + precision for practical use. +*/ + +#define PREC_REPR 17 +#define PREC_STR 12 + +/* elementary operations on complex numbers */ + +static Py_complex c_1 = {1., 0.}; + +Py_complex +c_sum(Py_complex a, Py_complex b) +{ + Py_complex r; + r.real = a.real + b.real; + r.imag = a.imag + b.imag; + return r; +} + +Py_complex +c_diff(Py_complex a, Py_complex b) +{ + Py_complex r; + r.real = a.real - b.real; + r.imag = a.imag - b.imag; + return r; +} + +Py_complex +c_neg(Py_complex a) +{ + Py_complex r; + r.real = -a.real; + r.imag = -a.imag; + return r; +} + +Py_complex +c_prod(Py_complex a, Py_complex b) +{ + Py_complex r; + r.real = a.real*b.real - a.imag*b.imag; + r.imag = a.real*b.imag + a.imag*b.real; + return r; +} + +Py_complex +c_quot(Py_complex a, Py_complex b) +{ + /****************************************************************** + This was the original algorithm. It's grossly prone to spurious + overflow and underflow errors. It also merrily divides by 0 despite + checking for that(!). The code still serves a doc purpose here, as + the algorithm following is a simple by-cases transformation of this + one: + + Py_complex r; + double d = b.real*b.real + b.imag*b.imag; + if (d == 0.) + errno = EDOM; + r.real = (a.real*b.real + a.imag*b.imag)/d; + r.imag = (a.imag*b.real - a.real*b.imag)/d; + return r; + ******************************************************************/ + + /* This algorithm is better, and is pretty obvious: first divide the + * numerators and denominator by whichever of {b.real, b.imag} has + * larger magnitude. The earliest reference I found was to CACM + * Algorithm 116 (Complex Division, Robert L. Smith, Stanford + * University). As usual, though, we're still ignoring all IEEE + * endcases. + */ + Py_complex r; /* the result */ + const double abs_breal = b.real < 0 ? -b.real : b.real; + const double abs_bimag = b.imag < 0 ? -b.imag : b.imag; + + if (abs_breal >= abs_bimag) { + /* divide tops and bottom by b.real */ + if (abs_breal == 0.0) { + errno = EDOM; + r.real = r.imag = 0.0; + } + else { + const double ratio = b.imag / b.real; + const double denom = b.real + b.imag * ratio; + r.real = (a.real + a.imag * ratio) / denom; + r.imag = (a.imag - a.real * ratio) / denom; + } + } + else { + /* divide tops and bottom by b.imag */ + const double ratio = b.real / b.imag; + const double denom = b.real * ratio + b.imag; + assert(b.imag != 0.0); + r.real = (a.real * ratio + a.imag) / denom; + r.imag = (a.imag * ratio - a.real) / denom; + } + return r; +} + +Py_complex +c_pow(Py_complex a, Py_complex b) +{ + Py_complex r; + double vabs,len,at,phase; + if (b.real == 0. && b.imag == 0.) { + r.real = 1.; + r.imag = 0.; + } + else if (a.real == 0. && a.imag == 0.) { + if (b.imag != 0. || b.real < 0.) + errno = EDOM; + r.real = 0.; + r.imag = 0.; + } + else { + vabs = hypot(a.real,a.imag); + len = pow(vabs,b.real); + at = atan2(a.imag, a.real); + phase = at*b.real; + if (b.imag != 0.0) { + len /= exp(at*b.imag); + phase += b.imag*log(vabs); + } + r.real = len*cos(phase); + r.imag = len*sin(phase); + } + return r; +} + +static Py_complex +c_powu(Py_complex x, long n) +{ + Py_complex r, p; + long mask = 1; + r = c_1; + p = x; + while (mask > 0 && n >= mask) { + if (n & mask) + r = c_prod(r,p); + mask <<= 1; + p = c_prod(p,p); + } + return r; +} + +static Py_complex +c_powi(Py_complex x, long n) +{ + Py_complex cn; + + if (n > 100 || n < -100) { + cn.real = (double) n; + cn.imag = 0.; + return c_pow(x,cn); + } + else if (n > 0) + return c_powu(x,n); + else + return c_quot(c_1,c_powu(x,-n)); + +} + +static PyObject * +complex_subtype_from_c_complex(PyTypeObject *type, Py_complex cval) +{ + PyObject *op; + + op = PyType_GenericAlloc(type, 0); + if (op != NULL) + ((PyComplexObject *)op)->cval = cval; + return op; +} + +PyObject * +PyComplex_FromCComplex(Py_complex cval) +{ + register PyComplexObject *op; + + /* Inline PyObject_New */ + op = (PyComplexObject *) PyObject_MALLOC(sizeof(PyComplexObject)); + if (op == NULL) + return PyErr_NoMemory(); + PyObject_INIT(op, &PyComplex_Type); + op->cval = cval; + return (PyObject *) op; +} + +static PyObject * +complex_subtype_from_doubles(PyTypeObject *type, double real, double imag) +{ + Py_complex c; + c.real = real; + c.imag = imag; + return complex_subtype_from_c_complex(type, c); +} + +PyObject * +PyComplex_FromDoubles(double real, double imag) +{ + Py_complex c; + c.real = real; + c.imag = imag; + return PyComplex_FromCComplex(c); +} + +double +PyComplex_RealAsDouble(PyObject *op) +{ + if (PyComplex_Check(op)) { + return ((PyComplexObject *)op)->cval.real; + } + else { + return PyFloat_AsDouble(op); + } +} + +double +PyComplex_ImagAsDouble(PyObject *op) +{ + if (PyComplex_Check(op)) { + return ((PyComplexObject *)op)->cval.imag; + } + else { + return 0.0; + } +} + +Py_complex +PyComplex_AsCComplex(PyObject *op) +{ + Py_complex cv; + if (PyComplex_Check(op)) { + return ((PyComplexObject *)op)->cval; + } + else { + cv.real = PyFloat_AsDouble(op); + cv.imag = 0.; + return cv; + } +} + +static void +complex_dealloc(PyObject *op) +{ + op->ob_type->tp_free(op); +} + + +static void +complex_to_buf(char *buf, int bufsz, PyComplexObject *v, int precision) +{ + if (v->cval.real == 0.) + PyOS_snprintf(buf, bufsz, "%.*gj", + precision, v->cval.imag); + else + PyOS_snprintf(buf, bufsz, "(%.*g%+.*gj)", + precision, v->cval.real, + precision, v->cval.imag); +} + +static int +complex_print(PyComplexObject *v, FILE *fp, int flags) +{ + char buf[100]; + complex_to_buf(buf, sizeof(buf), v, + (flags & Py_PRINT_RAW) ? PREC_STR : PREC_REPR); + fputs(buf, fp); + return 0; +} + +static PyObject * +complex_repr(PyComplexObject *v) +{ + char buf[100]; + complex_to_buf(buf, sizeof(buf), v, PREC_REPR); + return PyString_FromString(buf); +} + +static PyObject * +complex_str(PyComplexObject *v) +{ + char buf[100]; + complex_to_buf(buf, sizeof(buf), v, PREC_STR); + return PyString_FromString(buf); +} + +static long +complex_hash(PyComplexObject *v) +{ + long hashreal, hashimag, combined; + hashreal = _Py_HashDouble(v->cval.real); + if (hashreal == -1) + return -1; + hashimag = _Py_HashDouble(v->cval.imag); + if (hashimag == -1) + return -1; + /* Note: if the imaginary part is 0, hashimag is 0 now, + * so the following returns hashreal unchanged. This is + * important because numbers of different types that + * compare equal must have the same hash value, so that + * hash(x + 0*j) must equal hash(x). + */ + combined = hashreal + 1000003 * hashimag; + if (combined == -1) + combined = -2; + return combined; +} + +static PyObject * +complex_add(PyComplexObject *v, PyComplexObject *w) +{ + Py_complex result; + PyFPE_START_PROTECT("complex_add", return 0) + result = c_sum(v->cval,w->cval); + PyFPE_END_PROTECT(result) + return PyComplex_FromCComplex(result); +} + +static PyObject * +complex_sub(PyComplexObject *v, PyComplexObject *w) +{ + Py_complex result; + PyFPE_START_PROTECT("complex_sub", return 0) + result = c_diff(v->cval,w->cval); + PyFPE_END_PROTECT(result) + return PyComplex_FromCComplex(result); +} + +static PyObject * +complex_mul(PyComplexObject *v, PyComplexObject *w) +{ + Py_complex result; + PyFPE_START_PROTECT("complex_mul", return 0) + result = c_prod(v->cval,w->cval); + PyFPE_END_PROTECT(result) + return PyComplex_FromCComplex(result); +} + +static PyObject * +complex_div(PyComplexObject *v, PyComplexObject *w) +{ + Py_complex quot; + PyFPE_START_PROTECT("complex_div", return 0) + errno = 0; + quot = c_quot(v->cval,w->cval); + PyFPE_END_PROTECT(quot) + if (errno == EDOM) { + PyErr_SetString(PyExc_ZeroDivisionError, "complex division"); + return NULL; + } + return PyComplex_FromCComplex(quot); +} + +static PyObject * +complex_classic_div(PyComplexObject *v, PyComplexObject *w) +{ + Py_complex quot; + + if (Py_DivisionWarningFlag >= 2 && + PyErr_Warn(PyExc_DeprecationWarning, + "classic complex division") < 0) + return NULL; + + PyFPE_START_PROTECT("complex_classic_div", return 0) + errno = 0; + quot = c_quot(v->cval,w->cval); + PyFPE_END_PROTECT(quot) + if (errno == EDOM) { + PyErr_SetString(PyExc_ZeroDivisionError, "complex division"); + return NULL; + } + return PyComplex_FromCComplex(quot); +} + +static PyObject * +complex_remainder(PyComplexObject *v, PyComplexObject *w) +{ + Py_complex div, mod; + + if (PyErr_Warn(PyExc_DeprecationWarning, + "complex divmod(), // and % are deprecated") < 0) + return NULL; + + errno = 0; + div = c_quot(v->cval,w->cval); /* The raw divisor value. */ + if (errno == EDOM) { + PyErr_SetString(PyExc_ZeroDivisionError, "complex remainder"); + return NULL; + } + div.real = floor(div.real); /* Use the floor of the real part. */ + div.imag = 0.0; + mod = c_diff(v->cval, c_prod(w->cval, div)); + + return PyComplex_FromCComplex(mod); +} + + +static PyObject * +complex_divmod(PyComplexObject *v, PyComplexObject *w) +{ + Py_complex div, mod; + PyObject *d, *m, *z; + + if (PyErr_Warn(PyExc_DeprecationWarning, + "complex divmod(), // and % are deprecated") < 0) + return NULL; + + errno = 0; + div = c_quot(v->cval,w->cval); /* The raw divisor value. */ + if (errno == EDOM) { + PyErr_SetString(PyExc_ZeroDivisionError, "complex divmod()"); + return NULL; + } + div.real = floor(div.real); /* Use the floor of the real part. */ + div.imag = 0.0; + mod = c_diff(v->cval, c_prod(w->cval, div)); + d = PyComplex_FromCComplex(div); + m = PyComplex_FromCComplex(mod); + z = Py_BuildValue("(OO)", d, m); + Py_XDECREF(d); + Py_XDECREF(m); + return z; +} + +static PyObject * +complex_pow(PyComplexObject *v, PyObject *w, PyComplexObject *z) +{ + Py_complex p; + Py_complex exponent; + long int_exponent; + + if ((PyObject *)z!=Py_None) { + PyErr_SetString(PyExc_ValueError, "complex modulo"); + return NULL; + } + PyFPE_START_PROTECT("complex_pow", return 0) + errno = 0; + exponent = ((PyComplexObject*)w)->cval; + int_exponent = (long)exponent.real; + if (exponent.imag == 0. && exponent.real == int_exponent) + p = c_powi(v->cval,int_exponent); + else + p = c_pow(v->cval,exponent); + + PyFPE_END_PROTECT(p) + Py_ADJUST_ERANGE2(p.real, p.imag); + if (errno == EDOM) { + PyErr_SetString(PyExc_ZeroDivisionError, + "0.0 to a negative or complex power"); + return NULL; + } + else if (errno == ERANGE) { + PyErr_SetString(PyExc_OverflowError, + "complex exponentiaion"); + return NULL; + } + return PyComplex_FromCComplex(p); +} + +static PyObject * +complex_int_div(PyComplexObject *v, PyComplexObject *w) +{ + PyObject *t, *r; + + t = complex_divmod(v, w); + if (t != NULL) { + r = PyTuple_GET_ITEM(t, 0); + Py_INCREF(r); + Py_DECREF(t); + return r; + } + return NULL; +} + +static PyObject * +complex_neg(PyComplexObject *v) +{ + Py_complex neg; + neg.real = -v->cval.real; + neg.imag = -v->cval.imag; + return PyComplex_FromCComplex(neg); +} + +static PyObject * +complex_pos(PyComplexObject *v) +{ + if (PyComplex_CheckExact(v)) { + Py_INCREF(v); + return (PyObject *)v; + } + else + return PyComplex_FromCComplex(v->cval); +} + +static PyObject * +complex_abs(PyComplexObject *v) +{ + double result; + PyFPE_START_PROTECT("complex_abs", return 0) + result = hypot(v->cval.real,v->cval.imag); + PyFPE_END_PROTECT(result) + return PyFloat_FromDouble(result); +} + +static int +complex_nonzero(PyComplexObject *v) +{ + return v->cval.real != 0.0 || v->cval.imag != 0.0; +} + +static int +complex_coerce(PyObject **pv, PyObject **pw) +{ + Py_complex cval; + cval.imag = 0.; + if (PyInt_Check(*pw)) { + cval.real = (double)PyInt_AsLong(*pw); + *pw = PyComplex_FromCComplex(cval); + Py_INCREF(*pv); + return 0; + } + else if (PyLong_Check(*pw)) { + cval.real = PyLong_AsDouble(*pw); + if (cval.real == -1.0 && PyErr_Occurred()) + return -1; + *pw = PyComplex_FromCComplex(cval); + Py_INCREF(*pv); + return 0; + } + else if (PyFloat_Check(*pw)) { + cval.real = PyFloat_AsDouble(*pw); + *pw = PyComplex_FromCComplex(cval); + Py_INCREF(*pv); + return 0; + } + else if (PyComplex_Check(*pw)) { + Py_INCREF(*pv); + Py_INCREF(*pw); + return 0; + } + return 1; /* Can't do it */ +} + +static PyObject * +complex_richcompare(PyObject *v, PyObject *w, int op) +{ + int c; + Py_complex i, j; + PyObject *res; + + c = PyNumber_CoerceEx(&v, &w); + if (c < 0) + return NULL; + if (c > 0) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + /* Make sure both arguments are complex. */ + if (!(PyComplex_Check(v) && PyComplex_Check(w))) { + Py_DECREF(v); + Py_DECREF(w); + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + i = ((PyComplexObject *)v)->cval; + j = ((PyComplexObject *)w)->cval; + Py_DECREF(v); + Py_DECREF(w); + + if (op != Py_EQ && op != Py_NE) { + PyErr_SetString(PyExc_TypeError, + "cannot compare complex numbers using <, <=, >, >="); + return NULL; + } + + if ((i.real == j.real && i.imag == j.imag) == (op == Py_EQ)) + res = Py_True; + else + res = Py_False; + + Py_INCREF(res); + return res; +} + +static PyObject * +complex_int(PyObject *v) +{ + PyErr_SetString(PyExc_TypeError, + "can't convert complex to int; use int(abs(z))"); + return NULL; +} + +static PyObject * +complex_long(PyObject *v) +{ + PyErr_SetString(PyExc_TypeError, + "can't convert complex to long; use long(abs(z))"); + return NULL; +} + +static PyObject * +complex_float(PyObject *v) +{ + PyErr_SetString(PyExc_TypeError, + "can't convert complex to float; use abs(z)"); + return NULL; +} + +static PyObject * +complex_conjugate(PyObject *self) +{ + Py_complex c; + c = ((PyComplexObject *)self)->cval; + c.imag = -c.imag; + return PyComplex_FromCComplex(c); +} + +static PyObject * +complex_getnewargs(PyComplexObject *v) +{ + return Py_BuildValue("(D)", &v->cval); +} + +static PyMethodDef complex_methods[] = { + {"conjugate", (PyCFunction)complex_conjugate, METH_NOARGS}, + {"__getnewargs__", (PyCFunction)complex_getnewargs, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyMemberDef complex_members[] = { + {"real", T_DOUBLE, offsetof(PyComplexObject, cval.real), READONLY, + "the real part of a complex number"}, + {"imag", T_DOUBLE, offsetof(PyComplexObject, cval.imag), READONLY, + "the imaginary part of a complex number"}, + {0}, +}; + +static PyObject * +complex_subtype_from_string(PyTypeObject *type, PyObject *v) +{ + extern double strtod(const char *, char **); + const char *s, *start; + char *end; + double x=0.0, y=0.0, z; + int got_re=0, got_im=0, done=0; + int digit_or_dot; + int sw_error=0; + int sign; + char buffer[256]; /* For errors */ +#ifdef Py_USING_UNICODE + char s_buffer[256]; +#endif + int len; + + if (PyString_Check(v)) { + s = PyString_AS_STRING(v); + len = PyString_GET_SIZE(v); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(v)) { + if (PyUnicode_GET_SIZE(v) >= sizeof(s_buffer)) { + PyErr_SetString(PyExc_ValueError, + "complex() literal too large to convert"); + return NULL; + } + if (PyUnicode_EncodeDecimal(PyUnicode_AS_UNICODE(v), + PyUnicode_GET_SIZE(v), + s_buffer, + NULL)) + return NULL; + s = s_buffer; + len = (int)strlen(s); + } +#endif + else if (PyObject_AsCharBuffer(v, &s, &len)) { + PyErr_SetString(PyExc_TypeError, + "complex() arg is not a string"); + return NULL; + } + + /* position on first nonblank */ + start = s; + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] == '\0') { + PyErr_SetString(PyExc_ValueError, + "complex() arg is an empty string"); + return NULL; + } + + z = -1.0; + sign = 1; + do { + + switch (*s) { + + case '\0': + if (s-start != len) { + PyErr_SetString( + PyExc_ValueError, + "complex() arg contains a null byte"); + return NULL; + } + if(!done) sw_error=1; + break; + + case '-': + sign = -1; + /* Fallthrough */ + case '+': + if (done) sw_error=1; + s++; + if ( *s=='\0'||*s=='+'||*s=='-' || + isspace(Py_CHARMASK(*s)) ) sw_error=1; + break; + + case 'J': + case 'j': + if (got_im || done) { + sw_error = 1; + break; + } + if (z<0.0) { + y=sign; + } + else{ + y=sign*z; + } + got_im=1; + s++; + if (*s!='+' && *s!='-' ) + done=1; + break; + + default: + if (isspace(Py_CHARMASK(*s))) { + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s[0] != '\0') + sw_error=1; + else + done = 1; + break; + } + digit_or_dot = + (*s=='.' || isdigit(Py_CHARMASK(*s))); + if (done||!digit_or_dot) { + sw_error=1; + break; + } + errno = 0; + PyFPE_START_PROTECT("strtod", return 0) + z = strtod(s, &end) ; + PyFPE_END_PROTECT(z) + if (errno != 0) { + PyOS_snprintf(buffer, sizeof(buffer), + "float() out of range: %.150s", s); + PyErr_SetString( + PyExc_ValueError, + buffer); + return NULL; + } + s=end; + if (*s=='J' || *s=='j') { + + break; + } + if (got_re) { + sw_error=1; + break; + } + + /* accept a real part */ + x=sign*z; + got_re=1; + if (got_im) done=1; + z = -1.0; + sign = 1; + break; + + } /* end of switch */ + + } while (s - start < len && !sw_error); + + if (sw_error) { + PyErr_SetString(PyExc_ValueError, + "complex() arg is a malformed string"); + return NULL; + } + + return complex_subtype_from_doubles(type, x, y); +} + +static PyObject * +complex_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *r, *i, *tmp, *f; + PyNumberMethods *nbr, *nbi = NULL; + Py_complex cr, ci; + int own_r = 0; + static PyObject *complexstr; + static char *kwlist[] = {"real", "imag", 0}; + + r = Py_False; + i = NULL; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:complex", kwlist, + &r, &i)) + return NULL; + + /* Special-case for single argument that is already complex */ + if (PyComplex_CheckExact(r) && i == NULL && + type == &PyComplex_Type) { + /* Note that we can't know whether it's safe to return + a complex *subclass* instance as-is, hence the restriction + to exact complexes here. */ + Py_INCREF(r); + return r; + } + if (PyString_Check(r) || PyUnicode_Check(r)) { + if (i != NULL) { + PyErr_SetString(PyExc_TypeError, + "complex() can't take second arg" + " if first is a string"); + return NULL; + } + return complex_subtype_from_string(type, r); + } + if (i != NULL && (PyString_Check(i) || PyUnicode_Check(i))) { + PyErr_SetString(PyExc_TypeError, + "complex() second arg can't be a string"); + return NULL; + } + + /* XXX Hack to support classes with __complex__ method */ + if (complexstr == NULL) { + complexstr = PyString_InternFromString("__complex__"); + if (complexstr == NULL) + return NULL; + } + f = PyObject_GetAttr(r, complexstr); + if (f == NULL) + PyErr_Clear(); + else { + PyObject *args = Py_BuildValue("()"); + if (args == NULL) + return NULL; + r = PyEval_CallObject(f, args); + Py_DECREF(args); + Py_DECREF(f); + if (r == NULL) + return NULL; + own_r = 1; + } + nbr = r->ob_type->tp_as_number; + if (i != NULL) + nbi = i->ob_type->tp_as_number; + if (nbr == NULL || nbr->nb_float == NULL || + ((i != NULL) && (nbi == NULL || nbi->nb_float == NULL))) { + PyErr_SetString(PyExc_TypeError, + "complex() argument must be a string or a number"); + return NULL; + } + if (PyComplex_Check(r)) { + /* Note that if r is of a complex subtype, we're only + retaining its real & imag parts here, and the return + value is (properly) of the builtin complex type. */ + cr = ((PyComplexObject*)r)->cval; + if (own_r) { + Py_DECREF(r); + } + } + else { + tmp = PyNumber_Float(r); + if (own_r) { + Py_DECREF(r); + } + if (tmp == NULL) + return NULL; + if (!PyFloat_Check(tmp)) { + PyErr_SetString(PyExc_TypeError, + "float(r) didn't return a float"); + Py_DECREF(tmp); + return NULL; + } + cr.real = PyFloat_AsDouble(tmp); + Py_DECREF(tmp); + cr.imag = 0.0; + } + if (i == NULL) { + ci.real = 0.0; + ci.imag = 0.0; + } + else if (PyComplex_Check(i)) + ci = ((PyComplexObject*)i)->cval; + else { + tmp = (*nbi->nb_float)(i); + if (tmp == NULL) + return NULL; + ci.real = PyFloat_AsDouble(tmp); + Py_DECREF(tmp); + ci.imag = 0.; + } + cr.real -= ci.imag; + cr.imag += ci.real; + return complex_subtype_from_c_complex(type, cr); +} + +PyDoc_STRVAR(complex_doc, +"complex(real[, imag]) -> complex number\n" +"\n" +"Create a complex number from a real part and an optional imaginary part.\n" +"This is equivalent to (real + imag*1j) where imag defaults to 0."); + +static PyNumberMethods complex_as_number = { + (binaryfunc)complex_add, /* nb_add */ + (binaryfunc)complex_sub, /* nb_subtract */ + (binaryfunc)complex_mul, /* nb_multiply */ + (binaryfunc)complex_classic_div, /* nb_divide */ + (binaryfunc)complex_remainder, /* nb_remainder */ + (binaryfunc)complex_divmod, /* nb_divmod */ + (ternaryfunc)complex_pow, /* nb_power */ + (unaryfunc)complex_neg, /* nb_negative */ + (unaryfunc)complex_pos, /* nb_positive */ + (unaryfunc)complex_abs, /* nb_absolute */ + (inquiry)complex_nonzero, /* nb_nonzero */ + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + 0, /* nb_or */ + (coercion)complex_coerce, /* nb_coerce */ + (unaryfunc)complex_int, /* nb_int */ + (unaryfunc)complex_long, /* nb_long */ + (unaryfunc)complex_float, /* nb_float */ + 0, /* nb_oct */ + 0, /* nb_hex */ + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply*/ + 0, /* nb_inplace_divide */ + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + (binaryfunc)complex_int_div, /* nb_floor_divide */ + (binaryfunc)complex_div, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +}; + +PyTypeObject PyComplex_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "complex", + sizeof(PyComplexObject), + 0, + (destructor)complex_dealloc, /* tp_dealloc */ + (printfunc)complex_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)complex_repr, /* tp_repr */ + &complex_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)complex_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)complex_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + complex_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + complex_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + complex_methods, /* tp_methods */ + complex_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + complex_new, /* tp_new */ + PyObject_Del, /* tp_free */ +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/descrobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/descrobject.c new file mode 100644 index 00000000..cf0ec0d2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/descrobject.c @@ -0,0 +1,1255 @@ +/* Descriptors -- a new, flexible way to describe attributes */ + +#include "Python.h" +#include "structmember.h" /* Why is this not included in Python.h? */ + +static void +descr_dealloc(PyDescrObject *descr) +{ + _PyObject_GC_UNTRACK(descr); + Py_XDECREF(descr->d_type); + Py_XDECREF(descr->d_name); + PyObject_GC_Del(descr); +} + +static char * +descr_name(PyDescrObject *descr) +{ + if (descr->d_name != NULL && PyString_Check(descr->d_name)) + return PyString_AS_STRING(descr->d_name); + else + return "?"; +} + +static PyObject * +descr_repr(PyDescrObject *descr, char *format) +{ + return PyString_FromFormat(format, descr_name(descr), + descr->d_type->tp_name); +} + +static PyObject * +method_repr(PyMethodDescrObject *descr) +{ + return descr_repr((PyDescrObject *)descr, + ""); +} + +static PyObject * +member_repr(PyMemberDescrObject *descr) +{ + return descr_repr((PyDescrObject *)descr, + ""); +} + +static PyObject * +getset_repr(PyGetSetDescrObject *descr) +{ + return descr_repr((PyDescrObject *)descr, + ""); +} + +static PyObject * +wrapper_repr(PyWrapperDescrObject *descr) +{ + return descr_repr((PyDescrObject *)descr, + ""); +} + +static int +descr_check(PyDescrObject *descr, PyObject *obj, PyObject **pres) +{ + if (obj == NULL) { + Py_INCREF(descr); + *pres = (PyObject *)descr; + return 1; + } + if (!PyObject_TypeCheck(obj, descr->d_type)) { + PyErr_Format(PyExc_TypeError, + "descriptor '%s' for '%s' objects " + "doesn't apply to '%s' object", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name, + obj->ob_type->tp_name); + *pres = NULL; + return 1; + } + return 0; +} + +static PyObject * +classmethod_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type) +{ + /* Ensure a valid type. Class methods ignore obj. */ + if (type == NULL) { + if (obj != NULL) + type = (PyObject *)obj->ob_type; + else { + /* Wot - no type?! */ + PyErr_Format(PyExc_TypeError, + "descriptor '%s' for type '%s' " + "needs either an object or a type", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name); + return NULL; + } + } + if (!PyType_Check(type)) { + PyErr_Format(PyExc_TypeError, + "descriptor '%s' for type '%s' " + "needs a type, not a '%s' as arg 2", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name, + type->ob_type->tp_name); + return NULL; + } + if (!PyType_IsSubtype((PyTypeObject *)type, descr->d_type)) { + PyErr_Format(PyExc_TypeError, + "descriptor '%s' for type '%s' " + "doesn't apply to type '%s'", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name, + ((PyTypeObject *)type)->tp_name); + return NULL; + } + return PyCFunction_New(descr->d_method, type); +} + +static PyObject * +method_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type) +{ + PyObject *res; + + if (descr_check((PyDescrObject *)descr, obj, &res)) + return res; + return PyCFunction_New(descr->d_method, obj); +} + +static PyObject * +member_get(PyMemberDescrObject *descr, PyObject *obj, PyObject *type) +{ + PyObject *res; + + if (descr_check((PyDescrObject *)descr, obj, &res)) + return res; + return PyMember_GetOne((char *)obj, descr->d_member); +} + +static PyObject * +getset_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *type) +{ + PyObject *res; + + if (descr_check((PyDescrObject *)descr, obj, &res)) + return res; + if (descr->d_getset->get != NULL) + return descr->d_getset->get(obj, descr->d_getset->closure); + PyErr_Format(PyExc_TypeError, + "attribute '%.300s' of '%.100s' objects is not readable", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name); + return NULL; +} + +static PyObject * +wrapper_get(PyWrapperDescrObject *descr, PyObject *obj, PyObject *type) +{ + PyObject *res; + + if (descr_check((PyDescrObject *)descr, obj, &res)) + return res; + return PyWrapper_New((PyObject *)descr, obj); +} + +static int +descr_setcheck(PyDescrObject *descr, PyObject *obj, PyObject *value, + int *pres) +{ + assert(obj != NULL); + if (!PyObject_IsInstance(obj, (PyObject *)(descr->d_type))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.200s' for '%.100s' objects " + "doesn't apply to '%.100s' object", + descr_name(descr), + descr->d_type->tp_name, + obj->ob_type->tp_name); + *pres = -1; + return 1; + } + return 0; +} + +static int +member_set(PyMemberDescrObject *descr, PyObject *obj, PyObject *value) +{ + int res; + + if (descr_setcheck((PyDescrObject *)descr, obj, value, &res)) + return res; + return PyMember_SetOne((char *)obj, descr->d_member, value); +} + +static int +getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) +{ + int res; + + if (descr_setcheck((PyDescrObject *)descr, obj, value, &res)) + return res; + if (descr->d_getset->set != NULL) + return descr->d_getset->set(obj, value, + descr->d_getset->closure); + PyErr_Format(PyExc_TypeError, + "attribute '%.300s' of '%.100s' objects is not writable", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name); + return -1; +} + +static PyObject * +methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds) +{ + int argc; + PyObject *self, *func, *result; + + /* Make sure that the first argument is acceptable as 'self' */ + assert(PyTuple_Check(args)); + argc = PyTuple_GET_SIZE(args); + if (argc < 1) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.300s' of '%.100s' " + "object needs an argument", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name); + return NULL; + } + self = PyTuple_GET_ITEM(args, 0); + if (!PyObject_IsInstance(self, (PyObject *)(descr->d_type))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.200s' " + "requires a '%.100s' object " + "but received a '%.100s'", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name, + self->ob_type->tp_name); + return NULL; + } + + func = PyCFunction_New(descr->d_method, self); + if (func == NULL) + return NULL; + args = PyTuple_GetSlice(args, 1, argc); + if (args == NULL) { + Py_DECREF(func); + return NULL; + } + result = PyEval_CallObjectWithKeywords(func, args, kwds); + Py_DECREF(args); + Py_DECREF(func); + return result; +} + +static PyObject * +classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args, + PyObject *kwds) +{ + PyObject *func, *result; + + func = PyCFunction_New(descr->d_method, (PyObject *)descr->d_type); + if (func == NULL) + return NULL; + + result = PyEval_CallObjectWithKeywords(func, args, kwds); + Py_DECREF(func); + return result; +} + +static PyObject * +wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds) +{ + int argc; + PyObject *self, *func, *result; + + /* Make sure that the first argument is acceptable as 'self' */ + assert(PyTuple_Check(args)); + argc = PyTuple_GET_SIZE(args); + if (argc < 1) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.300s' of '%.100s' " + "object needs an argument", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name); + return NULL; + } + self = PyTuple_GET_ITEM(args, 0); + if (!PyObject_IsInstance(self, (PyObject *)(descr->d_type))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%.200s' " + "requires a '%.100s' object " + "but received a '%.100s'", + descr_name((PyDescrObject *)descr), + descr->d_type->tp_name, + self->ob_type->tp_name); + return NULL; + } + + func = PyWrapper_New((PyObject *)descr, self); + if (func == NULL) + return NULL; + args = PyTuple_GetSlice(args, 1, argc); + if (args == NULL) { + Py_DECREF(func); + return NULL; + } + result = PyEval_CallObjectWithKeywords(func, args, kwds); + Py_DECREF(args); + Py_DECREF(func); + return result; +} + +static PyObject * +method_get_doc(PyMethodDescrObject *descr, void *closure) +{ + if (descr->d_method->ml_doc == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString(descr->d_method->ml_doc); +} + +static PyMemberDef descr_members[] = { + {"__objclass__", T_OBJECT, offsetof(PyDescrObject, d_type), READONLY}, + {"__name__", T_OBJECT, offsetof(PyDescrObject, d_name), READONLY}, + {0} +}; + +static PyGetSetDef method_getset[] = { + {"__doc__", (getter)method_get_doc}, + {0} +}; + +static PyObject * +member_get_doc(PyMemberDescrObject *descr, void *closure) +{ + if (descr->d_member->doc == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString(descr->d_member->doc); +} + +static PyGetSetDef member_getset[] = { + {"__doc__", (getter)member_get_doc}, + {0} +}; + +static PyObject * +getset_get_doc(PyGetSetDescrObject *descr, void *closure) +{ + if (descr->d_getset->doc == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString(descr->d_getset->doc); +} + +static PyGetSetDef getset_getset[] = { + {"__doc__", (getter)getset_get_doc}, + {0} +}; + +static PyObject * +wrapper_get_doc(PyWrapperDescrObject *descr, void *closure) +{ + if (descr->d_base->doc == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromString(descr->d_base->doc); +} + +static PyGetSetDef wrapper_getset[] = { + {"__doc__", (getter)wrapper_get_doc}, + {0} +}; + +static int +descr_traverse(PyObject *self, visitproc visit, void *arg) +{ + PyDescrObject *descr = (PyDescrObject *)self; + int err; + + if (descr->d_type) { + err = visit((PyObject *)(descr->d_type), arg); + if (err) + return err; + } + return 0; +} + +static PyTypeObject PyMethodDescr_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "method_descriptor", + sizeof(PyMethodDescrObject), + 0, + (destructor)descr_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)method_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)methoddescr_call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + descr_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + descr_members, /* tp_members */ + method_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)method_get, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + +/* This is for METH_CLASS in C, not for "f = classmethod(f)" in Python! */ +static PyTypeObject PyClassMethodDescr_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "classmethod_descriptor", + sizeof(PyMethodDescrObject), + 0, + (destructor)descr_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)method_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)classmethoddescr_call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + descr_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + descr_members, /* tp_members */ + method_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)classmethod_get, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + +static PyTypeObject PyMemberDescr_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "member_descriptor", + sizeof(PyMemberDescrObject), + 0, + (destructor)descr_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)member_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + descr_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + descr_members, /* tp_members */ + member_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)member_get, /* tp_descr_get */ + (descrsetfunc)member_set, /* tp_descr_set */ +}; + +static PyTypeObject PyGetSetDescr_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "getset_descriptor", + sizeof(PyGetSetDescrObject), + 0, + (destructor)descr_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)getset_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + descr_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + descr_members, /* tp_members */ + getset_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)getset_get, /* tp_descr_get */ + (descrsetfunc)getset_set, /* tp_descr_set */ +}; + +PyTypeObject PyWrapperDescr_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "wrapper_descriptor", + sizeof(PyWrapperDescrObject), + 0, + (destructor)descr_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)wrapper_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)wrapperdescr_call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + descr_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + descr_members, /* tp_members */ + wrapper_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc)wrapper_get, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + +static PyDescrObject * +descr_new(PyTypeObject *descrtype, PyTypeObject *type, char *name) +{ + PyDescrObject *descr; + + descr = (PyDescrObject *)PyType_GenericAlloc(descrtype, 0); + if (descr != NULL) { + Py_XINCREF(type); + descr->d_type = type; + descr->d_name = PyString_InternFromString(name); + if (descr->d_name == NULL) { + Py_DECREF(descr); + descr = NULL; + } + } + return descr; +} + +PyObject * +PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method) +{ + PyMethodDescrObject *descr; + + descr = (PyMethodDescrObject *)descr_new(&PyMethodDescr_Type, + type, method->ml_name); + if (descr != NULL) + descr->d_method = method; + return (PyObject *)descr; +} + +PyObject * +PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method) +{ + PyMethodDescrObject *descr; + + descr = (PyMethodDescrObject *)descr_new(&PyClassMethodDescr_Type, + type, method->ml_name); + if (descr != NULL) + descr->d_method = method; + return (PyObject *)descr; +} + +PyObject * +PyDescr_NewMember(PyTypeObject *type, PyMemberDef *member) +{ + PyMemberDescrObject *descr; + + descr = (PyMemberDescrObject *)descr_new(&PyMemberDescr_Type, + type, member->name); + if (descr != NULL) + descr->d_member = member; + return (PyObject *)descr; +} + +PyObject * +PyDescr_NewGetSet(PyTypeObject *type, PyGetSetDef *getset) +{ + PyGetSetDescrObject *descr; + + descr = (PyGetSetDescrObject *)descr_new(&PyGetSetDescr_Type, + type, getset->name); + if (descr != NULL) + descr->d_getset = getset; + return (PyObject *)descr; +} + +PyObject * +PyDescr_NewWrapper(PyTypeObject *type, struct wrapperbase *base, void *wrapped) +{ + PyWrapperDescrObject *descr; + + descr = (PyWrapperDescrObject *)descr_new(&PyWrapperDescr_Type, + type, base->name); + if (descr != NULL) { + descr->d_base = base; + descr->d_wrapped = wrapped; + } + return (PyObject *)descr; +} + + +/* --- Readonly proxy for dictionaries (actually any mapping) --- */ + +/* This has no reason to be in this file except that adding new files is a + bit of a pain */ + +typedef struct { + PyObject_HEAD + PyObject *dict; +} proxyobject; + +static int +proxy_len(proxyobject *pp) +{ + return PyObject_Size(pp->dict); +} + +static PyObject * +proxy_getitem(proxyobject *pp, PyObject *key) +{ + return PyObject_GetItem(pp->dict, key); +} + +static PyMappingMethods proxy_as_mapping = { + (inquiry)proxy_len, /* mp_length */ + (binaryfunc)proxy_getitem, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + +static int +proxy_contains(proxyobject *pp, PyObject *key) +{ + return PySequence_Contains(pp->dict, key); +} + +static PySequenceMethods proxy_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)proxy_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyObject * +proxy_has_key(proxyobject *pp, PyObject *key) +{ + int res = PySequence_Contains(pp->dict, key); + if (res < 0) + return NULL; + return PyBool_FromLong(res); +} + +static PyObject * +proxy_get(proxyobject *pp, PyObject *args) +{ + PyObject *key, *def = Py_None; + + if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &def)) + return NULL; + return PyObject_CallMethod(pp->dict, "get", "(OO)", key, def); +} + +static PyObject * +proxy_keys(proxyobject *pp) +{ + return PyMapping_Keys(pp->dict); +} + +static PyObject * +proxy_values(proxyobject *pp) +{ + return PyMapping_Values(pp->dict); +} + +static PyObject * +proxy_items(proxyobject *pp) +{ + return PyMapping_Items(pp->dict); +} + +static PyObject * +proxy_iterkeys(proxyobject *pp) +{ + return PyObject_CallMethod(pp->dict, "iterkeys", NULL); +} + +static PyObject * +proxy_itervalues(proxyobject *pp) +{ + return PyObject_CallMethod(pp->dict, "itervalues", NULL); +} + +static PyObject * +proxy_iteritems(proxyobject *pp) +{ + return PyObject_CallMethod(pp->dict, "iteritems", NULL); +} +static PyObject * +proxy_copy(proxyobject *pp) +{ + return PyObject_CallMethod(pp->dict, "copy", NULL); +} + +static PyMethodDef proxy_methods[] = { + {"has_key", (PyCFunction)proxy_has_key, METH_O, + PyDoc_STR("D.has_key(k) -> True if D has a key k, else False")}, + {"get", (PyCFunction)proxy_get, METH_VARARGS, + PyDoc_STR("D.get(k[,d]) -> D[k] if D.has_key(k), else d." + " d defaults to None.")}, + {"keys", (PyCFunction)proxy_keys, METH_NOARGS, + PyDoc_STR("D.keys() -> list of D's keys")}, + {"values", (PyCFunction)proxy_values, METH_NOARGS, + PyDoc_STR("D.values() -> list of D's values")}, + {"items", (PyCFunction)proxy_items, METH_NOARGS, + PyDoc_STR("D.items() -> list of D's (key, value) pairs, as 2-tuples")}, + {"iterkeys", (PyCFunction)proxy_iterkeys, METH_NOARGS, + PyDoc_STR("D.iterkeys() -> an iterator over the keys of D")}, + {"itervalues",(PyCFunction)proxy_itervalues, METH_NOARGS, + PyDoc_STR("D.itervalues() -> an iterator over the values of D")}, + {"iteritems", (PyCFunction)proxy_iteritems, METH_NOARGS, + PyDoc_STR("D.iteritems() ->" + " an iterator over the (key, value) items of D")}, + {"copy", (PyCFunction)proxy_copy, METH_NOARGS, + PyDoc_STR("D.copy() -> a shallow copy of D")}, + {0} +}; + +static void +proxy_dealloc(proxyobject *pp) +{ + _PyObject_GC_UNTRACK(pp); + Py_DECREF(pp->dict); + PyObject_GC_Del(pp); +} + +static PyObject * +proxy_getiter(proxyobject *pp) +{ + return PyObject_GetIter(pp->dict); +} + +static PyObject * +proxy_str(proxyobject *pp) +{ + return PyObject_Str(pp->dict); +} + +static int +proxy_traverse(PyObject *self, visitproc visit, void *arg) +{ + proxyobject *pp = (proxyobject *)self; + int err; + + if (pp->dict) { + err = visit(pp->dict, arg); + if (err) + return err; + } + return 0; +} + +static int +proxy_compare(proxyobject *v, PyObject *w) +{ + return PyObject_Compare(v->dict, w); +} + +static PyObject * +proxy_richcompare(proxyobject *v, PyObject *w, int op) +{ + return PyObject_RichCompare(v->dict, w, op); +} + +static PyTypeObject proxytype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "dictproxy", /* tp_name */ + sizeof(proxyobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)proxy_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)proxy_compare, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &proxy_as_sequence, /* tp_as_sequence */ + &proxy_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)proxy_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + proxy_traverse, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)proxy_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)proxy_getiter, /* tp_iter */ + 0, /* tp_iternext */ + proxy_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + +PyObject * +PyDictProxy_New(PyObject *dict) +{ + proxyobject *pp; + + pp = PyObject_GC_New(proxyobject, &proxytype); + if (pp != NULL) { + Py_INCREF(dict); + pp->dict = dict; + _PyObject_GC_TRACK(pp); + } + return (PyObject *)pp; +} + + +/* --- Wrapper object for "slot" methods --- */ + +/* This has no reason to be in this file except that adding new files is a + bit of a pain */ + +typedef struct { + PyObject_HEAD + PyWrapperDescrObject *descr; + PyObject *self; +} wrapperobject; + +static void +wrapper_dealloc(wrapperobject *wp) +{ + _PyObject_GC_UNTRACK(wp); + Py_XDECREF(wp->descr); + Py_XDECREF(wp->self); + PyObject_GC_Del(wp); +} + +static PyMethodDef wrapper_methods[] = { + {0} +}; + +static PyObject * +wrapper_name(wrapperobject *wp) +{ + char *s = wp->descr->d_base->name; + + return PyString_FromString(s); +} + +static PyObject * +wrapper_doc(wrapperobject *wp) +{ + char *s = wp->descr->d_base->doc; + + if (s == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + else { + return PyString_FromString(s); + } +} + +static PyGetSetDef wrapper_getsets[] = { + {"__name__", (getter)wrapper_name}, + {"__doc__", (getter)wrapper_doc}, + {0} +}; + +static PyObject * +wrapper_call(wrapperobject *wp, PyObject *args, PyObject *kwds) +{ + wrapperfunc wrapper = wp->descr->d_base->wrapper; + PyObject *self = wp->self; + + if (wp->descr->d_base->flags & PyWrapperFlag_KEYWORDS) { + wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper; + return (*wk)(self, args, wp->descr->d_wrapped, kwds); + } + + if (kwds != NULL && (!PyDict_Check(kwds) || PyDict_Size(kwds) != 0)) { + PyErr_Format(PyExc_TypeError, + "wrapper %s doesn't take keyword arguments", + wp->descr->d_base->name); + return NULL; + } + return (*wrapper)(self, args, wp->descr->d_wrapped); +} + +static int +wrapper_traverse(PyObject *self, visitproc visit, void *arg) +{ + wrapperobject *wp = (wrapperobject *)self; + int err; + + if (wp->descr) { + err = visit((PyObject *)(wp->descr), arg); + if (err) + return err; + } + if (wp->self) { + err = visit(wp->self, arg); + if (err) + return err; + } + return 0; +} + +static PyTypeObject wrappertype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "method-wrapper", /* tp_name */ + sizeof(wrapperobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)wrapper_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)wrapper_call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + wrapper_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + wrapper_methods, /* tp_methods */ + 0, /* tp_members */ + wrapper_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + +PyObject * +PyWrapper_New(PyObject *d, PyObject *self) +{ + wrapperobject *wp; + PyWrapperDescrObject *descr; + + assert(PyObject_TypeCheck(d, &PyWrapperDescr_Type)); + descr = (PyWrapperDescrObject *)d; + assert(PyObject_IsInstance(self, (PyObject *)(descr->d_type))); + + wp = PyObject_GC_New(wrapperobject, &wrappertype); + if (wp != NULL) { + Py_INCREF(descr); + wp->descr = descr; + Py_INCREF(self); + wp->self = self; + _PyObject_GC_TRACK(wp); + } + return (PyObject *)wp; +} + + +/* A built-in 'property' type */ + +/* + class property(object): + + def __init__(self, fget=None, fset=None, fdel=None, doc=None): + self.__get = fget + self.__set = fset + self.__del = fdel + self.__doc__ = doc + + def __get__(self, inst, type=None): + if inst is None: + return self + if self.__get is None: + raise AttributeError, "unreadable attribute" + return self.__get(inst) + + def __set__(self, inst, value): + if self.__set is None: + raise AttributeError, "can't set attribute" + return self.__set(inst, value) + + def __delete__(self, inst): + if self.__del is None: + raise AttributeError, "can't delete attribute" + return self.__del(inst) + +*/ + +typedef struct { + PyObject_HEAD + PyObject *prop_get; + PyObject *prop_set; + PyObject *prop_del; + PyObject *prop_doc; +} propertyobject; + +static PyMemberDef property_members[] = { + {"fget", T_OBJECT, offsetof(propertyobject, prop_get), READONLY}, + {"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY}, + {"fdel", T_OBJECT, offsetof(propertyobject, prop_del), READONLY}, + {"__doc__", T_OBJECT, offsetof(propertyobject, prop_doc), READONLY}, + {0} +}; + + +static void +property_dealloc(PyObject *self) +{ + propertyobject *gs = (propertyobject *)self; + + _PyObject_GC_UNTRACK(self); + Py_XDECREF(gs->prop_get); + Py_XDECREF(gs->prop_set); + Py_XDECREF(gs->prop_del); + Py_XDECREF(gs->prop_doc); + self->ob_type->tp_free(self); +} + +static PyObject * +property_descr_get(PyObject *self, PyObject *obj, PyObject *type) +{ + propertyobject *gs = (propertyobject *)self; + + if (obj == NULL || obj == Py_None) { + Py_INCREF(self); + return self; + } + if (gs->prop_get == NULL) { + PyErr_SetString(PyExc_AttributeError, "unreadable attribute"); + return NULL; + } + return PyObject_CallFunction(gs->prop_get, "(O)", obj); +} + +static int +property_descr_set(PyObject *self, PyObject *obj, PyObject *value) +{ + propertyobject *gs = (propertyobject *)self; + PyObject *func, *res; + + if (value == NULL) + func = gs->prop_del; + else + func = gs->prop_set; + if (func == NULL) { + PyErr_SetString(PyExc_AttributeError, + value == NULL ? + "can't delete attribute" : + "can't set attribute"); + return -1; + } + if (value == NULL) + res = PyObject_CallFunction(func, "(O)", obj); + else + res = PyObject_CallFunction(func, "(OO)", obj, value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static int +property_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *get = NULL, *set = NULL, *del = NULL, *doc = NULL; + static char *kwlist[] = {"fget", "fset", "fdel", "doc", 0}; + propertyobject *gs = (propertyobject *)self; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO:property", + kwlist, &get, &set, &del, &doc)) + return -1; + + if (get == Py_None) + get = NULL; + if (set == Py_None) + set = NULL; + if (del == Py_None) + del = NULL; + + Py_XINCREF(get); + Py_XINCREF(set); + Py_XINCREF(del); + Py_XINCREF(doc); + + gs->prop_get = get; + gs->prop_set = set; + gs->prop_del = del; + gs->prop_doc = doc; + + return 0; +} + +PyDoc_STRVAR(property_doc, +"property(fget=None, fset=None, fdel=None, doc=None) -> property attribute\n" +"\n" +"fget is a function to be used for getting an attribute value, and likewise\n" +"fset is a function for setting, and fdel a function for del'ing, an\n" +"attribute. Typical use is to define a managed attribute x:\n" +"class C(object):\n" +" def getx(self): return self.__x\n" +" def setx(self, value): self.__x = value\n" +" def delx(self): del self.__x\n" +" x = property(getx, setx, delx, \"I'm the 'x' property.\")"); + +static int +property_traverse(PyObject *self, visitproc visit, void *arg) +{ + propertyobject *pp = (propertyobject *)self; + int err; + +#define VISIT(SLOT) \ + if (pp->SLOT) { \ + err = visit((PyObject *)(pp->SLOT), arg); \ + if (err) \ + return err; \ + } + + VISIT(prop_get); + VISIT(prop_set); + VISIT(prop_del); + VISIT(prop_doc); + + return 0; +} + +PyTypeObject PyProperty_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "property", /* tp_name */ + sizeof(propertyobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + property_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + property_doc, /* tp_doc */ + property_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + property_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + property_descr_get, /* tp_descr_get */ + property_descr_set, /* tp_descr_set */ + 0, /* tp_dictoffset */ + property_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/dictnotes.txt b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/dictnotes.txt new file mode 100644 index 00000000..88b13070 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/dictnotes.txt @@ -0,0 +1,245 @@ +NOTES ON OPTIMIZING DICTIONARIES +================================ + + +Principal Use Cases for Dictionaries +------------------------------------ + +Passing keyword arguments + Typically, one read and one write for 1 to 3 elements. + Occurs frequently in normal python code. + +Class method lookup + Dictionaries vary in size with 8 to 16 elements being common. + Usually written once with many lookups. + When base classes are used, there are many failed lookups + followed by a lookup in a base class. + +Instance attribute lookup and Global variables + Dictionaries vary in size. 4 to 10 elements are common. + Both reads and writes are common. + +Builtins + Frequent reads. Almost never written. + Size 126 interned strings (as of Py2.3b1). + A few keys are accessed much more frequently than others. + +Uniquification + Dictionaries of any size. Bulk of work is in creation. + Repeated writes to a smaller set of keys. + Single read of each key. + Some use cases have two consecutive accesses to the same key. + + * Removing duplicates from a sequence. + dict.fromkeys(seqn).keys() + + * Counting elements in a sequence. + for e in seqn: + d[e] = d.get(e,0) + 1 + + * Accumulating references in a dictionary of lists: + + for pagenumber, page in enumerate(pages): + for word in page: + d.setdefault(word, []).append(pagenumber) + + Note, the second example is a use case characterized by a get and set + to the same key. There are similar used cases with a __contains__ + followed by a get, set, or del to the same key. Part of the + justification for d.setdefault is combining the two lookups into one. + +Membership Testing + Dictionaries of any size. Created once and then rarely changes. + Single write to each key. + Many calls to __contains__() or has_key(). + Similar access patterns occur with replacement dictionaries + such as with the % formatting operator. + +Dynamic Mappings + Characterized by deletions interspersed with adds and replacements. + Performance benefits greatly from the re-use of dummy entries. + + +Data Layout (assuming a 32-bit box with 64 bytes per cache line) +---------------------------------------------------------------- + +Smalldicts (8 entries) are attached to the dictobject structure +and the whole group nearly fills two consecutive cache lines. + +Larger dicts use the first half of the dictobject structure (one cache +line) and a separate, continuous block of entries (at 12 bytes each +for a total of 5.333 entries per cache line). + + +Tunable Dictionary Parameters +----------------------------- + +* PyDict_MINSIZE. Currently set to 8. + Must be a power of two. New dicts have to zero-out every cell. + Each additional 8 consumes 1.5 cache lines. Increasing improves + the sparseness of small dictionaries but costs time to read in + the additional cache lines if they are not already in cache. + That case is common when keyword arguments are passed. + +* Maximum dictionary load in PyDict_SetItem. Currently set to 2/3. + Increasing this ratio makes dictionaries more dense resulting + in more collisions. Decreasing it improves sparseness at the + expense of spreading entries over more cache lines and at the + cost of total memory consumed. + + The load test occurs in highly time sensitive code. Efforts + to make the test more complex (for example, varying the load + for different sizes) have degraded performance. + +* Growth rate upon hitting maximum load. Currently set to *2. + Raising this to *4 results in half the number of resizes, + less effort to resize, better sparseness for some (but not + all dict sizes), and potentially double memory consumption + depending on the size of the dictionary. Setting to *4 + eliminates every other resize step. + +Tune-ups should be measured across a broad range of applications and +use cases. A change to any parameter will help in some situations and +hurt in others. The key is to find settings that help the most common +cases and do the least damage to the less common cases. Results will +vary dramatically depending on the exact number of keys, whether the +keys are all strings, whether reads or writes dominate, the exact +hash values of the keys (some sets of values have fewer collisions than +others). Any one test or benchmark is likely to prove misleading. + +While making a dictionary more sparse reduces collisions, it impairs +iteration and key listing. Those methods loop over every potential +entry. Doubling the size of dictionary results in twice as many +non-overlapping memory accesses for keys(), items(), values(), +__iter__(), iterkeys(), iteritems(), itervalues(), and update(). + + +Results of Cache Locality Experiments +------------------------------------- + +When an entry is retrieved from memory, 4.333 adjacent entries are also +retrieved into a cache line. Since accessing items in cache is *much* +cheaper than a cache miss, an enticing idea is to probe the adjacent +entries as a first step in collision resolution. Unfortunately, the +introduction of any regularity into collision searches results in more +collisions than the current random chaining approach. + +Exploiting cache locality at the expense of additional collisions fails +to payoff when the entries are already loaded in cache (the expense +is paid with no compensating benefit). This occurs in small dictionaries +where the whole dictionary fits into a pair of cache lines. It also +occurs frequently in large dictionaries which have a common access pattern +where some keys are accessed much more frequently than others. The +more popular entries *and* their collision chains tend to remain in cache. + +To exploit cache locality, change the collision resolution section +in lookdict() and lookdict_string(). Set i^=1 at the top of the +loop and move the i = (i << 2) + i + perturb + 1 to an unrolled +version of the loop. + +This optimization strategy can be leveraged in several ways: + +* If the dictionary is kept sparse (through the tunable parameters), +then the occurrence of additional collisions is lessened. + +* If lookdict() and lookdict_string() are specialized for small dicts +and for largedicts, then the versions for large_dicts can be given +an alternate search strategy without increasing collisions in small dicts +which already have the maximum benefit of cache locality. + +* If the use case for a dictionary is known to have a random key +access pattern (as opposed to a more common pattern with a Zipf's law +distribution), then there will be more benefit for large dictionaries +because any given key is no more likely than another to already be +in cache. + +* In use cases with paired accesses to the same key, the second access +is always in cache and gets no benefit from efforts to further improve +cache locality. + +Optimizing the Search of Small Dictionaries +------------------------------------------- + +If lookdict() and lookdict_string() are specialized for smaller dictionaries, +then a custom search approach can be implemented that exploits the small +search space and cache locality. + +* The simplest example is a linear search of contiguous entries. This is + simple to implement, guaranteed to terminate rapidly, never searches + the same entry twice, and precludes the need to check for dummy entries. + +* A more advanced example is a self-organizing search so that the most + frequently accessed entries get probed first. The organization + adapts if the access pattern changes over time. Treaps are ideally + suited for self-organization with the most common entries at the + top of the heap and a rapid binary search pattern. Most probes and + results are all located at the top of the tree allowing them all to + be located in one or two cache lines. + +* Also, small dictionaries may be made more dense, perhaps filling all + eight cells to take the maximum advantage of two cache lines. + + +Strategy Pattern +---------------- + +Consider allowing the user to set the tunable parameters or to select a +particular search method. Since some dictionary use cases have known +sizes and access patterns, the user may be able to provide useful hints. + +1) For example, if membership testing or lookups dominate runtime and memory + is not at a premium, the user may benefit from setting the maximum load + ratio at 5% or 10% instead of the usual 66.7%. This will sharply + curtail the number of collisions but will increase iteration time. + +2) Dictionary creation time can be shortened in cases where the ultimate + size of the dictionary is known in advance. The dictionary can be + pre-sized so that no resize operations are required during creation. + Not only does this save resizes, but the key insertion will go + more quickly because the first half of the keys will be inserted into + a more sparse environment than before. The preconditions for this + strategy arise whenever a dictionary is created from a key or item + sequence and the number of unique keys is known. + +3) If the key space is large and the access pattern is known to be random, + then search strategies exploiting cache locality can be fruitful. + The preconditions for this strategy arise in simulations and + numerical analysis. + +4) If the keys are fixed and the access pattern strongly favors some of + the keys, then the entries can be stored contiguously and accessed + with a linear search or treap. This exploits knowledge of the data, + cache locality, and a simplified search routine. It also eliminates + the need to test for dummy entries on each probe. The preconditions + for this strategy arise in symbol tables and in the builtin dictionary. + + +Readonly Dictionaries +--------------------- +Some dictionary use cases pass through a build stage and then move to a +more heavily exercised lookup stage with no further changes to the +dictionary. + +An idea that emerged on python-dev is to be able to convert a dictionary +to a read-only state. This can help prevent programming errors and also +provide knowledge that can be exploited for lookup optimization. + +The dictionary can be immediately rebuilt (eliminating dummy entries), +resized (to an appropriate level of sparseness), and the keys can be +jostled (to minimize collisions). The lookdict() routine can then +eliminate the test for dummy entries (saving about 1/4 of the time +spend in the collision resolution loop). + +An additional possibility is to insert links into the empty spaces +so that dictionary iteration can proceed in len(d) steps instead of +(mp->mask + 1) steps. + + +Caching Lookups +--------------- +The idea is to exploit key access patterns by anticipating future lookups +based of previous lookups. + +The simplest incarnation is to save the most recently accessed entry. +This gives optimal performance for use cases where every get is followed +by a set or del to the same key. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/dictobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/dictobject.c new file mode 100644 index 00000000..ca401048 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/dictobject.c @@ -0,0 +1,2086 @@ + +/* Dictionary object implementation using a hash table */ + +/* The distribution includes a separate file, Objects/dictnotes.txt, + describing explorations into dictionary design and optimization. + It covers typical dictionary use patterns, the parameters for + tuning dictionaries, and several ideas for possible optimizations. +*/ + +#include "Python.h" + +typedef PyDictEntry dictentry; +typedef PyDictObject dictobject; + +/* Define this out if you don't want conversion statistics on exit. */ +#undef SHOW_CONVERSION_COUNTS + +/* See large comment block below. This must be >= 1. */ +#define PERTURB_SHIFT 5 + +/* +Major subtleties ahead: Most hash schemes depend on having a "good" hash +function, in the sense of simulating randomness. Python doesn't: its most +important hash functions (for strings and ints) are very regular in common +cases: + +>>> map(hash, (0, 1, 2, 3)) +[0, 1, 2, 3] +>>> map(hash, ("namea", "nameb", "namec", "named")) +[-1658398457, -1658398460, -1658398459, -1658398462] +>>> + +This isn't necessarily bad! To the contrary, in a table of size 2**i, taking +the low-order i bits as the initial table index is extremely fast, and there +are no collisions at all for dicts indexed by a contiguous range of ints. +The same is approximately true when keys are "consecutive" strings. So this +gives better-than-random behavior in common cases, and that's very desirable. + +OTOH, when collisions occur, the tendency to fill contiguous slices of the +hash table makes a good collision resolution strategy crucial. Taking only +the last i bits of the hash code is also vulnerable: for example, consider +[i << 16 for i in range(20000)] as a set of keys. Since ints are their own +hash codes, and this fits in a dict of size 2**15, the last 15 bits of every +hash code are all 0: they *all* map to the same table index. + +But catering to unusual cases should not slow the usual ones, so we just take +the last i bits anyway. It's up to collision resolution to do the rest. If +we *usually* find the key we're looking for on the first try (and, it turns +out, we usually do -- the table load factor is kept under 2/3, so the odds +are solidly in our favor), then it makes best sense to keep the initial index +computation dirt cheap. + +The first half of collision resolution is to visit table indices via this +recurrence: + + j = ((5*j) + 1) mod 2**i + +For any initial j in range(2**i), repeating that 2**i times generates each +int in range(2**i) exactly once (see any text on random-number generation for +proof). By itself, this doesn't help much: like linear probing (setting +j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed +order. This would be bad, except that's not the only thing we do, and it's +actually *good* in the common cases where hash keys are consecutive. In an +example that's really too small to make this entirely clear, for a table of +size 2**3 the order of indices is: + + 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] + +If two things come in at index 5, the first place we look after is index 2, +not 6, so if another comes in at index 6 the collision at 5 didn't hurt it. +Linear probing is deadly in this case because there the fixed probe order +is the *same* as the order consecutive keys are likely to arrive. But it's +extremely unlikely hash codes will follow a 5*j+1 recurrence by accident, +and certain that consecutive hash codes do not. + +The other half of the strategy is to get the other bits of the hash code +into play. This is done by initializing a (unsigned) vrbl "perturb" to the +full hash code, and changing the recurrence to: + + j = (5*j) + 1 + perturb; + perturb >>= PERTURB_SHIFT; + use j % 2**i as the next table index; + +Now the probe sequence depends (eventually) on every bit in the hash code, +and the pseudo-scrambling property of recurring on 5*j+1 is more valuable, +because it quickly magnifies small differences in the bits that didn't affect +the initial index. Note that because perturb is unsigned, if the recurrence +is executed often enough perturb eventually becomes and remains 0. At that +point (very rarely reached) the recurrence is on (just) 5*j+1 again, and +that's certain to find an empty slot eventually (since it generates every int +in range(2**i), and we make sure there's always at least one empty slot). + +Selecting a good value for PERTURB_SHIFT is a balancing act. You want it +small so that the high bits of the hash code continue to affect the probe +sequence across iterations; but you want it large so that in really bad cases +the high-order hash bits have an effect on early iterations. 5 was "the +best" in minimizing total collisions across experiments Tim Peters ran (on +both normal and pathological cases), but 4 and 6 weren't significantly worse. + +Historical: Reimer Behrends contributed the idea of using a polynomial-based +approach, using repeated multiplication by x in GF(2**n) where an irreducible +polynomial for each table size was chosen such that x was a primitive root. +Christian Tismer later extended that to use division by x instead, as an +efficient way to get the high bits of the hash code into play. This scheme +also gave excellent collision statistics, but was more expensive: two +if-tests were required inside the loop; computing "the next" index took about +the same number of operations but without as much potential parallelism +(e.g., computing 5*j can go on at the same time as computing 1+perturb in the +above, and then shifting perturb can be done while the table index is being +masked); and the dictobject struct required a member to hold the table's +polynomial. In Tim's experiments the current scheme ran faster, produced +equally good collision statistics, needed less code & used less memory. +*/ + +/* Object used as dummy key to fill deleted entries */ +static PyObject *dummy; /* Initialized by first call to newdictobject() */ + +/* forward declarations */ +static dictentry * +lookdict_string(dictobject *mp, PyObject *key, long hash); + +#ifdef SHOW_CONVERSION_COUNTS +static long created = 0L; +static long converted = 0L; + +static void +show_counts(void) +{ + fprintf(stderr, "created %ld string dicts\n", created); + fprintf(stderr, "converted %ld to normal dicts\n", converted); + fprintf(stderr, "%.2f%% conversion rate\n", (100.0*converted)/created); +} +#endif + +/* Initialization macros. + There are two ways to create a dict: PyDict_New() is the main C API + function, and the tp_new slot maps to dict_new(). In the latter case we + can save a little time over what PyDict_New does because it's guaranteed + that the PyDictObject struct is already zeroed out. + Everyone except dict_new() should use EMPTY_TO_MINSIZE (unless they have + an excellent reason not to). +*/ + +#define INIT_NONZERO_DICT_SLOTS(mp) do { \ + (mp)->ma_table = (mp)->ma_smalltable; \ + (mp)->ma_mask = PyDict_MINSIZE - 1; \ + } while(0) + +#define EMPTY_TO_MINSIZE(mp) do { \ + memset((mp)->ma_smalltable, 0, sizeof((mp)->ma_smalltable)); \ + (mp)->ma_used = (mp)->ma_fill = 0; \ + INIT_NONZERO_DICT_SLOTS(mp); \ + } while(0) + +PyObject * +PyDict_New(void) +{ + register dictobject *mp; + if (dummy == NULL) { /* Auto-initialize dummy */ + dummy = PyString_FromString(""); + if (dummy == NULL) + return NULL; +#ifdef SHOW_CONVERSION_COUNTS + Py_AtExit(show_counts); +#endif + } + mp = PyObject_GC_New(dictobject, &PyDict_Type); + if (mp == NULL) + return NULL; + EMPTY_TO_MINSIZE(mp); + mp->ma_lookup = lookdict_string; +#ifdef SHOW_CONVERSION_COUNTS + ++created; +#endif + _PyObject_GC_TRACK(mp); + return (PyObject *)mp; +} + +/* +The basic lookup function used by all operations. +This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. +Open addressing is preferred over chaining since the link overhead for +chaining would be substantial (100% with typical malloc overhead). + +The initial probe index is computed as hash mod the table size. Subsequent +probe indices are computed as explained earlier. + +All arithmetic on hash should ignore overflow. + +(The details in this version are due to Tim Peters, building on many past +contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and +Christian Tismer). + +This function must never return NULL; failures are indicated by returning +a dictentry* for which the me_value field is NULL. Exceptions are never +reported by this function, and outstanding exceptions are maintained. +*/ + +static dictentry * +lookdict(dictobject *mp, PyObject *key, register long hash) +{ + register int i; + register unsigned int perturb; + register dictentry *freeslot; + register unsigned int mask = mp->ma_mask; + dictentry *ep0 = mp->ma_table; + register dictentry *ep; + register int restore_error; + register int checked_error; + register int cmp; + PyObject *err_type, *err_value, *err_tb; + PyObject *startkey; + + i = hash & mask; + ep = &ep0[i]; + if (ep->me_key == NULL || ep->me_key == key) + return ep; + + restore_error = checked_error = 0; + if (ep->me_key == dummy) + freeslot = ep; + else { + if (ep->me_hash == hash) { + /* error can't have been checked yet */ + checked_error = 1; + if (PyErr_Occurred()) { + restore_error = 1; + PyErr_Fetch(&err_type, &err_value, &err_tb); + } + startkey = ep->me_key; + cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + if (cmp < 0) + PyErr_Clear(); + if (ep0 == mp->ma_table && ep->me_key == startkey) { + if (cmp > 0) + goto Done; + } + else { + /* The compare did major nasty stuff to the + * dict: start over. + * XXX A clever adversary could prevent this + * XXX from terminating. + */ + ep = lookdict(mp, key, hash); + goto Done; + } + } + freeslot = NULL; + } + + /* In the loop, me_key == dummy is by far (factor of 100s) the + least likely outcome, so test for that last. */ + for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { + i = (i << 2) + i + perturb + 1; + ep = &ep0[i & mask]; + if (ep->me_key == NULL) { + if (freeslot != NULL) + ep = freeslot; + break; + } + if (ep->me_key == key) + break; + if (ep->me_hash == hash && ep->me_key != dummy) { + if (!checked_error) { + checked_error = 1; + if (PyErr_Occurred()) { + restore_error = 1; + PyErr_Fetch(&err_type, &err_value, + &err_tb); + } + } + startkey = ep->me_key; + cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); + if (cmp < 0) + PyErr_Clear(); + if (ep0 == mp->ma_table && ep->me_key == startkey) { + if (cmp > 0) + break; + } + else { + /* The compare did major nasty stuff to the + * dict: start over. + * XXX A clever adversary could prevent this + * XXX from terminating. + */ + ep = lookdict(mp, key, hash); + break; + } + } + else if (ep->me_key == dummy && freeslot == NULL) + freeslot = ep; + } + +Done: + if (restore_error) + PyErr_Restore(err_type, err_value, err_tb); + return ep; +} + +/* + * Hacked up version of lookdict which can assume keys are always strings; + * this assumption allows testing for errors during PyObject_Compare() to + * be dropped; string-string comparisons never raise exceptions. This also + * means we don't need to go through PyObject_Compare(); we can always use + * _PyString_Eq directly. + * + * This is valuable because the general-case error handling in lookdict() is + * expensive, and dicts with pure-string keys are very common. + */ +static dictentry * +lookdict_string(dictobject *mp, PyObject *key, register long hash) +{ + register int i; + register unsigned int perturb; + register dictentry *freeslot; + register unsigned int mask = mp->ma_mask; + dictentry *ep0 = mp->ma_table; + register dictentry *ep; + + /* Make sure this function doesn't have to handle non-string keys, + including subclasses of str; e.g., one reason to subclass + strings is to override __eq__, and for speed we don't cater to + that here. */ + if (!PyString_CheckExact(key)) { +#ifdef SHOW_CONVERSION_COUNTS + ++converted; +#endif + mp->ma_lookup = lookdict; + return lookdict(mp, key, hash); + } + i = hash & mask; + ep = &ep0[i]; + if (ep->me_key == NULL || ep->me_key == key) + return ep; + if (ep->me_key == dummy) + freeslot = ep; + else { + if (ep->me_hash == hash + && _PyString_Eq(ep->me_key, key)) { + return ep; + } + freeslot = NULL; + } + + /* In the loop, me_key == dummy is by far (factor of 100s) the + least likely outcome, so test for that last. */ + for (perturb = hash; ; perturb >>= PERTURB_SHIFT) { + i = (i << 2) + i + perturb + 1; + ep = &ep0[i & mask]; + if (ep->me_key == NULL) + return freeslot == NULL ? ep : freeslot; + if (ep->me_key == key + || (ep->me_hash == hash + && ep->me_key != dummy + && _PyString_Eq(ep->me_key, key))) + return ep; + if (ep->me_key == dummy && freeslot == NULL) + freeslot = ep; + } +} + +/* +Internal routine to insert a new item into the table. +Used both by the internal resize routine and by the public insert routine. +Eats a reference to key and one to value. +*/ +static void +insertdict(register dictobject *mp, PyObject *key, long hash, PyObject *value) +{ + PyObject *old_value; + register dictentry *ep; + typedef PyDictEntry *(*lookupfunc)(PyDictObject *, PyObject *, long); + + assert(mp->ma_lookup != NULL); + ep = mp->ma_lookup(mp, key, hash); + if (ep->me_value != NULL) { + old_value = ep->me_value; + ep->me_value = value; + Py_DECREF(old_value); /* which **CAN** re-enter */ + Py_DECREF(key); + } + else { + if (ep->me_key == NULL) + mp->ma_fill++; + else + Py_DECREF(ep->me_key); + ep->me_key = key; + ep->me_hash = hash; + ep->me_value = value; + mp->ma_used++; + } +} + +/* +Restructure the table by allocating a new table and reinserting all +items again. When entries have been deleted, the new table may +actually be smaller than the old one. +*/ +static int +dictresize(dictobject *mp, int minused) +{ + int newsize; + dictentry *oldtable, *newtable, *ep; + int i; + int is_oldtable_malloced; + dictentry small_copy[PyDict_MINSIZE]; + + assert(minused >= 0); + + /* Find the smallest table size > minused. */ + for (newsize = PyDict_MINSIZE; + newsize <= minused && newsize > 0; + newsize <<= 1) + ; + if (newsize <= 0) { + PyErr_NoMemory(); + return -1; + } + + /* Get space for a new table. */ + oldtable = mp->ma_table; + assert(oldtable != NULL); + is_oldtable_malloced = oldtable != mp->ma_smalltable; + + if (newsize == PyDict_MINSIZE) { + /* A large table is shrinking, or we can't get any smaller. */ + newtable = mp->ma_smalltable; + if (newtable == oldtable) { + if (mp->ma_fill == mp->ma_used) { + /* No dummies, so no point doing anything. */ + return 0; + } + /* We're not going to resize it, but rebuild the + table anyway to purge old dummy entries. + Subtle: This is *necessary* if fill==size, + as lookdict needs at least one virgin slot to + terminate failing searches. If fill < size, it's + merely desirable, as dummies slow searches. */ + assert(mp->ma_fill > mp->ma_used); + memcpy(small_copy, oldtable, sizeof(small_copy)); + oldtable = small_copy; + } + } + else { + newtable = PyMem_NEW(dictentry, newsize); + if (newtable == NULL) { + PyErr_NoMemory(); + return -1; + } + } + + /* Make the dict empty, using the new table. */ + assert(newtable != oldtable); + mp->ma_table = newtable; + mp->ma_mask = newsize - 1; + memset(newtable, 0, sizeof(dictentry) * newsize); + mp->ma_used = 0; + i = mp->ma_fill; + mp->ma_fill = 0; + + /* Copy the data over; this is refcount-neutral for active entries; + dummy entries aren't copied over, of course */ + for (ep = oldtable; i > 0; ep++) { + if (ep->me_value != NULL) { /* active entry */ + --i; + insertdict(mp, ep->me_key, ep->me_hash, ep->me_value); + } + else if (ep->me_key != NULL) { /* dummy entry */ + --i; + assert(ep->me_key == dummy); + Py_DECREF(ep->me_key); + } + /* else key == value == NULL: nothing to do */ + } + + if (is_oldtable_malloced) + PyMem_DEL(oldtable); + return 0; +} + +PyObject * +PyDict_GetItem(PyObject *op, PyObject *key) +{ + long hash; + dictobject *mp = (dictobject *)op; + if (!PyDict_Check(op)) { + return NULL; + } + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) + { + hash = PyObject_Hash(key); + if (hash == -1) { + PyErr_Clear(); + return NULL; + } + } + return (mp->ma_lookup)(mp, key, hash)->me_value; +} + +/* CAUTION: PyDict_SetItem() must guarantee that it won't resize the + * dictionary if it is merely replacing the value for an existing key. + * This is means that it's safe to loop over a dictionary with + * PyDict_Next() and occasionally replace a value -- but you can't + * insert new keys or remove them. + */ +int +PyDict_SetItem(register PyObject *op, PyObject *key, PyObject *value) +{ + register dictobject *mp; + register long hash; + register int n_used; + + if (!PyDict_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + mp = (dictobject *)op; + if (PyString_CheckExact(key)) { + hash = ((PyStringObject *)key)->ob_shash; + if (hash == -1) + hash = PyObject_Hash(key); + } + else { + hash = PyObject_Hash(key); + if (hash == -1) + return -1; + } + assert(mp->ma_fill <= mp->ma_mask); /* at least one empty slot */ + n_used = mp->ma_used; + Py_INCREF(value); + Py_INCREF(key); + insertdict(mp, key, hash, value); + /* If we added a key, we can safely resize. Otherwise just return! + * If fill >= 2/3 size, adjust size. Normally, this doubles or + * quaduples the size, but it's also possible for the dict to shrink + * (if ma_fill is much larger than ma_used, meaning a lot of dict + * keys have been * deleted). + * + * Quadrupling the size improves average dictionary sparseness + * (reducing collisions) at the cost of some memory and iteration + * speed (which loops over every possible entry). It also halves + * the number of expensive resize operations in a growing dictionary. + * + * Very large dictionaries (over 50K items) use doubling instead. + * This may help applications with severe memory constraints. + */ + if (!(mp->ma_used > n_used && mp->ma_fill*3 >= (mp->ma_mask+1)*2)) + return 0; + return dictresize(mp, mp->ma_used*(mp->ma_used>50000 ? 2 : 4)); +} + +int +PyDict_DelItem(PyObject *op, PyObject *key) +{ + register dictobject *mp; + register long hash; + register dictentry *ep; + PyObject *old_value, *old_key; + + if (!PyDict_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { + hash = PyObject_Hash(key); + if (hash == -1) + return -1; + } + mp = (dictobject *)op; + ep = (mp->ma_lookup)(mp, key, hash); + if (ep->me_value == NULL) { + PyErr_SetObject(PyExc_KeyError, key); + return -1; + } + old_key = ep->me_key; + Py_INCREF(dummy); + ep->me_key = dummy; + old_value = ep->me_value; + ep->me_value = NULL; + mp->ma_used--; + Py_DECREF(old_value); + Py_DECREF(old_key); + return 0; +} + +void +PyDict_Clear(PyObject *op) +{ + dictobject *mp; + dictentry *ep, *table; + int table_is_malloced; + int fill; + dictentry small_copy[PyDict_MINSIZE]; +#ifdef Py_DEBUG + int i, n; +#endif + + if (!PyDict_Check(op)) + return; + mp = (dictobject *)op; +#ifdef Py_DEBUG + n = mp->ma_mask + 1; + i = 0; +#endif + + table = mp->ma_table; + assert(table != NULL); + table_is_malloced = table != mp->ma_smalltable; + + /* This is delicate. During the process of clearing the dict, + * decrefs can cause the dict to mutate. To avoid fatal confusion + * (voice of experience), we have to make the dict empty before + * clearing the slots, and never refer to anything via mp->xxx while + * clearing. + */ + fill = mp->ma_fill; + if (table_is_malloced) + EMPTY_TO_MINSIZE(mp); + + else if (fill > 0) { + /* It's a small table with something that needs to be cleared. + * Afraid the only safe way is to copy the dict entries into + * another small table first. + */ + memcpy(small_copy, table, sizeof(small_copy)); + table = small_copy; + EMPTY_TO_MINSIZE(mp); + } + /* else it's a small table that's already empty */ + + /* Now we can finally clear things. If C had refcounts, we could + * assert that the refcount on table is 1 now, i.e. that this function + * has unique access to it, so decref side-effects can't alter it. + */ + for (ep = table; fill > 0; ++ep) { +#ifdef Py_DEBUG + assert(i < n); + ++i; +#endif + if (ep->me_key) { + --fill; + Py_DECREF(ep->me_key); + Py_XDECREF(ep->me_value); + } +#ifdef Py_DEBUG + else + assert(ep->me_value == NULL); +#endif + } + + if (table_is_malloced) + PyMem_DEL(table); +} + +/* + * Iterate over a dict. Use like so: + * + * int i; + * PyObject *key, *value; + * i = 0; # important! i should not otherwise be changed by you + * while (PyDict_Next(yourdict, &i, &key, &value)) { + * Refer to borrowed references in key and value. + * } + * + * CAUTION: In general, it isn't safe to use PyDict_Next in a loop that + * mutates the dict. One exception: it is safe if the loop merely changes + * the values associated with the keys (but doesn't insert new keys or + * delete keys), via PyDict_SetItem(). + */ +int +PyDict_Next(PyObject *op, int *ppos, PyObject **pkey, PyObject **pvalue) +{ + int i; + register dictobject *mp; + if (!PyDict_Check(op)) + return 0; + mp = (dictobject *)op; + i = *ppos; + if (i < 0) + return 0; + while (i <= mp->ma_mask && mp->ma_table[i].me_value == NULL) + i++; + *ppos = i+1; + if (i > mp->ma_mask) + return 0; + if (pkey) + *pkey = mp->ma_table[i].me_key; + if (pvalue) + *pvalue = mp->ma_table[i].me_value; + return 1; +} + +/* Methods */ + +static void +dict_dealloc(register dictobject *mp) +{ + register dictentry *ep; + int fill = mp->ma_fill; + PyObject_GC_UnTrack(mp); + Py_TRASHCAN_SAFE_BEGIN(mp) + for (ep = mp->ma_table; fill > 0; ep++) { + if (ep->me_key) { + --fill; + Py_DECREF(ep->me_key); + Py_XDECREF(ep->me_value); + } + } + if (mp->ma_table != mp->ma_smalltable) + PyMem_DEL(mp->ma_table); + mp->ob_type->tp_free((PyObject *)mp); + Py_TRASHCAN_SAFE_END(mp) +} + +static int +dict_print(register dictobject *mp, register FILE *fp, register int flags) +{ + register int i; + register int any; + + i = Py_ReprEnter((PyObject*)mp); + if (i != 0) { + if (i < 0) + return i; + fprintf(fp, "{...}"); + return 0; + } + + fprintf(fp, "{"); + any = 0; + for (i = 0; i <= mp->ma_mask; i++) { + dictentry *ep = mp->ma_table + i; + PyObject *pvalue = ep->me_value; + if (pvalue != NULL) { + /* Prevent PyObject_Repr from deleting value during + key format */ + Py_INCREF(pvalue); + if (any++ > 0) + fprintf(fp, ", "); + if (PyObject_Print((PyObject *)ep->me_key, fp, 0)!=0) { + Py_DECREF(pvalue); + Py_ReprLeave((PyObject*)mp); + return -1; + } + fprintf(fp, ": "); + if (PyObject_Print(pvalue, fp, 0) != 0) { + Py_DECREF(pvalue); + Py_ReprLeave((PyObject*)mp); + return -1; + } + Py_DECREF(pvalue); + } + } + fprintf(fp, "}"); + Py_ReprLeave((PyObject*)mp); + return 0; +} + +static PyObject * +dict_repr(dictobject *mp) +{ + int i; + PyObject *s, *temp, *colon = NULL; + PyObject *pieces = NULL, *result = NULL; + PyObject *key, *value; + + i = Py_ReprEnter((PyObject *)mp); + if (i != 0) { + return i > 0 ? PyString_FromString("{...}") : NULL; + } + + if (mp->ma_used == 0) { + result = PyString_FromString("{}"); + goto Done; + } + + pieces = PyList_New(0); + if (pieces == NULL) + goto Done; + + colon = PyString_FromString(": "); + if (colon == NULL) + goto Done; + + /* Do repr() on each key+value pair, and insert ": " between them. + Note that repr may mutate the dict. */ + i = 0; + while (PyDict_Next((PyObject *)mp, &i, &key, &value)) { + int status; + /* Prevent repr from deleting value during key format. */ + Py_INCREF(value); + s = PyObject_Repr(key); + PyString_Concat(&s, colon); + PyString_ConcatAndDel(&s, PyObject_Repr(value)); + Py_DECREF(value); + if (s == NULL) + goto Done; + status = PyList_Append(pieces, s); + Py_DECREF(s); /* append created a new ref */ + if (status < 0) + goto Done; + } + + /* Add "{}" decorations to the first and last items. */ + assert(PyList_GET_SIZE(pieces) > 0); + s = PyString_FromString("{"); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, 0); + PyString_ConcatAndDel(&s, temp); + PyList_SET_ITEM(pieces, 0, s); + if (s == NULL) + goto Done; + + s = PyString_FromString("}"); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1); + PyString_ConcatAndDel(&temp, s); + PyList_SET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1, temp); + if (temp == NULL) + goto Done; + + /* Paste them all together with ", " between. */ + s = PyString_FromString(", "); + if (s == NULL) + goto Done; + result = _PyString_Join(s, pieces); + Py_DECREF(s); + +Done: + Py_XDECREF(pieces); + Py_XDECREF(colon); + Py_ReprLeave((PyObject *)mp); + return result; +} + +static int +dict_length(dictobject *mp) +{ + return mp->ma_used; +} + +static PyObject * +dict_subscript(dictobject *mp, register PyObject *key) +{ + PyObject *v; + long hash; + assert(mp->ma_table != NULL); + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { + hash = PyObject_Hash(key); + if (hash == -1) + return NULL; + } + v = (mp->ma_lookup)(mp, key, hash) -> me_value; + if (v == NULL) + PyErr_SetObject(PyExc_KeyError, key); + else + Py_INCREF(v); + return v; +} + +static int +dict_ass_sub(dictobject *mp, PyObject *v, PyObject *w) +{ + if (w == NULL) + return PyDict_DelItem((PyObject *)mp, v); + else + return PyDict_SetItem((PyObject *)mp, v, w); +} + +static PyMappingMethods dict_as_mapping = { + (inquiry)dict_length, /*mp_length*/ + (binaryfunc)dict_subscript, /*mp_subscript*/ + (objobjargproc)dict_ass_sub, /*mp_ass_subscript*/ +}; + +static PyObject * +dict_keys(register dictobject *mp) +{ + register PyObject *v; + register int i, j, n; + + again: + n = mp->ma_used; + v = PyList_New(n); + if (v == NULL) + return NULL; + if (n != mp->ma_used) { + /* Durnit. The allocations caused the dict to resize. + * Just start over, this shouldn't normally happen. + */ + Py_DECREF(v); + goto again; + } + for (i = 0, j = 0; i <= mp->ma_mask; i++) { + if (mp->ma_table[i].me_value != NULL) { + PyObject *key = mp->ma_table[i].me_key; + Py_INCREF(key); + PyList_SET_ITEM(v, j, key); + j++; + } + } + return v; +} + +static PyObject * +dict_values(register dictobject *mp) +{ + register PyObject *v; + register int i, j, n; + + again: + n = mp->ma_used; + v = PyList_New(n); + if (v == NULL) + return NULL; + if (n != mp->ma_used) { + /* Durnit. The allocations caused the dict to resize. + * Just start over, this shouldn't normally happen. + */ + Py_DECREF(v); + goto again; + } + for (i = 0, j = 0; i <= mp->ma_mask; i++) { + if (mp->ma_table[i].me_value != NULL) { + PyObject *value = mp->ma_table[i].me_value; + Py_INCREF(value); + PyList_SET_ITEM(v, j, value); + j++; + } + } + return v; +} + +static PyObject * +dict_items(register dictobject *mp) +{ + register PyObject *v; + register int i, j, n; + PyObject *item, *key, *value; + + /* Preallocate the list of tuples, to avoid allocations during + * the loop over the items, which could trigger GC, which + * could resize the dict. :-( + */ + again: + n = mp->ma_used; + v = PyList_New(n); + if (v == NULL) + return NULL; + for (i = 0; i < n; i++) { + item = PyTuple_New(2); + if (item == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SET_ITEM(v, i, item); + } + if (n != mp->ma_used) { + /* Durnit. The allocations caused the dict to resize. + * Just start over, this shouldn't normally happen. + */ + Py_DECREF(v); + goto again; + } + /* Nothing we do below makes any function calls. */ + for (i = 0, j = 0; i <= mp->ma_mask; i++) { + if (mp->ma_table[i].me_value != NULL) { + key = mp->ma_table[i].me_key; + value = mp->ma_table[i].me_value; + item = PyList_GET_ITEM(v, j); + Py_INCREF(key); + PyTuple_SET_ITEM(item, 0, key); + Py_INCREF(value); + PyTuple_SET_ITEM(item, 1, value); + j++; + } + } + assert(j == n); + return v; +} + +static PyObject * +dict_fromkeys(PyObject *cls, PyObject *args) +{ + PyObject *seq; + PyObject *value = Py_None; + PyObject *it; /* iter(seq) */ + PyObject *key; + PyObject *d; + int status; + + if (!PyArg_UnpackTuple(args, "fromkeys", 1, 2, &seq, &value)) + return NULL; + + d = PyObject_CallObject(cls, NULL); + if (d == NULL) + return NULL; + + it = PyObject_GetIter(seq); + if (it == NULL){ + Py_DECREF(d); + return NULL; + } + + for (;;) { + key = PyIter_Next(it); + if (key == NULL) { + if (PyErr_Occurred()) + goto Fail; + break; + } + status = PyObject_SetItem(d, key, value); + Py_DECREF(key); + if (status < 0) + goto Fail; + } + + Py_DECREF(it); + return d; + +Fail: + Py_DECREF(it); + Py_DECREF(d); + return NULL; +} + +static PyObject * +dict_update(PyObject *mp, PyObject *other) +{ + if (PyDict_Update(mp, other) < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +/* Update unconditionally replaces existing items. + Merge has a 3rd argument 'override'; if set, it acts like Update, + otherwise it leaves existing items unchanged. + + PyDict_{Update,Merge} update/merge from a mapping object. + + PyDict_MergeFromSeq2 updates/merges from any iterable object + producing iterable objects of length 2. +*/ + +int +PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) +{ + PyObject *it; /* iter(seq2) */ + int i; /* index into seq2 of current element */ + PyObject *item; /* seq2[i] */ + PyObject *fast; /* item as a 2-tuple or 2-list */ + + assert(d != NULL); + assert(PyDict_Check(d)); + assert(seq2 != NULL); + + it = PyObject_GetIter(seq2); + if (it == NULL) + return -1; + + for (i = 0; ; ++i) { + PyObject *key, *value; + int n; + + fast = NULL; + item = PyIter_Next(it); + if (item == NULL) { + if (PyErr_Occurred()) + goto Fail; + break; + } + + /* Convert item to sequence, and verify length 2. */ + fast = PySequence_Fast(item, ""); + if (fast == NULL) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + PyErr_Format(PyExc_TypeError, + "cannot convert dictionary update " + "sequence element #%d to a sequence", + i); + goto Fail; + } + n = PySequence_Fast_GET_SIZE(fast); + if (n != 2) { + PyErr_Format(PyExc_ValueError, + "dictionary update sequence element #%d " + "has length %d; 2 is required", + i, n); + goto Fail; + } + + /* Update/merge with this (key, value) pair. */ + key = PySequence_Fast_GET_ITEM(fast, 0); + value = PySequence_Fast_GET_ITEM(fast, 1); + if (override || PyDict_GetItem(d, key) == NULL) { + int status = PyDict_SetItem(d, key, value); + if (status < 0) + goto Fail; + } + Py_DECREF(fast); + Py_DECREF(item); + } + + i = 0; + goto Return; +Fail: + Py_XDECREF(item); + Py_XDECREF(fast); + i = -1; +Return: + Py_DECREF(it); + return i; +} + +int +PyDict_Update(PyObject *a, PyObject *b) +{ + return PyDict_Merge(a, b, 1); +} + +int +PyDict_Merge(PyObject *a, PyObject *b, int override) +{ + register PyDictObject *mp, *other; + register int i; + dictentry *entry; + + /* We accept for the argument either a concrete dictionary object, + * or an abstract "mapping" object. For the former, we can do + * things quite efficiently. For the latter, we only require that + * PyMapping_Keys() and PyObject_GetItem() be supported. + */ + if (a == NULL || !PyDict_Check(a) || b == NULL) { + PyErr_BadInternalCall(); + return -1; + } + mp = (dictobject*)a; + if (PyDict_Check(b)) { + other = (dictobject*)b; + if (other == mp || other->ma_used == 0) + /* a.update(a) or a.update({}); nothing to do */ + return 0; + /* Do one big resize at the start, rather than + * incrementally resizing as we insert new items. Expect + * that there will be no (or few) overlapping keys. + */ + if ((mp->ma_fill + other->ma_used)*3 >= (mp->ma_mask+1)*2) { + if (dictresize(mp, (mp->ma_used + other->ma_used)*2) != 0) + return -1; + } + for (i = 0; i <= other->ma_mask; i++) { + entry = &other->ma_table[i]; + if (entry->me_value != NULL && + (override || + PyDict_GetItem(a, entry->me_key) == NULL)) { + Py_INCREF(entry->me_key); + Py_INCREF(entry->me_value); + insertdict(mp, entry->me_key, entry->me_hash, + entry->me_value); + } + } + } + else { + /* Do it the generic, slower way */ + PyObject *keys = PyMapping_Keys(b); + PyObject *iter; + PyObject *key, *value; + int status; + + if (keys == NULL) + /* Docstring says this is equivalent to E.keys() so + * if E doesn't have a .keys() method we want + * AttributeError to percolate up. Might as well + * do the same for any other error. + */ + return -1; + + iter = PyObject_GetIter(keys); + Py_DECREF(keys); + if (iter == NULL) + return -1; + + for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { + if (!override && PyDict_GetItem(a, key) != NULL) { + Py_DECREF(key); + continue; + } + value = PyObject_GetItem(b, key); + if (value == NULL) { + Py_DECREF(iter); + Py_DECREF(key); + return -1; + } + status = PyDict_SetItem(a, key, value); + Py_DECREF(key); + Py_DECREF(value); + if (status < 0) { + Py_DECREF(iter); + return -1; + } + } + Py_DECREF(iter); + if (PyErr_Occurred()) + /* Iterator completed, via error */ + return -1; + } + return 0; +} + +static PyObject * +dict_copy(register dictobject *mp) +{ + return PyDict_Copy((PyObject*)mp); +} + +PyObject * +PyDict_Copy(PyObject *o) +{ + register dictobject *mp; + register int i; + dictobject *copy; + dictentry *entry; + + if (o == NULL || !PyDict_Check(o)) { + PyErr_BadInternalCall(); + return NULL; + } + mp = (dictobject *)o; + copy = (dictobject *)PyDict_New(); + if (copy == NULL) + return NULL; + if (mp->ma_used > 0) { + if (dictresize(copy, mp->ma_used*2) != 0) + return NULL; + for (i = 0; i <= mp->ma_mask; i++) { + entry = &mp->ma_table[i]; + if (entry->me_value != NULL) { + Py_INCREF(entry->me_key); + Py_INCREF(entry->me_value); + insertdict(copy, entry->me_key, entry->me_hash, + entry->me_value); + } + } + } + return (PyObject *)copy; +} + +int +PyDict_Size(PyObject *mp) +{ + if (mp == NULL || !PyDict_Check(mp)) { + PyErr_BadInternalCall(); + return 0; + } + return ((dictobject *)mp)->ma_used; +} + +PyObject * +PyDict_Keys(PyObject *mp) +{ + if (mp == NULL || !PyDict_Check(mp)) { + PyErr_BadInternalCall(); + return NULL; + } + return dict_keys((dictobject *)mp); +} + +PyObject * +PyDict_Values(PyObject *mp) +{ + if (mp == NULL || !PyDict_Check(mp)) { + PyErr_BadInternalCall(); + return NULL; + } + return dict_values((dictobject *)mp); +} + +PyObject * +PyDict_Items(PyObject *mp) +{ + if (mp == NULL || !PyDict_Check(mp)) { + PyErr_BadInternalCall(); + return NULL; + } + return dict_items((dictobject *)mp); +} + +/* Subroutine which returns the smallest key in a for which b's value + is different or absent. The value is returned too, through the + pval argument. Both are NULL if no key in a is found for which b's status + differs. The refcounts on (and only on) non-NULL *pval and function return + values must be decremented by the caller (characterize() increments them + to ensure that mutating comparison and PyDict_GetItem calls can't delete + them before the caller is done looking at them). */ + +static PyObject * +characterize(dictobject *a, dictobject *b, PyObject **pval) +{ + PyObject *akey = NULL; /* smallest key in a s.t. a[akey] != b[akey] */ + PyObject *aval = NULL; /* a[akey] */ + int i, cmp; + + for (i = 0; i <= a->ma_mask; i++) { + PyObject *thiskey, *thisaval, *thisbval; + if (a->ma_table[i].me_value == NULL) + continue; + thiskey = a->ma_table[i].me_key; + Py_INCREF(thiskey); /* keep alive across compares */ + if (akey != NULL) { + cmp = PyObject_RichCompareBool(akey, thiskey, Py_LT); + if (cmp < 0) { + Py_DECREF(thiskey); + goto Fail; + } + if (cmp > 0 || + i > a->ma_mask || + a->ma_table[i].me_value == NULL) + { + /* Not the *smallest* a key; or maybe it is + * but the compare shrunk the dict so we can't + * find its associated value anymore; or + * maybe it is but the compare deleted the + * a[thiskey] entry. + */ + Py_DECREF(thiskey); + continue; + } + } + + /* Compare a[thiskey] to b[thiskey]; cmp <- true iff equal. */ + thisaval = a->ma_table[i].me_value; + assert(thisaval); + Py_INCREF(thisaval); /* keep alive */ + thisbval = PyDict_GetItem((PyObject *)b, thiskey); + if (thisbval == NULL) + cmp = 0; + else { + /* both dicts have thiskey: same values? */ + cmp = PyObject_RichCompareBool( + thisaval, thisbval, Py_EQ); + if (cmp < 0) { + Py_DECREF(thiskey); + Py_DECREF(thisaval); + goto Fail; + } + } + if (cmp == 0) { + /* New winner. */ + Py_XDECREF(akey); + Py_XDECREF(aval); + akey = thiskey; + aval = thisaval; + } + else { + Py_DECREF(thiskey); + Py_DECREF(thisaval); + } + } + *pval = aval; + return akey; + +Fail: + Py_XDECREF(akey); + Py_XDECREF(aval); + *pval = NULL; + return NULL; +} + +static int +dict_compare(dictobject *a, dictobject *b) +{ + PyObject *adiff, *bdiff, *aval, *bval; + int res; + + /* Compare lengths first */ + if (a->ma_used < b->ma_used) + return -1; /* a is shorter */ + else if (a->ma_used > b->ma_used) + return 1; /* b is shorter */ + + /* Same length -- check all keys */ + bdiff = bval = NULL; + adiff = characterize(a, b, &aval); + if (adiff == NULL) { + assert(!aval); + /* Either an error, or a is a subset with the same length so + * must be equal. + */ + res = PyErr_Occurred() ? -1 : 0; + goto Finished; + } + bdiff = characterize(b, a, &bval); + if (bdiff == NULL && PyErr_Occurred()) { + assert(!bval); + res = -1; + goto Finished; + } + res = 0; + if (bdiff) { + /* bdiff == NULL "should be" impossible now, but perhaps + * the last comparison done by the characterize() on a had + * the side effect of making the dicts equal! + */ + res = PyObject_Compare(adiff, bdiff); + } + if (res == 0 && bval != NULL) + res = PyObject_Compare(aval, bval); + +Finished: + Py_XDECREF(adiff); + Py_XDECREF(bdiff); + Py_XDECREF(aval); + Py_XDECREF(bval); + return res; +} + +/* Return 1 if dicts equal, 0 if not, -1 if error. + * Gets out as soon as any difference is detected. + * Uses only Py_EQ comparison. + */ +static int +dict_equal(dictobject *a, dictobject *b) +{ + int i; + + if (a->ma_used != b->ma_used) + /* can't be equal if # of entries differ */ + return 0; + + /* Same # of entries -- check all of 'em. Exit early on any diff. */ + for (i = 0; i <= a->ma_mask; i++) { + PyObject *aval = a->ma_table[i].me_value; + if (aval != NULL) { + int cmp; + PyObject *bval; + PyObject *key = a->ma_table[i].me_key; + /* temporarily bump aval's refcount to ensure it stays + alive until we're done with it */ + Py_INCREF(aval); + bval = PyDict_GetItem((PyObject *)b, key); + if (bval == NULL) { + Py_DECREF(aval); + return 0; + } + cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); + Py_DECREF(aval); + if (cmp <= 0) /* error or not equal */ + return cmp; + } + } + return 1; + } + +static PyObject * +dict_richcompare(PyObject *v, PyObject *w, int op) +{ + int cmp; + PyObject *res; + + if (!PyDict_Check(v) || !PyDict_Check(w)) { + res = Py_NotImplemented; + } + else if (op == Py_EQ || op == Py_NE) { + cmp = dict_equal((dictobject *)v, (dictobject *)w); + if (cmp < 0) + return NULL; + res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; + } + else + res = Py_NotImplemented; + Py_INCREF(res); + return res; + } + +static PyObject * +dict_has_key(register dictobject *mp, PyObject *key) +{ + long hash; + register long ok; + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { + hash = PyObject_Hash(key); + if (hash == -1) + return NULL; + } + ok = (mp->ma_lookup)(mp, key, hash)->me_value != NULL; + return PyBool_FromLong(ok); +} + +static PyObject * +dict_get(register dictobject *mp, PyObject *args) +{ + PyObject *key; + PyObject *failobj = Py_None; + PyObject *val = NULL; + long hash; + + if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &failobj)) + return NULL; + + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { + hash = PyObject_Hash(key); + if (hash == -1) + return NULL; + } + val = (mp->ma_lookup)(mp, key, hash)->me_value; + + if (val == NULL) + val = failobj; + Py_INCREF(val); + return val; +} + + +static PyObject * +dict_setdefault(register dictobject *mp, PyObject *args) +{ + PyObject *key; + PyObject *failobj = Py_None; + PyObject *val = NULL; + long hash; + + if (!PyArg_UnpackTuple(args, "setdefault", 1, 2, &key, &failobj)) + return NULL; + + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { + hash = PyObject_Hash(key); + if (hash == -1) + return NULL; + } + val = (mp->ma_lookup)(mp, key, hash)->me_value; + if (val == NULL) { + val = failobj; + if (PyDict_SetItem((PyObject*)mp, key, failobj)) + val = NULL; + } + Py_XINCREF(val); + return val; +} + + +static PyObject * +dict_clear(register dictobject *mp) +{ + PyDict_Clear((PyObject *)mp); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +dict_pop(dictobject *mp, PyObject *args) +{ + long hash; + dictentry *ep; + PyObject *old_value, *old_key; + PyObject *key, *deflt = NULL; + + if(!PyArg_UnpackTuple(args, "pop", 1, 2, &key, &deflt)) + return NULL; + if (mp->ma_used == 0) { + if (deflt) { + Py_INCREF(deflt); + return deflt; + } + PyErr_SetString(PyExc_KeyError, + "pop(): dictionary is empty"); + return NULL; + } + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { + hash = PyObject_Hash(key); + if (hash == -1) + return NULL; + } + ep = (mp->ma_lookup)(mp, key, hash); + if (ep->me_value == NULL) { + if (deflt) { + Py_INCREF(deflt); + return deflt; + } + PyErr_SetObject(PyExc_KeyError, key); + return NULL; + } + old_key = ep->me_key; + Py_INCREF(dummy); + ep->me_key = dummy; + old_value = ep->me_value; + ep->me_value = NULL; + mp->ma_used--; + Py_DECREF(old_key); + return old_value; +} + +static PyObject * +dict_popitem(dictobject *mp) +{ + int i = 0; + dictentry *ep; + PyObject *res; + + /* Allocate the result tuple before checking the size. Believe it + * or not, this allocation could trigger a garbage collection which + * could empty the dict, so if we checked the size first and that + * happened, the result would be an infinite loop (searching for an + * entry that no longer exists). Note that the usual popitem() + * idiom is "while d: k, v = d.popitem()". so needing to throw the + * tuple away if the dict *is* empty isn't a significant + * inefficiency -- possible, but unlikely in practice. + */ + res = PyTuple_New(2); + if (res == NULL) + return NULL; + if (mp->ma_used == 0) { + Py_DECREF(res); + PyErr_SetString(PyExc_KeyError, + "popitem(): dictionary is empty"); + return NULL; + } + /* Set ep to "the first" dict entry with a value. We abuse the hash + * field of slot 0 to hold a search finger: + * If slot 0 has a value, use slot 0. + * Else slot 0 is being used to hold a search finger, + * and we use its hash value as the first index to look. + */ + ep = &mp->ma_table[0]; + if (ep->me_value == NULL) { + i = (int)ep->me_hash; + /* The hash field may be a real hash value, or it may be a + * legit search finger, or it may be a once-legit search + * finger that's out of bounds now because it wrapped around + * or the table shrunk -- simply make sure it's in bounds now. + */ + if (i > mp->ma_mask || i < 1) + i = 1; /* skip slot 0 */ + while ((ep = &mp->ma_table[i])->me_value == NULL) { + i++; + if (i > mp->ma_mask) + i = 1; + } + } + PyTuple_SET_ITEM(res, 0, ep->me_key); + PyTuple_SET_ITEM(res, 1, ep->me_value); + Py_INCREF(dummy); + ep->me_key = dummy; + ep->me_value = NULL; + mp->ma_used--; + assert(mp->ma_table[0].me_value == NULL); + mp->ma_table[0].me_hash = i + 1; /* next place to start */ + return res; +} + +static int +dict_traverse(PyObject *op, visitproc visit, void *arg) +{ + int i = 0, err; + PyObject *pk; + PyObject *pv; + + while (PyDict_Next(op, &i, &pk, &pv)) { + err = visit(pk, arg); + if (err) + return err; + err = visit(pv, arg); + if (err) + return err; + } + return 0; +} + +static int +dict_tp_clear(PyObject *op) +{ + PyDict_Clear(op); + return 0; +} + + +static PyObject *dictiter_new(dictobject *, binaryfunc); + +static PyObject * +select_key(PyObject *key, PyObject *value) +{ + Py_INCREF(key); + return key; +} + +static PyObject * +select_value(PyObject *key, PyObject *value) +{ + Py_INCREF(value); + return value; +} + +static PyObject * +select_item(PyObject *key, PyObject *value) +{ + PyObject *res = PyTuple_New(2); + + if (res != NULL) { + Py_INCREF(key); + Py_INCREF(value); + PyTuple_SET_ITEM(res, 0, key); + PyTuple_SET_ITEM(res, 1, value); + } + return res; +} + +static PyObject * +dict_iterkeys(dictobject *dict) +{ + return dictiter_new(dict, select_key); +} + +static PyObject * +dict_itervalues(dictobject *dict) +{ + return dictiter_new(dict, select_value); +} + +static PyObject * +dict_iteritems(dictobject *dict) +{ + return dictiter_new(dict, select_item); +} + + +PyDoc_STRVAR(has_key__doc__, +"D.has_key(k) -> True if D has a key k, else False"); + +PyDoc_STRVAR(get__doc__, +"D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None."); + +PyDoc_STRVAR(setdefault_doc__, +"D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D"); + +PyDoc_STRVAR(pop__doc__, +"D.pop(k[,d]) -> v, remove specified key and return the corresponding value\n\ +If key is not found, d is returned if given, otherwise KeyError is raised"); + +PyDoc_STRVAR(popitem__doc__, +"D.popitem() -> (k, v), remove and return some (key, value) pair as a\n\ +2-tuple; but raise KeyError if D is empty"); + +PyDoc_STRVAR(keys__doc__, +"D.keys() -> list of D's keys"); + +PyDoc_STRVAR(items__doc__, +"D.items() -> list of D's (key, value) pairs, as 2-tuples"); + +PyDoc_STRVAR(values__doc__, +"D.values() -> list of D's values"); + +PyDoc_STRVAR(update__doc__, +"D.update(E) -> None. Update D from E: for k in E.keys(): D[k] = E[k]"); + +PyDoc_STRVAR(fromkeys__doc__, +"dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n\ +v defaults to None."); + +PyDoc_STRVAR(clear__doc__, +"D.clear() -> None. Remove all items from D."); + +PyDoc_STRVAR(copy__doc__, +"D.copy() -> a shallow copy of D"); + +PyDoc_STRVAR(iterkeys__doc__, +"D.iterkeys() -> an iterator over the keys of D"); + +PyDoc_STRVAR(itervalues__doc__, +"D.itervalues() -> an iterator over the values of D"); + +PyDoc_STRVAR(iteritems__doc__, +"D.iteritems() -> an iterator over the (key, value) items of D"); + +static PyMethodDef mapp_methods[] = { + {"has_key", (PyCFunction)dict_has_key, METH_O, + has_key__doc__}, + {"get", (PyCFunction)dict_get, METH_VARARGS, + get__doc__}, + {"setdefault", (PyCFunction)dict_setdefault, METH_VARARGS, + setdefault_doc__}, + {"pop", (PyCFunction)dict_pop, METH_VARARGS, + pop__doc__}, + {"popitem", (PyCFunction)dict_popitem, METH_NOARGS, + popitem__doc__}, + {"keys", (PyCFunction)dict_keys, METH_NOARGS, + keys__doc__}, + {"items", (PyCFunction)dict_items, METH_NOARGS, + items__doc__}, + {"values", (PyCFunction)dict_values, METH_NOARGS, + values__doc__}, + {"update", (PyCFunction)dict_update, METH_O, + update__doc__}, + {"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS | METH_CLASS, + fromkeys__doc__}, + {"clear", (PyCFunction)dict_clear, METH_NOARGS, + clear__doc__}, + {"copy", (PyCFunction)dict_copy, METH_NOARGS, + copy__doc__}, + {"iterkeys", (PyCFunction)dict_iterkeys, METH_NOARGS, + iterkeys__doc__}, + {"itervalues", (PyCFunction)dict_itervalues, METH_NOARGS, + itervalues__doc__}, + {"iteritems", (PyCFunction)dict_iteritems, METH_NOARGS, + iteritems__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static int +dict_contains(dictobject *mp, PyObject *key) +{ + long hash; + + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) { + hash = PyObject_Hash(key); + if (hash == -1) + return -1; + } + return (mp->ma_lookup)(mp, key, hash)->me_value != NULL; +} + +/* Hack to implement "key in dict" */ +static PySequenceMethods dict_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)dict_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyObject * +dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *self; + + assert(type != NULL && type->tp_alloc != NULL); + self = type->tp_alloc(type, 0); + if (self != NULL) { + PyDictObject *d = (PyDictObject *)self; + /* It's guaranteed that tp->alloc zeroed out the struct. */ + assert(d->ma_table == NULL && d->ma_fill == 0 && d->ma_used == 0); + INIT_NONZERO_DICT_SLOTS(d); + d->ma_lookup = lookdict_string; +#ifdef SHOW_CONVERSION_COUNTS + ++created; +#endif + } + return self; +} + +static int +dict_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *arg = NULL; + int result = 0; + + if (!PyArg_UnpackTuple(args, "dict", 0, 1, &arg)) + result = -1; + + else if (arg != NULL) { + if (PyObject_HasAttrString(arg, "keys")) + result = PyDict_Merge(self, arg, 1); + else + result = PyDict_MergeFromSeq2(self, arg, 1); + } + if (result == 0 && kwds != NULL) + result = PyDict_Merge(self, kwds, 1); + return result; +} + +static long +dict_nohash(PyObject *self) +{ + PyErr_SetString(PyExc_TypeError, "dict objects are unhashable"); + return -1; +} + +static PyObject * +dict_iter(dictobject *dict) +{ + return dictiter_new(dict, select_key); +} + +PyDoc_STRVAR(dictionary_doc, +"dict() -> new empty dictionary.\n" +"dict(mapping) -> new dictionary initialized from a mapping object's\n" +" (key, value) pairs.\n" +"dict(seq) -> new dictionary initialized as if via:\n" +" d = {}\n" +" for k, v in seq:\n" +" d[k] = v\n" +"dict(**kwargs) -> new dictionary initialized with the name=value pairs\n" +" in the keyword argument list. For example: dict(one=1, two=2)"); + +PyTypeObject PyDict_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "dict", + sizeof(dictobject), + 0, + (destructor)dict_dealloc, /* tp_dealloc */ + (printfunc)dict_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)dict_compare, /* tp_compare */ + (reprfunc)dict_repr, /* tp_repr */ + 0, /* tp_as_number */ + &dict_as_sequence, /* tp_as_sequence */ + &dict_as_mapping, /* tp_as_mapping */ + dict_nohash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + dictionary_doc, /* tp_doc */ + (traverseproc)dict_traverse, /* tp_traverse */ + (inquiry)dict_tp_clear, /* tp_clear */ + dict_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)dict_iter, /* tp_iter */ + 0, /* tp_iternext */ + mapp_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)dict_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + dict_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + +/* For backward compatibility with old dictionary interface */ + +PyObject * +PyDict_GetItemString(PyObject *v, const char *key) +{ + PyObject *kv, *rv; + kv = PyString_FromString(key); + if (kv == NULL) + return NULL; + rv = PyDict_GetItem(v, kv); + Py_DECREF(kv); + return rv; +} + +int +PyDict_SetItemString(PyObject *v, const char *key, PyObject *item) +{ + PyObject *kv; + int err; + kv = PyString_FromString(key); + if (kv == NULL) + return -1; + PyString_InternInPlace(&kv); /* XXX Should we really? */ + err = PyDict_SetItem(v, kv, item); + Py_DECREF(kv); + return err; +} + +int +PyDict_DelItemString(PyObject *v, const char *key) +{ + PyObject *kv; + int err; + kv = PyString_FromString(key); + if (kv == NULL) + return -1; + err = PyDict_DelItem(v, kv); + Py_DECREF(kv); + return err; +} + +/* Dictionary iterator type */ + +extern PyTypeObject PyDictIter_Type; /* Forward */ + +typedef struct { + PyObject_HEAD + dictobject *di_dict; /* Set to NULL when iterator is exhausted */ + int di_used; + int di_pos; + binaryfunc di_select; +} dictiterobject; + +static PyObject * +dictiter_new(dictobject *dict, binaryfunc select) +{ + dictiterobject *di; + di = PyObject_New(dictiterobject, &PyDictIter_Type); + if (di == NULL) + return NULL; + Py_INCREF(dict); + di->di_dict = dict; + di->di_used = dict->ma_used; + di->di_pos = 0; + di->di_select = select; + return (PyObject *)di; +} + +static void +dictiter_dealloc(dictiterobject *di) +{ + Py_XDECREF(di->di_dict); + PyObject_Del(di); +} + +static PyObject *dictiter_iternext(dictiterobject *di) +{ + PyObject *key, *value; + + if (di->di_dict == NULL) + return NULL; + + if (di->di_used != di->di_dict->ma_used) { + PyErr_SetString(PyExc_RuntimeError, + "dictionary changed size during iteration"); + di->di_used = -1; /* Make this state sticky */ + return NULL; + } + if (PyDict_Next((PyObject *)(di->di_dict), &di->di_pos, &key, &value)) + return (*di->di_select)(key, value); + + Py_DECREF(di->di_dict); + di->di_dict = NULL; + return NULL; +} + +PyTypeObject PyDictIter_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "dictionary-iterator", /* tp_name */ + sizeof(dictiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dictiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)dictiter_iternext, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/enumobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/enumobject.c new file mode 100644 index 00000000..9dec09cd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/enumobject.c @@ -0,0 +1,161 @@ +/* enumerate object */ + +#include "Python.h" + +typedef struct { + PyObject_HEAD + long en_index; /* current index of enumeration */ + PyObject* en_sit; /* secondary iterator of enumeration */ + PyObject* en_result; /* result tuple */ +} enumobject; + +PyTypeObject PyEnum_Type; + +static PyObject * +enum_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + enumobject *en; + PyObject *seq = NULL; + static char *kwlist[] = {"sequence", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:enumerate", kwlist, + &seq)) + return NULL; + + en = (enumobject *)type->tp_alloc(type, 0); + if (en == NULL) + return NULL; + en->en_index = 0; + en->en_sit = PyObject_GetIter(seq); + if (en->en_sit == NULL) { + Py_DECREF(en); + return NULL; + } + en->en_result = PyTuple_New(2); + if (en->en_result == NULL) { + Py_DECREF(en->en_sit); + Py_DECREF(en); + return NULL; + } + Py_INCREF(Py_None); + PyTuple_SET_ITEM(en->en_result, 0, Py_None); + Py_INCREF(Py_None); + PyTuple_SET_ITEM(en->en_result, 1, Py_None); + return (PyObject *)en; +} + +static void +enum_dealloc(enumobject *en) +{ + PyObject_GC_UnTrack(en); + Py_XDECREF(en->en_sit); + Py_XDECREF(en->en_result); + en->ob_type->tp_free(en); +} + +static int +enum_traverse(enumobject *en, visitproc visit, void *arg) +{ + int err; + + if (en->en_sit) { + err = visit(en->en_sit, arg); + if (err) + return err; + } + if (en->en_result) { + err = visit(en->en_result, arg); + if (err) + return err; + } + return 0; +} + +static PyObject * +enum_next(enumobject *en) +{ + PyObject *next_index; + PyObject *next_item; + PyObject *result = en->en_result; + PyObject *it = en->en_sit; + + next_item = (*it->ob_type->tp_iternext)(it); + if (next_item == NULL) + return NULL; + + next_index = PyInt_FromLong(en->en_index); + if (next_index == NULL) { + Py_DECREF(next_item); + return NULL; + } + en->en_index++; + + if (result->ob_refcnt == 1) { + Py_INCREF(result); + Py_DECREF(PyTuple_GET_ITEM(result, 0)); + Py_DECREF(PyTuple_GET_ITEM(result, 1)); + } else { + result = PyTuple_New(2); + if (result == NULL) { + Py_DECREF(next_index); + Py_DECREF(next_item); + return NULL; + } + } + PyTuple_SET_ITEM(result, 0, next_index); + PyTuple_SET_ITEM(result, 1, next_item); + return result; +} + +PyDoc_STRVAR(enum_doc, +"enumerate(iterable) -> iterator for index, value of iterable\n" +"\n" +"Return an enumerate object. iterable must be an other object that supports\n" +"iteration. The enumerate object yields pairs containing a count (from\n" +"zero) and a value yielded by the iterable argument. enumerate is useful\n" +"for obtaining an indexed list: (0, seq[0]), (1, seq[1]), (2, seq[2]), ..."); + +PyTypeObject PyEnum_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "enumerate", /* tp_name */ + sizeof(enumobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)enum_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + enum_doc, /* tp_doc */ + (traverseproc)enum_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)enum_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + enum_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/fileobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/fileobject.c new file mode 100644 index 00000000..2813f0d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/fileobject.c @@ -0,0 +1,2402 @@ +/* File object implementation */ + +#include "Python.h" +#include "structmember.h" + +#ifndef DONT_HAVE_SYS_TYPES_H +#include +#endif /* DONT_HAVE_SYS_TYPES_H */ + +#ifdef MS_WINDOWS +#define fileno _fileno +/* can simulate truncate with Win32 API functions; see file_truncate */ +#define HAVE_FTRUNCATE +#ifdef MS_XBOX +#include +#else +#define WIN32_LEAN_AND_MEAN +#include +#endif +#endif + +//#ifdef _MSC_VER +///* Need GetVersion to see if on NT so safe to use _wfopen */ +//#define WIN32_LEAN_AND_MEAN +//#include +//#endif /* _MSC_VER */ + +#ifdef macintosh +#ifdef USE_GUSI +#define HAVE_FTRUNCATE +#endif +#endif + +#ifdef __MWERKS__ +/* Mwerks fopen() doesn't always set errno */ +#define NO_FOPEN_ERRNO +#endif + +#if defined(PYOS_OS2) && defined(PYCC_GCC) +#include +#endif + +#define BUF(v) PyString_AS_STRING((PyStringObject *)v) + +#ifndef DONT_HAVE_ERRNO_H +#include +#endif + +#ifdef HAVE_GETC_UNLOCKED +#define GETC(f) getc_unlocked(f) +#define FLOCKFILE(f) flockfile(f) +#define FUNLOCKFILE(f) funlockfile(f) +#else +#define GETC(f) getc(f) +#define FLOCKFILE(f) +#define FUNLOCKFILE(f) +#endif + +#ifdef WITH_UNIVERSAL_NEWLINES +/* Bits in f_newlinetypes */ +#define NEWLINE_UNKNOWN 0 /* No newline seen, yet */ +#define NEWLINE_CR 1 /* \r newline seen */ +#define NEWLINE_LF 2 /* \n newline seen */ +#define NEWLINE_CRLF 4 /* \r\n newline seen */ +#endif + +FILE * +PyFile_AsFile(PyObject *f) +{ + if (f == NULL || !PyFile_Check(f)) + return NULL; + else + return ((PyFileObject *)f)->f_fp; +} + +PyObject * +PyFile_Name(PyObject *f) +{ + if (f == NULL || !PyFile_Check(f)) + return NULL; + else + return ((PyFileObject *)f)->f_name; +} + +/* On Unix, fopen will succeed for directories. + In Python, there should be no file objects referring to + directories, so we need a check. */ + +static PyFileObject* +dircheck(PyFileObject* f) +{ +#if defined(HAVE_FSTAT) && defined(S_IFDIR) && defined(EISDIR) + struct stat buf; + if (f->f_fp == NULL) + return f; + if (fstat(fileno(f->f_fp), &buf) == 0 && + S_ISDIR(buf.st_mode)) { +#ifdef HAVE_STRERROR + char *msg = strerror(EISDIR); +#else + char *msg = "Is a directory"; +#endif + PyObject *exc = PyObject_CallFunction(PyExc_IOError, "(is)", + EISDIR, msg); + PyErr_SetObject(PyExc_IOError, exc); + Py_XDECREF(exc); + return NULL; + } +#endif + return f; +} + + +static PyObject * +fill_file_fields(PyFileObject *f, FILE *fp, char *name, char *mode, + int (*close)(FILE *), PyObject *wname) +{ + assert(f != NULL); + assert(PyFile_Check(f)); + assert(f->f_fp == NULL); + + Py_DECREF(f->f_name); + Py_DECREF(f->f_mode); + Py_DECREF(f->f_encoding); +#ifdef Py_USING_UNICODE + if (wname) + f->f_name = PyUnicode_FromObject(wname); + else +#endif + f->f_name = PyString_FromString(name); + f->f_mode = PyString_FromString(mode); + + f->f_close = close; + f->f_softspace = 0; + f->f_binary = strchr(mode,'b') != NULL; + f->f_buf = NULL; +#ifdef WITH_UNIVERSAL_NEWLINES + f->f_univ_newline = (strchr(mode, 'U') != NULL); + f->f_newlinetypes = NEWLINE_UNKNOWN; + f->f_skipnextlf = 0; +#endif + Py_INCREF(Py_None); + f->f_encoding = Py_None; + + if (f->f_name == NULL || f->f_mode == NULL) + return NULL; + f->f_fp = fp; + f = dircheck(f); + return (PyObject *) f; +} + +static PyObject * +open_the_file(PyFileObject *f, char *name, char *mode) +{ + assert(f != NULL); + assert(PyFile_Check(f)); +#ifdef MS_WINDOWS + /* windows ignores the passed name in order to support Unicode */ + assert(f->f_name != NULL); +#else + assert(name != NULL); +#endif + assert(mode != NULL); + assert(f->f_fp == NULL); + + /* rexec.py can't stop a user from getting the file() constructor -- + all they have to do is get *any* file object f, and then do + type(f). Here we prevent them from doing damage with it. */ + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_IOError, + "file() constructor not accessible in restricted mode"); + return NULL; + } + errno = 0; +#ifdef HAVE_FOPENRF + if (*mode == '*') { + FILE *fopenRF(); + f->f_fp = fopenRF(name, mode+1); + } + else +#endif + { +#ifdef WITH_UNIVERSAL_NEWLINES + if (strcmp(mode, "U") == 0 || strcmp(mode, "rU") == 0) + mode = "rb"; +#else + /* Compatibility: specifying U in a Python without universal + ** newlines is allowed, and the file is opened as a normal text + ** file. + */ + if (strcmp(mode, "U") == 0 || strcmp(mode, "rU") == 0) + mode = "r"; +#endif +#ifdef MS_WINDOWS + if (PyUnicode_Check(f->f_name)) { + PyObject *wmode; + wmode = PyUnicode_DecodeASCII(mode, strlen(mode), NULL); + if (f->f_name && wmode) { + Py_BEGIN_ALLOW_THREADS + /* PyUnicode_AS_UNICODE OK without thread + lock as it is a simple dereference. */ + f->f_fp = _wfopen(PyUnicode_AS_UNICODE(f->f_name), + PyUnicode_AS_UNICODE(wmode)); + Py_END_ALLOW_THREADS + } + Py_XDECREF(wmode); + } +#endif + if (NULL == f->f_fp && NULL != name) { + Py_BEGIN_ALLOW_THREADS + f->f_fp = fopen(name, mode); + Py_END_ALLOW_THREADS + } + } + if (f->f_fp == NULL) { +#ifdef NO_FOPEN_ERRNO + /* Metroworks only, wich does not always sets errno */ + if (errno == 0) { + PyObject *v; + v = Py_BuildValue("(is)", 0, "Cannot open file"); + if (v != NULL) { + PyErr_SetObject(PyExc_IOError, v); + Py_DECREF(v); + } + return NULL; + } +#endif +#ifdef _MSC_VER + /* MSVC 6 (Microsoft) leaves errno at 0 for bad mode strings, + * across all Windows flavors. When it sets EINVAL varies + * across Windows flavors, the exact conditions aren't + * documented, and the answer lies in the OS's implementation + * of Win32's CreateFile function (whose source is secret). + * Seems the best we can do is map EINVAL to ENOENT. + */ + if (errno == 0) /* bad mode string */ + errno = EINVAL; + else if (errno == EINVAL) /* unknown, but not a mode string */ + errno = ENOENT; +#endif + if (errno == EINVAL) + PyErr_Format(PyExc_IOError, "invalid mode: %s", + mode); + else +#ifdef MS_WINDOWS + PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, f->f_name); +#else + PyErr_SetFromErrnoWithFilename(PyExc_IOError, name); +#endif /* MS_WINDOWS */ + f = NULL; + } + if (f != NULL) + f = dircheck(f); + return (PyObject *)f; +} + +PyObject * +PyFile_FromFile(FILE *fp, char *name, char *mode, int (*close)(FILE *)) +{ + PyFileObject *f = (PyFileObject *)PyFile_Type.tp_new(&PyFile_Type, + NULL, NULL); + if (f != NULL) { + if (fill_file_fields(f, fp, name, mode, close, NULL) == NULL) { + Py_DECREF(f); + f = NULL; + } + } + return (PyObject *) f; +} + +PyObject * +PyFile_FromString(char *name, char *mode) +{ + extern int fclose(FILE *); + PyFileObject *f; + + f = (PyFileObject *)PyFile_FromFile((FILE *)NULL, name, mode, fclose); + if (f != NULL) { + if (open_the_file(f, name, mode) == NULL) { + Py_DECREF(f); + f = NULL; + } + } + return (PyObject *)f; +} + +void +PyFile_SetBufSize(PyObject *f, int bufsize) +{ + PyFileObject *file = (PyFileObject *)f; + if (bufsize >= 0) { + int type; + switch (bufsize) { + case 0: + type = _IONBF; + break; +#ifdef HAVE_SETVBUF + case 1: + type = _IOLBF; + bufsize = BUFSIZ; + break; +#endif + default: + type = _IOFBF; +#ifndef HAVE_SETVBUF + bufsize = BUFSIZ; +#endif + break; + } + fflush(file->f_fp); + if (type == _IONBF) { + PyMem_Free(file->f_setbuf); + file->f_setbuf = NULL; + } else { + file->f_setbuf = PyMem_Realloc(file->f_setbuf, bufsize); + } +#ifdef HAVE_SETVBUF + setvbuf(file->f_fp, file->f_setbuf, type, bufsize); +#else /* !HAVE_SETVBUF */ + setbuf(file->f_fp, file->f_setbuf); +#endif /* !HAVE_SETVBUF */ + } +} + +/* Set the encoding used to output Unicode strings. + Returh 1 on success, 0 on failure. */ + +int +PyFile_SetEncoding(PyObject *f, const char *enc) +{ + PyFileObject *file = (PyFileObject*)f; + PyObject *str = PyString_FromString(enc); + if (!str) + return 0; + Py_DECREF(file->f_encoding); + file->f_encoding = str; + return 1; +} + +static PyObject * +err_closed(void) +{ + PyErr_SetString(PyExc_ValueError, "I/O operation on closed file"); + return NULL; +} + +static void drop_readahead(PyFileObject *); + +/* Methods */ + +static void +file_dealloc(PyFileObject *f) +{ + if (f->f_fp != NULL && f->f_close != NULL) { + Py_BEGIN_ALLOW_THREADS + (*f->f_close)(f->f_fp); + Py_END_ALLOW_THREADS + } + Py_XDECREF(f->f_name); + Py_XDECREF(f->f_mode); + Py_XDECREF(f->f_encoding); + drop_readahead(f); + f->ob_type->tp_free((PyObject *)f); +} + +static PyObject * +file_repr(PyFileObject *f) +{ + if (PyUnicode_Check(f->f_name)) { +#ifdef Py_USING_UNICODE + PyObject *ret = NULL; + PyObject *name; + name = PyUnicode_AsUnicodeEscapeString(f->f_name); + ret = PyString_FromFormat("<%s file u'%s', mode '%s' at %p>", + f->f_fp == NULL ? "closed" : "open", + PyString_AsString(name), + PyString_AsString(f->f_mode), + f); + Py_XDECREF(name); + return ret; +#endif + } else { + return PyString_FromFormat("<%s file '%s', mode '%s' at %p>", + f->f_fp == NULL ? "closed" : "open", + PyString_AsString(f->f_name), + PyString_AsString(f->f_mode), + f); + } +} + +static PyObject * +file_close(PyFileObject *f) +{ + int sts = 0; + if (f->f_fp != NULL) { + if (f->f_close != NULL) { + Py_BEGIN_ALLOW_THREADS + errno = 0; + sts = (*f->f_close)(f->f_fp); + Py_END_ALLOW_THREADS + } + f->f_fp = NULL; + } + PyMem_Free(f->f_setbuf); + if (sts == EOF) + return PyErr_SetFromErrno(PyExc_IOError); + if (sts != 0) + return PyInt_FromLong((long)sts); + Py_INCREF(Py_None); + return Py_None; +} + + +/* Our very own off_t-like type, 64-bit if possible */ +#if !defined(HAVE_LARGEFILE_SUPPORT) +typedef off_t Py_off_t; +#elif SIZEOF_OFF_T >= 8 +typedef off_t Py_off_t; +#elif SIZEOF_FPOS_T >= 8 +typedef fpos_t Py_off_t; +#else +#error "Large file support, but neither off_t nor fpos_t is large enough." +#endif + + +/* a portable fseek() function + return 0 on success, non-zero on failure (with errno set) */ +static int +_portable_fseek(FILE *fp, Py_off_t offset, int whence) +{ +#if !defined(HAVE_LARGEFILE_SUPPORT) + return fseek(fp, offset, whence); +#elif defined(HAVE_FSEEKO) && SIZEOF_OFF_T >= 8 + return fseeko(fp, offset, whence); +#elif defined(HAVE_FSEEK64) + return fseek64(fp, offset, whence); +#elif defined(__BEOS__) + return _fseek(fp, offset, whence); +#elif SIZEOF_FPOS_T >= 8 + /* lacking a 64-bit capable fseek(), use a 64-bit capable fsetpos() + and fgetpos() to implement fseek()*/ + fpos_t pos; + switch (whence) { + case SEEK_END: +#ifdef MS_WINDOWS + fflush(fp); + if (_lseeki64(fileno(fp), 0, 2) == -1) + return -1; +#else + if (fseek(fp, 0, SEEK_END) != 0) + return -1; +#endif + /* fall through */ + case SEEK_CUR: + if (fgetpos(fp, &pos) != 0) + return -1; + offset += pos; + break; + /* case SEEK_SET: break; */ + } + return fsetpos(fp, &offset); +#else +#error "Large file support, but no way to fseek." +#endif +} + + +/* a portable ftell() function + Return -1 on failure with errno set appropriately, current file + position on success */ +static Py_off_t +_portable_ftell(FILE* fp) +{ +#if !defined(HAVE_LARGEFILE_SUPPORT) + return ftell(fp); +#elif defined(HAVE_FTELLO) && SIZEOF_OFF_T >= 8 + return ftello(fp); +#elif defined(HAVE_FTELL64) + return ftell64(fp); +#elif SIZEOF_FPOS_T >= 8 + fpos_t pos; + if (fgetpos(fp, &pos) != 0) + return -1; + return pos; +#else +#error "Large file support, but no way to ftell." +#endif +} + + +static PyObject * +file_seek(PyFileObject *f, PyObject *args) +{ + int whence; + int ret; + Py_off_t offset; + PyObject *offobj; + + if (f->f_fp == NULL) + return err_closed(); + drop_readahead(f); + whence = 0; + if (!PyArg_ParseTuple(args, "O|i:seek", &offobj, &whence)) + return NULL; +#if !defined(HAVE_LARGEFILE_SUPPORT) + offset = PyInt_AsLong(offobj); +#else + offset = PyLong_Check(offobj) ? + PyLong_AsLongLong(offobj) : PyInt_AsLong(offobj); +#endif + if (PyErr_Occurred()) + return NULL; + + Py_BEGIN_ALLOW_THREADS + errno = 0; + ret = _portable_fseek(f->f_fp, offset, whence); + Py_END_ALLOW_THREADS + + if (ret != 0) { + PyErr_SetFromErrno(PyExc_IOError); + clearerr(f->f_fp); + return NULL; + } +#ifdef WITH_UNIVERSAL_NEWLINES + f->f_skipnextlf = 0; +#endif + Py_INCREF(Py_None); + return Py_None; +} + + +#ifdef HAVE_FTRUNCATE +static PyObject * +file_truncate(PyFileObject *f, PyObject *args) +{ + int ret; + Py_off_t newsize; + PyObject *newsizeobj; + + if (f->f_fp == NULL) + return err_closed(); + newsizeobj = NULL; + if (!PyArg_UnpackTuple(args, "truncate", 0, 1, &newsizeobj)) + return NULL; + + /* Set newsize to current postion if newsizeobj NULL, else to the + specified value. */ + if (newsizeobj != NULL) { +#if !defined(HAVE_LARGEFILE_SUPPORT) + newsize = PyInt_AsLong(newsizeobj); +#else + newsize = PyLong_Check(newsizeobj) ? + PyLong_AsLongLong(newsizeobj) : + PyInt_AsLong(newsizeobj); +#endif + if (PyErr_Occurred()) + return NULL; + } + else { + /* Default to current position. */ + Py_BEGIN_ALLOW_THREADS + errno = 0; + newsize = _portable_ftell(f->f_fp); + Py_END_ALLOW_THREADS + if (newsize == -1) + goto onioerror; + } + + /* Flush the file. */ + Py_BEGIN_ALLOW_THREADS + errno = 0; + ret = fflush(f->f_fp); + Py_END_ALLOW_THREADS + if (ret != 0) + goto onioerror; + +#ifdef MS_WINDOWS + /* MS _chsize doesn't work if newsize doesn't fit in 32 bits, + so don't even try using it. */ + { + Py_off_t current; /* current file position */ + HANDLE hFile; + int error; + + /* current <- current file postion. */ + if (newsizeobj == NULL) + current = newsize; + else { + Py_BEGIN_ALLOW_THREADS + errno = 0; + current = _portable_ftell(f->f_fp); + Py_END_ALLOW_THREADS + if (current == -1) + goto onioerror; + } + + /* Move to newsize. */ + if (current != newsize) { + Py_BEGIN_ALLOW_THREADS + errno = 0; + error = _portable_fseek(f->f_fp, newsize, SEEK_SET) + != 0; + Py_END_ALLOW_THREADS + if (error) + goto onioerror; + } + + /* Truncate. Note that this may grow the file! */ + Py_BEGIN_ALLOW_THREADS + errno = 0; + hFile = (HANDLE)_get_osfhandle(fileno(f->f_fp)); + error = hFile == (HANDLE)-1; + if (!error) { + error = SetEndOfFile(hFile) == 0; + if (error) + errno = EACCES; + } + Py_END_ALLOW_THREADS + if (error) + goto onioerror; + + /* Restore original file position. */ + if (current != newsize) { + Py_BEGIN_ALLOW_THREADS + errno = 0; + error = _portable_fseek(f->f_fp, current, SEEK_SET) + != 0; + Py_END_ALLOW_THREADS + if (error) + goto onioerror; + } + } +#else + Py_BEGIN_ALLOW_THREADS + errno = 0; + ret = ftruncate(fileno(f->f_fp), newsize); + Py_END_ALLOW_THREADS + if (ret != 0) goto onioerror; +#endif /* !MS_WINDOWS */ + + Py_INCREF(Py_None); + return Py_None; + +onioerror: + PyErr_SetFromErrno(PyExc_IOError); + clearerr(f->f_fp); + return NULL; +} +#endif /* HAVE_FTRUNCATE */ + +static PyObject * +file_tell(PyFileObject *f) +{ + Py_off_t pos; + + if (f->f_fp == NULL) + return err_closed(); + Py_BEGIN_ALLOW_THREADS + errno = 0; + pos = _portable_ftell(f->f_fp); + Py_END_ALLOW_THREADS + if (pos == -1) { + PyErr_SetFromErrno(PyExc_IOError); + clearerr(f->f_fp); + return NULL; + } +#ifdef WITH_UNIVERSAL_NEWLINES + if (f->f_skipnextlf) { + int c; + c = GETC(f->f_fp); + if (c == '\n') { + pos++; + f->f_skipnextlf = 0; + } else if (c != EOF) ungetc(c, f->f_fp); + } +#endif +#if !defined(HAVE_LARGEFILE_SUPPORT) + return PyInt_FromLong(pos); +#else + return PyLong_FromLongLong(pos); +#endif +} + +static PyObject * +file_fileno(PyFileObject *f) +{ + if (f->f_fp == NULL) + return err_closed(); + return PyInt_FromLong((long) fileno(f->f_fp)); +} + +static PyObject * +file_flush(PyFileObject *f) +{ + int res; + + if (f->f_fp == NULL) + return err_closed(); + Py_BEGIN_ALLOW_THREADS + errno = 0; + res = fflush(f->f_fp); + Py_END_ALLOW_THREADS + if (res != 0) { + PyErr_SetFromErrno(PyExc_IOError); + clearerr(f->f_fp); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +file_isatty(PyFileObject *f) +{ + long res; + if (f->f_fp == NULL) + return err_closed(); + Py_BEGIN_ALLOW_THREADS + res = isatty((int)fileno(f->f_fp)); + Py_END_ALLOW_THREADS + return PyBool_FromLong(res); +} + + +#if BUFSIZ < 8192 +#define SMALLCHUNK 8192 +#else +#define SMALLCHUNK BUFSIZ +#endif + +#if SIZEOF_INT < 4 +#define BIGCHUNK (512 * 32) +#else +#define BIGCHUNK (512 * 1024) +#endif + +static size_t +new_buffersize(PyFileObject *f, size_t currentsize) +{ +#ifdef HAVE_FSTAT + off_t pos, end; + struct stat st; + if (fstat(fileno(f->f_fp), &st) == 0) { + end = st.st_size; + /* The following is not a bug: we really need to call lseek() + *and* ftell(). The reason is that some stdio libraries + mistakenly flush their buffer when ftell() is called and + the lseek() call it makes fails, thereby throwing away + data that cannot be recovered in any way. To avoid this, + we first test lseek(), and only call ftell() if lseek() + works. We can't use the lseek() value either, because we + need to take the amount of buffered data into account. + (Yet another reason why stdio stinks. :-) */ +#ifdef USE_GUSI2 + pos = lseek(fileno(f->f_fp), 1L, SEEK_CUR); + pos = lseek(fileno(f->f_fp), -1L, SEEK_CUR); +#else + pos = lseek(fileno(f->f_fp), 0L, SEEK_CUR); +#endif + if (pos >= 0) { + pos = ftell(f->f_fp); + } + if (pos < 0) + clearerr(f->f_fp); + if (end > pos && pos >= 0) + return currentsize + end - pos + 1; + /* Add 1 so if the file were to grow we'd notice. */ + } +#endif + if (currentsize > SMALLCHUNK) { + /* Keep doubling until we reach BIGCHUNK; + then keep adding BIGCHUNK. */ + if (currentsize <= BIGCHUNK) + return currentsize + currentsize; + else + return currentsize + BIGCHUNK; + } + return currentsize + SMALLCHUNK; +} + +#if defined(EWOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK != EAGAIN +#define BLOCKED_ERRNO(x) ((x) == EWOULDBLOCK || (x) == EAGAIN) +#else +#ifdef EWOULDBLOCK +#define BLOCKED_ERRNO(x) ((x) == EWOULDBLOCK) +#else +#ifdef EAGAIN +#define BLOCKED_ERRNO(x) ((x) == EAGAIN) +#else +#define BLOCKED_ERRNO(x) 0 +#endif +#endif +#endif + +static PyObject * +file_read(PyFileObject *f, PyObject *args) +{ + long bytesrequested = -1; + size_t bytesread, buffersize, chunksize; + PyObject *v; + + if (f->f_fp == NULL) + return err_closed(); + if (!PyArg_ParseTuple(args, "|l:read", &bytesrequested)) + return NULL; + if (bytesrequested < 0) + buffersize = new_buffersize(f, (size_t)0); + else + buffersize = bytesrequested; + if (buffersize > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "requested number of bytes is more than a Python string can hold"); + return NULL; + } + v = PyString_FromStringAndSize((char *)NULL, buffersize); + if (v == NULL) + return NULL; + bytesread = 0; + for (;;) { + Py_BEGIN_ALLOW_THREADS + errno = 0; + chunksize = Py_UniversalNewlineFread(BUF(v) + bytesread, + buffersize - bytesread, f->f_fp, (PyObject *)f); + Py_END_ALLOW_THREADS + if (chunksize == 0) { + if (!ferror(f->f_fp)) + break; + clearerr(f->f_fp); + /* When in non-blocking mode, data shouldn't + * be discarded if a blocking signal was + * received. That will also happen if + * chunksize != 0, but bytesread < buffersize. */ + if (bytesread > 0 && BLOCKED_ERRNO(errno)) + break; + PyErr_SetFromErrno(PyExc_IOError); + Py_DECREF(v); + return NULL; + } + bytesread += chunksize; + if (bytesread < buffersize) { + clearerr(f->f_fp); + break; + } + if (bytesrequested < 0) { + buffersize = new_buffersize(f, buffersize); + if (_PyString_Resize(&v, buffersize) < 0) + return NULL; + } else { + /* Got what was requested. */ + break; + } + } + if (bytesread != buffersize) + _PyString_Resize(&v, bytesread); + return v; +} + +static PyObject * +file_readinto(PyFileObject *f, PyObject *args) +{ + char *ptr; + int ntodo; + size_t ndone, nnow; + + if (f->f_fp == NULL) + return err_closed(); + if (!PyArg_ParseTuple(args, "w#", &ptr, &ntodo)) + return NULL; + ndone = 0; + while (ntodo > 0) { + Py_BEGIN_ALLOW_THREADS + errno = 0; + nnow = Py_UniversalNewlineFread(ptr+ndone, ntodo, f->f_fp, + (PyObject *)f); + Py_END_ALLOW_THREADS + if (nnow == 0) { + if (!ferror(f->f_fp)) + break; + PyErr_SetFromErrno(PyExc_IOError); + clearerr(f->f_fp); + return NULL; + } + ndone += nnow; + ntodo -= nnow; + } + return PyInt_FromLong((long)ndone); +} + +/************************************************************************** +Routine to get next line using platform fgets(). + +Under MSVC 6: + ++ MS threadsafe getc is very slow (multiple layers of function calls before+ + after each character, to lock+unlock the stream). ++ The stream-locking functions are MS-internal -- can't access them from user + code. ++ There's nothing Tim could find in the MS C or platform SDK libraries that + can worm around this. ++ MS fgets locks/unlocks only once per line; it's the only hook we have. + +So we use fgets for speed(!), despite that it's painful. + +MS realloc is also slow. + +Reports from other platforms on this method vs getc_unlocked (which MS doesn't +have): + Linux a wash + Solaris a wash + Tru64 Unix getline_via_fgets significantly faster + +CAUTION: The C std isn't clear about this: in those cases where fgets +writes something into the buffer, can it write into any position beyond the +required trailing null byte? MSVC 6 fgets does not, and no platform is (yet) +known on which it does; and it would be a strange way to code fgets. Still, +getline_via_fgets may not work correctly if it does. The std test +test_bufio.py should fail if platform fgets() routinely writes beyond the +trailing null byte. #define DONT_USE_FGETS_IN_GETLINE to disable this code. +**************************************************************************/ + +/* Use this routine if told to, or by default on non-get_unlocked() + * platforms unless told not to. Yikes! Let's spell that out: + * On a platform with getc_unlocked(): + * By default, use getc_unlocked(). + * If you want to use fgets() instead, #define USE_FGETS_IN_GETLINE. + * On a platform without getc_unlocked(): + * By default, use fgets(). + * If you don't want to use fgets(), #define DONT_USE_FGETS_IN_GETLINE. + */ +#if !defined(USE_FGETS_IN_GETLINE) && !defined(HAVE_GETC_UNLOCKED) +#define USE_FGETS_IN_GETLINE +#endif + +#if defined(DONT_USE_FGETS_IN_GETLINE) && defined(USE_FGETS_IN_GETLINE) +#undef USE_FGETS_IN_GETLINE +#endif + +#ifdef USE_FGETS_IN_GETLINE +static PyObject* +getline_via_fgets(FILE *fp) +{ +/* INITBUFSIZE is the maximum line length that lets us get away with the fast + * no-realloc, one-fgets()-call path. Boosting it isn't free, because we have + * to fill this much of the buffer with a known value in order to figure out + * how much of the buffer fgets() overwrites. So if INITBUFSIZE is larger + * than "most" lines, we waste time filling unused buffer slots. 100 is + * surely adequate for most peoples' email archives, chewing over source code, + * etc -- "regular old text files". + * MAXBUFSIZE is the maximum line length that lets us get away with the less + * fast (but still zippy) no-realloc, two-fgets()-call path. See above for + * cautions about boosting that. 300 was chosen because the worst real-life + * text-crunching job reported on Python-Dev was a mail-log crawler where over + * half the lines were 254 chars. + */ +#define INITBUFSIZE 100 +#define MAXBUFSIZE 300 + char* p; /* temp */ + char buf[MAXBUFSIZE]; + PyObject* v; /* the string object result */ + char* pvfree; /* address of next free slot */ + char* pvend; /* address one beyond last free slot */ + size_t nfree; /* # of free buffer slots; pvend-pvfree */ + size_t total_v_size; /* total # of slots in buffer */ + size_t increment; /* amount to increment the buffer */ + + /* Optimize for normal case: avoid _PyString_Resize if at all + * possible via first reading into stack buffer "buf". + */ + total_v_size = INITBUFSIZE; /* start small and pray */ + pvfree = buf; + for (;;) { + Py_BEGIN_ALLOW_THREADS + pvend = buf + total_v_size; + nfree = pvend - pvfree; + memset(pvfree, '\n', nfree); + p = fgets(pvfree, nfree, fp); + Py_END_ALLOW_THREADS + + if (p == NULL) { + clearerr(fp); + if (PyErr_CheckSignals()) + return NULL; + v = PyString_FromStringAndSize(buf, pvfree - buf); + return v; + } + /* fgets read *something* */ + p = memchr(pvfree, '\n', nfree); + if (p != NULL) { + /* Did the \n come from fgets or from us? + * Since fgets stops at the first \n, and then writes + * \0, if it's from fgets a \0 must be next. But if + * that's so, it could not have come from us, since + * the \n's we filled the buffer with have only more + * \n's to the right. + */ + if (p+1 < pvend && *(p+1) == '\0') { + /* It's from fgets: we win! In particular, + * we haven't done any mallocs yet, and can + * build the final result on the first try. + */ + ++p; /* include \n from fgets */ + } + else { + /* Must be from us: fgets didn't fill the + * buffer and didn't find a newline, so it + * must be the last and newline-free line of + * the file. + */ + assert(p > pvfree && *(p-1) == '\0'); + --p; /* don't include \0 from fgets */ + } + v = PyString_FromStringAndSize(buf, p - buf); + return v; + } + /* yuck: fgets overwrote all the newlines, i.e. the entire + * buffer. So this line isn't over yet, or maybe it is but + * we're exactly at EOF. If we haven't already, try using the + * rest of the stack buffer. + */ + assert(*(pvend-1) == '\0'); + if (pvfree == buf) { + pvfree = pvend - 1; /* overwrite trailing null */ + total_v_size = MAXBUFSIZE; + } + else + break; + } + + /* The stack buffer isn't big enough; malloc a string object and read + * into its buffer. + */ + total_v_size = MAXBUFSIZE << 1; + v = PyString_FromStringAndSize((char*)NULL, (int)total_v_size); + if (v == NULL) + return v; + /* copy over everything except the last null byte */ + memcpy(BUF(v), buf, MAXBUFSIZE-1); + pvfree = BUF(v) + MAXBUFSIZE - 1; + + /* Keep reading stuff into v; if it ever ends successfully, break + * after setting p one beyond the end of the line. The code here is + * very much like the code above, except reads into v's buffer; see + * the code above for detailed comments about the logic. + */ + for (;;) { + Py_BEGIN_ALLOW_THREADS + pvend = BUF(v) + total_v_size; + nfree = pvend - pvfree; + memset(pvfree, '\n', nfree); + p = fgets(pvfree, nfree, fp); + Py_END_ALLOW_THREADS + + if (p == NULL) { + clearerr(fp); + if (PyErr_CheckSignals()) { + Py_DECREF(v); + return NULL; + } + p = pvfree; + break; + } + p = memchr(pvfree, '\n', nfree); + if (p != NULL) { + if (p+1 < pvend && *(p+1) == '\0') { + /* \n came from fgets */ + ++p; + break; + } + /* \n came from us; last line of file, no newline */ + assert(p > pvfree && *(p-1) == '\0'); + --p; + break; + } + /* expand buffer and try again */ + assert(*(pvend-1) == '\0'); + increment = total_v_size >> 2; /* mild exponential growth */ + total_v_size += increment; + if (total_v_size > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "line is longer than a Python string can hold"); + Py_DECREF(v); + return NULL; + } + if (_PyString_Resize(&v, (int)total_v_size) < 0) + return NULL; + /* overwrite the trailing null byte */ + pvfree = BUF(v) + (total_v_size - increment - 1); + } + if (BUF(v) + total_v_size != p) + _PyString_Resize(&v, p - BUF(v)); + return v; +#undef INITBUFSIZE +#undef MAXBUFSIZE +} +#endif /* ifdef USE_FGETS_IN_GETLINE */ + +/* Internal routine to get a line. + Size argument interpretation: + > 0: max length; + <= 0: read arbitrary line +*/ + +static PyObject * +get_line(PyFileObject *f, int n) +{ + FILE *fp = f->f_fp; + int c; + char *buf, *end; + size_t total_v_size; /* total # of slots in buffer */ + size_t used_v_size; /* # used slots in buffer */ + size_t increment; /* amount to increment the buffer */ + PyObject *v; +#ifdef WITH_UNIVERSAL_NEWLINES + int newlinetypes = f->f_newlinetypes; + int skipnextlf = f->f_skipnextlf; + int univ_newline = f->f_univ_newline; +#endif + +#if defined(USE_FGETS_IN_GETLINE) +#ifdef WITH_UNIVERSAL_NEWLINES + if (n <= 0 && !univ_newline ) +#else + if (n <= 0) +#endif + return getline_via_fgets(fp); +#endif + total_v_size = n > 0 ? n : 100; + v = PyString_FromStringAndSize((char *)NULL, total_v_size); + if (v == NULL) + return NULL; + buf = BUF(v); + end = buf + total_v_size; + + for (;;) { + Py_BEGIN_ALLOW_THREADS + FLOCKFILE(fp); +#ifdef WITH_UNIVERSAL_NEWLINES + if (univ_newline) { + c = 'x'; /* Shut up gcc warning */ + while ( buf != end && (c = GETC(fp)) != EOF ) { + if (skipnextlf ) { + skipnextlf = 0; + if (c == '\n') { + /* Seeing a \n here with + * skipnextlf true means we + * saw a \r before. + */ + newlinetypes |= NEWLINE_CRLF; + c = GETC(fp); + if (c == EOF) break; + } else { + newlinetypes |= NEWLINE_CR; + } + } + if (c == '\r') { + skipnextlf = 1; + c = '\n'; + } else if ( c == '\n') + newlinetypes |= NEWLINE_LF; + *buf++ = c; + if (c == '\n') break; + } + if ( c == EOF && skipnextlf ) + newlinetypes |= NEWLINE_CR; + } else /* If not universal newlines use the normal loop */ +#endif + while ((c = GETC(fp)) != EOF && + (*buf++ = c) != '\n' && + buf != end) + ; + FUNLOCKFILE(fp); + Py_END_ALLOW_THREADS +#ifdef WITH_UNIVERSAL_NEWLINES + f->f_newlinetypes = newlinetypes; + f->f_skipnextlf = skipnextlf; +#endif + if (c == '\n') + break; + if (c == EOF) { + if (ferror(fp)) { + PyErr_SetFromErrno(PyExc_IOError); + clearerr(fp); + Py_DECREF(v); + return NULL; + } + clearerr(fp); + if (PyErr_CheckSignals()) { + Py_DECREF(v); + return NULL; + } + break; + } + /* Must be because buf == end */ + if (n > 0) + break; + used_v_size = total_v_size; + increment = total_v_size >> 2; /* mild exponential growth */ + total_v_size += increment; + if (total_v_size > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "line is longer than a Python string can hold"); + Py_DECREF(v); + return NULL; + } + if (_PyString_Resize(&v, total_v_size) < 0) + return NULL; + buf = BUF(v) + used_v_size; + end = BUF(v) + total_v_size; + } + + used_v_size = buf - BUF(v); + if (used_v_size != total_v_size) + _PyString_Resize(&v, used_v_size); + return v; +} + +/* External C interface */ + +PyObject * +PyFile_GetLine(PyObject *f, int n) +{ + PyObject *result; + + if (f == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + + if (PyFile_Check(f)) { + if (((PyFileObject*)f)->f_fp == NULL) + return err_closed(); + result = get_line((PyFileObject *)f, n); + } + else { + PyObject *reader; + PyObject *args; + + reader = PyObject_GetAttrString(f, "readline"); + if (reader == NULL) + return NULL; + if (n <= 0) + args = Py_BuildValue("()"); + else + args = Py_BuildValue("(i)", n); + if (args == NULL) { + Py_DECREF(reader); + return NULL; + } + result = PyEval_CallObject(reader, args); + Py_DECREF(reader); + Py_DECREF(args); + if (result != NULL && !PyString_Check(result) && + !PyUnicode_Check(result)) { + Py_DECREF(result); + result = NULL; + PyErr_SetString(PyExc_TypeError, + "object.readline() returned non-string"); + } + } + + if (n < 0 && result != NULL && PyString_Check(result)) { + char *s = PyString_AS_STRING(result); + int len = PyString_GET_SIZE(result); + if (len == 0) { + Py_DECREF(result); + result = NULL; + PyErr_SetString(PyExc_EOFError, + "EOF when reading a line"); + } + else if (s[len-1] == '\n') { + if (result->ob_refcnt == 1) + _PyString_Resize(&result, len-1); + else { + PyObject *v; + v = PyString_FromStringAndSize(s, len-1); + Py_DECREF(result); + result = v; + } + } + } +#ifdef Py_USING_UNICODE + if (n < 0 && result != NULL && PyUnicode_Check(result)) { + Py_UNICODE *s = PyUnicode_AS_UNICODE(result); + int len = PyUnicode_GET_SIZE(result); + if (len == 0) { + Py_DECREF(result); + result = NULL; + PyErr_SetString(PyExc_EOFError, + "EOF when reading a line"); + } + else if (s[len-1] == '\n') { + if (result->ob_refcnt == 1) + PyUnicode_Resize(&result, len-1); + else { + PyObject *v; + v = PyUnicode_FromUnicode(s, len-1); + Py_DECREF(result); + result = v; + } + } + } +#endif + return result; +} + +/* Python method */ + +static PyObject * +file_readline(PyFileObject *f, PyObject *args) +{ + int n = -1; + + if (f->f_fp == NULL) + return err_closed(); + if (!PyArg_ParseTuple(args, "|i:readline", &n)) + return NULL; + if (n == 0) + return PyString_FromString(""); + if (n < 0) + n = 0; + return get_line(f, n); +} + +static PyObject * +file_readlines(PyFileObject *f, PyObject *args) +{ + long sizehint = 0; + PyObject *list; + PyObject *line; + char small_buffer[SMALLCHUNK]; + char *buffer = small_buffer; + size_t buffersize = SMALLCHUNK; + PyObject *big_buffer = NULL; + size_t nfilled = 0; + size_t nread; + size_t totalread = 0; + char *p, *q, *end; + int err; + int shortread = 0; + + if (f->f_fp == NULL) + return err_closed(); + if (!PyArg_ParseTuple(args, "|l:readlines", &sizehint)) + return NULL; + if ((list = PyList_New(0)) == NULL) + return NULL; + for (;;) { + if (shortread) + nread = 0; + else { + Py_BEGIN_ALLOW_THREADS + errno = 0; + nread = Py_UniversalNewlineFread(buffer+nfilled, + buffersize-nfilled, f->f_fp, (PyObject *)f); + Py_END_ALLOW_THREADS + shortread = (nread < buffersize-nfilled); + } + if (nread == 0) { + sizehint = 0; + if (!ferror(f->f_fp)) + break; + PyErr_SetFromErrno(PyExc_IOError); + clearerr(f->f_fp); + error: + Py_DECREF(list); + list = NULL; + goto cleanup; + } + totalread += nread; + p = memchr(buffer+nfilled, '\n', nread); + if (p == NULL) { + /* Need a larger buffer to fit this line */ + nfilled += nread; + buffersize *= 2; + if (buffersize > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "line is longer than a Python string can hold"); + goto error; + } + if (big_buffer == NULL) { + /* Create the big buffer */ + big_buffer = PyString_FromStringAndSize( + NULL, buffersize); + if (big_buffer == NULL) + goto error; + buffer = PyString_AS_STRING(big_buffer); + memcpy(buffer, small_buffer, nfilled); + } + else { + /* Grow the big buffer */ + if ( _PyString_Resize(&big_buffer, buffersize) < 0 ) + goto error; + buffer = PyString_AS_STRING(big_buffer); + } + continue; + } + end = buffer+nfilled+nread; + q = buffer; + do { + /* Process complete lines */ + p++; + line = PyString_FromStringAndSize(q, p-q); + if (line == NULL) + goto error; + err = PyList_Append(list, line); + Py_DECREF(line); + if (err != 0) + goto error; + q = p; + p = memchr(q, '\n', end-q); + } while (p != NULL); + /* Move the remaining incomplete line to the start */ + nfilled = end-q; + memmove(buffer, q, nfilled); + if (sizehint > 0) + if (totalread >= (size_t)sizehint) + break; + } + if (nfilled != 0) { + /* Partial last line */ + line = PyString_FromStringAndSize(buffer, nfilled); + if (line == NULL) + goto error; + if (sizehint > 0) { + /* Need to complete the last line */ + PyObject *rest = get_line(f, 0); + if (rest == NULL) { + Py_DECREF(line); + goto error; + } + PyString_Concat(&line, rest); + Py_DECREF(rest); + if (line == NULL) + goto error; + } + err = PyList_Append(list, line); + Py_DECREF(line); + if (err != 0) + goto error; + } + cleanup: + Py_XDECREF(big_buffer); + return list; +} + +static PyObject * +file_write(PyFileObject *f, PyObject *args) +{ + char *s; + int n, n2; + if (f->f_fp == NULL) + return err_closed(); + if (!PyArg_ParseTuple(args, f->f_binary ? "s#" : "t#", &s, &n)) + return NULL; + f->f_softspace = 0; + Py_BEGIN_ALLOW_THREADS + errno = 0; + n2 = fwrite(s, 1, n, f->f_fp); + Py_END_ALLOW_THREADS + if (n2 != n) { + PyErr_SetFromErrno(PyExc_IOError); + clearerr(f->f_fp); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +file_writelines(PyFileObject *f, PyObject *seq) +{ +#define CHUNKSIZE 1000 + PyObject *list, *line; + PyObject *it; /* iter(seq) */ + PyObject *result; + int i, j, index, len, nwritten, islist; + + assert(seq != NULL); + if (f->f_fp == NULL) + return err_closed(); + + result = NULL; + list = NULL; + islist = PyList_Check(seq); + if (islist) + it = NULL; + else { + it = PyObject_GetIter(seq); + if (it == NULL) { + PyErr_SetString(PyExc_TypeError, + "writelines() requires an iterable argument"); + return NULL; + } + /* From here on, fail by going to error, to reclaim "it". */ + list = PyList_New(CHUNKSIZE); + if (list == NULL) + goto error; + } + + /* Strategy: slurp CHUNKSIZE lines into a private list, + checking that they are all strings, then write that list + without holding the interpreter lock, then come back for more. */ + for (index = 0; ; index += CHUNKSIZE) { + if (islist) { + Py_XDECREF(list); + list = PyList_GetSlice(seq, index, index+CHUNKSIZE); + if (list == NULL) + goto error; + j = PyList_GET_SIZE(list); + } + else { + for (j = 0; j < CHUNKSIZE; j++) { + line = PyIter_Next(it); + if (line == NULL) { + if (PyErr_Occurred()) + goto error; + break; + } + PyList_SetItem(list, j, line); + } + } + if (j == 0) + break; + + /* Check that all entries are indeed strings. If not, + apply the same rules as for file.write() and + convert the results to strings. This is slow, but + seems to be the only way since all conversion APIs + could potentially execute Python code. */ + for (i = 0; i < j; i++) { + PyObject *v = PyList_GET_ITEM(list, i); + if (!PyString_Check(v)) { + const char *buffer; + int len; + if (((f->f_binary && + PyObject_AsReadBuffer(v, + (const void**)&buffer, + &len)) || + PyObject_AsCharBuffer(v, + &buffer, + &len))) { + PyErr_SetString(PyExc_TypeError, + "writelines() argument must be a sequence of strings"); + goto error; + } + line = PyString_FromStringAndSize(buffer, + len); + if (line == NULL) + goto error; + Py_DECREF(v); + PyList_SET_ITEM(list, i, line); + } + } + + /* Since we are releasing the global lock, the + following code may *not* execute Python code. */ + Py_BEGIN_ALLOW_THREADS + f->f_softspace = 0; + errno = 0; + for (i = 0; i < j; i++) { + line = PyList_GET_ITEM(list, i); + len = PyString_GET_SIZE(line); + nwritten = fwrite(PyString_AS_STRING(line), + 1, len, f->f_fp); + if (nwritten != len) { + Py_BLOCK_THREADS + PyErr_SetFromErrno(PyExc_IOError); + clearerr(f->f_fp); + goto error; + } + } + Py_END_ALLOW_THREADS + + if (j < CHUNKSIZE) + break; + } + + Py_INCREF(Py_None); + result = Py_None; + error: + Py_XDECREF(list); + Py_XDECREF(it); + return result; +#undef CHUNKSIZE +} + +static PyObject * +file_getiter(PyFileObject *f) +{ + if (f->f_fp == NULL) + return err_closed(); + Py_INCREF(f); + return (PyObject *)f; +} + +PyDoc_STRVAR(readline_doc, +"readline([size]) -> next line from the file, as a string.\n" +"\n" +"Retain newline. A non-negative size argument limits the maximum\n" +"number of bytes to return (an incomplete line may be returned then).\n" +"Return an empty string at EOF."); + +PyDoc_STRVAR(read_doc, +"read([size]) -> read at most size bytes, returned as a string.\n" +"\n" +"If the size argument is negative or omitted, read until EOF is reached.\n" +"Notice that when in non-blocking mode, less data than what was requested\n" +"may be returned, even if no size parameter was given."); + +PyDoc_STRVAR(write_doc, +"write(str) -> None. Write string str to file.\n" +"\n" +"Note that due to buffering, flush() or close() may be needed before\n" +"the file on disk reflects the data written."); + +PyDoc_STRVAR(fileno_doc, +"fileno() -> integer \"file descriptor\".\n" +"\n" +"This is needed for lower-level file interfaces, such os.read()."); + +PyDoc_STRVAR(seek_doc, +"seek(offset[, whence]) -> None. Move to new file position.\n" +"\n" +"Argument offset is a byte count. Optional argument whence defaults to\n" +"0 (offset from start of file, offset should be >= 0); other values are 1\n" +"(move relative to current position, positive or negative), and 2 (move\n" +"relative to end of file, usually negative, although many platforms allow\n" +"seeking beyond the end of a file). If the file is opened in text mode,\n" +"only offsets returned by tell() are legal. Use of other offsets causes\n" +"undefined behavior." +"\n" +"Note that not all file objects are seekable."); + +#ifdef HAVE_FTRUNCATE +PyDoc_STRVAR(truncate_doc, +"truncate([size]) -> None. Truncate the file to at most size bytes.\n" +"\n" +"Size defaults to the current file position, as returned by tell()."); +#endif + +PyDoc_STRVAR(tell_doc, +"tell() -> current file position, an integer (may be a long integer)."); + +PyDoc_STRVAR(readinto_doc, +"readinto() -> Undocumented. Don't use this; it may go away."); + +PyDoc_STRVAR(readlines_doc, +"readlines([size]) -> list of strings, each a line from the file.\n" +"\n" +"Call readline() repeatedly and return a list of the lines so read.\n" +"The optional size argument, if given, is an approximate bound on the\n" +"total number of bytes in the lines returned."); + +PyDoc_STRVAR(xreadlines_doc, +"xreadlines() -> returns self.\n" +"\n" +"For backward compatibility. File objects now include the performance\n" +"optimizations previously implemented in the xreadlines module."); + +PyDoc_STRVAR(writelines_doc, +"writelines(sequence_of_strings) -> None. Write the strings to the file.\n" +"\n" +"Note that newlines are not added. The sequence can be any iterable object\n" +"producing strings. This is equivalent to calling write() for each string."); + +PyDoc_STRVAR(flush_doc, +"flush() -> None. Flush the internal I/O buffer."); + +PyDoc_STRVAR(close_doc, +"close() -> None or (perhaps) an integer. Close the file.\n" +"\n" +"Sets data attribute .closed to True. A closed file cannot be used for\n" +"further I/O operations. close() may be called more than once without\n" +"error. Some kinds of file objects (for example, opened by popen())\n" +"may return an exit status upon closing."); + +PyDoc_STRVAR(isatty_doc, +"isatty() -> true or false. True if the file is connected to a tty device."); + +static PyMethodDef file_methods[] = { + {"readline", (PyCFunction)file_readline, METH_VARARGS, readline_doc}, + {"read", (PyCFunction)file_read, METH_VARARGS, read_doc}, + {"write", (PyCFunction)file_write, METH_VARARGS, write_doc}, + {"fileno", (PyCFunction)file_fileno, METH_NOARGS, fileno_doc}, + {"seek", (PyCFunction)file_seek, METH_VARARGS, seek_doc}, +#ifdef HAVE_FTRUNCATE + {"truncate", (PyCFunction)file_truncate, METH_VARARGS, truncate_doc}, +#endif + {"tell", (PyCFunction)file_tell, METH_NOARGS, tell_doc}, + {"readinto", (PyCFunction)file_readinto, METH_VARARGS, readinto_doc}, + {"readlines", (PyCFunction)file_readlines,METH_VARARGS, readlines_doc}, + {"xreadlines",(PyCFunction)file_getiter, METH_NOARGS, xreadlines_doc}, + {"writelines",(PyCFunction)file_writelines, METH_O, writelines_doc}, + {"flush", (PyCFunction)file_flush, METH_NOARGS, flush_doc}, + {"close", (PyCFunction)file_close, METH_NOARGS, close_doc}, + {"isatty", (PyCFunction)file_isatty, METH_NOARGS, isatty_doc}, + {NULL, NULL} /* sentinel */ +}; + +#define OFF(x) offsetof(PyFileObject, x) + +static PyMemberDef file_memberlist[] = { + {"softspace", T_INT, OFF(f_softspace), 0, + "flag indicating that a space needs to be printed; used by print"}, + {"mode", T_OBJECT, OFF(f_mode), RO, + "file mode ('r', 'U', 'w', 'a', possibly with 'b' or '+' added)"}, + {"name", T_OBJECT, OFF(f_name), RO, + "file name"}, + {"encoding", T_OBJECT, OFF(f_encoding), RO, + "file encoding"}, + /* getattr(f, "closed") is implemented without this table */ + {NULL} /* Sentinel */ +}; + +static PyObject * +get_closed(PyFileObject *f, void *closure) +{ + return PyBool_FromLong((long)(f->f_fp == 0)); +} +#ifdef WITH_UNIVERSAL_NEWLINES +static PyObject * +get_newlines(PyFileObject *f, void *closure) +{ + switch (f->f_newlinetypes) { + case NEWLINE_UNKNOWN: + Py_INCREF(Py_None); + return Py_None; + case NEWLINE_CR: + return PyString_FromString("\r"); + case NEWLINE_LF: + return PyString_FromString("\n"); + case NEWLINE_CR|NEWLINE_LF: + return Py_BuildValue("(ss)", "\r", "\n"); + case NEWLINE_CRLF: + return PyString_FromString("\r\n"); + case NEWLINE_CR|NEWLINE_CRLF: + return Py_BuildValue("(ss)", "\r", "\r\n"); + case NEWLINE_LF|NEWLINE_CRLF: + return Py_BuildValue("(ss)", "\n", "\r\n"); + case NEWLINE_CR|NEWLINE_LF|NEWLINE_CRLF: + return Py_BuildValue("(sss)", "\r", "\n", "\r\n"); + default: + PyErr_Format(PyExc_SystemError, + "Unknown newlines value 0x%x\n", + f->f_newlinetypes); + return NULL; + } +} +#endif + +static PyGetSetDef file_getsetlist[] = { + {"closed", (getter)get_closed, NULL, "True if the file is closed"}, +#ifdef WITH_UNIVERSAL_NEWLINES + {"newlines", (getter)get_newlines, NULL, + "end-of-line convention used in this file"}, +#endif + {0}, +}; + +static void +drop_readahead(PyFileObject *f) +{ + if (f->f_buf != NULL) { + PyMem_Free(f->f_buf); + f->f_buf = NULL; + } +} + +/* Make sure that file has a readahead buffer with at least one byte + (unless at EOF) and no more than bufsize. Returns negative value on + error */ +static int +readahead(PyFileObject *f, int bufsize) +{ + int chunksize; + + if (f->f_buf != NULL) { + if( (f->f_bufend - f->f_bufptr) >= 1) + return 0; + else + drop_readahead(f); + } + if ((f->f_buf = PyMem_Malloc(bufsize)) == NULL) { + return -1; + } + Py_BEGIN_ALLOW_THREADS + errno = 0; + chunksize = Py_UniversalNewlineFread( + f->f_buf, bufsize, f->f_fp, (PyObject *)f); + Py_END_ALLOW_THREADS + if (chunksize == 0) { + if (ferror(f->f_fp)) { + PyErr_SetFromErrno(PyExc_IOError); + clearerr(f->f_fp); + drop_readahead(f); + return -1; + } + } + f->f_bufptr = f->f_buf; + f->f_bufend = f->f_buf + chunksize; + return 0; +} + +/* Used by file_iternext. The returned string will start with 'skip' + uninitialized bytes followed by the remainder of the line. Don't be + horrified by the recursive call: maximum recursion depth is limited by + logarithmic buffer growth to about 50 even when reading a 1gb line. */ + +static PyStringObject * +readahead_get_line_skip(PyFileObject *f, int skip, int bufsize) +{ + PyStringObject* s; + char *bufptr; + char *buf; + int len; + + if (f->f_buf == NULL) + if (readahead(f, bufsize) < 0) + return NULL; + + len = f->f_bufend - f->f_bufptr; + if (len == 0) + return (PyStringObject *) + PyString_FromStringAndSize(NULL, skip); + bufptr = memchr(f->f_bufptr, '\n', len); + if (bufptr != NULL) { + bufptr++; /* Count the '\n' */ + len = bufptr - f->f_bufptr; + s = (PyStringObject *) + PyString_FromStringAndSize(NULL, skip+len); + if (s == NULL) + return NULL; + memcpy(PyString_AS_STRING(s)+skip, f->f_bufptr, len); + f->f_bufptr = bufptr; + if (bufptr == f->f_bufend) + drop_readahead(f); + } else { + bufptr = f->f_bufptr; + buf = f->f_buf; + f->f_buf = NULL; /* Force new readahead buffer */ + s = readahead_get_line_skip( + f, skip+len, bufsize + (bufsize>>2) ); + if (s == NULL) { + PyMem_Free(buf); + return NULL; + } + memcpy(PyString_AS_STRING(s)+skip, bufptr, len); + PyMem_Free(buf); + } + return s; +} + +/* A larger buffer size may actually decrease performance. */ +#define READAHEAD_BUFSIZE 8192 + +static PyObject * +file_iternext(PyFileObject *f) +{ + PyStringObject* l; + + if (f->f_fp == NULL) + return err_closed(); + + l = readahead_get_line_skip(f, 0, READAHEAD_BUFSIZE); + if (l == NULL || PyString_GET_SIZE(l) == 0) { + Py_XDECREF(l); + return NULL; + } + return (PyObject *)l; +} + + +static PyObject * +file_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *self; + static PyObject *not_yet_string; + + assert(type != NULL && type->tp_alloc != NULL); + + if (not_yet_string == NULL) { + not_yet_string = PyString_FromString(""); + if (not_yet_string == NULL) + return NULL; + } + + self = type->tp_alloc(type, 0); + if (self != NULL) { + /* Always fill in the name and mode, so that nobody else + needs to special-case NULLs there. */ + Py_INCREF(not_yet_string); + ((PyFileObject *)self)->f_name = not_yet_string; + Py_INCREF(not_yet_string); + ((PyFileObject *)self)->f_mode = not_yet_string; + Py_INCREF(Py_None); + ((PyFileObject *)self)->f_encoding = Py_None; + } + return self; +} + +static int +file_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyFileObject *foself = (PyFileObject *)self; + int ret = 0; + static char *kwlist[] = {"name", "mode", "buffering", 0}; + char *name = NULL; + char *mode = "r"; + int bufsize = -1; + int wideargument = 0; + + assert(PyFile_Check(self)); + if (foself->f_fp != NULL) { + /* Have to close the existing file first. */ + PyObject *closeresult = file_close(foself); + if (closeresult == NULL) + return -1; + Py_DECREF(closeresult); + } + +#ifdef Py_WIN_WIDE_FILENAMES + if (GetVersion() < 0x80000000) { /* On NT, so wide API available */ + PyObject *po; + if (PyArg_ParseTupleAndKeywords(args, kwds, "U|si:file", + kwlist, &po, &mode, &bufsize)) { + wideargument = 1; + if (fill_file_fields(foself, NULL, name, mode, + fclose, po) == NULL) + goto Error; + } else { + /* Drop the argument parsing error as narrow + strings are also valid. */ + PyErr_Clear(); + } + } +#endif + + if (!wideargument) { + if (!PyArg_ParseTupleAndKeywords(args, kwds, "et|si:file", kwlist, + Py_FileSystemDefaultEncoding, + &name, + &mode, &bufsize)) + return -1; + if (fill_file_fields(foself, NULL, name, mode, + fclose, NULL) == NULL) + goto Error; + } + if (open_the_file(foself, name, mode) == NULL) + goto Error; + foself->f_setbuf = NULL; + PyFile_SetBufSize(self, bufsize); + goto Done; + +Error: + ret = -1; + /* fall through */ +Done: + PyMem_Free(name); /* free the encoded string */ + return ret; +} + +PyDoc_VAR(file_doc) = +PyDoc_STR( +"file(name[, mode[, buffering]]) -> file object\n" +"\n" +"Open a file. The mode can be 'r', 'w' or 'a' for reading (default),\n" +"writing or appending. The file will be created if it doesn't exist\n" +"when opened for writing or appending; it will be truncated when\n" +"opened for writing. Add a 'b' to the mode for binary files.\n" +"Add a '+' to the mode to allow simultaneous reading and writing.\n" +"If the buffering argument is given, 0 means unbuffered, 1 means line\n" +"buffered, and larger numbers specify the buffer size.\n" +) +#ifdef WITH_UNIVERSAL_NEWLINES +PyDoc_STR( +"Add a 'U' to mode to open the file for input with universal newline\n" +"support. Any line ending in the input file will be seen as a '\\n'\n" +"in Python. Also, a file so opened gains the attribute 'newlines';\n" +"the value for this attribute is one of None (no newline read yet),\n" +"'\\r', '\\n', '\\r\\n' or a tuple containing all the newline types seen.\n" +"\n" +"'U' cannot be combined with 'w' or '+' mode.\n" +) +#endif /* WITH_UNIVERSAL_NEWLINES */ +PyDoc_STR( +"\n" +"Note: open() is an alias for file()." +); + +PyTypeObject PyFile_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "file", + sizeof(PyFileObject), + 0, + (destructor)file_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)file_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + /* softspace is writable: we must supply tp_setattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + file_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)file_getiter, /* tp_iter */ + (iternextfunc)file_iternext, /* tp_iternext */ + file_methods, /* tp_methods */ + file_memberlist, /* tp_members */ + file_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)file_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + file_new, /* tp_new */ + PyObject_Del, /* tp_free */ +}; + +/* Interface for the 'soft space' between print items. */ + +int +PyFile_SoftSpace(PyObject *f, int newflag) +{ + int oldflag = 0; + if (f == NULL) { + /* Do nothing */ + } + else if (PyFile_Check(f)) { + oldflag = ((PyFileObject *)f)->f_softspace; + ((PyFileObject *)f)->f_softspace = newflag; + } + else { + PyObject *v; + v = PyObject_GetAttrString(f, "softspace"); + if (v == NULL) + PyErr_Clear(); + else { + if (PyInt_Check(v)) + oldflag = PyInt_AsLong(v); + Py_DECREF(v); + } + v = PyInt_FromLong((long)newflag); + if (v == NULL) + PyErr_Clear(); + else { + if (PyObject_SetAttrString(f, "softspace", v) != 0) + PyErr_Clear(); + Py_DECREF(v); + } + } + return oldflag; +} + +/* Interfaces to write objects/strings to file-like objects */ + +int +PyFile_WriteObject(PyObject *v, PyObject *f, int flags) +{ + PyObject *writer, *value, *args, *result; + if (f == NULL) { + PyErr_SetString(PyExc_TypeError, "writeobject with NULL file"); + return -1; + } + else if (PyFile_Check(f)) { + FILE *fp = PyFile_AsFile(f); + PyObject *enc = ((PyFileObject*)f)->f_encoding; + int result; + if (fp == NULL) { + err_closed(); + return -1; + } +#ifdef Py_USING_UNICODE + if ((flags & Py_PRINT_RAW) && + PyUnicode_Check(v) && enc != Py_None) { + char *cenc = PyString_AS_STRING(enc); + value = PyUnicode_AsEncodedString(v, cenc, "strict"); + if (value == NULL) + return -1; + } else { + value = v; + Py_INCREF(value); + } + result = PyObject_Print(value, fp, flags); + Py_DECREF(value); + return result; +#else + return PyObject_Print(v, fp, flags); +#endif + } + writer = PyObject_GetAttrString(f, "write"); + if (writer == NULL) + return -1; + if (flags & Py_PRINT_RAW) { + if (PyUnicode_Check(v)) { + value = v; + Py_INCREF(value); + } else + value = PyObject_Str(v); + } + else + value = PyObject_Repr(v); + if (value == NULL) { + Py_DECREF(writer); + return -1; + } + args = Py_BuildValue("(O)", value); + if (args == NULL) { + Py_DECREF(value); + Py_DECREF(writer); + return -1; + } + result = PyEval_CallObject(writer, args); + Py_DECREF(args); + Py_DECREF(value); + Py_DECREF(writer); + if (result == NULL) + return -1; + Py_DECREF(result); + return 0; +} + +int +PyFile_WriteString(const char *s, PyObject *f) +{ + if (f == NULL) { + /* Should be caused by a pre-existing error */ + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_SystemError, + "null file for PyFile_WriteString"); + return -1; + } + else if (PyFile_Check(f)) { + FILE *fp = PyFile_AsFile(f); + if (fp == NULL) { + err_closed(); + return -1; + } + fputs(s, fp); + return 0; + } + else if (!PyErr_Occurred()) { + PyObject *v = PyString_FromString(s); + int err; + if (v == NULL) + return -1; + err = PyFile_WriteObject(v, f, Py_PRINT_RAW); + Py_DECREF(v); + return err; + } + else + return -1; +} + +/* Try to get a file-descriptor from a Python object. If the object + is an integer or long integer, its value is returned. If not, the + object's fileno() method is called if it exists; the method must return + an integer or long integer, which is returned as the file descriptor value. + -1 is returned on failure. +*/ + +int PyObject_AsFileDescriptor(PyObject *o) +{ + int fd; + PyObject *meth; + + if (PyInt_Check(o)) { + fd = PyInt_AsLong(o); + } + else if (PyLong_Check(o)) { + fd = PyLong_AsLong(o); + } + else if ((meth = PyObject_GetAttrString(o, "fileno")) != NULL) + { + PyObject *fno = PyEval_CallObject(meth, NULL); + Py_DECREF(meth); + if (fno == NULL) + return -1; + + if (PyInt_Check(fno)) { + fd = PyInt_AsLong(fno); + Py_DECREF(fno); + } + else if (PyLong_Check(fno)) { + fd = PyLong_AsLong(fno); + Py_DECREF(fno); + } + else { + PyErr_SetString(PyExc_TypeError, + "fileno() returned a non-integer"); + Py_DECREF(fno); + return -1; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "argument must be an int, or have a fileno() method."); + return -1; + } + + if (fd < 0) { + PyErr_Format(PyExc_ValueError, + "file descriptor cannot be a negative integer (%i)", + fd); + return -1; + } + return fd; +} + +#ifdef WITH_UNIVERSAL_NEWLINES +/* From here on we need access to the real fgets and fread */ +#undef fgets +#undef fread + +/* +** Py_UniversalNewlineFgets is an fgets variation that understands +** all of \r, \n and \r\n conventions. +** The stream should be opened in binary mode. +** If fobj is NULL the routine always does newline conversion, and +** it may peek one char ahead to gobble the second char in \r\n. +** If fobj is non-NULL it must be a PyFileObject. In this case there +** is no readahead but in stead a flag is used to skip a following +** \n on the next read. Also, if the file is open in binary mode +** the whole conversion is skipped. Finally, the routine keeps track of +** the different types of newlines seen. +** Note that we need no error handling: fgets() treats error and eof +** identically. +*/ +char * +Py_UniversalNewlineFgets(char *buf, int n, FILE *stream, PyObject *fobj) +{ + char *p = buf; + int c; + int newlinetypes = 0; + int skipnextlf = 0; + int univ_newline = 1; + + if (fobj) { + if (!PyFile_Check(fobj)) { + errno = ENXIO; /* What can you do... */ + return NULL; + } + univ_newline = ((PyFileObject *)fobj)->f_univ_newline; + if ( !univ_newline ) + return fgets(buf, n, stream); + newlinetypes = ((PyFileObject *)fobj)->f_newlinetypes; + skipnextlf = ((PyFileObject *)fobj)->f_skipnextlf; + } + FLOCKFILE(stream); + c = 'x'; /* Shut up gcc warning */ + while (--n > 0 && (c = GETC(stream)) != EOF ) { + if (skipnextlf ) { + skipnextlf = 0; + if (c == '\n') { + /* Seeing a \n here with skipnextlf true + ** means we saw a \r before. + */ + newlinetypes |= NEWLINE_CRLF; + c = GETC(stream); + if (c == EOF) break; + } else { + /* + ** Note that c == EOF also brings us here, + ** so we're okay if the last char in the file + ** is a CR. + */ + newlinetypes |= NEWLINE_CR; + } + } + if (c == '\r') { + /* A \r is translated into a \n, and we skip + ** an adjacent \n, if any. We don't set the + ** newlinetypes flag until we've seen the next char. + */ + skipnextlf = 1; + c = '\n'; + } else if ( c == '\n') { + newlinetypes |= NEWLINE_LF; + } + *p++ = c; + if (c == '\n') break; + } + if ( c == EOF && skipnextlf ) + newlinetypes |= NEWLINE_CR; + FUNLOCKFILE(stream); + *p = '\0'; + if (fobj) { + ((PyFileObject *)fobj)->f_newlinetypes = newlinetypes; + ((PyFileObject *)fobj)->f_skipnextlf = skipnextlf; + } else if ( skipnextlf ) { + /* If we have no file object we cannot save the + ** skipnextlf flag. We have to readahead, which + ** will cause a pause if we're reading from an + ** interactive stream, but that is very unlikely + ** unless we're doing something silly like + ** execfile("/dev/tty"). + */ + c = GETC(stream); + if ( c != '\n' ) + ungetc(c, stream); + } + if (p == buf) + return NULL; + return buf; +} + +/* +** Py_UniversalNewlineFread is an fread variation that understands +** all of \r, \n and \r\n conventions. +** The stream should be opened in binary mode. +** fobj must be a PyFileObject. In this case there +** is no readahead but in stead a flag is used to skip a following +** \n on the next read. Also, if the file is open in binary mode +** the whole conversion is skipped. Finally, the routine keeps track of +** the different types of newlines seen. +*/ +size_t +Py_UniversalNewlineFread(char *buf, size_t n, + FILE *stream, PyObject *fobj) +{ + char *dst = buf; + PyFileObject *f = (PyFileObject *)fobj; + int newlinetypes, skipnextlf; + + assert(buf != NULL); + assert(stream != NULL); + + if (!fobj || !PyFile_Check(fobj)) { + errno = ENXIO; /* What can you do... */ + return 0; + } + if (!f->f_univ_newline) + return fread(buf, 1, n, stream); + newlinetypes = f->f_newlinetypes; + skipnextlf = f->f_skipnextlf; + /* Invariant: n is the number of bytes remaining to be filled + * in the buffer. + */ + while (n) { + size_t nread; + int shortread; + char *src = dst; + + nread = fread(dst, 1, n, stream); + assert(nread <= n); + if (nread == 0) + break; + + n -= nread; /* assuming 1 byte out for each in; will adjust */ + shortread = n != 0; /* true iff EOF or error */ + while (nread--) { + char c = *src++; + if (c == '\r') { + /* Save as LF and set flag to skip next LF. */ + *dst++ = '\n'; + skipnextlf = 1; + } + else if (skipnextlf && c == '\n') { + /* Skip LF, and remember we saw CR LF. */ + skipnextlf = 0; + newlinetypes |= NEWLINE_CRLF; + ++n; + } + else { + /* Normal char to be stored in buffer. Also + * update the newlinetypes flag if either this + * is an LF or the previous char was a CR. + */ + if (c == '\n') + newlinetypes |= NEWLINE_LF; + else if (skipnextlf) + newlinetypes |= NEWLINE_CR; + *dst++ = c; + skipnextlf = 0; + } + } + if (shortread) { + /* If this is EOF, update type flags. */ + if (skipnextlf && feof(stream)) + newlinetypes |= NEWLINE_CR; + break; + } + } + f->f_newlinetypes = newlinetypes; + f->f_skipnextlf = skipnextlf; + return dst - buf; +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/floatobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/floatobject.c new file mode 100644 index 00000000..347e4f56 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/floatobject.c @@ -0,0 +1,1253 @@ + +/* Float object implementation */ + +/* XXX There should be overflow checks here, but it's hard to check + for any kind of float exception without losing portability. */ + +#include "Python.h" + +#include + +#if !defined(__STDC__) && !defined(macintosh) +extern double fmod(double, double); +extern double pow(double, double); +#endif + +#if defined(sun) && !defined(__SVR4) +/* On SunOS4.1 only libm.a exists. Make sure that references to all + needed math functions exist in the executable, so that dynamic + loading of mathmodule does not fail. */ +double (*_Py_math_funcs_hack[])() = { + acos, asin, atan, atan2, ceil, cos, cosh, exp, fabs, floor, + fmod, log, log10, pow, sin, sinh, sqrt, tan, tanh +}; +#endif + +/* Special free list -- see comments for same code in intobject.c. */ +#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */ +#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */ +#define N_FLOATOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyFloatObject)) + +struct _floatblock { + struct _floatblock *next; + PyFloatObject objects[N_FLOATOBJECTS]; +}; + +typedef struct _floatblock PyFloatBlock; + +static PyFloatBlock *block_list = NULL; +static PyFloatObject *free_list = NULL; + +static PyFloatObject * +fill_free_list(void) +{ + PyFloatObject *p, *q; + /* XXX Float blocks escape the object heap. Use PyObject_MALLOC ??? */ + p = (PyFloatObject *) PyMem_MALLOC(sizeof(PyFloatBlock)); + if (p == NULL) + return (PyFloatObject *) PyErr_NoMemory(); + ((PyFloatBlock *)p)->next = block_list; + block_list = (PyFloatBlock *)p; + p = &((PyFloatBlock *)p)->objects[0]; + q = p + N_FLOATOBJECTS; + while (--q > p) + q->ob_type = (struct _typeobject *)(q-1); + q->ob_type = NULL; + return p + N_FLOATOBJECTS - 1; +} + +PyObject * +PyFloat_FromDouble(double fval) +{ + register PyFloatObject *op; + if (free_list == NULL) { + if ((free_list = fill_free_list()) == NULL) + return NULL; + } + /* Inline PyObject_New */ + op = free_list; + free_list = (PyFloatObject *)op->ob_type; + PyObject_INIT(op, &PyFloat_Type); + op->ob_fval = fval; + return (PyObject *) op; +} + +/************************************************************************** +RED_FLAG 22-Sep-2000 tim +PyFloat_FromString's pend argument is braindead. Prior to this RED_FLAG, + +1. If v was a regular string, *pend was set to point to its terminating + null byte. That's useless (the caller can find that without any + help from this function!). + +2. If v was a Unicode string, or an object convertible to a character + buffer, *pend was set to point into stack trash (the auto temp + vector holding the character buffer). That was downright dangerous. + +Since we can't change the interface of a public API function, pend is +still supported but now *officially* useless: if pend is not NULL, +*pend is set to NULL. +**************************************************************************/ +PyObject * +PyFloat_FromString(PyObject *v, char **pend) +{ + const char *s, *last, *end; + double x; + char buffer[256]; /* for errors */ +#ifdef Py_USING_UNICODE + char s_buffer[256]; /* for objects convertible to a char buffer */ +#endif + int len; + + if (pend) + *pend = NULL; + if (PyString_Check(v)) { + s = PyString_AS_STRING(v); + len = PyString_GET_SIZE(v); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(v)) { + if (PyUnicode_GET_SIZE(v) >= sizeof(s_buffer)) { + PyErr_SetString(PyExc_ValueError, + "Unicode float() literal too long to convert"); + return NULL; + } + if (PyUnicode_EncodeDecimal(PyUnicode_AS_UNICODE(v), + PyUnicode_GET_SIZE(v), + s_buffer, + NULL)) + return NULL; + s = s_buffer; + len = (int)strlen(s); + } +#endif + else if (PyObject_AsCharBuffer(v, &s, &len)) { + PyErr_SetString(PyExc_TypeError, + "float() argument must be a string or a number"); + return NULL; + } + + last = s + len; + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (*s == '\0') { + PyErr_SetString(PyExc_ValueError, "empty string for float()"); + return NULL; + } + /* We don't care about overflow or underflow. If the platform supports + * them, infinities and signed zeroes (on underflow) are fine. + * However, strtod can return 0 for denormalized numbers, where atof + * does not. So (alas!) we special-case a zero result. Note that + * whether strtod sets errno on underflow is not defined, so we can't + * key off errno. + */ + PyFPE_START_PROTECT("strtod", return NULL) + x = strtod(s, (char **)&end); + PyFPE_END_PROTECT(x) + errno = 0; + /* Believe it or not, Solaris 2.6 can move end *beyond* the null + byte at the end of the string, when the input is inf(inity). */ + if (end > last) + end = last; + if (end == s) { + PyOS_snprintf(buffer, sizeof(buffer), + "invalid literal for float(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + /* Since end != s, the platform made *some* kind of sense out + of the input. Trust it. */ + while (*end && isspace(Py_CHARMASK(*end))) + end++; + if (*end != '\0') { + PyOS_snprintf(buffer, sizeof(buffer), + "invalid literal for float(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + else if (end != last) { + PyErr_SetString(PyExc_ValueError, + "null byte in argument for float()"); + return NULL; + } + if (x == 0.0) { + /* See above -- may have been strtod being anal + about denorms. */ + PyFPE_START_PROTECT("atof", return NULL) + x = atof(s); + PyFPE_END_PROTECT(x) + errno = 0; /* whether atof ever set errno is undefined */ + } + return PyFloat_FromDouble(x); +} + +static void +float_dealloc(PyFloatObject *op) +{ + if (PyFloat_CheckExact(op)) { + op->ob_type = (struct _typeobject *)free_list; + free_list = op; + } + else + op->ob_type->tp_free((PyObject *)op); +} + +double +PyFloat_AsDouble(PyObject *op) +{ + PyNumberMethods *nb; + PyFloatObject *fo; + double val; + + if (op && PyFloat_Check(op)) + return PyFloat_AS_DOUBLE((PyFloatObject*) op); + + if (op == NULL) { + PyErr_BadArgument(); + return -1; + } + + if ((nb = op->ob_type->tp_as_number) == NULL || nb->nb_float == NULL) { + PyErr_SetString(PyExc_TypeError, "a float is required"); + return -1; + } + + fo = (PyFloatObject*) (*nb->nb_float) (op); + if (fo == NULL) + return -1; + if (!PyFloat_Check(fo)) { + PyErr_SetString(PyExc_TypeError, + "nb_float should return float object"); + return -1; + } + + val = PyFloat_AS_DOUBLE(fo); + Py_DECREF(fo); + + return val; +} + +/* Methods */ + +static void +format_float(char *buf, size_t buflen, PyFloatObject *v, int precision) +{ + register char *cp; + /* Subroutine for float_repr and float_print. + We want float numbers to be recognizable as such, + i.e., they should contain a decimal point or an exponent. + However, %g may print the number as an integer; + in such cases, we append ".0" to the string. */ + + assert(PyFloat_Check(v)); + PyOS_snprintf(buf, buflen, "%.*g", precision, v->ob_fval); + cp = buf; + if (*cp == '-') + cp++; + for (; *cp != '\0'; cp++) { + /* Any non-digit means it's not an integer; + this takes care of NAN and INF as well. */ + if (!isdigit(Py_CHARMASK(*cp))) + break; + } + if (*cp == '\0') { + *cp++ = '.'; + *cp++ = '0'; + *cp++ = '\0'; + } +} + +/* XXX PyFloat_AsStringEx should not be a public API function (for one + XXX thing, its signature passes a buffer without a length; for another, + XXX it isn't useful outside this file). +*/ +void +PyFloat_AsStringEx(char *buf, PyFloatObject *v, int precision) +{ + format_float(buf, 100, v, precision); +} + +/* Macro and helper that convert PyObject obj to a C double and store + the value in dbl; this replaces the functionality of the coercion + slot function. If conversion to double raises an exception, obj is + set to NULL, and the function invoking this macro returns NULL. If + obj is not of float, int or long type, Py_NotImplemented is incref'ed, + stored in obj, and returned from the function invoking this macro. +*/ +#define CONVERT_TO_DOUBLE(obj, dbl) \ + if (PyFloat_Check(obj)) \ + dbl = PyFloat_AS_DOUBLE(obj); \ + else if (convert_to_double(&(obj), &(dbl)) < 0) \ + return obj; + +static int +convert_to_double(PyObject **v, double *dbl) +{ + register PyObject *obj = *v; + + if (PyInt_Check(obj)) { + *dbl = (double)PyInt_AS_LONG(obj); + } + else if (PyLong_Check(obj)) { + *dbl = PyLong_AsDouble(obj); + if (*dbl == -1.0 && PyErr_Occurred()) { + *v = NULL; + return -1; + } + } + else { + Py_INCREF(Py_NotImplemented); + *v = Py_NotImplemented; + return -1; + } + return 0; +} + +/* Precisions used by repr() and str(), respectively. + + The repr() precision (17 significant decimal digits) is the minimal number + that is guaranteed to have enough precision so that if the number is read + back in the exact same binary value is recreated. This is true for IEEE + floating point by design, and also happens to work for all other modern + hardware. + + The str() precision is chosen so that in most cases, the rounding noise + created by various operations is suppressed, while giving plenty of + precision for practical use. + +*/ + +#define PREC_REPR 17 +#define PREC_STR 12 + +/* XXX PyFloat_AsString and PyFloat_AsReprString should be deprecated: + XXX they pass a char buffer without passing a length. +*/ +void +PyFloat_AsString(char *buf, PyFloatObject *v) +{ + format_float(buf, 100, v, PREC_STR); +} + +void +PyFloat_AsReprString(char *buf, PyFloatObject *v) +{ + format_float(buf, 100, v, PREC_REPR); +} + +/* ARGSUSED */ +static int +float_print(PyFloatObject *v, FILE *fp, int flags) +{ + char buf[100]; + format_float(buf, sizeof(buf), v, + (flags & Py_PRINT_RAW) ? PREC_STR : PREC_REPR); + fputs(buf, fp); + return 0; +} + +static PyObject * +float_repr(PyFloatObject *v) +{ + char buf[100]; + format_float(buf, sizeof(buf), v, PREC_REPR); + return PyString_FromString(buf); +} + +static PyObject * +float_str(PyFloatObject *v) +{ + char buf[100]; + format_float(buf, sizeof(buf), v, PREC_STR); + return PyString_FromString(buf); +} + +static int +float_compare(PyFloatObject *v, PyFloatObject *w) +{ + double i = v->ob_fval; + double j = w->ob_fval; + return (i < j) ? -1 : (i > j) ? 1 : 0; +} + +static long +float_hash(PyFloatObject *v) +{ + return _Py_HashDouble(v->ob_fval); +} + +static PyObject * +float_add(PyObject *v, PyObject *w) +{ + double a,b; + CONVERT_TO_DOUBLE(v, a); + CONVERT_TO_DOUBLE(w, b); + PyFPE_START_PROTECT("add", return 0) + a = a + b; + PyFPE_END_PROTECT(a) + return PyFloat_FromDouble(a); +} + +static PyObject * +float_sub(PyObject *v, PyObject *w) +{ + double a,b; + CONVERT_TO_DOUBLE(v, a); + CONVERT_TO_DOUBLE(w, b); + PyFPE_START_PROTECT("subtract", return 0) + a = a - b; + PyFPE_END_PROTECT(a) + return PyFloat_FromDouble(a); +} + +static PyObject * +float_mul(PyObject *v, PyObject *w) +{ + double a,b; + CONVERT_TO_DOUBLE(v, a); + CONVERT_TO_DOUBLE(w, b); + PyFPE_START_PROTECT("multiply", return 0) + a = a * b; + PyFPE_END_PROTECT(a) + return PyFloat_FromDouble(a); +} + +static PyObject * +float_div(PyObject *v, PyObject *w) +{ + double a,b; + CONVERT_TO_DOUBLE(v, a); + CONVERT_TO_DOUBLE(w, b); + if (b == 0.0) { + PyErr_SetString(PyExc_ZeroDivisionError, "float division"); + return NULL; + } + PyFPE_START_PROTECT("divide", return 0) + a = a / b; + PyFPE_END_PROTECT(a) + return PyFloat_FromDouble(a); +} + +static PyObject * +float_classic_div(PyObject *v, PyObject *w) +{ + double a,b; + CONVERT_TO_DOUBLE(v, a); + CONVERT_TO_DOUBLE(w, b); + if (Py_DivisionWarningFlag >= 2 && + PyErr_Warn(PyExc_DeprecationWarning, "classic float division") < 0) + return NULL; + if (b == 0.0) { + PyErr_SetString(PyExc_ZeroDivisionError, "float division"); + return NULL; + } + PyFPE_START_PROTECT("divide", return 0) + a = a / b; + PyFPE_END_PROTECT(a) + return PyFloat_FromDouble(a); +} + +static PyObject * +float_rem(PyObject *v, PyObject *w) +{ + double vx, wx; + double mod; + CONVERT_TO_DOUBLE(v, vx); + CONVERT_TO_DOUBLE(w, wx); + if (wx == 0.0) { + PyErr_SetString(PyExc_ZeroDivisionError, "float modulo"); + return NULL; + } + PyFPE_START_PROTECT("modulo", return 0) + mod = fmod(vx, wx); + /* note: checking mod*wx < 0 is incorrect -- underflows to + 0 if wx < sqrt(smallest nonzero double) */ + if (mod && ((wx < 0) != (mod < 0))) { + mod += wx; + } + PyFPE_END_PROTECT(mod) + return PyFloat_FromDouble(mod); +} + +static PyObject * +float_divmod(PyObject *v, PyObject *w) +{ + double vx, wx; + double div, mod, floordiv; + CONVERT_TO_DOUBLE(v, vx); + CONVERT_TO_DOUBLE(w, wx); + if (wx == 0.0) { + PyErr_SetString(PyExc_ZeroDivisionError, "float divmod()"); + return NULL; + } + PyFPE_START_PROTECT("divmod", return 0) + mod = fmod(vx, wx); + /* fmod is typically exact, so vx-mod is *mathematically* an + exact multiple of wx. But this is fp arithmetic, and fp + vx - mod is an approximation; the result is that div may + not be an exact integral value after the division, although + it will always be very close to one. + */ + div = (vx - mod) / wx; + if (mod) { + /* ensure the remainder has the same sign as the denominator */ + if ((wx < 0) != (mod < 0)) { + mod += wx; + div -= 1.0; + } + } + else { + /* the remainder is zero, and in the presence of signed zeroes + fmod returns different results across platforms; ensure + it has the same sign as the denominator; we'd like to do + "mod = wx * 0.0", but that may get optimized away */ + mod *= mod; /* hide "mod = +0" from optimizer */ + if (wx < 0.0) + mod = -mod; + } + /* snap quotient to nearest integral value */ + if (div) { + floordiv = floor(div); + if (div - floordiv > 0.5) + floordiv += 1.0; + } + else { + /* div is zero - get the same sign as the true quotient */ + div *= div; /* hide "div = +0" from optimizers */ + floordiv = div * vx / wx; /* zero w/ sign of vx/wx */ + } + PyFPE_END_PROTECT(floordiv) + return Py_BuildValue("(dd)", floordiv, mod); +} + +static PyObject * +float_floor_div(PyObject *v, PyObject *w) +{ + PyObject *t, *r; + + t = float_divmod(v, w); + if (t == NULL || t == Py_NotImplemented) + return t; + assert(PyTuple_CheckExact(t)); + r = PyTuple_GET_ITEM(t, 0); + Py_INCREF(r); + Py_DECREF(t); + return r; +} + +static PyObject * +float_pow(PyObject *v, PyObject *w, PyObject *z) +{ + double iv, iw, ix; + + if ((PyObject *)z != Py_None) { + PyErr_SetString(PyExc_TypeError, "pow() 3rd argument not " + "allowed unless all arguments are integers"); + return NULL; + } + + CONVERT_TO_DOUBLE(v, iv); + CONVERT_TO_DOUBLE(w, iw); + + /* Sort out special cases here instead of relying on pow() */ + if (iw == 0) { /* v**0 is 1, even 0**0 */ + PyFPE_START_PROTECT("pow", return NULL) + if ((PyObject *)z != Py_None) { + double iz; + CONVERT_TO_DOUBLE(z, iz); + ix = fmod(1.0, iz); + if (ix != 0 && iz < 0) + ix += iz; + } + else + ix = 1.0; + PyFPE_END_PROTECT(ix) + return PyFloat_FromDouble(ix); + } + if (iv == 0.0) { /* 0**w is error if w<0, else 1 */ + if (iw < 0.0) { + PyErr_SetString(PyExc_ZeroDivisionError, + "0.0 cannot be raised to a negative power"); + return NULL; + } + return PyFloat_FromDouble(0.0); + } + if (iv < 0.0) { + /* Whether this is an error is a mess, and bumps into libm + * bugs so we have to figure it out ourselves. + */ + if (iw != floor(iw)) { + PyErr_SetString(PyExc_ValueError, "negative number " + "cannot be raised to a fractional power"); + return NULL; + } + /* iw is an exact integer, albeit perhaps a very large one. + * -1 raised to an exact integer should never be exceptional. + * Alas, some libms (chiefly glibc as of early 2003) return + * NaN and set EDOM on pow(-1, large_int) if the int doesn't + * happen to be representable in a *C* integer. That's a + * bug; we let that slide in math.pow() (which currently + * reflects all platform accidents), but not for Python's **. + */ + if (iv == -1.0 && !Py_IS_INFINITY(iw) && iw == iw) { + /* XXX the "iw == iw" was to weed out NaNs. This + * XXX doesn't actually work on all platforms. + */ + /* Return 1 if iw is even, -1 if iw is odd; there's + * no guarantee that any C integral type is big + * enough to hold iw, so we have to check this + * indirectly. + */ + ix = floor(iw * 0.5) * 2.0; + return PyFloat_FromDouble(ix == iw ? 1.0 : -1.0); + } + /* Else iv != -1.0, and overflow or underflow are possible. + * Unless we're to write pow() ourselves, we have to trust + * the platform to do this correctly. + */ + } + errno = 0; + PyFPE_START_PROTECT("pow", return NULL) + ix = pow(iv, iw); + PyFPE_END_PROTECT(ix) + Py_ADJUST_ERANGE1(ix); + if (errno != 0) { + /* We don't expect any errno value other than ERANGE, but + * the range of libm bugs appears unbounded. + */ + PyErr_SetFromErrno(errno == ERANGE ? PyExc_OverflowError : + PyExc_ValueError); + return NULL; + } + return PyFloat_FromDouble(ix); +} + +static PyObject * +float_neg(PyFloatObject *v) +{ + return PyFloat_FromDouble(-v->ob_fval); +} + +static PyObject * +float_pos(PyFloatObject *v) +{ + if (PyFloat_CheckExact(v)) { + Py_INCREF(v); + return (PyObject *)v; + } + else + return PyFloat_FromDouble(v->ob_fval); +} + +static PyObject * +float_abs(PyFloatObject *v) +{ + return PyFloat_FromDouble(fabs(v->ob_fval)); +} + +static int +float_nonzero(PyFloatObject *v) +{ + return v->ob_fval != 0.0; +} + +static int +float_coerce(PyObject **pv, PyObject **pw) +{ + if (PyInt_Check(*pw)) { + long x = PyInt_AsLong(*pw); + *pw = PyFloat_FromDouble((double)x); + Py_INCREF(*pv); + return 0; + } + else if (PyLong_Check(*pw)) { + double x = PyLong_AsDouble(*pw); + if (x == -1.0 && PyErr_Occurred()) + return -1; + *pw = PyFloat_FromDouble(x); + Py_INCREF(*pv); + return 0; + } + else if (PyFloat_Check(*pw)) { + Py_INCREF(*pv); + Py_INCREF(*pw); + return 0; + } + return 1; /* Can't do it */ +} + +static PyObject * +float_long(PyObject *v) +{ + double x = PyFloat_AsDouble(v); + return PyLong_FromDouble(x); +} + +static PyObject * +float_int(PyObject *v) +{ + double x = PyFloat_AsDouble(v); + double wholepart; /* integral portion of x, rounded toward 0 */ + + (void)modf(x, &wholepart); + /* Try to get out cheap if this fits in a Python int. The attempt + * to cast to long must be protected, as C doesn't define what + * happens if the double is too big to fit in a long. Some rare + * systems raise an exception then (RISCOS was mentioned as one, + * and someone using a non-default option on Sun also bumped into + * that). Note that checking for >= and <= LONG_{MIN,MAX} would + * still be vulnerable: if a long has more bits of precision than + * a double, casting MIN/MAX to double may yield an approximation, + * and if that's rounded up, then, e.g., wholepart=LONG_MAX+1 would + * yield true from the C expression wholepart<=LONG_MAX, despite + * that wholepart is actually greater than LONG_MAX. + */ + if (LONG_MIN < wholepart && wholepart < LONG_MAX) { + const long aslong = (long)wholepart; + return PyInt_FromLong(aslong); + } + return PyLong_FromDouble(wholepart); +} + +static PyObject * +float_float(PyObject *v) +{ + Py_INCREF(v); + return v; +} + + +static PyObject * +float_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + +static PyObject * +float_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *x = Py_False; /* Integer zero */ + static char *kwlist[] = {"x", 0}; + + if (type != &PyFloat_Type) + return float_subtype_new(type, args, kwds); /* Wimp out */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:float", kwlist, &x)) + return NULL; + if (PyString_Check(x)) + return PyFloat_FromString(x, NULL); + return PyNumber_Float(x); +} + +/* Wimpy, slow approach to tp_new calls for subtypes of float: + first create a regular float from whatever arguments we got, + then allocate a subtype instance and initialize its ob_fval + from the regular float. The regular float is then thrown away. +*/ +static PyObject * +float_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *tmp, *new; + + assert(PyType_IsSubtype(type, &PyFloat_Type)); + tmp = float_new(&PyFloat_Type, args, kwds); + if (tmp == NULL) + return NULL; + assert(PyFloat_CheckExact(tmp)); + new = type->tp_alloc(type, 0); + if (new == NULL) { + Py_DECREF(tmp); + return NULL; + } + ((PyFloatObject *)new)->ob_fval = ((PyFloatObject *)tmp)->ob_fval; + Py_DECREF(tmp); + return new; +} + +static PyObject * +float_getnewargs(PyFloatObject *v) +{ + return Py_BuildValue("(d)", v->ob_fval); +} + +static PyMethodDef float_methods[] = { + {"__getnewargs__", (PyCFunction)float_getnewargs, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +PyDoc_STRVAR(float_doc, +"float(x) -> floating point number\n\ +\n\ +Convert a string or number to a floating point number, if possible."); + + +static PyNumberMethods float_as_number = { + (binaryfunc)float_add, /*nb_add*/ + (binaryfunc)float_sub, /*nb_subtract*/ + (binaryfunc)float_mul, /*nb_multiply*/ + (binaryfunc)float_classic_div, /*nb_divide*/ + (binaryfunc)float_rem, /*nb_remainder*/ + (binaryfunc)float_divmod, /*nb_divmod*/ + (ternaryfunc)float_pow, /*nb_power*/ + (unaryfunc)float_neg, /*nb_negative*/ + (unaryfunc)float_pos, /*nb_positive*/ + (unaryfunc)float_abs, /*nb_absolute*/ + (inquiry)float_nonzero, /*nb_nonzero*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + 0, /*nb_and*/ + 0, /*nb_xor*/ + 0, /*nb_or*/ + (coercion)float_coerce, /*nb_coerce*/ + (unaryfunc)float_int, /*nb_int*/ + (unaryfunc)float_long, /*nb_long*/ + (unaryfunc)float_float, /*nb_float*/ + 0, /* nb_oct */ + 0, /* nb_hex */ + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ + 0, /* nb_inplace_divide */ + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + float_floor_div, /* nb_floor_divide */ + float_div, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +}; + +PyTypeObject PyFloat_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "float", + sizeof(PyFloatObject), + 0, + (destructor)float_dealloc, /* tp_dealloc */ + (printfunc)float_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)float_compare, /* tp_compare */ + (reprfunc)float_repr, /* tp_repr */ + &float_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)float_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)float_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + float_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + float_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + float_new, /* tp_new */ +}; + +void +PyFloat_Fini(void) +{ + PyFloatObject *p; + PyFloatBlock *list, *next; + int i; + int bc, bf; /* block count, number of freed blocks */ + int frem, fsum; /* remaining unfreed floats per block, total */ + + bc = 0; + bf = 0; + fsum = 0; + list = block_list; + block_list = NULL; + free_list = NULL; + while (list != NULL) { + bc++; + frem = 0; + for (i = 0, p = &list->objects[0]; + i < N_FLOATOBJECTS; + i++, p++) { + if (PyFloat_CheckExact(p) && p->ob_refcnt != 0) + frem++; + } + next = list->next; + if (frem) { + list->next = block_list; + block_list = list; + for (i = 0, p = &list->objects[0]; + i < N_FLOATOBJECTS; + i++, p++) { + if (!PyFloat_CheckExact(p) || + p->ob_refcnt == 0) { + p->ob_type = (struct _typeobject *) + free_list; + free_list = p; + } + } + } + else { + PyMem_FREE(list); /* XXX PyObject_FREE ??? */ + bf++; + } + fsum += frem; + list = next; + } + if (!Py_VerboseFlag) + return; + fprintf(stderr, "# cleanup floats"); + if (!fsum) { + fprintf(stderr, "\n"); + } + else { + fprintf(stderr, + ": %d unfreed float%s in %d out of %d block%s\n", + fsum, fsum == 1 ? "" : "s", + bc - bf, bc, bc == 1 ? "" : "s"); + } + if (Py_VerboseFlag > 1) { + list = block_list; + while (list != NULL) { + for (i = 0, p = &list->objects[0]; + i < N_FLOATOBJECTS; + i++, p++) { + if (PyFloat_CheckExact(p) && + p->ob_refcnt != 0) { + char buf[100]; + PyFloat_AsString(buf, p); + fprintf(stderr, + "# \n", + p, p->ob_refcnt, buf); + } + } + list = list->next; + } + } +} + +/*---------------------------------------------------------------------------- + * _PyFloat_{Pack,Unpack}{4,8}. See floatobject.h. + * + * TODO: On platforms that use the standard IEEE-754 single and double + * formats natively, these routines could simply copy the bytes. + */ +int +_PyFloat_Pack4(double x, unsigned char *p, int le) +{ + unsigned char sign; + int e; + double f; + unsigned int fbits; + int incr = 1; + + if (le) { + p += 3; + incr = -1; + } + + if (x < 0) { + sign = 1; + x = -x; + } + else + sign = 0; + + f = frexp(x, &e); + + /* Normalize f to be in the range [1.0, 2.0) */ + if (0.5 <= f && f < 1.0) { + f *= 2.0; + e--; + } + else if (f == 0.0) + e = 0; + else { + PyErr_SetString(PyExc_SystemError, + "frexp() result out of range"); + return -1; + } + + if (e >= 128) + goto Overflow; + else if (e < -126) { + /* Gradual underflow */ + f = ldexp(f, 126 + e); + e = 0; + } + else if (!(e == 0 && f == 0.0)) { + e += 127; + f -= 1.0; /* Get rid of leading 1 */ + } + + f *= 8388608.0; /* 2**23 */ + fbits = (unsigned int)(f + 0.5); /* Round */ + assert(fbits <= 8388608); + if (fbits >> 23) { + /* The carry propagated out of a string of 23 1 bits. */ + fbits = 0; + ++e; + if (e >= 255) + goto Overflow; + } + + /* First byte */ + *p = (sign << 7) | (e >> 1); + p += incr; + + /* Second byte */ + *p = (char) (((e & 1) << 7) | (fbits >> 16)); + p += incr; + + /* Third byte */ + *p = (fbits >> 8) & 0xFF; + p += incr; + + /* Fourth byte */ + *p = fbits & 0xFF; + + /* Done */ + return 0; + + Overflow: + PyErr_SetString(PyExc_OverflowError, + "float too large to pack with f format"); + return -1; +} + +int +_PyFloat_Pack8(double x, unsigned char *p, int le) +{ + unsigned char sign; + int e; + double f; + unsigned int fhi, flo; + int incr = 1; + + if (le) { + p += 7; + incr = -1; + } + + if (x < 0) { + sign = 1; + x = -x; + } + else + sign = 0; + + f = frexp(x, &e); + + /* Normalize f to be in the range [1.0, 2.0) */ + if (0.5 <= f && f < 1.0) { + f *= 2.0; + e--; + } + else if (f == 0.0) + e = 0; + else { + PyErr_SetString(PyExc_SystemError, + "frexp() result out of range"); + return -1; + } + + if (e >= 1024) + goto Overflow; + else if (e < -1022) { + /* Gradual underflow */ + f = ldexp(f, 1022 + e); + e = 0; + } + else if (!(e == 0 && f == 0.0)) { + e += 1023; + f -= 1.0; /* Get rid of leading 1 */ + } + + /* fhi receives the high 28 bits; flo the low 24 bits (== 52 bits) */ + f *= 268435456.0; /* 2**28 */ + fhi = (unsigned int)f; /* Truncate */ + assert(fhi < 268435456); + + f -= (double)fhi; + f *= 16777216.0; /* 2**24 */ + flo = (unsigned int)(f + 0.5); /* Round */ + assert(flo <= 16777216); + if (flo >> 24) { + /* The carry propagated out of a string of 24 1 bits. */ + flo = 0; + ++fhi; + if (fhi >> 28) { + /* And it also progagated out of the next 28 bits. */ + fhi = 0; + ++e; + if (e >= 2047) + goto Overflow; + } + } + + /* First byte */ + *p = (sign << 7) | (e >> 4); + p += incr; + + /* Second byte */ + *p = (unsigned char) (((e & 0xF) << 4) | (fhi >> 24)); + p += incr; + + /* Third byte */ + *p = (fhi >> 16) & 0xFF; + p += incr; + + /* Fourth byte */ + *p = (fhi >> 8) & 0xFF; + p += incr; + + /* Fifth byte */ + *p = fhi & 0xFF; + p += incr; + + /* Sixth byte */ + *p = (flo >> 16) & 0xFF; + p += incr; + + /* Seventh byte */ + *p = (flo >> 8) & 0xFF; + p += incr; + + /* Eighth byte */ + *p = flo & 0xFF; + p += incr; + + /* Done */ + return 0; + + Overflow: + PyErr_SetString(PyExc_OverflowError, + "float too large to pack with d format"); + return -1; +} + +double +_PyFloat_Unpack4(const unsigned char *p, int le) +{ + unsigned char sign; + int e; + unsigned int f; + double x; + int incr = 1; + + if (le) { + p += 3; + incr = -1; + } + + /* First byte */ + sign = (*p >> 7) & 1; + e = (*p & 0x7F) << 1; + p += incr; + + /* Second byte */ + e |= (*p >> 7) & 1; + f = (*p & 0x7F) << 16; + p += incr; + + /* Third byte */ + f |= *p << 8; + p += incr; + + /* Fourth byte */ + f |= *p; + + x = (double)f / 8388608.0; + + /* XXX This sadly ignores Inf/NaN issues */ + if (e == 0) + e = -126; + else { + x += 1.0; + e -= 127; + } + x = ldexp(x, e); + + if (sign) + x = -x; + + return x; +} + +double +_PyFloat_Unpack8(const unsigned char *p, int le) +{ + unsigned char sign; + int e; + unsigned int fhi, flo; + double x; + int incr = 1; + + if (le) { + p += 7; + incr = -1; + } + + /* First byte */ + sign = (*p >> 7) & 1; + e = (*p & 0x7F) << 4; + p += incr; + + /* Second byte */ + e |= (*p >> 4) & 0xF; + fhi = (*p & 0xF) << 24; + p += incr; + + /* Third byte */ + fhi |= *p << 16; + p += incr; + + /* Fourth byte */ + fhi |= *p << 8; + p += incr; + + /* Fifth byte */ + fhi |= *p; + p += incr; + + /* Sixth byte */ + flo = *p << 16; + p += incr; + + /* Seventh byte */ + flo |= *p << 8; + p += incr; + + /* Eighth byte */ + flo |= *p; + + x = (double)fhi + (double)flo / 16777216.0; /* 2**24 */ + x /= 268435456.0; /* 2**28 */ + + /* XXX This sadly ignores Inf/NaN */ + if (e == 0) + e = -1022; + else { + x += 1.0; + e -= 1023; + } + x = ldexp(x, e); + + if (sign) + x = -x; + + return x; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/frameobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/frameobject.c new file mode 100644 index 00000000..86b0dfda --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/frameobject.c @@ -0,0 +1,815 @@ + +/* Frame object implementation */ + +#include "Python.h" + +#include "compile.h" +#include "frameobject.h" +#include "opcode.h" +#include "structmember.h" + +#undef MIN +#undef MAX +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define OFF(x) offsetof(PyFrameObject, x) + +static PyMemberDef frame_memberlist[] = { + {"f_back", T_OBJECT, OFF(f_back), RO}, + {"f_code", T_OBJECT, OFF(f_code), RO}, + {"f_builtins", T_OBJECT, OFF(f_builtins),RO}, + {"f_globals", T_OBJECT, OFF(f_globals), RO}, + {"f_lasti", T_INT, OFF(f_lasti), RO}, + {"f_restricted",T_INT, OFF(f_restricted),RO}, + {"f_exc_type", T_OBJECT, OFF(f_exc_type)}, + {"f_exc_value", T_OBJECT, OFF(f_exc_value)}, + {"f_exc_traceback", T_OBJECT, OFF(f_exc_traceback)}, + {NULL} /* Sentinel */ +}; + +static PyObject * +frame_getlocals(PyFrameObject *f, void *closure) +{ + PyFrame_FastToLocals(f); + Py_INCREF(f->f_locals); + return f->f_locals; +} + +static PyObject * +frame_getlineno(PyFrameObject *f, void *closure) +{ + int lineno; + + if (f->f_trace) + lineno = f->f_lineno; + else + lineno = PyCode_Addr2Line(f->f_code, f->f_lasti); + + return PyInt_FromLong(lineno); +} + +/* Setter for f_lineno - you can set f_lineno from within a trace function in + * order to jump to a given line of code, subject to some restrictions. Most + * lines are OK to jump to because they don't make any assumptions about the + * state of the stack (obvious because you could remove the line and the code + * would still work without any stack errors), but there are some constructs + * that limit jumping: + * + * o Lines with an 'except' statement on them can't be jumped to, because + * they expect an exception to be on the top of the stack. + * o Lines that live in a 'finally' block can't be jumped from or to, since + * the END_FINALLY expects to clean up the stack after the 'try' block. + * o 'try'/'for'/'while' blocks can't be jumped into because the blockstack + * needs to be set up before their code runs, and for 'for' loops the + * iterator needs to be on the stack. + */ +static int +frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno) +{ + int new_lineno = 0; /* The new value of f_lineno */ + int new_lasti = 0; /* The new value of f_lasti */ + int new_iblock = 0; /* The new value of f_iblock */ + char *code = NULL; /* The bytecode for the frame... */ + int code_len = 0; /* ...and its length */ + char *lnotab = NULL; /* Iterating over co_lnotab */ + int lnotab_len = 0; /* (ditto) */ + int offset = 0; /* (ditto) */ + int line = 0; /* (ditto) */ + int addr = 0; /* (ditto) */ + int min_addr = 0; /* Scanning the SETUPs and POPs */ + int max_addr = 0; /* (ditto) */ + int delta_iblock = 0; /* (ditto) */ + int min_delta_iblock = 0; /* (ditto) */ + int min_iblock = 0; /* (ditto) */ + int f_lasti_setup_addr = 0; /* Policing no-jump-into-finally */ + int new_lasti_setup_addr = 0; /* (ditto) */ + int blockstack[CO_MAXBLOCKS]; /* Walking the 'finally' blocks */ + int in_finally[CO_MAXBLOCKS]; /* (ditto) */ + int blockstack_top = 0; /* (ditto) */ + int setup_op = 0; /* (ditto) */ + + /* f_lineno must be an integer. */ + if (!PyInt_Check(p_new_lineno)) { + PyErr_SetString(PyExc_ValueError, + "lineno must be an integer"); + return -1; + } + + /* You can only do this from within a trace function, not via + * _getframe or similar hackery. */ + if (!f->f_trace) + { + PyErr_Format(PyExc_ValueError, + "f_lineno can only be set by a trace function"); + return -1; + } + + /* Fail if the line comes before the start of the code block. */ + new_lineno = (int) PyInt_AsLong(p_new_lineno); + if (new_lineno < f->f_code->co_firstlineno) { + PyErr_Format(PyExc_ValueError, + "line %d comes before the current code block", + new_lineno); + return -1; + } + + /* Find the bytecode offset for the start of the given line, or the + * first code-owning line after it. */ + PyString_AsStringAndSize(f->f_code->co_lnotab, &lnotab, &lnotab_len); + addr = 0; + line = f->f_code->co_firstlineno; + new_lasti = -1; + for (offset = 0; offset < lnotab_len; offset += 2) { + addr += lnotab[offset]; + line += lnotab[offset+1]; + if (line >= new_lineno) { + new_lasti = addr; + new_lineno = line; + break; + } + } + + /* If we didn't reach the requested line, return an error. */ + if (new_lasti == -1) { + PyErr_Format(PyExc_ValueError, + "line %d comes after the current code block", + new_lineno); + return -1; + } + + /* We're now ready to look at the bytecode. */ + PyString_AsStringAndSize(f->f_code->co_code, &code, &code_len); + min_addr = MIN(new_lasti, f->f_lasti); + max_addr = MAX(new_lasti, f->f_lasti); + + /* You can't jump onto a line with an 'except' statement on it - + * they expect to have an exception on the top of the stack, which + * won't be true if you jump to them. They always start with code + * that either pops the exception using POP_TOP (plain 'except:' + * lines do this) or duplicates the exception on the stack using + * DUP_TOP (if there's an exception type specified). See compile.c, + * 'com_try_except' for the full details. There aren't any other + * cases (AFAIK) where a line's code can start with DUP_TOP or + * POP_TOP, but if any ever appear, they'll be subject to the same + * restriction (but with a different error message). */ + if (code[new_lasti] == DUP_TOP || code[new_lasti] == POP_TOP) { + PyErr_SetString(PyExc_ValueError, + "can't jump to 'except' line as there's no exception"); + return -1; + } + + /* You can't jump into or out of a 'finally' block because the 'try' + * block leaves something on the stack for the END_FINALLY to clean + * up. So we walk the bytecode, maintaining a simulated blockstack. + * When we reach the old or new address and it's in a 'finally' block + * we note the address of the corresponding SETUP_FINALLY. The jump + * is only legal if neither address is in a 'finally' block or + * they're both in the same one. 'blockstack' is a stack of the + * bytecode addresses of the SETUP_X opcodes, and 'in_finally' tracks + * whether we're in a 'finally' block at each blockstack level. */ + f_lasti_setup_addr = -1; + new_lasti_setup_addr = -1; + memset(blockstack, '\0', sizeof(blockstack)); + memset(in_finally, '\0', sizeof(in_finally)); + blockstack_top = 0; + for (addr = 0; addr < code_len; addr++) { + unsigned char op = code[addr]; + switch (op) { + case SETUP_LOOP: + case SETUP_EXCEPT: + case SETUP_FINALLY: + blockstack[blockstack_top++] = addr; + in_finally[blockstack_top-1] = 0; + break; + + case POP_BLOCK: + assert(blockstack_top > 0); + setup_op = code[blockstack[blockstack_top-1]]; + if (setup_op == SETUP_FINALLY) { + in_finally[blockstack_top-1] = 1; + } + else { + blockstack_top--; + } + break; + + case END_FINALLY: + /* Ignore END_FINALLYs for SETUP_EXCEPTs - they exist + * in the bytecode but don't correspond to an actual + * 'finally' block. (If blockstack_top is 0, we must + * be seeing such an END_FINALLY.) */ + if (blockstack_top > 0) { + setup_op = code[blockstack[blockstack_top-1]]; + if (setup_op == SETUP_FINALLY) { + blockstack_top--; + } + } + break; + } + + /* For the addresses we're interested in, see whether they're + * within a 'finally' block and if so, remember the address + * of the SETUP_FINALLY. */ + if (addr == new_lasti || addr == f->f_lasti) { + int i = 0; + int setup_addr = -1; + for (i = blockstack_top-1; i >= 0; i--) { + if (in_finally[i]) { + setup_addr = blockstack[i]; + break; + } + } + + if (setup_addr != -1) { + if (addr == new_lasti) { + new_lasti_setup_addr = setup_addr; + } + + if (addr == f->f_lasti) { + f_lasti_setup_addr = setup_addr; + } + } + } + + if (op >= HAVE_ARGUMENT) { + addr += 2; + } + } + + /* Verify that the blockstack tracking code didn't get lost. */ + assert(blockstack_top == 0); + + /* After all that, are we jumping into / out of a 'finally' block? */ + if (new_lasti_setup_addr != f_lasti_setup_addr) { + PyErr_SetString(PyExc_ValueError, + "can't jump into or out of a 'finally' block"); + return -1; + } + + + /* Police block-jumping (you can't jump into the middle of a block) + * and ensure that the blockstack finishes up in a sensible state (by + * popping any blocks we're jumping out of). We look at all the + * blockstack operations between the current position and the new + * one, and keep track of how many blocks we drop out of on the way. + * By also keeping track of the lowest blockstack position we see, we + * can tell whether the jump goes into any blocks without coming out + * again - in that case we raise an exception below. */ + delta_iblock = 0; + for (addr = min_addr; addr < max_addr; addr++) { + unsigned char op = code[addr]; + switch (op) { + case SETUP_LOOP: + case SETUP_EXCEPT: + case SETUP_FINALLY: + delta_iblock++; + break; + + case POP_BLOCK: + delta_iblock--; + break; + } + + min_delta_iblock = MIN(min_delta_iblock, delta_iblock); + + if (op >= HAVE_ARGUMENT) { + addr += 2; + } + } + + /* Derive the absolute iblock values from the deltas. */ + min_iblock = f->f_iblock + min_delta_iblock; + if (new_lasti > f->f_lasti) { + /* Forwards jump. */ + new_iblock = f->f_iblock + delta_iblock; + } + else { + /* Backwards jump. */ + new_iblock = f->f_iblock - delta_iblock; + } + + /* Are we jumping into a block? */ + if (new_iblock > min_iblock) { + PyErr_SetString(PyExc_ValueError, + "can't jump into the middle of a block"); + return -1; + } + + /* Pop any blocks that we're jumping out of. */ + while (f->f_iblock > new_iblock) { + PyTryBlock *b = &f->f_blockstack[--f->f_iblock]; + while ((f->f_stacktop - f->f_valuestack) > b->b_level) { + PyObject *v = (*--f->f_stacktop); + Py_DECREF(v); + } + } + + /* Finally set the new f_lineno and f_lasti and return OK. */ + f->f_lineno = new_lineno; + f->f_lasti = new_lasti; + return 0; +} + +static PyObject * +frame_gettrace(PyFrameObject *f, void *closure) +{ + PyObject* trace = f->f_trace; + + if (trace == NULL) + trace = Py_None; + + Py_INCREF(trace); + + return trace; +} + +static int +frame_settrace(PyFrameObject *f, PyObject* v, void *closure) +{ + /* We rely on f_lineno being accurate when f_trace is set. */ + + PyObject* old_value = f->f_trace; + + Py_XINCREF(v); + f->f_trace = v; + + if (v != NULL) + f->f_lineno = PyCode_Addr2Line(f->f_code, f->f_lasti); + + Py_XDECREF(old_value); + + return 0; +} + +static PyGetSetDef frame_getsetlist[] = { + {"f_locals", (getter)frame_getlocals, NULL, NULL}, + {"f_lineno", (getter)frame_getlineno, + (setter)frame_setlineno, NULL}, + {"f_trace", (getter)frame_gettrace, (setter)frame_settrace, NULL}, + {0} +}; + +/* Stack frames are allocated and deallocated at a considerable rate. + In an attempt to improve the speed of function calls, we maintain a + separate free list of stack frames (just like integers are + allocated in a special way -- see intobject.c). When a stack frame + is on the free list, only the following members have a meaning: + ob_type == &Frametype + f_back next item on free list, or NULL + f_nlocals number of locals + f_stacksize size of value stack + ob_size size of localsplus + Note that the value and block stacks are preserved -- this can save + another malloc() call or two (and two free() calls as well!). + Also note that, unlike for integers, each frame object is a + malloc'ed object in its own right -- it is only the actual calls to + malloc() that we are trying to save here, not the administration. + After all, while a typical program may make millions of calls, a + call depth of more than 20 or 30 is probably already exceptional + unless the program contains run-away recursion. I hope. + + Later, MAXFREELIST was added to bound the # of frames saved on + free_list. Else programs creating lots of cyclic trash involving + frames could provoke free_list into growing without bound. +*/ + +static PyFrameObject *free_list = NULL; +static int numfree = 0; /* number of frames currently in free_list */ +#define MAXFREELIST 200 /* max value for numfree */ + +static void +frame_dealloc(PyFrameObject *f) +{ + int i, slots; + PyObject **fastlocals; + PyObject **p; + + PyObject_GC_UnTrack(f); + Py_TRASHCAN_SAFE_BEGIN(f) + /* Kill all local variables */ + slots = f->f_nlocals + f->f_ncells + f->f_nfreevars; + fastlocals = f->f_localsplus; + for (i = slots; --i >= 0; ++fastlocals) { + Py_XDECREF(*fastlocals); + } + + /* Free stack */ + if (f->f_stacktop != NULL) { + for (p = f->f_valuestack; p < f->f_stacktop; p++) + Py_XDECREF(*p); + } + + Py_XDECREF(f->f_back); + Py_DECREF(f->f_code); + Py_DECREF(f->f_builtins); + Py_DECREF(f->f_globals); + Py_XDECREF(f->f_locals); + Py_XDECREF(f->f_trace); + Py_XDECREF(f->f_exc_type); + Py_XDECREF(f->f_exc_value); + Py_XDECREF(f->f_exc_traceback); + if (numfree < MAXFREELIST) { + ++numfree; + f->f_back = free_list; + free_list = f; + } + else + PyObject_GC_Del(f); + Py_TRASHCAN_SAFE_END(f) +} + +static int +frame_traverse(PyFrameObject *f, visitproc visit, void *arg) +{ + PyObject **fastlocals, **p; + int i, err, slots; +#define VISIT(o) if (o) {if ((err = visit((PyObject *)(o), arg))) return err;} + + VISIT(f->f_back); + VISIT(f->f_code); + VISIT(f->f_builtins); + VISIT(f->f_globals); + VISIT(f->f_locals); + VISIT(f->f_trace); + VISIT(f->f_exc_type); + VISIT(f->f_exc_value); + VISIT(f->f_exc_traceback); + + /* locals */ + slots = f->f_nlocals + f->f_ncells + f->f_nfreevars; + fastlocals = f->f_localsplus; + for (i = slots; --i >= 0; ++fastlocals) { + VISIT(*fastlocals); + } + + /* stack */ + if (f->f_stacktop != NULL) { + for (p = f->f_valuestack; p < f->f_stacktop; p++) + VISIT(*p); + } + return 0; +} + +static void +frame_clear(PyFrameObject *f) +{ + PyObject **fastlocals, **p; + int i, slots; + + Py_XDECREF(f->f_exc_type); + f->f_exc_type = NULL; + + Py_XDECREF(f->f_exc_value); + f->f_exc_value = NULL; + + Py_XDECREF(f->f_exc_traceback); + f->f_exc_traceback = NULL; + + Py_XDECREF(f->f_trace); + f->f_trace = NULL; + + /* locals */ + slots = f->f_nlocals + f->f_ncells + f->f_nfreevars; + fastlocals = f->f_localsplus; + for (i = slots; --i >= 0; ++fastlocals) { + if (*fastlocals != NULL) { + Py_XDECREF(*fastlocals); + *fastlocals = NULL; + } + } + + /* stack */ + if (f->f_stacktop != NULL) { + for (p = f->f_valuestack; p < f->f_stacktop; p++) { + Py_XDECREF(*p); + *p = NULL; + } + } +} + + +PyTypeObject PyFrame_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "frame", + sizeof(PyFrameObject), + sizeof(PyObject *), + (destructor)frame_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)frame_traverse, /* tp_traverse */ + (inquiry)frame_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + frame_memberlist, /* tp_members */ + frame_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ +}; + +static PyObject *builtin_object; + +int _PyFrame_Init() +{ + builtin_object = PyString_InternFromString("__builtins__"); + return (builtin_object != NULL); +} + +PyFrameObject * +PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, + PyObject *locals) +{ + PyFrameObject *back = tstate->frame; + PyFrameObject *f; + PyObject *builtins; + int extras, ncells, nfrees; + +#ifdef Py_DEBUG + if (code == NULL || globals == NULL || !PyDict_Check(globals) || + (locals != NULL && !PyDict_Check(locals))) { + PyErr_BadInternalCall(); + return NULL; + } +#endif + ncells = PyTuple_GET_SIZE(code->co_cellvars); + nfrees = PyTuple_GET_SIZE(code->co_freevars); + extras = code->co_stacksize + code->co_nlocals + ncells + nfrees; + if (back == NULL || back->f_globals != globals) { + builtins = PyDict_GetItem(globals, builtin_object); + if (builtins) { + if (PyModule_Check(builtins)) { + builtins = PyModule_GetDict(builtins); + assert(!builtins || PyDict_Check(builtins)); + } + else if (!PyDict_Check(builtins)) + builtins = NULL; + } + if (builtins == NULL) { + /* No builtins! Make up a minimal one + Give them 'None', at least. */ + builtins = PyDict_New(); + if (builtins == NULL || + PyDict_SetItemString( + builtins, "None", Py_None) < 0) + return NULL; + } + else + Py_INCREF(builtins); + + } + else { + /* If we share the globals, we share the builtins. + Save a lookup and a call. */ + builtins = back->f_builtins; + assert(builtins != NULL && PyDict_Check(builtins)); + Py_INCREF(builtins); + } + if (free_list == NULL) { + f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras); + if (f == NULL) + return NULL; + } + else { + assert(numfree > 0); + --numfree; + f = free_list; + free_list = free_list->f_back; + if (f->ob_size < extras) { + f = PyObject_GC_Resize(PyFrameObject, f, extras); + if (f == NULL) + return NULL; + } + _Py_NewReference((PyObject *)f); + } + f->f_builtins = builtins; + Py_XINCREF(back); + f->f_back = back; + Py_INCREF(code); + f->f_code = code; + Py_INCREF(globals); + f->f_globals = globals; + /* Most functions have CO_NEWLOCALS and CO_OPTIMIZED set. */ + if ((code->co_flags & (CO_NEWLOCALS | CO_OPTIMIZED)) == + (CO_NEWLOCALS | CO_OPTIMIZED)) + locals = NULL; /* PyFrame_Fast2Locals() will set. */ + else if (code->co_flags & CO_NEWLOCALS) { + locals = PyDict_New(); + if (locals == NULL) { + Py_DECREF(f); + return NULL; + } + } + else { + if (locals == NULL) + locals = globals; + Py_INCREF(locals); + } + f->f_locals = locals; + f->f_trace = NULL; + f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; + f->f_tstate = tstate; + + f->f_lasti = -1; + f->f_lineno = code->co_firstlineno; + f->f_restricted = (builtins != tstate->interp->builtins); + f->f_iblock = 0; + f->f_nlocals = code->co_nlocals; + f->f_stacksize = code->co_stacksize; + f->f_ncells = ncells; + f->f_nfreevars = nfrees; + + extras = f->f_nlocals + ncells + nfrees; + memset(f->f_localsplus, 0, extras * sizeof(f->f_localsplus[0])); + + f->f_valuestack = f->f_localsplus + extras; + f->f_stacktop = f->f_valuestack; + _PyObject_GC_TRACK(f); + return f; +} + +/* Block management */ + +void +PyFrame_BlockSetup(PyFrameObject *f, int type, int handler, int level) +{ + PyTryBlock *b; + if (f->f_iblock >= CO_MAXBLOCKS) + Py_FatalError("XXX block stack overflow"); + b = &f->f_blockstack[f->f_iblock++]; + b->b_type = type; + b->b_level = level; + b->b_handler = handler; +} + +PyTryBlock * +PyFrame_BlockPop(PyFrameObject *f) +{ + PyTryBlock *b; + if (f->f_iblock <= 0) + Py_FatalError("XXX block stack underflow"); + b = &f->f_blockstack[--f->f_iblock]; + return b; +} + +/* Convert between "fast" version of locals and dictionary version */ + +static void +map_to_dict(PyObject *map, int nmap, PyObject *dict, PyObject **values, + int deref) +{ + int j; + for (j = nmap; --j >= 0; ) { + PyObject *key = PyTuple_GET_ITEM(map, j); + PyObject *value = values[j]; + if (deref) + value = PyCell_GET(value); + if (value == NULL) { + if (PyDict_DelItem(dict, key) != 0) + PyErr_Clear(); + } + else { + if (PyDict_SetItem(dict, key, value) != 0) + PyErr_Clear(); + } + } +} + +static void +dict_to_map(PyObject *map, int nmap, PyObject *dict, PyObject **values, + int deref, int clear) +{ + int j; + for (j = nmap; --j >= 0; ) { + PyObject *key = PyTuple_GET_ITEM(map, j); + PyObject *value = PyDict_GetItem(dict, key); + if (deref) { + if (value || clear) { + if (PyCell_GET(values[j]) != value) { + if (PyCell_Set(values[j], value) < 0) + PyErr_Clear(); + } + } + } else if (value != NULL || clear) { + if (values[j] != value) { + Py_XINCREF(value); + Py_XDECREF(values[j]); + values[j] = value; + } + } + } +} + +void +PyFrame_FastToLocals(PyFrameObject *f) +{ + /* Merge fast locals into f->f_locals */ + PyObject *locals, *map; + PyObject **fast; + PyObject *error_type, *error_value, *error_traceback; + int j; + if (f == NULL) + return; + locals = f->f_locals; + if (locals == NULL) { + locals = f->f_locals = PyDict_New(); + if (locals == NULL) { + PyErr_Clear(); /* Can't report it :-( */ + return; + } + } + map = f->f_code->co_varnames; + if (!PyDict_Check(locals) || !PyTuple_Check(map)) + return; + PyErr_Fetch(&error_type, &error_value, &error_traceback); + fast = f->f_localsplus; + j = PyTuple_Size(map); + if (j > f->f_nlocals) + j = f->f_nlocals; + if (f->f_nlocals) + map_to_dict(map, j, locals, fast, 0); + if (f->f_ncells || f->f_nfreevars) { + if (!(PyTuple_Check(f->f_code->co_cellvars) + && PyTuple_Check(f->f_code->co_freevars))) { + return; + } + map_to_dict(f->f_code->co_cellvars, + PyTuple_GET_SIZE(f->f_code->co_cellvars), + locals, fast + f->f_nlocals, 1); + map_to_dict(f->f_code->co_freevars, + PyTuple_GET_SIZE(f->f_code->co_freevars), + locals, fast + f->f_nlocals + f->f_ncells, 1); + } + PyErr_Restore(error_type, error_value, error_traceback); +} + +void +PyFrame_LocalsToFast(PyFrameObject *f, int clear) +{ + /* Merge f->f_locals into fast locals */ + PyObject *locals, *map; + PyObject **fast; + PyObject *error_type, *error_value, *error_traceback; + int j; + if (f == NULL) + return; + locals = f->f_locals; + map = f->f_code->co_varnames; + if (locals == NULL) + return; + if (!PyDict_Check(locals) || !PyTuple_Check(map)) + return; + PyErr_Fetch(&error_type, &error_value, &error_traceback); + fast = f->f_localsplus; + j = PyTuple_Size(map); + if (j > f->f_nlocals) + j = f->f_nlocals; + if (f->f_nlocals) + dict_to_map(f->f_code->co_varnames, j, locals, fast, 0, clear); + if (f->f_ncells || f->f_nfreevars) { + if (!(PyTuple_Check(f->f_code->co_cellvars) + && PyTuple_Check(f->f_code->co_freevars))) + return; + dict_to_map(f->f_code->co_cellvars, + PyTuple_GET_SIZE(f->f_code->co_cellvars), + locals, fast + f->f_nlocals, 1, clear); + dict_to_map(f->f_code->co_freevars, + PyTuple_GET_SIZE(f->f_code->co_freevars), + locals, fast + f->f_nlocals + f->f_ncells, 1, + clear); + } + PyErr_Restore(error_type, error_value, error_traceback); +} + +/* Clear out the free list */ + +void +PyFrame_Fini(void) +{ + while (free_list != NULL) { + PyFrameObject *f = free_list; + free_list = free_list->f_back; + PyObject_GC_Del(f); + --numfree; + } + assert(numfree == 0); + Py_XDECREF(builtin_object); + builtin_object = NULL; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/funcobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/funcobject.c new file mode 100644 index 00000000..f4afbcb2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/funcobject.c @@ -0,0 +1,875 @@ + +/* Function object implementation */ + +#include "Python.h" +#include "compile.h" +#include "eval.h" +#include "structmember.h" + +PyObject * +PyFunction_New(PyObject *code, PyObject *globals) +{ + PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, + &PyFunction_Type); + if (op != NULL) { + PyObject *doc; + PyObject *consts; + PyObject *module; + op->func_weakreflist = NULL; + Py_INCREF(code); + op->func_code = code; + Py_INCREF(globals); + op->func_globals = globals; + op->func_name = ((PyCodeObject *)code)->co_name; + Py_INCREF(op->func_name); + op->func_defaults = NULL; /* No default arguments */ + op->func_closure = NULL; + consts = ((PyCodeObject *)code)->co_consts; + if (PyTuple_Size(consts) >= 1) { + doc = PyTuple_GetItem(consts, 0); + if (!PyString_Check(doc) && !PyUnicode_Check(doc)) + doc = Py_None; + } + else + doc = Py_None; + Py_INCREF(doc); + op->func_doc = doc; + op->func_dict = NULL; + op->func_module = NULL; + + /* __module__: If module name is in globals, use it. + Otherwise, use None. + */ + module = PyDict_GetItemString(globals, "__name__"); + if (module) { + Py_INCREF(module); + op->func_module = module; + } + } + else + return NULL; + _PyObject_GC_TRACK(op); + return (PyObject *)op; +} + +PyObject * +PyFunction_GetCode(PyObject *op) +{ + if (!PyFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyFunctionObject *) op) -> func_code; +} + +PyObject * +PyFunction_GetGlobals(PyObject *op) +{ + if (!PyFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyFunctionObject *) op) -> func_globals; +} + +PyObject * +PyFunction_GetModule(PyObject *op) +{ + if (!PyFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyFunctionObject *) op) -> func_module; +} + +PyObject * +PyFunction_GetDefaults(PyObject *op) +{ + if (!PyFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyFunctionObject *) op) -> func_defaults; +} + +int +PyFunction_SetDefaults(PyObject *op, PyObject *defaults) +{ + if (!PyFunction_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + if (defaults == Py_None) + defaults = NULL; + else if (PyTuple_Check(defaults)) { + Py_XINCREF(defaults); + } + else { + PyErr_SetString(PyExc_SystemError, "non-tuple default args"); + return -1; + } + Py_XDECREF(((PyFunctionObject *) op) -> func_defaults); + ((PyFunctionObject *) op) -> func_defaults = defaults; + return 0; +} + +PyObject * +PyFunction_GetClosure(PyObject *op) +{ + if (!PyFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyFunctionObject *) op) -> func_closure; +} + +int +PyFunction_SetClosure(PyObject *op, PyObject *closure) +{ + if (!PyFunction_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + if (closure == Py_None) + closure = NULL; + else if (PyTuple_Check(closure)) { + Py_XINCREF(closure); + } + else { + PyErr_SetString(PyExc_SystemError, "non-tuple closure"); + return -1; + } + Py_XDECREF(((PyFunctionObject *) op) -> func_closure); + ((PyFunctionObject *) op) -> func_closure = closure; + return 0; +} + +/* Methods */ + +#define OFF(x) offsetof(PyFunctionObject, x) + +#define RR () + +static PyMemberDef func_memberlist[] = { + {"func_closure", T_OBJECT, OFF(func_closure), + RESTRICTED|READONLY}, + {"func_doc", T_OBJECT, OFF(func_doc), WRITE_RESTRICTED}, + {"__doc__", T_OBJECT, OFF(func_doc), WRITE_RESTRICTED}, + {"func_globals", T_OBJECT, OFF(func_globals), + RESTRICTED|READONLY}, + {"func_name", T_OBJECT, OFF(func_name), READONLY}, + {"__name__", T_OBJECT, OFF(func_name), READONLY}, + {"__module__", T_OBJECT, OFF(func_module), WRITE_RESTRICTED}, + {NULL} /* Sentinel */ +}; + +static int +restricted(void) +{ + if (!PyEval_GetRestricted()) + return 0; + PyErr_SetString(PyExc_RuntimeError, + "function attributes not accessible in restricted mode"); + return 1; +} + +static PyObject * +func_get_dict(PyFunctionObject *op) +{ + if (restricted()) + return NULL; + if (op->func_dict == NULL) { + op->func_dict = PyDict_New(); + if (op->func_dict == NULL) + return NULL; + } + Py_INCREF(op->func_dict); + return op->func_dict; +} + +static int +func_set_dict(PyFunctionObject *op, PyObject *value) +{ + PyObject *tmp; + + if (restricted()) + return -1; + /* It is illegal to del f.func_dict */ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "function's dictionary may not be deleted"); + return -1; + } + /* Can only set func_dict to a dictionary */ + if (!PyDict_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "setting function's dictionary to a non-dict"); + return -1; + } + tmp = op->func_dict; + Py_INCREF(value); + op->func_dict = value; + Py_XDECREF(tmp); + return 0; +} + +static PyObject * +func_get_code(PyFunctionObject *op) +{ + if (restricted()) + return NULL; + Py_INCREF(op->func_code); + return op->func_code; +} + +static int +func_set_code(PyFunctionObject *op, PyObject *value) +{ + PyObject *tmp; + + if (restricted()) + return -1; + /* Not legal to del f.func_code or to set it to anything + * other than a code object. */ + if (value == NULL || !PyCode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "func_code must be set to a code object"); + return -1; + } + tmp = op->func_code; + Py_INCREF(value); + op->func_code = value; + Py_DECREF(tmp); + return 0; +} + +static PyObject * +func_get_defaults(PyFunctionObject *op) +{ + if (restricted()) + return NULL; + if (op->func_defaults == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + Py_INCREF(op->func_defaults); + return op->func_defaults; +} + +static int +func_set_defaults(PyFunctionObject *op, PyObject *value) +{ + PyObject *tmp; + + if (restricted()) + return -1; + /* Legal to del f.func_defaults. + * Can only set func_defaults to NULL or a tuple. */ + if (value == Py_None) + value = NULL; + if (value != NULL && !PyTuple_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "func_defaults must be set to a tuple object"); + return -1; + } + tmp = op->func_defaults; + Py_XINCREF(value); + op->func_defaults = value; + Py_XDECREF(tmp); + return 0; +} + +static PyGetSetDef func_getsetlist[] = { + {"func_code", (getter)func_get_code, (setter)func_set_code}, + {"func_defaults", (getter)func_get_defaults, + (setter)func_set_defaults}, + {"func_dict", (getter)func_get_dict, (setter)func_set_dict}, + {"__dict__", (getter)func_get_dict, (setter)func_set_dict}, + {NULL} /* Sentinel */ +}; + +PyDoc_STRVAR(func_doc, +"function(code, globals[, name[, argdefs[, closure]]])\n\ +\n\ +Create a function object from a code object and a dictionary.\n\ +The optional name string overrides the name from the code object.\n\ +The optional argdefs tuple specifies the default argument values.\n\ +The optional closure tuple supplies the bindings for free variables."); + +/* func_new() maintains the following invariants for closures. The + closure must correspond to the free variables of the code object. + + if len(code.co_freevars) == 0: + closure = NULL + else: + len(closure) == len(code.co_freevars) + for every elt in closure, type(elt) == cell +*/ + +static PyObject * +func_new(PyTypeObject* type, PyObject* args, PyObject* kw) +{ + PyCodeObject *code; + PyObject *globals; + PyObject *name = Py_None; + PyObject *defaults = Py_None; + PyObject *closure = Py_None; + PyFunctionObject *newfunc; + int nfree, nclosure; + static char *kwlist[] = {"code", "globals", "name", + "argdefs", "closure", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "O!O!|OOO:function", + kwlist, + &PyCode_Type, &code, + &PyDict_Type, &globals, + &name, &defaults, &closure)) + return NULL; + if (name != Py_None && !PyString_Check(name)) { + PyErr_SetString(PyExc_TypeError, + "arg 3 (name) must be None or string"); + return NULL; + } + if (defaults != Py_None && !PyTuple_Check(defaults)) { + PyErr_SetString(PyExc_TypeError, + "arg 4 (defaults) must be None or tuple"); + return NULL; + } + nfree = PyTuple_GET_SIZE(code->co_freevars); + if (!PyTuple_Check(closure)) { + if (nfree && closure == Py_None) { + PyErr_SetString(PyExc_TypeError, + "arg 5 (closure) must be tuple"); + return NULL; + } + else if (closure != Py_None) { + PyErr_SetString(PyExc_TypeError, + "arg 5 (closure) must be None or tuple"); + return NULL; + } + } + + /* check that the closure is well-formed */ + nclosure = closure == Py_None ? 0 : PyTuple_GET_SIZE(closure); + if (nfree != nclosure) + return PyErr_Format(PyExc_ValueError, + "%s requires closure of length %d, not %d", + PyString_AS_STRING(code->co_name), + nfree, nclosure); + if (nclosure) { + int i; + for (i = 0; i < nclosure; i++) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + if (!PyCell_Check(o)) { + return PyErr_Format(PyExc_TypeError, + "arg 5 (closure) expected cell, found %s", + o->ob_type->tp_name); + } + } + } + + newfunc = (PyFunctionObject *)PyFunction_New((PyObject *)code, + globals); + if (newfunc == NULL) + return NULL; + + if (name != Py_None) { + Py_INCREF(name); + Py_DECREF(newfunc->func_name); + newfunc->func_name = name; + } + if (defaults != Py_None) { + Py_INCREF(defaults); + newfunc->func_defaults = defaults; + } + if (closure != Py_None) { + Py_INCREF(closure); + newfunc->func_closure = closure; + } + + return (PyObject *)newfunc; +} + +static void +func_dealloc(PyFunctionObject *op) +{ + _PyObject_GC_UNTRACK(op); + if (op->func_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) op); + Py_DECREF(op->func_code); + Py_DECREF(op->func_globals); + Py_XDECREF(op->func_module); + Py_DECREF(op->func_name); + Py_XDECREF(op->func_defaults); + Py_XDECREF(op->func_doc); + Py_XDECREF(op->func_dict); + Py_XDECREF(op->func_closure); + PyObject_GC_Del(op); +} + +static PyObject* +func_repr(PyFunctionObject *op) +{ + if (op->func_name == Py_None) + return PyString_FromFormat("", op); + return PyString_FromFormat("", + PyString_AsString(op->func_name), + op); +} + +static int +func_traverse(PyFunctionObject *f, visitproc visit, void *arg) +{ + int err; + if (f->func_code) { + err = visit(f->func_code, arg); + if (err) + return err; + } + if (f->func_globals) { + err = visit(f->func_globals, arg); + if (err) + return err; + } + if (f->func_module) { + err = visit(f->func_module, arg); + if (err) + return err; + } + if (f->func_defaults) { + err = visit(f->func_defaults, arg); + if (err) + return err; + } + if (f->func_doc) { + err = visit(f->func_doc, arg); + if (err) + return err; + } + if (f->func_name) { + err = visit(f->func_name, arg); + if (err) + return err; + } + if (f->func_dict) { + err = visit(f->func_dict, arg); + if (err) + return err; + } + if (f->func_closure) { + err = visit(f->func_closure, arg); + if (err) + return err; + } + return 0; +} + +static PyObject * +function_call(PyObject *func, PyObject *arg, PyObject *kw) +{ + PyObject *result; + PyObject *argdefs; + PyObject **d, **k; + int nk, nd; + + argdefs = PyFunction_GET_DEFAULTS(func); + if (argdefs != NULL && PyTuple_Check(argdefs)) { + d = &PyTuple_GET_ITEM((PyTupleObject *)argdefs, 0); + nd = PyTuple_Size(argdefs); + } + else { + d = NULL; + nd = 0; + } + + if (kw != NULL && PyDict_Check(kw)) { + int pos, i; + nk = PyDict_Size(kw); + k = PyMem_NEW(PyObject *, 2*nk); + if (k == NULL) { + PyErr_NoMemory(); + return NULL; + } + pos = i = 0; + while (PyDict_Next(kw, &pos, &k[i], &k[i+1])) + i += 2; + nk = i/2; + /* XXX This is broken if the caller deletes dict items! */ + } + else { + k = NULL; + nk = 0; + } + + result = PyEval_EvalCodeEx( + (PyCodeObject *)PyFunction_GET_CODE(func), + PyFunction_GET_GLOBALS(func), (PyObject *)NULL, + &PyTuple_GET_ITEM(arg, 0), PyTuple_Size(arg), + k, nk, d, nd, + PyFunction_GET_CLOSURE(func)); + + if (k != NULL) + PyMem_DEL(k); + + return result; +} + +/* Bind a function to an object */ +static PyObject * +func_descr_get(PyObject *func, PyObject *obj, PyObject *type) +{ + if (obj == Py_None) + obj = NULL; + return PyMethod_New(func, obj, type); +} + +PyTypeObject PyFunction_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "function", + sizeof(PyFunctionObject), + 0, + (destructor)func_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)func_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + function_call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + func_doc, /* tp_doc */ + (traverseproc)func_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(PyFunctionObject, func_weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + func_memberlist, /* tp_members */ + func_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + func_descr_get, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof(PyFunctionObject, func_dict), /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + func_new, /* tp_new */ +}; + + +/* Class method object */ + +/* A class method receives the class as implicit first argument, + just like an instance method receives the instance. + To declare a class method, use this idiom: + + class C: + def f(cls, arg1, arg2, ...): ... + f = classmethod(f) + + It can be called either on the class (e.g. C.f()) or on an instance + (e.g. C().f()); the instance is ignored except for its class. + If a class method is called for a derived class, the derived class + object is passed as the implied first argument. + + Class methods are different than C++ or Java static methods. + If you want those, see static methods below. +*/ + +typedef struct { + PyObject_HEAD + PyObject *cm_callable; +} classmethod; + +static void +cm_dealloc(classmethod *cm) +{ + _PyObject_GC_UNTRACK((PyObject *)cm); + Py_XDECREF(cm->cm_callable); + cm->ob_type->tp_free((PyObject *)cm); +} + +static int +cm_traverse(classmethod *cm, visitproc visit, void *arg) +{ + if (!cm->cm_callable) + return 0; + return visit(cm->cm_callable, arg); +} + +static int +cm_clear(classmethod *cm) +{ + Py_XDECREF(cm->cm_callable); + cm->cm_callable = NULL; + + return 0; +} + + +static PyObject * +cm_descr_get(PyObject *self, PyObject *obj, PyObject *type) +{ + classmethod *cm = (classmethod *)self; + + if (cm->cm_callable == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "uninitialized classmethod object"); + return NULL; + } + if (type == NULL) + type = (PyObject *)(obj->ob_type); + return PyMethod_New(cm->cm_callable, + type, (PyObject *)(type->ob_type)); +} + +static int +cm_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + classmethod *cm = (classmethod *)self; + PyObject *callable; + + if (!PyArg_UnpackTuple(args, "classmethod", 1, 1, &callable)) + return -1; + if (!PyCallable_Check(callable)) { + PyErr_Format(PyExc_TypeError, "'%s' object is not callable", + callable->ob_type->tp_name); + return -1; + } + + Py_INCREF(callable); + cm->cm_callable = callable; + return 0; +} + +PyDoc_STRVAR(classmethod_doc, +"classmethod(function) -> method\n\ +\n\ +Convert a function to be a class method.\n\ +\n\ +A class method receives the class as implicit first argument,\n\ +just like an instance method receives the instance.\n\ +To declare a class method, use this idiom:\n\ +\n\ + class C:\n\ + def f(cls, arg1, arg2, ...): ...\n\ + f = classmethod(f)\n\ +\n\ +It can be called either on the class (e.g. C.f()) or on an instance\n\ +(e.g. C().f()). The instance is ignored except for its class.\n\ +If a class method is called for a derived class, the derived class\n\ +object is passed as the implied first argument.\n\ +\n\ +Class methods are different than C++ or Java static methods.\n\ +If you want those, see the staticmethod builtin."); + +PyTypeObject PyClassMethod_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "classmethod", + sizeof(classmethod), + 0, + (destructor)cm_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + classmethod_doc, /* tp_doc */ + (traverseproc)cm_traverse, /* tp_traverse */ + (inquiry)cm_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + cm_descr_get, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + cm_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + +PyObject * +PyClassMethod_New(PyObject *callable) +{ + classmethod *cm = (classmethod *) + PyType_GenericAlloc(&PyClassMethod_Type, 0); + if (cm != NULL) { + Py_INCREF(callable); + cm->cm_callable = callable; + } + return (PyObject *)cm; +} + + +/* Static method object */ + +/* A static method does not receive an implicit first argument. + To declare a static method, use this idiom: + + class C: + def f(arg1, arg2, ...): ... + f = staticmethod(f) + + It can be called either on the class (e.g. C.f()) or on an instance + (e.g. C().f()); the instance is ignored except for its class. + + Static methods in Python are similar to those found in Java or C++. + For a more advanced concept, see class methods above. +*/ + +typedef struct { + PyObject_HEAD + PyObject *sm_callable; +} staticmethod; + +static void +sm_dealloc(staticmethod *sm) +{ + _PyObject_GC_UNTRACK((PyObject *)sm); + Py_XDECREF(sm->sm_callable); + sm->ob_type->tp_free((PyObject *)sm); +} + +static int +sm_traverse(staticmethod *sm, visitproc visit, void *arg) +{ + if (!sm->sm_callable) + return 0; + return visit(sm->sm_callable, arg); +} + +static int +sm_clear(staticmethod *sm) +{ + Py_XDECREF(sm->sm_callable); + sm->sm_callable = NULL; + + return 0; +} + +static PyObject * +sm_descr_get(PyObject *self, PyObject *obj, PyObject *type) +{ + staticmethod *sm = (staticmethod *)self; + + if (sm->sm_callable == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "uninitialized staticmethod object"); + return NULL; + } + Py_INCREF(sm->sm_callable); + return sm->sm_callable; +} + +static int +sm_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + staticmethod *sm = (staticmethod *)self; + PyObject *callable; + + if (!PyArg_UnpackTuple(args, "staticmethod", 1, 1, &callable)) + return -1; + Py_INCREF(callable); + sm->sm_callable = callable; + return 0; +} + +PyDoc_STRVAR(staticmethod_doc, +"staticmethod(function) -> method\n\ +\n\ +Convert a function to be a static method.\n\ +\n\ +A static method does not receive an implicit first argument.\n\ +To declare a static method, use this idiom:\n\ +\n\ + class C:\n\ + def f(arg1, arg2, ...): ...\n\ + f = staticmethod(f)\n\ +\n\ +It can be called either on the class (e.g. C.f()) or on an instance\n\ +(e.g. C().f()). The instance is ignored except for its class.\n\ +\n\ +Static methods in Python are similar to those found in Java or C++.\n\ +For a more advanced concept, see the classmethod builtin."); + +PyTypeObject PyStaticMethod_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "staticmethod", + sizeof(staticmethod), + 0, + (destructor)sm_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + staticmethod_doc, /* tp_doc */ + (traverseproc)sm_traverse, /* tp_traverse */ + (inquiry)sm_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + sm_descr_get, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + sm_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + +PyObject * +PyStaticMethod_New(PyObject *callable) +{ + staticmethod *sm = (staticmethod *) + PyType_GenericAlloc(&PyStaticMethod_Type, 0); + if (sm != NULL) { + Py_INCREF(callable); + sm->sm_callable = callable; + } + return (PyObject *)sm; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/intobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/intobject.c new file mode 100644 index 00000000..3d0957fe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/intobject.c @@ -0,0 +1,1188 @@ + +/* Integer object implementation */ + +#include "Python.h" +#include + +long +PyInt_GetMax(void) +{ + return LONG_MAX; /* To initialize sys.maxint */ +} + +/* Return 1 if exception raised, 0 if caller should retry using longs */ +static int +err_ovf(char *msg) +{ + if (PyErr_Warn(PyExc_OverflowWarning, msg) < 0) { + if (PyErr_ExceptionMatches(PyExc_OverflowWarning)) + PyErr_SetString(PyExc_OverflowError, msg); + return 1; + } + else + return 0; +} + +/* Integers are quite normal objects, to make object handling uniform. + (Using odd pointers to represent integers would save much space + but require extra checks for this special case throughout the code.) + Since a typical Python program spends much of its time allocating + and deallocating integers, these operations should be very fast. + Therefore we use a dedicated allocation scheme with a much lower + overhead (in space and time) than straight malloc(): a simple + dedicated free list, filled when necessary with memory from malloc(). + + block_list is a singly-linked list of all PyIntBlocks ever allocated, + linked via their next members. PyIntBlocks are never returned to the + system before shutdown (PyInt_Fini). + + free_list is a singly-linked list of available PyIntObjects, linked + via abuse of their ob_type members. +*/ + +#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */ +#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */ +#define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject)) + +struct _intblock { + struct _intblock *next; + PyIntObject objects[N_INTOBJECTS]; +}; + +typedef struct _intblock PyIntBlock; + +static PyIntBlock *block_list = NULL; +static PyIntObject *free_list = NULL; + +static PyIntObject * +fill_free_list(void) +{ + PyIntObject *p, *q; + /* Python's object allocator isn't appropriate for large blocks. */ + p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock)); + if (p == NULL) + return (PyIntObject *) PyErr_NoMemory(); + ((PyIntBlock *)p)->next = block_list; + block_list = (PyIntBlock *)p; + /* Link the int objects together, from rear to front, then return + the address of the last int object in the block. */ + p = &((PyIntBlock *)p)->objects[0]; + q = p + N_INTOBJECTS; + while (--q > p) + q->ob_type = (struct _typeobject *)(q-1); + q->ob_type = NULL; + return p + N_INTOBJECTS - 1; +} + +#ifndef NSMALLPOSINTS +#define NSMALLPOSINTS 100 +#endif +#ifndef NSMALLNEGINTS +#define NSMALLNEGINTS 5 +#endif +#if NSMALLNEGINTS + NSMALLPOSINTS > 0 +/* References to small integers are saved in this array so that they + can be shared. + The integers that are saved are those in the range + -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive). +*/ +static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; +#endif +#ifdef COUNT_ALLOCS +int quick_int_allocs, quick_neg_int_allocs; +#endif + +PyObject * +PyInt_FromLong(long ival) +{ + register PyIntObject *v; +#if NSMALLNEGINTS + NSMALLPOSINTS > 0 + if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { + v = small_ints[ival + NSMALLNEGINTS]; + Py_INCREF(v); +#ifdef COUNT_ALLOCS + if (ival >= 0) + quick_int_allocs++; + else + quick_neg_int_allocs++; +#endif + return (PyObject *) v; + } +#endif + if (free_list == NULL) { + if ((free_list = fill_free_list()) == NULL) + return NULL; + } + /* Inline PyObject_New */ + v = free_list; + free_list = (PyIntObject *)v->ob_type; + PyObject_INIT(v, &PyInt_Type); + v->ob_ival = ival; + return (PyObject *) v; +} + +static void +int_dealloc(PyIntObject *v) +{ + if (PyInt_CheckExact(v)) { + v->ob_type = (struct _typeobject *)free_list; + free_list = v; + } + else + v->ob_type->tp_free((PyObject *)v); +} + +static void +int_free(PyIntObject *v) +{ + v->ob_type = (struct _typeobject *)free_list; + free_list = v; +} + +long +PyInt_AsLong(register PyObject *op) +{ + PyNumberMethods *nb; + PyIntObject *io; + long val; + + if (op && PyInt_Check(op)) + return PyInt_AS_LONG((PyIntObject*) op); + + if (op == NULL || (nb = op->ob_type->tp_as_number) == NULL || + nb->nb_int == NULL) { + PyErr_SetString(PyExc_TypeError, "an integer is required"); + return -1; + } + + io = (PyIntObject*) (*nb->nb_int) (op); + if (io == NULL) + return -1; + if (!PyInt_Check(io)) { + if (PyLong_Check(io)) { + /* got a long? => retry int conversion */ + val = PyLong_AsLong((PyObject *)io); + Py_DECREF(io); + if ((val == -1) && PyErr_Occurred()) + return -1; + return val; + } + else + { + Py_DECREF(io); + PyErr_SetString(PyExc_TypeError, + "nb_int should return int object"); + return -1; + } + } + + val = PyInt_AS_LONG(io); + Py_DECREF(io); + + return val; +} + +unsigned long +PyInt_AsUnsignedLongMask(register PyObject *op) +{ + PyNumberMethods *nb; + PyIntObject *io; + unsigned long val; + + if (op && PyInt_Check(op)) + return PyInt_AS_LONG((PyIntObject*) op); + if (op && PyLong_Check(op)) + return PyLong_AsUnsignedLongMask(op); + + if (op == NULL || (nb = op->ob_type->tp_as_number) == NULL || + nb->nb_int == NULL) { + PyErr_SetString(PyExc_TypeError, "an integer is required"); + return -1; + } + + io = (PyIntObject*) (*nb->nb_int) (op); + if (io == NULL) + return -1; + if (!PyInt_Check(io)) { + if (PyLong_Check(io)) { + val = PyLong_AsUnsignedLongMask((PyObject *)io); + Py_DECREF(io); + if (PyErr_Occurred()) + return -1; + return val; + } + else + { + Py_DECREF(io); + PyErr_SetString(PyExc_TypeError, + "nb_int should return int object"); + return -1; + } + } + + val = PyInt_AS_LONG(io); + Py_DECREF(io); + + return val; +} + +#ifdef HAVE_LONG_LONG +unsigned PY_LONG_LONG +PyInt_AsUnsignedLongLongMask(register PyObject *op) +{ + PyNumberMethods *nb; + PyIntObject *io; + unsigned PY_LONG_LONG val; + + if (op && PyInt_Check(op)) + return PyInt_AS_LONG((PyIntObject*) op); + if (op && PyLong_Check(op)) + return PyLong_AsUnsignedLongLongMask(op); + + if (op == NULL || (nb = op->ob_type->tp_as_number) == NULL || + nb->nb_int == NULL) { + PyErr_SetString(PyExc_TypeError, "an integer is required"); + return -1; + } + + io = (PyIntObject*) (*nb->nb_int) (op); + if (io == NULL) + return -1; + if (!PyInt_Check(io)) { + if (PyLong_Check(io)) { + val = PyLong_AsUnsignedLongLongMask((PyObject *)io); + Py_DECREF(io); + if (PyErr_Occurred()) + return -1; + return val; + } + else + { + Py_DECREF(io); + PyErr_SetString(PyExc_TypeError, + "nb_int should return int object"); + return -1; + } + } + + val = PyInt_AS_LONG(io); + Py_DECREF(io); + + return val; +} +#endif + +PyObject * +PyInt_FromString(char *s, char **pend, int base) +{ + char *end; + long x; + char buffer[256]; /* For errors */ + int warn = 0; + + if ((base != 0 && base < 2) || base > 36) { + PyErr_SetString(PyExc_ValueError, + "int() base must be >= 2 and <= 36"); + return NULL; + } + + while (*s && isspace(Py_CHARMASK(*s))) + s++; + errno = 0; + if (base == 0 && s[0] == '0') { + x = (long) PyOS_strtoul(s, &end, base); + if (x < 0) + warn = 1; + } + else + x = PyOS_strtol(s, &end, base); + if (end == s || !isalnum(Py_CHARMASK(end[-1]))) + goto bad; + while (*end && isspace(Py_CHARMASK(*end))) + end++; + if (*end != '\0') { + bad: + PyOS_snprintf(buffer, sizeof(buffer), + "invalid literal for int(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + return NULL; + } + else if (errno != 0) { + if (err_ovf("string/unicode conversion")) + return NULL; + return PyLong_FromString(s, pend, base); + } + if (warn) { + if (PyErr_Warn(PyExc_FutureWarning, + "int('0...', 0): sign will change in Python 2.4") < 0) + return NULL; + } + if (pend) + *pend = end; + return PyInt_FromLong(x); +} + +#ifdef Py_USING_UNICODE +PyObject * +PyInt_FromUnicode(Py_UNICODE *s, int length, int base) +{ + PyObject *result; + char *buffer = PyMem_MALLOC(length+1); + + if (buffer == NULL) + return NULL; + + if (PyUnicode_EncodeDecimal(s, length, buffer, NULL)) { + PyMem_FREE(buffer); + return NULL; + } + result = PyInt_FromString(buffer, NULL, base); + PyMem_FREE(buffer); + return result; +} +#endif + +/* Methods */ + +/* Integers are seen as the "smallest" of all numeric types and thus + don't have any knowledge about conversion of other types to + integers. */ + +#define CONVERT_TO_LONG(obj, lng) \ + if (PyInt_Check(obj)) { \ + lng = PyInt_AS_LONG(obj); \ + } \ + else { \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ + } + +/* ARGSUSED */ +static int +int_print(PyIntObject *v, FILE *fp, int flags) + /* flags -- not used but required by interface */ +{ + fprintf(fp, "%ld", v->ob_ival); + return 0; +} + +static PyObject * +int_repr(PyIntObject *v) +{ + char buf[64]; + PyOS_snprintf(buf, sizeof(buf), "%ld", v->ob_ival); + return PyString_FromString(buf); +} + +static int +int_compare(PyIntObject *v, PyIntObject *w) +{ + register long i = v->ob_ival; + register long j = w->ob_ival; + return (i < j) ? -1 : (i > j) ? 1 : 0; +} + +static long +int_hash(PyIntObject *v) +{ + /* XXX If this is changed, you also need to change the way + Python's long, float and complex types are hashed. */ + long x = v -> ob_ival; + if (x == -1) + x = -2; + return x; +} + +static PyObject * +int_add(PyIntObject *v, PyIntObject *w) +{ + register long a, b, x; + CONVERT_TO_LONG(v, a); + CONVERT_TO_LONG(w, b); + x = a + b; + if ((x^a) >= 0 || (x^b) >= 0) + return PyInt_FromLong(x); + if (err_ovf("integer addition")) + return NULL; + return PyLong_Type.tp_as_number->nb_add((PyObject *)v, (PyObject *)w); +} + +static PyObject * +int_sub(PyIntObject *v, PyIntObject *w) +{ + register long a, b, x; + CONVERT_TO_LONG(v, a); + CONVERT_TO_LONG(w, b); + x = a - b; + if ((x^a) >= 0 || (x^~b) >= 0) + return PyInt_FromLong(x); + if (err_ovf("integer subtraction")) + return NULL; + return PyLong_Type.tp_as_number->nb_subtract((PyObject *)v, + (PyObject *)w); +} + +/* +Integer overflow checking for * is painful: Python tried a couple ways, but +they didn't work on all platforms, or failed in endcases (a product of +-sys.maxint-1 has been a particular pain). + +Here's another way: + +The native long product x*y is either exactly right or *way* off, being +just the last n bits of the true product, where n is the number of bits +in a long (the delivered product is the true product plus i*2**n for +some integer i). + +The native double product (double)x * (double)y is subject to three +rounding errors: on a sizeof(long)==8 box, each cast to double can lose +info, and even on a sizeof(long)==4 box, the multiplication can lose info. +But, unlike the native long product, it's not in *range* trouble: even +if sizeof(long)==32 (256-bit longs), the product easily fits in the +dynamic range of a double. So the leading 50 (or so) bits of the double +product are correct. + +We check these two ways against each other, and declare victory if they're +approximately the same. Else, because the native long product is the only +one that can lose catastrophic amounts of information, it's the native long +product that must have overflowed. +*/ + +static PyObject * +int_mul(PyObject *v, PyObject *w) +{ + long a, b; + long longprod; /* a*b in native long arithmetic */ + double doubled_longprod; /* (double)longprod */ + double doubleprod; /* (double)a * (double)b */ + + CONVERT_TO_LONG(v, a); + CONVERT_TO_LONG(w, b); + longprod = a * b; + doubleprod = (double)a * (double)b; + doubled_longprod = (double)longprod; + + /* Fast path for normal case: small multiplicands, and no info + is lost in either method. */ + if (doubled_longprod == doubleprod) + return PyInt_FromLong(longprod); + + /* Somebody somewhere lost info. Close enough, or way off? Note + that a != 0 and b != 0 (else doubled_longprod == doubleprod == 0). + The difference either is or isn't significant compared to the + true value (of which doubleprod is a good approximation). + */ + { + const double diff = doubled_longprod - doubleprod; + const double absdiff = diff >= 0.0 ? diff : -diff; + const double absprod = doubleprod >= 0.0 ? doubleprod : + -doubleprod; + /* absdiff/absprod <= 1/32 iff + 32 * absdiff <= absprod -- 5 good bits is "close enough" */ + if (32.0 * absdiff <= absprod) + return PyInt_FromLong(longprod); + else if (err_ovf("integer multiplication")) + return NULL; + else + return PyLong_Type.tp_as_number->nb_multiply(v, w); + } +} + +/* Return type of i_divmod */ +enum divmod_result { + DIVMOD_OK, /* Correct result */ + DIVMOD_OVERFLOW, /* Overflow, try again using longs */ + DIVMOD_ERROR /* Exception raised */ +}; + +static enum divmod_result +i_divmod(register long x, register long y, + long *p_xdivy, long *p_xmody) +{ + long xdivy, xmody; + + if (y == 0) { + PyErr_SetString(PyExc_ZeroDivisionError, + "integer division or modulo by zero"); + return DIVMOD_ERROR; + } + /* (-sys.maxint-1)/-1 is the only overflow case. */ + if (y == -1 && x < 0 && x == -x) { + if (err_ovf("integer division")) + return DIVMOD_ERROR; + return DIVMOD_OVERFLOW; + } + xdivy = x / y; + xmody = x - xdivy * y; + /* If the signs of x and y differ, and the remainder is non-0, + * C89 doesn't define whether xdivy is now the floor or the + * ceiling of the infinitely precise quotient. We want the floor, + * and we have it iff the remainder's sign matches y's. + */ + if (xmody && ((y ^ xmody) < 0) /* i.e. and signs differ */) { + xmody += y; + --xdivy; + assert(xmody && ((y ^ xmody) >= 0)); + } + *p_xdivy = xdivy; + *p_xmody = xmody; + return DIVMOD_OK; +} + +static PyObject * +int_div(PyIntObject *x, PyIntObject *y) +{ + long xi, yi; + long d, m; + CONVERT_TO_LONG(x, xi); + CONVERT_TO_LONG(y, yi); + switch (i_divmod(xi, yi, &d, &m)) { + case DIVMOD_OK: + return PyInt_FromLong(d); + case DIVMOD_OVERFLOW: + return PyLong_Type.tp_as_number->nb_divide((PyObject *)x, + (PyObject *)y); + default: + return NULL; + } +} + +static PyObject * +int_classic_div(PyIntObject *x, PyIntObject *y) +{ + long xi, yi; + long d, m; + CONVERT_TO_LONG(x, xi); + CONVERT_TO_LONG(y, yi); + if (Py_DivisionWarningFlag && + PyErr_Warn(PyExc_DeprecationWarning, "classic int division") < 0) + return NULL; + switch (i_divmod(xi, yi, &d, &m)) { + case DIVMOD_OK: + return PyInt_FromLong(d); + case DIVMOD_OVERFLOW: + return PyLong_Type.tp_as_number->nb_divide((PyObject *)x, + (PyObject *)y); + default: + return NULL; + } +} + +static PyObject * +int_true_divide(PyObject *v, PyObject *w) +{ + /* If they aren't both ints, give someone else a chance. In + particular, this lets int/long get handled by longs, which + underflows to 0 gracefully if the long is too big to convert + to float. */ + if (PyInt_Check(v) && PyInt_Check(w)) + return PyFloat_Type.tp_as_number->nb_true_divide(v, w); + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static PyObject * +int_mod(PyIntObject *x, PyIntObject *y) +{ + long xi, yi; + long d, m; + CONVERT_TO_LONG(x, xi); + CONVERT_TO_LONG(y, yi); + switch (i_divmod(xi, yi, &d, &m)) { + case DIVMOD_OK: + return PyInt_FromLong(m); + case DIVMOD_OVERFLOW: + return PyLong_Type.tp_as_number->nb_remainder((PyObject *)x, + (PyObject *)y); + default: + return NULL; + } +} + +static PyObject * +int_divmod(PyIntObject *x, PyIntObject *y) +{ + long xi, yi; + long d, m; + CONVERT_TO_LONG(x, xi); + CONVERT_TO_LONG(y, yi); + switch (i_divmod(xi, yi, &d, &m)) { + case DIVMOD_OK: + return Py_BuildValue("(ll)", d, m); + case DIVMOD_OVERFLOW: + return PyLong_Type.tp_as_number->nb_divmod((PyObject *)x, + (PyObject *)y); + default: + return NULL; + } +} + +static PyObject * +int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z) +{ + register long iv, iw, iz=0, ix, temp, prev; + CONVERT_TO_LONG(v, iv); + CONVERT_TO_LONG(w, iw); + if (iw < 0) { + if ((PyObject *)z != Py_None) { + PyErr_SetString(PyExc_TypeError, "pow() 2nd argument " + "cannot be negative when 3rd argument specified"); + return NULL; + } + /* Return a float. This works because we know that + this calls float_pow() which converts its + arguments to double. */ + return PyFloat_Type.tp_as_number->nb_power( + (PyObject *)v, (PyObject *)w, (PyObject *)z); + } + if ((PyObject *)z != Py_None) { + CONVERT_TO_LONG(z, iz); + if (iz == 0) { + PyErr_SetString(PyExc_ValueError, + "pow() 3rd argument cannot be 0"); + return NULL; + } + } + /* + * XXX: The original exponentiation code stopped looping + * when temp hit zero; this code will continue onwards + * unnecessarily, but at least it won't cause any errors. + * Hopefully the speed improvement from the fast exponentiation + * will compensate for the slight inefficiency. + * XXX: Better handling of overflows is desperately needed. + */ + temp = iv; + ix = 1; + while (iw > 0) { + prev = ix; /* Save value for overflow check */ + if (iw & 1) { + ix = ix*temp; + if (temp == 0) + break; /* Avoid ix / 0 */ + if (ix / temp != prev) { + if (err_ovf("integer exponentiation")) + return NULL; + return PyLong_Type.tp_as_number->nb_power( + (PyObject *)v, + (PyObject *)w, + (PyObject *)z); + } + } + iw >>= 1; /* Shift exponent down by 1 bit */ + if (iw==0) break; + prev = temp; + temp *= temp; /* Square the value of temp */ + if (prev!=0 && temp/prev!=prev) { + if (err_ovf("integer exponentiation")) + return NULL; + return PyLong_Type.tp_as_number->nb_power( + (PyObject *)v, (PyObject *)w, (PyObject *)z); + } + if (iz) { + /* If we did a multiplication, perform a modulo */ + ix = ix % iz; + temp = temp % iz; + } + } + if (iz) { + long div, mod; + switch (i_divmod(ix, iz, &div, &mod)) { + case DIVMOD_OK: + ix = mod; + break; + case DIVMOD_OVERFLOW: + return PyLong_Type.tp_as_number->nb_power( + (PyObject *)v, (PyObject *)w, (PyObject *)z); + default: + return NULL; + } + } + return PyInt_FromLong(ix); +} + +static PyObject * +int_neg(PyIntObject *v) +{ + register long a, x; + a = v->ob_ival; + x = -a; + if (a < 0 && x < 0) { + PyObject *o; + if (err_ovf("integer negation")) + return NULL; + o = PyLong_FromLong(a); + if (o != NULL) { + PyObject *result = PyNumber_Negative(o); + Py_DECREF(o); + return result; + } + return NULL; + } + return PyInt_FromLong(x); +} + +static PyObject * +int_pos(PyIntObject *v) +{ + if (PyInt_CheckExact(v)) { + Py_INCREF(v); + return (PyObject *)v; + } + else + return PyInt_FromLong(v->ob_ival); +} + +static PyObject * +int_abs(PyIntObject *v) +{ + if (v->ob_ival >= 0) + return int_pos(v); + else + return int_neg(v); +} + +static int +int_nonzero(PyIntObject *v) +{ + return v->ob_ival != 0; +} + +static PyObject * +int_invert(PyIntObject *v) +{ + return PyInt_FromLong(~v->ob_ival); +} + +static PyObject * +int_lshift(PyIntObject *v, PyIntObject *w) +{ + long a, b, c; + CONVERT_TO_LONG(v, a); + CONVERT_TO_LONG(w, b); + if (b < 0) { + PyErr_SetString(PyExc_ValueError, "negative shift count"); + return NULL; + } + if (a == 0 || b == 0) + return int_pos(v); + if (b >= LONG_BIT) { + if (PyErr_Warn(PyExc_FutureWarning, + "x<= LONG_BIT) { + if (a < 0) + a = -1; + else + a = 0; + } + else { + a = Py_ARITHMETIC_RIGHT_SHIFT(long, a, b); + } + return PyInt_FromLong(a); +} + +static PyObject * +int_and(PyIntObject *v, PyIntObject *w) +{ + register long a, b; + CONVERT_TO_LONG(v, a); + CONVERT_TO_LONG(w, b); + return PyInt_FromLong(a & b); +} + +static PyObject * +int_xor(PyIntObject *v, PyIntObject *w) +{ + register long a, b; + CONVERT_TO_LONG(v, a); + CONVERT_TO_LONG(w, b); + return PyInt_FromLong(a ^ b); +} + +static PyObject * +int_or(PyIntObject *v, PyIntObject *w) +{ + register long a, b; + CONVERT_TO_LONG(v, a); + CONVERT_TO_LONG(w, b); + return PyInt_FromLong(a | b); +} + +static int +int_coerce(PyObject **pv, PyObject **pw) +{ + if (PyInt_Check(*pw)) { + Py_INCREF(*pv); + Py_INCREF(*pw); + return 0; + } + return 1; /* Can't do it */ +} + +static PyObject * +int_int(PyIntObject *v) +{ + Py_INCREF(v); + return (PyObject *)v; +} + +static PyObject * +int_long(PyIntObject *v) +{ + return PyLong_FromLong((v -> ob_ival)); +} + +static PyObject * +int_float(PyIntObject *v) +{ + return PyFloat_FromDouble((double)(v -> ob_ival)); +} + +static PyObject * +int_oct(PyIntObject *v) +{ + char buf[100]; + long x = v -> ob_ival; + if (x < 0) { + if (PyErr_Warn(PyExc_FutureWarning, + "hex()/oct() of negative int will return " + "a signed string in Python 2.4 and up") < 0) + return NULL; + } + if (x == 0) + strcpy(buf, "0"); + else + PyOS_snprintf(buf, sizeof(buf), "0%lo", x); + return PyString_FromString(buf); +} + +static PyObject * +int_hex(PyIntObject *v) +{ + char buf[100]; + long x = v -> ob_ival; + if (x < 0) { + if (PyErr_Warn(PyExc_FutureWarning, + "hex()/oct() of negative int will return " + "a signed string in Python 2.4 and up") < 0) + return NULL; + } + PyOS_snprintf(buf, sizeof(buf), "0x%lx", x); + return PyString_FromString(buf); +} + +static PyObject * +int_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + +static PyObject * +int_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *x = NULL; + int base = -909; + static char *kwlist[] = {"x", "base", 0}; + + if (type != &PyInt_Type) + return int_subtype_new(type, args, kwds); /* Wimp out */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:int", kwlist, + &x, &base)) + return NULL; + if (x == NULL) + return PyInt_FromLong(0L); + if (base == -909) + return PyNumber_Int(x); + if (PyString_Check(x)) + return PyInt_FromString(PyString_AS_STRING(x), NULL, base); +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(x)) + return PyInt_FromUnicode(PyUnicode_AS_UNICODE(x), + PyUnicode_GET_SIZE(x), + base); +#endif + PyErr_SetString(PyExc_TypeError, + "int() can't convert non-string with explicit base"); + return NULL; +} + +/* Wimpy, slow approach to tp_new calls for subtypes of int: + first create a regular int from whatever arguments we got, + then allocate a subtype instance and initialize its ob_ival + from the regular int. The regular int is then thrown away. +*/ +static PyObject * +int_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *tmp, *new; + long ival; + + assert(PyType_IsSubtype(type, &PyInt_Type)); + tmp = int_new(&PyInt_Type, args, kwds); + if (tmp == NULL) + return NULL; + if (!PyInt_Check(tmp)) { + if (!PyLong_Check(tmp)) { + PyErr_SetString(PyExc_ValueError, + "value must convertable to an int"); + return NULL; + } + ival = PyLong_AsLong(tmp); + if (ival == -1 && PyErr_Occurred()) + return NULL; + + } else { + ival = ((PyIntObject *)tmp)->ob_ival; + } + + new = type->tp_alloc(type, 0); + if (new == NULL) { + Py_DECREF(tmp); + return NULL; + } + ((PyIntObject *)new)->ob_ival = ival; + Py_DECREF(tmp); + return new; +} + +static PyObject * +int_getnewargs(PyIntObject *v) +{ + return Py_BuildValue("(l)", v->ob_ival); +} + +static PyMethodDef int_methods[] = { + {"__getnewargs__", (PyCFunction)int_getnewargs, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +PyDoc_STRVAR(int_doc, +"int(x[, base]) -> integer\n\ +\n\ +Convert a string or number to an integer, if possible. A floating point\n\ +argument will be truncated towards zero (this does not include a string\n\ +representation of a floating point number!) When converting a string, use\n\ +the optional base. It is an error to supply a base when converting a\n\ +non-string. If the argument is outside the integer range a long object\n\ +will be returned instead."); + +static PyNumberMethods int_as_number = { + (binaryfunc)int_add, /*nb_add*/ + (binaryfunc)int_sub, /*nb_subtract*/ + (binaryfunc)int_mul, /*nb_multiply*/ + (binaryfunc)int_classic_div, /*nb_divide*/ + (binaryfunc)int_mod, /*nb_remainder*/ + (binaryfunc)int_divmod, /*nb_divmod*/ + (ternaryfunc)int_pow, /*nb_power*/ + (unaryfunc)int_neg, /*nb_negative*/ + (unaryfunc)int_pos, /*nb_positive*/ + (unaryfunc)int_abs, /*nb_absolute*/ + (inquiry)int_nonzero, /*nb_nonzero*/ + (unaryfunc)int_invert, /*nb_invert*/ + (binaryfunc)int_lshift, /*nb_lshift*/ + (binaryfunc)int_rshift, /*nb_rshift*/ + (binaryfunc)int_and, /*nb_and*/ + (binaryfunc)int_xor, /*nb_xor*/ + (binaryfunc)int_or, /*nb_or*/ + int_coerce, /*nb_coerce*/ + (unaryfunc)int_int, /*nb_int*/ + (unaryfunc)int_long, /*nb_long*/ + (unaryfunc)int_float, /*nb_float*/ + (unaryfunc)int_oct, /*nb_oct*/ + (unaryfunc)int_hex, /*nb_hex*/ + 0, /*nb_inplace_add*/ + 0, /*nb_inplace_subtract*/ + 0, /*nb_inplace_multiply*/ + 0, /*nb_inplace_divide*/ + 0, /*nb_inplace_remainder*/ + 0, /*nb_inplace_power*/ + 0, /*nb_inplace_lshift*/ + 0, /*nb_inplace_rshift*/ + 0, /*nb_inplace_and*/ + 0, /*nb_inplace_xor*/ + 0, /*nb_inplace_or*/ + (binaryfunc)int_div, /* nb_floor_divide */ + int_true_divide, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +}; + +PyTypeObject PyInt_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "int", + sizeof(PyIntObject), + 0, + (destructor)int_dealloc, /* tp_dealloc */ + (printfunc)int_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)int_compare, /* tp_compare */ + (reprfunc)int_repr, /* tp_repr */ + &int_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)int_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)int_repr, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + int_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + int_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + int_new, /* tp_new */ + (freefunc)int_free, /* tp_free */ +}; + +int +_PyInt_Init(void) +{ + PyIntObject *v; + int ival; +#if NSMALLNEGINTS + NSMALLPOSINTS > 0 + for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++) { + if ((free_list = fill_free_list()) == NULL) + return 0; + /* PyObject_New is inlined */ + v = free_list; + free_list = (PyIntObject *)v->ob_type; + PyObject_INIT(v, &PyInt_Type); + v->ob_ival = ival; + small_ints[ival + NSMALLNEGINTS] = v; + } +#endif + return 1; +} + +void +PyInt_Fini(void) +{ + PyIntObject *p; + PyIntBlock *list, *next; + int i; + int bc, bf; /* block count, number of freed blocks */ + int irem, isum; /* remaining unfreed ints per block, total */ + +#if NSMALLNEGINTS + NSMALLPOSINTS > 0 + PyIntObject **q; + + i = NSMALLNEGINTS + NSMALLPOSINTS; + q = small_ints; + while (--i >= 0) { + Py_XDECREF(*q); + *q++ = NULL; + } +#endif + bc = 0; + bf = 0; + isum = 0; + list = block_list; + block_list = NULL; + free_list = NULL; + while (list != NULL) { + bc++; + irem = 0; + for (i = 0, p = &list->objects[0]; + i < N_INTOBJECTS; + i++, p++) { + if (PyInt_CheckExact(p) && p->ob_refcnt != 0) + irem++; + } + next = list->next; + if (irem) { + list->next = block_list; + block_list = list; + for (i = 0, p = &list->objects[0]; + i < N_INTOBJECTS; + i++, p++) { + if (!PyInt_CheckExact(p) || + p->ob_refcnt == 0) { + p->ob_type = (struct _typeobject *) + free_list; + free_list = p; + } +#if NSMALLNEGINTS + NSMALLPOSINTS > 0 + else if (-NSMALLNEGINTS <= p->ob_ival && + p->ob_ival < NSMALLPOSINTS && + small_ints[p->ob_ival + + NSMALLNEGINTS] == NULL) { + Py_INCREF(p); + small_ints[p->ob_ival + + NSMALLNEGINTS] = p; + } +#endif + } + } + else { + PyMem_FREE(list); + bf++; + } + isum += irem; + list = next; + } + if (!Py_VerboseFlag) + return; + fprintf(stderr, "# cleanup ints"); + if (!isum) { + fprintf(stderr, "\n"); + } + else { + fprintf(stderr, + ": %d unfreed int%s in %d out of %d block%s\n", + isum, isum == 1 ? "" : "s", + bc - bf, bc, bc == 1 ? "" : "s"); + } + if (Py_VerboseFlag > 1) { + list = block_list; + while (list != NULL) { + for (i = 0, p = &list->objects[0]; + i < N_INTOBJECTS; + i++, p++) { + if (PyInt_CheckExact(p) && p->ob_refcnt != 0) + fprintf(stderr, + "# \n", + p, p->ob_refcnt, p->ob_ival); + } + list = list->next; + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/iterobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/iterobject.c new file mode 100644 index 00000000..61fa6375 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/iterobject.c @@ -0,0 +1,228 @@ +/* Iterator objects */ + +#include "Python.h" + +typedef struct { + PyObject_HEAD + long it_index; + PyObject *it_seq; /* Set to NULL when iterator is exhausted */ +} seqiterobject; + +PyObject * +PySeqIter_New(PyObject *seq) +{ + seqiterobject *it; + + if (!PySequence_Check(seq)) { + PyErr_BadInternalCall(); + return NULL; + } + it = PyObject_GC_New(seqiterobject, &PySeqIter_Type); + if (it == NULL) + return NULL; + it->it_index = 0; + Py_INCREF(seq); + it->it_seq = seq; + _PyObject_GC_TRACK(it); + return (PyObject *)it; +} + +static void +iter_dealloc(seqiterobject *it) +{ + _PyObject_GC_UNTRACK(it); + Py_XDECREF(it->it_seq); + PyObject_GC_Del(it); +} + +static int +iter_traverse(seqiterobject *it, visitproc visit, void *arg) +{ + if (it->it_seq == NULL) + return 0; + return visit(it->it_seq, arg); +} + +static PyObject * +iter_iternext(PyObject *iterator) +{ + seqiterobject *it; + PyObject *seq; + PyObject *result; + + assert(PySeqIter_Check(iterator)); + it = (seqiterobject *)iterator; + seq = it->it_seq; + if (seq == NULL) + return NULL; + + result = PySequence_GetItem(seq, it->it_index); + if (result != NULL) { + it->it_index++; + return result; + } + if (PyErr_ExceptionMatches(PyExc_IndexError) || + PyErr_ExceptionMatches(PyExc_StopIteration)) + { + PyErr_Clear(); + Py_DECREF(seq); + it->it_seq = NULL; + } + return NULL; +} + +PyTypeObject PySeqIter_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "iterator", /* tp_name */ + sizeof(seqiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)iter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)iter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)iter_iternext, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; + +/* -------------------------------------- */ + +typedef struct { + PyObject_HEAD + PyObject *it_callable; /* Set to NULL when iterator is exhausted */ + PyObject *it_sentinel; /* Set to NULL when iterator is exhausted */ +} calliterobject; + +PyObject * +PyCallIter_New(PyObject *callable, PyObject *sentinel) +{ + calliterobject *it; + it = PyObject_GC_New(calliterobject, &PyCallIter_Type); + if (it == NULL) + return NULL; + Py_INCREF(callable); + it->it_callable = callable; + Py_INCREF(sentinel); + it->it_sentinel = sentinel; + _PyObject_GC_TRACK(it); + return (PyObject *)it; +} +static void +calliter_dealloc(calliterobject *it) +{ + _PyObject_GC_UNTRACK(it); + Py_XDECREF(it->it_callable); + Py_XDECREF(it->it_sentinel); + PyObject_GC_Del(it); +} + +static int +calliter_traverse(calliterobject *it, visitproc visit, void *arg) +{ + int err; + if (it->it_callable != NULL && (err = visit(it->it_callable, arg))) + return err; + if (it->it_sentinel != NULL && (err = visit(it->it_sentinel, arg))) + return err; + return 0; +} + +static PyObject * +calliter_iternext(calliterobject *it) +{ + if (it->it_callable != NULL) { + PyObject *args = PyTuple_New(0); + PyObject *result; + if (args == NULL) + return NULL; + result = PyObject_Call(it->it_callable, args, NULL); + Py_DECREF(args); + if (result != NULL) { + int ok; + ok = PyObject_RichCompareBool(result, + it->it_sentinel, + Py_EQ); + if (ok == 0) + return result; /* Common case, fast path */ + Py_DECREF(result); + if (ok > 0) { + Py_DECREF(it->it_callable); + it->it_callable = NULL; + Py_DECREF(it->it_sentinel); + it->it_sentinel = NULL; + } + } + else if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + PyErr_Clear(); + Py_DECREF(it->it_callable); + it->it_callable = NULL; + Py_DECREF(it->it_sentinel); + it->it_sentinel = NULL; + } + } + return NULL; +} + +PyTypeObject PyCallIter_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "callable-iterator", /* tp_name */ + sizeof(calliterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)calliter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)calliter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)calliter_iternext, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/listobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/listobject.c new file mode 100644 index 00000000..00d8b4c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/listobject.c @@ -0,0 +1,2501 @@ +/* List object implementation */ + +#include "Python.h" + +#ifdef STDC_HEADERS +#include +#else +#include /* For size_t */ +#endif + +static int +roundupsize(int n) +{ + unsigned int nbits = 0; + unsigned int n2 = (unsigned int)n >> 5; + + /* Round up: + * If n < 256, to a multiple of 8. + * If n < 2048, to a multiple of 64. + * If n < 16384, to a multiple of 512. + * If n < 131072, to a multiple of 4096. + * If n < 1048576, to a multiple of 32768. + * If n < 8388608, to a multiple of 262144. + * If n < 67108864, to a multiple of 2097152. + * If n < 536870912, to a multiple of 16777216. + * ... + * If n < 2**(5+3*i), to a multiple of 2**(3*i). + * + * This over-allocates proportional to the list size, making room + * for additional growth. The over-allocation is mild, but is + * enough to give linear-time amortized behavior over a long + * sequence of appends() in the presence of a poorly-performing + * system realloc() (which is a reality, e.g., across all flavors + * of Windows, with Win9x behavior being particularly bad -- and + * we've still got address space fragmentation problems on Win9x + * even with this scheme, although it requires much longer lists to + * provoke them than it used to). + */ + do { + n2 >>= 3; + nbits += 3; + } while (n2); + return ((n >> nbits) + 1) << nbits; + } + +#define NRESIZE(var, type, nitems) \ +do { \ + size_t _new_size = roundupsize(nitems); \ + if (_new_size <= ((~(size_t)0) / sizeof(type))) \ + PyMem_RESIZE(var, type, _new_size); \ + else \ + var = NULL; \ +} while (0) + +PyObject * +PyList_New(int size) +{ + PyListObject *op; + size_t nbytes; + if (size < 0) { + PyErr_BadInternalCall(); + return NULL; + } + nbytes = size * sizeof(PyObject *); + /* Check for overflow */ + if (nbytes / sizeof(PyObject *) != (size_t)size) { + return PyErr_NoMemory(); + } + op = PyObject_GC_New(PyListObject, &PyList_Type); + if (op == NULL) { + return NULL; + } + if (size <= 0) { + op->ob_item = NULL; + } + else { + op->ob_item = (PyObject **) PyMem_MALLOC(nbytes); + if (op->ob_item == NULL) { + return PyErr_NoMemory(); + } + memset(op->ob_item, 0, sizeof(*op->ob_item) * size); + } + op->ob_size = size; + _PyObject_GC_TRACK(op); + return (PyObject *) op; +} + +int +PyList_Size(PyObject *op) +{ + if (!PyList_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + else + return ((PyListObject *)op) -> ob_size; +} + +static PyObject *indexerr; + +PyObject * +PyList_GetItem(PyObject *op, int i) +{ + if (!PyList_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + if (i < 0 || i >= ((PyListObject *)op) -> ob_size) { + if (indexerr == NULL) + indexerr = PyString_FromString( + "list index out of range"); + PyErr_SetObject(PyExc_IndexError, indexerr); + return NULL; + } + return ((PyListObject *)op) -> ob_item[i]; +} + +int +PyList_SetItem(register PyObject *op, register int i, + register PyObject *newitem) +{ + register PyObject *olditem; + register PyObject **p; + if (!PyList_Check(op)) { + Py_XDECREF(newitem); + PyErr_BadInternalCall(); + return -1; + } + if (i < 0 || i >= ((PyListObject *)op) -> ob_size) { + Py_XDECREF(newitem); + PyErr_SetString(PyExc_IndexError, + "list assignment index out of range"); + return -1; + } + p = ((PyListObject *)op) -> ob_item + i; + olditem = *p; + *p = newitem; + Py_XDECREF(olditem); + return 0; +} + +static int +ins1(PyListObject *self, int where, PyObject *v) +{ + int i; + PyObject **items; + if (v == NULL) { + PyErr_BadInternalCall(); + return -1; + } + if (self->ob_size == INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "cannot add more objects to list"); + return -1; + } + items = self->ob_item; + NRESIZE(items, PyObject *, self->ob_size+1); + if (items == NULL) { + PyErr_NoMemory(); + return -1; + } + if (where < 0) { + where += self->ob_size; + if (where < 0) + where = 0; + } + if (where > self->ob_size) + where = self->ob_size; + for (i = self->ob_size; --i >= where; ) + items[i+1] = items[i]; + Py_INCREF(v); + items[where] = v; + self->ob_item = items; + self->ob_size++; + return 0; +} + +int +PyList_Insert(PyObject *op, int where, PyObject *newitem) +{ + if (!PyList_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + return ins1((PyListObject *)op, where, newitem); +} + +int +PyList_Append(PyObject *op, PyObject *newitem) +{ + if (!PyList_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + return ins1((PyListObject *)op, + (int) ((PyListObject *)op)->ob_size, newitem); +} + +/* Methods */ + +static void +list_dealloc(PyListObject *op) +{ + int i; + PyObject_GC_UnTrack(op); + Py_TRASHCAN_SAFE_BEGIN(op) + if (op->ob_item != NULL) { + /* Do it backwards, for Christian Tismer. + There's a simple test case where somehow this reduces + thrashing when a *very* large list is created and + immediately deleted. */ + i = op->ob_size; + while (--i >= 0) { + Py_XDECREF(op->ob_item[i]); + } + PyMem_FREE(op->ob_item); + } + op->ob_type->tp_free((PyObject *)op); + Py_TRASHCAN_SAFE_END(op) +} + +static int +list_print(PyListObject *op, FILE *fp, int flags) +{ + int i; + + i = Py_ReprEnter((PyObject*)op); + if (i != 0) { + if (i < 0) + return i; + fprintf(fp, "[...]"); + return 0; + } + fprintf(fp, "["); + for (i = 0; i < op->ob_size; i++) { + if (i > 0) + fprintf(fp, ", "); + if (PyObject_Print(op->ob_item[i], fp, 0) != 0) { + Py_ReprLeave((PyObject *)op); + return -1; + } + } + fprintf(fp, "]"); + Py_ReprLeave((PyObject *)op); + return 0; +} + +static PyObject * +list_repr(PyListObject *v) +{ + int i; + PyObject *s, *temp; + PyObject *pieces = NULL, *result = NULL; + + i = Py_ReprEnter((PyObject*)v); + if (i != 0) { + return i > 0 ? PyString_FromString("[...]") : NULL; + } + + if (v->ob_size == 0) { + result = PyString_FromString("[]"); + goto Done; + } + + pieces = PyList_New(0); + if (pieces == NULL) + goto Done; + + /* Do repr() on each element. Note that this may mutate the list, + so must refetch the list size on each iteration. */ + for (i = 0; i < v->ob_size; ++i) { + int status; + s = PyObject_Repr(v->ob_item[i]); + if (s == NULL) + goto Done; + status = PyList_Append(pieces, s); + Py_DECREF(s); /* append created a new ref */ + if (status < 0) + goto Done; + } + + /* Add "[]" decorations to the first and last items. */ + assert(PyList_GET_SIZE(pieces) > 0); + s = PyString_FromString("["); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, 0); + PyString_ConcatAndDel(&s, temp); + PyList_SET_ITEM(pieces, 0, s); + if (s == NULL) + goto Done; + + s = PyString_FromString("]"); + if (s == NULL) + goto Done; + temp = PyList_GET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1); + PyString_ConcatAndDel(&temp, s); + PyList_SET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1, temp); + if (temp == NULL) + goto Done; + + /* Paste them all together with ", " between. */ + s = PyString_FromString(", "); + if (s == NULL) + goto Done; + result = _PyString_Join(s, pieces); + Py_DECREF(s); + +Done: + Py_XDECREF(pieces); + Py_ReprLeave((PyObject *)v); + return result; +} + +static int +list_length(PyListObject *a) +{ + return a->ob_size; +} + + + +static int +list_contains(PyListObject *a, PyObject *el) +{ + int i, cmp; + + for (i = 0, cmp = 0 ; cmp == 0 && i < a->ob_size; ++i) + cmp = PyObject_RichCompareBool(el, PyList_GET_ITEM(a, i), + Py_EQ); + return cmp; +} + + +static PyObject * +list_item(PyListObject *a, int i) +{ + if (i < 0 || i >= a->ob_size) { + if (indexerr == NULL) + indexerr = PyString_FromString( + "list index out of range"); + PyErr_SetObject(PyExc_IndexError, indexerr); + return NULL; + } + Py_INCREF(a->ob_item[i]); + return a->ob_item[i]; +} + +static PyObject * +list_slice(PyListObject *a, int ilow, int ihigh) +{ + PyListObject *np; + int i; + if (ilow < 0) + ilow = 0; + else if (ilow > a->ob_size) + ilow = a->ob_size; + if (ihigh < ilow) + ihigh = ilow; + else if (ihigh > a->ob_size) + ihigh = a->ob_size; + np = (PyListObject *) PyList_New(ihigh - ilow); + if (np == NULL) + return NULL; + for (i = ilow; i < ihigh; i++) { + PyObject *v = a->ob_item[i]; + Py_INCREF(v); + np->ob_item[i - ilow] = v; + } + return (PyObject *)np; +} + +PyObject * +PyList_GetSlice(PyObject *a, int ilow, int ihigh) +{ + if (!PyList_Check(a)) { + PyErr_BadInternalCall(); + return NULL; + } + return list_slice((PyListObject *)a, ilow, ihigh); +} + +static PyObject * +list_concat(PyListObject *a, PyObject *bb) +{ + int size; + int i; + PyListObject *np; + if (!PyList_Check(bb)) { + PyErr_Format(PyExc_TypeError, + "can only concatenate list (not \"%.200s\") to list", + bb->ob_type->tp_name); + return NULL; + } +#define b ((PyListObject *)bb) + size = a->ob_size + b->ob_size; + if (size < 0) + return PyErr_NoMemory(); + np = (PyListObject *) PyList_New(size); + if (np == NULL) { + return NULL; + } + for (i = 0; i < a->ob_size; i++) { + PyObject *v = a->ob_item[i]; + Py_INCREF(v); + np->ob_item[i] = v; + } + for (i = 0; i < b->ob_size; i++) { + PyObject *v = b->ob_item[i]; + Py_INCREF(v); + np->ob_item[i + a->ob_size] = v; + } + return (PyObject *)np; +#undef b +} + +static PyObject * +list_repeat(PyListObject *a, int n) +{ + int i, j; + int size; + PyListObject *np; + PyObject **p; + PyObject *elem; + if (n < 0) + n = 0; + size = a->ob_size * n; + if (size == 0) + return PyList_New(0); + if (n && size/n != a->ob_size) + return PyErr_NoMemory(); + np = (PyListObject *) PyList_New(size); + if (np == NULL) + return NULL; + + if (a->ob_size == 1) { + elem = a->ob_item[0]; + for (i = 0; i < n; i++) { + np->ob_item[i] = elem; + Py_INCREF(elem); + } + return (PyObject *) np; + } + p = np->ob_item; + for (i = 0; i < n; i++) { + for (j = 0; j < a->ob_size; j++) { + *p = a->ob_item[j]; + Py_INCREF(*p); + p++; + } + } + return (PyObject *) np; +} + +static int +list_ass_slice(PyListObject *a, int ilow, int ihigh, PyObject *v) +{ + /* Because [X]DECREF can recursively invoke list operations on + this list, we must postpone all [X]DECREF activity until + after the list is back in its canonical shape. Therefore + we must allocate an additional array, 'recycle', into which + we temporarily copy the items that are deleted from the + list. :-( */ + PyObject **recycle, **p; + PyObject **item; + PyObject *v_as_SF = NULL; /* PySequence_Fast(v) */ + int n; /* Size of replacement list */ + int d; /* Change in size */ + int k; /* Loop index */ +#define b ((PyListObject *)v) + if (v == NULL) + n = 0; + else { + char msg[256]; + if (a == b) { + /* Special case "a[i:j] = a" -- copy b first */ + int ret; + v = list_slice(b, 0, b->ob_size); + if (v == NULL) + return -1; + ret = list_ass_slice(a, ilow, ihigh, v); + Py_DECREF(v); + return ret; + } + + PyOS_snprintf(msg, sizeof(msg), + "must assign sequence" + " (not \"%.200s\") to slice", + v->ob_type->tp_name); + v_as_SF = PySequence_Fast(v, msg); + if(v_as_SF == NULL) + return -1; + n = PySequence_Fast_GET_SIZE(v_as_SF); + } + if (ilow < 0) + ilow = 0; + else if (ilow > a->ob_size) + ilow = a->ob_size; + if (ihigh < ilow) + ihigh = ilow; + else if (ihigh > a->ob_size) + ihigh = a->ob_size; + item = a->ob_item; + d = n - (ihigh-ilow); + if (ihigh > ilow) { + p = recycle = PyMem_NEW(PyObject *, (ihigh-ilow)); + if (recycle == NULL) { + PyErr_NoMemory(); + return -1; + } + } + else + p = recycle = NULL; + if (d <= 0) { /* Delete -d items; recycle ihigh-ilow items */ + for (k = ilow; k < ihigh; k++) + *p++ = item[k]; + if (d < 0) { + for (/*k = ihigh*/; k < a->ob_size; k++) + item[k+d] = item[k]; + a->ob_size += d; + NRESIZE(item, PyObject *, a->ob_size); /* Can't fail */ + a->ob_item = item; + } + } + else { /* Insert d items; recycle ihigh-ilow items */ + NRESIZE(item, PyObject *, a->ob_size + d); + if (item == NULL) { + if (recycle != NULL) + PyMem_DEL(recycle); + PyErr_NoMemory(); + return -1; + } + for (k = a->ob_size; --k >= ihigh; ) + item[k+d] = item[k]; + for (/*k = ihigh-1*/; k >= ilow; --k) + *p++ = item[k]; + a->ob_item = item; + a->ob_size += d; + } + for (k = 0; k < n; k++, ilow++) { + PyObject *w = PySequence_Fast_GET_ITEM(v_as_SF, k); + Py_XINCREF(w); + item[ilow] = w; + } + if (recycle) { + while (--p >= recycle) + Py_XDECREF(*p); + PyMem_DEL(recycle); + } + if (a->ob_size == 0 && a->ob_item != NULL) { + PyMem_FREE(a->ob_item); + a->ob_item = NULL; + } + Py_XDECREF(v_as_SF); + return 0; +#undef b +} + +int +PyList_SetSlice(PyObject *a, int ilow, int ihigh, PyObject *v) +{ + if (!PyList_Check(a)) { + PyErr_BadInternalCall(); + return -1; + } + return list_ass_slice((PyListObject *)a, ilow, ihigh, v); +} + +static PyObject * +list_inplace_repeat(PyListObject *self, int n) +{ + PyObject **items; + int size, i, j; + + + size = PyList_GET_SIZE(self); + if (size == 0) { + Py_INCREF(self); + return (PyObject *)self; + } + + items = self->ob_item; + + if (n < 1) { + self->ob_item = NULL; + self->ob_size = 0; + for (i = 0; i < size; i++) + Py_XDECREF(items[i]); + PyMem_DEL(items); + Py_INCREF(self); + return (PyObject *)self; + } + + NRESIZE(items, PyObject*, size*n); + if (items == NULL) { + PyErr_NoMemory(); + goto finally; + } + self->ob_item = items; + for (i = 1; i < n; i++) { /* Start counting at 1, not 0 */ + for (j = 0; j < size; j++) { + PyObject *o = PyList_GET_ITEM(self, j); + Py_INCREF(o); + PyList_SET_ITEM(self, self->ob_size++, o); + } + } + Py_INCREF(self); + return (PyObject *)self; + finally: + return NULL; +} + +static int +list_ass_item(PyListObject *a, int i, PyObject *v) +{ + PyObject *old_value; + if (i < 0 || i >= a->ob_size) { + PyErr_SetString(PyExc_IndexError, + "list assignment index out of range"); + return -1; + } + if (v == NULL) + return list_ass_slice(a, i, i+1, v); + Py_INCREF(v); + old_value = a->ob_item[i]; + a->ob_item[i] = v; + Py_DECREF(old_value); + return 0; +} + +static PyObject * +ins(PyListObject *self, int where, PyObject *v) +{ + if (ins1(self, where, v) != 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +listinsert(PyListObject *self, PyObject *args) +{ + int i; + PyObject *v; + if (!PyArg_ParseTuple(args, "iO:insert", &i, &v)) + return NULL; + return ins(self, i, v); +} + +static PyObject * +listappend(PyListObject *self, PyObject *v) +{ + return ins(self, (int) self->ob_size, v); +} + +static int +listextend_internal(PyListObject *self, PyObject *b) +{ + PyObject **items; + int selflen = PyList_GET_SIZE(self); + int blen; + register int i; + + if (PyObject_Size(b) == 0) { + /* short circuit when b is empty */ + Py_DECREF(b); + return 0; + } + + if (self == (PyListObject*)b) { + /* as in list_ass_slice() we must special case the + * situation: a.extend(a) + * + * XXX: I think this way ought to be faster than using + * list_slice() the way list_ass_slice() does. + */ + Py_DECREF(b); + b = PyList_New(selflen); + if (!b) + return -1; + for (i = 0; i < selflen; i++) { + PyObject *o = PyList_GET_ITEM(self, i); + Py_INCREF(o); + PyList_SET_ITEM(b, i, o); + } + } + + blen = PyObject_Size(b); + + /* resize a using idiom */ + items = self->ob_item; + NRESIZE(items, PyObject*, selflen + blen); + if (items == NULL) { + PyErr_NoMemory(); + Py_DECREF(b); + return -1; + } + + self->ob_item = items; + + /* populate the end of self with b's items */ + for (i = 0; i < blen; i++) { + PyObject *o = PySequence_Fast_GET_ITEM(b, i); + Py_INCREF(o); + PyList_SET_ITEM(self, self->ob_size++, o); + } + Py_DECREF(b); + return 0; +} + + +static PyObject * +list_inplace_concat(PyListObject *self, PyObject *other) +{ + other = PySequence_Fast(other, "argument to += must be iterable"); + if (!other) + return NULL; + + if (listextend_internal(self, other) < 0) + return NULL; + + Py_INCREF(self); + return (PyObject *)self; +} + +static PyObject * +listextend(PyListObject *self, PyObject *b) +{ + + b = PySequence_Fast(b, "list.extend() argument must be iterable"); + if (!b) + return NULL; + + if (listextend_internal(self, b) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +listpop(PyListObject *self, PyObject *args) +{ + int i = -1; + PyObject *v; + if (!PyArg_ParseTuple(args, "|i:pop", &i)) + return NULL; + if (self->ob_size == 0) { + /* Special-case most common failure cause */ + PyErr_SetString(PyExc_IndexError, "pop from empty list"); + return NULL; + } + if (i < 0) + i += self->ob_size; + if (i < 0 || i >= self->ob_size) { + PyErr_SetString(PyExc_IndexError, "pop index out of range"); + return NULL; + } + v = self->ob_item[i]; + Py_INCREF(v); + if (list_ass_slice(self, i, i+1, (PyObject *)NULL) != 0) { + Py_DECREF(v); + return NULL; + } + return v; +} + +/* Reverse a slice of a list in place, from lo up to (exclusive) hi. */ +static void +reverse_slice(PyObject **lo, PyObject **hi) +{ + assert(lo && hi); + + --hi; + while (lo < hi) { + PyObject *t = *lo; + *lo = *hi; + *hi = t; + ++lo; + --hi; + } +} + +/* Lots of code for an adaptive, stable, natural mergesort. There are many + * pieces to this algorithm; read listsort.txt for overviews and details. + */ + +/* Comparison function. Takes care of calling a user-supplied + * comparison function (any callable Python object), which must not be + * NULL (use the ISLT macro if you don't know, or call PyObject_RichCompareBool + * with Py_LT if you know it's NULL). + * Returns -1 on error, 1 if x < y, 0 if x >= y. + */ +static int +islt(PyObject *x, PyObject *y, PyObject *compare) +{ + PyObject *res; + PyObject *args; + int i; + + assert(compare != NULL); + /* Call the user's comparison function and translate the 3-way + * result into true or false (or error). + */ + args = PyTuple_New(2); + if (args == NULL) + return -1; + Py_INCREF(x); + Py_INCREF(y); + PyTuple_SET_ITEM(args, 0, x); + PyTuple_SET_ITEM(args, 1, y); + res = PyObject_Call(compare, args, NULL); + Py_DECREF(args); + if (res == NULL) + return -1; + if (!PyInt_Check(res)) { + Py_DECREF(res); + PyErr_SetString(PyExc_TypeError, + "comparison function must return int"); + return -1; + } + i = PyInt_AsLong(res); + Py_DECREF(res); + return i < 0; +} + +/* If COMPARE is NULL, calls PyObject_RichCompareBool with Py_LT, else calls + * islt. This avoids a layer of function call in the usual case, and + * sorting does many comparisons. + * Returns -1 on error, 1 if x < y, 0 if x >= y. + */ +#define ISLT(X, Y, COMPARE) ((COMPARE) == NULL ? \ + PyObject_RichCompareBool(X, Y, Py_LT) : \ + islt(X, Y, COMPARE)) + +/* Compare X to Y via "<". Goto "fail" if the comparison raises an + error. Else "k" is set to true iff X. X and Y are PyObject*s. +*/ +#define IFLT(X, Y) if ((k = ISLT(X, Y, compare)) < 0) goto fail; \ + if (k) + +/* binarysort is the best method for sorting small arrays: it does + few compares, but can do data movement quadratic in the number of + elements. + [lo, hi) is a contiguous slice of a list, and is sorted via + binary insertion. This sort is stable. + On entry, must have lo <= start <= hi, and that [lo, start) is already + sorted (pass start == lo if you don't know!). + If islt() complains return -1, else 0. + Even in case of error, the output slice will be some permutation of + the input (nothing is lost or duplicated). +*/ +static int +binarysort(PyObject **lo, PyObject **hi, PyObject **start, PyObject *compare) + /* compare -- comparison function object, or NULL for default */ +{ + register int k; + register PyObject **l, **p, **r; + register PyObject *pivot; + + assert(lo <= start && start <= hi); + /* assert [lo, start) is sorted */ + if (lo == start) + ++start; + for (; start < hi; ++start) { + /* set l to where *start belongs */ + l = lo; + r = start; + pivot = *r; + /* Invariants: + * pivot >= all in [lo, l). + * pivot < all in [r, start). + * The second is vacuously true at the start. + */ + assert(l < r); + do { + p = l + ((r - l) >> 1); + IFLT(pivot, *p) + r = p; + else + l = p+1; + } while (l < r); + assert(l == r); + /* The invariants still hold, so pivot >= all in [lo, l) and + pivot < all in [l, start), so pivot belongs at l. Note + that if there are elements equal to pivot, l points to the + first slot after them -- that's why this sort is stable. + Slide over to make room. + Caution: using memmove is much slower under MSVC 5; + we're not usually moving many slots. */ + for (p = start; p > l; --p) + *p = *(p-1); + *l = pivot; + } + return 0; + + fail: + return -1; +} + +/* +Return the length of the run beginning at lo, in the slice [lo, hi). lo < hi +is required on entry. "A run" is the longest ascending sequence, with + + lo[0] <= lo[1] <= lo[2] <= ... + +or the longest descending sequence, with + + lo[0] > lo[1] > lo[2] > ... + +Boolean *descending is set to 0 in the former case, or to 1 in the latter. +For its intended use in a stable mergesort, the strictness of the defn of +"descending" is needed so that the caller can safely reverse a descending +sequence without violating stability (strict > ensures there are no equal +elements to get out of order). + +Returns -1 in case of error. +*/ +static int +count_run(PyObject **lo, PyObject **hi, PyObject *compare, int *descending) +{ + int k; + int n; + + assert(lo < hi); + *descending = 0; + ++lo; + if (lo == hi) + return 1; + + n = 2; + IFLT(*lo, *(lo-1)) { + *descending = 1; + for (lo = lo+1; lo < hi; ++lo, ++n) { + IFLT(*lo, *(lo-1)) + ; + else + break; + } + } + else { + for (lo = lo+1; lo < hi; ++lo, ++n) { + IFLT(*lo, *(lo-1)) + break; + } + } + + return n; +fail: + return -1; +} + +/* +Locate the proper position of key in a sorted vector; if the vector contains +an element equal to key, return the position immediately to the left of +the leftmost equal element. [gallop_right() does the same except returns +the position to the right of the rightmost equal element (if any).] + +"a" is a sorted vector with n elements, starting at a[0]. n must be > 0. + +"hint" is an index at which to begin the search, 0 <= hint < n. The closer +hint is to the final result, the faster this runs. + +The return value is the int k in 0..n such that + + a[k-1] < key <= a[k] + +pretending that *(a-1) is minus infinity and a[n] is plus infinity. IOW, +key belongs at index k; or, IOW, the first k elements of a should precede +key, and the last n-k should follow key. + +Returns -1 on error. See listsort.txt for info on the method. +*/ +static int +gallop_left(PyObject *key, PyObject **a, int n, int hint, PyObject *compare) +{ + int ofs; + int lastofs; + int k; + + assert(key && a && n > 0 && hint >= 0 && hint < n); + + a += hint; + lastofs = 0; + ofs = 1; + IFLT(*a, key) { + /* a[hint] < key -- gallop right, until + * a[hint + lastofs] < key <= a[hint + ofs] + */ + const int maxofs = n - hint; /* &a[n-1] is highest */ + while (ofs < maxofs) { + IFLT(a[ofs], key) { + lastofs = ofs; + ofs = (ofs << 1) + 1; + if (ofs <= 0) /* int overflow */ + ofs = maxofs; + } + else /* key <= a[hint + ofs] */ + break; + } + if (ofs > maxofs) + ofs = maxofs; + /* Translate back to offsets relative to &a[0]. */ + lastofs += hint; + ofs += hint; + } + else { + /* key <= a[hint] -- gallop left, until + * a[hint - ofs] < key <= a[hint - lastofs] + */ + const int maxofs = hint + 1; /* &a[0] is lowest */ + while (ofs < maxofs) { + IFLT(*(a-ofs), key) + break; + /* key <= a[hint - ofs] */ + lastofs = ofs; + ofs = (ofs << 1) + 1; + if (ofs <= 0) /* int overflow */ + ofs = maxofs; + } + if (ofs > maxofs) + ofs = maxofs; + /* Translate back to positive offsets relative to &a[0]. */ + k = lastofs; + lastofs = hint - ofs; + ofs = hint - k; + } + a -= hint; + + assert(-1 <= lastofs && lastofs < ofs && ofs <= n); + /* Now a[lastofs] < key <= a[ofs], so key belongs somewhere to the + * right of lastofs but no farther right than ofs. Do a binary + * search, with invariant a[lastofs-1] < key <= a[ofs]. + */ + ++lastofs; + while (lastofs < ofs) { + int m = lastofs + ((ofs - lastofs) >> 1); + + IFLT(a[m], key) + lastofs = m+1; /* a[m] < key */ + else + ofs = m; /* key <= a[m] */ + } + assert(lastofs == ofs); /* so a[ofs-1] < key <= a[ofs] */ + return ofs; + +fail: + return -1; +} + +/* +Exactly like gallop_left(), except that if key already exists in a[0:n], +finds the position immediately to the right of the rightmost equal value. + +The return value is the int k in 0..n such that + + a[k-1] <= key < a[k] + +or -1 if error. + +The code duplication is massive, but this is enough different given that +we're sticking to "<" comparisons that it's much harder to follow if +written as one routine with yet another "left or right?" flag. +*/ +static int +gallop_right(PyObject *key, PyObject **a, int n, int hint, PyObject *compare) +{ + int ofs; + int lastofs; + int k; + + assert(key && a && n > 0 && hint >= 0 && hint < n); + + a += hint; + lastofs = 0; + ofs = 1; + IFLT(key, *a) { + /* key < a[hint] -- gallop left, until + * a[hint - ofs] <= key < a[hint - lastofs] + */ + const int maxofs = hint + 1; /* &a[0] is lowest */ + while (ofs < maxofs) { + IFLT(key, *(a-ofs)) { + lastofs = ofs; + ofs = (ofs << 1) + 1; + if (ofs <= 0) /* int overflow */ + ofs = maxofs; + } + else /* a[hint - ofs] <= key */ + break; + } + if (ofs > maxofs) + ofs = maxofs; + /* Translate back to positive offsets relative to &a[0]. */ + k = lastofs; + lastofs = hint - ofs; + ofs = hint - k; + } + else { + /* a[hint] <= key -- gallop right, until + * a[hint + lastofs] <= key < a[hint + ofs] + */ + const int maxofs = n - hint; /* &a[n-1] is highest */ + while (ofs < maxofs) { + IFLT(key, a[ofs]) + break; + /* a[hint + ofs] <= key */ + lastofs = ofs; + ofs = (ofs << 1) + 1; + if (ofs <= 0) /* int overflow */ + ofs = maxofs; + } + if (ofs > maxofs) + ofs = maxofs; + /* Translate back to offsets relative to &a[0]. */ + lastofs += hint; + ofs += hint; + } + a -= hint; + + assert(-1 <= lastofs && lastofs < ofs && ofs <= n); + /* Now a[lastofs] <= key < a[ofs], so key belongs somewhere to the + * right of lastofs but no farther right than ofs. Do a binary + * search, with invariant a[lastofs-1] <= key < a[ofs]. + */ + ++lastofs; + while (lastofs < ofs) { + int m = lastofs + ((ofs - lastofs) >> 1); + + IFLT(key, a[m]) + ofs = m; /* key < a[m] */ + else + lastofs = m+1; /* a[m] <= key */ + } + assert(lastofs == ofs); /* so a[ofs-1] <= key < a[ofs] */ + return ofs; + +fail: + return -1; +} + +/* The maximum number of entries in a MergeState's pending-runs stack. + * This is enough to sort arrays of size up to about + * 32 * phi ** MAX_MERGE_PENDING + * where phi ~= 1.618. 85 is ridiculouslylarge enough, good for an array + * with 2**64 elements. + */ +#define MAX_MERGE_PENDING 85 + +/* When we get into galloping mode, we stay there until both runs win less + * often than MIN_GALLOP consecutive times. See listsort.txt for more info. + */ +#define MIN_GALLOP 7 + +/* Avoid malloc for small temp arrays. */ +#define MERGESTATE_TEMP_SIZE 256 + +/* One MergeState exists on the stack per invocation of mergesort. It's just + * a convenient way to pass state around among the helper functions. + */ +struct s_slice { + PyObject **base; + int len; +}; + +typedef struct s_MergeState { + /* The user-supplied comparison function. or NULL if none given. */ + PyObject *compare; + + /* This controls when we get *into* galloping mode. It's initialized + * to MIN_GALLOP. merge_lo and merge_hi tend to nudge it higher for + * random data, and lower for highly structured data. + */ + int min_gallop; + + /* 'a' is temp storage to help with merges. It contains room for + * alloced entries. + */ + PyObject **a; /* may point to temparray below */ + int alloced; + + /* A stack of n pending runs yet to be merged. Run #i starts at + * address base[i] and extends for len[i] elements. It's always + * true (so long as the indices are in bounds) that + * + * pending[i].base + pending[i].len == pending[i+1].base + * + * so we could cut the storage for this, but it's a minor amount, + * and keeping all the info explicit simplifies the code. + */ + int n; + struct s_slice pending[MAX_MERGE_PENDING]; + + /* 'a' points to this when possible, rather than muck with malloc. */ + PyObject *temparray[MERGESTATE_TEMP_SIZE]; +} MergeState; + +/* Conceptually a MergeState's constructor. */ +static void +merge_init(MergeState *ms, PyObject *compare) +{ + assert(ms != NULL); + ms->compare = compare; + ms->a = ms->temparray; + ms->alloced = MERGESTATE_TEMP_SIZE; + ms->n = 0; + ms->min_gallop = MIN_GALLOP; +} + +/* Free all the temp memory owned by the MergeState. This must be called + * when you're done with a MergeState, and may be called before then if + * you want to free the temp memory early. + */ +static void +merge_freemem(MergeState *ms) +{ + assert(ms != NULL); + if (ms->a != ms->temparray) + PyMem_Free(ms->a); + ms->a = ms->temparray; + ms->alloced = MERGESTATE_TEMP_SIZE; +} + +/* Ensure enough temp memory for 'need' array slots is available. + * Returns 0 on success and -1 if the memory can't be gotten. + */ +static int +merge_getmem(MergeState *ms, int need) +{ + assert(ms != NULL); + if (need <= ms->alloced) + return 0; + /* Don't realloc! That can cost cycles to copy the old data, but + * we don't care what's in the block. + */ + merge_freemem(ms); + ms->a = (PyObject **)PyMem_Malloc(need * sizeof(PyObject*)); + if (ms->a) { + ms->alloced = need; + return 0; + } + PyErr_NoMemory(); + merge_freemem(ms); /* reset to sane state */ + return -1; +} +#define MERGE_GETMEM(MS, NEED) ((NEED) <= (MS)->alloced ? 0 : \ + merge_getmem(MS, NEED)) + +/* Merge the na elements starting at pa with the nb elements starting at pb + * in a stable way, in-place. na and nb must be > 0, and pa + na == pb. + * Must also have that *pb < *pa, that pa[na-1] belongs at the end of the + * merge, and should have na <= nb. See listsort.txt for more info. + * Return 0 if successful, -1 if error. + */ +static int +merge_lo(MergeState *ms, PyObject **pa, int na, PyObject **pb, int nb) +{ + int k; + PyObject *compare; + PyObject **dest; + int result = -1; /* guilty until proved innocent */ + int min_gallop = ms->min_gallop; + + assert(ms && pa && pb && na > 0 && nb > 0 && pa + na == pb); + if (MERGE_GETMEM(ms, na) < 0) + return -1; + memcpy(ms->a, pa, na * sizeof(PyObject*)); + dest = pa; + pa = ms->a; + + *dest++ = *pb++; + --nb; + if (nb == 0) + goto Succeed; + if (na == 1) + goto CopyB; + + compare = ms->compare; + for (;;) { + int acount = 0; /* # of times A won in a row */ + int bcount = 0; /* # of times B won in a row */ + + /* Do the straightforward thing until (if ever) one run + * appears to win consistently. + */ + for (;;) { + assert(na > 1 && nb > 0); + k = ISLT(*pb, *pa, compare); + if (k) { + if (k < 0) + goto Fail; + *dest++ = *pb++; + ++bcount; + acount = 0; + --nb; + if (nb == 0) + goto Succeed; + if (bcount >= min_gallop) + break; + } + else { + *dest++ = *pa++; + ++acount; + bcount = 0; + --na; + if (na == 1) + goto CopyB; + if (acount >= min_gallop) + break; + } + } + + /* One run is winning so consistently that galloping may + * be a huge win. So try that, and continue galloping until + * (if ever) neither run appears to be winning consistently + * anymore. + */ + ++min_gallop; + do { + assert(na > 1 && nb > 0); + min_gallop -= min_gallop > 1; + ms->min_gallop = min_gallop; + k = gallop_right(*pb, pa, na, 0, compare); + acount = k; + if (k) { + if (k < 0) + goto Fail; + memcpy(dest, pa, k * sizeof(PyObject *)); + dest += k; + pa += k; + na -= k; + if (na == 1) + goto CopyB; + /* na==0 is impossible now if the comparison + * function is consistent, but we can't assume + * that it is. + */ + if (na == 0) + goto Succeed; + } + *dest++ = *pb++; + --nb; + if (nb == 0) + goto Succeed; + + k = gallop_left(*pa, pb, nb, 0, compare); + bcount = k; + if (k) { + if (k < 0) + goto Fail; + memmove(dest, pb, k * sizeof(PyObject *)); + dest += k; + pb += k; + nb -= k; + if (nb == 0) + goto Succeed; + } + *dest++ = *pa++; + --na; + if (na == 1) + goto CopyB; + } while (acount >= MIN_GALLOP || bcount >= MIN_GALLOP); + ++min_gallop; /* penalize it for leaving galloping mode */ + ms->min_gallop = min_gallop; + } +Succeed: + result = 0; +Fail: + if (na) + memcpy(dest, pa, na * sizeof(PyObject*)); + return result; +CopyB: + assert(na == 1 && nb > 0); + /* The last element of pa belongs at the end of the merge. */ + memmove(dest, pb, nb * sizeof(PyObject *)); + dest[nb] = *pa; + return 0; +} + +/* Merge the na elements starting at pa with the nb elements starting at pb + * in a stable way, in-place. na and nb must be > 0, and pa + na == pb. + * Must also have that *pb < *pa, that pa[na-1] belongs at the end of the + * merge, and should have na >= nb. See listsort.txt for more info. + * Return 0 if successful, -1 if error. + */ +static int +merge_hi(MergeState *ms, PyObject **pa, int na, PyObject **pb, int nb) +{ + int k; + PyObject *compare; + PyObject **dest; + int result = -1; /* guilty until proved innocent */ + PyObject **basea; + PyObject **baseb; + int min_gallop = ms->min_gallop; + + assert(ms && pa && pb && na > 0 && nb > 0 && pa + na == pb); + if (MERGE_GETMEM(ms, nb) < 0) + return -1; + dest = pb + nb - 1; + memcpy(ms->a, pb, nb * sizeof(PyObject*)); + basea = pa; + baseb = ms->a; + pb = ms->a + nb - 1; + pa += na - 1; + + *dest-- = *pa--; + --na; + if (na == 0) + goto Succeed; + if (nb == 1) + goto CopyA; + + compare = ms->compare; + for (;;) { + int acount = 0; /* # of times A won in a row */ + int bcount = 0; /* # of times B won in a row */ + + /* Do the straightforward thing until (if ever) one run + * appears to win consistently. + */ + for (;;) { + assert(na > 0 && nb > 1); + k = ISLT(*pb, *pa, compare); + if (k) { + if (k < 0) + goto Fail; + *dest-- = *pa--; + ++acount; + bcount = 0; + --na; + if (na == 0) + goto Succeed; + if (acount >= min_gallop) + break; + } + else { + *dest-- = *pb--; + ++bcount; + acount = 0; + --nb; + if (nb == 1) + goto CopyA; + if (bcount >= min_gallop) + break; + } + } + + /* One run is winning so consistently that galloping may + * be a huge win. So try that, and continue galloping until + * (if ever) neither run appears to be winning consistently + * anymore. + */ + ++min_gallop; + do { + assert(na > 0 && nb > 1); + min_gallop -= min_gallop > 1; + ms->min_gallop = min_gallop; + k = gallop_right(*pb, basea, na, na-1, compare); + if (k < 0) + goto Fail; + k = na - k; + acount = k; + if (k) { + dest -= k; + pa -= k; + memmove(dest+1, pa+1, k * sizeof(PyObject *)); + na -= k; + if (na == 0) + goto Succeed; + } + *dest-- = *pb--; + --nb; + if (nb == 1) + goto CopyA; + + k = gallop_left(*pa, baseb, nb, nb-1, compare); + if (k < 0) + goto Fail; + k = nb - k; + bcount = k; + if (k) { + dest -= k; + pb -= k; + memcpy(dest+1, pb+1, k * sizeof(PyObject *)); + nb -= k; + if (nb == 1) + goto CopyA; + /* nb==0 is impossible now if the comparison + * function is consistent, but we can't assume + * that it is. + */ + if (nb == 0) + goto Succeed; + } + *dest-- = *pa--; + --na; + if (na == 0) + goto Succeed; + } while (acount >= MIN_GALLOP || bcount >= MIN_GALLOP); + ++min_gallop; /* penalize it for leaving galloping mode */ + ms->min_gallop = min_gallop; + } +Succeed: + result = 0; +Fail: + if (nb) + memcpy(dest-(nb-1), baseb, nb * sizeof(PyObject*)); + return result; +CopyA: + assert(nb == 1 && na > 0); + /* The first element of pb belongs at the front of the merge. */ + dest -= na; + pa -= na; + memmove(dest+1, pa+1, na * sizeof(PyObject *)); + *dest = *pb; + return 0; +} + +/* Merge the two runs at stack indices i and i+1. + * Returns 0 on success, -1 on error. + */ +static int +merge_at(MergeState *ms, int i) +{ + PyObject **pa, **pb; + int na, nb; + int k; + PyObject *compare; + + assert(ms != NULL); + assert(ms->n >= 2); + assert(i >= 0); + assert(i == ms->n - 2 || i == ms->n - 3); + + pa = ms->pending[i].base; + na = ms->pending[i].len; + pb = ms->pending[i+1].base; + nb = ms->pending[i+1].len; + assert(na > 0 && nb > 0); + assert(pa + na == pb); + + /* Record the length of the combined runs; if i is the 3rd-last + * run now, also slide over the last run (which isn't involved + * in this merge). The current run i+1 goes away in any case. + */ + ms->pending[i].len = na + nb; + if (i == ms->n - 3) + ms->pending[i+1] = ms->pending[i+2]; + --ms->n; + + /* Where does b start in a? Elements in a before that can be + * ignored (already in place). + */ + compare = ms->compare; + k = gallop_right(*pb, pa, na, 0, compare); + if (k < 0) + return -1; + pa += k; + na -= k; + if (na == 0) + return 0; + + /* Where does a end in b? Elements in b after that can be + * ignored (already in place). + */ + nb = gallop_left(pa[na-1], pb, nb, nb-1, compare); + if (nb <= 0) + return nb; + + /* Merge what remains of the runs, using a temp array with + * min(na, nb) elements. + */ + if (na <= nb) + return merge_lo(ms, pa, na, pb, nb); + else + return merge_hi(ms, pa, na, pb, nb); +} + +/* Examine the stack of runs waiting to be merged, merging adjacent runs + * until the stack invariants are re-established: + * + * 1. len[-3] > len[-2] + len[-1] + * 2. len[-2] > len[-1] + * + * See listsort.txt for more info. + * + * Returns 0 on success, -1 on error. + */ +static int +merge_collapse(MergeState *ms) +{ + struct s_slice *p = ms->pending; + + assert(ms); + while (ms->n > 1) { + int n = ms->n - 2; + if (n > 0 && p[n-1].len <= p[n].len + p[n+1].len) { + if (p[n-1].len < p[n+1].len) + --n; + if (merge_at(ms, n) < 0) + return -1; + } + else if (p[n].len <= p[n+1].len) { + if (merge_at(ms, n) < 0) + return -1; + } + else + break; + } + return 0; +} + +/* Regardless of invariants, merge all runs on the stack until only one + * remains. This is used at the end of the mergesort. + * + * Returns 0 on success, -1 on error. + */ +static int +merge_force_collapse(MergeState *ms) +{ + struct s_slice *p = ms->pending; + + assert(ms); + while (ms->n > 1) { + int n = ms->n - 2; + if (n > 0 && p[n-1].len < p[n+1].len) + --n; + if (merge_at(ms, n) < 0) + return -1; + } + return 0; +} + +/* Compute a good value for the minimum run length; natural runs shorter + * than this are boosted artificially via binary insertion. + * + * If n < 64, return n (it's too small to bother with fancy stuff). + * Else if n is an exact power of 2, return 32. + * Else return an int k, 32 <= k <= 64, such that n/k is close to, but + * strictly less than, an exact power of 2. + * + * See listsort.txt for more info. + */ +static int +merge_compute_minrun(int n) +{ + int r = 0; /* becomes 1 if any 1 bits are shifted off */ + + assert(n >= 0); + while (n >= 64) { + r |= n & 1; + n >>= 1; + } + return n + r; +} + +/* An adaptive, stable, natural mergesort. See listsort.txt. + * Returns Py_None on success, NULL on error. Even in case of error, the + * list will be some permutation of its input state (nothing is lost or + * duplicated). + */ +static PyObject * +listsort(PyListObject *self, PyObject *args) +{ + MergeState ms; + PyObject **lo, **hi; + int nremaining; + int minrun; + int saved_ob_size; + PyObject **saved_ob_item; + PyObject **empty_ob_item; + PyObject *compare = NULL; + PyObject *result = NULL; /* guilty until proved innocent */ + + assert(self != NULL); + if (args != NULL) { + if (!PyArg_UnpackTuple(args, "sort", 0, 1, &compare)) + return NULL; + } + if (compare == Py_None) + compare = NULL; + + merge_init(&ms, compare); + + /* The list is temporarily made empty, so that mutations performed + * by comparison functions can't affect the slice of memory we're + * sorting (allowing mutations during sorting is a core-dump + * factory, since ob_item may change). + */ + saved_ob_size = self->ob_size; + saved_ob_item = self->ob_item; + self->ob_size = 0; + self->ob_item = empty_ob_item = PyMem_NEW(PyObject *, 0); + + nremaining = saved_ob_size; + if (nremaining < 2) + goto succeed; + + /* March over the array once, left to right, finding natural runs, + * and extending short natural runs to minrun elements. + */ + lo = saved_ob_item; + hi = lo + nremaining; + minrun = merge_compute_minrun(nremaining); + do { + int descending; + int n; + + /* Identify next run. */ + n = count_run(lo, hi, compare, &descending); + if (n < 0) + goto fail; + if (descending) + reverse_slice(lo, lo + n); + /* If short, extend to min(minrun, nremaining). */ + if (n < minrun) { + const int force = nremaining <= minrun ? + nremaining : minrun; + if (binarysort(lo, lo + force, lo + n, compare) < 0) + goto fail; + n = force; + } + /* Push run onto pending-runs stack, and maybe merge. */ + assert(ms.n < MAX_MERGE_PENDING); + ms.pending[ms.n].base = lo; + ms.pending[ms.n].len = n; + ++ms.n; + if (merge_collapse(&ms) < 0) + goto fail; + /* Advance to find next run. */ + lo += n; + nremaining -= n; + } while (nremaining); + assert(lo == hi); + + if (merge_force_collapse(&ms) < 0) + goto fail; + assert(ms.n == 1); + assert(ms.pending[0].base == saved_ob_item); + assert(ms.pending[0].len == saved_ob_size); + +succeed: + result = Py_None; +fail: + if (self->ob_item != empty_ob_item || self->ob_size) { + /* The user mucked with the list during the sort. */ + (void)list_ass_slice(self, 0, self->ob_size, (PyObject *)NULL); + if (result != NULL) { + PyErr_SetString(PyExc_ValueError, + "list modified during sort"); + result = NULL; + } + } + if (self->ob_item == empty_ob_item) + PyMem_FREE(empty_ob_item); + self->ob_size = saved_ob_size; + self->ob_item = saved_ob_item; + merge_freemem(&ms); + Py_XINCREF(result); + return result; +} +#undef IFLT +#undef ISLT + +int +PyList_Sort(PyObject *v) +{ + if (v == NULL || !PyList_Check(v)) { + PyErr_BadInternalCall(); + return -1; + } + v = listsort((PyListObject *)v, (PyObject *)NULL); + if (v == NULL) + return -1; + Py_DECREF(v); + return 0; +} + +static PyObject * +listreverse(PyListObject *self) +{ + if (self->ob_size > 1) + reverse_slice(self->ob_item, self->ob_item + self->ob_size); + Py_INCREF(Py_None); + return Py_None; +} + +int +PyList_Reverse(PyObject *v) +{ + PyListObject *self = (PyListObject *)v; + + if (v == NULL || !PyList_Check(v)) { + PyErr_BadInternalCall(); + return -1; + } + if (self->ob_size > 1) + reverse_slice(self->ob_item, self->ob_item + self->ob_size); + return 0; +} + +PyObject * +PyList_AsTuple(PyObject *v) +{ + PyObject *w; + PyObject **p; + int n; + if (v == NULL || !PyList_Check(v)) { + PyErr_BadInternalCall(); + return NULL; + } + n = ((PyListObject *)v)->ob_size; + w = PyTuple_New(n); + if (w == NULL) + return NULL; + p = ((PyTupleObject *)w)->ob_item; + memcpy((void *)p, + (void *)((PyListObject *)v)->ob_item, + n*sizeof(PyObject *)); + while (--n >= 0) { + Py_INCREF(*p); + p++; + } + return w; +} + +static PyObject * +listindex(PyListObject *self, PyObject *args) +{ + int i, start=0, stop=self->ob_size; + PyObject *v; + + if (!PyArg_ParseTuple(args, "O|O&O&:index", &v, + _PyEval_SliceIndex, &start, + _PyEval_SliceIndex, &stop)) + return NULL; + if (start < 0) { + start += self->ob_size; + if (start < 0) + start = 0; + } + if (stop < 0) { + stop += self->ob_size; + if (stop < 0) + stop = 0; + } + else if (stop > self->ob_size) + stop = self->ob_size; + for (i = start; i < stop; i++) { + int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ); + if (cmp > 0) + return PyInt_FromLong((long)i); + else if (cmp < 0) + return NULL; + } + PyErr_SetString(PyExc_ValueError, "list.index(x): x not in list"); + return NULL; +} + +static PyObject * +listcount(PyListObject *self, PyObject *v) +{ + int count = 0; + int i; + + for (i = 0; i < self->ob_size; i++) { + int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ); + if (cmp > 0) + count++; + else if (cmp < 0) + return NULL; + } + return PyInt_FromLong((long)count); +} + +static PyObject * +listremove(PyListObject *self, PyObject *v) +{ + int i; + + for (i = 0; i < self->ob_size; i++) { + int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ); + if (cmp > 0) { + if (list_ass_slice(self, i, i+1, + (PyObject *)NULL) != 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; + } + else if (cmp < 0) + return NULL; + } + PyErr_SetString(PyExc_ValueError, "list.remove(x): x not in list"); + return NULL; +} + +static int +list_traverse(PyListObject *o, visitproc visit, void *arg) +{ + int i, err; + PyObject *x; + + for (i = o->ob_size; --i >= 0; ) { + x = o->ob_item[i]; + if (x != NULL) { + err = visit(x, arg); + if (err) + return err; + } + } + return 0; +} + +static int +list_clear(PyListObject *lp) +{ + (void) PyList_SetSlice((PyObject *)lp, 0, lp->ob_size, 0); + return 0; +} + +static PyObject * +list_richcompare(PyObject *v, PyObject *w, int op) +{ + PyListObject *vl, *wl; + int i; + + if (!PyList_Check(v) || !PyList_Check(w)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + vl = (PyListObject *)v; + wl = (PyListObject *)w; + + if (vl->ob_size != wl->ob_size && (op == Py_EQ || op == Py_NE)) { + /* Shortcut: if the lengths differ, the lists differ */ + PyObject *res; + if (op == Py_EQ) + res = Py_False; + else + res = Py_True; + Py_INCREF(res); + return res; + } + + /* Search for the first index where items are different */ + for (i = 0; i < vl->ob_size && i < wl->ob_size; i++) { + int k = PyObject_RichCompareBool(vl->ob_item[i], + wl->ob_item[i], Py_EQ); + if (k < 0) + return NULL; + if (!k) + break; + } + + if (i >= vl->ob_size || i >= wl->ob_size) { + /* No more items to compare -- compare sizes */ + int vs = vl->ob_size; + int ws = wl->ob_size; + int cmp; + PyObject *res; + switch (op) { + case Py_LT: cmp = vs < ws; break; + case Py_LE: cmp = vs <= ws; break; + case Py_EQ: cmp = vs == ws; break; + case Py_NE: cmp = vs != ws; break; + case Py_GT: cmp = vs > ws; break; + case Py_GE: cmp = vs >= ws; break; + default: return NULL; /* cannot happen */ + } + if (cmp) + res = Py_True; + else + res = Py_False; + Py_INCREF(res); + return res; + } + + /* We have an item that differs -- shortcuts for EQ/NE */ + if (op == Py_EQ) { + Py_INCREF(Py_False); + return Py_False; + } + if (op == Py_NE) { + Py_INCREF(Py_True); + return Py_True; + } + + /* Compare the final item again using the proper operator */ + return PyObject_RichCompare(vl->ob_item[i], wl->ob_item[i], op); +} + +/* Adapted from newer code by Tim */ +static int +list_fill(PyListObject *result, PyObject *v) +{ + PyObject *it; /* iter(v) */ + int n; /* guess for result list size */ + int i; + + n = result->ob_size; + + /* Special-case list(a_list), for speed. */ + if (PyList_Check(v)) { + if (v == (PyObject *)result) + return 0; /* source is destination, we're done */ + return list_ass_slice(result, 0, n, v); + } + + /* Empty previous contents */ + if (n != 0) { + if (list_ass_slice(result, 0, n, (PyObject *)NULL) != 0) + return -1; + } + + /* Get iterator. There may be some low-level efficiency to be gained + * by caching the tp_iternext slot instead of using PyIter_Next() + * later, but premature optimization is the root etc. + */ + it = PyObject_GetIter(v); + if (it == NULL) + return -1; + + /* Guess a result list size. */ + n = -1; /* unknown */ + if (PySequence_Check(v) && + v->ob_type->tp_as_sequence->sq_length) { + n = PySequence_Size(v); + if (n < 0) + PyErr_Clear(); + } + if (n < 0) + n = 8; /* arbitrary */ + NRESIZE(result->ob_item, PyObject*, n); + if (result->ob_item == NULL) { + PyErr_NoMemory(); + goto error; + } + memset(result->ob_item, 0, sizeof(*result->ob_item) * n); + result->ob_size = n; + + /* Run iterator to exhaustion. */ + for (i = 0; ; i++) { + PyObject *item = PyIter_Next(it); + if (item == NULL) { + if (PyErr_Occurred()) + goto error; + break; + } + if (i < n) + PyList_SET_ITEM(result, i, item); /* steals ref */ + else { + int status = ins1(result, result->ob_size, item); + Py_DECREF(item); /* append creates a new ref */ + if (status < 0) + goto error; + } + } + + /* Cut back result list if initial guess was too large. */ + if (i < n && result != NULL) { + if (list_ass_slice(result, i, n, (PyObject *)NULL) != 0) + goto error; + } + Py_DECREF(it); + return 0; + + error: + Py_DECREF(it); + return -1; +} + +static int +list_init(PyListObject *self, PyObject *args, PyObject *kw) +{ + PyObject *arg = NULL; + static char *kwlist[] = {"sequence", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kw, "|O:list", kwlist, &arg)) + return -1; + if (arg != NULL) + return list_fill(self, arg); + if (self->ob_size > 0) + return list_ass_slice(self, 0, self->ob_size, (PyObject*)NULL); + return 0; +} + +static long +list_nohash(PyObject *self) +{ + PyErr_SetString(PyExc_TypeError, "list objects are unhashable"); + return -1; +} + +PyDoc_STRVAR(append_doc, +"L.append(object) -- append object to end"); +PyDoc_STRVAR(extend_doc, +"L.extend(iterable) -- extend list by appending elements from the iterable"); +PyDoc_STRVAR(insert_doc, +"L.insert(index, object) -- insert object before index"); +PyDoc_STRVAR(pop_doc, +"L.pop([index]) -> item -- remove and return item at index (default last)"); +PyDoc_STRVAR(remove_doc, +"L.remove(value) -- remove first occurrence of value"); +PyDoc_STRVAR(index_doc, +"L.index(value, [start, [stop]]) -> integer -- return first index of value"); +PyDoc_STRVAR(count_doc, +"L.count(value) -> integer -- return number of occurrences of value"); +PyDoc_STRVAR(reverse_doc, +"L.reverse() -- reverse *IN PLACE*"); +PyDoc_STRVAR(sort_doc, +"L.sort(cmpfunc=None) -- stable sort *IN PLACE*; cmpfunc(x, y) -> -1, 0, 1"); + +static PyMethodDef list_methods[] = { + {"append", (PyCFunction)listappend, METH_O, append_doc}, + {"insert", (PyCFunction)listinsert, METH_VARARGS, insert_doc}, + {"extend", (PyCFunction)listextend, METH_O, extend_doc}, + {"pop", (PyCFunction)listpop, METH_VARARGS, pop_doc}, + {"remove", (PyCFunction)listremove, METH_O, remove_doc}, + {"index", (PyCFunction)listindex, METH_VARARGS, index_doc}, + {"count", (PyCFunction)listcount, METH_O, count_doc}, + {"reverse", (PyCFunction)listreverse, METH_NOARGS, reverse_doc}, + {"sort", (PyCFunction)listsort, METH_VARARGS, sort_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PySequenceMethods list_as_sequence = { + (inquiry)list_length, /* sq_length */ + (binaryfunc)list_concat, /* sq_concat */ + (intargfunc)list_repeat, /* sq_repeat */ + (intargfunc)list_item, /* sq_item */ + (intintargfunc)list_slice, /* sq_slice */ + (intobjargproc)list_ass_item, /* sq_ass_item */ + (intintobjargproc)list_ass_slice, /* sq_ass_slice */ + (objobjproc)list_contains, /* sq_contains */ + (binaryfunc)list_inplace_concat, /* sq_inplace_concat */ + (intargfunc)list_inplace_repeat, /* sq_inplace_repeat */ +}; + +PyDoc_STRVAR(list_doc, +"list() -> new list\n" +"list(sequence) -> new list initialized from sequence's items"); + +static PyObject *list_iter(PyObject *seq); + +static PyObject * +list_subscript(PyListObject* self, PyObject* item) +{ + if (PyInt_Check(item)) { + long i = PyInt_AS_LONG(item); + if (i < 0) + i += PyList_GET_SIZE(self); + return list_item(self, i); + } + else if (PyLong_Check(item)) { + long i = PyLong_AsLong(item); + if (i == -1 && PyErr_Occurred()) + return NULL; + if (i < 0) + i += PyList_GET_SIZE(self); + return list_item(self, i); + } + else if (PySlice_Check(item)) { + int start, stop, step, slicelength, cur, i; + PyObject* result; + PyObject* it; + + if (PySlice_GetIndicesEx((PySliceObject*)item, self->ob_size, + &start, &stop, &step, &slicelength) < 0) { + return NULL; + } + + if (slicelength <= 0) { + return PyList_New(0); + } + else { + result = PyList_New(slicelength); + if (!result) return NULL; + + for (cur = start, i = 0; i < slicelength; + cur += step, i++) { + it = PyList_GET_ITEM(self, cur); + Py_INCREF(it); + PyList_SET_ITEM(result, i, it); + } + + return result; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "list indices must be integers"); + return NULL; + } +} + +static int +list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value) +{ + if (PyInt_Check(item)) { + long i = PyInt_AS_LONG(item); + if (i < 0) + i += PyList_GET_SIZE(self); + return list_ass_item(self, i, value); + } + else if (PyLong_Check(item)) { + long i = PyLong_AsLong(item); + if (i == -1 && PyErr_Occurred()) + return -1; + if (i < 0) + i += PyList_GET_SIZE(self); + return list_ass_item(self, i, value); + } + else if (PySlice_Check(item)) { + int start, stop, step, slicelength; + + if (PySlice_GetIndicesEx((PySliceObject*)item, self->ob_size, + &start, &stop, &step, &slicelength) < 0) { + return -1; + } + + /* treat L[slice(a,b)] = v _exactly_ like L[a:b] = v */ + if (step == 1 && ((PySliceObject*)item)->step == Py_None) + return list_ass_slice(self, start, stop, value); + + if (value == NULL) { + /* delete slice */ + PyObject **garbage, **it; + int cur, i, j; + + if (slicelength <= 0) + return 0; + + if (step < 0) { + stop = start + 1; + start = stop + step*(slicelength - 1) - 1; + step = -step; + } + + garbage = (PyObject**) + PyMem_MALLOC(slicelength*sizeof(PyObject*)); + + /* drawing pictures might help + understand these for loops */ + for (cur = start, i = 0; + cur < stop; + cur += step, i++) { + int lim = step; + + garbage[i] = PyList_GET_ITEM(self, cur); + + if (cur + step >= self->ob_size) { + lim = self->ob_size - cur - 1; + } + + for (j = 0; j < lim; j++) { + PyList_SET_ITEM(self, cur + j - i, + PyList_GET_ITEM(self, + cur + j + 1)); + } + } + for (cur = start + slicelength*step + 1; + cur < self->ob_size; cur++) { + PyList_SET_ITEM(self, cur - slicelength, + PyList_GET_ITEM(self, cur)); + } + self->ob_size -= slicelength; + it = self->ob_item; + NRESIZE(it, PyObject*, self->ob_size); + self->ob_item = it; + + for (i = 0; i < slicelength; i++) { + Py_DECREF(garbage[i]); + } + PyMem_FREE(garbage); + + return 0; + } + else { + /* assign slice */ + PyObject **garbage, *ins, *seq; + int cur, i; + + /* protect against a[::-1] = a */ + if (self == (PyListObject*)value) { + seq = list_slice((PyListObject*)value, 0, + PyList_GET_SIZE(value)); + } + else { + char msg[256]; + PyOS_snprintf(msg, sizeof(msg), + "must assign sequence (not \"%.200s\") to extended slice", + value->ob_type->tp_name); + seq = PySequence_Fast(value, msg); + if (!seq) + return -1; + } + + if (PySequence_Fast_GET_SIZE(seq) != slicelength) { + PyErr_Format(PyExc_ValueError, + "attempt to assign sequence of size %d to extended slice of size %d", + PySequence_Fast_GET_SIZE(seq), + slicelength); + Py_DECREF(seq); + return -1; + } + + if (!slicelength) { + Py_DECREF(seq); + return 0; + } + + garbage = (PyObject**) + PyMem_MALLOC(slicelength*sizeof(PyObject*)); + + for (cur = start, i = 0; i < slicelength; + cur += step, i++) { + garbage[i] = PyList_GET_ITEM(self, cur); + + ins = PySequence_Fast_GET_ITEM(seq, i); + Py_INCREF(ins); + PyList_SET_ITEM(self, cur, ins); + } + + for (i = 0; i < slicelength; i++) { + Py_DECREF(garbage[i]); + } + + PyMem_FREE(garbage); + Py_DECREF(seq); + + return 0; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "list indices must be integers"); + return -1; + } +} + +static PyMappingMethods list_as_mapping = { + (inquiry)list_length, + (binaryfunc)list_subscript, + (objobjargproc)list_ass_subscript +}; + +PyTypeObject PyList_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "list", + sizeof(PyListObject), + 0, + (destructor)list_dealloc, /* tp_dealloc */ + (printfunc)list_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)list_repr, /* tp_repr */ + 0, /* tp_as_number */ + &list_as_sequence, /* tp_as_sequence */ + &list_as_mapping, /* tp_as_mapping */ + list_nohash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + list_doc, /* tp_doc */ + (traverseproc)list_traverse, /* tp_traverse */ + (inquiry)list_clear, /* tp_clear */ + list_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + list_iter, /* tp_iter */ + 0, /* tp_iternext */ + list_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)list_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + + +/*********************** List Iterator **************************/ + +typedef struct { + PyObject_HEAD + long it_index; + PyListObject *it_seq; /* Set to NULL when iterator is exhausted */ +} listiterobject; + +PyTypeObject PyListIter_Type; + +static PyObject * +list_iter(PyObject *seq) +{ + listiterobject *it; + + if (!PyList_Check(seq)) { + PyErr_BadInternalCall(); + return NULL; + } + it = PyObject_GC_New(listiterobject, &PyListIter_Type); + if (it == NULL) + return NULL; + it->it_index = 0; + Py_INCREF(seq); + it->it_seq = (PyListObject *)seq; + _PyObject_GC_TRACK(it); + return (PyObject *)it; +} + +static void +listiter_dealloc(listiterobject *it) +{ + _PyObject_GC_UNTRACK(it); + Py_XDECREF(it->it_seq); + PyObject_GC_Del(it); +} + +static int +listiter_traverse(listiterobject *it, visitproc visit, void *arg) +{ + if (it->it_seq == NULL) + return 0; + return visit((PyObject *)it->it_seq, arg); +} + +static PyObject * +listiter_next(listiterobject *it) +{ + PyListObject *seq; + PyObject *item; + + assert(it != NULL); + seq = it->it_seq; + if (seq == NULL) + return NULL; + assert(PyList_Check(seq)); + + if (it->it_index < PyList_GET_SIZE(seq)) { + item = PyList_GET_ITEM(seq, it->it_index); + ++it->it_index; + Py_INCREF(item); + return item; + } + + Py_DECREF(seq); + it->it_seq = NULL; + return NULL; +} + +PyTypeObject PyListIter_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "listiterator", /* tp_name */ + sizeof(listiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)listiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)listiter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)listiter_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/listsort.txt b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/listsort.txt new file mode 100644 index 00000000..3ccae1b5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/listsort.txt @@ -0,0 +1,667 @@ +Intro +----- +This describes an adaptive, stable, natural mergesort, modestly called +timsort (hey, I earned it ). It has supernatural performance on many +kinds of partially ordered arrays (less than lg(N!) comparisons needed, and +as few as N-1), yet as fast as Python's previous highly tuned samplesort +hybrid on random arrays. + +In a nutshell, the main routine marches over the array once, left to right, +alternately identifying the next run, then merging it into the previous +runs "intelligently". Everything else is complication for speed, and some +hard-won measure of memory efficiency. + + +Comparison with Python's Samplesort Hybrid +------------------------------------------ ++ timsort can require a temp array containing as many as N//2 pointers, + which means as many as 2*N extra bytes on 32-bit boxes. It can be + expected to require a temp array this large when sorting random data; on + data with significant structure, it may get away without using any extra + heap memory. This appears to be the strongest argument against it, but + compared to the size of an object, 2 temp bytes worst-case (also expected- + case for random data) doesn't scare me much. + + It turns out that Perl is moving to a stable mergesort, and the code for + that appears always to require a temp array with room for at least N + pointers. (Note that I wouldn't want to do that even if space weren't an + issue; I believe its efforts at memory frugality also save timsort + significant pointer-copying costs, and allow it to have a smaller working + set.) + ++ Across about four hours of generating random arrays, and sorting them + under both methods, samplesort required about 1.5% more comparisons + (the program is at the end of this file). + ++ In real life, this may be faster or slower on random arrays than + samplesort was, depending on platform quirks. Since it does fewer + comparisons on average, it can be expected to do better the more + expensive a comparison function is. OTOH, it does more data movement + (pointer copying) than samplesort, and that may negate its small + comparison advantage (depending on platform quirks) unless comparison + is very expensive. + ++ On arrays with many kinds of pre-existing order, this blows samplesort out + of the water. It's significantly faster than samplesort even on some + cases samplesort was special-casing the snot out of. I believe that lists + very often do have exploitable partial order in real life, and this is the + strongest argument in favor of timsort (indeed, samplesort's special cases + for extreme partial order are appreciated by real users, and timsort goes + much deeper than those, in particular naturally covering every case where + someone has suggested "and it would be cool if list.sort() had a special + case for this too ... and for that ..."). + ++ Here are exact comparison counts across all the tests in sortperf.py, + when run with arguments "15 20 1". + + First the trivial cases, trivial for samplesort because it special-cased + them, and trivial for timsort because it naturally works on runs. Within + an "n" block, the first line gives the # of compares done by samplesort, + the second line by timsort, and the third line is the percentage by + which the samplesort count exceeds the timsort count: + + n \sort /sort =sort +------- ------ ------ ------ + 32768 32768 32767 32767 samplesort + 32767 32767 32767 timsort + 0.00% 0.00% 0.00% (samplesort - timsort) / timsort + + 65536 65536 65535 65535 + 65535 65535 65535 + 0.00% 0.00% 0.00% + + 131072 131072 131071 131071 + 131071 131071 131071 + 0.00% 0.00% 0.00% + + 262144 262144 262143 262143 + 262143 262143 262143 + 0.00% 0.00% 0.00% + + 524288 524288 524287 524287 + 524287 524287 524287 + 0.00% 0.00% 0.00% + +1048576 1048576 1048575 1048575 + 1048575 1048575 1048575 + 0.00% 0.00% 0.00% + + The algorithms are effectively identical in these cases, except that + timsort does one less compare in \sort. + + Now for the more interesting cases. lg(n!) is the information-theoretic + limit for the best any comparison-based sorting algorithm can do on + average (across all permutations). When a method gets significantly + below that, it's either astronomically lucky, or is finding exploitable + structure in the data. + + n lg(n!) *sort 3sort +sort %sort ~sort !sort +------- ------- ------ ------- ------- ------ ------- -------- + 32768 444255 453096 453614 32908 452871 130491 469141 old + 448885 33016 33007 50426 182083 65534 new + 0.94% 1273.92% -0.30% 798.09% -28.33% 615.87% %ch from new + + 65536 954037 972699 981940 65686 973104 260029 1004607 + 962991 65821 65808 101667 364341 131070 + 1.01% 1391.83% -0.19% 857.15% -28.63% 666.47% + + 131072 2039137 2101881 2091491 131232 2092894 554790 2161379 + 2057533 131410 131361 206193 728871 262142 + 2.16% 1491.58% -0.10% 915.02% -23.88% 724.51% + + 262144 4340409 4464460 4403233 262314 4445884 1107842 4584560 + 4377402 262437 262459 416347 1457945 524286 + 1.99% 1577.82% -0.06% 967.83% -24.01% 774.44% + + 524288 9205096 9453356 9408463 524468 9441930 2218577 9692015 + 9278734 524580 524633 837947 2916107 1048574 + 1.88% 1693.52% -0.03% 1026.79% -23.92% 824.30% + +1048576 19458756 19950272 19838588 1048766 19912134 4430649 20434212 + 19606028 1048958 1048941 1694896 5832445 2097150 + 1.76% 1791.27% -0.02% 1074.83% -24.03% 874.38% + + Discussion of cases: + + *sort: There's no structure in random data to exploit, so the theoretical + limit is lg(n!). Both methods get close to that, and timsort is hugging + it (indeed, in a *marginal* sense, it's a spectacular improvement -- + there's only about 1% left before hitting the wall, and timsort knows + darned well it's doing compares that won't pay on random data -- but so + does the samplesort hybrid). For contrast, Hoare's original random-pivot + quicksort does about 39% more compares than the limit, and the median-of-3 + variant about 19% more. + + 3sort, %sort, and !sort: No contest; there's structure in this data, but + not of the specific kinds samplesort special-cases. Note that structure + in !sort wasn't put there on purpose -- it was crafted as a worst case for + a previous quicksort implementation. That timsort nails it came as a + surprise to me (although it's obvious in retrospect). + + +sort: samplesort special-cases this data, and does a few less compares + than timsort. However, timsort runs this case significantly faster on all + boxes we have timings for, because timsort is in the business of merging + runs efficiently, while samplesort does much more data movement in this + (for it) special case. + + ~sort: samplesort's special cases for large masses of equal elements are + extremely effective on ~sort's specific data pattern, and timsort just + isn't going to get close to that, despite that it's clearly getting a + great deal of benefit out of the duplicates (the # of compares is much less + than lg(n!)). ~sort has a perfectly uniform distribution of just 4 + distinct values, and as the distribution gets more skewed, samplesort's + equal-element gimmicks become less effective, while timsort's adaptive + strategies find more to exploit; in a database supplied by Kevin Altis, a + sort on its highly skewed "on which stock exchange does this company's + stock trade?" field ran over twice as fast under timsort. + + However, despite that timsort does many more comparisons on ~sort, and + that on several platforms ~sort runs highly significantly slower under + timsort, on other platforms ~sort runs highly significantly faster under + timsort. No other kind of data has shown this wild x-platform behavior, + and we don't have an explanation for it. The only thing I can think of + that could transform what "should be" highly significant slowdowns into + highly significant speedups on some boxes are catastrophic cache effects + in samplesort. + + But timsort "should be" slower than samplesort on ~sort, so it's hard + to count that it isn't on some boxes as a strike against it . + ++ Here's the highwater mark for the number of heap-based temp slots (4 + bytes each on this box) needed by each test, again with arguments + "15 20 1": + + 2**i *sort \sort /sort 3sort +sort %sort ~sort =sort !sort + 32768 16384 0 0 6256 0 10821 12288 0 16383 + 65536 32766 0 0 21652 0 31276 24576 0 32767 + 131072 65534 0 0 17258 0 58112 49152 0 65535 + 262144 131072 0 0 35660 0 123561 98304 0 131071 + 524288 262142 0 0 31302 0 212057 196608 0 262143 +1048576 524286 0 0 312438 0 484942 393216 0 524287 + + Discussion: The tests that end up doing (close to) perfectly balanced + merges (*sort, !sort) need all N//2 temp slots (or almost all). ~sort + also ends up doing balanced merges, but systematically benefits a lot from + the preliminary pre-merge searches described under "Merge Memory" later. + %sort approaches having a balanced merge at the end because the random + selection of elements to replace is expected to produce an out-of-order + element near the midpoint. \sort, /sort, =sort are the trivial one-run + cases, needing no merging at all. +sort ends up having one very long run + and one very short, and so gets all the temp space it needs from the small + temparray member of the MergeState struct (note that the same would be + true if the new random elements were prefixed to the sorted list instead, + but not if they appeared "in the middle"). 3sort approaches N//3 temp + slots twice, but the run lengths that remain after 3 random exchanges + clearly has very high variance. + + +A detailed description of timsort follows. + +Runs +---- +count_run() returns the # of elements in the next run. A run is either +"ascending", which means non-decreasing: + + a0 <= a1 <= a2 <= ... + +or "descending", which means strictly decreasing: + + a0 > a1 > a2 > ... + +Note that a run is always at least 2 long, unless we start at the array's +last element. + +The definition of descending is strict, because the main routine reverses +a descending run in-place, transforming a descending run into an ascending +run. Reversal is done via the obvious fast "swap elements starting at each +end, and converge at the middle" method, and that can violate stability if +the slice contains any equal elements. Using a strict definition of +descending ensures that a descending run contains distinct elements. + +If an array is random, it's very unlikely we'll see long runs. If a natural +run contains less than minrun elements (see next section), the main loop +artificially boosts it to minrun elements, via a stable binary insertion sort +applied to the right number of array elements following the short natural +run. In a random array, *all* runs are likely to be minrun long as a +result. This has two primary good effects: + +1. Random data strongly tends then toward perfectly balanced (both runs have + the same length) merges, which is the most efficient way to proceed when + data is random. + +2. Because runs are never very short, the rest of the code doesn't make + heroic efforts to shave a few cycles off per-merge overheads. For + example, reasonable use of function calls is made, rather than trying to + inline everything. Since there are no more than N/minrun runs to begin + with, a few "extra" function calls per merge is barely measurable. + + +Computing minrun +---------------- +If N < 64, minrun is N. IOW, binary insertion sort is used for the whole +array then; it's hard to beat that given the overheads of trying something +fancier. + +When N is a power of 2, testing on random data showed that minrun values of +16, 32, 64 and 128 worked about equally well. At 256 the data-movement cost +in binary insertion sort clearly hurt, and at 8 the increase in the number +of function calls clearly hurt. Picking *some* power of 2 is important +here, so that the merges end up perfectly balanced (see next section). We +pick 32 as a good value in the sweet range; picking a value at the low end +allows the adaptive gimmicks more opportunity to exploit shorter natural +runs. + +Because sortperf.py only tries powers of 2, it took a long time to notice +that 32 isn't a good choice for the general case! Consider N=2112: + +>>> divmod(2112, 32) +(66, 0) +>>> + +If the data is randomly ordered, we're very likely to end up with 66 runs +each of length 32. The first 64 of these trigger a sequence of perfectly +balanced merges (see next section), leaving runs of lengths 2048 and 64 to +merge at the end. The adaptive gimmicks can do that with fewer than 2048+64 +compares, but it's still more compares than necessary, and-- mergesort's +bugaboo relative to samplesort --a lot more data movement (O(N) copies just +to get 64 elements into place). + +If we take minrun=33 in this case, then we're very likely to end up with 64 +runs each of length 33, and then all merges are perfectly balanced. Better! + +What we want to avoid is picking minrun such that in + + q, r = divmod(N, minrun) + +q is a power of 2 and r>0 (then the last merge only gets r elements into +place, and r < minrun is small compared to N), or q a little larger than a +power of 2 regardless of r (then we've got a case similar to "2112", again +leaving too little work for the last merge to do). + +Instead we pick a minrun in range(32, 65) such that N/minrun is exactly a +power of 2, or if that isn't possible, is close to, but strictly less than, +a power of 2. This is easier to do than it may sound: take the first 6 +bits of N, and add 1 if any of the remaining bits are set. In fact, that +rule covers every case in this section, including small N and exact powers +of 2; merge_compute_minrun() is a deceptively simple function. + + +The Merge Pattern +----------------- +In order to exploit regularities in the data, we're merging on natural +run lengths, and they can become wildly unbalanced. That's a Good Thing +for this sort! It means we have to find a way to manage an assortment of +potentially very different run lengths, though. + +Stability constrains permissible merging patterns. For example, if we have +3 consecutive runs of lengths + + A:10000 B:20000 C:10000 + +we dare not merge A with C first, because if A, B and C happen to contain +a common element, it would get out of order wrt its occurence(s) in B. The +merging must be done as (A+B)+C or A+(B+C) instead. + +So merging is always done on two consecutive runs at a time, and in-place, +although this may require some temp memory (more on that later). + +When a run is identified, its base address and length are pushed on a stack +in the MergeState struct. merge_collapse() is then called to see whether it +should merge it with preceding run(s). We would like to delay merging as +long as possible in order to exploit patterns that may come up later, but we +like even more to do merging as soon as possible to exploit that the run just +found is still high in the memory hierarchy. We also can't delay merging +"too long" because it consumes memory to remember the runs that are still +unmerged, and the stack has a fixed size. + +What turned out to be a good compromise maintains two invariants on the +stack entries, where A, B and C are the lengths of the three righmost not-yet +merged slices: + +1. A > B+C +2. B > C + +Note that, by induction, #2 implies the lengths of pending runs form a +decreasing sequence. #1 implies that, reading the lengths right to left, +the pending-run lengths grow at least as fast as the Fibonacci numbers. +Therefore the stack can never grow larger than about log_base_phi(N) entries, +where phi = (1+sqrt(5))/2 ~= 1.618. Thus a small # of stack slots suffice +for very large arrays. + +If A <= B+C, the smaller of A and C is merged with B (ties favor C, for the +freshness-in-cache reason), and the new run replaces the A,B or B,C entries; +e.g., if the last 3 entries are + + A:30 B:20 C:10 + +then B is merged with C, leaving + + A:30 BC:30 + +on the stack. Or if they were + + A:500 B:400: C:1000 + +then A is merged with B, leaving + + AB:900 C:1000 + +on the stack. + +In both examples, the stack configuration after the merge still violates +invariant #2, and merge_collapse() goes on to continue merging runs until +both invariants are satisfied. As an extreme case, suppose we didn't do the +minrun gimmick, and natural runs were of lengths 128, 64, 32, 16, 8, 4, 2, +and 2. Nothing would get merged until the final 2 was seen, and that would +trigger 7 perfectly balanced merges. + +The thrust of these rules when they trigger merging is to balance the run +lengths as closely as possible, while keeping a low bound on the number of +runs we have to remember. This is maximally effective for random data, +where all runs are likely to be of (artificially forced) length minrun, and +then we get a sequence of perfectly balanced merges (with, perhaps, some +oddballs at the end). + +OTOH, one reason this sort is so good for partly ordered data has to do +with wildly unbalanced run lengths. + + +Merge Memory +------------ +Merging adjacent runs of lengths A and B in-place is very difficult. +Theoretical constructions are known that can do it, but they're too difficult +and slow for practical use. But if we have temp memory equal to min(A, B), +it's easy. + +If A is smaller (function merge_lo), copy A to a temp array, leave B alone, +and then we can do the obvious merge algorithm left to right, from the temp +area and B, starting the stores into where A used to live. There's always a +free area in the original area comprising a number of elements equal to the +number not yet merged from the temp array (trivially true at the start; +proceed by induction). The only tricky bit is that if a comparison raises an +exception, we have to remember to copy the remaining elements back in from +the temp area, lest the array end up with duplicate entries from B. But +that's exactly the same thing we need to do if we reach the end of B first, +so the exit code is pleasantly common to both the normal and error cases. + +If B is smaller (function merge_hi, which is merge_lo's "mirror image"), +much the same, except that we need to merge right to left, copying B into a +temp array and starting the stores at the right end of where B used to live. + +A refinement: When we're about to merge adjacent runs A and B, we first do +a form of binary search (more on that later) to see where B[0] should end up +in A. Elements in A preceding that point are already in their final +positions, effectively shrinking the size of A. Likewise we also search to +see where A[-1] should end up in B, and elements of B after that point can +also be ignored. This cuts the amount of temp memory needed by the same +amount. + +These preliminary searches may not pay off, and can be expected *not* to +repay their cost if the data is random. But they can win huge in all of +time, copying, and memory savings when they do pay, so this is one of the +"per-merge overheads" mentioned above that we're happy to endure because +there is at most one very short run. It's generally true in this algorithm +that we're willing to gamble a little to win a lot, even though the net +expectation is negative for random data. + + +Merge Algorithms +---------------- +merge_lo() and merge_hi() are where the bulk of the time is spent. merge_lo +deals with runs where A <= B, and merge_hi where A > B. They don't know +whether the data is clustered or uniform, but a lovely thing about merging +is that many kinds of clustering "reveal themselves" by how many times in a +row the winning merge element comes from the same run. We'll only discuss +merge_lo here; merge_hi is exactly analogous. + +Merging begins in the usual, obvious way, comparing the first element of A +to the first of B, and moving B[0] to the merge area if it's less than A[0], +else moving A[0] to the merge area. Call that the "one pair at a time" +mode. The only twist here is keeping track of how many times in a row "the +winner" comes from the same run. + +If that count reaches MIN_GALLOP, we switch to "galloping mode". Here +we *search* B for where A[0] belongs, and move over all the B's before +that point in one chunk to the merge area, then move A[0] to the merge +area. Then we search A for where B[0] belongs, and similarly move a +slice of A in one chunk. Then back to searching B for where A[0] belongs, +etc. We stay in galloping mode until both searches find slices to copy +less than MIN_GALLOP elements long, at which point we go back to one-pair- +at-a-time mode. + +A refinement: The MergeState struct contains the value of min_gallop that +controls when we enter galloping mode, initialized to MIN_GALLOP. +merge_lo() and merge_hi() adjust this higher when galloping isn't paying +off, and lower when it is. + + +Galloping +--------- +Still without loss of generality, assume A is the shorter run. In galloping +mode, we first look for A[0] in B. We do this via "galloping", comparing +A[0] in turn to B[0], B[1], B[3], B[7], ..., B[2**j - 1], ..., until finding +the k such that B[2**(k-1) - 1] < A[0] <= B[2**k - 1]. This takes at most +roughly lg(B) comparisons, and, unlike a straight binary search, favors +finding the right spot early in B (more on that later). + +After finding such a k, the region of uncertainty is reduced to 2**(k-1) - 1 +consecutive elements, and a straight binary search requires exactly k-1 +additional comparisons to nail it. Then we copy all the B's up to that +point in one chunk, and then copy A[0]. Note that no matter where A[0] +belongs in B, the combination of galloping + binary search finds it in no +more than about 2*lg(B) comparisons. + +If we did a straight binary search, we could find it in no more than +ceiling(lg(B+1)) comparisons -- but straight binary search takes that many +comparisons no matter where A[0] belongs. Straight binary search thus loses +to galloping unless the run is quite long, and we simply can't guess +whether it is in advance. + +If data is random and runs have the same length, A[0] belongs at B[0] half +the time, at B[1] a quarter of the time, and so on: a consecutive winning +sub-run in B of length k occurs with probability 1/2**(k+1). So long +winning sub-runs are extremely unlikely in random data, and guessing that a +winning sub-run is going to be long is a dangerous game. + +OTOH, if data is lopsided or lumpy or contains many duplicates, long +stretches of winning sub-runs are very likely, and cutting the number of +comparisons needed to find one from O(B) to O(log B) is a huge win. + +Galloping compromises by getting out fast if there isn't a long winning +sub-run, yet finding such very efficiently when they exist. + +I first learned about the galloping strategy in a related context; see: + + "Adaptive Set Intersections, Unions, and Differences" (2000) + Erik D. Demaine, Alejandro López-Ortiz, J. Ian Munro + +and its followup(s). An earlier paper called the same strategy +"exponential search": + + "Optimistic Sorting and Information Theoretic Complexity" + Peter McIlroy + SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms), pp + 467-474, Austin, Texas, 25-27 January 1993. + +and it probably dates back to an earlier paper by Bentley and Yao. The +McIlory paper in particular has good analysis of a mergesort that's +probably strongly related to this one in its galloping strategy. + + +Galloping with a Broken Leg +--------------------------- +So why don't we always gallop? Because it can lose, on two counts: + +1. While we're willing to endure small per-merge overheads, per-comparison + overheads are a different story. Calling Yet Another Function per + comparison is expensive, and gallop_left() and gallop_right() are + too long-winded for sane inlining. + +2. Galloping can-- alas --require more comparisons than linear one-at-time + search, depending on the data. + +#2 requires details. If A[0] belongs before B[0], galloping requires 1 +compare to determine that, same as linear search, except it costs more +to call the gallop function. If A[0] belongs right before B[1], galloping +requires 2 compares, again same as linear search. On the third compare, +galloping checks A[0] against B[3], and if it's <=, requires one more +compare to determine whether A[0] belongs at B[2] or B[3]. That's a total +of 4 compares, but if A[0] does belong at B[2], linear search would have +discovered that in only 3 compares, and that's a huge loss! Really. It's +an increase of 33% in the number of compares needed, and comparisons are +expensive in Python. + +index in B where # compares linear # gallop # binary gallop +A[0] belongs search needs compares compares total +---------------- ----------------- -------- -------- ------ + 0 1 1 0 1 + + 1 2 2 0 2 + + 2 3 3 1 4 + 3 4 3 1 4 + + 4 5 4 2 6 + 5 6 4 2 6 + 6 7 4 2 6 + 7 8 4 2 6 + + 8 9 5 3 8 + 9 10 5 3 8 + 10 11 5 3 8 + 11 12 5 3 8 + ... + +In general, if A[0] belongs at B[i], linear search requires i+1 comparisons +to determine that, and galloping a total of 2*floor(lg(i))+2 comparisons. +The advantage of galloping is unbounded as i grows, but it doesn't win at +all until i=6. Before then, it loses twice (at i=2 and i=4), and ties +at the other values. At and after i=6, galloping always wins. + +We can't guess in advance when it's going to win, though, so we do one pair +at a time until the evidence seems strong that galloping may pay. MIN_GALLOP +is 7, and that's pretty strong evidence. However, if the data is random, it +simply will trigger galloping mode purely by luck every now and again, and +it's quite likely to hit one of the losing cases next. On the other hand, +in cases like ~sort, galloping always pays, and MIN_GALLOP is larger than it +"should be" then. So the MergeState struct keeps a min_gallop variable +that merge_lo and merge_hi adjust: the longer we stay in galloping mode, +the smaller min_gallop gets, making it easier to transition back to +galloping mode (if we ever leave it in the current merge, and at the +start of the next merge). But whenever the gallop loop doesn't pay, +min_gallop is increased by one, making it harder to transition back +to galloping mode (and again both within a merge and across merges). For +random data, this all but eliminates the gallop penalty: min_gallop grows +large enough that we almost never get into galloping mode. And for cases +like ~sort, min_gallop can fall to as low as 1. This seems to work well, +but in all it's a minor improvement over using a fixed MIN_GALLOP value. + + +Galloping Complication +---------------------- +The description above was for merge_lo. merge_hi has to merge "from the +other end", and really needs to gallop starting at the last element in a run +instead of the first. Galloping from the first still works, but does more +comparisons than it should (this is significant -- I timed it both ways). +For this reason, the gallop_left() and gallop_right() functions have a +"hint" argument, which is the index at which galloping should begin. So +galloping can actually start at any index, and proceed at offsets of 1, 3, +7, 15, ... or -1, -3, -7, -15, ... from the starting index. + +In the code as I type it's always called with either 0 or n-1 (where n is +the # of elements in a run). It's tempting to try to do something fancier, +melding galloping with some form of interpolation search; for example, if +we're merging a run of length 1 with a run of length 10000, index 5000 is +probably a better guess at the final result than either 0 or 9999. But +it's unclear how to generalize that intuition usefully, and merging of +wildly unbalanced runs already enjoys excellent performance. + +~sort is a good example of when balanced runs could benefit from a better +hint value: to the extent possible, this would like to use a starting +offset equal to the previous value of acount/bcount. Doing so saves about +10% of the compares in ~sort. However, doing so is also a mixed bag, +hurting other cases. + + +Comparing Average # of Compares on Random Arrays +------------------------------------------------ +[NOTE: This was done when the new algorithm used about 0.1% more compares + on random data than does its current incarnation.] + +Here list.sort() is samplesort, and list.msort() this sort: + +""" +import random +from time import clock as now + +def fill(n): + from random import random + return [random() for i in xrange(n)] + +def mycmp(x, y): + global ncmp + ncmp += 1 + return cmp(x, y) + +def timeit(values, method): + global ncmp + X = values[:] + bound = getattr(X, method) + ncmp = 0 + t1 = now() + bound(mycmp) + t2 = now() + return t2-t1, ncmp + +format = "%5s %9.2f %11d" +f2 = "%5s %9.2f %11.2f" + +def drive(): + count = sst = sscmp = mst = mscmp = nelts = 0 + while True: + n = random.randrange(100000) + nelts += n + x = fill(n) + + t, c = timeit(x, 'sort') + sst += t + sscmp += c + + t, c = timeit(x, 'msort') + mst += t + mscmp += c + + count += 1 + if count % 10: + continue + + print "count", count, "nelts", nelts + print format % ("sort", sst, sscmp) + print format % ("msort", mst, mscmp) + print f2 % ("", (sst-mst)*1e2/mst, (sscmp-mscmp)*1e2/mscmp) + +drive() +""" + +I ran this on Windows and kept using the computer lightly while it was +running. time.clock() is wall-clock time on Windows, with better than +microsecond resolution. samplesort started with a 1.52% #-of-comparisons +disadvantage, fell quickly to 1.48%, and then fluctuated within that small +range. Here's the last chunk of output before I killed the job: + +count 2630 nelts 130906543 + sort 6110.80 1937887573 +msort 6002.78 1909389381 + 1.80 1.49 + +We've done nearly 2 billion comparisons apiece at Python speed there, and +that's enough . + +For random arrays of size 2 (yes, there are only 2 interesting ones), +samplesort has a 50%(!) comparison disadvantage. This is a consequence of +samplesort special-casing at most one ascending run at the start, then +falling back to the general case if it doesn't find an ascending run +immediately. The consequence is that it ends up using two compares to sort +[2, 1]. Gratifyingly, timsort doesn't do any special-casing, so had to be +taught how to deal with mixtures of ascending and descending runs +efficiently in all cases. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/longobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/longobject.c new file mode 100644 index 00000000..9c8fa9dc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/longobject.c @@ -0,0 +1,2912 @@ + +/* Long (arbitrary precision) integer object implementation */ + +/* XXX The functional organization of this file is terrible */ + +#include "Python.h" +#include "longintrepr.h" + +#include + +/* For long multiplication, use the O(N**2) school algorithm unless + * both operands contain more than KARATSUBA_CUTOFF digits (this + * being an internal Python long digit, in base BASE). + */ +#define KARATSUBA_CUTOFF 35 + +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +#undef MIN +#undef MAX +#define MAX(x, y) ((x) < (y) ? (y) : (x)) +#define MIN(x, y) ((x) > (y) ? (y) : (x)) + +/* Forward */ +static PyLongObject *long_normalize(PyLongObject *); +static PyLongObject *mul1(PyLongObject *, wdigit); +static PyLongObject *muladd1(PyLongObject *, wdigit, wdigit); +static PyLongObject *divrem1(PyLongObject *, digit, digit *); +static PyObject *long_format(PyObject *aa, int base, int addL); + +#define SIGCHECK(PyTryBlock) \ + if (--_Py_Ticker < 0) { \ + _Py_Ticker = _Py_CheckInterval; \ + if (PyErr_CheckSignals()) { PyTryBlock; } \ + } + +/* Normalize (remove leading zeros from) a long int object. + Doesn't attempt to free the storage--in most cases, due to the nature + of the algorithms used, this could save at most be one word anyway. */ + +static PyLongObject * +long_normalize(register PyLongObject *v) +{ + int j = ABS(v->ob_size); + register int i = j; + + while (i > 0 && v->ob_digit[i-1] == 0) + --i; + if (i != j) + v->ob_size = (v->ob_size < 0) ? -(i) : i; + return v; +} + +/* Allocate a new long int object with size digits. + Return NULL and set exception if we run out of memory. */ + +PyLongObject * +_PyLong_New(int size) +{ + return PyObject_NEW_VAR(PyLongObject, &PyLong_Type, size); +} + +PyObject * +_PyLong_Copy(PyLongObject *src) +{ + PyLongObject *result; + int i; + + assert(src != NULL); + i = src->ob_size; + if (i < 0) + i = -(i); + result = _PyLong_New(i); + if (result != NULL) { + result->ob_size = src->ob_size; + while (--i >= 0) + result->ob_digit[i] = src->ob_digit[i]; + } + return (PyObject *)result; +} + +/* Create a new long int object from a C long int */ + +PyObject * +PyLong_FromLong(long ival) +{ + PyLongObject *v; + unsigned long t; /* unsigned so >> doesn't propagate sign bit */ + int ndigits = 0; + int negative = 0; + + if (ival < 0) { + ival = -ival; + negative = 1; + } + + /* Count the number of Python digits. + We used to pick 5 ("big enough for anything"), but that's a + waste of time and space given that 5*15 = 75 bits are rarely + needed. */ + t = (unsigned long)ival; + while (t) { + ++ndigits; + t >>= SHIFT; + } + v = _PyLong_New(ndigits); + if (v != NULL) { + digit *p = v->ob_digit; + v->ob_size = negative ? -ndigits : ndigits; + t = (unsigned long)ival; + while (t) { + *p++ = (digit)(t & MASK); + t >>= SHIFT; + } + } + return (PyObject *)v; +} + +/* Create a new long int object from a C unsigned long int */ + +PyObject * +PyLong_FromUnsignedLong(unsigned long ival) +{ + PyLongObject *v; + unsigned long t; + int ndigits = 0; + + /* Count the number of Python digits. */ + t = (unsigned long)ival; + while (t) { + ++ndigits; + t >>= SHIFT; + } + v = _PyLong_New(ndigits); + if (v != NULL) { + digit *p = v->ob_digit; + v->ob_size = ndigits; + while (ival) { + *p++ = (digit)(ival & MASK); + ival >>= SHIFT; + } + } + return (PyObject *)v; +} + +/* Create a new long int object from a C double */ + +PyObject * +PyLong_FromDouble(double dval) +{ + PyLongObject *v; + double frac; + int i, ndig, expo, neg; + neg = 0; + if (Py_IS_INFINITY(dval)) { + PyErr_SetString(PyExc_OverflowError, + "cannot convert float infinity to long"); + return NULL; + } + if (dval < 0.0) { + neg = 1; + dval = -dval; + } + frac = frexp(dval, &expo); /* dval = frac*2**expo; 0.0 <= frac < 1.0 */ + if (expo <= 0) + return PyLong_FromLong(0L); + ndig = (expo-1) / SHIFT + 1; /* Number of 'digits' in result */ + v = _PyLong_New(ndig); + if (v == NULL) + return NULL; + frac = ldexp(frac, (expo-1) % SHIFT + 1); + for (i = ndig; --i >= 0; ) { + long bits = (long)frac; + v->ob_digit[i] = (digit) bits; + frac = frac - (double)bits; + frac = ldexp(frac, SHIFT); + } + if (neg) + v->ob_size = -(v->ob_size); + return (PyObject *)v; +} + +/* Get a C long int from a long int object. + Returns -1 and sets an error condition if overflow occurs. */ + +long +PyLong_AsLong(PyObject *vv) +{ + /* This version by Tim Peters */ + register PyLongObject *v; + unsigned long x, prev; + int i, sign; + + if (vv == NULL || !PyLong_Check(vv)) { + if (vv != NULL && PyInt_Check(vv)) + return PyInt_AsLong(vv); + PyErr_BadInternalCall(); + return -1; + } + v = (PyLongObject *)vv; + i = v->ob_size; + sign = 1; + x = 0; + if (i < 0) { + sign = -1; + i = -(i); + } + while (--i >= 0) { + prev = x; + x = (x << SHIFT) + v->ob_digit[i]; + if ((x >> SHIFT) != prev) + goto overflow; + } + /* Haven't lost any bits, but if the sign bit is set we're in + * trouble *unless* this is the min negative number. So, + * trouble iff sign bit set && (positive || some bit set other + * than the sign bit). + */ + if ((long)x < 0 && (sign > 0 || (x << 1) != 0)) + goto overflow; + return (long)x * sign; + + overflow: + PyErr_SetString(PyExc_OverflowError, + "long int too large to convert to int"); + return -1; +} + +/* Get a C unsigned long int from a long int object. + Returns -1 and sets an error condition if overflow occurs. */ + +unsigned long +PyLong_AsUnsignedLong(PyObject *vv) +{ + register PyLongObject *v; + unsigned long x, prev; + int i; + + if (vv == NULL || !PyLong_Check(vv)) { + PyErr_BadInternalCall(); + return (unsigned long) -1; + } + v = (PyLongObject *)vv; + i = v->ob_size; + x = 0; + if (i < 0) { + PyErr_SetString(PyExc_OverflowError, + "can't convert negative value to unsigned long"); + return (unsigned long) -1; + } + while (--i >= 0) { + prev = x; + x = (x << SHIFT) + v->ob_digit[i]; + if ((x >> SHIFT) != prev) { + PyErr_SetString(PyExc_OverflowError, + "long int too large to convert"); + return (unsigned long) -1; + } + } + return x; +} + +/* Get a C unsigned long int from a long int object, ignoring the high bits. + Returns -1 and sets an error condition if an error occurs. */ + +unsigned long +PyLong_AsUnsignedLongMask(PyObject *vv) +{ + register PyLongObject *v; + unsigned long x; + int i, sign; + + if (vv == NULL || !PyLong_Check(vv)) { + PyErr_BadInternalCall(); + return (unsigned long) -1; + } + v = (PyLongObject *)vv; + i = v->ob_size; + sign = 1; + x = 0; + if (i < 0) { + sign = -1; + i = -i; + } + while (--i >= 0) { + x = (x << SHIFT) + v->ob_digit[i]; + } + return x * sign; +} + +int +_PyLong_Sign(PyObject *vv) +{ + PyLongObject *v = (PyLongObject *)vv; + + assert(v != NULL); + assert(PyLong_Check(v)); + + return v->ob_size == 0 ? 0 : (v->ob_size < 0 ? -1 : 1); +} + +size_t +_PyLong_NumBits(PyObject *vv) +{ + PyLongObject *v = (PyLongObject *)vv; + size_t result = 0; + int ndigits; + + assert(v != NULL); + assert(PyLong_Check(v)); + ndigits = ABS(v->ob_size); + assert(ndigits == 0 || v->ob_digit[ndigits - 1] != 0); + if (ndigits > 0) { + digit msd = v->ob_digit[ndigits - 1]; + + result = (ndigits - 1) * SHIFT; + if (result / SHIFT != (size_t)ndigits - 1) + goto Overflow; + do { + ++result; + if (result == 0) + goto Overflow; + msd >>= 1; + } while (msd); + } + return result; + +Overflow: + PyErr_SetString(PyExc_OverflowError, "long has too many bits " + "to express in a platform size_t"); + return (size_t)-1; +} + +PyObject * +_PyLong_FromByteArray(const unsigned char* bytes, size_t n, + int little_endian, int is_signed) +{ + const unsigned char* pstartbyte;/* LSB of bytes */ + int incr; /* direction to move pstartbyte */ + const unsigned char* pendbyte; /* MSB of bytes */ + size_t numsignificantbytes; /* number of bytes that matter */ + size_t ndigits; /* number of Python long digits */ + PyLongObject* v; /* result */ + int idigit = 0; /* next free index in v->ob_digit */ + + if (n == 0) + return PyLong_FromLong(0L); + + if (little_endian) { + pstartbyte = bytes; + pendbyte = bytes + n - 1; + incr = 1; + } + else { + pstartbyte = bytes + n - 1; + pendbyte = bytes; + incr = -1; + } + + if (is_signed) + is_signed = *pendbyte >= 0x80; + + /* Compute numsignificantbytes. This consists of finding the most + significant byte. Leading 0 bytes are insignficant if the number + is positive, and leading 0xff bytes if negative. */ + { + size_t i; + const unsigned char* p = pendbyte; + const int pincr = -incr; /* search MSB to LSB */ + const unsigned char insignficant = is_signed ? 0xff : 0x00; + + for (i = 0; i < n; ++i, p += pincr) { + if (*p != insignficant) + break; + } + numsignificantbytes = n - i; + /* 2's-comp is a bit tricky here, e.g. 0xff00 == -0x0100, so + actually has 2 significant bytes. OTOH, 0xff0001 == + -0x00ffff, so we wouldn't *need* to bump it there; but we + do for 0xffff = -0x0001. To be safe without bothering to + check every case, bump it regardless. */ + if (is_signed && numsignificantbytes < n) + ++numsignificantbytes; + } + + /* How many Python long digits do we need? We have + 8*numsignificantbytes bits, and each Python long digit has SHIFT + bits, so it's the ceiling of the quotient. */ + ndigits = (numsignificantbytes * 8 + SHIFT - 1) / SHIFT; + if (ndigits > (size_t)INT_MAX) + return PyErr_NoMemory(); + v = _PyLong_New((int)ndigits); + if (v == NULL) + return NULL; + + /* Copy the bits over. The tricky parts are computing 2's-comp on + the fly for signed numbers, and dealing with the mismatch between + 8-bit bytes and (probably) 15-bit Python digits.*/ + { + size_t i; + twodigits carry = 1; /* for 2's-comp calculation */ + twodigits accum = 0; /* sliding register */ + unsigned int accumbits = 0; /* number of bits in accum */ + const unsigned char* p = pstartbyte; + + for (i = 0; i < numsignificantbytes; ++i, p += incr) { + twodigits thisbyte = *p; + /* Compute correction for 2's comp, if needed. */ + if (is_signed) { + thisbyte = (0xff ^ thisbyte) + carry; + carry = thisbyte >> 8; + thisbyte &= 0xff; + } + /* Because we're going LSB to MSB, thisbyte is + more significant than what's already in accum, + so needs to be prepended to accum. */ + accum |= thisbyte << accumbits; + accumbits += 8; + if (accumbits >= SHIFT) { + /* There's enough to fill a Python digit. */ + assert(idigit < (int)ndigits); + v->ob_digit[idigit] = (digit)(accum & MASK); + ++idigit; + accum >>= SHIFT; + accumbits -= SHIFT; + assert(accumbits < SHIFT); + } + } + assert(accumbits < SHIFT); + if (accumbits) { + assert(idigit < (int)ndigits); + v->ob_digit[idigit] = (digit)accum; + ++idigit; + } + } + + v->ob_size = is_signed ? -idigit : idigit; + return (PyObject *)long_normalize(v); +} + +int +_PyLong_AsByteArray(PyLongObject* v, + unsigned char* bytes, size_t n, + int little_endian, int is_signed) +{ + int i; /* index into v->ob_digit */ + int ndigits; /* |v->ob_size| */ + twodigits accum; /* sliding register */ + unsigned int accumbits; /* # bits in accum */ + int do_twos_comp; /* store 2's-comp? is_signed and v < 0 */ + twodigits carry; /* for computing 2's-comp */ + size_t j; /* # bytes filled */ + unsigned char* p; /* pointer to next byte in bytes */ + int pincr; /* direction to move p */ + + assert(v != NULL && PyLong_Check(v)); + + if (v->ob_size < 0) { + ndigits = -(v->ob_size); + if (!is_signed) { + PyErr_SetString(PyExc_TypeError, + "can't convert negative long to unsigned"); + return -1; + } + do_twos_comp = 1; + } + else { + ndigits = v->ob_size; + do_twos_comp = 0; + } + + if (little_endian) { + p = bytes; + pincr = 1; + } + else { + p = bytes + n - 1; + pincr = -1; + } + + /* Copy over all the Python digits. + It's crucial that every Python digit except for the MSD contribute + exactly SHIFT bits to the total, so first assert that the long is + normalized. */ + assert(ndigits == 0 || v->ob_digit[ndigits - 1] != 0); + j = 0; + accum = 0; + accumbits = 0; + carry = do_twos_comp ? 1 : 0; + for (i = 0; i < ndigits; ++i) { + twodigits thisdigit = v->ob_digit[i]; + if (do_twos_comp) { + thisdigit = (thisdigit ^ MASK) + carry; + carry = thisdigit >> SHIFT; + thisdigit &= MASK; + } + /* Because we're going LSB to MSB, thisdigit is more + significant than what's already in accum, so needs to be + prepended to accum. */ + accum |= thisdigit << accumbits; + accumbits += SHIFT; + + /* The most-significant digit may be (probably is) at least + partly empty. */ + if (i == ndigits - 1) { + /* Count # of sign bits -- they needn't be stored, + * although for signed conversion we need later to + * make sure at least one sign bit gets stored. + * First shift conceptual sign bit to real sign bit. + */ + stwodigits s = (stwodigits)(thisdigit << + (8*sizeof(stwodigits) - SHIFT)); + unsigned int nsignbits = 0; + while ((s < 0) == do_twos_comp && nsignbits < SHIFT) { + ++nsignbits; + s <<= 1; + } + accumbits -= nsignbits; + } + + /* Store as many bytes as possible. */ + while (accumbits >= 8) { + if (j >= n) + goto Overflow; + ++j; + *p = (unsigned char)(accum & 0xff); + p += pincr; + accumbits -= 8; + accum >>= 8; + } + } + + /* Store the straggler (if any). */ + assert(accumbits < 8); + assert(carry == 0); /* else do_twos_comp and *every* digit was 0 */ + if (accumbits > 0) { + if (j >= n) + goto Overflow; + ++j; + if (do_twos_comp) { + /* Fill leading bits of the byte with sign bits + (appropriately pretending that the long had an + infinite supply of sign bits). */ + accum |= (~(twodigits)0) << accumbits; + } + *p = (unsigned char)(accum & 0xff); + p += pincr; + } + else if (j == n && n > 0 && is_signed) { + /* The main loop filled the byte array exactly, so the code + just above didn't get to ensure there's a sign bit, and the + loop below wouldn't add one either. Make sure a sign bit + exists. */ + unsigned char msb = *(p - pincr); + int sign_bit_set = msb >= 0x80; + assert(accumbits == 0); + if (sign_bit_set == do_twos_comp) + return 0; + else + goto Overflow; + } + + /* Fill remaining bytes with copies of the sign bit. */ + { + unsigned char signbyte = do_twos_comp ? 0xffU : 0U; + for ( ; j < n; ++j, p += pincr) + *p = signbyte; + } + + return 0; + +Overflow: + PyErr_SetString(PyExc_OverflowError, "long too big to convert"); + return -1; + +} + +double +_PyLong_AsScaledDouble(PyObject *vv, int *exponent) +{ +/* NBITS_WANTED should be > the number of bits in a double's precision, + but small enough so that 2**NBITS_WANTED is within the normal double + range. nbitsneeded is set to 1 less than that because the most-significant + Python digit contains at least 1 significant bit, but we don't want to + bother counting them (catering to the worst case cheaply). + + 57 is one more than VAX-D double precision; I (Tim) don't know of a double + format with more precision than that; it's 1 larger so that we add in at + least one round bit to stand in for the ignored least-significant bits. +*/ +#define NBITS_WANTED 57 + PyLongObject *v; + double x; + const double multiplier = (double)(1L << SHIFT); + int i, sign; + int nbitsneeded; + + if (vv == NULL || !PyLong_Check(vv)) { + PyErr_BadInternalCall(); + return -1; + } + v = (PyLongObject *)vv; + i = v->ob_size; + sign = 1; + if (i < 0) { + sign = -1; + i = -(i); + } + else if (i == 0) { + *exponent = 0; + return 0.0; + } + --i; + x = (double)v->ob_digit[i]; + nbitsneeded = NBITS_WANTED - 1; + /* Invariant: i Python digits remain unaccounted for. */ + while (i > 0 && nbitsneeded > 0) { + --i; + x = x * multiplier + (double)v->ob_digit[i]; + nbitsneeded -= SHIFT; + } + /* There are i digits we didn't shift in. Pretending they're all + zeroes, the true value is x * 2**(i*SHIFT). */ + *exponent = i; + assert(x > 0.0); + return x * sign; +#undef NBITS_WANTED +} + +/* Get a C double from a long int object. */ + +double +PyLong_AsDouble(PyObject *vv) +{ + int e; + double x; + + if (vv == NULL || !PyLong_Check(vv)) { + PyErr_BadInternalCall(); + return -1; + } + x = _PyLong_AsScaledDouble(vv, &e); + if (x == -1.0 && PyErr_Occurred()) + return -1.0; + if (e > INT_MAX / SHIFT) + goto overflow; + errno = 0; + x = ldexp(x, e * SHIFT); + if (Py_OVERFLOWED(x)) + goto overflow; + return x; + +overflow: + PyErr_SetString(PyExc_OverflowError, + "long int too large to convert to float"); + return -1.0; +} + +/* Create a new long (or int) object from a C pointer */ + +PyObject * +PyLong_FromVoidPtr(void *p) +{ +#if SIZEOF_VOID_P <= SIZEOF_LONG + return PyInt_FromLong((long)p); +#else + +#ifndef HAVE_LONG_LONG +# error "PyLong_FromVoidPtr: sizeof(void*) > sizeof(long), but no long long" +#endif +#if SIZEOF_LONG_LONG < SIZEOF_VOID_P +# error "PyLong_FromVoidPtr: sizeof(PY_LONG_LONG) < sizeof(void*)" +#endif + /* optimize null pointers */ + if (p == NULL) + return PyInt_FromLong(0); + return PyLong_FromLongLong((PY_LONG_LONG)p); + +#endif /* SIZEOF_VOID_P <= SIZEOF_LONG */ +} + +/* Get a C pointer from a long object (or an int object in some cases) */ + +void * +PyLong_AsVoidPtr(PyObject *vv) +{ + /* This function will allow int or long objects. If vv is neither, + then the PyLong_AsLong*() functions will raise the exception: + PyExc_SystemError, "bad argument to internal function" + */ +#if SIZEOF_VOID_P <= SIZEOF_LONG + long x; + + if (PyInt_Check(vv)) + x = PyInt_AS_LONG(vv); + else + x = PyLong_AsLong(vv); +#else + +#ifndef HAVE_LONG_LONG +# error "PyLong_AsVoidPtr: sizeof(void*) > sizeof(long), but no long long" +#endif +#if SIZEOF_LONG_LONG < SIZEOF_VOID_P +# error "PyLong_AsVoidPtr: sizeof(PY_LONG_LONG) < sizeof(void*)" +#endif + PY_LONG_LONG x; + + if (PyInt_Check(vv)) + x = PyInt_AS_LONG(vv); + else + x = PyLong_AsLongLong(vv); + +#endif /* SIZEOF_VOID_P <= SIZEOF_LONG */ + + if (x == -1 && PyErr_Occurred()) + return NULL; + return (void *)x; +} + +#ifdef HAVE_LONG_LONG + +/* Initial PY_LONG_LONG support by Chris Herborth (chrish@qnx.com), later + * rewritten to use the newer PyLong_{As,From}ByteArray API. + */ + +#define IS_LITTLE_ENDIAN (int)*(unsigned char*)&one + +/* Create a new long int object from a C PY_LONG_LONG int. */ + +PyObject * +PyLong_FromLongLong(PY_LONG_LONG ival) +{ + PY_LONG_LONG bytes = ival; + int one = 1; + return _PyLong_FromByteArray( + (unsigned char *)&bytes, + SIZEOF_LONG_LONG, IS_LITTLE_ENDIAN, 1); +} + +/* Create a new long int object from a C unsigned PY_LONG_LONG int. */ + +PyObject * +PyLong_FromUnsignedLongLong(unsigned PY_LONG_LONG ival) +{ + unsigned PY_LONG_LONG bytes = ival; + int one = 1; + return _PyLong_FromByteArray( + (unsigned char *)&bytes, + SIZEOF_LONG_LONG, IS_LITTLE_ENDIAN, 0); +} + +/* Get a C PY_LONG_LONG int from a long int object. + Return -1 and set an error if overflow occurs. */ + +PY_LONG_LONG +PyLong_AsLongLong(PyObject *vv) +{ + PY_LONG_LONG bytes; + int one = 1; + int res; + + if (vv == NULL) { + PyErr_BadInternalCall(); + return -1; + } + if (!PyLong_Check(vv)) { + if (PyInt_Check(vv)) + return (PY_LONG_LONG)PyInt_AsLong(vv); + PyErr_BadInternalCall(); + return -1; + } + + res = _PyLong_AsByteArray( + (PyLongObject *)vv, (unsigned char *)&bytes, + SIZEOF_LONG_LONG, IS_LITTLE_ENDIAN, 1); + + /* Plan 9 can't handle PY_LONG_LONG in ? : expressions */ + if (res < 0) + return (PY_LONG_LONG)-1; + else + return bytes; +} + +/* Get a C unsigned PY_LONG_LONG int from a long int object. + Return -1 and set an error if overflow occurs. */ + +unsigned PY_LONG_LONG +PyLong_AsUnsignedLongLong(PyObject *vv) +{ + unsigned PY_LONG_LONG bytes; + int one = 1; + int res; + + if (vv == NULL || !PyLong_Check(vv)) { + PyErr_BadInternalCall(); + return -1; + } + + res = _PyLong_AsByteArray( + (PyLongObject *)vv, (unsigned char *)&bytes, + SIZEOF_LONG_LONG, IS_LITTLE_ENDIAN, 0); + + /* Plan 9 can't handle PY_LONG_LONG in ? : expressions */ + if (res < 0) + return (unsigned PY_LONG_LONG)res; + else + return bytes; +} + +/* Get a C unsigned long int from a long int object, ignoring the high bits. + Returns -1 and sets an error condition if an error occurs. */ + +unsigned PY_LONG_LONG +PyLong_AsUnsignedLongLongMask(PyObject *vv) +{ + register PyLongObject *v; + unsigned PY_LONG_LONG x; + int i, sign; + + if (vv == NULL || !PyLong_Check(vv)) { + PyErr_BadInternalCall(); + return (unsigned long) -1; + } + v = (PyLongObject *)vv; + i = v->ob_size; + sign = 1; + x = 0; + if (i < 0) { + sign = -1; + i = -i; + } + while (--i >= 0) { + x = (x << SHIFT) + v->ob_digit[i]; + } + return x * sign; +} +#undef IS_LITTLE_ENDIAN + +#endif /* HAVE_LONG_LONG */ + + +static int +convert_binop(PyObject *v, PyObject *w, PyLongObject **a, PyLongObject **b) { + if (PyLong_Check(v)) { + *a = (PyLongObject *) v; + Py_INCREF(v); + } + else if (PyInt_Check(v)) { + *a = (PyLongObject *) PyLong_FromLong(PyInt_AS_LONG(v)); + } + else { + return 0; + } + if (PyLong_Check(w)) { + *b = (PyLongObject *) w; + Py_INCREF(w); + } + else if (PyInt_Check(w)) { + *b = (PyLongObject *) PyLong_FromLong(PyInt_AS_LONG(w)); + } + else { + Py_DECREF(*a); + return 0; + } + return 1; +} + +#define CONVERT_BINOP(v, w, a, b) \ + if (!convert_binop(v, w, a, b)) { \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ + } + +/* x[0:m] and y[0:n] are digit vectors, LSD first, m >= n required. x[0:n] + * is modified in place, by adding y to it. Carries are propagated as far as + * x[m-1], and the remaining carry (0 or 1) is returned. + */ +static digit +v_iadd(digit *x, int m, digit *y, int n) +{ + int i; + digit carry = 0; + + assert(m >= n); + for (i = 0; i < n; ++i) { + carry += x[i] + y[i]; + x[i] = carry & MASK; + carry >>= SHIFT; + assert((carry & 1) == carry); + } + for (; carry && i < m; ++i) { + carry += x[i]; + x[i] = carry & MASK; + carry >>= SHIFT; + assert((carry & 1) == carry); + } + return carry; +} + +/* x[0:m] and y[0:n] are digit vectors, LSD first, m >= n required. x[0:n] + * is modified in place, by subtracting y from it. Borrows are propagated as + * far as x[m-1], and the remaining borrow (0 or 1) is returned. + */ +static digit +v_isub(digit *x, int m, digit *y, int n) +{ + int i; + digit borrow = 0; + + assert(m >= n); + for (i = 0; i < n; ++i) { + borrow = x[i] - y[i] - borrow; + x[i] = borrow & MASK; + borrow >>= SHIFT; + borrow &= 1; /* keep only 1 sign bit */ + } + for (; borrow && i < m; ++i) { + borrow = x[i] - borrow; + x[i] = borrow & MASK; + borrow >>= SHIFT; + borrow &= 1; + } + return borrow; +} + +/* Multiply by a single digit, ignoring the sign. */ + +static PyLongObject * +mul1(PyLongObject *a, wdigit n) +{ + return muladd1(a, n, (digit)0); +} + +/* Multiply by a single digit and add a single digit, ignoring the sign. */ + +static PyLongObject * +muladd1(PyLongObject *a, wdigit n, wdigit extra) +{ + int size_a = ABS(a->ob_size); + PyLongObject *z = _PyLong_New(size_a+1); + twodigits carry = extra; + int i; + + if (z == NULL) + return NULL; + for (i = 0; i < size_a; ++i) { + carry += (twodigits)a->ob_digit[i] * n; + z->ob_digit[i] = (digit) (carry & MASK); + carry >>= SHIFT; + } + z->ob_digit[i] = (digit) carry; + return long_normalize(z); +} + +/* Divide long pin, w/ size digits, by non-zero digit n, storing quotient + in pout, and returning the remainder. pin and pout point at the LSD. + It's OK for pin == pout on entry, which saves oodles of mallocs/frees in + long_format, but that should be done with great care since longs are + immutable. */ + +static digit +inplace_divrem1(digit *pout, digit *pin, int size, digit n) +{ + twodigits rem = 0; + + assert(n > 0 && n <= MASK); + pin += size; + pout += size; + while (--size >= 0) { + digit hi; + rem = (rem << SHIFT) + *--pin; + *--pout = hi = (digit)(rem / n); + rem -= hi * n; + } + return (digit)rem; +} + +/* Divide a long integer by a digit, returning both the quotient + (as function result) and the remainder (through *prem). + The sign of a is ignored; n should not be zero. */ + +static PyLongObject * +divrem1(PyLongObject *a, digit n, digit *prem) +{ + const int size = ABS(a->ob_size); + PyLongObject *z; + + assert(n > 0 && n <= MASK); + z = _PyLong_New(size); + if (z == NULL) + return NULL; + *prem = inplace_divrem1(z->ob_digit, a->ob_digit, size, n); + return long_normalize(z); +} + +/* Convert a long int object to a string, using a given conversion base. + Return a string object. + If base is 8 or 16, add the proper prefix '0' or '0x'. */ + +static PyObject * +long_format(PyObject *aa, int base, int addL) +{ + register PyLongObject *a = (PyLongObject *)aa; + PyStringObject *str; + int i; + const int size_a = ABS(a->ob_size); + char *p; + int bits; + char sign = '\0'; + + if (a == NULL || !PyLong_Check(a)) { + PyErr_BadInternalCall(); + return NULL; + } + assert(base >= 2 && base <= 36); + + /* Compute a rough upper bound for the length of the string */ + i = base; + bits = 0; + while (i > 1) { + ++bits; + i >>= 1; + } + i = 5 + (addL ? 1 : 0) + (size_a*SHIFT + bits-1) / bits; + str = (PyStringObject *) PyString_FromStringAndSize((char *)0, i); + if (str == NULL) + return NULL; + p = PyString_AS_STRING(str) + i; + *p = '\0'; + if (addL) + *--p = 'L'; + if (a->ob_size < 0) + sign = '-'; + + if (a->ob_size == 0) { + *--p = '0'; + } + else if ((base & (base - 1)) == 0) { + /* JRH: special case for power-of-2 bases */ + twodigits accum = 0; + int accumbits = 0; /* # of bits in accum */ + int basebits = 1; /* # of bits in base-1 */ + i = base; + while ((i >>= 1) > 1) + ++basebits; + + for (i = 0; i < size_a; ++i) { + accum |= (twodigits)a->ob_digit[i] << accumbits; + accumbits += SHIFT; + assert(accumbits >= basebits); + do { + char cdigit = (char)(accum & (base - 1)); + cdigit += (cdigit < 10) ? '0' : 'A'-10; + assert(p > PyString_AS_STRING(str)); + *--p = cdigit; + accumbits -= basebits; + accum >>= basebits; + } while (i < size_a-1 ? accumbits >= basebits : + accum > 0); + } + } + else { + /* Not 0, and base not a power of 2. Divide repeatedly by + base, but for speed use the highest power of base that + fits in a digit. */ + int size = size_a; + digit *pin = a->ob_digit; + PyLongObject *scratch; + /* powbasw <- largest power of base that fits in a digit. */ + digit powbase = base; /* powbase == base ** power */ + int power = 1; + for (;;) { + unsigned long newpow = powbase * (unsigned long)base; + if (newpow >> SHIFT) /* doesn't fit in a digit */ + break; + powbase = (digit)newpow; + ++power; + } + + /* Get a scratch area for repeated division. */ + scratch = _PyLong_New(size); + if (scratch == NULL) { + Py_DECREF(str); + return NULL; + } + + /* Repeatedly divide by powbase. */ + do { + int ntostore = power; + digit rem = inplace_divrem1(scratch->ob_digit, + pin, size, powbase); + pin = scratch->ob_digit; /* no need to use a again */ + if (pin[size - 1] == 0) + --size; + SIGCHECK({ + Py_DECREF(scratch); + Py_DECREF(str); + return NULL; + }) + + /* Break rem into digits. */ + assert(ntostore > 0); + do { + digit nextrem = (digit)(rem / base); + char c = (char)(rem - nextrem * base); + assert(p > PyString_AS_STRING(str)); + c += (c < 10) ? '0' : 'A'-10; + *--p = c; + rem = nextrem; + --ntostore; + /* Termination is a bit delicate: must not + store leading zeroes, so must get out if + remaining quotient and rem are both 0. */ + } while (ntostore && (size || rem)); + } while (size != 0); + Py_DECREF(scratch); + } + + if (base == 8) { + if (size_a != 0) + *--p = '0'; + } + else if (base == 16) { + *--p = 'x'; + *--p = '0'; + } + else if (base != 10) { + *--p = '#'; + *--p = '0' + base%10; + if (base > 10) + *--p = '0' + base/10; + } + if (sign) + *--p = sign; + if (p != PyString_AS_STRING(str)) { + char *q = PyString_AS_STRING(str); + assert(p > q); + do { + } while ((*q++ = *p++) != '\0'); + q--; + _PyString_Resize((PyObject **)&str, + (int) (q - PyString_AS_STRING(str))); + } + return (PyObject *)str; +} + +/* *str points to the first digit in a string of base base digits. base + * is a power of 2 (2, 4, 8, 16, or 32). *str is set to point to the first + * non-digit (which may be *str!). A normalized long is returned. + * The point to this routine is that it takes time linear in the number of + * string characters. + */ +static PyLongObject * +long_from_binary_base(char **str, int base) +{ + char *p = *str; + char *start = p; + int bits_per_char; + int n; + PyLongObject *z; + twodigits accum; + int bits_in_accum; + digit *pdigit; + + assert(base >= 2 && base <= 32 && (base & (base - 1)) == 0); + n = base; + for (bits_per_char = -1; n; ++bits_per_char) + n >>= 1; + /* n <- total # of bits needed, while setting p to end-of-string */ + n = 0; + for (;;) { + int k = -1; + char ch = *p; + + if (ch <= '9') + k = ch - '0'; + else if (ch >= 'a') + k = ch - 'a' + 10; + else if (ch >= 'A') + k = ch - 'A' + 10; + if (k < 0 || k >= base) + break; + ++p; + } + *str = p; + n = (p - start) * bits_per_char; + if (n / bits_per_char != p - start) { + PyErr_SetString(PyExc_ValueError, + "long string too large to convert"); + return NULL; + } + /* n <- # of Python digits needed, = ceiling(n/SHIFT). */ + n = (n + SHIFT - 1) / SHIFT; + z = _PyLong_New(n); + if (z == NULL) + return NULL; + /* Read string from right, and fill in long from left; i.e., + * from least to most significant in both. + */ + accum = 0; + bits_in_accum = 0; + pdigit = z->ob_digit; + while (--p >= start) { + int k; + char ch = *p; + + if (ch <= '9') + k = ch - '0'; + else if (ch >= 'a') + k = ch - 'a' + 10; + else { + assert(ch >= 'A'); + k = ch - 'A' + 10; + } + assert(k >= 0 && k < base); + accum |= (twodigits)(k << bits_in_accum); + bits_in_accum += bits_per_char; + if (bits_in_accum >= SHIFT) { + *pdigit++ = (digit)(accum & MASK); + assert(pdigit - z->ob_digit <= n); + accum >>= SHIFT; + bits_in_accum -= SHIFT; + assert(bits_in_accum < SHIFT); + } + } + if (bits_in_accum) { + assert(bits_in_accum <= SHIFT); + *pdigit++ = (digit)accum; + assert(pdigit - z->ob_digit <= n); + } + while (pdigit - z->ob_digit < n) + *pdigit++ = 0; + return long_normalize(z); +} + +PyObject * +PyLong_FromString(char *str, char **pend, int base) +{ + int sign = 1; + char *start, *orig_str = str; + PyLongObject *z; + + if ((base != 0 && base < 2) || base > 36) { + PyErr_SetString(PyExc_ValueError, + "long() arg 2 must be >= 2 and <= 36"); + return NULL; + } + while (*str != '\0' && isspace(Py_CHARMASK(*str))) + str++; + if (*str == '+') + ++str; + else if (*str == '-') { + ++str; + sign = -1; + } + while (*str != '\0' && isspace(Py_CHARMASK(*str))) + str++; + if (base == 0) { + if (str[0] != '0') + base = 10; + else if (str[1] == 'x' || str[1] == 'X') + base = 16; + else + base = 8; + } + if (base == 16 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + str += 2; + start = str; + if ((base & (base - 1)) == 0) + z = long_from_binary_base(&str, base); + else { + z = _PyLong_New(0); + for ( ; z != NULL; ++str) { + int k = -1; + PyLongObject *temp; + + if (*str <= '9') + k = *str - '0'; + else if (*str >= 'a') + k = *str - 'a' + 10; + else if (*str >= 'A') + k = *str - 'A' + 10; + if (k < 0 || k >= base) + break; + temp = muladd1(z, (digit)base, (digit)k); + Py_DECREF(z); + z = temp; + } + } + if (z == NULL) + return NULL; + if (str == start) + goto onError; + if (sign < 0 && z != NULL && z->ob_size != 0) + z->ob_size = -(z->ob_size); + if (*str == 'L' || *str == 'l') + str++; + while (*str && isspace(Py_CHARMASK(*str))) + str++; + if (*str != '\0') + goto onError; + if (pend) + *pend = str; + return (PyObject *) z; + + onError: + PyErr_Format(PyExc_ValueError, + "invalid literal for long(): %.200s", orig_str); + Py_XDECREF(z); + return NULL; +} + +#ifdef Py_USING_UNICODE +PyObject * +PyLong_FromUnicode(Py_UNICODE *u, int length, int base) +{ + PyObject *result; + char *buffer = PyMem_MALLOC(length+1); + + if (buffer == NULL) + return NULL; + + if (PyUnicode_EncodeDecimal(u, length, buffer, NULL)) { + PyMem_FREE(buffer); + return NULL; + } + result = PyLong_FromString(buffer, NULL, base); + PyMem_FREE(buffer); + return result; +} +#endif + +/* forward */ +static PyLongObject *x_divrem + (PyLongObject *, PyLongObject *, PyLongObject **); +static PyObject *long_pos(PyLongObject *); +static int long_divrem(PyLongObject *, PyLongObject *, + PyLongObject **, PyLongObject **); + +/* Long division with remainder, top-level routine */ + +static int +long_divrem(PyLongObject *a, PyLongObject *b, + PyLongObject **pdiv, PyLongObject **prem) +{ + int size_a = ABS(a->ob_size), size_b = ABS(b->ob_size); + PyLongObject *z; + + if (size_b == 0) { + PyErr_SetString(PyExc_ZeroDivisionError, + "long division or modulo by zero"); + return -1; + } + if (size_a < size_b || + (size_a == size_b && + a->ob_digit[size_a-1] < b->ob_digit[size_b-1])) { + /* |a| < |b|. */ + *pdiv = _PyLong_New(0); + Py_INCREF(a); + *prem = (PyLongObject *) a; + return 0; + } + if (size_b == 1) { + digit rem = 0; + z = divrem1(a, b->ob_digit[0], &rem); + if (z == NULL) + return -1; + *prem = (PyLongObject *) PyLong_FromLong((long)rem); + } + else { + z = x_divrem(a, b, prem); + if (z == NULL) + return -1; + } + /* Set the signs. + The quotient z has the sign of a*b; + the remainder r has the sign of a, + so a = b*z + r. */ + if ((a->ob_size < 0) != (b->ob_size < 0)) + z->ob_size = -(z->ob_size); + if (a->ob_size < 0 && (*prem)->ob_size != 0) + (*prem)->ob_size = -((*prem)->ob_size); + *pdiv = z; + return 0; +} + +/* Unsigned long division with remainder -- the algorithm */ + +static PyLongObject * +x_divrem(PyLongObject *v1, PyLongObject *w1, PyLongObject **prem) +{ + int size_v = ABS(v1->ob_size), size_w = ABS(w1->ob_size); + digit d = (digit) ((twodigits)BASE / (w1->ob_digit[size_w-1] + 1)); + PyLongObject *v = mul1(v1, d); + PyLongObject *w = mul1(w1, d); + PyLongObject *a; + int j, k; + + if (v == NULL || w == NULL) { + Py_XDECREF(v); + Py_XDECREF(w); + return NULL; + } + + assert(size_v >= size_w && size_w > 1); /* Assert checks by div() */ + assert(v->ob_refcnt == 1); /* Since v will be used as accumulator! */ + assert(size_w == ABS(w->ob_size)); /* That's how d was calculated */ + + size_v = ABS(v->ob_size); + a = _PyLong_New(size_v - size_w + 1); + + for (j = size_v, k = a->ob_size-1; a != NULL && k >= 0; --j, --k) { + digit vj = (j >= size_v) ? 0 : v->ob_digit[j]; + twodigits q; + stwodigits carry = 0; + int i; + + SIGCHECK({ + Py_DECREF(a); + a = NULL; + break; + }) + if (vj == w->ob_digit[size_w-1]) + q = MASK; + else + q = (((twodigits)vj << SHIFT) + v->ob_digit[j-1]) / + w->ob_digit[size_w-1]; + + while (w->ob_digit[size_w-2]*q > + (( + ((twodigits)vj << SHIFT) + + v->ob_digit[j-1] + - q*w->ob_digit[size_w-1] + ) << SHIFT) + + v->ob_digit[j-2]) + --q; + + for (i = 0; i < size_w && i+k < size_v; ++i) { + twodigits z = w->ob_digit[i] * q; + digit zz = (digit) (z >> SHIFT); + carry += v->ob_digit[i+k] - z + + ((twodigits)zz << SHIFT); + v->ob_digit[i+k] = (digit)(carry & MASK); + carry = Py_ARITHMETIC_RIGHT_SHIFT(BASE_TWODIGITS_TYPE, + carry, SHIFT); + carry -= zz; + } + + if (i+k < size_v) { + carry += v->ob_digit[i+k]; + v->ob_digit[i+k] = 0; + } + + if (carry == 0) + a->ob_digit[k] = (digit) q; + else { + assert(carry == -1); + a->ob_digit[k] = (digit) q-1; + carry = 0; + for (i = 0; i < size_w && i+k < size_v; ++i) { + carry += v->ob_digit[i+k] + w->ob_digit[i]; + v->ob_digit[i+k] = (digit)(carry & MASK); + carry = Py_ARITHMETIC_RIGHT_SHIFT( + BASE_TWODIGITS_TYPE, + carry, SHIFT); + } + } + } /* for j, k */ + + if (a == NULL) + *prem = NULL; + else { + a = long_normalize(a); + *prem = divrem1(v, d, &d); + /* d receives the (unused) remainder */ + if (*prem == NULL) { + Py_DECREF(a); + a = NULL; + } + } + Py_DECREF(v); + Py_DECREF(w); + return a; +} + +/* Methods */ + +static void +long_dealloc(PyObject *v) +{ + v->ob_type->tp_free(v); +} + +static PyObject * +long_repr(PyObject *v) +{ + return long_format(v, 10, 1); +} + +static PyObject * +long_str(PyObject *v) +{ + return long_format(v, 10, 0); +} + +static int +long_compare(PyLongObject *a, PyLongObject *b) +{ + int sign; + + if (a->ob_size != b->ob_size) { + if (ABS(a->ob_size) == 0 && ABS(b->ob_size) == 0) + sign = 0; + else + sign = a->ob_size - b->ob_size; + } + else { + int i = ABS(a->ob_size); + while (--i >= 0 && a->ob_digit[i] == b->ob_digit[i]) + ; + if (i < 0) + sign = 0; + else { + sign = (int)a->ob_digit[i] - (int)b->ob_digit[i]; + if (a->ob_size < 0) + sign = -sign; + } + } + return sign < 0 ? -1 : sign > 0 ? 1 : 0; +} + +static long +long_hash(PyLongObject *v) +{ + long x; + int i, sign; + + /* This is designed so that Python ints and longs with the + same value hash to the same value, otherwise comparisons + of mapping keys will turn out weird */ + i = v->ob_size; + sign = 1; + x = 0; + if (i < 0) { + sign = -1; + i = -(i); + } +#define LONG_BIT_SHIFT (8*sizeof(long) - SHIFT) + while (--i >= 0) { + /* Force a native long #-bits (32 or 64) circular shift */ + x = ((x << SHIFT) & ~MASK) | ((x >> LONG_BIT_SHIFT) & MASK); + x += v->ob_digit[i]; + } +#undef LONG_BIT_SHIFT + x = x * sign; + if (x == -1) + x = -2; + return x; +} + + +/* Add the absolute values of two long integers. */ + +static PyLongObject * +x_add(PyLongObject *a, PyLongObject *b) +{ + int size_a = ABS(a->ob_size), size_b = ABS(b->ob_size); + PyLongObject *z; + int i; + digit carry = 0; + + /* Ensure a is the larger of the two: */ + if (size_a < size_b) { + { PyLongObject *temp = a; a = b; b = temp; } + { int size_temp = size_a; + size_a = size_b; + size_b = size_temp; } + } + z = _PyLong_New(size_a+1); + if (z == NULL) + return NULL; + for (i = 0; i < size_b; ++i) { + carry += a->ob_digit[i] + b->ob_digit[i]; + z->ob_digit[i] = carry & MASK; + carry >>= SHIFT; + } + for (; i < size_a; ++i) { + carry += a->ob_digit[i]; + z->ob_digit[i] = carry & MASK; + carry >>= SHIFT; + } + z->ob_digit[i] = carry; + return long_normalize(z); +} + +/* Subtract the absolute values of two integers. */ + +static PyLongObject * +x_sub(PyLongObject *a, PyLongObject *b) +{ + int size_a = ABS(a->ob_size), size_b = ABS(b->ob_size); + PyLongObject *z; + int i; + int sign = 1; + digit borrow = 0; + + /* Ensure a is the larger of the two: */ + if (size_a < size_b) { + sign = -1; + { PyLongObject *temp = a; a = b; b = temp; } + { int size_temp = size_a; + size_a = size_b; + size_b = size_temp; } + } + else if (size_a == size_b) { + /* Find highest digit where a and b differ: */ + i = size_a; + while (--i >= 0 && a->ob_digit[i] == b->ob_digit[i]) + ; + if (i < 0) + return _PyLong_New(0); + if (a->ob_digit[i] < b->ob_digit[i]) { + sign = -1; + { PyLongObject *temp = a; a = b; b = temp; } + } + size_a = size_b = i+1; + } + z = _PyLong_New(size_a); + if (z == NULL) + return NULL; + for (i = 0; i < size_b; ++i) { + /* The following assumes unsigned arithmetic + works module 2**N for some N>SHIFT. */ + borrow = a->ob_digit[i] - b->ob_digit[i] - borrow; + z->ob_digit[i] = borrow & MASK; + borrow >>= SHIFT; + borrow &= 1; /* Keep only one sign bit */ + } + for (; i < size_a; ++i) { + borrow = a->ob_digit[i] - borrow; + z->ob_digit[i] = borrow & MASK; + borrow >>= SHIFT; + borrow &= 1; /* Keep only one sign bit */ + } + assert(borrow == 0); + if (sign < 0) + z->ob_size = -(z->ob_size); + return long_normalize(z); +} + +static PyObject * +long_add(PyLongObject *v, PyLongObject *w) +{ + PyLongObject *a, *b, *z; + + CONVERT_BINOP((PyObject *)v, (PyObject *)w, &a, &b); + + if (a->ob_size < 0) { + if (b->ob_size < 0) { + z = x_add(a, b); + if (z != NULL && z->ob_size != 0) + z->ob_size = -(z->ob_size); + } + else + z = x_sub(b, a); + } + else { + if (b->ob_size < 0) + z = x_sub(a, b); + else + z = x_add(a, b); + } + Py_DECREF(a); + Py_DECREF(b); + return (PyObject *)z; +} + +static PyObject * +long_sub(PyLongObject *v, PyLongObject *w) +{ + PyLongObject *a, *b, *z; + + CONVERT_BINOP((PyObject *)v, (PyObject *)w, &a, &b); + + if (a->ob_size < 0) { + if (b->ob_size < 0) + z = x_sub(a, b); + else + z = x_add(a, b); + if (z != NULL && z->ob_size != 0) + z->ob_size = -(z->ob_size); + } + else { + if (b->ob_size < 0) + z = x_add(a, b); + else + z = x_sub(a, b); + } + Py_DECREF(a); + Py_DECREF(b); + return (PyObject *)z; +} + +/* Grade school multiplication, ignoring the signs. + * Returns the absolute value of the product, or NULL if error. + */ +static PyLongObject * +x_mul(PyLongObject *a, PyLongObject *b) +{ + PyLongObject *z; + int size_a = ABS(a->ob_size); + int size_b = ABS(b->ob_size); + int i; + + z = _PyLong_New(size_a + size_b); + if (z == NULL) + return NULL; + + memset(z->ob_digit, 0, z->ob_size * sizeof(digit)); + for (i = 0; i < size_a; ++i) { + twodigits carry = 0; + twodigits f = a->ob_digit[i]; + int j; + digit *pz = z->ob_digit + i; + + SIGCHECK({ + Py_DECREF(z); + return NULL; + }) + for (j = 0; j < size_b; ++j) { + carry += *pz + b->ob_digit[j] * f; + *pz++ = (digit) (carry & MASK); + carry >>= SHIFT; + } + for (; carry != 0; ++j) { + assert(i+j < z->ob_size); + carry += *pz; + *pz++ = (digit) (carry & MASK); + carry >>= SHIFT; + } + } + return long_normalize(z); +} + +/* A helper for Karatsuba multiplication (k_mul). + Takes a long "n" and an integer "size" representing the place to + split, and sets low and high such that abs(n) == (high << size) + low, + viewing the shift as being by digits. The sign bit is ignored, and + the return values are >= 0. + Returns 0 on success, -1 on failure. +*/ +static int +kmul_split(PyLongObject *n, int size, PyLongObject **high, PyLongObject **low) +{ + PyLongObject *hi, *lo; + int size_lo, size_hi; + const int size_n = ABS(n->ob_size); + + size_lo = MIN(size_n, size); + size_hi = size_n - size_lo; + + if ((hi = _PyLong_New(size_hi)) == NULL) + return -1; + if ((lo = _PyLong_New(size_lo)) == NULL) { + Py_DECREF(hi); + return -1; + } + + memcpy(lo->ob_digit, n->ob_digit, size_lo * sizeof(digit)); + memcpy(hi->ob_digit, n->ob_digit + size_lo, size_hi * sizeof(digit)); + + *high = long_normalize(hi); + *low = long_normalize(lo); + return 0; +} + +static PyLongObject *k_lopsided_mul(PyLongObject *a, PyLongObject *b); + +/* Karatsuba multiplication. Ignores the input signs, and returns the + * absolute value of the product (or NULL if error). + * See Knuth Vol. 2 Chapter 4.3.3 (Pp. 294-295). + */ +static PyLongObject * +k_mul(PyLongObject *a, PyLongObject *b) +{ + int asize = ABS(a->ob_size); + int bsize = ABS(b->ob_size); + PyLongObject *ah = NULL; + PyLongObject *al = NULL; + PyLongObject *bh = NULL; + PyLongObject *bl = NULL; + PyLongObject *ret = NULL; + PyLongObject *t1, *t2, *t3; + int shift; /* the number of digits we split off */ + int i; + + /* (ah*X+al)(bh*X+bl) = ah*bh*X*X + (ah*bl + al*bh)*X + al*bl + * Let k = (ah+al)*(bh+bl) = ah*bl + al*bh + ah*bh + al*bl + * Then the original product is + * ah*bh*X*X + (k - ah*bh - al*bl)*X + al*bl + * By picking X to be a power of 2, "*X" is just shifting, and it's + * been reduced to 3 multiplies on numbers half the size. + */ + + /* We want to split based on the larger number; fiddle so that b + * is largest. + */ + if (asize > bsize) { + t1 = a; + a = b; + b = t1; + + i = asize; + asize = bsize; + bsize = i; + } + + /* Use gradeschool math when either number is too small. */ + if (asize <= KARATSUBA_CUTOFF) { + if (asize == 0) + return _PyLong_New(0); + else + return x_mul(a, b); + } + + /* If a is small compared to b, splitting on b gives a degenerate + * case with ah==0, and Karatsuba may be (even much) less efficient + * than "grade school" then. However, we can still win, by viewing + * b as a string of "big digits", each of width a->ob_size. That + * leads to a sequence of balanced calls to k_mul. + */ + if (2 * asize <= bsize) + return k_lopsided_mul(a, b); + + /* Split a & b into hi & lo pieces. */ + shift = bsize >> 1; + if (kmul_split(a, shift, &ah, &al) < 0) goto fail; + assert(ah->ob_size > 0); /* the split isn't degenerate */ + + if (kmul_split(b, shift, &bh, &bl) < 0) goto fail; + + /* The plan: + * 1. Allocate result space (asize + bsize digits: that's always + * enough). + * 2. Compute ah*bh, and copy into result at 2*shift. + * 3. Compute al*bl, and copy into result at 0. Note that this + * can't overlap with #2. + * 4. Subtract al*bl from the result, starting at shift. This may + * underflow (borrow out of the high digit), but we don't care: + * we're effectively doing unsigned arithmetic mod + * BASE**(sizea + sizeb), and so long as the *final* result fits, + * borrows and carries out of the high digit can be ignored. + * 5. Subtract ah*bh from the result, starting at shift. + * 6. Compute (ah+al)*(bh+bl), and add it into the result starting + * at shift. + */ + + /* 1. Allocate result space. */ + ret = _PyLong_New(asize + bsize); + if (ret == NULL) goto fail; +#ifdef Py_DEBUG + /* Fill with trash, to catch reference to uninitialized digits. */ + memset(ret->ob_digit, 0xDF, ret->ob_size * sizeof(digit)); +#endif + + /* 2. t1 <- ah*bh, and copy into high digits of result. */ + if ((t1 = k_mul(ah, bh)) == NULL) goto fail; + assert(t1->ob_size >= 0); + assert(2*shift + t1->ob_size <= ret->ob_size); + memcpy(ret->ob_digit + 2*shift, t1->ob_digit, + t1->ob_size * sizeof(digit)); + + /* Zero-out the digits higher than the ah*bh copy. */ + i = ret->ob_size - 2*shift - t1->ob_size; + if (i) + memset(ret->ob_digit + 2*shift + t1->ob_size, 0, + i * sizeof(digit)); + + /* 3. t2 <- al*bl, and copy into the low digits. */ + if ((t2 = k_mul(al, bl)) == NULL) { + Py_DECREF(t1); + goto fail; + } + assert(t2->ob_size >= 0); + assert(t2->ob_size <= 2*shift); /* no overlap with high digits */ + memcpy(ret->ob_digit, t2->ob_digit, t2->ob_size * sizeof(digit)); + + /* Zero out remaining digits. */ + i = 2*shift - t2->ob_size; /* number of uninitialized digits */ + if (i) + memset(ret->ob_digit + t2->ob_size, 0, i * sizeof(digit)); + + /* 4 & 5. Subtract ah*bh (t1) and al*bl (t2). We do al*bl first + * because it's fresher in cache. + */ + i = ret->ob_size - shift; /* # digits after shift */ + (void)v_isub(ret->ob_digit + shift, i, t2->ob_digit, t2->ob_size); + Py_DECREF(t2); + + (void)v_isub(ret->ob_digit + shift, i, t1->ob_digit, t1->ob_size); + Py_DECREF(t1); + + /* 6. t3 <- (ah+al)(bh+bl), and add into result. */ + if ((t1 = x_add(ah, al)) == NULL) goto fail; + Py_DECREF(ah); + Py_DECREF(al); + ah = al = NULL; + + if ((t2 = x_add(bh, bl)) == NULL) { + Py_DECREF(t1); + goto fail; + } + Py_DECREF(bh); + Py_DECREF(bl); + bh = bl = NULL; + + t3 = k_mul(t1, t2); + Py_DECREF(t1); + Py_DECREF(t2); + if (t3 == NULL) goto fail; + assert(t3->ob_size >= 0); + + /* Add t3. It's not obvious why we can't run out of room here. + * See the (*) comment after this function. + */ + (void)v_iadd(ret->ob_digit + shift, i, t3->ob_digit, t3->ob_size); + Py_DECREF(t3); + + return long_normalize(ret); + + fail: + Py_XDECREF(ret); + Py_XDECREF(ah); + Py_XDECREF(al); + Py_XDECREF(bh); + Py_XDECREF(bl); + return NULL; +} + +/* (*) Why adding t3 can't "run out of room" above. + +Let f(x) mean the floor of x and c(x) mean the ceiling of x. Some facts +to start with: + +1. For any integer i, i = c(i/2) + f(i/2). In particular, + bsize = c(bsize/2) + f(bsize/2). +2. shift = f(bsize/2) +3. asize <= bsize +4. Since we call k_lopsided_mul if asize*2 <= bsize, asize*2 > bsize in this + routine, so asize > bsize/2 >= f(bsize/2) in this routine. + +We allocated asize + bsize result digits, and add t3 into them at an offset +of shift. This leaves asize+bsize-shift allocated digit positions for t3 +to fit into, = (by #1 and #2) asize + f(bsize/2) + c(bsize/2) - f(bsize/2) = +asize + c(bsize/2) available digit positions. + +bh has c(bsize/2) digits, and bl at most f(size/2) digits. So bh+hl has +at most c(bsize/2) digits + 1 bit. + +If asize == bsize, ah has c(bsize/2) digits, else ah has at most f(bsize/2) +digits, and al has at most f(bsize/2) digits in any case. So ah+al has at +most (asize == bsize ? c(bsize/2) : f(bsize/2)) digits + 1 bit. + +The product (ah+al)*(bh+bl) therefore has at most + + c(bsize/2) + (asize == bsize ? c(bsize/2) : f(bsize/2)) digits + 2 bits + +and we have asize + c(bsize/2) available digit positions. We need to show +this is always enough. An instance of c(bsize/2) cancels out in both, so +the question reduces to whether asize digits is enough to hold +(asize == bsize ? c(bsize/2) : f(bsize/2)) digits + 2 bits. If asize < bsize, +then we're asking whether asize digits >= f(bsize/2) digits + 2 bits. By #4, +asize is at least f(bsize/2)+1 digits, so this in turn reduces to whether 1 +digit is enough to hold 2 bits. This is so since SHIFT=15 >= 2. If +asize == bsize, then we're asking whether bsize digits is enough to hold +c(bsize/2) digits + 2 bits, or equivalently (by #1) whether f(bsize/2) digits +is enough to hold 2 bits. This is so if bsize >= 2, which holds because +bsize >= KARATSUBA_CUTOFF >= 2. + +Note that since there's always enough room for (ah+al)*(bh+bl), and that's +clearly >= each of ah*bh and al*bl, there's always enough room to subtract +ah*bh and al*bl too. +*/ + +/* b has at least twice the digits of a, and a is big enough that Karatsuba + * would pay off *if* the inputs had balanced sizes. View b as a sequence + * of slices, each with a->ob_size digits, and multiply the slices by a, + * one at a time. This gives k_mul balanced inputs to work with, and is + * also cache-friendly (we compute one double-width slice of the result + * at a time, then move on, never bactracking except for the helpful + * single-width slice overlap between successive partial sums). + */ +static PyLongObject * +k_lopsided_mul(PyLongObject *a, PyLongObject *b) +{ + const int asize = ABS(a->ob_size); + int bsize = ABS(b->ob_size); + int nbdone; /* # of b digits already multiplied */ + PyLongObject *ret; + PyLongObject *bslice = NULL; + + assert(asize > KARATSUBA_CUTOFF); + assert(2 * asize <= bsize); + + /* Allocate result space, and zero it out. */ + ret = _PyLong_New(asize + bsize); + if (ret == NULL) + return NULL; + memset(ret->ob_digit, 0, ret->ob_size * sizeof(digit)); + + /* Successive slices of b are copied into bslice. */ + bslice = _PyLong_New(asize); + if (bslice == NULL) + goto fail; + + nbdone = 0; + while (bsize > 0) { + PyLongObject *product; + const int nbtouse = MIN(bsize, asize); + + /* Multiply the next slice of b by a. */ + memcpy(bslice->ob_digit, b->ob_digit + nbdone, + nbtouse * sizeof(digit)); + bslice->ob_size = nbtouse; + product = k_mul(a, bslice); + if (product == NULL) + goto fail; + + /* Add into result. */ + (void)v_iadd(ret->ob_digit + nbdone, ret->ob_size - nbdone, + product->ob_digit, product->ob_size); + Py_DECREF(product); + + bsize -= nbtouse; + nbdone += nbtouse; + } + + Py_DECREF(bslice); + return long_normalize(ret); + + fail: + Py_DECREF(ret); + Py_XDECREF(bslice); + return NULL; +} + +static PyObject * +long_mul(PyLongObject *v, PyLongObject *w) +{ + PyLongObject *a, *b, *z; + + if (!convert_binop((PyObject *)v, (PyObject *)w, &a, &b)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + z = k_mul(a, b); + /* Negate if exactly one of the inputs is negative. */ + if (((a->ob_size ^ b->ob_size) < 0) && z) + z->ob_size = -(z->ob_size); + Py_DECREF(a); + Py_DECREF(b); + return (PyObject *)z; +} + +/* The / and % operators are now defined in terms of divmod(). + The expression a mod b has the value a - b*floor(a/b). + The long_divrem function gives the remainder after division of + |a| by |b|, with the sign of a. This is also expressed + as a - b*trunc(a/b), if trunc truncates towards zero. + Some examples: + a b a rem b a mod b + 13 10 3 3 + -13 10 -3 7 + 13 -10 3 -7 + -13 -10 -3 -3 + So, to get from rem to mod, we have to add b if a and b + have different signs. We then subtract one from the 'div' + part of the outcome to keep the invariant intact. */ + +static int +l_divmod(PyLongObject *v, PyLongObject *w, + PyLongObject **pdiv, PyLongObject **pmod) +{ + PyLongObject *div, *mod; + + if (long_divrem(v, w, &div, &mod) < 0) + return -1; + if ((mod->ob_size < 0 && w->ob_size > 0) || + (mod->ob_size > 0 && w->ob_size < 0)) { + PyLongObject *temp; + PyLongObject *one; + temp = (PyLongObject *) long_add(mod, w); + Py_DECREF(mod); + mod = temp; + if (mod == NULL) { + Py_DECREF(div); + return -1; + } + one = (PyLongObject *) PyLong_FromLong(1L); + if (one == NULL || + (temp = (PyLongObject *) long_sub(div, one)) == NULL) { + Py_DECREF(mod); + Py_DECREF(div); + Py_XDECREF(one); + return -1; + } + Py_DECREF(one); + Py_DECREF(div); + div = temp; + } + *pdiv = div; + *pmod = mod; + return 0; +} + +static PyObject * +long_div(PyObject *v, PyObject *w) +{ + PyLongObject *a, *b, *div, *mod; + + CONVERT_BINOP(v, w, &a, &b); + + if (l_divmod(a, b, &div, &mod) < 0) { + Py_DECREF(a); + Py_DECREF(b); + return NULL; + } + Py_DECREF(a); + Py_DECREF(b); + Py_DECREF(mod); + return (PyObject *)div; +} + +static PyObject * +long_classic_div(PyObject *v, PyObject *w) +{ + PyLongObject *a, *b, *div, *mod; + + CONVERT_BINOP(v, w, &a, &b); + + if (Py_DivisionWarningFlag && + PyErr_Warn(PyExc_DeprecationWarning, "classic long division") < 0) + div = NULL; + else if (l_divmod(a, b, &div, &mod) < 0) + div = NULL; + else + Py_DECREF(mod); + + Py_DECREF(a); + Py_DECREF(b); + return (PyObject *)div; +} + +static PyObject * +long_true_divide(PyObject *v, PyObject *w) +{ + PyLongObject *a, *b; + double ad, bd; + int aexp, bexp, failed; + + CONVERT_BINOP(v, w, &a, &b); + ad = _PyLong_AsScaledDouble((PyObject *)a, &aexp); + bd = _PyLong_AsScaledDouble((PyObject *)b, &bexp); + failed = (ad == -1.0 || bd == -1.0) && PyErr_Occurred(); + Py_DECREF(a); + Py_DECREF(b); + if (failed) + return NULL; + + if (bd == 0.0) { + PyErr_SetString(PyExc_ZeroDivisionError, + "long division or modulo by zero"); + return NULL; + } + + /* True value is very close to ad/bd * 2**(SHIFT*(aexp-bexp)) */ + ad /= bd; /* overflow/underflow impossible here */ + aexp -= bexp; + if (aexp > INT_MAX / SHIFT) + goto overflow; + else if (aexp < -(INT_MAX / SHIFT)) + return PyFloat_FromDouble(0.0); /* underflow to 0 */ + errno = 0; + ad = ldexp(ad, aexp * SHIFT); + if (Py_OVERFLOWED(ad)) /* ignore underflow to 0.0 */ + goto overflow; + return PyFloat_FromDouble(ad); + +overflow: + PyErr_SetString(PyExc_OverflowError, + "long/long too large for a float"); + return NULL; + +} + +static PyObject * +long_mod(PyObject *v, PyObject *w) +{ + PyLongObject *a, *b, *div, *mod; + + CONVERT_BINOP(v, w, &a, &b); + + if (l_divmod(a, b, &div, &mod) < 0) { + Py_DECREF(a); + Py_DECREF(b); + return NULL; + } + Py_DECREF(a); + Py_DECREF(b); + Py_DECREF(div); + return (PyObject *)mod; +} + +static PyObject * +long_divmod(PyObject *v, PyObject *w) +{ + PyLongObject *a, *b, *div, *mod; + PyObject *z; + + CONVERT_BINOP(v, w, &a, &b); + + if (l_divmod(a, b, &div, &mod) < 0) { + Py_DECREF(a); + Py_DECREF(b); + return NULL; + } + z = PyTuple_New(2); + if (z != NULL) { + PyTuple_SetItem(z, 0, (PyObject *) div); + PyTuple_SetItem(z, 1, (PyObject *) mod); + } + else { + Py_DECREF(div); + Py_DECREF(mod); + } + Py_DECREF(a); + Py_DECREF(b); + return z; +} + +static PyObject * +long_pow(PyObject *v, PyObject *w, PyObject *x) +{ + PyLongObject *a, *b; + PyObject *c; + PyLongObject *z, *div, *mod; + int size_b, i; + + CONVERT_BINOP(v, w, &a, &b); + if (PyLong_Check(x) || Py_None == x) { + c = x; + Py_INCREF(x); + } + else if (PyInt_Check(x)) { + c = PyLong_FromLong(PyInt_AS_LONG(x)); + } + else { + Py_DECREF(a); + Py_DECREF(b); + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + if (c != Py_None && ((PyLongObject *)c)->ob_size == 0) { + PyErr_SetString(PyExc_ValueError, + "pow() 3rd argument cannot be 0"); + z = NULL; + goto error; + } + + size_b = b->ob_size; + if (size_b < 0) { + Py_DECREF(a); + Py_DECREF(b); + Py_DECREF(c); + if (x != Py_None) { + PyErr_SetString(PyExc_TypeError, "pow() 2nd argument " + "cannot be negative when 3rd argument specified"); + return NULL; + } + /* Return a float. This works because we know that + this calls float_pow() which converts its + arguments to double. */ + return PyFloat_Type.tp_as_number->nb_power(v, w, x); + } + z = (PyLongObject *)PyLong_FromLong(1L); + for (i = 0; i < size_b; ++i) { + digit bi = b->ob_digit[i]; + int j; + + for (j = 0; j < SHIFT; ++j) { + PyLongObject *temp; + + if (bi & 1) { + temp = (PyLongObject *)long_mul(z, a); + Py_DECREF(z); + if (c!=Py_None && temp!=NULL) { + if (l_divmod(temp,(PyLongObject *)c, + &div,&mod) < 0) { + Py_DECREF(temp); + z = NULL; + goto error; + } + Py_XDECREF(div); + Py_DECREF(temp); + temp = mod; + } + z = temp; + if (z == NULL) + break; + } + bi >>= 1; + if (bi == 0 && i+1 == size_b) + break; + temp = (PyLongObject *)long_mul(a, a); + Py_DECREF(a); + if (c!=Py_None && temp!=NULL) { + if (l_divmod(temp, (PyLongObject *)c, &div, + &mod) < 0) { + Py_DECREF(temp); + z = NULL; + goto error; + } + Py_XDECREF(div); + Py_DECREF(temp); + temp = mod; + } + a = temp; + if (a == NULL) { + Py_DECREF(z); + z = NULL; + break; + } + } + if (a == NULL || z == NULL) + break; + } + if (c!=Py_None && z!=NULL) { + if (l_divmod(z, (PyLongObject *)c, &div, &mod) < 0) { + Py_DECREF(z); + z = NULL; + } + else { + Py_XDECREF(div); + Py_DECREF(z); + z = mod; + } + } + error: + Py_XDECREF(a); + Py_DECREF(b); + Py_DECREF(c); + return (PyObject *)z; +} + +static PyObject * +long_invert(PyLongObject *v) +{ + /* Implement ~x as -(x+1) */ + PyLongObject *x; + PyLongObject *w; + w = (PyLongObject *)PyLong_FromLong(1L); + if (w == NULL) + return NULL; + x = (PyLongObject *) long_add(v, w); + Py_DECREF(w); + if (x == NULL) + return NULL; + x->ob_size = -(x->ob_size); + return (PyObject *)x; +} + +static PyObject * +long_pos(PyLongObject *v) +{ + if (PyLong_CheckExact(v)) { + Py_INCREF(v); + return (PyObject *)v; + } + else + return _PyLong_Copy(v); +} + +static PyObject * +long_neg(PyLongObject *v) +{ + PyLongObject *z; + if (v->ob_size == 0 && PyLong_CheckExact(v)) { + /* -0 == 0 */ + Py_INCREF(v); + return (PyObject *) v; + } + z = (PyLongObject *)_PyLong_Copy(v); + if (z != NULL) + z->ob_size = -(v->ob_size); + return (PyObject *)z; +} + +static PyObject * +long_abs(PyLongObject *v) +{ + if (v->ob_size < 0) + return long_neg(v); + else + return long_pos(v); +} + +static int +long_nonzero(PyLongObject *v) +{ + return ABS(v->ob_size) != 0; +} + +static PyObject * +long_rshift(PyLongObject *v, PyLongObject *w) +{ + PyLongObject *a, *b; + PyLongObject *z = NULL; + long shiftby; + int newsize, wordshift, loshift, hishift, i, j; + digit lomask, himask; + + CONVERT_BINOP((PyObject *)v, (PyObject *)w, &a, &b); + + if (a->ob_size < 0) { + /* Right shifting negative numbers is harder */ + PyLongObject *a1, *a2; + a1 = (PyLongObject *) long_invert(a); + if (a1 == NULL) + goto rshift_error; + a2 = (PyLongObject *) long_rshift(a1, b); + Py_DECREF(a1); + if (a2 == NULL) + goto rshift_error; + z = (PyLongObject *) long_invert(a2); + Py_DECREF(a2); + } + else { + + shiftby = PyLong_AsLong((PyObject *)b); + if (shiftby == -1L && PyErr_Occurred()) + goto rshift_error; + if (shiftby < 0) { + PyErr_SetString(PyExc_ValueError, + "negative shift count"); + goto rshift_error; + } + wordshift = shiftby / SHIFT; + newsize = ABS(a->ob_size) - wordshift; + if (newsize <= 0) { + z = _PyLong_New(0); + Py_DECREF(a); + Py_DECREF(b); + return (PyObject *)z; + } + loshift = shiftby % SHIFT; + hishift = SHIFT - loshift; + lomask = ((digit)1 << hishift) - 1; + himask = MASK ^ lomask; + z = _PyLong_New(newsize); + if (z == NULL) + goto rshift_error; + if (a->ob_size < 0) + z->ob_size = -(z->ob_size); + for (i = 0, j = wordshift; i < newsize; i++, j++) { + z->ob_digit[i] = (a->ob_digit[j] >> loshift) & lomask; + if (i+1 < newsize) + z->ob_digit[i] |= + (a->ob_digit[j+1] << hishift) & himask; + } + z = long_normalize(z); + } +rshift_error: + Py_DECREF(a); + Py_DECREF(b); + return (PyObject *) z; + +} + +static PyObject * +long_lshift(PyObject *v, PyObject *w) +{ + /* This version due to Tim Peters */ + PyLongObject *a, *b; + PyLongObject *z = NULL; + long shiftby; + int oldsize, newsize, wordshift, remshift, i, j; + twodigits accum; + + CONVERT_BINOP(v, w, &a, &b); + + shiftby = PyLong_AsLong((PyObject *)b); + if (shiftby == -1L && PyErr_Occurred()) + goto lshift_error; + if (shiftby < 0) { + PyErr_SetString(PyExc_ValueError, "negative shift count"); + goto lshift_error; + } + if ((long)(int)shiftby != shiftby) { + PyErr_SetString(PyExc_ValueError, + "outrageous left shift count"); + goto lshift_error; + } + /* wordshift, remshift = divmod(shiftby, SHIFT) */ + wordshift = (int)shiftby / SHIFT; + remshift = (int)shiftby - wordshift * SHIFT; + + oldsize = ABS(a->ob_size); + newsize = oldsize + wordshift; + if (remshift) + ++newsize; + z = _PyLong_New(newsize); + if (z == NULL) + goto lshift_error; + if (a->ob_size < 0) + z->ob_size = -(z->ob_size); + for (i = 0; i < wordshift; i++) + z->ob_digit[i] = 0; + accum = 0; + for (i = wordshift, j = 0; j < oldsize; i++, j++) { + accum |= (twodigits)a->ob_digit[j] << remshift; + z->ob_digit[i] = (digit)(accum & MASK); + accum >>= SHIFT; + } + if (remshift) + z->ob_digit[newsize-1] = (digit)accum; + else + assert(!accum); + z = long_normalize(z); +lshift_error: + Py_DECREF(a); + Py_DECREF(b); + return (PyObject *) z; +} + + +/* Bitwise and/xor/or operations */ + +static PyObject * +long_bitwise(PyLongObject *a, + int op, /* '&', '|', '^' */ + PyLongObject *b) +{ + digit maska, maskb; /* 0 or MASK */ + int negz; + int size_a, size_b, size_z; + PyLongObject *z; + int i; + digit diga, digb; + PyObject *v; + + if (a->ob_size < 0) { + a = (PyLongObject *) long_invert(a); + maska = MASK; + } + else { + Py_INCREF(a); + maska = 0; + } + if (b->ob_size < 0) { + b = (PyLongObject *) long_invert(b); + maskb = MASK; + } + else { + Py_INCREF(b); + maskb = 0; + } + + negz = 0; + switch (op) { + case '^': + if (maska != maskb) { + maska ^= MASK; + negz = -1; + } + break; + case '&': + if (maska && maskb) { + op = '|'; + maska ^= MASK; + maskb ^= MASK; + negz = -1; + } + break; + case '|': + if (maska || maskb) { + op = '&'; + maska ^= MASK; + maskb ^= MASK; + negz = -1; + } + break; + } + + /* JRH: The original logic here was to allocate the result value (z) + as the longer of the two operands. However, there are some cases + where the result is guaranteed to be shorter than that: AND of two + positives, OR of two negatives: use the shorter number. AND with + mixed signs: use the positive number. OR with mixed signs: use the + negative number. After the transformations above, op will be '&' + iff one of these cases applies, and mask will be non-0 for operands + whose length should be ignored. + */ + + size_a = a->ob_size; + size_b = b->ob_size; + size_z = op == '&' + ? (maska + ? size_b + : (maskb ? size_a : MIN(size_a, size_b))) + : MAX(size_a, size_b); + z = _PyLong_New(size_z); + if (a == NULL || b == NULL || z == NULL) { + Py_XDECREF(a); + Py_XDECREF(b); + Py_XDECREF(z); + return NULL; + } + + for (i = 0; i < size_z; ++i) { + diga = (i < size_a ? a->ob_digit[i] : 0) ^ maska; + digb = (i < size_b ? b->ob_digit[i] : 0) ^ maskb; + switch (op) { + case '&': z->ob_digit[i] = diga & digb; break; + case '|': z->ob_digit[i] = diga | digb; break; + case '^': z->ob_digit[i] = diga ^ digb; break; + } + } + + Py_DECREF(a); + Py_DECREF(b); + z = long_normalize(z); + if (negz == 0) + return (PyObject *) z; + v = long_invert(z); + Py_DECREF(z); + return v; +} + +static PyObject * +long_and(PyObject *v, PyObject *w) +{ + PyLongObject *a, *b; + PyObject *c; + CONVERT_BINOP(v, w, &a, &b); + c = long_bitwise(a, '&', b); + Py_DECREF(a); + Py_DECREF(b); + return c; +} + +static PyObject * +long_xor(PyObject *v, PyObject *w) +{ + PyLongObject *a, *b; + PyObject *c; + CONVERT_BINOP(v, w, &a, &b); + c = long_bitwise(a, '^', b); + Py_DECREF(a); + Py_DECREF(b); + return c; +} + +static PyObject * +long_or(PyObject *v, PyObject *w) +{ + PyLongObject *a, *b; + PyObject *c; + CONVERT_BINOP(v, w, &a, &b); + c = long_bitwise(a, '|', b); + Py_DECREF(a); + Py_DECREF(b); + return c; +} + +static int +long_coerce(PyObject **pv, PyObject **pw) +{ + if (PyInt_Check(*pw)) { + *pw = PyLong_FromLong(PyInt_AS_LONG(*pw)); + Py_INCREF(*pv); + return 0; + } + else if (PyLong_Check(*pw)) { + Py_INCREF(*pv); + Py_INCREF(*pw); + return 0; + } + return 1; /* Can't do it */ +} + +static PyObject * +long_long(PyObject *v) +{ + Py_INCREF(v); + return v; +} + +static PyObject * +long_int(PyObject *v) +{ + long x; + x = PyLong_AsLong(v); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) { + PyErr_Clear(); + if (PyLong_CheckExact(v)) { + Py_INCREF(v); + return v; + } + else + return _PyLong_Copy((PyLongObject *)v); + } + else + return NULL; + } + return PyInt_FromLong(x); +} + +static PyObject * +long_float(PyObject *v) +{ + double result; + result = PyLong_AsDouble(v); + if (result == -1.0 && PyErr_Occurred()) + return NULL; + return PyFloat_FromDouble(result); +} + +static PyObject * +long_oct(PyObject *v) +{ + return long_format(v, 8, 1); +} + +static PyObject * +long_hex(PyObject *v) +{ + return long_format(v, 16, 1); +} + +static PyObject * +long_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + +static PyObject * +long_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *x = NULL; + int base = -909; /* unlikely! */ + static char *kwlist[] = {"x", "base", 0}; + + if (type != &PyLong_Type) + return long_subtype_new(type, args, kwds); /* Wimp out */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oi:long", kwlist, + &x, &base)) + return NULL; + if (x == NULL) + return PyLong_FromLong(0L); + if (base == -909) + return PyNumber_Long(x); + else if (PyString_Check(x)) + return PyLong_FromString(PyString_AS_STRING(x), NULL, base); +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(x)) + return PyLong_FromUnicode(PyUnicode_AS_UNICODE(x), + PyUnicode_GET_SIZE(x), + base); +#endif + else { + PyErr_SetString(PyExc_TypeError, + "long() can't convert non-string with explicit base"); + return NULL; + } +} + +/* Wimpy, slow approach to tp_new calls for subtypes of long: + first create a regular long from whatever arguments we got, + then allocate a subtype instance and initialize it from + the regular long. The regular long is then thrown away. +*/ +static PyObject * +long_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyLongObject *tmp, *new; + int i, n; + + assert(PyType_IsSubtype(type, &PyLong_Type)); + tmp = (PyLongObject *)long_new(&PyLong_Type, args, kwds); + if (tmp == NULL) + return NULL; + assert(PyLong_CheckExact(tmp)); + n = tmp->ob_size; + if (n < 0) + n = -n; + new = (PyLongObject *)type->tp_alloc(type, n); + if (new == NULL) { + Py_DECREF(tmp); + return NULL; + } + assert(PyLong_Check(new)); + new->ob_size = tmp->ob_size; + for (i = 0; i < n; i++) + new->ob_digit[i] = tmp->ob_digit[i]; + Py_DECREF(tmp); + return (PyObject *)new; +} + +static PyObject * +long_getnewargs(PyLongObject *v) +{ + return Py_BuildValue("(N)", _PyLong_Copy(v)); +} + +static PyMethodDef long_methods[] = { + {"__getnewargs__", (PyCFunction)long_getnewargs, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +PyDoc_STRVAR(long_doc, +"long(x[, base]) -> integer\n\ +\n\ +Convert a string or number to a long integer, if possible. A floating\n\ +point argument will be truncated towards zero (this does not include a\n\ +string representation of a floating point number!) When converting a\n\ +string, use the optional base. It is an error to supply a base when\n\ +converting a non-string."); + +static PyNumberMethods long_as_number = { + (binaryfunc) long_add, /*nb_add*/ + (binaryfunc) long_sub, /*nb_subtract*/ + (binaryfunc) long_mul, /*nb_multiply*/ + (binaryfunc) long_classic_div, /*nb_divide*/ + (binaryfunc) long_mod, /*nb_remainder*/ + (binaryfunc) long_divmod, /*nb_divmod*/ + (ternaryfunc) long_pow, /*nb_power*/ + (unaryfunc) long_neg, /*nb_negative*/ + (unaryfunc) long_pos, /*tp_positive*/ + (unaryfunc) long_abs, /*tp_absolute*/ + (inquiry) long_nonzero, /*tp_nonzero*/ + (unaryfunc) long_invert, /*nb_invert*/ + (binaryfunc) long_lshift, /*nb_lshift*/ + (binaryfunc) long_rshift, /*nb_rshift*/ + (binaryfunc) long_and, /*nb_and*/ + (binaryfunc) long_xor, /*nb_xor*/ + (binaryfunc) long_or, /*nb_or*/ + (coercion) long_coerce, /*nb_coerce*/ + (unaryfunc) long_int, /*nb_int*/ + (unaryfunc) long_long, /*nb_long*/ + (unaryfunc) long_float, /*nb_float*/ + (unaryfunc) long_oct, /*nb_oct*/ + (unaryfunc) long_hex, /*nb_hex*/ + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ + 0, /* nb_inplace_divide */ + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + (binaryfunc)long_div, /* nb_floor_divide */ + long_true_divide, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +}; + +PyTypeObject PyLong_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "long", /* tp_name */ + sizeof(PyLongObject) - sizeof(digit), /* tp_basicsize */ + sizeof(digit), /* tp_itemsize */ + (destructor)long_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)long_compare, /* tp_compare */ + (reprfunc)long_repr, /* tp_repr */ + &long_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)long_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)long_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + long_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + long_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + long_new, /* tp_new */ + PyObject_Del, /* tp_free */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/methodobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/methodobject.c new file mode 100644 index 00000000..6c2f6270 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/methodobject.c @@ -0,0 +1,374 @@ + +/* Method object implementation */ + +#include "Python.h" +#include "structmember.h" + +static PyCFunctionObject *free_list = NULL; + +PyObject * +PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) +{ + PyCFunctionObject *op; + op = free_list; + if (op != NULL) { + free_list = (PyCFunctionObject *)(op->m_self); + PyObject_INIT(op, &PyCFunction_Type); + } + else { + op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type); + if (op == NULL) + return NULL; + } + op->m_ml = ml; + Py_XINCREF(self); + op->m_self = self; + Py_XINCREF(module); + op->m_module = module; + _PyObject_GC_TRACK(op); + return (PyObject *)op; +} + +PyCFunction +PyCFunction_GetFunction(PyObject *op) +{ + if (!PyCFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyCFunctionObject *)op) -> m_ml -> ml_meth; +} + +PyObject * +PyCFunction_GetSelf(PyObject *op) +{ + if (!PyCFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyCFunctionObject *)op) -> m_self; +} + +int +PyCFunction_GetFlags(PyObject *op) +{ + if (!PyCFunction_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + return ((PyCFunctionObject *)op) -> m_ml -> ml_flags; +} + +PyObject * +PyCFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) +{ + PyCFunctionObject* f = (PyCFunctionObject*)func; + PyCFunction meth = PyCFunction_GET_FUNCTION(func); + PyObject *self = PyCFunction_GET_SELF(func); + int size; + + switch (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC)) { + case METH_VARARGS: + if (kw == NULL || PyDict_Size(kw) == 0) + return (*meth)(self, arg); + break; + case METH_VARARGS | METH_KEYWORDS: + case METH_OLDARGS | METH_KEYWORDS: + return (*(PyCFunctionWithKeywords)meth)(self, arg, kw); + case METH_NOARGS: + if (kw == NULL || PyDict_Size(kw) == 0) { + size = PyTuple_GET_SIZE(arg); + if (size == 0) + return (*meth)(self, NULL); + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%d given)", + f->m_ml->ml_name, size); + return NULL; + } + break; + case METH_O: + if (kw == NULL || PyDict_Size(kw) == 0) { + size = PyTuple_GET_SIZE(arg); + if (size == 1) + return (*meth)(self, PyTuple_GET_ITEM(arg, 0)); + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%d given)", + f->m_ml->ml_name, size); + return NULL; + } + break; + case METH_OLDARGS: + /* the really old style */ + if (kw == NULL || PyDict_Size(kw) == 0) { + size = PyTuple_GET_SIZE(arg); + if (size == 1) + arg = PyTuple_GET_ITEM(arg, 0); + else if (size == 0) + arg = NULL; + return (*meth)(self, arg); + } + break; + default: + PyErr_BadInternalCall(); + return NULL; + } + PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", + f->m_ml->ml_name); + return NULL; +} + +/* Methods (the standard built-in methods, that is) */ + +static void +meth_dealloc(PyCFunctionObject *m) +{ + _PyObject_GC_UNTRACK(m); + Py_XDECREF(m->m_self); + Py_XDECREF(m->m_module); + m->m_self = (PyObject *)free_list; + free_list = m; +} + +static PyObject * +meth_get__doc__(PyCFunctionObject *m, void *closure) +{ + char *doc = m->m_ml->ml_doc; + + if (doc != NULL) + return PyString_FromString(doc); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +meth_get__name__(PyCFunctionObject *m, void *closure) +{ + return PyString_FromString(m->m_ml->ml_name); +} + +static int +meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg) +{ + int err; + if (m->m_self != NULL) { + err = visit(m->m_self, arg); + if (err) + return err; + } + if (m->m_module != NULL) { + err = visit(m->m_module, arg); + if (err) + return err; + } + return 0; +} + +static PyObject * +meth_get__self__(PyCFunctionObject *m, void *closure) +{ + PyObject *self; + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, + "method.__self__ not accessible in restricted mode"); + return NULL; + } + self = m->m_self; + if (self == NULL) + self = Py_None; + Py_INCREF(self); + return self; +} + +static PyGetSetDef meth_getsets [] = { + {"__doc__", (getter)meth_get__doc__, NULL, NULL}, + {"__name__", (getter)meth_get__name__, NULL, NULL}, + {"__self__", (getter)meth_get__self__, NULL, NULL}, + {0} +}; + +#define OFF(x) offsetof(PyCFunctionObject, x) + +static PyMemberDef meth_members[] = { + {"__module__", T_OBJECT, OFF(m_module), WRITE_RESTRICTED}, + {NULL} +}; + +static PyObject * +meth_repr(PyCFunctionObject *m) +{ + if (m->m_self == NULL) + return PyString_FromFormat("", + m->m_ml->ml_name); + return PyString_FromFormat("", + m->m_ml->ml_name, + m->m_self->ob_type->tp_name, + m->m_self); +} + +static int +meth_compare(PyCFunctionObject *a, PyCFunctionObject *b) +{ + if (a->m_self != b->m_self) + return (a->m_self < b->m_self) ? -1 : 1; + if (a->m_ml->ml_meth == b->m_ml->ml_meth) + return 0; + if (strcmp(a->m_ml->ml_name, b->m_ml->ml_name) < 0) + return -1; + else + return 1; +} + +static long +meth_hash(PyCFunctionObject *a) +{ + long x,y; + if (a->m_self == NULL) + x = 0; + else { + x = PyObject_Hash(a->m_self); + if (x == -1) + return -1; + } + y = _Py_HashPointer((void*)(a->m_ml->ml_meth)); + if (y == -1) + return -1; + x ^= y; + if (x == -1) + x = -2; + return x; +} + + +PyTypeObject PyCFunction_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "builtin_function_or_method", + sizeof(PyCFunctionObject), + 0, + (destructor)meth_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)meth_compare, /* tp_compare */ + (reprfunc)meth_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)meth_hash, /* tp_hash */ + PyCFunction_Call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)meth_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + meth_members, /* tp_members */ + meth_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ +}; + +/* List all methods in a chain -- helper for findmethodinchain */ + +static PyObject * +listmethodchain(PyMethodChain *chain) +{ + PyMethodChain *c; + PyMethodDef *ml; + int i, n; + PyObject *v; + + n = 0; + for (c = chain; c != NULL; c = c->link) { + for (ml = c->methods; ml->ml_name != NULL; ml++) + n++; + } + v = PyList_New(n); + if (v == NULL) + return NULL; + i = 0; + for (c = chain; c != NULL; c = c->link) { + for (ml = c->methods; ml->ml_name != NULL; ml++) { + PyList_SetItem(v, i, PyString_FromString(ml->ml_name)); + i++; + } + } + if (PyErr_Occurred()) { + Py_DECREF(v); + return NULL; + } + PyList_Sort(v); + return v; +} + +/* Find a method in a method chain */ + +PyObject * +Py_FindMethodInChain(PyMethodChain *chain, PyObject *self, char *name) +{ + if (name[0] == '_' && name[1] == '_') { + if (strcmp(name, "__methods__") == 0) + return listmethodchain(chain); + if (strcmp(name, "__doc__") == 0) { + char *doc = self->ob_type->tp_doc; + if (doc != NULL) + return PyString_FromString(doc); + } + } + while (chain != NULL) { + PyMethodDef *ml = chain->methods; + for (; ml->ml_name != NULL; ml++) { + if (name[0] == ml->ml_name[0] && + strcmp(name+1, ml->ml_name+1) == 0) + /* XXX */ + return PyCFunction_New(ml, self); + } + chain = chain->link; + } + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +/* Find a method in a single method list */ + +PyObject * +Py_FindMethod(PyMethodDef *methods, PyObject *self, char *name) +{ + PyMethodChain chain; + chain.methods = methods; + chain.link = NULL; + return Py_FindMethodInChain(&chain, self, name); +} + +/* Clear out the free list */ + +void +PyCFunction_Fini(void) +{ + while (free_list) { + PyCFunctionObject *v = free_list; + free_list = (PyCFunctionObject *)(v->m_self); + PyObject_GC_Del(v); + } +} + +/* PyCFunction_New() is now just a macro that calls PyCFunction_NewEx(), + but it's part of the API so we need to keep a function around that + existing C extensions can call. +*/ + +#undef PyCFunction_New +PyAPI_FUNC(PyObject *) PyCFunction_New(PyMethodDef *, PyObject *); + +PyObject * +PyCFunction_New(PyMethodDef *ml, PyObject *self) +{ + return PyCFunction_NewEx(ml, self, NULL); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/moduleobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/moduleobject.c new file mode 100644 index 00000000..31ffdc00 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/moduleobject.c @@ -0,0 +1,260 @@ + +/* Module object implementation */ + +#include "Python.h" +#include "structmember.h" + +typedef struct { + PyObject_HEAD + PyObject *md_dict; +} PyModuleObject; + +static PyMemberDef module_members[] = { + {"__dict__", T_OBJECT, offsetof(PyModuleObject, md_dict), READONLY}, + {0} +}; + +PyObject * +PyModule_New(char *name) +{ + PyModuleObject *m; + PyObject *nameobj; + m = PyObject_GC_New(PyModuleObject, &PyModule_Type); + if (m == NULL) + return NULL; + nameobj = PyString_FromString(name); + m->md_dict = PyDict_New(); + if (m->md_dict == NULL || nameobj == NULL) + goto fail; + if (PyDict_SetItemString(m->md_dict, "__name__", nameobj) != 0) + goto fail; + if (PyDict_SetItemString(m->md_dict, "__doc__", Py_None) != 0) + goto fail; + Py_DECREF(nameobj); + PyObject_GC_Track(m); + return (PyObject *)m; + + fail: + Py_XDECREF(nameobj); + Py_DECREF(m); + return NULL; +} + +PyObject * +PyModule_GetDict(PyObject *m) +{ + PyObject *d; + if (!PyModule_Check(m)) { + PyErr_BadInternalCall(); + return NULL; + } + d = ((PyModuleObject *)m) -> md_dict; + if (d == NULL) + ((PyModuleObject *)m) -> md_dict = d = PyDict_New(); + return d; +} + +char * +PyModule_GetName(PyObject *m) +{ + PyObject *d; + PyObject *nameobj; + if (!PyModule_Check(m)) { + PyErr_BadArgument(); + return NULL; + } + d = ((PyModuleObject *)m)->md_dict; + if (d == NULL || + (nameobj = PyDict_GetItemString(d, "__name__")) == NULL || + !PyString_Check(nameobj)) + { + PyErr_SetString(PyExc_SystemError, "nameless module"); + return NULL; + } + return PyString_AsString(nameobj); +} + +char * +PyModule_GetFilename(PyObject *m) +{ + PyObject *d; + PyObject *fileobj; + if (!PyModule_Check(m)) { + PyErr_BadArgument(); + return NULL; + } + d = ((PyModuleObject *)m)->md_dict; + if (d == NULL || + (fileobj = PyDict_GetItemString(d, "__file__")) == NULL || + !PyString_Check(fileobj)) + { + PyErr_SetString(PyExc_SystemError, "module filename missing"); + return NULL; + } + return PyString_AsString(fileobj); +} + +void +_PyModule_Clear(PyObject *m) +{ + /* To make the execution order of destructors for global + objects a bit more predictable, we first zap all objects + whose name starts with a single underscore, before we clear + the entire dictionary. We zap them by replacing them with + None, rather than deleting them from the dictionary, to + avoid rehashing the dictionary (to some extent). */ + + int pos; + PyObject *key, *value; + PyObject *d; + + d = ((PyModuleObject *)m)->md_dict; + if (d == NULL) + return; + + /* First, clear only names starting with a single underscore */ + pos = 0; + while (PyDict_Next(d, &pos, &key, &value)) { + if (value != Py_None && PyString_Check(key)) { + char *s = PyString_AsString(key); + if (s[0] == '_' && s[1] != '_') { + if (Py_VerboseFlag > 1) + PySys_WriteStderr("# clear[1] %s\n", s); + PyDict_SetItem(d, key, Py_None); + } + } + } + + /* Next, clear all names except for __builtins__ */ + pos = 0; + while (PyDict_Next(d, &pos, &key, &value)) { + if (value != Py_None && PyString_Check(key)) { + char *s = PyString_AsString(key); + if (s[0] != '_' || strcmp(s, "__builtins__") != 0) { + if (Py_VerboseFlag > 1) + PySys_WriteStderr("# clear[2] %s\n", s); + PyDict_SetItem(d, key, Py_None); + } + } + } + + /* Note: we leave __builtins__ in place, so that destructors + of non-global objects defined in this module can still use + builtins, in particularly 'None'. */ + +} + +/* Methods */ + +static int +module_init(PyModuleObject *m, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", "doc", NULL}; + PyObject *dict, *name = Py_None, *doc = Py_None; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "S|O", kwlist, + &name, &doc)) + return -1; + dict = m->md_dict; + if (dict == NULL) { + dict = PyDict_New(); + if (dict == NULL) + return -1; + m->md_dict = dict; + } + if (PyDict_SetItemString(dict, "__name__", name) < 0) + return -1; + if (PyDict_SetItemString(dict, "__doc__", doc) < 0) + return -1; + return 0; +} + +static void +module_dealloc(PyModuleObject *m) +{ + PyObject_GC_UnTrack(m); + if (m->md_dict != NULL) { + _PyModule_Clear((PyObject *)m); + Py_DECREF(m->md_dict); + } + m->ob_type->tp_free((PyObject *)m); +} + +static PyObject * +module_repr(PyModuleObject *m) +{ + char *name; + char *filename; + + name = PyModule_GetName((PyObject *)m); + if (name == NULL) { + PyErr_Clear(); + name = "?"; + } + filename = PyModule_GetFilename((PyObject *)m); + if (filename == NULL) { + PyErr_Clear(); + return PyString_FromFormat("", name); + } + return PyString_FromFormat("", name, filename); +} + +/* We only need a traverse function, no clear function: If the module + is in a cycle, md_dict will be cleared as well, which will break + the cycle. */ +static int +module_traverse(PyModuleObject *m, visitproc visit, void *arg) +{ + if (m->md_dict != NULL) + return visit(m->md_dict, arg); + return 0; +} + +PyDoc_STRVAR(module_doc, +"module(name[, doc])\n\ +\n\ +Create a module object.\n\ +The name must be a string; the optional doc argument can have any type."); + +PyTypeObject PyModule_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "module", /* tp_name */ + sizeof(PyModuleObject), /* tp_size */ + 0, /* tp_itemsize */ + (destructor)module_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)module_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + module_doc, /* tp_doc */ + (traverseproc)module_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + module_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof(PyModuleObject, md_dict), /* tp_dictoffset */ + (initproc)module_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/object.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/object.c new file mode 100644 index 00000000..86f4b6ac --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/object.c @@ -0,0 +1,2228 @@ + +/* Generic object operations; and implementation of None (NoObject) */ + +#include "Python.h" + +#ifdef macintosh +#include "macglue.h" +#endif + +#ifdef Py_REF_DEBUG +long _Py_RefTotal; +#endif + +int Py_DivisionWarningFlag; + +/* Object allocation routines used by NEWOBJ and NEWVAROBJ macros. + These are used by the individual routines for object creation. + Do not call them otherwise, they do not initialize the object! */ + +#ifdef Py_TRACE_REFS +/* Head of circular doubly-linked list of all objects. These are linked + * together via the _ob_prev and _ob_next members of a PyObject, which + * exist only in a Py_TRACE_REFS build. + */ +static PyObject refchain = {&refchain, &refchain}; + +/* Insert op at the front of the list of all objects. If force is true, + * op is added even if _ob_prev and _ob_next are non-NULL already. If + * force is false amd _ob_prev or _ob_next are non-NULL, do nothing. + * force should be true if and only if op points to freshly allocated, + * uninitialized memory, or you've unlinked op from the list and are + * relinking it into the front. + * Note that objects are normally added to the list via _Py_NewReference, + * which is called by PyObject_Init. Not all objects are initialized that + * way, though; exceptions include statically allocated type objects, and + * statically allocated singletons (like Py_True and Py_None). + */ +void +_Py_AddToAllObjects(PyObject *op, int force) +{ +#ifdef Py_DEBUG + if (!force) { + /* If it's initialized memory, op must be in or out of + * the list unambiguously. + */ + assert((op->_ob_prev == NULL) == (op->_ob_next == NULL)); + } +#endif + if (force || op->_ob_prev == NULL) { + op->_ob_next = refchain._ob_next; + op->_ob_prev = &refchain; + refchain._ob_next->_ob_prev = op; + refchain._ob_next = op; + } +} +#endif /* Py_TRACE_REFS */ + +#ifdef COUNT_ALLOCS +static PyTypeObject *type_list; +extern int tuple_zero_allocs, fast_tuple_allocs; +extern int quick_int_allocs, quick_neg_int_allocs; +extern int null_strings, one_strings; +void +dump_counts(void) +{ + PyTypeObject *tp; + + for (tp = type_list; tp; tp = tp->tp_next) + fprintf(stderr, "%s alloc'd: %d, freed: %d, max in use: %d\n", + tp->tp_name, tp->tp_allocs, tp->tp_frees, + tp->tp_maxalloc); + fprintf(stderr, "fast tuple allocs: %d, empty: %d\n", + fast_tuple_allocs, tuple_zero_allocs); + fprintf(stderr, "fast int allocs: pos: %d, neg: %d\n", + quick_int_allocs, quick_neg_int_allocs); + fprintf(stderr, "null strings: %d, 1-strings: %d\n", + null_strings, one_strings); +} + +PyObject * +get_counts(void) +{ + PyTypeObject *tp; + PyObject *result; + PyObject *v; + + result = PyList_New(0); + if (result == NULL) + return NULL; + for (tp = type_list; tp; tp = tp->tp_next) { + v = Py_BuildValue("(siii)", tp->tp_name, tp->tp_allocs, + tp->tp_frees, tp->tp_maxalloc); + if (v == NULL) { + Py_DECREF(result); + return NULL; + } + if (PyList_Append(result, v) < 0) { + Py_DECREF(v); + Py_DECREF(result); + return NULL; + } + Py_DECREF(v); + } + return result; +} + +void +inc_count(PyTypeObject *tp) +{ + if (tp->tp_allocs == 0) { + /* first time; insert in linked list */ + if (tp->tp_next != NULL) /* sanity check */ + Py_FatalError("XXX inc_count sanity check"); + tp->tp_next = type_list; + /* Note that as of Python 2.2, heap-allocated type objects + * can go away, but this code requires that they stay alive + * until program exit. That's why we're careful with + * refcounts here. type_list gets a new reference to tp, + * while ownership of the reference type_list used to hold + * (if any) was transferred to tp->tp_next in the line above. + * tp is thus effectively immortal after this. + */ + Py_INCREF(tp); + type_list = tp; +#ifdef Py_TRACE_REFS + /* Also insert in the doubly-linked list of all objects, + * if not already there. + */ + _Py_AddToAllObjects((PyObject *)tp, 0); +#endif + } + tp->tp_allocs++; + if (tp->tp_allocs - tp->tp_frees > tp->tp_maxalloc) + tp->tp_maxalloc = tp->tp_allocs - tp->tp_frees; +} +#endif + +#ifdef Py_REF_DEBUG +/* Log a fatal error; doesn't return. */ +void +_Py_NegativeRefcount(const char *fname, int lineno, PyObject *op) +{ + char buf[300]; + + PyOS_snprintf(buf, sizeof(buf), + "%s:%i object at %p has negative ref count %i", + fname, lineno, op, op->ob_refcnt); + Py_FatalError(buf); +} + +#endif /* Py_REF_DEBUG */ + +PyObject * +PyObject_Init(PyObject *op, PyTypeObject *tp) +{ + if (op == NULL) + return PyErr_NoMemory(); + /* Any changes should be reflected in PyObject_INIT (objimpl.h) */ + op->ob_type = tp; + _Py_NewReference(op); + return op; +} + +PyVarObject * +PyObject_InitVar(PyVarObject *op, PyTypeObject *tp, int size) +{ + if (op == NULL) + return (PyVarObject *) PyErr_NoMemory(); + /* Any changes should be reflected in PyObject_INIT_VAR */ + op->ob_size = size; + op->ob_type = tp; + _Py_NewReference((PyObject *)op); + return op; +} + +PyObject * +_PyObject_New(PyTypeObject *tp) +{ + PyObject *op; + op = (PyObject *) PyObject_MALLOC(_PyObject_SIZE(tp)); + if (op == NULL) + return PyErr_NoMemory(); + return PyObject_INIT(op, tp); +} + +PyVarObject * +_PyObject_NewVar(PyTypeObject *tp, int nitems) +{ + PyVarObject *op; + const size_t size = _PyObject_VAR_SIZE(tp, nitems); + op = (PyVarObject *) PyObject_MALLOC(size); + if (op == NULL) + return (PyVarObject *)PyErr_NoMemory(); + return PyObject_INIT_VAR(op, tp, nitems); +} + +/* for binary compatibility with 2.2 */ +#undef _PyObject_Del +void +_PyObject_Del(PyObject *op) +{ + PyObject_FREE(op); +} + +/* Implementation of PyObject_Print with recursion checking */ +static int +internal_print(PyObject *op, FILE *fp, int flags, int nesting) +{ + int ret = 0; + if (nesting > 10) { + PyErr_SetString(PyExc_RuntimeError, "print recursion"); + return -1; + } + if (PyErr_CheckSignals()) + return -1; +#ifdef USE_STACKCHECK + if (PyOS_CheckStack()) { + PyErr_SetString(PyExc_MemoryError, "stack overflow"); + return -1; + } +#endif + clearerr(fp); /* Clear any previous error condition */ + if (op == NULL) { + fprintf(fp, ""); + } + else { + if (op->ob_refcnt <= 0) + fprintf(fp, "", + op->ob_refcnt, op); + else if (op->ob_type->tp_print == NULL) { + PyObject *s; + if (flags & Py_PRINT_RAW) + s = PyObject_Str(op); + else + s = PyObject_Repr(op); + if (s == NULL) + ret = -1; + else { + ret = internal_print(s, fp, Py_PRINT_RAW, + nesting+1); + } + Py_XDECREF(s); + } + else + ret = (*op->ob_type->tp_print)(op, fp, flags); + } + if (ret == 0) { + if (ferror(fp)) { + PyErr_SetFromErrno(PyExc_IOError); + clearerr(fp); + ret = -1; + } + } + return ret; +} + +int +PyObject_Print(PyObject *op, FILE *fp, int flags) +{ + return internal_print(op, fp, flags, 0); +} + + +/* For debugging convenience. See Misc/gdbinit for some useful gdb hooks */ +void _PyObject_Dump(PyObject* op) +{ + if (op == NULL) + fprintf(stderr, "NULL\n"); + else { + fprintf(stderr, "object : "); + (void)PyObject_Print(op, stderr, 0); + fprintf(stderr, "\n" + "type : %s\n" + "refcount: %d\n" + "address : %p\n", + op->ob_type==NULL ? "NULL" : op->ob_type->tp_name, + op->ob_refcnt, + op); + } +} + +PyObject * +PyObject_Repr(PyObject *v) +{ + if (PyErr_CheckSignals()) + return NULL; +#ifdef USE_STACKCHECK + if (PyOS_CheckStack()) { + PyErr_SetString(PyExc_MemoryError, "stack overflow"); + return NULL; + } +#endif + if (v == NULL) + return PyString_FromString(""); + else if (v->ob_type->tp_repr == NULL) + return PyString_FromFormat("<%s object at %p>", + v->ob_type->tp_name, v); + else { + PyObject *res; + res = (*v->ob_type->tp_repr)(v); + if (res == NULL) + return NULL; +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(res)) { + PyObject* str; + str = PyUnicode_AsUnicodeEscapeString(res); + Py_DECREF(res); + if (str) + res = str; + else + return NULL; + } +#endif + if (!PyString_Check(res)) { + PyErr_Format(PyExc_TypeError, + "__repr__ returned non-string (type %.200s)", + res->ob_type->tp_name); + Py_DECREF(res); + return NULL; + } + return res; + } +} + +PyObject * +PyObject_Str(PyObject *v) +{ + PyObject *res; + + if (v == NULL) + return PyString_FromString(""); + if (PyString_CheckExact(v)) { + Py_INCREF(v); + return v; + } + if (v->ob_type->tp_str == NULL) + return PyObject_Repr(v); + + res = (*v->ob_type->tp_str)(v); + if (res == NULL) + return NULL; +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(res)) { + PyObject* str; + str = PyUnicode_AsEncodedString(res, NULL, NULL); + Py_DECREF(res); + if (str) + res = str; + else + return NULL; + } +#endif + if (!PyString_Check(res)) { + PyErr_Format(PyExc_TypeError, + "__str__ returned non-string (type %.200s)", + res->ob_type->tp_name); + Py_DECREF(res); + return NULL; + } + return res; +} + +#ifdef Py_USING_UNICODE +PyObject * +PyObject_Unicode(PyObject *v) +{ + PyObject *res; + + if (v == NULL) + res = PyString_FromString(""); + if (PyUnicode_CheckExact(v)) { + Py_INCREF(v); + return v; + } + if (PyUnicode_Check(v)) { + /* For a Unicode subtype that's not a Unicode object, + return a true Unicode object with the same data. */ + return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(v), + PyUnicode_GET_SIZE(v)); + } + if (PyString_Check(v)) { + Py_INCREF(v); + res = v; + } + else { + PyObject *func; + static PyObject *unicodestr; + /* XXX As soon as we have a tp_unicode slot, we should + check this before trying the __unicode__ + method. */ + if (unicodestr == NULL) { + unicodestr= PyString_InternFromString( + "__unicode__"); + if (unicodestr == NULL) + return NULL; + } + func = PyObject_GetAttr(v, unicodestr); + if (func != NULL) { + res = PyEval_CallObject(func, (PyObject *)NULL); + Py_DECREF(func); + } + else { + PyErr_Clear(); + if (v->ob_type->tp_str != NULL) + res = (*v->ob_type->tp_str)(v); + else + res = PyObject_Repr(v); + } + } + if (res == NULL) + return NULL; + if (!PyUnicode_Check(res)) { + PyObject *str; + str = PyUnicode_FromEncodedObject(res, NULL, "strict"); + Py_DECREF(res); + if (str) + res = str; + else + return NULL; + } + return res; +} +#endif + + +/* Helper to warn about deprecated tp_compare return values. Return: + -2 for an exception; + -1 if v < w; + 0 if v == w; + 1 if v > w. + (This function cannot return 2.) +*/ +static int +adjust_tp_compare(int c) +{ + if (PyErr_Occurred()) { + if (c != -1 && c != -2) { + PyObject *t, *v, *tb; + PyErr_Fetch(&t, &v, &tb); + if (PyErr_Warn(PyExc_RuntimeWarning, + "tp_compare didn't return -1 or -2 " + "for exception") < 0) { + Py_XDECREF(t); + Py_XDECREF(v); + Py_XDECREF(tb); + } + else + PyErr_Restore(t, v, tb); + } + return -2; + } + else if (c < -1 || c > 1) { + if (PyErr_Warn(PyExc_RuntimeWarning, + "tp_compare didn't return -1, 0 or 1") < 0) + return -2; + else + return c < -1 ? -1 : 1; + } + else { + assert(c >= -1 && c <= 1); + return c; + } +} + + +/* Macro to get the tp_richcompare field of a type if defined */ +#define RICHCOMPARE(t) (PyType_HasFeature((t), Py_TPFLAGS_HAVE_RICHCOMPARE) \ + ? (t)->tp_richcompare : NULL) + +/* Map rich comparison operators to their swapped version, e.g. LT --> GT */ +static int swapped_op[] = {Py_GT, Py_GE, Py_EQ, Py_NE, Py_LT, Py_LE}; + +/* Try a genuine rich comparison, returning an object. Return: + NULL for exception; + NotImplemented if this particular rich comparison is not implemented or + undefined; + some object not equal to NotImplemented if it is implemented + (this latter object may not be a Boolean). +*/ +static PyObject * +try_rich_compare(PyObject *v, PyObject *w, int op) +{ + richcmpfunc f; + PyObject *res; + + if (v->ob_type != w->ob_type && + PyType_IsSubtype(w->ob_type, v->ob_type) && + (f = RICHCOMPARE(w->ob_type)) != NULL) { + res = (*f)(w, v, swapped_op[op]); + if (res != Py_NotImplemented) + return res; + Py_DECREF(res); + } + if ((f = RICHCOMPARE(v->ob_type)) != NULL) { + res = (*f)(v, w, op); + if (res != Py_NotImplemented) + return res; + Py_DECREF(res); + } + if ((f = RICHCOMPARE(w->ob_type)) != NULL) { + return (*f)(w, v, swapped_op[op]); + } + res = Py_NotImplemented; + Py_INCREF(res); + return res; +} + +/* Try a genuine rich comparison, returning an int. Return: + -1 for exception (including the case where try_rich_compare() returns an + object that's not a Boolean); + 0 if the outcome is false; + 1 if the outcome is true; + 2 if this particular rich comparison is not implemented or undefined. +*/ +static int +try_rich_compare_bool(PyObject *v, PyObject *w, int op) +{ + PyObject *res; + int ok; + + if (RICHCOMPARE(v->ob_type) == NULL && RICHCOMPARE(w->ob_type) == NULL) + return 2; /* Shortcut, avoid INCREF+DECREF */ + res = try_rich_compare(v, w, op); + if (res == NULL) + return -1; + if (res == Py_NotImplemented) { + Py_DECREF(res); + return 2; + } + ok = PyObject_IsTrue(res); + Py_DECREF(res); + return ok; +} + +/* Try rich comparisons to determine a 3-way comparison. Return: + -2 for an exception; + -1 if v < w; + 0 if v == w; + 1 if v > w; + 2 if this particular rich comparison is not implemented or undefined. +*/ +static int +try_rich_to_3way_compare(PyObject *v, PyObject *w) +{ + static struct { int op; int outcome; } tries[3] = { + /* Try this operator, and if it is true, use this outcome: */ + {Py_EQ, 0}, + {Py_LT, -1}, + {Py_GT, 1}, + }; + int i; + + if (RICHCOMPARE(v->ob_type) == NULL && RICHCOMPARE(w->ob_type) == NULL) + return 2; /* Shortcut */ + + for (i = 0; i < 3; i++) { + switch (try_rich_compare_bool(v, w, tries[i].op)) { + case -1: + return -2; + case 1: + return tries[i].outcome; + } + } + + return 2; +} + +/* Try a 3-way comparison, returning an int. Return: + -2 for an exception; + -1 if v < w; + 0 if v == w; + 1 if v > w; + 2 if this particular 3-way comparison is not implemented or undefined. +*/ +static int +try_3way_compare(PyObject *v, PyObject *w) +{ + int c; + cmpfunc f; + + /* Comparisons involving instances are given to instance_compare, + which has the same return conventions as this function. */ + + f = v->ob_type->tp_compare; + if (PyInstance_Check(v)) + return (*f)(v, w); + if (PyInstance_Check(w)) + return (*w->ob_type->tp_compare)(v, w); + + /* If both have the same (non-NULL) tp_compare, use it. */ + if (f != NULL && f == w->ob_type->tp_compare) { + c = (*f)(v, w); + return adjust_tp_compare(c); + } + + /* If either tp_compare is _PyObject_SlotCompare, that's safe. */ + if (f == _PyObject_SlotCompare || + w->ob_type->tp_compare == _PyObject_SlotCompare) + return _PyObject_SlotCompare(v, w); + + /* Try coercion; if it fails, give up */ + c = PyNumber_CoerceEx(&v, &w); + if (c < 0) + return -2; + if (c > 0) + return 2; + + /* Try v's comparison, if defined */ + if ((f = v->ob_type->tp_compare) != NULL) { + c = (*f)(v, w); + Py_DECREF(v); + Py_DECREF(w); + return adjust_tp_compare(c); + } + + /* Try w's comparison, if defined */ + if ((f = w->ob_type->tp_compare) != NULL) { + c = (*f)(w, v); /* swapped! */ + Py_DECREF(v); + Py_DECREF(w); + c = adjust_tp_compare(c); + if (c >= -1) + return -c; /* Swapped! */ + else + return c; + } + + /* No comparison defined */ + Py_DECREF(v); + Py_DECREF(w); + return 2; +} + +/* Final fallback 3-way comparison, returning an int. Return: + -2 if an error occurred; + -1 if v < w; + 0 if v == w; + 1 if v > w. +*/ +static int +default_3way_compare(PyObject *v, PyObject *w) +{ + int c; + char *vname, *wname; + + if (v->ob_type == w->ob_type) { + /* When comparing these pointers, they must be cast to + * integer types (i.e. Py_uintptr_t, our spelling of C9X's + * uintptr_t). ANSI specifies that pointer compares other + * than == and != to non-related structures are undefined. + */ + Py_uintptr_t vv = (Py_uintptr_t)v; + Py_uintptr_t ww = (Py_uintptr_t)w; + return (vv < ww) ? -1 : (vv > ww) ? 1 : 0; + } + +#ifdef Py_USING_UNICODE + /* Special case for Unicode */ + if (PyUnicode_Check(v) || PyUnicode_Check(w)) { + c = PyUnicode_Compare(v, w); + if (!PyErr_Occurred()) + return c; + /* TypeErrors are ignored: if Unicode coercion fails due + to one of the arguments not having the right type, we + continue as defined by the coercion protocol (see + above). Luckily, decoding errors are reported as + ValueErrors and are not masked by this technique. */ + if (!PyErr_ExceptionMatches(PyExc_TypeError)) + return -2; + PyErr_Clear(); + } +#endif + + /* None is smaller than anything */ + if (v == Py_None) + return -1; + if (w == Py_None) + return 1; + + /* different type: compare type names; numbers are smaller */ + if (PyNumber_Check(v)) + vname = ""; + else + vname = v->ob_type->tp_name; + if (PyNumber_Check(w)) + wname = ""; + else + wname = w->ob_type->tp_name; + c = strcmp(vname, wname); + if (c < 0) + return -1; + if (c > 0) + return 1; + /* Same type name, or (more likely) incomparable numeric types */ + return ((Py_uintptr_t)(v->ob_type) < ( + Py_uintptr_t)(w->ob_type)) ? -1 : 1; +} + +#define CHECK_TYPES(o) PyType_HasFeature((o)->ob_type, Py_TPFLAGS_CHECKTYPES) + +/* Do a 3-way comparison, by hook or by crook. Return: + -2 for an exception (but see below); + -1 if v < w; + 0 if v == w; + 1 if v > w; + BUT: if the object implements a tp_compare function, it returns + whatever this function returns (whether with an exception or not). +*/ +static int +do_cmp(PyObject *v, PyObject *w) +{ + int c; + cmpfunc f; + + if (v->ob_type == w->ob_type + && (f = v->ob_type->tp_compare) != NULL) { + c = (*f)(v, w); + if (PyInstance_Check(v)) { + /* Instance tp_compare has a different signature. + But if it returns undefined we fall through. */ + if (c != 2) + return c; + /* Else fall through to try_rich_to_3way_compare() */ + } + else + return adjust_tp_compare(c); + } + /* We only get here if one of the following is true: + a) v and w have different types + b) v and w have the same type, which doesn't have tp_compare + c) v and w are instances, and either __cmp__ is not defined or + __cmp__ returns NotImplemented + */ + c = try_rich_to_3way_compare(v, w); + if (c < 2) + return c; + c = try_3way_compare(v, w); + if (c < 2) + return c; + return default_3way_compare(v, w); +} + +/* compare_nesting is incremented before calling compare (for + some types) and decremented on exit. If the count exceeds the + nesting limit, enable code to detect circular data structures. + + This is a tunable parameter that should only affect the performance + of comparisons, nothing else. Setting it high makes comparing deeply + nested non-cyclical data structures faster, but makes comparing cyclical + data structures slower. +*/ +#define NESTING_LIMIT 20 + +static int compare_nesting = 0; + +static PyObject* +get_inprogress_dict(void) +{ + static PyObject *key; + PyObject *tstate_dict, *inprogress; + + if (key == NULL) { + key = PyString_InternFromString("cmp_state"); + if (key == NULL) + return NULL; + } + + tstate_dict = PyThreadState_GetDict(); + if (tstate_dict == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + + inprogress = PyDict_GetItem(tstate_dict, key); + if (inprogress == NULL) { + inprogress = PyDict_New(); + if (inprogress == NULL) + return NULL; + if (PyDict_SetItem(tstate_dict, key, inprogress) == -1) { + Py_DECREF(inprogress); + return NULL; + } + Py_DECREF(inprogress); + } + + return inprogress; +} + +/* If the comparison "v op w" is already in progress in this thread, returns + * a borrowed reference to Py_None (the caller must not decref). + * If it's not already in progress, returns "a token" which must eventually + * be passed to delete_token(). The caller must not decref this either + * (delete_token decrefs it). The token must not survive beyond any point + * where v or w may die. + * If an error occurs (out-of-memory), returns NULL. + */ +static PyObject * +check_recursion(PyObject *v, PyObject *w, int op) +{ + PyObject *inprogress; + PyObject *token; + Py_uintptr_t iv = (Py_uintptr_t)v; + Py_uintptr_t iw = (Py_uintptr_t)w; + PyObject *x, *y, *z; + + inprogress = get_inprogress_dict(); + if (inprogress == NULL) + return NULL; + + token = PyTuple_New(3); + if (token == NULL) + return NULL; + + if (iv <= iw) { + PyTuple_SET_ITEM(token, 0, x = PyLong_FromVoidPtr((void *)v)); + PyTuple_SET_ITEM(token, 1, y = PyLong_FromVoidPtr((void *)w)); + if (op >= 0) + op = swapped_op[op]; + } else { + PyTuple_SET_ITEM(token, 0, x = PyLong_FromVoidPtr((void *)w)); + PyTuple_SET_ITEM(token, 1, y = PyLong_FromVoidPtr((void *)v)); + } + PyTuple_SET_ITEM(token, 2, z = PyInt_FromLong((long)op)); + if (x == NULL || y == NULL || z == NULL) { + Py_DECREF(token); + return NULL; + } + + if (PyDict_GetItem(inprogress, token) != NULL) { + Py_DECREF(token); + return Py_None; /* Without INCREF! */ + } + + if (PyDict_SetItem(inprogress, token, token) < 0) { + Py_DECREF(token); + return NULL; + } + + return token; +} + +static void +delete_token(PyObject *token) +{ + PyObject *inprogress; + + if (token == NULL || token == Py_None) + return; + inprogress = get_inprogress_dict(); + if (inprogress == NULL) + PyErr_Clear(); + else + PyDict_DelItem(inprogress, token); + Py_DECREF(token); +} + +/* Compare v to w. Return + -1 if v < w or exception (PyErr_Occurred() true in latter case). + 0 if v == w. + 1 if v > w. + XXX The docs (C API manual) say the return value is undefined in case + XXX of error. +*/ +int +PyObject_Compare(PyObject *v, PyObject *w) +{ + PyTypeObject *vtp; + int result; + +#if defined(USE_STACKCHECK) + if (PyOS_CheckStack()) { + PyErr_SetString(PyExc_MemoryError, "Stack overflow"); + return -1; + } +#endif + if (v == NULL || w == NULL) { + PyErr_BadInternalCall(); + return -1; + } + if (v == w) + return 0; + vtp = v->ob_type; + compare_nesting++; + if (compare_nesting > NESTING_LIMIT && + (vtp->tp_as_mapping || vtp->tp_as_sequence) && + !PyString_CheckExact(v) && + !PyTuple_CheckExact(v)) { + /* try to detect circular data structures */ + PyObject *token = check_recursion(v, w, -1); + + if (token == NULL) { + result = -1; + } + else if (token == Py_None) { + /* already comparing these objects. assume + they're equal until shown otherwise */ + result = 0; + } + else { + result = do_cmp(v, w); + delete_token(token); + } + } + else { + result = do_cmp(v, w); + } + compare_nesting--; + return result < 0 ? -1 : result; +} + +/* Return (new reference to) Py_True or Py_False. */ +static PyObject * +convert_3way_to_object(int op, int c) +{ + PyObject *result; + switch (op) { + case Py_LT: c = c < 0; break; + case Py_LE: c = c <= 0; break; + case Py_EQ: c = c == 0; break; + case Py_NE: c = c != 0; break; + case Py_GT: c = c > 0; break; + case Py_GE: c = c >= 0; break; + } + result = c ? Py_True : Py_False; + Py_INCREF(result); + return result; +} + +/* We want a rich comparison but don't have one. Try a 3-way cmp instead. + Return + NULL if error + Py_True if v op w + Py_False if not (v op w) +*/ +static PyObject * +try_3way_to_rich_compare(PyObject *v, PyObject *w, int op) +{ + int c; + + c = try_3way_compare(v, w); + if (c >= 2) + c = default_3way_compare(v, w); + if (c <= -2) + return NULL; + return convert_3way_to_object(op, c); +} + +/* Do rich comparison on v and w. Return + NULL if error + Else a new reference to an object other than Py_NotImplemented, usually(?): + Py_True if v op w + Py_False if not (v op w) +*/ +static PyObject * +do_richcmp(PyObject *v, PyObject *w, int op) +{ + PyObject *res; + + res = try_rich_compare(v, w, op); + if (res != Py_NotImplemented) + return res; + Py_DECREF(res); + + return try_3way_to_rich_compare(v, w, op); +} + +/* Return: + NULL for exception; + some object not equal to NotImplemented if it is implemented + (this latter object may not be a Boolean). +*/ +PyObject * +PyObject_RichCompare(PyObject *v, PyObject *w, int op) +{ + PyObject *res; + + assert(Py_LT <= op && op <= Py_GE); + + compare_nesting++; + if (compare_nesting > NESTING_LIMIT && + (v->ob_type->tp_as_mapping || v->ob_type->tp_as_sequence) && + !PyString_CheckExact(v) && + !PyTuple_CheckExact(v)) { + /* try to detect circular data structures */ + PyObject *token = check_recursion(v, w, op); + if (token == NULL) { + res = NULL; + goto Done; + } + else if (token == Py_None) { + /* already comparing these objects with this operator. + assume they're equal until shown otherwise */ + if (op == Py_EQ) + res = Py_True; + else if (op == Py_NE) + res = Py_False; + else { + PyErr_SetString(PyExc_ValueError, + "can't order recursive values"); + res = NULL; + } + Py_XINCREF(res); + } + else { + res = do_richcmp(v, w, op); + delete_token(token); + } + goto Done; + } + + /* No nesting extremism. + If the types are equal, and not old-style instances, try to + get out cheap (don't bother with coercions etc.). */ + if (v->ob_type == w->ob_type && !PyInstance_Check(v)) { + cmpfunc fcmp; + richcmpfunc frich = RICHCOMPARE(v->ob_type); + /* If the type has richcmp, try it first. try_rich_compare + tries it two-sided, which is not needed since we've a + single type only. */ + if (frich != NULL) { + res = (*frich)(v, w, op); + if (res != Py_NotImplemented) + goto Done; + Py_DECREF(res); + } + /* No richcmp, or this particular richmp not implemented. + Try 3-way cmp. */ + fcmp = v->ob_type->tp_compare; + if (fcmp != NULL) { + int c = (*fcmp)(v, w); + c = adjust_tp_compare(c); + if (c == -2) { + res = NULL; + goto Done; + } + res = convert_3way_to_object(op, c); + goto Done; + } + } + + /* Fast path not taken, or couldn't deliver a useful result. */ + res = do_richcmp(v, w, op); +Done: + compare_nesting--; + return res; +} + +/* Return -1 if error; 1 if v op w; 0 if not (v op w). */ +int +PyObject_RichCompareBool(PyObject *v, PyObject *w, int op) +{ + PyObject *res = PyObject_RichCompare(v, w, op); + int ok; + + if (res == NULL) + return -1; + if (PyBool_Check(res)) + ok = (res == Py_True); + else + ok = PyObject_IsTrue(res); + Py_DECREF(res); + return ok; +} + +/* Set of hash utility functions to help maintaining the invariant that + iff a==b then hash(a)==hash(b) + + All the utility functions (_Py_Hash*()) return "-1" to signify an error. +*/ + +long +_Py_HashDouble(double v) +{ + double intpart, fractpart; + int expo; + long hipart; + long x; /* the final hash value */ + /* This is designed so that Python numbers of different types + * that compare equal hash to the same value; otherwise comparisons + * of mapping keys will turn out weird. + */ + +#ifdef MPW /* MPW C modf expects pointer to extended as second argument */ +{ + extended e; + fractpart = modf(v, &e); + intpart = e; +} +#else + fractpart = modf(v, &intpart); +#endif + if (fractpart == 0.0) { + /* This must return the same hash as an equal int or long. */ + if (intpart > LONG_MAX || -intpart > LONG_MAX) { + /* Convert to long and use its hash. */ + PyObject *plong; /* converted to Python long */ + if (Py_IS_INFINITY(intpart)) + /* can't convert to long int -- arbitrary */ + v = v < 0 ? -271828.0 : 314159.0; + plong = PyLong_FromDouble(v); + if (plong == NULL) + return -1; + x = PyObject_Hash(plong); + Py_DECREF(plong); + return x; + } + /* Fits in a C long == a Python int, so is its own hash. */ + x = (long)intpart; + if (x == -1) + x = -2; + return x; + } + /* The fractional part is non-zero, so we don't have to worry about + * making this match the hash of some other type. + * Use frexp to get at the bits in the double. + * Since the VAX D double format has 56 mantissa bits, which is the + * most of any double format in use, each of these parts may have as + * many as (but no more than) 56 significant bits. + * So, assuming sizeof(long) >= 4, each part can be broken into two + * longs; frexp and multiplication are used to do that. + * Also, since the Cray double format has 15 exponent bits, which is + * the most of any double format in use, shifting the exponent field + * left by 15 won't overflow a long (again assuming sizeof(long) >= 4). + */ + v = frexp(v, &expo); + v *= 2147483648.0; /* 2**31 */ + hipart = (long)v; /* take the top 32 bits */ + v = (v - (double)hipart) * 2147483648.0; /* get the next 32 bits */ + x = hipart + (long)v + (expo << 15); + if (x == -1) + x = -2; + return x; +} + +long +_Py_HashPointer(void *p) +{ +#if SIZEOF_LONG >= SIZEOF_VOID_P + return (long)p; +#else + /* convert to a Python long and hash that */ + PyObject* longobj; + long x; + + if ((longobj = PyLong_FromVoidPtr(p)) == NULL) { + x = -1; + goto finally; + } + x = PyObject_Hash(longobj); + +finally: + Py_XDECREF(longobj); + return x; +#endif +} + + +long +PyObject_Hash(PyObject *v) +{ + PyTypeObject *tp = v->ob_type; + if (tp->tp_hash != NULL) + return (*tp->tp_hash)(v); + if (tp->tp_compare == NULL && RICHCOMPARE(tp) == NULL) { + return _Py_HashPointer(v); /* Use address as hash value */ + } + /* If there's a cmp but no hash defined, the object can't be hashed */ + PyErr_SetString(PyExc_TypeError, "unhashable type"); + return -1; +} + +PyObject * +PyObject_GetAttrString(PyObject *v, char *name) +{ + PyObject *w, *res; + + if (v->ob_type->tp_getattr != NULL) + return (*v->ob_type->tp_getattr)(v, name); + w = PyString_InternFromString(name); + if (w == NULL) + return NULL; + res = PyObject_GetAttr(v, w); + Py_XDECREF(w); + return res; +} + +int +PyObject_HasAttrString(PyObject *v, char *name) +{ + PyObject *res = PyObject_GetAttrString(v, name); + if (res != NULL) { + Py_DECREF(res); + return 1; + } + PyErr_Clear(); + return 0; +} + +int +PyObject_SetAttrString(PyObject *v, char *name, PyObject *w) +{ + PyObject *s; + int res; + + if (v->ob_type->tp_setattr != NULL) + return (*v->ob_type->tp_setattr)(v, name, w); + s = PyString_InternFromString(name); + if (s == NULL) + return -1; + res = PyObject_SetAttr(v, s, w); + Py_XDECREF(s); + return res; +} + +PyObject * +PyObject_GetAttr(PyObject *v, PyObject *name) +{ + PyTypeObject *tp = v->ob_type; + + if (!PyString_Check(name)) { +#ifdef Py_USING_UNICODE + /* The Unicode to string conversion is done here because the + existing tp_getattro slots expect a string object as name + and we wouldn't want to break those. */ + if (PyUnicode_Check(name)) { + name = _PyUnicode_AsDefaultEncodedString(name, NULL); + if (name == NULL) + return NULL; + } + else +#endif + { + PyErr_SetString(PyExc_TypeError, + "attribute name must be string"); + return NULL; + } + } + if (tp->tp_getattro != NULL) + return (*tp->tp_getattro)(v, name); + if (tp->tp_getattr != NULL) + return (*tp->tp_getattr)(v, PyString_AS_STRING(name)); + PyErr_Format(PyExc_AttributeError, + "'%.50s' object has no attribute '%.400s'", + tp->tp_name, PyString_AS_STRING(name)); + return NULL; +} + +int +PyObject_HasAttr(PyObject *v, PyObject *name) +{ + PyObject *res = PyObject_GetAttr(v, name); + if (res != NULL) { + Py_DECREF(res); + return 1; + } + PyErr_Clear(); + return 0; +} + +int +PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value) +{ + PyTypeObject *tp = v->ob_type; + int err; + + if (!PyString_Check(name)){ +#ifdef Py_USING_UNICODE + /* The Unicode to string conversion is done here because the + existing tp_setattro slots expect a string object as name + and we wouldn't want to break those. */ + if (PyUnicode_Check(name)) { + name = PyUnicode_AsEncodedString(name, NULL, NULL); + if (name == NULL) + return -1; + } + else +#endif + { + PyErr_SetString(PyExc_TypeError, + "attribute name must be string"); + return -1; + } + } + else + Py_INCREF(name); + + PyString_InternInPlace(&name); + if (tp->tp_setattro != NULL) { + err = (*tp->tp_setattro)(v, name, value); + Py_DECREF(name); + return err; + } + if (tp->tp_setattr != NULL) { + err = (*tp->tp_setattr)(v, PyString_AS_STRING(name), value); + Py_DECREF(name); + return err; + } + Py_DECREF(name); + if (tp->tp_getattr == NULL && tp->tp_getattro == NULL) + PyErr_Format(PyExc_TypeError, + "'%.100s' object has no attributes " + "(%s .%.100s)", + tp->tp_name, + value==NULL ? "del" : "assign to", + PyString_AS_STRING(name)); + else + PyErr_Format(PyExc_TypeError, + "'%.100s' object has only read-only attributes " + "(%s .%.100s)", + tp->tp_name, + value==NULL ? "del" : "assign to", + PyString_AS_STRING(name)); + return -1; +} + +/* Helper to get a pointer to an object's __dict__ slot, if any */ + +PyObject ** +_PyObject_GetDictPtr(PyObject *obj) +{ + long dictoffset; + PyTypeObject *tp = obj->ob_type; + + if (!(tp->tp_flags & Py_TPFLAGS_HAVE_CLASS)) + return NULL; + dictoffset = tp->tp_dictoffset; + if (dictoffset == 0) + return NULL; + if (dictoffset < 0) { + int tsize; + size_t size; + + tsize = ((PyVarObject *)obj)->ob_size; + if (tsize < 0) + tsize = -tsize; + size = _PyObject_VAR_SIZE(tp, tsize); + + dictoffset += (long)size; + assert(dictoffset > 0); + assert(dictoffset % SIZEOF_VOID_P == 0); + } + return (PyObject **) ((char *)obj + dictoffset); +} + +/* Generic GetAttr functions - put these in your tp_[gs]etattro slot */ + +PyObject * +PyObject_SelfIter(PyObject *obj) +{ + Py_INCREF(obj); + return obj; +} + +PyObject * +PyObject_GenericGetAttr(PyObject *obj, PyObject *name) +{ + PyTypeObject *tp = obj->ob_type; + PyObject *descr = NULL; + PyObject *res = NULL; + descrgetfunc f; + long dictoffset; + PyObject **dictptr; + + if (!PyString_Check(name)){ +#ifdef Py_USING_UNICODE + /* The Unicode to string conversion is done here because the + existing tp_setattro slots expect a string object as name + and we wouldn't want to break those. */ + if (PyUnicode_Check(name)) { + name = PyUnicode_AsEncodedString(name, NULL, NULL); + if (name == NULL) + return NULL; + } + else +#endif + { + PyErr_SetString(PyExc_TypeError, + "attribute name must be string"); + return NULL; + } + } + else + Py_INCREF(name); + + if (tp->tp_dict == NULL) { + if (PyType_Ready(tp) < 0) + goto done; + } + + /* Inline _PyType_Lookup */ + { + int i, n; + PyObject *mro, *base, *dict; + + /* Look in tp_dict of types in MRO */ + mro = tp->tp_mro; + assert(mro != NULL); + assert(PyTuple_Check(mro)); + n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + base = PyTuple_GET_ITEM(mro, i); + if (PyClass_Check(base)) + dict = ((PyClassObject *)base)->cl_dict; + else { + assert(PyType_Check(base)); + dict = ((PyTypeObject *)base)->tp_dict; + } + assert(dict && PyDict_Check(dict)); + descr = PyDict_GetItem(dict, name); + if (descr != NULL) + break; + } + } + + f = NULL; + if (descr != NULL && + PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) { + f = descr->ob_type->tp_descr_get; + if (f != NULL && PyDescr_IsData(descr)) { + res = f(descr, obj, (PyObject *)obj->ob_type); + goto done; + } + } + + /* Inline _PyObject_GetDictPtr */ + dictoffset = tp->tp_dictoffset; + if (dictoffset != 0) { + PyObject *dict; + if (dictoffset < 0) { + int tsize; + size_t size; + + tsize = ((PyVarObject *)obj)->ob_size; + if (tsize < 0) + tsize = -tsize; + size = _PyObject_VAR_SIZE(tp, tsize); + + dictoffset += (long)size; + assert(dictoffset > 0); + assert(dictoffset % SIZEOF_VOID_P == 0); + } + dictptr = (PyObject **) ((char *)obj + dictoffset); + dict = *dictptr; + if (dict != NULL) { + res = PyDict_GetItem(dict, name); + if (res != NULL) { + Py_INCREF(res); + goto done; + } + } + } + + if (f != NULL) { + res = f(descr, obj, (PyObject *)obj->ob_type); + goto done; + } + + if (descr != NULL) { + Py_INCREF(descr); + res = descr; + goto done; + } + + PyErr_Format(PyExc_AttributeError, + "'%.50s' object has no attribute '%.400s'", + tp->tp_name, PyString_AS_STRING(name)); + done: + Py_DECREF(name); + return res; +} + +int +PyObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value) +{ + PyTypeObject *tp = obj->ob_type; + PyObject *descr; + descrsetfunc f; + PyObject **dictptr; + int res = -1; + + if (!PyString_Check(name)){ +#ifdef Py_USING_UNICODE + /* The Unicode to string conversion is done here because the + existing tp_setattro slots expect a string object as name + and we wouldn't want to break those. */ + if (PyUnicode_Check(name)) { + name = PyUnicode_AsEncodedString(name, NULL, NULL); + if (name == NULL) + return -1; + } + else +#endif + { + PyErr_SetString(PyExc_TypeError, + "attribute name must be string"); + return -1; + } + } + else + Py_INCREF(name); + + if (tp->tp_dict == NULL) { + if (PyType_Ready(tp) < 0) + goto done; + } + + descr = _PyType_Lookup(tp, name); + f = NULL; + if (descr != NULL && + PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) { + f = descr->ob_type->tp_descr_set; + if (f != NULL && PyDescr_IsData(descr)) { + res = f(descr, obj, value); + goto done; + } + } + + dictptr = _PyObject_GetDictPtr(obj); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + if (dict == NULL && value != NULL) { + dict = PyDict_New(); + if (dict == NULL) + goto done; + *dictptr = dict; + } + if (dict != NULL) { + if (value == NULL) + res = PyDict_DelItem(dict, name); + else + res = PyDict_SetItem(dict, name, value); + if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) + PyErr_SetObject(PyExc_AttributeError, name); + goto done; + } + } + + if (f != NULL) { + res = f(descr, obj, value); + goto done; + } + + if (descr == NULL) { + PyErr_Format(PyExc_AttributeError, + "'%.50s' object has no attribute '%.400s'", + tp->tp_name, PyString_AS_STRING(name)); + goto done; + } + + PyErr_Format(PyExc_AttributeError, + "'%.50s' object attribute '%.400s' is read-only", + tp->tp_name, PyString_AS_STRING(name)); + done: + Py_DECREF(name); + return res; +} + +/* Test a value used as condition, e.g., in a for or if statement. + Return -1 if an error occurred */ + +int +PyObject_IsTrue(PyObject *v) +{ + int res; + if (v == Py_True) + return 1; + if (v == Py_False) + return 0; + if (v == Py_None) + return 0; + else if (v->ob_type->tp_as_number != NULL && + v->ob_type->tp_as_number->nb_nonzero != NULL) + res = (*v->ob_type->tp_as_number->nb_nonzero)(v); + else if (v->ob_type->tp_as_mapping != NULL && + v->ob_type->tp_as_mapping->mp_length != NULL) + res = (*v->ob_type->tp_as_mapping->mp_length)(v); + else if (v->ob_type->tp_as_sequence != NULL && + v->ob_type->tp_as_sequence->sq_length != NULL) + res = (*v->ob_type->tp_as_sequence->sq_length)(v); + else + return 1; + return (res > 0) ? 1 : res; +} + +/* equivalent of 'not v' + Return -1 if an error occurred */ + +int +PyObject_Not(PyObject *v) +{ + int res; + res = PyObject_IsTrue(v); + if (res < 0) + return res; + return res == 0; +} + +/* Coerce two numeric types to the "larger" one. + Increment the reference count on each argument. + Return value: + -1 if an error occurred; + 0 if the coercion succeeded (and then the reference counts are increased); + 1 if no coercion is possible (and no error is raised). +*/ +int +PyNumber_CoerceEx(PyObject **pv, PyObject **pw) +{ + register PyObject *v = *pv; + register PyObject *w = *pw; + int res; + + /* Shortcut only for old-style types */ + if (v->ob_type == w->ob_type && + !PyType_HasFeature(v->ob_type, Py_TPFLAGS_CHECKTYPES)) + { + Py_INCREF(v); + Py_INCREF(w); + return 0; + } + if (v->ob_type->tp_as_number && v->ob_type->tp_as_number->nb_coerce) { + res = (*v->ob_type->tp_as_number->nb_coerce)(pv, pw); + if (res <= 0) + return res; + } + if (w->ob_type->tp_as_number && w->ob_type->tp_as_number->nb_coerce) { + res = (*w->ob_type->tp_as_number->nb_coerce)(pw, pv); + if (res <= 0) + return res; + } + return 1; +} + +/* Coerce two numeric types to the "larger" one. + Increment the reference count on each argument. + Return -1 and raise an exception if no coercion is possible + (and then no reference count is incremented). +*/ +int +PyNumber_Coerce(PyObject **pv, PyObject **pw) +{ + int err = PyNumber_CoerceEx(pv, pw); + if (err <= 0) + return err; + PyErr_SetString(PyExc_TypeError, "number coercion failed"); + return -1; +} + + +/* Test whether an object can be called */ + +int +PyCallable_Check(PyObject *x) +{ + if (x == NULL) + return 0; + if (PyInstance_Check(x)) { + PyObject *call = PyObject_GetAttrString(x, "__call__"); + if (call == NULL) { + PyErr_Clear(); + return 0; + } + /* Could test recursively but don't, for fear of endless + recursion if some joker sets self.__call__ = self */ + Py_DECREF(call); + return 1; + } + else { + return x->ob_type->tp_call != NULL; + } +} + +/* Helper for PyObject_Dir. + Merge the __dict__ of aclass into dict, and recursively also all + the __dict__s of aclass's base classes. The order of merging isn't + defined, as it's expected that only the final set of dict keys is + interesting. + Return 0 on success, -1 on error. +*/ + +static int +merge_class_dict(PyObject* dict, PyObject* aclass) +{ + PyObject *classdict; + PyObject *bases; + + assert(PyDict_Check(dict)); + assert(aclass); + + /* Merge in the type's dict (if any). */ + classdict = PyObject_GetAttrString(aclass, "__dict__"); + if (classdict == NULL) + PyErr_Clear(); + else { + int status = PyDict_Update(dict, classdict); + Py_DECREF(classdict); + if (status < 0) + return -1; + } + + /* Recursively merge in the base types' (if any) dicts. */ + bases = PyObject_GetAttrString(aclass, "__bases__"); + if (bases == NULL) + PyErr_Clear(); + else { + /* We have no guarantee that bases is a real tuple */ + int i, n; + n = PySequence_Size(bases); /* This better be right */ + if (n < 0) + PyErr_Clear(); + else { + for (i = 0; i < n; i++) { + int status; + PyObject *base = PySequence_GetItem(bases, i); + if (base == NULL) { + Py_DECREF(bases); + return -1; + } + status = merge_class_dict(dict, base); + Py_DECREF(base); + if (status < 0) { + Py_DECREF(bases); + return -1; + } + } + } + Py_DECREF(bases); + } + return 0; +} + +/* Helper for PyObject_Dir. + If obj has an attr named attrname that's a list, merge its string + elements into keys of dict. + Return 0 on success, -1 on error. Errors due to not finding the attr, + or the attr not being a list, are suppressed. +*/ + +static int +merge_list_attr(PyObject* dict, PyObject* obj, char *attrname) +{ + PyObject *list; + int result = 0; + + assert(PyDict_Check(dict)); + assert(obj); + assert(attrname); + + list = PyObject_GetAttrString(obj, attrname); + if (list == NULL) + PyErr_Clear(); + + else if (PyList_Check(list)) { + int i; + for (i = 0; i < PyList_GET_SIZE(list); ++i) { + PyObject *item = PyList_GET_ITEM(list, i); + if (PyString_Check(item)) { + result = PyDict_SetItem(dict, item, Py_None); + if (result < 0) + break; + } + } + } + + Py_XDECREF(list); + return result; +} + +/* Like __builtin__.dir(arg). See bltinmodule.c's builtin_dir for the + docstring, which should be kept in synch with this implementation. */ + +PyObject * +PyObject_Dir(PyObject *arg) +{ + /* Set exactly one of these non-NULL before the end. */ + PyObject *result = NULL; /* result list */ + PyObject *masterdict = NULL; /* result is masterdict.keys() */ + + /* If NULL arg, return the locals. */ + if (arg == NULL) { + PyObject *locals = PyEval_GetLocals(); + if (locals == NULL) + goto error; + result = PyDict_Keys(locals); + if (result == NULL) + goto error; + } + + /* Elif this is some form of module, we only want its dict. */ + else if (PyModule_Check(arg)) { + masterdict = PyObject_GetAttrString(arg, "__dict__"); + if (masterdict == NULL) + goto error; + if (!PyDict_Check(masterdict)) { + PyErr_SetString(PyExc_TypeError, + "module.__dict__ is not a dictionary"); + goto error; + } + } + + /* Elif some form of type or class, grab its dict and its bases. + We deliberately don't suck up its __class__, as methods belonging + to the metaclass would probably be more confusing than helpful. */ + else if (PyType_Check(arg) || PyClass_Check(arg)) { + masterdict = PyDict_New(); + if (masterdict == NULL) + goto error; + if (merge_class_dict(masterdict, arg) < 0) + goto error; + } + + /* Else look at its dict, and the attrs reachable from its class. */ + else { + PyObject *itsclass; + /* Create a dict to start with. CAUTION: Not everything + responding to __dict__ returns a dict! */ + masterdict = PyObject_GetAttrString(arg, "__dict__"); + if (masterdict == NULL) { + PyErr_Clear(); + masterdict = PyDict_New(); + } + else if (!PyDict_Check(masterdict)) { + Py_DECREF(masterdict); + masterdict = PyDict_New(); + } + else { + /* The object may have returned a reference to its + dict, so copy it to avoid mutating it. */ + PyObject *temp = PyDict_Copy(masterdict); + Py_DECREF(masterdict); + masterdict = temp; + } + if (masterdict == NULL) + goto error; + + /* Merge in __members__ and __methods__ (if any). + XXX Would like this to go away someday; for now, it's + XXX needed to get at im_self etc of method objects. */ + if (merge_list_attr(masterdict, arg, "__members__") < 0) + goto error; + if (merge_list_attr(masterdict, arg, "__methods__") < 0) + goto error; + + /* Merge in attrs reachable from its class. + CAUTION: Not all objects have a __class__ attr. */ + itsclass = PyObject_GetAttrString(arg, "__class__"); + if (itsclass == NULL) + PyErr_Clear(); + else { + int status = merge_class_dict(masterdict, itsclass); + Py_DECREF(itsclass); + if (status < 0) + goto error; + } + } + + assert((result == NULL) ^ (masterdict == NULL)); + if (masterdict != NULL) { + /* The result comes from its keys. */ + assert(result == NULL); + result = PyDict_Keys(masterdict); + if (result == NULL) + goto error; + } + + assert(result); + if (PyList_Sort(result) != 0) + goto error; + else + goto normal_return; + + error: + Py_XDECREF(result); + result = NULL; + /* fall through */ + normal_return: + Py_XDECREF(masterdict); + return result; +} + +/* +NoObject is usable as a non-NULL undefined value, used by the macro None. +There is (and should be!) no way to create other objects of this type, +so there is exactly one (which is indestructible, by the way). +(XXX This type and the type of NotImplemented below should be unified.) +*/ + +/* ARGSUSED */ +static PyObject * +none_repr(PyObject *op) +{ + return PyString_FromString("None"); +} + +/* ARGUSED */ +static void +none_dealloc(PyObject* ignore) +{ + /* This should never get called, but we also don't want to SEGV if + * we accidently decref None out of existance. + */ + Py_FatalError("deallocating None"); +} + + +static PyTypeObject PyNone_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "NoneType", + 0, + 0, + (destructor)none_dealloc, /*tp_dealloc*/ /*never called*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)none_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ +}; + +PyObject _Py_NoneStruct = { + PyObject_HEAD_INIT(&PyNone_Type) +}; + +/* NotImplemented is an object that can be used to signal that an + operation is not implemented for the given type combination. */ + +static PyObject * +NotImplemented_repr(PyObject *op) +{ + return PyString_FromString("NotImplemented"); +} + +static PyTypeObject PyNotImplemented_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "NotImplementedType", + 0, + 0, + (destructor)none_dealloc, /*tp_dealloc*/ /*never called*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)NotImplemented_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ +}; + +PyObject _Py_NotImplementedStruct = { + PyObject_HEAD_INIT(&PyNotImplemented_Type) +}; + +void +_Py_ReadyTypes(void) +{ + if (PyType_Ready(&PyType_Type) < 0) + Py_FatalError("Can't initialize 'type'"); + + if (PyType_Ready(&PyBool_Type) < 0) + Py_FatalError("Can't initialize 'bool'"); + + if (PyType_Ready(&PyString_Type) < 0) + Py_FatalError("Can't initialize 'str'"); + + if (PyType_Ready(&PyList_Type) < 0) + Py_FatalError("Can't initialize 'list'"); + + if (PyType_Ready(&PyNone_Type) < 0) + Py_FatalError("Can't initialize type(None)"); + + if (PyType_Ready(&PyNotImplemented_Type) < 0) + Py_FatalError("Can't initialize type(NotImplemented)"); +} + + +#ifdef Py_TRACE_REFS + +void +_Py_NewReference(PyObject *op) +{ + _Py_INC_REFTOTAL; + op->ob_refcnt = 1; + _Py_AddToAllObjects(op, 1); + _Py_INC_TPALLOCS(op); +} + +void +_Py_ForgetReference(register PyObject *op) +{ +#ifdef SLOW_UNREF_CHECK + register PyObject *p; +#endif + if (op->ob_refcnt < 0) + Py_FatalError("UNREF negative refcnt"); + if (op == &refchain || + op->_ob_prev->_ob_next != op || op->_ob_next->_ob_prev != op) + Py_FatalError("UNREF invalid object"); +#ifdef SLOW_UNREF_CHECK + for (p = refchain._ob_next; p != &refchain; p = p->_ob_next) { + if (p == op) + break; + } + if (p == &refchain) /* Not found */ + Py_FatalError("UNREF unknown object"); +#endif + op->_ob_next->_ob_prev = op->_ob_prev; + op->_ob_prev->_ob_next = op->_ob_next; + op->_ob_next = op->_ob_prev = NULL; + _Py_INC_TPFREES(op); +} + +void +_Py_Dealloc(PyObject *op) +{ + destructor dealloc = op->ob_type->tp_dealloc; + _Py_ForgetReference(op); + (*dealloc)(op); +} + +/* Print all live objects. Because PyObject_Print is called, the + * interpreter must be in a healthy state. + */ +void +_Py_PrintReferences(FILE *fp) +{ + PyObject *op; + fprintf(fp, "Remaining objects:\n"); + for (op = refchain._ob_next; op != &refchain; op = op->_ob_next) { + fprintf(fp, "%p [%d] ", op, op->ob_refcnt); + if (PyObject_Print(op, fp, 0) != 0) + PyErr_Clear(); + putc('\n', fp); + } +} + +/* Print the addresses of all live objects. Unlike _Py_PrintReferences, this + * doesn't make any calls to the Python C API, so is always safe to call. + */ +void +_Py_PrintReferenceAddresses(FILE *fp) +{ + PyObject *op; + fprintf(fp, "Remaining object addresses:\n"); + for (op = refchain._ob_next; op != &refchain; op = op->_ob_next) + fprintf(fp, "%p [%d] %s\n", op, op->ob_refcnt, + op->ob_type->tp_name); +} + +PyObject * +_Py_GetObjects(PyObject *self, PyObject *args) +{ + int i, n; + PyObject *t = NULL; + PyObject *res, *op; + + if (!PyArg_ParseTuple(args, "i|O", &n, &t)) + return NULL; + op = refchain._ob_next; + res = PyList_New(0); + if (res == NULL) + return NULL; + for (i = 0; (n == 0 || i < n) && op != &refchain; i++) { + while (op == self || op == args || op == res || op == t || + (t != NULL && op->ob_type != (PyTypeObject *) t)) { + op = op->_ob_next; + if (op == &refchain) + return res; + } + if (PyList_Append(res, op) < 0) { + Py_DECREF(res); + return NULL; + } + op = op->_ob_next; + } + return res; +} + +#endif + + +/* Hack to force loading of cobject.o */ +PyTypeObject *_Py_cobject_hack = &PyCObject_Type; + + +/* Hack to force loading of abstract.o */ +int (*_Py_abstract_hack)(PyObject *) = PyObject_Size; + + +/* Python's malloc wrappers (see pymem.h) */ + +void * +PyMem_Malloc(size_t nbytes) +{ + return PyMem_MALLOC(nbytes); +} + +void * +PyMem_Realloc(void *p, size_t nbytes) +{ + return PyMem_REALLOC(p, nbytes); +} + +void +PyMem_Free(void *p) +{ + PyMem_FREE(p); +} + + +/* These methods are used to control infinite recursion in repr, str, print, + etc. Container objects that may recursively contain themselves, + e.g. builtin dictionaries and lists, should used Py_ReprEnter() and + Py_ReprLeave() to avoid infinite recursion. + + Py_ReprEnter() returns 0 the first time it is called for a particular + object and 1 every time thereafter. It returns -1 if an exception + occurred. Py_ReprLeave() has no return value. + + See dictobject.c and listobject.c for examples of use. +*/ + +#define KEY "Py_Repr" + +int +Py_ReprEnter(PyObject *obj) +{ + PyObject *dict; + PyObject *list; + int i; + + dict = PyThreadState_GetDict(); + if (dict == NULL) + return 0; + list = PyDict_GetItemString(dict, KEY); + if (list == NULL) { + list = PyList_New(0); + if (list == NULL) + return -1; + if (PyDict_SetItemString(dict, KEY, list) < 0) + return -1; + Py_DECREF(list); + } + i = PyList_GET_SIZE(list); + while (--i >= 0) { + if (PyList_GET_ITEM(list, i) == obj) + return 1; + } + PyList_Append(list, obj); + return 0; +} + +void +Py_ReprLeave(PyObject *obj) +{ + PyObject *dict; + PyObject *list; + int i; + + dict = PyThreadState_GetDict(); + if (dict == NULL) + return; + list = PyDict_GetItemString(dict, KEY); + if (list == NULL || !PyList_Check(list)) + return; + i = PyList_GET_SIZE(list); + /* Count backwards because we always expect obj to be list[-1] */ + while (--i >= 0) { + if (PyList_GET_ITEM(list, i) == obj) { + PyList_SetSlice(list, i, i + 1, NULL); + break; + } + } +} + +/* Trashcan support. */ + +/* Current call-stack depth of tp_dealloc calls. */ +int _PyTrash_delete_nesting = 0; + +/* List of objects that still need to be cleaned up, singly linked via their + * gc headers' gc_prev pointers. + */ +PyObject *_PyTrash_delete_later = NULL; + +/* Add op to the _PyTrash_delete_later list. Called when the current + * call-stack depth gets large. op must be a currently untracked gc'ed + * object, with refcount 0. Py_DECREF must already have been called on it. + */ +void +_PyTrash_deposit_object(PyObject *op) +{ + assert(PyObject_IS_GC(op)); + assert(_Py_AS_GC(op)->gc.gc_refs == _PyGC_REFS_UNTRACKED); + assert(op->ob_refcnt == 0); + _Py_AS_GC(op)->gc.gc_prev = (PyGC_Head *)_PyTrash_delete_later; + _PyTrash_delete_later = op; +} + +/* Dealloccate all the objects in the _PyTrash_delete_later list. Called when + * the call-stack unwinds again. + */ +void +_PyTrash_destroy_chain(void) +{ + while (_PyTrash_delete_later) { + PyObject *op = _PyTrash_delete_later; + destructor dealloc = op->ob_type->tp_dealloc; + + _PyTrash_delete_later = + (PyObject*) _Py_AS_GC(op)->gc.gc_prev; + + /* Call the deallocator directly. This used to try to + * fool Py_DECREF into calling it indirectly, but + * Py_DECREF was already called on this object, and in + * assorted non-release builds calling Py_DECREF again ends + * up distorting allocation statistics. + */ + assert(op->ob_refcnt == 0); + ++_PyTrash_delete_nesting; + (*dealloc)(op); + --_PyTrash_delete_nesting; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/obmalloc.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/obmalloc.c new file mode 100644 index 00000000..a2cec1d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/obmalloc.c @@ -0,0 +1,1375 @@ +#include "Python.h" + +#ifdef WITH_PYMALLOC + +/* An object allocator for Python. + + Here is an introduction to the layers of the Python memory architecture, + showing where the object allocator is actually used (layer +2), It is + called for every object allocation and deallocation (PyObject_New/Del), + unless the object-specific allocators implement a proprietary allocation + scheme (ex.: ints use a simple free list). This is also the place where + the cyclic garbage collector operates selectively on container objects. + + + Object-specific allocators + _____ ______ ______ ________ + [ int ] [ dict ] [ list ] ... [ string ] Python core | ++3 | <----- Object-specific memory -----> | <-- Non-object memory --> | + _______________________________ | | + [ Python's object allocator ] | | ++2 | ####### Object memory ####### | <------ Internal buffers ------> | + ______________________________________________________________ | + [ Python's raw memory allocator (PyMem_ API) ] | ++1 | <----- Python memory (under PyMem manager's control) ------> | | + __________________________________________________________________ + [ Underlying general-purpose allocator (ex: C library malloc) ] + 0 | <------ Virtual memory allocated for the python process -------> | + + ========================================================================= + _______________________________________________________________________ + [ OS-specific Virtual Memory Manager (VMM) ] +-1 | <--- Kernel dynamic storage allocation & management (page-based) ---> | + __________________________________ __________________________________ + [ ] [ ] +-2 | <-- Physical memory: ROM/RAM --> | | <-- Secondary storage (swap) --> | + +*/ +/*==========================================================================*/ + +/* A fast, special-purpose memory allocator for small blocks, to be used + on top of a general-purpose malloc -- heavily based on previous art. */ + +/* Vladimir Marangozov -- August 2000 */ + +/* + * "Memory management is where the rubber meets the road -- if we do the wrong + * thing at any level, the results will not be good. And if we don't make the + * levels work well together, we are in serious trouble." (1) + * + * (1) Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles, + * "Dynamic Storage Allocation: A Survey and Critical Review", + * in Proc. 1995 Int'l. Workshop on Memory Management, September 1995. + */ + +/* #undef WITH_MEMORY_LIMITS */ /* disable mem limit checks */ + +/*==========================================================================*/ + +/* + * Allocation strategy abstract: + * + * For small requests, the allocator sub-allocates blocks of memory. + * Requests greater than 256 bytes are routed to the system's allocator. + * + * Small requests are grouped in size classes spaced 8 bytes apart, due + * to the required valid alignment of the returned address. Requests of + * a particular size are serviced from memory pools of 4K (one VMM page). + * Pools are fragmented on demand and contain free lists of blocks of one + * particular size class. In other words, there is a fixed-size allocator + * for each size class. Free pools are shared by the different allocators + * thus minimizing the space reserved for a particular size class. + * + * This allocation strategy is a variant of what is known as "simple + * segregated storage based on array of free lists". The main drawback of + * simple segregated storage is that we might end up with lot of reserved + * memory for the different free lists, which degenerate in time. To avoid + * this, we partition each free list in pools and we share dynamically the + * reserved space between all free lists. This technique is quite efficient + * for memory intensive programs which allocate mainly small-sized blocks. + * + * For small requests we have the following table: + * + * Request in bytes Size of allocated block Size class idx + * ---------------------------------------------------------------- + * 1-8 8 0 + * 9-16 16 1 + * 17-24 24 2 + * 25-32 32 3 + * 33-40 40 4 + * 41-48 48 5 + * 49-56 56 6 + * 57-64 64 7 + * 65-72 72 8 + * ... ... ... + * 241-248 248 30 + * 249-256 256 31 + * + * 0, 257 and up: routed to the underlying allocator. + */ + +/*==========================================================================*/ + +/* + * -- Main tunable settings section -- + */ + +/* + * Alignment of addresses returned to the user. 8-bytes alignment works + * on most current architectures (with 32-bit or 64-bit address busses). + * The alignment value is also used for grouping small requests in size + * classes spaced ALIGNMENT bytes apart. + * + * You shouldn't change this unless you know what you are doing. + */ +#define ALIGNMENT 8 /* must be 2^N */ +#define ALIGNMENT_SHIFT 3 +#define ALIGNMENT_MASK (ALIGNMENT - 1) + +/* Return the number of bytes in size class I, as a uint. */ +#define INDEX2SIZE(I) (((uint)(I) + 1) << ALIGNMENT_SHIFT) + +/* + * Max size threshold below which malloc requests are considered to be + * small enough in order to use preallocated memory pools. You can tune + * this value according to your application behaviour and memory needs. + * + * The following invariants must hold: + * 1) ALIGNMENT <= SMALL_REQUEST_THRESHOLD <= 256 + * 2) SMALL_REQUEST_THRESHOLD is evenly divisible by ALIGNMENT + * + * Although not required, for better performance and space efficiency, + * it is recommended that SMALL_REQUEST_THRESHOLD is set to a power of 2. + */ +#define SMALL_REQUEST_THRESHOLD 256 +#define NB_SMALL_SIZE_CLASSES (SMALL_REQUEST_THRESHOLD / ALIGNMENT) + +/* + * The system's VMM page size can be obtained on most unices with a + * getpagesize() call or deduced from various header files. To make + * things simpler, we assume that it is 4K, which is OK for most systems. + * It is probably better if this is the native page size, but it doesn't + * have to be. In theory, if SYSTEM_PAGE_SIZE is larger than the native page + * size, then `POOL_ADDR(p)->arenaindex' could rarely cause a segmentation + * violation fault. 4K is apparently OK for all the platforms that python + * currently targets. + */ +#define SYSTEM_PAGE_SIZE (4 * 1024) +#define SYSTEM_PAGE_SIZE_MASK (SYSTEM_PAGE_SIZE - 1) + +/* + * Maximum amount of memory managed by the allocator for small requests. + */ +#ifdef WITH_MEMORY_LIMITS +#ifndef SMALL_MEMORY_LIMIT +#define SMALL_MEMORY_LIMIT (64 * 1024 * 1024) /* 64 MB -- more? */ +#endif +#endif + +/* + * The allocator sub-allocates blocks of memory (called arenas) aligned + * on a page boundary. This is a reserved virtual address space for the + * current process (obtained through a malloc call). In no way this means + * that the memory arenas will be used entirely. A malloc() is usually + * an address range reservation for bytes, unless all pages within this + * space are referenced subsequently. So malloc'ing big blocks and not using + * them does not mean "wasting memory". It's an addressable range wastage... + * + * Therefore, allocating arenas with malloc is not optimal, because there is + * some address space wastage, but this is the most portable way to request + * memory from the system across various platforms. + */ +#define ARENA_SIZE (256 << 10) /* 256KB */ + +#ifdef WITH_MEMORY_LIMITS +#define MAX_ARENAS (SMALL_MEMORY_LIMIT / ARENA_SIZE) +#endif + +/* + * Size of the pools used for small blocks. Should be a power of 2, + * between 1K and SYSTEM_PAGE_SIZE, that is: 1k, 2k, 4k. + */ +#define POOL_SIZE SYSTEM_PAGE_SIZE /* must be 2^N */ +#define POOL_SIZE_MASK SYSTEM_PAGE_SIZE_MASK + +/* + * -- End of tunable settings section -- + */ + +/*==========================================================================*/ + +/* + * Locking + * + * To reduce lock contention, it would probably be better to refine the + * crude function locking with per size class locking. I'm not positive + * however, whether it's worth switching to such locking policy because + * of the performance penalty it might introduce. + * + * The following macros describe the simplest (should also be the fastest) + * lock object on a particular platform and the init/fini/lock/unlock + * operations on it. The locks defined here are not expected to be recursive + * because it is assumed that they will always be called in the order: + * INIT, [LOCK, UNLOCK]*, FINI. + */ + +/* + * Python's threads are serialized, so object malloc locking is disabled. + */ +#define SIMPLELOCK_DECL(lock) /* simple lock declaration */ +#define SIMPLELOCK_INIT(lock) /* allocate (if needed) and initialize */ +#define SIMPLELOCK_FINI(lock) /* free/destroy an existing lock */ +#define SIMPLELOCK_LOCK(lock) /* acquire released lock */ +#define SIMPLELOCK_UNLOCK(lock) /* release acquired lock */ + +/* + * Basic types + * I don't care if these are defined in or elsewhere. Axiom. + */ +#undef uchar +#define uchar unsigned char /* assuming == 8 bits */ + +#undef uint +#define uint unsigned int /* assuming >= 16 bits */ + +#undef ulong +#define ulong unsigned long /* assuming >= 32 bits */ + +#undef uptr +#define uptr Py_uintptr_t + +/* When you say memory, my mind reasons in terms of (pointers to) blocks */ +typedef uchar block; + +/* Pool for small blocks. */ +struct pool_header { + union { block *_padding; + uint count; } ref; /* number of allocated blocks */ + block *freeblock; /* pool's free list head */ + struct pool_header *nextpool; /* next pool of this size class */ + struct pool_header *prevpool; /* previous pool "" */ + uint arenaindex; /* index into arenas of base adr */ + uint szidx; /* block size class index */ + uint nextoffset; /* bytes to virgin block */ + uint maxnextoffset; /* largest valid nextoffset */ +}; + +typedef struct pool_header *poolp; + +#undef ROUNDUP +#define ROUNDUP(x) (((x) + ALIGNMENT_MASK) & ~ALIGNMENT_MASK) +#define POOL_OVERHEAD ROUNDUP(sizeof(struct pool_header)) + +#define DUMMY_SIZE_IDX 0xffff /* size class of newly cached pools */ + +/* Round pointer P down to the closest pool-aligned address <= P, as a poolp */ +#define POOL_ADDR(P) ((poolp)((uptr)(P) & ~(uptr)POOL_SIZE_MASK)) + +/* Return total number of blocks in pool of size index I, as a uint. */ +#define NUMBLOCKS(I) ((uint)(POOL_SIZE - POOL_OVERHEAD) / INDEX2SIZE(I)) + +/*==========================================================================*/ + +/* + * This malloc lock + */ +SIMPLELOCK_DECL(_malloc_lock) +#define LOCK() SIMPLELOCK_LOCK(_malloc_lock) +#define UNLOCK() SIMPLELOCK_UNLOCK(_malloc_lock) +#define LOCK_INIT() SIMPLELOCK_INIT(_malloc_lock) +#define LOCK_FINI() SIMPLELOCK_FINI(_malloc_lock) + +/* + * Pool table -- headed, circular, doubly-linked lists of partially used pools. + +This is involved. For an index i, usedpools[i+i] is the header for a list of +all partially used pools holding small blocks with "size class idx" i. So +usedpools[0] corresponds to blocks of size 8, usedpools[2] to blocks of size +16, and so on: index 2*i <-> blocks of size (i+1)<freeblock points to +the start of a singly-linked list of free blocks within the pool. When a +block is freed, it's inserted at the front of its pool's freeblock list. Note +that the available blocks in a pool are *not* linked all together when a pool +is initialized. Instead only "the first two" (lowest addresses) blocks are +set up, returning the first such block, and setting pool->freeblock to a +one-block list holding the second such block. This is consistent with that +pymalloc strives at all levels (arena, pool, and block) never to touch a piece +of memory until it's actually needed. + +So long as a pool is in the used state, we're certain there *is* a block +available for allocating, and pool->freeblock is not NULL. If pool->freeblock +points to the end of the free list before we've carved the entire pool into +blocks, that means we simply haven't yet gotten to one of the higher-address +blocks. The offset from the pool_header to the start of "the next" virgin +block is stored in the pool_header nextoffset member, and the largest value +of nextoffset that makes sense is stored in the maxnextoffset member when a +pool is initialized. All the blocks in a pool have been passed out at least +once when and only when nextoffset > maxnextoffset. + + +Major obscurity: While the usedpools vector is declared to have poolp +entries, it doesn't really. It really contains two pointers per (conceptual) +poolp entry, the nextpool and prevpool members of a pool_header. The +excruciating initialization code below fools C so that + + usedpool[i+i] + +"acts like" a genuine poolp, but only so long as you only reference its +nextpool and prevpool members. The "- 2*sizeof(block *)" gibberish is +compensating for that a pool_header's nextpool and prevpool members +immediately follow a pool_header's first two members: + + union { block *_padding; + uint count; } ref; + block *freeblock; + +each of which consume sizeof(block *) bytes. So what usedpools[i+i] really +contains is a fudged-up pointer p such that *if* C believes it's a poolp +pointer, then p->nextpool and p->prevpool are both p (meaning that the headed +circular list is empty). + +It's unclear why the usedpools setup is so convoluted. It could be to +minimize the amount of cache required to hold this heavily-referenced table +(which only *needs* the two interpool pointer members of a pool_header). OTOH, +referencing code has to remember to "double the index" and doing so isn't +free, usedpools[0] isn't a strictly legal pointer, and we're crucially relying +on that C doesn't insert any padding anywhere in a pool_header at or before +the prevpool member. +**************************************************************************** */ + +#define PTA(x) ((poolp )((uchar *)&(usedpools[2*(x)]) - 2*sizeof(block *))) +#define PT(x) PTA(x), PTA(x) + +static poolp usedpools[2 * ((NB_SMALL_SIZE_CLASSES + 7) / 8) * 8] = { + PT(0), PT(1), PT(2), PT(3), PT(4), PT(5), PT(6), PT(7) +#if NB_SMALL_SIZE_CLASSES > 8 + , PT(8), PT(9), PT(10), PT(11), PT(12), PT(13), PT(14), PT(15) +#if NB_SMALL_SIZE_CLASSES > 16 + , PT(16), PT(17), PT(18), PT(19), PT(20), PT(21), PT(22), PT(23) +#if NB_SMALL_SIZE_CLASSES > 24 + , PT(24), PT(25), PT(26), PT(27), PT(28), PT(29), PT(30), PT(31) +#if NB_SMALL_SIZE_CLASSES > 32 + , PT(32), PT(33), PT(34), PT(35), PT(36), PT(37), PT(38), PT(39) +#if NB_SMALL_SIZE_CLASSES > 40 + , PT(40), PT(41), PT(42), PT(43), PT(44), PT(45), PT(46), PT(47) +#if NB_SMALL_SIZE_CLASSES > 48 + , PT(48), PT(49), PT(50), PT(51), PT(52), PT(53), PT(54), PT(55) +#if NB_SMALL_SIZE_CLASSES > 56 + , PT(56), PT(57), PT(58), PT(59), PT(60), PT(61), PT(62), PT(63) +#endif /* NB_SMALL_SIZE_CLASSES > 56 */ +#endif /* NB_SMALL_SIZE_CLASSES > 48 */ +#endif /* NB_SMALL_SIZE_CLASSES > 40 */ +#endif /* NB_SMALL_SIZE_CLASSES > 32 */ +#endif /* NB_SMALL_SIZE_CLASSES > 24 */ +#endif /* NB_SMALL_SIZE_CLASSES > 16 */ +#endif /* NB_SMALL_SIZE_CLASSES > 8 */ +}; + +/* + * Free (cached) pools + */ +static poolp freepools = NULL; /* free list for cached pools */ + +/*==========================================================================*/ +/* Arena management. */ + +/* arenas is a vector of arena base addresses, in order of allocation time. + * arenas currently contains narenas entries, and has space allocated + * for at most maxarenas entries. + * + * CAUTION: See the long comment block about thread safety in new_arena(): + * the code currently relies in deep ways on that this vector only grows, + * and only grows by appending at the end. For now we never return an arena + * to the OS. + */ +static uptr *volatile arenas = NULL; /* the pointer itself is volatile */ +static volatile uint narenas = 0; +static uint maxarenas = 0; + +/* Number of pools still available to be allocated in the current arena. */ +static uint nfreepools = 0; + +/* Free space start address in current arena. This is pool-aligned. */ +static block *arenabase = NULL; + +/* Allocate a new arena and return its base address. If we run out of + * memory, return NULL. + */ +static block * +new_arena(void) +{ + uint excess; /* number of bytes above pool alignment */ + block *bp = (block *)malloc(ARENA_SIZE); + if (bp == NULL) + return NULL; + +#ifdef PYMALLOC_DEBUG + if (Py_GETENV("PYTHONMALLOCSTATS")) + _PyObject_DebugMallocStats(); +#endif + + /* arenabase <- first pool-aligned address in the arena + nfreepools <- number of whole pools that fit after alignment */ + arenabase = bp; + nfreepools = ARENA_SIZE / POOL_SIZE; + assert(POOL_SIZE * nfreepools == ARENA_SIZE); + excess = (uint) ((Py_uintptr_t)bp & POOL_SIZE_MASK); + if (excess != 0) { + --nfreepools; + arenabase += POOL_SIZE - excess; + } + + /* Make room for a new entry in the arenas vector. */ + if (arenas == NULL) { + assert(narenas == 0 && maxarenas == 0); + arenas = (uptr *)malloc(16 * sizeof(*arenas)); + if (arenas == NULL) + goto error; + maxarenas = 16; + } + else if (narenas == maxarenas) { + /* Grow arenas. + * + * Exceedingly subtle: Someone may be calling the pymalloc + * free via PyMem_{DEL, Del, FREE, Free} without holding the + *.GIL. Someone else may simultaneously be calling the + * pymalloc malloc while holding the GIL via, e.g., + * PyObject_New. Now the pymalloc free may index into arenas + * for an address check, while the pymalloc malloc calls + * new_arena and we end up here to grow a new arena *and* + * grow the arenas vector. If the value for arenas pymalloc + * free picks up "vanishes" during this resize, anything may + * happen, and it would be an incredibly rare bug. Therefore + * the code here takes great pains to make sure that, at every + * moment, arenas always points to an intact vector of + * addresses. It doesn't matter whether arenas points to a + * wholly up-to-date vector when pymalloc free checks it in + * this case, because the only legal (and that even this is + * legal is debatable) way to call PyMem_{Del, etc} while not + * holding the GIL is if the memory being released is not + * object memory, i.e. if the address check in pymalloc free + * is supposed to fail. Having an incomplete vector can't + * make a supposed-to-fail case succeed by mistake (it could + * only make a supposed-to-succeed case fail by mistake). + * + * In addition, without a lock we can't know for sure when + * an old vector is no longer referenced, so we simply let + * old vectors leak. + * + * And on top of that, since narenas and arenas can't be + * changed as-a-pair atomically without a lock, we're also + * careful to declare them volatile and ensure that we change + * arenas first. This prevents another thread from picking + * up an narenas value too large for the arenas value it + * reads up (arenas never shrinks). + * + * Read the above 50 times before changing anything in this + * block. + */ + uptr *p; + uint newmax = maxarenas << 1; + if (newmax <= maxarenas) /* overflow */ + goto error; + p = (uptr *)malloc(newmax * sizeof(*arenas)); + if (p == NULL) + goto error; + memcpy(p, arenas, narenas * sizeof(*arenas)); + arenas = p; /* old arenas deliberately leaked */ + maxarenas = newmax; + } + + /* Append the new arena address to arenas. */ + assert(narenas < maxarenas); + arenas[narenas] = (uptr)bp; + ++narenas; /* can't overflow, since narenas < maxarenas before */ + return bp; + +error: + free(bp); + nfreepools = 0; + return NULL; +} + +/* Return true if and only if P is an address that was allocated by + * pymalloc. I must be the index into arenas that the address claims + * to come from. + * + * Tricky: Letting B be the arena base address in arenas[I], P belongs to the + * arena if and only if + * B <= P < B + ARENA_SIZE + * Subtracting B throughout, this is true iff + * 0 <= P-B < ARENA_SIZE + * By using unsigned arithmetic, the "0 <=" half of the test can be skipped. + * + * Obscure: A PyMem "free memory" function can call the pymalloc free or + * realloc before the first arena has been allocated. arenas is still + * NULL in that case. We're relying on that narenas is also 0 in that case, + * so the (I) < narenas must be false, saving us from trying to index into + * a NULL arenas. + */ +#define ADDRESS_IN_RANGE(P, I) \ + ((I) < narenas && (uptr)(P) - arenas[I] < (uptr)ARENA_SIZE) + +/*==========================================================================*/ + +/* malloc. Note that nbytes==0 tries to return a non-NULL pointer, distinct + * from all other currently live pointers. This may not be possible. + */ + +/* + * The basic blocks are ordered by decreasing execution frequency, + * which minimizes the number of jumps in the most common cases, + * improves branching prediction and instruction scheduling (small + * block allocations typically result in a couple of instructions). + * Unless the optimizer reorders everything, being too smart... + */ + +#undef PyObject_Malloc +void * +PyObject_Malloc(size_t nbytes) +{ + block *bp; + poolp pool; + poolp next; + uint size; + + /* + * This implicitly redirects malloc(0). + */ + if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) { + LOCK(); + /* + * Most frequent paths first + */ + size = (uint )(nbytes - 1) >> ALIGNMENT_SHIFT; + pool = usedpools[size + size]; + if (pool != pool->nextpool) { + /* + * There is a used pool for this size class. + * Pick up the head block of its free list. + */ + ++pool->ref.count; + bp = pool->freeblock; + assert(bp != NULL); + if ((pool->freeblock = *(block **)bp) != NULL) { + UNLOCK(); + return (void *)bp; + } + /* + * Reached the end of the free list, try to extend it + */ + if (pool->nextoffset <= pool->maxnextoffset) { + /* + * There is room for another block + */ + pool->freeblock = (block *)pool + + pool->nextoffset; + pool->nextoffset += INDEX2SIZE(size); + *(block **)(pool->freeblock) = NULL; + UNLOCK(); + return (void *)bp; + } + /* + * Pool is full, unlink from used pools + */ + next = pool->nextpool; + pool = pool->prevpool; + next->prevpool = pool; + pool->nextpool = next; + UNLOCK(); + return (void *)bp; + } + /* + * Try to get a cached free pool + */ + pool = freepools; + if (pool != NULL) { + /* + * Unlink from cached pools + */ + freepools = pool->nextpool; + init_pool: + /* + * Frontlink to used pools + */ + next = usedpools[size + size]; /* == prev */ + pool->nextpool = next; + pool->prevpool = next; + next->nextpool = pool; + next->prevpool = pool; + pool->ref.count = 1; + if (pool->szidx == size) { + /* + * Luckily, this pool last contained blocks + * of the same size class, so its header + * and free list are already initialized. + */ + bp = pool->freeblock; + pool->freeblock = *(block **)bp; + UNLOCK(); + return (void *)bp; + } + /* + * Initialize the pool header, set up the free list to + * contain just the second block, and return the first + * block. + */ + pool->szidx = size; + size = INDEX2SIZE(size); + bp = (block *)pool + POOL_OVERHEAD; + pool->nextoffset = POOL_OVERHEAD + (size << 1); + pool->maxnextoffset = POOL_SIZE - size; + pool->freeblock = bp + size; + *(block **)(pool->freeblock) = NULL; + UNLOCK(); + return (void *)bp; + } + /* + * Allocate new pool + */ + if (nfreepools) { + commit_pool: + --nfreepools; + pool = (poolp)arenabase; + arenabase += POOL_SIZE; + pool->arenaindex = narenas - 1; + pool->szidx = DUMMY_SIZE_IDX; + goto init_pool; + } + /* + * Allocate new arena + */ +#ifdef WITH_MEMORY_LIMITS + if (!(narenas < MAX_ARENAS)) { + UNLOCK(); + goto redirect; + } +#endif + bp = new_arena(); + if (bp != NULL) + goto commit_pool; + UNLOCK(); + goto redirect; + } + + /* The small block allocator ends here. */ + +redirect: + /* + * Redirect the original request to the underlying (libc) allocator. + * We jump here on bigger requests, on error in the code above (as a + * last chance to serve the request) or when the max memory limit + * has been reached. + */ + if (nbytes == 0) + nbytes = 1; + return (void *)malloc(nbytes); +} + +/* free */ + +#undef PyObject_Free +void +PyObject_Free(void *p) +{ + poolp pool; + block *lastfree; + poolp next, prev; + uint size; + + if (p == NULL) /* free(NULL) has no effect */ + return; + + pool = POOL_ADDR(p); + if (ADDRESS_IN_RANGE(p, pool->arenaindex)) { + /* We allocated this address. */ + LOCK(); + /* + * Link p to the start of the pool's freeblock list. Since + * the pool had at least the p block outstanding, the pool + * wasn't empty (so it's already in a usedpools[] list, or + * was full and is in no list -- it's not in the freeblocks + * list in any case). + */ + assert(pool->ref.count > 0); /* else it was empty */ + *(block **)p = lastfree = pool->freeblock; + pool->freeblock = (block *)p; + if (lastfree) { + /* + * freeblock wasn't NULL, so the pool wasn't full, + * and the pool is in a usedpools[] list. + */ + if (--pool->ref.count != 0) { + /* pool isn't empty: leave it in usedpools */ + UNLOCK(); + return; + } + /* + * Pool is now empty: unlink from usedpools, and + * link to the front of freepools. This ensures that + * previously freed pools will be allocated later + * (being not referenced, they are perhaps paged out). + */ + next = pool->nextpool; + prev = pool->prevpool; + next->prevpool = prev; + prev->nextpool = next; + /* Link to freepools. This is a singly-linked list, + * and pool->prevpool isn't used there. + */ + pool->nextpool = freepools; + freepools = pool; + UNLOCK(); + return; + } + /* + * Pool was full, so doesn't currently live in any list: + * link it to the front of the appropriate usedpools[] list. + * This mimics LRU pool usage for new allocations and + * targets optimal filling when several pools contain + * blocks of the same size class. + */ + --pool->ref.count; + assert(pool->ref.count > 0); /* else the pool is empty */ + size = pool->szidx; + next = usedpools[size + size]; + prev = next->prevpool; + /* insert pool before next: prev <-> pool <-> next */ + pool->nextpool = next; + pool->prevpool = prev; + next->prevpool = pool; + prev->nextpool = pool; + UNLOCK(); + return; + } + + /* We didn't allocate this address. */ + free(p); +} + +/* realloc. If p is NULL, this acts like malloc(nbytes). Else if nbytes==0, + * then as the Python docs promise, we do not treat this like free(p), and + * return a non-NULL result. + */ + +#undef PyObject_Realloc +void * +PyObject_Realloc(void *p, size_t nbytes) +{ + void *bp; + poolp pool; + uint size; + + if (p == NULL) + return PyObject_Malloc(nbytes); + + pool = POOL_ADDR(p); + if (ADDRESS_IN_RANGE(p, pool->arenaindex)) { + /* We're in charge of this block */ + size = INDEX2SIZE(pool->szidx); + if (nbytes <= size) { + /* The block is staying the same or shrinking. If + * it's shrinking, there's a tradeoff: it costs + * cycles to copy the block to a smaller size class, + * but it wastes memory not to copy it. The + * compromise here is to copy on shrink only if at + * least 25% of size can be shaved off. + */ + if (4 * nbytes > 3 * size) { + /* It's the same, + * or shrinking and new/old > 3/4. + */ + return p; + } + size = nbytes; + } + bp = PyObject_Malloc(nbytes); + if (bp != NULL) { + memcpy(bp, p, size); + PyObject_Free(p); + } + return bp; + } + /* We're not managing this block. */ + if (nbytes <= SMALL_REQUEST_THRESHOLD) { + /* Take over this block -- ask for at least one byte so + * we really do take it over (PyObject_Malloc(0) goes to + * the system malloc). + */ + bp = PyObject_Malloc(nbytes ? nbytes : 1); + if (bp != NULL) { + memcpy(bp, p, nbytes); + free(p); + } + else if (nbytes == 0) { + /* Meet the doc's promise that nbytes==0 will + * never return a NULL pointer when p isn't NULL. + */ + bp = p; + } + + } + else { + assert(nbytes != 0); + bp = realloc(p, nbytes); + } + return bp; +} + +#else /* ! WITH_PYMALLOC */ + +/*==========================================================================*/ +/* pymalloc not enabled: Redirect the entry points to malloc. These will + * only be used by extensions that are compiled with pymalloc enabled. */ + +void * +PyObject_Malloc(size_t n) +{ + return PyMem_MALLOC(n); +} + +void * +PyObject_Realloc(void *p, size_t n) +{ + return PyMem_REALLOC(p, n); +} + +void +PyObject_Free(void *p) +{ + PyMem_FREE(p); +} +#endif /* WITH_PYMALLOC */ + +#ifdef PYMALLOC_DEBUG +/*==========================================================================*/ +/* A x-platform debugging allocator. This doesn't manage memory directly, + * it wraps a real allocator, adding extra debugging info to the memory blocks. + */ + +/* Special bytes broadcast into debug memory blocks at appropriate times. + * Strings of these are unlikely to be valid addresses, floats, ints or + * 7-bit ASCII. + */ +#undef CLEANBYTE +#undef DEADBYTE +#undef FORBIDDENBYTE +#define CLEANBYTE 0xCB /* clean (newly allocated) memory */ +#define DEADBYTE 0xDB /* dead (newly freed) memory */ +#define FORBIDDENBYTE 0xFB /* untouchable bytes at each end of a block */ + +static ulong serialno = 0; /* incremented on each debug {m,re}alloc */ + +/* serialno is always incremented via calling this routine. The point is + to supply a single place to set a breakpoint. +*/ +static void +bumpserialno(void) +{ + ++serialno; +} + + +/* Read 4 bytes at p as a big-endian ulong. */ +static ulong +read4(const void *p) +{ + const uchar *q = (const uchar *)p; + return ((ulong)q[0] << 24) | + ((ulong)q[1] << 16) | + ((ulong)q[2] << 8) | + (ulong)q[3]; +} + +/* Write the 4 least-significant bytes of n as a big-endian unsigned int, + MSB at address p, LSB at p+3. */ +static void +write4(void *p, ulong n) +{ + uchar *q = (uchar *)p; + q[0] = (uchar)((n >> 24) & 0xff); + q[1] = (uchar)((n >> 16) & 0xff); + q[2] = (uchar)((n >> 8) & 0xff); + q[3] = (uchar)( n & 0xff); +} + +#ifdef Py_DEBUG +/* Is target in the list? The list is traversed via the nextpool pointers. + * The list may be NULL-terminated, or circular. Return 1 if target is in + * list, else 0. + */ +static int +pool_is_in_list(const poolp target, poolp list) +{ + poolp origlist = list; + assert(target != NULL); + if (list == NULL) + return 0; + do { + if (target == list) + return 1; + list = list->nextpool; + } while (list != NULL && list != origlist); + return 0; +} + +#else +#define pool_is_in_list(X, Y) 1 + +#endif /* Py_DEBUG */ + +/* The debug malloc asks for 16 extra bytes and fills them with useful stuff, + here calling the underlying malloc's result p: + +p[0:4] + Number of bytes originally asked for. 4-byte unsigned integer, + big-endian (easier to read in a memory dump). +p[4:8] + Copies of FORBIDDENBYTE. Used to catch under- writes and reads. +p[8:8+n] + The requested memory, filled with copies of CLEANBYTE. + Used to catch reference to uninitialized memory. + &p[8] is returned. Note that this is 8-byte aligned if pymalloc + handled the request itself. +p[8+n:8+n+4] + Copies of FORBIDDENBYTE. Used to catch over- writes and reads. +p[8+n+4:8+n+8] + A serial number, incremented by 1 on each call to _PyObject_DebugMalloc + and _PyObject_DebugRealloc. + 4-byte unsigned integer, big-endian. + If "bad memory" is detected later, the serial number gives an + excellent way to set a breakpoint on the next run, to capture the + instant at which this block was passed out. +*/ + +void * +_PyObject_DebugMalloc(size_t nbytes) +{ + uchar *p; /* base address of malloc'ed block */ + uchar *tail; /* p + 8 + nbytes == pointer to tail pad bytes */ + size_t total; /* nbytes + 16 */ + + bumpserialno(); + total = nbytes + 16; + if (total < nbytes || (total >> 31) > 1) { + /* overflow, or we can't represent it in 4 bytes */ + /* Obscure: can't do (total >> 32) != 0 instead, because + C doesn't define what happens for a right-shift of 32 + when size_t is a 32-bit type. At least C guarantees + size_t is an unsigned type. */ + return NULL; + } + + p = (uchar *)PyObject_Malloc(total); + if (p == NULL) + return NULL; + + write4(p, nbytes); + p[4] = p[5] = p[6] = p[7] = FORBIDDENBYTE; + + if (nbytes > 0) + memset(p+8, CLEANBYTE, nbytes); + + tail = p + 8 + nbytes; + tail[0] = tail[1] = tail[2] = tail[3] = FORBIDDENBYTE; + write4(tail + 4, serialno); + + return p+8; +} + +/* The debug free first checks the 8 bytes on each end for sanity (in + particular, that the FORBIDDENBYTEs are still intact). + Then fills the original bytes with DEADBYTE. + Then calls the underlying free. +*/ +void +_PyObject_DebugFree(void *p) +{ + uchar *q = (uchar *)p; + size_t nbytes; + + if (p == NULL) + return; + _PyObject_DebugCheckAddress(p); + nbytes = read4(q-8); + if (nbytes > 0) + memset(q, DEADBYTE, nbytes); + PyObject_Free(q-8); +} + +void * +_PyObject_DebugRealloc(void *p, size_t nbytes) +{ + uchar *q = (uchar *)p; + uchar *tail; + size_t total; /* nbytes + 16 */ + size_t original_nbytes; + + if (p == NULL) + return _PyObject_DebugMalloc(nbytes); + + _PyObject_DebugCheckAddress(p); + bumpserialno(); + original_nbytes = read4(q-8); + total = nbytes + 16; + if (total < nbytes || (total >> 31) > 1) { + /* overflow, or we can't represent it in 4 bytes */ + return NULL; + } + + if (nbytes < original_nbytes) { + /* shrinking: mark old extra memory dead */ + memset(q + nbytes, DEADBYTE, original_nbytes - nbytes); + } + + /* Resize and add decorations. */ + q = (uchar *)PyObject_Realloc(q-8, total); + if (q == NULL) + return NULL; + + write4(q, nbytes); + assert(q[4] == FORBIDDENBYTE && + q[5] == FORBIDDENBYTE && + q[6] == FORBIDDENBYTE && + q[7] == FORBIDDENBYTE); + q += 8; + tail = q + nbytes; + tail[0] = tail[1] = tail[2] = tail[3] = FORBIDDENBYTE; + write4(tail + 4, serialno); + + if (nbytes > original_nbytes) { + /* growing: mark new extra memory clean */ + memset(q + original_nbytes, CLEANBYTE, + nbytes - original_nbytes); + } + + return q; +} + +/* Check the forbidden bytes on both ends of the memory allocated for p. + * If anything is wrong, print info to stderr via _PyObject_DebugDumpAddress, + * and call Py_FatalError to kill the program. + */ + void +_PyObject_DebugCheckAddress(const void *p) +{ + const uchar *q = (const uchar *)p; + char *msg; + ulong nbytes; + const uchar *tail; + int i; + + if (p == NULL) { + msg = "didn't expect a NULL pointer"; + goto error; + } + + /* Check the stuff at the start of p first: if there's underwrite + * corruption, the number-of-bytes field may be nuts, and checking + * the tail could lead to a segfault then. + */ + for (i = 4; i >= 1; --i) { + if (*(q-i) != FORBIDDENBYTE) { + msg = "bad leading pad byte"; + goto error; + } + } + + nbytes = read4(q-8); + tail = q + nbytes; + for (i = 0; i < 4; ++i) { + if (tail[i] != FORBIDDENBYTE) { + msg = "bad trailing pad byte"; + goto error; + } + } + + return; + +error: + _PyObject_DebugDumpAddress(p); + Py_FatalError(msg); +} + +/* Display info to stderr about the memory block at p. */ +void +_PyObject_DebugDumpAddress(const void *p) +{ + const uchar *q = (const uchar *)p; + const uchar *tail; + ulong nbytes, serial; + int i; + + fprintf(stderr, "Debug memory block at address p=%p:\n", p); + if (p == NULL) + return; + + nbytes = read4(q-8); + fprintf(stderr, " %lu bytes originally requested\n", nbytes); + + /* In case this is nuts, check the leading pad bytes first. */ + fputs(" The 4 pad bytes at p-4 are ", stderr); + if (*(q-4) == FORBIDDENBYTE && + *(q-3) == FORBIDDENBYTE && + *(q-2) == FORBIDDENBYTE && + *(q-1) == FORBIDDENBYTE) { + fputs("FORBIDDENBYTE, as expected.\n", stderr); + } + else { + fprintf(stderr, "not all FORBIDDENBYTE (0x%02x):\n", + FORBIDDENBYTE); + for (i = 4; i >= 1; --i) { + const uchar byte = *(q-i); + fprintf(stderr, " at p-%d: 0x%02x", i, byte); + if (byte != FORBIDDENBYTE) + fputs(" *** OUCH", stderr); + fputc('\n', stderr); + } + + fputs(" Because memory is corrupted at the start, the " + "count of bytes requested\n" + " may be bogus, and checking the trailing pad " + "bytes may segfault.\n", stderr); + } + + tail = q + nbytes; + fprintf(stderr, " The 4 pad bytes at tail=%p are ", tail); + if (tail[0] == FORBIDDENBYTE && + tail[1] == FORBIDDENBYTE && + tail[2] == FORBIDDENBYTE && + tail[3] == FORBIDDENBYTE) { + fputs("FORBIDDENBYTE, as expected.\n", stderr); + } + else { + fprintf(stderr, "not all FORBIDDENBYTE (0x%02x):\n", + FORBIDDENBYTE); + for (i = 0; i < 4; ++i) { + const uchar byte = tail[i]; + fprintf(stderr, " at tail+%d: 0x%02x", + i, byte); + if (byte != FORBIDDENBYTE) + fputs(" *** OUCH", stderr); + fputc('\n', stderr); + } + } + + serial = read4(tail+4); + fprintf(stderr, " The block was made by call #%lu to " + "debug malloc/realloc.\n", serial); + + if (nbytes > 0) { + int i = 0; + fputs(" Data at p:", stderr); + /* print up to 8 bytes at the start */ + while (q < tail && i < 8) { + fprintf(stderr, " %02x", *q); + ++i; + ++q; + } + /* and up to 8 at the end */ + if (q < tail) { + if (tail - q > 8) { + fputs(" ...", stderr); + q = tail - 8; + } + while (q < tail) { + fprintf(stderr, " %02x", *q); + ++q; + } + } + fputc('\n', stderr); + } +} + +static ulong +printone(const char* msg, ulong value) +{ + int i, k; + char buf[100]; + ulong origvalue = value; + + fputs(msg, stderr); + for (i = (int)strlen(msg); i < 35; ++i) + fputc(' ', stderr); + fputc('=', stderr); + + /* Write the value with commas. */ + i = 22; + buf[i--] = '\0'; + buf[i--] = '\n'; + k = 3; + do { + ulong nextvalue = value / 10UL; + uint digit = value - nextvalue * 10UL; + value = nextvalue; + buf[i--] = (char)(digit + '0'); + --k; + if (k == 0 && value && i >= 0) { + k = 3; + buf[i--] = ','; + } + } while (value && i >= 0); + + while (i >= 0) + buf[i--] = ' '; + fputs(buf, stderr); + + return origvalue; +} + +/* Print summary info to stderr about the state of pymalloc's structures. + * In Py_DEBUG mode, also perform some expensive internal consistency + * checks. + */ +void +_PyObject_DebugMallocStats(void) +{ + uint i; + const uint numclasses = SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT; + /* # of pools, allocated blocks, and free blocks per class index */ + ulong numpools[SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT]; + ulong numblocks[SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT]; + ulong numfreeblocks[SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT]; + /* total # of allocated bytes in used and full pools */ + ulong allocated_bytes = 0; + /* total # of available bytes in used pools */ + ulong available_bytes = 0; + /* # of free pools + pools not yet carved out of current arena */ + uint numfreepools = 0; + /* # of bytes for arena alignment padding */ + ulong arena_alignment = 0; + /* # of bytes in used and full pools used for pool_headers */ + ulong pool_header_bytes = 0; + /* # of bytes in used and full pools wasted due to quantization, + * i.e. the necessarily leftover space at the ends of used and + * full pools. + */ + ulong quantization = 0; + /* running total -- should equal narenas * ARENA_SIZE */ + ulong total; + char buf[128]; + + fprintf(stderr, "Small block threshold = %d, in %u size classes.\n", + SMALL_REQUEST_THRESHOLD, numclasses); + + for (i = 0; i < numclasses; ++i) + numpools[i] = numblocks[i] = numfreeblocks[i] = 0; + + /* Because full pools aren't linked to from anything, it's easiest + * to march over all the arenas. If we're lucky, most of the memory + * will be living in full pools -- would be a shame to miss them. + */ + for (i = 0; i < narenas; ++i) { + uint poolsinarena; + uint j; + uptr base = arenas[i]; + + /* round up to pool alignment */ + poolsinarena = ARENA_SIZE / POOL_SIZE; + if (base & (uptr)POOL_SIZE_MASK) { + --poolsinarena; + arena_alignment += POOL_SIZE; + base &= ~(uptr)POOL_SIZE_MASK; + base += POOL_SIZE; + } + + if (i == narenas - 1) { + /* current arena may have raw memory at the end */ + numfreepools += nfreepools; + poolsinarena -= nfreepools; + } + + /* visit every pool in the arena */ + for (j = 0; j < poolsinarena; ++j, base += POOL_SIZE) { + poolp p = (poolp)base; + const uint sz = p->szidx; + uint freeblocks; + + if (p->ref.count == 0) { + /* currently unused */ + ++numfreepools; + assert(pool_is_in_list(p, freepools)); + continue; + } + ++numpools[sz]; + numblocks[sz] += p->ref.count; + freeblocks = NUMBLOCKS(sz) - p->ref.count; + numfreeblocks[sz] += freeblocks; +#ifdef Py_DEBUG + if (freeblocks > 0) + assert(pool_is_in_list(p, usedpools[sz + sz])); +#endif + } + } + + fputc('\n', stderr); + fputs("class size num pools blocks in use avail blocks\n" + "----- ---- --------- ------------- ------------\n", + stderr); + + for (i = 0; i < numclasses; ++i) { + ulong p = numpools[i]; + ulong b = numblocks[i]; + ulong f = numfreeblocks[i]; + uint size = INDEX2SIZE(i); + if (p == 0) { + assert(b == 0 && f == 0); + continue; + } + fprintf(stderr, "%5u %6u %11lu %15lu %13lu\n", + i, size, p, b, f); + allocated_bytes += b * size; + available_bytes += f * size; + pool_header_bytes += p * POOL_OVERHEAD; + quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size); + } + fputc('\n', stderr); + (void)printone("# times object malloc called", serialno); + + PyOS_snprintf(buf, sizeof(buf), + "%u arenas * %d bytes/arena", narenas, ARENA_SIZE); + (void)printone(buf, (ulong)narenas * ARENA_SIZE); + + fputc('\n', stderr); + + total = printone("# bytes in allocated blocks", allocated_bytes); + total += printone("# bytes in available blocks", available_bytes); + + PyOS_snprintf(buf, sizeof(buf), + "%u unused pools * %d bytes", numfreepools, POOL_SIZE); + total += printone(buf, (ulong)numfreepools * POOL_SIZE); + + total += printone("# bytes lost to pool headers", pool_header_bytes); + total += printone("# bytes lost to quantization", quantization); + total += printone("# bytes lost to arena alignment", arena_alignment); + (void)printone("Total", total); +} + +#endif /* PYMALLOC_DEBUG */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/rangeobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/rangeobject.c new file mode 100644 index 00000000..635f0dd8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/rangeobject.c @@ -0,0 +1,287 @@ +/* Range object implementation */ + +#include "Python.h" + +typedef struct { + PyObject_HEAD + long start; + long step; + long len; +} rangeobject; + +PyObject * +PyRange_New(long start, long len, long step, int reps) +{ + rangeobject *obj; + + if (reps != 1) { + PyErr_SetString(PyExc_ValueError, + "PyRange_New's 'repetitions' argument must be 1"); + return NULL; + } + + obj = PyObject_New(rangeobject, &PyRange_Type); + if (obj == NULL) + return NULL; + + if (len == 0) { + start = 0; + len = 0; + step = 1; + } + else { + long last = start + (len - 1) * step; + if ((step > 0) ? + (last > (PyInt_GetMax() - step)) : + (last < (-1 - PyInt_GetMax() - step))) { + PyErr_SetString(PyExc_OverflowError, + "integer addition"); + return NULL; + } + } + obj->start = start; + obj->len = len; + obj->step = step; + + return (PyObject *) obj; +} + +/* Return number of items in range/xrange (lo, hi, step). step > 0 + * required. Return a value < 0 if & only if the true value is too + * large to fit in a signed long. + */ +static long +get_len_of_range(long lo, long hi, long step) +{ + /* ------------------------------------------------------------- + If lo >= hi, the range is empty. + Else if n values are in the range, the last one is + lo + (n-1)*step, which must be <= hi-1. Rearranging, + n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives + the proper value. Since lo < hi in this case, hi-lo-1 >= 0, so + the RHS is non-negative and so truncation is the same as the + floor. Letting M be the largest positive long, the worst case + for the RHS numerator is hi=M, lo=-M-1, and then + hi-lo-1 = M-(-M-1)-1 = 2*M. Therefore unsigned long has enough + precision to compute the RHS exactly. + ---------------------------------------------------------------*/ + long n = 0; + if (lo < hi) { + unsigned long uhi = (unsigned long)hi; + unsigned long ulo = (unsigned long)lo; + unsigned long diff = uhi - ulo - 1; + n = (long)(diff / (unsigned long)step + 1); + } + return n; +} + +static PyObject * +range_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + long ilow = 0, ihigh = 0, istep = 1; + long n; + + if (PyTuple_Size(args) <= 1) { + if (!PyArg_ParseTuple(args, + "l;xrange() requires 1-3 int arguments", + &ihigh)) + return NULL; + } + else { + if (!PyArg_ParseTuple(args, + "ll|l;xrange() requires 1-3 int arguments", + &ilow, &ihigh, &istep)) + return NULL; + } + if (istep == 0) { + PyErr_SetString(PyExc_ValueError, "xrange() arg 3 must not be zero"); + return NULL; + } + if (istep > 0) + n = get_len_of_range(ilow, ihigh, istep); + else + n = get_len_of_range(ihigh, ilow, -istep); + if (n < 0) { + PyErr_SetString(PyExc_OverflowError, + "xrange() result has too many items"); + return NULL; + } + return PyRange_New(ilow, n, istep, 1); +} + +PyDoc_STRVAR(range_doc, +"xrange([start,] stop[, step]) -> xrange object\n\ +\n\ +Like range(), but instead of returning a list, returns an object that\n\ +generates the numbers in the range on demand. For looping, this is \n\ +slightly faster than range() and more memory efficient."); + +static PyObject * +range_item(rangeobject *r, int i) +{ + if (i < 0 || i >= r->len) { + PyErr_SetString(PyExc_IndexError, + "xrange object index out of range"); + return NULL; + } + return PyInt_FromLong(r->start + (i % r->len) * r->step); +} + +static int +range_length(rangeobject *r) +{ +#if LONG_MAX != INT_MAX + if (r->len > INT_MAX) { + PyErr_SetString(PyExc_ValueError, + "xrange object size cannot be reported"); + return -1; + } +#endif + return (int)(r->len); +} + +static PyObject * +range_repr(rangeobject *r) +{ + PyObject *rtn; + + if (r->start == 0 && r->step == 1) + rtn = PyString_FromFormat("xrange(%ld)", + r->start + r->len * r->step); + + else if (r->step == 1) + rtn = PyString_FromFormat("xrange(%ld, %ld)", + r->start, + r->start + r->len * r->step); + + else + rtn = PyString_FromFormat("xrange(%ld, %ld, %ld)", + r->start, + r->start + r->len * r->step, + r->step); + return rtn; +} + +static PySequenceMethods range_as_sequence = { + (inquiry)range_length, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + (intargfunc)range_item, /* sq_item */ + 0, /* sq_slice */ +}; + +static PyObject * range_iter(PyObject *seq); + +PyTypeObject PyRange_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* Number of items for varobject */ + "xrange", /* Name of this type */ + sizeof(rangeobject), /* Basic object size */ + 0, /* Item size for varobject */ + (destructor)PyObject_Del, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)range_repr, /* tp_repr */ + 0, /* tp_as_number */ + &range_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + range_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)range_iter, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + range_new, /* tp_new */ +}; + +/*********************** Xrange Iterator **************************/ + +typedef struct { + PyObject_HEAD + long index; + long start; + long step; + long len; +} rangeiterobject; + +static PyTypeObject Pyrangeiter_Type; + +static PyObject * +range_iter(PyObject *seq) +{ + rangeiterobject *it; + + if (!PyRange_Check(seq)) { + PyErr_BadInternalCall(); + return NULL; + } + it = PyObject_New(rangeiterobject, &Pyrangeiter_Type); + if (it == NULL) + return NULL; + it->index = 0; + it->start = ((rangeobject *)seq)->start; + it->step = ((rangeobject *)seq)->step; + it->len = ((rangeobject *)seq)->len; + return (PyObject *)it; +} + +static PyObject * +rangeiter_next(rangeiterobject *r) +{ + if (r->index < r->len) + return PyInt_FromLong(r->start + (r->index++) * r->step); + return NULL; +} + +static PyTypeObject Pyrangeiter_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "rangeiterator", /* tp_name */ + sizeof(rangeiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)PyObject_Del, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)rangeiter_next, /* tp_iternext */ + 0, /* tp_methods */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/sliceobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/sliceobject.c new file mode 100644 index 00000000..90367b0d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/sliceobject.c @@ -0,0 +1,328 @@ +/* +Written by Jim Hugunin and Chris Chase. + +This includes both the singular ellipsis object and slice objects. + +Guido, feel free to do whatever you want in the way of copyrights +for this file. +*/ + +/* +Py_Ellipsis encodes the '...' rubber index token. It is similar to +the Py_NoneStruct in that there is no way to create other objects of +this type and there is exactly one in existence. +*/ + +#include "Python.h" +#include "structmember.h" + +static PyObject * +ellipsis_repr(PyObject *op) +{ + return PyString_FromString("Ellipsis"); +} + +static PyTypeObject PyEllipsis_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "ellipsis", /* tp_name */ + 0, /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /*never called*/ /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)ellipsis_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ +}; + +PyObject _Py_EllipsisObject = { + PyObject_HEAD_INIT(&PyEllipsis_Type) +}; + + +/* Slice object implementation + + start, stop, and step are python objects with None indicating no + index is present. +*/ + +PyObject * +PySlice_New(PyObject *start, PyObject *stop, PyObject *step) +{ + PySliceObject *obj = PyObject_New(PySliceObject, &PySlice_Type); + + if (obj == NULL) + return NULL; + + if (step == NULL) step = Py_None; + Py_INCREF(step); + if (start == NULL) start = Py_None; + Py_INCREF(start); + if (stop == NULL) stop = Py_None; + Py_INCREF(stop); + + obj->step = step; + obj->start = start; + obj->stop = stop; + + return (PyObject *) obj; +} + +int +PySlice_GetIndices(PySliceObject *r, int length, + int *start, int *stop, int *step) +{ + if (r->step == Py_None) { + *step = 1; + } else { + if (!PyInt_Check(r->step)) return -1; + *step = PyInt_AsLong(r->step); + } + if (r->start == Py_None) { + *start = *step < 0 ? length-1 : 0; + } else { + if (!PyInt_Check(r->start)) return -1; + *start = PyInt_AsLong(r->start); + if (*start < 0) *start += length; + } + if (r->stop == Py_None) { + *stop = *step < 0 ? -1 : length; + } else { + if (!PyInt_Check(r->stop)) return -1; + *stop = PyInt_AsLong(r->stop); + if (*stop < 0) *stop += length; + } + if (*stop > length) return -1; + if (*start >= length) return -1; + if (*step == 0) return -1; + return 0; +} + +int +PySlice_GetIndicesEx(PySliceObject *r, int length, + int *start, int *stop, int *step, int *slicelength) +{ + /* this is harder to get right than you might think */ + + int defstart, defstop; + + if (r->step == Py_None) { + *step = 1; + } + else { + if (!_PyEval_SliceIndex(r->step, step)) return -1; + if (*step == 0) { + PyErr_SetString(PyExc_ValueError, + "slice step cannot be zero"); + return -1; + } + } + + defstart = *step < 0 ? length-1 : 0; + defstop = *step < 0 ? -1 : length; + + if (r->start == Py_None) { + *start = defstart; + } + else { + if (!_PyEval_SliceIndex(r->start, start)) return -1; + if (*start < 0) *start += length; + if (*start < 0) *start = (*step < 0) ? -1 : 0; + if (*start >= length) + *start = (*step < 0) ? length - 1 : length; + } + + if (r->stop == Py_None) { + *stop = defstop; + } + else { + if (!_PyEval_SliceIndex(r->stop, stop)) return -1; + if (*stop < 0) *stop += length; + if (*stop < 0) *stop = -1; + if (*stop > length) *stop = length; + } + + if ((*step < 0 && *stop >= *start) + || (*step > 0 && *start >= *stop)) { + *slicelength = 0; + } + else if (*step < 0) { + *slicelength = (*stop-*start+1)/(*step)+1; + } + else { + *slicelength = (*stop-*start-1)/(*step)+1; + } + + return 0; +} + +static PyObject * +slice_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + PyObject *start, *stop, *step; + + start = stop = step = NULL; + + if (!PyArg_UnpackTuple(args, "slice", 1, 3, &start, &stop, &step)) + return NULL; + + /* This swapping of stop and start is to maintain similarity with + range(). */ + if (stop == NULL) { + stop = start; + start = NULL; + } + return PySlice_New(start, stop, step); +} + +PyDoc_STRVAR(slice_doc, +"slice([start,] stop[, step])\n\ +\n\ +Create a slice object. This is used for extended slicing (e.g. a[0:10:2])."); + +static void +slice_dealloc(PySliceObject *r) +{ + Py_DECREF(r->step); + Py_DECREF(r->start); + Py_DECREF(r->stop); + PyObject_Del(r); +} + +static PyObject * +slice_repr(PySliceObject *r) +{ + PyObject *s, *comma; + + s = PyString_FromString("slice("); + comma = PyString_FromString(", "); + PyString_ConcatAndDel(&s, PyObject_Repr(r->start)); + PyString_Concat(&s, comma); + PyString_ConcatAndDel(&s, PyObject_Repr(r->stop)); + PyString_Concat(&s, comma); + PyString_ConcatAndDel(&s, PyObject_Repr(r->step)); + PyString_ConcatAndDel(&s, PyString_FromString(")")); + Py_DECREF(comma); + return s; +} + +static PyMemberDef slice_members[] = { + {"start", T_OBJECT, offsetof(PySliceObject, start), READONLY}, + {"stop", T_OBJECT, offsetof(PySliceObject, stop), READONLY}, + {"step", T_OBJECT, offsetof(PySliceObject, step), READONLY}, + {0} +}; + +static PyObject* +slice_indices(PySliceObject* self, PyObject* len) +{ + int ilen, start, stop, step, slicelength; + + ilen = PyInt_AsLong(len); + + if (ilen == -1 && PyErr_Occurred()) { + return NULL; + } + + if (PySlice_GetIndicesEx(self, ilen, &start, &stop, + &step, &slicelength) < 0) { + return NULL; + } + + return Py_BuildValue("(iii)", start, stop, step); +} + +PyDoc_STRVAR(slice_indices_doc, +"S.indices(len) -> (start, stop, stride)\n\ +\n\ +Assuming a sequence of length len, calculate the start and stop\n\ +indices, and the stride length of the extended slice described by\n\ +S. Out of bounds indices are clipped in a manner consistent with the\n\ +handling of normal slices."); + +static PyMethodDef slice_methods[] = { + {"indices", (PyCFunction)slice_indices, + METH_O, slice_indices_doc}, + {NULL, NULL} +}; + +static int +slice_compare(PySliceObject *v, PySliceObject *w) +{ + int result = 0; + + if (v == w) + return 0; + + if (PyObject_Cmp(v->start, w->start, &result) < 0) + return -2; + if (result != 0) + return result; + if (PyObject_Cmp(v->stop, w->stop, &result) < 0) + return -2; + if (result != 0) + return result; + if (PyObject_Cmp(v->step, w->step, &result) < 0) + return -2; + return result; +} + +static long +slice_hash(PySliceObject *v) +{ + PyErr_SetString(PyExc_TypeError, "unhashable type"); + return -1L; +} + +PyTypeObject PySlice_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* Number of items for varobject */ + "slice", /* Name of this type */ + sizeof(PySliceObject), /* Basic object size */ + 0, /* Item size for varobject */ + (destructor)slice_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)slice_compare, /* tp_compare */ + (reprfunc)slice_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)slice_hash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + slice_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + slice_methods, /* tp_methods */ + slice_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + slice_new, /* tp_new */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/stringobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/stringobject.c new file mode 100644 index 00000000..74b58154 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/stringobject.c @@ -0,0 +1,4228 @@ +/* String object implementation */ + +#include "Python.h" + +#include + +#ifdef COUNT_ALLOCS +int null_strings, one_strings; +#endif + +#if !defined(HAVE_LIMITS_H) && !defined(UCHAR_MAX) +#define UCHAR_MAX 255 +#endif + +static PyStringObject *characters[UCHAR_MAX + 1]; +static PyStringObject *nullstring; + +/* This dictionary holds all interned strings. Note that references to + strings in this dictionary are *not* counted in the string's ob_refcnt. + When the interned string reaches a refcnt of 0 the string deallocation + function will delete the reference from this dictionary. + + Another way to look at this is that to say that the actual reference + count of a string is: s->ob_refcnt + (s->ob_sstate?2:0) +*/ +static PyObject *interned; + + +/* + For both PyString_FromString() and PyString_FromStringAndSize(), the + parameter `size' denotes number of characters to allocate, not counting any + null terminating character. + + For PyString_FromString(), the parameter `str' points to a null-terminated + string containing exactly `size' bytes. + + For PyString_FromStringAndSize(), the parameter the parameter `str' is + either NULL or else points to a string containing at least `size' bytes. + For PyString_FromStringAndSize(), the string in the `str' parameter does + not have to be null-terminated. (Therefore it is safe to construct a + substring by calling `PyString_FromStringAndSize(origstring, substrlen)'.) + If `str' is NULL then PyString_FromStringAndSize() will allocate `size+1' + bytes (setting the last byte to the null terminating character) and you can + fill in the data yourself. If `str' is non-NULL then the resulting + PyString object must be treated as immutable and you must not fill in nor + alter the data yourself, since the strings may be shared. + + The PyObject member `op->ob_size', which denotes the number of "extra + items" in a variable-size object, will contain the number of bytes + allocated for string data, not counting the null terminating character. It + is therefore equal to the equal to the `size' parameter (for + PyString_FromStringAndSize()) or the length of the string in the `str' + parameter (for PyString_FromString()). +*/ +PyObject * +PyString_FromStringAndSize(const char *str, int size) +{ + register PyStringObject *op; + if (size == 0 && (op = nullstring) != NULL) { +#ifdef COUNT_ALLOCS + null_strings++; +#endif + Py_INCREF(op); + return (PyObject *)op; + } + if (size == 1 && str != NULL && + (op = characters[*str & UCHAR_MAX]) != NULL) + { +#ifdef COUNT_ALLOCS + one_strings++; +#endif + Py_INCREF(op); + return (PyObject *)op; + } + + /* Inline PyObject_NewVar */ + op = (PyStringObject *) + PyObject_MALLOC(sizeof(PyStringObject) + size * sizeof(char)); + if (op == NULL) + return PyErr_NoMemory(); + PyObject_INIT_VAR(op, &PyString_Type, size); + op->ob_shash = -1; + op->ob_sstate = SSTATE_NOT_INTERNED; + if (str != NULL) + memcpy(op->ob_sval, str, size); + op->ob_sval[size] = '\0'; + /* share short strings */ + if (size == 0) { + PyObject *t = (PyObject *)op; + PyString_InternInPlace(&t); + op = (PyStringObject *)t; + nullstring = op; + Py_INCREF(op); + } else if (size == 1 && str != NULL) { + PyObject *t = (PyObject *)op; + PyString_InternInPlace(&t); + op = (PyStringObject *)t; + characters[*str & UCHAR_MAX] = op; + Py_INCREF(op); + } + return (PyObject *) op; +} + +PyObject * +PyString_FromString(const char *str) +{ + register size_t size; + register PyStringObject *op; + + assert(str != NULL); + size = strlen(str); + if (size > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "string is too long for a Python string"); + return NULL; + } + if (size == 0 && (op = nullstring) != NULL) { +#ifdef COUNT_ALLOCS + null_strings++; +#endif + Py_INCREF(op); + return (PyObject *)op; + } + if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) { +#ifdef COUNT_ALLOCS + one_strings++; +#endif + Py_INCREF(op); + return (PyObject *)op; + } + + /* Inline PyObject_NewVar */ + op = (PyStringObject *) + PyObject_MALLOC(sizeof(PyStringObject) + size * sizeof(char)); + if (op == NULL) + return PyErr_NoMemory(); + PyObject_INIT_VAR(op, &PyString_Type, size); + op->ob_shash = -1; + op->ob_sstate = SSTATE_NOT_INTERNED; + memcpy(op->ob_sval, str, size+1); + /* share short strings */ + if (size == 0) { + PyObject *t = (PyObject *)op; + PyString_InternInPlace(&t); + op = (PyStringObject *)t; + nullstring = op; + Py_INCREF(op); + } else if (size == 1) { + PyObject *t = (PyObject *)op; + PyString_InternInPlace(&t); + op = (PyStringObject *)t; + characters[*str & UCHAR_MAX] = op; + Py_INCREF(op); + } + return (PyObject *) op; +} + +PyObject * +PyString_FromFormatV(const char *format, va_list vargs) +{ + va_list count; + int n = 0; + const char* f; + char *s; + PyObject* string; + +#ifdef VA_LIST_IS_ARRAY + memcpy(count, vargs, sizeof(va_list)); +#else +#ifdef __va_copy + __va_copy(count, vargs); +#else + count = vargs; +#endif +#endif + /* step 1: figure out how large a buffer we need */ + for (f = format; *f; f++) { + if (*f == '%') { + const char* p = f; + while (*++f && *f != '%' && !isalpha(Py_CHARMASK(*f))) + ; + + /* skip the 'l' in %ld, since it doesn't change the + width. although only %d is supported (see + "expand" section below), others can be easily + added */ + if (*f == 'l' && *(f+1) == 'd') + ++f; + + switch (*f) { + case 'c': + (void)va_arg(count, int); + /* fall through... */ + case '%': + n++; + break; + case 'd': case 'i': case 'x': + (void) va_arg(count, int); + /* 20 bytes is enough to hold a 64-bit + integer. Decimal takes the most space. + This isn't enough for octal. */ + n += 20; + break; + case 's': + s = va_arg(count, char*); + n += strlen(s); + break; + case 'p': + (void) va_arg(count, int); + /* maximum 64-bit pointer representation: + * 0xffffffffffffffff + * so 19 characters is enough. + * XXX I count 18 -- what's the extra for? + */ + n += 19; + break; + default: + /* if we stumble upon an unknown + formatting code, copy the rest of + the format string to the output + string. (we cannot just skip the + code, since there's no way to know + what's in the argument list) */ + n += strlen(p); + goto expand; + } + } else + n++; + } + expand: + /* step 2: fill the buffer */ + /* Since we've analyzed how much space we need for the worst case, + use sprintf directly instead of the slower PyOS_snprintf. */ + string = PyString_FromStringAndSize(NULL, n); + if (!string) + return NULL; + + s = PyString_AsString(string); + + for (f = format; *f; f++) { + if (*f == '%') { + const char* p = f++; + int i, longflag = 0; + /* parse the width.precision part (we're only + interested in the precision value, if any) */ + n = 0; + while (isdigit(Py_CHARMASK(*f))) + n = (n*10) + *f++ - '0'; + if (*f == '.') { + f++; + n = 0; + while (isdigit(Py_CHARMASK(*f))) + n = (n*10) + *f++ - '0'; + } + while (*f && *f != '%' && !isalpha(Py_CHARMASK(*f))) + f++; + /* handle the long flag, but only for %ld. others + can be added when necessary. */ + if (*f == 'l' && *(f+1) == 'd') { + longflag = 1; + ++f; + } + + switch (*f) { + case 'c': + *s++ = va_arg(vargs, int); + break; + case 'd': + if (longflag) + sprintf(s, "%ld", va_arg(vargs, long)); + else + sprintf(s, "%d", va_arg(vargs, int)); + s += strlen(s); + break; + case 'i': + sprintf(s, "%i", va_arg(vargs, int)); + s += strlen(s); + break; + case 'x': + sprintf(s, "%x", va_arg(vargs, int)); + s += strlen(s); + break; + case 's': + p = va_arg(vargs, char*); + i = strlen(p); + if (n > 0 && i > n) + i = n; + memcpy(s, p, i); + s += i; + break; + case 'p': + sprintf(s, "%p", va_arg(vargs, void*)); + /* %p is ill-defined: ensure leading 0x. */ + if (s[1] == 'X') + s[1] = 'x'; + else if (s[1] != 'x') { + memmove(s+2, s, strlen(s)+1); + s[0] = '0'; + s[1] = 'x'; + } + s += strlen(s); + break; + case '%': + *s++ = '%'; + break; + default: + strcpy(s, p); + s += strlen(s); + goto end; + } + } else + *s++ = *f; + } + + end: + _PyString_Resize(&string, s - PyString_AS_STRING(string)); + return string; +} + +PyObject * +PyString_FromFormat(const char *format, ...) +{ + PyObject* ret; + va_list vargs; + +#ifdef HAVE_STDARG_PROTOTYPES + va_start(vargs, format); +#else + va_start(vargs); +#endif + ret = PyString_FromFormatV(format, vargs); + va_end(vargs); + return ret; +} + + +PyObject *PyString_Decode(const char *s, + int size, + const char *encoding, + const char *errors) +{ + PyObject *v, *str; + + str = PyString_FromStringAndSize(s, size); + if (str == NULL) + return NULL; + v = PyString_AsDecodedString(str, encoding, errors); + Py_DECREF(str); + return v; +} + +PyObject *PyString_AsDecodedObject(PyObject *str, + const char *encoding, + const char *errors) +{ + PyObject *v; + + if (!PyString_Check(str)) { + PyErr_BadArgument(); + goto onError; + } + + if (encoding == NULL) { +#ifdef Py_USING_UNICODE + encoding = PyUnicode_GetDefaultEncoding(); +#else + PyErr_SetString(PyExc_ValueError, "no encoding specified"); + goto onError; +#endif + } + + /* Decode via the codec registry */ + v = PyCodec_Decode(str, encoding, errors); + if (v == NULL) + goto onError; + + return v; + + onError: + return NULL; +} + +PyObject *PyString_AsDecodedString(PyObject *str, + const char *encoding, + const char *errors) +{ + PyObject *v; + + v = PyString_AsDecodedObject(str, encoding, errors); + if (v == NULL) + goto onError; + +#ifdef Py_USING_UNICODE + /* Convert Unicode to a string using the default encoding */ + if (PyUnicode_Check(v)) { + PyObject *temp = v; + v = PyUnicode_AsEncodedString(v, NULL, NULL); + Py_DECREF(temp); + if (v == NULL) + goto onError; + } +#endif + if (!PyString_Check(v)) { + PyErr_Format(PyExc_TypeError, + "decoder did not return a string object (type=%.400s)", + v->ob_type->tp_name); + Py_DECREF(v); + goto onError; + } + + return v; + + onError: + return NULL; +} + +PyObject *PyString_Encode(const char *s, + int size, + const char *encoding, + const char *errors) +{ + PyObject *v, *str; + + str = PyString_FromStringAndSize(s, size); + if (str == NULL) + return NULL; + v = PyString_AsEncodedString(str, encoding, errors); + Py_DECREF(str); + return v; +} + +PyObject *PyString_AsEncodedObject(PyObject *str, + const char *encoding, + const char *errors) +{ + PyObject *v; + + if (!PyString_Check(str)) { + PyErr_BadArgument(); + goto onError; + } + + if (encoding == NULL) { +#ifdef Py_USING_UNICODE + encoding = PyUnicode_GetDefaultEncoding(); +#else + PyErr_SetString(PyExc_ValueError, "no encoding specified"); + goto onError; +#endif + } + + /* Encode via the codec registry */ + v = PyCodec_Encode(str, encoding, errors); + if (v == NULL) + goto onError; + + return v; + + onError: + return NULL; +} + +PyObject *PyString_AsEncodedString(PyObject *str, + const char *encoding, + const char *errors) +{ + PyObject *v; + + v = PyString_AsEncodedObject(str, encoding, errors); + if (v == NULL) + goto onError; + +#ifdef Py_USING_UNICODE + /* Convert Unicode to a string using the default encoding */ + if (PyUnicode_Check(v)) { + PyObject *temp = v; + v = PyUnicode_AsEncodedString(v, NULL, NULL); + Py_DECREF(temp); + if (v == NULL) + goto onError; + } +#endif + if (!PyString_Check(v)) { + PyErr_Format(PyExc_TypeError, + "encoder did not return a string object (type=%.400s)", + v->ob_type->tp_name); + Py_DECREF(v); + goto onError; + } + + return v; + + onError: + return NULL; +} + +static void +string_dealloc(PyObject *op) +{ + switch (PyString_CHECK_INTERNED(op)) { + case SSTATE_NOT_INTERNED: + break; + + case SSTATE_INTERNED_MORTAL: + /* revive dead object temporarily for DelItem */ + op->ob_refcnt = 3; + if (PyDict_DelItem(interned, op) != 0) + Py_FatalError( + "deletion of interned string failed"); + break; + + case SSTATE_INTERNED_IMMORTAL: + Py_FatalError("Immortal interned string died."); + + default: + Py_FatalError("Inconsistent interned string state."); + } + op->ob_type->tp_free(op); +} + +/* Unescape a backslash-escaped string. If unicode is non-zero, + the string is a u-literal. If recode_encoding is non-zero, + the string is UTF-8 encoded and should be re-encoded in the + specified encoding. */ + +PyObject *PyString_DecodeEscape(const char *s, + int len, + const char *errors, + int unicode, + const char *recode_encoding) +{ + int c; + char *p, *buf; + const char *end; + PyObject *v; + int newlen = recode_encoding ? 4*len:len; + v = PyString_FromStringAndSize((char *)NULL, newlen); + if (v == NULL) + return NULL; + p = buf = PyString_AsString(v); + end = s + len; + while (s < end) { + if (*s != '\\') { + non_esc: +#ifdef Py_USING_UNICODE + if (recode_encoding && (*s & 0x80)) { + PyObject *u, *w; + char *r; + const char* t; + int rn; + t = s; + /* Decode non-ASCII bytes as UTF-8. */ + while (t < end && (*t & 0x80)) t++; + u = PyUnicode_DecodeUTF8(s, t - s, errors); + if(!u) goto failed; + + /* Recode them in target encoding. */ + w = PyUnicode_AsEncodedString( + u, recode_encoding, errors); + Py_DECREF(u); + if (!w) goto failed; + + /* Append bytes to output buffer. */ + r = PyString_AsString(w); + rn = PyString_Size(w); + memcpy(p, r, rn); + p += rn; + Py_DECREF(w); + s = t; + } else { + *p++ = *s++; + } +#else + *p++ = *s++; +#endif + continue; + } + s++; + if (s==end) { + PyErr_SetString(PyExc_ValueError, + "Trailing \\ in string"); + goto failed; + } + switch (*s++) { + /* XXX This assumes ASCII! */ + case '\n': break; + case '\\': *p++ = '\\'; break; + case '\'': *p++ = '\''; break; + case '\"': *p++ = '\"'; break; + case 'b': *p++ = '\b'; break; + case 'f': *p++ = '\014'; break; /* FF */ + case 't': *p++ = '\t'; break; + case 'n': *p++ = '\n'; break; + case 'r': *p++ = '\r'; break; + case 'v': *p++ = '\013'; break; /* VT */ + case 'a': *p++ = '\007'; break; /* BEL, not classic C */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c = s[-1] - '0'; + if ('0' <= *s && *s <= '7') { + c = (c<<3) + *s++ - '0'; + if ('0' <= *s && *s <= '7') + c = (c<<3) + *s++ - '0'; + } + *p++ = c; + break; + case 'x': + if (isxdigit(Py_CHARMASK(s[0])) + && isxdigit(Py_CHARMASK(s[1]))) { + unsigned int x = 0; + c = Py_CHARMASK(*s); + s++; + if (isdigit(c)) + x = c - '0'; + else if (islower(c)) + x = 10 + c - 'a'; + else + x = 10 + c - 'A'; + x = x << 4; + c = Py_CHARMASK(*s); + s++; + if (isdigit(c)) + x += c - '0'; + else if (islower(c)) + x += 10 + c - 'a'; + else + x += 10 + c - 'A'; + *p++ = x; + break; + } + if (!errors || strcmp(errors, "strict") == 0) { + PyErr_SetString(PyExc_ValueError, + "invalid \\x escape"); + goto failed; + } + if (strcmp(errors, "replace") == 0) { + *p++ = '?'; + } else if (strcmp(errors, "ignore") == 0) + /* do nothing */; + else { + PyErr_Format(PyExc_ValueError, + "decoding error; " + "unknown error handling code: %.400s", + errors); + goto failed; + } +#ifndef Py_USING_UNICODE + case 'u': + case 'U': + case 'N': + if (unicode) { + PyErr_SetString(PyExc_ValueError, + "Unicode escapes not legal " + "when Unicode disabled"); + goto failed; + } +#endif + default: + *p++ = '\\'; + s--; + goto non_esc; /* an arbitry number of unescaped + UTF-8 bytes may follow. */ + } + } + if (p-buf < newlen) + _PyString_Resize(&v, (int)(p - buf)); + return v; + failed: + Py_DECREF(v); + return NULL; +} + +static int +string_getsize(register PyObject *op) +{ + char *s; + int len; + if (PyString_AsStringAndSize(op, &s, &len)) + return -1; + return len; +} + +static /*const*/ char * +string_getbuffer(register PyObject *op) +{ + char *s; + int len; + if (PyString_AsStringAndSize(op, &s, &len)) + return NULL; + return s; +} + +int +PyString_Size(register PyObject *op) +{ + if (!PyString_Check(op)) + return string_getsize(op); + return ((PyStringObject *)op) -> ob_size; +} + +/*const*/ char * +PyString_AsString(register PyObject *op) +{ + if (!PyString_Check(op)) + return string_getbuffer(op); + return ((PyStringObject *)op) -> ob_sval; +} + +int +PyString_AsStringAndSize(register PyObject *obj, + register char **s, + register int *len) +{ + if (s == NULL) { + PyErr_BadInternalCall(); + return -1; + } + + if (!PyString_Check(obj)) { +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(obj)) { + obj = _PyUnicode_AsDefaultEncodedString(obj, NULL); + if (obj == NULL) + return -1; + } + else +#endif + { + PyErr_Format(PyExc_TypeError, + "expected string or Unicode object, " + "%.200s found", obj->ob_type->tp_name); + return -1; + } + } + + *s = PyString_AS_STRING(obj); + if (len != NULL) + *len = PyString_GET_SIZE(obj); + else if ((int)strlen(*s) != PyString_GET_SIZE(obj)) { + PyErr_SetString(PyExc_TypeError, + "expected string without null bytes"); + return -1; + } + return 0; +} + +/* Methods */ + +static int +string_print(PyStringObject *op, FILE *fp, int flags) +{ + int i; + char c; + int quote; + + /* XXX Ought to check for interrupts when writing long strings */ + if (! PyString_CheckExact(op)) { + int ret; + /* A str subclass may have its own __str__ method. */ + op = (PyStringObject *) PyObject_Str((PyObject *)op); + if (op == NULL) + return -1; + ret = string_print(op, fp, flags); + Py_DECREF(op); + return ret; + } + if (flags & Py_PRINT_RAW) { +#ifdef __VMS + if (op->ob_size) fwrite(op->ob_sval, (int) op->ob_size, 1, fp); +#else + fwrite(op->ob_sval, 1, (int) op->ob_size, fp); +#endif + return 0; + } + + /* figure out which quote to use; single is preferred */ + quote = '\''; + if (memchr(op->ob_sval, '\'', op->ob_size) && + !memchr(op->ob_sval, '"', op->ob_size)) + quote = '"'; + + fputc(quote, fp); + for (i = 0; i < op->ob_size; i++) { + c = op->ob_sval[i]; + if (c == quote || c == '\\') + fprintf(fp, "\\%c", c); + else if (c == '\t') + fprintf(fp, "\\t"); + else if (c == '\n') + fprintf(fp, "\\n"); + else if (c == '\r') + fprintf(fp, "\\r"); + else if (c < ' ' || c >= 0x7f) + fprintf(fp, "\\x%02x", c & 0xff); + else + fputc(c, fp); + } + fputc(quote, fp); + return 0; +} + +PyObject * +PyString_Repr(PyObject *obj, int smartquotes) +{ + register PyStringObject* op = (PyStringObject*) obj; + size_t newsize = 2 + 4 * op->ob_size * sizeof(char); + PyObject *v; + if (newsize > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "string is too large to make repr"); + } + v = PyString_FromStringAndSize((char *)NULL, newsize); + if (v == NULL) { + return NULL; + } + else { + register int i; + register char c; + register char *p; + int quote; + + /* figure out which quote to use; single is preferred */ + quote = '\''; + if (smartquotes && + memchr(op->ob_sval, '\'', op->ob_size) && + !memchr(op->ob_sval, '"', op->ob_size)) + quote = '"'; + + p = PyString_AS_STRING(v); + *p++ = quote; + for (i = 0; i < op->ob_size; i++) { + /* There's at least enough room for a hex escape + and a closing quote. */ + assert(newsize - (p - PyString_AS_STRING(v)) >= 5); + c = op->ob_sval[i]; + if (c == quote || c == '\\') + *p++ = '\\', *p++ = c; + else if (c == '\t') + *p++ = '\\', *p++ = 't'; + else if (c == '\n') + *p++ = '\\', *p++ = 'n'; + else if (c == '\r') + *p++ = '\\', *p++ = 'r'; + else if (c < ' ' || c >= 0x7f) { + /* For performance, we don't want to call + PyOS_snprintf here (extra layers of + function call). */ + sprintf(p, "\\x%02x", c & 0xff); + p += 4; + } + else + *p++ = c; + } + assert(newsize - (p - PyString_AS_STRING(v)) >= 1); + *p++ = quote; + *p = '\0'; + _PyString_Resize( + &v, (int) (p - PyString_AS_STRING(v))); + return v; + } +} + +static PyObject * +string_repr(PyObject *op) +{ + return PyString_Repr(op, 1); +} + +static PyObject * +string_str(PyObject *s) +{ + assert(PyString_Check(s)); + if (PyString_CheckExact(s)) { + Py_INCREF(s); + return s; + } + else { + /* Subtype -- return genuine string with the same value. */ + PyStringObject *t = (PyStringObject *) s; + return PyString_FromStringAndSize(t->ob_sval, t->ob_size); + } +} + +static int +string_length(PyStringObject *a) +{ + return a->ob_size; +} + +static PyObject * +string_concat(register PyStringObject *a, register PyObject *bb) +{ + register unsigned int size; + register PyStringObject *op; + if (!PyString_Check(bb)) { +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(bb)) + return PyUnicode_Concat((PyObject *)a, bb); +#endif + PyErr_Format(PyExc_TypeError, + "cannot concatenate 'str' and '%.200s' objects", + bb->ob_type->tp_name); + return NULL; + } +#define b ((PyStringObject *)bb) + /* Optimize cases with empty left or right operand */ + if ((a->ob_size == 0 || b->ob_size == 0) && + PyString_CheckExact(a) && PyString_CheckExact(b)) { + if (a->ob_size == 0) { + Py_INCREF(bb); + return bb; + } + Py_INCREF(a); + return (PyObject *)a; + } + size = a->ob_size + b->ob_size; + /* Inline PyObject_NewVar */ + op = (PyStringObject *) + PyObject_MALLOC(sizeof(PyStringObject) + size * sizeof(char)); + if (op == NULL) + return PyErr_NoMemory(); + PyObject_INIT_VAR(op, &PyString_Type, size); + op->ob_shash = -1; + op->ob_sstate = SSTATE_NOT_INTERNED; + memcpy(op->ob_sval, a->ob_sval, (int) a->ob_size); + memcpy(op->ob_sval + a->ob_size, b->ob_sval, (int) b->ob_size); + op->ob_sval[size] = '\0'; + return (PyObject *) op; +#undef b +} + +static PyObject * +string_repeat(register PyStringObject *a, register int n) +{ + register int i; + register int j; + register int size; + register PyStringObject *op; + size_t nbytes; + if (n < 0) + n = 0; + /* watch out for overflows: the size can overflow int, + * and the # of bytes needed can overflow size_t + */ + size = a->ob_size * n; + if (n && size / n != a->ob_size) { + PyErr_SetString(PyExc_OverflowError, + "repeated string is too long"); + return NULL; + } + if (size == a->ob_size && PyString_CheckExact(a)) { + Py_INCREF(a); + return (PyObject *)a; + } + nbytes = size * sizeof(char); + if (nbytes / sizeof(char) != (size_t)size || + nbytes + sizeof(PyStringObject) <= nbytes) { + PyErr_SetString(PyExc_OverflowError, + "repeated string is too long"); + return NULL; + } + op = (PyStringObject *) + PyObject_MALLOC(sizeof(PyStringObject) + nbytes); + if (op == NULL) + return PyErr_NoMemory(); + PyObject_INIT_VAR(op, &PyString_Type, size); + op->ob_shash = -1; + op->ob_sstate = SSTATE_NOT_INTERNED; + op->ob_sval[size] = '\0'; + if (a->ob_size == 1 && n > 0) { + memset(op->ob_sval, a->ob_sval[0] , n); + return (PyObject *) op; + } + i = 0; + if (i < size) { + memcpy(op->ob_sval, a->ob_sval, (int) a->ob_size); + i = (int) a->ob_size; + } + while (i < size) { + j = (i <= size-i) ? i : size-i; + memcpy(op->ob_sval+i, op->ob_sval, j); + i += j; + } + return (PyObject *) op; +} + +/* String slice a[i:j] consists of characters a[i] ... a[j-1] */ + +static PyObject * +string_slice(register PyStringObject *a, register int i, register int j) + /* j -- may be negative! */ +{ + if (i < 0) + i = 0; + if (j < 0) + j = 0; /* Avoid signed/unsigned bug in next line */ + if (j > a->ob_size) + j = a->ob_size; + if (i == 0 && j == a->ob_size && PyString_CheckExact(a)) { + /* It's the same as a */ + Py_INCREF(a); + return (PyObject *)a; + } + if (j < i) + j = i; + return PyString_FromStringAndSize(a->ob_sval + i, (int) (j-i)); +} + +static int +string_contains(PyObject *a, PyObject *el) +{ + const char *lhs, *rhs, *end; + int size; + + if (!PyString_CheckExact(el)) { +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(el)) + return PyUnicode_Contains(a, el); +#endif + if (!PyString_Check(el)) { + PyErr_SetString(PyExc_TypeError, + "'in ' requires string as left operand"); + return -1; + } + } + size = PyString_GET_SIZE(el); + rhs = PyString_AS_STRING(el); + lhs = PyString_AS_STRING(a); + + /* optimize for a single character */ + if (size == 1) + return memchr(lhs, *rhs, PyString_GET_SIZE(a)) != NULL; + + end = lhs + (PyString_GET_SIZE(a) - size); + while (lhs <= end) { + if (memcmp(lhs++, rhs, size) == 0) + return 1; + } + + return 0; +} + +static PyObject * +string_item(PyStringObject *a, register int i) +{ + PyObject *v; + char *pchar; + if (i < 0 || i >= a->ob_size) { + PyErr_SetString(PyExc_IndexError, "string index out of range"); + return NULL; + } + pchar = a->ob_sval + i; + v = (PyObject *)characters[*pchar & UCHAR_MAX]; + if (v == NULL) + v = PyString_FromStringAndSize(pchar, 1); + else { +#ifdef COUNT_ALLOCS + one_strings++; +#endif + Py_INCREF(v); + } + return v; +} + +static PyObject* +string_richcompare(PyStringObject *a, PyStringObject *b, int op) +{ + int c; + int len_a, len_b; + int min_len; + PyObject *result; + + /* Make sure both arguments are strings. */ + if (!(PyString_Check(a) && PyString_Check(b))) { + result = Py_NotImplemented; + goto out; + } + if (a == b) { + switch (op) { + case Py_EQ:case Py_LE:case Py_GE: + result = Py_True; + goto out; + case Py_NE:case Py_LT:case Py_GT: + result = Py_False; + goto out; + } + } + if (op == Py_EQ) { + /* Supporting Py_NE here as well does not save + much time, since Py_NE is rarely used. */ + if (a->ob_size == b->ob_size + && (a->ob_sval[0] == b->ob_sval[0] + && memcmp(a->ob_sval, b->ob_sval, + a->ob_size) == 0)) { + result = Py_True; + } else { + result = Py_False; + } + goto out; + } + len_a = a->ob_size; len_b = b->ob_size; + min_len = (len_a < len_b) ? len_a : len_b; + if (min_len > 0) { + c = Py_CHARMASK(*a->ob_sval) - Py_CHARMASK(*b->ob_sval); + if (c==0) + c = memcmp(a->ob_sval, b->ob_sval, min_len); + }else + c = 0; + if (c == 0) + c = (len_a < len_b) ? -1 : (len_a > len_b) ? 1 : 0; + switch (op) { + case Py_LT: c = c < 0; break; + case Py_LE: c = c <= 0; break; + case Py_EQ: assert(0); break; /* unreachable */ + case Py_NE: c = c != 0; break; + case Py_GT: c = c > 0; break; + case Py_GE: c = c >= 0; break; + default: + result = Py_NotImplemented; + goto out; + } + result = c ? Py_True : Py_False; + out: + Py_INCREF(result); + return result; +} + +int +_PyString_Eq(PyObject *o1, PyObject *o2) +{ + PyStringObject *a, *b; + a = (PyStringObject*)o1; + b = (PyStringObject*)o2; + return a->ob_size == b->ob_size + && *a->ob_sval == *b->ob_sval + && memcmp(a->ob_sval, b->ob_sval, a->ob_size) == 0; +} + +static long +string_hash(PyStringObject *a) +{ + register int len; + register unsigned char *p; + register long x; + + if (a->ob_shash != -1) + return a->ob_shash; + len = a->ob_size; + p = (unsigned char *) a->ob_sval; + x = *p << 7; + while (--len >= 0) + x = (1000003*x) ^ *p++; + x ^= a->ob_size; + if (x == -1) + x = -2; + a->ob_shash = x; + return x; +} + +static PyObject* +string_subscript(PyStringObject* self, PyObject* item) +{ + if (PyInt_Check(item)) { + long i = PyInt_AS_LONG(item); + if (i < 0) + i += PyString_GET_SIZE(self); + return string_item(self,i); + } + else if (PyLong_Check(item)) { + long i = PyLong_AsLong(item); + if (i == -1 && PyErr_Occurred()) + return NULL; + if (i < 0) + i += PyString_GET_SIZE(self); + return string_item(self,i); + } + else if (PySlice_Check(item)) { + int start, stop, step, slicelength, cur, i; + char* source_buf; + char* result_buf; + PyObject* result; + + if (PySlice_GetIndicesEx((PySliceObject*)item, + PyString_GET_SIZE(self), + &start, &stop, &step, &slicelength) < 0) { + return NULL; + } + + if (slicelength <= 0) { + return PyString_FromStringAndSize("", 0); + } + else { + source_buf = PyString_AsString((PyObject*)self); + result_buf = PyMem_Malloc(slicelength); + + for (cur = start, i = 0; i < slicelength; + cur += step, i++) { + result_buf[i] = source_buf[cur]; + } + + result = PyString_FromStringAndSize(result_buf, + slicelength); + PyMem_Free(result_buf); + return result; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "string indices must be integers"); + return NULL; + } +} + +static int +string_buffer_getreadbuf(PyStringObject *self, int index, const void **ptr) +{ + if ( index != 0 ) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent string segment"); + return -1; + } + *ptr = (void *)self->ob_sval; + return self->ob_size; +} + +static int +string_buffer_getwritebuf(PyStringObject *self, int index, const void **ptr) +{ + PyErr_SetString(PyExc_TypeError, + "Cannot use string as modifiable buffer"); + return -1; +} + +static int +string_buffer_getsegcount(PyStringObject *self, int *lenp) +{ + if ( lenp ) + *lenp = self->ob_size; + return 1; +} + +static int +string_buffer_getcharbuf(PyStringObject *self, int index, const char **ptr) +{ + if ( index != 0 ) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent string segment"); + return -1; + } + *ptr = self->ob_sval; + return self->ob_size; +} + +static PySequenceMethods string_as_sequence = { + (inquiry)string_length, /*sq_length*/ + (binaryfunc)string_concat, /*sq_concat*/ + (intargfunc)string_repeat, /*sq_repeat*/ + (intargfunc)string_item, /*sq_item*/ + (intintargfunc)string_slice, /*sq_slice*/ + 0, /*sq_ass_item*/ + 0, /*sq_ass_slice*/ + (objobjproc)string_contains /*sq_contains*/ +}; + +static PyMappingMethods string_as_mapping = { + (inquiry)string_length, + (binaryfunc)string_subscript, + 0, +}; + +static PyBufferProcs string_as_buffer = { + (getreadbufferproc)string_buffer_getreadbuf, + (getwritebufferproc)string_buffer_getwritebuf, + (getsegcountproc)string_buffer_getsegcount, + (getcharbufferproc)string_buffer_getcharbuf, +}; + + + +#define LEFTSTRIP 0 +#define RIGHTSTRIP 1 +#define BOTHSTRIP 2 + +/* Arrays indexed by above */ +static const char *stripformat[] = {"|O:lstrip", "|O:rstrip", "|O:strip"}; + +#define STRIPNAME(i) (stripformat[i]+3) + + +static PyObject * +split_whitespace(const char *s, int len, int maxsplit) +{ + int i, j, err; + PyObject* item; + PyObject *list = PyList_New(0); + + if (list == NULL) + return NULL; + + for (i = j = 0; i < len; ) { + while (i < len && isspace(Py_CHARMASK(s[i]))) + i++; + j = i; + while (i < len && !isspace(Py_CHARMASK(s[i]))) + i++; + if (j < i) { + if (maxsplit-- <= 0) + break; + item = PyString_FromStringAndSize(s+j, (int)(i-j)); + if (item == NULL) + goto finally; + err = PyList_Append(list, item); + Py_DECREF(item); + if (err < 0) + goto finally; + while (i < len && isspace(Py_CHARMASK(s[i]))) + i++; + j = i; + } + } + if (j < len) { + item = PyString_FromStringAndSize(s+j, (int)(len - j)); + if (item == NULL) + goto finally; + err = PyList_Append(list, item); + Py_DECREF(item); + if (err < 0) + goto finally; + } + return list; + finally: + Py_DECREF(list); + return NULL; +} + + +PyDoc_STRVAR(split__doc__, +"S.split([sep [,maxsplit]]) -> list of strings\n\ +\n\ +Return a list of the words in the string S, using sep as the\n\ +delimiter string. If maxsplit is given, at most maxsplit\n\ +splits are done. If sep is not specified or is None, any\n\ +whitespace string is a separator."); + +static PyObject * +string_split(PyStringObject *self, PyObject *args) +{ + int len = PyString_GET_SIZE(self), n, i, j, err; + int maxsplit = -1; + const char *s = PyString_AS_STRING(self), *sub; + PyObject *list, *item, *subobj = Py_None; + + if (!PyArg_ParseTuple(args, "|Oi:split", &subobj, &maxsplit)) + return NULL; + if (maxsplit < 0) + maxsplit = INT_MAX; + if (subobj == Py_None) + return split_whitespace(s, len, maxsplit); + if (PyString_Check(subobj)) { + sub = PyString_AS_STRING(subobj); + n = PyString_GET_SIZE(subobj); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(subobj)) + return PyUnicode_Split((PyObject *)self, subobj, maxsplit); +#endif + else if (PyObject_AsCharBuffer(subobj, &sub, &n)) + return NULL; + if (n == 0) { + PyErr_SetString(PyExc_ValueError, "empty separator"); + return NULL; + } + + list = PyList_New(0); + if (list == NULL) + return NULL; + + i = j = 0; + while (i+n <= len) { + if (s[i] == sub[0] && memcmp(s+i, sub, n) == 0) { + if (maxsplit-- <= 0) + break; + item = PyString_FromStringAndSize(s+j, (int)(i-j)); + if (item == NULL) + goto fail; + err = PyList_Append(list, item); + Py_DECREF(item); + if (err < 0) + goto fail; + i = j = i + n; + } + else + i++; + } + item = PyString_FromStringAndSize(s+j, (int)(len-j)); + if (item == NULL) + goto fail; + err = PyList_Append(list, item); + Py_DECREF(item); + if (err < 0) + goto fail; + + return list; + + fail: + Py_DECREF(list); + return NULL; +} + + +PyDoc_STRVAR(join__doc__, +"S.join(sequence) -> string\n\ +\n\ +Return a string which is the concatenation of the strings in the\n\ +sequence. The separator between elements is S."); + +static PyObject * +string_join(PyStringObject *self, PyObject *orig) +{ + char *sep = PyString_AS_STRING(self); + const int seplen = PyString_GET_SIZE(self); + PyObject *res = NULL; + char *p; + int seqlen = 0; + size_t sz = 0; + int i; + PyObject *seq, *item; + + seq = PySequence_Fast(orig, ""); + if (seq == NULL) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + PyErr_Format(PyExc_TypeError, + "sequence expected, %.80s found", + orig->ob_type->tp_name); + return NULL; + } + + seqlen = PySequence_Size(seq); + if (seqlen == 0) { + Py_DECREF(seq); + return PyString_FromString(""); + } + if (seqlen == 1) { + item = PySequence_Fast_GET_ITEM(seq, 0); + if (!PyString_Check(item) && !PyUnicode_Check(item)) { + PyErr_Format(PyExc_TypeError, + "sequence item 0: expected string," + " %.80s found", + item->ob_type->tp_name); + Py_DECREF(seq); + return NULL; + } + Py_INCREF(item); + Py_DECREF(seq); + return item; + } + + /* There are at least two things to join. Do a pre-pass to figure out + * the total amount of space we'll need (sz), see whether any argument + * is absurd, and defer to the Unicode join if appropriate. + */ + for (i = 0; i < seqlen; i++) { + const size_t old_sz = sz; + item = PySequence_Fast_GET_ITEM(seq, i); + if (!PyString_Check(item)){ +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(item)) { + /* Defer to Unicode join. + * CAUTION: There's no gurantee that the + * original sequence can be iterated over + * again, so we must pass seq here. + */ + PyObject *result; + result = PyUnicode_Join((PyObject *)self, seq); + Py_DECREF(seq); + return result; + } +#endif + PyErr_Format(PyExc_TypeError, + "sequence item %i: expected string," + " %.80s found", + i, item->ob_type->tp_name); + Py_DECREF(seq); + return NULL; + } + sz += PyString_GET_SIZE(item); + if (i != 0) + sz += seplen; + if (sz < old_sz || sz > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "join() is too long for a Python string"); + Py_DECREF(seq); + return NULL; + } + } + + /* Allocate result space. */ + res = PyString_FromStringAndSize((char*)NULL, (int)sz); + if (res == NULL) { + Py_DECREF(seq); + return NULL; + } + + /* Catenate everything. */ + p = PyString_AS_STRING(res); + for (i = 0; i < seqlen; ++i) { + size_t n; + item = PySequence_Fast_GET_ITEM(seq, i); + n = PyString_GET_SIZE(item); + memcpy(p, PyString_AS_STRING(item), n); + p += n; + if (i < seqlen - 1) { + memcpy(p, sep, seplen); + p += seplen; + } + } + + Py_DECREF(seq); + return res; +} + +PyObject * +_PyString_Join(PyObject *sep, PyObject *x) +{ + assert(sep != NULL && PyString_Check(sep)); + assert(x != NULL); + return string_join((PyStringObject *)sep, x); +} + +static void +string_adjust_indices(int *start, int *end, int len) +{ + if (*end > len) + *end = len; + else if (*end < 0) + *end += len; + if (*end < 0) + *end = 0; + if (*start < 0) + *start += len; + if (*start < 0) + *start = 0; +} + +static long +string_find_internal(PyStringObject *self, PyObject *args, int dir) +{ + const char *s = PyString_AS_STRING(self), *sub; + int len = PyString_GET_SIZE(self); + int n, i = 0, last = INT_MAX; + PyObject *subobj; + + if (!PyArg_ParseTuple(args, "O|O&O&:find/rfind/index/rindex", + &subobj, _PyEval_SliceIndex, &i, _PyEval_SliceIndex, &last)) + return -2; + if (PyString_Check(subobj)) { + sub = PyString_AS_STRING(subobj); + n = PyString_GET_SIZE(subobj); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(subobj)) + return PyUnicode_Find((PyObject *)self, subobj, i, last, dir); +#endif + else if (PyObject_AsCharBuffer(subobj, &sub, &n)) + return -2; + + string_adjust_indices(&i, &last, len); + + if (dir > 0) { + if (n == 0 && i <= last) + return (long)i; + last -= n; + for (; i <= last; ++i) + if (s[i] == sub[0] && memcmp(&s[i], sub, n) == 0) + return (long)i; + } + else { + int j; + + if (n == 0 && i <= last) + return (long)last; + for (j = last-n; j >= i; --j) + if (s[j] == sub[0] && memcmp(&s[j], sub, n) == 0) + return (long)j; + } + + return -1; +} + + +PyDoc_STRVAR(find__doc__, +"S.find(sub [,start [,end]]) -> int\n\ +\n\ +Return the lowest index in S where substring sub is found,\n\ +such that sub is contained within s[start,end]. Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Return -1 on failure."); + +static PyObject * +string_find(PyStringObject *self, PyObject *args) +{ + long result = string_find_internal(self, args, +1); + if (result == -2) + return NULL; + return PyInt_FromLong(result); +} + + +PyDoc_STRVAR(index__doc__, +"S.index(sub [,start [,end]]) -> int\n\ +\n\ +Like S.find() but raise ValueError when the substring is not found."); + +static PyObject * +string_index(PyStringObject *self, PyObject *args) +{ + long result = string_find_internal(self, args, +1); + if (result == -2) + return NULL; + if (result == -1) { + PyErr_SetString(PyExc_ValueError, + "substring not found"); + return NULL; + } + return PyInt_FromLong(result); +} + + +PyDoc_STRVAR(rfind__doc__, +"S.rfind(sub [,start [,end]]) -> int\n\ +\n\ +Return the highest index in S where substring sub is found,\n\ +such that sub is contained within s[start,end]. Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Return -1 on failure."); + +static PyObject * +string_rfind(PyStringObject *self, PyObject *args) +{ + long result = string_find_internal(self, args, -1); + if (result == -2) + return NULL; + return PyInt_FromLong(result); +} + + +PyDoc_STRVAR(rindex__doc__, +"S.rindex(sub [,start [,end]]) -> int\n\ +\n\ +Like S.rfind() but raise ValueError when the substring is not found."); + +static PyObject * +string_rindex(PyStringObject *self, PyObject *args) +{ + long result = string_find_internal(self, args, -1); + if (result == -2) + return NULL; + if (result == -1) { + PyErr_SetString(PyExc_ValueError, + "substring not found"); + return NULL; + } + return PyInt_FromLong(result); +} + + +static PyObject * +do_xstrip(PyStringObject *self, int striptype, PyObject *sepobj) +{ + char *s = PyString_AS_STRING(self); + int len = PyString_GET_SIZE(self); + char *sep = PyString_AS_STRING(sepobj); + int seplen = PyString_GET_SIZE(sepobj); + int i, j; + + i = 0; + if (striptype != RIGHTSTRIP) { + while (i < len && memchr(sep, Py_CHARMASK(s[i]), seplen)) { + i++; + } + } + + j = len; + if (striptype != LEFTSTRIP) { + do { + j--; + } while (j >= i && memchr(sep, Py_CHARMASK(s[j]), seplen)); + j++; + } + + if (i == 0 && j == len && PyString_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*)self; + } + else + return PyString_FromStringAndSize(s+i, j-i); +} + + +static PyObject * +do_strip(PyStringObject *self, int striptype) +{ + char *s = PyString_AS_STRING(self); + int len = PyString_GET_SIZE(self), i, j; + + i = 0; + if (striptype != RIGHTSTRIP) { + while (i < len && isspace(Py_CHARMASK(s[i]))) { + i++; + } + } + + j = len; + if (striptype != LEFTSTRIP) { + do { + j--; + } while (j >= i && isspace(Py_CHARMASK(s[j]))); + j++; + } + + if (i == 0 && j == len && PyString_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*)self; + } + else + return PyString_FromStringAndSize(s+i, j-i); +} + + +static PyObject * +do_argstrip(PyStringObject *self, int striptype, PyObject *args) +{ + PyObject *sep = NULL; + + if (!PyArg_ParseTuple(args, (char *)stripformat[striptype], &sep)) + return NULL; + + if (sep != NULL && sep != Py_None) { + if (PyString_Check(sep)) + return do_xstrip(self, striptype, sep); +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(sep)) { + PyObject *uniself = PyUnicode_FromObject((PyObject *)self); + PyObject *res; + if (uniself==NULL) + return NULL; + res = _PyUnicode_XStrip((PyUnicodeObject *)uniself, + striptype, sep); + Py_DECREF(uniself); + return res; + } +#endif + else { + PyErr_Format(PyExc_TypeError, +#ifdef Py_USING_UNICODE + "%s arg must be None, str or unicode", +#else + "%s arg must be None or str", +#endif + STRIPNAME(striptype)); + return NULL; + } + return do_xstrip(self, striptype, sep); + } + + return do_strip(self, striptype); +} + + +PyDoc_STRVAR(strip__doc__, +"S.strip([chars]) -> string or unicode\n\ +\n\ +Return a copy of the string S with leading and trailing\n\ +whitespace removed.\n\ +If chars is given and not None, remove characters in chars instead.\n\ +If chars is unicode, S will be converted to unicode before stripping"); + +static PyObject * +string_strip(PyStringObject *self, PyObject *args) +{ + if (PyTuple_GET_SIZE(args) == 0) + return do_strip(self, BOTHSTRIP); /* Common case */ + else + return do_argstrip(self, BOTHSTRIP, args); +} + + +PyDoc_STRVAR(lstrip__doc__, +"S.lstrip([chars]) -> string or unicode\n\ +\n\ +Return a copy of the string S with leading whitespace removed.\n\ +If chars is given and not None, remove characters in chars instead.\n\ +If chars is unicode, S will be converted to unicode before stripping"); + +static PyObject * +string_lstrip(PyStringObject *self, PyObject *args) +{ + if (PyTuple_GET_SIZE(args) == 0) + return do_strip(self, LEFTSTRIP); /* Common case */ + else + return do_argstrip(self, LEFTSTRIP, args); +} + + +PyDoc_STRVAR(rstrip__doc__, +"S.rstrip([chars]) -> string or unicode\n\ +\n\ +Return a copy of the string S with trailing whitespace removed.\n\ +If chars is given and not None, remove characters in chars instead.\n\ +If chars is unicode, S will be converted to unicode before stripping"); + +static PyObject * +string_rstrip(PyStringObject *self, PyObject *args) +{ + if (PyTuple_GET_SIZE(args) == 0) + return do_strip(self, RIGHTSTRIP); /* Common case */ + else + return do_argstrip(self, RIGHTSTRIP, args); +} + + +PyDoc_STRVAR(lower__doc__, +"S.lower() -> string\n\ +\n\ +Return a copy of the string S converted to lowercase."); + +static PyObject * +string_lower(PyStringObject *self) +{ + char *s = PyString_AS_STRING(self), *s_new; + int i, n = PyString_GET_SIZE(self); + PyObject *new; + + new = PyString_FromStringAndSize(NULL, n); + if (new == NULL) + return NULL; + s_new = PyString_AsString(new); + for (i = 0; i < n; i++) { + int c = Py_CHARMASK(*s++); + if (isupper(c)) { + *s_new = tolower(c); + } else + *s_new = c; + s_new++; + } + return new; +} + + +PyDoc_STRVAR(upper__doc__, +"S.upper() -> string\n\ +\n\ +Return a copy of the string S converted to uppercase."); + +static PyObject * +string_upper(PyStringObject *self) +{ + char *s = PyString_AS_STRING(self), *s_new; + int i, n = PyString_GET_SIZE(self); + PyObject *new; + + new = PyString_FromStringAndSize(NULL, n); + if (new == NULL) + return NULL; + s_new = PyString_AsString(new); + for (i = 0; i < n; i++) { + int c = Py_CHARMASK(*s++); + if (islower(c)) { + *s_new = toupper(c); + } else + *s_new = c; + s_new++; + } + return new; +} + + +PyDoc_STRVAR(title__doc__, +"S.title() -> string\n\ +\n\ +Return a titlecased version of S, i.e. words start with uppercase\n\ +characters, all remaining cased characters have lowercase."); + +static PyObject* +string_title(PyStringObject *self) +{ + char *s = PyString_AS_STRING(self), *s_new; + int i, n = PyString_GET_SIZE(self); + int previous_is_cased = 0; + PyObject *new; + + new = PyString_FromStringAndSize(NULL, n); + if (new == NULL) + return NULL; + s_new = PyString_AsString(new); + for (i = 0; i < n; i++) { + int c = Py_CHARMASK(*s++); + if (islower(c)) { + if (!previous_is_cased) + c = toupper(c); + previous_is_cased = 1; + } else if (isupper(c)) { + if (previous_is_cased) + c = tolower(c); + previous_is_cased = 1; + } else + previous_is_cased = 0; + *s_new++ = c; + } + return new; +} + +PyDoc_STRVAR(capitalize__doc__, +"S.capitalize() -> string\n\ +\n\ +Return a copy of the string S with only its first character\n\ +capitalized."); + +static PyObject * +string_capitalize(PyStringObject *self) +{ + char *s = PyString_AS_STRING(self), *s_new; + int i, n = PyString_GET_SIZE(self); + PyObject *new; + + new = PyString_FromStringAndSize(NULL, n); + if (new == NULL) + return NULL; + s_new = PyString_AsString(new); + if (0 < n) { + int c = Py_CHARMASK(*s++); + if (islower(c)) + *s_new = toupper(c); + else + *s_new = c; + s_new++; + } + for (i = 1; i < n; i++) { + int c = Py_CHARMASK(*s++); + if (isupper(c)) + *s_new = tolower(c); + else + *s_new = c; + s_new++; + } + return new; +} + + +PyDoc_STRVAR(count__doc__, +"S.count(sub[, start[, end]]) -> int\n\ +\n\ +Return the number of occurrences of substring sub in string\n\ +S[start:end]. Optional arguments start and end are\n\ +interpreted as in slice notation."); + +static PyObject * +string_count(PyStringObject *self, PyObject *args) +{ + const char *s = PyString_AS_STRING(self), *sub; + int len = PyString_GET_SIZE(self), n; + int i = 0, last = INT_MAX; + int m, r; + PyObject *subobj; + + if (!PyArg_ParseTuple(args, "O|O&O&:count", &subobj, + _PyEval_SliceIndex, &i, _PyEval_SliceIndex, &last)) + return NULL; + + if (PyString_Check(subobj)) { + sub = PyString_AS_STRING(subobj); + n = PyString_GET_SIZE(subobj); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(subobj)) { + int count; + count = PyUnicode_Count((PyObject *)self, subobj, i, last); + if (count == -1) + return NULL; + else + return PyInt_FromLong((long) count); + } +#endif + else if (PyObject_AsCharBuffer(subobj, &sub, &n)) + return NULL; + + string_adjust_indices(&i, &last, len); + + m = last + 1 - n; + if (n == 0) + return PyInt_FromLong((long) (m-i)); + + r = 0; + while (i < m) { + if (!memcmp(s+i, sub, n)) { + r++; + i += n; + } else { + i++; + } + } + return PyInt_FromLong((long) r); +} + + +PyDoc_STRVAR(swapcase__doc__, +"S.swapcase() -> string\n\ +\n\ +Return a copy of the string S with uppercase characters\n\ +converted to lowercase and vice versa."); + +static PyObject * +string_swapcase(PyStringObject *self) +{ + char *s = PyString_AS_STRING(self), *s_new; + int i, n = PyString_GET_SIZE(self); + PyObject *new; + + new = PyString_FromStringAndSize(NULL, n); + if (new == NULL) + return NULL; + s_new = PyString_AsString(new); + for (i = 0; i < n; i++) { + int c = Py_CHARMASK(*s++); + if (islower(c)) { + *s_new = toupper(c); + } + else if (isupper(c)) { + *s_new = tolower(c); + } + else + *s_new = c; + s_new++; + } + return new; +} + + +PyDoc_STRVAR(translate__doc__, +"S.translate(table [,deletechars]) -> string\n\ +\n\ +Return a copy of the string S, where all characters occurring\n\ +in the optional argument deletechars are removed, and the\n\ +remaining characters have been mapped through the given\n\ +translation table, which must be a string of length 256."); + +static PyObject * +string_translate(PyStringObject *self, PyObject *args) +{ + register char *input, *output; + register const char *table; + register int i, c, changed = 0; + PyObject *input_obj = (PyObject*)self; + const char *table1, *output_start, *del_table=NULL; + int inlen, tablen, dellen = 0; + PyObject *result; + int trans_table[256]; + PyObject *tableobj, *delobj = NULL; + + if (!PyArg_UnpackTuple(args, "translate", 1, 2, + &tableobj, &delobj)) + return NULL; + + if (PyString_Check(tableobj)) { + table1 = PyString_AS_STRING(tableobj); + tablen = PyString_GET_SIZE(tableobj); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(tableobj)) { + /* Unicode .translate() does not support the deletechars + parameter; instead a mapping to None will cause characters + to be deleted. */ + if (delobj != NULL) { + PyErr_SetString(PyExc_TypeError, + "deletions are implemented differently for unicode"); + return NULL; + } + return PyUnicode_Translate((PyObject *)self, tableobj, NULL); + } +#endif + else if (PyObject_AsCharBuffer(tableobj, &table1, &tablen)) + return NULL; + + if (tablen != 256) { + PyErr_SetString(PyExc_ValueError, + "translation table must be 256 characters long"); + return NULL; + } + + if (delobj != NULL) { + if (PyString_Check(delobj)) { + del_table = PyString_AS_STRING(delobj); + dellen = PyString_GET_SIZE(delobj); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(delobj)) { + PyErr_SetString(PyExc_TypeError, + "deletions are implemented differently for unicode"); + return NULL; + } +#endif + else if (PyObject_AsCharBuffer(delobj, &del_table, &dellen)) + return NULL; + } + else { + del_table = NULL; + dellen = 0; + } + + table = table1; + inlen = PyString_Size(input_obj); + result = PyString_FromStringAndSize((char *)NULL, inlen); + if (result == NULL) + return NULL; + output_start = output = PyString_AsString(result); + input = PyString_AsString(input_obj); + + if (dellen == 0) { + /* If no deletions are required, use faster code */ + for (i = inlen; --i >= 0; ) { + c = Py_CHARMASK(*input++); + if (Py_CHARMASK((*output++ = table[c])) != c) + changed = 1; + } + if (changed || !PyString_CheckExact(input_obj)) + return result; + Py_DECREF(result); + Py_INCREF(input_obj); + return input_obj; + } + + for (i = 0; i < 256; i++) + trans_table[i] = Py_CHARMASK(table[i]); + + for (i = 0; i < dellen; i++) + trans_table[(int) Py_CHARMASK(del_table[i])] = -1; + + for (i = inlen; --i >= 0; ) { + c = Py_CHARMASK(*input++); + if (trans_table[c] != -1) + if (Py_CHARMASK(*output++ = (char)trans_table[c]) == c) + continue; + changed = 1; + } + if (!changed && PyString_CheckExact(input_obj)) { + Py_DECREF(result); + Py_INCREF(input_obj); + return input_obj; + } + /* Fix the size of the resulting string */ + if (inlen > 0) + _PyString_Resize(&result, output - output_start); + return result; +} + + +/* What follows is used for implementing replace(). Perry Stoll. */ + +/* + mymemfind + + strstr replacement for arbitrary blocks of memory. + + Locates the first occurrence in the memory pointed to by MEM of the + contents of memory pointed to by PAT. Returns the index into MEM if + found, or -1 if not found. If len of PAT is greater than length of + MEM, the function returns -1. +*/ +static int +mymemfind(const char *mem, int len, const char *pat, int pat_len) +{ + register int ii; + + /* pattern can not occur in the last pat_len-1 chars */ + len -= pat_len; + + for (ii = 0; ii <= len; ii++) { + if (mem[ii] == pat[0] && memcmp(&mem[ii], pat, pat_len) == 0) { + return ii; + } + } + return -1; +} + +/* + mymemcnt + + Return the number of distinct times PAT is found in MEM. + meaning mem=1111 and pat==11 returns 2. + mem=11111 and pat==11 also return 2. + */ +static int +mymemcnt(const char *mem, int len, const char *pat, int pat_len) +{ + register int offset = 0; + int nfound = 0; + + while (len >= 0) { + offset = mymemfind(mem, len, pat, pat_len); + if (offset == -1) + break; + mem += offset + pat_len; + len -= offset + pat_len; + nfound++; + } + return nfound; +} + +/* + mymemreplace + + Return a string in which all occurrences of PAT in memory STR are + replaced with SUB. + + If length of PAT is less than length of STR or there are no occurrences + of PAT in STR, then the original string is returned. Otherwise, a new + string is allocated here and returned. + + on return, out_len is: + the length of output string, or + -1 if the input string is returned, or + unchanged if an error occurs (no memory). + + return value is: + the new string allocated locally, or + NULL if an error occurred. +*/ +static char * +mymemreplace(const char *str, int len, /* input string */ + const char *pat, int pat_len, /* pattern string to find */ + const char *sub, int sub_len, /* substitution string */ + int count, /* number of replacements */ + int *out_len) +{ + char *out_s; + char *new_s; + int nfound, offset, new_len; + + if (len == 0 || (pat_len == 0 && sub_len == 0) || pat_len > len) + goto return_same; + + /* find length of output string */ + nfound = (pat_len > 0) ? mymemcnt(str, len, pat, pat_len) : len + 1; + if (count < 0) + count = INT_MAX; + else if (nfound > count) + nfound = count; + if (nfound == 0) + goto return_same; + + new_len = len + nfound*(sub_len - pat_len); + if (new_len == 0) { + /* Have to allocate something for the caller to free(). */ + out_s = (char *)PyMem_MALLOC(1); + if (out_s == NULL) + return NULL; + out_s[0] = '\0'; + } + else { + assert(new_len > 0); + new_s = (char *)PyMem_MALLOC(new_len); + if (new_s == NULL) + return NULL; + out_s = new_s; + + if (pat_len > 0) { + for (; nfound > 0; --nfound) { + /* find index of next instance of pattern */ + offset = mymemfind(str, len, pat, pat_len); + if (offset == -1) + break; + + /* copy non matching part of input string */ + memcpy(new_s, str, offset); + str += offset + pat_len; + len -= offset + pat_len; + + /* copy substitute into the output string */ + new_s += offset; + memcpy(new_s, sub, sub_len); + new_s += sub_len; + } + /* copy any remaining values into output string */ + if (len > 0) + memcpy(new_s, str, len); + } + else { + for (;;++str, --len) { + memcpy(new_s, sub, sub_len); + new_s += sub_len; + if (--nfound <= 0) { + memcpy(new_s, str, len); + break; + } + *new_s++ = *str; + } + } + } + *out_len = new_len; + return out_s; + + return_same: + *out_len = -1; + return (char *)str; /* cast away const */ +} + + +PyDoc_STRVAR(replace__doc__, +"S.replace (old, new[, count]) -> string\n\ +\n\ +Return a copy of string S with all occurrences of substring\n\ +old replaced by new. If the optional argument count is\n\ +given, only the first count occurrences are replaced."); + +static PyObject * +string_replace(PyStringObject *self, PyObject *args) +{ + const char *str = PyString_AS_STRING(self), *sub, *repl; + char *new_s; + const int len = PyString_GET_SIZE(self); + int sub_len, repl_len, out_len; + int count = -1; + PyObject *new; + PyObject *subobj, *replobj; + + if (!PyArg_ParseTuple(args, "OO|i:replace", + &subobj, &replobj, &count)) + return NULL; + + if (PyString_Check(subobj)) { + sub = PyString_AS_STRING(subobj); + sub_len = PyString_GET_SIZE(subobj); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(subobj)) + return PyUnicode_Replace((PyObject *)self, + subobj, replobj, count); +#endif + else if (PyObject_AsCharBuffer(subobj, &sub, &sub_len)) + return NULL; + + if (PyString_Check(replobj)) { + repl = PyString_AS_STRING(replobj); + repl_len = PyString_GET_SIZE(replobj); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(replobj)) + return PyUnicode_Replace((PyObject *)self, + subobj, replobj, count); +#endif + else if (PyObject_AsCharBuffer(replobj, &repl, &repl_len)) + return NULL; + + new_s = mymemreplace(str,len,sub,sub_len,repl,repl_len,count,&out_len); + if (new_s == NULL) { + PyErr_NoMemory(); + return NULL; + } + if (out_len == -1) { + if (PyString_CheckExact(self)) { + /* we're returning another reference to self */ + new = (PyObject*)self; + Py_INCREF(new); + } + else { + new = PyString_FromStringAndSize(str, len); + if (new == NULL) + return NULL; + } + } + else { + new = PyString_FromStringAndSize(new_s, out_len); + PyMem_FREE(new_s); + } + return new; +} + + +PyDoc_STRVAR(startswith__doc__, +"S.startswith(prefix[, start[, end]]) -> bool\n\ +\n\ +Return True if S starts with the specified prefix, False otherwise.\n\ +With optional start, test S beginning at that position.\n\ +With optional end, stop comparing S at that position."); + +static PyObject * +string_startswith(PyStringObject *self, PyObject *args) +{ + const char* str = PyString_AS_STRING(self); + int len = PyString_GET_SIZE(self); + const char* prefix; + int plen; + int start = 0; + int end = INT_MAX; + PyObject *subobj; + + if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &subobj, + _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) + return NULL; + if (PyString_Check(subobj)) { + prefix = PyString_AS_STRING(subobj); + plen = PyString_GET_SIZE(subobj); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(subobj)) { + int rc; + rc = PyUnicode_Tailmatch((PyObject *)self, + subobj, start, end, -1); + if (rc == -1) + return NULL; + else + return PyBool_FromLong((long) rc); + } +#endif + else if (PyObject_AsCharBuffer(subobj, &prefix, &plen)) + return NULL; + + string_adjust_indices(&start, &end, len); + + if (start+plen > len) + return PyBool_FromLong(0); + + if (end-start >= plen) + return PyBool_FromLong(!memcmp(str+start, prefix, plen)); + else + return PyBool_FromLong(0); +} + + +PyDoc_STRVAR(endswith__doc__, +"S.endswith(suffix[, start[, end]]) -> bool\n\ +\n\ +Return True if S ends with the specified suffix, False otherwise.\n\ +With optional start, test S beginning at that position.\n\ +With optional end, stop comparing S at that position."); + +static PyObject * +string_endswith(PyStringObject *self, PyObject *args) +{ + const char* str = PyString_AS_STRING(self); + int len = PyString_GET_SIZE(self); + const char* suffix; + int slen; + int start = 0; + int end = INT_MAX; + PyObject *subobj; + + if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &subobj, + _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) + return NULL; + if (PyString_Check(subobj)) { + suffix = PyString_AS_STRING(subobj); + slen = PyString_GET_SIZE(subobj); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(subobj)) { + int rc; + rc = PyUnicode_Tailmatch((PyObject *)self, + subobj, start, end, +1); + if (rc == -1) + return NULL; + else + return PyBool_FromLong((long) rc); + } +#endif + else if (PyObject_AsCharBuffer(subobj, &suffix, &slen)) + return NULL; + + string_adjust_indices(&start, &end, len); + + if (end-start < slen || start > len) + return PyBool_FromLong(0); + + if (end-slen > start) + start = end - slen; + if (end-start >= slen) + return PyBool_FromLong(!memcmp(str+start, suffix, slen)); + else + return PyBool_FromLong(0); +} + + +PyDoc_STRVAR(encode__doc__, +"S.encode([encoding[,errors]]) -> object\n\ +\n\ +Encodes S using the codec registered for encoding. encoding defaults\n\ +to the default encoding. errors may be given to set a different error\n\ +handling scheme. Default is 'strict' meaning that encoding errors raise\n\ +a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and\n\ +'xmlcharrefreplace' as well as any other name registered with\n\ +codecs.register_error that is able to handle UnicodeEncodeErrors."); + +static PyObject * +string_encode(PyStringObject *self, PyObject *args) +{ + char *encoding = NULL; + char *errors = NULL; + if (!PyArg_ParseTuple(args, "|ss:encode", &encoding, &errors)) + return NULL; + return PyString_AsEncodedObject((PyObject *)self, encoding, errors); +} + + +PyDoc_STRVAR(decode__doc__, +"S.decode([encoding[,errors]]) -> object\n\ +\n\ +Decodes S using the codec registered for encoding. encoding defaults\n\ +to the default encoding. errors may be given to set a different error\n\ +handling scheme. Default is 'strict' meaning that encoding errors raise\n\ +a UnicodeDecodeError. Other possible values are 'ignore' and 'replace'\n\ +as well as any other name registerd with codecs.register_error that is\n\ +able to handle UnicodeDecodeErrors."); + +static PyObject * +string_decode(PyStringObject *self, PyObject *args) +{ + char *encoding = NULL; + char *errors = NULL; + if (!PyArg_ParseTuple(args, "|ss:decode", &encoding, &errors)) + return NULL; + return PyString_AsDecodedObject((PyObject *)self, encoding, errors); +} + + +PyDoc_STRVAR(expandtabs__doc__, +"S.expandtabs([tabsize]) -> string\n\ +\n\ +Return a copy of S where all tab characters are expanded using spaces.\n\ +If tabsize is not given, a tab size of 8 characters is assumed."); + +static PyObject* +string_expandtabs(PyStringObject *self, PyObject *args) +{ + const char *e, *p; + char *q; + int i, j; + PyObject *u; + int tabsize = 8; + + if (!PyArg_ParseTuple(args, "|i:expandtabs", &tabsize)) + return NULL; + + /* First pass: determine size of output string */ + i = j = 0; + e = PyString_AS_STRING(self) + PyString_GET_SIZE(self); + for (p = PyString_AS_STRING(self); p < e; p++) + if (*p == '\t') { + if (tabsize > 0) + j += tabsize - (j % tabsize); + } + else { + j++; + if (*p == '\n' || *p == '\r') { + i += j; + j = 0; + } + } + + /* Second pass: create output string and fill it */ + u = PyString_FromStringAndSize(NULL, i + j); + if (!u) + return NULL; + + j = 0; + q = PyString_AS_STRING(u); + + for (p = PyString_AS_STRING(self); p < e; p++) + if (*p == '\t') { + if (tabsize > 0) { + i = tabsize - (j % tabsize); + j += i; + while (i--) + *q++ = ' '; + } + } + else { + j++; + *q++ = *p; + if (*p == '\n' || *p == '\r') + j = 0; + } + + return u; +} + +static PyObject * +pad(PyStringObject *self, int left, int right, char fill) +{ + PyObject *u; + + if (left < 0) + left = 0; + if (right < 0) + right = 0; + + if (left == 0 && right == 0 && PyString_CheckExact(self)) { + Py_INCREF(self); + return (PyObject *)self; + } + + u = PyString_FromStringAndSize(NULL, + left + PyString_GET_SIZE(self) + right); + if (u) { + if (left) + memset(PyString_AS_STRING(u), fill, left); + memcpy(PyString_AS_STRING(u) + left, + PyString_AS_STRING(self), + PyString_GET_SIZE(self)); + if (right) + memset(PyString_AS_STRING(u) + left + PyString_GET_SIZE(self), + fill, right); + } + + return u; +} + +PyDoc_STRVAR(ljust__doc__, +"S.ljust(width) -> string\n" +"\n" +"Return S left justified in a string of length width. Padding is\n" +"done using spaces."); + +static PyObject * +string_ljust(PyStringObject *self, PyObject *args) +{ + int width; + if (!PyArg_ParseTuple(args, "i:ljust", &width)) + return NULL; + + if (PyString_GET_SIZE(self) >= width && PyString_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*) self; + } + + return pad(self, 0, width - PyString_GET_SIZE(self), ' '); +} + + +PyDoc_STRVAR(rjust__doc__, +"S.rjust(width) -> string\n" +"\n" +"Return S right justified in a string of length width. Padding is\n" +"done using spaces."); + +static PyObject * +string_rjust(PyStringObject *self, PyObject *args) +{ + int width; + if (!PyArg_ParseTuple(args, "i:rjust", &width)) + return NULL; + + if (PyString_GET_SIZE(self) >= width && PyString_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*) self; + } + + return pad(self, width - PyString_GET_SIZE(self), 0, ' '); +} + + +PyDoc_STRVAR(center__doc__, +"S.center(width) -> string\n" +"\n" +"Return S centered in a string of length width. Padding is done\n" +"using spaces."); + +static PyObject * +string_center(PyStringObject *self, PyObject *args) +{ + int marg, left; + int width; + + if (!PyArg_ParseTuple(args, "i:center", &width)) + return NULL; + + if (PyString_GET_SIZE(self) >= width && PyString_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*) self; + } + + marg = width - PyString_GET_SIZE(self); + left = marg / 2 + (marg & width & 1); + + return pad(self, left, marg - left, ' '); +} + +PyDoc_STRVAR(zfill__doc__, +"S.zfill(width) -> string\n" +"\n" +"Pad a numeric string S with zeros on the left, to fill a field\n" +"of the specified width. The string S is never truncated."); + +static PyObject * +string_zfill(PyStringObject *self, PyObject *args) +{ + int fill; + PyObject *s; + char *p; + + int width; + if (!PyArg_ParseTuple(args, "i:zfill", &width)) + return NULL; + + if (PyString_GET_SIZE(self) >= width) { + if (PyString_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*) self; + } + else + return PyString_FromStringAndSize( + PyString_AS_STRING(self), + PyString_GET_SIZE(self) + ); + } + + fill = width - PyString_GET_SIZE(self); + + s = pad(self, fill, 0, '0'); + + if (s == NULL) + return NULL; + + p = PyString_AS_STRING(s); + if (p[fill] == '+' || p[fill] == '-') { + /* move sign to beginning of string */ + p[0] = p[fill]; + p[fill] = '0'; + } + + return (PyObject*) s; +} + +PyDoc_STRVAR(isspace__doc__, +"S.isspace() -> bool\n\ +\n\ +Return True if all characters in S are whitespace\n\ +and there is at least one character in S, False otherwise."); + +static PyObject* +string_isspace(PyStringObject *self) +{ + register const unsigned char *p + = (unsigned char *) PyString_AS_STRING(self); + register const unsigned char *e; + + /* Shortcut for single character strings */ + if (PyString_GET_SIZE(self) == 1 && + isspace(*p)) + return PyBool_FromLong(1); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyString_GET_SIZE(self); + for (; p < e; p++) { + if (!isspace(*p)) + return PyBool_FromLong(0); + } + return PyBool_FromLong(1); +} + + +PyDoc_STRVAR(isalpha__doc__, +"S.isalpha() -> bool\n\ +\n\ +Return True if all characters in S are alphabetic\n\ +and there is at least one character in S, False otherwise."); + +static PyObject* +string_isalpha(PyStringObject *self) +{ + register const unsigned char *p + = (unsigned char *) PyString_AS_STRING(self); + register const unsigned char *e; + + /* Shortcut for single character strings */ + if (PyString_GET_SIZE(self) == 1 && + isalpha(*p)) + return PyBool_FromLong(1); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyString_GET_SIZE(self); + for (; p < e; p++) { + if (!isalpha(*p)) + return PyBool_FromLong(0); + } + return PyBool_FromLong(1); +} + + +PyDoc_STRVAR(isalnum__doc__, +"S.isalnum() -> bool\n\ +\n\ +Return True if all characters in S are alphanumeric\n\ +and there is at least one character in S, False otherwise."); + +static PyObject* +string_isalnum(PyStringObject *self) +{ + register const unsigned char *p + = (unsigned char *) PyString_AS_STRING(self); + register const unsigned char *e; + + /* Shortcut for single character strings */ + if (PyString_GET_SIZE(self) == 1 && + isalnum(*p)) + return PyBool_FromLong(1); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyString_GET_SIZE(self); + for (; p < e; p++) { + if (!isalnum(*p)) + return PyBool_FromLong(0); + } + return PyBool_FromLong(1); +} + + +PyDoc_STRVAR(isdigit__doc__, +"S.isdigit() -> bool\n\ +\n\ +Return True if all characters in S are digits\n\ +and there is at least one character in S, False otherwise."); + +static PyObject* +string_isdigit(PyStringObject *self) +{ + register const unsigned char *p + = (unsigned char *) PyString_AS_STRING(self); + register const unsigned char *e; + + /* Shortcut for single character strings */ + if (PyString_GET_SIZE(self) == 1 && + isdigit(*p)) + return PyBool_FromLong(1); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyString_GET_SIZE(self); + for (; p < e; p++) { + if (!isdigit(*p)) + return PyBool_FromLong(0); + } + return PyBool_FromLong(1); +} + + +PyDoc_STRVAR(islower__doc__, +"S.islower() -> bool\n\ +\n\ +Return True if all cased characters in S are lowercase and there is\n\ +at least one cased character in S, False otherwise."); + +static PyObject* +string_islower(PyStringObject *self) +{ + register const unsigned char *p + = (unsigned char *) PyString_AS_STRING(self); + register const unsigned char *e; + int cased; + + /* Shortcut for single character strings */ + if (PyString_GET_SIZE(self) == 1) + return PyBool_FromLong(islower(*p) != 0); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyString_GET_SIZE(self); + cased = 0; + for (; p < e; p++) { + if (isupper(*p)) + return PyBool_FromLong(0); + else if (!cased && islower(*p)) + cased = 1; + } + return PyBool_FromLong(cased); +} + + +PyDoc_STRVAR(isupper__doc__, +"S.isupper() -> bool\n\ +\n\ +Return True if all cased characters in S are uppercase and there is\n\ +at least one cased character in S, False otherwise."); + +static PyObject* +string_isupper(PyStringObject *self) +{ + register const unsigned char *p + = (unsigned char *) PyString_AS_STRING(self); + register const unsigned char *e; + int cased; + + /* Shortcut for single character strings */ + if (PyString_GET_SIZE(self) == 1) + return PyBool_FromLong(isupper(*p) != 0); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyString_GET_SIZE(self); + cased = 0; + for (; p < e; p++) { + if (islower(*p)) + return PyBool_FromLong(0); + else if (!cased && isupper(*p)) + cased = 1; + } + return PyBool_FromLong(cased); +} + + +PyDoc_STRVAR(istitle__doc__, +"S.istitle() -> bool\n\ +\n\ +Return True if S is a titlecased string and there is at least one\n\ +character in S, i.e. uppercase characters may only follow uncased\n\ +characters and lowercase characters only cased ones. Return False\n\ +otherwise."); + +static PyObject* +string_istitle(PyStringObject *self, PyObject *uncased) +{ + register const unsigned char *p + = (unsigned char *) PyString_AS_STRING(self); + register const unsigned char *e; + int cased, previous_is_cased; + + /* Shortcut for single character strings */ + if (PyString_GET_SIZE(self) == 1) + return PyBool_FromLong(isupper(*p) != 0); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyString_GET_SIZE(self); + cased = 0; + previous_is_cased = 0; + for (; p < e; p++) { + register const unsigned char ch = *p; + + if (isupper(ch)) { + if (previous_is_cased) + return PyBool_FromLong(0); + previous_is_cased = 1; + cased = 1; + } + else if (islower(ch)) { + if (!previous_is_cased) + return PyBool_FromLong(0); + previous_is_cased = 1; + cased = 1; + } + else + previous_is_cased = 0; + } + return PyBool_FromLong(cased); +} + + +PyDoc_STRVAR(splitlines__doc__, +"S.splitlines([keepends]) -> list of strings\n\ +\n\ +Return a list of the lines in S, breaking at line boundaries.\n\ +Line breaks are not included in the resulting list unless keepends\n\ +is given and true."); + +#define SPLIT_APPEND(data, left, right) \ + str = PyString_FromStringAndSize(data + left, right - left); \ + if (!str) \ + goto onError; \ + if (PyList_Append(list, str)) { \ + Py_DECREF(str); \ + goto onError; \ + } \ + else \ + Py_DECREF(str); + +static PyObject* +string_splitlines(PyStringObject *self, PyObject *args) +{ + register int i; + register int j; + int len; + int keepends = 0; + PyObject *list; + PyObject *str; + char *data; + + if (!PyArg_ParseTuple(args, "|i:splitlines", &keepends)) + return NULL; + + data = PyString_AS_STRING(self); + len = PyString_GET_SIZE(self); + + list = PyList_New(0); + if (!list) + goto onError; + + for (i = j = 0; i < len; ) { + int eol; + + /* Find a line and append it */ + while (i < len && data[i] != '\n' && data[i] != '\r') + i++; + + /* Skip the line break reading CRLF as one line break */ + eol = i; + if (i < len) { + if (data[i] == '\r' && i + 1 < len && + data[i+1] == '\n') + i += 2; + else + i++; + if (keepends) + eol = i; + } + SPLIT_APPEND(data, j, eol); + j = i; + } + if (j < len) { + SPLIT_APPEND(data, j, len); + } + + return list; + + onError: + Py_DECREF(list); + return NULL; +} + +#undef SPLIT_APPEND + +static PyObject * +string_getnewargs(PyStringObject *v) +{ + return Py_BuildValue("(s#)", v->ob_sval, v->ob_size); +} + + +static PyMethodDef +string_methods[] = { + /* Counterparts of the obsolete stropmodule functions; except + string.maketrans(). */ + {"join", (PyCFunction)string_join, METH_O, join__doc__}, + {"split", (PyCFunction)string_split, METH_VARARGS, split__doc__}, + {"lower", (PyCFunction)string_lower, METH_NOARGS, lower__doc__}, + {"upper", (PyCFunction)string_upper, METH_NOARGS, upper__doc__}, + {"islower", (PyCFunction)string_islower, METH_NOARGS, islower__doc__}, + {"isupper", (PyCFunction)string_isupper, METH_NOARGS, isupper__doc__}, + {"isspace", (PyCFunction)string_isspace, METH_NOARGS, isspace__doc__}, + {"isdigit", (PyCFunction)string_isdigit, METH_NOARGS, isdigit__doc__}, + {"istitle", (PyCFunction)string_istitle, METH_NOARGS, istitle__doc__}, + {"isalpha", (PyCFunction)string_isalpha, METH_NOARGS, isalpha__doc__}, + {"isalnum", (PyCFunction)string_isalnum, METH_NOARGS, isalnum__doc__}, + {"capitalize", (PyCFunction)string_capitalize, METH_NOARGS, + capitalize__doc__}, + {"count", (PyCFunction)string_count, METH_VARARGS, count__doc__}, + {"endswith", (PyCFunction)string_endswith, METH_VARARGS, + endswith__doc__}, + {"find", (PyCFunction)string_find, METH_VARARGS, find__doc__}, + {"index", (PyCFunction)string_index, METH_VARARGS, index__doc__}, + {"lstrip", (PyCFunction)string_lstrip, METH_VARARGS, lstrip__doc__}, + {"replace", (PyCFunction)string_replace, METH_VARARGS, replace__doc__}, + {"rfind", (PyCFunction)string_rfind, METH_VARARGS, rfind__doc__}, + {"rindex", (PyCFunction)string_rindex, METH_VARARGS, rindex__doc__}, + {"rstrip", (PyCFunction)string_rstrip, METH_VARARGS, rstrip__doc__}, + {"startswith", (PyCFunction)string_startswith, METH_VARARGS, + startswith__doc__}, + {"strip", (PyCFunction)string_strip, METH_VARARGS, strip__doc__}, + {"swapcase", (PyCFunction)string_swapcase, METH_NOARGS, + swapcase__doc__}, + {"translate", (PyCFunction)string_translate, METH_VARARGS, + translate__doc__}, + {"title", (PyCFunction)string_title, METH_NOARGS, title__doc__}, + {"ljust", (PyCFunction)string_ljust, METH_VARARGS, ljust__doc__}, + {"rjust", (PyCFunction)string_rjust, METH_VARARGS, rjust__doc__}, + {"center", (PyCFunction)string_center, METH_VARARGS, center__doc__}, + {"zfill", (PyCFunction)string_zfill, METH_VARARGS, zfill__doc__}, + {"encode", (PyCFunction)string_encode, METH_VARARGS, encode__doc__}, + {"decode", (PyCFunction)string_decode, METH_VARARGS, decode__doc__}, + {"expandtabs", (PyCFunction)string_expandtabs, METH_VARARGS, + expandtabs__doc__}, + {"splitlines", (PyCFunction)string_splitlines, METH_VARARGS, + splitlines__doc__}, + {"__getnewargs__", (PyCFunction)string_getnewargs, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + +static PyObject * +string_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *x = NULL; + static char *kwlist[] = {"object", 0}; + + if (type != &PyString_Type) + return str_subtype_new(type, args, kwds); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:str", kwlist, &x)) + return NULL; + if (x == NULL) + return PyString_FromString(""); + return PyObject_Str(x); +} + +static PyObject * +str_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *tmp, *pnew; + int n; + + assert(PyType_IsSubtype(type, &PyString_Type)); + tmp = string_new(&PyString_Type, args, kwds); + if (tmp == NULL) + return NULL; + assert(PyString_CheckExact(tmp)); + n = PyString_GET_SIZE(tmp); + pnew = type->tp_alloc(type, n); + if (pnew != NULL) { + memcpy(PyString_AS_STRING(pnew), PyString_AS_STRING(tmp), n+1); + ((PyStringObject *)pnew)->ob_shash = + ((PyStringObject *)tmp)->ob_shash; + ((PyStringObject *)pnew)->ob_sstate = SSTATE_NOT_INTERNED; + } + Py_DECREF(tmp); + return pnew; +} + +static PyObject * +basestring_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyErr_SetString(PyExc_TypeError, + "The basestring type cannot be instantiated"); + return NULL; +} + +static PyObject * +string_mod(PyObject *v, PyObject *w) +{ + if (!PyString_Check(v)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return PyString_Format(v, w); +} + +PyDoc_STRVAR(basestring_doc, +"Type basestring cannot be instantiated; it is the base for str and unicode."); + +static PyNumberMethods string_as_number = { + 0, /*nb_add*/ + 0, /*nb_subtract*/ + 0, /*nb_multiply*/ + 0, /*nb_divide*/ + string_mod, /*nb_remainder*/ +}; + + +PyTypeObject PyBaseString_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "basestring", + 0, + 0, + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + basestring_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &PyBaseObject_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + basestring_new, /* tp_new */ + 0, /* tp_free */ +}; + +PyDoc_STRVAR(string_doc, +"str(object) -> string\n\ +\n\ +Return a nice string representation of the object.\n\ +If the argument is a string, the return value is the same object."); + +PyTypeObject PyString_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "str", + sizeof(PyStringObject), + sizeof(char), + (destructor)string_dealloc, /* tp_dealloc */ + (printfunc)string_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)string_repr, /* tp_repr */ + &string_as_number, /* tp_as_number */ + &string_as_sequence, /* tp_as_sequence */ + &string_as_mapping, /* tp_as_mapping */ + (hashfunc)string_hash, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)string_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + &string_as_buffer, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + string_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)string_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + string_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &PyBaseString_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + string_new, /* tp_new */ + PyObject_Del, /* tp_free */ +}; + +void +PyString_Concat(register PyObject **pv, register PyObject *w) +{ + register PyObject *v; + if (*pv == NULL) + return; + if (w == NULL || !PyString_Check(*pv)) { + Py_DECREF(*pv); + *pv = NULL; + return; + } + v = string_concat((PyStringObject *) *pv, w); + Py_DECREF(*pv); + *pv = v; +} + +void +PyString_ConcatAndDel(register PyObject **pv, register PyObject *w) +{ + PyString_Concat(pv, w); + Py_XDECREF(w); +} + + +/* The following function breaks the notion that strings are immutable: + it changes the size of a string. We get away with this only if there + is only one module referencing the object. You can also think of it + as creating a new string object and destroying the old one, only + more efficiently. In any case, don't use this if the string may + already be known to some other part of the code... + Note that if there's not enough memory to resize the string, the original + string object at *pv is deallocated, *pv is set to NULL, an "out of + memory" exception is set, and -1 is returned. Else (on success) 0 is + returned, and the value in *pv may or may not be the same as on input. + As always, an extra byte is allocated for a trailing \0 byte (newsize + does *not* include that), and a trailing \0 byte is stored. +*/ + +int +_PyString_Resize(PyObject **pv, int newsize) +{ + register PyObject *v; + register PyStringObject *sv; + v = *pv; + if (!PyString_Check(v) || v->ob_refcnt != 1 || newsize < 0) { + *pv = 0; + Py_DECREF(v); + PyErr_BadInternalCall(); + return -1; + } + /* XXX UNREF/NEWREF interface should be more symmetrical */ + _Py_DEC_REFTOTAL; + _Py_ForgetReference(v); + *pv = (PyObject *) + PyObject_REALLOC((char *)v, + sizeof(PyStringObject) + newsize * sizeof(char)); + if (*pv == NULL) { + PyObject_Del(v); + PyErr_NoMemory(); + return -1; + } + _Py_NewReference(*pv); + sv = (PyStringObject *) *pv; + sv->ob_size = newsize; + sv->ob_sval[newsize] = '\0'; + return 0; +} + +/* Helpers for formatstring */ + +static PyObject * +getnextarg(PyObject *args, int arglen, int *p_argidx) +{ + int argidx = *p_argidx; + if (argidx < arglen) { + (*p_argidx)++; + if (arglen < 0) + return args; + else + return PyTuple_GetItem(args, argidx); + } + PyErr_SetString(PyExc_TypeError, + "not enough arguments for format string"); + return NULL; +} + +/* Format codes + * F_LJUST '-' + * F_SIGN '+' + * F_BLANK ' ' + * F_ALT '#' + * F_ZERO '0' + */ +#define F_LJUST (1<<0) +#define F_SIGN (1<<1) +#define F_BLANK (1<<2) +#define F_ALT (1<<3) +#define F_ZERO (1<<4) + +static int +formatfloat(char *buf, size_t buflen, int flags, + int prec, int type, PyObject *v) +{ + /* fmt = '%#.' + `prec` + `type` + worst case length = 3 + 10 (len of INT_MAX) + 1 = 14 (use 20)*/ + char fmt[20]; + double x; + x = PyFloat_AsDouble(v); + if (x == -1.0 && PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "float argument required"); + return -1; + } + if (prec < 0) + prec = 6; + if (type == 'f' && fabs(x)/1e25 >= 1e25) + type = 'g'; + /* Worst case length calc to ensure no buffer overrun: + + 'g' formats: + fmt = %#.g + buf = '-' + [0-9]*prec + '.' + 'e+' + (longest exp + for any double rep.) + len = 1 + prec + 1 + 2 + 5 = 9 + prec + + 'f' formats: + buf = '-' + [0-9]*x + '.' + [0-9]*prec (with x < 50) + len = 1 + 50 + 1 + prec = 52 + prec + + If prec=0 the effective precision is 1 (the leading digit is + always given), therefore increase the length by one. + + */ + if ((type == 'g' && buflen <= (size_t)10 + (size_t)prec) || + (type == 'f' && buflen <= (size_t)53 + (size_t)prec)) { + PyErr_SetString(PyExc_OverflowError, + "formatted float is too long (precision too large?)"); + return -1; + } + PyOS_snprintf(fmt, sizeof(fmt), "%%%s.%d%c", + (flags&F_ALT) ? "#" : "", + prec, type); + PyOS_snprintf(buf, buflen, fmt, x); + return strlen(buf); +} + +/* _PyString_FormatLong emulates the format codes d, u, o, x and X, and + * the F_ALT flag, for Python's long (unbounded) ints. It's not used for + * Python's regular ints. + * Return value: a new PyString*, or NULL if error. + * . *pbuf is set to point into it, + * *plen set to the # of chars following that. + * Caller must decref it when done using pbuf. + * The string starting at *pbuf is of the form + * "-"? ("0x" | "0X")? digit+ + * "0x"/"0X" are present only for x and X conversions, with F_ALT + * set in flags. The case of hex digits will be correct, + * There will be at least prec digits, zero-filled on the left if + * necessary to get that many. + * val object to be converted + * flags bitmask of format flags; only F_ALT is looked at + * prec minimum number of digits; 0-fill on left if needed + * type a character in [duoxX]; u acts the same as d + * + * CAUTION: o, x and X conversions on regular ints can never + * produce a '-' sign, but can for Python's unbounded ints. + */ +PyObject* +_PyString_FormatLong(PyObject *val, int flags, int prec, int type, + char **pbuf, int *plen) +{ + PyObject *result = NULL; + char *buf; + int i; + int sign; /* 1 if '-', else 0 */ + int len; /* number of characters */ + int numdigits; /* len == numnondigits + numdigits */ + int numnondigits = 0; + + switch (type) { + case 'd': + case 'u': + result = val->ob_type->tp_str(val); + break; + case 'o': + result = val->ob_type->tp_as_number->nb_oct(val); + break; + case 'x': + case 'X': + numnondigits = 2; + result = val->ob_type->tp_as_number->nb_hex(val); + break; + default: + assert(!"'type' not in [duoxX]"); + } + if (!result) + return NULL; + + /* To modify the string in-place, there can only be one reference. */ + if (result->ob_refcnt != 1) { + PyErr_BadInternalCall(); + return NULL; + } + buf = PyString_AsString(result); + len = PyString_Size(result); + if (buf[len-1] == 'L') { + --len; + buf[len] = '\0'; + } + sign = buf[0] == '-'; + numnondigits += sign; + numdigits = len - numnondigits; + assert(numdigits > 0); + + /* Get rid of base marker unless F_ALT */ + if ((flags & F_ALT) == 0) { + /* Need to skip 0x, 0X or 0. */ + int skipped = 0; + switch (type) { + case 'o': + assert(buf[sign] == '0'); + /* If 0 is only digit, leave it alone. */ + if (numdigits > 1) { + skipped = 1; + --numdigits; + } + break; + case 'x': + case 'X': + assert(buf[sign] == '0'); + assert(buf[sign + 1] == 'x'); + skipped = 2; + numnondigits -= 2; + break; + } + if (skipped) { + buf += skipped; + len -= skipped; + if (sign) + buf[0] = '-'; + } + assert(len == numnondigits + numdigits); + assert(numdigits > 0); + } + + /* Fill with leading zeroes to meet minimum width. */ + if (prec > numdigits) { + PyObject *r1 = PyString_FromStringAndSize(NULL, + numnondigits + prec); + char *b1; + if (!r1) { + Py_DECREF(result); + return NULL; + } + b1 = PyString_AS_STRING(r1); + for (i = 0; i < numnondigits; ++i) + *b1++ = *buf++; + for (i = 0; i < prec - numdigits; i++) + *b1++ = '0'; + for (i = 0; i < numdigits; i++) + *b1++ = *buf++; + *b1 = '\0'; + Py_DECREF(result); + result = r1; + buf = PyString_AS_STRING(result); + len = numnondigits + prec; + } + + /* Fix up case for hex conversions. */ + switch (type) { + case 'x': + /* Need to convert all upper case letters to lower case. */ + for (i = 0; i < len; i++) + if (buf[i] >= 'A' && buf[i] <= 'F') + buf[i] += 'a'-'A'; + break; + case 'X': + /* Need to convert 0x to 0X (and -0x to -0X). */ + if (buf[sign + 1] == 'x') + buf[sign + 1] = 'X'; + break; + } + *pbuf = buf; + *plen = len; + return result; +} + +static int +formatint(char *buf, size_t buflen, int flags, + int prec, int type, PyObject *v) +{ + /* fmt = '%#.' + `prec` + 'l' + `type` + worst case length = 3 + 19 (worst len of INT_MAX on 64-bit machine) + + 1 + 1 = 24 */ + char fmt[64]; /* plenty big enough! */ + long x; + + x = PyInt_AsLong(v); + if (x == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "int argument required"); + return -1; + } + if (x < 0 && type != 'd' && type != 'i') { + if (PyErr_Warn(PyExc_FutureWarning, + "%u/%o/%x/%X of negative int will return " + "a signed string in Python 2.4 and up") < 0) + return -1; + } + if (prec < 0) + prec = 1; + + if ((flags & F_ALT) && + (type == 'x' || type == 'X')) { + /* When converting under %#x or %#X, there are a number + * of issues that cause pain: + * - when 0 is being converted, the C standard leaves off + * the '0x' or '0X', which is inconsistent with other + * %#x/%#X conversions and inconsistent with Python's + * hex() function + * - there are platforms that violate the standard and + * convert 0 with the '0x' or '0X' + * (Metrowerks, Compaq Tru64) + * - there are platforms that give '0x' when converting + * under %#X, but convert 0 in accordance with the + * standard (OS/2 EMX) + * + * We can achieve the desired consistency by inserting our + * own '0x' or '0X' prefix, and substituting %x/%X in place + * of %#x/%#X. + * + * Note that this is the same approach as used in + * formatint() in unicodeobject.c + */ + PyOS_snprintf(fmt, sizeof(fmt), "0%c%%.%dl%c", + type, prec, type); + } + else { + PyOS_snprintf(fmt, sizeof(fmt), "%%%s.%dl%c", + (flags&F_ALT) ? "#" : "", + prec, type); + } + + /* buf = '+'/'-'/'0'/'0x' + '[0-9]'*max(prec, len(x in octal)) + * worst case buf = '0x' + [0-9]*prec, where prec >= 11 + */ + if (buflen <= 13 || buflen <= (size_t)2 + (size_t)prec) { + PyErr_SetString(PyExc_OverflowError, + "formatted integer is too long (precision too large?)"); + return -1; + } + PyOS_snprintf(buf, buflen, fmt, x); + return strlen(buf); +} + +static int +formatchar(char *buf, size_t buflen, PyObject *v) +{ + /* presume that the buffer is at least 2 characters long */ + if (PyString_Check(v)) { + if (!PyArg_Parse(v, "c;%c requires int or char", &buf[0])) + return -1; + } + else { + if (!PyArg_Parse(v, "b;%c requires int or char", &buf[0])) + return -1; + } + buf[1] = '\0'; + return 1; +} + + +/* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...) + + FORMATBUFLEN is the length of the buffer in which the floats, ints, & + chars are formatted. XXX This is a magic number. Each formatting + routine does bounds checking to ensure no overflow, but a better + solution may be to malloc a buffer of appropriate size for each + format. For now, the current solution is sufficient. +*/ +#define FORMATBUFLEN (size_t)120 + +PyObject * +PyString_Format(PyObject *format, PyObject *args) +{ + char *fmt, *res; + int fmtcnt, rescnt, reslen, arglen, argidx; + int args_owned = 0; + PyObject *result, *orig_args; +#ifdef Py_USING_UNICODE + PyObject *v, *w; +#endif + PyObject *dict = NULL; + if (format == NULL || !PyString_Check(format) || args == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + orig_args = args; + fmt = PyString_AS_STRING(format); + fmtcnt = PyString_GET_SIZE(format); + reslen = rescnt = fmtcnt + 100; + result = PyString_FromStringAndSize((char *)NULL, reslen); + if (result == NULL) + return NULL; + res = PyString_AsString(result); + if (PyTuple_Check(args)) { + arglen = PyTuple_GET_SIZE(args); + argidx = 0; + } + else { + arglen = -1; + argidx = -2; + } + if (args->ob_type->tp_as_mapping && !PyTuple_Check(args) && + !PyObject_TypeCheck(args, &PyBaseString_Type)) + dict = args; + while (--fmtcnt >= 0) { + if (*fmt != '%') { + if (--rescnt < 0) { + rescnt = fmtcnt + 100; + reslen += rescnt; + if (_PyString_Resize(&result, reslen) < 0) + return NULL; + res = PyString_AS_STRING(result) + + reslen - rescnt; + --rescnt; + } + *res++ = *fmt++; + } + else { + /* Got a format specifier */ + int flags = 0; + int width = -1; + int prec = -1; + int c = '\0'; + int fill; + PyObject *v = NULL; + PyObject *temp = NULL; + char *pbuf; + int sign; + int len; + char formatbuf[FORMATBUFLEN]; + /* For format{float,int,char}() */ +#ifdef Py_USING_UNICODE + char *fmt_start = fmt; + int argidx_start = argidx; +#endif + + fmt++; + if (*fmt == '(') { + char *keystart; + int keylen; + PyObject *key; + int pcount = 1; + + if (dict == NULL) { + PyErr_SetString(PyExc_TypeError, + "format requires a mapping"); + goto error; + } + ++fmt; + --fmtcnt; + keystart = fmt; + /* Skip over balanced parentheses */ + while (pcount > 0 && --fmtcnt >= 0) { + if (*fmt == ')') + --pcount; + else if (*fmt == '(') + ++pcount; + fmt++; + } + keylen = fmt - keystart - 1; + if (fmtcnt < 0 || pcount > 0) { + PyErr_SetString(PyExc_ValueError, + "incomplete format key"); + goto error; + } + key = PyString_FromStringAndSize(keystart, + keylen); + if (key == NULL) + goto error; + if (args_owned) { + Py_DECREF(args); + args_owned = 0; + } + args = PyObject_GetItem(dict, key); + Py_DECREF(key); + if (args == NULL) { + goto error; + } + args_owned = 1; + arglen = -1; + argidx = -2; + } + while (--fmtcnt >= 0) { + switch (c = *fmt++) { + case '-': flags |= F_LJUST; continue; + case '+': flags |= F_SIGN; continue; + case ' ': flags |= F_BLANK; continue; + case '#': flags |= F_ALT; continue; + case '0': flags |= F_ZERO; continue; + } + break; + } + if (c == '*') { + v = getnextarg(args, arglen, &argidx); + if (v == NULL) + goto error; + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "* wants int"); + goto error; + } + width = PyInt_AsLong(v); + if (width < 0) { + flags |= F_LJUST; + width = -width; + } + if (--fmtcnt >= 0) + c = *fmt++; + } + else if (c >= 0 && isdigit(c)) { + width = c - '0'; + while (--fmtcnt >= 0) { + c = Py_CHARMASK(*fmt++); + if (!isdigit(c)) + break; + if ((width*10) / 10 != width) { + PyErr_SetString( + PyExc_ValueError, + "width too big"); + goto error; + } + width = width*10 + (c - '0'); + } + } + if (c == '.') { + prec = 0; + if (--fmtcnt >= 0) + c = *fmt++; + if (c == '*') { + v = getnextarg(args, arglen, &argidx); + if (v == NULL) + goto error; + if (!PyInt_Check(v)) { + PyErr_SetString( + PyExc_TypeError, + "* wants int"); + goto error; + } + prec = PyInt_AsLong(v); + if (prec < 0) + prec = 0; + if (--fmtcnt >= 0) + c = *fmt++; + } + else if (c >= 0 && isdigit(c)) { + prec = c - '0'; + while (--fmtcnt >= 0) { + c = Py_CHARMASK(*fmt++); + if (!isdigit(c)) + break; + if ((prec*10) / 10 != prec) { + PyErr_SetString( + PyExc_ValueError, + "prec too big"); + goto error; + } + prec = prec*10 + (c - '0'); + } + } + } /* prec */ + if (fmtcnt >= 0) { + if (c == 'h' || c == 'l' || c == 'L') { + if (--fmtcnt >= 0) + c = *fmt++; + } + } + if (fmtcnt < 0) { + PyErr_SetString(PyExc_ValueError, + "incomplete format"); + goto error; + } + if (c != '%') { + v = getnextarg(args, arglen, &argidx); + if (v == NULL) + goto error; + } + sign = 0; + fill = ' '; + switch (c) { + case '%': + pbuf = "%"; + len = 1; + break; + case 's': +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(v)) { + fmt = fmt_start; + argidx = argidx_start; + goto unicode; + } +#endif + /* Fall through */ + case 'r': + if (c == 's') + temp = PyObject_Str(v); + else + temp = PyObject_Repr(v); + if (temp == NULL) + goto error; + if (!PyString_Check(temp)) { + /* XXX Note: this should never happen, + since PyObject_Repr() and + PyObject_Str() assure this */ + PyErr_SetString(PyExc_TypeError, + "%s argument has non-string str()"); + Py_DECREF(temp); + goto error; + } + pbuf = PyString_AS_STRING(temp); + len = PyString_GET_SIZE(temp); + if (prec >= 0 && len > prec) + len = prec; + break; + case 'i': + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + if (c == 'i') + c = 'd'; + if (PyLong_Check(v)) { + temp = _PyString_FormatLong(v, flags, + prec, c, &pbuf, &len); + if (!temp) + goto error; + /* unbounded ints can always produce + a sign character! */ + sign = 1; + } + else { + pbuf = formatbuf; + len = formatint(pbuf, + sizeof(formatbuf), + flags, prec, c, v); + if (len < 0) + goto error; + /* only d conversion is signed */ + sign = c == 'd'; + } + if (flags & F_ZERO) + fill = '0'; + break; + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (c == 'F') + c = 'f'; + pbuf = formatbuf; + len = formatfloat(pbuf, sizeof(formatbuf), + flags, prec, c, v); + if (len < 0) + goto error; + sign = 1; + if (flags & F_ZERO) + fill = '0'; + break; + case 'c': +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(v)) { + fmt = fmt_start; + argidx = argidx_start; + goto unicode; + } +#endif + pbuf = formatbuf; + len = formatchar(pbuf, sizeof(formatbuf), v); + if (len < 0) + goto error; + break; + default: + PyErr_Format(PyExc_ValueError, + "unsupported format character '%c' (0x%x) " + "at index %i", + c, c, + (int)(fmt - 1 - PyString_AsString(format))); + goto error; + } + if (sign) { + if (*pbuf == '-' || *pbuf == '+') { + sign = *pbuf++; + len--; + } + else if (flags & F_SIGN) + sign = '+'; + else if (flags & F_BLANK) + sign = ' '; + else + sign = 0; + } + if (width < len) + width = len; + if (rescnt - (sign != 0) < width) { + reslen -= rescnt; + rescnt = width + fmtcnt + 100; + reslen += rescnt; + if (reslen < 0) { + Py_DECREF(result); + return PyErr_NoMemory(); + } + if (_PyString_Resize(&result, reslen) < 0) + return NULL; + res = PyString_AS_STRING(result) + + reslen - rescnt; + } + if (sign) { + if (fill != ' ') + *res++ = sign; + rescnt--; + if (width > len) + width--; + } + if ((flags & F_ALT) && (c == 'x' || c == 'X')) { + assert(pbuf[0] == '0'); + assert(pbuf[1] == c); + if (fill != ' ') { + *res++ = *pbuf++; + *res++ = *pbuf++; + } + rescnt -= 2; + width -= 2; + if (width < 0) + width = 0; + len -= 2; + } + if (width > len && !(flags & F_LJUST)) { + do { + --rescnt; + *res++ = fill; + } while (--width > len); + } + if (fill == ' ') { + if (sign) + *res++ = sign; + if ((flags & F_ALT) && + (c == 'x' || c == 'X')) { + assert(pbuf[0] == '0'); + assert(pbuf[1] == c); + *res++ = *pbuf++; + *res++ = *pbuf++; + } + } + memcpy(res, pbuf, len); + res += len; + rescnt -= len; + while (--width >= len) { + --rescnt; + *res++ = ' '; + } + if (dict && (argidx < arglen) && c != '%') { + PyErr_SetString(PyExc_TypeError, + "not all arguments converted during string formatting"); + goto error; + } + Py_XDECREF(temp); + } /* '%' */ + } /* until end */ + if (argidx < arglen && !dict) { + PyErr_SetString(PyExc_TypeError, + "not all arguments converted during string formatting"); + goto error; + } + if (args_owned) { + Py_DECREF(args); + } + _PyString_Resize(&result, reslen - rescnt); + return result; + +#ifdef Py_USING_UNICODE + unicode: + if (args_owned) { + Py_DECREF(args); + args_owned = 0; + } + /* Fiddle args right (remove the first argidx arguments) */ + if (PyTuple_Check(orig_args) && argidx > 0) { + PyObject *v; + int n = PyTuple_GET_SIZE(orig_args) - argidx; + v = PyTuple_New(n); + if (v == NULL) + goto error; + while (--n >= 0) { + PyObject *w = PyTuple_GET_ITEM(orig_args, n + argidx); + Py_INCREF(w); + PyTuple_SET_ITEM(v, n, w); + } + args = v; + } else { + Py_INCREF(orig_args); + args = orig_args; + } + args_owned = 1; + /* Take what we have of the result and let the Unicode formatting + function format the rest of the input. */ + rescnt = res - PyString_AS_STRING(result); + if (_PyString_Resize(&result, rescnt)) + goto error; + fmtcnt = PyString_GET_SIZE(format) - \ + (fmt - PyString_AS_STRING(format)); + format = PyUnicode_Decode(fmt, fmtcnt, NULL, NULL); + if (format == NULL) + goto error; + v = PyUnicode_Format(format, args); + Py_DECREF(format); + if (v == NULL) + goto error; + /* Paste what we have (result) to what the Unicode formatting + function returned (v) and return the result (or error) */ + w = PyUnicode_Concat(result, v); + Py_DECREF(result); + Py_DECREF(v); + Py_DECREF(args); + return w; +#endif /* Py_USING_UNICODE */ + + error: + Py_DECREF(result); + if (args_owned) { + Py_DECREF(args); + } + return NULL; +} + +void +PyString_InternInPlace(PyObject **p) +{ + register PyStringObject *s = (PyStringObject *)(*p); + PyObject *t; + if (s == NULL || !PyString_Check(s)) + Py_FatalError("PyString_InternInPlace: strings only please!"); + if (PyString_CHECK_INTERNED(s)) + return; + if (interned == NULL) { + interned = PyDict_New(); + if (interned == NULL) { + PyErr_Clear(); /* Don't leave an exception */ + return; + } + } + if ((t = PyDict_GetItem(interned, (PyObject *)s)) != NULL) { + Py_INCREF(t); + Py_DECREF(*p); + *p = t; + return; + } + /* Ensure that only true string objects appear in the intern dict */ + if (!PyString_CheckExact(s)) { + t = PyString_FromStringAndSize(PyString_AS_STRING(s), + PyString_GET_SIZE(s)); + if (t == NULL) { + PyErr_Clear(); + return; + } + } else { + t = (PyObject*) s; + Py_INCREF(t); + } + + if (PyDict_SetItem(interned, t, t) == 0) { + /* The two references in interned are not counted by + refcnt. The string deallocator will take care of this */ + ((PyObject *)t)->ob_refcnt-=2; + PyString_CHECK_INTERNED(t) = SSTATE_INTERNED_MORTAL; + Py_DECREF(*p); + *p = t; + return; + } + Py_DECREF(t); + PyErr_Clear(); +} + +void +PyString_InternImmortal(PyObject **p) +{ + PyString_InternInPlace(p); + if (PyString_CHECK_INTERNED(*p) != SSTATE_INTERNED_IMMORTAL) { + PyString_CHECK_INTERNED(*p) = SSTATE_INTERNED_IMMORTAL; + Py_INCREF(*p); + } +} + + +PyObject * +PyString_InternFromString(const char *cp) +{ + PyObject *s = PyString_FromString(cp); + if (s == NULL) + return NULL; + PyString_InternInPlace(&s); + return s; +} + +void +PyString_Fini(void) +{ + int i; + for (i = 0; i < UCHAR_MAX + 1; i++) { + Py_XDECREF(characters[i]); + characters[i] = NULL; + } + Py_XDECREF(nullstring); + nullstring = NULL; +} + +void _Py_ReleaseInternedStrings(void) +{ + PyObject *keys; + PyStringObject *s; + int i, n; + + if (interned == NULL || !PyDict_Check(interned)) + return; + keys = PyDict_Keys(interned); + if (keys == NULL || !PyList_Check(keys)) { + PyErr_Clear(); + return; + } + + /* Since _Py_ReleaseInternedStrings() is intended to help a leak + detector, interned strings are not forcibly deallocated; rather, we + give them their stolen references back, and then clear and DECREF + the interned dict. */ + + fprintf(stderr, "releasing interned strings\n"); + n = PyList_GET_SIZE(keys); + for (i = 0; i < n; i++) { + s = (PyStringObject *) PyList_GET_ITEM(keys, i); + switch (s->ob_sstate) { + case SSTATE_NOT_INTERNED: + /* XXX Shouldn't happen */ + break; + case SSTATE_INTERNED_IMMORTAL: + s->ob_refcnt += 1; + break; + case SSTATE_INTERNED_MORTAL: + s->ob_refcnt += 2; + break; + default: + Py_FatalError("Inconsistent interned string state."); + } + s->ob_sstate = SSTATE_NOT_INTERNED; + } + Py_DECREF(keys); + PyDict_Clear(interned); + Py_DECREF(interned); + interned = NULL; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/structseq.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/structseq.c new file mode 100644 index 00000000..a968f1fc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/structseq.c @@ -0,0 +1,395 @@ +/* Implementation helper: a struct that looks like a tuple. See timemodule + and posixmodule for example uses. */ + +#include "Python.h" +#include "structmember.h" +#include "structseq.h" + +static char visible_length_key[] = "n_sequence_fields"; +static char real_length_key[] = "n_fields"; +static char unnamed_fields_key[] = "n_unnamed_fields"; + +/* Fields with this name have only a field index, not a field name. + They are only allowed for indices < n_visible_fields. */ +char *PyStructSequence_UnnamedField = "unnamed field"; + +#define VISIBLE_SIZE(op) ((op)->ob_size) +#define VISIBLE_SIZE_TP(tp) PyInt_AsLong( \ + PyDict_GetItemString((tp)->tp_dict, visible_length_key)) + +#define REAL_SIZE_TP(tp) PyInt_AsLong( \ + PyDict_GetItemString((tp)->tp_dict, real_length_key)) +#define REAL_SIZE(op) REAL_SIZE_TP((op)->ob_type) + +#define UNNAMED_FIELDS_TP(tp) PyInt_AsLong( \ + PyDict_GetItemString((tp)->tp_dict, unnamed_fields_key)) +#define UNNAMED_FIELDS(op) UNNAMED_FIELDS_TP((op)->ob_type) + + +PyObject * +PyStructSequence_New(PyTypeObject *type) +{ + PyStructSequence *obj; + + obj = PyObject_New(PyStructSequence, type); + obj->ob_size = VISIBLE_SIZE_TP(type); + + return (PyObject*) obj; +} + +static void +structseq_dealloc(PyStructSequence *obj) +{ + int i, size; + + size = REAL_SIZE(obj); + for (i = 0; i < size; ++i) { + Py_XDECREF(obj->ob_item[i]); + } + PyObject_Del(obj); +} + +static int +structseq_length(PyStructSequence *obj) +{ + return VISIBLE_SIZE(obj); +} + +static PyObject* +structseq_item(PyStructSequence *obj, int i) +{ + if (i < 0 || i >= VISIBLE_SIZE(obj)) { + PyErr_SetString(PyExc_IndexError, "tuple index out of range"); + return NULL; + } + Py_INCREF(obj->ob_item[i]); + return obj->ob_item[i]; +} + +static PyObject* +structseq_slice(PyStructSequence *obj, int low, int high) +{ + PyTupleObject *np; + int i; + + if (low < 0) + low = 0; + if (high > VISIBLE_SIZE(obj)) + high = VISIBLE_SIZE(obj); + if (high < low) + high = low; + np = (PyTupleObject *)PyTuple_New(high-low); + if (np == NULL) + return NULL; + for(i = low; i < high; ++i) { + PyObject *v = obj->ob_item[i]; + Py_INCREF(v); + PyTuple_SET_ITEM(np, i-low, v); + } + return (PyObject *) np; +} + +static PyObject * +structseq_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *arg = NULL; + PyObject *dict = NULL; + PyObject *ob; + PyStructSequence *res = NULL; + int len, min_len, max_len, i, n_unnamed_fields; + static char *kwlist[] = {"sequence", "dict", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:structseq", + kwlist, &arg, &dict)) + return NULL; + + arg = PySequence_Fast(arg, "constructor requires a sequence"); + + if (!arg) { + return NULL; + } + + if (dict && !PyDict_Check(dict)) { + PyErr_Format(PyExc_TypeError, + "%.500s() takes a dict as second arg, if any", + type->tp_name); + Py_DECREF(arg); + return NULL; + } + + len = PySequence_Fast_GET_SIZE(arg); + min_len = VISIBLE_SIZE_TP(type); + max_len = REAL_SIZE_TP(type); + n_unnamed_fields = UNNAMED_FIELDS_TP(type); + + if (min_len != max_len) { + if (len < min_len) { + PyErr_Format(PyExc_TypeError, + "%.500s() takes an at least %d-sequence (%d-sequence given)", + type->tp_name, min_len, len); + Py_DECREF(arg); + return NULL; + } + + if (len > max_len) { + PyErr_Format(PyExc_TypeError, + "%.500s() takes an at most %d-sequence (%d-sequence given)", + type->tp_name, max_len, len); + Py_DECREF(arg); + return NULL; + } + } + else { + if (len != min_len) { + PyErr_Format(PyExc_TypeError, + "%.500s() takes a %d-sequence (%d-sequence given)", + type->tp_name, min_len, len); + Py_DECREF(arg); + return NULL; + } + } + + res = (PyStructSequence*) PyStructSequence_New(type); + if (res == NULL) { + return NULL; + } + for (i = 0; i < len; ++i) { + PyObject *v = PySequence_Fast_GET_ITEM(arg, i); + Py_INCREF(v); + res->ob_item[i] = v; + } + for (; i < max_len; ++i) { + if (dict && (ob = PyDict_GetItemString( + dict, type->tp_members[i-n_unnamed_fields].name))) { + } + else { + ob = Py_None; + } + Py_INCREF(ob); + res->ob_item[i] = ob; + } + + Py_DECREF(arg); + return (PyObject*) res; +} + +static PyObject * +make_tuple(PyStructSequence *obj) +{ + return structseq_slice(obj, 0, VISIBLE_SIZE(obj)); +} + +static PyObject * +structseq_repr(PyStructSequence *obj) +{ + PyObject *tup, *str; + tup = make_tuple(obj); + str = PyObject_Repr(tup); + Py_DECREF(tup); + return str; +} + +static PyObject * +structseq_concat(PyStructSequence *obj, PyObject *b) +{ + PyObject *tup, *result; + tup = make_tuple(obj); + result = PySequence_Concat(tup, b); + Py_DECREF(tup); + return result; +} + +static PyObject * +structseq_repeat(PyStructSequence *obj, int n) +{ + PyObject *tup, *result; + tup = make_tuple(obj); + result = PySequence_Repeat(tup, n); + Py_DECREF(tup); + return result; +} + +static int +structseq_contains(PyStructSequence *obj, PyObject *o) +{ + PyObject *tup; + int result; + tup = make_tuple(obj); + result = PySequence_Contains(tup, o); + Py_DECREF(tup); + return result; +} + +static long +structseq_hash(PyObject *obj) +{ + PyObject *tup; + long result; + tup = make_tuple((PyStructSequence*) obj); + result = PyObject_Hash(tup); + Py_DECREF(tup); + return result; +} + +static PyObject * +structseq_richcompare(PyObject *obj, PyObject *o2, int op) +{ + PyObject *tup, *result; + tup = make_tuple((PyStructSequence*) obj); + result = PyObject_RichCompare(tup, o2, op); + Py_DECREF(tup); + return result; +} + +static PyObject * +structseq_reduce(PyStructSequence* self) +{ + PyObject* tup; + PyObject* dict; + PyObject* result; + long n_fields, n_visible_fields, n_unnamed_fields; + int i; + + n_fields = REAL_SIZE(self); + n_visible_fields = VISIBLE_SIZE(self); + n_unnamed_fields = UNNAMED_FIELDS(self); + tup = PyTuple_New(n_visible_fields); + if (!tup) { + return NULL; + } + + dict = PyDict_New(); + if (!dict) { + Py_DECREF(tup); + return NULL; + } + + for (i = 0; i < n_visible_fields; i++) { + Py_INCREF(self->ob_item[i]); + PyTuple_SET_ITEM(tup, i, self->ob_item[i]); + } + + for (; i < n_fields; i++) { + char *n = self->ob_type->tp_members[i-n_unnamed_fields].name; + PyDict_SetItemString(dict, n, + self->ob_item[i]); + } + + result = Py_BuildValue("(O(OO))", self->ob_type, tup, dict); + + Py_DECREF(tup); + Py_DECREF(dict); + + return result; +} + +static PySequenceMethods structseq_as_sequence = { + (inquiry)structseq_length, + (binaryfunc)structseq_concat, /* sq_concat */ + (intargfunc)structseq_repeat, /* sq_repeat */ + (intargfunc)structseq_item, /* sq_item */ + (intintargfunc)structseq_slice, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)structseq_contains, /* sq_contains */ +}; + +static PyMethodDef structseq_methods[] = { + {"__reduce__", (PyCFunction)structseq_reduce, + METH_NOARGS, NULL}, + {NULL, NULL} +}; + +static PyTypeObject _struct_sequence_template = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + NULL, /* tp_name */ + 0, /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)structseq_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)structseq_repr, /* tp_repr */ + 0, /* tp_as_number */ + &structseq_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)structseq_hash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + NULL, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + structseq_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + structseq_methods, /* tp_methods */ + NULL, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + structseq_new, /* tp_new */ +}; + +void +PyStructSequence_InitType(PyTypeObject *type, PyStructSequence_Desc *desc) +{ + PyObject *dict; + PyMemberDef* members; + int n_members, n_unnamed_members, i, k; + + n_unnamed_members = 0; + for (i = 0; desc->fields[i].name != NULL; ++i) + if (desc->fields[i].name == PyStructSequence_UnnamedField) + n_unnamed_members++; + n_members = i; + + memcpy(type, &_struct_sequence_template, sizeof(PyTypeObject)); + type->tp_name = desc->name; + type->tp_doc = desc->doc; + type->tp_basicsize = sizeof(PyStructSequence)+ + sizeof(PyObject*)*(n_members-1); + type->tp_itemsize = 0; + + members = PyMem_NEW(PyMemberDef, n_members-n_unnamed_members+1); + if (members == NULL) + return; + + for (i = k = 0; i < n_members; ++i) { + if (desc->fields[i].name == PyStructSequence_UnnamedField) + continue; + members[k].name = desc->fields[i].name; + members[k].type = T_OBJECT; + members[k].offset = offsetof(PyStructSequence, ob_item) + + i * sizeof(PyObject*); + members[k].flags = READONLY; + members[k].doc = desc->fields[i].doc; + k++; + } + members[k].name = NULL; + + type->tp_members = members; + + if (PyType_Ready(type) < 0) + return; + Py_INCREF(type); + + dict = type->tp_dict; + PyDict_SetItemString(dict, visible_length_key, + PyInt_FromLong((long) desc->n_in_sequence)); + PyDict_SetItemString(dict, real_length_key, + PyInt_FromLong((long) n_members)); + PyDict_SetItemString(dict, unnamed_fields_key, + PyInt_FromLong((long) n_unnamed_members)); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/tupleobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/tupleobject.c new file mode 100644 index 00000000..369db6ca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/tupleobject.c @@ -0,0 +1,836 @@ + +/* Tuple object implementation */ + +#include "Python.h" + +/* Speed optimization to avoid frequent malloc/free of small tuples */ +#ifndef MAXSAVESIZE +#define MAXSAVESIZE 20 /* Largest tuple to save on free list */ +#endif +#ifndef MAXSAVEDTUPLES +#define MAXSAVEDTUPLES 2000 /* Maximum number of tuples of each size to save */ +#endif + +#if MAXSAVESIZE > 0 +/* Entries 1 up to MAXSAVESIZE are free lists, entry 0 is the empty + tuple () of which at most one instance will be allocated. +*/ +static PyTupleObject *free_tuples[MAXSAVESIZE]; +static int num_free_tuples[MAXSAVESIZE]; +#endif +#ifdef COUNT_ALLOCS +int fast_tuple_allocs; +int tuple_zero_allocs; +#endif + +PyObject * +PyTuple_New(register int size) +{ + register PyTupleObject *op; + if (size < 0) { + PyErr_BadInternalCall(); + return NULL; + } +#if MAXSAVESIZE > 0 + if (size == 0 && free_tuples[0]) { + op = free_tuples[0]; + Py_INCREF(op); +#ifdef COUNT_ALLOCS + tuple_zero_allocs++; +#endif + return (PyObject *) op; + } + if (0 < size && size < MAXSAVESIZE && + (op = free_tuples[size]) != NULL) + { + free_tuples[size] = (PyTupleObject *) op->ob_item[0]; + num_free_tuples[size]--; +#ifdef COUNT_ALLOCS + fast_tuple_allocs++; +#endif + /* Inline PyObject_InitVar */ +#ifdef Py_TRACE_REFS + op->ob_size = size; + op->ob_type = &PyTuple_Type; +#endif + _Py_NewReference((PyObject *)op); + } + else +#endif + { + int nbytes = size * sizeof(PyObject *); + /* Check for overflow */ + if (nbytes / sizeof(PyObject *) != (size_t)size || + (nbytes += sizeof(PyTupleObject) - sizeof(PyObject *)) + <= 0) + { + return PyErr_NoMemory(); + } + op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size); + if (op == NULL) + return NULL; + } + memset(op->ob_item, 0, sizeof(*op->ob_item) * size); +#if MAXSAVESIZE > 0 + if (size == 0) { + free_tuples[0] = op; + ++num_free_tuples[0]; + Py_INCREF(op); /* extra INCREF so that this is never freed */ + } +#endif + _PyObject_GC_TRACK(op); + return (PyObject *) op; +} + +int +PyTuple_Size(register PyObject *op) +{ + if (!PyTuple_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + else + return ((PyTupleObject *)op)->ob_size; +} + +PyObject * +PyTuple_GetItem(register PyObject *op, register int i) +{ + if (!PyTuple_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + if (i < 0 || i >= ((PyTupleObject *)op) -> ob_size) { + PyErr_SetString(PyExc_IndexError, "tuple index out of range"); + return NULL; + } + return ((PyTupleObject *)op) -> ob_item[i]; +} + +int +PyTuple_SetItem(register PyObject *op, register int i, PyObject *newitem) +{ + register PyObject *olditem; + register PyObject **p; + if (!PyTuple_Check(op) || op->ob_refcnt != 1) { + Py_XDECREF(newitem); + PyErr_BadInternalCall(); + return -1; + } + if (i < 0 || i >= ((PyTupleObject *)op) -> ob_size) { + Py_XDECREF(newitem); + PyErr_SetString(PyExc_IndexError, + "tuple assignment index out of range"); + return -1; + } + p = ((PyTupleObject *)op) -> ob_item + i; + olditem = *p; + *p = newitem; + Py_XDECREF(olditem); + return 0; +} + +/* Methods */ + +static void +tupledealloc(register PyTupleObject *op) +{ + register int i; + register int len = op->ob_size; + PyObject_GC_UnTrack(op); + Py_TRASHCAN_SAFE_BEGIN(op) + if (len > 0) { + i = len; + while (--i >= 0) + Py_XDECREF(op->ob_item[i]); +#if MAXSAVESIZE > 0 + if (len < MAXSAVESIZE && + num_free_tuples[len] < MAXSAVEDTUPLES && + op->ob_type == &PyTuple_Type) + { + op->ob_item[0] = (PyObject *) free_tuples[len]; + num_free_tuples[len]++; + free_tuples[len] = op; + goto done; /* return */ + } +#endif + } + op->ob_type->tp_free((PyObject *)op); +done: + Py_TRASHCAN_SAFE_END(op) +} + +static int +tupleprint(PyTupleObject *op, FILE *fp, int flags) +{ + int i; + fprintf(fp, "("); + for (i = 0; i < op->ob_size; i++) { + if (i > 0) + fprintf(fp, ", "); + if (PyObject_Print(op->ob_item[i], fp, 0) != 0) + return -1; + } + if (op->ob_size == 1) + fprintf(fp, ","); + fprintf(fp, ")"); + return 0; +} + +static PyObject * +tuplerepr(PyTupleObject *v) +{ + int i, n; + PyObject *s, *temp; + PyObject *pieces, *result = NULL; + + n = v->ob_size; + if (n == 0) + return PyString_FromString("()"); + + pieces = PyTuple_New(n); + if (pieces == NULL) + return NULL; + + /* Do repr() on each element. */ + for (i = 0; i < n; ++i) { + s = PyObject_Repr(v->ob_item[i]); + if (s == NULL) + goto Done; + PyTuple_SET_ITEM(pieces, i, s); + } + + /* Add "()" decorations to the first and last items. */ + assert(n > 0); + s = PyString_FromString("("); + if (s == NULL) + goto Done; + temp = PyTuple_GET_ITEM(pieces, 0); + PyString_ConcatAndDel(&s, temp); + PyTuple_SET_ITEM(pieces, 0, s); + if (s == NULL) + goto Done; + + s = PyString_FromString(n == 1 ? ",)" : ")"); + if (s == NULL) + goto Done; + temp = PyTuple_GET_ITEM(pieces, n-1); + PyString_ConcatAndDel(&temp, s); + PyTuple_SET_ITEM(pieces, n-1, temp); + if (temp == NULL) + goto Done; + + /* Paste them all together with ", " between. */ + s = PyString_FromString(", "); + if (s == NULL) + goto Done; + result = _PyString_Join(s, pieces); + Py_DECREF(s); + +Done: + Py_DECREF(pieces); + return result; +} + +static long +tuplehash(PyTupleObject *v) +{ + register long x, y; + register int len = v->ob_size; + register PyObject **p; + x = 0x345678L; + p = v->ob_item; + while (--len >= 0) { + y = PyObject_Hash(*p++); + if (y == -1) + return -1; + x = (1000003*x) ^ y; + } + x ^= v->ob_size; + if (x == -1) + x = -2; + return x; +} + +static int +tuplelength(PyTupleObject *a) +{ + return a->ob_size; +} + +static int +tuplecontains(PyTupleObject *a, PyObject *el) +{ + int i, cmp; + + for (i = 0, cmp = 0 ; cmp == 0 && i < a->ob_size; ++i) + cmp = PyObject_RichCompareBool(el, PyTuple_GET_ITEM(a, i), + Py_EQ); + return cmp; +} + +static PyObject * +tupleitem(register PyTupleObject *a, register int i) +{ + if (i < 0 || i >= a->ob_size) { + PyErr_SetString(PyExc_IndexError, "tuple index out of range"); + return NULL; + } + Py_INCREF(a->ob_item[i]); + return a->ob_item[i]; +} + +static PyObject * +tupleslice(register PyTupleObject *a, register int ilow, register int ihigh) +{ + register PyTupleObject *np; + register int i; + if (ilow < 0) + ilow = 0; + if (ihigh > a->ob_size) + ihigh = a->ob_size; + if (ihigh < ilow) + ihigh = ilow; + if (ilow == 0 && ihigh == a->ob_size && PyTuple_CheckExact(a)) { + Py_INCREF(a); + return (PyObject *)a; + } + np = (PyTupleObject *)PyTuple_New(ihigh - ilow); + if (np == NULL) + return NULL; + for (i = ilow; i < ihigh; i++) { + PyObject *v = a->ob_item[i]; + Py_INCREF(v); + np->ob_item[i - ilow] = v; + } + return (PyObject *)np; +} + +PyObject * +PyTuple_GetSlice(PyObject *op, int i, int j) +{ + if (op == NULL || !PyTuple_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return tupleslice((PyTupleObject *)op, i, j); +} + +static PyObject * +tupleconcat(register PyTupleObject *a, register PyObject *bb) +{ + register int size; + register int i; + PyTupleObject *np; + if (!PyTuple_Check(bb)) { + PyErr_Format(PyExc_TypeError, + "can only concatenate tuple (not \"%.200s\") to tuple", + bb->ob_type->tp_name); + return NULL; + } +#define b ((PyTupleObject *)bb) + size = a->ob_size + b->ob_size; + if (size < 0) + return PyErr_NoMemory(); + np = (PyTupleObject *) PyTuple_New(size); + if (np == NULL) { + return NULL; + } + for (i = 0; i < a->ob_size; i++) { + PyObject *v = a->ob_item[i]; + Py_INCREF(v); + np->ob_item[i] = v; + } + for (i = 0; i < b->ob_size; i++) { + PyObject *v = b->ob_item[i]; + Py_INCREF(v); + np->ob_item[i + a->ob_size] = v; + } + return (PyObject *)np; +#undef b +} + +static PyObject * +tuplerepeat(PyTupleObject *a, int n) +{ + int i, j; + int size; + PyTupleObject *np; + PyObject **p; + if (n < 0) + n = 0; + if (a->ob_size == 0 || n == 1) { + if (PyTuple_CheckExact(a)) { + /* Since tuples are immutable, we can return a shared + copy in this case */ + Py_INCREF(a); + return (PyObject *)a; + } + if (a->ob_size == 0) + return PyTuple_New(0); + } + size = a->ob_size * n; + if (size/a->ob_size != n) + return PyErr_NoMemory(); + np = (PyTupleObject *) PyTuple_New(size); + if (np == NULL) + return NULL; + p = np->ob_item; + for (i = 0; i < n; i++) { + for (j = 0; j < a->ob_size; j++) { + *p = a->ob_item[j]; + Py_INCREF(*p); + p++; + } + } + return (PyObject *) np; +} + +static int +tupletraverse(PyTupleObject *o, visitproc visit, void *arg) +{ + int i, err; + PyObject *x; + + for (i = o->ob_size; --i >= 0; ) { + x = o->ob_item[i]; + if (x != NULL) { + err = visit(x, arg); + if (err) + return err; + } + } + return 0; +} + +static PyObject * +tuplerichcompare(PyObject *v, PyObject *w, int op) +{ + PyTupleObject *vt, *wt; + int i; + int vlen, wlen; + + if (!PyTuple_Check(v) || !PyTuple_Check(w)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + vt = (PyTupleObject *)v; + wt = (PyTupleObject *)w; + + vlen = vt->ob_size; + wlen = wt->ob_size; + + /* Note: the corresponding code for lists has an "early out" test + * here when op is EQ or NE and the lengths differ. That pays there, + * but Tim was unable to find any real code where EQ/NE tuple + * compares don't have the same length, so testing for it here would + * have cost without benefit. + */ + + /* Search for the first index where items are different. + * Note that because tuples are immutable, it's safe to reuse + * vlen and wlen across the comparison calls. + */ + for (i = 0; i < vlen && i < wlen; i++) { + int k = PyObject_RichCompareBool(vt->ob_item[i], + wt->ob_item[i], Py_EQ); + if (k < 0) + return NULL; + if (!k) + break; + } + + if (i >= vlen || i >= wlen) { + /* No more items to compare -- compare sizes */ + int cmp; + PyObject *res; + switch (op) { + case Py_LT: cmp = vlen < wlen; break; + case Py_LE: cmp = vlen <= wlen; break; + case Py_EQ: cmp = vlen == wlen; break; + case Py_NE: cmp = vlen != wlen; break; + case Py_GT: cmp = vlen > wlen; break; + case Py_GE: cmp = vlen >= wlen; break; + default: return NULL; /* cannot happen */ + } + if (cmp) + res = Py_True; + else + res = Py_False; + Py_INCREF(res); + return res; + } + + /* We have an item that differs -- shortcuts for EQ/NE */ + if (op == Py_EQ) { + Py_INCREF(Py_False); + return Py_False; + } + if (op == Py_NE) { + Py_INCREF(Py_True); + return Py_True; + } + + /* Compare the final item again using the proper operator */ + return PyObject_RichCompare(vt->ob_item[i], wt->ob_item[i], op); +} + +static PyObject * +tuple_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + +static PyObject * +tuple_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *arg = NULL; + static char *kwlist[] = {"sequence", 0}; + + if (type != &PyTuple_Type) + return tuple_subtype_new(type, args, kwds); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:tuple", kwlist, &arg)) + return NULL; + + if (arg == NULL) + return PyTuple_New(0); + else + return PySequence_Tuple(arg); +} + +static PyObject * +tuple_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *tmp, *new, *item; + int i, n; + + assert(PyType_IsSubtype(type, &PyTuple_Type)); + tmp = tuple_new(&PyTuple_Type, args, kwds); + if (tmp == NULL) + return NULL; + assert(PyTuple_Check(tmp)); + new = type->tp_alloc(type, n = PyTuple_GET_SIZE(tmp)); + if (new == NULL) + return NULL; + for (i = 0; i < n; i++) { + item = PyTuple_GET_ITEM(tmp, i); + Py_INCREF(item); + PyTuple_SET_ITEM(new, i, item); + } + Py_DECREF(tmp); + return new; +} + +PyDoc_STRVAR(tuple_doc, +"tuple() -> an empty tuple\n" +"tuple(sequence) -> tuple initialized from sequence's items\n" +"\n" +"If the argument is a tuple, the return value is the same object."); + +static PySequenceMethods tuple_as_sequence = { + (inquiry)tuplelength, /* sq_length */ + (binaryfunc)tupleconcat, /* sq_concat */ + (intargfunc)tuplerepeat, /* sq_repeat */ + (intargfunc)tupleitem, /* sq_item */ + (intintargfunc)tupleslice, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)tuplecontains, /* sq_contains */ +}; + +static PyObject* +tuplesubscript(PyTupleObject* self, PyObject* item) +{ + if (PyInt_Check(item)) { + long i = PyInt_AS_LONG(item); + if (i < 0) + i += PyTuple_GET_SIZE(self); + return tupleitem(self, i); + } + else if (PyLong_Check(item)) { + long i = PyLong_AsLong(item); + if (i == -1 && PyErr_Occurred()) + return NULL; + if (i < 0) + i += PyTuple_GET_SIZE(self); + return tupleitem(self, i); + } + else if (PySlice_Check(item)) { + int start, stop, step, slicelength, cur, i; + PyObject* result; + PyObject* it; + + if (PySlice_GetIndicesEx((PySliceObject*)item, + PyTuple_GET_SIZE(self), + &start, &stop, &step, &slicelength) < 0) { + return NULL; + } + + if (slicelength <= 0) { + return PyTuple_New(0); + } + else { + result = PyTuple_New(slicelength); + + for (cur = start, i = 0; i < slicelength; + cur += step, i++) { + it = PyTuple_GET_ITEM(self, cur); + Py_INCREF(it); + PyTuple_SET_ITEM(result, i, it); + } + + return result; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "tuple indices must be integers"); + return NULL; + } +} + +static PyObject * +tuple_getnewargs(PyTupleObject *v) +{ + return Py_BuildValue("(N)", tupleslice(v, 0, v->ob_size)); + +} + +static PyMethodDef tuple_methods[] = { + {"__getnewargs__", (PyCFunction)tuple_getnewargs, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyMappingMethods tuple_as_mapping = { + (inquiry)tuplelength, + (binaryfunc)tuplesubscript, + 0 +}; + +static PyObject *tuple_iter(PyObject *seq); + +PyTypeObject PyTuple_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "tuple", + sizeof(PyTupleObject) - sizeof(PyObject *), + sizeof(PyObject *), + (destructor)tupledealloc, /* tp_dealloc */ + (printfunc)tupleprint, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)tuplerepr, /* tp_repr */ + 0, /* tp_as_number */ + &tuple_as_sequence, /* tp_as_sequence */ + &tuple_as_mapping, /* tp_as_mapping */ + (hashfunc)tuplehash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + tuple_doc, /* tp_doc */ + (traverseproc)tupletraverse, /* tp_traverse */ + 0, /* tp_clear */ + tuplerichcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + tuple_iter, /* tp_iter */ + 0, /* tp_iternext */ + tuple_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + tuple_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + +/* The following function breaks the notion that tuples are immutable: + it changes the size of a tuple. We get away with this only if there + is only one module referencing the object. You can also think of it + as creating a new tuple object and destroying the old one, only more + efficiently. In any case, don't use this if the tuple may already be + known to some other part of the code. */ + +int +_PyTuple_Resize(PyObject **pv, int newsize) +{ + register PyTupleObject *v; + register PyTupleObject *sv; + int i; + int oldsize; + + v = (PyTupleObject *) *pv; + if (v == NULL || v->ob_type != &PyTuple_Type || + (v->ob_size != 0 && v->ob_refcnt != 1)) { + *pv = 0; + Py_XDECREF(v); + PyErr_BadInternalCall(); + return -1; + } + oldsize = v->ob_size; + if (oldsize == newsize) + return 0; + + if (oldsize == 0) { + /* Empty tuples are often shared, so we should never + resize them in-place even if we do own the only + (current) reference */ + Py_DECREF(v); + *pv = PyTuple_New(newsize); + return *pv == NULL ? -1 : 0; + } + + /* XXX UNREF/NEWREF interface should be more symmetrical */ + _Py_DEC_REFTOTAL; + _PyObject_GC_UNTRACK(v); + _Py_ForgetReference((PyObject *) v); + /* DECREF items deleted by shrinkage */ + for (i = newsize; i < oldsize; i++) { + Py_XDECREF(v->ob_item[i]); + v->ob_item[i] = NULL; + } + sv = PyObject_GC_Resize(PyTupleObject, v, newsize); + if (sv == NULL) { + *pv = NULL; + PyObject_GC_Del(v); + return -1; + } + _Py_NewReference((PyObject *) sv); + /* Zero out items added by growing */ + if (newsize > oldsize) + memset(&sv->ob_item[oldsize], 0, + sizeof(*sv->ob_item) * (newsize - oldsize)); + *pv = (PyObject *) sv; + _PyObject_GC_TRACK(sv); + return 0; +} + +void +PyTuple_Fini(void) +{ +#if MAXSAVESIZE > 0 + int i; + + Py_XDECREF(free_tuples[0]); + free_tuples[0] = NULL; + + for (i = 1; i < MAXSAVESIZE; i++) { + PyTupleObject *p, *q; + p = free_tuples[i]; + free_tuples[i] = NULL; + while (p) { + q = p; + p = (PyTupleObject *)(p->ob_item[0]); + PyObject_GC_Del(q); + } + } +#endif +} + +/*********************** Tuple Iterator **************************/ + +typedef struct { + PyObject_HEAD + long it_index; + PyTupleObject *it_seq; /* Set to NULL when iterator is exhausted */ +} tupleiterobject; + +PyTypeObject PyTupleIter_Type; + +static PyObject * +tuple_iter(PyObject *seq) +{ + tupleiterobject *it; + + if (!PyTuple_Check(seq)) { + PyErr_BadInternalCall(); + return NULL; + } + it = PyObject_GC_New(tupleiterobject, &PyTupleIter_Type); + if (it == NULL) + return NULL; + it->it_index = 0; + Py_INCREF(seq); + it->it_seq = (PyTupleObject *)seq; + _PyObject_GC_TRACK(it); + return (PyObject *)it; +} + +static void +tupleiter_dealloc(tupleiterobject *it) +{ + _PyObject_GC_UNTRACK(it); + Py_XDECREF(it->it_seq); + PyObject_GC_Del(it); +} + +static int +tupleiter_traverse(tupleiterobject *it, visitproc visit, void *arg) +{ + if (it->it_seq == NULL) + return 0; + return visit((PyObject *)it->it_seq, arg); +} + +static PyObject * +tupleiter_next(tupleiterobject *it) +{ + PyTupleObject *seq; + PyObject *item; + + assert(it != NULL); + seq = it->it_seq; + if (seq == NULL) + return NULL; + assert(PyTuple_Check(seq)); + + if (it->it_index < PyTuple_GET_SIZE(seq)) { + item = PyTuple_GET_ITEM(seq, it->it_index); + ++it->it_index; + Py_INCREF(item); + return item; + } + + Py_DECREF(seq); + it->it_seq = NULL; + return NULL; +} + +PyTypeObject PyTupleIter_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "tupleiterator", /* tp_name */ + sizeof(tupleiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)tupleiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)tupleiter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)tupleiter_next, /* tp_iternext */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/typeobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/typeobject.c new file mode 100644 index 00000000..3c0e5894 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/typeobject.c @@ -0,0 +1,5707 @@ +/* Type object implementation */ + +#include "Python.h" +#include "structmember.h" + +#include + +static PyMemberDef type_members[] = { + {"__basicsize__", T_INT, offsetof(PyTypeObject,tp_basicsize),READONLY}, + {"__itemsize__", T_INT, offsetof(PyTypeObject, tp_itemsize), READONLY}, + {"__flags__", T_LONG, offsetof(PyTypeObject, tp_flags), READONLY}, + {"__weakrefoffset__", T_LONG, + offsetof(PyTypeObject, tp_weaklistoffset), READONLY}, + {"__base__", T_OBJECT, offsetof(PyTypeObject, tp_base), READONLY}, + {"__dictoffset__", T_LONG, + offsetof(PyTypeObject, tp_dictoffset), READONLY}, + {"__mro__", T_OBJECT, offsetof(PyTypeObject, tp_mro), READONLY}, + {0} +}; + +static PyObject * +type_name(PyTypeObject *type, void *context) +{ + char *s; + + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + PyHeapTypeObject* et = (PyHeapTypeObject*)type; + + Py_INCREF(et->name); + return et->name; + } + else { + s = strrchr(type->tp_name, '.'); + if (s == NULL) + s = type->tp_name; + else + s++; + return PyString_FromString(s); + } +} + +static int +type_set_name(PyTypeObject *type, PyObject *value, void *context) +{ + PyHeapTypeObject* et; + + if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { + PyErr_Format(PyExc_TypeError, + "can't set %s.__name__", type->tp_name); + return -1; + } + if (!value) { + PyErr_Format(PyExc_TypeError, + "can't delete %s.__name__", type->tp_name); + return -1; + } + if (!PyString_Check(value)) { + PyErr_Format(PyExc_TypeError, + "can only assign string to %s.__name__, not '%s'", + type->tp_name, value->ob_type->tp_name); + return -1; + } + if (strlen(PyString_AS_STRING(value)) + != (size_t)PyString_GET_SIZE(value)) { + PyErr_Format(PyExc_ValueError, + "__name__ must not contain null bytes"); + return -1; + } + + et = (PyHeapTypeObject*)type; + + Py_INCREF(value); + + Py_DECREF(et->name); + et->name = value; + + type->tp_name = PyString_AS_STRING(value); + + return 0; +} + +static PyObject * +type_module(PyTypeObject *type, void *context) +{ + PyObject *mod; + char *s; + + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + mod = PyDict_GetItemString(type->tp_dict, "__module__"); + Py_XINCREF(mod); + return mod; + } + else { + s = strrchr(type->tp_name, '.'); + if (s != NULL) + return PyString_FromStringAndSize( + type->tp_name, (int)(s - type->tp_name)); + return PyString_FromString("__builtin__"); + } +} + +static int +type_set_module(PyTypeObject *type, PyObject *value, void *context) +{ + if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { + PyErr_Format(PyExc_TypeError, + "can't set %s.__module__", type->tp_name); + return -1; + } + if (!value) { + PyErr_Format(PyExc_TypeError, + "can't delete %s.__module__", type->tp_name); + return -1; + } + + return PyDict_SetItemString(type->tp_dict, "__module__", value); +} + +static PyObject * +type_get_bases(PyTypeObject *type, void *context) +{ + Py_INCREF(type->tp_bases); + return type->tp_bases; +} + +static PyTypeObject *best_base(PyObject *); +static int mro_internal(PyTypeObject *); +static int compatible_for_assignment(PyTypeObject *, PyTypeObject *, char *); +static int add_subclass(PyTypeObject*, PyTypeObject*); +static void remove_subclass(PyTypeObject *, PyTypeObject *); +static void update_all_slots(PyTypeObject *); + +typedef int (*update_callback)(PyTypeObject *, void *); +static int update_subclasses(PyTypeObject *type, PyObject *name, + update_callback callback, void *data); +static int recurse_down_subclasses(PyTypeObject *type, PyObject *name, + update_callback callback, void *data); + +static int +mro_subclasses(PyTypeObject *type, PyObject* temp) +{ + PyTypeObject *subclass; + PyObject *ref, *subclasses, *old_mro; + int i, n; + + subclasses = type->tp_subclasses; + if (subclasses == NULL) + return 0; + assert(PyList_Check(subclasses)); + n = PyList_GET_SIZE(subclasses); + for (i = 0; i < n; i++) { + ref = PyList_GET_ITEM(subclasses, i); + assert(PyWeakref_CheckRef(ref)); + subclass = (PyTypeObject *)PyWeakref_GET_OBJECT(ref); + assert(subclass != NULL); + if ((PyObject *)subclass == Py_None) + continue; + assert(PyType_Check(subclass)); + old_mro = subclass->tp_mro; + if (mro_internal(subclass) < 0) { + subclass->tp_mro = old_mro; + return -1; + } + else { + PyObject* tuple; + tuple = Py_BuildValue("OO", subclass, old_mro); + Py_DECREF(old_mro); + if (!tuple) + return -1; + if (PyList_Append(temp, tuple) < 0) + return -1; + Py_DECREF(tuple); + } + if (mro_subclasses(subclass, temp) < 0) + return -1; + } + return 0; +} + +static int +type_set_bases(PyTypeObject *type, PyObject *value, void *context) +{ + int i, r = 0; + PyObject *ob, *temp; + PyTypeObject *new_base, *old_base; + PyObject *old_bases, *old_mro; + + if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { + PyErr_Format(PyExc_TypeError, + "can't set %s.__bases__", type->tp_name); + return -1; + } + if (!value) { + PyErr_Format(PyExc_TypeError, + "can't delete %s.__bases__", type->tp_name); + return -1; + } + if (!PyTuple_Check(value)) { + PyErr_Format(PyExc_TypeError, + "can only assign tuple to %s.__bases__, not %s", + type->tp_name, value->ob_type->tp_name); + return -1; + } + if (PyTuple_GET_SIZE(value) == 0) { + PyErr_Format(PyExc_TypeError, + "can only assign non-empty tuple to %s.__bases__, not ()", + type->tp_name); + return -1; + } + for (i = 0; i < PyTuple_GET_SIZE(value); i++) { + ob = PyTuple_GET_ITEM(value, i); + if (!PyClass_Check(ob) && !PyType_Check(ob)) { + PyErr_Format( + PyExc_TypeError, + "%s.__bases__ must be tuple of old- or new-style classes, not '%s'", + type->tp_name, ob->ob_type->tp_name); + return -1; + } + if (PyType_Check(ob)) { + if (PyType_IsSubtype((PyTypeObject*)ob, type)) { + PyErr_SetString(PyExc_TypeError, + "a __bases__ item causes an inheritance cycle"); + return -1; + } + } + } + + new_base = best_base(value); + + if (!new_base) { + return -1; + } + + if (!compatible_for_assignment(type->tp_base, new_base, "__bases__")) + return -1; + + Py_INCREF(new_base); + Py_INCREF(value); + + old_bases = type->tp_bases; + old_base = type->tp_base; + old_mro = type->tp_mro; + + type->tp_bases = value; + type->tp_base = new_base; + + if (mro_internal(type) < 0) { + goto bail; + } + + temp = PyList_New(0); + if (!temp) + goto bail; + + r = mro_subclasses(type, temp); + + if (r < 0) { + for (i = 0; i < PyList_Size(temp); i++) { + PyTypeObject* cls; + PyObject* mro; + PyArg_ParseTuple(PyList_GET_ITEM(temp, i), + "OO", &cls, &mro); + Py_DECREF(cls->tp_mro); + cls->tp_mro = mro; + Py_INCREF(cls->tp_mro); + } + Py_DECREF(temp); + goto bail; + } + + Py_DECREF(temp); + + /* any base that was in __bases__ but now isn't, we + need to remove |type| from its tp_subclasses. + conversely, any class now in __bases__ that wasn't + needs to have |type| added to its subclasses. */ + + /* for now, sod that: just remove from all old_bases, + add to all new_bases */ + + for (i = PyTuple_GET_SIZE(old_bases) - 1; i >= 0; i--) { + ob = PyTuple_GET_ITEM(old_bases, i); + if (PyType_Check(ob)) { + remove_subclass( + (PyTypeObject*)ob, type); + } + } + + for (i = PyTuple_GET_SIZE(value) - 1; i >= 0; i--) { + ob = PyTuple_GET_ITEM(value, i); + if (PyType_Check(ob)) { + if (add_subclass((PyTypeObject*)ob, type) < 0) + r = -1; + } + } + + update_all_slots(type); + + Py_DECREF(old_bases); + Py_DECREF(old_base); + Py_DECREF(old_mro); + + return r; + + bail: + Py_DECREF(type->tp_bases); + Py_DECREF(type->tp_base); + if (type->tp_mro != old_mro) { + Py_DECREF(type->tp_mro); + } + + type->tp_bases = old_bases; + type->tp_base = old_base; + type->tp_mro = old_mro; + + return -1; +} + +static PyObject * +type_dict(PyTypeObject *type, void *context) +{ + if (type->tp_dict == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + return PyDictProxy_New(type->tp_dict); +} + +static PyObject * +type_get_doc(PyTypeObject *type, void *context) +{ + PyObject *result; + if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE) && type->tp_doc != NULL) + return PyString_FromString(type->tp_doc); + result = PyDict_GetItemString(type->tp_dict, "__doc__"); + if (result == NULL) { + result = Py_None; + Py_INCREF(result); + } + else if (result->ob_type->tp_descr_get) { + result = result->ob_type->tp_descr_get(result, NULL, + (PyObject *)type); + } + else { + Py_INCREF(result); + } + return result; +} + +static PyGetSetDef type_getsets[] = { + {"__name__", (getter)type_name, (setter)type_set_name, NULL}, + {"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL}, + {"__module__", (getter)type_module, (setter)type_set_module, NULL}, + {"__dict__", (getter)type_dict, NULL, NULL}, + {"__doc__", (getter)type_get_doc, NULL, NULL}, + {0} +}; + +static int +type_compare(PyObject *v, PyObject *w) +{ + /* This is called with type objects only. So we + can just compare the addresses. */ + Py_uintptr_t vv = (Py_uintptr_t)v; + Py_uintptr_t ww = (Py_uintptr_t)w; + return (vv < ww) ? -1 : (vv > ww) ? 1 : 0; +} + +static PyObject * +type_repr(PyTypeObject *type) +{ + PyObject *mod, *name, *rtn; + char *kind; + + mod = type_module(type, NULL); + if (mod == NULL) + PyErr_Clear(); + else if (!PyString_Check(mod)) { + Py_DECREF(mod); + mod = NULL; + } + name = type_name(type, NULL); + if (name == NULL) + return NULL; + + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) + kind = "class"; + else + kind = "type"; + + if (mod != NULL && strcmp(PyString_AS_STRING(mod), "__builtin__")) { + rtn = PyString_FromFormat("<%s '%s.%s'>", + kind, + PyString_AS_STRING(mod), + PyString_AS_STRING(name)); + } + else + rtn = PyString_FromFormat("<%s '%s'>", kind, type->tp_name); + + Py_XDECREF(mod); + Py_DECREF(name); + return rtn; +} + +static PyObject * +type_call(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *obj; + + if (type->tp_new == NULL) { + PyErr_Format(PyExc_TypeError, + "cannot create '%.100s' instances", + type->tp_name); + return NULL; + } + + obj = type->tp_new(type, args, kwds); + if (obj != NULL) { + /* Ugly exception: when the call was type(something), + don't call tp_init on the result. */ + if (type == &PyType_Type && + PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 && + (kwds == NULL || + (PyDict_Check(kwds) && PyDict_Size(kwds) == 0))) + return obj; + /* If the returned object is not an instance of type, + it won't be initialized. */ + if (!PyType_IsSubtype(obj->ob_type, type)) + return obj; + type = obj->ob_type; + if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) && + type->tp_init != NULL && + type->tp_init(obj, args, kwds) < 0) { + Py_DECREF(obj); + obj = NULL; + } + } + return obj; +} + +PyObject * +PyType_GenericAlloc(PyTypeObject *type, int nitems) +{ + PyObject *obj; + const size_t size = _PyObject_VAR_SIZE(type, nitems+1); + /* note that we need to add one, for the sentinel */ + + if (PyType_IS_GC(type)) + obj = _PyObject_GC_Malloc(size); + else + obj = PyObject_MALLOC(size); + + if (obj == NULL) + return PyErr_NoMemory(); + + memset(obj, '\0', size); + + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) + Py_INCREF(type); + + if (type->tp_itemsize == 0) + PyObject_INIT(obj, type); + else + (void) PyObject_INIT_VAR((PyVarObject *)obj, type, nitems); + + if (PyType_IS_GC(type)) + _PyObject_GC_TRACK(obj); + return obj; +} + +PyObject * +PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + return type->tp_alloc(type, 0); +} + +/* Helpers for subtyping */ + +static int +traverse_slots(PyTypeObject *type, PyObject *self, visitproc visit, void *arg) +{ + int i, n; + PyMemberDef *mp; + + n = type->ob_size; + mp = PyHeapType_GET_MEMBERS((PyHeapTypeObject *)type); + for (i = 0; i < n; i++, mp++) { + if (mp->type == T_OBJECT_EX) { + char *addr = (char *)self + mp->offset; + PyObject *obj = *(PyObject **)addr; + if (obj != NULL) { + int err = visit(obj, arg); + if (err) + return err; + } + } + } + return 0; +} + +static int +subtype_traverse(PyObject *self, visitproc visit, void *arg) +{ + PyTypeObject *type, *base; + traverseproc basetraverse; + + /* Find the nearest base with a different tp_traverse, + and traverse slots while we're at it */ + type = self->ob_type; + base = type; + while ((basetraverse = base->tp_traverse) == subtype_traverse) { + if (base->ob_size) { + int err = traverse_slots(base, self, visit, arg); + if (err) + return err; + } + base = base->tp_base; + assert(base); + } + + if (type->tp_dictoffset != base->tp_dictoffset) { + PyObject **dictptr = _PyObject_GetDictPtr(self); + if (dictptr && *dictptr) { + int err = visit(*dictptr, arg); + if (err) + return err; + } + } + + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + /* For a heaptype, the instances count as references + to the type. Traverse the type so the collector + can find cycles involving this link. */ + int err = visit((PyObject *)type, arg); + if (err) + return err; + } + + if (basetraverse) + return basetraverse(self, visit, arg); + return 0; +} + +static void +clear_slots(PyTypeObject *type, PyObject *self) +{ + int i, n; + PyMemberDef *mp; + + n = type->ob_size; + mp = PyHeapType_GET_MEMBERS((PyHeapTypeObject *)type); + for (i = 0; i < n; i++, mp++) { + if (mp->type == T_OBJECT_EX && !(mp->flags & READONLY)) { + char *addr = (char *)self + mp->offset; + PyObject *obj = *(PyObject **)addr; + if (obj != NULL) { + Py_DECREF(obj); + *(PyObject **)addr = NULL; + } + } + } +} + +static int +subtype_clear(PyObject *self) +{ + PyTypeObject *type, *base; + inquiry baseclear; + + /* Find the nearest base with a different tp_clear + and clear slots while we're at it */ + type = self->ob_type; + base = type; + while ((baseclear = base->tp_clear) == subtype_clear) { + if (base->ob_size) + clear_slots(base, self); + base = base->tp_base; + assert(base); + } + + /* There's no need to clear the instance dict (if any); + the collector will call its tp_clear handler. */ + + if (baseclear) + return baseclear(self); + return 0; +} + +static void +subtype_dealloc(PyObject *self) +{ + PyTypeObject *type, *base; + destructor basedealloc; + + /* Extract the type; we expect it to be a heap type */ + type = self->ob_type; + assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE); + + /* Test whether the type has GC exactly once */ + + if (!PyType_IS_GC(type)) { + /* It's really rare to find a dynamic type that doesn't have + GC; it can only happen when deriving from 'object' and not + adding any slots or instance variables. This allows + certain simplifications: there's no need to call + clear_slots(), or DECREF the dict, or clear weakrefs. */ + + /* Maybe call finalizer; exit early if resurrected */ + if (type->tp_del) { + type->tp_del(self); + if (self->ob_refcnt > 0) + return; + } + + /* Find the nearest base with a different tp_dealloc */ + base = type; + while ((basedealloc = base->tp_dealloc) == subtype_dealloc) { + assert(base->ob_size == 0); + base = base->tp_base; + assert(base); + } + + /* Call the base tp_dealloc() */ + assert(basedealloc); + basedealloc(self); + + /* Can't reference self beyond this point */ + Py_DECREF(type); + + /* Done */ + return; + } + + /* We get here only if the type has GC */ + + /* UnTrack and re-Track around the trashcan macro, alas */ + /* See explanation at end of function for full disclosure */ + PyObject_GC_UnTrack(self); + ++_PyTrash_delete_nesting; + Py_TRASHCAN_SAFE_BEGIN(self); + --_PyTrash_delete_nesting; + /* DO NOT restore GC tracking at this point. weakref callbacks + * (if any, and whether directly here or indirectly in something we + * call) may trigger GC, and if self is tracked at that point, it + * will look like trash to GC and GC will try to delete self again. + */ + + /* Find the nearest base with a different tp_dealloc */ + base = type; + while ((basedealloc = base->tp_dealloc) == subtype_dealloc) { + base = base->tp_base; + assert(base); + } + + /* If we added a weaklist, we clear it. Do this *before* calling + the finalizer (__del__), clearing slots, or clearing the instance + dict. */ + + if (type->tp_weaklistoffset && !base->tp_weaklistoffset) + PyObject_ClearWeakRefs(self); + + /* Maybe call finalizer; exit early if resurrected */ + if (type->tp_del) { + _PyObject_GC_TRACK(self); + type->tp_del(self); + if (self->ob_refcnt > 0) + goto endlabel; /* resurrected */ + else + _PyObject_GC_UNTRACK(self); + } + + /* Clear slots up to the nearest base with a different tp_dealloc */ + base = type; + while ((basedealloc = base->tp_dealloc) == subtype_dealloc) { + if (base->ob_size) + clear_slots(base, self); + base = base->tp_base; + assert(base); + } + + /* If we added a dict, DECREF it */ + if (type->tp_dictoffset && !base->tp_dictoffset) { + PyObject **dictptr = _PyObject_GetDictPtr(self); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + if (dict != NULL) { + Py_DECREF(dict); + *dictptr = NULL; + } + } + } + + /* Call the base tp_dealloc(); first retrack self if + * basedealloc knows about gc. + */ + if (PyType_IS_GC(base)) + _PyObject_GC_TRACK(self); + assert(basedealloc); + basedealloc(self); + + /* Can't reference self beyond this point */ + Py_DECREF(type); + + endlabel: + ++_PyTrash_delete_nesting; + Py_TRASHCAN_SAFE_END(self); + --_PyTrash_delete_nesting; + + /* Explanation of the weirdness around the trashcan macros: + + Q. What do the trashcan macros do? + + A. Read the comment titled "Trashcan mechanism" in object.h. + For one, this explains why there must be a call to GC-untrack + before the trashcan begin macro. Without understanding the + trashcan code, the answers to the following questions don't make + sense. + + Q. Why do we GC-untrack before the trashcan and then immediately + GC-track again afterward? + + A. In the case that the base class is GC-aware, the base class + probably GC-untracks the object. If it does that using the + UNTRACK macro, this will crash when the object is already + untracked. Because we don't know what the base class does, the + only safe thing is to make sure the object is tracked when we + call the base class dealloc. But... The trashcan begin macro + requires that the object is *untracked* before it is called. So + the dance becomes: + + GC untrack + trashcan begin + GC track + + Q. Why did the last question say "immediately GC-track again"? + It's nowhere near immediately. + + A. Because the code *used* to re-track immediately. Bad Idea. + self has a refcount of 0, and if gc ever gets its hands on it + (which can happen if any weakref callback gets invoked), it + looks like trash to gc too, and gc also tries to delete self + then. But we're already deleting self. Double dealloction is + a subtle disaster. + + Q. Why the bizarre (net-zero) manipulation of + _PyTrash_delete_nesting around the trashcan macros? + + A. Some base classes (e.g. list) also use the trashcan mechanism. + The following scenario used to be possible: + + - suppose the trashcan level is one below the trashcan limit + + - subtype_dealloc() is called + + - the trashcan limit is not yet reached, so the trashcan level + is incremented and the code between trashcan begin and end is + executed + + - this destroys much of the object's contents, including its + slots and __dict__ + + - basedealloc() is called; this is really list_dealloc(), or + some other type which also uses the trashcan macros + + - the trashcan limit is now reached, so the object is put on the + trashcan's to-be-deleted-later list + + - basedealloc() returns + + - subtype_dealloc() decrefs the object's type + + - subtype_dealloc() returns + + - later, the trashcan code starts deleting the objects from its + to-be-deleted-later list + + - subtype_dealloc() is called *AGAIN* for the same object + + - at the very least (if the destroyed slots and __dict__ don't + cause problems) the object's type gets decref'ed a second + time, which is *BAD*!!! + + The remedy is to make sure that if the code between trashcan + begin and end in subtype_dealloc() is called, the code between + trashcan begin and end in basedealloc() will also be called. + This is done by decrementing the level after passing into the + trashcan block, and incrementing it just before leaving the + block. + + But now it's possible that a chain of objects consisting solely + of objects whose deallocator is subtype_dealloc() will defeat + the trashcan mechanism completely: the decremented level means + that the effective level never reaches the limit. Therefore, we + *increment* the level *before* entering the trashcan block, and + matchingly decrement it after leaving. This means the trashcan + code will trigger a little early, but that's no big deal. + + Q. Are there any live examples of code in need of all this + complexity? + + A. Yes. See SF bug 668433 for code that crashed (when Python was + compiled in debug mode) before the trashcan level manipulations + were added. For more discussion, see SF patches 581742, 575073 + and bug 574207. + */ +} + +static PyTypeObject *solid_base(PyTypeObject *type); + +/* type test with subclassing support */ + +int +PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b) +{ + PyObject *mro; + + if (!(a->tp_flags & Py_TPFLAGS_HAVE_CLASS)) + return b == a || b == &PyBaseObject_Type; + + mro = a->tp_mro; + if (mro != NULL) { + /* Deal with multiple inheritance without recursion + by walking the MRO tuple */ + int i, n; + assert(PyTuple_Check(mro)); + n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b) + return 1; + } + return 0; + } + else { + /* a is not completely initilized yet; follow tp_base */ + do { + if (a == b) + return 1; + a = a->tp_base; + } while (a != NULL); + return b == &PyBaseObject_Type; + } +} + +/* Internal routines to do a method lookup in the type + without looking in the instance dictionary + (so we can't use PyObject_GetAttr) but still binding + it to the instance. The arguments are the object, + the method name as a C string, and the address of a + static variable used to cache the interned Python string. + + Two variants: + + - lookup_maybe() returns NULL without raising an exception + when the _PyType_Lookup() call fails; + + - lookup_method() always raises an exception upon errors. +*/ + +static PyObject * +lookup_maybe(PyObject *self, char *attrstr, PyObject **attrobj) +{ + PyObject *res; + + if (*attrobj == NULL) { + *attrobj = PyString_InternFromString(attrstr); + if (*attrobj == NULL) + return NULL; + } + res = _PyType_Lookup(self->ob_type, *attrobj); + if (res != NULL) { + descrgetfunc f; + if ((f = res->ob_type->tp_descr_get) == NULL) + Py_INCREF(res); + else + res = f(res, self, (PyObject *)(self->ob_type)); + } + return res; +} + +static PyObject * +lookup_method(PyObject *self, char *attrstr, PyObject **attrobj) +{ + PyObject *res = lookup_maybe(self, attrstr, attrobj); + if (res == NULL && !PyErr_Occurred()) + PyErr_SetObject(PyExc_AttributeError, *attrobj); + return res; +} + +/* A variation of PyObject_CallMethod that uses lookup_method() + instead of PyObject_GetAttrString(). This uses the same convention + as lookup_method to cache the interned name string object. */ + +static PyObject * +call_method(PyObject *o, char *name, PyObject **nameobj, char *format, ...) +{ + va_list va; + PyObject *args, *func = 0, *retval; + va_start(va, format); + + func = lookup_maybe(o, name, nameobj); + if (func == NULL) { + va_end(va); + if (!PyErr_Occurred()) + PyErr_SetObject(PyExc_AttributeError, *nameobj); + return NULL; + } + + if (format && *format) + args = Py_VaBuildValue(format, va); + else + args = PyTuple_New(0); + + va_end(va); + + if (args == NULL) + return NULL; + + assert(PyTuple_Check(args)); + retval = PyObject_Call(func, args, NULL); + + Py_DECREF(args); + Py_DECREF(func); + + return retval; +} + +/* Clone of call_method() that returns NotImplemented when the lookup fails. */ + +static PyObject * +call_maybe(PyObject *o, char *name, PyObject **nameobj, char *format, ...) +{ + va_list va; + PyObject *args, *func = 0, *retval; + va_start(va, format); + + func = lookup_maybe(o, name, nameobj); + if (func == NULL) { + va_end(va); + if (!PyErr_Occurred()) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return NULL; + } + + if (format && *format) + args = Py_VaBuildValue(format, va); + else + args = PyTuple_New(0); + + va_end(va); + + if (args == NULL) + return NULL; + + assert(PyTuple_Check(args)); + retval = PyObject_Call(func, args, NULL); + + Py_DECREF(args); + Py_DECREF(func); + + return retval; +} + +static int +fill_classic_mro(PyObject *mro, PyObject *cls) +{ + PyObject *bases, *base; + int i, n; + + assert(PyList_Check(mro)); + assert(PyClass_Check(cls)); + i = PySequence_Contains(mro, cls); + if (i < 0) + return -1; + if (!i) { + if (PyList_Append(mro, cls) < 0) + return -1; + } + bases = ((PyClassObject *)cls)->cl_bases; + assert(bases && PyTuple_Check(bases)); + n = PyTuple_GET_SIZE(bases); + for (i = 0; i < n; i++) { + base = PyTuple_GET_ITEM(bases, i); + if (fill_classic_mro(mro, base) < 0) + return -1; + } + return 0; +} + +static PyObject * +classic_mro(PyObject *cls) +{ + PyObject *mro; + + assert(PyClass_Check(cls)); + mro = PyList_New(0); + if (mro != NULL) { + if (fill_classic_mro(mro, cls) == 0) + return mro; + Py_DECREF(mro); + } + return NULL; +} + +/* + Method resolution order algorithm C3 described in + "A Monotonic Superclass Linearization for Dylan", + by Kim Barrett, Bob Cassel, Paul Haahr, + David A. Moon, Keith Playford, and P. Tucker Withington. + (OOPSLA 1996) + + Some notes about the rules implied by C3: + + No duplicate bases. + It isn't legal to repeat a class in a list of base classes. + + The next three properties are the 3 constraints in "C3". + + Local precendece order. + If A precedes B in C's MRO, then A will precede B in the MRO of all + subclasses of C. + + Monotonicity. + The MRO of a class must be an extension without reordering of the + MRO of each of its superclasses. + + Extended Precedence Graph (EPG). + Linearization is consistent if there is a path in the EPG from + each class to all its successors in the linearization. See + the paper for definition of EPG. + */ + +static int +tail_contains(PyObject *list, int whence, PyObject *o) { + int j, size; + size = PyList_GET_SIZE(list); + + for (j = whence+1; j < size; j++) { + if (PyList_GET_ITEM(list, j) == o) + return 1; + } + return 0; +} + +static PyObject * +class_name(PyObject *cls) +{ + PyObject *name = PyObject_GetAttrString(cls, "__name__"); + if (name == NULL) { + PyErr_Clear(); + Py_XDECREF(name); + name = PyObject_Repr(cls); + } + if (name == NULL) + return NULL; + if (!PyString_Check(name)) { + Py_DECREF(name); + return NULL; + } + return name; +} + +static int +check_duplicates(PyObject *list) +{ + int i, j, n; + /* Let's use a quadratic time algorithm, + assuming that the bases lists is short. + */ + n = PyList_GET_SIZE(list); + for (i = 0; i < n; i++) { + PyObject *o = PyList_GET_ITEM(list, i); + for (j = i + 1; j < n; j++) { + if (PyList_GET_ITEM(list, j) == o) { + o = class_name(o); + PyErr_Format(PyExc_TypeError, + "duplicate base class %s", + o ? PyString_AS_STRING(o) : "?"); + Py_XDECREF(o); + return -1; + } + } + } + return 0; +} + +/* Raise a TypeError for an MRO order disagreement. + + It's hard to produce a good error message. In the absence of better + insight into error reporting, report the classes that were candidates + to be put next into the MRO. There is some conflict between the + order in which they should be put in the MRO, but it's hard to + diagnose what constraint can't be satisfied. +*/ + +static void +set_mro_error(PyObject *to_merge, int *remain) +{ + int i, n, off, to_merge_size; + char buf[1000]; + PyObject *k, *v; + PyObject *set = PyDict_New(); + + to_merge_size = PyList_GET_SIZE(to_merge); + for (i = 0; i < to_merge_size; i++) { + PyObject *L = PyList_GET_ITEM(to_merge, i); + if (remain[i] < PyList_GET_SIZE(L)) { + PyObject *c = PyList_GET_ITEM(L, remain[i]); + if (PyDict_SetItem(set, c, Py_None) < 0) + return; + } + } + n = PyDict_Size(set); + + off = PyOS_snprintf(buf, sizeof(buf), "Cannot create a \ +consistent method resolution\norder (MRO) for bases"); + i = 0; + while (PyDict_Next(set, &i, &k, &v) && off < sizeof(buf)) { + PyObject *name = class_name(k); + off += PyOS_snprintf(buf + off, sizeof(buf) - off, " %s", + name ? PyString_AS_STRING(name) : "?"); + Py_XDECREF(name); + if (--n && off+1 < sizeof(buf)) { + buf[off++] = ','; + buf[off] = '\0'; + } + } + PyErr_SetString(PyExc_TypeError, buf); + Py_DECREF(set); +} + +static int +pmerge(PyObject *acc, PyObject* to_merge) { + int i, j, to_merge_size; + int *remain; + int ok, empty_cnt; + + to_merge_size = PyList_GET_SIZE(to_merge); + + /* remain stores an index into each sublist of to_merge. + remain[i] is the index of the next base in to_merge[i] + that is not included in acc. + */ + remain = PyMem_MALLOC(SIZEOF_INT*to_merge_size); + if (remain == NULL) + return -1; + for (i = 0; i < to_merge_size; i++) + remain[i] = 0; + + again: + empty_cnt = 0; + for (i = 0; i < to_merge_size; i++) { + PyObject *candidate; + + PyObject *cur_list = PyList_GET_ITEM(to_merge, i); + + if (remain[i] >= PyList_GET_SIZE(cur_list)) { + empty_cnt++; + continue; + } + + /* Choose next candidate for MRO. + + The input sequences alone can determine the choice. + If not, choose the class which appears in the MRO + of the earliest direct superclass of the new class. + */ + + candidate = PyList_GET_ITEM(cur_list, remain[i]); + for (j = 0; j < to_merge_size; j++) { + PyObject *j_lst = PyList_GET_ITEM(to_merge, j); + if (tail_contains(j_lst, remain[j], candidate)) { + goto skip; /* continue outer loop */ + } + } + ok = PyList_Append(acc, candidate); + if (ok < 0) { + PyMem_Free(remain); + return -1; + } + for (j = 0; j < to_merge_size; j++) { + PyObject *j_lst = PyList_GET_ITEM(to_merge, j); + if (remain[j] < PyList_GET_SIZE(j_lst) && + PyList_GET_ITEM(j_lst, remain[j]) == candidate) { + remain[j]++; + } + } + goto again; + skip: ; + } + + if (empty_cnt == to_merge_size) { + PyMem_FREE(remain); + return 0; + } + set_mro_error(to_merge, remain); + PyMem_FREE(remain); + return -1; +} + +static PyObject * +mro_implementation(PyTypeObject *type) +{ + int i, n, ok; + PyObject *bases, *result; + PyObject *to_merge, *bases_aslist; + + if(type->tp_dict == NULL) { + if(PyType_Ready(type) < 0) + return NULL; + } + + /* Find a superclass linearization that honors the constraints + of the explicit lists of bases and the constraints implied by + each base class. + + to_merge is a list of lists, where each list is a superclass + linearization implied by a base class. The last element of + to_merge is the declared list of bases. + */ + + bases = type->tp_bases; + n = PyTuple_GET_SIZE(bases); + + to_merge = PyList_New(n+1); + if (to_merge == NULL) + return NULL; + + for (i = 0; i < n; i++) { + PyObject *base = PyTuple_GET_ITEM(bases, i); + PyObject *parentMRO; + if (PyType_Check(base)) + parentMRO = PySequence_List( + ((PyTypeObject*)base)->tp_mro); + else + parentMRO = classic_mro(base); + if (parentMRO == NULL) { + Py_DECREF(to_merge); + return NULL; + } + + PyList_SET_ITEM(to_merge, i, parentMRO); + } + + bases_aslist = PySequence_List(bases); + if (bases_aslist == NULL) { + Py_DECREF(to_merge); + return NULL; + } + /* This is just a basic sanity check. */ + if (check_duplicates(bases_aslist) < 0) { + Py_DECREF(to_merge); + Py_DECREF(bases_aslist); + return NULL; + } + PyList_SET_ITEM(to_merge, n, bases_aslist); + + result = Py_BuildValue("[O]", (PyObject *)type); + if (result == NULL) { + Py_DECREF(to_merge); + return NULL; + } + + ok = pmerge(result, to_merge); + Py_DECREF(to_merge); + if (ok < 0) { + Py_DECREF(result); + return NULL; + } + + return result; +} + +static PyObject * +mro_external(PyObject *self) +{ + PyTypeObject *type = (PyTypeObject *)self; + + return mro_implementation(type); +} + +static int +mro_internal(PyTypeObject *type) +{ + PyObject *mro, *result, *tuple; + + if (type->ob_type == &PyType_Type) { + result = mro_implementation(type); + } + else { + static PyObject *mro_str; + mro = lookup_method((PyObject *)type, "mro", &mro_str); + if (mro == NULL) + return -1; + result = PyObject_CallObject(mro, NULL); + Py_DECREF(mro); + } + if (result == NULL) + return -1; + tuple = PySequence_Tuple(result); + Py_DECREF(result); + type->tp_mro = tuple; + return 0; +} + + +/* Calculate the best base amongst multiple base classes. + This is the first one that's on the path to the "solid base". */ + +static PyTypeObject * +best_base(PyObject *bases) +{ + int i, n; + PyTypeObject *base, *winner, *candidate, *base_i; + PyObject *base_proto; + + assert(PyTuple_Check(bases)); + n = PyTuple_GET_SIZE(bases); + assert(n > 0); + base = NULL; + winner = NULL; + for (i = 0; i < n; i++) { + base_proto = PyTuple_GET_ITEM(bases, i); + if (PyClass_Check(base_proto)) + continue; + if (!PyType_Check(base_proto)) { + PyErr_SetString( + PyExc_TypeError, + "bases must be types"); + return NULL; + } + base_i = (PyTypeObject *)base_proto; + if (base_i->tp_dict == NULL) { + if (PyType_Ready(base_i) < 0) + return NULL; + } + candidate = solid_base(base_i); + if (winner == NULL) { + winner = candidate; + base = base_i; + } + else if (PyType_IsSubtype(winner, candidate)) + ; + else if (PyType_IsSubtype(candidate, winner)) { + winner = candidate; + base = base_i; + } + else { + PyErr_SetString( + PyExc_TypeError, + "multiple bases have " + "instance lay-out conflict"); + return NULL; + } + } + if (base == NULL) + PyErr_SetString(PyExc_TypeError, + "a new-style class can't have only classic bases"); + return base; +} + +static int +extra_ivars(PyTypeObject *type, PyTypeObject *base) +{ + size_t t_size = type->tp_basicsize; + size_t b_size = base->tp_basicsize; + + assert(t_size >= b_size); /* Else type smaller than base! */ + if (type->tp_itemsize || base->tp_itemsize) { + /* If itemsize is involved, stricter rules */ + return t_size != b_size || + type->tp_itemsize != base->tp_itemsize; + } + if (type->tp_weaklistoffset && base->tp_weaklistoffset == 0 && + type->tp_weaklistoffset + sizeof(PyObject *) == t_size) + t_size -= sizeof(PyObject *); + if (type->tp_dictoffset && base->tp_dictoffset == 0 && + type->tp_dictoffset + sizeof(PyObject *) == t_size) + t_size -= sizeof(PyObject *); + + return t_size != b_size; +} + +static PyTypeObject * +solid_base(PyTypeObject *type) +{ + PyTypeObject *base; + + if (type->tp_base) + base = solid_base(type->tp_base); + else + base = &PyBaseObject_Type; + if (extra_ivars(type, base)) + return type; + else + return base; +} + +static void object_dealloc(PyObject *); +static int object_init(PyObject *, PyObject *, PyObject *); +static int update_slot(PyTypeObject *, PyObject *); +static void fixup_slot_dispatchers(PyTypeObject *); + +static PyObject * +subtype_dict(PyObject *obj, void *context) +{ + PyObject **dictptr = _PyObject_GetDictPtr(obj); + PyObject *dict; + + if (dictptr == NULL) { + PyErr_SetString(PyExc_AttributeError, + "This object has no __dict__"); + return NULL; + } + dict = *dictptr; + if (dict == NULL) + *dictptr = dict = PyDict_New(); + Py_XINCREF(dict); + return dict; +} + +static int +subtype_setdict(PyObject *obj, PyObject *value, void *context) +{ + PyObject **dictptr = _PyObject_GetDictPtr(obj); + PyObject *dict; + + if (dictptr == NULL) { + PyErr_SetString(PyExc_AttributeError, + "This object has no __dict__"); + return -1; + } + if (value != NULL && !PyDict_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "__dict__ must be set to a dictionary"); + return -1; + } + dict = *dictptr; + Py_XINCREF(value); + *dictptr = value; + Py_XDECREF(dict); + return 0; +} + +static PyObject * +subtype_getweakref(PyObject *obj, void *context) +{ + PyObject **weaklistptr; + PyObject *result; + + if (obj->ob_type->tp_weaklistoffset == 0) { + PyErr_SetString(PyExc_AttributeError, + "This object has no __weaklist__"); + return NULL; + } + assert(obj->ob_type->tp_weaklistoffset > 0); + assert(obj->ob_type->tp_weaklistoffset + sizeof(PyObject *) <= + (size_t)(obj->ob_type->tp_basicsize)); + weaklistptr = (PyObject **) + ((char *)obj + obj->ob_type->tp_weaklistoffset); + if (*weaklistptr == NULL) + result = Py_None; + else + result = *weaklistptr; + Py_INCREF(result); + return result; +} + +/* Three variants on the subtype_getsets list. */ + +static PyGetSetDef subtype_getsets_full[] = { + {"__dict__", subtype_dict, subtype_setdict, + PyDoc_STR("dictionary for instance variables (if defined)")}, + {"__weakref__", subtype_getweakref, NULL, + PyDoc_STR("list of weak references to the object (if defined)")}, + {0} +}; + +static PyGetSetDef subtype_getsets_dict_only[] = { + {"__dict__", subtype_dict, subtype_setdict, + PyDoc_STR("dictionary for instance variables (if defined)")}, + {0} +}; + +static PyGetSetDef subtype_getsets_weakref_only[] = { + {"__weakref__", subtype_getweakref, NULL, + PyDoc_STR("list of weak references to the object (if defined)")}, + {0} +}; + +static int +valid_identifier(PyObject *s) +{ + unsigned char *p; + int i, n; + + if (!PyString_Check(s)) { + PyErr_SetString(PyExc_TypeError, + "__slots__ must be strings"); + return 0; + } + p = (unsigned char *) PyString_AS_STRING(s); + n = PyString_GET_SIZE(s); + /* We must reject an empty name. As a hack, we bump the + length to 1 so that the loop will balk on the trailing \0. */ + if (n == 0) + n = 1; + for (i = 0; i < n; i++, p++) { + if (!(i == 0 ? isalpha(*p) : isalnum(*p)) && *p != '_') { + PyErr_SetString(PyExc_TypeError, + "__slots__ must be identifiers"); + return 0; + } + } + return 1; +} + +#ifdef Py_USING_UNICODE +/* Replace Unicode objects in slots. */ + +static PyObject * +_unicode_to_string(PyObject *slots, int nslots) +{ + PyObject *tmp = slots; + PyObject *o, *o1; + int i; + intintargfunc copy = slots->ob_type->tp_as_sequence->sq_slice; + for (i = 0; i < nslots; i++) { + if (PyUnicode_Check(o = PyTuple_GET_ITEM(tmp, i))) { + if (tmp == slots) { + tmp = copy(slots, 0, PyTuple_GET_SIZE(slots)); + if (tmp == NULL) + return NULL; + } + o1 = _PyUnicode_AsDefaultEncodedString + (o, NULL); + if (o1 == NULL) { + Py_DECREF(tmp); + return 0; + } + Py_INCREF(o1); + Py_DECREF(o); + PyTuple_SET_ITEM(tmp, i, o1); + } + } + return tmp; +} +#endif + +static PyObject * +type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) +{ + PyObject *name, *bases, *dict; + static char *kwlist[] = {"name", "bases", "dict", 0}; + PyObject *slots, *tmp, *newslots; + PyTypeObject *type, *base, *tmptype, *winner; + PyHeapTypeObject *et; + PyMemberDef *mp; + int i, nbases, nslots, slotoffset, add_dict, add_weak; + int j, may_add_dict, may_add_weak; + + assert(args != NULL && PyTuple_Check(args)); + assert(kwds == NULL || PyDict_Check(kwds)); + + /* Special case: type(x) should return x->ob_type */ + { + const int nargs = PyTuple_GET_SIZE(args); + const int nkwds = kwds == NULL ? 0 : PyDict_Size(kwds); + + if (PyType_CheckExact(metatype) && nargs == 1 && nkwds == 0) { + PyObject *x = PyTuple_GET_ITEM(args, 0); + Py_INCREF(x->ob_type); + return (PyObject *) x->ob_type; + } + + /* SF bug 475327 -- if that didn't trigger, we need 3 + arguments. but PyArg_ParseTupleAndKeywords below may give + a msg saying type() needs exactly 3. */ + if (nargs + nkwds != 3) { + PyErr_SetString(PyExc_TypeError, + "type() takes 1 or 3 arguments"); + return NULL; + } + } + + /* Check arguments: (name, bases, dict) */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "SO!O!:type", kwlist, + &name, + &PyTuple_Type, &bases, + &PyDict_Type, &dict)) + return NULL; + + /* Determine the proper metatype to deal with this, + and check for metatype conflicts while we're at it. + Note that if some other metatype wins to contract, + it's possible that its instances are not types. */ + nbases = PyTuple_GET_SIZE(bases); + winner = metatype; + for (i = 0; i < nbases; i++) { + tmp = PyTuple_GET_ITEM(bases, i); + tmptype = tmp->ob_type; + if (tmptype == &PyClass_Type) + continue; /* Special case classic classes */ + if (PyType_IsSubtype(winner, tmptype)) + continue; + if (PyType_IsSubtype(tmptype, winner)) { + winner = tmptype; + continue; + } + PyErr_SetString(PyExc_TypeError, + "metaclass conflict: " + "the metaclass of a derived class " + "must be a (non-strict) subclass " + "of the metaclasses of all its bases"); + return NULL; + } + if (winner != metatype) { + if (winner->tp_new != type_new) /* Pass it to the winner */ + return winner->tp_new(winner, args, kwds); + metatype = winner; + } + + /* Adjust for empty tuple bases */ + if (nbases == 0) { + bases = Py_BuildValue("(O)", &PyBaseObject_Type); + if (bases == NULL) + return NULL; + nbases = 1; + } + else + Py_INCREF(bases); + + /* XXX From here until type is allocated, "return NULL" leaks bases! */ + + /* Calculate best base, and check that all bases are type objects */ + base = best_base(bases); + if (base == NULL) + return NULL; + if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { + PyErr_Format(PyExc_TypeError, + "type '%.100s' is not an acceptable base type", + base->tp_name); + return NULL; + } + + /* Check for a __slots__ sequence variable in dict, and count it */ + slots = PyDict_GetItemString(dict, "__slots__"); + nslots = 0; + add_dict = 0; + add_weak = 0; + may_add_dict = base->tp_dictoffset == 0; + may_add_weak = base->tp_weaklistoffset == 0 && base->tp_itemsize == 0; + if (slots == NULL) { + if (may_add_dict) { + add_dict++; + } + if (may_add_weak) { + add_weak++; + } + } + else { + /* Have slots */ + + /* Make it into a tuple */ + if (PyString_Check(slots)) + slots = Py_BuildValue("(O)", slots); + else + slots = PySequence_Tuple(slots); + if (slots == NULL) + return NULL; + assert(PyTuple_Check(slots)); + + /* Are slots allowed? */ + nslots = PyTuple_GET_SIZE(slots); + if (nslots > 0 && base->tp_itemsize != 0) { + PyErr_Format(PyExc_TypeError, + "nonempty __slots__ " + "not supported for subtype of '%s'", + base->tp_name); + bad_slots: + Py_DECREF(slots); + return NULL; + } + +#ifdef Py_USING_UNICODE + tmp = _unicode_to_string(slots, nslots); + if (tmp != slots) { + Py_DECREF(slots); + slots = tmp; + } + if (!tmp) + return NULL; +#endif + /* Check for valid slot names and two special cases */ + for (i = 0; i < nslots; i++) { + PyObject *tmp = PyTuple_GET_ITEM(slots, i); + char *s; + if (!valid_identifier(tmp)) + goto bad_slots; + assert(PyString_Check(tmp)); + s = PyString_AS_STRING(tmp); + if (strcmp(s, "__dict__") == 0) { + if (!may_add_dict || add_dict) { + PyErr_SetString(PyExc_TypeError, + "__dict__ slot disallowed: " + "we already got one"); + goto bad_slots; + } + add_dict++; + } + if (strcmp(s, "__weakref__") == 0) { + if (!may_add_weak || add_weak) { + PyErr_SetString(PyExc_TypeError, + "__weakref__ slot disallowed: " + "either we already got one, " + "or __itemsize__ != 0"); + goto bad_slots; + } + add_weak++; + } + } + + /* Copy slots into yet another tuple, demangling names */ + newslots = PyTuple_New(nslots - add_dict - add_weak); + if (newslots == NULL) + goto bad_slots; + for (i = j = 0; i < nslots; i++) { + char *s; + char buffer[256]; + tmp = PyTuple_GET_ITEM(slots, i); + s = PyString_AS_STRING(tmp); + if ((add_dict && strcmp(s, "__dict__") == 0) || + (add_weak && strcmp(s, "__weakref__") == 0)) + continue; + if (_Py_Mangle(PyString_AS_STRING(name), + PyString_AS_STRING(tmp), + buffer, sizeof(buffer))) + { + tmp = PyString_FromString(buffer); + } else { + Py_INCREF(tmp); + } + PyTuple_SET_ITEM(newslots, j, tmp); + j++; + } + assert(j == nslots - add_dict - add_weak); + nslots = j; + Py_DECREF(slots); + slots = newslots; + + /* Secondary bases may provide weakrefs or dict */ + if (nbases > 1 && + ((may_add_dict && !add_dict) || + (may_add_weak && !add_weak))) { + for (i = 0; i < nbases; i++) { + tmp = PyTuple_GET_ITEM(bases, i); + if (tmp == (PyObject *)base) + continue; /* Skip primary base */ + if (PyClass_Check(tmp)) { + /* Classic base class provides both */ + if (may_add_dict && !add_dict) + add_dict++; + if (may_add_weak && !add_weak) + add_weak++; + break; + } + assert(PyType_Check(tmp)); + tmptype = (PyTypeObject *)tmp; + if (may_add_dict && !add_dict && + tmptype->tp_dictoffset != 0) + add_dict++; + if (may_add_weak && !add_weak && + tmptype->tp_weaklistoffset != 0) + add_weak++; + if (may_add_dict && !add_dict) + continue; + if (may_add_weak && !add_weak) + continue; + /* Nothing more to check */ + break; + } + } + } + + /* XXX From here until type is safely allocated, + "return NULL" may leak slots! */ + + /* Allocate the type object */ + type = (PyTypeObject *)metatype->tp_alloc(metatype, nslots); + if (type == NULL) { + Py_XDECREF(slots); + return NULL; + } + + /* Keep name and slots alive in the extended type object */ + et = (PyHeapTypeObject *)type; + Py_INCREF(name); + et->name = name; + et->slots = slots; + + /* Initialize tp_flags */ + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | + Py_TPFLAGS_BASETYPE; + if (base->tp_flags & Py_TPFLAGS_HAVE_GC) + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + + /* It's a new-style number unless it specifically inherits any + old-style numeric behavior */ + if ((base->tp_flags & Py_TPFLAGS_CHECKTYPES) || + (base->tp_as_number == NULL)) + type->tp_flags |= Py_TPFLAGS_CHECKTYPES; + + /* Initialize essential fields */ + type->tp_as_number = &et->as_number; + type->tp_as_sequence = &et->as_sequence; + type->tp_as_mapping = &et->as_mapping; + type->tp_as_buffer = &et->as_buffer; + type->tp_name = PyString_AS_STRING(name); + + /* Set tp_base and tp_bases */ + type->tp_bases = bases; + Py_INCREF(base); + type->tp_base = base; + + /* Initialize tp_dict from passed-in dict */ + type->tp_dict = dict = PyDict_Copy(dict); + if (dict == NULL) { + Py_DECREF(type); + return NULL; + } + + /* Set __module__ in the dict */ + if (PyDict_GetItemString(dict, "__module__") == NULL) { + tmp = PyEval_GetGlobals(); + if (tmp != NULL) { + tmp = PyDict_GetItemString(tmp, "__name__"); + if (tmp != NULL) { + if (PyDict_SetItemString(dict, "__module__", + tmp) < 0) + return NULL; + } + } + } + + /* Set tp_doc to a copy of dict['__doc__'], if the latter is there + and is a string. The __doc__ accessor will first look for tp_doc; + if that fails, it will still look into __dict__. + */ + { + PyObject *doc = PyDict_GetItemString(dict, "__doc__"); + if (doc != NULL && PyString_Check(doc)) { + const size_t n = (size_t)PyString_GET_SIZE(doc); + type->tp_doc = (char *)PyObject_MALLOC(n+1); + if (type->tp_doc == NULL) { + Py_DECREF(type); + return NULL; + } + memcpy(type->tp_doc, PyString_AS_STRING(doc), n+1); + } + } + + /* Special-case __new__: if it's a plain function, + make it a static function */ + tmp = PyDict_GetItemString(dict, "__new__"); + if (tmp != NULL && PyFunction_Check(tmp)) { + tmp = PyStaticMethod_New(tmp); + if (tmp == NULL) { + Py_DECREF(type); + return NULL; + } + PyDict_SetItemString(dict, "__new__", tmp); + Py_DECREF(tmp); + } + + /* Add descriptors for custom slots from __slots__, or for __dict__ */ + mp = PyHeapType_GET_MEMBERS(et); + slotoffset = base->tp_basicsize; + if (slots != NULL) { + for (i = 0; i < nslots; i++, mp++) { + mp->name = PyString_AS_STRING( + PyTuple_GET_ITEM(slots, i)); + mp->type = T_OBJECT_EX; + mp->offset = slotoffset; + if (base->tp_weaklistoffset == 0 && + strcmp(mp->name, "__weakref__") == 0) { + add_weak++; + mp->type = T_OBJECT; + mp->flags = READONLY; + type->tp_weaklistoffset = slotoffset; + } + slotoffset += sizeof(PyObject *); + } + } + if (add_dict) { + if (base->tp_itemsize) + type->tp_dictoffset = -(long)sizeof(PyObject *); + else + type->tp_dictoffset = slotoffset; + slotoffset += sizeof(PyObject *); + } + if (add_weak) { + assert(!base->tp_itemsize); + type->tp_weaklistoffset = slotoffset; + slotoffset += sizeof(PyObject *); + } + type->tp_basicsize = slotoffset; + type->tp_itemsize = base->tp_itemsize; + type->tp_members = PyHeapType_GET_MEMBERS(et); + + if (type->tp_weaklistoffset && type->tp_dictoffset) + type->tp_getset = subtype_getsets_full; + else if (type->tp_weaklistoffset && !type->tp_dictoffset) + type->tp_getset = subtype_getsets_weakref_only; + else if (!type->tp_weaklistoffset && type->tp_dictoffset) + type->tp_getset = subtype_getsets_dict_only; + else + type->tp_getset = NULL; + + /* Special case some slots */ + if (type->tp_dictoffset != 0 || nslots > 0) { + if (base->tp_getattr == NULL && base->tp_getattro == NULL) + type->tp_getattro = PyObject_GenericGetAttr; + if (base->tp_setattr == NULL && base->tp_setattro == NULL) + type->tp_setattro = PyObject_GenericSetAttr; + } + type->tp_dealloc = subtype_dealloc; + + /* Enable GC unless there are really no instance variables possible */ + if (!(type->tp_basicsize == sizeof(PyObject) && + type->tp_itemsize == 0)) + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + + /* Always override allocation strategy to use regular heap */ + type->tp_alloc = PyType_GenericAlloc; + if (type->tp_flags & Py_TPFLAGS_HAVE_GC) { + type->tp_free = PyObject_GC_Del; + type->tp_traverse = subtype_traverse; + type->tp_clear = subtype_clear; + } + else + type->tp_free = PyObject_Del; + + /* Initialize the rest */ + if (PyType_Ready(type) < 0) { + Py_DECREF(type); + return NULL; + } + + /* Put the proper slots in place */ + fixup_slot_dispatchers(type); + + return (PyObject *)type; +} + +/* Internal API to look for a name through the MRO. + This returns a borrowed reference, and doesn't set an exception! */ +PyObject * +_PyType_Lookup(PyTypeObject *type, PyObject *name) +{ + int i, n; + PyObject *mro, *res, *base, *dict; + + /* Look in tp_dict of types in MRO */ + mro = type->tp_mro; + + /* If mro is NULL, the type is either not yet initialized + by PyType_Ready(), or already cleared by type_clear(). + Either way the safest thing to do is to return NULL. */ + if (mro == NULL) + return NULL; + + assert(PyTuple_Check(mro)); + n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + base = PyTuple_GET_ITEM(mro, i); + if (PyClass_Check(base)) + dict = ((PyClassObject *)base)->cl_dict; + else { + assert(PyType_Check(base)); + dict = ((PyTypeObject *)base)->tp_dict; + } + assert(dict && PyDict_Check(dict)); + res = PyDict_GetItem(dict, name); + if (res != NULL) + return res; + } + return NULL; +} + +/* This is similar to PyObject_GenericGetAttr(), + but uses _PyType_Lookup() instead of just looking in type->tp_dict. */ +static PyObject * +type_getattro(PyTypeObject *type, PyObject *name) +{ + PyTypeObject *metatype = type->ob_type; + PyObject *meta_attribute, *attribute; + descrgetfunc meta_get; + + /* Initialize this type (we'll assume the metatype is initialized) */ + if (type->tp_dict == NULL) { + if (PyType_Ready(type) < 0) + return NULL; + } + + /* No readable descriptor found yet */ + meta_get = NULL; + + /* Look for the attribute in the metatype */ + meta_attribute = _PyType_Lookup(metatype, name); + + if (meta_attribute != NULL) { + meta_get = meta_attribute->ob_type->tp_descr_get; + + if (meta_get != NULL && PyDescr_IsData(meta_attribute)) { + /* Data descriptors implement tp_descr_set to intercept + * writes. Assume the attribute is not overridden in + * type's tp_dict (and bases): call the descriptor now. + */ + return meta_get(meta_attribute, (PyObject *)type, + (PyObject *)metatype); + } + } + + /* No data descriptor found on metatype. Look in tp_dict of this + * type and its bases */ + attribute = _PyType_Lookup(type, name); + if (attribute != NULL) { + /* Implement descriptor functionality, if any */ + descrgetfunc local_get = attribute->ob_type->tp_descr_get; + if (local_get != NULL) { + /* NULL 2nd argument indicates the descriptor was + * found on the target object itself (or a base) */ + return local_get(attribute, (PyObject *)NULL, + (PyObject *)type); + } + + Py_INCREF(attribute); + return attribute; + } + + /* No attribute found in local __dict__ (or bases): use the + * descriptor from the metatype, if any */ + if (meta_get != NULL) + return meta_get(meta_attribute, (PyObject *)type, + (PyObject *)metatype); + + /* If an ordinary attribute was found on the metatype, return it now */ + if (meta_attribute != NULL) { + Py_INCREF(meta_attribute); + return meta_attribute; + } + + /* Give up */ + PyErr_Format(PyExc_AttributeError, + "type object '%.50s' has no attribute '%.400s'", + type->tp_name, PyString_AS_STRING(name)); + return NULL; +} + +static int +type_setattro(PyTypeObject *type, PyObject *name, PyObject *value) +{ + if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { + PyErr_Format( + PyExc_TypeError, + "can't set attributes of built-in/extension type '%s'", + type->tp_name); + return -1; + } + /* XXX Example of how I expect this to be used... + if (update_subclasses(type, name, invalidate_cache, NULL) < 0) + return -1; + */ + if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0) + return -1; + return update_slot(type, name); +} + +static void +type_dealloc(PyTypeObject *type) +{ + PyHeapTypeObject *et; + + /* Assert this is a heap-allocated type object */ + assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE); + _PyObject_GC_UNTRACK(type); + PyObject_ClearWeakRefs((PyObject *)type); + et = (PyHeapTypeObject *)type; + Py_XDECREF(type->tp_base); + Py_XDECREF(type->tp_dict); + Py_XDECREF(type->tp_bases); + Py_XDECREF(type->tp_mro); + Py_XDECREF(type->tp_cache); + Py_XDECREF(type->tp_subclasses); + PyObject_Free(type->tp_doc); + Py_XDECREF(et->name); + Py_XDECREF(et->slots); + type->ob_type->tp_free((PyObject *)type); +} + +static PyObject * +type_subclasses(PyTypeObject *type, PyObject *args_ignored) +{ + PyObject *list, *raw, *ref; + int i, n; + + list = PyList_New(0); + if (list == NULL) + return NULL; + raw = type->tp_subclasses; + if (raw == NULL) + return list; + assert(PyList_Check(raw)); + n = PyList_GET_SIZE(raw); + for (i = 0; i < n; i++) { + ref = PyList_GET_ITEM(raw, i); + assert(PyWeakref_CheckRef(ref)); + ref = PyWeakref_GET_OBJECT(ref); + if (ref != Py_None) { + if (PyList_Append(list, ref) < 0) { + Py_DECREF(list); + return NULL; + } + } + } + return list; +} + +static PyMethodDef type_methods[] = { + {"mro", (PyCFunction)mro_external, METH_NOARGS, + PyDoc_STR("mro() -> list\nreturn a type's method resolution order")}, + {"__subclasses__", (PyCFunction)type_subclasses, METH_NOARGS, + PyDoc_STR("__subclasses__() -> list of immediate subclasses")}, + {0} +}; + +PyDoc_STRVAR(type_doc, +"type(object) -> the object's type\n" +"type(name, bases, dict) -> a new type"); + +static int +type_traverse(PyTypeObject *type, visitproc visit, void *arg) +{ + int err; + + /* Because of type_is_gc(), the collector only calls this + for heaptypes. */ + assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE); + +#define VISIT(SLOT) \ + if (SLOT) { \ + err = visit((PyObject *)(SLOT), arg); \ + if (err) \ + return err; \ + } + + VISIT(type->tp_dict); + VISIT(type->tp_cache); + VISIT(type->tp_mro); + VISIT(type->tp_bases); + VISIT(type->tp_base); + + /* There's no need to visit type->tp_subclasses or + ((PyHeapTypeObject *)type)->slots, because they can't be involved + in cycles; tp_subclasses is a list of weak references, + and slots is a tuple of strings. */ + +#undef VISIT + + return 0; +} + +static int +type_clear(PyTypeObject *type) +{ + PyObject *tmp; + + /* Because of type_is_gc(), the collector only calls this + for heaptypes. */ + assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE); + +#define CLEAR(SLOT) \ + if (SLOT) { \ + tmp = (PyObject *)(SLOT); \ + SLOT = NULL; \ + Py_DECREF(tmp); \ + } + + /* The only field we need to clear is tp_mro, which is part of a + hard cycle (its first element is the class itself) that won't + be broken otherwise (it's a tuple and tuples don't have a + tp_clear handler). None of the other fields need to be + cleared, and here's why: + + tp_dict: + It is a dict, so the collector will call its tp_clear. + + tp_cache: + Not used; if it were, it would be a dict. + + tp_bases, tp_base: + If these are involved in a cycle, there must be at least + one other, mutable object in the cycle, e.g. a base + class's dict; the cycle will be broken that way. + + tp_subclasses: + A list of weak references can't be part of a cycle; and + lists have their own tp_clear. + + slots (in PyHeapTypeObject): + A tuple of strings can't be part of a cycle. + */ + + CLEAR(type->tp_mro); + +#undef CLEAR + + return 0; +} + +static int +type_is_gc(PyTypeObject *type) +{ + return type->tp_flags & Py_TPFLAGS_HEAPTYPE; +} + +PyTypeObject PyType_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "type", /* tp_name */ + sizeof(PyHeapTypeObject), /* tp_basicsize */ + sizeof(PyMemberDef), /* tp_itemsize */ + (destructor)type_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + type_compare, /* tp_compare */ + (reprfunc)type_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)_Py_HashPointer, /* tp_hash */ + (ternaryfunc)type_call, /* tp_call */ + 0, /* tp_str */ + (getattrofunc)type_getattro, /* tp_getattro */ + (setattrofunc)type_setattro, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + type_doc, /* tp_doc */ + (traverseproc)type_traverse, /* tp_traverse */ + (inquiry)type_clear, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(PyTypeObject, tp_weaklist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + type_methods, /* tp_methods */ + type_members, /* tp_members */ + type_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof(PyTypeObject, tp_dict), /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + type_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ + (inquiry)type_is_gc, /* tp_is_gc */ +}; + + +/* The base type of all types (eventually)... except itself. */ + +static int +object_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + return 0; +} + +/* If we don't have a tp_new for a new-style class, new will use this one. + Therefore this should take no arguments/keywords. However, this new may + also be inherited by objects that define a tp_init but no tp_new. These + objects WILL pass argumets to tp_new, because it gets the same args as + tp_init. So only allow arguments if we aren't using the default init, in + which case we expect init to handle argument parsing. */ +static PyObject * +object_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + if (type->tp_init == object_init && (PyTuple_GET_SIZE(args) || + (kwds && PyDict_Check(kwds) && PyDict_Size(kwds)))) { + PyErr_SetString(PyExc_TypeError, + "default __new__ takes no parameters"); + return NULL; + } + return type->tp_alloc(type, 0); +} + +static void +object_dealloc(PyObject *self) +{ + self->ob_type->tp_free(self); +} + +static PyObject * +object_repr(PyObject *self) +{ + PyTypeObject *type; + PyObject *mod, *name, *rtn; + + type = self->ob_type; + mod = type_module(type, NULL); + if (mod == NULL) + PyErr_Clear(); + else if (!PyString_Check(mod)) { + Py_DECREF(mod); + mod = NULL; + } + name = type_name(type, NULL); + if (name == NULL) + return NULL; + if (mod != NULL && strcmp(PyString_AS_STRING(mod), "__builtin__")) + rtn = PyString_FromFormat("<%s.%s object at %p>", + PyString_AS_STRING(mod), + PyString_AS_STRING(name), + self); + else + rtn = PyString_FromFormat("<%s object at %p>", + type->tp_name, self); + Py_XDECREF(mod); + Py_DECREF(name); + return rtn; +} + +static PyObject * +object_str(PyObject *self) +{ + unaryfunc f; + + f = self->ob_type->tp_repr; + if (f == NULL) + f = object_repr; + return f(self); +} + +static long +object_hash(PyObject *self) +{ + return _Py_HashPointer(self); +} + +static PyObject * +object_get_class(PyObject *self, void *closure) +{ + Py_INCREF(self->ob_type); + return (PyObject *)(self->ob_type); +} + +static int +equiv_structs(PyTypeObject *a, PyTypeObject *b) +{ + return a == b || + (a != NULL && + b != NULL && + a->tp_basicsize == b->tp_basicsize && + a->tp_itemsize == b->tp_itemsize && + a->tp_dictoffset == b->tp_dictoffset && + a->tp_weaklistoffset == b->tp_weaklistoffset && + ((a->tp_flags & Py_TPFLAGS_HAVE_GC) == + (b->tp_flags & Py_TPFLAGS_HAVE_GC))); +} + +static int +same_slots_added(PyTypeObject *a, PyTypeObject *b) +{ + PyTypeObject *base = a->tp_base; + int size; + + if (base != b->tp_base) + return 0; + if (equiv_structs(a, base) && equiv_structs(b, base)) + return 1; + size = base->tp_basicsize; + if (a->tp_dictoffset == size && b->tp_dictoffset == size) + size += sizeof(PyObject *); + if (a->tp_weaklistoffset == size && b->tp_weaklistoffset == size) + size += sizeof(PyObject *); + return size == a->tp_basicsize && size == b->tp_basicsize; +} + +static int +compatible_for_assignment(PyTypeObject* old, PyTypeObject* new, char* attr) +{ + PyTypeObject *newbase, *oldbase; + + if (new->tp_dealloc != old->tp_dealloc || + new->tp_free != old->tp_free) + { + PyErr_Format(PyExc_TypeError, + "%s assignment: " + "'%s' deallocator differs from '%s'", + attr, + new->tp_name, + old->tp_name); + return 0; + } + newbase = new; + oldbase = old; + while (equiv_structs(newbase, newbase->tp_base)) + newbase = newbase->tp_base; + while (equiv_structs(oldbase, oldbase->tp_base)) + oldbase = oldbase->tp_base; + if (newbase != oldbase && + (newbase->tp_base != oldbase->tp_base || + !same_slots_added(newbase, oldbase))) { + PyErr_Format(PyExc_TypeError, + "%s assignment: " + "'%s' object layout differs from '%s'", + attr, + new->tp_name, + old->tp_name); + return 0; + } + + return 1; +} + +static int +object_set_class(PyObject *self, PyObject *value, void *closure) +{ + PyTypeObject *old = self->ob_type; + PyTypeObject *new; + + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, + "can't delete __class__ attribute"); + return -1; + } + if (!PyType_Check(value)) { + PyErr_Format(PyExc_TypeError, + "__class__ must be set to new-style class, not '%s' object", + value->ob_type->tp_name); + return -1; + } + new = (PyTypeObject *)value; + if (!(new->tp_flags & Py_TPFLAGS_HEAPTYPE) || + !(old->tp_flags & Py_TPFLAGS_HEAPTYPE)) + { + PyErr_Format(PyExc_TypeError, + "__class__ assignment: only for heap types"); + return -1; + } + if (compatible_for_assignment(new, old, "__class__")) { + Py_INCREF(new); + self->ob_type = new; + Py_DECREF(old); + return 0; + } + else { + return -1; + } +} + +static PyGetSetDef object_getsets[] = { + {"__class__", object_get_class, object_set_class, + PyDoc_STR("the object's class")}, + {0} +}; + + +/* Stuff to implement __reduce_ex__ for pickle protocols >= 2. + We fall back to helpers in copy_reg for: + - pickle protocols < 2 + - calculating the list of slot names (done only once per class) + - the __newobj__ function (which is used as a token but never called) +*/ + +static PyObject * +import_copy_reg(void) +{ + static PyObject *copy_reg_str; + + if (!copy_reg_str) { + copy_reg_str = PyString_InternFromString("copy_reg"); + if (copy_reg_str == NULL) + return NULL; + } + + return PyImport_Import(copy_reg_str); +} + +static PyObject * +slotnames(PyObject *cls) +{ + PyObject *clsdict; + PyObject *copy_reg; + PyObject *slotnames; + + if (!PyType_Check(cls)) { + Py_INCREF(Py_None); + return Py_None; + } + + clsdict = ((PyTypeObject *)cls)->tp_dict; + slotnames = PyDict_GetItemString(clsdict, "__slotnames__"); + if (slotnames != NULL) { + Py_INCREF(slotnames); + return slotnames; + } + + copy_reg = import_copy_reg(); + if (copy_reg == NULL) + return NULL; + + slotnames = PyObject_CallMethod(copy_reg, "_slotnames", "O", cls); + Py_DECREF(copy_reg); + if (slotnames != NULL && + slotnames != Py_None && + !PyList_Check(slotnames)) + { + PyErr_SetString(PyExc_TypeError, + "copy_reg._slotnames didn't return a list or None"); + Py_DECREF(slotnames); + slotnames = NULL; + } + + return slotnames; +} + +static PyObject * +reduce_2(PyObject *obj) +{ + PyObject *cls, *getnewargs; + PyObject *args = NULL, *args2 = NULL; + PyObject *getstate = NULL, *state = NULL, *names = NULL; + PyObject *slots = NULL, *listitems = NULL, *dictitems = NULL; + PyObject *copy_reg = NULL, *newobj = NULL, *res = NULL; + int i, n; + + cls = PyObject_GetAttrString(obj, "__class__"); + if (cls == NULL) + return NULL; + + getnewargs = PyObject_GetAttrString(obj, "__getnewargs__"); + if (getnewargs != NULL) { + args = PyObject_CallObject(getnewargs, NULL); + Py_DECREF(getnewargs); + if (args != NULL && !PyTuple_Check(args)) { + PyErr_SetString(PyExc_TypeError, + "__getnewargs__ should return a tuple"); + goto end; + } + } + else { + PyErr_Clear(); + args = PyTuple_New(0); + } + if (args == NULL) + goto end; + + getstate = PyObject_GetAttrString(obj, "__getstate__"); + if (getstate != NULL) { + state = PyObject_CallObject(getstate, NULL); + Py_DECREF(getstate); + if (state == NULL) + goto end; + } + else { + state = PyObject_GetAttrString(obj, "__dict__"); + if (state == NULL) { + PyErr_Clear(); + state = Py_None; + Py_INCREF(state); + } + names = slotnames(cls); + if (names == NULL) + goto end; + if (names != Py_None) { + assert(PyList_Check(names)); + slots = PyDict_New(); + if (slots == NULL) + goto end; + n = 0; + /* Can't pre-compute the list size; the list + is stored on the class so accessible to other + threads, which may be run by DECREF */ + for (i = 0; i < PyList_GET_SIZE(names); i++) { + PyObject *name, *value; + name = PyList_GET_ITEM(names, i); + value = PyObject_GetAttr(obj, name); + if (value == NULL) + PyErr_Clear(); + else { + int err = PyDict_SetItem(slots, name, + value); + Py_DECREF(value); + if (err) + goto end; + n++; + } + } + if (n) { + state = Py_BuildValue("(NO)", state, slots); + if (state == NULL) + goto end; + } + } + } + + if (!PyList_Check(obj)) { + listitems = Py_None; + Py_INCREF(listitems); + } + else { + listitems = PyObject_GetIter(obj); + if (listitems == NULL) + goto end; + } + + if (!PyDict_Check(obj)) { + dictitems = Py_None; + Py_INCREF(dictitems); + } + else { + dictitems = PyObject_CallMethod(obj, "iteritems", ""); + if (dictitems == NULL) + goto end; + } + + copy_reg = import_copy_reg(); + if (copy_reg == NULL) + goto end; + newobj = PyObject_GetAttrString(copy_reg, "__newobj__"); + if (newobj == NULL) + goto end; + + n = PyTuple_GET_SIZE(args); + args2 = PyTuple_New(n+1); + if (args2 == NULL) + goto end; + PyTuple_SET_ITEM(args2, 0, cls); + cls = NULL; + for (i = 0; i < n; i++) { + PyObject *v = PyTuple_GET_ITEM(args, i); + Py_INCREF(v); + PyTuple_SET_ITEM(args2, i+1, v); + } + + res = Py_BuildValue("(OOOOO)", + newobj, args2, state, listitems, dictitems); + + end: + Py_XDECREF(cls); + Py_XDECREF(args); + Py_XDECREF(args2); + Py_XDECREF(slots); + Py_XDECREF(state); + Py_XDECREF(names); + Py_XDECREF(listitems); + Py_XDECREF(dictitems); + Py_XDECREF(copy_reg); + Py_XDECREF(newobj); + return res; +} + +static PyObject * +object_reduce_ex(PyObject *self, PyObject *args) +{ + /* Call copy_reg._reduce_ex(self, proto) */ + PyObject *reduce, *copy_reg, *res; + int proto = 0; + + if (!PyArg_ParseTuple(args, "|i:__reduce_ex__", &proto)) + return NULL; + + reduce = PyObject_GetAttrString(self, "__reduce__"); + if (reduce == NULL) + PyErr_Clear(); + else { + PyObject *cls, *clsreduce, *objreduce; + int override; + cls = PyObject_GetAttrString(self, "__class__"); + if (cls == NULL) { + Py_DECREF(reduce); + return NULL; + } + clsreduce = PyObject_GetAttrString(cls, "__reduce__"); + Py_DECREF(cls); + if (clsreduce == NULL) { + Py_DECREF(reduce); + return NULL; + } + objreduce = PyDict_GetItemString(PyBaseObject_Type.tp_dict, + "__reduce__"); + override = (clsreduce != objreduce); + Py_DECREF(clsreduce); + if (override) { + res = PyObject_CallObject(reduce, NULL); + Py_DECREF(reduce); + return res; + } + else + Py_DECREF(reduce); + } + + if (proto >= 2) + return reduce_2(self); + + copy_reg = import_copy_reg(); + if (!copy_reg) + return NULL; + + res = PyEval_CallMethod(copy_reg, "_reduce_ex", "(Oi)", self, proto); + Py_DECREF(copy_reg); + + return res; +} + +static PyMethodDef object_methods[] = { + {"__reduce_ex__", object_reduce_ex, METH_VARARGS, + PyDoc_STR("helper for pickle")}, + {"__reduce__", object_reduce_ex, METH_VARARGS, + PyDoc_STR("helper for pickle")}, + {0} +}; + + +PyTypeObject PyBaseObject_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "object", /* tp_name */ + sizeof(PyObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)object_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + object_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + object_hash, /* tp_hash */ + 0, /* tp_call */ + object_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + PyDoc_STR("The most base type"), /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + object_methods, /* tp_methods */ + 0, /* tp_members */ + object_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + object_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + object_new, /* tp_new */ + PyObject_Del, /* tp_free */ +}; + + +/* Initialize the __dict__ in a type object */ + +static int +add_methods(PyTypeObject *type, PyMethodDef *meth) +{ + PyObject *dict = type->tp_dict; + + for (; meth->ml_name != NULL; meth++) { + PyObject *descr; + if (PyDict_GetItemString(dict, meth->ml_name)) + continue; + if (meth->ml_flags & METH_CLASS) { + if (meth->ml_flags & METH_STATIC) { + PyErr_SetString(PyExc_ValueError, + "method cannot be both class and static"); + return -1; + } + descr = PyDescr_NewClassMethod(type, meth); + } + else if (meth->ml_flags & METH_STATIC) { + PyObject *cfunc = PyCFunction_New(meth, NULL); + if (cfunc == NULL) + return -1; + descr = PyStaticMethod_New(cfunc); + Py_DECREF(cfunc); + } + else { + descr = PyDescr_NewMethod(type, meth); + } + if (descr == NULL) + return -1; + if (PyDict_SetItemString(dict, meth->ml_name, descr) < 0) + return -1; + Py_DECREF(descr); + } + return 0; +} + +static int +add_members(PyTypeObject *type, PyMemberDef *memb) +{ + PyObject *dict = type->tp_dict; + + for (; memb->name != NULL; memb++) { + PyObject *descr; + if (PyDict_GetItemString(dict, memb->name)) + continue; + descr = PyDescr_NewMember(type, memb); + if (descr == NULL) + return -1; + if (PyDict_SetItemString(dict, memb->name, descr) < 0) + return -1; + Py_DECREF(descr); + } + return 0; +} + +static int +add_getset(PyTypeObject *type, PyGetSetDef *gsp) +{ + PyObject *dict = type->tp_dict; + + for (; gsp->name != NULL; gsp++) { + PyObject *descr; + if (PyDict_GetItemString(dict, gsp->name)) + continue; + descr = PyDescr_NewGetSet(type, gsp); + + if (descr == NULL) + return -1; + if (PyDict_SetItemString(dict, gsp->name, descr) < 0) + return -1; + Py_DECREF(descr); + } + return 0; +} + +static void +inherit_special(PyTypeObject *type, PyTypeObject *base) +{ + int oldsize, newsize; + + /* Special flag magic */ + if (!type->tp_as_buffer && base->tp_as_buffer) { + type->tp_flags &= ~Py_TPFLAGS_HAVE_GETCHARBUFFER; + type->tp_flags |= + base->tp_flags & Py_TPFLAGS_HAVE_GETCHARBUFFER; + } + if (!type->tp_as_sequence && base->tp_as_sequence) { + type->tp_flags &= ~Py_TPFLAGS_HAVE_SEQUENCE_IN; + type->tp_flags |= base->tp_flags & Py_TPFLAGS_HAVE_SEQUENCE_IN; + } + if ((type->tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS) != + (base->tp_flags & Py_TPFLAGS_HAVE_INPLACEOPS)) { + if ((!type->tp_as_number && base->tp_as_number) || + (!type->tp_as_sequence && base->tp_as_sequence)) { + type->tp_flags &= ~Py_TPFLAGS_HAVE_INPLACEOPS; + if (!type->tp_as_number && !type->tp_as_sequence) { + type->tp_flags |= base->tp_flags & + Py_TPFLAGS_HAVE_INPLACEOPS; + } + } + /* Wow */ + } + if (!type->tp_as_number && base->tp_as_number) { + type->tp_flags &= ~Py_TPFLAGS_CHECKTYPES; + type->tp_flags |= base->tp_flags & Py_TPFLAGS_CHECKTYPES; + } + + /* Copying basicsize is connected to the GC flags */ + oldsize = base->tp_basicsize; + newsize = type->tp_basicsize ? type->tp_basicsize : oldsize; + if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC) && + (base->tp_flags & Py_TPFLAGS_HAVE_GC) && + (type->tp_flags & Py_TPFLAGS_HAVE_RICHCOMPARE/*GC slots exist*/) && + (!type->tp_traverse && !type->tp_clear)) { + type->tp_flags |= Py_TPFLAGS_HAVE_GC; + if (type->tp_traverse == NULL) + type->tp_traverse = base->tp_traverse; + if (type->tp_clear == NULL) + type->tp_clear = base->tp_clear; + } + if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_CLASS) { + /* The condition below could use some explanation. + It appears that tp_new is not inherited for static types + whose base class is 'object'; this seems to be a precaution + so that old extension types don't suddenly become + callable (object.__new__ wouldn't insure the invariants + that the extension type's own factory function ensures). + Heap types, of course, are under our control, so they do + inherit tp_new; static extension types that specify some + other built-in type as the default are considered + new-style-aware so they also inherit object.__new__. */ + if (base != &PyBaseObject_Type || + (type->tp_flags & Py_TPFLAGS_HEAPTYPE)) { + if (type->tp_new == NULL) + type->tp_new = base->tp_new; + } + } + type->tp_basicsize = newsize; + + /* Copy other non-function slots */ + +#undef COPYVAL +#define COPYVAL(SLOT) \ + if (type->SLOT == 0) type->SLOT = base->SLOT + + COPYVAL(tp_itemsize); + if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_WEAKREFS) { + COPYVAL(tp_weaklistoffset); + } + if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_CLASS) { + COPYVAL(tp_dictoffset); + } +} + +static void +inherit_slots(PyTypeObject *type, PyTypeObject *base) +{ + PyTypeObject *basebase; + +#undef SLOTDEFINED +#undef COPYSLOT +#undef COPYNUM +#undef COPYSEQ +#undef COPYMAP +#undef COPYBUF + +#define SLOTDEFINED(SLOT) \ + (base->SLOT != 0 && \ + (basebase == NULL || base->SLOT != basebase->SLOT)) + +#define COPYSLOT(SLOT) \ + if (!type->SLOT && SLOTDEFINED(SLOT)) type->SLOT = base->SLOT + +#define COPYNUM(SLOT) COPYSLOT(tp_as_number->SLOT) +#define COPYSEQ(SLOT) COPYSLOT(tp_as_sequence->SLOT) +#define COPYMAP(SLOT) COPYSLOT(tp_as_mapping->SLOT) +#define COPYBUF(SLOT) COPYSLOT(tp_as_buffer->SLOT) + + /* This won't inherit indirect slots (from tp_as_number etc.) + if type doesn't provide the space. */ + + if (type->tp_as_number != NULL && base->tp_as_number != NULL) { + basebase = base->tp_base; + if (basebase->tp_as_number == NULL) + basebase = NULL; + COPYNUM(nb_add); + COPYNUM(nb_subtract); + COPYNUM(nb_multiply); + COPYNUM(nb_divide); + COPYNUM(nb_remainder); + COPYNUM(nb_divmod); + COPYNUM(nb_power); + COPYNUM(nb_negative); + COPYNUM(nb_positive); + COPYNUM(nb_absolute); + COPYNUM(nb_nonzero); + COPYNUM(nb_invert); + COPYNUM(nb_lshift); + COPYNUM(nb_rshift); + COPYNUM(nb_and); + COPYNUM(nb_xor); + COPYNUM(nb_or); + COPYNUM(nb_coerce); + COPYNUM(nb_int); + COPYNUM(nb_long); + COPYNUM(nb_float); + COPYNUM(nb_oct); + COPYNUM(nb_hex); + COPYNUM(nb_inplace_add); + COPYNUM(nb_inplace_subtract); + COPYNUM(nb_inplace_multiply); + COPYNUM(nb_inplace_divide); + COPYNUM(nb_inplace_remainder); + COPYNUM(nb_inplace_power); + COPYNUM(nb_inplace_lshift); + COPYNUM(nb_inplace_rshift); + COPYNUM(nb_inplace_and); + COPYNUM(nb_inplace_xor); + COPYNUM(nb_inplace_or); + if (base->tp_flags & Py_TPFLAGS_CHECKTYPES) { + COPYNUM(nb_true_divide); + COPYNUM(nb_floor_divide); + COPYNUM(nb_inplace_true_divide); + COPYNUM(nb_inplace_floor_divide); + } + } + + if (type->tp_as_sequence != NULL && base->tp_as_sequence != NULL) { + basebase = base->tp_base; + if (basebase->tp_as_sequence == NULL) + basebase = NULL; + COPYSEQ(sq_length); + COPYSEQ(sq_concat); + COPYSEQ(sq_repeat); + COPYSEQ(sq_item); + COPYSEQ(sq_slice); + COPYSEQ(sq_ass_item); + COPYSEQ(sq_ass_slice); + COPYSEQ(sq_contains); + COPYSEQ(sq_inplace_concat); + COPYSEQ(sq_inplace_repeat); + } + + if (type->tp_as_mapping != NULL && base->tp_as_mapping != NULL) { + basebase = base->tp_base; + if (basebase->tp_as_mapping == NULL) + basebase = NULL; + COPYMAP(mp_length); + COPYMAP(mp_subscript); + COPYMAP(mp_ass_subscript); + } + + if (type->tp_as_buffer != NULL && base->tp_as_buffer != NULL) { + basebase = base->tp_base; + if (basebase->tp_as_buffer == NULL) + basebase = NULL; + COPYBUF(bf_getreadbuffer); + COPYBUF(bf_getwritebuffer); + COPYBUF(bf_getsegcount); + COPYBUF(bf_getcharbuffer); + } + + basebase = base->tp_base; + + COPYSLOT(tp_dealloc); + COPYSLOT(tp_print); + if (type->tp_getattr == NULL && type->tp_getattro == NULL) { + type->tp_getattr = base->tp_getattr; + type->tp_getattro = base->tp_getattro; + } + if (type->tp_setattr == NULL && type->tp_setattro == NULL) { + type->tp_setattr = base->tp_setattr; + type->tp_setattro = base->tp_setattro; + } + /* tp_compare see tp_richcompare */ + COPYSLOT(tp_repr); + /* tp_hash see tp_richcompare */ + COPYSLOT(tp_call); + COPYSLOT(tp_str); + if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_RICHCOMPARE) { + if (type->tp_compare == NULL && + type->tp_richcompare == NULL && + type->tp_hash == NULL) + { + type->tp_compare = base->tp_compare; + type->tp_richcompare = base->tp_richcompare; + type->tp_hash = base->tp_hash; + } + } + else { + COPYSLOT(tp_compare); + } + if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_ITER) { + COPYSLOT(tp_iter); + COPYSLOT(tp_iternext); + } + if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_CLASS) { + COPYSLOT(tp_descr_get); + COPYSLOT(tp_descr_set); + COPYSLOT(tp_dictoffset); + COPYSLOT(tp_init); + COPYSLOT(tp_alloc); + COPYSLOT(tp_is_gc); + if ((type->tp_flags & Py_TPFLAGS_HAVE_GC) == + (base->tp_flags & Py_TPFLAGS_HAVE_GC)) { + /* They agree about gc. */ + COPYSLOT(tp_free); + } + else if ((type->tp_flags & Py_TPFLAGS_HAVE_GC) && + type->tp_free == NULL && + base->tp_free == _PyObject_Del) { + /* A bit of magic to plug in the correct default + * tp_free function when a derived class adds gc, + * didn't define tp_free, and the base uses the + * default non-gc tp_free. + */ + type->tp_free = PyObject_GC_Del; + } + /* else they didn't agree about gc, and there isn't something + * obvious to be done -- the type is on its own. + */ + } +} + +static int add_operators(PyTypeObject *); + +int +PyType_Ready(PyTypeObject *type) +{ + PyObject *dict, *bases; + PyTypeObject *base; + int i, n; + + if (type->tp_flags & Py_TPFLAGS_READY) { + assert(type->tp_dict != NULL); + return 0; + } + assert((type->tp_flags & Py_TPFLAGS_READYING) == 0); + + type->tp_flags |= Py_TPFLAGS_READYING; + +#ifdef Py_TRACE_REFS + /* PyType_Ready is the closest thing we have to a choke point + * for type objects, so is the best place I can think of to try + * to get type objects into the doubly-linked list of all objects. + * Still, not all type objects go thru PyType_Ready. + */ + _Py_AddToAllObjects((PyObject *)type, 0); +#endif + + /* Initialize tp_base (defaults to BaseObject unless that's us) */ + base = type->tp_base; + if (base == NULL && type != &PyBaseObject_Type) + base = type->tp_base = &PyBaseObject_Type; + + /* Initialize the base class */ + if (base && base->tp_dict == NULL) { + if (PyType_Ready(base) < 0) + goto error; + } + + /* Initialize ob_type if NULL. This means extensions that want to be + compilable separately on Windows can call PyType_Ready() instead of + initializing the ob_type field of their type objects. */ + if (type->ob_type == NULL) + type->ob_type = base->ob_type; + + /* Initialize tp_bases */ + bases = type->tp_bases; + if (bases == NULL) { + if (base == NULL) + bases = PyTuple_New(0); + else + bases = Py_BuildValue("(O)", base); + if (bases == NULL) + goto error; + type->tp_bases = bases; + } + + /* Initialize tp_dict */ + dict = type->tp_dict; + if (dict == NULL) { + dict = PyDict_New(); + if (dict == NULL) + goto error; + type->tp_dict = dict; + } + + /* Add type-specific descriptors to tp_dict */ + if (add_operators(type) < 0) + goto error; + if (type->tp_methods != NULL) { + if (add_methods(type, type->tp_methods) < 0) + goto error; + } + if (type->tp_members != NULL) { + if (add_members(type, type->tp_members) < 0) + goto error; + } + if (type->tp_getset != NULL) { + if (add_getset(type, type->tp_getset) < 0) + goto error; + } + + /* Calculate method resolution order */ + if (mro_internal(type) < 0) { + goto error; + } + + /* Inherit special flags from dominant base */ + if (type->tp_base != NULL) + inherit_special(type, type->tp_base); + + /* Initialize tp_dict properly */ + bases = type->tp_mro; + assert(bases != NULL); + assert(PyTuple_Check(bases)); + n = PyTuple_GET_SIZE(bases); + for (i = 1; i < n; i++) { + PyObject *b = PyTuple_GET_ITEM(bases, i); + if (PyType_Check(b)) + inherit_slots(type, (PyTypeObject *)b); + } + + /* Sanity check for tp_free. */ + if (PyType_IS_GC(type) && (type->tp_flags & Py_TPFLAGS_BASETYPE) && + (type->tp_free == NULL || type->tp_free == PyObject_Del)) { + /* This base class needs to call tp_free, but doesn't have + * one, or its tp_free is for non-gc'ed objects. + */ + PyErr_Format(PyExc_TypeError, "type '%.100s' participates in " + "gc and is a base type but has inappropriate " + "tp_free slot", + type->tp_name); + goto error; + } + + /* if the type dictionary doesn't contain a __doc__, set it from + the tp_doc slot. + */ + if (PyDict_GetItemString(type->tp_dict, "__doc__") == NULL) { + if (type->tp_doc != NULL) { + PyObject *doc = PyString_FromString(type->tp_doc); + PyDict_SetItemString(type->tp_dict, "__doc__", doc); + Py_DECREF(doc); + } else { + PyDict_SetItemString(type->tp_dict, + "__doc__", Py_None); + } + } + + /* Some more special stuff */ + base = type->tp_base; + if (base != NULL) { + if (type->tp_as_number == NULL) + type->tp_as_number = base->tp_as_number; + if (type->tp_as_sequence == NULL) + type->tp_as_sequence = base->tp_as_sequence; + if (type->tp_as_mapping == NULL) + type->tp_as_mapping = base->tp_as_mapping; + if (type->tp_as_buffer == NULL) + type->tp_as_buffer = base->tp_as_buffer; + } + + /* Link into each base class's list of subclasses */ + bases = type->tp_bases; + n = PyTuple_GET_SIZE(bases); + for (i = 0; i < n; i++) { + PyObject *b = PyTuple_GET_ITEM(bases, i); + if (PyType_Check(b) && + add_subclass((PyTypeObject *)b, type) < 0) + goto error; + } + + /* All done -- set the ready flag */ + assert(type->tp_dict != NULL); + type->tp_flags = + (type->tp_flags & ~Py_TPFLAGS_READYING) | Py_TPFLAGS_READY; + return 0; + + error: + type->tp_flags &= ~Py_TPFLAGS_READYING; + return -1; +} + +static int +add_subclass(PyTypeObject *base, PyTypeObject *type) +{ + int i; + PyObject *list, *ref, *new; + + list = base->tp_subclasses; + if (list == NULL) { + base->tp_subclasses = list = PyList_New(0); + if (list == NULL) + return -1; + } + assert(PyList_Check(list)); + new = PyWeakref_NewRef((PyObject *)type, NULL); + i = PyList_GET_SIZE(list); + while (--i >= 0) { + ref = PyList_GET_ITEM(list, i); + assert(PyWeakref_CheckRef(ref)); + if (PyWeakref_GET_OBJECT(ref) == Py_None) + return PyList_SetItem(list, i, new); + } + i = PyList_Append(list, new); + Py_DECREF(new); + return i; +} + +static void +remove_subclass(PyTypeObject *base, PyTypeObject *type) +{ + int i; + PyObject *list, *ref; + + list = base->tp_subclasses; + if (list == NULL) { + return; + } + assert(PyList_Check(list)); + i = PyList_GET_SIZE(list); + while (--i >= 0) { + ref = PyList_GET_ITEM(list, i); + assert(PyWeakref_CheckRef(ref)); + if (PyWeakref_GET_OBJECT(ref) == (PyObject*)type) { + /* this can't fail, right? */ + PySequence_DelItem(list, i); + return; + } + } +} + +/* Generic wrappers for overloadable 'operators' such as __getitem__ */ + +/* There's a wrapper *function* for each distinct function typedef used + for type object slots (e.g. binaryfunc, ternaryfunc, etc.). There's a + wrapper *table* for each distinct operation (e.g. __len__, __add__). + Most tables have only one entry; the tables for binary operators have two + entries, one regular and one with reversed arguments. */ + +static PyObject * +wrap_inquiry(PyObject *self, PyObject *args, void *wrapped) +{ + inquiry func = (inquiry)wrapped; + int res; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + res = (*func)(self); + if (res == -1 && PyErr_Occurred()) + return NULL; + return PyInt_FromLong((long)res); +} + +static PyObject * +wrap_inquirypred(PyObject *self, PyObject *args, void *wrapped) +{ + inquiry func = (inquiry)wrapped; + int res; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + res = (*func)(self); + if (res == -1 && PyErr_Occurred()) + return NULL; + return PyBool_FromLong((long)res); +} + +static PyObject * +wrap_binaryfunc(PyObject *self, PyObject *args, void *wrapped) +{ + binaryfunc func = (binaryfunc)wrapped; + PyObject *other; + + if (!PyArg_ParseTuple(args, "O", &other)) + return NULL; + return (*func)(self, other); +} + +static PyObject * +wrap_binaryfunc_l(PyObject *self, PyObject *args, void *wrapped) +{ + binaryfunc func = (binaryfunc)wrapped; + PyObject *other; + + if (!PyArg_ParseTuple(args, "O", &other)) + return NULL; + if (!(self->ob_type->tp_flags & Py_TPFLAGS_CHECKTYPES) && + !PyType_IsSubtype(other->ob_type, self->ob_type)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return (*func)(self, other); +} + +static PyObject * +wrap_binaryfunc_r(PyObject *self, PyObject *args, void *wrapped) +{ + binaryfunc func = (binaryfunc)wrapped; + PyObject *other; + + if (!PyArg_ParseTuple(args, "O", &other)) + return NULL; + if (!(self->ob_type->tp_flags & Py_TPFLAGS_CHECKTYPES) && + !PyType_IsSubtype(other->ob_type, self->ob_type)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return (*func)(other, self); +} + +static PyObject * +wrap_coercefunc(PyObject *self, PyObject *args, void *wrapped) +{ + coercion func = (coercion)wrapped; + PyObject *other, *res; + int ok; + + if (!PyArg_ParseTuple(args, "O", &other)) + return NULL; + ok = func(&self, &other); + if (ok < 0) + return NULL; + if (ok > 0) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + res = PyTuple_New(2); + if (res == NULL) { + Py_DECREF(self); + Py_DECREF(other); + return NULL; + } + PyTuple_SET_ITEM(res, 0, self); + PyTuple_SET_ITEM(res, 1, other); + return res; +} + +static PyObject * +wrap_ternaryfunc(PyObject *self, PyObject *args, void *wrapped) +{ + ternaryfunc func = (ternaryfunc)wrapped; + PyObject *other; + PyObject *third = Py_None; + + /* Note: This wrapper only works for __pow__() */ + + if (!PyArg_ParseTuple(args, "O|O", &other, &third)) + return NULL; + return (*func)(self, other, third); +} + +static PyObject * +wrap_ternaryfunc_r(PyObject *self, PyObject *args, void *wrapped) +{ + ternaryfunc func = (ternaryfunc)wrapped; + PyObject *other; + PyObject *third = Py_None; + + /* Note: This wrapper only works for __pow__() */ + + if (!PyArg_ParseTuple(args, "O|O", &other, &third)) + return NULL; + return (*func)(other, self, third); +} + +static PyObject * +wrap_unaryfunc(PyObject *self, PyObject *args, void *wrapped) +{ + unaryfunc func = (unaryfunc)wrapped; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + return (*func)(self); +} + +static PyObject * +wrap_intargfunc(PyObject *self, PyObject *args, void *wrapped) +{ + intargfunc func = (intargfunc)wrapped; + int i; + + if (!PyArg_ParseTuple(args, "i", &i)) + return NULL; + return (*func)(self, i); +} + +static int +getindex(PyObject *self, PyObject *arg) +{ + int i; + + i = PyInt_AsLong(arg); + if (i == -1 && PyErr_Occurred()) + return -1; + if (i < 0) { + PySequenceMethods *sq = self->ob_type->tp_as_sequence; + if (sq && sq->sq_length) { + int n = (*sq->sq_length)(self); + if (n < 0) + return -1; + i += n; + } + } + return i; +} + +static PyObject * +wrap_sq_item(PyObject *self, PyObject *args, void *wrapped) +{ + intargfunc func = (intargfunc)wrapped; + PyObject *arg; + int i; + + if (PyTuple_GET_SIZE(args) == 1) { + arg = PyTuple_GET_ITEM(args, 0); + i = getindex(self, arg); + if (i == -1 && PyErr_Occurred()) + return NULL; + return (*func)(self, i); + } + PyArg_ParseTuple(args, "O", &arg); + assert(PyErr_Occurred()); + return NULL; +} + +static PyObject * +wrap_intintargfunc(PyObject *self, PyObject *args, void *wrapped) +{ + intintargfunc func = (intintargfunc)wrapped; + int i, j; + + if (!PyArg_ParseTuple(args, "ii", &i, &j)) + return NULL; + return (*func)(self, i, j); +} + +static PyObject * +wrap_sq_setitem(PyObject *self, PyObject *args, void *wrapped) +{ + intobjargproc func = (intobjargproc)wrapped; + int i, res; + PyObject *arg, *value; + + if (!PyArg_ParseTuple(args, "OO", &arg, &value)) + return NULL; + i = getindex(self, arg); + if (i == -1 && PyErr_Occurred()) + return NULL; + res = (*func)(self, i, value); + if (res == -1 && PyErr_Occurred()) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +wrap_sq_delitem(PyObject *self, PyObject *args, void *wrapped) +{ + intobjargproc func = (intobjargproc)wrapped; + int i, res; + PyObject *arg; + + if (!PyArg_ParseTuple(args, "O", &arg)) + return NULL; + i = getindex(self, arg); + if (i == -1 && PyErr_Occurred()) + return NULL; + res = (*func)(self, i, NULL); + if (res == -1 && PyErr_Occurred()) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +wrap_intintobjargproc(PyObject *self, PyObject *args, void *wrapped) +{ + intintobjargproc func = (intintobjargproc)wrapped; + int i, j, res; + PyObject *value; + + if (!PyArg_ParseTuple(args, "iiO", &i, &j, &value)) + return NULL; + res = (*func)(self, i, j, value); + if (res == -1 && PyErr_Occurred()) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +wrap_delslice(PyObject *self, PyObject *args, void *wrapped) +{ + intintobjargproc func = (intintobjargproc)wrapped; + int i, j, res; + + if (!PyArg_ParseTuple(args, "ii", &i, &j)) + return NULL; + res = (*func)(self, i, j, NULL); + if (res == -1 && PyErr_Occurred()) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +/* XXX objobjproc is a misnomer; should be objargpred */ +static PyObject * +wrap_objobjproc(PyObject *self, PyObject *args, void *wrapped) +{ + objobjproc func = (objobjproc)wrapped; + int res; + PyObject *value; + + if (!PyArg_ParseTuple(args, "O", &value)) + return NULL; + res = (*func)(self, value); + if (res == -1 && PyErr_Occurred()) + return NULL; + else + return PyBool_FromLong(res); +} + +static PyObject * +wrap_objobjargproc(PyObject *self, PyObject *args, void *wrapped) +{ + objobjargproc func = (objobjargproc)wrapped; + int res; + PyObject *key, *value; + + if (!PyArg_ParseTuple(args, "OO", &key, &value)) + return NULL; + res = (*func)(self, key, value); + if (res == -1 && PyErr_Occurred()) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +wrap_delitem(PyObject *self, PyObject *args, void *wrapped) +{ + objobjargproc func = (objobjargproc)wrapped; + int res; + PyObject *key; + + if (!PyArg_ParseTuple(args, "O", &key)) + return NULL; + res = (*func)(self, key, NULL); + if (res == -1 && PyErr_Occurred()) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +wrap_cmpfunc(PyObject *self, PyObject *args, void *wrapped) +{ + cmpfunc func = (cmpfunc)wrapped; + int res; + PyObject *other; + + if (!PyArg_ParseTuple(args, "O", &other)) + return NULL; + if (other->ob_type->tp_compare != func && + !PyType_IsSubtype(other->ob_type, self->ob_type)) { + PyErr_Format( + PyExc_TypeError, + "%s.__cmp__(x,y) requires y to be a '%s', not a '%s'", + self->ob_type->tp_name, + self->ob_type->tp_name, + other->ob_type->tp_name); + return NULL; + } + res = (*func)(self, other); + if (PyErr_Occurred()) + return NULL; + return PyInt_FromLong((long)res); +} + +/* Helper to check for object.__setattr__ or __delattr__ applied to a type. + This is called the Carlo Verre hack after its discoverer. */ +static int +hackcheck(PyObject *self, setattrofunc func, char *what) +{ + PyTypeObject *type = self->ob_type; + while (type && type->tp_flags & Py_TPFLAGS_HEAPTYPE) + type = type->tp_base; + if (type->tp_setattro != func) { + PyErr_Format(PyExc_TypeError, + "can't apply this %s to %s object", + what, + type->tp_name); + return 0; + } + return 1; +} + +static PyObject * +wrap_setattr(PyObject *self, PyObject *args, void *wrapped) +{ + setattrofunc func = (setattrofunc)wrapped; + int res; + PyObject *name, *value; + + if (!PyArg_ParseTuple(args, "OO", &name, &value)) + return NULL; + if (!hackcheck(self, func, "__setattr__")) + return NULL; + res = (*func)(self, name, value); + if (res < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +wrap_delattr(PyObject *self, PyObject *args, void *wrapped) +{ + setattrofunc func = (setattrofunc)wrapped; + int res; + PyObject *name; + + if (!PyArg_ParseTuple(args, "O", &name)) + return NULL; + if (!hackcheck(self, func, "__delattr__")) + return NULL; + res = (*func)(self, name, NULL); + if (res < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +wrap_hashfunc(PyObject *self, PyObject *args, void *wrapped) +{ + hashfunc func = (hashfunc)wrapped; + long res; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + res = (*func)(self); + if (res == -1 && PyErr_Occurred()) + return NULL; + return PyInt_FromLong(res); +} + +static PyObject * +wrap_call(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds) +{ + ternaryfunc func = (ternaryfunc)wrapped; + + return (*func)(self, args, kwds); +} + +static PyObject * +wrap_richcmpfunc(PyObject *self, PyObject *args, void *wrapped, int op) +{ + richcmpfunc func = (richcmpfunc)wrapped; + PyObject *other; + + if (!PyArg_ParseTuple(args, "O", &other)) + return NULL; + return (*func)(self, other, op); +} + +#undef RICHCMP_WRAPPER +#define RICHCMP_WRAPPER(NAME, OP) \ +static PyObject * \ +richcmp_##NAME(PyObject *self, PyObject *args, void *wrapped) \ +{ \ + return wrap_richcmpfunc(self, args, wrapped, OP); \ +} + +RICHCMP_WRAPPER(lt, Py_LT) +RICHCMP_WRAPPER(le, Py_LE) +RICHCMP_WRAPPER(eq, Py_EQ) +RICHCMP_WRAPPER(ne, Py_NE) +RICHCMP_WRAPPER(gt, Py_GT) +RICHCMP_WRAPPER(ge, Py_GE) + +static PyObject * +wrap_next(PyObject *self, PyObject *args, void *wrapped) +{ + unaryfunc func = (unaryfunc)wrapped; + PyObject *res; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + res = (*func)(self); + if (res == NULL && !PyErr_Occurred()) + PyErr_SetNone(PyExc_StopIteration); + return res; +} + +static PyObject * +wrap_descr_get(PyObject *self, PyObject *args, void *wrapped) +{ + descrgetfunc func = (descrgetfunc)wrapped; + PyObject *obj; + PyObject *type = NULL; + + if (!PyArg_ParseTuple(args, "O|O", &obj, &type)) + return NULL; + if (obj == Py_None) + obj = NULL; + if (type == Py_None) + type = NULL; + if (type == NULL &&obj == NULL) { + PyErr_SetString(PyExc_TypeError, + "__get__(None, None) is invalid"); + return NULL; + } + return (*func)(self, obj, type); +} + +static PyObject * +wrap_descr_set(PyObject *self, PyObject *args, void *wrapped) +{ + descrsetfunc func = (descrsetfunc)wrapped; + PyObject *obj, *value; + int ret; + + if (!PyArg_ParseTuple(args, "OO", &obj, &value)) + return NULL; + ret = (*func)(self, obj, value); + if (ret < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +wrap_descr_delete(PyObject *self, PyObject *args, void *wrapped) +{ + descrsetfunc func = (descrsetfunc)wrapped; + PyObject *obj; + int ret; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + ret = (*func)(self, obj, NULL); + if (ret < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +wrap_init(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds) +{ + initproc func = (initproc)wrapped; + + if (func(self, args, kwds) < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyTypeObject *type, *subtype, *staticbase; + PyObject *arg0, *res; + + if (self == NULL || !PyType_Check(self)) + Py_FatalError("__new__() called with non-type 'self'"); + type = (PyTypeObject *)self; + if (!PyTuple_Check(args) || PyTuple_GET_SIZE(args) < 1) { + PyErr_Format(PyExc_TypeError, + "%s.__new__(): not enough arguments", + type->tp_name); + return NULL; + } + arg0 = PyTuple_GET_ITEM(args, 0); + if (!PyType_Check(arg0)) { + PyErr_Format(PyExc_TypeError, + "%s.__new__(X): X is not a type object (%s)", + type->tp_name, + arg0->ob_type->tp_name); + return NULL; + } + subtype = (PyTypeObject *)arg0; + if (!PyType_IsSubtype(subtype, type)) { + PyErr_Format(PyExc_TypeError, + "%s.__new__(%s): %s is not a subtype of %s", + type->tp_name, + subtype->tp_name, + subtype->tp_name, + type->tp_name); + return NULL; + } + + /* Check that the use doesn't do something silly and unsafe like + object.__new__(dict). To do this, we check that the + most derived base that's not a heap type is this type. */ + staticbase = subtype; + while (staticbase && (staticbase->tp_flags & Py_TPFLAGS_HEAPTYPE)) + staticbase = staticbase->tp_base; + if (staticbase->tp_new != type->tp_new) { + PyErr_Format(PyExc_TypeError, + "%s.__new__(%s) is not safe, use %s.__new__()", + type->tp_name, + subtype->tp_name, + staticbase == NULL ? "?" : staticbase->tp_name); + return NULL; + } + + args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)); + if (args == NULL) + return NULL; + res = type->tp_new(subtype, args, kwds); + Py_DECREF(args); + return res; +} + +static struct PyMethodDef tp_new_methoddef[] = { + {"__new__", (PyCFunction)tp_new_wrapper, METH_KEYWORDS, + PyDoc_STR("T.__new__(S, ...) -> " + "a new object with type S, a subtype of T")}, + {0} +}; + +static int +add_tp_new_wrapper(PyTypeObject *type) +{ + PyObject *func; + + if (PyDict_GetItemString(type->tp_dict, "__new__") != NULL) + return 0; + func = PyCFunction_New(tp_new_methoddef, (PyObject *)type); + if (func == NULL) + return -1; + return PyDict_SetItemString(type->tp_dict, "__new__", func); +} + +/* Slot wrappers that call the corresponding __foo__ slot. See comments + below at override_slots() for more explanation. */ + +#define SLOT0(FUNCNAME, OPSTR) \ +static PyObject * \ +FUNCNAME(PyObject *self) \ +{ \ + static PyObject *cache_str; \ + return call_method(self, OPSTR, &cache_str, "()"); \ +} + +#define SLOT1(FUNCNAME, OPSTR, ARG1TYPE, ARGCODES) \ +static PyObject * \ +FUNCNAME(PyObject *self, ARG1TYPE arg1) \ +{ \ + static PyObject *cache_str; \ + return call_method(self, OPSTR, &cache_str, "(" ARGCODES ")", arg1); \ +} + +/* Boolean helper for SLOT1BINFULL(). + right.__class__ is a nontrivial subclass of left.__class__. */ +static int +method_is_overloaded(PyObject *left, PyObject *right, char *name) +{ + PyObject *a, *b; + int ok; + + b = PyObject_GetAttrString((PyObject *)(right->ob_type), name); + if (b == NULL) { + PyErr_Clear(); + /* If right doesn't have it, it's not overloaded */ + return 0; + } + + a = PyObject_GetAttrString((PyObject *)(left->ob_type), name); + if (a == NULL) { + PyErr_Clear(); + Py_DECREF(b); + /* If right has it but left doesn't, it's overloaded */ + return 1; + } + + ok = PyObject_RichCompareBool(a, b, Py_NE); + Py_DECREF(a); + Py_DECREF(b); + if (ok < 0) { + PyErr_Clear(); + return 0; + } + + return ok; +} + + +#define SLOT1BINFULL(FUNCNAME, TESTFUNC, SLOTNAME, OPSTR, ROPSTR) \ +static PyObject * \ +FUNCNAME(PyObject *self, PyObject *other) \ +{ \ + static PyObject *cache_str, *rcache_str; \ + int do_other = self->ob_type != other->ob_type && \ + other->ob_type->tp_as_number != NULL && \ + other->ob_type->tp_as_number->SLOTNAME == TESTFUNC; \ + if (self->ob_type->tp_as_number != NULL && \ + self->ob_type->tp_as_number->SLOTNAME == TESTFUNC) { \ + PyObject *r; \ + if (do_other && \ + PyType_IsSubtype(other->ob_type, self->ob_type) && \ + method_is_overloaded(self, other, ROPSTR)) { \ + r = call_maybe( \ + other, ROPSTR, &rcache_str, "(O)", self); \ + if (r != Py_NotImplemented) \ + return r; \ + Py_DECREF(r); \ + do_other = 0; \ + } \ + r = call_maybe( \ + self, OPSTR, &cache_str, "(O)", other); \ + if (r != Py_NotImplemented || \ + other->ob_type == self->ob_type) \ + return r; \ + Py_DECREF(r); \ + } \ + if (do_other) { \ + return call_maybe( \ + other, ROPSTR, &rcache_str, "(O)", self); \ + } \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ +} + +#define SLOT1BIN(FUNCNAME, SLOTNAME, OPSTR, ROPSTR) \ + SLOT1BINFULL(FUNCNAME, FUNCNAME, SLOTNAME, OPSTR, ROPSTR) + +#define SLOT2(FUNCNAME, OPSTR, ARG1TYPE, ARG2TYPE, ARGCODES) \ +static PyObject * \ +FUNCNAME(PyObject *self, ARG1TYPE arg1, ARG2TYPE arg2) \ +{ \ + static PyObject *cache_str; \ + return call_method(self, OPSTR, &cache_str, \ + "(" ARGCODES ")", arg1, arg2); \ +} + +static int +slot_sq_length(PyObject *self) +{ + static PyObject *len_str; + PyObject *res = call_method(self, "__len__", &len_str, "()"); + int len; + + if (res == NULL) + return -1; + len = (int)PyInt_AsLong(res); + Py_DECREF(res); + if (len == -1 && PyErr_Occurred()) + return -1; + if (len < 0) { + PyErr_SetString(PyExc_ValueError, + "__len__() should return >= 0"); + return -1; + } + return len; +} + +SLOT1(slot_sq_concat, "__add__", PyObject *, "O") +SLOT1(slot_sq_repeat, "__mul__", int, "i") + +/* Super-optimized version of slot_sq_item. + Other slots could do the same... */ +static PyObject * +slot_sq_item(PyObject *self, int i) +{ + static PyObject *getitem_str; + PyObject *func, *args = NULL, *ival = NULL, *retval = NULL; + descrgetfunc f; + + if (getitem_str == NULL) { + getitem_str = PyString_InternFromString("__getitem__"); + if (getitem_str == NULL) + return NULL; + } + func = _PyType_Lookup(self->ob_type, getitem_str); + if (func != NULL) { + if ((f = func->ob_type->tp_descr_get) == NULL) + Py_INCREF(func); + else { + func = f(func, self, (PyObject *)(self->ob_type)); + if (func == NULL) { + return NULL; + } + } + ival = PyInt_FromLong(i); + if (ival != NULL) { + args = PyTuple_New(1); + if (args != NULL) { + PyTuple_SET_ITEM(args, 0, ival); + retval = PyObject_Call(func, args, NULL); + Py_XDECREF(args); + Py_XDECREF(func); + return retval; + } + } + } + else { + PyErr_SetObject(PyExc_AttributeError, getitem_str); + } + Py_XDECREF(args); + Py_XDECREF(ival); + Py_XDECREF(func); + return NULL; +} + +SLOT2(slot_sq_slice, "__getslice__", int, int, "ii") + +static int +slot_sq_ass_item(PyObject *self, int index, PyObject *value) +{ + PyObject *res; + static PyObject *delitem_str, *setitem_str; + + if (value == NULL) + res = call_method(self, "__delitem__", &delitem_str, + "(i)", index); + else + res = call_method(self, "__setitem__", &setitem_str, + "(iO)", index, value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static int +slot_sq_ass_slice(PyObject *self, int i, int j, PyObject *value) +{ + PyObject *res; + static PyObject *delslice_str, *setslice_str; + + if (value == NULL) + res = call_method(self, "__delslice__", &delslice_str, + "(ii)", i, j); + else + res = call_method(self, "__setslice__", &setslice_str, + "(iiO)", i, j, value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static int +slot_sq_contains(PyObject *self, PyObject *value) +{ + PyObject *func, *res, *args; + int result = -1; + + static PyObject *contains_str; + + func = lookup_maybe(self, "__contains__", &contains_str); + if (func != NULL) { + args = Py_BuildValue("(O)", value); + if (args == NULL) + res = NULL; + else { + res = PyObject_Call(func, args, NULL); + Py_DECREF(args); + } + Py_DECREF(func); + if (res != NULL) { + result = PyObject_IsTrue(res); + Py_DECREF(res); + } + } + else if (! PyErr_Occurred()) { + result = _PySequence_IterSearch(self, value, + PY_ITERSEARCH_CONTAINS); + } + return result; +} + +SLOT1(slot_sq_inplace_concat, "__iadd__", PyObject *, "O") +SLOT1(slot_sq_inplace_repeat, "__imul__", int, "i") + +#define slot_mp_length slot_sq_length + +SLOT1(slot_mp_subscript, "__getitem__", PyObject *, "O") + +static int +slot_mp_ass_subscript(PyObject *self, PyObject *key, PyObject *value) +{ + PyObject *res; + static PyObject *delitem_str, *setitem_str; + + if (value == NULL) + res = call_method(self, "__delitem__", &delitem_str, + "(O)", key); + else + res = call_method(self, "__setitem__", &setitem_str, + "(OO)", key, value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +SLOT1BIN(slot_nb_add, nb_add, "__add__", "__radd__") +SLOT1BIN(slot_nb_subtract, nb_subtract, "__sub__", "__rsub__") +SLOT1BIN(slot_nb_multiply, nb_multiply, "__mul__", "__rmul__") +SLOT1BIN(slot_nb_divide, nb_divide, "__div__", "__rdiv__") +SLOT1BIN(slot_nb_remainder, nb_remainder, "__mod__", "__rmod__") +SLOT1BIN(slot_nb_divmod, nb_divmod, "__divmod__", "__rdivmod__") + +static PyObject *slot_nb_power(PyObject *, PyObject *, PyObject *); + +SLOT1BINFULL(slot_nb_power_binary, slot_nb_power, + nb_power, "__pow__", "__rpow__") + +static PyObject * +slot_nb_power(PyObject *self, PyObject *other, PyObject *modulus) +{ + static PyObject *pow_str; + + if (modulus == Py_None) + return slot_nb_power_binary(self, other); + /* Three-arg power doesn't use __rpow__. But ternary_op + can call this when the second argument's type uses + slot_nb_power, so check before calling self.__pow__. */ + if (self->ob_type->tp_as_number != NULL && + self->ob_type->tp_as_number->nb_power == slot_nb_power) { + return call_method(self, "__pow__", &pow_str, + "(OO)", other, modulus); + } + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +SLOT0(slot_nb_negative, "__neg__") +SLOT0(slot_nb_positive, "__pos__") +SLOT0(slot_nb_absolute, "__abs__") + +static int +slot_nb_nonzero(PyObject *self) +{ + PyObject *func, *args; + static PyObject *nonzero_str, *len_str; + int result = -1; + + func = lookup_maybe(self, "__nonzero__", &nonzero_str); + if (func == NULL) { + if (PyErr_Occurred()) + return -1; + func = lookup_maybe(self, "__len__", &len_str); + if (func == NULL) + return PyErr_Occurred() ? -1 : 1; + } + args = PyTuple_New(0); + if (args != NULL) { + PyObject *temp = PyObject_Call(func, args, NULL); + Py_DECREF(args); + if (temp != NULL) { + if (PyInt_CheckExact(temp) || PyBool_Check(temp)) + result = PyObject_IsTrue(temp); + else { + PyErr_Format(PyExc_TypeError, + "__nonzero__ should return " + "bool or int, returned %s", + temp->ob_type->tp_name); + result = -1; + } + Py_DECREF(temp); + } + } + Py_DECREF(func); + return result; +} + +SLOT0(slot_nb_invert, "__invert__") +SLOT1BIN(slot_nb_lshift, nb_lshift, "__lshift__", "__rlshift__") +SLOT1BIN(slot_nb_rshift, nb_rshift, "__rshift__", "__rrshift__") +SLOT1BIN(slot_nb_and, nb_and, "__and__", "__rand__") +SLOT1BIN(slot_nb_xor, nb_xor, "__xor__", "__rxor__") +SLOT1BIN(slot_nb_or, nb_or, "__or__", "__ror__") + +static int +slot_nb_coerce(PyObject **a, PyObject **b) +{ + static PyObject *coerce_str; + PyObject *self = *a, *other = *b; + + if (self->ob_type->tp_as_number != NULL && + self->ob_type->tp_as_number->nb_coerce == slot_nb_coerce) { + PyObject *r; + r = call_maybe( + self, "__coerce__", &coerce_str, "(O)", other); + if (r == NULL) + return -1; + if (r == Py_NotImplemented) { + Py_DECREF(r); + } + else { + if (!PyTuple_Check(r) || PyTuple_GET_SIZE(r) != 2) { + PyErr_SetString(PyExc_TypeError, + "__coerce__ didn't return a 2-tuple"); + Py_DECREF(r); + return -1; + } + *a = PyTuple_GET_ITEM(r, 0); + Py_INCREF(*a); + *b = PyTuple_GET_ITEM(r, 1); + Py_INCREF(*b); + Py_DECREF(r); + return 0; + } + } + if (other->ob_type->tp_as_number != NULL && + other->ob_type->tp_as_number->nb_coerce == slot_nb_coerce) { + PyObject *r; + r = call_maybe( + other, "__coerce__", &coerce_str, "(O)", self); + if (r == NULL) + return -1; + if (r == Py_NotImplemented) { + Py_DECREF(r); + return 1; + } + if (!PyTuple_Check(r) || PyTuple_GET_SIZE(r) != 2) { + PyErr_SetString(PyExc_TypeError, + "__coerce__ didn't return a 2-tuple"); + Py_DECREF(r); + return -1; + } + *a = PyTuple_GET_ITEM(r, 1); + Py_INCREF(*a); + *b = PyTuple_GET_ITEM(r, 0); + Py_INCREF(*b); + Py_DECREF(r); + return 0; + } + return 1; +} + +SLOT0(slot_nb_int, "__int__") +SLOT0(slot_nb_long, "__long__") +SLOT0(slot_nb_float, "__float__") +SLOT0(slot_nb_oct, "__oct__") +SLOT0(slot_nb_hex, "__hex__") +SLOT1(slot_nb_inplace_add, "__iadd__", PyObject *, "O") +SLOT1(slot_nb_inplace_subtract, "__isub__", PyObject *, "O") +SLOT1(slot_nb_inplace_multiply, "__imul__", PyObject *, "O") +SLOT1(slot_nb_inplace_divide, "__idiv__", PyObject *, "O") +SLOT1(slot_nb_inplace_remainder, "__imod__", PyObject *, "O") +SLOT1(slot_nb_inplace_power, "__ipow__", PyObject *, "O") +SLOT1(slot_nb_inplace_lshift, "__ilshift__", PyObject *, "O") +SLOT1(slot_nb_inplace_rshift, "__irshift__", PyObject *, "O") +SLOT1(slot_nb_inplace_and, "__iand__", PyObject *, "O") +SLOT1(slot_nb_inplace_xor, "__ixor__", PyObject *, "O") +SLOT1(slot_nb_inplace_or, "__ior__", PyObject *, "O") +SLOT1BIN(slot_nb_floor_divide, nb_floor_divide, + "__floordiv__", "__rfloordiv__") +SLOT1BIN(slot_nb_true_divide, nb_true_divide, "__truediv__", "__rtruediv__") +SLOT1(slot_nb_inplace_floor_divide, "__ifloordiv__", PyObject *, "O") +SLOT1(slot_nb_inplace_true_divide, "__itruediv__", PyObject *, "O") + +static int +half_compare(PyObject *self, PyObject *other) +{ + PyObject *func, *args, *res; + static PyObject *cmp_str; + int c; + + func = lookup_method(self, "__cmp__", &cmp_str); + if (func == NULL) { + PyErr_Clear(); + } + else { + args = Py_BuildValue("(O)", other); + if (args == NULL) + res = NULL; + else { + res = PyObject_Call(func, args, NULL); + Py_DECREF(args); + } + Py_DECREF(func); + if (res != Py_NotImplemented) { + if (res == NULL) + return -2; + c = PyInt_AsLong(res); + Py_DECREF(res); + if (c == -1 && PyErr_Occurred()) + return -2; + return (c < 0) ? -1 : (c > 0) ? 1 : 0; + } + Py_DECREF(res); + } + return 2; +} + +/* This slot is published for the benefit of try_3way_compare in object.c */ +int +_PyObject_SlotCompare(PyObject *self, PyObject *other) +{ + int c; + + if (self->ob_type->tp_compare == _PyObject_SlotCompare) { + c = half_compare(self, other); + if (c <= 1) + return c; + } + if (other->ob_type->tp_compare == _PyObject_SlotCompare) { + c = half_compare(other, self); + if (c < -1) + return -2; + if (c <= 1) + return -c; + } + return (void *)self < (void *)other ? -1 : + (void *)self > (void *)other ? 1 : 0; +} + +static PyObject * +slot_tp_repr(PyObject *self) +{ + PyObject *func, *res; + static PyObject *repr_str; + + func = lookup_method(self, "__repr__", &repr_str); + if (func != NULL) { + res = PyEval_CallObject(func, NULL); + Py_DECREF(func); + return res; + } + PyErr_Clear(); + return PyString_FromFormat("<%s object at %p>", + self->ob_type->tp_name, self); +} + +static PyObject * +slot_tp_str(PyObject *self) +{ + PyObject *func, *res; + static PyObject *str_str; + + func = lookup_method(self, "__str__", &str_str); + if (func != NULL) { + res = PyEval_CallObject(func, NULL); + Py_DECREF(func); + return res; + } + else { + PyErr_Clear(); + return slot_tp_repr(self); + } +} + +static long +slot_tp_hash(PyObject *self) +{ + PyObject *func; + static PyObject *hash_str, *eq_str, *cmp_str; + long h; + + func = lookup_method(self, "__hash__", &hash_str); + + if (func != NULL) { + PyObject *res = PyEval_CallObject(func, NULL); + Py_DECREF(func); + if (res == NULL) + return -1; + h = PyInt_AsLong(res); + Py_DECREF(res); + } + else { + PyErr_Clear(); + func = lookup_method(self, "__eq__", &eq_str); + if (func == NULL) { + PyErr_Clear(); + func = lookup_method(self, "__cmp__", &cmp_str); + } + if (func != NULL) { + Py_DECREF(func); + PyErr_SetString(PyExc_TypeError, "unhashable type"); + return -1; + } + PyErr_Clear(); + h = _Py_HashPointer((void *)self); + } + if (h == -1 && !PyErr_Occurred()) + h = -2; + return h; +} + +static PyObject * +slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds) +{ + static PyObject *call_str; + PyObject *meth = lookup_method(self, "__call__", &call_str); + PyObject *res; + + if (meth == NULL) + return NULL; + res = PyObject_Call(meth, args, kwds); + Py_DECREF(meth); + return res; +} + +/* There are two slot dispatch functions for tp_getattro. + + - slot_tp_getattro() is used when __getattribute__ is overridden + but no __getattr__ hook is present; + + - slot_tp_getattr_hook() is used when a __getattr__ hook is present. + + The code in update_one_slot() always installs slot_tp_getattr_hook(); this + detects the absence of __getattr__ and then installs the simpler slot if + necessary. */ + +static PyObject * +slot_tp_getattro(PyObject *self, PyObject *name) +{ + static PyObject *getattribute_str = NULL; + return call_method(self, "__getattribute__", &getattribute_str, + "(O)", name); +} + +static PyObject * +slot_tp_getattr_hook(PyObject *self, PyObject *name) +{ + PyTypeObject *tp = self->ob_type; + PyObject *getattr, *getattribute, *res; + static PyObject *getattribute_str = NULL; + static PyObject *getattr_str = NULL; + + if (getattr_str == NULL) { + getattr_str = PyString_InternFromString("__getattr__"); + if (getattr_str == NULL) + return NULL; + } + if (getattribute_str == NULL) { + getattribute_str = + PyString_InternFromString("__getattribute__"); + if (getattribute_str == NULL) + return NULL; + } + getattr = _PyType_Lookup(tp, getattr_str); + if (getattr == NULL) { + /* No __getattr__ hook: use a simpler dispatcher */ + tp->tp_getattro = slot_tp_getattro; + return slot_tp_getattro(self, name); + } + getattribute = _PyType_Lookup(tp, getattribute_str); + if (getattribute == NULL || + (getattribute->ob_type == &PyWrapperDescr_Type && + ((PyWrapperDescrObject *)getattribute)->d_wrapped == + (void *)PyObject_GenericGetAttr)) + res = PyObject_GenericGetAttr(self, name); + else + res = PyObject_CallFunction(getattribute, "OO", self, name); + if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + res = PyObject_CallFunction(getattr, "OO", self, name); + } + return res; +} + +static int +slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value) +{ + PyObject *res; + static PyObject *delattr_str, *setattr_str; + + if (value == NULL) + res = call_method(self, "__delattr__", &delattr_str, + "(O)", name); + else + res = call_method(self, "__setattr__", &setattr_str, + "(OO)", name, value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +/* Map rich comparison operators to their __xx__ namesakes */ +static char *name_op[] = { + "__lt__", + "__le__", + "__eq__", + "__ne__", + "__gt__", + "__ge__", +}; + +static PyObject * +half_richcompare(PyObject *self, PyObject *other, int op) +{ + PyObject *func, *args, *res; + static PyObject *op_str[6]; + + func = lookup_method(self, name_op[op], &op_str[op]); + if (func == NULL) { + PyErr_Clear(); + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + args = Py_BuildValue("(O)", other); + if (args == NULL) + res = NULL; + else { + res = PyObject_Call(func, args, NULL); + Py_DECREF(args); + } + Py_DECREF(func); + return res; +} + +/* Map rich comparison operators to their swapped version, e.g. LT --> GT */ +static int swapped_op[] = {Py_GT, Py_GE, Py_EQ, Py_NE, Py_LT, Py_LE}; + +static PyObject * +slot_tp_richcompare(PyObject *self, PyObject *other, int op) +{ + PyObject *res; + + if (self->ob_type->tp_richcompare == slot_tp_richcompare) { + res = half_richcompare(self, other, op); + if (res != Py_NotImplemented) + return res; + Py_DECREF(res); + } + if (other->ob_type->tp_richcompare == slot_tp_richcompare) { + res = half_richcompare(other, self, swapped_op[op]); + if (res != Py_NotImplemented) { + return res; + } + Py_DECREF(res); + } + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static PyObject * +slot_tp_iter(PyObject *self) +{ + PyObject *func, *res; + static PyObject *iter_str, *getitem_str; + + func = lookup_method(self, "__iter__", &iter_str); + if (func != NULL) { + PyObject *args; + args = res = PyTuple_New(0); + if (args != NULL) { + res = PyObject_Call(func, args, NULL); + Py_DECREF(args); + } + Py_DECREF(func); + return res; + } + PyErr_Clear(); + func = lookup_method(self, "__getitem__", &getitem_str); + if (func == NULL) { + PyErr_SetString(PyExc_TypeError, + "iteration over non-sequence"); + return NULL; + } + Py_DECREF(func); + return PySeqIter_New(self); +} + +static PyObject * +slot_tp_iternext(PyObject *self) +{ + static PyObject *next_str; + return call_method(self, "next", &next_str, "()"); +} + +static PyObject * +slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type) +{ + PyTypeObject *tp = self->ob_type; + PyObject *get; + static PyObject *get_str = NULL; + + if (get_str == NULL) { + get_str = PyString_InternFromString("__get__"); + if (get_str == NULL) + return NULL; + } + get = _PyType_Lookup(tp, get_str); + if (get == NULL) { + /* Avoid further slowdowns */ + if (tp->tp_descr_get == slot_tp_descr_get) + tp->tp_descr_get = NULL; + Py_INCREF(self); + return self; + } + if (obj == NULL) + obj = Py_None; + if (type == NULL) + type = Py_None; + return PyObject_CallFunction(get, "OOO", self, obj, type); +} + +static int +slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value) +{ + PyObject *res; + static PyObject *del_str, *set_str; + + if (value == NULL) + res = call_method(self, "__delete__", &del_str, + "(O)", target); + else + res = call_method(self, "__set__", &set_str, + "(OO)", target, value); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static int +slot_tp_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + static PyObject *init_str; + PyObject *meth = lookup_method(self, "__init__", &init_str); + PyObject *res; + + if (meth == NULL) + return -1; + res = PyObject_Call(meth, args, kwds); + Py_DECREF(meth); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; +} + +static PyObject * +slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + static PyObject *new_str; + PyObject *func; + PyObject *newargs, *x; + int i, n; + + if (new_str == NULL) { + new_str = PyString_InternFromString("__new__"); + if (new_str == NULL) + return NULL; + } + func = PyObject_GetAttr((PyObject *)type, new_str); + if (func == NULL) + return NULL; + assert(PyTuple_Check(args)); + n = PyTuple_GET_SIZE(args); + newargs = PyTuple_New(n+1); + if (newargs == NULL) + return NULL; + Py_INCREF(type); + PyTuple_SET_ITEM(newargs, 0, (PyObject *)type); + for (i = 0; i < n; i++) { + x = PyTuple_GET_ITEM(args, i); + Py_INCREF(x); + PyTuple_SET_ITEM(newargs, i+1, x); + } + x = PyObject_Call(func, newargs, kwds); + Py_DECREF(newargs); + Py_DECREF(func); + return x; +} + +static void +slot_tp_del(PyObject *self) +{ + static PyObject *del_str = NULL; + PyObject *del, *res; + PyObject *error_type, *error_value, *error_traceback; + + /* Temporarily resurrect the object. */ + assert(self->ob_refcnt == 0); + self->ob_refcnt = 1; + + /* Save the current exception, if any. */ + PyErr_Fetch(&error_type, &error_value, &error_traceback); + + /* Execute __del__ method, if any. */ + del = lookup_maybe(self, "__del__", &del_str); + if (del != NULL) { + res = PyEval_CallObject(del, NULL); + if (res == NULL) + PyErr_WriteUnraisable(del); + else + Py_DECREF(res); + Py_DECREF(del); + } + + /* Restore the saved exception. */ + PyErr_Restore(error_type, error_value, error_traceback); + + /* Undo the temporary resurrection; can't use DECREF here, it would + * cause a recursive call. + */ + assert(self->ob_refcnt > 0); + if (--self->ob_refcnt == 0) + return; /* this is the normal path out */ + + /* __del__ resurrected it! Make it look like the original Py_DECREF + * never happened. + */ + { + int refcnt = self->ob_refcnt; + _Py_NewReference(self); + self->ob_refcnt = refcnt; + } + assert(!PyType_IS_GC(self->ob_type) || + _Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED); + /* If Py_REF_DEBUG, the original decref dropped _Py_RefTotal, but + * _Py_NewReference bumped it again, so that's a wash. + * If Py_TRACE_REFS, _Py_NewReference re-added self to the object + * chain, so no more to do there either. + * If COUNT_ALLOCS, the original decref bumped tp_frees, and + * _Py_NewReference bumped tp_allocs: both of those need to be + * undone. + */ +#ifdef COUNT_ALLOCS + --self->ob_type->tp_frees; + --self->ob_type->tp_allocs; +#endif +} + + +/* Table mapping __foo__ names to tp_foo offsets and slot_tp_foo wrapper + functions. The offsets here are relative to the 'PyHeapTypeObject' + structure, which incorporates the additional structures used for numbers, + sequences and mappings. + Note that multiple names may map to the same slot (e.g. __eq__, + __ne__ etc. all map to tp_richcompare) and one name may map to multiple + slots (e.g. __str__ affects tp_str as well as tp_repr). The table is + terminated with an all-zero entry. (This table is further initialized and + sorted in init_slotdefs() below.) */ + +typedef struct wrapperbase slotdef; + +#undef TPSLOT +#undef FLSLOT +#undef ETSLOT +#undef SQSLOT +#undef MPSLOT +#undef NBSLOT +#undef UNSLOT +#undef IBSLOT +#undef BINSLOT +#undef RBINSLOT + +#define TPSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ + {NAME, offsetof(PyTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \ + PyDoc_STR(DOC)} +#define FLSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC, FLAGS) \ + {NAME, offsetof(PyTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \ + PyDoc_STR(DOC), FLAGS} +#define ETSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ + {NAME, offsetof(PyHeapTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \ + PyDoc_STR(DOC)} +#define SQSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ + ETSLOT(NAME, as_sequence.SLOT, FUNCTION, WRAPPER, DOC) +#define MPSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ + ETSLOT(NAME, as_mapping.SLOT, FUNCTION, WRAPPER, DOC) +#define NBSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ + ETSLOT(NAME, as_number.SLOT, FUNCTION, WRAPPER, DOC) +#define UNSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ + ETSLOT(NAME, as_number.SLOT, FUNCTION, WRAPPER, \ + "x." NAME "() <==> " DOC) +#define IBSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \ + ETSLOT(NAME, as_number.SLOT, FUNCTION, WRAPPER, \ + "x." NAME "(y) <==> x" DOC "y") +#define BINSLOT(NAME, SLOT, FUNCTION, DOC) \ + ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_l, \ + "x." NAME "(y) <==> x" DOC "y") +#define RBINSLOT(NAME, SLOT, FUNCTION, DOC) \ + ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_r, \ + "x." NAME "(y) <==> y" DOC "x") + +static slotdef slotdefs[] = { + SQSLOT("__len__", sq_length, slot_sq_length, wrap_inquiry, + "x.__len__() <==> len(x)"), + SQSLOT("__add__", sq_concat, slot_sq_concat, wrap_binaryfunc, + "x.__add__(y) <==> x+y"), + SQSLOT("__mul__", sq_repeat, slot_sq_repeat, wrap_intargfunc, + "x.__mul__(n) <==> x*n"), + SQSLOT("__rmul__", sq_repeat, slot_sq_repeat, wrap_intargfunc, + "x.__rmul__(n) <==> n*x"), + SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item, + "x.__getitem__(y) <==> x[y]"), + SQSLOT("__getslice__", sq_slice, slot_sq_slice, wrap_intintargfunc, + "x.__getslice__(i, j) <==> x[i:j]\n\ + \n\ + Use of negative indices is not supported."), + SQSLOT("__setitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_setitem, + "x.__setitem__(i, y) <==> x[i]=y"), + SQSLOT("__delitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_delitem, + "x.__delitem__(y) <==> del x[y]"), + SQSLOT("__setslice__", sq_ass_slice, slot_sq_ass_slice, + wrap_intintobjargproc, + "x.__setslice__(i, j, y) <==> x[i:j]=y\n\ + \n\ + Use of negative indices is not supported."), + SQSLOT("__delslice__", sq_ass_slice, slot_sq_ass_slice, wrap_delslice, + "x.__delslice__(i, j) <==> del x[i:j]\n\ + \n\ + Use of negative indices is not supported."), + SQSLOT("__contains__", sq_contains, slot_sq_contains, wrap_objobjproc, + "x.__contains__(y) <==> y in x"), + SQSLOT("__iadd__", sq_inplace_concat, slot_sq_inplace_concat, + wrap_binaryfunc, "x.__iadd__(y) <==> x+=y"), + SQSLOT("__imul__", sq_inplace_repeat, slot_sq_inplace_repeat, + wrap_intargfunc, "x.__imul__(y) <==> x*=y"), + + MPSLOT("__len__", mp_length, slot_mp_length, wrap_inquiry, + "x.__len__() <==> len(x)"), + MPSLOT("__getitem__", mp_subscript, slot_mp_subscript, + wrap_binaryfunc, + "x.__getitem__(y) <==> x[y]"), + MPSLOT("__setitem__", mp_ass_subscript, slot_mp_ass_subscript, + wrap_objobjargproc, + "x.__setitem__(i, y) <==> x[i]=y"), + MPSLOT("__delitem__", mp_ass_subscript, slot_mp_ass_subscript, + wrap_delitem, + "x.__delitem__(y) <==> del x[y]"), + + BINSLOT("__add__", nb_add, slot_nb_add, + "+"), + RBINSLOT("__radd__", nb_add, slot_nb_add, + "+"), + BINSLOT("__sub__", nb_subtract, slot_nb_subtract, + "-"), + RBINSLOT("__rsub__", nb_subtract, slot_nb_subtract, + "-"), + BINSLOT("__mul__", nb_multiply, slot_nb_multiply, + "*"), + RBINSLOT("__rmul__", nb_multiply, slot_nb_multiply, + "*"), + BINSLOT("__div__", nb_divide, slot_nb_divide, + "/"), + RBINSLOT("__rdiv__", nb_divide, slot_nb_divide, + "/"), + BINSLOT("__mod__", nb_remainder, slot_nb_remainder, + "%"), + RBINSLOT("__rmod__", nb_remainder, slot_nb_remainder, + "%"), + BINSLOT("__divmod__", nb_divmod, slot_nb_divmod, + "divmod(x, y)"), + RBINSLOT("__rdivmod__", nb_divmod, slot_nb_divmod, + "divmod(y, x)"), + NBSLOT("__pow__", nb_power, slot_nb_power, wrap_ternaryfunc, + "x.__pow__(y[, z]) <==> pow(x, y[, z])"), + NBSLOT("__rpow__", nb_power, slot_nb_power, wrap_ternaryfunc_r, + "y.__rpow__(x[, z]) <==> pow(x, y[, z])"), + UNSLOT("__neg__", nb_negative, slot_nb_negative, wrap_unaryfunc, "-x"), + UNSLOT("__pos__", nb_positive, slot_nb_positive, wrap_unaryfunc, "+x"), + UNSLOT("__abs__", nb_absolute, slot_nb_absolute, wrap_unaryfunc, + "abs(x)"), + UNSLOT("__nonzero__", nb_nonzero, slot_nb_nonzero, wrap_inquirypred, + "x != 0"), + UNSLOT("__invert__", nb_invert, slot_nb_invert, wrap_unaryfunc, "~x"), + BINSLOT("__lshift__", nb_lshift, slot_nb_lshift, "<<"), + RBINSLOT("__rlshift__", nb_lshift, slot_nb_lshift, "<<"), + BINSLOT("__rshift__", nb_rshift, slot_nb_rshift, ">>"), + RBINSLOT("__rrshift__", nb_rshift, slot_nb_rshift, ">>"), + BINSLOT("__and__", nb_and, slot_nb_and, "&"), + RBINSLOT("__rand__", nb_and, slot_nb_and, "&"), + BINSLOT("__xor__", nb_xor, slot_nb_xor, "^"), + RBINSLOT("__rxor__", nb_xor, slot_nb_xor, "^"), + BINSLOT("__or__", nb_or, slot_nb_or, "|"), + RBINSLOT("__ror__", nb_or, slot_nb_or, "|"), + NBSLOT("__coerce__", nb_coerce, slot_nb_coerce, wrap_coercefunc, + "x.__coerce__(y) <==> coerce(x, y)"), + UNSLOT("__int__", nb_int, slot_nb_int, wrap_unaryfunc, + "int(x)"), + UNSLOT("__long__", nb_long, slot_nb_long, wrap_unaryfunc, + "long(x)"), + UNSLOT("__float__", nb_float, slot_nb_float, wrap_unaryfunc, + "float(x)"), + UNSLOT("__oct__", nb_oct, slot_nb_oct, wrap_unaryfunc, + "oct(x)"), + UNSLOT("__hex__", nb_hex, slot_nb_hex, wrap_unaryfunc, + "hex(x)"), + IBSLOT("__iadd__", nb_inplace_add, slot_nb_inplace_add, + wrap_binaryfunc, "+"), + IBSLOT("__isub__", nb_inplace_subtract, slot_nb_inplace_subtract, + wrap_binaryfunc, "-"), + IBSLOT("__imul__", nb_inplace_multiply, slot_nb_inplace_multiply, + wrap_binaryfunc, "*"), + IBSLOT("__idiv__", nb_inplace_divide, slot_nb_inplace_divide, + wrap_binaryfunc, "/"), + IBSLOT("__imod__", nb_inplace_remainder, slot_nb_inplace_remainder, + wrap_binaryfunc, "%"), + IBSLOT("__ipow__", nb_inplace_power, slot_nb_inplace_power, + wrap_binaryfunc, "**"), + IBSLOT("__ilshift__", nb_inplace_lshift, slot_nb_inplace_lshift, + wrap_binaryfunc, "<<"), + IBSLOT("__irshift__", nb_inplace_rshift, slot_nb_inplace_rshift, + wrap_binaryfunc, ">>"), + IBSLOT("__iand__", nb_inplace_and, slot_nb_inplace_and, + wrap_binaryfunc, "&"), + IBSLOT("__ixor__", nb_inplace_xor, slot_nb_inplace_xor, + wrap_binaryfunc, "^"), + IBSLOT("__ior__", nb_inplace_or, slot_nb_inplace_or, + wrap_binaryfunc, "|"), + BINSLOT("__floordiv__", nb_floor_divide, slot_nb_floor_divide, "//"), + RBINSLOT("__rfloordiv__", nb_floor_divide, slot_nb_floor_divide, "//"), + BINSLOT("__truediv__", nb_true_divide, slot_nb_true_divide, "/"), + RBINSLOT("__rtruediv__", nb_true_divide, slot_nb_true_divide, "/"), + IBSLOT("__ifloordiv__", nb_inplace_floor_divide, + slot_nb_inplace_floor_divide, wrap_binaryfunc, "//"), + IBSLOT("__itruediv__", nb_inplace_true_divide, + slot_nb_inplace_true_divide, wrap_binaryfunc, "/"), + + TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc, + "x.__str__() <==> str(x)"), + TPSLOT("__str__", tp_print, NULL, NULL, ""), + TPSLOT("__repr__", tp_repr, slot_tp_repr, wrap_unaryfunc, + "x.__repr__() <==> repr(x)"), + TPSLOT("__repr__", tp_print, NULL, NULL, ""), + TPSLOT("__cmp__", tp_compare, _PyObject_SlotCompare, wrap_cmpfunc, + "x.__cmp__(y) <==> cmp(x,y)"), + TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc, + "x.__hash__() <==> hash(x)"), + FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call, + "x.__call__(...) <==> x(...)", PyWrapperFlag_KEYWORDS), + TPSLOT("__getattribute__", tp_getattro, slot_tp_getattr_hook, + wrap_binaryfunc, "x.__getattribute__('name') <==> x.name"), + TPSLOT("__getattribute__", tp_getattr, NULL, NULL, ""), + TPSLOT("__getattr__", tp_getattro, slot_tp_getattr_hook, NULL, ""), + TPSLOT("__getattr__", tp_getattr, NULL, NULL, ""), + TPSLOT("__setattr__", tp_setattro, slot_tp_setattro, wrap_setattr, + "x.__setattr__('name', value) <==> x.name = value"), + TPSLOT("__setattr__", tp_setattr, NULL, NULL, ""), + TPSLOT("__delattr__", tp_setattro, slot_tp_setattro, wrap_delattr, + "x.__delattr__('name') <==> del x.name"), + TPSLOT("__delattr__", tp_setattr, NULL, NULL, ""), + TPSLOT("__lt__", tp_richcompare, slot_tp_richcompare, richcmp_lt, + "x.__lt__(y) <==> x x<=y"), + TPSLOT("__eq__", tp_richcompare, slot_tp_richcompare, richcmp_eq, + "x.__eq__(y) <==> x==y"), + TPSLOT("__ne__", tp_richcompare, slot_tp_richcompare, richcmp_ne, + "x.__ne__(y) <==> x!=y"), + TPSLOT("__gt__", tp_richcompare, slot_tp_richcompare, richcmp_gt, + "x.__gt__(y) <==> x>y"), + TPSLOT("__ge__", tp_richcompare, slot_tp_richcompare, richcmp_ge, + "x.__ge__(y) <==> x>=y"), + TPSLOT("__iter__", tp_iter, slot_tp_iter, wrap_unaryfunc, + "x.__iter__() <==> iter(x)"), + TPSLOT("next", tp_iternext, slot_tp_iternext, wrap_next, + "x.next() -> the next value, or raise StopIteration"), + TPSLOT("__get__", tp_descr_get, slot_tp_descr_get, wrap_descr_get, + "descr.__get__(obj[, type]) -> value"), + TPSLOT("__set__", tp_descr_set, slot_tp_descr_set, wrap_descr_set, + "descr.__set__(obj, value)"), + TPSLOT("__delete__", tp_descr_set, slot_tp_descr_set, + wrap_descr_delete, "descr.__delete__(obj)"), + FLSLOT("__init__", tp_init, slot_tp_init, (wrapperfunc)wrap_init, + "x.__init__(...) initializes x; " + "see x.__class__.__doc__ for signature", + PyWrapperFlag_KEYWORDS), + TPSLOT("__new__", tp_new, slot_tp_new, NULL, ""), + TPSLOT("__del__", tp_del, slot_tp_del, NULL, ""), + {NULL} +}; + +/* Given a type pointer and an offset gotten from a slotdef entry, return a + pointer to the actual slot. This is not quite the same as simply adding + the offset to the type pointer, since it takes care to indirect through the + proper indirection pointer (as_buffer, etc.); it returns NULL if the + indirection pointer is NULL. */ +static void ** +slotptr(PyTypeObject *type, int offset) +{ + char *ptr; + + /* Note: this depends on the order of the members of PyHeapTypeObject! */ + assert(offset >= 0); + assert(offset < offsetof(PyHeapTypeObject, as_buffer)); + if (offset >= offsetof(PyHeapTypeObject, as_sequence)) { + ptr = (void *)type->tp_as_sequence; + offset -= offsetof(PyHeapTypeObject, as_sequence); + } + else if (offset >= offsetof(PyHeapTypeObject, as_mapping)) { + ptr = (void *)type->tp_as_mapping; + offset -= offsetof(PyHeapTypeObject, as_mapping); + } + else if (offset >= offsetof(PyHeapTypeObject, as_number)) { + ptr = (void *)type->tp_as_number; + offset -= offsetof(PyHeapTypeObject, as_number); + } + else { + ptr = (void *)type; + } + if (ptr != NULL) + ptr += offset; + return (void **)ptr; +} + +/* Length of array of slotdef pointers used to store slots with the + same __name__. There should be at most MAX_EQUIV-1 slotdef entries with + the same __name__, for any __name__. Since that's a static property, it is + appropriate to declare fixed-size arrays for this. */ +#define MAX_EQUIV 10 + +/* Return a slot pointer for a given name, but ONLY if the attribute has + exactly one slot function. The name must be an interned string. */ +static void ** +resolve_slotdups(PyTypeObject *type, PyObject *name) +{ + /* XXX Maybe this could be optimized more -- but is it worth it? */ + + /* pname and ptrs act as a little cache */ + static PyObject *pname; + static slotdef *ptrs[MAX_EQUIV]; + slotdef *p, **pp; + void **res, **ptr; + + if (pname != name) { + /* Collect all slotdefs that match name into ptrs. */ + pname = name; + pp = ptrs; + for (p = slotdefs; p->name_strobj; p++) { + if (p->name_strobj == name) + *pp++ = p; + } + *pp = NULL; + } + + /* Look in all matching slots of the type; if exactly one of these has + a filled-in slot, return its value. Otherwise return NULL. */ + res = NULL; + for (pp = ptrs; *pp; pp++) { + ptr = slotptr(type, (*pp)->offset); + if (ptr == NULL || *ptr == NULL) + continue; + if (res != NULL) + return NULL; + res = ptr; + } + return res; +} + +/* Common code for update_slots_callback() and fixup_slot_dispatchers(). This + does some incredibly complex thinking and then sticks something into the + slot. (It sees if the adjacent slotdefs for the same slot have conflicting + interests, and then stores a generic wrapper or a specific function into + the slot.) Return a pointer to the next slotdef with a different offset, + because that's convenient for fixup_slot_dispatchers(). */ +static slotdef * +update_one_slot(PyTypeObject *type, slotdef *p) +{ + PyObject *descr; + PyWrapperDescrObject *d; + void *generic = NULL, *specific = NULL; + int use_generic = 0; + int offset = p->offset; + void **ptr = slotptr(type, offset); + + if (ptr == NULL) { + do { + ++p; + } while (p->offset == offset); + return p; + } + do { + descr = _PyType_Lookup(type, p->name_strobj); + if (descr == NULL) + continue; + if (descr->ob_type == &PyWrapperDescr_Type) { + void **tptr = resolve_slotdups(type, p->name_strobj); + if (tptr == NULL || tptr == ptr) + generic = p->function; + d = (PyWrapperDescrObject *)descr; + if (d->d_base->wrapper == p->wrapper && + PyType_IsSubtype(type, d->d_type)) + { + if (specific == NULL || + specific == d->d_wrapped) + specific = d->d_wrapped; + else + use_generic = 1; + } + } + else if (descr->ob_type == &PyCFunction_Type && + PyCFunction_GET_FUNCTION(descr) == + (PyCFunction)tp_new_wrapper && + strcmp(p->name, "__new__") == 0) + { + /* The __new__ wrapper is not a wrapper descriptor, + so must be special-cased differently. + If we don't do this, creating an instance will + always use slot_tp_new which will look up + __new__ in the MRO which will call tp_new_wrapper + which will look through the base classes looking + for a static base and call its tp_new (usually + PyType_GenericNew), after performing various + sanity checks and constructing a new argument + list. Cut all that nonsense short -- this speeds + up instance creation tremendously. */ + specific = (void *)type->tp_new; + /* XXX I'm not 100% sure that there isn't a hole + in this reasoning that requires additional + sanity checks. I'll buy the first person to + point out a bug in this reasoning a beer. */ + } + else { + use_generic = 1; + generic = p->function; + } + } while ((++p)->offset == offset); + if (specific && !use_generic) + *ptr = specific; + else + *ptr = generic; + return p; +} + +/* In the type, update the slots whose slotdefs are gathered in the pp array. + This is a callback for update_subclasses(). */ +static int +update_slots_callback(PyTypeObject *type, void *data) +{ + slotdef **pp = (slotdef **)data; + + for (; *pp; pp++) + update_one_slot(type, *pp); + return 0; +} + +/* Comparison function for qsort() to compare slotdefs by their offset, and + for equal offset by their address (to force a stable sort). */ +static int +slotdef_cmp(const void *aa, const void *bb) +{ + const slotdef *a = (const slotdef *)aa, *b = (const slotdef *)bb; + int c = a->offset - b->offset; + if (c != 0) + return c; + else + return a - b; +} + +/* Initialize the slotdefs table by adding interned string objects for the + names and sorting the entries. */ +static void +init_slotdefs(void) +{ + slotdef *p; + static int initialized = 0; + + if (initialized) + return; + for (p = slotdefs; p->name; p++) { + p->name_strobj = PyString_InternFromString(p->name); + if (!p->name_strobj) + Py_FatalError("Out of memory interning slotdef names"); + } + qsort((void *)slotdefs, (size_t)(p-slotdefs), sizeof(slotdef), + slotdef_cmp); + initialized = 1; +} + +/* Update the slots after assignment to a class (type) attribute. */ +static int +update_slot(PyTypeObject *type, PyObject *name) +{ + slotdef *ptrs[MAX_EQUIV]; + slotdef *p; + slotdef **pp; + int offset; + + init_slotdefs(); + pp = ptrs; + for (p = slotdefs; p->name; p++) { + /* XXX assume name is interned! */ + if (p->name_strobj == name) + *pp++ = p; + } + *pp = NULL; + for (pp = ptrs; *pp; pp++) { + p = *pp; + offset = p->offset; + while (p > slotdefs && (p-1)->offset == offset) + --p; + *pp = p; + } + if (ptrs[0] == NULL) + return 0; /* Not an attribute that affects any slots */ + return update_subclasses(type, name, + update_slots_callback, (void *)ptrs); +} + +/* Store the proper functions in the slot dispatches at class (type) + definition time, based upon which operations the class overrides in its + dict. */ +static void +fixup_slot_dispatchers(PyTypeObject *type) +{ + slotdef *p; + + init_slotdefs(); + for (p = slotdefs; p->name; ) + p = update_one_slot(type, p); +} + +static void +update_all_slots(PyTypeObject* type) +{ + slotdef *p; + + init_slotdefs(); + for (p = slotdefs; p->name; p++) { + /* update_slot returns int but can't actually fail */ + update_slot(type, p->name_strobj); + } +} + +/* recurse_down_subclasses() and update_subclasses() are mutually + recursive functions to call a callback for all subclasses, + but refraining from recursing into subclasses that define 'name'. */ + +static int +update_subclasses(PyTypeObject *type, PyObject *name, + update_callback callback, void *data) +{ + if (callback(type, data) < 0) + return -1; + return recurse_down_subclasses(type, name, callback, data); +} + +static int +recurse_down_subclasses(PyTypeObject *type, PyObject *name, + update_callback callback, void *data) +{ + PyTypeObject *subclass; + PyObject *ref, *subclasses, *dict; + int i, n; + + subclasses = type->tp_subclasses; + if (subclasses == NULL) + return 0; + assert(PyList_Check(subclasses)); + n = PyList_GET_SIZE(subclasses); + for (i = 0; i < n; i++) { + ref = PyList_GET_ITEM(subclasses, i); + assert(PyWeakref_CheckRef(ref)); + subclass = (PyTypeObject *)PyWeakref_GET_OBJECT(ref); + assert(subclass != NULL); + if ((PyObject *)subclass == Py_None) + continue; + assert(PyType_Check(subclass)); + /* Avoid recursing down into unaffected classes */ + dict = subclass->tp_dict; + if (dict != NULL && PyDict_Check(dict) && + PyDict_GetItem(dict, name) != NULL) + continue; + if (update_subclasses(subclass, name, callback, data) < 0) + return -1; + } + return 0; +} + +/* This function is called by PyType_Ready() to populate the type's + dictionary with method descriptors for function slots. For each + function slot (like tp_repr) that's defined in the type, one or more + corresponding descriptors are added in the type's tp_dict dictionary + under the appropriate name (like __repr__). Some function slots + cause more than one descriptor to be added (for example, the nb_add + slot adds both __add__ and __radd__ descriptors) and some function + slots compete for the same descriptor (for example both sq_item and + mp_subscript generate a __getitem__ descriptor). + + In the latter case, the first slotdef entry encoutered wins. Since + slotdef entries are sorted by the offset of the slot in the + PyHeapTypeObject, this gives us some control over disambiguating + between competing slots: the members of PyHeapTypeObject are listed + from most general to least general, so the most general slot is + preferred. In particular, because as_mapping comes before as_sequence, + for a type that defines both mp_subscript and sq_item, mp_subscript + wins. + + This only adds new descriptors and doesn't overwrite entries in + tp_dict that were previously defined. The descriptors contain a + reference to the C function they must call, so that it's safe if they + are copied into a subtype's __dict__ and the subtype has a different + C function in its slot -- calling the method defined by the + descriptor will call the C function that was used to create it, + rather than the C function present in the slot when it is called. + (This is important because a subtype may have a C function in the + slot that calls the method from the dictionary, and we want to avoid + infinite recursion here.) */ + +static int +add_operators(PyTypeObject *type) +{ + PyObject *dict = type->tp_dict; + slotdef *p; + PyObject *descr; + void **ptr; + + init_slotdefs(); + for (p = slotdefs; p->name; p++) { + if (p->wrapper == NULL) + continue; + ptr = slotptr(type, p->offset); + if (!ptr || !*ptr) + continue; + if (PyDict_GetItem(dict, p->name_strobj)) + continue; + descr = PyDescr_NewWrapper(type, p, *ptr); + if (descr == NULL) + return -1; + if (PyDict_SetItem(dict, p->name_strobj, descr) < 0) + return -1; + Py_DECREF(descr); + } + if (type->tp_new != NULL) { + if (add_tp_new_wrapper(type) < 0) + return -1; + } + return 0; +} + + +/* Cooperative 'super' */ + +typedef struct { + PyObject_HEAD + PyTypeObject *type; + PyObject *obj; + PyTypeObject *obj_type; +} superobject; + +static PyMemberDef super_members[] = { + {"__thisclass__", T_OBJECT, offsetof(superobject, type), READONLY, + "the class invoking super()"}, + {"__self__", T_OBJECT, offsetof(superobject, obj), READONLY, + "the instance invoking super(); may be None"}, + {"__self_class__", T_OBJECT, offsetof(superobject, obj_type), READONLY, + "the type of the instance invoking super(); may be None"}, + {0} +}; + +static void +super_dealloc(PyObject *self) +{ + superobject *su = (superobject *)self; + + _PyObject_GC_UNTRACK(self); + Py_XDECREF(su->obj); + Py_XDECREF(su->type); + Py_XDECREF(su->obj_type); + self->ob_type->tp_free(self); +} + +static PyObject * +super_repr(PyObject *self) +{ + superobject *su = (superobject *)self; + + if (su->obj_type) + return PyString_FromFormat( + ", <%s object>>", + su->type ? su->type->tp_name : "NULL", + su->obj_type->tp_name); + else + return PyString_FromFormat( + ", NULL>", + su->type ? su->type->tp_name : "NULL"); +} + +static PyObject * +super_getattro(PyObject *self, PyObject *name) +{ + superobject *su = (superobject *)self; + int skip = su->obj_type == NULL; + + if (!skip) { + /* We want __class__ to return the class of the super object + (i.e. super, or a subclass), not the class of su->obj. */ + skip = (PyString_Check(name) && + PyString_GET_SIZE(name) == 9 && + strcmp(PyString_AS_STRING(name), "__class__") == 0); + } + + if (!skip) { + PyObject *mro, *res, *tmp, *dict; + PyTypeObject *starttype; + descrgetfunc f; + int i, n; + + starttype = su->obj_type; + mro = starttype->tp_mro; + + if (mro == NULL) + n = 0; + else { + assert(PyTuple_Check(mro)); + n = PyTuple_GET_SIZE(mro); + } + for (i = 0; i < n; i++) { + if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i)) + break; + } + i++; + res = NULL; + for (; i < n; i++) { + tmp = PyTuple_GET_ITEM(mro, i); + if (PyType_Check(tmp)) + dict = ((PyTypeObject *)tmp)->tp_dict; + else if (PyClass_Check(tmp)) + dict = ((PyClassObject *)tmp)->cl_dict; + else + continue; + res = PyDict_GetItem(dict, name); + if (res != NULL) { + Py_INCREF(res); + f = res->ob_type->tp_descr_get; + if (f != NULL) { + tmp = f(res, su->obj, + (PyObject *)starttype); + Py_DECREF(res); + res = tmp; + } + return res; + } + } + } + return PyObject_GenericGetAttr(self, name); +} + +static PyTypeObject * +supercheck(PyTypeObject *type, PyObject *obj) +{ + /* Check that a super() call makes sense. Return a type object. + + obj can be a new-style class, or an instance of one: + + - If it is a class, it must be a subclass of 'type'. This case is + used for class methods; the return value is obj. + + - If it is an instance, it must be an instance of 'type'. This is + the normal case; the return value is obj.__class__. + + But... when obj is an instance, we want to allow for the case where + obj->ob_type is not a subclass of type, but obj.__class__ is! + This will allow using super() with a proxy for obj. + */ + + /* Check for first bullet above (special case) */ + if (PyType_Check(obj) && PyType_IsSubtype((PyTypeObject *)obj, type)) { + Py_INCREF(obj); + return (PyTypeObject *)obj; + } + + /* Normal case */ + if (PyType_IsSubtype(obj->ob_type, type)) { + Py_INCREF(obj->ob_type); + return obj->ob_type; + } + else { + /* Try the slow way */ + static PyObject *class_str = NULL; + PyObject *class_attr; + + if (class_str == NULL) { + class_str = PyString_FromString("__class__"); + if (class_str == NULL) + return NULL; + } + + class_attr = PyObject_GetAttr(obj, class_str); + + if (class_attr != NULL && + PyType_Check(class_attr) && + (PyTypeObject *)class_attr != obj->ob_type) + { + int ok = PyType_IsSubtype( + (PyTypeObject *)class_attr, type); + if (ok) + return (PyTypeObject *)class_attr; + } + + if (class_attr == NULL) + PyErr_Clear(); + else + Py_DECREF(class_attr); + } + + PyErr_SetString(PyExc_TypeError, + "super(type, obj): " + "obj must be an instance or subtype of type"); + return NULL; +} + +static PyObject * +super_descr_get(PyObject *self, PyObject *obj, PyObject *type) +{ + superobject *su = (superobject *)self; + superobject *new; + + if (obj == NULL || obj == Py_None || su->obj != NULL) { + /* Not binding to an object, or already bound */ + Py_INCREF(self); + return self; + } + if (su->ob_type != &PySuper_Type) + /* If su is not an instance of a subclass of super, + call its type */ + return PyObject_CallFunction((PyObject *)su->ob_type, + "OO", su->type, obj); + else { + /* Inline the common case */ + PyTypeObject *obj_type = supercheck(su->type, obj); + if (obj_type == NULL) + return NULL; + new = (superobject *)PySuper_Type.tp_new(&PySuper_Type, + NULL, NULL); + if (new == NULL) + return NULL; + Py_INCREF(su->type); + Py_INCREF(obj); + new->type = su->type; + new->obj = obj; + new->obj_type = obj_type; + return (PyObject *)new; + } +} + +static int +super_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + superobject *su = (superobject *)self; + PyTypeObject *type; + PyObject *obj = NULL; + PyTypeObject *obj_type = NULL; + + if (!PyArg_ParseTuple(args, "O!|O:super", &PyType_Type, &type, &obj)) + return -1; + if (obj == Py_None) + obj = NULL; + if (obj != NULL) { + obj_type = supercheck(type, obj); + if (obj_type == NULL) + return -1; + Py_INCREF(obj); + } + Py_INCREF(type); + su->type = type; + su->obj = obj; + su->obj_type = obj_type; + return 0; +} + +PyDoc_STRVAR(super_doc, +"super(type) -> unbound super object\n" +"super(type, obj) -> bound super object; requires isinstance(obj, type)\n" +"super(type, type2) -> bound super object; requires issubclass(type2, type)\n" +"Typical use to call a cooperative superclass method:\n" +"class C(B):\n" +" def meth(self, arg):\n" +" super(C, self).meth(arg)"); + +static int +super_traverse(PyObject *self, visitproc visit, void *arg) +{ + superobject *su = (superobject *)self; + int err; + +#define VISIT(SLOT) \ + if (SLOT) { \ + err = visit((PyObject *)(SLOT), arg); \ + if (err) \ + return err; \ + } + + VISIT(su->obj); + VISIT(su->type); + VISIT(su->obj_type); + +#undef VISIT + + return 0; +} + +PyTypeObject PySuper_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "super", /* tp_name */ + sizeof(superobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + super_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + super_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + super_getattro, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + super_doc, /* tp_doc */ + super_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + super_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + super_descr_get, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + super_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodectype.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodectype.c new file mode 100644 index 00000000..ede86130 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodectype.c @@ -0,0 +1,423 @@ +/* + Unicode character type helpers. + + Written by Marc-Andre Lemburg (mal@lemburg.com). + Modified for Python 2.0 by Fredrik Lundh (fredrik@pythonware.com) + + Copyright (c) Corporation for National Research Initiatives. + +*/ + +#include "Python.h" +#include "unicodeobject.h" + +#define ALPHA_MASK 0x01 +#define DECIMAL_MASK 0x02 +#define DIGIT_MASK 0x04 +#define LOWER_MASK 0x08 +#define LINEBREAK_MASK 0x10 +#define SPACE_MASK 0x20 +#define TITLE_MASK 0x40 +#define UPPER_MASK 0x80 + +typedef struct { + const unsigned short flags; + const Py_UNICODE upper; + const Py_UNICODE lower; + const Py_UNICODE title; + const unsigned char decimal; + const unsigned char digit; +} _PyUnicode_TypeRecord; + +#include "unicodetype_db.h" + +static const _PyUnicode_TypeRecord * +gettyperecord(Py_UNICODE code) +{ + int index; + + if (code >= 0x110000) + index = 0; + else { + index = index1[(code>>SHIFT)]; + index = index2[(index<flags & LINEBREAK_MASK) != 0; +} + +/* Returns the titlecase Unicode characters corresponding to ch or just + ch if no titlecase mapping is known. */ + +Py_UNICODE _PyUnicode_ToTitlecase(register Py_UNICODE ch) +{ + const _PyUnicode_TypeRecord *ctype = gettyperecord(ch); + int delta; + + if (ctype->title) + delta = ctype->title; + else + delta = ctype->upper; + + if (delta >= 32768) + delta -= 65536; + + return ch + delta; +} + +/* Returns 1 for Unicode characters having the category 'Lt', 0 + otherwise. */ + +int _PyUnicode_IsTitlecase(Py_UNICODE ch) +{ + const _PyUnicode_TypeRecord *ctype = gettyperecord(ch); + + return (ctype->flags & TITLE_MASK) != 0; +} + +/* Returns the integer decimal (0-9) for Unicode characters having + this property, -1 otherwise. */ + +int _PyUnicode_ToDecimalDigit(Py_UNICODE ch) +{ + const _PyUnicode_TypeRecord *ctype = gettyperecord(ch); + + return (ctype->flags & DECIMAL_MASK) ? ctype->decimal : -1; +} + +int _PyUnicode_IsDecimalDigit(Py_UNICODE ch) +{ + if (_PyUnicode_ToDecimalDigit(ch) < 0) + return 0; + return 1; +} + +/* Returns the integer digit (0-9) for Unicode characters having + this property, -1 otherwise. */ + +int _PyUnicode_ToDigit(Py_UNICODE ch) +{ + const _PyUnicode_TypeRecord *ctype = gettyperecord(ch); + + return (ctype->flags & DIGIT_MASK) ? ctype->digit : -1; +} + +int _PyUnicode_IsDigit(Py_UNICODE ch) +{ + if (_PyUnicode_ToDigit(ch) < 0) + return 0; + return 1; +} + +/* Returns the numeric value as double for Unicode characters having + this property, -1.0 otherwise. */ + +/* TODO: replace with unicodetype_db.h table */ + +double _PyUnicode_ToNumeric(Py_UNICODE ch) +{ + switch (ch) { + case 0x3007: + return (double) 0; + case 0x09F4: + case 0x215F: + case 0x2160: + case 0x2170: + case 0x3021: + case 0x3280: + return (double) 1; + case 0x00BD: + return (double) 1 / 2; + case 0x2153: + return (double) 1 / 3; + case 0x00BC: + return (double) 1 / 4; + case 0x2155: + return (double) 1 / 5; + case 0x2159: + return (double) 1 / 6; + case 0x215B: + return (double) 1 / 8; + case 0x0BF0: + case 0x1372: + case 0x2169: + case 0x2179: + case 0x2469: + case 0x247D: + case 0x2491: + case 0x277F: + case 0x2789: + case 0x2793: + case 0x3038: + case 0x3289: + return (double) 10; + case 0x0BF1: + case 0x137B: + case 0x216D: + case 0x217D: + return (double) 100; + case 0x0BF2: + case 0x216F: + case 0x217F: + case 0x2180: + return (double) 1000; + case 0x137C: + case 0x2182: + return (double) 10000; + case 0x216A: + case 0x217A: + case 0x246A: + case 0x247E: + case 0x2492: + return (double) 11; + case 0x216B: + case 0x217B: + case 0x246B: + case 0x247F: + case 0x2493: + return (double) 12; + case 0x246C: + case 0x2480: + case 0x2494: + return (double) 13; + case 0x246D: + case 0x2481: + case 0x2495: + return (double) 14; + case 0x246E: + case 0x2482: + case 0x2496: + return (double) 15; + case 0x09F9: + case 0x246F: + case 0x2483: + case 0x2497: + return (double) 16; + case 0x16EE: + case 0x2470: + case 0x2484: + case 0x2498: + return (double) 17; + case 0x16EF: + case 0x2471: + case 0x2485: + case 0x2499: + return (double) 18; + case 0x16F0: + case 0x2472: + case 0x2486: + case 0x249A: + return (double) 19; + case 0x09F5: + case 0x2161: + case 0x2171: + case 0x3022: + case 0x3281: + return (double) 2; + case 0x2154: + return (double) 2 / 3; + case 0x2156: + return (double) 2 / 5; + case 0x1373: + case 0x2473: + case 0x2487: + case 0x249B: + case 0x3039: + return (double) 20; + case 0x09F6: + case 0x2162: + case 0x2172: + case 0x3023: + case 0x3282: + return (double) 3; + case 0x00BE: + return (double) 3 / 4; + case 0x2157: + return (double) 3 / 5; + case 0x215C: + return (double) 3 / 8; + case 0x1374: + case 0x303A: + return (double) 30; + case 0x09F7: + case 0x2163: + case 0x2173: + case 0x3024: + case 0x3283: + return (double) 4; + case 0x2158: + return (double) 4 / 5; + case 0x1375: + return (double) 40; + case 0x2164: + case 0x2174: + case 0x3025: + case 0x3284: + return (double) 5; + case 0x215A: + return (double) 5 / 6; + case 0x215D: + return (double) 5 / 8; + case 0x1376: + case 0x216C: + case 0x217C: + return (double) 50; + case 0x216E: + case 0x217E: + return (double) 500; + case 0x2181: + return (double) 5000; + case 0x2165: + case 0x2175: + case 0x3026: + case 0x3285: + return (double) 6; + case 0x1377: + return (double) 60; + case 0x2166: + case 0x2176: + case 0x3027: + case 0x3286: + return (double) 7; + case 0x215E: + return (double) 7 / 8; + case 0x1378: + return (double) 70; + case 0x2167: + case 0x2177: + case 0x3028: + case 0x3287: + return (double) 8; + case 0x1379: + return (double) 80; + case 0x2168: + case 0x2178: + case 0x3029: + case 0x3288: + return (double) 9; + case 0x137A: + return (double) 90; + default: + return (double) _PyUnicode_ToDigit(ch); + } +} + +int _PyUnicode_IsNumeric(Py_UNICODE ch) +{ + if (_PyUnicode_ToNumeric(ch) < 0.0) + return 0; + return 1; +} + +#ifndef WANT_WCTYPE_FUNCTIONS + +/* Returns 1 for Unicode characters having the bidirectional type + 'WS', 'B' or 'S' or the category 'Zs', 0 otherwise. */ + +int _PyUnicode_IsWhitespace(Py_UNICODE ch) +{ + const _PyUnicode_TypeRecord *ctype = gettyperecord(ch); + + return (ctype->flags & SPACE_MASK) != 0; +} + +/* Returns 1 for Unicode characters having the category 'Ll', 0 + otherwise. */ + +int _PyUnicode_IsLowercase(Py_UNICODE ch) +{ + const _PyUnicode_TypeRecord *ctype = gettyperecord(ch); + + return (ctype->flags & LOWER_MASK) != 0; +} + +/* Returns 1 for Unicode characters having the category 'Lu', 0 + otherwise. */ + +int _PyUnicode_IsUppercase(Py_UNICODE ch) +{ + const _PyUnicode_TypeRecord *ctype = gettyperecord(ch); + + return (ctype->flags & UPPER_MASK) != 0; +} + +/* Returns the uppercase Unicode characters corresponding to ch or just + ch if no uppercase mapping is known. */ + +Py_UNICODE _PyUnicode_ToUppercase(Py_UNICODE ch) +{ + const _PyUnicode_TypeRecord *ctype = gettyperecord(ch); + int delta = ctype->upper; + if (delta >= 32768) + delta -= 65536; + return ch + delta; +} + +/* Returns the lowercase Unicode characters corresponding to ch or just + ch if no lowercase mapping is known. */ + +Py_UNICODE _PyUnicode_ToLowercase(Py_UNICODE ch) +{ + const _PyUnicode_TypeRecord *ctype = gettyperecord(ch); + int delta = ctype->lower; + if (delta >= 32768) + delta -= 65536; + return ch + delta; +} + +/* Returns 1 for Unicode characters having the category 'Ll', 'Lu', 'Lt', + 'Lo' or 'Lm', 0 otherwise. */ + +int _PyUnicode_IsAlpha(Py_UNICODE ch) +{ + const _PyUnicode_TypeRecord *ctype = gettyperecord(ch); + + return (ctype->flags & ALPHA_MASK) != 0; +} + +#else + +/* Export the interfaces using the wchar_t type for portability + reasons: */ + +int _PyUnicode_IsWhitespace(Py_UNICODE ch) +{ + return iswspace(ch); +} + +int _PyUnicode_IsLowercase(Py_UNICODE ch) +{ + return iswlower(ch); +} + +int _PyUnicode_IsUppercase(Py_UNICODE ch) +{ + return iswupper(ch); +} + +Py_UNICODE _PyUnicode_ToLowercase(Py_UNICODE ch) +{ + return towlower(ch); +} + +Py_UNICODE _PyUnicode_ToUppercase(Py_UNICODE ch) +{ + return towupper(ch); +} + +int _PyUnicode_IsAlpha(Py_UNICODE ch) +{ + return iswalpha(ch); +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodeobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodeobject.c new file mode 100644 index 00000000..6a4ee505 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodeobject.c @@ -0,0 +1,6859 @@ +/* + +Unicode implementation based on original code by Fredrik Lundh, +modified by Marc-Andre Lemburg according to the +Unicode Integration Proposal (see file Misc/unicode.txt). + +Copyright (c) Corporation for National Research Initiatives. + +-------------------------------------------------------------------- +The original string type implementation is: + + Copyright (c) 1999 by Secret Labs AB + Copyright (c) 1999 by Fredrik Lundh + +By obtaining, using, and/or copying this software and/or its +associated documentation, you agree that you have read, understood, +and will comply with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its +associated documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies, and that both that copyright notice and this permission notice +appear in supporting documentation, and that the name of Secret Labs +AB or the author not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR +ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +-------------------------------------------------------------------- + +*/ + +#include "Python.h" + +#include "unicodeobject.h" +#include "ucnhash.h" + +#ifdef MS_WINDOWS +#ifdef MS_XBOX +#include +#else +#include +#endif +#endif + +/* Limit for the Unicode object free list */ + +#define MAX_UNICODE_FREELIST_SIZE 1024 + +/* Limit for the Unicode object free list stay alive optimization. + + The implementation will keep allocated Unicode memory intact for + all objects on the free list having a size less than this + limit. This reduces malloc() overhead for small Unicode objects. + + At worst this will result in MAX_UNICODE_FREELIST_SIZE * + (sizeof(PyUnicodeObject) + KEEPALIVE_SIZE_LIMIT + + malloc()-overhead) bytes of unused garbage. + + Setting the limit to 0 effectively turns the feature off. + + Note: This is an experimental feature ! If you get core dumps when + using Unicode objects, turn this feature off. + +*/ + +#define KEEPALIVE_SIZE_LIMIT 9 + +/* Endianness switches; defaults to little endian */ + +#ifdef WORDS_BIGENDIAN +# define BYTEORDER_IS_BIG_ENDIAN +#else +# define BYTEORDER_IS_LITTLE_ENDIAN +#endif + +/* --- Globals ------------------------------------------------------------ + + The globals are initialized by the _PyUnicode_Init() API and should + not be used before calling that API. + +*/ + +/* Free list for Unicode objects */ +static PyUnicodeObject *unicode_freelist; +static int unicode_freelist_size; + +/* The empty Unicode object is shared to improve performance. */ +static PyUnicodeObject *unicode_empty; + +/* Single character Unicode strings in the Latin-1 range are being + shared as well. */ +static PyUnicodeObject *unicode_latin1[256]; + +/* Default encoding to use and assume when NULL is passed as encoding + parameter; it is initialized by _PyUnicode_Init(). + + Always use the PyUnicode_SetDefaultEncoding() and + PyUnicode_GetDefaultEncoding() APIs to access this global. + +*/ +static char unicode_default_encoding[100]; + +Py_UNICODE +PyUnicode_GetMax(void) +{ +#ifdef Py_UNICODE_WIDE + return 0x10FFFF; +#else + /* This is actually an illegal character, so it should + not be passed to unichr. */ + return 0xFFFF; +#endif +} + +/* --- Unicode Object ----------------------------------------------------- */ + +static +int unicode_resize(register PyUnicodeObject *unicode, + int length) +{ + void *oldstr; + + /* Shortcut if there's nothing much to do. */ + if (unicode->length == length) + goto reset; + + /* Resizing shared object (unicode_empty or single character + objects) in-place is not allowed. Use PyUnicode_Resize() + instead ! */ + if (unicode == unicode_empty || + (unicode->length == 1 && + /* MvL said unicode->str[] may be signed. Python generally assumes + * an int contains at least 32 bits, and we don't use more than + * 32 bits even in a UCS4 build, so casting to unsigned int should + * be correct. + */ + (unsigned int)unicode->str[0] < 256U && + unicode_latin1[unicode->str[0]] == unicode)) { + PyErr_SetString(PyExc_SystemError, + "can't resize shared unicode objects"); + return -1; + } + + /* We allocate one more byte to make sure the string is + Ux0000 terminated -- XXX is this needed ? */ + oldstr = unicode->str; + PyMem_RESIZE(unicode->str, Py_UNICODE, length + 1); + if (!unicode->str) { + unicode->str = oldstr; + PyErr_NoMemory(); + return -1; + } + unicode->str[length] = 0; + unicode->length = length; + + reset: + /* Reset the object caches */ + if (unicode->defenc) { + Py_DECREF(unicode->defenc); + unicode->defenc = NULL; + } + unicode->hash = -1; + + return 0; +} + +/* We allocate one more byte to make sure the string is + Ux0000 terminated -- XXX is this needed ? + + XXX This allocator could further be enhanced by assuring that the + free list never reduces its size below 1. + +*/ + +static +PyUnicodeObject *_PyUnicode_New(int length) +{ + register PyUnicodeObject *unicode; + + /* Optimization for empty strings */ + if (length == 0 && unicode_empty != NULL) { + Py_INCREF(unicode_empty); + return unicode_empty; + } + + /* Unicode freelist & memory allocation */ + if (unicode_freelist) { + unicode = unicode_freelist; + unicode_freelist = *(PyUnicodeObject **)unicode; + unicode_freelist_size--; + if (unicode->str) { + /* Keep-Alive optimization: we only upsize the buffer, + never downsize it. */ + if ((unicode->length < length) && + unicode_resize(unicode, length)) { + PyMem_DEL(unicode->str); + goto onError; + } + } + else { + unicode->str = PyMem_NEW(Py_UNICODE, length + 1); + } + PyObject_INIT(unicode, &PyUnicode_Type); + } + else { + unicode = PyObject_New(PyUnicodeObject, &PyUnicode_Type); + if (unicode == NULL) + return NULL; + unicode->str = PyMem_NEW(Py_UNICODE, length + 1); + } + + if (!unicode->str) { + PyErr_NoMemory(); + goto onError; + } + /* Initialize the first element to guard against cases where + * the caller fails before initializing str -- unicode_resize() + * reads str[0], and the Keep-Alive optimization can keep memory + * allocated for str alive across a call to unicode_dealloc(unicode). + * We don't want unicode_resize to read uninitialized memory in + * that case. + */ + unicode->str[0] = 0; + unicode->str[length] = 0; + unicode->length = length; + unicode->hash = -1; + unicode->defenc = NULL; + return unicode; + + onError: + _Py_ForgetReference((PyObject *)unicode); + PyObject_Del(unicode); + return NULL; +} + +static +void unicode_dealloc(register PyUnicodeObject *unicode) +{ + if (PyUnicode_CheckExact(unicode) && + unicode_freelist_size < MAX_UNICODE_FREELIST_SIZE) { + /* Keep-Alive optimization */ + if (unicode->length >= KEEPALIVE_SIZE_LIMIT) { + PyMem_DEL(unicode->str); + unicode->str = NULL; + unicode->length = 0; + } + if (unicode->defenc) { + Py_DECREF(unicode->defenc); + unicode->defenc = NULL; + } + /* Add to free list */ + *(PyUnicodeObject **)unicode = unicode_freelist; + unicode_freelist = unicode; + unicode_freelist_size++; + } + else { + PyMem_DEL(unicode->str); + Py_XDECREF(unicode->defenc); + unicode->ob_type->tp_free((PyObject *)unicode); + } +} + +int PyUnicode_Resize(PyObject **unicode, + int length) +{ + register PyUnicodeObject *v; + + /* Argument checks */ + if (unicode == NULL) { + PyErr_BadInternalCall(); + return -1; + } + v = (PyUnicodeObject *)*unicode; + if (v == NULL || !PyUnicode_Check(v) || v->ob_refcnt != 1 || length < 0) { + PyErr_BadInternalCall(); + return -1; + } + + /* Resizing unicode_empty and single character objects is not + possible since these are being shared. We simply return a fresh + copy with the same Unicode content. */ + if (v->length != length && + (v == unicode_empty || v->length == 1)) { + PyUnicodeObject *w = _PyUnicode_New(length); + if (w == NULL) + return -1; + Py_UNICODE_COPY(w->str, v->str, + length < v->length ? length : v->length); + Py_DECREF(*unicode); + *unicode = (PyObject *)w; + return 0; + } + + /* Note that we don't have to modify *unicode for unshared Unicode + objects, since we can modify them in-place. */ + return unicode_resize(v, length); +} + +/* Internal API for use in unicodeobject.c only ! */ +#define _PyUnicode_Resize(unicodevar, length) \ + PyUnicode_Resize(((PyObject **)(unicodevar)), length) + +PyObject *PyUnicode_FromUnicode(const Py_UNICODE *u, + int size) +{ + PyUnicodeObject *unicode; + + /* If the Unicode data is known at construction time, we can apply + some optimizations which share commonly used objects. */ + if (u != NULL) { + + /* Optimization for empty strings */ + if (size == 0 && unicode_empty != NULL) { + Py_INCREF(unicode_empty); + return (PyObject *)unicode_empty; + } + + /* Single character Unicode objects in the Latin-1 range are + shared when using this constructor */ + if (size == 1 && *u < 256) { + unicode = unicode_latin1[*u]; + if (!unicode) { + unicode = _PyUnicode_New(1); + if (!unicode) + return NULL; + unicode->str[0] = *u; + unicode_latin1[*u] = unicode; + } + Py_INCREF(unicode); + return (PyObject *)unicode; + } + } + + unicode = _PyUnicode_New(size); + if (!unicode) + return NULL; + + /* Copy the Unicode data into the new object */ + if (u != NULL) + Py_UNICODE_COPY(unicode->str, u, size); + + return (PyObject *)unicode; +} + +#ifdef HAVE_WCHAR_H + +PyObject *PyUnicode_FromWideChar(register const wchar_t *w, + int size) +{ + PyUnicodeObject *unicode; + + if (w == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + + unicode = _PyUnicode_New(size); + if (!unicode) + return NULL; + + /* Copy the wchar_t data into the new object */ +#ifdef HAVE_USABLE_WCHAR_T + memcpy(unicode->str, w, size * sizeof(wchar_t)); +#else + { + register Py_UNICODE *u; + register int i; + u = PyUnicode_AS_UNICODE(unicode); + for (i = size; i >= 0; i--) + *u++ = *w++; + } +#endif + + return (PyObject *)unicode; +} + +int PyUnicode_AsWideChar(PyUnicodeObject *unicode, + register wchar_t *w, + int size) +{ + if (unicode == NULL) { + PyErr_BadInternalCall(); + return -1; + } + if (size > PyUnicode_GET_SIZE(unicode)) + size = PyUnicode_GET_SIZE(unicode); +#ifdef HAVE_USABLE_WCHAR_T + memcpy(w, unicode->str, size * sizeof(wchar_t)); +#else + { + register Py_UNICODE *u; + register int i; + u = PyUnicode_AS_UNICODE(unicode); + for (i = size; i >= 0; i--) + *w++ = *u++; + } +#endif + + return size; +} + +#endif + +PyObject *PyUnicode_FromOrdinal(int ordinal) +{ + Py_UNICODE s[2]; + +#ifdef Py_UNICODE_WIDE + if (ordinal < 0 || ordinal > 0x10ffff) { + PyErr_SetString(PyExc_ValueError, + "unichr() arg not in range(0x110000) " + "(wide Python build)"); + return NULL; + } +#else + if (ordinal < 0 || ordinal > 0xffff) { + PyErr_SetString(PyExc_ValueError, + "unichr() arg not in range(0x10000) " + "(narrow Python build)"); + return NULL; + } +#endif + + if (ordinal <= 0xffff) { + /* UCS-2 character */ + s[0] = (Py_UNICODE) ordinal; + return PyUnicode_FromUnicode(s, 1); + } + else { +#ifndef Py_UNICODE_WIDE + /* UCS-4 character. store as two surrogate characters */ + ordinal -= 0x10000L; + s[0] = 0xD800 + (Py_UNICODE) (ordinal >> 10); + s[1] = 0xDC00 + (Py_UNICODE) (ordinal & 0x03FF); + return PyUnicode_FromUnicode(s, 2); +#else + s[0] = (Py_UNICODE)ordinal; + return PyUnicode_FromUnicode(s, 1); +#endif + } +} + +PyObject *PyUnicode_FromObject(register PyObject *obj) +{ + /* XXX Perhaps we should make this API an alias of + PyObject_Unicode() instead ?! */ + if (PyUnicode_CheckExact(obj)) { + Py_INCREF(obj); + return obj; + } + if (PyUnicode_Check(obj)) { + /* For a Unicode subtype that's not a Unicode object, + return a true Unicode object with the same data. */ + return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(obj), + PyUnicode_GET_SIZE(obj)); + } + return PyUnicode_FromEncodedObject(obj, NULL, "strict"); +} + +PyObject *PyUnicode_FromEncodedObject(register PyObject *obj, + const char *encoding, + const char *errors) +{ + const char *s = NULL; + int len; + PyObject *v; + + if (obj == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + +#if 0 + /* For b/w compatibility we also accept Unicode objects provided + that no encodings is given and then redirect to + PyObject_Unicode() which then applies the additional logic for + Unicode subclasses. + + NOTE: This API should really only be used for object which + represent *encoded* Unicode ! + + */ + if (PyUnicode_Check(obj)) { + if (encoding) { + PyErr_SetString(PyExc_TypeError, + "decoding Unicode is not supported"); + return NULL; + } + return PyObject_Unicode(obj); + } +#else + if (PyUnicode_Check(obj)) { + PyErr_SetString(PyExc_TypeError, + "decoding Unicode is not supported"); + return NULL; + } +#endif + + /* Coerce object */ + if (PyString_Check(obj)) { + s = PyString_AS_STRING(obj); + len = PyString_GET_SIZE(obj); + } + else if (PyObject_AsCharBuffer(obj, &s, &len)) { + /* Overwrite the error message with something more useful in + case of a TypeError. */ + if (PyErr_ExceptionMatches(PyExc_TypeError)) + PyErr_Format(PyExc_TypeError, + "coercing to Unicode: need string or buffer, " + "%.80s found", + obj->ob_type->tp_name); + goto onError; + } + + /* Convert to Unicode */ + if (len == 0) { + Py_INCREF(unicode_empty); + v = (PyObject *)unicode_empty; + } + else + v = PyUnicode_Decode(s, len, encoding, errors); + + return v; + + onError: + return NULL; +} + +PyObject *PyUnicode_Decode(const char *s, + int size, + const char *encoding, + const char *errors) +{ + PyObject *buffer = NULL, *unicode; + + if (encoding == NULL) + encoding = PyUnicode_GetDefaultEncoding(); + + /* Shortcuts for common default encodings */ + if (strcmp(encoding, "utf-8") == 0) + return PyUnicode_DecodeUTF8(s, size, errors); + else if (strcmp(encoding, "latin-1") == 0) + return PyUnicode_DecodeLatin1(s, size, errors); +#if defined(MS_WINDOWS) && defined(HAVE_USABLE_WCHAR_T) + else if (strcmp(encoding, "mbcs") == 0) + return PyUnicode_DecodeMBCS(s, size, errors); +#endif + else if (strcmp(encoding, "ascii") == 0) + return PyUnicode_DecodeASCII(s, size, errors); + + /* Decode via the codec registry */ + buffer = PyBuffer_FromMemory((void *)s, size); + if (buffer == NULL) + goto onError; + unicode = PyCodec_Decode(buffer, encoding, errors); + if (unicode == NULL) + goto onError; + if (!PyUnicode_Check(unicode)) { + PyErr_Format(PyExc_TypeError, + "decoder did not return an unicode object (type=%.400s)", + unicode->ob_type->tp_name); + Py_DECREF(unicode); + goto onError; + } + Py_DECREF(buffer); + return unicode; + + onError: + Py_XDECREF(buffer); + return NULL; +} + +PyObject *PyUnicode_Encode(const Py_UNICODE *s, + int size, + const char *encoding, + const char *errors) +{ + PyObject *v, *unicode; + + unicode = PyUnicode_FromUnicode(s, size); + if (unicode == NULL) + return NULL; + v = PyUnicode_AsEncodedString(unicode, encoding, errors); + Py_DECREF(unicode); + return v; +} + +PyObject *PyUnicode_AsEncodedString(PyObject *unicode, + const char *encoding, + const char *errors) +{ + PyObject *v; + + if (!PyUnicode_Check(unicode)) { + PyErr_BadArgument(); + goto onError; + } + + if (encoding == NULL) + encoding = PyUnicode_GetDefaultEncoding(); + + /* Shortcuts for common default encodings */ + if (errors == NULL) { + if (strcmp(encoding, "utf-8") == 0) + return PyUnicode_AsUTF8String(unicode); + else if (strcmp(encoding, "latin-1") == 0) + return PyUnicode_AsLatin1String(unicode); +#if defined(MS_WINDOWS) && defined(HAVE_USABLE_WCHAR_T) + else if (strcmp(encoding, "mbcs") == 0) + return PyUnicode_AsMBCSString(unicode); +#endif + else if (strcmp(encoding, "ascii") == 0) + return PyUnicode_AsASCIIString(unicode); + } + + /* Encode via the codec registry */ + v = PyCodec_Encode(unicode, encoding, errors); + if (v == NULL) + goto onError; + /* XXX Should we really enforce this ? */ + if (!PyString_Check(v)) { + PyErr_Format(PyExc_TypeError, + "encoder did not return a string object (type=%.400s)", + v->ob_type->tp_name); + Py_DECREF(v); + goto onError; + } + return v; + + onError: + return NULL; +} + +PyObject *_PyUnicode_AsDefaultEncodedString(PyObject *unicode, + const char *errors) +{ + PyObject *v = ((PyUnicodeObject *)unicode)->defenc; + + if (v) + return v; + v = PyUnicode_AsEncodedString(unicode, NULL, errors); + if (v && errors == NULL) + ((PyUnicodeObject *)unicode)->defenc = v; + return v; +} + +Py_UNICODE *PyUnicode_AsUnicode(PyObject *unicode) +{ + if (!PyUnicode_Check(unicode)) { + PyErr_BadArgument(); + goto onError; + } + return PyUnicode_AS_UNICODE(unicode); + + onError: + return NULL; +} + +int PyUnicode_GetSize(PyObject *unicode) +{ + if (!PyUnicode_Check(unicode)) { + PyErr_BadArgument(); + goto onError; + } + return PyUnicode_GET_SIZE(unicode); + + onError: + return -1; +} + +const char *PyUnicode_GetDefaultEncoding(void) +{ + return unicode_default_encoding; +} + +int PyUnicode_SetDefaultEncoding(const char *encoding) +{ + PyObject *v; + + /* Make sure the encoding is valid. As side effect, this also + loads the encoding into the codec registry cache. */ + v = _PyCodec_Lookup(encoding); + if (v == NULL) + goto onError; + Py_DECREF(v); + strncpy(unicode_default_encoding, + encoding, + sizeof(unicode_default_encoding)); + return 0; + + onError: + return -1; +} + +/* error handling callback helper: + build arguments, call the callback and check the arguments, + if no exception occured, copy the replacement to the output + and adjust various state variables. + return 0 on success, -1 on error +*/ + +static +int unicode_decode_call_errorhandler(const char *errors, PyObject **errorHandler, + const char *encoding, const char *reason, + const char *input, int insize, int *startinpos, int *endinpos, PyObject **exceptionObject, const char **inptr, + PyObject **output, int *outpos, Py_UNICODE **outptr) +{ + static char *argparse = "O!i;decoding error handler must return (unicode, int) tuple"; + + PyObject *restuple = NULL; + PyObject *repunicode = NULL; + int outsize = PyUnicode_GET_SIZE(*output); + int requiredsize; + int newpos; + Py_UNICODE *repptr; + int repsize; + int res = -1; + + if (*errorHandler == NULL) { + *errorHandler = PyCodec_LookupError(errors); + if (*errorHandler == NULL) + goto onError; + } + + if (*exceptionObject == NULL) { + *exceptionObject = PyUnicodeDecodeError_Create( + encoding, input, insize, *startinpos, *endinpos, reason); + if (*exceptionObject == NULL) + goto onError; + } + else { + if (PyUnicodeDecodeError_SetStart(*exceptionObject, *startinpos)) + goto onError; + if (PyUnicodeDecodeError_SetEnd(*exceptionObject, *endinpos)) + goto onError; + if (PyUnicodeDecodeError_SetReason(*exceptionObject, reason)) + goto onError; + } + + restuple = PyObject_CallFunctionObjArgs(*errorHandler, *exceptionObject, NULL); + if (restuple == NULL) + goto onError; + if (!PyTuple_Check(restuple)) { + PyErr_Format(PyExc_TypeError, &argparse[4]); + goto onError; + } + if (!PyArg_ParseTuple(restuple, argparse, &PyUnicode_Type, &repunicode, &newpos)) + goto onError; + if (newpos<0) + newpos = insize+newpos; + if (newpos<0 || newpos>insize) { + PyErr_Format(PyExc_IndexError, "position %d from error handler out of bounds", newpos); + goto onError; + } + + /* need more space? (at least enough for what we + have+the replacement+the rest of the string (starting + at the new input position), so we won't have to check space + when there are no errors in the rest of the string) */ + repptr = PyUnicode_AS_UNICODE(repunicode); + repsize = PyUnicode_GET_SIZE(repunicode); + requiredsize = *outpos + repsize + insize-newpos; + if (requiredsize > outsize) { + if (requiredsize<2*outsize) + requiredsize = 2*outsize; + if (PyUnicode_Resize(output, requiredsize)) + goto onError; + *outptr = PyUnicode_AS_UNICODE(*output) + *outpos; + } + *endinpos = newpos; + *inptr = input + newpos; + Py_UNICODE_COPY(*outptr, repptr, repsize); + *outptr += repsize; + *outpos += repsize; + /* we made it! */ + res = 0; + + onError: + Py_XDECREF(restuple); + return res; +} + +/* --- UTF-7 Codec -------------------------------------------------------- */ + +/* see RFC2152 for details */ + +static +char utf7_special[128] = { + /* indicate whether a UTF-7 character is special i.e. cannot be directly + encoded: + 0 - not special + 1 - special + 2 - whitespace (optional) + 3 - RFC2152 Set O (optional) */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 1, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 3, 3, 3, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 1, 1, + +}; + +#define SPECIAL(c, encodeO, encodeWS) \ + (((c)>127 || utf7_special[(c)] == 1) || \ + (encodeWS && (utf7_special[(c)] == 2)) || \ + (encodeO && (utf7_special[(c)] == 3))) + +#define B64(n) ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(n) & 0x3f]) +#define B64CHAR(c) (isalnum(c) || (c) == '+' || (c) == '/') +#define UB64(c) ((c) == '+' ? 62 : (c) == '/' ? 63 : (c) >= 'a' ? \ + (c) - 71 : (c) >= 'A' ? (c) - 65 : (c) + 4) + +#define ENCODE(out, ch, bits) \ + while (bits >= 6) { \ + *out++ = B64(ch >> (bits-6)); \ + bits -= 6; \ + } + +#define DECODE(out, ch, bits, surrogate) \ + while (bits >= 16) { \ + Py_UNICODE outCh = (Py_UNICODE) ((ch >> (bits-16)) & 0xffff); \ + bits -= 16; \ + if (surrogate) { \ + /* We have already generated an error for the high surrogate + so let's not bother seeing if the low surrogate is correct or not */\ + surrogate = 0; \ + } else if (0xDC00 <= outCh && outCh <= 0xDFFF) { \ + /* This is a surrogate pair. Unfortunately we can't represent \ + it in a 16-bit character */ \ + surrogate = 1; \ + errmsg = "code pairs are not supported"; \ + goto utf7Error; \ + } else { \ + *out++ = outCh; \ + } \ + } \ + +PyObject *PyUnicode_DecodeUTF7(const char *s, + int size, + const char *errors) +{ + const char *starts = s; + int startinpos; + int endinpos; + int outpos; + const char *e; + PyUnicodeObject *unicode; + Py_UNICODE *p; + const char *errmsg = ""; + int inShift = 0; + unsigned int bitsleft = 0; + unsigned long charsleft = 0; + int surrogate = 0; + PyObject *errorHandler = NULL; + PyObject *exc = NULL; + + unicode = _PyUnicode_New(size); + if (!unicode) + return NULL; + if (size == 0) + return (PyObject *)unicode; + + p = unicode->str; + e = s + size; + + while (s < e) { + Py_UNICODE ch; + restart: + ch = *s; + + if (inShift) { + if ((ch == '-') || !B64CHAR(ch)) { + inShift = 0; + s++; + + /* p, charsleft, bitsleft, surrogate = */ DECODE(p, charsleft, bitsleft, surrogate); + if (bitsleft >= 6) { + /* The shift sequence has a partial character in it. If + bitsleft < 6 then we could just classify it as padding + but that is not the case here */ + + errmsg = "partial character in shift sequence"; + goto utf7Error; + } + /* According to RFC2152 the remaining bits should be zero. We + choose to signal an error/insert a replacement character + here so indicate the potential of a misencoded character. */ + + /* On x86, a << b == a << (b%32) so make sure that bitsleft != 0 */ + if (bitsleft && charsleft << (sizeof(charsleft) * 8 - bitsleft)) { + errmsg = "non-zero padding bits in shift sequence"; + goto utf7Error; + } + + if (ch == '-') { + if ((s < e) && (*(s) == '-')) { + *p++ = '-'; + inShift = 1; + } + } else if (SPECIAL(ch,0,0)) { + errmsg = "unexpected special character"; + goto utf7Error; + } else { + *p++ = ch; + } + } else { + charsleft = (charsleft << 6) | UB64(ch); + bitsleft += 6; + s++; + /* p, charsleft, bitsleft, surrogate = */ DECODE(p, charsleft, bitsleft, surrogate); + } + } + else if ( ch == '+' ) { + startinpos = s-starts; + s++; + if (s < e && *s == '-') { + s++; + *p++ = '+'; + } else + { + inShift = 1; + bitsleft = 0; + } + } + else if (SPECIAL(ch,0,0)) { + errmsg = "unexpected special character"; + s++; + goto utf7Error; + } + else { + *p++ = ch; + s++; + } + continue; + utf7Error: + outpos = p-PyUnicode_AS_UNICODE(unicode); + endinpos = s-starts; + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "utf7", errmsg, + starts, size, &startinpos, &endinpos, &exc, &s, + (PyObject **)&unicode, &outpos, &p)) + goto onError; + } + + if (inShift) { + outpos = p-PyUnicode_AS_UNICODE(unicode); + endinpos = size; + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "utf7", "unterminated shift sequence", + starts, size, &startinpos, &endinpos, &exc, &s, + (PyObject **)&unicode, &outpos, &p)) + goto onError; + if (s < e) + goto restart; + } + + if (_PyUnicode_Resize(&unicode, p - PyUnicode_AS_UNICODE(unicode))) + goto onError; + + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + return (PyObject *)unicode; + +onError: + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + Py_DECREF(unicode); + return NULL; +} + + +PyObject *PyUnicode_EncodeUTF7(const Py_UNICODE *s, + int size, + int encodeSetO, + int encodeWhiteSpace, + const char *errors) +{ + PyObject *v; + /* It might be possible to tighten this worst case */ + unsigned int cbAllocated = 5 * size; + int inShift = 0; + int i = 0; + unsigned int bitsleft = 0; + unsigned long charsleft = 0; + char * out; + char * start; + + if (size == 0) + return PyString_FromStringAndSize(NULL, 0); + + v = PyString_FromStringAndSize(NULL, cbAllocated); + if (v == NULL) + return NULL; + + start = out = PyString_AS_STRING(v); + for (;i < size; ++i) { + Py_UNICODE ch = s[i]; + + if (!inShift) { + if (ch == '+') { + *out++ = '+'; + *out++ = '-'; + } else if (SPECIAL(ch, encodeSetO, encodeWhiteSpace)) { + charsleft = ch; + bitsleft = 16; + *out++ = '+'; + /* out, charsleft, bitsleft = */ ENCODE(out, charsleft, bitsleft); + inShift = bitsleft > 0; + } else { + *out++ = (char) ch; + } + } else { + if (!SPECIAL(ch, encodeSetO, encodeWhiteSpace)) { + *out++ = B64(charsleft << (6-bitsleft)); + charsleft = 0; + bitsleft = 0; + /* Characters not in the BASE64 set implicitly unshift the sequence + so no '-' is required, except if the character is itself a '-' */ + if (B64CHAR(ch) || ch == '-') { + *out++ = '-'; + } + inShift = 0; + *out++ = (char) ch; + } else { + bitsleft += 16; + charsleft = (charsleft << 16) | ch; + /* out, charsleft, bitsleft = */ ENCODE(out, charsleft, bitsleft); + + /* If the next character is special then we dont' need to terminate + the shift sequence. If the next character is not a BASE64 character + or '-' then the shift sequence will be terminated implicitly and we + don't have to insert a '-'. */ + + if (bitsleft == 0) { + if (i + 1 < size) { + Py_UNICODE ch2 = s[i+1]; + + if (SPECIAL(ch2, encodeSetO, encodeWhiteSpace)) { + + } else if (B64CHAR(ch2) || ch2 == '-') { + *out++ = '-'; + inShift = 0; + } else { + inShift = 0; + } + + } + else { + *out++ = '-'; + inShift = 0; + } + } + } + } + } + if (bitsleft) { + *out++= B64(charsleft << (6-bitsleft) ); + *out++ = '-'; + } + + _PyString_Resize(&v, out - start); + return v; +} + +#undef SPECIAL +#undef B64 +#undef B64CHAR +#undef UB64 +#undef ENCODE +#undef DECODE + +/* --- UTF-8 Codec -------------------------------------------------------- */ + +static +char utf8_code_length[256] = { + /* Map UTF-8 encoded prefix byte to sequence length. zero means + illegal prefix. see RFC 2279 for details */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0 +}; + +PyObject *PyUnicode_DecodeUTF8(const char *s, + int size, + const char *errors) +{ + const char *starts = s; + int n; + int startinpos; + int endinpos; + int outpos; + const char *e; + PyUnicodeObject *unicode; + Py_UNICODE *p; + const char *errmsg = ""; + PyObject *errorHandler = NULL; + PyObject *exc = NULL; + + /* Note: size will always be longer than the resulting Unicode + character count */ + unicode = _PyUnicode_New(size); + if (!unicode) + return NULL; + if (size == 0) + return (PyObject *)unicode; + + /* Unpack UTF-8 encoded data */ + p = unicode->str; + e = s + size; + + while (s < e) { + Py_UCS4 ch = (unsigned char)*s; + + if (ch < 0x80) { + *p++ = (Py_UNICODE)ch; + s++; + continue; + } + + n = utf8_code_length[ch]; + + if (s + n > e) { + errmsg = "unexpected end of data"; + startinpos = s-starts; + endinpos = size; + goto utf8Error; + } + + switch (n) { + + case 0: + errmsg = "unexpected code byte"; + startinpos = s-starts; + endinpos = startinpos+1; + goto utf8Error; + + case 1: + errmsg = "internal error"; + startinpos = s-starts; + endinpos = startinpos+1; + goto utf8Error; + + case 2: + if ((s[1] & 0xc0) != 0x80) { + errmsg = "invalid data"; + startinpos = s-starts; + endinpos = startinpos+2; + goto utf8Error; + } + ch = ((s[0] & 0x1f) << 6) + (s[1] & 0x3f); + if (ch < 0x80) { + startinpos = s-starts; + endinpos = startinpos+2; + errmsg = "illegal encoding"; + goto utf8Error; + } + else + *p++ = (Py_UNICODE)ch; + break; + + case 3: + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80) { + errmsg = "invalid data"; + startinpos = s-starts; + endinpos = startinpos+3; + goto utf8Error; + } + ch = ((s[0] & 0x0f) << 12) + ((s[1] & 0x3f) << 6) + (s[2] & 0x3f); + if (ch < 0x0800) { + /* Note: UTF-8 encodings of surrogates are considered + legal UTF-8 sequences; + + XXX For wide builds (UCS-4) we should probably try + to recombine the surrogates into a single code + unit. + */ + errmsg = "illegal encoding"; + startinpos = s-starts; + endinpos = startinpos+3; + goto utf8Error; + } + else + *p++ = (Py_UNICODE)ch; + break; + + case 4: + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80) { + errmsg = "invalid data"; + startinpos = s-starts; + endinpos = startinpos+4; + goto utf8Error; + } + ch = ((s[0] & 0x7) << 18) + ((s[1] & 0x3f) << 12) + + ((s[2] & 0x3f) << 6) + (s[3] & 0x3f); + /* validate and convert to UTF-16 */ + if ((ch < 0x10000) /* minimum value allowed for 4 + byte encoding */ + || (ch > 0x10ffff)) /* maximum value allowed for + UTF-16 */ + { + errmsg = "illegal encoding"; + startinpos = s-starts; + endinpos = startinpos+4; + goto utf8Error; + } +#ifdef Py_UNICODE_WIDE + *p++ = (Py_UNICODE)ch; +#else + /* compute and append the two surrogates: */ + + /* translate from 10000..10FFFF to 0..FFFF */ + ch -= 0x10000; + + /* high surrogate = top 10 bits added to D800 */ + *p++ = (Py_UNICODE)(0xD800 + (ch >> 10)); + + /* low surrogate = bottom 10 bits added to DC00 */ + *p++ = (Py_UNICODE)(0xDC00 + (ch & 0x03FF)); +#endif + break; + + default: + /* Other sizes are only needed for UCS-4 */ + errmsg = "unsupported Unicode code range"; + startinpos = s-starts; + endinpos = startinpos+n; + goto utf8Error; + } + s += n; + continue; + + utf8Error: + outpos = p-PyUnicode_AS_UNICODE(unicode); + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "utf8", errmsg, + starts, size, &startinpos, &endinpos, &exc, &s, + (PyObject **)&unicode, &outpos, &p)) + goto onError; + } + + /* Adjust length */ + if (_PyUnicode_Resize(&unicode, p - unicode->str)) + goto onError; + + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + return (PyObject *)unicode; + +onError: + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + Py_DECREF(unicode); + return NULL; +} + +/* Allocation strategy: if the string is short, convert into a stack buffer + and allocate exactly as much space needed at the end. Else allocate the + maximum possible needed (4 result bytes per Unicode character), and return + the excess memory at the end. +*/ +PyObject * +PyUnicode_EncodeUTF8(const Py_UNICODE *s, + int size, + const char *errors) +{ +#define MAX_SHORT_UNICHARS 300 /* largest size we'll do on the stack */ + + int i; /* index into s of next input byte */ + PyObject *v; /* result string object */ + char *p; /* next free byte in output buffer */ + int nallocated; /* number of result bytes allocated */ + int nneeded; /* number of result bytes needed */ + char stackbuf[MAX_SHORT_UNICHARS * 4]; + + assert(s != NULL); + assert(size >= 0); + + if (size <= MAX_SHORT_UNICHARS) { + /* Write into the stack buffer; nallocated can't overflow. + * At the end, we'll allocate exactly as much heap space as it + * turns out we need. + */ + nallocated = Py_SAFE_DOWNCAST(sizeof(stackbuf), size_t, int); + v = NULL; /* will allocate after we're done */ + p = stackbuf; + } + else { + /* Overallocate on the heap, and give the excess back at the end. */ + nallocated = size * 4; + if (nallocated / 4 != size) /* overflow! */ + return PyErr_NoMemory(); + v = PyString_FromStringAndSize(NULL, nallocated); + if (v == NULL) + return NULL; + p = PyString_AS_STRING(v); + } + + for (i = 0; i < size;) { + Py_UCS4 ch = s[i++]; + + if (ch < 0x80) + /* Encode ASCII */ + *p++ = (char) ch; + + else if (ch < 0x0800) { + /* Encode Latin-1 */ + *p++ = (char)(0xc0 | (ch >> 6)); + *p++ = (char)(0x80 | (ch & 0x3f)); + } + else { + /* Encode UCS2 Unicode ordinals */ + if (ch < 0x10000) { + /* Special case: check for high surrogate */ + if (0xD800 <= ch && ch <= 0xDBFF && i != size) { + Py_UCS4 ch2 = s[i]; + /* Check for low surrogate and combine the two to + form a UCS4 value */ + if (0xDC00 <= ch2 && ch2 <= 0xDFFF) { + ch = ((ch - 0xD800) << 10 | (ch2 - 0xDC00)) + 0x10000; + i++; + goto encodeUCS4; + } + /* Fall through: handles isolated high surrogates */ + } + *p++ = (char)(0xe0 | (ch >> 12)); + *p++ = (char)(0x80 | ((ch >> 6) & 0x3f)); + *p++ = (char)(0x80 | (ch & 0x3f)); + continue; + } +encodeUCS4: + /* Encode UCS4 Unicode ordinals */ + *p++ = (char)(0xf0 | (ch >> 18)); + *p++ = (char)(0x80 | ((ch >> 12) & 0x3f)); + *p++ = (char)(0x80 | ((ch >> 6) & 0x3f)); + *p++ = (char)(0x80 | (ch & 0x3f)); + } + } + + if (v == NULL) { + /* This was stack allocated. */ + nneeded = Py_SAFE_DOWNCAST(p - stackbuf, long, int); + assert(nneeded <= nallocated); + v = PyString_FromStringAndSize(stackbuf, nneeded); + } + else { + /* Cut back to size actually needed. */ + nneeded = Py_SAFE_DOWNCAST(p - PyString_AS_STRING(v), long, int); + assert(nneeded <= nallocated); + _PyString_Resize(&v, nneeded); + } + return v; + +#undef MAX_SHORT_UNICHARS +} + +PyObject *PyUnicode_AsUTF8String(PyObject *unicode) +{ + if (!PyUnicode_Check(unicode)) { + PyErr_BadArgument(); + return NULL; + } + return PyUnicode_EncodeUTF8(PyUnicode_AS_UNICODE(unicode), + PyUnicode_GET_SIZE(unicode), + NULL); +} + +/* --- UTF-16 Codec ------------------------------------------------------- */ + +PyObject * +PyUnicode_DecodeUTF16(const char *s, + int size, + const char *errors, + int *byteorder) +{ + const char *starts = s; + int startinpos; + int endinpos; + int outpos; + PyUnicodeObject *unicode; + Py_UNICODE *p; + const unsigned char *q, *e; + int bo = 0; /* assume native ordering by default */ + const char *errmsg = ""; + /* Offsets from q for retrieving byte pairs in the right order. */ +#ifdef BYTEORDER_IS_LITTLE_ENDIAN + int ihi = 1, ilo = 0; +#else + int ihi = 0, ilo = 1; +#endif + PyObject *errorHandler = NULL; + PyObject *exc = NULL; + + /* Note: size will always be longer than the resulting Unicode + character count */ + unicode = _PyUnicode_New(size); + if (!unicode) + return NULL; + if (size == 0) + return (PyObject *)unicode; + + /* Unpack UTF-16 encoded data */ + p = unicode->str; + q = (unsigned char *)s; + e = q + size; + + if (byteorder) + bo = *byteorder; + + /* Check for BOM marks (U+FEFF) in the input and adjust current + byte order setting accordingly. In native mode, the leading BOM + mark is skipped, in all other modes, it is copied to the output + stream as-is (giving a ZWNBSP character). */ + if (bo == 0) { + const Py_UNICODE bom = (q[ihi] << 8) | q[ilo]; +#ifdef BYTEORDER_IS_LITTLE_ENDIAN + if (bom == 0xFEFF) { + q += 2; + bo = -1; + } + else if (bom == 0xFFFE) { + q += 2; + bo = 1; + } +#else + if (bom == 0xFEFF) { + q += 2; + bo = 1; + } + else if (bom == 0xFFFE) { + q += 2; + bo = -1; + } +#endif + } + + if (bo == -1) { + /* force LE */ + ihi = 1; + ilo = 0; + } + else if (bo == 1) { + /* force BE */ + ihi = 0; + ilo = 1; + } + + while (q < e) { + Py_UNICODE ch; + /* remaing bytes at the end? (size should be even) */ + if (e-q<2) { + errmsg = "truncated data"; + startinpos = ((const char *)q)-starts; + endinpos = ((const char *)e)-starts; + goto utf16Error; + /* The remaining input chars are ignored if the callback + chooses to skip the input */ + } + ch = (q[ihi] << 8) | q[ilo]; + + q += 2; + + if (ch < 0xD800 || ch > 0xDFFF) { + *p++ = ch; + continue; + } + + /* UTF-16 code pair: */ + if (q >= e) { + errmsg = "unexpected end of data"; + startinpos = (((const char *)q)-2)-starts; + endinpos = ((const char *)e)-starts; + goto utf16Error; + } + if (0xD800 <= ch && ch <= 0xDBFF) { + Py_UNICODE ch2 = (q[ihi] << 8) | q[ilo]; + q += 2; + if (0xDC00 <= ch2 && ch2 <= 0xDFFF) { +#ifndef Py_UNICODE_WIDE + *p++ = ch; + *p++ = ch2; +#else + *p++ = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000; +#endif + continue; + } + else { + errmsg = "illegal UTF-16 surrogate"; + startinpos = (((const char *)q)-4)-starts; + endinpos = startinpos+2; + goto utf16Error; + } + + } + errmsg = "illegal encoding"; + startinpos = (((const char *)q)-2)-starts; + endinpos = startinpos+2; + /* Fall through to report the error */ + + utf16Error: + outpos = p-PyUnicode_AS_UNICODE(unicode); + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "utf16", errmsg, + starts, size, &startinpos, &endinpos, &exc, (const char **)&q, + (PyObject **)&unicode, &outpos, &p)) + goto onError; + } + + if (byteorder) + *byteorder = bo; + + /* Adjust length */ + if (_PyUnicode_Resize(&unicode, p - unicode->str)) + goto onError; + + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + return (PyObject *)unicode; + +onError: + Py_DECREF(unicode); + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + return NULL; +} + +PyObject * +PyUnicode_EncodeUTF16(const Py_UNICODE *s, + int size, + const char *errors, + int byteorder) +{ + PyObject *v; + unsigned char *p; + int i, pairs; + /* Offsets from p for storing byte pairs in the right order. */ +#ifdef BYTEORDER_IS_LITTLE_ENDIAN + int ihi = 1, ilo = 0; +#else + int ihi = 0, ilo = 1; +#endif + +#define STORECHAR(CH) \ + do { \ + p[ihi] = ((CH) >> 8) & 0xff; \ + p[ilo] = (CH) & 0xff; \ + p += 2; \ + } while(0) + + for (i = pairs = 0; i < size; i++) + if (s[i] >= 0x10000) + pairs++; + v = PyString_FromStringAndSize(NULL, + 2 * (size + pairs + (byteorder == 0))); + if (v == NULL) + return NULL; + + p = (unsigned char *)PyString_AS_STRING(v); + if (byteorder == 0) + STORECHAR(0xFEFF); + if (size == 0) + return v; + + if (byteorder == -1) { + /* force LE */ + ihi = 1; + ilo = 0; + } + else if (byteorder == 1) { + /* force BE */ + ihi = 0; + ilo = 1; + } + + while (size-- > 0) { + Py_UNICODE ch = *s++; + Py_UNICODE ch2 = 0; + if (ch >= 0x10000) { + ch2 = 0xDC00 | ((ch-0x10000) & 0x3FF); + ch = 0xD800 | ((ch-0x10000) >> 10); + } + STORECHAR(ch); + if (ch2) + STORECHAR(ch2); + } + return v; +#undef STORECHAR +} + +PyObject *PyUnicode_AsUTF16String(PyObject *unicode) +{ + if (!PyUnicode_Check(unicode)) { + PyErr_BadArgument(); + return NULL; + } + return PyUnicode_EncodeUTF16(PyUnicode_AS_UNICODE(unicode), + PyUnicode_GET_SIZE(unicode), + NULL, + 0); +} + +/* --- Unicode Escape Codec ----------------------------------------------- */ + +static _PyUnicode_Name_CAPI *ucnhash_CAPI = NULL; + +PyObject *PyUnicode_DecodeUnicodeEscape(const char *s, + int size, + const char *errors) +{ + const char *starts = s; + int startinpos; + int endinpos; + int outpos; + int i; + PyUnicodeObject *v; + Py_UNICODE *p; + const char *end; + char* message; + Py_UCS4 chr = 0xffffffff; /* in case 'getcode' messes up */ + PyObject *errorHandler = NULL; + PyObject *exc = NULL; + + /* Escaped strings will always be longer than the resulting + Unicode string, so we start with size here and then reduce the + length after conversion to the true value. + (but if the error callback returns a long replacement string + we'll have to allocate more space) */ + v = _PyUnicode_New(size); + if (v == NULL) + goto onError; + if (size == 0) + return (PyObject *)v; + + p = PyUnicode_AS_UNICODE(v); + end = s + size; + + while (s < end) { + unsigned char c; + Py_UNICODE x; + int digits; + + /* Non-escape characters are interpreted as Unicode ordinals */ + if (*s != '\\') { + *p++ = (unsigned char) *s++; + continue; + } + + startinpos = s-starts; + /* \ - Escapes */ + s++; + switch (*s++) { + + /* \x escapes */ + case '\n': break; + case '\\': *p++ = '\\'; break; + case '\'': *p++ = '\''; break; + case '\"': *p++ = '\"'; break; + case 'b': *p++ = '\b'; break; + case 'f': *p++ = '\014'; break; /* FF */ + case 't': *p++ = '\t'; break; + case 'n': *p++ = '\n'; break; + case 'r': *p++ = '\r'; break; + case 'v': *p++ = '\013'; break; /* VT */ + case 'a': *p++ = '\007'; break; /* BEL, not classic C */ + + /* \OOO (octal) escapes */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + x = s[-1] - '0'; + if ('0' <= *s && *s <= '7') { + x = (x<<3) + *s++ - '0'; + if ('0' <= *s && *s <= '7') + x = (x<<3) + *s++ - '0'; + } + *p++ = x; + break; + + /* hex escapes */ + /* \xXX */ + case 'x': + digits = 2; + message = "truncated \\xXX escape"; + goto hexescape; + + /* \uXXXX */ + case 'u': + digits = 4; + message = "truncated \\uXXXX escape"; + goto hexescape; + + /* \UXXXXXXXX */ + case 'U': + digits = 8; + message = "truncated \\UXXXXXXXX escape"; + hexescape: + chr = 0; + outpos = p-PyUnicode_AS_UNICODE(v); + if (s+digits>end) { + endinpos = size; + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "unicodeescape", "end of string in escape sequence", + starts, size, &startinpos, &endinpos, &exc, &s, + (PyObject **)&v, &outpos, &p)) + goto onError; + goto nextByte; + } + for (i = 0; i < digits; ++i) { + c = (unsigned char) s[i]; + if (!isxdigit(c)) { + endinpos = (s+i+1)-starts; + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "unicodeescape", message, + starts, size, &startinpos, &endinpos, &exc, &s, + (PyObject **)&v, &outpos, &p)) + goto onError; + goto nextByte; + } + chr = (chr<<4) & ~0xF; + if (c >= '0' && c <= '9') + chr += c - '0'; + else if (c >= 'a' && c <= 'f') + chr += 10 + c - 'a'; + else + chr += 10 + c - 'A'; + } + s += i; + if (chr == 0xffffffff) + /* _decoding_error will have already written into the + target buffer. */ + break; + store: + /* when we get here, chr is a 32-bit unicode character */ + if (chr <= 0xffff) + /* UCS-2 character */ + *p++ = (Py_UNICODE) chr; + else if (chr <= 0x10ffff) { + /* UCS-4 character. Either store directly, or as + surrogate pair. */ +#ifdef Py_UNICODE_WIDE + *p++ = chr; +#else + chr -= 0x10000L; + *p++ = 0xD800 + (Py_UNICODE) (chr >> 10); + *p++ = 0xDC00 + (Py_UNICODE) (chr & 0x03FF); +#endif + } else { + endinpos = s-starts; + outpos = p-PyUnicode_AS_UNICODE(v); + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "unicodeescape", "illegal Unicode character", + starts, size, &startinpos, &endinpos, &exc, &s, + (PyObject **)&v, &outpos, &p)) + goto onError; + } + break; + + /* \N{name} */ + case 'N': + message = "malformed \\N character escape"; + if (ucnhash_CAPI == NULL) { + /* load the unicode data module */ + PyObject *m, *v; + m = PyImport_ImportModule("unicodedata"); + if (m == NULL) + goto ucnhashError; + v = PyObject_GetAttrString(m, "ucnhash_CAPI"); + Py_DECREF(m); + if (v == NULL) + goto ucnhashError; + ucnhash_CAPI = PyCObject_AsVoidPtr(v); + Py_DECREF(v); + if (ucnhash_CAPI == NULL) + goto ucnhashError; + } + if (*s == '{') { + const char *start = s+1; + /* look for the closing brace */ + while (*s != '}' && s < end) + s++; + if (s > start && s < end && *s == '}') { + /* found a name. look it up in the unicode database */ + message = "unknown Unicode character name"; + s++; + if (ucnhash_CAPI->getcode(start, s-start-1, &chr)) + goto store; + } + } + endinpos = s-starts; + outpos = p-PyUnicode_AS_UNICODE(v); + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "unicodeescape", message, + starts, size, &startinpos, &endinpos, &exc, &s, + (PyObject **)&v, &outpos, &p)) + goto onError; + break; + + default: + if (s > end) { + message = "\\ at end of string"; + s--; + endinpos = s-starts; + outpos = p-PyUnicode_AS_UNICODE(v); + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "unicodeescape", message, + starts, size, &startinpos, &endinpos, &exc, &s, + (PyObject **)&v, &outpos, &p)) + goto onError; + } + else { + *p++ = '\\'; + *p++ = (unsigned char)s[-1]; + } + break; + } + nextByte: + ; + } + if (_PyUnicode_Resize(&v, (int)(p - PyUnicode_AS_UNICODE(v)))) + goto onError; + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + return (PyObject *)v; + +ucnhashError: + PyErr_SetString( + PyExc_UnicodeError, + "\\N escapes not supported (can't load unicodedata module)" + ); + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + return NULL; + +onError: + Py_XDECREF(v); + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + return NULL; +} + +/* Return a Unicode-Escape string version of the Unicode object. + + If quotes is true, the string is enclosed in u"" or u'' quotes as + appropriate. + +*/ + +static const Py_UNICODE *findchar(const Py_UNICODE *s, + int size, + Py_UNICODE ch); + +static +PyObject *unicodeescape_string(const Py_UNICODE *s, + int size, + int quotes) +{ + PyObject *repr; + char *p; + + static const char *hexdigit = "0123456789abcdef"; + + repr = PyString_FromStringAndSize(NULL, 2 + 6*size + 1); + if (repr == NULL) + return NULL; + + p = PyString_AS_STRING(repr); + + if (quotes) { + *p++ = 'u'; + *p++ = (findchar(s, size, '\'') && + !findchar(s, size, '"')) ? '"' : '\''; + } + while (size-- > 0) { + Py_UNICODE ch = *s++; + + /* Escape quotes */ + if (quotes && + (ch == (Py_UNICODE) PyString_AS_STRING(repr)[1] || ch == '\\')) { + *p++ = '\\'; + *p++ = (char) ch; + continue; + } + +#ifdef Py_UNICODE_WIDE + /* Map 21-bit characters to '\U00xxxxxx' */ + else if (ch >= 0x10000) { + int offset = p - PyString_AS_STRING(repr); + + /* Resize the string if necessary */ + if (offset + 12 > PyString_GET_SIZE(repr)) { + if (_PyString_Resize(&repr, PyString_GET_SIZE(repr) + 100)) + return NULL; + p = PyString_AS_STRING(repr) + offset; + } + + *p++ = '\\'; + *p++ = 'U'; + *p++ = hexdigit[(ch >> 28) & 0x0000000F]; + *p++ = hexdigit[(ch >> 24) & 0x0000000F]; + *p++ = hexdigit[(ch >> 20) & 0x0000000F]; + *p++ = hexdigit[(ch >> 16) & 0x0000000F]; + *p++ = hexdigit[(ch >> 12) & 0x0000000F]; + *p++ = hexdigit[(ch >> 8) & 0x0000000F]; + *p++ = hexdigit[(ch >> 4) & 0x0000000F]; + *p++ = hexdigit[ch & 0x0000000F]; + continue; + } +#endif + /* Map UTF-16 surrogate pairs to Unicode \UXXXXXXXX escapes */ + else if (ch >= 0xD800 && ch < 0xDC00) { + Py_UNICODE ch2; + Py_UCS4 ucs; + + ch2 = *s++; + size--; + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; + *p++ = '\\'; + *p++ = 'U'; + *p++ = hexdigit[(ucs >> 28) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 24) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 20) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 16) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 12) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 8) & 0x0000000F]; + *p++ = hexdigit[(ucs >> 4) & 0x0000000F]; + *p++ = hexdigit[ucs & 0x0000000F]; + continue; + } + /* Fall through: isolated surrogates are copied as-is */ + s--; + size++; + } + + /* Map 16-bit characters to '\uxxxx' */ + if (ch >= 256) { + *p++ = '\\'; + *p++ = 'u'; + *p++ = hexdigit[(ch >> 12) & 0x000F]; + *p++ = hexdigit[(ch >> 8) & 0x000F]; + *p++ = hexdigit[(ch >> 4) & 0x000F]; + *p++ = hexdigit[ch & 0x000F]; + } + + /* Map special whitespace to '\t', \n', '\r' */ + else if (ch == '\t') { + *p++ = '\\'; + *p++ = 't'; + } + else if (ch == '\n') { + *p++ = '\\'; + *p++ = 'n'; + } + else if (ch == '\r') { + *p++ = '\\'; + *p++ = 'r'; + } + + /* Map non-printable US ASCII to '\xhh' */ + else if (ch < ' ' || ch >= 0x7F) { + *p++ = '\\'; + *p++ = 'x'; + *p++ = hexdigit[(ch >> 4) & 0x000F]; + *p++ = hexdigit[ch & 0x000F]; + } + + /* Copy everything else as-is */ + else + *p++ = (char) ch; + } + if (quotes) + *p++ = PyString_AS_STRING(repr)[1]; + + *p = '\0'; + _PyString_Resize(&repr, p - PyString_AS_STRING(repr)); + return repr; +} + +PyObject *PyUnicode_EncodeUnicodeEscape(const Py_UNICODE *s, + int size) +{ + return unicodeescape_string(s, size, 0); +} + +PyObject *PyUnicode_AsUnicodeEscapeString(PyObject *unicode) +{ + if (!PyUnicode_Check(unicode)) { + PyErr_BadArgument(); + return NULL; + } + return PyUnicode_EncodeUnicodeEscape(PyUnicode_AS_UNICODE(unicode), + PyUnicode_GET_SIZE(unicode)); +} + +/* --- Raw Unicode Escape Codec ------------------------------------------- */ + +PyObject *PyUnicode_DecodeRawUnicodeEscape(const char *s, + int size, + const char *errors) +{ + const char *starts = s; + int startinpos; + int endinpos; + int outpos; + PyUnicodeObject *v; + Py_UNICODE *p; + const char *end; + const char *bs; + PyObject *errorHandler = NULL; + PyObject *exc = NULL; + + /* Escaped strings will always be longer than the resulting + Unicode string, so we start with size here and then reduce the + length after conversion to the true value. (But decoding error + handler might have to resize the string) */ + v = _PyUnicode_New(size); + if (v == NULL) + goto onError; + if (size == 0) + return (PyObject *)v; + p = PyUnicode_AS_UNICODE(v); + end = s + size; + while (s < end) { + unsigned char c; + Py_UCS4 x; + int i; + int count; + + /* Non-escape characters are interpreted as Unicode ordinals */ + if (*s != '\\') { + *p++ = (unsigned char)*s++; + continue; + } + startinpos = s-starts; + + /* \u-escapes are only interpreted iff the number of leading + backslashes if odd */ + bs = s; + for (;s < end;) { + if (*s != '\\') + break; + *p++ = (unsigned char)*s++; + } + if (((s - bs) & 1) == 0 || + s >= end || + (*s != 'u' && *s != 'U')) { + continue; + } + p--; + count = *s=='u' ? 4 : 8; + s++; + + /* \uXXXX with 4 hex digits, \Uxxxxxxxx with 8 */ + outpos = p-PyUnicode_AS_UNICODE(v); + for (x = 0, i = 0; i < count; ++i, ++s) { + c = (unsigned char)*s; + if (!isxdigit(c)) { + endinpos = s-starts; + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "rawunicodeescape", "truncated \\uXXXX", + starts, size, &startinpos, &endinpos, &exc, &s, + (PyObject **)&v, &outpos, &p)) + goto onError; + goto nextByte; + } + x = (x<<4) & ~0xF; + if (c >= '0' && c <= '9') + x += c - '0'; + else if (c >= 'a' && c <= 'f') + x += 10 + c - 'a'; + else + x += 10 + c - 'A'; + } +#ifndef Py_UNICODE_WIDE + if (x > 0x10000) { + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "rawunicodeescape", "\\Uxxxxxxxx out of range", + starts, size, &startinpos, &endinpos, &exc, &s, + (PyObject **)&v, &outpos, &p)) + goto onError; + } +#endif + *p++ = x; + nextByte: + ; + } + if (_PyUnicode_Resize(&v, (int)(p - PyUnicode_AS_UNICODE(v)))) + goto onError; + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + return (PyObject *)v; + + onError: + Py_XDECREF(v); + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + return NULL; +} + +PyObject *PyUnicode_EncodeRawUnicodeEscape(const Py_UNICODE *s, + int size) +{ + PyObject *repr; + char *p; + char *q; + + static const char *hexdigit = "0123456789abcdef"; + +#ifdef Py_UNICODE_WIDE + repr = PyString_FromStringAndSize(NULL, 10 * size); +#else + repr = PyString_FromStringAndSize(NULL, 6 * size); +#endif + if (repr == NULL) + return NULL; + if (size == 0) + return repr; + + p = q = PyString_AS_STRING(repr); + while (size-- > 0) { + Py_UNICODE ch = *s++; +#ifdef Py_UNICODE_WIDE + /* Map 32-bit characters to '\Uxxxxxxxx' */ + if (ch >= 0x10000) { + *p++ = '\\'; + *p++ = 'U'; + *p++ = hexdigit[(ch >> 28) & 0xf]; + *p++ = hexdigit[(ch >> 24) & 0xf]; + *p++ = hexdigit[(ch >> 20) & 0xf]; + *p++ = hexdigit[(ch >> 16) & 0xf]; + *p++ = hexdigit[(ch >> 12) & 0xf]; + *p++ = hexdigit[(ch >> 8) & 0xf]; + *p++ = hexdigit[(ch >> 4) & 0xf]; + *p++ = hexdigit[ch & 15]; + } + else +#endif + /* Map 16-bit characters to '\uxxxx' */ + if (ch >= 256) { + *p++ = '\\'; + *p++ = 'u'; + *p++ = hexdigit[(ch >> 12) & 0xf]; + *p++ = hexdigit[(ch >> 8) & 0xf]; + *p++ = hexdigit[(ch >> 4) & 0xf]; + *p++ = hexdigit[ch & 15]; + } + /* Copy everything else as-is */ + else + *p++ = (char) ch; + } + *p = '\0'; + _PyString_Resize(&repr, p - q); + return repr; +} + +PyObject *PyUnicode_AsRawUnicodeEscapeString(PyObject *unicode) +{ + if (!PyUnicode_Check(unicode)) { + PyErr_BadArgument(); + return NULL; + } + return PyUnicode_EncodeRawUnicodeEscape(PyUnicode_AS_UNICODE(unicode), + PyUnicode_GET_SIZE(unicode)); +} + +/* --- Latin-1 Codec ------------------------------------------------------ */ + +PyObject *PyUnicode_DecodeLatin1(const char *s, + int size, + const char *errors) +{ + PyUnicodeObject *v; + Py_UNICODE *p; + + /* Latin-1 is equivalent to the first 256 ordinals in Unicode. */ + if (size == 1 && *(unsigned char*)s < 256) { + Py_UNICODE r = *(unsigned char*)s; + return PyUnicode_FromUnicode(&r, 1); + } + + v = _PyUnicode_New(size); + if (v == NULL) + goto onError; + if (size == 0) + return (PyObject *)v; + p = PyUnicode_AS_UNICODE(v); + while (size-- > 0) + *p++ = (unsigned char)*s++; + return (PyObject *)v; + + onError: + Py_XDECREF(v); + return NULL; +} + +/* create or adjust a UnicodeEncodeError */ +static void make_encode_exception(PyObject **exceptionObject, + const char *encoding, + const Py_UNICODE *unicode, int size, + int startpos, int endpos, + const char *reason) +{ + if (*exceptionObject == NULL) { + *exceptionObject = PyUnicodeEncodeError_Create( + encoding, unicode, size, startpos, endpos, reason); + } + else { + if (PyUnicodeEncodeError_SetStart(*exceptionObject, startpos)) + goto onError; + if (PyUnicodeEncodeError_SetEnd(*exceptionObject, endpos)) + goto onError; + if (PyUnicodeEncodeError_SetReason(*exceptionObject, reason)) + goto onError; + return; + onError: + Py_DECREF(*exceptionObject); + *exceptionObject = NULL; + } +} + +/* raises a UnicodeEncodeError */ +static void raise_encode_exception(PyObject **exceptionObject, + const char *encoding, + const Py_UNICODE *unicode, int size, + int startpos, int endpos, + const char *reason) +{ + make_encode_exception(exceptionObject, + encoding, unicode, size, startpos, endpos, reason); + if (*exceptionObject != NULL) + PyCodec_StrictErrors(*exceptionObject); +} + +/* error handling callback helper: + build arguments, call the callback and check the arguments, + put the result into newpos and return the replacement string, which + has to be freed by the caller */ +static PyObject *unicode_encode_call_errorhandler(const char *errors, + PyObject **errorHandler, + const char *encoding, const char *reason, + const Py_UNICODE *unicode, int size, PyObject **exceptionObject, + int startpos, int endpos, + int *newpos) +{ + static char *argparse = "O!i;encoding error handler must return (unicode, int) tuple"; + + PyObject *restuple; + PyObject *resunicode; + + if (*errorHandler == NULL) { + *errorHandler = PyCodec_LookupError(errors); + if (*errorHandler == NULL) + return NULL; + } + + make_encode_exception(exceptionObject, + encoding, unicode, size, startpos, endpos, reason); + if (*exceptionObject == NULL) + return NULL; + + restuple = PyObject_CallFunctionObjArgs( + *errorHandler, *exceptionObject, NULL); + if (restuple == NULL) + return NULL; + if (!PyTuple_Check(restuple)) { + PyErr_Format(PyExc_TypeError, &argparse[4]); + Py_DECREF(restuple); + return NULL; + } + if (!PyArg_ParseTuple(restuple, argparse, &PyUnicode_Type, + &resunicode, newpos)) { + Py_DECREF(restuple); + return NULL; + } + if (*newpos<0) + *newpos = size+*newpos; + if (*newpos<0 || *newpos>size) { + PyErr_Format(PyExc_IndexError, "position %d from error handler out of bounds", *newpos); + Py_DECREF(restuple); + return NULL; + } + Py_INCREF(resunicode); + Py_DECREF(restuple); + return resunicode; +} + +static PyObject *unicode_encode_ucs1(const Py_UNICODE *p, + int size, + const char *errors, + int limit) +{ + /* output object */ + PyObject *res; + /* pointers to the beginning and end+1 of input */ + const Py_UNICODE *startp = p; + const Py_UNICODE *endp = p + size; + /* pointer to the beginning of the unencodable characters */ + /* const Py_UNICODE *badp = NULL; */ + /* pointer into the output */ + char *str; + /* current output position */ + int respos = 0; + int ressize; + char *encoding = (limit == 256) ? "latin-1" : "ascii"; + char *reason = (limit == 256) ? "ordinal not in range(256)" : "ordinal not in range(128)"; + PyObject *errorHandler = NULL; + PyObject *exc = NULL; + /* the following variable is used for caching string comparisons + * -1=not initialized, 0=unknown, 1=strict, 2=replace, 3=ignore, 4=xmlcharrefreplace */ + int known_errorHandler = -1; + + /* allocate enough for a simple encoding without + replacements, if we need more, we'll resize */ + res = PyString_FromStringAndSize(NULL, size); + if (res == NULL) + goto onError; + if (size == 0) + return res; + str = PyString_AS_STRING(res); + ressize = size; + + while (p=limit)) + ++collend; + /* cache callback name lookup (if not done yet, i.e. it's the first error) */ + if (known_errorHandler==-1) { + if ((errors==NULL) || (!strcmp(errors, "strict"))) + known_errorHandler = 1; + else if (!strcmp(errors, "replace")) + known_errorHandler = 2; + else if (!strcmp(errors, "ignore")) + known_errorHandler = 3; + else if (!strcmp(errors, "xmlcharrefreplace")) + known_errorHandler = 4; + else + known_errorHandler = 0; + } + switch (known_errorHandler) { + case 1: /* strict */ + raise_encode_exception(&exc, encoding, startp, size, collstart-startp, collend-startp, reason); + goto onError; + case 2: /* replace */ + while (collstart++ ressize) { + if (requiredsize<2*ressize) + requiredsize = 2*ressize; + if (_PyString_Resize(&res, requiredsize)) + goto onError; + str = PyString_AS_STRING(res) + respos; + ressize = requiredsize; + } + /* generate replacement (temporarily (mis)uses p) */ + for (p = collstart; p < collend; ++p) { + str += sprintf(str, "&#%d;", (int)*p); + } + p = collend; + break; + default: + repunicode = unicode_encode_call_errorhandler(errors, &errorHandler, + encoding, reason, startp, size, &exc, + collstart-startp, collend-startp, &newpos); + if (repunicode == NULL) + goto onError; + /* need more space? (at least enough for what we + have+the replacement+the rest of the string, so + we won't have to check space for encodable characters) */ + respos = str-PyString_AS_STRING(res); + repsize = PyUnicode_GET_SIZE(repunicode); + requiredsize = respos+repsize+(endp-collend); + if (requiredsize > ressize) { + if (requiredsize<2*ressize) + requiredsize = 2*ressize; + if (_PyString_Resize(&res, requiredsize)) { + Py_DECREF(repunicode); + goto onError; + } + str = PyString_AS_STRING(res) + respos; + ressize = requiredsize; + } + /* check if there is anything unencodable in the replacement + and copy it to the output */ + for (uni2 = PyUnicode_AS_UNICODE(repunicode);repsize-->0; ++uni2, ++str) { + c = *uni2; + if (c >= limit) { + raise_encode_exception(&exc, encoding, startp, size, + unicodepos, unicodepos+1, reason); + Py_DECREF(repunicode); + goto onError; + } + *str = (char)c; + } + p = startp + newpos; + Py_DECREF(repunicode); + } + } + } + /* Resize if we allocated to much */ + respos = str-PyString_AS_STRING(res); + if (respos 0 && usize==0) + return PyErr_SetFromWindowsErrWithFilename(0, NULL); + + v = _PyUnicode_New(usize); + if (v == NULL) + return NULL; + if (usize == 0) + return (PyObject *)v; + p = PyUnicode_AS_UNICODE(v); + if (0 == MultiByteToWideChar(CP_ACP, 0, s, size, p, usize)) { + Py_DECREF(v); + return PyErr_SetFromWindowsErrWithFilename(0, NULL); + } + + return (PyObject *)v; +} + +PyObject *PyUnicode_EncodeMBCS(const Py_UNICODE *p, + int size, + const char *errors) +{ + PyObject *repr; + char *s; + DWORD mbcssize; + + /* If there are no characters, bail now! */ + if (size==0) + return PyString_FromString(""); + + /* First get the size of the result */ + mbcssize = WideCharToMultiByte(CP_ACP, 0, p, size, NULL, 0, NULL, NULL); + if (mbcssize==0) + return PyErr_SetFromWindowsErrWithFilename(0, NULL); + + repr = PyString_FromStringAndSize(NULL, mbcssize); + if (repr == NULL) + return NULL; + if (mbcssize == 0) + return repr; + + /* Do the conversion */ + s = PyString_AS_STRING(repr); + if (0 == WideCharToMultiByte(CP_ACP, 0, p, size, s, mbcssize, NULL, NULL)) { + Py_DECREF(repr); + return PyErr_SetFromWindowsErrWithFilename(0, NULL); + } + return repr; +} + +PyObject *PyUnicode_AsMBCSString(PyObject *unicode) +{ + if (!PyUnicode_Check(unicode)) { + PyErr_BadArgument(); + return NULL; + } + return PyUnicode_EncodeMBCS(PyUnicode_AS_UNICODE(unicode), + PyUnicode_GET_SIZE(unicode), + NULL); +} + +#endif /* MS_WINDOWS */ + +/* --- Character Mapping Codec -------------------------------------------- */ + +PyObject *PyUnicode_DecodeCharmap(const char *s, + int size, + PyObject *mapping, + const char *errors) +{ + const char *starts = s; + int startinpos; + int endinpos; + int outpos; + const char *e; + PyUnicodeObject *v; + Py_UNICODE *p; + int extrachars = 0; + PyObject *errorHandler = NULL; + PyObject *exc = NULL; + + /* Default to Latin-1 */ + if (mapping == NULL) + return PyUnicode_DecodeLatin1(s, size, errors); + + v = _PyUnicode_New(size); + if (v == NULL) + goto onError; + if (size == 0) + return (PyObject *)v; + p = PyUnicode_AS_UNICODE(v); + e = s + size; + while (s < e) { + unsigned char ch = *s; + PyObject *w, *x; + + /* Get mapping (char ordinal -> integer, Unicode char or None) */ + w = PyInt_FromLong((long)ch); + if (w == NULL) + goto onError; + x = PyObject_GetItem(mapping, w); + Py_DECREF(w); + if (x == NULL) { + if (PyErr_ExceptionMatches(PyExc_LookupError)) { + /* No mapping found means: mapping is undefined. */ + PyErr_Clear(); + x = Py_None; + Py_INCREF(x); + } else + goto onError; + } + + /* Apply mapping */ + if (PyInt_Check(x)) { + long value = PyInt_AS_LONG(x); + if (value < 0 || value > 65535) { + PyErr_SetString(PyExc_TypeError, + "character mapping must be in range(65536)"); + Py_DECREF(x); + goto onError; + } + *p++ = (Py_UNICODE)value; + } + else if (x == Py_None) { + /* undefined mapping */ + outpos = p-PyUnicode_AS_UNICODE(v); + startinpos = s-starts; + endinpos = startinpos+1; + if (unicode_decode_call_errorhandler( + errors, &errorHandler, + "charmap", "character maps to ", + starts, size, &startinpos, &endinpos, &exc, &s, + (PyObject **)&v, &outpos, &p)) { + Py_DECREF(x); + goto onError; + } + continue; + } + else if (PyUnicode_Check(x)) { + int targetsize = PyUnicode_GET_SIZE(x); + + if (targetsize == 1) + /* 1-1 mapping */ + *p++ = *PyUnicode_AS_UNICODE(x); + + else if (targetsize > 1) { + /* 1-n mapping */ + if (targetsize > extrachars) { + /* resize first */ + int oldpos = (int)(p - PyUnicode_AS_UNICODE(v)); + int needed = (targetsize - extrachars) + \ + (targetsize << 2); + extrachars += needed; + if (_PyUnicode_Resize(&v, + PyUnicode_GET_SIZE(v) + needed)) { + Py_DECREF(x); + goto onError; + } + p = PyUnicode_AS_UNICODE(v) + oldpos; + } + Py_UNICODE_COPY(p, + PyUnicode_AS_UNICODE(x), + targetsize); + p += targetsize; + extrachars -= targetsize; + } + /* 1-0 mapping: skip the character */ + } + else { + /* wrong return value */ + PyErr_SetString(PyExc_TypeError, + "character mapping must return integer, None or unicode"); + Py_DECREF(x); + goto onError; + } + Py_DECREF(x); + ++s; + } + if (p - PyUnicode_AS_UNICODE(v) < PyUnicode_GET_SIZE(v)) + if (_PyUnicode_Resize(&v, (int)(p - PyUnicode_AS_UNICODE(v)))) + goto onError; + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + return (PyObject *)v; + + onError: + Py_XDECREF(errorHandler); + Py_XDECREF(exc); + Py_XDECREF(v); + return NULL; +} + +/* Lookup the character ch in the mapping. If the character + can't be found, Py_None is returned (or NULL, if another + error occured). */ +static PyObject *charmapencode_lookup(Py_UNICODE c, PyObject *mapping) +{ + PyObject *w = PyInt_FromLong((long)c); + PyObject *x; + + if (w == NULL) + return NULL; + x = PyObject_GetItem(mapping, w); + Py_DECREF(w); + if (x == NULL) { + if (PyErr_ExceptionMatches(PyExc_LookupError)) { + /* No mapping found means: mapping is undefined. */ + PyErr_Clear(); + x = Py_None; + Py_INCREF(x); + return x; + } else + return NULL; + } + else if (x == Py_None) + return x; + else if (PyInt_Check(x)) { + long value = PyInt_AS_LONG(x); + if (value < 0 || value > 255) { + PyErr_SetString(PyExc_TypeError, + "character mapping must be in range(256)"); + Py_DECREF(x); + return NULL; + } + return x; + } + else if (PyString_Check(x)) + return x; + else { + /* wrong return value */ + PyErr_SetString(PyExc_TypeError, + "character mapping must return integer, None or str"); + Py_DECREF(x); + return NULL; + } +} + +/* lookup the character, put the result in the output string and adjust + various state variables. Reallocate the output string if not enough + space is available. Return a new reference to the object that + was put in the output buffer, or Py_None, if the mapping was undefined + (in which case no character was written) or NULL, if a + reallocation error ocurred. The called must decref the result */ +static +PyObject *charmapencode_output(Py_UNICODE c, PyObject *mapping, + PyObject **outobj, int *outpos) +{ + PyObject *rep = charmapencode_lookup(c, mapping); + + if (rep==NULL) + return NULL; + else if (rep==Py_None) + return rep; + else { + char *outstart = PyString_AS_STRING(*outobj); + int outsize = PyString_GET_SIZE(*outobj); + if (PyInt_Check(rep)) { + int requiredsize = *outpos+1; + if (outsize0; ++uni2) { + x = charmapencode_output(*uni2, mapping, res, respos); + if (x==NULL) { + Py_DECREF(repunicode); + return -1; + } + else if (x==Py_None) { + Py_DECREF(repunicode); + Py_DECREF(x); + raise_encode_exception(exceptionObject, encoding, p, size, collstartpos, collendpos, reason); + return -1; + } + Py_DECREF(x); + } + *inpos = newpos; + Py_DECREF(repunicode); + } + return 0; +} + +PyObject *PyUnicode_EncodeCharmap(const Py_UNICODE *p, + int size, + PyObject *mapping, + const char *errors) +{ + /* output object */ + PyObject *res = NULL; + /* current input position */ + int inpos = 0; + /* current output position */ + int respos = 0; + PyObject *errorHandler = NULL; + PyObject *exc = NULL; + /* the following variable is used for caching string comparisons + * -1=not initialized, 0=unknown, 1=strict, 2=replace, + * 3=ignore, 4=xmlcharrefreplace */ + int known_errorHandler = -1; + + /* Default to Latin-1 */ + if (mapping == NULL) + return PyUnicode_EncodeLatin1(p, size, errors); + + /* allocate enough for a simple encoding without + replacements, if we need more, we'll resize */ + res = PyString_FromStringAndSize(NULL, size); + if (res == NULL) + goto onError; + if (size == 0) + return res; + + while (inpos adjust input position */ + ++inpos; + Py_DECREF(x); + } + + /* Resize if we allocated to much */ + if (respossize) { + PyErr_Format(PyExc_IndexError, "position %d from error handler out of bounds", *newpos); + Py_DECREF(restuple); + return NULL; + } + Py_INCREF(resunicode); + Py_DECREF(restuple); + return resunicode; +} + +/* Lookup the character ch in the mapping and put the result in result, + which must be decrefed by the caller. + Return 0 on success, -1 on error */ +static +int charmaptranslate_lookup(Py_UNICODE c, PyObject *mapping, PyObject **result) +{ + PyObject *w = PyInt_FromLong((long)c); + PyObject *x; + + if (w == NULL) + return -1; + x = PyObject_GetItem(mapping, w); + Py_DECREF(w); + if (x == NULL) { + if (PyErr_ExceptionMatches(PyExc_LookupError)) { + /* No mapping found means: use 1:1 mapping. */ + PyErr_Clear(); + *result = NULL; + return 0; + } else + return -1; + } + else if (x == Py_None) { + *result = x; + return 0; + } + else if (PyInt_Check(x)) { + long value = PyInt_AS_LONG(x); + long max = PyUnicode_GetMax(); + if (value < 0 || value > max) { + PyErr_Format(PyExc_TypeError, + "character mapping must be in range(0x%lx)", max+1); + Py_DECREF(x); + return -1; + } + *result = x; + return 0; + } + else if (PyUnicode_Check(x)) { + *result = x; + return 0; + } + else { + /* wrong return value */ + PyErr_SetString(PyExc_TypeError, + "character mapping must return integer, None or unicode"); + Py_DECREF(x); + return -1; + } +} +/* ensure that *outobj is at least requiredsize characters long, +if not reallocate and adjust various state variables. +Return 0 on success, -1 on error */ +static +int charmaptranslate_makespace(PyObject **outobj, Py_UNICODE **outp, + int requiredsize) +{ + int oldsize = PyUnicode_GET_SIZE(*outobj); + if (requiredsize > oldsize) { + /* remember old output position */ + int outpos = *outp-PyUnicode_AS_UNICODE(*outobj); + /* exponentially overallocate to minimize reallocations */ + if (requiredsize < 2 * oldsize) + requiredsize = 2 * oldsize; + if (_PyUnicode_Resize(outobj, requiredsize)) + return -1; + *outp = PyUnicode_AS_UNICODE(*outobj) + outpos; + } + return 0; +} +/* lookup the character, put the result in the output string and adjust + various state variables. Return a new reference to the object that + was put in the output buffer in *result, or Py_None, if the mapping was + undefined (in which case no character was written). + The called must decref result. + Return 0 on success, -1 on error. */ +static +int charmaptranslate_output(const Py_UNICODE *startinp, const Py_UNICODE *curinp, + int insize, PyObject *mapping, PyObject **outobj, Py_UNICODE **outp, + PyObject **res) +{ + if (charmaptranslate_lookup(*curinp, mapping, res)) + return -1; + if (*res==NULL) { + /* not found => default to 1:1 mapping */ + *(*outp)++ = *curinp; + } + else if (*res==Py_None) + ; + else if (PyInt_Check(*res)) { + /* no overflow check, because we know that the space is enough */ + *(*outp)++ = (Py_UNICODE)PyInt_AS_LONG(*res); + } + else if (PyUnicode_Check(*res)) { + int repsize = PyUnicode_GET_SIZE(*res); + if (repsize==1) { + /* no overflow check, because we know that the space is enough */ + *(*outp)++ = *PyUnicode_AS_UNICODE(*res); + } + else if (repsize!=0) { + /* more than one character */ + int requiredsize = (*outp-PyUnicode_AS_UNICODE(*outobj)) + + (insize - (*curinp-*startinp)) + + repsize - 1; + if (charmaptranslate_makespace(outobj, outp, requiredsize)) + return -1; + memcpy(*outp, PyUnicode_AS_UNICODE(*res), sizeof(Py_UNICODE)*repsize); + *outp += repsize; + } + } + else + return -1; + return 0; +} + +PyObject *PyUnicode_TranslateCharmap(const Py_UNICODE *p, + int size, + PyObject *mapping, + const char *errors) +{ + /* output object */ + PyObject *res = NULL; + /* pointers to the beginning and end+1 of input */ + const Py_UNICODE *startp = p; + const Py_UNICODE *endp = p + size; + /* pointer into the output */ + Py_UNICODE *str; + /* current output position */ + int respos = 0; + char *reason = "character maps to "; + PyObject *errorHandler = NULL; + PyObject *exc = NULL; + /* the following variable is used for caching string comparisons + * -1=not initialized, 0=unknown, 1=strict, 2=replace, + * 3=ignore, 4=xmlcharrefreplace */ + int known_errorHandler = -1; + + if (mapping == NULL) { + PyErr_BadArgument(); + return NULL; + } + + /* allocate enough for a simple 1:1 translation without + replacements, if we need more, we'll resize */ + res = PyUnicode_FromUnicode(NULL, size); + if (res == NULL) + goto onError; + if (size == 0) + return res; + str = PyUnicode_AS_UNICODE(res); + + while (p adjust input pointer */ + ++p; + else { /* untranslatable character */ + PyObject *repunicode = NULL; /* initialize to prevent gcc warning */ + int repsize; + int newpos; + Py_UNICODE *uni2; + /* startpos for collecting untranslatable chars */ + const Py_UNICODE *collstart = p; + const Py_UNICODE *collend = p+1; + const Py_UNICODE *coll; + + /* find all untranslatable characters */ + while (collend < endp) { + if (charmaptranslate_lookup(*collend, mapping, &x)) + goto onError; + Py_XDECREF(x); + if (x!=Py_None) + break; + ++collend; + } + /* cache callback name lookup + * (if not done yet, i.e. it's the first error) */ + if (known_errorHandler==-1) { + if ((errors==NULL) || (!strcmp(errors, "strict"))) + known_errorHandler = 1; + else if (!strcmp(errors, "replace")) + known_errorHandler = 2; + else if (!strcmp(errors, "ignore")) + known_errorHandler = 3; + else if (!strcmp(errors, "xmlcharrefreplace")) + known_errorHandler = 4; + else + known_errorHandler = 0; + } + switch (known_errorHandler) { + case 1: /* strict */ + raise_translate_exception(&exc, startp, size, collstart-startp, collend-startp, reason); + goto onError; + case 2: /* replace */ + /* No need to check for space, this is a 1:1 replacement */ + for (coll = collstart; coll0; ++uni2) + *str++ = *uni2; + p = startp + newpos; + Py_DECREF(repunicode); + } + } + } + /* Resize if we allocated to much */ + respos = str-PyUnicode_AS_UNICODE(res); + if (respos= 0) { + *output++ = '0' + decimal; + ++p; + continue; + } + if (0 < ch && ch < 256) { + *output++ = (char)ch; + ++p; + continue; + } + /* All other characters are considered unencodable */ + collstart = p; + collend = p+1; + while (collend < end) { + if ((0 < *collend && *collend < 256) || + !Py_UNICODE_ISSPACE(*collend) || + Py_UNICODE_TODECIMAL(*collend)) + break; + } + /* cache callback name lookup + * (if not done yet, i.e. it's the first error) */ + if (known_errorHandler==-1) { + if ((errors==NULL) || (!strcmp(errors, "strict"))) + known_errorHandler = 1; + else if (!strcmp(errors, "replace")) + known_errorHandler = 2; + else if (!strcmp(errors, "ignore")) + known_errorHandler = 3; + else if (!strcmp(errors, "xmlcharrefreplace")) + known_errorHandler = 4; + else + known_errorHandler = 0; + } + switch (known_errorHandler) { + case 1: /* strict */ + raise_encode_exception(&exc, encoding, s, length, collstart-s, collend-s, reason); + goto onError; + case 2: /* replace */ + for (p = collstart; p < collend; ++p) + *output++ = '?'; + /* fall through */ + case 3: /* ignore */ + p = collend; + break; + case 4: /* xmlcharrefreplace */ + /* generate replacement (temporarily (mis)uses p) */ + for (p = collstart; p < collend; ++p) + output += sprintf(output, "&#%d;", (int)*p); + p = collend; + break; + default: + repunicode = unicode_encode_call_errorhandler(errors, &errorHandler, + encoding, reason, s, length, &exc, + collstart-s, collend-s, &newpos); + if (repunicode == NULL) + goto onError; + /* generate replacement */ + repsize = PyUnicode_GET_SIZE(repunicode); + for (uni2 = PyUnicode_AS_UNICODE(repunicode); repsize-->0; ++uni2) { + Py_UNICODE ch = *uni2; + if (Py_UNICODE_ISSPACE(ch)) + *output++ = ' '; + else { + decimal = Py_UNICODE_TODECIMAL(ch); + if (decimal >= 0) + *output++ = '0' + decimal; + else if (0 < ch && ch < 256) + *output++ = (char)ch; + else { + Py_DECREF(repunicode); + raise_encode_exception(&exc, encoding, + s, length, collstart-s, collend-s, reason); + goto onError; + } + } + } + p = s + newpos; + Py_DECREF(repunicode); + } + } + /* 0-terminate the output string */ + *output++ = '\0'; + Py_XDECREF(exc); + Py_XDECREF(errorHandler); + return 0; + + onError: + Py_XDECREF(exc); + Py_XDECREF(errorHandler); + return -1; +} + +/* --- Helpers ------------------------------------------------------------ */ + +static +int count(PyUnicodeObject *self, + int start, + int end, + PyUnicodeObject *substring) +{ + int count = 0; + + if (start < 0) + start += self->length; + if (start < 0) + start = 0; + if (end > self->length) + end = self->length; + if (end < 0) + end += self->length; + if (end < 0) + end = 0; + + if (substring->length == 0) + return (end - start + 1); + + end -= substring->length; + + while (start <= end) + if (Py_UNICODE_MATCH(self, start, substring)) { + count++; + start += substring->length; + } else + start++; + + return count; +} + +int PyUnicode_Count(PyObject *str, + PyObject *substr, + int start, + int end) +{ + int result; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return -1; + substr = PyUnicode_FromObject(substr); + if (substr == NULL) { + Py_DECREF(str); + return -1; + } + + result = count((PyUnicodeObject *)str, + start, end, + (PyUnicodeObject *)substr); + + Py_DECREF(str); + Py_DECREF(substr); + return result; +} + +static +int findstring(PyUnicodeObject *self, + PyUnicodeObject *substring, + int start, + int end, + int direction) +{ + if (start < 0) + start += self->length; + if (start < 0) + start = 0; + + if (end > self->length) + end = self->length; + if (end < 0) + end += self->length; + if (end < 0) + end = 0; + + if (substring->length == 0) + return (direction > 0) ? start : end; + + end -= substring->length; + + if (direction < 0) { + for (; end >= start; end--) + if (Py_UNICODE_MATCH(self, end, substring)) + return end; + } else { + for (; start <= end; start++) + if (Py_UNICODE_MATCH(self, start, substring)) + return start; + } + + return -1; +} + +int PyUnicode_Find(PyObject *str, + PyObject *substr, + int start, + int end, + int direction) +{ + int result; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return -2; + substr = PyUnicode_FromObject(substr); + if (substr == NULL) { + Py_DECREF(str); + return -2; + } + + result = findstring((PyUnicodeObject *)str, + (PyUnicodeObject *)substr, + start, end, direction); + Py_DECREF(str); + Py_DECREF(substr); + return result; +} + +static +int tailmatch(PyUnicodeObject *self, + PyUnicodeObject *substring, + int start, + int end, + int direction) +{ + if (start < 0) + start += self->length; + if (start < 0) + start = 0; + + if (substring->length == 0) + return 1; + + if (end > self->length) + end = self->length; + if (end < 0) + end += self->length; + if (end < 0) + end = 0; + + end -= substring->length; + if (end < start) + return 0; + + if (direction > 0) { + if (Py_UNICODE_MATCH(self, end, substring)) + return 1; + } else { + if (Py_UNICODE_MATCH(self, start, substring)) + return 1; + } + + return 0; +} + +int PyUnicode_Tailmatch(PyObject *str, + PyObject *substr, + int start, + int end, + int direction) +{ + int result; + + str = PyUnicode_FromObject(str); + if (str == NULL) + return -1; + substr = PyUnicode_FromObject(substr); + if (substr == NULL) { + Py_DECREF(substr); + return -1; + } + + result = tailmatch((PyUnicodeObject *)str, + (PyUnicodeObject *)substr, + start, end, direction); + Py_DECREF(str); + Py_DECREF(substr); + return result; +} + +static +const Py_UNICODE *findchar(const Py_UNICODE *s, + int size, + Py_UNICODE ch) +{ + /* like wcschr, but doesn't stop at NULL characters */ + + while (size-- > 0) { + if (*s == ch) + return s; + s++; + } + + return NULL; +} + +/* Apply fixfct filter to the Unicode object self and return a + reference to the modified object */ + +static +PyObject *fixup(PyUnicodeObject *self, + int (*fixfct)(PyUnicodeObject *s)) +{ + + PyUnicodeObject *u; + + u = (PyUnicodeObject*) PyUnicode_FromUnicode(NULL, self->length); + if (u == NULL) + return NULL; + + Py_UNICODE_COPY(u->str, self->str, self->length); + + if (!fixfct(u) && PyUnicode_CheckExact(self)) { + /* fixfct should return TRUE if it modified the buffer. If + FALSE, return a reference to the original buffer instead + (to save space, not time) */ + Py_INCREF(self); + Py_DECREF(u); + return (PyObject*) self; + } + return (PyObject*) u; +} + +static +int fixupper(PyUnicodeObject *self) +{ + int len = self->length; + Py_UNICODE *s = self->str; + int status = 0; + + while (len-- > 0) { + register Py_UNICODE ch; + + ch = Py_UNICODE_TOUPPER(*s); + if (ch != *s) { + status = 1; + *s = ch; + } + s++; + } + + return status; +} + +static +int fixlower(PyUnicodeObject *self) +{ + int len = self->length; + Py_UNICODE *s = self->str; + int status = 0; + + while (len-- > 0) { + register Py_UNICODE ch; + + ch = Py_UNICODE_TOLOWER(*s); + if (ch != *s) { + status = 1; + *s = ch; + } + s++; + } + + return status; +} + +static +int fixswapcase(PyUnicodeObject *self) +{ + int len = self->length; + Py_UNICODE *s = self->str; + int status = 0; + + while (len-- > 0) { + if (Py_UNICODE_ISUPPER(*s)) { + *s = Py_UNICODE_TOLOWER(*s); + status = 1; + } else if (Py_UNICODE_ISLOWER(*s)) { + *s = Py_UNICODE_TOUPPER(*s); + status = 1; + } + s++; + } + + return status; +} + +static +int fixcapitalize(PyUnicodeObject *self) +{ + int len = self->length; + Py_UNICODE *s = self->str; + int status = 0; + + if (len == 0) + return 0; + if (Py_UNICODE_ISLOWER(*s)) { + *s = Py_UNICODE_TOUPPER(*s); + status = 1; + } + s++; + while (--len > 0) { + if (Py_UNICODE_ISUPPER(*s)) { + *s = Py_UNICODE_TOLOWER(*s); + status = 1; + } + s++; + } + return status; +} + +static +int fixtitle(PyUnicodeObject *self) +{ + register Py_UNICODE *p = PyUnicode_AS_UNICODE(self); + register Py_UNICODE *e; + int previous_is_cased; + + /* Shortcut for single character strings */ + if (PyUnicode_GET_SIZE(self) == 1) { + Py_UNICODE ch = Py_UNICODE_TOTITLE(*p); + if (*p != ch) { + *p = ch; + return 1; + } + else + return 0; + } + + e = p + PyUnicode_GET_SIZE(self); + previous_is_cased = 0; + for (; p < e; p++) { + register const Py_UNICODE ch = *p; + + if (previous_is_cased) + *p = Py_UNICODE_TOLOWER(ch); + else + *p = Py_UNICODE_TOTITLE(ch); + + if (Py_UNICODE_ISLOWER(ch) || + Py_UNICODE_ISUPPER(ch) || + Py_UNICODE_ISTITLE(ch)) + previous_is_cased = 1; + else + previous_is_cased = 0; + } + return 1; +} + +PyObject *PyUnicode_Join(PyObject *separator, + PyObject *seq) +{ + Py_UNICODE *sep; + int seplen; + PyUnicodeObject *res = NULL; + int reslen = 0; + Py_UNICODE *p; + int sz = 100; + int i; + PyObject *it; + + it = PyObject_GetIter(seq); + if (it == NULL) + return NULL; + + if (separator == NULL) { + Py_UNICODE blank = ' '; + sep = ␣ + seplen = 1; + } + else { + separator = PyUnicode_FromObject(separator); + if (separator == NULL) + goto onError; + sep = PyUnicode_AS_UNICODE(separator); + seplen = PyUnicode_GET_SIZE(separator); + } + + res = _PyUnicode_New(sz); + if (res == NULL) + goto onError; + p = PyUnicode_AS_UNICODE(res); + reslen = 0; + + for (i = 0; ; ++i) { + int itemlen; + PyObject *item = PyIter_Next(it); + if (item == NULL) { + if (PyErr_Occurred()) + goto onError; + break; + } + if (!PyUnicode_Check(item)) { + PyObject *v; + if (!PyString_Check(item)) { + PyErr_Format(PyExc_TypeError, + "sequence item %i: expected string or Unicode," + " %.80s found", + i, item->ob_type->tp_name); + Py_DECREF(item); + goto onError; + } + v = PyUnicode_FromObject(item); + Py_DECREF(item); + item = v; + if (item == NULL) + goto onError; + } + itemlen = PyUnicode_GET_SIZE(item); + while (reslen + itemlen + seplen >= sz) { + if (_PyUnicode_Resize(&res, sz*2)) { + Py_DECREF(item); + goto onError; + } + sz *= 2; + p = PyUnicode_AS_UNICODE(res) + reslen; + } + if (i > 0) { + Py_UNICODE_COPY(p, sep, seplen); + p += seplen; + reslen += seplen; + } + Py_UNICODE_COPY(p, PyUnicode_AS_UNICODE(item), itemlen); + p += itemlen; + reslen += itemlen; + Py_DECREF(item); + } + if (_PyUnicode_Resize(&res, reslen)) + goto onError; + + Py_XDECREF(separator); + Py_DECREF(it); + return (PyObject *)res; + + onError: + Py_XDECREF(separator); + Py_XDECREF(res); + Py_DECREF(it); + return NULL; +} + +static +PyUnicodeObject *pad(PyUnicodeObject *self, + int left, + int right, + Py_UNICODE fill) +{ + PyUnicodeObject *u; + + if (left < 0) + left = 0; + if (right < 0) + right = 0; + + if (left == 0 && right == 0 && PyUnicode_CheckExact(self)) { + Py_INCREF(self); + return self; + } + + u = _PyUnicode_New(left + self->length + right); + if (u) { + if (left) + Py_UNICODE_FILL(u->str, fill, left); + Py_UNICODE_COPY(u->str + left, self->str, self->length); + if (right) + Py_UNICODE_FILL(u->str + left + self->length, fill, right); + } + + return u; +} + +#define SPLIT_APPEND(data, left, right) \ + str = PyUnicode_FromUnicode(data + left, right - left); \ + if (!str) \ + goto onError; \ + if (PyList_Append(list, str)) { \ + Py_DECREF(str); \ + goto onError; \ + } \ + else \ + Py_DECREF(str); + +static +PyObject *split_whitespace(PyUnicodeObject *self, + PyObject *list, + int maxcount) +{ + register int i; + register int j; + int len = self->length; + PyObject *str; + + for (i = j = 0; i < len; ) { + /* find a token */ + while (i < len && Py_UNICODE_ISSPACE(self->str[i])) + i++; + j = i; + while (i < len && !Py_UNICODE_ISSPACE(self->str[i])) + i++; + if (j < i) { + if (maxcount-- <= 0) + break; + SPLIT_APPEND(self->str, j, i); + while (i < len && Py_UNICODE_ISSPACE(self->str[i])) + i++; + j = i; + } + } + if (j < len) { + SPLIT_APPEND(self->str, j, len); + } + return list; + + onError: + Py_DECREF(list); + return NULL; +} + +PyObject *PyUnicode_Splitlines(PyObject *string, + int keepends) +{ + register int i; + register int j; + int len; + PyObject *list; + PyObject *str; + Py_UNICODE *data; + + string = PyUnicode_FromObject(string); + if (string == NULL) + return NULL; + data = PyUnicode_AS_UNICODE(string); + len = PyUnicode_GET_SIZE(string); + + list = PyList_New(0); + if (!list) + goto onError; + + for (i = j = 0; i < len; ) { + int eol; + + /* Find a line and append it */ + while (i < len && !Py_UNICODE_ISLINEBREAK(data[i])) + i++; + + /* Skip the line break reading CRLF as one line break */ + eol = i; + if (i < len) { + if (data[i] == '\r' && i + 1 < len && + data[i+1] == '\n') + i += 2; + else + i++; + if (keepends) + eol = i; + } + SPLIT_APPEND(data, j, eol); + j = i; + } + if (j < len) { + SPLIT_APPEND(data, j, len); + } + + Py_DECREF(string); + return list; + + onError: + Py_DECREF(list); + Py_DECREF(string); + return NULL; +} + +static +PyObject *split_char(PyUnicodeObject *self, + PyObject *list, + Py_UNICODE ch, + int maxcount) +{ + register int i; + register int j; + int len = self->length; + PyObject *str; + + for (i = j = 0; i < len; ) { + if (self->str[i] == ch) { + if (maxcount-- <= 0) + break; + SPLIT_APPEND(self->str, j, i); + i = j = i + 1; + } else + i++; + } + if (j <= len) { + SPLIT_APPEND(self->str, j, len); + } + return list; + + onError: + Py_DECREF(list); + return NULL; +} + +static +PyObject *split_substring(PyUnicodeObject *self, + PyObject *list, + PyUnicodeObject *substring, + int maxcount) +{ + register int i; + register int j; + int len = self->length; + int sublen = substring->length; + PyObject *str; + + for (i = j = 0; i <= len - sublen; ) { + if (Py_UNICODE_MATCH(self, i, substring)) { + if (maxcount-- <= 0) + break; + SPLIT_APPEND(self->str, j, i); + i = j = i + sublen; + } else + i++; + } + if (j <= len) { + SPLIT_APPEND(self->str, j, len); + } + return list; + + onError: + Py_DECREF(list); + return NULL; +} + +#undef SPLIT_APPEND + +static +PyObject *split(PyUnicodeObject *self, + PyUnicodeObject *substring, + int maxcount) +{ + PyObject *list; + + if (maxcount < 0) + maxcount = INT_MAX; + + list = PyList_New(0); + if (!list) + return NULL; + + if (substring == NULL) + return split_whitespace(self,list,maxcount); + + else if (substring->length == 1) + return split_char(self,list,substring->str[0],maxcount); + + else if (substring->length == 0) { + Py_DECREF(list); + PyErr_SetString(PyExc_ValueError, "empty separator"); + return NULL; + } + else + return split_substring(self,list,substring,maxcount); +} + +static +PyObject *replace(PyUnicodeObject *self, + PyUnicodeObject *str1, + PyUnicodeObject *str2, + int maxcount) +{ + PyUnicodeObject *u; + + if (maxcount < 0) + maxcount = INT_MAX; + + if (str1->length == 1 && str2->length == 1) { + int i; + + /* replace characters */ + if (!findchar(self->str, self->length, str1->str[0]) && + PyUnicode_CheckExact(self)) { + /* nothing to replace, return original string */ + Py_INCREF(self); + u = self; + } else { + Py_UNICODE u1 = str1->str[0]; + Py_UNICODE u2 = str2->str[0]; + + u = (PyUnicodeObject*) PyUnicode_FromUnicode( + NULL, + self->length + ); + if (u != NULL) { + Py_UNICODE_COPY(u->str, self->str, + self->length); + for (i = 0; i < u->length; i++) + if (u->str[i] == u1) { + if (--maxcount < 0) + break; + u->str[i] = u2; + } + } + } + + } else { + int n, i; + Py_UNICODE *p; + + /* replace strings */ + n = count(self, 0, self->length, str1); + if (n > maxcount) + n = maxcount; + if (n == 0) { + /* nothing to replace, return original string */ + if (PyUnicode_CheckExact(self)) { + Py_INCREF(self); + u = self; + } + else { + u = (PyUnicodeObject *) + PyUnicode_FromUnicode(self->str, self->length); + } + } else { + u = _PyUnicode_New( + self->length + n * (str2->length - str1->length)); + if (u) { + i = 0; + p = u->str; + if (str1->length > 0) { + while (i <= self->length - str1->length) + if (Py_UNICODE_MATCH(self, i, str1)) { + /* replace string segment */ + Py_UNICODE_COPY(p, str2->str, str2->length); + p += str2->length; + i += str1->length; + if (--n <= 0) { + /* copy remaining part */ + Py_UNICODE_COPY(p, self->str+i, self->length-i); + break; + } + } else + *p++ = self->str[i++]; + } else { + while (n > 0) { + Py_UNICODE_COPY(p, str2->str, str2->length); + p += str2->length; + if (--n <= 0) + break; + *p++ = self->str[i++]; + } + Py_UNICODE_COPY(p, self->str+i, self->length-i); + } + } + } + } + + return (PyObject *) u; +} + +/* --- Unicode Object Methods --------------------------------------------- */ + +PyDoc_STRVAR(title__doc__, +"S.title() -> unicode\n\ +\n\ +Return a titlecased version of S, i.e. words start with title case\n\ +characters, all remaining cased characters have lower case."); + +static PyObject* +unicode_title(PyUnicodeObject *self) +{ + return fixup(self, fixtitle); +} + +PyDoc_STRVAR(capitalize__doc__, +"S.capitalize() -> unicode\n\ +\n\ +Return a capitalized version of S, i.e. make the first character\n\ +have upper case."); + +static PyObject* +unicode_capitalize(PyUnicodeObject *self) +{ + return fixup(self, fixcapitalize); +} + +#if 0 +PyDoc_STRVAR(capwords__doc__, +"S.capwords() -> unicode\n\ +\n\ +Apply .capitalize() to all words in S and return the result with\n\ +normalized whitespace (all whitespace strings are replaced by ' ')."); + +static PyObject* +unicode_capwords(PyUnicodeObject *self) +{ + PyObject *list; + PyObject *item; + int i; + + /* Split into words */ + list = split(self, NULL, -1); + if (!list) + return NULL; + + /* Capitalize each word */ + for (i = 0; i < PyList_GET_SIZE(list); i++) { + item = fixup((PyUnicodeObject *)PyList_GET_ITEM(list, i), + fixcapitalize); + if (item == NULL) + goto onError; + Py_DECREF(PyList_GET_ITEM(list, i)); + PyList_SET_ITEM(list, i, item); + } + + /* Join the words to form a new string */ + item = PyUnicode_Join(NULL, list); + +onError: + Py_DECREF(list); + return (PyObject *)item; +} +#endif + +PyDoc_STRVAR(center__doc__, +"S.center(width) -> unicode\n\ +\n\ +Return S centered in a Unicode string of length width. Padding is done\n\ +using spaces."); + +static PyObject * +unicode_center(PyUnicodeObject *self, PyObject *args) +{ + int marg, left; + int width; + + if (!PyArg_ParseTuple(args, "i:center", &width)) + return NULL; + + if (self->length >= width && PyUnicode_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*) self; + } + + marg = width - self->length; + left = marg / 2 + (marg & width & 1); + + return (PyObject*) pad(self, left, marg - left, ' '); +} + +#if 0 + +/* This code should go into some future Unicode collation support + module. The basic comparison should compare ordinals on a naive + basis (this is what Java does and thus JPython too). */ + +/* speedy UTF-16 code point order comparison */ +/* gleaned from: */ +/* http://www-4.ibm.com/software/developer/library/utf16.html?dwzone=unicode */ + +static short utf16Fixup[32] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0x2000, -0x800, -0x800, -0x800, -0x800 +}; + +static int +unicode_compare(PyUnicodeObject *str1, PyUnicodeObject *str2) +{ + int len1, len2; + + Py_UNICODE *s1 = str1->str; + Py_UNICODE *s2 = str2->str; + + len1 = str1->length; + len2 = str2->length; + + while (len1 > 0 && len2 > 0) { + Py_UNICODE c1, c2; + + c1 = *s1++; + c2 = *s2++; + + if (c1 > (1<<11) * 26) + c1 += utf16Fixup[c1>>11]; + if (c2 > (1<<11) * 26) + c2 += utf16Fixup[c2>>11]; + /* now c1 and c2 are in UTF-32-compatible order */ + + if (c1 != c2) + return (c1 < c2) ? -1 : 1; + + len1--; len2--; + } + + return (len1 < len2) ? -1 : (len1 != len2); +} + +#else + +static int +unicode_compare(PyUnicodeObject *str1, PyUnicodeObject *str2) +{ + register int len1, len2; + + Py_UNICODE *s1 = str1->str; + Py_UNICODE *s2 = str2->str; + + len1 = str1->length; + len2 = str2->length; + + while (len1 > 0 && len2 > 0) { + Py_UNICODE c1, c2; + + c1 = *s1++; + c2 = *s2++; + + if (c1 != c2) + return (c1 < c2) ? -1 : 1; + + len1--; len2--; + } + + return (len1 < len2) ? -1 : (len1 != len2); +} + +#endif + +int PyUnicode_Compare(PyObject *left, + PyObject *right) +{ + PyUnicodeObject *u = NULL, *v = NULL; + int result; + + /* Coerce the two arguments */ + u = (PyUnicodeObject *)PyUnicode_FromObject(left); + if (u == NULL) + goto onError; + v = (PyUnicodeObject *)PyUnicode_FromObject(right); + if (v == NULL) + goto onError; + + /* Shortcut for empty or interned objects */ + if (v == u) { + Py_DECREF(u); + Py_DECREF(v); + return 0; + } + + result = unicode_compare(u, v); + + Py_DECREF(u); + Py_DECREF(v); + return result; + +onError: + Py_XDECREF(u); + Py_XDECREF(v); + return -1; +} + +int PyUnicode_Contains(PyObject *container, + PyObject *element) +{ + PyUnicodeObject *u = NULL, *v = NULL; + int result, size; + register const Py_UNICODE *lhs, *end, *rhs; + + /* Coerce the two arguments */ + v = (PyUnicodeObject *)PyUnicode_FromObject(element); + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, + "'in ' requires string as left operand"); + goto onError; + } + u = (PyUnicodeObject *)PyUnicode_FromObject(container); + if (u == NULL) + goto onError; + + size = PyUnicode_GET_SIZE(v); + rhs = PyUnicode_AS_UNICODE(v); + lhs = PyUnicode_AS_UNICODE(u); + + result = 0; + if (size == 1) { + end = lhs + PyUnicode_GET_SIZE(u); + while (lhs < end) { + if (*lhs++ == *rhs) { + result = 1; + break; + } + } + } + else { + end = lhs + (PyUnicode_GET_SIZE(u) - size); + while (lhs <= end) { + if (memcmp(lhs++, rhs, size * sizeof(Py_UNICODE)) == 0) { + result = 1; + break; + } + } + } + + Py_DECREF(u); + Py_DECREF(v); + return result; + +onError: + Py_XDECREF(u); + Py_XDECREF(v); + return -1; +} + +/* Concat to string or Unicode object giving a new Unicode object. */ + +PyObject *PyUnicode_Concat(PyObject *left, + PyObject *right) +{ + PyUnicodeObject *u = NULL, *v = NULL, *w; + + /* Coerce the two arguments */ + u = (PyUnicodeObject *)PyUnicode_FromObject(left); + if (u == NULL) + goto onError; + v = (PyUnicodeObject *)PyUnicode_FromObject(right); + if (v == NULL) + goto onError; + + /* Shortcuts */ + if (v == unicode_empty) { + Py_DECREF(v); + return (PyObject *)u; + } + if (u == unicode_empty) { + Py_DECREF(u); + return (PyObject *)v; + } + + /* Concat the two Unicode strings */ + w = _PyUnicode_New(u->length + v->length); + if (w == NULL) + goto onError; + Py_UNICODE_COPY(w->str, u->str, u->length); + Py_UNICODE_COPY(w->str + u->length, v->str, v->length); + + Py_DECREF(u); + Py_DECREF(v); + return (PyObject *)w; + +onError: + Py_XDECREF(u); + Py_XDECREF(v); + return NULL; +} + +PyDoc_STRVAR(count__doc__, +"S.count(sub[, start[, end]]) -> int\n\ +\n\ +Return the number of occurrences of substring sub in Unicode string\n\ +S[start:end]. Optional arguments start and end are\n\ +interpreted as in slice notation."); + +static PyObject * +unicode_count(PyUnicodeObject *self, PyObject *args) +{ + PyUnicodeObject *substring; + int start = 0; + int end = INT_MAX; + PyObject *result; + + if (!PyArg_ParseTuple(args, "O|O&O&:count", &substring, + _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) + return NULL; + + substring = (PyUnicodeObject *)PyUnicode_FromObject( + (PyObject *)substring); + if (substring == NULL) + return NULL; + + if (start < 0) + start += self->length; + if (start < 0) + start = 0; + if (end > self->length) + end = self->length; + if (end < 0) + end += self->length; + if (end < 0) + end = 0; + + result = PyInt_FromLong((long) count(self, start, end, substring)); + + Py_DECREF(substring); + return result; +} + +PyDoc_STRVAR(encode__doc__, +"S.encode([encoding[,errors]]) -> string\n\ +\n\ +Return an encoded string version of S. Default encoding is the current\n\ +default string encoding. errors may be given to set a different error\n\ +handling scheme. Default is 'strict' meaning that encoding errors raise\n\ +a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and\n\ +'xmlcharrefreplace' as well as any other name registered with\n\ +codecs.register_error that can handle UnicodeEncodeErrors."); + +static PyObject * +unicode_encode(PyUnicodeObject *self, PyObject *args) +{ + char *encoding = NULL; + char *errors = NULL; + if (!PyArg_ParseTuple(args, "|ss:encode", &encoding, &errors)) + return NULL; + return PyUnicode_AsEncodedString((PyObject *)self, encoding, errors); +} + +PyDoc_STRVAR(expandtabs__doc__, +"S.expandtabs([tabsize]) -> unicode\n\ +\n\ +Return a copy of S where all tab characters are expanded using spaces.\n\ +If tabsize is not given, a tab size of 8 characters is assumed."); + +static PyObject* +unicode_expandtabs(PyUnicodeObject *self, PyObject *args) +{ + Py_UNICODE *e; + Py_UNICODE *p; + Py_UNICODE *q; + int i, j; + PyUnicodeObject *u; + int tabsize = 8; + + if (!PyArg_ParseTuple(args, "|i:expandtabs", &tabsize)) + return NULL; + + /* First pass: determine size of output string */ + i = j = 0; + e = self->str + self->length; + for (p = self->str; p < e; p++) + if (*p == '\t') { + if (tabsize > 0) + j += tabsize - (j % tabsize); + } + else { + j++; + if (*p == '\n' || *p == '\r') { + i += j; + j = 0; + } + } + + /* Second pass: create output string and fill it */ + u = _PyUnicode_New(i + j); + if (!u) + return NULL; + + j = 0; + q = u->str; + + for (p = self->str; p < e; p++) + if (*p == '\t') { + if (tabsize > 0) { + i = tabsize - (j % tabsize); + j += i; + while (i--) + *q++ = ' '; + } + } + else { + j++; + *q++ = *p; + if (*p == '\n' || *p == '\r') + j = 0; + } + + return (PyObject*) u; +} + +PyDoc_STRVAR(find__doc__, +"S.find(sub [,start [,end]]) -> int\n\ +\n\ +Return the lowest index in S where substring sub is found,\n\ +such that sub is contained within s[start,end]. Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Return -1 on failure."); + +static PyObject * +unicode_find(PyUnicodeObject *self, PyObject *args) +{ + PyUnicodeObject *substring; + int start = 0; + int end = INT_MAX; + PyObject *result; + + if (!PyArg_ParseTuple(args, "O|O&O&:find", &substring, + _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) + return NULL; + substring = (PyUnicodeObject *)PyUnicode_FromObject( + (PyObject *)substring); + if (substring == NULL) + return NULL; + + result = PyInt_FromLong(findstring(self, substring, start, end, 1)); + + Py_DECREF(substring); + return result; +} + +static PyObject * +unicode_getitem(PyUnicodeObject *self, int index) +{ + if (index < 0 || index >= self->length) { + PyErr_SetString(PyExc_IndexError, "string index out of range"); + return NULL; + } + + return (PyObject*) PyUnicode_FromUnicode(&self->str[index], 1); +} + +static long +unicode_hash(PyUnicodeObject *self) +{ + /* Since Unicode objects compare equal to their ASCII string + counterparts, they should use the individual character values + as basis for their hash value. This is needed to assure that + strings and Unicode objects behave in the same way as + dictionary keys. */ + + register int len; + register Py_UNICODE *p; + register long x; + + if (self->hash != -1) + return self->hash; + len = PyUnicode_GET_SIZE(self); + p = PyUnicode_AS_UNICODE(self); + x = *p << 7; + while (--len >= 0) + x = (1000003*x) ^ *p++; + x ^= PyUnicode_GET_SIZE(self); + if (x == -1) + x = -2; + self->hash = x; + return x; +} + +PyDoc_STRVAR(index__doc__, +"S.index(sub [,start [,end]]) -> int\n\ +\n\ +Like S.find() but raise ValueError when the substring is not found."); + +static PyObject * +unicode_index(PyUnicodeObject *self, PyObject *args) +{ + int result; + PyUnicodeObject *substring; + int start = 0; + int end = INT_MAX; + + if (!PyArg_ParseTuple(args, "O|O&O&:index", &substring, + _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) + return NULL; + + substring = (PyUnicodeObject *)PyUnicode_FromObject( + (PyObject *)substring); + if (substring == NULL) + return NULL; + + result = findstring(self, substring, start, end, 1); + + Py_DECREF(substring); + if (result < 0) { + PyErr_SetString(PyExc_ValueError, "substring not found"); + return NULL; + } + return PyInt_FromLong(result); +} + +PyDoc_STRVAR(islower__doc__, +"S.islower() -> bool\n\ +\n\ +Return True if all cased characters in S are lowercase and there is\n\ +at least one cased character in S, False otherwise."); + +static PyObject* +unicode_islower(PyUnicodeObject *self) +{ + register const Py_UNICODE *p = PyUnicode_AS_UNICODE(self); + register const Py_UNICODE *e; + int cased; + + /* Shortcut for single character strings */ + if (PyUnicode_GET_SIZE(self) == 1) + return PyBool_FromLong(Py_UNICODE_ISLOWER(*p)); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyUnicode_GET_SIZE(self); + cased = 0; + for (; p < e; p++) { + register const Py_UNICODE ch = *p; + + if (Py_UNICODE_ISUPPER(ch) || Py_UNICODE_ISTITLE(ch)) + return PyBool_FromLong(0); + else if (!cased && Py_UNICODE_ISLOWER(ch)) + cased = 1; + } + return PyBool_FromLong(cased); +} + +PyDoc_STRVAR(isupper__doc__, +"S.isupper() -> bool\n\ +\n\ +Return True if all cased characters in S are uppercase and there is\n\ +at least one cased character in S, False otherwise."); + +static PyObject* +unicode_isupper(PyUnicodeObject *self) +{ + register const Py_UNICODE *p = PyUnicode_AS_UNICODE(self); + register const Py_UNICODE *e; + int cased; + + /* Shortcut for single character strings */ + if (PyUnicode_GET_SIZE(self) == 1) + return PyBool_FromLong(Py_UNICODE_ISUPPER(*p) != 0); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyUnicode_GET_SIZE(self); + cased = 0; + for (; p < e; p++) { + register const Py_UNICODE ch = *p; + + if (Py_UNICODE_ISLOWER(ch) || Py_UNICODE_ISTITLE(ch)) + return PyBool_FromLong(0); + else if (!cased && Py_UNICODE_ISUPPER(ch)) + cased = 1; + } + return PyBool_FromLong(cased); +} + +PyDoc_STRVAR(istitle__doc__, +"S.istitle() -> bool\n\ +\n\ +Return True if S is a titlecased string and there is at least one\n\ +character in S, i.e. upper- and titlecase characters may only\n\ +follow uncased characters and lowercase characters only cased ones.\n\ +Return False otherwise."); + +static PyObject* +unicode_istitle(PyUnicodeObject *self) +{ + register const Py_UNICODE *p = PyUnicode_AS_UNICODE(self); + register const Py_UNICODE *e; + int cased, previous_is_cased; + + /* Shortcut for single character strings */ + if (PyUnicode_GET_SIZE(self) == 1) + return PyBool_FromLong((Py_UNICODE_ISTITLE(*p) != 0) || + (Py_UNICODE_ISUPPER(*p) != 0)); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyUnicode_GET_SIZE(self); + cased = 0; + previous_is_cased = 0; + for (; p < e; p++) { + register const Py_UNICODE ch = *p; + + if (Py_UNICODE_ISUPPER(ch) || Py_UNICODE_ISTITLE(ch)) { + if (previous_is_cased) + return PyBool_FromLong(0); + previous_is_cased = 1; + cased = 1; + } + else if (Py_UNICODE_ISLOWER(ch)) { + if (!previous_is_cased) + return PyBool_FromLong(0); + previous_is_cased = 1; + cased = 1; + } + else + previous_is_cased = 0; + } + return PyBool_FromLong(cased); +} + +PyDoc_STRVAR(isspace__doc__, +"S.isspace() -> bool\n\ +\n\ +Return True if all characters in S are whitespace\n\ +and there is at least one character in S, False otherwise."); + +static PyObject* +unicode_isspace(PyUnicodeObject *self) +{ + register const Py_UNICODE *p = PyUnicode_AS_UNICODE(self); + register const Py_UNICODE *e; + + /* Shortcut for single character strings */ + if (PyUnicode_GET_SIZE(self) == 1 && + Py_UNICODE_ISSPACE(*p)) + return PyBool_FromLong(1); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyUnicode_GET_SIZE(self); + for (; p < e; p++) { + if (!Py_UNICODE_ISSPACE(*p)) + return PyBool_FromLong(0); + } + return PyBool_FromLong(1); +} + +PyDoc_STRVAR(isalpha__doc__, +"S.isalpha() -> bool\n\ +\n\ +Return True if all characters in S are alphabetic\n\ +and there is at least one character in S, False otherwise."); + +static PyObject* +unicode_isalpha(PyUnicodeObject *self) +{ + register const Py_UNICODE *p = PyUnicode_AS_UNICODE(self); + register const Py_UNICODE *e; + + /* Shortcut for single character strings */ + if (PyUnicode_GET_SIZE(self) == 1 && + Py_UNICODE_ISALPHA(*p)) + return PyBool_FromLong(1); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyUnicode_GET_SIZE(self); + for (; p < e; p++) { + if (!Py_UNICODE_ISALPHA(*p)) + return PyBool_FromLong(0); + } + return PyBool_FromLong(1); +} + +PyDoc_STRVAR(isalnum__doc__, +"S.isalnum() -> bool\n\ +\n\ +Return True if all characters in S are alphanumeric\n\ +and there is at least one character in S, False otherwise."); + +static PyObject* +unicode_isalnum(PyUnicodeObject *self) +{ + register const Py_UNICODE *p = PyUnicode_AS_UNICODE(self); + register const Py_UNICODE *e; + + /* Shortcut for single character strings */ + if (PyUnicode_GET_SIZE(self) == 1 && + Py_UNICODE_ISALNUM(*p)) + return PyBool_FromLong(1); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyUnicode_GET_SIZE(self); + for (; p < e; p++) { + if (!Py_UNICODE_ISALNUM(*p)) + return PyBool_FromLong(0); + } + return PyBool_FromLong(1); +} + +PyDoc_STRVAR(isdecimal__doc__, +"S.isdecimal() -> bool\n\ +\n\ +Return True if there are only decimal characters in S,\n\ +False otherwise."); + +static PyObject* +unicode_isdecimal(PyUnicodeObject *self) +{ + register const Py_UNICODE *p = PyUnicode_AS_UNICODE(self); + register const Py_UNICODE *e; + + /* Shortcut for single character strings */ + if (PyUnicode_GET_SIZE(self) == 1 && + Py_UNICODE_ISDECIMAL(*p)) + return PyBool_FromLong(1); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyUnicode_GET_SIZE(self); + for (; p < e; p++) { + if (!Py_UNICODE_ISDECIMAL(*p)) + return PyBool_FromLong(0); + } + return PyBool_FromLong(1); +} + +PyDoc_STRVAR(isdigit__doc__, +"S.isdigit() -> bool\n\ +\n\ +Return True if all characters in S are digits\n\ +and there is at least one character in S, False otherwise."); + +static PyObject* +unicode_isdigit(PyUnicodeObject *self) +{ + register const Py_UNICODE *p = PyUnicode_AS_UNICODE(self); + register const Py_UNICODE *e; + + /* Shortcut for single character strings */ + if (PyUnicode_GET_SIZE(self) == 1 && + Py_UNICODE_ISDIGIT(*p)) + return PyBool_FromLong(1); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyUnicode_GET_SIZE(self); + for (; p < e; p++) { + if (!Py_UNICODE_ISDIGIT(*p)) + return PyBool_FromLong(0); + } + return PyBool_FromLong(1); +} + +PyDoc_STRVAR(isnumeric__doc__, +"S.isnumeric() -> bool\n\ +\n\ +Return True if there are only numeric characters in S,\n\ +False otherwise."); + +static PyObject* +unicode_isnumeric(PyUnicodeObject *self) +{ + register const Py_UNICODE *p = PyUnicode_AS_UNICODE(self); + register const Py_UNICODE *e; + + /* Shortcut for single character strings */ + if (PyUnicode_GET_SIZE(self) == 1 && + Py_UNICODE_ISNUMERIC(*p)) + return PyBool_FromLong(1); + + /* Special case for empty strings */ + if (PyString_GET_SIZE(self) == 0) + return PyBool_FromLong(0); + + e = p + PyUnicode_GET_SIZE(self); + for (; p < e; p++) { + if (!Py_UNICODE_ISNUMERIC(*p)) + return PyBool_FromLong(0); + } + return PyBool_FromLong(1); +} + +PyDoc_STRVAR(join__doc__, +"S.join(sequence) -> unicode\n\ +\n\ +Return a string which is the concatenation of the strings in the\n\ +sequence. The separator between elements is S."); + +static PyObject* +unicode_join(PyObject *self, PyObject *data) +{ + return PyUnicode_Join(self, data); +} + +static int +unicode_length(PyUnicodeObject *self) +{ + return self->length; +} + +PyDoc_STRVAR(ljust__doc__, +"S.ljust(width) -> unicode\n\ +\n\ +Return S left justified in a Unicode string of length width. Padding is\n\ +done using spaces."); + +static PyObject * +unicode_ljust(PyUnicodeObject *self, PyObject *args) +{ + int width; + if (!PyArg_ParseTuple(args, "i:ljust", &width)) + return NULL; + + if (self->length >= width && PyUnicode_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*) self; + } + + return (PyObject*) pad(self, 0, width - self->length, ' '); +} + +PyDoc_STRVAR(lower__doc__, +"S.lower() -> unicode\n\ +\n\ +Return a copy of the string S converted to lowercase."); + +static PyObject* +unicode_lower(PyUnicodeObject *self) +{ + return fixup(self, fixlower); +} + +#define LEFTSTRIP 0 +#define RIGHTSTRIP 1 +#define BOTHSTRIP 2 + +/* Arrays indexed by above */ +static const char *stripformat[] = {"|O:lstrip", "|O:rstrip", "|O:strip"}; + +#define STRIPNAME(i) (stripformat[i]+3) + +static const Py_UNICODE * +unicode_memchr(const Py_UNICODE *s, Py_UNICODE c, size_t n) +{ + size_t i; + for (i = 0; i < n; ++i) + if (s[i] == c) + return s+i; + return NULL; +} + +/* externally visible for str.strip(unicode) */ +PyObject * +_PyUnicode_XStrip(PyUnicodeObject *self, int striptype, PyObject *sepobj) +{ + Py_UNICODE *s = PyUnicode_AS_UNICODE(self); + int len = PyUnicode_GET_SIZE(self); + Py_UNICODE *sep = PyUnicode_AS_UNICODE(sepobj); + int seplen = PyUnicode_GET_SIZE(sepobj); + int i, j; + + i = 0; + if (striptype != RIGHTSTRIP) { + while (i < len && unicode_memchr(sep, s[i], seplen)) { + i++; + } + } + + j = len; + if (striptype != LEFTSTRIP) { + do { + j--; + } while (j >= i && unicode_memchr(sep, s[j], seplen)); + j++; + } + + if (i == 0 && j == len && PyUnicode_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*)self; + } + else + return PyUnicode_FromUnicode(s+i, j-i); +} + + +static PyObject * +do_strip(PyUnicodeObject *self, int striptype) +{ + Py_UNICODE *s = PyUnicode_AS_UNICODE(self); + int len = PyUnicode_GET_SIZE(self), i, j; + + i = 0; + if (striptype != RIGHTSTRIP) { + while (i < len && Py_UNICODE_ISSPACE(s[i])) { + i++; + } + } + + j = len; + if (striptype != LEFTSTRIP) { + do { + j--; + } while (j >= i && Py_UNICODE_ISSPACE(s[j])); + j++; + } + + if (i == 0 && j == len && PyUnicode_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*)self; + } + else + return PyUnicode_FromUnicode(s+i, j-i); +} + + +static PyObject * +do_argstrip(PyUnicodeObject *self, int striptype, PyObject *args) +{ + PyObject *sep = NULL; + + if (!PyArg_ParseTuple(args, (char *)stripformat[striptype], &sep)) + return NULL; + + if (sep != NULL && sep != Py_None) { + if (PyUnicode_Check(sep)) + return _PyUnicode_XStrip(self, striptype, sep); + else if (PyString_Check(sep)) { + PyObject *res; + sep = PyUnicode_FromObject(sep); + if (sep==NULL) + return NULL; + res = _PyUnicode_XStrip(self, striptype, sep); + Py_DECREF(sep); + return res; + } + else { + PyErr_Format(PyExc_TypeError, + "%s arg must be None, unicode or str", + STRIPNAME(striptype)); + return NULL; + } + } + + return do_strip(self, striptype); +} + + +PyDoc_STRVAR(strip__doc__, +"S.strip([chars]) -> unicode\n\ +\n\ +Return a copy of the string S with leading and trailing\n\ +whitespace removed.\n\ +If chars is given and not None, remove characters in chars instead.\n\ +If chars is a str, it will be converted to unicode before stripping"); + +static PyObject * +unicode_strip(PyUnicodeObject *self, PyObject *args) +{ + if (PyTuple_GET_SIZE(args) == 0) + return do_strip(self, BOTHSTRIP); /* Common case */ + else + return do_argstrip(self, BOTHSTRIP, args); +} + + +PyDoc_STRVAR(lstrip__doc__, +"S.lstrip([chars]) -> unicode\n\ +\n\ +Return a copy of the string S with leading whitespace removed.\n\ +If chars is given and not None, remove characters in chars instead.\n\ +If chars is a str, it will be converted to unicode before stripping"); + +static PyObject * +unicode_lstrip(PyUnicodeObject *self, PyObject *args) +{ + if (PyTuple_GET_SIZE(args) == 0) + return do_strip(self, LEFTSTRIP); /* Common case */ + else + return do_argstrip(self, LEFTSTRIP, args); +} + + +PyDoc_STRVAR(rstrip__doc__, +"S.rstrip([chars]) -> unicode\n\ +\n\ +Return a copy of the string S with trailing whitespace removed.\n\ +If chars is given and not None, remove characters in chars instead.\n\ +If chars is a str, it will be converted to unicode before stripping"); + +static PyObject * +unicode_rstrip(PyUnicodeObject *self, PyObject *args) +{ + if (PyTuple_GET_SIZE(args) == 0) + return do_strip(self, RIGHTSTRIP); /* Common case */ + else + return do_argstrip(self, RIGHTSTRIP, args); +} + + +static PyObject* +unicode_repeat(PyUnicodeObject *str, int len) +{ + PyUnicodeObject *u; + Py_UNICODE *p; + int nchars; + size_t nbytes; + + if (len < 0) + len = 0; + + if (len == 1 && PyUnicode_CheckExact(str)) { + /* no repeat, return original string */ + Py_INCREF(str); + return (PyObject*) str; + } + + /* ensure # of chars needed doesn't overflow int and # of bytes + * needed doesn't overflow size_t + */ + nchars = len * str->length; + if (len && nchars / len != str->length) { + PyErr_SetString(PyExc_OverflowError, + "repeated string is too long"); + return NULL; + } + nbytes = (nchars + 1) * sizeof(Py_UNICODE); + if (nbytes / sizeof(Py_UNICODE) != (size_t)(nchars + 1)) { + PyErr_SetString(PyExc_OverflowError, + "repeated string is too long"); + return NULL; + } + u = _PyUnicode_New(nchars); + if (!u) + return NULL; + + p = u->str; + + while (len-- > 0) { + Py_UNICODE_COPY(p, str->str, str->length); + p += str->length; + } + + return (PyObject*) u; +} + +PyObject *PyUnicode_Replace(PyObject *obj, + PyObject *subobj, + PyObject *replobj, + int maxcount) +{ + PyObject *self; + PyObject *str1; + PyObject *str2; + PyObject *result; + + self = PyUnicode_FromObject(obj); + if (self == NULL) + return NULL; + str1 = PyUnicode_FromObject(subobj); + if (str1 == NULL) { + Py_DECREF(self); + return NULL; + } + str2 = PyUnicode_FromObject(replobj); + if (str2 == NULL) { + Py_DECREF(self); + Py_DECREF(str1); + return NULL; + } + result = replace((PyUnicodeObject *)self, + (PyUnicodeObject *)str1, + (PyUnicodeObject *)str2, + maxcount); + Py_DECREF(self); + Py_DECREF(str1); + Py_DECREF(str2); + return result; +} + +PyDoc_STRVAR(replace__doc__, +"S.replace (old, new[, maxsplit]) -> unicode\n\ +\n\ +Return a copy of S with all occurrences of substring\n\ +old replaced by new. If the optional argument maxsplit is\n\ +given, only the first maxsplit occurrences are replaced."); + +static PyObject* +unicode_replace(PyUnicodeObject *self, PyObject *args) +{ + PyUnicodeObject *str1; + PyUnicodeObject *str2; + int maxcount = -1; + PyObject *result; + + if (!PyArg_ParseTuple(args, "OO|i:replace", &str1, &str2, &maxcount)) + return NULL; + str1 = (PyUnicodeObject *)PyUnicode_FromObject((PyObject *)str1); + if (str1 == NULL) + return NULL; + str2 = (PyUnicodeObject *)PyUnicode_FromObject((PyObject *)str2); + if (str2 == NULL) { + Py_DECREF(str1); + return NULL; + } + + result = replace(self, str1, str2, maxcount); + + Py_DECREF(str1); + Py_DECREF(str2); + return result; +} + +static +PyObject *unicode_repr(PyObject *unicode) +{ + return unicodeescape_string(PyUnicode_AS_UNICODE(unicode), + PyUnicode_GET_SIZE(unicode), + 1); +} + +PyDoc_STRVAR(rfind__doc__, +"S.rfind(sub [,start [,end]]) -> int\n\ +\n\ +Return the highest index in S where substring sub is found,\n\ +such that sub is contained within s[start,end]. Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Return -1 on failure."); + +static PyObject * +unicode_rfind(PyUnicodeObject *self, PyObject *args) +{ + PyUnicodeObject *substring; + int start = 0; + int end = INT_MAX; + PyObject *result; + + if (!PyArg_ParseTuple(args, "O|O&O&:rfind", &substring, + _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) + return NULL; + substring = (PyUnicodeObject *)PyUnicode_FromObject( + (PyObject *)substring); + if (substring == NULL) + return NULL; + + result = PyInt_FromLong(findstring(self, substring, start, end, -1)); + + Py_DECREF(substring); + return result; +} + +PyDoc_STRVAR(rindex__doc__, +"S.rindex(sub [,start [,end]]) -> int\n\ +\n\ +Like S.rfind() but raise ValueError when the substring is not found."); + +static PyObject * +unicode_rindex(PyUnicodeObject *self, PyObject *args) +{ + int result; + PyUnicodeObject *substring; + int start = 0; + int end = INT_MAX; + + if (!PyArg_ParseTuple(args, "O|O&O&:rindex", &substring, + _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) + return NULL; + substring = (PyUnicodeObject *)PyUnicode_FromObject( + (PyObject *)substring); + if (substring == NULL) + return NULL; + + result = findstring(self, substring, start, end, -1); + + Py_DECREF(substring); + if (result < 0) { + PyErr_SetString(PyExc_ValueError, "substring not found"); + return NULL; + } + return PyInt_FromLong(result); +} + +PyDoc_STRVAR(rjust__doc__, +"S.rjust(width) -> unicode\n\ +\n\ +Return S right justified in a Unicode string of length width. Padding is\n\ +done using spaces."); + +static PyObject * +unicode_rjust(PyUnicodeObject *self, PyObject *args) +{ + int width; + if (!PyArg_ParseTuple(args, "i:rjust", &width)) + return NULL; + + if (self->length >= width && PyUnicode_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*) self; + } + + return (PyObject*) pad(self, width - self->length, 0, ' '); +} + +static PyObject* +unicode_slice(PyUnicodeObject *self, int start, int end) +{ + /* standard clamping */ + if (start < 0) + start = 0; + if (end < 0) + end = 0; + if (end > self->length) + end = self->length; + if (start == 0 && end == self->length && PyUnicode_CheckExact(self)) { + /* full slice, return original string */ + Py_INCREF(self); + return (PyObject*) self; + } + if (start > end) + start = end; + /* copy slice */ + return (PyObject*) PyUnicode_FromUnicode(self->str + start, + end - start); +} + +PyObject *PyUnicode_Split(PyObject *s, + PyObject *sep, + int maxsplit) +{ + PyObject *result; + + s = PyUnicode_FromObject(s); + if (s == NULL) + return NULL; + if (sep != NULL) { + sep = PyUnicode_FromObject(sep); + if (sep == NULL) { + Py_DECREF(s); + return NULL; + } + } + + result = split((PyUnicodeObject *)s, (PyUnicodeObject *)sep, maxsplit); + + Py_DECREF(s); + Py_XDECREF(sep); + return result; +} + +PyDoc_STRVAR(split__doc__, +"S.split([sep [,maxsplit]]) -> list of strings\n\ +\n\ +Return a list of the words in S, using sep as the\n\ +delimiter string. If maxsplit is given, at most maxsplit\n\ +splits are done. If sep is not specified, any whitespace string\n\ +is a separator."); + +static PyObject* +unicode_split(PyUnicodeObject *self, PyObject *args) +{ + PyObject *substring = Py_None; + int maxcount = -1; + + if (!PyArg_ParseTuple(args, "|Oi:split", &substring, &maxcount)) + return NULL; + + if (substring == Py_None) + return split(self, NULL, maxcount); + else if (PyUnicode_Check(substring)) + return split(self, (PyUnicodeObject *)substring, maxcount); + else + return PyUnicode_Split((PyObject *)self, substring, maxcount); +} + +PyDoc_STRVAR(splitlines__doc__, +"S.splitlines([keepends]]) -> list of strings\n\ +\n\ +Return a list of the lines in S, breaking at line boundaries.\n\ +Line breaks are not included in the resulting list unless keepends\n\ +is given and true."); + +static PyObject* +unicode_splitlines(PyUnicodeObject *self, PyObject *args) +{ + int keepends = 0; + + if (!PyArg_ParseTuple(args, "|i:splitlines", &keepends)) + return NULL; + + return PyUnicode_Splitlines((PyObject *)self, keepends); +} + +static +PyObject *unicode_str(PyUnicodeObject *self) +{ + return PyUnicode_AsEncodedString((PyObject *)self, NULL, NULL); +} + +PyDoc_STRVAR(swapcase__doc__, +"S.swapcase() -> unicode\n\ +\n\ +Return a copy of S with uppercase characters converted to lowercase\n\ +and vice versa."); + +static PyObject* +unicode_swapcase(PyUnicodeObject *self) +{ + return fixup(self, fixswapcase); +} + +PyDoc_STRVAR(translate__doc__, +"S.translate(table) -> unicode\n\ +\n\ +Return a copy of the string S, where all characters have been mapped\n\ +through the given translation table, which must be a mapping of\n\ +Unicode ordinals to Unicode ordinals, Unicode strings or None.\n\ +Unmapped characters are left untouched. Characters mapped to None\n\ +are deleted."); + +static PyObject* +unicode_translate(PyUnicodeObject *self, PyObject *table) +{ + return PyUnicode_TranslateCharmap(self->str, + self->length, + table, + "ignore"); +} + +PyDoc_STRVAR(upper__doc__, +"S.upper() -> unicode\n\ +\n\ +Return a copy of S converted to uppercase."); + +static PyObject* +unicode_upper(PyUnicodeObject *self) +{ + return fixup(self, fixupper); +} + +PyDoc_STRVAR(zfill__doc__, +"S.zfill(width) -> unicode\n\ +\n\ +Pad a numeric string x with zeros on the left, to fill a field\n\ +of the specified width. The string x is never truncated."); + +static PyObject * +unicode_zfill(PyUnicodeObject *self, PyObject *args) +{ + int fill; + PyUnicodeObject *u; + + int width; + if (!PyArg_ParseTuple(args, "i:zfill", &width)) + return NULL; + + if (self->length >= width) { + if (PyUnicode_CheckExact(self)) { + Py_INCREF(self); + return (PyObject*) self; + } + else + return PyUnicode_FromUnicode( + PyUnicode_AS_UNICODE(self), + PyUnicode_GET_SIZE(self) + ); + } + + fill = width - self->length; + + u = pad(self, fill, 0, '0'); + + if (u == NULL) + return NULL; + + if (u->str[fill] == '+' || u->str[fill] == '-') { + /* move sign to beginning of string */ + u->str[0] = u->str[fill]; + u->str[fill] = '0'; + } + + return (PyObject*) u; +} + +#if 0 +static PyObject* +unicode_freelistsize(PyUnicodeObject *self) +{ + return PyInt_FromLong(unicode_freelist_size); +} +#endif + +PyDoc_STRVAR(startswith__doc__, +"S.startswith(prefix[, start[, end]]) -> bool\n\ +\n\ +Return True if S starts with the specified prefix, False otherwise.\n\ +With optional start, test S beginning at that position.\n\ +With optional end, stop comparing S at that position."); + +static PyObject * +unicode_startswith(PyUnicodeObject *self, + PyObject *args) +{ + PyUnicodeObject *substring; + int start = 0; + int end = INT_MAX; + PyObject *result; + + if (!PyArg_ParseTuple(args, "O|O&O&:startswith", &substring, + _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) + return NULL; + substring = (PyUnicodeObject *)PyUnicode_FromObject( + (PyObject *)substring); + if (substring == NULL) + return NULL; + + result = PyBool_FromLong(tailmatch(self, substring, start, end, -1)); + + Py_DECREF(substring); + return result; +} + + +PyDoc_STRVAR(endswith__doc__, +"S.endswith(suffix[, start[, end]]) -> bool\n\ +\n\ +Return True if S ends with the specified suffix, False otherwise.\n\ +With optional start, test S beginning at that position.\n\ +With optional end, stop comparing S at that position."); + +static PyObject * +unicode_endswith(PyUnicodeObject *self, + PyObject *args) +{ + PyUnicodeObject *substring; + int start = 0; + int end = INT_MAX; + PyObject *result; + + if (!PyArg_ParseTuple(args, "O|O&O&:endswith", &substring, + _PyEval_SliceIndex, &start, _PyEval_SliceIndex, &end)) + return NULL; + substring = (PyUnicodeObject *)PyUnicode_FromObject( + (PyObject *)substring); + if (substring == NULL) + return NULL; + + result = PyBool_FromLong(tailmatch(self, substring, start, end, +1)); + + Py_DECREF(substring); + return result; +} + + + +static PyObject * +unicode_getnewargs(PyUnicodeObject *v) +{ + return Py_BuildValue("(u#)", v->str, v->length); +} + + +static PyMethodDef unicode_methods[] = { + + /* Order is according to common usage: often used methods should + appear first, since lookup is done sequentially. */ + + {"encode", (PyCFunction) unicode_encode, METH_VARARGS, encode__doc__}, + {"replace", (PyCFunction) unicode_replace, METH_VARARGS, replace__doc__}, + {"split", (PyCFunction) unicode_split, METH_VARARGS, split__doc__}, + {"join", (PyCFunction) unicode_join, METH_O, join__doc__}, + {"capitalize", (PyCFunction) unicode_capitalize, METH_NOARGS, capitalize__doc__}, + {"title", (PyCFunction) unicode_title, METH_NOARGS, title__doc__}, + {"center", (PyCFunction) unicode_center, METH_VARARGS, center__doc__}, + {"count", (PyCFunction) unicode_count, METH_VARARGS, count__doc__}, + {"expandtabs", (PyCFunction) unicode_expandtabs, METH_VARARGS, expandtabs__doc__}, + {"find", (PyCFunction) unicode_find, METH_VARARGS, find__doc__}, + {"index", (PyCFunction) unicode_index, METH_VARARGS, index__doc__}, + {"ljust", (PyCFunction) unicode_ljust, METH_VARARGS, ljust__doc__}, + {"lower", (PyCFunction) unicode_lower, METH_NOARGS, lower__doc__}, + {"lstrip", (PyCFunction) unicode_lstrip, METH_VARARGS, lstrip__doc__}, +/* {"maketrans", (PyCFunction) unicode_maketrans, METH_VARARGS, maketrans__doc__}, */ + {"rfind", (PyCFunction) unicode_rfind, METH_VARARGS, rfind__doc__}, + {"rindex", (PyCFunction) unicode_rindex, METH_VARARGS, rindex__doc__}, + {"rjust", (PyCFunction) unicode_rjust, METH_VARARGS, rjust__doc__}, + {"rstrip", (PyCFunction) unicode_rstrip, METH_VARARGS, rstrip__doc__}, + {"splitlines", (PyCFunction) unicode_splitlines, METH_VARARGS, splitlines__doc__}, + {"strip", (PyCFunction) unicode_strip, METH_VARARGS, strip__doc__}, + {"swapcase", (PyCFunction) unicode_swapcase, METH_NOARGS, swapcase__doc__}, + {"translate", (PyCFunction) unicode_translate, METH_O, translate__doc__}, + {"upper", (PyCFunction) unicode_upper, METH_NOARGS, upper__doc__}, + {"startswith", (PyCFunction) unicode_startswith, METH_VARARGS, startswith__doc__}, + {"endswith", (PyCFunction) unicode_endswith, METH_VARARGS, endswith__doc__}, + {"islower", (PyCFunction) unicode_islower, METH_NOARGS, islower__doc__}, + {"isupper", (PyCFunction) unicode_isupper, METH_NOARGS, isupper__doc__}, + {"istitle", (PyCFunction) unicode_istitle, METH_NOARGS, istitle__doc__}, + {"isspace", (PyCFunction) unicode_isspace, METH_NOARGS, isspace__doc__}, + {"isdecimal", (PyCFunction) unicode_isdecimal, METH_NOARGS, isdecimal__doc__}, + {"isdigit", (PyCFunction) unicode_isdigit, METH_NOARGS, isdigit__doc__}, + {"isnumeric", (PyCFunction) unicode_isnumeric, METH_NOARGS, isnumeric__doc__}, + {"isalpha", (PyCFunction) unicode_isalpha, METH_NOARGS, isalpha__doc__}, + {"isalnum", (PyCFunction) unicode_isalnum, METH_NOARGS, isalnum__doc__}, + {"zfill", (PyCFunction) unicode_zfill, METH_VARARGS, zfill__doc__}, +#if 0 + {"capwords", (PyCFunction) unicode_capwords, METH_NOARGS, capwords__doc__}, +#endif + +#if 0 + /* This one is just used for debugging the implementation. */ + {"freelistsize", (PyCFunction) unicode_freelistsize, METH_NOARGS}, +#endif + + {"__getnewargs__", (PyCFunction)unicode_getnewargs, METH_NOARGS}, + {NULL, NULL} +}; + +static PyObject * +unicode_mod(PyObject *v, PyObject *w) +{ + if (!PyUnicode_Check(v)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return PyUnicode_Format(v, w); +} + +static PyNumberMethods unicode_as_number = { + 0, /*nb_add*/ + 0, /*nb_subtract*/ + 0, /*nb_multiply*/ + 0, /*nb_divide*/ + unicode_mod, /*nb_remainder*/ +}; + +static PySequenceMethods unicode_as_sequence = { + (inquiry) unicode_length, /* sq_length */ + (binaryfunc) PyUnicode_Concat, /* sq_concat */ + (intargfunc) unicode_repeat, /* sq_repeat */ + (intargfunc) unicode_getitem, /* sq_item */ + (intintargfunc) unicode_slice, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)PyUnicode_Contains, /*sq_contains*/ +}; + +static PyObject* +unicode_subscript(PyUnicodeObject* self, PyObject* item) +{ + if (PyInt_Check(item)) { + long i = PyInt_AS_LONG(item); + if (i < 0) + i += PyString_GET_SIZE(self); + return unicode_getitem(self, i); + } else if (PyLong_Check(item)) { + long i = PyLong_AsLong(item); + if (i == -1 && PyErr_Occurred()) + return NULL; + if (i < 0) + i += PyString_GET_SIZE(self); + return unicode_getitem(self, i); + } else if (PySlice_Check(item)) { + int start, stop, step, slicelength, cur, i; + Py_UNICODE* source_buf; + Py_UNICODE* result_buf; + PyObject* result; + + if (PySlice_GetIndicesEx((PySliceObject*)item, PyString_GET_SIZE(self), + &start, &stop, &step, &slicelength) < 0) { + return NULL; + } + + if (slicelength <= 0) { + return PyUnicode_FromUnicode(NULL, 0); + } else { + source_buf = PyUnicode_AS_UNICODE((PyObject*)self); + result_buf = PyMem_MALLOC(slicelength*sizeof(Py_UNICODE)); + + for (cur = start, i = 0; i < slicelength; cur += step, i++) { + result_buf[i] = source_buf[cur]; + } + + result = PyUnicode_FromUnicode(result_buf, slicelength); + PyMem_FREE(result_buf); + return result; + } + } else { + PyErr_SetString(PyExc_TypeError, "string indices must be integers"); + return NULL; + } +} + +static PyMappingMethods unicode_as_mapping = { + (inquiry)unicode_length, /* mp_length */ + (binaryfunc)unicode_subscript, /* mp_subscript */ + (objobjargproc)0, /* mp_ass_subscript */ +}; + +static int +unicode_buffer_getreadbuf(PyUnicodeObject *self, + int index, + const void **ptr) +{ + if (index != 0) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent unicode segment"); + return -1; + } + *ptr = (void *) self->str; + return PyUnicode_GET_DATA_SIZE(self); +} + +static int +unicode_buffer_getwritebuf(PyUnicodeObject *self, int index, + const void **ptr) +{ + PyErr_SetString(PyExc_TypeError, + "cannot use unicode as modifiable buffer"); + return -1; +} + +static int +unicode_buffer_getsegcount(PyUnicodeObject *self, + int *lenp) +{ + if (lenp) + *lenp = PyUnicode_GET_DATA_SIZE(self); + return 1; +} + +static int +unicode_buffer_getcharbuf(PyUnicodeObject *self, + int index, + const void **ptr) +{ + PyObject *str; + + if (index != 0) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent unicode segment"); + return -1; + } + str = _PyUnicode_AsDefaultEncodedString((PyObject *)self, NULL); + if (str == NULL) + return -1; + *ptr = (void *) PyString_AS_STRING(str); + return PyString_GET_SIZE(str); +} + +/* Helpers for PyUnicode_Format() */ + +static PyObject * +getnextarg(PyObject *args, int arglen, int *p_argidx) +{ + int argidx = *p_argidx; + if (argidx < arglen) { + (*p_argidx)++; + if (arglen < 0) + return args; + else + return PyTuple_GetItem(args, argidx); + } + PyErr_SetString(PyExc_TypeError, + "not enough arguments for format string"); + return NULL; +} + +#define F_LJUST (1<<0) +#define F_SIGN (1<<1) +#define F_BLANK (1<<2) +#define F_ALT (1<<3) +#define F_ZERO (1<<4) + +static +int usprintf(register Py_UNICODE *buffer, char *format, ...) +{ + register int i; + int len; + va_list va; + char *charbuffer; + va_start(va, format); + + /* First, format the string as char array, then expand to Py_UNICODE + array. */ + charbuffer = (char *)buffer; + len = vsprintf(charbuffer, format, va); + for (i = len - 1; i >= 0; i--) + buffer[i] = (Py_UNICODE) charbuffer[i]; + + va_end(va); + return len; +} + +/* XXX To save some code duplication, formatfloat/long/int could have been + shared with stringobject.c, converting from 8-bit to Unicode after the + formatting is done. */ + +static int +formatfloat(Py_UNICODE *buf, + size_t buflen, + int flags, + int prec, + int type, + PyObject *v) +{ + /* fmt = '%#.' + `prec` + `type` + worst case length = 3 + 10 (len of INT_MAX) + 1 = 14 (use 20)*/ + char fmt[20]; + double x; + + x = PyFloat_AsDouble(v); + if (x == -1.0 && PyErr_Occurred()) + return -1; + if (prec < 0) + prec = 6; + if (type == 'f' && (fabs(x) / 1e25) >= 1e25) + type = 'g'; + /* Worst case length calc to ensure no buffer overrun: + + 'g' formats: + fmt = %#.g + buf = '-' + [0-9]*prec + '.' + 'e+' + (longest exp + for any double rep.) + len = 1 + prec + 1 + 2 + 5 = 9 + prec + + 'f' formats: + buf = '-' + [0-9]*x + '.' + [0-9]*prec (with x < 50) + len = 1 + 50 + 1 + prec = 52 + prec + + If prec=0 the effective precision is 1 (the leading digit is + always given), therefore increase the length by one. + + */ + if ((type == 'g' && buflen <= (size_t)10 + (size_t)prec) || + (type == 'f' && buflen <= (size_t)53 + (size_t)prec)) { + PyErr_SetString(PyExc_OverflowError, + "formatted float is too long (precision too large?)"); + return -1; + } + PyOS_snprintf(fmt, sizeof(fmt), "%%%s.%d%c", + (flags&F_ALT) ? "#" : "", + prec, type); + return usprintf(buf, fmt, x); +} + +static PyObject* +formatlong(PyObject *val, int flags, int prec, int type) +{ + char *buf; + int i, len; + PyObject *str; /* temporary string object. */ + PyUnicodeObject *result; + + str = _PyString_FormatLong(val, flags, prec, type, &buf, &len); + if (!str) + return NULL; + result = _PyUnicode_New(len); + for (i = 0; i < len; i++) + result->str[i] = buf[i]; + result->str[len] = 0; + Py_DECREF(str); + return (PyObject*)result; +} + +static int +formatint(Py_UNICODE *buf, + size_t buflen, + int flags, + int prec, + int type, + PyObject *v) +{ + /* fmt = '%#.' + `prec` + 'l' + `type` + * worst case length = 3 + 19 (worst len of INT_MAX on 64-bit machine) + * + 1 + 1 + * = 24 + */ + char fmt[64]; /* plenty big enough! */ + long x; + + x = PyInt_AsLong(v); + if (x == -1 && PyErr_Occurred()) + return -1; + if (x < 0 && type != 'd' && type != 'i') { + if (PyErr_Warn(PyExc_FutureWarning, + "%u/%o/%x/%X of negative int will return " + "a signed string in Python 2.4 and up") < 0) + return -1; + } + if (prec < 0) + prec = 1; + + /* buf = '+'/'-'/'0'/'0x' + '[0-9]'*max(prec,len(x in octal)) + * worst case buf = '0x' + [0-9]*prec, where prec >= 11 + */ + if (buflen <= 13 || buflen <= (size_t)2 + (size_t)prec) { + PyErr_SetString(PyExc_OverflowError, + "formatted integer is too long (precision too large?)"); + return -1; + } + + if ((flags & F_ALT) && + (type == 'x' || type == 'X')) { + /* When converting under %#x or %#X, there are a number + * of issues that cause pain: + * - when 0 is being converted, the C standard leaves off + * the '0x' or '0X', which is inconsistent with other + * %#x/%#X conversions and inconsistent with Python's + * hex() function + * - there are platforms that violate the standard and + * convert 0 with the '0x' or '0X' + * (Metrowerks, Compaq Tru64) + * - there are platforms that give '0x' when converting + * under %#X, but convert 0 in accordance with the + * standard (OS/2 EMX) + * + * We can achieve the desired consistency by inserting our + * own '0x' or '0X' prefix, and substituting %x/%X in place + * of %#x/%#X. + * + * Note that this is the same approach as used in + * formatint() in stringobject.c + */ + PyOS_snprintf(fmt, sizeof(fmt), "0%c%%.%dl%c", + type, prec, type); + } + else { + PyOS_snprintf(fmt, sizeof(fmt), "%%%s.%dl%c", + (flags&F_ALT) ? "#" : "", + prec, type); + } + return usprintf(buf, fmt, x); +} + +static int +formatchar(Py_UNICODE *buf, + size_t buflen, + PyObject *v) +{ + /* presume that the buffer is at least 2 characters long */ + if (PyUnicode_Check(v)) { + if (PyUnicode_GET_SIZE(v) != 1) + goto onError; + buf[0] = PyUnicode_AS_UNICODE(v)[0]; + } + + else if (PyString_Check(v)) { + if (PyString_GET_SIZE(v) != 1) + goto onError; + buf[0] = (Py_UNICODE)PyString_AS_STRING(v)[0]; + } + + else { + /* Integer input truncated to a character */ + long x; + x = PyInt_AsLong(v); + if (x == -1 && PyErr_Occurred()) + goto onError; +#ifdef Py_UNICODE_WIDE + if (x < 0 || x > 0x10ffff) { + PyErr_SetString(PyExc_OverflowError, + "%c arg not in range(0x110000) " + "(wide Python build)"); + return -1; + } +#else + if (x < 0 || x > 0xffff) { + PyErr_SetString(PyExc_OverflowError, + "%c arg not in range(0x10000) " + "(narrow Python build)"); + return -1; + } +#endif + buf[0] = (Py_UNICODE) x; + } + buf[1] = '\0'; + return 1; + + onError: + PyErr_SetString(PyExc_TypeError, + "%c requires int or char"); + return -1; +} + +/* fmt%(v1,v2,...) is roughly equivalent to sprintf(fmt, v1, v2, ...) + + FORMATBUFLEN is the length of the buffer in which the floats, ints, & + chars are formatted. XXX This is a magic number. Each formatting + routine does bounds checking to ensure no overflow, but a better + solution may be to malloc a buffer of appropriate size for each + format. For now, the current solution is sufficient. +*/ +#define FORMATBUFLEN (size_t)120 + +PyObject *PyUnicode_Format(PyObject *format, + PyObject *args) +{ + Py_UNICODE *fmt, *res; + int fmtcnt, rescnt, reslen, arglen, argidx; + int args_owned = 0; + PyUnicodeObject *result = NULL; + PyObject *dict = NULL; + PyObject *uformat; + + if (format == NULL || args == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + uformat = PyUnicode_FromObject(format); + if (uformat == NULL) + return NULL; + fmt = PyUnicode_AS_UNICODE(uformat); + fmtcnt = PyUnicode_GET_SIZE(uformat); + + reslen = rescnt = fmtcnt + 100; + result = _PyUnicode_New(reslen); + if (result == NULL) + goto onError; + res = PyUnicode_AS_UNICODE(result); + + if (PyTuple_Check(args)) { + arglen = PyTuple_Size(args); + argidx = 0; + } + else { + arglen = -1; + argidx = -2; + } + if (args->ob_type->tp_as_mapping && !PyTuple_Check(args) && + !PyObject_TypeCheck(args, &PyBaseString_Type)) + dict = args; + + while (--fmtcnt >= 0) { + if (*fmt != '%') { + if (--rescnt < 0) { + rescnt = fmtcnt + 100; + reslen += rescnt; + if (_PyUnicode_Resize(&result, reslen) < 0) + return NULL; + res = PyUnicode_AS_UNICODE(result) + reslen - rescnt; + --rescnt; + } + *res++ = *fmt++; + } + else { + /* Got a format specifier */ + int flags = 0; + int width = -1; + int prec = -1; + Py_UNICODE c = '\0'; + Py_UNICODE fill; + PyObject *v = NULL; + PyObject *temp = NULL; + Py_UNICODE *pbuf; + Py_UNICODE sign; + int len; + Py_UNICODE formatbuf[FORMATBUFLEN]; /* For format{float,int,char}() */ + + fmt++; + if (*fmt == '(') { + Py_UNICODE *keystart; + int keylen; + PyObject *key; + int pcount = 1; + + if (dict == NULL) { + PyErr_SetString(PyExc_TypeError, + "format requires a mapping"); + goto onError; + } + ++fmt; + --fmtcnt; + keystart = fmt; + /* Skip over balanced parentheses */ + while (pcount > 0 && --fmtcnt >= 0) { + if (*fmt == ')') + --pcount; + else if (*fmt == '(') + ++pcount; + fmt++; + } + keylen = fmt - keystart - 1; + if (fmtcnt < 0 || pcount > 0) { + PyErr_SetString(PyExc_ValueError, + "incomplete format key"); + goto onError; + } +#if 0 + /* keys are converted to strings using UTF-8 and + then looked up since Python uses strings to hold + variables names etc. in its namespaces and we + wouldn't want to break common idioms. */ + key = PyUnicode_EncodeUTF8(keystart, + keylen, + NULL); +#else + key = PyUnicode_FromUnicode(keystart, keylen); +#endif + if (key == NULL) + goto onError; + if (args_owned) { + Py_DECREF(args); + args_owned = 0; + } + args = PyObject_GetItem(dict, key); + Py_DECREF(key); + if (args == NULL) { + goto onError; + } + args_owned = 1; + arglen = -1; + argidx = -2; + } + while (--fmtcnt >= 0) { + switch (c = *fmt++) { + case '-': flags |= F_LJUST; continue; + case '+': flags |= F_SIGN; continue; + case ' ': flags |= F_BLANK; continue; + case '#': flags |= F_ALT; continue; + case '0': flags |= F_ZERO; continue; + } + break; + } + if (c == '*') { + v = getnextarg(args, arglen, &argidx); + if (v == NULL) + goto onError; + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "* wants int"); + goto onError; + } + width = PyInt_AsLong(v); + if (width < 0) { + flags |= F_LJUST; + width = -width; + } + if (--fmtcnt >= 0) + c = *fmt++; + } + else if (c >= '0' && c <= '9') { + width = c - '0'; + while (--fmtcnt >= 0) { + c = *fmt++; + if (c < '0' || c > '9') + break; + if ((width*10) / 10 != width) { + PyErr_SetString(PyExc_ValueError, + "width too big"); + goto onError; + } + width = width*10 + (c - '0'); + } + } + if (c == '.') { + prec = 0; + if (--fmtcnt >= 0) + c = *fmt++; + if (c == '*') { + v = getnextarg(args, arglen, &argidx); + if (v == NULL) + goto onError; + if (!PyInt_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "* wants int"); + goto onError; + } + prec = PyInt_AsLong(v); + if (prec < 0) + prec = 0; + if (--fmtcnt >= 0) + c = *fmt++; + } + else if (c >= '0' && c <= '9') { + prec = c - '0'; + while (--fmtcnt >= 0) { + c = Py_CHARMASK(*fmt++); + if (c < '0' || c > '9') + break; + if ((prec*10) / 10 != prec) { + PyErr_SetString(PyExc_ValueError, + "prec too big"); + goto onError; + } + prec = prec*10 + (c - '0'); + } + } + } /* prec */ + if (fmtcnt >= 0) { + if (c == 'h' || c == 'l' || c == 'L') { + if (--fmtcnt >= 0) + c = *fmt++; + } + } + if (fmtcnt < 0) { + PyErr_SetString(PyExc_ValueError, + "incomplete format"); + goto onError; + } + if (c != '%') { + v = getnextarg(args, arglen, &argidx); + if (v == NULL) + goto onError; + } + sign = 0; + fill = ' '; + switch (c) { + + case '%': + pbuf = formatbuf; + /* presume that buffer length is at least 1 */ + pbuf[0] = '%'; + len = 1; + break; + + case 's': + case 'r': + if (PyUnicode_Check(v) && c == 's') { + temp = v; + Py_INCREF(temp); + } + else { + PyObject *unicode; + if (c == 's') + temp = PyObject_Str(v); + else + temp = PyObject_Repr(v); + if (temp == NULL) + goto onError; + if (!PyString_Check(temp)) { + /* XXX Note: this should never happen, since + PyObject_Repr() and PyObject_Str() assure + this */ + Py_DECREF(temp); + PyErr_SetString(PyExc_TypeError, + "%s argument has non-string str()"); + goto onError; + } + unicode = PyUnicode_Decode(PyString_AS_STRING(temp), + PyString_GET_SIZE(temp), + NULL, + "strict"); + Py_DECREF(temp); + temp = unicode; + if (temp == NULL) + goto onError; + } + pbuf = PyUnicode_AS_UNICODE(temp); + len = PyUnicode_GET_SIZE(temp); + if (prec >= 0 && len > prec) + len = prec; + break; + + case 'i': + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + if (c == 'i') + c = 'd'; + if (PyLong_Check(v)) { + temp = formatlong(v, flags, prec, c); + if (!temp) + goto onError; + pbuf = PyUnicode_AS_UNICODE(temp); + len = PyUnicode_GET_SIZE(temp); + /* unbounded ints can always produce + a sign character! */ + sign = 1; + } + else { + pbuf = formatbuf; + len = formatint(pbuf, sizeof(formatbuf)/sizeof(Py_UNICODE), + flags, prec, c, v); + if (len < 0) + goto onError; + /* only d conversion is signed */ + sign = c == 'd'; + } + if (flags & F_ZERO) + fill = '0'; + break; + + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (c == 'F') + c = 'f'; + pbuf = formatbuf; + len = formatfloat(pbuf, sizeof(formatbuf)/sizeof(Py_UNICODE), + flags, prec, c, v); + if (len < 0) + goto onError; + sign = 1; + if (flags & F_ZERO) + fill = '0'; + break; + + case 'c': + pbuf = formatbuf; + len = formatchar(pbuf, sizeof(formatbuf)/sizeof(Py_UNICODE), v); + if (len < 0) + goto onError; + break; + + default: + PyErr_Format(PyExc_ValueError, + "unsupported format character '%c' (0x%x) " + "at index %i", + (31<=c && c<=126) ? (char)c : '?', + (int)c, + (int)(fmt -1 - PyUnicode_AS_UNICODE(uformat))); + goto onError; + } + if (sign) { + if (*pbuf == '-' || *pbuf == '+') { + sign = *pbuf++; + len--; + } + else if (flags & F_SIGN) + sign = '+'; + else if (flags & F_BLANK) + sign = ' '; + else + sign = 0; + } + if (width < len) + width = len; + if (rescnt - (sign != 0) < width) { + reslen -= rescnt; + rescnt = width + fmtcnt + 100; + reslen += rescnt; + if (reslen < 0) { + Py_DECREF(result); + return PyErr_NoMemory(); + } + if (_PyUnicode_Resize(&result, reslen) < 0) + return NULL; + res = PyUnicode_AS_UNICODE(result) + + reslen - rescnt; + } + if (sign) { + if (fill != ' ') + *res++ = sign; + rescnt--; + if (width > len) + width--; + } + if ((flags & F_ALT) && (c == 'x' || c == 'X')) { + assert(pbuf[0] == '0'); + assert(pbuf[1] == c); + if (fill != ' ') { + *res++ = *pbuf++; + *res++ = *pbuf++; + } + rescnt -= 2; + width -= 2; + if (width < 0) + width = 0; + len -= 2; + } + if (width > len && !(flags & F_LJUST)) { + do { + --rescnt; + *res++ = fill; + } while (--width > len); + } + if (fill == ' ') { + if (sign) + *res++ = sign; + if ((flags & F_ALT) && (c == 'x' || c == 'X')) { + assert(pbuf[0] == '0'); + assert(pbuf[1] == c); + *res++ = *pbuf++; + *res++ = *pbuf++; + } + } + Py_UNICODE_COPY(res, pbuf, len); + res += len; + rescnt -= len; + while (--width >= len) { + --rescnt; + *res++ = ' '; + } + if (dict && (argidx < arglen) && c != '%') { + PyErr_SetString(PyExc_TypeError, + "not all arguments converted during string formatting"); + goto onError; + } + Py_XDECREF(temp); + } /* '%' */ + } /* until end */ + if (argidx < arglen && !dict) { + PyErr_SetString(PyExc_TypeError, + "not all arguments converted during string formatting"); + goto onError; + } + + if (args_owned) { + Py_DECREF(args); + } + Py_DECREF(uformat); + if (_PyUnicode_Resize(&result, reslen - rescnt)) + goto onError; + return (PyObject *)result; + + onError: + Py_XDECREF(result); + Py_DECREF(uformat); + if (args_owned) { + Py_DECREF(args); + } + return NULL; +} + +static PyBufferProcs unicode_as_buffer = { + (getreadbufferproc) unicode_buffer_getreadbuf, + (getwritebufferproc) unicode_buffer_getwritebuf, + (getsegcountproc) unicode_buffer_getsegcount, + (getcharbufferproc) unicode_buffer_getcharbuf, +}; + +static PyObject * +unicode_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + +static PyObject * +unicode_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *x = NULL; + static char *kwlist[] = {"string", "encoding", "errors", 0}; + char *encoding = NULL; + char *errors = NULL; + + if (type != &PyUnicode_Type) + return unicode_subtype_new(type, args, kwds); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oss:unicode", + kwlist, &x, &encoding, &errors)) + return NULL; + if (x == NULL) + return (PyObject *)_PyUnicode_New(0); + if (encoding == NULL && errors == NULL) + return PyObject_Unicode(x); + else + return PyUnicode_FromEncodedObject(x, encoding, errors); +} + +static PyObject * +unicode_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyUnicodeObject *tmp, *pnew; + int n; + + assert(PyType_IsSubtype(type, &PyUnicode_Type)); + tmp = (PyUnicodeObject *)unicode_new(&PyUnicode_Type, args, kwds); + if (tmp == NULL) + return NULL; + assert(PyUnicode_Check(tmp)); + pnew = (PyUnicodeObject *) type->tp_alloc(type, n = tmp->length); + if (pnew == NULL) { + Py_DECREF(tmp); + return NULL; + } + pnew->str = PyMem_NEW(Py_UNICODE, n+1); + if (pnew->str == NULL) { + _Py_ForgetReference((PyObject *)pnew); + PyObject_Del(pnew); + Py_DECREF(tmp); + return PyErr_NoMemory(); + } + Py_UNICODE_COPY(pnew->str, tmp->str, n+1); + pnew->length = n; + pnew->hash = tmp->hash; + Py_DECREF(tmp); + return (PyObject *)pnew; +} + +PyDoc_STRVAR(unicode_doc, +"unicode(string [, encoding[, errors]]) -> object\n\ +\n\ +Create a new Unicode object from the given encoded string.\n\ +encoding defaults to the current default string encoding.\n\ +errors can be 'strict', 'replace' or 'ignore' and defaults to 'strict'."); + +PyTypeObject PyUnicode_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "unicode", /* tp_name */ + sizeof(PyUnicodeObject), /* tp_size */ + 0, /* tp_itemsize */ + /* Slots */ + (destructor)unicode_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc) unicode_compare, /* tp_compare */ + (reprfunc) unicode_repr, /* tp_repr */ + &unicode_as_number, /* tp_as_number */ + &unicode_as_sequence, /* tp_as_sequence */ + &unicode_as_mapping, /* tp_as_mapping */ + (hashfunc) unicode_hash, /* tp_hash*/ + 0, /* tp_call*/ + (reprfunc) unicode_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + &unicode_as_buffer, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | + Py_TPFLAGS_BASETYPE, /* tp_flags */ + unicode_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + unicode_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &PyBaseString_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + unicode_new, /* tp_new */ + PyObject_Del, /* tp_free */ +}; + +/* Initialize the Unicode implementation */ + +void _PyUnicode_Init(void) +{ + int i; + + /* Init the implementation */ + unicode_freelist = NULL; + unicode_freelist_size = 0; + unicode_empty = _PyUnicode_New(0); + strcpy(unicode_default_encoding, "ascii"); + for (i = 0; i < 256; i++) + unicode_latin1[i] = NULL; + if (PyType_Ready(&PyUnicode_Type) < 0) + Py_FatalError("Can't initialize 'unicode'"); +} + +/* Finalize the Unicode implementation */ + +void +_PyUnicode_Fini(void) +{ + PyUnicodeObject *u; + int i; + + Py_XDECREF(unicode_empty); + unicode_empty = NULL; + + for (i = 0; i < 256; i++) { + if (unicode_latin1[i]) { + Py_DECREF(unicode_latin1[i]); + unicode_latin1[i] = NULL; + } + } + + for (u = unicode_freelist; u != NULL;) { + PyUnicodeObject *v = u; + u = *(PyUnicodeObject **)u; + if (v->str) + PyMem_DEL(v->str); + Py_XDECREF(v->defenc); + PyObject_Del(v); + } + unicode_freelist = NULL; + unicode_freelist_size = 0; +} + +/* +Local variables: +c-basic-offset: 4 +indent-tabs-mode: nil +End: +*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodetype_db.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodetype_db.h new file mode 100644 index 00000000..0e72ae27 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/unicodetype_db.h @@ -0,0 +1,1090 @@ +/* this file was generated by Tools/unicode/makeunicodedata.py 2.2 */ + +/* a list of unique character type descriptors */ +const _PyUnicode_TypeRecord _PyUnicode_TypeRecords[] = { + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}, + {32, 0, 0, 0, 0, 0}, + {48, 0, 0, 0, 0, 0}, + {6, 0, 0, 0, 0, 0}, + {6, 0, 0, 0, 1, 1}, + {6, 0, 0, 0, 2, 2}, + {6, 0, 0, 0, 3, 3}, + {6, 0, 0, 0, 4, 4}, + {6, 0, 0, 0, 5, 5}, + {6, 0, 0, 0, 6, 6}, + {6, 0, 0, 0, 7, 7}, + {6, 0, 0, 0, 8, 8}, + {6, 0, 0, 0, 9, 9}, + {129, 0, 32, 0, 0, 0}, + {9, 65504, 0, 65504, 0, 0}, + {9, 0, 0, 0, 0, 0}, + {9, 743, 0, 743, 0, 0}, + {9, 121, 0, 121, 0, 0}, + {129, 0, 1, 0, 0, 0}, + {9, 65535, 0, 65535, 0, 0}, + {129, 0, 65337, 0, 0, 0}, + {9, 65304, 0, 65304, 0, 0}, + {129, 0, 65415, 0, 0, 0}, + {9, 65236, 0, 65236, 0, 0}, + {129, 0, 210, 0, 0, 0}, + {129, 0, 206, 0, 0, 0}, + {129, 0, 205, 0, 0, 0}, + {129, 0, 79, 0, 0, 0}, + {129, 0, 202, 0, 0, 0}, + {129, 0, 203, 0, 0, 0}, + {129, 0, 207, 0, 0, 0}, + {9, 97, 0, 97, 0, 0}, + {129, 0, 211, 0, 0, 0}, + {129, 0, 209, 0, 0, 0}, + {129, 0, 213, 0, 0, 0}, + {9, 130, 0, 130, 0, 0}, + {129, 0, 214, 0, 0, 0}, + {129, 0, 218, 0, 0, 0}, + {129, 0, 217, 0, 0, 0}, + {129, 0, 219, 0, 0, 0}, + {1, 0, 0, 0, 0, 0}, + {9, 56, 0, 56, 0, 0}, + {129, 0, 2, 1, 0, 0}, + {65, 65535, 1, 0, 0, 0}, + {9, 65534, 0, 65535, 0, 0}, + {9, 65457, 0, 65457, 0, 0}, + {129, 0, 65439, 0, 0, 0}, + {129, 0, 65480, 0, 0, 0}, + {129, 0, 65406, 0, 0, 0}, + {9, 65326, 0, 65326, 0, 0}, + {9, 65330, 0, 65330, 0, 0}, + {9, 65331, 0, 65331, 0, 0}, + {9, 65334, 0, 65334, 0, 0}, + {9, 65333, 0, 65333, 0, 0}, + {9, 65329, 0, 65329, 0, 0}, + {9, 65327, 0, 65327, 0, 0}, + {9, 65325, 0, 65325, 0, 0}, + {9, 65323, 0, 65323, 0, 0}, + {9, 65322, 0, 65322, 0, 0}, + {9, 65318, 0, 65318, 0, 0}, + {9, 65319, 0, 65319, 0, 0}, + {9, 65317, 0, 65317, 0, 0}, + {0, 84, 0, 84, 0, 0}, + {129, 0, 38, 0, 0, 0}, + {129, 0, 37, 0, 0, 0}, + {129, 0, 64, 0, 0, 0}, + {129, 0, 63, 0, 0, 0}, + {9, 65498, 0, 65498, 0, 0}, + {9, 65499, 0, 65499, 0, 0}, + {9, 65505, 0, 65505, 0, 0}, + {9, 65472, 0, 65472, 0, 0}, + {9, 65473, 0, 65473, 0, 0}, + {9, 65474, 0, 65474, 0, 0}, + {9, 65479, 0, 65479, 0, 0}, + {129, 0, 0, 0, 0, 0}, + {9, 65489, 0, 65489, 0, 0}, + {9, 65482, 0, 65482, 0, 0}, + {9, 65450, 0, 65450, 0, 0}, + {9, 65456, 0, 65456, 0, 0}, + {129, 0, 65476, 0, 0, 0}, + {9, 65440, 0, 65440, 0, 0}, + {129, 0, 80, 0, 0, 0}, + {129, 0, 48, 0, 0, 0}, + {9, 65488, 0, 65488, 0, 0}, + {9, 65477, 0, 65477, 0, 0}, + {9, 8, 0, 8, 0, 0}, + {129, 0, 65528, 0, 0, 0}, + {9, 74, 0, 74, 0, 0}, + {9, 86, 0, 86, 0, 0}, + {9, 100, 0, 100, 0, 0}, + {9, 128, 0, 128, 0, 0}, + {9, 112, 0, 112, 0, 0}, + {9, 126, 0, 126, 0, 0}, + {65, 0, 65528, 0, 0, 0}, + {9, 9, 0, 9, 0, 0}, + {129, 0, 65462, 0, 0, 0}, + {65, 0, 65527, 0, 0, 0}, + {9, 58331, 0, 58331, 0, 0}, + {129, 0, 65450, 0, 0, 0}, + {129, 0, 65436, 0, 0, 0}, + {9, 7, 0, 7, 0, 0}, + {129, 0, 65424, 0, 0, 0}, + {129, 0, 65529, 0, 0, 0}, + {129, 0, 65408, 0, 0, 0}, + {129, 0, 65410, 0, 0, 0}, + {129, 0, 58019, 0, 0, 0}, + {129, 0, 57153, 0, 0, 0}, + {129, 0, 57274, 0, 0, 0}, + {0, 0, 16, 0, 0, 0}, + {0, 65520, 0, 65520, 0, 0}, + {4, 0, 0, 0, 0, 1}, + {4, 0, 0, 0, 0, 2}, + {4, 0, 0, 0, 0, 3}, + {4, 0, 0, 0, 0, 4}, + {4, 0, 0, 0, 0, 5}, + {4, 0, 0, 0, 0, 6}, + {4, 0, 0, 0, 0, 7}, + {4, 0, 0, 0, 0, 8}, + {4, 0, 0, 0, 0, 9}, + {0, 0, 26, 0, 0, 0}, + {0, 65510, 0, 65510, 0, 0}, + {4, 0, 0, 0, 0, 0}, + {129, 0, 40, 0, 0, 0}, + {9, 65496, 0, 65496, 0, 0}, +}; + +/* type indexes */ +#define SHIFT 8 +static unsigned char index1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 8, 8, 8, 8, 8, 25, 26, 27, 28, 29, 30, 31, 29, 32, 33, + 29, 29, 29, 8, 8, 8, 34, 35, 36, 37, 38, 39, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 40, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 41, 21, 21, 21, 21, 42, 8, 8, 8, + 8, 8, 8, 8, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 43, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 21, 44, 45, 21, 46, 47, 48, 8, 8, 8, 49, + 50, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 51, 52, 8, 8, 53, 54, 55, 56, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 57, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 21, 21, 58, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 59, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 60, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 60, +}; + +static unsigned char index2[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 1, 1, 1, 1, 1, 1, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 1, 1, 1, 1, 1, 1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 16, 1, 1, 1, + 1, 1, 1, 1, 6, 7, 1, 17, 1, 1, 1, 5, 16, 1, 1, 1, 1, 1, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 1, 14, 14, 14, 14, 14, 14, 14, 16, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 1, 15, 15, + 15, 15, 15, 15, 15, 18, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 21, 22, 19, 20, 19, 20, 19, 20, 16, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 16, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 23, 19, 20, 19, 20, 19, 20, 24, 16, 25, 19, 20, 19, 20, 26, 19, 20, 27, + 27, 19, 20, 16, 28, 29, 30, 19, 20, 27, 31, 32, 33, 34, 19, 20, 16, 16, + 33, 35, 36, 37, 19, 20, 19, 20, 19, 20, 38, 19, 20, 38, 16, 16, 19, 20, + 38, 19, 20, 39, 39, 19, 20, 19, 20, 40, 19, 20, 16, 41, 19, 20, 16, 42, + 41, 41, 41, 41, 43, 44, 45, 43, 44, 45, 43, 44, 45, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 46, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 16, 43, 44, 45, 19, 20, + 47, 48, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 49, 0, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 16, 16, 50, 51, 16, 52, 52, + 16, 53, 16, 54, 16, 16, 16, 16, 52, 16, 16, 55, 16, 16, 16, 16, 56, 57, + 16, 16, 16, 16, 16, 57, 16, 16, 58, 16, 16, 59, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 60, 16, 16, 60, 16, 16, 16, 16, 60, 16, 61, 61, 16, 16, + 16, 16, 16, 16, 62, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 0, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 1, 1, 41, 41, 41, 41, 41, 41, 41, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 41, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 41, 41, 41, 41, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 63, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 41, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, + 1, 64, 1, 65, 65, 65, 0, 66, 0, 67, 67, 16, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 68, 69, 69, 69, 16, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 70, 15, 15, 15, 15, 15, 15, 15, 15, 15, 71, 72, + 72, 0, 73, 74, 75, 75, 75, 76, 77, 16, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 78, 79, + 46, 16, 80, 81, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 1, 1, 1, 1, + 1, 0, 1, 1, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 75, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 0, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 0, 0, 19, 20, 0, 0, 0, 0, 0, 0, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 0, 0, 41, 1, 1, 1, 1, 1, 1, 0, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 16, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 0, 0, 0, 0, 0, 41, 41, 41, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 1, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 1, + 1, 1, 41, 41, 1, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 1, 41, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 41, 41, 1, 1, 1, 1, 1, 1, 1, 0, 0, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 41, 41, 41, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 1, 41, 1, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 41, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 0, 0, 1, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 41, 1, + 1, 1, 1, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 1, 1, 1, 1, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 0, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 41, 41, 0, 0, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 0, 41, 0, 0, 0, 41, 41, + 41, 41, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 41, 41, 0, 41, 41, 41, 1, 1, 0, 0, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 41, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, 41, 41, 0, 0, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 0, 41, 41, 0, + 41, 41, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 0, 41, 0, 0, 0, 0, 0, 0, 0, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 1, 41, 41, 41, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 0, 41, 41, 41, 41, 41, 41, 41, 0, 41, 0, 41, 41, 41, + 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 0, 41, 41, + 41, 41, 41, 0, 0, 1, 41, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, + 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 0, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 41, 41, 0, + 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 0, 0, 41, + 41, 41, 41, 0, 0, 1, 41, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 41, 41, 0, 41, 41, 41, 0, 0, 0, + 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 41, 0, 41, 41, 41, 41, 41, 41, 0, 0, 0, 41, 41, 41, + 0, 41, 41, 41, 41, 0, 0, 0, 41, 41, 0, 41, 0, 41, 41, 0, 0, 0, 41, 41, 0, + 0, 0, 41, 41, 41, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 0, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 0, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 0, 0, 0, 0, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 0, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 41, 0, 41, 41, 0, 0, 0, 0, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 0, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 0, 0, 0, 0, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 0, 41, 0, 0, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, 1, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 1, 41, + 41, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 41, 41, 41, 41, 41, 41, 41, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 0, 41, 0, 0, 41, 41, 0, 41, 0, 0, 41, + 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 0, 41, + 41, 41, 0, 41, 0, 41, 0, 0, 41, 41, 0, 41, 41, 41, 41, 1, 41, 41, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 41, 0, 0, 41, 41, 41, 41, 41, 0, 41, 0, 1, 1, 1, 1, + 1, 1, 0, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0, 41, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 41, + 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 41, 41, 41, 41, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 0, 41, 41, 0, 1, + 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 1, 1, 1, 1, 1, 1, 41, 41, 41, 41, 41, 41, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 1, 0, 0, 0, 0, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, + 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, + 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, 41, 41, 41, + 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 0, 41, 41, + 41, 41, 0, 0, 41, 41, 41, 41, 41, 41, 41, 0, 41, 0, 41, 41, 41, 41, 0, 0, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 0, 41, 0, 41, 41, 41, 41, 0, 0, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 0, 41, 0, 41, 41, 41, 41, 0, 0, 41, 41, 41, 41, + 41, 41, 41, 0, 41, 0, 41, 41, 41, 41, 0, 0, 41, 41, 41, 41, 41, 41, 41, + 0, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 0, 41, 41, 41, 41, 0, 0, 41, + 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 1, 1, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 1, 1, 0, 0, 0, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 0, 41, 41, 41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 41, 1, 1, 1, 1, 41, 0, 0, 0, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, + 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 16, + 16, 16, 16, 16, 85, 0, 0, 0, 0, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, 19, 20, + 19, 20, 19, 20, 19, 20, 19, 20, 0, 0, 0, 0, 0, 0, 86, 86, 86, 86, 86, 86, + 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 86, 86, 86, 86, 86, 86, 0, 0, 87, + 87, 87, 87, 87, 87, 0, 0, 86, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, + 87, 87, 87, 87, 86, 86, 86, 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, + 87, 87, 86, 86, 86, 86, 86, 86, 0, 0, 87, 87, 87, 87, 87, 87, 0, 0, 16, + 86, 16, 86, 16, 86, 16, 86, 0, 87, 0, 87, 0, 87, 0, 87, 86, 86, 86, 86, + 86, 86, 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 88, 88, 89, 89, 89, 89, + 90, 90, 91, 91, 92, 92, 93, 93, 0, 0, 86, 86, 86, 86, 86, 86, 86, 86, 94, + 94, 94, 94, 94, 94, 94, 94, 86, 86, 86, 86, 86, 86, 86, 86, 94, 94, 94, + 94, 94, 94, 94, 94, 86, 86, 86, 86, 86, 86, 86, 86, 94, 94, 94, 94, 94, + 94, 94, 94, 86, 86, 16, 95, 16, 0, 16, 16, 87, 87, 96, 96, 97, 1, 98, 1, + 1, 1, 16, 95, 16, 0, 16, 16, 99, 99, 99, 99, 97, 1, 1, 1, 86, 86, 16, 16, + 0, 0, 16, 16, 87, 87, 100, 100, 0, 1, 1, 1, 86, 86, 16, 16, 16, 101, 16, + 16, 87, 87, 102, 102, 103, 1, 1, 1, 0, 0, 16, 95, 16, 0, 16, 16, 104, + 104, 105, 105, 97, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 3, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 4, 16, 0, 0, 8, 9, 10, 11, 12, 13, 1, 1, 1, 1, 1, 16, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 75, 1, 1, 1, 1, 75, 1, 1, 16, 75, 75, 75, 16, 16, 75, + 75, 75, 16, 1, 75, 1, 1, 1, 75, 75, 75, 75, 75, 1, 1, 1, 1, 1, 1, 75, 1, + 106, 1, 75, 1, 107, 108, 75, 75, 1, 16, 75, 75, 1, 75, 16, 41, 41, 41, + 41, 16, 1, 0, 0, 16, 75, 75, 1, 1, 1, 1, 1, 75, 16, 16, 16, 16, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 109, 109, 109, + 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 122, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 111, 112, 113, 114, 115, 116, 117, 118, 119, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, + 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 1, 111, 112, 113, 114, 115, 116, 117, 118, 119, 1, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 2, 1, 1, 1, 1, 41, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 41, 41, 41, 41, 41, 1, 1, 1, 1, 1, 41, 41, 1, 1, + 1, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 1, 1, + 1, 1, 41, 41, 41, 1, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 1, 41, 41, 41, 41, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, + 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 0, 0, 0, 0, 0, + 41, 1, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 1, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 0, 41, 0, 41, 41, 0, + 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, + 41, 41, 41, 41, 41, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 1, 1, 1, 1, 1, 1, 1, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 1, 1, 1, 1, + 1, 1, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, + 41, 41, 41, 41, 41, 41, 0, 0, 41, 41, 41, 41, 41, 41, 0, 0, 41, 41, 41, + 41, 41, 41, 0, 0, 41, 41, 41, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 0, + 0, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 16, 16, 16, 16, 16, + 16, 16, 0, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 75, 0, 75, 75, 0, 0, 75, 0, 0, 75, 75, 0, 0, 75, 75, 75, 75, 0, 75, + 75, 75, 75, 75, 75, 75, 75, 16, 16, 16, 16, 0, 16, 0, 16, 16, 16, 16, 0, + 16, 16, 0, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 75, 75, 0, 75, 75, 75, + 75, 0, 0, 75, 75, 75, 75, 75, 75, 75, 75, 0, 75, 75, 75, 75, 75, 75, 75, + 0, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 75, 75, 0, 75, 75, 75, 75, 0, 75, 75, + 75, 75, 75, 0, 75, 0, 0, 0, 75, 75, 75, 75, 75, 75, 75, 0, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 0, 0, 0, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 1, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 1, 16, 16, 16, 16, 16, 16, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 1, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 1, 16, 16, + 16, 16, 16, 16, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 1, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 1, 16, 16, 16, 16, 16, 16, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 1, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 1, 16, 16, 16, 16, 16, 16, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 1, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 1, 16, 16, 16, 16, 16, 16, 0, 0, 0, + 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/weakrefobject.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/weakrefobject.c new file mode 100644 index 00000000..bae98cc9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Objects/weakrefobject.c @@ -0,0 +1,783 @@ +#include "Python.h" +#include "structmember.h" + + +#define GET_WEAKREFS_LISTPTR(o) \ + ((PyWeakReference **) PyObject_GET_WEAKREFS_LISTPTR(o)) + + +long +_PyWeakref_GetWeakrefCount(PyWeakReference *head) +{ + long count = 0; + + while (head != NULL) { + ++count; + head = head->wr_next; + } + return count; +} + + +static PyWeakReference * +new_weakref(PyObject *ob, PyObject *callback) +{ + PyWeakReference *result; + + result = PyObject_GC_New(PyWeakReference, &_PyWeakref_RefType); + if (result) { + result->hash = -1; + result->wr_object = ob; + Py_XINCREF(callback); + result->wr_callback = callback; + PyObject_GC_Track(result); + } + return result; +} + + +/* This function clears the passed-in reference and removes it from the + * list of weak references for the referent. This is the only code that + * removes an item from the doubly-linked list of weak references for an + * object; it is also responsible for clearing the callback slot. + */ +static void +clear_weakref(PyWeakReference *self) +{ + PyObject *callback = self->wr_callback; + + if (PyWeakref_GET_OBJECT(self) != Py_None) { + PyWeakReference **list = GET_WEAKREFS_LISTPTR( + PyWeakref_GET_OBJECT(self)); + + if (*list == self) + *list = self->wr_next; + self->wr_object = Py_None; + if (self->wr_prev != NULL) + self->wr_prev->wr_next = self->wr_next; + if (self->wr_next != NULL) + self->wr_next->wr_prev = self->wr_prev; + self->wr_prev = NULL; + self->wr_next = NULL; + } + if (callback != NULL) { + Py_DECREF(callback); + self->wr_callback = NULL; + } +} + +/* Cyclic gc uses this to *just* clear the passed-in reference, leaving + * the callback intact and uncalled. It must be possible to call self's + * tp_dealloc() after calling this, so self has to be left in a sane enough + * state for that to work. We expect tp_dealloc to decref the callback + * then. The reason for not letting clear_weakref() decref the callback + * right now is that if the callback goes away, that may in turn trigger + * another callback (if a weak reference to the callback exists) -- running + * arbitrary Python code in the middle of gc is a disaster. The convolution + * here allows gc to delay triggering such callbacks until the world is in + * a sane state again. + */ +void +_PyWeakref_ClearRef(PyWeakReference *self) +{ + PyObject *callback; + + assert(self != NULL); + assert(PyWeakref_Check(self)); + /* Preserve and restore the callback around clear_weakref. */ + callback = self->wr_callback; + self->wr_callback = NULL; + clear_weakref(self); + self->wr_callback = callback; +} + +static void +weakref_dealloc(PyWeakReference *self) +{ + PyObject_GC_UnTrack((PyObject *)self); + clear_weakref(self); + PyObject_GC_Del(self); +} + + +static int +gc_traverse(PyWeakReference *self, visitproc visit, void *arg) +{ + if (self->wr_callback != NULL) + return visit(self->wr_callback, arg); + return 0; +} + + +static int +gc_clear(PyWeakReference *self) +{ + clear_weakref(self); + return 0; +} + + +static PyObject * +weakref_call(PyWeakReference *self, PyObject *args, PyObject *kw) +{ + static char *argnames[] = {NULL}; + + if (PyArg_ParseTupleAndKeywords(args, kw, ":__call__", argnames)) { + PyObject *object = PyWeakref_GET_OBJECT(self); + Py_INCREF(object); + return (object); + } + return NULL; +} + + +static long +weakref_hash(PyWeakReference *self) +{ + if (self->hash != -1) + return self->hash; + if (PyWeakref_GET_OBJECT(self) == Py_None) { + PyErr_SetString(PyExc_TypeError, "weak object has gone away"); + return -1; + } + self->hash = PyObject_Hash(PyWeakref_GET_OBJECT(self)); + return self->hash; +} + + +static PyObject * +weakref_repr(PyWeakReference *self) +{ + char buffer[256]; + if (PyWeakref_GET_OBJECT(self) == Py_None) { + PyOS_snprintf(buffer, sizeof(buffer), "", self); + } + else { + char *name = NULL; + PyObject *nameobj = PyObject_GetAttrString(PyWeakref_GET_OBJECT(self), + "__name__"); + if (nameobj == NULL) + PyErr_Clear(); + else if (PyString_Check(nameobj)) + name = PyString_AS_STRING(nameobj); + PyOS_snprintf(buffer, sizeof(buffer), + name ? "" + : "", + self, + PyWeakref_GET_OBJECT(self)->ob_type->tp_name, + PyWeakref_GET_OBJECT(self), + name); + Py_XDECREF(nameobj); + } + return PyString_FromString(buffer); +} + +/* Weak references only support equality, not ordering. Two weak references + are equal if the underlying objects are equal. If the underlying object has + gone away, they are equal if they are identical. */ + +static PyObject * +weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op) +{ + if (op != Py_EQ || self->ob_type != other->ob_type) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + if (PyWeakref_GET_OBJECT(self) == Py_None + || PyWeakref_GET_OBJECT(other) == Py_None) { + PyObject *res = self==other ? Py_True : Py_False; + Py_INCREF(res); + return res; + } + return PyObject_RichCompare(PyWeakref_GET_OBJECT(self), + PyWeakref_GET_OBJECT(other), op); +} + + +PyTypeObject +_PyWeakref_RefType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "weakref", + sizeof(PyWeakReference), + 0, + (destructor)weakref_dealloc,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)weakref_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + (hashfunc)weakref_hash, /*tp_hash*/ + (ternaryfunc)weakref_call, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_RICHCOMPARE, + 0, /*tp_doc*/ + (traverseproc)gc_traverse, /*tp_traverse*/ + (inquiry)gc_clear, /*tp_clear*/ + (richcmpfunc)weakref_richcompare, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ +}; + + +static int +proxy_checkref(PyWeakReference *proxy) +{ + if (PyWeakref_GET_OBJECT(proxy) == Py_None) { + PyErr_SetString(PyExc_ReferenceError, + "weakly-referenced object no longer exists"); + return 0; + } + return 1; +} + + +/* If a parameter is a proxy, check that it is still "live" and wrap it, + * replacing the original value with the raw object. Raises ReferenceError + * if the param is a dead proxy. + */ +#define UNWRAP(o) \ + if (PyWeakref_CheckProxy(o)) { \ + if (!proxy_checkref((PyWeakReference *)o)) \ + return NULL; \ + o = PyWeakref_GET_OBJECT(o); \ + } + +#define UNWRAP_I(o) \ + if (PyWeakref_CheckProxy(o)) { \ + if (!proxy_checkref((PyWeakReference *)o)) \ + return -1; \ + o = PyWeakref_GET_OBJECT(o); \ + } + +#define WRAP_UNARY(method, generic) \ + static PyObject * \ + method(PyObject *proxy) { \ + UNWRAP(proxy); \ + return generic(proxy); \ + } + +#define WRAP_BINARY(method, generic) \ + static PyObject * \ + method(PyObject *x, PyObject *y) { \ + UNWRAP(x); \ + UNWRAP(y); \ + return generic(x, y); \ + } + +/* Note that the third arg needs to be checked for NULL since the tp_call + * slot can receive NULL for this arg. + */ +#define WRAP_TERNARY(method, generic) \ + static PyObject * \ + method(PyObject *proxy, PyObject *v, PyObject *w) { \ + UNWRAP(proxy); \ + UNWRAP(v); \ + if (w != NULL) \ + UNWRAP(w); \ + return generic(proxy, v, w); \ + } + + +/* direct slots */ + +WRAP_BINARY(proxy_getattr, PyObject_GetAttr) +WRAP_UNARY(proxy_str, PyObject_Str) +WRAP_TERNARY(proxy_call, PyEval_CallObjectWithKeywords) + +static PyObject * +proxy_repr(PyWeakReference *proxy) +{ + char buf[160]; + PyOS_snprintf(buf, sizeof(buf), + "", proxy, + PyWeakref_GET_OBJECT(proxy)->ob_type->tp_name, + PyWeakref_GET_OBJECT(proxy)); + return PyString_FromString(buf); +} + + +static int +proxy_setattr(PyWeakReference *proxy, PyObject *name, PyObject *value) +{ + if (!proxy_checkref(proxy)) + return -1; + return PyObject_SetAttr(PyWeakref_GET_OBJECT(proxy), name, value); +} + +static int +proxy_compare(PyObject *proxy, PyObject *v) +{ + UNWRAP_I(proxy); + UNWRAP_I(v); + return PyObject_Compare(proxy, v); +} + +/* number slots */ +WRAP_BINARY(proxy_add, PyNumber_Add) +WRAP_BINARY(proxy_sub, PyNumber_Subtract) +WRAP_BINARY(proxy_mul, PyNumber_Multiply) +WRAP_BINARY(proxy_div, PyNumber_Divide) +WRAP_BINARY(proxy_mod, PyNumber_Remainder) +WRAP_BINARY(proxy_divmod, PyNumber_Divmod) +WRAP_TERNARY(proxy_pow, PyNumber_Power) +WRAP_UNARY(proxy_neg, PyNumber_Negative) +WRAP_UNARY(proxy_pos, PyNumber_Positive) +WRAP_UNARY(proxy_abs, PyNumber_Absolute) +WRAP_UNARY(proxy_invert, PyNumber_Invert) +WRAP_BINARY(proxy_lshift, PyNumber_Lshift) +WRAP_BINARY(proxy_rshift, PyNumber_Rshift) +WRAP_BINARY(proxy_and, PyNumber_And) +WRAP_BINARY(proxy_xor, PyNumber_Xor) +WRAP_BINARY(proxy_or, PyNumber_Or) +WRAP_UNARY(proxy_int, PyNumber_Int) +WRAP_UNARY(proxy_long, PyNumber_Long) +WRAP_UNARY(proxy_float, PyNumber_Float) +WRAP_BINARY(proxy_iadd, PyNumber_InPlaceAdd) +WRAP_BINARY(proxy_isub, PyNumber_InPlaceSubtract) +WRAP_BINARY(proxy_imul, PyNumber_InPlaceMultiply) +WRAP_BINARY(proxy_idiv, PyNumber_InPlaceDivide) +WRAP_BINARY(proxy_imod, PyNumber_InPlaceRemainder) +WRAP_TERNARY(proxy_ipow, PyNumber_InPlacePower) +WRAP_BINARY(proxy_ilshift, PyNumber_InPlaceLshift) +WRAP_BINARY(proxy_irshift, PyNumber_InPlaceRshift) +WRAP_BINARY(proxy_iand, PyNumber_InPlaceAnd) +WRAP_BINARY(proxy_ixor, PyNumber_InPlaceXor) +WRAP_BINARY(proxy_ior, PyNumber_InPlaceOr) + +static int +proxy_nonzero(PyWeakReference *proxy) +{ + PyObject *o = PyWeakref_GET_OBJECT(proxy); + if (!proxy_checkref(proxy)) + return 1; + if (o->ob_type->tp_as_number && + o->ob_type->tp_as_number->nb_nonzero) + return (*o->ob_type->tp_as_number->nb_nonzero)(o); + else + return 1; +} + +/* sequence slots */ + +static PyObject * +proxy_slice(PyWeakReference *proxy, int i, int j) +{ + if (!proxy_checkref(proxy)) + return NULL; + return PySequence_GetSlice(PyWeakref_GET_OBJECT(proxy), i, j); +} + +static int +proxy_ass_slice(PyWeakReference *proxy, int i, int j, PyObject *value) +{ + if (!proxy_checkref(proxy)) + return -1; + return PySequence_SetSlice(PyWeakref_GET_OBJECT(proxy), i, j, value); +} + +static int +proxy_contains(PyWeakReference *proxy, PyObject *value) +{ + if (!proxy_checkref(proxy)) + return -1; + return PySequence_Contains(PyWeakref_GET_OBJECT(proxy), value); +} + + +/* mapping slots */ + +static int +proxy_length(PyWeakReference *proxy) +{ + if (!proxy_checkref(proxy)) + return -1; + return PyObject_Length(PyWeakref_GET_OBJECT(proxy)); +} + +WRAP_BINARY(proxy_getitem, PyObject_GetItem) + +static int +proxy_setitem(PyWeakReference *proxy, PyObject *key, PyObject *value) +{ + if (!proxy_checkref(proxy)) + return -1; + + if (value == NULL) + return PyObject_DelItem(PyWeakref_GET_OBJECT(proxy), key); + else + return PyObject_SetItem(PyWeakref_GET_OBJECT(proxy), key, value); +} + +/* iterator slots */ + +static PyObject * +proxy_iter(PyWeakReference *proxy) +{ + if (!proxy_checkref(proxy)) + return NULL; + return PyObject_GetIter(PyWeakref_GET_OBJECT(proxy)); +} + +static PyObject * +proxy_iternext(PyWeakReference *proxy) +{ + if (!proxy_checkref(proxy)) + return NULL; + return PyIter_Next(PyWeakref_GET_OBJECT(proxy)); +} + + +static PyNumberMethods proxy_as_number = { + (binaryfunc)proxy_add, /*nb_add*/ + (binaryfunc)proxy_sub, /*nb_subtract*/ + (binaryfunc)proxy_mul, /*nb_multiply*/ + (binaryfunc)proxy_div, /*nb_divide*/ + (binaryfunc)proxy_mod, /*nb_remainder*/ + (binaryfunc)proxy_divmod, /*nb_divmod*/ + (ternaryfunc)proxy_pow, /*nb_power*/ + (unaryfunc)proxy_neg, /*nb_negative*/ + (unaryfunc)proxy_pos, /*nb_positive*/ + (unaryfunc)proxy_abs, /*nb_absolute*/ + (inquiry)proxy_nonzero, /*nb_nonzero*/ + (unaryfunc)proxy_invert, /*nb_invert*/ + (binaryfunc)proxy_lshift, /*nb_lshift*/ + (binaryfunc)proxy_rshift, /*nb_rshift*/ + (binaryfunc)proxy_and, /*nb_and*/ + (binaryfunc)proxy_xor, /*nb_xor*/ + (binaryfunc)proxy_or, /*nb_or*/ + (coercion)0, /*nb_coerce*/ + (unaryfunc)proxy_int, /*nb_int*/ + (unaryfunc)proxy_long, /*nb_long*/ + (unaryfunc)proxy_float, /*nb_float*/ + (unaryfunc)0, /*nb_oct*/ + (unaryfunc)0, /*nb_hex*/ + (binaryfunc)proxy_iadd, /*nb_inplace_add*/ + (binaryfunc)proxy_isub, /*nb_inplace_subtract*/ + (binaryfunc)proxy_imul, /*nb_inplace_multiply*/ + (binaryfunc)proxy_idiv, /*nb_inplace_divide*/ + (binaryfunc)proxy_imod, /*nb_inplace_remainder*/ + (ternaryfunc)proxy_ipow, /*nb_inplace_power*/ + (binaryfunc)proxy_ilshift, /*nb_inplace_lshift*/ + (binaryfunc)proxy_irshift, /*nb_inplace_rshift*/ + (binaryfunc)proxy_iand, /*nb_inplace_and*/ + (binaryfunc)proxy_ixor, /*nb_inplace_xor*/ + (binaryfunc)proxy_ior, /*nb_inplace_or*/ +}; + +static PySequenceMethods proxy_as_sequence = { + (inquiry)proxy_length, /*sq_length*/ + 0, /*sq_concat*/ + 0, /*sq_repeat*/ + 0, /*sq_item*/ + (intintargfunc)proxy_slice, /*sq_slice*/ + 0, /*sq_ass_item*/ + (intintobjargproc)proxy_ass_slice, /*sq_ass_slice*/ + (objobjproc)proxy_contains, /* sq_contains */ +}; + +static PyMappingMethods proxy_as_mapping = { + (inquiry)proxy_length, /*mp_length*/ + (binaryfunc)proxy_getitem, /*mp_subscript*/ + (objobjargproc)proxy_setitem, /*mp_ass_subscript*/ +}; + + +PyTypeObject +_PyWeakref_ProxyType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "weakproxy", + sizeof(PyWeakReference), + 0, + /* methods */ + (destructor)weakref_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + proxy_compare, /* tp_compare */ + (unaryfunc)proxy_repr, /* tp_repr */ + &proxy_as_number, /* tp_as_number */ + &proxy_as_sequence, /* tp_as_sequence */ + &proxy_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (unaryfunc)proxy_str, /* tp_str */ + (getattrofunc)proxy_getattr, /* tp_getattro */ + (setattrofunc)proxy_setattr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)gc_traverse, /* tp_traverse */ + (inquiry)gc_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)proxy_iter, /* tp_iter */ + (iternextfunc)proxy_iternext, /* tp_iternext */ +}; + + +PyTypeObject +_PyWeakref_CallableProxyType = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "weakcallableproxy", + sizeof(PyWeakReference), + 0, + /* methods */ + (destructor)weakref_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + proxy_compare, /* tp_compare */ + (unaryfunc)proxy_repr, /* tp_repr */ + &proxy_as_number, /* tp_as_number */ + &proxy_as_sequence, /* tp_as_sequence */ + &proxy_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + (ternaryfunc)proxy_call, /* tp_call */ + (unaryfunc)proxy_str, /* tp_str */ + (getattrofunc)proxy_getattr, /* tp_getattro */ + (setattrofunc)proxy_setattr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)gc_traverse, /* tp_traverse */ + (inquiry)gc_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)proxy_iter, /* tp_iter */ + (iternextfunc)proxy_iternext, /* tp_iternext */ +}; + + +/* Given the head of an object's list of weak references, extract the + * two callback-less refs (ref and proxy). Used to determine if the + * shared references exist and to determine the back link for newly + * inserted references. + */ +static void +get_basic_refs(PyWeakReference *head, + PyWeakReference **refp, PyWeakReference **proxyp) +{ + *refp = NULL; + *proxyp = NULL; + + if (head != NULL && head->wr_callback == NULL) { + if (head->ob_type == &_PyWeakref_RefType) { + *refp = head; + head = head->wr_next; + } + if (head != NULL && head->wr_callback == NULL) { + *proxyp = head; + head = head->wr_next; + } + } +} + +/* Insert 'newref' in the list after 'prev'. Both must be non-NULL. */ +static void +insert_after(PyWeakReference *newref, PyWeakReference *prev) +{ + newref->wr_prev = prev; + newref->wr_next = prev->wr_next; + if (prev->wr_next != NULL) + prev->wr_next->wr_prev = newref; + prev->wr_next = newref; +} + +/* Insert 'newref' at the head of the list; 'list' points to the variable + * that stores the head. + */ +static void +insert_head(PyWeakReference *newref, PyWeakReference **list) +{ + PyWeakReference *next = *list; + + newref->wr_prev = NULL; + newref->wr_next = next; + if (next != NULL) + next->wr_prev = newref; + *list = newref; +} + + +PyObject * +PyWeakref_NewRef(PyObject *ob, PyObject *callback) +{ + PyWeakReference *result = NULL; + PyWeakReference **list; + PyWeakReference *ref, *proxy; + + if (!PyType_SUPPORTS_WEAKREFS(ob->ob_type)) { + PyErr_Format(PyExc_TypeError, + "cannot create weak reference to '%s' object", + ob->ob_type->tp_name); + return NULL; + } + list = GET_WEAKREFS_LISTPTR(ob); + get_basic_refs(*list, &ref, &proxy); + if (callback == NULL || callback == Py_None) + /* return existing weak reference if it exists */ + result = ref; + if (result != NULL) + Py_XINCREF(result); + else { + result = new_weakref(ob, callback); + if (result != NULL) { + if (callback == NULL) { + insert_head(result, list); + } + else { + PyWeakReference *prev = (proxy == NULL) ? ref : proxy; + + if (prev == NULL) + insert_head(result, list); + else + insert_after(result, prev); + } + } + } + return (PyObject *) result; +} + + +PyObject * +PyWeakref_NewProxy(PyObject *ob, PyObject *callback) +{ + PyWeakReference *result = NULL; + PyWeakReference **list; + PyWeakReference *ref, *proxy; + + if (!PyType_SUPPORTS_WEAKREFS(ob->ob_type)) { + PyErr_Format(PyExc_TypeError, + "cannot create weak reference to '%s' object", + ob->ob_type->tp_name); + return NULL; + } + list = GET_WEAKREFS_LISTPTR(ob); + get_basic_refs(*list, &ref, &proxy); + if (callback == NULL) + /* attempt to return an existing weak reference if it exists */ + result = proxy; + if (result != NULL) + Py_XINCREF(result); + else { + result = new_weakref(ob, callback); + if (result != NULL) { + PyWeakReference *prev; + + if (PyCallable_Check(ob)) + result->ob_type = &_PyWeakref_CallableProxyType; + else + result->ob_type = &_PyWeakref_ProxyType; + if (callback == NULL) + prev = ref; + else + prev = (proxy == NULL) ? ref : proxy; + + if (prev == NULL) + insert_head(result, list); + else + insert_after(result, prev); + } + } + return (PyObject *) result; +} + + +PyObject * +PyWeakref_GetObject(PyObject *ref) +{ + if (ref == NULL || !PyWeakref_Check(ref)) { + PyErr_BadInternalCall(); + return NULL; + } + return PyWeakref_GET_OBJECT(ref); +} + + +static void +handle_callback(PyWeakReference *ref, PyObject *callback) +{ + PyObject *cbresult = PyObject_CallFunction(callback, "O", ref); + + if (cbresult == NULL) + PyErr_WriteUnraisable(callback); + else + Py_DECREF(cbresult); +} + +/* This function is called by the tp_dealloc handler to clear weak references. + * + * This iterates through the weak references for 'object' and calls callbacks + * for those references which have one. It returns when all callbacks have + * been attempted. + */ +void +PyObject_ClearWeakRefs(PyObject *object) +{ + PyWeakReference **list; + + if (object == NULL + || !PyType_SUPPORTS_WEAKREFS(object->ob_type) + || object->ob_refcnt != 0) { + PyErr_BadInternalCall(); + return; + } + list = GET_WEAKREFS_LISTPTR(object); + /* Remove the callback-less basic and proxy references */ + if (*list != NULL && (*list)->wr_callback == NULL) { + clear_weakref(*list); + if (*list != NULL && (*list)->wr_callback == NULL) + clear_weakref(*list); + } + if (*list != NULL) { + PyWeakReference *current = *list; + int count = _PyWeakref_GetWeakrefCount(current); + int restore_error = PyErr_Occurred() ? 1 : 0; + PyObject *err_type, *err_value, *err_tb; + + if (restore_error) + PyErr_Fetch(&err_type, &err_value, &err_tb); + if (count == 1) { + PyObject *callback = current->wr_callback; + + current->wr_callback = NULL; + clear_weakref(current); + handle_callback(current, callback); + Py_DECREF(callback); + } + else { + PyObject *tuple = PyTuple_New(count * 2); + int i = 0; + + for (i = 0; i < count; ++i) { + PyWeakReference *next = current->wr_next; + + Py_INCREF(current); + PyTuple_SET_ITEM(tuple, i * 2, (PyObject *) current); + PyTuple_SET_ITEM(tuple, i * 2 + 1, current->wr_callback); + current->wr_callback = NULL; + clear_weakref(current); + current = next; + } + for (i = 0; i < count; ++i) { + PyObject *current = PyTuple_GET_ITEM(tuple, i * 2); + PyObject *callback = PyTuple_GET_ITEM(tuple, i * 2 + 1); + + handle_callback((PyWeakReference *)current, callback); + } + Py_DECREF(tuple); + } + if (restore_error) + PyErr_Restore(err_type, err_value, err_tb); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/.cvsignore b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/.cvsignore new file mode 100644 index 00000000..34fd700b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/.cvsignore @@ -0,0 +1,2 @@ +pythonnt_rc.h +pythonnt_rc_d.h diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/PlasmaPack.cpp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/PlasmaPack.cpp new file mode 100644 index 00000000..c5fe8ca2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/PlasmaPack.cpp @@ -0,0 +1,17 @@ +// gateway to cpp code + + +#include "Python.h" +#include "plPythonPack.h" + +#include "PlasmaPack.h" + +PyObject* Pl_OpenPacked(const char* fileName) +{ + return PythonPack::OpenPythonPacked(fileName); +} + +int Pl_IsItPacked(const char* fileName) +{ + return PythonPack::IsItPythonPacked(fileName); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/PlasmaPack.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/PlasmaPack.h new file mode 100644 index 00000000..a13b00c8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/PlasmaPack.h @@ -0,0 +1,15 @@ +#ifndef PlasmaPack_h_inc +#define PlasmaPack_h_inc + +#ifdef __cplusplus +extern "C" { +#endif + +extern PyObject* Pl_OpenPacked(const char* fileName); +extern int Pl_IsItPacked(const char* fileName); + +#ifdef __cplusplus +} +#endif + +#endif // PlasmaPack_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/WinMain.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/WinMain.c new file mode 100644 index 00000000..c4ee86de --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/WinMain.c @@ -0,0 +1,16 @@ +/* Minimal main program -- everything is loaded from the library. */ + +#define WIN32_LEAN_AND_MEAN +#include + +#include "Python.h" + +int WINAPI WinMain( + HINSTANCE hInstance, /* handle to current instance */ + HINSTANCE hPrevInstance, /* handle to previous instance */ + LPSTR lpCmdLine, /* pointer to command line */ + int nCmdShow /* show state of window */ +) +{ + return Py_Main(__argc, __argv); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/_winreg.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/_winreg.c new file mode 100644 index 00000000..a0f7d085 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/_winreg.c @@ -0,0 +1,1526 @@ +/* + _winreg.c + + Windows Registry access module for Python. + + * Simple registry access written by Mark Hammond in win32api + module circa 1995. + * Bill Tutt expanded the support significantly not long after. + * Numerous other people have submitted patches since then. + * Ripped from win32api module 03-Feb-2000 by Mark Hammond, and + basic Unicode support added. + +*/ + +#include "windows.h" +#include "Python.h" +#include "structmember.h" +#include "malloc.h" /* for alloca */ + +static BOOL PyHKEY_AsHKEY(PyObject *ob, HKEY *pRes, BOOL bNoneOK); +static PyObject *PyHKEY_FromHKEY(HKEY h); +static BOOL PyHKEY_Close(PyObject *obHandle); + +static char errNotAHandle[] = "Object is not a handle"; + +/* The win32api module reports the function name that failed, + but this concept is not in the Python core. + Hopefully it will one day, and in the meantime I dont + want to lose this info... +*/ +#define PyErr_SetFromWindowsErrWithFunction(rc, fnname) \ + PyErr_SetFromWindowsErr(rc) + +/* Forward declares */ + +/* Doc strings */ +PyDoc_STRVAR(module_doc, +"This module provides access to the Windows registry API.\n" +"\n" +"Functions:\n" +"\n" +"CloseKey() - Closes a registry key.\n" +"ConnectRegistry() - Establishes a connection to a predefined registry handle\n" +" on another computer.\n" +"CreateKey() - Creates the specified key, or opens it if it already exists.\n" +"DeleteKey() - Deletes the specified key.\n" +"DeleteValue() - Removes a named value from the specified registry key.\n" +"EnumKey() - Enumerates subkeys of the specified open registry key.\n" +"EnumValue() - Enumerates values of the specified open registry key.\n" +"FlushKey() - Writes all the attributes of the specified key to the registry.\n" +"LoadKey() - Creates a subkey under HKEY_USER or HKEY_LOCAL_MACHINE and stores\n" +" registration information from a specified file into that subkey.\n" +"OpenKey() - Alias for \n" +"OpenKeyEx() - Opens the specified key.\n" +"QueryValue() - Retrieves the value associated with the unnamed value for a\n" +" specified key in the registry.\n" +"QueryValueEx() - Retrieves the type and data for a specified value name\n" +" associated with an open registry key.\n" +"QueryInfoKey() - Returns information about the specified key.\n" +"SaveKey() - Saves the specified key, and all its subkeys a file.\n" +"SetValue() - Associates a value with a specified key.\n" +"SetValueEx() - Stores data in the value field of an open registry key.\n" +"\n" +"Special objects:\n" +"\n" +"HKEYType -- type object for HKEY objects\n" +"error -- exception raised for Win32 errors\n" +"\n" +"Integer constants:\n" +"Many constants are defined - see the documentation for each function\n" +"to see what constants are used, and where."); + + +PyDoc_STRVAR(CloseKey_doc, +"CloseKey(hkey) - Closes a previously opened registry key.\n" +"\n" +"The hkey argument specifies a previously opened key.\n" +"\n" +"Note that if the key is not closed using this method, it will be\n" +"closed when the hkey object is destroyed by Python."); + +PyDoc_STRVAR(ConnectRegistry_doc, +"key = ConnectRegistry(computer_name, key) - " +"Establishes a connection to a predefined registry handle on another computer.\n" +"\n" +"computer_name is the name of the remote computer, of the form \\\\computername.\n" +" If None, the local computer is used.\n" +"key is the predefined handle to connect to.\n" +"\n" +"The return value is the handle of the opened key.\n" +"If the function fails, an EnvironmentError exception is raised."); + +PyDoc_STRVAR(CreateKey_doc, +"key = CreateKey(key, sub_key) - Creates or opens the specified key.\n" +"\n" +"key is an already open key, or one of the predefined HKEY_* constants\n" +"sub_key is a string that names the key this method opens or creates.\n" +" If key is one of the predefined keys, sub_key may be None. In that case,\n" +" the handle returned is the same key handle passed in to the function.\n" +"\n" +"If the key already exists, this function opens the existing key\n" +"\n" +"The return value is the handle of the opened key.\n" +"If the function fails, an exception is raised."); + +PyDoc_STRVAR(DeleteKey_doc, +"DeleteKey(key, sub_key) - Deletes the specified key.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"sub_key is a string that must be a subkey of the key identified by the key parameter.\n" +" This value must not be None, and the key may not have subkeys.\n" +"\n" +"This method can not delete keys with subkeys.\n" +"\n" +"If the method succeeds, the entire key, including all of its values,\n" +"is removed. If the method fails, an EnvironmentError exception is raised."); + +PyDoc_STRVAR(DeleteValue_doc, +"DeleteValue(key, value) - Removes a named value from a registry key.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"value is a string that identifies the value to remove."); + +PyDoc_STRVAR(EnumKey_doc, +"string = EnumKey(key, index) - Enumerates subkeys of an open registry key.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"index is an integer that identifies the index of the key to retrieve.\n" +"\n" +"The function retrieves the name of one subkey each time it is called.\n" +"It is typically called repeatedly until an EnvironmentError exception is\n" +"raised, indicating no more values are available."); + +PyDoc_STRVAR(EnumValue_doc, +"tuple = EnumValue(key, index) - Enumerates values of an open registry key.\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"index is an integer that identifies the index of the value to retrieve.\n" +"\n" +"The function retrieves the name of one subkey each time it is called.\n" +"It is typically called repeatedly, until an EnvironmentError exception\n" +"is raised, indicating no more values.\n" +"\n" +"The result is a tuple of 3 items:\n" +"value_name is a string that identifies the value.\n" +"value_data is an object that holds the value data, and whose type depends\n" +" on the underlying registry type.\n" +"data_type is an integer that identifies the type of the value data."); + +PyDoc_STRVAR(FlushKey_doc, +"FlushKey(key) - Writes all the attributes of a key to the registry.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"\n" +"It is not necessary to call RegFlushKey to change a key.\n" +"Registry changes are flushed to disk by the registry using its lazy flusher.\n" +"Registry changes are also flushed to disk at system shutdown.\n" +"Unlike CloseKey(), the FlushKey() method returns only when all the data has\n" +"been written to the registry.\n" +"An application should only call FlushKey() if it requires absolute certainty that registry changes are on disk.\n" +"If you don't know whether a FlushKey() call is required, it probably isn't."); + +PyDoc_STRVAR(LoadKey_doc, +"LoadKey(key, sub_key, file_name) - Creates a subkey under the specified key\n" +"and stores registration information from a specified file into that subkey.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"sub_key is a string that identifies the sub_key to load\n" +"file_name is the name of the file to load registry data from.\n" +" This file must have been created with the SaveKey() function.\n" +" Under the file allocation table (FAT) file system, the filename may not\n" +"have an extension.\n" +"\n" +"A call to LoadKey() fails if the calling process does not have the\n" +"SE_RESTORE_PRIVILEGE privilege.\n" +"\n" +"If key is a handle returned by ConnectRegistry(), then the path specified\n" +"in fileName is relative to the remote computer.\n" +"\n" +"The docs imply key must be in the HKEY_USER or HKEY_LOCAL_MACHINE tree"); + +PyDoc_STRVAR(OpenKey_doc, +"key = OpenKey(key, sub_key, res = 0, sam = KEY_READ) - Opens the specified key.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"sub_key is a string that identifies the sub_key to open\n" +"res is a reserved integer, and must be zero. Default is zero.\n" +"sam is an integer that specifies an access mask that describes the desired\n" +" security access for the key. Default is KEY_READ\n" +"\n" +"The result is a new handle to the specified key\n" +"If the function fails, an EnvironmentError exception is raised."); + +PyDoc_STRVAR(OpenKeyEx_doc, "See OpenKey()"); + +PyDoc_STRVAR(QueryInfoKey_doc, +"tuple = QueryInfoKey(key) - Returns information about a key.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"\n" +"The result is a tuple of 3 items:" +"An integer that identifies the number of sub keys this key has.\n" +"An integer that identifies the number of values this key has.\n" +"A long integer that identifies when the key was last modified (if available)\n" +" as 100's of nanoseconds since Jan 1, 1600."); + +PyDoc_STRVAR(QueryValue_doc, +"string = QueryValue(key, sub_key) - retrieves the unnamed value for a key.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"sub_key is a string that holds the name of the subkey with which the value\n" +" is associated. If this parameter is None or empty, the function retrieves\n" +" the value set by the SetValue() method for the key identified by key." +"\n" +"Values in the registry have name, type, and data components. This method\n" +"retrieves the data for a key's first value that has a NULL name.\n" +"But the underlying API call doesn't return the type, Lame Lame Lame, DONT USE THIS!!!"); + +PyDoc_STRVAR(QueryValueEx_doc, +"value,type_id = QueryValueEx(key, value_name) - Retrieves the type and data for a specified value name associated with an open registry key.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"value_name is a string indicating the value to query"); + +PyDoc_STRVAR(SaveKey_doc, +"SaveKey(key, file_name) - Saves the specified key, and all its subkeys to the specified file.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"file_name is the name of the file to save registry data to.\n" +" This file cannot already exist. If this filename includes an extension,\n" +" it cannot be used on file allocation table (FAT) file systems by the\n" +" LoadKey(), ReplaceKey() or RestoreKey() methods.\n" +"\n" +"If key represents a key on a remote computer, the path described by\n" +"file_name is relative to the remote computer.\n" +"The caller of this method must possess the SeBackupPrivilege security privilege.\n" +"This function passes NULL for security_attributes to the API."); + +PyDoc_STRVAR(SetValue_doc, +"SetValue(key, sub_key, type, value) - Associates a value with a specified key.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"sub_key is a string that names the subkey with which the value is associated.\n" +"type is an integer that specifies the type of the data. Currently this\n" +" must be REG_SZ, meaning only strings are supported.\n" +"value is a string that specifies the new value.\n" +"\n" +"If the key specified by the sub_key parameter does not exist, the SetValue\n" +"function creates it.\n" +"\n" +"Value lengths are limited by available memory. Long values (more than\n" +"2048 bytes) should be stored as files with the filenames stored in \n" +"the configuration registry. This helps the registry perform efficiently.\n" +"\n" +"The key identified by the key parameter must have been opened with\n" +"KEY_SET_VALUE access."); + +PyDoc_STRVAR(SetValueEx_doc, +"SetValueEx(key, value_name, reserved, type, value) - Stores data in the value field of an open registry key.\n" +"\n" +"key is an already open key, or any one of the predefined HKEY_* constants.\n" +"value_name is a string containing the name of the value to set, or None\n" +"type is an integer that specifies the type of the data. This should be one of:\n" +" REG_BINARY -- Binary data in any form.\n" +" REG_DWORD -- A 32-bit number.\n" +" REG_DWORD_LITTLE_ENDIAN -- A 32-bit number in little-endian format.\n" +" REG_DWORD_BIG_ENDIAN -- A 32-bit number in big-endian format.\n" +" REG_EXPAND_SZ -- A null-terminated string that contains unexpanded references\n" +" to environment variables (for example, %PATH%).\n" +" REG_LINK -- A Unicode symbolic link.\n" +" REG_MULTI_SZ -- An sequence of null-terminated strings, terminated by\n" +" two null characters. Note that Python handles this\n" +" termination automatically.\n" +" REG_NONE -- No defined value type.\n" +" REG_RESOURCE_LIST -- A device-driver resource list.\n" +" REG_SZ -- A null-terminated string.\n" +"reserved can be anything - zero is always passed to the API.\n" +"value is a string that specifies the new value.\n" +"\n" +"This method can also set additional value and type information for the\n" +"specified key. The key identified by the key parameter must have been\n" +"opened with KEY_SET_VALUE access.\n" +"\n" +"To open the key, use the CreateKeyEx() or OpenKeyEx() methods.\n" +"\n" +"Value lengths are limited by available memory. Long values (more than\n" +"2048 bytes) should be stored as files with the filenames stored in \n" +"the configuration registry. This helps the registry perform efficiently."); + +/* PyHKEY docstrings */ +PyDoc_STRVAR(PyHKEY_doc, +"PyHKEY Object - A Python object, representing a win32 registry key.\n" +"\n" +"This object wraps a Windows HKEY object, automatically closing it when\n" +"the object is destroyed. To guarantee cleanup, you can call either\n" +"the Close() method on the PyHKEY, or the CloseKey() method.\n" +"\n" +"All functions which accept a handle object also accept an integer - \n" +"however, use of the handle object is encouraged.\n" +"\n" +"Functions:\n" +"Close() - Closes the underlying handle.\n" +"Detach() - Returns the integer Win32 handle, detaching it from the object\n" +"\n" +"Properties:\n" +"handle - The integer Win32 handle.\n" +"\n" +"Operations:\n" +"__nonzero__ - Handles with an open object return true, otherwise false.\n" +"__int__ - Converting a handle to an integer returns the Win32 handle.\n" +"__cmp__ - Handle objects are compared using the handle value."); + + +PyDoc_STRVAR(PyHKEY_Close_doc, +"key.Close() - Closes the underlying Windows handle.\n" +"\n" +"If the handle is already closed, no error is raised."); + +PyDoc_STRVAR(PyHKEY_Detach_doc, +"int = key.Detach() - Detaches the Windows handle from the handle object.\n" +"\n" +"The result is the value of the handle before it is detached. If the\n" +"handle is already detached, this will return zero.\n" +"\n" +"After calling this function, the handle is effectively invalidated,\n" +"but the handle is not closed. You would call this function when you\n" +"need the underlying win32 handle to exist beyond the lifetime of the\n" +"handle object.\n" +"On 64 bit windows, the result of this function is a long integer"); + + +/************************************************************************ + + The PyHKEY object definition + +************************************************************************/ +typedef struct { + PyObject_VAR_HEAD + HKEY hkey; +} PyHKEYObject; + +#define PyHKEY_Check(op) ((op)->ob_type == &PyHKEY_Type) + +static char *failMsg = "bad operand type"; + +static PyObject * +PyHKEY_unaryFailureFunc(PyObject *ob) +{ + PyErr_SetString(PyExc_TypeError, failMsg); + return NULL; +} +static PyObject * +PyHKEY_binaryFailureFunc(PyObject *ob1, PyObject *ob2) +{ + PyErr_SetString(PyExc_TypeError, failMsg); + return NULL; +} +static PyObject * +PyHKEY_ternaryFailureFunc(PyObject *ob1, PyObject *ob2, PyObject *ob3) +{ + PyErr_SetString(PyExc_TypeError, failMsg); + return NULL; +} + +static void +PyHKEY_deallocFunc(PyObject *ob) +{ + /* Can not call PyHKEY_Close, as the ob->tp_type + has already been cleared, thus causing the type + check to fail! + */ + PyHKEYObject *obkey = (PyHKEYObject *)ob; + if (obkey->hkey) + RegCloseKey((HKEY)obkey->hkey); + PyObject_DEL(ob); +} + +static int +PyHKEY_nonzeroFunc(PyObject *ob) +{ + return ((PyHKEYObject *)ob)->hkey != 0; +} + +static PyObject * +PyHKEY_intFunc(PyObject *ob) +{ + PyHKEYObject *pyhkey = (PyHKEYObject *)ob; + return PyLong_FromVoidPtr(pyhkey->hkey); +} + +static int +PyHKEY_printFunc(PyObject *ob, FILE *fp, int flags) +{ + PyHKEYObject *pyhkey = (PyHKEYObject *)ob; + char resBuf[160]; + wsprintf(resBuf, "", + ob, pyhkey->hkey); + fputs(resBuf, fp); + return 0; +} + +static PyObject * +PyHKEY_strFunc(PyObject *ob) +{ + PyHKEYObject *pyhkey = (PyHKEYObject *)ob; + char resBuf[160]; + wsprintf(resBuf, "", pyhkey->hkey); + return PyString_FromString(resBuf); +} + +static int +PyHKEY_compareFunc(PyObject *ob1, PyObject *ob2) +{ + PyHKEYObject *pyhkey1 = (PyHKEYObject *)ob1; + PyHKEYObject *pyhkey2 = (PyHKEYObject *)ob2; + return pyhkey1 == pyhkey2 ? 0 : + (pyhkey1 < pyhkey2 ? -1 : 1); +} + +static long +PyHKEY_hashFunc(PyObject *ob) +{ + /* Just use the address. + XXX - should we use the handle value? + */ + return _Py_HashPointer(ob); +} + + +static PyNumberMethods PyHKEY_NumberMethods = +{ + PyHKEY_binaryFailureFunc, /* nb_add */ + PyHKEY_binaryFailureFunc, /* nb_subtract */ + PyHKEY_binaryFailureFunc, /* nb_multiply */ + PyHKEY_binaryFailureFunc, /* nb_divide */ + PyHKEY_binaryFailureFunc, /* nb_remainder */ + PyHKEY_binaryFailureFunc, /* nb_divmod */ + PyHKEY_ternaryFailureFunc, /* nb_power */ + PyHKEY_unaryFailureFunc, /* nb_negative */ + PyHKEY_unaryFailureFunc, /* nb_positive */ + PyHKEY_unaryFailureFunc, /* nb_absolute */ + PyHKEY_nonzeroFunc, /* nb_nonzero */ + PyHKEY_unaryFailureFunc, /* nb_invert */ + PyHKEY_binaryFailureFunc, /* nb_lshift */ + PyHKEY_binaryFailureFunc, /* nb_rshift */ + PyHKEY_binaryFailureFunc, /* nb_and */ + PyHKEY_binaryFailureFunc, /* nb_xor */ + PyHKEY_binaryFailureFunc, /* nb_or */ + 0, /* nb_coerce (allowed to be zero) */ + PyHKEY_intFunc, /* nb_int */ + PyHKEY_unaryFailureFunc, /* nb_long */ + PyHKEY_unaryFailureFunc, /* nb_float */ + PyHKEY_unaryFailureFunc, /* nb_oct */ + PyHKEY_unaryFailureFunc, /* nb_hex */ +}; + + +/* fwd declare __getattr__ */ +static PyObject *PyHKEY_getattr(PyObject *self, char *name); + +/* The type itself */ +PyTypeObject PyHKEY_Type = +{ + PyObject_HEAD_INIT(0) /* fill in type at module init */ + 0, + "PyHKEY", + sizeof(PyHKEYObject), + 0, + PyHKEY_deallocFunc, /* tp_dealloc */ + PyHKEY_printFunc, /* tp_print */ + PyHKEY_getattr, /* tp_getattr */ + 0, /* tp_setattr */ + PyHKEY_compareFunc, /* tp_compare */ + 0, /* tp_repr */ + &PyHKEY_NumberMethods, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + PyHKEY_hashFunc, /* tp_hash */ + 0, /* tp_call */ + PyHKEY_strFunc, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + 0, /* tp_flags */ + PyHKEY_doc, /* tp_doc */ +}; + +#define OFF(e) offsetof(PyHKEYObject, e) + +static struct memberlist PyHKEY_memberlist[] = { + {"handle", T_INT, OFF(hkey)}, + {NULL} /* Sentinel */ +}; + +/************************************************************************ + + The PyHKEY object methods + +************************************************************************/ +static PyObject * +PyHKEY_CloseMethod(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":Close")) + return NULL; + if (!PyHKEY_Close(self)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyHKEY_DetachMethod(PyObject *self, PyObject *args) +{ + void* ret; + PyHKEYObject *pThis = (PyHKEYObject *)self; + if (!PyArg_ParseTuple(args, ":Detach")) + return NULL; + ret = (void*)pThis->hkey; + pThis->hkey = 0; + return PyLong_FromVoidPtr(ret); +} + +static struct PyMethodDef PyHKEY_methods[] = { + {"Close", PyHKEY_CloseMethod, METH_VARARGS, PyHKEY_Close_doc}, + {"Detach", PyHKEY_DetachMethod, METH_VARARGS, PyHKEY_Detach_doc}, + {NULL} +}; + +/*static*/ PyObject * +PyHKEY_getattr(PyObject *self, char *name) +{ + PyObject *res; + + res = Py_FindMethod(PyHKEY_methods, self, name); + if (res != NULL) + return res; + PyErr_Clear(); + if (strcmp(name, "handle") == 0) + return PyLong_FromVoidPtr(((PyHKEYObject *)self)->hkey); + return PyMember_Get((char *)self, PyHKEY_memberlist, name); +} + +/************************************************************************ + The public PyHKEY API (well, not public yet :-) +************************************************************************/ +PyObject * +PyHKEY_New(HKEY hInit) +{ + PyHKEYObject *key = PyObject_NEW(PyHKEYObject, &PyHKEY_Type); + if (key) + key->hkey = hInit; + return (PyObject *)key; +} + +BOOL +PyHKEY_Close(PyObject *ob_handle) +{ + LONG rc; + PyHKEYObject *key; + + if (!PyHKEY_Check(ob_handle)) { + PyErr_SetString(PyExc_TypeError, "bad operand type"); + return FALSE; + } + key = (PyHKEYObject *)ob_handle; + rc = key->hkey ? RegCloseKey((HKEY)key->hkey) : ERROR_SUCCESS; + key->hkey = 0; + if (rc != ERROR_SUCCESS) + PyErr_SetFromWindowsErrWithFunction(rc, "RegCloseKey"); + return rc == ERROR_SUCCESS; +} + +BOOL +PyHKEY_AsHKEY(PyObject *ob, HKEY *pHANDLE, BOOL bNoneOK) +{ + if (ob == Py_None) { + if (!bNoneOK) { + PyErr_SetString( + PyExc_TypeError, + "None is not a valid HKEY in this context"); + return FALSE; + } + *pHANDLE = (HKEY)0; + } + else if (PyHKEY_Check(ob)) { + PyHKEYObject *pH = (PyHKEYObject *)ob; + *pHANDLE = pH->hkey; + } + else if (PyInt_Check(ob) || PyLong_Check(ob)) { + /* We also support integers */ + PyErr_Clear(); + *pHANDLE = (HKEY)PyLong_AsVoidPtr(ob); + if (PyErr_Occurred()) + return FALSE; + } + else { + PyErr_SetString( + PyExc_TypeError, + "The object is not a PyHKEY object"); + return FALSE; + } + return TRUE; +} + +PyObject * +PyHKEY_FromHKEY(HKEY h) +{ + PyHKEYObject *op; + + /* Inline PyObject_New */ + op = (PyHKEYObject *) PyObject_MALLOC(sizeof(PyHKEYObject)); + if (op == NULL) + return PyErr_NoMemory(); + PyObject_INIT(op, &PyHKEY_Type); + op->hkey = h; + return (PyObject *)op; +} + + +/************************************************************************ + The module methods +************************************************************************/ +BOOL +PyWinObject_CloseHKEY(PyObject *obHandle) +{ + BOOL ok; + if (PyHKEY_Check(obHandle)) { + ok = PyHKEY_Close(obHandle); + } +#if SIZEOF_LONG >= SIZEOF_HKEY + else if (PyInt_Check(obHandle)) { + long rc = RegCloseKey((HKEY)PyInt_AsLong(obHandle)); + ok = (rc == ERROR_SUCCESS); + if (!ok) + PyErr_SetFromWindowsErrWithFunction(rc, "RegCloseKey"); + } +#else + else if (PyLong_Check(obHandle)) { + long rc = RegCloseKey((HKEY)PyLong_AsVoidPtr(obHandle)); + ok = (rc == ERROR_SUCCESS); + if (!ok) + PyErr_SetFromWindowsErrWithFunction(rc, "RegCloseKey"); + } +#endif + else { + PyErr_SetString( + PyExc_TypeError, + "A handle must be a HKEY object or an integer"); + return FALSE; + } + return ok; +} + + +/* + Private Helper functions for the registry interfaces + +** Note that fixupMultiSZ and countString have both had changes +** made to support "incorrect strings". The registry specification +** calls for strings to be terminated with 2 null bytes. It seems +** some commercial packages install strings which dont conform, +** causing this code to fail - however, "regedit" etc still work +** with these strings (ie only we dont!). +*/ +static void +fixupMultiSZ(char **str, char *data, int len) +{ + char *P; + int i; + char *Q; + + Q = data + len; + for (P = data, i = 0; P < Q && *P != '\0'; P++, i++) { + str[i] = P; + for(; *P != '\0'; P++) + ; + } +} + +static int +countStrings(char *data, int len) +{ + int strings; + char *P; + char *Q = data + len; + + for (P = data, strings = 0; P < Q && *P != '\0'; P++, strings++) + for (; P < Q && *P != '\0'; P++) + ; + return strings; +} + +/* Convert PyObject into Registry data. + Allocates space as needed. */ +static BOOL +Py2Reg(PyObject *value, DWORD typ, BYTE **retDataBuf, DWORD *retDataSize) +{ + int i,j; + switch (typ) { + case REG_DWORD: + if (value != Py_None && !PyInt_Check(value)) + return FALSE; + *retDataBuf = (BYTE *)PyMem_NEW(DWORD, sizeof(DWORD)); + if (*retDataBuf==NULL){ + PyErr_NoMemory(); + return FALSE; + } + *retDataSize = sizeof(DWORD); + if (value == Py_None) { + DWORD zero = 0; + memcpy(*retDataBuf, &zero, sizeof(DWORD)); + } + else + memcpy(*retDataBuf, + &PyInt_AS_LONG((PyIntObject *)value), + sizeof(DWORD)); + break; + case REG_SZ: + case REG_EXPAND_SZ: + { + int need_decref = 0; + if (value == Py_None) + *retDataSize = 1; + else { + if (PyUnicode_Check(value)) { + value = PyUnicode_AsEncodedString( + value, + "mbcs", + NULL); + if (value==NULL) + return FALSE; + need_decref = 1; + } + if (!PyString_Check(value)) + return FALSE; + *retDataSize = 1 + strlen( + PyString_AS_STRING( + (PyStringObject *)value)); + } + *retDataBuf = (BYTE *)PyMem_NEW(DWORD, *retDataSize); + if (*retDataBuf==NULL){ + PyErr_NoMemory(); + return FALSE; + } + if (value == Py_None) + strcpy((char *)*retDataBuf, ""); + else + strcpy((char *)*retDataBuf, + PyString_AS_STRING( + (PyStringObject *)value)); + if (need_decref) + Py_DECREF(value); + break; + } + case REG_MULTI_SZ: + { + DWORD size = 0; + char *P; + PyObject **obs = NULL; + + if (value == Py_None) + i = 0; + else { + if (!PyList_Check(value)) + return FALSE; + i = PyList_Size(value); + } + obs = malloc(sizeof(PyObject *) * i); + memset(obs, 0, sizeof(PyObject *) * i); + for (j = 0; j < i; j++) + { + PyObject *t; + t = PyList_GET_ITEM( + (PyListObject *)value,j); + if (PyString_Check(t)) { + obs[j] = t; + Py_INCREF(t); + } else if (PyUnicode_Check(t)) { + obs[j] = PyUnicode_AsEncodedString( + t, + "mbcs", + NULL); + if (obs[j]==NULL) + goto reg_multi_fail; + } else + goto reg_multi_fail; + size += 1 + strlen( + PyString_AS_STRING( + (PyStringObject *)obs[j])); + } + + *retDataSize = size + 1; + *retDataBuf = (BYTE *)PyMem_NEW(char, + *retDataSize); + if (*retDataBuf==NULL){ + PyErr_NoMemory(); + goto reg_multi_fail; + } + P = (char *)*retDataBuf; + + for (j = 0; j < i; j++) + { + PyObject *t; + t = obs[j]; + strcpy(P, + PyString_AS_STRING( + (PyStringObject *)t)); + P += 1 + strlen( + PyString_AS_STRING( + (PyStringObject *)t)); + Py_DECREF(obs[j]); + } + /* And doubly-terminate the list... */ + *P = '\0'; + free(obs); + break; + reg_multi_fail: + if (obs) { + for (j = 0; j < i; j++) + Py_XDECREF(obs[j]); + + free(obs); + } + return FALSE; + } + case REG_BINARY: + /* ALSO handle ALL unknown data types here. Even if we can't + support it natively, we should handle the bits. */ + default: + if (value == Py_None) + *retDataSize = 0; + else { + void *src_buf; + PyBufferProcs *pb = value->ob_type->tp_as_buffer; + if (pb==NULL) { + PyErr_Format(PyExc_TypeError, + "Objects of type '%s' can not " + "be used as binary registry values", + value->ob_type->tp_name); + return FALSE; + } + *retDataSize = (*pb->bf_getreadbuffer)(value, 0, &src_buf); + *retDataBuf = (BYTE *)PyMem_NEW(char, + *retDataSize); + if (*retDataBuf==NULL){ + PyErr_NoMemory(); + return FALSE; + } + memcpy(*retDataBuf, src_buf, *retDataSize); + } + break; + } + return TRUE; +} + +/* Convert Registry data into PyObject*/ +static PyObject * +Reg2Py(char *retDataBuf, DWORD retDataSize, DWORD typ) +{ + PyObject *obData; + + switch (typ) { + case REG_DWORD: + if (retDataSize == 0) + obData = Py_BuildValue("i", 0); + else + obData = Py_BuildValue("i", + *(int *)retDataBuf); + break; + case REG_SZ: + case REG_EXPAND_SZ: + /* retDataBuf may or may not have a trailing NULL in + the buffer. */ + if (retDataSize && retDataBuf[retDataSize-1] == '\0') + --retDataSize; + if (retDataSize ==0) + retDataBuf = ""; + obData = PyUnicode_DecodeMBCS(retDataBuf, + retDataSize, + NULL); + break; + case REG_MULTI_SZ: + if (retDataSize == 0) + obData = PyList_New(0); + else + { + int index = 0; + int s = countStrings(retDataBuf, retDataSize); + char **str = (char **)malloc(sizeof(char *)*s); + if (str == NULL) + return PyErr_NoMemory(); + + fixupMultiSZ(str, retDataBuf, retDataSize); + obData = PyList_New(s); + if (obData == NULL) + return NULL; + for (index = 0; index < s; index++) + { + size_t len = _mbstrlen(str[index]); + if (len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "registry string is too long for a Python string"); + Py_DECREF(obData); + return NULL; + } + PyList_SetItem(obData, + index, + PyUnicode_DecodeMBCS( + (const char *)str[index], + (int)len, + NULL) + ); + } + free(str); + + break; + } + case REG_BINARY: + /* ALSO handle ALL unknown data types here. Even if we can't + support it natively, we should handle the bits. */ + default: + if (retDataSize == 0) { + Py_INCREF(Py_None); + obData = Py_None; + } + else + obData = Py_BuildValue("s#", + (char *)retDataBuf, + retDataSize); + break; + } + if (obData == NULL) + return NULL; + else + return obData; +} + +/* The Python methods */ + +static PyObject * +PyCloseKey(PyObject *self, PyObject *args) +{ + PyObject *obKey; + if (!PyArg_ParseTuple(args, "O:CloseKey", &obKey)) + return NULL; + if (!PyHKEY_Close(obKey)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyConnectRegistry(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + char *szCompName = NULL; + HKEY retKey; + long rc; + if (!PyArg_ParseTuple(args, "zO:ConnectRegistry", &szCompName, &obKey)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + rc = RegConnectRegistry(szCompName, hKey, &retKey); + if (rc != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, + "ConnectRegistry"); + return PyHKEY_FromHKEY(retKey); +} + +static PyObject * +PyCreateKey(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + char *subKey; + HKEY retKey; + long rc; + if (!PyArg_ParseTuple(args, "Oz:CreateKey", &obKey, &subKey)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + rc = RegCreateKey(hKey, subKey, &retKey); + if (rc != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, "CreateKey"); + return PyHKEY_FromHKEY(retKey); +} + +static PyObject * +PyDeleteKey(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + char *subKey; + long rc; + if (!PyArg_ParseTuple(args, "Os:DeleteKey", &obKey, &subKey)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + rc = RegDeleteKey(hKey, subKey ); + if (rc != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, "RegDeleteKey"); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyDeleteValue(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + char *subKey; + long rc; + if (!PyArg_ParseTuple(args, "Oz:DeleteValue", &obKey, &subKey)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rc = RegDeleteValue(hKey, subKey); + Py_END_ALLOW_THREADS + if (rc !=ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, + "RegDeleteValue"); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyEnumKey(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + int index; + long rc; + PyObject *retStr; + char *retBuf; + DWORD len; + + if (!PyArg_ParseTuple(args, "Oi:EnumKey", &obKey, &index)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + + if ((rc = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, &len, + NULL, NULL, NULL, NULL, NULL, NULL)) + != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, + "RegQueryInfoKey"); + ++len; /* include null terminator */ + retStr = PyString_FromStringAndSize(NULL, len); + if (retStr == NULL) + return NULL; + retBuf = PyString_AS_STRING(retStr); + + if ((rc = RegEnumKey(hKey, index, retBuf, len)) != ERROR_SUCCESS) { + Py_DECREF(retStr); + return PyErr_SetFromWindowsErrWithFunction(rc, "RegEnumKey"); + } + _PyString_Resize(&retStr, strlen(retBuf)); + return retStr; +} + +static PyObject * +PyEnumValue(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + int index; + long rc; + char *retValueBuf; + char *retDataBuf; + DWORD retValueSize; + DWORD retDataSize; + DWORD typ; + PyObject *obData; + PyObject *retVal; + + if (!PyArg_ParseTuple(args, "Oi:EnumValue", &obKey, &index)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + + if ((rc = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + &retValueSize, &retDataSize, NULL, NULL)) + != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, + "RegQueryInfoKey"); + ++retValueSize; /* include null terminators */ + ++retDataSize; + retValueBuf = (char *)PyMem_Malloc(retValueSize); + if (retValueBuf == NULL) + return PyErr_NoMemory(); + retDataBuf = (char *)PyMem_Malloc(retDataSize); + if (retDataBuf == NULL) { + PyMem_Free(retValueBuf); + return PyErr_NoMemory(); + } + + Py_BEGIN_ALLOW_THREADS + rc = RegEnumValue(hKey, + index, + retValueBuf, + &retValueSize, + NULL, + &typ, + (BYTE *)retDataBuf, + &retDataSize); + Py_END_ALLOW_THREADS + + if (rc != ERROR_SUCCESS) { + retVal = PyErr_SetFromWindowsErrWithFunction(rc, + "PyRegEnumValue"); + goto fail; + } + obData = Reg2Py(retDataBuf, retDataSize, typ); + if (obData == NULL) { + retVal = NULL; + goto fail; + } + retVal = Py_BuildValue("sOi", retValueBuf, obData, typ); + Py_DECREF(obData); + fail: + PyMem_Free(retValueBuf); + PyMem_Free(retDataBuf); + return retVal; +} + +static PyObject * +PyFlushKey(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + long rc; + if (!PyArg_ParseTuple(args, "O:FlushKey", &obKey)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rc = RegFlushKey(hKey); + Py_END_ALLOW_THREADS + if (rc != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, "RegFlushKey"); + Py_INCREF(Py_None); + return Py_None; +} +static PyObject * +PyLoadKey(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + char *subKey; + char *fileName; + + long rc; + if (!PyArg_ParseTuple(args, "Oss:LoadKey", &obKey, &subKey, &fileName)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + Py_BEGIN_ALLOW_THREADS + rc = RegLoadKey(hKey, subKey, fileName ); + Py_END_ALLOW_THREADS + if (rc != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, "RegLoadKey"); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PyOpenKey(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + + char *subKey; + int res = 0; + HKEY retKey; + long rc; + REGSAM sam = KEY_READ; + if (!PyArg_ParseTuple(args, "Oz|ii:OpenKey", &obKey, &subKey, + &res, &sam)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + rc = RegOpenKeyEx(hKey, subKey, res, sam, &retKey); + Py_END_ALLOW_THREADS + if (rc != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, "RegOpenKeyEx"); + return PyHKEY_FromHKEY(retKey); +} + + +static PyObject * +PyQueryInfoKey(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + long rc; + DWORD nSubKeys, nValues; + FILETIME ft; + LARGE_INTEGER li; + PyObject *l; + PyObject *ret; + if (!PyArg_ParseTuple(args, "O:QueryInfoKey", &obKey)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + if ((rc = RegQueryInfoKey(hKey, NULL, NULL, 0, &nSubKeys, NULL, NULL, + &nValues, NULL, NULL, NULL, &ft)) + != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, "RegQueryInfoKey"); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + l = PyLong_FromLongLong(li.QuadPart); + if (l == NULL) + return NULL; + ret = Py_BuildValue("iiO", nSubKeys, nValues, l); + Py_DECREF(l); + return ret; +} + +static PyObject * +PyQueryValue(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + char *subKey; + long rc; + PyObject *retStr; + char *retBuf; + long bufSize = 0; + + if (!PyArg_ParseTuple(args, "Oz:QueryValue", &obKey, &subKey)) + return NULL; + + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + if ((rc = RegQueryValue(hKey, subKey, NULL, &bufSize)) + != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, + "RegQueryValue"); + retStr = PyString_FromStringAndSize(NULL, bufSize); + if (retStr == NULL) + return NULL; + retBuf = PyString_AS_STRING(retStr); + if ((rc = RegQueryValue(hKey, subKey, retBuf, &bufSize)) + != ERROR_SUCCESS) { + Py_DECREF(retStr); + return PyErr_SetFromWindowsErrWithFunction(rc, + "RegQueryValue"); + } + _PyString_Resize(&retStr, strlen(retBuf)); + return retStr; +} + +static PyObject * +PyQueryValueEx(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + char *valueName; + + long rc; + char *retBuf; + DWORD bufSize = 0; + DWORD typ; + PyObject *obData; + PyObject *result; + + if (!PyArg_ParseTuple(args, "Oz:QueryValueEx", &obKey, &valueName)) + return NULL; + + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + if ((rc = RegQueryValueEx(hKey, valueName, + NULL, NULL, NULL, + &bufSize)) + != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, + "RegQueryValueEx"); + retBuf = (char *)PyMem_Malloc(bufSize); + if (retBuf == NULL) + return PyErr_NoMemory(); + if ((rc = RegQueryValueEx(hKey, valueName, NULL, + &typ, (BYTE *)retBuf, &bufSize)) + != ERROR_SUCCESS) { + PyMem_Free(retBuf); + return PyErr_SetFromWindowsErrWithFunction(rc, + "RegQueryValueEx"); + } + obData = Reg2Py(retBuf, bufSize, typ); + PyMem_Free((void *)retBuf); + if (obData == NULL) + return NULL; + result = Py_BuildValue("Oi", obData, typ); + Py_DECREF(obData); + return result; +} + + +static PyObject * +PySaveKey(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + char *fileName; + LPSECURITY_ATTRIBUTES pSA = NULL; + + long rc; + if (!PyArg_ParseTuple(args, "Os:SaveKey", &obKey, &fileName)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; +/* One day we may get security into the core? + if (!PyWinObject_AsSECURITY_ATTRIBUTES(obSA, &pSA, TRUE)) + return NULL; +*/ + Py_BEGIN_ALLOW_THREADS + rc = RegSaveKey(hKey, fileName, pSA ); + Py_END_ALLOW_THREADS + if (rc != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, "RegSaveKey"); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PySetValue(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + char *subKey; + char *str; + DWORD typ; + DWORD len; + long rc; + PyObject *obStrVal; + PyObject *obSubKey; + if (!PyArg_ParseTuple(args, "OOiO:SetValue", + &obKey, + &obSubKey, + &typ, + &obStrVal)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + if (typ != REG_SZ) { + PyErr_SetString(PyExc_TypeError, + "Type must be _winreg.REG_SZ"); + return NULL; + } + /* XXX - need Unicode support */ + str = PyString_AsString(obStrVal); + if (str == NULL) + return NULL; + len = PyString_Size(obStrVal); + if (obSubKey == Py_None) + subKey = NULL; + else { + subKey = PyString_AsString(obSubKey); + if (subKey == NULL) + return NULL; + } + Py_BEGIN_ALLOW_THREADS + rc = RegSetValue(hKey, subKey, REG_SZ, str, len+1); + Py_END_ALLOW_THREADS + if (rc != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, "RegSetValue"); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PySetValueEx(PyObject *self, PyObject *args) +{ + HKEY hKey; + PyObject *obKey; + char *valueName; + PyObject *obRes; + PyObject *value; + BYTE *data; + DWORD len; + DWORD typ; + + LONG rc; + + if (!PyArg_ParseTuple(args, "OzOiO:SetValueEx", + &obKey, + &valueName, + &obRes, + &typ, + &value)) + return NULL; + if (!PyHKEY_AsHKEY(obKey, &hKey, FALSE)) + return NULL; + if (!Py2Reg(value, typ, &data, &len)) + { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_ValueError, + "Could not convert the data to the specified type."); + return NULL; + } + Py_BEGIN_ALLOW_THREADS + rc = RegSetValueEx(hKey, valueName, 0, typ, data, len); + Py_END_ALLOW_THREADS + PyMem_DEL(data); + if (rc != ERROR_SUCCESS) + return PyErr_SetFromWindowsErrWithFunction(rc, + "RegSetValueEx"); + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef winreg_methods[] = { + {"CloseKey", PyCloseKey, METH_VARARGS, CloseKey_doc}, + {"ConnectRegistry", PyConnectRegistry, METH_VARARGS, ConnectRegistry_doc}, + {"CreateKey", PyCreateKey, METH_VARARGS, CreateKey_doc}, + {"DeleteKey", PyDeleteKey, METH_VARARGS, DeleteKey_doc}, + {"DeleteValue", PyDeleteValue, METH_VARARGS, DeleteValue_doc}, + {"EnumKey", PyEnumKey, METH_VARARGS, EnumKey_doc}, + {"EnumValue", PyEnumValue, METH_VARARGS, EnumValue_doc}, + {"FlushKey", PyFlushKey, METH_VARARGS, FlushKey_doc}, + {"LoadKey", PyLoadKey, METH_VARARGS, LoadKey_doc}, + {"OpenKey", PyOpenKey, METH_VARARGS, OpenKey_doc}, + {"OpenKeyEx", PyOpenKey, METH_VARARGS, OpenKeyEx_doc}, + {"QueryValue", PyQueryValue, METH_VARARGS, QueryValue_doc}, + {"QueryValueEx", PyQueryValueEx, METH_VARARGS, QueryValueEx_doc}, + {"QueryInfoKey", PyQueryInfoKey, METH_VARARGS, QueryInfoKey_doc}, + {"SaveKey", PySaveKey, METH_VARARGS, SaveKey_doc}, + {"SetValue", PySetValue, METH_VARARGS, SetValue_doc}, + {"SetValueEx", PySetValueEx, METH_VARARGS, SetValueEx_doc}, + NULL, +}; + +static void +insint(PyObject * d, char * name, long value) +{ + PyObject *v = PyInt_FromLong(value); + if (!v || PyDict_SetItemString(d, name, v)) + PyErr_Clear(); + Py_XDECREF(v); +} + +#define ADD_INT(val) insint(d, #val, val) + +static void +inskey(PyObject * d, char * name, HKEY key) +{ + PyObject *v = PyLong_FromVoidPtr(key); + if (!v || PyDict_SetItemString(d, name, v)) + PyErr_Clear(); + Py_XDECREF(v); +} + +#define ADD_KEY(val) inskey(d, #val, val) + +PyMODINIT_FUNC init_winreg(void) +{ + PyObject *m, *d; + m = Py_InitModule3("_winreg", winreg_methods, module_doc); + d = PyModule_GetDict(m); + PyHKEY_Type.ob_type = &PyType_Type; + PyHKEY_Type.tp_doc = PyHKEY_doc; + Py_INCREF(&PyHKEY_Type); + if (PyDict_SetItemString(d, "HKEYType", + (PyObject *)&PyHKEY_Type) != 0) + return; + Py_INCREF(PyExc_WindowsError); + if (PyDict_SetItemString(d, "error", + PyExc_WindowsError) != 0) + return; + + /* Add the relevant constants */ + ADD_KEY(HKEY_CLASSES_ROOT); + ADD_KEY(HKEY_CURRENT_USER); + ADD_KEY(HKEY_LOCAL_MACHINE); + ADD_KEY(HKEY_USERS); + ADD_KEY(HKEY_PERFORMANCE_DATA); +#ifdef HKEY_CURRENT_CONFIG + ADD_KEY(HKEY_CURRENT_CONFIG); +#endif +#ifdef HKEY_DYN_DATA + ADD_KEY(HKEY_DYN_DATA); +#endif + ADD_INT(KEY_QUERY_VALUE); + ADD_INT(KEY_SET_VALUE); + ADD_INT(KEY_CREATE_SUB_KEY); + ADD_INT(KEY_ENUMERATE_SUB_KEYS); + ADD_INT(KEY_NOTIFY); + ADD_INT(KEY_CREATE_LINK); + ADD_INT(KEY_READ); + ADD_INT(KEY_WRITE); + ADD_INT(KEY_EXECUTE); + ADD_INT(KEY_ALL_ACCESS); + ADD_INT(REG_OPTION_RESERVED); + ADD_INT(REG_OPTION_NON_VOLATILE); + ADD_INT(REG_OPTION_VOLATILE); + ADD_INT(REG_OPTION_CREATE_LINK); + ADD_INT(REG_OPTION_BACKUP_RESTORE); + ADD_INT(REG_OPTION_OPEN_LINK); + ADD_INT(REG_LEGAL_OPTION); + ADD_INT(REG_CREATED_NEW_KEY); + ADD_INT(REG_OPENED_EXISTING_KEY); + ADD_INT(REG_WHOLE_HIVE_VOLATILE); + ADD_INT(REG_REFRESH_HIVE); + ADD_INT(REG_NO_LAZY_FLUSH); + ADD_INT(REG_NOTIFY_CHANGE_NAME); + ADD_INT(REG_NOTIFY_CHANGE_ATTRIBUTES); + ADD_INT(REG_NOTIFY_CHANGE_LAST_SET); + ADD_INT(REG_NOTIFY_CHANGE_SECURITY); + ADD_INT(REG_LEGAL_CHANGE_FILTER); + ADD_INT(REG_NONE); + ADD_INT(REG_SZ); + ADD_INT(REG_EXPAND_SZ); + ADD_INT(REG_BINARY); + ADD_INT(REG_DWORD); + ADD_INT(REG_DWORD_LITTLE_ENDIAN); + ADD_INT(REG_DWORD_BIG_ENDIAN); + ADD_INT(REG_LINK); + ADD_INT(REG_MULTI_SZ); + ADD_INT(REG_RESOURCE_LIST); + ADD_INT(REG_FULL_RESOURCE_DESCRIPTOR); + ADD_INT(REG_RESOURCE_REQUIREMENTS_LIST); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/.cvsignore b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/.cvsignore new file mode 100644 index 00000000..6030df50 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/.cvsignore @@ -0,0 +1,4 @@ +temp-debug +temp-release +wininst.ncb +wininst.plg diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/PythonPowered.bmp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/PythonPowered.bmp new file mode 100644 index 0000000000000000000000000000000000000000..dd70709da61c3014d300147201498a653d15c483 GIT binary patch literal 2582 zcmdT_u}<7T5S>UVNUB9TTa?#;f|SN3-PuJTNGa}`fGGoQy5tK`{(wHsxuv5+*X0u^ zucb5HN%+t?@6C8^?;Vj)ib(NkXJ=<`_U-JxXJ?<{x1!H?*mr1O(cYuIlGm^zKi_B< zn7Xdz{{CL(^SRvJ-O26ktz2GSN|t4EaBv_=lF0u4zU=Jmh;vT1wzfp(wM@U*GUeYr z@^@+F|CaYq*Q^vBoSx+{jO)0q5A!R`Gp%0rEQeuS$3Bk;m{%ap>Y4R8*YSDv&5U{V zO;OBh3@|i5j2E2l=Pf5^M5rcdmcyP-Xw&SB@m2YtJ|hSPd5hs5^QKNDx}Lg$qZQfv zh4`w}_?}wfxZ7tnI!4-ZoTO=zkHJmaG|hYQTxZ>JkhgFVw!Rof#neTB1E3pReEU(j z80#&D>g#MARL0e-(c$O=eus#p*@^Chv*tfy+~4N{#Yn`}f!zG47x>pWSkA(a zQQ{_Ens9aE9ZgOQ&jB^BFajR@<7&K7-{9ih?V?K77Z-)rcMdDj72=!)_LY<8@bnBD z)FVAxASHUjcFt+CjyGfhdS_9AJHvhIT^7Ru5G_tSSgUVIDi}v{as*AevjlZ~9DK<> zubn5!Sj<+QlW=C&4W2?VMGKrcoxGryjlftvo zUM)Rkt8u8~W>0@DUbCuU8Cx6MaWlv>1yb@*0B{tmK_Y65I-9!-@=Sqs1+nLLPpUeq zKs9nWZn+ucnF7HfZRdnP-YO9pq3yJ(Z9Pt{qwxvb$G{| literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/README.txt b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/README.txt new file mode 100644 index 00000000..3571d2b1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/README.txt @@ -0,0 +1,5 @@ + +XXX Write description +XXX Dont't forget to mention upx + +XXX Add pointer to this file into PC/README.txt diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/archive.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/archive.h new file mode 100644 index 00000000..8b1f8639 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/archive.h @@ -0,0 +1,96 @@ +#pragma pack(1) + +/* zip-archive headers + * See: http://www.pkware.com/appnote.html + */ + +struct eof_cdir { + long tag; /* must be 0x06054b50 */ + short disknum; + short firstdisk; + short nTotalCDirThis; + short nTotalCDir; + long nBytesCDir; + long ofsCDir; + short commentlen; +}; + +struct cdir { + long tag; /* must be 0x02014b50 */ + short version_made; + short version_extract; + short gp_bitflag; + short comp_method; + short last_mod_file_time; + short last_mod_file_date; + long crc32; + long comp_size; + long uncomp_size; + short fname_length; + short extra_length; + short comment_length; + short disknum_start; + short int_file_attr; + long ext_file_attr; + long ofs_local_header; +}; + +struct fhdr { + long tag; /* must be 0x04034b50 */ + short version_needed; + short flags; + short method; + short last_mod_file_time; + short last_mod_file_date; + long crc32; + long comp_size; + long uncomp_size; + short fname_length; + short extra_length; +}; + + +struct meta_data_hdr { + int tag; + int uncomp_size; + int bitmap_size; +}; + +#pragma pack() + +/* installation scheme */ + +typedef struct tagSCHEME { + char *name; + char *prefix; +} SCHEME; + +typedef int (*NOTIFYPROC)(int code, LPSTR text, ...); + +extern BOOL +extract_file(char *dst, char *src, int method, int comp_size, + int uncomp_size, NOTIFYPROC notify); + +extern BOOL +unzip_archive(SCHEME *scheme, char *dirname, char *data, + DWORD size, NOTIFYPROC notify); + +extern char * +map_new_file(DWORD flags, char *filename, char + *pathname_part, int size, + WORD wFatDate, WORD wFatTime, + NOTIFYPROC callback); + +extern BOOL +ensure_directory (char *pathname, char *new_part, + NOTIFYPROC callback); + +/* codes for NOITIFYPROC */ +#define DIR_CREATED 1 +#define CAN_OVERWRITE 2 +#define FILE_CREATED 3 +#define ZLIB_ERROR 4 +#define SYSTEM_ERROR 5 +#define NUM_FILES 6 +#define FILE_OVERWRITTEN 7 + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/extract.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/extract.c new file mode 100644 index 00000000..abfe23ec --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/extract.c @@ -0,0 +1,312 @@ +#include + +#include "zlib.h" + +#include +#include + +#include "archive.h" + +/* Convert unix-path to dos-path */ +static void normpath(char *path) +{ + while (path && *path) { + if (*path == '/') + *path = '\\'; + ++path; + } +} + +BOOL ensure_directory(char *pathname, char *new_part, NOTIFYPROC notify) +{ + while (new_part && *new_part && (new_part = strchr(new_part, '\\'))) { + DWORD attr; + *new_part = '\0'; + attr = GetFileAttributes(pathname); + if (attr == -1) { + /* nothing found */ + if (!CreateDirectory(pathname, NULL) && notify) + notify(SYSTEM_ERROR, + "CreateDirectory (%s)", pathname); + else + notify(DIR_CREATED, pathname); + } + if (attr & FILE_ATTRIBUTE_DIRECTORY) { + ; + } else { + SetLastError(183); + if (notify) + notify(SYSTEM_ERROR, + "CreateDirectory (%s)", pathname); + } + *new_part = '\\'; + ++new_part; + } + return TRUE; +} + +/* XXX Should better explicitely specify + * uncomp_size and file_times instead of pfhdr! + */ +char *map_new_file(DWORD flags, char *filename, + char *pathname_part, int size, + WORD wFatDate, WORD wFatTime, + NOTIFYPROC notify) +{ + HANDLE hFile, hFileMapping; + char *dst; + FILETIME ft; + + try_again: + if (!flags) + flags = CREATE_NEW; + hFile = CreateFile(filename, + GENERIC_WRITE | GENERIC_READ, + 0, NULL, + flags, + FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + DWORD x = GetLastError(); + switch (x) { + case ERROR_FILE_EXISTS: + if (notify && notify(CAN_OVERWRITE, filename)) + hFile = CreateFile(filename, + GENERIC_WRITE|GENERIC_READ, + 0, NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + else { + if (notify) + notify(FILE_OVERWRITTEN, filename); + return NULL; + } + break; + case ERROR_PATH_NOT_FOUND: + if (ensure_directory(filename, pathname_part, notify)) + goto try_again; + else + return FALSE; + break; + default: + SetLastError(x); + break; + } + } + if (hFile == INVALID_HANDLE_VALUE) { + if (notify) + notify (SYSTEM_ERROR, "CreateFile (%s)", filename); + return NULL; + } + + if (notify) + notify(FILE_CREATED, filename); + + DosDateTimeToFileTime(wFatDate, wFatTime, &ft); + SetFileTime(hFile, &ft, &ft, &ft); + + + if (size == 0) { + /* We cannot map a zero-length file (Also it makes + no sense */ + CloseHandle(hFile); + return NULL; + } + + hFileMapping = CreateFileMapping(hFile, + NULL, PAGE_READWRITE, 0, size, NULL); + + CloseHandle(hFile); + + if (hFileMapping == INVALID_HANDLE_VALUE) { + if (notify) + notify(SYSTEM_ERROR, + "CreateFileMapping (%s)", filename); + return NULL; + } + + dst = MapViewOfFile(hFileMapping, + FILE_MAP_WRITE, 0, 0, 0); + + CloseHandle(hFileMapping); + + if (!dst) { + if (notify) + notify(SYSTEM_ERROR, "MapViewOfFile (%s)", filename); + return NULL; + } + return dst; +} + + +BOOL +extract_file(char *dst, char *src, int method, int comp_size, + int uncomp_size, NOTIFYPROC notify) +{ + z_stream zstream; + int result; + + if (method == Z_DEFLATED) { + int x; + memset(&zstream, 0, sizeof(zstream)); + zstream.next_in = src; + zstream.avail_in = comp_size+1; + zstream.next_out = dst; + zstream.avail_out = uncomp_size; + +/* Apparently an undocumented feature of zlib: Set windowsize + to negative values to supress the gzip header and be compatible with + zip! */ + result = TRUE; + if (Z_OK != (x = inflateInit2(&zstream, -15))) { + if (notify) + notify(ZLIB_ERROR, + "inflateInit2 returns %d", x); + result = FALSE; + goto cleanup; + } + if (Z_STREAM_END != (x = inflate(&zstream, Z_FINISH))) { + if (notify) + notify(ZLIB_ERROR, + "inflate returns %d", x); + result = FALSE; + } + cleanup: + if (Z_OK != (x = inflateEnd(&zstream))) { + if (notify) + notify (ZLIB_ERROR, + "inflateEnd returns %d", x); + result = FALSE; + } + } else if (method == 0) { + memcpy(dst, src, uncomp_size); + result = TRUE; + } else + result = FALSE; + UnmapViewOfFile(dst); + return result; +} + +/* Open a zip-compatible archive and extract all files + * into the specified directory (which is assumed to exist) + */ +BOOL +unzip_archive(SCHEME *scheme, char *dirname, char *data, DWORD size, + NOTIFYPROC notify) +{ + int n; + char pathname[MAX_PATH]; + char *new_part; + + /* read the end of central directory record */ + struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof + (struct eof_cdir)]; + + int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir - + pe->ofsCDir; + + /* set position to start of central directory */ + int pos = arc_start + pe->ofsCDir; + + /* make sure this is a zip file */ + if (pe->tag != 0x06054b50) + return FALSE; + + /* Loop through the central directory, reading all entries */ + for (n = 0; n < pe->nTotalCDir; ++n) { + int i; + char *fname; + char *pcomp; + char *dst; + struct cdir *pcdir; + struct fhdr *pfhdr; + + pcdir = (struct cdir *)&data[pos]; + pfhdr = (struct fhdr *)&data[pcdir->ofs_local_header + + arc_start]; + + if (pcdir->tag != 0x02014b50) + return FALSE; + if (pfhdr->tag != 0x04034b50) + return FALSE; + pos += sizeof(struct cdir); + fname = (char *)&data[pos]; /* This is not null terminated! */ + pos += pcdir->fname_length + pcdir->extra_length + + pcdir->comment_length; + + pcomp = &data[pcdir->ofs_local_header + + sizeof(struct fhdr) + + arc_start + + pfhdr->fname_length + + pfhdr->extra_length]; + + /* dirname is the Python home directory (prefix) */ + strcpy(pathname, dirname); + if (pathname[strlen(pathname)-1] != '\\') + strcat(pathname, "\\"); + new_part = &pathname[lstrlen(pathname)]; + /* we must now match the first part of the pathname + * in the archive to a component in the installation + * scheme (PURELIB, PLATLIB, HEADERS, SCRIPTS, or DATA) + * and replace this part by the one in the scheme to use + */ + for (i = 0; scheme[i].name; ++i) { + if (0 == strnicmp(scheme[i].name, fname, + strlen(scheme[i].name))) { + char *rest; + int len; + + /* length of the replaced part */ + int namelen = strlen(scheme[i].name); + + strcat(pathname, scheme[i].prefix); + + rest = fname + namelen; + len = pfhdr->fname_length - namelen; + + if ((pathname[strlen(pathname)-1] != '\\') + && (pathname[strlen(pathname)-1] != '/')) + strcat(pathname, "\\"); + /* Now that pathname ends with a separator, + * we must make sure rest does not start with + * an additional one. + */ + if ((rest[0] == '\\') || (rest[0] == '/')) { + ++rest; + --len; + } + + strncat(pathname, rest, len); + goto Done; + } + } + /* no prefix to replace found, go unchanged */ + strncat(pathname, fname, pfhdr->fname_length); + Done: + normpath(pathname); + if (pathname[strlen(pathname)-1] != '\\') { + /* + * The local file header (pfhdr) does not always + * contain the compressed and uncompressed sizes of + * the data depending on bit 3 of the flags field. So + * it seems better to use the data from the central + * directory (pcdir). + */ + dst = map_new_file(0, pathname, new_part, + pcdir->uncomp_size, + pcdir->last_mod_file_date, + pcdir->last_mod_file_time, notify); + if (dst) { + if (!extract_file(dst, pcomp, pfhdr->method, + pcdir->comp_size, + pcdir->uncomp_size, + notify)) + return FALSE; + } /* else ??? */ + } + if (notify) + notify(NUM_FILES, new_part, (int)pe->nTotalCDir, + (int)n+1); + } + return TRUE; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/install.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/install.c new file mode 100644 index 00000000..a71b738e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/install.c @@ -0,0 +1,2294 @@ +/* + * Written by Thomas Heller, May 2000 + * + * $Id: install.c,v 1.1 2002/11/22 20:39:33 theller Exp $ + */ + +/* + * Windows Installer program for distutils. + * + * (a kind of self-extracting zip-file) + * + * At runtime, the exefile has appended: + * - compressed setup-data in ini-format, containing the following sections: + * [metadata] + * author=Greg Ward + * author_email=gward@python.net + * description=Python Distribution Utilities + * licence=Python + * name=Distutils + * url=http://www.python.org/sigs/distutils-sig/ + * version=0.9pre + * + * [Setup] + * info= text to be displayed in the edit-box + * title= to be displayed by this program + * target_version = if present, python version required + * pyc_compile = if 0, do not compile py to pyc + * pyo_compile = if 0, do not compile py to pyo + * + * - a struct meta_data_hdr, describing the above + * - a zip-file, containing the modules to be installed. + * for the format see http://www.pkware.com/appnote.html + * + * What does this program do? + * - the setup-data is uncompressed and written to a temporary file. + * - setup-data is queried with GetPrivateProfile... calls + * - [metadata] - info is displayed in the dialog box + * - The registry is searched for installations of python + * - The user can select the python version to use. + * - The python-installation directory (sys.prefix) is displayed + * - When the start-button is pressed, files from the zip-archive + * are extracted to the file system. All .py filenames are stored + * in a list. + */ +/* + * Includes now an uninstaller. + */ + +/* + * To Do: + * + * display some explanation when no python version is found + * instead showing the user an empty listbox to select something from. + * + * Finish the code so that we can use other python installations + * additionaly to those found in the registry, + * and then #define USE_OTHER_PYTHON_VERSIONS + * + * - install a help-button, which will display something meaningful + * to the poor user. + * text to the user + * - should there be a possibility to display a README file + * before starting the installation (if one is present in the archive) + * - more comments about what the code does(?) + * + * - evolve this into a full blown installer (???) + */ + +#include +#include +#include +#include +#include +#include +#include "resource.h" + +#include +#include +#include +#include +#include + +#include "archive.h" + +/* Only for debugging! + static int dprintf(char *fmt, ...) + { + char Buffer[4096]; + va_list marker; + int result; + + va_start(marker, fmt); + result = wvsprintf(Buffer, fmt, marker); + OutputDebugString(Buffer); + return result; + } +*/ + +/* Bah: global variables */ +FILE *logfile; + +char modulename[MAX_PATH]; + +HWND hwndMain; +HWND hDialog; + +char *ini_file; /* Full pathname of ini-file */ +/* From ini-file */ +char info[4096]; /* [Setup] info= */ +char title[80]; /* [Setup] title=, contains package name + including version: "Distutils-1.0.1" */ +char target_version[10]; /* [Setup] target_version=, required python + version or empty string */ +char build_info[80]; /* [Setup] build_info=, distutils version + and build date */ + +char meta_name[80]; /* package name without version like + 'Distutils' */ +char install_script[MAX_PATH]; + + +int py_major, py_minor; /* Python version selected for installation */ + +char *arc_data; /* memory mapped archive */ +DWORD arc_size; /* number of bytes in archive */ +int exe_size; /* number of bytes for exe-file portion */ +char python_dir[MAX_PATH]; +char pythondll[MAX_PATH]; +BOOL pyc_compile, pyo_compile; + +BOOL success; /* Installation successfull? */ + +HANDLE hBitmap; +char *bitmap_bytes; + + +#define WM_NUMFILES WM_USER+1 +/* wParam: 0, lParam: total number of files */ +#define WM_NEXTFILE WM_USER+2 +/* wParam: number of this file */ +/* lParam: points to pathname */ + +enum { UNSPECIFIED, ALWAYS, NEVER } allow_overwrite = UNSPECIFIED; + +static BOOL notify(int code, char *fmt, ...); + +/* Note: If scheme.prefix is nonempty, it must end with a '\'! */ +/* Note: purelib must be the FIRST entry! */ +SCHEME old_scheme[] = { + { "PURELIB", "" }, + { "PLATLIB", "" }, + { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */ + { "SCRIPTS", "Scripts\\" }, + { "DATA", "" }, + { NULL, NULL }, +}; + +SCHEME new_scheme[] = { + { "PURELIB", "Lib\\site-packages\\" }, + { "PLATLIB", "Lib\\site-packages\\" }, + { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */ + { "SCRIPTS", "Scripts\\" }, + { "DATA", "" }, + { NULL, NULL }, +}; + +static void unescape(char *dst, char *src, unsigned size) +{ + char *eon; + char ch; + + while (src && *src && (size > 2)) { + if (*src == '\\') { + switch (*++src) { + case 'n': + ++src; + *dst++ = '\r'; + *dst++ = '\n'; + size -= 2; + break; + case 'r': + ++src; + *dst++ = '\r'; + --size; + break; + case '0': case '1': case '2': case '3': + ch = (char)strtol(src, &eon, 8); + if (ch == '\n') { + *dst++ = '\r'; + --size; + } + *dst++ = ch; + --size; + src = eon; + } + } else { + *dst++ = *src++; + --size; + } + } + *dst = '\0'; +} + +static struct tagFile { + char *path; + struct tagFile *next; +} *file_list = NULL; + +static void add_to_filelist(char *path) +{ + struct tagFile *p; + p = (struct tagFile *)malloc(sizeof(struct tagFile)); + p->path = strdup(path); + p->next = file_list; + file_list = p; +} + +static int do_compile_files(int (__cdecl * PyRun_SimpleString)(char *), + int optimize) +{ + struct tagFile *p; + int total, n; + char Buffer[MAX_PATH + 64]; + int errors = 0; + + total = 0; + p = file_list; + while (p) { + ++total; + p = p->next; + } + SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETRANGE, 0, + MAKELPARAM(0, total)); + SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, 0, 0); + + n = 0; + p = file_list; + while (p) { + ++n; + wsprintf(Buffer, + "import py_compile; py_compile.compile (r'%s')", + p->path); + if (PyRun_SimpleString(Buffer)) { + ++errors; + } + /* We send the notification even if the files could not + * be created so that the uninstaller will remove them + * in case they are created later. + */ + wsprintf(Buffer, "%s%c", p->path, optimize ? 'o' : 'c'); + notify(FILE_CREATED, Buffer); + + SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, n, 0); + SetDlgItemText(hDialog, IDC_INFO, p->path); + p = p->next; + } + return errors; +} + +#define DECLPROC(dll, result, name, args)\ + typedef result (*__PROC__##name) args;\ + result (*name)args = (__PROC__##name)GetProcAddress(dll, #name) + + +#define DECLVAR(dll, type, name)\ + type *name = (type*)GetProcAddress(dll, #name) + +typedef void PyObject; + + +/* + * Returns number of files which failed to compile, + * -1 if python could not be loaded at all + */ +static int compile_filelist(HINSTANCE hPython, BOOL optimize_flag) +{ + DECLPROC(hPython, void, Py_Initialize, (void)); + DECLPROC(hPython, void, Py_SetProgramName, (char *)); + DECLPROC(hPython, void, Py_Finalize, (void)); + DECLPROC(hPython, int, PyRun_SimpleString, (char *)); + DECLPROC(hPython, PyObject *, PySys_GetObject, (char *)); + DECLVAR(hPython, int, Py_OptimizeFlag); + + int errors = 0; + struct tagFile *p = file_list; + + if (!p) + return 0; + + if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize) + return -1; + + if (!PyRun_SimpleString || !PySys_GetObject || !Py_OptimizeFlag) + return -1; + + *Py_OptimizeFlag = optimize_flag ? 1 : 0; + Py_SetProgramName(modulename); + Py_Initialize(); + + errors += do_compile_files(PyRun_SimpleString, optimize_flag); + Py_Finalize(); + + return errors; +} + +typedef PyObject *(*PyCFunction)(PyObject *, PyObject *); + +struct PyMethodDef { + char *ml_name; + PyCFunction ml_meth; + int ml_flags; + char *ml_doc; +}; +typedef struct PyMethodDef PyMethodDef; + +void *(*g_Py_BuildValue)(char *, ...); +int (*g_PyArg_ParseTuple)(PyObject *, char *, ...); + +PyObject *g_PyExc_ValueError; +PyObject *g_PyExc_OSError; + +PyObject *(*g_PyErr_Format)(PyObject *, char *, ...); + +#define DEF_CSIDL(name) { name, #name } + +struct { + int nFolder; + char *name; +} csidl_names[] = { + /* Startup menu for all users. + NT only */ + DEF_CSIDL(CSIDL_COMMON_STARTMENU), + /* Startup menu. */ + DEF_CSIDL(CSIDL_STARTMENU), + +/* DEF_CSIDL(CSIDL_COMMON_APPDATA), */ +/* DEF_CSIDL(CSIDL_LOCAL_APPDATA), */ + /* Repository for application-specific data. + Needs Internet Explorer 4.0 */ + DEF_CSIDL(CSIDL_APPDATA), + + /* The desktop for all users. + NT only */ + DEF_CSIDL(CSIDL_COMMON_DESKTOPDIRECTORY), + /* The desktop. */ + DEF_CSIDL(CSIDL_DESKTOPDIRECTORY), + + /* Startup folder for all users. + NT only */ + DEF_CSIDL(CSIDL_COMMON_STARTUP), + /* Startup folder. */ + DEF_CSIDL(CSIDL_STARTUP), + + /* Programs item in the start menu for all users. + NT only */ + DEF_CSIDL(CSIDL_COMMON_PROGRAMS), + /* Program item in the user's start menu. */ + DEF_CSIDL(CSIDL_PROGRAMS), + +/* DEF_CSIDL(CSIDL_PROGRAM_FILES_COMMON), */ +/* DEF_CSIDL(CSIDL_PROGRAM_FILES), */ + + /* Virtual folder containing fonts. */ + DEF_CSIDL(CSIDL_FONTS), +}; + +#define DIM(a) (sizeof(a) / sizeof((a)[0])) + +static PyObject *FileCreated(PyObject *self, PyObject *args) +{ + char *path; + if (!g_PyArg_ParseTuple(args, "s", &path)) + return NULL; + notify(FILE_CREATED, path); + return g_Py_BuildValue(""); +} + +static PyObject *DirectoryCreated(PyObject *self, PyObject *args) +{ + char *path; + if (!g_PyArg_ParseTuple(args, "s", &path)) + return NULL; + notify(DIR_CREATED, path); + return g_Py_BuildValue(""); +} + +static PyObject *GetSpecialFolderPath(PyObject *self, PyObject *args) +{ + char *name; + char lpszPath[MAX_PATH]; + int i; + static HRESULT (WINAPI *My_SHGetSpecialFolderPath)(HWND hwnd, + LPTSTR lpszPath, + int nFolder, + BOOL fCreate); + + if (!My_SHGetSpecialFolderPath) { + HINSTANCE hLib = LoadLibrary("shell32.dll"); + if (!hLib) { + g_PyErr_Format(g_PyExc_OSError, + "function not available"); + return NULL; + } + My_SHGetSpecialFolderPath = (BOOL (WINAPI *)(HWND, LPTSTR, + int, BOOL)) + GetProcAddress(hLib, + "SHGetSpecialFolderPathA"); + } + + if (!g_PyArg_ParseTuple(args, "s", &name)) + return NULL; + + if (!My_SHGetSpecialFolderPath) { + g_PyErr_Format(g_PyExc_OSError, "function not available"); + return NULL; + } + + for (i = 0; i < DIM(csidl_names); ++i) { + if (0 == strcmpi(csidl_names[i].name, name)) { + int nFolder; + nFolder = csidl_names[i].nFolder; + if (My_SHGetSpecialFolderPath(NULL, lpszPath, + nFolder, 0)) + return g_Py_BuildValue("s", lpszPath); + else { + g_PyErr_Format(g_PyExc_OSError, + "no such folder (%s)", name); + return NULL; + } + + } + }; + g_PyErr_Format(g_PyExc_ValueError, "unknown CSIDL (%s)", name); + return NULL; +} + +static PyObject *CreateShortcut(PyObject *self, PyObject *args) +{ + char *path; /* path and filename */ + char *description; + char *filename; + + char *arguments = NULL; + char *iconpath = NULL; + int iconindex = 0; + char *workdir = NULL; + + WCHAR wszFilename[MAX_PATH]; + + IShellLink *ps1 = NULL; + IPersistFile *pPf = NULL; + + HRESULT hr; + + hr = CoInitialize(NULL); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "CoInitialize failed, error 0x%x", hr); + goto error; + } + + if (!g_PyArg_ParseTuple(args, "sss|sssi", + &path, &description, &filename, + &arguments, &workdir, &iconpath, &iconindex)) + return NULL; + + hr = CoCreateInstance(&CLSID_ShellLink, + NULL, + CLSCTX_INPROC_SERVER, + &IID_IShellLink, + &ps1); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "CoCreateInstance failed, error 0x%x", hr); + goto error; + } + + hr = ps1->lpVtbl->QueryInterface(ps1, &IID_IPersistFile, + (void **)&pPf); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "QueryInterface(IPersistFile) error 0x%x", hr); + goto error; + } + + + hr = ps1->lpVtbl->SetPath(ps1, path); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "SetPath() failed, error 0x%x", hr); + goto error; + } + + hr = ps1->lpVtbl->SetDescription(ps1, description); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "SetDescription() failed, error 0x%x", hr); + goto error; + } + + if (arguments) { + hr = ps1->lpVtbl->SetArguments(ps1, arguments); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "SetArguments() error 0x%x", hr); + goto error; + } + } + + if (iconpath) { + hr = ps1->lpVtbl->SetIconLocation(ps1, iconpath, iconindex); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "SetIconLocation() error 0x%x", hr); + goto error; + } + } + + if (workdir) { + hr = ps1->lpVtbl->SetWorkingDirectory(ps1, workdir); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "SetWorkingDirectory() error 0x%x", hr); + goto error; + } + } + + MultiByteToWideChar(CP_ACP, 0, + filename, -1, + wszFilename, MAX_PATH); + + hr = pPf->lpVtbl->Save(pPf, wszFilename, TRUE); + if (FAILED(hr)) { + g_PyErr_Format(g_PyExc_OSError, + "Save() failed, error 0x%x", hr); + goto error; + } + + pPf->lpVtbl->Release(pPf); + ps1->lpVtbl->Release(ps1); + CoUninitialize(); + return g_Py_BuildValue(""); + + error: + if (pPf) + pPf->lpVtbl->Release(pPf); + + if (ps1) + ps1->lpVtbl->Release(ps1); + + CoUninitialize(); + + return NULL; +} + +#define METH_VARARGS 0x0001 + +PyMethodDef meth[] = { + {"create_shortcut", CreateShortcut, METH_VARARGS, NULL}, + {"get_special_folder_path", GetSpecialFolderPath, METH_VARARGS, NULL}, + {"file_created", FileCreated, METH_VARARGS, NULL}, + {"directory_created", DirectoryCreated, METH_VARARGS, NULL}, +}; + +/* + * This function returns one of the following error codes: + * 1 if the Python-dll does not export the functions we need + * 2 if no install-script is specified in pathname + * 3 if the install-script file could not be opened + * the return value of PyRun_SimpleFile() otherwise, + * which is 0 if everything is ok, -1 if an exception had occurred + * in the install-script. + */ + +static int +run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv) +{ + DECLPROC(hPython, void, Py_Initialize, (void)); + DECLPROC(hPython, int, PySys_SetArgv, (int, char **)); + DECLPROC(hPython, int, PyRun_SimpleFile, (FILE *, char *)); + DECLPROC(hPython, void, Py_Finalize, (void)); + DECLPROC(hPython, PyObject *, PyImport_ImportModule, (char *)); + DECLPROC(hPython, int, PyObject_SetAttrString, + (PyObject *, char *, PyObject *)); + DECLPROC(hPython, PyObject *, PyObject_GetAttrString, + (PyObject *, char *)); + DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...)); + DECLPROC(hPython, PyObject *, PyCFunction_New, + (PyMethodDef *, PyObject *)); + DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...)); + DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *)); + + PyObject *mod; + + int result = 0; + FILE *fp; + + if (!Py_Initialize || !PySys_SetArgv + || !PyRun_SimpleFile || !Py_Finalize) + return 1; + + if (!PyImport_ImportModule || !PyObject_SetAttrString + || !Py_BuildValue) + return 1; + + if (!PyCFunction_New || !PyArg_ParseTuple || !PyErr_Format) + return 1; + + if (!PyObject_GetAttrString) + return 1; + + g_Py_BuildValue = Py_BuildValue; + g_PyArg_ParseTuple = PyArg_ParseTuple; + g_PyErr_Format = PyErr_Format; + + if (pathname == NULL || pathname[0] == '\0') + return 2; + + fp = fopen(pathname, "r"); + if (!fp) { + fprintf(stderr, "Could not open postinstall-script %s\n", + pathname); + return 3; + } + + SetDlgItemText(hDialog, IDC_INFO, "Running Script..."); + + Py_Initialize(); + + mod = PyImport_ImportModule("__builtin__"); + if (mod) { + int i; + + g_PyExc_ValueError = PyObject_GetAttrString(mod, + "ValueError"); + g_PyExc_OSError = PyObject_GetAttrString(mod, "OSError"); + for (i = 0; i < DIM(meth); ++i) { + PyObject_SetAttrString(mod, meth[i].ml_name, + PyCFunction_New(&meth[i], NULL)); + } + } + + PySys_SetArgv(argc, argv); + result = PyRun_SimpleFile(fp, pathname); + Py_Finalize(); + + fclose(fp); + + return result; +} + +static BOOL SystemError(int error, char *msg) +{ + char Buffer[1024]; + int n; + + if (error) { + LPVOID lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&lpMsgBuf, + 0, + NULL + ); + strncpy(Buffer, lpMsgBuf, sizeof(Buffer)); + LocalFree(lpMsgBuf); + } else + Buffer[0] = '\0'; + n = lstrlen(Buffer); + _snprintf(Buffer+n, sizeof(Buffer)-n, msg); + MessageBox(hwndMain, Buffer, "Runtime Error", MB_OK | MB_ICONSTOP); + return FALSE; +} + +static BOOL AskOverwrite(char *filename) +{ + int result; + again: + if (allow_overwrite == ALWAYS) + return TRUE; + if (allow_overwrite == NEVER) + return FALSE; + result = MessageBox(hDialog, + "Overwrite existing files?\n" + "\n" + "Press YES to ALWAYS overwrite existing files,\n" + "press NO to NEVER overwrite existing files.", + "Overwrite options", + MB_YESNO | MB_ICONQUESTION); + if (result == IDYES) + allow_overwrite = ALWAYS; + else if (result == IDNO) + allow_overwrite = NEVER; + goto again; +} + +static BOOL notify (int code, char *fmt, ...) +{ + char Buffer[1024]; + va_list marker; + BOOL result = TRUE; + int a, b; + char *cp; + + va_start(marker, fmt); + _vsnprintf(Buffer, sizeof(Buffer), fmt, marker); + + switch (code) { +/* Questions */ + case CAN_OVERWRITE: + result = AskOverwrite(Buffer); + break; + +/* Information notification */ + case DIR_CREATED: + if (logfile) + fprintf(logfile, "100 Made Dir: %s\n", fmt); + break; + + case FILE_CREATED: + if (logfile) + fprintf(logfile, "200 File Copy: %s\n", fmt); + goto add_to_filelist_label; + break; + + case FILE_OVERWRITTEN: + if (logfile) + fprintf(logfile, "200 File Overwrite: %s\n", fmt); + add_to_filelist_label: + if ((cp = strrchr(fmt, '.')) && (0 == strcmp (cp, ".py"))) + add_to_filelist(fmt); + break; + +/* Error Messages */ + case ZLIB_ERROR: + MessageBox(GetFocus(), Buffer, "Error", + MB_OK | MB_ICONWARNING); + break; + + case SYSTEM_ERROR: + SystemError(GetLastError(), Buffer); + break; + + case NUM_FILES: + a = va_arg(marker, int); + b = va_arg(marker, int); + SendMessage(hDialog, WM_NUMFILES, 0, MAKELPARAM(0, a)); + SendMessage(hDialog, WM_NEXTFILE, b,(LPARAM)fmt); + } + va_end(marker); + + return result; +} + +static char *MapExistingFile(char *pathname, DWORD *psize) +{ + HANDLE hFile, hFileMapping; + DWORD nSizeLow, nSizeHigh; + char *data; + + hFile = CreateFile(pathname, + GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return NULL; + nSizeLow = GetFileSize(hFile, &nSizeHigh); + hFileMapping = CreateFileMapping(hFile, + NULL, PAGE_READONLY, 0, 0, NULL); + CloseHandle(hFile); + + if (hFileMapping == INVALID_HANDLE_VALUE) + return NULL; + + data = MapViewOfFile(hFileMapping, + FILE_MAP_READ, 0, 0, 0); + + CloseHandle(hFileMapping); + *psize = nSizeLow; + return data; +} + + +static void create_bitmap(HWND hwnd) +{ + BITMAPFILEHEADER *bfh; + BITMAPINFO *bi; + HDC hdc; + + if (!bitmap_bytes) + return; + + if (hBitmap) + return; + + hdc = GetDC(hwnd); + + bfh = (BITMAPFILEHEADER *)bitmap_bytes; + bi = (BITMAPINFO *)(bitmap_bytes + sizeof(BITMAPFILEHEADER)); + + hBitmap = CreateDIBitmap(hdc, + &bi->bmiHeader, + CBM_INIT, + bitmap_bytes + bfh->bfOffBits, + bi, + DIB_RGB_COLORS); + ReleaseDC(hwnd, hdc); +} + +static char *ExtractIniFile(char *data, DWORD size, int *pexe_size) +{ + /* read the end of central directory record */ + struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof + (struct eof_cdir)]; + + int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir - + pe->ofsCDir; + + int ofs = arc_start - sizeof (struct meta_data_hdr); + + /* read meta_data info */ + struct meta_data_hdr *pmd = (struct meta_data_hdr *)&data[ofs]; + char *src, *dst; + char *ini_file; + char tempdir[MAX_PATH]; + + if (pe->tag != 0x06054b50) { + return NULL; + } + + if (pmd->tag != 0x1234567A || ofs < 0) { + return NULL; + } + + if (pmd->bitmap_size) { + /* Store pointer to bitmap bytes */ + bitmap_bytes = (char *)pmd - pmd->uncomp_size - pmd->bitmap_size; + } + + *pexe_size = ofs - pmd->uncomp_size - pmd->bitmap_size; + + src = ((char *)pmd) - pmd->uncomp_size; + ini_file = malloc(MAX_PATH); /* will be returned, so do not free it */ + if (!ini_file) + return NULL; + if (!GetTempPath(sizeof(tempdir), tempdir) + || !GetTempFileName(tempdir, "~du", 0, ini_file)) { + SystemError(GetLastError(), + "Could not create temporary file"); + return NULL; + } + + dst = map_new_file(CREATE_ALWAYS, ini_file, NULL, pmd->uncomp_size, + 0, 0, NULL/*notify*/); + if (!dst) + return NULL; + memcpy(dst, src, pmd->uncomp_size); + UnmapViewOfFile(dst); + return ini_file; +} + +static void PumpMessages(void) +{ + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +LRESULT CALLBACK +WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HDC hdc; + HFONT hFont; + int h; + PAINTSTRUCT ps; + switch (msg) { + case WM_PAINT: + hdc = BeginPaint(hwnd, &ps); + h = GetSystemMetrics(SM_CYSCREEN) / 10; + hFont = CreateFont(h, 0, 0, 0, 700, TRUE, + 0, 0, 0, 0, 0, 0, 0, "Times Roman"); + hFont = SelectObject(hdc, hFont); + SetBkMode(hdc, TRANSPARENT); + TextOut(hdc, 15, 15, title, strlen(title)); + SetTextColor(hdc, RGB(255, 255, 255)); + TextOut(hdc, 10, 10, title, strlen(title)); + DeleteObject(SelectObject(hdc, hFont)); + EndPaint(hwnd, &ps); + return 0; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +static HWND CreateBackground(char *title) +{ + WNDCLASS wc; + HWND hwnd; + char buffer[4096]; + + wc.style = CS_VREDRAW | CS_HREDRAW; + wc.lpfnWndProc = WindowProc; + wc.cbWndExtra = 0; + wc.cbClsExtra = 0; + wc.hInstance = GetModuleHandle(NULL); + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 128)); + wc.lpszMenuName = NULL; + wc.lpszClassName = "SetupWindowClass"; + + if (!RegisterClass(&wc)) + MessageBox(hwndMain, + "Could not register window class", + "Setup.exe", MB_OK); + + wsprintf(buffer, "Setup %s", title); + hwnd = CreateWindow("SetupWindowClass", + buffer, + 0, + 0, 0, + GetSystemMetrics(SM_CXFULLSCREEN), + GetSystemMetrics(SM_CYFULLSCREEN), + NULL, + NULL, + GetModuleHandle(NULL), + NULL); + ShowWindow(hwnd, SW_SHOWMAXIMIZED); + UpdateWindow(hwnd); + return hwnd; +} + +/* + * Center a window on the screen + */ +static void CenterWindow(HWND hwnd) +{ + RECT rc; + int w, h; + + GetWindowRect(hwnd, &rc); + w = GetSystemMetrics(SM_CXSCREEN); + h = GetSystemMetrics(SM_CYSCREEN); + MoveWindow(hwnd, + (w - (rc.right-rc.left))/2, + (h - (rc.bottom-rc.top))/2, + rc.right-rc.left, rc.bottom-rc.top, FALSE); +} + +#include + +BOOL CALLBACK +IntroDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LPNMHDR lpnm; + char Buffer[4096]; + + switch (msg) { + case WM_INITDIALOG: + create_bitmap(hwnd); + if(hBitmap) + SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE, + IMAGE_BITMAP, (LPARAM)hBitmap); + CenterWindow(GetParent(hwnd)); + wsprintf(Buffer, + "This Wizard will install %s on your computer. " + "Click Next to continue " + "or Cancel to exit the Setup Wizard.", + meta_name); + SetDlgItemText(hwnd, IDC_TITLE, Buffer); + SetDlgItemText(hwnd, IDC_INTRO_TEXT, info); + SetDlgItemText(hwnd, IDC_BUILD_INFO, build_info); + return FALSE; + + case WM_NOTIFY: + lpnm = (LPNMHDR) lParam; + + switch (lpnm->code) { + case PSN_SETACTIVE: + PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT); + break; + + case PSN_WIZNEXT: + break; + + case PSN_RESET: + break; + + default: + break; + } + } + return FALSE; +} + +#ifdef USE_OTHER_PYTHON_VERSIONS +/* These are really private variables used to communicate + * between StatusRoutine and CheckPythonExe + */ +char bound_image_dll[_MAX_PATH]; +int bound_image_major; +int bound_image_minor; + +static BOOL __stdcall StatusRoutine(IMAGEHLP_STATUS_REASON reason, + PSTR ImageName, + PSTR DllName, + ULONG Va, + ULONG Parameter) +{ + char fname[_MAX_PATH]; + int int_version; + + switch(reason) { + case BindOutOfMemory: + case BindRvaToVaFailed: + case BindNoRoomInImage: + case BindImportProcedureFailed: + break; + + case BindImportProcedure: + case BindForwarder: + case BindForwarderNOT: + case BindImageModified: + case BindExpandFileHeaders: + case BindImageComplete: + case BindSymbolsNotUpdated: + case BindMismatchedSymbols: + case BindImportModuleFailed: + break; + + case BindImportModule: + if (1 == sscanf(DllName, "python%d", &int_version)) { + SearchPath(NULL, DllName, NULL, sizeof(fname), + fname, NULL); + strcpy(bound_image_dll, fname); + bound_image_major = int_version / 10; + bound_image_minor = int_version % 10; + OutputDebugString("BOUND "); + OutputDebugString(fname); + OutputDebugString("\n"); + } + break; + } + return TRUE; +} + +/* + */ +static LPSTR get_sys_prefix(LPSTR exe, LPSTR dll) +{ + void (__cdecl * Py_Initialize)(void); + void (__cdecl * Py_SetProgramName)(char *); + void (__cdecl * Py_Finalize)(void); + void* (__cdecl * PySys_GetObject)(char *); + void (__cdecl * PySys_SetArgv)(int, char **); + char* (__cdecl * Py_GetPrefix)(void); + char* (__cdecl * Py_GetPath)(void); + HINSTANCE hPython; + LPSTR prefix = NULL; + int (__cdecl * PyRun_SimpleString)(char *); + + { + char Buffer[256]; + wsprintf(Buffer, "PYTHONHOME=%s", exe); + *strrchr(Buffer, '\\') = '\0'; +// MessageBox(GetFocus(), Buffer, "PYTHONHOME", MB_OK); + _putenv(Buffer); + _putenv("PYTHONPATH="); + } + + hPython = LoadLibrary(dll); + if (!hPython) + return NULL; + Py_Initialize = (void (*)(void))GetProcAddress + (hPython,"Py_Initialize"); + + PySys_SetArgv = (void (*)(int, char **))GetProcAddress + (hPython,"PySys_SetArgv"); + + PyRun_SimpleString = (int (*)(char *))GetProcAddress + (hPython,"PyRun_SimpleString"); + + Py_SetProgramName = (void (*)(char *))GetProcAddress + (hPython,"Py_SetProgramName"); + + PySys_GetObject = (void* (*)(char *))GetProcAddress + (hPython,"PySys_GetObject"); + + Py_GetPrefix = (char * (*)(void))GetProcAddress + (hPython,"Py_GetPrefix"); + + Py_GetPath = (char * (*)(void))GetProcAddress + (hPython,"Py_GetPath"); + + Py_Finalize = (void (*)(void))GetProcAddress(hPython, + "Py_Finalize"); + Py_SetProgramName(exe); + Py_Initialize(); + PySys_SetArgv(1, &exe); + + MessageBox(GetFocus(), Py_GetPrefix(), "PREFIX", MB_OK); + MessageBox(GetFocus(), Py_GetPath(), "PATH", MB_OK); + + Py_Finalize(); + FreeLibrary(hPython); + + return prefix; +} + +static BOOL +CheckPythonExe(LPSTR pathname, LPSTR version, int *pmajor, int *pminor) +{ + bound_image_dll[0] = '\0'; + if (!BindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES, + pathname, + NULL, + NULL, + StatusRoutine)) + return SystemError(0, "Could not bind image"); + if (bound_image_dll[0] == '\0') + return SystemError(0, "Does not seem to be a python executable"); + *pmajor = bound_image_major; + *pminor = bound_image_minor; + if (version && *version) { + char core_version[12]; + wsprintf(core_version, "%d.%d", bound_image_major, bound_image_minor); + if (strcmp(version, core_version)) + return SystemError(0, "Wrong Python version"); + } + get_sys_prefix(pathname, bound_image_dll); + return TRUE; +} + +/* + * Browse for other python versions. Insert it into the listbox specified + * by hwnd. version, if not NULL or empty, is the version required. + */ +static BOOL GetOtherPythonVersion(HWND hwnd, LPSTR version) +{ + char vers_name[_MAX_PATH + 80]; + DWORD itemindex; + OPENFILENAME of; + char pathname[_MAX_PATH]; + DWORD result; + + strcpy(pathname, "python.exe"); + + memset(&of, 0, sizeof(of)); + of.lStructSize = sizeof(OPENFILENAME); + of.hwndOwner = GetParent(hwnd); + of.hInstance = NULL; + of.lpstrFilter = "python.exe\0python.exe\0"; + of.lpstrCustomFilter = NULL; + of.nMaxCustFilter = 0; + of.nFilterIndex = 1; + of.lpstrFile = pathname; + of.nMaxFile = sizeof(pathname); + of.lpstrFileTitle = NULL; + of.nMaxFileTitle = 0; + of.lpstrInitialDir = NULL; + of.lpstrTitle = "Python executable"; + of.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + of.lpstrDefExt = "exe"; + + result = GetOpenFileName(&of); + if (result) { + int major, minor; + if (!CheckPythonExe(pathname, version, &major, &minor)) { + return FALSE; + } + *strrchr(pathname, '\\') = '\0'; + wsprintf(vers_name, "Python Version %d.%d in %s", + major, minor, pathname); + itemindex = SendMessage(hwnd, LB_INSERTSTRING, -1, + (LPARAM)(LPSTR)vers_name); + SendMessage(hwnd, LB_SETCURSEL, itemindex, 0); + SendMessage(hwnd, LB_SETITEMDATA, itemindex, + (LPARAM)(LPSTR)strdup(pathname)); + return TRUE; + } + return FALSE; +} +#endif /* USE_OTHER_PYTHON_VERSIONS */ + + +/* + * Fill the listbox specified by hwnd with all python versions found + * in the registry. version, if not NULL or empty, is the version + * required. + */ +static BOOL GetPythonVersions(HWND hwnd, HKEY hkRoot, LPSTR version) +{ + DWORD index = 0; + char core_version[80]; + HKEY hKey; + BOOL result = TRUE; + DWORD bufsize; + + if (ERROR_SUCCESS != RegOpenKeyEx(hkRoot, + "Software\\Python\\PythonCore", + 0, KEY_READ, &hKey)) + return FALSE; + bufsize = sizeof(core_version); + while (ERROR_SUCCESS == RegEnumKeyEx(hKey, index, + core_version, &bufsize, NULL, + NULL, NULL, NULL)) { + char subkey_name[80], vers_name[80], prefix_buf[MAX_PATH+1]; + int itemindex; + DWORD value_size; + HKEY hk; + + bufsize = sizeof(core_version); + ++index; + if (version && *version && strcmp(version, core_version)) + continue; + + wsprintf(vers_name, "Python Version %s (found in registry)", + core_version); + wsprintf(subkey_name, + "Software\\Python\\PythonCore\\%s\\InstallPath", + core_version); + value_size = sizeof(subkey_name); + if (ERROR_SUCCESS == RegOpenKeyEx(hkRoot, subkey_name, 0, KEY_READ, &hk)) { + if (ERROR_SUCCESS == RegQueryValueEx(hk, NULL, NULL, NULL, prefix_buf, + &value_size)) { + itemindex = SendMessage(hwnd, LB_ADDSTRING, 0, + (LPARAM)(LPSTR)vers_name); + SendMessage(hwnd, LB_SETITEMDATA, itemindex, + (LPARAM)(LPSTR)strdup(prefix_buf)); + } + RegCloseKey(hk); + } + } + RegCloseKey(hKey); + return result; +} + +/* Return the installation scheme depending on Python version number */ +SCHEME *GetScheme(int major, int minor) +{ + if (major > 2) + return new_scheme; + else if((major == 2) && (minor >= 2)) + return new_scheme; + return old_scheme; +} + +BOOL CALLBACK +SelectPythonDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LPNMHDR lpnm; + + switch (msg) { + case WM_INITDIALOG: + if (hBitmap) + SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE, + IMAGE_BITMAP, (LPARAM)hBitmap); + GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST), + HKEY_LOCAL_MACHINE, target_version); + GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST), + HKEY_CURRENT_USER, target_version); + { /* select the last entry which is the highest python + version found */ + int count; + count = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, + LB_GETCOUNT, 0, 0); + if (count && count != LB_ERR) + SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, LB_SETCURSEL, + count-1, 0); + + /* If a specific Python version is required, + * display a prominent notice showing this fact. + */ + if (target_version && target_version[0]) { + char buffer[4096]; + wsprintf(buffer, + "Python %s is required for this package. " + "Select installation to use:", + target_version); + SetDlgItemText(hwnd, IDC_TITLE, buffer); + } + + if (count == 0) { + char Buffer[4096]; + char *msg; + if (target_version && target_version[0]) { + wsprintf(Buffer, + "Python version %s required, which was not found" + " in the registry.", target_version); + msg = Buffer; + } else + msg = "No Python installation found in the registry."; + MessageBox(hwnd, msg, "Cannot install", + MB_OK | MB_ICONSTOP); + } + } + goto UpdateInstallDir; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) { +/* + case IDC_OTHERPYTHON: + if (GetOtherPythonVersion(GetDlgItem(hwnd, IDC_VERSIONS_LIST), + target_version)) + goto UpdateInstallDir; + break; +*/ + case IDC_VERSIONS_LIST: + switch (HIWORD(wParam)) { + int id; + char *cp; + case LBN_SELCHANGE: + UpdateInstallDir: + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK | PSWIZB_NEXT); + id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, + LB_GETCURSEL, 0, 0); + if (id == LB_ERR) { + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK); + SetDlgItemText(hwnd, IDC_PATH, ""); + SetDlgItemText(hwnd, IDC_INSTALL_PATH, ""); + strcpy(python_dir, ""); + strcpy(pythondll, ""); + } else { + char *pbuf; + int result; + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK | PSWIZB_NEXT); + /* Get the python directory */ + cp = (LPSTR)SendDlgItemMessage(hwnd, + IDC_VERSIONS_LIST, + LB_GETITEMDATA, + id, + 0); + strcpy(python_dir, cp); + SetDlgItemText(hwnd, IDC_PATH, python_dir); + /* retrieve the python version and pythondll to use */ + result = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, + LB_GETTEXTLEN, (WPARAM)id, 0); + pbuf = (char *)malloc(result + 1); + if (pbuf) { + /* guess the name of the python-dll */ + SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, + LB_GETTEXT, (WPARAM)id, + (LPARAM)pbuf); + result = sscanf(pbuf, "Python Version %d.%d", + &py_major, &py_minor); + if (result == 2) +#ifdef _DEBUG + wsprintf(pythondll, "c:\\python22\\PCBuild\\python%d%d_d.dll", + py_major, py_minor); +#else + wsprintf(pythondll, "python%d%d.dll", + py_major, py_minor); +#endif + free(pbuf); + } else + strcpy(pythondll, ""); + /* retrieve the scheme for this version */ + { + char install_path[_MAX_PATH]; + SCHEME *scheme = GetScheme(py_major, py_minor); + strcpy(install_path, python_dir); + if (install_path[strlen(install_path)-1] != '\\') + strcat(install_path, "\\"); + strcat(install_path, scheme[0].prefix); + SetDlgItemText(hwnd, IDC_INSTALL_PATH, install_path); + } + } + } + break; + } + return 0; + + case WM_NOTIFY: + lpnm = (LPNMHDR) lParam; + + switch (lpnm->code) { + int id; + case PSN_SETACTIVE: + id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, + LB_GETCURSEL, 0, 0); + if (id == LB_ERR) + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK); + else + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK | PSWIZB_NEXT); + break; + + case PSN_WIZNEXT: + break; + + case PSN_WIZFINISH: + break; + + case PSN_RESET: + break; + + default: + break; + } + } + return 0; +} + +static BOOL OpenLogfile(char *dir) +{ + char buffer[_MAX_PATH+1]; + time_t ltime; + struct tm *now; + long result; + HKEY hKey, hSubkey; + char subkey_name[256]; + static char KeyName[] = + "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; + DWORD disposition; + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + KeyName, + 0, + KEY_CREATE_SUB_KEY, + &hKey); + if (result != ERROR_SUCCESS) { + if (result == ERROR_ACCESS_DENIED) { + MessageBox(GetFocus(), + "You do not seem to have sufficient access rights\n" + "on this machine to install this software", + NULL, + MB_OK | MB_ICONSTOP); + return FALSE; + } else { + MessageBox(GetFocus(), KeyName, "Could not open key", MB_OK); + } + } + + sprintf(buffer, "%s\\%s-wininst.log", dir, meta_name); + logfile = fopen(buffer, "a"); + time(<ime); + now = localtime(<ime); + strftime(buffer, sizeof(buffer), + "*** Installation started %Y/%m/%d %H:%M ***\n", + localtime(<ime)); + fprintf(logfile, buffer); + fprintf(logfile, "Source: %s\n", modulename); + + sprintf(subkey_name, "%s-py%d.%d", meta_name, py_major, py_minor); + + result = RegCreateKeyEx(hKey, subkey_name, + 0, NULL, 0, + KEY_WRITE, + NULL, + &hSubkey, + &disposition); + + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), subkey_name, "Could not create key", MB_OK); + + RegCloseKey(hKey); + + if (disposition == REG_CREATED_NEW_KEY) + fprintf(logfile, "020 Reg DB Key: [%s]%s\n", KeyName, subkey_name); + + sprintf(buffer, "Python %d.%d %s", py_major, py_minor, title); + + result = RegSetValueEx(hSubkey, "DisplayName", + 0, + REG_SZ, + buffer, + strlen(buffer)+1); + + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK); + + fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n", + KeyName, subkey_name, "DisplayName", buffer); + + { + FILE *fp; + sprintf(buffer, "%s\\Remove%s.exe", dir, meta_name); + fp = fopen(buffer, "wb"); + fwrite(arc_data, exe_size, 1, fp); + fclose(fp); + + sprintf(buffer, "\"%s\\Remove%s.exe\" -u \"%s\\%s-wininst.log\"", + dir, meta_name, dir, meta_name); + + result = RegSetValueEx(hSubkey, "UninstallString", + 0, + REG_SZ, + buffer, + strlen(buffer)+1); + + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK); + + fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n", + KeyName, subkey_name, "UninstallString", buffer); + } + return TRUE; +} + +static void CloseLogfile(void) +{ + char buffer[_MAX_PATH+1]; + time_t ltime; + struct tm *now; + + time(<ime); + now = localtime(<ime); + strftime(buffer, sizeof(buffer), + "*** Installation finished %Y/%m/%d %H:%M ***\n", + localtime(<ime)); + fprintf(logfile, buffer); + if (logfile) + fclose(logfile); +} + +BOOL CALLBACK +InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LPNMHDR lpnm; + char Buffer[4096]; + SCHEME *scheme; + + switch (msg) { + case WM_INITDIALOG: + if (hBitmap) + SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE, + IMAGE_BITMAP, (LPARAM)hBitmap); + wsprintf(Buffer, + "Click Next to begin the installation of %s. " + "If you want to review or change any of your " + " installation settings, click Back. " + "Click Cancel to exit the wizard.", + meta_name); + SetDlgItemText(hwnd, IDC_TITLE, Buffer); + break; + + case WM_NUMFILES: + SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, lParam); + PumpMessages(); + return TRUE; + + case WM_NEXTFILE: + SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, wParam, + 0); + SetDlgItemText(hwnd, IDC_INFO, (LPSTR)lParam); + PumpMessages(); + return TRUE; + + case WM_NOTIFY: + lpnm = (LPNMHDR) lParam; + + switch (lpnm->code) { + case PSN_SETACTIVE: + PropSheet_SetWizButtons(GetParent(hwnd), + PSWIZB_BACK | PSWIZB_NEXT); + break; + + case PSN_WIZFINISH: + break; + + case PSN_WIZNEXT: + /* Handle a Next button click here */ + hDialog = hwnd; + + /* Make sure the installation directory name ends in a */ + /* backslash */ + if (python_dir[strlen(python_dir)-1] != '\\') + strcat(python_dir, "\\"); + /* Strip the trailing backslash again */ + python_dir[strlen(python_dir)-1] = '\0'; + + if (!OpenLogfile(python_dir)) + break; + +/* + * The scheme we have to use depends on the Python version... + if sys.version < "2.2": + WINDOWS_SCHEME = { + 'purelib': '$base', + 'platlib': '$base', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } + else: + WINDOWS_SCHEME = { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } +*/ + scheme = GetScheme(py_major, py_minor); + + /* Extract all files from the archive */ + SetDlgItemText(hwnd, IDC_TITLE, "Installing files..."); + success = unzip_archive(scheme, + python_dir, arc_data, + arc_size, notify); + /* Compile the py-files */ + if (pyc_compile) { + int errors; + HINSTANCE hPython; + SetDlgItemText(hwnd, IDC_TITLE, + "Compiling files to .pyc..."); + + SetDlgItemText(hDialog, IDC_INFO, "Loading python..."); + hPython = LoadLibrary(pythondll); + if (hPython) { + errors = compile_filelist(hPython, FALSE); + FreeLibrary(hPython); + } + /* Compilation errors are intentionally ignored: + * Python2.0 contains a bug which will result + * in sys.path containing garbage under certain + * circumstances, and an error message will only + * confuse the user. + */ + } + if (pyo_compile) { + int errors; + HINSTANCE hPython; + SetDlgItemText(hwnd, IDC_TITLE, + "Compiling files to .pyo..."); + + SetDlgItemText(hDialog, IDC_INFO, "Loading python..."); + hPython = LoadLibrary(pythondll); + if (hPython) { + errors = compile_filelist(hPython, TRUE); + FreeLibrary(hPython); + } + /* Errors ignored: see above */ + } + + + break; + + case PSN_RESET: + break; + + default: + break; + } + } + return 0; +} + + +BOOL CALLBACK +FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LPNMHDR lpnm; + + switch (msg) { + case WM_INITDIALOG: + if (hBitmap) + SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE, + IMAGE_BITMAP, (LPARAM)hBitmap); + if (!success) + SetDlgItemText(hwnd, IDC_INFO, "Installation failed."); + + /* async delay: will show the dialog box completely before + the install_script is started */ + PostMessage(hwnd, WM_USER, 0, 0L); + return TRUE; + + case WM_USER: + + if (install_script && install_script[0]) { + char fname[MAX_PATH]; + char *tempname; + FILE *fp; + char buffer[4096]; + int n; + HCURSOR hCursor; + HINSTANCE hPython; + + char *argv[3] = {NULL, "-install", NULL}; + + SetDlgItemText(hwnd, IDC_TITLE, + "Please wait while running postinstall script..."); + strcpy(fname, python_dir); + strcat(fname, "\\Scripts\\"); + strcat(fname, install_script); + + if (logfile) + fprintf(logfile, "300 Run Script: [%s]%s\n", pythondll, fname); + + tempname = tmpnam(NULL); + + if (!freopen(tempname, "a", stderr)) + MessageBox(GetFocus(), "freopen stderr", NULL, MB_OK); + if (!freopen(tempname, "a", stdout)) + MessageBox(GetFocus(), "freopen stdout", NULL, MB_OK); +/* + if (0 != setvbuf(stdout, NULL, _IONBF, 0)) + MessageBox(GetFocus(), "setvbuf stdout", NULL, MB_OK); +*/ + hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT)); + + argv[0] = fname; + + hPython = LoadLibrary(pythondll); + if (hPython) { + int result; + result = run_installscript(hPython, fname, 2, argv); + if (-1 == result) { + fprintf(stderr, "*** run_installscript: internal error 0x%X ***\n", result); + } + FreeLibrary(hPython); + } else { + fprintf(stderr, "*** Could not load Python ***"); + } + fflush(stderr); + fflush(stdout); + + fp = fopen(tempname, "rb"); + n = fread(buffer, 1, sizeof(buffer), fp); + fclose(fp); + remove(tempname); + + buffer[n] = '\0'; + + SetDlgItemText(hwnd, IDC_INFO, buffer); + SetDlgItemText(hwnd, IDC_TITLE, + "Postinstall script finished.\n" + "Click the Finish button to exit the Setup wizard."); + + SetCursor(hCursor); + CloseLogfile(); + } + + return TRUE; + + case WM_NOTIFY: + lpnm = (LPNMHDR) lParam; + + switch (lpnm->code) { + case PSN_SETACTIVE: /* Enable the Finish button */ + PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH); + break; + + case PSN_WIZNEXT: + break; + + case PSN_WIZFINISH: + break; + + case PSN_RESET: + break; + + default: + break; + } + } + return 0; +} + +void RunWizard(HWND hwnd) +{ + PROPSHEETPAGE psp = {0}; + HPROPSHEETPAGE ahpsp[4] = {0}; + PROPSHEETHEADER psh = {0}; + + /* Display module information */ + psp.dwSize = sizeof(psp); + psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER; + psp.hInstance = GetModuleHandle (NULL); + psp.lParam = 0; + psp.pfnDlgProc = IntroDlgProc; + psp.pszTemplate = MAKEINTRESOURCE(IDD_INTRO); + + ahpsp[0] = CreatePropertySheetPage(&psp); + + /* Select python version to use */ + psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER; + psp.pszTemplate = MAKEINTRESOURCE(IDD_SELECTPYTHON); + psp.pfnDlgProc = SelectPythonDlgProc; + + ahpsp[1] = CreatePropertySheetPage(&psp); + + /* Install the files */ + psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER; + psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLFILES); + psp.pfnDlgProc = InstallFilesDlgProc; + + ahpsp[2] = CreatePropertySheetPage(&psp); + + /* Show success or failure */ + psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER; + psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHED); + psp.pfnDlgProc = FinishedDlgProc; + + ahpsp[3] = CreatePropertySheetPage(&psp); + + /* Create the property sheet */ + psh.dwSize = sizeof(psh); + psh.hInstance = GetModuleHandle(NULL); + psh.hwndParent = hwnd; + psh.phpage = ahpsp; + psh.dwFlags = PSH_WIZARD/*97*//*|PSH_WATERMARK|PSH_HEADER*/; + psh.pszbmWatermark = NULL; + psh.pszbmHeader = NULL; + psh.nStartPage = 0; + psh.nPages = 4; + + PropertySheet(&psh); +} + +int DoInstall(void) +{ + char ini_buffer[4096]; + + /* Read installation information */ + GetPrivateProfileString("Setup", "title", "", ini_buffer, + sizeof(ini_buffer), ini_file); + unescape(title, ini_buffer, sizeof(title)); + + GetPrivateProfileString("Setup", "info", "", ini_buffer, + sizeof(ini_buffer), ini_file); + unescape(info, ini_buffer, sizeof(info)); + + GetPrivateProfileString("Setup", "build_info", "", build_info, + sizeof(build_info), ini_file); + + pyc_compile = GetPrivateProfileInt("Setup", "target_compile", 1, + ini_file); + pyo_compile = GetPrivateProfileInt("Setup", "target_optimize", 1, + ini_file); + + GetPrivateProfileString("Setup", "target_version", "", + target_version, sizeof(target_version), + ini_file); + + GetPrivateProfileString("metadata", "name", "", + meta_name, sizeof(meta_name), + ini_file); + + GetPrivateProfileString("Setup", "install_script", "", + install_script, sizeof(install_script), + ini_file); + + + hwndMain = CreateBackground(title); + + RunWizard(hwndMain); + + /* Clean up */ + UnmapViewOfFile(arc_data); + if (ini_file) + DeleteFile(ini_file); + + if (hBitmap) + DeleteObject(hBitmap); + + return 0; +} + +/*********************** uninstall section ******************************/ + +static int compare(const void *p1, const void *p2) +{ + return strcmp(*(char **)p2, *(char **)p1); +} + +/* + * Commit suicide (remove the uninstaller itself). + * + * Create a batch file to first remove the uninstaller + * (will succeed after it has finished), then the batch file itself. + * + * This technique has been demonstrated by Jeff Richter, + * MSJ 1/1996 + */ +void remove_exe(void) +{ + char exename[_MAX_PATH]; + char batname[_MAX_PATH]; + FILE *fp; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + GetModuleFileName(NULL, exename, sizeof(exename)); + sprintf(batname, "%s.bat", exename); + fp = fopen(batname, "w"); + fprintf(fp, ":Repeat\n"); + fprintf(fp, "del \"%s\"\n", exename); + fprintf(fp, "if exist \"%s\" goto Repeat\n", exename); + fprintf(fp, "del \"%s\"\n", batname); + fclose(fp); + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + if (CreateProcess(NULL, + batname, + NULL, + NULL, + FALSE, + CREATE_SUSPENDED | IDLE_PRIORITY_CLASS, + NULL, + "\\", + &si, + &pi)) { + SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); + CloseHandle(pi.hProcess); + ResumeThread(pi.hThread); + CloseHandle(pi.hThread); + } +} + +void DeleteRegistryKey(char *string) +{ + char *keyname; + char *subkeyname; + char *delim; + HKEY hKey; + long result; + char *line; + + line = strdup(string); /* so we can change it */ + + keyname = strchr(line, '['); + if (!keyname) + return; + ++keyname; + + subkeyname = strchr(keyname, ']'); + if (!subkeyname) + return; + *subkeyname++='\0'; + delim = strchr(subkeyname, '\n'); + if (delim) + *delim = '\0'; + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + keyname, + 0, + KEY_WRITE, + &hKey); + + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), string, "Could not open key", MB_OK); + else { + result = RegDeleteKey(hKey, subkeyname); + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), string, "Could not delete key", MB_OK); + RegCloseKey(hKey); + } + free(line); +} + +void DeleteRegistryValue(char *string) +{ + char *keyname; + char *valuename; + char *value; + HKEY hKey; + long result; + char *line; + + line = strdup(string); /* so we can change it */ + +/* Format is 'Reg DB Value: [key]name=value' */ + keyname = strchr(line, '['); + if (!keyname) + return; + ++keyname; + valuename = strchr(keyname, ']'); + if (!valuename) + return; + *valuename++ = '\0'; + value = strchr(valuename, '='); + if (!value) + return; + + *value++ = '\0'; + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + keyname, + 0, + KEY_WRITE, + &hKey); + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), string, "Could not open key", MB_OK); + else { + result = RegDeleteValue(hKey, valuename); + if (result != ERROR_SUCCESS) + MessageBox(GetFocus(), string, "Could not delete value", MB_OK); + RegCloseKey(hKey); + } + free(line); +} + +BOOL MyDeleteFile(char *line) +{ + char *pathname = strchr(line, ':'); + if (!pathname) + return FALSE; + ++pathname; + while (isspace(*pathname)) + ++pathname; + return DeleteFile(pathname); +} + +BOOL MyRemoveDirectory(char *line) +{ + char *pathname = strchr(line, ':'); + if (!pathname) + return FALSE; + ++pathname; + while (isspace(*pathname)) + ++pathname; + return RemoveDirectory(pathname); +} + +BOOL Run_RemoveScript(char *line) +{ + char *dllname; + char *scriptname; + static char lastscript[MAX_PATH]; + +/* Format is 'Run Scripts: [pythondll]scriptname' */ +/* XXX Currently, pythondll carries no path!!! */ + dllname = strchr(line, '['); + if (!dllname) + return FALSE; + ++dllname; + scriptname = strchr(dllname, ']'); + if (!scriptname) + return FALSE; + *scriptname++ = '\0'; + /* this function may be called more than one time with the same + script, only run it one time */ + if (strcmp(lastscript, scriptname)) { + HINSTANCE hPython; + char *argv[3] = {NULL, "-remove", NULL}; + char buffer[4096]; + FILE *fp; + char *tempname; + int n; + + argv[0] = scriptname; + + tempname = tmpnam(NULL); + + if (!freopen(tempname, "a", stderr)) + MessageBox(GetFocus(), "freopen stderr", NULL, MB_OK); + if (!freopen(tempname, "a", stdout)) + MessageBox(GetFocus(), "freopen stdout", NULL, MB_OK); + + hPython = LoadLibrary(dllname); + if (hPython) { + if (0x80000000 == run_installscript(hPython, scriptname, 2, argv)) + fprintf(stderr, "*** Could not load Python ***"); + FreeLibrary(hPython); + } + + fflush(stderr); + fflush(stdout); + + fp = fopen(tempname, "rb"); + n = fread(buffer, 1, sizeof(buffer), fp); + fclose(fp); + remove(tempname); + + buffer[n] = '\0'; + if (buffer[0]) + MessageBox(GetFocus(), buffer, "uninstall-script", MB_OK); + + strcpy(lastscript, scriptname); + } + return TRUE; +} + +int DoUninstall(int argc, char **argv) +{ + FILE *logfile; + char buffer[4096]; + int nLines = 0; + int i; + char *cp; + int nFiles = 0; + int nDirs = 0; + int nErrors = 0; + char **lines; + int lines_buffer_size = 10; + + if (argc != 3) { + MessageBox(NULL, + "Wrong number of args", + NULL, + MB_OK); + return 1; /* Error */ + } + if (strcmp(argv[1], "-u")) { + MessageBox(NULL, + "2. arg is not -u", + NULL, + MB_OK); + return 1; /* Error */ + } + + { + DWORD result; + HKEY hKey; + static char KeyName[] = + "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + KeyName, + 0, + KEY_CREATE_SUB_KEY, + &hKey); + if (result == ERROR_ACCESS_DENIED) { + MessageBox(GetFocus(), + "You do not seem to have sufficient access rights\n" + "on this machine to uninstall this software", + NULL, + MB_OK | MB_ICONSTOP); + return 1; /* Error */ + } + RegCloseKey(hKey); + } + + logfile = fopen(argv[2], "r"); + if (!logfile) { + MessageBox(NULL, + "could not open logfile", + NULL, + MB_OK); + return 1; /* Error */ + } + + lines = (char **)malloc(sizeof(char *) * lines_buffer_size); + if (!lines) + return SystemError(0, "Out of memory"); + + /* Read the whole logfile, realloacting the buffer */ + while (fgets(buffer, sizeof(buffer), logfile)) { + int len = strlen(buffer); + /* remove trailing white space */ + while (isspace(buffer[len-1])) + len -= 1; + buffer[len] = '\0'; + lines[nLines++] = strdup(buffer); + if (nLines >= lines_buffer_size) { + lines_buffer_size += 10; + lines = (char **)realloc(lines, + sizeof(char *) * lines_buffer_size); + if (!lines) + return SystemError(0, "Out of memory"); + } + } + fclose(logfile); + + /* Sort all the lines, so that highest 3-digit codes are first */ + qsort(&lines[0], nLines, sizeof(char *), + compare); + + if (IDYES != MessageBox(NULL, + "Are you sure you want to remove\n" + "this package from your computer?", + "Please confirm", + MB_YESNO | MB_ICONQUESTION)) + return 0; + + cp = ""; + for (i = 0; i < nLines; ++i) { + /* Ignore duplicate lines */ + if (strcmp(cp, lines[i])) { + int ign; + cp = lines[i]; + /* Parse the lines */ + if (2 == sscanf(cp, "%d Made Dir: %s", &ign, &buffer)) { + if (MyRemoveDirectory(cp)) + ++nDirs; + else { + int code = GetLastError(); + if (code != 2 && code != 3) { /* file or path not found */ + ++nErrors; + } + } + } else if (2 == sscanf(cp, "%d File Copy: %s", &ign, &buffer)) { + if (MyDeleteFile(cp)) + ++nFiles; + else { + int code = GetLastError(); + if (code != 2 && code != 3) { /* file or path not found */ + ++nErrors; + } + } + } else if (2 == sscanf(cp, "%d File Overwrite: %s", &ign, &buffer)) { + if (MyDeleteFile(cp)) + ++nFiles; + else { + int code = GetLastError(); + if (code != 2 && code != 3) { /* file or path not found */ + ++nErrors; + } + } + } else if (2 == sscanf(cp, "%d Reg DB Key: %s", &ign, &buffer)) { + DeleteRegistryKey(cp); + } else if (2 == sscanf(cp, "%d Reg DB Value: %s", &ign, &buffer)) { + DeleteRegistryValue(cp); + } else if (2 == sscanf(cp, "%d Run Script: %s", &ign, &buffer)) { + Run_RemoveScript(cp); + } + } + } + + if (DeleteFile(argv[2])) { + ++nFiles; + } else { + ++nErrors; + SystemError(GetLastError(), argv[2]); + } + if (nErrors) + wsprintf(buffer, + "%d files and %d directories removed\n" + "%d files or directories could not be removed", + nFiles, nDirs, nErrors); + else + wsprintf(buffer, "%d files and %d directories removed", + nFiles, nDirs); + MessageBox(NULL, buffer, "Uninstall Finished!", + MB_OK | MB_ICONINFORMATION); + remove_exe(); + return 0; +} + +int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, + LPSTR lpszCmdLine, INT nCmdShow) +{ + extern int __argc; + extern char **__argv; + char *basename; + + GetModuleFileName(NULL, modulename, sizeof(modulename)); + + /* Map the executable file to memory */ + arc_data = MapExistingFile(modulename, &arc_size); + if (!arc_data) { + SystemError(GetLastError(), "Could not open archive"); + return 1; + } + + /* OK. So this program can act as installer (self-extracting + * zip-file, or as uninstaller when started with '-u logfile' + * command line flags. + * + * The installer is usually started without command line flags, + * and the uninstaller is usually started with the '-u logfile' + * flag. What to do if some innocent user double-clicks the + * exe-file? + * The following implements a defensive strategy... + */ + + /* Try to extract the configuration data into a temporary file */ + ini_file = ExtractIniFile(arc_data, arc_size, &exe_size); + + if (ini_file) + return DoInstall(); + + if (!ini_file && __argc > 1) { + return DoUninstall(__argc, __argv); + } + + + basename = strrchr(modulename, '\\'); + if (basename) + ++basename; + + /* Last guess about the purpose of this program */ + if (basename && (0 == strncmp(basename, "Remove", 6))) + SystemError(0, "This program is normally started by windows"); + else + SystemError(0, "Setup program invalid or damaged"); + return 1; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/install.rc b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/install.rc new file mode 100644 index 00000000..b145e336 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/install.rc @@ -0,0 +1,220 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Neutral resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_BITMAP BITMAP DISCARDABLE "PythonPowered.bmp" +#endif // Neutral resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// German (Germany) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) +#ifdef _WIN32 +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // German (Germany) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_INTRO DIALOGEX 0, 0, 379, 178 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Setup" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "This Wizard will install %s on your computer. Click Next to continue or Cancel to exit the Setup Wizard.", + IDC_TITLE,125,10,247,20,NOT WS_GROUP + EDITTEXT IDC_INTRO_TEXT,125,31,247,131,ES_MULTILINE | ES_READONLY | + WS_VSCROLL | WS_HSCROLL | NOT WS_TABSTOP + CONTROL 110,IDC_BITMAP,"Static",SS_BITMAP | SS_CENTERIMAGE,6,8, + 104,163,WS_EX_CLIENTEDGE + LTEXT "",IDC_BUILD_INFO,125,163,247,8 +END + +IDD_SELECTPYTHON DIALOGEX 0, 0, 379, 178 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Setup" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "Select python installation to use:",IDC_TITLE,125,10, + 247,12,NOT WS_GROUP + EDITTEXT IDC_PATH,191,136,181,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Python Directory:",IDC_STATIC,125,137,55,8 + LISTBOX IDC_VERSIONS_LIST,125,24,247,106,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + CONTROL 110,IDC_BITMAP,"Static",SS_BITMAP | SS_CENTERIMAGE,6,8, + 104,163,WS_EX_CLIENTEDGE + EDITTEXT IDC_INSTALL_PATH,191,157,181,14,ES_AUTOHSCROLL | + ES_READONLY + LTEXT "Installation Directory:",IDC_STATIC,125,158,66,8 + PUSHBUTTON "Find other ...",IDC_OTHERPYTHON,322,7,50,14,NOT + WS_VISIBLE +END + +IDD_INSTALLFILES DIALOGEX 0, 0, 379, 178 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Setup" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "Click Next to begin the installation. If you want to review or change any of your installation settings, click Back. Click Cancel to exit the Wizard.", + IDC_TITLE,125,10,246,31,NOT WS_GROUP + CONTROL "Progress1",IDC_PROGRESS,"msctls_progress32",WS_BORDER, + 125,157,246,14 + CTEXT "Installation progress:",IDC_INFO,125,137,246,8 + CONTROL 110,IDC_BITMAP,"Static",SS_BITMAP | SS_CENTERIMAGE,6,8, + 104,163,WS_EX_CLIENTEDGE +END + +IDD_FINISHED DIALOGEX 0, 0, 379, 178 +STYLE WS_CHILD | WS_DISABLED | WS_CAPTION +CAPTION "Setup" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Click the Finish button to exit the Setup wizard.", + IDC_TITLE,125,10,247,31,NOT WS_GROUP + CONTROL 110,IDC_BITMAP,"Static",SS_BITMAP | SS_CENTERIMAGE,6,8, + 104,163,WS_EX_CLIENTEDGE + EDITTEXT IDC_INFO,125,40,247,131,ES_MULTILINE | ES_READONLY | + WS_VSCROLL | WS_HSCROLL | NOT WS_TABSTOP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_INTRO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 372 + VERTGUIDE, 125 + VERTGUIDE, 372 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + HORZGUIDE, 8 + HORZGUIDE, 31 + END + + IDD_SELECTPYTHON, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 372 + VERTGUIDE, 125 + VERTGUIDE, 372 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + HORZGUIDE, 8 + HORZGUIDE, 41 + END + + IDD_INSTALLFILES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 372 + VERTGUIDE, 125 + VERTGUIDE, 371 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + HORZGUIDE, 8 + HORZGUIDE, 41 + END + + IDD_FINISHED, DIALOG + BEGIN + LEFTMARGIN, 6 + RIGHTMARGIN, 372 + VERTGUIDE, 125 + VERTGUIDE, 372 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + HORZGUIDE, 8 + HORZGUIDE, 41 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/resource.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/resource.h new file mode 100644 index 00000000..709fe425 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/resource.h @@ -0,0 +1,38 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by install.rc +// +#define IDD_DIALOG1 101 +#define IDB_BITMAP1 103 +#define IDD_INTRO 107 +#define IDD_SELECTPYTHON 108 +#define IDD_INSTALLFILES 109 +#define IDD_FINISHED 110 +#define IDB_BITMAP 110 +#define IDC_EDIT1 1000 +#define IDC_TITLE 1000 +#define IDC_START 1001 +#define IDC_PROGRESS 1003 +#define IDC_INFO 1004 +#define IDC_PYTHON15 1006 +#define IDC_PATH 1007 +#define IDC_PYTHON16 1008 +#define IDC_INSTALL_PATH 1008 +#define IDC_PYTHON20 1009 +#define IDC_BROWSE 1010 +#define IDC_INTRO_TEXT 1021 +#define IDC_VERSIONS_LIST 1022 +#define IDC_BUILD_INFO 1024 +#define IDC_BITMAP 1025 +#define IDC_OTHERPYTHON 1026 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 112 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1028 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/wininst.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/wininst.dsp new file mode 100644 index 00000000..ef92e503 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/wininst.dsp @@ -0,0 +1,128 @@ +# Microsoft Developer Studio Project File - Name="wininst" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=wininst - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "wininst.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "wininst.mak" CFG="wininst - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "wininst - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "wininst - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "wininst - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\..\lib\distutils\command" +# PROP Intermediate_Dir "temp-release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /O1 /I "..\..\Include" /I "..\..\..\zlib-1.1.4" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x407 /d "NDEBUG" +# ADD RSC /l 0x407 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 ..\..\..\zlib-1.1.4\zlib.lib imagehlp.lib comdlg32.lib ole32.lib comctl32.lib kernel32.lib user32.lib gdi32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"LIBC" +# Begin Special Build Tool +TargetPath=\sf\python\dist\src\lib\distutils\command\wininst.exe +SOURCE="$(InputPath)" +PostBuild_Cmds=upx.exe --best $(TARGETPATH) || echo "wininst.exe not compressed" +# End Special Build Tool + +!ELSEIF "$(CFG)" == "wininst - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "temp-debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MD /W3 /Z7 /Od /I "..\..\Include" /I "..\..\..\zlib-1.1.4" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FR /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x407 /d "_DEBUG" +# ADD RSC /l 0x407 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ..\..\..\zlib-1.1.4\zlib.lib imagehlp.lib comdlg32.lib ole32.lib comctl32.lib kernel32.lib user32.lib gdi32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"LIBC" /out:"./wininst_d.exe" + +!ENDIF + +# Begin Target + +# Name "wininst - Win32 Release" +# Name "wininst - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\extract.c +# End Source File +# Begin Source File + +SOURCE=.\install.c +# End Source File +# Begin Source File + +SOURCE=.\install.rc +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\archive.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\PythonPowered.bmp +# End Source File +# End Group +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/wininst.dsw b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/wininst.dsw new file mode 100644 index 00000000..fbc66aa9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/bdist_wininst/wininst.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "wininst"=.\wininst.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/config.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/config.c new file mode 100644 index 00000000..ca86e7b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/config.c @@ -0,0 +1,127 @@ +/* Module configuration */ + +/* This file contains the table of built-in modules. + See init_builtin() in import.c. */ + +#include "Python.h" + +extern void initarray(void); +#ifndef MS_WIN64 +extern void initaudioop(void); +#endif +extern void initbinascii(void); +extern void initcmath(void); +extern void initerrno(void); +extern void initgc(void); +#ifndef MS_WIN64 +extern void initimageop(void); +#endif +extern void initmath(void); +extern void initmd5(void); +extern void initnt(void); +extern void initoperator(void); +extern void initregex(void); +#ifndef MS_WIN64 +extern void initrgbimg(void); +#endif +extern void initrotor(void); +extern void initsignal(void); +extern void initsha(void); +extern void initstrop(void); +extern void initstruct(void); +extern void inittime(void); +extern void initthread(void); +extern void initcStringIO(void); +extern void initcPickle(void); +extern void initpcre(void); +#ifdef WIN32 +extern void initmsvcrt(void); +extern void init_locale(void); +#endif +extern void init_codecs(void); +extern void initxreadlines(void); +extern void init_weakref(void); +extern void init_hotshot(void); +extern void initxxsubtype(void); +extern void initzipimport(void); +extern void init_random(void); +extern void inititertools(void); + +/* tools/freeze/makeconfig.py marker for additional "extern" */ +/* -- ADDMODULE MARKER 1 -- */ + +extern void init_sre(void); + +extern void PyMarshal_Init(void); +extern void initimp(void); + +struct _inittab _PyImport_Inittab[] = { + + {"array", initarray}, +#ifdef MS_WINDOWS +#ifndef MS_WIN64 + {"audioop", initaudioop}, +#endif +#endif + {"binascii", initbinascii}, + {"cmath", initcmath}, + {"errno", initerrno}, + {"gc", initgc}, +#ifndef MS_WIN64 + {"imageop", initimageop}, +#endif + {"math", initmath}, + {"md5", initmd5}, + {"nt", initnt}, /* Use the NT os functions, not posix */ + {"operator", initoperator}, + {"regex", initregex}, +#ifndef MS_WIN64 + {"rgbimg", initrgbimg}, +#endif + {"rotor", initrotor}, + {"signal", initsignal}, + {"sha", initsha}, + {"strop", initstrop}, + {"struct", initstruct}, + {"time", inittime}, +#ifdef WITH_THREAD + {"thread", initthread}, +#endif + {"cStringIO", initcStringIO}, + {"cPickle", initcPickle}, + {"pcre", initpcre}, +#ifdef WIN32 + {"msvcrt", initmsvcrt}, + {"_locale", init_locale}, +#endif + + {"_codecs", init_codecs}, + {"xreadlines", initxreadlines}, + {"_weakref", init_weakref}, + {"_hotshot", init_hotshot}, + {"_random", init_random}, + {"itertools", inititertools}, + + {"xxsubtype", initxxsubtype}, + {"zipimport", initzipimport}, + +/* tools/freeze/makeconfig.py marker for additional "_inittab" entries */ +/* -- ADDMODULE MARKER 2 -- */ + + {"_sre", init_sre}, + + /* This module "lives in" with marshal.c */ + {"marshal", PyMarshal_Init}, + + /* This lives it with import.c */ + {"imp", initimp}, + + /* These entries are here for sys.builtin_module_names */ + {"__main__", NULL}, + {"__builtin__", NULL}, + {"sys", NULL}, + {"exceptions", NULL}, + + /* Sentinel */ + {0, 0} +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/dl_nt.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/dl_nt.c new file mode 100644 index 00000000..c688938a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/dl_nt.c @@ -0,0 +1,44 @@ +/* + +Entry point for the Windows NT DLL. + +About the only reason for having this, is so initall() can automatically +be called, removing that burden (and possible source of frustration if +forgotten) from the programmer. + +*/ +/* NT and Python share these */ +#include "pyconfig.h" +#include "Python.h" + +#ifndef MS_XBOX +#include "windows.h" +#else +#include +#endif + +char dllVersionBuffer[16] = ""; // a private buffer + +// Python Globals +HMODULE PyWin_DLLhModule = NULL; +const char *PyWin_DLLVersionString = dllVersionBuffer; + +#if 0 // Static linking makes this not necessary +BOOL WINAPI DllMain (HANDLE hInst, + ULONG ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + PyWin_DLLhModule = hInst; + // 1000 is a magic number I picked out of the air. Could do with a #define, I spose... + LoadString(hInst, 1000, dllVersionBuffer, sizeof(dllVersionBuffer)); + //initall(); + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif /* 0 */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/dllbase_nt.txt b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/dllbase_nt.txt new file mode 100644 index 00000000..4220b28e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/dllbase_nt.txt @@ -0,0 +1,76 @@ +In Win32, DLL's are "pre-linked" using a specified base address. +When the DLL is loaded, an attempt is made to place it at +that address. If that address is already in use, a new base address +is selected, and the DLL subject to fixups. Apparently, these +fixups are very slow, and significant performance gains can be +made by selecting a good base address. + +This document is to allocate base addresses to core Python +and Python .PYD files, to give a better change of optimal performance. +This base address is passed to the linker using the /BASE +command line switch. + + +Python.exe/Pythonw.exe - 1d000000 - 1e000000 (-1) +Python.dll - 1e000000 - 1e100000 (-1) + +Standard Extension Modules 1e100000 - 1e200000 "" + - _symtable 1e100000 - 1e110000 + - bsddb 1e180000 - 1e188000 "" + - _tkinter 1e190000 - 1e1A0000 + - parser 1e1A0000 - 1e1B0000 + - zlib 1e1B0000 - 1e1C0000 + - winreg 1e1C0000 - 1e1D0000 + - _socket 1e1D0000 - 1e1E0000 + - _sre 1e1E0000 - 1e1F0000 + - mmap 1e1F0000 - 1e1FFFFF + +More standard extensions 1D100000 - 1e000000 + - pyexpat 1D100000 - 1D110000 + - select 1D110000 - 1D120000 + - unicodedata 1D120000 - 1D160000 + - winsound 1D160000 - 1D170000 + - bZ2 1D170000 - 1D180000 + - datetime 1D180000 - 1D190000 + - _csv 1D190000 - 1D1A0000 + +Other extension modules + - win32api 1e200000 - 1e220000 + - win32ras 1e220000 - 1e230000 + - win32lz 1e230000 - 1e240000 + - timer 1e240000 - 1e250000 + - mmapfile 1e250000 - 1e260000 + - win32pipe 1e260000 - 1e270000 + - avl 1e270000 - 1e270000 + - dbhash 1e280000 - 1e290000 + - win32net 1e290000 - 1e2A0000 + - win32security 1e2A0000 - 1e2B0000 + - win32print 1e2B0000 - 1e2c0000 + - 1e2d0000 - 1e2e0000 + - win32gui 1e2e0000 - 1e2f0000 + - _imaging 1e2f0000 - 1e300000 + - multiarray 1e300000 - 1e310000 + - win32help 1e310000 - 1e320000 + - win32clipboard 1e320000 - 1e330000 + - win2kras 1e330000 - 1e340000 + - pythoncom 1e340000 - 1e400000 + - win32ui 1e400000 - 1e500000 + - win32uiole 1e500000 - 1e600000 + - pywintypes 1e600000 - 1e700000 + - win32process 1e700000 - 1e800000 + - odbc 1e710000 - 1e720000 + - dbi 1e720000 - 1e730000 + - win32file 1e730000 - 1e740000 + - win32wnet 1e740000 - 1e750000 + - win32com.shell 1e750000 - 1e760000 + - win32com.internet 1e760000 - 1e770000 + - win32com.exchange 1e770000 - 1e780000 + - win32com.exchdapi 1e780000 - 1e790000 + - win32com.axscript 1e790000 - 1e7a0000 + - win32com.axdebug 1e7b0000 - 1e7c0000 + - win32com.adsi 1e7f0000 - 1e800000 + - win32event 1e810000 - 1e820000 + - win32evtlog 1e820000 - 1e830000 + - win32com.axcontrol 1e830000 - 1e840000 + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/.cvsignore b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/.cvsignore new file mode 100644 index 00000000..ca624eac --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/.cvsignore @@ -0,0 +1,5 @@ +? example.ncb +? release +? debug +? .c +? example.mdp diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.c new file mode 100644 index 00000000..6cff0ae2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.c @@ -0,0 +1,20 @@ +#include "Python.h" + +static PyObject * +ex_foo(PyObject *self, PyObject *args) +{ + printf("Hello, world\n"); + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef example_methods[] = { + {"foo", ex_foo, 1, "foo() doc string"}, + {NULL, NULL} +}; + +void +initexample(void) +{ + Py_InitModule("example", example_methods); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.def b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.def new file mode 100644 index 00000000..1dce3522 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.def @@ -0,0 +1,2 @@ +EXPORTS + initexample diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.dsp new file mode 100644 index 00000000..13ae887b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.dsp @@ -0,0 +1,117 @@ +# Microsoft Developer Studio Project File - Name="example" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=example - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "example.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "example.mak" CFG="example - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "example - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "example - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "example - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir ".\Release" +# PROP BASE Intermediate_Dir ".\Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\Release" +# PROP Intermediate_Dir ".\Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\Include" /I "..\PC" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib python22.lib /nologo /subsystem:windows /dll /machine:I386 /libpath:"..\PCbuild" /export:initexample +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "example - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir ".\Debug" +# PROP BASE Intermediate_Dir ".\Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\Debug" +# PROP Intermediate_Dir ".\Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\Include" /I "..\PC" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib python22_d.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:".\Debug/example_d.dll" /libpath:"..\PCbuild" /export:initexample +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "example - Win32 Release" +# Name "example - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\example.c +# End Source File +# Begin Source File + +SOURCE=.\example.def +# End Source File +# Begin Source File + +SOURCE=.\readme.txt +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.dsw b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.dsw new file mode 100644 index 00000000..8ddad803 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/example.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "example"=.\example.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/readme.txt b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/readme.txt new file mode 100644 index 00000000..aa2b3091 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/example_nt/readme.txt @@ -0,0 +1,165 @@ +Example Python extension for Windows NT +======================================= + +This directory contains everything needed (except for the Python +distribution!) to build a Python extension module using Microsoft VC++ +("Developer Studio") version 6. It has been tested with VC++ 6.0 on Python +2.2c1. You can also use earlier versions of VC to build Python extensions, +but the sample VC project file (example.dsw in this directory) is in VC 6 +format. + +COPY THIS DIRECTORY! +-------------------- +This "example_nt" directory is a subdirectory of the PC directory, in order +to keep all the PC-specific files under the same directory. However, the +example_nt directory can't actually be used from this location. You first +need to copy or move it up one level, so that example_nt is a direct +sibling of the PC\ and Include\ directories. Do all your work from within +this new location -- sorry, but you'll be sorry if you don't. + +OPEN THE PROJECT +---------------- +From VC 6.x, use the + File -> Open Workspace... +dialog (*not* the "File -> Open..." dialog!). Navigate to and select the +file "example.dsw", in the *copy* of the example_nt directory you made +above. +Click Open. + +BUILD THE EXAMPLE DLL +--------------------- +In order to check that everything is set up right, try building: + +1. Select a configuration. This step is optional. Do + Build -> Select Active Configuration... + and select either "example - Win32 Release" or "example - Win32 Debug". + If you skip this step, you'll use the Debug configuration by default. + +2. Build the DLL. Do + Build -> Build example_d.dll + in Debug mode, or + Build -> Build example.dll + in Release mode. + This creates all intermediate and result files in a subdirectory which + is called either Debug or Release, depending on which configuration you + picked in the preceding step. + +TESTING THE DEBUG-MODE DLL +-------------------------- +Once the Debug build has succeeded, bring up a DOS box, and cd to +example_nt\Debug. You should now be able to repeat the following session +("C>" is the DOS prompt, ">>>" is the Python prompt) (note that various +debug output from Python may not match this screen dump exactly): + + C>..\..\PCbuild\python_d + Adding parser accelerators ... + Done. + Python 2.2c1+ (#28, Dec 14 2001, 18:06:39) [MSC 32 bit (Intel)] on win32 + Type "help", "copyright", "credits" or "license" for more information. + >>> import example + [7052 refs] + >>> example.foo() + Hello, world + [7052 refs] + >>> + +TESTING THE RELEASE-MODE DLL +---------------------------- +Once the Release build has succeeded, bring up a DOS box, and cd to +example_nt\Release. You should now be able to repeat the following session +("C>" is the DOS prompt, ">>>" is the Python prompt): + + C>..\..\PCbuild\python + Python 2.2c1+ (#28, Dec 14 2001, 18:06:04) [MSC 32 bit (Intel)] on win32 + Type "help", "copyright", "credits" or "license" for more information. + >>> import example + >>> example.foo() + Hello, world + >>> + +Congratulations! You've successfully built your first Python extension +module. + +CREATING YOUR OWN PROJECT +------------------------- +Choose a name ("spam" is always a winner :-) and create a directory for +it. Copy your C sources into it. Note that the module source file name +does not necessarily have to match the module name, but the "init" function +name should match the module name -- i.e. you can only import a module +"spam" if its init function is called "initspam()", and it should call +Py_InitModule with the string "spam" as its first argument (use the minimal +example.c in this directory as a guide). By convention, it lives in a file +called "spam.c" or "spammodule.c". The output file should be called +"spam.dll" or "spam.pyd" (the latter is supported to avoid confusion with a +system library "spam.dll" to which your module could be a Python interface) +in Release mode, or spam_d.dll or spam_d.pyd in Debug mode. + +Now your options are: + +1) Copy example.dsw and example.dsp, rename them to spam.*, and edit them +by hand. + +or + +2) Create a brand new project; instructions are below. + +In either case, copy example_nt\example.def to spam\spam.def, and edit the +new spam.def so its second line contains the string "initspam". If you +created a new project yourself, add the file spam.def to the project now. +(This is an annoying little file with only two lines. An alternative +approach is to forget about the .def file, and add the option +"/export:initspam" somewhere to the Link settings, by manually editing the +"Project Options" box). + +You are now all set to build your extension, unless it requires other +external libraries, include files, etc. See Python's Extending and +Embedding manual for instructions on how to write an extension. + + +CREATING A BRAND NEW PROJECT +---------------------------- +Use the + File -> New... -> Projects +dialog to create a new Project Workspace. Select "Win32 Dynamic-Link +Library", enter the name ("spam"), and make sure the "Location" is set to +the spam directory you have created (which should be a direct subdirectory +of the Python build tree, a sibling of Include and PC). Select Win32 as the +platform (in my version, this is the only choice). Make sure the "Create +new workspace" radio button is selected. Click OK. + +Now open the + Project -> Settings... +dialog. (Impressive, isn't it? :-) You only need to change a few +settings. Make sure "All Configurations" is selected from the "Settings +for:" dropdown list. Select the "C/C++" tab. Choose the "Preprocessor" +category in the popup menu at the top. Type the following text in the +entry box labeled "Addditional include directories:" + + ..\Include,..\PC + +Then, choose the "Input" category in the Link tab, and enter + ..\PCbuild +in the "Additional library path:" box. + +Now you need to add some mode-specific settings: + +Select "Win32 Release" in the "Settings for:" dropdown list. Click the +"Link" tab, choose the "Input" Category, and append "python22.lib" to the +list in the "Object/library modules:" box. + +Select "Win32 Debug" in the "Settings for:" dropdown list, and append +"python22_d.lib" to the list in the "Object/library modules:" box. Then +click on the C/C++ tab, select "Code Generation" from the "Category:" +dropdown list, and select "Debug Multithreaded DLL" from the "Use run-time +library:" dropdown list. + +Select "Win32 Release" again from the "Settings for:" dropdown list. +Select "Multithreaded DLL" from the "Use run-time library:" dropdown list. + +That's all . + +You should now create the file spam.def as instructed in the previous +section. Then chose the + Insert -> Files into Project... +dialog. Set the pattern to *.* and select both spam.c and spam.def and +click OK. (Inserting them one by one is fine too.) diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/frozen_dllmain.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/frozen_dllmain.c new file mode 100644 index 00000000..f99b0061 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/frozen_dllmain.c @@ -0,0 +1,134 @@ +/* FreezeDLLMain.cpp + +This is a DLLMain suitable for frozen applications/DLLs on +a Windows platform. + +The general problem is that many Python extension modules may define +DLL main functions, but when statically linked together to form +a frozen application, this DLLMain symbol exists multiple times. + +The solution is: +* Each module checks for a frozen build, and if so, defines its DLLMain + function as "__declspec(dllexport) DllMain%module%" + (eg, DllMainpythoncom, or DllMainpywintypes) + +* The frozen .EXE/.DLL links against this module, which provides + the single DllMain. + +* This DllMain attempts to locate and call the DllMain for each + of the extension modules. + +* This code also has hooks to "simulate" DllMain when used from + a frozen .EXE. + +At this stage, there is a static table of "possibly embedded modules". +This should change to something better, but it will work OK for now. + +Note that this scheme does not handle dependencies in the order +of DllMain calls - except it does call pywintypes first :-) + +As an example of how an extension module with a DllMain should be +changed, here is a snippet from the pythoncom extension module. + + // end of example code from pythoncom's DllMain.cpp + #ifndef BUILD_FREEZE + #define DLLMAIN DllMain + #define DLLMAIN_DECL + #else + #define DLLMAIN DllMainpythoncom + #define DLLMAIN_DECL __declspec(dllexport) + #endif + + extern "C" DLLMAIN_DECL + BOOL WINAPI DLLMAIN(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) + // end of example code from pythoncom's DllMain.cpp + +***************************************************************************/ +#include "windows.h" + +static char *possibleModules[] = { + "pywintypes", + "pythoncom", + "win32ui", + NULL, +}; + +BOOL CallModuleDllMain(char *modName, DWORD dwReason); + + +/* + Called by a frozen .EXE only, so that built-in extension + modules are initialized correctly +*/ +void PyWinFreeze_ExeInit(void) +{ + char **modName; + for (modName = possibleModules;*modName;*modName++) { +/* printf("Initialising '%s'\n", *modName); */ + CallModuleDllMain(*modName, DLL_PROCESS_ATTACH); + } +} + +/* + Called by a frozen .EXE only, so that built-in extension + modules are cleaned up +*/ +void PyWinFreeze_ExeTerm(void) +{ + // Must go backwards + char **modName; + for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2; + modName >= possibleModules; + *modName--) { +/* printf("Terminating '%s'\n", *modName);*/ + CallModuleDllMain(*modName, DLL_PROCESS_DETACH); + } +} + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + BOOL ret = TRUE; + switch (dwReason) { + case DLL_PROCESS_ATTACH: + { + char **modName; + for (modName = possibleModules;*modName;*modName++) { + BOOL ok = CallModuleDllMain(*modName, dwReason); + if (!ok) + ret = FALSE; + } + break; + } + case DLL_PROCESS_DETACH: + { + // Must go backwards + char **modName; + for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2; + modName >= possibleModules; + *modName--) + CallModuleDllMain(*modName, DLL_PROCESS_DETACH); + break; + } + } + return ret; +} + +BOOL CallModuleDllMain(char *modName, DWORD dwReason) +{ + BOOL (WINAPI * pfndllmain)(HINSTANCE, DWORD, LPVOID); + + char funcName[255]; + HMODULE hmod = GetModuleHandle(NULL); + strcpy(funcName, "_DllMain"); + strcat(funcName, modName); + strcat(funcName, "@12"); // stdcall convention. + pfndllmain = (BOOL (WINAPI *)(HINSTANCE, DWORD, LPVOID))GetProcAddress(hmod, funcName); + if (pfndllmain==NULL) { + /* No function by that name exported - then that module does + not appear in our frozen program - return OK + */ + return TRUE; + } + return (*pfndllmain)(hmod, dwReason, NULL); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/getpathp.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/getpathp.c new file mode 100644 index 00000000..9ecce810 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/getpathp.c @@ -0,0 +1,686 @@ + +/* Return the initial module search path. */ +/* Used by DOS, OS/2, Windows 3.1, Windows 95/98, Windows NT. */ + +/* ---------------------------------------------------------------- + PATH RULES FOR WINDOWS: + This describes how sys.path is formed on Windows. It describes the + functionality, not the implementation (ie, the order in which these + are actually fetched is different) + + * Python always adds an empty entry at the start, which corresponds + to the current directory. + + * If the PYTHONPATH env. var. exists, it's entries are added next. + + * We look in the registry for "application paths" - that is, sub-keys + under the main PythonPath registry key. These are added next (the + order of sub-key processing is undefined). + HKEY_CURRENT_USER is searched and added first. + HKEY_LOCAL_MACHINE is searched and added next. + (Note that all known installers only use HKLM, so HKCU is typically + empty) + + * We attempt to locate the "Python Home" - if the PYTHONHOME env var + is set, we believe it. Otherwise, we use the path of our host .EXE's + to try and locate our "landmark" (lib\\os.py) and deduce our home. + - If we DO have a Python Home: The relevant sub-directories (Lib, + plat-win, lib-tk, etc) are based on the Python Home + - If we DO NOT have a Python Home, the core Python Path is + loaded from the registry. This is the main PythonPath key, + and both HKLM and HKCU are combined to form the path) + + * Iff - we can not locate the Python Home, have not had a PYTHONPATH + specified, and can't locate any Registry entries (ie, we have _nothing_ + we can assume is a good path), a default path with relative entries is + used (eg. .\Lib;.\plat-win, etc) + + + The end result of all this is: + * When running python.exe, or any other .exe in the main Python directory + (either an installed version, or directly from the PCbuild directory), + the core path is deduced, and the core paths in the registry are + ignored. Other "application paths" in the registry are always read. + + * When Python is hosted in another exe (different directory, embedded via + COM, etc), the Python Home will not be deduced, so the core path from + the registry is used. Other "application paths" in the registry are + always read. + + * If Python can't find its home and there is no registry (eg, frozen + exe, some very strange installation setup) you get a path with + some default, but relative, paths. + + ---------------------------------------------------------------- */ + + +#include "Python.h" +#include "osdefs.h" + +#ifdef MS_WINDOWS +#ifdef MS_XBOX +#include +#else +#include +#endif +#include +#endif + +#include +#include +#include + +/* Search in some common locations for the associated Python libraries. + * + * Py_GetPath() tries to return a sensible Python module search path. + * + * The approach is an adaptation for Windows of the strategy used in + * ../Modules/getpath.c; it uses the Windows Registry as one of its + * information sources. + */ + +#ifndef LANDMARK +#define LANDMARK "lib\\os.py" +#endif + +static char prefix[MAXPATHLEN+1]; +static char progpath[MAXPATHLEN+1]; +static char dllpath[MAXPATHLEN+1]; +static char *module_search_path = NULL; + + +static int +is_sep(char ch) /* determine if "ch" is a separator character */ +{ +#ifdef ALTSEP + return ch == SEP || ch == ALTSEP; +#else + return ch == SEP; +#endif +} + +/* assumes 'dir' null terminated in bounds. Never writes + beyond existing terminator. +*/ +static void +reduce(char *dir) +{ + size_t i = strlen(dir); + while (i > 0 && !is_sep(dir[i])) + --i; + dir[i] = '\0'; +} + + +static int +exists(char *filename) +{ + struct stat buf; + return stat(filename, &buf) == 0; +} + +/* Assumes 'filename' MAXPATHLEN+1 bytes long - + may extend 'filename' by one character. +*/ +static int +ismodule(char *filename) /* Is module -- check for .pyc/.pyo too */ +{ + if (exists(filename)) + return 1; + + /* Check for the compiled version of prefix. */ + if (strlen(filename) < MAXPATHLEN) { + strcat(filename, Py_OptimizeFlag ? "o" : "c"); + if (exists(filename)) + return 1; + } + return 0; +} + +/* guarantees buffer will never overflow MAXPATHLEN+1 bytes */ +static void +join(char *buffer, char *stuff) +{ + size_t n, k; + if (is_sep(stuff[0])) + n = 0; + else { + n = strlen(buffer); + if (n > 0 && !is_sep(buffer[n-1]) && n < MAXPATHLEN) + buffer[n++] = SEP; + } + k = strlen(stuff); + if (n + k > MAXPATHLEN) + k = MAXPATHLEN - n; + strncpy(buffer+n, stuff, k); + buffer[n+k] = '\0'; +} + +/* gotlandmark only called by search_for_prefix, which ensures + 'prefix' is null terminated in bounds. join() ensures + 'landmark' can not overflow prefix if too long. +*/ +static int +gotlandmark(char *landmark) +{ + int n, ok; + + n = strlen(prefix); + join(prefix, landmark); + ok = ismodule(prefix); + prefix[n] = '\0'; + return ok; +} + +/* assumes argv0_path is MAXPATHLEN+1 bytes long, already \0 term'd. + assumption provided by only caller, calculate_path() */ +static int +search_for_prefix(char *argv0_path, char *landmark) +{ + /* Search from argv0_path, until landmark is found */ + strcpy(prefix, argv0_path); + do { + if (gotlandmark(landmark)) + return 1; + reduce(prefix); + } while (prefix[0]); + return 0; +} + +#if defined(MS_WINDOWS) && !defined(MS_XBOX) + +/* a string loaded from the DLL at startup.*/ +extern const char *PyWin_DLLVersionString; + + +/* Load a PYTHONPATH value from the registry. + Load from either HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER. + + Works in both Unicode and 8bit environments. Only uses the + Ex family of functions so it also works with Windows CE. + + Returns NULL, or a pointer that should be freed. + + XXX - this code is pretty strange, as it used to also + work on Win16, where the buffer sizes werent available + in advance. It could be simplied now Win16/Win32s is dead! +*/ + +static char * +getpythonregpath(HKEY keyBase, int skipcore) +{ + HKEY newKey = 0; + DWORD dataSize = 0; + DWORD numKeys = 0; + LONG rc; + char *retval = NULL; + TCHAR *dataBuf = NULL; + static const TCHAR keyPrefix[] = _T("Software\\Python\\PythonCore\\"); + static const TCHAR keySuffix[] = _T("\\PythonPath"); + size_t versionLen; + DWORD index; + TCHAR *keyBuf = NULL; + TCHAR *keyBufPtr; + TCHAR **ppPaths = NULL; + + /* Tried to use sysget("winver") but here is too early :-( */ + versionLen = _tcslen(PyWin_DLLVersionString); + /* Space for all the chars, plus one \0 */ + keyBuf = keyBufPtr = malloc(sizeof(keyPrefix) + + sizeof(TCHAR)*(versionLen-1) + + sizeof(keySuffix)); + if (keyBuf==NULL) goto done; + + memcpy(keyBufPtr, keyPrefix, sizeof(keyPrefix)-sizeof(TCHAR)); + keyBufPtr += sizeof(keyPrefix)/sizeof(TCHAR) - 1; + memcpy(keyBufPtr, PyWin_DLLVersionString, versionLen * sizeof(TCHAR)); + keyBufPtr += versionLen; + /* NULL comes with this one! */ + memcpy(keyBufPtr, keySuffix, sizeof(keySuffix)); + /* Open the root Python key */ + rc=RegOpenKeyEx(keyBase, + keyBuf, /* subkey */ + 0, /* reserved */ + KEY_READ, + &newKey); + if (rc!=ERROR_SUCCESS) goto done; + /* Find out how big our core buffer is, and how many subkeys we have */ + rc = RegQueryInfoKey(newKey, NULL, NULL, NULL, &numKeys, NULL, NULL, + NULL, NULL, &dataSize, NULL, NULL); + if (rc!=ERROR_SUCCESS) goto done; + if (skipcore) dataSize = 0; /* Only count core ones if we want them! */ + /* Allocate a temp array of char buffers, so we only need to loop + reading the registry once + */ + ppPaths = malloc( sizeof(TCHAR *) * numKeys ); + if (ppPaths==NULL) goto done; + memset(ppPaths, 0, sizeof(TCHAR *) * numKeys); + /* Loop over all subkeys, allocating a temp sub-buffer. */ + for(index=0;index 0) { + *(szCur++) = _T(';'); + dataSize--; + } + if (ppPaths[index]) { + int len = _tcslen(ppPaths[index]); + _tcsncpy(szCur, ppPaths[index], len); + szCur += len; + dataSize -= len; + } + } + if (skipcore) + *szCur = '\0'; + else { + /* If we have no values, we dont need a ';' */ + if (numKeys) { + *(szCur++) = _T(';'); + dataSize--; + } + /* Now append the core path entries - + this will include the NULL + */ + rc = RegQueryValueEx(newKey, NULL, 0, NULL, + (LPBYTE)szCur, &dataSize); + } + /* And set the result - caller must free + If MBCS, it is fine as is. If Unicode, allocate new + buffer and convert. + */ +#ifdef UNICODE + retval = (char *)malloc(reqdSize+1); + if (retval) + WideCharToMultiByte(CP_ACP, 0, + dataBuf, -1, /* source */ + retval, reqdSize+1, /* dest */ + NULL, NULL); + free(dataBuf); +#else + retval = dataBuf; +#endif + } +done: + /* Loop freeing my temp buffers */ + if (ppPaths) { + for(index=0;index 4) { + zip_path[len-3] = 'z'; /* change ending to "zip" */ + zip_path[len-2] = 'i'; + zip_path[len-1] = 'p'; + } + else { + zip_path[0] = 0; + } + + skiphome = pythonhome==NULL ? 0 : 1; + machinepath = getpythonregpath(HKEY_LOCAL_MACHINE, skiphome); + userpath = getpythonregpath(HKEY_CURRENT_USER, skiphome); + /* We only use the default relative PYTHONPATH if we havent + anything better to use! */ + skipdefault = envpath!=NULL || pythonhome!=NULL || \ + machinepath!=NULL || userpath!=NULL; +#elif defined(MS_XBOX) + skipdefault = 1; +#endif + + /* We need to construct a path from the following parts. + (1) the PYTHONPATH environment variable, if set; + (2) for Win32, the zip archive file path; + (3) for Win32, the machinepath and userpath, if set; + (4) the PYTHONPATH config macro, with the leading "." + of each component replaced with pythonhome, if set; + (5) the directory containing the executable (argv0_path). + The length calculation calculates #4 first. + Extra rules: + - If PYTHONHOME is set (in any way) item (3) is ignored. + - If registry values are used, (4) and (5) are ignored. + */ + + /* Calculate size of return buffer */ + if (pythonhome != NULL) { + char *p; + bufsz = 1; + for (p = PYTHONPATH; *p; p++) { + if (*p == DELIM) + bufsz++; /* number of DELIM plus one */ + } + bufsz *= strlen(pythonhome); + } + else + bufsz = 0; + bufsz += strlen(PYTHONPATH) + 1; + bufsz += strlen(argv0_path) + 1; +#ifdef MS_WINDOWS + if (userpath) + bufsz += strlen(userpath) + 1; + if (machinepath) + bufsz += strlen(machinepath) + 1; + bufsz += strlen(zip_path) + 1; +#endif + if (envpath != NULL) + bufsz += strlen(envpath) + 1; + + module_search_path = buf = malloc(bufsz); + if (buf == NULL) { + /* We can't exit, so print a warning and limp along */ + fprintf(stderr, "Can't malloc dynamic PYTHONPATH.\n"); + if (envpath) { + fprintf(stderr, "Using environment $PYTHONPATH.\n"); + module_search_path = envpath; + } + else { + fprintf(stderr, "Using default static path.\n"); + module_search_path = PYTHONPATH; + } +#ifdef MS_WINDOWS + if (machinepath) + free(machinepath); + if (userpath) + free(userpath); +#endif /* MS_WINDOWS */ + return; + } + + if (envpath) { + strcpy(buf, envpath); + buf = strchr(buf, '\0'); + *buf++ = DELIM; + } +#ifdef MS_WINDOWS + if (zip_path[0]) { + strcpy(buf, zip_path); + buf = strchr(buf, '\0'); + *buf++ = DELIM; + } + if (userpath) { + strcpy(buf, userpath); + buf = strchr(buf, '\0'); + *buf++ = DELIM; + free(userpath); + } + if (machinepath) { + strcpy(buf, machinepath); + buf = strchr(buf, '\0'); + *buf++ = DELIM; + free(machinepath); + } + if (pythonhome == NULL) { + if (!skipdefault) { + strcpy(buf, PYTHONPATH); + buf = strchr(buf, '\0'); + } + } +#else + if (pythonhome == NULL) { + strcpy(buf, PYTHONPATH); + buf = strchr(buf, '\0'); + } +#endif /* MS_WINDOWS */ + else { + char *p = PYTHONPATH; + char *q; + size_t n; + for (;;) { + q = strchr(p, DELIM); + if (q == NULL) + n = strlen(p); + else + n = q-p; + if (p[0] == '.' && is_sep(p[1])) { + strcpy(buf, pythonhome); + buf = strchr(buf, '\0'); + p++; + n--; + } + strncpy(buf, p, n); + buf += n; + if (q == NULL) + break; + *buf++ = DELIM; + p = q+1; + } + } + if (argv0_path) { + *buf++ = DELIM; + strcpy(buf, argv0_path); + buf = strchr(buf, '\0'); + } + *buf = '\0'; + /* Now to pull one last hack/trick. If sys.prefix is + empty, then try and find it somewhere on the paths + we calculated. We scan backwards, as our general policy + is that Python core directories are at the *end* of + sys.path. We assume that our "lib" directory is + on the path, and that our 'prefix' directory is + the parent of that. + */ + if (*prefix=='\0') { + char lookBuf[MAXPATHLEN+1]; + char *look = buf - 1; /* 'buf' is at the end of the buffer */ + while (1) { + int nchars; + char *lookEnd = look; + /* 'look' will end up one character before the + start of the path in question - even if this + is one character before the start of the buffer + */ + while (*look != DELIM && look >= module_search_path) + look--; + nchars = lookEnd-look; + strncpy(lookBuf, look+1, nchars); + lookBuf[nchars] = '\0'; + /* Up one level to the parent */ + reduce(lookBuf); + if (search_for_prefix(lookBuf, LANDMARK)) { + break; + } + /* If we are out of paths to search - give up */ + if (look < module_search_path) + break; + look--; + } + } +} + + +/* External interface */ + +char * +Py_GetPath(void) +{ + if (!module_search_path) + calculate_path(); + return module_search_path; +} + +char * +Py_GetPrefix(void) +{ + if (!module_search_path) + calculate_path(); + return prefix; +} + +char * +Py_GetExecPrefix(void) +{ + return Py_GetPrefix(); +} + +char * +Py_GetProgramFullPath(void) +{ + if (!module_search_path) + calculate_path(); + return progpath; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/import_nt.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/import_nt.c new file mode 100644 index 00000000..d464a0fa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/import_nt.c @@ -0,0 +1,93 @@ +/******************************************************************** + + import_nt.c + + Win32 specific import code. + +*/ + +#include "Python.h" +#include "osdefs.h" +#ifdef MS_XBOX +#include +#else +#include +#endif +#include "importdl.h" +#include "malloc.h" /* for alloca */ + +/* a string loaded from the DLL at startup */ +extern const char *PyWin_DLLVersionString; + +FILE *PyWin_FindRegisteredModule(const char *moduleName, + struct filedescr **ppFileDesc, + char *pathBuf, + int pathLen) +{ +#ifndef MS_XBOX + char *moduleKey; + const char keyPrefix[] = "Software\\Python\\PythonCore\\"; + const char keySuffix[] = "\\Modules\\"; +#ifdef _DEBUG + /* In debugging builds, we _must_ have the debug version + * registered. + */ + const char debugString[] = "\\Debug"; +#else + const char debugString[] = ""; +#endif + struct filedescr *fdp = NULL; + FILE *fp; + HKEY keyBase = HKEY_CURRENT_USER; + int modNameSize; + long regStat; + + /* Calculate the size for the sprintf buffer. + * Get the size of the chars only, plus 1 NULL. + */ + size_t bufSize = sizeof(keyPrefix)-1 + + strlen(PyWin_DLLVersionString) + + sizeof(keySuffix) + + strlen(moduleName) + + sizeof(debugString) - 1; + /* alloca == no free required, but memory only local to fn, + * also no heap fragmentation! + */ + moduleKey = alloca(bufSize); + PyOS_snprintf(moduleKey, bufSize, + "Software\\Python\\PythonCore\\%s\\Modules\\%s%s", + PyWin_DLLVersionString, moduleName, debugString); + + modNameSize = pathLen; + regStat = RegQueryValue(keyBase, moduleKey, pathBuf, &modNameSize); + if (regStat != ERROR_SUCCESS) { + /* No user setting - lookup in machine settings */ + keyBase = HKEY_LOCAL_MACHINE; + /* be anal - failure may have reset size param */ + modNameSize = pathLen; + regStat = RegQueryValue(keyBase, moduleKey, + pathBuf, &modNameSize); + + if (regStat != ERROR_SUCCESS) + return NULL; + } + /* use the file extension to locate the type entry. */ + for (fdp = _PyImport_Filetab; fdp->suffix != NULL; fdp++) { + size_t extLen = strlen(fdp->suffix); + assert(modNameSize >= 0); /* else cast to size_t is wrong */ + if ((size_t)modNameSize > extLen && + strnicmp(pathBuf + ((size_t)modNameSize-extLen-1), + fdp->suffix, + extLen) == 0) + break; + } + if (fdp->suffix == NULL) + return NULL; + fp = fopen(pathBuf, fdp->mode); + if (fp != NULL) + *ppFileDesc = fdp; + return fp; +#else + return NULL; +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/make_versioninfo.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/make_versioninfo.c new file mode 100644 index 00000000..b12a445c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/make_versioninfo.c @@ -0,0 +1,33 @@ +#include +#include "patchlevel.h" +/* + * This program prints out an include file containing fields required to build + * the version info resource of pythonxx.dll because the resource compiler + * cannot do the arithmetic. + */ +/* + * FIELD3 is the third field of the version number. + * This is what we'd like FIELD3 to be: + * + * #define FIELD3 (PY_MICRO_VERSION*1000 + PY_RELEASE_LEVEL*10 + PY_RELEASE_SERIAL) + * + * but that neither gives an error nor comes anywhere close to working. + * + * For 2.4a0, + * PY_MICRO_VERSION = 0 + * PY_RELEASE_LEVEL = 'alpha' = 0xa + * PY_RELEASE_SERIAL = 0 + * + * gives FIELD3 = 0*1000 + 10*10 + 0 = 100 + */ +int main(int argc, char **argv) +{ + printf("/* This file created by make_versioninfo.exe */\n"); + printf("#define FIELD3 %d\n", + PY_MICRO_VERSION*1000 + PY_RELEASE_LEVEL*10 + PY_RELEASE_SERIAL); + printf("#define MS_DLL_ID \"%d.%d\"\n", + PY_MAJOR_VERSION, PY_MINOR_VERSION); + printf("#define PYTHON_DLL_NAME \"python%d%d.dll\"\n", + PY_MAJOR_VERSION, PY_MINOR_VERSION); + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/msvcrtmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/msvcrtmodule.c new file mode 100644 index 00000000..1be12cc3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/msvcrtmodule.c @@ -0,0 +1,232 @@ +/********************************************************* + + msvcrtmodule.c + + A Python interface to the Microsoft Visual C Runtime + Library, providing access to those non-portable, but + still useful routines. + + Only ever compiled with an MS compiler, so no attempt + has been made to avoid MS language extensions, etc... + + This may only work on NT or 95... + + Author: Mark Hammond and Guido van Rossum. + Maintenance: Guido van Rossum. + +***********************************************************/ + +#include "Python.h" +#include "malloc.h" +#include +#include +#include + +// Force the malloc heap to clean itself up, and free unused blocks +// back to the OS. (According to the docs, only works on NT.) +static PyObject * +msvcrt_heapmin(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":heapmin")) + return NULL; + + if (_heapmin() != 0) + return PyErr_SetFromErrno(PyExc_IOError); + + Py_INCREF(Py_None); + return Py_None; +} + +// Perform locking operations on a C runtime file descriptor. +static PyObject * +msvcrt_locking(PyObject *self, PyObject *args) +{ + int fd; + int mode; + long nbytes; + int err; + + if (!PyArg_ParseTuple(args, "iil:locking", &fd, &mode, &nbytes)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + err = _locking(fd, mode, nbytes); + Py_END_ALLOW_THREADS + if (err != 0) + return PyErr_SetFromErrno(PyExc_IOError); + + Py_INCREF(Py_None); + return Py_None; +} + +// Set the file translation mode for a C runtime file descriptor. +static PyObject * +msvcrt_setmode(PyObject *self, PyObject *args) +{ + int fd; + int flags; + if (!PyArg_ParseTuple(args,"ii:setmode", &fd, &flags)) + return NULL; + + flags = _setmode(fd, flags); + if (flags == -1) + return PyErr_SetFromErrno(PyExc_IOError); + + return PyInt_FromLong(flags); +} + +// Convert an OS file handle to a C runtime file descriptor. +static PyObject * +msvcrt_open_osfhandle(PyObject *self, PyObject *args) +{ + long handle; + int flags; + int fd; + + if (!PyArg_ParseTuple(args, "li:open_osfhandle", &handle, &flags)) + return NULL; + + fd = _open_osfhandle(handle, flags); + if (fd == -1) + return PyErr_SetFromErrno(PyExc_IOError); + + return PyInt_FromLong(fd); +} + +// Convert a C runtime file descriptor to an OS file handle. +static PyObject * +msvcrt_get_osfhandle(PyObject *self, PyObject *args) +{ + int fd; + Py_intptr_t handle; + + if (!PyArg_ParseTuple(args,"i:get_osfhandle", &fd)) + return NULL; + + handle = _get_osfhandle(fd); + if (handle == -1) + return PyErr_SetFromErrno(PyExc_IOError); + + /* technically 'handle' is not a pointer, but a integer as + large as a pointer, Python's *VoidPtr interface is the + most appropriate here */ + return PyLong_FromVoidPtr((void*)handle); +} + +/* Console I/O */ + +static PyObject * +msvcrt_kbhit(PyObject *self, PyObject *args) +{ + int ok; + + if (!PyArg_ParseTuple(args, ":kbhit")) + return NULL; + + ok = _kbhit(); + return PyInt_FromLong(ok); +} + +static PyObject * +msvcrt_getch(PyObject *self, PyObject *args) +{ + int ch; + char s[1]; + + if (!PyArg_ParseTuple(args, ":getch")) + return NULL; + + Py_BEGIN_ALLOW_THREADS + ch = _getch(); + Py_END_ALLOW_THREADS + s[0] = ch; + return PyString_FromStringAndSize(s, 1); +} + +static PyObject * +msvcrt_getche(PyObject *self, PyObject *args) +{ + int ch; + char s[1]; + + if (!PyArg_ParseTuple(args, ":getche")) + return NULL; + + Py_BEGIN_ALLOW_THREADS + ch = _getche(); + Py_END_ALLOW_THREADS + s[0] = ch; + return PyString_FromStringAndSize(s, 1); +} + +static PyObject * +msvcrt_putch(PyObject *self, PyObject *args) +{ + char ch; + + if (!PyArg_ParseTuple(args, "c:putch", &ch)) + return NULL; + + _putch(ch); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +msvcrt_ungetch(PyObject *self, PyObject *args) +{ + char ch; + + if (!PyArg_ParseTuple(args, "c:ungetch", &ch)) + return NULL; + + if (_ungetch(ch) == EOF) + return PyErr_SetFromErrno(PyExc_IOError); + Py_INCREF(Py_None); + return Py_None; +} + + +static void +insertint(PyObject *d, char *name, int value) +{ + PyObject *v = PyInt_FromLong((long) value); + if (v == NULL) { + /* Don't bother reporting this error */ + PyErr_Clear(); + } + else { + PyDict_SetItemString(d, name, v); + Py_DECREF(v); + } +} + + +/* List of functions exported by this module */ +static struct PyMethodDef msvcrt_functions[] = { + {"heapmin", msvcrt_heapmin, METH_VARARGS}, + {"locking", msvcrt_locking, METH_VARARGS}, + {"setmode", msvcrt_setmode, METH_VARARGS}, + {"open_osfhandle", msvcrt_open_osfhandle, METH_VARARGS}, + {"get_osfhandle", msvcrt_get_osfhandle, METH_VARARGS}, + {"kbhit", msvcrt_kbhit, METH_VARARGS}, + {"getch", msvcrt_getch, METH_VARARGS}, + {"getche", msvcrt_getche, METH_VARARGS}, + {"putch", msvcrt_putch, METH_VARARGS}, + {"ungetch", msvcrt_ungetch, METH_VARARGS}, + {NULL, NULL} +}; + +__declspec(dllexport) void +initmsvcrt(void) +{ + PyObject *m = Py_InitModule("msvcrt", msvcrt_functions); + PyObject *d = PyModule_GetDict(m); + + /* constants for the locking() function's mode argument */ + insertint(d, "LK_LOCK", _LK_LOCK); + insertint(d, "LK_NBLCK", _LK_NBLCK); + insertint(d, "LK_NBRLCK", _LK_NBRLCK); + insertint(d, "LK_RLCK", _LK_RLCK); + insertint(d, "LK_UNLCK", _LK_UNLCK); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/Makefile b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/Makefile new file mode 100644 index 00000000..59713b25 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/Makefile @@ -0,0 +1,671 @@ +#####################==================---------------- +# +# Top-Level Makefile for Building Python 2.3 for OS/2 using GCC/EMX +# Originally written by Andrew Zabolotny, for Python 1.5.2 +# Modified by Andrew MacIntyre, for Python 2.3 +# +# This makefile was developed for use with [P]GCC/EMX compiler any +# version and GNU Make. +# +# The output of the build is a largish Python23.DLL containing the +# essential modules of Python and a small Python.exe program to start +# the interpreter. When embedding Python within another program, only +# Python23.DLL is needed. We also build python_s.a static library (which +# can be converted into OMF (.lib) format using emxomf tool) and both +# python.a and python.lib import libraries. Then the optional +# extension modules, which are OS/2 DLLs renamed with a PYD file extension. +# +# Recommended build order: +# make depend (if you have makedep) +# make all +# make lx (if you have lxlite) +# make test (optional) +# +#####################==================---------------- + +# === Compilation mode: debug or release === +MODE= optimize +#MODE= debug +# === Assert() enabled === +ASSERTIONS=no +#ASSERTIONS=yes +# === Hard-wire installation location === +FIXED_PYHOME=no +#FIXED_PYHOME=yes + +# === Optional modules === +# Do you have the InfoZip compression library installed? +HAVE_ZLIB= no +# Do you have the Ultra Fast Crypt (UFC) library installed? +HAVE_UFC= no +# Do you have the Tcl/Tk library installed? +HAVE_TCLTK= no +# Do you have the GNU multiprecision library installed? +HAVE_GMPZ= no +# Do you have the GNU readline library installed? +# NOTE: I'm using a modified version of Kai Uwe Rommel's port that +# - is compiled with multithreading enabled +# - is linked statically +# I have had no success trying to use a DLL version, even when +# compiled with multithreading enabled. +HAVE_GREADLINE= no +# Do you have the BSD DB library (v1.85) as included in the EMXBSD package? +# NOTE: this library needs to be recompiled with a structure member +# renamed to avoid problems with the multithreaded errno support +# (there is a structure member called errno, used for shadowing the +# real errno, which conflicts with the errno redefinition of -Zmt) +HAVE_BSDDB= no +# Do you have the ncurses library installed? EMX's BSD curses aren't enough! +HAVE_NCURSES= no +# Do you have the GDBM library installed? +HAVE_GDBM= no +# Do you have the BZ2 compression library installed? +HAVE_BZ2= no + +# === install locations === +# default value of PYTHONHOME +LIB_DIR=C:/Python23 +# default is to have everything in or under PYTHONHOME +EXE_DIR=$(LIB_DIR) +DLL_DIR=$(EXE_DIR) + + +# === The Tools === +CC= gcc +CFLAGS= -Zmt -Wall $(INCLUDE) +CFLAGS.LIB= $(CFLAGS) +LD= gcc +LDFLAGS= -Zmt -Zcrtdll -L. -lgcc +LDFLAGS.EXE= $(LDFLAGS) +LDFLAGS.DLL= $(LDFLAGS) -Zdll +LDFLAGS.A= $(LDFLAGS) $(LIBS) +ARFLAGS= crs +IMPLIB= emximp +EXPLIB= emxexp +EXEOPT= emxbind +PY_DEF= -DPy_BUILD_CORE + + +# adjust C compiler settings based on build options +ifeq ($(MODE),debug) + CFLAGS+= -g -O + LDFLAGS+= -g +else + CFLAGS+= -s -O3 -fomit-frame-pointer -mprobe + LDFLAGS+= -s +endif +CFLAGS+= $(PY_DEF) +ifeq ($(ASSERTIONS),no) + CFLAGS+= -DNDEBUG +endif +ifeq ($(FIXED_PYHOME),yes) + CFLAGS+= -DPREFIX=$(DQUOTE)$(LIB_DIR)$(DQUOTE) +endif + +# We're using the OMF format since EMX's ld has a obscure bug +# because of which it sometimes fails to build relocations +# in .data segment that point to another .data locations +# (except for the final linking if the .EXEs) +OMF= yes + +# if fork() support is required, the main executable must be linked with ld +EXEOMF= no + +# File extensions +MODULE.EXT= .pyd +MODLIB.EXT= .dll +ifeq ($(OMF),yes) + O= .obj + A= .lib + AR= emxomfar + CFLAGS+= -Zomf + LDFLAGS+= -Zomf + ifeq ($(MODE),debug) + ARFLAGS= -p64 crs + else + ARFLAGS= -p32 crs + endif +else + O= .o + A= .a + AR= ar +endif + +# EMX's default number of file handles is 40, which is sometimes insufficient +# (the tempfile regression test tries to create 100 temporary files) +NFILES=250 + +# Source file paths +SRCPATH=.;../../Python;../../Parser;../../Objects;../../Include;../../Modules +# Python contains the central core, containing the builtins and interpreter. +# Parser contains Python's Internal Parser and +# Standalone Parser Generator Program (Shares Some of Python's Modules) +# Objects contains Python Object Types +# Modules contains extension Modules (Built-In or as Separate DLLs) + +# Unix shells tend to use "$" as delimiter for variable names. +# Test for this behaviour and set $(BUCK) variable correspondigly ... +__TMP__:=$(shell echo $$$$) +ifeq ($(__TMP__),$$$$) + BUCK= $$ + BRO= ( + BRC= ) +else + BUCK= \$$ + BRO= \( + BRC= \) +endif +# Compute the "double quote" variable +__TMP__:=$(shell echo "") +ifeq ($(__TMP__),"") + DQUOTE= " +else + DQUOTE= \" +endif + +# Include paths +#INCLUDE= -I$(subst ;, -I, $(SRCPATH)) +INCLUDE= -I. -I../../Include + +# Path to search for .c files +vpath %.c .;..;$(SRCPATH) + +# Top of the package tree +TOP= ../../ + +# Directory for output files +OUTBASE= out/ +OUT= $(OUTBASE)$(MODE)/ + +# Additional libraries +LIBS= -lsocket + +# Utility macro: replacement for $^ +^^= $(filter-out %$A,$^) +# Use $(L^) to link with all libraries specified as dependencies +L^= $(addprefix -l,$(basename $(notdir $(filter %$A,$+)))) + +# Build rules +$(OUT)%$O: %.c + $(CC) $(CFLAGS.LIB) -c $< -o $@ + +%.a: + $(LD) $(LDFLAGS.A) -o $@ $(^^) $(L^) + +%.dll: + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) + +%.pyd: $(OUT)%module$O $(OUT)%_m.def + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(PYTHON.IMPLIB) $(LIBS) + +%.exe: + $(LD) $(LDFLAGS.EXE) -o $@ $(^^) $(L^) + +%_m.def: + @echo Creating .DEF file: $@ + @echo LIBRARY $(notdir $*) INITINSTANCE TERMINSTANCE >$@ + ifeq ($(DESCRIPTION.$(notdir $*)$(MODULE.EXT)),) + @echo DESCRIPTION $(DQUOTE)Python standard module $(notdir $*)$(DQUOTE) >>$@ + else + @echo DESCRIPTION $(DQUOTE)$(DESCRIPTION.$(notdir $*)$(MODULE.EXT))$(DQUOTE) >>$@ + endif + @echo DATA MULTIPLE NONSHARED >>$@ + @echo EXPORTS >>$@ + @echo init$(notdir $*) >>$@ + +%.def: + @echo Creating .DEF file: $@ + @echo NAME $(notdir $*) $(EXETYPE.$(notdir $*).exe) >$@ + @echo DESCRIPTION $(DQUOTE)$(DESCRIPTION.$(notdir $*).exe)$(DQUOTE) >>$@ + @echo STACKSIZE 1572864 >>$@ + +# Output file names +PYTHON_VER= 2.3 +PYTHON_LIB= python23 +PYTHON.LIB= $(PYTHON_LIB)_s$A +PYTHON.IMPLIB= $(PYTHON_LIB)$A +ifeq ($(EXEOMF),yes) + PYTHON.EXEIMP= $(PYTHON.IMPLIB) + LDMODE.EXE= -Zomf +else + PYTHON.EXEIMP= $(PYTHON_LIB).a + LDMODE.EXE = +endif +PYTHON.DLL= $(PYTHON_LIB).dll +PYTHON.DEF= $(PYTHON_LIB).def +PYTHON.EXE= python.exe +PYTHONPM.EXE= pythonpm.exe +PGEN.EXE= pgen.exe +LIBRARY= $(PYTHON.LIB) +LD_LIBRARY= $(PYTHON.IMPLIB) + +# Additional executable parameters +EXETYPE.$(PYTHON.EXE)= WINDOWCOMPAT +EXETYPE.$(PYTHONPM.EXE)= WINDOWAPI +EXETYPE.$(PGEN.EXE)= WINDOWCOMPAT +DESCRIPTION.$(PYTHON.EXE)= Python object-oriented programming language interpreter for OS/2 +DESCRIPTION.$(PYTHONPM.EXE)= $(DESCRIPTION.$(PYTHON.EXE)) +DESCRIPTION.$(PGEN.EXE)= Python object-oriented programming language parser generator for OS/2 + +# Module descriptions +DESCRIPTION.zlib$(MODULE.EXT)= Python Extension DLL for accessing the InfoZip compression library +DESCRIPTION.crypt$(MODULE.EXT)= Python Extension DLL implementing the crypt$(BRO)$(BRC) function +DESCRIPTION._tkinter$(MODULE.EXT)= Python Extension DLL for access to Tcl/Tk Environment +DESCRIPTION.mpz$(MODULE.EXT)= Python Extension DLL for access to GNU multi-precision library +DESCRIPTION.readline$(MODULE.EXT)= Python Extension DLL for access to GNU ReadLine library +DESCRIPTION.bsddb185$(MODULE.EXT)= Python Extension DLL for access to BSD DB (v1.85) library +DESCRIPTION._curses$(MODLIB.EXT)= Python Extension DLL for access to ncurses library +DESCRIPTION.pyexpat$(MODULE.EXT)= Python Extension DLL for access to expat library +DESCRIPTION.bz2$(MODULE.EXT)= Python Extension DLL for accessing the bz2 compression library + +# Source files +SRC.OS2EMX= config.c dlfcn.c getpathp.c +SRC.MAIN= $(addprefix $(TOP), \ + Modules/getbuildinfo.c \ + Modules/main.c) +SRC.MODULES= $(addprefix $(TOP), \ + Modules/gcmodule.c \ + Modules/signalmodule.c \ + Modules/posixmodule.c \ + Modules/threadmodule.c \ + Modules/arraymodule.c \ + Modules/binascii.c \ + Modules/cmathmodule.c \ + Modules/_codecsmodule.c \ + Modules/cPickle.c \ + Modules/cStringIO.c \ + Modules/_csv.c \ + Modules/datetimemodule.c \ + Modules/dlmodule.c \ + Modules/errnomodule.c \ + Modules/fcntlmodule.c \ + Modules/imageop.c \ + Modules/itertoolsmodule.c \ + Modules/_localemodule.c \ + Modules/mathmodule.c \ + Modules/md5c.c \ + Modules/md5module.c \ + Modules/operator.c \ + Modules/pcremodule.c \ + Modules/pypcre.c \ + Modules/_randommodule.c \ + Modules/regexmodule.c \ + Modules/regexpr.c \ + Modules/rgbimgmodule.c \ + Modules/shamodule.c \ + Modules/_sre.c \ + Modules/stropmodule.c \ + Modules/structmodule.c \ + Modules/symtablemodule.c \ + Modules/termios.c \ + Modules/timemodule.c \ + Modules/timingmodule.c \ + Modules/_weakref.c \ + Modules/xreadlinesmodule.c \ + Modules/xxsubtype.c \ + Modules/zipimport.c) +SRC.PARSE1= $(addprefix $(TOP), \ + Parser/acceler.c \ + Parser/grammar1.c \ + Parser/listnode.c \ + Parser/node.c \ + Parser/parser.c \ + Parser/parsetok.c \ + Parser/bitset.c \ + Parser/metagrammar.c) +SRC.PARSE2= $(addprefix $(TOP), \ + Parser/tokenizer.c \ + Parser/myreadline.c) +SRC.PARSER= $(SRC.PARSE1) \ + $(SRC.PARSE2) +SRC.PYTHON= $(addprefix $(TOP), \ + Python/bltinmodule.c \ + Python/exceptions.c \ + Python/ceval.c \ + Python/compile.c \ + Python/codecs.c \ + Python/errors.c \ + Python/frozen.c \ + Python/frozenmain.c \ + Python/future.c \ + Python/getargs.c \ + Python/getcompiler.c \ + Python/getcopyright.c \ + Python/getmtime.c \ + Python/getplatform.c \ + Python/getversion.c \ + Python/graminit.c \ + Python/import.c \ + Python/importdl.c \ + Python/marshal.c \ + Python/modsupport.c \ + Python/mysnprintf.c \ + Python/mystrtoul.c \ + Python/pyfpe.c \ + Python/pystate.c \ + Python/pythonrun.c \ + Python/structmember.c \ + Python/symtable.c \ + Python/sysmodule.c \ + Python/traceback.c \ + Python/getopt.c \ + Python/dynload_shlib.c \ + Python/thread.c) +SRC.OBJECT= $(addprefix $(TOP), \ + Objects/abstract.c \ + Objects/boolobject.c \ + Objects/bufferobject.c \ + Objects/cellobject.c \ + Objects/classobject.c \ + Objects/cobject.c \ + Objects/complexobject.c \ + Objects/descrobject.c \ + Objects/dictobject.c \ + Objects/enumobject.c \ + Objects/fileobject.c \ + Objects/floatobject.c \ + Objects/frameobject.c \ + Objects/funcobject.c \ + Objects/intobject.c \ + Objects/iterobject.c \ + Objects/listobject.c \ + Objects/longobject.c \ + Objects/methodobject.c \ + Objects/moduleobject.c \ + Objects/object.c \ + Objects/obmalloc.c \ + Objects/rangeobject.c \ + Objects/sliceobject.c \ + Objects/stringobject.c \ + Objects/structseq.c \ + Objects/tupleobject.c \ + Objects/typeobject.c \ + Objects/unicodeobject.c \ + Objects/unicodectype.c \ + Objects/weakrefobject.c) + +SRC.LIB= $(SRC.OS2EMX) \ + $(SRC.MAIN) \ + $(SRC.PARSER) \ + $(SRC.OBJECT) \ + $(SRC.PYTHON) \ + $(SRC.MODULES) +OBJ.LIB= $(addprefix $(OUT),$(notdir $(SRC.LIB:.c=$O))) + +SRC.PGEN= $(SRC.PARSE1) \ + $(addprefix $(TOP), \ + Objects/obmalloc.c) \ + $(addprefix $(TOP), \ + Python/mysnprintf.c) \ + $(addprefix $(TOP), \ + Parser/tokenizer_pgen.c \ + Parser/pgenmain.c \ + Parser/pgen.c \ + Parser/printgrammar.c \ + Parser/grammar.c \ + Parser/firstsets.c) \ + +OBJ.PGEN= $(addprefix $(OUT),$(notdir $(SRC.PGEN:.c=$O))) + +SRC.EXE= $(TOP)Modules/python.c +SRC.PMEXE= pythonpm.c + +# Python modules to be dynamically loaded that: +# 1) have only single source file and require no extra libs +# 2) use the standard module naming convention +# (the 'module' in ?????module.c is assumed) +# - these can be built with implicit rules +EASYEXTMODULES= fpectl \ + fpetest \ + parser \ + pwd \ + rotor \ + select + +# Python modules to be dynamically loaded that need explicit build rules +# (either multiple source files and/or non-standard module naming) +# (NOTE: use shortened names for modules affected by 8 char name limit) +HARDEXTMODULES= _hotshot \ + _socket \ + _testcap \ + unicoded + +# Python modules that are used as libraries and therefore must use +# a .DLL extension +LIBEXTMODULES= + +# Python external ($(MODULE.EXT)) modules - can be EASY or HARD +ifeq ($(HAVE_ZLIB),yes) + HARDEXTMODULES+= zlib +endif +ifeq ($(HAVE_UFC),yes) + HARDEXTMODULES+= crypt +endif +ifeq ($(HAVE_TCLTK),yes) + HARDEXTMODULES+= _tkinter + CFLAGS+= -DHAS_DIRENT -I/TclTk80/include + TK_LIBS+= -L/TclTk80/lib -ltcl80 -ltk80 +endif +ifeq ($(HAVE_GMPZ),yes) + HARDEXTMODULES+= mpz +endif +ifeq ($(HAVE_GREADLINE),yes) + HARDEXTMODULES+= readline +endif +ifeq ($(HAVE_BSDDB),yes) + HARDEXTMODULES+= bsddb185 +endif +ifeq ($(HAVE_NCURSES),yes) + LIBEXTMODULES+= _curses + HARDEXTMODULES+= _curses_ +endif +ifeq ($(HAVE_GDBM),yes) + HARDEXTMODULES+= gdbm dbm +endif +ifeq ($(HAVE_BZ2),yes) + HARDEXTMODULES+= bz2 +endif + +# Expat is now distributed with the Python source +HARDEXTMODULES+= pyexpat +EXPAT.INC= -I../../Modules/expat +EXPAT.DEF= -DHAVE_EXPAT_H -DXML_NS=1 -DXML_DTD=1 -DXML_BYTE_ORDER=12 \ + -DXML_CONTENT_BYTES=1024 +EXPAT.SRC= $(addprefix ../../Modules/expat/, \ + xmlparse.c \ + xmlrole.c \ + xmltok.c) + +# all the external modules +EXTERNDLLS= $(addsuffix $(MODULE.EXT),$(patsubst %module,%,$(EASYEXTMODULES))) +EXTERNDLLS+= $(addsuffix $(MODULE.EXT),$(patsubst %module,%,$(HARDEXTMODULES))) +EXTERNDLLS+= $(addsuffix $(MODLIB.EXT),$(patsubst %module,%,$(LIBEXTMODULES))) + +# Targets +all: $(OUT) $(PYTHON.LIB) $(PYTHON.DEF) $(PYTHON.IMPLIB) $(PYTHON.DLL) \ + python_noncore + +python_noncore: + make PY_DEF= $(PYTHON.EXE) $(PYTHONPM.EXE) $(PGEN.EXE) $(EXTERNDLLS) + +clean: + rm -f $(OUT)* + rm -f $(PYTHON.LIB) $(PYTHON.IMPLIB) $(PYTHON.EXEIMP) $(PYTHON.DLL) \ + $(PYTHON.EXE) $(PYTHONPM.EXE) $(PGEN.EXE) *$(MODULE.EXT) + find ../../Lib -name "*.py[co]" -exec rm {} ";" + +lx: + @echo Packing everything with lxLite... + lxlite $(PYTHON.DLL) $(PYTHON.EXE) $(PYTHONPM.EXE) $(PGEN.EXE) + +depend: $(OUTBASE) + makedep -f $(OUTBASE)python.dep -o $(BUCK)O -p $(BUCK)\(OUT\) \ + -r -c $(INCLUDE) $(SRC.LIB) $(SRC.PGEN) + +$(OUT): $(OUTBASE) + +$(OUT) $(OUTBASE): + mkdir.exe $@ + +$(PYTHON.LIB): $(OBJ.LIB) + rm.exe -f $@ + $(AR) $(ARFLAGS) $@ $^ + +# the Python core DLL .def file needs to have a number of non-static +# symbols that aren't part of the Python C API removed (commented out) +# from the DLL export list. +$(PYTHON.DEF): $(PYTHON.LIB) + @echo Creating .DEF file: $@ + @echo LIBRARY $(PYTHON_LIB) INITINSTANCE TERMINSTANCE >$@ + @echo DESCRIPTION $(DQUOTE)Python $(PYTHON_VER) Core DLL$(DQUOTE) >>$@ + @echo PROTMODE >>$@ + @echo DATA MULTIPLE NONSHARED >>$@ + @echo EXPORTS >>$@ + $(EXPLIB) -u $(PYTHON.LIB) |\ + sed -e "/^ .init.*/s/^ /; /" \ + -e "/^ .pcre_.*/s/^ /; /" \ + -e "/^ .array_methods/s/^ /; /" \ + -e "/^ .fast_save_leave/s/^ /; /" \ + -e "/^ .dlopen/s/^ /; /" \ + -e "/^ .dlsym/s/^ /; /" \ + -e "/^ .dlclose/s/^ /; /" \ + -e "/^ .dlerror/s/^ /; /" \ + -e "/^ ._Py_re_.*/s/^ /; /" \ + -e "/^ ._Py_MD5.*/s/^ /; /" >>$@ + +$(PYTHON.IMPLIB): $(PYTHON.DEF) + $(IMPLIB) -o $@ $^ + +$(PYTHON.EXEIMP): $(PYTHON.DEF) + $(IMPLIB) -o $@ $^ + +$(PYTHON.DLL): $(OUT)dllentry$O $(PYTHON.LIB) $(PYTHON.DEF) + +# Explicit make targets for the .EXEs to be able to use LD to link +# (so that fork() will work if required) + +$(PYTHON.EXE): $(SRC.EXE) $(PYTHON.EXEIMP) $(OUT)python.def + $(CC) -Zmt $(LDMODE.EXE) -Zcrtdll -Wall $(INCLUDE) -L. -lgcc -o $@ $(SRC.EXE) $(PYTHON.EXEIMP) $(LIBS) $(OUT)python.def + $(EXEOPT) -aq $(PYTHON.EXE) -h$(NFILES) + +$(PYTHONPM.EXE): $(SRC.PMEXE) $(PYTHON.EXEIMP) $(OUT)pythonpm.def + $(CC) -Zmt $(LDMODE.EXE) -Zcrtdll -Wall $(INCLUDE) -L. -lgcc -o $@ $(SRC.PMEXE) $(PYTHON.EXEIMP) $(LIBS) $(OUT)pythonpm.def + $(EXEOPT) -aq $(PYTHONPM.EXE) -h$(NFILES) + +$(PGEN.EXE): $(OBJ.PGEN) $(OUT)pgen.def + +# Explicit building instructions for those external modules that require +# awkward handling (due e.g. to non-std naming, or multiple source files) +# - standard modules + +_hotshot$(MODULE.EXT): $(OUT)_hotshot$O $(OUT)_hotshot_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) + +_socket$(MODULE.EXT): $(OUT)socketmodule$O $(OUT)_socket_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) + +# _testcapi needs to be renamed to be useful +_testcapi$(MODULE.EXT): $(OUT)_testcapimodule$O $(OUT)_testcapi_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) + +_testcap$(MODULE.EXT): _testcapi$(MODULE.EXT) + cp $^ $@ + +# unicodedata needs to be renamed to be useful +unicodedata$(MODULE.EXT): $(OUT)unicodedata$O $(OUT)unicodedata_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) $(MODULE_LIBS) + +unicoded$(MODULE.EXT): unicodedata$(MODULE.EXT) + cp $^ $@ + +# - optional modules (requiring other software to be installed) +bsddb185$(MODULE.EXT): $(OUT)bsddbmodule$O $(OUT)bsddb185_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) -ldb $(LIBS) + +crypt$(MODULE.EXT): $(OUT)cryptmodule$O $(OUT)crypt_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) -lufc $(LIBS) + +# The _curses_panel module requires a couple of ncurses library entry +# points, which are best exposed as exports from the _curses module DLL +$(OUT)_curses_m.def: + @echo Creating .DEF file: $@ + @echo LIBRARY $(notdir $*) INITINSTANCE TERMINSTANCE >$@ + @echo DESCRIPTION $(DQUOTE)$(DESCRIPTION.$(notdir $*)$(MODLIB.EXT))$(DQUOTE) >>$@ + @echo DATA MULTIPLE NONSHARED >>$@ + @echo EXPORTS >>$@ + @echo init_curses >>$@ + @echo wnoutrefresh >>$@ + @echo _nc_panelhook >>$@ + @echo is_linetouched >>$@ + @echo mvwin >>$@ + @echo stdscr >>$@ + @echo wtouchln >>$@ + +$(OUT)_curses_panel_m.def: + @echo Creating .DEF file: $@ + @echo LIBRARY $(notdir $*) INITINSTANCE TERMINSTANCE >$@ + @echo DESCRIPTION $(DQUOTE)Python standard module $(notdir $*)$(DQUOTE) >>$@ + @echo DATA MULTIPLE NONSHARED >>$@ + @echo IMPORTS >>$@ + @echo _curses.wnoutrefresh >>$@ + @echo _curses._nc_panelhook >>$@ + @echo _curses.is_linetouched >>$@ + @echo _curses.mvwin >>$@ + @echo _curses.stdscr >>$@ + @echo _curses.wtouchln >>$@ + @echo EXPORTS >>$@ + @echo init_curses_panel >>$@ + +_curses$(MODLIB.EXT): $(OUT)_cursesmodule$O $(OUT)_curses_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) -lncurses + +# curses_panel needs to be renamed to be useful +_curses_panel$(MODULE.EXT): $(OUT)_curses_panel$O $(OUT)_curses_panel_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) -lpanel + +_curses_$(MODULE.EXT): _curses_panel$(MODULE.EXT) + cp $^ $@ + +dbm$(MODULE.EXT): $(OUT)dbmmodule$O $(OUT)dbm_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) -lgdbm + +gdbm$(MODULE.EXT): $(OUT)gdbmmodule$O $(OUT)gdbm_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) -lgdbm + +mpz$(MODULE.EXT): $(OUT)mpzmodule$O $(OUT)mpz_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) -lgmp + +# Expat is now distributed with Python, so use the included version +$(OUT)pyexpat$O: ../../Modules/pyexpat.c + $(CC) $(CFLAGS) $(EXPAT.INC) -c -o $@ $^ +$(OUT)xmlparse$O: ../../Modules/expat/xmlparse.c + $(CC) $(CFLAGS) $(EXPAT.INC) $(EXPAT.DEF) -c -o $@ $^ +$(OUT)xmlrole$O: ../../Modules/expat/xmlrole.c + $(CC) $(CFLAGS) $(EXPAT.INC) $(EXPAT.DEF) -c -o $@ $^ +$(OUT)xmltok$O: ../../Modules/expat/xmltok.c + $(CC) $(CFLAGS) $(EXPAT.INC) $(EXPAT.DEF) -c -o $@ $^ +pyexpat$(MODULE.EXT): $(OUT)pyexpat$O $(OUT)xmlparse$O $(OUT)xmlrole$O \ + $(OUT)xmltok$O $(OUT)pyexpat_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) + +readline$(MODULE.EXT): $(OUT)readline$O $(OUT)readline_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) -lreadline -lncurses + +#_tkinter$(MODULE.EXT): $(OUT)_tkinter$O $(OUT)tclNotify$O $(OUT)tkappinit$O +_tkinter$(MODULE.EXT): $(OUT)_tkinter$O $(OUT)tclNotify$O \ + $(OUT)_tkinter_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) $(TK_LIBS) + +zlib$(MODULE.EXT): $(OUT)zlibmodule$O $(OUT)zlib_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) -lz + +bz2$(MODULE.EXT): $(OUT)bz2module$O $(OUT)bz2_m.def $(PYTHON.IMPLIB) + $(LD) $(LDFLAGS.DLL) -o $@ $(^^) $(L^) $(LIBS) -lbz2 + +# the test target +test: + -find ../../Lib -name "*.py[co]" -exec rm {} ";" + -./python -E -tt ../../lib/test/regrtest.py -l -u "network" + ./python -E -tt ../../lib/test/regrtest.py -l -u "network" + +-include $(OUTBASE)python.dep diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/README.os2emx b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/README.os2emx new file mode 100644 index 00000000..de8a3a0e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/README.os2emx @@ -0,0 +1,671 @@ +This is a port of Python 2.3.3 to OS/2 using the EMX development tools +========================================================================= + +What's new since the previous release +------------------------------------- + +Another day, another version... + + +Licenses and info about Python and EMX +-------------------------------------- + +Please read the file README.Python-2.3.3 included in this package for +information about Python 2.3.3. This file is the README file from the +Python 2.3.3 source distribution available via http://www.python.org/ +and its mirrors. The file LICENCE.Python-2.3.3 is the text of the Licence +from the Python 2.3.3 source distribution. + +Note that the EMX package that this package depends on is released under +the GNU General Public Licence. Please refer to the documentation +accompanying the EMX Runtime libraries for more information about the +implications of this. A copy of version 2 of the GPL is included as the +file COPYING.gpl2. + +Readline and GDBM are covered by the GNU General Public Licence. I think +Eberhard Mattes' porting changes to BSD DB v1.85 are also GPL'ed (BSD DB +itself is BSD Licenced). ncurses and expat appear to be covered by MIT +style licences - please refer to the source distributions for more detail. +zlib is distributable under a very free license. GNU MP and GNU UFC are +under the GNU LGPL (see file COPYING.lib). + +My patches to the Python-2.x source distributions, and any other packages +used in this port, are placed in the public domain. + +This software is provided 'as-is', without any express or implied warranty. +In no event will the author be held liable for any damages arising from the +use of the software. + +I do hope however that it proves useful to someone. + + +Other ports +----------- + +There have been ports of previous versions of Python to OS/2. + +The best known would be that by Jeff Rush, most recently of version +1.5.2. Jeff used IBM's Visual Age C++ (v3) for his ports, and his +patches have been included in the Python 2.3.3 source distribution. + +Andy Zabolotny implemented a port of Python v1.5.2 using the EMX +development tools. His patches against the Python v1.5.2 source +distribution have become the core of this port, and without his efforts +this port wouldn't exist. Andy's port also appears to have been +compiled with his port of gcc 2.95.2 to EMX, which I have but have +chosen not to use for the binary distribution of this port (see item 16 +of the "YOU HAVE BEEN WARNED" section below). + +It is possible to have these earlier ports still usable after installing +this port - see the README.os2emx.multiple_versions file, contributed by +Dr David Mertz, for a suggested approach to achieving this. + + +Software requirements +--------------------- + +This package requires the EMX Runtime package, available from the +Hobbes (http://hobbes.nmsu.edu/) and LEO (http://archiv.leo.org/) +archives of OS/2 software. I have used EMX version 0.9d fix04 in +developing this port. + +My development system is running OS/2 v4 with fixpack 12. + +3rd party software which has been linked into dynamically loaded modules: +- ncurses (see http://dickey.his.com/ for more info, v5.2) +- GNU Readline (Kai Uwe Rommel's port available from Hobbes or LEO, v2.1) +- GNU GDBM (Kai Uwe Rommel's port available from Hobbes or LEO, v1.7.3) +- zlib (derived from Hung-Chi Chu's port of v1.1.3, v1.1.4) +- expat (distributed with Python, v1.95.6) +- GNU MP (Peter Meerwald's port available from LEO, v2.0.2) +- GNU UFC (Kai Uwe Rommel's port available from LEO, v2.0.4) + + +About this port +--------------- + +I have attempted to make this port as complete and functional as I can, +notwithstanding the issues in the "YOU HAVE BEEN WARNED" section below. + +Core components: + +Python.exe is linked as an a.out executable, ie using EMX method E1 +to compile & link the executable. This is so that fork() works (see +"YOU HAVE BEEN WARNED" item 1). + +Python23.dll is created as a normal OMF DLL, with an OMF import +library and module definition file. There is also an a.out (.a) import +library to support linking the DLL to a.out executables. The DLL +requires the EMX runtime DLLs. + +This port has been built with complete support for multithreading. + +Modules: + +With the exception of modules that have a significant code size, or are +not recommended or desired for normal use, the standard modules are now +built into the core DLL rather than configured as dynamically loadable +modules. This is for both reasons of performance (startup time) and +memory use (lots of small DLLs fragment the address space). + +I haven't yet changed the building of Python's dynamically loadable +modules over to using the DistUtils. + +See "YOU HAVE BEEN WARNED" item 3 for notes about the fcntl module, and +"YOU HAVE BEEN WARNED" item 10 for notes about the pwd and grp modules. + +This port supports case sensitive module import semantics, matching +the Windows release. This can be deactivated by setting the PYTHONCASEOK +environment variable (the value doesn't matter) - see "YOU HAVE BEEN WARNED" +item 12. + +Optional modules: + +Where I've been able to locate the required 3rd party packages already +ported to OS/2, I've built and included them. + +These include ncurses (_curses, _curses_panel), BSD DB (bsddb185), +GNU GDBM (gdbm, dbm), zlib (zlib), GNU Readline (readline), GNU MP (mpz) +and GNU UFC (crypt). + +Expat is now included in the Python release sourceball, and the pyexpat +module is always built. + +I have built these modules statically linked against the 3rd party +libraries. Unfortunately my attempts to use the dll version of GNU +readline have been a dismal failure, in that when the dynamically +linked readline module is active other modules immediately provoke a +core dump when imported. + +Only the BSD DB package (part of the BSD package distributed with EMX) +needs source modifications to be used for this port, pertaining to use +of errno with multithreading. + +The other packages, except for ncurses and zlib, needed Makefile changes +for multithreading support but no source changes. + +The _curses_panel module is a potential problem - see "YOU HAVE BEEN +WARNED" item 13. + +Upstream source patches: + +No updates to the Python 2.3.3 release have become available. + +Eberhard Mattes' EMXFIX04 update to his EMX 0.9d tools suite includes +bug fixes for the BSD DB library. The bsddb module included in this +port incorporates these fixes. + +Library and other distributed Python code: + +The Python standard library lives in the Lib directory. All the standard +library code included with the Python 2.3.3 source distribution is included +in the binary archive, with the exception of the dos-8x3 and tkinter +subdirectories which have been omitted to reduce the size of the binary +archive - the dos-8x3 components are unnecessary duplicates and Tkinter +is not supported by this port (yet). All the plat-* subdirectories in the +source distribution have also been omitted, except for the plat-os2emx +subdirectory. + +The Tools and Demo directories contain a collection of Python scripts. +To reduce the size of the binary archive, the Demo/sgi, Demo/Tix, +Demo/tkinter, Tools/audiopy and Tools/IDLE subdirectories have been +omitted as not being supported by this port. The Misc directory has +also been omitted. + +All subdirectories omitted from the binary archive can be reconstituted +from the Python 2.3.3 source distribution, if desired. + +Support for building Python extensions: + +The Config subdirectory contains the files describing the configuration +of the interpreter and the Makefile, import libraries for the Python DLL, +and the module definition file used to create the Python DLL. The +Include subdirectory contains all the standard Python header files +needed for building extensions. + +As I don't have the Visual Age C++ compiler, I've made no attempt to +have this port support extensions built with that compiler. + + +Packaging +--------- + +This port is packaged as follows: +- python-2.3.3-os2emx-bin-03????.zip (binaries, library modules) +- python-2.3.3-os2emx-src-03???? (patches+makefiles for non-Python code) + +As all the Python specific patches for the port are now part of the +Python release tarball, only the patches and makefiles involved in +building external libraries for optional extensions are included in +the source archive. + +Documentation for the Python language, as well as the Python 2.3.3 +source distibution, can be obtained from the Python website +(http://www.python.org/) or the Python project pages at Sourceforge +(http://sf.net/projects/python/). + + +Installation +------------ + +Obtain and install, as per the included instructions, the EMX runtime +package. + +Unpack this archive, preserving the subdirectories, in the root directory +of the drive where you want Python to live. + +Add the Python directory (eg C:\Python23) to the PATH and LIBPATH +variables in CONFIG.SYS. + +You should then set the PYTHONHOME and PYTHONPATH environment variables +in CONFIG.SYS. + +PYTHONHOME should be set to Python's top level directory. PYTHONPATH +should be set to the semicolon separated list of principal Python library +directories. +I use: + SET PYTHONHOME=F:/Python23 + SET PYTHONPATH=F:/Python23/Lib;F:/Python23/Lib/plat-os2emx; + F:/Python23/Lib/lib-dynload;F:/Python23/Lib/site-packages + +NOTE!: the PYTHONPATH setting above is linewrapped for this document - it +should all be on one line in CONFIG.SYS! + +If you wish to use the curses module, you should set the TERM and TERMINFO +environment variables appropriately. + +If you don't already have ncurses installed, I have included a copy of the +EMX subset of the Terminfo database included with the ncurses-5.2 source +distribution. This can be used by setting the TERMINFO environment variable +to the path of the Terminfo subdirectory below the Python home directory. +On my system this looks like: + SET TERMINFO=F:/Python23/Terminfo + +For the TERM environment variable, I would try one of the following: + SET TERM=ansi + SET TERM=os2 + SET TERM=window + +You will have to reboot your system for these changes to CONFIG.SYS to take +effect. + +If you wish to compile all the included Python library modules to bytecode, +you can change into the Python home directory and run the COMPILEALL.CMD +batch file. + +You can execute the regression tests included with the Python 2.3.3 source +distribution by changing to the Python 2.3.3 home directory and executing the +REGRTEST.CMD batch file. The following tests are known to fail at this +time: +- test_mhlib (I don't know of any port of MH to OS/2); +- test_strptime (see "YOU HAVE BEEN WARNED" item 22); +- test_time (see "YOU HAVE BEEN WARNED" item 22); +- test_posixpath (see "YOU HAVE BEEN WARNED" item 23). + +Note that some of the network related tests expect the loopback interface +(interface "lo", with IP address 127.0.0.1) to be enabled, which from my +experience is not the default configuration. Additionally, test_popen2 +expects the "cat" utility (such as found in ports of the GNU tools) to +be installed. + + +Building from source +-------------------- + +With the EMX port now checked into Python's CVS repository, the build +infrastructure is part of the Python release sourceball. + +Prerequisites + +First and foremost, you need an operational EMX development installation - +EMX v0.9d with fix04 (the latest at time of writing) & the gcc 2.8.1 +compiler released by Eberhard Mattes is the recommended setup. + +If you have a different version of gcc installed, see "YOU HAVE BEEN +WARNED" item 16. + +Other items of software required:- + +- GNU make (I'm using v3.76.1) +- rm, cp, mkdir from the GNU file utilities package +- GNU find +- GNU sed + +Procedure + +0. all changes mentioned apply to files in the PC/os2emx subdirectory + of the Python release source tree. make is also executed from this + directory, so change into this directory before proceeding. + +1. decide if you need to change the location of the Python installation. + If you wish to do this, set the value of the Makefile variable LIB_DIR + to the directory you wish to use for PYTHONHOME + (eg /usr/local/lib/python2.3.3). + + If you want Python to find its library without the PYTHONHOME + environment variable set, set the value of the Makefile variable + FIXED_PYHOME to "yes" (uncomment the appropriate line). + +2. If you wish the Python executables (python.exe, pythonpm.exe & pgen.exe) + to be installed in a directory other than the PYTHONHOME directory, set + the value of the Makefile variable EXE_DIR to the appropriate directory. + +3. If you wish the Python core DLL (python23.dll) to be installed in a + directory other than the directory in which the Python executables are + installed (by default, the PYTHONHOME directory), set the value of the + Makefile variable DLL_DIR to the appropriate directory. This DLL must + be placed in a directory on the system's LIBPATH, or that gets set + with BEGINLIBPATH or ENDLIBPATH. + +4. If you have installed any of the libraries that can be used to build + optional Python modules, set the value of the relevant HAVE_ + Makefile variable to "yes". The Makefile currently supports: + + library Makefile variable + ........................................ + zlib (1.1.4) HAVE_ZLIB + GNU UltraFast Crypt HAVE_UFC + Tcl/Tk HAVE_TCLTK (not known to work) + GNU MP HAVE_GMPZ + GNU Readline HAVE_GREADLINE + BSD DB (v1.85) HAVE_BSDDB + ncurses HAVE_NCURSES + GNU gdbm HAVE_GDBM + libbz2 HAVE_BZ2 + + Please note that you need to check that what you have installed + is compatible with Python's build options. In particular, the + BSD DB v1.85 library needs to be rebuilt with a source patch for + multithread support (doesn't change the library's reentrant status + but allows it to be linked to Python which is multithreaded). + Widely available binary packages of other librarys & DLLs are + not built/linked with multithread support. Beware! + + Also note that the Makefile currently expects any libraries to be + found with the default library search path. You may need to add + -L switches to the LDFLAGS Makefile variable if you have installed + libraries in directories not in the default search path (which can + be controlled by the LIBRARY_PATH environment variable used by EMX). + +5. make + + It is usually a good idea to redirect the stdout and stderr streams + of the make process to log files, so that you can review any messages. + +6. make test + + This runs the Python regression tests, and completion is a sign of + a usable build. You should check the list of skipped modules to + ensure that any optional modules you selected have been built; + checking the list of failures against the list of known failures + elsewhere in this document is also prudent. + +7. make install + >>>>>> NOT YET COMPLETE <<<<<< + +8. change to a directory outside the Python source tree and start Python. + Check the version and build date to confirm satisfactory installation. + + +YOU HAVE BEEN WARNED!! +---------------------- + +I know about a number of nasties in this port. + +1. Eberhard Mattes, author of EMX, writes in his documentation that fork() +is very inefficient in the OS/2 environment. It also requires that the +executable be linked in a.out format rather than OMF. Use the os.exec +and/or the os.spawn family of functions where possible. + +2. In the absence of GNU Readline, terminating the interpreter requires a +control-Z (^Z) followed by a carriage return. Jeff Rush documented this +problem in his Python 1.5.2 port. With Readline, a control-D (^D) works +as per the standard Unix environment. + +3. EMX only has a partial implementation of fcntl(). The fcntl module +in this port supports what EMX supports. If fcntl is important to you, +please review the EMX C Library Reference (included in .INF format in the +EMXVIEW.ZIP archive as part of the complete EMX development tools suite). +Because of other side-effects I have modified the test_fcntl.py test +script to deactivate the exercising of the missing functionality. + +4. the PyBSDDB3 module has been imported into the Python standard +library, with the intent of superceding the BSDDB 1.85 module (bsddb). +As I don't yet have a satisfactory port of Sleepcat's more recent DB +library (3.3.x/4.0.x/4.1.x), I haven't included a binary of this +module. I have left the Python part of the PyBSDDB package in this +distribution for completeness. + +5. As a consequence of the PyBSDDB3 module being imported, the former +BSD DB (bsddb) module, linked against the DB v1.85 library from EMX, +has been renamed bsddb185. The bsddb185 module will not be built by +default on most platforms, but in the absence of a PyBSDDB3 module I +have retained it in the EMX port. + +Version 1.85 of the DB library is widely known to have bugs, although +some patches have become available (and are incorporated into the +included bsddb185 module). Unless you have problems with software +licenses which would rule out GDBM (and the dbm module because it is +linked against the GDBM library) or need it for file format compatibility, +you may be better off deleting it and relying on GDBM. + +Any code you have which uses the v1.85 bsddb module can be modified to +use the renamed module by changing + + import bsddb + +to + + import bsddb185 as bsddb + +6. The readline module has been linked against ncurses rather than the +termcap library supplied with EMX. + +7. I have configured this port to use "/" as the preferred path separator +character, rather than "\" ('\\'), in line with the convention supported +by EMX. Backslashes are still supported of course, and still appear in +unexpected places due to outside sources that don't get normalised. + +8. While the DistUtils components are now functional, other +packaging/binary handling tools and utilities such as those included in +the Demo and Tools directories - freeze in particular - are unlikely to +work. If you do get them going, I'd like to know about your success. + +9. I haven't set out to support the [BEGIN|END]LIBPATH functionality +supported by one of the earlier ports (Rush's??). If it works let me know. + +10. As a result of the limitations imposed by EMX's library routines, the +standard extension module pwd only synthesises a simple passwd database, +and the grp module cannot be supported at all. + +I have written pure Python substitutes for pwd and grp, which can process +real passwd and group files for those applications (such as MailMan) that +require more than EMX emulates. I have placed pwd.py and grp.py in +Lib/plat-os2emx, which is usually before Lib/lib-dynload (which contains +pwd.pyd) in the PYTHONPATH. If you have become attached to what pwd.pyd +supports, you can put Lib/lib-dynload before Lib/plat-os2emx in PYTHONPATH +or delete/rename pwd.py & grp.py. + +pwd.py & grp.py support locating their data files by looking in the +environment for them in the following sequence: +pwd.py: $ETC_PASSWD (%ETC_PASSWD%) + $ETC/passwd (%ETC%/passwd) + $PYTHONHOME/Etc/passwd (%PYTHONHOME%/Etc/passwd) +grp.py: $ETC_GROUP (%ETC_GROUP%) + $ETC/group (%ETC%/group) + $PYTHONHOME/Etc/group (%PYTHONHOME%/Etc/group) + +The ETC_PASSWD and ETC_GROUP environment variables are intended to allow +support for multiple passwd/grp files, where other applications may not +support as wide a variety of input variations (drive remappings, +separators etc). + +Both modules support using either the ":" character (Unix standard) or +";" (OS/2, DOS, Windows standard) field separator character, and pwd.py +implements the following drive letter conversions for the home_directory and +shell fields (for the ":" separator only): + $x -> x: + x; -> x: + +Example versions of passwd and group are in the Etc subdirectory. The +regression tests (test_pwd and test_grp) will fail if valid password and +group files cannot be found, but should pass otherwise. + +Be aware that Python's pwd & group modules are for reading password and +group information only. + +11. EMX's termios routines don't support all of the functionality now +exposed by the termios module - refer to the EMX documentation to find +out what is supported. + +12. The case sensitive import semantics introduced in Python 2.1 for other +case insensitive but case preserving file/operating systems (Windows etc), +have been incorporated into this port, and are active by default. Setting +the PYTHONCASEOK environment variable (to any value) reverts to the +previous (case insensitive) semantics. This can be an issue with some +file management utilities that do not preserve the case of file and +directory names. + +13. Because I am statically linking ncurses, the _curses_panel +module has potential problems arising from separate library data areas. +To avoid this, I have configured the _curses_.pyd (imported as +"_curses_panel") to import the ncurses symbols it needs from _curses.dll +(which is the curses module, but with a .dll extension rather than .pyd +so that the dynamic loader can actually import the symbols from it as a +DLL). + +The site module (Lib/site.py) has code added to tweak BEGINLIBPATH so +that _curses.dll is found when _curses_panel is imported. If you have +problems attempting to use the _curses_panel support please let me know, +and I'll have another look at this. + +14. sys.platform reports "os2emx" instead of "os2". os.name still +reports "os2". This change was to make it easier to distinguish between +the VAC++ build (formerly maintained by Michael Muller) and the EMX build +(this port), principally for DistUtils. + +15. it appears that the %W substitution in the EMX strftime() routine has +an off-by-one bug. strftime was listed as passing the regression tests +in previous releases, but this fact appears to have been an oversight in +the regression test suite. To fix this really requires a portable +strftime routine - I'm looking into using one from FreeBSD, but its not +ready yet. + +16. I have successfully built this port with Andy Zabolotny's ports of +pgcc 2.95 and gcc 3.2.1, in addition to EM's gcc 2.8.1. To use the +bsddb185 module with the gcc 3.2.1 build, I had to recompile the DB library +with gcc 3.2.1 - I don't know why, but trying to import the module built +against a DB library compiled with gcc 2.8.1 would result in a SYS3175 +error. + +I have not attempted to compile Python with any version of gcc prior to +v2.8.1. + +This release sees the default optimisation change to +"-O3 -fomit-frame-pointer -mprobe". This works fine too for pgcc 2.95 +but not for gcc 3.2.1. + +With gcc 3.2.1, -O3 causes 2 unexpected test failures: test_format and +test_unicode. Both these tests pass if -O2 is instead of -O3 with this +compiler, and the performance difference is negligible (in contrast to +gcc 2.8.1 and pgcc 2.95, where the performance difference between the +2 optimisation settings approaches 10%). + +17. os.spawnv() and os.spawnve() expose EMX's library routines rather +than use the emulation in os.py. + +In order to make use of some of the features this makes available in +the OS/2 environment, you should peruse the relevant EMX documentation +(EMXLIB.INF in the EMXVIEW.ZIP archive accompanying the EMX archives +on Hobbes or LEO). Be aware that I have exposed all the "mode" options +supported by EMX, but there are combinations that either cannot be +practically used by/in Python or have the potential to compromise your +system's stability. + +18. pythonpm.exe used to be just python.exe with the WINDOWAPI linker +option set in the pythonpm.def file. In practice, this turns out to do +nothing useful. + +I have written a replacement which wraps the Python DLL in a genuine +Presentation Manager application. This version actually runs the +Python interpreter in a separate thread from the PM shell, in order +that PythonPM has a functioning message queue as good PM apps should. +In its current state, PythonPM's window is hidden. It can be displayed, +although it will have no content as nothing is ever written to the +window. Only the "hide" button is available. Although the code +has support for shutting PythonPM down when the Python interpreter is +still busy (via the "control" menu), this is not well tested and given +comments I've come across in EMX documentation suggesting that the +thread killing operation has problems I would suggest caution in +relying on this capability. + +PythonPM processes commandline parameters normally. The standard input, +output and error streams are only useful if redirected, as PythonPM's +window is not a console in any form and so cannot accept or display +anything. This means that the -i option is ineffective. + +Because the Python thread doesn't create its own message queue, creating +PM Windows and performing most PM operations is not possible from within +this thread. How this will affect supporting PM extensions (such as +Tkinter using a PM port of Tcl/Tk, or wxPython using the PM port of +WxWindows) is still being researched. + +Note that os.fork() _DOES_NOT_WORK_ in PythonPM - SYS3175s are the result +of trying. os.spawnv() _does_ work. PythonPM passes all regression tests +that the standard Python interpreter (python.exe) passes, with the exception +of test_fork1 and test_socket which both attempt to use os.fork(). + +I very much want feedback on the performance, behaviour and utility of +PythonPM. I would like to add a PM console capability to it, but that +will be a non-trivial effort. I may be able to leverage the code in +Illya Vaes' Tcl/Tk port, which would make it easier. + +19. os.chdir() uses EMX's _chdir2(), which supports changing both drive +and directory at once. Similarly, os.getcwd() uses EMX's _getcwd() +which returns drive as well as path. + +20. pyconfig.h is installed in the Include subdirectory with all +other include files. + +21. the default build explicitly sets the number of file handles +available to a Python process to 250. EMX default is 40, which is +insufficient for the tempfile regression test (test_tempfile) which +tries to create 100 temporary files. + +This setting can be overridden via the EMXOPT environment variable: + set EMXOPT=-h250 +is equivalent to the setting currently used. The emxbind utility (if you +have it installed) can also be used to permanently change the setting in +python.exe - please refer to the EMX documentation for more information. + +22. a pure python strptime module is now part of the Python standard +library, superceding a platform specific extension module. This module +leverages the strftime module, and as a result test_strptime fails +due to the EMX strftime bug in item 20 above. + +23. test_posixpath attempts to exercise various Posix path related +functionality. Most of the sub-tests pass, but the "ismount" and +"samestat" subtests fail: +- EMX provides not satisfactory mount point emulation, so "ismount" + cannot succeed; +- EMX documents that successive stat() calls will produce different + results, so "samestat" cannot succeed. + +test_posixpath should skip these tests on EMX. + +24. I have had a report that attempting to use the Bittorrent package +(http://bitconjurer.org/BitTorrent/) with this port causes traps not +long after starting the download; this using the "headless" download +script on eCS v1.1. I have not been able to duplicate this myself, +but the indications I have suggest a failure in the 32 bit TCP/IP +stack (v4.3.2? on eCS v1.1) - on my v4.0 FP12 system with MPTS fixpack +WR8425 applied (16 bit TCP/IP stack v4.02), BitTorrent appears to work +normally in testing on a 100Mbit LAN. With the curses.panel fix (see +item 13 above), the BitTorrent curses downloader works too. I'd +appreciate any success or failure reports with BitTorrent, though +I've regretfully recommended that the person who reported the failure +take this up with eCS support. Since this report, I have received a +followup which suggests that the problem may have been a buggy network +card driver. I think it suffices to say that BitTorrent is a fair stress +test of a system's networking capability. + +... probably other issues that I've not encountered, or don't remember :-( + +If you encounter other difficulties with this port, which can be +characterised as peculiar to this port rather than to the Python release, +I would like to hear about them. However I cannot promise to be able to do +anything to resolve such problems. See the Contact section below... + + +To do... +-------- + +In no particular order of apparent importance or likelihood... + +- support Tkinter and/or alternative GUI (wxWindows??) + + +Credits +------- + +In addition to people identified above, I'd like to thank: +- the BDFL, Guido van Rossum, and crew for Python; +- Dr David Mertz, for trying out a pre-release of this port; +- the Python-list/comp.lang.python community; +- John Poltorak, for input about pwd/grp. + +Contact +------- + +Constructive feedback, negative or positive, about this port is welcome +and should be addressed to me at the e-mail addresses below. + +I have a private mailing list for announcements of fixes & updates to +this port. If you wish to receive such e-mail announcments, please send +me an e-mail requesting that you be added to this list. + +Andrew MacIntyre +E-mail: andymac@bullseye.apana.org.au, or andymac@pcug.org.au +Web: http://www.andymac.org/ + +2 December, 2003. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/config.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/config.c new file mode 100644 index 00000000..5dbe29cd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/config.c @@ -0,0 +1,180 @@ +/* -*- C -*- *********************************************** +Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, +The Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI or Corporation for National Research Initiatives or +CNRI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +While CWI is the initial source for this software, a modified version +is made available by the Corporation for National Research Initiatives +(CNRI) at the Internet address ftp://ftp.python.org. + +STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH +CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* Module configuration */ + +/* This file contains the table of built-in modules. + See init_builtin() in import.c. */ + +#include "Python.h" + +extern void initos2(); +extern void initsignal(); +#ifdef WITH_THREAD +extern void initthread(); +#endif +extern void init_codecs(); +extern void init_csv(); +extern void init_locale(); +extern void init_random(); +extern void init_sre(); +extern void init_symtable(); +extern void init_weakref(); +extern void initarray(); +extern void initbinascii(); +extern void initcPickle(); +extern void initcStringIO(); +extern void initcmath(); +extern void initdatetime(); +extern void initdl(); +extern void initerrno(); +extern void initfcntl(); +extern void initimageop(); +extern void inititertools(); +extern void initmath(); +extern void initmd5(); +extern void initoperator(); +extern void initpcre(); +extern void initregex(); +extern void initrgbimg(); +extern void initsha(); +extern void initstrop(); +extern void initstruct(); +extern void inittermios(); +extern void inittime(); +extern void inittiming(); +extern void initxreadlines(); +extern void initxxsubtype(); +extern void initzipimport(); +#if !HAVE_DYNAMIC_LOADING +extern void init_curses(); +extern void init_curses_panel(); +extern void init_hotshot(); +extern void init_testcapi(); +extern void initbsddb185(); +extern void initbz2(); +extern void initfpectl(); +extern void initfpetest(); +extern void initparser(); +extern void initpwd(); +extern void initrotor(); +extern void initunicodedata(); +extern void initzlib(); +#ifdef USE_SOCKET +extern void init_socket(); +extern void initselect(); +#endif +#endif +/* -- ADDMODULE MARKER 1 -- */ + +extern void PyMarshal_Init(); +extern void initimp(); +extern void initgc(); + +struct _inittab _PyImport_Inittab[] = { + + {"os2", initos2}, + {"signal", initsignal}, +#ifdef WITH_THREAD + {"thread", initthread}, +#endif + {"_codecs", init_codecs}, + {"_csv", init_csv}, + {"_locale", init_locale}, + {"_random", init_random}, + {"_sre", init_sre}, + {"_symtable", init_symtable}, + {"_weakref", init_weakref}, + {"array", initarray}, + {"binascii", initbinascii}, + {"cPickle", initcPickle}, + {"cStringIO", initcStringIO}, + {"cmath", initcmath}, + {"datetime", initdatetime}, + {"dl", initdl}, + {"errno", initerrno}, + {"fcntl", initfcntl}, + {"imageop", initimageop}, + {"itertools", inititertools}, + {"math", initmath}, + {"md5", initmd5}, + {"operator", initoperator}, + {"pcre", initpcre}, + {"regex", initregex}, + {"rgbimg", initrgbimg}, + {"sha", initsha}, + {"strop", initstrop}, + {"struct", initstruct}, + {"termios", inittermios}, + {"time", inittime}, + {"timing", inittiming}, + {"xreadlines", initxreadlines}, + {"xxsubtype", initxxsubtype}, + {"zipimport", initzipimport}, +#if !HAVE_DYNAMIC_LOADING + {"_curses", init_curses}, + {"_curses_panel", init_curses_panel}, + {"_hotshot", init_hotshot}, + {"_testcapi", init_testcapi}, + {"bsddb185", initbsddb185}, + {"bz2", initbz2}, + {"fpectl", initfpectl}, + {"fpetest", initfpetest}, + {"parser", initparser}, + {"pwd", initpwd}, + {"rotor", initrotor}, + {"unicodedata", initunicodedata}, + {"zlib", initzlib}, +#ifdef USE_SOCKET + {"_socket", init_socket}, + {"select", initselect}, +#endif +#endif +/* -- ADDMODULE MARKER 2 -- */ + + /* This module "lives in" with marshal.c */ + {"marshal", PyMarshal_Init}, + + /* This lives it with import.c */ + {"imp", initimp}, + + /* These entries are here for sys.builtin_module_names */ + {"__main__", NULL}, + {"__builtin__", NULL}, + {"sys", NULL}, + {"exceptions", NULL}, + + /* This lives in gcmodule.c */ + {"gc", initgc}, + + /* Sentinel */ + {0, 0} +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dlfcn.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dlfcn.c new file mode 100644 index 00000000..25a805fe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dlfcn.c @@ -0,0 +1,223 @@ +/* -*- C -*- *********************************************** +Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, +The Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI or Corporation for National Research Initiatives or +CNRI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +While CWI is the initial source for this software, a modified version +is made available by the Corporation for National Research Initiatives +(CNRI) at the Internet address ftp://ftp.python.org. + +STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH +CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* This library implements dlopen() - Unix-like dynamic linking + * emulation functions for OS/2 using DosLoadModule() and company. + */ + +#define INCL_DOS +#define INCL_DOSERRORS +#define INCL_DOSSESMGR +#define INCL_WINPROGRAMLIST +#define INCL_WINFRAMEMGR +#include + +#include +#include +#include +#include + +typedef struct _track_rec { + char *name; + HMODULE handle; + void *id; + struct _track_rec *next; +} tDLLchain, *DLLchain; + +static DLLchain dlload = NULL; /* A simple chained list of DLL names */ +static char dlerr [256]; /* last error text string */ +static void *last_id; + +static DLLchain find_id(void *id) +{ + DLLchain tmp; + + for (tmp = dlload; tmp; tmp = tmp->next) + if (id == tmp->id) + return tmp; + + return NULL; +} + +/* load a dynamic-link library and return handle */ +void *dlopen(char *filename, int flags) +{ + HMODULE hm; + DLLchain tmp; + char err[256]; + char *errtxt; + int rc = 0, set_chain = 0; + + for (tmp = dlload; tmp; tmp = tmp->next) + if (strnicmp(tmp->name, filename, 999) == 0) + break; + + if (!tmp) + { + tmp = (DLLchain) malloc(sizeof(tDLLchain)); + if (!tmp) + goto nomem; + tmp->name = strdup(filename); + tmp->next = dlload; + set_chain = 1; + } + + switch (rc = DosLoadModule((PSZ)&err, sizeof(err), filename, &hm)) + { + case NO_ERROR: + tmp->handle = hm; + if (set_chain) + { + do + last_id++; + while ((last_id == 0) || (find_id(last_id))); + tmp->id = last_id; + dlload = tmp; + } + return tmp->id; + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + errtxt = "module `%s' not found"; + break; + case ERROR_TOO_MANY_OPEN_FILES: + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_SHARING_BUFFER_EXCEEDED: +nomem: + errtxt = "out of system resources"; + break; + case ERROR_ACCESS_DENIED: + errtxt = "access denied"; + break; + case ERROR_BAD_FORMAT: + case ERROR_INVALID_SEGMENT_NUMBER: + case ERROR_INVALID_ORDINAL: + case ERROR_INVALID_MODULETYPE: + case ERROR_INVALID_EXE_SIGNATURE: + case ERROR_EXE_MARKED_INVALID: + case ERROR_ITERATED_DATA_EXCEEDS_64K: + case ERROR_INVALID_MINALLOCSIZE: + case ERROR_INVALID_SEGDPL: + case ERROR_AUTODATASEG_EXCEEDS_64K: + case ERROR_RELOCSRC_CHAIN_EXCEEDS_SEGLIMIT: + errtxt = "invalid module format"; + break; + case ERROR_INVALID_NAME: + errtxt = "filename doesn't match module name"; + break; + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + errtxt = "sharing violation"; + break; + case ERROR_INIT_ROUTINE_FAILED: + errtxt = "module initialization failed"; + break; + default: + errtxt = "cause `%s', error code = %d"; + break; + } + snprintf(dlerr, sizeof(dlerr), errtxt, &err, rc); + if (tmp) + { + if (tmp->name) + free(tmp->name); + free(tmp); + } + return 0; +} + +/* return a pointer to the `symbol' in DLL */ +void *dlsym(void *handle, char *symbol) +{ + int rc = 0; + PFN addr; + char *errtxt; + int symord = 0; + DLLchain tmp = find_id(handle); + + if (!tmp) + goto inv_handle; + + if (*symbol == '#') + symord = atoi(symbol + 1); + + switch (rc = DosQueryProcAddr(tmp->handle, symord, symbol, &addr)) + { + case NO_ERROR: + return (void *)addr; + case ERROR_INVALID_HANDLE: +inv_handle: + errtxt = "invalid module handle"; + break; + case ERROR_PROC_NOT_FOUND: + case ERROR_INVALID_NAME: + errtxt = "no symbol `%s' in module"; + break; + default: + errtxt = "symbol `%s', error code = %d"; + break; + } + snprintf(dlerr, sizeof(dlerr), errtxt, symbol, rc); + return NULL; +} + +/* free dynamicaly-linked library */ +int dlclose(void *handle) +{ + int rc; + DLLchain tmp = find_id(handle); + + if (!tmp) + goto inv_handle; + + switch (rc = DosFreeModule(tmp->handle)) + { + case NO_ERROR: + free(tmp->name); + dlload = tmp->next; + free(tmp); + return 0; + case ERROR_INVALID_HANDLE: +inv_handle: + strcpy(dlerr, "invalid module handle"); + return -1; + case ERROR_INVALID_ACCESS: + strcpy(dlerr, "access denied"); + return -1; + default: + return -1; + } +} + +/* return a string describing last occured dl error */ +char *dlerror() +{ + return dlerr; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dlfcn.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dlfcn.h new file mode 100644 index 00000000..833ef9b0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dlfcn.h @@ -0,0 +1,51 @@ +/* -*- C -*- *********************************************** +Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, +The Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI or Corporation for National Research Initiatives or +CNRI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +While CWI is the initial source for this software, a modified version +is made available by the Corporation for National Research Initiatives +(CNRI) at the Internet address ftp://ftp.python.org. + +STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH +CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* This library implements dlopen() - Unix-like dynamic linking + * emulation functions for OS/2 using DosLoadModule() and company. + */ + +#ifndef _DLFCN_H +#define _DLFCN_H + +/* load a dynamic-link library and return handle */ +void *dlopen(char *filename, int flags); + +/* return a pointer to the `symbol' in DLL */ +void *dlsym(void *handle, char *symbol); + +/* free dynamicaly-linked library */ +int dlclose(void *handle); + +/* return a string describing last occured dl error */ +char *dlerror(void); + +#endif /* !_DLFCN_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dllentry.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dllentry.c new file mode 100644 index 00000000..20baff37 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/dllentry.c @@ -0,0 +1,42 @@ +/* + * This is the entry point for the Python 2.3 core DLL. + */ + +#define NULL 0 + +#define REF(s) extern void s(); void *____ref_##s = &s; + +/* Make references to imported symbols to pull them from static library */ +REF(Py_Main); + +#include + +extern int _CRT_init(void); +extern void _CRT_term(void); +extern void __ctordtorInit(void); +extern void __ctordtorTerm(void); + +unsigned long _DLL_InitTerm(unsigned long mod_handle, unsigned long flag) +{ + switch (flag) + { + case 0: + if (_CRT_init()) + return 0; + __ctordtorInit(); + + /* Ignore fatal signals */ + signal(SIGSEGV, SIG_IGN); + signal(SIGFPE, SIG_IGN); + + return 1; + + case 1: + __ctordtorTerm(); + _CRT_term(); + return 1; + + default: + return 0; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/getpathp.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/getpathp.c new file mode 100644 index 00000000..183ce703 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/getpathp.c @@ -0,0 +1,407 @@ + +/* Return the initial module search path. */ +/* This version used by OS/2+EMX */ + +/* ---------------------------------------------------------------- + PATH RULES FOR OS/2+EMX: + This describes how sys.path is formed on OS/2+EMX. It describes the + functionality, not the implementation (ie, the order in which these + are actually fetched is different) + + * Python always adds an empty entry at the start, which corresponds + to the current directory. + + * If the PYTHONPATH env. var. exists, it's entries are added next. + + * We attempt to locate the "Python Home" - if the PYTHONHOME env var + is set, we believe it. Otherwise, we use the path of our host .EXE's + to try and locate our "landmark" (lib\\os.py) and deduce our home. + - If we DO have a Python Home: The relevant sub-directories (Lib, + plat-win, lib-tk, etc) are based on the Python Home + - If we DO NOT have a Python Home, the core Python Path is + loaded from the registry. This is the main PythonPath key, + and both HKLM and HKCU are combined to form the path) + + * Iff - we can not locate the Python Home, and have not had a PYTHONPATH + specified (ie, we have _nothing_ we can assume is a good path), a + default path with relative entries is used (eg. .\Lib;.\plat-win, etc) + + + The end result of all this is: + * When running python.exe, or any other .exe in the main Python directory + (either an installed version, or directly from the PCbuild directory), + the core path is deduced. + + * When Python is hosted in another exe (different directory, embedded via + COM, etc), the Python Home will not be deduced, so the core path from + the registry is used. Other "application paths "in the registry are + always read. + + * If Python can't find its home and there is no registry (eg, frozen + exe, some very strange installation setup) you get a path with + some default, but relative, paths. + + ---------------------------------------------------------------- */ + + +#include "Python.h" +#include "osdefs.h" + +#ifndef PYOS_OS2 +#error This file only compilable on OS/2 +#endif + +#define INCL_DOS +#include + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ + +/* Search in some common locations for the associated Python libraries. + * + * Py_GetPath() tries to return a sensible Python module search path. + * + * The approach is an adaptation for Windows of the strategy used in + * ../Modules/getpath.c; it uses the Windows Registry as one of its + * information sources. + */ + +#ifndef LANDMARK +#if defined(PYCC_GCC) +#define LANDMARK "lib/os.py" +#else +#define LANDMARK "lib\\os.py" +#endif +#endif + +static char prefix[MAXPATHLEN+1]; +static char progpath[MAXPATHLEN+1]; +static char *module_search_path = NULL; + + +static int +is_sep(char ch) /* determine if "ch" is a separator character */ +{ +#ifdef ALTSEP + return ch == SEP || ch == ALTSEP; +#else + return ch == SEP; +#endif +} + +/* assumes 'dir' null terminated in bounds. + * Never writes beyond existing terminator. + */ +static void +reduce(char *dir) +{ + size_t i = strlen(dir); + while (i > 0 && !is_sep(dir[i])) + --i; + dir[i] = '\0'; +} + +static int +exists(char *filename) +{ + struct stat buf; + return stat(filename, &buf) == 0; +} + +/* Is module (check for .pyc/.pyo too) + * Assumes 'filename' MAXPATHLEN+1 bytes long - + * may extend 'filename' by one character. + */ +static int +ismodule(char *filename) +{ + if (exists(filename)) + return 1; + + /* Check for the compiled version of prefix. */ + if (strlen(filename) < MAXPATHLEN) { + strcat(filename, Py_OptimizeFlag ? "o" : "c"); + if (exists(filename)) + return 1; + } + return 0; +} + +/* guarantees buffer will never overflow MAXPATHLEN+1 bytes */ +static void +join(char *buffer, char *stuff) +{ + size_t n, k; + if (is_sep(stuff[0])) + n = 0; + else { + n = strlen(buffer); + if (n > 0 && !is_sep(buffer[n-1]) && n < MAXPATHLEN) + buffer[n++] = SEP; + } + k = strlen(stuff); + if (n + k > MAXPATHLEN) + k = MAXPATHLEN - n; + strncpy(buffer+n, stuff, k); + buffer[n+k] = '\0'; +} + +/* gotlandmark only called by search_for_prefix, which ensures + * 'prefix' is null terminated in bounds. join() ensures + * 'landmark' can not overflow prefix if too long. + */ +static int +gotlandmark(char *landmark) +{ + int n, ok; + + n = strlen(prefix); + join(prefix, landmark); + ok = ismodule(prefix); + prefix[n] = '\0'; + return ok; +} + +/* assumes argv0_path is MAXPATHLEN+1 bytes long, already \0 term'd. + * assumption provided by only caller, calculate_path() + */ +static int +search_for_prefix(char *argv0_path, char *landmark) +{ + /* Search from argv0_path, until landmark is found */ + strcpy(prefix, argv0_path); + do { + if (gotlandmark(landmark)) + return 1; + reduce(prefix); + } while (prefix[0]); + return 0; +} + + +static void +get_progpath(void) +{ + extern char *Py_GetProgramName(void); + char *path = getenv("PATH"); + char *prog = Py_GetProgramName(); + + PPIB pib; + if ((DosGetInfoBlocks(NULL, &pib) == 0) && + (DosQueryModuleName(pib->pib_hmte, sizeof(progpath), progpath) == 0)) + return; + + if (prog == NULL || *prog == '\0') + prog = "python"; + + /* If there is no slash in the argv0 path, then we have to + * assume python is on the user's $PATH, since there's no + * other way to find a directory to start the search from. If + * $PATH isn't exported, you lose. + */ +#ifdef ALTSEP + if (strchr(prog, SEP) || strchr(prog, ALTSEP)) +#else + if (strchr(prog, SEP)) +#endif + strncpy(progpath, prog, MAXPATHLEN); + else if (path) { + while (1) { + char *delim = strchr(path, DELIM); + + if (delim) { + size_t len = delim - path; + /* ensure we can't overwrite buffer */ +#if !defined(PYCC_GCC) + len = min(MAXPATHLEN,len); +#else + len = MAXPATHLEN < len ? MAXPATHLEN : len; +#endif + strncpy(progpath, path, len); + *(progpath + len) = '\0'; + } + else + strncpy(progpath, path, MAXPATHLEN); + + /* join() is safe for MAXPATHLEN+1 size buffer */ + join(progpath, prog); + if (exists(progpath)) + break; + + if (!delim) { + progpath[0] = '\0'; + break; + } + path = delim + 1; + } + } + else + progpath[0] = '\0'; +} + +static void +calculate_path(void) +{ + char argv0_path[MAXPATHLEN+1]; + char *buf; + size_t bufsz; + char *pythonhome = Py_GetPythonHome(); + char *envpath = getenv("PYTHONPATH"); + char zip_path[MAXPATHLEN+1]; + size_t len; + + get_progpath(); + /* progpath guaranteed \0 terminated in MAXPATH+1 bytes. */ + strcpy(argv0_path, progpath); + reduce(argv0_path); + if (pythonhome == NULL || *pythonhome == '\0') { + if (search_for_prefix(argv0_path, LANDMARK)) + pythonhome = prefix; + else + pythonhome = NULL; + } + else + strncpy(prefix, pythonhome, MAXPATHLEN); + + if (envpath && *envpath == '\0') + envpath = NULL; + + /* Calculate zip archive path */ + strncpy(zip_path, progpath, MAXPATHLEN); + zip_path[MAXPATHLEN] = '\0'; + len = strlen(zip_path); + if (len > 4) { + zip_path[len-3] = 'z'; /* change ending to "zip" */ + zip_path[len-2] = 'i'; + zip_path[len-1] = 'p'; + } + else { + zip_path[0] = 0; + } + + /* We need to construct a path from the following parts. + * (1) the PYTHONPATH environment variable, if set; + * (2) the zip archive file path; + * (3) the PYTHONPATH config macro, with the leading "." + * of each component replaced with pythonhome, if set; + * (4) the directory containing the executable (argv0_path). + * The length calculation calculates #3 first. + */ + + /* Calculate size of return buffer */ + if (pythonhome != NULL) { + char *p; + bufsz = 1; + for (p = PYTHONPATH; *p; p++) { + if (*p == DELIM) + bufsz++; /* number of DELIM plus one */ + } + bufsz *= strlen(pythonhome); + } + else + bufsz = 0; + bufsz += strlen(PYTHONPATH) + 1; + bufsz += strlen(argv0_path) + 1; + bufsz += strlen(zip_path) + 1; + if (envpath != NULL) + bufsz += strlen(envpath) + 1; + + module_search_path = buf = malloc(bufsz); + if (buf == NULL) { + /* We can't exit, so print a warning and limp along */ + fprintf(stderr, "Can't malloc dynamic PYTHONPATH.\n"); + if (envpath) { + fprintf(stderr, "Using environment $PYTHONPATH.\n"); + module_search_path = envpath; + } + else { + fprintf(stderr, "Using default static path.\n"); + module_search_path = PYTHONPATH; + } + return; + } + + if (envpath) { + strcpy(buf, envpath); + buf = strchr(buf, '\0'); + *buf++ = DELIM; + } + if (zip_path[0]) { + strcpy(buf, zip_path); + buf = strchr(buf, '\0'); + *buf++ = DELIM; + } + + if (pythonhome == NULL) { + strcpy(buf, PYTHONPATH); + buf = strchr(buf, '\0'); + } + else { + char *p = PYTHONPATH; + char *q; + size_t n; + for (;;) { + q = strchr(p, DELIM); + if (q == NULL) + n = strlen(p); + else + n = q-p; + if (p[0] == '.' && is_sep(p[1])) { + strcpy(buf, pythonhome); + buf = strchr(buf, '\0'); + p++; + n--; + } + strncpy(buf, p, n); + buf += n; + if (q == NULL) + break; + *buf++ = DELIM; + p = q+1; + } + } + if (argv0_path) { + *buf++ = DELIM; + strcpy(buf, argv0_path); + buf = strchr(buf, '\0'); + } + *buf = '\0'; +} + + +/* External interface */ + +char * +Py_GetPath(void) +{ + if (!module_search_path) + calculate_path(); + return module_search_path; +} + +char * +Py_GetPrefix(void) +{ + if (!module_search_path) + calculate_path(); + return prefix; +} + +char * +Py_GetExecPrefix(void) +{ + return Py_GetPrefix(); +} + +char * +Py_GetProgramFullPath(void) +{ + if (!module_search_path) + calculate_path(); + return progpath; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/pyconfig.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/pyconfig.h new file mode 100644 index 00000000..79d54cb0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/pyconfig.h @@ -0,0 +1,327 @@ +#ifndef Py_CONFIG_H +#define Py_CONFIG_H + +/* config.h. + * At some time in the past, generated automatically by/from configure. + * now maintained manually. + */ + +/* build environment */ +#define PLATFORM "os2emx" +#define COMPILER "[EMX GCC " __VERSION__ "]" +#define PYOS_OS2 1 +#define PYCC_GCC 1 + +/* default location(s) */ +#ifndef PREFIX +#define PREFIX "" +#endif +#ifndef PYTHONPATH +#define PYTHONPATH "./Lib;./Lib/plat-" PLATFORM \ + ";./Lib/lib-dynload;./Lib/site-packages" +#endif + +/* Debugging */ +#ifndef Py_DEBUG +/*#define Py_DEBUG 1*/ +#endif + +/* if building an extension or wrapper executable, + * mark Python API symbols "extern" so that symbols + * imported from the Python core DLL aren't duplicated. + */ +#ifdef Py_BUILD_CORE +# define PyAPI_FUNC(RTYPE) RTYPE +#else +# define PyAPI_FUNC(RTYPE) extern RTYPE +#endif +#define PyAPI_DATA(RTYPE) extern RTYPE +#define PyMODINIT_FUNC void + +/* Use OS/2 flavour of threads */ +#define WITH_THREAD 1 +#define OS2_THREADS 1 + +/* We want sockets */ +#define TCPIPV4 1 +#define USE_SOCKET 1 +#define socklen_t int + +/* enable the Python object allocator */ +#define WITH_PYMALLOC 1 + +/* enable the GC module */ +#define WITH_CYCLE_GC 1 + +/* Define if you want to read files with foreign newlines. */ +#define WITH_UNIVERSAL_NEWLINES 1 + +/* Define if you want documentation strings in extension modules */ +#define WITH_DOC_STRINGS 1 + +/* Unicode related */ +#define Py_USING_UNICODE 1 +#define PY_UNICODE_TYPE wchar_t +#define Py_UNICODE_SIZE SIZEOF_SHORT + +/* system capabilities */ +#define HAVE_TTYNAME 1 +#define HAVE_WAIT 1 +#define HAVE_GETEGID 1 +#define HAVE_GETEUID 1 +#define HAVE_GETGID 1 +#define HAVE_GETPPID 1 +#define HAVE_GETUID 1 +#define HAVE_OPENDIR 1 +#define HAVE_PIPE 1 +#define HAVE_POPEN 1 +#define HAVE_SYSTEM 1 +#define HAVE_TTYNAME 1 +#define HAVE_DYNAMIC_LOADING 1 + +/* if port of GDBM installed, it includes NDBM emulation */ +#define HAVE_NDBM_H 1 + +/* need this for spawnv code in posixmodule (cloned from WIN32 def'n) */ +typedef long intptr_t; + +/* we don't have tm_zone but do have the external array tzname */ +#define HAVE_TZNAME 1 + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define this if you have the type long long. */ +#define HAVE_LONG_LONG 1 + +/* Define if your compiler supports function prototypes. */ +#define HAVE_PROTOTYPES 1 + +/* Define if your compiler supports variable length function prototypes + * (e.g. void fprintf(FILE *, char *, ...);) *and* . + */ +#define HAVE_STDARG_PROTOTYPES 1 + +/* Define if malloc(0) returns a NULL pointer. */ +#define MALLOC_ZERO_RETURNS_NULL 1 + +/* Define to force use of thread-safe errno, h_errno, and other functions. */ +#define _REENTRANT 1 + +/* Define if you can safely include both and + * (which you can't on SCO ODT 3.0). + */ +#define SYS_SELECT_WITH_SYS_TIME 1 + +/* The number of bytes in an off_t. */ +#define SIZEOF_OFF_T 4 + +/* The number of bytes in an time_t. */ +#define SIZEOF_TIME_T 4 + +/* The number of bytes in a short. */ +#define SIZEOF_SHORT 2 + +/* The number of bytes in a int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* The number of bytes in a long long. */ +#define SIZEOF_LONG_LONG 8 + +/* The number of bytes in a void *. */ +#define SIZEOF_VOID_P 4 + +/* Define if you have the alarm function. */ +#define HAVE_ALARM 1 + +/* Define if you have the clock function. */ +#define HAVE_CLOCK 1 + +/* Define if you have the dup2 function. */ +#define HAVE_DUP2 1 + +/* Define if you have the execv function. */ +#define HAVE_EXECV 1 + +/* Define if you have the spawnv function. */ +#define HAVE_SPAWNV 1 + +/* Define if you have the flock function. */ +#define HAVE_FLOCK 1 + +/* Define if you have the fork function. */ +#define HAVE_FORK 1 + +/* Define if you have the fsync function. */ +#define HAVE_FSYNC 1 + +/* Define if you have the ftime function. */ +#define HAVE_FTIME 1 + +/* Define if you have the ftruncate function. */ +#define HAVE_FTRUNCATE 1 + +/* Define if you have the getcwd function. */ +#define HAVE_GETCWD 1 + +/* Define if you have the getpeername function. */ +#define HAVE_GETPEERNAME 1 + +/* Define if you have the getpgrp function. */ +#define HAVE_GETPGRP 1 + +/* Define if you have the getpid function. */ +#define HAVE_GETPID 1 + +/* Define if you have the getpwent function. */ +#define HAVE_GETPWENT 1 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the getwd function. */ +#define HAVE_GETWD 1 + +/* Define if you have the hypot function. */ +#define HAVE_HYPOT 1 + +/* Define if you have the kill function. */ +#define HAVE_KILL 1 + +/* Define if you have the memmove function. */ +#define HAVE_MEMMOVE 1 + +/* Define if you have the mktime function. */ +#define HAVE_MKTIME 1 + +/* Define if you have the pause function. */ +#define HAVE_PAUSE 1 + +/* Define if you have the putenv function. */ +#define HAVE_PUTENV 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the setgid function. */ +#define HAVE_SETGID 1 + +/* Define if you have the setlocale function. */ +#define HAVE_SETLOCALE 1 + +/* Define if you have the setpgid function. */ +#define HAVE_SETPGID 1 + +/* Define if you have the setuid function. */ +#define HAVE_SETUID 1 + +/* Define if you have the setvbuf function. */ +#define HAVE_SETVBUF 1 + +/* Define if you have the sigaction function. */ +#define HAVE_SIGACTION 1 + +/* Define if you have the strdup function. */ +#define HAVE_STRDUP 1 + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strftime function. */ +#define HAVE_STRFTIME 1 + +/* Define if you have the strptime function. */ +#define HAVE_STRPTIME 1 + +/* Define if you have the tcgetpgrp function. */ +#define HAVE_TCGETPGRP 1 + +/* Define if you have the tcsetpgrp function. */ +#define HAVE_TCSETPGRP 1 + +/* Define if you have the tmpfile function. */ +#define HAVE_TMPFILE 1 + +/* Define if you have the times function. */ +#define HAVE_TIMES 1 + +/* Define if you have the truncate function. */ +#define HAVE_TRUNCATE 1 + +/* Define if you have the uname function. */ +#define HAVE_UNAME 1 + +/* Define if you have the waitpid function. */ +#define HAVE_WAITPID 1 + +/* Define if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define if you have the header file. */ +#define HAVE_NCURSES_H 1 + +/* Define if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_TIMES_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_UN_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_UTSNAME_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* EMX has an snprintf(). */ +#define HAVE_SNPRINTF 1 + +#endif /* !Py_CONFIG_H */ + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/python23.def b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/python23.def new file mode 100644 index 00000000..c2050d37 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/python23.def @@ -0,0 +1,1161 @@ +LIBRARY python23 INITINSTANCE TERMINSTANCE +DESCRIPTION "Python 2.3 Core DLL" +PROTMODE +DATA MULTIPLE NONSHARED +EXPORTS + +; From python23_s.lib(config) + "_PyImport_Inittab" + +; From python23_s.lib(dlfcn) +; "dlopen" +; "dlsym" +; "dlclose" +; "dlerror" + +; From python23_s.lib(getpathp) + "Py_GetProgramFullPath" + "Py_GetPrefix" + "Py_GetExecPrefix" + "Py_GetPath" + +; From python23_s.lib(getbuildinfo) + "Py_GetBuildInfo" + +; From python23_s.lib(main) + "Py_Main" + "Py_GetArgcArgv" + +; From python23_s.lib(acceler) + "PyGrammar_AddAccelerators" + "PyGrammar_RemoveAccelerators" + +; From python23_s.lib(grammar1) + "PyGrammar_FindDFA" + "PyGrammar_LabelRepr" + +; From python23_s.lib(listnode) + "PyNode_ListTree" + +; From python23_s.lib(node) + "PyNode_AddChild" + "PyNode_New" + "PyNode_Free" + +; From python23_s.lib(parser) + "PyParser_AddToken" + "PyParser_New" + "PyParser_Delete" + +; From python23_s.lib(parsetok) + "Py_TabcheckFlag" + "PyParser_ParseString" + "PyParser_ParseStringFlags" + "PyParser_ParseFile" + "PyParser_ParseFileFlags" + "PyParser_ParseStringFlagsFilename" + +; From python23_s.lib(bitset) + "_Py_newbitset" + "_Py_delbitset" + "_Py_addbit" + "_Py_samebitset" + "_Py_mergebitset" + +; From python23_s.lib(metagrammar) + "_Py_meta_grammar" + "Py_meta_grammar" + +; From python23_s.lib(tokenizer) + "PyToken_OneChar" + "PyToken_TwoChars" + "PyToken_ThreeChars" + "PyTokenizer_FromString" + "PyTokenizer_FromFile" + "PyTokenizer_Free" + "PyTokenizer_Get" + "_PyParser_TokenNames" + +; From python23_s.lib(myreadline) + "PyOS_ReadlineFunctionPointer" + "PyOS_StdioReadline" + "PyOS_Readline" + "PyOS_InputHook" + +; From python23_s.lib(abstract) + "PyObject_GetItem" + "PySequence_GetItem" + "PyObject_SetItem" + "PySequence_SetItem" + "PyObject_DelItem" + "PySequence_DelItem" + "PyNumber_Multiply" + "PyNumber_InPlaceAdd" + "PyNumber_InPlaceMultiply" + "PyNumber_Int" + "PyNumber_Long" + "PyNumber_Float" + "PySequence_GetSlice" + "PySequence_SetSlice" + "PySequence_Tuple" + "PyObject_GetIter" + "PyIter_Next" + "PySequence_List" + "_PySequence_IterSearch" + "PyObject_CallFunction" + "PyObject_CallMethod" + "PyObject_CallMethodObjArgs" + "PyObject_CallFunctionObjArgs" + "PyObject_IsInstance" + "PyObject_IsSubclass" + "PyObject_Cmp" + "PyObject_Call" + "PyObject_CallObject" + "PyObject_Type" + "PyObject_Size" + "PyMapping_Size" + "PyObject_Length" + "PyObject_DelItemString" + "PyObject_AsCharBuffer" + "PyObject_CheckReadBuffer" + "PyObject_AsReadBuffer" + "PyObject_AsWriteBuffer" + "PyNumber_Check" + "PyNumber_Add" + "PyNumber_Subtract" + "PyNumber_Divide" + "PyNumber_FloorDivide" + "PyNumber_TrueDivide" + "PyNumber_Remainder" + "PyNumber_Divmod" + "PyNumber_Power" + "PyNumber_Negative" + "PyNumber_Positive" + "PyNumber_Absolute" + "PyNumber_Invert" + "PyNumber_Lshift" + "PyNumber_Rshift" + "PyNumber_And" + "PyNumber_Xor" + "PyNumber_Or" + "PyNumber_InPlaceSubtract" + "PyNumber_InPlaceDivide" + "PyNumber_InPlaceFloorDivide" + "PyNumber_InPlaceTrueDivide" + "PyNumber_InPlaceRemainder" + "PyNumber_InPlacePower" + "PyNumber_InPlaceLshift" + "PyNumber_InPlaceRshift" + "PyNumber_InPlaceAnd" + "PyNumber_InPlaceXor" + "PyNumber_InPlaceOr" + "PySequence_Check" + "PySequence_Size" + "PySequence_Length" + "PySequence_Concat" + "PySequence_Repeat" + "PySequence_DelSlice" + "PySequence_Fast" + "PySequence_Count" + "PySequence_Contains" + "PySequence_In" + "PySequence_Index" + "PySequence_InPlaceConcat" + "PySequence_InPlaceRepeat" + "PyMapping_Check" + "PyMapping_Length" + "PyMapping_HasKeyString" + "PyMapping_HasKey" + "PyMapping_GetItemString" + "PyMapping_SetItemString" + +; From python23_s.lib(boolobject) + "PyBool_FromLong" + "PyBool_Type" + "_Py_ZeroStruct" + "_Py_TrueStruct" + +; From python23_s.lib(bufferobject) + "PyBuffer_FromObject" + "PyBuffer_FromReadWriteObject" + "PyBuffer_FromMemory" + "PyBuffer_FromReadWriteMemory" + "PyBuffer_New" + "PyBuffer_Type" + +; From python23_s.lib(cellobject) + "PyCell_New" + "PyCell_Get" + "PyCell_Set" + "PyCell_Type" + +; From python23_s.lib(classobject) + "PyClass_New" + "PyClass_IsSubclass" + "PyInstance_New" + "PyInstance_NewRaw" + "PyMethod_New" + "PyMethod_Function" + "PyMethod_Self" + "PyMethod_Class" + "_PyInstance_Lookup" + "PyMethod_Fini" + "PyClass_Type" + "PyInstance_Type" + "PyMethod_Type" + +; From python23_s.lib(cobject) + "PyCObject_FromVoidPtr" + "PyCObject_FromVoidPtrAndDesc" + "PyCObject_AsVoidPtr" + "PyCObject_GetDesc" + "PyCObject_Import" + "PyCObject_Type" + +; From python23_s.lib(complexobject) + "_Py_c_pow" + "_Py_c_sum" + "_Py_c_diff" + "_Py_c_neg" + "_Py_c_prod" + "_Py_c_quot" + "PyComplex_FromCComplex" + "PyComplex_FromDoubles" + "PyComplex_RealAsDouble" + "PyComplex_ImagAsDouble" + "PyComplex_AsCComplex" + "PyComplex_Type" + +; From python23_s.lib(descrobject) + "PyWrapper_New" + "PyDescr_NewMethod" + "PyDescr_NewClassMethod" + "PyDescr_NewMember" + "PyDescr_NewGetSet" + "PyDescr_NewWrapper" + "PyDictProxy_New" + "PyWrapperDescr_Type" + "PyProperty_Type" + +; From python23_s.lib(dictobject) + "PyDict_SetItem" + "PyDict_DelItem" + "PyDict_Clear" + "PyDict_MergeFromSeq2" + "PyDict_Merge" + "PyDict_Copy" + "PyDict_Keys" + "PyDict_Values" + "PyDict_New" + "PyDict_GetItem" + "PyDict_Next" + "PyDict_Items" + "PyDict_Size" + "PyDict_Update" + "PyDict_GetItemString" + "PyDict_SetItemString" + "PyDict_DelItemString" + "PyDict_Type" + "PyDictIter_Type" + +; From python23_s.lib(enumobject) + "PyEnum_Type" + +; From python23_s.lib(fileobject) + "Py_UniversalNewlineFread" + "PyFile_GetLine" + "PyFile_SoftSpace" + "PyFile_WriteObject" + "PyFile_WriteString" + "PyObject_AsFileDescriptor" + "Py_UniversalNewlineFgets" + "PyFile_FromString" + "PyFile_SetBufSize" + "PyFile_SetEncoding" + "PyFile_FromFile" + "PyFile_AsFile" + "PyFile_Name" + "PyFile_Type" + +; From python23_s.lib(floatobject) + "PyFloat_FromString" + "PyFloat_AsDouble" + "PyFloat_Fini" + "_PyFloat_Pack4" + "_PyFloat_Pack8" + "PyFloat_FromDouble" + "PyFloat_AsReprString" + "PyFloat_AsString" + "_PyFloat_Unpack4" + "_PyFloat_Unpack8" + "PyFloat_AsStringEx" + "PyFloat_Type" + +; From python23_s.lib(frameobject) + "PyFrame_New" + "PyFrame_FastToLocals" + "PyFrame_LocalsToFast" + "_PyFrame_Init" + "PyFrame_Fini" + "PyFrame_BlockSetup" + "PyFrame_BlockPop" + "PyFrame_Type" + +; From python23_s.lib(funcobject) + "PyFunction_New" + "PyFunction_GetCode" + "PyFunction_GetGlobals" + "PyFunction_GetModule" + "PyFunction_GetDefaults" + "PyFunction_SetDefaults" + "PyFunction_GetClosure" + "PyFunction_SetClosure" + "PyClassMethod_New" + "PyStaticMethod_New" + "PyFunction_Type" + "PyClassMethod_Type" + "PyStaticMethod_Type" + +; From python23_s.lib(intobject) + "PyInt_AsLong" + "PyInt_AsUnsignedLongMask" + "PyInt_AsUnsignedLongLongMask" + "PyInt_FromString" + "PyInt_Fini" + "PyInt_FromUnicode" + "PyInt_FromLong" + "PyInt_GetMax" + "_PyInt_Init" + "PyInt_Type" + +; From python23_s.lib(iterobject) + "PySeqIter_New" + "PyCallIter_New" + "PySeqIter_Type" + "PyCallIter_Type" + +; From python23_s.lib(listobject) + "PyList_New" + "PyList_Insert" + "PyList_Append" + "PyList_Size" + "PyList_GetItem" + "PyList_SetItem" + "PyList_GetSlice" + "PyList_SetSlice" + "PyList_Sort" + "PyList_Reverse" + "PyList_AsTuple" + "PyList_Type" + "PyListIter_Type" + +; From python23_s.lib(longobject) + "PyLong_FromDouble" + "PyLong_AsLong" + "_PyLong_FromByteArray" + "_PyLong_AsByteArray" + "PyLong_AsDouble" + "PyLong_FromString" + "PyLong_FromLong" + "PyLong_FromUnsignedLong" + "PyLong_AsUnsignedLong" + "PyLong_AsUnsignedLongMask" + "_PyLong_AsScaledDouble" + "PyLong_FromVoidPtr" + "PyLong_AsVoidPtr" + "PyLong_FromLongLong" + "PyLong_FromUnsignedLongLong" + "PyLong_AsLongLong" + "PyLong_AsUnsignedLongLong" + "PyLong_AsUnsignedLongLongMask" + "PyLong_FromUnicode" + "_PyLong_Sign" + "_PyLong_NumBits" + "_PyLong_New" + "_PyLong_Copy" + "PyLong_Type" + +; From python23_s.lib(methodobject) + "PyCFunction_Call" + "Py_FindMethodInChain" + "PyCFunction_GetFunction" + "PyCFunction_GetSelf" + "PyCFunction_GetFlags" + "Py_FindMethod" + "PyCFunction_NewEx" + "PyCFunction_Fini" + "PyCFunction_New" + "PyCFunction_Type" + +; From python23_s.lib(moduleobject) + "PyModule_New" + "_PyModule_Clear" + "PyModule_GetDict" + "PyModule_GetName" + "PyModule_GetFilename" + "PyModule_Type" + +; From python23_s.lib(object) + "Py_DivisionWarningFlag" + "PyObject_Str" + "PyObject_Repr" + "PyObject_Unicode" + "PyObject_GetAttr" + "PyObject_IsTrue" + "PyNumber_CoerceEx" + "PyObject_Compare" + "PyObject_RichCompare" + "_Py_HashDouble" + "PyObject_Hash" + "PyObject_SetAttr" + "PyObject_GenericGetAttr" + "PyObject_GenericSetAttr" + "PyCallable_Check" + "PyObject_Dir" + "PyMem_Malloc" + "PyMem_Realloc" + "PyMem_Free" + "PyObject_Print" + "_PyObject_Dump" + "PyObject_RichCompareBool" + "PyObject_GetAttrString" + "PyObject_SetAttrString" + "PyObject_HasAttrString" + "PyObject_HasAttr" + "_PyObject_GetDictPtr" + "PyObject_SelfIter" + "PyObject_Not" + "PyNumber_Coerce" + "Py_ReprEnter" + "Py_ReprLeave" + "_Py_HashPointer" + "_PyTrash_deposit_object" + "_PyTrash_destroy_chain" + "PyObject_Init" + "PyObject_InitVar" + "_PyObject_New" + "_PyObject_NewVar" + "_PyObject_Del" + "_Py_ReadyTypes" + "_Py_NotImplementedStruct" + "_Py_NoneStruct" + "_Py_cobject_hack" + "_Py_abstract_hack" + "_PyTrash_delete_nesting" + "_PyTrash_delete_later" + +; From python23_s.lib(obmalloc) + "PyObject_Malloc" + "PyObject_Realloc" + "PyObject_Free" + +; From python23_s.lib(rangeobject) + "PyRange_New" + "PyRange_Type" + +; From python23_s.lib(sliceobject) + "PySlice_GetIndices" + "PySlice_GetIndicesEx" + "PySlice_New" + "_Py_EllipsisObject" + "PySlice_Type" + +; From python23_s.lib(stringobject) + "PyString_FromStringAndSize" + "PyString_InternInPlace" + "PyString_FromString" + "PyString_FromFormatV" + "PyString_AsString" + "_PyString_Resize" + "PyString_FromFormat" + "PyString_AsDecodedString" + "PyString_AsEncodedString" + "PyString_DecodeEscape" + "PyString_Size" + "PyString_Repr" + "PyString_AsStringAndSize" + "_PyString_FormatLong" + "PyString_Format" + "_Py_ReleaseInternedStrings" + "PyString_Concat" + "PyString_ConcatAndDel" + "_PyString_Eq" + "PyString_InternImmortal" + "PyString_InternFromString" + "_PyString_Join" + "PyString_Decode" + "PyString_Encode" + "PyString_AsEncodedObject" + "PyString_AsDecodedObject" + "PyString_Fini" + "PyString_Type" + "PyBaseString_Type" + +; From python23_s.lib(structseq) + "PyStructSequence_InitType" + "PyStructSequence_New" + "PyStructSequence_UnnamedField" + +; From python23_s.lib(tupleobject) + "PyTuple_New" + "_PyTuple_Resize" + "PyTuple_Size" + "PyTuple_GetItem" + "PyTuple_SetItem" + "PyTuple_GetSlice" + "PyTuple_Fini" + "PyTuple_Type" + "PyTupleIter_Type" + +; From python23_s.lib(typeobject) + "PyType_IsSubtype" + "_PyType_Lookup" + "PyType_Ready" + "PyType_GenericAlloc" + "_PyObject_SlotCompare" + "PyType_GenericNew" + "PyType_Type" + "PyBaseObject_Type" + "PySuper_Type" + +; From python23_s.lib(unicodeobject) + "PyUnicodeUCS2_Resize" + "PyUnicodeUCS2_FromOrdinal" + "PyUnicodeUCS2_FromObject" + "PyUnicodeUCS2_FromEncodedObject" + "PyUnicodeUCS2_Decode" + "PyUnicodeUCS2_GetDefaultEncoding" + "PyUnicodeUCS2_DecodeUTF8" + "PyUnicodeUCS2_DecodeLatin1" + "PyUnicodeUCS2_DecodeASCII" + "PyUnicodeUCS2_AsEncodedString" + "PyUnicodeUCS2_AsUTF8String" + "PyUnicodeUCS2_AsLatin1String" + "PyUnicodeUCS2_AsASCIIString" + "PyUnicode_DecodeUTF7" + "PyUnicode_EncodeUTF7" + "PyUnicodeUCS2_EncodeUTF8" + "PyUnicodeUCS2_DecodeUTF16" + "PyUnicodeUCS2_AsUTF16String" + "PyUnicodeUCS2_DecodeUnicodeEscape" + "PyUnicodeUCS2_DecodeRawUnicodeEscape" + "PyUnicodeUCS2_EncodeRawUnicodeEscape" + "PyUnicodeUCS2_DecodeCharmap" + "PyUnicodeUCS2_EncodeCharmap" + "PyUnicodeUCS2_TranslateCharmap" + "PyUnicodeUCS2_EncodeDecimal" + "PyUnicodeUCS2_Count" + "PyUnicodeUCS2_Find" + "PyUnicodeUCS2_Tailmatch" + "PyUnicodeUCS2_Join" + "PyUnicodeUCS2_Splitlines" + "PyUnicodeUCS2_Compare" + "PyUnicodeUCS2_Contains" + "PyUnicodeUCS2_Concat" + "_PyUnicode_XStrip" + "PyUnicodeUCS2_Replace" + "PyUnicodeUCS2_Split" + "PyUnicodeUCS2_Format" + "_PyUnicodeUCS2_Fini" + "PyUnicodeUCS2_FromUnicode" + "PyUnicodeUCS2_AsUnicode" + "PyUnicodeUCS2_GetSize" + "PyUnicodeUCS2_GetMax" + "_PyUnicodeUCS2_AsDefaultEncodedString" + "PyUnicodeUCS2_SetDefaultEncoding" + "PyUnicodeUCS2_Encode" + "PyUnicodeUCS2_EncodeUTF16" + "PyUnicodeUCS2_AsUnicodeEscapeString" + "PyUnicodeUCS2_EncodeUnicodeEscape" + "PyUnicodeUCS2_AsRawUnicodeEscapeString" + "PyUnicodeUCS2_EncodeLatin1" + "PyUnicodeUCS2_EncodeASCII" + "PyUnicodeUCS2_AsCharmapString" + "PyUnicodeUCS2_Translate" + "_PyUnicodeUCS2_Init" + "PyUnicode_Type" + +; From python23_s.lib(unicodectype) + "_PyUnicode_TypeRecords" + "_PyUnicodeUCS2_ToNumeric" + "_PyUnicodeUCS2_IsLowercase" + "_PyUnicodeUCS2_IsUppercase" + "_PyUnicodeUCS2_IsTitlecase" + "_PyUnicodeUCS2_IsWhitespace" + "_PyUnicodeUCS2_IsLinebreak" + "_PyUnicodeUCS2_ToLowercase" + "_PyUnicodeUCS2_ToUppercase" + "_PyUnicodeUCS2_ToTitlecase" + "_PyUnicodeUCS2_ToDecimalDigit" + "_PyUnicodeUCS2_ToDigit" + "_PyUnicodeUCS2_IsDecimalDigit" + "_PyUnicodeUCS2_IsDigit" + "_PyUnicodeUCS2_IsNumeric" + "_PyUnicodeUCS2_IsAlpha" + +; From python23_s.lib(weakrefobject) + "PyWeakref_NewRef" + "PyWeakref_NewProxy" + "PyObject_ClearWeakRefs" + "PyWeakref_GetObject" + "_PyWeakref_GetWeakrefCount" + "_PyWeakref_RefType" + "_PyWeakref_ProxyType" + "_PyWeakref_CallableProxyType" + +; From python23_s.lib(bltinmodule) + "_PyBuiltin_Init" + "Py_FileSystemDefaultEncoding" + +; From python23_s.lib(exceptions) + "PyExc_Exception" + "PyExc_TypeError" + "PyExc_StopIteration" + "PyExc_StandardError" + "PyExc_SystemExit" + "PyExc_KeyboardInterrupt" + "PyExc_ImportError" + "PyExc_EnvironmentError" + "PyExc_IOError" + "PyExc_OSError" + "PyExc_EOFError" + "PyExc_RuntimeError" + "PyExc_NotImplementedError" + "PyExc_NameError" + "PyExc_UnboundLocalError" + "PyExc_AttributeError" + "PyExc_SyntaxError" + "PyExc_IndentationError" + "PyExc_TabError" + "PyExc_AssertionError" + "PyExc_LookupError" + "PyExc_IndexError" + "PyExc_KeyError" + "PyExc_ArithmeticError" + "PyExc_OverflowError" + "PyExc_ZeroDivisionError" + "PyExc_FloatingPointError" + "PyExc_ValueError" + "PyExc_UnicodeError" + "PyExc_UnicodeEncodeError" + "PyExc_UnicodeDecodeError" + "PyExc_UnicodeTranslateError" + "PyExc_ReferenceError" + "PyExc_SystemError" + "PyExc_MemoryError" + "PyExc_Warning" + "PyExc_UserWarning" + "PyExc_DeprecationWarning" + "PyExc_PendingDeprecationWarning" + "PyExc_SyntaxWarning" + "PyExc_OverflowWarning" + "PyExc_RuntimeWarning" + "PyExc_FutureWarning" + "PyExc_MemoryErrorInst" + "PyUnicodeEncodeError_GetStart" + "PyUnicodeDecodeError_GetStart" + "PyUnicodeEncodeError_GetEnd" + "PyUnicodeDecodeError_GetEnd" + "_PyExc_Init" + "_PyExc_Fini" + "PyUnicodeDecodeError_Create" + "PyUnicodeEncodeError_Create" + "PyUnicodeTranslateError_Create" + "PyUnicodeEncodeError_GetEncoding" + "PyUnicodeDecodeError_GetEncoding" + "PyUnicodeEncodeError_GetObject" + "PyUnicodeDecodeError_GetObject" + "PyUnicodeTranslateError_GetObject" + "PyUnicodeTranslateError_GetStart" + "PyUnicodeEncodeError_SetStart" + "PyUnicodeDecodeError_SetStart" + "PyUnicodeTranslateError_SetStart" + "PyUnicodeTranslateError_GetEnd" + "PyUnicodeEncodeError_SetEnd" + "PyUnicodeDecodeError_SetEnd" + "PyUnicodeTranslateError_SetEnd" + "PyUnicodeEncodeError_GetReason" + "PyUnicodeDecodeError_GetReason" + "PyUnicodeTranslateError_GetReason" + "PyUnicodeEncodeError_SetReason" + "PyUnicodeDecodeError_SetReason" + "PyUnicodeTranslateError_SetReason" + +; From python23_s.lib(ceval) + "PyEval_CallObjectWithKeywords" + "PyEval_EvalCodeEx" + "_PyEval_SliceIndex" + "PyEval_GetFrame" + "PyEval_CallObject" + "PyEval_SetProfile" + "PyEval_SetTrace" + "PyEval_GetBuiltins" + "PyEval_GetGlobals" + "PyEval_GetLocals" + "PyEval_GetRestricted" + "PyEval_MergeCompilerFlags" + "Py_FlushLine" + "Py_AddPendingCall" + "Py_MakePendingCalls" + "Py_SetRecursionLimit" + "Py_GetRecursionLimit" + "PyEval_GetFuncName" + "PyEval_GetFuncDesc" + "PyEval_GetCallStats" + "PyEval_SaveThread" + "PyEval_RestoreThread" + "PyEval_InitThreads" + "PyEval_AcquireLock" + "PyEval_ReleaseLock" + "PyEval_AcquireThread" + "PyEval_ReleaseThread" + "PyEval_ReInitThreads" + "PyEval_EvalCode" + "_PyEval_CallTracing" + "_Py_CheckInterval" + "_Py_Ticker" + +; From python23_s.lib(compile) + "PyCode_New" + "PySymtable_Free" + "_Py_Mangle" + "PyNode_Compile" + "PyNode_CompileFlags" + "PyCode_Addr2Line" + "PyNode_CompileSymtable" + "Py_OptimizeFlag" + "PyCode_Type" + +; From python23_s.lib(codecs) + "_PyCodec_Lookup" + "PyCodec_StreamReader" + "PyCodec_StreamWriter" + "PyCodec_Encode" + "PyCodec_Decode" + "PyCodec_IgnoreErrors" + "PyCodec_ReplaceErrors" + "PyCodec_XMLCharRefReplaceErrors" + "PyCodec_BackslashReplaceErrors" + "PyCodec_Register" + "PyCodec_Encoder" + "PyCodec_Decoder" + "PyCodec_RegisterError" + "PyCodec_LookupError" + "PyCodec_StrictErrors" + +; From python23_s.lib(errors) + "PyErr_SetNone" + "PyErr_SetString" + "PyErr_ExceptionMatches" + "PyErr_GivenExceptionMatches" + "PyErr_NormalizeException" + "PyErr_Fetch" + "PyErr_Clear" + "PyErr_NoMemory" + "PyErr_SetFromErrnoWithFilenameObject" + "PyErr_Format" + "PyErr_NewException" + "PyErr_WriteUnraisable" + "PyErr_SyntaxLocation" + "PyErr_ProgramText" + "PyErr_SetObject" + "PyErr_Occurred" + "PyErr_Restore" + "PyErr_BadArgument" + "PyErr_SetFromErrno" + "PyErr_SetFromErrnoWithFilename" + "PyErr_BadInternalCall" + "_PyErr_BadInternalCall" + "PyErr_Warn" + "PyErr_WarnExplicit" + +; From python23_s.lib(frozen) + "PyImport_FrozenModules" + +; From python23_s.lib(frozenmain) + "Py_FrozenMain" + +; From python23_s.lib(future) + "PyNode_Future" + +; From python23_s.lib(getargs) + "PyArg_Parse" + "PyArg_ParseTuple" + "PyArg_ParseTupleAndKeywords" + "PyArg_UnpackTuple" + "PyArg_VaParse" + +; From python23_s.lib(getcompiler) + "Py_GetCompiler" + +; From python23_s.lib(getcopyright) + "Py_GetCopyright" + +; From python23_s.lib(getmtime) + "PyOS_GetLastModificationTime" + +; From python23_s.lib(getplatform) + "Py_GetPlatform" + +; From python23_s.lib(getversion) + "Py_GetVersion" + +; From python23_s.lib(graminit) + "_PyParser_Grammar" + +; From python23_s.lib(import) + "_PyImport_Init" + "_PyImportHooks_Init" + "PyImport_ImportModule" + "PyImport_Cleanup" + "_PyImport_FixupExtension" + "PyImport_AddModule" + "PyImport_ExecCodeModuleEx" + "PyImport_ImportFrozenModule" + "PyImport_ImportModuleEx" + "PyImport_ReloadModule" + "PyImport_Import" +; "initimp" + "_PyImport_Fini" + "PyImport_GetMagicNumber" + "PyImport_ExecCodeModule" + "PyImport_GetModuleDict" + "_PyImport_FindExtension" + "PyImport_AppendInittab" + "PyImport_ExtendInittab" + "PyImport_Inittab" + "_PyImport_Filetab" + +; From python23_s.lib(importdl) + "_PyImport_LoadDynamicModule" + +; From python23_s.lib(marshal) + "PyMarshal_ReadLongFromFile" + "PyMarshal_ReadLastObjectFromFile" + "PyMarshal_ReadObjectFromString" + "PyMarshal_ReadObjectFromFile" + "PyMarshal_WriteObjectToString" + "PyMarshal_WriteLongToFile" + "PyMarshal_WriteObjectToFile" + "PyMarshal_ReadShortFromFile" + "PyMarshal_Init" + +; From python23_s.lib(modsupport) + "Py_InitModule4" + "Py_BuildValue" + "Py_VaBuildValue" + "PyEval_CallFunction" + "PyEval_CallMethod" + "PyModule_AddObject" + "PyModule_AddIntConstant" + "PyModule_AddStringConstant" + "_Py_PackageContext" + +; From python23_s.lib(mysnprintf) + "PyOS_snprintf" + "PyOS_vsnprintf" + +; From python23_s.lib(mystrtoul) + "PyOS_strtoul" + "PyOS_strtol" + +; From python23_s.lib(pyfpe) + "PyFPE_dummy" + +; From python23_s.lib(pystate) + "PyInterpreterState_Clear" + "PyThreadState_Clear" + "PyGILState_Ensure" + "PyGILState_Release" + "PyInterpreterState_New" + "PyInterpreterState_Delete" + "PyThreadState_Delete" + "PyThreadState_New" + "PyThreadState_DeleteCurrent" + "PyThreadState_Get" + "PyThreadState_Swap" + "PyThreadState_GetDict" + "PyThreadState_SetAsyncExc" + "PyGILState_GetThisThreadState" + "PyInterpreterState_Head" + "PyInterpreterState_Next" + "PyInterpreterState_ThreadHead" + "PyThreadState_Next" + "_PyGILState_Init" + "_PyGILState_Fini" + "_PyThreadState_Current" + "_PyThreadState_GetFrame" + +; From python23_s.lib(pythonrun) + "Py_IgnoreEnvironmentFlag" + "Py_DebugFlag" + "Py_VerboseFlag" + "Py_NoSiteFlag" + "Py_InteractiveFlag" + "Py_FrozenFlag" + "Py_Initialize" + "Py_FatalError" + "Py_NewInterpreter" + "PyErr_Print" + "PyRun_InteractiveOneFlags" + "PyRun_SimpleFileExFlags" + "PyRun_FileExFlags" + "Py_Exit" + "PyErr_PrintEx" + "PyErr_Display" + "Py_SetProgramName" + "Py_GetProgramName" + "Py_SetPythonHome" + "Py_GetPythonHome" + "Py_Finalize" + "Py_IsInitialized" + "Py_EndInterpreter" + "PyRun_AnyFile" + "PyRun_AnyFileExFlags" + "PyRun_AnyFileEx" + "PyRun_AnyFileFlags" + "Py_FdIsInteractive" + "PyRun_InteractiveLoopFlags" + "PyRun_SimpleString" + "PyRun_SimpleStringFlags" + "PyRun_StringFlags" + "PyRun_SimpleFile" + "PyRun_SimpleFileEx" + "PyRun_InteractiveOne" + "PyRun_InteractiveLoop" + "PyParser_SimpleParseString" + "PyParser_SimpleParseFile" + "PyParser_SimpleParseStringFlags" + "PyParser_SimpleParseStringFlagsFilename" + "PyParser_SimpleParseFileFlags" + "PyRun_String" + "PyRun_File" + "PyRun_FileEx" + "PyRun_FileFlags" + "Py_CompileString" + "Py_CompileStringFlags" + "Py_SymtableString" + "Py_AtExit" + "PyOS_getsig" + "PyOS_setsig" + "PyParser_SetError" + "PyParser_SimpleParseStringFilename" + "Py_UseClassExceptionsFlag" + "Py_UnicodeFlag" + "_Py_QnewFlag" + "PyModule_WarningsModule" + "_PyThread_Started" + +; From python23_s.lib(structmember) + "PyMember_Get" + "PyMember_GetOne" + "PyMember_SetOne" + "PyMember_Set" + +; From python23_s.lib(symtable) + "PySymtableEntry_New" + "PySymtableEntry_Type" + +; From python23_s.lib(sysmodule) + "_PySys_Init" + "PySys_SetPath" + "PySys_SetArgv" + "PySys_WriteStdout" + "PySys_WriteStderr" + "PySys_GetObject" + "PySys_SetObject" + "PySys_GetFile" + "PySys_ResetWarnOptions" + "PySys_AddWarnOption" + +; From python23_s.lib(traceback) + "PyTraceBack_Here" + "PyTraceBack_Print" + "PyTraceBack_Type" + +; From python23_s.lib(getopt) + "_PyOS_GetOpt" + "_PyOS_opterr" + "_PyOS_optind" + "_PyOS_optarg" + +; From python23_s.lib(dynload_shlib) + "_PyImport_DynLoadFiletab" + "_PyImport_GetDynLoadFunc" + +; From python23_s.lib(thread) + "PyThread_delete_key_value" + "PyThread_init_thread" + "PyThread_start_new_thread" + "PyThread_exit_thread" + "PyThread_get_thread_ident" + "PyThread_allocate_lock" + "PyThread_free_lock" + "PyThread_acquire_lock" + "PyThread_release_lock" + "PyThread_create_key" + "PyThread_delete_key" + "PyThread_set_key_value" + "PyThread_get_key_value" + "PyThread__exit_thread" + +; From python23_s.lib(gcmodule) +; "initgc" + "_PyObject_GC_New" + "_PyObject_GC_NewVar" + "PyGC_Collect" + "_PyObject_GC_Resize" + "_PyObject_GC_Malloc" + "PyObject_GC_Track" + "PyObject_GC_UnTrack" + "PyObject_GC_Del" + "_PyGC_Dump" + "_PyObject_GC_Track" + "_PyObject_GC_UnTrack" + "_PyObject_GC_Del" + "_PyGC_generation0" + +; From python23_s.lib(signalmodule) +; "initsignal" + "PyErr_CheckSignals" + "PyErr_SetInterrupt" + "PyOS_FiniInterrupts" + "PyOS_InterruptOccurred" + "PyOS_InitInterrupts" + "PyOS_AfterFork" + +; From python23_s.lib(posixmodule) +; "initos2" + +; From python23_s.lib(threadmodule) +; "initthread" + +; From python23_s.lib(arraymodule) +; "initarray" +; "array_methods" + +; From python23_s.lib(binascii) +; "initbinascii" + +; From python23_s.lib(cmathmodule) +; "initcmath" + +; From python23_s.lib(_codecsmodule) +; "init_codecs" + +; From python23_s.lib(cPickle) +; "initcPickle" +; "fast_save_leave" + +; From python23_s.lib(cStringIO) +; "initcStringIO" + +; From python23_s.lib(_csv) +; "init_csv" + +; From python23_s.lib(datetimemodule) +; "initdatetime" + +; From python23_s.lib(dlmodule) +; "initdl" + +; From python23_s.lib(errnomodule) +; "initerrno" + +; From python23_s.lib(fcntlmodule) +; "initfcntl" + +; From python23_s.lib(imageop) +; "initimageop" + +; From python23_s.lib(itertoolsmodule) +; "inititertools" + +; From python23_s.lib(_localemodule) +; "init_locale" + +; From python23_s.lib(mathmodule) +; "initmath" + +; From python23_s.lib(md5c) +; "_Py_MD5Final" +; "_Py_MD5Init" +; "_Py_MD5Update" + +; From python23_s.lib(md5module) +; "initmd5" + +; From python23_s.lib(operator) +; "initoperator" + +; From python23_s.lib(pcremodule) +; "initpcre" + +; From python23_s.lib(pypcre) +; "pcre_study" +; "pcre_compile" +; "pcre_exec" +; "pcre_info" +; "pcre_version" +; "pcre_lcc" +; "pcre_fcc" +; "pcre_cbits" +; "pcre_ctypes" +; "pcre_malloc" +; "pcre_free" + +; From python23_s.lib(_randommodule) +; "init_random" + +; From python23_s.lib(regexmodule) +; "initregex" + +; From python23_s.lib(regexpr) +; "_Py_re_syntax_table" +; "_Py_re_compile_initialize" +; "_Py_re_compile_pattern" +; "_Py_re_match" +; "_Py_re_search" +; "_Py_re_set_syntax" +; "_Py_re_compile_fastmap" +; "_Py_re_syntax" + +; From python23_s.lib(rgbimgmodule) +; "initrgbimg" + +; From python23_s.lib(shamodule) +; "initsha" + +; From python23_s.lib(_sre) +; "init_sre" + +; From python23_s.lib(stropmodule) +; "initstrop" + +; From python23_s.lib(structmodule) +; "initstruct" + +; From python23_s.lib(symtablemodule) +; "init_symtable" + +; From python23_s.lib(termios) +; "inittermios" + +; From python23_s.lib(timemodule) +; "inittime" +; "inittimezone" + +; From python23_s.lib(timingmodule) +; "inittiming" + +; From python23_s.lib(_weakref) +; "init_weakref" + +; From python23_s.lib(xreadlinesmodule) +; "initxreadlines" + +; From python23_s.lib(xxsubtype) +; "initxxsubtype" + +; From python23_s.lib(zipimport) +; "initzipimport" diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/pythonpm.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/pythonpm.c new file mode 100644 index 00000000..2d7e7a5e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2emx/pythonpm.c @@ -0,0 +1,124 @@ +/* OS/2 PM main program - creates a hidden window, and starts Python + * interpreter in a separate thread, so that Python scripts can be + * run in PM process space without a console Window. The interpreter + * is incorporated by linking in the Python DLL. + * + * As it stands, I don't think this is adequate for supporting Python + * GUI modules, as the Python thread doesn't have its own message + * queue - which is required of threads that want to create/use + * PM windows. + * + * This code owes a lot to "OS/2 Presentation Manager Programming", by + * Charles Petzold. + * + * Andrew MacIntyre , August 2001. + * Released under the terms of the Python 2.1.1 licence - see the LICENCE + * file in the Python v2.1.1 (or later) source distribution. + * Copyright assigned to the Python Software Foundation, 2001. + */ + +#define INCL_DOS +#define INCL_WIN +#include +#include + +#include "Python.h" + +/* use structure to pass command line to Python thread */ +typedef struct +{ + int argc; + char **argv; + HWND Frame; + int running; +} arglist; + +/* make this a global to simplify access. + * it should only be set from the Python thread, or by the code that + * initiates the Python thread when the thread cannot be created. + */ +int PythonRC; + +extern DL_EXPORT(int) Py_Main(int, char **); +void PythonThread(void *); + +int +main(int argc, char **argv) +{ + ULONG FrameFlags = FCF_TITLEBAR | + FCF_SYSMENU | + FCF_SIZEBORDER | + FCF_HIDEBUTTON | + FCF_SHELLPOSITION | + FCF_TASKLIST; + HAB hab; + HMQ hmq; + HWND Client; + QMSG qmsg; + arglist args; + int python_tid; + + /* init PM and create message queue */ + hab = WinInitialize(0); + hmq = WinCreateMsgQueue(hab, 0); + + /* create a (hidden) Window to house the window procedure */ + args.Frame = WinCreateStdWindow(HWND_DESKTOP, + 0, + &FrameFlags, + NULL, + "PythonPM", + 0L, + 0, + 0, + &Client); + + /* run Python interpreter in a thread */ + args.argc = argc; + args.argv = argv; + args.running = 0; + if (-1 == (python_tid = _beginthread(PythonThread, NULL, 1024 * 1024, &args))) + { + /* couldn't start thread */ + WinAlarm(HWND_DESKTOP, WA_ERROR); + PythonRC = 1; + } + else + { + /* process PM messages, until Python exits */ + while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0)) + WinDispatchMsg(hab, &qmsg); + if (args.running > 0) + DosKillThread(python_tid); + } + + /* destroy window, shutdown message queue and PM */ + WinDestroyWindow(args.Frame); + WinDestroyMsgQueue(hmq); + WinTerminate(hab); + + return PythonRC; +} + +void PythonThread(void *argl) +{ + HAB hab; + arglist *args; + + /* PM initialisation */ + hab = WinInitialize(0); + + /* start Python */ + args = (arglist *)argl; + args->running = 1; + PythonRC = Py_Main(args->argc, args->argv); + + /* enter a critical section and send the termination message */ + DosEnterCritSec(); + args->running = 0; + WinPostMsg(args->Frame, WM_QUIT, NULL, NULL); + + /* shutdown PM and terminate thread */ + WinTerminate(hab); + _endthread(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/_tkinter.def b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/_tkinter.def new file mode 100644 index 00000000..9df56968 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/_tkinter.def @@ -0,0 +1,8 @@ +LIBRARY _TKINTER INITINSTANCE TERMINSTANCE +DESCRIPTION 'Python Extension DLL v1.0 for Access to Tcl/Tk Environment' +PROTMODE +DATA MULTIPLE NONSHARED + +EXPORTS + init_tkinter + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/config.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/config.c new file mode 100644 index 00000000..e4b61b13 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/config.c @@ -0,0 +1,110 @@ +/* -*- C -*- *********************************************** +Copyright (c) 2000, BeOpen.com. +Copyright (c) 1995-2000, Corporation for National Research Initiatives. +Copyright (c) 1990-1995, Stichting Mathematisch Centrum. +All rights reserved. + +See the file "Misc/COPYRIGHT" for information on usage and +redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. +******************************************************************/ + +/* Module configuration */ + +/* This file contains the table of built-in modules. + See init_builtin() in import.c. */ + +#include "Python.h" + +extern void initarray(void); +extern void initaudioop(void); +extern void initbinascii(void); +extern void initcmath(void); +extern void initerrno(void); +extern void initimageop(void); +extern void initmath(void); +extern void initmd5(void); +extern void initnt(void); +extern void initos2(void); +extern void initoperator(void); +extern void initposix(void); +extern void initregex(void); +extern void initrgbimg(void); +extern void initrotor(void); +extern void initsignal(void); +extern void initselect(void); +extern void init_socket(void); +extern void initstrop(void); +extern void initstruct(void); +extern void inittime(void); +extern void initthread(void); +extern void initcStringIO(void); +extern void initcPickle(void); +extern void initpcre(void); +#ifdef WIN32 +extern void initmsvcrt(void); +#endif + +/* -- ADDMODULE MARKER 1 -- */ + +extern void PyMarshal_Init(void); +extern void initimp(void); + +struct _inittab _PyImport_Inittab[] = { + + {"array", initarray}, +#ifdef M_I386 + {"audioop", initaudioop}, +#endif + {"binascii", initbinascii}, + {"cmath", initcmath}, + {"errno", initerrno}, +// {"imageop", initimageop}, + {"math", initmath}, + {"md5", initmd5}, +#if defined(MS_WINDOWS) || defined(__BORLANDC__) || defined(__WATCOMC__) + {"nt", initnt}, /* Use the NT os functions, not posix */ +#else +#if defined(PYOS_OS2) + {"os2", initos2}, +#else + {"posix", initposix}, +#endif +#endif + {"operator", initoperator}, + {"regex", initregex}, +// {"rgbimg", initrgbimg}, +// {"rotor", initrotor}, + {"signal", initsignal}, +#ifdef USE_SOCKET + {"_socket", init_socket}, + {"select", initselect}, +#endif + {"strop", initstrop}, + {"struct", initstruct}, + {"time", inittime}, +#ifdef WITH_THREAD + {"thread", initthread}, +#endif + {"cStringIO", initcStringIO}, + {"cPickle", initcPickle}, + {"pcre", initpcre}, +#ifdef WIN32 + {"msvcrt", initmsvcrt}, +#endif + +/* -- ADDMODULE MARKER 2 -- */ + + /* This module "lives in" with marshal.c */ + {"marshal", PyMarshal_Init}, + + /* This lives it with import.c */ + {"imp", initimp}, + + /* These entries are here for sys.builtin_module_names */ + {"__main__", NULL}, + {"__builtin__", NULL}, + {"sys", NULL}, + + /* Sentinel */ + {0, 0} +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/getpathp.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/getpathp.c new file mode 100644 index 00000000..33cf112c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/getpathp.c @@ -0,0 +1,471 @@ + +/* Return the initial module search path. */ +/* Used by DOS, OS/2, Windows 3.1. Works on NT too. */ + +#include "Python.h" +#include "osdefs.h" + +#ifdef MS_WIN32 +#include +extern BOOL PyWin_IsWin32s(void); +#endif + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ + +/* Search in some common locations for the associated Python libraries. + * + * Two directories must be found, the platform independent directory + * (prefix), containing the common .py and .pyc files, and the platform + * dependent directory (exec_prefix), containing the shared library + * modules. Note that prefix and exec_prefix can be the same directory, + * but for some installations, they are different. + * + * Py_GetPath() tries to return a sensible Python module search path. + * + * First, we look to see if the executable is in a subdirectory of + * the Python build directory. We calculate the full path of the + * directory containing the executable as progpath. We work backwards + * along progpath and look for $dir/Modules/Setup.in, a distinctive + * landmark. If found, we use $dir/Lib as $root. The returned + * Python path is the compiled #define PYTHONPATH with all the initial + * "./lib" replaced by $root. + * + * Otherwise, if there is a PYTHONPATH environment variable, we return that. + * + * Otherwise we try to find $progpath/lib/os.py, and if found, then + * root is $progpath/lib, and we return Python path as compiled PYTHONPATH + * with all "./lib" replaced by $root (as above). + * + */ + +#ifndef LANDMARK +#define LANDMARK "lib\\os.py" +#endif + +static char prefix[MAXPATHLEN+1]; +static char exec_prefix[MAXPATHLEN+1]; +static char progpath[MAXPATHLEN+1]; +static char *module_search_path = NULL; + + +static int +is_sep(char ch) /* determine if "ch" is a separator character */ +{ +#ifdef ALTSEP + return ch == SEP || ch == ALTSEP; +#else + return ch == SEP; +#endif +} + + +static void +reduce(char *dir) +{ + int i = strlen(dir); + while (i > 0 && !is_sep(dir[i])) + --i; + dir[i] = '\0'; +} + + +static int +exists(char *filename) +{ + struct stat buf; + return stat(filename, &buf) == 0; +} + + +static void +join(char *buffer, char *stuff) +{ + int n, k; + if (is_sep(stuff[0])) + n = 0; + else { + n = strlen(buffer); + if (n > 0 && !is_sep(buffer[n-1]) && n < MAXPATHLEN) + buffer[n++] = SEP; + } + k = strlen(stuff); + if (n + k > MAXPATHLEN) + k = MAXPATHLEN - n; + strncpy(buffer+n, stuff, k); + buffer[n+k] = '\0'; +} + + +static int +search_for_prefix(char *argv0_path, char *landmark) +{ + int n; + + /* Search from argv0_path, until root is found */ + strcpy(prefix, argv0_path); + do { + n = strlen(prefix); + join(prefix, landmark); + if (exists(prefix)) { + prefix[n] = '\0'; + return 1; + } + prefix[n] = '\0'; + reduce(prefix); + } while (prefix[0]); + return 0; +} + +#ifdef MS_WIN32 +#include "malloc.h" // for alloca - see comments below! +extern const char *PyWin_DLLVersionString; // a string loaded from the DLL at startup. + + +/* Load a PYTHONPATH value from the registry. + Load from either HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER. + + Returns NULL, or a pointer that should be freed. +*/ + +static char * +getpythonregpath(HKEY keyBase, BOOL bWin32s) +{ + HKEY newKey = 0; + DWORD nameSize = 0; + DWORD dataSize = 0; + DWORD numEntries = 0; + LONG rc; + char *retval = NULL; + char *dataBuf; + const char keyPrefix[] = "Software\\Python\\PythonCore\\"; + const char keySuffix[] = "\\PythonPath"; + int versionLen; + char *keyBuf; + + // Tried to use sysget("winver") but here is too early :-( + versionLen = strlen(PyWin_DLLVersionString); + // alloca == no free required, but memory only local to fn. + // also no heap fragmentation! Am I being silly? + keyBuf = alloca(sizeof(keyPrefix)-1 + versionLen + sizeof(keySuffix)); // chars only, plus 1 NULL. + // lots of constants here for the compiler to optimize away :-) + memcpy(keyBuf, keyPrefix, sizeof(keyPrefix)-1); + memcpy(keyBuf+sizeof(keyPrefix)-1, PyWin_DLLVersionString, versionLen); + memcpy(keyBuf+sizeof(keyPrefix)-1+versionLen, keySuffix, sizeof(keySuffix)); // NULL comes with this one! + + rc=RegOpenKey(keyBase, + keyBuf, + &newKey); + if (rc==ERROR_SUCCESS) { + RegQueryInfoKey(newKey, NULL, NULL, NULL, NULL, NULL, NULL, + &numEntries, &nameSize, &dataSize, NULL, NULL); + } + if (bWin32s && numEntries==0 && dataSize==0) { + /* must hardcode for Win32s */ + numEntries = 1; + dataSize = 511; + } + if (numEntries) { + /* Loop over all subkeys. */ + /* Win32s doesnt know how many subkeys, so we do + it twice */ + char keyBuf[MAX_PATH+1]; + int index = 0; + int off = 0; + for(index=0;;index++) { + long reqdSize = 0; + DWORD rc = RegEnumKey(newKey, + index, keyBuf, MAX_PATH+1); + if (rc) break; + rc = RegQueryValue(newKey, keyBuf, NULL, &reqdSize); + if (rc) break; + if (bWin32s && reqdSize==0) reqdSize = 512; + dataSize += reqdSize + 1; /* 1 for the ";" */ + } + dataBuf = malloc(dataSize+1); + if (dataBuf==NULL) + return NULL; /* pretty serious? Raise error? */ + /* Now loop over, grabbing the paths. + Subkeys before main library */ + for(index=0;;index++) { + int adjust; + long reqdSize = dataSize; + DWORD rc = RegEnumKey(newKey, + index, keyBuf,MAX_PATH+1); + if (rc) break; + rc = RegQueryValue(newKey, + keyBuf, dataBuf+off, &reqdSize); + if (rc) break; + if (reqdSize>1) { + /* If Nothing, or only '\0' copied. */ + adjust = strlen(dataBuf+off); + dataSize -= adjust; + off += adjust; + dataBuf[off++] = ';'; + dataBuf[off] = '\0'; + dataSize--; + } + } + /* Additionally, win32s doesnt work as expected, so + the specific strlen() is required for 3.1. */ + rc = RegQueryValue(newKey, "", dataBuf+off, &dataSize); + if (rc==ERROR_SUCCESS) { + if (strlen(dataBuf)==0) + free(dataBuf); + else + retval = dataBuf; /* caller will free */ + } + else + free(dataBuf); + } + + if (newKey) + RegCloseKey(newKey); + return retval; +} +#endif /* MS_WIN32 */ + +static void +get_progpath(void) +{ + extern char *Py_GetProgramName(void); + char *path = getenv("PATH"); + char *prog = Py_GetProgramName(); + +#ifdef MS_WIN32 + if (GetModuleFileName(NULL, progpath, MAXPATHLEN)) + return; +#endif + if (prog == NULL || *prog == '\0') + prog = "python"; + + /* If there is no slash in the argv0 path, then we have to + * assume python is on the user's $PATH, since there's no + * other way to find a directory to start the search from. If + * $PATH isn't exported, you lose. + */ +#ifdef ALTSEP + if (strchr(prog, SEP) || strchr(prog, ALTSEP)) +#else + if (strchr(prog, SEP)) +#endif + strcpy(progpath, prog); + else if (path) { + while (1) { + char *delim = strchr(path, DELIM); + + if (delim) { + int len = delim - path; + strncpy(progpath, path, len); + *(progpath + len) = '\0'; + } + else + strcpy(progpath, path); + + join(progpath, prog); + if (exists(progpath)) + break; + + if (!delim) { + progpath[0] = '\0'; + break; + } + path = delim + 1; + } + } + else + progpath[0] = '\0'; +} + +static void +calculate_path(void) +{ + char argv0_path[MAXPATHLEN+1]; + char *buf; + int bufsz; + char *pythonhome = Py_GetPythonHome(); + char *envpath = Py_GETENV("PYTHONPATH"); +#ifdef MS_WIN32 + char *machinepath, *userpath; + + /* Are we running under Windows 3.1(1) Win32s? */ + if (PyWin_IsWin32s()) { + /* Only CLASSES_ROOT is supported */ + machinepath = getpythonregpath(HKEY_CLASSES_ROOT, TRUE); + userpath = NULL; + } else { + machinepath = getpythonregpath(HKEY_LOCAL_MACHINE, FALSE); + userpath = getpythonregpath(HKEY_CURRENT_USER, FALSE); + } +#endif + + get_progpath(); + strcpy(argv0_path, progpath); + reduce(argv0_path); + if (pythonhome == NULL || *pythonhome == '\0') { + if (search_for_prefix(argv0_path, LANDMARK)) + pythonhome = prefix; + else + pythonhome = NULL; + } + else { + char *delim; + + strcpy(prefix, pythonhome); + + /* Extract Any Optional Trailing EXEC_PREFIX */ + /* e.g. PYTHONHOME=: */ + delim = strchr(prefix, DELIM); + if (delim) { + *delim = '\0'; + strcpy(exec_prefix, delim+1); + } else + strcpy(exec_prefix, EXEC_PREFIX); + } + + if (envpath && *envpath == '\0') + envpath = NULL; + + /* We need to construct a path from the following parts: + (1) the PYTHONPATH environment variable, if set; + (2) for Win32, the machinepath and userpath, if set; + (3) the PYTHONPATH config macro, with the leading "." + of each component replaced with pythonhome, if set; + (4) the directory containing the executable (argv0_path). + The length calculation calculates #3 first. + */ + + /* Calculate size of return buffer */ + if (pythonhome != NULL) { + char *p; + bufsz = 1; + for (p = PYTHONPATH; *p; p++) { + if (*p == DELIM) + bufsz++; /* number of DELIM plus one */ + } + bufsz *= strlen(pythonhome); + } + else + bufsz = 0; + bufsz += strlen(PYTHONPATH) + 1; + if (envpath != NULL) + bufsz += strlen(envpath) + 1; + bufsz += strlen(argv0_path) + 1; +#ifdef MS_WIN32 + if (machinepath) + bufsz += strlen(machinepath) + 1; + if (userpath) + bufsz += strlen(userpath) + 1; +#endif + + module_search_path = buf = malloc(bufsz); + if (buf == NULL) { + /* We can't exit, so print a warning and limp along */ + fprintf(stderr, "Can't malloc dynamic PYTHONPATH.\n"); + if (envpath) { + fprintf(stderr, "Using default static $PYTHONPATH.\n"); + module_search_path = envpath; + } + else { + fprintf(stderr, "Using environment $PYTHONPATH.\n"); + module_search_path = PYTHONPATH; + } + return; + } + + if (envpath) { + strcpy(buf, envpath); + buf = strchr(buf, '\0'); + *buf++ = DELIM; + } +#ifdef MS_WIN32 + if (machinepath) { + strcpy(buf, machinepath); + buf = strchr(buf, '\0'); + *buf++ = DELIM; + } + if (userpath) { + strcpy(buf, userpath); + buf = strchr(buf, '\0'); + *buf++ = DELIM; + } +#endif + if (pythonhome == NULL) { + strcpy(buf, PYTHONPATH); + buf = strchr(buf, '\0'); + } + else { + char *p = PYTHONPATH; + char *q; + int n; + for (;;) { + q = strchr(p, DELIM); + if (q == NULL) + n = strlen(p); + else + n = q-p; + if (p[0] == '.' && is_sep(p[1])) { + strcpy(buf, pythonhome); + buf = strchr(buf, '\0'); + p++; + n--; + } + strncpy(buf, p, n); + buf += n; + if (q == NULL) + break; + *buf++ = DELIM; + p = q+1; + } + } + if (argv0_path) { + *buf++ = DELIM; + strcpy(buf, argv0_path); + buf = strchr(buf, '\0'); + } + *buf = '\0'; +} + + +/* External interface */ + +char * +Py_GetPath(void) +{ + if (!module_search_path) + calculate_path(); + + return module_search_path; +} + +char * +Py_GetPrefix(void) +{ + if (!module_search_path) + calculate_path(); + + return prefix; +} + +char * +Py_GetExecPrefix(void) +{ + if (!module_search_path) + calculate_path(); + + return exec_prefix; +} + +char * +Py_GetProgramFullPath(void) +{ + if (!module_search_path) + calculate_path(); + + return progpath; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/makefile b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/makefile new file mode 100644 index 00000000..e4a71e8b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/makefile @@ -0,0 +1,1928 @@ +###################################################################### +# +# Top-Level Makefile for Building Python for OS/2 +# +# This makefile was developed for use with IBM's VisualAge C/C++ +# for OS/2 compiler, version 3.0, with Fixpack 8 applied. It uses +# version 4.0 of the NMAKE tool that comes with that package. +# +# The output of the build is a largish Python23.DLL containing the +# essential modules of Python and a small Python.exe program to start +# the interpreter. When embedding Python within another program, only +# Python23.DLL is needed. +# +# These two binaries can be statically linked with the VisualAge C/C++ +# runtime library (producing larger binaries), or dynamically linked +# to make smaller ones that require the compiler to be installed on +# any system Python is used on. Review the /Gd+ compiler option for +# how to do this. +# +# NOTE: IBM's NMAKE 4.0 is rather dumb, requiring this makefile to +# be much more complicated than necessary. I use OpusMAKE +# myself for a much more powerful MAKE tool but not everyone +# wishes to buy it. However, as a result I didn't hook in +# the dependencies on the include files as NMAKE has no easy +# way to do this without explicitly spelling it all out. +# +# History (Most Recent First) +# +# 26-Sep-98 jrr Retested and adjusted for building w/Python 1.5.2a1 +# 20-Nov-97 jrr Cleaned Up for Applying to Distribution +# 29-Oct-97 jrr Modified for Use with Python 1.5 Alpha 4 +# 03-Aug-96 jrr Original for Use with Python 1.4 Release +# +###################################################################### + +################### +# Places and Things +################### +PY_MODULES = ..\..\Modules +PY_OBJECTS = ..\..\Objects +PY_PARSER = ..\..\Parser +PY_PYTHON = ..\..\Python +PY_INCLUDE = ..\..\Include +PY_INCLUDES = .;$(PY_INCLUDE);$(PY_MODULES);$(PY_OBJECTS);$(PY_PARSER);$(PY_PYTHON) + +# File to Collect Wordy Compiler Output re Errors +ERRS = make.out + +# Where to Find the IBM TCP/IP Socket Includes and Libraries +OS2TCPIP = C:\MPTN + +# Where to Find the Tcl/Tk Base Directory for Libs/Includes +TCLTK = D:\TclTk +TCLBASE = D:\Tcl7.6\OS2 +TKBASE = D:\Tk4.2\OS2 + +# Where to Put the .OBJ Files, To Keep Them Out of the Way +PATHOBJ = obj + +# Search Path for Include Files +PROJINCLUDE = .;$(TCLBASE);$(TKBASE);$(OS2TCPIP)\Include;$(PY_INCLUDES) + +# Place to Search for Sources re OpusMAKE Dependency Generator (Commercial) +MKMF_SRCS = $(PY_MODULES)\*.c $(PY_OBJECTS)\*.c $(PY_PARSER)\*.c $(PY_PYTHON)\*.c + +#.HDRPATH.c := $(PROJINCLUDE,;= ) $(.HDRPATH.c) +#.PATH.c = .;$(PY_MODULES);$(PY_OBJECTS);$(PY_PARSER);$(PY_PYTHON) +OTHERLIBS = so32dll.lib tcp32dll.lib # Tcl76.lib Tk42.lib + +################# +# Inference Rules +################# +{$(PY_MODULES)\}.c{$(PATHOBJ)\}.obj: # Compile C Code into a .OBJ File + @ Echo Compiling $< + @ $(CC) -c $(CFLAGS) -Fo$@ $< >>$(ERRS) + +{$(PY_OBJECTS)\}.c{$(PATHOBJ)\}.obj: # Compile C Code into a .OBJ File + @ Echo Compiling $< + @ $(CC) -c $(CFLAGS) -Fo$@ $< >>$(ERRS) + +{$(PY_PARSER)\}.c{$(PATHOBJ)\}.obj: # Compile C Code into a .OBJ File + @ Echo Compiling $< + @ $(CC) -c $(CFLAGS) -Fo$@ $< >>$(ERRS) + +{$(PY_PYTHON)\}.c{$(PATHOBJ)\}.obj: # Compile C Code into a .OBJ File + @ Echo Compiling $< + @ $(CC) -c $(CFLAGS) -Fo$@ $< >>$(ERRS) + +.c{$(PATHOBJ)\}.obj: # Compile C Code into a .OBJ File + @ Echo Compiling $< + @ $(CC) -c $(CFLAGS) -Fo$@ $< >>$(ERRS) + +################### +# Python Subsystems +################### + +# PYTHON is the central core, containing the builtins and interpreter. +PYTHON = \ + $(PATHOBJ)\BltinModule.obj \ + $(PATHOBJ)\CEval.obj \ + $(PATHOBJ)\Compile.obj \ + $(PATHOBJ)\Errors.obj \ + $(PATHOBJ)\Frozen.obj \ + $(PATHOBJ)\Getargs.obj \ + $(PATHOBJ)\GetCompiler.obj \ + $(PATHOBJ)\GetCopyright.obj \ + $(PATHOBJ)\GetMTime.obj \ + $(PATHOBJ)\GetOpt.obj \ + $(PATHOBJ)\GetPlatform.obj \ + $(PATHOBJ)\GetVersion.obj \ + $(PATHOBJ)\GramInit.obj \ + $(PATHOBJ)\Import.obj \ + $(PATHOBJ)\ImportDL.obj \ + $(PATHOBJ)\Marshal.obj \ + $(PATHOBJ)\ModSupport.obj \ + $(PATHOBJ)\MyStrtoul.obj \ + $(PATHOBJ)\PyState.obj \ + $(PATHOBJ)\PythonRun.obj \ + $(PATHOBJ)\StructMember.obj \ + $(PATHOBJ)\SysModule.obj \ + $(PATHOBJ)\Thread.obj \ + $(PATHOBJ)\TraceBack.obj \ + $(PATHOBJ)\FrozenMain.obj \ + $(PATHOBJ)\exceptions.obj \ + $(PATHOBJ)\symtable.obj \ + $(PATHOBJ)\codecs.obj \ + $(PATHOBJ)\future.obj \ + $(PATHOBJ)\dynload_os2.obj \ + $(PATHOBJ)\mysnprintf.obj \ + $(PATHOBJ)\iterobject.obj + +# Python's Internal Parser +PARSER = \ + $(PATHOBJ)\Acceler.obj \ + $(PATHOBJ)\Grammar1.obj \ + $(PATHOBJ)\ListNode.obj \ + $(PATHOBJ)\Node.obj \ + $(PATHOBJ)\Parser.obj \ + $(PATHOBJ)\ParseTok.obj \ + $(PATHOBJ)\BitSet.obj \ + $(PATHOBJ)\MetaGrammar.obj \ + $(PATHOBJ)\Tokenizer.obj \ + $(PATHOBJ)\MyReadline.obj + +# Python Object Types +OBJECTS = \ + $(PATHOBJ)\Abstract.obj \ + $(PATHOBJ)\BoolObject.obj \ + $(PATHOBJ)\BufferObject.obj \ + $(PATHOBJ)\CellObject.obj \ + $(PATHOBJ)\ClassObject.obj \ + $(PATHOBJ)\CObject.obj \ + $(PATHOBJ)\ComplexObject.obj \ + $(PATHOBJ)\DescrObject.obj \ + $(PATHOBJ)\DictObject.obj \ + $(PATHOBJ)\EnumObject.obj \ + $(PATHOBJ)\FileObject.obj \ + $(PATHOBJ)\FloatObject.obj \ + $(PATHOBJ)\FrameObject.obj \ + $(PATHOBJ)\FuncObject.obj \ + $(PATHOBJ)\IntObject.obj \ + $(PATHOBJ)\IterObject.obj \ + $(PATHOBJ)\ListObject.obj \ + $(PATHOBJ)\LongObject.obj \ + $(PATHOBJ)\MethodObject.obj \ + $(PATHOBJ)\ModuleObject.obj \ + $(PATHOBJ)\Object.obj \ + $(PATHOBJ)\RangeObject.obj \ + $(PATHOBJ)\SliceObject.obj \ + $(PATHOBJ)\StringObject.obj \ + $(PATHOBJ)\StructSeq.obj \ + $(PATHOBJ)\TupleObject.obj \ + $(PATHOBJ)\TypeObject.obj \ + $(PATHOBJ)\UnicodeObject.obj \ + $(PATHOBJ)\UnicodeCType.obj \ + $(PATHOBJ)\WeakrefObject.obj + +# Extension Modules (Built-In or as Separate DLLs) +MODULES = \ + $(PATHOBJ)\ArrayModule.obj \ + $(PATHOBJ)\BinAscii.obj \ + $(PATHOBJ)\CMathModule.obj \ + $(PATHOBJ)\cPickle.obj \ + $(PATHOBJ)\cStringIO.obj \ + $(PATHOBJ)\ErrnoModule.obj \ + $(PATHOBJ)\GCModule.obj \ + $(PATHOBJ)\GetBuildInfo.obj \ + $(PATHOBJ)\GetPathP.obj \ + $(PATHOBJ)\Main.obj \ + $(PATHOBJ)\MathModule.obj \ + $(PATHOBJ)\MD5c.obj \ + $(PATHOBJ)\MD5Module.obj \ + $(PATHOBJ)\Operator.obj \ + $(PATHOBJ)\PCREModule.obj \ + $(PATHOBJ)\PyPCRE.obj \ + $(PATHOBJ)\RotorModule.obj \ + $(PATHOBJ)\PosixModule.obj \ + $(PATHOBJ)\RegexModule.obj \ + $(PATHOBJ)\RegExpr.obj \ + $(PATHOBJ)\SelectModule.obj \ + $(PATHOBJ)\SignalModule.obj \ + $(PATHOBJ)\SocketModule.obj \ + $(PATHOBJ)\StropModule.obj \ + $(PATHOBJ)\StructModule.obj \ + $(PATHOBJ)\TimeModule.obj \ + $(PATHOBJ)\ThreadModule.obj \ + $(PATHOBJ)\YUVConvert.obj + +# Standalone Parser Generator Program (Shares Some of Python's Modules) +PGEN = \ + $(PATHOBJ)\PGen.obj \ + $(PATHOBJ)\PGenMain.obj \ + $(PATHOBJ)\MySNPrintf.obj \ + $(PATHOBJ)\Tokenizer_Pgen.obj \ + $(PATHOBJ)\PrintGrammar.obj \ + $(PATHOBJ)\Grammar.obj \ + $(PATHOBJ)\FirstSets.obj + +################## +# Macros and Flags +################## +_BASE = /Q /W2 /I$(PROJINCLUDE) + # /Q = Omit IBM Copyright + # /W2 = Show Warnings/Errors Only + +_GEN = /G4 /Gm /Gd- + # /G4 = Generate Code for 486 (Use 386 for Debugger) + # /Gm = Use Multithread Runtime + # /Gd = Dynamically Load Runtime + # /Ms = Use _System Calling Convention (vs _Optlink) + # (to allow non-VAC++ code to call into Python23.dll) + +_OPT = /O /Gl + # /O = Enable Speed-Optimizations + # /Ol = Pass Code Thru Intermediate Linker + # /Gu = Advise Linker All Ext Data is ID'd + # /Gl = Have Linker Remove Unused Fns + +_DBG = /Wpro- /Ti- /DHAVE_CONFIG_H /DUSE_SOCKET + # /Wpro= Generate Compiler Warnings re Missing Prototypes + # /Ti = Embed Debugger/Analyzer Recs + # /Tm = Enable Debug Memory Fns + # /Tx = Request Full Dump Upon Exception + # /DHAVE_CONFIG_H = Causes Use of CONFIG.H Settings + # /DUSE_SOCKET = Enables Building In of Socket API + +_OUT = + # /Fb = Embed Browser Recs + # /Gh = Generate Code for Profiler Hooks + # /Fl = Output C/C++ Listing Files + # /Lf = Provide Full (Detailed) Listing Files + # /Fm. = Output Linker Map File + # /Ft. = Output C++ Template Resolution Files + +_MAP = /FmNoise\$(@R).map + +_DLL = /Ge- +_EXE = /Ge + # /Ge = Create an EXE, not DLL + +CFLAGS = $(_BASE) $(_GEN) $(_OPT) $(_DBG) $(_OUT) $(_EXE) /Ss + +################### +# Primary Target(s) +################### +All: obj noise PyCore.lib Python23.lib PGen.exe \ + Python.exe PythonPM.exe Python23.dll # _tkinter.dll + +Modules: $(MODULES) +Objects: $(OBJECTS) +Parser: $(PARSER) +Python: $(PYTHON) + +# Directory to Keep .OBJ Files Out of the Way +obj: + @-mkdir obj >>NUL + +# Directory to Keep .MAP and Such Text Files Out of the Way +noise: + @-mkdir noise >>NUL + +############## +# +############## + +# Python Extension DLL: Tcl/Tk Interface +#_tkinter.dll: $(PATHOBJ)\_tkinter.obj Python23.lib _tkinter.def +# @ Echo Linking $@ As DLL +# @ $(CC) $(CFLAGS) /B"/NOE" $(_DLL) /Fe$@ $(_MAP) $** $(OTHERLIBS) >>$(ERRS) + +#$(PATHOBJ)\_tkinter.obj: $(PY_MODULES)\_tkinter.c +# @ Echo Compiling $** +# @ $(CC) -c $(CFLAGS) $(_DLL) -Fo$@ $** >>$(ERRS) + +# Object Library of All Essential Python Routines +PyCore.lib: $(MODULES) $(OBJECTS) $(PARSER) $(PYTHON) $(PATHOBJ)\Config.obj + @ Echo Adding Updated Object Files to Link Library $@ + @ ! ILIB $@ /NOLOGO /NOBACKUP -+$? ; >>$(ERRS) + +Python23.dll: $(PATHOBJ)\Compile.obj PyCore.lib Python.def + @ Echo Linking $@ As DLL + @ $(CC) $(CFLAGS) /B"/NOE" $(_DLL) /Fe$@ $(_MAP) $** $(OTHERLIBS) >>$(ERRS) +# @ Echo Compressing $@ with LxLite +# @ lxlite $@ + +# IBM Linker Requires One Explicit .OBJ To Build a .DLL from a .LIB +$(PATHOBJ)\Compile.obj: $(PY_PYTHON)\Compile.c + @ Echo Compiling $** + @ $(CC) -c $(CFLAGS) $(_DLL) -Fo$@ $** >>$(ERRS) + +# Import Library for Using the Python23.dll +Python23.lib: Python.def + @ Echo Making $@ + @ IMPLIB /NOLOGO /NOIGNORE $@ $** >>$(ERRS) + @ ILIB /NOLOGO /CONVFORMAT /NOEXTDICTIONARY /NOBROWSE /NOBACKUP $@; >>$(ERRS) + +# Small Command-Line Program to Start Interpreter in Python23.dll +Python.exe: $(PATHOBJ)\Python.obj Python23.lib + @ Echo Linking $@ As EXE + @ $(CC) $(CFLAGS) $(_EXE) /B"/PM:VIO /STACK:360000" /Fe$@ $(_MAP) $** $(OTHERLIBS) >>$(ERRS) + +# Small PM-GUI Program to Start Interpreter in Python23.dll +PythonPM.exe: $(PATHOBJ)\Python.obj Python23.lib + @ Echo Linking $@ As EXE + @ $(CC) $(CFLAGS) $(_EXE) /B"/PM:PM /STACK:360000" /Fe$@ $(_MAP) $** $(OTHERLIBS) >>$(ERRS) + +PGen.exe: $(PGEN) PyCore.lib + @ Echo Linking $@ As EXE + @ $(CC) $(CFLAGS) $(_EXE) /B"/STACK:360000" /Fe$@ $(_MAP) $** $(OTHERLIBS) >>$(ERRS) + +####################### +# Miscellaneous Targets +####################### + +# Remove Intermediate Targets but Leave Executable Binaries +clean: + -- Del /Q $(PATHOBJ)\*.obj >NUL 2>&1 + -- Del /Q /Y Noise >NUL 2>&1 + -- Del /Q $(ERRS) >NUL 2>&1 + +# Remove All Targets, Including Final Binaries +distclean: clean + -- Del /Q PyCore.lib Python23.lib >NUL 2>&1 + -- Del /Q Python23.dll Python.exe PGen.exe >NUL 2>&1 + +release: Python.exe Python23.dll Python23.lib + -- @Echo Y | copy /U Python.exe D:\EXEs + -- @Echo Y | copy /U Python23.dll D:\DLLs + -- @Echo Y | copy /U Python23.lib E:\Tau\Lib + -- @Echo Y | copy /U _tkinter.dll D:\Python + +test: + python ..\..\lib\test\regrtest.py + +# Update Dependencies on Targets (Uses OpusMAKE Commercial Product) +depend: + D:\OpusMake\os2mkmf -c -s + +### OPUS MKMF: Do not remove this line! Generated dependencies follow. + +_tkinter.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +almodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +arraymodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +audioop.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\mymath.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +binascii.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +bsddbmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +cdmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +cgensupport.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_MODULES)\cgensupport.h $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h \ + $(PY_INCLUDE)\complexobject.h pyconfig.h $(PY_INCLUDE)\dictobject.h \ + $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h \ + $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h \ + $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +clmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +cmathmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\mymath.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h \ + $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +cpickle.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\cstringio.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\mymath.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h \ + $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +cryptmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +cstringio.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\cstringio.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +cursesmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +dbmmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +dlmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +errno.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +errnomodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +fcntlmodule.obj: $(PY_INCLUDE)\abstract.h $(OS2TCPIP)\Include\sys\ioctl.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +flmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\structmember.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +fmmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +fpectlmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +fpetestmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +gdbmmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +getbuildinfo.obj: pyconfig.h + +getpath.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\osdefs.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +glmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_MODULES)\cgensupport.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +grpmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(OS2TCPIP)\Include\grp.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +imageop.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +imgfile.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +main.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +mathmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\mymath.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +md5c.obj: pyconfig.h $(PY_MODULES)\md5.h + +md5module.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_MODULES)\md5.h $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +mpzmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longintrepr.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +nismodule.obj: $(PY_INCLUDE)\abstract.h $(OS2TCPIP)\Include\sys\time.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +operator.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +parsermodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\compile.h \ + $(PY_INCLUDE)\complexobject.h pyconfig.h $(PY_INCLUDE)\dictobject.h \ + $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h \ + $(PY_INCLUDE)\graminit.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\node.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\token.h \ + $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +pcremodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_MODULES)\pcre-internal.h \ + $(PY_MODULES)\pcre.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +posix.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\mytime.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +posixmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\mytime.h $(PY_INCLUDE)\object.h \ + $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +puremodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +pwdmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(OS2TCPIP)\Include\pwd.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +pypcre.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\graminit.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_MODULES)\pcre-internal.h $(PY_MODULES)\pcre.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +readline.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +regexmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_MODULES)\regexpr.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +regexpr.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_MODULES)\regexpr.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +resource.obj: $(PY_INCLUDE)\abstract.h $(OS2TCPIP)\Include\sys\time.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +rgbimgmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +rotormodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\mymath.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h \ + $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +selectmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\myselect.h $(PY_INCLUDE)\mytime.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +sgimodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +signalmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +socketmodule.obj: $(PY_INCLUDE)\abstract.h $(OS2TCPIP)\Include\netinet\in.h \ + $(OS2TCPIP)\Include\sys\socket.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\mytime.h $(OS2TCPIP)\Include\netdb.h $(PY_INCLUDE)\object.h \ + $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +soundex.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +stdwinmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +stropmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +structmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\mymath.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h \ + $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +sunaudiodev.obj: $(PY_INCLUDE)\abstract.h $(OS2TCPIP)\Include\sys\ioctl.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\structmember.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +svmodule.obj: $(PY_INCLUDE)\abstract.h $(OS2TCPIP)\Include\sys\time.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\compile.h \ + $(PY_INCLUDE)\complexobject.h pyconfig.h $(PY_INCLUDE)\dictobject.h \ + $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h \ + $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h \ + $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h \ + $(PY_MODULES)\yuv.h + +syslogmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(OS2TCPIP)\Include\syslog.h $(PY_INCLUDE)\sysmodule.h \ + $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +termios.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +threadmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\thread.h \ + $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +timemodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\mytime.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +timingmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_MODULES)\timing.h \ + $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +xxmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +yuvconvert.obj: $(PY_MODULES)\yuv.h + +zlibmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +abstract.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +classobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\structmember.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +cobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +complexobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\mymath.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h \ + $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +dictobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +fileobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\structmember.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +floatobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\mymath.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h \ + $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +frameobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\compile.h \ + $(PY_INCLUDE)\complexobject.h pyconfig.h $(PY_INCLUDE)\dictobject.h \ + $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\frameobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\opcode.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\structmember.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +funcobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\compile.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\structmember.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +intobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +listobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +longobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longintrepr.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\mymath.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +methodobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\token.h \ + $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +moduleobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +object.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +rangeobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +sliceobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +stringobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\mymath.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h \ + $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +tupleobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +typeobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +xxobject.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +acceler.obj: $(PY_INCLUDE)\bitset.h pyconfig.h $(PY_INCLUDE)\grammar.h \ + $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\node.h \ + $(PY_PARSER)\parser.h $(PY_INCLUDE)\pgenheaders.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\token.h + +bitset.obj: $(PY_INCLUDE)\bitset.h pyconfig.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\pgenheaders.h $(PY_INCLUDE)\pydebug.h + +firstsets.obj: $(PY_INCLUDE)\bitset.h pyconfig.h $(PY_INCLUDE)\grammar.h \ + $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\pgenheaders.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\token.h + +grammar.obj: $(PY_INCLUDE)\bitset.h pyconfig.h \ + $(PY_INCLUDE)\grammar.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\pgenheaders.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\token.h + +grammar1.obj: $(PY_INCLUDE)\bitset.h pyconfig.h \ + $(PY_INCLUDE)\grammar.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\pgenheaders.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\token.h + +intrcheck.obj: pyconfig.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h + +listnode.obj: pyconfig.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\node.h $(PY_INCLUDE)\pgenheaders.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\token.h + +metagrammar.obj: $(PY_INCLUDE)\bitset.h pyconfig.h $(PY_INCLUDE)\grammar.h \ + $(PY_INCLUDE)\metagrammar.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_PARSER)\pgen.h $(PY_INCLUDE)\pgenheaders.h $(PY_INCLUDE)\pydebug.h + +myreadline.obj: pyconfig.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h + +node.obj: pyconfig.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\node.h \ + $(PY_INCLUDE)\pgenheaders.h $(PY_INCLUDE)\pydebug.h + +parser.obj: $(PY_INCLUDE)\bitset.h pyconfig.h $(PY_INCLUDE)\errcode.h \ + $(PY_INCLUDE)\grammar.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\node.h $(PY_PARSER)\parser.h $(PY_INCLUDE)\pgenheaders.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\token.h + +parsetok.obj: $(PY_INCLUDE)\bitset.h pyconfig.h $(PY_INCLUDE)\errcode.h \ + $(PY_INCLUDE)\grammar.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\node.h $(PY_PARSER)\parser.h $(PY_INCLUDE)\parsetok.h \ + $(PY_INCLUDE)\pgenheaders.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\token.h \ + $(PY_PARSER)\tokenizer.h + +pgen.obj: $(PY_INCLUDE)\bitset.h pyconfig.h $(PY_INCLUDE)\grammar.h \ + $(PY_INCLUDE)\metagrammar.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\node.h $(PY_PARSER)\pgen.h $(PY_INCLUDE)\pgenheaders.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\token.h + +pgenmain.obj: $(PY_INCLUDE)\bitset.h pyconfig.h $(PY_INCLUDE)\grammar.h \ + $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\node.h \ + $(PY_INCLUDE)\parsetok.h $(PY_PARSER)\pgen.h $(PY_INCLUDE)\pgenheaders.h \ + $(PY_INCLUDE)\pydebug.h + +printgrammar.obj: $(PY_INCLUDE)\bitset.h pyconfig.h $(PY_INCLUDE)\grammar.h \ + $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\pgenheaders.h \ + $(PY_INCLUDE)\pydebug.h + +tokenizer.obj: pyconfig.h $(PY_INCLUDE)\errcode.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\pgenheaders.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\token.h $(PY_PARSER)\tokenizer.h + +atof.obj: pyconfig.h + +bltinmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\compile.h \ + $(PY_INCLUDE)\complexobject.h pyconfig.h $(PY_INCLUDE)\dictobject.h \ + $(PY_INCLUDE)\eval.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\mymath.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\node.h $(PY_INCLUDE)\object.h \ + $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +ceval.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\compile.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\eval.h \ + $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\frameobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\opcode.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +compile.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\compile.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\graminit.h \ + $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h \ + $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\node.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\opcode.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\structmember.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\token.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +errors.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +fmod.obj: pyconfig.h $(PY_INCLUDE)\mymath.h + +frozen.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +frozenmain.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +getargs.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +getcompiler.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +getcopyright.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +getmtime.obj: pyconfig.h + +getplatform.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +getversion.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\patchlevel.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +graminit.obj: $(PY_INCLUDE)\bitset.h pyconfig.h $(PY_INCLUDE)\grammar.h \ + $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\pgenheaders.h \ + $(PY_INCLUDE)\pydebug.h + +hypot.obj: pyconfig.h $(PY_INCLUDE)\mymath.h $(PY_INCLUDE)\myproto.h + +import.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\compile.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\errcode.h $(PY_INCLUDE)\eval.h \ + $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h \ + $(PY_INCLUDE)\import.h $(PY_PYTHON)\importdl.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\marshal.h $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\node.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\osdefs.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\token.h \ + $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +importdl.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_PYTHON)\importdl.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\osdefs.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +marshal.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\compile.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longintrepr.h $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\marshal.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +modsupport.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +mystrtoul.obj: pyconfig.h + +pyfpe.obj: pyconfig.h $(PY_INCLUDE)\pyfpe.h + +pystate.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +pythonrun.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\bitset.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\compile.h \ + $(PY_INCLUDE)\complexobject.h pyconfig.h $(PY_INCLUDE)\dictobject.h \ + $(PY_INCLUDE)\errcode.h $(PY_INCLUDE)\eval.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\grammar.h \ + $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h \ + $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\marshal.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\node.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\parsetok.h $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h \ + $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h \ + $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +sigcheck.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\pydebug.h \ + $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h $(PY_INCLUDE)\pystate.h \ + $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h $(PY_INCLUDE)\rangeobject.h \ + $(PY_INCLUDE)\sliceobject.h $(PY_INCLUDE)\stringobject.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +strdup.obj: pyconfig.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h + +strtod.obj: pyconfig.h + +structmember.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h \ + $(PY_INCLUDE)\classobject.h $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h \ + $(PY_INCLUDE)\intobject.h $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h \ + $(PY_INCLUDE)\longobject.h $(PY_INCLUDE)\methodobject.h \ + $(PY_INCLUDE)\modsupport.h $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h \ + $(PY_INCLUDE)\myproto.h $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\structmember.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + +sysmodule.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\complexobject.h pyconfig.h \ + $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h $(PY_INCLUDE)\floatobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\osdefs.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h \ + $(PY_INCLUDE)\tupleobject.h + +thread.obj: pyconfig.h $(PY_INCLUDE)\thread.h + +traceback.obj: $(PY_INCLUDE)\abstract.h $(PY_INCLUDE)\ceval.h $(PY_INCLUDE)\classobject.h \ + $(PY_INCLUDE)\cobject.h $(PY_INCLUDE)\compile.h $(PY_INCLUDE)\complexobject.h \ + pyconfig.h $(PY_INCLUDE)\dictobject.h $(PY_INCLUDE)\fileobject.h \ + $(PY_INCLUDE)\floatobject.h $(PY_INCLUDE)\frameobject.h \ + $(PY_INCLUDE)\funcobject.h $(PY_INCLUDE)\import.h $(PY_INCLUDE)\intobject.h \ + $(PY_INCLUDE)\intrcheck.h $(PY_INCLUDE)\listobject.h $(PY_INCLUDE)\longobject.h \ + $(PY_INCLUDE)\methodobject.h $(PY_INCLUDE)\modsupport.h \ + $(PY_INCLUDE)\moduleobject.h $(PY_INCLUDE)\mymalloc.h $(PY_INCLUDE)\myproto.h \ + $(PY_INCLUDE)\object.h $(PY_INCLUDE)\objimpl.h $(PY_INCLUDE)\osdefs.h \ + $(PY_INCLUDE)\pydebug.h $(PY_INCLUDE)\pyerrors.h $(PY_INCLUDE)\pyfpe.h \ + $(PY_INCLUDE)\pystate.h $(PY_INCLUDE)\python.h $(PY_INCLUDE)\pythonrun.h \ + $(PY_INCLUDE)\rangeobject.h $(PY_INCLUDE)\sliceobject.h \ + $(PY_INCLUDE)\stringobject.h $(PY_INCLUDE)\structmember.h \ + $(PY_INCLUDE)\sysmodule.h $(PY_INCLUDE)\traceback.h $(PY_INCLUDE)\tupleobject.h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/makefile.omk b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/makefile.omk new file mode 100644 index 00000000..28c5de45 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/makefile.omk @@ -0,0 +1,1301 @@ +###################################################################### +# +# Top-Level Makefile for Building Python for OS/2 +# +# This makefile was developed for use with IBM's VisualAge C/C++ +# for OS/2 compiler, version 3.0, with Fixpack 8 applied. It uses +# the commercial OpusMAKE tool. +# +# The output of the build is a largish Python15.DLL containing the +# essential modules of Python and a small Python.exe program to start +# the interpreter. When embedding Python within another program, only +# Python15.DLL is needed. +# +# These two binaries can be statically linked with the VisualAge C/C++ +# runtime library (producing larger binaries), or dynamically linked +# to make smaller ones that require the compiler to be installed on +# any system Python is used on. +# +# History (Most Recent First) +# +# 20-Nov-97 jrr Cleaned Up for Applying to Distribution +# 29-Oct-97 jrr Modified for Use with Python 1.5 Alpha 4 +# 03-Aug-96 jrr Original for Use with Python 1.4 Release +# +###################################################################### + +################### +# Places and Things +################### +PY_MODULES = ..\..\Modules +PY_OBJECTS = ..\..\Objects +PY_PARSER = ..\..\Parser +PY_PYTHON = ..\..\Python +PY_INCLUDE = ..\..\Include +PY_INCLUDES = .;$(PY_INCLUDE);$(PY_MODULES);$(PY_OBJECTS);$(PY_PARSER);$(PY_PYTHON) + +# Where to Find the IBM TCP/IP Socket Includes and Libraries +OS2TCPIP = C:\MPTN + +# Where to Put the .OBJ Files, To Keep Them Out of the Way +.PATH.obj = obj + +# Search Path for Include Files +PROJINCLUDE = .;$(OS2TCPIP)\Include;$(PY_INCLUDES) + +# Place to Search for Sources re OpusMAKE Dependency Generator (Commercial) +MKMF_SRCS = $(PY_MODULES)\*.c $(PY_OBJECTS)\*.c $(PY_PARSER)\*.c $(PY_PYTHON)\*.c + +.HDRPATH.c := $(PROJINCLUDE,;= ) $(.HDRPATH.c) +.PATH.c = .;$(PY_MODULES);$(PY_OBJECTS);$(PY_PARSER);$(PY_PYTHON) +OTHERLIBS = $(OS2TCPIP)\lib\so32dll.lib $(OS2TCPIP)\lib\tcp32dll.lib + +################# +# Inference Rules +################# + + +################### +# Python Subsystems +################### + +# PYTHON is the central core, containing the builtins and interpreter. +PYTHON = \ + BltinModule.obj \ + CEval.obj \ + Compile.obj \ + Errors.obj \ + Frozen.obj \ + Getargs.obj \ + GetCompiler.obj \ + GetCopyright.obj \ + GetMTime.obj \ + GetOpt.obj \ + GetPlatform.obj \ + GetVersion.obj \ + GramInit.obj \ + Import.obj \ + ImportDL.obj \ + Marshal.obj \ + ModSupport.obj \ + MyStrtoul.obj \ + PyState.obj \ + PythonRun.obj \ + StructMember.obj \ + SysModule.obj \ + Thread.obj \ + TraceBack.obj \ + FrozenMain.obj + +# Omitted Python Elements (and Reason): + # atof.c -- Implementation for Platforms w/o This Function + # dup2.c -- Implementation for Platforms w/o This Function + # fmod.c -- Implementation for Platforms w/o This Function + # getcwd.c -- Implementation for Platforms w/o This Function + # hypot.c -- Implementation for Platforms w/o This Function + # memmove.c -- Implementation for Platforms w/o This Function + # strdup.c -- Implementation for Platforms w/o This Function + # strerror.c -- Implementation for Platforms w/o This Function + # strtod.c -- Implementation for Platforms w/o This Function + + # sigcheck.c -- Primitive Signal Catcher (SignalModule.c Used Instead) + # pyfpe.c -- Primitive FPE Catcher (Not Referenced by Anyone) + # frozenmain.c + +# Python's Internal Parser +PARSER = \ + Acceler.obj \ + Grammar1.obj \ + MyReadline.obj \ + Node.obj \ + Parser.obj \ + ParseTok.obj \ + Tokenizer.obj + +# Python Object Types +OBJECTS = \ + Abstract.obj \ + ClassObject.obj \ + CObject.obj \ + ComplexObject.obj \ + DictObject.obj \ + FileObject.obj \ + FloatObject.obj \ + FrameObject.obj \ + FuncObject.obj \ + IntObject.obj \ + ListObject.obj \ + LongObject.obj \ + MethodObject.obj \ + ModuleObject.obj \ + Object.obj \ + RangeObject.obj \ + SliceObject.obj \ + StringObject.obj \ + TupleObject.obj \ + TypeObject.obj + +# Omitted Objects (and Reason): + # xxobject.c -- Template to Create Your Own Object Types + +# Extension Modules (Built-In or as Separate DLLs) +MODULES = \ + ArrayModule.obj \ + BinAscii.obj \ + CMathModule.obj \ + cPickle.obj \ + cStringIO.obj \ + ErrnoModule.obj \ + GetBuildInfo.obj \ + GetPathP.obj \ + Main.obj \ + MathModule.obj \ + MD5c.obj \ + MD5Module.obj \ + Operator.obj \ + PosixModule.obj \ + RegexModule.obj \ + RegExpr.obj \ + ReopModule.obj \ + SelectModule.obj \ + SignalModule.obj \ + SocketModule.obj \ + SoundEx.obj \ + StropModule.obj \ + StructModule.obj \ + TimeModule.obj \ + ThreadModule.obj \ + YUVConvert.obj + +# Omitted Modules (and Description/Reason): + # + # Multimedia: + # almodule.c -- Non-OS/2 Audio Channel Facility (?) + # cdmodule.c -- Wrapper of Non-OS/2 CD Audio Functions + # audioop.c -- Various Compute Operations on Audio Samples + # imageop.c -- Various Compute Operations on Video Samples + # imgfile.c -- Wrapper of SGI ImageLib API + # rgbimgmodule.c -- Non-OS/2 Image Read/Write Capability (Primitive) + # sunaudiodev.c -- Wrapper of Sun Audio Device API + # clmodule.c -- Wrapper of SGI Image/Audio Compression API + + # Database: + # dbmmodule.c -- Wrapper of DBM Database API (Generic Flavor) + # bsddbmodule.c -- Wrapper of DBM Database API (BSD Flavor) + # gdbmmodule.c -- Wrapper of DBM Database API (GNU Flavor) + + # Cryptography: + # cryptmodule.c -- Simple Wrapper for crypt() Function + # rotormodule.c -- Implementation of Enigma Crypto Based on Rotors + +# cgensupport.obj \ +# fcntlmodule.obj \ +# fmmodule.obj \ +# fpectlmodule.obj \ +# fpetestmodule.obj \ +# Unix-Specific getpath.obj \ +# glmodule.obj \ +# grpmodule.obj \ +# mpzmodule.obj \ +# nismodule.obj \ +# parsermodule.obj \ +# pcremodule.obj \ +# pwdmodule.obj \ +# pypcre.obj \ +# readline.obj \ +# resource.obj \ +# sgimodule.obj \ +# svmodule.obj \ +# syslogmodule.obj \ +# termios.obj \ +# timingmodule.obj \ + + # User Interface: +# _tkinter.obj \ +# stdwinmodule.obj \ +# cursesmodule.obj \ +# tclNotify.obj \ +# tkappinit.obj \ + # flmodule.c -- Wrapper of FORMS Library (Screen Forms) + + # zlibmodule.c -- Wrapper of ZLib Compression API (GZip Format) + # puremodule.c -- Wrapper of Purify Debugging API (Probably Non-OS/2) + # dlmodule.c -- Some Wierd Form of Data Processing Module + # xxmodule.c -- Template to Create Your Own Module + +# +# Standalone Parser Generator Program (Shares Some of Python's Modules) +PGEN = \ + PGenMain.obj \ + PGen.obj \ + PrintGrammar.obj \ + ListNode.obj \ + Grammar.obj \ + BitSet.obj \ + FirstSets.obj \ + MetaGrammar.obj + +# Omitted Parser Elements (and Reason): + # intrcheck.c -- Not Referenced by Anyone (?) + +################## +# Macros and Flags +################## +_BASE = /Q /W2 /I$(PROJINCLUDE) + # /Q = Omit IBM Copyright + # /W2 = Show Warnings/Errors Only + # /Fi = Create Precompiled Headers + # /Si = Utilize Precompiled Headers + +_GEN = /G4 /Gm /Gd /B"/STACK:360000" + # /G4 = Generate Code for 486 (Use 386 for Debugger) + # /Gm = Use Multithread Runtime + # /Gd = Dynamically Load Runtime + # /Gs = Remove Code for Stack Probes + # /Gx = Remove C++ Exception-Handling Info + # /Tdp = Generate Code for C++ Templates + # /Rn = Generate Code without a Runtime + # /B"/STACK:n" = Set Stack Size + +_OPT = /O /Gl + # /O = Enable Speed-Optimizations + # /Ol = Pass Code Thru Intermediate Linker + # /Gu = Advise Linker All Ext Data is ID'd + # /Gl = Have Linker Remove Unused Fns + +_DBG = /DHAVE_CONFIG_H /DUSE_SOCKET + # /Ti = Embed Debugger/Analyzer Recs + # /Tm = Enable Debug Memory Fns + # /Tx = Request Full Dump Upon Exception + # /DDEBUG = Enable App-Internal Debugging Code + # /DUSE_SOCKET = + # /DUSE_DL_EXPORT = + +_OUT = + # /Fb = Embed Browser Recs + # /Gh = Generate Code for Profiler Hooks + # /Fl = Output C/C++ Listing Files + # /Lf = Provide Full (Detailed) Listing Files + # /Fm. = Output Linker Map File + # /Ft. = Output C++ Template Resolution Files + +_MAP = /FmNoise\$(.TARGET,B,>.map) + +_DLL = /Ge- +_EXE = /Ge + # /Ge = Create an EXE, not DLL + +CFLAGS = $(_BASE) $(_GEN) $(_OPT) $(_DBG) $(_OUT) $(_EXE) /Ss +CPPFLAGS = $(_BASE) $(_GEN) $(_OPT) $(_DBG) $(_OUT) $(_EXE) + +################### +# Primary Target(s) +################### +All: obj noise PyCore.lib Python15.lib Python15.dll Python.exe PGen.exe + +############## +# +############## + +# Object Library of All Essential Python Routines +PyCore.lib: $(MODULES) $(OBJECTS) $(PARSER) $(PYTHON) Config.obj + %do "%.lib" + +Python15.dll: Compile.obj PyCore.lib Python.def + %do "%.dll" CPPFLAGS+=/B"/NOE" CPPFLAGS+=$(_MAP) + +Compile.obj: Compile.c + %do ".c.obj" CFLAGS+=$(_DLL) + +# Import Library for Using the Python15.dll +Python15.lib: Python.def + %do ".def.lib" + +# Small Program to Start Interpreter in Python15.dll +Python.exe: Python.obj Python15.lib + %do "%.exe" CPPFLAGS+=$(_MAP) + +#Python.obj: Python.c +# %do ".c.obj" CFLAGS+=$(_EXE) + +PGen.exe: $(PGEN) PyCore.lib + %do "%.exe" CPPFLAGS+=$(_MAP) + +####################### +# Miscellaneous Targets +####################### + +# Remove Intermediate Targets but Leave Executable Binaries +clean: + -- Del /Q $(.PATH.obj)\*.obj >NUL 2>&1 + -- Del /Q /Y Noise >NUL 2>&1 + -- Del /Q $(ERRS) >NUL 2>&1 + +# Remove All Targets, Including Final Binaries +distclean: clean + -- Del /Q PyCore.lib Python15.lib >NUL 2>&1 + -- Del /Q Python15.dll Python.exe >NUL 2>&1 + +release: Python.exe Python15.dll Python15.lib + -- @Echo Y | copy /U $(.SOURCES,M"*.exe") D:\EXEs + -- @Echo Y | copy /U $(.SOURCES,M"*.dll") D:\DLLs + -- @Echo Y | copy /U $(.SOURCES,M"*.lib") E:\Tau\Lib + +test: + python ..\..\lib\test\regrtest.py + +# Update Dependencies on Targets (Uses OpusMAKE Commercial Product) +depend: + D:\OpusMake\os2mkmf -c -s + +### OPUS MKMF: Do not remove this line! Generated dependencies follow. + +_tkinter.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +almodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +arraymodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +audioop.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h mymath.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +binascii.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +bsddbmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +cdmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +cgensupport.obj: abstract.h ceval.h cgensupport.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +clmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +cmathmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h mymath.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +cpickle.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h cstringio.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + mymath.h myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +cryptmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +cstringio.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h cstringio.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +cursesmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +dbmmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +dlmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +errno.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +errnomodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +fcntlmodule.obj: abstract.h c:\mptn\include\sys\ioctl.h ceval.h \ + classobject.h cobject.h complexobject.h pyconfig.h dictobject.h \ + fileobject.h floatobject.h funcobject.h import.h intobject.h \ + intrcheck.h listobject.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h object.h objimpl.h pydebug.h \ + pyerrors.h pyfpe.h pystate.h python.h pythonrun.h rangeobject.h \ + sliceobject.h stringobject.h sysmodule.h traceback.h tupleobject.h + +flmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h \ + structmember.h sysmodule.h traceback.h tupleobject.h + +fmmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +fpectlmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +fpetestmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +gdbmmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +getbuildinfo.obj: pyconfig.h + +getpath.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h osdefs.h pydebug.h pyerrors.h pyfpe.h pystate.h \ + python.h pythonrun.h rangeobject.h sliceobject.h stringobject.h \ + sysmodule.h traceback.h tupleobject.h + +glmodule.obj: abstract.h ceval.h cgensupport.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +grpmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + grp.h import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +imageop.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +imgfile.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +main.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +mathmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h mymath.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +md5c.obj: pyconfig.h md5.h + +md5module.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h md5.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +mpzmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longintrepr.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h object.h objimpl.h pydebug.h \ + pyerrors.h pyfpe.h pystate.h python.h pythonrun.h rangeobject.h \ + sliceobject.h stringobject.h sysmodule.h traceback.h tupleobject.h + +nismodule.obj: abstract.h c:\mptn\include\sys\time.h ceval.h classobject.h \ + cobject.h complexobject.h pyconfig.h dictobject.h fileobject.h \ + floatobject.h funcobject.h import.h intobject.h intrcheck.h \ + listobject.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h object.h objimpl.h pydebug.h \ + pyerrors.h pyfpe.h pystate.h python.h pythonrun.h rangeobject.h \ + sliceobject.h stringobject.h sysmodule.h traceback.h tupleobject.h + +operator.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +parsermodule.obj: abstract.h ceval.h classobject.h cobject.h compile.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h graminit.h import.h intobject.h intrcheck.h \ + listobject.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h node.h object.h objimpl.h \ + pydebug.h pyerrors.h pyfpe.h pystate.h python.h pythonrun.h \ + rangeobject.h sliceobject.h stringobject.h sysmodule.h token.h \ + traceback.h tupleobject.h + +pcremodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pcre-internal.h pcre.h pydebug.h pyerrors.h \ + pyfpe.h pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +posix.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + mytime.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h \ + python.h pythonrun.h rangeobject.h sliceobject.h stringobject.h \ + sysmodule.h traceback.h tupleobject.h + +posixmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + mytime.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h \ + python.h pythonrun.h rangeobject.h sliceobject.h stringobject.h \ + sysmodule.h traceback.h tupleobject.h + +puremodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +pwdmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pwd.h pydebug.h pyerrors.h pyfpe.h pystate.h \ + python.h pythonrun.h rangeobject.h sliceobject.h stringobject.h \ + sysmodule.h traceback.h tupleobject.h + +pypcre.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + graminit.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pcre-internal.h pcre.h pydebug.h \ + pyerrors.h pyfpe.h pystate.h python.h pythonrun.h rangeobject.h \ + sliceobject.h stringobject.h sysmodule.h traceback.h tupleobject.h + +readline.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +regexmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h regexpr.h sliceobject.h stringobject.h \ + sysmodule.h traceback.h tupleobject.h + +regexpr.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h regexpr.h \ + sliceobject.h stringobject.h sysmodule.h traceback.h tupleobject.h + +reopmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h regexpr.h sliceobject.h stringobject.h \ + sysmodule.h traceback.h tupleobject.h + +resource.obj: abstract.h c:\mptn\include\sys\time.h ceval.h classobject.h \ + cobject.h complexobject.h pyconfig.h dictobject.h fileobject.h \ + floatobject.h funcobject.h import.h intobject.h intrcheck.h \ + listobject.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h object.h objimpl.h pydebug.h \ + pyerrors.h pyfpe.h pystate.h python.h pythonrun.h rangeobject.h \ + sliceobject.h stringobject.h sysmodule.h traceback.h tupleobject.h + +rgbimgmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +rotormodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h mymath.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +selectmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h myselect.h mytime.h object.h objimpl.h pydebug.h \ + pyerrors.h pyfpe.h pystate.h python.h pythonrun.h rangeobject.h \ + sliceobject.h stringobject.h sysmodule.h traceback.h tupleobject.h + +sgimodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +signalmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +socketmodule.obj: abstract.h c:\mptn\include\netinet\in.h \ + c:\mptn\include\sys\socket.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h mytime.h netdb.h object.h objimpl.h pydebug.h pyerrors.h \ + pyfpe.h pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +soundex.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +stdwinmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +stropmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +structmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + mymath.h myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +sunaudiodev.obj: abstract.h c:\mptn\include\sys\ioctl.h ceval.h \ + classobject.h cobject.h complexobject.h pyconfig.h dictobject.h \ + fileobject.h floatobject.h funcobject.h import.h intobject.h \ + intrcheck.h listobject.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h object.h objimpl.h pydebug.h \ + pyerrors.h pyfpe.h pystate.h python.h pythonrun.h rangeobject.h \ + sliceobject.h stringobject.h structmember.h sysmodule.h \ + traceback.h tupleobject.h + +svmodule.obj: abstract.h c:\mptn\include\sys\time.h ceval.h classobject.h \ + cobject.h compile.h complexobject.h pyconfig.h dictobject.h \ + fileobject.h floatobject.h funcobject.h import.h intobject.h \ + intrcheck.h listobject.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h object.h objimpl.h pydebug.h \ + pyerrors.h pyfpe.h pystate.h python.h pythonrun.h rangeobject.h \ + sliceobject.h stringobject.h sysmodule.h traceback.h tupleobject.h \ + yuv.h + +syslogmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h syslog.h sysmodule.h traceback.h tupleobject.h + +termios.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +threadmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h thread.h traceback.h tupleobject.h + +timemodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + mytime.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h \ + python.h pythonrun.h rangeobject.h sliceobject.h stringobject.h \ + sysmodule.h traceback.h tupleobject.h + +timingmodule.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h timing.h traceback.h tupleobject.h + +xxmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +yuvconvert.obj: yuv.h + +zlibmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +abstract.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +classobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h \ + structmember.h sysmodule.h traceback.h tupleobject.h + +cobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +complexobject.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + mymath.h myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +dictobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +fileobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h \ + structmember.h sysmodule.h traceback.h tupleobject.h + +floatobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h mymath.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +frameobject.obj: abstract.h ceval.h classobject.h cobject.h compile.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + frameobject.h funcobject.h import.h intobject.h intrcheck.h \ + listobject.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h object.h objimpl.h opcode.h \ + pydebug.h pyerrors.h pyfpe.h pystate.h python.h pythonrun.h \ + rangeobject.h sliceobject.h stringobject.h structmember.h \ + sysmodule.h traceback.h tupleobject.h + +funcobject.obj: abstract.h ceval.h classobject.h cobject.h compile.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h structmember.h sysmodule.h traceback.h \ + tupleobject.h + +intobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +listobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +longobject.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longintrepr.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h mymath.h myproto.h object.h objimpl.h \ + pydebug.h pyerrors.h pyfpe.h pystate.h python.h pythonrun.h \ + rangeobject.h sliceobject.h stringobject.h sysmodule.h traceback.h \ + tupleobject.h + +methodobject.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h token.h traceback.h tupleobject.h + +moduleobject.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +object.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +rangeobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +sliceobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +stringobject.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + mymath.h myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +tupleobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +typeobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +xxobject.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +acceler.obj: bitset.h pyconfig.h grammar.h mymalloc.h myproto.h node.h \ + parser.h pgenheaders.h pydebug.h token.h + +bitset.obj: bitset.h pyconfig.h mymalloc.h myproto.h pgenheaders.h pydebug.h + +firstsets.obj: bitset.h pyconfig.h grammar.h mymalloc.h myproto.h \ + pgenheaders.h pydebug.h token.h + +grammar.obj: bitset.h pyconfig.h grammar.h mymalloc.h myproto.h \ + pgenheaders.h pydebug.h token.h + +grammar1.obj: bitset.h pyconfig.h grammar.h mymalloc.h myproto.h \ + pgenheaders.h pydebug.h token.h + +intrcheck.obj: pyconfig.h intrcheck.h mymalloc.h myproto.h + +listnode.obj: pyconfig.h mymalloc.h myproto.h node.h pgenheaders.h pydebug.h \ + token.h + +metagrammar.obj: bitset.h pyconfig.h grammar.h metagrammar.h mymalloc.h \ + myproto.h pgen.h pgenheaders.h pydebug.h + +myreadline.obj: pyconfig.h intrcheck.h mymalloc.h myproto.h + +node.obj: pyconfig.h mymalloc.h myproto.h node.h pgenheaders.h pydebug.h + +parser.obj: bitset.h pyconfig.h errcode.h grammar.h mymalloc.h \ + myproto.h node.h parser.h pgenheaders.h pydebug.h token.h + +parsetok.obj: bitset.h pyconfig.h errcode.h grammar.h mymalloc.h myproto.h \ + node.h parser.h parsetok.h pgenheaders.h pydebug.h token.h \ + tokenizer.h + +pgen.obj: bitset.h pyconfig.h grammar.h metagrammar.h mymalloc.h \ + myproto.h node.h pgen.h pgenheaders.h pydebug.h token.h + +pgenmain.obj: bitset.h pyconfig.h grammar.h mymalloc.h myproto.h node.h \ + parsetok.h pgen.h pgenheaders.h pydebug.h + +printgrammar.obj: bitset.h pyconfig.h grammar.h mymalloc.h myproto.h \ + pgenheaders.h pydebug.h + +tokenizer.obj: pyconfig.h errcode.h mymalloc.h myproto.h pgenheaders.h \ + pydebug.h token.h tokenizer.h + +atof.obj: pyconfig.h + +bltinmodule.obj: abstract.h ceval.h classobject.h cobject.h compile.h \ + complexobject.h pyconfig.h dictobject.h eval.h fileobject.h \ + floatobject.h funcobject.h import.h intobject.h intrcheck.h \ + listobject.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h mymath.h myproto.h node.h object.h \ + objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +ceval.obj: abstract.h ceval.h classobject.h cobject.h compile.h \ + complexobject.h pyconfig.h dictobject.h eval.h fileobject.h \ + floatobject.h frameobject.h funcobject.h import.h intobject.h \ + intrcheck.h listobject.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h object.h objimpl.h opcode.h \ + pydebug.h pyerrors.h pyfpe.h pystate.h python.h pythonrun.h \ + rangeobject.h sliceobject.h stringobject.h sysmodule.h traceback.h \ + tupleobject.h + +compile.obj: abstract.h ceval.h classobject.h cobject.h compile.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h graminit.h import.h intobject.h intrcheck.h \ + listobject.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h node.h object.h objimpl.h \ + opcode.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h \ + structmember.h sysmodule.h token.h traceback.h tupleobject.h + +errors.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +fmod.obj: pyconfig.h mymath.h + +frozen.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +frozenmain.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +getargs.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +getcompiler.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +getcopyright.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +getmtime.obj: pyconfig.h + +getplatform.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +getversion.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h patchlevel.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +graminit.obj: bitset.h pyconfig.h grammar.h mymalloc.h myproto.h \ + pgenheaders.h pydebug.h + +hypot.obj: pyconfig.h mymath.h myproto.h + +import.obj: abstract.h ceval.h classobject.h cobject.h compile.h \ + complexobject.h pyconfig.h dictobject.h errcode.h eval.h \ + fileobject.h floatobject.h funcobject.h import.h importdl.h \ + intobject.h intrcheck.h listobject.h longobject.h marshal.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + node.h object.h objimpl.h osdefs.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h token.h traceback.h tupleobject.h + +importdl.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h importdl.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h osdefs.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +marshal.obj: abstract.h ceval.h classobject.h cobject.h compile.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longintrepr.h longobject.h marshal.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h object.h objimpl.h pydebug.h \ + pyerrors.h pyfpe.h pystate.h python.h pythonrun.h rangeobject.h \ + sliceobject.h stringobject.h sysmodule.h traceback.h tupleobject.h + +modsupport.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +mystrtoul.obj: pyconfig.h + +pyfpe.obj: pyconfig.h pyfpe.h + +pystate.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +pythonrun.obj: abstract.h bitset.h ceval.h classobject.h cobject.h \ + compile.h complexobject.h pyconfig.h dictobject.h errcode.h eval.h \ + fileobject.h floatobject.h funcobject.h grammar.h import.h \ + intobject.h intrcheck.h listobject.h longobject.h marshal.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + node.h object.h objimpl.h parsetok.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h sysmodule.h traceback.h tupleobject.h + +sigcheck.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h pydebug.h pyerrors.h pyfpe.h pystate.h python.h \ + pythonrun.h rangeobject.h sliceobject.h stringobject.h sysmodule.h \ + traceback.h tupleobject.h + +strdup.obj: pyconfig.h mymalloc.h myproto.h + +strtod.obj: pyconfig.h + +structmember.obj: abstract.h ceval.h classobject.h cobject.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + funcobject.h import.h intobject.h intrcheck.h listobject.h \ + longobject.h methodobject.h modsupport.h moduleobject.h mymalloc.h \ + myproto.h object.h objimpl.h pydebug.h pyerrors.h pyfpe.h \ + pystate.h python.h pythonrun.h rangeobject.h sliceobject.h \ + stringobject.h structmember.h sysmodule.h traceback.h \ + tupleobject.h + +sysmodule.obj: abstract.h ceval.h classobject.h cobject.h complexobject.h \ + pyconfig.h dictobject.h fileobject.h floatobject.h funcobject.h \ + import.h intobject.h intrcheck.h listobject.h longobject.h \ + methodobject.h modsupport.h moduleobject.h mymalloc.h myproto.h \ + object.h objimpl.h osdefs.h pydebug.h pyerrors.h pyfpe.h pystate.h \ + python.h pythonrun.h rangeobject.h sliceobject.h stringobject.h \ + sysmodule.h traceback.h tupleobject.h + +thread.obj: pyconfig.h thread.h + +traceback.obj: abstract.h ceval.h classobject.h cobject.h compile.h \ + complexobject.h pyconfig.h dictobject.h fileobject.h floatobject.h \ + frameobject.h funcobject.h import.h intobject.h intrcheck.h \ + listobject.h longobject.h methodobject.h modsupport.h \ + moduleobject.h mymalloc.h myproto.h object.h objimpl.h osdefs.h \ + pydebug.h pyerrors.h pyfpe.h pystate.h python.h pythonrun.h \ + rangeobject.h sliceobject.h stringobject.h structmember.h \ + sysmodule.h traceback.h tupleobject.h diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/pyconfig.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/pyconfig.h new file mode 100644 index 00000000..b834fa73 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/pyconfig.h @@ -0,0 +1,213 @@ +#ifndef Py_CONFIG_H +#define Py_CONFIG_H + +/********************************************************************** + * pyconfig.h. NOT Generated automatically by configure. + * + * This is a manually maintained version used for the IBM VisualAge + * C/C++ compiler on the OS/2 platform. It is a standard part of + * the Python distribution. + * + * FILESYSTEM DEFINES: + * The code specific to a particular way of naming files and + * directory paths should be wrapped around one of the following + * #defines: + * + * DOSFILESYS PCDOS-Style (for PCDOS, Windows and OS/2) + * MACFILESYS Macintosh-Style + * UNIXFILESYS Unix-Style + * AMIGAFILESYS AmigaDOS-Style + * + * Because of the different compilers and operating systems in + * use on the Intel platform, neither the compiler name nor + * the operating system name is sufficient. + * + * OS/2 DEFINES: + * The code specific to OS/2's Program API should be wrapped around + * + * __TOS_OS2__ Target Operating System, OS/2 + * + * Any code specific to the compiler itself should be wrapped with + * + * __IBMC__ IBM C Compiler + * __IBMCPP__ IBM C++ Compiler + * + * Note that since the VisualAge C/C++ compiler is also available + * for the Windows platform, it may be necessary to use both a + * __TOS_OS2__ and a __IBMC__ to select a very specific environment. + * + **********************************************************************/ + +/* + * Some systems require special declarations for data items imported + * or exported from dynamic link libraries. Note that the definition + * of DL_IMPORT covers both cases. Define USE_DL_IMPORT for the client + * of a DLL. Define USE_DL_EXPORT when making a DLL. + */ + +#include + +/* Configuration Options for Finding Modules */ +#define PREFIX "" +#define EXEC_PREFIX "" + +/* Provide a default library so writers of extension modules + * won't have to explicitly specify it anymore + */ +#pragma library("Python22.lib") + +/***************************************************/ +/* 32-Bit IBM VisualAge C/C++ v3.0 for OS/2 */ +/* (Convert Compiler Flags into Useful Switches) */ +/***************************************************/ +#define PLATFORM "os2" +#define COMPILER "[VisualAge C/C++]" +#define PYOS_OS2 /* Define Indicator of Operating System */ +#define PYCC_VACPP /* Define Indicator of C Compiler */ + + /* Platform Filesystem */ +#define PYTHONPATH ".;.\\lib;.\\lib\\plat-win;.\\lib\\lib-tk" +#define DOSFILESYS /* OS/2 Uses the DOS File Naming Conventions */ +/* #define IMPORT_8x3_NAMES (let's move up to long filenames) */ + + /* Platform CPU-Mode Dependencies */ +#define WORD_BIT 32 /* OS/2 is a 32-Bit Operating System */ +#define LONG_BIT 32 +#define SIZEOF_INT 4 /* Count of Bytes in an (int) */ +#define SIZEOF_LONG 4 /* Count of Bytes in a (long) */ +#define SIZEOF_VOID_P 4 /* Count of Bytes in a (void *) */ +/* #define HAVE_LONG_LONG 1 */ /* VAC++ does not support (long long) */ +/* #define SIZEOF_LONG_LONG 8 */ /* Count of Bytes in a (long long) */ + +/* unicode definines */ +#define Py_USING_UNICODE +#define PY_UNICODE_TYPE wchar_t +#define Py_UNICODE_SIZE SIZEOF_SHORT + +/* dynamic loading */ +#define HAVE_DYNAMIC_LOADING 1 + +/* Define if type char is unsigned and you are not using gcc. */ +#ifndef __CHAR_UNSIGNED__ +/* #undef __CHAR_UNSIGNED__ */ +#endif + +typedef int mode_t; +typedef int uid_t; +typedef int gid_t; +typedef int pid_t; + +#if defined(__MULTI__) /* If Compiler /Gt+ Multithread Option Enabled, */ + #define WITH_THREAD 1 /* Enable Threading Throughout Python */ + #define OS2_THREADS 1 /* And Use the OS/2 Flavor of Threads */ +/* #define _REENTRANT 1 */ /* Use thread-safe errno, h_errno, and other fns */ +#endif + + /* Compiler Runtime Library Capabilities */ +#include +#include +/* #undef BAD_STATIC_FORWARD */ /* if compiler botches static fwd decls */ + +#define STDC_HEADERS 1 /* VAC++ is an ANSI C Compiler */ +#define HAVE_LIMITS_H 1 /* #include */ +#define HAVE_STDLIB_H 1 /* #include */ +#define HAVE_HYPOT 1 /* hypot() */ +#define HAVE_PUTENV 1 /* putenv() */ +#define HAVE_STDDEF_H 1 /* #include */ +/* #define VA_LIST_IS_ARRAY 1 */ /* if va_list is an array of some kind */ + + /* Variable-Arguments/Prototypes */ +#define HAVE_PROTOTYPES 1 /* VAC++ supports C Function Prototypes */ +#define HAVE_STDARG_H 1 /* #include */ +#define HAVE_STDARG_PROTOTYPES 1 /* Our has prototypes */ + + /* String/Memory/Locale Operations */ +#define HAVE_STRDUP 1 /* strdup() */ +#define HAVE_MEMMOVE 1 /* memmove() */ +#define HAVE_STRERROR 1 /* strerror() */ +#define HAVE_SETLOCALE 1 /* setlocale() */ +#define HAVE_LOCALE_H 1 /* #include */ +#define MALLOC_ZERO_RETURNS_NULL 1 /* Our malloc(0) returns a NULL ptr */ + + /* Signal Handling */ +#define RETSIGTYPE void /* Return type of handlers (int or void) */ +#define HAVE_SIGNAL_H 1 /* #include */ +/* #undef WANT_SIGFPE_HANDLER */ /* Handle SIGFPE (see Include/pyfpe.h) */ +/* #define HAVE_ALARM 1 */ /* alarm() */ +/* #define HAVE_SIGINTERRUPT 1 */ /* siginterrupt() */ +/* #define HAVE_SIGRELSE 1 */ /* sigrelse() */ +#define DONT_HAVE_SIG_ALARM 1 +#define DONT_HAVE_SIG_PAUSE 1 + + /* Clock/Time Support */ +#define HAVE_FTIME 1 /* We have ftime() in */ +#define HAVE_CLOCK 1 /* clock() */ +#define HAVE_STRFTIME 1 /* strftime() */ +#define HAVE_STRPTIME 1 /* strptime() */ +#define HAVE_MKTIME 1 /* mktime() */ +#define HAVE_TZNAME 1 /* No tm_zone but do have tzname[] */ +#define HAVE_TIMES 1 /* #include */ +#define HAVE_SYS_UTIME_H 1 /* #include */ +/* #define HAVE_UTIME_H 1 */ /* #include */ +#define HAVE_SYS_TIME_H 1 /* #include */ +/* #define TM_IN_SYS_TIME 1 */ /* declares struct tm */ +#define HAVE_GETTIMEOFDAY 1 /* gettimeofday() */ +/* #define GETTIMEOFDAY_NO_TZ 1 */ /* gettimeofday() does not have 2nd arg */ +/* #define HAVE_TIMEGM 1 */ /* timegm() */ +#define TIME_WITH_SYS_TIME 1 /* Mix and */ +#define SYS_SELECT_WITH_SYS_TIME 1 /* Mix and */ +/* #define HAVE_ALTZONE 1 */ /* if defines altzone */ + + /* Network/Sockets Support */ +#define HAVE_SYS_SELECT_H 1 /* #include */ +#define BSD_SELECT 1 /* Use BSD versus OS/2 form of select() */ +#define HAVE_SELECT 1 /* select() */ +#define HAVE_GETPEERNAME 1 /* getpeername() */ +/* #undef HAVE_GETHOSTNAME_R 1 */ /* gethostname_r() */ + + /* File I/O */ +#define HAVE_DUP2 1 /* dup2() */ +#define HAVE_EXECV 1 /* execv() */ +#define HAVE_SETVBUF 1 /* setvbuf() */ +#define HAVE_GETCWD 1 /* getcwd() */ +#define HAVE_PIPE 1 /* pipe() [OS/2-specific code added] */ +#define HAVE_FCNTL_H 1 /* #include */ +/* #define HAVE_FLOCK 1 */ /* flock() */ +/* #define HAVE_TRUNCATE 1 */ /* truncate() */ +/* #define HAVE_FTRUNCATE 1 */ /* ftruncate() */ +/* #define HAVE_LSTAT 1 */ /* lstat() */ +/* #define HAVE_DIRENT_H 1 */ /* #include */ +/* #define HAVE_OPENDIR 1 */ /* opendir() */ + + /* Process Operations */ +#define HAVE_GETPID 1 /* getpid() */ +#define HAVE_SYSTEM 1 /* system() */ +#define HAVE_WAIT 1 /* wait() */ +#define HAVE_KILL 1 /* kill() [OS/2-specific code added] */ +#define HAVE_POPEN 1 /* popen() [OS/2-specific code added] */ +/* #define HAVE_GETPPID 1 */ /* getppid() */ +/* #define HAVE_WAITPID 1 */ /* waitpid() */ +/* #define HAVE_FORK 1 */ /* fork() */ + + /* User/Group ID Queries */ +/* #define HAVE_GETEGID 1 */ +/* #define HAVE_GETEUID 1 */ +/* #define HAVE_GETGID 1 */ +/* #define HAVE_GETUID 1 */ + + /* Unix-Specific */ +/* #define HAVE_SYS_UN_H 1 /* #include */ +/* #define HAVE_SYS_UTSNAME_H 1 */ /* #include */ +/* #define HAVE_SYS_WAIT_H 1 */ /* #include */ +/* #define HAVE_UNISTD_H 1 */ /* #include */ +/* #define HAVE_UNAME 1 */ /* uname () */ + +/* Define if you want documentation strings in extension modules */ +#define WITH_DOC_STRINGS 1 + +#ifdef USE_DL_EXPORT + #define DL_IMPORT(RTYPE) RTYPE _System +#endif + +#endif /* !Py_CONFIG_H */ + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/python.def b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/python.def new file mode 100644 index 00000000..b75cb151 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/python.def @@ -0,0 +1,480 @@ +LIBRARY PYTHON22 INITINSTANCE TERMINSTANCE +DESCRIPTION 'Python 2.2 Core DLL' +PROTMODE +DATA MULTIPLE NONSHARED + +EXPORTS + ; Data + PyCFunction_Type + PyCObject_Type + PyClass_Type + PyCode_Type + PyComplex_Type + PyDict_Type + PyExc_ArithmeticError + PyExc_AssertionError + PyExc_AttributeError + PyExc_EOFError + PyExc_EnvironmentError + PyExc_Exception + PyExc_FloatingPointError + PyExc_IOError + PyExc_ImportError + PyExc_IndexError + PyExc_KeyError + PyExc_KeyboardInterrupt + PyExc_LookupError + PyExc_MemoryError + PyExc_MemoryErrorInst + PyExc_NameError + PyExc_OSError + PyExc_OverflowError + PyExc_RuntimeError + PyExc_StandardError + PyExc_SyntaxError + PyExc_SystemError + PyExc_SystemExit + PyExc_TypeError + PyExc_ValueError + PyExc_ZeroDivisionError + PyFile_Type + PyFloat_Type + PyFrame_Type + PyFunction_Type + PyImport_FrozenModules + PyImport_Inittab + PyInstance_Type + PyInt_Type + PyList_Type + PyLong_Type + PyMethod_Type + PyModule_Type + PyOS_InputHook + PyOS_ReadlineFunctionPointer + PyRange_Type + PySlice_Type + PyString_Type + PyTraceBack_Type + PyTuple_Type + PyType_Type + Py_DebugFlag + Py_FrozenFlag + Py_InteractiveFlag + Py_NoSiteFlag + Py_OptimizeFlag + Py_TabcheckFlag + Py_UseClassExceptionsFlag + Py_VerboseFlag + _PyImport_Filetab + _PyImport_Inittab + _PyParser_Grammar + _PyParser_TokenNames + _PyThread_Started + _Py_EllipsisObject + _Py_NoneStruct + _Py_PackageContext + _Py_TrueStruct + _Py_ZeroStruct + _Py_abstract_hack + _Py_cobject_hack + _Py_re_syntax + _Py_re_syntax_table + + ; Code + PyArg_Parse + PyArg_ParseTuple + PyArg_ParseTupleAndKeywords + PyArg_VaParse + PyCFunction_Fini + PyCFunction_GetFlags + PyCFunction_GetFunction + PyCFunction_GetSelf + PyCFunction_New + PyCObject_AsVoidPtr + PyCObject_FromVoidPtrAndDesc + PyCObject_FromVoidPtr + PyCObject_GetDesc + PyCObject_Import + PyCallable_Check + PyClass_IsSubclass + PyClass_New + PyCode_Addr2Line + PyCode_New + PyComplex_AsCComplex + PyComplex_FromCComplex + PyComplex_FromDoubles + PyComplex_ImagAsDouble + PyComplex_RealAsDouble + PyDict_Clear + PyDict_DelItem + PyDict_DelItemString + PyDict_GetItem + PyDict_GetItemString + PyDict_Items + PyDict_Keys + PyDict_New + PyDict_Next + PyDict_SetItem + PyDict_SetItemString + PyDict_Size + PyDict_Values + PyErr_BadArgument + PyErr_BadInternalCall + PyErr_CheckSignals + PyErr_Clear + PyErr_ExceptionMatches + PyErr_Fetch + PyErr_Format + PyErr_GivenExceptionMatches + PyErr_NewException + PyErr_NoMemory + PyErr_NormalizeException + PyErr_Occurred + PyErr_Print + PyErr_PrintEx + PyErr_Restore + PyErr_SetFromErrno + PyErr_SetFromErrnoWithFilename + PyErr_SetInterrupt + PyErr_SetNone + PyErr_SetObject + PyErr_SetString + PyEval_AcquireLock + PyEval_AcquireThread + PyEval_CallFunction + PyEval_CallMethod + PyEval_CallObject + PyEval_CallObjectWithKeywords + PyEval_EvalCode + PyEval_GetBuiltins + PyEval_GetFrame + PyEval_GetGlobals + PyEval_GetLocals + PyEval_GetRestricted + PyEval_InitThreads + PyEval_ReleaseLock + PyEval_ReleaseThread + PyEval_RestoreThread + PyEval_SaveThread + PyFile_AsFile + PyFile_FromFile + PyFile_FromString + PyFile_GetLine + PyFile_Name + PyFile_SetBufSize + PyFile_SoftSpace + PyFile_WriteObject + PyFile_WriteString + PyFloat_AsDouble + PyFloat_AsString + PyFloat_Fini + PyFloat_FromDouble + PyFrame_BlockPop + PyFrame_BlockSetup + PyFrame_FastToLocals + PyFrame_Fini + PyFrame_LocalsToFast + PyFrame_New + PyFunction_GetCode + PyFunction_GetDefaults + PyFunction_GetGlobals + PyFunction_New + PyFunction_SetDefaults + PyGrammar_AddAccelerators + PyGrammar_FindDFA + PyGrammar_LabelRepr + PyGrammar_RemoveAccelerators + PyImport_AddModule + PyImport_AppendInittab + PyImport_Cleanup + PyImport_ExecCodeModule + PyImport_ExecCodeModuleEx + PyImport_ExtendInittab + PyImport_GetMagicNumber + PyImport_GetModuleDict + PyImport_Import + PyImport_ImportFrozenModule + PyImport_ImportModule + PyImport_ImportModuleEx + PyImport_ReloadModule + PyInstance_DoBinOp + PyInstance_New + PyInt_AsLong + PyInt_Fini + PyInt_FromLong + PyInt_GetMax + PyInterpreterState_Clear + PyInterpreterState_Delete + PyInterpreterState_New + PyList_Append + PyList_AsTuple + PyList_GetItem + PyList_GetSlice + PyList_Insert + PyList_New + PyList_Reverse + PyList_SetItem + PyList_SetSlice + PyList_Size + PyList_Sort + PyLong_AsDouble + PyLong_AsLong +; PyLong_AsLongLong + PyLong_AsUnsignedLong +; PyLong_AsUnsignedLongLong + PyLong_AsVoidPtr + PyLong_FromDouble + PyLong_FromLong +; PyLong_FromLongLong + PyLong_FromString + PyLong_FromUnsignedLong +; PyLong_FromUnsignedLongLong + PyLong_FromVoidPtr + PyMapping_Check + PyMapping_GetItemString + PyMapping_HasKey + PyMapping_HasKeyString + PyMapping_Length + PyMapping_SetItemString + PyMarshal_Init + PyMarshal_ReadLongFromFile + PyMarshal_ReadObjectFromFile + PyMarshal_ReadObjectFromString + PyMarshal_WriteLongToFile + PyMarshal_WriteObjectToFile + PyMarshal_WriteObjectToString + PyMem_Free + PyMem_Malloc + PyMem_Realloc + PyMember_Get + PyMember_Set + PyMethod_Class + PyMethod_Fini + PyMethod_Function + PyMethod_New + PyMethod_Self + PyModule_GetDict + PyModule_GetName + PyModule_New + PyNode_AddChild + PyNode_Compile + PyNode_Free +; PyNode_ListTree + PyNode_New + PyNumber_Absolute + PyNumber_Add + PyNumber_And + PyNumber_Check + PyNumber_Coerce + PyNumber_CoerceEx + PyNumber_Divide + PyNumber_Divmod + PyNumber_Float + PyNumber_Int + PyNumber_Invert + PyNumber_Long + PyNumber_Lshift + PyNumber_Multiply + PyNumber_Negative + PyNumber_Or + PyNumber_Positive + PyNumber_Power + PyNumber_Remainder + PyNumber_Rshift + PyNumber_Subtract + PyNumber_Xor + PyOS_AfterFork + PyOS_FiniInterrupts + PyOS_GetLastModificationTime + PyOS_InitInterrupts + PyOS_InterruptOccurred + PyOS_Readline + PyOS_StdioReadline + PyOS_strtol + PyOS_strtoul + PyObject_CallFunction + PyObject_CallMethod + PyObject_CallObject + PyObject_Cmp + PyObject_Compare + PyObject_DelItem + PyObject_GetAttr + PyObject_GetAttrString + PyObject_GetItem + PyObject_HasAttr + PyObject_HasAttrString + PyObject_Hash + PyObject_IsTrue + PyObject_Length + PyObject_Not + PyObject_Print + PyObject_Repr + PyObject_SetAttr + PyObject_SetAttrString + PyObject_SetItem + PyObject_Str + PyObject_Type + PyParser_AddToken + PyParser_Delete + PyParser_New + PyParser_ParseFile + PyParser_ParseString + PyParser_SimpleParseFile + PyParser_SimpleParseString + PyRange_New + PyRun_AnyFile + PyRun_File + PyRun_InteractiveLoop + PyRun_InteractiveOne + PyRun_SimpleFile + PyRun_SimpleString + PyRun_String + PySequence_Check + PySequence_Concat + PySequence_Contains + PySequence_Count + PySequence_DelItem + PySequence_DelSlice + PySequence_GetItem + PySequence_GetSlice + PySequence_In + PySequence_Index + PySequence_Length + PySequence_List + PySequence_Repeat + PySequence_SetItem + PySequence_SetSlice + PySequence_Tuple + PySlice_GetIndices + PySlice_New + PyString_AsString + PyString_Concat + PyString_ConcatAndDel + PyString_Fini + PyString_Format + PyString_FromString + PyString_FromStringAndSize + PyString_InternFromString + PyString_InternInPlace + PyString_Size + PySys_GetFile + PySys_GetObject + PySys_SetArgv + PySys_SetObject + PySys_SetPath + PySys_WriteStderr + PySys_WriteStdout + PyThreadState_Clear + PyThreadState_Delete + PyThreadState_Get + PyThreadState_GetDict + PyThreadState_New + PyThreadState_Swap + PyThread__exit_thread + PyThread_acquire_lock + PyThread_allocate_lock + PyThread_allocate_sema + PyThread_down_sema + PyThread_exit_thread + PyThread_free_lock + PyThread_free_sema + PyThread_get_thread_ident + PyThread_init_thread + PyThread_release_lock + PyThread_start_new_thread + PyThread_up_sema + PyToken_OneChar + PyToken_TwoChars + PyTokenizer_Free + PyTokenizer_FromFile + PyTokenizer_FromString + PyTokenizer_Get + PyTraceBack_Here + PyTraceBack_Print + PyTuple_Fini + PyTuple_GetItem + PyTuple_GetSlice + PyTuple_New + PyTuple_SetItem + PyTuple_Size + Py_AddPendingCall + Py_AtExit + Py_BuildValue + Py_CompileString + Py_EndInterpreter + Py_Exit + Py_FatalError + Py_FdIsInteractive + Py_Finalize + Py_FindMethod + Py_FindMethodInChain + Py_FlushLine + Py_Free + Py_GetArgcArgv + Py_GetBuildInfo + Py_GetCompiler + Py_GetCopyright + Py_GetExecPrefix + Py_GetPath + Py_GetPlatform + Py_GetPrefix + Py_GetProgramFullPath + Py_GetProgramName + Py_GetPythonHome + Py_GetVersion + Py_InitModule4 + Py_Initialize + Py_IsInitialized + Py_Main + Py_MakePendingCalls + Py_Malloc + Py_NewInterpreter + Py_Realloc + Py_ReprEnter + Py_ReprLeave + Py_SetProgramName + Py_SetPythonHome + Py_VaBuildValue + _PyBuiltin_Fini_1 + _PyBuiltin_Fini_2 + _PyBuiltin_Init_1 + _PyBuiltin_Init_2 + _PyImport_FindExtension + _PyImport_Fini + _PyImport_FixupExtension + _PyImport_Init + _PyImport_LoadDynamicModule + _PyLong_New + _PyModule_Clear + _PyObject_New + _PyObject_NewVar + _PyString_Resize + _PySys_Init + _PyTuple_Resize + _Py_MD5Final + _Py_MD5Init + _Py_MD5Update +; _Py_addbit + _Py_c_diff + _Py_c_neg + _Py_c_pow + _Py_c_prod + _Py_c_quot + _Py_c_sum +; _Py_delbitset +; _Py_mergebitset +; _Py_meta_grammar +; _Py_newbitset + _Py_re_compile_fastmap + _Py_re_compile_initialize + _Py_re_compile_pattern + _Py_re_match + _Py_re_search + _Py_re_set_syntax +; _Py_samebitset + PyBuffer_Type + PyBuffer_FromObject + PyBuffer_FromMemory + PyBuffer_FromReadWriteMemory + PyBuffer_New + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/readme.txt b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/readme.txt new file mode 100644 index 00000000..6fa178eb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/os2vacpp/readme.txt @@ -0,0 +1,119 @@ +IBM VisualAge C/C++ for OS/2 +============================ + +To build Python for OS/2, change into ./os2vacpp and issue an 'NMAKE' +command. This will build a PYTHON15.DLL containing the set of Python +modules listed in config.c and a small PYTHON.EXE to start the +interpreter. + +By changing the C compiler flag /Gd- in the makefile to /Gd+, you can +reduce the size of these by causing Python to dynamically link to the +C runtime DLLs instead of including their bulk in your binaries. +However, this means that any system on which you run Python must have +the VAC++ compiler installed in order to have those DLLs available. + +During the build process you may see a couple of harmless warnings: + + From the C Compiler, "No function prototype given for XXX", which + comes from the use of K&R parameters within Python for portability. + + From the ILIB librarian, "Module Not Found (XXX)", which comes + from its attempt to perform the (-+) operation, which removes and + then adds a .OBJ to the library. The first time a build is done, + it obviously cannot remove what is not yet built. + +This build includes support for most Python functionality as well as +TCP/IP sockets. It omits the Posix ability to 'fork' a process but +supports threads using OS/2 native capabilities. I have tried to +support everything possible but here are a few usage notes. + + +-- os.popen() Usage Warnings + +With respect to my implementation of popen() under OS/2: + + import os + + fd = os.popen("pkzip.exe -@ junk.zip", 'wb') + fd.write("file1.txt\n") + fd.write("file2.txt\n") + fd.write("file3.txt\n") + fd.write("\x1a") # Should Not Be Necessary But Is + fd.close() + +There is a bug, either in the VAC++ compiler or OS/2 itself, where the +simple closure of the write-side of a pipe -to- a process does not +send an EOF to that process. I find I must explicitly write a +control-Z (EOF) before closing the pipe. This is not a problem when +using popen() in read mode. + +One other slight difference with my popen() is that I return None +from the close(), instead of the Unix convention of the return code +of the spawned program. I could find no easy way to do this under +OS/2. + + +-- BEGINLIBPATH/ENDLIBPATH + +With respect to environment variables, this OS/2 port supports the +special-to-OS/2 magic names of 'BEGINLIBPATH' and 'ENDLIBPATH' to +control where to load conventional DLLs from. Those names are +intercepted and converted to calls on the OS/2 kernel APIs and +are inherited by child processes, whether Python-based or not. + +A few new attributes have been added to the os module: + + os.meminstalled # Count of Bytes of RAM Installed on Machine + os.memkernel # Count of Bytes of RAM Reserved (Non-Swappable) + os.memvirtual # Count of Bytes of Virtual RAM Possible + os.timeslice # Duration of Scheduler Timeslice, in Milliseconds + os.maxpathlen # Maximum Length of a Path Specification, in chars + os.maxnamelen # Maximum Length of a Single Dir/File Name, in chars + os.version # Version of OS/2 Being Run e.g. "4.00" + os.revision # Revision of OS/2 Being Run (usually zero) + os.bootdrive # Drive that System Booted From e.g. "C:" + # (useful to find the CONFIG.SYS used to boot with) + + +-- Using Python as the Default OS/2 Batch Language + +Note that OS/2 supports the Unix technique of putting the special +comment line at the time of scripts e.g. "#!/usr/bin/python" in +a different syntactic form. To do this, put your script into a file +with a .CMD extension and added 'extproc' to the top as follows: + + extproc C:\Python\Python.exe -x + import os + print "Hello from Python" + +The '-x' option tells Python to skip the first line of the file +while processing the rest as normal Python source. + + +-- Suggested Environment Variable Setup + +With respect to the environment variables for Python, I use the +following setup: + + Set PYTHONHOME=E:\Tau\Projects\Python;D:\DLLs + Set PYTHONPATH=.;E:\Tau\Projects\Python\Lib; \ + E:\Tau\Projects\Python\Lib\plat-win + +The EXEC_PREFIX (optional second pathspec on PYTHONHOME) is where +you put any Python extension DLLs you may create/obtain. There +are none provided with this release. + + +-- Contact Info + +Jeff Rush is no longer supporting the VACPP port :-( + +I don't have the VACPP compiler, so can't reliably maintain this port. + +Anyone with VACPP who can contribute patches to keep this port buildable +should upload them to the Python Patch Manager at Sourceforge and +assign them to me for review/checkin. + +Andrew MacIntyre +aimacintyre at users.sourceforge.net +August 18, 2002. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/py.ico b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/py.ico new file mode 100644 index 0000000000000000000000000000000000000000..f714eea446e4b6bd28f9e0f1c8f75f6c81fba9c6 GIT binary patch literal 766 zcmaJm8xh$y8nAma-W z^$0#H>2kty>X)hwIrlE-S<5+vz&A`n&h*v18|y+>sjix$aZle8p4@4^r5n6eZnc>@ zG>`3+P&u~b2-=|v7s{bGs2nePG0P+8gx8LOTO>dQCQJqF(DmA(7NnHmI`9r7=bX7` z!W?&ubNbFr2j~zfBlk1694)bC>aTGXW{#1EPUo#Q3jUW;)Gsjwb=$3Xc0uvBe*c&M z&1daA>)3`jT()1f=0DJg9L%!}boam$VF^QEz=~*YKd>1d7=z`x4td24G7-ucUwk-LUlMo|)S>aP%-HcSLQWP)fJ3>Qx5~j;^zT?g4lvGQtrUup;u!2e!jgxH*Q;-1_^>`fF$XC9#49 b{Z1!vP?klQ!5>}z&#rx-Gx`)Kw9yD5f& literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pycon.ico b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pycon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a54682d91c85164fe9c910f5831935bd7fd6983c GIT binary patch literal 766 zcmah{t!~3G7`)7rqGq5)qYE62WM;HS*&~dF6j&+f*hV`PDa@#R11{P?rM^Pp9-*L8 zc3*y6cbe8q&Y$nQvtuOz5?*;egW&rLxMjU3bMk>qaLp00^!|pWNypm0aO9M<@ZQsC zyBlpQGm@9IWVnR1jPL{;o+B)&U%1VHgJ@<(Ogp_2nK`YCUQga!4d_PeUSrirgMmR$ zUKJav88uc=PIl=1%$NdAt<0bu!9ehxF~%`4uGd-{Q)f;M`9do;T+s9-5wz+Wx(?^* z8%kq1AD}>mpl{ai>mt_v!~d&)<+)VFdGLhnb^A+=u{{G&?vCxQ-5%NgoR`4z 64 +# error "PY_MICRO_VERSION > 64" +#endif +#if PY_RELEASE_LEVEL > 99 +# error "PY_RELEASE_LEVEL > 99" +#endif +#if PY_RELEASE_SERIAL > 9 +# error "PY_RELEASE_SERIAL > 9" +#endif +#define PYVERSION64 PY_MAJOR_VERSION, PY_MINOR_VERSION, FIELD3, PYTHON_API_VERSION + +// String Tables +STRINGTABLE DISCARDABLE +BEGIN + 1000, MS_DLL_ID +END + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION PYVERSION64 + PRODUCTVERSION PYVERSION64 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", "PythonLabs at Zope Corporation\0" + VALUE "FileDescription", "Python Core\0" + VALUE "FileVersion", PYTHON_VERSION + VALUE "InternalName", "Python DLL\0" + VALUE "LegalCopyright", "Copyright © 2001-2003 Python Software Foundation. Copyright © 2000 BeOpen.com. Copyright © 1995-2001 CNRI. Copyright © 1991-1995 SMC.\0" + VALUE "OriginalFilename", PYTHON_DLL_NAME "\0" + VALUE "ProductName", "Python\0" + VALUE "ProductVersion", PYTHON_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pythonnt_rc.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pythonnt_rc.h new file mode 100644 index 00000000..57b4d393 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pythonnt_rc.h @@ -0,0 +1,4 @@ +/* This file created by make_versioninfo.exe */ +#define FIELD3 3150 +#define MS_DLL_ID "2.3" +#define PYTHON_DLL_NAME "python23.dll" diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pythonnt_rc_d.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pythonnt_rc_d.h new file mode 100644 index 00000000..57b4d393 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/pythonnt_rc_d.h @@ -0,0 +1,4 @@ +/* This file created by make_versioninfo.exe */ +#define FIELD3 3150 +#define MS_DLL_ID "2.3" +#define PYTHON_DLL_NAME "python23.dll" diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/readme.txt b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/readme.txt new file mode 100644 index 00000000..08266990 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/readme.txt @@ -0,0 +1,95 @@ +Welcome to the "PC" subdirectory of the Python distribution +*********************************************************** + +*** Note: the project files for MS VC++ 6.0 are now in the +*** PCbuild directory. See the file readme.txt there for build +*** instructions. There is some information below that might +*** still be relevant. + +This "PC" subdirectory contains complete project files to make +several older PC ports of Python, as well as all the PC-specific +Python source files. It should be located in the root of the +Python distribution, and there should be directories "Modules", +"Objects", "Python", etc. in the parent directory of this "PC" +subdirectory. Be sure to read the documentation in the Python +distribution. + +Python requires library files such as string.py to be available in +one or more library directories. The search path of libraries is +set up when Python starts. To see the current Python library search +path, start Python and enter "import sys" and "print sys.path". + +All PC ports use this scheme to try to set up a module search path: + + 1) The script location; the current directory without script. + 2) The PYTHONPATH variable, if set. + 3) For Win32 platforms (NT/95), paths specified in the Registry. + 4) Default directories lib, lib/win, lib/test, lib/tkinter; + these are searched relative to the environment variable + PYTHONHOME, if set, or relative to the executable and its + ancestors, if a landmark file (Lib/string.py) is found , + or the current directory (not useful). + 5) The directory containing the executable. + +The best installation strategy is to put the Python executable (and +DLL, for Win32 platforms) in some convenient directory such as +C:/python, and copy all library files and subdirectories (using XCOPY) +to C:/python/lib. Then you don't need to set PYTHONPATH. Otherwise, +set the environment variable PYTHONPATH to your Python search path. +For example, + set PYTHONPATH=.;d:\python\lib;d:\python\lib\win;d:\python\lib\dos-8x3 + +There are several add-in modules to build Python programs which use +the native Windows operating environment. The ports here just make +"QuickWin" and DOS Python versions which support a character-mode +(console) environment. Look in www.python.org for Tkinter, PythonWin, +WPY and wxPython. + +To make a Python port, start the Integrated Development Environment +(IDE) of your compiler, and read in the native "project file" +(or makefile) provided. This will enable you to change any source +files or build settings so you can make custom builds. + +pyconfig.h An important configuration file specific to PC's. + +config.c The list of C modules to include in the Python PC + version. Manually edit this file to add or + remove Python modules. + +testpy.py A Python test program. Run this to test your + Python port. It should produce copious output, + ending in a report on how many tests were OK, how many + failed, and how many were skipped. Don't worry about + skipped tests (these test unavailable optional features). + + +Additional files and subdirectories for 32-bit Windows +====================================================== + +python_nt.rc Resource compiler input for python15.dll. + +dl_nt.c, import_nt.c + Additional sources used for 32-bit Windows features. + +getpathp.c Default sys.path calculations (for all PC platforms). + +dllbase_nt.txt A (manually maintained) list of base addresses for + various DLLs, to avoid run-time relocation. + +example_nt A subdirectory showing how to build an extension as a + DLL. + + +IBM VisualAge C/C++ for OS/2 +============================ + +See os2vacpp/readme.txt. This platform is supported by Jeff Rush. + + +Note for Windows 3.x and DOS users +================================== + +Neither Windows 3.x nor DOS is supported any more. The last Python +version that supported these was Python 1.5.2; the support files were +present in Python 2.0 but weren't updated, and it is not our intention +to support these platforms for Python 2.x. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/testpy.py b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/testpy.py new file mode 100644 index 00000000..ef8ba7ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/testpy.py @@ -0,0 +1,32 @@ +import sys + +# This is a test module for Python. It looks in the standard +# places for various *.py files. If these are moved, you must +# change this module too. + +try: + import string +except: + print """Could not import the standard "string" module. +Please check your PYTHONPATH environment variable.""" + sys.exit(1) + +try: + import regex_syntax +except: + print """Could not import the standard "regex_syntax" module. If this is +a PC, you should add the dos_8x3 directory to your PYTHONPATH.""" + sys.exit(1) + +import os + +for dir in sys.path: + file = os.path.join(dir, "string.py") + if os.path.isfile(file): + test = os.path.join(dir, "test") + if os.path.isdir(test): + # Add the "test" directory to PYTHONPATH. + sys.path = sys.path + [test] + +import regrtest # Standard Python tester. +regrtest.main() diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/w9xpopen.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/w9xpopen.c new file mode 100644 index 00000000..8f074c55 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PC/w9xpopen.c @@ -0,0 +1,112 @@ +/* + * w9xpopen.c + * + * Serves as an intermediate stub Win32 console application to + * avoid a hanging pipe when redirecting 16-bit console based + * programs (including MS-DOS console based programs and batch + * files) on Window 95 and Windows 98. + * + * This program is to be launched with redirected standard + * handles. It will launch the command line specified 16-bit + * console based application in the same console, forwarding + * it's own redirected standard handles to the 16-bit child. + + * AKA solution to the problem described in KB: Q150956. + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include /* for malloc and its friends */ + +const char *usage = +"This program is used by Python's os.popen function\n" +"to work around a limitation in Windows 95/98. It is\n" +"not designed to be used as a stand-alone program."; + +int main(int argc, char *argv[]) +{ + BOOL bRet; + STARTUPINFO si; + PROCESS_INFORMATION pi; + DWORD exit_code=0; + int cmdlen = 0; + int i; + char *cmdline, *cmdlinefill; + + if (argc < 2) { + if (GetFileType(GetStdHandle(STD_INPUT_HANDLE))==FILE_TYPE_CHAR) + /* Attached to a console, and therefore not executed by Python + Display a message box for the inquisitive user + */ + MessageBox(NULL, usage, argv[0], MB_OK); + else { + /* Eeek - executed by Python, but args are screwed! + Write an error message to stdout so there is at + least some clue for the end user when it appears + in their output. + A message box would be hidden and blocks the app. + */ + fprintf(stdout, "Internal popen error - no args specified\n%s\n", usage); + } + return 1; + } + /* Build up the command-line from the args. + Args with a space are quoted, existing quotes are escaped. + To keep things simple calculating the buffer size, we assume + every character is a quote - ie, we allocate double what we need + in the worst case. As this is only double the command line passed + to us, there is a good chance this is reasonably small, so the total + allocation will almost always be < 512 bytes. + */ + for (i=1;i + * + * Copyright (c) 1999 Toby Dickenson + * + * Permission to use this software in any way is granted without + * fee, provided that the copyright notice above appears in all + * copies. This software is provided "as is" without any warranty. + */ + +/* Modified by Guido van Rossum */ +/* Beep added by Mark Hammond */ +/* Win9X Beep and platform identification added by Uncle Timmy */ + +/* Example: + + import winsound + import time + + # Play wav file + winsound.PlaySound('c:/windows/media/Chord.wav', winsound.SND_FILENAME) + + # Play sound from control panel settings + winsound.PlaySound('SystemQuestion', winsound.SND_ALIAS) + + # Play wav file from memory + data=open('c:/windows/media/Chimes.wav',"rb").read() + winsound.PlaySound(data, winsound.SND_MEMORY) + + # Start playing the first bit of wav file asynchronously + winsound.PlaySound('c:/windows/media/Chord.wav', + winsound.SND_FILENAME|winsound.SND_ASYNC) + # But dont let it go for too long... + time.sleep(0.1) + # ...Before stopping it + winsound.PlaySound(None, 0) +*/ + +#include +#include +#include /* port functions on Win9x */ +#include + +PyDoc_STRVAR(sound_playsound_doc, +"PlaySound(sound, flags) - a wrapper around the Windows PlaySound API\n" +"\n" +"The sound argument can be a filename, data, or None.\n" +"For flag values, ored together, see module documentation."); + +PyDoc_STRVAR(sound_beep_doc, +"Beep(frequency, duration) - a wrapper around the Windows Beep API\n" +"\n" +"The frequency argument specifies frequency, in hertz, of the sound.\n" +"This parameter must be in the range 37 through 32,767.\n" +"The duration argument specifies the number of milliseconds.\n" +"On WinNT and 2000, the platform Beep API is used directly. Else funky\n" +"code doing direct port manipulation is used; it's unknown whether that\n" +"will work on all systems."); + +PyDoc_STRVAR(sound_msgbeep_doc, +"MessageBeep(x) - call Windows MessageBeep(x). x defaults to MB_OK."); + +PyDoc_STRVAR(sound_module_doc, +"PlaySound(sound, flags) - play a sound\n" +"SND_FILENAME - sound is a wav file name\n" +"SND_ALIAS - sound is a registry sound association name\n" +"SND_LOOP - Play the sound repeatedly; must also specify SND_ASYNC\n" +"SND_MEMORY - sound is a memory image of a wav file\n" +"SND_PURGE - stop all instances of the specified sound\n" +"SND_ASYNC - PlaySound returns immediately\n" +"SND_NODEFAULT - Do not play a default beep if the sound can not be found\n" +"SND_NOSTOP - Do not interrupt any sounds currently playing\n" // Raising RuntimeError if needed +"SND_NOWAIT - Return immediately if the sound driver is busy\n" // Without any errors +"\n" +"Beep(frequency, duration) - Make a beep through the PC speaker."); + +static PyObject * +sound_playsound(PyObject *s, PyObject *args) +{ + const char *sound; + int flags; + int length; + int ok; + + if(!PyArg_ParseTuple(args,"z#i:PlaySound",&sound,&length,&flags)) { + return NULL; + } + + if(flags&SND_ASYNC && flags &SND_MEMORY) { + /* Sidestep reference counting headache; unfortunately this also + prevent SND_LOOP from memory. */ + PyErr_SetString(PyExc_RuntimeError,"Cannot play asynchronously from memory"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + ok = PlaySound(sound,NULL,flags); + Py_END_ALLOW_THREADS + if(!ok) + { + PyErr_SetString(PyExc_RuntimeError,"Failed to play sound"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +enum OSType {Win9X, WinNT2000}; +static enum OSType whichOS; /* set by module init */ + +static PyObject * +sound_beep(PyObject *self, PyObject *args) +{ + int freq; + int dur; + + if (!PyArg_ParseTuple(args, "ii:Beep", &freq, &dur)) + return NULL; + + if (freq < 37 || freq > 32767) { + PyErr_SetString(PyExc_ValueError, + "frequency must be in 37 thru 32767"); + return NULL; + } + + /* On NT and 2000, the SDK Beep() function does the whole job. + * But while Beep() exists before NT, it ignores its arguments and + * plays the system default sound. Sheesh ... + * The Win9X code is mondo bizarre. I (Tim) pieced it together from + * crap all over the web. The original IBM PC used some particular + * pieces of hardware (Intel 8255 and 8254 chips) hardwired to + * particular port addresses and running at particular clock speeds, + * and the poor sound card folks have been forced to emulate that in + * all particulars ever since. But NT and 2000 don't support port + * manipulation. Don't know about WinME; guessing it's like 98. + */ + + if (whichOS == WinNT2000) { + BOOL ok; + Py_BEGIN_ALLOW_THREADS + ok = Beep(freq, dur); + Py_END_ALLOW_THREADS + if (!ok) { + PyErr_SetString(PyExc_RuntimeError,"Failed to beep"); + return NULL; + } + } + else if (whichOS == Win9X) { + int speaker_state; + /* Force timer into oscillator mode via timer control port. */ + _outp(0x43, 0xb6); + /* Compute ratio of ancient hardcoded timer frequency to + * frequency we want. Then feed that ratio (lowest byte + * first) into timer data port. + */ + freq = 1193180 / freq; + _outp(0x42, freq & 0xff); + _outp(0x42, (freq >> 8) & 0xff); + /* Get speaker control state. */ + speaker_state = _inp(0x61); + /* Turn the speaker on (bit 1) + * and drive speaker from timer (bit 0). + */ + _outp(0x61, speaker_state | 0x3); + /* Let it blast in peace for the duration. */ + Py_BEGIN_ALLOW_THREADS + Sleep(dur); + Py_END_ALLOW_THREADS + /* Restore speaker control to original state. */ + _outp(0x61, speaker_state); + } + else { + assert(!"winsound's whichOS has insane value"); + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +sound_msgbeep(PyObject *self, PyObject *args) +{ + int x = MB_OK; + if (!PyArg_ParseTuple(args, "|i:MessageBeep", &x)) + return NULL; + MessageBeep(x); + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef sound_methods[] = +{ + {"PlaySound", sound_playsound, METH_VARARGS, sound_playsound_doc}, + {"Beep", sound_beep, METH_VARARGS, sound_beep_doc}, + {"MessageBeep", sound_msgbeep, METH_VARARGS, sound_msgbeep_doc}, + {NULL, NULL} +}; + +static void +add_define(PyObject *dict, const char *key, long value) +{ + PyObject *k=PyString_FromString(key); + PyObject *v=PyLong_FromLong(value); + if(v&&k) + { + PyDict_SetItem(dict,k,v); + } + Py_XDECREF(k); + Py_XDECREF(v); +} + +#define ADD_DEFINE(tok) add_define(dict,#tok,tok) + +PyMODINIT_FUNC +initwinsound(void) +{ + OSVERSIONINFO version; + + PyObject *module = Py_InitModule3("winsound", + sound_methods, + sound_module_doc); + PyObject *dict = PyModule_GetDict(module); + + ADD_DEFINE(SND_ASYNC); + ADD_DEFINE(SND_NODEFAULT); + ADD_DEFINE(SND_NOSTOP); + ADD_DEFINE(SND_NOWAIT); + ADD_DEFINE(SND_ALIAS); + ADD_DEFINE(SND_FILENAME); + ADD_DEFINE(SND_MEMORY); + ADD_DEFINE(SND_PURGE); + ADD_DEFINE(SND_LOOP); + ADD_DEFINE(SND_APPLICATION); + + ADD_DEFINE(MB_OK); + ADD_DEFINE(MB_ICONASTERISK); + ADD_DEFINE(MB_ICONEXCLAMATION); + ADD_DEFINE(MB_ICONHAND); + ADD_DEFINE(MB_ICONQUESTION); + + version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&version); + whichOS = Win9X; + if (version.dwPlatformId != VER_PLATFORM_WIN32s && + version.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) + whichOS = WinNT2000; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/.cvsignore b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/.cvsignore new file mode 100644 index 00000000..b7dd0a14 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/.cvsignore @@ -0,0 +1,14 @@ +*.WSM +*.bsc +*.dbg +*.dll +*.exe +*.exp +*.ilk +*.lib +*.ncb +*.opt +*.pdb +*.plg +*.pyd +*-temp-* diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/BUILDno.txt b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/BUILDno.txt new file mode 100644 index 00000000..09df129e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/BUILDno.txt @@ -0,0 +1,137 @@ +Python has a "build number" scheme on Unix-like systems that's hard to +explain: + +Python 2.0b1 (#4, Sep 7 2000, 02:40:55) [MSC 32 bit (Intel)] on win32 + ^^ +The build number there is "#4". + +Each developer's unique build tree generates its own "build numbers", +starting at 0, and increasing by 1 each time a build is done in that tree. +These numbers are never checked in, or coordinated in any other way. It's +just handy for a developer to distinguish among their own personal builds. + +The makefile tricks used to accomplish this under Unix-like systems don't +work under MSDev. Here we fake it by hand, but much less frequently, and +do check it in. The build number only changes often enough to distinguish +releases from each other, and from the long "in between" stretches of CVS +development. An account of all Windows BUILD numbers follows; when you +check in a new one, please add an entry to the top of the list. + +How to change the Windows build number: + ++ Right-click on getbuildinfo.c from within MSDev. Select Settings ... ++ Select the General category of the C/C++ tab. ++ In "Settings For:" select "Multiple Configurations ...". ++ Check the "Win32 Release" and "Win32 Debug" boxes and click OK. ++ In the Preprocessor Definitions box, increment the number after BUILD=. ++ Click OK. ++ This is not enough to convince MSDev to recompile getbuildinfo.c, + so force that and relink. ++ Verify that the new build number shows up in both release and debug + builds. + + +Windows Python BUILD numbers +---------------------------- + 51 2.3.3 + 19-Dec-2003 + 50 2.3.3c1 + 5-Dec-2003 + 49 2.3.2 (final) + 3-Oct-2003 + 48 2.3.2c1 + 30-Sep-2003 + 47 2.3.1 (final) + 23-Sep-2003 + 46 2.3 (final) + 29-Jul-2003 + 45 2.3c2 + 24-Jul-2003 + 44 2.3c1 + 18-Jul-2003 + 43 2.3b2 + 29-Jun-2003 + 42 2.2.3 (final) + 30-May-2003 + 41 2.2.3c1 + 22-May-2003 + 40 2.3b1 + 25-Apr-2003 + 39 2.3a2 + 19-Feb-2003 + 38 2.3a1 + 31-Dec-2002 + 37 2.2.2 (final) + 14-Oct-2002 + 36 2.2.2b1 + 7-Oct-2002 + 35 2.1.3 (final) + 8-Apr-2002 + 34 2.2.1 (final) + 10-Apr-2002 + 33 2.2.1c2 + 26-Mar-2002 + 32 2.2.1c1 + 18-Mar-2002 + 31 2.1.2 final + 16-Jan-2002 + 30 2.1.2c1 + 10-Jan-2002 + 29 CVS development + 21-Dec-2001 + 28 2.2 final + 21-Dec-2001 + 27 2.2c1 + 14-Dec-2001 + 26 2.2b2 + 16-Nov-2001 + 25 2.2b1 + 19-Oct-2001 + 24 2.2a4 + 28-Sep-2001 + 23 2.2a3 + 07-Sep-2001 + 22 2.2a2 + 22-Aug-2001 + 21 2.2a1 + 18-Jul-2001 + 20 2.1.1 + 20-Jul-2001 + 19 2.1.1c1 + 13-Jul-2001 + 18 2.0.1 + 22-Jun-2001 + 17 2.0.1c1 + 13-Jun-2001 + 16 CVS development + 18-Apr-2001 + 15 2.1 final + 16-Apr-2001 + 14 2.1c2 + 15-Apr-2001 + 13 2.1c1 + 12-Apr-2001 + 12 2.1b2 + 20-Mar-2001 + 11 2.1b1 + 28-Feb-2001 + 10 2.1a2 + 1-Feb-2001 + 9 2.1a1 + 17-Jan-2001 + 8 2.0 (final) + 14-Oct-2000 + 7 2.0c1 + 07-Oct-2000 + 6 2.0b2 + 26-Sep-2000 + 5 CVS development + 07-Sep-2000 + 4 2.0b1 repaired to include Lib\xml + Lib\lib-old + Lib\test\*.xml + 07-Sep-2000 + 3 2.0b1 + 05-Sep-2000 + 2 CVS development + 1 unused + 0 2.0b1p1 and 2.0b1p2 + 01-Sep-2000 for both -- this scheme hadn't started yet diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/Uninstal.wse b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/Uninstal.wse new file mode 100644 index 00000000..306f3bc3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/Uninstal.wse @@ -0,0 +1,514 @@ +Document Type: WSE +item: Global + Version=8.14 + Flags=00000100 + Split=1420 + Languages=65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + Copy Default=1 + Japanese Font Name=MS Gothic + Japanese Font Size=10 + Start Gradient=0 0 255 + End Gradient=0 0 0 + Windows Flags=00000000000000000000101000001000 + Message Font=MS Sans Serif + Font Size=8 + Disk Label=GLBS + Disk Filename=INSTALL + Patch Flags=0000000000000001 + Patch Threshold=200 + Patch Memory=4096 + Per-User Version ID=1 + Crystal Format=10111100101100000010001001001001 + Step View=&Properties +end +item: Remark + Text=Note from Tim: This is a verbatim copy of Wise's Uninstal.wse, altered at the end to write +end +item: Remark + Text=uninstall info under HKCU instead of HKLM if our DOADMIN var is false. +end +item: Remark +end +item: Remark + Text= Install Support for uninstalling the application. +end +item: Remark +end +item: Set Variable + Variable=UNINSTALL_PATH + Value=%_LOGFILE_PATH_% + Flags=00000010 +end +item: Set Variable + Variable=UNINSTALL_PATH + Value=%UNINSTALL_PATH%\UNWISE.EXE +end +item: Compiler Variable If + Variable=_EXE_OS_TYPE_ + Value=WIN32 +end +item: Install File + Source=%_WISE_%\UNWISE32.EXE + Destination=%UNINSTALL_PATH% + Flags=0000000000000010 +end +item: Compiler Variable Else +end +item: Install File + Source=%_WISE_%\UNWISE.EXE + Destination=%UNINSTALL_PATH% + Flags=0000000000000010 +end +item: Compiler Variable End +end +item: Remark +end +item: Remark + Text= Install Support for multiple languages +end +item: Remark +end +item: Set Variable + Variable=UNINSTALL_LANG + Value=%UNINSTALL_PATH% + Flags=00000010 +end +item: Set Variable + Variable=UNINSTALL_LANG + Value=%UNINSTALL_LANG%\UNWISE.INI +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=C + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.FRA + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_C_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.FRA + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=D + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.FRA + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_D_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.FRA + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=E + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.DEU + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_E_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.DEU + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=F + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.PTG + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_F_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.PTG + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=G + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.ESP + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_G_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.ESP + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=H + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.ESP + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_H_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.ESP + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=I + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.ITA + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_I_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.ITA + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=J + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.DAN + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_J_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.DAN + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=K + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.FIN + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_K_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.FIN + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=L + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.ISL + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_L_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.ISL + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=M + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.NLD + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_M_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.NLD + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=N + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.NOR + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_N_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.NOR + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=O + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.SVE + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_O_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.SVE + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Compiler Variable If + Variable=_LANG_LIST_ + Value=P + Flags=00000010 +end +item: Compiler Variable If + Value=%_WISE_%\LANGUAGE\UNWISE.JPN + Flags=00000011 +end +item: If/While Statement + Variable=LANG + Value=%_LANG_P_NAME_% +end +item: Install File + Source=%_WISE_%\LANGUAGE\UNWISE.JPN + Destination=%UNINSTALL_LANG% + Flags=0000000000000010 +end +item: End Block +end +item: Compiler Variable End +end +item: Compiler Variable End +end +item: Remark +end +item: Remark + Text= Install the add/remove or uninstall icon +end +item: Remark +end +item: Set Variable + Variable=UNINSTALL_PATH + Value=%UNINSTALL_PATH% + Flags=00010100 +end +item: Set Variable + Variable=INST_LOG_PATH + Value=%_LOGFILE_PATH_% + Flags=00010100 +end +item: Check Configuration + Flags=10111011 +end +item: If/While Statement + Variable=DOADMIN + Value=1 +end +item: Remark + Text=Write uninstall info under HKLM. This if/else/end block added by Tim. +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=%APPTITLE% + Value Name=DisplayName + Root=2 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=%UNINSTALL_PATH% %INST_LOG_PATH% + New Value= + Value Name=UninstallString + Root=2 +end +item: Else Statement +end +item: Remark + Text=The same, but write under HKCU instead. +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=%APPTITLE% + Value Name=DisplayName + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=%UNINSTALL_PATH% %INST_LOG_PATH% + New Value= + Value Name=UninstallString + Root=1 +end +item: End Block +end +item: Else Statement +end +item: Add ProgMan Icon + Group=%GROUP% + Icon Name=Uninstall %APPTITLE% + Command Line=%UNINSTALL_PATH% %INST_LOG_PATH% +end +item: End Block +end +item: Check Configuration + Flags=11110010 +end +item: If/While Statement + Variable=DOBRAND + Value=1 +end +item: Edit Registry + Total Keys=2 + item: Key + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=%COMPANY% + Value Name=RegCompany + Root=2 + end + item: Key + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=%NAME% + Value Name=RegOwner + Root=2 + end +end +item: End Block +end +item: End Block +end diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_bsddb.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_bsddb.dsp new file mode 100644 index 00000000..ea13a157 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_bsddb.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="_bsddb" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=_bsddb - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "_bsddb.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "_bsddb.mak" CFG="_bsddb - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "_bsddb - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "_bsddb - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "_bsddb - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\_bsddb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /I "..\..\db-4.1.25\build_win32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\..\db-4.1.25\build_win32\Release_static\libdb41s.lib /nologo /base:"0x1e180000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"msvcrt" /out:"./_bsddb.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "_bsddb - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\_bsddb" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /I "..\..\db-4.1.25\build_win32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ..\..\db-4.1.25\build_win32\Release_static\libdb41s.lib /nologo /base:"0x1e180000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"msvcrtd" /out:"./_bsddb_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "_bsddb - Win32 Release" +# Name "_bsddb - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\_bsddb.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_csv.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_csv.dsp new file mode 100644 index 00000000..f9b9c306 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_csv.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="_csv" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=_csv - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "_csv.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "_csv.mak" CFG="_csv - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "_csv - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "_csv - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "_csv - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\_csv" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /I "C:\Program Files\Tcl\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1d190000" /subsystem:windows /dll /debug /machine:I386 /out:"./_csv.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "_csv - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\_csv" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /I "C:\Program Files\Tcl\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1d190000" /subsystem:windows /dll /debug /machine:I386 /out:"./_csv_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "_csv - Win32 Release" +# Name "_csv - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\_csv.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_socket.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_socket.dsp new file mode 100644 index 00000000..cdc6932f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_socket.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="_socket" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=_socket - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "_socket.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "_socket.mak" CFG="_socket - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "_socket - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "_socket - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "_socket - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\_socket" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /base:"0x1e1D0000" /subsystem:windows /dll /debug /machine:I386 /out:"./_socket.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "_socket - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\_socket" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /base:"0x1e1D0000" /subsystem:windows /dll /debug /machine:I386 /out:"./_socket_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "_socket - Win32 Release" +# Name "_socket - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\socketmodule.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_sre.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_sre.dsp new file mode 100644 index 00000000..381ec11a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_sre.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="_sre" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=_sre - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "_sre.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "_sre.mak" CFG="_sre - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "_sre - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "_sre - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "_sre - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\_sre" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /I "C:\Program Files\Tcl\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e1e0000" /subsystem:windows /dll /debug /machine:I386 /out:"./_sre.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "_sre - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\_sre" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /I "C:\Program Files\Tcl\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e1e0000" /subsystem:windows /dll /debug /machine:I386 /out:"./_sre_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "_sre - Win32 Release" +# Name "_sre - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\_sre.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_sre_static.vcproj b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_sre_static.vcproj new file mode 100644 index 00000000..569e03f3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_sre_static.vcproj @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_ssl.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_ssl.dsp new file mode 100644 index 00000000..22447ebb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_ssl.dsp @@ -0,0 +1,89 @@ +# Microsoft Developer Studio Project File - Name="_ssl" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=_ssl - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "_ssl.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "_ssl.mak" CFG="_ssl - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "_ssl - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "_ssl - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "_ssl - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Cmd_Line "NMAKE /f _ssl.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "_ssl.exe" +# PROP BASE Bsc_Name "_ssl.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\_ssl" +# PROP Cmd_Line "python build_ssl.py" +# PROP Rebuild_Opt "-a" +# PROP Target_File "_ssl.pyd" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "_ssl - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "x86-temp-debug\_ssl" +# PROP BASE Intermediate_Dir "x86-temp-debug\_ssl" +# PROP BASE Cmd_Line "NMAKE /f _ssl.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "_ssl_d.pyd" +# PROP BASE Bsc_Name "_ssl_d.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\_ssl" +# PROP Cmd_Line "python_d -u build_ssl.py -d" +# PROP Rebuild_Opt "-a" +# PROP Target_File "_ssl_d.pyd" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "_ssl - Win32 Release" +# Name "_ssl - Win32 Debug" + +!IF "$(CFG)" == "_ssl - Win32 Release" + +!ELSEIF "$(CFG)" == "_ssl - Win32 Debug" + +!ENDIF + +# Begin Source File + +SOURCE=..\Modules\_ssl.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_ssl.mak b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_ssl.mak new file mode 100644 index 00000000..62f1cf10 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_ssl.mak @@ -0,0 +1,21 @@ + +!IFDEF DEBUG +MODULE=_ssl_d.pyd +TEMP_DIR=x86-temp-debug/_ssl +CFLAGS=/Od /Zi /MDd /LDd /DDEBUG /D_DEBUG +SSL_LIB_DIR=$(SSL_DIR)/out32.dbg +!ELSE +MODULE=_ssl.pyd +TEMP_DIR=x86-temp-release/_ssl +CFLAGS=/Ox /MD /LD +SSL_LIB_DIR=$(SSL_DIR)/out32 +!ENDIF + +INCLUDES=-I ../Include -I ../PC -I $(SSL_DIR)/inc32 +LIBS=gdi32.lib wsock32.lib /libpath:$(SSL_LIB_DIR) libeay32.lib ssleay32.lib + +SOURCE=../Modules/_ssl.c $(SSL_LIB_DIR)/libeay32.lib $(SSL_LIB_DIR)/ssleay32.lib + +$(MODULE): $(SOURCE) ../PC/*.h ../Include/*.h + @if not exist "$(TEMP_DIR)/." mkdir "$(TEMP_DIR)" + cl /nologo $(SOURCE) $(CFLAGS) /Fo$(TEMP_DIR)\$*.obj $(INCLUDES) /link /out:$(MODULE) $(LIBS) diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_symtable.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_symtable.dsp new file mode 100644 index 00000000..949192e2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_symtable.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="_symtable" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=_symtable - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "_symtable.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "_symtable.mak" CFG="_symtable - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "_symtable - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "_symtable - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "_symtable - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\_symtable" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\Include" /I "..\PC" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "NDEBUG" +# ADD RSC /l 0xc09 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e100000" /dll /machine:I386 /out:"./_symtable.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "_symtable - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\_symtable" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "_DEBUG" +# ADD RSC /l 0xc09 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e100000" /dll /debug /machine:I386 /out:"./_symtable_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "_symtable - Win32 Release" +# Name "_symtable - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\symtablemodule.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_testcapi.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_testcapi.dsp new file mode 100644 index 00000000..2b0e2e85 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_testcapi.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="_testcapi" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=_testcapi - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "_testcapi.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "_testcapi.mak" CFG="_testcapi - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "_testcapi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "_testcapi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "_testcapi - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\_testcapi" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\Include" /I "..\PC" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "NDEBUG" +# ADD RSC /l 0xc09 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e1F0000" /dll /machine:I386 /out:"./_testcapi.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "_testcapi - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\_testcapi" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "_DEBUG" +# ADD RSC /l 0xc09 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e1F0000" /dll /debug /machine:I386 /out:"./_testcapi_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "_testcapi - Win32 Release" +# Name "_testcapi - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\_testcapimodule.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_tkinter.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_tkinter.dsp new file mode 100644 index 00000000..dd39ea22 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/_tkinter.dsp @@ -0,0 +1,103 @@ +# Microsoft Developer Studio Project File - Name="_tkinter" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=_tkinter - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "_tkinter.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "_tkinter.mak" CFG="_tkinter - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "_tkinter - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "_tkinter - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "_tkinter - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\_tkinter" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\tcl84\include" /I "..\Include" /I "..\PC" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "WITH_APPINIT" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ..\..\tcl84\lib\tk84.lib ..\..\tcl84\lib\tcl84.lib odbc32.lib odbccp32.lib user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /base:"0x1e190000" /subsystem:windows /dll /debug /machine:I386 /out:"./_tkinter_d.pyd" /pdbtype:sept /libpath:"C:\Program Files\Tcl\lib" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "_tkinter - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\_tkinter" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\..\tcl84\include" /I "..\Include" /I "..\PC" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "WITH_APPINIT" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\tcl84\lib\tk84.lib ..\..\tcl84\lib\tcl84.lib odbc32.lib odbccp32.lib user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /base:"0x1e190000" /subsystem:windows /dll /debug /machine:I386 /out:"./_tkinter.pyd" /libpath:"C:\Program Files\Tcl\lib" +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "_tkinter - Win32 Debug" +# Name "_tkinter - Win32 Release" +# Begin Source File + +SOURCE=..\Modules\_tkinter.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\tkappinit.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/build_ssl.py b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/build_ssl.py new file mode 100644 index 00000000..ff7b70c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/build_ssl.py @@ -0,0 +1,163 @@ +# Script for building the _ssl module for Windows. +# Uses Perl to setup the OpenSSL environment correctly +# and build OpenSSL, then invokes a simple nmake session +# for _ssl.pyd itself. + +# THEORETICALLY, you can: +# * Unpack the latest SSL release one level above your main Python source +# directory. It is likely you will already find the zlib library and +# any other external packages there. +# * Install ActivePerl and ensure it is somewhere on your path. +# * Run this script from the PCBuild directory. +# +# it should configure and build SSL, then build the ssl Python extension +# without intervention. + +import os, sys, re + +# Find all "foo.exe" files on the PATH. +def find_all_on_path(filename, extras = None): + entries = os.environ["PATH"].split(os.pathsep) + ret = [] + for p in entries: + fname = os.path.abspath(os.path.join(p, filename)) + if os.path.isfile(fname) and fname not in ret: + ret.append(fname) + if extras: + for p in extras: + fname = os.path.abspath(os.path.join(p, filename)) + if os.path.isfile(fname) and fname not in ret: + ret.append(fname) + return ret + +# Find a suitable Perl installation for OpenSSL. +# cygwin perl does *not* work. ActivePerl does. +# Being a Perl dummy, the simplest way I can check is if the "Win32" package +# is available. +def find_working_perl(perls): + for perl in perls: + fh = os.popen(perl + ' -e "use Win32;"') + fh.read() + rc = fh.close() + if rc: + continue + return perl + print "Can not find a suitable PERL:" + if perls: + print " the following perl interpreters were found:" + for p in perls: + print " ", p + print " None of these versions appear suitable for building OpenSSL" + else: + print " NO perl interpreters were found on this machine at all!" + print " Please install ActivePerl and ensure it appears on your path" + print "The Python SSL module was not built" + return None + +# Locate the best SSL directory given a few roots to look into. +def find_best_ssl_dir(sources): + candidates = [] + for s in sources: + try: + s = os.path.abspath(s) + fnames = os.listdir(s) + except os.error: + fnames = [] + for fname in fnames: + fqn = os.path.join(s, fname) + if os.path.isdir(fqn) and fname.startswith("openssl-"): + candidates.append(fqn) + # Now we have all the candidates, locate the best. + best_parts = [] + best_name = None + for c in candidates: + parts = re.split("[.-]", os.path.basename(c))[1:] + # eg - openssl-0.9.7-beta1 - ignore all "beta" or any other qualifiers + if len(parts) >= 4: + continue + if parts > best_parts: + best_parts = parts + best_name = c + if best_name is not None: + print "Found an SSL directory at '%s'" % (best_name,) + else: + print "Could not find an SSL directory in '%s'" % (sources,) + return best_name + +def main(): + debug = "-d" in sys.argv + build_all = "-a" in sys.argv + make_flags = "" + if build_all: + make_flags = "-a" + # perl should be on the path, but we also look in "\perl" and "c:\\perl" + # as "well known" locations + perls = find_all_on_path("perl.exe", ["\\perl\\bin", "C:\\perl\\bin"]) + perl = find_working_perl(perls) + if perl is None: + sys.exit(1) + + print "Found a working perl at '%s'" % (perl,) + # Look for SSL 2 levels up from pcbuild - ie, same place zlib etc all live. + ssl_dir = find_best_ssl_dir(("../..",)) + if ssl_dir is None: + sys.exit(1) + + old_cd = os.getcwd() + try: + os.chdir(ssl_dir) + # If the ssl makefiles do not exist, we invoke Perl to generate them. + if not os.path.isfile(os.path.join(ssl_dir, "32.mak")) or \ + not os.path.isfile(os.path.join(ssl_dir, "d32.mak")): + print "Creating the makefiles..." + # Put our working Perl at the front of our path + os.environ["PATH"] = os.path.split(perl)[0] + \ + os.pathsep + \ + os.environ["PATH"] + # ms\32all.bat will reconfigure OpenSSL and then try to build + # all outputs (debug/nondebug/dll/lib). So we filter the file + # to exclude any "nmake" commands and then execute. + tempname = "ms\\32all_py.bat" + + in_bat = open("ms\\32all.bat") + temp_bat = open(tempname,"w") + while 1: + cmd = in_bat.readline() + print 'cmd', repr(cmd) + if not cmd: break + if cmd.strip()[:5].lower() == "nmake": + continue + temp_bat.write(cmd) + in_bat.close() + temp_bat.close() + os.system(tempname) + try: + os.remove(tempname) + except: + pass + + # Now run make. + print "Executing nmake over the ssl makefiles..." + if debug: + rc = os.system("nmake /nologo -f d32.mak") + if rc: + print "Executing d32.mak failed" + print rc + sys.exit(rc) + else: + rc = os.system("nmake /nologo -f 32.mak") + if rc: + print "Executing 32.mak failed" + print rc + sys.exit(rc) + finally: + os.chdir(old_cd) + # And finally, we can build the _ssl module itself for Python. + defs = "SSL_DIR=%s" % (ssl_dir,) + if debug: + defs = defs + " " + "DEBUG=1" + rc = os.system('nmake /nologo -f _ssl.mak ' + defs + " " + make_flags) + sys.exit(rc) + +if __name__=='__main__': + main() diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/bz2.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/bz2.dsp new file mode 100644 index 00000000..e6f72ec1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/bz2.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="bz2" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=bz2 - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "bz2.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "bz2.mak" CFG="bz2 - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "bz2 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "bz2 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "bz2 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\bz2" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /I "..\..\bzip2-1.0.2" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\bzip2-1.0.2\libbz2.lib /nologo /base:"0x1D170000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc" /out:"./bz2.pyd" +# SUBTRACT LINK32 /pdb:none /nodefaultlib + +!ELSEIF "$(CFG)" == "bz2 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\bz2" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /I "..\..\bzip2-1.0.2" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ..\..\bzip2-1.0.2\libbz2.lib /nologo /base:"0x1D170000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"msvcrt" /nodefaultlib:"libc" /out:"./bz2_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "bz2 - Win32 Release" +# Name "bz2 - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\bz2module.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/datetime.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/datetime.dsp new file mode 100644 index 00000000..8d786ab9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/datetime.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="datetime" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=datetime - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "datetime.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "datetime.mak" CFG="datetime - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "datetime - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "datetime - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "datetime - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\datetime" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /base:"0x1D180000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc" /out:"./datetime.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "datetime - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\datetime" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /base:"0x1D180000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc" /nodefaultlib:"msvcrt" /out:"./datetime_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "datetime - Win32 Release" +# Name "datetime - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\datetimemodule.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/field3.py b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/field3.py new file mode 100644 index 00000000..8ed94e93 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/field3.py @@ -0,0 +1,35 @@ +# An absurd workaround for the lack of arithmetic in MS's resource compiler. +# After building Python, run this, then paste the output into the appropriate +# part of PC\python_nt.rc. +# Example output: +# +# * For 2.3a0, +# * PY_MICRO_VERSION = 0 +# * PY_RELEASE_LEVEL = 'alpha' = 0xA +# * PY_RELEASE_SERIAL = 1 +# * +# * and 0*1000 + 10*10 + 1 = 101. +# */ +# #define FIELD3 101 + +import sys + +major, minor, micro, level, serial = sys.version_info +levelnum = {'alpha': 0xA, + 'beta': 0xB, + 'candidate': 0xC, + 'final': 0xF, + }[level] +string = sys.version.split()[0] # like '2.3a0' + +print " * For %s," % string +print " * PY_MICRO_VERSION = %d" % micro +print " * PY_RELEASE_LEVEL = %r = %s" % (level, hex(levelnum)) +print " * PY_RELEASE_SERIAL = %d" % serial +print " *" + +field3 = micro * 1000 + levelnum * 10 + serial + +print " * and %d*1000 + %d*10 + %d = %d" % (micro, levelnum, serial, field3) +print " */" +print "#define FIELD3", field3 diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/installer.bmp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/installer.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6c5656931f85304b213633fa9b6836cc36d3db03 GIT binary patch literal 50324 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 +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mmap - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mmap.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mmap.mak" CFG="mmap - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mmap - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mmap - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mmap - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\mmap" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\Include" /I "..\PC" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "NDEBUG" +# ADD RSC /l 0xc09 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e1F0000" /dll /machine:I386 /out:"./mmap.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "mmap - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\mmap" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "_DEBUG" +# ADD RSC /l 0xc09 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e1F0000" /dll /debug /machine:I386 /out:"./mmap_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "mmap - Win32 Release" +# Name "mmap - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\mmapmodule.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/parser.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/parser.dsp new file mode 100644 index 00000000..267b2d29 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/parser.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="parser" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=parser - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "parser.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "parser.mak" CFG="parser - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "parser - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "parser - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "parser - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\parser" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /I "C:\Program Files\Tcl\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e1A0000" /subsystem:windows /dll /debug /machine:I386 /out:"./parser.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "parser - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\parser" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /I "C:\Program Files\Tcl\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e1A0000" /subsystem:windows /dll /debug /machine:I386 /out:"./parser_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "parser - Win32 Release" +# Name "parser - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\parsermodule.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pcbuild.dsw b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pcbuild.dsw new file mode 100644 index 00000000..a90316bd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pcbuild.dsw @@ -0,0 +1,359 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "_bsddb"=".\_bsddb.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "_csv"=".\_csv.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "_socket"=".\_socket.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "_sre"=".\_sre.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "_ssl"=".\_ssl.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency + Begin Project Dependency + Project_Dep_Name _sre + End Project Dependency + Begin Project Dependency + Project_Dep_Name python + End Project Dependency + Begin Project Dependency + Project_Dep_Name w9xpopen + End Project Dependency +}}} + +############################################################################### + +Project: "_symtable"=".\_symtable.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "_testcapi"=".\_testcapi.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "_tkinter"=".\_tkinter.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "bz2"=".\bz2.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "datetime"=".\datetime.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "make_versioninfo"=".\make_versioninfo.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "mmap"=".\mmap.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "parser"=".\parser.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "pyexpat"=".\pyexpat.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "python"=".\python.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "pythoncore"=".\pythoncore.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "pythonw"=".\pythonw.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "select"=".\select.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "unicodedata"=".\unicodedata.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "w9xpopen"=".\w9xpopen.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "winreg"=".\winreg.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "winsound"=".\winsound.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Project: "zlib"=".\zlib.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name pythoncore + End Project Dependency +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pyexpat.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pyexpat.dsp new file mode 100644 index 00000000..50df6015 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pyexpat.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="pyexpat" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=pyexpat - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "pyexpat.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "pyexpat.mak" CFG="pyexpat - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "pyexpat - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "pyexpat - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "pyexpat - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\pyexpat" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /I "..\Modules\expat" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "XML_NS" /D "XML_DTD" /D BYTEORDER=1234 /D XML_CONTEXT_BYTES=1024 /D "XML_STATIC" /D "HAVE_MEMMOVE" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1D100000" /subsystem:windows /dll /debug /machine:I386 /out:"./pyexpat.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "pyexpat - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\pyexpat" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /I "..\Modules\expat" /D "_DEBUG" /D "HAVE_EXPAT_H" /D "WIN32" /D "_WINDOWS" /D "XML_NS" /D "XML_DTD" /D BYTEORDER=1234 /D XML_CONTEXT_BYTES=1024 /D "XML_STATIC" /D "HAVE_MEMMOVE" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1D100000" /subsystem:windows /dll /debug /machine:I386 /out:"./pyexpat_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "pyexpat - Win32 Release" +# Name "pyexpat - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\pyexpat.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\expat\xmlparse.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\expat\xmlrole.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\expat\xmltok.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python.dsp new file mode 100644 index 00000000..c34a0b00 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python.dsp @@ -0,0 +1,100 @@ +# Microsoft Developer Studio Project File - Name="python" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=python - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "python.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "python.mak" CFG="python - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "python - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "python - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "python - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\python" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /base:"0x1d000000" /subsystem:console /debug /machine:I386 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "python - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\python" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /i "..\Include" /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /base:"0x1d000000" /subsystem:console /debug /machine:I386 /out:"./python_d.exe" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "python - Win32 Release" +# Name "python - Win32 Debug" +# Begin Source File + +SOURCE=..\PC\pycon.ico +# End Source File +# Begin Source File + +SOURCE=..\Modules\python.c +# End Source File +# Begin Source File + +SOURCE=..\PC\python_exe.rc +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python.iss b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python.iss new file mode 100644 index 00000000..ff63a3e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python.iss @@ -0,0 +1,241 @@ +; Script generated by the Inno Setup Script Wizard. +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! + +; This is the whole ball of wax for an Inno installer for Python. +; To use, download Inno Setup from http://www.jrsoftware.org/isdl.htm/, +; install it, and double-click on this file. That launches the Inno +; script compiler. The GUI is extemely simple, and has only one button +; you may not recognize instantly: click it. You're done. It builds +; the installer into PCBuild/Python-2.2a1.exe. Size and speed of the +; installer are competitive with the Wise installer; Inno uninstall +; seems much quicker than Wise (but also feebler, and the uninstall +; log is in some un(human)readable binary format). +; +; What's Done +; ----------- +; All the usual Windows Python files are installed by this now. +; All the usual Windows Python Start menu entries are created and +; work fine. +; .py, .pyw, .pyc and .pyo extensions are registered. +; PROBLEM: Inno uninstall does not restore their previous registry +; associations (if any). Wise did. This will make life +; difficult for alpha (etc) testers. +; The Python install is fully functional for "typical" uses. +; +; What's Not Done +; --------------- +; None of "Mark Hammond's" registry entries are written. +; No installation of files is done into the system dir: +; The MS DLLs aren't handled at all by this yet. +; Python22.dll is unpacked into the main Python dir. +; +; Inno can't do different things on NT/2000 depending on whether the user +; has Admin privileges, so I don't know how to "solve" either of those, +; short of building two installers (one *requiring* Admin privs, the +; other not doing anything that needs Admin privs). +; +; Inno has no concept of variables, so lots of lines in this file need +; to be fiddled by hand across releases. Simplest way out: stick this +; file in a giant triple-quoted r-string (note that backslashes are +; required all over the place here -- forward slashes DON'T WORK in +; Inno), and use %(yadda)s string interpolation to do substitutions; i.e., +; write a very simple Python program to *produce* this script. + +[Setup] +; Note: we *want* the version number to show up everywhere. +; Which of these controls App Path??? +AppName=Python 2.2 alpha 1 +AppVerName=Python 2.2 alpha 1 +AppId=Python 2.2 +AppVersion=2.2a1 +AppCopyright=Copyright © 2001 Python Software Foundation + +; Default install dir; value of {app} later (unless user overrides). +; {sd} = system root drive, probably "C:". +DefaultDirName={sd}\Python22 + +; Start menu folder name; value of {group} later (unless user overrides). +DefaultGroupName=Python 2.2 + +; Point SourceDir to one above PCBuild = src. +; YAY! That actually worked: means this script can run unchanged from anyone's +; CVS tree, no matter what they called the top-level directories. Wise +; required hardcoded absolute paths all over the place. +SourceDir=.. +OutputDir=PCBuild +OutputBaseFilename=Python-2.2a1 + +AppPublisher=PythonLabs at Zope Corporation +AppPublisherURL=http://www.python.org +AppSupportURL=http://www.python.org +AppUpdatesURL=http://www.python.org + +AlwaysCreateUninstallIcon=yes +ChangesAssociations=yes +UninstallLogMode=new + +; The fewer screens the better; leave these commented. +;LicenseFile=LICENSE +;InfoBeforeFile=Misc\NEWS + +; uncomment the following line if you want your installation to run on NT 3.51 too. +; MinVersion=4,3.51 + +[Types] +Name: normal; Description: "Select desired components"; Flags: iscustom + +[Components] +Name: main; Description: "Python interpreter, library and Tk"; Types: normal +Name: docs; Description: "Python documentation (HTML)"; Types: normal +Name: tools; Description: "Python utility scripts (Tools\)"; Types: normal +Name: test; Description: "Python test suite (Lib\test\)"; Types: normal + +[Tasks] +Name: startmenu; Description: "Create Start menu shortcuts"; Components: main docs tools +Name: extensions; Description: "Register file extensions (.py, .pyw, .pyc, .pyo)"; Components: main + +[Files] +; Caution: Using forward slashes instead screws up in amazing ways. +; Unknown: By the time Components (and other attrs) are added to these lines, they're +; going to get awfully long. But don't see a way to continue logical lines across +; physical lines. + +Source: LICENSE; DestDir: "{app}"; DestName: LICENSE.txt; CopyMode: alwaysoverwrite +Source: README; DestDir: "{app}"; DestName: README.txt; CopyMode: alwaysoverwrite +Source: Misc\News; DestDir: "{app}"; DestName: NEWS.txt; CopyMode: alwaysoverwrite +Source: PC\*.ico; DestDir: "{app}"; CopyMode: alwaysoverwrite; Components: main + + +Source: PCbuild\python.exe; DestDir: "{app}"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\pythonw.exe; DestDir: "{app}"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\w9xpopen.exe; DestDir: "{app}"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\python22.dll; DestDir: "{app}"; CopyMode: alwaysoverwrite; Components: main + +Source: ..\tcl\bin\tcl83.dll; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: ..\tcl\bin\tk83.dll; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: ..\tcl\lib\*.*; DestDir: "{app}\tcl"; CopyMode: alwaysoverwrite; Components: main; Flags: recursesubdirs + +Source: PCbuild\_socket.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\_socket.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\_sre.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\_sre.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\_symtable.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\_symtable.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\_testcapi.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\_testcapi.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\_tkinter.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\_tkinter.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\bsddb.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\bsddb.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\mmap.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\mmap.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\parser.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\parser.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\pyexpat.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\pyexpat.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\select.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\select.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\unicodedata.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\unicodedata.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\_winreg.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\_winreg.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\winsound.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\winsound.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\zlib.pyd; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main +Source: PCbuild\zlib.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: PCbuild\python22.lib; DestDir: "{app}\libs"; CopyMode: alwaysoverwrite; Components: main + +Source: ..\expat\Libs\expat.dll; DestDir: "{app}\DLLs"; CopyMode: alwaysoverwrite; Components: main + +Source: Lib\*.py; DestDir: "{app}\Lib"; CopyMode: alwaysoverwrite; Components: main +Source: Lib\lib-tk\*.py; DestDir: "{app}\Lib\lib-tk"; CopyMode: alwaysoverwrite; Components: main + +Source: Lib\encodings\*.py; DestDir: "{app}\Lib\encodings"; CopyMode: alwaysoverwrite; Components: main +Source: Lib\distutils\*.py; DestDir: "{app}\Lib\distutils"; CopyMode: alwaysoverwrite; Components: main +Source: Lib\xml\*.py; DestDir: "{app}\Lib\xml"; CopyMode: alwaysoverwrite; Flags: recursesubdirs; Components: main +Source: Lib\lib-old\*.py; DestDir: "{app}\Lib\lib-old"; CopyMode: alwaysoverwrite; Components: main +Source: Lib\site-packages\README; DestDir: "{app}\Lib\site-packages"; DestName: README.txt; CopyMode: alwaysoverwrite; Components: main +Source: Include\*.h; DestDir: "{app}\include"; CopyMode: alwaysoverwrite; Components: main +Source: PC\pyconfig.h; DestDir: "{app}\include"; CopyMode: alwaysoverwrite; Components: main + +Source: Tools\scripts\*.py; DestDir: "{app}\Tools\Scripts"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\scripts\*.pyw; DestDir: "{app}\Tools\Scripts"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\scripts\*.doc; DestDir: "{app}\Tools\Scripts"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\scripts\README; DestDir: "{app}\Tools\Scripts"; DestName: README.txt; CopyMode: alwaysoverwrite; Components: tools + +Source: Tools\webchecker\*.py; DestDir: "{app}\Tools\webchecker"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\webchecker\README; DestDir: "{app}\Tools\webchecker"; DestName: README.txt; CopyMode: alwaysoverwrite; Components: tools + +Source: Tools\versioncheck\*.py; DestDir: "{app}\Tools\versioncheck"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\versioncheck\README; DestDir: "{app}\Tools\versioncheck"; DestName: README.txt; CopyMode: alwaysoverwrite; Components: tools + +Source: Tools\idle\*.py; DestDir: "{app}\Tools\idle"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\idle\idle.pyw; DestDir: "{app}\Tools\idle"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\idle\*.txt; DestDir: "{app}\Tools\idle"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\idle\Icons\*.*; DestDir: "{app}\Tools\idle\Icons"; CopyMode: alwaysoverwrite; Components: tools + +Source: Tools\compiler\*.py; DestDir: "{app}\Tools\compiler"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\compiler\README; DestDir: "{app}\Tools\compiler"; DestName: README.txt; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\compiler\compiler\*.py; DestDir: "{app}\Tools\compiler\compiler"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\compiler\compiler\*.txt; DestDir: "{app}\Tools\compiler\compiler"; CopyMode: alwaysoverwrite; Components: tools + +Source: Tools\pynche\*.py; DestDir: "{app}\Tools\pynche"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\pynche\pynche.pyw; DestDir: "{app}\Tools\pynche"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\pynche\X\*.txt; DestDir: "{app}\Tools\pynche\X"; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\pynche\README; DestDir: "{app}\Tools\pynche"; DestName: README.txt; CopyMode: alwaysoverwrite; Components: tools +Source: Tools\pynche\pynche; DestDir: "{app}\Tools\pynche"; DestName: pynche.py; CopyMode: alwaysoverwrite; Components: tools + +Source: html\*.*; DestDir: "{app}\Doc"; CopyMode: alwaysoverwrite; Flags: recursesubdirs; Components: docs + +Source: Lib\test\*.py; DestDir: "{app}\Lib\test"; CopyMode: alwaysoverwrite; Components: test +Source: Lib\test\*.uue; DestDir: "{app}\Lib\test"; CopyMode: alwaysoverwrite; Components: test +Source: Lib\test\*.xml; DestDir: "{app}\Lib\test"; CopyMode: alwaysoverwrite; Components: test +Source: Lib\test\*.out; DestDir: "{app}\Lib\test"; CopyMode: alwaysoverwrite; Components: test +Source: Lib\test\audiotest.au; DestDir: "{app}\Lib\test"; CopyMode: alwaysoverwrite; Components: test +Source: Lib\test\output\*.*; DestDir: "{app}\Lib\test\output"; CopyMode: alwaysoverwrite; Components: test + +[Icons] +Tasks: startmenu; Name: "{group}\IDLE (Python GUI)"; Filename: "{app}\pythonw.exe"; WorkingDir: "{app}"; Parameters: """{app}\Tools\idle\idle.pyw"""; Components: tools +Tasks: startmenu; Name: "{group}\Module Docs"; Filename: "{app}\pythonw.exe"; WorkingDir: "{app}"; Parameters: """{app}\Tools\Scripts\pydoc.pyw"""; Components: tools +Tasks: startmenu; Name: "{group}\Python (command line)"; Filename: "{app}\python.exe"; WorkingDir: "{app}"; Components: main +Tasks: startmenu; Name: "{group}\Python Manuals"; Filename: "{app}\Doc\index.html"; WorkingDir: "{app}"; Components: docs + +[Registry] +; Register .py +Tasks: extensions; Root: HKCR; Subkey: ".py"; ValueType: string; ValueName: ""; ValueData: "Python File"; Flags: uninsdeletevalue +Tasks: extensions; Root: HKCR; Subkey: ".py"; ValueType: string; ValueName: "Content Type"; ValueData: "text/plain"; Flags: uninsdeletevalue +Tasks: extensions; Root: HKCR; Subkey: "Python File"; ValueType: string; ValueName: ""; ValueData: "Python File"; Flags: uninsdeletekey +Tasks: extensions; Root: HKCR; Subkey: "Python File\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\Py.ico" +Tasks: extensions; Root: HKCR; Subkey: "Python File\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\python.exe"" ""%1"" %*" + +; Register .pyc +Tasks: extensions; Root: HKCR; Subkey: ".pyc"; ValueType: string; ValueName: ""; ValueData: "Python CompiledFile"; Flags: uninsdeletevalue +Tasks: extensions; Root: HKCR; Subkey: "Python CompiledFile"; ValueType: string; ValueName: ""; ValueData: "Compiled Python File"; Flags: uninsdeletekey +Tasks: extensions; Root: HKCR; Subkey: "Python CompiledFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\pyc.ico" +Tasks: extensions; Root: HKCR; Subkey: "Python CompiledFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\python.exe"" ""%1"" %*" + +; Register .pyo +Tasks: extensions; Root: HKCR; Subkey: ".pyo"; ValueType: string; ValueName: ""; ValueData: "Python CompiledFile"; Flags: uninsdeletevalue + +; Register .pyw +Tasks: extensions; Root: HKCR; Subkey: ".pyw"; ValueType: string; ValueName: ""; ValueData: "Python NoConFile"; Flags: uninsdeletevalue +Tasks: extensions; Root: HKCR; Subkey: ".pyw"; ValueType: string; ValueName: "Content Type"; ValueData: "text/plain"; Flags: uninsdeletevalue +Tasks: extensions; Root: HKCR; Subkey: "Python NoConFile"; ValueType: string; ValueName: ""; ValueData: "Python File (no console)"; Flags: uninsdeletekey +Tasks: extensions; Root: HKCR; Subkey: "Python NoConFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\Py.ico" +Tasks: extensions; Root: HKCR; Subkey: "Python NoConFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\pythonw.exe"" ""%1"" %*" + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python20.wse b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python20.wse new file mode 100644 index 00000000..69b1677d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/python20.wse @@ -0,0 +1,3168 @@ +Document Type: WSE +item: Global + Version=9.0 + Title=Python 2.3.3 + Flags=00010100 + Languages=65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + Japanese Font Name=MS Gothic + Japanese Font Size=10 + Start Gradient=0 255 0 + End Gradient=0 128 0 + Windows Flags=00000100000011010010010100001010 + Log Pathname=%MAINDIR%\INSTALL.LOG + Message Font=MS Sans Serif + Font Size=8 + Pages Modified=00010000011101000000000100000111 + Extra Pages=00000000000000000000000010110010 + Disk Filename=SETUP + Patch Flags=0000000000001001 + Patch Threshold=85 + Patch Memory=4000 + MIF PDF Version=1.0 + MIF SMS Version=2.0 + EXE Filename=Python-2.3.3.exe + Dialogs Version=8 + Version File=2.3.3 + Version Description=Python Programming Language + Version Copyright=©2001-2003 Python Software Foundation + Version Company=PythonLabs at Zope Corporation + Crystal Format=10111100101100000010001001001001 + Step View=&All + Variable Name1=_WISE_ + Variable Description1=WISE root directory + Variable Default1=C:\Programme\Wise Installation System + Variable Flags1=00001000 + Variable Name2=_TCLDIR_ + Variable Description2=The directory in which the Tcl/Tk installation + Variable Description2=lives. This must be a sibling of the Python + Variable Description2=directory. + Variable Default2=tcl84 + Variable Flags2=00001000 + Variable Name3=_DOC_ + Variable Description3=The unpacked HTML doc directory. + Variable Default3=..\chm + Variable Flags3=00001001 + Variable Name4=_SYS_ + Variable Description4=VC redistributables directory (where to find MSVCRT.DLL). + Variable Description4= + Variable Description4=NEVER USE A SYSTEM DIRECTORY! + Variable Default4=C:\VCREDIST\VC6SP5 + Variable Values4=C:\Code\MSDLLs + Variable Values4=C:\VCREDIST\VC6SP5 + Variable Flags4=00000010 + Variable Name5=_PYMAJOR_ + Variable Description5=Python major version number; the 2 in 2.3. + Variable Default5=2 + Variable Flags5=00001000 + Variable Name6=_PYMINOR_ + Variable Description6=Python minor version number; the 3 in 2.3 + Variable Default6=3 + Variable Flags6=00001000 + Variable Name7=_DOADMIN_ + Variable Description7=The initial value for %DOADMIN%. + Variable Description7=When 0, we never try to write under HKLM, + Variable Description7=and install the Python + MS runtime DLLs in + Variable Description7=the Python directory instead of the system dir. + Variable Default7=1 + Variable Values7=1 + Variable Values7=0 + Variable Flags7=00001010 + Variable Name8=_ALIASNAME_ + Variable Flags8=00001000 + Variable Name9=_ALIASPATH_ + Variable Flags9=00001000 + Variable Name10=_ALIASTYPE_ + Variable Flags10=00001000 +end +item: Set Variable + Variable=PYVER_STRING + Value=2.3.3 +end +item: Remark +end +item: Remark + Text=When the version number changes, set the compiler +end +item: Remark + Text=vrbls _PYMAJOR_ and _PYMINOR_. +end +item: Remark + Text=Nothing in the script below should need fiddling then. +end +item: Remark + Text=Other things that need fiddling: +end +item: Remark + Text= PYVER_STRING above. +end +item: Remark + Text= The "Title:" in the upper left corner of the GUI. +end +item: Remark + Text= Build Settings and Version Resource on step 6 (Finish) of the Installation Expert +end +item: Remark + Text= Be sure to select Steps->All or you may not see these! +end +item: Remark +end +item: Remark + Text=When the version of Tcl/Tk changes, the compiler vrbl +end +item: Remark + Text=_TCLDIR_ may also need to be changed. +end +item: Remark +end +item: Set Variable + Variable=APPTITLE + Value=Python %PYVER_STRING% +end +item: Remark + Text=PY_VERSION should be major.minor only; used to create the registry key; must match MS_DLL_ID in python_nt.rc +end +item: Set Variable + Variable=PY_VERSION + Value=%_PYMAJOR_%.%_PYMINOR_% +end +item: Remark + Text=GROUP is the Start menu group name; user can override. +end +item: Set Variable + Variable=GROUP + Value=Python %PY_VERSION% + Flags=10000000 +end +item: Remark + Text=MAINDIR is the app directory; user can override. +end +item: Set Variable + Variable=MAINDIR + Value=Python%_PYMAJOR_%%_PYMINOR_% +end +item: Remark +end +item: Set Variable + Variable=DOADMIN + Value=%_DOADMIN_% +end +item: Remark + Text=Give non-admin users a chance to abort. +end +item: Check Configuration + Flags=10011111 +end +item: Set Variable + Variable=DOADMIN + Value=0 +end +item: Display Message + Title=Doing non-admin install + Text=The current login does not have Administrator Privileges on this machine. Python will install its registry information into the per-user area only for the current login, instead of into the per-machine area for every account on this machine. Some advanced uses of Python may not work as a result (for example, running a Python script as a service). + Text= + Text=If this is not what you want, please click Cancel to abort this installation, log on as an Administrator, and start the installation again. + Flags=00001000 +end +item: End Block +end +item: Remark +end +item: Remark + Text=BEGIN WIZARD STUFF ----------------------------------------------------------------------------------------------------------------------------- +end +item: Remark + Text=Note from Tim: the "stop" on the next line is actually "pause". +end +item: Open/Close INSTALL.LOG + Flags=00000001 +end +item: Remark + Text=If the destination system does not have a writable Windows\System directory, system files will be written to the Windows\ directory +end +item: Check if File/Dir Exists + Pathname=%SYS% + Flags=10000100 +end +item: Set Variable + Variable=SYS + Value=%WIN% +end +item: End Block +end +item: Check Configuration + Flags=10111011 +end +item: Get Registry Key Value + Variable=COMMON + Key=SOFTWARE\Microsoft\Windows\CurrentVersion + Default=C:\Program Files\Common Files + Value Name=CommonFilesDir + Flags=00000100 +end +item: Get Registry Key Value + Variable=PROGRAM_FILES + Key=SOFTWARE\Microsoft\Windows\CurrentVersion + Default=C:\Program Files + Value Name=ProgramFilesDir + Flags=00000100 +end +item: Set Variable + Variable=EXPLORER + Value=1 +end +item: End Block +end +item: Remark + Text=Note from Tim: The Wizard hardcod "C:" at the start of the replacement text for MAINDIR. +end +item: Remark + Text=That's not appropriate if the system drive doesn't happen to be C:. +end +item: Remark + Text=I removed the "C:", and that did the right thing for two people who tested it on non-C: machines, +end +item: Remark + Text=but it's unclear whether it will always do the right thing. +end +item: Set Variable + Variable=MAINDIR + Value=\%MAINDIR% + Flags=00001100 +end +item: Remark + Text=BACKUP is the variable that holds the path that all backup files will be copied to when overwritten +end +item: Set Variable + Variable=BACKUP + Value=%MAINDIR%\BACKUP + Flags=10000000 +end +item: Remark + Text=DOBACKUP determines if a backup will be performed. The possible values are A (do backup) or B (do not do backup) +end +item: Set Variable + Variable=DOBACKUP + Value=A +end +item: Remark + Text=BRANDING determines if the installation will be branded with a name and company. By default, this is written to the INST directory (installation media). +end +item: Set Variable + Variable=BRANDING + Value=0 +end +item: If/While Statement + Variable=BRANDING + Value=1 +end +item: Read INI Value + Variable=NAME + Pathname=%INST%\CUSTDATA.INI + Section=Registration + Item=Name +end +item: Read INI Value + Variable=COMPANY + Pathname=%INST%\CUSTDATA.INI + Section=Registration + Item=Company +end +item: If/While Statement + Variable=NAME +end +item: Set Variable + Variable=DOBRAND + Value=1 +end +item: Get System Information + Variable=NAME + Flags=00000110 +end +item: Get System Information + Variable=COMPANY + Flags=00000111 +end +item: End Block +end +item: End Block +end +item: Remark + Text=END WIZARD STUFF ----------------------------------------------------------------------------------------------------------------------------- +end +item: Remark +end +item: Remark + Text=Set vrbls for the "Advanced Options" subdialog of Components. +end +item: Set Variable + Variable=SELECT_ADMIN + Value=A +end +item: If/While Statement + Variable=DOADMIN + Value=0 +end +item: Set Variable + Variable=SELECT_ADMIN + Value=B +end +item: End Block +end +item: Remark +end +item: Remark + Text=TASKS values: +end +item: Remark + Text=A: Register file extensions +end +item: Remark + Text=B: Create Start Menu shortcuts +end +item: Remark + Text=C: Compile Python library files +end +item: Set Variable + Variable=TASKS + Value=AB +end +item: Remark +end +item: Remark + Text=COMPONENTS values: +end +item: Remark + Text=A: interpreter and libraries +end +item: Remark + Text=B: Tcl/Tk +end +item: Remark + Text=C: docs +end +item: Remark + Text=D: tools +end +item: Remark + Text=E: test suite +end +item: Set Variable + Variable=COMPONENTS + Value=ABCDE +end +item: Remark +end +item: Remark + Text=March thru the user GUI. +end +item: Wizard Block + Direction Variable=DIRECTION + Display Variable=DISPLAY + Bitmap Pathname=.\installer.bmp + X Position=9 + Y Position=10 + Filler Color=11173759 + Dialog=Select Destination Directory + Dialog=Backup Replaced Files + Dialog=Select Components + Dialog=Select Program Manager Group + Variable= + Variable= + Variable= + Variable=TASKS + Value= + Value= + Value= + Value=B + Compare=0 + Compare=0 + Compare=0 + Compare=3 + Flags=00000011 +end +item: If/While Statement + Variable=DISPLAY + Value=Start Installation +end +item: Set Variable + Variable=SUMMARY + Value=Install directory: %MAINDIR%%CRLF% +end +item: Remark +end +item: If/While Statement + Variable=SELECT_ADMIN + Value=A +end +item: Set Variable + Variable=SUMMARY + Value=%CRLF%Doing admin install.%CRLF% + Flags=00000001 +end +item: Else Statement +end +item: Set Variable + Variable=SUMMARY + Value=%CRLF%Doing non-admin install.%CRLF% + Flags=00000001 +end +item: End Block +end +item: Remark +end +item: If/While Statement + Variable=DOBACKUP + Value=A +end +item: Set Variable + Variable=SUMMARY + Value=%CRLF%Make backups, into %BACKUP%%CRLF% + Flags=00000001 +end +item: Else Statement +end +item: Set Variable + Variable=SUMMARY + Value=%CRLF%Don't make backups.%CRLF% + Flags=00000001 +end +item: End Block +end +item: Remark +end +item: Set Variable + Variable=SUMMARY + Value=%CRLF%Components:%CRLF% + Flags=00000001 +end +item: If/While Statement + Variable=COMPONENTS + Value=A + Flags=00000010 +end +item: Set Variable + Variable=SUMMARY + Value= Python interpreter and libraries%CRLF% + Flags=00000001 +end +item: End Block +end +item: If/While Statement + Variable=COMPONENTS + Value=B + Flags=00000010 +end +item: Set Variable + Variable=SUMMARY + Value= Tcl/Tk (Tkinter, IDLE, pydoc)%CRLF% + Flags=00000001 +end +item: End Block +end +item: If/While Statement + Variable=COMPONENTS + Value=C + Flags=00000010 +end +item: Set Variable + Variable=SUMMARY + Value= Python documentation%CRLF% + Flags=00000001 +end +item: End Block +end +item: If/While Statement + Variable=COMPONENTS + Value=D + Flags=00000010 +end +item: Set Variable + Variable=SUMMARY + Value= Tool and utility scripts%CRLF% + Flags=00000001 +end +item: End Block +end +item: If/While Statement + Variable=COMPONENTS + Value=E + Flags=00000010 +end +item: Set Variable + Variable=SUMMARY + Value= Python test suite%CRLF% + Flags=00000001 +end +item: End Block +end +item: Remark +end +item: If/While Statement + Variable=TASKS + Value=A + Flags=00000010 +end +item: Set Variable + Variable=SUMMARY + Value=%CRLF%Register file extensions.%CRLF% + Flags=00000001 +end +item: Else Statement +end +item: Set Variable + Variable=SUMMARY + Value=%CRLF%Don't register file extensions.%CRLF% + Flags=00000001 +end +item: End Block +end +item: Remark +end +item: Set Variable + Variable=SUMMARY + Value=%CRLF%Start Menu group: %GROUP%%CRLF% + Flags=00000001 +end +item: If/While Statement + Variable=TASKS + Value=B + Flags=00000010 +end +item: Else Statement +end +item: Set Variable + Variable=SUMMARY + Value=%CRLF%No Start Menu shortcuts.%CRLF% + Flags=00000001 +end +item: End Block +end +item: If/While Statement + Variable=TASKS + Value=C + Flags=00000010 +end +item: Set Variable + Variable=SUMMARY + Value=%CRLF%Compile Python files%CRLF% + Flags=00000001 +end +item: End Block +end +item: End Block +end +item: Remark +end +item: Custom Dialog Set + Name=Select Destination Directory + Display Variable=DISPLAY + item: Dialog + Title=%APPTITLE% Installation + Title French=Installation de %APPTITLE% + Title German=Installation von %APPTITLE% + Title Spanish=Instalación de %APPTITLE% + Title Italian=Installazione di %APPTITLE% + Width=339 + Height=280 + Font Name=Helv + Font Size=8 + item: Push Button + Rectangle=188 234 244 253 + Variable=DIRECTION + Value=N + Create Flags=01010000000000010000000000000001 + Text=&Next > + Text French=&Suite > + Text German=&Weiter > + Text Spanish=&Siguiente > + Text Italian=&Avanti > + end + item: Push Button + Rectangle=264 234 320 253 + Action=3 + Create Flags=01010000000000010000000000000000 + Text=&Cancel + Text French=&Annuler + Text German=&Abbrechen + Text Spanish=&Cancelar + Text Italian=&Annulla + end + item: Static + Rectangle=10 225 320 226 + Action=3 + Create Flags=01010000000000000000000000000111 + end + item: Static + Rectangle=108 11 323 33 + Create Flags=01010000000000000000000000000000 + Flags=0000000000000001 + Name=Times New Roman + Font Style=-24 0 0 0 700 255 0 0 0 3 2 1 18 + Text=Select Destination Directory + Text French=Sélectionner le répertoire de destination + Text German=Zielverzeichnis wählen + Text Spanish=Seleccione el directorio de destino + Text Italian=Selezionare Directory di destinazione + end + item: Listbox + Rectangle=108 58 321 219 + Variable=MAINDIR + Enabled Color=00000000000000001111111111111111 + Create Flags=01010000100000010000000101000001 + Flags=0000110000001010 + Text=%MAINDIR% + Text= + end + item: Static + Rectangle=108 40 313 58 + Enabled Color=00000000000000001111111111111111 + Create Flags=01010000000000000000000000000000 + Text=Please select a directory for the %APPTITLE% files. + end + end + item: Dialog + Title=Select Destination Directory + Title French=Sélectionner le répertoire de destination + Title German=Zielverzeichnis wählen + Title Spanish=Seleccione el directorio de destino + Title Italian=Selezionare Directory di destinazione + Width=276 + Height=216 + Font Name=Helv + Font Size=8 + item: Listbox + Rectangle=6 6 204 186 + Variable=MAINDIR + Create Flags=01010000100000010000000101000000 + Flags=0000110000100010 + Text=%MAINDIR% + Text French=%MAINDIR% + Text German=%MAINDIR% + Text Spanish=%MAINDIR% + Text Italian=%MAINDIR% + end + item: Push Button + Rectangle=209 8 265 26 + Create Flags=01010000000000010000000000000001 + Text=OK + Text French=OK + Text German=OK + Text Spanish=Aceptar + Text Italian=OK + end + item: Push Button + Rectangle=209 31 265 50 + Variable=MAINDIR + Value=%MAINDIR_SAVE% + Create Flags=01010000000000010000000000000000 + Flags=0000000000000001 + Text=Cancel + Text French=Annuler + Text German=Abbrechen + Text Spanish=Cancelar + Text Italian=Annulla + end + end +end +item: Custom Dialog Set + Name=Backup Replaced Files + Display Variable=DISPLAY + item: Dialog + Title=%APPTITLE% Installation + Title French=Fichiers de Sauvegarde Remplacés + Title German=Sicherungskopie von ersetzten Dateien erstellen + Title Portuguese=Ficheiros substituídos de segurança + Title Spanish=Copias de seguridad de los archivos reemplazados + Title Italian=Backup file sostituiti + Title Danish=Sikkerhedskopiering af erstattede filer + Title Dutch=Vervangen bestanden kopiëren + Title Norwegian=Sikkerhetskopiere erstattede filer + Title Swedish=Säkerhetskopiera utbytta filer + Width=350 + Height=280 + Font Name=Helv + Font Size=8 + item: Push Button + Rectangle=188 234 244 251 + Variable=DIRECTION + Value=N + Create Flags=01010000000000010000000000000001 + Text=&Next > + Text French=&Suivant> + Text German=&Weiter> + Text Portuguese=&Próximo> + Text Spanish=&Siguiente > + Text Italian=&Avanti > + Text Danish=&Næste> + Text Dutch=&Volgende> + Text Norwegian=&Neste> + Text Swedish=&Nästa > + end + item: Push Button + Rectangle=131 234 188 251 + Variable=DIRECTION + Value=B + Create Flags=01010000000000010000000000000000 + Text=< &Back + Text French=<&Retour + Text German=<&Zurück + Text Portuguese=<&Retornar + Text Spanish=<&Retroceder + Text Italian=< &Indietro + Text Danish=<&Tilbage + Text Dutch=<&Terug + Text Norwegian=<&Tilbake + Text Swedish=< &Tillbaka + end + item: Push Button + Rectangle=278 234 330 251 + Action=3 + Create Flags=01010000000000010000000000000000 + Text=Cancel + Text French=Annuler + Text German=Abbrechen + Text Portuguese=Cancelar + Text Spanish=Cancelar + Text Italian=Annulla + Text Danish=Annuller + Text Dutch=Annuleren + Text Norwegian=Avbryt + Text Swedish=Avbryt + end + item: Static + Rectangle=11 221 329 223 + Action=3 + Create Flags=01010000000000000000000000000111 + end + item: Static + Rectangle=108 46 320 98 + Create Flags=01010000000000000000000000000000 + Text=This installation program can create backup copies of all files replaced during the installation. These files will be used when the software is uninstalled and a rollback is requested. If backup copies are not created, you will only be able to uninstall the software and not roll the system back to a previous state. + Text= + Text=Do you want to create backups of replaced files? + Text French=Le programme d'installation peut créer des copies de sauvegarde de tous les fichiers remplacés pendant l'installation. Ces fichiers sont utilisés au cas où le logiciel est désinstallé et que l'on procède à la reprise du système. Si les copies de sauvegarde ne sont pas créées, on ne pourra que désinstaller le logiciel sans reprendre le système à un état précédent. Voulez-vous créer une sauvegarde des fichiers remplacés ? + Text German=Dieses Installationsprogramm kann Sicherungskopien von allen während der Installation ersetzten Dateien erstellen. Diese Dateien werden zur Rückgängigmachung der Installation und bei Anforderung eines Rollbacks verwendet. Ohne Sicherungskopien ist nur eine Rückgängigmachung der Installation möglich, nicht aber ein Rollback des Systems. Sicherungskopien der ersetzten Dateien erstellen? + Text Portuguese=Este programa de instalação pode criar cópias de segurança de todos os ficheiros substituídos durante a instalação. Estes ficheiros serão utilizados quando o programa for desinstalado e for requisitada uma retomada. Se as cópias de segurança não forem criadas, só poderá desinstalar o programa e não pode retomar um estado anterior do sistema. Deseja criar cópias de segurança dos ficheiros substituídos? + Text Spanish=Este programa de instalación puede crear copias de seguridad de todos los archivos reemplazados durante la instalación. Estos archivos se utilizarán cuando se desinstale el software y se solicite volver al estado anterior. Si no se crean copias de seguridad, únicamente podrá desinstalar el software y no podrá devolver el sistema al estado anterior. ¿Desea crear archivos de seguridad de los archivos reemplazados? + Text Italian=Questo programma di installazione può creare copie di backup di tutti i file sostituiti durante l’installazione. Questi file saranno usati quando il software sarà disinstallato e sarà richiesto un ritorno allo stato precedente. Se non crei le copie di backup, potrai solo disinstallare il software, ma non potrai riportare il sistema allo stato precedente. Vuoi creare i file di backup dei file sostituiti? + Text Danish=Dette installationsprogram kan oprette sikkerhedskopier af alle filer, som erstattes under installationen. Disse filer benyttes, når softwaren fjernes, og den tidligere systemkonfiguration genetableres. Hvis der ikke oprettes sikkerhedskopier, kan du kun fjerne den installerede software og ikke genetablere den tidligere systemkonfiguration. Vil du oprette sikkerhedskopier af filer, som erstattes? + Text Dutch=Dit installatieprogramma kan kopieën maken van alle bestanden die tijdens de installatie worden vervangen. Deze worden dan gebruikt als de software-installatie ongedaan wordt gemaakt en u het systeem wilt laten terugkeren naar de oorspronkelijke staat. Als er geen back-up kopieën worden gemaakt, kunt u de software enkel verwijderen maar het systeem niet in de oorspronkelijke staat terugbrengen. Wilt u een back-up maken van de vervangen bestanden? + Text Norwegian=Dette installasjonsprogrammet kan lage sikkerhetskopier av alle filer som blir erstattet under installasjonen. Disse filene vil tas i bruk når programvaren er avinstallert og det er behov for tilbakestilling. Hvis det ikke er laget sikkerhetskopier, kan du kun avinstallere programvaren og ikke stille systemet tilbake til tidligere status. Ønsker du å lage sikkerhetskopier av de filene som blir erstattet nå? + Text Swedish=Installationsprogrammet kan skapa säkerhetskopior av alla filer som byts ut under installationen. Dessa filer kan sedan användas när programvaran avinstalleras och du begär rollback. Om du då inte har några säkerhetskopior kan du bara avinstallera programvaran, inte återskapa systemet i dess tidigare skick. Vill du göra säkerhetskopior av de ersatta filerna? + end + item: Radio Button + Rectangle=141 106 265 136 + Variable=DOBACKUP + Create Flags=01010000000000010000000000001001 + Text=&Yes, make backups + Text=N&o, do not make backups + Text= + Text French=&Oui + Text French=N&on + Text French= + Text German=&Ja + Text German=N&ein + Text German= + Text Portuguese=&Sim + Text Portuguese=Nã&o + Text Portuguese= + Text Spanish=&Sí + Text Spanish=N&o + Text Spanish= + Text Italian=&Sì + Text Italian=N&o + Text Italian= + Text Danish=&Ja + Text Danish=&Nej + Text Danish= + Text Dutch=&Ja + Text Dutch=N&ee + Text Dutch= + Text Norwegian=&Ja + Text Norwegian=&Nei + Text Norwegian= + Text Swedish=&Ja + Text Swedish=N&ej + Text Swedish= + end + item: Static + Control Name=BACK2 + Rectangle=108 173 320 208 + Action=1 + Create Flags=01010000000000000000000000000111 + Text=Backup File Destination Directory + Text French=Répertoire de destination des fichiers de sauvegarde + Text German=Zielverzeichnis für die Sicherungsdatei + Text Portuguese=Directório de destino de ficheiro de segurança + Text Spanish=Directorio de Destino de los Archivos de Seguridad + Text Italian=Directory di destinazione dei file di backup + Text Danish=Destinationsbibliotek til sikkerhedskopier + Text Dutch=Doeldirectory backup-bestand + Text Norwegian=Målkatalog for sikkerhetskopier + Text Swedish=Katalog för säkerhetskopierade filer + end + item: Push Button + Control Name=BACK3 + Rectangle=265 185 318 203 + Variable=BACKUP_SAVE + Value=%BACKUP% + Destination Dialog=1 + Action=2 + Create Flags=01010000000000010000000000000000 + Text=B&rowse... + Text French=P&arcourir + Text German=B&lättern... + Text Portuguese=P&rocurar + Text Spanish=V&isualizar... + Text Italian=Sfoglia... + Text Danish=&Gennemse... + Text Dutch=B&laderen... + Text Norwegian=Bla igjennom + Text Swedish=&Bläddra + end + item: Static + Control Name=BACK4 + Rectangle=129 188 254 200 + Destination Dialog=2 + Create Flags=01010000000000000000000000000000 + Text=%BACKUP% + Text French=%BACKUP% + Text German=%BACKUP% + Text Portuguese=%BACKUP% + Text Spanish=%BACKUP% + Text Italian=%BACKUP% + Text Danish=%BACKUP% + Text Dutch=%BACKUP% + Text Norwegian=%BACKUP% + Text Swedish=%BACKUP% + end + item: Static + Rectangle=108 11 323 36 + Create Flags=01010000000000000000000000000000 + Flags=0000000000000001 + Name=Times New Roman + Font Style=-24 0 0 0 700 255 0 0 0 3 2 1 18 + Text=Backup Replaced Files + Text French=Sélectionner les composants + Text German=Komponenten auswählen + Text Spanish=Seleccione componentes + Text Italian=Selezionare i componenti + end + item: If/While Statement + Variable=DOBACKUP + Value=B + end + item: Set Control Attribute + Control Name=BACK3 + Operation=1 + end + item: Set Control Attribute + Control Name=BACK4 + Operation=1 + end + item: Else Statement + end + item: Set Control Attribute + Control Name=BACK3 + end + item: Set Control Attribute + Control Name=BACK4 + end + item: End Block + end + end + item: Dialog + Title=Select Destination Directory + Title French=Choisissez le répertoire de destination + Title German=Zielverzeichnis wählen + Title Portuguese=Seleccionar Directório de Destino + Title Spanish=Seleccione el Directorio de Destino + Title Italian=Seleziona Directory di destinazione + Title Danish=Vælg Destinationsbibliotek + Title Dutch=Kies Doeldirectory + Title Norwegian=Velg målkatalog + Title Swedish=Välj destinationskalatog + Width=276 + Height=216 + Font Name=Helv + Font Size=8 + item: Listbox + Rectangle=6 3 200 186 + Variable=BACKUP + Create Flags=01010000100000010000000101000000 + Flags=0000110000100010 + Text=%BACKUP% + Text= + Text French=%BACKUP% + Text French= + Text German=%BACKUP% + Text German= + Text Portuguese=%BACKUP% + Text Portuguese= + Text Spanish=%BACKUP% + Text Spanish= + Text Italian=%BACKUP% + Text Italian= + Text Danish=%BACKUP% + Text Danish= + Text Dutch=%BACKUP% + Text Dutch= + Text Norwegian=%BACKUP% + Text Norwegian= + Text Swedish=%BACKUP% + Text Swedish= + end + item: Push Button + Rectangle=209 8 265 26 + Create Flags=01010000000000010000000000000001 + Text=OK + Text French=OK + Text German=OK + Text Portuguese=OK + Text Spanish=ACEPTAR + Text Italian=OK + Text Danish=OK + Text Dutch=OK + Text Norwegian=OK + Text Swedish=OK + end + item: Push Button + Rectangle=209 31 265 50 + Variable=BACKUP + Value=%BACKUP_SAVE% + Create Flags=01010000000000010000000000000000 + Flags=0000000000000001 + Text=Cancel + Text French=Annuler + Text German=Abbrechen + Text Portuguese=Cancelar + Text Spanish=Cancelar + Text Italian=Annulla + Text Danish=Slet + Text Dutch=Annuleren + Text Norwegian=Avbryt + Text Swedish=Avbryt + end + end +end +item: Custom Dialog Set + Name=Select Components + Display Variable=DISPLAY + item: Dialog + Title=%APPTITLE% Installation + Title French=Installation de %APPTITLE% + Title German=Installation von %APPTITLE% + Title Spanish=Instalación de %APPTITLE% + Title Italian=Installazione di %APPTITLE% + Width=339 + Height=280 + Font Name=Helv + Font Size=8 + item: Push Button + Rectangle=188 234 244 253 + Variable=DIRECTION + Value=N + Create Flags=01010000000000010000000000000001 + Text=&Next > + Text French=&Suite > + Text German=&Weiter > + Text Spanish=&Siguiente > + Text Italian=&Avanti > + end + item: Push Button + Rectangle=131 234 188 253 + Variable=DIRECTION + Value=B + Create Flags=01010000000000010000000000000000 + Text=< &Back + Text French=< &Retour + Text German=< &Zurück + Text Spanish=< &Atrás + Text Italian=< &Indietro + end + item: Push Button + Rectangle=264 234 320 253 + Action=3 + Create Flags=01010000000000010000000000000000 + Text=&Cancel + Text French=&Annuler + Text German=&Abbrechen + Text Spanish=&Cancelar + Text Italian=&Annulla + end + item: Checkbox + Rectangle=108 66 313 156 + Variable=COMPONENTS + Create Flags=01010000000000010000000000000011 + Flags=0000000000000110 + Text=Python interpreter and libraries + Text=Tcl/Tk (Tkinter, IDLE, pydoc) + Text=Python HTMLHelp file + Text=Python utility scripts (Tools/) + Text=Python test suite (Lib/test/) + Text= + Text French=Python interpreter, library and IDLE + Text French=Python HTML docs + Text French=Python utility scripts (Tools/) + Text French=Python test suite (Lib/test/) + Text French= + Text German=Python interpreter, library and IDLE + Text German=Python HTML docs + Text German=Python utility scripts (Tools/) + Text German=Python test suite (Lib/test/) + Text German= + Text Spanish=Python interpreter, library and IDLE + Text Spanish=Python HTML docs + Text Spanish=Python utility scripts (Tools/) + Text Spanish=Python test suite (Lib/test/) + Text Spanish= + Text Italian=Python interpreter, library and IDLE + Text Italian=Python HTML docs + Text Italian=Python utility scripts (Tools/) + Text Italian=Python test suite (Lib/test/) + Text Italian= + end + item: Static + Rectangle=108 45 320 63 + Create Flags=01010000000000000000000000000000 + Text=Choose which components to install by checking the boxes below. + Text French=Choisissez les composants que vous voulez installer en cochant les cases ci-dessous. + Text German=Wählen Sie die zu installierenden Komponenten, indem Sie in die entsprechenden Kästchen klicken. + Text Spanish=Elija los componentes que desee instalar marcando los cuadros de abajo. + Text Italian=Scegliere quali componenti installare selezionando le caselle sottostanti. + end + item: Push Button + Rectangle=188 203 269 220 + Destination Dialog=1 + Action=2 + Enabled Color=00000000000000000000000011111111 + Create Flags=01010000000000010000000000000000 + Text=Advanced Options ... + end + item: Static + Rectangle=10 225 320 226 + Action=3 + Create Flags=01010000000000000000000000000111 + end + item: Static + Rectangle=108 10 323 43 + Create Flags=01010000000000000000000000000000 + Flags=0000000000000001 + Name=Times New Roman + Font Style=-24 0 0 0 700 255 0 0 0 3 2 1 18 + Text=Select Components + Text French=Sélectionner les composants + Text German=Komponenten auswählen + Text Spanish=Seleccione componentes + Text Italian=Selezionare i componenti + end + item: Static + Rectangle=251 180 311 193 + Variable=COMPONENTS + Value=MAINDIR + Create Flags=01010000000000000000000000000010 + end + item: Static + Rectangle=251 168 311 179 + Variable=COMPONENTS + Create Flags=01010000000000000000000000000010 + end + item: Static + Rectangle=123 168 234 181 + Create Flags=01010000000000000000000000000000 + Text=Disk Space Required: + Text French=Espace disque requis : + Text German=Notwendiger Speicherplatz: + Text Spanish=Espacio requerido en el disco: + Text Italian=Spazio su disco necessario: + end + item: Static + Rectangle=123 180 234 193 + Create Flags=01010000000000000000000000000000 + Text=Disk Space Remaining: + Text French=Espace disque disponible : + Text German=Verbleibender Speicherplatz: + Text Spanish=Espacio en disco disponible: + Text Italian=Spazio su disco disponibile: + end + item: Static + Rectangle=108 158 320 196 + Action=1 + Create Flags=01010000000000000000000000000111 + end + item: If/While Statement + Variable=DLG_EVENT_TYPE + Value=VERIFY + end + item: Remark + Text=If they're installing Tcl/Tk, Tools, or the test suite, doesn't make much sense unless they're installing Python too. + end + item: If/While Statement + Variable=COMPONENTS + Value=BDE + Flags=00001010 + end + item: If/While Statement + Variable=COMPONENTS + Value=A + Flags=00000011 + end + item: Display Message + Title=Are you sure? + Text=Installing Tcl/Tk, Tools or the test suite doesn't make much sense unless you install the Python interpreter and libraries too. + Text= + Text=Click Yes if that's really what you want. + Flags=00101101 + end + item: Remark + Text=Nothing -- just proceed to the next dialog. + end + item: Else Statement + end + item: Remark + Text=Return to the dialog. + end + item: Set Variable + Variable=DLG_EVENT_TYPE + end + item: End Block + end + item: End Block + end + item: End Block + end + item: End Block + end + end + item: Dialog + Title=Advanced Options + Width=339 + Height=213 + Font Name=Helv + Font Size=8 + item: Radio Button + Control Name=ADMIN2 + Rectangle=11 46 90 76 + Variable=SELECT_ADMIN + Enabled Color=00000000000000001111111111111111 + Create Flags=01010000000000010000000000001001 + Text=Admin install + Text=Non-Admin installl + Text= + end + item: Push Button + Rectangle=188 170 244 189 + Variable=DIRECTION + Value=N + Create Flags=01010000000000010000000000000001 + Text=OK + Text French=&Suite > + Text German=&Weiter > + Text Spanish=&Siguiente > + Text Italian=&Avanti > + end + item: Static + Rectangle=5 3 326 83 + Action=1 + Enabled Color=00000000000000001111111111111111 + Create Flags=01010000000000000000000000000111 + end + item: Static + Control Name=ADMIN1 + Rectangle=11 11 321 45 + Enabled Color=00000000000000001111111111111111 + Create Flags=01010000000000000000000000000000 + Text=By default, the install records settings in the per-machine area of the registry (HKLM), and installs the Python and C runtime DLLs to %SYS32%. Choose "Non-Admin install" if you would prefer settings made in the per-user registry (HKCU), and DLLs installed in %MAINDIR%. + end + item: Static + Rectangle=5 90 326 157 + Action=1 + Enabled Color=00000000000000001111111111111111 + Create Flags=01010000000000000000000000000111 + end + item: Checkbox + Rectangle=11 121 243 151 + Variable=TASKS + Enabled Color=00000000000000001111111111111111 + Create Flags=01010000000000010000000000000011 + Text=Register file extensions (.py, .pyw, .pyc, .pyo) + Text=Create Start Menu shortcuts + Text=Compile Python files to .pyc + Text= + end + item: Static + Rectangle=11 103 320 121 + Enabled Color=00000000000000001111111111111111 + Create Flags=01010000000000000000000000000000 + Text=Choose tasks to perform by checking the boxes below. + end + item: If/While Statement + Variable=DLG_EVENT_TYPE + Value=INIT + end + item: If/While Statement + Variable=DOADMIN + Value=1 + end + item: Set Control Attribute + Control Name=ADMIN2 + end + item: Else Statement + end + item: Set Control Text + Control Name=ADMIN1 + Control Text=This section is available only if logged in to an account with Administrator privileges. + end + item: Set Control Attribute + Control Name=ADMIN2 + Operation=1 + end + item: End Block + end + item: End Block + end + end +end +item: Custom Dialog Set + Name=Select Program Manager Group + Display Variable=DISPLAY + item: Dialog + Title=%APPTITLE% Installation + Title French=Installation de %APPTITLE% + Title German=Installation von %APPTITLE% + Title Spanish=Instalación de %APPTITLE% + Title Italian=Installazione di %APPTITLE% + Width=339 + Height=280 + Font Name=Helv + Font Size=8 + item: Push Button + Rectangle=188 234 244 253 + Variable=DIRECTION + Value=N + Create Flags=01010000000000010000000000000001 + Text=&Next > + Text French=&Suite > + Text German=&Weiter > + Text Spanish=&Siguiente > + Text Italian=&Avanti > + end + item: Push Button + Rectangle=131 234 188 253 + Variable=DIRECTION + Value=B + Create Flags=01010000000000010000000000000000 + Flags=0000000000000001 + Text=< &Back + Text French=< &Retour + Text German=< &Zurück + Text Spanish=< &Atrás + Text Italian=< &Indietro + end + item: Push Button + Rectangle=264 234 320 253 + Action=3 + Create Flags=01010000000000010000000000000000 + Text=&Cancel + Text French=&Annuler + Text German=&Abbrechen + Text Spanish=&Cancelar + Text Italian=&Annulla + end + item: Static + Rectangle=10 225 320 226 + Action=3 + Create Flags=01010000000000000000000000000111 + end + item: Static + Rectangle=108 10 323 53 + Create Flags=01010000000000000000000000000000 + Flags=0000000000000001 + Name=Times New Roman + Font Style=-24 0 0 0 700 255 0 0 0 3 2 1 18 + Text=Select Start Menu Group + Text French=Sélectionner le groupe du Gestionnaire de programme + Text German=Bestimmung der Programm-Managergruppe + Text Spanish=Seleccione grupo del Administrador de programas + Text Italian=Selezionare il gruppo ProgMan + end + item: Static + Rectangle=108 35 320 65 + Create Flags=01010000000000000000000000000000 + Text=Enter the name of the Start Menu program group to which to add the %APPTITLE% icons: + Text French=Entrez le nom du groupe du Gestionnaire de programme dans lequel vous souhaitez ajouter les icônes de %APPTITLE% : + Text German=Geben Sie den Namen der Programmgruppe ein, der das Symbol %APPTITLE% hinzugefügt werden soll: + Text Spanish=Escriba el nombre del grupo del Administrador de programas en el que desea agregar los iconos de %APPTITLE%: + Text Italian=Inserire il nome del gruppo Program Manager per aggiungere le icone %APPTITLE% a: + end + item: Combobox + Rectangle=108 56 320 219 + Variable=GROUP + Create Flags=01010000001000010000001100000001 + Flags=0000000000000001 + Text=%GROUP% + Text= + Text French=%GROUP% + Text German=%GROUP% + Text Spanish=%GROUP% + Text Italian=%GROUP% + end + end +end +item: Custom Dialog Set + Name=Start Installation + Display Variable=DISPLAY + item: Dialog + Title=%APPTITLE% Installation + Title French=Installation de %APPTITLE% + Title German=Installation von %APPTITLE% + Title Spanish=Instalación de %APPTITLE% + Title Italian=Installazione di %APPTITLE% + Width=339 + Height=280 + Font Name=Helv + Font Size=8 + item: Push Button + Rectangle=188 234 244 253 + Variable=DIRECTION + Value=N + Create Flags=01010000000000010000000000000001 + Text=&Next > + Text French=&Suite > + Text German=&Weiter > + Text Spanish=&Siguiente > + Text Italian=&Avanti > + end + item: Push Button + Rectangle=131 234 188 253 + Variable=DIRECTION + Value=B + Create Flags=01010000000000010000000000000000 + Text=< &Back + Text French=< &Retour + Text German=< &Zurück + Text Spanish=< &Atrás + Text Italian=< &Indietro + end + item: Push Button + Rectangle=264 234 320 253 + Action=3 + Create Flags=01010000000000010000000000000000 + Text=&Cancel + Text French=&Annuler + Text German=&Abbrechen + Text Spanish=&Cancelar + Text Italian=&Annulla + end + item: Static + Rectangle=10 225 320 226 + Action=3 + Create Flags=01010000000000000000000000000111 + end + item: Static + Rectangle=108 10 323 53 + Create Flags=01010000000000000000000000000000 + Flags=0000000000000001 + Name=Times New Roman + Font Style=-24 0 0 0 700 255 0 0 0 3 2 1 18 + Text=Ready to Install! + Text French=Prêt à installer ! + Text German=Installationsbereit! + Text Spanish=¡Preparado para la instalación! + Text Italian=Pronto per l'installazione! + end + item: Static + Rectangle=108 40 320 62 + Create Flags=01010000000000000000000000000000 + Text=Click the Next button to install %APPTITLE%, or the Back button to change choices: + Text French=Vous êtes maintenant prêt à installer les fichiers %APPTITLE%. + Text French= + Text French=Cliquez sur le bouton Suite pour commencer l'installation ou sur le bouton Retour pour entrer les informations d'installation à nouveau. + Text German=Sie können %APPTITLE% nun installieren. + Text German= + Text German=Klicken Sie auf "Weiter", um mit der Installation zu beginnen. Klicken Sie auf "Zurück", um die Installationsinformationen neu einzugeben. + Text Spanish=Ya está listo para instalar %APPTITLE%. + Text Spanish= + Text Spanish=Presione el botón Siguiente para comenzar la instalación o presione Atrás para volver a ingresar la información para la instalación. + Text Italian=Ora è possibile installare %APPTITLE%. + Text Italian= + Text Italian=Premere il pulsante Avanti per avviare l'installazione o il pulsante Indietro per reinserire le informazioni di installazione. + end + item: Editbox + Rectangle=108 66 324 219 + Help Context=16711681 + Enabled Color=00000000000000001111111111111111 + Create Flags=01010000100000000001100011000100 + Text=%SUMMARY% + end + end +end +item: Remark +end +item: If/While Statement + Variable=DISPLAY + Value=Select Destination Directory +end +item: Remark + Text=User may have changed MAINDIR, so reset BACKUP to match. +end +item: Set Variable + Variable=BACKUP + Value=%MAINDIR%\BACKUP +end +item: End Block +end +item: Remark +end +item: End Block +end +item: Remark +end +item: Remark + Text=BEGIN WIZARD STUFF ----------------------------------------------------------------------------------------------------------------------------- +end +item: Remark + Text=When the BACKUP feature is enabled, the BACKUPDIR is initialized +end +item: If/While Statement + Variable=DOBACKUP + Value=A +end +item: Set Variable + Variable=BACKUPDIR + Value=%BACKUP% +end +item: End Block +end +item: Remark + Text=The BRANDING information is written to the INI file on the installation media. +end +item: If/While Statement + Variable=BRANDING + Value=1 +end +item: If/While Statement + Variable=DOBRAND + Value=1 +end +item: Edit INI File + Pathname=%INST%\CUSTDATA.INI + Settings=[Registration] + Settings=NAME=%NAME% + Settings=COMPANY=%COMPANY% + Settings= +end +item: End Block +end +item: End Block +end +item: Remark + Text=Begin writing to the INSTALL.LOG +end +item: Open/Close INSTALL.LOG +end +item: Remark + Text=Check free disk space calculates free disk space as well as component sizes. +end +item: Remark + Text=It should be located before all Install File actions. +end +item: Check Disk Space + Component=COMPONENTS +end +item: Remark + Text=This include script allows uninstall support +end +item: Remark + Text=Note from Tim: this is our own Uninstal.wse, a copy of Wise's except +end +item: Remark + Text=it writes to HKCU (instead of HKLM) if the user doesn't have admin privs. +end +item: Include Script + Pathname=.\Uninstal.wse +end +item: Remark + Text=Note from Tim: these seeming no-ops actually convert to short filenames. +end +item: Set Variable + Variable=COMMON + Value=%COMMON% + Flags=00010100 +end +item: Set Variable + Variable=MAINDIR + Value=%MAINDIR% + Flags=00010100 +end +item: Remark + Text=This IF/THEN/ELSE reads the correct registry entries for shortcut/icon placement +end +item: Check Configuration + Flags=10111011 +end +item: Get Registry Key Value + Variable=STARTUPDIR + Key=Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders + Default=%WIN%\Start Menu\Programs\StartUp + Value Name=StartUp + Flags=00000010 +end +item: Get Registry Key Value + Variable=DESKTOPDIR + Key=Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders + Default=%WIN%\Desktop + Value Name=Desktop + Flags=00000010 +end +item: Get Registry Key Value + Variable=STARTMENUDIR + Key=Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders + Default=%WIN%\Start Menu + Value Name=Start Menu + Flags=00000010 +end +item: Get Registry Key Value + Variable=GROUPDIR + Key=Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders + Default=%WIN%\Start Menu\Programs + Value Name=Programs + Flags=00000010 +end +item: Get Registry Key Value + Variable=CSTARTUPDIR + Key=Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders + Default=%STARTUPDIR% + Value Name=Common Startup + Flags=00000100 +end +item: Get Registry Key Value + Variable=CDESKTOPDIR + Key=Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders + Default=%DESKTOPDIR% + Value Name=Common Desktop + Flags=00000100 +end +item: Get Registry Key Value + Variable=CSTARTMENUDIR + Key=Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders + Default=%STARTMENUDIR% + Value Name=Common Start Menu + Flags=00000100 +end +item: Get Registry Key Value + Variable=CGROUPDIR + Key=Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders + Default=%GROUPDIR% + Value Name=Common Programs + Flags=00000100 +end +item: Else Statement +end +item: Remark + Text=Note from Tim: the Wizard left this block empty! +end +item: Remark + Text=Perhaps it's only relevant on Windows 3.1. +end +item: End Block +end +item: Remark + Text=END WIZARD STUFF ----------------------------------------------------------------------------------------------------------------------------- +end +item: Remark +end +item: If/While Statement + Variable=SELECT_ADMIN + Value=B +end +item: Remark + Text=The user chose a non-admin install in "Advanced Options". +end +item: Remark + Text=This should come after the include of Uninstal.wse above, because +end +item: Remark + Text=writing uninstall info to HKCU is ineffective except under Win2K. +end +item: Set Variable + Variable=DOADMIN + Value=0 +end +item: End Block +end +item: Remark +end +item: Set Variable + Variable=CGROUP_SAVE + Value=%GROUP% +end +item: If/While Statement + Variable=TASKS + Value=B + Flags=00000010 +end +item: If/While Statement + Variable=DOADMIN + Value=1 +end +item: Set Variable + Variable=GROUP + Value=%CGROUPDIR%\%GROUP% +end +item: Else Statement +end +item: Set Variable + Variable=GROUP + Value=%GROUPDIR%\%GROUP% +end +item: End Block +end +item: End Block +end +item: Remark +end +item: Remark + Text=Long section to install files. +end +item: Remark +end +item: If/While Statement + Variable=DOADMIN + Value=1 +end +item: Set Variable + Variable=DLLDEST + Value=%SYS32% +end +item: Else Statement +end +item: Set Variable + Variable=DLLDEST + Value=%MAINDIR% +end +item: End Block +end +item: Remark +end +item: Remark + Text=Install the license even if they deselect everything . +end +item: Install File + Source=..\license + Destination=%MAINDIR%\LICENSE.txt + Flags=0000000000000010 +end +item: Install File + Source=..\readme + Destination=%MAINDIR%\README.txt + Flags=0000000000000010 +end +item: Install File + Source=..\misc\news + Destination=%MAINDIR%\NEWS.txt + Flags=0000000000000010 +end +item: Remark + Text=Icons -- always install so that the uninstaller can use them for its own display. +end +item: Install File + Source=..\pc\pycon.ico + Destination=%MAINDIR%\pycon.ico + Flags=0000000010000010 +end +item: Install File + Source=..\pc\pyc.ico + Destination=%MAINDIR%\pyc.ico + Flags=0000000010000010 +end +item: Install File + Source=..\pc\py.ico + Destination=%MAINDIR%\py.ico + Flags=0000000010000010 +end +item: Remark +end +item: Remark + Text=These arrange to (recursively!) delete all .pyc and .pyo files at uninstall time. +end +item: Remark + Text=This "does the right thing": any directories left empty at the end are removed. +end +item: Add Text to INSTALL.LOG + Text=File Tree: %MAINDIR%\*.pyc +end +item: Add Text to INSTALL.LOG + Text=File Tree: %MAINDIR%\*.pyo +end +item: Remark +end +item: Remark + Text=A: interpreter and libraries +end +item: If/While Statement + Variable=COMPONENTS + Value=A + Flags=00000010 +end +item: Remark + Text=Executables +end +item: Install File + Source=.\python.exe + Destination=%MAINDIR%\python.exe + Flags=0000000000000010 +end +item: Install File + Source=.\pythonw.exe + Destination=%MAINDIR%\pythonw.exe + Flags=0000000000000010 +end +item: Install File + Source=.\w9xpopen.exe + Destination=%MAINDIR%\w9xpopen.exe + Flags=0000000000000010 +end +item: Remark +end +item: Remark + Text=Extension module DLLs (.pyd); keep in synch with libs directory next +end +item: Install File + Source=.\_winreg.pyd + Destination=%MAINDIR%\DLLs\_winreg.pyd + Description=Extension modules + Flags=0000000000000010 +end +item: Install File + Source=.\_csv.pyd + Destination=%MAINDIR%\DLLs\_csv.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\_sre.pyd + Destination=%MAINDIR%\DLLs\_sre.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\_ssl.pyd + Destination=%MAINDIR%\DLLs\_ssl.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\_symtable.pyd + Destination=%MAINDIR%\DLLs\_symtable.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\_testcapi.pyd + Destination=%MAINDIR%\DLLs\_testcapi.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\_tkinter.pyd + Destination=%MAINDIR%\DLLs\_tkinter.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\_socket.pyd + Destination=%MAINDIR%\DLLs\_socket.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\_bsddb.pyd + Destination=%MAINDIR%\DLLs\_bsddb.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\bz2.pyd + Destination=%MAINDIR%\DLLs\bz2.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\datetime.pyd + Destination=%MAINDIR%\DLLs\datetime.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\mmap.pyd + Destination=%MAINDIR%\DLLs\mmap.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\parser.pyd + Destination=%MAINDIR%\DLLs\parser.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\pyexpat.pyd + Destination=%MAINDIR%\DLLs\pyexpat.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\select.pyd + Destination=%MAINDIR%\DLLs\select.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\unicodedata.pyd + Destination=%MAINDIR%\DLLs\unicodedata.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\winsound.pyd + Destination=%MAINDIR%\DLLs\winsound.pyd + Flags=0000000000000010 +end +item: Install File + Source=.\zlib.pyd + Destination=%MAINDIR%\DLLs\zlib.pyd + Flags=0000000000000010 +end +item: Remark +end +item: Remark + Text=Link libraries (.lib); keep in synch with DLLs above, except that the Python lib lives here. +end +item: Install File + Source=.\_winreg.lib + Destination=%MAINDIR%\libs\_winreg.lib + Description=Link library files + Flags=0000000000000010 +end +item: Install File + Source=.\_csv.lib + Destination=%MAINDIR%\libs\_csv.lib + Flags=0000000000000010 +end +item: Install File + Source=.\_sre.lib + Destination=%MAINDIR%\libs\_sre.lib + Flags=0000000000000010 +end +item: Install File + Source=.\_ssl.lib + Destination=%MAINDIR%\libs\_ssl.lib + Flags=0000000000000010 +end +item: Install File + Source=.\_symtable.lib + Destination=%MAINDIR%\libs\_symtable.lib + Flags=0000000000000010 +end +item: Install File + Source=.\_testcapi.lib + Destination=%MAINDIR%\libs\_testcapi.lib + Flags=0000000000000010 +end +item: Install File + Source=.\_tkinter.lib + Destination=%MAINDIR%\libs\_tkinter.lib + Description=Extension modules + Flags=0000000000000010 +end +item: Install File + Source=.\_socket.lib + Destination=%MAINDIR%\libs\_socket.lib + Flags=0000000000000010 +end +item: Install File + Source=.\_bsddb.lib + Destination=%MAINDIR%\libs\_bsddb.lib + Flags=0000000000000010 +end +item: Install File + Source=.\bz2.lib + Destination=%MAINDIR%\libs\bz2.lib + Flags=0000000000000010 +end +item: Install File + Source=.\datetime.lib + Destination=%MAINDIR%\libs\datetime.lib + Flags=0000000000000010 +end +item: Install File + Source=.\mmap.lib + Destination=%MAINDIR%\libs\mmap.lib + Flags=0000000000000010 +end +item: Install File + Source=.\parser.lib + Destination=%MAINDIR%\libs\parser.lib + Flags=0000000000000010 +end +item: Install File + Source=.\pyexpat.lib + Destination=%MAINDIR%\libs\pyexpat.lib + Flags=0000000000000010 +end +item: Install File + Source=.\select.lib + Destination=%MAINDIR%\libs\select.lib + Flags=0000000000000010 +end +item: Install File + Source=.\unicodedata.lib + Destination=%MAINDIR%\libs\unicodedata.lib + Flags=0000000000000010 +end +item: Install File + Source=.\winsound.lib + Destination=%MAINDIR%\libs\winsound.lib + Flags=0000000000000010 +end +item: Install File + Source=.\zlib.lib + Destination=%MAINDIR%\libs\zlib.lib + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=.\python%_pymajor_%%_pyminor_%.lib + Destination=%MAINDIR%\libs\python%_PYMAJOR_%%_PYMINOR_%.lib + Flags=0000000000000010 +end +item: Remark +end +item: Remark + Text=Main Python DLL +end +item: Remark + Text=Tell Wise it's OK to delete the Python DLL at uninstall time, +end +item: Remark + Text=despite that we (may) write it into a system directory. +end +item: Add Text to INSTALL.LOG + Text=Non-System File: +end +item: Install File + Source=.\python%_pymajor_%%_pyminor_%.dll + Destination=%DLLDEST%\python%_PYMAJOR_%%_PYMINOR_%.dll + Flags=0000000000000010 +end +item: Remark +end +item: Remark + Text=Libraries (Lib/) +end +item: Install File + Source=..\lib\*.py + Destination=%MAINDIR%\Lib + Description=Library Modules + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\lib\bsddb\*.py + Destination=%MAINDIR%\Lib\bsddb + Description=Berkeley database package + Flags=0000000100000010 +end +item: Remark +end +item: Install File + Source=..\lib\compiler\*.py + Destination=%MAINDIR%\Lib\compiler + Description=Python compiler written in Python + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\lib\distutils\*.py + Destination=%MAINDIR%\Lib\distutils + Description=Distribution utility modules + Flags=0000000000000010 +end +item: Install File + Source=..\lib\distutils\readme + Destination=%MAINDIR%\Lib\distutils\README.txt + Flags=0000000000000010 +end +item: Install File + Source=..\lib\distutils\command\*.py + Destination=%MAINDIR%\Lib\distutils\command + Flags=0000000000000010 +end +item: Install File + Source=..\lib\distutils\command\wininst.exe + Destination=%MAINDIR%\Lib\distutils\command\wininst.exe + Flags=0000000000000010 +end +item: Install File + Source=..\lib\distutils\command\command_template + Destination=%MAINDIR%\Lib\distutils\command\command_template + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\lib\email\*.py + Destination=%MAINDIR%\Lib\email + Description=Library email package + Flags=0000000000000010 +end +item: Install File + Source=..\lib\email\test\*.py + Destination=%MAINDIR%\Lib\email\test + Description=email tests + Flags=0000000000000010 +end +item: Install File + Source=..\lib\email\test\data\*.txt + Destination=%MAINDIR%\Lib\email\test\data + Description=email test data + Flags=0000000000000010 +end +item: Install File + Source=..\lib\email\test\data\*.gif + Destination=%MAINDIR%\Lib\email\test\data + Description=email test data + Flags=0000000000000010 +end +item: Install File + Source=..\lib\email\test\data\*.au + Destination=%MAINDIR%\Lib\email\test\data + Description=email test data + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\lib\encodings\*.py + Destination=%MAINDIR%\Lib\encodings + Description=Unicode encoding tables + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\lib\hotshot\*.py + Destination=%MAINDIR%\Lib\hotshot + Description=Fast Python profiler + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\lib\lib-old\*.py + Destination=%MAINDIR%\Lib\lib-old + Description=Obsolete modules + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\lib\lib-tk\*.py + Destination=%MAINDIR%\Lib\lib-tk + Description=Tkinter related library modules + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\lib\logging\*.py + Destination=%MAINDIR%\Lib\logging + Description=Logging package + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\lib\site-packages\readme + Destination=%MAINDIR%\Lib\site-packages\README.txt + Description=Site packages + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\lib\xml\*.py + Destination=%MAINDIR%\Lib\xml + Description=XML support packages + Flags=0000000000000010 +end +item: Install File + Source=..\lib\xml\dom\*.py + Destination=%MAINDIR%\Lib\xml\dom + Flags=0000000000000010 +end +item: Install File + Source=..\lib\xml\parsers\*.py + Destination=%MAINDIR%\Lib\xml\parsers + Flags=0000000000000010 +end +item: Install File + Source=..\lib\xml\sax\*.py + Destination=%MAINDIR%\Lib\xml\sax + Flags=0000000000000010 +end +item: Remark +end +item: Remark + Text=C Include files +end +item: Install File + Source=..\include\*.h + Destination=%MAINDIR%\include + Description=Header files + Flags=0000000000000010 +end +item: Install File + Source=..\pc\pyconfig.h + Destination=%MAINDIR%\include\pyconfig.h + Description=Header files (pyconfig.h) + Flags=0000000000000010 +end +item: Remark +end +item: Remark + Text=Microsoft C runtime libraries +end +item: Install File + Source=%_SYS_%\MSVCIRT.DLL + Destination=%DLLDEST%\MSVCIRT.DLL + Description=Visual C++ Runtime DLLs + Flags=0000011000010011 +end +item: Install File + Source=%_SYS_%\MSVCRT.DLL + Destination=%DLLDEST%\MSVCRT.DLL + Description=Visual C++ Runtime DLLs + Flags=0000011000010011 +end +item: End Block +end +item: Remark +end +item: Remark + Text=B: Tcl/Tk (Tkinter, IDLE, pydoc) +end +item: If/While Statement + Variable=COMPONENTS + Value=B + Flags=00000010 +end +item: Remark + Text=Tcl/Tk +end +item: Install File + Source=..\..\%_tcldir_%\bin\*.dll + Destination=%MAINDIR%\DLLs + Description=Tcl/Tk binaries and libraries + Flags=0000000000000010 +end +item: Install File + Source=..\..\%_tcldir_%\lib\*.* + Destination=%MAINDIR%\tcl + Description=Tcl/Tk binaries and libraries + Flags=0000000100000010 +end +item: Remark +end +item: Remark + Text=IDLE +end +item: Install File + Source=..\Lib\idlelib\*.py + Destination=%MAINDIR%\Lib\idlelib + Description=Integrated DeveLopment Environment for Python + Flags=0000000000000010 +end +item: Install File + Source=..\Lib\idlelib\*.txt + Destination=%MAINDIR%\Lib\idlelib + Description=Integrated DeveLopment Environment for Python + Flags=0000000000000010 +end +item: Install File + Source=..\Lib\idlelib\*.def + Destination=%MAINDIR%\Lib\idlelib + Description=Integrated DeveLopment Environment for Python + Flags=0000000000000010 +end +item: Install File + Source=..\Lib\idlelib\Icons\* + Destination=%MAINDIR%\Lib\idlelib\Icons + Description=Integrated DeveLopment Environment for Python + Flags=0000000000000010 +end +item: Install File + Source=..\Tools\scripts\idle + Destination=%MAINDIR%\Lib\idlelib\idle.pyw + Description=IDLE bootstrap script + Flags=0000000000000010 +end +item: Remark +end +item: Remark + Text=Windows pydoc driver +end +item: Install File + Source=..\tools\scripts\*.pyw + Destination=%MAINDIR%\Tools\Scripts + Description=Windows pydoc driver + Flags=0000000000000010 +end +item: End Block +end +item: Remark +end +item: Remark + Text=C: docs +end +item: If/While Statement + Variable=COMPONENTS + Value=C + Flags=00000010 +end +item: Install File + Source=%_DOC_%\Python%_PYMAJOR_%%_PYMINOR_%.chm + Destination=%MAINDIR%\Doc\Python%_PYMAJOR_%%_PYMINOR_%.chm + Description=Python Documentation (HTMLHelp) + Flags=0000000000000010 +end +item: End Block +end +item: Remark +end +item: Remark + Text=D: tools +end +item: If/While Statement + Variable=COMPONENTS + Value=D + Flags=00000010 +end +item: Install File + Source=..\tools\scripts\*.py + Destination=%MAINDIR%\Tools\Scripts + Description=Utility Scripts + Flags=0000000000000010 +end +item: Install File + Source=..\tools\scripts\*.doc + Destination=%MAINDIR%\Tools\Scripts + Description=Utility Scripts + Flags=0000000000000010 +end +item: Install File + Source=..\tools\scripts\readme + Destination=%MAINDIR%\Tools\Scripts\README.txt + Description=Utility Scripts + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\tools\webchecker\*.py + Destination=%MAINDIR%\Tools\webchecker + Description=Web checker tool + Flags=0000000000000010 +end +item: Install File + Source=..\tools\webchecker\readme + Destination=%MAINDIR%\Tools\webchecker\README.txt + Description=Web checker tool + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\tools\versioncheck\*.py + Destination=%MAINDIR%\Tools\versioncheck + Description=Version checker tool + Flags=0000000000000010 +end +item: Install File + Source=..\tools\versioncheck\readme + Destination=%MAINDIR%\Tools\versioncheck\README.txt + Description=Version checker tool + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\tools\pynche\*.py + Destination=%MAINDIR%\Tools\pynche + Description=pynche color editor + Flags=0000000000000010 +end +item: Install File + Source=..\tools\pynche\*.txt + Destination=%MAINDIR%\Tools\pynche + Description=pynche color editor + Flags=0000000000000010 +end +item: Install File + Source=..\tools\pynche\x\*.txt + Destination=%MAINDIR%\Tools\pynche\X + Description=pynche color editor - X files + Flags=0000000000000010 +end +item: Install File + Source=..\tools\pynche\readme + Destination=%MAINDIR%\Tools\pynche\README.txt + Description=pynche color editor - README + Flags=0000000100000010 +end +item: Install File + Source=..\tools\pynche\pynche + Destination=%MAINDIR%\Tools\pynche\pynche.py + Description=pynche color editor - main + Flags=0000000100000010 +end +item: Install File + Source=..\tools\pynche\pynche.pyw + Destination=%MAINDIR%\Tools\pynche\pynche.pyw + Description=pynche color editor - noconsole main + Flags=0000000100000010 +end +item: Remark +end +item: Install File + Source=..\tools\i18n\*.py + Destination=%MAINDIR%\Tools\i18n + Description=Internationalization helpers + Flags=0000000000000010 +end +item: End Block +end +item: Remark +end +item: Remark + Text=E: test suite +end +item: If/While Statement + Variable=COMPONENTS + Value=E + Flags=00000010 +end +item: Install File + Source=..\lib\test\audiotest.au + Destination=%MAINDIR%\Lib\test\audiotest.au + Description=Python Test files + Flags=0000000000000010 +end +item: Install File + Source=..\lib\test\*.uue + Destination=%MAINDIR%\Lib\test + Description=Python Test files + Flags=0000000000000010 +end +item: Install File + Source=..\lib\test\*.py + Destination=%MAINDIR%\Lib\test + Description=Python Test files + Flags=0000000000000010 +end +item: Install File + Source=..\lib\test\*.xml + Destination=%MAINDIR%\Lib\test + Description=Python Test files + Flags=0000000000000010 +end +item: Install File + Source=..\lib\test\*.out + Destination=%MAINDIR%\Lib\test + Description=Python Test files + Flags=0000000000000010 +end +item: Install File + Source=..\lib\test\*.bz2 + Destination=%MAINDIR%\Lib\test + Description=Python Test files + Flags=0000000000000010 +end +item: Install File + Source=..\lib\test\*.tar + Destination=%MAINDIR%\Lib\test + Description=Python Test files + Flags=0000000000000010 +end +item: Install File + Source=..\lib\test\*.gz + Destination=%MAINDIR%\Lib\test + Description=Python Test files + Flags=0000000000000010 +end +item: Install File + Source=..\lib\test\*.txt + Destination=%MAINDIR%\Lib\test + Description=Python Test files + Flags=0000000000000010 +end +item: Remark +end +item: Install File + Source=..\lib\test\output\*.* + Destination=%MAINDIR%\Lib\test\output + Description=Python Test output files + Flags=0000000000000010 +end +item: End Block +end +item: Remark +end +item: Remark + Text=DONE with file copying. +end +item: Remark + Text=The rest is registry and Start Menu fiddling. +end +item: Remark +end +item: If/While Statement + Variable=COMPONENTS + Value=A + Flags=00000010 +end +item: If/While Statement + Variable=TASKS + Value=A + Flags=00000010 +end +item: Remark + Text=Register file extensions. As usual, Admin privs get in the way, but with a twist: +end +item: Remark + Text=You don't need admin privs to write to HKEY_CLASSES_ROOT *except* under Win2K. +end +item: Remark + Text=On Win2K, a user without Admin privs has to register extensions under HKCU\Software\CLASSES instead. +end +item: Remark + Text=But while you can *do* that under other flavors of Windows too, it has no useful effect except in Win2K. +end +item: Set Variable + Variable=USE_HKCR + Value=1 +end +item: Check Configuration + Flags=11110010 +end +item: If/While Statement + Variable=DOADMIN + Value=0 +end +item: Set Variable + Variable=USE_HKCR + Value=0 +end +item: End Block +end +item: End Block +end +item: If/While Statement + Variable=USE_HKCR + Value=1 +end +item: Remark + Text=File types. +end +item: Edit Registry + Total Keys=1 + Key=Python.File + New Value=Python File +end +item: Edit Registry + Total Keys=1 + Key=Python.File\shell\open\command + New Value=%MAINDIR%\python.exe "%%1" %%* +end +item: Edit Registry + Total Keys=1 + Key=Python.File\DefaultIcon + New Value=%MAINDIR%\Py.ico +end +item: Remark +end +item: Edit Registry + Total Keys=1 + Key=Python.NoConFile + New Value=Python File (no console) +end +item: Edit Registry + Total Keys=1 + Key=Python.NoConFile\shell\open\command + New Value=%MAINDIR%\pythonw.exe "%%1" %%* +end +item: Edit Registry + Total Keys=1 + Key=Python.NoConFile\DefaultIcon + New Value=%MAINDIR%\Py.ico +end +item: Remark +end +item: Edit Registry + Total Keys=1 + Key=Python.CompiledFile + New Value=Compiled Python File +end +item: Edit Registry + Total Keys=1 + Key=Python.CompiledFile\shell\open\command + New Value=%MAINDIR%\python.exe "%%1" %%* +end +item: Edit Registry + Total Keys=1 + Key=Python.CompiledFile\DefaultIcon + New Value=%MAINDIR%\pyc.ico +end +item: Remark +end +item: Remark + Text=File extensions. +end +item: Edit Registry + Total Keys=1 + Key=.py + New Value=Python.File +end +item: Edit Registry + Total Keys=1 + Key=.py + New Value=text/plain + Value Name=Content Type +end +item: Remark +end +item: Edit Registry + Total Keys=1 + Key=.pyw + New Value=Python.NoConFile +end +item: Edit Registry + Total Keys=1 + Key=.pyw + New Value=text/plain + Value Name=Content Type +end +item: Remark +end +item: Edit Registry + Total Keys=1 + Key=.pyc + New Value=Python.CompiledFile +end +item: Edit Registry + Total Keys=1 + Key=.pyo + New Value=Python.CompiledFile +end +item: Else Statement +end +item: Remark + Text=File types. +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\Python.File + New Value=Python File + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\Python.File\shell\open\command + New Value=%MAINDIR%\python.exe "%%1" %%* + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\Python.File\DefaultIcon + New Value=%MAINDIR%\Py.ico + Root=1 +end +item: Remark +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\Python.NoConFile + New Value=Python File (no console) + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\Python.NoConFile\shell\open\command + New Value=%MAINDIR%\pythonw.exe "%%1" %%* + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\Python.NoConFile\DefaultIcon + New Value=%MAINDIR%\Py.ico + Root=1 +end +item: Remark +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\Python.CompiledFile + New Value=Compiled Python File + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\Python.CompiledFile\shell\open\command + New Value=%MAINDIR%\python.exe "%%1" %%* + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\Python.CompiledFile\DefaultIcon + New Value=%MAINDIR%\pyc.ico + Root=1 +end +item: Remark +end +item: Remark + Text=File extensions. +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\.py + New Value=Python.File + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\.py + New Value=text/plain + Value Name=Content Type + Root=1 +end +item: Remark +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\.pyw + New Value=Python.NoConFile + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\.pyw + New Value=text/plain + Value Name=Content Type + Root=1 +end +item: Remark +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\.pyc + New Value=Python.CompiledFile + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\.pyo + New Value=Python.CompiledFile + Root=1 +end +item: End Block +end +item: Remark +end +item: Remark + Text=If we're installing IDLE, also set an Edit context menu action to use IDLE, for .py and .pyw files. +end +item: If/While Statement + Variable=COMPONENTS + Value=B + Flags=00000010 +end +item: If/While Statement + Variable=USE_HKCR + Value=1 +end +item: Edit Registry + Total Keys=1 + Key=Python.NoConFile\shell\Edit with IDLE\command + New Value=%MAINDIR%\pythonw.exe %MAINDIR%\Lib\idlelib\idle.pyw -n -e "%%1" +end +item: Edit Registry + Total Keys=1 + Key=Python.File\shell\Edit with IDLE\command + New Value=%MAINDIR%\pythonw.exe %MAINDIR%\Lib\idlelib\idle.pyw -n -e "%%1" +end +item: Else Statement +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\Python.NoConFile\shell\Edit with IDLE\command + New Value=%MAINDIR%\pythonw.exe %MAINDIR%\Lib\idlelib\idle.pyw -n -e "%%1" + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\CLASSES\Python.File\shell\Edit with IDLE\command + New Value=%MAINDIR%\pythonw.exe %MAINDIR%\Lib\idlelib\idle.pyw -n -e "%%1" + Root=1 +end +item: End Block +end +item: End Block +end +item: End Block +end +item: Remark +end +item: Remark + Text=Register Python paths. +end +item: Remark + Text=Write to HKLM for admin, else HKCU. Keep these blocks otherwise identical! +end +item: If/While Statement + Variable=DOADMIN + Value=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\CurrentVersion + Root=130 +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\%PY_VERSION%\InstallPath + New Value=%MAINDIR% + Root=2 +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\%PY_VERSION%\InstallPath\InstallGroup + New Value=%CGROUP_SAVE% + New Value= + Root=2 +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\%PY_VERSION%\PythonPath + New Value=%MAINDIR%\Lib;%MAINDIR%\DLLs;%MAINDIR%\Lib\lib-tk + New Value= + Root=2 +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\%PY_VERSION%\Modules + Root=2 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe + New Value=%MAINDIR%\Python.exe + Root=2 +end +item: Else Statement +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\CurrentVersion + Root=129 +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\%PY_VERSION%\InstallPath + New Value=%MAINDIR% + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\%PY_VERSION%\InstallPath\InstallGroup + New Value=%CGROUP_SAVE% + New Value= + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\%PY_VERSION%\PythonPath + New Value=%MAINDIR%\Lib;%MAINDIR%\DLLs;%MAINDIR%\Lib\lib-tk + New Value= + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\%PY_VERSION%\Modules + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\App Paths\Python.exe + New Value=%MAINDIR%\Python.exe + Root=1 +end +item: End Block +end +item: End Block +end +item: Remark +end +item: Remark + Text=Registry fiddling for docs. +end +item: Remark + Text=Write to HKLM for admin, else HKCU. Keep these blocks otherwise identical! +end +item: If/While Statement + Variable=COMPONENTS + Value=C + Flags=00000010 +end +item: If/While Statement + Variable=DOADMIN + Value=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\%PY_VERSION%\Help\Main Python Documentation + New Value=%MAINDIR%\Doc\Python%_PYMAJOR_%%_PYMINOR_%.chm + New Value= + Root=2 +end +item: Else Statement +end +item: Edit Registry + Total Keys=1 + Key=Software\Python\PythonCore\%PY_VERSION%\Help\Main Python Documentation + New Value=%MAINDIR%\Doc\Python%_PYMAJOR_%%_PYMINOR_%.chm + New Value= + Root=1 +end +item: End Block +end +item: End Block +end +item: Remark +end +item: Remark + Text=Set the app publisher and URL entries for Win2K add/remove. +end +item: Remark + Text=It doesn't hurt on other systems. +end +item: Remark + Text=As usual, write to HKLM or HKCU depending on Admin privs. +end +item: Remark + Text=CAUTION: If you set this info on the "Windows 2000" page (step 6) of the +end +item: Remark + Text=Installation Expert, it only shows up in the "If" block below. Keep in synch! +end +item: If/While Statement + Variable=DOADMIN + Value=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=http://www.python.org/ + Value Name=HelpLink + Root=2 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=PythonLabs at Zope Corporation + Value Name=Publisher + Root=2 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=http://www.python.org/ + Value Name=URLInfoAbout + Root=2 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=%PYVER_STRING% + Value Name=DisplayVersion + Root=2 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=%MAINDIR%\py.ico,-0 + Value Name=DisplayIcon + Root=2 +end +item: Else Statement +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=http://www.python.org/ + Value Name=HelpLink + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=PythonLabs at Zope Corporation + Value Name=Publisher + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=http://www.python.org/ + Value Name=URLInfoAbout + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=%PYVER_STRING% + Value Name=DisplayVersion + Root=1 +end +item: Edit Registry + Total Keys=1 + Key=Software\Microsoft\Windows\CurrentVersion\Uninstall\%APPTITLE% + New Value=%MAINDIR%\py.ico,-0 + Value Name=DisplayIcon + Root=1 +end +item: End Block +end +item: Remark +end +item: Remark + Text=Populate Start Menu group +end +item: If/While Statement + Variable=TASKS + Value=B + Flags=00000010 +end +item: Remark + Text=Shortcut to installer no matter what. +end +item: Create Shortcut + Source=%MAINDIR%\unwise.exe + Destination=%GROUP%\Uninstall Python.lnk + Working Directory=%MAINDIR% + Key Type=1536 + Flags=00000001 +end +item: Remark +end +item: If/While Statement + Variable=COMPONENTS + Value=A + Flags=00000010 +end +item: Create Shortcut + Source=%MAINDIR%\python.exe + Destination=%GROUP%\Python (command line).lnk + Working Directory=%MAINDIR% + Icon Pathname=%MAINDIR%\pycon.ico + Key Type=1536 + Flags=00000001 +end +item: End Block +end +item: Remark +end +item: If/While Statement + Variable=COMPONENTS + Value=B + Flags=00000010 +end +item: Create Shortcut + Source=%MAINDIR%\pythonw.exe + Destination=%GROUP%\IDLE (Python GUI).lnk + Command Options="%MAINDIR%\Lib\idlelib\idle.pyw" + Working Directory=%MAINDIR% + Key Type=1536 + Flags=00000001 +end +item: Create Shortcut + Source=%MAINDIR%\pythonw.exe + Destination=%GROUP%\Module Docs.lnk + Command Options="%MAINDIR%\Tools\Scripts\pydocgui.pyw" + Working Directory=%MAINDIR% + Key Type=1536 + Flags=00000001 +end +item: End Block +end +item: Remark +end +item: If/While Statement + Variable=COMPONENTS + Value=C + Flags=00000010 +end +item: Create Shortcut + Source=%MAINDIR%\Doc\Python%_PYMAJOR_%%_PYMINOR_%.chm + Destination=%GROUP%\Python Manuals.lnk + Working Directory=%MAINDIR% + Key Type=1536 + Flags=00000001 +end +item: End Block +end +item: End Block +end +item: Remark +end +item: Remark + Text=I don't think we need this, but have always done it. +end +item: Self-Register OCXs/DLLs + Description=Updating System Configuration, Please Wait... +end +item: Remark +end +item: Remark + Text=Compile to .pyc +end +item: If/While Statement + Variable=TASKS + Value=C + Flags=00000010 +end +item: Execute Program + Pathname=%MAINDIR%\Python.exe + Command Line=%MAINDIR%\Lib\compileall.py -f -x .*\\Lib\\test.* %MAINDIR%\Lib + Default Directory=%MAINDIR%\Lib + Flags=00000010 +end +item: End Block +end +item: Remark +end +remarked item: Remark + Text=Don't enable "Delete in-use files". Here's what happens: +end +remarked item: Remark + Text=Install Python; uninstall Python; install Python again. Reboot the machine. +end +remarked item: Remark + Text=Now UNWISE.EXE is missing. I think this is a Wise bug, but so it goes. +end +remarked item: Add Text to INSTALL.LOG + Text=Delete in-use files: On +end +item: Remark +end +item: Wizard Block + Direction Variable=DIRECTION + Display Variable=DISPLAY + Bitmap Pathname=.\installer.bmp + X Position=9 + Y Position=10 + Filler Color=11173759 + Flags=00000011 +end +item: Custom Dialog Set + Name=Finished + Display Variable=DISPLAY + item: Dialog + Title=%APPTITLE% Installation + Title French=Installation de %APPTITLE% + Title German=Installation von %APPTITLE% + Title Spanish=Instalación de %APPTITLE% + Title Italian=Installazione di %APPTITLE% + Width=339 + Height=280 + Font Name=Helv + Font Size=8 + item: Push Button + Rectangle=188 234 244 253 + Variable=DIRECTION + Value=N + Create Flags=01010000000000010000000000000001 + Text=&Finish + Text French=&Fin + Text German=&Weiter + Text Spanish=&Terminar + Text Italian=&Fine + end + item: Push Button + Rectangle=264 234 320 253 + Variable=DISABLED + Value=! + Action=3 + Create Flags=01010000000000010000000000000000 + Text=&Cancel + Text French=&Annuler + Text German=&Abbrechen + Text Spanish=&Cancelar + Text Italian=&Annulla + end + item: Static + Rectangle=108 10 323 48 + Create Flags=01010000000000000000000000000000 + Flags=0000000000000001 + Name=Times New Roman + Font Style=-24 0 0 0 700 255 0 0 0 3 2 1 18 + Text=Installation Completed! + Text French=Installation terminée ! + Text German=Die Installation ist abgeschlossen! + Text Spanish=¡Instalación terminada! + Text Italian=Installazione completata! + end + item: Static + Rectangle=108 44 320 82 + Create Flags=01010000000000000000000000000000 + Text=%APPTITLE% has been successfully installed. + Text= + Text=Press the Finish button to exit this installation. + Text French=%APPTITLE% est maintenant installé. + Text French= + Text French=Cliquez sur le bouton Fin pour quitter l'installation. + Text German=%APPTITLE% wurde erfolgreich installiert. + Text German= + Text German=Klicken Sie auf "Weiter", um die Installation zu beenden. + Text Spanish=%APPTITLE% se ha instalado con éxito. + Text Spanish= + Text Spanish=Presione el botón Terminar para salir de esta instalación. + Text Italian=L'installazione %APPTITLE% è stata portata a termine con successo. + Text Italian= + Text Italian=Premere il pulsante Fine per uscire dall'installazione. + end + item: Static + Rectangle=10 225 320 226 + Action=3 + Create Flags=01010000000000000000000000000111 + end + item: Static + Rectangle=106 105 312 210 + Enabled Color=00000000000000001111111111111111 + Create Flags=01010000000000000000000000000000 + Text=Special Windows thanks to: + Text= + Text= + Text=LettError, Erik van Blokland, for the Python for Windows graphic. + Text= http://www.letterror.com/ + Text= + Text= + Text=Mark Hammond, without whose years of freely shared Windows expertise, Python for Windows would still be Python for DOS. + end + item: Static + Rectangle=106 95 312 96 + Action=3 + Enabled Color=00000000000000001111111111111111 + Create Flags=01010000000000000000000000001001 + end + end +end +item: End Block +end +item: New Event + Name=Cancel +end +item: Remark + Text=This include script supports a rollback to preinstallation state if the user chooses to cancel before the installation is complete. +end +item: Include Script + Pathname=%_WISE_%\INCLUDE\rollback.wse +end diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore.dsp new file mode 100644 index 00000000..4867ca79 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore.dsp @@ -0,0 +1,592 @@ +# Microsoft Developer Studio Project File - Name="pythoncore" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=pythoncore - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "pythoncore.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "pythoncore.mak" CFG="pythoncore - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "pythoncore - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "pythoncore - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "pythoncore - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\pythoncore" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "../Include" /I "../PC" /I "../Python" /I "../Modules" /I "../../StaticSDKs/XPlatform/zlib" /I "../../../../Sources/Plasma/FeatureLib/pfPython" /I "../../../../Sources/Plasma/CoreLib" /I "../../../../Sources/Plasma/PubUtilLib/plFile" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "USE_DL_EXPORT" /YX /FD /Zm200 /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL" +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o /win32 "NUL" +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /i "..\Include" /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 largeint.lib kernel32.lib user32.lib advapi32.lib shell32.lib /nologo /base:"0x1e000000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc" /out:"./cypython23.dll" +# SUBTRACT LINK32 /pdb:none +# Begin Special Build Tool +TargetPath=.\cypython23.dll +SOURCE="$(InputPath)" +PostBuild_Cmds=xcopy /Y $(TargetPath) ..\..\..\..\test\ +# End Special Build Tool + +!ELSEIF "$(CFG)" == "pythoncore - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\pythoncore" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "../Include" /I "../PC" /I "../Python" /I "../Modules" /I "../../StaticSDKs/XPlatform/zlib" /I "../../../../Sources/Plasma/FeatureLib/pfPython" /I "../../../../Sources/Plasma/CoreLib" /I "../../../../Sources/Plasma/PubUtilLib/plFile" /D "_DEBUG" /D "USE_DL_EXPORT" /D "WIN32" /D "_WINDOWS" /FD /Zm200 /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL" +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o /win32 "NUL" +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /i "..\Include" /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 largeint.lib kernel32.lib user32.lib advapi32.lib shell32.lib /nologo /base:"0x1e000000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc" /out:"./cypython23_d.dll" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none +# Begin Special Build Tool +TargetPath=.\cypython23_d.dll +SOURCE="$(InputPath)" +PostBuild_Cmds=xcopy /Y $(TargetPath) ..\..\..\..\test\ +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "pythoncore - Win32 Release" +# Name "pythoncore - Win32 Debug" +# Begin Group "header files" + +# PROP Default_Filter ".h" +# End Group +# Begin Group "source files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\Modules\_codecsmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\_hotshot.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\_localemodule.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\_randommodule.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\_weakref.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\abstract.c +# End Source File +# Begin Source File + +SOURCE=..\Parser\acceler.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\arraymodule.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\audioop.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\binascii.c +# End Source File +# Begin Source File + +SOURCE=..\Parser\bitset.c +# End Source File +# Begin Source File + +SOURCE=..\Python\bltinmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\boolobject.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\bufferobject.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\cellobject.c +# End Source File +# Begin Source File + +SOURCE=..\Python\ceval.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\classobject.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\cmathmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\cobject.c +# End Source File +# Begin Source File + +SOURCE=..\Python\codecs.c +# End Source File +# Begin Source File + +SOURCE=..\Python\compile.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\complexobject.c +# End Source File +# Begin Source File + +SOURCE=..\PC\config.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\cPickle.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\cStringIO.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\descrobject.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\dictobject.c +# End Source File +# Begin Source File + +SOURCE=..\PC\dl_nt.c +# End Source File +# Begin Source File + +SOURCE=..\Python\dynload_win.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\enumobject.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\errnomodule.c +# End Source File +# Begin Source File + +SOURCE=..\Python\errors.c +# End Source File +# Begin Source File + +SOURCE=..\Python\exceptions.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\fileobject.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\floatobject.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\frameobject.c +# End Source File +# Begin Source File + +SOURCE=..\Python\frozen.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\funcobject.c +# End Source File +# Begin Source File + +SOURCE=..\Python\future.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\gcmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Python\getargs.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\getbuildinfo.c +# ADD CPP /D BUILD=51 +# End Source File +# Begin Source File + +SOURCE=..\Python\getcompiler.c +# End Source File +# Begin Source File + +SOURCE=..\Python\getcopyright.c +# End Source File +# Begin Source File + +SOURCE=..\Python\getmtime.c +# End Source File +# Begin Source File + +SOURCE=..\Python\getopt.c +# End Source File +# Begin Source File + +SOURCE=..\PC\getpathp.c +# End Source File +# Begin Source File + +SOURCE=..\Python\getplatform.c +# End Source File +# Begin Source File + +SOURCE=..\Python\getversion.c +# End Source File +# Begin Source File + +SOURCE=..\Python\graminit.c +# End Source File +# Begin Source File + +SOURCE=..\Parser\grammar1.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\imageop.c +# End Source File +# Begin Source File + +SOURCE=..\Python\import.c +# End Source File +# Begin Source File + +SOURCE=..\PC\import_nt.c +# ADD CPP /I "..\Python" +# End Source File +# Begin Source File + +SOURCE=..\Python\importdl.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\intobject.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\iterobject.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\itertoolsmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Parser\listnode.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\listobject.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\longobject.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\main.c +# End Source File +# Begin Source File + +SOURCE=..\Python\marshal.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\mathmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\md5c.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\md5module.c +# End Source File +# Begin Source File + +SOURCE=..\Parser\metagrammar.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\methodobject.c +# End Source File +# Begin Source File + +SOURCE=..\Python\modsupport.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\moduleobject.c +# End Source File +# Begin Source File + +SOURCE=..\PC\msvcrtmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Parser\myreadline.c +# End Source File +# Begin Source File + +SOURCE=..\Python\mysnprintf.c +# End Source File +# Begin Source File + +SOURCE=..\Python\mystrtoul.c +# End Source File +# Begin Source File + +SOURCE=..\Parser\node.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\object.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\obmalloc.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\operator.c +# End Source File +# Begin Source File + +SOURCE=..\Parser\parser.c +# End Source File +# Begin Source File + +SOURCE=..\Parser\parsetok.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\pcremodule.c +# End Source File +# Begin Source File + +SOURCE=..\PC\PlasmaPack.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\Sources\Plasma\PubUtilLib\plFile\plEncryptedStream.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\Sources\Plasma\FeatureLib\pfPython\plPythonPack.cpp +# End Source File +# Begin Source File + +SOURCE=..\Modules\posixmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Python\pyfpe.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\pypcre.c +# End Source File +# Begin Source File + +SOURCE=..\Python\pystate.c +# End Source File +# Begin Source File + +SOURCE=..\PC\python_nt.rc +# End Source File +# Begin Source File + +SOURCE=..\Python\pythonrun.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\rangeobject.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\regexmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\regexpr.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\rgbimgmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\rotormodule.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\shamodule.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\signalmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\sliceobject.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\stringobject.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\stropmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Python\structmember.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\structmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\structseq.c +# End Source File +# Begin Source File + +SOURCE=..\Python\symtable.c +# End Source File +# Begin Source File + +SOURCE=..\Python\sysmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Python\thread.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\threadmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\timemodule.c +# End Source File +# Begin Source File + +SOURCE=..\Parser\tokenizer.c +# End Source File +# Begin Source File + +SOURCE=..\Python\traceback.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\tupleobject.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\typeobject.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\unicodectype.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\unicodeobject.c +# End Source File +# Begin Source File + +SOURCE=..\Objects\weakrefobject.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\xreadlinesmodule.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\xxsubtype.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\yuvconvert.c +# End Source File +# Begin Source File + +SOURCE=..\Modules\zipimport.c +# End Source File +# End Group +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore.vcproj b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore.vcproj new file mode 100644 index 00000000..49d54a5e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore.vcproj @@ -0,0 +1,2427 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore_dyn_server.vcproj b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore_dyn_server.vcproj new file mode 100644 index 00000000..e5d7f5f8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore_dyn_server.vcproj @@ -0,0 +1,4337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore_static.vcproj b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore_static.vcproj new file mode 100644 index 00000000..8b6671df --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythoncore_static.vcproj @@ -0,0 +1,549 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythonw.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythonw.dsp new file mode 100644 index 00000000..ee3750e2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/pythonw.dsp @@ -0,0 +1,101 @@ +# Microsoft Developer Studio Project File - Name="pythonw" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=pythonw - Win32 Alpha Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "pythonw.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "pythonw.mak" CFG="pythonw - Win32 Alpha Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "pythonw - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "pythonw - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "pythonw - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\pythonw" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1d000000" /subsystem:windows /debug /machine:I386 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "pythonw - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\pythonw" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1d000000" /subsystem:windows /debug /machine:I386 /out:"./pythonw_d.exe" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "pythonw - Win32 Release" +# Name "pythonw - Win32 Debug" +# Begin Source File + +SOURCE=..\PC\python_exe.rc +# End Source File +# Begin Source File + +SOURCE=..\PC\WinMain.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/readme.txt b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/readme.txt new file mode 100644 index 00000000..a3518244 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/readme.txt @@ -0,0 +1,314 @@ +Building Python using VC++ 6.0 or 5.0 +------------------------------------- +This directory is used to build Python for Win32 platforms, e.g. Windows +95, 98 and NT. It requires Microsoft Visual C++ 6.x or 5.x. +(For other Windows platforms and compilers, see ../PC/readme.txt.) + +All you need to do is open the workspace "pcbuild.dsw" in MSVC++, select +the Debug or Release setting (using Build -> Set Active Configuration...), +and build the projects. + +The proper order to build subprojects: + +1) pythoncore (this builds the main Python DLL and library files, + python21.{dll, lib} in Release mode) + NOTE: in previous releases, this subproject was + named after the release number, e.g. python20. + +2) python (this builds the main Python executable, + python.exe in Release mode) + +3) the other subprojects, as desired or needed (note: you probably don't + want to build most of the other subprojects, unless you're building an + entire Python distribution from scratch, or specifically making changes + to the subsystems they implement; see SUBPROJECTS below) + +When using the Debug setting, the output files have a _d added to +their name: python21_d.dll, python_d.exe, parser_d.pyd, and so on. + +SUBPROJECTS +----------- +These subprojects should build out of the box. Subprojects other than the +main ones (pythoncore, python, pythonw) generally build a DLL (renamed to +.pyd) from a specific module so that users don't have to load the code +supporting that module unless they import the module. + +pythoncore + .dll and .lib +python + .exe +pythonw + pythonw.exe, a variant of python.exe that doesn't pop up a DOS box +_csv + C support for the comma-separated values module +_socket + socketmodule.c +_sre + Unicode-aware regular expression engine +_symtable + the _symtable module, symtablemodule.c +_testcapi + tests of the Python C API, run via Lib/test/test_capi.py, and + implemented by module Modules/_testcapimodule.c +datetime + datetimemodule.c +mmap + mmapmodule.c +parser + the parser module +pyexpat + Python wrapper for accelerated XML parsing, which incorporates stable + code from the Expat project: http://sourceforge.net/projects/expat/ +select + selectmodule.c +unicodedata + large tables of Unicode data +winreg + Windows registry API +winsound + play sounds (typically .wav files) under Windows + +The following subprojects will generally NOT build out of the box. They +wrap code Python doesn't control, and you'll need to download the base +packages first and unpack them into siblings of PCbuilds's parent +directory; for example, if your PCbuild is .......\dist\src\PCbuild\, +unpack into new subdirectories of dist\. + +_tkinter + Python wrapper for the Tk windowing system. Requires building + Tcl/Tk first. Following are instructions for Tcl/Tk 8.4.3: + + Get source + ---------- + Go to + http://prdownloads.sourceforge.net/tcl/ + and download + tcl843-src.zip + tk843-src.zip + Unzip into + dist\tcl8.4.3\ + dist\tk8.4.3\ + respectively. + + Build Tcl first (done here w/ MSVC 6 on Win98SE) + --------------- + cd dist\tcl8.4.3\win + run vcvars32.bat [necessary even on Win2K] + nmake -f makefile.vc + nmake -f makefile.vc INSTALLDIR=..\..\tcl84 install + + XXX Should we compile with OPTS=threads? + + XXX Some tests failed in "nmake -f makefile.vc test". + XXX all.tcl: Total 10480 Passed 9743 Skipped 719 Failed 18 + XXX + XXX That was on Win98SE. On Win2K: + XXX all.tcl Total 10480 Passed 9781 Skipped 698 Failed 1 + + Build Tk + -------- + cd dist\tk8.4.3\win + nmake -f makefile.vc TCLDIR=..\..\tcl8.4.3 + nmake -f makefile.vc TCLDIR=..\..\tcl8.4.3 INSTALLDIR=..\..\tcl84 install + + XXX Should we compile with OPTS=threads? + + XXX I have no idea whether "nmake -f makefile.vc test" passed or + XXX failed. It popped up tons of little windows, and did lots of + XXX stuff, and nothing blew up. + + XXX Our installer copies a lot of stuff out of the Tcl/Tk install + XXX directory. Is all of that really needed for Python use of Tcl/Tk? + + Make sure the installer matches + ------------------------------- + Ensure that the Wise compiler vrbl _TCLDIR_ is set to the name of + the common Tcl/Tk installation directory (tcl84 for the instructions + above). This is needed so the installer can copy various Tcl/Tk + files into the Python distribution. + + +zlib + Python wrapper for the zlib compression library. Get the source code + for version 1.1.4 from a convenient mirror at: + http://www.gzip.org/zlib/ + Unpack into dist\zlib-1.1.4. + A custom pre-link step in the zlib project settings should manage to + build zlib-1.1.4\zlib.lib by magic before zlib.pyd (or zlib_d.pyd) is + linked in PCbuild\. + However, the zlib project is not smart enough to remove anything under + zlib-1.1.4\ when you do a clean, so if you want to rebuild zlib.lib + you need to clean up zlib-1.1.4\ by hand. + +bz2 + Python wrapper for the libbz2 compression library. Homepage + http://sources.redhat.com/bzip2/ + Download the source tarball, bzip2-1.0.2.tar.gz. + Unpack into dist\bzip2-1.0.2. WARNING: If you're using WinZip, you + must disable its "TAR file smart CR/LF conversion" feature (under + Options -> Configuration -> Miscellaneous -> Other) for the duration. + + Don't bother trying to use libbz2.dsp with MSVC. After 10 minutes + of fiddling, I couldn't get it to work. Perhaps it works with + MSVC 5 (I used MSVC 6). It's better to run the by-hand makefile + anyway, because it runs a helpful test step at the end. + + cd into dist\bzip2-1.0.2, and run + nmake -f makefile.msc + [Note that if you're running Win9X, you'll need to run vcvars32.bat + before running nmake (this batch file is in your MSVC installation). + TODO: make this work like zlib (in particular, MSVC runs the prelink + step in an enviroment that already has the correct envars set up). + ] + The make step shouldn't yield any warnings or errors, and should end + by displaying 6 blocks each terminated with + FC: no differences encountered + If FC finds differences, see the warning abou WinZip above (when I + first tried it, sample3.ref failed due to CRLF conversion). + + All of this managed to build bzip2-1.0.2\libbz2.lib, which the Python + project links in. + + +_bsddb + Go to Sleepycat's download page: + http://www.sleepycat.com/download/ + + and download version 4.1.25. The file name is db-4.1.25.NC.zip. + XXX with or without strong cryptography? I picked "without". + + Unpack into + dist\db-4.1.25 + + [If using WinZip to unpack the db-4.1.25.NC distro, that requires + renaming the directory (to remove ".NC") after unpacking. + ] + + Open + dist\db-4.1.25\docs\index.html + + and follow the Windows instructions for building the Sleepycat + software. Note that Berkeley_DB.dsw is in the build_win32 subdirectory. + Build the Release version ("build_all -- Win32 Release"). + + XXX We're actually linking against Release_static\libdb41s.lib. + XXX This yields the following warnings: +""" +Compiling... +_bsddb.c +Linking... + Creating library ./_bsddb.lib and object ./_bsddb.exp +LINK : warning LNK4049: locally defined symbol "_malloc" imported +LINK : warning LNK4049: locally defined symbol "_free" imported +LINK : warning LNK4049: locally defined symbol "_fclose" imported +LINK : warning LNK4049: locally defined symbol "_fopen" imported +_bsddb.pyd - 0 error(s), 4 warning(s) +""" + XXX This isn't encouraging, but I don't know what to do about it. + + To run extensive tests, pass "-u bsddb" to regrtest.py. test_bsddb3.py + is then enabled. Running in verbose mode may be helpful. + + XXX The test_bsddb3 tests don't always pass, on Windows (according to + XXX me) or on Linux (according to Barry). I had much better luck + XXX on Win2K than on Win98SE. The common failure mode across platforms + XXX is + XXX DBAgainError: (11, 'Resource temporarily unavailable -- unable + XXX to join the environment') + XXX + XXX and it appears timing-dependent. On Win2K I also saw this once: + XXX + XXX test02_SimpleLocks (bsddb.test.test_thread.HashSimpleThreaded) ... + XXX Exception in thread reader 1: + XXX Traceback (most recent call last): + XXX File "C:\Code\python\lib\threading.py", line 411, in __bootstrap + XXX self.run() + XXX File "C:\Code\python\lib\threading.py", line 399, in run + XXX apply(self.__target, self.__args, self.__kwargs) + XXX File "C:\Code\python\lib\bsddb\test\test_thread.py", line 268, in + XXX readerThread + XXX rec = c.next() + XXX DBLockDeadlockError: (-30996, 'DB_LOCK_DEADLOCK: Locker killed + XXX to resolve a deadlock') + XXX + XXX I'm told that DBLockDeadlockError is expected at times. It + XXX doesn't cause a test to fail when it happens (exceptions in + XXX threads are invisible to unittest). + + +_ssl + Python wrapper for the secure sockets library. + + Get the latest source code for OpenSSL from + http://www.openssl.org + + You (probably) don't want the "engine" code. For example, get + openssl-0.9.7b.tar.gz + not + openssl-engine-0.9.7b.tar.gz + + Unpack into the "dist" directory, retaining the folder name from + the archive - for example, the latest stable OpenSSL will install as + dist/openssl-0.9.7b + + You can (theoretically) use any version of OpenSSL you like - the + build process will automatically select the latest version. + + You must also install ActivePerl from + http://www.activestate.com/Products/ActivePerl/ + as this is used by the OpenSSL build process. Complain to them . + + The MSVC project simply invokes PCBuild/build_ssl.py to perform + the build. This Python script locates and builds your OpenSSL + installation, then invokes a simple makefile to build the final .pyd. + + Win9x users: see "Win9x note" below. + + build_ssl.py attempts to catch the most common errors (such as not + being able to find OpenSSL sources, or not being able to find a Perl + that works with OpenSSL) and give a reasonable error message. + If you have a problem that doesn't seem to be handled correctly + (eg, you know you have ActivePerl but we can't find it), please take + a peek at build_ssl.py and suggest patches. Note that build_ssl.py + should be able to be run directly from the command-line. + + build_ssl.py/MSVC isn't clever enough to clean OpenSSL - you must do + this by hand. + + Win9x note: If, near the start of the build process, you see + something like + + C:\Code\openssl-0.9.7b>set OPTS=no-asm + Out of environment space + + then you're in trouble, and will probably also see these errors near + the end of the process: + + NMAKE : fatal error U1073: don't know how to make + 'crypto\md5\asm\m5_win32.asm' + Stop. + NMAKE : fatal error U1073: don't know how to make + 'C:\Code\openssl-0.9.7b/out32/libeay32.lib' + Stop. + + You need more environment space. Win9x only has room for 256 bytes + by default, and especially after installing ActivePerl (which fiddles + the PATH envar), you're likely to run out. KB Q230205 + + http://support.microsoft.com/default.aspx?scid=KB;en-us;q230205 + + explains how to edit CONFIG.SYS to cure this. + + +YOUR OWN EXTENSION DLLs +----------------------- +If you want to create your own extension module DLL, there's an example +with easy-to-follow instructions in ../PC/example/; read the file +readme.txt there first. + +HTML Help +--------- + +The compiled HTML help file is built from the HTML pages by the script +Doc/tools/prechm.py. This creates project files which must be compiled +with MS HTML Help Workshop. diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/rmpyc.py b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/rmpyc.py new file mode 100644 index 00000000..95de0f6b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/rmpyc.py @@ -0,0 +1,25 @@ +# Remove all the .pyc and .pyo files under ../Lib. + + +def deltree(root): + import os + from os.path import join + + npyc = npyo = 0 + for root, dirs, files in os.walk(root): + for name in files: + delete = False + if name.endswith('.pyc'): + delete = True + npyc += 1 + elif name.endswith('.pyo'): + delete = True + npyo += 1 + + if delete: + os.remove(join(root, name)) + + return npyc, npyo + +npyc, npyo = deltree("../Lib") +print npyc, ".pyc deleted,", npyo, ".pyo deleted" diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/rt.bat b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/rt.bat new file mode 100644 index 00000000..67a28a63 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/rt.bat @@ -0,0 +1,41 @@ +@rem Run Tests. Run the regression test suite. +@rem Usage: rt [-d] [-O] [-q] regrtest_args +@rem -d Run Debug build (python_d.exe). Else release build. +@rem -O Run python.exe or python_d.exe (see -d) with -O. +@rem -q "quick" -- normally the tests are run twice, the first time +@rem after deleting all the .py[co] files reachable from Lib/. +@rem -q runs the tests just once, and without deleting .py[co] files. +@rem All leading instances of these switches are shifted off, and +@rem whatever remains is passed to regrtest.py. For example, +@rem rt -O -d -x test_thread +@rem runs +@rem python_d -O ../lib/test/regrtest.py -x test_thread +@rem twice, and +@rem rt -q -g test_binascii +@rem runs +@rem python_d ../lib/test/regrtest.py -g test_binascii +@rem to generate the expected-output file for binascii quickly. +@set _exe=python +@set _qmode=no +@set _dashO= +@goto CheckOpts +:Again +@shift +:CheckOpts +@if "%1"=="-O" set _dashO=-O +@if "%1"=="-O" goto Again +@if "%1"=="-q" set _qmode=yes +@if "%1"=="-q" goto Again +@if "%1"=="-d" set _exe=python_d +@if "%1"=="-d" goto Again +@if "%_qmode%"=="yes" goto Qmode +@echo Deleting .pyc/.pyo files ... +@%_exe% rmpyc.py +%_exe% %_dashO% -E -tt ../lib/test/regrtest.py %1 %2 %3 %4 %5 %6 %7 %8 %9 +@echo About to run again without deleting .pyc/.pyo first: +@pause +:Qmode +%_exe% %_dashO% -E -tt ../lib/test/regrtest.py %1 %2 %3 %4 %5 %6 %7 %8 %9 +@set _exe= +@set _qmode= +@set _dashO= diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/select.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/select.dsp new file mode 100644 index 00000000..12bc846c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/select.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="select" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=select - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "select.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "select.mak" CFG="select - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "select - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "select - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "select - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\select" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /I "..\..\select113" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /base:"0x1D110000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc" /out:"./select.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "select - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\select" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /I "..\..\select113" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib kernel32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /base:"0x1D110000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc" /nodefaultlib:"msvcrt" /out:"./select_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "select - Win32 Release" +# Name "select - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\selectmodule.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/unicodedata.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/unicodedata.dsp new file mode 100644 index 00000000..0ad5645e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/unicodedata.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="unicodedata" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=unicodedata - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "unicodedata.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "unicodedata.mak" CFG="unicodedata - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "unicodedata - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "unicodedata - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "unicodedata - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\unicodedata" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\Include" /I "..\PC" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "NDEBUG" +# ADD RSC /l 0xc09 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1D120000" /dll /machine:I386 /out:"./unicodedata.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "unicodedata - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\unicodedata" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MMAP_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "_DEBUG" +# ADD RSC /l 0xc09 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1D120000" /dll /debug /machine:I386 /out:"./unicodedata_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "unicodedata - Win32 Release" +# Name "unicodedata - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\unicodedata.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/w9xpopen.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/w9xpopen.dsp new file mode 100644 index 00000000..9c50ba0f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/w9xpopen.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="w9xpopen" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=w9xpopen - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "w9xpopen.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "w9xpopen.mak" CFG="w9xpopen - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "w9xpopen - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "w9xpopen - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "w9xpopen - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\w9xpopen" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "NDEBUG" +# ADD RSC /l 0xc09 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 user32.lib /nologo /machine:I386 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "w9xpopen - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\w9xpopen" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "_DEBUG" +# ADD RSC /l 0xc09 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib /nologo /debug /machine:I386 /out:"./w9xpopen_d.exe" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "w9xpopen - Win32 Release" +# Name "w9xpopen - Win32 Debug" +# Begin Source File + +SOURCE=..\PC\w9xpopen.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/winreg.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/winreg.dsp new file mode 100644 index 00000000..af40f437 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/winreg.dsp @@ -0,0 +1,108 @@ +# Microsoft Developer Studio Project File - Name="winreg" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=winreg - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "winreg.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "winreg.mak" CFG="winreg - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "winreg - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "winreg - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "winreg - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\winreg" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "winreg_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\Include" /I "..\PC" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "NDEBUG" +# ADD RSC /l 0xc09 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e1C0000" /dll /machine:I386 /out:"_winreg.pyd" + +!ELSEIF "$(CFG)" == "winreg - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\winreg" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "winreg_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\Include" /I "..\PC" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "_DEBUG" +# ADD RSC /l 0xc09 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x1e1C0000" /dll /debug /machine:I386 /out:"_winreg_d.pyd" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "winreg - Win32 Release" +# Name "winreg - Win32 Debug" +# Begin Source File + +SOURCE=..\PC\_winreg.c + +!IF "$(CFG)" == "winreg - Win32 Release" + +# ADD CPP /MD + +!ELSEIF "$(CFG)" == "winreg - Win32 Debug" + +# ADD CPP /MDd + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/winsound.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/winsound.dsp new file mode 100644 index 00000000..a6ae658c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/winsound.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="winsound" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=winsound - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "winsound.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "winsound.mak" CFG="winsound - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "winsound - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "winsound - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "winsound - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\winsound" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINSOUND_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\Include" /I "..\PC" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINSOUND_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "NDEBUG" +# ADD RSC /l 0xc09 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib winmm.lib user32.lib /nologo /base:"0x1D160000" /dll /machine:I386 /out:"./winsound.pyd" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "winsound - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\winsound" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINSOUND_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WINSOUND_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc09 /d "_DEBUG" +# ADD RSC /l 0xc09 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 user32.lib kernel32.lib winmm.lib /nologo /base:"0x1D160000" /dll /debug /machine:I386 /out:"./winsound_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "winsound - Win32 Release" +# Name "winsound - Win32 Debug" +# Begin Source File + +SOURCE=..\PC\winsound.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/zlib.dsp b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/zlib.dsp new file mode 100644 index 00000000..c997bc48 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/PCbuild/zlib.dsp @@ -0,0 +1,109 @@ +# Microsoft Developer Studio Project File - Name="zlib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=zlib - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "zlib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "zlib.mak" CFG="zlib - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "zlib - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "zlib - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "zlib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-release\zlib" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "..\Include" /I "..\PC" /I "..\..\zlib-1.1.4" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\zlib-1.1.4\zlib.lib /nologo /base:"0x1e1B0000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc" /out:"./zlib.pyd" +# SUBTRACT LINK32 /pdb:none +# Begin Special Build Tool +SOURCE="$(InputPath)" +PreLink_Desc=Checking static zlib has been built +PreLink_Cmds=cd ..\..\zlib-1.1.4 nmake -nologo -f msdos\makefile.w32 zlib.lib +# End Special Build Tool + +!ELSEIF "$(CFG)" == "zlib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "." +# PROP Intermediate_Dir "x86-temp-debug\zlib" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\Include" /I "..\PC" /I "..\..\zlib-1.1.4" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ..\..\zlib-1.1.4\zlib.lib /nologo /base:"0x1e1B0000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libc" /out:"./zlib_d.pyd" /pdbtype:sept +# SUBTRACT LINK32 /pdb:none +# Begin Special Build Tool +SOURCE="$(InputPath)" +PreLink_Desc=Checking static zlib has been built +PreLink_Cmds=cd ..\..\zlib-1.1.4 nmake -nologo -f msdos\makefile.w32 zlib.lib +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "zlib - Win32 Release" +# Name "zlib - Win32 Debug" +# Begin Source File + +SOURCE=..\Modules\zlibmodule.c +# End Source File +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/.cvsignore b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/.cvsignore new file mode 100644 index 00000000..0be9f1a7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/.cvsignore @@ -0,0 +1,3 @@ +Makefile +pgen +add2lib diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/acceler.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/acceler.c new file mode 100644 index 00000000..b33e8786 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/acceler.c @@ -0,0 +1,138 @@ + +/* Parser accelerator module */ + +/* The parser as originally conceived had disappointing performance. + This module does some precomputation that speeds up the selection + of a DFA based upon a token, turning a search through an array + into a simple indexing operation. The parser now cannot work + without the accelerators installed. Note that the accelerators + are installed dynamically when the parser is initialized, they + are not part of the static data structure written on graminit.[ch] + by the parser generator. */ + +#include "pgenheaders.h" +#include "grammar.h" +#include "node.h" +#include "token.h" +#include "parser.h" + +/* Forward references */ +static void fixdfa(grammar *, dfa *); +static void fixstate(grammar *, state *); + +void +PyGrammar_AddAccelerators(grammar *g) +{ + dfa *d; + int i; + d = g->g_dfa; + for (i = g->g_ndfas; --i >= 0; d++) + fixdfa(g, d); + g->g_accel = 1; +} + +void +PyGrammar_RemoveAccelerators(grammar *g) +{ + dfa *d; + int i; + g->g_accel = 0; + d = g->g_dfa; + for (i = g->g_ndfas; --i >= 0; d++) { + state *s; + int j; + s = d->d_state; + for (j = 0; j < d->d_nstates; j++, s++) { + if (s->s_accel) + PyObject_FREE(s->s_accel); + s->s_accel = NULL; + } + } +} + +static void +fixdfa(grammar *g, dfa *d) +{ + state *s; + int j; + s = d->d_state; + for (j = 0; j < d->d_nstates; j++, s++) + fixstate(g, s); +} + +static void +fixstate(grammar *g, state *s) +{ + arc *a; + int k; + int *accel; + int nl = g->g_ll.ll_nlabels; + s->s_accept = 0; + accel = (int *) PyObject_MALLOC(nl * sizeof(int)); + for (k = 0; k < nl; k++) + accel[k] = -1; + a = s->s_arc; + for (k = s->s_narcs; --k >= 0; a++) { + int lbl = a->a_lbl; + label *l = &g->g_ll.ll_label[lbl]; + int type = l->lb_type; + if (a->a_arrow >= (1 << 7)) { + printf("XXX too many states!\n"); + continue; + } + if (ISNONTERMINAL(type)) { + dfa *d1 = PyGrammar_FindDFA(g, type); + int ibit; + if (type - NT_OFFSET >= (1 << 7)) { + printf("XXX too high nonterminal number!\n"); + continue; + } + for (ibit = 0; ibit < g->g_ll.ll_nlabels; ibit++) { + if (testbit(d1->d_first, ibit)) { +#ifdef applec +#define MPW_881_BUG /* Undefine if bug below is fixed */ +#endif +#ifdef MPW_881_BUG + /* In 881 mode MPW 3.1 has a code + generation bug which seems to + set the upper bits; fix this by + explicitly masking them off */ + int temp; +#endif + if (accel[ibit] != -1) + printf("XXX ambiguity!\n"); +#ifdef MPW_881_BUG + temp = 0xFFFF & + (a->a_arrow | (1 << 7) | + ((type - NT_OFFSET) << 8)); + accel[ibit] = temp; +#else + accel[ibit] = a->a_arrow | (1 << 7) | + ((type - NT_OFFSET) << 8); +#endif + } + } + } + else if (lbl == EMPTY) + s->s_accept = 1; + else if (lbl >= 0 && lbl < nl) + accel[lbl] = a->a_arrow; + } + while (nl > 0 && accel[nl-1] == -1) + nl--; + for (k = 0; k < nl && accel[k] == -1;) + k++; + if (k < nl) { + int i; + s->s_accel = (int *) PyObject_MALLOC((nl-k) * sizeof(int)); + if (s->s_accel == NULL) { + fprintf(stderr, "no mem to add parser accelerators\n"); + exit(1); + } + s->s_lower = k; + s->s_upper = nl; + for (i = 0; k < nl; i++, k++) + s->s_accel[i] = accel[k]; + } + PyObject_FREE(accel); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/bitset.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/bitset.c new file mode 100644 index 00000000..1597c9a0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/bitset.c @@ -0,0 +1,66 @@ + +/* Bitset primitives used by the parser generator */ + +#include "pgenheaders.h" +#include "bitset.h" + +bitset +newbitset(int nbits) +{ + int nbytes = NBYTES(nbits); + bitset ss = PyMem_NEW(BYTE, nbytes); + + if (ss == NULL) + Py_FatalError("no mem for bitset"); + + ss += nbytes; + while (--nbytes >= 0) + *--ss = 0; + return ss; +} + +void +delbitset(bitset ss) +{ + PyMem_DEL(ss); +} + +int +addbit(bitset ss, int ibit) +{ + int ibyte = BIT2BYTE(ibit); + BYTE mask = BIT2MASK(ibit); + + if (ss[ibyte] & mask) + return 0; /* Bit already set */ + ss[ibyte] |= mask; + return 1; +} + +#if 0 /* Now a macro */ +int +testbit(bitset ss, int ibit) +{ + return (ss[BIT2BYTE(ibit)] & BIT2MASK(ibit)) != 0; +} +#endif + +int +samebitset(bitset ss1, bitset ss2, int nbits) +{ + int i; + + for (i = NBYTES(nbits); --i >= 0; ) + if (*ss1++ != *ss2++) + return 0; + return 1; +} + +void +mergebitset(bitset ss1, bitset ss2, int nbits) +{ + int i; + + for (i = NBYTES(nbits); --i >= 0; ) + *ss1++ |= *ss2++; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/firstsets.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/firstsets.c new file mode 100644 index 00000000..f2531f8e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/firstsets.c @@ -0,0 +1,110 @@ + +/* Computation of FIRST stets */ + +#include "pgenheaders.h" +#include "grammar.h" +#include "token.h" + +extern int Py_DebugFlag; + +/* Forward */ +static void calcfirstset(grammar *, dfa *); + +void +addfirstsets(grammar *g) +{ + int i; + dfa *d; + + if (Py_DebugFlag) + printf("Adding FIRST sets ...\n"); + for (i = 0; i < g->g_ndfas; i++) { + d = &g->g_dfa[i]; + if (d->d_first == NULL) + calcfirstset(g, d); + } +} + +static void +calcfirstset(grammar *g, dfa *d) +{ + int i, j; + state *s; + arc *a; + int nsyms; + int *sym; + int nbits; + static bitset dummy; + bitset result; + int type; + dfa *d1; + label *l0; + + if (Py_DebugFlag) + printf("Calculate FIRST set for '%s'\n", d->d_name); + + if (dummy == NULL) + dummy = newbitset(1); + if (d->d_first == dummy) { + fprintf(stderr, "Left-recursion for '%s'\n", d->d_name); + return; + } + if (d->d_first != NULL) { + fprintf(stderr, "Re-calculating FIRST set for '%s' ???\n", + d->d_name); + } + d->d_first = dummy; + + l0 = g->g_ll.ll_label; + nbits = g->g_ll.ll_nlabels; + result = newbitset(nbits); + + sym = PyMem_NEW(int, 1); + if (sym == NULL) + Py_FatalError("no mem for new sym in calcfirstset"); + nsyms = 1; + sym[0] = findlabel(&g->g_ll, d->d_type, (char *)NULL); + + s = &d->d_state[d->d_initial]; + for (i = 0; i < s->s_narcs; i++) { + a = &s->s_arc[i]; + for (j = 0; j < nsyms; j++) { + if (sym[j] == a->a_lbl) + break; + } + if (j >= nsyms) { /* New label */ + PyMem_RESIZE(sym, int, nsyms + 1); + if (sym == NULL) + Py_FatalError( + "no mem to resize sym in calcfirstset"); + sym[nsyms++] = a->a_lbl; + type = l0[a->a_lbl].lb_type; + if (ISNONTERMINAL(type)) { + d1 = PyGrammar_FindDFA(g, type); + if (d1->d_first == dummy) { + fprintf(stderr, + "Left-recursion below '%s'\n", + d->d_name); + } + else { + if (d1->d_first == NULL) + calcfirstset(g, d1); + mergebitset(result, + d1->d_first, nbits); + } + } + else if (ISTERMINAL(type)) { + addbit(result, a->a_lbl); + } + } + } + d->d_first = result; + if (Py_DebugFlag) { + printf("FIRST set for '%s': {", d->d_name); + for (i = 0; i < nbits; i++) { + if (testbit(result, i)) + printf(" %s", PyGrammar_LabelRepr(&l0[i])); + } + printf(" }\n"); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar.c new file mode 100644 index 00000000..2b467059 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar.c @@ -0,0 +1,246 @@ + +/* Grammar implementation */ + +#include "Python.h" +#include "pgenheaders.h" + +#include + +#include "token.h" +#include "grammar.h" + +#ifdef RISCOS +#include +#endif + +extern int Py_DebugFlag; + +grammar * +newgrammar(int start) +{ + grammar *g; + + g = PyMem_NEW(grammar, 1); + if (g == NULL) + Py_FatalError("no mem for new grammar"); + g->g_ndfas = 0; + g->g_dfa = NULL; + g->g_start = start; + g->g_ll.ll_nlabels = 0; + g->g_ll.ll_label = NULL; + g->g_accel = 0; + return g; +} + +dfa * +adddfa(grammar *g, int type, char *name) +{ + dfa *d; + + PyMem_RESIZE(g->g_dfa, dfa, g->g_ndfas + 1); + if (g->g_dfa == NULL) + Py_FatalError("no mem to resize dfa in adddfa"); + d = &g->g_dfa[g->g_ndfas++]; + d->d_type = type; + d->d_name = strdup(name); + d->d_nstates = 0; + d->d_state = NULL; + d->d_initial = -1; + d->d_first = NULL; + return d; /* Only use while fresh! */ +} + +int +addstate(dfa *d) +{ + state *s; + + PyMem_RESIZE(d->d_state, state, d->d_nstates + 1); + if (d->d_state == NULL) + Py_FatalError("no mem to resize state in addstate"); + s = &d->d_state[d->d_nstates++]; + s->s_narcs = 0; + s->s_arc = NULL; + s->s_lower = 0; + s->s_upper = 0; + s->s_accel = NULL; + s->s_accept = 0; + return s - d->d_state; +} + +void +addarc(dfa *d, int from, int to, int lbl) +{ + state *s; + arc *a; + + assert(0 <= from && from < d->d_nstates); + assert(0 <= to && to < d->d_nstates); + + s = &d->d_state[from]; + PyMem_RESIZE(s->s_arc, arc, s->s_narcs + 1); + if (s->s_arc == NULL) + Py_FatalError("no mem to resize arc list in addarc"); + a = &s->s_arc[s->s_narcs++]; + a->a_lbl = lbl; + a->a_arrow = to; +} + +int +addlabel(labellist *ll, int type, char *str) +{ + int i; + label *lb; + + for (i = 0; i < ll->ll_nlabels; i++) { + if (ll->ll_label[i].lb_type == type && + strcmp(ll->ll_label[i].lb_str, str) == 0) + return i; + } + PyMem_RESIZE(ll->ll_label, label, ll->ll_nlabels + 1); + if (ll->ll_label == NULL) + Py_FatalError("no mem to resize labellist in addlabel"); + lb = &ll->ll_label[ll->ll_nlabels++]; + lb->lb_type = type; + lb->lb_str = strdup(str); + if (Py_DebugFlag) + printf("Label @ %08x, %d: %s\n", (unsigned)ll, ll->ll_nlabels, + PyGrammar_LabelRepr(lb)); + return lb - ll->ll_label; +} + +/* Same, but rather dies than adds */ + +int +findlabel(labellist *ll, int type, char *str) +{ + int i; + + for (i = 0; i < ll->ll_nlabels; i++) { + if (ll->ll_label[i].lb_type == type /*&& + strcmp(ll->ll_label[i].lb_str, str) == 0*/) + return i; + } + fprintf(stderr, "Label %d/'%s' not found\n", type, str); + Py_FatalError("grammar.c:findlabel()"); + return 0; /* Make gcc -Wall happy */ +} + +/* Forward */ +static void translabel(grammar *, label *); + +void +translatelabels(grammar *g) +{ + int i; + +#ifdef Py_DEBUG + printf("Translating labels ...\n"); +#endif + /* Don't translate EMPTY */ + for (i = EMPTY+1; i < g->g_ll.ll_nlabels; i++) + translabel(g, &g->g_ll.ll_label[i]); +} + +static void +translabel(grammar *g, label *lb) +{ + int i; + + if (Py_DebugFlag) + printf("Translating label %s ...\n", PyGrammar_LabelRepr(lb)); + + if (lb->lb_type == NAME) { + for (i = 0; i < g->g_ndfas; i++) { + if (strcmp(lb->lb_str, g->g_dfa[i].d_name) == 0) { + if (Py_DebugFlag) + printf( + "Label %s is non-terminal %d.\n", + lb->lb_str, + g->g_dfa[i].d_type); + lb->lb_type = g->g_dfa[i].d_type; + free(lb->lb_str); + lb->lb_str = NULL; + return; + } + } + for (i = 0; i < (int)N_TOKENS; i++) { + if (strcmp(lb->lb_str, _PyParser_TokenNames[i]) == 0) { + if (Py_DebugFlag) + printf("Label %s is terminal %d.\n", + lb->lb_str, i); + lb->lb_type = i; + free(lb->lb_str); + lb->lb_str = NULL; + return; + } + } + printf("Can't translate NAME label '%s'\n", lb->lb_str); + return; + } + + if (lb->lb_type == STRING) { + if (isalpha((int)(lb->lb_str[1])) || lb->lb_str[1] == '_') { + char *p; + char *src; + char *dest; + size_t name_len; + if (Py_DebugFlag) + printf("Label %s is a keyword\n", lb->lb_str); + lb->lb_type = NAME; + src = lb->lb_str + 1; + p = strchr(src, '\''); + if (p) + name_len = p - src; + else + name_len = strlen(src); + dest = malloc(name_len + 1); + strncpy(dest, src, name_len); + dest[name_len] = '\0'; + free(lb->lb_str); + lb->lb_str = dest; + } + else if (lb->lb_str[2] == lb->lb_str[0]) { + int type = (int) PyToken_OneChar(lb->lb_str[1]); + if (type != OP) { + lb->lb_type = type; + free(lb->lb_str); + lb->lb_str = NULL; + } + else + printf("Unknown OP label %s\n", + lb->lb_str); + } + else if (lb->lb_str[2] && lb->lb_str[3] == lb->lb_str[0]) { + int type = (int) PyToken_TwoChars(lb->lb_str[1], + lb->lb_str[2]); + if (type != OP) { + lb->lb_type = type; + free(lb->lb_str); + lb->lb_str = NULL; + } + else + printf("Unknown OP label %s\n", + lb->lb_str); + } + else if (lb->lb_str[2] && lb->lb_str[3] && lb->lb_str[4] == lb->lb_str[0]) { + int type = (int) PyToken_ThreeChars(lb->lb_str[1], + lb->lb_str[2], + lb->lb_str[3]); + if (type != OP) { + lb->lb_type = type; + free(lb->lb_str); + lb->lb_str = NULL; + } + else + printf("Unknown OP label %s\n", + lb->lb_str); + } + else + printf("Can't translate STRING label %s\n", + lb->lb_str); + } + else + printf("Can't translate label '%s'\n", + PyGrammar_LabelRepr(lb)); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar.mak b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar.mak new file mode 100644 index 00000000..45413a3e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar.mak @@ -0,0 +1,45 @@ +# This manages to rebuild graminit.{h, c} under MSVC 6 (Windows), via +# +# nmake /f grammar.mak +# +# You may also need to copy python23.dll into this directory, or get +# it on your search path. +# +# The intermediate files can be nuked afterwards: +# +# nmake /f grammar.mak clean +# +# I don't understand the maze of preprocessor #define's on Windows, and +# as a result this requires linking with python23.lib, so it's of no use +# for bootstrapping (the cause appears to be a useless-- in this +# particular case --pragma in PC\pyconfig.h, which demands that +# python23.lib get linked in). + +LIBS= ..\PCbuild\python23.lib + +CFLAGS= /I ..\Include /I ..\PC /D MS_NO_COREDLL /D PGEN /MD + +GRAMMAR_H= ..\Include\graminit.h +GRAMMAR_C= ..\Python\graminit.c +GRAMMAR_INPUT= ..\Grammar\Grammar + +PGEN= pgen.exe + +POBJS= acceler.obj grammar1.obj listnode.obj node.obj parser.obj \ + parsetok.obj tokenizer.obj bitset.obj metagrammar.obj + +PARSER_OBJS= $(POBJS) myreadline.obj + +PGOBJS= firstsets.obj grammar.obj pgen.obj printgrammar.obj pgenmain.obj + +PGENOBJS= $(POBJS) $(PGOBJS) + +$(GRAMMAR_H) $(GRAMMAR_C): $(PGEN) $(GRAMMAR_INPUT) + $(PGEN) $(GRAMMAR_INPUT) $(GRAMMAR_H) $(GRAMMAR_C) + +$(PGEN): $(PGENOBJS) + $(CC) $(PGENOBJS) $(LIBS) /Fe$(PGEN) + +clean: + del *.obj + del $(PGEN) diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar1.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar1.c new file mode 100644 index 00000000..b1fe0c18 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/grammar1.c @@ -0,0 +1,57 @@ + +/* Grammar subroutines needed by parser */ + +#include "Python.h" +#include "pgenheaders.h" +#include "grammar.h" +#include "token.h" + +/* Return the DFA for the given type */ + +dfa * +PyGrammar_FindDFA(grammar *g, register int type) +{ + register dfa *d; +#if 1 + /* Massive speed-up */ + d = &g->g_dfa[type - NT_OFFSET]; + assert(d->d_type == type); + return d; +#else + /* Old, slow version */ + register int i; + + for (i = g->g_ndfas, d = g->g_dfa; --i >= 0; d++) { + if (d->d_type == type) + return d; + } + assert(0); + /* NOTREACHED */ +#endif +} + +char * +PyGrammar_LabelRepr(label *lb) +{ + static char buf[100]; + + if (lb->lb_type == ENDMARKER) + return "EMPTY"; + else if (ISNONTERMINAL(lb->lb_type)) { + if (lb->lb_str == NULL) { + PyOS_snprintf(buf, sizeof(buf), "NT%d", lb->lb_type); + return buf; + } + else + return lb->lb_str; + } + else { + if (lb->lb_str == NULL) + return _PyParser_TokenNames[lb->lb_type]; + else { + PyOS_snprintf(buf, sizeof(buf), "%.32s(%.32s)", + _PyParser_TokenNames[lb->lb_type], lb->lb_str); + return buf; + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/intrcheck.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/intrcheck.c new file mode 100644 index 00000000..4935dcff --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/intrcheck.c @@ -0,0 +1,193 @@ + +/* Check for interrupts */ + +#include "Python.h" + +#ifdef QUICKWIN + +#include + +void +PyOS_InitInterrupts(void) +{ +} + +void +PyOS_FiniInterrupts(void) +{ +} + +int +PyOS_InterruptOccurred(void) +{ + _wyield(); +} + +#define OK + +#endif /* QUICKWIN */ + +#if defined(_M_IX86) && !defined(__QNX__) +#include +#endif + +#if defined(MSDOS) && !defined(QUICKWIN) + +#ifdef __GNUC__ + +/* This is for DJGPP's GO32 extender. I don't know how to trap + * control-C (There's no API for ctrl-C, and I don't want to mess with + * the interrupt vectors.) However, this DOES catch control-break. + * --Amrit + */ + +#include + +void +PyOS_InitInterrupts(void) +{ + _go32_want_ctrl_break(1 /* TRUE */); +} + +void +PyOS_FiniInterrupts(void) +{ +} + +int +PyOS_InterruptOccurred(void) +{ + return _go32_was_ctrl_break_hit(); +} + +#else /* !__GNUC__ */ + +/* This might work for MS-DOS (untested though): */ + +void +PyOS_InitInterrupts(void) +{ +} + +void +PyOS_FiniInterrupts(void) +{ +} + +int +PyOS_InterruptOccurred(void) +{ + int interrupted = 0; + while (kbhit()) { + if (getch() == '\003') + interrupted = 1; + } + return interrupted; +} + +#endif /* __GNUC__ */ + +#define OK + +#endif /* MSDOS && !QUICKWIN */ + + +#ifdef macintosh + +/* The Mac interrupt code has moved to macglue.c */ +#define OK + +#endif /* macintosh */ + + +#ifndef OK + +/* Default version -- for real operating systems and for Standard C */ + +#include +#include +#include + +static int interrupted; + +void +PyErr_SetInterrupt(void) +{ + interrupted = 1; +} + +extern int PyErr_CheckSignals(void); + +static int +checksignals_witharg(void * arg) +{ + return PyErr_CheckSignals(); +} + +static void +intcatcher(int sig) +{ + extern void Py_Exit(int); + static char message[] = +"python: to interrupt a truly hanging Python program, interrupt once more.\n"; + switch (interrupted++) { + case 0: + break; + case 1: +#ifdef RISCOS + fprintf(stderr, message); +#else + write(2, message, strlen(message)); +#endif + break; + case 2: + interrupted = 0; + Py_Exit(1); + break; + } + signal(SIGINT, intcatcher); + Py_AddPendingCall(checksignals_witharg, NULL); +} + +static void (*old_siginthandler)(int) = SIG_DFL; + +void +PyOS_InitInterrupts(void) +{ + if ((old_siginthandler = signal(SIGINT, SIG_IGN)) != SIG_IGN) + signal(SIGINT, intcatcher); +#ifdef HAVE_SIGINTERRUPT + /* This is for SunOS and other modern BSD derivatives. + It means that system calls (like read()) are not restarted + after an interrupt. This is necessary so interrupting a + read() or readline() call works as expected. + XXX On old BSD (pure 4.2 or older) you may have to do this + differently! */ + siginterrupt(SIGINT, 1); +#endif /* HAVE_SIGINTERRUPT */ +} + +void +PyOS_FiniInterrupts(void) +{ + signal(SIGINT, old_siginthandler); +} + +int +PyOS_InterruptOccurred(void) +{ + if (!interrupted) + return 0; + interrupted = 0; + return 1; +} + +#endif /* !OK */ + +void +PyOS_AfterFork(void) +{ +#ifdef WITH_THREAD + PyEval_ReInitThreads(); +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/listnode.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/listnode.c new file mode 100644 index 00000000..0e9fa450 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/listnode.c @@ -0,0 +1,66 @@ + +/* List a node on a file */ + +#include "pgenheaders.h" +#include "token.h" +#include "node.h" + +/* Forward */ +static void list1node(FILE *, node *); +static void listnode(FILE *, node *); + +void +PyNode_ListTree(node *n) +{ + listnode(stdout, n); +} + +static int level, atbol; + +static void +listnode(FILE *fp, node *n) +{ + level = 0; + atbol = 1; + list1node(fp, n); +} + +static void +list1node(FILE *fp, node *n) +{ + if (n == 0) + return; + if (ISNONTERMINAL(TYPE(n))) { + int i; + for (i = 0; i < NCH(n); i++) + list1node(fp, CHILD(n, i)); + } + else if (ISTERMINAL(TYPE(n))) { + switch (TYPE(n)) { + case INDENT: + ++level; + break; + case DEDENT: + --level; + break; + default: + if (atbol) { + int i; + for (i = 0; i < level; ++i) + fprintf(fp, "\t"); + atbol = 0; + } + if (TYPE(n) == NEWLINE) { + if (STR(n) != NULL) + fprintf(fp, "%s", STR(n)); + fprintf(fp, "\n"); + atbol = 1; + } + else + fprintf(fp, "%s ", STR(n)); + break; + } + } + else + fprintf(fp, "? "); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/metagrammar.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/metagrammar.c new file mode 100644 index 00000000..20cc7c8b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/metagrammar.c @@ -0,0 +1,159 @@ + +#include "pgenheaders.h" +#include "metagrammar.h" +#include "grammar.h" +#include "pgen.h" +static arc arcs_0_0[3] = { + {2, 0}, + {3, 0}, + {4, 1}, +}; +static arc arcs_0_1[1] = { + {0, 1}, +}; +static state states_0[2] = { + {3, arcs_0_0}, + {1, arcs_0_1}, +}; +static arc arcs_1_0[1] = { + {5, 1}, +}; +static arc arcs_1_1[1] = { + {6, 2}, +}; +static arc arcs_1_2[1] = { + {7, 3}, +}; +static arc arcs_1_3[1] = { + {3, 4}, +}; +static arc arcs_1_4[1] = { + {0, 4}, +}; +static state states_1[5] = { + {1, arcs_1_0}, + {1, arcs_1_1}, + {1, arcs_1_2}, + {1, arcs_1_3}, + {1, arcs_1_4}, +}; +static arc arcs_2_0[1] = { + {8, 1}, +}; +static arc arcs_2_1[2] = { + {9, 0}, + {0, 1}, +}; +static state states_2[2] = { + {1, arcs_2_0}, + {2, arcs_2_1}, +}; +static arc arcs_3_0[1] = { + {10, 1}, +}; +static arc arcs_3_1[2] = { + {10, 1}, + {0, 1}, +}; +static state states_3[2] = { + {1, arcs_3_0}, + {2, arcs_3_1}, +}; +static arc arcs_4_0[2] = { + {11, 1}, + {13, 2}, +}; +static arc arcs_4_1[1] = { + {7, 3}, +}; +static arc arcs_4_2[3] = { + {14, 4}, + {15, 4}, + {0, 2}, +}; +static arc arcs_4_3[1] = { + {12, 4}, +}; +static arc arcs_4_4[1] = { + {0, 4}, +}; +static state states_4[5] = { + {2, arcs_4_0}, + {1, arcs_4_1}, + {3, arcs_4_2}, + {1, arcs_4_3}, + {1, arcs_4_4}, +}; +static arc arcs_5_0[3] = { + {5, 1}, + {16, 1}, + {17, 2}, +}; +static arc arcs_5_1[1] = { + {0, 1}, +}; +static arc arcs_5_2[1] = { + {7, 3}, +}; +static arc arcs_5_3[1] = { + {18, 1}, +}; +static state states_5[4] = { + {3, arcs_5_0}, + {1, arcs_5_1}, + {1, arcs_5_2}, + {1, arcs_5_3}, +}; +static dfa dfas[6] = { + {256, "MSTART", 0, 2, states_0, + "\070\000\000"}, + {257, "RULE", 0, 5, states_1, + "\040\000\000"}, + {258, "RHS", 0, 2, states_2, + "\040\010\003"}, + {259, "ALT", 0, 2, states_3, + "\040\010\003"}, + {260, "ITEM", 0, 5, states_4, + "\040\010\003"}, + {261, "ATOM", 0, 4, states_5, + "\040\000\003"}, +}; +static label labels[19] = { + {0, "EMPTY"}, + {256, 0}, + {257, 0}, + {4, 0}, + {0, 0}, + {1, 0}, + {11, 0}, + {258, 0}, + {259, 0}, + {18, 0}, + {260, 0}, + {9, 0}, + {10, 0}, + {261, 0}, + {16, 0}, + {14, 0}, + {3, 0}, + {7, 0}, + {8, 0}, +}; +static grammar _PyParser_Grammar = { + 6, + dfas, + {19, labels}, + 256 +}; + +grammar * +meta_grammar(void) +{ + return &_PyParser_Grammar; +} + +grammar * +Py_meta_grammar(void) +{ + return meta_grammar(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/myreadline.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/myreadline.c new file mode 100644 index 00000000..f5122306 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/myreadline.c @@ -0,0 +1,191 @@ + +/* Readline interface for tokenizer.c and [raw_]input() in bltinmodule.c. + By default, or when stdin is not a tty device, we have a super + simple my_readline function using fgets. + Optionally, we can use the GNU readline library. + my_readline() has a different return value from GNU readline(): + - NULL if an interrupt occurred or if an error occurred + - a malloc'ed empty string if EOF was read + - a malloc'ed string ending in \n normally +*/ + +#include "Python.h" +#ifdef MS_WINDOWS +#ifdef MS_XBOX +#include +#else +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#endif +#endif /* MS_WINDOWS */ + +#ifdef __VMS +extern char* vms__StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt); +#endif + +int (*PyOS_InputHook)(void) = NULL; + +#ifdef RISCOS +int Py_RISCOSWimpFlag; +#endif + +/* This function restarts a fgets() after an EINTR error occurred + except if PyOS_InterruptOccurred() returns true. */ + +static int +my_fgets(char *buf, int len, FILE *fp) +{ + char *p; + for (;;) { + if (PyOS_InputHook != NULL) + (void)(PyOS_InputHook)(); + errno = 0; + p = fgets(buf, len, fp); + if (p != NULL) + return 0; /* No error */ +#ifdef MS_WINDOWS + /* In the case of a Ctrl+C or some other external event + interrupting the operation: + Win2k/NT: ERROR_OPERATION_ABORTED is the most recent Win32 + error code (and feof() returns TRUE). + Win9x: Ctrl+C seems to have no effect on fgets() returning + early - the signal handler is called, but the fgets() + only returns "normally" (ie, when Enter hit or feof()) + */ + if (GetLastError()==ERROR_OPERATION_ABORTED) { + /* Signals come asynchronously, so we sleep a brief + moment before checking if the handler has been + triggered (we cant just return 1 before the + signal handler has been called, as the later + signal may be treated as a separate interrupt). + */ + Sleep(1); + if (PyOS_InterruptOccurred()) { + return 1; /* Interrupt */ + } + /* Either the sleep wasn't long enough (need a + short loop retrying?) or not interrupted at all + (in which case we should revisit the whole thing!) + Logging some warning would be nice. assert is not + viable as under the debugger, the various dialogs + mean the condition is not true. + */ + } +#endif /* MS_WINDOWS */ + if (feof(fp)) { + return -1; /* EOF */ + } +#ifdef EINTR + if (errno == EINTR) { + if (PyOS_InterruptOccurred()) { + return 1; /* Interrupt */ + } + continue; + } +#endif + if (PyOS_InterruptOccurred()) { + return 1; /* Interrupt */ + } + return -2; /* Error */ + } + /* NOTREACHED */ +} + + +/* Readline implementation using fgets() */ + +char * +PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) +{ + size_t n; + char *p; + n = 100; + if ((p = PyMem_MALLOC(n)) == NULL) + return NULL; + fflush(sys_stdout); +#ifndef RISCOS + if (prompt) + fprintf(stderr, "%s", prompt); +#else + if (prompt) { + if(Py_RISCOSWimpFlag) + fprintf(stderr, "\x0cr%s\x0c", prompt); + else + fprintf(stderr, "%s", prompt); + } +#endif + fflush(stderr); + switch (my_fgets(p, (int)n, sys_stdin)) { + case 0: /* Normal case */ + break; + case 1: /* Interrupt */ + PyMem_FREE(p); + return NULL; + case -1: /* EOF */ + case -2: /* Error */ + default: /* Shouldn't happen */ + *p = '\0'; + break; + } +#ifdef MPW + /* Hack for MPW C where the prompt comes right back in the input */ + /* XXX (Actually this would be rather nice on most systems...) */ + n = strlen(prompt); + if (strncmp(p, prompt, n) == 0) + memmove(p, p + n, strlen(p) - n + 1); +#endif + n = strlen(p); + while (n > 0 && p[n-1] != '\n') { + size_t incr = n+2; + p = PyMem_REALLOC(p, n + incr); + if (p == NULL) + return NULL; + if (incr > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, "input line too long"); + } + if (my_fgets(p+n, (int)incr, sys_stdin) != 0) + break; + n += strlen(p+n); + } + return PyMem_REALLOC(p, n+1); +} + + +/* By initializing this function pointer, systems embedding Python can + override the readline function. + + Note: Python expects in return a buffer allocated with PyMem_Malloc. */ + +char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, char *); + + +/* Interface used by tokenizer.c and bltinmodule.c */ + +char * +PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) +{ + char *rv; + + if (PyOS_ReadlineFunctionPointer == NULL) { +#ifdef __VMS + PyOS_ReadlineFunctionPointer = vms__StdioReadline; +#else + PyOS_ReadlineFunctionPointer = PyOS_StdioReadline; +#endif + } + + Py_BEGIN_ALLOW_THREADS + + /* This is needed to handle the unlikely case that the + * interpreter is in interactive mode *and* stdin/out are not + * a tty. This can happen, for example if python is run like + * this: python -i < test1.py + */ + if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout))) + rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt); + else + rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout, + prompt); + Py_END_ALLOW_THREADS + return rv; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/node.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/node.c new file mode 100644 index 00000000..8d786e09 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/node.c @@ -0,0 +1,134 @@ +/* Parse tree node implementation */ + +#include "Python.h" +#include "node.h" +#include "errcode.h" + +node * +PyNode_New(int type) +{ + node *n = (node *) PyObject_MALLOC(1 * sizeof(node)); + if (n == NULL) + return NULL; + n->n_type = type; + n->n_str = NULL; + n->n_lineno = 0; + n->n_nchildren = 0; + n->n_child = NULL; + return n; +} + +/* See comments at XXXROUNDUP below. Returns -1 on overflow. */ +static int +fancy_roundup(int n) +{ + /* Round up to the closest power of 2 >= n. */ + int result = 256; + assert(n > 128); + while (result < n) { + result <<= 1; + if (result <= 0) + return -1; + } + return result; +} + +/* A gimmick to make massive numbers of reallocs quicker. The result is + * a number >= the input. In PyNode_AddChild, it's used like so, when + * we're about to add child number current_size + 1: + * + * if XXXROUNDUP(current_size) < XXXROUNDUP(current_size + 1): + * allocate space for XXXROUNDUP(current_size + 1) total children + * else: + * we already have enough space + * + * Since a node starts out empty, we must have + * + * XXXROUNDUP(0) < XXXROUNDUP(1) + * + * so that we allocate space for the first child. One-child nodes are very + * common (presumably that would change if we used a more abstract form + * of syntax tree), so to avoid wasting memory it's desirable that + * XXXROUNDUP(1) == 1. That in turn forces XXXROUNDUP(0) == 0. + * + * Else for 2 <= n <= 128, we round up to the closest multiple of 4. Why 4? + * Rounding up to a multiple of an exact power of 2 is very efficient, and + * most nodes with more than one child have <= 4 kids. + * + * Else we call fancy_roundup() to grow proportionately to n. We've got an + * extreme case then (like test_longexp.py), and on many platforms doing + * anything less than proportional growth leads to exorbitant runtime + * (e.g., MacPython), or extreme fragmentation of user address space (e.g., + * Win98). + * + * In a run of compileall across the 2.3a0 Lib directory, Andrew MacIntyre + * reported that, with this scheme, 89% of PyMem_RESIZE calls in + * PyNode_AddChild passed 1 for the size, and 9% passed 4. So this usually + * wastes very little memory, but is very effective at sidestepping + * platform-realloc disasters on vulnernable platforms. + * + * Note that this would be straightforward if a node stored its current + * capacity. The code is tricky to avoid that. + */ +#define XXXROUNDUP(n) ((n) <= 1 ? (n) : \ + (n) <= 128 ? (((n) + 3) & ~3) : \ + fancy_roundup(n)) + + +int +PyNode_AddChild(register node *n1, int type, char *str, int lineno) +{ + const int nch = n1->n_nchildren; + int current_capacity; + int required_capacity; + node *n; + + if (nch == INT_MAX || nch < 0) + return E_OVERFLOW; + + current_capacity = XXXROUNDUP(nch); + required_capacity = XXXROUNDUP(nch + 1); + if (current_capacity < 0 || required_capacity < 0) + return E_OVERFLOW; + if (current_capacity < required_capacity) { + n = n1->n_child; + n = (node *) PyObject_REALLOC(n, + required_capacity * sizeof(node)); + if (n == NULL) + return E_NOMEM; + n1->n_child = n; + } + + n = &n1->n_child[n1->n_nchildren++]; + n->n_type = type; + n->n_str = str; + n->n_lineno = lineno; + n->n_nchildren = 0; + n->n_child = NULL; + return 0; +} + +/* Forward */ +static void freechildren(node *); + + +void +PyNode_Free(node *n) +{ + if (n != NULL) { + freechildren(n); + PyObject_FREE(n); + } +} + +static void +freechildren(node *n) +{ + int i; + for (i = NCH(n); --i >= 0; ) + freechildren(CHILD(n, i)); + if (n->n_child != NULL) + PyObject_FREE(n->n_child); + if (STR(n) != NULL) + PyObject_FREE(STR(n)); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parser.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parser.c new file mode 100644 index 00000000..855b254a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parser.c @@ -0,0 +1,418 @@ + +/* Parser implementation */ + +/* For a description, see the comments at end of this file */ + +/* XXX To do: error recovery */ + +#include "Python.h" +#include "pgenheaders.h" +#include "token.h" +#include "grammar.h" +#include "node.h" +#include "parser.h" +#include "errcode.h" + + +#ifdef Py_DEBUG +extern int Py_DebugFlag; +#define D(x) if (!Py_DebugFlag); else x +#else +#define D(x) +#endif + + +/* STACK DATA TYPE */ + +static void s_reset(stack *); + +static void +s_reset(stack *s) +{ + s->s_top = &s->s_base[MAXSTACK]; +} + +#define s_empty(s) ((s)->s_top == &(s)->s_base[MAXSTACK]) + +static int +s_push(register stack *s, dfa *d, node *parent) +{ + register stackentry *top; + if (s->s_top == s->s_base) { + fprintf(stderr, "s_push: parser stack overflow\n"); + return E_NOMEM; + } + top = --s->s_top; + top->s_dfa = d; + top->s_parent = parent; + top->s_state = 0; + return 0; +} + +#ifdef Py_DEBUG + +static void +s_pop(register stack *s) +{ + if (s_empty(s)) + Py_FatalError("s_pop: parser stack underflow -- FATAL"); + s->s_top++; +} + +#else /* !Py_DEBUG */ + +#define s_pop(s) (s)->s_top++ + +#endif + + +/* PARSER CREATION */ + +parser_state * +PyParser_New(grammar *g, int start) +{ + parser_state *ps; + + if (!g->g_accel) + PyGrammar_AddAccelerators(g); + ps = PyMem_NEW(parser_state, 1); + if (ps == NULL) + return NULL; + ps->p_grammar = g; +#if 0 /* future keyword */ + ps->p_generators = 0; +#endif + ps->p_tree = PyNode_New(start); + if (ps->p_tree == NULL) { + PyMem_DEL(ps); + return NULL; + } + s_reset(&ps->p_stack); + (void) s_push(&ps->p_stack, PyGrammar_FindDFA(g, start), ps->p_tree); + return ps; +} + +void +PyParser_Delete(parser_state *ps) +{ + /* NB If you want to save the parse tree, + you must set p_tree to NULL before calling delparser! */ + PyNode_Free(ps->p_tree); + PyMem_DEL(ps); +} + + +/* PARSER STACK OPERATIONS */ + +static int +shift(register stack *s, int type, char *str, int newstate, int lineno) +{ + int err; + assert(!s_empty(s)); + err = PyNode_AddChild(s->s_top->s_parent, type, str, lineno); + if (err) + return err; + s->s_top->s_state = newstate; + return 0; +} + +static int +push(register stack *s, int type, dfa *d, int newstate, int lineno) +{ + int err; + register node *n; + n = s->s_top->s_parent; + assert(!s_empty(s)); + err = PyNode_AddChild(n, type, (char *)NULL, lineno); + if (err) + return err; + s->s_top->s_state = newstate; + return s_push(s, d, CHILD(n, NCH(n)-1)); +} + + +/* PARSER PROPER */ + +static int +classify(parser_state *ps, int type, char *str) +{ + grammar *g = ps->p_grammar; + register int n = g->g_ll.ll_nlabels; + + if (type == NAME) { + register char *s = str; + register label *l = g->g_ll.ll_label; + register int i; + for (i = n; i > 0; i--, l++) { + if (l->lb_type == NAME && l->lb_str != NULL && + l->lb_str[0] == s[0] && + strcmp(l->lb_str, s) == 0) { +#if 0 /* future keyword */ + if (!ps->p_generators && + s[0] == 'y' && + strcmp(s, "yield") == 0) + break; /* not a keyword */ +#endif + D(printf("It's a keyword\n")); + return n - i; + } + } + } + + { + register label *l = g->g_ll.ll_label; + register int i; + for (i = n; i > 0; i--, l++) { + if (l->lb_type == type && l->lb_str == NULL) { + D(printf("It's a token we know\n")); + return n - i; + } + } + } + + D(printf("Illegal token\n")); + return -1; +} + +#if 0 /* future keyword */ +static void +future_hack(parser_state *ps) +{ + node *n = ps->p_stack.s_top->s_parent; + node *ch; + int i; + + if (strcmp(STR(CHILD(n, 0)), "from") != 0) + return; + ch = CHILD(n, 1); + if (strcmp(STR(CHILD(ch, 0)), "__future__") != 0) + return; + for (i = 3; i < NCH(n); i += 2) { + ch = CHILD(n, i); + if (NCH(ch) >= 1 && TYPE(CHILD(ch, 0)) == NAME && + strcmp(STR(CHILD(ch, 0)), "generators") == 0) { + ps->p_generators = 1; + break; + } + } +} +#endif /* future keyword */ + +int +PyParser_AddToken(register parser_state *ps, register int type, char *str, + int lineno, int *expected_ret) +{ + register int ilabel; + int err; + + D(printf("Token %s/'%s' ... ", _PyParser_TokenNames[type], str)); + + /* Find out which label this token is */ + ilabel = classify(ps, type, str); + if (ilabel < 0) + return E_SYNTAX; + + /* Loop until the token is shifted or an error occurred */ + for (;;) { + /* Fetch the current dfa and state */ + register dfa *d = ps->p_stack.s_top->s_dfa; + register state *s = &d->d_state[ps->p_stack.s_top->s_state]; + + D(printf(" DFA '%s', state %d:", + d->d_name, ps->p_stack.s_top->s_state)); + + /* Check accelerator */ + if (s->s_lower <= ilabel && ilabel < s->s_upper) { + register int x = s->s_accel[ilabel - s->s_lower]; + if (x != -1) { + if (x & (1<<7)) { + /* Push non-terminal */ + int nt = (x >> 8) + NT_OFFSET; + int arrow = x & ((1<<7)-1); + dfa *d1 = PyGrammar_FindDFA( + ps->p_grammar, nt); + if ((err = push(&ps->p_stack, nt, d1, + arrow, lineno)) > 0) { + D(printf(" MemError: push\n")); + return err; + } + D(printf(" Push ...\n")); + continue; + } + + /* Shift the token */ + if ((err = shift(&ps->p_stack, type, str, + x, lineno)) > 0) { + D(printf(" MemError: shift.\n")); + return err; + } + D(printf(" Shift.\n")); + /* Pop while we are in an accept-only state */ + while (s = &d->d_state + [ps->p_stack.s_top->s_state], + s->s_accept && s->s_narcs == 1) { + D(printf(" DFA '%s', state %d: " + "Direct pop.\n", + d->d_name, + ps->p_stack.s_top->s_state)); +#if 0 /* future keyword */ + if (d->d_name[0] == 'i' && + strcmp(d->d_name, + "import_stmt") == 0) + future_hack(ps); +#endif + s_pop(&ps->p_stack); + if (s_empty(&ps->p_stack)) { + D(printf(" ACCEPT.\n")); + return E_DONE; + } + d = ps->p_stack.s_top->s_dfa; + } + return E_OK; + } + } + + if (s->s_accept) { +#if 0 /* future keyword */ + if (d->d_name[0] == 'i' && + strcmp(d->d_name, "import_stmt") == 0) + future_hack(ps); +#endif + /* Pop this dfa and try again */ + s_pop(&ps->p_stack); + D(printf(" Pop ...\n")); + if (s_empty(&ps->p_stack)) { + D(printf(" Error: bottom of stack.\n")); + return E_SYNTAX; + } + continue; + } + + /* Stuck, report syntax error */ + D(printf(" Error.\n")); + if (expected_ret) { + if (s->s_lower == s->s_upper - 1) { + /* Only one possible expected token */ + *expected_ret = ps->p_grammar-> + g_ll.ll_label[s->s_lower].lb_type; + } + else + *expected_ret = -1; + } + return E_SYNTAX; + } +} + + +#ifdef Py_DEBUG + +/* DEBUG OUTPUT */ + +void +dumptree(grammar *g, node *n) +{ + int i; + + if (n == NULL) + printf("NIL"); + else { + label l; + l.lb_type = TYPE(n); + l.lb_str = STR(n); + printf("%s", PyGrammar_LabelRepr(&l)); + if (ISNONTERMINAL(TYPE(n))) { + printf("("); + for (i = 0; i < NCH(n); i++) { + if (i > 0) + printf(","); + dumptree(g, CHILD(n, i)); + } + printf(")"); + } + } +} + +void +showtree(grammar *g, node *n) +{ + int i; + + if (n == NULL) + return; + if (ISNONTERMINAL(TYPE(n))) { + for (i = 0; i < NCH(n); i++) + showtree(g, CHILD(n, i)); + } + else if (ISTERMINAL(TYPE(n))) { + printf("%s", _PyParser_TokenNames[TYPE(n)]); + if (TYPE(n) == NUMBER || TYPE(n) == NAME) + printf("(%s)", STR(n)); + printf(" "); + } + else + printf("? "); +} + +void +printtree(parser_state *ps) +{ + if (Py_DebugFlag) { + printf("Parse tree:\n"); + dumptree(ps->p_grammar, ps->p_tree); + printf("\n"); + printf("Tokens:\n"); + showtree(ps->p_grammar, ps->p_tree); + printf("\n"); + } + printf("Listing:\n"); + PyNode_ListTree(ps->p_tree); + printf("\n"); +} + +#endif /* Py_DEBUG */ + +/* + +Description +----------- + +The parser's interface is different than usual: the function addtoken() +must be called for each token in the input. This makes it possible to +turn it into an incremental parsing system later. The parsing system +constructs a parse tree as it goes. + +A parsing rule is represented as a Deterministic Finite-state Automaton +(DFA). A node in a DFA represents a state of the parser; an arc represents +a transition. Transitions are either labeled with terminal symbols or +with non-terminals. When the parser decides to follow an arc labeled +with a non-terminal, it is invoked recursively with the DFA representing +the parsing rule for that as its initial state; when that DFA accepts, +the parser that invoked it continues. The parse tree constructed by the +recursively called parser is inserted as a child in the current parse tree. + +The DFA's can be constructed automatically from a more conventional +language description. An extended LL(1) grammar (ELL(1)) is suitable. +Certain restrictions make the parser's life easier: rules that can produce +the empty string should be outlawed (there are other ways to put loops +or optional parts in the language). To avoid the need to construct +FIRST sets, we can require that all but the last alternative of a rule +(really: arc going out of a DFA's state) must begin with a terminal +symbol. + +As an example, consider this grammar: + +expr: term (OP term)* +term: CONSTANT | '(' expr ')' + +The DFA corresponding to the rule for expr is: + +------->.---term-->.-------> + ^ | + | | + \----OP----/ + +The parse tree generated for the input a+b is: + +(expr: (term: (NAME: a)), (OP: +), (term: (NAME: b))) + +*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parser.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parser.h new file mode 100644 index 00000000..141fe8b0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parser.h @@ -0,0 +1,42 @@ +#ifndef Py_PARSER_H +#define Py_PARSER_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Parser interface */ + +#define MAXSTACK 500 + +typedef struct { + int s_state; /* State in current DFA */ + dfa *s_dfa; /* Current DFA */ + struct _node *s_parent; /* Where to add next node */ +} stackentry; + +typedef struct { + stackentry *s_top; /* Top entry */ + stackentry s_base[MAXSTACK];/* Array of stack entries */ + /* NB The stack grows down */ +} stack; + +typedef struct { + stack p_stack; /* Stack of parser states */ + grammar *p_grammar; /* Grammar to use */ + node *p_tree; /* Top of parse tree */ +#if 0 /* future keyword */ + int p_generators; /* 1 if yield is a keyword */ +#endif +} parser_state; + +parser_state *PyParser_New(grammar *g, int start); +void PyParser_Delete(parser_state *ps); +int PyParser_AddToken(parser_state *ps, int type, char *str, int lineno, + int *expected_ret); +void PyGrammar_AddAccelerators(grammar *g); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PARSER_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parsetok.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parsetok.c new file mode 100644 index 00000000..c5ee455b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/parsetok.c @@ -0,0 +1,222 @@ + +/* Parser-tokenizer link implementation */ + +#include "pgenheaders.h" +#include "tokenizer.h" +#include "node.h" +#include "grammar.h" +#include "parser.h" +#include "parsetok.h" +#include "errcode.h" +#include "graminit.h" + +int Py_TabcheckFlag; + + +/* Forward */ +static node *parsetok(struct tok_state *, grammar *, int, perrdetail *, int); +static void initerr(perrdetail *err_ret, const char* filename); + +/* Parse input coming from a string. Return error code, print some errors. */ +node * +PyParser_ParseString(const char *s, grammar *g, int start, perrdetail *err_ret) +{ + return PyParser_ParseStringFlags(s, g, start, err_ret, 0); +} + +node * +PyParser_ParseStringFlags(const char *s, grammar *g, int start, + perrdetail *err_ret, int flags) +{ + return PyParser_ParseStringFlagsFilename(s, NULL, + g, start, err_ret, 0); +} + +node * +PyParser_ParseStringFlagsFilename(const char *s, const char *filename, + grammar *g, int start, + perrdetail *err_ret, int flags) +{ + struct tok_state *tok; + + initerr(err_ret, filename); + + if ((tok = PyTokenizer_FromString(s)) == NULL) { + err_ret->error = E_NOMEM; + return NULL; + } + + tok->filename = filename ? filename : ""; + if (Py_TabcheckFlag || Py_VerboseFlag) { + tok->altwarning = (tok->filename != NULL); + if (Py_TabcheckFlag >= 2) + tok->alterror++; + } + + return parsetok(tok, g, start, err_ret, flags); +} + + +/* Parse input coming from a file. Return error code, print some errors. */ + +node * +PyParser_ParseFile(FILE *fp, const char *filename, grammar *g, int start, + char *ps1, char *ps2, perrdetail *err_ret) +{ + return PyParser_ParseFileFlags(fp, filename, g, start, ps1, ps2, + err_ret, 0); +} + +node * +PyParser_ParseFileFlags(FILE *fp, const char *filename, grammar *g, int start, + char *ps1, char *ps2, perrdetail *err_ret, int flags) +{ + struct tok_state *tok; + + initerr(err_ret, filename); + + if ((tok = PyTokenizer_FromFile(fp, ps1, ps2)) == NULL) { + err_ret->error = E_NOMEM; + return NULL; + } + tok->filename = filename; + if (Py_TabcheckFlag || Py_VerboseFlag) { + tok->altwarning = (filename != NULL); + if (Py_TabcheckFlag >= 2) + tok->alterror++; + } + + + return parsetok(tok, g, start, err_ret, flags); +} + +/* Parse input coming from the given tokenizer structure. + Return error code. */ + +#if 0 /* future keyword */ +static char yield_msg[] = +"%s:%d: Warning: 'yield' will become a reserved keyword in the future\n"; +#endif + +static node * +parsetok(struct tok_state *tok, grammar *g, int start, perrdetail *err_ret, + int flags) +{ + parser_state *ps; + node *n; + int started = 0; + + if ((ps = PyParser_New(g, start)) == NULL) { + fprintf(stderr, "no mem for new parser\n"); + err_ret->error = E_NOMEM; + return NULL; + } +#if 0 /* future keyword */ + if (flags & PyPARSE_YIELD_IS_KEYWORD) + ps->p_generators = 1; +#endif + + for (;;) { + char *a, *b; + int type; + size_t len; + char *str; + + type = PyTokenizer_Get(tok, &a, &b); + if (type == ERRORTOKEN) { + err_ret->error = tok->done; + break; + } + if (type == ENDMARKER && started) { + type = NEWLINE; /* Add an extra newline */ + started = 0; + /* Add the right number of dedent tokens, + except if a certain flag is given -- + codeop.py uses this. */ + if (tok->indent && + !(flags & PyPARSE_DONT_IMPLY_DEDENT)) + { + tok->pendin = -tok->indent; + tok->indent = 0; + } + } + else + started = 1; + len = b - a; /* XXX this may compute NULL - NULL */ + str = (char *) PyObject_MALLOC(len + 1); + if (str == NULL) { + fprintf(stderr, "no mem for next token\n"); + err_ret->error = E_NOMEM; + break; + } + if (len > 0) + strncpy(str, a, len); + str[len] = '\0'; + +#if 0 /* future keyword */ + /* Warn about yield as NAME */ + if (type == NAME && !ps->p_generators && + len == 5 && str[0] == 'y' && strcmp(str, "yield") == 0) + PySys_WriteStderr(yield_msg, + err_ret->filename==NULL ? + "" : err_ret->filename, + tok->lineno); +#endif + + if ((err_ret->error = + PyParser_AddToken(ps, (int)type, str, tok->lineno, + &(err_ret->expected))) != E_OK) { + if (err_ret->error != E_DONE) + PyObject_FREE(str); + break; + } + } + + if (err_ret->error == E_DONE) { + n = ps->p_tree; + ps->p_tree = NULL; + } + else + n = NULL; + + PyParser_Delete(ps); + + if (n == NULL) { + if (tok->lineno <= 1 && tok->done == E_EOF) + err_ret->error = E_EOF; + err_ret->lineno = tok->lineno; + err_ret->offset = tok->cur - tok->buf; + if (tok->buf != NULL) { + size_t len = tok->inp - tok->buf; + err_ret->text = (char *) PyObject_MALLOC(len + 1); + if (err_ret->text != NULL) { + if (len > 0) + strncpy(err_ret->text, tok->buf, len); + err_ret->text[len] = '\0'; + } + } + } else if (tok->encoding != NULL) { + node* r = PyNode_New(encoding_decl); + r->n_str = tok->encoding; + r->n_nchildren = 1; + r->n_child = n; + tok->encoding = NULL; + n = r; + } + + PyTokenizer_Free(tok); + + return n; +} + +static void +initerr(perrdetail *err_ret, const char* filename) +{ + err_ret->error = E_OK; + err_ret->filename = filename; + err_ret->lineno = 0; + err_ret->offset = 0; + err_ret->text = NULL; + err_ret->token = -1; + err_ret->expected = -1; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/pgen.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/pgen.c new file mode 100644 index 00000000..b1ac446a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/pgen.c @@ -0,0 +1,706 @@ + +/* Parser generator */ +/* XXX This file is not yet fully PROTOized */ + +/* For a description, see the comments at end of this file */ + +#include "Python.h" +#include "pgenheaders.h" +#include "token.h" +#include "node.h" +#include "grammar.h" +#include "metagrammar.h" +#include "pgen.h" + +extern int Py_DebugFlag; +extern int Py_IgnoreEnvironmentFlag; /* needed by Py_GETENV */ + + +/* PART ONE -- CONSTRUCT NFA -- Cf. Algorithm 3.2 from [Aho&Ullman 77] */ + +typedef struct _nfaarc { + int ar_label; + int ar_arrow; +} nfaarc; + +typedef struct _nfastate { + int st_narcs; + nfaarc *st_arc; +} nfastate; + +typedef struct _nfa { + int nf_type; + char *nf_name; + int nf_nstates; + nfastate *nf_state; + int nf_start, nf_finish; +} nfa; + +/* Forward */ +static void compile_rhs(labellist *ll, + nfa *nf, node *n, int *pa, int *pb); +static void compile_alt(labellist *ll, + nfa *nf, node *n, int *pa, int *pb); +static void compile_item(labellist *ll, + nfa *nf, node *n, int *pa, int *pb); +static void compile_atom(labellist *ll, + nfa *nf, node *n, int *pa, int *pb); + +static int +addnfastate(nfa *nf) +{ + nfastate *st; + + PyMem_RESIZE(nf->nf_state, nfastate, nf->nf_nstates + 1); + if (nf->nf_state == NULL) + Py_FatalError("out of mem"); + st = &nf->nf_state[nf->nf_nstates++]; + st->st_narcs = 0; + st->st_arc = NULL; + return st - nf->nf_state; +} + +static void +addnfaarc(nfa *nf, int from, int to, int lbl) +{ + nfastate *st; + nfaarc *ar; + + st = &nf->nf_state[from]; + PyMem_RESIZE(st->st_arc, nfaarc, st->st_narcs + 1); + if (st->st_arc == NULL) + Py_FatalError("out of mem"); + ar = &st->st_arc[st->st_narcs++]; + ar->ar_label = lbl; + ar->ar_arrow = to; +} + +static nfa * +newnfa(char *name) +{ + nfa *nf; + static int type = NT_OFFSET; /* All types will be disjunct */ + + nf = PyMem_NEW(nfa, 1); + if (nf == NULL) + Py_FatalError("no mem for new nfa"); + nf->nf_type = type++; + nf->nf_name = name; /* XXX strdup(name) ??? */ + nf->nf_nstates = 0; + nf->nf_state = NULL; + nf->nf_start = nf->nf_finish = -1; + return nf; +} + +typedef struct _nfagrammar { + int gr_nnfas; + nfa **gr_nfa; + labellist gr_ll; +} nfagrammar; + +/* Forward */ +static void compile_rule(nfagrammar *gr, node *n); + +static nfagrammar * +newnfagrammar(void) +{ + nfagrammar *gr; + + gr = PyMem_NEW(nfagrammar, 1); + if (gr == NULL) + Py_FatalError("no mem for new nfa grammar"); + gr->gr_nnfas = 0; + gr->gr_nfa = NULL; + gr->gr_ll.ll_nlabels = 0; + gr->gr_ll.ll_label = NULL; + addlabel(&gr->gr_ll, ENDMARKER, "EMPTY"); + return gr; +} + +static nfa * +addnfa(nfagrammar *gr, char *name) +{ + nfa *nf; + + nf = newnfa(name); + PyMem_RESIZE(gr->gr_nfa, nfa *, gr->gr_nnfas + 1); + if (gr->gr_nfa == NULL) + Py_FatalError("out of mem"); + gr->gr_nfa[gr->gr_nnfas++] = nf; + addlabel(&gr->gr_ll, NAME, nf->nf_name); + return nf; +} + +#ifdef Py_DEBUG + +static char REQNFMT[] = "metacompile: less than %d children\n"; + +#define REQN(i, count) \ + if (i < count) { \ + fprintf(stderr, REQNFMT, count); \ + Py_FatalError("REQN"); \ + } else + +#else +#define REQN(i, count) /* empty */ +#endif + +static nfagrammar * +metacompile(node *n) +{ + nfagrammar *gr; + int i; + + if (Py_DebugFlag) + printf("Compiling (meta-) parse tree into NFA grammar\n"); + gr = newnfagrammar(); + REQ(n, MSTART); + i = n->n_nchildren - 1; /* Last child is ENDMARKER */ + n = n->n_child; + for (; --i >= 0; n++) { + if (n->n_type != NEWLINE) + compile_rule(gr, n); + } + return gr; +} + +static void +compile_rule(nfagrammar *gr, node *n) +{ + nfa *nf; + + REQ(n, RULE); + REQN(n->n_nchildren, 4); + n = n->n_child; + REQ(n, NAME); + nf = addnfa(gr, n->n_str); + n++; + REQ(n, COLON); + n++; + REQ(n, RHS); + compile_rhs(&gr->gr_ll, nf, n, &nf->nf_start, &nf->nf_finish); + n++; + REQ(n, NEWLINE); +} + +static void +compile_rhs(labellist *ll, nfa *nf, node *n, int *pa, int *pb) +{ + int i; + int a, b; + + REQ(n, RHS); + i = n->n_nchildren; + REQN(i, 1); + n = n->n_child; + REQ(n, ALT); + compile_alt(ll, nf, n, pa, pb); + if (--i <= 0) + return; + n++; + a = *pa; + b = *pb; + *pa = addnfastate(nf); + *pb = addnfastate(nf); + addnfaarc(nf, *pa, a, EMPTY); + addnfaarc(nf, b, *pb, EMPTY); + for (; --i >= 0; n++) { + REQ(n, VBAR); + REQN(i, 1); + --i; + n++; + REQ(n, ALT); + compile_alt(ll, nf, n, &a, &b); + addnfaarc(nf, *pa, a, EMPTY); + addnfaarc(nf, b, *pb, EMPTY); + } +} + +static void +compile_alt(labellist *ll, nfa *nf, node *n, int *pa, int *pb) +{ + int i; + int a, b; + + REQ(n, ALT); + i = n->n_nchildren; + REQN(i, 1); + n = n->n_child; + REQ(n, ITEM); + compile_item(ll, nf, n, pa, pb); + --i; + n++; + for (; --i >= 0; n++) { + if (n->n_type == COMMA) { /* XXX Temporary */ + REQN(i, 1); + --i; + n++; + } + REQ(n, ITEM); + compile_item(ll, nf, n, &a, &b); + addnfaarc(nf, *pb, a, EMPTY); + *pb = b; + } +} + +static void +compile_item(labellist *ll, nfa *nf, node *n, int *pa, int *pb) +{ + int i; + int a, b; + + REQ(n, ITEM); + i = n->n_nchildren; + REQN(i, 1); + n = n->n_child; + if (n->n_type == LSQB) { + REQN(i, 3); + n++; + REQ(n, RHS); + *pa = addnfastate(nf); + *pb = addnfastate(nf); + addnfaarc(nf, *pa, *pb, EMPTY); + compile_rhs(ll, nf, n, &a, &b); + addnfaarc(nf, *pa, a, EMPTY); + addnfaarc(nf, b, *pb, EMPTY); + REQN(i, 1); + n++; + REQ(n, RSQB); + } + else { + compile_atom(ll, nf, n, pa, pb); + if (--i <= 0) + return; + n++; + addnfaarc(nf, *pb, *pa, EMPTY); + if (n->n_type == STAR) + *pb = *pa; + else + REQ(n, PLUS); + } +} + +static void +compile_atom(labellist *ll, nfa *nf, node *n, int *pa, int *pb) +{ + int i; + + REQ(n, ATOM); + i = n->n_nchildren; + REQN(i, 1); + n = n->n_child; + if (n->n_type == LPAR) { + REQN(i, 3); + n++; + REQ(n, RHS); + compile_rhs(ll, nf, n, pa, pb); + n++; + REQ(n, RPAR); + } + else if (n->n_type == NAME || n->n_type == STRING) { + *pa = addnfastate(nf); + *pb = addnfastate(nf); + addnfaarc(nf, *pa, *pb, addlabel(ll, n->n_type, n->n_str)); + } + else + REQ(n, NAME); +} + +static void +dumpstate(labellist *ll, nfa *nf, int istate) +{ + nfastate *st; + int i; + nfaarc *ar; + + printf("%c%2d%c", + istate == nf->nf_start ? '*' : ' ', + istate, + istate == nf->nf_finish ? '.' : ' '); + st = &nf->nf_state[istate]; + ar = st->st_arc; + for (i = 0; i < st->st_narcs; i++) { + if (i > 0) + printf("\n "); + printf("-> %2d %s", ar->ar_arrow, + PyGrammar_LabelRepr(&ll->ll_label[ar->ar_label])); + ar++; + } + printf("\n"); +} + +static void +dumpnfa(labellist *ll, nfa *nf) +{ + int i; + + printf("NFA '%s' has %d states; start %d, finish %d\n", + nf->nf_name, nf->nf_nstates, nf->nf_start, nf->nf_finish); + for (i = 0; i < nf->nf_nstates; i++) + dumpstate(ll, nf, i); +} + + +/* PART TWO -- CONSTRUCT DFA -- Algorithm 3.1 from [Aho&Ullman 77] */ + +static void +addclosure(bitset ss, nfa *nf, int istate) +{ + if (addbit(ss, istate)) { + nfastate *st = &nf->nf_state[istate]; + nfaarc *ar = st->st_arc; + int i; + + for (i = st->st_narcs; --i >= 0; ) { + if (ar->ar_label == EMPTY) + addclosure(ss, nf, ar->ar_arrow); + ar++; + } + } +} + +typedef struct _ss_arc { + bitset sa_bitset; + int sa_arrow; + int sa_label; +} ss_arc; + +typedef struct _ss_state { + bitset ss_ss; + int ss_narcs; + ss_arc *ss_arc; + int ss_deleted; + int ss_finish; + int ss_rename; +} ss_state; + +typedef struct _ss_dfa { + int sd_nstates; + ss_state *sd_state; +} ss_dfa; + +/* Forward */ +static void printssdfa(int xx_nstates, ss_state *xx_state, int nbits, + labellist *ll, char *msg); +static void simplify(int xx_nstates, ss_state *xx_state); +static void convert(dfa *d, int xx_nstates, ss_state *xx_state); + +static void +makedfa(nfagrammar *gr, nfa *nf, dfa *d) +{ + int nbits = nf->nf_nstates; + bitset ss; + int xx_nstates; + ss_state *xx_state, *yy; + ss_arc *zz; + int istate, jstate, iarc, jarc, ibit; + nfastate *st; + nfaarc *ar; + + ss = newbitset(nbits); + addclosure(ss, nf, nf->nf_start); + xx_state = PyMem_NEW(ss_state, 1); + if (xx_state == NULL) + Py_FatalError("no mem for xx_state in makedfa"); + xx_nstates = 1; + yy = &xx_state[0]; + yy->ss_ss = ss; + yy->ss_narcs = 0; + yy->ss_arc = NULL; + yy->ss_deleted = 0; + yy->ss_finish = testbit(ss, nf->nf_finish); + if (yy->ss_finish) + printf("Error: nonterminal '%s' may produce empty.\n", + nf->nf_name); + + /* This algorithm is from a book written before + the invention of structured programming... */ + + /* For each unmarked state... */ + for (istate = 0; istate < xx_nstates; ++istate) { + yy = &xx_state[istate]; + ss = yy->ss_ss; + /* For all its states... */ + for (ibit = 0; ibit < nf->nf_nstates; ++ibit) { + if (!testbit(ss, ibit)) + continue; + st = &nf->nf_state[ibit]; + /* For all non-empty arcs from this state... */ + for (iarc = 0; iarc < st->st_narcs; iarc++) { + ar = &st->st_arc[iarc]; + if (ar->ar_label == EMPTY) + continue; + /* Look up in list of arcs from this state */ + for (jarc = 0; jarc < yy->ss_narcs; ++jarc) { + zz = &yy->ss_arc[jarc]; + if (ar->ar_label == zz->sa_label) + goto found; + } + /* Add new arc for this state */ + PyMem_RESIZE(yy->ss_arc, ss_arc, + yy->ss_narcs + 1); + if (yy->ss_arc == NULL) + Py_FatalError("out of mem"); + zz = &yy->ss_arc[yy->ss_narcs++]; + zz->sa_label = ar->ar_label; + zz->sa_bitset = newbitset(nbits); + zz->sa_arrow = -1; + found: ; + /* Add destination */ + addclosure(zz->sa_bitset, nf, ar->ar_arrow); + } + } + /* Now look up all the arrow states */ + for (jarc = 0; jarc < xx_state[istate].ss_narcs; jarc++) { + zz = &xx_state[istate].ss_arc[jarc]; + for (jstate = 0; jstate < xx_nstates; jstate++) { + if (samebitset(zz->sa_bitset, + xx_state[jstate].ss_ss, nbits)) { + zz->sa_arrow = jstate; + goto done; + } + } + PyMem_RESIZE(xx_state, ss_state, xx_nstates + 1); + if (xx_state == NULL) + Py_FatalError("out of mem"); + zz->sa_arrow = xx_nstates; + yy = &xx_state[xx_nstates++]; + yy->ss_ss = zz->sa_bitset; + yy->ss_narcs = 0; + yy->ss_arc = NULL; + yy->ss_deleted = 0; + yy->ss_finish = testbit(yy->ss_ss, nf->nf_finish); + done: ; + } + } + + if (Py_DebugFlag) + printssdfa(xx_nstates, xx_state, nbits, &gr->gr_ll, + "before minimizing"); + + simplify(xx_nstates, xx_state); + + if (Py_DebugFlag) + printssdfa(xx_nstates, xx_state, nbits, &gr->gr_ll, + "after minimizing"); + + convert(d, xx_nstates, xx_state); + + /* XXX cleanup */ +} + +static void +printssdfa(int xx_nstates, ss_state *xx_state, int nbits, + labellist *ll, char *msg) +{ + int i, ibit, iarc; + ss_state *yy; + ss_arc *zz; + + printf("Subset DFA %s\n", msg); + for (i = 0; i < xx_nstates; i++) { + yy = &xx_state[i]; + if (yy->ss_deleted) + continue; + printf(" Subset %d", i); + if (yy->ss_finish) + printf(" (finish)"); + printf(" { "); + for (ibit = 0; ibit < nbits; ibit++) { + if (testbit(yy->ss_ss, ibit)) + printf("%d ", ibit); + } + printf("}\n"); + for (iarc = 0; iarc < yy->ss_narcs; iarc++) { + zz = &yy->ss_arc[iarc]; + printf(" Arc to state %d, label %s\n", + zz->sa_arrow, + PyGrammar_LabelRepr( + &ll->ll_label[zz->sa_label])); + } + } +} + + +/* PART THREE -- SIMPLIFY DFA */ + +/* Simplify the DFA by repeatedly eliminating states that are + equivalent to another oner. This is NOT Algorithm 3.3 from + [Aho&Ullman 77]. It does not always finds the minimal DFA, + but it does usually make a much smaller one... (For an example + of sub-optimal behavior, try S: x a b+ | y a b+.) +*/ + +static int +samestate(ss_state *s1, ss_state *s2) +{ + int i; + + if (s1->ss_narcs != s2->ss_narcs || s1->ss_finish != s2->ss_finish) + return 0; + for (i = 0; i < s1->ss_narcs; i++) { + if (s1->ss_arc[i].sa_arrow != s2->ss_arc[i].sa_arrow || + s1->ss_arc[i].sa_label != s2->ss_arc[i].sa_label) + return 0; + } + return 1; +} + +static void +renamestates(int xx_nstates, ss_state *xx_state, int from, int to) +{ + int i, j; + + if (Py_DebugFlag) + printf("Rename state %d to %d.\n", from, to); + for (i = 0; i < xx_nstates; i++) { + if (xx_state[i].ss_deleted) + continue; + for (j = 0; j < xx_state[i].ss_narcs; j++) { + if (xx_state[i].ss_arc[j].sa_arrow == from) + xx_state[i].ss_arc[j].sa_arrow = to; + } + } +} + +static void +simplify(int xx_nstates, ss_state *xx_state) +{ + int changes; + int i, j; + + do { + changes = 0; + for (i = 1; i < xx_nstates; i++) { + if (xx_state[i].ss_deleted) + continue; + for (j = 0; j < i; j++) { + if (xx_state[j].ss_deleted) + continue; + if (samestate(&xx_state[i], &xx_state[j])) { + xx_state[i].ss_deleted++; + renamestates(xx_nstates, xx_state, + i, j); + changes++; + break; + } + } + } + } while (changes); +} + + +/* PART FOUR -- GENERATE PARSING TABLES */ + +/* Convert the DFA into a grammar that can be used by our parser */ + +static void +convert(dfa *d, int xx_nstates, ss_state *xx_state) +{ + int i, j; + ss_state *yy; + ss_arc *zz; + + for (i = 0; i < xx_nstates; i++) { + yy = &xx_state[i]; + if (yy->ss_deleted) + continue; + yy->ss_rename = addstate(d); + } + + for (i = 0; i < xx_nstates; i++) { + yy = &xx_state[i]; + if (yy->ss_deleted) + continue; + for (j = 0; j < yy->ss_narcs; j++) { + zz = &yy->ss_arc[j]; + addarc(d, yy->ss_rename, + xx_state[zz->sa_arrow].ss_rename, + zz->sa_label); + } + if (yy->ss_finish) + addarc(d, yy->ss_rename, yy->ss_rename, 0); + } + + d->d_initial = 0; +} + + +/* PART FIVE -- GLUE IT ALL TOGETHER */ + +static grammar * +maketables(nfagrammar *gr) +{ + int i; + nfa *nf; + dfa *d; + grammar *g; + + if (gr->gr_nnfas == 0) + return NULL; + g = newgrammar(gr->gr_nfa[0]->nf_type); + /* XXX first rule must be start rule */ + g->g_ll = gr->gr_ll; + + for (i = 0; i < gr->gr_nnfas; i++) { + nf = gr->gr_nfa[i]; + if (Py_DebugFlag) { + printf("Dump of NFA for '%s' ...\n", nf->nf_name); + dumpnfa(&gr->gr_ll, nf); + printf("Making DFA for '%s' ...\n", nf->nf_name); + } + d = adddfa(g, nf->nf_type, nf->nf_name); + makedfa(gr, gr->gr_nfa[i], d); + } + + return g; +} + +grammar * +pgen(node *n) +{ + nfagrammar *gr; + grammar *g; + + gr = metacompile(n); + g = maketables(gr); + translatelabels(g); + addfirstsets(g); + return g; +} + +grammar * +Py_pgen(node *n) +{ + return pgen(n); +} + +/* + +Description +----------- + +Input is a grammar in extended BNF (using * for repetition, + for +at-least-once repetition, [] for optional parts, | for alternatives and +() for grouping). This has already been parsed and turned into a parse +tree. + +Each rule is considered as a regular expression in its own right. +It is turned into a Non-deterministic Finite Automaton (NFA), which +is then turned into a Deterministic Finite Automaton (DFA), which is then +optimized to reduce the number of states. See [Aho&Ullman 77] chapter 3, +or similar compiler books (this technique is more often used for lexical +analyzers). + +The DFA's are used by the parser as parsing tables in a special way +that's probably unique. Before they are usable, the FIRST sets of all +non-terminals are computed. + +Reference +--------- + +[Aho&Ullman 77] + Aho&Ullman, Principles of Compiler Design, Addison-Wesley 1977 + (first edition) + +*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/pgenmain.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/pgenmain.c new file mode 100644 index 00000000..6c823c32 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/pgenmain.c @@ -0,0 +1,212 @@ + +/* Parser generator main program */ + +/* This expects a filename containing the grammar as argv[1] (UNIX) + or asks the console for such a file name (THINK C). + It writes its output on two files in the current directory: + - "graminit.c" gets the grammar as a bunch of initialized data + - "graminit.h" gets the grammar's non-terminals as #defines. + Error messages and status info during the generation process are + written to stdout, or sometimes to stderr. */ + +/* XXX TO DO: + - check for duplicate definitions of names (instead of fatal err) +*/ + +#include "Python.h" +#include "pgenheaders.h" +#include "grammar.h" +#include "node.h" +#include "parsetok.h" +#include "pgen.h" + +int Py_DebugFlag; +int Py_VerboseFlag; +int Py_IgnoreEnvironmentFlag; + +/* Forward */ +grammar *getgrammar(char *filename); +#ifdef THINK_C +int main(int, char **); +char *askfile(void); +#endif + +void +Py_Exit(int sts) +{ + //exit(sts); + + // Force crash instead of just terminating the process...this way + // we get a stack dump if something goes wrong + char* fatal = NULL; + *fatal = 1; +} + +int +main(int argc, char **argv) +{ + grammar *g; + FILE *fp; + char *filename, *graminit_h, *graminit_c; + +#ifdef THINK_C + filename = askfile(); + graminit_h = askfile(); + graminit_c = askfile(); +#else + if (argc != 4) { + fprintf(stderr, + "usage: %s grammar graminit.h graminit.c\n", argv[0]); + Py_Exit(2); + } + filename = argv[1]; + graminit_h = argv[2]; + graminit_c = argv[3]; +#endif + g = getgrammar(filename); + fp = fopen(graminit_c, "w"); + if (fp == NULL) { + perror(graminit_c); + Py_Exit(1); + } + if (Py_DebugFlag) + printf("Writing %s ...\n", graminit_c); + printgrammar(g, fp); + fclose(fp); + fp = fopen(graminit_h, "w"); + if (fp == NULL) { + perror(graminit_h); + Py_Exit(1); + } + if (Py_DebugFlag) + printf("Writing %s ...\n", graminit_h); + printnonterminals(g, fp); + fclose(fp); + Py_Exit(0); + return 0; /* Make gcc -Wall happy */ +} + +grammar * +getgrammar(char *filename) +{ + FILE *fp; + node *n; + grammar *g0, *g; + perrdetail err; + + fp = fopen(filename, "r"); + if (fp == NULL) { + perror(filename); + Py_Exit(1); + } + g0 = meta_grammar(); + n = PyParser_ParseFile(fp, filename, g0, g0->g_start, + (char *)NULL, (char *)NULL, &err); + fclose(fp); + if (n == NULL) { + fprintf(stderr, "Parsing error %d, line %d.\n", + err.error, err.lineno); + if (err.text != NULL) { + size_t i; + fprintf(stderr, "%s", err.text); + i = strlen(err.text); + if (i == 0 || err.text[i-1] != '\n') + fprintf(stderr, "\n"); + for (i = 0; i < err.offset; i++) { + if (err.text[i] == '\t') + putc('\t', stderr); + else + putc(' ', stderr); + } + fprintf(stderr, "^\n"); + PyMem_DEL(err.text); + } + Py_Exit(1); + } + g = pgen(n); + if (g == NULL) { + printf("Bad grammar.\n"); + Py_Exit(1); + } + return g; +} + +#ifdef THINK_C +char * +askfile(void) +{ + char buf[256]; + static char name[256]; + printf("Input file name: "); + if (fgets(buf, sizeof buf, stdin) == NULL) { + printf("EOF\n"); + Py_Exit(1); + } + /* XXX The (unsigned char *) case is needed by THINK C 3.0 */ + if (sscanf(/*(unsigned char *)*/buf, " %s ", name) != 1) { + printf("No file\n"); + Py_Exit(1); + } + return name; +} +#endif + +void +Py_FatalError(const char *msg) +{ + fprintf(stderr, "pgen: FATAL ERROR: %s\n", msg); + Py_Exit(1); +} + +#ifdef macintosh +/* ARGSUSED */ +int +guesstabsize(char *path) +{ + return 4; +} +#endif + +/* No-nonsense my_readline() for tokenizer.c */ + +char * +PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) +{ + size_t n = 1000; + char *p = PyMem_MALLOC(n); + char *q; + if (p == NULL) + return NULL; + fprintf(stderr, "%s", prompt); + q = fgets(p, n, sys_stdin); + if (q == NULL) { + *p = '\0'; + return p; + } + n = strlen(p); + if (n > 0 && p[n-1] != '\n') + p[n-1] = '\n'; + return PyMem_REALLOC(p, n+1); +} + +#ifdef WITH_UNIVERSAL_NEWLINES +/* No-nonsense fgets */ +char * +Py_UniversalNewlineFgets(char *buf, int n, FILE *stream, PyObject *fobj) +{ + return fgets(buf, n, stream); +} +#endif + + +#include + +void +PySys_WriteStderr(const char *format, ...) +{ + va_list va; + + va_start(va, format); + vfprintf(stderr, format, va); + va_end(va); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/printgrammar.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/printgrammar.c new file mode 100644 index 00000000..76ce49df --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/printgrammar.c @@ -0,0 +1,113 @@ + +/* Print a bunch of C initializers that represent a grammar */ + +#include "pgenheaders.h" +#include "grammar.h" + +/* Forward */ +static void printarcs(int, dfa *, FILE *); +static void printstates(grammar *, FILE *); +static void printdfas(grammar *, FILE *); +static void printlabels(grammar *, FILE *); + +void +printgrammar(grammar *g, FILE *fp) +{ + fprintf(fp, "#include \"pgenheaders.h\"\n"); + fprintf(fp, "#include \"grammar.h\"\n"); + printdfas(g, fp); + printlabels(g, fp); + fprintf(fp, "grammar _PyParser_Grammar = {\n"); + fprintf(fp, "\t%d,\n", g->g_ndfas); + fprintf(fp, "\tdfas,\n"); + fprintf(fp, "\t{%d, labels},\n", g->g_ll.ll_nlabels); + fprintf(fp, "\t%d\n", g->g_start); + fprintf(fp, "};\n"); +} + +void +printnonterminals(grammar *g, FILE *fp) +{ + dfa *d; + int i; + + d = g->g_dfa; + for (i = g->g_ndfas; --i >= 0; d++) + fprintf(fp, "#define %s %d\n", d->d_name, d->d_type); +} + +static void +printarcs(int i, dfa *d, FILE *fp) +{ + arc *a; + state *s; + int j, k; + + s = d->d_state; + for (j = 0; j < d->d_nstates; j++, s++) { + fprintf(fp, "static arc arcs_%d_%d[%d] = {\n", + i, j, s->s_narcs); + a = s->s_arc; + for (k = 0; k < s->s_narcs; k++, a++) + fprintf(fp, "\t{%d, %d},\n", a->a_lbl, a->a_arrow); + fprintf(fp, "};\n"); + } +} + +static void +printstates(grammar *g, FILE *fp) +{ + state *s; + dfa *d; + int i, j; + + d = g->g_dfa; + for (i = 0; i < g->g_ndfas; i++, d++) { + printarcs(i, d, fp); + fprintf(fp, "static state states_%d[%d] = {\n", + i, d->d_nstates); + s = d->d_state; + for (j = 0; j < d->d_nstates; j++, s++) + fprintf(fp, "\t{%d, arcs_%d_%d},\n", + s->s_narcs, i, j); + fprintf(fp, "};\n"); + } +} + +static void +printdfas(grammar *g, FILE *fp) +{ + dfa *d; + int i, j; + + printstates(g, fp); + fprintf(fp, "static dfa dfas[%d] = {\n", g->g_ndfas); + d = g->g_dfa; + for (i = 0; i < g->g_ndfas; i++, d++) { + fprintf(fp, "\t{%d, \"%s\", %d, %d, states_%d,\n", + d->d_type, d->d_name, d->d_initial, d->d_nstates, i); + fprintf(fp, "\t \""); + for (j = 0; j < NBYTES(g->g_ll.ll_nlabels); j++) + fprintf(fp, "\\%03o", d->d_first[j] & 0xff); + fprintf(fp, "\"},\n"); + } + fprintf(fp, "};\n"); +} + +static void +printlabels(grammar *g, FILE *fp) +{ + label *l; + int i; + + fprintf(fp, "static label labels[%d] = {\n", g->g_ll.ll_nlabels); + l = g->g_ll.ll_label; + for (i = g->g_ll.ll_nlabels; --i >= 0; l++) { + if (l->lb_str == NULL) + fprintf(fp, "\t{%d, 0},\n", l->lb_type); + else + fprintf(fp, "\t{%d, \"%s\"},\n", + l->lb_type, l->lb_str); + } + fprintf(fp, "};\n"); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer.c new file mode 100644 index 00000000..e8f82643 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer.c @@ -0,0 +1,1432 @@ + +/* Tokenizer implementation */ + +#include "Python.h" +#include "pgenheaders.h" + +#include +#include + +#include "tokenizer.h" +#include "errcode.h" + +#ifndef PGEN +#include "unicodeobject.h" +#include "stringobject.h" +#include "fileobject.h" +#include "codecs.h" +#include "abstract.h" +#endif /* PGEN */ + +extern char *PyOS_Readline(FILE *, FILE *, char *); +/* Return malloc'ed string including trailing \n; + empty malloc'ed string for EOF; + NULL if interrupted */ + +/* Don't ever change this -- it would break the portability of Python code */ +#define TABSIZE 8 + +/* Convert a possibly signed character to a nonnegative int */ +/* XXX This assumes characters are 8 bits wide */ +#ifdef __CHAR_UNSIGNED__ +#define Py_CHARMASK(c) (c) +#else +#define Py_CHARMASK(c) ((c) & 0xff) +#endif + +/* Forward */ +static struct tok_state *tok_new(void); +static int tok_nextc(struct tok_state *tok); +static void tok_backup(struct tok_state *tok, int c); + +/* Token names */ + +char *_PyParser_TokenNames[] = { + "ENDMARKER", + "NAME", + "NUMBER", + "STRING", + "NEWLINE", + "INDENT", + "DEDENT", + "LPAR", + "RPAR", + "LSQB", + "RSQB", + "COLON", + "COMMA", + "SEMI", + "PLUS", + "MINUS", + "STAR", + "SLASH", + "VBAR", + "AMPER", + "LESS", + "GREATER", + "EQUAL", + "DOT", + "PERCENT", + "BACKQUOTE", + "LBRACE", + "RBRACE", + "EQEQUAL", + "NOTEQUAL", + "LESSEQUAL", + "GREATEREQUAL", + "TILDE", + "CIRCUMFLEX", + "LEFTSHIFT", + "RIGHTSHIFT", + "DOUBLESTAR", + "PLUSEQUAL", + "MINEQUAL", + "STAREQUAL", + "SLASHEQUAL", + "PERCENTEQUAL", + "AMPEREQUAL", + "VBAREQUAL", + "CIRCUMFLEXEQUAL", + "LEFTSHIFTEQUAL", + "RIGHTSHIFTEQUAL", + "DOUBLESTAREQUAL", + "DOUBLESLASH", + "DOUBLESLASHEQUAL", + /* This table must match the #defines in token.h! */ + "OP", + "", + "" +}; + + +/* Create and initialize a new tok_state structure */ + +static struct tok_state * +tok_new(void) +{ + struct tok_state *tok = PyMem_NEW(struct tok_state, 1); + if (tok == NULL) + return NULL; + tok->buf = tok->cur = tok->end = tok->inp = tok->start = NULL; + tok->done = E_OK; + tok->fp = NULL; + tok->tabsize = TABSIZE; + tok->indent = 0; + tok->indstack[0] = 0; + tok->atbol = 1; + tok->pendin = 0; + tok->prompt = tok->nextprompt = NULL; + tok->lineno = 0; + tok->level = 0; + tok->filename = NULL; + tok->altwarning = 0; + tok->alterror = 0; + tok->alttabsize = 1; + tok->altindstack[0] = 0; + tok->decoding_state = 0; + tok->decoding_erred = 0; + tok->read_coding_spec = 0; + tok->issued_encoding_warning = 0; + tok->encoding = NULL; + tok->cont_line = 0; +#ifndef PGEN + tok->decoding_readline = NULL; + tok->decoding_buffer = NULL; +#endif + return tok; +} + +#ifdef PGEN + +static char * +decoding_fgets(char *s, int size, struct tok_state *tok) +{ + return fgets(s, size, tok->fp); +} + +static int +decoding_feof(struct tok_state *tok) +{ + return feof(tok->fp); +} + +static const char * +decode_str(const char *str, struct tok_state *tok) +{ + return str; +} + +#else /* PGEN */ + +static char * +error_ret(struct tok_state *tok) /* XXX */ +{ + tok->decoding_erred = 1; + if (tok->fp != NULL && tok->buf != NULL) /* see PyTokenizer_Free */ + PyMem_DEL(tok->buf); + tok->buf = NULL; + return NULL; /* as if it were EOF */ +} + +static char * +new_string(const char *s, int len) +{ + char* result = PyMem_NEW(char, len + 1); + if (result != NULL) { + memcpy(result, s, len); + result[len] = '\0'; + } + return result; +} + +static char * +get_normal_name(char *s) /* for utf-8 and latin-1 */ +{ + char buf[13]; + int i; + for (i = 0; i < 12; i++) { + int c = s[i]; + if (c == '\0') break; + else if (c == '_') buf[i] = '-'; + else buf[i] = tolower(c); + } + buf[i] = '\0'; + if (strcmp(buf, "utf-8") == 0 || + strncmp(buf, "utf-8-", 6) == 0) return "utf-8"; + else if (strcmp(buf, "latin-1") == 0 || + strcmp(buf, "iso-8859-1") == 0 || + strcmp(buf, "iso-latin-1") == 0 || + strncmp(buf, "latin-1-", 8) == 0 || + strncmp(buf, "iso-8859-1-", 11) == 0 || + strncmp(buf, "iso-latin-1-", 12) == 0) return "iso-8859-1"; + else return s; +} + +/* Return the coding spec in S, or NULL if none is found. */ + +static char * +get_coding_spec(const char *s, int size) +{ + int i; + /* Coding spec must be in a comment, and that comment must be + * the only statement on the source code line. */ + for (i = 0; i < size - 6; i++) { + if (s[i] == '#') + break; + if (s[i] != ' ' && s[i] != '\t' && s[i] != '\014') + return NULL; + } + for (; i < size - 6; i++) { /* XXX inefficient search */ + const char* t = s + i; + if (strncmp(t, "coding", 6) == 0) { + const char* begin = NULL; + t += 6; + if (t[0] != ':' && t[0] != '=') + continue; + do { + t++; + } while (t[0] == '\x20' || t[0] == '\t'); + + begin = t; + while (isalnum((int)t[0]) || + t[0] == '-' || t[0] == '_' || t[0] == '.') + t++; + + if (begin < t) { + char* r = new_string(begin, t - begin); + char* q = get_normal_name(r); + if (r != q) { + PyMem_DEL(r); + r = new_string(q, strlen(q)); + } + return r; + } + } + } + return NULL; +} + +/* Check whether the line contains a coding spec. If it does, + invoke the set_readline function for the new encoding. + This function receives the tok_state and the new encoding. + Return 1 on success, 0 on failure. */ + +static int +check_coding_spec(const char* line, int size, struct tok_state *tok, + int set_readline(struct tok_state *, const char *)) +{ + char * cs; + int r = 1; + + if (tok->cont_line) + /* It's a continuation line, so it can't be a coding spec. */ + return 1; + cs = get_coding_spec(line, size); + if (cs != NULL) { + tok->read_coding_spec = 1; + if (tok->encoding == NULL) { + assert(tok->decoding_state == 1); /* raw */ + if (strcmp(cs, "utf-8") == 0 || + strcmp(cs, "iso-8859-1") == 0) { + tok->encoding = cs; + } else { +#ifdef Py_USING_UNICODE + r = set_readline(tok, cs); + if (r) { + tok->encoding = cs; + tok->decoding_state = -1; + } +#else + /* Without Unicode support, we cannot + process the coding spec. Since there + won't be any Unicode literals, that + won't matter. */ +#endif + } + } else { /* then, compare cs with BOM */ + r = (strcmp(tok->encoding, cs) == 0); + PyMem_DEL(cs); + } + } + return r; +} + +/* See whether the file starts with a BOM. If it does, + invoke the set_readline function with the new encoding. + Return 1 on success, 0 on failure. */ + +static int +check_bom(int get_char(struct tok_state *), + void unget_char(int, struct tok_state *), + int set_readline(struct tok_state *, const char *), + struct tok_state *tok) +{ + int ch = get_char(tok); + tok->decoding_state = 1; + if (ch == EOF) { + return 1; + } else if (ch == 0xEF) { + ch = get_char(tok); if (ch != 0xBB) goto NON_BOM; + ch = get_char(tok); if (ch != 0xBF) goto NON_BOM; +#if 0 + /* Disable support for UTF-16 BOMs until a decision + is made whether this needs to be supported. */ + } else if (ch == 0xFE) { + ch = get_char(tok); if (ch != 0xFF) goto NON_BOM; + if (!set_readline(tok, "utf-16-be")) return 0; + tok->decoding_state = -1; + } else if (ch == 0xFF) { + ch = get_char(tok); if (ch != 0xFE) goto NON_BOM; + if (!set_readline(tok, "utf-16-le")) return 0; + tok->decoding_state = -1; +#endif + } else { + unget_char(ch, tok); + return 1; + } + tok->encoding = new_string("utf-8", 5); /* resulting is in utf-8 */ + return 1; + NON_BOM: + /* any token beginning with '\xEF', '\xFE', '\xFF' is a bad token */ + unget_char(0xFF, tok); /* XXX this will cause a syntax error */ + return 1; +} + +/* Read a line of text from TOK into S, using the stream in TOK. + Return NULL on failure, else S. */ + +static char * +fp_readl(char *s, int size, struct tok_state *tok) +{ +#ifndef Py_USING_UNICODE + /* In a non-Unicode built, this should never be called. */ + Py_FatalError("fp_readl should not be called in this build."); + return NULL; /* Keep compiler happy (not reachable) */ +#else + PyObject* utf8; + PyObject* buf = tok->decoding_buffer; + if (buf == NULL) { + /* Ask for one less byte so we can terminate it */ + PyObject *args = Py_BuildValue("(i)", size-1); + if (args == NULL) + return error_ret(tok); + buf = PyObject_Call(tok->decoding_readline, args, NULL); + Py_DECREF(args); + if (buf == NULL) + return error_ret(tok); + } else { + tok->decoding_buffer = NULL; + } + utf8 = PyUnicode_AsUTF8String(buf); + Py_DECREF(buf); + if (utf8 == NULL) + return error_ret(tok); + else { + const char* str = PyString_AsString(utf8); + assert(strlen(str) < (size_t)size); /* XXX */ + strcpy(s, str); + Py_DECREF(utf8); + if (s[0] == '\0') return NULL; /* EOF */ + return s; + } +#endif +} + +/* Set the readline function for TOK to a StreamReader's + readline function. The StreamReader is named ENC. + + This function is called from check_bom and check_coding_spec. + + ENC is usually identical to the future value of tok->encoding, + except for the (currently unsupported) case of UTF-16. + + Return 1 on success, 0 on failure. */ + +static int +fp_setreadl(struct tok_state *tok, const char* enc) +{ + PyObject *reader, *stream, *readline; + + /* XXX: constify filename argument. */ + stream = PyFile_FromFile(tok->fp, (char*)tok->filename, "rb", NULL); + if (stream == NULL) + return 0; + + reader = PyCodec_StreamReader(enc, stream, NULL); + Py_DECREF(stream); + if (reader == NULL) + return 0; + + readline = PyObject_GetAttrString(reader, "readline"); + Py_DECREF(reader); + if (readline == NULL) + return 0; + + tok->decoding_readline = readline; + return 1; +} + +/* Fetch the next byte from TOK. */ + +static int fp_getc(struct tok_state *tok) { + return getc(tok->fp); +} + +/* Unfetch the last byte back into TOK. */ + +static void fp_ungetc(int c, struct tok_state *tok) { + ungetc(c, tok->fp); +} + +/* Read a line of input from TOK. Determine encoding + if necessary. */ + +static char * +decoding_fgets(char *s, int size, struct tok_state *tok) +{ + char *line = NULL; + int warn = 0, badchar = 0; + for (;;) { + if (tok->decoding_state < 0) { + /* We already have a codec associated with + this input. */ + line = fp_readl(s, size, tok); + break; + } else if (tok->decoding_state > 0) { + /* We want a 'raw' read. */ + line = Py_UniversalNewlineFgets(s, size, + tok->fp, NULL); + warn = 1; + break; + } else { + /* We have not yet determined the encoding. + If an encoding is found, use the file-pointer + reader functions from now on. */ + if (!check_bom(fp_getc, fp_ungetc, fp_setreadl, tok)) + return error_ret(tok); + assert(tok->decoding_state != 0); + } + } + if (line != NULL && tok->lineno < 2 && !tok->read_coding_spec) { + if (!check_coding_spec(line, strlen(line), tok, fp_setreadl)) { + return error_ret(tok); + } + } +#ifndef PGEN + if (warn && line && !tok->issued_encoding_warning && !tok->encoding) { + unsigned char *c; + for (c = (unsigned char *)line; *c; c++) + if (*c > 127) { + badchar = *c; + break; + } + } + if (badchar) { + char buf[500]; + /* Need to add 1 to the line number, since this line + has not been counted, yet. */ + sprintf(buf, + "Non-ASCII character '\\x%.2x' " + "in file %.200s on line %i, " + "but no encoding declared; " + "see http://www.python.org/peps/pep-0263.html for details", + badchar, tok->filename, tok->lineno + 1); + /* We don't use PyErr_WarnExplicit() here because + printing the line in question to e.g. a log file + could result in sensitive information being + exposed. */ + PyErr_Warn(PyExc_DeprecationWarning, buf); + tok->issued_encoding_warning = 1; + } +#endif + return line; +} + +static int +decoding_feof(struct tok_state *tok) +{ + if (tok->decoding_state >= 0) { + return feof(tok->fp); + } else { + PyObject* buf = tok->decoding_buffer; + if (buf == NULL) { + PyObject *args = PyTuple_New(0); + if (args == NULL) { + error_ret(tok); + return 1; + } + buf = PyObject_Call(tok->decoding_readline, + args, NULL); + Py_DECREF(args); + if (buf == NULL) { + error_ret(tok); + return 1; + } else { + tok->decoding_buffer = buf; + } + } + return PyObject_Length(buf) == 0; + } +} + +/* Fetch a byte from TOK, using the string buffer. */ + +static int buf_getc(struct tok_state *tok) { + return Py_CHARMASK(*tok->str++); +} + +/* Unfetch a byte from TOK, using the string buffer. */ + +static void buf_ungetc(int c, struct tok_state *tok) { + tok->str--; + assert(Py_CHARMASK(*tok->str) == c); /* tok->cur may point to read-only segment */ +} + +/* Set the readline function for TOK to ENC. For the string-based + tokenizer, this means to just record the encoding. */ + +static int buf_setreadl(struct tok_state *tok, const char* enc) { + tok->enc = enc; + return 1; +} + +/* Return a UTF-8 encoding Python string object from the + C byte string STR, which is encoded with ENC. */ + +#ifdef Py_USING_UNICODE +static PyObject * +translate_into_utf8(const char* str, const char* enc) { + PyObject *utf8; + PyObject* buf = PyUnicode_Decode(str, strlen(str), enc, NULL); + if (buf == NULL) + return NULL; + utf8 = PyUnicode_AsUTF8String(buf); + Py_DECREF(buf); + return utf8; +} +#endif + +/* Decode a byte string STR for use as the buffer of TOK. + Look for encoding declarations inside STR, and record them + inside TOK. */ + +static const char * +decode_str(const char *str, struct tok_state *tok) +{ + PyObject* utf8 = NULL; + const char *s; + int lineno = 0; + tok->enc = NULL; + tok->str = str; + if (!check_bom(buf_getc, buf_ungetc, buf_setreadl, tok)) + return NULL; + str = tok->str; /* string after BOM if any */ + assert(str); +#ifdef Py_USING_UNICODE + if (tok->enc != NULL) { + utf8 = translate_into_utf8(str, tok->enc); + if (utf8 == NULL) + return NULL; + str = PyString_AsString(utf8); + } +#endif + for (s = str;; s++) { + if (*s == '\0') break; + else if (*s == '\n') { + lineno++; + if (lineno == 2) break; + } + } + tok->enc = NULL; + if (!check_coding_spec(str, s - str, tok, buf_setreadl)) + return NULL; +#ifdef Py_USING_UNICODE + if (tok->enc != NULL) { + assert(utf8 == NULL); + utf8 = translate_into_utf8(str, tok->enc); + if (utf8 == NULL) + return NULL; + str = PyString_AsString(utf8); + } +#endif + assert(tok->decoding_buffer == NULL); + tok->decoding_buffer = utf8; /* CAUTION */ + return str; +} + +#endif /* PGEN */ + +/* Set up tokenizer for string */ + +struct tok_state * +PyTokenizer_FromString(const char *str) +{ + struct tok_state *tok = tok_new(); + if (tok == NULL) + return NULL; + str = (char *)decode_str(str, tok); + if (str == NULL) + return NULL; + /* XXX: constify members. */ + tok->buf = tok->cur = tok->end = tok->inp = (char*)str; + return tok; +} + + +/* Set up tokenizer for file */ + +struct tok_state * +PyTokenizer_FromFile(FILE *fp, char *ps1, char *ps2) +{ + struct tok_state *tok = tok_new(); + if (tok == NULL) + return NULL; + if ((tok->buf = PyMem_NEW(char, BUFSIZ)) == NULL) { + PyMem_DEL(tok); + return NULL; + } + tok->cur = tok->inp = tok->buf; + tok->end = tok->buf + BUFSIZ; + tok->fp = fp; + tok->prompt = ps1; + tok->nextprompt = ps2; + return tok; +} + + +/* Free a tok_state structure */ + +void +PyTokenizer_Free(struct tok_state *tok) +{ + if (tok->encoding != NULL) + PyMem_DEL(tok->encoding); +#ifndef PGEN + Py_XDECREF(tok->decoding_readline); + Py_XDECREF(tok->decoding_buffer); +#endif + if (tok->fp != NULL && tok->buf != NULL) + PyMem_DEL(tok->buf); + PyMem_DEL(tok); +} + + +/* Get next char, updating state; error code goes into tok->done */ + +static int +tok_nextc(register struct tok_state *tok) +{ + for (;;) { + if (tok->cur != tok->inp) { + return Py_CHARMASK(*tok->cur++); /* Fast path */ + } + if (tok->done != E_OK) + return EOF; + if (tok->fp == NULL) { + char *end = strchr(tok->inp, '\n'); + if (end != NULL) + end++; + else { + end = strchr(tok->inp, '\0'); + if (end == tok->inp) { + tok->done = E_EOF; + return EOF; + } + } + if (tok->start == NULL) + tok->buf = tok->cur; + tok->lineno++; + tok->inp = end; + return Py_CHARMASK(*tok->cur++); + } + if (tok->prompt != NULL) { + char *new = PyOS_Readline(stdin, stdout, tok->prompt); + if (tok->nextprompt != NULL) + tok->prompt = tok->nextprompt; + if (new == NULL) + tok->done = E_INTR; + else if (*new == '\0') { + PyMem_FREE(new); + tok->done = E_EOF; + } + else if (tok->start != NULL) { + size_t start = tok->start - tok->buf; + size_t oldlen = tok->cur - tok->buf; + size_t newlen = oldlen + strlen(new); + char *buf = tok->buf; + PyMem_RESIZE(buf, char, newlen+1); + tok->lineno++; + if (buf == NULL) { + PyMem_DEL(tok->buf); + tok->buf = NULL; + PyMem_FREE(new); + tok->done = E_NOMEM; + return EOF; + } + tok->buf = buf; + tok->cur = tok->buf + oldlen; + strcpy(tok->buf + oldlen, new); + PyMem_FREE(new); + tok->inp = tok->buf + newlen; + tok->end = tok->inp + 1; + tok->start = tok->buf + start; + } + else { + tok->lineno++; + if (tok->buf != NULL) + PyMem_DEL(tok->buf); + tok->buf = new; + tok->cur = tok->buf; + tok->inp = strchr(tok->buf, '\0'); + tok->end = tok->inp + 1; + } + } + else { + int done = 0; + int cur = 0; + char *pt; + if (tok->start == NULL) { + if (tok->buf == NULL) { + tok->buf = PyMem_NEW(char, BUFSIZ); + if (tok->buf == NULL) { + tok->done = E_NOMEM; + return EOF; + } + tok->end = tok->buf + BUFSIZ; + } + if (decoding_fgets(tok->buf, (int)(tok->end - tok->buf), + tok) == NULL) { + tok->done = E_EOF; + done = 1; + } + else { + tok->done = E_OK; + tok->inp = strchr(tok->buf, '\0'); + done = tok->inp[-1] == '\n'; + } + } + else { + cur = tok->cur - tok->buf; + if (decoding_feof(tok)) { + tok->done = E_EOF; + done = 1; + } + else + tok->done = E_OK; + } + tok->lineno++; + /* Read until '\n' or EOF */ + while (!done) { + int curstart = tok->start == NULL ? -1 : + tok->start - tok->buf; + int curvalid = tok->inp - tok->buf; + int newsize = curvalid + BUFSIZ; + char *newbuf = tok->buf; + PyMem_RESIZE(newbuf, char, newsize); + if (newbuf == NULL) { + tok->done = E_NOMEM; + tok->cur = tok->inp; + return EOF; + } + tok->buf = newbuf; + tok->inp = tok->buf + curvalid; + tok->end = tok->buf + newsize; + tok->start = curstart < 0 ? NULL : + tok->buf + curstart; + if (decoding_fgets(tok->inp, + (int)(tok->end - tok->inp), + tok) == NULL) { + /* Last line does not end in \n, + fake one */ + strcpy(tok->inp, "\n"); + } + tok->inp = strchr(tok->inp, '\0'); + done = tok->inp[-1] == '\n'; + } + tok->cur = tok->buf + cur; +#ifndef macintosh + /* replace "\r\n" with "\n" */ + /* For Mac we leave the \r, giving a syntax error */ + pt = tok->inp - 2; + if (pt >= tok->buf && *pt == '\r') { + *pt++ = '\n'; + *pt = '\0'; + tok->inp = pt; + } +#endif + } + if (tok->done != E_OK) { + if (tok->prompt != NULL) + PySys_WriteStderr("\n"); + tok->cur = tok->inp; + return EOF; + } + } + /*NOTREACHED*/ +} + + +/* Back-up one character */ + +static void +tok_backup(register struct tok_state *tok, register int c) +{ + if (c != EOF) { + if (--tok->cur < tok->buf) + Py_FatalError("tok_backup: begin of buffer"); + if (*tok->cur != c) + *tok->cur = c; + } +} + + +/* Return the token corresponding to a single character */ + +int +PyToken_OneChar(int c) +{ + switch (c) { + case '(': return LPAR; + case ')': return RPAR; + case '[': return LSQB; + case ']': return RSQB; + case ':': return COLON; + case ',': return COMMA; + case ';': return SEMI; + case '+': return PLUS; + case '-': return MINUS; + case '*': return STAR; + case '/': return SLASH; + case '|': return VBAR; + case '&': return AMPER; + case '<': return LESS; + case '>': return GREATER; + case '=': return EQUAL; + case '.': return DOT; + case '%': return PERCENT; + case '`': return BACKQUOTE; + case '{': return LBRACE; + case '}': return RBRACE; + case '^': return CIRCUMFLEX; + case '~': return TILDE; + default: return OP; + } +} + + +int +PyToken_TwoChars(int c1, int c2) +{ + switch (c1) { + case '=': + switch (c2) { + case '=': return EQEQUAL; + } + break; + case '!': + switch (c2) { + case '=': return NOTEQUAL; + } + break; + case '<': + switch (c2) { + case '>': return NOTEQUAL; + case '=': return LESSEQUAL; + case '<': return LEFTSHIFT; + } + break; + case '>': + switch (c2) { + case '=': return GREATEREQUAL; + case '>': return RIGHTSHIFT; + } + break; + case '+': + switch (c2) { + case '=': return PLUSEQUAL; + } + break; + case '-': + switch (c2) { + case '=': return MINEQUAL; + } + break; + case '*': + switch (c2) { + case '*': return DOUBLESTAR; + case '=': return STAREQUAL; + } + break; + case '/': + switch (c2) { + case '/': return DOUBLESLASH; + case '=': return SLASHEQUAL; + } + break; + case '|': + switch (c2) { + case '=': return VBAREQUAL; + } + break; + case '%': + switch (c2) { + case '=': return PERCENTEQUAL; + } + break; + case '&': + switch (c2) { + case '=': return AMPEREQUAL; + } + break; + case '^': + switch (c2) { + case '=': return CIRCUMFLEXEQUAL; + } + break; + } + return OP; +} + +int +PyToken_ThreeChars(int c1, int c2, int c3) +{ + switch (c1) { + case '<': + switch (c2) { + case '<': + switch (c3) { + case '=': + return LEFTSHIFTEQUAL; + } + break; + } + break; + case '>': + switch (c2) { + case '>': + switch (c3) { + case '=': + return RIGHTSHIFTEQUAL; + } + break; + } + break; + case '*': + switch (c2) { + case '*': + switch (c3) { + case '=': + return DOUBLESTAREQUAL; + } + break; + } + break; + case '/': + switch (c2) { + case '/': + switch (c3) { + case '=': + return DOUBLESLASHEQUAL; + } + break; + } + break; + } + return OP; +} + +static int +indenterror(struct tok_state *tok) +{ + if (tok->alterror) { + tok->done = E_TABSPACE; + tok->cur = tok->inp; + return 1; + } + if (tok->altwarning) { + PySys_WriteStderr("%s: inconsistent use of tabs and spaces " + "in indentation\n", tok->filename); + tok->altwarning = 0; + } + return 0; +} + + +/* Get next token, after space stripping etc. */ + +static int +tok_get(register struct tok_state *tok, char **p_start, char **p_end) +{ + register int c; + int blankline; + + *p_start = *p_end = NULL; + nextline: + tok->start = NULL; + blankline = 0; + + /* Get indentation level */ + if (tok->atbol) { + register int col = 0; + register int altcol = 0; + tok->atbol = 0; + for (;;) { + c = tok_nextc(tok); + if (c == ' ') + col++, altcol++; + else if (c == '\t') { + col = (col/tok->tabsize + 1) * tok->tabsize; + altcol = (altcol/tok->alttabsize + 1) + * tok->alttabsize; + } + else if (c == '\014') /* Control-L (formfeed) */ + col = altcol = 0; /* For Emacs users */ + else + break; + } + tok_backup(tok, c); + if (c == '#' || c == '\n') { + /* Lines with only whitespace and/or comments + shouldn't affect the indentation and are + not passed to the parser as NEWLINE tokens, + except *totally* empty lines in interactive + mode, which signal the end of a command group. */ + if (col == 0 && c == '\n' && tok->prompt != NULL) + blankline = 0; /* Let it through */ + else + blankline = 1; /* Ignore completely */ + /* We can't jump back right here since we still + may need to skip to the end of a comment */ + } + if (!blankline && tok->level == 0) { + if (col == tok->indstack[tok->indent]) { + /* No change */ + if (altcol != tok->altindstack[tok->indent]) { + if (indenterror(tok)) + return ERRORTOKEN; + } + } + else if (col > tok->indstack[tok->indent]) { + /* Indent -- always one */ + if (tok->indent+1 >= MAXINDENT) { + tok->done = E_TOODEEP; + tok->cur = tok->inp; + return ERRORTOKEN; + } + if (altcol <= tok->altindstack[tok->indent]) { + if (indenterror(tok)) + return ERRORTOKEN; + } + tok->pendin++; + tok->indstack[++tok->indent] = col; + tok->altindstack[tok->indent] = altcol; + } + else /* col < tok->indstack[tok->indent] */ { + /* Dedent -- any number, must be consistent */ + while (tok->indent > 0 && + col < tok->indstack[tok->indent]) { + tok->pendin--; + tok->indent--; + } + if (col != tok->indstack[tok->indent]) { + tok->done = E_DEDENT; + tok->cur = tok->inp; + return ERRORTOKEN; + } + if (altcol != tok->altindstack[tok->indent]) { + if (indenterror(tok)) + return ERRORTOKEN; + } + } + } + } + + tok->start = tok->cur; + + /* Return pending indents/dedents */ + if (tok->pendin != 0) { + if (tok->pendin < 0) { + tok->pendin++; + return DEDENT; + } + else { + tok->pendin--; + return INDENT; + } + } + + again: + tok->start = NULL; + /* Skip spaces */ + do { + c = tok_nextc(tok); + } while (c == ' ' || c == '\t' || c == '\014'); + + /* Set start of current token */ + tok->start = tok->cur - 1; + + /* Skip comment, while looking for tab-setting magic */ + if (c == '#') { + static char *tabforms[] = { + "tab-width:", /* Emacs */ + ":tabstop=", /* vim, full form */ + ":ts=", /* vim, abbreviated form */ + "set tabsize=", /* will vi never die? */ + /* more templates can be added here to support other editors */ + }; + char cbuf[80]; + char *tp, **cp; + tp = cbuf; + do { + *tp++ = c = tok_nextc(tok); + } while (c != EOF && c != '\n' && + tp - cbuf + 1 < sizeof(cbuf)); + *tp = '\0'; + for (cp = tabforms; + cp < tabforms + sizeof(tabforms)/sizeof(tabforms[0]); + cp++) { + if ((tp = strstr(cbuf, *cp))) { + int newsize = atoi(tp + strlen(*cp)); + + if (newsize >= 1 && newsize <= 40) { + tok->tabsize = newsize; + if (Py_VerboseFlag) + PySys_WriteStderr( + "Tab size set to %d\n", + newsize); + } + } + } + while (c != EOF && c != '\n') + c = tok_nextc(tok); + } + + /* Check for EOF and errors now */ + if (c == EOF) { + return tok->done == E_EOF ? ENDMARKER : ERRORTOKEN; + } + + /* Identifier (most frequent token!) */ + if (isalpha(c) || c == '_') { + /* Process r"", u"" and ur"" */ + switch (c) { + case 'r': + case 'R': + c = tok_nextc(tok); + if (c == '"' || c == '\'') + goto letter_quote; + break; + case 'u': + case 'U': + c = tok_nextc(tok); + if (c == 'r' || c == 'R') + c = tok_nextc(tok); + if (c == '"' || c == '\'') + goto letter_quote; + break; + } + while (isalnum(c) || c == '_') { + c = tok_nextc(tok); + } + tok_backup(tok, c); + *p_start = tok->start; + *p_end = tok->cur; + return NAME; + } + + /* Newline */ + if (c == '\n') { + tok->atbol = 1; + if (blankline || tok->level > 0) + goto nextline; + *p_start = tok->start; + *p_end = tok->cur - 1; /* Leave '\n' out of the string */ + tok->cont_line = 0; + return NEWLINE; + } + +#ifdef macintosh + if (c == '\r') { + PySys_WriteStderr( + "File contains \\r characters (incorrect line endings?)\n"); + tok->done = E_TOKEN; + tok->cur = tok->inp; + return ERRORTOKEN; + } +#endif + /* Period or number starting with period? */ + if (c == '.') { + c = tok_nextc(tok); + if (isdigit(c)) { + goto fraction; + } + else { + tok_backup(tok, c); + *p_start = tok->start; + *p_end = tok->cur; + return DOT; + } + } + + /* Number */ + if (isdigit(c)) { + if (c == '0') { + /* Hex or octal -- maybe. */ + c = tok_nextc(tok); + if (c == '.') + goto fraction; +#ifndef WITHOUT_COMPLEX + if (c == 'j' || c == 'J') + goto imaginary; +#endif + if (c == 'x' || c == 'X') { + /* Hex */ + do { + c = tok_nextc(tok); + } while (isxdigit(c)); + } + else { + int found_decimal = 0; + /* Octal; c is first char of it */ + /* There's no 'isoctdigit' macro, sigh */ + while ('0' <= c && c < '8') { + c = tok_nextc(tok); + } + if (isdigit(c)) { + found_decimal = 1; + do { + c = tok_nextc(tok); + } while (isdigit(c)); + } + if (c == '.') + goto fraction; + else if (c == 'e' || c == 'E') + goto exponent; +#ifndef WITHOUT_COMPLEX + else if (c == 'j' || c == 'J') + goto imaginary; +#endif + else if (found_decimal) { + tok->done = E_TOKEN; + tok_backup(tok, c); + return ERRORTOKEN; + } + } + if (c == 'l' || c == 'L') + c = tok_nextc(tok); + } + else { + /* Decimal */ + do { + c = tok_nextc(tok); + } while (isdigit(c)); + if (c == 'l' || c == 'L') + c = tok_nextc(tok); + else { + /* Accept floating point numbers. */ + if (c == '.') { + fraction: + /* Fraction */ + do { + c = tok_nextc(tok); + } while (isdigit(c)); + } + if (c == 'e' || c == 'E') { + exponent: + /* Exponent part */ + c = tok_nextc(tok); + if (c == '+' || c == '-') + c = tok_nextc(tok); + if (!isdigit(c)) { + tok->done = E_TOKEN; + tok_backup(tok, c); + return ERRORTOKEN; + } + do { + c = tok_nextc(tok); + } while (isdigit(c)); + } +#ifndef WITHOUT_COMPLEX + if (c == 'j' || c == 'J') + /* Imaginary part */ + imaginary: + c = tok_nextc(tok); +#endif + } + } + tok_backup(tok, c); + *p_start = tok->start; + *p_end = tok->cur; + return NUMBER; + } + + letter_quote: + /* String */ + if (c == '\'' || c == '"') { + int quote2 = tok->cur - tok->start + 1; + int quote = c; + int triple = 0; + int tripcount = 0; + for (;;) { + c = tok_nextc(tok); + if (c == '\n') { + if (!triple) { + tok->done = E_EOLS; + tok_backup(tok, c); + return ERRORTOKEN; + } + tripcount = 0; + tok->cont_line = 1; /* multiline string. */ + } + else if (c == EOF) { + if (triple) + tok->done = E_EOFS; + else + tok->done = E_EOLS; + tok->cur = tok->inp; + return ERRORTOKEN; + } + else if (c == quote) { + tripcount++; + if (tok->cur - tok->start == quote2) { + c = tok_nextc(tok); + if (c == quote) { + triple = 1; + tripcount = 0; + continue; + } + tok_backup(tok, c); + } + if (!triple || tripcount == 3) + break; + } + else if (c == '\\') { + tripcount = 0; + c = tok_nextc(tok); + if (c == EOF) { + tok->done = E_EOLS; + tok->cur = tok->inp; + return ERRORTOKEN; + } + } + else + tripcount = 0; + } + *p_start = tok->start; + *p_end = tok->cur; + return STRING; + } + + /* Line continuation */ + if (c == '\\') { + c = tok_nextc(tok); + if (c != '\n') { + tok->done = E_TOKEN; + tok->cur = tok->inp; + return ERRORTOKEN; + } + tok->cont_line = 1; + goto again; /* Read next line */ + } + + /* Check for two-character token */ + { + int c2 = tok_nextc(tok); + int token = PyToken_TwoChars(c, c2); + if (token != OP) { + int c3 = tok_nextc(tok); + int token3 = PyToken_ThreeChars(c, c2, c3); + if (token3 != OP) { + token = token3; + } else { + tok_backup(tok, c3); + } + *p_start = tok->start; + *p_end = tok->cur; + return token; + } + tok_backup(tok, c2); + } + + /* Keep track of parentheses nesting level */ + switch (c) { + case '(': + case '[': + case '{': + tok->level++; + break; + case ')': + case ']': + case '}': + tok->level--; + break; + } + + /* Punctuation character */ + *p_start = tok->start; + *p_end = tok->cur; + return PyToken_OneChar(c); +} + +int +PyTokenizer_Get(struct tok_state *tok, char **p_start, char **p_end) +{ + int result = tok_get(tok, p_start, p_end); + if (tok->decoding_erred) { + result = ERRORTOKEN; + tok->done = E_DECODE; + } + return result; +} + +#ifdef Py_DEBUG + +void +tok_dump(int type, char *start, char *end) +{ + printf("%s", _PyParser_TokenNames[type]); + if (type == NAME || type == NUMBER || type == STRING || type == OP) + printf("(%.*s)", (int)(end - start), start); +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer.h new file mode 100644 index 00000000..342e7fa9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer.h @@ -0,0 +1,65 @@ +#ifndef Py_TOKENIZER_H +#define Py_TOKENIZER_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "object.h" + +/* Tokenizer interface */ + +#include "token.h" /* For token types */ + +#define MAXINDENT 100 /* Max indentation level */ + +/* Tokenizer state */ +struct tok_state { + /* Input state; buf <= cur <= inp <= end */ + /* NB an entire line is held in the buffer */ + char *buf; /* Input buffer, or NULL; malloc'ed if fp != NULL */ + char *cur; /* Next character in buffer */ + char *inp; /* End of data in buffer */ + char *end; /* End of input buffer if buf != NULL */ + char *start; /* Start of current token if not NULL */ + int done; /* E_OK normally, E_EOF at EOF, otherwise error code */ + /* NB If done != E_OK, cur must be == inp!!! */ + FILE *fp; /* Rest of input; NULL if tokenizing a string */ + int tabsize; /* Tab spacing */ + int indent; /* Current indentation index */ + int indstack[MAXINDENT]; /* Stack of indents */ + int atbol; /* Nonzero if at begin of new line */ + int pendin; /* Pending indents (if > 0) or dedents (if < 0) */ + char *prompt, *nextprompt; /* For interactive prompting */ + int lineno; /* Current line number */ + int level; /* () [] {} Parentheses nesting level */ + /* Used to allow free continuations inside them */ + /* Stuff for checking on different tab sizes */ + const char *filename; /* For error messages */ + int altwarning; /* Issue warning if alternate tabs don't match */ + int alterror; /* Issue error if alternate tabs don't match */ + int alttabsize; /* Alternate tab spacing */ + int altindstack[MAXINDENT]; /* Stack of alternate indents */ + /* Stuff for PEP 0263 */ + int decoding_state; /* -1:decoding, 0:init, 1:raw */ + int decoding_erred; /* whether erred in decoding */ + int read_coding_spec; /* whether 'coding:...' has been read */ + int issued_encoding_warning; /* whether non-ASCII warning was issued */ + char *encoding; + int cont_line; /* whether we are in a continuation line. */ +#ifndef PGEN + PyObject *decoding_readline; /* codecs.open(...).readline */ + PyObject *decoding_buffer; +#endif + const char* enc; + const char* str; +}; + +extern struct tok_state *PyTokenizer_FromString(const char *); +extern struct tok_state *PyTokenizer_FromFile(FILE *, char *, char *); +extern void PyTokenizer_Free(struct tok_state *); +extern int PyTokenizer_Get(struct tok_state *, char **, char **); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_TOKENIZER_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer_pgen.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer_pgen.c new file mode 100644 index 00000000..ecbda9c4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Parser/tokenizer_pgen.c @@ -0,0 +1,2 @@ +#define PGEN +#include "tokenizer.c" diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/.cvsignore b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/.cvsignore new file mode 100644 index 00000000..100224c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/.cvsignore @@ -0,0 +1,2 @@ +Makefile +add2lib diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/atof.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/atof.c new file mode 100644 index 00000000..c83c3c1c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/atof.c @@ -0,0 +1,50 @@ + +/* Just in case you haven't got an atof() around... + This one doesn't check for bad syntax or overflow, + and is slow and inaccurate. + But it's good enough for the occasional string literal... */ + +#include "pyconfig.h" + +#include + +double atof(char *s) +{ + double a = 0.0; + int e = 0; + int c; + while ((c = *s++) != '\0' && isdigit(c)) { + a = a*10.0 + (c - '0'); + } + if (c == '.') { + while ((c = *s++) != '\0' && isdigit(c)) { + a = a*10.0 + (c - '0'); + e = e-1; + } + } + if (c == 'e' || c == 'E') { + int sign = 1; + int i = 0; + c = *s++; + if (c == '+') + c = *s++; + else if (c == '-') { + c = *s++; + sign = -1; + } + while (isdigit(c)) { + i = i*10 + (c - '0'); + c = *s++; + } + e += i*sign; + } + while (e > 0) { + a *= 10.0; + e--; + } + while (e < 0) { + a *= 0.1; + e++; + } + return a; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/bltinmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/bltinmodule.c new file mode 100644 index 00000000..ece30f11 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/bltinmodule.c @@ -0,0 +1,2406 @@ + +/* Built-in functions */ + +#include "Python.h" + +#include "node.h" +#include "compile.h" +#include "eval.h" + +#include + +#ifdef RISCOS +#include "unixstuff.h" +#endif + +/* The default encoding used by the platform file system APIs + Can remain NULL for all platforms that don't have such a concept +*/ +#if defined(MS_WINDOWS) && defined(HAVE_USABLE_WCHAR_T) +const char *Py_FileSystemDefaultEncoding = "mbcs"; +#elif defined(__APPLE__) +const char *Py_FileSystemDefaultEncoding = "utf-8"; +#else +const char *Py_FileSystemDefaultEncoding = NULL; /* use default */ +#endif + +/* Forward */ +static PyObject *filterstring(PyObject *, PyObject *); +#ifdef Py_USING_UNICODE +static PyObject *filterunicode(PyObject *, PyObject *); +#endif +static PyObject *filtertuple (PyObject *, PyObject *); + +static PyObject * +builtin___import__(PyObject *self, PyObject *args) +{ + char *name; + PyObject *globals = NULL; + PyObject *locals = NULL; + PyObject *fromlist = NULL; + + if (!PyArg_ParseTuple(args, "s|OOO:__import__", + &name, &globals, &locals, &fromlist)) + return NULL; + return PyImport_ImportModuleEx(name, globals, locals, fromlist); +} + +PyDoc_STRVAR(import_doc, +"__import__(name, globals, locals, fromlist) -> module\n\ +\n\ +Import a module. The globals are only used to determine the context;\n\ +they are not modified. The locals are currently unused. The fromlist\n\ +should be a list of names to emulate ``from name import ...'', or an\n\ +empty list to emulate ``import name''.\n\ +When importing a module from a package, note that __import__('A.B', ...)\n\ +returns package A when fromlist is empty, but its submodule B when\n\ +fromlist is not empty."); + + +static PyObject * +builtin_abs(PyObject *self, PyObject *v) +{ + return PyNumber_Absolute(v); +} + +PyDoc_STRVAR(abs_doc, +"abs(number) -> number\n\ +\n\ +Return the absolute value of the argument."); + + +static PyObject * +builtin_apply(PyObject *self, PyObject *args) +{ + PyObject *func, *alist = NULL, *kwdict = NULL; + PyObject *t = NULL, *retval = NULL; + + if (!PyArg_UnpackTuple(args, "apply", 1, 3, &func, &alist, &kwdict)) + return NULL; + if (alist != NULL) { + if (!PyTuple_Check(alist)) { + if (!PySequence_Check(alist)) { + PyErr_Format(PyExc_TypeError, + "apply() arg 2 expected sequence, found %s", + alist->ob_type->tp_name); + return NULL; + } + t = PySequence_Tuple(alist); + if (t == NULL) + return NULL; + alist = t; + } + } + if (kwdict != NULL && !PyDict_Check(kwdict)) { + PyErr_Format(PyExc_TypeError, + "apply() arg 3 expected dictionary, found %s", + kwdict->ob_type->tp_name); + goto finally; + } + retval = PyEval_CallObjectWithKeywords(func, alist, kwdict); + finally: + Py_XDECREF(t); + return retval; +} + +PyDoc_STRVAR(apply_doc, +"apply(object[, args[, kwargs]]) -> value\n\ +\n\ +Call a callable object with positional arguments taken from the tuple args,\n\ +and keyword arguments taken from the optional dictionary kwargs.\n\ +Note that classes are callable, as are instances with a __call__() method.\n\ +\n\ +Deprecated since release 2.3. Instead, use the extended call syntax:\n\ + function(*args, **keywords)."); + + +static PyObject * +builtin_callable(PyObject *self, PyObject *v) +{ + return PyBool_FromLong((long)PyCallable_Check(v)); +} + +PyDoc_STRVAR(callable_doc, +"callable(object) -> bool\n\ +\n\ +Return whether the object is callable (i.e., some kind of function).\n\ +Note that classes are callable, as are instances with a __call__() method."); + + +static PyObject * +builtin_filter(PyObject *self, PyObject *args) +{ + PyObject *func, *seq, *result, *it, *arg; + int len; /* guess for result list size */ + register int j; + + if (!PyArg_UnpackTuple(args, "filter", 2, 2, &func, &seq)) + return NULL; + + /* Strings and tuples return a result of the same type. */ + if (PyString_Check(seq)) + return filterstring(func, seq); +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(seq)) + return filterunicode(func, seq); +#endif + if (PyTuple_Check(seq)) + return filtertuple(func, seq); + + /* Get iterator. */ + it = PyObject_GetIter(seq); + if (it == NULL) + return NULL; + + /* Guess a result list size. */ + len = -1; /* unknown */ + if (PySequence_Check(seq) && + seq->ob_type->tp_as_sequence->sq_length) { + len = PySequence_Size(seq); + if (len < 0) + PyErr_Clear(); + } + if (len < 0) + len = 8; /* arbitrary */ + + /* Pre-allocate argument list tuple. */ + arg = PyTuple_New(1); + if (arg == NULL) + goto Fail_arg; + + /* Get a result list. */ + if (PyList_Check(seq) && seq->ob_refcnt == 1) { + /* Eww - can modify the list in-place. */ + Py_INCREF(seq); + result = seq; + } + else { + result = PyList_New(len); + if (result == NULL) + goto Fail_it; + } + + /* Build the result list. */ + j = 0; + for (;;) { + PyObject *item; + int ok; + + item = PyIter_Next(it); + if (item == NULL) { + if (PyErr_Occurred()) + goto Fail_result_it; + break; + } + + if (func == Py_None) { + ok = PyObject_IsTrue(item); + } + else { + PyObject *good; + PyTuple_SET_ITEM(arg, 0, item); + good = PyObject_Call(func, arg, NULL); + PyTuple_SET_ITEM(arg, 0, NULL); + if (good == NULL) { + Py_DECREF(item); + goto Fail_result_it; + } + ok = PyObject_IsTrue(good); + Py_DECREF(good); + } + if (ok) { + if (j < len) + PyList_SET_ITEM(result, j, item); + else { + int status = PyList_Append(result, item); + Py_DECREF(item); + if (status < 0) + goto Fail_result_it; + } + ++j; + } + else + Py_DECREF(item); + } + + + /* Cut back result list if len is too big. */ + if (j < len && PyList_SetSlice(result, j, len, NULL) < 0) + goto Fail_result_it; + + Py_DECREF(it); + Py_DECREF(arg); + return result; + +Fail_result_it: + Py_DECREF(result); +Fail_it: + Py_DECREF(it); +Fail_arg: + Py_DECREF(arg); + return NULL; +} + +PyDoc_STRVAR(filter_doc, +"filter(function or None, sequence) -> list, tuple, or string\n" +"\n" +"Return those items of sequence for which function(item) is true. If\n" +"function is None, return the items that are true. If sequence is a tuple\n" +"or string, return the same type, else return a list."); + +static PyObject * +builtin_chr(PyObject *self, PyObject *args) +{ + long x; + char s[1]; + + if (!PyArg_ParseTuple(args, "l:chr", &x)) + return NULL; + if (x < 0 || x >= 256) { + PyErr_SetString(PyExc_ValueError, + "chr() arg not in range(256)"); + return NULL; + } + s[0] = (char)x; + return PyString_FromStringAndSize(s, 1); +} + +PyDoc_STRVAR(chr_doc, +"chr(i) -> character\n\ +\n\ +Return a string of one character with ordinal i; 0 <= i < 256."); + + +#ifdef Py_USING_UNICODE +static PyObject * +builtin_unichr(PyObject *self, PyObject *args) +{ + long x; + + if (!PyArg_ParseTuple(args, "l:unichr", &x)) + return NULL; + + return PyUnicode_FromOrdinal(x); +} + +PyDoc_STRVAR(unichr_doc, +"unichr(i) -> Unicode character\n\ +\n\ +Return a Unicode string of one character with ordinal i; 0 <= i <= 0x10ffff."); +#endif + + +static PyObject * +builtin_cmp(PyObject *self, PyObject *args) +{ + PyObject *a, *b; + int c; + + if (!PyArg_UnpackTuple(args, "cmp", 2, 2, &a, &b)) + return NULL; + if (PyObject_Cmp(a, b, &c) < 0) + return NULL; + return PyInt_FromLong((long)c); +} + +PyDoc_STRVAR(cmp_doc, +"cmp(x, y) -> integer\n\ +\n\ +Return negative if xy."); + + +static PyObject * +builtin_coerce(PyObject *self, PyObject *args) +{ + PyObject *v, *w; + PyObject *res; + + if (!PyArg_UnpackTuple(args, "coerce", 2, 2, &v, &w)) + return NULL; + if (PyNumber_Coerce(&v, &w) < 0) + return NULL; + res = Py_BuildValue("(OO)", v, w); + Py_DECREF(v); + Py_DECREF(w); + return res; +} + +PyDoc_STRVAR(coerce_doc, +"coerce(x, y) -> None or (x1, y1)\n\ +\n\ +When x and y can be coerced to values of the same type, return a tuple\n\ +containing the coerced values. When they can't be coerced, return None."); + + +static PyObject * +builtin_compile(PyObject *self, PyObject *args) +{ + char *str; + char *filename; + char *startstr; + int start; + int dont_inherit = 0; + int supplied_flags = 0; + PyCompilerFlags cf; + PyObject *result, *cmd, *tmp = NULL; + int length; + + if (!PyArg_ParseTuple(args, "Oss|ii:compile", &cmd, &filename, + &startstr, &supplied_flags, &dont_inherit)) + return NULL; + + cf.cf_flags = supplied_flags; + +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(cmd)) { + tmp = PyUnicode_AsUTF8String(cmd); + if (tmp == NULL) + return NULL; + cmd = tmp; + cf.cf_flags |= PyCF_SOURCE_IS_UTF8; + } +#endif + if (PyObject_AsReadBuffer(cmd, (const void **)&str, &length)) + return NULL; + if ((size_t)length != strlen(str)) { + PyErr_SetString(PyExc_TypeError, + "compile() expected string without null bytes"); + return NULL; + } + + if (strcmp(startstr, "exec") == 0) + start = Py_file_input; + else if (strcmp(startstr, "eval") == 0) + start = Py_eval_input; + else if (strcmp(startstr, "single") == 0) + start = Py_single_input; + else { + PyErr_SetString(PyExc_ValueError, + "compile() arg 3 must be 'exec' or 'eval' or 'single'"); + return NULL; + } + + if (supplied_flags & + ~(PyCF_MASK | PyCF_MASK_OBSOLETE | PyCF_DONT_IMPLY_DEDENT)) + { + PyErr_SetString(PyExc_ValueError, + "compile(): unrecognised flags"); + return NULL; + } + /* XXX Warn if (supplied_flags & PyCF_MASK_OBSOLETE) != 0? */ + + if (!dont_inherit) { + PyEval_MergeCompilerFlags(&cf); + } + result = Py_CompileStringFlags(str, filename, start, &cf); + Py_XDECREF(tmp); + return result; +} + +PyDoc_STRVAR(compile_doc, +"compile(source, filename, mode[, flags[, dont_inherit]]) -> code object\n\ +\n\ +Compile the source string (a Python module, statement or expression)\n\ +into a code object that can be executed by the exec statement or eval().\n\ +The filename will be used for run-time error messages.\n\ +The mode must be 'exec' to compile a module, 'single' to compile a\n\ +single (interactive) statement, or 'eval' to compile an expression.\n\ +The flags argument, if present, controls which future statements influence\n\ +the compilation of the code.\n\ +The dont_inherit argument, if non-zero, stops the compilation inheriting\n\ +the effects of any future statements in effect in the code calling\n\ +compile; if absent or zero these statements do influence the compilation,\n\ +in addition to any features explicitly specified."); + +static PyObject * +builtin_dir(PyObject *self, PyObject *args) +{ + PyObject *arg = NULL; + + if (!PyArg_UnpackTuple(args, "dir", 0, 1, &arg)) + return NULL; + return PyObject_Dir(arg); +} + +PyDoc_STRVAR(dir_doc, +"dir([object]) -> list of strings\n" +"\n" +"Return an alphabetized list of names comprising (some of) the attributes\n" +"of the given object, and of attributes reachable from it:\n" +"\n" +"No argument: the names in the current scope.\n" +"Module object: the module attributes.\n" +"Type or class object: its attributes, and recursively the attributes of\n" +" its bases.\n" +"Otherwise: its attributes, its class's attributes, and recursively the\n" +" attributes of its class's base classes."); + +static PyObject * +builtin_divmod(PyObject *self, PyObject *args) +{ + PyObject *v, *w; + + if (!PyArg_UnpackTuple(args, "divmod", 2, 2, &v, &w)) + return NULL; + return PyNumber_Divmod(v, w); +} + +PyDoc_STRVAR(divmod_doc, +"divmod(x, y) -> (div, mod)\n\ +\n\ +Return the tuple ((x-x%y)/y, x%y). Invariant: div*y + mod == x."); + + +static PyObject * +builtin_eval(PyObject *self, PyObject *args) +{ + PyObject *cmd, *result, *tmp = NULL; + PyObject *globals = Py_None, *locals = Py_None; + char *str; + PyCompilerFlags cf; + + if (!PyArg_ParseTuple(args, "O|O!O!:eval", + &cmd, + &PyDict_Type, &globals, + &PyDict_Type, &locals)) + return NULL; + if (globals == Py_None) { + globals = PyEval_GetGlobals(); + if (locals == Py_None) + locals = PyEval_GetLocals(); + } + else if (locals == Py_None) + locals = globals; + + if (PyDict_GetItemString(globals, "__builtins__") == NULL) { + if (PyDict_SetItemString(globals, "__builtins__", + PyEval_GetBuiltins()) != 0) + return NULL; + } + + if (PyCode_Check(cmd)) { + if (PyCode_GetNumFree((PyCodeObject *)cmd) > 0) { + PyErr_SetString(PyExc_TypeError, + "code object passed to eval() may not contain free variables"); + return NULL; + } + return PyEval_EvalCode((PyCodeObject *) cmd, globals, locals); + } + + if (!PyString_Check(cmd) && + !PyUnicode_Check(cmd)) { + PyErr_SetString(PyExc_TypeError, + "eval() arg 1 must be a string or code object"); + return NULL; + } + cf.cf_flags = 0; + +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(cmd)) { + tmp = PyUnicode_AsUTF8String(cmd); + if (tmp == NULL) + return NULL; + cmd = tmp; + cf.cf_flags |= PyCF_SOURCE_IS_UTF8; + } +#endif + if (PyString_AsStringAndSize(cmd, &str, NULL)) + return NULL; + while (*str == ' ' || *str == '\t') + str++; + + (void)PyEval_MergeCompilerFlags(&cf); + result = PyRun_StringFlags(str, Py_eval_input, globals, locals, &cf); + Py_XDECREF(tmp); + return result; +} + +PyDoc_STRVAR(eval_doc, +"eval(source[, globals[, locals]]) -> value\n\ +\n\ +Evaluate the source in the context of globals and locals.\n\ +The source may be a string representing a Python expression\n\ +or a code object as returned by compile().\n\ +The globals and locals are dictionaries, defaulting to the current\n\ +globals and locals. If only globals is given, locals defaults to it."); + + +static PyObject * +builtin_execfile(PyObject *self, PyObject *args) +{ + char *filename; + PyObject *globals = Py_None, *locals = Py_None; + PyObject *res; + FILE* fp = NULL; + PyCompilerFlags cf; + int exists; + + if (!PyArg_ParseTuple(args, "s|O!O!:execfile", + &filename, + &PyDict_Type, &globals, + &PyDict_Type, &locals)) + return NULL; + if (globals == Py_None) { + globals = PyEval_GetGlobals(); + if (locals == Py_None) + locals = PyEval_GetLocals(); + } + else if (locals == Py_None) + locals = globals; + if (PyDict_GetItemString(globals, "__builtins__") == NULL) { + if (PyDict_SetItemString(globals, "__builtins__", + PyEval_GetBuiltins()) != 0) + return NULL; + } + + exists = 0; + /* Test for existence or directory. */ +#if defined(PLAN9) + { + Dir *d; + + if ((d = dirstat(filename))!=nil) { + if(d->mode & DMDIR) + werrstr("is a directory"); + else + exists = 1; + free(d); + } + } +#elif defined(RISCOS) + if (object_exists(filename)) { + if (isdir(filename)) + errno = EISDIR; + else + exists = 1; + } +#else /* standard Posix */ + { + struct stat s; + if (stat(filename, &s) == 0) { + if (S_ISDIR(s.st_mode)) +# if defined(PY_OS2) && defined(PYCC_VACPP) + errno = EOS2ERR; +# else + errno = EISDIR; +# endif + else + exists = 1; + } + } +#endif + + if (exists) { + Py_BEGIN_ALLOW_THREADS + fp = fopen(filename, "r" PY_STDIOTEXTMODE); + Py_END_ALLOW_THREADS + + if (fp == NULL) { + exists = 0; + } + } + + if (!exists) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename); + return NULL; + } + cf.cf_flags = 0; + if (PyEval_MergeCompilerFlags(&cf)) + res = PyRun_FileExFlags(fp, filename, Py_file_input, globals, + locals, 1, &cf); + else + res = PyRun_FileEx(fp, filename, Py_file_input, globals, + locals, 1); + return res; +} + +PyDoc_STRVAR(execfile_doc, +"execfile(filename[, globals[, locals]])\n\ +\n\ +Read and execute a Python script from a file.\n\ +The globals and locals are dictionaries, defaulting to the current\n\ +globals and locals. If only globals is given, locals defaults to it."); + + +static PyObject * +builtin_getattr(PyObject *self, PyObject *args) +{ + PyObject *v, *result, *dflt = NULL; + PyObject *name; + + if (!PyArg_UnpackTuple(args, "getattr", 2, 3, &v, &name, &dflt)) + return NULL; +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(name)) { + name = _PyUnicode_AsDefaultEncodedString(name, NULL); + if (name == NULL) + return NULL; + } +#endif + + if (!PyString_Check(name)) { + PyErr_SetString(PyExc_TypeError, + "getattr(): attribute name must be string"); + return NULL; + } + result = PyObject_GetAttr(v, name); + if (result == NULL && dflt != NULL && + PyErr_ExceptionMatches(PyExc_AttributeError)) + { + PyErr_Clear(); + Py_INCREF(dflt); + result = dflt; + } + return result; +} + +PyDoc_STRVAR(getattr_doc, +"getattr(object, name[, default]) -> value\n\ +\n\ +Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.\n\ +When a default argument is given, it is returned when the attribute doesn't\n\ +exist; without it, an exception is raised in that case."); + + +static PyObject * +builtin_globals(PyObject *self) +{ + PyObject *d; + + d = PyEval_GetGlobals(); + Py_INCREF(d); + return d; +} + +PyDoc_STRVAR(globals_doc, +"globals() -> dictionary\n\ +\n\ +Return the dictionary containing the current scope's global variables."); + + +static PyObject * +builtin_hasattr(PyObject *self, PyObject *args) +{ + PyObject *v; + PyObject *name; + + if (!PyArg_UnpackTuple(args, "hasattr", 2, 2, &v, &name)) + return NULL; +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(name)) { + name = _PyUnicode_AsDefaultEncodedString(name, NULL); + if (name == NULL) + return NULL; + } +#endif + + if (!PyString_Check(name)) { + PyErr_SetString(PyExc_TypeError, + "hasattr(): attribute name must be string"); + return NULL; + } + v = PyObject_GetAttr(v, name); + if (v == NULL) { + PyErr_Clear(); + Py_INCREF(Py_False); + return Py_False; + } + Py_DECREF(v); + Py_INCREF(Py_True); + return Py_True; +} + +PyDoc_STRVAR(hasattr_doc, +"hasattr(object, name) -> bool\n\ +\n\ +Return whether the object has an attribute with the given name.\n\ +(This is done by calling getattr(object, name) and catching exceptions.)"); + + +static PyObject * +builtin_id(PyObject *self, PyObject *v) +{ + return PyLong_FromVoidPtr(v); +} + +PyDoc_STRVAR(id_doc, +"id(object) -> integer\n\ +\n\ +Return the identity of an object. This is guaranteed to be unique among\n\ +simultaneously existing objects. (Hint: it's the object's memory address.)"); + + +static PyObject * +builtin_map(PyObject *self, PyObject *args) +{ + typedef struct { + PyObject *it; /* the iterator object */ + int saw_StopIteration; /* bool: did the iterator end? */ + } sequence; + + PyObject *func, *result; + sequence *seqs = NULL, *sqp; + int n, len; + register int i, j; + + n = PyTuple_Size(args); + if (n < 2) { + PyErr_SetString(PyExc_TypeError, + "map() requires at least two args"); + return NULL; + } + + func = PyTuple_GetItem(args, 0); + n--; + + if (func == Py_None && n == 1) { + /* map(None, S) is the same as list(S). */ + return PySequence_List(PyTuple_GetItem(args, 1)); + } + + /* Get space for sequence descriptors. Must NULL out the iterator + * pointers so that jumping to Fail_2 later doesn't see trash. + */ + if ((seqs = PyMem_NEW(sequence, n)) == NULL) { + PyErr_NoMemory(); + return NULL; + } + for (i = 0; i < n; ++i) { + seqs[i].it = (PyObject*)NULL; + seqs[i].saw_StopIteration = 0; + } + + /* Do a first pass to obtain iterators for the arguments, and set len + * to the largest of their lengths. + */ + len = 0; + for (i = 0, sqp = seqs; i < n; ++i, ++sqp) { + PyObject *curseq; + int curlen; + + /* Get iterator. */ + curseq = PyTuple_GetItem(args, i+1); + sqp->it = PyObject_GetIter(curseq); + if (sqp->it == NULL) { + static char errmsg[] = + "argument %d to map() must support iteration"; + char errbuf[sizeof(errmsg) + 25]; + PyOS_snprintf(errbuf, sizeof(errbuf), errmsg, i+2); + PyErr_SetString(PyExc_TypeError, errbuf); + goto Fail_2; + } + + /* Update len. */ + curlen = -1; /* unknown */ + if (PySequence_Check(curseq) && + curseq->ob_type->tp_as_sequence->sq_length) { + curlen = PySequence_Size(curseq); + if (curlen < 0) + PyErr_Clear(); + } + if (curlen < 0) + curlen = 8; /* arbitrary */ + if (curlen > len) + len = curlen; + } + + /* Get space for the result list. */ + if ((result = (PyObject *) PyList_New(len)) == NULL) + goto Fail_2; + + /* Iterate over the sequences until all have stopped. */ + for (i = 0; ; ++i) { + PyObject *alist, *item=NULL, *value; + int numactive = 0; + + if (func == Py_None && n == 1) + alist = NULL; + else if ((alist = PyTuple_New(n)) == NULL) + goto Fail_1; + + for (j = 0, sqp = seqs; j < n; ++j, ++sqp) { + if (sqp->saw_StopIteration) { + Py_INCREF(Py_None); + item = Py_None; + } + else { + item = PyIter_Next(sqp->it); + if (item) + ++numactive; + else { + if (PyErr_Occurred()) { + Py_XDECREF(alist); + goto Fail_1; + } + Py_INCREF(Py_None); + item = Py_None; + sqp->saw_StopIteration = 1; + } + } + if (alist) + PyTuple_SET_ITEM(alist, j, item); + else + break; + } + + if (!alist) + alist = item; + + if (numactive == 0) { + Py_DECREF(alist); + break; + } + + if (func == Py_None) + value = alist; + else { + value = PyEval_CallObject(func, alist); + Py_DECREF(alist); + if (value == NULL) + goto Fail_1; + } + if (i >= len) { + int status = PyList_Append(result, value); + Py_DECREF(value); + if (status < 0) + goto Fail_1; + } + else if (PyList_SetItem(result, i, value) < 0) + goto Fail_1; + } + + if (i < len && PyList_SetSlice(result, i, len, NULL) < 0) + goto Fail_1; + + goto Succeed; + +Fail_1: + Py_DECREF(result); +Fail_2: + result = NULL; +Succeed: + assert(seqs); + for (i = 0; i < n; ++i) + Py_XDECREF(seqs[i].it); + PyMem_DEL(seqs); + return result; +} + +PyDoc_STRVAR(map_doc, +"map(function, sequence[, sequence, ...]) -> list\n\ +\n\ +Return a list of the results of applying the function to the items of\n\ +the argument sequence(s). If more than one sequence is given, the\n\ +function is called with an argument list consisting of the corresponding\n\ +item of each sequence, substituting None for missing values when not all\n\ +sequences have the same length. If the function is None, return a list of\n\ +the items of the sequence (or a list of tuples if more than one sequence)."); + + +static PyObject * +builtin_setattr(PyObject *self, PyObject *args) +{ + PyObject *v; + PyObject *name; + PyObject *value; + + if (!PyArg_UnpackTuple(args, "setattr", 3, 3, &v, &name, &value)) + return NULL; + if (PyObject_SetAttr(v, name, value) != 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(setattr_doc, +"setattr(object, name, value)\n\ +\n\ +Set a named attribute on an object; setattr(x, 'y', v) is equivalent to\n\ +``x.y = v''."); + + +static PyObject * +builtin_delattr(PyObject *self, PyObject *args) +{ + PyObject *v; + PyObject *name; + + if (!PyArg_UnpackTuple(args, "delattr", 2, 2, &v, &name)) + return NULL; + if (PyObject_SetAttr(v, name, (PyObject *)NULL) != 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(delattr_doc, +"delattr(object, name)\n\ +\n\ +Delete a named attribute on an object; delattr(x, 'y') is equivalent to\n\ +``del x.y''."); + + +static PyObject * +builtin_hash(PyObject *self, PyObject *v) +{ + long x; + + x = PyObject_Hash(v); + if (x == -1) + return NULL; + return PyInt_FromLong(x); +} + +PyDoc_STRVAR(hash_doc, +"hash(object) -> integer\n\ +\n\ +Return a hash value for the object. Two objects with the same value have\n\ +the same hash value. The reverse is not necessarily true, but likely."); + + +static PyObject * +builtin_hex(PyObject *self, PyObject *v) +{ + PyNumberMethods *nb; + + if ((nb = v->ob_type->tp_as_number) == NULL || + nb->nb_hex == NULL) { + PyErr_SetString(PyExc_TypeError, + "hex() argument can't be converted to hex"); + return NULL; + } + return (*nb->nb_hex)(v); +} + +PyDoc_STRVAR(hex_doc, +"hex(number) -> string\n\ +\n\ +Return the hexadecimal representation of an integer or long integer."); + + +static PyObject *builtin_raw_input(PyObject *, PyObject *); + +static PyObject * +builtin_input(PyObject *self, PyObject *args) +{ + PyObject *line; + char *str; + PyObject *res; + PyObject *globals, *locals; + + line = builtin_raw_input(self, args); + if (line == NULL) + return line; + if (!PyArg_Parse(line, "s;embedded '\\0' in input line", &str)) + return NULL; + while (*str == ' ' || *str == '\t') + str++; + globals = PyEval_GetGlobals(); + locals = PyEval_GetLocals(); + if (PyDict_GetItemString(globals, "__builtins__") == NULL) { + if (PyDict_SetItemString(globals, "__builtins__", + PyEval_GetBuiltins()) != 0) + return NULL; + } + res = PyRun_String(str, Py_eval_input, globals, locals); + Py_DECREF(line); + return res; +} + +PyDoc_STRVAR(input_doc, +"input([prompt]) -> value\n\ +\n\ +Equivalent to eval(raw_input(prompt))."); + + +static PyObject * +builtin_intern(PyObject *self, PyObject *args) +{ + PyObject *s; + if (!PyArg_ParseTuple(args, "S:intern", &s)) + return NULL; + Py_INCREF(s); + PyString_InternInPlace(&s); + return s; +} + +PyDoc_STRVAR(intern_doc, +"intern(string) -> string\n\ +\n\ +``Intern'' the given string. This enters the string in the (global)\n\ +table of interned strings whose purpose is to speed up dictionary lookups.\n\ +Return the string itself or the previously interned string object with the\n\ +same value."); + + +static PyObject * +builtin_iter(PyObject *self, PyObject *args) +{ + PyObject *v, *w = NULL; + + if (!PyArg_UnpackTuple(args, "iter", 1, 2, &v, &w)) + return NULL; + if (w == NULL) + return PyObject_GetIter(v); + if (!PyCallable_Check(v)) { + PyErr_SetString(PyExc_TypeError, + "iter(v, w): v must be callable"); + return NULL; + } + return PyCallIter_New(v, w); +} + +PyDoc_STRVAR(iter_doc, +"iter(collection) -> iterator\n\ +iter(callable, sentinel) -> iterator\n\ +\n\ +Get an iterator from an object. In the first form, the argument must\n\ +supply its own iterator, or be a sequence.\n\ +In the second form, the callable is called until it returns the sentinel."); + + +static PyObject * +builtin_len(PyObject *self, PyObject *v) +{ + long res; + + res = PyObject_Size(v); + if (res < 0 && PyErr_Occurred()) + return NULL; + return PyInt_FromLong(res); +} + +PyDoc_STRVAR(len_doc, +"len(object) -> integer\n\ +\n\ +Return the number of items of a sequence or mapping."); + + +static PyObject * +builtin_locals(PyObject *self) +{ + PyObject *d; + + d = PyEval_GetLocals(); + Py_INCREF(d); + return d; +} + +PyDoc_STRVAR(locals_doc, +"locals() -> dictionary\n\ +\n\ +Update and return a dictionary containing the current scope's local variables."); + + +static PyObject * +min_max(PyObject *args, int op) +{ + PyObject *v, *w, *x, *it; + + if (PyTuple_Size(args) > 1) + v = args; + else if (!PyArg_UnpackTuple(args, (op==Py_LT) ? "min" : "max", 1, 1, &v)) + return NULL; + + it = PyObject_GetIter(v); + if (it == NULL) + return NULL; + + w = NULL; /* the result */ + for (;;) { + x = PyIter_Next(it); + if (x == NULL) { + if (PyErr_Occurred()) { + Py_XDECREF(w); + Py_DECREF(it); + return NULL; + } + break; + } + + if (w == NULL) + w = x; + else { + int cmp = PyObject_RichCompareBool(x, w, op); + if (cmp > 0) { + Py_DECREF(w); + w = x; + } + else if (cmp < 0) { + Py_DECREF(x); + Py_DECREF(w); + Py_DECREF(it); + return NULL; + } + else + Py_DECREF(x); + } + } + if (w == NULL) + PyErr_SetString(PyExc_ValueError, + "min() or max() arg is an empty sequence"); + Py_DECREF(it); + return w; +} + +static PyObject * +builtin_min(PyObject *self, PyObject *v) +{ + return min_max(v, Py_LT); +} + +PyDoc_STRVAR(min_doc, +"min(sequence) -> value\n\ +min(a, b, c, ...) -> value\n\ +\n\ +With a single sequence argument, return its smallest item.\n\ +With two or more arguments, return the smallest argument."); + + +static PyObject * +builtin_max(PyObject *self, PyObject *v) +{ + return min_max(v, Py_GT); +} + +PyDoc_STRVAR(max_doc, +"max(sequence) -> value\n\ +max(a, b, c, ...) -> value\n\ +\n\ +With a single sequence argument, return its largest item.\n\ +With two or more arguments, return the largest argument."); + + +static PyObject * +builtin_oct(PyObject *self, PyObject *v) +{ + PyNumberMethods *nb; + + if (v == NULL || (nb = v->ob_type->tp_as_number) == NULL || + nb->nb_oct == NULL) { + PyErr_SetString(PyExc_TypeError, + "oct() argument can't be converted to oct"); + return NULL; + } + return (*nb->nb_oct)(v); +} + +PyDoc_STRVAR(oct_doc, +"oct(number) -> string\n\ +\n\ +Return the octal representation of an integer or long integer."); + + +static PyObject * +builtin_ord(PyObject *self, PyObject* obj) +{ + long ord; + int size; + + if (PyString_Check(obj)) { + size = PyString_GET_SIZE(obj); + if (size == 1) { + ord = (long)((unsigned char)*PyString_AS_STRING(obj)); + return PyInt_FromLong(ord); + } +#ifdef Py_USING_UNICODE + } else if (PyUnicode_Check(obj)) { + size = PyUnicode_GET_SIZE(obj); + if (size == 1) { + ord = (long)*PyUnicode_AS_UNICODE(obj); + return PyInt_FromLong(ord); + } +#endif + } else { + PyErr_Format(PyExc_TypeError, + "ord() expected string of length 1, but " \ + "%.200s found", obj->ob_type->tp_name); + return NULL; + } + + PyErr_Format(PyExc_TypeError, + "ord() expected a character, " + "but string of length %d found", + size); + return NULL; +} + +PyDoc_STRVAR(ord_doc, +"ord(c) -> integer\n\ +\n\ +Return the integer ordinal of a one-character string."); + + +static PyObject * +builtin_pow(PyObject *self, PyObject *args) +{ + PyObject *v, *w, *z = Py_None; + + if (!PyArg_UnpackTuple(args, "pow", 2, 3, &v, &w, &z)) + return NULL; + return PyNumber_Power(v, w, z); +} + +PyDoc_STRVAR(pow_doc, +"pow(x, y[, z]) -> number\n\ +\n\ +With two arguments, equivalent to x**y. With three arguments,\n\ +equivalent to (x**y) % z, but may be more efficient (e.g. for longs)."); + + + +/* Return number of items in range (lo, hi, step), when arguments are + * PyInt or PyLong objects. step > 0 required. Return a value < 0 if + * & only if the true value is too large to fit in a signed long. + * Arguments MUST return 1 with either PyInt_Check() or + * PyLong_Check(). Return -1 when there is an error. + */ +static long +get_len_of_range_longs(PyObject *lo, PyObject *hi, PyObject *step) +{ + /* ------------------------------------------------------------- + Algorithm is equal to that of get_len_of_range(), but it operates + on PyObjects (which are assumed to be PyLong or PyInt objects). + ---------------------------------------------------------------*/ + long n; + PyObject *diff = NULL; + PyObject *one = NULL; + PyObject *tmp1 = NULL, *tmp2 = NULL, *tmp3 = NULL; + /* holds sub-expression evaluations */ + + /* if (lo >= hi), return length of 0. */ + if (PyObject_Compare(lo, hi) >= 0) + return 0; + + if ((one = PyLong_FromLong(1L)) == NULL) + goto Fail; + + if ((tmp1 = PyNumber_Subtract(hi, lo)) == NULL) + goto Fail; + + if ((diff = PyNumber_Subtract(tmp1, one)) == NULL) + goto Fail; + + if ((tmp2 = PyNumber_FloorDivide(diff, step)) == NULL) + goto Fail; + + if ((tmp3 = PyNumber_Add(tmp2, one)) == NULL) + goto Fail; + + n = PyLong_AsLong(tmp3); + if (PyErr_Occurred()) { /* Check for Overflow */ + PyErr_Clear(); + goto Fail; + } + + Py_DECREF(tmp3); + Py_DECREF(tmp2); + Py_DECREF(diff); + Py_DECREF(tmp1); + Py_DECREF(one); + return n; + + Fail: + Py_XDECREF(tmp3); + Py_XDECREF(tmp2); + Py_XDECREF(diff); + Py_XDECREF(tmp1); + Py_XDECREF(one); + return -1; +} + +/* An extension of builtin_range() that handles the case when PyLong + * arguments are given. */ +static PyObject * +handle_range_longs(PyObject *self, PyObject *args) +{ + PyObject *ilow; + PyObject *ihigh = NULL; + PyObject *istep = NULL; + + PyObject *curnum = NULL; + PyObject *v = NULL; + long bign; + int i, n; + int cmp_result; + + PyObject *zero = PyLong_FromLong(0); + + if (zero == NULL) + return NULL; + + if (!PyArg_UnpackTuple(args, "range", 1, 3, &ilow, &ihigh, &istep)) { + Py_DECREF(zero); + return NULL; + } + + /* Figure out which way we were called, supply defaults, and be + * sure to incref everything so that the decrefs at the end + * are correct. + */ + assert(ilow != NULL); + if (ihigh == NULL) { + /* only 1 arg -- it's the upper limit */ + ihigh = ilow; + ilow = NULL; + } + assert(ihigh != NULL); + Py_INCREF(ihigh); + + /* ihigh correct now; do ilow */ + if (ilow == NULL) + ilow = zero; + Py_INCREF(ilow); + + /* ilow and ihigh correct now; do istep */ + if (istep == NULL) { + istep = PyLong_FromLong(1L); + if (istep == NULL) + goto Fail; + } + else { + Py_INCREF(istep); + } + + if (!PyInt_Check(ilow) && !PyLong_Check(ilow)) { + PyErr_Format(PyExc_TypeError, + "range() integer start argument expected, got %s.", + ilow->ob_type->tp_name); + goto Fail; + } + + if (!PyInt_Check(ihigh) && !PyLong_Check(ihigh)) { + PyErr_Format(PyExc_TypeError, + "range() integer end argument expected, got %s.", + ihigh->ob_type->tp_name); + goto Fail; + } + + if (!PyInt_Check(istep) && !PyLong_Check(istep)) { + PyErr_Format(PyExc_TypeError, + "range() integer step argument expected, got %s.", + istep->ob_type->tp_name); + goto Fail; + } + + if (PyObject_Cmp(istep, zero, &cmp_result) == -1) + goto Fail; + if (cmp_result == 0) { + PyErr_SetString(PyExc_ValueError, + "range() step argument must not be zero"); + goto Fail; + } + + if (cmp_result > 0) + bign = get_len_of_range_longs(ilow, ihigh, istep); + else { + PyObject *neg_istep = PyNumber_Negative(istep); + if (neg_istep == NULL) + goto Fail; + bign = get_len_of_range_longs(ihigh, ilow, neg_istep); + Py_DECREF(neg_istep); + } + + n = (int)bign; + if (bign < 0 || (long)n != bign) { + PyErr_SetString(PyExc_OverflowError, + "range() result has too many items"); + goto Fail; + } + + v = PyList_New(n); + if (v == NULL) + goto Fail; + + curnum = ilow; + Py_INCREF(curnum); + + for (i = 0; i < n; i++) { + PyObject *w = PyNumber_Long(curnum); + PyObject *tmp_num; + if (w == NULL) + goto Fail; + + PyList_SET_ITEM(v, i, w); + + tmp_num = PyNumber_Add(curnum, istep); + if (tmp_num == NULL) + goto Fail; + + Py_DECREF(curnum); + curnum = tmp_num; + } + Py_DECREF(ilow); + Py_DECREF(ihigh); + Py_DECREF(istep); + Py_DECREF(zero); + Py_DECREF(curnum); + return v; + + Fail: + Py_DECREF(ilow); + Py_DECREF(ihigh); + Py_XDECREF(istep); + Py_DECREF(zero); + Py_XDECREF(curnum); + Py_XDECREF(v); + return NULL; +} + +/* Return number of items in range/xrange (lo, hi, step). step > 0 + * required. Return a value < 0 if & only if the true value is too + * large to fit in a signed long. + */ +static long +get_len_of_range(long lo, long hi, long step) +{ + /* ------------------------------------------------------------- + If lo >= hi, the range is empty. + Else if n values are in the range, the last one is + lo + (n-1)*step, which must be <= hi-1. Rearranging, + n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives + the proper value. Since lo < hi in this case, hi-lo-1 >= 0, so + the RHS is non-negative and so truncation is the same as the + floor. Letting M be the largest positive long, the worst case + for the RHS numerator is hi=M, lo=-M-1, and then + hi-lo-1 = M-(-M-1)-1 = 2*M. Therefore unsigned long has enough + precision to compute the RHS exactly. + ---------------------------------------------------------------*/ + long n = 0; + if (lo < hi) { + unsigned long uhi = (unsigned long)hi; + unsigned long ulo = (unsigned long)lo; + unsigned long diff = uhi - ulo - 1; + n = (long)(diff / (unsigned long)step + 1); + } + return n; +} + +static PyObject * +builtin_range(PyObject *self, PyObject *args) +{ + long ilow = 0, ihigh = 0, istep = 1; + long bign; + int i, n; + + PyObject *v; + + if (PyTuple_Size(args) <= 1) { + if (!PyArg_ParseTuple(args, + "l;range() requires 1-3 int arguments", + &ihigh)) { + PyErr_Clear(); + return handle_range_longs(self, args); + } + } + else { + if (!PyArg_ParseTuple(args, + "ll|l;range() requires 1-3 int arguments", + &ilow, &ihigh, &istep)) { + PyErr_Clear(); + return handle_range_longs(self, args); + } + } + if (istep == 0) { + PyErr_SetString(PyExc_ValueError, + "range() step argument must not be zero"); + return NULL; + } + if (istep > 0) + bign = get_len_of_range(ilow, ihigh, istep); + else + bign = get_len_of_range(ihigh, ilow, -istep); + n = (int)bign; + if (bign < 0 || (long)n != bign) { + PyErr_SetString(PyExc_OverflowError, + "range() result has too many items"); + return NULL; + } + v = PyList_New(n); + if (v == NULL) + return NULL; + for (i = 0; i < n; i++) { + PyObject *w = PyInt_FromLong(ilow); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SET_ITEM(v, i, w); + ilow += istep; + } + return v; +} + +PyDoc_STRVAR(range_doc, +"range([start,] stop[, step]) -> list of integers\n\ +\n\ +Return a list containing an arithmetic progression of integers.\n\ +range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.\n\ +When step is given, it specifies the increment (or decrement).\n\ +For example, range(4) returns [0, 1, 2, 3]. The end point is omitted!\n\ +These are exactly the valid indices for a list of 4 elements."); + + +static PyObject * +builtin_raw_input(PyObject *self, PyObject *args) +{ + PyObject *v = NULL; + PyObject *fin = PySys_GetObject("stdin"); + PyObject *fout = PySys_GetObject("stdout"); + + if (!PyArg_UnpackTuple(args, "[raw_]input", 0, 1, &v)) + return NULL; + + if (fin == NULL) { + PyErr_SetString(PyExc_RuntimeError, "[raw_]input: lost sys.stdin"); + return NULL; + } + if (fout == NULL) { + PyErr_SetString(PyExc_RuntimeError, "[raw_]input: lost sys.stdout"); + return NULL; + } + if (PyFile_SoftSpace(fout, 0)) { + if (PyFile_WriteString(" ", fout) != 0) + return NULL; + } + if (PyFile_Check (fin) && PyFile_Check (fout) + && isatty(fileno(PyFile_AsFile(fin))) + && isatty(fileno(PyFile_AsFile(fout)))) { + PyObject *po; + char *prompt; + char *s; + PyObject *result; + if (v != NULL) { + po = PyObject_Str(v); + if (po == NULL) + return NULL; + prompt = PyString_AsString(po); + if (prompt == NULL) + return NULL; + } + else { + po = NULL; + prompt = ""; + } + s = PyOS_Readline(PyFile_AsFile (fin), PyFile_AsFile (fout), + prompt); + Py_XDECREF(po); + if (s == NULL) { + PyErr_SetNone(PyExc_KeyboardInterrupt); + return NULL; + } + if (*s == '\0') { + PyErr_SetNone(PyExc_EOFError); + result = NULL; + } + else { /* strip trailing '\n' */ + size_t len = strlen(s); + if (len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "[raw_]input: input too long"); + result = NULL; + } + else { + result = PyString_FromStringAndSize(s, + (int)(len-1)); + } + } + PyMem_FREE(s); + return result; + } + if (v != NULL) { + if (PyFile_WriteObject(v, fout, Py_PRINT_RAW) != 0) + return NULL; + } + return PyFile_GetLine(fin, -1); +} + +PyDoc_STRVAR(raw_input_doc, +"raw_input([prompt]) -> string\n\ +\n\ +Read a string from standard input. The trailing newline is stripped.\n\ +If the user hits EOF (Unix: Ctl-D, Windows: Ctl-Z+Return), raise EOFError.\n\ +On Unix, GNU readline is used if enabled. The prompt string, if given,\n\ +is printed without a trailing newline before reading."); + + +static PyObject * +builtin_reduce(PyObject *self, PyObject *args) +{ + PyObject *seq, *func, *result = NULL, *it; + + if (!PyArg_UnpackTuple(args, "reduce", 2, 3, &func, &seq, &result)) + return NULL; + if (result != NULL) + Py_INCREF(result); + + it = PyObject_GetIter(seq); + if (it == NULL) { + PyErr_SetString(PyExc_TypeError, + "reduce() arg 2 must support iteration"); + Py_XDECREF(result); + return NULL; + } + + if ((args = PyTuple_New(2)) == NULL) + goto Fail; + + for (;;) { + PyObject *op2; + + if (args->ob_refcnt > 1) { + Py_DECREF(args); + if ((args = PyTuple_New(2)) == NULL) + goto Fail; + } + + op2 = PyIter_Next(it); + if (op2 == NULL) { + if (PyErr_Occurred()) + goto Fail; + break; + } + + if (result == NULL) + result = op2; + else { + PyTuple_SetItem(args, 0, result); + PyTuple_SetItem(args, 1, op2); + if ((result = PyEval_CallObject(func, args)) == NULL) + goto Fail; + } + } + + Py_DECREF(args); + + if (result == NULL) + PyErr_SetString(PyExc_TypeError, + "reduce() of empty sequence with no initial value"); + + Py_DECREF(it); + return result; + +Fail: + Py_XDECREF(args); + Py_XDECREF(result); + Py_DECREF(it); + return NULL; +} + +PyDoc_STRVAR(reduce_doc, +"reduce(function, sequence[, initial]) -> value\n\ +\n\ +Apply a function of two arguments cumulatively to the items of a sequence,\n\ +from left to right, so as to reduce the sequence to a single value.\n\ +For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\ +((((1+2)+3)+4)+5). If initial is present, it is placed before the items\n\ +of the sequence in the calculation, and serves as a default when the\n\ +sequence is empty."); + + +static PyObject * +builtin_reload(PyObject *self, PyObject *v) +{ + return PyImport_ReloadModule(v); +} + +PyDoc_STRVAR(reload_doc, +"reload(module) -> module\n\ +\n\ +Reload the module. The module must have been successfully imported before."); + + +static PyObject * +builtin_repr(PyObject *self, PyObject *v) +{ + return PyObject_Repr(v); +} + +PyDoc_STRVAR(repr_doc, +"repr(object) -> string\n\ +\n\ +Return the canonical string representation of the object.\n\ +For most object types, eval(repr(object)) == object."); + + +static PyObject * +builtin_round(PyObject *self, PyObject *args) +{ + double x; + double f; + int ndigits = 0; + int i; + + if (!PyArg_ParseTuple(args, "d|i:round", &x, &ndigits)) + return NULL; + f = 1.0; + i = abs(ndigits); + while (--i >= 0) + f = f*10.0; + if (ndigits < 0) + x /= f; + else + x *= f; + if (x >= 0.0) + x = floor(x + 0.5); + else + x = ceil(x - 0.5); + if (ndigits < 0) + x *= f; + else + x /= f; + return PyFloat_FromDouble(x); +} + +PyDoc_STRVAR(round_doc, +"round(number[, ndigits]) -> floating point number\n\ +\n\ +Round a number to a given precision in decimal digits (default 0 digits).\n\ +This always returns a floating point number. Precision may be negative."); + + +static PyObject * +builtin_vars(PyObject *self, PyObject *args) +{ + PyObject *v = NULL; + PyObject *d; + + if (!PyArg_UnpackTuple(args, "vars", 0, 1, &v)) + return NULL; + if (v == NULL) { + d = PyEval_GetLocals(); + if (d == NULL) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_SystemError, + "vars(): no locals!?"); + } + else + Py_INCREF(d); + } + else { + d = PyObject_GetAttrString(v, "__dict__"); + if (d == NULL) { + PyErr_SetString(PyExc_TypeError, + "vars() argument must have __dict__ attribute"); + return NULL; + } + } + return d; +} + +PyDoc_STRVAR(vars_doc, +"vars([object]) -> dictionary\n\ +\n\ +Without arguments, equivalent to locals().\n\ +With an argument, equivalent to object.__dict__."); + + +static PyObject* +builtin_sum(PyObject *self, PyObject *args) +{ + PyObject *seq; + PyObject *result = NULL; + PyObject *temp, *item, *iter; + + if (!PyArg_ParseTuple(args, "O|O:sum", &seq, &result)) + return NULL; + + iter = PyObject_GetIter(seq); + if (iter == NULL) + return NULL; + + if (result == NULL) { + result = PyInt_FromLong(0); + if (result == NULL) { + Py_DECREF(iter); + return NULL; + } + } else { + /* reject string values for 'start' parameter */ + if (PyObject_TypeCheck(result, &PyBaseString_Type)) { + PyErr_SetString(PyExc_TypeError, + "sum() can't sum strings [use ''.join(seq) instead]"); + Py_DECREF(iter); + return NULL; + } + Py_INCREF(result); + } + + for(;;) { + item = PyIter_Next(iter); + if (item == NULL) { + /* error, or end-of-sequence */ + if (PyErr_Occurred()) { + Py_DECREF(result); + result = NULL; + } + break; + } + temp = PyNumber_Add(result, item); + Py_DECREF(result); + Py_DECREF(item); + result = temp; + if (result == NULL) + break; + } + Py_DECREF(iter); + return result; +} + +PyDoc_STRVAR(sum_doc, +"sum(sequence, start=0) -> value\n\ +\n\ +Returns the sum of a sequence of numbers (NOT strings) plus the value\n\ +of parameter 'start'. When the sequence is empty, returns start."); + + +static PyObject * +builtin_isinstance(PyObject *self, PyObject *args) +{ + PyObject *inst; + PyObject *cls; + int retval; + + if (!PyArg_UnpackTuple(args, "isinstance", 2, 2, &inst, &cls)) + return NULL; + + retval = PyObject_IsInstance(inst, cls); + if (retval < 0) + return NULL; + return PyBool_FromLong(retval); +} + +PyDoc_STRVAR(isinstance_doc, +"isinstance(object, class-or-type-or-tuple) -> bool\n\ +\n\ +Return whether an object is an instance of a class or of a subclass thereof.\n\ +With a type as second argument, return whether that is the object's type.\n\ +The form using a tuple, isinstance(x, (A, B, ...)), is a shortcut for\n\ +isinstance(x, A) or isinstance(x, B) or ... (etc.)."); + + +static PyObject * +builtin_issubclass(PyObject *self, PyObject *args) +{ + PyObject *derived; + PyObject *cls; + int retval; + + if (!PyArg_UnpackTuple(args, "issubclass", 2, 2, &derived, &cls)) + return NULL; + + retval = PyObject_IsSubclass(derived, cls); + if (retval < 0) + return NULL; + return PyBool_FromLong(retval); +} + +PyDoc_STRVAR(issubclass_doc, +"issubclass(C, B) -> bool\n\ +\n\ +Return whether class C is a subclass (i.e., a derived class) of class B.\n\ +When using a tuple as the second argument issubclass(X, (A, B, ...)),\n\ +is a shortcut for issubclass(X, A) or issubclass(X, B) or ... (etc.)."); + + +static PyObject* +builtin_zip(PyObject *self, PyObject *args) +{ + PyObject *ret; + const int itemsize = PySequence_Length(args); + int i; + PyObject *itlist; /* tuple of iterators */ + int len; /* guess at result length */ + + if (itemsize < 1) { + PyErr_SetString(PyExc_TypeError, + "zip() requires at least one sequence"); + return NULL; + } + /* args must be a tuple */ + assert(PyTuple_Check(args)); + + /* Guess at result length: the shortest of the input lengths. + If some argument refuses to say, we refuse to guess too, lest + an argument like xrange(sys.maxint) lead us astray.*/ + len = -1; /* unknown */ + for (i = 0; i < itemsize; ++i) { + PyObject *item = PyTuple_GET_ITEM(args, i); + int thislen = PySequence_Length(item); + if (thislen < 0) { + PyErr_Clear(); + len = -1; + break; + } + else if (len < 0 || thislen < len) + len = thislen; + } + + /* allocate result list */ + if (len < 0) + len = 10; /* arbitrary */ + if ((ret = PyList_New(len)) == NULL) + return NULL; + + /* obtain iterators */ + itlist = PyTuple_New(itemsize); + if (itlist == NULL) + goto Fail_ret; + for (i = 0; i < itemsize; ++i) { + PyObject *item = PyTuple_GET_ITEM(args, i); + PyObject *it = PyObject_GetIter(item); + if (it == NULL) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + PyErr_Format(PyExc_TypeError, + "zip argument #%d must support iteration", + i+1); + goto Fail_ret_itlist; + } + PyTuple_SET_ITEM(itlist, i, it); + } + + /* build result into ret list */ + for (i = 0; ; ++i) { + int j; + PyObject *next = PyTuple_New(itemsize); + if (!next) + goto Fail_ret_itlist; + + for (j = 0; j < itemsize; j++) { + PyObject *it = PyTuple_GET_ITEM(itlist, j); + PyObject *item = PyIter_Next(it); + if (!item) { + if (PyErr_Occurred()) { + Py_DECREF(ret); + ret = NULL; + } + Py_DECREF(next); + Py_DECREF(itlist); + goto Done; + } + PyTuple_SET_ITEM(next, j, item); + } + + if (i < len) + PyList_SET_ITEM(ret, i, next); + else { + int status = PyList_Append(ret, next); + Py_DECREF(next); + ++len; + if (status < 0) + goto Fail_ret_itlist; + } + } + +Done: + if (ret != NULL && i < len) { + /* The list is too big. */ + if (PyList_SetSlice(ret, i, len, NULL) < 0) + return NULL; + } + return ret; + +Fail_ret_itlist: + Py_DECREF(itlist); +Fail_ret: + Py_DECREF(ret); + return NULL; +} + + +PyDoc_STRVAR(zip_doc, +"zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]\n\ +\n\ +Return a list of tuples, where each tuple contains the i-th element\n\ +from each of the argument sequences. The returned list is truncated\n\ +in length to the length of the shortest argument sequence."); + + +static PyMethodDef builtin_methods[] = { + {"__import__", builtin___import__, METH_VARARGS, import_doc}, + {"abs", builtin_abs, METH_O, abs_doc}, + {"apply", builtin_apply, METH_VARARGS, apply_doc}, + {"callable", builtin_callable, METH_O, callable_doc}, + {"chr", builtin_chr, METH_VARARGS, chr_doc}, + {"cmp", builtin_cmp, METH_VARARGS, cmp_doc}, + {"coerce", builtin_coerce, METH_VARARGS, coerce_doc}, + {"compile", builtin_compile, METH_VARARGS, compile_doc}, + {"delattr", builtin_delattr, METH_VARARGS, delattr_doc}, + {"dir", builtin_dir, METH_VARARGS, dir_doc}, + {"divmod", builtin_divmod, METH_VARARGS, divmod_doc}, + {"eval", builtin_eval, METH_VARARGS, eval_doc}, + {"execfile", builtin_execfile, METH_VARARGS, execfile_doc}, + {"filter", builtin_filter, METH_VARARGS, filter_doc}, + {"getattr", builtin_getattr, METH_VARARGS, getattr_doc}, + {"globals", (PyCFunction)builtin_globals, METH_NOARGS, globals_doc}, + {"hasattr", builtin_hasattr, METH_VARARGS, hasattr_doc}, + {"hash", builtin_hash, METH_O, hash_doc}, + {"hex", builtin_hex, METH_O, hex_doc}, + {"id", builtin_id, METH_O, id_doc}, + {"input", builtin_input, METH_VARARGS, input_doc}, + {"intern", builtin_intern, METH_VARARGS, intern_doc}, + {"isinstance", builtin_isinstance, METH_VARARGS, isinstance_doc}, + {"issubclass", builtin_issubclass, METH_VARARGS, issubclass_doc}, + {"iter", builtin_iter, METH_VARARGS, iter_doc}, + {"len", builtin_len, METH_O, len_doc}, + {"locals", (PyCFunction)builtin_locals, METH_NOARGS, locals_doc}, + {"map", builtin_map, METH_VARARGS, map_doc}, + {"max", builtin_max, METH_VARARGS, max_doc}, + {"min", builtin_min, METH_VARARGS, min_doc}, + {"oct", builtin_oct, METH_O, oct_doc}, + {"ord", builtin_ord, METH_O, ord_doc}, + {"pow", builtin_pow, METH_VARARGS, pow_doc}, + {"range", builtin_range, METH_VARARGS, range_doc}, + {"raw_input", builtin_raw_input, METH_VARARGS, raw_input_doc}, + {"reduce", builtin_reduce, METH_VARARGS, reduce_doc}, + {"reload", builtin_reload, METH_O, reload_doc}, + {"repr", builtin_repr, METH_O, repr_doc}, + {"round", builtin_round, METH_VARARGS, round_doc}, + {"setattr", builtin_setattr, METH_VARARGS, setattr_doc}, + {"sum", builtin_sum, METH_VARARGS, sum_doc}, +#ifdef Py_USING_UNICODE + {"unichr", builtin_unichr, METH_VARARGS, unichr_doc}, +#endif + {"vars", builtin_vars, METH_VARARGS, vars_doc}, + {"zip", builtin_zip, METH_VARARGS, zip_doc}, + {NULL, NULL}, +}; + +PyDoc_STRVAR(builtin_doc, +"Built-in functions, exceptions, and other objects.\n\ +\n\ +Noteworthy: None is the `nil' object; Ellipsis represents `...' in slices."); + +PyObject * +_PyBuiltin_Init(void) +{ + PyObject *mod, *dict, *debug; + mod = Py_InitModule4("__builtin__", builtin_methods, + builtin_doc, (PyObject *)NULL, + PYTHON_API_VERSION); + if (mod == NULL) + return NULL; + dict = PyModule_GetDict(mod); + +#ifdef Py_TRACE_REFS + /* __builtin__ exposes a number of statically allocated objects + * that, before this code was added in 2.3, never showed up in + * the list of "all objects" maintained by Py_TRACE_REFS. As a + * result, programs leaking references to None and False (etc) + * couldn't be diagnosed by examining sys.getobjects(0). + */ +#define ADD_TO_ALL(OBJECT) _Py_AddToAllObjects((PyObject *)(OBJECT), 0) +#else +#define ADD_TO_ALL(OBJECT) (void)0 +#endif + +#define SETBUILTIN(NAME, OBJECT) \ + if (PyDict_SetItemString(dict, NAME, (PyObject *)OBJECT) < 0) \ + return NULL; \ + ADD_TO_ALL(OBJECT) + + SETBUILTIN("None", Py_None); + SETBUILTIN("Ellipsis", Py_Ellipsis); + SETBUILTIN("NotImplemented", Py_NotImplemented); + SETBUILTIN("False", Py_False); + SETBUILTIN("True", Py_True); + SETBUILTIN("basestring", &PyBaseString_Type); + SETBUILTIN("bool", &PyBool_Type); + SETBUILTIN("buffer", &PyBuffer_Type); + SETBUILTIN("classmethod", &PyClassMethod_Type); +#ifndef WITHOUT_COMPLEX + SETBUILTIN("complex", &PyComplex_Type); +#endif + SETBUILTIN("dict", &PyDict_Type); + SETBUILTIN("enumerate", &PyEnum_Type); + SETBUILTIN("float", &PyFloat_Type); + SETBUILTIN("property", &PyProperty_Type); + SETBUILTIN("int", &PyInt_Type); + SETBUILTIN("list", &PyList_Type); + SETBUILTIN("long", &PyLong_Type); + SETBUILTIN("object", &PyBaseObject_Type); + SETBUILTIN("slice", &PySlice_Type); + SETBUILTIN("staticmethod", &PyStaticMethod_Type); + SETBUILTIN("str", &PyString_Type); + SETBUILTIN("super", &PySuper_Type); + SETBUILTIN("tuple", &PyTuple_Type); + SETBUILTIN("type", &PyType_Type); + SETBUILTIN("xrange", &PyRange_Type); + + /* Note that open() is just an alias of file(). */ + SETBUILTIN("open", &PyFile_Type); + SETBUILTIN("file", &PyFile_Type); +#ifdef Py_USING_UNICODE + SETBUILTIN("unicode", &PyUnicode_Type); +#endif + debug = PyBool_FromLong(Py_OptimizeFlag == 0); + if (PyDict_SetItemString(dict, "__debug__", debug) < 0) { + Py_XDECREF(debug); + return NULL; + } + Py_XDECREF(debug); + + return mod; +#undef ADD_TO_ALL +#undef SETBUILTIN +} + +/* Helper for filter(): filter a tuple through a function */ + +static PyObject * +filtertuple(PyObject *func, PyObject *tuple) +{ + PyObject *result; + register int i, j; + int len = PyTuple_Size(tuple); + + if (len == 0) { + if (PyTuple_CheckExact(tuple)) + Py_INCREF(tuple); + else + tuple = PyTuple_New(0); + return tuple; + } + + if ((result = PyTuple_New(len)) == NULL) + return NULL; + + for (i = j = 0; i < len; ++i) { + PyObject *item, *good; + int ok; + + if (tuple->ob_type->tp_as_sequence && + tuple->ob_type->tp_as_sequence->sq_item) { + item = tuple->ob_type->tp_as_sequence->sq_item(tuple, i); + if (item == NULL) + goto Fail_1; + } else { + PyErr_SetString(PyExc_TypeError, "filter(): unsubscriptable tuple"); + goto Fail_1; + } + if (func == Py_None) { + Py_INCREF(item); + good = item; + } + else { + PyObject *arg = Py_BuildValue("(O)", item); + if (arg == NULL) { + Py_DECREF(item); + goto Fail_1; + } + good = PyEval_CallObject(func, arg); + Py_DECREF(arg); + if (good == NULL) { + Py_DECREF(item); + goto Fail_1; + } + } + ok = PyObject_IsTrue(good); + Py_DECREF(good); + if (ok) { + if (PyTuple_SetItem(result, j++, item) < 0) + goto Fail_1; + } + else + Py_DECREF(item); + } + + if (_PyTuple_Resize(&result, j) < 0) + return NULL; + + return result; + +Fail_1: + Py_DECREF(result); + return NULL; +} + + +/* Helper for filter(): filter a string through a function */ + +static PyObject * +filterstring(PyObject *func, PyObject *strobj) +{ + PyObject *result; + register int i, j; + int len = PyString_Size(strobj); + int outlen = len; + + if (func == Py_None) { + /* If it's a real string we can return the original, + * as no character is ever false and __getitem__ + * does return this character. If it's a subclass + * we must go through the __getitem__ loop */ + if (PyString_CheckExact(strobj)) { + Py_INCREF(strobj); + return strobj; + } + } + if ((result = PyString_FromStringAndSize(NULL, len)) == NULL) + return NULL; + + for (i = j = 0; i < len; ++i) { + PyObject *item; + int ok; + + item = (*strobj->ob_type->tp_as_sequence->sq_item)(strobj, i); + if (item == NULL) + goto Fail_1; + if (func==Py_None) { + ok = 1; + } else { + PyObject *arg, *good; + arg = Py_BuildValue("(O)", item); + if (arg == NULL) { + Py_DECREF(item); + goto Fail_1; + } + good = PyEval_CallObject(func, arg); + Py_DECREF(arg); + if (good == NULL) { + Py_DECREF(item); + goto Fail_1; + } + ok = PyObject_IsTrue(good); + Py_DECREF(good); + } + if (ok) { + int reslen; + if (!PyString_Check(item)) { + PyErr_SetString(PyExc_TypeError, "can't filter str to str:" + " __getitem__ returned different type"); + Py_DECREF(item); + goto Fail_1; + } + reslen = PyString_GET_SIZE(item); + if (reslen == 1) { + PyString_AS_STRING(result)[j++] = + PyString_AS_STRING(item)[0]; + } else { + /* do we need more space? */ + int need = j + reslen + len-i-1; + if (need > outlen) { + /* overallocate, to avoid reallocations */ + if (need<2*outlen) + need = 2*outlen; + if (_PyString_Resize(&result, need)) { + Py_DECREF(item); + return NULL; + } + outlen = need; + } + memcpy( + PyString_AS_STRING(result) + j, + PyString_AS_STRING(item), + reslen + ); + j += reslen; + } + } + Py_DECREF(item); + } + + if (j < outlen) + _PyString_Resize(&result, j); + + return result; + +Fail_1: + Py_DECREF(result); + return NULL; +} + +#ifdef Py_USING_UNICODE +/* Helper for filter(): filter a Unicode object through a function */ + +static PyObject * +filterunicode(PyObject *func, PyObject *strobj) +{ + PyObject *result; + register int i, j; + int len = PyUnicode_GetSize(strobj); + int outlen = len; + + if (func == Py_None) { + /* If it's a real string we can return the original, + * as no character is ever false and __getitem__ + * does return this character. If it's a subclass + * we must go through the __getitem__ loop */ + if (PyUnicode_CheckExact(strobj)) { + Py_INCREF(strobj); + return strobj; + } + } + if ((result = PyUnicode_FromUnicode(NULL, len)) == NULL) + return NULL; + + for (i = j = 0; i < len; ++i) { + PyObject *item, *arg, *good; + int ok; + + item = (*strobj->ob_type->tp_as_sequence->sq_item)(strobj, i); + if (item == NULL) + goto Fail_1; + if (func == Py_None) { + ok = 1; + } else { + arg = Py_BuildValue("(O)", item); + if (arg == NULL) { + Py_DECREF(item); + goto Fail_1; + } + good = PyEval_CallObject(func, arg); + Py_DECREF(arg); + if (good == NULL) { + Py_DECREF(item); + goto Fail_1; + } + ok = PyObject_IsTrue(good); + Py_DECREF(good); + } + if (ok) { + int reslen; + if (!PyUnicode_Check(item)) { + PyErr_SetString(PyExc_TypeError, "can't filter unicode to unicode:" + " __getitem__ returned different type"); + Py_DECREF(item); + goto Fail_1; + } + reslen = PyUnicode_GET_SIZE(item); + if (reslen == 1) { + PyUnicode_AS_UNICODE(result)[j++] = + PyUnicode_AS_UNICODE(item)[0]; + } else { + /* do we need more space? */ + int need = j + reslen + len-i-1; + if (need > outlen) { + /* overallocate, to avoid reallocations */ + if (need<2*outlen) + need = 2*outlen; + if (PyUnicode_Resize(&result, need)) { + Py_DECREF(item); + goto Fail_1; + } + outlen = need; + } + memcpy( + PyUnicode_AS_UNICODE(result) + j, + PyUnicode_AS_UNICODE(item), + reslen*sizeof(Py_UNICODE) + ); + j += reslen; + } + } + Py_DECREF(item); + } + + if (j < outlen) + PyUnicode_Resize(&result, j); + + return result; + +Fail_1: + Py_DECREF(result); + return NULL; +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/ceval.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/ceval.c new file mode 100644 index 00000000..6a865ab5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/ceval.c @@ -0,0 +1,4142 @@ + +/* Execute compiled code */ + +/* XXX TO DO: + XXX speed up searching for keywords by using a dictionary + XXX document it! + */ + +#include "Python.h" + +#include "compile.h" +#include "frameobject.h" +#include "eval.h" +#include "opcode.h" +#include "structmember.h" + +#ifdef macintosh +#include "macglue.h" +#endif + +#include + +/* Turn this on if your compiler chokes on the big switch: */ +/* #define CASE_TOO_BIG 1 */ + +#ifdef Py_DEBUG +/* For debugging the interpreter: */ +#define LLTRACE 1 /* Low-level trace feature */ +#define CHECKEXC 1 /* Double-check exception checking */ +#endif + +typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *); + +/* Forward declarations */ +static PyObject *eval_frame(PyFrameObject *); +static PyObject *call_function(PyObject ***, int); +static PyObject *fast_function(PyObject *, PyObject ***, int, int, int); +static PyObject *do_call(PyObject *, PyObject ***, int, int); +static PyObject *ext_do_call(PyObject *, PyObject ***, int, int, int); +static PyObject *update_keyword_args(PyObject *, int, PyObject ***,PyObject *); +static PyObject *update_star_args(int, int, PyObject *, PyObject ***); +static PyObject *load_args(PyObject ***, int); +#define CALL_FLAG_VAR 1 +#define CALL_FLAG_KW 2 + +#ifdef LLTRACE +static int prtrace(PyObject *, char *); +#endif +static int call_trace(Py_tracefunc, PyObject *, PyFrameObject *, + int, PyObject *); +static void call_trace_protected(Py_tracefunc, PyObject *, + PyFrameObject *, int); +static void call_exc_trace(Py_tracefunc, PyObject *, PyFrameObject *); +static int maybe_call_line_trace(Py_tracefunc, PyObject *, + PyFrameObject *, int *, int *); + +static PyObject *apply_slice(PyObject *, PyObject *, PyObject *); +static int assign_slice(PyObject *, PyObject *, + PyObject *, PyObject *); +static PyObject *cmp_outcome(int, PyObject *, PyObject *); +static PyObject *import_from(PyObject *, PyObject *); +static int import_all_from(PyObject *, PyObject *); +static PyObject *build_class(PyObject *, PyObject *, PyObject *); +static int exec_statement(PyFrameObject *, + PyObject *, PyObject *, PyObject *); +static void set_exc_info(PyThreadState *, PyObject *, PyObject *, PyObject *); +static void reset_exc_info(PyThreadState *); +static void format_exc_check_arg(PyObject *, char *, PyObject *); + +#define NAME_ERROR_MSG \ + "name '%.200s' is not defined" +#define GLOBAL_NAME_ERROR_MSG \ + "global name '%.200s' is not defined" +#define UNBOUNDLOCAL_ERROR_MSG \ + "local variable '%.200s' referenced before assignment" +#define UNBOUNDFREE_ERROR_MSG \ + "free variable '%.200s' referenced before assignment" \ + " in enclosing scope" + +/* Dynamic execution profile */ +#ifdef DYNAMIC_EXECUTION_PROFILE +#ifdef DXPAIRS +static long dxpairs[257][256]; +#define dxp dxpairs[256] +#else +static long dxp[256]; +#endif +#endif + +/* Function call profile */ +#ifdef CALL_PROFILE +#define PCALL_NUM 11 +static int pcall[PCALL_NUM]; + +#define PCALL_ALL 0 +#define PCALL_FUNCTION 1 +#define PCALL_FAST_FUNCTION 2 +#define PCALL_FASTER_FUNCTION 3 +#define PCALL_METHOD 4 +#define PCALL_BOUND_METHOD 5 +#define PCALL_CFUNCTION 6 +#define PCALL_TYPE 7 +#define PCALL_GENERATOR 8 +#define PCALL_OTHER 9 +#define PCALL_POP 10 + +/* Notes about the statistics + + PCALL_FAST stats + + FAST_FUNCTION means no argument tuple needs to be created. + FASTER_FUNCTION means that the fast-path frame setup code is used. + + If there is a method call where the call can be optimized by changing + the argument tuple and calling the function directly, it gets recorded + twice. + + As a result, the relationship among the statistics appears to be + PCALL_ALL == PCALL_FUNCTION + PCALL_METHOD - PCALL_BOUND_METHOD + + PCALL_CFUNCTION + PCALL_TYPE + PCALL_GENERATOR + PCALL_OTHER + PCALL_FUNCTION > PCALL_FAST_FUNCTION > PCALL_FASTER_FUNCTION + PCALL_METHOD > PCALL_BOUND_METHOD +*/ + +#define PCALL(POS) pcall[POS]++ + +PyObject * +PyEval_GetCallStats(PyObject *self) +{ + return Py_BuildValue("iiiiiiiiii", + pcall[0], pcall[1], pcall[2], pcall[3], + pcall[4], pcall[5], pcall[6], pcall[7], + pcall[8], pcall[9]); +} +#else +#define PCALL(O) + +PyObject * +PyEval_GetCallStats(PyObject *self) +{ + Py_INCREF(Py_None); + return Py_None; +} +#endif + +static PyTypeObject gentype; + +typedef struct { + PyObject_HEAD + /* The gi_ prefix is intended to remind of generator-iterator. */ + + PyFrameObject *gi_frame; + + /* True if generator is being executed. */ + int gi_running; + + /* List of weak reference. */ + PyObject *gi_weakreflist; +} genobject; + +static PyObject * +gen_new(PyFrameObject *f) +{ + genobject *gen = PyObject_GC_New(genobject, &gentype); + if (gen == NULL) { + Py_DECREF(f); + return NULL; + } + gen->gi_frame = f; + gen->gi_running = 0; + gen->gi_weakreflist = NULL; + _PyObject_GC_TRACK(gen); + return (PyObject *)gen; +} + +static int +gen_traverse(genobject *gen, visitproc visit, void *arg) +{ + return visit((PyObject *)gen->gi_frame, arg); +} + +static void +gen_dealloc(genobject *gen) +{ + _PyObject_GC_UNTRACK(gen); + if (gen->gi_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) gen); + Py_DECREF(gen->gi_frame); + PyObject_GC_Del(gen); +} + +static PyObject * +gen_iternext(genobject *gen) +{ + PyThreadState *tstate = PyThreadState_GET(); + PyFrameObject *f = gen->gi_frame; + PyObject *result; + + if (gen->gi_running) { + PyErr_SetString(PyExc_ValueError, + "generator already executing"); + return NULL; + } + if (f->f_stacktop == NULL) + return NULL; + + /* Generators always return to their most recent caller, not + * necessarily their creator. */ + Py_XINCREF(tstate->frame); + assert(f->f_back == NULL); + f->f_back = tstate->frame; + + gen->gi_running = 1; + result = eval_frame(f); + gen->gi_running = 0; + + /* Don't keep the reference to f_back any longer than necessary. It + * may keep a chain of frames alive or it could create a reference + * cycle. */ + Py_XDECREF(f->f_back); + f->f_back = NULL; + + /* If the generator just returned (as opposed to yielding), signal + * that the generator is exhausted. */ + if (result == Py_None && f->f_stacktop == NULL) { + Py_DECREF(result); + result = NULL; + } + + return result; +} + +static PyObject * +gen_getiter(PyObject *gen) +{ + Py_INCREF(gen); + return gen; +} + +static PyMemberDef gen_memberlist[] = { + {"gi_frame", T_OBJECT, offsetof(genobject, gi_frame), RO}, + {"gi_running", T_INT, offsetof(genobject, gi_running), RO}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject gentype = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "generator", /* tp_name */ + sizeof(genobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)gen_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)gen_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(genobject, gi_weakreflist), /* tp_weaklistoffset */ + (getiterfunc)gen_getiter, /* tp_iter */ + (iternextfunc)gen_iternext, /* tp_iternext */ + 0, /* tp_methods */ + gen_memberlist, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ +}; + + +#ifdef WITH_THREAD + +#ifndef DONT_HAVE_ERRNO_H +#include +#endif +#include "pythread.h" + +extern int _PyThread_Started; /* Flag for Py_Exit */ + +static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */ +static long main_thread = 0; + +void +PyEval_InitThreads(void) +{ + if (interpreter_lock) + return; + _PyThread_Started = 1; + interpreter_lock = PyThread_allocate_lock(); + PyThread_acquire_lock(interpreter_lock, 1); + main_thread = PyThread_get_thread_ident(); +} + +void +PyEval_AcquireLock(void) +{ + PyThread_acquire_lock(interpreter_lock, 1); +} + +void +PyEval_ReleaseLock(void) +{ + PyThread_release_lock(interpreter_lock); +} + +void +PyEval_AcquireThread(PyThreadState *tstate) +{ + if (tstate == NULL) + Py_FatalError("PyEval_AcquireThread: NULL new thread state"); + /* Check someone has called PyEval_InitThreads() to create the lock */ + assert(interpreter_lock); + PyThread_acquire_lock(interpreter_lock, 1); + if (PyThreadState_Swap(tstate) != NULL) + Py_FatalError( + "PyEval_AcquireThread: non-NULL old thread state"); +} + +void +PyEval_ReleaseThread(PyThreadState *tstate) +{ + if (tstate == NULL) + Py_FatalError("PyEval_ReleaseThread: NULL thread state"); + if (PyThreadState_Swap(NULL) != tstate) + Py_FatalError("PyEval_ReleaseThread: wrong thread state"); + PyThread_release_lock(interpreter_lock); +} + +/* This function is called from PyOS_AfterFork to ensure that newly + created child processes don't hold locks referring to threads which + are not running in the child process. (This could also be done using + pthread_atfork mechanism, at least for the pthreads implementation.) */ + +void +PyEval_ReInitThreads(void) +{ + if (!interpreter_lock) + return; + /*XXX Can't use PyThread_free_lock here because it does too + much error-checking. Doing this cleanly would require + adding a new function to each thread_*.h. Instead, just + create a new lock and waste a little bit of memory */ + interpreter_lock = PyThread_allocate_lock(); + PyThread_acquire_lock(interpreter_lock, 1); + main_thread = PyThread_get_thread_ident(); +} +#endif + +/* Functions save_thread and restore_thread are always defined so + dynamically loaded modules needn't be compiled separately for use + with and without threads: */ + +PyThreadState * +PyEval_SaveThread(void) +{ + PyThreadState *tstate = PyThreadState_Swap(NULL); + if (tstate == NULL) + Py_FatalError("PyEval_SaveThread: NULL tstate"); +#ifdef WITH_THREAD + if (interpreter_lock) + PyThread_release_lock(interpreter_lock); +#endif + return tstate; +} + +void +PyEval_RestoreThread(PyThreadState *tstate) +{ + if (tstate == NULL) + Py_FatalError("PyEval_RestoreThread: NULL tstate"); +#ifdef WITH_THREAD + if (interpreter_lock) { + int err = errno; + PyThread_acquire_lock(interpreter_lock, 1); + errno = err; + } +#endif + PyThreadState_Swap(tstate); +} + + +/* Mechanism whereby asynchronously executing callbacks (e.g. UNIX + signal handlers or Mac I/O completion routines) can schedule calls + to a function to be called synchronously. + The synchronous function is called with one void* argument. + It should return 0 for success or -1 for failure -- failure should + be accompanied by an exception. + + If registry succeeds, the registry function returns 0; if it fails + (e.g. due to too many pending calls) it returns -1 (without setting + an exception condition). + + Note that because registry may occur from within signal handlers, + or other asynchronous events, calling malloc() is unsafe! + +#ifdef WITH_THREAD + Any thread can schedule pending calls, but only the main thread + will execute them. +#endif + + XXX WARNING! ASYNCHRONOUSLY EXECUTING CODE! + There are two possible race conditions: + (1) nested asynchronous registry calls; + (2) registry calls made while pending calls are being processed. + While (1) is very unlikely, (2) is a real possibility. + The current code is safe against (2), but not against (1). + The safety against (2) is derived from the fact that only one + thread (the main thread) ever takes things out of the queue. + + XXX Darn! With the advent of thread state, we should have an array + of pending calls per thread in the thread state! Later... +*/ + +#define NPENDINGCALLS 32 +static struct { + int (*func)(void *); + void *arg; +} pendingcalls[NPENDINGCALLS]; +static volatile int pendingfirst = 0; +static volatile int pendinglast = 0; +static volatile int things_to_do = 0; + +int +Py_AddPendingCall(int (*func)(void *), void *arg) +{ + static int busy = 0; + int i, j; + /* XXX Begin critical section */ + /* XXX If you want this to be safe against nested + XXX asynchronous calls, you'll have to work harder! */ + if (busy) + return -1; + busy = 1; + i = pendinglast; + j = (i + 1) % NPENDINGCALLS; + if (j == pendingfirst) { + busy = 0; + return -1; /* Queue full */ + } + pendingcalls[i].func = func; + pendingcalls[i].arg = arg; + pendinglast = j; + + _Py_Ticker = 0; + things_to_do = 1; /* Signal main loop */ + busy = 0; + /* XXX End critical section */ + return 0; +} + +int +Py_MakePendingCalls(void) +{ + static int busy = 0; +#ifdef WITH_THREAD + if (main_thread && PyThread_get_thread_ident() != main_thread) + return 0; +#endif + if (busy) + return 0; + busy = 1; + things_to_do = 0; + for (;;) { + int i; + int (*func)(void *); + void *arg; + i = pendingfirst; + if (i == pendinglast) + break; /* Queue empty */ + func = pendingcalls[i].func; + arg = pendingcalls[i].arg; + pendingfirst = (i + 1) % NPENDINGCALLS; + if (func(arg) < 0) { + busy = 0; + things_to_do = 1; /* We're not done yet */ + return -1; + } + } + busy = 0; + return 0; +} + + +/* The interpreter's recursion limit */ + +static int recursion_limit = 1000; + +int +Py_GetRecursionLimit(void) +{ + return recursion_limit; +} + +void +Py_SetRecursionLimit(int new_limit) +{ + recursion_limit = new_limit; +} + +/* Status code for main loop (reason for stack unwind) */ + +enum why_code { + WHY_NOT, /* No error */ + WHY_EXCEPTION, /* Exception occurred */ + WHY_RERAISE, /* Exception re-raised by 'finally' */ + WHY_RETURN, /* 'return' statement */ + WHY_BREAK, /* 'break' statement */ + WHY_CONTINUE, /* 'continue' statement */ + WHY_YIELD /* 'yield' operator */ +}; + +static enum why_code do_raise(PyObject *, PyObject *, PyObject *); +static int unpack_iterable(PyObject *, int, PyObject **); + +/* for manipulating the thread switch and periodic "stuff" - used to be + per thread, now just a pair o' globals */ +int _Py_CheckInterval = 100; +volatile int _Py_Ticker = 100; + +PyObject * +PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals) +{ + /* XXX raise SystemError if globals is NULL */ + return PyEval_EvalCodeEx(co, + globals, locals, + (PyObject **)NULL, 0, + (PyObject **)NULL, 0, + (PyObject **)NULL, 0, + NULL); +} + + +/* Interpreter main loop */ + +static PyObject * +eval_frame(PyFrameObject *f) +{ +#ifdef DXPAIRS + int lastopcode = 0; +#endif + PyObject **stack_pointer; /* Next free slot in value stack */ + register unsigned char *next_instr; + register int opcode=0; /* Current opcode */ + register int oparg=0; /* Current opcode argument, if any */ + register enum why_code why; /* Reason for block stack unwind */ + register int err; /* Error status -- nonzero if error */ + register PyObject *x; /* Result object -- NULL if error */ + register PyObject *v; /* Temporary objects popped off stack */ + register PyObject *w; + register PyObject *u; + register PyObject *t; + register PyObject *stream = NULL; /* for PRINT opcodes */ + register PyObject **fastlocals, **freevars; + PyObject *retval = NULL; /* Return value */ + PyThreadState *tstate = PyThreadState_GET(); + PyCodeObject *co; + + /* when tracing we set things up so that + + not (instr_lb <= current_bytecode_offset < instr_ub) + + is true when the line being executed has changed. The + initial values are such as to make this false the first + time it is tested. */ + int instr_ub = -1, instr_lb = 0; + + unsigned char *first_instr; + PyObject *names; + PyObject *consts; +#ifdef LLTRACE + int lltrace; +#endif +#if defined(Py_DEBUG) || defined(LLTRACE) + /* Make it easier to find out where we are with a debugger */ + char *filename; +#endif + +/* Tuple access macros */ + +#ifndef Py_DEBUG +#define GETITEM(v, i) PyTuple_GET_ITEM((PyTupleObject *)(v), (i)) +#else +#define GETITEM(v, i) PyTuple_GetItem((v), (i)) +#endif + +/* Code access macros */ + +#define INSTR_OFFSET() (next_instr - first_instr) +#define NEXTOP() (*next_instr++) +#define NEXTARG() (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2]) +#define JUMPTO(x) (next_instr = first_instr + (x)) +#define JUMPBY(x) (next_instr += (x)) + +/* OpCode prediction macros + Some opcodes tend to come in pairs thus making it possible to predict + the second code when the first is run. For example, COMPARE_OP is often + followed by JUMP_IF_FALSE or JUMP_IF_TRUE. And, those opcodes are often + followed by a POP_TOP. + + Verifying the prediction costs a single high-speed test of register + variable against a constant. If the pairing was good, then the + processor has a high likelihood of making its own successful branch + prediction which results in a nearly zero overhead transition to the + next opcode. + + A successful prediction saves a trip through the eval-loop including + its two unpredictable branches, the HASARG test and the switch-case. +*/ + +#define PREDICT(op) if (*next_instr == op) goto PRED_##op +#define PREDICTED(op) PRED_##op: next_instr++ +#define PREDICTED_WITH_ARG(op) PRED_##op: oparg = (next_instr[2]<<8) + \ + next_instr[1]; next_instr += 3 + +/* Stack manipulation macros */ + +#define STACK_LEVEL() (stack_pointer - f->f_valuestack) +#define EMPTY() (STACK_LEVEL() == 0) +#define TOP() (stack_pointer[-1]) +#define SECOND() (stack_pointer[-2]) +#define THIRD() (stack_pointer[-3]) +#define FOURTH() (stack_pointer[-4]) +#define SET_TOP(v) (stack_pointer[-1] = (v)) +#define SET_SECOND(v) (stack_pointer[-2] = (v)) +#define SET_THIRD(v) (stack_pointer[-3] = (v)) +#define SET_FOURTH(v) (stack_pointer[-4] = (v)) +#define BASIC_STACKADJ(n) (stack_pointer += n) +#define BASIC_PUSH(v) (*stack_pointer++ = (v)) +#define BASIC_POP() (*--stack_pointer) + +#ifdef LLTRACE +#define PUSH(v) { (void)(BASIC_PUSH(v), \ + lltrace && prtrace(TOP(), "push")); \ + assert(STACK_LEVEL() <= f->f_stacksize); } +#define POP() ((void)(lltrace && prtrace(TOP(), "pop")), BASIC_POP()) +#define STACKADJ(n) { (void)(BASIC_STACKADJ(n), \ + lltrace && prtrace(TOP(), "stackadj")); \ + assert(STACK_LEVEL() <= f->f_stacksize); } +#else +#define PUSH(v) BASIC_PUSH(v) +#define POP() BASIC_POP() +#define STACKADJ(n) BASIC_STACKADJ(n) +#endif + +/* Local variable macros */ + +#define GETLOCAL(i) (fastlocals[i]) + +/* The SETLOCAL() macro must not DECREF the local variable in-place and + then store the new value; it must copy the old value to a temporary + value, then store the new value, and then DECREF the temporary value. + This is because it is possible that during the DECREF the frame is + accessed by other code (e.g. a __del__ method or gc.collect()) and the + variable would be pointing to already-freed memory. */ +#define SETLOCAL(i, value) do { PyObject *tmp = GETLOCAL(i); \ + GETLOCAL(i) = value; \ + Py_XDECREF(tmp); } while (0) + +/* Start of code */ + + if (f == NULL) + return NULL; + +#ifdef USE_STACKCHECK + if (tstate->recursion_depth%10 == 0 && PyOS_CheckStack()) { + PyErr_SetString(PyExc_MemoryError, "Stack overflow"); + return NULL; + } +#endif + + /* push frame */ + if (++tstate->recursion_depth > recursion_limit) { + --tstate->recursion_depth; + PyErr_SetString(PyExc_RuntimeError, + "maximum recursion depth exceeded"); + tstate->frame = f->f_back; + return NULL; + } + + tstate->frame = f; + + if (tstate->use_tracing) { + if (tstate->c_tracefunc != NULL) { + /* tstate->c_tracefunc, if defined, is a + function that will be called on *every* entry + to a code block. Its return value, if not + None, is a function that will be called at + the start of each executed line of code. + (Actually, the function must return itself + in order to continue tracing.) The trace + functions are called with three arguments: + a pointer to the current frame, a string + indicating why the function is called, and + an argument which depends on the situation. + The global trace function is also called + whenever an exception is detected. */ + if (call_trace(tstate->c_tracefunc, tstate->c_traceobj, + f, PyTrace_CALL, Py_None)) { + /* Trace function raised an error */ + --tstate->recursion_depth; + tstate->frame = f->f_back; + return NULL; + } + } + if (tstate->c_profilefunc != NULL) { + /* Similar for c_profilefunc, except it needn't + return itself and isn't called for "line" events */ + if (call_trace(tstate->c_profilefunc, + tstate->c_profileobj, + f, PyTrace_CALL, Py_None)) { + /* Profile function raised an error */ + --tstate->recursion_depth; + tstate->frame = f->f_back; + return NULL; + } + } + } + + co = f->f_code; + names = co->co_names; + consts = co->co_consts; + fastlocals = f->f_localsplus; + freevars = f->f_localsplus + f->f_nlocals; + _PyCode_GETCODEPTR(co, &first_instr); + /* An explanation is in order for the next line. + + f->f_lasti now refers to the index of the last instruction + executed. You might think this was obvious from the name, but + this wasn't always true before 2.3! PyFrame_New now sets + f->f_lasti to -1 (i.e. the index *before* the first instruction) + and YIELD_VALUE doesn't fiddle with f_lasti any more. So this + does work. Promise. */ + next_instr = first_instr + f->f_lasti + 1; + stack_pointer = f->f_stacktop; + assert(stack_pointer != NULL); + f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ + +#ifdef LLTRACE + lltrace = PyDict_GetItemString(f->f_globals,"__lltrace__") != NULL; +#endif +#if defined(Py_DEBUG) || defined(LLTRACE) + filename = PyString_AsString(co->co_filename); +#endif + + why = WHY_NOT; + err = 0; + x = Py_None; /* Not a reference, just anything non-NULL */ + w = NULL; + + for (;;) { + assert(stack_pointer >= f->f_valuestack); /* else underflow */ + assert(STACK_LEVEL() <= f->f_stacksize); /* else overflow */ + + /* Do periodic things. Doing this every time through + the loop would add too much overhead, so we do it + only every Nth instruction. We also do it if + ``things_to_do'' is set, i.e. when an asynchronous + event needs attention (e.g. a signal handler or + async I/O handler); see Py_AddPendingCall() and + Py_MakePendingCalls() above. */ + + if (--_Py_Ticker < 0) { + if (*next_instr == SETUP_FINALLY) { + /* Make the last opcode before + a try: finally: block uninterruptable. */ + goto fast_next_opcode; + } + _Py_Ticker = _Py_CheckInterval; + tstate->tick_counter++; + if (things_to_do) { + if (Py_MakePendingCalls() < 0) { + why = WHY_EXCEPTION; + goto on_error; + } + } +#if !defined(HAVE_SIGNAL_H) || defined(macintosh) + /* If we have true signals, the signal handler + will call Py_AddPendingCall() so we don't + have to call PyErr_CheckSignals(). On the + Mac and DOS, alas, we have to call it. */ + if (PyErr_CheckSignals()) { + why = WHY_EXCEPTION; + goto on_error; + } +#endif + +#ifdef WITH_THREAD + if (interpreter_lock) { + /* Give another thread a chance */ + + if (PyThreadState_Swap(NULL) != tstate) + Py_FatalError("ceval: tstate mix-up"); + PyThread_release_lock(interpreter_lock); + + /* Other threads may run now */ + + PyThread_acquire_lock(interpreter_lock, 1); + if (PyThreadState_Swap(tstate) != NULL) + Py_FatalError("ceval: orphan tstate"); + + /* Check for thread interrupts */ + + if (tstate->async_exc != NULL) { + x = tstate->async_exc; + tstate->async_exc = NULL; + PyErr_SetNone(x); + Py_DECREF(x); + why = WHY_EXCEPTION; + goto on_error; + } + } +#endif + } + + fast_next_opcode: + f->f_lasti = INSTR_OFFSET(); + + /* line-by-line tracing support */ + + if (tstate->c_tracefunc != NULL && !tstate->tracing) { + /* see maybe_call_line_trace + for expository comments */ + f->f_stacktop = stack_pointer; + + err = maybe_call_line_trace(tstate->c_tracefunc, + tstate->c_traceobj, + f, &instr_lb, &instr_ub); + /* Reload possibly changed frame fields */ + JUMPTO(f->f_lasti); + if (f->f_stacktop != NULL) { + stack_pointer = f->f_stacktop; + f->f_stacktop = NULL; + } + if (err) { + /* trace function raised an exception */ + goto on_error; + } + } + + /* Extract opcode and argument */ + + opcode = NEXTOP(); + if (HAS_ARG(opcode)) + oparg = NEXTARG(); + dispatch_opcode: +#ifdef DYNAMIC_EXECUTION_PROFILE +#ifdef DXPAIRS + dxpairs[lastopcode][opcode]++; + lastopcode = opcode; +#endif + dxp[opcode]++; +#endif + +#ifdef LLTRACE + /* Instruction tracing */ + + if (lltrace) { + if (HAS_ARG(opcode)) { + printf("%d: %d, %d\n", + f->f_lasti, opcode, oparg); + } + else { + printf("%d: %d\n", + f->f_lasti, opcode); + } + } +#endif + + /* Main switch on opcode */ + + switch (opcode) { + + /* BEWARE! + It is essential that any operation that fails sets either + x to NULL, err to nonzero, or why to anything but WHY_NOT, + and that no operation that succeeds does this! */ + + /* case STOP_CODE: this is an error! */ + + case LOAD_FAST: + x = GETLOCAL(oparg); + if (x != NULL) { + Py_INCREF(x); + PUSH(x); + goto fast_next_opcode; + } + format_exc_check_arg(PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(co->co_varnames, oparg)); + break; + + case LOAD_CONST: + x = GETITEM(consts, oparg); + Py_INCREF(x); + PUSH(x); + goto fast_next_opcode; + + PREDICTED_WITH_ARG(STORE_FAST); + case STORE_FAST: + v = POP(); + SETLOCAL(oparg, v); + goto fast_next_opcode; + + PREDICTED(POP_TOP); + case POP_TOP: + v = POP(); + Py_DECREF(v); + goto fast_next_opcode; + + case ROT_TWO: + v = TOP(); + w = SECOND(); + SET_TOP(w); + SET_SECOND(v); + goto fast_next_opcode; + + case ROT_THREE: + v = TOP(); + w = SECOND(); + x = THIRD(); + SET_TOP(w); + SET_SECOND(x); + SET_THIRD(v); + goto fast_next_opcode; + + case ROT_FOUR: + u = TOP(); + v = SECOND(); + w = THIRD(); + x = FOURTH(); + SET_TOP(v); + SET_SECOND(w); + SET_THIRD(x); + SET_FOURTH(u); + goto fast_next_opcode; + + case DUP_TOP: + v = TOP(); + Py_INCREF(v); + PUSH(v); + goto fast_next_opcode; + + case DUP_TOPX: + if (oparg == 2) { + x = TOP(); + Py_INCREF(x); + w = SECOND(); + Py_INCREF(w); + STACKADJ(2); + SET_TOP(x); + SET_SECOND(w); + goto fast_next_opcode; + } else if (oparg == 3) { + x = TOP(); + Py_INCREF(x); + w = SECOND(); + Py_INCREF(w); + v = THIRD(); + Py_INCREF(v); + STACKADJ(3); + SET_TOP(x); + SET_SECOND(w); + SET_THIRD(v); + goto fast_next_opcode; + } + Py_FatalError("invalid argument to DUP_TOPX" + " (bytecode corruption?)"); + break; + + case UNARY_POSITIVE: + v = TOP(); + x = PyNumber_Positive(v); + Py_DECREF(v); + SET_TOP(x); + if (x != NULL) continue; + break; + + case UNARY_NEGATIVE: + v = TOP(); + x = PyNumber_Negative(v); + Py_DECREF(v); + SET_TOP(x); + if (x != NULL) continue; + break; + + case UNARY_NOT: + v = TOP(); + err = PyObject_IsTrue(v); + Py_DECREF(v); + if (err == 0) { + Py_INCREF(Py_True); + SET_TOP(Py_True); + continue; + } + else if (err > 0) { + Py_INCREF(Py_False); + SET_TOP(Py_False); + err = 0; + continue; + } + STACKADJ(-1); + break; + + case UNARY_CONVERT: + v = TOP(); + x = PyObject_Repr(v); + Py_DECREF(v); + SET_TOP(x); + if (x != NULL) continue; + break; + + case UNARY_INVERT: + v = TOP(); + x = PyNumber_Invert(v); + Py_DECREF(v); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_POWER: + w = POP(); + v = TOP(); + x = PyNumber_Power(v, w, Py_None); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_MULTIPLY: + w = POP(); + v = TOP(); + x = PyNumber_Multiply(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_DIVIDE: + if (!_Py_QnewFlag) { + w = POP(); + v = TOP(); + x = PyNumber_Divide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + } + /* -Qnew is in effect: fall through to + BINARY_TRUE_DIVIDE */ + case BINARY_TRUE_DIVIDE: + w = POP(); + v = TOP(); + x = PyNumber_TrueDivide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_FLOOR_DIVIDE: + w = POP(); + v = TOP(); + x = PyNumber_FloorDivide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_MODULO: + w = POP(); + v = TOP(); + x = PyNumber_Remainder(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_ADD: + w = POP(); + v = TOP(); + if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { + /* INLINE: int + int */ + register long a, b, i; + a = PyInt_AS_LONG(v); + b = PyInt_AS_LONG(w); + i = a + b; + if ((i^a) < 0 && (i^b) < 0) + goto slow_add; + x = PyInt_FromLong(i); + } + else { + slow_add: + x = PyNumber_Add(v, w); + } + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_SUBTRACT: + w = POP(); + v = TOP(); + if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { + /* INLINE: int - int */ + register long a, b, i; + a = PyInt_AS_LONG(v); + b = PyInt_AS_LONG(w); + i = a - b; + if ((i^a) < 0 && (i^~b) < 0) + goto slow_sub; + x = PyInt_FromLong(i); + } + else { + slow_sub: + x = PyNumber_Subtract(v, w); + } + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_SUBSCR: + w = POP(); + v = TOP(); + if (PyList_CheckExact(v) && PyInt_CheckExact(w)) { + /* INLINE: list[int] */ + long i = PyInt_AsLong(w); + if (i < 0) + i += PyList_GET_SIZE(v); + if (i < 0 || + i >= PyList_GET_SIZE(v)) { + PyErr_SetString(PyExc_IndexError, + "list index out of range"); + x = NULL; + } + else { + x = PyList_GET_ITEM(v, i); + Py_INCREF(x); + } + } + else + x = PyObject_GetItem(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_LSHIFT: + w = POP(); + v = TOP(); + x = PyNumber_Lshift(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_RSHIFT: + w = POP(); + v = TOP(); + x = PyNumber_Rshift(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_AND: + w = POP(); + v = TOP(); + x = PyNumber_And(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_XOR: + w = POP(); + v = TOP(); + x = PyNumber_Xor(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case BINARY_OR: + w = POP(); + v = TOP(); + x = PyNumber_Or(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_POWER: + w = POP(); + v = TOP(); + x = PyNumber_InPlacePower(v, w, Py_None); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_MULTIPLY: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceMultiply(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_DIVIDE: + if (!_Py_QnewFlag) { + w = POP(); + v = TOP(); + x = PyNumber_InPlaceDivide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + } + /* -Qnew is in effect: fall through to + INPLACE_TRUE_DIVIDE */ + case INPLACE_TRUE_DIVIDE: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceTrueDivide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_FLOOR_DIVIDE: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceFloorDivide(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_MODULO: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceRemainder(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_ADD: + w = POP(); + v = TOP(); + if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { + /* INLINE: int + int */ + register long a, b, i; + a = PyInt_AS_LONG(v); + b = PyInt_AS_LONG(w); + i = a + b; + if ((i^a) < 0 && (i^b) < 0) + goto slow_iadd; + x = PyInt_FromLong(i); + } + else { + slow_iadd: + x = PyNumber_InPlaceAdd(v, w); + } + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_SUBTRACT: + w = POP(); + v = TOP(); + if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { + /* INLINE: int - int */ + register long a, b, i; + a = PyInt_AS_LONG(v); + b = PyInt_AS_LONG(w); + i = a - b; + if ((i^a) < 0 && (i^~b) < 0) + goto slow_isub; + x = PyInt_FromLong(i); + } + else { + slow_isub: + x = PyNumber_InPlaceSubtract(v, w); + } + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_LSHIFT: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceLshift(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_RSHIFT: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceRshift(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_AND: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceAnd(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_XOR: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceXor(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case INPLACE_OR: + w = POP(); + v = TOP(); + x = PyNumber_InPlaceOr(v, w); + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case SLICE+0: + case SLICE+1: + case SLICE+2: + case SLICE+3: + if ((opcode-SLICE) & 2) + w = POP(); + else + w = NULL; + if ((opcode-SLICE) & 1) + v = POP(); + else + v = NULL; + u = TOP(); + x = apply_slice(u, v, w); + Py_DECREF(u); + Py_XDECREF(v); + Py_XDECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case STORE_SLICE+0: + case STORE_SLICE+1: + case STORE_SLICE+2: + case STORE_SLICE+3: + if ((opcode-STORE_SLICE) & 2) + w = POP(); + else + w = NULL; + if ((opcode-STORE_SLICE) & 1) + v = POP(); + else + v = NULL; + u = POP(); + t = POP(); + err = assign_slice(u, v, w, t); /* u[v:w] = t */ + Py_DECREF(t); + Py_DECREF(u); + Py_XDECREF(v); + Py_XDECREF(w); + if (err == 0) continue; + break; + + case DELETE_SLICE+0: + case DELETE_SLICE+1: + case DELETE_SLICE+2: + case DELETE_SLICE+3: + if ((opcode-DELETE_SLICE) & 2) + w = POP(); + else + w = NULL; + if ((opcode-DELETE_SLICE) & 1) + v = POP(); + else + v = NULL; + u = POP(); + err = assign_slice(u, v, w, (PyObject *)NULL); + /* del u[v:w] */ + Py_DECREF(u); + Py_XDECREF(v); + Py_XDECREF(w); + if (err == 0) continue; + break; + + case STORE_SUBSCR: + w = TOP(); + v = SECOND(); + u = THIRD(); + STACKADJ(-3); + /* v[w] = u */ + err = PyObject_SetItem(v, w, u); + Py_DECREF(u); + Py_DECREF(v); + Py_DECREF(w); + if (err == 0) continue; + break; + + case DELETE_SUBSCR: + w = TOP(); + v = SECOND(); + STACKADJ(-2); + /* del v[w] */ + err = PyObject_DelItem(v, w); + Py_DECREF(v); + Py_DECREF(w); + if (err == 0) continue; + break; + + case PRINT_EXPR: + v = POP(); + w = PySys_GetObject("displayhook"); + if (w == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "lost sys.displayhook"); + err = -1; + x = NULL; + } + if (err == 0) { + x = Py_BuildValue("(O)", v); + if (x == NULL) + err = -1; + } + if (err == 0) { + w = PyEval_CallObject(w, x); + Py_XDECREF(w); + if (w == NULL) + err = -1; + } + Py_DECREF(v); + Py_XDECREF(x); + break; + + case PRINT_ITEM_TO: + w = stream = POP(); + /* fall through to PRINT_ITEM */ + + case PRINT_ITEM: + v = POP(); + if (stream == NULL || stream == Py_None) { + w = PySys_GetObject("stdout"); + if (w == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "lost sys.stdout"); + err = -1; + } + } + /* PyFile_SoftSpace() can exececute arbitrary code + if sys.stdout is an instance with a __getattr__. + If __getattr__ raises an exception, w will + be freed, so we need to prevent that temporarily. */ + Py_XINCREF(w); + if (w != NULL && PyFile_SoftSpace(w, 0)) + err = PyFile_WriteString(" ", w); + if (err == 0) + err = PyFile_WriteObject(v, w, Py_PRINT_RAW); + if (err == 0) { + /* XXX move into writeobject() ? */ + if (PyString_Check(v)) { + char *s = PyString_AS_STRING(v); + int len = PyString_GET_SIZE(v); + if (len == 0 || + !isspace(Py_CHARMASK(s[len-1])) || + s[len-1] == ' ') + PyFile_SoftSpace(w, 1); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(v)) { + Py_UNICODE *s = PyUnicode_AS_UNICODE(v); + int len = PyUnicode_GET_SIZE(v); + if (len == 0 || + !Py_UNICODE_ISSPACE(s[len-1]) || + s[len-1] == ' ') + PyFile_SoftSpace(w, 1); + } +#endif + else + PyFile_SoftSpace(w, 1); + } + Py_XDECREF(w); + Py_DECREF(v); + Py_XDECREF(stream); + stream = NULL; + if (err == 0) + continue; + break; + + case PRINT_NEWLINE_TO: + w = stream = POP(); + /* fall through to PRINT_NEWLINE */ + + case PRINT_NEWLINE: + if (stream == NULL || stream == Py_None) { + w = PySys_GetObject("stdout"); + if (w == NULL) + PyErr_SetString(PyExc_RuntimeError, + "lost sys.stdout"); + } + if (w != NULL) { + err = PyFile_WriteString("\n", w); + if (err == 0) + PyFile_SoftSpace(w, 0); + } + Py_XDECREF(stream); + stream = NULL; + break; + + +#ifdef CASE_TOO_BIG + default: switch (opcode) { +#endif + case BREAK_LOOP: + why = WHY_BREAK; + break; + + case CONTINUE_LOOP: + retval = PyInt_FromLong(oparg); + why = WHY_CONTINUE; + break; + + case RAISE_VARARGS: + u = v = w = NULL; + switch (oparg) { + case 3: + u = POP(); /* traceback */ + /* Fallthrough */ + case 2: + v = POP(); /* value */ + /* Fallthrough */ + case 1: + w = POP(); /* exc */ + case 0: /* Fallthrough */ + why = do_raise(w, v, u); + break; + default: + PyErr_SetString(PyExc_SystemError, + "bad RAISE_VARARGS oparg"); + why = WHY_EXCEPTION; + break; + } + break; + + case LOAD_LOCALS: + if ((x = f->f_locals) == NULL) { + PyErr_SetString(PyExc_SystemError, + "no locals"); + break; + } + Py_INCREF(x); + PUSH(x); + break; + + case RETURN_VALUE: + retval = POP(); + why = WHY_RETURN; + break; + + case YIELD_VALUE: + retval = POP(); + f->f_stacktop = stack_pointer; + why = WHY_YIELD; + break; + + + case EXEC_STMT: + w = TOP(); + v = SECOND(); + u = THIRD(); + STACKADJ(-3); + err = exec_statement(f, u, v, w); + Py_DECREF(u); + Py_DECREF(v); + Py_DECREF(w); + break; + + case POP_BLOCK: + { + PyTryBlock *b = PyFrame_BlockPop(f); + while (STACK_LEVEL() > b->b_level) { + v = POP(); + Py_DECREF(v); + } + } + break; + + case END_FINALLY: + v = POP(); + if (PyInt_Check(v)) { + why = (enum why_code) PyInt_AS_LONG(v); + if (why == WHY_RETURN || + why == WHY_YIELD || + why == WHY_CONTINUE) + retval = POP(); + } + else if (PyString_Check(v) || PyClass_Check(v)) { + w = POP(); + u = POP(); + PyErr_Restore(v, w, u); + why = WHY_RERAISE; + break; + } + else if (v != Py_None) { + PyErr_SetString(PyExc_SystemError, + "'finally' pops bad exception"); + why = WHY_EXCEPTION; + } + Py_DECREF(v); + break; + + case BUILD_CLASS: + u = TOP(); + v = SECOND(); + w = THIRD(); + STACKADJ(-2); + x = build_class(u, v, w); + SET_TOP(x); + Py_DECREF(u); + Py_DECREF(v); + Py_DECREF(w); + break; + + case STORE_NAME: + w = GETITEM(names, oparg); + v = POP(); + if ((x = f->f_locals) == NULL) { + PyErr_Format(PyExc_SystemError, + "no locals found when storing %s", + PyObject_REPR(w)); + break; + } + err = PyDict_SetItem(x, w, v); + Py_DECREF(v); + break; + + case DELETE_NAME: + w = GETITEM(names, oparg); + if ((x = f->f_locals) == NULL) { + PyErr_Format(PyExc_SystemError, + "no locals when deleting %s", + PyObject_REPR(w)); + break; + } + if ((err = PyDict_DelItem(x, w)) != 0) + format_exc_check_arg(PyExc_NameError, + NAME_ERROR_MSG ,w); + break; + + PREDICTED_WITH_ARG(UNPACK_SEQUENCE); + case UNPACK_SEQUENCE: + v = POP(); + if (PyTuple_CheckExact(v)) { + if (PyTuple_Size(v) != oparg) { + PyErr_SetString(PyExc_ValueError, + "unpack tuple of wrong size"); + why = WHY_EXCEPTION; + } + else { + for (; --oparg >= 0; ) { + w = PyTuple_GET_ITEM(v, oparg); + Py_INCREF(w); + PUSH(w); + } + } + } + else if (PyList_CheckExact(v)) { + if (PyList_Size(v) != oparg) { + PyErr_SetString(PyExc_ValueError, + "unpack list of wrong size"); + why = WHY_EXCEPTION; + } + else { + for (; --oparg >= 0; ) { + w = PyList_GET_ITEM(v, oparg); + Py_INCREF(w); + PUSH(w); + } + } + } + else if (unpack_iterable(v, oparg, + stack_pointer + oparg)) + stack_pointer += oparg; + else { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + PyErr_SetString(PyExc_TypeError, + "unpack non-sequence"); + why = WHY_EXCEPTION; + } + Py_DECREF(v); + break; + + case STORE_ATTR: + w = GETITEM(names, oparg); + v = TOP(); + u = SECOND(); + STACKADJ(-2); + err = PyObject_SetAttr(v, w, u); /* v.w = u */ + Py_DECREF(v); + Py_DECREF(u); + break; + + case DELETE_ATTR: + w = GETITEM(names, oparg); + v = POP(); + err = PyObject_SetAttr(v, w, (PyObject *)NULL); + /* del v.w */ + Py_DECREF(v); + break; + + case STORE_GLOBAL: + w = GETITEM(names, oparg); + v = POP(); + err = PyDict_SetItem(f->f_globals, w, v); + Py_DECREF(v); + break; + + case DELETE_GLOBAL: + w = GETITEM(names, oparg); + if ((err = PyDict_DelItem(f->f_globals, w)) != 0) + format_exc_check_arg( + PyExc_NameError, GLOBAL_NAME_ERROR_MSG, w); + break; + + case LOAD_NAME: + w = GETITEM(names, oparg); + if ((x = f->f_locals) == NULL) { + PyErr_Format(PyExc_SystemError, + "no locals when loading %s", + PyObject_REPR(w)); + break; + } + x = PyDict_GetItem(x, w); + if (x == NULL) { + x = PyDict_GetItem(f->f_globals, w); + if (x == NULL) { + x = PyDict_GetItem(f->f_builtins, w); + if (x == NULL) { + format_exc_check_arg( + PyExc_NameError, + NAME_ERROR_MSG ,w); + break; + } + } + } + Py_INCREF(x); + PUSH(x); + break; + + case LOAD_GLOBAL: + w = GETITEM(names, oparg); + if (PyString_CheckExact(w)) { + /* Inline the PyDict_GetItem() calls. + WARNING: this is an extreme speed hack. + Do not try this at home. */ + long hash = ((PyStringObject *)w)->ob_shash; + if (hash != -1) { + PyDictObject *d; + d = (PyDictObject *)(f->f_globals); + x = d->ma_lookup(d, w, hash)->me_value; + if (x != NULL) { + Py_INCREF(x); + PUSH(x); + continue; + } + d = (PyDictObject *)(f->f_builtins); + x = d->ma_lookup(d, w, hash)->me_value; + if (x != NULL) { + Py_INCREF(x); + PUSH(x); + continue; + } + goto load_global_error; + } + } + /* This is the un-inlined version of the code above */ + x = PyDict_GetItem(f->f_globals, w); + if (x == NULL) { + x = PyDict_GetItem(f->f_builtins, w); + if (x == NULL) { + load_global_error: + format_exc_check_arg( + PyExc_NameError, + GLOBAL_NAME_ERROR_MSG, w); + break; + } + } + Py_INCREF(x); + PUSH(x); + break; + + case DELETE_FAST: + x = GETLOCAL(oparg); + if (x == NULL) { + format_exc_check_arg( + PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(co->co_varnames, oparg) + ); + break; + } + SETLOCAL(oparg, NULL); + continue; + + case LOAD_CLOSURE: + x = freevars[oparg]; + Py_INCREF(x); + PUSH(x); + break; + + case LOAD_DEREF: + x = freevars[oparg]; + w = PyCell_Get(x); + if (w == NULL) { + err = -1; + /* Don't stomp existing exception */ + if (PyErr_Occurred()) + break; + if (oparg < f->f_ncells) { + v = PyTuple_GetItem(co->co_cellvars, + oparg); + format_exc_check_arg( + PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + v); + } else { + v = PyTuple_GetItem( + co->co_freevars, + oparg - f->f_ncells); + format_exc_check_arg( + PyExc_NameError, + UNBOUNDFREE_ERROR_MSG, + v); + } + break; + } + PUSH(w); + break; + + case STORE_DEREF: + w = POP(); + x = freevars[oparg]; + PyCell_Set(x, w); + Py_DECREF(w); + continue; + + case BUILD_TUPLE: + x = PyTuple_New(oparg); + if (x != NULL) { + for (; --oparg >= 0;) { + w = POP(); + PyTuple_SET_ITEM(x, oparg, w); + } + PUSH(x); + continue; + } + break; + + case BUILD_LIST: + x = PyList_New(oparg); + if (x != NULL) { + for (; --oparg >= 0;) { + w = POP(); + PyList_SET_ITEM(x, oparg, w); + } + PUSH(x); + continue; + } + break; + + case BUILD_MAP: + x = PyDict_New(); + PUSH(x); + if (x != NULL) continue; + break; + + case LOAD_ATTR: + w = GETITEM(names, oparg); + v = TOP(); + x = PyObject_GetAttr(v, w); + Py_DECREF(v); + SET_TOP(x); + if (x != NULL) continue; + break; + + case COMPARE_OP: + w = POP(); + v = TOP(); + if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) { + /* INLINE: cmp(int, int) */ + register long a, b; + register int res; + a = PyInt_AS_LONG(v); + b = PyInt_AS_LONG(w); + switch (oparg) { + case PyCmp_LT: res = a < b; break; + case PyCmp_LE: res = a <= b; break; + case PyCmp_EQ: res = a == b; break; + case PyCmp_NE: res = a != b; break; + case PyCmp_GT: res = a > b; break; + case PyCmp_GE: res = a >= b; break; + case PyCmp_IS: res = v == w; break; + case PyCmp_IS_NOT: res = v != w; break; + default: goto slow_compare; + } + x = res ? Py_True : Py_False; + Py_INCREF(x); + } + else { + slow_compare: + x = cmp_outcome(oparg, v, w); + } + Py_DECREF(v); + Py_DECREF(w); + SET_TOP(x); + if (x == NULL) break; + PREDICT(JUMP_IF_FALSE); + PREDICT(JUMP_IF_TRUE); + continue; + + case IMPORT_NAME: + w = GETITEM(names, oparg); + x = PyDict_GetItemString(f->f_builtins, "__import__"); + if (x == NULL) { + PyErr_SetString(PyExc_ImportError, + "__import__ not found"); + break; + } + u = TOP(); + w = Py_BuildValue("(OOOO)", + w, + f->f_globals, + f->f_locals == NULL ? + Py_None : f->f_locals, + u); + Py_DECREF(u); + if (w == NULL) { + u = POP(); + x = NULL; + break; + } + x = PyEval_CallObject(x, w); + Py_DECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case IMPORT_STAR: + v = POP(); + PyFrame_FastToLocals(f); + if ((x = f->f_locals) == NULL) { + PyErr_SetString(PyExc_SystemError, + "no locals found during 'import *'"); + break; + } + err = import_all_from(x, v); + PyFrame_LocalsToFast(f, 0); + Py_DECREF(v); + if (err == 0) continue; + break; + + case IMPORT_FROM: + w = GETITEM(names, oparg); + v = TOP(); + x = import_from(v, w); + PUSH(x); + if (x != NULL) continue; + break; + + case JUMP_FORWARD: + JUMPBY(oparg); + goto fast_next_opcode; + + PREDICTED_WITH_ARG(JUMP_IF_FALSE); + case JUMP_IF_FALSE: + w = TOP(); + if (w == Py_True) { + PREDICT(POP_TOP); + goto fast_next_opcode; + } + if (w == Py_False) { + JUMPBY(oparg); + goto fast_next_opcode; + } + err = PyObject_IsTrue(w); + if (err > 0) + err = 0; + else if (err == 0) + JUMPBY(oparg); + else + break; + continue; + + PREDICTED_WITH_ARG(JUMP_IF_TRUE); + case JUMP_IF_TRUE: + w = TOP(); + if (w == Py_False) { + PREDICT(POP_TOP); + goto fast_next_opcode; + } + if (w == Py_True) { + JUMPBY(oparg); + goto fast_next_opcode; + } + err = PyObject_IsTrue(w); + if (err > 0) { + err = 0; + JUMPBY(oparg); + } + else if (err == 0) + ; + else + break; + continue; + + case JUMP_ABSOLUTE: + JUMPTO(oparg); + continue; + + case GET_ITER: + /* before: [obj]; after [getiter(obj)] */ + v = TOP(); + x = PyObject_GetIter(v); + Py_DECREF(v); + if (x != NULL) { + SET_TOP(x); + PREDICT(FOR_ITER); + continue; + } + STACKADJ(-1); + break; + + PREDICTED_WITH_ARG(FOR_ITER); + case FOR_ITER: + /* before: [iter]; after: [iter, iter()] *or* [] */ + v = TOP(); + x = PyIter_Next(v); + if (x != NULL) { + PUSH(x); + PREDICT(STORE_FAST); + PREDICT(UNPACK_SEQUENCE); + continue; + } + if (!PyErr_Occurred()) { + /* iterator ended normally */ + x = v = POP(); + Py_DECREF(v); + JUMPBY(oparg); + continue; + } + break; + + case SETUP_LOOP: + case SETUP_EXCEPT: + case SETUP_FINALLY: + PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg, + STACK_LEVEL()); + continue; + + case CALL_FUNCTION: + PCALL(PCALL_ALL); + x = call_function(&stack_pointer, oparg); + PUSH(x); + if (x != NULL) + continue; + break; + + case CALL_FUNCTION_VAR: + case CALL_FUNCTION_KW: + case CALL_FUNCTION_VAR_KW: + { + int na = oparg & 0xff; + int nk = (oparg>>8) & 0xff; + int flags = (opcode - CALL_FUNCTION) & 3; + int n = na + 2 * nk; + PyObject **pfunc, *func; + PCALL(PCALL_ALL); + if (flags & CALL_FLAG_VAR) + n++; + if (flags & CALL_FLAG_KW) + n++; + pfunc = stack_pointer - n - 1; + func = *pfunc; + + if (PyMethod_Check(func) + && PyMethod_GET_SELF(func) != NULL) { + PyObject *self = PyMethod_GET_SELF(func); + Py_INCREF(self); + func = PyMethod_GET_FUNCTION(func); + Py_INCREF(func); + Py_DECREF(*pfunc); + *pfunc = self; + na++; + n++; + } else + Py_INCREF(func); + x = ext_do_call(func, &stack_pointer, flags, na, nk); + Py_DECREF(func); + + while (stack_pointer > pfunc) { + w = POP(); + Py_DECREF(w); + } + PUSH(x); + if (x != NULL) + continue; + break; + } + + case MAKE_FUNCTION: + v = POP(); /* code object */ + x = PyFunction_New(v, f->f_globals); + Py_DECREF(v); + /* XXX Maybe this should be a separate opcode? */ + if (x != NULL && oparg > 0) { + v = PyTuple_New(oparg); + if (v == NULL) { + Py_DECREF(x); + x = NULL; + break; + } + while (--oparg >= 0) { + w = POP(); + PyTuple_SET_ITEM(v, oparg, w); + } + err = PyFunction_SetDefaults(x, v); + Py_DECREF(v); + } + PUSH(x); + break; + + case MAKE_CLOSURE: + { + int nfree; + v = POP(); /* code object */ + x = PyFunction_New(v, f->f_globals); + nfree = PyCode_GetNumFree((PyCodeObject *)v); + Py_DECREF(v); + /* XXX Maybe this should be a separate opcode? */ + if (x != NULL && nfree > 0) { + v = PyTuple_New(nfree); + if (v == NULL) { + Py_DECREF(x); + x = NULL; + break; + } + while (--nfree >= 0) { + w = POP(); + PyTuple_SET_ITEM(v, nfree, w); + } + err = PyFunction_SetClosure(x, v); + Py_DECREF(v); + } + if (x != NULL && oparg > 0) { + v = PyTuple_New(oparg); + if (v == NULL) { + Py_DECREF(x); + x = NULL; + break; + } + while (--oparg >= 0) { + w = POP(); + PyTuple_SET_ITEM(v, oparg, w); + } + err = PyFunction_SetDefaults(x, v); + Py_DECREF(v); + } + PUSH(x); + break; + } + + case BUILD_SLICE: + if (oparg == 3) + w = POP(); + else + w = NULL; + v = POP(); + u = TOP(); + x = PySlice_New(u, v, w); + Py_DECREF(u); + Py_DECREF(v); + Py_XDECREF(w); + SET_TOP(x); + if (x != NULL) continue; + break; + + case EXTENDED_ARG: + opcode = NEXTOP(); + oparg = oparg<<16 | NEXTARG(); + goto dispatch_opcode; + + default: + fprintf(stderr, + "XXX lineno: %d, opcode: %d\n", + PyCode_Addr2Line(f->f_code, f->f_lasti), + opcode); + PyErr_SetString(PyExc_SystemError, "unknown opcode"); + why = WHY_EXCEPTION; + break; + +#ifdef CASE_TOO_BIG + } +#endif + + } /* switch */ + + on_error: + + /* Quickly continue if no error occurred */ + + if (why == WHY_NOT) { + if (err == 0 && x != NULL) { +#ifdef CHECKEXC + /* This check is expensive! */ + if (PyErr_Occurred()) + fprintf(stderr, + "XXX undetected error\n"); + else +#endif + continue; /* Normal, fast path */ + } + why = WHY_EXCEPTION; + x = Py_None; + err = 0; + } + + /* Double-check exception status */ + + if (why == WHY_EXCEPTION || why == WHY_RERAISE) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_SystemError, + "error return without exception set"); + why = WHY_EXCEPTION; + } + } +#ifdef CHECKEXC + else { + /* This check is expensive! */ + if (PyErr_Occurred()) { + fprintf(stderr, + "XXX undetected error (why=%d)\n", + why); + why = WHY_EXCEPTION; + } + } +#endif + + /* Log traceback info if this is a real exception */ + + if (why == WHY_EXCEPTION) { + PyTraceBack_Here(f); + + if (tstate->c_tracefunc != NULL) + call_exc_trace(tstate->c_tracefunc, + tstate->c_traceobj, f); + } + + /* For the rest, treat WHY_RERAISE as WHY_EXCEPTION */ + + if (why == WHY_RERAISE) + why = WHY_EXCEPTION; + + /* Unwind stacks if a (pseudo) exception occurred */ + + while (why != WHY_NOT && why != WHY_YIELD && f->f_iblock > 0) { + PyTryBlock *b = PyFrame_BlockPop(f); + + if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) { + /* For a continue inside a try block, + don't pop the block for the loop. */ + PyFrame_BlockSetup(f, b->b_type, b->b_handler, + b->b_level); + why = WHY_NOT; + JUMPTO(PyInt_AS_LONG(retval)); + Py_DECREF(retval); + break; + } + + while (STACK_LEVEL() > b->b_level) { + v = POP(); + Py_XDECREF(v); + } + if (b->b_type == SETUP_LOOP && why == WHY_BREAK) { + why = WHY_NOT; + JUMPTO(b->b_handler); + break; + } + if (b->b_type == SETUP_FINALLY || + (b->b_type == SETUP_EXCEPT && + why == WHY_EXCEPTION)) { + if (why == WHY_EXCEPTION) { + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + if (val == NULL) { + val = Py_None; + Py_INCREF(val); + } + /* Make the raw exception data + available to the handler, + so a program can emulate the + Python main loop. Don't do + this for 'finally'. */ + if (b->b_type == SETUP_EXCEPT) { + PyErr_NormalizeException( + &exc, &val, &tb); + set_exc_info(tstate, + exc, val, tb); + } + if (tb == NULL) { + Py_INCREF(Py_None); + PUSH(Py_None); + } else + PUSH(tb); + PUSH(val); + PUSH(exc); + } + else { + if (why == WHY_RETURN || + why == WHY_CONTINUE) + PUSH(retval); + v = PyInt_FromLong((long)why); + PUSH(v); + } + why = WHY_NOT; + JUMPTO(b->b_handler); + break; + } + } /* unwind stack */ + + /* End the loop if we still have an error (or return) */ + + if (why != WHY_NOT) + break; + + } /* main loop */ + + if (why != WHY_YIELD) { + /* Pop remaining stack entries -- but when yielding */ + while (!EMPTY()) { + v = POP(); + Py_XDECREF(v); + } + } + + if (why != WHY_RETURN && why != WHY_YIELD) + retval = NULL; + + if (tstate->use_tracing) { + if (tstate->c_tracefunc + && (why == WHY_RETURN || why == WHY_YIELD)) { + if (call_trace(tstate->c_tracefunc, + tstate->c_traceobj, f, + PyTrace_RETURN, retval)) { + Py_XDECREF(retval); + retval = NULL; + why = WHY_EXCEPTION; + } + } + if (tstate->c_profilefunc) { + if (why == WHY_EXCEPTION) + call_trace_protected(tstate->c_profilefunc, + tstate->c_profileobj, f, + PyTrace_RETURN); + else if (call_trace(tstate->c_profilefunc, + tstate->c_profileobj, f, + PyTrace_RETURN, retval)) { + Py_XDECREF(retval); + retval = NULL; + why = WHY_EXCEPTION; + } + } + } + + reset_exc_info(tstate); + + /* pop frame */ + --tstate->recursion_depth; + tstate->frame = f->f_back; + + return retval; +} + +PyObject * +PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, + PyObject **args, int argcount, PyObject **kws, int kwcount, + PyObject **defs, int defcount, PyObject *closure) +{ + register PyFrameObject *f; + register PyObject *retval = NULL; + register PyObject **fastlocals, **freevars; + PyThreadState *tstate = PyThreadState_GET(); + PyObject *x, *u; + + if (globals == NULL) { + PyErr_SetString(PyExc_SystemError, + "PyEval_EvalCodeEx: NULL globals"); + return NULL; + } + + assert(globals != NULL); + f = PyFrame_New(tstate, co, globals, locals); + if (f == NULL) + return NULL; + + fastlocals = f->f_localsplus; + freevars = f->f_localsplus + f->f_nlocals; + + if (co->co_argcount > 0 || + co->co_flags & (CO_VARARGS | CO_VARKEYWORDS)) { + int i; + int n = argcount; + PyObject *kwdict = NULL; + if (co->co_flags & CO_VARKEYWORDS) { + kwdict = PyDict_New(); + if (kwdict == NULL) + goto fail; + i = co->co_argcount; + if (co->co_flags & CO_VARARGS) + i++; + SETLOCAL(i, kwdict); + } + if (argcount > co->co_argcount) { + if (!(co->co_flags & CO_VARARGS)) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes %s %d " + "%sargument%s (%d given)", + PyString_AsString(co->co_name), + defcount ? "at most" : "exactly", + co->co_argcount, + kwcount ? "non-keyword " : "", + co->co_argcount == 1 ? "" : "s", + argcount); + goto fail; + } + n = co->co_argcount; + } + for (i = 0; i < n; i++) { + x = args[i]; + Py_INCREF(x); + SETLOCAL(i, x); + } + if (co->co_flags & CO_VARARGS) { + u = PyTuple_New(argcount - n); + if (u == NULL) + goto fail; + SETLOCAL(co->co_argcount, u); + for (i = n; i < argcount; i++) { + x = args[i]; + Py_INCREF(x); + PyTuple_SET_ITEM(u, i-n, x); + } + } + for (i = 0; i < kwcount; i++) { + PyObject *keyword = kws[2*i]; + PyObject *value = kws[2*i + 1]; + int j; + if (keyword == NULL || !PyString_Check(keyword)) { + PyErr_Format(PyExc_TypeError, + "%.200s() keywords must be strings", + PyString_AsString(co->co_name)); + goto fail; + } + /* XXX slow -- speed up using dictionary? */ + for (j = 0; j < co->co_argcount; j++) { + PyObject *nm = PyTuple_GET_ITEM( + co->co_varnames, j); + int cmp = PyObject_RichCompareBool( + keyword, nm, Py_EQ); + if (cmp > 0) + break; + else if (cmp < 0) + goto fail; + } + /* Check errors from Compare */ + if (PyErr_Occurred()) + goto fail; + if (j >= co->co_argcount) { + if (kwdict == NULL) { + PyErr_Format(PyExc_TypeError, + "%.200s() got an unexpected " + "keyword argument '%.400s'", + PyString_AsString(co->co_name), + PyString_AsString(keyword)); + goto fail; + } + PyDict_SetItem(kwdict, keyword, value); + } + else { + if (GETLOCAL(j) != NULL) { + PyErr_Format(PyExc_TypeError, + "%.200s() got multiple " + "values for keyword " + "argument '%.400s'", + PyString_AsString(co->co_name), + PyString_AsString(keyword)); + goto fail; + } + Py_INCREF(value); + SETLOCAL(j, value); + } + } + if (argcount < co->co_argcount) { + int m = co->co_argcount - defcount; + for (i = argcount; i < m; i++) { + if (GETLOCAL(i) == NULL) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes %s %d " + "%sargument%s (%d given)", + PyString_AsString(co->co_name), + ((co->co_flags & CO_VARARGS) || + defcount) ? "at least" + : "exactly", + m, kwcount ? "non-keyword " : "", + m == 1 ? "" : "s", i); + goto fail; + } + } + if (n > m) + i = n - m; + else + i = 0; + for (; i < defcount; i++) { + if (GETLOCAL(m+i) == NULL) { + PyObject *def = defs[i]; + Py_INCREF(def); + SETLOCAL(m+i, def); + } + } + } + } + else { + if (argcount > 0 || kwcount > 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%d given)", + PyString_AsString(co->co_name), + argcount + kwcount); + goto fail; + } + } + /* Allocate and initialize storage for cell vars, and copy free + vars into frame. This isn't too efficient right now. */ + if (f->f_ncells) { + int i = 0, j = 0, nargs, found; + char *cellname, *argname; + PyObject *c; + + nargs = co->co_argcount; + if (co->co_flags & CO_VARARGS) + nargs++; + if (co->co_flags & CO_VARKEYWORDS) + nargs++; + + /* Check for cells that shadow args */ + for (i = 0; i < f->f_ncells && j < nargs; ++i) { + cellname = PyString_AS_STRING( + PyTuple_GET_ITEM(co->co_cellvars, i)); + found = 0; + while (j < nargs) { + argname = PyString_AS_STRING( + PyTuple_GET_ITEM(co->co_varnames, j)); + if (strcmp(cellname, argname) == 0) { + c = PyCell_New(GETLOCAL(j)); + if (c == NULL) + goto fail; + GETLOCAL(f->f_nlocals + i) = c; + found = 1; + break; + } + j++; + } + if (found == 0) { + c = PyCell_New(NULL); + if (c == NULL) + goto fail; + SETLOCAL(f->f_nlocals + i, c); + } + } + /* Initialize any that are left */ + while (i < f->f_ncells) { + c = PyCell_New(NULL); + if (c == NULL) + goto fail; + SETLOCAL(f->f_nlocals + i, c); + i++; + } + } + if (f->f_nfreevars) { + int i; + for (i = 0; i < f->f_nfreevars; ++i) { + PyObject *o = PyTuple_GET_ITEM(closure, i); + Py_INCREF(o); + freevars[f->f_ncells + i] = o; + } + } + + if (co->co_flags & CO_GENERATOR) { + /* Don't need to keep the reference to f_back, it will be set + * when the generator is resumed. */ + Py_XDECREF(f->f_back); + f->f_back = NULL; + + PCALL(PCALL_GENERATOR); + + /* Create a new generator that owns the ready to run frame + * and return that as the value. */ + return gen_new(f); + } + + retval = eval_frame(f); + + fail: /* Jump here from prelude on failure */ + + /* decref'ing the frame can cause __del__ methods to get invoked, + which can call back into Python. While we're done with the + current Python frame (f), the associated C stack is still in use, + so recursion_depth must be boosted for the duration. + */ + assert(tstate != NULL); + ++tstate->recursion_depth; + Py_DECREF(f); + --tstate->recursion_depth; + return retval; +} + + +/* Implementation notes for set_exc_info() and reset_exc_info(): + +- Below, 'exc_ZZZ' stands for 'exc_type', 'exc_value' and + 'exc_traceback'. These always travel together. + +- tstate->curexc_ZZZ is the "hot" exception that is set by + PyErr_SetString(), cleared by PyErr_Clear(), and so on. + +- Once an exception is caught by an except clause, it is transferred + from tstate->curexc_ZZZ to tstate->exc_ZZZ, from which sys.exc_info() + can pick it up. This is the primary task of set_exc_info(). + +- Now let me explain the complicated dance with frame->f_exc_ZZZ. + + Long ago, when none of this existed, there were just a few globals: + one set corresponding to the "hot" exception, and one set + corresponding to sys.exc_ZZZ. (Actually, the latter weren't C + globals; they were simply stored as sys.exc_ZZZ. For backwards + compatibility, they still are!) The problem was that in code like + this: + + try: + "something that may fail" + except "some exception": + "do something else first" + "print the exception from sys.exc_ZZZ." + + if "do something else first" invoked something that raised and caught + an exception, sys.exc_ZZZ were overwritten. That was a frequent + cause of subtle bugs. I fixed this by changing the semantics as + follows: + + - Within one frame, sys.exc_ZZZ will hold the last exception caught + *in that frame*. + + - But initially, and as long as no exception is caught in a given + frame, sys.exc_ZZZ will hold the last exception caught in the + previous frame (or the frame before that, etc.). + + The first bullet fixed the bug in the above example. The second + bullet was for backwards compatibility: it was (and is) common to + have a function that is called when an exception is caught, and to + have that function access the caught exception via sys.exc_ZZZ. + (Example: traceback.print_exc()). + + At the same time I fixed the problem that sys.exc_ZZZ weren't + thread-safe, by introducing sys.exc_info() which gets it from tstate; + but that's really a separate improvement. + + The reset_exc_info() function in ceval.c restores the tstate->exc_ZZZ + variables to what they were before the current frame was called. The + set_exc_info() function saves them on the frame so that + reset_exc_info() can restore them. The invariant is that + frame->f_exc_ZZZ is NULL iff the current frame never caught an + exception (where "catching" an exception applies only to successful + except clauses); and if the current frame ever caught an exception, + frame->f_exc_ZZZ is the exception that was stored in tstate->exc_ZZZ + at the start of the current frame. + +*/ + +static void +set_exc_info(PyThreadState *tstate, + PyObject *type, PyObject *value, PyObject *tb) +{ + PyFrameObject *frame; + PyObject *tmp_type, *tmp_value, *tmp_tb; + + frame = tstate->frame; + if (frame->f_exc_type == NULL) { + /* This frame didn't catch an exception before */ + /* Save previous exception of this thread in this frame */ + if (tstate->exc_type == NULL) { + Py_INCREF(Py_None); + tstate->exc_type = Py_None; + } + tmp_type = frame->f_exc_type; + tmp_value = frame->f_exc_value; + tmp_tb = frame->f_exc_traceback; + Py_XINCREF(tstate->exc_type); + Py_XINCREF(tstate->exc_value); + Py_XINCREF(tstate->exc_traceback); + frame->f_exc_type = tstate->exc_type; + frame->f_exc_value = tstate->exc_value; + frame->f_exc_traceback = tstate->exc_traceback; + Py_XDECREF(tmp_type); + Py_XDECREF(tmp_value); + Py_XDECREF(tmp_tb); + } + /* Set new exception for this thread */ + tmp_type = tstate->exc_type; + tmp_value = tstate->exc_value; + tmp_tb = tstate->exc_traceback; + Py_XINCREF(type); + Py_XINCREF(value); + Py_XINCREF(tb); + tstate->exc_type = type; + tstate->exc_value = value; + tstate->exc_traceback = tb; + Py_XDECREF(tmp_type); + Py_XDECREF(tmp_value); + Py_XDECREF(tmp_tb); + /* For b/w compatibility */ + PySys_SetObject("exc_type", type); + PySys_SetObject("exc_value", value); + PySys_SetObject("exc_traceback", tb); +} + +static void +reset_exc_info(PyThreadState *tstate) +{ + PyFrameObject *frame; + PyObject *tmp_type, *tmp_value, *tmp_tb; + frame = tstate->frame; + if (frame->f_exc_type != NULL) { + /* This frame caught an exception */ + tmp_type = tstate->exc_type; + tmp_value = tstate->exc_value; + tmp_tb = tstate->exc_traceback; + Py_XINCREF(frame->f_exc_type); + Py_XINCREF(frame->f_exc_value); + Py_XINCREF(frame->f_exc_traceback); + tstate->exc_type = frame->f_exc_type; + tstate->exc_value = frame->f_exc_value; + tstate->exc_traceback = frame->f_exc_traceback; + Py_XDECREF(tmp_type); + Py_XDECREF(tmp_value); + Py_XDECREF(tmp_tb); + /* For b/w compatibility */ + PySys_SetObject("exc_type", frame->f_exc_type); + PySys_SetObject("exc_value", frame->f_exc_value); + PySys_SetObject("exc_traceback", frame->f_exc_traceback); + } + tmp_type = frame->f_exc_type; + tmp_value = frame->f_exc_value; + tmp_tb = frame->f_exc_traceback; + frame->f_exc_type = NULL; + frame->f_exc_value = NULL; + frame->f_exc_traceback = NULL; + Py_XDECREF(tmp_type); + Py_XDECREF(tmp_value); + Py_XDECREF(tmp_tb); +} + +/* Logic for the raise statement (too complicated for inlining). + This *consumes* a reference count to each of its arguments. */ +static enum why_code +do_raise(PyObject *type, PyObject *value, PyObject *tb) +{ + if (type == NULL) { + /* Reraise */ + PyThreadState *tstate = PyThreadState_Get(); + type = tstate->exc_type == NULL ? Py_None : tstate->exc_type; + value = tstate->exc_value; + tb = tstate->exc_traceback; + Py_XINCREF(type); + Py_XINCREF(value); + Py_XINCREF(tb); + } + + /* We support the following forms of raise: + raise , + raise , + raise , None + raise , + raise , None + raise , + raise , None + + An omitted second argument is the same as None. + + In addition, raise , is the same as + raising the tuple's first item (and it better have one!); + this rule is applied recursively. + + Finally, an optional third argument can be supplied, which + gives the traceback to be substituted (useful when + re-raising an exception after examining it). */ + + /* First, check the traceback argument, replacing None with + NULL. */ + if (tb == Py_None) { + Py_DECREF(tb); + tb = NULL; + } + else if (tb != NULL && !PyTraceBack_Check(tb)) { + PyErr_SetString(PyExc_TypeError, + "raise: arg 3 must be a traceback or None"); + goto raise_error; + } + + /* Next, replace a missing value with None */ + if (value == NULL) { + value = Py_None; + Py_INCREF(value); + } + + /* Next, repeatedly, replace a tuple exception with its first item */ + while (PyTuple_Check(type) && PyTuple_Size(type) > 0) { + PyObject *tmp = type; + type = PyTuple_GET_ITEM(type, 0); + Py_INCREF(type); + Py_DECREF(tmp); + } + + if (PyString_CheckExact(type)) + /* Raising builtin string is deprecated but still allowed -- + * do nothing. Raising an instance of a new-style str + * subclass is right out. */ + PyErr_Warn(PyExc_PendingDeprecationWarning, + "raising a string exception is deprecated"); + + else if (PyClass_Check(type)) + PyErr_NormalizeException(&type, &value, &tb); + + else if (PyInstance_Check(type)) { + /* Raising an instance. The value should be a dummy. */ + if (value != Py_None) { + PyErr_SetString(PyExc_TypeError, + "instance exception may not have a separate value"); + goto raise_error; + } + else { + /* Normalize to raise , */ + Py_DECREF(value); + value = type; + type = (PyObject*) ((PyInstanceObject*)type)->in_class; + Py_INCREF(type); + } + } + else { + /* Not something you can raise. You get an exception + anyway, just not what you specified :-) */ + PyErr_Format(PyExc_TypeError, + "exceptions must be classes, instances, or " + "strings (deprecated), not %s", + type->ob_type->tp_name); + goto raise_error; + } + PyErr_Restore(type, value, tb); + if (tb == NULL) + return WHY_EXCEPTION; + else + return WHY_RERAISE; + raise_error: + Py_XDECREF(value); + Py_XDECREF(type); + Py_XDECREF(tb); + return WHY_EXCEPTION; +} + +/* Iterate v argcnt times and store the results on the stack (via decreasing + sp). Return 1 for success, 0 if error. */ + +static int +unpack_iterable(PyObject *v, int argcnt, PyObject **sp) +{ + int i = 0; + PyObject *it; /* iter(v) */ + PyObject *w; + + assert(v != NULL); + + it = PyObject_GetIter(v); + if (it == NULL) + goto Error; + + for (; i < argcnt; i++) { + w = PyIter_Next(it); + if (w == NULL) { + /* Iterator done, via error or exhaustion. */ + if (!PyErr_Occurred()) { + PyErr_Format(PyExc_ValueError, + "need more than %d value%s to unpack", + i, i == 1 ? "" : "s"); + } + goto Error; + } + *--sp = w; + } + + /* We better have exhausted the iterator now. */ + w = PyIter_Next(it); + if (w == NULL) { + if (PyErr_Occurred()) + goto Error; + Py_DECREF(it); + return 1; + } + Py_DECREF(w); + PyErr_SetString(PyExc_ValueError, "too many values to unpack"); + /* fall through */ +Error: + for (; i > 0; i--, sp++) + Py_DECREF(*sp); + Py_XDECREF(it); + return 0; +} + + +#ifdef LLTRACE +static int +prtrace(PyObject *v, char *str) +{ + printf("%s ", str); + if (PyObject_Print(v, stdout, 0) != 0) + PyErr_Clear(); /* Don't know what else to do */ + printf("\n"); + return 1; +} +#endif + +static void +call_exc_trace(Py_tracefunc func, PyObject *self, PyFrameObject *f) +{ + PyObject *type, *value, *traceback, *arg; + int err; + PyErr_Fetch(&type, &value, &traceback); + if (value == NULL) { + value = Py_None; + Py_INCREF(value); + } + arg = Py_BuildValue("(OOO)", type, value, traceback); + if (arg == NULL) { + PyErr_Restore(type, value, traceback); + return; + } + err = call_trace(func, self, f, PyTrace_EXCEPTION, arg); + Py_DECREF(arg); + if (err == 0) + PyErr_Restore(type, value, traceback); + else { + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); + } +} + +static void +call_trace_protected(Py_tracefunc func, PyObject *obj, PyFrameObject *frame, + int what) +{ + PyObject *type, *value, *traceback; + int err; + PyErr_Fetch(&type, &value, &traceback); + err = call_trace(func, obj, frame, what, NULL); + if (err == 0) + PyErr_Restore(type, value, traceback); + else { + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); + } +} + +static int +call_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame, + int what, PyObject *arg) +{ + register PyThreadState *tstate = frame->f_tstate; + int result; + if (tstate->tracing) + return 0; + tstate->tracing++; + tstate->use_tracing = 0; + result = func(obj, frame, what, arg); + tstate->use_tracing = ((tstate->c_tracefunc != NULL) + || (tstate->c_profilefunc != NULL)); + tstate->tracing--; + return result; +} + +PyObject * +_PyEval_CallTracing(PyObject *func, PyObject *args) +{ + PyFrameObject *frame = PyEval_GetFrame(); + PyThreadState *tstate = frame->f_tstate; + int save_tracing = tstate->tracing; + int save_use_tracing = tstate->use_tracing; + PyObject *result; + + tstate->tracing = 0; + tstate->use_tracing = ((tstate->c_tracefunc != NULL) + || (tstate->c_profilefunc != NULL)); + result = PyObject_Call(func, args, NULL); + tstate->tracing = save_tracing; + tstate->use_tracing = save_use_tracing; + return result; +} + +static int +maybe_call_line_trace(Py_tracefunc func, PyObject *obj, + PyFrameObject *frame, int *instr_lb, int *instr_ub) +{ + /* The theory of SET_LINENO-less tracing. + + In a nutshell, we use the co_lnotab field of the code object + to tell when execution has moved onto a different line. + + As mentioned above, the basic idea is so set things up so + that + + *instr_lb <= frame->f_lasti < *instr_ub + + is true so long as execution does not change lines. + + This is all fairly simple. Digging the information out of + co_lnotab takes some work, but is conceptually clear. + + Somewhat harder to explain is why we don't *always* call the + line trace function when the above test fails. + + Consider this code: + + 1: def f(a): + 2: if a: + 3: print 1 + 4: else: + 5: print 2 + + which compiles to this: + + 2 0 LOAD_FAST 0 (a) + 3 JUMP_IF_FALSE 9 (to 15) + 6 POP_TOP + + 3 7 LOAD_CONST 1 (1) + 10 PRINT_ITEM + 11 PRINT_NEWLINE + 12 JUMP_FORWARD 6 (to 21) + >> 15 POP_TOP + + 5 16 LOAD_CONST 2 (2) + 19 PRINT_ITEM + 20 PRINT_NEWLINE + >> 21 LOAD_CONST 0 (None) + 24 RETURN_VALUE + + If 'a' is false, execution will jump to instruction at offset + 15 and the co_lnotab will claim that execution has moved to + line 3. This is at best misleading. In this case we could + associate the POP_TOP with line 4, but that doesn't make + sense in all cases (I think). + + What we do is only call the line trace function if the co_lnotab + indicates we have jumped to the *start* of a line, i.e. if the + current instruction offset matches the offset given for the + start of a line by the co_lnotab. + + This also takes care of the situation where 'a' is true. + Execution will jump from instruction offset 12 to offset 21. + Then the co_lnotab would imply that execution has moved to line + 5, which is again misleading. + + Why do we set f_lineno when tracing? Well, consider the code + above when 'a' is true. If stepping through this with 'n' in + pdb, you would stop at line 1 with a "call" type event, then + line events on lines 2 and 3, then a "return" type event -- but + you would be shown line 5 during this event. This is a change + from the behaviour in 2.2 and before, and I've found it + confusing in practice. By setting and using f_lineno when + tracing, one can report a line number different from that + suggested by f_lasti on this one occasion where it's desirable. + */ + + int result = 0; + + if ((frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub)) { + PyCodeObject* co = frame->f_code; + int size, addr, line; + unsigned char* p; + + size = PyString_GET_SIZE(co->co_lnotab) / 2; + p = (unsigned char*)PyString_AS_STRING(co->co_lnotab); + + addr = 0; + line = co->co_firstlineno; + + /* possible optimization: if f->f_lasti == instr_ub + (likely to be a common case) then we already know + instr_lb -- if we stored the matching value of p + somwhere we could skip the first while loop. */ + + /* see comments in compile.c for the description of + co_lnotab. A point to remember: increments to p + should come in pairs -- although we don't care about + the line increments here, treating them as byte + increments gets confusing, to say the least. */ + + while (size > 0) { + if (addr + *p > frame->f_lasti) + break; + addr += *p++; + if (*p) *instr_lb = addr; + line += *p++; + --size; + } + + if (addr == frame->f_lasti) { + frame->f_lineno = line; + result = call_trace(func, obj, frame, + PyTrace_LINE, Py_None); + } + + if (size > 0) { + while (--size >= 0) { + addr += *p++; + if (*p++) + break; + } + *instr_ub = addr; + } + else { + *instr_ub = INT_MAX; + } + } + + return result; +} + +void +PyEval_SetProfile(Py_tracefunc func, PyObject *arg) +{ + PyThreadState *tstate = PyThreadState_Get(); + PyObject *temp = tstate->c_profileobj; + Py_XINCREF(arg); + tstate->c_profilefunc = NULL; + tstate->c_profileobj = NULL; + tstate->use_tracing = tstate->c_tracefunc != NULL; + Py_XDECREF(temp); + tstate->c_profilefunc = func; + tstate->c_profileobj = arg; + tstate->use_tracing = (func != NULL) || (tstate->c_tracefunc != NULL); +} + +void +PyEval_SetTrace(Py_tracefunc func, PyObject *arg) +{ + PyThreadState *tstate = PyThreadState_Get(); + PyObject *temp = tstate->c_traceobj; + Py_XINCREF(arg); + tstate->c_tracefunc = NULL; + tstate->c_traceobj = NULL; + tstate->use_tracing = tstate->c_profilefunc != NULL; + Py_XDECREF(temp); + tstate->c_tracefunc = func; + tstate->c_traceobj = arg; + tstate->use_tracing = ((func != NULL) + || (tstate->c_profilefunc != NULL)); +} + +PyObject * +PyEval_GetBuiltins(void) +{ + PyFrameObject *current_frame = PyEval_GetFrame(); + if (current_frame == NULL) + return PyThreadState_Get()->interp->builtins; + else + return current_frame->f_builtins; +} + +PyObject * +PyEval_GetLocals(void) +{ + PyFrameObject *current_frame = PyEval_GetFrame(); + if (current_frame == NULL) + return NULL; + PyFrame_FastToLocals(current_frame); + return current_frame->f_locals; +} + +PyObject * +PyEval_GetGlobals(void) +{ + PyFrameObject *current_frame = PyEval_GetFrame(); + if (current_frame == NULL) + return NULL; + else + return current_frame->f_globals; +} + +PyFrameObject * +PyEval_GetFrame(void) +{ + PyThreadState *tstate = PyThreadState_Get(); + return _PyThreadState_GetFrame(tstate); +} + +int +PyEval_GetRestricted(void) +{ + PyFrameObject *current_frame = PyEval_GetFrame(); + return current_frame == NULL ? 0 : current_frame->f_restricted; +} + +int +PyEval_MergeCompilerFlags(PyCompilerFlags *cf) +{ + PyFrameObject *current_frame = PyEval_GetFrame(); + int result = cf->cf_flags != 0; + + if (current_frame != NULL) { + const int codeflags = current_frame->f_code->co_flags; + const int compilerflags = codeflags & PyCF_MASK; + if (compilerflags) { + result = 1; + cf->cf_flags |= compilerflags; + } +#if 0 /* future keyword */ + if (codeflags & CO_GENERATOR_ALLOWED) { + result = 1; + cf->cf_flags |= CO_GENERATOR_ALLOWED; + } +#endif + } + return result; +} + +int +Py_FlushLine(void) +{ + PyObject *f = PySys_GetObject("stdout"); + if (f == NULL) + return 0; + if (!PyFile_SoftSpace(f, 0)) + return 0; + return PyFile_WriteString("\n", f); +} + + +/* External interface to call any callable object. + The arg must be a tuple or NULL. */ + +#undef PyEval_CallObject +/* for backward compatibility: export this interface */ + +PyObject * +PyEval_CallObject(PyObject *func, PyObject *arg) +{ + return PyEval_CallObjectWithKeywords(func, arg, (PyObject *)NULL); +} +#define PyEval_CallObject(func,arg) \ + PyEval_CallObjectWithKeywords(func, arg, (PyObject *)NULL) + +PyObject * +PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw) +{ + PyObject *result; + + if (arg == NULL) + arg = PyTuple_New(0); + else if (!PyTuple_Check(arg)) { + PyErr_SetString(PyExc_TypeError, + "argument list must be a tuple"); + return NULL; + } + else + Py_INCREF(arg); + + if (kw != NULL && !PyDict_Check(kw)) { + PyErr_SetString(PyExc_TypeError, + "keyword list must be a dictionary"); + Py_DECREF(arg); + return NULL; + } + + result = PyObject_Call(func, arg, kw); + Py_DECREF(arg); + return result; +} + +char * +PyEval_GetFuncName(PyObject *func) +{ + if (PyMethod_Check(func)) + return PyEval_GetFuncName(PyMethod_GET_FUNCTION(func)); + else if (PyFunction_Check(func)) + return PyString_AsString(((PyFunctionObject*)func)->func_name); + else if (PyCFunction_Check(func)) + return ((PyCFunctionObject*)func)->m_ml->ml_name; + else if (PyClass_Check(func)) + return PyString_AsString(((PyClassObject*)func)->cl_name); + else if (PyInstance_Check(func)) { + return PyString_AsString( + ((PyInstanceObject*)func)->in_class->cl_name); + } else { + return func->ob_type->tp_name; + } +} + +char * +PyEval_GetFuncDesc(PyObject *func) +{ + if (PyMethod_Check(func)) + return "()"; + else if (PyFunction_Check(func)) + return "()"; + else if (PyCFunction_Check(func)) + return "()"; + else if (PyClass_Check(func)) + return " constructor"; + else if (PyInstance_Check(func)) { + return " instance"; + } else { + return " object"; + } +} + +#define EXT_POP(STACK_POINTER) (*--(STACK_POINTER)) + +static void +err_args(PyObject *func, int flags, int nargs) +{ + if (flags & METH_NOARGS) + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%d given)", + ((PyCFunctionObject *)func)->m_ml->ml_name, + nargs); + else + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%d given)", + ((PyCFunctionObject *)func)->m_ml->ml_name, + nargs); +} + +static PyObject * +call_function(PyObject ***pp_stack, int oparg) +{ + int na = oparg & 0xff; + int nk = (oparg>>8) & 0xff; + int n = na + 2 * nk; + PyObject **pfunc = (*pp_stack) - n - 1; + PyObject *func = *pfunc; + PyObject *x, *w; + + /* Always dispatch PyCFunction first, because these are + presumed to be the most frequent callable object. + */ + if (PyCFunction_Check(func) && nk == 0) { + int flags = PyCFunction_GET_FLAGS(func); + PCALL(PCALL_CFUNCTION); + if (flags & (METH_NOARGS | METH_O)) { + PyCFunction meth = PyCFunction_GET_FUNCTION(func); + PyObject *self = PyCFunction_GET_SELF(func); + if (flags & METH_NOARGS && na == 0) + x = (*meth)(self, NULL); + else if (flags & METH_O && na == 1) { + PyObject *arg = EXT_POP(*pp_stack); + x = (*meth)(self, arg); + Py_DECREF(arg); + } + else { + err_args(func, flags, na); + x = NULL; + } + } + else { + PyObject *callargs; + callargs = load_args(pp_stack, na); + x = PyCFunction_Call(func, callargs, NULL); + Py_XDECREF(callargs); + } + } else { + if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { + /* optimize access to bound methods */ + PyObject *self = PyMethod_GET_SELF(func); + PCALL(PCALL_METHOD); + PCALL(PCALL_BOUND_METHOD); + Py_INCREF(self); + func = PyMethod_GET_FUNCTION(func); + Py_INCREF(func); + Py_DECREF(*pfunc); + *pfunc = self; + na++; + n++; + } else + Py_INCREF(func); + if (PyFunction_Check(func)) + x = fast_function(func, pp_stack, n, na, nk); + else + x = do_call(func, pp_stack, na, nk); + Py_DECREF(func); + } + + /* What does this do? */ + while ((*pp_stack) > pfunc) { + w = EXT_POP(*pp_stack); + Py_DECREF(w); + PCALL(PCALL_POP); + } + return x; +} + +/* The fast_function() function optimize calls for which no argument + tuple is necessary; the objects are passed directly from the stack. + For the simplest case -- a function that takes only positional + arguments and is called with only positional arguments -- it + inlines the most primitive frame setup code from + PyEval_EvalCodeEx(), which vastly reduces the checks that must be + done before evaluating the frame. +*/ + +static PyObject * +fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk) +{ + PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); + PyObject *globals = PyFunction_GET_GLOBALS(func); + PyObject *argdefs = PyFunction_GET_DEFAULTS(func); + PyObject **d = NULL; + int nd = 0; + + PCALL(PCALL_FUNCTION); + PCALL(PCALL_FAST_FUNCTION); + if (argdefs == NULL && co->co_argcount == n && nk==0 && + co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { + PyFrameObject *f; + PyObject *retval = NULL; + PyThreadState *tstate = PyThreadState_GET(); + PyObject **fastlocals, **stack; + int i; + + PCALL(PCALL_FASTER_FUNCTION); + assert(globals != NULL); + /* XXX Perhaps we should create a specialized + PyFrame_New() that doesn't take locals, but does + take builtins without sanity checking them. + */ + f = PyFrame_New(tstate, co, globals, NULL); + if (f == NULL) + return NULL; + + fastlocals = f->f_localsplus; + stack = (*pp_stack) - n; + + for (i = 0; i < n; i++) { + Py_INCREF(*stack); + fastlocals[i] = *stack++; + } + retval = eval_frame(f); + assert(tstate != NULL); + ++tstate->recursion_depth; + Py_DECREF(f); + --tstate->recursion_depth; + return retval; + } + if (argdefs != NULL) { + d = &PyTuple_GET_ITEM(argdefs, 0); + nd = ((PyTupleObject *)argdefs)->ob_size; + } + return PyEval_EvalCodeEx(co, globals, + (PyObject *)NULL, (*pp_stack)-n, na, + (*pp_stack)-2*nk, nk, d, nd, + PyFunction_GET_CLOSURE(func)); +} + +static PyObject * +update_keyword_args(PyObject *orig_kwdict, int nk, PyObject ***pp_stack, + PyObject *func) +{ + PyObject *kwdict = NULL; + if (orig_kwdict == NULL) + kwdict = PyDict_New(); + else { + kwdict = PyDict_Copy(orig_kwdict); + Py_DECREF(orig_kwdict); + } + if (kwdict == NULL) + return NULL; + while (--nk >= 0) { + int err; + PyObject *value = EXT_POP(*pp_stack); + PyObject *key = EXT_POP(*pp_stack); + if (PyDict_GetItem(kwdict, key) != NULL) { + PyErr_Format(PyExc_TypeError, + "%.200s%s got multiple values " + "for keyword argument '%.200s'", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func), + PyString_AsString(key)); + Py_DECREF(key); + Py_DECREF(value); + Py_DECREF(kwdict); + return NULL; + } + err = PyDict_SetItem(kwdict, key, value); + Py_DECREF(key); + Py_DECREF(value); + if (err) { + Py_DECREF(kwdict); + return NULL; + } + } + return kwdict; +} + +static PyObject * +update_star_args(int nstack, int nstar, PyObject *stararg, + PyObject ***pp_stack) +{ + PyObject *callargs, *w; + + callargs = PyTuple_New(nstack + nstar); + if (callargs == NULL) { + return NULL; + } + if (nstar) { + int i; + for (i = 0; i < nstar; i++) { + PyObject *a = PyTuple_GET_ITEM(stararg, i); + Py_INCREF(a); + PyTuple_SET_ITEM(callargs, nstack + i, a); + } + } + while (--nstack >= 0) { + w = EXT_POP(*pp_stack); + PyTuple_SET_ITEM(callargs, nstack, w); + } + return callargs; +} + +static PyObject * +load_args(PyObject ***pp_stack, int na) +{ + PyObject *args = PyTuple_New(na); + PyObject *w; + + if (args == NULL) + return NULL; + while (--na >= 0) { + w = EXT_POP(*pp_stack); + PyTuple_SET_ITEM(args, na, w); + } + return args; +} + +static PyObject * +do_call(PyObject *func, PyObject ***pp_stack, int na, int nk) +{ + PyObject *callargs = NULL; + PyObject *kwdict = NULL; + PyObject *result = NULL; + + if (nk > 0) { + kwdict = update_keyword_args(NULL, nk, pp_stack, func); + if (kwdict == NULL) + goto call_fail; + } + callargs = load_args(pp_stack, na); + if (callargs == NULL) + goto call_fail; +#ifdef CALL_PROFILE + /* At this point, we have to look at the type of func to + update the call stats properly. Do it here so as to avoid + exposing the call stats machinery outside ceval.c + */ + if (PyFunction_Check(func)) + PCALL(PCALL_FUNCTION); + else if (PyMethod_Check(func)) + PCALL(PCALL_METHOD); + else if (PyType_Check(func)) + PCALL(PCALL_TYPE); + else + PCALL(PCALL_OTHER); +#endif + result = PyObject_Call(func, callargs, kwdict); + call_fail: + Py_XDECREF(callargs); + Py_XDECREF(kwdict); + return result; +} + +static PyObject * +ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk) +{ + int nstar = 0; + PyObject *callargs = NULL; + PyObject *stararg = NULL; + PyObject *kwdict = NULL; + PyObject *result = NULL; + + if (flags & CALL_FLAG_KW) { + kwdict = EXT_POP(*pp_stack); + if (!(kwdict && PyDict_Check(kwdict))) { + PyErr_Format(PyExc_TypeError, + "%s%s argument after ** " + "must be a dictionary", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func)); + goto ext_call_fail; + } + } + if (flags & CALL_FLAG_VAR) { + stararg = EXT_POP(*pp_stack); + if (!PyTuple_Check(stararg)) { + PyObject *t = NULL; + t = PySequence_Tuple(stararg); + if (t == NULL) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Format(PyExc_TypeError, + "%s%s argument after * " + "must be a sequence", + PyEval_GetFuncName(func), + PyEval_GetFuncDesc(func)); + } + goto ext_call_fail; + } + Py_DECREF(stararg); + stararg = t; + } + nstar = PyTuple_GET_SIZE(stararg); + } + if (nk > 0) { + kwdict = update_keyword_args(kwdict, nk, pp_stack, func); + if (kwdict == NULL) + goto ext_call_fail; + } + callargs = update_star_args(na, nstar, stararg, pp_stack); + if (callargs == NULL) + goto ext_call_fail; +#ifdef CALL_PROFILE + /* At this point, we have to look at the type of func to + update the call stats properly. Do it here so as to avoid + exposing the call stats machinery outside ceval.c + */ + if (PyFunction_Check(func)) + PCALL(PCALL_FUNCTION); + else if (PyMethod_Check(func)) + PCALL(PCALL_METHOD); + else if (PyType_Check(func)) + PCALL(PCALL_TYPE); + else + PCALL(PCALL_OTHER); +#endif + result = PyObject_Call(func, callargs, kwdict); + ext_call_fail: + Py_XDECREF(callargs); + Py_XDECREF(kwdict); + Py_XDECREF(stararg); + return result; +} + +#define SLICE_ERROR_MSG \ + "standard sequence type does not support step size other than one" + +/* Extract a slice index from a PyInt or PyLong, and store in *pi. + Silently reduce values larger than INT_MAX to INT_MAX, and silently + boost values less than -INT_MAX to 0. Return 0 on error, 1 on success. +*/ +/* Note: If v is NULL, return success without storing into *pi. This + is because_PyEval_SliceIndex() is called by apply_slice(), which can be + called by the SLICE opcode with v and/or w equal to NULL. +*/ +int +_PyEval_SliceIndex(PyObject *v, int *pi) +{ + if (v != NULL) { + long x; + if (PyInt_Check(v)) { + x = PyInt_AsLong(v); + } else if (PyLong_Check(v)) { + x = PyLong_AsLong(v); + if (x==-1 && PyErr_Occurred()) { + PyObject *long_zero; + int cmp; + + if (!PyErr_ExceptionMatches( + PyExc_OverflowError)) { + /* It's not an overflow error, so just + signal an error */ + return 0; + } + + /* Clear the OverflowError */ + PyErr_Clear(); + + /* It's an overflow error, so we need to + check the sign of the long integer, + set the value to INT_MAX or -INT_MAX, + and clear the error. */ + + /* Create a long integer with a value of 0 */ + long_zero = PyLong_FromLong(0L); + if (long_zero == NULL) + return 0; + + /* Check sign */ + cmp = PyObject_RichCompareBool(v, long_zero, + Py_GT); + Py_DECREF(long_zero); + if (cmp < 0) + return 0; + else if (cmp) + x = INT_MAX; + else + x = -INT_MAX; + } + } else { + PyErr_SetString(PyExc_TypeError, + "slice indices must be integers"); + return 0; + } + /* Truncate -- very long indices are truncated anyway */ + if (x > INT_MAX) + x = INT_MAX; + else if (x < -INT_MAX) + x = -INT_MAX; + *pi = x; + } + return 1; +} + +#undef ISINT +#define ISINT(x) ((x) == NULL || PyInt_Check(x) || PyLong_Check(x)) + +static PyObject * +apply_slice(PyObject *u, PyObject *v, PyObject *w) /* return u[v:w] */ +{ + PyTypeObject *tp = u->ob_type; + PySequenceMethods *sq = tp->tp_as_sequence; + + if (sq && sq->sq_slice && ISINT(v) && ISINT(w)) { + int ilow = 0, ihigh = INT_MAX; + if (!_PyEval_SliceIndex(v, &ilow)) + return NULL; + if (!_PyEval_SliceIndex(w, &ihigh)) + return NULL; + return PySequence_GetSlice(u, ilow, ihigh); + } + else { + PyObject *slice = PySlice_New(v, w, NULL); + if (slice != NULL) { + PyObject *res = PyObject_GetItem(u, slice); + Py_DECREF(slice); + return res; + } + else + return NULL; + } +} + +static int +assign_slice(PyObject *u, PyObject *v, PyObject *w, PyObject *x) + /* u[v:w] = x */ +{ + PyTypeObject *tp = u->ob_type; + PySequenceMethods *sq = tp->tp_as_sequence; + + if (sq && sq->sq_slice && ISINT(v) && ISINT(w)) { + int ilow = 0, ihigh = INT_MAX; + if (!_PyEval_SliceIndex(v, &ilow)) + return -1; + if (!_PyEval_SliceIndex(w, &ihigh)) + return -1; + if (x == NULL) + return PySequence_DelSlice(u, ilow, ihigh); + else + return PySequence_SetSlice(u, ilow, ihigh, x); + } + else { + PyObject *slice = PySlice_New(v, w, NULL); + if (slice != NULL) { + int res; + if (x != NULL) + res = PyObject_SetItem(u, slice, x); + else + res = PyObject_DelItem(u, slice); + Py_DECREF(slice); + return res; + } + else + return -1; + } +} + +static PyObject * +cmp_outcome(int op, register PyObject *v, register PyObject *w) +{ + int res = 0; + switch (op) { + case PyCmp_IS: + res = (v == w); + break; + case PyCmp_IS_NOT: + res = (v != w); + break; + case PyCmp_IN: + res = PySequence_Contains(w, v); + if (res < 0) + return NULL; + break; + case PyCmp_NOT_IN: + res = PySequence_Contains(w, v); + if (res < 0) + return NULL; + res = !res; + break; + case PyCmp_EXC_MATCH: + res = PyErr_GivenExceptionMatches(v, w); + break; + default: + return PyObject_RichCompare(v, w, op); + } + v = res ? Py_True : Py_False; + Py_INCREF(v); + return v; +} + +static PyObject * +import_from(PyObject *v, PyObject *name) +{ + PyObject *x; + + x = PyObject_GetAttr(v, name); + if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Format(PyExc_ImportError, + "cannot import name %.230s", + PyString_AsString(name)); + } + return x; +} + +static int +import_all_from(PyObject *locals, PyObject *v) +{ + PyObject *all = PyObject_GetAttrString(v, "__all__"); + PyObject *dict, *name, *value; + int skip_leading_underscores = 0; + int pos, err; + + if (all == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; /* Unexpected error */ + PyErr_Clear(); + dict = PyObject_GetAttrString(v, "__dict__"); + if (dict == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return -1; + PyErr_SetString(PyExc_ImportError, + "from-import-* object has no __dict__ and no __all__"); + return -1; + } + all = PyMapping_Keys(dict); + Py_DECREF(dict); + if (all == NULL) + return -1; + skip_leading_underscores = 1; + } + + for (pos = 0, err = 0; ; pos++) { + name = PySequence_GetItem(all, pos); + if (name == NULL) { + if (!PyErr_ExceptionMatches(PyExc_IndexError)) + err = -1; + else + PyErr_Clear(); + break; + } + if (skip_leading_underscores && + PyString_Check(name) && + PyString_AS_STRING(name)[0] == '_') + { + Py_DECREF(name); + continue; + } + value = PyObject_GetAttr(v, name); + if (value == NULL) + err = -1; + else + err = PyDict_SetItem(locals, name, value); + Py_DECREF(name); + Py_XDECREF(value); + if (err != 0) + break; + } + Py_DECREF(all); + return err; +} + +static PyObject * +build_class(PyObject *methods, PyObject *bases, PyObject *name) +{ + PyObject *metaclass = NULL, *result, *base; + + if (PyDict_Check(methods)) + metaclass = PyDict_GetItemString(methods, "__metaclass__"); + if (metaclass != NULL) + Py_INCREF(metaclass); + else if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) { + base = PyTuple_GET_ITEM(bases, 0); + metaclass = PyObject_GetAttrString(base, "__class__"); + if (metaclass == NULL) { + PyErr_Clear(); + metaclass = (PyObject *)base->ob_type; + Py_INCREF(metaclass); + } + } + else { + PyObject *g = PyEval_GetGlobals(); + if (g != NULL && PyDict_Check(g)) + metaclass = PyDict_GetItemString(g, "__metaclass__"); + if (metaclass == NULL) + metaclass = (PyObject *) &PyClass_Type; + Py_INCREF(metaclass); + } + result = PyObject_CallFunction(metaclass, "OOO", name, bases, methods); + Py_DECREF(metaclass); + return result; +} + +static int +exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals, + PyObject *locals) +{ + int n; + PyObject *v; + int plain = 0; + + if (PyTuple_Check(prog) && globals == Py_None && locals == Py_None && + ((n = PyTuple_Size(prog)) == 2 || n == 3)) { + /* Backward compatibility hack */ + globals = PyTuple_GetItem(prog, 1); + if (n == 3) + locals = PyTuple_GetItem(prog, 2); + prog = PyTuple_GetItem(prog, 0); + } + if (globals == Py_None) { + globals = PyEval_GetGlobals(); + if (locals == Py_None) { + locals = PyEval_GetLocals(); + plain = 1; + } + } + else if (locals == Py_None) + locals = globals; + if (!PyString_Check(prog) && + !PyUnicode_Check(prog) && + !PyCode_Check(prog) && + !PyFile_Check(prog)) { + PyErr_SetString(PyExc_TypeError, + "exec: arg 1 must be a string, file, or code object"); + return -1; + } + if (!PyDict_Check(globals)) { + PyErr_SetString(PyExc_TypeError, + "exec: arg 2 must be a dictionary or None"); + return -1; + } + if (!PyDict_Check(locals)) { + PyErr_SetString(PyExc_TypeError, + "exec: arg 3 must be a dictionary or None"); + return -1; + } + if (PyDict_GetItemString(globals, "__builtins__") == NULL) + PyDict_SetItemString(globals, "__builtins__", f->f_builtins); + if (PyCode_Check(prog)) { + if (PyCode_GetNumFree((PyCodeObject *)prog) > 0) { + PyErr_SetString(PyExc_TypeError, + "code object passed to exec may not contain free variables"); + return -1; + } + v = PyEval_EvalCode((PyCodeObject *) prog, globals, locals); + } + else if (PyFile_Check(prog)) { + FILE *fp = PyFile_AsFile(prog); + char *name = PyString_AsString(PyFile_Name(prog)); + PyCompilerFlags cf; + cf.cf_flags = 0; + if (PyEval_MergeCompilerFlags(&cf)) + v = PyRun_FileFlags(fp, name, Py_file_input, globals, + locals, &cf); + else + v = PyRun_File(fp, name, Py_file_input, globals, + locals); + } + else { + PyObject *tmp = NULL; + char *str; + PyCompilerFlags cf; + cf.cf_flags = 0; +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(prog)) { + tmp = PyUnicode_AsUTF8String(prog); + if (tmp == NULL) + return -1; + prog = tmp; + cf.cf_flags |= PyCF_SOURCE_IS_UTF8; + } +#endif + if (PyString_AsStringAndSize(prog, &str, NULL)) + return -1; + if (PyEval_MergeCompilerFlags(&cf)) + v = PyRun_StringFlags(str, Py_file_input, globals, + locals, &cf); + else + v = PyRun_String(str, Py_file_input, globals, locals); + Py_XDECREF(tmp); + } + if (plain) + PyFrame_LocalsToFast(f, 0); + if (v == NULL) + return -1; + Py_DECREF(v); + return 0; +} + +static void +format_exc_check_arg(PyObject *exc, char *format_str, PyObject *obj) +{ + char *obj_str; + + if (!obj) + return; + + obj_str = PyString_AsString(obj); + if (!obj_str) + return; + + PyErr_Format(exc, format_str, obj_str); +} + +#ifdef DYNAMIC_EXECUTION_PROFILE + +static PyObject * +getarray(long a[256]) +{ + int i; + PyObject *l = PyList_New(256); + if (l == NULL) return NULL; + for (i = 0; i < 256; i++) { + PyObject *x = PyInt_FromLong(a[i]); + if (x == NULL) { + Py_DECREF(l); + return NULL; + } + PyList_SetItem(l, i, x); + } + for (i = 0; i < 256; i++) + a[i] = 0; + return l; +} + +PyObject * +_Py_GetDXProfile(PyObject *self, PyObject *args) +{ +#ifndef DXPAIRS + return getarray(dxp); +#else + int i; + PyObject *l = PyList_New(257); + if (l == NULL) return NULL; + for (i = 0; i < 257; i++) { + PyObject *x = getarray(dxpairs[i]); + if (x == NULL) { + Py_DECREF(l); + return NULL; + } + PyList_SetItem(l, i, x); + } + return l; +#endif +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/codecs.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/codecs.c new file mode 100644 index 00000000..cb15126f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/codecs.c @@ -0,0 +1,831 @@ +/* ------------------------------------------------------------------------ + + Python Codec Registry and support functions + +Written by Marc-Andre Lemburg (mal@lemburg.com). + +Copyright (c) Corporation for National Research Initiatives. + + ------------------------------------------------------------------------ */ + +#include "Python.h" +#include + +/* --- Codec Registry ----------------------------------------------------- */ + +/* Import the standard encodings package which will register the first + codec search function. + + This is done in a lazy way so that the Unicode implementation does + not downgrade startup time of scripts not needing it. + + ImportErrors are silently ignored by this function. Only one try is + made. + +*/ + +static int _PyCodecRegistry_Init(void); /* Forward */ + +int PyCodec_Register(PyObject *search_function) +{ + PyInterpreterState *interp = PyThreadState_Get()->interp; + if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) + goto onError; + if (search_function == NULL) { + PyErr_BadArgument(); + goto onError; + } + if (!PyCallable_Check(search_function)) { + PyErr_SetString(PyExc_TypeError, + "argument must be callable"); + goto onError; + } + return PyList_Append(interp->codec_search_path, search_function); + + onError: + return -1; +} + +/* Convert a string to a normalized Python string: all characters are + converted to lower case, spaces are replaced with underscores. */ + +static +PyObject *normalizestring(const char *string) +{ + register size_t i; + size_t len = strlen(string); + char *p; + PyObject *v; + + if (len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, "string is too large"); + return NULL; + } + + v = PyString_FromStringAndSize(NULL, (int)len); + if (v == NULL) + return NULL; + p = PyString_AS_STRING(v); + for (i = 0; i < len; i++) { + register char ch = string[i]; + if (ch == ' ') + ch = '-'; + else + ch = tolower(ch); + p[i] = ch; + } + return v; +} + +/* Lookup the given encoding and return a tuple providing the codec + facilities. + + The encoding string is looked up converted to all lower-case + characters. This makes encodings looked up through this mechanism + effectively case-insensitive. + + If no codec is found, a LookupError is set and NULL returned. + + As side effect, this tries to load the encodings package, if not + yet done. This is part of the lazy load strategy for the encodings + package. + +*/ + +PyObject *_PyCodec_Lookup(const char *encoding) +{ + PyInterpreterState *interp; + PyObject *result, *args = NULL, *v; + int i, len; + + if (encoding == NULL) { + PyErr_BadArgument(); + goto onError; + } + + interp = PyThreadState_Get()->interp; + if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) + goto onError; + + /* Convert the encoding to a normalized Python string: all + characters are converted to lower case, spaces and hyphens are + replaced with underscores. */ + v = normalizestring(encoding); + if (v == NULL) + goto onError; + PyString_InternInPlace(&v); + + /* First, try to lookup the name in the registry dictionary */ + result = PyDict_GetItem(interp->codec_search_cache, v); + if (result != NULL) { + Py_INCREF(result); + Py_DECREF(v); + return result; + } + + /* Next, scan the search functions in order of registration */ + args = PyTuple_New(1); + if (args == NULL) + goto onError; + PyTuple_SET_ITEM(args,0,v); + + len = PyList_Size(interp->codec_search_path); + if (len < 0) + goto onError; + if (len == 0) { + PyErr_SetString(PyExc_LookupError, + "no codec search functions registered: " + "can't find encoding"); + goto onError; + } + + for (i = 0; i < len; i++) { + PyObject *func; + + func = PyList_GetItem(interp->codec_search_path, i); + if (func == NULL) + goto onError; + result = PyEval_CallObject(func, args); + if (result == NULL) + goto onError; + if (result == Py_None) { + Py_DECREF(result); + continue; + } + if (!PyTuple_Check(result) || PyTuple_GET_SIZE(result) != 4) { + PyErr_SetString(PyExc_TypeError, + "codec search functions must return 4-tuples"); + Py_DECREF(result); + goto onError; + } + break; + } + if (i == len) { + /* XXX Perhaps we should cache misses too ? */ + PyErr_Format(PyExc_LookupError, + "unknown encoding: %s", encoding); + goto onError; + } + + /* Cache and return the result */ + PyDict_SetItem(interp->codec_search_cache, v, result); + Py_DECREF(args); + return result; + + onError: + Py_XDECREF(args); + return NULL; +} + +static +PyObject *args_tuple(PyObject *object, + const char *errors) +{ + PyObject *args; + + args = PyTuple_New(1 + (errors != NULL)); + if (args == NULL) + return NULL; + Py_INCREF(object); + PyTuple_SET_ITEM(args,0,object); + if (errors) { + PyObject *v; + + v = PyString_FromString(errors); + if (v == NULL) { + Py_DECREF(args); + return NULL; + } + PyTuple_SET_ITEM(args, 1, v); + } + return args; +} + +/* Build a codec by calling factory(stream[,errors]) or just + factory(errors) depending on whether the given parameters are + non-NULL. */ + +static +PyObject *build_stream_codec(PyObject *factory, + PyObject *stream, + const char *errors) +{ + PyObject *args, *codec; + + args = args_tuple(stream, errors); + if (args == NULL) + return NULL; + + codec = PyEval_CallObject(factory, args); + Py_DECREF(args); + return codec; +} + +/* Convenience APIs to query the Codec registry. + + All APIs return a codec object with incremented refcount. + + */ + +PyObject *PyCodec_Encoder(const char *encoding) +{ + PyObject *codecs; + PyObject *v; + + codecs = _PyCodec_Lookup(encoding); + if (codecs == NULL) + goto onError; + v = PyTuple_GET_ITEM(codecs,0); + Py_DECREF(codecs); + Py_INCREF(v); + return v; + + onError: + return NULL; +} + +PyObject *PyCodec_Decoder(const char *encoding) +{ + PyObject *codecs; + PyObject *v; + + codecs = _PyCodec_Lookup(encoding); + if (codecs == NULL) + goto onError; + v = PyTuple_GET_ITEM(codecs,1); + Py_DECREF(codecs); + Py_INCREF(v); + return v; + + onError: + return NULL; +} + +PyObject *PyCodec_StreamReader(const char *encoding, + PyObject *stream, + const char *errors) +{ + PyObject *codecs, *ret; + + codecs = _PyCodec_Lookup(encoding); + if (codecs == NULL) + goto onError; + ret = build_stream_codec(PyTuple_GET_ITEM(codecs,2),stream,errors); + Py_DECREF(codecs); + return ret; + + onError: + return NULL; +} + +PyObject *PyCodec_StreamWriter(const char *encoding, + PyObject *stream, + const char *errors) +{ + PyObject *codecs, *ret; + + codecs = _PyCodec_Lookup(encoding); + if (codecs == NULL) + goto onError; + ret = build_stream_codec(PyTuple_GET_ITEM(codecs,3),stream,errors); + Py_DECREF(codecs); + return ret; + + onError: + return NULL; +} + +/* Encode an object (e.g. an Unicode object) using the given encoding + and return the resulting encoded object (usually a Python string). + + errors is passed to the encoder factory as argument if non-NULL. */ + +PyObject *PyCodec_Encode(PyObject *object, + const char *encoding, + const char *errors) +{ + PyObject *encoder = NULL; + PyObject *args = NULL, *result; + PyObject *v; + + encoder = PyCodec_Encoder(encoding); + if (encoder == NULL) + goto onError; + + args = args_tuple(object, errors); + if (args == NULL) + goto onError; + + result = PyEval_CallObject(encoder,args); + if (result == NULL) + goto onError; + + if (!PyTuple_Check(result) || + PyTuple_GET_SIZE(result) != 2) { + PyErr_SetString(PyExc_TypeError, + "encoder must return a tuple (object,integer)"); + goto onError; + } + v = PyTuple_GET_ITEM(result,0); + Py_INCREF(v); + /* We don't check or use the second (integer) entry. */ + + Py_DECREF(args); + Py_DECREF(encoder); + Py_DECREF(result); + return v; + + onError: + Py_XDECREF(args); + Py_XDECREF(encoder); + return NULL; +} + +/* Decode an object (usually a Python string) using the given encoding + and return an equivalent object (e.g. an Unicode object). + + errors is passed to the decoder factory as argument if non-NULL. */ + +PyObject *PyCodec_Decode(PyObject *object, + const char *encoding, + const char *errors) +{ + PyObject *decoder = NULL; + PyObject *args = NULL, *result = NULL; + PyObject *v; + + decoder = PyCodec_Decoder(encoding); + if (decoder == NULL) + goto onError; + + args = args_tuple(object, errors); + if (args == NULL) + goto onError; + + result = PyEval_CallObject(decoder,args); + if (result == NULL) + goto onError; + if (!PyTuple_Check(result) || + PyTuple_GET_SIZE(result) != 2) { + PyErr_SetString(PyExc_TypeError, + "decoder must return a tuple (object,integer)"); + goto onError; + } + v = PyTuple_GET_ITEM(result,0); + Py_INCREF(v); + /* We don't check or use the second (integer) entry. */ + + Py_DECREF(args); + Py_DECREF(decoder); + Py_DECREF(result); + return v; + + onError: + Py_XDECREF(args); + Py_XDECREF(decoder); + Py_XDECREF(result); + return NULL; +} + +/* Register the error handling callback function error under the name + name. This function will be called by the codec when it encounters + an unencodable characters/undecodable bytes and doesn't know the + callback name, when name is specified as the error parameter + in the call to the encode/decode function. + Return 0 on success, -1 on error */ +int PyCodec_RegisterError(const char *name, PyObject *error) +{ + PyInterpreterState *interp = PyThreadState_Get()->interp; + if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) + return -1; + if (!PyCallable_Check(error)) { + PyErr_SetString(PyExc_TypeError, "handler must be callable"); + return -1; + } + return PyDict_SetItemString(interp->codec_error_registry, + (char *)name, error); +} + +/* Lookup the error handling callback function registered under the + name error. As a special case NULL can be passed, in which case + the error handling callback for strict encoding will be returned. */ +PyObject *PyCodec_LookupError(const char *name) +{ + PyObject *handler = NULL; + + PyInterpreterState *interp = PyThreadState_Get()->interp; + if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) + return NULL; + + if (name==NULL) + name = "strict"; + handler = PyDict_GetItemString(interp->codec_error_registry, (char *)name); + if (!handler) + PyErr_Format(PyExc_LookupError, "unknown error handler name '%.400s'", name); + else + Py_INCREF(handler); + return handler; +} + +static void wrong_exception_type(PyObject *exc) +{ + PyObject *type = PyObject_GetAttrString(exc, "__class__"); + if (type != NULL) { + PyObject *name = PyObject_GetAttrString(type, "__name__"); + Py_DECREF(type); + if (name != NULL) { + PyObject *string = PyObject_Str(name); + Py_DECREF(name); + if (string != NULL) { + PyErr_Format(PyExc_TypeError, + "don't know how to handle %.400s in error callback", + PyString_AS_STRING(string)); + Py_DECREF(string); + } + } + } +} + +PyObject *PyCodec_StrictErrors(PyObject *exc) +{ + if (PyInstance_Check(exc)) + PyErr_SetObject((PyObject*)((PyInstanceObject*)exc)->in_class, + exc); + else + PyErr_SetString(PyExc_TypeError, "codec must pass exception instance"); + return NULL; +} + + +#ifdef Py_USING_UNICODE +PyObject *PyCodec_IgnoreErrors(PyObject *exc) +{ + int end; + if (PyObject_IsInstance(exc, PyExc_UnicodeEncodeError)) { + if (PyUnicodeEncodeError_GetEnd(exc, &end)) + return NULL; + } + else if (PyObject_IsInstance(exc, PyExc_UnicodeDecodeError)) { + if (PyUnicodeDecodeError_GetEnd(exc, &end)) + return NULL; + } + else if (PyObject_IsInstance(exc, PyExc_UnicodeTranslateError)) { + if (PyUnicodeTranslateError_GetEnd(exc, &end)) + return NULL; + } + else { + wrong_exception_type(exc); + return NULL; + } + /* ouch: passing NULL, 0, pos gives None instead of u'' */ + return Py_BuildValue("(u#i)", &end, 0, end); +} + + +PyObject *PyCodec_ReplaceErrors(PyObject *exc) +{ + PyObject *restuple; + int start; + int end; + int i; + + if (PyObject_IsInstance(exc, PyExc_UnicodeEncodeError)) { + PyObject *res; + Py_UNICODE *p; + if (PyUnicodeEncodeError_GetStart(exc, &start)) + return NULL; + if (PyUnicodeEncodeError_GetEnd(exc, &end)) + return NULL; + res = PyUnicode_FromUnicode(NULL, end-start); + if (res == NULL) + return NULL; + for (p = PyUnicode_AS_UNICODE(res), i = start; + i0) { + *outp++ = '0' + c/base; + c %= base; + base /= 10; + } + *outp++ = ';'; + } + restuple = Py_BuildValue("(Oi)", res, end); + Py_DECREF(res); + Py_DECREF(object); + return restuple; + } + else { + wrong_exception_type(exc); + return NULL; + } +} + +static Py_UNICODE hexdigits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' +}; + +PyObject *PyCodec_BackslashReplaceErrors(PyObject *exc) +{ + if (PyObject_IsInstance(exc, PyExc_UnicodeEncodeError)) { + PyObject *restuple; + PyObject *object; + int start; + int end; + PyObject *res; + Py_UNICODE *p; + Py_UNICODE *startp; + Py_UNICODE *outp; + int ressize; + if (PyUnicodeEncodeError_GetStart(exc, &start)) + return NULL; + if (PyUnicodeEncodeError_GetEnd(exc, &end)) + return NULL; + if (!(object = PyUnicodeEncodeError_GetObject(exc))) + return NULL; + startp = PyUnicode_AS_UNICODE(object); + for (p = startp+start, ressize = 0; p < startp+end; ++p) { + if (*p >= 0x00010000) + ressize += 1+1+8; + else if (*p >= 0x100) { + ressize += 1+1+4; + } + else + ressize += 1+1+2; + } + res = PyUnicode_FromUnicode(NULL, ressize); + if (res==NULL) + return NULL; + for (p = startp+start, outp = PyUnicode_AS_UNICODE(res); + p < startp+end; ++p) { + Py_UNICODE c = *p; + *outp++ = '\\'; + if (c >= 0x00010000) { + *outp++ = 'U'; + *outp++ = hexdigits[(c>>28)&0xf]; + *outp++ = hexdigits[(c>>24)&0xf]; + *outp++ = hexdigits[(c>>20)&0xf]; + *outp++ = hexdigits[(c>>16)&0xf]; + *outp++ = hexdigits[(c>>12)&0xf]; + *outp++ = hexdigits[(c>>8)&0xf]; + } + else if (c >= 0x100) { + *outp++ = 'u'; + *outp++ = hexdigits[(c>>12)&0xf]; + *outp++ = hexdigits[(c>>8)&0xf]; + } + else + *outp++ = 'x'; + *outp++ = hexdigits[(c>>4)&0xf]; + *outp++ = hexdigits[c&0xf]; + } + + restuple = Py_BuildValue("(Oi)", res, end); + Py_DECREF(res); + Py_DECREF(object); + return restuple; + } + else { + wrong_exception_type(exc); + return NULL; + } +} +#endif + +static PyObject *strict_errors(PyObject *self, PyObject *exc) +{ + return PyCodec_StrictErrors(exc); +} + + +#ifdef Py_USING_UNICODE +static PyObject *ignore_errors(PyObject *self, PyObject *exc) +{ + return PyCodec_IgnoreErrors(exc); +} + + +static PyObject *replace_errors(PyObject *self, PyObject *exc) +{ + return PyCodec_ReplaceErrors(exc); +} + + +static PyObject *xmlcharrefreplace_errors(PyObject *self, PyObject *exc) +{ + return PyCodec_XMLCharRefReplaceErrors(exc); +} + + +static PyObject *backslashreplace_errors(PyObject *self, PyObject *exc) +{ + return PyCodec_BackslashReplaceErrors(exc); +} +#endif + +static int _PyCodecRegistry_Init(void) +{ + static struct { + char *name; + PyMethodDef def; + } methods[] = + { + { + "strict", + { + "strict_errors", + strict_errors, + METH_O + } + }, +#ifdef Py_USING_UNICODE + { + "ignore", + { + "ignore_errors", + ignore_errors, + METH_O + } + }, + { + "replace", + { + "replace_errors", + replace_errors, + METH_O + } + }, + { + "xmlcharrefreplace", + { + "xmlcharrefreplace_errors", + xmlcharrefreplace_errors, + METH_O + } + }, + { + "backslashreplace", + { + "backslashreplace_errors", + backslashreplace_errors, + METH_O + } + } +#endif + }; + + PyInterpreterState *interp = PyThreadState_Get()->interp; + PyObject *mod; + int i; + + if (interp->codec_search_path != NULL) + return 0; + + interp->codec_search_path = PyList_New(0); + interp->codec_search_cache = PyDict_New(); + interp->codec_error_registry = PyDict_New(); + + if (interp->codec_error_registry) { + for (i = 0; i < sizeof(methods)/sizeof(methods[0]); ++i) { + PyObject *func = PyCFunction_New(&methods[i].def, NULL); + int res; + if (!func) + Py_FatalError("can't initialize codec error registry"); + res = PyCodec_RegisterError(methods[i].name, func); + Py_DECREF(func); + if (res) + Py_FatalError("can't initialize codec error registry"); + } + } + + if (interp->codec_search_path == NULL || + interp->codec_search_cache == NULL || + interp->codec_error_registry == NULL) + Py_FatalError("can't initialize codec registry"); + + mod = PyImport_ImportModuleEx("encodings", NULL, NULL, NULL); + if (mod == NULL) { + if (PyErr_ExceptionMatches(PyExc_ImportError)) { + /* Ignore ImportErrors... this is done so that + distributions can disable the encodings package. Note + that other errors are not masked, e.g. SystemErrors + raised to inform the user of an error in the Python + configuration are still reported back to the user. */ + PyErr_Clear(); + return 0; + } + return -1; + } + Py_DECREF(mod); + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/compile.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/compile.c new file mode 100644 index 00000000..463bf91f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/compile.c @@ -0,0 +1,5779 @@ +/* Compile an expression node to intermediate code */ + +/* XXX TO DO: + XXX add __doc__ attribute == co_doc to code object attributes? + XXX (it's currently the first item of the co_const tuple) + XXX Generate simple jump for break/return outside 'try...finally' + XXX Allow 'continue' inside finally clause of try-finally + XXX New opcode for loading the initial index for a for loop + XXX other JAR tricks? +*/ + +#include "Python.h" + +#include "node.h" +#include "token.h" +#include "graminit.h" +#include "compile.h" +#include "symtable.h" +#include "opcode.h" +#include "structmember.h" + +#include + +/* Three symbols from graminit.h are also defined in Python.h, with + Py_ prefixes to their names. Python.h can't include graminit.h + (which defines too many confusing symbols), but we can check here + that they haven't changed (which is very unlikely, but possible). */ +#if Py_single_input != single_input + #error "single_input has changed -- update Py_single_input in Python.h" +#endif +#if Py_file_input != file_input + #error "file_input has changed -- update Py_file_input in Python.h" +#endif +#if Py_eval_input != eval_input + #error "eval_input has changed -- update Py_eval_input in Python.h" +#endif + +int Py_OptimizeFlag = 0; + +#define OP_DELETE 0 +#define OP_ASSIGN 1 +#define OP_APPLY 2 + +#define VAR_LOAD 0 +#define VAR_STORE 1 +#define VAR_DELETE 2 + +#define DEL_CLOSURE_ERROR \ +"can not delete variable '%.400s' referenced in nested scope" + +#define DUPLICATE_ARGUMENT \ +"duplicate argument '%s' in function definition" + +#define ILLEGAL_DYNAMIC_SCOPE \ +"%.100s: exec or 'import *' makes names ambiguous in nested scope" + +#define GLOBAL_AFTER_ASSIGN \ +"name '%.400s' is assigned to before global declaration" + +#define GLOBAL_AFTER_USE \ +"name '%.400s' is used prior to global declaration" + +#define LOCAL_GLOBAL \ +"name '%.400s' is a function parameter and declared global" + +#define LATE_FUTURE \ +"from __future__ imports must occur at the beginning of the file" + +#define ASSIGN_DEBUG \ +"can not assign to __debug__" + +#define MANGLE_LEN 256 + +#define OFF(x) offsetof(PyCodeObject, x) + +static PyMemberDef code_memberlist[] = { + {"co_argcount", T_INT, OFF(co_argcount), READONLY}, + {"co_nlocals", T_INT, OFF(co_nlocals), READONLY}, + {"co_stacksize",T_INT, OFF(co_stacksize), READONLY}, + {"co_flags", T_INT, OFF(co_flags), READONLY}, + {"co_code", T_OBJECT, OFF(co_code), READONLY}, + {"co_consts", T_OBJECT, OFF(co_consts), READONLY}, + {"co_names", T_OBJECT, OFF(co_names), READONLY}, + {"co_varnames", T_OBJECT, OFF(co_varnames), READONLY}, + {"co_freevars", T_OBJECT, OFF(co_freevars), READONLY}, + {"co_cellvars", T_OBJECT, OFF(co_cellvars), READONLY}, + {"co_filename", T_OBJECT, OFF(co_filename), READONLY}, + {"co_name", T_OBJECT, OFF(co_name), READONLY}, + {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, + {"co_lnotab", T_OBJECT, OFF(co_lnotab), READONLY}, + {NULL} /* Sentinel */ +}; + +PyDoc_STRVAR(code_doc, +"code(argcount, nlocals, stacksize, flags, codestring, constants, names,\n\ + varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])\n\ +\n\ +Create a code object. Not for the faint of heart."); + +static PyObject * +code_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + int argcount; + int nlocals; + int stacksize; + int flags; + PyObject *co; + PyObject *empty = NULL; + PyObject *code; + PyObject *consts; + PyObject *names; + PyObject *varnames; + PyObject *freevars = NULL; + PyObject *cellvars = NULL; + PyObject *filename; + PyObject *name; + int firstlineno; + PyObject *lnotab; + + if (!PyArg_ParseTuple(args, "iiiiSO!O!O!SSiS|O!O!:code", + &argcount, &nlocals, &stacksize, &flags, + &code, + &PyTuple_Type, &consts, + &PyTuple_Type, &names, + &PyTuple_Type, &varnames, + &filename, &name, + &firstlineno, &lnotab, + &PyTuple_Type, &freevars, + &PyTuple_Type, &cellvars)) + return NULL; + + if (!PyObject_CheckReadBuffer(code)) { + PyErr_SetString(PyExc_TypeError, + "bytecode object must be a single-segment read-only buffer"); + return NULL; + } + + if (freevars == NULL || cellvars == NULL) { + empty = PyTuple_New(0); + if (empty == NULL) + return NULL; + if (freevars == NULL) + freevars = empty; + if (cellvars == NULL) + cellvars = empty; + } + + co = (PyObject *) PyCode_New(argcount, nlocals, stacksize, flags, + code, consts, names, varnames, + freevars, cellvars, filename, name, + firstlineno, lnotab); + Py_XDECREF(empty); + return co; +} + +static void +code_dealloc(PyCodeObject *co) +{ + Py_XDECREF(co->co_code); + Py_XDECREF(co->co_consts); + Py_XDECREF(co->co_names); + Py_XDECREF(co->co_varnames); + Py_XDECREF(co->co_freevars); + Py_XDECREF(co->co_cellvars); + Py_XDECREF(co->co_filename); + Py_XDECREF(co->co_name); + Py_XDECREF(co->co_lnotab); + PyObject_DEL(co); +} + +static PyObject * +code_repr(PyCodeObject *co) +{ + char buf[500]; + int lineno = -1; + char *filename = "???"; + char *name = "???"; + + if (co->co_firstlineno != 0) + lineno = co->co_firstlineno; + if (co->co_filename && PyString_Check(co->co_filename)) + filename = PyString_AS_STRING(co->co_filename); + if (co->co_name && PyString_Check(co->co_name)) + name = PyString_AS_STRING(co->co_name); + PyOS_snprintf(buf, sizeof(buf), + "", + name, co, filename, lineno); + return PyString_FromString(buf); +} + +static int +code_compare(PyCodeObject *co, PyCodeObject *cp) +{ + int cmp; + cmp = PyObject_Compare(co->co_name, cp->co_name); + if (cmp) return cmp; + cmp = co->co_argcount - cp->co_argcount; + if (cmp) return (cmp<0)?-1:1; + cmp = co->co_nlocals - cp->co_nlocals; + if (cmp) return (cmp<0)?-1:1; + cmp = co->co_flags - cp->co_flags; + if (cmp) return (cmp<0)?-1:1; + cmp = PyObject_Compare(co->co_code, cp->co_code); + if (cmp) return cmp; + cmp = PyObject_Compare(co->co_consts, cp->co_consts); + if (cmp) return cmp; + cmp = PyObject_Compare(co->co_names, cp->co_names); + if (cmp) return cmp; + cmp = PyObject_Compare(co->co_varnames, cp->co_varnames); + if (cmp) return cmp; + cmp = PyObject_Compare(co->co_freevars, cp->co_freevars); + if (cmp) return cmp; + cmp = PyObject_Compare(co->co_cellvars, cp->co_cellvars); + return cmp; +} + +static long +code_hash(PyCodeObject *co) +{ + long h, h0, h1, h2, h3, h4, h5, h6; + h0 = PyObject_Hash(co->co_name); + if (h0 == -1) return -1; + h1 = PyObject_Hash(co->co_code); + if (h1 == -1) return -1; + h2 = PyObject_Hash(co->co_consts); + if (h2 == -1) return -1; + h3 = PyObject_Hash(co->co_names); + if (h3 == -1) return -1; + h4 = PyObject_Hash(co->co_varnames); + if (h4 == -1) return -1; + h5 = PyObject_Hash(co->co_freevars); + if (h5 == -1) return -1; + h6 = PyObject_Hash(co->co_cellvars); + if (h6 == -1) return -1; + h = h0 ^ h1 ^ h2 ^ h3 ^ h4 ^ h5 ^ h6 ^ + co->co_argcount ^ co->co_nlocals ^ co->co_flags; + if (h == -1) h = -2; + return h; +} + +/* XXX code objects need to participate in GC? */ + +PyTypeObject PyCode_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "code", + sizeof(PyCodeObject), + 0, + (destructor)code_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + (cmpfunc)code_compare, /* tp_compare */ + (reprfunc)code_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)code_hash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + code_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + code_memberlist, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + code_new, /* tp_new */ +}; + +#define NAME_CHARS \ + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" + +/* all_name_chars(s): true iff all chars in s are valid NAME_CHARS */ + +static int +all_name_chars(unsigned char *s) +{ + static char ok_name_char[256]; + static unsigned char *name_chars = (unsigned char *)NAME_CHARS; + + if (ok_name_char[*name_chars] == 0) { + unsigned char *p; + for (p = name_chars; *p; p++) + ok_name_char[*p] = 1; + } + while (*s) { + if (ok_name_char[*s++] == 0) + return 0; + } + return 1; +} + +static int +intern_strings(PyObject *tuple) +{ + int i; + + for (i = PyTuple_GET_SIZE(tuple); --i >= 0; ) { + PyObject *v = PyTuple_GET_ITEM(tuple, i); + if (v == NULL || !PyString_Check(v)) { + Py_FatalError("non-string found in code slot"); + PyErr_BadInternalCall(); + return -1; + } + PyString_InternInPlace(&PyTuple_GET_ITEM(tuple, i)); + } + return 0; +} + +#define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1])) +#define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD) +#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP) +#define GETJUMPTGT(arr, i) (GETARG(arr,i) + (ABSOLUTE_JUMP(arr[i]) ? 0 : i+3)) +#define SETARG(arr, i, val) arr[i+2] = val>>8; arr[i+1] = val & 255 + +static PyObject * +optimize_code(PyObject *code, PyObject* consts) +{ + int i, j, codelen; + int tgt, tgttgt, opcode; + unsigned char *codestr; + + /* Make a modifiable copy of the code string */ + if (!PyString_Check(code)) + goto exitUnchanged; + codelen = PyString_Size(code); + codestr = PyMem_Malloc(codelen); + if (codestr == NULL) + goto exitUnchanged; + codestr = memcpy(codestr, PyString_AS_STRING(code), codelen); + assert(PyTuple_Check(consts)); + + for (i=0 ; i= 0; ) { + PyObject *v = PyTuple_GetItem(consts, i); + if (!PyString_Check(v)) + continue; + if (!all_name_chars((unsigned char *)PyString_AS_STRING(v))) + continue; + PyString_InternInPlace(&PyTuple_GET_ITEM(consts, i)); + } + co = PyObject_NEW(PyCodeObject, &PyCode_Type); + if (co != NULL) { + co->co_argcount = argcount; + co->co_nlocals = nlocals; + co->co_stacksize = stacksize; + co->co_flags = flags; + co->co_code = optimize_code(code, consts); + Py_INCREF(consts); + co->co_consts = consts; + Py_INCREF(names); + co->co_names = names; + Py_INCREF(varnames); + co->co_varnames = varnames; + Py_INCREF(freevars); + co->co_freevars = freevars; + Py_INCREF(cellvars); + co->co_cellvars = cellvars; + Py_INCREF(filename); + co->co_filename = filename; + Py_INCREF(name); + co->co_name = name; + co->co_firstlineno = firstlineno; + Py_INCREF(lnotab); + co->co_lnotab = lnotab; + if (PyTuple_GET_SIZE(freevars) == 0 && + PyTuple_GET_SIZE(cellvars) == 0) + co->co_flags |= CO_NOFREE; + } + return co; +} + + +/* Data structure used internally */ + +/* The compiler uses two passes to generate bytecodes. The first pass + builds the symbol table. The second pass generates the bytecode. + + The first pass uses a single symtable struct. The second pass uses + a compiling struct for each code block. The compiling structs + share a reference to the symtable. + + The two passes communicate via symtable_load_symbols() and via + is_local() and is_global(). The former initializes several slots + in the compiling struct: c_varnames, c_locals, c_nlocals, + c_argcount, c_globals, and c_flags. +*/ + +/* All about c_lnotab. + +c_lnotab is an array of unsigned bytes disguised as a Python string. Since +version 2.3, SET_LINENO opcodes are never generated and bytecode offsets are +mapped to source code line #s via c_lnotab instead. + +The array is conceptually a list of + (bytecode offset increment, line number increment) +pairs. The details are important and delicate, best illustrated by example: + + byte code offset source code line number + 0 1 + 6 2 + 50 7 + 350 307 + 361 308 + +The first trick is that these numbers aren't stored, only the increments +from one row to the next (this doesn't really work, but it's a start): + + 0, 1, 6, 1, 44, 5, 300, 300, 11, 1 + +The second trick is that an unsigned byte can't hold negative values, or +values larger than 255, so (a) there's a deep assumption that byte code +offsets and their corresponding line #s both increase monotonically, and (b) +if at least one column jumps by more than 255 from one row to the next, more +than one pair is written to the table. In case #b, there's no way to know +from looking at the table later how many were written. That's the delicate +part. A user of c_lnotab desiring to find the source line number +corresponding to a bytecode address A should do something like this + + lineno = addr = 0 + for addr_incr, line_incr in c_lnotab: + addr += addr_incr + if addr > A: + return lineno + lineno += line_incr + +In order for this to work, when the addr field increments by more than 255, +the line # increment in each pair generated must be 0 until the remaining addr +increment is < 256. So, in the example above, com_set_lineno should not (as +was actually done until 2.2) expand 300, 300 to 255, 255, 45, 45, but to +255, 0, 45, 255, 0, 45. +*/ + +struct compiling { + PyObject *c_code; /* string */ + PyObject *c_consts; /* list of objects */ + PyObject *c_const_dict; /* inverse of c_consts */ + PyObject *c_names; /* list of strings (names) */ + PyObject *c_name_dict; /* inverse of c_names */ + PyObject *c_globals; /* dictionary (value=None or True) */ + PyObject *c_locals; /* dictionary (value=localID) */ + PyObject *c_varnames; /* list (inverse of c_locals) */ + PyObject *c_freevars; /* dictionary (value=None) */ + PyObject *c_cellvars; /* list */ + int c_nlocals; /* index of next local */ + int c_argcount; /* number of top-level arguments */ + int c_flags; /* same as co_flags */ + int c_nexti; /* index into c_code */ + int c_errors; /* counts errors occurred */ + int c_infunction; /* set when compiling a function */ + int c_interactive; /* generating code for interactive command */ + int c_loops; /* counts nested loops */ + int c_begin; /* begin of current loop, for 'continue' */ + int c_block[CO_MAXBLOCKS]; /* stack of block types */ + int c_nblocks; /* current block stack level */ + const char *c_filename; /* filename of current node */ + char *c_name; /* name of object (e.g. function) */ + int c_lineno; /* Current line number */ + int c_stacklevel; /* Current stack level */ + int c_maxstacklevel; /* Maximum stack level */ + int c_firstlineno; + PyObject *c_lnotab; /* Table mapping address to line number */ + int c_last_addr, c_last_line, c_lnotab_next; + char *c_private; /* for private name mangling */ + int c_tmpname; /* temporary local name counter */ + int c_nested; /* Is block nested funcdef or lamdef? */ + int c_closure; /* Is nested w/freevars? */ + struct symtable *c_symtable; /* pointer to module symbol table */ + PyFutureFeatures *c_future; /* pointer to module's __future__ */ + char *c_encoding; /* source encoding (a borrowed reference) */ +}; + +static int +is_free(int v) +{ + if ((v & (USE | DEF_FREE)) + && !(v & (DEF_LOCAL | DEF_PARAM | DEF_GLOBAL))) + return 1; + if (v & DEF_FREE_CLASS) + return 1; + return 0; +} + +static void +com_error(struct compiling *c, PyObject *exc, char *msg) +{ + PyObject *t = NULL, *v = NULL, *w = NULL, *line = NULL; + + if (c == NULL) { + /* Error occurred via symtable call to + is_constant_false */ + PyErr_SetString(exc, msg); + return; + } + c->c_errors++; + if (c->c_lineno < 1 || c->c_interactive) { + /* Unknown line number or interactive input */ + PyErr_SetString(exc, msg); + return; + } + v = PyString_FromString(msg); + if (v == NULL) + return; /* MemoryError, too bad */ + + line = PyErr_ProgramText(c->c_filename, c->c_lineno); + if (line == NULL) { + Py_INCREF(Py_None); + line = Py_None; + } + if (exc == PyExc_SyntaxError) { + t = Py_BuildValue("(ziOO)", c->c_filename, c->c_lineno, + Py_None, line); + if (t == NULL) + goto exit; + w = Py_BuildValue("(OO)", v, t); + if (w == NULL) + goto exit; + PyErr_SetObject(exc, w); + } else { + /* Make sure additional exceptions are printed with + file and line, also. */ + PyErr_SetObject(exc, v); + PyErr_SyntaxLocation(c->c_filename, c->c_lineno); + } + exit: + Py_XDECREF(t); + Py_XDECREF(v); + Py_XDECREF(w); + Py_XDECREF(line); +} + +/* Interface to the block stack */ + +static void +block_push(struct compiling *c, int type) +{ + if (c->c_nblocks >= CO_MAXBLOCKS) { + com_error(c, PyExc_SystemError, + "too many statically nested blocks"); + } + else { + c->c_block[c->c_nblocks++] = type; + } +} + +static void +block_pop(struct compiling *c, int type) +{ + if (c->c_nblocks > 0) + c->c_nblocks--; + if (c->c_block[c->c_nblocks] != type && c->c_errors == 0) { + com_error(c, PyExc_SystemError, "bad block pop"); + } +} + +/* Prototype forward declarations */ + +static int issue_warning(const char *, const char *, int); +static int com_init(struct compiling *, const char *); +static void com_free(struct compiling *); +static void com_push(struct compiling *, int); +static void com_pop(struct compiling *, int); +static void com_done(struct compiling *); +static void com_node(struct compiling *, node *); +static void com_factor(struct compiling *, node *); +static void com_addbyte(struct compiling *, int); +static void com_addint(struct compiling *, int); +static void com_addoparg(struct compiling *, int, int); +static void com_addfwref(struct compiling *, int, int *); +static void com_backpatch(struct compiling *, int); +static int com_add(struct compiling *, PyObject *, PyObject *, PyObject *); +static int com_addconst(struct compiling *, PyObject *); +static int com_addname(struct compiling *, PyObject *); +static void com_addopname(struct compiling *, int, node *); +static void com_list(struct compiling *, node *, int); +static void com_list_iter(struct compiling *, node *, node *, char *); +static int com_argdefs(struct compiling *, node *); +static void com_assign(struct compiling *, node *, int, node *); +static void com_assign_name(struct compiling *, node *, int); +static PyCodeObject *icompile(node *, struct compiling *); +static PyCodeObject *jcompile(node *, const char *, struct compiling *, + PyCompilerFlags *); +static PyObject *parsestrplus(struct compiling*, node *); +static PyObject *parsestr(struct compiling *, char *); +static node *get_rawdocstring(node *); + +static int get_ref_type(struct compiling *, char *); + +/* symtable operations */ +static struct symtable *symtable_build(node *, PyFutureFeatures *, + const char *filename); +static int symtable_load_symbols(struct compiling *); +static struct symtable *symtable_init(void); +static void symtable_enter_scope(struct symtable *, char *, int, int); +static int symtable_exit_scope(struct symtable *); +static int symtable_add_def(struct symtable *, char *, int); +static int symtable_add_def_o(struct symtable *, PyObject *, PyObject *, int); + +static void symtable_node(struct symtable *, node *); +static void symtable_funcdef(struct symtable *, node *); +static void symtable_default_args(struct symtable *, node *); +static void symtable_params(struct symtable *, node *); +static void symtable_params_fplist(struct symtable *, node *n); +static void symtable_global(struct symtable *, node *); +static void symtable_import(struct symtable *, node *); +static void symtable_assign(struct symtable *, node *, int); +static void symtable_list_comprehension(struct symtable *, node *); +static void symtable_list_for(struct symtable *, node *); + +static int symtable_update_free_vars(struct symtable *); +static int symtable_undo_free(struct symtable *, PyObject *, PyObject *); +static int symtable_check_global(struct symtable *, PyObject *, PyObject *); + +/* helper */ +static void +do_pad(int pad) +{ + int i; + for (i = 0; i < pad; ++i) + fprintf(stderr, " "); +} + +static void +dump(node *n, int pad, int depth) +{ + int i; + if (depth == 0) + return; + do_pad(pad); + fprintf(stderr, "%d: %s\n", TYPE(n), STR(n)); + if (depth > 0) + depth--; + for (i = 0; i < NCH(n); ++i) + dump(CHILD(n, i), pad + 1, depth); +} + +#define DUMP(N) dump(N, 0, -1) + +static int +com_init(struct compiling *c, const char *filename) +{ + memset((void *)c, '\0', sizeof(struct compiling)); + if ((c->c_code = PyString_FromStringAndSize((char *)NULL, + 1000)) == NULL) + goto fail; + if ((c->c_consts = PyList_New(0)) == NULL) + goto fail; + if ((c->c_const_dict = PyDict_New()) == NULL) + goto fail; + if ((c->c_names = PyList_New(0)) == NULL) + goto fail; + if ((c->c_name_dict = PyDict_New()) == NULL) + goto fail; + if ((c->c_locals = PyDict_New()) == NULL) + goto fail; + if ((c->c_lnotab = PyString_FromStringAndSize((char *)NULL, + 1000)) == NULL) + goto fail; + c->c_globals = NULL; + c->c_varnames = NULL; + c->c_freevars = NULL; + c->c_cellvars = NULL; + c->c_nlocals = 0; + c->c_argcount = 0; + c->c_flags = 0; + c->c_nexti = 0; + c->c_errors = 0; + c->c_infunction = 0; + c->c_interactive = 0; + c->c_loops = 0; + c->c_begin = 0; + c->c_nblocks = 0; + c->c_filename = filename; + c->c_name = "?"; + c->c_lineno = 0; + c->c_stacklevel = 0; + c->c_maxstacklevel = 0; + c->c_firstlineno = 0; + c->c_last_addr = 0; + c->c_last_line = 0; + c->c_lnotab_next = 0; + c->c_tmpname = 0; + c->c_nested = 0; + c->c_closure = 0; + c->c_symtable = NULL; + return 1; + + fail: + com_free(c); + return 0; +} + +static void +com_free(struct compiling *c) +{ + Py_XDECREF(c->c_code); + Py_XDECREF(c->c_consts); + Py_XDECREF(c->c_const_dict); + Py_XDECREF(c->c_names); + Py_XDECREF(c->c_name_dict); + Py_XDECREF(c->c_globals); + Py_XDECREF(c->c_locals); + Py_XDECREF(c->c_varnames); + Py_XDECREF(c->c_freevars); + Py_XDECREF(c->c_cellvars); + Py_XDECREF(c->c_lnotab); + if (c->c_future) + PyObject_FREE((void *)c->c_future); +} + +static void +com_push(struct compiling *c, int n) +{ + c->c_stacklevel += n; + if (c->c_stacklevel > c->c_maxstacklevel) { + c->c_maxstacklevel = c->c_stacklevel; + /* + fprintf(stderr, "%s:%s:%d max stack nexti=%d level=%d n=%d\n", + c->c_filename, c->c_name, c->c_lineno, + c->c_nexti, c->c_stacklevel, n); + */ + } +} + +static void +com_pop(struct compiling *c, int n) +{ + if (c->c_stacklevel < n) + c->c_stacklevel = 0; + else + c->c_stacklevel -= n; +} + +static void +com_done(struct compiling *c) +{ + if (c->c_code != NULL) + _PyString_Resize(&c->c_code, c->c_nexti); + if (c->c_lnotab != NULL) + _PyString_Resize(&c->c_lnotab, c->c_lnotab_next); +} + +static int +com_check_size(PyObject **s, int offset) +{ + int len = PyString_GET_SIZE(*s); + if (offset >= len) + return _PyString_Resize(s, len * 2); + return 0; +} + +static void +com_addbyte(struct compiling *c, int byte) +{ + /*fprintf(stderr, "%3d: %3d\n", c->c_nexti, byte);*/ + assert(byte >= 0 && byte <= 255); + assert(c->c_code != 0); + if (com_check_size(&c->c_code, c->c_nexti)) { + c->c_errors++; + return; + } + PyString_AS_STRING(c->c_code)[c->c_nexti++] = byte; +} + +static void +com_addint(struct compiling *c, int x) +{ + com_addbyte(c, x & 0xff); + com_addbyte(c, x >> 8); /* XXX x should be positive */ +} + +static void +com_add_lnotab(struct compiling *c, int addr, int line) +{ + char *p; + if (c->c_lnotab == NULL) + return; + if (com_check_size(&c->c_lnotab, c->c_lnotab_next + 2)) { + c->c_errors++; + return; + } + p = PyString_AS_STRING(c->c_lnotab) + c->c_lnotab_next; + *p++ = addr; + *p++ = line; + c->c_lnotab_next += 2; +} + +static void +com_set_lineno(struct compiling *c, int lineno) +{ + c->c_lineno = lineno; + if (c->c_firstlineno == 0) { + c->c_firstlineno = c->c_last_line = lineno; + } + else { + int incr_addr = c->c_nexti - c->c_last_addr; + int incr_line = lineno - c->c_last_line; + while (incr_addr > 255) { + com_add_lnotab(c, 255, 0); + incr_addr -= 255; + } + while (incr_line > 255) { + com_add_lnotab(c, incr_addr, 255); + incr_line -=255; + incr_addr = 0; + } + if (incr_addr > 0 || incr_line > 0) + com_add_lnotab(c, incr_addr, incr_line); + c->c_last_addr = c->c_nexti; + c->c_last_line = lineno; + } +} + +static void +com_addoparg(struct compiling *c, int op, int arg) +{ + int extended_arg = arg >> 16; + if (extended_arg){ + com_addbyte(c, EXTENDED_ARG); + com_addint(c, extended_arg); + arg &= 0xffff; + } + com_addbyte(c, op); + com_addint(c, arg); +} + +static void +com_addfwref(struct compiling *c, int op, int *p_anchor) +{ + /* Compile a forward reference for backpatching */ + int here; + int anchor; + com_addbyte(c, op); + here = c->c_nexti; + anchor = *p_anchor; + *p_anchor = here; + com_addint(c, anchor == 0 ? 0 : here - anchor); +} + +static void +com_backpatch(struct compiling *c, int anchor) +{ + unsigned char *code = (unsigned char *) PyString_AS_STRING(c->c_code); + int target = c->c_nexti; + int dist; + int prev; + for (;;) { + /* Make the JUMP instruction at anchor point to target */ + prev = code[anchor] + (code[anchor+1] << 8); + dist = target - (anchor+2); + code[anchor] = dist & 0xff; + dist >>= 8; + code[anchor+1] = dist; + dist >>= 8; + if (dist) { + com_error(c, PyExc_SystemError, + "com_backpatch: offset too large"); + break; + } + if (!prev) + break; + anchor -= prev; + } +} + +/* Handle literals and names uniformly */ + +static int +com_add(struct compiling *c, PyObject *list, PyObject *dict, PyObject *v) +{ + PyObject *w, *t, *np=NULL; + long n; + + t = Py_BuildValue("(OO)", v, v->ob_type); + if (t == NULL) + goto fail; + w = PyDict_GetItem(dict, t); + if (w != NULL) { + n = PyInt_AsLong(w); + } else { + n = PyList_Size(list); + np = PyInt_FromLong(n); + if (np == NULL) + goto fail; + if (PyList_Append(list, v) != 0) + goto fail; + if (PyDict_SetItem(dict, t, np) != 0) + goto fail; + Py_DECREF(np); + } + Py_DECREF(t); + return n; + fail: + Py_XDECREF(np); + Py_XDECREF(t); + c->c_errors++; + return 0; +} + +static int +com_addconst(struct compiling *c, PyObject *v) +{ + return com_add(c, c->c_consts, c->c_const_dict, v); +} + +static int +com_addname(struct compiling *c, PyObject *v) +{ + return com_add(c, c->c_names, c->c_name_dict, v); +} + +int +_Py_Mangle(char *p, char *name, char *buffer, size_t maxlen) +{ + /* Name mangling: __private becomes _classname__private. + This is independent from how the name is used. */ + size_t nlen, plen; + if (p == NULL || name == NULL || name[0] != '_' || name[1] != '_') + return 0; + nlen = strlen(name); + if (nlen+2 >= maxlen) + return 0; /* Don't mangle __extremely_long_names */ + if (name[nlen-1] == '_' && name[nlen-2] == '_') + return 0; /* Don't mangle __whatever__ */ + /* Strip leading underscores from class name */ + while (*p == '_') + p++; + if (*p == '\0') + return 0; /* Don't mangle if class is just underscores */ + plen = strlen(p); + if (plen + nlen >= maxlen) + plen = maxlen-nlen-2; /* Truncate class name if too long */ + /* buffer = "_" + p[:plen] + name # i.e. 1+plen+nlen bytes */ + buffer[0] = '_'; + strncpy(buffer+1, p, plen); + strcpy(buffer+1+plen, name); + return 1; +} + +static void +com_addop_name(struct compiling *c, int op, char *name) +{ + PyObject *v; + int i; + char buffer[MANGLE_LEN]; + + if (_Py_Mangle(c->c_private, name, buffer, sizeof(buffer))) + name = buffer; + if (name == NULL || (v = PyString_InternFromString(name)) == NULL) { + c->c_errors++; + i = 255; + } + else { + i = com_addname(c, v); + Py_DECREF(v); + } + com_addoparg(c, op, i); +} + +#define NAME_LOCAL 0 +#define NAME_GLOBAL 1 +#define NAME_DEFAULT 2 +#define NAME_CLOSURE 3 + +static int +com_lookup_arg(PyObject *dict, PyObject *name) +{ + PyObject *v = PyDict_GetItem(dict, name); + if (v == NULL) + return -1; + else + return PyInt_AS_LONG(v); +} + +static int +none_assignment_check(struct compiling *c, char *name, int assigning) +{ + if (name[0] == 'N' && strcmp(name, "None") == 0) { + char *msg; + if (assigning) + msg = "assignment to None"; + else + msg = "deleting None"; + if (issue_warning(msg, c->c_filename, c->c_lineno) < 0) { + c->c_errors++; + return -1; + } + } + return 0; +} + +static void +com_addop_varname(struct compiling *c, int kind, char *name) +{ + PyObject *v; + int i, reftype; + int scope = NAME_DEFAULT; + int op = STOP_CODE; + char buffer[MANGLE_LEN]; + + if (kind != VAR_LOAD && + none_assignment_check(c, name, kind == VAR_STORE)) + { + c->c_errors++; + i = 255; + goto done; + } + if (_Py_Mangle(c->c_private, name, buffer, sizeof(buffer))) + name = buffer; + if (name == NULL || (v = PyString_InternFromString(name)) == NULL) { + c->c_errors++; + i = 255; + goto done; + } + + reftype = get_ref_type(c, name); + switch (reftype) { + case LOCAL: + if (c->c_symtable->st_cur->ste_type == TYPE_FUNCTION) + scope = NAME_LOCAL; + break; + case GLOBAL_EXPLICIT: + scope = NAME_GLOBAL; + break; + case GLOBAL_IMPLICIT: + if (c->c_flags & CO_OPTIMIZED) + scope = NAME_GLOBAL; + break; + case FREE: + case CELL: + scope = NAME_CLOSURE; + break; + } + + i = com_addname(c, v); + if (scope == NAME_LOCAL) + i = com_lookup_arg(c->c_locals, v); + else if (reftype == FREE) + i = com_lookup_arg(c->c_freevars, v); + else if (reftype == CELL) + i = com_lookup_arg(c->c_cellvars, v); + if (i == -1) { + c->c_errors++; /* XXX no exception set */ + i = 255; + goto done; + } + Py_DECREF(v); + + switch (kind) { + case VAR_LOAD: + switch (scope) { + case NAME_LOCAL: + op = LOAD_FAST; + break; + case NAME_GLOBAL: + op = LOAD_GLOBAL; + break; + case NAME_DEFAULT: + op = LOAD_NAME; + break; + case NAME_CLOSURE: + op = LOAD_DEREF; + break; + } + break; + case VAR_STORE: + switch (scope) { + case NAME_LOCAL: + op = STORE_FAST; + break; + case NAME_GLOBAL: + op = STORE_GLOBAL; + break; + case NAME_DEFAULT: + op = STORE_NAME; + break; + case NAME_CLOSURE: + op = STORE_DEREF; + break; + } + break; + case VAR_DELETE: + switch (scope) { + case NAME_LOCAL: + op = DELETE_FAST; + break; + case NAME_GLOBAL: + op = DELETE_GLOBAL; + break; + case NAME_DEFAULT: + op = DELETE_NAME; + break; + case NAME_CLOSURE: { + char buf[500]; + PyOS_snprintf(buf, sizeof(buf), + DEL_CLOSURE_ERROR, name); + com_error(c, PyExc_SyntaxError, buf); + i = 255; + break; + } + } + break; + } +done: + com_addoparg(c, op, i); +} + +static void +com_addopname(struct compiling *c, int op, node *n) +{ + char *name; + char buffer[1000]; + /* XXX it is possible to write this code without the 1000 + chars on the total length of dotted names, I just can't be + bothered right now */ + if (TYPE(n) == STAR) + name = "*"; + else if (TYPE(n) == dotted_name) { + char *p = buffer; + int i; + name = buffer; + for (i = 0; i < NCH(n); i += 2) { + char *s = STR(CHILD(n, i)); + if (p + strlen(s) > buffer + (sizeof buffer) - 2) { + com_error(c, PyExc_MemoryError, + "dotted_name too long"); + name = NULL; + break; + } + if (p != buffer) + *p++ = '.'; + strcpy(p, s); + p = strchr(p, '\0'); + } + } + else { + REQ(n, NAME); + name = STR(n); + } + com_addop_name(c, op, name); +} + +static PyObject * +parsenumber(struct compiling *c, char *s) +{ + char *end; + long x; + double dx; +#ifndef WITHOUT_COMPLEX + int imflag; +#endif + + errno = 0; + end = s + strlen(s) - 1; +#ifndef WITHOUT_COMPLEX + imflag = *end == 'j' || *end == 'J'; +#endif + if (*end == 'l' || *end == 'L') + return PyLong_FromString(s, (char **)0, 0); + if (s[0] == '0') { + x = (long) PyOS_strtoul(s, &end, 0); + if (x < 0 && errno == 0) { + if (PyErr_WarnExplicit( + PyExc_FutureWarning, + "hex/oct constants > sys.maxint " + "will return positive values " + "in Python 2.4 and up", + /* XXX: Give WarnExplicit + a const char* argument. */ + (char*)c->c_filename, + c->c_lineno, + NULL, + NULL) < 0) + return NULL; + errno = 0; /* Might be changed by PyErr_Warn() */ + } + } + else + x = PyOS_strtol(s, &end, 0); + if (*end == '\0') { + if (errno != 0) + return PyLong_FromString(s, (char **)0, 0); + return PyInt_FromLong(x); + } + /* XXX Huge floats may silently fail */ +#ifndef WITHOUT_COMPLEX + if (imflag) { + Py_complex z; + z.real = 0.; + PyFPE_START_PROTECT("atof", return 0) + z.imag = atof(s); + PyFPE_END_PROTECT(z) + return PyComplex_FromCComplex(z); + } + else +#endif + { + PyFPE_START_PROTECT("atof", return 0) + dx = atof(s); + PyFPE_END_PROTECT(dx) + return PyFloat_FromDouble(dx); + } +} + +static PyObject * +decode_utf8(char **sPtr, char *end, char* encoding) +{ +#ifndef Py_USING_UNICODE + Py_FatalError("decode_utf8 should not be called in this build."); + return NULL; +#else + PyObject *u, *v; + char *s, *t; + t = s = *sPtr; + /* while (s < end && *s != '\\') s++; */ /* inefficient for u".." */ + while (s < end && (*s & 0x80)) s++; + *sPtr = s; + u = PyUnicode_DecodeUTF8(t, s - t, NULL); + if (u == NULL) + return NULL; + v = PyUnicode_AsEncodedString(u, encoding, NULL); + Py_DECREF(u); + return v; +#endif +} + +/* compiler.transformer.Transformer.decode_literal depends on what + might seem like minor details of this function -- changes here + must be reflected there. */ +static PyObject * +parsestr(struct compiling *c, char *s) +{ + PyObject *v; + size_t len; + int quote = *s; + int rawmode = 0; + char* encoding = ((c == NULL) ? NULL : c->c_encoding); + int need_encoding; + int unicode = 0; + + if (isalpha(quote) || quote == '_') { + if (quote == 'u' || quote == 'U') { + quote = *++s; + unicode = 1; + } + if (quote == 'r' || quote == 'R') { + quote = *++s; + rawmode = 1; + } + } + if (quote != '\'' && quote != '\"') { + PyErr_BadInternalCall(); + return NULL; + } + s++; + len = strlen(s); + if (len > INT_MAX) { + com_error(c, PyExc_OverflowError, + "string to parse is too long"); + return NULL; + } + if (s[--len] != quote) { + PyErr_BadInternalCall(); + return NULL; + } + if (len >= 4 && s[0] == quote && s[1] == quote) { + s += 2; + len -= 2; + if (s[--len] != quote || s[--len] != quote) { + PyErr_BadInternalCall(); + return NULL; + } + } +#ifdef Py_USING_UNICODE + if (unicode || Py_UnicodeFlag) { + PyObject *u, *w; + char *buf; + char *p; + char *end; + if (encoding == NULL) { + buf = s; + u = NULL; + } else if (strcmp(encoding, "iso-8859-1") == 0) { + buf = s; + u = NULL; + } else { + /* "\XX" may become "\u005c\uHHLL" (12 bytes) */ + u = PyString_FromStringAndSize((char *)NULL, len * 4); + if (u == NULL) + return NULL; + p = buf = PyString_AsString(u); + end = s + len; + while (s < end) { + if (*s == '\\') { + *p++ = *s++; + if (*s & 0x80) { + strcpy(p, "u005c"); + p += 5; + } + } + if (*s & 0x80) { /* XXX inefficient */ + char *r; + int rn, i; + w = decode_utf8(&s, end, "utf-16-be"); + if (w == NULL) { + Py_DECREF(u); + return NULL; + } + r = PyString_AsString(w); + rn = PyString_Size(w); + assert(rn % 2 == 0); + for (i = 0; i < rn; i += 2) { + sprintf(p, "\\u%02x%02x", + r[i + 0] & 0xFF, + r[i + 1] & 0xFF); + p += 6; + } + Py_DECREF(w); + } else { + *p++ = *s++; + } + } + len = p - buf; + } + if (rawmode) + v = PyUnicode_DecodeRawUnicodeEscape(buf, len, NULL); + else + v = PyUnicode_DecodeUnicodeEscape(buf, len, NULL); + Py_XDECREF(u); + if (v == NULL) + PyErr_SyntaxLocation(c->c_filename, c->c_lineno); + return v; + + } +#endif + need_encoding = (encoding != NULL && + strcmp(encoding, "utf-8") != 0 && + strcmp(encoding, "iso-8859-1") != 0); + if (rawmode || strchr(s, '\\') == NULL) { + if (need_encoding) { +#ifndef Py_USING_UNICODE + /* This should not happen - we never see any other + encoding. */ + Py_FatalError("cannot deal with encodings in this build."); +#else + PyObject* u = PyUnicode_DecodeUTF8(s, len, NULL); + if (u == NULL) + return NULL; + v = PyUnicode_AsEncodedString(u, encoding, NULL); + Py_DECREF(u); + return v; +#endif + } else { + return PyString_FromStringAndSize(s, len); + } + } + + v = PyString_DecodeEscape(s, len, NULL, unicode, + need_encoding ? encoding : NULL); + if (v == NULL) + PyErr_SyntaxLocation(c->c_filename, c->c_lineno); + return v; +} + +static PyObject * +parsestrplus(struct compiling* c, node *n) +{ + PyObject *v; + int i; + REQ(CHILD(n, 0), STRING); + if ((v = parsestr(c, STR(CHILD(n, 0)))) != NULL) { + /* String literal concatenation */ + for (i = 1; i < NCH(n); i++) { + PyObject *s; + s = parsestr(c, STR(CHILD(n, i))); + if (s == NULL) + goto onError; + if (PyString_Check(v) && PyString_Check(s)) { + PyString_ConcatAndDel(&v, s); + if (v == NULL) + goto onError; + } +#ifdef Py_USING_UNICODE + else { + PyObject *temp; + temp = PyUnicode_Concat(v, s); + Py_DECREF(s); + if (temp == NULL) + goto onError; + Py_DECREF(v); + v = temp; + } +#endif + } + } + return v; + + onError: + Py_XDECREF(v); + return NULL; +} + +static void +com_list_for(struct compiling *c, node *n, node *e, char *t) +{ + int anchor = 0; + int save_begin = c->c_begin; + + /* list_iter: for v in expr [list_iter] */ + com_node(c, CHILD(n, 3)); /* expr */ + com_addbyte(c, GET_ITER); + c->c_begin = c->c_nexti; + com_addfwref(c, FOR_ITER, &anchor); + com_push(c, 1); + com_assign(c, CHILD(n, 1), OP_ASSIGN, NULL); + c->c_loops++; + com_list_iter(c, n, e, t); + c->c_loops--; + com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); + c->c_begin = save_begin; + com_backpatch(c, anchor); + com_pop(c, 1); /* FOR_ITER has popped this */ +} + +static void +com_list_if(struct compiling *c, node *n, node *e, char *t) +{ + int anchor = 0; + int a = 0; + /* list_iter: 'if' test [list_iter] */ + com_node(c, CHILD(n, 1)); + com_addfwref(c, JUMP_IF_FALSE, &a); + com_addbyte(c, POP_TOP); + com_pop(c, 1); + com_list_iter(c, n, e, t); + com_addfwref(c, JUMP_FORWARD, &anchor); + com_backpatch(c, a); + /* We jump here with an extra entry which we now pop */ + com_addbyte(c, POP_TOP); + com_backpatch(c, anchor); +} + +static void +com_list_iter(struct compiling *c, + node *p, /* parent of list_iter node */ + node *e, /* element expression node */ + char *t /* name of result list temp local */) +{ + /* list_iter is the last child in a listmaker, list_for, or list_if */ + node *n = CHILD(p, NCH(p)-1); + if (TYPE(n) == list_iter) { + n = CHILD(n, 0); + switch (TYPE(n)) { + case list_for: + com_list_for(c, n, e, t); + break; + case list_if: + com_list_if(c, n, e, t); + break; + default: + com_error(c, PyExc_SystemError, + "invalid list_iter node type"); + } + } + else { + com_addop_varname(c, VAR_LOAD, t); + com_push(c, 1); + com_node(c, e); + com_addoparg(c, CALL_FUNCTION, 1); + com_addbyte(c, POP_TOP); + com_pop(c, 2); + } +} + +static void +com_list_comprehension(struct compiling *c, node *n) +{ + /* listmaker: test list_for */ + char tmpname[30]; + + REQ(n, listmaker); + PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", ++c->c_tmpname); + com_addoparg(c, BUILD_LIST, 0); + com_addbyte(c, DUP_TOP); /* leave the result on the stack */ + com_push(c, 2); + com_addop_name(c, LOAD_ATTR, "append"); + com_addop_varname(c, VAR_STORE, tmpname); + com_pop(c, 1); + com_list_for(c, CHILD(n, 1), CHILD(n, 0), tmpname); + com_addop_varname(c, VAR_DELETE, tmpname); + --c->c_tmpname; +} + +static void +com_listmaker(struct compiling *c, node *n) +{ + /* listmaker: test ( list_for | (',' test)* [','] ) */ + if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) + com_list_comprehension(c, n); + else { + int len = 0; + int i; + for (i = 0; i < NCH(n); i += 2, len++) + com_node(c, CHILD(n, i)); + com_addoparg(c, BUILD_LIST, len); + com_pop(c, len-1); + } +} + +static void +com_dictmaker(struct compiling *c, node *n) +{ + int i; + /* dictmaker: test ':' test (',' test ':' value)* [','] */ + for (i = 0; i+2 < NCH(n); i += 4) { + /* We must arrange things just right for STORE_SUBSCR. + It wants the stack to look like (value) (dict) (key) */ + com_addbyte(c, DUP_TOP); + com_push(c, 1); + com_node(c, CHILD(n, i)); /* key */ + com_node(c, CHILD(n, i+2)); /* value */ + com_addbyte(c, ROT_THREE); + com_addbyte(c, STORE_SUBSCR); + com_pop(c, 3); + } +} + +static void +com_atom(struct compiling *c, node *n) +{ + node *ch; + PyObject *v; + int i; + REQ(n, atom); + ch = CHILD(n, 0); + switch (TYPE(ch)) { + case LPAR: + if (TYPE(CHILD(n, 1)) == RPAR) { + com_addoparg(c, BUILD_TUPLE, 0); + com_push(c, 1); + } + else + com_node(c, CHILD(n, 1)); + break; + case LSQB: /* '[' [listmaker] ']' */ + if (TYPE(CHILD(n, 1)) == RSQB) { + com_addoparg(c, BUILD_LIST, 0); + com_push(c, 1); + } + else + com_listmaker(c, CHILD(n, 1)); + break; + case LBRACE: /* '{' [dictmaker] '}' */ + com_addoparg(c, BUILD_MAP, 0); + com_push(c, 1); + if (TYPE(CHILD(n, 1)) == dictmaker) + com_dictmaker(c, CHILD(n, 1)); + break; + case BACKQUOTE: + com_node(c, CHILD(n, 1)); + com_addbyte(c, UNARY_CONVERT); + break; + case NUMBER: + if ((v = parsenumber(c, STR(ch))) == NULL) { + i = 255; + } + else { + i = com_addconst(c, v); + Py_DECREF(v); + } + com_addoparg(c, LOAD_CONST, i); + com_push(c, 1); + break; + case STRING: + v = parsestrplus(c, n); + if (v == NULL) { + c->c_errors++; + i = 255; + } + else { + i = com_addconst(c, v); + Py_DECREF(v); + } + com_addoparg(c, LOAD_CONST, i); + com_push(c, 1); + break; + case NAME: + com_addop_varname(c, VAR_LOAD, STR(ch)); + com_push(c, 1); + break; + default: + com_error(c, PyExc_SystemError, + "com_atom: unexpected node type"); + } +} + +static void +com_slice(struct compiling *c, node *n, int op) +{ + if (NCH(n) == 1) { + com_addbyte(c, op); + } + else if (NCH(n) == 2) { + if (TYPE(CHILD(n, 0)) != COLON) { + com_node(c, CHILD(n, 0)); + com_addbyte(c, op+1); + } + else { + com_node(c, CHILD(n, 1)); + com_addbyte(c, op+2); + } + com_pop(c, 1); + } + else { + com_node(c, CHILD(n, 0)); + com_node(c, CHILD(n, 2)); + com_addbyte(c, op+3); + com_pop(c, 2); + } +} + +static void +com_augassign_slice(struct compiling *c, node *n, int opcode, node *augn) +{ + if (NCH(n) == 1) { + com_addbyte(c, DUP_TOP); + com_push(c, 1); + com_addbyte(c, SLICE); + com_node(c, augn); + com_addbyte(c, opcode); + com_pop(c, 1); + com_addbyte(c, ROT_TWO); + com_addbyte(c, STORE_SLICE); + com_pop(c, 2); + } else if (NCH(n) == 2 && TYPE(CHILD(n, 0)) != COLON) { + com_node(c, CHILD(n, 0)); + com_addoparg(c, DUP_TOPX, 2); + com_push(c, 2); + com_addbyte(c, SLICE+1); + com_pop(c, 1); + com_node(c, augn); + com_addbyte(c, opcode); + com_pop(c, 1); + com_addbyte(c, ROT_THREE); + com_addbyte(c, STORE_SLICE+1); + com_pop(c, 3); + } else if (NCH(n) == 2) { + com_node(c, CHILD(n, 1)); + com_addoparg(c, DUP_TOPX, 2); + com_push(c, 2); + com_addbyte(c, SLICE+2); + com_pop(c, 1); + com_node(c, augn); + com_addbyte(c, opcode); + com_pop(c, 1); + com_addbyte(c, ROT_THREE); + com_addbyte(c, STORE_SLICE+2); + com_pop(c, 3); + } else { + com_node(c, CHILD(n, 0)); + com_node(c, CHILD(n, 2)); + com_addoparg(c, DUP_TOPX, 3); + com_push(c, 3); + com_addbyte(c, SLICE+3); + com_pop(c, 2); + com_node(c, augn); + com_addbyte(c, opcode); + com_pop(c, 1); + com_addbyte(c, ROT_FOUR); + com_addbyte(c, STORE_SLICE+3); + com_pop(c, 4); + } +} + +static void +com_argument(struct compiling *c, node *n, PyObject **pkeywords) +{ + node *m; + REQ(n, argument); /* [test '='] test; really [keyword '='] test */ + if (NCH(n) == 1) { + if (*pkeywords != NULL) { + com_error(c, PyExc_SyntaxError, + "non-keyword arg after keyword arg"); + } + else { + com_node(c, CHILD(n, 0)); + } + return; + } + m = n; + do { + m = CHILD(m, 0); + } while (NCH(m) == 1); + if (TYPE(m) != NAME) { + /* f(lambda x: x[0] = 3) ends up getting parsed with + * LHS test = lambda x: x[0], and RHS test = 3. + * SF bug 132313 points out that complaining about a keyword + * then is very confusing. + */ + com_error(c, PyExc_SyntaxError, + TYPE(m) == lambdef ? + "lambda cannot contain assignment" : + "keyword can't be an expression"); + } + else { + PyObject *v = PyString_InternFromString(STR(m)); + (void) none_assignment_check(c, STR(m), 1); + if (v != NULL && *pkeywords == NULL) + *pkeywords = PyDict_New(); + if (v == NULL) + c->c_errors++; + else if (*pkeywords == NULL) { + c->c_errors++; + Py_DECREF(v); + } else { + if (PyDict_GetItem(*pkeywords, v) != NULL) + com_error(c, PyExc_SyntaxError, + "duplicate keyword argument"); + else + if (PyDict_SetItem(*pkeywords, v, v) != 0) + c->c_errors++; + com_addoparg(c, LOAD_CONST, com_addconst(c, v)); + com_push(c, 1); + Py_DECREF(v); + } + } + com_node(c, CHILD(n, 2)); +} + +static void +com_call_function(struct compiling *c, node *n) +{ + if (TYPE(n) == RPAR) { + com_addoparg(c, CALL_FUNCTION, 0); + } + else { + PyObject *keywords = NULL; + int i, na, nk; + int lineno = n->n_lineno; + int star_flag = 0; + int starstar_flag = 0; + int opcode; + REQ(n, arglist); + na = 0; + nk = 0; + for (i = 0; i < NCH(n); i += 2) { + node *ch = CHILD(n, i); + if (TYPE(ch) == STAR || + TYPE(ch) == DOUBLESTAR) + break; + if (ch->n_lineno != lineno) { + lineno = ch->n_lineno; + com_set_lineno(c, lineno); + } + com_argument(c, ch, &keywords); + if (keywords == NULL) + na++; + else + nk++; + } + Py_XDECREF(keywords); + while (i < NCH(n)) { + node *tok = CHILD(n, i); + node *ch = CHILD(n, i+1); + i += 3; + switch (TYPE(tok)) { + case STAR: star_flag = 1; break; + case DOUBLESTAR: starstar_flag = 1; break; + } + com_node(c, ch); + } + if (na > 255 || nk > 255) { + com_error(c, PyExc_SyntaxError, + "more than 255 arguments"); + } + if (star_flag || starstar_flag) + opcode = CALL_FUNCTION_VAR - 1 + + star_flag + (starstar_flag << 1); + else + opcode = CALL_FUNCTION; + com_addoparg(c, opcode, na | (nk << 8)); + com_pop(c, na + 2*nk + star_flag + starstar_flag); + } +} + +static void +com_select_member(struct compiling *c, node *n) +{ + com_addopname(c, LOAD_ATTR, n); +} + +static void +com_sliceobj(struct compiling *c, node *n) +{ + int i=0; + int ns=2; /* number of slice arguments */ + node *ch; + + /* first argument */ + if (TYPE(CHILD(n,i)) == COLON) { + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); + com_push(c, 1); + i++; + } + else { + com_node(c, CHILD(n,i)); + i++; + REQ(CHILD(n,i),COLON); + i++; + } + /* second argument */ + if (i < NCH(n) && TYPE(CHILD(n,i)) == test) { + com_node(c, CHILD(n,i)); + i++; + } + else { + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); + com_push(c, 1); + } + /* remaining arguments */ + for (; i < NCH(n); i++) { + ns++; + ch=CHILD(n,i); + REQ(ch, sliceop); + if (NCH(ch) == 1) { + /* right argument of ':' missing */ + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); + com_push(c, 1); + } + else + com_node(c, CHILD(ch,1)); + } + com_addoparg(c, BUILD_SLICE, ns); + com_pop(c, 1 + (ns == 3)); +} + +static void +com_subscript(struct compiling *c, node *n) +{ + node *ch; + REQ(n, subscript); + ch = CHILD(n,0); + /* check for rubber index */ + if (TYPE(ch) == DOT && TYPE(CHILD(n,1)) == DOT) { + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_Ellipsis)); + com_push(c, 1); + } + else { + /* check for slice */ + if ((TYPE(ch) == COLON || NCH(n) > 1)) + com_sliceobj(c, n); + else { + REQ(ch, test); + com_node(c, ch); + } + } +} + +static void +com_subscriptlist(struct compiling *c, node *n, int assigning, node *augn) +{ + int i, op; + REQ(n, subscriptlist); + /* Check to make backward compatible slice behavior for '[i:j]' */ + if (NCH(n) == 1) { + node *sub = CHILD(n, 0); /* subscript */ + /* 'Basic' slice, should have exactly one colon. */ + if ((TYPE(CHILD(sub, 0)) == COLON + || (NCH(sub) > 1 && TYPE(CHILD(sub, 1)) == COLON)) + && (TYPE(CHILD(sub,NCH(sub)-1)) != sliceop)) + { + switch (assigning) { + case OP_DELETE: + op = DELETE_SLICE; + break; + case OP_ASSIGN: + op = STORE_SLICE; + break; + case OP_APPLY: + op = SLICE; + break; + default: + com_augassign_slice(c, sub, assigning, augn); + return; + } + com_slice(c, sub, op); + if (op == STORE_SLICE) + com_pop(c, 2); + else if (op == DELETE_SLICE) + com_pop(c, 1); + return; + } + } + /* Else normal subscriptlist. Compile each subscript. */ + for (i = 0; i < NCH(n); i += 2) + com_subscript(c, CHILD(n, i)); + /* Put multiple subscripts into a tuple */ + if (NCH(n) > 1) { + i = (NCH(n)+1) / 2; + com_addoparg(c, BUILD_TUPLE, i); + com_pop(c, i-1); + } + switch (assigning) { + case OP_DELETE: + op = DELETE_SUBSCR; + i = 2; + break; + default: + case OP_ASSIGN: + op = STORE_SUBSCR; + i = 3; + break; + case OP_APPLY: + op = BINARY_SUBSCR; + i = 1; + break; + } + if (assigning > OP_APPLY) { + com_addoparg(c, DUP_TOPX, 2); + com_push(c, 2); + com_addbyte(c, BINARY_SUBSCR); + com_pop(c, 1); + com_node(c, augn); + com_addbyte(c, assigning); + com_pop(c, 1); + com_addbyte(c, ROT_THREE); + } + com_addbyte(c, op); + com_pop(c, i); +} + +static void +com_apply_trailer(struct compiling *c, node *n) +{ + REQ(n, trailer); + switch (TYPE(CHILD(n, 0))) { + case LPAR: + com_call_function(c, CHILD(n, 1)); + break; + case DOT: + com_select_member(c, CHILD(n, 1)); + break; + case LSQB: + com_subscriptlist(c, CHILD(n, 1), OP_APPLY, NULL); + break; + default: + com_error(c, PyExc_SystemError, + "com_apply_trailer: unknown trailer type"); + } +} + +static void +com_power(struct compiling *c, node *n) +{ + int i; + REQ(n, power); + com_atom(c, CHILD(n, 0)); + for (i = 1; i < NCH(n); i++) { + if (TYPE(CHILD(n, i)) == DOUBLESTAR) { + com_factor(c, CHILD(n, i+1)); + com_addbyte(c, BINARY_POWER); + com_pop(c, 1); + break; + } + else + com_apply_trailer(c, CHILD(n, i)); + } +} + +static void +com_invert_constant(struct compiling *c, node *n) +{ + /* Compute the inverse of int and longs and use them directly, + but be prepared to generate code for all other + possibilities (invalid numbers, floats, complex). + */ + PyObject *num, *inv = NULL; + int i; + + REQ(n, NUMBER); + num = parsenumber(c, STR(n)); + if (num == NULL) + i = 255; + else { + inv = PyNumber_Invert(num); + if (inv == NULL) { + PyErr_Clear(); + i = com_addconst(c, num); + } else { + i = com_addconst(c, inv); + Py_DECREF(inv); + } + Py_DECREF(num); + } + com_addoparg(c, LOAD_CONST, i); + com_push(c, 1); + if (num != NULL && inv == NULL) + com_addbyte(c, UNARY_INVERT); +} + +static int +is_float_zero(const char *p) +{ + int found_radix_point = 0; + int ch; + while ((ch = Py_CHARMASK(*p++)) != '\0') { + switch (ch) { + case '0': + /* no reason to believe it's not 0 -- continue */ + break; + + case 'e': case 'E': case 'j': case 'J': + /* If this was a hex constant, we already would have + returned 0 due to the 'x' or 'X', so 'e' or 'E' + must be an exponent marker, and we haven't yet + seen a non-zero digit, and it doesn't matter what + the exponent is then. For 'j' or 'J' similarly, + except that this is an imaginary 0 then. */ + return 1; + + case '.': + found_radix_point = 1; + break; + + default: + return 0; + } + } + return found_radix_point; +} + +static void +com_factor(struct compiling *c, node *n) +{ + int childtype = TYPE(CHILD(n, 0)); + node *pfactor, *ppower, *patom, *pnum; + REQ(n, factor); + /* If the unary +, -, or ~ operator is applied to a constant, + don't generate a UNARY_xxx opcode. Just store the + approriate value as a constant. If the value is negative, + extend the string containing the constant and insert a + negative in the 0th position -- unless we're doing unary minus + of a floating zero! In that case the sign is significant, but + the const dict can't distinguish +0.0 from -0.0. + */ + if ((childtype == PLUS || childtype == MINUS || childtype == TILDE) + && NCH(n) == 2 + && TYPE((pfactor = CHILD(n, 1))) == factor + && NCH(pfactor) == 1 + && TYPE((ppower = CHILD(pfactor, 0))) == power + && NCH(ppower) == 1 + && TYPE((patom = CHILD(ppower, 0))) == atom + && TYPE((pnum = CHILD(patom, 0))) == NUMBER + && !(childtype == MINUS && + (STR(pnum)[0] == '0' || is_float_zero(STR(pnum))))) { + if (childtype == TILDE) { + com_invert_constant(c, pnum); + return; + } + if (childtype == MINUS) { + char *s = PyObject_MALLOC(strlen(STR(pnum)) + 2); + if (s == NULL) { + com_error(c, PyExc_MemoryError, ""); + com_addbyte(c, 255); + return; + } + s[0] = '-'; + strcpy(s + 1, STR(pnum)); + PyObject_FREE(STR(pnum)); + STR(pnum) = s; + } + com_atom(c, patom); + } + else if (childtype == PLUS) { + com_factor(c, CHILD(n, 1)); + com_addbyte(c, UNARY_POSITIVE); + } + else if (childtype == MINUS) { + com_factor(c, CHILD(n, 1)); + com_addbyte(c, UNARY_NEGATIVE); + } + else if (childtype == TILDE) { + com_factor(c, CHILD(n, 1)); + com_addbyte(c, UNARY_INVERT); + } + else { + com_power(c, CHILD(n, 0)); + } +} + +static void +com_term(struct compiling *c, node *n) +{ + int i; + int op; + REQ(n, term); + com_factor(c, CHILD(n, 0)); + for (i = 2; i < NCH(n); i += 2) { + com_factor(c, CHILD(n, i)); + switch (TYPE(CHILD(n, i-1))) { + case STAR: + op = BINARY_MULTIPLY; + break; + case SLASH: + if (c->c_flags & CO_FUTURE_DIVISION) + op = BINARY_TRUE_DIVIDE; + else + op = BINARY_DIVIDE; + break; + case PERCENT: + op = BINARY_MODULO; + break; + case DOUBLESLASH: + op = BINARY_FLOOR_DIVIDE; + break; + default: + com_error(c, PyExc_SystemError, + "com_term: operator not *, /, // or %"); + op = 255; + } + com_addbyte(c, op); + com_pop(c, 1); + } +} + +static void +com_arith_expr(struct compiling *c, node *n) +{ + int i; + int op; + REQ(n, arith_expr); + com_term(c, CHILD(n, 0)); + for (i = 2; i < NCH(n); i += 2) { + com_term(c, CHILD(n, i)); + switch (TYPE(CHILD(n, i-1))) { + case PLUS: + op = BINARY_ADD; + break; + case MINUS: + op = BINARY_SUBTRACT; + break; + default: + com_error(c, PyExc_SystemError, + "com_arith_expr: operator not + or -"); + op = 255; + } + com_addbyte(c, op); + com_pop(c, 1); + } +} + +static void +com_shift_expr(struct compiling *c, node *n) +{ + int i; + int op; + REQ(n, shift_expr); + com_arith_expr(c, CHILD(n, 0)); + for (i = 2; i < NCH(n); i += 2) { + com_arith_expr(c, CHILD(n, i)); + switch (TYPE(CHILD(n, i-1))) { + case LEFTSHIFT: + op = BINARY_LSHIFT; + break; + case RIGHTSHIFT: + op = BINARY_RSHIFT; + break; + default: + com_error(c, PyExc_SystemError, + "com_shift_expr: operator not << or >>"); + op = 255; + } + com_addbyte(c, op); + com_pop(c, 1); + } +} + +static void +com_and_expr(struct compiling *c, node *n) +{ + int i; + int op; + REQ(n, and_expr); + com_shift_expr(c, CHILD(n, 0)); + for (i = 2; i < NCH(n); i += 2) { + com_shift_expr(c, CHILD(n, i)); + if (TYPE(CHILD(n, i-1)) == AMPER) { + op = BINARY_AND; + } + else { + com_error(c, PyExc_SystemError, + "com_and_expr: operator not &"); + op = 255; + } + com_addbyte(c, op); + com_pop(c, 1); + } +} + +static void +com_xor_expr(struct compiling *c, node *n) +{ + int i; + int op; + REQ(n, xor_expr); + com_and_expr(c, CHILD(n, 0)); + for (i = 2; i < NCH(n); i += 2) { + com_and_expr(c, CHILD(n, i)); + if (TYPE(CHILD(n, i-1)) == CIRCUMFLEX) { + op = BINARY_XOR; + } + else { + com_error(c, PyExc_SystemError, + "com_xor_expr: operator not ^"); + op = 255; + } + com_addbyte(c, op); + com_pop(c, 1); + } +} + +static void +com_expr(struct compiling *c, node *n) +{ + int i; + int op; + REQ(n, expr); + com_xor_expr(c, CHILD(n, 0)); + for (i = 2; i < NCH(n); i += 2) { + com_xor_expr(c, CHILD(n, i)); + if (TYPE(CHILD(n, i-1)) == VBAR) { + op = BINARY_OR; + } + else { + com_error(c, PyExc_SystemError, + "com_expr: expr operator not |"); + op = 255; + } + com_addbyte(c, op); + com_pop(c, 1); + } +} + +static enum cmp_op +cmp_type(node *n) +{ + REQ(n, comp_op); + /* comp_op: '<' | '>' | '>=' | '<=' | '<>' | '!=' | '==' + | 'in' | 'not' 'in' | 'is' | 'is' not' */ + if (NCH(n) == 1) { + n = CHILD(n, 0); + switch (TYPE(n)) { + case LESS: return PyCmp_LT; + case GREATER: return PyCmp_GT; + case EQEQUAL: return PyCmp_EQ; + case LESSEQUAL: return PyCmp_LE; + case GREATEREQUAL: return PyCmp_GE; + case NOTEQUAL: return PyCmp_NE; /* <> or != */ + case NAME: if (strcmp(STR(n), "in") == 0) return PyCmp_IN; + if (strcmp(STR(n), "is") == 0) return PyCmp_IS; + } + } + else if (NCH(n) == 2) { + switch (TYPE(CHILD(n, 0))) { + case NAME: if (strcmp(STR(CHILD(n, 1)), "in") == 0) + return PyCmp_NOT_IN; + if (strcmp(STR(CHILD(n, 0)), "is") == 0) + return PyCmp_IS_NOT; + } + } + return PyCmp_BAD; +} + +static void +com_comparison(struct compiling *c, node *n) +{ + int i; + enum cmp_op op; + int anchor; + REQ(n, comparison); /* comparison: expr (comp_op expr)* */ + com_expr(c, CHILD(n, 0)); + if (NCH(n) == 1) + return; + + /**************************************************************** + The following code is generated for all but the last + comparison in a chain: + + label: on stack: opcode: jump to: + + a + a, b DUP_TOP + a, b, b ROT_THREE + b, a, b COMPARE_OP + b, 0-or-1 JUMP_IF_FALSE L1 + b, 1 POP_TOP + b + + We are now ready to repeat this sequence for the next + comparison in the chain. + + For the last we generate: + + b + b, c COMPARE_OP + 0-or-1 + + If there were any jumps to L1 (i.e., there was more than one + comparison), we generate: + + 0-or-1 JUMP_FORWARD L2 + L1: b, 0 ROT_TWO + 0, b POP_TOP + 0 + L2: 0-or-1 + ****************************************************************/ + + anchor = 0; + + for (i = 2; i < NCH(n); i += 2) { + com_expr(c, CHILD(n, i)); + if (i+2 < NCH(n)) { + com_addbyte(c, DUP_TOP); + com_push(c, 1); + com_addbyte(c, ROT_THREE); + } + op = cmp_type(CHILD(n, i-1)); + if (op == PyCmp_BAD) { + com_error(c, PyExc_SystemError, + "com_comparison: unknown comparison op"); + } + com_addoparg(c, COMPARE_OP, op); + com_pop(c, 1); + if (i+2 < NCH(n)) { + com_addfwref(c, JUMP_IF_FALSE, &anchor); + com_addbyte(c, POP_TOP); + com_pop(c, 1); + } + } + + if (anchor) { + int anchor2 = 0; + com_addfwref(c, JUMP_FORWARD, &anchor2); + com_backpatch(c, anchor); + com_addbyte(c, ROT_TWO); + com_addbyte(c, POP_TOP); + com_backpatch(c, anchor2); + } +} + +static void +com_not_test(struct compiling *c, node *n) +{ + REQ(n, not_test); /* 'not' not_test | comparison */ + if (NCH(n) == 1) { + com_comparison(c, CHILD(n, 0)); + } + else { + com_not_test(c, CHILD(n, 1)); + com_addbyte(c, UNARY_NOT); + } +} + +static void +com_and_test(struct compiling *c, node *n) +{ + int i; + int anchor; + REQ(n, and_test); /* not_test ('and' not_test)* */ + anchor = 0; + i = 0; + for (;;) { + com_not_test(c, CHILD(n, i)); + if ((i += 2) >= NCH(n)) + break; + com_addfwref(c, JUMP_IF_FALSE, &anchor); + com_addbyte(c, POP_TOP); + com_pop(c, 1); + } + if (anchor) + com_backpatch(c, anchor); +} + +static int +com_make_closure(struct compiling *c, PyCodeObject *co) +{ + int i, free = PyCode_GetNumFree(co); + if (free == 0) + return 0; + for (i = 0; i < free; ++i) { + /* Bypass com_addop_varname because it will generate + LOAD_DEREF but LOAD_CLOSURE is needed. + */ + PyObject *name = PyTuple_GET_ITEM(co->co_freevars, i); + int arg, reftype; + + /* Special case: If a class contains a method with a + free variable that has the same name as a method, + the name will be considered free *and* local in the + class. It should be handled by the closure, as + well as by the normal name loookup logic. + */ + reftype = get_ref_type(c, PyString_AS_STRING(name)); + if (reftype == CELL) + arg = com_lookup_arg(c->c_cellvars, name); + else /* (reftype == FREE) */ + arg = com_lookup_arg(c->c_freevars, name); + if (arg == -1) { + fprintf(stderr, "lookup %s in %s %d %d\n" + "freevars of %s: %s\n", + PyObject_REPR(name), + c->c_name, + reftype, arg, + PyString_AS_STRING(co->co_name), + PyObject_REPR(co->co_freevars)); + Py_FatalError("com_make_closure()"); + } + com_addoparg(c, LOAD_CLOSURE, arg); + + } + com_push(c, free); + return 1; +} + +static void +com_test(struct compiling *c, node *n) +{ + REQ(n, test); /* and_test ('or' and_test)* | lambdef */ + if (NCH(n) == 1 && TYPE(CHILD(n, 0)) == lambdef) { + PyCodeObject *co; + int i, closure; + int ndefs = com_argdefs(c, CHILD(n, 0)); + symtable_enter_scope(c->c_symtable, "lambda", lambdef, + n->n_lineno); + co = icompile(CHILD(n, 0), c); + if (co == NULL) { + c->c_errors++; + return; + } + symtable_exit_scope(c->c_symtable); + i = com_addconst(c, (PyObject *)co); + closure = com_make_closure(c, co); + com_addoparg(c, LOAD_CONST, i); + com_push(c, 1); + if (closure) { + com_addoparg(c, MAKE_CLOSURE, ndefs); + com_pop(c, PyCode_GetNumFree(co)); + } else + com_addoparg(c, MAKE_FUNCTION, ndefs); + Py_DECREF(co); + com_pop(c, ndefs); + } + else { + int anchor = 0; + int i = 0; + for (;;) { + com_and_test(c, CHILD(n, i)); + if ((i += 2) >= NCH(n)) + break; + com_addfwref(c, JUMP_IF_TRUE, &anchor); + com_addbyte(c, POP_TOP); + com_pop(c, 1); + } + if (anchor) + com_backpatch(c, anchor); + } +} + +static void +com_list(struct compiling *c, node *n, int toplevel) +{ + /* exprlist: expr (',' expr)* [',']; likewise for testlist */ + if (NCH(n) == 1 && !toplevel) { + com_node(c, CHILD(n, 0)); + } + else { + int i; + int len; + len = (NCH(n) + 1) / 2; + for (i = 0; i < NCH(n); i += 2) + com_node(c, CHILD(n, i)); + com_addoparg(c, BUILD_TUPLE, len); + com_pop(c, len-1); + } +} + + +/* Begin of assignment compilation */ + + +static void +com_augassign_attr(struct compiling *c, node *n, int opcode, node *augn) +{ + com_addbyte(c, DUP_TOP); + com_push(c, 1); + com_addopname(c, LOAD_ATTR, n); + com_node(c, augn); + com_addbyte(c, opcode); + com_pop(c, 1); + com_addbyte(c, ROT_TWO); + com_addopname(c, STORE_ATTR, n); + com_pop(c, 2); +} + +static void +com_assign_attr(struct compiling *c, node *n, int assigning) +{ + if (none_assignment_check(c, STR(n), assigning)) + return; + com_addopname(c, assigning ? STORE_ATTR : DELETE_ATTR, n); + com_pop(c, assigning ? 2 : 1); +} + +static void +com_assign_trailer(struct compiling *c, node *n, int assigning, node *augn) +{ + REQ(n, trailer); + switch (TYPE(CHILD(n, 0))) { + case LPAR: /* '(' [exprlist] ')' */ + if (assigning == OP_DELETE) + com_error(c, PyExc_SyntaxError, + "can't delete function call"); + else + com_error(c, PyExc_SyntaxError, + "can't assign to function call"); + break; + case DOT: /* '.' NAME */ + if (assigning > OP_APPLY) + com_augassign_attr(c, CHILD(n, 1), assigning, augn); + else + com_assign_attr(c, CHILD(n, 1), assigning); + break; + case LSQB: /* '[' subscriptlist ']' */ + com_subscriptlist(c, CHILD(n, 1), assigning, augn); + break; + default: + com_error(c, PyExc_SystemError, "unknown trailer type"); + } +} + +static void +com_assign_sequence(struct compiling *c, node *n, int assigning) +{ + int i; + if (TYPE(n) != testlist && TYPE(n) != listmaker) + REQ(n, exprlist); + if (assigning) { + i = (NCH(n)+1)/2; + com_addoparg(c, UNPACK_SEQUENCE, i); + com_push(c, i-1); + } + for (i = 0; i < NCH(n); i += 2) + com_assign(c, CHILD(n, i), assigning, NULL); +} + +static void +com_augassign_name(struct compiling *c, node *n, int opcode, node *augn) +{ + REQ(n, NAME); + com_addop_varname(c, VAR_LOAD, STR(n)); + com_push(c, 1); + com_node(c, augn); + com_addbyte(c, opcode); + com_pop(c, 1); + com_assign_name(c, n, OP_ASSIGN); +} + +static void +com_assign_name(struct compiling *c, node *n, int assigning) +{ + REQ(n, NAME); + com_addop_varname(c, assigning ? VAR_STORE : VAR_DELETE, STR(n)); + if (assigning) + com_pop(c, 1); +} + +static void +com_assign(struct compiling *c, node *n, int assigning, node *augn) +{ + /* Loop to avoid trivial recursion */ + for (;;) { + switch (TYPE(n)) { + + case exprlist: + case testlist: + case testlist1: + if (NCH(n) > 1) { + if (assigning > OP_APPLY) { + com_error(c, PyExc_SyntaxError, + "augmented assign to tuple not possible"); + return; + } + com_assign_sequence(c, n, assigning); + return; + } + n = CHILD(n, 0); + break; + + case test: + case and_test: + case not_test: + case comparison: + case expr: + case xor_expr: + case and_expr: + case shift_expr: + case arith_expr: + case term: + case factor: + if (NCH(n) > 1) { + com_error(c, PyExc_SyntaxError, + "can't assign to operator"); + return; + } + n = CHILD(n, 0); + break; + + case power: /* atom trailer* ('**' power)* + ('+'|'-'|'~') factor | atom trailer* */ + if (TYPE(CHILD(n, 0)) != atom) { + com_error(c, PyExc_SyntaxError, + "can't assign to operator"); + return; + } + if (NCH(n) > 1) { /* trailer or exponent present */ + int i; + com_node(c, CHILD(n, 0)); + for (i = 1; i+1 < NCH(n); i++) { + if (TYPE(CHILD(n, i)) == DOUBLESTAR) { + com_error(c, PyExc_SyntaxError, + "can't assign to operator"); + return; + } + com_apply_trailer(c, CHILD(n, i)); + } /* NB i is still alive */ + com_assign_trailer(c, + CHILD(n, i), assigning, augn); + return; + } + n = CHILD(n, 0); + break; + + case atom: + switch (TYPE(CHILD(n, 0))) { + case LPAR: + n = CHILD(n, 1); + if (TYPE(n) == RPAR) { + /* XXX Should allow () = () ??? */ + com_error(c, PyExc_SyntaxError, + "can't assign to ()"); + return; + } + if (assigning > OP_APPLY) { + com_error(c, PyExc_SyntaxError, + "augmented assign to tuple literal not possible"); + return; + } + break; + case LSQB: + n = CHILD(n, 1); + if (TYPE(n) == RSQB) { + com_error(c, PyExc_SyntaxError, + "can't assign to []"); + return; + } + if (assigning > OP_APPLY) { + com_error(c, PyExc_SyntaxError, + "augmented assign to list literal not possible"); + return; + } + if (NCH(n) > 1 + && TYPE(CHILD(n, 1)) == list_for) { + com_error(c, PyExc_SyntaxError, + "can't assign to list comprehension"); + return; + } + com_assign_sequence(c, n, assigning); + return; + case NAME: + if (assigning > OP_APPLY) + com_augassign_name(c, CHILD(n, 0), + assigning, augn); + else + com_assign_name(c, CHILD(n, 0), + assigning); + return; + default: + com_error(c, PyExc_SyntaxError, + "can't assign to literal"); + return; + } + break; + + case lambdef: + com_error(c, PyExc_SyntaxError, + "can't assign to lambda"); + return; + + default: + com_error(c, PyExc_SystemError, + "com_assign: bad node"); + return; + + } + } +} + +static void +com_augassign(struct compiling *c, node *n) +{ + int opcode; + + switch (STR(CHILD(CHILD(n, 1), 0))[0]) { + case '+': opcode = INPLACE_ADD; break; + case '-': opcode = INPLACE_SUBTRACT; break; + case '/': + if (STR(CHILD(CHILD(n, 1), 0))[1] == '/') + opcode = INPLACE_FLOOR_DIVIDE; + else if (c->c_flags & CO_FUTURE_DIVISION) + opcode = INPLACE_TRUE_DIVIDE; + else + opcode = INPLACE_DIVIDE; + break; + case '%': opcode = INPLACE_MODULO; break; + case '<': opcode = INPLACE_LSHIFT; break; + case '>': opcode = INPLACE_RSHIFT; break; + case '&': opcode = INPLACE_AND; break; + case '^': opcode = INPLACE_XOR; break; + case '|': opcode = INPLACE_OR; break; + case '*': + if (STR(CHILD(CHILD(n, 1), 0))[1] == '*') + opcode = INPLACE_POWER; + else + opcode = INPLACE_MULTIPLY; + break; + default: + com_error(c, PyExc_SystemError, "com_augassign: bad operator"); + return; + } + com_assign(c, CHILD(n, 0), opcode, CHILD(n, 2)); +} + +static void +com_expr_stmt(struct compiling *c, node *n) +{ + REQ(n, expr_stmt); + /* testlist (('=' testlist)* | augassign testlist) */ + /* Forget it if we have just a doc string here */ + if (!c->c_interactive && NCH(n) == 1 && get_rawdocstring(n) != NULL) + return; + if (NCH(n) == 1) { + com_node(c, CHILD(n, NCH(n)-1)); + if (c->c_interactive) + com_addbyte(c, PRINT_EXPR); + else + com_addbyte(c, POP_TOP); + com_pop(c, 1); + } + else if (TYPE(CHILD(n,1)) == augassign) + com_augassign(c, n); + else { + int i; + com_node(c, CHILD(n, NCH(n)-1)); + for (i = 0; i < NCH(n)-2; i+=2) { + if (i+2 < NCH(n)-2) { + com_addbyte(c, DUP_TOP); + com_push(c, 1); + } + com_assign(c, CHILD(n, i), OP_ASSIGN, NULL); + } + } +} + +static void +com_assert_stmt(struct compiling *c, node *n) +{ + int a = 0; + int i; + REQ(n, assert_stmt); /* 'assert' test [',' test] */ + if (Py_OptimizeFlag) + return; + /* Generate code like + + if not : + raise AssertionError [, ] + + where is the second test, if present. + */ + com_node(c, CHILD(n, 1)); + com_addfwref(c, JUMP_IF_TRUE, &a); + com_addbyte(c, POP_TOP); + com_pop(c, 1); + /* Raise that exception! */ + com_addop_name(c, LOAD_GLOBAL, "AssertionError"); + com_push(c, 1); + i = NCH(n)/2; /* Either 2 or 4 */ + if (i > 1) + com_node(c, CHILD(n, 3)); + com_addoparg(c, RAISE_VARARGS, i); + com_pop(c, i); + /* The interpreter does not fall through */ + /* Jump ends up here */ + com_backpatch(c, a); + com_addbyte(c, POP_TOP); +} + +static void +com_print_stmt(struct compiling *c, node *n) +{ + int i = 1; + node* stream = NULL; + + REQ(n, print_stmt); /* 'print' (test ',')* [test] */ + + /* are we using the extended print form? */ + if (NCH(n) >= 2 && TYPE(CHILD(n, 1)) == RIGHTSHIFT) { + stream = CHILD(n, 2); + com_node(c, stream); + /* stack: [...] => [... stream] */ + com_push(c, 1); + if (NCH(n) > 3 && TYPE(CHILD(n, 3)) == COMMA) + i = 4; + else + i = 3; + } + for (; i < NCH(n); i += 2) { + if (stream != NULL) { + com_addbyte(c, DUP_TOP); + /* stack: [stream] => [stream stream] */ + com_push(c, 1); + com_node(c, CHILD(n, i)); + /* stack: [stream stream] => [stream stream obj] */ + com_addbyte(c, ROT_TWO); + /* stack: [stream stream obj] => [stream obj stream] */ + com_addbyte(c, PRINT_ITEM_TO); + /* stack: [stream obj stream] => [stream] */ + com_pop(c, 2); + } + else { + com_node(c, CHILD(n, i)); + /* stack: [...] => [... obj] */ + com_addbyte(c, PRINT_ITEM); + com_pop(c, 1); + } + } + /* XXX Alternatively, LOAD_CONST '\n' and then PRINT_ITEM */ + if (TYPE(CHILD(n, NCH(n)-1)) == COMMA) { + if (stream != NULL) { + /* must pop the extra stream object off the stack */ + com_addbyte(c, POP_TOP); + /* stack: [... stream] => [...] */ + com_pop(c, 1); + } + } + else { + if (stream != NULL) { + /* this consumes the last stream object on stack */ + com_addbyte(c, PRINT_NEWLINE_TO); + /* stack: [... stream] => [...] */ + com_pop(c, 1); + } + else + com_addbyte(c, PRINT_NEWLINE); + } +} + +static void +com_return_stmt(struct compiling *c, node *n) +{ + REQ(n, return_stmt); /* 'return' [testlist] */ + if (!c->c_infunction) { + com_error(c, PyExc_SyntaxError, "'return' outside function"); + } + if (c->c_flags & CO_GENERATOR) { + if (NCH(n) > 1) { + com_error(c, PyExc_SyntaxError, + "'return' with argument inside generator"); + } + } + if (NCH(n) < 2) { + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); + com_push(c, 1); + } + else + com_node(c, CHILD(n, 1)); + com_addbyte(c, RETURN_VALUE); + com_pop(c, 1); +} + +static void +com_yield_stmt(struct compiling *c, node *n) +{ + int i; + REQ(n, yield_stmt); /* 'yield' testlist */ + if (!c->c_infunction) { + com_error(c, PyExc_SyntaxError, "'yield' outside function"); + } + + for (i = 0; i < c->c_nblocks; ++i) { + if (c->c_block[i] == SETUP_FINALLY) { + com_error(c, PyExc_SyntaxError, + "'yield' not allowed in a 'try' block " + "with a 'finally' clause"); + return; + } + } + com_node(c, CHILD(n, 1)); + com_addbyte(c, YIELD_VALUE); + com_pop(c, 1); +} + +static void +com_raise_stmt(struct compiling *c, node *n) +{ + int i; + REQ(n, raise_stmt); /* 'raise' [test [',' test [',' test]]] */ + if (NCH(n) > 1) { + com_node(c, CHILD(n, 1)); + if (NCH(n) > 3) { + com_node(c, CHILD(n, 3)); + if (NCH(n) > 5) + com_node(c, CHILD(n, 5)); + } + } + i = NCH(n)/2; + com_addoparg(c, RAISE_VARARGS, i); + com_pop(c, i); +} + +static void +com_from_import(struct compiling *c, node *n) +{ + com_addopname(c, IMPORT_FROM, CHILD(n, 0)); + com_push(c, 1); + if (NCH(n) > 1) { + if (strcmp(STR(CHILD(n, 1)), "as") != 0) { + com_error(c, PyExc_SyntaxError, "invalid syntax"); + return; + } + com_addop_varname(c, VAR_STORE, STR(CHILD(n, 2))); + } else + com_addop_varname(c, VAR_STORE, STR(CHILD(n, 0))); + com_pop(c, 1); +} + +static void +com_import_stmt(struct compiling *c, node *n) +{ + int i; + REQ(n, import_stmt); + /* 'import' dotted_name (',' dotted_name)* | + 'from' dotted_name 'import' ('*' | NAME (',' NAME)*) */ + if (STR(CHILD(n, 0))[0] == 'f') { + PyObject *tup; + /* 'from' dotted_name 'import' ... */ + REQ(CHILD(n, 1), dotted_name); + + if (TYPE(CHILD(n, 3)) == STAR) { + tup = Py_BuildValue("(s)", "*"); + } else { + tup = PyTuple_New((NCH(n) - 2)/2); + for (i = 3; i < NCH(n); i += 2) { + PyTuple_SET_ITEM(tup, (i-3)/2, + PyString_FromString(STR( + CHILD(CHILD(n, i), 0)))); + } + } + com_addoparg(c, LOAD_CONST, com_addconst(c, tup)); + Py_DECREF(tup); + com_push(c, 1); + com_addopname(c, IMPORT_NAME, CHILD(n, 1)); + if (TYPE(CHILD(n, 3)) == STAR) + com_addbyte(c, IMPORT_STAR); + else { + for (i = 3; i < NCH(n); i += 2) + com_from_import(c, CHILD(n, i)); + com_addbyte(c, POP_TOP); + } + com_pop(c, 1); + } + else { + /* 'import' ... */ + for (i = 1; i < NCH(n); i += 2) { + node *subn = CHILD(n, i); + REQ(subn, dotted_as_name); + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); + com_push(c, 1); + com_addopname(c, IMPORT_NAME, CHILD(subn, 0)); + if (NCH(subn) > 1) { + int j; + if (strcmp(STR(CHILD(subn, 1)), "as") != 0) { + com_error(c, PyExc_SyntaxError, + "invalid syntax"); + return; + } + for (j=2 ; j < NCH(CHILD(subn, 0)); j += 2) + com_addopname(c, LOAD_ATTR, + CHILD(CHILD(subn, 0), + j)); + com_addop_varname(c, VAR_STORE, + STR(CHILD(subn, 2))); + } else + com_addop_varname(c, VAR_STORE, + STR(CHILD(CHILD(subn, 0), + 0))); + com_pop(c, 1); + } + } +} + +static void +com_exec_stmt(struct compiling *c, node *n) +{ + REQ(n, exec_stmt); + /* exec_stmt: 'exec' expr ['in' expr [',' expr]] */ + com_node(c, CHILD(n, 1)); + if (NCH(n) >= 4) + com_node(c, CHILD(n, 3)); + else { + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); + com_push(c, 1); + } + if (NCH(n) >= 6) + com_node(c, CHILD(n, 5)); + else { + com_addbyte(c, DUP_TOP); + com_push(c, 1); + } + com_addbyte(c, EXEC_STMT); + com_pop(c, 3); +} + +static int +is_constant_false(struct compiling *c, node *n) +{ + PyObject *v; + int i; + /* argument c will be NULL when called from symtable_node() */ + + /* Label to avoid tail recursion */ + next: + switch (TYPE(n)) { + + case suite: + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto next; + } + /* Fall through */ + case file_input: + for (i = 0; i < NCH(n); i++) { + node *ch = CHILD(n, i); + if (TYPE(ch) == stmt) { + n = ch; + goto next; + } + } + break; + + case stmt: + case simple_stmt: + case small_stmt: + n = CHILD(n, 0); + goto next; + + case expr_stmt: + case testlist: + case testlist1: + case test: + case and_test: + case not_test: + case comparison: + case expr: + case xor_expr: + case and_expr: + case shift_expr: + case arith_expr: + case term: + case factor: + case power: + case atom: + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto next; + } + break; + + case NAME: + if (Py_OptimizeFlag && strcmp(STR(n), "__debug__") == 0) + return 1; + break; + + case NUMBER: + v = parsenumber(c, STR(n)); + if (v == NULL) { + PyErr_Clear(); + break; + } + i = PyObject_IsTrue(v); + Py_DECREF(v); + return i == 0; + + case STRING: + v = parsestr(c, STR(n)); + if (v == NULL) { + PyErr_Clear(); + break; + } + i = PyObject_IsTrue(v); + Py_DECREF(v); + return i == 0; + + } + return 0; +} + + +/* Look under n for a return stmt with an expression. + * This hack is used to find illegal returns under "if 0:" blocks in + * functions already known to be generators (as determined by the symtable + * pass). + * Return the offending return node if found, else NULL. + */ +static node * +look_for_offending_return(node *n) +{ + int i; + + for (i = 0; i < NCH(n); ++i) { + node *kid = CHILD(n, i); + + switch (TYPE(kid)) { + case classdef: + case funcdef: + case lambdef: + /* Stuff in nested functions & classes doesn't + affect the code block we started in. */ + return NULL; + + case return_stmt: + if (NCH(kid) > 1) + return kid; + break; + + default: { + node *bad = look_for_offending_return(kid); + if (bad != NULL) + return bad; + } + } + } + + return NULL; +} + +static void +com_if_stmt(struct compiling *c, node *n) +{ + int i; + int anchor = 0; + REQ(n, if_stmt); + /*'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] */ + for (i = 0; i+3 < NCH(n); i+=4) { + int a = 0; + node *ch = CHILD(n, i+1); + if (is_constant_false(c, ch)) { + /* We're going to skip this block. However, if this + is a generator, we have to check the dead code + anyway to make sure there aren't any return stmts + with expressions, in the same scope. */ + if (c->c_flags & CO_GENERATOR) { + node *p = look_for_offending_return(n); + if (p != NULL) { + int savelineno = c->c_lineno; + c->c_lineno = p->n_lineno; + com_error(c, PyExc_SyntaxError, + "'return' with argument " + "inside generator"); + c->c_lineno = savelineno; + } + } + continue; + } + if (i > 0) + com_set_lineno(c, ch->n_lineno); + com_node(c, ch); + com_addfwref(c, JUMP_IF_FALSE, &a); + com_addbyte(c, POP_TOP); + com_pop(c, 1); + com_node(c, CHILD(n, i+3)); + com_addfwref(c, JUMP_FORWARD, &anchor); + com_backpatch(c, a); + /* We jump here with an extra entry which we now pop */ + com_addbyte(c, POP_TOP); + } + if (i+2 < NCH(n)) + com_node(c, CHILD(n, i+2)); + if (anchor) + com_backpatch(c, anchor); +} + +static void +com_while_stmt(struct compiling *c, node *n) +{ + int break_anchor = 0; + int anchor = 0; + int save_begin = c->c_begin; + REQ(n, while_stmt); /* 'while' test ':' suite ['else' ':' suite] */ + com_addfwref(c, SETUP_LOOP, &break_anchor); + block_push(c, SETUP_LOOP); + c->c_begin = c->c_nexti; + com_set_lineno(c, n->n_lineno); + com_node(c, CHILD(n, 1)); + com_addfwref(c, JUMP_IF_FALSE, &anchor); + com_addbyte(c, POP_TOP); + com_pop(c, 1); + c->c_loops++; + com_node(c, CHILD(n, 3)); + c->c_loops--; + com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); + c->c_begin = save_begin; + com_backpatch(c, anchor); + /* We jump here with one entry more on the stack */ + com_addbyte(c, POP_TOP); + com_addbyte(c, POP_BLOCK); + block_pop(c, SETUP_LOOP); + if (NCH(n) > 4) + com_node(c, CHILD(n, 6)); + com_backpatch(c, break_anchor); +} + +static void +com_for_stmt(struct compiling *c, node *n) +{ + int break_anchor = 0; + int anchor = 0; + int save_begin = c->c_begin; + REQ(n, for_stmt); + /* 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] */ + com_addfwref(c, SETUP_LOOP, &break_anchor); + block_push(c, SETUP_LOOP); + com_node(c, CHILD(n, 3)); + com_addbyte(c, GET_ITER); + c->c_begin = c->c_nexti; + com_set_lineno(c, c->c_last_line); + com_addfwref(c, FOR_ITER, &anchor); + com_push(c, 1); + com_assign(c, CHILD(n, 1), OP_ASSIGN, NULL); + c->c_loops++; + com_node(c, CHILD(n, 5)); + c->c_loops--; + com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); + c->c_begin = save_begin; + com_backpatch(c, anchor); + com_pop(c, 1); /* FOR_ITER has popped this */ + com_addbyte(c, POP_BLOCK); + block_pop(c, SETUP_LOOP); + if (NCH(n) > 8) + com_node(c, CHILD(n, 8)); + com_backpatch(c, break_anchor); +} + +/* Code generated for "try: S finally: Sf" is as follows: + + SETUP_FINALLY L + + POP_BLOCK + LOAD_CONST + L: + END_FINALLY + + The special instructions use the block stack. Each block + stack entry contains the instruction that created it (here + SETUP_FINALLY), the level of the value stack at the time the + block stack entry was created, and a label (here L). + + SETUP_FINALLY: + Pushes the current value stack level and the label + onto the block stack. + POP_BLOCK: + Pops en entry from the block stack, and pops the value + stack until its level is the same as indicated on the + block stack. (The label is ignored.) + END_FINALLY: + Pops a variable number of entries from the *value* stack + and re-raises the exception they specify. The number of + entries popped depends on the (pseudo) exception type. + + The block stack is unwound when an exception is raised: + when a SETUP_FINALLY entry is found, the exception is pushed + onto the value stack (and the exception condition is cleared), + and the interpreter jumps to the label gotten from the block + stack. + + Code generated for "try: S except E1, V1: S1 except E2, V2: S2 ...": + (The contents of the value stack is shown in [], with the top + at the right; 'tb' is trace-back info, 'val' the exception's + associated value, and 'exc' the exception.) + + Value stack Label Instruction Argument + [] SETUP_EXCEPT L1 + [] + [] POP_BLOCK + [] JUMP_FORWARD L0 + + [tb, val, exc] L1: DUP ) + [tb, val, exc, exc] ) + [tb, val, exc, exc, E1] COMPARE_OP EXC_MATCH ) only if E1 + [tb, val, exc, 1-or-0] JUMP_IF_FALSE L2 ) + [tb, val, exc, 1] POP ) + [tb, val, exc] POP + [tb, val] (or POP if no V1) + [tb] POP + [] + JUMP_FORWARD L0 + + [tb, val, exc, 0] L2: POP + [tb, val, exc] DUP + .............................etc....................... + + [tb, val, exc, 0] Ln+1: POP + [tb, val, exc] END_FINALLY # re-raise exception + + [] L0: + + Of course, parts are not generated if Vi or Ei is not present. +*/ + +static void +com_try_except(struct compiling *c, node *n) +{ + int except_anchor = 0; + int end_anchor = 0; + int else_anchor = 0; + int i; + node *ch; + + com_addfwref(c, SETUP_EXCEPT, &except_anchor); + block_push(c, SETUP_EXCEPT); + com_node(c, CHILD(n, 2)); + com_addbyte(c, POP_BLOCK); + block_pop(c, SETUP_EXCEPT); + com_addfwref(c, JUMP_FORWARD, &else_anchor); + com_backpatch(c, except_anchor); + for (i = 3; + i < NCH(n) && TYPE(ch = CHILD(n, i)) == except_clause; + i += 3) { + /* except_clause: 'except' [expr [',' var]] */ + if (except_anchor == 0) { + com_error(c, PyExc_SyntaxError, + "default 'except:' must be last"); + break; + } + except_anchor = 0; + com_push(c, 3); /* tb, val, exc pushed by exception */ + com_set_lineno(c, ch->n_lineno); + if (NCH(ch) > 1) { + com_addbyte(c, DUP_TOP); + com_push(c, 1); + com_node(c, CHILD(ch, 1)); + com_addoparg(c, COMPARE_OP, PyCmp_EXC_MATCH); + com_pop(c, 1); + com_addfwref(c, JUMP_IF_FALSE, &except_anchor); + com_addbyte(c, POP_TOP); + com_pop(c, 1); + } + com_addbyte(c, POP_TOP); + com_pop(c, 1); + if (NCH(ch) > 3) + com_assign(c, CHILD(ch, 3), OP_ASSIGN, NULL); + else { + com_addbyte(c, POP_TOP); + com_pop(c, 1); + } + com_addbyte(c, POP_TOP); + com_pop(c, 1); + com_node(c, CHILD(n, i+2)); + com_addfwref(c, JUMP_FORWARD, &end_anchor); + if (except_anchor) { + com_backpatch(c, except_anchor); + /* We come in with [tb, val, exc, 0] on the + stack; one pop and it's the same as + expected at the start of the loop */ + com_addbyte(c, POP_TOP); + } + } + /* We actually come in here with [tb, val, exc] but the + END_FINALLY will zap those and jump around. + The c_stacklevel does not reflect them so we need not pop + anything. */ + com_addbyte(c, END_FINALLY); + com_backpatch(c, else_anchor); + if (i < NCH(n)) + com_node(c, CHILD(n, i+2)); + com_backpatch(c, end_anchor); +} + +static void +com_try_finally(struct compiling *c, node *n) +{ + int finally_anchor = 0; + node *ch; + + com_addfwref(c, SETUP_FINALLY, &finally_anchor); + block_push(c, SETUP_FINALLY); + com_node(c, CHILD(n, 2)); + com_addbyte(c, POP_BLOCK); + block_pop(c, SETUP_FINALLY); + block_push(c, END_FINALLY); + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); + /* While the generated code pushes only one item, + the try-finally handling can enter here with + up to three items. OK, here are the details: + 3 for an exception, 2 for RETURN, 1 for BREAK. */ + com_push(c, 3); + com_backpatch(c, finally_anchor); + ch = CHILD(n, NCH(n)-1); + com_set_lineno(c, ch->n_lineno); + com_node(c, ch); + com_addbyte(c, END_FINALLY); + block_pop(c, END_FINALLY); + com_pop(c, 3); /* Matches the com_push above */ +} + +static void +com_try_stmt(struct compiling *c, node *n) +{ + REQ(n, try_stmt); + /* 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite] + | 'try' ':' suite 'finally' ':' suite */ + if (TYPE(CHILD(n, 3)) != except_clause) + com_try_finally(c, n); + else + com_try_except(c, n); +} + +static node * +get_rawdocstring(node *n) +{ + int i; + + /* Label to avoid tail recursion */ + next: + switch (TYPE(n)) { + + case suite: + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto next; + } + /* Fall through */ + case file_input: + for (i = 0; i < NCH(n); i++) { + node *ch = CHILD(n, i); + if (TYPE(ch) == stmt) { + n = ch; + goto next; + } + } + break; + + case stmt: + case simple_stmt: + case small_stmt: + n = CHILD(n, 0); + goto next; + + case expr_stmt: + case testlist: + case testlist1: + case test: + case and_test: + case not_test: + case comparison: + case expr: + case xor_expr: + case and_expr: + case shift_expr: + case arith_expr: + case term: + case factor: + case power: + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto next; + } + break; + + case atom: + if (TYPE(CHILD(n, 0)) == STRING) + return n; + break; + + } + return NULL; +} + +static PyObject * +get_docstring(struct compiling *c, node *n) +{ + /* Don't generate doc-strings if run with -OO */ + if (Py_OptimizeFlag > 1) + return NULL; + n = get_rawdocstring(n); + if (n == NULL) + return NULL; + return parsestrplus(c, n); +} + +static void +com_suite(struct compiling *c, node *n) +{ + REQ(n, suite); + /* simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT */ + if (NCH(n) == 1) { + com_node(c, CHILD(n, 0)); + } + else { + int i; + for (i = 0; i < NCH(n) && c->c_errors == 0; i++) { + node *ch = CHILD(n, i); + if (TYPE(ch) == stmt) + com_node(c, ch); + } + } +} + +/* ARGSUSED */ +static void +com_continue_stmt(struct compiling *c, node *n) +{ + int i = c->c_nblocks; + if (i-- > 0 && c->c_block[i] == SETUP_LOOP) { + com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); + } + else if (i <= 0) { + /* at the outer level */ + com_error(c, PyExc_SyntaxError, + "'continue' not properly in loop"); + } + else { + int j; + for (j = i-1; j >= 0; --j) { + if (c->c_block[j] == SETUP_LOOP) + break; + } + if (j >= 0) { + /* there is a loop, but something interferes */ + for (; i > j; --i) { + if (c->c_block[i] == SETUP_EXCEPT || + c->c_block[i] == SETUP_FINALLY) { + com_addoparg(c, CONTINUE_LOOP, + c->c_begin); + return; + } + if (c->c_block[i] == END_FINALLY) { + com_error(c, PyExc_SyntaxError, + "'continue' not supported inside 'finally' clause"); + return; + } + } + } + com_error(c, PyExc_SyntaxError, + "'continue' not properly in loop"); + } + /* XXX Could allow it inside a 'finally' clause + XXX if we could pop the exception still on the stack */ +} + +static int +com_argdefs(struct compiling *c, node *n) +{ + int i, nch, nargs, ndefs; + if (TYPE(n) == lambdef) { + /* lambdef: 'lambda' [varargslist] ':' test */ + n = CHILD(n, 1); + } + else { + REQ(n, funcdef); /* funcdef: 'def' NAME parameters ... */ + n = CHILD(n, 2); + REQ(n, parameters); /* parameters: '(' [varargslist] ')' */ + n = CHILD(n, 1); + } + if (TYPE(n) != varargslist) + return 0; + /* varargslist: + (fpdef ['=' test] ',')* '*' ....... | + fpdef ['=' test] (',' fpdef ['=' test])* [','] */ + nch = NCH(n); + nargs = 0; + ndefs = 0; + for (i = 0; i < nch; i++) { + int t; + if (TYPE(CHILD(n, i)) == STAR || + TYPE(CHILD(n, i)) == DOUBLESTAR) + break; + nargs++; + i++; + if (i >= nch) + t = RPAR; /* Anything except EQUAL or COMMA */ + else + t = TYPE(CHILD(n, i)); + if (t == EQUAL) { + i++; + ndefs++; + com_node(c, CHILD(n, i)); + i++; + if (i >= nch) + break; + t = TYPE(CHILD(n, i)); + } + else { + /* Treat "(a=1, b)" as an error */ + if (ndefs) + com_error(c, PyExc_SyntaxError, + "non-default argument follows default argument"); + } + if (t != COMMA) + break; + } + return ndefs; +} + +static void +com_funcdef(struct compiling *c, node *n) +{ + PyObject *co; + int ndefs; + REQ(n, funcdef); /* funcdef: 'def' NAME parameters ':' suite */ + ndefs = com_argdefs(c, n); + symtable_enter_scope(c->c_symtable, STR(CHILD(n, 1)), TYPE(n), + n->n_lineno); + co = (PyObject *)icompile(n, c); + symtable_exit_scope(c->c_symtable); + if (co == NULL) + c->c_errors++; + else { + int closure = com_make_closure(c, (PyCodeObject *)co); + int i = com_addconst(c, co); + com_addoparg(c, LOAD_CONST, i); + com_push(c, 1); + if (closure) + com_addoparg(c, MAKE_CLOSURE, ndefs); + else + com_addoparg(c, MAKE_FUNCTION, ndefs); + com_pop(c, ndefs); + com_addop_varname(c, VAR_STORE, STR(CHILD(n, 1))); + com_pop(c, 1); + Py_DECREF(co); + } +} + +static void +com_bases(struct compiling *c, node *n) +{ + int i; + REQ(n, testlist); + /* testlist: test (',' test)* [','] */ + for (i = 0; i < NCH(n); i += 2) + com_node(c, CHILD(n, i)); + i = (NCH(n)+1) / 2; + com_addoparg(c, BUILD_TUPLE, i); + com_pop(c, i-1); +} + +static void +com_classdef(struct compiling *c, node *n) +{ + int i; + PyObject *v; + PyCodeObject *co; + char *name; + + REQ(n, classdef); + /* classdef: class NAME ['(' testlist ')'] ':' suite */ + if ((v = PyString_InternFromString(STR(CHILD(n, 1)))) == NULL) { + c->c_errors++; + return; + } + /* Push the class name on the stack */ + i = com_addconst(c, v); + com_addoparg(c, LOAD_CONST, i); + com_push(c, 1); + Py_DECREF(v); + /* Push the tuple of base classes on the stack */ + if (TYPE(CHILD(n, 2)) != LPAR) { + com_addoparg(c, BUILD_TUPLE, 0); + com_push(c, 1); + } + else + com_bases(c, CHILD(n, 3)); + name = STR(CHILD(n, 1)); + symtable_enter_scope(c->c_symtable, name, TYPE(n), n->n_lineno); + co = icompile(n, c); + symtable_exit_scope(c->c_symtable); + if (co == NULL) + c->c_errors++; + else { + int closure = com_make_closure(c, co); + i = com_addconst(c, (PyObject *)co); + com_addoparg(c, LOAD_CONST, i); + com_push(c, 1); + if (closure) { + com_addoparg(c, MAKE_CLOSURE, 0); + com_pop(c, PyCode_GetNumFree(co)); + } else + com_addoparg(c, MAKE_FUNCTION, 0); + com_addoparg(c, CALL_FUNCTION, 0); + com_addbyte(c, BUILD_CLASS); + com_pop(c, 2); + com_addop_varname(c, VAR_STORE, STR(CHILD(n, 1))); + com_pop(c, 1); + Py_DECREF(co); + } +} + +static void +com_node(struct compiling *c, node *n) +{ + loop: + if (c->c_errors) + return; + switch (TYPE(n)) { + + /* Definition nodes */ + + case funcdef: + com_funcdef(c, n); + break; + case classdef: + com_classdef(c, n); + break; + + /* Trivial parse tree nodes */ + + case stmt: + case small_stmt: + case flow_stmt: + n = CHILD(n, 0); + goto loop; + + case simple_stmt: + /* small_stmt (';' small_stmt)* [';'] NEWLINE */ + com_set_lineno(c, n->n_lineno); + { + int i; + for (i = 0; i < NCH(n)-1; i += 2) + com_node(c, CHILD(n, i)); + } + break; + + case compound_stmt: + com_set_lineno(c, n->n_lineno); + n = CHILD(n, 0); + goto loop; + + /* Statement nodes */ + + case expr_stmt: + com_expr_stmt(c, n); + break; + case print_stmt: + com_print_stmt(c, n); + break; + case del_stmt: /* 'del' exprlist */ + com_assign(c, CHILD(n, 1), OP_DELETE, NULL); + break; + case pass_stmt: + break; + case break_stmt: + if (c->c_loops == 0) { + com_error(c, PyExc_SyntaxError, + "'break' outside loop"); + } + com_addbyte(c, BREAK_LOOP); + break; + case continue_stmt: + com_continue_stmt(c, n); + break; + case return_stmt: + com_return_stmt(c, n); + break; + case yield_stmt: + com_yield_stmt(c, n); + break; + case raise_stmt: + com_raise_stmt(c, n); + break; + case import_stmt: + com_import_stmt(c, n); + break; + case global_stmt: + break; + case exec_stmt: + com_exec_stmt(c, n); + break; + case assert_stmt: + com_assert_stmt(c, n); + break; + case if_stmt: + com_if_stmt(c, n); + break; + case while_stmt: + com_while_stmt(c, n); + break; + case for_stmt: + com_for_stmt(c, n); + break; + case try_stmt: + com_try_stmt(c, n); + break; + case suite: + com_suite(c, n); + break; + + /* Expression nodes */ + + case testlist: + case testlist1: + case testlist_safe: + com_list(c, n, 0); + break; + case test: + com_test(c, n); + break; + case and_test: + com_and_test(c, n); + break; + case not_test: + com_not_test(c, n); + break; + case comparison: + com_comparison(c, n); + break; + case exprlist: + com_list(c, n, 0); + break; + case expr: + com_expr(c, n); + break; + case xor_expr: + com_xor_expr(c, n); + break; + case and_expr: + com_and_expr(c, n); + break; + case shift_expr: + com_shift_expr(c, n); + break; + case arith_expr: + com_arith_expr(c, n); + break; + case term: + com_term(c, n); + break; + case factor: + com_factor(c, n); + break; + case power: + com_power(c, n); + break; + case atom: + com_atom(c, n); + break; + + default: + com_error(c, PyExc_SystemError, + "com_node: unexpected node type"); + } +} + +static void com_fplist(struct compiling *, node *); + +static void +com_fpdef(struct compiling *c, node *n) +{ + REQ(n, fpdef); /* fpdef: NAME | '(' fplist ')' */ + if (TYPE(CHILD(n, 0)) == LPAR) + com_fplist(c, CHILD(n, 1)); + else { + com_addop_varname(c, VAR_STORE, STR(CHILD(n, 0))); + com_pop(c, 1); + } +} + +static void +com_fplist(struct compiling *c, node *n) +{ + REQ(n, fplist); /* fplist: fpdef (',' fpdef)* [','] */ + if (NCH(n) == 1) { + com_fpdef(c, CHILD(n, 0)); + } + else { + int i = (NCH(n)+1)/2; + com_addoparg(c, UNPACK_SEQUENCE, i); + com_push(c, i-1); + for (i = 0; i < NCH(n); i += 2) + com_fpdef(c, CHILD(n, i)); + } +} + +static void +com_arglist(struct compiling *c, node *n) +{ + int nch, i, narg; + int complex = 0; + char nbuf[30]; + REQ(n, varargslist); + /* varargslist: + (fpdef ['=' test] ',')* (fpdef ['=' test] | '*' .....) */ + nch = NCH(n); + /* Enter all arguments in table of locals */ + for (i = 0, narg = 0; i < nch; i++) { + node *ch = CHILD(n, i); + node *fp; + if (TYPE(ch) == STAR || TYPE(ch) == DOUBLESTAR) + break; + REQ(ch, fpdef); /* fpdef: NAME | '(' fplist ')' */ + fp = CHILD(ch, 0); + if (TYPE(fp) != NAME) { + PyOS_snprintf(nbuf, sizeof(nbuf), ".%d", i); + complex = 1; + } + narg++; + /* all name updates handled by symtable */ + if (++i >= nch) + break; + ch = CHILD(n, i); + if (TYPE(ch) == EQUAL) + i += 2; + else + REQ(ch, COMMA); + } + if (complex) { + /* Generate code for complex arguments only after + having counted the simple arguments */ + int ilocal = 0; + for (i = 0; i < nch; i++) { + node *ch = CHILD(n, i); + node *fp; + if (TYPE(ch) == STAR || TYPE(ch) == DOUBLESTAR) + break; + REQ(ch, fpdef); /* fpdef: NAME | '(' fplist ')' */ + fp = CHILD(ch, 0); + if (TYPE(fp) != NAME) { + com_addoparg(c, LOAD_FAST, ilocal); + com_push(c, 1); + com_fpdef(c, ch); + } + ilocal++; + if (++i >= nch) + break; + ch = CHILD(n, i); + if (TYPE(ch) == EQUAL) + i += 2; + else + REQ(ch, COMMA); + } + } +} + +static void +com_file_input(struct compiling *c, node *n) +{ + int i; + PyObject *doc; + REQ(n, file_input); /* (NEWLINE | stmt)* ENDMARKER */ + doc = get_docstring(c, n); + if (doc != NULL) { + int i = com_addconst(c, doc); + Py_DECREF(doc); + com_addoparg(c, LOAD_CONST, i); + com_push(c, 1); + com_addop_name(c, STORE_NAME, "__doc__"); + com_pop(c, 1); + } + for (i = 0; i < NCH(n); i++) { + node *ch = CHILD(n, i); + if (TYPE(ch) != ENDMARKER && TYPE(ch) != NEWLINE) + com_node(c, ch); + } +} + +/* Top-level compile-node interface */ + +static void +compile_funcdef(struct compiling *c, node *n) +{ + PyObject *doc; + node *ch; + REQ(n, funcdef); /* funcdef: 'def' NAME parameters ':' suite */ + c->c_name = STR(CHILD(n, 1)); + doc = get_docstring(c, CHILD(n, 4)); + if (doc != NULL) { + (void) com_addconst(c, doc); + Py_DECREF(doc); + } + else + (void) com_addconst(c, Py_None); /* No docstring */ + ch = CHILD(n, 2); /* parameters: '(' [varargslist] ')' */ + ch = CHILD(ch, 1); /* ')' | varargslist */ + if (TYPE(ch) == varargslist) + com_arglist(c, ch); + c->c_infunction = 1; + com_node(c, CHILD(n, 4)); + c->c_infunction = 0; + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); + com_push(c, 1); + com_addbyte(c, RETURN_VALUE); + com_pop(c, 1); +} + +static void +compile_lambdef(struct compiling *c, node *n) +{ + node *ch; + REQ(n, lambdef); /* lambdef: 'lambda' [varargslist] ':' test */ + c->c_name = ""; + + ch = CHILD(n, 1); + (void) com_addconst(c, Py_None); /* No docstring */ + if (TYPE(ch) == varargslist) { + com_arglist(c, ch); + ch = CHILD(n, 3); + } + else + ch = CHILD(n, 2); + com_node(c, ch); + com_addbyte(c, RETURN_VALUE); + com_pop(c, 1); +} + +static void +compile_classdef(struct compiling *c, node *n) +{ + node *ch; + PyObject *doc; + REQ(n, classdef); + /* classdef: 'class' NAME ['(' testlist ')'] ':' suite */ + c->c_name = STR(CHILD(n, 1)); + c->c_private = c->c_name; + /* Initialize local __module__ from global __name__ */ + com_addop_name(c, LOAD_GLOBAL, "__name__"); + com_addop_name(c, STORE_NAME, "__module__"); + ch = CHILD(n, NCH(n)-1); /* The suite */ + doc = get_docstring(c, ch); + if (doc != NULL) { + int i = com_addconst(c, doc); + Py_DECREF(doc); + com_addoparg(c, LOAD_CONST, i); + com_push(c, 1); + com_addop_name(c, STORE_NAME, "__doc__"); + com_pop(c, 1); + } + else + (void) com_addconst(c, Py_None); + com_node(c, ch); + com_addbyte(c, LOAD_LOCALS); + com_push(c, 1); + com_addbyte(c, RETURN_VALUE); + com_pop(c, 1); +} + +static void +compile_node(struct compiling *c, node *n) +{ + com_set_lineno(c, n->n_lineno); + + switch (TYPE(n)) { + + case single_input: /* One interactive command */ + /* NEWLINE | simple_stmt | compound_stmt NEWLINE */ + c->c_interactive++; + n = CHILD(n, 0); + if (TYPE(n) != NEWLINE) + com_node(c, n); + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); + com_push(c, 1); + com_addbyte(c, RETURN_VALUE); + com_pop(c, 1); + c->c_interactive--; + break; + + case file_input: /* A whole file, or built-in function exec() */ + com_file_input(c, n); + com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); + com_push(c, 1); + com_addbyte(c, RETURN_VALUE); + com_pop(c, 1); + break; + + case eval_input: /* Built-in function input() */ + com_node(c, CHILD(n, 0)); + com_addbyte(c, RETURN_VALUE); + com_pop(c, 1); + break; + + case lambdef: /* anonymous function definition */ + compile_lambdef(c, n); + break; + + case funcdef: /* A function definition */ + compile_funcdef(c, n); + break; + + case classdef: /* A class definition */ + compile_classdef(c, n); + break; + + default: + com_error(c, PyExc_SystemError, + "compile_node: unexpected node type"); + } +} + +static PyObject * +dict_keys_inorder(PyObject *dict, int offset) +{ + PyObject *tuple, *k, *v; + int i, pos = 0, size = PyDict_Size(dict); + + tuple = PyTuple_New(size); + if (tuple == NULL) + return NULL; + while (PyDict_Next(dict, &pos, &k, &v)) { + i = PyInt_AS_LONG(v); + Py_INCREF(k); + assert((i - offset) < size); + PyTuple_SET_ITEM(tuple, i - offset, k); + } + return tuple; +} + +PyCodeObject * +PyNode_Compile(node *n, const char *filename) +{ + return PyNode_CompileFlags(n, filename, NULL); +} + +PyCodeObject * +PyNode_CompileFlags(node *n, const char *filename, PyCompilerFlags *flags) +{ + return jcompile(n, filename, NULL, flags); +} + +struct symtable * +PyNode_CompileSymtable(node *n, const char *filename) +{ + struct symtable *st; + PyFutureFeatures *ff; + + ff = PyNode_Future(n, filename); + if (ff == NULL) + return NULL; + st = symtable_build(n, ff, filename); + if (st == NULL) { + PyObject_FREE((void *)ff); + return NULL; + } + return st; +} + +static PyCodeObject * +icompile(node *n, struct compiling *base) +{ + return jcompile(n, base->c_filename, base, NULL); +} + +static PyCodeObject * +jcompile(node *n, const char *filename, struct compiling *base, + PyCompilerFlags *flags) +{ + struct compiling sc; + PyCodeObject *co; + if (!com_init(&sc, filename)) + return NULL; + if (flags && flags->cf_flags & PyCF_SOURCE_IS_UTF8) { + sc.c_encoding = "utf-8"; + } else if (TYPE(n) == encoding_decl) { + sc.c_encoding = STR(n); + n = CHILD(n, 0); + } else { + sc.c_encoding = NULL; + } + if (base) { + sc.c_private = base->c_private; + sc.c_symtable = base->c_symtable; + /* c_symtable still points to parent's symbols */ + if (base->c_nested + || (sc.c_symtable->st_cur->ste_type == TYPE_FUNCTION)) + sc.c_nested = 1; + sc.c_flags |= base->c_flags & PyCF_MASK; + if (base->c_encoding != NULL) { + assert(sc.c_encoding == NULL); + sc.c_encoding = base->c_encoding; + } + } else { + sc.c_private = NULL; + sc.c_future = PyNode_Future(n, filename); + if (sc.c_future == NULL) { + com_free(&sc); + return NULL; + } + if (flags) { + int merged = sc.c_future->ff_features | + flags->cf_flags; + sc.c_future->ff_features = merged; + flags->cf_flags = merged; + } + sc.c_symtable = symtable_build(n, sc.c_future, sc.c_filename); + if (sc.c_symtable == NULL) { + com_free(&sc); + return NULL; + } + /* reset symbol table for second pass */ + sc.c_symtable->st_nscopes = 1; + sc.c_symtable->st_pass = 2; + } + co = NULL; + if (symtable_load_symbols(&sc) < 0) { + sc.c_errors++; + goto exit; + } + compile_node(&sc, n); + com_done(&sc); + if (sc.c_errors == 0) { + PyObject *consts, *names, *varnames, *filename, *name, + *freevars, *cellvars; + consts = PyList_AsTuple(sc.c_consts); + names = PyList_AsTuple(sc.c_names); + varnames = PyList_AsTuple(sc.c_varnames); + cellvars = dict_keys_inorder(sc.c_cellvars, 0); + freevars = dict_keys_inorder(sc.c_freevars, + PyTuple_GET_SIZE(cellvars)); + filename = PyString_InternFromString(sc.c_filename); + name = PyString_InternFromString(sc.c_name); + if (!PyErr_Occurred()) + co = PyCode_New(sc.c_argcount, + sc.c_nlocals, + sc.c_maxstacklevel, + sc.c_flags, + sc.c_code, + consts, + names, + varnames, + freevars, + cellvars, + filename, + name, + sc.c_firstlineno, + sc.c_lnotab); + Py_XDECREF(consts); + Py_XDECREF(names); + Py_XDECREF(varnames); + Py_XDECREF(freevars); + Py_XDECREF(cellvars); + Py_XDECREF(filename); + Py_XDECREF(name); + } + else if (!PyErr_Occurred()) { + /* This could happen if someone called PyErr_Clear() after an + error was reported above. That's not supposed to happen, + but I just plugged one case and I'm not sure there can't be + others. In that case, raise SystemError so that at least + it gets reported instead dumping core. */ + PyErr_SetString(PyExc_SystemError, "lost syntax error"); + } + exit: + if (base == NULL) { + PySymtable_Free(sc.c_symtable); + sc.c_symtable = NULL; + } + com_free(&sc); + return co; +} + +int +PyCode_Addr2Line(PyCodeObject *co, int addrq) +{ + int size = PyString_Size(co->co_lnotab) / 2; + unsigned char *p = (unsigned char*)PyString_AsString(co->co_lnotab); + int line = co->co_firstlineno; + int addr = 0; + while (--size >= 0) { + addr += *p++; + if (addr > addrq) + break; + line += *p++; + } + return line; +} + +/* The test for LOCAL must come before the test for FREE in order to + handle classes where name is both local and free. The local var is + a method and the free var is a free var referenced within a method. +*/ + +static int +get_ref_type(struct compiling *c, char *name) +{ + char buf[350]; + PyObject *v; + + if (PyDict_GetItemString(c->c_cellvars, name) != NULL) + return CELL; + if (PyDict_GetItemString(c->c_locals, name) != NULL) + return LOCAL; + if (PyDict_GetItemString(c->c_freevars, name) != NULL) + return FREE; + v = PyDict_GetItemString(c->c_globals, name); + if (v) { + if (v == Py_None) + return GLOBAL_EXPLICIT; + else { + return GLOBAL_IMPLICIT; + } + } + PyOS_snprintf(buf, sizeof(buf), + "unknown scope for %.100s in %.100s(%s) " + "in %s\nsymbols: %s\nlocals: %s\nglobals: %s\n", + name, c->c_name, + PyObject_REPR(c->c_symtable->st_cur->ste_id), + c->c_filename, + PyObject_REPR(c->c_symtable->st_cur->ste_symbols), + PyObject_REPR(c->c_locals), + PyObject_REPR(c->c_globals) + ); + + Py_FatalError(buf); + return -1; +} + +/* Helper functions to issue warnings */ + +static int +issue_warning(const char *msg, const char *filename, int lineno) +{ + if (PyErr_Occurred()) { + /* This can happen because symtable_node continues + processing even after raising a SyntaxError. + Calling PyErr_WarnExplicit now would clobber the + pending exception; instead we fail and let that + exception propagate. + */ + return -1; + } + if (PyErr_WarnExplicit(PyExc_SyntaxWarning, msg, filename, + lineno, NULL, NULL) < 0) { + if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) { + PyErr_SetString(PyExc_SyntaxError, msg); + PyErr_SyntaxLocation(filename, lineno); + } + return -1; + } + return 0; +} + +static int +symtable_warn(struct symtable *st, char *msg) +{ + if (issue_warning(msg, st->st_filename, st->st_cur->ste_lineno) < 0) { + st->st_errors++; + return -1; + } + return 0; +} + +/* Helper function for setting lineno and filename */ + +static struct symtable * +symtable_build(node *n, PyFutureFeatures *ff, const char *filename) +{ + struct symtable *st; + + st = symtable_init(); + if (st == NULL) + return NULL; + st->st_future = ff; + st->st_filename = filename; + symtable_enter_scope(st, TOP, TYPE(n), n->n_lineno); + if (st->st_errors > 0) + goto fail; + symtable_node(st, n); + if (st->st_errors > 0) + goto fail; + return st; + fail: + if (!PyErr_Occurred()) { + /* This could happen because after a syntax error is + detected, the symbol-table-building continues for + a while, and PyErr_Clear() might erroneously be + called during that process. One such case has been + fixed, but there might be more (now or later). + */ + PyErr_SetString(PyExc_SystemError, "lost exception"); + } + st->st_future = NULL; + st->st_filename = NULL; + PySymtable_Free(st); + return NULL; +} + +static int +symtable_init_compiling_symbols(struct compiling *c) +{ + PyObject *varnames; + + varnames = c->c_symtable->st_cur->ste_varnames; + if (varnames == NULL) { + varnames = PyList_New(0); + if (varnames == NULL) + return -1; + c->c_symtable->st_cur->ste_varnames = varnames; + Py_INCREF(varnames); + } else + Py_INCREF(varnames); + c->c_varnames = varnames; + + c->c_globals = PyDict_New(); + if (c->c_globals == NULL) + return -1; + c->c_freevars = PyDict_New(); + if (c->c_freevars == NULL) + return -1; + c->c_cellvars = PyDict_New(); + if (c->c_cellvars == NULL) + return -1; + return 0; +} + +struct symbol_info { + int si_nlocals; + int si_ncells; + int si_nfrees; + int si_nimplicit; +}; + +static void +symtable_init_info(struct symbol_info *si) +{ + si->si_nlocals = 0; + si->si_ncells = 0; + si->si_nfrees = 0; + si->si_nimplicit = 0; +} + +static int +symtable_resolve_free(struct compiling *c, PyObject *name, int flags, + struct symbol_info *si) +{ + PyObject *dict, *v; + + /* Seperate logic for DEF_FREE. If it occurs in a function, + it indicates a local that we must allocate storage for (a + cell var). If it occurs in a class, then the class has a + method and a free variable with the same name. + */ + if (c->c_symtable->st_cur->ste_type == TYPE_FUNCTION) { + /* If it isn't declared locally, it can't be a cell. */ + if (!(flags & (DEF_LOCAL | DEF_PARAM))) + return 0; + v = PyInt_FromLong(si->si_ncells++); + dict = c->c_cellvars; + } else { + /* If it is free anyway, then there is no need to do + anything here. + */ + if (is_free(flags ^ DEF_FREE_CLASS) + || (flags == DEF_FREE_CLASS)) + return 0; + v = PyInt_FromLong(si->si_nfrees++); + dict = c->c_freevars; + } + if (v == NULL) + return -1; + if (PyDict_SetItem(dict, name, v) < 0) { + Py_DECREF(v); + return -1; + } + Py_DECREF(v); + return 0; +} + +/* If a variable is a cell and an argument, make sure that appears in + co_cellvars before any variable to its right in varnames. +*/ + + +static int +symtable_cellvar_offsets(PyObject **cellvars, int argcount, + PyObject *varnames, int flags) +{ + PyObject *v = NULL; + PyObject *w, *d, *list = NULL; + int i, pos; + + if (flags & CO_VARARGS) + argcount++; + if (flags & CO_VARKEYWORDS) + argcount++; + for (i = argcount; --i >= 0; ) { + v = PyList_GET_ITEM(varnames, i); + if (PyDict_GetItem(*cellvars, v)) { + if (list == NULL) { + list = PyList_New(1); + if (list == NULL) + return -1; + PyList_SET_ITEM(list, 0, v); + Py_INCREF(v); + } else { + if (PyList_Insert(list, 0, v) < 0) { + Py_DECREF(list); + return -1; + } + } + } + } + if (list == NULL) + return 0; + + /* There are cellvars that are also arguments. Create a dict + to replace cellvars and put the args at the front. + */ + d = PyDict_New(); + if (d == NULL) + return -1; + for (i = PyList_GET_SIZE(list); --i >= 0; ) { + v = PyInt_FromLong(i); + if (v == NULL) + goto fail; + if (PyDict_SetItem(d, PyList_GET_ITEM(list, i), v) < 0) + goto fail; + if (PyDict_DelItem(*cellvars, PyList_GET_ITEM(list, i)) < 0) + goto fail; + Py_DECREF(v); + } + pos = 0; + i = PyList_GET_SIZE(list); + Py_DECREF(list); + while (PyDict_Next(*cellvars, &pos, &v, &w)) { + w = PyInt_FromLong(i++); /* don't care about the old key */ + if (w == NULL) + goto fail; + if (PyDict_SetItem(d, v, w) < 0) { + Py_DECREF(w); + v = NULL; + goto fail; + } + Py_DECREF(w); + } + Py_DECREF(*cellvars); + *cellvars = d; + return 1; + fail: + Py_DECREF(d); + Py_XDECREF(v); + return -1; +} + +static int +symtable_freevar_offsets(PyObject *freevars, int offset) +{ + PyObject *name, *v; + int pos; + + /* The cell vars are the first elements of the closure, + followed by the free vars. Update the offsets in + c_freevars to account for number of cellvars. */ + pos = 0; + while (PyDict_Next(freevars, &pos, &name, &v)) { + int i = PyInt_AS_LONG(v) + offset; + PyObject *o = PyInt_FromLong(i); + if (o == NULL) + return -1; + if (PyDict_SetItem(freevars, name, o) < 0) { + Py_DECREF(o); + return -1; + } + Py_DECREF(o); + } + return 0; +} + +static int +symtable_check_unoptimized(struct compiling *c, + PySymtableEntryObject *ste, + struct symbol_info *si) +{ + char buf[300]; + + if (!(si->si_ncells || si->si_nfrees || ste->ste_child_free + || (ste->ste_nested && si->si_nimplicit))) + return 0; + +#define ILLEGAL_CONTAINS "contains a nested function with free variables" + +#define ILLEGAL_IS "is a nested function" + +#define ILLEGAL_IMPORT_STAR \ +"import * is not allowed in function '%.100s' because it %s" + +#define ILLEGAL_BARE_EXEC \ +"unqualified exec is not allowed in function '%.100s' it %s" + +#define ILLEGAL_EXEC_AND_IMPORT_STAR \ +"function '%.100s' uses import * and bare exec, which are illegal " \ +"because it %s" + + /* XXX perhaps the linenos for these opt-breaking statements + should be stored so the exception can point to them. */ + + if (ste->ste_child_free) { + if (ste->ste_optimized == OPT_IMPORT_STAR) + PyOS_snprintf(buf, sizeof(buf), + ILLEGAL_IMPORT_STAR, + PyString_AS_STRING(ste->ste_name), + ILLEGAL_CONTAINS); + else if (ste->ste_optimized == (OPT_BARE_EXEC | OPT_EXEC)) + PyOS_snprintf(buf, sizeof(buf), + ILLEGAL_BARE_EXEC, + PyString_AS_STRING(ste->ste_name), + ILLEGAL_CONTAINS); + else { + PyOS_snprintf(buf, sizeof(buf), + ILLEGAL_EXEC_AND_IMPORT_STAR, + PyString_AS_STRING(ste->ste_name), + ILLEGAL_CONTAINS); + } + } else { + if (ste->ste_optimized == OPT_IMPORT_STAR) + PyOS_snprintf(buf, sizeof(buf), + ILLEGAL_IMPORT_STAR, + PyString_AS_STRING(ste->ste_name), + ILLEGAL_IS); + else if (ste->ste_optimized == (OPT_BARE_EXEC | OPT_EXEC)) + PyOS_snprintf(buf, sizeof(buf), + ILLEGAL_BARE_EXEC, + PyString_AS_STRING(ste->ste_name), + ILLEGAL_IS); + else { + PyOS_snprintf(buf, sizeof(buf), + ILLEGAL_EXEC_AND_IMPORT_STAR, + PyString_AS_STRING(ste->ste_name), + ILLEGAL_IS); + } + } + + PyErr_SetString(PyExc_SyntaxError, buf); + PyErr_SyntaxLocation(c->c_symtable->st_filename, + ste->ste_opt_lineno); + return -1; +} + +static int +symtable_update_flags(struct compiling *c, PySymtableEntryObject *ste, + struct symbol_info *si) +{ + if (c->c_future) + c->c_flags |= c->c_future->ff_features; + if (ste->ste_generator) + c->c_flags |= CO_GENERATOR; + if (ste->ste_type != TYPE_MODULE) + c->c_flags |= CO_NEWLOCALS; + if (ste->ste_type == TYPE_FUNCTION) { + c->c_nlocals = si->si_nlocals; + if (ste->ste_optimized == 0) + c->c_flags |= CO_OPTIMIZED; + else if (ste->ste_optimized != OPT_EXEC) + return symtable_check_unoptimized(c, ste, si); + } + return 0; +} + +static int +symtable_error(struct symtable *st, int lineno) +{ + if (lineno == 0) + lineno = st->st_cur->ste_lineno; + PyErr_SyntaxLocation(st->st_filename, lineno); + st->st_errors++; + return -1; +} + +static int +symtable_load_symbols(struct compiling *c) +{ + struct symtable *st = c->c_symtable; + PySymtableEntryObject *ste = st->st_cur; + PyObject *name, *varnames, *v; + int i, flags, pos; + struct symbol_info si; + + v = NULL; + + if (symtable_init_compiling_symbols(c) < 0) + goto fail; + symtable_init_info(&si); + varnames = st->st_cur->ste_varnames; + si.si_nlocals = PyList_GET_SIZE(varnames); + c->c_argcount = si.si_nlocals; + + for (i = 0; i < si.si_nlocals; ++i) { + v = PyInt_FromLong(i); + if (v == NULL) + goto fail; + if (PyDict_SetItem(c->c_locals, + PyList_GET_ITEM(varnames, i), v) < 0) + goto fail; + Py_DECREF(v); + } + + /* XXX The cases below define the rules for whether a name is + local or global. The logic could probably be clearer. */ + pos = 0; + while (PyDict_Next(ste->ste_symbols, &pos, &name, &v)) { + flags = PyInt_AS_LONG(v); + + if (flags & DEF_FREE_GLOBAL) + /* undo the original DEF_FREE */ + flags &= ~(DEF_FREE | DEF_FREE_CLASS); + + /* Deal with names that need two actions: + 1. Cell variables that are also locals. + 2. Free variables in methods that are also class + variables or declared global. + */ + if (flags & (DEF_FREE | DEF_FREE_CLASS)) + symtable_resolve_free(c, name, flags, &si); + + if (flags & DEF_STAR) { + c->c_argcount--; + c->c_flags |= CO_VARARGS; + } else if (flags & DEF_DOUBLESTAR) { + c->c_argcount--; + c->c_flags |= CO_VARKEYWORDS; + } else if (flags & DEF_INTUPLE) + c->c_argcount--; + else if (flags & DEF_GLOBAL) { + if (flags & DEF_PARAM) { + PyErr_Format(PyExc_SyntaxError, LOCAL_GLOBAL, + PyString_AS_STRING(name)); + symtable_error(st, 0); + goto fail; + } + if (PyDict_SetItem(c->c_globals, name, Py_None) < 0) + goto fail; + } else if (flags & DEF_FREE_GLOBAL) { + si.si_nimplicit++; + if (PyDict_SetItem(c->c_globals, name, Py_True) < 0) + goto fail; + } else if ((flags & DEF_LOCAL) && !(flags & DEF_PARAM)) { + v = PyInt_FromLong(si.si_nlocals++); + if (v == NULL) + goto fail; + if (PyDict_SetItem(c->c_locals, name, v) < 0) + goto fail; + Py_DECREF(v); + if (ste->ste_type != TYPE_CLASS) + if (PyList_Append(c->c_varnames, name) < 0) + goto fail; + } else if (is_free(flags)) { + if (ste->ste_nested) { + v = PyInt_FromLong(si.si_nfrees++); + if (v == NULL) + goto fail; + if (PyDict_SetItem(c->c_freevars, name, v) < 0) + goto fail; + Py_DECREF(v); + } else { + si.si_nimplicit++; + if (PyDict_SetItem(c->c_globals, name, + Py_True) < 0) + goto fail; + if (st->st_nscopes != 1) { + v = PyInt_FromLong(flags); + if (v == NULL) + goto fail; + if (PyDict_SetItem(st->st_global, + name, v)) + goto fail; + Py_DECREF(v); + } + } + } + } + + assert(PyDict_Size(c->c_freevars) == si.si_nfrees); + + if (si.si_ncells > 1) { /* one cell is always in order */ + if (symtable_cellvar_offsets(&c->c_cellvars, c->c_argcount, + c->c_varnames, c->c_flags) < 0) + return -1; + } + if (symtable_freevar_offsets(c->c_freevars, si.si_ncells) < 0) + return -1; + return symtable_update_flags(c, ste, &si); + fail: + /* is this always the right thing to do? */ + Py_XDECREF(v); + return -1; +} + +static struct symtable * +symtable_init() +{ + struct symtable *st; + + st = (struct symtable *)PyObject_MALLOC(sizeof(struct symtable)); + if (st == NULL) + return NULL; + st->st_pass = 1; + + st->st_filename = NULL; + st->st_symbols = NULL; + if ((st->st_stack = PyList_New(0)) == NULL) + goto fail; + if ((st->st_symbols = PyDict_New()) == NULL) + goto fail; + st->st_cur = NULL; + st->st_nscopes = 0; + st->st_errors = 0; + st->st_private = NULL; + return st; + fail: + PySymtable_Free(st); + return NULL; +} + +void +PySymtable_Free(struct symtable *st) +{ + Py_XDECREF(st->st_symbols); + Py_XDECREF(st->st_stack); + Py_XDECREF(st->st_cur); + PyObject_FREE((void *)st); +} + +/* When the compiler exits a scope, it must should update the scope's + free variable information with the list of free variables in its + children. + + Variables that are free in children and defined in the current + scope are cellvars. + + If the scope being exited is defined at the top-level (ste_nested is + false), free variables in children that are not defined here are + implicit globals. + +*/ + +static int +symtable_update_free_vars(struct symtable *st) +{ + int i, j, def; + PyObject *o, *name, *list = NULL; + PySymtableEntryObject *child, *ste = st->st_cur; + + if (ste->ste_type == TYPE_CLASS) + def = DEF_FREE_CLASS; + else + def = DEF_FREE; + for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) { + int pos = 0; + + if (list && PyList_SetSlice(list, 0, + PyList_GET_SIZE(list), 0) < 0) + return -1; + child = (PySymtableEntryObject *) + PyList_GET_ITEM(ste->ste_children, i); + while (PyDict_Next(child->ste_symbols, &pos, &name, &o)) { + int flags = PyInt_AS_LONG(o); + if (!(is_free(flags))) + continue; /* avoids indentation */ + if (list == NULL) { + list = PyList_New(0); + if (list == NULL) + return -1; + } + ste->ste_child_free = 1; + if (PyList_Append(list, name) < 0) { + Py_DECREF(list); + return -1; + } + } + for (j = 0; list && j < PyList_GET_SIZE(list); j++) { + PyObject *v; + name = PyList_GET_ITEM(list, j); + v = PyDict_GetItem(ste->ste_symbols, name); + /* If a name N is declared global in scope A and + referenced in scope B contained (perhaps + indirectly) in A and there are no scopes + with bindings for N between B and A, then N + is global in B. Unless A is a class scope, + because class scopes are not considered for + nested scopes. + */ + if (v && (ste->ste_type != TYPE_CLASS)) { + int flags = PyInt_AS_LONG(v); + if (flags & DEF_GLOBAL) { + symtable_undo_free(st, child->ste_id, + name); + continue; + } + } + if (ste->ste_nested) { + if (symtable_add_def_o(st, ste->ste_symbols, + name, def) < 0) { + Py_DECREF(list); + return -1; + } + } else { + if (symtable_check_global(st, child->ste_id, + name) < 0) { + Py_DECREF(list); + return -1; + } + } + } + } + + Py_XDECREF(list); + return 0; +} + +/* If the current scope is a non-nested class or if name is not + defined in the current, non-nested scope, then it is an implicit + global in all nested scopes. +*/ + +static int +symtable_check_global(struct symtable *st, PyObject *child, PyObject *name) +{ + PyObject *o; + int v; + PySymtableEntryObject *ste = st->st_cur; + + if (ste->ste_type == TYPE_CLASS) + return symtable_undo_free(st, child, name); + o = PyDict_GetItem(ste->ste_symbols, name); + if (o == NULL) + return symtable_undo_free(st, child, name); + v = PyInt_AS_LONG(o); + + if (is_free(v) || (v & DEF_GLOBAL)) + return symtable_undo_free(st, child, name); + else + return symtable_add_def_o(st, ste->ste_symbols, + name, DEF_FREE); +} + +static int +symtable_undo_free(struct symtable *st, PyObject *id, + PyObject *name) +{ + int i, v, x; + PyObject *info; + PySymtableEntryObject *ste; + + ste = (PySymtableEntryObject *)PyDict_GetItem(st->st_symbols, id); + if (ste == NULL) + return -1; + + info = PyDict_GetItem(ste->ste_symbols, name); + if (info == NULL) + return 0; + v = PyInt_AS_LONG(info); + if (is_free(v)) { + if (symtable_add_def_o(st, ste->ste_symbols, name, + DEF_FREE_GLOBAL) < 0) + return -1; + } else + /* If the name is defined here or declared global, + then the recursion stops. */ + return 0; + + for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) { + PySymtableEntryObject *child; + child = (PySymtableEntryObject *) + PyList_GET_ITEM(ste->ste_children, i); + x = symtable_undo_free(st, child->ste_id, name); + if (x < 0) + return x; + } + return 0; +} + +/* symtable_enter_scope() gets a reference via PySymtableEntry_New(). + This reference is released when the scope is exited, via the DECREF + in symtable_exit_scope(). +*/ + +static int +symtable_exit_scope(struct symtable *st) +{ + int end; + + if (st->st_pass == 1) + symtable_update_free_vars(st); + Py_DECREF(st->st_cur); + end = PyList_GET_SIZE(st->st_stack) - 1; + st->st_cur = (PySymtableEntryObject *)PyList_GET_ITEM(st->st_stack, + end); + if (PySequence_DelItem(st->st_stack, end) < 0) + return -1; + return 0; +} + +static void +symtable_enter_scope(struct symtable *st, char *name, int type, + int lineno) +{ + PySymtableEntryObject *prev = NULL; + + if (st->st_cur) { + prev = st->st_cur; + if (PyList_Append(st->st_stack, (PyObject *)st->st_cur) < 0) { + st->st_errors++; + return; + } + } + st->st_cur = (PySymtableEntryObject *) + PySymtableEntry_New(st, name, type, lineno); + if (st->st_cur == NULL) { + st->st_errors++; + return; + } + if (strcmp(name, TOP) == 0) + st->st_global = st->st_cur->ste_symbols; + if (prev && st->st_pass == 1) { + if (PyList_Append(prev->ste_children, + (PyObject *)st->st_cur) < 0) + st->st_errors++; + } +} + +static int +symtable_lookup(struct symtable *st, char *name) +{ + char buffer[MANGLE_LEN]; + PyObject *v; + int flags; + + if (_Py_Mangle(st->st_private, name, buffer, sizeof(buffer))) + name = buffer; + v = PyDict_GetItemString(st->st_cur->ste_symbols, name); + if (v == NULL) { + if (PyErr_Occurred()) + return -1; + else + return 0; + } + + flags = PyInt_AS_LONG(v); + return flags; +} + +static int +symtable_add_def(struct symtable *st, char *name, int flag) +{ + PyObject *s; + char buffer[MANGLE_LEN]; + int ret; + + /* Warn about None, except inside a tuple (where the assignment + code already issues a warning). */ + if ((flag & DEF_PARAM) && !(flag & DEF_INTUPLE) && + *name == 'N' && strcmp(name, "None") == 0) + { + if (symtable_warn(st, "argument named None")) + return -1; + } + if (_Py_Mangle(st->st_private, name, buffer, sizeof(buffer))) + name = buffer; + if ((s = PyString_InternFromString(name)) == NULL) + return -1; + ret = symtable_add_def_o(st, st->st_cur->ste_symbols, s, flag); + Py_DECREF(s); + return ret; +} + +/* Must only be called with mangled names */ + +static int +symtable_add_def_o(struct symtable *st, PyObject *dict, + PyObject *name, int flag) +{ + PyObject *o; + int val; + + if ((o = PyDict_GetItem(dict, name))) { + val = PyInt_AS_LONG(o); + if ((flag & DEF_PARAM) && (val & DEF_PARAM)) { + PyErr_Format(PyExc_SyntaxError, DUPLICATE_ARGUMENT, + PyString_AsString(name)); + return symtable_error(st, 0); + } + val |= flag; + } else + val = flag; + o = PyInt_FromLong(val); + if (o == NULL) + return -1; + if (PyDict_SetItem(dict, name, o) < 0) { + Py_DECREF(o); + return -1; + } + Py_DECREF(o); + + if (flag & DEF_PARAM) { + if (PyList_Append(st->st_cur->ste_varnames, name) < 0) + return -1; + } else if (flag & DEF_GLOBAL) { + /* XXX need to update DEF_GLOBAL for other flags too; + perhaps only DEF_FREE_GLOBAL */ + if ((o = PyDict_GetItem(st->st_global, name))) { + val = PyInt_AS_LONG(o); + val |= flag; + } else + val = flag; + o = PyInt_FromLong(val); + if (o == NULL) + return -1; + if (PyDict_SetItem(st->st_global, name, o) < 0) { + Py_DECREF(o); + return -1; + } + Py_DECREF(o); + } + return 0; +} + +#define symtable_add_use(ST, NAME) symtable_add_def((ST), (NAME), USE) + +/* Look for a yield stmt under n. Return 1 if found, else 0. + This hack is used to look inside "if 0:" blocks (which are normally + ignored) in case those are the only places a yield occurs (so that this + function is a generator). */ +static int +look_for_yield(node *n) +{ + int i; + + for (i = 0; i < NCH(n); ++i) { + node *kid = CHILD(n, i); + + switch (TYPE(kid)) { + + case classdef: + case funcdef: + case lambdef: + /* Stuff in nested functions and classes can't make + the parent a generator. */ + return 0; + + case yield_stmt: + return 1; + + default: + if (look_for_yield(kid)) + return 1; + } + } + return 0; +} + +static void +symtable_node(struct symtable *st, node *n) +{ + int i; + + loop: + switch (TYPE(n)) { + case funcdef: { + char *func_name = STR(CHILD(n, 1)); + symtable_add_def(st, func_name, DEF_LOCAL); + symtable_default_args(st, CHILD(n, 2)); + symtable_enter_scope(st, func_name, TYPE(n), n->n_lineno); + symtable_funcdef(st, n); + symtable_exit_scope(st); + break; + } + case lambdef: + if (NCH(n) == 4) + symtable_default_args(st, CHILD(n, 1)); + symtable_enter_scope(st, "lambda", TYPE(n), n->n_lineno); + symtable_funcdef(st, n); + symtable_exit_scope(st); + break; + case classdef: { + char *tmp, *class_name = STR(CHILD(n, 1)); + symtable_add_def(st, class_name, DEF_LOCAL); + if (TYPE(CHILD(n, 2)) == LPAR) { + node *bases = CHILD(n, 3); + int i; + for (i = 0; i < NCH(bases); i += 2) { + symtable_node(st, CHILD(bases, i)); + } + } + symtable_enter_scope(st, class_name, TYPE(n), n->n_lineno); + tmp = st->st_private; + st->st_private = class_name; + symtable_node(st, CHILD(n, NCH(n) - 1)); + st->st_private = tmp; + symtable_exit_scope(st); + break; + } + case if_stmt: + for (i = 0; i + 3 < NCH(n); i += 4) { + if (is_constant_false(NULL, (CHILD(n, i + 1)))) { + if (st->st_cur->ste_generator == 0) + st->st_cur->ste_generator = + look_for_yield(CHILD(n, i+3)); + continue; + } + symtable_node(st, CHILD(n, i + 1)); + symtable_node(st, CHILD(n, i + 3)); + } + if (i + 2 < NCH(n)) + symtable_node(st, CHILD(n, i + 2)); + break; + case global_stmt: + symtable_global(st, n); + break; + case import_stmt: + symtable_import(st, n); + break; + case exec_stmt: { + st->st_cur->ste_optimized |= OPT_EXEC; + symtable_node(st, CHILD(n, 1)); + if (NCH(n) > 2) + symtable_node(st, CHILD(n, 3)); + else { + st->st_cur->ste_optimized |= OPT_BARE_EXEC; + st->st_cur->ste_opt_lineno = n->n_lineno; + } + if (NCH(n) > 4) + symtable_node(st, CHILD(n, 5)); + break; + + } + case assert_stmt: + if (Py_OptimizeFlag) + return; + if (NCH(n) == 2) { + n = CHILD(n, 1); + goto loop; + } else { + symtable_node(st, CHILD(n, 1)); + n = CHILD(n, 3); + goto loop; + } + case except_clause: + if (NCH(n) == 4) + symtable_assign(st, CHILD(n, 3), 0); + if (NCH(n) > 1) { + n = CHILD(n, 1); + goto loop; + } + break; + case del_stmt: + symtable_assign(st, CHILD(n, 1), 0); + break; + case yield_stmt: + st->st_cur->ste_generator = 1; + n = CHILD(n, 1); + goto loop; + case expr_stmt: + if (NCH(n) == 1) + n = CHILD(n, 0); + else { + if (TYPE(CHILD(n, 1)) == augassign) { + symtable_assign(st, CHILD(n, 0), 0); + symtable_node(st, CHILD(n, 2)); + break; + } else { + int i; + for (i = 0; i < NCH(n) - 2; i += 2) + symtable_assign(st, CHILD(n, i), 0); + n = CHILD(n, NCH(n) - 1); + } + } + goto loop; + case list_iter: + /* only occurs when there are multiple for loops + in a list comprehension */ + n = CHILD(n, 0); + if (TYPE(n) == list_for) + symtable_list_for(st, n); + else { + REQ(n, list_if); + symtable_node(st, CHILD(n, 1)); + if (NCH(n) == 3) { + n = CHILD(n, 2); + goto loop; + } + } + break; + case for_stmt: + symtable_assign(st, CHILD(n, 1), 0); + for (i = 3; i < NCH(n); ++i) + if (TYPE(CHILD(n, i)) >= single_input) + symtable_node(st, CHILD(n, i)); + break; + /* The remaining cases fall through to default except in + special circumstances. This requires the individual cases + to be coded with great care, even though they look like + rather innocuous. Each case must double-check TYPE(n). + */ + case argument: + if (TYPE(n) == argument && NCH(n) == 3) { + n = CHILD(n, 2); + goto loop; + } + /* fall through */ + case listmaker: + if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) { + symtable_list_comprehension(st, n); + break; + } + /* fall through */ + case atom: + if (TYPE(n) == atom && TYPE(CHILD(n, 0)) == NAME) { + symtable_add_use(st, STR(CHILD(n, 0))); + break; + } + /* fall through */ + default: + /* Walk over every non-token child with a special case + for one child. + */ + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto loop; + } + for (i = 0; i < NCH(n); ++i) + if (TYPE(CHILD(n, i)) >= single_input) + symtable_node(st, CHILD(n, i)); + } +} + +static void +symtable_funcdef(struct symtable *st, node *n) +{ + node *body; + + if (TYPE(n) == lambdef) { + if (NCH(n) == 4) + symtable_params(st, CHILD(n, 1)); + } else + symtable_params(st, CHILD(n, 2)); + body = CHILD(n, NCH(n) - 1); + symtable_node(st, body); +} + +/* The next two functions parse the argument tuple. + symtable_default_args() checks for names in the default arguments, + which are references in the defining scope. symtable_params() + parses the parameter names, which are defined in the function's + body. + + varargslist: + (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) + | fpdef ['=' test] (',' fpdef ['=' test])* [','] +*/ + +static void +symtable_default_args(struct symtable *st, node *n) +{ + node *c; + int i; + + if (TYPE(n) == parameters) { + n = CHILD(n, 1); + if (TYPE(n) == RPAR) + return; + } + REQ(n, varargslist); + for (i = 0; i < NCH(n); i += 2) { + c = CHILD(n, i); + if (TYPE(c) == STAR || TYPE(c) == DOUBLESTAR) { + break; + } + if (i > 0 && (TYPE(CHILD(n, i - 1)) == EQUAL)) + symtable_node(st, CHILD(n, i)); + } +} + +static void +symtable_params(struct symtable *st, node *n) +{ + int i, complex = -1, ext = 0; + node *c = NULL; + + if (TYPE(n) == parameters) { + n = CHILD(n, 1); + if (TYPE(n) == RPAR) + return; + } + REQ(n, varargslist); + for (i = 0; i < NCH(n); i += 2) { + c = CHILD(n, i); + if (TYPE(c) == STAR || TYPE(c) == DOUBLESTAR) { + ext = 1; + break; + } + if (TYPE(c) == test) { + continue; + } + if (TYPE(CHILD(c, 0)) == NAME) + symtable_add_def(st, STR(CHILD(c, 0)), DEF_PARAM); + else { + char nbuf[30]; + PyOS_snprintf(nbuf, sizeof(nbuf), ".%d", i); + symtable_add_def(st, nbuf, DEF_PARAM); + complex = i; + } + } + if (ext) { + c = CHILD(n, i); + if (TYPE(c) == STAR) { + i++; + symtable_add_def(st, STR(CHILD(n, i)), + DEF_PARAM | DEF_STAR); + i += 2; + if (i >= NCH(n)) + c = NULL; + else + c = CHILD(n, i); + } + if (c && TYPE(c) == DOUBLESTAR) { + i++; + symtable_add_def(st, STR(CHILD(n, i)), + DEF_PARAM | DEF_DOUBLESTAR); + } + } + if (complex >= 0) { + int j; + for (j = 0; j <= complex; j++) { + c = CHILD(n, j); + if (TYPE(c) == COMMA) + c = CHILD(n, ++j); + else if (TYPE(c) == EQUAL) + c = CHILD(n, j += 3); + if (TYPE(CHILD(c, 0)) == LPAR) + symtable_params_fplist(st, CHILD(c, 1)); + } + } +} + +static void +symtable_params_fplist(struct symtable *st, node *n) +{ + int i; + node *c; + + REQ(n, fplist); + for (i = 0; i < NCH(n); i += 2) { + c = CHILD(n, i); + REQ(c, fpdef); + if (NCH(c) == 1) + symtable_add_def(st, STR(CHILD(c, 0)), + DEF_PARAM | DEF_INTUPLE); + else + symtable_params_fplist(st, CHILD(c, 1)); + } + +} + +static void +symtable_global(struct symtable *st, node *n) +{ + int i; + + /* XXX It might be helpful to warn about module-level global + statements, but it's hard to tell the difference between + module-level and a string passed to exec. + */ + + for (i = 1; i < NCH(n); i += 2) { + char *name = STR(CHILD(n, i)); + int flags; + + flags = symtable_lookup(st, name); + if (flags < 0) + continue; + if (flags && flags != DEF_GLOBAL) { + char buf[500]; + if (flags & DEF_PARAM) { + PyErr_Format(PyExc_SyntaxError, + "name '%.400s' is local and global", + name); + symtable_error(st, 0); + return; + } + else { + if (flags & DEF_LOCAL) + PyOS_snprintf(buf, sizeof(buf), + GLOBAL_AFTER_ASSIGN, + name); + else + PyOS_snprintf(buf, sizeof(buf), + GLOBAL_AFTER_USE, name); + symtable_warn(st, buf); + } + } + symtable_add_def(st, name, DEF_GLOBAL); + } +} + +static void +symtable_list_comprehension(struct symtable *st, node *n) +{ + /* listmaker: test list_for */ + char tmpname[30]; + + REQ(n, listmaker); + PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", + ++st->st_cur->ste_tmpname); + symtable_add_def(st, tmpname, DEF_LOCAL); + symtable_list_for(st, CHILD(n, 1)); + symtable_node(st, CHILD(n, 0)); + --st->st_cur->ste_tmpname; +} + +static void +symtable_list_for(struct symtable *st, node *n) +{ + REQ(n, list_for); + /* list_for: for v in expr [list_iter] */ + symtable_assign(st, CHILD(n, 1), 0); + symtable_node(st, CHILD(n, 3)); + if (NCH(n) == 5) + symtable_node(st, CHILD(n, 4)); +} + +static void +symtable_import(struct symtable *st, node *n) +{ + int i; + /* import_stmt: 'import' dotted_as_name (',' dotted_as_name)* + | 'from' dotted_name 'import' + ('*' | import_as_name (',' import_as_name)*) + import_as_name: NAME [NAME NAME] + */ + if (STR(CHILD(n, 0))[0] == 'f') { /* from */ + node *dotname = CHILD(n, 1); + if (strcmp(STR(CHILD(dotname, 0)), "__future__") == 0) { + /* check for bogus imports */ + if (n->n_lineno >= st->st_future->ff_last_lineno) { + PyErr_SetString(PyExc_SyntaxError, + LATE_FUTURE); + symtable_error(st, n->n_lineno); + return; + } + } + if (TYPE(CHILD(n, 3)) == STAR) { + if (st->st_cur->ste_type != TYPE_MODULE) { + if (symtable_warn(st, + "import * only allowed at module level") < 0) + return; + } + st->st_cur->ste_optimized |= OPT_IMPORT_STAR; + st->st_cur->ste_opt_lineno = n->n_lineno; + } else { + for (i = 3; i < NCH(n); i += 2) { + node *c = CHILD(n, i); + if (NCH(c) > 1) /* import as */ + symtable_assign(st, CHILD(c, 2), + DEF_IMPORT); + else + symtable_assign(st, CHILD(c, 0), + DEF_IMPORT); + } + } + } else { + for (i = 1; i < NCH(n); i += 2) { + symtable_assign(st, CHILD(n, i), DEF_IMPORT); + } + } +} + +/* The third argument to symatble_assign() is a flag to be passed to + symtable_add_def() if it is eventually called. The flag is useful + to specify the particular type of assignment that should be + recorded, e.g. an assignment caused by import. + */ + +static void +symtable_assign(struct symtable *st, node *n, int def_flag) +{ + node *tmp; + int i; + + loop: + switch (TYPE(n)) { + case lambdef: + /* invalid assignment, e.g. lambda x:x=2. The next + pass will catch this error. */ + return; + case power: + if (NCH(n) > 2) { + for (i = 2; i < NCH(n); ++i) + if (TYPE(CHILD(n, i)) != DOUBLESTAR) + symtable_node(st, CHILD(n, i)); + } + if (NCH(n) > 1) { + symtable_node(st, CHILD(n, 0)); + symtable_node(st, CHILD(n, 1)); + } else { + n = CHILD(n, 0); + goto loop; + } + return; + case listmaker: + if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) { + /* XXX This is an error, but the next pass + will catch it. */ + return; + } else { + for (i = 0; i < NCH(n); i += 2) + symtable_assign(st, CHILD(n, i), def_flag); + } + return; + case exprlist: + case testlist: + case testlist1: + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto loop; + } + else { + int i; + for (i = 0; i < NCH(n); i += 2) + symtable_assign(st, CHILD(n, i), def_flag); + return; + } + case atom: + tmp = CHILD(n, 0); + if (TYPE(tmp) == LPAR || TYPE(tmp) == LSQB) { + n = CHILD(n, 1); + goto loop; + } else if (TYPE(tmp) == NAME) { + if (strcmp(STR(tmp), "__debug__") == 0) { + PyErr_SetString(PyExc_SyntaxError, + ASSIGN_DEBUG); + symtable_error(st, n->n_lineno); + return; + } + symtable_add_def(st, STR(tmp), DEF_LOCAL | def_flag); + } + return; + case dotted_as_name: + if (NCH(n) == 3) + symtable_add_def(st, STR(CHILD(n, 2)), + DEF_LOCAL | def_flag); + else + symtable_add_def(st, + STR(CHILD(CHILD(n, + 0), 0)), + DEF_LOCAL | def_flag); + return; + case dotted_name: + symtable_add_def(st, STR(CHILD(n, 0)), DEF_LOCAL | def_flag); + return; + case NAME: + symtable_add_def(st, STR(n), DEF_LOCAL | def_flag); + return; + default: + if (NCH(n) == 0) + return; + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto loop; + } + /* Should only occur for errors like x + 1 = 1, + which will be caught in the next pass. */ + for (i = 0; i < NCH(n); ++i) + if (TYPE(CHILD(n, i)) >= single_input) + symtable_assign(st, CHILD(n, i), def_flag); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dup2.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dup2.c new file mode 100644 index 00000000..cc067e04 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dup2.c @@ -0,0 +1,30 @@ +/* + * Public domain dup2() lookalike + * by Curtis Jackson @ AT&T Technologies, Burlington, NC + * electronic address: burl!rcj + * + * dup2 performs the following functions: + * + * Check to make sure that fd1 is a valid open file descriptor. + * Check to see if fd2 is already open; if so, close it. + * Duplicate fd1 onto fd2; checking to make sure fd2 is a valid fd. + * Return fd2 if all went well; return BADEXIT otherwise. + */ + +#include + +#define BADEXIT -1 + +int +dup2(int fd1, int fd2) +{ + if (fd1 != fd2) { + if (fcntl(fd1, F_GETFL) < 0) + return BADEXIT; + if (fcntl(fd2, F_GETFL) >= 0) + close(fd2); + if (fcntl(fd1, F_DUPFD, fd2) < 0) + return BADEXIT; + } + return fd2; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_aix.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_aix.c new file mode 100644 index 00000000..f9a0b2ee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_aix.c @@ -0,0 +1,183 @@ + +/* Support for dynamic loading of extension modules */ + +#include "Python.h" +#include "importdl.h" + +#include /* for isdigit() */ +#include /* for global errno */ +#include /* for strerror() */ +#include /* for malloc(), free() */ +#include + + +#ifdef AIX_GENUINE_CPLUSPLUS +#include "/usr/lpp/xlC/include/load.h" +#define aix_load loadAndInit +#else +#define aix_load load +#endif + + +extern char *Py_GetProgramName(void); + +typedef struct Module { + struct Module *next; + void *entry; +} Module, *ModulePtr; + +const struct filedescr _PyImport_DynLoadFiletab[] = { + {".so", "rb", C_EXTENSION}, + {"module.so", "rb", C_EXTENSION}, + {0, 0} +}; + +static int +aix_getoldmodules(void **modlistptr) +{ + register ModulePtr modptr, prevmodptr; + register struct ld_info *ldiptr; + register char *ldibuf; + register int errflag, bufsize = 1024; + register unsigned int offset; + char *progname = Py_GetProgramName(); + + /* + -- Get the list of loaded modules into ld_info structures. + */ + if ((ldibuf = malloc(bufsize)) == NULL) { + PyErr_SetString(PyExc_ImportError, strerror(errno)); + return -1; + } + while ((errflag = loadquery(L_GETINFO, ldibuf, bufsize)) == -1 + && errno == ENOMEM) { + free(ldibuf); + bufsize += 1024; + if ((ldibuf = malloc(bufsize)) == NULL) { + PyErr_SetString(PyExc_ImportError, strerror(errno)); + return -1; + } + } + if (errflag == -1) { + PyErr_SetString(PyExc_ImportError, strerror(errno)); + return -1; + } + /* + -- Make the modules list from the ld_info structures. + */ + ldiptr = (struct ld_info *)ldibuf; + prevmodptr = NULL; + do { + if (strstr(progname, ldiptr->ldinfo_filename) == NULL && + strstr(ldiptr->ldinfo_filename, "python") == NULL) { + /* + -- Extract only the modules belonging to the main + -- executable + those containing "python" as a + -- substring (like the "python[version]" binary or + -- "libpython[version].a" in case it's a shared lib). + */ + offset = (unsigned int)ldiptr->ldinfo_next; + ldiptr = (struct ld_info *)((char*)ldiptr + offset); + continue; + } + if ((modptr = (ModulePtr)malloc(sizeof(Module))) == NULL) { + PyErr_SetString(PyExc_ImportError, strerror(errno)); + while (*modlistptr) { + modptr = (ModulePtr)*modlistptr; + *modlistptr = (void *)modptr->next; + free(modptr); + } + return -1; + } + modptr->entry = ldiptr->ldinfo_dataorg; + modptr->next = NULL; + if (prevmodptr == NULL) + *modlistptr = (void *)modptr; + else + prevmodptr->next = modptr; + prevmodptr = modptr; + offset = (unsigned int)ldiptr->ldinfo_next; + ldiptr = (struct ld_info *)((char*)ldiptr + offset); + } while (offset); + free(ldibuf); + return 0; +} + + +static void +aix_loaderror(const char *pathname) +{ + + char *message[1024], errbuf[1024]; + register int i,j; + + struct errtab { + int errNo; + char *errstr; + } load_errtab[] = { + {L_ERROR_TOOMANY, "too many errors, rest skipped."}, + {L_ERROR_NOLIB, "can't load library:"}, + {L_ERROR_UNDEF, "can't find symbol in library:"}, + {L_ERROR_RLDBAD, + "RLD index out of range or bad relocation type:"}, + {L_ERROR_FORMAT, "not a valid, executable xcoff file:"}, + {L_ERROR_MEMBER, + "file not an archive or does not contain requested member:"}, + {L_ERROR_TYPE, "symbol table mismatch:"}, + {L_ERROR_ALIGN, "text alignment in file is wrong."}, + {L_ERROR_SYSTEM, "System error:"}, + {L_ERROR_ERRNO, NULL} + }; + +#define LOAD_ERRTAB_LEN (sizeof(load_errtab)/sizeof(load_errtab[0])) +#define ERRBUF_APPEND(s) strncat(errbuf, s, sizeof(errbuf)-strlen(errbuf)-1) + + PyOS_snprintf(errbuf, sizeof(errbuf), "from module %.200s ", pathname); + + if (!loadquery(L_GETMESSAGES, &message[0], sizeof(message))) { + ERRBUF_APPEND(strerror(errno)); + ERRBUF_APPEND("\n"); + } + for(i = 0; message[i] && *message[i]; i++) { + int nerr = atoi(message[i]); + for (j=0; j +#include + +#include "Python.h" +#include "importdl.h" + + +const struct filedescr _PyImport_DynLoadFiletab[] = { + {".so", "rb", C_EXTENSION}, + {"module.so", "rb", C_EXTENSION}, + {0, 0} +}; + +dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname, + const char *pathname, FILE *fp) +{ + void *p; + int lib; + char funcname[258]; + + if (Py_VerboseFlag) + printf("load_library %s\n", pathname); + + lib = load_library(pathname, 0); + if (lib < 0) { + char buf[512]; + if (Py_VerboseFlag) + perror(pathname); + PyOS_snprintf(buf, sizeof(buf), "Failed to load %.200s: %.200s", + pathname, strerror(errno)); + PyErr_SetString(PyExc_ImportError, buf); + return NULL; + } + PyOS_snprintf(funcname, sizeof(funcname), "init%.200s", shortname); + if (Py_VerboseFlag) + printf("get_symbol_address %s\n", funcname); + if (get_symbol_address(lib, funcname, -1, &p) < 0) { + p = NULL; + if (Py_VerboseFlag) + perror(funcname); + } + + return (dl_funcptr) p; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_beos.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_beos.c new file mode 100644 index 00000000..4d6c3af3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_beos.c @@ -0,0 +1,254 @@ + +/* Support for dynamic loading of extension modules */ + +#include +#include +#include + +#include "Python.h" +#include "importdl.h" + +const struct filedescr _PyImport_DynLoadFiletab[] = { + {".so", "rb", C_EXTENSION}, + {"module.so", "rb", C_EXTENSION}, + {0, 0} +}; + +#if defined(MAXPATHLEN) && !defined(_SYS_PARAM_H) +#undef MAXPATHLEN +#endif + +#ifdef WITH_THREAD +#include "pythread.h" +static PyThread_type_lock beos_dyn_lock; +#endif + +static PyObject *beos_dyn_images = NULL; + +/* ---------------------------------------------------------------------- + * BeOS dynamic loading support + * + * This uses shared libraries, but BeOS has its own way of doing things + * (much easier than dlfnc.h, from the look of things). We'll use a + * Python Dictionary object to store the images_ids so we can be very + * nice and unload them when we exit. + * + * Note that this is thread-safe. Probably irrelevent, because of losing + * systems... Python probably disables threads while loading modules. + * Note the use of "probably"! Better to be safe than sorry. [chrish] + * + * As of 1.5.1 this should also work properly when you've configured + * Python without thread support; the 1.5 version required it, which wasn't + * very friendly. Note that I haven't tested it without threading... why + * would you want to avoid threads on BeOS? [chrish] + * + * As of 1.5.2, the PyImport_BeImageID() function has been removed; Donn + * tells me it's not necessary anymore because of PyCObject_Import(). + * [chrish] + */ + +/* Whack an item; the item is an image_id in disguise, so we'll call + * unload_add_on() for it. + */ +static void beos_nuke_dyn( PyObject *item ) +{ + status_t retval; + + if( item ) { + image_id id = (image_id)PyInt_AsLong( item ); + + retval = unload_add_on( id ); + } +} + +/* atexit() handler that'll call unload_add_on() for every item in the + * dictionary. + */ +static void beos_cleanup_dyn( void ) +{ + if( beos_dyn_images ) { + int idx; + int list_size; + PyObject *id_list; + +#ifdef WITH_THREAD + PyThread_acquire_lock( beos_dyn_lock, 1 ); +#endif + + id_list = PyDict_Values( beos_dyn_images ); + + list_size = PyList_Size( id_list ); + for( idx = 0; idx < list_size; idx++ ) { + PyObject *the_item; + + the_item = PyList_GetItem( id_list, idx ); + beos_nuke_dyn( the_item ); + } + + PyDict_Clear( beos_dyn_images ); + +#ifdef WITH_THREAD + PyThread_free_lock( beos_dyn_lock ); +#endif + } +} + +/* + * Initialize our dictionary, and the dictionary mutex. + */ +static void beos_init_dyn( void ) +{ + /* We're protected from a race condition here by the atomic init_count + * variable. + */ + static int32 init_count = 0; + int32 val; + + val = atomic_add( &init_count, 1 ); + if( beos_dyn_images == NULL && val == 0 ) { + beos_dyn_images = PyDict_New(); +#ifdef WITH_THREAD + beos_dyn_lock = PyThread_allocate_lock(); +#endif + atexit( beos_cleanup_dyn ); + } +} + +/* + * Add an image_id to the dictionary; the module name of the loaded image + * is the key. Note that if the key is already in the dict, we unload + * that image; this should allow reload() to work on dynamically loaded + * modules (super-keen!). + */ +static void beos_add_dyn( char *name, image_id id ) +{ + int retval; + PyObject *py_id; + + if( beos_dyn_images == NULL ) { + beos_init_dyn(); + } + +#ifdef WITH_THREAD + retval = PyThread_acquire_lock( beos_dyn_lock, 1 ); +#endif + + /* If there's already an object with this key in the dictionary, + * we're doing a reload(), so let's nuke it. + */ + py_id = PyDict_GetItemString( beos_dyn_images, name ); + if( py_id ) { + beos_nuke_dyn( py_id ); + retval = PyDict_DelItemString( beos_dyn_images, name ); + } + + py_id = PyInt_FromLong( (long)id ); + if( py_id ) { + retval = PyDict_SetItemString( beos_dyn_images, name, py_id ); + } + +#ifdef WITH_THREAD + PyThread_release_lock( beos_dyn_lock ); +#endif +} + + + +dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname, + const char *pathname, FILE *fp) +{ + dl_funcptr p; + image_id the_id; + status_t retval; + char fullpath[PATH_MAX]; + char funcname[258]; + + if( Py_VerboseFlag ) { + printf( "load_add_on( %s )\n", pathname ); + } + + /* Hmm, this old bug appears to have regenerated itself; if the + * path isn't absolute, load_add_on() will fail. Reported to Be + * April 21, 1998. + */ + if( pathname[0] != '/' ) { + (void)getcwd( fullpath, PATH_MAX ); + (void)strncat( fullpath, "/", PATH_MAX ); + (void)strncat( fullpath, pathname, PATH_MAX ); + + if( Py_VerboseFlag ) { + printf( "load_add_on( %s )\n", fullpath ); + } + } else { + (void)strcpy( fullpath, pathname ); + } + + the_id = load_add_on( fullpath ); + if( the_id < B_NO_ERROR ) { + /* It's too bad load_add_on() doesn't set errno or something... + */ + char buff[256]; /* hate hard-coded string sizes... */ + + if( Py_VerboseFlag ) { + printf( "load_add_on( %s ) failed", fullpath ); + } + + if( the_id == B_ERROR ) + PyOS_snprintf( buff, sizeof(buff), + "BeOS: Failed to load %.200s", + fullpath ); + else + PyOS_snprintf( buff, sizeof(buff), + "Unknown error loading %.200s", + fullpath ); + + PyErr_SetString( PyExc_ImportError, buff ); + return NULL; + } + + PyOS_snprintf(funcname, sizeof(funcname), "init%.200s", shortname); + if( Py_VerboseFlag ) { + printf( "get_image_symbol( %s )\n", funcname ); + } + + retval = get_image_symbol( the_id, funcname, B_SYMBOL_TYPE_TEXT, &p ); + if( retval != B_NO_ERROR || p == NULL ) { + /* That's bad, we can't find that symbol in the module... + */ + char buff[256]; /* hate hard-coded string sizes... */ + + if( Py_VerboseFlag ) { + printf( "get_image_symbol( %s ) failed", funcname ); + } + + switch( retval ) { + case B_BAD_IMAGE_ID: + PyOS_snprintf( buff, sizeof(buff), + "can't load init function for dynamic module: " + "Invalid image ID for %.180s", fullpath ); + break; + case B_BAD_INDEX: + PyOS_snprintf( buff, sizeof(buff), + "can't load init function for dynamic module: " + "Bad index for %.180s", funcname ); + break; + default: + PyOS_snprintf( buff, sizeof(buff), + "can't load init function for dynamic module: " + "Unknown error looking up %.180s", funcname ); + break; + } + + retval = unload_add_on( the_id ); + + PyErr_SetString( PyExc_ImportError, buff ); + return NULL; + } + + /* Save the module name and image ID for later so we can clean up + * gracefully. + */ + beos_add_dyn( fqname, the_id ); + + return p; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_dl.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_dl.c new file mode 100644 index 00000000..794e3f8b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_dl.c @@ -0,0 +1,26 @@ + +/* Support for dynamic loading of extension modules */ + +#include "dl.h" + +#include "Python.h" +#include "importdl.h" + + +extern char *Py_GetProgramName(void); + +const struct filedescr _PyImport_DynLoadFiletab[] = { + {".o", "rb", C_EXTENSION}, + {"module.o", "rb", C_EXTENSION}, + {0, 0} +}; + + +dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname, + const char *pathname, FILE *fp) +{ + char funcname[258]; + + PyOS_snprintf(funcname, sizeof(funcname), "init%.200s", shortname); + return dl_loadmod(Py_GetProgramName(), pathname, funcname); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_hpux.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_hpux.c new file mode 100644 index 00000000..7909b927 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_hpux.c @@ -0,0 +1,58 @@ + +/* Support for dynamic loading of extension modules */ + +#include "dl.h" +#include + +#include "Python.h" +#include "importdl.h" + +#if defined(__hp9000s300) +#define FUNCNAME_PATTERN "_init%.200s" +#else +#define FUNCNAME_PATTERN "init%.200s" +#endif + +const struct filedescr _PyImport_DynLoadFiletab[] = { + {".sl", "rb", C_EXTENSION}, + {"module.sl", "rb", C_EXTENSION}, + {0, 0} +}; + +dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname, + const char *pathname, FILE *fp) +{ + dl_funcptr p; + shl_t lib; + int flags; + char funcname[258]; + + flags = BIND_FIRST | BIND_DEFERRED; + if (Py_VerboseFlag) { + flags = BIND_FIRST | BIND_IMMEDIATE | + BIND_NONFATAL | BIND_VERBOSE; + printf("shl_load %s\n",pathname); + } + lib = shl_load(pathname, flags, 0); + /* XXX Chuck Blake once wrote that 0 should be BIND_NOSTART? */ + if (lib == NULL) { + char buf[256]; + if (Py_VerboseFlag) + perror(pathname); + PyOS_snprintf(buf, sizeof(buf), "Failed to load %.200s", + pathname); + PyErr_SetString(PyExc_ImportError, buf); + return NULL; + } + PyOS_snprintf(funcname, sizeof(funcname), FUNCNAME_PATTERN, shortname); + if (Py_VerboseFlag) + printf("shl_findsym %s\n", funcname); + if (shl_findsym(&lib, funcname, TYPE_UNDEFINED, (void *) &p) == -1) { + shl_unload(lib); + p = NULL; + } + if (p == NULL && Py_VerboseFlag) + perror(funcname); + + return p; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_mac.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_mac.c new file mode 100644 index 00000000..b39b167f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_mac.c @@ -0,0 +1,113 @@ + +/* Support for dynamic loading of extension modules */ + +#include "Python.h" +#include "importdl.h" + +#include +#include +#ifdef USE_GUSI1 +#include "TFileSpec.h" /* for Path2FSSpec() */ +#endif +#include +#include +#include "macdefs.h" +#include "macglue.h" + + +const struct filedescr _PyImport_DynLoadFiletab[] = { + {".slb", "rb", C_EXTENSION}, + {".carbon.slb", "rb", C_EXTENSION}, + {0, 0} +}; + + +dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname, + const char *pathname, FILE *fp) +{ + dl_funcptr p; + char funcname[258]; + + /* + ** Dynamic loading of CFM shared libraries on the Mac. The + ** code has become more convoluted than it was, because we + ** want to be able to put multiple modules in a single + ** file. For this reason, we have to determine the fragment + ** name, and we cannot use the library entry point but we have + ** to locate the correct init routine "by hand". + */ + FSSpec libspec; + CFragConnectionID connID; + Ptr mainAddr; + Str255 errMessage; + OSErr err; +#ifndef USE_GUSI1 + Boolean isfolder, didsomething; +#endif + char buf[512]; + Str63 fragname; + Ptr symAddr; + CFragSymbolClass class; + + /* First resolve any aliases to find the real file */ +#ifdef USE_GUSI1 + err = Path2FSSpec(pathname, &libspec); +#else + c2pstrcpy((unsigned char *)buf, pathname); + (void)FSMakeFSSpec(0, 0, (unsigned char *)buf, &libspec); + err = ResolveAliasFile(&libspec, 1, &isfolder, &didsomething); +#endif + if ( err ) { + PyOS_snprintf(buf, sizeof(buf), + "%.200s: %.200s", pathname, PyMac_StrError(err)); + PyErr_SetString(PyExc_ImportError, buf); + return NULL; + } + /* Next, determine the fragment name, + by stripping '.slb' and 'module' */ + memcpy(fragname+1, libspec.name+1, libspec.name[0]); + fragname[0] = libspec.name[0]; + if( strncmp((char *)(fragname+1+fragname[0]-4), + ".slb", 4) == 0 ) + fragname[0] -= 4; + if ( strncmp((char *)(fragname+1+fragname[0]-6), + "module", 6) == 0 ) + fragname[0] -= 6; + /* Load the fragment + (or return the connID if it is already loaded */ + err = GetDiskFragment(&libspec, 0, 0, fragname, + kLoadCFrag, &connID, &mainAddr, + errMessage); + if ( err == cfragImportTooOldErr || err == cfragImportTooNewErr ) { + /* + ** Special-case code: if PythonCore is too old or too new this means + ** the dynamic module was meant for a different Python. + */ + if (errMessage[0] == 10 && strncmp((char *)errMessage+1, "PythonCore", 10) == 0 ) { + PyOS_snprintf(buf, sizeof(buf), + "Dynamic module was built for %s version of MacPython", + (err == cfragImportTooOldErr ? "a newer" : "an older")); + PyErr_SetString(PyExc_ImportError, buf); + return NULL; + } + } + if ( err ) { + PyOS_snprintf(buf, sizeof(buf), "%.*s: %.200s", + errMessage[0], errMessage+1, + PyMac_StrError(err)); + PyErr_SetString(PyExc_ImportError, buf); + return NULL; + } + /* Locate the address of the correct init function */ + PyOS_snprintf(funcname, sizeof(funcname), "init%.200s", shortname); + err = FindSymbol(connID, Pstring(funcname), &symAddr, &class); + if ( err ) { + PyOS_snprintf(buf, sizeof(buf), "%s: %.200s", + funcname, PyMac_StrError(err)); + PyErr_SetString(PyExc_ImportError, buf); + return NULL; + } + p = (dl_funcptr)symAddr; + + return p; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_next.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_next.c new file mode 100644 index 00000000..4b8a1295 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_next.c @@ -0,0 +1,114 @@ + +/* Support for dynamic loading of extension modules on Mac OS X +** All references to "NeXT" are for historical reasons. +*/ + +#include "Python.h" +#include "importdl.h" + +#include + +const struct filedescr _PyImport_DynLoadFiletab[] = { + {".so", "rb", C_EXTENSION}, + {"module.so", "rb", C_EXTENSION}, + {0, 0} +}; + +/* +** Python modules are Mach-O MH_BUNDLE files. The best way to load these +** is each in a private namespace, so you can load, say, a module bar and a +** module foo.bar. If we load everything in the global namespace the two +** initbar() symbols will conflict. +** However, it seems some extension packages depend upon being able to access +** each others' global symbols. There seems to be no way to eat our cake and +** have it, so the USE_DYLD_GLOBAL_NAMESPACE define determines which behaviour +** you get. +*/ + +#ifdef USE_DYLD_GLOBAL_NAMESPACE +#define LINKOPTIONS NSLINKMODULE_OPTION_BINDNOW|NSLINKMODULE_OPTION_RETURN_ON_ERROR +#else +#define LINKOPTIONS NSLINKMODULE_OPTION_BINDNOW| \ + NSLINKMODULE_OPTION_RETURN_ON_ERROR|NSLINKMODULE_OPTION_PRIVATE +#endif +dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname, + const char *pathname, FILE *fp) +{ + dl_funcptr p = NULL; + char funcname[258]; + NSObjectFileImageReturnCode rc; + NSObjectFileImage image; + NSModule newModule; + NSSymbol theSym; + const char *errString; + char errBuf[512]; + + PyOS_snprintf(funcname, sizeof(funcname), "_init%.200s", shortname); + +#ifdef USE_DYLD_GLOBAL_NAMESPACE + if (NSIsSymbolNameDefined(funcname)) { + theSym = NSLookupAndBindSymbol(funcname); + p = (dl_funcptr)NSAddressOfSymbol(theSym); + return p; + } +#endif + rc = NSCreateObjectFileImageFromFile(pathname, &image); + switch(rc) { + default: + case NSObjectFileImageFailure: + case NSObjectFileImageFormat: + /* for these a message is printed on stderr by dyld */ + errString = "Can't create object file image"; + break; + case NSObjectFileImageSuccess: + errString = NULL; + break; + case NSObjectFileImageInappropriateFile: + errString = "Inappropriate file type for dynamic loading"; + break; + case NSObjectFileImageArch: + errString = "Wrong CPU type in object file"; + break; + case NSObjectFileImageAccess: + errString = "Can't read object file (no access)"; + break; + } + if (errString == NULL) { + newModule = NSLinkModule(image, pathname, LINKOPTIONS); + if (newModule == NULL) { + int errNo; + const char *fileName, *moreErrorStr; + NSLinkEditErrors c; + NSLinkEditError( &c, &errNo, &fileName, &moreErrorStr ); + PyOS_snprintf(errBuf, 512, "Failure linking new module: %s: %s", + fileName, moreErrorStr); + errString = errBuf; + } + } + if (errString != NULL) { + PyErr_SetString(PyExc_ImportError, errString); + return NULL; + } +#ifdef USE_DYLD_GLOBAL_NAMESPACE + if (!NSIsSymbolNameDefined(funcname)) { + /* UnlinkModule() isn't implemented in current versions, but calling it does no harm */ + NSUnLinkModule(newModule, FALSE); + PyErr_Format(PyExc_ImportError, + "Loaded module does not contain symbol %.200s", + funcname); + return NULL; + } + theSym = NSLookupAndBindSymbol(funcname); +#else + theSym = NSLookupSymbolInModule(newModule, funcname); + if ( theSym == NULL ) { + NSUnLinkModule(newModule, FALSE); + PyErr_Format(PyExc_ImportError, + "Loaded module does not contain symbol %.200s", + funcname); + return NULL; + } +#endif + p = (dl_funcptr)NSAddressOfSymbol(theSym); + return p; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_os2.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_os2.c new file mode 100644 index 00000000..c43213a0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_os2.c @@ -0,0 +1,46 @@ + +/* Support for dynamic loading of extension modules */ + +#define INCL_DOSERRORS +#define INCL_DOSMODULEMGR +#include + +#include "Python.h" +#include "importdl.h" + + +const struct filedescr _PyImport_DynLoadFiletab[] = { + {".pyd", "rb", C_EXTENSION}, + {".dll", "rb", C_EXTENSION}, + {0, 0} +}; + +dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname, + const char *pathname, FILE *fp) +{ + dl_funcptr p; + APIRET rc; + HMODULE hDLL; + char failreason[256]; + char funcname[258]; + + rc = DosLoadModule(failreason, + sizeof(failreason), + pathname, + &hDLL); + + if (rc != NO_ERROR) { + char errBuf[256]; + PyOS_snprintf(errBuf, sizeof(errBuf), + "DLL load failed, rc = %d: %.200s", + rc, failreason); + PyErr_SetString(PyExc_ImportError, errBuf); + return NULL; + } + + PyOS_snprintf(funcname, sizeof(funcname), "init%.200s", shortname); + rc = DosQueryProcAddr(hDLL, 0L, funcname, &p); + if (rc != NO_ERROR) + p = NULL; /* Signify Failure to Acquire Entrypoint */ + return p; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_shlib.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_shlib.c new file mode 100644 index 00000000..eb59c394 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_shlib.c @@ -0,0 +1,139 @@ + +/* Support for dynamic loading of extension modules */ + +#include "Python.h" +#include "importdl.h" + +#include +#include + +#if defined(__NetBSD__) +#include +#if (NetBSD < 199712) +#include +#include +#define dlerror() "error in dynamic linking" +#endif +#endif /* NetBSD */ + +#ifdef HAVE_DLFCN_H +#include +#else +#if defined(PYOS_OS2) && defined(PYCC_GCC) +#include "dlfcn.h" +#endif +#endif + +#if (defined(__OpenBSD__) || defined(__NetBSD__)) && !defined(__ELF__) +#define LEAD_UNDERSCORE "_" +#else +#define LEAD_UNDERSCORE "" +#endif + + +const struct filedescr _PyImport_DynLoadFiletab[] = { +#ifdef __CYGWIN__ + {".dll", "rb", C_EXTENSION}, + {"module.dll", "rb", C_EXTENSION}, +#else +#if defined(PYOS_OS2) && defined(PYCC_GCC) + {".pyd", "rb", C_EXTENSION}, + {".dll", "rb", C_EXTENSION}, +#else +#ifdef __VMS + {".exe", "rb", C_EXTENSION}, + {".EXE", "rb", C_EXTENSION}, + {"module.exe", "rb", C_EXTENSION}, + {"MODULE.EXE", "rb", C_EXTENSION}, +#else + {".so", "rb", C_EXTENSION}, + {"module.so", "rb", C_EXTENSION}, +#endif +#endif +#endif + {0, 0} +}; + +static struct { + dev_t dev; +#ifdef __VMS + ino_t ino[3]; +#else + ino_t ino; +#endif + void *handle; +} handles[128]; +static int nhandles = 0; + + +dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname, + const char *pathname, FILE *fp) +{ + dl_funcptr p; + void *handle; + char funcname[258]; + char pathbuf[260]; + int dlopenflags=0; + + if (strchr(pathname, '/') == NULL) { + /* Prefix bare filename with "./" */ + PyOS_snprintf(pathbuf, sizeof(pathbuf), "./%-.255s", pathname); + pathname = pathbuf; + } + + PyOS_snprintf(funcname, sizeof(funcname), + LEAD_UNDERSCORE "init%.200s", shortname); + + if (fp != NULL) { + int i; + struct stat statb; + fstat(fileno(fp), &statb); + for (i = 0; i < nhandles; i++) { + if (statb.st_dev == handles[i].dev && + statb.st_ino == handles[i].ino) { + p = (dl_funcptr) dlsym(handles[i].handle, + funcname); + return p; + } + } + if (nhandles < 128) { + handles[nhandles].dev = statb.st_dev; +#ifdef __VMS + handles[nhandles].ino[0] = statb.st_ino[0]; + handles[nhandles].ino[1] = statb.st_ino[1]; + handles[nhandles].ino[2] = statb.st_ino[2]; +#else + handles[nhandles].ino = statb.st_ino; +#endif + } + } + +#if !(defined(PYOS_OS2) && defined(PYCC_GCC)) + dlopenflags = PyThreadState_Get()->interp->dlopenflags; +#endif + + if (Py_VerboseFlag) + printf("dlopen(\"%s\", %x);\n", pathname, dlopenflags); + +#ifdef __VMS + /* VMS currently don't allow a pathname, use a logical name instead */ + /* Concatenate 'python_module_' and shortname */ + /* so "import vms.bar" will use the logical python_module_bar */ + /* As C module use only one name space this is probably not a */ + /* important limitation */ + PyOS_snprintf(pathbuf, sizeof(pathbuf), "python_module_%-.200s", + shortname); + pathname = pathbuf; +#endif + + handle = dlopen(pathname, dlopenflags); + + if (handle == NULL) { + PyErr_SetString(PyExc_ImportError, dlerror()); + return NULL; + } + if (fp != NULL && nhandles < 128) + handles[nhandles++].handle = handle; + p = (dl_funcptr) dlsym(handle, funcname); + return p; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_stub.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_stub.c new file mode 100644 index 00000000..80f4abf8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_stub.c @@ -0,0 +1,11 @@ + +/* This module provides the necessary stubs for when dynamic loading is + not present. */ + +#include "Python.h" +#include "importdl.h" + + +const struct filedescr _PyImport_DynLoadFiletab[] = { + {0, 0} +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_win.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_win.c new file mode 100644 index 00000000..6ccac785 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/dynload_win.c @@ -0,0 +1,252 @@ + +/* Support for dynamic loading of extension modules */ + +#include "Python.h" + +#ifdef MS_XBOX +#include +#else +#include +#endif +#include +#include + +#include "importdl.h" + +const struct filedescr _PyImport_DynLoadFiletab[] = { +#ifdef _DEBUG + {"_d.pyd", "rb", C_EXTENSION}, + {"_d.dll", "rb", C_EXTENSION}, +#else + {".pyd", "rb", C_EXTENSION}, + {".dll", "rb", C_EXTENSION}, +#endif + {0, 0} +}; + + +/* Case insensitive string compare, to avoid any dependencies on particular + C RTL implementations */ + +static int strcasecmp (char *string1, char *string2) +{ + int first, second; + + do { + first = tolower(*string1); + second = tolower(*string2); + string1++; + string2++; + } while (first && first == second); + + return (first - second); +} + + +/* Function to return the name of the "python" DLL that the supplied module + directly imports. Looks through the list of imported modules and + returns the first entry that starts with "python" (case sensitive) and + is followed by nothing but numbers until the separator (period). + + Returns a pointer to the import name, or NULL if no matching name was + located. + + This function parses through the PE header for the module as loaded in + memory by the system loader. The PE header is accessed as documented by + Microsoft in the MSDN PE and COFF specification (2/99), and handles + both PE32 and PE32+. It only worries about the direct import table and + not the delay load import table since it's unlikely an extension is + going to be delay loading Python (after all, it's already loaded). + + If any magic values are not found (e.g., the PE header or optional + header magic), then this function simply returns NULL. */ + +#define DWORD_AT(mem) (*(DWORD *)(mem)) +#define WORD_AT(mem) (*(WORD *)(mem)) + +static char *GetPythonImport (HINSTANCE hModule) +{ + unsigned char *dllbase, *import_data, *import_name; + DWORD pe_offset, opt_offset; + WORD opt_magic; + int num_dict_off, import_off; + + /* Safety check input */ + if (hModule == NULL) { + return NULL; + } + + /* Module instance is also the base load address. First portion of + memory is the MS-DOS loader, which holds the offset to the PE + header (from the load base) at 0x3C */ + dllbase = (unsigned char *)hModule; + pe_offset = DWORD_AT(dllbase + 0x3C); + + /* The PE signature must be "PE\0\0" */ + if (memcmp(dllbase+pe_offset,"PE\0\0",4)) { + return NULL; + } + + /* Following the PE signature is the standard COFF header (20 + bytes) and then the optional header. The optional header starts + with a magic value of 0x10B for PE32 or 0x20B for PE32+ (PE32+ + uses 64-bits for some fields). It might also be 0x107 for a ROM + image, but we don't process that here. + + The optional header ends with a data dictionary that directly + points to certain types of data, among them the import entries + (in the second table entry). Based on the header type, we + determine offsets for the data dictionary count and the entry + within the dictionary pointing to the imports. */ + + opt_offset = pe_offset + 4 + 20; + opt_magic = WORD_AT(dllbase+opt_offset); + if (opt_magic == 0x10B) { + /* PE32 */ + num_dict_off = 92; + import_off = 104; + } else if (opt_magic == 0x20B) { + /* PE32+ */ + num_dict_off = 108; + import_off = 120; + } else { + /* Unsupported */ + return NULL; + } + + /* Now if an import table exists, offset to it and walk the list of + imports. The import table is an array (ending when an entry has + empty values) of structures (20 bytes each), which contains (at + offset 12) a relative address (to the module base) at which a + string constant holding the import name is located. */ + + if (DWORD_AT(dllbase + opt_offset + num_dict_off) >= 2) { + import_data = dllbase + DWORD_AT(dllbase + + opt_offset + + import_off); + while (DWORD_AT(import_data)) { + import_name = dllbase + DWORD_AT(import_data+12); + if (strlen(import_name) >= 6 && + !strncmp(import_name,"python",6)) { + char *pch; + + /* Ensure python prefix is followed only + by numbers to the end of the basename */ + pch = import_name + 6; + while (*pch && *pch != '.') { + if (*pch >= '0' && *pch <= '9') { + pch++; + } else { + pch = NULL; + break; + } + } + + if (pch) { + /* Found it - return the name */ + return import_name; + } + } + import_data += 20; + } + } + + return NULL; +} + + +dl_funcptr _PyImport_GetDynLoadFunc(const char *fqname, const char *shortname, + const char *pathname, FILE *fp) +{ +#ifdef MS_XBOX + return NULL; +#else + dl_funcptr p; + char funcname[258], *import_python; + + PyOS_snprintf(funcname, sizeof(funcname), "init%.200s", shortname); + + { + HINSTANCE hDLL = NULL; + char pathbuf[260]; + LPTSTR dummy; + /* We use LoadLibraryEx so Windows looks for dependent DLLs + in directory of pathname first. However, Windows95 + can sometimes not work correctly unless the absolute + path is used. If GetFullPathName() fails, the LoadLibrary + will certainly fail too, so use its error code */ + if (GetFullPathName(pathname, + sizeof(pathbuf), + pathbuf, + &dummy)) + /* XXX This call doesn't exist in Windows CE */ + hDLL = LoadLibraryEx(pathname, NULL, + LOAD_WITH_ALTERED_SEARCH_PATH); + if (hDLL==NULL){ + char errBuf[256]; + unsigned int errorCode; + + /* Get an error string from Win32 error code */ + char theInfo[256]; /* Pointer to error text + from system */ + int theLength; /* Length of error text */ + + errorCode = GetLastError(); + + theLength = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM, /* flags */ + NULL, /* message source */ + errorCode, /* the message (error) ID */ + 0, /* default language environment */ + (LPTSTR) theInfo, /* the buffer */ + sizeof(theInfo), /* the buffer size */ + NULL); /* no additional format args. */ + + /* Problem: could not get the error message. + This should not happen if called correctly. */ + if (theLength == 0) { + PyOS_snprintf(errBuf, sizeof(errBuf), + "DLL load failed with error code %d", + errorCode); + } else { + size_t len; + /* For some reason a \r\n + is appended to the text */ + if (theLength >= 2 && + theInfo[theLength-2] == '\r' && + theInfo[theLength-1] == '\n') { + theLength -= 2; + theInfo[theLength] = '\0'; + } + strcpy(errBuf, "DLL load failed: "); + len = strlen(errBuf); + strncpy(errBuf+len, theInfo, + sizeof(errBuf)-len); + errBuf[sizeof(errBuf)-1] = '\0'; + } + PyErr_SetString(PyExc_ImportError, errBuf); + return NULL; + } else { + char buffer[256]; + + PyOS_snprintf(buffer, sizeof(buffer), "python%d%d.dll", + PY_MAJOR_VERSION,PY_MINOR_VERSION); + import_python = GetPythonImport(hDLL); + + if (import_python && + strcasecmp(buffer,import_python)) { + PyOS_snprintf(buffer, sizeof(buffer), + "Module use of %.150s conflicts " + "with this version of Python.", + import_python); + PyErr_SetString(PyExc_ImportError,buffer); + FreeLibrary(hDLL); + return NULL; + } + } + p = GetProcAddress(hDLL, funcname); + } + + return p; +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/errors.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/errors.c new file mode 100644 index 00000000..5911fa78 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/errors.c @@ -0,0 +1,785 @@ + +/* Error handling */ + +#include "Python.h" + +#ifdef macintosh +extern char *PyMac_StrError(int); +#undef strerror +#define strerror PyMac_StrError +#endif /* macintosh */ + +#ifndef __STDC__ +#ifndef MS_WINDOWS +extern char *strerror(int); +#endif +#endif + +#ifdef MS_WINDOWS +#ifdef MS_XBOX +#include +#else +#include +#endif +#include "winbase.h" +#endif + +#include + +void +PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback) +{ + PyThreadState *tstate = PyThreadState_GET(); + PyObject *oldtype, *oldvalue, *oldtraceback; + + if (traceback != NULL && !PyTraceBack_Check(traceback)) { + /* XXX Should never happen -- fatal error instead? */ + Py_DECREF(traceback); + traceback = NULL; + } + + /* Save these in locals to safeguard against recursive + invocation through Py_XDECREF */ + oldtype = tstate->curexc_type; + oldvalue = tstate->curexc_value; + oldtraceback = tstate->curexc_traceback; + + tstate->curexc_type = type; + tstate->curexc_value = value; + tstate->curexc_traceback = traceback; + + Py_XDECREF(oldtype); + Py_XDECREF(oldvalue); + Py_XDECREF(oldtraceback); +} + +void +PyErr_SetObject(PyObject *exception, PyObject *value) +{ + Py_XINCREF(exception); + Py_XINCREF(value); + PyErr_Restore(exception, value, (PyObject *)NULL); +} + +void +PyErr_SetNone(PyObject *exception) +{ + PyErr_SetObject(exception, (PyObject *)NULL); +} + +void +PyErr_SetString(PyObject *exception, const char *string) +{ + PyObject *value = PyString_FromString(string); + PyErr_SetObject(exception, value); + Py_XDECREF(value); +} + + +PyObject * +PyErr_Occurred(void) +{ + PyThreadState *tstate = PyThreadState_GET(); + + return tstate->curexc_type; +} + + +int +PyErr_GivenExceptionMatches(PyObject *err, PyObject *exc) +{ + if (err == NULL || exc == NULL) { + /* maybe caused by "import exceptions" that failed early on */ + return 0; + } + if (PyTuple_Check(exc)) { + int i, n; + n = PyTuple_Size(exc); + for (i = 0; i < n; i++) { + /* Test recursively */ + if (PyErr_GivenExceptionMatches( + err, PyTuple_GET_ITEM(exc, i))) + { + return 1; + } + } + return 0; + } + /* err might be an instance, so check its class. */ + if (PyInstance_Check(err)) + err = (PyObject*)((PyInstanceObject*)err)->in_class; + + if (PyClass_Check(err) && PyClass_Check(exc)) + return PyClass_IsSubclass(err, exc); + + return err == exc; +} + + +int +PyErr_ExceptionMatches(PyObject *exc) +{ + return PyErr_GivenExceptionMatches(PyErr_Occurred(), exc); +} + + +/* Used in many places to normalize a raised exception, including in + eval_code2(), do_raise(), and PyErr_Print() +*/ +void +PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) +{ + PyObject *type = *exc; + PyObject *value = *val; + PyObject *inclass = NULL; + PyObject *initial_tb = NULL; + + if (type == NULL) { + /* There was no exception, so nothing to do. */ + return; + } + + /* If PyErr_SetNone() was used, the value will have been actually + set to NULL. + */ + if (!value) { + value = Py_None; + Py_INCREF(value); + } + + if (PyInstance_Check(value)) + inclass = (PyObject*)((PyInstanceObject*)value)->in_class; + + /* Normalize the exception so that if the type is a class, the + value will be an instance. + */ + if (PyClass_Check(type)) { + /* if the value was not an instance, or is not an instance + whose class is (or is derived from) type, then use the + value as an argument to instantiation of the type + class. + */ + if (!inclass || !PyClass_IsSubclass(inclass, type)) { + PyObject *args, *res; + + if (value == Py_None) + args = Py_BuildValue("()"); + else if (PyTuple_Check(value)) { + Py_INCREF(value); + args = value; + } + else + args = Py_BuildValue("(O)", value); + + if (args == NULL) + goto finally; + res = PyEval_CallObject(type, args); + Py_DECREF(args); + if (res == NULL) + goto finally; + Py_DECREF(value); + value = res; + } + /* if the class of the instance doesn't exactly match the + class of the type, believe the instance + */ + else if (inclass != type) { + Py_DECREF(type); + type = inclass; + Py_INCREF(type); + } + } + *exc = type; + *val = value; + return; +finally: + Py_DECREF(type); + Py_DECREF(value); + /* If the new exception doesn't set a traceback and the old + exception had a traceback, use the old traceback for the + new exception. It's better than nothing. + */ + initial_tb = *tb; + PyErr_Fetch(exc, val, tb); + if (initial_tb != NULL) { + if (*tb == NULL) + *tb = initial_tb; + else + Py_DECREF(initial_tb); + } + /* normalize recursively */ + PyErr_NormalizeException(exc, val, tb); +} + + +void +PyErr_Fetch(PyObject **p_type, PyObject **p_value, PyObject **p_traceback) +{ + PyThreadState *tstate = PyThreadState_Get(); + + *p_type = tstate->curexc_type; + *p_value = tstate->curexc_value; + *p_traceback = tstate->curexc_traceback; + + tstate->curexc_type = NULL; + tstate->curexc_value = NULL; + tstate->curexc_traceback = NULL; +} + +void +PyErr_Clear(void) +{ + PyErr_Restore(NULL, NULL, NULL); +} + +/* Convenience functions to set a type error exception and return 0 */ + +int +PyErr_BadArgument(void) +{ + PyErr_SetString(PyExc_TypeError, + "bad argument type for built-in operation"); + return 0; +} + +PyObject * +PyErr_NoMemory(void) +{ + if (PyErr_ExceptionMatches(PyExc_MemoryError)) + /* already current */ + return NULL; + + /* raise the pre-allocated instance if it still exists */ + if (PyExc_MemoryErrorInst) + PyErr_SetObject(PyExc_MemoryError, PyExc_MemoryErrorInst); + else + /* this will probably fail since there's no memory and hee, + hee, we have to instantiate this class + */ + PyErr_SetNone(PyExc_MemoryError); + + return NULL; +} + +PyObject * +PyErr_SetFromErrnoWithFilenameObject(PyObject *exc, PyObject *filenameObject) +{ + PyObject *v; + char *s; + int i = errno; +#ifdef PLAN9 + char errbuf[ERRMAX]; +#endif +#ifdef MS_WINDOWS + char *s_buf = NULL; + char s_small_buf[28]; /* Room for "Windows Error 0xFFFFFFFF" */ +#endif +#ifdef EINTR + if (i == EINTR && PyErr_CheckSignals()) + return NULL; +#endif +#ifdef PLAN9 + rerrstr(errbuf, sizeof errbuf); + s = errbuf; +#else + if (i == 0) + s = "Error"; /* Sometimes errno didn't get set */ + else +#if defined(MS_XBOX) || !defined(MS_WINDOWS) + s = strerror(i); +#else + { + /* Note that the Win32 errors do not lineup with the + errno error. So if the error is in the MSVC error + table, we use it, otherwise we assume it really _is_ + a Win32 error code + */ + if (i > 0 && i < _sys_nerr) { + s = _sys_errlist[i]; + } + else { + int len = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, /* no message source */ + i, + MAKELANGID(LANG_NEUTRAL, + SUBLANG_DEFAULT), + /* Default language */ + (LPTSTR) &s_buf, + 0, /* size not used */ + NULL); /* no args */ + if (len==0) { + /* Only ever seen this in out-of-mem + situations */ + sprintf(s_small_buf, "Windows Error 0x%X", i); + s = s_small_buf; + s_buf = NULL; + } else { + s = s_buf; + /* remove trailing cr/lf and dots */ + while (len > 0 && (s[len-1] <= ' ' || s[len-1] == '.')) + s[--len] = '\0'; + } + } + } +#endif /* Unix/Windows */ +#endif /* PLAN 9*/ + if (filenameObject != NULL) + v = Py_BuildValue("(isO)", i, s, filenameObject); + else + v = Py_BuildValue("(is)", i, s); + if (v != NULL) { + PyErr_SetObject(exc, v); + Py_DECREF(v); + } +#ifdef MS_WINDOWS + LocalFree(s_buf); +#endif + return NULL; +} + + +PyObject * +PyErr_SetFromErrnoWithFilename(PyObject *exc, char *filename) +{ + PyObject *name = filename ? PyString_FromString(filename) : NULL; + PyObject *result = PyErr_SetFromErrnoWithFilenameObject(exc, name); + Py_XDECREF(name); + return result; +} + +#ifdef Py_WIN_WIDE_FILENAMES +PyObject * +PyErr_SetFromErrnoWithUnicodeFilename(PyObject *exc, Py_UNICODE *filename) +{ + PyObject *name = filename ? + PyUnicode_FromUnicode(filename, wcslen(filename)) : + NULL; + PyObject *result = PyErr_SetFromErrnoWithFilenameObject(exc, name); + Py_XDECREF(name); + return result; +} +#endif /* Py_WIN_WIDE_FILENAMES */ + +PyObject * +PyErr_SetFromErrno(PyObject *exc) +{ + return PyErr_SetFromErrnoWithFilenameObject(exc, NULL); +} + +#ifdef MS_WINDOWS +/* Windows specific error code handling */ +PyObject *PyErr_SetExcFromWindowsErrWithFilenameObject( + PyObject *exc, + int ierr, + PyObject *filenameObject) +{ + int len; + char *s; + char *s_buf = NULL; /* Free via LocalFree */ + char s_small_buf[28]; /* Room for "Windows Error 0xFFFFFFFF" */ + PyObject *v; + DWORD err = (DWORD)ierr; + if (err==0) err = GetLastError(); +#ifdef MS_XBOX + len = 0; +#else + len = FormatMessage( + /* Error API error */ + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, /* no message source */ + err, + MAKELANGID(LANG_NEUTRAL, + SUBLANG_DEFAULT), /* Default language */ + (LPTSTR) &s_buf, + 0, /* size not used */ + NULL); /* no args */ +#endif // !MS_XBOX + if (len==0) { + /* Only seen this in out of mem situations */ + sprintf(s_small_buf, "Windows Error 0x%X", err); + s = s_small_buf; + s_buf = NULL; + } else { + s = s_buf; + /* remove trailing cr/lf and dots */ + while (len > 0 && (s[len-1] <= ' ' || s[len-1] == '.')) + s[--len] = '\0'; + } + if (filenameObject != NULL) + v = Py_BuildValue("(isO)", err, s, filenameObject); + else + v = Py_BuildValue("(is)", err, s); + if (v != NULL) { + PyErr_SetObject(exc, v); + Py_DECREF(v); + } + LocalFree(s_buf); + return NULL; +} + +PyObject *PyErr_SetExcFromWindowsErrWithFilename( + PyObject *exc, + int ierr, + const char *filename) +{ + PyObject *name = filename ? PyString_FromString(filename) : NULL; + PyObject *ret = PyErr_SetExcFromWindowsErrWithFilenameObject(exc, + ierr, + name); + Py_XDECREF(name); + return ret; +} + +#ifdef Py_WIN_WIDE_FILENAMES +PyObject *PyErr_SetExcFromWindowsErrWithUnicodeFilename( + PyObject *exc, + int ierr, + const Py_UNICODE *filename) +{ + PyObject *name = filename ? + PyUnicode_FromUnicode(filename, wcslen(filename)) : + NULL; + PyObject *ret = PyErr_SetExcFromWindowsErrWithFilenameObject(exc, + ierr, + name); + Py_XDECREF(name); + return ret; +} +#endif /* Py_WIN_WIDE_FILENAMES */ + +PyObject *PyErr_SetExcFromWindowsErr(PyObject *exc, int ierr) +{ + return PyErr_SetExcFromWindowsErrWithFilename(exc, ierr, NULL); +} + +PyObject *PyErr_SetFromWindowsErr(int ierr) +{ + return PyErr_SetExcFromWindowsErrWithFilename(PyExc_WindowsError, + ierr, NULL); +} +PyObject *PyErr_SetFromWindowsErrWithFilename( + int ierr, + const char *filename) +{ + PyObject *name = filename ? PyString_FromString(filename) : NULL; + PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObject( + PyExc_WindowsError, + ierr, name); + Py_XDECREF(name); + return result; +} + +#ifdef Py_WIN_WIDE_FILENAMES +PyObject *PyErr_SetFromWindowsErrWithUnicodeFilename( + int ierr, + const Py_UNICODE *filename) +{ + PyObject *name = filename ? + PyUnicode_FromUnicode(filename, wcslen(filename)) : + NULL; + PyObject *result = PyErr_SetExcFromWindowsErrWithFilenameObject( + PyExc_WindowsError, + ierr, name); + Py_XDECREF(name); + return result; +} +#endif /* Py_WIN_WIDE_FILENAMES */ +#endif /* MS_WINDOWS */ + +void +_PyErr_BadInternalCall(char *filename, int lineno) +{ + PyErr_Format(PyExc_SystemError, + "%s:%d: bad argument to internal function", + filename, lineno); +} + +/* Remove the preprocessor macro for PyErr_BadInternalCall() so that we can + export the entry point for existing object code: */ +#undef PyErr_BadInternalCall +void +PyErr_BadInternalCall(void) +{ + PyErr_Format(PyExc_SystemError, + "bad argument to internal function"); +} +#define PyErr_BadInternalCall() _PyErr_BadInternalCall(__FILE__, __LINE__) + + + +PyObject * +PyErr_Format(PyObject *exception, const char *format, ...) +{ + va_list vargs; + PyObject* string; + +#ifdef HAVE_STDARG_PROTOTYPES + va_start(vargs, format); +#else + va_start(vargs); +#endif + + string = PyString_FromFormatV(format, vargs); + PyErr_SetObject(exception, string); + Py_XDECREF(string); + va_end(vargs); + return NULL; +} + + +PyObject * +PyErr_NewException(char *name, PyObject *base, PyObject *dict) +{ + char *dot; + PyObject *modulename = NULL; + PyObject *classname = NULL; + PyObject *mydict = NULL; + PyObject *bases = NULL; + PyObject *result = NULL; + dot = strrchr(name, '.'); + if (dot == NULL) { + PyErr_SetString(PyExc_SystemError, + "PyErr_NewException: name must be module.class"); + return NULL; + } + if (base == NULL) + base = PyExc_Exception; + if (!PyClass_Check(base)) { + /* Must be using string-based standard exceptions (-X) */ + return PyString_FromString(name); + } + if (dict == NULL) { + dict = mydict = PyDict_New(); + if (dict == NULL) + goto failure; + } + if (PyDict_GetItemString(dict, "__module__") == NULL) { + modulename = PyString_FromStringAndSize(name, (int)(dot-name)); + if (modulename == NULL) + goto failure; + if (PyDict_SetItemString(dict, "__module__", modulename) != 0) + goto failure; + } + classname = PyString_FromString(dot+1); + if (classname == NULL) + goto failure; + bases = Py_BuildValue("(O)", base); + if (bases == NULL) + goto failure; + result = PyClass_New(bases, dict, classname); + failure: + Py_XDECREF(bases); + Py_XDECREF(mydict); + Py_XDECREF(classname); + Py_XDECREF(modulename); + return result; +} + +/* Call when an exception has occurred but there is no way for Python + to handle it. Examples: exception in __del__ or during GC. */ +void +PyErr_WriteUnraisable(PyObject *obj) +{ + PyObject *f, *t, *v, *tb; + PyErr_Fetch(&t, &v, &tb); + f = PySys_GetObject("stderr"); + if (f != NULL) { + PyFile_WriteString("Exception ", f); + if (t) { + PyFile_WriteObject(t, f, Py_PRINT_RAW); + if (v && v != Py_None) { + PyFile_WriteString(": ", f); + PyFile_WriteObject(v, f, 0); + } + } + PyFile_WriteString(" in ", f); + PyFile_WriteObject(obj, f, 0); + PyFile_WriteString(" ignored\n", f); + PyErr_Clear(); /* Just in case */ + } + Py_XDECREF(t); + Py_XDECREF(v); + Py_XDECREF(tb); +} + +extern PyObject *PyModule_GetWarningsModule(); + +/* Function to issue a warning message; may raise an exception. */ +int +PyErr_Warn(PyObject *category, char *message) +{ + PyObject *dict, *func = NULL; + PyObject *warnings_module = PyModule_GetWarningsModule(); + + if (warnings_module != NULL) { + dict = PyModule_GetDict(warnings_module); + func = PyDict_GetItemString(dict, "warn"); + } + if (func == NULL) { + PySys_WriteStderr("warning: %s\n", message); + return 0; + } + else { + PyObject *args, *res; + + if (category == NULL) + category = PyExc_RuntimeWarning; + args = Py_BuildValue("(sO)", message, category); + if (args == NULL) + return -1; + res = PyEval_CallObject(func, args); + Py_DECREF(args); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; + } +} + + +/* Warning with explicit origin */ +int +PyErr_WarnExplicit(PyObject *category, const char *message, + const char *filename, int lineno, + const char *module, PyObject *registry) +{ + PyObject *mod, *dict, *func = NULL; + + mod = PyImport_ImportModule("warnings"); + if (mod != NULL) { + dict = PyModule_GetDict(mod); + func = PyDict_GetItemString(dict, "warn_explicit"); + Py_DECREF(mod); + } + if (func == NULL) { + PySys_WriteStderr("warning: %s\n", message); + return 0; + } + else { + PyObject *args, *res; + + if (category == NULL) + category = PyExc_RuntimeWarning; + if (registry == NULL) + registry = Py_None; + args = Py_BuildValue("(sOsizO)", message, category, + filename, lineno, module, registry); + if (args == NULL) + return -1; + res = PyEval_CallObject(func, args); + Py_DECREF(args); + if (res == NULL) + return -1; + Py_DECREF(res); + return 0; + } +} + + +/* Set file and line information for the current exception. + If the exception is not a SyntaxError, also sets additional attributes + to make printing of exceptions believe it is a syntax error. */ + +void +PyErr_SyntaxLocation(const char *filename, int lineno) +{ + PyObject *exc, *v, *tb, *tmp; + + /* add attributes for the line number and filename for the error */ + PyErr_Fetch(&exc, &v, &tb); + PyErr_NormalizeException(&exc, &v, &tb); + /* XXX check that it is, indeed, a syntax error */ + tmp = PyInt_FromLong(lineno); + if (tmp == NULL) + PyErr_Clear(); + else { + if (PyObject_SetAttrString(v, "lineno", tmp)) + PyErr_Clear(); + Py_DECREF(tmp); + } + if (filename != NULL) { + tmp = PyString_FromString(filename); + if (tmp == NULL) + PyErr_Clear(); + else { + if (PyObject_SetAttrString(v, "filename", tmp)) + PyErr_Clear(); + Py_DECREF(tmp); + } + + tmp = PyErr_ProgramText(filename, lineno); + if (tmp) { + PyObject_SetAttrString(v, "text", tmp); + Py_DECREF(tmp); + } + } + if (PyObject_SetAttrString(v, "offset", Py_None)) { + PyErr_Clear(); + } + if (exc != PyExc_SyntaxError) { + if (!PyObject_HasAttrString(v, "msg")) { + tmp = PyObject_Str(v); + if (tmp) { + if (PyObject_SetAttrString(v, "msg", tmp)) + PyErr_Clear(); + Py_DECREF(tmp); + } else { + PyErr_Clear(); + } + } + if (!PyObject_HasAttrString(v, "print_file_and_line")) { + if (PyObject_SetAttrString(v, "print_file_and_line", + Py_None)) + PyErr_Clear(); + } + } + PyErr_Restore(exc, v, tb); +} + +/* com_fetch_program_text will attempt to load the line of text that + the exception refers to. If it fails, it will return NULL but will + not set an exception. + + XXX The functionality of this function is quite similar to the + functionality in tb_displayline() in traceback.c. +*/ + +PyObject * +PyErr_ProgramText(const char *filename, int lineno) +{ + FILE *fp; + int i; + char linebuf[1000]; + + if (filename == NULL || lineno <= 0) + return NULL; + fp = fopen(filename, "r" PY_STDIOTEXTMODE); + if (fp == NULL) + return NULL; + for (i = 0; i < lineno; i++) { + char *pLastChar = &linebuf[sizeof(linebuf) - 2]; + do { + *pLastChar = '\0'; + if (Py_UniversalNewlineFgets(linebuf, sizeof linebuf, fp, NULL) == NULL) + break; + /* fgets read *something*; if it didn't get as + far as pLastChar, it must have found a newline + or hit the end of the file; if pLastChar is \n, + it obviously found a newline; else we haven't + yet seen a newline, so must continue */ + } while (*pLastChar != '\0' && *pLastChar != '\n'); + } + fclose(fp); + if (i == lineno) { + char *p = linebuf; + while (*p == ' ' || *p == '\t' || *p == '\014') + p++; + return PyString_FromString(p); + } + return NULL; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/exceptions.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/exceptions.c new file mode 100644 index 00000000..9a6bbba7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/exceptions.c @@ -0,0 +1,1857 @@ +/* This module provides the suite of standard class-based exceptions for + * Python's builtin module. This is a complete C implementation of what, + * in Python 1.5.2, was contained in the exceptions.py module. The problem + * there was that if exceptions.py could not be imported for some reason, + * the entire interpreter would abort. + * + * By moving the exceptions into C and statically linking, we can guarantee + * that the standard exceptions will always be available. + * + * history: + * 98-08-19 fl created (for pyexe) + * 00-02-08 fl updated for 1.5.2 + * 26-May-2000 baw vetted for Python 1.6 + * + * written by Fredrik Lundh + * modifications, additions, cleanups, and proofreading by Barry Warsaw + * + * Copyright (c) 1998-2000 by Secret Labs AB. All rights reserved. + */ + +#include "Python.h" +#include "osdefs.h" + +/* Caution: MS Visual C++ 6 errors if a single string literal exceeds + * 2Kb. So the module docstring has been broken roughly in half, using + * compile-time literal concatenation. + */ + +/* NOTE: If the exception class hierarchy changes, don't forget to update + * Doc/lib/libexcs.tex! + */ + +PyDoc_STRVAR(module__doc__, +"Python's standard exception class hierarchy.\n\ +\n\ +Before Python 1.5, the standard exceptions were all simple string objects.\n\ +In Python 1.5, the standard exceptions were converted to classes organized\n\ +into a relatively flat hierarchy. String-based standard exceptions were\n\ +optional, or used as a fallback if some problem occurred while importing\n\ +the exception module. With Python 1.6, optional string-based standard\n\ +exceptions were removed (along with the -X command line flag).\n\ +\n\ +The class exceptions were implemented in such a way as to be almost\n\ +completely backward compatible. Some tricky uses of IOError could\n\ +potentially have broken, but by Python 1.6, all of these should have\n\ +been fixed. As of Python 1.6, the class-based standard exceptions are\n\ +now implemented in C, and are guaranteed to exist in the Python\n\ +interpreter.\n\ +\n\ +Here is a rundown of the class hierarchy. The classes found here are\n\ +inserted into both the exceptions module and the `built-in' module. It is\n\ +recommended that user defined class based exceptions be derived from the\n\ +`Exception' class, although this is currently not enforced.\n" + /* keep string pieces "small" */ +"\n\ +Exception\n\ + |\n\ + +-- SystemExit\n\ + +-- StopIteration\n\ + +-- StandardError\n\ + | |\n\ + | +-- KeyboardInterrupt\n\ + | +-- ImportError\n\ + | +-- EnvironmentError\n\ + | | |\n\ + | | +-- IOError\n\ + | | +-- OSError\n\ + | | |\n\ + | | +-- WindowsError\n\ + | | +-- VMSError\n\ + | |\n\ + | +-- EOFError\n\ + | +-- RuntimeError\n\ + | | |\n\ + | | +-- NotImplementedError\n\ + | |\n\ + | +-- NameError\n\ + | | |\n\ + | | +-- UnboundLocalError\n\ + | |\n\ + | +-- AttributeError\n\ + | +-- SyntaxError\n\ + | | |\n\ + | | +-- IndentationError\n\ + | | |\n\ + | | +-- TabError\n\ + | |\n\ + | +-- TypeError\n\ + | +-- AssertionError\n\ + | +-- LookupError\n\ + | | |\n\ + | | +-- IndexError\n\ + | | +-- KeyError\n\ + | |\n\ + | +-- ArithmeticError\n\ + | | |\n\ + | | +-- OverflowError\n\ + | | +-- ZeroDivisionError\n\ + | | +-- FloatingPointError\n\ + | |\n\ + | +-- ValueError\n\ + | | |\n\ + | | +-- UnicodeError\n\ + | | |\n\ + | | +-- UnicodeEncodeError\n\ + | | +-- UnicodeDecodeError\n\ + | | +-- UnicodeTranslateError\n\ + | |\n\ + | +-- ReferenceError\n\ + | +-- SystemError\n\ + | +-- MemoryError\n\ + |\n\ + +---Warning\n\ + |\n\ + +-- UserWarning\n\ + +-- DeprecationWarning\n\ + +-- PendingDeprecationWarning\n\ + +-- SyntaxWarning\n\ + +-- OverflowWarning\n\ + +-- RuntimeWarning\n\ + +-- FutureWarning" +); + + +/* Helper function for populating a dictionary with method wrappers. */ +static int +populate_methods(PyObject *klass, PyObject *dict, PyMethodDef *methods) +{ + PyObject *module; + int status = -1; + + if (!methods) + return 0; + + module = PyString_FromString("exceptions"); + if (!module) + return 0; + while (methods->ml_name) { + /* get a wrapper for the built-in function */ + PyObject *func = PyCFunction_NewEx(methods, NULL, module); + PyObject *meth; + + if (!func) + goto status; + + /* turn the function into an unbound method */ + if (!(meth = PyMethod_New(func, NULL, klass))) { + Py_DECREF(func); + goto status; + } + + /* add method to dictionary */ + status = PyDict_SetItemString(dict, methods->ml_name, meth); + Py_DECREF(meth); + Py_DECREF(func); + + /* stop now if an error occurred, otherwise do the next method */ + if (status) + goto status; + + methods++; + } + status = 0; + status: + Py_DECREF(module); + return status; +} + + + +/* This function is used to create all subsequent exception classes. */ +static int +make_class(PyObject **klass, PyObject *base, + char *name, PyMethodDef *methods, + char *docstr) +{ + PyObject *dict = PyDict_New(); + PyObject *str = NULL; + int status = -1; + + if (!dict) + return -1; + + /* If an error occurs from here on, goto finally instead of explicitly + * returning NULL. + */ + + if (docstr) { + if (!(str = PyString_FromString(docstr))) + goto finally; + if (PyDict_SetItemString(dict, "__doc__", str)) + goto finally; + } + + if (!(*klass = PyErr_NewException(name, base, dict))) + goto finally; + + if (populate_methods(*klass, dict, methods)) { + Py_DECREF(*klass); + *klass = NULL; + goto finally; + } + + status = 0; + + finally: + Py_XDECREF(dict); + Py_XDECREF(str); + return status; +} + + +/* Use this for *args signatures, otherwise just use PyArg_ParseTuple() */ +static PyObject * +get_self(PyObject *args) +{ + PyObject *self = PyTuple_GetItem(args, 0); + if (!self) { + /* Watch out for being called to early in the bootstrapping process */ + if (PyExc_TypeError) { + PyErr_SetString(PyExc_TypeError, + "unbound method must be called with instance as first argument"); + } + return NULL; + } + return self; +} + + + +/* Notes on bootstrapping the exception classes. + * + * First thing we create is the base class for all exceptions, called + * appropriately enough: Exception. Creation of this class makes no + * assumptions about the existence of any other exception class -- except + * for TypeError, which can conditionally exist. + * + * Next, StandardError is created (which is quite simple) followed by + * TypeError, because the instantiation of other exceptions can potentially + * throw a TypeError. Once these exceptions are created, all the others + * can be created in any order. See the static exctable below for the + * explicit bootstrap order. + * + * All classes after Exception can be created using PyErr_NewException(). + */ + +PyDoc_STRVAR(Exception__doc__, "Common base class for all exceptions."); + + +static PyObject * +Exception__init__(PyObject *self, PyObject *args) +{ + int status; + + if (!(self = get_self(args))) + return NULL; + + /* set args attribute */ + /* XXX size is only a hint */ + args = PySequence_GetSlice(args, 1, PySequence_Size(args)); + if (!args) + return NULL; + status = PyObject_SetAttrString(self, "args", args); + Py_DECREF(args); + if (status < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject * +Exception__str__(PyObject *self, PyObject *args) +{ + PyObject *out; + + if (!PyArg_ParseTuple(args, "O:__str__", &self)) + return NULL; + + args = PyObject_GetAttrString(self, "args"); + if (!args) + return NULL; + + switch (PySequence_Size(args)) { + case 0: + out = PyString_FromString(""); + break; + case 1: + { + PyObject *tmp = PySequence_GetItem(args, 0); + if (tmp) { + out = PyObject_Str(tmp); + Py_DECREF(tmp); + } + else + out = NULL; + break; + } + case -1: + PyErr_Clear(); + /* Fall through */ + default: + out = PyObject_Str(args); + break; + } + + Py_DECREF(args); + return out; +} + + +static PyObject * +Exception__getitem__(PyObject *self, PyObject *args) +{ + PyObject *out; + PyObject *index; + + if (!PyArg_ParseTuple(args, "OO:__getitem__", &self, &index)) + return NULL; + + args = PyObject_GetAttrString(self, "args"); + if (!args) + return NULL; + + out = PyObject_GetItem(args, index); + Py_DECREF(args); + return out; +} + + +static PyMethodDef +Exception_methods[] = { + /* methods for the Exception class */ + { "__getitem__", Exception__getitem__, METH_VARARGS}, + { "__str__", Exception__str__, METH_VARARGS}, + { "__init__", Exception__init__, METH_VARARGS}, + { NULL, NULL } +}; + + +static int +make_Exception(char *modulename) +{ + PyObject *dict = PyDict_New(); + PyObject *str = NULL; + PyObject *name = NULL; + int status = -1; + + if (!dict) + return -1; + + /* If an error occurs from here on, goto finally instead of explicitly + * returning NULL. + */ + + if (!(str = PyString_FromString(modulename))) + goto finally; + if (PyDict_SetItemString(dict, "__module__", str)) + goto finally; + Py_DECREF(str); + if (!(str = PyString_FromString(Exception__doc__))) + goto finally; + if (PyDict_SetItemString(dict, "__doc__", str)) + goto finally; + + if (!(name = PyString_FromString("Exception"))) + goto finally; + + if (!(PyExc_Exception = PyClass_New(NULL, dict, name))) + goto finally; + + /* Now populate the dictionary with the method suite */ + if (populate_methods(PyExc_Exception, dict, Exception_methods)) + /* Don't need to reclaim PyExc_Exception here because that'll + * happen during interpreter shutdown. + */ + goto finally; + + status = 0; + + finally: + Py_XDECREF(dict); + Py_XDECREF(str); + Py_XDECREF(name); + return status; +} + + + +PyDoc_STRVAR(StandardError__doc__, +"Base class for all standard Python exceptions."); + +PyDoc_STRVAR(TypeError__doc__, "Inappropriate argument type."); + +PyDoc_STRVAR(StopIteration__doc__, "Signal the end from iterator.next()."); + + + +PyDoc_STRVAR(SystemExit__doc__, "Request to exit from the interpreter."); + + +static PyObject * +SystemExit__init__(PyObject *self, PyObject *args) +{ + PyObject *code; + int status; + + if (!(self = get_self(args))) + return NULL; + + /* Set args attribute. */ + if (!(args = PySequence_GetSlice(args, 1, PySequence_Size(args)))) + return NULL; + + status = PyObject_SetAttrString(self, "args", args); + if (status < 0) { + Py_DECREF(args); + return NULL; + } + + /* set code attribute */ + switch (PySequence_Size(args)) { + case 0: + Py_INCREF(Py_None); + code = Py_None; + break; + case 1: + code = PySequence_GetItem(args, 0); + break; + case -1: + PyErr_Clear(); + /* Fall through */ + default: + Py_INCREF(args); + code = args; + break; + } + + status = PyObject_SetAttrString(self, "code", code); + Py_DECREF(code); + Py_DECREF(args); + if (status < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + + +static PyMethodDef SystemExit_methods[] = { + { "__init__", SystemExit__init__, METH_VARARGS}, + {NULL, NULL} +}; + + + +PyDoc_STRVAR(KeyboardInterrupt__doc__, "Program interrupted by user."); + +PyDoc_STRVAR(ImportError__doc__, +"Import can't find module, or can't find name in module."); + + + +PyDoc_STRVAR(EnvironmentError__doc__, "Base class for I/O related errors."); + + +static PyObject * +EnvironmentError__init__(PyObject *self, PyObject *args) +{ + PyObject *item0 = NULL; + PyObject *item1 = NULL; + PyObject *item2 = NULL; + PyObject *subslice = NULL; + PyObject *rtnval = NULL; + + if (!(self = get_self(args))) + return NULL; + + if (!(args = PySequence_GetSlice(args, 1, PySequence_Size(args)))) + return NULL; + + if (PyObject_SetAttrString(self, "args", args) || + PyObject_SetAttrString(self, "errno", Py_None) || + PyObject_SetAttrString(self, "strerror", Py_None) || + PyObject_SetAttrString(self, "filename", Py_None)) + { + goto finally; + } + + switch (PySequence_Size(args)) { + case 3: + /* Where a function has a single filename, such as open() or some + * of the os module functions, PyErr_SetFromErrnoWithFilename() is + * called, giving a third argument which is the filename. But, so + * that old code using in-place unpacking doesn't break, e.g.: + * + * except IOError, (errno, strerror): + * + * we hack args so that it only contains two items. This also + * means we need our own __str__() which prints out the filename + * when it was supplied. + */ + item0 = PySequence_GetItem(args, 0); + item1 = PySequence_GetItem(args, 1); + item2 = PySequence_GetItem(args, 2); + if (!item0 || !item1 || !item2) + goto finally; + + if (PyObject_SetAttrString(self, "errno", item0) || + PyObject_SetAttrString(self, "strerror", item1) || + PyObject_SetAttrString(self, "filename", item2)) + { + goto finally; + } + + subslice = PySequence_GetSlice(args, 0, 2); + if (!subslice || PyObject_SetAttrString(self, "args", subslice)) + goto finally; + break; + + case 2: + /* Used when PyErr_SetFromErrno() is called and no filename + * argument is given. + */ + item0 = PySequence_GetItem(args, 0); + item1 = PySequence_GetItem(args, 1); + if (!item0 || !item1) + goto finally; + + if (PyObject_SetAttrString(self, "errno", item0) || + PyObject_SetAttrString(self, "strerror", item1)) + { + goto finally; + } + break; + + case -1: + PyErr_Clear(); + break; + } + + Py_INCREF(Py_None); + rtnval = Py_None; + + finally: + Py_DECREF(args); + Py_XDECREF(item0); + Py_XDECREF(item1); + Py_XDECREF(item2); + Py_XDECREF(subslice); + return rtnval; +} + + +static PyObject * +EnvironmentError__str__(PyObject *self, PyObject *args) +{ + PyObject *originalself = self; + PyObject *filename; + PyObject *serrno; + PyObject *strerror; + PyObject *rtnval = NULL; + + if (!PyArg_ParseTuple(args, "O:__str__", &self)) + return NULL; + + filename = PyObject_GetAttrString(self, "filename"); + serrno = PyObject_GetAttrString(self, "errno"); + strerror = PyObject_GetAttrString(self, "strerror"); + if (!filename || !serrno || !strerror) + goto finally; + + if (filename != Py_None) { + PyObject *fmt = PyString_FromString("[Errno %s] %s: %s"); + PyObject *repr = PyObject_Repr(filename); + PyObject *tuple = PyTuple_New(3); + + if (!fmt || !repr || !tuple) { + Py_XDECREF(fmt); + Py_XDECREF(repr); + Py_XDECREF(tuple); + goto finally; + } + + PyTuple_SET_ITEM(tuple, 0, serrno); + PyTuple_SET_ITEM(tuple, 1, strerror); + PyTuple_SET_ITEM(tuple, 2, repr); + + rtnval = PyString_Format(fmt, tuple); + + Py_DECREF(fmt); + Py_DECREF(tuple); + /* already freed because tuple owned only reference */ + serrno = NULL; + strerror = NULL; + } + else if (PyObject_IsTrue(serrno) && PyObject_IsTrue(strerror)) { + PyObject *fmt = PyString_FromString("[Errno %s] %s"); + PyObject *tuple = PyTuple_New(2); + + if (!fmt || !tuple) { + Py_XDECREF(fmt); + Py_XDECREF(tuple); + goto finally; + } + + PyTuple_SET_ITEM(tuple, 0, serrno); + PyTuple_SET_ITEM(tuple, 1, strerror); + + rtnval = PyString_Format(fmt, tuple); + + Py_DECREF(fmt); + Py_DECREF(tuple); + /* already freed because tuple owned only reference */ + serrno = NULL; + strerror = NULL; + } + else + /* The original Python code said: + * + * return StandardError.__str__(self) + * + * but there is no StandardError__str__() function; we happen to + * know that's just a pass through to Exception__str__(). + */ + rtnval = Exception__str__(originalself, args); + + finally: + Py_XDECREF(filename); + Py_XDECREF(serrno); + Py_XDECREF(strerror); + return rtnval; +} + + +static +PyMethodDef EnvironmentError_methods[] = { + {"__init__", EnvironmentError__init__, METH_VARARGS}, + {"__str__", EnvironmentError__str__, METH_VARARGS}, + {NULL, NULL} +}; + + + + +PyDoc_STRVAR(IOError__doc__, "I/O operation failed."); + +PyDoc_STRVAR(OSError__doc__, "OS system call failed."); + +#ifdef MS_WINDOWS +PyDoc_STRVAR(WindowsError__doc__, "MS-Windows OS system call failed."); +#endif /* MS_WINDOWS */ + +#ifdef __VMS +static char +VMSError__doc__[] = "OpenVMS OS system call failed."; +#endif + +PyDoc_STRVAR(EOFError__doc__, "Read beyond end of file."); + +PyDoc_STRVAR(RuntimeError__doc__, "Unspecified run-time error."); + +PyDoc_STRVAR(NotImplementedError__doc__, +"Method or function hasn't been implemented yet."); + +PyDoc_STRVAR(NameError__doc__, "Name not found globally."); + +PyDoc_STRVAR(UnboundLocalError__doc__, +"Local name referenced but not bound to a value."); + +PyDoc_STRVAR(AttributeError__doc__, "Attribute not found."); + + + +PyDoc_STRVAR(SyntaxError__doc__, "Invalid syntax."); + + +static int +SyntaxError__classinit__(PyObject *klass) +{ + int retval = 0; + PyObject *emptystring = PyString_FromString(""); + + /* Additional class-creation time initializations */ + if (!emptystring || + PyObject_SetAttrString(klass, "msg", emptystring) || + PyObject_SetAttrString(klass, "filename", Py_None) || + PyObject_SetAttrString(klass, "lineno", Py_None) || + PyObject_SetAttrString(klass, "offset", Py_None) || + PyObject_SetAttrString(klass, "text", Py_None) || + PyObject_SetAttrString(klass, "print_file_and_line", Py_None)) + { + retval = -1; + } + Py_XDECREF(emptystring); + return retval; +} + + +static PyObject * +SyntaxError__init__(PyObject *self, PyObject *args) +{ + PyObject *rtnval = NULL; + int lenargs; + + if (!(self = get_self(args))) + return NULL; + + if (!(args = PySequence_GetSlice(args, 1, PySequence_Size(args)))) + return NULL; + + if (PyObject_SetAttrString(self, "args", args)) + goto finally; + + lenargs = PySequence_Size(args); + if (lenargs >= 1) { + PyObject *item0 = PySequence_GetItem(args, 0); + int status; + + if (!item0) + goto finally; + status = PyObject_SetAttrString(self, "msg", item0); + Py_DECREF(item0); + if (status) + goto finally; + } + if (lenargs == 2) { + PyObject *info = PySequence_GetItem(args, 1); + PyObject *filename = NULL, *lineno = NULL; + PyObject *offset = NULL, *text = NULL; + int status = 1; + + if (!info) + goto finally; + + filename = PySequence_GetItem(info, 0); + if (filename != NULL) { + lineno = PySequence_GetItem(info, 1); + if (lineno != NULL) { + offset = PySequence_GetItem(info, 2); + if (offset != NULL) { + text = PySequence_GetItem(info, 3); + if (text != NULL) { + status = + PyObject_SetAttrString(self, "filename", filename) + || PyObject_SetAttrString(self, "lineno", lineno) + || PyObject_SetAttrString(self, "offset", offset) + || PyObject_SetAttrString(self, "text", text); + Py_DECREF(text); + } + Py_DECREF(offset); + } + Py_DECREF(lineno); + } + Py_DECREF(filename); + } + Py_DECREF(info); + + if (status) + goto finally; + } + Py_INCREF(Py_None); + rtnval = Py_None; + + finally: + Py_DECREF(args); + return rtnval; +} + + +/* This is called "my_basename" instead of just "basename" to avoid name + conflicts with glibc; basename is already prototyped if _GNU_SOURCE is + defined, and Python does define that. */ +static char * +my_basename(char *name) +{ + char *cp = name; + char *result = name; + + if (name == NULL) + return "???"; + while (*cp != '\0') { + if (*cp == SEP) + result = cp + 1; + ++cp; + } + return result; +} + + +static PyObject * +SyntaxError__str__(PyObject *self, PyObject *args) +{ + PyObject *msg; + PyObject *str; + PyObject *filename, *lineno, *result; + + if (!PyArg_ParseTuple(args, "O:__str__", &self)) + return NULL; + + if (!(msg = PyObject_GetAttrString(self, "msg"))) + return NULL; + + str = PyObject_Str(msg); + Py_DECREF(msg); + result = str; + + /* XXX -- do all the additional formatting with filename and + lineno here */ + + if (str != NULL && PyString_Check(str)) { + int have_filename = 0; + int have_lineno = 0; + char *buffer = NULL; + + if ((filename = PyObject_GetAttrString(self, "filename")) != NULL) + have_filename = PyString_Check(filename); + else + PyErr_Clear(); + + if ((lineno = PyObject_GetAttrString(self, "lineno")) != NULL) + have_lineno = PyInt_Check(lineno); + else + PyErr_Clear(); + + if (have_filename || have_lineno) { + int bufsize = PyString_GET_SIZE(str) + 64; + if (have_filename) + bufsize += PyString_GET_SIZE(filename); + + buffer = PyMem_MALLOC(bufsize); + if (buffer != NULL) { + if (have_filename && have_lineno) + PyOS_snprintf(buffer, bufsize, "%s (%s, line %ld)", + PyString_AS_STRING(str), + my_basename(PyString_AS_STRING(filename)), + PyInt_AsLong(lineno)); + else if (have_filename) + PyOS_snprintf(buffer, bufsize, "%s (%s)", + PyString_AS_STRING(str), + my_basename(PyString_AS_STRING(filename))); + else if (have_lineno) + PyOS_snprintf(buffer, bufsize, "%s (line %ld)", + PyString_AS_STRING(str), + PyInt_AsLong(lineno)); + + result = PyString_FromString(buffer); + PyMem_FREE(buffer); + + if (result == NULL) + result = str; + else + Py_DECREF(str); + } + } + Py_XDECREF(filename); + Py_XDECREF(lineno); + } + return result; +} + + +static PyMethodDef SyntaxError_methods[] = { + {"__init__", SyntaxError__init__, METH_VARARGS}, + {"__str__", SyntaxError__str__, METH_VARARGS}, + {NULL, NULL} +}; + + +static PyObject * +KeyError__str__(PyObject *self, PyObject *args) +{ + PyObject *argsattr; + PyObject *result; + + if (!PyArg_ParseTuple(args, "O:__str__", &self)) + return NULL; + + if (!(argsattr = PyObject_GetAttrString(self, "args"))) + return NULL; + + /* If args is a tuple of exactly one item, apply repr to args[0]. + This is done so that e.g. the exception raised by {}[''] prints + KeyError: '' + rather than the confusing + KeyError + alone. The downside is that if KeyError is raised with an explanatory + string, that string will be displayed in quotes. Too bad. + If args is anything else, use the default Exception__str__(). + */ + if (PyTuple_Check(argsattr) && PyTuple_GET_SIZE(argsattr) == 1) { + PyObject *key = PyTuple_GET_ITEM(argsattr, 0); + result = PyObject_Repr(key); + } + else + result = Exception__str__(self, args); + + Py_DECREF(argsattr); + return result; +} + +static PyMethodDef KeyError_methods[] = { + {"__str__", KeyError__str__, METH_VARARGS}, + {NULL, NULL} +}; + + +#ifdef Py_USING_UNICODE +static +int get_int(PyObject *exc, const char *name, int *value) +{ + PyObject *attr = PyObject_GetAttrString(exc, (char *)name); + + if (!attr) + return -1; + if (!PyInt_Check(attr)) { + PyErr_Format(PyExc_TypeError, "%.200s attribute must be int", name); + Py_DECREF(attr); + return -1; + } + *value = PyInt_AS_LONG(attr); + Py_DECREF(attr); + return 0; +} + + +static +int set_int(PyObject *exc, const char *name, int value) +{ + PyObject *obj = PyInt_FromLong(value); + int result; + + if (!obj) + return -1; + result = PyObject_SetAttrString(exc, (char *)name, obj); + Py_DECREF(obj); + return result; +} + + +static +PyObject *get_string(PyObject *exc, const char *name) +{ + PyObject *attr = PyObject_GetAttrString(exc, (char *)name); + + if (!attr) + return NULL; + if (!PyString_Check(attr)) { + PyErr_Format(PyExc_TypeError, "%.200s attribute must be str", name); + Py_DECREF(attr); + return NULL; + } + return attr; +} + + +static +int set_string(PyObject *exc, const char *name, const char *value) +{ + PyObject *obj = PyString_FromString(value); + int result; + + if (!obj) + return -1; + result = PyObject_SetAttrString(exc, (char *)name, obj); + Py_DECREF(obj); + return result; +} + + +static +PyObject *get_unicode(PyObject *exc, const char *name) +{ + PyObject *attr = PyObject_GetAttrString(exc, (char *)name); + + if (!attr) + return NULL; + if (!PyUnicode_Check(attr)) { + PyErr_Format(PyExc_TypeError, "%.200s attribute must be unicode", name); + Py_DECREF(attr); + return NULL; + } + return attr; +} + +PyObject * PyUnicodeEncodeError_GetEncoding(PyObject *exc) +{ + return get_string(exc, "encoding"); +} + +PyObject * PyUnicodeDecodeError_GetEncoding(PyObject *exc) +{ + return get_string(exc, "encoding"); +} + +PyObject *PyUnicodeEncodeError_GetObject(PyObject *exc) +{ + return get_unicode(exc, "object"); +} + +PyObject *PyUnicodeDecodeError_GetObject(PyObject *exc) +{ + return get_string(exc, "object"); +} + +PyObject *PyUnicodeTranslateError_GetObject(PyObject *exc) +{ + return get_unicode(exc, "object"); +} + +int PyUnicodeEncodeError_GetStart(PyObject *exc, int *start) +{ + if (!get_int(exc, "start", start)) { + PyObject *object = PyUnicodeEncodeError_GetObject(exc); + int size; + if (!object) + return -1; + size = PyUnicode_GET_SIZE(object); + if (*start<0) + *start = 0; + if (*start>=size) + *start = size-1; + Py_DECREF(object); + return 0; + } + return -1; +} + + +int PyUnicodeDecodeError_GetStart(PyObject *exc, int *start) +{ + if (!get_int(exc, "start", start)) { + PyObject *object = PyUnicodeDecodeError_GetObject(exc); + int size; + if (!object) + return -1; + size = PyString_GET_SIZE(object); + if (*start<0) + *start = 0; + if (*start>=size) + *start = size-1; + Py_DECREF(object); + return 0; + } + return -1; +} + + +int PyUnicodeTranslateError_GetStart(PyObject *exc, int *start) +{ + return PyUnicodeEncodeError_GetStart(exc, start); +} + + +int PyUnicodeEncodeError_SetStart(PyObject *exc, int start) +{ + return set_int(exc, "start", start); +} + + +int PyUnicodeDecodeError_SetStart(PyObject *exc, int start) +{ + return set_int(exc, "start", start); +} + + +int PyUnicodeTranslateError_SetStart(PyObject *exc, int start) +{ + return set_int(exc, "start", start); +} + + +int PyUnicodeEncodeError_GetEnd(PyObject *exc, int *end) +{ + if (!get_int(exc, "end", end)) { + PyObject *object = PyUnicodeEncodeError_GetObject(exc); + int size; + if (!object) + return -1; + size = PyUnicode_GET_SIZE(object); + if (*end<1) + *end = 1; + if (*end>size) + *end = size; + Py_DECREF(object); + return 0; + } + return -1; +} + + +int PyUnicodeDecodeError_GetEnd(PyObject *exc, int *end) +{ + if (!get_int(exc, "end", end)) { + PyObject *object = PyUnicodeDecodeError_GetObject(exc); + int size; + if (!object) + return -1; + size = PyString_GET_SIZE(object); + if (*end<1) + *end = 1; + if (*end>size) + *end = size; + Py_DECREF(object); + return 0; + } + return -1; +} + + +int PyUnicodeTranslateError_GetEnd(PyObject *exc, int *start) +{ + return PyUnicodeEncodeError_GetEnd(exc, start); +} + + +int PyUnicodeEncodeError_SetEnd(PyObject *exc, int end) +{ + return set_int(exc, "end", end); +} + + +int PyUnicodeDecodeError_SetEnd(PyObject *exc, int end) +{ + return set_int(exc, "end", end); +} + + +int PyUnicodeTranslateError_SetEnd(PyObject *exc, int end) +{ + return set_int(exc, "end", end); +} + + +PyObject *PyUnicodeEncodeError_GetReason(PyObject *exc) +{ + return get_string(exc, "reason"); +} + + +PyObject *PyUnicodeDecodeError_GetReason(PyObject *exc) +{ + return get_string(exc, "reason"); +} + + +PyObject *PyUnicodeTranslateError_GetReason(PyObject *exc) +{ + return get_string(exc, "reason"); +} + + +int PyUnicodeEncodeError_SetReason(PyObject *exc, const char *reason) +{ + return set_string(exc, "reason", reason); +} + + +int PyUnicodeDecodeError_SetReason(PyObject *exc, const char *reason) +{ + return set_string(exc, "reason", reason); +} + + +int PyUnicodeTranslateError_SetReason(PyObject *exc, const char *reason) +{ + return set_string(exc, "reason", reason); +} + + +static PyObject * +UnicodeError__init__(PyObject *self, PyObject *args, PyTypeObject *objecttype) +{ + PyObject *rtnval = NULL; + PyObject *encoding; + PyObject *object; + PyObject *start; + PyObject *end; + PyObject *reason; + + if (!(self = get_self(args))) + return NULL; + + if (!(args = PySequence_GetSlice(args, 1, PySequence_Size(args)))) + return NULL; + + if (!PyArg_ParseTuple(args, "O!O!O!O!O!", + &PyString_Type, &encoding, + objecttype, &object, + &PyInt_Type, &start, + &PyInt_Type, &end, + &PyString_Type, &reason)) + goto finally; + + if (PyObject_SetAttrString(self, "args", args)) + goto finally; + + if (PyObject_SetAttrString(self, "encoding", encoding)) + goto finally; + if (PyObject_SetAttrString(self, "object", object)) + goto finally; + if (PyObject_SetAttrString(self, "start", start)) + goto finally; + if (PyObject_SetAttrString(self, "end", end)) + goto finally; + if (PyObject_SetAttrString(self, "reason", reason)) + goto finally; + + Py_INCREF(Py_None); + rtnval = Py_None; + + finally: + Py_DECREF(args); + return rtnval; +} + + +static PyObject * +UnicodeEncodeError__init__(PyObject *self, PyObject *args) +{ + return UnicodeError__init__(self, args, &PyUnicode_Type); +} + +static PyObject * +UnicodeEncodeError__str__(PyObject *self, PyObject *arg) +{ + PyObject *encodingObj = NULL; + PyObject *objectObj = NULL; + int start; + int end; + PyObject *reasonObj = NULL; + char buffer[1000]; + PyObject *result = NULL; + + self = arg; + + if (!(encodingObj = PyUnicodeEncodeError_GetEncoding(self))) + goto error; + + if (!(objectObj = PyUnicodeEncodeError_GetObject(self))) + goto error; + + if (PyUnicodeEncodeError_GetStart(self, &start)) + goto error; + + if (PyUnicodeEncodeError_GetEnd(self, &end)) + goto error; + + if (!(reasonObj = PyUnicodeEncodeError_GetReason(self))) + goto error; + + if (end==start+1) { + int badchar = (int)PyUnicode_AS_UNICODE(objectObj)[start]; + char *format; + if (badchar <= 0xff) + format = "'%.400s' codec can't encode character u'\\x%02x' in position %d: %.400s"; + else if (badchar <= 0xffff) + format = "'%.400s' codec can't encode character u'\\u%04x' in position %d: %.400s"; + else + format = "'%.400s' codec can't encode character u'\\U%08x' in position %d: %.400s"; + PyOS_snprintf(buffer, sizeof(buffer), + format, + PyString_AS_STRING(encodingObj), + badchar, + start, + PyString_AS_STRING(reasonObj) + ); + } + else { + PyOS_snprintf(buffer, sizeof(buffer), + "'%.400s' codec can't encode characters in position %d-%d: %.400s", + PyString_AS_STRING(encodingObj), + start, + end-1, + PyString_AS_STRING(reasonObj) + ); + } + result = PyString_FromString(buffer); + +error: + Py_XDECREF(reasonObj); + Py_XDECREF(objectObj); + Py_XDECREF(encodingObj); + return result; +} + +static PyMethodDef UnicodeEncodeError_methods[] = { + {"__init__", UnicodeEncodeError__init__, METH_VARARGS}, + {"__str__", UnicodeEncodeError__str__, METH_O}, + {NULL, NULL} +}; + + +PyObject * PyUnicodeEncodeError_Create( + const char *encoding, const Py_UNICODE *object, int length, + int start, int end, const char *reason) +{ + return PyObject_CallFunction(PyExc_UnicodeEncodeError, "su#iis", + encoding, object, length, start, end, reason); +} + + +static PyObject * +UnicodeDecodeError__init__(PyObject *self, PyObject *args) +{ + return UnicodeError__init__(self, args, &PyString_Type); +} + +static PyObject * +UnicodeDecodeError__str__(PyObject *self, PyObject *arg) +{ + PyObject *encodingObj = NULL; + PyObject *objectObj = NULL; + int start; + int end; + PyObject *reasonObj = NULL; + char buffer[1000]; + PyObject *result = NULL; + + self = arg; + + if (!(encodingObj = PyUnicodeDecodeError_GetEncoding(self))) + goto error; + + if (!(objectObj = PyUnicodeDecodeError_GetObject(self))) + goto error; + + if (PyUnicodeDecodeError_GetStart(self, &start)) + goto error; + + if (PyUnicodeDecodeError_GetEnd(self, &end)) + goto error; + + if (!(reasonObj = PyUnicodeDecodeError_GetReason(self))) + goto error; + + if (end==start+1) { + PyOS_snprintf(buffer, sizeof(buffer), + "'%.400s' codec can't decode byte 0x%02x in position %d: %.400s", + PyString_AS_STRING(encodingObj), + ((int)PyString_AS_STRING(objectObj)[start])&0xff, + start, + PyString_AS_STRING(reasonObj) + ); + } + else { + PyOS_snprintf(buffer, sizeof(buffer), + "'%.400s' codec can't decode bytes in position %d-%d: %.400s", + PyString_AS_STRING(encodingObj), + start, + end-1, + PyString_AS_STRING(reasonObj) + ); + } + result = PyString_FromString(buffer); + +error: + Py_XDECREF(reasonObj); + Py_XDECREF(objectObj); + Py_XDECREF(encodingObj); + return result; +} + +static PyMethodDef UnicodeDecodeError_methods[] = { + {"__init__", UnicodeDecodeError__init__, METH_VARARGS}, + {"__str__", UnicodeDecodeError__str__, METH_O}, + {NULL, NULL} +}; + + +PyObject * PyUnicodeDecodeError_Create( + const char *encoding, const char *object, int length, + int start, int end, const char *reason) +{ + return PyObject_CallFunction(PyExc_UnicodeDecodeError, "ss#iis", + encoding, object, length, start, end, reason); +} + + +static PyObject * +UnicodeTranslateError__init__(PyObject *self, PyObject *args) +{ + PyObject *rtnval = NULL; + PyObject *object; + PyObject *start; + PyObject *end; + PyObject *reason; + + if (!(self = get_self(args))) + return NULL; + + if (!(args = PySequence_GetSlice(args, 1, PySequence_Size(args)))) + return NULL; + + if (!PyArg_ParseTuple(args, "O!O!O!O!", + &PyUnicode_Type, &object, + &PyInt_Type, &start, + &PyInt_Type, &end, + &PyString_Type, &reason)) + goto finally; + + if (PyObject_SetAttrString(self, "args", args)) + goto finally; + + if (PyObject_SetAttrString(self, "object", object)) + goto finally; + if (PyObject_SetAttrString(self, "start", start)) + goto finally; + if (PyObject_SetAttrString(self, "end", end)) + goto finally; + if (PyObject_SetAttrString(self, "reason", reason)) + goto finally; + + Py_INCREF(Py_None); + rtnval = Py_None; + + finally: + Py_DECREF(args); + return rtnval; +} + + +static PyObject * +UnicodeTranslateError__str__(PyObject *self, PyObject *arg) +{ + PyObject *objectObj = NULL; + int start; + int end; + PyObject *reasonObj = NULL; + char buffer[1000]; + PyObject *result = NULL; + + self = arg; + + if (!(objectObj = PyUnicodeTranslateError_GetObject(self))) + goto error; + + if (PyUnicodeTranslateError_GetStart(self, &start)) + goto error; + + if (PyUnicodeTranslateError_GetEnd(self, &end)) + goto error; + + if (!(reasonObj = PyUnicodeTranslateError_GetReason(self))) + goto error; + + if (end==start+1) { + int badchar = (int)PyUnicode_AS_UNICODE(objectObj)[start]; + char *format; + if (badchar <= 0xff) + format = "can't translate character u'\\x%02x' in position %d: %.400s"; + else if (badchar <= 0xffff) + format = "can't translate character u'\\u%04x' in position %d: %.400s"; + else + format = "can't translate character u'\\U%08x' in position %d: %.400s"; + PyOS_snprintf(buffer, sizeof(buffer), + format, + badchar, + start, + PyString_AS_STRING(reasonObj) + ); + } + else { + PyOS_snprintf(buffer, sizeof(buffer), + "can't translate characters in position %d-%d: %.400s", + start, + end-1, + PyString_AS_STRING(reasonObj) + ); + } + result = PyString_FromString(buffer); + +error: + Py_XDECREF(reasonObj); + Py_XDECREF(objectObj); + return result; +} + +static PyMethodDef UnicodeTranslateError_methods[] = { + {"__init__", UnicodeTranslateError__init__, METH_VARARGS}, + {"__str__", UnicodeTranslateError__str__, METH_O}, + {NULL, NULL} +}; + + +PyObject * PyUnicodeTranslateError_Create( + const Py_UNICODE *object, int length, + int start, int end, const char *reason) +{ + return PyObject_CallFunction(PyExc_UnicodeTranslateError, "u#iis", + object, length, start, end, reason); +} +#endif + + + +/* Exception doc strings */ + +PyDoc_STRVAR(AssertionError__doc__, "Assertion failed."); + +PyDoc_STRVAR(LookupError__doc__, "Base class for lookup errors."); + +PyDoc_STRVAR(IndexError__doc__, "Sequence index out of range."); + +PyDoc_STRVAR(KeyError__doc__, "Mapping key not found."); + +PyDoc_STRVAR(ArithmeticError__doc__, "Base class for arithmetic errors."); + +PyDoc_STRVAR(OverflowError__doc__, "Result too large to be represented."); + +PyDoc_STRVAR(ZeroDivisionError__doc__, +"Second argument to a division or modulo operation was zero."); + +PyDoc_STRVAR(FloatingPointError__doc__, "Floating point operation failed."); + +PyDoc_STRVAR(ValueError__doc__, +"Inappropriate argument value (of correct type)."); + +PyDoc_STRVAR(UnicodeError__doc__, "Unicode related error."); + +#ifdef Py_USING_UNICODE +PyDoc_STRVAR(UnicodeEncodeError__doc__, "Unicode encoding error."); + +PyDoc_STRVAR(UnicodeDecodeError__doc__, "Unicode decoding error."); + +PyDoc_STRVAR(UnicodeTranslateError__doc__, "Unicode translation error."); +#endif + +PyDoc_STRVAR(SystemError__doc__, +"Internal error in the Python interpreter.\n\ +\n\ +Please report this to the Python maintainer, along with the traceback,\n\ +the Python version, and the hardware/OS platform and version."); + +PyDoc_STRVAR(ReferenceError__doc__, +"Weak ref proxy used after referent went away."); + +PyDoc_STRVAR(MemoryError__doc__, "Out of memory."); + +PyDoc_STRVAR(IndentationError__doc__, "Improper indentation."); + +PyDoc_STRVAR(TabError__doc__, "Improper mixture of spaces and tabs."); + +/* Warning category docstrings */ + +PyDoc_STRVAR(Warning__doc__, "Base class for warning categories."); + +PyDoc_STRVAR(UserWarning__doc__, +"Base class for warnings generated by user code."); + +PyDoc_STRVAR(DeprecationWarning__doc__, +"Base class for warnings about deprecated features."); + +PyDoc_STRVAR(PendingDeprecationWarning__doc__, +"Base class for warnings about features which will be deprecated " +"in the future."); + +PyDoc_STRVAR(SyntaxWarning__doc__, +"Base class for warnings about dubious syntax."); + +PyDoc_STRVAR(OverflowWarning__doc__, +"Base class for warnings about numeric overflow."); + +PyDoc_STRVAR(RuntimeWarning__doc__, +"Base class for warnings about dubious runtime behavior."); + +PyDoc_STRVAR(FutureWarning__doc__, +"Base class for warnings about constructs that will change semantically " +"in the future."); + + + +/* module global functions */ +static PyMethodDef functions[] = { + /* Sentinel */ + {NULL, NULL} +}; + + + +/* Global C API defined exceptions */ + +PyObject *PyExc_Exception; +PyObject *PyExc_StopIteration; +PyObject *PyExc_StandardError; +PyObject *PyExc_ArithmeticError; +PyObject *PyExc_LookupError; + +PyObject *PyExc_AssertionError; +PyObject *PyExc_AttributeError; +PyObject *PyExc_EOFError; +PyObject *PyExc_FloatingPointError; +PyObject *PyExc_EnvironmentError; +PyObject *PyExc_IOError; +PyObject *PyExc_OSError; +PyObject *PyExc_ImportError; +PyObject *PyExc_IndexError; +PyObject *PyExc_KeyError; +PyObject *PyExc_KeyboardInterrupt; +PyObject *PyExc_MemoryError; +PyObject *PyExc_NameError; +PyObject *PyExc_OverflowError; +PyObject *PyExc_RuntimeError; +PyObject *PyExc_NotImplementedError; +PyObject *PyExc_SyntaxError; +PyObject *PyExc_IndentationError; +PyObject *PyExc_TabError; +PyObject *PyExc_ReferenceError; +PyObject *PyExc_SystemError; +PyObject *PyExc_SystemExit; +PyObject *PyExc_UnboundLocalError; +PyObject *PyExc_UnicodeError; +PyObject *PyExc_UnicodeEncodeError; +PyObject *PyExc_UnicodeDecodeError; +PyObject *PyExc_UnicodeTranslateError; +PyObject *PyExc_TypeError; +PyObject *PyExc_ValueError; +PyObject *PyExc_ZeroDivisionError; +#ifdef MS_WINDOWS +PyObject *PyExc_WindowsError; +#endif +#ifdef __VMS +PyObject *PyExc_VMSError; +#endif + +/* Pre-computed MemoryError instance. Best to create this as early as + * possibly and not wait until a MemoryError is actually raised! + */ +PyObject *PyExc_MemoryErrorInst; + +/* Predefined warning categories */ +PyObject *PyExc_Warning; +PyObject *PyExc_UserWarning; +PyObject *PyExc_DeprecationWarning; +PyObject *PyExc_PendingDeprecationWarning; +PyObject *PyExc_SyntaxWarning; +PyObject *PyExc_OverflowWarning; +PyObject *PyExc_RuntimeWarning; +PyObject *PyExc_FutureWarning; + + + +/* mapping between exception names and their PyObject ** */ +static struct { + char *name; + PyObject **exc; + PyObject **base; /* NULL == PyExc_StandardError */ + char *docstr; + PyMethodDef *methods; + int (*classinit)(PyObject *); +} exctable[] = { + /* + * The first three classes MUST appear in exactly this order + */ + {"Exception", &PyExc_Exception}, + {"StopIteration", &PyExc_StopIteration, &PyExc_Exception, + StopIteration__doc__}, + {"StandardError", &PyExc_StandardError, &PyExc_Exception, + StandardError__doc__}, + {"TypeError", &PyExc_TypeError, 0, TypeError__doc__}, + /* + * The rest appear in depth-first order of the hierarchy + */ + {"SystemExit", &PyExc_SystemExit, &PyExc_Exception, SystemExit__doc__, + SystemExit_methods}, + {"KeyboardInterrupt", &PyExc_KeyboardInterrupt, 0, KeyboardInterrupt__doc__}, + {"ImportError", &PyExc_ImportError, 0, ImportError__doc__}, + {"EnvironmentError", &PyExc_EnvironmentError, 0, EnvironmentError__doc__, + EnvironmentError_methods}, + {"IOError", &PyExc_IOError, &PyExc_EnvironmentError, IOError__doc__}, + {"OSError", &PyExc_OSError, &PyExc_EnvironmentError, OSError__doc__}, +#ifdef MS_WINDOWS + {"WindowsError", &PyExc_WindowsError, &PyExc_OSError, + WindowsError__doc__}, +#endif /* MS_WINDOWS */ +#ifdef __VMS + {"VMSError", &PyExc_VMSError, &PyExc_OSError, + VMSError__doc__}, +#endif + {"EOFError", &PyExc_EOFError, 0, EOFError__doc__}, + {"RuntimeError", &PyExc_RuntimeError, 0, RuntimeError__doc__}, + {"NotImplementedError", &PyExc_NotImplementedError, + &PyExc_RuntimeError, NotImplementedError__doc__}, + {"NameError", &PyExc_NameError, 0, NameError__doc__}, + {"UnboundLocalError", &PyExc_UnboundLocalError, &PyExc_NameError, + UnboundLocalError__doc__}, + {"AttributeError", &PyExc_AttributeError, 0, AttributeError__doc__}, + {"SyntaxError", &PyExc_SyntaxError, 0, SyntaxError__doc__, + SyntaxError_methods, SyntaxError__classinit__}, + {"IndentationError", &PyExc_IndentationError, &PyExc_SyntaxError, + IndentationError__doc__}, + {"TabError", &PyExc_TabError, &PyExc_IndentationError, + TabError__doc__}, + {"AssertionError", &PyExc_AssertionError, 0, AssertionError__doc__}, + {"LookupError", &PyExc_LookupError, 0, LookupError__doc__}, + {"IndexError", &PyExc_IndexError, &PyExc_LookupError, + IndexError__doc__}, + {"KeyError", &PyExc_KeyError, &PyExc_LookupError, + KeyError__doc__, KeyError_methods}, + {"ArithmeticError", &PyExc_ArithmeticError, 0, ArithmeticError__doc__}, + {"OverflowError", &PyExc_OverflowError, &PyExc_ArithmeticError, + OverflowError__doc__}, + {"ZeroDivisionError", &PyExc_ZeroDivisionError, &PyExc_ArithmeticError, + ZeroDivisionError__doc__}, + {"FloatingPointError", &PyExc_FloatingPointError, &PyExc_ArithmeticError, + FloatingPointError__doc__}, + {"ValueError", &PyExc_ValueError, 0, ValueError__doc__}, + {"UnicodeError", &PyExc_UnicodeError, &PyExc_ValueError, UnicodeError__doc__}, +#ifdef Py_USING_UNICODE + {"UnicodeEncodeError", &PyExc_UnicodeEncodeError, &PyExc_UnicodeError, + UnicodeEncodeError__doc__, UnicodeEncodeError_methods}, + {"UnicodeDecodeError", &PyExc_UnicodeDecodeError, &PyExc_UnicodeError, + UnicodeDecodeError__doc__, UnicodeDecodeError_methods}, + {"UnicodeTranslateError", &PyExc_UnicodeTranslateError, &PyExc_UnicodeError, + UnicodeTranslateError__doc__, UnicodeTranslateError_methods}, +#endif + {"ReferenceError", &PyExc_ReferenceError, 0, ReferenceError__doc__}, + {"SystemError", &PyExc_SystemError, 0, SystemError__doc__}, + {"MemoryError", &PyExc_MemoryError, 0, MemoryError__doc__}, + /* Warning categories */ + {"Warning", &PyExc_Warning, &PyExc_Exception, Warning__doc__}, + {"UserWarning", &PyExc_UserWarning, &PyExc_Warning, UserWarning__doc__}, + {"DeprecationWarning", &PyExc_DeprecationWarning, &PyExc_Warning, + DeprecationWarning__doc__}, + {"PendingDeprecationWarning", &PyExc_PendingDeprecationWarning, &PyExc_Warning, + PendingDeprecationWarning__doc__}, + {"SyntaxWarning", &PyExc_SyntaxWarning, &PyExc_Warning, SyntaxWarning__doc__}, + {"OverflowWarning", &PyExc_OverflowWarning, &PyExc_Warning, + OverflowWarning__doc__}, + {"RuntimeWarning", &PyExc_RuntimeWarning, &PyExc_Warning, + RuntimeWarning__doc__}, + {"FutureWarning", &PyExc_FutureWarning, &PyExc_Warning, + FutureWarning__doc__}, + /* Sentinel */ + {NULL} +}; + + + +void +_PyExc_Init(void) +{ + char *modulename = "exceptions"; + int modnamesz = strlen(modulename); + int i; + PyObject *me, *mydict, *bltinmod, *bdict, *doc, *args; + + me = Py_InitModule(modulename, functions); + if (me == NULL) + goto err; + mydict = PyModule_GetDict(me); + if (mydict == NULL) + goto err; + bltinmod = PyImport_ImportModule("__builtin__"); + if (bltinmod == NULL) + goto err; + bdict = PyModule_GetDict(bltinmod); + if (bdict == NULL) + goto err; + doc = PyString_FromString(module__doc__); + if (doc == NULL) + goto err; + + i = PyDict_SetItemString(mydict, "__doc__", doc); + Py_DECREF(doc); + if (i < 0) { + err: + Py_FatalError("exceptions bootstrapping error."); + return; + } + + /* This is the base class of all exceptions, so make it first. */ + if (make_Exception(modulename) || + PyDict_SetItemString(mydict, "Exception", PyExc_Exception) || + PyDict_SetItemString(bdict, "Exception", PyExc_Exception)) + { + Py_FatalError("Base class `Exception' could not be created."); + } + + /* Now we can programmatically create all the remaining exceptions. + * Remember to start the loop at 1 to skip Exceptions. + */ + for (i=1; exctable[i].name; i++) { + int status; + char *cname = PyMem_NEW(char, modnamesz+strlen(exctable[i].name)+2); + PyObject *base; + + (void)strcpy(cname, modulename); + (void)strcat(cname, "."); + (void)strcat(cname, exctable[i].name); + + if (exctable[i].base == 0) + base = PyExc_StandardError; + else + base = *exctable[i].base; + + status = make_class(exctable[i].exc, base, cname, + exctable[i].methods, + exctable[i].docstr); + + PyMem_DEL(cname); + + if (status) + Py_FatalError("Standard exception classes could not be created."); + + if (exctable[i].classinit) { + status = (*exctable[i].classinit)(*exctable[i].exc); + if (status) + Py_FatalError("An exception class could not be initialized."); + } + + /* Now insert the class into both this module and the __builtin__ + * module. + */ + if (PyDict_SetItemString(mydict, exctable[i].name, *exctable[i].exc) || + PyDict_SetItemString(bdict, exctable[i].name, *exctable[i].exc)) + { + Py_FatalError("Module dictionary insertion problem."); + } + } + + /* Now we need to pre-allocate a MemoryError instance */ + args = Py_BuildValue("()"); + if (!args || + !(PyExc_MemoryErrorInst = PyEval_CallObject(PyExc_MemoryError, args))) + { + Py_FatalError("Cannot pre-allocate MemoryError instance\n"); + } + Py_DECREF(args); + + /* We're done with __builtin__ */ + Py_DECREF(bltinmod); +} + + +void +_PyExc_Fini(void) +{ + int i; + + Py_XDECREF(PyExc_MemoryErrorInst); + PyExc_MemoryErrorInst = NULL; + + for (i=0; exctable[i].name; i++) { + /* clear the class's dictionary, freeing up circular references + * between the class and its methods. + */ + PyObject* cdict = PyObject_GetAttrString(*exctable[i].exc, "__dict__"); + PyDict_Clear(cdict); + Py_DECREF(cdict); + + /* Now decref the exception class */ + Py_XDECREF(*exctable[i].exc); + *exctable[i].exc = NULL; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/fmod.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/fmod.c new file mode 100644 index 00000000..5d45b68a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/fmod.c @@ -0,0 +1,27 @@ + +/* Portable fmod(x, y) implementation for systems that don't have it */ + +#include "pyconfig.h" + +#include "pyport.h" +#include + +double +fmod(double x, double y) +{ + double i, f; + + if (y == 0.0) { + errno = EDOM; + return 0.0; + } + + /* return f such that x = i*y + f for some integer i + such that |f| < |y| and f has the same sign as x */ + + i = floor(x/y); + f = x - i*y; + if ((x < 0.0) != (y < 0.0)) + f = f-y; + return f; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/frozen.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/frozen.c new file mode 100644 index 00000000..c10d7d00 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/frozen.c @@ -0,0 +1,38 @@ + +/* Dummy frozen modules initializer */ + +#include "Python.h" + +/* In order to test the support for frozen modules, by default we + define a single frozen module, __hello__. Loading it will print + some famous words... */ + +/* To regenerate this data after the bytecode or marshal format has changed, + go to ../Tools/freeze/ and freeze the hello.py file; then copy and paste + the appropriate bytes from M___main__.c. */ + +static unsigned char M___hello__[] = { + 99,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, + 0,115,9,0,0,0,100,0,0,71,72,100,1,0,83,40, + 2,0,0,0,115,14,0,0,0,72,101,108,108,111,32,119, + 111,114,108,100,46,46,46,78,40,0,0,0,0,40,0,0, + 0,0,40,0,0,0,0,40,0,0,0,0,115,8,0,0, + 0,104,101,108,108,111,46,112,121,115,1,0,0,0,63,1, + 0,0,0,115,0,0,0,0, +}; + +#define SIZE (int)sizeof(M___hello__) + +static struct _frozen _PyImport_FrozenModules[] = { + /* Test module */ + {"__hello__", M___hello__, SIZE}, + /* Test package (negative size indicates package-ness) */ + {"__phello__", M___hello__, -SIZE}, + {"__phello__.spam", M___hello__, SIZE}, + {0, 0, 0} /* sentinel */ +}; + +/* Embedding apps may change this pointer to point to their favorite + collection of frozen modules: */ + +struct _frozen *PyImport_FrozenModules = _PyImport_FrozenModules; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/frozenmain.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/frozenmain.c new file mode 100644 index 00000000..b44b4b27 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/frozenmain.c @@ -0,0 +1,68 @@ + +/* Python interpreter main program for frozen scripts */ + +#include "Python.h" + +#ifdef MS_WINDOWS +extern void PyWinFreeze_ExeInit(void); +extern void PyWinFreeze_ExeTerm(void); +extern int PyInitFrozenExtensions(void); +#endif + +/* Main program */ + +int +Py_FrozenMain(int argc, char **argv) +{ + char *p; + int n, sts; + int inspect = 0; + int unbuffered = 0; + + Py_FrozenFlag = 1; /* Suppress errors from getpath.c */ + + if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') + inspect = 1; + if ((p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0') + unbuffered = 1; + + if (unbuffered) { + setbuf(stdin, (char *)NULL); + setbuf(stdout, (char *)NULL); + setbuf(stderr, (char *)NULL); + } + +#ifdef MS_WINDOWS + PyInitFrozenExtensions(); +#endif /* MS_WINDOWS */ + Py_SetProgramName(argv[0]); + Py_Initialize(); +#ifdef MS_WINDOWS + PyWinFreeze_ExeInit(); +#endif + + if (Py_VerboseFlag) + fprintf(stderr, "Python %s\n%s\n", + Py_GetVersion(), Py_GetCopyright()); + + PySys_SetArgv(argc, argv); + + n = PyImport_ImportFrozenModule("__main__"); + if (n == 0) + Py_FatalError("__main__ not frozen"); + if (n < 0) { + PyErr_Print(); + sts = 1; + } + else + sts = 0; + + if (inspect && isatty((int)fileno(stdin))) + sts = PyRun_AnyFile(stdin, "") != 0; + +#ifdef MS_WINDOWS + PyWinFreeze_ExeTerm(); +#endif + Py_Finalize(); + return sts; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/future.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/future.c new file mode 100644 index 00000000..2d06a443 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/future.c @@ -0,0 +1,260 @@ +#include "Python.h" +#include "node.h" +#include "token.h" +#include "graminit.h" +#include "compile.h" +#include "symtable.h" + +#define UNDEFINED_FUTURE_FEATURE "future feature %.100s is not defined" +#define FUTURE_IMPORT_STAR "future statement does not support import *" + +/* FUTURE_POSSIBLE() is provided to accomodate doc strings, which is + the only statement that can occur before a future statement. +*/ +#define FUTURE_POSSIBLE(FF) ((FF)->ff_last_lineno == -1) + +static int +future_check_features(PyFutureFeatures *ff, node *n, const char *filename) +{ + int i; + char *feature; + node *ch; + + REQ(n, import_stmt); /* must by from __future__ import ... */ + + for (i = 3; i < NCH(n); i += 2) { + ch = CHILD(n, i); + if (TYPE(ch) == STAR) { + PyErr_SetString(PyExc_SyntaxError, + FUTURE_IMPORT_STAR); + PyErr_SyntaxLocation(filename, ch->n_lineno); + return -1; + } + REQ(ch, import_as_name); + feature = STR(CHILD(ch, 0)); + if (strcmp(feature, FUTURE_NESTED_SCOPES) == 0) { + continue; + } else if (strcmp(feature, FUTURE_GENERATORS) == 0) { + continue; + } else if (strcmp(feature, FUTURE_DIVISION) == 0) { + ff->ff_features |= CO_FUTURE_DIVISION; + } else if (strcmp(feature, "braces") == 0) { + PyErr_SetString(PyExc_SyntaxError, + "not a chance"); + PyErr_SyntaxLocation(filename, CHILD(ch, 0)->n_lineno); + return -1; + } else { + PyErr_Format(PyExc_SyntaxError, + UNDEFINED_FUTURE_FEATURE, feature); + PyErr_SyntaxLocation(filename, CHILD(ch, 0)->n_lineno); + return -1; + } + } + return 0; +} + +static void +future_error(node *n, const char *filename) +{ + PyErr_SetString(PyExc_SyntaxError, + "from __future__ imports must occur at the " + "beginning of the file"); + PyErr_SyntaxLocation(filename, n->n_lineno); +} + +/* Relevant portions of the grammar: + +single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE +file_input: (NEWLINE | stmt)* ENDMARKER +stmt: simple_stmt | compound_stmt +simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE +small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt + | import_stmt | global_stmt | exec_stmt | assert_stmt +import_stmt: 'import' dotted_as_name (',' dotted_as_name)* + | 'from' dotted_name 'import' ('*' | import_as_name (',' import_as_name)*) +import_as_name: NAME [NAME NAME] +dotted_as_name: dotted_name [NAME NAME] +dotted_name: NAME ('.' NAME)* +*/ + +/* future_parse() finds future statements at the beginnning of a + module. The function calls itself recursively, rather than + factoring out logic for different kinds of statements into + different routines. + + Return values: + -1 indicates an error occurred, e.g. unknown feature name + 0 indicates no feature was found + 1 indicates a feature was found +*/ + +static int +future_parse(PyFutureFeatures *ff, node *n, const char *filename) +{ + int i, r; + loop: + + switch (TYPE(n)) { + + case single_input: + if (TYPE(CHILD(n, 0)) == simple_stmt) { + n = CHILD(n, 0); + goto loop; + } + return 0; + + case file_input: + /* Check each statement in the file, starting with the + first, and continuing until the first statement + that isn't a future statement. + */ + for (i = 0; i < NCH(n); i++) { + node *ch = CHILD(n, i); + if (TYPE(ch) == stmt) { + r = future_parse(ff, ch, filename); + /* Need to check both conditions below + to accomodate doc strings, which + causes r < 0. + */ + if (r < 1 && !FUTURE_POSSIBLE(ff)) + return r; + } + } + return 0; + + case simple_stmt: + if (NCH(n) == 2) { + REQ(CHILD(n, 0), small_stmt); + n = CHILD(n, 0); + goto loop; + } else { + /* Deal with the special case of a series of + small statements on a single line. If a + future statement follows some other + statement, the SyntaxError is raised here. + In all other cases, the symtable pass + raises the exception. + */ + int found = 0, end_of_future = 0; + + for (i = 0; i < NCH(n); i += 2) { + if (TYPE(CHILD(n, i)) == small_stmt) { + r = future_parse(ff, CHILD(n, i), + filename); + if (r < 1) + end_of_future = 1; + else { + found = 1; + if (end_of_future) { + future_error(n, + filename); + return -1; + } + } + } + } + + /* If we found one and only one, then the + current lineno is legal. + */ + if (found) + ff->ff_last_lineno = n->n_lineno + 1; + else + ff->ff_last_lineno = n->n_lineno; + + if (end_of_future && found) + return 1; + else + return 0; + } + + case stmt: + if (TYPE(CHILD(n, 0)) == simple_stmt) { + n = CHILD(n, 0); + goto loop; + } else if (TYPE(CHILD(n, 0)) == expr_stmt) { + n = CHILD(n, 0); + goto loop; + } else { + REQ(CHILD(n, 0), compound_stmt); + ff->ff_last_lineno = n->n_lineno; + return 0; + } + + case small_stmt: + n = CHILD(n, 0); + goto loop; + + case import_stmt: { + node *name; + + if (STR(CHILD(n, 0))[0] != 'f') { /* from */ + ff->ff_last_lineno = n->n_lineno; + return 0; + } + name = CHILD(n, 1); + if (strcmp(STR(CHILD(name, 0)), "__future__") != 0) + return 0; + if (future_check_features(ff, n, filename) < 0) + return -1; + ff->ff_last_lineno = n->n_lineno + 1; + return 1; + } + + /* The cases below -- all of them! -- are necessary to find + and skip doc strings. */ + case expr_stmt: + case testlist: + case test: + case and_test: + case not_test: + case comparison: + case expr: + case xor_expr: + case and_expr: + case shift_expr: + case arith_expr: + case term: + case factor: + case power: + if (NCH(n) == 1) { + n = CHILD(n, 0); + goto loop; + } + break; + + case atom: + if (TYPE(CHILD(n, 0)) == STRING + && ff->ff_found_docstring == 0) { + ff->ff_found_docstring = 1; + return 0; + } + ff->ff_last_lineno = n->n_lineno; + return 0; + + default: + ff->ff_last_lineno = n->n_lineno; + return 0; + } + return 0; +} + +PyFutureFeatures * +PyNode_Future(node *n, const char *filename) +{ + PyFutureFeatures *ff; + + ff = (PyFutureFeatures *)PyMem_Malloc(sizeof(PyFutureFeatures)); + if (ff == NULL) + return NULL; + ff->ff_found_docstring = 0; + ff->ff_last_lineno = -1; + ff->ff_features = 0; + + if (future_parse(ff, n, filename) < 0) { + PyMem_Free((void *)ff); + return NULL; + } + return ff; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getargs.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getargs.c new file mode 100644 index 00000000..47080b31 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getargs.c @@ -0,0 +1,1562 @@ + +/* New getargs implementation */ + +#include "Python.h" + +#include + + +int PyArg_Parse(PyObject *, char *, ...); +int PyArg_ParseTuple(PyObject *, char *, ...); +int PyArg_VaParse(PyObject *, char *, va_list); + +int PyArg_ParseTupleAndKeywords(PyObject *, PyObject *, + char *, char **, ...); + +/* Forward */ +static int vgetargs1(PyObject *, char *, va_list *, int); +static void seterror(int, char *, int *, char *, char *); +static char *convertitem(PyObject *, char **, va_list *, int *, char *, + size_t, PyObject **); +static char *converttuple(PyObject *, char **, va_list *, + int *, char *, size_t, int, PyObject **); +static char *convertsimple(PyObject *, char **, va_list *, char *, + size_t, PyObject **); +static int convertbuffer(PyObject *, void **p, char **); + +static int vgetargskeywords(PyObject *, PyObject *, + char *, char **, va_list *); +static char *skipitem(char **, va_list *); + +int +PyArg_Parse(PyObject *args, char *format, ...) +{ + int retval; + va_list va; + + va_start(va, format); + retval = vgetargs1(args, format, &va, 1); + va_end(va); + return retval; +} + + +int +PyArg_ParseTuple(PyObject *args, char *format, ...) +{ + int retval; + va_list va; + + va_start(va, format); + retval = vgetargs1(args, format, &va, 0); + va_end(va); + return retval; +} + + +int +PyArg_VaParse(PyObject *args, char *format, va_list va) +{ + va_list lva; + +#ifdef VA_LIST_IS_ARRAY + memcpy(lva, va, sizeof(va_list)); +#else +#ifdef __va_copy + __va_copy(lva, va); +#else + lva = va; +#endif +#endif + + return vgetargs1(args, format, &lva, 0); +} + + +/* Handle cleanup of allocated memory in case of exception */ + +static int +addcleanup(void *ptr, PyObject **freelist) +{ + PyObject *cobj; + if (!*freelist) { + *freelist = PyList_New(0); + if (!*freelist) { + PyMem_FREE(ptr); + return -1; + } + } + cobj = PyCObject_FromVoidPtr(ptr, NULL); + if (!cobj) { + PyMem_FREE(ptr); + return -1; + } + if(PyList_Append(*freelist, cobj)) { + PyMem_FREE(ptr); + Py_DECREF(cobj); + return -1; + } + Py_DECREF(cobj); + return 0; +} + +static int +cleanreturn(int retval, PyObject *freelist) +{ + if(freelist) { + if((retval) == 0) { + int len = PyList_GET_SIZE(freelist), i; + for (i = 0; i < len; i++) + PyMem_FREE(PyCObject_AsVoidPtr( + PyList_GET_ITEM(freelist, i))); + } + Py_DECREF(freelist); + } + return retval; +} + + +static int +vgetargs1(PyObject *args, char *format, va_list *p_va, int compat) +{ + char msgbuf[256]; + int levels[32]; + char *fname = NULL; + char *message = NULL; + int min = -1; + int max = 0; + int level = 0; + int endfmt = 0; + char *formatsave = format; + int i, len; + char *msg; + PyObject *freelist = NULL; + + assert(compat || (args != (PyObject*)NULL)); + + while (endfmt == 0) { + int c = *format++; + switch (c) { + case '(': + if (level == 0) + max++; + level++; + break; + case ')': + if (level == 0) + Py_FatalError("excess ')' in getargs format"); + else + level--; + break; + case '\0': + endfmt = 1; + break; + case ':': + fname = format; + endfmt = 1; + break; + case ';': + message = format; + endfmt = 1; + break; + default: + if (level == 0) { + if (c == 'O') + max++; + else if (isalpha(c)) { + if (c != 'e') /* skip encoded */ + max++; + } else if (c == '|') + min = max; + } + break; + } + } + + if (level != 0) + Py_FatalError(/* '(' */ "missing ')' in getargs format"); + + if (min < 0) + min = max; + + format = formatsave; + + if (compat) { + if (max == 0) { + if (args == NULL) + return 1; + PyOS_snprintf(msgbuf, sizeof(msgbuf), + "%.200s%s takes no arguments", + fname==NULL ? "function" : fname, + fname==NULL ? "" : "()"); + PyErr_SetString(PyExc_TypeError, msgbuf); + return 0; + } + else if (min == 1 && max == 1) { + if (args == NULL) { + PyOS_snprintf(msgbuf, sizeof(msgbuf), + "%.200s%s takes at least one argument", + fname==NULL ? "function" : fname, + fname==NULL ? "" : "()"); + PyErr_SetString(PyExc_TypeError, msgbuf); + return 0; + } + msg = convertitem(args, &format, p_va, levels, msgbuf, + sizeof(msgbuf), &freelist); + if (msg == NULL) + return cleanreturn(1, freelist); + seterror(levels[0], msg, levels+1, fname, message); + return cleanreturn(0, freelist); + } + else { + PyErr_SetString(PyExc_SystemError, + "old style getargs format uses new features"); + return 0; + } + } + + if (!PyTuple_Check(args)) { + PyErr_SetString(PyExc_SystemError, + "new style getargs format but argument is not a tuple"); + return 0; + } + + len = PyTuple_GET_SIZE(args); + + if (len < min || max < len) { + if (message == NULL) { + PyOS_snprintf(msgbuf, sizeof(msgbuf), + "%.150s%s takes %s %d argument%s " + "(%d given)", + fname==NULL ? "function" : fname, + fname==NULL ? "" : "()", + min==max ? "exactly" + : len < min ? "at least" : "at most", + len < min ? min : max, + (len < min ? min : max) == 1 ? "" : "s", + len); + message = msgbuf; + } + PyErr_SetString(PyExc_TypeError, message); + return 0; + } + + for (i = 0; i < len; i++) { + if (*format == '|') + format++; + msg = convertitem(PyTuple_GET_ITEM(args, i), &format, p_va, + levels, msgbuf, sizeof(msgbuf), &freelist); + if (msg) { + seterror(i+1, msg, levels, fname, message); + return cleanreturn(0, freelist); + } + } + + if (*format != '\0' && !isalpha((int)(*format)) && + *format != '(' && + *format != '|' && *format != ':' && *format != ';') { + PyErr_Format(PyExc_SystemError, + "bad format string: %.200s", formatsave); + return cleanreturn(0, freelist); + } + + return cleanreturn(1, freelist); +} + + + +static void +seterror(int iarg, char *msg, int *levels, char *fname, char *message) +{ + char buf[512]; + int i; + char *p = buf; + + if (PyErr_Occurred()) + return; + else if (message == NULL) { + if (fname != NULL) { + PyOS_snprintf(p, sizeof(buf), "%.200s() ", fname); + p += strlen(p); + } + if (iarg != 0) { + PyOS_snprintf(p, sizeof(buf) - (p - buf), + "argument %d", iarg); + i = 0; + p += strlen(p); + while (levels[i] > 0 && (int)(p-buf) < 220) { + PyOS_snprintf(p, sizeof(buf) - (buf - p), + ", item %d", levels[i]-1); + p += strlen(p); + i++; + } + } + else { + PyOS_snprintf(p, sizeof(buf) - (p - buf), "argument"); + p += strlen(p); + } + PyOS_snprintf(p, sizeof(buf) - (p - buf), " %.256s", msg); + message = buf; + } + PyErr_SetString(PyExc_TypeError, message); +} + + +/* Convert a tuple argument. + On entry, *p_format points to the character _after_ the opening '('. + On successful exit, *p_format points to the closing ')'. + If successful: + *p_format and *p_va are updated, + *levels and *msgbuf are untouched, + and NULL is returned. + If the argument is invalid: + *p_format is unchanged, + *p_va is undefined, + *levels is a 0-terminated list of item numbers, + *msgbuf contains an error message, whose format is: + "must be , not ", where: + is the name of the expected type, and + is the name of the actual type, + and msgbuf is returned. +*/ + +static char * +converttuple(PyObject *arg, char **p_format, va_list *p_va, int *levels, + char *msgbuf, size_t bufsize, int toplevel, PyObject **freelist) +{ + int level = 0; + int n = 0; + char *format = *p_format; + int i; + + for (;;) { + int c = *format++; + if (c == '(') { + if (level == 0) + n++; + level++; + } + else if (c == ')') { + if (level == 0) + break; + level--; + } + else if (c == ':' || c == ';' || c == '\0') + break; + else if (level == 0 && isalpha(c)) + n++; + } + + if (!PySequence_Check(arg) || PyString_Check(arg)) { + levels[0] = 0; + PyOS_snprintf(msgbuf, bufsize, + toplevel ? "expected %d arguments, not %.50s" : + "must be %d-item sequence, not %.50s", + n, + arg == Py_None ? "None" : arg->ob_type->tp_name); + return msgbuf; + } + + if ((i = PySequence_Size(arg)) != n) { + levels[0] = 0; + PyOS_snprintf(msgbuf, bufsize, + toplevel ? "expected %d arguments, not %d" : + "must be sequence of length %d, not %d", + n, i); + return msgbuf; + } + + format = *p_format; + for (i = 0; i < n; i++) { + char *msg; + PyObject *item; + item = PySequence_GetItem(arg, i); + msg = convertitem(item, &format, p_va, levels+1, msgbuf, + bufsize, freelist); + /* PySequence_GetItem calls tp->sq_item, which INCREFs */ + Py_XDECREF(item); + if (msg != NULL) { + levels[0] = i+1; + return msg; + } + } + + *p_format = format; + return NULL; +} + + +/* Convert a single item. */ + +static char * +convertitem(PyObject *arg, char **p_format, va_list *p_va, int *levels, + char *msgbuf, size_t bufsize, PyObject **freelist) +{ + char *msg; + char *format = *p_format; + + if (*format == '(' /* ')' */) { + format++; + msg = converttuple(arg, &format, p_va, levels, msgbuf, + bufsize, 0, freelist); + if (msg == NULL) + format++; + } + else { + msg = convertsimple(arg, &format, p_va, msgbuf, bufsize, + freelist); + if (msg != NULL) + levels[0] = 0; + } + if (msg == NULL) + *p_format = format; + return msg; +} + + + +#define UNICODE_DEFAULT_ENCODING(arg) \ + _PyUnicode_AsDefaultEncodedString(arg, NULL) + +/* Format an error message generated by convertsimple(). */ + +static char * +converterr(char *expected, PyObject *arg, char *msgbuf, size_t bufsize) +{ + assert(expected != NULL); + assert(arg != NULL); + PyOS_snprintf(msgbuf, bufsize, + "must be %.50s, not %.50s", expected, + arg == Py_None ? "None" : arg->ob_type->tp_name); + return msgbuf; +} + +#define CONV_UNICODE "(unicode conversion error)" + +/* explicitly check for float arguments when integers are expected. For now + * signal a warning. Returns true if an exception was raised. */ +static int +float_argument_error(PyObject *arg) +{ + if (PyFloat_Check(arg) && + PyErr_Warn(PyExc_DeprecationWarning, + "integer argument expected, got float" )) + return 1; + else + return 0; +} + +/* Convert a non-tuple argument. Return NULL if conversion went OK, + or a string with a message describing the failure. The message is + formatted as "must be , not ". + When failing, an exception may or may not have been raised. + Don't call if a tuple is expected. +*/ + +static char * +convertsimple(PyObject *arg, char **p_format, va_list *p_va, char *msgbuf, + size_t bufsize, PyObject **freelist) +{ + char *format = *p_format; + char c = *format++; +#ifdef Py_USING_UNICODE + PyObject *uarg; +#endif + + switch (c) { + + case 'b': { /* unsigned byte -- very short int */ + char *p = va_arg(*p_va, char *); + long ival; + if (float_argument_error(arg)) + return NULL; + ival = PyInt_AsLong(arg); + if (ival == -1 && PyErr_Occurred()) + return converterr("integer", arg, msgbuf, bufsize); + else if (ival < 0) { + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is less than minimum"); + return converterr("integer", arg, msgbuf, bufsize); + } + else if (ival > UCHAR_MAX) { + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is greater than maximum"); + return converterr("integer", arg, msgbuf, bufsize); + } + else + *p = (unsigned char) ival; + break; + } + + case 'B': {/* byte sized bitfield - both signed and unsigned + values allowed */ + char *p = va_arg(*p_va, char *); + long ival; + if (float_argument_error(arg)) + return NULL; + ival = PyInt_AsUnsignedLongMask(arg); + if (ival == -1 && PyErr_Occurred()) + return converterr("integer", arg, msgbuf, bufsize); + else + *p = (unsigned char) ival; + break; + } + + case 'h': {/* signed short int */ + short *p = va_arg(*p_va, short *); + long ival; + if (float_argument_error(arg)) + return NULL; + ival = PyInt_AsLong(arg); + if (ival == -1 && PyErr_Occurred()) + return converterr("integer", arg, msgbuf, bufsize); + else if (ival < SHRT_MIN) { + PyErr_SetString(PyExc_OverflowError, + "signed short integer is less than minimum"); + return converterr("integer", arg, msgbuf, bufsize); + } + else if (ival > SHRT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "signed short integer is greater than maximum"); + return converterr("integer", arg, msgbuf, bufsize); + } + else + *p = (short) ival; + break; + } + + case 'H': { /* short int sized bitfield, both signed and + unsigned allowed */ + unsigned short *p = va_arg(*p_va, unsigned short *); + long ival; + if (float_argument_error(arg)) + return NULL; + ival = PyInt_AsUnsignedLongMask(arg); + if (ival == -1 && PyErr_Occurred()) + return converterr("integer", arg, msgbuf, bufsize); + else + *p = (unsigned short) ival; + break; + } + + case 'i': {/* signed int */ + int *p = va_arg(*p_va, int *); + long ival; + if (float_argument_error(arg)) + return NULL; + ival = PyInt_AsLong(arg); + if (ival == -1 && PyErr_Occurred()) + return converterr("integer", arg, msgbuf, bufsize); + else if (ival > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "signed integer is greater than maximum"); + return converterr("integer", arg, msgbuf, bufsize); + } + else if (ival < INT_MIN) { + PyErr_SetString(PyExc_OverflowError, + "signed integer is less than minimum"); + return converterr("integer", arg, msgbuf, bufsize); + } + else + *p = ival; + break; + } + + case 'I': { /* int sized bitfield, both signed and + unsigned allowed */ + unsigned int *p = va_arg(*p_va, unsigned int *); + unsigned int ival; + if (float_argument_error(arg)) + return NULL; + ival = PyInt_AsUnsignedLongMask(arg); + if (ival == -1 && PyErr_Occurred()) + return converterr("integer", arg, msgbuf, bufsize); + else + *p = ival; + break; + } + + case 'l': {/* long int */ + long *p = va_arg(*p_va, long *); + long ival; + if (float_argument_error(arg)) + return NULL; + ival = PyInt_AsLong(arg); + if (ival == -1 && PyErr_Occurred()) + return converterr("integer", arg, msgbuf, bufsize); + else + *p = ival; + break; + } + + case 'k': { /* long sized bitfield */ + unsigned long *p = va_arg(*p_va, unsigned long *); + unsigned long ival; + if (PyInt_Check(arg)) + ival = PyInt_AsUnsignedLongMask(arg); + else if (PyLong_Check(arg)) + ival = PyLong_AsUnsignedLongMask(arg); + else + return converterr("integer", arg, msgbuf, bufsize); + *p = ival; + break; + } + +#ifdef HAVE_LONG_LONG + case 'L': {/* PY_LONG_LONG */ + PY_LONG_LONG *p = va_arg( *p_va, PY_LONG_LONG * ); + PY_LONG_LONG ival = PyLong_AsLongLong( arg ); + if( ival == (PY_LONG_LONG)-1 && PyErr_Occurred() ) { + return converterr("long", arg, msgbuf, bufsize); + } else { + *p = ival; + } + break; + } + + case 'K': { /* long long sized bitfield */ + unsigned PY_LONG_LONG *p = va_arg(*p_va, unsigned PY_LONG_LONG *); + unsigned PY_LONG_LONG ival; + if (float_argument_error(arg)) + return NULL; + if (PyInt_Check(arg)) + ival = PyInt_AsUnsignedLongMask(arg); + else if (PyLong_Check(arg)) + ival = PyLong_AsUnsignedLongLongMask(arg); + else + return converterr("integer", arg, msgbuf, bufsize); + *p = ival; + break; + } +#endif + + case 'f': {/* float */ + float *p = va_arg(*p_va, float *); + double dval = PyFloat_AsDouble(arg); + if (PyErr_Occurred()) + return converterr("float", arg, msgbuf, bufsize); + else + *p = (float) dval; + break; + } + + case 'd': {/* double */ + double *p = va_arg(*p_va, double *); + double dval = PyFloat_AsDouble(arg); + if (PyErr_Occurred()) + return converterr("float", arg, msgbuf, bufsize); + else + *p = dval; + break; + } + +#ifndef WITHOUT_COMPLEX + case 'D': {/* complex double */ + Py_complex *p = va_arg(*p_va, Py_complex *); + Py_complex cval; + cval = PyComplex_AsCComplex(arg); + if (PyErr_Occurred()) + return converterr("complex", arg, msgbuf, bufsize); + else + *p = cval; + break; + } +#endif /* WITHOUT_COMPLEX */ + + case 'c': {/* char */ + char *p = va_arg(*p_va, char *); + if (PyString_Check(arg) && PyString_Size(arg) == 1) + *p = PyString_AS_STRING(arg)[0]; + else + return converterr("char", arg, msgbuf, bufsize); + break; + } + + case 's': {/* string */ + if (*format == '#') { + void **p = (void **)va_arg(*p_va, char **); + int *q = va_arg(*p_va, int *); + + if (PyString_Check(arg)) { + *p = PyString_AS_STRING(arg); + *q = PyString_GET_SIZE(arg); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(arg)) { + uarg = UNICODE_DEFAULT_ENCODING(arg); + if (uarg == NULL) + return converterr(CONV_UNICODE, + arg, msgbuf, bufsize); + *p = PyString_AS_STRING(uarg); + *q = PyString_GET_SIZE(uarg); + } +#endif + else { /* any buffer-like object */ + char *buf; + int count = convertbuffer(arg, p, &buf); + if (count < 0) + return converterr(buf, arg, msgbuf, bufsize); + *q = count; + } + format++; + } else { + char **p = va_arg(*p_va, char **); + + if (PyString_Check(arg)) + *p = PyString_AS_STRING(arg); +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(arg)) { + uarg = UNICODE_DEFAULT_ENCODING(arg); + if (uarg == NULL) + return converterr(CONV_UNICODE, + arg, msgbuf, bufsize); + *p = PyString_AS_STRING(uarg); + } +#endif + else + return converterr("string", arg, msgbuf, bufsize); + if ((int)strlen(*p) != PyString_Size(arg)) + return converterr("string without null bytes", + arg, msgbuf, bufsize); + } + break; + } + + case 'z': {/* string, may be NULL (None) */ + if (*format == '#') { /* any buffer-like object */ + void **p = (void **)va_arg(*p_va, char **); + int *q = va_arg(*p_va, int *); + + if (arg == Py_None) { + *p = 0; + *q = 0; + } + else if (PyString_Check(arg)) { + *p = PyString_AS_STRING(arg); + *q = PyString_GET_SIZE(arg); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(arg)) { + uarg = UNICODE_DEFAULT_ENCODING(arg); + if (uarg == NULL) + return converterr(CONV_UNICODE, + arg, msgbuf, bufsize); + *p = PyString_AS_STRING(uarg); + *q = PyString_GET_SIZE(uarg); + } +#endif + else { /* any buffer-like object */ + char *buf; + int count = convertbuffer(arg, p, &buf); + if (count < 0) + return converterr(buf, arg, msgbuf, bufsize); + *q = count; + } + format++; + } else { + char **p = va_arg(*p_va, char **); + + if (arg == Py_None) + *p = 0; + else if (PyString_Check(arg)) + *p = PyString_AS_STRING(arg); +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(arg)) { + uarg = UNICODE_DEFAULT_ENCODING(arg); + if (uarg == NULL) + return converterr(CONV_UNICODE, + arg, msgbuf, bufsize); + *p = PyString_AS_STRING(uarg); + } +#endif + else + return converterr("string or None", + arg, msgbuf, bufsize); + if (*format == '#') { + int *q = va_arg(*p_va, int *); + if (arg == Py_None) + *q = 0; + else + *q = PyString_Size(arg); + format++; + } + else if (*p != NULL && + (int)strlen(*p) != PyString_Size(arg)) + return converterr( + "string without null bytes or None", + arg, msgbuf, bufsize); + } + break; + } + + case 'e': {/* encoded string */ + char **buffer; + const char *encoding; + PyObject *s; + int size, recode_strings; + + /* Get 'e' parameter: the encoding name */ + encoding = (const char *)va_arg(*p_va, const char *); +#ifdef Py_USING_UNICODE + if (encoding == NULL) + encoding = PyUnicode_GetDefaultEncoding(); +#endif + + /* Get output buffer parameter: + 's' (recode all objects via Unicode) or + 't' (only recode non-string objects) + */ + if (*format == 's') + recode_strings = 1; + else if (*format == 't') + recode_strings = 0; + else + return converterr( + "(unknown parser marker combination)", + arg, msgbuf, bufsize); + buffer = (char **)va_arg(*p_va, char **); + format++; + if (buffer == NULL) + return converterr("(buffer is NULL)", + arg, msgbuf, bufsize); + + /* Encode object */ + if (!recode_strings && PyString_Check(arg)) { + s = arg; + Py_INCREF(s); + } + else { +#ifdef Py_USING_UNICODE + PyObject *u; + + /* Convert object to Unicode */ + u = PyUnicode_FromObject(arg); + if (u == NULL) + return converterr( + "string or unicode or text buffer", + arg, msgbuf, bufsize); + + /* Encode object; use default error handling */ + s = PyUnicode_AsEncodedString(u, + encoding, + NULL); + Py_DECREF(u); + if (s == NULL) + return converterr("(encoding failed)", + arg, msgbuf, bufsize); + if (!PyString_Check(s)) { + Py_DECREF(s); + return converterr( + "(encoder failed to return a string)", + arg, msgbuf, bufsize); + } +#else + return converterr("string", arg, msgbuf, bufsize); +#endif + } + size = PyString_GET_SIZE(s); + + /* Write output; output is guaranteed to be 0-terminated */ + if (*format == '#') { + /* Using buffer length parameter '#': + + - if *buffer is NULL, a new buffer of the + needed size is allocated and the data + copied into it; *buffer is updated to point + to the new buffer; the caller is + responsible for PyMem_Free()ing it after + usage + + - if *buffer is not NULL, the data is + copied to *buffer; *buffer_len has to be + set to the size of the buffer on input; + buffer overflow is signalled with an error; + buffer has to provide enough room for the + encoded string plus the trailing 0-byte + + - in both cases, *buffer_len is updated to + the size of the buffer /excluding/ the + trailing 0-byte + + */ + int *buffer_len = va_arg(*p_va, int *); + + format++; + if (buffer_len == NULL) { + Py_DECREF(s); + return converterr( + "(buffer_len is NULL)", + arg, msgbuf, bufsize); + } + if (*buffer == NULL) { + *buffer = PyMem_NEW(char, size + 1); + if (*buffer == NULL) { + Py_DECREF(s); + return converterr( + "(memory error)", + arg, msgbuf, bufsize); + } + if(addcleanup(*buffer, freelist)) { + Py_DECREF(s); + return converterr( + "(cleanup problem)", + arg, msgbuf, bufsize); + } + } else { + if (size + 1 > *buffer_len) { + Py_DECREF(s); + return converterr( + "(buffer overflow)", + arg, msgbuf, bufsize); + } + } + memcpy(*buffer, + PyString_AS_STRING(s), + size + 1); + *buffer_len = size; + } else { + /* Using a 0-terminated buffer: + + - the encoded string has to be 0-terminated + for this variant to work; if it is not, an + error raised + + - a new buffer of the needed size is + allocated and the data copied into it; + *buffer is updated to point to the new + buffer; the caller is responsible for + PyMem_Free()ing it after usage + + */ + if ((int)strlen(PyString_AS_STRING(s)) != size) { + Py_DECREF(s); + return converterr( + "(encoded string without NULL bytes)", + arg, msgbuf, bufsize); + } + *buffer = PyMem_NEW(char, size + 1); + if (*buffer == NULL) { + Py_DECREF(s); + return converterr("(memory error)", + arg, msgbuf, bufsize); + } + if(addcleanup(*buffer, freelist)) { + Py_DECREF(s); + return converterr("(cleanup problem)", + arg, msgbuf, bufsize); + } + memcpy(*buffer, + PyString_AS_STRING(s), + size + 1); + } + Py_DECREF(s); + break; + } + +#ifdef Py_USING_UNICODE + case 'u': {/* raw unicode buffer (Py_UNICODE *) */ + if (*format == '#') { /* any buffer-like object */ + void **p = (void **)va_arg(*p_va, char **); + int *q = va_arg(*p_va, int *); + if (PyUnicode_Check(arg)) { + *p = PyUnicode_AS_UNICODE(arg); + *q = PyUnicode_GET_SIZE(arg); + } + else { + char *buf; + int count = convertbuffer(arg, p, &buf); + if (count < 0) + return converterr(buf, arg, msgbuf, bufsize); + *q = count/(sizeof(Py_UNICODE)); + } + format++; + } else { + Py_UNICODE **p = va_arg(*p_va, Py_UNICODE **); + if (PyUnicode_Check(arg)) + *p = PyUnicode_AS_UNICODE(arg); + else + return converterr("unicode", arg, msgbuf, bufsize); + } + break; + } +#endif + + case 'S': { /* string object */ + PyObject **p = va_arg(*p_va, PyObject **); + if (PyString_Check(arg)) + *p = arg; + else + return converterr("string", arg, msgbuf, bufsize); + break; + } + +#ifdef Py_USING_UNICODE + case 'U': { /* Unicode object */ + PyObject **p = va_arg(*p_va, PyObject **); + if (PyUnicode_Check(arg)) + *p = arg; + else + return converterr("unicode", arg, msgbuf, bufsize); + break; + } +#endif + + case 'O': { /* object */ + PyTypeObject *type; + PyObject **p; + if (*format == '!') { + type = va_arg(*p_va, PyTypeObject*); + p = va_arg(*p_va, PyObject **); + format++; + if (PyType_IsSubtype(arg->ob_type, type)) + *p = arg; + else + return converterr(type->tp_name, arg, msgbuf, bufsize); + + } + else if (*format == '?') { + inquiry pred = va_arg(*p_va, inquiry); + p = va_arg(*p_va, PyObject **); + format++; + if ((*pred)(arg)) + *p = arg; + else + return converterr("(unspecified)", + arg, msgbuf, bufsize); + + } + else if (*format == '&') { + typedef int (*converter)(PyObject *, void *); + converter convert = va_arg(*p_va, converter); + void *addr = va_arg(*p_va, void *); + format++; + if (! (*convert)(arg, addr)) + return converterr("(unspecified)", + arg, msgbuf, bufsize); + } + else { + p = va_arg(*p_va, PyObject **); + *p = arg; + } + break; + } + + + case 'w': { /* memory buffer, read-write access */ + void **p = va_arg(*p_va, void **); + PyBufferProcs *pb = arg->ob_type->tp_as_buffer; + int count; + + if (pb == NULL || + pb->bf_getwritebuffer == NULL || + pb->bf_getsegcount == NULL) + return converterr("read-write buffer", arg, msgbuf, bufsize); + if ((*pb->bf_getsegcount)(arg, NULL) != 1) + return converterr("single-segment read-write buffer", + arg, msgbuf, bufsize); + if ((count = pb->bf_getwritebuffer(arg, 0, p)) < 0) + return converterr("(unspecified)", arg, msgbuf, bufsize); + if (*format == '#') { + int *q = va_arg(*p_va, int *); + + *q = count; + format++; + } + break; + } + + case 't': { /* 8-bit character buffer, read-only access */ + const char **p = va_arg(*p_va, const char **); + PyBufferProcs *pb = arg->ob_type->tp_as_buffer; + int count; + + if (*format++ != '#') + return converterr( + "invalid use of 't' format character", + arg, msgbuf, bufsize); + if (!PyType_HasFeature(arg->ob_type, + Py_TPFLAGS_HAVE_GETCHARBUFFER) || + pb == NULL || pb->bf_getcharbuffer == NULL || + pb->bf_getsegcount == NULL) + return converterr( + "string or read-only character buffer", + arg, msgbuf, bufsize); + + if (pb->bf_getsegcount(arg, NULL) != 1) + return converterr( + "string or single-segment read-only buffer", + arg, msgbuf, bufsize); + + count = pb->bf_getcharbuffer(arg, 0, p); + if (count < 0) + return converterr("(unspecified)", arg, msgbuf, bufsize); + *va_arg(*p_va, int *) = count; + break; + } + + default: + return converterr("impossible", arg, msgbuf, bufsize); + + } + + *p_format = format; + return NULL; +} + +static int +convertbuffer(PyObject *arg, void **p, char **errmsg) +{ + PyBufferProcs *pb = arg->ob_type->tp_as_buffer; + int count; + if (pb == NULL || + pb->bf_getreadbuffer == NULL || + pb->bf_getsegcount == NULL) { + *errmsg = "string or read-only buffer"; + return -1; + } + if ((*pb->bf_getsegcount)(arg, NULL) != 1) { + *errmsg = "string or single-segment read-only buffer"; + return -1; + } + if ((count = (*pb->bf_getreadbuffer)(arg, 0, p)) < 0) { + *errmsg = "(unspecified)"; + } + return count; +} + +/* Support for keyword arguments donated by + Geoff Philbrick */ + +/* Return false (0) for error, else true. */ +int +PyArg_ParseTupleAndKeywords(PyObject *args, + PyObject *keywords, + char *format, + char **kwlist, ...) +{ + int retval; + va_list va; + + if ((args == NULL || !PyTuple_Check(args)) || + (keywords != NULL && !PyDict_Check(keywords)) || + format == NULL || + kwlist == NULL) + { + PyErr_BadInternalCall(); + return 0; + } + + va_start(va, kwlist); + retval = vgetargskeywords(args, keywords, format, kwlist, &va); + va_end(va); + return retval; +} + + +static int +vgetargskeywords(PyObject *args, PyObject *keywords, char *format, + char **kwlist, va_list *p_va) +{ + char msgbuf[512]; + int levels[32]; + char *fname, *message; + int min, max; + char *formatsave; + int i, len, nargs, nkeywords; + char *msg, **p; + PyObject *freelist = NULL; + + assert(args != NULL && PyTuple_Check(args)); + assert(keywords == NULL || PyDict_Check(keywords)); + assert(format != NULL); + assert(kwlist != NULL); + assert(p_va != NULL); + + /* Search the format: + message <- error msg, if any (else NULL). + fname <- routine name, if any (else NULL). + min <- # of required arguments, or -1 if all are required. + max <- most arguments (required + optional). + Check that kwlist has a non-NULL entry for each arg. + Raise error if a tuple arg spec is found. + */ + fname = message = NULL; + formatsave = format; + p = kwlist; + min = -1; + max = 0; + while ((i = *format++) != '\0') { + if (isalpha(i) && i != 'e') { + max++; + if (*p == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "more argument specifiers than " + "keyword list entries"); + return 0; + } + p++; + } + else if (i == '|') + min = max; + else if (i == ':') { + fname = format; + break; + } + else if (i == ';') { + message = format; + break; + } + else if (i == '(') { + PyErr_SetString(PyExc_RuntimeError, + "tuple found in format when using keyword " + "arguments"); + return 0; + } + } + format = formatsave; + if (*p != NULL) { + PyErr_SetString(PyExc_RuntimeError, + "more keyword list entries than " + "argument specifiers"); + return 0; + } + if (min < 0) { + /* All arguments are required. */ + min = max; + } + + nargs = PyTuple_GET_SIZE(args); + nkeywords = keywords == NULL ? 0 : PyDict_Size(keywords); + + /* make sure there are no duplicate values for an argument; + its not clear when to use the term "keyword argument vs. + keyword parameter in messages */ + if (nkeywords > 0) { + for (i = 0; i < nargs; i++) { + char *thiskw = kwlist[i]; + if (thiskw == NULL) + break; + if (PyDict_GetItemString(keywords, thiskw)) { + PyErr_Format(PyExc_TypeError, + "keyword parameter '%s' was given " + "by position and by name", + thiskw); + return 0; + } + else if (PyErr_Occurred()) + return 0; + } + } + + /* required arguments missing from args can be supplied by keyword + arguments; set len to the number of posiitional arguments, and, + if that's less than the minimum required, add in the number of + required arguments that are supplied by keywords */ + len = nargs; + if (nkeywords > 0 && nargs < min) { + for (i = nargs; i < min; i++) { + if (PyDict_GetItemString(keywords, kwlist[i])) + len++; + else if (PyErr_Occurred()) + return 0; + } + } + + /* make sure we got an acceptable number of arguments; the message + is a little confusing with keywords since keyword arguments + which are supplied, but don't match the required arguments + are not included in the "%d given" part of the message */ + if (len < min || max < len) { + if (message == NULL) { + PyOS_snprintf(msgbuf, sizeof(msgbuf), + "%.200s%s takes %s %d argument%s " + "(%d given)", + fname==NULL ? "function" : fname, + fname==NULL ? "" : "()", + min==max ? "exactly" + : len < min ? "at least" : "at most", + len < min ? min : max, + (len < min ? min : max) == 1 ? "" : "s", + len); + message = msgbuf; + } + PyErr_SetString(PyExc_TypeError, message); + return 0; + } + + /* convert the positional arguments */ + for (i = 0; i < nargs; i++) { + if (*format == '|') + format++; + msg = convertitem(PyTuple_GET_ITEM(args, i), &format, p_va, + levels, msgbuf, sizeof(msgbuf), &freelist); + if (msg) { + seterror(i+1, msg, levels, fname, message); + return cleanreturn(0, freelist); + } + } + + /* handle no keyword parameters in call */ + if (nkeywords == 0) + return cleanreturn(1, freelist); + + /* convert the keyword arguments; this uses the format + string where it was left after processing args */ + for (i = nargs; i < max; i++) { + PyObject *item; + if (*format == '|') + format++; + item = PyDict_GetItemString(keywords, kwlist[i]); + if (item != NULL) { + Py_INCREF(item); + msg = convertitem(item, &format, p_va, levels, msgbuf, + sizeof(msgbuf), &freelist); + Py_DECREF(item); + if (msg) { + seterror(i+1, msg, levels, fname, message); + return cleanreturn(0, freelist); + } + --nkeywords; + if (nkeywords == 0) + break; + } + else if (PyErr_Occurred()) + return cleanreturn(0, freelist); + else { + msg = skipitem(&format, p_va); + if (msg) { + seterror(i+1, msg, levels, fname, message); + return cleanreturn(0, freelist); + } + } + } + + /* make sure there are no extraneous keyword arguments */ + if (nkeywords > 0) { + PyObject *key, *value; + int pos = 0; + while (PyDict_Next(keywords, &pos, &key, &value)) { + int match = 0; + char *ks; + if (!PyString_Check(key)) { + PyErr_SetString(PyExc_TypeError, + "keywords must be strings"); + return cleanreturn(0, freelist); + } + ks = PyString_AsString(key); + for (i = 0; i < max; i++) { + if (!strcmp(ks, kwlist[i])) { + match = 1; + break; + } + } + if (!match) { + PyErr_Format(PyExc_TypeError, + "'%s' is an invalid keyword " + "argument for this function", + ks); + return cleanreturn(0, freelist); + } + } + } + + return cleanreturn(1, freelist); +} + + +static char * +skipitem(char **p_format, va_list *p_va) +{ + char *format = *p_format; + char c = *format++; + + switch (c) { + + case 'b': /* byte -- very short int */ + case 'B': /* byte as bitfield */ + { + (void) va_arg(*p_va, char *); + break; + } + + case 'h': /* short int */ + { + (void) va_arg(*p_va, short *); + break; + } + + case 'H': /* short int as bitfield */ + { + (void) va_arg(*p_va, unsigned short *); + break; + } + + case 'i': /* int */ + { + (void) va_arg(*p_va, int *); + break; + } + + case 'l': /* long int */ + { + (void) va_arg(*p_va, long *); + break; + } + +#ifdef HAVE_LONG_LONG + case 'L': /* PY_LONG_LONG int */ + { + (void) va_arg(*p_va, PY_LONG_LONG *); + break; + } +#endif + + case 'f': /* float */ + { + (void) va_arg(*p_va, float *); + break; + } + + case 'd': /* double */ + { + (void) va_arg(*p_va, double *); + break; + } + +#ifndef WITHOUT_COMPLEX + case 'D': /* complex double */ + { + (void) va_arg(*p_va, Py_complex *); + break; + } +#endif /* WITHOUT_COMPLEX */ + + case 'c': /* char */ + { + (void) va_arg(*p_va, char *); + break; + } + + case 's': /* string */ + { + (void) va_arg(*p_va, char **); + if (*format == '#') { + (void) va_arg(*p_va, int *); + format++; + } + break; + } + + case 'z': /* string */ + { + (void) va_arg(*p_va, char **); + if (*format == '#') { + (void) va_arg(*p_va, int *); + format++; + } + break; + } + + case 'S': /* string object */ + { + (void) va_arg(*p_va, PyObject **); + break; + } + + case 'O': /* object */ + { + if (*format == '!') { + format++; + (void) va_arg(*p_va, PyTypeObject*); + (void) va_arg(*p_va, PyObject **); + } +#if 0 +/* I don't know what this is for */ + else if (*format == '?') { + inquiry pred = va_arg(*p_va, inquiry); + format++; + if ((*pred)(arg)) { + (void) va_arg(*p_va, PyObject **); + } + } +#endif + else if (*format == '&') { + typedef int (*converter)(PyObject *, void *); + (void) va_arg(*p_va, converter); + (void) va_arg(*p_va, void *); + format++; + } + else { + (void) va_arg(*p_va, PyObject **); + } + break; + } + + default: + return "impossible"; + + } + + *p_format = format; + return NULL; +} + + +int +PyArg_UnpackTuple(PyObject *args, char *name, int min, int max, ...) +{ + int i, l; + PyObject **o; + va_list vargs; + +#ifdef HAVE_STDARG_PROTOTYPES + va_start(vargs, max); +#else + va_start(vargs); +#endif + + assert(min >= 0); + assert(min <= max); + if (!PyTuple_Check(args)) { + PyErr_SetString(PyExc_SystemError, + "PyArg_UnpackTuple() argument list is not a tuple"); + return 0; + } + l = PyTuple_GET_SIZE(args); + if (l < min) { + if (name != NULL) + PyErr_Format( + PyExc_TypeError, + "%s expected %s%d arguments, got %d", + name, (min == max ? "" : "at least "), min, l); + else + PyErr_Format( + PyExc_TypeError, + "unpacked tuple should have %s%d elements," + " but has %d", + (min == max ? "" : "at least "), min, l); + va_end(vargs); + return 0; + } + if (l > max) { + if (name != NULL) + PyErr_Format( + PyExc_TypeError, + "%s expected %s%d arguments, got %d", + name, (min == max ? "" : "at most "), max, l); + else + PyErr_Format( + PyExc_TypeError, + "unpacked tuple should have %s%d elements," + " but has %d", + (min == max ? "" : "at most "), max, l); + va_end(vargs); + return 0; + } + for (i = 0; i < l; i++) { + o = va_arg(vargs, PyObject **); + *o = PyTuple_GET_ITEM(args, i); + } + va_end(vargs); + return 1; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcompiler.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcompiler.c new file mode 100644 index 00000000..33a7639a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcompiler.c @@ -0,0 +1,28 @@ + +/* Return the compiler identification, if possible. */ + +#include "Python.h" + +#ifndef COMPILER + +#ifdef __GNUC__ +#define COMPILER "\n[GCC " __VERSION__ "]" +#endif + +#endif /* !COMPILER */ + +#ifndef COMPILER + +#ifdef __cplusplus +#define COMPILER "[C++]" +#else +#define COMPILER "[C]" +#endif + +#endif /* !COMPILER */ + +const char * +Py_GetCompiler(void) +{ + return COMPILER; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcopyright.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcopyright.c new file mode 100644 index 00000000..e60c57ca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcopyright.c @@ -0,0 +1,23 @@ +/* Return the copyright string. This is updated manually. */ + +#include "Python.h" + +static char cprt[] = +"\ +Copyright (c) 2001, 2002, 2003 Python Software Foundation.\n\ +All Rights Reserved.\n\ +\n\ +Copyright (c) 2000 BeOpen.com.\n\ +All Rights Reserved.\n\ +\n\ +Copyright (c) 1995-2001 Corporation for National Research Initiatives.\n\ +All Rights Reserved.\n\ +\n\ +Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.\n\ +All Rights Reserved."; + +const char * +Py_GetCopyright(void) +{ + return cprt; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcwd.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcwd.c new file mode 100644 index 00000000..c53d8e38 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getcwd.c @@ -0,0 +1,79 @@ + +/* Two PD getcwd() implementations. + Author: Guido van Rossum, CWI Amsterdam, Jan 1991, . */ + +#include +#include + +#ifdef HAVE_GETWD + +/* Version for BSD systems -- use getwd() */ + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +extern char *getwd(char *); + +char * +getcwd(char *buf, int size) +{ + char localbuf[MAXPATHLEN+1]; + char *ret; + + if (size <= 0) { + errno = EINVAL; + return NULL; + } + ret = getwd(localbuf); + if (ret != NULL && strlen(localbuf) >= (size_t)size) { + errno = ERANGE; + return NULL; + } + if (ret == NULL) { + errno = EACCES; /* Most likely error */ + return NULL; + } + strncpy(buf, localbuf, size); + return buf; +} + +#else /* !HAVE_GETWD */ + +/* Version for really old UNIX systems -- use pipe from pwd */ + +#ifndef PWD_CMD +#define PWD_CMD "/bin/pwd" +#endif + +char * +getcwd(char *buf, int size) +{ + FILE *fp; + char *p; + int sts; + if (size <= 0) { + errno = EINVAL; + return NULL; + } + if ((fp = popen(PWD_CMD, "r")) == NULL) + return NULL; + if (fgets(buf, size, fp) == NULL || (sts = pclose(fp)) != 0) { + errno = EACCES; /* Most likely error */ + return NULL; + } + for (p = buf; *p != '\n'; p++) { + if (*p == '\0') { + errno = ERANGE; + return NULL; + } + } + *p = '\0'; + return buf; +} + +#endif /* !HAVE_GETWD */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getmtime.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getmtime.c new file mode 100644 index 00000000..0c1eea21 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getmtime.c @@ -0,0 +1,21 @@ + +/* Subroutine to get the last modification time of a file */ + +/* (A separate file because this may be OS dependent) */ + +#include "Python.h" +#include "pyconfig.h" + +time_t +PyOS_GetLastModificationTime(char *path, FILE *fp) +{ + struct stat st; + /*if (fstat(fileno(fp), &st) != 0) + return -1; + else + return st.st_mtime;*/ + if (stat(path, &st) != 0) // use stat because fstat breaks in debug (end result should be identical) + return -1; + else + return st.st_mtime; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getopt.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getopt.c new file mode 100644 index 00000000..2401a553 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getopt.c @@ -0,0 +1,83 @@ +/*---------------------------------------------------------------------------* + * + * + * C++ Library + * + * Copyright 1992-1994, David Gottner + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice, this permission notice and + * the following disclaimer notice appear unmodified in all copies. + * + * I DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL I + * BE LIABLE FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Nevertheless, I would like to know about bugs in this library or + * suggestions for improvment. Send bug reports and feedback to + * davegottner@delphi.com. + *---------------------------------------------------------------------------*/ + +#include +#include + +int _PyOS_opterr = 1; /* generate error messages */ +int _PyOS_optind = 1; /* index into argv array */ +char *_PyOS_optarg = NULL; /* optional argument */ + +int _PyOS_GetOpt(int argc, char **argv, char *optstring) +{ + static char *opt_ptr = ""; + char *ptr; + int option; + + if (*opt_ptr == '\0') { + + if (_PyOS_optind >= argc || argv[_PyOS_optind][0] != '-' || + argv[_PyOS_optind][1] == '\0' /* lone dash */ ) + return -1; + + else if (strcmp(argv[_PyOS_optind], "--") == 0) { + ++_PyOS_optind; + return -1; + } + + opt_ptr = &argv[_PyOS_optind++][1]; + } + + if ( (option = *opt_ptr++) == '\0') + return -1; + + if ((ptr = strchr(optstring, option)) == NULL) { + if (_PyOS_opterr) + fprintf(stderr, "Unknown option: -%c\n", option); + + return '?'; + } + + if (*(ptr + 1) == ':') { + if (*opt_ptr != '\0') { + _PyOS_optarg = opt_ptr; + opt_ptr = ""; + } + + else { + if (_PyOS_optind >= argc) { + if (_PyOS_opterr) + fprintf(stderr, + "Argument expected for the -%c option\n", option); + return '?'; + } + + _PyOS_optarg = argv[_PyOS_optind++]; + } + } + + return option; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getplatform.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getplatform.c new file mode 100644 index 00000000..ce79ffe2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getplatform.c @@ -0,0 +1,12 @@ + +#include "Python.h" + +#ifndef PLATFORM +#define PLATFORM "unknown" +#endif + +const char * +Py_GetPlatform(void) +{ + return PLATFORM; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getversion.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getversion.c new file mode 100644 index 00000000..7c75348f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/getversion.c @@ -0,0 +1,15 @@ + +/* Return the full version string. */ + +#include "Python.h" + +#include "patchlevel.h" + +const char * +Py_GetVersion(void) +{ + static char version[250]; + PyOS_snprintf(version, sizeof(version), "%.80s (%.80s) %.80s", + PY_VERSION, Py_GetBuildInfo(), Py_GetCompiler()); + return version; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/graminit.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/graminit.c new file mode 100644 index 00000000..d951abc3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/graminit.c @@ -0,0 +1,1770 @@ +#include "pgenheaders.h" +#include "grammar.h" +static arc arcs_0_0[3] = { + {2, 1}, + {3, 1}, + {4, 2}, +}; +static arc arcs_0_1[1] = { + {0, 1}, +}; +static arc arcs_0_2[1] = { + {2, 1}, +}; +static state states_0[3] = { + {3, arcs_0_0}, + {1, arcs_0_1}, + {1, arcs_0_2}, +}; +static arc arcs_1_0[3] = { + {2, 0}, + {6, 0}, + {7, 1}, +}; +static arc arcs_1_1[1] = { + {0, 1}, +}; +static state states_1[2] = { + {3, arcs_1_0}, + {1, arcs_1_1}, +}; +static arc arcs_2_0[1] = { + {9, 1}, +}; +static arc arcs_2_1[2] = { + {2, 1}, + {7, 2}, +}; +static arc arcs_2_2[1] = { + {0, 2}, +}; +static state states_2[3] = { + {1, arcs_2_0}, + {2, arcs_2_1}, + {1, arcs_2_2}, +}; +static arc arcs_3_0[1] = { + {11, 1}, +}; +static arc arcs_3_1[1] = { + {12, 2}, +}; +static arc arcs_3_2[1] = { + {13, 3}, +}; +static arc arcs_3_3[1] = { + {14, 4}, +}; +static arc arcs_3_4[1] = { + {15, 5}, +}; +static arc arcs_3_5[1] = { + {0, 5}, +}; +static state states_3[6] = { + {1, arcs_3_0}, + {1, arcs_3_1}, + {1, arcs_3_2}, + {1, arcs_3_3}, + {1, arcs_3_4}, + {1, arcs_3_5}, +}; +static arc arcs_4_0[1] = { + {16, 1}, +}; +static arc arcs_4_1[2] = { + {17, 2}, + {18, 3}, +}; +static arc arcs_4_2[1] = { + {18, 3}, +}; +static arc arcs_4_3[1] = { + {0, 3}, +}; +static state states_4[4] = { + {1, arcs_4_0}, + {2, arcs_4_1}, + {1, arcs_4_2}, + {1, arcs_4_3}, +}; +static arc arcs_5_0[3] = { + {19, 1}, + {23, 2}, + {24, 3}, +}; +static arc arcs_5_1[3] = { + {20, 4}, + {22, 5}, + {0, 1}, +}; +static arc arcs_5_2[1] = { + {12, 6}, +}; +static arc arcs_5_3[1] = { + {12, 7}, +}; +static arc arcs_5_4[1] = { + {21, 8}, +}; +static arc arcs_5_5[4] = { + {19, 1}, + {23, 2}, + {24, 3}, + {0, 5}, +}; +static arc arcs_5_6[2] = { + {22, 9}, + {0, 6}, +}; +static arc arcs_5_7[1] = { + {0, 7}, +}; +static arc arcs_5_8[2] = { + {22, 5}, + {0, 8}, +}; +static arc arcs_5_9[1] = { + {24, 3}, +}; +static state states_5[10] = { + {3, arcs_5_0}, + {3, arcs_5_1}, + {1, arcs_5_2}, + {1, arcs_5_3}, + {1, arcs_5_4}, + {4, arcs_5_5}, + {2, arcs_5_6}, + {1, arcs_5_7}, + {2, arcs_5_8}, + {1, arcs_5_9}, +}; +static arc arcs_6_0[2] = { + {12, 1}, + {16, 2}, +}; +static arc arcs_6_1[1] = { + {0, 1}, +}; +static arc arcs_6_2[1] = { + {25, 3}, +}; +static arc arcs_6_3[1] = { + {18, 1}, +}; +static state states_6[4] = { + {2, arcs_6_0}, + {1, arcs_6_1}, + {1, arcs_6_2}, + {1, arcs_6_3}, +}; +static arc arcs_7_0[1] = { + {19, 1}, +}; +static arc arcs_7_1[2] = { + {22, 2}, + {0, 1}, +}; +static arc arcs_7_2[2] = { + {19, 1}, + {0, 2}, +}; +static state states_7[3] = { + {1, arcs_7_0}, + {2, arcs_7_1}, + {2, arcs_7_2}, +}; +static arc arcs_8_0[2] = { + {3, 1}, + {4, 1}, +}; +static arc arcs_8_1[1] = { + {0, 1}, +}; +static state states_8[2] = { + {2, arcs_8_0}, + {1, arcs_8_1}, +}; +static arc arcs_9_0[1] = { + {26, 1}, +}; +static arc arcs_9_1[2] = { + {27, 2}, + {2, 3}, +}; +static arc arcs_9_2[2] = { + {26, 1}, + {2, 3}, +}; +static arc arcs_9_3[1] = { + {0, 3}, +}; +static state states_9[4] = { + {1, arcs_9_0}, + {2, arcs_9_1}, + {2, arcs_9_2}, + {1, arcs_9_3}, +}; +static arc arcs_10_0[9] = { + {28, 1}, + {29, 1}, + {30, 1}, + {31, 1}, + {32, 1}, + {33, 1}, + {34, 1}, + {35, 1}, + {36, 1}, +}; +static arc arcs_10_1[1] = { + {0, 1}, +}; +static state states_10[2] = { + {9, arcs_10_0}, + {1, arcs_10_1}, +}; +static arc arcs_11_0[1] = { + {9, 1}, +}; +static arc arcs_11_1[3] = { + {37, 2}, + {20, 3}, + {0, 1}, +}; +static arc arcs_11_2[1] = { + {9, 4}, +}; +static arc arcs_11_3[1] = { + {9, 5}, +}; +static arc arcs_11_4[1] = { + {0, 4}, +}; +static arc arcs_11_5[2] = { + {20, 3}, + {0, 5}, +}; +static state states_11[6] = { + {1, arcs_11_0}, + {3, arcs_11_1}, + {1, arcs_11_2}, + {1, arcs_11_3}, + {1, arcs_11_4}, + {2, arcs_11_5}, +}; +static arc arcs_12_0[12] = { + {38, 1}, + {39, 1}, + {40, 1}, + {41, 1}, + {42, 1}, + {43, 1}, + {44, 1}, + {45, 1}, + {46, 1}, + {47, 1}, + {48, 1}, + {49, 1}, +}; +static arc arcs_12_1[1] = { + {0, 1}, +}; +static state states_12[2] = { + {12, arcs_12_0}, + {1, arcs_12_1}, +}; +static arc arcs_13_0[1] = { + {50, 1}, +}; +static arc arcs_13_1[3] = { + {21, 2}, + {51, 3}, + {0, 1}, +}; +static arc arcs_13_2[2] = { + {22, 4}, + {0, 2}, +}; +static arc arcs_13_3[1] = { + {21, 5}, +}; +static arc arcs_13_4[2] = { + {21, 2}, + {0, 4}, +}; +static arc arcs_13_5[2] = { + {22, 6}, + {0, 5}, +}; +static arc arcs_13_6[1] = { + {21, 7}, +}; +static arc arcs_13_7[2] = { + {22, 8}, + {0, 7}, +}; +static arc arcs_13_8[2] = { + {21, 7}, + {0, 8}, +}; +static state states_13[9] = { + {1, arcs_13_0}, + {3, arcs_13_1}, + {2, arcs_13_2}, + {1, arcs_13_3}, + {2, arcs_13_4}, + {2, arcs_13_5}, + {1, arcs_13_6}, + {2, arcs_13_7}, + {2, arcs_13_8}, +}; +static arc arcs_14_0[1] = { + {52, 1}, +}; +static arc arcs_14_1[1] = { + {53, 2}, +}; +static arc arcs_14_2[1] = { + {0, 2}, +}; +static state states_14[3] = { + {1, arcs_14_0}, + {1, arcs_14_1}, + {1, arcs_14_2}, +}; +static arc arcs_15_0[1] = { + {54, 1}, +}; +static arc arcs_15_1[1] = { + {0, 1}, +}; +static state states_15[2] = { + {1, arcs_15_0}, + {1, arcs_15_1}, +}; +static arc arcs_16_0[5] = { + {55, 1}, + {56, 1}, + {57, 1}, + {58, 1}, + {59, 1}, +}; +static arc arcs_16_1[1] = { + {0, 1}, +}; +static state states_16[2] = { + {5, arcs_16_0}, + {1, arcs_16_1}, +}; +static arc arcs_17_0[1] = { + {60, 1}, +}; +static arc arcs_17_1[1] = { + {0, 1}, +}; +static state states_17[2] = { + {1, arcs_17_0}, + {1, arcs_17_1}, +}; +static arc arcs_18_0[1] = { + {61, 1}, +}; +static arc arcs_18_1[1] = { + {0, 1}, +}; +static state states_18[2] = { + {1, arcs_18_0}, + {1, arcs_18_1}, +}; +static arc arcs_19_0[1] = { + {62, 1}, +}; +static arc arcs_19_1[2] = { + {9, 2}, + {0, 1}, +}; +static arc arcs_19_2[1] = { + {0, 2}, +}; +static state states_19[3] = { + {1, arcs_19_0}, + {2, arcs_19_1}, + {1, arcs_19_2}, +}; +static arc arcs_20_0[1] = { + {63, 1}, +}; +static arc arcs_20_1[1] = { + {9, 2}, +}; +static arc arcs_20_2[1] = { + {0, 2}, +}; +static state states_20[3] = { + {1, arcs_20_0}, + {1, arcs_20_1}, + {1, arcs_20_2}, +}; +static arc arcs_21_0[1] = { + {64, 1}, +}; +static arc arcs_21_1[2] = { + {21, 2}, + {0, 1}, +}; +static arc arcs_21_2[2] = { + {22, 3}, + {0, 2}, +}; +static arc arcs_21_3[1] = { + {21, 4}, +}; +static arc arcs_21_4[2] = { + {22, 5}, + {0, 4}, +}; +static arc arcs_21_5[1] = { + {21, 6}, +}; +static arc arcs_21_6[1] = { + {0, 6}, +}; +static state states_21[7] = { + {1, arcs_21_0}, + {2, arcs_21_1}, + {2, arcs_21_2}, + {1, arcs_21_3}, + {2, arcs_21_4}, + {1, arcs_21_5}, + {1, arcs_21_6}, +}; +static arc arcs_22_0[2] = { + {65, 1}, + {67, 2}, +}; +static arc arcs_22_1[1] = { + {66, 3}, +}; +static arc arcs_22_2[1] = { + {68, 4}, +}; +static arc arcs_22_3[2] = { + {22, 1}, + {0, 3}, +}; +static arc arcs_22_4[1] = { + {65, 5}, +}; +static arc arcs_22_5[2] = { + {23, 6}, + {69, 7}, +}; +static arc arcs_22_6[1] = { + {0, 6}, +}; +static arc arcs_22_7[2] = { + {22, 8}, + {0, 7}, +}; +static arc arcs_22_8[1] = { + {69, 7}, +}; +static state states_22[9] = { + {2, arcs_22_0}, + {1, arcs_22_1}, + {1, arcs_22_2}, + {2, arcs_22_3}, + {1, arcs_22_4}, + {2, arcs_22_5}, + {1, arcs_22_6}, + {2, arcs_22_7}, + {1, arcs_22_8}, +}; +static arc arcs_23_0[1] = { + {12, 1}, +}; +static arc arcs_23_1[2] = { + {12, 2}, + {0, 1}, +}; +static arc arcs_23_2[1] = { + {12, 3}, +}; +static arc arcs_23_3[1] = { + {0, 3}, +}; +static state states_23[4] = { + {1, arcs_23_0}, + {2, arcs_23_1}, + {1, arcs_23_2}, + {1, arcs_23_3}, +}; +static arc arcs_24_0[1] = { + {68, 1}, +}; +static arc arcs_24_1[2] = { + {12, 2}, + {0, 1}, +}; +static arc arcs_24_2[1] = { + {12, 3}, +}; +static arc arcs_24_3[1] = { + {0, 3}, +}; +static state states_24[4] = { + {1, arcs_24_0}, + {2, arcs_24_1}, + {1, arcs_24_2}, + {1, arcs_24_3}, +}; +static arc arcs_25_0[1] = { + {12, 1}, +}; +static arc arcs_25_1[2] = { + {70, 0}, + {0, 1}, +}; +static state states_25[2] = { + {1, arcs_25_0}, + {2, arcs_25_1}, +}; +static arc arcs_26_0[1] = { + {71, 1}, +}; +static arc arcs_26_1[1] = { + {12, 2}, +}; +static arc arcs_26_2[2] = { + {22, 1}, + {0, 2}, +}; +static state states_26[3] = { + {1, arcs_26_0}, + {1, arcs_26_1}, + {2, arcs_26_2}, +}; +static arc arcs_27_0[1] = { + {72, 1}, +}; +static arc arcs_27_1[1] = { + {73, 2}, +}; +static arc arcs_27_2[2] = { + {74, 3}, + {0, 2}, +}; +static arc arcs_27_3[1] = { + {21, 4}, +}; +static arc arcs_27_4[2] = { + {22, 5}, + {0, 4}, +}; +static arc arcs_27_5[1] = { + {21, 6}, +}; +static arc arcs_27_6[1] = { + {0, 6}, +}; +static state states_27[7] = { + {1, arcs_27_0}, + {1, arcs_27_1}, + {2, arcs_27_2}, + {1, arcs_27_3}, + {2, arcs_27_4}, + {1, arcs_27_5}, + {1, arcs_27_6}, +}; +static arc arcs_28_0[1] = { + {75, 1}, +}; +static arc arcs_28_1[1] = { + {21, 2}, +}; +static arc arcs_28_2[2] = { + {22, 3}, + {0, 2}, +}; +static arc arcs_28_3[1] = { + {21, 4}, +}; +static arc arcs_28_4[1] = { + {0, 4}, +}; +static state states_28[5] = { + {1, arcs_28_0}, + {1, arcs_28_1}, + {2, arcs_28_2}, + {1, arcs_28_3}, + {1, arcs_28_4}, +}; +static arc arcs_29_0[6] = { + {76, 1}, + {77, 1}, + {78, 1}, + {79, 1}, + {10, 1}, + {80, 1}, +}; +static arc arcs_29_1[1] = { + {0, 1}, +}; +static state states_29[2] = { + {6, arcs_29_0}, + {1, arcs_29_1}, +}; +static arc arcs_30_0[1] = { + {81, 1}, +}; +static arc arcs_30_1[1] = { + {21, 2}, +}; +static arc arcs_30_2[1] = { + {14, 3}, +}; +static arc arcs_30_3[1] = { + {15, 4}, +}; +static arc arcs_30_4[3] = { + {82, 1}, + {83, 5}, + {0, 4}, +}; +static arc arcs_30_5[1] = { + {14, 6}, +}; +static arc arcs_30_6[1] = { + {15, 7}, +}; +static arc arcs_30_7[1] = { + {0, 7}, +}; +static state states_30[8] = { + {1, arcs_30_0}, + {1, arcs_30_1}, + {1, arcs_30_2}, + {1, arcs_30_3}, + {3, arcs_30_4}, + {1, arcs_30_5}, + {1, arcs_30_6}, + {1, arcs_30_7}, +}; +static arc arcs_31_0[1] = { + {84, 1}, +}; +static arc arcs_31_1[1] = { + {21, 2}, +}; +static arc arcs_31_2[1] = { + {14, 3}, +}; +static arc arcs_31_3[1] = { + {15, 4}, +}; +static arc arcs_31_4[2] = { + {83, 5}, + {0, 4}, +}; +static arc arcs_31_5[1] = { + {14, 6}, +}; +static arc arcs_31_6[1] = { + {15, 7}, +}; +static arc arcs_31_7[1] = { + {0, 7}, +}; +static state states_31[8] = { + {1, arcs_31_0}, + {1, arcs_31_1}, + {1, arcs_31_2}, + {1, arcs_31_3}, + {2, arcs_31_4}, + {1, arcs_31_5}, + {1, arcs_31_6}, + {1, arcs_31_7}, +}; +static arc arcs_32_0[1] = { + {85, 1}, +}; +static arc arcs_32_1[1] = { + {53, 2}, +}; +static arc arcs_32_2[1] = { + {74, 3}, +}; +static arc arcs_32_3[1] = { + {9, 4}, +}; +static arc arcs_32_4[1] = { + {14, 5}, +}; +static arc arcs_32_5[1] = { + {15, 6}, +}; +static arc arcs_32_6[2] = { + {83, 7}, + {0, 6}, +}; +static arc arcs_32_7[1] = { + {14, 8}, +}; +static arc arcs_32_8[1] = { + {15, 9}, +}; +static arc arcs_32_9[1] = { + {0, 9}, +}; +static state states_32[10] = { + {1, arcs_32_0}, + {1, arcs_32_1}, + {1, arcs_32_2}, + {1, arcs_32_3}, + {1, arcs_32_4}, + {1, arcs_32_5}, + {2, arcs_32_6}, + {1, arcs_32_7}, + {1, arcs_32_8}, + {1, arcs_32_9}, +}; +static arc arcs_33_0[1] = { + {86, 1}, +}; +static arc arcs_33_1[1] = { + {14, 2}, +}; +static arc arcs_33_2[1] = { + {15, 3}, +}; +static arc arcs_33_3[2] = { + {87, 4}, + {88, 5}, +}; +static arc arcs_33_4[1] = { + {14, 6}, +}; +static arc arcs_33_5[1] = { + {14, 7}, +}; +static arc arcs_33_6[1] = { + {15, 8}, +}; +static arc arcs_33_7[1] = { + {15, 9}, +}; +static arc arcs_33_8[3] = { + {87, 4}, + {83, 5}, + {0, 8}, +}; +static arc arcs_33_9[1] = { + {0, 9}, +}; +static state states_33[10] = { + {1, arcs_33_0}, + {1, arcs_33_1}, + {1, arcs_33_2}, + {2, arcs_33_3}, + {1, arcs_33_4}, + {1, arcs_33_5}, + {1, arcs_33_6}, + {1, arcs_33_7}, + {3, arcs_33_8}, + {1, arcs_33_9}, +}; +static arc arcs_34_0[1] = { + {89, 1}, +}; +static arc arcs_34_1[2] = { + {21, 2}, + {0, 1}, +}; +static arc arcs_34_2[2] = { + {22, 3}, + {0, 2}, +}; +static arc arcs_34_3[1] = { + {21, 4}, +}; +static arc arcs_34_4[1] = { + {0, 4}, +}; +static state states_34[5] = { + {1, arcs_34_0}, + {2, arcs_34_1}, + {2, arcs_34_2}, + {1, arcs_34_3}, + {1, arcs_34_4}, +}; +static arc arcs_35_0[2] = { + {3, 1}, + {2, 2}, +}; +static arc arcs_35_1[1] = { + {0, 1}, +}; +static arc arcs_35_2[1] = { + {90, 3}, +}; +static arc arcs_35_3[1] = { + {6, 4}, +}; +static arc arcs_35_4[2] = { + {6, 4}, + {91, 1}, +}; +static state states_35[5] = { + {2, arcs_35_0}, + {1, arcs_35_1}, + {1, arcs_35_2}, + {1, arcs_35_3}, + {2, arcs_35_4}, +}; +static arc arcs_36_0[2] = { + {92, 1}, + {94, 2}, +}; +static arc arcs_36_1[2] = { + {93, 3}, + {0, 1}, +}; +static arc arcs_36_2[1] = { + {0, 2}, +}; +static arc arcs_36_3[1] = { + {92, 1}, +}; +static state states_36[4] = { + {2, arcs_36_0}, + {2, arcs_36_1}, + {1, arcs_36_2}, + {1, arcs_36_3}, +}; +static arc arcs_37_0[1] = { + {95, 1}, +}; +static arc arcs_37_1[2] = { + {96, 0}, + {0, 1}, +}; +static state states_37[2] = { + {1, arcs_37_0}, + {2, arcs_37_1}, +}; +static arc arcs_38_0[2] = { + {97, 1}, + {98, 2}, +}; +static arc arcs_38_1[1] = { + {95, 2}, +}; +static arc arcs_38_2[1] = { + {0, 2}, +}; +static state states_38[3] = { + {2, arcs_38_0}, + {1, arcs_38_1}, + {1, arcs_38_2}, +}; +static arc arcs_39_0[1] = { + {73, 1}, +}; +static arc arcs_39_1[2] = { + {99, 0}, + {0, 1}, +}; +static state states_39[2] = { + {1, arcs_39_0}, + {2, arcs_39_1}, +}; +static arc arcs_40_0[10] = { + {100, 1}, + {101, 1}, + {102, 1}, + {103, 1}, + {104, 1}, + {105, 1}, + {106, 1}, + {74, 1}, + {97, 2}, + {107, 3}, +}; +static arc arcs_40_1[1] = { + {0, 1}, +}; +static arc arcs_40_2[1] = { + {74, 1}, +}; +static arc arcs_40_3[2] = { + {97, 1}, + {0, 3}, +}; +static state states_40[4] = { + {10, arcs_40_0}, + {1, arcs_40_1}, + {1, arcs_40_2}, + {2, arcs_40_3}, +}; +static arc arcs_41_0[1] = { + {108, 1}, +}; +static arc arcs_41_1[2] = { + {109, 0}, + {0, 1}, +}; +static state states_41[2] = { + {1, arcs_41_0}, + {2, arcs_41_1}, +}; +static arc arcs_42_0[1] = { + {110, 1}, +}; +static arc arcs_42_1[2] = { + {111, 0}, + {0, 1}, +}; +static state states_42[2] = { + {1, arcs_42_0}, + {2, arcs_42_1}, +}; +static arc arcs_43_0[1] = { + {112, 1}, +}; +static arc arcs_43_1[2] = { + {113, 0}, + {0, 1}, +}; +static state states_43[2] = { + {1, arcs_43_0}, + {2, arcs_43_1}, +}; +static arc arcs_44_0[1] = { + {114, 1}, +}; +static arc arcs_44_1[3] = { + {115, 0}, + {51, 0}, + {0, 1}, +}; +static state states_44[2] = { + {1, arcs_44_0}, + {3, arcs_44_1}, +}; +static arc arcs_45_0[1] = { + {116, 1}, +}; +static arc arcs_45_1[3] = { + {117, 0}, + {118, 0}, + {0, 1}, +}; +static state states_45[2] = { + {1, arcs_45_0}, + {3, arcs_45_1}, +}; +static arc arcs_46_0[1] = { + {119, 1}, +}; +static arc arcs_46_1[5] = { + {23, 0}, + {120, 0}, + {121, 0}, + {122, 0}, + {0, 1}, +}; +static state states_46[2] = { + {1, arcs_46_0}, + {5, arcs_46_1}, +}; +static arc arcs_47_0[4] = { + {117, 1}, + {118, 1}, + {123, 1}, + {124, 2}, +}; +static arc arcs_47_1[1] = { + {119, 2}, +}; +static arc arcs_47_2[1] = { + {0, 2}, +}; +static state states_47[3] = { + {4, arcs_47_0}, + {1, arcs_47_1}, + {1, arcs_47_2}, +}; +static arc arcs_48_0[1] = { + {125, 1}, +}; +static arc arcs_48_1[3] = { + {126, 1}, + {24, 2}, + {0, 1}, +}; +static arc arcs_48_2[1] = { + {119, 3}, +}; +static arc arcs_48_3[1] = { + {0, 3}, +}; +static state states_48[4] = { + {1, arcs_48_0}, + {3, arcs_48_1}, + {1, arcs_48_2}, + {1, arcs_48_3}, +}; +static arc arcs_49_0[7] = { + {16, 1}, + {127, 2}, + {130, 3}, + {133, 4}, + {12, 5}, + {135, 5}, + {136, 6}, +}; +static arc arcs_49_1[2] = { + {9, 7}, + {18, 5}, +}; +static arc arcs_49_2[2] = { + {128, 8}, + {129, 5}, +}; +static arc arcs_49_3[2] = { + {131, 9}, + {132, 5}, +}; +static arc arcs_49_4[1] = { + {134, 10}, +}; +static arc arcs_49_5[1] = { + {0, 5}, +}; +static arc arcs_49_6[2] = { + {136, 6}, + {0, 6}, +}; +static arc arcs_49_7[1] = { + {18, 5}, +}; +static arc arcs_49_8[1] = { + {129, 5}, +}; +static arc arcs_49_9[1] = { + {132, 5}, +}; +static arc arcs_49_10[1] = { + {133, 5}, +}; +static state states_49[11] = { + {7, arcs_49_0}, + {2, arcs_49_1}, + {2, arcs_49_2}, + {2, arcs_49_3}, + {1, arcs_49_4}, + {1, arcs_49_5}, + {2, arcs_49_6}, + {1, arcs_49_7}, + {1, arcs_49_8}, + {1, arcs_49_9}, + {1, arcs_49_10}, +}; +static arc arcs_50_0[1] = { + {21, 1}, +}; +static arc arcs_50_1[3] = { + {137, 2}, + {22, 3}, + {0, 1}, +}; +static arc arcs_50_2[1] = { + {0, 2}, +}; +static arc arcs_50_3[2] = { + {21, 4}, + {0, 3}, +}; +static arc arcs_50_4[2] = { + {22, 3}, + {0, 4}, +}; +static state states_50[5] = { + {1, arcs_50_0}, + {3, arcs_50_1}, + {1, arcs_50_2}, + {2, arcs_50_3}, + {2, arcs_50_4}, +}; +static arc arcs_51_0[1] = { + {138, 1}, +}; +static arc arcs_51_1[2] = { + {17, 2}, + {14, 3}, +}; +static arc arcs_51_2[1] = { + {14, 3}, +}; +static arc arcs_51_3[1] = { + {21, 4}, +}; +static arc arcs_51_4[1] = { + {0, 4}, +}; +static state states_51[5] = { + {1, arcs_51_0}, + {2, arcs_51_1}, + {1, arcs_51_2}, + {1, arcs_51_3}, + {1, arcs_51_4}, +}; +static arc arcs_52_0[3] = { + {16, 1}, + {127, 2}, + {70, 3}, +}; +static arc arcs_52_1[2] = { + {139, 4}, + {18, 5}, +}; +static arc arcs_52_2[1] = { + {140, 6}, +}; +static arc arcs_52_3[1] = { + {12, 5}, +}; +static arc arcs_52_4[1] = { + {18, 5}, +}; +static arc arcs_52_5[1] = { + {0, 5}, +}; +static arc arcs_52_6[1] = { + {129, 5}, +}; +static state states_52[7] = { + {3, arcs_52_0}, + {2, arcs_52_1}, + {1, arcs_52_2}, + {1, arcs_52_3}, + {1, arcs_52_4}, + {1, arcs_52_5}, + {1, arcs_52_6}, +}; +static arc arcs_53_0[1] = { + {141, 1}, +}; +static arc arcs_53_1[2] = { + {22, 2}, + {0, 1}, +}; +static arc arcs_53_2[2] = { + {141, 1}, + {0, 2}, +}; +static state states_53[3] = { + {1, arcs_53_0}, + {2, arcs_53_1}, + {2, arcs_53_2}, +}; +static arc arcs_54_0[3] = { + {70, 1}, + {21, 2}, + {14, 3}, +}; +static arc arcs_54_1[1] = { + {70, 4}, +}; +static arc arcs_54_2[2] = { + {14, 3}, + {0, 2}, +}; +static arc arcs_54_3[3] = { + {21, 5}, + {142, 6}, + {0, 3}, +}; +static arc arcs_54_4[1] = { + {70, 6}, +}; +static arc arcs_54_5[2] = { + {142, 6}, + {0, 5}, +}; +static arc arcs_54_6[1] = { + {0, 6}, +}; +static state states_54[7] = { + {3, arcs_54_0}, + {1, arcs_54_1}, + {2, arcs_54_2}, + {3, arcs_54_3}, + {1, arcs_54_4}, + {2, arcs_54_5}, + {1, arcs_54_6}, +}; +static arc arcs_55_0[1] = { + {14, 1}, +}; +static arc arcs_55_1[2] = { + {21, 2}, + {0, 1}, +}; +static arc arcs_55_2[1] = { + {0, 2}, +}; +static state states_55[3] = { + {1, arcs_55_0}, + {2, arcs_55_1}, + {1, arcs_55_2}, +}; +static arc arcs_56_0[1] = { + {73, 1}, +}; +static arc arcs_56_1[2] = { + {22, 2}, + {0, 1}, +}; +static arc arcs_56_2[2] = { + {73, 1}, + {0, 2}, +}; +static state states_56[3] = { + {1, arcs_56_0}, + {2, arcs_56_1}, + {2, arcs_56_2}, +}; +static arc arcs_57_0[1] = { + {21, 1}, +}; +static arc arcs_57_1[2] = { + {22, 2}, + {0, 1}, +}; +static arc arcs_57_2[2] = { + {21, 1}, + {0, 2}, +}; +static state states_57[3] = { + {1, arcs_57_0}, + {2, arcs_57_1}, + {2, arcs_57_2}, +}; +static arc arcs_58_0[1] = { + {21, 1}, +}; +static arc arcs_58_1[2] = { + {22, 2}, + {0, 1}, +}; +static arc arcs_58_2[1] = { + {21, 3}, +}; +static arc arcs_58_3[2] = { + {22, 4}, + {0, 3}, +}; +static arc arcs_58_4[2] = { + {21, 3}, + {0, 4}, +}; +static state states_58[5] = { + {1, arcs_58_0}, + {2, arcs_58_1}, + {1, arcs_58_2}, + {2, arcs_58_3}, + {2, arcs_58_4}, +}; +static arc arcs_59_0[1] = { + {21, 1}, +}; +static arc arcs_59_1[1] = { + {14, 2}, +}; +static arc arcs_59_2[1] = { + {21, 3}, +}; +static arc arcs_59_3[2] = { + {22, 4}, + {0, 3}, +}; +static arc arcs_59_4[2] = { + {21, 1}, + {0, 4}, +}; +static state states_59[5] = { + {1, arcs_59_0}, + {1, arcs_59_1}, + {1, arcs_59_2}, + {2, arcs_59_3}, + {2, arcs_59_4}, +}; +static arc arcs_60_0[1] = { + {144, 1}, +}; +static arc arcs_60_1[1] = { + {12, 2}, +}; +static arc arcs_60_2[2] = { + {16, 3}, + {14, 4}, +}; +static arc arcs_60_3[1] = { + {9, 5}, +}; +static arc arcs_60_4[1] = { + {15, 6}, +}; +static arc arcs_60_5[1] = { + {18, 7}, +}; +static arc arcs_60_6[1] = { + {0, 6}, +}; +static arc arcs_60_7[1] = { + {14, 4}, +}; +static state states_60[8] = { + {1, arcs_60_0}, + {1, arcs_60_1}, + {2, arcs_60_2}, + {1, arcs_60_3}, + {1, arcs_60_4}, + {1, arcs_60_5}, + {1, arcs_60_6}, + {1, arcs_60_7}, +}; +static arc arcs_61_0[3] = { + {145, 1}, + {23, 2}, + {24, 3}, +}; +static arc arcs_61_1[2] = { + {22, 4}, + {0, 1}, +}; +static arc arcs_61_2[1] = { + {21, 5}, +}; +static arc arcs_61_3[1] = { + {21, 6}, +}; +static arc arcs_61_4[4] = { + {145, 1}, + {23, 2}, + {24, 3}, + {0, 4}, +}; +static arc arcs_61_5[2] = { + {22, 7}, + {0, 5}, +}; +static arc arcs_61_6[1] = { + {0, 6}, +}; +static arc arcs_61_7[1] = { + {24, 3}, +}; +static state states_61[8] = { + {3, arcs_61_0}, + {2, arcs_61_1}, + {1, arcs_61_2}, + {1, arcs_61_3}, + {4, arcs_61_4}, + {2, arcs_61_5}, + {1, arcs_61_6}, + {1, arcs_61_7}, +}; +static arc arcs_62_0[1] = { + {21, 1}, +}; +static arc arcs_62_1[2] = { + {20, 2}, + {0, 1}, +}; +static arc arcs_62_2[1] = { + {21, 3}, +}; +static arc arcs_62_3[1] = { + {0, 3}, +}; +static state states_62[4] = { + {1, arcs_62_0}, + {2, arcs_62_1}, + {1, arcs_62_2}, + {1, arcs_62_3}, +}; +static arc arcs_63_0[2] = { + {137, 1}, + {147, 1}, +}; +static arc arcs_63_1[1] = { + {0, 1}, +}; +static state states_63[2] = { + {2, arcs_63_0}, + {1, arcs_63_1}, +}; +static arc arcs_64_0[1] = { + {85, 1}, +}; +static arc arcs_64_1[1] = { + {53, 2}, +}; +static arc arcs_64_2[1] = { + {74, 3}, +}; +static arc arcs_64_3[1] = { + {143, 4}, +}; +static arc arcs_64_4[2] = { + {146, 5}, + {0, 4}, +}; +static arc arcs_64_5[1] = { + {0, 5}, +}; +static state states_64[6] = { + {1, arcs_64_0}, + {1, arcs_64_1}, + {1, arcs_64_2}, + {1, arcs_64_3}, + {2, arcs_64_4}, + {1, arcs_64_5}, +}; +static arc arcs_65_0[1] = { + {81, 1}, +}; +static arc arcs_65_1[1] = { + {21, 2}, +}; +static arc arcs_65_2[2] = { + {146, 3}, + {0, 2}, +}; +static arc arcs_65_3[1] = { + {0, 3}, +}; +static state states_65[4] = { + {1, arcs_65_0}, + {1, arcs_65_1}, + {2, arcs_65_2}, + {1, arcs_65_3}, +}; +static arc arcs_66_0[1] = { + {21, 1}, +}; +static arc arcs_66_1[2] = { + {22, 0}, + {0, 1}, +}; +static state states_66[2] = { + {1, arcs_66_0}, + {2, arcs_66_1}, +}; +static arc arcs_67_0[1] = { + {12, 1}, +}; +static arc arcs_67_1[1] = { + {0, 1}, +}; +static state states_67[2] = { + {1, arcs_67_0}, + {1, arcs_67_1}, +}; +static dfa dfas[68] = { + {256, "single_input", 0, 3, states_0, + "\004\030\001\000\000\000\124\360\213\011\162\000\002\000\140\210\244\005\001"}, + {257, "file_input", 0, 2, states_1, + "\204\030\001\000\000\000\124\360\213\011\162\000\002\000\140\210\244\005\001"}, + {258, "eval_input", 0, 3, states_2, + "\000\020\001\000\000\000\000\000\000\000\000\000\002\000\140\210\244\005\000"}, + {259, "funcdef", 0, 6, states_3, + "\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + {260, "parameters", 0, 4, states_4, + "\000\000\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + {261, "varargslist", 0, 10, states_5, + "\000\020\201\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + {262, "fpdef", 0, 4, states_6, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + {263, "fplist", 0, 3, states_7, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + {264, "stmt", 0, 2, states_8, + "\000\030\001\000\000\000\124\360\213\011\162\000\002\000\140\210\244\005\001"}, + {265, "simple_stmt", 0, 4, states_9, + "\000\020\001\000\000\000\124\360\213\011\000\000\002\000\140\210\244\005\000"}, + {266, "small_stmt", 0, 2, states_10, + "\000\020\001\000\000\000\124\360\213\011\000\000\002\000\140\210\244\005\000"}, + {267, "expr_stmt", 0, 6, states_11, + "\000\020\001\000\000\000\000\000\000\000\000\000\002\000\140\210\244\005\000"}, + {268, "augassign", 0, 2, states_12, + "\000\000\000\000\300\377\003\000\000\000\000\000\000\000\000\000\000\000\000"}, + {269, "print_stmt", 0, 9, states_13, + "\000\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000\000"}, + {270, "del_stmt", 0, 3, states_14, + "\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000\000\000"}, + {271, "pass_stmt", 0, 2, states_15, + "\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000\000\000\000"}, + {272, "flow_stmt", 0, 2, states_16, + "\000\000\000\000\000\000\000\360\001\000\000\000\000\000\000\000\000\000\000"}, + {273, "break_stmt", 0, 2, states_17, + "\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000\000\000"}, + {274, "continue_stmt", 0, 2, states_18, + "\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000\000\000"}, + {275, "return_stmt", 0, 3, states_19, + "\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000\000\000"}, + {276, "yield_stmt", 0, 3, states_20, + "\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000"}, + {277, "raise_stmt", 0, 7, states_21, + "\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000"}, + {278, "import_stmt", 0, 9, states_22, + "\000\000\000\000\000\000\000\000\012\000\000\000\000\000\000\000\000\000\000"}, + {279, "import_as_name", 0, 4, states_23, + "\000\020\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + {280, "dotted_as_name", 0, 4, states_24, + "\000\020\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + {281, "dotted_name", 0, 2, states_25, + "\000\020\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + {282, "global_stmt", 0, 3, states_26, + "\000\000\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000"}, + {283, "exec_stmt", 0, 7, states_27, + "\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000"}, + {284, "assert_stmt", 0, 5, states_28, + "\000\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\000\000"}, + {285, "compound_stmt", 0, 2, states_29, + "\000\010\000\000\000\000\000\000\000\000\162\000\000\000\000\000\000\000\001"}, + {286, "if_stmt", 0, 8, states_30, + "\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000"}, + {287, "while_stmt", 0, 8, states_31, + "\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000"}, + {288, "for_stmt", 0, 10, states_32, + "\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000"}, + {289, "try_stmt", 0, 10, states_33, + "\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000"}, + {290, "except_clause", 0, 5, states_34, + "\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000"}, + {291, "suite", 0, 5, states_35, + "\004\020\001\000\000\000\124\360\213\011\000\000\002\000\140\210\244\005\000"}, + {292, "test", 0, 4, states_36, + "\000\020\001\000\000\000\000\000\000\000\000\000\002\000\140\210\244\005\000"}, + {293, "and_test", 0, 2, states_37, + "\000\020\001\000\000\000\000\000\000\000\000\000\002\000\140\210\244\001\000"}, + {294, "not_test", 0, 3, states_38, + "\000\020\001\000\000\000\000\000\000\000\000\000\002\000\140\210\244\001\000"}, + {295, "comparison", 0, 2, states_39, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\140\210\244\001\000"}, + {296, "comp_op", 0, 4, states_40, + "\000\000\000\000\000\000\000\000\000\004\000\000\362\017\000\000\000\000\000"}, + {297, "expr", 0, 2, states_41, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\140\210\244\001\000"}, + {298, "xor_expr", 0, 2, states_42, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\140\210\244\001\000"}, + {299, "and_expr", 0, 2, states_43, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\140\210\244\001\000"}, + {300, "shift_expr", 0, 2, states_44, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\140\210\244\001\000"}, + {301, "arith_expr", 0, 2, states_45, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\140\210\244\001\000"}, + {302, "term", 0, 2, states_46, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\140\210\244\001\000"}, + {303, "factor", 0, 3, states_47, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\140\210\244\001\000"}, + {304, "power", 0, 4, states_48, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\000\200\244\001\000"}, + {305, "atom", 0, 11, states_49, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\000\200\244\001\000"}, + {306, "listmaker", 0, 5, states_50, + "\000\020\001\000\000\000\000\000\000\000\000\000\002\000\140\210\244\005\000"}, + {307, "lambdef", 0, 5, states_51, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\004\000"}, + {308, "trailer", 0, 7, states_52, + "\000\000\001\000\000\000\000\000\100\000\000\000\000\000\000\200\000\000\000"}, + {309, "subscriptlist", 0, 3, states_53, + "\000\120\001\000\000\000\000\000\100\000\000\000\002\000\140\210\244\005\000"}, + {310, "subscript", 0, 7, states_54, + "\000\120\001\000\000\000\000\000\100\000\000\000\002\000\140\210\244\005\000"}, + {311, "sliceop", 0, 3, states_55, + "\000\100\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, + {312, "exprlist", 0, 3, states_56, + "\000\020\001\000\000\000\000\000\000\000\000\000\000\000\140\210\244\001\000"}, + {313, "testlist", 0, 3, states_57, + "\000\020\001\000\000\000\000\000\000\000\000\000\002\000\140\210\244\005\000"}, + {314, "testlist_safe", 0, 5, states_58, + "\000\020\001\000\000\000\000\000\000\000\000\000\002\000\140\210\244\005\000"}, + {315, "dictmaker", 0, 5, states_59, + "\000\020\001\000\000\000\000\000\000\000\000\000\002\000\140\210\244\005\000"}, + {316, "classdef", 0, 8, states_60, + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001"}, + {317, "arglist", 0, 8, states_61, + "\000\020\201\001\000\000\000\000\000\000\000\000\002\000\140\210\244\005\000"}, + {318, "argument", 0, 4, states_62, + "\000\020\001\000\000\000\000\000\000\000\000\000\002\000\140\210\244\005\000"}, + {319, "list_iter", 0, 2, states_63, + "\000\000\000\000\000\000\000\000\000\000\042\000\000\000\000\000\000\000\000"}, + {320, "list_for", 0, 6, states_64, + "\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000"}, + {321, "list_if", 0, 4, states_65, + "\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000"}, + {322, "testlist1", 0, 2, states_66, + "\000\020\001\000\000\000\000\000\000\000\000\000\002\000\140\210\244\005\000"}, + {323, "encoding_decl", 0, 2, states_67, + "\000\020\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"}, +}; +static label labels[149] = { + {0, "EMPTY"}, + {256, 0}, + {4, 0}, + {265, 0}, + {285, 0}, + {257, 0}, + {264, 0}, + {0, 0}, + {258, 0}, + {313, 0}, + {259, 0}, + {1, "def"}, + {1, 0}, + {260, 0}, + {11, 0}, + {291, 0}, + {7, 0}, + {261, 0}, + {8, 0}, + {262, 0}, + {22, 0}, + {292, 0}, + {12, 0}, + {16, 0}, + {36, 0}, + {263, 0}, + {266, 0}, + {13, 0}, + {267, 0}, + {269, 0}, + {270, 0}, + {271, 0}, + {272, 0}, + {278, 0}, + {282, 0}, + {283, 0}, + {284, 0}, + {268, 0}, + {37, 0}, + {38, 0}, + {39, 0}, + {40, 0}, + {41, 0}, + {42, 0}, + {43, 0}, + {44, 0}, + {45, 0}, + {46, 0}, + {47, 0}, + {49, 0}, + {1, "print"}, + {35, 0}, + {1, "del"}, + {312, 0}, + {1, "pass"}, + {273, 0}, + {274, 0}, + {275, 0}, + {277, 0}, + {276, 0}, + {1, "break"}, + {1, "continue"}, + {1, "return"}, + {1, "yield"}, + {1, "raise"}, + {1, "import"}, + {280, 0}, + {1, "from"}, + {281, 0}, + {279, 0}, + {23, 0}, + {1, "global"}, + {1, "exec"}, + {297, 0}, + {1, "in"}, + {1, "assert"}, + {286, 0}, + {287, 0}, + {288, 0}, + {289, 0}, + {316, 0}, + {1, "if"}, + {1, "elif"}, + {1, "else"}, + {1, "while"}, + {1, "for"}, + {1, "try"}, + {290, 0}, + {1, "finally"}, + {1, "except"}, + {5, 0}, + {6, 0}, + {293, 0}, + {1, "or"}, + {307, 0}, + {294, 0}, + {1, "and"}, + {1, "not"}, + {295, 0}, + {296, 0}, + {20, 0}, + {21, 0}, + {28, 0}, + {31, 0}, + {30, 0}, + {29, 0}, + {29, 0}, + {1, "is"}, + {298, 0}, + {18, 0}, + {299, 0}, + {33, 0}, + {300, 0}, + {19, 0}, + {301, 0}, + {34, 0}, + {302, 0}, + {14, 0}, + {15, 0}, + {303, 0}, + {17, 0}, + {24, 0}, + {48, 0}, + {32, 0}, + {304, 0}, + {305, 0}, + {308, 0}, + {9, 0}, + {306, 0}, + {10, 0}, + {26, 0}, + {315, 0}, + {27, 0}, + {25, 0}, + {322, 0}, + {2, 0}, + {3, 0}, + {320, 0}, + {1, "lambda"}, + {317, 0}, + {309, 0}, + {310, 0}, + {311, 0}, + {314, 0}, + {1, "class"}, + {318, 0}, + {319, 0}, + {321, 0}, + {323, 0}, +}; +grammar _PyParser_Grammar = { + 68, + dfas, + {149, labels}, + 256 +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/hypot.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/hypot.c new file mode 100644 index 00000000..cdc430ec --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/hypot.c @@ -0,0 +1,23 @@ +/* hypot() replacement */ + +#include "pyconfig.h" +#include "pyport.h" + +double hypot(double x, double y) +{ + double yx; + + x = fabs(x); + y = fabs(y); + if (x < y) { + double temp = x; + x = y; + y = temp; + } + if (x == 0.) + return 0.; + else { + yx = y/x; + return x*sqrt(1.+yx*yx); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/import.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/import.c new file mode 100644 index 00000000..1c35b5ed --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/import.c @@ -0,0 +1,2920 @@ + +/* Module definition and import implementation */ + +#include "Python.h" + +#include "node.h" +#include "token.h" +#include "errcode.h" +#include "marshal.h" +#include "compile.h" +#include "eval.h" +#include "osdefs.h" +#include "importdl.h" +#ifdef macintosh +#include "macglue.h" +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +extern time_t PyOS_GetLastModificationTime(char *, FILE *); + /* In getmtime.c */ + +/* Magic word to reject .pyc files generated by other Python versions */ +/* Change for each incompatible change */ +/* The value of CR and LF is incorporated so if you ever read or write + a .pyc file in text mode the magic number will be wrong; also, the + Apple MPW compiler swaps their values, botching string constants. + XXX That probably isn't important anymore. +*/ +/* XXX Perhaps the magic number should be frozen and a version field + added to the .pyc file header? */ +/* New way to come up with the low 16 bits of the magic number: + (YEAR-1995) * 10000 + MONTH * 100 + DAY + where MONTH and DAY are 1-based. + XXX Whatever the "old way" may have been isn't documented. + XXX This scheme breaks in 2002, as (2002-1995)*10000 = 70000 doesn't + fit in 16 bits. + XXX Later, sometimes 1 gets added to MAGIC in order to record that + the Unicode -U option is in use. IMO (Tim's), that's a Bad Idea + (quite apart from that the -U option doesn't work so isn't used + anyway). + + XXX MAL, 2002-02-07: I had to modify the MAGIC due to a fix of the + UTF-8 encoder (it previously produced invalid UTF-8 for unpaired + high surrogates), so I simply bumped the month value to 20 (invalid + month) and set the day to 1. This should be recognizable by any + algorithm relying on the above scheme. Perhaps we should simply + start counting in increments of 10 from now on ?! + + MWH, 2002-08-03: Removed SET_LINENO. Couldn't be bothered figuring + out the MAGIC schemes, so just incremented it by 10. + + GvR, 2002-08-31: Because MWH changed the bytecode again, moved the + magic number *back* to 62011. This should get the snake-farm to + throw away its old .pyc files, amongst others. + + Known values: + Python 1.5: 20121 + Python 1.5.1: 20121 + Python 1.5.2: 20121 + Python 2.0: 50823 + Python 2.0.1: 50823 + Python 2.1: 60202 + Python 2.1.1: 60202 + Python 2.1.2: 60202 + Python 2.2: 60717 + Python 2.3a0: 62011 + Python 2.3a0: 62021 + Python 2.3a0: 62011 (!) +*/ +/* we'll make our own magic number.... the D'ni way (whatever that means)*/ +#define MAGIC (18686 | ((long)'\r'<<16) | ((long)'\n'<<24)) + +/* Magic word as global; note that _PyImport_Init() can change the + value of this global to accommodate for alterations of how the + compiler works which are enabled by command line switches. */ +static long pyc_magic = MAGIC; + +/* See _PyImport_FixupExtension() below */ +static PyObject *extensions = NULL; + +/* This table is defined in config.c: */ +extern struct _inittab _PyImport_Inittab[]; + +struct _inittab *PyImport_Inittab = _PyImport_Inittab; + +/* these tables define the module suffixes that Python recognizes */ +struct filedescr * _PyImport_Filetab = NULL; + +#ifdef RISCOS +static const struct filedescr _PyImport_StandardFiletab[] = { + {"/py", "U", PY_SOURCE}, + {"/pyc", "rb", PY_COMPILED}, + {0, 0} +}; +#else +static const struct filedescr _PyImport_StandardFiletab[] = { + {".py", "U", PY_SOURCE}, +#ifdef MS_WINDOWS + {".pyw", "U", PY_SOURCE}, +#endif + {".pyc", "rb", PY_COMPILED}, + {0, 0} +}; +#endif + +/* Initialize things */ + +void +_PyImport_Init(void) +{ + const struct filedescr *scan; + struct filedescr *filetab; + int countD = 0; + int countS = 0; + + /* prepare _PyImport_Filetab: copy entries from + _PyImport_DynLoadFiletab and _PyImport_StandardFiletab. + */ + for (scan = _PyImport_DynLoadFiletab; scan->suffix != NULL; ++scan) + ++countD; + for (scan = _PyImport_StandardFiletab; scan->suffix != NULL; ++scan) + ++countS; + filetab = PyMem_NEW(struct filedescr, countD + countS + 1); + memcpy(filetab, _PyImport_DynLoadFiletab, + countD * sizeof(struct filedescr)); + memcpy(filetab + countD, _PyImport_StandardFiletab, + countS * sizeof(struct filedescr)); + filetab[countD + countS].suffix = NULL; + + _PyImport_Filetab = filetab; + + if (Py_OptimizeFlag) { + /* Replace ".pyc" with ".pyo" in _PyImport_Filetab */ + for (; filetab->suffix != NULL; filetab++) { +#ifndef RISCOS + if (strcmp(filetab->suffix, ".pyc") == 0) + filetab->suffix = ".pyo"; +#else + if (strcmp(filetab->suffix, "/pyc") == 0) + filetab->suffix = "/pyo"; +#endif + } + } + + if (Py_UnicodeFlag) { + /* Fix the pyc_magic so that byte compiled code created + using the all-Unicode method doesn't interfere with + code created in normal operation mode. */ + pyc_magic = MAGIC + 1; + } +} + +void +_PyImportHooks_Init(void) +{ + PyObject *v, *path_hooks = NULL, *zimpimport; + int err = 0; + + /* adding sys.path_hooks and sys.path_importer_cache, setting up + zipimport */ + + if (Py_VerboseFlag) + PySys_WriteStderr("# installing zipimport hook\n"); + + v = PyList_New(0); + if (v == NULL) + goto error; + err = PySys_SetObject("meta_path", v); + Py_DECREF(v); + if (err) + goto error; + v = PyDict_New(); + if (v == NULL) + goto error; + err = PySys_SetObject("path_importer_cache", v); + Py_DECREF(v); + if (err) + goto error; + path_hooks = PyList_New(0); + if (path_hooks == NULL) + goto error; + err = PySys_SetObject("path_hooks", path_hooks); + if (err) { + error: + PyErr_Print(); + Py_FatalError("initializing sys.meta_path, sys.path_hooks or " + "path_importer_cache failed"); + } + zimpimport = PyImport_ImportModule("zipimport"); + if (zimpimport == NULL) { + PyErr_Clear(); /* No zip import module -- okay */ + if (Py_VerboseFlag) + PySys_WriteStderr("# can't import zipimport\n"); + } + else { + PyObject *zipimporter = PyObject_GetAttrString(zimpimport, + "zipimporter"); + Py_DECREF(zimpimport); + if (zipimporter == NULL) { + PyErr_Clear(); /* No zipimporter object -- okay */ + if (Py_VerboseFlag) + PySys_WriteStderr( + "# can't import zipimport.zipimporter\n"); + } + else { + /* sys.path_hooks.append(zipimporter) */ + err = PyList_Append(path_hooks, zipimporter); + Py_DECREF(zipimporter); + if (err) + goto error; + if (Py_VerboseFlag) + PySys_WriteStderr( + "# installed zipimport hook\n"); + } + } + Py_DECREF(path_hooks); +} + +void +_PyImport_Fini(void) +{ + Py_XDECREF(extensions); + extensions = NULL; + PyMem_DEL(_PyImport_Filetab); + _PyImport_Filetab = NULL; +} + + +/* Locking primitives to prevent parallel imports of the same module + in different threads to return with a partially loaded module. + These calls are serialized by the global interpreter lock. */ + +#ifdef WITH_THREAD + +#include "pythread.h" + +static PyThread_type_lock import_lock = 0; +static long import_lock_thread = -1; +static int import_lock_level = 0; + +static void +lock_import(void) +{ + long me = PyThread_get_thread_ident(); + if (me == -1) + return; /* Too bad */ + if (import_lock == NULL) + import_lock = PyThread_allocate_lock(); + if (import_lock_thread == me) { + import_lock_level++; + return; + } + if (import_lock_thread != -1 || !PyThread_acquire_lock(import_lock, 0)) + { + PyThreadState *tstate = PyEval_SaveThread(); + PyThread_acquire_lock(import_lock, 1); + PyEval_RestoreThread(tstate); + } + import_lock_thread = me; + import_lock_level = 1; +} + +static int +unlock_import(void) +{ + long me = PyThread_get_thread_ident(); + if (me == -1) + return 0; /* Too bad */ + if (import_lock_thread != me) + return -1; + import_lock_level--; + if (import_lock_level == 0) { + import_lock_thread = -1; + PyThread_release_lock(import_lock); + } + return 1; +} + +#else + +#define lock_import() +#define unlock_import() 0 + +#endif + +static PyObject * +imp_lock_held(PyObject *self, PyObject *noargs) +{ +#ifdef WITH_THREAD + return PyBool_FromLong(import_lock_thread != -1); +#else + return PyBool_FromLong(0); +#endif +} + +static PyObject * +imp_acquire_lock(PyObject *self, PyObject *noargs) +{ +#ifdef WITH_THREAD + lock_import(); +#endif + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +imp_release_lock(PyObject *self, PyObject *noargs) +{ +#ifdef WITH_THREAD + if (unlock_import() < 0) { + PyErr_SetString(PyExc_RuntimeError, + "not holding the import lock"); + return NULL; + } +#endif + Py_INCREF(Py_None); + return Py_None; +} + +/* Helper for sys */ + +PyObject * +PyImport_GetModuleDict(void) +{ + PyInterpreterState *interp = PyThreadState_Get()->interp; + if (interp->modules == NULL) + Py_FatalError("PyImport_GetModuleDict: no module dictionary!"); + return interp->modules; +} + + +/* List of names to clear in sys */ +static char* sys_deletes[] = { + "path", "argv", "ps1", "ps2", "exitfunc", + "exc_type", "exc_value", "exc_traceback", + "last_type", "last_value", "last_traceback", + "path_hooks", "path_importer_cache", "meta_path", + NULL +}; + +static char* sys_files[] = { + "stdin", "__stdin__", + "stdout", "__stdout__", + "stderr", "__stderr__", + NULL +}; + + +/* Un-initialize things, as good as we can */ + +void +PyImport_Cleanup(void) +{ + int pos, ndone; + char *name; + PyObject *key, *value, *dict; + PyInterpreterState *interp = PyThreadState_Get()->interp; + PyObject *modules = interp->modules; + + if (modules == NULL) + return; /* Already done */ + + /* Delete some special variables first. These are common + places where user values hide and people complain when their + destructors fail. Since the modules containing them are + deleted *last* of all, they would come too late in the normal + destruction order. Sigh. */ + + value = PyDict_GetItemString(modules, "__builtin__"); + if (value != NULL && PyModule_Check(value)) { + dict = PyModule_GetDict(value); + if (Py_VerboseFlag) + PySys_WriteStderr("# clear __builtin__._\n"); + PyDict_SetItemString(dict, "_", Py_None); + } + value = PyDict_GetItemString(modules, "sys"); + if (value != NULL && PyModule_Check(value)) { + char **p; + PyObject *v; + dict = PyModule_GetDict(value); + for (p = sys_deletes; *p != NULL; p++) { + if (Py_VerboseFlag) + PySys_WriteStderr("# clear sys.%s\n", *p); + PyDict_SetItemString(dict, *p, Py_None); + } + for (p = sys_files; *p != NULL; p+=2) { + if (Py_VerboseFlag) + PySys_WriteStderr("# restore sys.%s\n", *p); + v = PyDict_GetItemString(dict, *(p+1)); + if (v == NULL) + v = Py_None; + PyDict_SetItemString(dict, *p, v); + } + } + + /* First, delete __main__ */ + value = PyDict_GetItemString(modules, "__main__"); + if (value != NULL && PyModule_Check(value)) { + if (Py_VerboseFlag) + PySys_WriteStderr("# cleanup __main__\n"); + _PyModule_Clear(value); + PyDict_SetItemString(modules, "__main__", Py_None); + } + + /* The special treatment of __builtin__ here is because even + when it's not referenced as a module, its dictionary is + referenced by almost every module's __builtins__. Since + deleting a module clears its dictionary (even if there are + references left to it), we need to delete the __builtin__ + module last. Likewise, we don't delete sys until the very + end because it is implicitly referenced (e.g. by print). + + Also note that we 'delete' modules by replacing their entry + in the modules dict with None, rather than really deleting + them; this avoids a rehash of the modules dictionary and + also marks them as "non existent" so they won't be + re-imported. */ + + /* Next, repeatedly delete modules with a reference count of + one (skipping __builtin__ and sys) and delete them */ + do { + ndone = 0; + pos = 0; + while (PyDict_Next(modules, &pos, &key, &value)) { + if (value->ob_refcnt != 1) + continue; + if (PyString_Check(key) && PyModule_Check(value)) { + name = PyString_AS_STRING(key); + if (strcmp(name, "__builtin__") == 0) + continue; + if (strcmp(name, "sys") == 0) + continue; + if (Py_VerboseFlag) + PySys_WriteStderr( + "# cleanup[1] %s\n", name); + _PyModule_Clear(value); + PyDict_SetItem(modules, key, Py_None); + ndone++; + } + } + } while (ndone > 0); + + /* Next, delete all modules (still skipping __builtin__ and sys) */ + pos = 0; + while (PyDict_Next(modules, &pos, &key, &value)) { + if (PyString_Check(key) && PyModule_Check(value)) { + name = PyString_AS_STRING(key); + if (strcmp(name, "__builtin__") == 0) + continue; + if (strcmp(name, "sys") == 0) + continue; + if (Py_VerboseFlag) + PySys_WriteStderr("# cleanup[2] %s\n", name); + _PyModule_Clear(value); + PyDict_SetItem(modules, key, Py_None); + } + } + + /* Next, delete sys and __builtin__ (in that order) */ + value = PyDict_GetItemString(modules, "sys"); + if (value != NULL && PyModule_Check(value)) { + if (Py_VerboseFlag) + PySys_WriteStderr("# cleanup sys\n"); + _PyModule_Clear(value); + PyDict_SetItemString(modules, "sys", Py_None); + } + value = PyDict_GetItemString(modules, "__builtin__"); + if (value != NULL && PyModule_Check(value)) { + if (Py_VerboseFlag) + PySys_WriteStderr("# cleanup __builtin__\n"); + _PyModule_Clear(value); + PyDict_SetItemString(modules, "__builtin__", Py_None); + } + + /* Finally, clear and delete the modules directory */ + PyDict_Clear(modules); + interp->modules = NULL; + Py_DECREF(modules); +} + + +/* Helper for pythonrun.c -- return magic number */ + +long +PyImport_GetMagicNumber(void) +{ + return pyc_magic; +} + + +/* Magic for extension modules (built-in as well as dynamically + loaded). To prevent initializing an extension module more than + once, we keep a static dictionary 'extensions' keyed by module name + (for built-in modules) or by filename (for dynamically loaded + modules), containing these modules. A copy of the module's + dictionary is stored by calling _PyImport_FixupExtension() + immediately after the module initialization function succeeds. A + copy can be retrieved from there by calling + _PyImport_FindExtension(). */ + +PyObject * +_PyImport_FixupExtension(char *name, char *filename) +{ + PyObject *modules, *mod, *dict, *copy; + if (extensions == NULL) { + extensions = PyDict_New(); + if (extensions == NULL) + return NULL; + } + modules = PyImport_GetModuleDict(); + mod = PyDict_GetItemString(modules, name); + if (mod == NULL || !PyModule_Check(mod)) { + PyErr_Format(PyExc_SystemError, + "_PyImport_FixupExtension: module %.200s not loaded", name); + return NULL; + } + dict = PyModule_GetDict(mod); + if (dict == NULL) + return NULL; + copy = PyDict_Copy(dict); + if (copy == NULL) + return NULL; + PyDict_SetItemString(extensions, filename, copy); + Py_DECREF(copy); + return copy; +} + +PyObject * +_PyImport_FindExtension(char *name, char *filename) +{ + PyObject *dict, *mod, *mdict; + if (extensions == NULL) + return NULL; + dict = PyDict_GetItemString(extensions, filename); + if (dict == NULL) + return NULL; + mod = PyImport_AddModule(name); + if (mod == NULL) + return NULL; + mdict = PyModule_GetDict(mod); + if (mdict == NULL) + return NULL; + if (PyDict_Update(mdict, dict)) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # previously loaded (%s)\n", + name, filename); + return mod; +} + + +/* Get the module object corresponding to a module name. + First check the modules dictionary if there's one there, + if not, create a new one and insert it in the modules dictionary. + Because the former action is most common, THIS DOES NOT RETURN A + 'NEW' REFERENCE! */ + +PyObject * +PyImport_AddModule(char *name) +{ + PyObject *modules = PyImport_GetModuleDict(); + PyObject *m; + + if ((m = PyDict_GetItemString(modules, name)) != NULL && + PyModule_Check(m)) + return m; + m = PyModule_New(name); + if (m == NULL) + return NULL; + if (PyDict_SetItemString(modules, name, m) != 0) { + Py_DECREF(m); + return NULL; + } + Py_DECREF(m); /* Yes, it still exists, in modules! */ + + return m; +} + + +/* Execute a code object in a module and return the module object + WITH INCREMENTED REFERENCE COUNT */ + +PyObject * +PyImport_ExecCodeModule(char *name, PyObject *co) +{ + return PyImport_ExecCodeModuleEx(name, co, (char *)NULL); +} + +PyObject * +PyImport_ExecCodeModuleEx(char *name, PyObject *co, char *pathname) +{ + PyObject *modules = PyImport_GetModuleDict(); + PyObject *m, *d, *v; + + m = PyImport_AddModule(name); + if (m == NULL) + return NULL; + d = PyModule_GetDict(m); + if (PyDict_GetItemString(d, "__builtins__") == NULL) { + if (PyDict_SetItemString(d, "__builtins__", + PyEval_GetBuiltins()) != 0) + return NULL; + } + /* Remember the filename as the __file__ attribute */ + v = NULL; + if (pathname != NULL) { + v = PyString_FromString(pathname); + if (v == NULL) + PyErr_Clear(); + } + if (v == NULL) { + v = ((PyCodeObject *)co)->co_filename; + Py_INCREF(v); + } + if (PyDict_SetItemString(d, "__file__", v) != 0) + PyErr_Clear(); /* Not important enough to report */ + Py_DECREF(v); + + v = PyEval_EvalCode((PyCodeObject *)co, d, d); + if (v == NULL) + return NULL; + Py_DECREF(v); + + if ((m = PyDict_GetItemString(modules, name)) == NULL) { + PyErr_Format(PyExc_ImportError, + "Loaded module %.200s not found in sys.modules", + name); + return NULL; + } + + Py_INCREF(m); + + return m; +} + + +/* Given a pathname for a Python source file, fill a buffer with the + pathname for the corresponding compiled file. Return the pathname + for the compiled file, or NULL if there's no space in the buffer. + Doesn't set an exception. */ + +static char * +make_compiled_pathname(char *pathname, char *buf, size_t buflen) +{ + size_t len = strlen(pathname); + if (len+2 > buflen) + return NULL; + +#ifdef MS_WINDOWS + /* Treat .pyw as if it were .py. The case of ".pyw" must match + that used in _PyImport_StandardFiletab. */ + if (len >= 4 && strcmp(&pathname[len-4], ".pyw") == 0) + --len; /* pretend 'w' isn't there */ +#endif + memcpy(buf, pathname, len); + buf[len] = Py_OptimizeFlag ? 'o' : 'c'; + buf[len+1] = '\0'; + + return buf; +} + + +/* Given a pathname for a Python source file, its time of last + modification, and a pathname for a compiled file, check whether the + compiled file represents the same version of the source. If so, + return a FILE pointer for the compiled file, positioned just after + the header; if not, return NULL. + Doesn't set an exception. */ + +static FILE * +check_compiled_module(char *pathname, long mtime, char *cpathname) +{ + FILE *fp; + long magic; + long pyc_mtime; + + fp = fopen(cpathname, "rb"); + if (fp == NULL) + return NULL; + magic = PyMarshal_ReadLongFromFile(fp); + if (magic != pyc_magic) { + if (Py_VerboseFlag) + PySys_WriteStderr("# %s has bad magic\n", cpathname); + fclose(fp); + return NULL; + } + pyc_mtime = PyMarshal_ReadLongFromFile(fp); + if (pyc_mtime != mtime) { + if (Py_VerboseFlag) + PySys_WriteStderr("# %s has bad mtime\n", cpathname); + fclose(fp); + return NULL; + } + if (Py_VerboseFlag) + PySys_WriteStderr("# %s matches %s\n", cpathname, pathname); + return fp; +} + + +/* Read a code object from a file and check it for validity */ + +static PyCodeObject * +read_compiled_module(char *cpathname, FILE *fp) +{ + PyObject *co; + + co = PyMarshal_ReadLastObjectFromFile(fp); + /* Ugly: rd_object() may return NULL with or without error */ + if (co == NULL || !PyCode_Check(co)) { + if (!PyErr_Occurred()) + PyErr_Format(PyExc_ImportError, + "Non-code object in %.200s", cpathname); + Py_XDECREF(co); + return NULL; + } + return (PyCodeObject *)co; +} + + +/* Load a module from a compiled file, execute it, and return its + module object WITH INCREMENTED REFERENCE COUNT */ + +static PyObject * +load_compiled_module(char *name, char *cpathname, FILE *fp) +{ + long magic; + PyCodeObject *co; + PyObject *m; + + magic = PyMarshal_ReadLongFromFile(fp); + if (magic != pyc_magic) { + PyErr_Format(PyExc_ImportError, + "Bad magic number in %.200s", cpathname); + return NULL; + } + (void) PyMarshal_ReadLongFromFile(fp); + co = read_compiled_module(cpathname, fp); + if (co == NULL) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # precompiled from %s\n", + name, cpathname); + m = PyImport_ExecCodeModuleEx(name, (PyObject *)co, cpathname); + Py_DECREF(co); + + return m; +} + +/* Parse a source file and return the corresponding code object */ + +static PyCodeObject * +parse_source_module(char *pathname, FILE *fp) +{ + PyCodeObject *co; + node *n; + + n = PyParser_SimpleParseFile(fp, pathname, Py_file_input); + if (n == NULL) + return NULL; + co = PyNode_Compile(n, pathname); + PyNode_Free(n); + + return co; +} + + +/* Helper to open a bytecode file for writing in exclusive mode */ + +static FILE * +open_exclusive(char *filename) +{ +#if defined(O_EXCL)&&defined(O_CREAT)&&defined(O_WRONLY)&&defined(O_TRUNC)&&!defined(_DEBUG) + /* Use O_EXCL to avoid a race condition when another process tries to + write the same file. When that happens, our open() call fails, + which is just fine (since it's only a cache). + XXX If the file exists and is writable but the directory is not + writable, the file will never be written. Oh well. + */ + int fd; + (void) unlink(filename); + fd = open(filename, O_EXCL|O_CREAT|O_WRONLY|O_TRUNC +#ifdef O_BINARY + |O_BINARY /* necessary for Windows */ +#endif +#ifdef __VMS + , 0666, "ctxt=bin", "shr=nil"); +#else + , 0666); +#endif + if (fd < 0) + return NULL; + return fdopen(fd, "wb"); +#else + /* Best we can do -- on Windows this can't happen anyway */ + return fopen(filename, "wb"); +#endif +} + + +/* Write a compiled module to a file, placing the time of last + modification of its source into the header. + Errors are ignored, if a write error occurs an attempt is made to + remove the file. */ + +static void +write_compiled_module(PyCodeObject *co, char *cpathname, long mtime) +{ + FILE *fp; + + fp = open_exclusive(cpathname); + if (fp == NULL) { + if (Py_VerboseFlag) + PySys_WriteStderr( + "# can't create %s\n", cpathname); + return; + } + PyMarshal_WriteLongToFile(pyc_magic, fp); + /* First write a 0 for mtime */ + PyMarshal_WriteLongToFile(0L, fp); + PyMarshal_WriteObjectToFile((PyObject *)co, fp); + if (ferror(fp)) { + if (Py_VerboseFlag) + PySys_WriteStderr("# can't write %s\n", cpathname); + /* Don't keep partial file */ + fclose(fp); + (void) unlink(cpathname); + return; + } + /* Now write the true mtime */ + fseek(fp, 4L, 0); + PyMarshal_WriteLongToFile(mtime, fp); + fflush(fp); + fclose(fp); + if (Py_VerboseFlag) + PySys_WriteStderr("# wrote %s\n", cpathname); +#ifdef macintosh + PyMac_setfiletype(cpathname, 'Pyth', 'PYC '); +#endif +} + + +/* Load a source module from a given file and return its module + object WITH INCREMENTED REFERENCE COUNT. If there's a matching + byte-compiled file, use that instead. */ + +static PyObject * +load_source_module(char *name, char *pathname, FILE *fp) +{ + time_t mtime; + FILE *fpc; + char buf[MAXPATHLEN+1]; + char *cpathname; + PyCodeObject *co; + PyObject *m; + + mtime = PyOS_GetLastModificationTime(pathname, fp); + if (mtime == (time_t)(-1)) + return NULL; +#if SIZEOF_TIME_T > 4 + /* Python's .pyc timestamp handling presumes that the timestamp fits + in 4 bytes. This will be fine until sometime in the year 2038, + when a 4-byte signed time_t will overflow. + */ + if (mtime >> 32) { + PyErr_SetString(PyExc_OverflowError, + "modification time overflows a 4 byte field"); + return NULL; + } +#endif + cpathname = make_compiled_pathname(pathname, buf, + (size_t)MAXPATHLEN + 1); + if (cpathname != NULL && + (fpc = check_compiled_module(pathname, mtime, cpathname))) { + co = read_compiled_module(cpathname, fpc); + fclose(fpc); + if (co == NULL) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # precompiled from %s\n", + name, cpathname); + pathname = cpathname; + } + else { + co = parse_source_module(pathname, fp); + if (co == NULL) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # from %s\n", + name, pathname); + write_compiled_module(co, cpathname, mtime); + } + m = PyImport_ExecCodeModuleEx(name, (PyObject *)co, pathname); + Py_DECREF(co); + + return m; +} + + +/* Forward */ +static PyObject *load_module(char *, FILE *, char *, int, PyObject *); +static struct filedescr *find_module(char *, char *, PyObject *, + char *, size_t, FILE **, PyObject **); +static struct _frozen *find_frozen(char *name); + +/* Load a package and return its module object WITH INCREMENTED + REFERENCE COUNT */ + +static PyObject * +load_package(char *name, char *pathname) +{ + PyObject *m, *d, *file, *path; + int err; + char buf[MAXPATHLEN+1]; + FILE *fp = NULL; + struct filedescr *fdp; + + m = PyImport_AddModule(name); + if (m == NULL) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # directory %s\n", + name, pathname); + d = PyModule_GetDict(m); + file = PyString_FromString(pathname); + if (file == NULL) + return NULL; + path = Py_BuildValue("[O]", file); + if (path == NULL) { + Py_DECREF(file); + return NULL; + } + err = PyDict_SetItemString(d, "__file__", file); + if (err == 0) + err = PyDict_SetItemString(d, "__path__", path); + if (err != 0) { + m = NULL; + goto cleanup; + } + buf[0] = '\0'; + fdp = find_module(name, "__init__", path, buf, sizeof(buf), &fp, NULL); + if (fdp == NULL) { + if (PyErr_ExceptionMatches(PyExc_ImportError)) { + PyErr_Clear(); + } + else + m = NULL; + goto cleanup; + } + m = load_module(name, fp, buf, fdp->type, NULL); + if (fp != NULL) + fclose(fp); + cleanup: + Py_XDECREF(path); + Py_XDECREF(file); + return m; +} + + +/* Helper to test for built-in module */ + +static int +is_builtin(char *name) +{ + int i; + for (i = 0; PyImport_Inittab[i].name != NULL; i++) { + if (strcmp(name, PyImport_Inittab[i].name) == 0) { + if (PyImport_Inittab[i].initfunc == NULL) + return -1; + else + return 1; + } + } + return 0; +} + + +/* Return an importer object for a sys.path/pkg.__path__ item 'p', + possibly by fetching it from the path_importer_cache dict. If it + wasn't yet cached, traverse path_hooks until it a hook is found + that can handle the path item. Return None if no hook could; + this tells our caller it should fall back to the builtin + import mechanism. Cache the result in path_importer_cache. + Returns a borrowed reference. */ + +static PyObject * +get_path_importer(PyObject *path_importer_cache, PyObject *path_hooks, + PyObject *p) +{ + PyObject *importer; + int j, nhooks; + + /* These conditions are the caller's responsibility: */ + assert(PyList_Check(path_hooks)); + assert(PyDict_Check(path_importer_cache)); + + nhooks = PyList_Size(path_hooks); + if (nhooks < 0) + return NULL; /* Shouldn't happen */ + + importer = PyDict_GetItem(path_importer_cache, p); + if (importer != NULL) + return importer; + + /* set path_importer_cache[p] to None to avoid recursion */ + if (PyDict_SetItem(path_importer_cache, p, Py_None) != 0) + return NULL; + + for (j = 0; j < nhooks; j++) { + PyObject *hook = PyList_GetItem(path_hooks, j); + if (hook == NULL) + return NULL; + importer = PyObject_CallFunction(hook, "O", p); + if (importer != NULL) + break; + + if (!PyErr_ExceptionMatches(PyExc_ImportError)) { + return NULL; + } + PyErr_Clear(); + } + if (importer == NULL) + importer = Py_None; + else if (importer != Py_None) { + int err = PyDict_SetItem(path_importer_cache, p, importer); + Py_DECREF(importer); + if (err != 0) + return NULL; + } + return importer; +} + +/* Search the path (default sys.path) for a module. Return the + corresponding filedescr struct, and (via return arguments) the + pathname and an open file. Return NULL if the module is not found. */ + +#ifdef MS_COREDLL +extern FILE *PyWin_FindRegisteredModule(const char *, struct filedescr **, + char *, int); +#endif + +static int case_ok(char *, int, int, char *); +static int find_init_module(char *); /* Forward */ +static struct filedescr importhookdescr = {"", "", IMP_HOOK}; + +static struct filedescr * +find_module(char *fullname, char *subname, PyObject *path, char *buf, + size_t buflen, FILE **p_fp, PyObject **p_loader) +{ + int i, npath; + size_t len, namelen; + struct filedescr *fdp = NULL; + char *filemode; + FILE *fp = NULL; + PyObject *path_hooks, *path_importer_cache; +#ifndef RISCOS + struct stat statbuf; +#endif + static struct filedescr fd_frozen = {"", "", PY_FROZEN}; + static struct filedescr fd_builtin = {"", "", C_BUILTIN}; + static struct filedescr fd_package = {"", "", PKG_DIRECTORY}; + char name[MAXPATHLEN+1]; +#if defined(PYOS_OS2) + size_t saved_len; + size_t saved_namelen; + char *saved_buf = NULL; +#endif + if (p_loader != NULL) + *p_loader = NULL; + + if (strlen(subname) > MAXPATHLEN) { + PyErr_SetString(PyExc_OverflowError, + "module name is too long"); + return NULL; + } + strcpy(name, subname); + + /* sys.meta_path import hook */ + if (p_loader != NULL) { + PyObject *meta_path; + + meta_path = PySys_GetObject("meta_path"); + if (meta_path == NULL || !PyList_Check(meta_path)) { + PyErr_SetString(PyExc_ImportError, + "sys.meta_path must be a list of " + "import hooks"); + return NULL; + } + Py_INCREF(meta_path); /* zap guard */ + npath = PyList_Size(meta_path); + for (i = 0; i < npath; i++) { + PyObject *loader; + PyObject *hook = PyList_GetItem(meta_path, i); + loader = PyObject_CallMethod(hook, "find_module", + "sO", fullname, + path != NULL ? + path : Py_None); + if (loader == NULL) { + Py_DECREF(meta_path); + return NULL; /* true error */ + } + if (loader != Py_None) { + /* a loader was found */ + *p_loader = loader; + Py_DECREF(meta_path); + return &importhookdescr; + } + Py_DECREF(loader); + } + Py_DECREF(meta_path); + } + + if (path != NULL && PyString_Check(path)) { + /* The only type of submodule allowed inside a "frozen" + package are other frozen modules or packages. */ + if (PyString_Size(path) + 1 + strlen(name) >= (size_t)buflen) { + PyErr_SetString(PyExc_ImportError, + "full frozen module name too long"); + return NULL; + } + strcpy(buf, PyString_AsString(path)); + strcat(buf, "."); + strcat(buf, name); + strcpy(name, buf); +#ifdef macintosh + /* Freezing on the mac works different, and the modules are + ** actually on sys.path. So we don't take the quick exit but + ** continue with the normal flow. + */ + path = NULL; +#else + if (find_frozen(name) != NULL) { + strcpy(buf, name); + return &fd_frozen; + } + PyErr_Format(PyExc_ImportError, + "No frozen submodule named %.200s", name); + return NULL; +#endif + } + if (path == NULL) { + if (is_builtin(name)) { + strcpy(buf, name); + return &fd_builtin; + } + if ((find_frozen(name)) != NULL) { + strcpy(buf, name); + return &fd_frozen; + } + +#ifdef MS_COREDLL + fp = PyWin_FindRegisteredModule(name, &fdp, buf, buflen); + if (fp != NULL) { + *p_fp = fp; + return fdp; + } +#endif + path = PySys_GetObject("path"); + } + if (path == NULL || !PyList_Check(path)) { + PyErr_SetString(PyExc_ImportError, + "sys.path must be a list of directory names"); + return NULL; + } + + path_hooks = PySys_GetObject("path_hooks"); + if (path_hooks == NULL || !PyList_Check(path_hooks)) { + PyErr_SetString(PyExc_ImportError, + "sys.path_hooks must be a list of " + "import hooks"); + return NULL; + } + path_importer_cache = PySys_GetObject("path_importer_cache"); + if (path_importer_cache == NULL || + !PyDict_Check(path_importer_cache)) { + PyErr_SetString(PyExc_ImportError, + "sys.path_importer_cache must be a dict"); + return NULL; + } + + npath = PyList_Size(path); + namelen = strlen(name); + for (i = 0; i < npath; i++) { + PyObject *copy = NULL; + PyObject *v = PyList_GetItem(path, i); +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(v)) { + copy = PyUnicode_Encode(PyUnicode_AS_UNICODE(v), + PyUnicode_GET_SIZE(v), Py_FileSystemDefaultEncoding, NULL); + if (copy == NULL) + return NULL; + v = copy; + } + else +#endif + if (!PyString_Check(v)) + continue; + len = PyString_Size(v); + if (len + 2 + namelen + MAXSUFFIXSIZE >= buflen) { + Py_XDECREF(copy); + continue; /* Too long */ + } + strcpy(buf, PyString_AsString(v)); + if (strlen(buf) != len) { + Py_XDECREF(copy); + continue; /* v contains '\0' */ + } + + /* sys.path_hooks import hook */ + if (p_loader != NULL) { + PyObject *importer; + + importer = get_path_importer(path_importer_cache, + path_hooks, v); + if (importer == NULL) + return NULL; + /* Note: importer is a borrowed reference */ + if (importer != Py_None) { + PyObject *loader; + loader = PyObject_CallMethod(importer, + "find_module", + "s", fullname); + if (loader == NULL) + return NULL; /* error */ + if (loader != Py_None) { + /* a loader was found */ + *p_loader = loader; + return &importhookdescr; + } + Py_DECREF(loader); + } + /* no hook was successful, use builtin import */ + } + +#ifdef macintosh + /* + ** Speedup: each sys.path item is interned, and + ** FindResourceModule remembers which items refer to + ** folders (so we don't have to bother trying to look + ** into them for resources). We only do this for string + ** items. + */ + if (PyString_Check(PyList_GET_ITEM(path, i))) { + PyString_InternInPlace(&PyList_GET_ITEM(path, i)); + v = PyList_GET_ITEM(path, i); + if (PyMac_FindResourceModule((PyStringObject *)v, name, buf)) { + static struct filedescr resfiledescr = + {"", "", PY_RESOURCE}; + + Py_XDECREF(copy); + return &resfiledescr; + } + if (PyMac_FindCodeResourceModule((PyStringObject *)v, name, buf)) { + static struct filedescr resfiledescr = + {"", "", PY_CODERESOURCE}; + + Py_XDECREF(copy); + return &resfiledescr; + } + } +#endif + if (len > 0 && buf[len-1] != SEP +#ifdef ALTSEP + && buf[len-1] != ALTSEP +#endif + ) + buf[len++] = SEP; + strcpy(buf+len, name); + len += namelen; + + /* Check for package import (buf holds a directory name, + and there's an __init__ module in that directory */ +#ifdef HAVE_STAT + if (stat(buf, &statbuf) == 0 && /* it exists */ + S_ISDIR(statbuf.st_mode) && /* it's a directory */ + find_init_module(buf) && /* it has __init__.py */ + case_ok(buf, len, namelen, name)) { /* and case matches */ + Py_XDECREF(copy); + return &fd_package; + } +#else + /* XXX How are you going to test for directories? */ +#ifdef RISCOS + if (isdir(buf) && + find_init_module(buf) && + case_ok(buf, len, namelen, name)) { + Py_XDECREF(copy); + return &fd_package; + } +#endif +#endif +#ifdef macintosh + fdp = PyMac_FindModuleExtension(buf, &len, name); + if (fdp) { +#else +#if defined(PYOS_OS2) + /* take a snapshot of the module spec for restoration + * after the 8 character DLL hackery + */ + saved_buf = strdup(buf); + saved_len = len; + saved_namelen = namelen; +#endif /* PYOS_OS2 */ + for (fdp = _PyImport_Filetab; fdp->suffix != NULL; fdp++) { +#if defined(PYOS_OS2) + /* OS/2 limits DLLs to 8 character names (w/o + extension) + * so if the name is longer than that and its a + * dynamically loaded module we're going to try, + * truncate the name before trying + */ + if (strlen(subname) > 8) { + /* is this an attempt to load a C extension? */ + const struct filedescr *scan; + scan = _PyImport_DynLoadFiletab; + while (scan->suffix != NULL) { + if (!strcmp(scan->suffix, fdp->suffix)) + break; + else + scan++; + } + if (scan->suffix != NULL) { + /* yes, so truncate the name */ + namelen = 8; + len -= strlen(subname) - namelen; + buf[len] = '\0'; + } + } +#endif /* PYOS_OS2 */ + strcpy(buf+len, fdp->suffix); + if (Py_VerboseFlag > 1) + PySys_WriteStderr("# trying %s\n", buf); +#endif /* !macintosh */ + filemode = fdp->mode; + if (filemode[0] == 'U') + filemode = "r" PY_STDIOTEXTMODE; + fp = fopen(buf, filemode); + if (fp != NULL) { + if (case_ok(buf, len, namelen, name)) + break; + else { /* continue search */ + fclose(fp); + fp = NULL; + } + } +#if defined(PYOS_OS2) + /* restore the saved snapshot */ + strcpy(buf, saved_buf); + len = saved_len; + namelen = saved_namelen; +#endif + } +#if defined(PYOS_OS2) + /* don't need/want the module name snapshot anymore */ + if (saved_buf) + { + free(saved_buf); + saved_buf = NULL; + } +#endif + Py_XDECREF(copy); + if (fp != NULL) + break; + } + if (fp == NULL) { + PyErr_Format(PyExc_ImportError, + "No module named %.200s", name); + return NULL; + } + *p_fp = fp; + return fdp; +} + +/* case_ok(char* buf, int len, int namelen, char* name) + * The arguments here are tricky, best shown by example: + * /a/b/c/d/e/f/g/h/i/j/k/some_long_module_name.py\0 + * ^ ^ ^ ^ + * |--------------------- buf ---------------------| + * |------------------- len ------------------| + * |------ name -------| + * |----- namelen -----| + * buf is the full path, but len only counts up to (& exclusive of) the + * extension. name is the module name, also exclusive of extension. + * + * We've already done a successful stat() or fopen() on buf, so know that + * there's some match, possibly case-insensitive. + * + * case_ok() is to return 1 if there's a case-sensitive match for + * name, else 0. case_ok() is also to return 1 if envar PYTHONCASEOK + * exists. + * + * case_ok() is used to implement case-sensitive import semantics even + * on platforms with case-insensitive filesystems. It's trivial to implement + * for case-sensitive filesystems. It's pretty much a cross-platform + * nightmare for systems with case-insensitive filesystems. + */ + +/* First we may need a pile of platform-specific header files; the sequence + * of #if's here should match the sequence in the body of case_ok(). + */ +#if defined(MS_WINDOWS) || defined(__CYGWIN__) +#include +#ifdef __CYGWIN__ +#include +#endif + +#elif defined(DJGPP) +#include + +#elif defined(macintosh) +#include + +#elif defined(__MACH__) && defined(__APPLE__) && defined(HAVE_DIRENT_H) +#include +#include + +#elif defined(PYOS_OS2) +#define INCL_DOS +#define INCL_DOSERRORS +#define INCL_NOPMAPI +#include + +#elif defined(RISCOS) +#include "oslib/osfscontrol.h" +#endif + +static int +case_ok(char *buf, int len, int namelen, char *name) +{ +/* Pick a platform-specific implementation; the sequence of #if's here should + * match the sequence just above. + */ + +/* MS_WINDOWS || __CYGWIN__ */ +#if defined(MS_WINDOWS) || defined(__CYGWIN__) + WIN32_FIND_DATA data; + HANDLE h; +#ifdef __CYGWIN__ + char tempbuf[MAX_PATH]; +#endif + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + +#ifdef __CYGWIN__ + cygwin32_conv_to_win32_path(buf, tempbuf); + h = FindFirstFile(tempbuf, &data); +#else + h = FindFirstFile(buf, &data); +#endif + if (h == INVALID_HANDLE_VALUE) { + PyErr_Format(PyExc_NameError, + "Can't find file for module %.100s\n(filename %.300s)", + name, buf); + return 0; + } + FindClose(h); + return strncmp(data.cFileName, name, namelen) == 0; + +/* DJGPP */ +#elif defined(DJGPP) + struct ffblk ffblk; + int done; + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + + done = findfirst(buf, &ffblk, FA_ARCH|FA_RDONLY|FA_HIDDEN|FA_DIREC); + if (done) { + PyErr_Format(PyExc_NameError, + "Can't find file for module %.100s\n(filename %.300s)", + name, buf); + return 0; + } + return strncmp(ffblk.ff_name, name, namelen) == 0; + +/* macintosh */ +#elif defined(macintosh) + FSSpec fss; + OSErr err; + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + + err = FSMakeFSSpec(0, 0, Pstring(buf), &fss); + if (err) { + PyErr_Format(PyExc_NameError, + "Can't find file for module %.100s\n(filename %.300s)", + name, buf); + return 0; + } + return fss.name[0] >= namelen && + strncmp(name, (char *)fss.name+1, namelen) == 0; + +/* new-fangled macintosh (macosx) */ +#elif defined(__MACH__) && defined(__APPLE__) && defined(HAVE_DIRENT_H) + DIR *dirp; + struct dirent *dp; + char dirname[MAXPATHLEN + 1]; + const int dirlen = len - namelen - 1; /* don't want trailing SEP */ + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + + /* Copy the dir component into dirname; substitute "." if empty */ + if (dirlen <= 0) { + dirname[0] = '.'; + dirname[1] = '\0'; + } + else { + assert(dirlen <= MAXPATHLEN); + memcpy(dirname, buf, dirlen); + dirname[dirlen] = '\0'; + } + /* Open the directory and search the entries for an exact match. */ + dirp = opendir(dirname); + if (dirp) { + char *nameWithExt = buf + len - namelen; + while ((dp = readdir(dirp)) != NULL) { + const int thislen = +#ifdef _DIRENT_HAVE_D_NAMELEN + dp->d_namlen; +#else + strlen(dp->d_name); +#endif + if (thislen >= namelen && + strcmp(dp->d_name, nameWithExt) == 0) { + (void)closedir(dirp); + return 1; /* Found */ + } + } + (void)closedir(dirp); + } + return 0 ; /* Not found */ + +/* RISC OS */ +#elif defined(RISCOS) + char canon[MAXPATHLEN+1]; /* buffer for the canonical form of the path */ + char buf2[MAXPATHLEN+2]; + char *nameWithExt = buf+len-namelen; + int canonlen; + os_error *e; + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + + /* workaround: + append wildcard, otherwise case of filename wouldn't be touched */ + strcpy(buf2, buf); + strcat(buf2, "*"); + + e = xosfscontrol_canonicalise_path(buf2,canon,0,0,MAXPATHLEN+1,&canonlen); + canonlen = MAXPATHLEN+1-canonlen; + if (e || canonlen<=0 || canonlen>(MAXPATHLEN+1) ) + return 0; + if (strcmp(nameWithExt, canon+canonlen-strlen(nameWithExt))==0) + return 1; /* match */ + + return 0; + +/* OS/2 */ +#elif defined(PYOS_OS2) + HDIR hdir = 1; + ULONG srchcnt = 1; + FILEFINDBUF3 ffbuf; + APIRET rc; + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + + rc = DosFindFirst(buf, + &hdir, + FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_DIRECTORY, + &ffbuf, sizeof(ffbuf), + &srchcnt, + FIL_STANDARD); + if (rc != NO_ERROR) + return 0; + return strncmp(ffbuf.achName, name, namelen) == 0; + +/* assuming it's a case-sensitive filesystem, so there's nothing to do! */ +#else + return 1; + +#endif +} + + +#ifdef HAVE_STAT +/* Helper to look for __init__.py or __init__.py[co] in potential package */ +static int +find_init_module(char *buf) +{ + const size_t save_len = strlen(buf); + size_t i = save_len; + char *pname; /* pointer to start of __init__ */ + struct stat statbuf; + +/* For calling case_ok(buf, len, namelen, name): + * /a/b/c/d/e/f/g/h/i/j/k/some_long_module_name.py\0 + * ^ ^ ^ ^ + * |--------------------- buf ---------------------| + * |------------------- len ------------------| + * |------ name -------| + * |----- namelen -----| + */ + if (save_len + 13 >= MAXPATHLEN) + return 0; + buf[i++] = SEP; + pname = buf + i; + strcpy(pname, "__init__.py"); + if (stat(buf, &statbuf) == 0) { + if (case_ok(buf, + save_len + 9, /* len("/__init__") */ + 8, /* len("__init__") */ + pname)) { + buf[save_len] = '\0'; + return 1; + } + } + i += strlen(pname); + strcpy(buf+i, Py_OptimizeFlag ? "o" : "c"); + if (stat(buf, &statbuf) == 0) { + if (case_ok(buf, + save_len + 9, /* len("/__init__") */ + 8, /* len("__init__") */ + pname)) { + buf[save_len] = '\0'; + return 1; + } + } + buf[save_len] = '\0'; + return 0; +} + +#else + +#ifdef RISCOS +static int +find_init_module(buf) + char *buf; +{ + int save_len = strlen(buf); + int i = save_len; + + if (save_len + 13 >= MAXPATHLEN) + return 0; + buf[i++] = SEP; + strcpy(buf+i, "__init__/py"); + if (isfile(buf)) { + buf[save_len] = '\0'; + return 1; + } + + if (Py_OptimizeFlag) + strcpy(buf+i, "o"); + else + strcpy(buf+i, "c"); + if (isfile(buf)) { + buf[save_len] = '\0'; + return 1; + } + buf[save_len] = '\0'; + return 0; +} +#endif /*RISCOS*/ + +#endif /* HAVE_STAT */ + + +static int init_builtin(char *); /* Forward */ + +/* Load an external module using the default search path and return + its module object WITH INCREMENTED REFERENCE COUNT */ + +static PyObject * +load_module(char *name, FILE *fp, char *buf, int type, PyObject *loader) +{ + PyObject *modules; + PyObject *m; + int err; + + /* First check that there's an open file (if we need one) */ + switch (type) { + case PY_SOURCE: + case PY_COMPILED: + if (fp == NULL) { + PyErr_Format(PyExc_ValueError, + "file object required for import (type code %d)", + type); + return NULL; + } + } + + switch (type) { + + case PY_SOURCE: + m = load_source_module(name, buf, fp); + break; + + case PY_COMPILED: + m = load_compiled_module(name, buf, fp); + break; + +#ifdef HAVE_DYNAMIC_LOADING + case C_EXTENSION: + m = _PyImport_LoadDynamicModule(name, buf, fp); + break; +#endif + +#ifdef macintosh + case PY_RESOURCE: + m = PyMac_LoadResourceModule(name, buf); + break; + case PY_CODERESOURCE: + m = PyMac_LoadCodeResourceModule(name, buf); + break; +#endif + + case PKG_DIRECTORY: + m = load_package(name, buf); + break; + + case C_BUILTIN: + case PY_FROZEN: + if (buf != NULL && buf[0] != '\0') + name = buf; + if (type == C_BUILTIN) + err = init_builtin(name); + else + err = PyImport_ImportFrozenModule(name); + if (err < 0) + return NULL; + if (err == 0) { + PyErr_Format(PyExc_ImportError, + "Purported %s module %.200s not found", + type == C_BUILTIN ? + "builtin" : "frozen", + name); + return NULL; + } + modules = PyImport_GetModuleDict(); + m = PyDict_GetItemString(modules, name); + if (m == NULL) { + PyErr_Format( + PyExc_ImportError, + "%s module %.200s not properly initialized", + type == C_BUILTIN ? + "builtin" : "frozen", + name); + return NULL; + } + Py_INCREF(m); + break; + + case IMP_HOOK: { + if (loader == NULL) { + PyErr_SetString(PyExc_ImportError, + "import hook without loader"); + return NULL; + } + m = PyObject_CallMethod(loader, "load_module", "s", name); + break; + } + + default: + PyErr_Format(PyExc_ImportError, + "Don't know how to import %.200s (type code %d)", + name, type); + m = NULL; + + } + + return m; +} + + +/* Initialize a built-in module. + Return 1 for succes, 0 if the module is not found, and -1 with + an exception set if the initialization failed. */ + +static int +init_builtin(char *name) +{ + struct _inittab *p; + + if (_PyImport_FindExtension(name, name) != NULL) + return 1; + + for (p = PyImport_Inittab; p->name != NULL; p++) { + if (strcmp(name, p->name) == 0) { + if (p->initfunc == NULL) { + PyErr_Format(PyExc_ImportError, + "Cannot re-init internal module %.200s", + name); + return -1; + } + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # builtin\n", name); + (*p->initfunc)(); + if (PyErr_Occurred()) + return -1; + if (_PyImport_FixupExtension(name, name) == NULL) + return -1; + return 1; + } + } + return 0; +} + + +/* Frozen modules */ + +static struct _frozen * +find_frozen(char *name) +{ + struct _frozen *p; + + for (p = PyImport_FrozenModules; ; p++) { + if (p->name == NULL) + return NULL; + if (strcmp(p->name, name) == 0) + break; + } + return p; +} + +static PyObject * +get_frozen_object(char *name) +{ + struct _frozen *p = find_frozen(name); + int size; + + if (p == NULL) { + PyErr_Format(PyExc_ImportError, + "No such frozen object named %.200s", + name); + return NULL; + } + if (p->code == NULL) { + PyErr_Format(PyExc_ImportError, + "Excluded frozen object named %.200s", + name); + return NULL; + } + size = p->size; + if (size < 0) + size = -size; + return PyMarshal_ReadObjectFromString((char *)p->code, size); +} + +/* Initialize a frozen module. + Return 1 for succes, 0 if the module is not found, and -1 with + an exception set if the initialization failed. + This function is also used from frozenmain.c */ + +int +PyImport_ImportFrozenModule(char *name) +{ + struct _frozen *p = find_frozen(name); + PyObject *co; + PyObject *m; + int ispackage; + int size; + + if (p == NULL) + return 0; + if (p->code == NULL) { + PyErr_Format(PyExc_ImportError, + "Excluded frozen object named %.200s", + name); + return -1; + } + size = p->size; + ispackage = (size < 0); + if (ispackage) + size = -size; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # frozen%s\n", + name, ispackage ? " package" : ""); + co = PyMarshal_ReadObjectFromString((char *)p->code, size); + if (co == NULL) + return -1; + if (!PyCode_Check(co)) { + Py_DECREF(co); + PyErr_Format(PyExc_TypeError, + "frozen object %.200s is not a code object", + name); + return -1; + } + if (ispackage) { + /* Set __path__ to the package name */ + PyObject *d, *s; + int err; + m = PyImport_AddModule(name); + if (m == NULL) + return -1; + d = PyModule_GetDict(m); + s = PyString_InternFromString(name); + if (s == NULL) + return -1; + err = PyDict_SetItemString(d, "__path__", s); + Py_DECREF(s); + if (err != 0) + return err; + } + m = PyImport_ExecCodeModuleEx(name, co, ""); + Py_DECREF(co); + if (m == NULL) + return -1; + Py_DECREF(m); + return 1; +} + + +/* Import a module, either built-in, frozen, or external, and return + its module object WITH INCREMENTED REFERENCE COUNT */ + +PyObject * +PyImport_ImportModule(char *name) +{ + PyObject *pname; + PyObject *result; + + pname = PyString_FromString(name); + if (pname == NULL) + return NULL; + result = PyImport_Import(pname); + Py_DECREF(pname); + return result; +} + +/* Forward declarations for helper routines */ +static PyObject *get_parent(PyObject *globals, char *buf, int *p_buflen); +static PyObject *load_next(PyObject *mod, PyObject *altmod, + char **p_name, char *buf, int *p_buflen); +static int mark_miss(char *name); +static int ensure_fromlist(PyObject *mod, PyObject *fromlist, + char *buf, int buflen, int recursive); +static PyObject * import_submodule(PyObject *mod, char *name, char *fullname); + +/* The Magnum Opus of dotted-name import :-) */ + +static PyObject * +import_module_ex(char *name, PyObject *globals, PyObject *locals, + PyObject *fromlist) +{ + char buf[MAXPATHLEN+1]; + int buflen = 0; + PyObject *parent, *head, *next, *tail; + + parent = get_parent(globals, buf, &buflen); + if (parent == NULL) + return NULL; + + head = load_next(parent, Py_None, &name, buf, &buflen); + if (head == NULL) + return NULL; + + tail = head; + Py_INCREF(tail); + while (name) { + next = load_next(tail, tail, &name, buf, &buflen); + Py_DECREF(tail); + if (next == NULL) { + Py_DECREF(head); + return NULL; + } + tail = next; + } + + if (fromlist != NULL) { + if (fromlist == Py_None || !PyObject_IsTrue(fromlist)) + fromlist = NULL; + } + + if (fromlist == NULL) { + Py_DECREF(tail); + return head; + } + + Py_DECREF(head); + if (!ensure_fromlist(tail, fromlist, buf, buflen, 0)) { + Py_DECREF(tail); + return NULL; + } + + return tail; +} + +PyObject * +PyImport_ImportModuleEx(char *name, PyObject *globals, PyObject *locals, + PyObject *fromlist) +{ + PyObject *result; + lock_import(); + result = import_module_ex(name, globals, locals, fromlist); + if (unlock_import() < 0) { + Py_XDECREF(result); + PyErr_SetString(PyExc_RuntimeError, + "not holding the import lock"); + return NULL; + } + return result; +} + +static PyObject * +get_parent(PyObject *globals, char *buf, int *p_buflen) +{ + static PyObject *namestr = NULL; + static PyObject *pathstr = NULL; + PyObject *modname, *modpath, *modules, *parent; + + if (globals == NULL || !PyDict_Check(globals)) + return Py_None; + + if (namestr == NULL) { + namestr = PyString_InternFromString("__name__"); + if (namestr == NULL) + return NULL; + } + if (pathstr == NULL) { + pathstr = PyString_InternFromString("__path__"); + if (pathstr == NULL) + return NULL; + } + + *buf = '\0'; + *p_buflen = 0; + modname = PyDict_GetItem(globals, namestr); + if (modname == NULL || !PyString_Check(modname)) + return Py_None; + + modpath = PyDict_GetItem(globals, pathstr); + if (modpath != NULL) { + int len = PyString_GET_SIZE(modname); + if (len > MAXPATHLEN) { + PyErr_SetString(PyExc_ValueError, + "Module name too long"); + return NULL; + } + strcpy(buf, PyString_AS_STRING(modname)); + *p_buflen = len; + } + else { + char *start = PyString_AS_STRING(modname); + char *lastdot = strrchr(start, '.'); + size_t len; + if (lastdot == NULL) + return Py_None; + len = lastdot - start; + if (len >= MAXPATHLEN) { + PyErr_SetString(PyExc_ValueError, + "Module name too long"); + return NULL; + } + strncpy(buf, start, len); + buf[len] = '\0'; + *p_buflen = len; + } + + modules = PyImport_GetModuleDict(); + parent = PyDict_GetItemString(modules, buf); + if (parent == NULL) + parent = Py_None; + return parent; + /* We expect, but can't guarantee, if parent != None, that: + - parent.__name__ == buf + - parent.__dict__ is globals + If this is violated... Who cares? */ +} + +/* altmod is either None or same as mod */ +static PyObject * +load_next(PyObject *mod, PyObject *altmod, char **p_name, char *buf, + int *p_buflen) +{ + char *name = *p_name; + char *dot = strchr(name, '.'); + size_t len; + char *p; + PyObject *result; + + if (dot == NULL) { + *p_name = NULL; + len = strlen(name); + } + else { + *p_name = dot+1; + len = dot-name; + } + if (len == 0) { + PyErr_SetString(PyExc_ValueError, + "Empty module name"); + return NULL; + } + + p = buf + *p_buflen; + if (p != buf) + *p++ = '.'; + if (p+len-buf >= MAXPATHLEN) { + PyErr_SetString(PyExc_ValueError, + "Module name too long"); + return NULL; + } + strncpy(p, name, len); + p[len] = '\0'; + *p_buflen = p+len-buf; + + result = import_submodule(mod, p, buf); + if (result == Py_None && altmod != mod) { + Py_DECREF(result); + /* Here, altmod must be None and mod must not be None */ + result = import_submodule(altmod, p, p); + if (result != NULL && result != Py_None) { + if (mark_miss(buf) != 0) { + Py_DECREF(result); + return NULL; + } + strncpy(buf, name, len); + buf[len] = '\0'; + *p_buflen = len; + } + } + if (result == NULL) + return NULL; + + if (result == Py_None) { + Py_DECREF(result); + PyErr_Format(PyExc_ImportError, + "No module named %.200s", name); + return NULL; + } + + return result; +} + +static int +mark_miss(char *name) +{ + PyObject *modules = PyImport_GetModuleDict(); + return PyDict_SetItemString(modules, name, Py_None); +} + +static int +ensure_fromlist(PyObject *mod, PyObject *fromlist, char *buf, int buflen, + int recursive) +{ + int i; + + if (!PyObject_HasAttrString(mod, "__path__")) + return 1; + + for (i = 0; ; i++) { + PyObject *item = PySequence_GetItem(fromlist, i); + int hasit; + if (item == NULL) { + if (PyErr_ExceptionMatches(PyExc_IndexError)) { + PyErr_Clear(); + return 1; + } + return 0; + } + if (!PyString_Check(item)) { + PyErr_SetString(PyExc_TypeError, + "Item in ``from list'' not a string"); + Py_DECREF(item); + return 0; + } + if (PyString_AS_STRING(item)[0] == '*') { + PyObject *all; + Py_DECREF(item); + /* See if the package defines __all__ */ + if (recursive) + continue; /* Avoid endless recursion */ + all = PyObject_GetAttrString(mod, "__all__"); + if (all == NULL) + PyErr_Clear(); + else { + if (!ensure_fromlist(mod, all, buf, buflen, 1)) + return 0; + Py_DECREF(all); + } + continue; + } + hasit = PyObject_HasAttr(mod, item); + if (!hasit) { + char *subname = PyString_AS_STRING(item); + PyObject *submod; + char *p; + if (buflen + strlen(subname) >= MAXPATHLEN) { + PyErr_SetString(PyExc_ValueError, + "Module name too long"); + Py_DECREF(item); + return 0; + } + p = buf + buflen; + *p++ = '.'; + strcpy(p, subname); + submod = import_submodule(mod, subname, buf); + Py_XDECREF(submod); + if (submod == NULL) { + Py_DECREF(item); + return 0; + } + } + Py_DECREF(item); + } + + /* NOTREACHED */ +} + +static int +add_submodule(PyObject *mod, PyObject *submod, char *fullname, char *subname, + PyObject *modules) +{ + if (mod == Py_None) + return 1; + /* Irrespective of the success of this load, make a + reference to it in the parent package module. A copy gets + saved in the modules dictionary under the full name, so get a + reference from there, if need be. (The exception is when the + load failed with a SyntaxError -- then there's no trace in + sys.modules. In that case, of course, do nothing extra.) */ + if (submod == NULL) { + submod = PyDict_GetItemString(modules, fullname); + if (submod == NULL) + return 1; + } + if (PyModule_Check(mod)) { + /* We can't use setattr here since it can give a + * spurious warning if the submodule name shadows a + * builtin name */ + PyObject *dict = PyModule_GetDict(mod); + if (!dict) + return 0; + if (PyDict_SetItemString(dict, subname, submod) < 0) + return 0; + } + else { + if (PyObject_SetAttrString(mod, subname, submod) < 0) + return 0; + } + return 1; +} + +static PyObject * +import_submodule(PyObject *mod, char *subname, char *fullname) +{ + PyObject *modules = PyImport_GetModuleDict(); + PyObject *m = NULL; + + /* Require: + if mod == None: subname == fullname + else: mod.__name__ + "." + subname == fullname + */ + + if ((m = PyDict_GetItemString(modules, fullname)) != NULL) { + Py_INCREF(m); + } + else { + PyObject *path, *loader = NULL; + char buf[MAXPATHLEN+1]; + struct filedescr *fdp; + FILE *fp = NULL; + + if (mod == Py_None) + path = NULL; + else { + path = PyObject_GetAttrString(mod, "__path__"); + if (path == NULL) { + PyErr_Clear(); + Py_INCREF(Py_None); + return Py_None; + } + } + + buf[0] = '\0'; + fdp = find_module(fullname, subname, path, buf, MAXPATHLEN+1, + &fp, &loader); + Py_XDECREF(path); + if (fdp == NULL) { + if (!PyErr_ExceptionMatches(PyExc_ImportError)) + return NULL; + PyErr_Clear(); + Py_INCREF(Py_None); + return Py_None; + } + m = load_module(fullname, fp, buf, fdp->type, loader); + Py_XDECREF(loader); + if (fp) + fclose(fp); + if (!add_submodule(mod, m, fullname, subname, modules)) { + Py_XDECREF(m); + m = NULL; + } + } + + return m; +} + + +/* Re-import a module of any kind and return its module object, WITH + INCREMENTED REFERENCE COUNT */ + +PyObject * +PyImport_ReloadModule(PyObject *m) +{ + PyObject *modules = PyImport_GetModuleDict(); + PyObject *path = NULL; + char *name, *subname; + char buf[MAXPATHLEN+1]; + struct filedescr *fdp; + FILE *fp = NULL; + + if (m == NULL || !PyModule_Check(m)) { + PyErr_SetString(PyExc_TypeError, + "reload() argument must be module"); + return NULL; + } + name = PyModule_GetName(m); + if (name == NULL) + return NULL; + if (m != PyDict_GetItemString(modules, name)) { + PyErr_Format(PyExc_ImportError, + "reload(): module %.200s not in sys.modules", + name); + return NULL; + } + subname = strrchr(name, '.'); + if (subname == NULL) + subname = name; + else { + PyObject *parentname, *parent; + parentname = PyString_FromStringAndSize(name, (subname-name)); + if (parentname == NULL) + return NULL; + parent = PyDict_GetItem(modules, parentname); + Py_DECREF(parentname); + if (parent == NULL) { + PyErr_Format(PyExc_ImportError, + "reload(): parent %.200s not in sys.modules", + name); + return NULL; + } + subname++; + path = PyObject_GetAttrString(parent, "__path__"); + if (path == NULL) + PyErr_Clear(); + } + buf[0] = '\0'; + fdp = find_module(name, subname, path, buf, MAXPATHLEN+1, &fp, NULL); + Py_XDECREF(path); + if (fdp == NULL) + return NULL; + m = load_module(name, fp, buf, fdp->type, NULL); + if (fp) + fclose(fp); + return m; +} + + +/* Higher-level import emulator which emulates the "import" statement + more accurately -- it invokes the __import__() function from the + builtins of the current globals. This means that the import is + done using whatever import hooks are installed in the current + environment, e.g. by "rexec". + A dummy list ["__doc__"] is passed as the 4th argument so that + e.g. PyImport_Import(PyString_FromString("win32com.client.gencache")) + will return instead of . */ + +PyObject * +PyImport_Import(PyObject *module_name) +{ + static PyObject *silly_list = NULL; + static PyObject *builtins_str = NULL; + static PyObject *import_str = NULL; + PyObject *globals = NULL; + PyObject *import = NULL; + PyObject *builtins = NULL; + PyObject *r = NULL; + + /* Initialize constant string objects */ + if (silly_list == NULL) { + import_str = PyString_InternFromString("__import__"); + if (import_str == NULL) + return NULL; + builtins_str = PyString_InternFromString("__builtins__"); + if (builtins_str == NULL) + return NULL; + silly_list = Py_BuildValue("[s]", "__doc__"); + if (silly_list == NULL) + return NULL; + } + + /* Get the builtins from current globals */ + globals = PyEval_GetGlobals(); + if (globals != NULL) { + Py_INCREF(globals); + builtins = PyObject_GetItem(globals, builtins_str); + if (builtins == NULL) + goto err; + } + else { + /* No globals -- use standard builtins, and fake globals */ + PyErr_Clear(); + + builtins = PyImport_ImportModuleEx("__builtin__", + NULL, NULL, NULL); + if (builtins == NULL) + return NULL; + globals = Py_BuildValue("{OO}", builtins_str, builtins); + if (globals == NULL) + goto err; + } + + /* Get the __import__ function from the builtins */ + if (PyDict_Check(builtins)) { + import = PyObject_GetItem(builtins, import_str); + if (import == NULL) + PyErr_SetObject(PyExc_KeyError, import_str); + } + else + import = PyObject_GetAttr(builtins, import_str); + if (import == NULL) + goto err; + + /* Call the _import__ function with the proper argument list */ + r = PyObject_CallFunction(import, "OOOO", + module_name, globals, globals, silly_list); + + err: + Py_XDECREF(globals); + Py_XDECREF(builtins); + Py_XDECREF(import); + + return r; +} + + +/* Module 'imp' provides Python access to the primitives used for + importing modules. +*/ + +static PyObject * +imp_get_magic(PyObject *self, PyObject *noargs) +{ + char buf[4]; + + buf[0] = (char) ((pyc_magic >> 0) & 0xff); + buf[1] = (char) ((pyc_magic >> 8) & 0xff); + buf[2] = (char) ((pyc_magic >> 16) & 0xff); + buf[3] = (char) ((pyc_magic >> 24) & 0xff); + + return PyString_FromStringAndSize(buf, 4); +} + +static PyObject * +imp_get_suffixes(PyObject *self, PyObject *noargs) +{ + PyObject *list; + struct filedescr *fdp; + + list = PyList_New(0); + if (list == NULL) + return NULL; + for (fdp = _PyImport_Filetab; fdp->suffix != NULL; fdp++) { + PyObject *item = Py_BuildValue("ssi", + fdp->suffix, fdp->mode, fdp->type); + if (item == NULL) { + Py_DECREF(list); + return NULL; + } + if (PyList_Append(list, item) < 0) { + Py_DECREF(list); + Py_DECREF(item); + return NULL; + } + Py_DECREF(item); + } + return list; +} + +static PyObject * +call_find_module(char *name, PyObject *path) +{ + extern int fclose(FILE *); + PyObject *fob, *ret; + struct filedescr *fdp; + char pathname[MAXPATHLEN+1]; + FILE *fp = NULL; + + pathname[0] = '\0'; + if (path == Py_None) + path = NULL; + fdp = find_module(NULL, name, path, pathname, MAXPATHLEN+1, &fp, NULL); + if (fdp == NULL) + return NULL; + if (fp != NULL) { + fob = PyFile_FromFile(fp, pathname, fdp->mode, fclose); + if (fob == NULL) { + fclose(fp); + return NULL; + } + } + else { + fob = Py_None; + Py_INCREF(fob); + } + ret = Py_BuildValue("Os(ssi)", + fob, pathname, fdp->suffix, fdp->mode, fdp->type); + Py_DECREF(fob); + return ret; +} + +static PyObject * +imp_find_module(PyObject *self, PyObject *args) +{ + char *name; + PyObject *path = NULL; + if (!PyArg_ParseTuple(args, "s|O:find_module", &name, &path)) + return NULL; + return call_find_module(name, path); +} + +static PyObject * +imp_init_builtin(PyObject *self, PyObject *args) +{ + char *name; + int ret; + PyObject *m; + if (!PyArg_ParseTuple(args, "s:init_builtin", &name)) + return NULL; + ret = init_builtin(name); + if (ret < 0) + return NULL; + if (ret == 0) { + Py_INCREF(Py_None); + return Py_None; + } + m = PyImport_AddModule(name); + Py_XINCREF(m); + return m; +} + +static PyObject * +imp_init_frozen(PyObject *self, PyObject *args) +{ + char *name; + int ret; + PyObject *m; + if (!PyArg_ParseTuple(args, "s:init_frozen", &name)) + return NULL; + ret = PyImport_ImportFrozenModule(name); + if (ret < 0) + return NULL; + if (ret == 0) { + Py_INCREF(Py_None); + return Py_None; + } + m = PyImport_AddModule(name); + Py_XINCREF(m); + return m; +} + +static PyObject * +imp_get_frozen_object(PyObject *self, PyObject *args) +{ + char *name; + + if (!PyArg_ParseTuple(args, "s:get_frozen_object", &name)) + return NULL; + return get_frozen_object(name); +} + +static PyObject * +imp_is_builtin(PyObject *self, PyObject *args) +{ + char *name; + if (!PyArg_ParseTuple(args, "s:is_builtin", &name)) + return NULL; + return PyInt_FromLong(is_builtin(name)); +} + +static PyObject * +imp_is_frozen(PyObject *self, PyObject *args) +{ + char *name; + struct _frozen *p; + if (!PyArg_ParseTuple(args, "s:is_frozen", &name)) + return NULL; + p = find_frozen(name); + return PyBool_FromLong((long) (p == NULL ? 0 : p->size)); +} + +static FILE * +get_file(char *pathname, PyObject *fob, char *mode) +{ + FILE *fp; + if (fob == NULL) { + if (mode[0] == 'U') + mode = "r" PY_STDIOTEXTMODE; + fp = fopen(pathname, mode); + if (fp == NULL) + PyErr_SetFromErrno(PyExc_IOError); + } + else { + fp = PyFile_AsFile(fob); + if (fp == NULL) + PyErr_SetString(PyExc_ValueError, + "bad/closed file object"); + } + return fp; +} + +static PyObject * +imp_load_compiled(PyObject *self, PyObject *args) +{ + char *name; + char *pathname; + PyObject *fob = NULL; + PyObject *m; + FILE *fp; + if (!PyArg_ParseTuple(args, "ss|O!:load_compiled", &name, &pathname, + &PyFile_Type, &fob)) + return NULL; + fp = get_file(pathname, fob, "rb"); + if (fp == NULL) + return NULL; + m = load_compiled_module(name, pathname, fp); + if (fob == NULL) + fclose(fp); + return m; +} + +#ifdef HAVE_DYNAMIC_LOADING + +static PyObject * +imp_load_dynamic(PyObject *self, PyObject *args) +{ + char *name; + char *pathname; + PyObject *fob = NULL; + PyObject *m; + FILE *fp = NULL; + if (!PyArg_ParseTuple(args, "ss|O!:load_dynamic", &name, &pathname, + &PyFile_Type, &fob)) + return NULL; + if (fob) { + fp = get_file(pathname, fob, "r"); + if (fp == NULL) + return NULL; + } + m = _PyImport_LoadDynamicModule(name, pathname, fp); + return m; +} + +#endif /* HAVE_DYNAMIC_LOADING */ + +static PyObject * +imp_load_source(PyObject *self, PyObject *args) +{ + char *name; + char *pathname; + PyObject *fob = NULL; + PyObject *m; + FILE *fp; + if (!PyArg_ParseTuple(args, "ss|O!:load_source", &name, &pathname, + &PyFile_Type, &fob)) + return NULL; + fp = get_file(pathname, fob, "r"); + if (fp == NULL) + return NULL; + m = load_source_module(name, pathname, fp); + if (fob == NULL) + fclose(fp); + return m; +} + +#ifdef macintosh +static PyObject * +imp_load_resource(PyObject *self, PyObject *args) +{ + char *name; + char *pathname; + PyObject *m; + + if (!PyArg_ParseTuple(args, "ss:load_resource", &name, &pathname)) + return NULL; + m = PyMac_LoadResourceModule(name, pathname); + return m; +} +#endif /* macintosh */ + +static PyObject * +imp_load_module(PyObject *self, PyObject *args) +{ + char *name; + PyObject *fob; + char *pathname; + char *suffix; /* Unused */ + char *mode; + int type; + FILE *fp; + + if (!PyArg_ParseTuple(args, "sOs(ssi):load_module", + &name, &fob, &pathname, + &suffix, &mode, &type)) + return NULL; + if (*mode) { + /* Mode must start with 'r' or 'U' and must not contain '+'. + Implicit in this test is the assumption that the mode + may contain other modifiers like 'b' or 't'. */ + + if (!(*mode == 'r' || *mode == 'U') || strchr(mode, '+')) { + PyErr_Format(PyExc_ValueError, + "invalid file open mode %.200s", mode); + return NULL; + } + } + if (fob == Py_None) + fp = NULL; + else { + if (!PyFile_Check(fob)) { + PyErr_SetString(PyExc_ValueError, + "load_module arg#2 should be a file or None"); + return NULL; + } + fp = get_file(pathname, fob, mode); + if (fp == NULL) + return NULL; + } + return load_module(name, fp, pathname, type, NULL); +} + +static PyObject * +imp_load_package(PyObject *self, PyObject *args) +{ + char *name; + char *pathname; + if (!PyArg_ParseTuple(args, "ss:load_package", &name, &pathname)) + return NULL; + return load_package(name, pathname); +} + +static PyObject * +imp_new_module(PyObject *self, PyObject *args) +{ + char *name; + if (!PyArg_ParseTuple(args, "s:new_module", &name)) + return NULL; + return PyModule_New(name); +} + +/* Doc strings */ + +PyDoc_STRVAR(doc_imp, +"This module provides the components needed to build your own\n\ +__import__ function. Undocumented functions are obsolete."); + +PyDoc_STRVAR(doc_find_module, +"find_module(name, [path]) -> (file, filename, (suffix, mode, type))\n\ +Search for a module. If path is omitted or None, search for a\n\ +built-in, frozen or special module and continue search in sys.path.\n\ +The module name cannot contain '.'; to search for a submodule of a\n\ +package, pass the submodule name and the package's __path__."); + +PyDoc_STRVAR(doc_load_module, +"load_module(name, file, filename, (suffix, mode, type)) -> module\n\ +Load a module, given information returned by find_module().\n\ +The module name must include the full package name, if any."); + +PyDoc_STRVAR(doc_get_magic, +"get_magic() -> string\n\ +Return the magic number for .pyc or .pyo files."); + +PyDoc_STRVAR(doc_get_suffixes, +"get_suffixes() -> [(suffix, mode, type), ...]\n\ +Return a list of (suffix, mode, type) tuples describing the files\n\ +that find_module() looks for."); + +PyDoc_STRVAR(doc_new_module, +"new_module(name) -> module\n\ +Create a new module. Do not enter it in sys.modules.\n\ +The module name must include the full package name, if any."); + +PyDoc_STRVAR(doc_lock_held, +"lock_held() -> 0 or 1\n\ +Return 1 if the import lock is currently held.\n\ +On platforms without threads, return 0."); + +PyDoc_STRVAR(doc_acquire_lock, +"acquire_lock() -> None\n\ +Acquires the interpreter's import lock for the current thread.\n\ +This lock should be used by import hooks to ensure thread-safety\n\ +when importing modules.\n\ +On platforms without threads, this function does nothing."); + +PyDoc_STRVAR(doc_release_lock, +"release_lock() -> None\n\ +Release the interpreter's import lock.\n\ +On platforms without threads, this function does nothing."); + +static PyMethodDef imp_methods[] = { + {"find_module", imp_find_module, METH_VARARGS, doc_find_module}, + {"get_magic", imp_get_magic, METH_NOARGS, doc_get_magic}, + {"get_suffixes", imp_get_suffixes, METH_NOARGS, doc_get_suffixes}, + {"load_module", imp_load_module, METH_VARARGS, doc_load_module}, + {"new_module", imp_new_module, METH_VARARGS, doc_new_module}, + {"lock_held", imp_lock_held, METH_NOARGS, doc_lock_held}, + {"acquire_lock", imp_acquire_lock, METH_NOARGS, doc_acquire_lock}, + {"release_lock", imp_release_lock, METH_NOARGS, doc_release_lock}, + /* The rest are obsolete */ + {"get_frozen_object", imp_get_frozen_object, METH_VARARGS}, + {"init_builtin", imp_init_builtin, METH_VARARGS}, + {"init_frozen", imp_init_frozen, METH_VARARGS}, + {"is_builtin", imp_is_builtin, METH_VARARGS}, + {"is_frozen", imp_is_frozen, METH_VARARGS}, + {"load_compiled", imp_load_compiled, METH_VARARGS}, +#ifdef HAVE_DYNAMIC_LOADING + {"load_dynamic", imp_load_dynamic, METH_VARARGS}, +#endif + {"load_package", imp_load_package, METH_VARARGS}, +#ifdef macintosh + {"load_resource", imp_load_resource, METH_VARARGS}, +#endif + {"load_source", imp_load_source, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +static int +setint(PyObject *d, char *name, int value) +{ + PyObject *v; + int err; + + v = PyInt_FromLong((long)value); + err = PyDict_SetItemString(d, name, v); + Py_XDECREF(v); + return err; +} + +void +initimp(void) +{ + PyObject *m, *d; + + m = Py_InitModule4("imp", imp_methods, doc_imp, + NULL, PYTHON_API_VERSION); + d = PyModule_GetDict(m); + + if (setint(d, "SEARCH_ERROR", SEARCH_ERROR) < 0) goto failure; + if (setint(d, "PY_SOURCE", PY_SOURCE) < 0) goto failure; + if (setint(d, "PY_COMPILED", PY_COMPILED) < 0) goto failure; + if (setint(d, "C_EXTENSION", C_EXTENSION) < 0) goto failure; + if (setint(d, "PY_RESOURCE", PY_RESOURCE) < 0) goto failure; + if (setint(d, "PKG_DIRECTORY", PKG_DIRECTORY) < 0) goto failure; + if (setint(d, "C_BUILTIN", C_BUILTIN) < 0) goto failure; + if (setint(d, "PY_FROZEN", PY_FROZEN) < 0) goto failure; + if (setint(d, "PY_CODERESOURCE", PY_CODERESOURCE) < 0) goto failure; + if (setint(d, "IMP_HOOK", IMP_HOOK) < 0) goto failure; + + failure: + ; +} + + +/* API for embedding applications that want to add their own entries + to the table of built-in modules. This should normally be called + *before* Py_Initialize(). When the table resize fails, -1 is + returned and the existing table is unchanged. + + After a similar function by Just van Rossum. */ + +int +PyImport_ExtendInittab(struct _inittab *newtab) +{ + static struct _inittab *our_copy = NULL; + struct _inittab *p; + int i, n; + + /* Count the number of entries in both tables */ + for (n = 0; newtab[n].name != NULL; n++) + ; + if (n == 0) + return 0; /* Nothing to do */ + for (i = 0; PyImport_Inittab[i].name != NULL; i++) + ; + + /* Allocate new memory for the combined table */ + p = our_copy; + PyMem_RESIZE(p, struct _inittab, i+n+1); + if (p == NULL) + return -1; + + /* Copy the tables into the new memory */ + if (our_copy != PyImport_Inittab) + memcpy(p, PyImport_Inittab, (i+1) * sizeof(struct _inittab)); + PyImport_Inittab = our_copy = p; + memcpy(p+i, newtab, (n+1) * sizeof(struct _inittab)); + + return 0; +} + +/* Shorthand to add a single entry given a name and a function */ + +int +PyImport_AppendInittab(char *name, void (*initfunc)(void)) +{ + struct _inittab newtab[2]; + + memset(newtab, '\0', sizeof newtab); + + newtab[0].name = name; + newtab[0].initfunc = initfunc; + + return PyImport_ExtendInittab(newtab); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/import_pack.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/import_pack.c new file mode 100644 index 00000000..58d8155c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/import_pack.c @@ -0,0 +1,2977 @@ + +/* Module definition and import implementation */ + +#include "Python.h" + +#include "node.h" +#include "token.h" +#include "errcode.h" +#include "marshal.h" +#include "compile.h" +#include "eval.h" +#include "osdefs.h" +#include "importdl.h" +#ifdef macintosh +#include "macglue.h" +#endif + +#include "PlasmaPack.h" + +#ifdef HAVE_FCNTL_H +#include +#endif + +extern time_t PyOS_GetLastModificationTime(char *, FILE *); + /* In getmtime.c */ + +/* Magic word to reject .pyc files generated by other Python versions */ +/* Change for each incompatible change */ +/* The value of CR and LF is incorporated so if you ever read or write + a .pyc file in text mode the magic number will be wrong; also, the + Apple MPW compiler swaps their values, botching string constants. + XXX That probably isn't important anymore. +*/ +/* XXX Perhaps the magic number should be frozen and a version field + added to the .pyc file header? */ +/* New way to come up with the low 16 bits of the magic number: + (YEAR-1995) * 10000 + MONTH * 100 + DAY + where MONTH and DAY are 1-based. + XXX Whatever the "old way" may have been isn't documented. + XXX This scheme breaks in 2002, as (2002-1995)*10000 = 70000 doesn't + fit in 16 bits. + XXX Later, sometimes 1 gets added to MAGIC in order to record that + the Unicode -U option is in use. IMO (Tim's), that's a Bad Idea + (quite apart from that the -U option doesn't work so isn't used + anyway). + + XXX MAL, 2002-02-07: I had to modify the MAGIC due to a fix of the + UTF-8 encoder (it previously produced invalid UTF-8 for unpaired + high surrogates), so I simply bumped the month value to 20 (invalid + month) and set the day to 1. This should be recognizable by any + algorithm relying on the above scheme. Perhaps we should simply + start counting in increments of 10 from now on ?! + + MWH, 2002-08-03: Removed SET_LINENO. Couldn't be bothered figuring + out the MAGIC schemes, so just incremented it by 10. + + GvR, 2002-08-31: Because MWH changed the bytecode again, moved the + magic number *back* to 62011. This should get the snake-farm to + throw away its old .pyc files, amongst others. + + Known values: + Python 1.5: 20121 + Python 1.5.1: 20121 + Python 1.5.2: 20121 + Python 2.0: 50823 + Python 2.0.1: 50823 + Python 2.1: 60202 + Python 2.1.1: 60202 + Python 2.1.2: 60202 + Python 2.2: 60717 + Python 2.3a0: 62011 + Python 2.3a0: 62021 + Python 2.3a0: 62011 (!) +*/ +/* we'll make our own magic number.... the D'ni way (whatever that means)*/ +#define MAGIC (18686 | ((long)'\r'<<16) | ((long)'\n'<<24)) + +/* Magic word as global; note that _PyImport_Init() can change the + value of this global to accommodate for alterations of how the + compiler works which are enabled by command line switches. */ +static long pyc_magic = MAGIC; + +/* See _PyImport_FixupExtension() below */ +static PyObject *extensions = NULL; + +/* This table is defined in config.c: */ +extern struct _inittab _PyImport_Inittab[]; + +struct _inittab *PyImport_Inittab = _PyImport_Inittab; + +/* these tables define the module suffixes that Python recognizes */ +struct filedescr * _PyImport_Filetab = NULL; + +#ifdef RISCOS +static const struct filedescr _PyImport_StandardFiletab[] = { + {"/py", "U", PY_SOURCE}, + {"/pyc", "rb", PY_COMPILED}, + {0, 0} +}; +#else +static const struct filedescr _PyImport_StandardFiletab[] = { + {".py", "U", PY_SOURCE}, +#ifdef MS_WINDOWS + {".pyw", "U", PY_SOURCE}, +#endif + {".pyc", "rb", PY_COMPILED}, + {0, 0} +}; +#endif + +/* Initialize things */ + +void +_PyImport_Init(void) +{ + const struct filedescr *scan; + struct filedescr *filetab; + int countD = 0; + int countS = 0; + + /* prepare _PyImport_Filetab: copy entries from + _PyImport_DynLoadFiletab and _PyImport_StandardFiletab. + */ + for (scan = _PyImport_DynLoadFiletab; scan->suffix != NULL; ++scan) + ++countD; + for (scan = _PyImport_StandardFiletab; scan->suffix != NULL; ++scan) + ++countS; + filetab = PyMem_NEW(struct filedescr, countD + countS + 1); + memcpy(filetab, _PyImport_DynLoadFiletab, + countD * sizeof(struct filedescr)); + memcpy(filetab + countD, _PyImport_StandardFiletab, + countS * sizeof(struct filedescr)); + filetab[countD + countS].suffix = NULL; + + _PyImport_Filetab = filetab; + + if (Py_OptimizeFlag) { + /* Replace ".pyc" with ".pyo" in _PyImport_Filetab */ + for (; filetab->suffix != NULL; filetab++) { +#ifndef RISCOS + if (strcmp(filetab->suffix, ".pyc") == 0) + filetab->suffix = ".pyo"; +#else + if (strcmp(filetab->suffix, "/pyc") == 0) + filetab->suffix = "/pyo"; +#endif + } + } + + if (Py_UnicodeFlag) { + /* Fix the pyc_magic so that byte compiled code created + using the all-Unicode method doesn't interfere with + code created in normal operation mode. */ + pyc_magic = MAGIC + 1; + } +} + +void +_PyImportHooks_Init(void) +{ + PyObject *v, *path_hooks = NULL, *zimpimport; + int err = 0; + + /* adding sys.path_hooks and sys.path_importer_cache, setting up + zipimport */ + + if (Py_VerboseFlag) + PySys_WriteStderr("# installing zipimport hook\n"); + + v = PyList_New(0); + if (v == NULL) + goto error; + err = PySys_SetObject("meta_path", v); + Py_DECREF(v); + if (err) + goto error; + v = PyDict_New(); + if (v == NULL) + goto error; + err = PySys_SetObject("path_importer_cache", v); + Py_DECREF(v); + if (err) + goto error; + path_hooks = PyList_New(0); + if (path_hooks == NULL) + goto error; + err = PySys_SetObject("path_hooks", path_hooks); + if (err) { + error: + PyErr_Print(); + Py_FatalError("initializing sys.meta_path, sys.path_hooks or " + "path_importer_cache failed"); + } + zimpimport = PyImport_ImportModule("zipimport"); + if (zimpimport == NULL) { + PyErr_Clear(); /* No zip import module -- okay */ + if (Py_VerboseFlag) + PySys_WriteStderr("# can't import zipimport\n"); + } + else { + PyObject *zipimporter = PyObject_GetAttrString(zimpimport, + "zipimporter"); + Py_DECREF(zimpimport); + if (zipimporter == NULL) { + PyErr_Clear(); /* No zipimporter object -- okay */ + if (Py_VerboseFlag) + PySys_WriteStderr( + "# can't import zipimport.zipimporter\n"); + } + else { + /* sys.path_hooks.append(zipimporter) */ + err = PyList_Append(path_hooks, zipimporter); + Py_DECREF(zipimporter); + if (err) + goto error; + if (Py_VerboseFlag) + PySys_WriteStderr( + "# installed zipimport hook\n"); + } + } + Py_DECREF(path_hooks); +} + +void +_PyImport_Fini(void) +{ + Py_XDECREF(extensions); + extensions = NULL; + PyMem_DEL(_PyImport_Filetab); + _PyImport_Filetab = NULL; +} + + +/* Locking primitives to prevent parallel imports of the same module + in different threads to return with a partially loaded module. + These calls are serialized by the global interpreter lock. */ + +#ifdef WITH_THREAD + +#include "pythread.h" + +static PyThread_type_lock import_lock = 0; +static long import_lock_thread = -1; +static int import_lock_level = 0; + +static void +lock_import(void) +{ + long me = PyThread_get_thread_ident(); + if (me == -1) + return; /* Too bad */ + if (import_lock == NULL) + import_lock = PyThread_allocate_lock(); + if (import_lock_thread == me) { + import_lock_level++; + return; + } + if (import_lock_thread != -1 || !PyThread_acquire_lock(import_lock, 0)) + { + PyThreadState *tstate = PyEval_SaveThread(); + PyThread_acquire_lock(import_lock, 1); + PyEval_RestoreThread(tstate); + } + import_lock_thread = me; + import_lock_level = 1; +} + +static int +unlock_import(void) +{ + long me = PyThread_get_thread_ident(); + if (me == -1) + return 0; /* Too bad */ + if (import_lock_thread != me) + return -1; + import_lock_level--; + if (import_lock_level == 0) { + import_lock_thread = -1; + PyThread_release_lock(import_lock); + } + return 1; +} + +#else + +#define lock_import() +#define unlock_import() 0 + +#endif + +static PyObject * +imp_lock_held(PyObject *self, PyObject *noargs) +{ +#ifdef WITH_THREAD + return PyBool_FromLong(import_lock_thread != -1); +#else + return PyBool_FromLong(0); +#endif +} + +static PyObject * +imp_acquire_lock(PyObject *self, PyObject *noargs) +{ +#ifdef WITH_THREAD + lock_import(); +#endif + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +imp_release_lock(PyObject *self, PyObject *noargs) +{ +#ifdef WITH_THREAD + if (unlock_import() < 0) { + PyErr_SetString(PyExc_RuntimeError, + "not holding the import lock"); + return NULL; + } +#endif + Py_INCREF(Py_None); + return Py_None; +} + +/* Helper for sys */ + +PyObject * +PyImport_GetModuleDict(void) +{ + PyInterpreterState *interp = PyThreadState_Get()->interp; + if (interp->modules == NULL) + Py_FatalError("PyImport_GetModuleDict: no module dictionary!"); + return interp->modules; +} + + +/* List of names to clear in sys */ +static char* sys_deletes[] = { + "path", "argv", "ps1", "ps2", "exitfunc", + "exc_type", "exc_value", "exc_traceback", + "last_type", "last_value", "last_traceback", + "path_hooks", "path_importer_cache", "meta_path", + NULL +}; + +static char* sys_files[] = { + "stdin", "__stdin__", + "stdout", "__stdout__", + "stderr", "__stderr__", + NULL +}; + + +/* Un-initialize things, as good as we can */ + +void +PyImport_Cleanup(void) +{ + int pos, ndone; + char *name; + PyObject *key, *value, *dict; + PyInterpreterState *interp = PyThreadState_Get()->interp; + PyObject *modules = interp->modules; + + if (modules == NULL) + return; /* Already done */ + + /* Delete some special variables first. These are common + places where user values hide and people complain when their + destructors fail. Since the modules containing them are + deleted *last* of all, they would come too late in the normal + destruction order. Sigh. */ + + value = PyDict_GetItemString(modules, "__builtin__"); + if (value != NULL && PyModule_Check(value)) { + dict = PyModule_GetDict(value); + if (Py_VerboseFlag) + PySys_WriteStderr("# clear __builtin__._\n"); + PyDict_SetItemString(dict, "_", Py_None); + } + value = PyDict_GetItemString(modules, "sys"); + if (value != NULL && PyModule_Check(value)) { + char **p; + PyObject *v; + dict = PyModule_GetDict(value); + for (p = sys_deletes; *p != NULL; p++) { + if (Py_VerboseFlag) + PySys_WriteStderr("# clear sys.%s\n", *p); + PyDict_SetItemString(dict, *p, Py_None); + } + for (p = sys_files; *p != NULL; p+=2) { + if (Py_VerboseFlag) + PySys_WriteStderr("# restore sys.%s\n", *p); + v = PyDict_GetItemString(dict, *(p+1)); + if (v == NULL) + v = Py_None; + PyDict_SetItemString(dict, *p, v); + } + } + + /* First, delete __main__ */ + value = PyDict_GetItemString(modules, "__main__"); + if (value != NULL && PyModule_Check(value)) { + if (Py_VerboseFlag) + PySys_WriteStderr("# cleanup __main__\n"); + _PyModule_Clear(value); + PyDict_SetItemString(modules, "__main__", Py_None); + } + + /* The special treatment of __builtin__ here is because even + when it's not referenced as a module, its dictionary is + referenced by almost every module's __builtins__. Since + deleting a module clears its dictionary (even if there are + references left to it), we need to delete the __builtin__ + module last. Likewise, we don't delete sys until the very + end because it is implicitly referenced (e.g. by print). + + Also note that we 'delete' modules by replacing their entry + in the modules dict with None, rather than really deleting + them; this avoids a rehash of the modules dictionary and + also marks them as "non existent" so they won't be + re-imported. */ + + /* Next, repeatedly delete modules with a reference count of + one (skipping __builtin__ and sys) and delete them */ + do { + ndone = 0; + pos = 0; + while (PyDict_Next(modules, &pos, &key, &value)) { + if (value->ob_refcnt != 1) + continue; + if (PyString_Check(key) && PyModule_Check(value)) { + name = PyString_AS_STRING(key); + if (strcmp(name, "__builtin__") == 0) + continue; + if (strcmp(name, "sys") == 0) + continue; + if (Py_VerboseFlag) + PySys_WriteStderr( + "# cleanup[1] %s\n", name); + _PyModule_Clear(value); + PyDict_SetItem(modules, key, Py_None); + ndone++; + } + } + } while (ndone > 0); + + /* Next, delete all modules (still skipping __builtin__ and sys) */ + pos = 0; + while (PyDict_Next(modules, &pos, &key, &value)) { + if (PyString_Check(key) && PyModule_Check(value)) { + name = PyString_AS_STRING(key); + if (strcmp(name, "__builtin__") == 0) + continue; + if (strcmp(name, "sys") == 0) + continue; + if (Py_VerboseFlag) + PySys_WriteStderr("# cleanup[2] %s\n", name); + _PyModule_Clear(value); + PyDict_SetItem(modules, key, Py_None); + } + } + + /* Next, delete sys and __builtin__ (in that order) */ + value = PyDict_GetItemString(modules, "sys"); + if (value != NULL && PyModule_Check(value)) { + if (Py_VerboseFlag) + PySys_WriteStderr("# cleanup sys\n"); + _PyModule_Clear(value); + PyDict_SetItemString(modules, "sys", Py_None); + } + value = PyDict_GetItemString(modules, "__builtin__"); + if (value != NULL && PyModule_Check(value)) { + if (Py_VerboseFlag) + PySys_WriteStderr("# cleanup __builtin__\n"); + _PyModule_Clear(value); + PyDict_SetItemString(modules, "__builtin__", Py_None); + } + + /* Finally, clear and delete the modules directory */ + PyDict_Clear(modules); + interp->modules = NULL; + Py_DECREF(modules); +} + + +/* Helper for pythonrun.c -- return magic number */ + +long +PyImport_GetMagicNumber(void) +{ + return pyc_magic; +} + + +/* Magic for extension modules (built-in as well as dynamically + loaded). To prevent initializing an extension module more than + once, we keep a static dictionary 'extensions' keyed by module name + (for built-in modules) or by filename (for dynamically loaded + modules), containing these modules. A copy of the module's + dictionary is stored by calling _PyImport_FixupExtension() + immediately after the module initialization function succeeds. A + copy can be retrieved from there by calling + _PyImport_FindExtension(). */ + +PyObject * +_PyImport_FixupExtension(char *name, char *filename) +{ + PyObject *modules, *mod, *dict, *copy; + if (extensions == NULL) { + extensions = PyDict_New(); + if (extensions == NULL) + return NULL; + } + modules = PyImport_GetModuleDict(); + mod = PyDict_GetItemString(modules, name); + if (mod == NULL || !PyModule_Check(mod)) { + PyErr_Format(PyExc_SystemError, + "_PyImport_FixupExtension: module %.200s not loaded", name); + return NULL; + } + dict = PyModule_GetDict(mod); + if (dict == NULL) + return NULL; + copy = PyDict_Copy(dict); + if (copy == NULL) + return NULL; + PyDict_SetItemString(extensions, filename, copy); + Py_DECREF(copy); + return copy; +} + +PyObject * +_PyImport_FindExtension(char *name, char *filename) +{ + PyObject *dict, *mod, *mdict; + if (extensions == NULL) + return NULL; + dict = PyDict_GetItemString(extensions, filename); + if (dict == NULL) + return NULL; + mod = PyImport_AddModule(name); + if (mod == NULL) + return NULL; + mdict = PyModule_GetDict(mod); + if (mdict == NULL) + return NULL; + if (PyDict_Update(mdict, dict)) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # previously loaded (%s)\n", + name, filename); + return mod; +} + + +/* Get the module object corresponding to a module name. + First check the modules dictionary if there's one there, + if not, create a new one and insert it in the modules dictionary. + Because the former action is most common, THIS DOES NOT RETURN A + 'NEW' REFERENCE! */ + +PyObject * +PyImport_AddModule(char *name) +{ + PyObject *modules = PyImport_GetModuleDict(); + PyObject *m; + + if ((m = PyDict_GetItemString(modules, name)) != NULL && + PyModule_Check(m)) + return m; + m = PyModule_New(name); + if (m == NULL) + return NULL; + if (PyDict_SetItemString(modules, name, m) != 0) { + Py_DECREF(m); + return NULL; + } + Py_DECREF(m); /* Yes, it still exists, in modules! */ + + return m; +} + + +/* Execute a code object in a module and return the module object + WITH INCREMENTED REFERENCE COUNT */ + +PyObject * +PyImport_ExecCodeModule(char *name, PyObject *co) +{ + return PyImport_ExecCodeModuleEx(name, co, (char *)NULL); +} + +PyObject * +PyImport_ExecCodeModuleEx(char *name, PyObject *co, char *pathname) +{ + PyObject *modules = PyImport_GetModuleDict(); + PyObject *m, *d, *v; + + m = PyImport_AddModule(name); + if (m == NULL) + return NULL; + d = PyModule_GetDict(m); + if (PyDict_GetItemString(d, "__builtins__") == NULL) { + if (PyDict_SetItemString(d, "__builtins__", + PyEval_GetBuiltins()) != 0) + return NULL; + } + /* Remember the filename as the __file__ attribute */ + v = NULL; + if (pathname != NULL) { + v = PyString_FromString(pathname); + if (v == NULL) + PyErr_Clear(); + } + if (v == NULL) { + v = ((PyCodeObject *)co)->co_filename; + Py_INCREF(v); + } + if (PyDict_SetItemString(d, "__file__", v) != 0) + PyErr_Clear(); /* Not important enough to report */ + Py_DECREF(v); + + v = PyEval_EvalCode((PyCodeObject *)co, d, d); + if (v == NULL) + return NULL; + Py_DECREF(v); + + if ((m = PyDict_GetItemString(modules, name)) == NULL) { + PyErr_Format(PyExc_ImportError, + "Loaded module %.200s not found in sys.modules", + name); + return NULL; + } + + Py_INCREF(m); + + return m; +} + + +/* Given a pathname for a Python source file, fill a buffer with the + pathname for the corresponding compiled file. Return the pathname + for the compiled file, or NULL if there's no space in the buffer. + Doesn't set an exception. */ + +static char * +make_compiled_pathname(char *pathname, char *buf, size_t buflen) +{ + size_t len = strlen(pathname); + if (len+2 > buflen) + return NULL; + +#ifdef MS_WINDOWS + /* Treat .pyw as if it were .py. The case of ".pyw" must match + that used in _PyImport_StandardFiletab. */ + if (len >= 4 && strcmp(&pathname[len-4], ".pyw") == 0) + --len; /* pretend 'w' isn't there */ +#endif + memcpy(buf, pathname, len); + buf[len] = Py_OptimizeFlag ? 'o' : 'c'; + buf[len+1] = '\0'; + + return buf; +} + + +/* Given a pathname for a Python source file, its time of last + modification, and a pathname for a compiled file, check whether the + compiled file represents the same version of the source. If so, + return a FILE pointer for the compiled file, positioned just after + the header; if not, return NULL. + Doesn't set an exception. */ + +static FILE * +check_compiled_module(char *pathname, long mtime, char *cpathname) +{ + FILE *fp; + long magic; + long pyc_mtime; + + fp = fopen(cpathname, "rb"); + if (fp == NULL) + return NULL; + magic = PyMarshal_ReadLongFromFile(fp); + if (magic != pyc_magic) { + if (Py_VerboseFlag) + PySys_WriteStderr("# %s has bad magic\n", cpathname); + fclose(fp); + return NULL; + } + pyc_mtime = PyMarshal_ReadLongFromFile(fp); + if (pyc_mtime != mtime) { + if (Py_VerboseFlag) + PySys_WriteStderr("# %s has bad mtime\n", cpathname); + fclose(fp); + return NULL; + } + if (Py_VerboseFlag) + PySys_WriteStderr("# %s matches %s\n", cpathname, pathname); + return fp; +} + + +/* Read a code object from a file and check it for validity */ + +static PyCodeObject * +read_compiled_module(char *cpathname, FILE *fp) +{ + PyObject *co; + + co = PyMarshal_ReadLastObjectFromFile(fp); + /* Ugly: rd_object() may return NULL with or without error */ + if (co == NULL || !PyCode_Check(co)) { + if (!PyErr_Occurred()) + PyErr_Format(PyExc_ImportError, + "Non-code object in %.200s", cpathname); + Py_XDECREF(co); + return NULL; + } + return (PyCodeObject *)co; +} + + +/* Load a module from a compiled file, execute it, and return its + module object WITH INCREMENTED REFERENCE COUNT */ + +static PyObject * +load_compiled_module(char *name, char *cpathname, FILE *fp) +{ + long magic; + PyCodeObject *co; + PyObject *m; + + magic = PyMarshal_ReadLongFromFile(fp); + if (magic != pyc_magic) { + PyErr_Format(PyExc_ImportError, + "Bad magic number in %.200s", cpathname); + return NULL; + } + (void) PyMarshal_ReadLongFromFile(fp); + co = read_compiled_module(cpathname, fp); + if (co == NULL) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # precompiled from %s\n", + name, cpathname); + m = PyImport_ExecCodeModuleEx(name, (PyObject *)co, cpathname); + Py_DECREF(co); + + return m; +} + +/* +Load a python Plasma packfile. +Its already compiled... just load and server +*/ +static PyObject * +load_plasmapacked_module(char *name, char *cpathname) +{ + PyObject *co; + PyObject *m; + + co = Pl_OpenPacked(name); + if (co == NULL) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # precompiled from %s\n", + name, cpathname); + m = PyImport_ExecCodeModuleEx(name, (PyObject *)co, cpathname); + Py_DECREF(co); + + return m; +} + +/* Parse a source file and return the corresponding code object */ + +static PyCodeObject * +parse_source_module(char *pathname, FILE *fp) +{ + PyCodeObject *co; + node *n; + + n = PyParser_SimpleParseFile(fp, pathname, Py_file_input); + if (n == NULL) + return NULL; + co = PyNode_Compile(n, pathname); + PyNode_Free(n); + + return co; +} + + +/* Helper to open a bytecode file for writing in exclusive mode */ + +static FILE * +open_exclusive(char *filename) +{ +#if defined(O_EXCL)&&defined(O_CREAT)&&defined(O_WRONLY)&&defined(O_TRUNC)&&!defined(_DEBUG) + /* Use O_EXCL to avoid a race condition when another process tries to + write the same file. When that happens, our open() call fails, + which is just fine (since it's only a cache). + XXX If the file exists and is writable but the directory is not + writable, the file will never be written. Oh well. + */ + int fd; + (void) unlink(filename); + fd = open(filename, O_EXCL|O_CREAT|O_WRONLY|O_TRUNC +#ifdef O_BINARY + |O_BINARY /* necessary for Windows */ +#endif +#ifdef __VMS + , 0666, "ctxt=bin", "shr=nil"); +#else + , 0666); +#endif + if (fd < 0) + return NULL; + return fdopen(fd, "wb"); +#else + /* Best we can do -- on Windows this can't happen anyway */ + return fopen(filename, "wb"); +#endif +} + + +/* Write a compiled module to a file, placing the time of last + modification of its source into the header. + Errors are ignored, if a write error occurs an attempt is made to + remove the file. */ + +static void +write_compiled_module(PyCodeObject *co, char *cpathname, long mtime) +{ + FILE *fp; + + fp = open_exclusive(cpathname); + if (fp == NULL) { + if (Py_VerboseFlag) + PySys_WriteStderr( + "# can't create %s\n", cpathname); + return; + } + PyMarshal_WriteLongToFile(pyc_magic, fp); + /* First write a 0 for mtime */ + PyMarshal_WriteLongToFile(0L, fp); + PyMarshal_WriteObjectToFile((PyObject *)co, fp); + if (ferror(fp)) { + if (Py_VerboseFlag) + PySys_WriteStderr("# can't write %s\n", cpathname); + /* Don't keep partial file */ + fclose(fp); + (void) unlink(cpathname); + return; + } + /* Now write the true mtime */ + fseek(fp, 4L, 0); + PyMarshal_WriteLongToFile(mtime, fp); + fflush(fp); + fclose(fp); + if (Py_VerboseFlag) + PySys_WriteStderr("# wrote %s\n", cpathname); +#ifdef macintosh + PyMac_setfiletype(cpathname, 'Pyth', 'PYC '); +#endif +} + + +/* Load a source module from a given file and return its module + object WITH INCREMENTED REFERENCE COUNT. If there's a matching + byte-compiled file, use that instead. */ + +static PyObject * +load_source_module(char *name, char *pathname, FILE *fp) +{ + time_t mtime; + FILE *fpc; + char buf[MAXPATHLEN+1]; + char *cpathname; + PyCodeObject *co; + PyObject *m; + + mtime = PyOS_GetLastModificationTime(pathname, fp); + if (mtime == (time_t)(-1)) + return NULL; +#if SIZEOF_TIME_T > 4 + /* Python's .pyc timestamp handling presumes that the timestamp fits + in 4 bytes. This will be fine until sometime in the year 2038, + when a 4-byte signed time_t will overflow. + */ + if (mtime >> 32) { + PyErr_SetString(PyExc_OverflowError, + "modification time overflows a 4 byte field"); + return NULL; + } +#endif + cpathname = make_compiled_pathname(pathname, buf, + (size_t)MAXPATHLEN + 1); + if (cpathname != NULL && + (fpc = check_compiled_module(pathname, mtime, cpathname))) { + co = read_compiled_module(cpathname, fpc); + fclose(fpc); + if (co == NULL) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # precompiled from %s\n", + name, cpathname); + pathname = cpathname; + } + else { + co = parse_source_module(pathname, fp); + if (co == NULL) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # from %s\n", + name, pathname); + write_compiled_module(co, cpathname, mtime); + } + m = PyImport_ExecCodeModuleEx(name, (PyObject *)co, pathname); + Py_DECREF(co); + + return m; +} + + +/* Forward */ +static PyObject *load_module(char *, FILE *, char *, int, PyObject *); +static struct filedescr *find_module(char *, char *, PyObject *, + char *, size_t, FILE **, PyObject **); +static struct _frozen *find_frozen(char *name); + +/* Load a package and return its module object WITH INCREMENTED + REFERENCE COUNT */ + +static PyObject * +load_package(char *name, char *pathname) +{ + PyObject *m, *d, *file, *path; + int err; + char buf[MAXPATHLEN+1]; + FILE *fp = NULL; + struct filedescr *fdp; + + m = PyImport_AddModule(name); + if (m == NULL) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # directory %s\n", + name, pathname); + d = PyModule_GetDict(m); + file = PyString_FromString(pathname); + if (file == NULL) + return NULL; + path = Py_BuildValue("[O]", file); + if (path == NULL) { + Py_DECREF(file); + return NULL; + } + err = PyDict_SetItemString(d, "__file__", file); + if (err == 0) + err = PyDict_SetItemString(d, "__path__", path); + if (err != 0) { + m = NULL; + goto cleanup; + } + buf[0] = '\0'; + fdp = find_module(name, "__init__", path, buf, sizeof(buf), &fp, NULL); + if (fdp == NULL) { + if (PyErr_ExceptionMatches(PyExc_ImportError)) { + PyErr_Clear(); + } + else + m = NULL; + goto cleanup; + } + m = load_module(name, fp, buf, fdp->type, NULL); + if (fp != NULL) + fclose(fp); + cleanup: + Py_XDECREF(path); + Py_XDECREF(file); + return m; +} + + +/* Helper to test for built-in module */ + +static int +is_builtin(char *name) +{ + int i; + for (i = 0; PyImport_Inittab[i].name != NULL; i++) { + if (strcmp(name, PyImport_Inittab[i].name) == 0) { + if (PyImport_Inittab[i].initfunc == NULL) + return -1; + else + return 1; + } + } + return 0; +} + + +/* Return an importer object for a sys.path/pkg.__path__ item 'p', + possibly by fetching it from the path_importer_cache dict. If it + wasn't yet cached, traverse path_hooks until it a hook is found + that can handle the path item. Return None if no hook could; + this tells our caller it should fall back to the builtin + import mechanism. Cache the result in path_importer_cache. + Returns a borrowed reference. */ + +static PyObject * +get_path_importer(PyObject *path_importer_cache, PyObject *path_hooks, + PyObject *p) +{ + PyObject *importer; + int j, nhooks; + + /* These conditions are the caller's responsibility: */ + assert(PyList_Check(path_hooks)); + assert(PyDict_Check(path_importer_cache)); + + nhooks = PyList_Size(path_hooks); + if (nhooks < 0) + return NULL; /* Shouldn't happen */ + + importer = PyDict_GetItem(path_importer_cache, p); + if (importer != NULL) + return importer; + + /* set path_importer_cache[p] to None to avoid recursion */ + if (PyDict_SetItem(path_importer_cache, p, Py_None) != 0) + return NULL; + + for (j = 0; j < nhooks; j++) { + PyObject *hook = PyList_GetItem(path_hooks, j); + if (hook == NULL) + return NULL; + importer = PyObject_CallFunction(hook, "O", p); + if (importer != NULL) + break; + + if (!PyErr_ExceptionMatches(PyExc_ImportError)) { + return NULL; + } + PyErr_Clear(); + } + if (importer == NULL) + importer = Py_None; + else if (importer != Py_None) { + int err = PyDict_SetItem(path_importer_cache, p, importer); + Py_DECREF(importer); + if (err != 0) + return NULL; + } + return importer; +} + +/* Search the path (default sys.path) for a module. Return the + corresponding filedescr struct, and (via return arguments) the + pathname and an open file. Return NULL if the module is not found. */ + +#ifdef MS_COREDLL +extern FILE *PyWin_FindRegisteredModule(const char *, struct filedescr **, + char *, int); +#endif + +static int case_ok(char *, int, int, char *); +static int find_init_module(char *); /* Forward */ +static struct filedescr importhookdescr = {"", "", IMP_HOOK}; + +static struct filedescr * +find_module(char *fullname, char *subname, PyObject *path, char *buf, + size_t buflen, FILE **p_fp, PyObject **p_loader) +{ + int i, npath; + size_t len, namelen; + struct filedescr *fdp = NULL; + char *filemode; + FILE *fp = NULL; + PyObject *path_hooks, *path_importer_cache; +#ifndef RISCOS + struct stat statbuf; +#endif + static struct filedescr fd_frozen = {"", "", PY_FROZEN}; + static struct filedescr fd_builtin = {"", "", C_BUILTIN}; + static struct filedescr fd_package = {"", "", PKG_DIRECTORY}; + static struct filedescr fd_plasmaPack = {"", "", PLASMA_PACKFILE}; + char name[MAXPATHLEN+1]; +#if defined(PYOS_OS2) + size_t saved_len; + size_t saved_namelen; + char *saved_buf = NULL; +#endif + // for plasma pak files (so we can override them with local python, if it exists) + int isPacked = 0; + char pakName[MAXPATHLEN+1]; + + if (p_loader != NULL) + *p_loader = NULL; + + if (strlen(subname) > MAXPATHLEN) { + PyErr_SetString(PyExc_OverflowError, + "module name is too long"); + return NULL; + } + strcpy(name, subname); + + /* sys.meta_path import hook */ + if (p_loader != NULL) { + PyObject *meta_path; + + meta_path = PySys_GetObject("meta_path"); + if (meta_path == NULL || !PyList_Check(meta_path)) { + PyErr_SetString(PyExc_ImportError, + "sys.meta_path must be a list of " + "import hooks"); + return NULL; + } + Py_INCREF(meta_path); /* zap guard */ + npath = PyList_Size(meta_path); + for (i = 0; i < npath; i++) { + PyObject *loader; + PyObject *hook = PyList_GetItem(meta_path, i); + loader = PyObject_CallMethod(hook, "find_module", + "sO", fullname, + path != NULL ? + path : Py_None); + if (loader == NULL) { + Py_DECREF(meta_path); + return NULL; /* true error */ + } + if (loader != Py_None) { + /* a loader was found */ + *p_loader = loader; + Py_DECREF(meta_path); + return &importhookdescr; + } + Py_DECREF(loader); + } + Py_DECREF(meta_path); + } + + if (path != NULL && PyString_Check(path)) { + /* The only type of submodule allowed inside a "frozen" + package are other frozen modules or packages. */ + if (PyString_Size(path) + 1 + strlen(name) >= (size_t)buflen) { + PyErr_SetString(PyExc_ImportError, + "full frozen module name too long"); + return NULL; + } + strcpy(buf, PyString_AsString(path)); + strcat(buf, "."); + strcat(buf, name); + strcpy(name, buf); +#ifdef macintosh + /* Freezing on the mac works different, and the modules are + ** actually on sys.path. So we don't take the quick exit but + ** continue with the normal flow. + */ + path = NULL; +#else + if (find_frozen(name) != NULL) { + strcpy(buf, name); + return &fd_frozen; + } + PyErr_Format(PyExc_ImportError, + "No frozen submodule named %.200s", name); + return NULL; +#endif + } + if (path == NULL) { + if (is_builtin(name)) { + strcpy(buf, name); + return &fd_builtin; + } + if ((find_frozen(name)) != NULL) { + strcpy(buf, name); + return &fd_frozen; + } + + // check to see if its in the plasma python pack file + if ( Pl_IsItPacked(name) ) { +#ifdef PLASMA_EXTERNAL_RELEASE + strcpy(buf, name); + return &fd_plasmaPack; +#else + // internal builds can override the pak file + isPacked = 1; + strcpy(pakName, name); +#endif + } + +#ifdef MS_COREDLL + fp = PyWin_FindRegisteredModule(name, &fdp, buf, buflen); + if (fp != NULL) { + *p_fp = fp; + return fdp; + } +#endif + path = PySys_GetObject("path"); + } + if (path == NULL || !PyList_Check(path)) { + PyErr_SetString(PyExc_ImportError, + "sys.path must be a list of directory names"); + return NULL; + } + + path_hooks = PySys_GetObject("path_hooks"); + if (path_hooks == NULL || !PyList_Check(path_hooks)) { + PyErr_SetString(PyExc_ImportError, + "sys.path_hooks must be a list of " + "import hooks"); + return NULL; + } + path_importer_cache = PySys_GetObject("path_importer_cache"); + if (path_importer_cache == NULL || + !PyDict_Check(path_importer_cache)) { + PyErr_SetString(PyExc_ImportError, + "sys.path_importer_cache must be a dict"); + return NULL; + } + + npath = PyList_Size(path); + namelen = strlen(name); + for (i = 0; i < npath; i++) { + PyObject *copy = NULL; + PyObject *v = PyList_GetItem(path, i); +#ifdef Py_USING_UNICODE + if (PyUnicode_Check(v)) { + copy = PyUnicode_Encode(PyUnicode_AS_UNICODE(v), + PyUnicode_GET_SIZE(v), Py_FileSystemDefaultEncoding, NULL); + if (copy == NULL) + return NULL; + v = copy; + } + else +#endif + if (!PyString_Check(v)) + continue; + len = PyString_Size(v); + if (len + 2 + namelen + MAXSUFFIXSIZE >= buflen) { + Py_XDECREF(copy); + continue; /* Too long */ + } + strcpy(buf, PyString_AsString(v)); + if (strlen(buf) != len) { + Py_XDECREF(copy); + continue; /* v contains '\0' */ + } + + /* sys.path_hooks import hook */ + if (p_loader != NULL) { + PyObject *importer; + + importer = get_path_importer(path_importer_cache, + path_hooks, v); + if (importer == NULL) + return NULL; + /* Note: importer is a borrowed reference */ + if (importer != Py_None) { + PyObject *loader; + loader = PyObject_CallMethod(importer, + "find_module", + "s", fullname); + if (loader == NULL) + return NULL; /* error */ + if (loader != Py_None) { + /* a loader was found */ + *p_loader = loader; + return &importhookdescr; + } + Py_DECREF(loader); + } + /* no hook was successful, use builtin import */ + } + +#ifdef macintosh + /* + ** Speedup: each sys.path item is interned, and + ** FindResourceModule remembers which items refer to + ** folders (so we don't have to bother trying to look + ** into them for resources). We only do this for string + ** items. + */ + if (PyString_Check(PyList_GET_ITEM(path, i))) { + PyString_InternInPlace(&PyList_GET_ITEM(path, i)); + v = PyList_GET_ITEM(path, i); + if (PyMac_FindResourceModule((PyStringObject *)v, name, buf)) { + static struct filedescr resfiledescr = + {"", "", PY_RESOURCE}; + + Py_XDECREF(copy); + return &resfiledescr; + } + if (PyMac_FindCodeResourceModule((PyStringObject *)v, name, buf)) { + static struct filedescr resfiledescr = + {"", "", PY_CODERESOURCE}; + + Py_XDECREF(copy); + return &resfiledescr; + } + } +#endif + if (len > 0 && buf[len-1] != SEP +#ifdef ALTSEP + && buf[len-1] != ALTSEP +#endif + ) + buf[len++] = SEP; + strcpy(buf+len, name); + len += namelen; + + /* Check for package import (buf holds a directory name, + and there's an __init__ module in that directory */ +#ifdef HAVE_STAT + if (stat(buf, &statbuf) == 0 && /* it exists */ + S_ISDIR(statbuf.st_mode) && /* it's a directory */ + find_init_module(buf) && /* it has __init__.py */ + case_ok(buf, len, namelen, name)) { /* and case matches */ + Py_XDECREF(copy); + return &fd_package; + } +#else + /* XXX How are you going to test for directories? */ +#ifdef RISCOS + if (isdir(buf) && + find_init_module(buf) && + case_ok(buf, len, namelen, name)) { + Py_XDECREF(copy); + return &fd_package; + } +#endif +#endif +#ifdef macintosh + fdp = PyMac_FindModuleExtension(buf, &len, name); + if (fdp) { +#else +#if defined(PYOS_OS2) + /* take a snapshot of the module spec for restoration + * after the 8 character DLL hackery + */ + saved_buf = strdup(buf); + saved_len = len; + saved_namelen = namelen; +#endif /* PYOS_OS2 */ + for (fdp = _PyImport_Filetab; fdp->suffix != NULL; fdp++) { +#if defined(PYOS_OS2) + /* OS/2 limits DLLs to 8 character names (w/o + extension) + * so if the name is longer than that and its a + * dynamically loaded module we're going to try, + * truncate the name before trying + */ + if (strlen(subname) > 8) { + /* is this an attempt to load a C extension? */ + const struct filedescr *scan; + scan = _PyImport_DynLoadFiletab; + while (scan->suffix != NULL) { + if (!strcmp(scan->suffix, fdp->suffix)) + break; + else + scan++; + } + if (scan->suffix != NULL) { + /* yes, so truncate the name */ + namelen = 8; + len -= strlen(subname) - namelen; + buf[len] = '\0'; + } + } +#endif /* PYOS_OS2 */ + strcpy(buf+len, fdp->suffix); + if (Py_VerboseFlag > 1) + PySys_WriteStderr("# trying %s\n", buf); +#endif /* !macintosh */ + filemode = fdp->mode; + if (filemode[0] == 'U') + filemode = "r" PY_STDIOTEXTMODE; + fp = fopen(buf, filemode); + if (fp != NULL) { + if (case_ok(buf, len, namelen, name)) + break; + else { /* continue search */ + fclose(fp); + fp = NULL; + } + } +#if defined(PYOS_OS2) + /* restore the saved snapshot */ + strcpy(buf, saved_buf); + len = saved_len; + namelen = saved_namelen; +#endif + } +#if defined(PYOS_OS2) + /* don't need/want the module name snapshot anymore */ + if (saved_buf) + { + free(saved_buf); + saved_buf = NULL; + } +#endif + Py_XDECREF(copy); + if (fp != NULL) + break; + } + if (fp == NULL) { +#ifndef PLASMA_EXTERNAL_RELEASE + if (isPacked) { + // we didn't find anything to override it, use the pak file + strcpy(buf, pakName); + return &fd_plasmaPack; + } +#endif + PyErr_Format(PyExc_ImportError, + "No module named %.200s", name); + return NULL; + } + *p_fp = fp; + return fdp; +} + +/* case_ok(char* buf, int len, int namelen, char* name) + * The arguments here are tricky, best shown by example: + * /a/b/c/d/e/f/g/h/i/j/k/some_long_module_name.py\0 + * ^ ^ ^ ^ + * |--------------------- buf ---------------------| + * |------------------- len ------------------| + * |------ name -------| + * |----- namelen -----| + * buf is the full path, but len only counts up to (& exclusive of) the + * extension. name is the module name, also exclusive of extension. + * + * We've already done a successful stat() or fopen() on buf, so know that + * there's some match, possibly case-insensitive. + * + * case_ok() is to return 1 if there's a case-sensitive match for + * name, else 0. case_ok() is also to return 1 if envar PYTHONCASEOK + * exists. + * + * case_ok() is used to implement case-sensitive import semantics even + * on platforms with case-insensitive filesystems. It's trivial to implement + * for case-sensitive filesystems. It's pretty much a cross-platform + * nightmare for systems with case-insensitive filesystems. + */ + +/* First we may need a pile of platform-specific header files; the sequence + * of #if's here should match the sequence in the body of case_ok(). + */ +#if defined(MS_WINDOWS) || defined(__CYGWIN__) +#ifdef MS_XBOX +#include +#else +#include +#endif +#ifdef __CYGWIN__ +#include +#endif + +#elif defined(DJGPP) +#include + +#elif defined(macintosh) +#include + +#elif defined(__MACH__) && defined(__APPLE__) && defined(HAVE_DIRENT_H) +#include +#include + +#elif defined(PYOS_OS2) +#define INCL_DOS +#define INCL_DOSERRORS +#define INCL_NOPMAPI +#include + +#elif defined(RISCOS) +#include "oslib/osfscontrol.h" +#endif + +static int +case_ok(char *buf, int len, int namelen, char *name) +{ +/* Pick a platform-specific implementation; the sequence of #if's here should + * match the sequence just above. + */ + +/* MS_WINDOWS || __CYGWIN__ */ +#if defined(MS_WINDOWS) || defined(__CYGWIN__) + WIN32_FIND_DATA data; + HANDLE h; +#ifdef __CYGWIN__ + char tempbuf[MAX_PATH]; +#endif + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + +#ifdef __CYGWIN__ + cygwin32_conv_to_win32_path(buf, tempbuf); + h = FindFirstFile(tempbuf, &data); +#else + h = FindFirstFile(buf, &data); +#endif + if (h == INVALID_HANDLE_VALUE) { + PyErr_Format(PyExc_NameError, + "Can't find file for module %.100s\n(filename %.300s)", + name, buf); + return 0; + } + FindClose(h); + return strncmp(data.cFileName, name, namelen) == 0; + +/* DJGPP */ +#elif defined(DJGPP) + struct ffblk ffblk; + int done; + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + + done = findfirst(buf, &ffblk, FA_ARCH|FA_RDONLY|FA_HIDDEN|FA_DIREC); + if (done) { + PyErr_Format(PyExc_NameError, + "Can't find file for module %.100s\n(filename %.300s)", + name, buf); + return 0; + } + return strncmp(ffblk.ff_name, name, namelen) == 0; + +/* macintosh */ +#elif defined(macintosh) + FSSpec fss; + OSErr err; + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + + err = FSMakeFSSpec(0, 0, Pstring(buf), &fss); + if (err) { + PyErr_Format(PyExc_NameError, + "Can't find file for module %.100s\n(filename %.300s)", + name, buf); + return 0; + } + return fss.name[0] >= namelen && + strncmp(name, (char *)fss.name+1, namelen) == 0; + +/* new-fangled macintosh (macosx) */ +#elif defined(__MACH__) && defined(__APPLE__) && defined(HAVE_DIRENT_H) + DIR *dirp; + struct dirent *dp; + char dirname[MAXPATHLEN + 1]; + const int dirlen = len - namelen - 1; /* don't want trailing SEP */ + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + + /* Copy the dir component into dirname; substitute "." if empty */ + if (dirlen <= 0) { + dirname[0] = '.'; + dirname[1] = '\0'; + } + else { + assert(dirlen <= MAXPATHLEN); + memcpy(dirname, buf, dirlen); + dirname[dirlen] = '\0'; + } + /* Open the directory and search the entries for an exact match. */ + dirp = opendir(dirname); + if (dirp) { + char *nameWithExt = buf + len - namelen; + while ((dp = readdir(dirp)) != NULL) { + const int thislen = +#ifdef _DIRENT_HAVE_D_NAMELEN + dp->d_namlen; +#else + strlen(dp->d_name); +#endif + if (thislen >= namelen && + strcmp(dp->d_name, nameWithExt) == 0) { + (void)closedir(dirp); + return 1; /* Found */ + } + } + (void)closedir(dirp); + } + return 0 ; /* Not found */ + +/* RISC OS */ +#elif defined(RISCOS) + char canon[MAXPATHLEN+1]; /* buffer for the canonical form of the path */ + char buf2[MAXPATHLEN+2]; + char *nameWithExt = buf+len-namelen; + int canonlen; + os_error *e; + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + + /* workaround: + append wildcard, otherwise case of filename wouldn't be touched */ + strcpy(buf2, buf); + strcat(buf2, "*"); + + e = xosfscontrol_canonicalise_path(buf2,canon,0,0,MAXPATHLEN+1,&canonlen); + canonlen = MAXPATHLEN+1-canonlen; + if (e || canonlen<=0 || canonlen>(MAXPATHLEN+1) ) + return 0; + if (strcmp(nameWithExt, canon+canonlen-strlen(nameWithExt))==0) + return 1; /* match */ + + return 0; + +/* OS/2 */ +#elif defined(PYOS_OS2) + HDIR hdir = 1; + ULONG srchcnt = 1; + FILEFINDBUF3 ffbuf; + APIRET rc; + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + + rc = DosFindFirst(buf, + &hdir, + FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_DIRECTORY, + &ffbuf, sizeof(ffbuf), + &srchcnt, + FIL_STANDARD); + if (rc != NO_ERROR) + return 0; + return strncmp(ffbuf.achName, name, namelen) == 0; + +/* assuming it's a case-sensitive filesystem, so there's nothing to do! */ +#else + return 1; + +#endif +} + + +#ifdef HAVE_STAT +/* Helper to look for __init__.py or __init__.py[co] in potential package */ +static int +find_init_module(char *buf) +{ + const size_t save_len = strlen(buf); + size_t i = save_len; + char *pname; /* pointer to start of __init__ */ + struct stat statbuf; + +/* For calling case_ok(buf, len, namelen, name): + * /a/b/c/d/e/f/g/h/i/j/k/some_long_module_name.py\0 + * ^ ^ ^ ^ + * |--------------------- buf ---------------------| + * |------------------- len ------------------| + * |------ name -------| + * |----- namelen -----| + */ + if (save_len + 13 >= MAXPATHLEN) + return 0; + buf[i++] = SEP; + pname = buf + i; + strcpy(pname, "__init__.py"); + if (stat(buf, &statbuf) == 0) { + if (case_ok(buf, + save_len + 9, /* len("/__init__") */ + 8, /* len("__init__") */ + pname)) { + buf[save_len] = '\0'; + return 1; + } + } + i += strlen(pname); + strcpy(buf+i, Py_OptimizeFlag ? "o" : "c"); + if (stat(buf, &statbuf) == 0) { + if (case_ok(buf, + save_len + 9, /* len("/__init__") */ + 8, /* len("__init__") */ + pname)) { + buf[save_len] = '\0'; + return 1; + } + } + buf[save_len] = '\0'; + return 0; +} + +#else + +#ifdef RISCOS +static int +find_init_module(buf) + char *buf; +{ + int save_len = strlen(buf); + int i = save_len; + + if (save_len + 13 >= MAXPATHLEN) + return 0; + buf[i++] = SEP; + strcpy(buf+i, "__init__/py"); + if (isfile(buf)) { + buf[save_len] = '\0'; + return 1; + } + + if (Py_OptimizeFlag) + strcpy(buf+i, "o"); + else + strcpy(buf+i, "c"); + if (isfile(buf)) { + buf[save_len] = '\0'; + return 1; + } + buf[save_len] = '\0'; + return 0; +} +#endif /*RISCOS*/ + +#endif /* HAVE_STAT */ + + +static int init_builtin(char *); /* Forward */ + +/* Load an external module using the default search path and return + its module object WITH INCREMENTED REFERENCE COUNT */ + +static PyObject * +load_module(char *name, FILE *fp, char *buf, int type, PyObject *loader) +{ + PyObject *modules; + PyObject *m; + int err; + + /* First check that there's an open file (if we need one) */ + switch (type) { + case PY_SOURCE: + case PY_COMPILED: + if (fp == NULL) { + PyErr_Format(PyExc_ValueError, + "file object required for import (type code %d)", + type); + return NULL; + } + } + + switch (type) { + + case PY_SOURCE: + m = load_source_module(name, buf, fp); + break; + + case PY_COMPILED: + m = load_compiled_module(name, buf, fp); + break; + +#ifdef HAVE_DYNAMIC_LOADING + case C_EXTENSION: + m = _PyImport_LoadDynamicModule(name, buf, fp); + break; +#endif + +#ifdef macintosh + case PY_RESOURCE: + m = PyMac_LoadResourceModule(name, buf); + break; + case PY_CODERESOURCE: + m = PyMac_LoadCodeResourceModule(name, buf); + break; +#endif + + case PKG_DIRECTORY: + m = load_package(name, buf); + break; + + case C_BUILTIN: + case PY_FROZEN: + if (buf != NULL && buf[0] != '\0') + name = buf; + if (type == C_BUILTIN) + err = init_builtin(name); + else + err = PyImport_ImportFrozenModule(name); + if (err < 0) + return NULL; + if (err == 0) { + PyErr_Format(PyExc_ImportError, + "Purported %s module %.200s not found", + type == C_BUILTIN ? + "builtin" : "frozen", + name); + return NULL; + } + modules = PyImport_GetModuleDict(); + m = PyDict_GetItemString(modules, name); + if (m == NULL) { + PyErr_Format( + PyExc_ImportError, + "%s module %.200s not properly initialized", + type == C_BUILTIN ? + "builtin" : "frozen", + name); + return NULL; + } + Py_INCREF(m); + break; + + case IMP_HOOK: { + if (loader == NULL) { + PyErr_SetString(PyExc_ImportError, + "import hook without loader"); + return NULL; + } + m = PyObject_CallMethod(loader, "load_module", "s", name); + break; + } + + case PLASMA_PACKFILE: + m = load_plasmapacked_module(name, buf); + break; + + default: + PyErr_Format(PyExc_ImportError, + "Don't know how to import %.200s (type code %d)", + name, type); + m = NULL; + + } + + return m; +} + + +/* Initialize a built-in module. + Return 1 for succes, 0 if the module is not found, and -1 with + an exception set if the initialization failed. */ + +static int +init_builtin(char *name) +{ + struct _inittab *p; + + if (_PyImport_FindExtension(name, name) != NULL) + return 1; + + for (p = PyImport_Inittab; p->name != NULL; p++) { + if (strcmp(name, p->name) == 0) { + if (p->initfunc == NULL) { + PyErr_Format(PyExc_ImportError, + "Cannot re-init internal module %.200s", + name); + return -1; + } + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # builtin\n", name); + (*p->initfunc)(); + if (PyErr_Occurred()) + return -1; + if (_PyImport_FixupExtension(name, name) == NULL) + return -1; + return 1; + } + } + return 0; +} + + +/* Frozen modules */ + +static struct _frozen * +find_frozen(char *name) +{ + struct _frozen *p; + + for (p = PyImport_FrozenModules; ; p++) { + if (p->name == NULL) + return NULL; + if (strcmp(p->name, name) == 0) + break; + } + return p; +} + +static PyObject * +get_frozen_object(char *name) +{ + struct _frozen *p = find_frozen(name); + int size; + + if (p == NULL) { + PyErr_Format(PyExc_ImportError, + "No such frozen object named %.200s", + name); + return NULL; + } + if (p->code == NULL) { + PyErr_Format(PyExc_ImportError, + "Excluded frozen object named %.200s", + name); + return NULL; + } + size = p->size; + if (size < 0) + size = -size; + return PyMarshal_ReadObjectFromString((char *)p->code, size); +} + +/* Initialize a frozen module. + Return 1 for succes, 0 if the module is not found, and -1 with + an exception set if the initialization failed. + This function is also used from frozenmain.c */ + +int +PyImport_ImportFrozenModule(char *name) +{ + struct _frozen *p = find_frozen(name); + PyObject *co; + PyObject *m; + int ispackage; + int size; + + if (p == NULL) + return 0; + if (p->code == NULL) { + PyErr_Format(PyExc_ImportError, + "Excluded frozen object named %.200s", + name); + return -1; + } + size = p->size; + ispackage = (size < 0); + if (ispackage) + size = -size; + if (Py_VerboseFlag) + PySys_WriteStderr("import %s # frozen%s\n", + name, ispackage ? " package" : ""); + co = PyMarshal_ReadObjectFromString((char *)p->code, size); + if (co == NULL) + return -1; + if (!PyCode_Check(co)) { + Py_DECREF(co); + PyErr_Format(PyExc_TypeError, + "frozen object %.200s is not a code object", + name); + return -1; + } + if (ispackage) { + /* Set __path__ to the package name */ + PyObject *d, *s; + int err; + m = PyImport_AddModule(name); + if (m == NULL) + return -1; + d = PyModule_GetDict(m); + s = PyString_InternFromString(name); + if (s == NULL) + return -1; + err = PyDict_SetItemString(d, "__path__", s); + Py_DECREF(s); + if (err != 0) + return err; + } + m = PyImport_ExecCodeModuleEx(name, co, ""); + Py_DECREF(co); + if (m == NULL) + return -1; + Py_DECREF(m); + return 1; +} + + +/* Import a module, either built-in, frozen, or external, and return + its module object WITH INCREMENTED REFERENCE COUNT */ + +PyObject * +PyImport_ImportModule(char *name) +{ + PyObject *pname; + PyObject *result; + + pname = PyString_FromString(name); + if (pname == NULL) + return NULL; + result = PyImport_Import(pname); + Py_DECREF(pname); + return result; +} + +/* Forward declarations for helper routines */ +static PyObject *get_parent(PyObject *globals, char *buf, int *p_buflen); +static PyObject *load_next(PyObject *mod, PyObject *altmod, + char **p_name, char *buf, int *p_buflen); +static int mark_miss(char *name); +static int ensure_fromlist(PyObject *mod, PyObject *fromlist, + char *buf, int buflen, int recursive); +static PyObject * import_submodule(PyObject *mod, char *name, char *fullname); + +/* The Magnum Opus of dotted-name import :-) */ + +static PyObject * +import_module_ex(char *name, PyObject *globals, PyObject *locals, + PyObject *fromlist) +{ + char buf[MAXPATHLEN+1]; + int buflen = 0; + PyObject *parent, *head, *next, *tail; + + parent = get_parent(globals, buf, &buflen); + if (parent == NULL) + return NULL; + + head = load_next(parent, Py_None, &name, buf, &buflen); + if (head == NULL) + return NULL; + + tail = head; + Py_INCREF(tail); + while (name) { + next = load_next(tail, tail, &name, buf, &buflen); + Py_DECREF(tail); + if (next == NULL) { + Py_DECREF(head); + return NULL; + } + tail = next; + } + + if (fromlist != NULL) { + if (fromlist == Py_None || !PyObject_IsTrue(fromlist)) + fromlist = NULL; + } + + if (fromlist == NULL) { + Py_DECREF(tail); + return head; + } + + Py_DECREF(head); + if (!ensure_fromlist(tail, fromlist, buf, buflen, 0)) { + Py_DECREF(tail); + return NULL; + } + + return tail; +} + +PyObject * +PyImport_ImportModuleEx(char *name, PyObject *globals, PyObject *locals, + PyObject *fromlist) +{ + PyObject *result; + lock_import(); + result = import_module_ex(name, globals, locals, fromlist); + if (unlock_import() < 0) { + Py_XDECREF(result); + PyErr_SetString(PyExc_RuntimeError, + "not holding the import lock"); + return NULL; + } + return result; +} + +static PyObject * +get_parent(PyObject *globals, char *buf, int *p_buflen) +{ + static PyObject *namestr = NULL; + static PyObject *pathstr = NULL; + PyObject *modname, *modpath, *modules, *parent; + + if (globals == NULL || !PyDict_Check(globals)) + return Py_None; + + if (namestr == NULL) { + namestr = PyString_InternFromString("__name__"); + if (namestr == NULL) + return NULL; + } + if (pathstr == NULL) { + pathstr = PyString_InternFromString("__path__"); + if (pathstr == NULL) + return NULL; + } + + *buf = '\0'; + *p_buflen = 0; + modname = PyDict_GetItem(globals, namestr); + if (modname == NULL || !PyString_Check(modname)) + return Py_None; + + modpath = PyDict_GetItem(globals, pathstr); + if (modpath != NULL) { + int len = PyString_GET_SIZE(modname); + if (len > MAXPATHLEN) { + PyErr_SetString(PyExc_ValueError, + "Module name too long"); + return NULL; + } + strcpy(buf, PyString_AS_STRING(modname)); + *p_buflen = len; + } + else { + char *start = PyString_AS_STRING(modname); + char *lastdot = strrchr(start, '.'); + size_t len; + if (lastdot == NULL) + return Py_None; + len = lastdot - start; + if (len >= MAXPATHLEN) { + PyErr_SetString(PyExc_ValueError, + "Module name too long"); + return NULL; + } + strncpy(buf, start, len); + buf[len] = '\0'; + *p_buflen = len; + } + + modules = PyImport_GetModuleDict(); + parent = PyDict_GetItemString(modules, buf); + if (parent == NULL) + parent = Py_None; + return parent; + /* We expect, but can't guarantee, if parent != None, that: + - parent.__name__ == buf + - parent.__dict__ is globals + If this is violated... Who cares? */ +} + +/* altmod is either None or same as mod */ +static PyObject * +load_next(PyObject *mod, PyObject *altmod, char **p_name, char *buf, + int *p_buflen) +{ + char *name = *p_name; + char *dot = strchr(name, '.'); + size_t len; + char *p; + PyObject *result; + + if (dot == NULL) { + *p_name = NULL; + len = strlen(name); + } + else { + *p_name = dot+1; + len = dot-name; + } + if (len == 0) { + PyErr_SetString(PyExc_ValueError, + "Empty module name"); + return NULL; + } + + p = buf + *p_buflen; + if (p != buf) + *p++ = '.'; + if (p+len-buf >= MAXPATHLEN) { + PyErr_SetString(PyExc_ValueError, + "Module name too long"); + return NULL; + } + strncpy(p, name, len); + p[len] = '\0'; + *p_buflen = p+len-buf; + + result = import_submodule(mod, p, buf); + if (result == Py_None && altmod != mod) { + Py_DECREF(result); + /* Here, altmod must be None and mod must not be None */ + result = import_submodule(altmod, p, p); + if (result != NULL && result != Py_None) { + if (mark_miss(buf) != 0) { + Py_DECREF(result); + return NULL; + } + strncpy(buf, name, len); + buf[len] = '\0'; + *p_buflen = len; + } + } + if (result == NULL) + return NULL; + + if (result == Py_None) { + Py_DECREF(result); + PyErr_Format(PyExc_ImportError, + "No module named %.200s", name); + return NULL; + } + + return result; +} + +static int +mark_miss(char *name) +{ + PyObject *modules = PyImport_GetModuleDict(); + return PyDict_SetItemString(modules, name, Py_None); +} + +static int +ensure_fromlist(PyObject *mod, PyObject *fromlist, char *buf, int buflen, + int recursive) +{ + int i; + + if (!PyObject_HasAttrString(mod, "__path__")) + return 1; + + for (i = 0; ; i++) { + PyObject *item = PySequence_GetItem(fromlist, i); + int hasit; + if (item == NULL) { + if (PyErr_ExceptionMatches(PyExc_IndexError)) { + PyErr_Clear(); + return 1; + } + return 0; + } + if (!PyString_Check(item)) { + PyErr_SetString(PyExc_TypeError, + "Item in ``from list'' not a string"); + Py_DECREF(item); + return 0; + } + if (PyString_AS_STRING(item)[0] == '*') { + PyObject *all; + Py_DECREF(item); + /* See if the package defines __all__ */ + if (recursive) + continue; /* Avoid endless recursion */ + all = PyObject_GetAttrString(mod, "__all__"); + if (all == NULL) + PyErr_Clear(); + else { + if (!ensure_fromlist(mod, all, buf, buflen, 1)) + return 0; + Py_DECREF(all); + } + continue; + } + hasit = PyObject_HasAttr(mod, item); + if (!hasit) { + char *subname = PyString_AS_STRING(item); + PyObject *submod; + char *p; + if (buflen + strlen(subname) >= MAXPATHLEN) { + PyErr_SetString(PyExc_ValueError, + "Module name too long"); + Py_DECREF(item); + return 0; + } + p = buf + buflen; + *p++ = '.'; + strcpy(p, subname); + submod = import_submodule(mod, subname, buf); + Py_XDECREF(submod); + if (submod == NULL) { + Py_DECREF(item); + return 0; + } + } + Py_DECREF(item); + } + + /* NOTREACHED */ +} + +static int +add_submodule(PyObject *mod, PyObject *submod, char *fullname, char *subname, + PyObject *modules) +{ + if (mod == Py_None) + return 1; + /* Irrespective of the success of this load, make a + reference to it in the parent package module. A copy gets + saved in the modules dictionary under the full name, so get a + reference from there, if need be. (The exception is when the + load failed with a SyntaxError -- then there's no trace in + sys.modules. In that case, of course, do nothing extra.) */ + if (submod == NULL) { + submod = PyDict_GetItemString(modules, fullname); + if (submod == NULL) + return 1; + } + if (PyModule_Check(mod)) { + /* We can't use setattr here since it can give a + * spurious warning if the submodule name shadows a + * builtin name */ + PyObject *dict = PyModule_GetDict(mod); + if (!dict) + return 0; + if (PyDict_SetItemString(dict, subname, submod) < 0) + return 0; + } + else { + if (PyObject_SetAttrString(mod, subname, submod) < 0) + return 0; + } + return 1; +} + +static PyObject * +import_submodule(PyObject *mod, char *subname, char *fullname) +{ + PyObject *modules = PyImport_GetModuleDict(); + PyObject *m = NULL; + + /* Require: + if mod == None: subname == fullname + else: mod.__name__ + "." + subname == fullname + */ + + if ((m = PyDict_GetItemString(modules, fullname)) != NULL) { + Py_INCREF(m); + } + else { + PyObject *path, *loader = NULL; + char buf[MAXPATHLEN+1]; + struct filedescr *fdp; + FILE *fp = NULL; + + if (mod == Py_None) + path = NULL; + else { + path = PyObject_GetAttrString(mod, "__path__"); + if (path == NULL) { + PyErr_Clear(); + Py_INCREF(Py_None); + return Py_None; + } + } + + buf[0] = '\0'; + fdp = find_module(fullname, subname, path, buf, MAXPATHLEN+1, + &fp, &loader); + Py_XDECREF(path); + if (fdp == NULL) { + if (!PyErr_ExceptionMatches(PyExc_ImportError)) + return NULL; + PyErr_Clear(); + Py_INCREF(Py_None); + return Py_None; + } + m = load_module(fullname, fp, buf, fdp->type, loader); + Py_XDECREF(loader); + if (fp) + fclose(fp); + if (!add_submodule(mod, m, fullname, subname, modules)) { + Py_XDECREF(m); + m = NULL; + } + } + + return m; +} + + +/* Re-import a module of any kind and return its module object, WITH + INCREMENTED REFERENCE COUNT */ + +PyObject * +PyImport_ReloadModule(PyObject *m) +{ + PyObject *modules = PyImport_GetModuleDict(); + PyObject *path = NULL; + char *name, *subname; + char buf[MAXPATHLEN+1]; + struct filedescr *fdp; + FILE *fp = NULL; + + if (m == NULL || !PyModule_Check(m)) { + PyErr_SetString(PyExc_TypeError, + "reload() argument must be module"); + return NULL; + } + name = PyModule_GetName(m); + if (name == NULL) + return NULL; + if (m != PyDict_GetItemString(modules, name)) { + PyErr_Format(PyExc_ImportError, + "reload(): module %.200s not in sys.modules", + name); + return NULL; + } + subname = strrchr(name, '.'); + if (subname == NULL) + subname = name; + else { + PyObject *parentname, *parent; + parentname = PyString_FromStringAndSize(name, (subname-name)); + if (parentname == NULL) + return NULL; + parent = PyDict_GetItem(modules, parentname); + Py_DECREF(parentname); + if (parent == NULL) { + PyErr_Format(PyExc_ImportError, + "reload(): parent %.200s not in sys.modules", + name); + return NULL; + } + subname++; + path = PyObject_GetAttrString(parent, "__path__"); + if (path == NULL) + PyErr_Clear(); + } + buf[0] = '\0'; + fdp = find_module(name, subname, path, buf, MAXPATHLEN+1, &fp, NULL); + Py_XDECREF(path); + if (fdp == NULL) + return NULL; + m = load_module(name, fp, buf, fdp->type, NULL); + if (fp) + fclose(fp); + return m; +} + + +/* Higher-level import emulator which emulates the "import" statement + more accurately -- it invokes the __import__() function from the + builtins of the current globals. This means that the import is + done using whatever import hooks are installed in the current + environment, e.g. by "rexec". + A dummy list ["__doc__"] is passed as the 4th argument so that + e.g. PyImport_Import(PyString_FromString("win32com.client.gencache")) + will return instead of . */ + +PyObject * +PyImport_Import(PyObject *module_name) +{ + static PyObject *silly_list = NULL; + static PyObject *builtins_str = NULL; + static PyObject *import_str = NULL; + PyObject *globals = NULL; + PyObject *import = NULL; + PyObject *builtins = NULL; + PyObject *r = NULL; + + /* Initialize constant string objects */ + if (silly_list == NULL) { + import_str = PyString_InternFromString("__import__"); + if (import_str == NULL) + return NULL; + builtins_str = PyString_InternFromString("__builtins__"); + if (builtins_str == NULL) + return NULL; + silly_list = Py_BuildValue("[s]", "__doc__"); + if (silly_list == NULL) + return NULL; + } + + /* Get the builtins from current globals */ + globals = PyEval_GetGlobals(); + if (globals != NULL) { + Py_INCREF(globals); + builtins = PyObject_GetItem(globals, builtins_str); + if (builtins == NULL) + goto err; + } + else { + /* No globals -- use standard builtins, and fake globals */ + PyErr_Clear(); + + builtins = PyImport_ImportModuleEx("__builtin__", + NULL, NULL, NULL); + if (builtins == NULL) + return NULL; + globals = Py_BuildValue("{OO}", builtins_str, builtins); + if (globals == NULL) + goto err; + } + + /* Get the __import__ function from the builtins */ + if (PyDict_Check(builtins)) { + import = PyObject_GetItem(builtins, import_str); + if (import == NULL) + PyErr_SetObject(PyExc_KeyError, import_str); + } + else + import = PyObject_GetAttr(builtins, import_str); + if (import == NULL) + goto err; + + /* Call the _import__ function with the proper argument list */ + r = PyObject_CallFunction(import, "OOOO", + module_name, globals, globals, silly_list); + + err: + Py_XDECREF(globals); + Py_XDECREF(builtins); + Py_XDECREF(import); + + return r; +} + + +/* Module 'imp' provides Python access to the primitives used for + importing modules. +*/ + +static PyObject * +imp_get_magic(PyObject *self, PyObject *noargs) +{ + char buf[4]; + + buf[0] = (char) ((pyc_magic >> 0) & 0xff); + buf[1] = (char) ((pyc_magic >> 8) & 0xff); + buf[2] = (char) ((pyc_magic >> 16) & 0xff); + buf[3] = (char) ((pyc_magic >> 24) & 0xff); + + return PyString_FromStringAndSize(buf, 4); +} + +static PyObject * +imp_get_suffixes(PyObject *self, PyObject *noargs) +{ + PyObject *list; + struct filedescr *fdp; + + list = PyList_New(0); + if (list == NULL) + return NULL; + for (fdp = _PyImport_Filetab; fdp->suffix != NULL; fdp++) { + PyObject *item = Py_BuildValue("ssi", + fdp->suffix, fdp->mode, fdp->type); + if (item == NULL) { + Py_DECREF(list); + return NULL; + } + if (PyList_Append(list, item) < 0) { + Py_DECREF(list); + Py_DECREF(item); + return NULL; + } + Py_DECREF(item); + } + return list; +} + +static PyObject * +call_find_module(char *name, PyObject *path) +{ + extern int fclose(FILE *); + PyObject *fob, *ret; + struct filedescr *fdp; + char pathname[MAXPATHLEN+1]; + FILE *fp = NULL; + + pathname[0] = '\0'; + if (path == Py_None) + path = NULL; + fdp = find_module(NULL, name, path, pathname, MAXPATHLEN+1, &fp, NULL); + if (fdp == NULL) + return NULL; + if (fp != NULL) { + fob = PyFile_FromFile(fp, pathname, fdp->mode, fclose); + if (fob == NULL) { + fclose(fp); + return NULL; + } + } + else { + fob = Py_None; + Py_INCREF(fob); + } + ret = Py_BuildValue("Os(ssi)", + fob, pathname, fdp->suffix, fdp->mode, fdp->type); + Py_DECREF(fob); + return ret; +} + +static PyObject * +imp_find_module(PyObject *self, PyObject *args) +{ + char *name; + PyObject *path = NULL; + if (!PyArg_ParseTuple(args, "s|O:find_module", &name, &path)) + return NULL; + return call_find_module(name, path); +} + +static PyObject * +imp_init_builtin(PyObject *self, PyObject *args) +{ + char *name; + int ret; + PyObject *m; + if (!PyArg_ParseTuple(args, "s:init_builtin", &name)) + return NULL; + ret = init_builtin(name); + if (ret < 0) + return NULL; + if (ret == 0) { + Py_INCREF(Py_None); + return Py_None; + } + m = PyImport_AddModule(name); + Py_XINCREF(m); + return m; +} + +static PyObject * +imp_init_frozen(PyObject *self, PyObject *args) +{ + char *name; + int ret; + PyObject *m; + if (!PyArg_ParseTuple(args, "s:init_frozen", &name)) + return NULL; + ret = PyImport_ImportFrozenModule(name); + if (ret < 0) + return NULL; + if (ret == 0) { + Py_INCREF(Py_None); + return Py_None; + } + m = PyImport_AddModule(name); + Py_XINCREF(m); + return m; +} + +static PyObject * +imp_get_frozen_object(PyObject *self, PyObject *args) +{ + char *name; + + if (!PyArg_ParseTuple(args, "s:get_frozen_object", &name)) + return NULL; + return get_frozen_object(name); +} + +static PyObject * +imp_is_builtin(PyObject *self, PyObject *args) +{ + char *name; + if (!PyArg_ParseTuple(args, "s:is_builtin", &name)) + return NULL; + return PyInt_FromLong(is_builtin(name)); +} + +static PyObject * +imp_is_frozen(PyObject *self, PyObject *args) +{ + char *name; + struct _frozen *p; + if (!PyArg_ParseTuple(args, "s:is_frozen", &name)) + return NULL; + p = find_frozen(name); + return PyBool_FromLong((long) (p == NULL ? 0 : p->size)); +} + +static FILE * +get_file(char *pathname, PyObject *fob, char *mode) +{ + FILE *fp; + if (fob == NULL) { + if (mode[0] == 'U') + mode = "r" PY_STDIOTEXTMODE; + fp = fopen(pathname, mode); + if (fp == NULL) + PyErr_SetFromErrno(PyExc_IOError); + } + else { + fp = PyFile_AsFile(fob); + if (fp == NULL) + PyErr_SetString(PyExc_ValueError, + "bad/closed file object"); + } + return fp; +} + +static PyObject * +imp_load_compiled(PyObject *self, PyObject *args) +{ + char *name; + char *pathname; + PyObject *fob = NULL; + PyObject *m; + FILE *fp; + if (!PyArg_ParseTuple(args, "ss|O!:load_compiled", &name, &pathname, + &PyFile_Type, &fob)) + return NULL; + fp = get_file(pathname, fob, "rb"); + if (fp == NULL) + return NULL; + m = load_compiled_module(name, pathname, fp); + if (fob == NULL) + fclose(fp); + return m; +} + +#ifdef HAVE_DYNAMIC_LOADING + +static PyObject * +imp_load_dynamic(PyObject *self, PyObject *args) +{ + char *name; + char *pathname; + PyObject *fob = NULL; + PyObject *m; + FILE *fp = NULL; + if (!PyArg_ParseTuple(args, "ss|O!:load_dynamic", &name, &pathname, + &PyFile_Type, &fob)) + return NULL; + if (fob) { + fp = get_file(pathname, fob, "r"); + if (fp == NULL) + return NULL; + } + m = _PyImport_LoadDynamicModule(name, pathname, fp); + return m; +} + +#endif /* HAVE_DYNAMIC_LOADING */ + +static PyObject * +imp_load_source(PyObject *self, PyObject *args) +{ + char *name; + char *pathname; + PyObject *fob = NULL; + PyObject *m; + FILE *fp; + if (!PyArg_ParseTuple(args, "ss|O!:load_source", &name, &pathname, + &PyFile_Type, &fob)) + return NULL; + fp = get_file(pathname, fob, "r"); + if (fp == NULL) + return NULL; + m = load_source_module(name, pathname, fp); + if (fob == NULL) + fclose(fp); + return m; +} + +#ifdef macintosh +static PyObject * +imp_load_resource(PyObject *self, PyObject *args) +{ + char *name; + char *pathname; + PyObject *m; + + if (!PyArg_ParseTuple(args, "ss:load_resource", &name, &pathname)) + return NULL; + m = PyMac_LoadResourceModule(name, pathname); + return m; +} +#endif /* macintosh */ + +static PyObject * +imp_load_module(PyObject *self, PyObject *args) +{ + char *name; + PyObject *fob; + char *pathname; + char *suffix; /* Unused */ + char *mode; + int type; + FILE *fp; + + if (!PyArg_ParseTuple(args, "sOs(ssi):load_module", + &name, &fob, &pathname, + &suffix, &mode, &type)) + return NULL; + if (*mode) { + /* Mode must start with 'r' or 'U' and must not contain '+'. + Implicit in this test is the assumption that the mode + may contain other modifiers like 'b' or 't'. */ + + if (!(*mode == 'r' || *mode == 'U') || strchr(mode, '+')) { + PyErr_Format(PyExc_ValueError, + "invalid file open mode %.200s", mode); + return NULL; + } + } + if (fob == Py_None) + fp = NULL; + else { + if (!PyFile_Check(fob)) { + PyErr_SetString(PyExc_ValueError, + "load_module arg#2 should be a file or None"); + return NULL; + } + fp = get_file(pathname, fob, mode); + if (fp == NULL) + return NULL; + } + return load_module(name, fp, pathname, type, NULL); +} + +static PyObject * +imp_load_package(PyObject *self, PyObject *args) +{ + char *name; + char *pathname; + if (!PyArg_ParseTuple(args, "ss:load_package", &name, &pathname)) + return NULL; + return load_package(name, pathname); +} + +static PyObject * +imp_new_module(PyObject *self, PyObject *args) +{ + char *name; + if (!PyArg_ParseTuple(args, "s:new_module", &name)) + return NULL; + return PyModule_New(name); +} + +/* Doc strings */ + +PyDoc_STRVAR(doc_imp, +"This module provides the components needed to build your own\n\ +__import__ function. Undocumented functions are obsolete."); + +PyDoc_STRVAR(doc_find_module, +"find_module(name, [path]) -> (file, filename, (suffix, mode, type))\n\ +Search for a module. If path is omitted or None, search for a\n\ +built-in, frozen or special module and continue search in sys.path.\n\ +The module name cannot contain '.'; to search for a submodule of a\n\ +package, pass the submodule name and the package's __path__."); + +PyDoc_STRVAR(doc_load_module, +"load_module(name, file, filename, (suffix, mode, type)) -> module\n\ +Load a module, given information returned by find_module().\n\ +The module name must include the full package name, if any."); + +PyDoc_STRVAR(doc_get_magic, +"get_magic() -> string\n\ +Return the magic number for .pyc or .pyo files."); + +PyDoc_STRVAR(doc_get_suffixes, +"get_suffixes() -> [(suffix, mode, type), ...]\n\ +Return a list of (suffix, mode, type) tuples describing the files\n\ +that find_module() looks for."); + +PyDoc_STRVAR(doc_new_module, +"new_module(name) -> module\n\ +Create a new module. Do not enter it in sys.modules.\n\ +The module name must include the full package name, if any."); + +PyDoc_STRVAR(doc_lock_held, +"lock_held() -> 0 or 1\n\ +Return 1 if the import lock is currently held.\n\ +On platforms without threads, return 0."); + +PyDoc_STRVAR(doc_acquire_lock, +"acquire_lock() -> None\n\ +Acquires the interpreter's import lock for the current thread.\n\ +This lock should be used by import hooks to ensure thread-safety\n\ +when importing modules.\n\ +On platforms without threads, this function does nothing."); + +PyDoc_STRVAR(doc_release_lock, +"release_lock() -> None\n\ +Release the interpreter's import lock.\n\ +On platforms without threads, this function does nothing."); + +static PyMethodDef imp_methods[] = { + {"find_module", imp_find_module, METH_VARARGS, doc_find_module}, + {"get_magic", imp_get_magic, METH_NOARGS, doc_get_magic}, + {"get_suffixes", imp_get_suffixes, METH_NOARGS, doc_get_suffixes}, + {"load_module", imp_load_module, METH_VARARGS, doc_load_module}, + {"new_module", imp_new_module, METH_VARARGS, doc_new_module}, + {"lock_held", imp_lock_held, METH_NOARGS, doc_lock_held}, + {"acquire_lock", imp_acquire_lock, METH_NOARGS, doc_acquire_lock}, + {"release_lock", imp_release_lock, METH_NOARGS, doc_release_lock}, + /* The rest are obsolete */ + {"get_frozen_object", imp_get_frozen_object, METH_VARARGS}, + {"init_builtin", imp_init_builtin, METH_VARARGS}, + {"init_frozen", imp_init_frozen, METH_VARARGS}, + {"is_builtin", imp_is_builtin, METH_VARARGS}, + {"is_frozen", imp_is_frozen, METH_VARARGS}, + {"load_compiled", imp_load_compiled, METH_VARARGS}, +#ifdef HAVE_DYNAMIC_LOADING + {"load_dynamic", imp_load_dynamic, METH_VARARGS}, +#endif + {"load_package", imp_load_package, METH_VARARGS}, +#ifdef macintosh + {"load_resource", imp_load_resource, METH_VARARGS}, +#endif + {"load_source", imp_load_source, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +static int +setint(PyObject *d, char *name, int value) +{ + PyObject *v; + int err; + + v = PyInt_FromLong((long)value); + err = PyDict_SetItemString(d, name, v); + Py_XDECREF(v); + return err; +} + +void +initimp(void) +{ + PyObject *m, *d; + + m = Py_InitModule4("imp", imp_methods, doc_imp, + NULL, PYTHON_API_VERSION); + d = PyModule_GetDict(m); + + if (setint(d, "SEARCH_ERROR", SEARCH_ERROR) < 0) goto failure; + if (setint(d, "PY_SOURCE", PY_SOURCE) < 0) goto failure; + if (setint(d, "PY_COMPILED", PY_COMPILED) < 0) goto failure; + if (setint(d, "C_EXTENSION", C_EXTENSION) < 0) goto failure; + if (setint(d, "PY_RESOURCE", PY_RESOURCE) < 0) goto failure; + if (setint(d, "PKG_DIRECTORY", PKG_DIRECTORY) < 0) goto failure; + if (setint(d, "C_BUILTIN", C_BUILTIN) < 0) goto failure; + if (setint(d, "PY_FROZEN", PY_FROZEN) < 0) goto failure; + if (setint(d, "PY_CODERESOURCE", PY_CODERESOURCE) < 0) goto failure; + if (setint(d, "IMP_HOOK", IMP_HOOK) < 0) goto failure; + if (setint(d, "PLASMA_PACKFILE", PLASMA_PACKFILE) < 0) goto failure; + + failure: + ; +} + + +/* API for embedding applications that want to add their own entries + to the table of built-in modules. This should normally be called + *before* Py_Initialize(). When the table resize fails, -1 is + returned and the existing table is unchanged. + + After a similar function by Just van Rossum. */ + +int +PyImport_ExtendInittab(struct _inittab *newtab) +{ + static struct _inittab *our_copy = NULL; + struct _inittab *p; + int i, n; + + /* Count the number of entries in both tables */ + for (n = 0; newtab[n].name != NULL; n++) + ; + if (n == 0) + return 0; /* Nothing to do */ + for (i = 0; PyImport_Inittab[i].name != NULL; i++) + ; + + /* Allocate new memory for the combined table */ + p = our_copy; + PyMem_RESIZE(p, struct _inittab, i+n+1); + if (p == NULL) + return -1; + + /* Copy the tables into the new memory */ + if (our_copy != PyImport_Inittab) + memcpy(p, PyImport_Inittab, (i+1) * sizeof(struct _inittab)); + PyImport_Inittab = our_copy = p; + memcpy(p+i, newtab, (n+1) * sizeof(struct _inittab)); + + return 0; +} + +/* Shorthand to add a single entry given a name and a function */ + +int +PyImport_AppendInittab(char *name, void (*initfunc)(void)) +{ + struct _inittab newtab[2]; + + memset(newtab, '\0', sizeof newtab); + + newtab[0].name = name; + newtab[0].initfunc = initfunc; + + return PyImport_ExtendInittab(newtab); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/importdl.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/importdl.c new file mode 100644 index 00000000..5784a1c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/importdl.c @@ -0,0 +1,78 @@ + +/* Support for dynamic loading of extension modules */ + +#include "Python.h" + +/* ./configure sets HAVE_DYNAMIC_LOADING if dynamic loading of modules is + supported on this platform. configure will then compile and link in one + of the dynload_*.c files, as appropriate. We will call a function in + those modules to get a function pointer to the module's init function. +*/ +#ifdef HAVE_DYNAMIC_LOADING + +#include "importdl.h" + +extern dl_funcptr _PyImport_GetDynLoadFunc(const char *name, + const char *shortname, + const char *pathname, FILE *fp); + + + +PyObject * +_PyImport_LoadDynamicModule(char *name, char *pathname, FILE *fp) +{ + PyObject *m; + char *lastdot, *shortname, *packagecontext, *oldcontext; + dl_funcptr p; + + if ((m = _PyImport_FindExtension(name, pathname)) != NULL) { + Py_INCREF(m); + return m; + } + lastdot = strrchr(name, '.'); + if (lastdot == NULL) { + packagecontext = NULL; + shortname = name; + } + else { + packagecontext = name; + shortname = lastdot+1; + } + + p = _PyImport_GetDynLoadFunc(name, shortname, pathname, fp); + if (PyErr_Occurred()) + return NULL; + if (p == NULL) { + PyErr_Format(PyExc_ImportError, + "dynamic module does not define init function (init%.200s)", + shortname); + return NULL; + } + oldcontext = _Py_PackageContext; + _Py_PackageContext = packagecontext; + (*p)(); + _Py_PackageContext = oldcontext; + if (PyErr_Occurred()) + return NULL; + + m = PyDict_GetItemString(PyImport_GetModuleDict(), name); + if (m == NULL) { + PyErr_SetString(PyExc_SystemError, + "dynamic module not initialized properly"); + return NULL; + } + /* Remember the filename as the __file__ attribute */ + if (PyModule_AddStringConstant(m, "__file__", pathname) < 0) + PyErr_Clear(); /* Not important enough to report */ + + if (_PyImport_FixupExtension(name, pathname) == NULL) + return NULL; + if (Py_VerboseFlag) + PySys_WriteStderr( + "import %s # dynamically loaded from %s\n", + name, pathname); + Py_INCREF(m); + return m; +} + +#endif /* HAVE_DYNAMIC_LOADING */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/importdl.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/importdl.h new file mode 100644 index 00000000..b2c2ea78 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/importdl.h @@ -0,0 +1,54 @@ +#ifndef Py_IMPORTDL_H +#define Py_IMPORTDL_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Definitions for dynamic loading of extension modules */ +enum filetype { + SEARCH_ERROR, + PY_SOURCE, + PY_COMPILED, + C_EXTENSION, + PY_RESOURCE, /* Mac only */ + PKG_DIRECTORY, + C_BUILTIN, + PY_FROZEN, + PY_CODERESOURCE, /* Mac only */ + IMP_HOOK, + PLASMA_PACKFILE +}; + +struct filedescr { + char *suffix; + char *mode; + enum filetype type; +}; +extern struct filedescr * _PyImport_Filetab; +extern const struct filedescr _PyImport_DynLoadFiletab[]; + +extern PyObject *_PyImport_LoadDynamicModule(char *name, char *pathname, + FILE *); + +/* Max length of module suffix searched for -- accommodates "module.slb" */ +#define MAXSUFFIXSIZE 12 + +#if defined(MS_WINDOWS) && !defined(MS_XBOX) +#include +typedef FARPROC dl_funcptr; +#else +#if defined(PYOS_OS2) && !defined(PYCC_GCC) +#include +typedef int (* APIENTRY dl_funcptr)(); +#else +typedef void (*dl_funcptr)(void); +#endif +#endif + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_IMPORTDL_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mactoolboxglue.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mactoolboxglue.c new file mode 100644 index 00000000..2965bc25 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mactoolboxglue.c @@ -0,0 +1,621 @@ +/*********************************************************** +Copyright 1991-1997 by Stichting Mathematisch Centrum, Amsterdam, +The Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + + +#include "Python.h" +#include "pymactoolbox.h" + +#ifdef WITHOUT_FRAMEWORKS +#include +#include +#endif + +/* +** Find out what the current script is. +** Donated by Fredrik Lund. +*/ +char *PyMac_getscript() +{ +#if TARGET_API_MAC_OSX + CFStringEncoding enc = CFStringGetSystemEncoding(); + static CFStringRef name = NULL; + /* Return the code name for the encodings for which we have codecs. */ + switch(enc) { + case kCFStringEncodingMacRoman: return "mac-roman"; + case kCFStringEncodingMacGreek: return "mac-greek"; + case kCFStringEncodingMacCyrillic: return "mac-cyrillic"; + case kCFStringEncodingMacTurkish: return "mac-turkish"; + case kCFStringEncodingMacIcelandic: return "mac-icelandic"; + /* XXX which one is mac-latin2? */ + } + if (!name) { + /* This leaks a an object. */ + name = CFStringConvertEncodingToIANACharSetName(enc); + } + return (char *)CFStringGetCStringPtr(name, 0); +#else + int font, script, lang; + font = 0; + font = GetSysFont(); + script = FontToScript(font); + switch (script) { + case smRoman: + lang = GetScriptVariable(script, smScriptLang); + if (lang == langIcelandic) + return "mac-iceland"; + else if (lang == langTurkish) + return "mac-turkish"; + else if (lang == langGreek) + return "mac-greek"; + else + return "mac-roman"; + break; +#if 0 + /* We don't have a codec for this, so don't return it */ + case smJapanese: + return "mac-japan"; +#endif + case smGreek: + return "mac-greek"; + case smCyrillic: + return "mac-cyrillic"; + default: + return "ascii"; /* better than nothing */ + } +#endif /* TARGET_API_MAC_OSX */ +} + +/* Like strerror() but for Mac OS error numbers */ +char *PyMac_StrError(int err) +{ + static char buf[256]; + Handle h; + char *str; + static int errors_loaded; + + h = GetResource('Estr', err); + if (!h && !errors_loaded) { + /* + ** Attempt to open the resource file containing the + ** Estr resources. We ignore all errors. We also try + ** this only once. + */ + PyObject *m, *rv; + errors_loaded = 1; + + m = PyImport_ImportModule("macresource"); + if (!m) { + if (Py_VerboseFlag) + PyErr_Print(); + PyErr_Clear(); + } else { + rv = PyObject_CallMethod(m, "open_error_resource", ""); + if (!rv) { + if (Py_VerboseFlag) + PyErr_Print(); + PyErr_Clear(); + } else { + Py_DECREF(rv); + /* And try again... */ + h = GetResource('Estr', err); + } + } + } + /* + ** Whether the code above succeeded or not, we won't try + ** again. + */ + errors_loaded = 1; + + if ( h ) { + HLock(h); + str = (char *)*h; + memcpy(buf, str+1, (unsigned char)str[0]); + buf[(unsigned char)str[0]] = '\0'; + HUnlock(h); + ReleaseResource(h); + } else { + PyOS_snprintf(buf, sizeof(buf), "Mac OS error code %d", err); + } + return buf; +} + +/* Exception object shared by all Mac specific modules for Mac OS errors */ +PyObject *PyMac_OSErrException; + +/* Initialize and return PyMac_OSErrException */ +PyObject * +PyMac_GetOSErrException(void) +{ + if (PyMac_OSErrException == NULL) + PyMac_OSErrException = PyErr_NewException("MacOS.Error", NULL, NULL); + return PyMac_OSErrException; +} + +/* Set a MAC-specific error from errno, and return NULL; return None if no error */ +PyObject * +PyErr_Mac(PyObject *eobj, int err) +{ + char *msg; + PyObject *v; + + if (err == 0 && !PyErr_Occurred()) { + Py_INCREF(Py_None); + return Py_None; + } + if (err == -1 && PyErr_Occurred()) + return NULL; + msg = PyMac_StrError(err); + v = Py_BuildValue("(is)", err, msg); + PyErr_SetObject(eobj, v); + Py_DECREF(v); + return NULL; +} + +/* Call PyErr_Mac with PyMac_OSErrException */ +PyObject * +PyMac_Error(OSErr err) +{ + return PyErr_Mac(PyMac_GetOSErrException(), err); +} + + +#if TARGET_API_MAC_OSX +OSErr +PyMac_GetFullPathname(FSSpec *fss, char *path, int len) +{ + FSRef fsr; + OSErr err; + + *path = '\0'; + err = FSpMakeFSRef(fss, &fsr); + if ( err == fnfErr ) { + /* FSSpecs can point to non-existing files, fsrefs can't. */ + FSSpec fss2; + int tocopy; + + err = FSMakeFSSpec(fss->vRefNum, fss->parID, "", &fss2); + if ( err ) return err; + err = FSpMakeFSRef(&fss2, &fsr); + if ( err ) return err; + err = (OSErr)FSRefMakePath(&fsr, path, len-1); + if ( err ) return err; + /* This part is not 100% safe: we append the filename part, but + ** I'm not sure that we don't run afoul of the various 8bit + ** encodings here. Will have to look this up at some point... + */ + strcat(path, "/"); + tocopy = fss->name[0]; + if ( strlen(path) + tocopy >= len ) + tocopy = len - strlen(path) - 1; + if ( tocopy > 0 ) + strncat(path, fss->name+1, tocopy); + } else { + if ( err ) return err; + err = (OSErr)FSRefMakePath(&fsr, path, len); + if ( err ) return err; + } + return 0; +} + +#endif /* TARGET_API_MAC_OSX */ + +#ifdef WITH_NEXT_FRAMEWORK +/* +** In a bundle, find a file "resourceName" of type "resourceType". Return the +** full pathname in "resourceURLCstr". +*/ +static int +locateResourcePy(CFStringRef resourceType, CFStringRef resourceName, char *resourceURLCStr, int length) +{ + CFBundleRef mainBundle = NULL; + CFURLRef URL, absoluteURL; + CFStringRef filenameString, filepathString; + CFIndex size, i; + CFArrayRef arrayRef = NULL; + int success = 0; + +#if TARGET_API_MAC_OSX + CFURLPathStyle thePathStyle = kCFURLPOSIXPathStyle; +#else + CFURLPathStyle thePathStyle = kCFURLHFSPathStyle; +#endif + + /* Get a reference to our main bundle */ + mainBundle = CFBundleGetMainBundle(); + + /* If we are running inside a bundle, look through it. Otherwise, do nothing. */ + if (mainBundle) { + + /* Look for py files in the main bundle by type */ + arrayRef = CFBundleCopyResourceURLsOfType( mainBundle, + resourceType, + NULL ); + + /* See if there are any filename matches */ + size = CFArrayGetCount(arrayRef); + for (i = 0; i < size; i++) { + URL = CFArrayGetValueAtIndex(arrayRef, i); + filenameString = CFURLCopyLastPathComponent(URL); + if (CFStringCompare(filenameString, resourceName, 0) == kCFCompareEqualTo) { + /* We found a match, get the file's full path */ + absoluteURL = CFURLCopyAbsoluteURL(URL); + filepathString = CFURLCopyFileSystemPath(absoluteURL, thePathStyle); + CFRelease(absoluteURL); + + /* Copy the full path into the caller's character buffer */ + success = CFStringGetCString(filepathString, resourceURLCStr, length, + kCFStringEncodingMacRoman); + + CFRelease(filepathString); + } + CFRelease(filenameString); + } + CFRelease(arrayRef); + } + return success; +} + +/* +** iff we are running in a .app framework then we could be +** the main program for an applet. In that case, return the +** script filename for the applet. +** Otherwise return NULL. +*/ +char * +PyMac_GetAppletScriptFile(void) +{ + static char scriptpath[1024]; + + /* First we see whether we have __rawmain__.py and run that if it + ** is there. This is used for applets that want sys.argv to be + ** unix-like: __rawmain__ will construct it (from the initial appleevent) + ** and then call __main__.py. + */ + if (locateResourcePy(CFSTR("py"), CFSTR("__rawmain__.py"), scriptpath, 1024)) { + return scriptpath; + } else if (locateResourcePy(CFSTR("pyc"), CFSTR("__rawmain__.pyc"), scriptpath, 1024)) { + return scriptpath; + } else if (locateResourcePy(CFSTR("py"), CFSTR("__main__.py"), scriptpath, 1024)) { + return scriptpath; + } else if (locateResourcePy(CFSTR("pyc"), CFSTR("__main__.pyc"), scriptpath, 1024)) { + return scriptpath; + } + return NULL; +} + +#endif + + +/* Convert a 4-char string object argument to an OSType value */ +int +PyMac_GetOSType(PyObject *v, OSType *pr) +{ + if (!PyString_Check(v) || PyString_Size(v) != 4) { + PyErr_SetString(PyExc_TypeError, + "OSType arg must be string of 4 chars"); + return 0; + } + memcpy((char *)pr, PyString_AsString(v), 4); + return 1; +} + +/* Convert an OSType value to a 4-char string object */ +PyObject * +PyMac_BuildOSType(OSType t) +{ + return PyString_FromStringAndSize((char *)&t, 4); +} + +/* Convert an NumVersion value to a 4-element tuple */ +PyObject * +PyMac_BuildNumVersion(NumVersion t) +{ + return Py_BuildValue("(hhhh)", t.majorRev, t.minorAndBugRev, t.stage, t.nonRelRev); +} + + +/* Convert a Python string object to a Str255 */ +int +PyMac_GetStr255(PyObject *v, Str255 pbuf) +{ + int len; + if (!PyString_Check(v) || (len = PyString_Size(v)) > 255) { + PyErr_SetString(PyExc_TypeError, + "Str255 arg must be string of at most 255 chars"); + return 0; + } + pbuf[0] = len; + memcpy((char *)(pbuf+1), PyString_AsString(v), len); + return 1; +} + +/* Convert a Str255 to a Python string object */ +PyObject * +PyMac_BuildStr255(Str255 s) +{ + if ( s == NULL ) { + PyErr_SetString(PyExc_SystemError, "Str255 pointer is NULL"); + return NULL; + } + return PyString_FromStringAndSize((char *)&s[1], (int)s[0]); +} + +PyObject * +PyMac_BuildOptStr255(Str255 s) +{ + if ( s == NULL ) { + Py_INCREF(Py_None); + return Py_None; + } + return PyString_FromStringAndSize((char *)&s[1], (int)s[0]); +} + + + +/* Convert a Python object to a Rect. + The object must be a (left, top, right, bottom) tuple. + (This differs from the order in the struct but is consistent with + the arguments to SetRect(), and also with STDWIN). */ +int +PyMac_GetRect(PyObject *v, Rect *r) +{ + return PyArg_Parse(v, "(hhhh)", &r->left, &r->top, &r->right, &r->bottom); +} + +/* Convert a Rect to a Python object */ +PyObject * +PyMac_BuildRect(Rect *r) +{ + return Py_BuildValue("(hhhh)", r->left, r->top, r->right, r->bottom); +} + + +/* Convert a Python object to a Point. + The object must be a (h, v) tuple. + (This differs from the order in the struct but is consistent with + the arguments to SetPoint(), and also with STDWIN). */ +int +PyMac_GetPoint(PyObject *v, Point *p) +{ + return PyArg_Parse(v, "(hh)", &p->h, &p->v); +} + +/* Convert a Point to a Python object */ +PyObject * +PyMac_BuildPoint(Point p) +{ + return Py_BuildValue("(hh)", p.h, p.v); +} + + +/* Convert a Python object to an EventRecord. + The object must be a (what, message, when, (v, h), modifiers) tuple. */ +int +PyMac_GetEventRecord(PyObject *v, EventRecord *e) +{ + return PyArg_Parse(v, "(Hkk(hh)H)", + &e->what, + &e->message, + &e->when, + &e->where.h, + &e->where.v, + &e->modifiers); +} + +/* Convert a Rect to an EventRecord object */ +PyObject * +PyMac_BuildEventRecord(EventRecord *e) +{ + return Py_BuildValue("(hll(hh)h)", + e->what, + e->message, + e->when, + e->where.h, + e->where.v, + e->modifiers); +} + +/* Convert Python object to Fixed */ +int +PyMac_GetFixed(PyObject *v, Fixed *f) +{ + double d; + + if( !PyArg_Parse(v, "d", &d)) + return 0; + *f = (Fixed)(d * 0x10000); + return 1; +} + +/* Convert a Fixed to a Python object */ +PyObject * +PyMac_BuildFixed(Fixed f) +{ + double d; + + d = f; + d = d / 0x10000; + return Py_BuildValue("d", d); +} + +/* Convert wide to/from Python int or (hi, lo) tuple. XXXX Should use Python longs */ +int +PyMac_Getwide(PyObject *v, wide *rv) +{ + if (PyInt_Check(v)) { + rv->hi = 0; + rv->lo = PyInt_AsLong(v); + if( rv->lo & 0x80000000 ) + rv->hi = -1; + return 1; + } + return PyArg_Parse(v, "(kk)", &rv->hi, &rv->lo); +} + + +PyObject * +PyMac_Buildwide(wide *w) +{ + if ( (w->hi == 0 && (w->lo & 0x80000000) == 0) || + (w->hi == -1 && (w->lo & 0x80000000) ) ) + return PyInt_FromLong(w->lo); + return Py_BuildValue("(ll)", w->hi, w->lo); +} + +#ifdef USE_TOOLBOX_OBJECT_GLUE +/* +** Glue together the toolbox objects. +** +** Because toolbox modules interdepend on each other, they use each others +** object types, on MacOSX/MachO this leads to the situation that they +** cannot be dynamically loaded (or they would all have to be lumped into +** a single .so, but this would be bad for extensibility). +** +** This file defines wrappers for all the _New and _Convert functions, +** which are the Py_BuildValue and PyArg_ParseTuple helpers. The wrappers +** check an indirection function pointer, and if it isn't filled in yet +** they import the appropriate module, whose init routine should fill in +** the pointer. +*/ + +#define GLUE_NEW(object, routinename, module) \ +PyObject *(*PyMacGluePtr_##routinename)(object); \ +\ +PyObject *routinename(object cobj) { \ + if (!PyMacGluePtr_##routinename) { \ + if (!PyImport_ImportModule(module)) return NULL; \ + if (!PyMacGluePtr_##routinename) { \ + PyErr_SetString(PyExc_ImportError, "Module did not provide routine: " module ": " #routinename); \ + return NULL; \ + } \ + } \ + return (*PyMacGluePtr_##routinename)(cobj); \ +} + +#define GLUE_CONVERT(object, routinename, module) \ +int (*PyMacGluePtr_##routinename)(PyObject *, object *); \ +\ +int routinename(PyObject *pyobj, object *cobj) { \ + if (!PyMacGluePtr_##routinename) { \ + if (!PyImport_ImportModule(module)) return NULL; \ + if (!PyMacGluePtr_##routinename) { \ + PyErr_SetString(PyExc_ImportError, "Module did not provide routine: " module ": " #routinename); \ + return NULL; \ + } \ + } \ + return (*PyMacGluePtr_##routinename)(pyobj, cobj); \ +} + +GLUE_NEW(FSSpec *, PyMac_BuildFSSpec, "Carbon.File") +GLUE_CONVERT(FSSpec, PyMac_GetFSSpec, "Carbon.File") +GLUE_NEW(FSRef *, PyMac_BuildFSRef, "Carbon.File") +GLUE_CONVERT(FSRef, PyMac_GetFSRef, "Carbon.File") + +GLUE_NEW(AppleEvent *, AEDesc_New, "Carbon.AE") /* XXXX Why by address? */ +GLUE_NEW(AppleEvent *, AEDesc_NewBorrowed, "Carbon.AE") +GLUE_CONVERT(AppleEvent, AEDesc_Convert, "Carbon.AE") + +GLUE_NEW(Component, CmpObj_New, "Carbon.Cm") +GLUE_CONVERT(Component, CmpObj_Convert, "Carbon.Cm") +GLUE_NEW(ComponentInstance, CmpInstObj_New, "Carbon.Cm") +GLUE_CONVERT(ComponentInstance, CmpInstObj_Convert, "Carbon.Cm") + +GLUE_NEW(ControlHandle, CtlObj_New, "Carbon.Ctl") +GLUE_CONVERT(ControlHandle, CtlObj_Convert, "Carbon.Ctl") + +GLUE_NEW(DialogPtr, DlgObj_New, "Carbon.Dlg") +GLUE_CONVERT(DialogPtr, DlgObj_Convert, "Carbon.Dlg") +GLUE_NEW(DialogPtr, DlgObj_WhichDialog, "Carbon.Dlg") + +GLUE_NEW(DragReference, DragObj_New, "Carbon.Drag") +GLUE_CONVERT(DragReference, DragObj_Convert, "Carbon.Drag") + +GLUE_NEW(ListHandle, ListObj_New, "Carbon.List") +GLUE_CONVERT(ListHandle, ListObj_Convert, "Carbon.List") + +GLUE_NEW(MenuHandle, MenuObj_New, "Carbon.Menu") +GLUE_CONVERT(MenuHandle, MenuObj_Convert, "Carbon.Menu") + +GLUE_NEW(GrafPtr, GrafObj_New, "Carbon.Qd") +GLUE_CONVERT(GrafPtr, GrafObj_Convert, "Carbon.Qd") +GLUE_NEW(BitMapPtr, BMObj_New, "Carbon.Qd") +GLUE_CONVERT(BitMapPtr, BMObj_Convert, "Carbon.Qd") +GLUE_NEW(RGBColor *, QdRGB_New, "Carbon.Qd") /* XXXX Why? */ +GLUE_CONVERT(RGBColor, QdRGB_Convert, "Carbon.Qd") + +GLUE_NEW(GWorldPtr, GWorldObj_New, "Carbon.Qdoffs") +GLUE_CONVERT(GWorldPtr, GWorldObj_Convert, "Carbon.Qdoffs") + +GLUE_NEW(Track, TrackObj_New, "Carbon.Qt") +GLUE_CONVERT(Track, TrackObj_Convert, "Carbon.Qt") +GLUE_NEW(Movie, MovieObj_New, "Carbon.Qt") +GLUE_CONVERT(Movie, MovieObj_Convert, "Carbon.Qt") +GLUE_NEW(MovieController, MovieCtlObj_New, "Carbon.Qt") +GLUE_CONVERT(MovieController, MovieCtlObj_Convert, "Carbon.Qt") +GLUE_NEW(TimeBase, TimeBaseObj_New, "Carbon.Qt") +GLUE_CONVERT(TimeBase, TimeBaseObj_Convert, "Carbon.Qt") +GLUE_NEW(UserData, UserDataObj_New, "Carbon.Qt") +GLUE_CONVERT(UserData, UserDataObj_Convert, "Carbon.Qt") +GLUE_NEW(Media, MediaObj_New, "Carbon.Qt") +GLUE_CONVERT(Media, MediaObj_Convert, "Carbon.Qt") + +GLUE_NEW(Handle, ResObj_New, "Carbon.Res") +GLUE_CONVERT(Handle, ResObj_Convert, "Carbon.Res") +GLUE_NEW(Handle, OptResObj_New, "Carbon.Res") +GLUE_CONVERT(Handle, OptResObj_Convert, "Carbon.Res") + +GLUE_NEW(TEHandle, TEObj_New, "Carbon.TE") +GLUE_CONVERT(TEHandle, TEObj_Convert, "Carbon.TE") + +GLUE_NEW(WindowPtr, WinObj_New, "Carbon.Win") +GLUE_CONVERT(WindowPtr, WinObj_Convert, "Carbon.Win") +GLUE_NEW(WindowPtr, WinObj_WhichWindow, "Carbon.Win") + +GLUE_CONVERT(CFTypeRef, CFObj_Convert, "Carbon.CF") +GLUE_NEW(CFTypeRef, CFObj_New, "Carbon.CF") + +GLUE_CONVERT(CFTypeRef, CFTypeRefObj_Convert, "Carbon.CF") +GLUE_NEW(CFTypeRef, CFTypeRefObj_New, "Carbon.CF") + +GLUE_CONVERT(CFStringRef, CFStringRefObj_Convert, "Carbon.CF") +GLUE_NEW(CFStringRef, CFStringRefObj_New, "Carbon.CF") +GLUE_CONVERT(CFMutableStringRef, CFMutableStringRefObj_Convert, "Carbon.CF") +GLUE_NEW(CFMutableStringRef, CFMutableStringRefObj_New, "Carbon.CF") + +GLUE_CONVERT(CFArrayRef, CFArrayRefObj_Convert, "Carbon.CF") +GLUE_NEW(CFArrayRef, CFArrayRefObj_New, "Carbon.CF") +GLUE_CONVERT(CFMutableArrayRef, CFMutableArrayRefObj_Convert, "Carbon.CF") +GLUE_NEW(CFMutableArrayRef, CFMutableArrayRefObj_New, "Carbon.CF") + +GLUE_CONVERT(CFDictionaryRef, CFDictionaryRefObj_Convert, "Carbon.CF") +GLUE_NEW(CFDictionaryRef, CFDictionaryRefObj_New, "Carbon.CF") +GLUE_CONVERT(CFMutableDictionaryRef, CFMutableDictionaryRefObj_Convert, "Carbon.CF") +GLUE_NEW(CFMutableDictionaryRef, CFMutableDictionaryRefObj_New, "Carbon.CF") + +GLUE_CONVERT(CFURLRef, CFURLRefObj_Convert, "Carbon.CF") +GLUE_CONVERT(CFURLRef, OptionalCFURLRefObj_Convert, "Carbon.CF") +GLUE_NEW(CFURLRef, CFURLRefObj_New, "Carbon.CF") + +#endif /* USE_TOOLBOX_OBJECT_GLUE */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/marshal.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/marshal.c new file mode 100644 index 00000000..c478aff7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/marshal.c @@ -0,0 +1,882 @@ + +/* Write Python objects to files and read them back. + This is intended for writing and reading compiled Python code only; + a true persistent storage facility would be much harder, since + it would have to take circular links and sharing into account. */ + +#include "Python.h" +#include "longintrepr.h" +#include "compile.h" +#include "marshal.h" + +/* High water mark to determine when the marshalled object is dangerously deep + * and risks coring the interpreter. When the object stack gets this deep, + * raise an exception instead of continuing. + */ +#define MAX_MARSHAL_STACK_DEPTH 5000 + +#define TYPE_NULL '0' +#define TYPE_NONE 'N' +#define TYPE_FALSE 'F' +#define TYPE_TRUE 'T' +#define TYPE_STOPITER 'S' +#define TYPE_ELLIPSIS '.' +#define TYPE_INT 'i' +#define TYPE_INT64 'I' +#define TYPE_FLOAT 'f' +#define TYPE_COMPLEX 'x' +#define TYPE_LONG 'l' +#define TYPE_STRING 's' +#define TYPE_TUPLE '(' +#define TYPE_LIST '[' +#define TYPE_DICT '{' +#define TYPE_CODE 'c' +#define TYPE_UNICODE 'u' +#define TYPE_UNKNOWN '?' + +typedef struct { + FILE *fp; + int error; + int depth; + /* If fp == NULL, the following are valid: */ + PyObject *str; + char *ptr; + char *end; +} WFILE; + +#define w_byte(c, p) if (((p)->fp)) putc((c), (p)->fp); \ + else if ((p)->ptr != (p)->end) *(p)->ptr++ = (c); \ + else w_more(c, p) + +static void +w_more(int c, WFILE *p) +{ + int size, newsize; + if (p->str == NULL) + return; /* An error already occurred */ + size = PyString_Size(p->str); + newsize = size + 1024; + if (_PyString_Resize(&p->str, newsize) != 0) { + p->ptr = p->end = NULL; + } + else { + p->ptr = PyString_AS_STRING((PyStringObject *)p->str) + size; + p->end = + PyString_AS_STRING((PyStringObject *)p->str) + newsize; + *p->ptr++ = Py_SAFE_DOWNCAST(c, int, char); + } +} + +static void +w_string(char *s, int n, WFILE *p) +{ + if (p->fp != NULL) { + fwrite(s, 1, n, p->fp); + } + else { + while (--n >= 0) { + w_byte(*s, p); + s++; + } + } +} + +static void +w_short(int x, WFILE *p) +{ + w_byte((char)( x & 0xff), p); + w_byte((char)((x>> 8) & 0xff), p); +} + +static void +w_long(long x, WFILE *p) +{ + w_byte((char)( x & 0xff), p); + w_byte((char)((x>> 8) & 0xff), p); + w_byte((char)((x>>16) & 0xff), p); + w_byte((char)((x>>24) & 0xff), p); +} + +#if SIZEOF_LONG > 4 +static void +w_long64(long x, WFILE *p) +{ + w_long(x, p); + w_long(x>>32, p); +} +#endif + +static void +w_object(PyObject *v, WFILE *p) +{ + int i, n; + + p->depth++; + + if (p->depth > MAX_MARSHAL_STACK_DEPTH) { + p->error = 2; + } + else if (v == NULL) { + w_byte(TYPE_NULL, p); + } + else if (v == Py_None) { + w_byte(TYPE_NONE, p); + } + else if (v == PyExc_StopIteration) { + w_byte(TYPE_STOPITER, p); + } + else if (v == Py_Ellipsis) { + w_byte(TYPE_ELLIPSIS, p); + } + else if (v == Py_False) { + w_byte(TYPE_FALSE, p); + } + else if (v == Py_True) { + w_byte(TYPE_TRUE, p); + } + else if (PyInt_Check(v)) { + long x = PyInt_AS_LONG((PyIntObject *)v); +#if SIZEOF_LONG > 4 + long y = Py_ARITHMETIC_RIGHT_SHIFT(long, x, 31); + if (y && y != -1) { + w_byte(TYPE_INT64, p); + w_long64(x, p); + } + else +#endif + { + w_byte(TYPE_INT, p); + w_long(x, p); + } + } + else if (PyLong_Check(v)) { + PyLongObject *ob = (PyLongObject *)v; + w_byte(TYPE_LONG, p); + n = ob->ob_size; + w_long((long)n, p); + if (n < 0) + n = -n; + for (i = 0; i < n; i++) + w_short(ob->ob_digit[i], p); + } + else if (PyFloat_Check(v)) { + char buf[256]; /* Plenty to format any double */ + PyFloat_AsReprString(buf, (PyFloatObject *)v); + n = strlen(buf); + w_byte(TYPE_FLOAT, p); + w_byte(n, p); + w_string(buf, n, p); + } +#ifndef WITHOUT_COMPLEX + else if (PyComplex_Check(v)) { + char buf[256]; /* Plenty to format any double */ + PyFloatObject *temp; + w_byte(TYPE_COMPLEX, p); + temp = (PyFloatObject*)PyFloat_FromDouble( + PyComplex_RealAsDouble(v)); + PyFloat_AsReprString(buf, temp); + Py_DECREF(temp); + n = strlen(buf); + w_byte(n, p); + w_string(buf, n, p); + temp = (PyFloatObject*)PyFloat_FromDouble( + PyComplex_ImagAsDouble(v)); + PyFloat_AsReprString(buf, temp); + Py_DECREF(temp); + n = strlen(buf); + w_byte(n, p); + w_string(buf, n, p); + } +#endif + else if (PyString_Check(v)) { + w_byte(TYPE_STRING, p); + n = PyString_GET_SIZE(v); + w_long((long)n, p); + w_string(PyString_AS_STRING(v), n, p); + } +#ifdef Py_USING_UNICODE + else if (PyUnicode_Check(v)) { + PyObject *utf8; + utf8 = PyUnicode_AsUTF8String(v); + if (utf8 == NULL) { + p->depth--; + p->error = 1; + return; + } + w_byte(TYPE_UNICODE, p); + n = PyString_GET_SIZE(utf8); + w_long((long)n, p); + w_string(PyString_AS_STRING(utf8), n, p); + Py_DECREF(utf8); + } +#endif + else if (PyTuple_Check(v)) { + w_byte(TYPE_TUPLE, p); + n = PyTuple_Size(v); + w_long((long)n, p); + for (i = 0; i < n; i++) { + w_object(PyTuple_GET_ITEM(v, i), p); + } + } + else if (PyList_Check(v)) { + w_byte(TYPE_LIST, p); + n = PyList_GET_SIZE(v); + w_long((long)n, p); + for (i = 0; i < n; i++) { + w_object(PyList_GET_ITEM(v, i), p); + } + } + else if (PyDict_Check(v)) { + int pos; + PyObject *key, *value; + w_byte(TYPE_DICT, p); + /* This one is NULL object terminated! */ + pos = 0; + while (PyDict_Next(v, &pos, &key, &value)) { + w_object(key, p); + w_object(value, p); + } + w_object((PyObject *)NULL, p); + } + else if (PyCode_Check(v)) { + PyCodeObject *co = (PyCodeObject *)v; + w_byte(TYPE_CODE, p); + w_long(co->co_argcount, p); + w_long(co->co_nlocals, p); + w_long(co->co_stacksize, p); + w_long(co->co_flags, p); + w_object(co->co_code, p); + w_object(co->co_consts, p); + w_object(co->co_names, p); + w_object(co->co_varnames, p); + w_object(co->co_freevars, p); + w_object(co->co_cellvars, p); + w_object(co->co_filename, p); + w_object(co->co_name, p); + w_long(co->co_firstlineno, p); + w_object(co->co_lnotab, p); + } + else if (PyObject_CheckReadBuffer(v)) { + /* Write unknown buffer-style objects as a string */ + char *s; + PyBufferProcs *pb = v->ob_type->tp_as_buffer; + w_byte(TYPE_STRING, p); + n = (*pb->bf_getreadbuffer)(v, 0, (void **)&s); + w_long((long)n, p); + w_string(s, n, p); + } + else { + w_byte(TYPE_UNKNOWN, p); + p->error = 1; + } + + p->depth--; +} + +void +PyMarshal_WriteLongToFile(long x, FILE *fp) +{ + WFILE wf; + wf.fp = fp; + wf.error = 0; + wf.depth = 0; + w_long(x, &wf); +} + +void +PyMarshal_WriteObjectToFile(PyObject *x, FILE *fp) +{ + WFILE wf; + wf.fp = fp; + wf.error = 0; + wf.depth = 0; + w_object(x, &wf); +} + +typedef WFILE RFILE; /* Same struct with different invariants */ + +#define rs_byte(p) (((p)->ptr != (p)->end) ? (unsigned char)*(p)->ptr++ : EOF) + +#define r_byte(p) ((p)->fp ? getc((p)->fp) : rs_byte(p)) + +static int +r_string(char *s, int n, RFILE *p) +{ + if (p->fp != NULL) + return fread(s, 1, n, p->fp); + if (p->end - p->ptr < n) + n = p->end - p->ptr; + memcpy(s, p->ptr, n); + p->ptr += n; + return n; +} + +static int +r_short(RFILE *p) +{ + register short x; + x = r_byte(p); + x |= r_byte(p) << 8; + /* Sign-extension, in case short greater than 16 bits */ + x |= -(x & 0x8000); + return x; +} + +static long +r_long(RFILE *p) +{ + register long x; + register FILE *fp = p->fp; + if (fp) { + x = getc(fp); + x |= (long)getc(fp) << 8; + x |= (long)getc(fp) << 16; + x |= (long)getc(fp) << 24; + } + else { + x = rs_byte(p); + x |= (long)rs_byte(p) << 8; + x |= (long)rs_byte(p) << 16; + x |= (long)rs_byte(p) << 24; + } +#if SIZEOF_LONG > 4 + /* Sign extension for 64-bit machines */ + x |= -(x & 0x80000000L); +#endif + return x; +} + +/* r_long64 deals with the TYPE_INT64 code. On a machine with + sizeof(long) > 4, it returns a Python int object, else a Python long + object. Note that w_long64 writes out TYPE_INT if 32 bits is enough, + so there's no inefficiency here in returning a PyLong on 32-bit boxes + for everything written via TYPE_INT64 (i.e., if an int is written via + TYPE_INT64, it *needs* more than 32 bits). +*/ +static PyObject * +r_long64(RFILE *p) +{ + long lo4 = r_long(p); + long hi4 = r_long(p); +#if SIZEOF_LONG > 4 + long x = (hi4 << 32) | (lo4 & 0xFFFFFFFFL); + return PyInt_FromLong(x); +#else + unsigned char buf[8]; + int one = 1; + int is_little_endian = (int)*(char*)&one; + if (is_little_endian) { + memcpy(buf, &lo4, 4); + memcpy(buf+4, &hi4, 4); + } + else { + memcpy(buf, &hi4, 4); + memcpy(buf+4, &lo4, 4); + } + return _PyLong_FromByteArray(buf, 8, is_little_endian, 1); +#endif +} + +static PyObject * +r_object(RFILE *p) +{ + PyObject *v, *v2; + long i, n; + int type = r_byte(p); + + switch (type) { + + case EOF: + PyErr_SetString(PyExc_EOFError, + "EOF read where object expected"); + return NULL; + + case TYPE_NULL: + return NULL; + + case TYPE_NONE: + Py_INCREF(Py_None); + return Py_None; + + case TYPE_STOPITER: + Py_INCREF(PyExc_StopIteration); + return PyExc_StopIteration; + + case TYPE_ELLIPSIS: + Py_INCREF(Py_Ellipsis); + return Py_Ellipsis; + + case TYPE_FALSE: + Py_INCREF(Py_False); + return Py_False; + + case TYPE_TRUE: + Py_INCREF(Py_True); + return Py_True; + + case TYPE_INT: + return PyInt_FromLong(r_long(p)); + + case TYPE_INT64: + return r_long64(p); + + case TYPE_LONG: + { + int size; + PyLongObject *ob; + n = r_long(p); + size = n<0 ? -n : n; + ob = _PyLong_New(size); + if (ob == NULL) + return NULL; + ob->ob_size = n; + for (i = 0; i < size; i++) + ob->ob_digit[i] = r_short(p); + return (PyObject *)ob; + } + + case TYPE_FLOAT: + { + char buf[256]; + double dx; + n = r_byte(p); + if (r_string(buf, (int)n, p) != n) { + PyErr_SetString(PyExc_EOFError, + "EOF read where object expected"); + return NULL; + } + buf[n] = '\0'; + PyFPE_START_PROTECT("atof", return 0) + dx = atof(buf); + PyFPE_END_PROTECT(dx) + return PyFloat_FromDouble(dx); + } + +#ifndef WITHOUT_COMPLEX + case TYPE_COMPLEX: + { + char buf[256]; + Py_complex c; + n = r_byte(p); + if (r_string(buf, (int)n, p) != n) { + PyErr_SetString(PyExc_EOFError, + "EOF read where object expected"); + return NULL; + } + buf[n] = '\0'; + PyFPE_START_PROTECT("atof", return 0) + c.real = atof(buf); + PyFPE_END_PROTECT(c) + n = r_byte(p); + if (r_string(buf, (int)n, p) != n) { + PyErr_SetString(PyExc_EOFError, + "EOF read where object expected"); + return NULL; + } + buf[n] = '\0'; + PyFPE_START_PROTECT("atof", return 0) + c.imag = atof(buf); + PyFPE_END_PROTECT(c) + return PyComplex_FromCComplex(c); + } +#endif + + case TYPE_STRING: + n = r_long(p); + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "bad marshal data"); + return NULL; + } + v = PyString_FromStringAndSize((char *)NULL, n); + if (v != NULL) { + if (r_string(PyString_AS_STRING(v), (int)n, p) != n) { + Py_DECREF(v); + v = NULL; + PyErr_SetString(PyExc_EOFError, + "EOF read where object expected"); + } + } + return v; + +#ifdef Py_USING_UNICODE + case TYPE_UNICODE: + { + char *buffer; + + n = r_long(p); + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "bad marshal data"); + return NULL; + } + buffer = PyMem_NEW(char, n); + if (buffer == NULL) + return PyErr_NoMemory(); + if (r_string(buffer, (int)n, p) != n) { + PyMem_DEL(buffer); + PyErr_SetString(PyExc_EOFError, + "EOF read where object expected"); + return NULL; + } + v = PyUnicode_DecodeUTF8(buffer, n, NULL); + PyMem_DEL(buffer); + return v; + } +#endif + + case TYPE_TUPLE: + n = r_long(p); + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "bad marshal data"); + return NULL; + } + v = PyTuple_New((int)n); + if (v == NULL) + return v; + for (i = 0; i < n; i++) { + v2 = r_object(p); + if ( v2 == NULL ) { + Py_DECREF(v); + v = NULL; + break; + } + PyTuple_SET_ITEM(v, (int)i, v2); + } + return v; + + case TYPE_LIST: + n = r_long(p); + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "bad marshal data"); + return NULL; + } + v = PyList_New((int)n); + if (v == NULL) + return v; + for (i = 0; i < n; i++) { + v2 = r_object(p); + if ( v2 == NULL ) { + Py_DECREF(v); + v = NULL; + break; + } + PyList_SetItem(v, (int)i, v2); + } + return v; + + case TYPE_DICT: + v = PyDict_New(); + if (v == NULL) + return NULL; + for (;;) { + PyObject *key, *val; + key = r_object(p); + if (key == NULL) + break; /* XXX Assume TYPE_NULL, not an error */ + val = r_object(p); + if (val != NULL) + PyDict_SetItem(v, key, val); + Py_DECREF(key); + Py_XDECREF(val); + } + return v; + + case TYPE_CODE: + if (PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, + "cannot unmarshal code objects in " + "restricted execution mode"); + return NULL; + } + else { + int argcount = r_long(p); + int nlocals = r_long(p); + int stacksize = r_long(p); + int flags = r_long(p); + PyObject *code = NULL; + PyObject *consts = NULL; + PyObject *names = NULL; + PyObject *varnames = NULL; + PyObject *freevars = NULL; + PyObject *cellvars = NULL; + PyObject *filename = NULL; + PyObject *name = NULL; + int firstlineno = 0; + PyObject *lnotab = NULL; + + code = r_object(p); + if (code) consts = r_object(p); + if (consts) names = r_object(p); + if (names) varnames = r_object(p); + if (varnames) freevars = r_object(p); + if (freevars) cellvars = r_object(p); + if (cellvars) filename = r_object(p); + if (filename) name = r_object(p); + if (name) { + firstlineno = r_long(p); + lnotab = r_object(p); + } + + if (!PyErr_Occurred()) { + v = (PyObject *) PyCode_New( + argcount, nlocals, stacksize, flags, + code, consts, names, varnames, + freevars, cellvars, filename, name, + firstlineno, lnotab); + } + else + v = NULL; + Py_XDECREF(code); + Py_XDECREF(consts); + Py_XDECREF(names); + Py_XDECREF(varnames); + Py_XDECREF(freevars); + Py_XDECREF(cellvars); + Py_XDECREF(filename); + Py_XDECREF(name); + Py_XDECREF(lnotab); + + } + return v; + + default: + /* Bogus data got written, which isn't ideal. + This will let you keep working and recover. */ + PyErr_SetString(PyExc_ValueError, "bad marshal data"); + return NULL; + + } +} + +int +PyMarshal_ReadShortFromFile(FILE *fp) +{ + RFILE rf; + rf.fp = fp; + return r_short(&rf); +} + +long +PyMarshal_ReadLongFromFile(FILE *fp) +{ + RFILE rf; + rf.fp = fp; + return r_long(&rf); +} + +#ifdef HAVE_FSTAT +/* Return size of file in bytes; < 0 if unknown. */ +static off_t +getfilesize(FILE *fp) +{ + struct stat st; + if (fstat(fileno(fp), &st) != 0) + return -1; + else + return st.st_size; +} +#endif + +/* If we can get the size of the file up-front, and it's reasonably small, + * read it in one gulp and delegate to ...FromString() instead. Much quicker + * than reading a byte at a time from file; speeds .pyc imports. + * CAUTION: since this may read the entire remainder of the file, don't + * call it unless you know you're done with the file. + */ +PyObject * +PyMarshal_ReadLastObjectFromFile(FILE *fp) +{ +/* 75% of 2.1's .pyc files can exploit SMALL_FILE_LIMIT. + * REASONABLE_FILE_LIMIT is by defn something big enough for Tkinter.pyc. + */ +#define SMALL_FILE_LIMIT (1L << 14) +#define REASONABLE_FILE_LIMIT (1L << 18) +#ifdef HAVE_FSTAT + off_t filesize; +#endif + if (PyErr_Occurred()) { + fprintf(stderr, "XXX rd_object called with exception set\n"); + return NULL; + } +#ifdef HAVE_FSTAT + filesize = getfilesize(fp); + if (filesize > 0) { + char buf[SMALL_FILE_LIMIT]; + char* pBuf = NULL; + if (filesize <= SMALL_FILE_LIMIT) + pBuf = buf; + else if (filesize <= REASONABLE_FILE_LIMIT) + pBuf = (char *)PyMem_MALLOC(filesize); + if (pBuf != NULL) { + PyObject* v; + size_t n = fread(pBuf, 1, filesize, fp); + v = PyMarshal_ReadObjectFromString(pBuf, n); + if (pBuf != buf) + PyMem_FREE(pBuf); + return v; + } + + } +#endif + /* We don't have fstat, or we do but the file is larger than + * REASONABLE_FILE_LIMIT or malloc failed -- read a byte at a time. + */ + return PyMarshal_ReadObjectFromFile(fp); + +#undef SMALL_FILE_LIMIT +#undef REASONABLE_FILE_LIMIT +} + +PyObject * +PyMarshal_ReadObjectFromFile(FILE *fp) +{ + RFILE rf; + if (PyErr_Occurred()) { + fprintf(stderr, "XXX rd_object called with exception set\n"); + return NULL; + } + rf.fp = fp; + return r_object(&rf); +} + +PyObject * +PyMarshal_ReadObjectFromString(char *str, int len) +{ + RFILE rf; + if (PyErr_Occurred()) { + fprintf(stderr, "XXX rds_object called with exception set\n"); + return NULL; + } + rf.fp = NULL; + rf.str = NULL; + rf.ptr = str; + rf.end = str + len; + return r_object(&rf); +} + +PyObject * +PyMarshal_WriteObjectToString(PyObject *x) /* wrs_object() */ +{ + WFILE wf; + wf.fp = NULL; + wf.str = PyString_FromStringAndSize((char *)NULL, 50); + if (wf.str == NULL) + return NULL; + wf.ptr = PyString_AS_STRING((PyStringObject *)wf.str); + wf.end = wf.ptr + PyString_Size(wf.str); + wf.error = 0; + wf.depth = 0; + w_object(x, &wf); + if (wf.str != NULL) + _PyString_Resize(&wf.str, + (int) (wf.ptr - + PyString_AS_STRING((PyStringObject *)wf.str))); + if (wf.error) { + Py_XDECREF(wf.str); + PyErr_SetString(PyExc_ValueError, + (wf.error==1)?"unmarshallable object" + :"object too deeply nested to marshal"); + return NULL; + } + return wf.str; +} + +/* And an interface for Python programs... */ + +static PyObject * +marshal_dump(PyObject *self, PyObject *args) +{ + WFILE wf; + PyObject *x; + PyObject *f; + if (!PyArg_ParseTuple(args, "OO:dump", &x, &f)) + return NULL; + if (!PyFile_Check(f)) { + PyErr_SetString(PyExc_TypeError, + "marshal.dump() 2nd arg must be file"); + return NULL; + } + wf.fp = PyFile_AsFile(f); + wf.str = NULL; + wf.ptr = wf.end = NULL; + wf.error = 0; + wf.depth = 0; + w_object(x, &wf); + if (wf.error) { + PyErr_SetString(PyExc_ValueError, + (wf.error==1)?"unmarshallable object" + :"object too deeply nested to marshal"); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +marshal_load(PyObject *self, PyObject *args) +{ + RFILE rf; + PyObject *f; + PyObject *v; + if (!PyArg_ParseTuple(args, "O:load", &f)) + return NULL; + if (!PyFile_Check(f)) { + PyErr_SetString(PyExc_TypeError, + "marshal.load() arg must be file"); + return NULL; + } + rf.fp = PyFile_AsFile(f); + rf.str = NULL; + rf.ptr = rf.end = NULL; + PyErr_Clear(); + v = r_object(&rf); + if (PyErr_Occurred()) { + Py_XDECREF(v); + v = NULL; + } + return v; +} + +static PyObject * +marshal_dumps(PyObject *self, PyObject *args) +{ + PyObject *x; + if (!PyArg_ParseTuple(args, "O:dumps", &x)) + return NULL; + return PyMarshal_WriteObjectToString(x); +} + +static PyObject * +marshal_loads(PyObject *self, PyObject *args) +{ + RFILE rf; + PyObject *v; + char *s; + int n; + if (!PyArg_ParseTuple(args, "s#:loads", &s, &n)) + return NULL; + rf.fp = NULL; + rf.str = args; + rf.ptr = s; + rf.end = s + n; + PyErr_Clear(); + v = r_object(&rf); + if (PyErr_Occurred()) { + Py_XDECREF(v); + v = NULL; + } + return v; +} + +static PyMethodDef marshal_methods[] = { + {"dump", marshal_dump, METH_VARARGS}, + {"load", marshal_load, METH_VARARGS}, + {"dumps", marshal_dumps, METH_VARARGS}, + {"loads", marshal_loads, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +void +PyMarshal_Init(void) +{ + (void) Py_InitModule("marshal", marshal_methods); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/memmove.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/memmove.c new file mode 100644 index 00000000..fc4b2afa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/memmove.c @@ -0,0 +1,25 @@ + +/* A perhaps slow but I hope correct implementation of memmove */ + +extern char *memcpy(char *, char *, int); + +char * +memmove(char *dst, char *src, int n) +{ + char *realdst = dst; + if (n <= 0) + return dst; + if (src >= dst+n || dst >= src+n) + return memcpy(dst, src, n); + if (src > dst) { + while (--n >= 0) + *dst++ = *src++; + } + else if (src < dst) { + src += n; + dst += n; + while (--n >= 0) + *--dst = *--src; + } + return realdst; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/modsupport.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/modsupport.c new file mode 100644 index 00000000..a3aee329 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/modsupport.c @@ -0,0 +1,551 @@ + +/* Module support implementation */ + +#include "Python.h" + +#ifdef MPW /* MPW pushes 'extended' for float and double types with varargs */ +typedef extended va_double; +#else +typedef double va_double; +#endif + +/* Package context -- the full module name for package imports */ +char *_Py_PackageContext = NULL; + +/* Py_InitModule4() parameters: + - name is the module name + - methods is the list of top-level functions + - doc is the documentation string + - passthrough is passed as self to functions defined in the module + - api_version is the value of PYTHON_API_VERSION at the time the + module was compiled + + Return value is a borrowed reference to the module object; or NULL + if an error occurred (in Python 1.4 and before, errors were fatal). + Errors may still leak memory. +*/ + +static char api_version_warning[] = +"Python C API version mismatch for module %.100s:\ + This Python has API version %d, module %.100s has version %d."; + +PyObject * +Py_InitModule4(char *name, PyMethodDef *methods, char *doc, + PyObject *passthrough, int module_api_version) +{ + PyObject *m, *d, *v, *n; + PyMethodDef *ml; + if (!Py_IsInitialized()) + Py_FatalError("Interpreter not initialized (version mismatch?)"); + if (module_api_version != PYTHON_API_VERSION) { + char message[512]; + PyOS_snprintf(message, sizeof(message), + api_version_warning, name, + PYTHON_API_VERSION, name, + module_api_version); + if (PyErr_Warn(PyExc_RuntimeWarning, message)) + return NULL; + } + /* Make sure name is fully qualified. + + This is a bit of a hack: when the shared library is loaded, + the module name is "package.module", but the module calls + Py_InitModule*() with just "module" for the name. The shared + library loader squirrels away the true name of the module in + _Py_PackageContext, and Py_InitModule*() will substitute this + (if the name actually matches). + */ + if (_Py_PackageContext != NULL) { + char *p = strrchr(_Py_PackageContext, '.'); + if (p != NULL && strcmp(name, p+1) == 0) { + name = _Py_PackageContext; + _Py_PackageContext = NULL; + } + } + if ((m = PyImport_AddModule(name)) == NULL) + return NULL; + d = PyModule_GetDict(m); + if (methods != NULL) { + n = PyString_FromString(name); + if (n == NULL) + return NULL; + for (ml = methods; ml->ml_name != NULL; ml++) { + if ((ml->ml_flags & METH_CLASS) || + (ml->ml_flags & METH_STATIC)) { + PyErr_SetString(PyExc_ValueError, + "module functions cannot set" + " METH_CLASS or METH_STATIC"); + return NULL; + } + v = PyCFunction_NewEx(ml, passthrough, n); + if (v == NULL) + return NULL; + if (PyDict_SetItemString(d, ml->ml_name, v) != 0) { + Py_DECREF(v); + return NULL; + } + Py_DECREF(v); + } + } + if (doc != NULL) { + v = PyString_FromString(doc); + if (v == NULL || PyDict_SetItemString(d, "__doc__", v) != 0) { + Py_XDECREF(v); + return NULL; + } + Py_DECREF(v); + } + return m; +} + + +/* Helper for mkvalue() to scan the length of a format */ + +static int +countformat(char *format, int endchar) +{ + int count = 0; + int level = 0; + while (level > 0 || *format != endchar) { + switch (*format) { + case '\0': + /* Premature end */ + PyErr_SetString(PyExc_SystemError, + "unmatched paren in format"); + return -1; + case '(': + case '[': + case '{': + if (level == 0) + count++; + level++; + break; + case ')': + case ']': + case '}': + level--; + break; + case '#': + case '&': + case ',': + case ':': + case ' ': + case '\t': + break; + default: + if (level == 0) + count++; + } + format++; + } + return count; +} + + +/* Generic function to create a value -- the inverse of getargs() */ +/* After an original idea and first implementation by Steven Miale */ + +static PyObject *do_mktuple(char**, va_list *, int, int); +static PyObject *do_mklist(char**, va_list *, int, int); +static PyObject *do_mkdict(char**, va_list *, int, int); +static PyObject *do_mkvalue(char**, va_list *); + + +static PyObject * +do_mkdict(char **p_format, va_list *p_va, int endchar, int n) +{ + PyObject *d; + int i; + if (n < 0) + return NULL; + if ((d = PyDict_New()) == NULL) + return NULL; + for (i = 0; i < n; i+= 2) { + PyObject *k, *v; + int err; + k = do_mkvalue(p_format, p_va); + if (k == NULL) { + Py_DECREF(d); + return NULL; + } + v = do_mkvalue(p_format, p_va); + if (v == NULL) { + Py_DECREF(k); + Py_DECREF(d); + return NULL; + } + err = PyDict_SetItem(d, k, v); + Py_DECREF(k); + Py_DECREF(v); + if (err < 0) { + Py_DECREF(d); + return NULL; + } + } + if (d != NULL && **p_format != endchar) { + Py_DECREF(d); + d = NULL; + PyErr_SetString(PyExc_SystemError, + "Unmatched paren in format"); + } + else if (endchar) + ++*p_format; + return d; +} + +static PyObject * +do_mklist(char **p_format, va_list *p_va, int endchar, int n) +{ + PyObject *v; + int i; + if (n < 0) + return NULL; + if ((v = PyList_New(n)) == NULL) + return NULL; + for (i = 0; i < n; i++) { + PyObject *w = do_mkvalue(p_format, p_va); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(v, i, w); + } + if (v != NULL && **p_format != endchar) { + Py_DECREF(v); + v = NULL; + PyErr_SetString(PyExc_SystemError, + "Unmatched paren in format"); + } + else if (endchar) + ++*p_format; + return v; +} + +#ifdef Py_USING_UNICODE +static int +_ustrlen(Py_UNICODE *u) +{ + int i = 0; + Py_UNICODE *v = u; + while (*v != 0) { i++; v++; } + return i; +} +#endif + +static PyObject * +do_mktuple(char **p_format, va_list *p_va, int endchar, int n) +{ + PyObject *v; + int i; + if (n < 0) + return NULL; + if ((v = PyTuple_New(n)) == NULL) + return NULL; + for (i = 0; i < n; i++) { + PyObject *w = do_mkvalue(p_format, p_va); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyTuple_SetItem(v, i, w); + } + if (v != NULL && **p_format != endchar) { + Py_DECREF(v); + v = NULL; + PyErr_SetString(PyExc_SystemError, + "Unmatched paren in format"); + } + else if (endchar) + ++*p_format; + return v; +} + +static PyObject * +do_mkvalue(char **p_format, va_list *p_va) +{ + for (;;) { + switch (*(*p_format)++) { + case '(': + return do_mktuple(p_format, p_va, ')', + countformat(*p_format, ')')); + + case '[': + return do_mklist(p_format, p_va, ']', + countformat(*p_format, ']')); + + case '{': + return do_mkdict(p_format, p_va, '}', + countformat(*p_format, '}')); + + case 'b': + case 'B': + case 'h': + case 'i': + return PyInt_FromLong((long)va_arg(*p_va, int)); + + case 'H': + return PyInt_FromLong((long)va_arg(*p_va, unsigned int)); + + case 'l': + return PyInt_FromLong((long)va_arg(*p_va, long)); + + case 'k': + return PyInt_FromLong((long)va_arg(*p_va, unsigned long)); + +#ifdef HAVE_LONG_LONG + case 'L': + return PyLong_FromLongLong((PY_LONG_LONG)va_arg(*p_va, PY_LONG_LONG)); + + case 'K': + return PyLong_FromLongLong((PY_LONG_LONG)va_arg(*p_va, unsigned PY_LONG_LONG)); +#endif +#ifdef Py_USING_UNICODE + case 'u': + { + PyObject *v; + Py_UNICODE *u = va_arg(*p_va, Py_UNICODE *); + int n; + if (**p_format == '#') { + ++*p_format; + n = va_arg(*p_va, int); + } + else + n = -1; + if (u == NULL) { + v = Py_None; + Py_INCREF(v); + } + else { + if (n < 0) + n = _ustrlen(u); + v = PyUnicode_FromUnicode(u, n); + } + return v; + } +#endif + case 'f': + case 'd': + return PyFloat_FromDouble( + (double)va_arg(*p_va, va_double)); + +#ifndef WITHOUT_COMPLEX + case 'D': + return PyComplex_FromCComplex( + *((Py_complex *)va_arg(*p_va, Py_complex *))); +#endif /* WITHOUT_COMPLEX */ + + case 'c': + { + char p[1]; + p[0] = va_arg(*p_va, int); + return PyString_FromStringAndSize(p, 1); + } + + case 's': + case 'z': + { + PyObject *v; + char *str = va_arg(*p_va, char *); + int n; + if (**p_format == '#') { + ++*p_format; + n = va_arg(*p_va, int); + } + else + n = -1; + if (str == NULL) { + v = Py_None; + Py_INCREF(v); + } + else { + if (n < 0) { + size_t m = strlen(str); + if (m > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "string too long for Python string"); + return NULL; + } + n = (int)m; + } + v = PyString_FromStringAndSize(str, n); + } + return v; + } + + case 'N': + case 'S': + case 'O': + if (**p_format == '&') { + typedef PyObject *(*converter)(void *); + converter func = va_arg(*p_va, converter); + void *arg = va_arg(*p_va, void *); + ++*p_format; + return (*func)(arg); + } + else { + PyObject *v; + v = va_arg(*p_va, PyObject *); + if (v != NULL) { + if (*(*p_format - 1) != 'N') + Py_INCREF(v); + } + else if (!PyErr_Occurred()) + /* If a NULL was passed + * because a call that should + * have constructed a value + * failed, that's OK, and we + * pass the error on; but if + * no error occurred it's not + * clear that the caller knew + * what she was doing. */ + PyErr_SetString(PyExc_SystemError, + "NULL object passed to Py_BuildValue"); + return v; + } + + case ':': + case ',': + case ' ': + case '\t': + break; + + default: + PyErr_SetString(PyExc_SystemError, + "bad format char passed to Py_BuildValue"); + return NULL; + + } + } +} + + +PyObject * +Py_BuildValue(char *format, ...) +{ + va_list va; + PyObject* retval; + va_start(va, format); + retval = Py_VaBuildValue(format, va); + va_end(va); + return retval; +} + +PyObject * +Py_VaBuildValue(char *format, va_list va) +{ + char *f = format; + int n = countformat(f, '\0'); + va_list lva; + +#ifdef VA_LIST_IS_ARRAY + memcpy(lva, va, sizeof(va_list)); +#else +#ifdef __va_copy + __va_copy(lva, va); +#else + lva = va; +#endif +#endif + + if (n < 0) + return NULL; + if (n == 0) { + Py_INCREF(Py_None); + return Py_None; + } + if (n == 1) + return do_mkvalue(&f, &lva); + return do_mktuple(&f, &lva, '\0', n); +} + + +PyObject * +PyEval_CallFunction(PyObject *obj, char *format, ...) +{ + va_list vargs; + PyObject *args; + PyObject *res; + + va_start(vargs, format); + + args = Py_VaBuildValue(format, vargs); + va_end(vargs); + + if (args == NULL) + return NULL; + + res = PyEval_CallObject(obj, args); + Py_DECREF(args); + + return res; +} + + +PyObject * +PyEval_CallMethod(PyObject *obj, char *methodname, char *format, ...) +{ + va_list vargs; + PyObject *meth; + PyObject *args; + PyObject *res; + + meth = PyObject_GetAttrString(obj, methodname); + if (meth == NULL) + return NULL; + + va_start(vargs, format); + + args = Py_VaBuildValue(format, vargs); + va_end(vargs); + + if (args == NULL) { + Py_DECREF(meth); + return NULL; + } + + res = PyEval_CallObject(meth, args); + Py_DECREF(meth); + Py_DECREF(args); + + return res; +} + +int +PyModule_AddObject(PyObject *m, char *name, PyObject *o) +{ + PyObject *dict; + if (!PyModule_Check(m)) { + PyErr_SetString(PyExc_TypeError, + "PyModule_AddObject() needs module as first arg"); + return -1; + } + if (!o) { + PyErr_SetString(PyExc_TypeError, + "PyModule_AddObject() needs non-NULL value"); + return -1; + } + + dict = PyModule_GetDict(m); + if (dict == NULL) { + /* Internal error -- modules must have a dict! */ + PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__", + PyModule_GetName(m)); + return -1; + } + if (PyDict_SetItemString(dict, name, o)) + return -1; + Py_DECREF(o); + return 0; +} + +int +PyModule_AddIntConstant(PyObject *m, char *name, long value) +{ + return PyModule_AddObject(m, name, PyInt_FromLong(value)); +} + +int +PyModule_AddStringConstant(PyObject *m, char *name, char *value) +{ + return PyModule_AddObject(m, name, PyString_FromString(value)); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mysnprintf.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mysnprintf.c new file mode 100644 index 00000000..4e6da179 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mysnprintf.c @@ -0,0 +1,93 @@ +#include "Python.h" +#include + +/* snprintf() wrappers. If the platform has vsnprintf, we use it, else we + emulate it in a half-hearted way. Even if the platform has it, we wrap + it because platforms differ in what vsnprintf does in case the buffer + is too small: C99 behavior is to return the number of characters that + would have been written had the buffer not been too small, and to set + the last byte of the buffer to \0. At least MS _vsnprintf returns a + negative value instead, and fills the entire buffer with non-\0 data. + + The wrappers ensure that str[size-1] is always \0 upon return. + + PyOS_snprintf and PyOS_vsnprintf never write more than size bytes + (including the trailing '\0') into str. + + If the platform doesn't have vsnprintf, and the buffer size needed to + avoid truncation exceeds size by more than 512, Python aborts with a + Py_FatalError. + + Return value (rv): + + When 0 <= rv < size, the output conversion was unexceptional, and + rv characters were written to str (excluding a trailing \0 byte at + str[rv]). + + When rv >= size, output conversion was truncated, and a buffer of + size rv+1 would have been needed to avoid truncation. str[size-1] + is \0 in this case. + + When rv < 0, "something bad happened". str[size-1] is \0 in this + case too, but the rest of str is unreliable. It could be that + an error in format codes was detected by libc, or on platforms + with a non-C99 vsnprintf simply that the buffer wasn't big enough + to avoid truncation, or on platforms without any vsnprintf that + PyMem_Malloc couldn't obtain space for a temp buffer. + + CAUTION: Unlike C99, str != NULL and size > 0 are required. +*/ + +int +PyOS_snprintf(char *str, size_t size, const char *format, ...) +{ + int rc; + va_list va; + + va_start(va, format); + rc = PyOS_vsnprintf(str, size, format, va); + va_end(va); + return rc; +} + +int +PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) +{ + int len; /* # bytes written, excluding \0 */ +#ifndef HAVE_SNPRINTF + char *buffer; +#endif + assert(str != NULL); + assert(size > 0); + assert(format != NULL); + +#ifdef HAVE_SNPRINTF + len = vsnprintf(str, size, format, va); +#else + /* Emulate it. */ + buffer = PyMem_MALLOC(size + 512); + if (buffer == NULL) { + len = -666; + goto Done; + } + + len = vsprintf(buffer, format, va); + if (len < 0) + /* ignore the error */; + + else if ((size_t)len >= size + 512) + Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf"); + + else { + const size_t to_copy = (size_t)len < size ? + (size_t)len : size - 1; + assert(to_copy < size); + memcpy(str, buffer, to_copy); + str[to_copy] = '\0'; + } + PyMem_FREE(buffer); +Done: +#endif + str[size-1] = '\0'; + return len; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mystrtoul.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mystrtoul.c new file mode 100644 index 00000000..bc624928 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/mystrtoul.c @@ -0,0 +1,153 @@ + +#include "Python.h" + +#if defined(__sgi) && defined(WITH_THREAD) && !defined(_SGI_MP_SOURCE) +#define _SGI_MP_SOURCE +#endif + +/* Convert a possibly signed character to a nonnegative int */ +/* XXX This assumes characters are 8 bits wide */ +#ifdef __CHAR_UNSIGNED__ +#define Py_CHARMASK(c) (c) +#else +#define Py_CHARMASK(c) ((c) & 0xff) +#endif + +/* strtol and strtoul, renamed to avoid conflicts */ + +/* +** strtoul +** This is a general purpose routine for converting +** an ascii string to an integer in an arbitrary base. +** Leading white space is ignored. If 'base' is zero +** it looks for a leading 0, 0x or 0X to tell which +** base. If these are absent it defaults to 10. +** Base must be 0 or between 2 and 36 (inclusive). +** If 'ptr' is non-NULL it will contain a pointer to +** the end of the scan. +** Errors due to bad pointers will probably result in +** exceptions - we don't check for them. +*/ + +#include +#ifndef DONT_HAVE_ERRNO_H +#include +#endif + +unsigned long +PyOS_strtoul(register char *str, char **ptr, int base) +{ + register unsigned long result; /* return value of the function */ + register int c; /* current input character */ + register unsigned long temp; /* used in overflow testing */ + int ovf; /* true if overflow occurred */ + + result = 0; + ovf = 0; + +/* catch silly bases */ + if (base != 0 && (base < 2 || base > 36)) + { + if (ptr) + *ptr = str; + return 0; + } + +/* skip leading white space */ + while (*str && isspace(Py_CHARMASK(*str))) + str++; + +/* check for leading 0 or 0x for auto-base or base 16 */ + switch (base) + { + case 0: /* look for leading 0, 0x or 0X */ + if (*str == '0') + { + str++; + if (*str == 'x' || *str == 'X') + { + str++; + base = 16; + } + else + base = 8; + } + else + base = 10; + break; + + case 16: /* skip leading 0x or 0X */ + if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X')) + str += 2; + break; + } + +/* do the conversion */ + while ((c = Py_CHARMASK(*str)) != '\0') + { + if (isdigit(c) && c - '0' < base) + c -= '0'; + else + { + if (isupper(c)) + c = tolower(c); + if (c >= 'a' && c <= 'z') + c -= 'a' - 10; + else /* non-"digit" character */ + break; + if (c >= base) /* non-"digit" character */ + break; + } + temp = result; + result = result * base + c; +#ifndef MPW + if(base == 10) { + if(((long)(result - c) / base != (long)temp)) /* overflow */ + ovf = 1; + } + else { + if ((result - c) / base != temp) /* overflow */ + ovf = 1; + } +#endif + str++; + } + +/* set pointer to point to the last character scanned */ + if (ptr) + *ptr = str; + if (ovf) + { + result = (unsigned long) ~0L; + errno = ERANGE; + } + return result; +} + +long +PyOS_strtol(char *str, char **ptr, int base) +{ + long result; + char sign; + + while (*str && isspace(Py_CHARMASK(*str))) + str++; + + sign = *str; + if (sign == '+' || sign == '-') + str++; + + result = (long) PyOS_strtoul(str, ptr, base); + + /* Signal overflow if the result appears negative, + except for the largest negative integer */ + if (result < 0 && !(sign == '-' && result == -result)) { + errno = ERANGE; + result = 0x7fffffff; + } + + if (sign == '-') + result = -result; + + return result; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pyfpe.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pyfpe.c new file mode 100644 index 00000000..7b686d24 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pyfpe.c @@ -0,0 +1,23 @@ +#include "pyconfig.h" +#include "pyfpe.h" +/* + * The signal handler for SIGFPE is actually declared in an external + * module fpectl, or as preferred by the user. These variable + * definitions are required in order to compile Python without + * getting missing externals, but to actually handle SIGFPE requires + * defining a handler and enabling generation of SIGFPE. + */ + +#ifdef WANT_SIGFPE_HANDLER +jmp_buf PyFPE_jbuf; +int PyFPE_counter = 0; +#endif + +/* Have this outside the above #ifdef, since some picky ANSI compilers issue a + warning when compiling an empty file. */ + +double +PyFPE_dummy(void *dummy) +{ + return 1.0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pystate.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pystate.c new file mode 100644 index 00000000..1f173e1b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pystate.c @@ -0,0 +1,483 @@ + +/* Thread and interpreter state structures and their interfaces */ + +#include "Python.h" + +#ifdef HAVE_DLOPEN +#ifdef HAVE_DLFCN_H +#include +#endif +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#endif + + +#define ZAP(x) { \ + PyObject *tmp = (PyObject *)(x); \ + (x) = NULL; \ + Py_XDECREF(tmp); \ +} + + +#ifdef WITH_THREAD +#include "pythread.h" +static PyThread_type_lock head_mutex = NULL; /* Protects interp->tstate_head */ +#define HEAD_INIT() (void)(head_mutex || (head_mutex = PyThread_allocate_lock())) +#define HEAD_LOCK() PyThread_acquire_lock(head_mutex, WAIT_LOCK) +#define HEAD_UNLOCK() PyThread_release_lock(head_mutex) +#else +#define HEAD_INIT() /* Nothing */ +#define HEAD_LOCK() /* Nothing */ +#define HEAD_UNLOCK() /* Nothing */ +#endif + +static PyInterpreterState *interp_head = NULL; + +PyThreadState *_PyThreadState_Current = NULL; +PyThreadFrameGetter _PyThreadState_GetFrame = NULL; + + +PyInterpreterState * +PyInterpreterState_New(void) +{ + PyInterpreterState *interp = PyMem_NEW(PyInterpreterState, 1); + + if (interp != NULL) { + HEAD_INIT(); + interp->modules = NULL; + interp->sysdict = NULL; + interp->builtins = NULL; + interp->tstate_head = NULL; + interp->codec_search_path = NULL; + interp->codec_search_cache = NULL; + interp->codec_error_registry = NULL; +#ifdef HAVE_DLOPEN +#ifdef RTLD_NOW + interp->dlopenflags = RTLD_NOW; +#else + interp->dlopenflags = RTLD_LAZY; +#endif +#endif + + HEAD_LOCK(); + interp->next = interp_head; + interp_head = interp; + HEAD_UNLOCK(); + } + + return interp; +} + + +void +PyInterpreterState_Clear(PyInterpreterState *interp) +{ + PyThreadState *p; + HEAD_LOCK(); + for (p = interp->tstate_head; p != NULL; p = p->next) + PyThreadState_Clear(p); + HEAD_UNLOCK(); + ZAP(interp->codec_search_path); + ZAP(interp->codec_search_cache); + ZAP(interp->codec_error_registry); + ZAP(interp->modules); + ZAP(interp->sysdict); + ZAP(interp->builtins); +} + + +static void +zapthreads(PyInterpreterState *interp) +{ + PyThreadState *p; + /* No need to lock the mutex here because this should only happen + when the threads are all really dead (XXX famous last words). */ + while ((p = interp->tstate_head) != NULL) { + PyThreadState_Delete(p); + } +} + + +void +PyInterpreterState_Delete(PyInterpreterState *interp) +{ + PyInterpreterState **p; + zapthreads(interp); + HEAD_LOCK(); + for (p = &interp_head; ; p = &(*p)->next) { + if (*p == NULL) + Py_FatalError( + "PyInterpreterState_Delete: invalid interp"); + if (*p == interp) + break; + } + if (interp->tstate_head != NULL) + Py_FatalError("PyInterpreterState_Delete: remaining threads"); + *p = interp->next; + HEAD_UNLOCK(); + PyMem_DEL(interp); +} + + +/* Default implementation for _PyThreadState_GetFrame */ +static struct _frame * +threadstate_getframe(PyThreadState *self) +{ + return self->frame; +} + +PyThreadState * +PyThreadState_New(PyInterpreterState *interp) +{ + PyThreadState *tstate = PyMem_NEW(PyThreadState, 1); + if (_PyThreadState_GetFrame == NULL) + _PyThreadState_GetFrame = threadstate_getframe; + + if (tstate != NULL) { + tstate->interp = interp; + + tstate->frame = NULL; + tstate->recursion_depth = 0; + tstate->tracing = 0; + tstate->use_tracing = 0; + tstate->tick_counter = 0; + tstate->gilstate_counter = 0; + tstate->async_exc = NULL; +#ifdef WITH_THREAD + tstate->thread_id = PyThread_get_thread_ident(); +#else + tstate->thread_id = 0; +#endif + + tstate->dict = NULL; + + tstate->curexc_type = NULL; + tstate->curexc_value = NULL; + tstate->curexc_traceback = NULL; + + tstate->exc_type = NULL; + tstate->exc_value = NULL; + tstate->exc_traceback = NULL; + + tstate->c_profilefunc = NULL; + tstate->c_tracefunc = NULL; + tstate->c_profileobj = NULL; + tstate->c_traceobj = NULL; + + HEAD_LOCK(); + tstate->next = interp->tstate_head; + interp->tstate_head = tstate; + HEAD_UNLOCK(); + } + + return tstate; +} + + +void +PyThreadState_Clear(PyThreadState *tstate) +{ + if (Py_VerboseFlag && tstate->frame != NULL) + fprintf(stderr, + "PyThreadState_Clear: warning: thread still has a frame\n"); + + ZAP(tstate->frame); + + ZAP(tstate->dict); + ZAP(tstate->async_exc); + + ZAP(tstate->curexc_type); + ZAP(tstate->curexc_value); + ZAP(tstate->curexc_traceback); + + ZAP(tstate->exc_type); + ZAP(tstate->exc_value); + ZAP(tstate->exc_traceback); + + tstate->c_profilefunc = NULL; + tstate->c_tracefunc = NULL; + ZAP(tstate->c_profileobj); + ZAP(tstate->c_traceobj); +} + + +/* Common code for PyThreadState_Delete() and PyThreadState_DeleteCurrent() */ +static void +tstate_delete_common(PyThreadState *tstate) +{ + PyInterpreterState *interp; + PyThreadState **p; + if (tstate == NULL) + Py_FatalError("PyThreadState_Delete: NULL tstate"); + interp = tstate->interp; + if (interp == NULL) + Py_FatalError("PyThreadState_Delete: NULL interp"); + HEAD_LOCK(); + for (p = &interp->tstate_head; ; p = &(*p)->next) { + if (*p == NULL) + Py_FatalError( + "PyThreadState_Delete: invalid tstate"); + if (*p == tstate) + break; + } + *p = tstate->next; + HEAD_UNLOCK(); + PyMem_DEL(tstate); +} + + +void +PyThreadState_Delete(PyThreadState *tstate) +{ + if (tstate == _PyThreadState_Current) + Py_FatalError("PyThreadState_Delete: tstate is still current"); + tstate_delete_common(tstate); +} + + +#ifdef WITH_THREAD +void +PyThreadState_DeleteCurrent() +{ + PyThreadState *tstate = _PyThreadState_Current; + if (tstate == NULL) + Py_FatalError( + "PyThreadState_DeleteCurrent: no current tstate"); + _PyThreadState_Current = NULL; + tstate_delete_common(tstate); + PyEval_ReleaseLock(); +} +#endif /* WITH_THREAD */ + + +PyThreadState * +PyThreadState_Get(void) +{ + if (_PyThreadState_Current == NULL) + Py_FatalError("PyThreadState_Get: no current thread"); + + return _PyThreadState_Current; +} + + +PyThreadState * +PyThreadState_Swap(PyThreadState *new) +{ + PyThreadState *old = _PyThreadState_Current; + + _PyThreadState_Current = new; + /* It should not be possible for more than one thread state + to be used for a thread. Check this the best we can in debug + builds. + */ +#if defined(Py_DEBUG) && defined(WITH_THREAD) + if (new) { + PyThreadState *check = PyGILState_GetThisThreadState(); + if (check && check != new) + Py_FatalError("Invalid thread state for this thread"); + } +#endif + return old; +} + +/* An extension mechanism to store arbitrary additional per-thread state. + PyThreadState_GetDict() returns a dictionary that can be used to hold such + state; the caller should pick a unique key and store its state there. If + PyThreadState_GetDict() returns NULL, an exception has *not* been raised + and the caller should assume no per-thread state is available. */ + +PyObject * +PyThreadState_GetDict(void) +{ + if (_PyThreadState_Current == NULL) + return NULL; + + if (_PyThreadState_Current->dict == NULL) { + PyObject *d; + _PyThreadState_Current->dict = d = PyDict_New(); + if (d == NULL) + PyErr_Clear(); + } + return _PyThreadState_Current->dict; +} + + +/* Asynchronously raise an exception in a thread. + Requested by Just van Rossum and Alex Martelli. + To prevent naive misuse, you must write your own exception + to call this. Must be called with the GIL held. + Returns the number of tstates modified; if it returns a number + greater than one, you're in trouble, and you should call it again + with exc=NULL to revert the effect. This raises no exceptions. */ + +int +PyThreadState_SetAsyncExc(long id, PyObject *exc) { + PyThreadState *tstate = PyThreadState_Get(); + PyInterpreterState *interp = tstate->interp; + PyThreadState *p; + int count = 0; + for (p = interp->tstate_head; p != NULL; p = p->next) { + if (p->thread_id != id) + continue; + ZAP(p->async_exc); + Py_XINCREF(exc); + p->async_exc = exc; + count += 1; + } + return count; +} + + +/* Routines for advanced debuggers, requested by David Beazley. + Don't use unless you know what you are doing! */ + +PyInterpreterState * +PyInterpreterState_Head(void) +{ + return interp_head; +} + +PyInterpreterState * +PyInterpreterState_Next(PyInterpreterState *interp) { + return interp->next; +} + +PyThreadState * +PyInterpreterState_ThreadHead(PyInterpreterState *interp) { + return interp->tstate_head; +} + +PyThreadState * +PyThreadState_Next(PyThreadState *tstate) { + return tstate->next; +} + + +/* Python "auto thread state" API. */ +#ifdef WITH_THREAD + +/* Keep this as a static, as it is not reliable! It can only + ever be compared to the state for the *current* thread. + * If not equal, then it doesn't matter that the actual + value may change immediately after comparison, as it can't + possibly change to the current thread's state. + * If equal, then the current thread holds the lock, so the value can't + change until we yield the lock. +*/ +static int +PyThreadState_IsCurrent(PyThreadState *tstate) +{ + /* Must be the tstate for this thread */ + assert(PyGILState_GetThisThreadState()==tstate); + /* On Windows at least, simple reads and writes to 32 bit values + are atomic. + */ + return tstate == _PyThreadState_Current; +} + +/* The single PyInterpreterState used by this process' + GILState implementation +*/ +static PyInterpreterState *autoInterpreterState = NULL; +static int autoTLSkey = 0; + +/* Internal initialization/finalization functions called by + Py_Initialize/Py_Finalize +*/ +void _PyGILState_Init(PyInterpreterState *i, PyThreadState *t) +{ + assert(i && t); /* must init with a valid states */ + autoTLSkey = PyThread_create_key(); + autoInterpreterState = i; + /* Now stash the thread state for this thread in TLS */ + PyThread_set_key_value(autoTLSkey, (void *)t); + assert(t->gilstate_counter==0); /* must be a new thread state */ + t->gilstate_counter = 1; +} + +void _PyGILState_Fini(void) +{ + PyThread_delete_key(autoTLSkey); + autoTLSkey = 0; + autoInterpreterState = NULL;; +} + +/* The public functions */ +PyThreadState *PyGILState_GetThisThreadState(void) +{ + if (autoInterpreterState==NULL || autoTLSkey==0) + return NULL; + return (PyThreadState *) PyThread_get_key_value(autoTLSkey); +} + +PyGILState_STATE PyGILState_Ensure(void) +{ + int current; + PyThreadState *tcur; + /* Note that we do not auto-init Python here - apart from + potential races with 2 threads auto-initializing, pep-311 + spells out other issues. Embedders are expected to have + called Py_Initialize() and usually PyEval_InitThreads(). + */ + assert(autoInterpreterState); /* Py_Initialize() hasn't been called! */ + tcur = PyThread_get_key_value(autoTLSkey); + if (tcur==NULL) { + /* Create a new thread state for this thread */ + tcur = PyThreadState_New(autoInterpreterState); + if (tcur==NULL) + Py_FatalError("Couldn't create thread-state for new thread"); + PyThread_set_key_value(autoTLSkey, (void *)tcur); + current = 0; /* new thread state is never current */ + } else + current = PyThreadState_IsCurrent(tcur); + if (!current) + PyEval_RestoreThread(tcur); + /* Update our counter in the thread-state - no need for locks: + - tcur will remain valid as we hold the GIL. + - the counter is safe as we are the only thread "allowed" + to modify this value + */ + tcur->gilstate_counter++; + return current ? PyGILState_LOCKED : PyGILState_UNLOCKED; +} + +void PyGILState_Release(PyGILState_STATE oldstate) +{ + PyThreadState *tcur = PyThread_get_key_value(autoTLSkey); + if (tcur==NULL) + Py_FatalError("auto-releasing thread-state, " + "but no thread-state for this thread"); + /* We must hold the GIL and have our thread state current */ + /* XXX - remove the check - the assert should be fine, + but while this is very new (April 2003), the extra check + by release-only users can't hurt. + */ + if (!PyThreadState_IsCurrent(tcur)) + Py_FatalError("This thread state must be current when releasing"); + assert (PyThreadState_IsCurrent(tcur)); + tcur->gilstate_counter -= 1; + assert (tcur->gilstate_counter >= 0); /* illegal counter value */ + + /* If we are about to destroy this thread-state, we must + clear it while the lock is held, as destructors may run + */ + if (tcur->gilstate_counter==0) { + /* can't have been locked when we created it */ + assert(oldstate==PyGILState_UNLOCKED); + PyThreadState_Clear(tcur); + } + + /* Release the lock if necessary */ + if (oldstate==PyGILState_UNLOCKED) + PyEval_ReleaseThread(tcur); + + /* Now complete destruction of the thread if necessary */ + if (tcur->gilstate_counter==0) { + /* Delete this thread from our TLS */ + PyThread_delete_key_value(autoTLSkey); + /* Delete the thread-state */ + PyThreadState_Delete(tcur); + } +} +#endif /* WITH_THREAD */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pythonrun.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pythonrun.c new file mode 100644 index 00000000..9854e859 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/pythonrun.c @@ -0,0 +1,1724 @@ + +/* Python interpreter top-level routines, including init/exit */ + +#include "Python.h" + +#include "grammar.h" +#include "node.h" +#include "token.h" +#include "parsetok.h" +#include "errcode.h" +#include "compile.h" +#include "symtable.h" +#include "eval.h" +#include "marshal.h" + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#ifdef HAVE_LANGINFO_H +#include +#include +#endif + +#ifdef MS_WINDOWS +#undef BYTE +#ifdef MS_XBOX +#include +#else +#include "windows.h" +#endif +#endif + +#ifdef macintosh +#include "macglue.h" +#endif +extern char *Py_GetPath(void); + +extern grammar _PyParser_Grammar; /* From graminit.c */ + +/* Forward */ +static void initmain(void); +static void initsite(void); +static PyObject *run_err_node(node *, const char *, PyObject *, PyObject *, + PyCompilerFlags *); +static PyObject *run_node(node *, const char *, PyObject *, PyObject *, + PyCompilerFlags *); +static PyObject *run_pyc_file(FILE *, const char *, PyObject *, PyObject *, + PyCompilerFlags *); +static void err_input(perrdetail *); +static void initsigs(void); +static void call_sys_exitfunc(void); +static void call_ll_exitfuncs(void); +extern void _PyUnicode_Init(void); +extern void _PyUnicode_Fini(void); + +#ifdef WITH_THREAD +extern void _PyGILState_Init(PyInterpreterState *, PyThreadState *); +extern void _PyGILState_Fini(void); +#endif /* WITH_THREAD */ + +int Py_DebugFlag; /* Needed by parser.c */ +int Py_VerboseFlag; /* Needed by import.c */ +int Py_InteractiveFlag; /* Needed by Py_FdIsInteractive() below */ +int Py_NoSiteFlag; /* Suppress 'import site' */ +int Py_UseClassExceptionsFlag = 1; /* Needed by bltinmodule.c: deprecated */ +int Py_FrozenFlag; /* Needed by getpath.c */ +int Py_UnicodeFlag = 0; /* Needed by compile.c */ +int Py_IgnoreEnvironmentFlag; /* e.g. PYTHONPATH, PYTHONHOME */ +/* _XXX Py_QnewFlag should go away in 2.3. It's true iff -Qnew is passed, + on the command line, and is used in 2.2 by ceval.c to make all "/" divisions + true divisions (which they will be in 2.3). */ +int _Py_QnewFlag = 0; + +/* Reference to 'warnings' module, to avoid importing it + on the fly when the import lock may be held. See 683658/771097 +*/ +static PyObject *warnings_module = NULL; + +/* Returns a borrowed reference to the 'warnings' module, or NULL. + If the module is returned, it is guaranteed to have been obtained + without acquiring the import lock +*/ +PyObject *PyModule_GetWarningsModule(void) +{ + PyObject *typ, *val, *tb; + PyObject *all_modules; + /* If we managed to get the module at init time, just use it */ + if (warnings_module) + return warnings_module; + /* If it wasn't available at init time, it may be available + now in sys.modules (common scenario is frozen apps: import + at init time fails, but the frozen init code sets up sys.path + correctly, then does an implicit import of warnings for us + */ + /* Save and restore any exceptions */ + PyErr_Fetch(&typ, &val, &tb); + + all_modules = PySys_GetObject("modules"); + if (all_modules) { + warnings_module = PyDict_GetItemString(all_modules, "warnings"); + /* We keep a ref in the global */ + Py_XINCREF(warnings_module); + } + PyErr_Restore(typ, val, tb); + return warnings_module; +} + +static int initialized = 0; + +/* API to access the initialized flag -- useful for esoteric use */ + +int +Py_IsInitialized(void) +{ + return initialized; +} + +/* Global initializations. Can be undone by Py_Finalize(). Don't + call this twice without an intervening Py_Finalize() call. When + initializations fail, a fatal error is issued and the function does + not return. On return, the first thread and interpreter state have + been created. + + Locking: you must hold the interpreter lock while calling this. + (If the lock has not yet been initialized, that's equivalent to + having the lock, but you cannot use multiple threads.) + +*/ + +static int +add_flag(int flag, const char *envs) +{ + int env = atoi(envs); + if (flag < env) + flag = env; + if (flag < 1) + flag = 1; + return flag; +} + +void +Py_Initialize(void) +{ + PyInterpreterState *interp; + PyThreadState *tstate; + PyObject *bimod, *sysmod; + char *p; +#if defined(Py_USING_UNICODE) && defined(HAVE_LANGINFO_H) && defined(CODESET) + char *codeset; + char *saved_locale; + PyObject *sys_stream, *sys_isatty; +#endif + extern void _Py_ReadyTypes(void); + + if (initialized) + return; + initialized = 1; + + if ((p = Py_GETENV("PYTHONDEBUG")) && *p != '\0') + Py_DebugFlag = add_flag(Py_DebugFlag, p); + if ((p = Py_GETENV("PYTHONVERBOSE")) && *p != '\0') + Py_VerboseFlag = add_flag(Py_VerboseFlag, p); + if ((p = Py_GETENV("PYTHONOPTIMIZE")) && *p != '\0') + Py_OptimizeFlag = add_flag(Py_OptimizeFlag, p); + + interp = PyInterpreterState_New(); + if (interp == NULL) + Py_FatalError("Py_Initialize: can't make first interpreter"); + + tstate = PyThreadState_New(interp); + if (tstate == NULL) + Py_FatalError("Py_Initialize: can't make first thread"); + (void) PyThreadState_Swap(tstate); + + _Py_ReadyTypes(); + + if (!_PyFrame_Init()) + Py_FatalError("Py_Initialize: can't init frames"); + + if (!_PyInt_Init()) + Py_FatalError("Py_Initialize: can't init ints"); + + interp->modules = PyDict_New(); + if (interp->modules == NULL) + Py_FatalError("Py_Initialize: can't make modules dictionary"); + +#ifdef Py_USING_UNICODE + /* Init Unicode implementation; relies on the codec registry */ + _PyUnicode_Init(); +#endif + + bimod = _PyBuiltin_Init(); + if (bimod == NULL) + Py_FatalError("Py_Initialize: can't initialize __builtin__"); + interp->builtins = PyModule_GetDict(bimod); + Py_INCREF(interp->builtins); + + sysmod = _PySys_Init(); + if (sysmod == NULL) + Py_FatalError("Py_Initialize: can't initialize sys"); + interp->sysdict = PyModule_GetDict(sysmod); + Py_INCREF(interp->sysdict); + _PyImport_FixupExtension("sys", "sys"); + PySys_SetPath(Py_GetPath()); + PyDict_SetItemString(interp->sysdict, "modules", + interp->modules); + + _PyImport_Init(); + + /* initialize builtin exceptions */ + _PyExc_Init(); + _PyImport_FixupExtension("exceptions", "exceptions"); + + /* phase 2 of builtins */ + _PyImport_FixupExtension("__builtin__", "__builtin__"); + + _PyImportHooks_Init(); + + initsigs(); /* Signal handling stuff, including initintr() */ + + initmain(); /* Module __main__ */ + if (!Py_NoSiteFlag) + initsite(); /* Module site */ + + /* auto-thread-state API, if available */ +#ifdef WITH_THREAD + _PyGILState_Init(interp, tstate); +#endif /* WITH_THREAD */ + + warnings_module = PyImport_ImportModule("warnings"); + if (!warnings_module) + PyErr_Clear(); + +#if defined(Py_USING_UNICODE) && defined(HAVE_LANGINFO_H) && defined(CODESET) + /* On Unix, set the file system encoding according to the + user's preference, if the CODESET names a well-known + Python codec, and Py_FileSystemDefaultEncoding isn't + initialized by other means. Also set the encoding of + stdin and stdout if these are terminals. */ + + saved_locale = strdup(setlocale(LC_CTYPE, NULL)); + setlocale(LC_CTYPE, ""); + codeset = nl_langinfo(CODESET); + if (codeset && *codeset) { + PyObject *enc = PyCodec_Encoder(codeset); + if (enc) { + codeset = strdup(codeset); + Py_DECREF(enc); + } else { + codeset = NULL; + PyErr_Clear(); + } + } else + codeset = NULL; + setlocale(LC_CTYPE, saved_locale); + free(saved_locale); + + if (codeset) { + sys_stream = PySys_GetObject("stdin"); + sys_isatty = PyObject_CallMethod(sys_stream, "isatty", ""); + if (!sys_isatty) + PyErr_Clear(); + if(sys_isatty && PyObject_IsTrue(sys_isatty)) { + if (!PyFile_SetEncoding(sys_stream, codeset)) + Py_FatalError("Cannot set codeset of stdin"); + } + Py_XDECREF(sys_isatty); + + sys_stream = PySys_GetObject("stdout"); + sys_isatty = PyObject_CallMethod(sys_stream, "isatty", ""); + if (!sys_isatty) + PyErr_Clear(); + if(sys_isatty && PyObject_IsTrue(sys_isatty)) { + if (!PyFile_SetEncoding(sys_stream, codeset)) + Py_FatalError("Cannot set codeset of stdout"); + } + Py_XDECREF(sys_isatty); + + if (!Py_FileSystemDefaultEncoding) + Py_FileSystemDefaultEncoding = codeset; + else + free(codeset); + } +#endif +} + +#ifdef COUNT_ALLOCS +extern void dump_counts(void); +#endif + +/* Undo the effect of Py_Initialize(). + + Beware: if multiple interpreter and/or thread states exist, these + are not wiped out; only the current thread and interpreter state + are deleted. But since everything else is deleted, those other + interpreter and thread states should no longer be used. + + (XXX We should do better, e.g. wipe out all interpreters and + threads.) + + Locking: as above. + +*/ + +void +Py_Finalize(void) +{ + PyInterpreterState *interp; + PyThreadState *tstate; + + if (!initialized) + return; + + /* The interpreter is still entirely intact at this point, and the + * exit funcs may be relying on that. In particular, if some thread + * or exit func is still waiting to do an import, the import machinery + * expects Py_IsInitialized() to return true. So don't say the + * interpreter is uninitialized until after the exit funcs have run. + * Note that Threading.py uses an exit func to do a join on all the + * threads created thru it, so this also protects pending imports in + * the threads created via Threading. + */ + call_sys_exitfunc(); + initialized = 0; + + /* Get current thread state and interpreter pointer */ + tstate = PyThreadState_Get(); + interp = tstate->interp; + + /* Disable signal handling */ + PyOS_FiniInterrupts(); + + /* drop module references we saved */ + Py_XDECREF(warnings_module); + warnings_module = NULL; + + /* Collect garbage. This may call finalizers; it's nice to call these + * before all modules are destroyed. + * XXX If a __del__ or weakref callback is triggered here, and tries to + * XXX import a module, bad things can happen, because Python no + * XXX longer believes it's initialized. + * XXX Fatal Python error: Interpreter not initialized (version mismatch?) + * XXX is easy to provoke that way. I've also seen, e.g., + * XXX Exception exceptions.ImportError: 'No module named sha' + * XXX in ignored + * XXX but I'm unclear on exactly how that one happens. In any case, + * XXX I haven't seen a real-life report of either of these. + */ + PyGC_Collect(); + + /* Destroy all modules */ + PyImport_Cleanup(); + + /* Collect final garbage. This disposes of cycles created by + * new-style class definitions, for example. + * XXX This is disabled because it caused too many problems. If + * XXX a __del__ or weakref callback triggers here, Python code has + * XXX a hard time running, because even the sys module has been + * XXX cleared out (sys.stdout is gone, sys.excepthook is gone, etc). + * XXX One symptom is a sequence of information-free messages + * XXX coming from threads (if a __del__ or callback is invoked, + * XXX other threads can execute too, and any exception they encounter + * XXX triggers a comedy of errors as subsystem after subsystem + * XXX fails to find what it *expects* to find in sys to help report + * XXX the exception and consequent unexpected failures). I've also + * XXX seen segfaults then, after adding print statements to the + * XXX Python code getting called. + */ +#if 0 + PyGC_Collect(); +#endif + + /* Destroy the database used by _PyImport_{Fixup,Find}Extension */ + _PyImport_Fini(); + + /* Debugging stuff */ +#ifdef COUNT_ALLOCS + dump_counts(); +#endif + +#ifdef Py_REF_DEBUG + fprintf(stderr, "[%ld refs]\n", _Py_RefTotal); +#endif + +#ifdef Py_TRACE_REFS + /* Display all objects still alive -- this can invoke arbitrary + * __repr__ overrides, so requires a mostly-intact interpreter. + * Alas, a lot of stuff may still be alive now that will be cleaned + * up later. + */ + if (Py_GETENV("PYTHONDUMPREFS")) + _Py_PrintReferences(stderr); +#endif /* Py_TRACE_REFS */ + + /* Now we decref the exception classes. After this point nothing + can raise an exception. That's okay, because each Fini() method + below has been checked to make sure no exceptions are ever + raised. + */ + _PyExc_Fini(); + + /* Cleanup auto-thread-state */ +#ifdef WITH_THREAD + _PyGILState_Fini(); +#endif /* WITH_THREAD */ + + /* Clear interpreter state */ + PyInterpreterState_Clear(interp); + + /* Delete current thread */ + PyThreadState_Swap(NULL); + PyInterpreterState_Delete(interp); + + /* Sundry finalizers */ + PyMethod_Fini(); + PyFrame_Fini(); + PyCFunction_Fini(); + PyTuple_Fini(); + PyString_Fini(); + PyInt_Fini(); + PyFloat_Fini(); + +#ifdef Py_USING_UNICODE + /* Cleanup Unicode implementation */ + _PyUnicode_Fini(); +#endif + + /* XXX Still allocated: + - various static ad-hoc pointers to interned strings + - int and float free list blocks + - whatever various modules and libraries allocate + */ + + PyGrammar_RemoveAccelerators(&_PyParser_Grammar); + +#ifdef Py_TRACE_REFS + /* Display addresses (& refcnts) of all objects still alive. + * An address can be used to find the repr of the object, printed + * above by _Py_PrintReferences. + */ + if (Py_GETENV("PYTHONDUMPREFS")) + _Py_PrintReferenceAddresses(stderr); +#endif /* Py_TRACE_REFS */ +#ifdef PYMALLOC_DEBUG + if (Py_GETENV("PYTHONMALLOCSTATS")) + _PyObject_DebugMallocStats(); +#endif + + call_ll_exitfuncs(); +} + +/* Create and initialize a new interpreter and thread, and return the + new thread. This requires that Py_Initialize() has been called + first. + + Unsuccessful initialization yields a NULL pointer. Note that *no* + exception information is available even in this case -- the + exception information is held in the thread, and there is no + thread. + + Locking: as above. + +*/ + +PyThreadState * +Py_NewInterpreter(void) +{ + PyInterpreterState *interp; + PyThreadState *tstate, *save_tstate; + PyObject *bimod, *sysmod; + + if (!initialized) + Py_FatalError("Py_NewInterpreter: call Py_Initialize first"); + + interp = PyInterpreterState_New(); + if (interp == NULL) + return NULL; + + tstate = PyThreadState_New(interp); + if (tstate == NULL) { + PyInterpreterState_Delete(interp); + return NULL; + } + + save_tstate = PyThreadState_Swap(tstate); + + /* XXX The following is lax in error checking */ + + interp->modules = PyDict_New(); + + bimod = _PyImport_FindExtension("__builtin__", "__builtin__"); + if (bimod != NULL) { + interp->builtins = PyModule_GetDict(bimod); + Py_INCREF(interp->builtins); + } + sysmod = _PyImport_FindExtension("sys", "sys"); + if (bimod != NULL && sysmod != NULL) { + interp->sysdict = PyModule_GetDict(sysmod); + Py_INCREF(interp->sysdict); + PySys_SetPath(Py_GetPath()); + PyDict_SetItemString(interp->sysdict, "modules", + interp->modules); + _PyImportHooks_Init(); + initmain(); + if (!Py_NoSiteFlag) + initsite(); + } + + if (!PyErr_Occurred()) + return tstate; + + /* Oops, it didn't work. Undo it all. */ + + PyErr_Print(); + PyThreadState_Clear(tstate); + PyThreadState_Swap(save_tstate); + PyThreadState_Delete(tstate); + PyInterpreterState_Delete(interp); + + return NULL; +} + +/* Delete an interpreter and its last thread. This requires that the + given thread state is current, that the thread has no remaining + frames, and that it is its interpreter's only remaining thread. + It is a fatal error to violate these constraints. + + (Py_Finalize() doesn't have these constraints -- it zaps + everything, regardless.) + + Locking: as above. + +*/ + +void +Py_EndInterpreter(PyThreadState *tstate) +{ + PyInterpreterState *interp = tstate->interp; + + if (tstate != PyThreadState_Get()) + Py_FatalError("Py_EndInterpreter: thread is not current"); + if (tstate->frame != NULL) + Py_FatalError("Py_EndInterpreter: thread still has a frame"); + if (tstate != interp->tstate_head || tstate->next != NULL) + Py_FatalError("Py_EndInterpreter: not the last thread"); + + PyImport_Cleanup(); + PyInterpreterState_Clear(interp); + PyThreadState_Swap(NULL); + PyInterpreterState_Delete(interp); +} + +static char *progname = "python"; + +void +Py_SetProgramName(char *pn) +{ + if (pn && *pn) + progname = pn; +} + +char * +Py_GetProgramName(void) +{ + return progname; +} + +static char *default_home = NULL; + +void +Py_SetPythonHome(char *home) +{ + default_home = home; +} + +char * +Py_GetPythonHome(void) +{ + char *home = default_home; + if (home == NULL && !Py_IgnoreEnvironmentFlag) + home = Py_GETENV("PYTHONHOME"); + return home; +} + +/* Create __main__ module */ + +static void +initmain(void) +{ + PyObject *m, *d; + m = PyImport_AddModule("__main__"); + if (m == NULL) + Py_FatalError("can't create __main__ module"); + d = PyModule_GetDict(m); + if (PyDict_GetItemString(d, "__builtins__") == NULL) { + PyObject *bimod = PyImport_ImportModule("__builtin__"); + if (bimod == NULL || + PyDict_SetItemString(d, "__builtins__", bimod) != 0) + Py_FatalError("can't add __builtins__ to __main__"); + Py_DECREF(bimod); + } +} + +/* Import the site module (not into __main__ though) */ + +static void +initsite(void) +{ + PyObject *m, *f; + m = PyImport_ImportModule("site"); + if (m == NULL) { + f = PySys_GetObject("stderr"); + if (Py_VerboseFlag) { + PyFile_WriteString( + "'import site' failed; traceback:\n", f); + PyErr_Print(); + } + else { + PyFile_WriteString( + "'import site' failed; use -v for traceback\n", f); + PyErr_Clear(); + } + } + else { + Py_DECREF(m); + } +} + +/* Parse input from a file and execute it */ + +int +PyRun_AnyFile(FILE *fp, const char *filename) +{ + return PyRun_AnyFileExFlags(fp, filename, 0, NULL); +} + +int +PyRun_AnyFileFlags(FILE *fp, const char *filename, PyCompilerFlags *flags) +{ + return PyRun_AnyFileExFlags(fp, filename, 0, flags); +} + +int +PyRun_AnyFileEx(FILE *fp, const char *filename, int closeit) +{ + return PyRun_AnyFileExFlags(fp, filename, closeit, NULL); +} + +int +PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit, + PyCompilerFlags *flags) +{ + if (filename == NULL) + filename = "???"; + if (Py_FdIsInteractive(fp, filename)) { + int err = PyRun_InteractiveLoopFlags(fp, filename, flags); + if (closeit) + fclose(fp); + return err; + } + else + return PyRun_SimpleFileExFlags(fp, filename, closeit, flags); +} + +int +PyRun_InteractiveLoop(FILE *fp, const char *filename) +{ + return PyRun_InteractiveLoopFlags(fp, filename, NULL); +} + +int +PyRun_InteractiveLoopFlags(FILE *fp, const char *filename, PyCompilerFlags *flags) +{ + PyObject *v; + int ret; + PyCompilerFlags local_flags; + + if (flags == NULL) { + flags = &local_flags; + local_flags.cf_flags = 0; + } + v = PySys_GetObject("ps1"); + if (v == NULL) { + PySys_SetObject("ps1", v = PyString_FromString(">>> ")); + Py_XDECREF(v); + } + v = PySys_GetObject("ps2"); + if (v == NULL) { + PySys_SetObject("ps2", v = PyString_FromString("... ")); + Py_XDECREF(v); + } + for (;;) { + ret = PyRun_InteractiveOneFlags(fp, filename, flags); +#ifdef Py_REF_DEBUG + fprintf(stderr, "[%ld refs]\n", _Py_RefTotal); +#endif + if (ret == E_EOF) + return 0; + /* + if (ret == E_NOMEM) + return -1; + */ + } +} + +int +PyRun_InteractiveOne(FILE *fp, const char *filename) +{ + return PyRun_InteractiveOneFlags(fp, filename, NULL); +} + +/* compute parser flags based on compiler flags */ +#define PARSER_FLAGS(flags) \ + (((flags) && (flags)->cf_flags & PyCF_DONT_IMPLY_DEDENT) ? \ + PyPARSE_DONT_IMPLY_DEDENT : 0) + +int +PyRun_InteractiveOneFlags(FILE *fp, const char *filename, PyCompilerFlags *flags) +{ + PyObject *m, *d, *v, *w; + node *n; + perrdetail err; + char *ps1 = "", *ps2 = ""; + + v = PySys_GetObject("ps1"); + if (v != NULL) { + v = PyObject_Str(v); + if (v == NULL) + PyErr_Clear(); + else if (PyString_Check(v)) + ps1 = PyString_AsString(v); + } + w = PySys_GetObject("ps2"); + if (w != NULL) { + w = PyObject_Str(w); + if (w == NULL) + PyErr_Clear(); + else if (PyString_Check(w)) + ps2 = PyString_AsString(w); + } + n = PyParser_ParseFileFlags(fp, filename, &_PyParser_Grammar, + Py_single_input, ps1, ps2, &err, + PARSER_FLAGS(flags)); + Py_XDECREF(v); + Py_XDECREF(w); + if (n == NULL) { + if (err.error == E_EOF) { + if (err.text) + PyMem_DEL(err.text); + return E_EOF; + } + err_input(&err); + PyErr_Print(); + return err.error; + } + m = PyImport_AddModule("__main__"); + if (m == NULL) + return -1; + d = PyModule_GetDict(m); + v = run_node(n, filename, d, d, flags); + if (v == NULL) { + PyErr_Print(); + return -1; + } + Py_DECREF(v); + if (Py_FlushLine()) + PyErr_Clear(); + return 0; +} + +int +PyRun_SimpleFile(FILE *fp, const char *filename) +{ + return PyRun_SimpleFileEx(fp, filename, 0); +} + +/* Check whether a file maybe a pyc file: Look at the extension, + the file type, and, if we may close it, at the first few bytes. */ + +static int +maybe_pyc_file(FILE *fp, const char* filename, const char* ext, int closeit) +{ + if (strcmp(ext, ".pyc") == 0 || strcmp(ext, ".pyo") == 0) + return 1; + +#ifdef macintosh + /* On a mac, we also assume a pyc file for types 'PYC ' and 'APPL' */ + if (PyMac_getfiletype((char *)filename) == 'PYC ' + || PyMac_getfiletype((char *)filename) == 'APPL') + return 1; +#endif /* macintosh */ + + /* Only look into the file if we are allowed to close it, since + it then should also be seekable. */ + if (closeit) { + /* Read only two bytes of the magic. If the file was opened in + text mode, the bytes 3 and 4 of the magic (\r\n) might not + be read as they are on disk. */ + unsigned int halfmagic = PyImport_GetMagicNumber() & 0xFFFF; + unsigned char buf[2]; + /* Mess: In case of -x, the stream is NOT at its start now, + and ungetc() was used to push back the first newline, + which makes the current stream position formally undefined, + and a x-platform nightmare. + Unfortunately, we have no direct way to know whether -x + was specified. So we use a terrible hack: if the current + stream position is not 0, we assume -x was specified, and + give up. Bug 132850 on SourceForge spells out the + hopelessness of trying anything else (fseek and ftell + don't work predictably x-platform for text-mode files). + */ + int ispyc = 0; + if (ftell(fp) == 0) { + if (fread(buf, 1, 2, fp) == 2 && + ((unsigned int)buf[1]<<8 | buf[0]) == halfmagic) + ispyc = 1; + rewind(fp); + } + return ispyc; + } + return 0; +} + +int +PyRun_SimpleFileEx(FILE *fp, const char *filename, int closeit) +{ + return PyRun_SimpleFileExFlags(fp, filename, closeit, NULL); +} + +int +PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, + PyCompilerFlags *flags) +{ + PyObject *m, *d, *v; + const char *ext; + + m = PyImport_AddModule("__main__"); + if (m == NULL) + return -1; + d = PyModule_GetDict(m); + if (PyDict_GetItemString(d, "__file__") == NULL) { + PyObject *f = PyString_FromString(filename); + if (f == NULL) + return -1; + if (PyDict_SetItemString(d, "__file__", f) < 0) { + Py_DECREF(f); + return -1; + } + Py_DECREF(f); + } + ext = filename + strlen(filename) - 4; + if (maybe_pyc_file(fp, filename, ext, closeit)) { + /* Try to run a pyc file. First, re-open in binary */ + if (closeit) + fclose(fp); + if ((fp = fopen(filename, "rb")) == NULL) { + fprintf(stderr, "python: Can't reopen .pyc file\n"); + return -1; + } + /* Turn on optimization if a .pyo file is given */ + if (strcmp(ext, ".pyo") == 0) + Py_OptimizeFlag = 1; + v = run_pyc_file(fp, filename, d, d, flags); + } else { + v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d, + closeit, flags); + } + if (v == NULL) { + PyErr_Print(); + return -1; + } + Py_DECREF(v); + if (Py_FlushLine()) + PyErr_Clear(); + return 0; +} + +int +PyRun_SimpleString(const char *command) +{ + return PyRun_SimpleStringFlags(command, NULL); +} + +int +PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags) +{ + PyObject *m, *d, *v; + m = PyImport_AddModule("__main__"); + if (m == NULL) + return -1; + d = PyModule_GetDict(m); + v = PyRun_StringFlags(command, Py_file_input, d, d, flags); + if (v == NULL) { + PyErr_Print(); + return -1; + } + Py_DECREF(v); + if (Py_FlushLine()) + PyErr_Clear(); + return 0; +} + +static int +parse_syntax_error(PyObject *err, PyObject **message, const char **filename, + int *lineno, int *offset, const char **text) +{ + long hold; + PyObject *v; + + /* old style errors */ + if (PyTuple_Check(err)) + return PyArg_ParseTuple(err, "O(ziiz)", message, filename, + lineno, offset, text); + + /* new style errors. `err' is an instance */ + + if (! (v = PyObject_GetAttrString(err, "msg"))) + goto finally; + *message = v; + + if (!(v = PyObject_GetAttrString(err, "filename"))) + goto finally; + if (v == Py_None) + *filename = NULL; + else if (! (*filename = PyString_AsString(v))) + goto finally; + + Py_DECREF(v); + if (!(v = PyObject_GetAttrString(err, "lineno"))) + goto finally; + hold = PyInt_AsLong(v); + Py_DECREF(v); + v = NULL; + if (hold < 0 && PyErr_Occurred()) + goto finally; + *lineno = (int)hold; + + if (!(v = PyObject_GetAttrString(err, "offset"))) + goto finally; + if (v == Py_None) { + *offset = -1; + Py_DECREF(v); + v = NULL; + } else { + hold = PyInt_AsLong(v); + Py_DECREF(v); + v = NULL; + if (hold < 0 && PyErr_Occurred()) + goto finally; + *offset = (int)hold; + } + + if (!(v = PyObject_GetAttrString(err, "text"))) + goto finally; + if (v == Py_None) + *text = NULL; + else if (! (*text = PyString_AsString(v))) + goto finally; + Py_DECREF(v); + return 1; + +finally: + Py_XDECREF(v); + return 0; +} + +void +PyErr_Print(void) +{ + PyErr_PrintEx(1); +} + +static void +print_error_text(PyObject *f, int offset, const char *text) +{ + char *nl; + if (offset >= 0) { + if (offset > 0 && offset == (int)strlen(text)) + offset--; + for (;;) { + nl = strchr(text, '\n'); + if (nl == NULL || nl-text >= offset) + break; + offset -= (nl+1-text); + text = nl+1; + } + while (*text == ' ' || *text == '\t') { + text++; + offset--; + } + } + PyFile_WriteString(" ", f); + PyFile_WriteString(text, f); + if (*text == '\0' || text[strlen(text)-1] != '\n') + PyFile_WriteString("\n", f); + if (offset == -1) + return; + PyFile_WriteString(" ", f); + offset--; + while (offset > 0) { + PyFile_WriteString(" ", f); + offset--; + } + PyFile_WriteString("^\n", f); +} + +static void +handle_system_exit(void) +{ + PyObject *exception, *value, *tb; + int exitcode = 0; + + PyErr_Fetch(&exception, &value, &tb); + if (Py_FlushLine()) + PyErr_Clear(); + fflush(stdout); + if (value == NULL || value == Py_None) + goto done; + if (PyInstance_Check(value)) { + /* The error code should be in the `code' attribute. */ + PyObject *code = PyObject_GetAttrString(value, "code"); + if (code) { + Py_DECREF(value); + value = code; + if (value == Py_None) + goto done; + } + /* If we failed to dig out the 'code' attribute, + just let the else clause below print the error. */ + } + if (PyInt_Check(value)) + exitcode = (int)PyInt_AsLong(value); + else { + PyObject_Print(value, stderr, Py_PRINT_RAW); + PySys_WriteStderr("\n"); + exitcode = 1; + } + done: + /* Restore and clear the exception info, in order to properly decref + * the exception, value, and traceback. If we just exit instead, + * these leak, which confuses PYTHONDUMPREFS output, and may prevent + * some finalizers from running. + */ + PyErr_Restore(exception, value, tb); + PyErr_Clear(); + Py_Exit(exitcode); + /* NOTREACHED */ +} + +void +PyErr_PrintEx(int set_sys_last_vars) +{ + PyObject *exception, *v, *tb, *hook; + + if (PyErr_ExceptionMatches(PyExc_SystemExit)) { + handle_system_exit(); + } + PyErr_Fetch(&exception, &v, &tb); + PyErr_NormalizeException(&exception, &v, &tb); + if (exception == NULL) + return; + if (set_sys_last_vars) { + PySys_SetObject("last_type", exception); + PySys_SetObject("last_value", v); + PySys_SetObject("last_traceback", tb); + } + hook = PySys_GetObject("excepthook"); + if (hook) { + PyObject *args = Py_BuildValue("(OOO)", + exception, v ? v : Py_None, tb ? tb : Py_None); + PyObject *result = PyEval_CallObject(hook, args); + if (result == NULL) { + PyObject *exception2, *v2, *tb2; + if (PyErr_ExceptionMatches(PyExc_SystemExit)) { + handle_system_exit(); + } + PyErr_Fetch(&exception2, &v2, &tb2); + PyErr_NormalizeException(&exception2, &v2, &tb2); + if (Py_FlushLine()) + PyErr_Clear(); + fflush(stdout); + PySys_WriteStderr("Error in sys.excepthook:\n"); + PyErr_Display(exception2, v2, tb2); + PySys_WriteStderr("\nOriginal exception was:\n"); + PyErr_Display(exception, v, tb); + Py_XDECREF(exception2); + Py_XDECREF(v2); + Py_XDECREF(tb2); + } + Py_XDECREF(result); + Py_XDECREF(args); + } else { + PySys_WriteStderr("sys.excepthook is missing\n"); + PyErr_Display(exception, v, tb); + } + Py_XDECREF(exception); + Py_XDECREF(v); + Py_XDECREF(tb); +} + +void PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb) +{ + int err = 0; + PyObject *v = value; + PyObject *f = PySys_GetObject("stderr"); + if (f == NULL) + fprintf(stderr, "lost sys.stderr\n"); + else { + if (Py_FlushLine()) + PyErr_Clear(); + fflush(stdout); + if (tb && tb != Py_None) + err = PyTraceBack_Print(tb, f); + if (err == 0 && + PyObject_HasAttrString(v, "print_file_and_line")) + { + PyObject *message; + const char *filename, *text; + int lineno, offset; + if (!parse_syntax_error(v, &message, &filename, + &lineno, &offset, &text)) + PyErr_Clear(); + else { + char buf[10]; + PyFile_WriteString(" File \"", f); + if (filename == NULL) + PyFile_WriteString("", f); + else + PyFile_WriteString(filename, f); + PyFile_WriteString("\", line ", f); + PyOS_snprintf(buf, sizeof(buf), "%d", lineno); + PyFile_WriteString(buf, f); + PyFile_WriteString("\n", f); + if (text != NULL) + print_error_text(f, offset, text); + v = message; + /* Can't be bothered to check all those + PyFile_WriteString() calls */ + if (PyErr_Occurred()) + err = -1; + } + } + if (err) { + /* Don't do anything else */ + } + else if (PyClass_Check(exception)) { + PyClassObject* exc = (PyClassObject*)exception; + PyObject* className = exc->cl_name; + PyObject* moduleName = + PyDict_GetItemString(exc->cl_dict, "__module__"); + + if (moduleName == NULL) + err = PyFile_WriteString("", f); + else { + char* modstr = PyString_AsString(moduleName); + if (modstr && strcmp(modstr, "exceptions")) + { + err = PyFile_WriteString(modstr, f); + err += PyFile_WriteString(".", f); + } + } + if (err == 0) { + if (className == NULL) + err = PyFile_WriteString("", f); + else + err = PyFile_WriteObject(className, f, + Py_PRINT_RAW); + } + } + else + err = PyFile_WriteObject(exception, f, Py_PRINT_RAW); + if (err == 0) { + if (v != NULL && v != Py_None) { + PyObject *s = PyObject_Str(v); + /* only print colon if the str() of the + object is not the empty string + */ + if (s == NULL) + err = -1; + else if (!PyString_Check(s) || + PyString_GET_SIZE(s) != 0) + err = PyFile_WriteString(": ", f); + if (err == 0) + err = PyFile_WriteObject(s, f, Py_PRINT_RAW); + Py_XDECREF(s); + } + } + if (err == 0) + err = PyFile_WriteString("\n", f); + } + /* If an error happened here, don't show it. + XXX This is wrong, but too many callers rely on this behavior. */ + if (err != 0) + PyErr_Clear(); +} + +PyObject * +PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals) +{ + return run_err_node(PyParser_SimpleParseString(str, start), + "", globals, locals, NULL); +} + +PyObject * +PyRun_File(FILE *fp, const char *filename, int start, PyObject *globals, + PyObject *locals) +{ + return PyRun_FileEx(fp, filename, start, globals, locals, 0); +} + +PyObject * +PyRun_FileEx(FILE *fp, const char *filename, int start, PyObject *globals, + PyObject *locals, int closeit) +{ + node *n = PyParser_SimpleParseFile(fp, filename, start); + if (closeit) + fclose(fp); + return run_err_node(n, filename, globals, locals, NULL); +} + +PyObject * +PyRun_StringFlags(const char *str, int start, PyObject *globals, PyObject *locals, + PyCompilerFlags *flags) +{ + return run_err_node(PyParser_SimpleParseStringFlags( + str, start, PARSER_FLAGS(flags)), + "", globals, locals, flags); +} + +PyObject * +PyRun_FileFlags(FILE *fp, const char *filename, int start, PyObject *globals, + PyObject *locals, PyCompilerFlags *flags) +{ + return PyRun_FileExFlags(fp, filename, start, globals, locals, 0, + flags); +} + +PyObject * +PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals, + PyObject *locals, int closeit, PyCompilerFlags *flags) +{ + node *n = PyParser_SimpleParseFileFlags(fp, filename, start, + PARSER_FLAGS(flags)); + if (closeit) + fclose(fp); + return run_err_node(n, filename, globals, locals, flags); +} + +static PyObject * +run_err_node(node *n, const char *filename, PyObject *globals, PyObject *locals, + PyCompilerFlags *flags) +{ + if (n == NULL) + return NULL; + return run_node(n, filename, globals, locals, flags); +} + +static PyObject * +run_node(node *n, const char *filename, PyObject *globals, PyObject *locals, + PyCompilerFlags *flags) +{ + PyCodeObject *co; + PyObject *v; + co = PyNode_CompileFlags(n, filename, flags); + PyNode_Free(n); + if (co == NULL) + return NULL; + v = PyEval_EvalCode(co, globals, locals); + Py_DECREF(co); + return v; +} + +static PyObject * +run_pyc_file(FILE *fp, const char *filename, PyObject *globals, PyObject *locals, + PyCompilerFlags *flags) +{ + PyCodeObject *co; + PyObject *v; + long magic; + long PyImport_GetMagicNumber(void); + + magic = PyMarshal_ReadLongFromFile(fp); + if (magic != PyImport_GetMagicNumber()) { + PyErr_SetString(PyExc_RuntimeError, + "Bad magic number in .pyc file"); + return NULL; + } + (void) PyMarshal_ReadLongFromFile(fp); + v = PyMarshal_ReadLastObjectFromFile(fp); + fclose(fp); + if (v == NULL || !PyCode_Check(v)) { + Py_XDECREF(v); + PyErr_SetString(PyExc_RuntimeError, + "Bad code object in .pyc file"); + return NULL; + } + co = (PyCodeObject *)v; + v = PyEval_EvalCode(co, globals, locals); + if (v && flags) + flags->cf_flags |= (co->co_flags & PyCF_MASK); + Py_DECREF(co); + return v; +} + +PyObject * +Py_CompileString(const char *str, const char *filename, int start) +{ + return Py_CompileStringFlags(str, filename, start, NULL); +} + +PyObject * +Py_CompileStringFlags(const char *str, const char *filename, int start, + PyCompilerFlags *flags) +{ + node *n; + PyCodeObject *co; + + n = PyParser_SimpleParseStringFlagsFilename(str, filename, start, + PARSER_FLAGS(flags)); + if (n == NULL) + return NULL; + co = PyNode_CompileFlags(n, filename, flags); + PyNode_Free(n); + return (PyObject *)co; +} + +struct symtable * +Py_SymtableString(const char *str, const char *filename, int start) +{ + node *n; + struct symtable *st; + n = PyParser_SimpleParseStringFlagsFilename(str, filename, + start, 0); + if (n == NULL) + return NULL; + st = PyNode_CompileSymtable(n, filename); + PyNode_Free(n); + return st; +} + +/* Simplified interface to parsefile -- return node or set exception */ + +node * +PyParser_SimpleParseFileFlags(FILE *fp, const char *filename, int start, int flags) +{ + node *n; + perrdetail err; + n = PyParser_ParseFileFlags(fp, filename, &_PyParser_Grammar, start, + (char *)0, (char *)0, &err, flags); + if (n == NULL) + err_input(&err); + return n; +} + +node * +PyParser_SimpleParseFile(FILE *fp, const char *filename, int start) +{ + return PyParser_SimpleParseFileFlags(fp, filename, start, 0); +} + +/* Simplified interface to parsestring -- return node or set exception */ + +node * +PyParser_SimpleParseStringFlags(const char *str, int start, int flags) +{ + node *n; + perrdetail err; + n = PyParser_ParseStringFlags(str, &_PyParser_Grammar, start, &err, + flags); + if (n == NULL) + err_input(&err); + return n; +} + +node * +PyParser_SimpleParseString(const char *str, int start) +{ + return PyParser_SimpleParseStringFlags(str, start, 0); +} + +node * +PyParser_SimpleParseStringFlagsFilename(const char *str, const char *filename, + int start, int flags) +{ + node *n; + perrdetail err; + + n = PyParser_ParseStringFlagsFilename(str, filename, + &_PyParser_Grammar, + start, &err, flags); + if (n == NULL) + err_input(&err); + return n; +} + +node * +PyParser_SimpleParseStringFilename(const char *str, const char *filename, int start) +{ + return PyParser_SimpleParseStringFlagsFilename(str, filename, + start, 0); +} + +/* May want to move a more generalized form of this to parsetok.c or + even parser modules. */ + +void +PyParser_SetError(perrdetail *err) +{ + err_input(err); +} + +/* Set the error appropriate to the given input error code (see errcode.h) */ + +static void +err_input(perrdetail *err) +{ + PyObject *v, *w, *errtype; + PyObject* u = NULL; + char *msg = NULL; + errtype = PyExc_SyntaxError; + v = Py_BuildValue("(ziiz)", err->filename, + err->lineno, err->offset, err->text); + if (err->text != NULL) { + PyMem_DEL(err->text); + err->text = NULL; + } + switch (err->error) { + case E_SYNTAX: + errtype = PyExc_IndentationError; + if (err->expected == INDENT) + msg = "expected an indented block"; + else if (err->token == INDENT) + msg = "unexpected indent"; + else if (err->token == DEDENT) + msg = "unexpected unindent"; + else { + errtype = PyExc_SyntaxError; + msg = "invalid syntax"; + } + break; + case E_TOKEN: + msg = "invalid token"; + break; + case E_EOFS: + msg = "EOF while scanning triple-quoted string"; + break; + case E_EOLS: + msg = "EOL while scanning single-quoted string"; + break; + case E_INTR: + PyErr_SetNone(PyExc_KeyboardInterrupt); + Py_XDECREF(v); + return; + case E_NOMEM: + PyErr_NoMemory(); + Py_XDECREF(v); + return; + case E_EOF: + msg = "unexpected EOF while parsing"; + break; + case E_TABSPACE: + errtype = PyExc_TabError; + msg = "inconsistent use of tabs and spaces in indentation"; + break; + case E_OVERFLOW: + msg = "expression too long"; + break; + case E_DEDENT: + errtype = PyExc_IndentationError; + msg = "unindent does not match any outer indentation level"; + break; + case E_TOODEEP: + errtype = PyExc_IndentationError; + msg = "too many levels of indentation"; + break; + case E_DECODE: { /* XXX */ + PyThreadState* tstate = PyThreadState_Get(); + PyObject* value = tstate->curexc_value; + if (value != NULL) { + u = PyObject_Repr(value); + if (u != NULL) { + msg = PyString_AsString(u); + break; + } + } + } + default: + fprintf(stderr, "error=%d\n", err->error); + msg = "unknown parsing error"; + break; + } + w = Py_BuildValue("(sO)", msg, v); + Py_XDECREF(u); + Py_XDECREF(v); + PyErr_SetObject(errtype, w); + Py_XDECREF(w); +} + +/* Print fatal error message and abort */ + +void +Py_FatalError(const char *msg) +{ + char *crash = NULL; + fprintf(stderr, "Fatal Python error: %s\n", msg); +#ifdef MS_WINDOWS + OutputDebugString("Fatal Python error: "); + OutputDebugString(msg); + OutputDebugString("\n"); +#ifdef _DEBUG + DebugBreak(); +#endif +#endif /* MS_WINDOWS */ + // crash the game instead of aborting, which allows our exception handler to catch it + *crash = 1; + //abort(); +} + +/* Clean up and exit */ + +#ifdef WITH_THREAD +#include "pythread.h" +int _PyThread_Started = 0; /* Set by threadmodule.c and maybe others */ +#endif + +#define NEXITFUNCS 32 +static void (*exitfuncs[NEXITFUNCS])(void); +static int nexitfuncs = 0; + +int Py_AtExit(void (*func)(void)) +{ + if (nexitfuncs >= NEXITFUNCS) + return -1; + exitfuncs[nexitfuncs++] = func; + return 0; +} + +static void +call_sys_exitfunc(void) +{ + PyObject *exitfunc = PySys_GetObject("exitfunc"); + + if (exitfunc) { + PyObject *res; + Py_INCREF(exitfunc); + PySys_SetObject("exitfunc", (PyObject *)NULL); + res = PyEval_CallObject(exitfunc, (PyObject *)NULL); + if (res == NULL) { + if (!PyErr_ExceptionMatches(PyExc_SystemExit)) { + PySys_WriteStderr("Error in sys.exitfunc:\n"); + } + PyErr_Print(); + } + Py_DECREF(exitfunc); + } + + if (Py_FlushLine()) + PyErr_Clear(); +} + +static void +call_ll_exitfuncs(void) +{ + while (nexitfuncs > 0) + (*exitfuncs[--nexitfuncs])(); + + fflush(stdout); + fflush(stderr); +} + +void +Py_Exit(int sts) +{ + char* fatal = NULL; + + Py_Finalize(); + +#ifdef macintosh + PyMac_Exit(sts); +#else + //exit(sts); + + // Force crash instead of just terminating the process...this way + // we get a stack dump if something goes wrong + *fatal = 1; +#endif +} + +static void +initsigs(void) +{ +#ifdef HAVE_SIGNAL_H +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif +#ifdef SIGXFZ + signal(SIGXFZ, SIG_IGN); +#endif +#ifdef SIGXFSZ + signal(SIGXFSZ, SIG_IGN); +#endif +#endif /* HAVE_SIGNAL_H */ + PyOS_InitInterrupts(); /* May imply initsignal() */ +} + +#ifdef MPW + +/* Check for file descriptor connected to interactive device. + Pretend that stdin is always interactive, other files never. */ + +int +isatty(int fd) +{ + return fd == fileno(stdin); +} + +#endif + +/* + * The file descriptor fd is considered ``interactive'' if either + * a) isatty(fd) is TRUE, or + * b) the -i flag was given, and the filename associated with + * the descriptor is NULL or "" or "???". + */ +int +Py_FdIsInteractive(FILE *fp, const char *filename) +{ + if (isatty((int)fileno(fp))) + return 1; + if (!Py_InteractiveFlag) + return 0; + return (filename == NULL) || + (strcmp(filename, "") == 0) || + (strcmp(filename, "???") == 0); +} + + +#if defined(USE_STACKCHECK) +#if defined(WIN32) && defined(_MSC_VER) + +/* Stack checking for Microsoft C */ + +#include +#include + +/* + * Return non-zero when we run out of memory on the stack; zero otherwise. + */ +int +PyOS_CheckStack(void) +{ + __try { + /* alloca throws a stack overflow exception if there's + not enough space left on the stack */ + alloca(PYOS_STACK_MARGIN * sizeof(void*)); + return 0; + } __except (EXCEPTION_EXECUTE_HANDLER) { + /* just ignore all errors */ + } + return 1; +} + +#endif /* WIN32 && _MSC_VER */ + +/* Alternate implementations can be added here... */ + +#endif /* USE_STACKCHECK */ + + +/* Wrappers around sigaction() or signal(). */ + +PyOS_sighandler_t +PyOS_getsig(int sig) +{ +#ifdef MS_XBOX + /* Xbox blows up sometimes when signal is called. Until that gets figured out, + * just pretend every signal is invalid. + */ + return SIG_ERR; + +#else + +#ifdef HAVE_SIGACTION + struct sigaction context; + /* Initialize context.sa_handler to SIG_ERR which makes about as + * much sense as anything else. It should get overwritten if + * sigaction actually succeeds and otherwise we avoid an + * uninitialized memory read. + */ + context.sa_handler = SIG_ERR; + sigaction(sig, NULL, &context); + return context.sa_handler; +#else + PyOS_sighandler_t handler; + handler = signal(sig, SIG_IGN); + signal(sig, handler); + return handler; +#endif + +#endif +} + +PyOS_sighandler_t +PyOS_setsig(int sig, PyOS_sighandler_t handler) +{ +#ifdef HAVE_SIGACTION + struct sigaction context; + PyOS_sighandler_t oldhandler; + /* Initialize context.sa_handler to SIG_ERR which makes about as + * much sense as anything else. It should get overwritten if + * sigaction actually succeeds and otherwise we avoid an + * uninitialized memory read. + */ + context.sa_handler = SIG_ERR; + sigaction(sig, NULL, &context); + oldhandler = context.sa_handler; + context.sa_handler = handler; + sigaction(sig, &context, NULL); + return oldhandler; +#else + return signal(sig, handler); +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/sigcheck.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/sigcheck.c new file mode 100644 index 00000000..730cd2e2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/sigcheck.c @@ -0,0 +1,19 @@ + +/* Sigcheck is similar to intrcheck() but sets an exception when an + interrupt occurs. It can't be in the intrcheck.c file since that + file (and the whole directory it is in) doesn't know about objects + or exceptions. It can't be in errors.c because it can be + overridden (at link time) by a more powerful version implemented in + signalmodule.c. */ + +#include "Python.h" + +/* ARGSUSED */ +int +PyErr_CheckSignals(void) +{ + if (!PyOS_InterruptOccurred()) + return 0; + PyErr_SetNone(PyExc_KeyboardInterrupt); + return -1; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strdup.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strdup.c new file mode 100644 index 00000000..deef5342 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strdup.c @@ -0,0 +1,14 @@ +/* strdup() replacement (from stdwin, if you must know) */ + +#include "pgenheaders.h" + +char * +strdup(const char *str) +{ + if (str != NULL) { + register char *copy = malloc(strlen(str) + 1); + if (copy != NULL) + return strcpy(copy, str); + } + return NULL; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strerror.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strerror.c new file mode 100644 index 00000000..1365894b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strerror.c @@ -0,0 +1,24 @@ + +/* PD implementation of strerror() for systems that don't have it. + Author: Guido van Rossum, CWI Amsterdam, Oct. 1990, . */ + +#include +#include "Python.h" + +extern int sys_nerr; +extern char *sys_errlist[]; + +char * +strerror(int err) +{ + static char buf[20]; + if (err >= 0 && err < sys_nerr) + return sys_errlist[err]; + PyOS_snprintf(buf, sizeof(buf), "Unknown errno %d", err); + return buf; +} + +#ifdef macintosh +int sys_nerr = 0; +char *sys_errlist[1] = 0; +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strtod.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strtod.c new file mode 100644 index 00000000..d06e613a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/strtod.c @@ -0,0 +1,156 @@ +#include "pyconfig.h" + +/* comp.sources.misc strtod(), as posted in comp.lang.tcl, + with bugfix for "123000.0" and acceptance of space after 'e' sign nuked. + + ************************************************************ + * YOU MUST EDIT THE MACHINE-DEPENDENT DEFINITIONS BELOW!!! * + ************************************************************ +*/ + +/* File : stdtod.c (Modified version of str2dbl.c) + Author : Richard A. O'Keefe @ Quintus Computer Systems, Inc. + Updated: Tuesday August 2nd, 1988 + Defines: double strtod (char *str, char**ptr) +*/ + +/* This is an implementation of the strtod() function described in the + System V manuals, with a different name to avoid linker problems. + All that str2dbl() does itself is check that the argument is well-formed + and is in range. It leaves the work of conversion to atof(), which is + assumed to exist and deliver correct results (if they can be represented). + + There are two reasons why this should be provided to the net: + (a) some UNIX systems do not yet have strtod(), or do not have it + available in the BSD "universe" (but they do have atof()). + (b) some of the UNIX systems that *do* have it get it wrong. + (some crash with large arguments, some assign the wrong *ptr value). + There is a reason why *we* are providing it: we need a correct version + of strtod(), and if we give this one away maybe someone will look for + mistakes in it and fix them for us (:-). +*/ + +/* The following constants are machine-specific. MD{MIN,MAX}EXPT are + integers and MD{MIN,MAX}FRAC are strings such that + 0.${MDMAXFRAC}e${MDMAXEXPT} is the largest representable double, + 0.${MDMINFRAC}e${MDMINEXPT} is the smallest representable +ve double + MD{MIN,MAX}FRAC must not have any trailing zeros. + The values here are for IEEE-754 64-bit floats. + It is not perfectly clear to me whether an IEEE infinity should be + returned for overflow, nor what a portable way of writing one is, + so HUGE is just 0.MAXFRAC*10**MAXEXPT (this seems still to be the + UNIX convention). + + I do know about , but the whole point of this file is that + we can't always trust that stuff to be there or to be correct. +*/ +static int MDMINEXPT = {-323}; +static char MDMINFRAC[] = "494065645841246544"; +static double ZERO = 0.0; + +static int MDMAXEXPT = { 309}; +static char MDMAXFRAC[] = "17976931348623157"; +static double HUGE = 1.7976931348623157e308; + +extern double atof(const char *); /* Only called when result known to be ok */ + +#ifndef DONT_HAVE_ERRNO_H +#include +#endif +extern int errno; + +double strtod(char *str, char **ptr) +{ + int sign, scale, dotseen; + int esign, expt; + char *save; + register char *sp, *dp; + register int c; + char *buforg, *buflim; + char buffer[64]; /* 45-digit significant + */ + /* 13-digit exponent */ + sp = str; + while (*sp == ' ') sp++; + sign = 1; + if (*sp == '-') sign -= 2, sp++; + dotseen = 0, scale = 0; + dp = buffer; + *dp++ = '0'; *dp++ = '.'; + buforg = dp, buflim = buffer+48; + for (save = sp; c = *sp; sp++) + if (c == '.') { + if (dotseen) break; + dotseen++; + } else + if ((unsigned)(c-'0') > (unsigned)('9'-'0')) { + break; + } else + if (c == '0') { + if (dp != buforg) { + /* This is not the first digit, so we want to keep it */ + if (dp < buflim) *dp++ = c; + if (!dotseen) scale++; + } else { + /* No non-zero digits seen yet */ + /* If a . has been seen, scale must be adjusted */ + if (dotseen) scale--; + } + } else { + /* This is a nonzero digit, so we want to keep it */ + if (dp < buflim) *dp++ = c; + /* If it precedes a ., scale must be adjusted */ + if (!dotseen) scale++; + } + if (sp == save) { + if (ptr) *ptr = str; + errno = EDOM; /* what should this be? */ + return ZERO; + } + + while (dp > buforg && dp[-1] == '0') --dp; + if (dp == buforg) *dp++ = '0'; + *dp = '\0'; + /* Now the contents of buffer are + +--+--------+-+--------+ + |0.|fraction|\|leftover| + +--+--------+-+--------+ + ^dp points here + where fraction begins with 0 iff it is "0", and has at most + 45 digits in it, and leftover is at least 16 characters. + */ + save = sp, expt = 0, esign = 1; + do { + c = *sp++; + if (c != 'e' && c != 'E') break; + c = *sp++; + if (c == '-') esign -= 2, c = *sp++; else + if (c == '+' /* || c == ' ' */ ) c = *sp++; + if ((unsigned)(c-'0') > (unsigned)('9'-'0')) break; + while (c == '0') c = *sp++; + for (; (unsigned)(c-'0') <= (unsigned)('9'-'0'); c = *sp++) + expt = expt*10 + c-'0'; + if (esign < 0) expt = -expt; + save = sp-1; + } while (0); + if (ptr) *ptr = save; + expt += scale; + /* Now the number is sign*0.fraction*10**expt */ + errno = ERANGE; + if (expt > MDMAXEXPT) { + return HUGE*sign; + } else + if (expt == MDMAXEXPT) { + if (strcmp(buforg, MDMAXFRAC) > 0) return HUGE*sign; + } else + if (expt < MDMINEXPT) { + return ZERO*sign; + } else + if (expt == MDMINEXPT) { + if (strcmp(buforg, MDMINFRAC) < 0) return ZERO*sign; + } + /* We have now established that the number can be */ + /* represented without overflow or underflow */ + (void) sprintf(dp, "E%d", expt); + errno = 0; + return atof(buffer)*sign; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/structmember.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/structmember.c new file mode 100644 index 00000000..a3c6dc0f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/structmember.c @@ -0,0 +1,276 @@ + +/* Map C struct members to Python object attributes */ + +#include "Python.h" + +#include "structmember.h" + +static PyObject * +listmembers(struct memberlist *mlist) +{ + int i, n; + PyObject *v; + for (n = 0; mlist[n].name != NULL; n++) + ; + v = PyList_New(n); + if (v != NULL) { + for (i = 0; i < n; i++) + PyList_SetItem(v, i, + PyString_FromString(mlist[i].name)); + if (PyErr_Occurred()) { + Py_DECREF(v); + v = NULL; + } + else { + PyList_Sort(v); + } + } + return v; +} + +PyObject * +PyMember_Get(char *addr, struct memberlist *mlist, char *name) +{ + struct memberlist *l; + + if (strcmp(name, "__members__") == 0) + return listmembers(mlist); + for (l = mlist; l->name != NULL; l++) { + if (strcmp(l->name, name) == 0) { + PyMemberDef copy; + copy.name = l->name; + copy.type = l->type; + copy.offset = l->offset; + copy.flags = l->flags; + copy.doc = NULL; + return PyMember_GetOne(addr, ©); + } + } + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +PyObject * +PyMember_GetOne(char *addr, PyMemberDef *l) +{ + PyObject *v; + if ((l->flags & READ_RESTRICTED) && + PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, "restricted attribute"); + return NULL; + } + addr += l->offset; + switch (l->type) { + case T_BYTE: + v = PyInt_FromLong( + (long) (((*(char*)addr & 0xff) ^ 0x80) - 0x80)); + break; + case T_UBYTE: + v = PyInt_FromLong((long) *(char*)addr & 0xff); + break; + case T_SHORT: + v = PyInt_FromLong((long) *(short*)addr); + break; + case T_USHORT: + v = PyInt_FromLong((long) *(unsigned short*)addr); + break; + case T_INT: + v = PyInt_FromLong((long) *(int*)addr); + break; + case T_UINT: + v = PyInt_FromLong((long) *(unsigned int*)addr); + break; + case T_LONG: + v = PyInt_FromLong(*(long*)addr); + break; + case T_ULONG: + v = PyLong_FromDouble((double) *(unsigned long*)addr); + break; + case T_FLOAT: + v = PyFloat_FromDouble((double)*(float*)addr); + break; + case T_DOUBLE: + v = PyFloat_FromDouble(*(double*)addr); + break; + case T_STRING: + if (*(char**)addr == NULL) { + Py_INCREF(Py_None); + v = Py_None; + } + else + v = PyString_FromString(*(char**)addr); + break; + case T_STRING_INPLACE: + v = PyString_FromString((char*)addr); + break; +#ifdef macintosh + case T_PSTRING: + if (*(char**)addr == NULL) { + Py_INCREF(Py_None); + v = Py_None; + } + else + v = PyString_FromStringAndSize( + (*(char**)addr)+1, + **(unsigned char**)addr); + break; + case T_PSTRING_INPLACE: + v = PyString_FromStringAndSize( + ((char*)addr)+1, + *(unsigned char*)addr); + break; +#endif /* macintosh */ + case T_CHAR: + v = PyString_FromStringAndSize((char*)addr, 1); + break; + case T_OBJECT: + v = *(PyObject **)addr; + if (v == NULL) + v = Py_None; + Py_INCREF(v); + break; + case T_OBJECT_EX: + v = *(PyObject **)addr; + if (v == NULL) + PyErr_SetString(PyExc_AttributeError, l->name); + Py_XINCREF(v); + break; + default: + PyErr_SetString(PyExc_SystemError, "bad memberdescr type"); + v = NULL; + } + return v; +} + +int +PyMember_Set(char *addr, struct memberlist *mlist, char *name, PyObject *v) +{ + struct memberlist *l; + + for (l = mlist; l->name != NULL; l++) { + if (strcmp(l->name, name) == 0) { + PyMemberDef copy; + copy.name = l->name; + copy.type = l->type; + copy.offset = l->offset; + copy.flags = l->flags; + copy.doc = NULL; + return PyMember_SetOne(addr, ©, v); + } + } + + PyErr_SetString(PyExc_AttributeError, name); + return -1; +} + +int +PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) +{ + PyObject *oldv; + + if ((l->flags & READONLY) || l->type == T_STRING +#ifdef macintosh + || l->type == T_PSTRING +#endif + ) + { + PyErr_SetString(PyExc_TypeError, "readonly attribute"); + return -1; + } + if ((l->flags & WRITE_RESTRICTED) && PyEval_GetRestricted()) { + PyErr_SetString(PyExc_RuntimeError, "restricted attribute"); + return -1; + } + if (v == NULL && l->type != T_OBJECT_EX && l->type != T_OBJECT) { + PyErr_SetString(PyExc_TypeError, + "can't delete numeric/char attribute"); + return -1; + } + addr += l->offset; + switch (l->type) { + case T_BYTE: + case T_UBYTE: + if (!PyInt_Check(v)) { + PyErr_BadArgument(); + return -1; + } + *(char*)addr = (char) PyInt_AsLong(v); + break; + case T_SHORT: + case T_USHORT: + if (!PyInt_Check(v)) { + PyErr_BadArgument(); + return -1; + } + *(short*)addr = (short) PyInt_AsLong(v); + break; + case T_UINT: + case T_INT: + if (!PyInt_Check(v)) { + PyErr_BadArgument(); + return -1; + } + *(int*)addr = (int) PyInt_AsLong(v); + break; + case T_LONG: + if (!PyInt_Check(v)) { + PyErr_BadArgument(); + return -1; + } + *(long*)addr = PyInt_AsLong(v); + break; + case T_ULONG: + if (PyInt_Check(v)) + *(long*)addr = PyInt_AsLong(v); + else if (PyLong_Check(v)) + *(long*)addr = PyLong_AsLong(v); + else { + PyErr_BadArgument(); + return -1; + } + break; + case T_FLOAT: + if (PyInt_Check(v)) + *(float*)addr = + (float) PyInt_AsLong(v); + else if (PyFloat_Check(v)) + *(float*)addr = + (float) PyFloat_AsDouble(v); + else { + PyErr_BadArgument(); + return -1; + } + break; + case T_DOUBLE: + if (PyInt_Check(v)) + *(double*)addr = (double) PyInt_AsLong(v); + else if (PyFloat_Check(v)) + *(double*)addr = PyFloat_AsDouble(v); + else { + PyErr_BadArgument(); + return -1; + } + break; + case T_OBJECT: + case T_OBJECT_EX: + Py_XINCREF(v); + oldv = *(PyObject **)addr; + *(PyObject **)addr = v; + Py_XDECREF(oldv); + break; + case T_CHAR: + if (PyString_Check(v) && PyString_Size(v) == 1) { + *(char*)addr = PyString_AsString(v)[0]; + } + else { + PyErr_BadArgument(); + return -1; + } + break; + default: + PyErr_Format(PyExc_SystemError, + "bad memberdescr type for %s", l->name); + return -1; + } + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/symtable.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/symtable.c new file mode 100644 index 00000000..65cf7987 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/symtable.c @@ -0,0 +1,180 @@ +#include "Python.h" +#include "compile.h" +#include "symtable.h" +#include "graminit.h" +#include "structmember.h" + +/* The compiler uses this function to load a PySymtableEntry object + for a code block. Each block is loaded twice, once during the + symbol table pass and once during the code gen pass. Entries + created during the first pass are cached for the second pass, using + the st_symbols dictionary. + + The cache is keyed by st_nscopes. Each code block node in a + module's parse tree can be assigned a unique id based on the order + in which the nodes are visited by the compiler. This strategy + works so long as the symbol table and codegen passes visit the same + nodes in the same order. +*/ + + +PyObject * +PySymtableEntry_New(struct symtable *st, char *name, int type, int lineno) +{ + PySymtableEntryObject *ste = NULL; + PyObject *k, *v; + + k = PyInt_FromLong(st->st_nscopes++); + if (k == NULL) + goto fail; + v = PyDict_GetItem(st->st_symbols, k); + if (v) { + Py_DECREF(k); + Py_INCREF(v); + return v; + } + + ste = (PySymtableEntryObject *)PyObject_New(PySymtableEntryObject, + &PySymtableEntry_Type); + ste->ste_table = st; + ste->ste_id = k; + + v = PyString_FromString(name); + if (v == NULL) + goto fail; + ste->ste_name = v; + + v = PyDict_New(); + if (v == NULL) + goto fail; + ste->ste_symbols = v; + + v = PyList_New(0); + if (v == NULL) + goto fail; + ste->ste_varnames = v; + + v = PyList_New(0); + if (v == NULL) + goto fail; + ste->ste_children = v; + + ste->ste_optimized = 0; + ste->ste_opt_lineno = 0; + ste->ste_tmpname = 0; + ste->ste_lineno = lineno; + switch (type) { + case funcdef: + case lambdef: + ste->ste_type = TYPE_FUNCTION; + break; + case classdef: + ste->ste_type = TYPE_CLASS; + break; + case single_input: + case eval_input: + case file_input: + ste->ste_type = TYPE_MODULE; + break; + } + + if (st->st_cur == NULL) + ste->ste_nested = 0; + else if (st->st_cur->ste_nested + || st->st_cur->ste_type == TYPE_FUNCTION) + ste->ste_nested = 1; + else + ste->ste_nested = 0; + ste->ste_child_free = 0; + ste->ste_generator = 0; + + if (PyDict_SetItem(st->st_symbols, ste->ste_id, (PyObject *)ste) < 0) + goto fail; + + return (PyObject *)ste; + fail: + Py_XDECREF(ste); + return NULL; +} + +static PyObject * +ste_repr(PySymtableEntryObject *ste) +{ + char buf[256]; + + PyOS_snprintf(buf, sizeof(buf), + "", + PyString_AS_STRING(ste->ste_name), + PyInt_AS_LONG(ste->ste_id), + ste->ste_lineno); + return PyString_FromString(buf); +} + +static void +ste_dealloc(PySymtableEntryObject *ste) +{ + ste->ste_table = NULL; + Py_XDECREF(ste->ste_id); + Py_XDECREF(ste->ste_name); + Py_XDECREF(ste->ste_symbols); + Py_XDECREF(ste->ste_varnames); + Py_XDECREF(ste->ste_children); + PyObject_Del(ste); +} + +#define OFF(x) offsetof(PySymtableEntryObject, x) + +static PyMemberDef ste_memberlist[] = { + {"id", T_OBJECT, OFF(ste_id), READONLY}, + {"name", T_OBJECT, OFF(ste_name), READONLY}, + {"symbols", T_OBJECT, OFF(ste_symbols), READONLY}, + {"varnames", T_OBJECT, OFF(ste_varnames), READONLY}, + {"children", T_OBJECT, OFF(ste_children), READONLY}, + {"type", T_INT, OFF(ste_type), READONLY}, + {"lineno", T_INT, OFF(ste_lineno), READONLY}, + {"optimized",T_INT, OFF(ste_optimized), READONLY}, + {"nested", T_INT, OFF(ste_nested), READONLY}, + {NULL} +}; + +PyTypeObject PySymtableEntry_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "symtable entry", + sizeof(PySymtableEntryObject), + 0, + (destructor)ste_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)ste_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + ste_memberlist, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/sysmodule.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/sysmodule.c new file mode 100644 index 00000000..5e0e7b31 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/sysmodule.c @@ -0,0 +1,1298 @@ + +/* System module */ + +/* +Various bits of information used by the interpreter are collected in +module 'sys'. +Function member: +- exit(sts): raise SystemExit +Data members: +- stdin, stdout, stderr: standard file objects +- modules: the table of modules (dictionary) +- path: module search path (list of strings) +- argv: script arguments (list of strings) +- ps1, ps2: optional primary and secondary prompts (strings) +*/ + +#include "Python.h" +#include "compile.h" +#include "frameobject.h" +#include "eval.h" + +#include "osdefs.h" + +#ifdef MS_WINDOWS +#ifdef MS_XBOX +#include +#else +#define WIN32_LEAN_AND_MEAN +#include +#endif +#endif + +#ifdef MS_COREDLL +extern void *PyWin_DLLhModule; +/* A string loaded from the DLL at startup: */ +extern const char *PyWin_DLLVersionString; +#endif + +#ifdef __VMS +#include +#endif + +#ifdef HAVE_LANGINFO_H +#include +#include +#endif + +PyObject * +PySys_GetObject(char *name) +{ + PyThreadState *tstate = PyThreadState_Get(); + PyObject *sd = tstate->interp->sysdict; + if (sd == NULL) + return NULL; + return PyDict_GetItemString(sd, name); +} + +FILE * +PySys_GetFile(char *name, FILE *def) +{ + FILE *fp = NULL; + PyObject *v = PySys_GetObject(name); + if (v != NULL && PyFile_Check(v)) + fp = PyFile_AsFile(v); + if (fp == NULL) + fp = def; + return fp; +} + +int +PySys_SetObject(char *name, PyObject *v) +{ + PyThreadState *tstate = PyThreadState_Get(); + PyObject *sd = tstate->interp->sysdict; + if (v == NULL) { + if (PyDict_GetItemString(sd, name) == NULL) + return 0; + else + return PyDict_DelItemString(sd, name); + } + else + return PyDict_SetItemString(sd, name, v); +} + +static PyObject * +sys_displayhook(PyObject *self, PyObject *o) +{ + PyObject *outf; + PyInterpreterState *interp = PyThreadState_Get()->interp; + PyObject *modules = interp->modules; + PyObject *builtins = PyDict_GetItemString(modules, "__builtin__"); + + if (builtins == NULL) { + PyErr_SetString(PyExc_RuntimeError, "lost __builtin__"); + return NULL; + } + + /* Print value except if None */ + /* After printing, also assign to '_' */ + /* Before, set '_' to None to avoid recursion */ + if (o == Py_None) { + Py_INCREF(Py_None); + return Py_None; + } + if (PyObject_SetAttrString(builtins, "_", Py_None) != 0) + return NULL; + if (Py_FlushLine() != 0) + return NULL; + outf = PySys_GetObject("stdout"); + if (outf == NULL) { + PyErr_SetString(PyExc_RuntimeError, "lost sys.stdout"); + return NULL; + } + if (PyFile_WriteObject(o, outf, 0) != 0) + return NULL; + PyFile_SoftSpace(outf, 1); + if (Py_FlushLine() != 0) + return NULL; + if (PyObject_SetAttrString(builtins, "_", o) != 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(displayhook_doc, +"displayhook(object) -> None\n" +"\n" +"Print an object to sys.stdout and also save it in __builtin__._\n" +); + +static PyObject * +sys_excepthook(PyObject* self, PyObject* args) +{ + PyObject *exc, *value, *tb; + if (!PyArg_UnpackTuple(args, "excepthook", 3, 3, &exc, &value, &tb)) + return NULL; + PyErr_Display(exc, value, tb); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(excepthook_doc, +"excepthook(exctype, value, traceback) -> None\n" +"\n" +"Handle an exception by displaying it with a traceback on sys.stderr.\n" +); + +static PyObject * +sys_exc_info(PyObject *self, PyObject *noargs) +{ + PyThreadState *tstate; + tstate = PyThreadState_Get(); + return Py_BuildValue( + "(OOO)", + tstate->exc_type != NULL ? tstate->exc_type : Py_None, + tstate->exc_value != NULL ? tstate->exc_value : Py_None, + tstate->exc_traceback != NULL ? + tstate->exc_traceback : Py_None); +} + +PyDoc_STRVAR(exc_info_doc, +"exc_info() -> (type, value, traceback)\n\ +\n\ +Return information about the most recent exception caught by an except\n\ +clause in the current stack frame or in an older stack frame." +); + +static PyObject * +sys_exc_clear(PyObject *self, PyObject *noargs) +{ + PyThreadState *tstate = PyThreadState_Get(); + PyObject *tmp_type, *tmp_value, *tmp_tb; + tmp_type = tstate->exc_type; + tmp_value = tstate->exc_value; + tmp_tb = tstate->exc_traceback; + tstate->exc_type = NULL; + tstate->exc_value = NULL; + tstate->exc_traceback = NULL; + Py_XDECREF(tmp_type); + Py_XDECREF(tmp_value); + Py_XDECREF(tmp_tb); + /* For b/w compatibility */ + PySys_SetObject("exc_type", Py_None); + PySys_SetObject("exc_value", Py_None); + PySys_SetObject("exc_traceback", Py_None); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(exc_clear_doc, +"exc_clear() -> None\n\ +\n\ +Clear global information on the current exception. Subsequent calls to\n\ +exc_info() will return (None,None,None) until another exception is raised\n\ +in the current thread or the execution stack returns to a frame where\n\ +another exception is being handled." +); + +static PyObject * +sys_exit(PyObject *self, PyObject *args) +{ + PyObject *exit_code = 0; + if (!PyArg_ParseTuple(args, "|O:exit", &exit_code)) + return NULL; + /* Raise SystemExit so callers may catch it or clean up. */ + PyErr_SetObject(PyExc_SystemExit, exit_code); + return NULL; +} + +PyDoc_STRVAR(exit_doc, +"exit([status])\n\ +\n\ +Exit the interpreter by raising SystemExit(status).\n\ +If the status is omitted or None, it defaults to zero (i.e., success).\n\ +If the status is numeric, it will be used as the system exit status.\n\ +If it is another kind of object, it will be printed and the system\n\ +exit status will be one (i.e., failure)." +); + +#ifdef Py_USING_UNICODE + +static PyObject * +sys_getdefaultencoding(PyObject *self) +{ + return PyString_FromString(PyUnicode_GetDefaultEncoding()); +} + +PyDoc_STRVAR(getdefaultencoding_doc, +"getdefaultencoding() -> string\n\ +\n\ +Return the current default string encoding used by the Unicode \n\ +implementation." +); + +static PyObject * +sys_setdefaultencoding(PyObject *self, PyObject *args) +{ + char *encoding; + if (!PyArg_ParseTuple(args, "s:setdefaultencoding", &encoding)) + return NULL; + if (PyUnicode_SetDefaultEncoding(encoding)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(setdefaultencoding_doc, +"setdefaultencoding(encoding)\n\ +\n\ +Set the current default string encoding used by the Unicode implementation." +); + +static PyObject * +sys_getfilesystemencoding(PyObject *self) +{ + if (Py_FileSystemDefaultEncoding) + return PyString_FromString(Py_FileSystemDefaultEncoding); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(getfilesystemencoding_doc, +"getfilesystemencoding() -> string\n\ +\n\ +Return the encoding used to convert Unicode filenames in\n\ +operating system filenames." +); + +#endif + +/* + * Cached interned string objects used for calling the profile and + * trace functions. Initialized by trace_init(). + */ +static PyObject *whatstrings[4] = {NULL, NULL, NULL, NULL}; + +static int +trace_init(void) +{ + static char *whatnames[4] = {"call", "exception", "line", "return"}; + PyObject *name; + int i; + for (i = 0; i < 4; ++i) { + if (whatstrings[i] == NULL) { + name = PyString_InternFromString(whatnames[i]); + if (name == NULL) + return -1; + whatstrings[i] = name; + } + } + return 0; +} + + +static PyObject * +call_trampoline(PyThreadState *tstate, PyObject* callback, + PyFrameObject *frame, int what, PyObject *arg) +{ + PyObject *args = PyTuple_New(3); + PyObject *whatstr; + PyObject *result; + + if (args == NULL) + return NULL; + Py_INCREF(frame); + whatstr = whatstrings[what]; + Py_INCREF(whatstr); + if (arg == NULL) + arg = Py_None; + Py_INCREF(arg); + PyTuple_SET_ITEM(args, 0, (PyObject *)frame); + PyTuple_SET_ITEM(args, 1, whatstr); + PyTuple_SET_ITEM(args, 2, arg); + + /* call the Python-level function */ + PyFrame_FastToLocals(frame); + result = PyEval_CallObject(callback, args); + PyFrame_LocalsToFast(frame, 1); + if (result == NULL) + PyTraceBack_Here(frame); + + /* cleanup */ + Py_DECREF(args); + return result; +} + +static int +profile_trampoline(PyObject *self, PyFrameObject *frame, + int what, PyObject *arg) +{ + PyThreadState *tstate = frame->f_tstate; + PyObject *result; + + if (arg == NULL) + arg = Py_None; + result = call_trampoline(tstate, self, frame, what, arg); + if (result == NULL) { + PyEval_SetProfile(NULL, NULL); + return -1; + } + Py_DECREF(result); + return 0; +} + +static int +trace_trampoline(PyObject *self, PyFrameObject *frame, + int what, PyObject *arg) +{ + PyThreadState *tstate = frame->f_tstate; + PyObject *callback; + PyObject *result; + + if (what == PyTrace_CALL) + callback = self; + else + callback = frame->f_trace; + if (callback == NULL) + return 0; + result = call_trampoline(tstate, callback, frame, what, arg); + if (result == NULL) { + PyEval_SetTrace(NULL, NULL); + Py_XDECREF(frame->f_trace); + frame->f_trace = NULL; + return -1; + } + if (result != Py_None) { + PyObject *temp = frame->f_trace; + frame->f_trace = NULL; + Py_XDECREF(temp); + frame->f_trace = result; + } + else { + Py_DECREF(result); + } + return 0; +} + +static PyObject * +sys_settrace(PyObject *self, PyObject *args) +{ + if (trace_init() == -1) + return NULL; + if (args == Py_None) + PyEval_SetTrace(NULL, NULL); + else + PyEval_SetTrace(trace_trampoline, args); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(settrace_doc, +"settrace(function)\n\ +\n\ +Set the global debug tracing function. It will be called on each\n\ +function call. See the debugger chapter in the library manual." +); + +static PyObject * +sys_setprofile(PyObject *self, PyObject *args) +{ + if (trace_init() == -1) + return NULL; + if (args == Py_None) + PyEval_SetProfile(NULL, NULL); + else + PyEval_SetProfile(profile_trampoline, args); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(setprofile_doc, +"setprofile(function)\n\ +\n\ +Set the profiling function. It will be called on each function call\n\ +and return. See the profiler chapter in the library manual." +); + +static PyObject * +sys_setcheckinterval(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "i:setcheckinterval", &_Py_CheckInterval)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(setcheckinterval_doc, +"setcheckinterval(n)\n\ +\n\ +Tell the Python interpreter to check for asynchronous events every\n\ +n instructions. This also affects how often thread switches occur." +); + +static PyObject * +sys_getcheckinterval(PyObject *self, PyObject *args) +{ + return PyInt_FromLong(_Py_CheckInterval); +} + +PyDoc_STRVAR(getcheckinterval_doc, +"getcheckinterval() -> current check interval; see setcheckinterval()." +); + +static PyObject * +sys_setrecursionlimit(PyObject *self, PyObject *args) +{ + int new_limit; + if (!PyArg_ParseTuple(args, "i:setrecursionlimit", &new_limit)) + return NULL; + if (new_limit <= 0) { + PyErr_SetString(PyExc_ValueError, + "recursion limit must be positive"); + return NULL; + } + Py_SetRecursionLimit(new_limit); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(setrecursionlimit_doc, +"setrecursionlimit(n)\n\ +\n\ +Set the maximum depth of the Python interpreter stack to n. This\n\ +limit prevents infinite recursion from causing an overflow of the C\n\ +stack and crashing Python. The highest possible limit is platform-\n\ +dependent." +); + +static PyObject * +sys_getrecursionlimit(PyObject *self) +{ + return PyInt_FromLong(Py_GetRecursionLimit()); +} + +PyDoc_STRVAR(getrecursionlimit_doc, +"getrecursionlimit()\n\ +\n\ +Return the current value of the recursion limit, the maximum depth\n\ +of the Python interpreter stack. This limit prevents infinite\n\ +recursion from causing an overflow of the C stack and crashing Python." +); + +#ifdef MS_WINDOWS +PyDoc_STRVAR(getwindowsversion_doc, +"getwindowsversion()\n\ +\n\ +Return information about the running version of Windows.\n\ +The result is a tuple of (major, minor, build, platform, text)\n\ +All elements are numbers, except text which is a string.\n\ +Platform may be 0 for win32s, 1 for Windows 9x/ME, 2 for Windows NT/2000/XP\n\ +" +); + +static PyObject * +sys_getwindowsversion(PyObject *self) +{ +#ifdef MS_XBOX + return PyErr_SetFromWindowsErr(0); +#else + OSVERSIONINFO ver; + ver.dwOSVersionInfoSize = sizeof(ver); + if (!GetVersionEx(&ver)) + return PyErr_SetFromWindowsErr(0); + return Py_BuildValue("HHHHs", + ver.dwMajorVersion, + ver.dwMinorVersion, + ver.dwBuildNumber, + ver.dwPlatformId, + ver.szCSDVersion); +#endif +} + +#endif /* MS_WINDOWS */ + +#ifdef HAVE_DLOPEN +static PyObject * +sys_setdlopenflags(PyObject *self, PyObject *args) +{ + int new_val; + PyThreadState *tstate = PyThreadState_Get(); + if (!PyArg_ParseTuple(args, "i:setdlopenflags", &new_val)) + return NULL; + if (!tstate) + return NULL; + tstate->interp->dlopenflags = new_val; + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(setdlopenflags_doc, +"setdlopenflags(n) -> None\n\ +\n\ +Set the flags that will be used for dlopen() calls. Among other\n\ +things, this will enable a lazy resolving of symbols when importing\n\ +a module, if called as sys.setdlopenflags(0)\n\ +To share symbols across extension modules, call as\n\ +sys.setdlopenflags(dl.RTLD_NOW|dl.RTLD_GLOBAL)" +); + +static PyObject * +sys_getdlopenflags(PyObject *self, PyObject *args) +{ + PyThreadState *tstate = PyThreadState_Get(); + if (!tstate) + return NULL; + return PyInt_FromLong(tstate->interp->dlopenflags); +} + +PyDoc_STRVAR(getdlopenflags_doc, +"getdlopenflags() -> int\n\ +\n\ +Return the current value of the flags that are used for dlopen()\n\ +calls. The flag constants are defined in the dl module." +); +#endif + +#ifdef USE_MALLOPT +/* Link with -lmalloc (or -lmpc) on an SGI */ +#include + +static PyObject * +sys_mdebug(PyObject *self, PyObject *args) +{ + int flag; + if (!PyArg_ParseTuple(args, "i:mdebug", &flag)) + return NULL; + mallopt(M_DEBUG, flag); + Py_INCREF(Py_None); + return Py_None; +} +#endif /* USE_MALLOPT */ + +static PyObject * +sys_getrefcount(PyObject *self, PyObject *arg) +{ + return PyInt_FromLong(arg->ob_refcnt); +} + +#ifdef Py_REF_DEBUG +static PyObject * +sys_gettotalrefcount(PyObject *self) +{ + return PyInt_FromLong(_Py_RefTotal); +} + +#endif /* Py_TRACE_REFS */ + +PyDoc_STRVAR(getrefcount_doc, +"getrefcount(object) -> integer\n\ +\n\ +Return the reference count of object. The count returned is generally\n\ +one higher than you might expect, because it includes the (temporary)\n\ +reference as an argument to getrefcount()." +); + +#ifdef COUNT_ALLOCS +static PyObject * +sys_getcounts(PyObject *self) +{ + extern PyObject *get_counts(void); + + return get_counts(); +} +#endif + +PyDoc_STRVAR(getframe_doc, +"_getframe([depth]) -> frameobject\n\ +\n\ +Return a frame object from the call stack. If optional integer depth is\n\ +given, return the frame object that many calls below the top of the stack.\n\ +If that is deeper than the call stack, ValueError is raised. The default\n\ +for depth is zero, returning the frame at the top of the call stack.\n\ +\n\ +This function should be used for internal and specialized\n\ +purposes only." +); + +static PyObject * +sys_getframe(PyObject *self, PyObject *args) +{ + PyFrameObject *f = PyThreadState_Get()->frame; + int depth = -1; + + if (!PyArg_ParseTuple(args, "|i:_getframe", &depth)) + return NULL; + + while (depth > 0 && f != NULL) { + f = f->f_back; + --depth; + } + if (f == NULL) { + PyErr_SetString(PyExc_ValueError, + "call stack is not deep enough"); + return NULL; + } + Py_INCREF(f); + return (PyObject*)f; +} + +PyDoc_STRVAR(call_tracing_doc, +"call_tracing(func, args) -> object\n\ +\n\ +Call func(*args), while tracing is enabled. The tracing state is\n\ +saved, and restored afterwards. This is intended to be called from\n\ +a debugger from a checkpoint, to recursively debug some other code." +); + +static PyObject * +sys_call_tracing(PyObject *self, PyObject *args) +{ + PyObject *func, *funcargs; + if (!PyArg_ParseTuple(args, "OO:call_tracing", &func, &funcargs)) + return NULL; + return _PyEval_CallTracing(func, funcargs); +} + +PyDoc_STRVAR(callstats_doc, +"callstats() -> tuple of integers\n\ +\n\ +Return a tuple of function call statistics, if CALL_PROFILE was defined\n\ +when Python was built. Otherwise, return None.\n\ +\n\ +When enabled, this function returns detailed, implementation-specific\n\ +details about the number of function calls executed. The return value is\n\ +a 11-tuple where the entries in the tuple are counts of:\n\ +0. all function calls\n\ +1. calls to PyFunction_Type objects\n\ +2. PyFunction calls that do not create an argument tuple\n\ +3. PyFunction calls that do not create an argument tuple\n\ + and bypass PyEval_EvalCodeEx()\n\ +4. PyMethod calls\n\ +5. PyMethod calls on bound methods\n\ +6. PyType calls\n\ +7. PyCFunction calls\n\ +8. generator calls\n\ +9. All other calls\n\ +10. Number of stack pops performed by call_function()" +); + +#ifdef Py_TRACE_REFS +/* Defined in objects.c because it uses static globals if that file */ +extern PyObject *_Py_GetObjects(PyObject *, PyObject *); +#endif + +#ifdef DYNAMIC_EXECUTION_PROFILE +/* Defined in ceval.c because it uses static globals if that file */ +extern PyObject *_Py_GetDXProfile(PyObject *, PyObject *); +#endif + +static PyMethodDef sys_methods[] = { + /* Might as well keep this in alphabetic order */ + {"callstats", (PyCFunction)PyEval_GetCallStats, METH_NOARGS, + callstats_doc}, + {"displayhook", sys_displayhook, METH_O, displayhook_doc}, + {"exc_info", sys_exc_info, METH_NOARGS, exc_info_doc}, + {"exc_clear", sys_exc_clear, METH_NOARGS, exc_clear_doc}, + {"excepthook", sys_excepthook, METH_VARARGS, excepthook_doc}, + {"exit", sys_exit, METH_VARARGS, exit_doc}, +#ifdef Py_USING_UNICODE + {"getdefaultencoding", (PyCFunction)sys_getdefaultencoding, + METH_NOARGS, getdefaultencoding_doc}, +#endif +#ifdef HAVE_DLOPEN + {"getdlopenflags", (PyCFunction)sys_getdlopenflags, METH_NOARGS, + getdlopenflags_doc}, +#endif +#ifdef COUNT_ALLOCS + {"getcounts", (PyCFunction)sys_getcounts, METH_NOARGS}, +#endif +#ifdef DYNAMIC_EXECUTION_PROFILE + {"getdxp", _Py_GetDXProfile, METH_VARARGS}, +#endif +#ifdef Py_USING_UNICODE + {"getfilesystemencoding", (PyCFunction)sys_getfilesystemencoding, + METH_NOARGS, getfilesystemencoding_doc}, +#endif +#ifdef Py_TRACE_REFS + {"getobjects", _Py_GetObjects, METH_VARARGS}, +#endif +#ifdef Py_REF_DEBUG + {"gettotalrefcount", (PyCFunction)sys_gettotalrefcount, METH_NOARGS}, +#endif + {"getrefcount", (PyCFunction)sys_getrefcount, METH_O, getrefcount_doc}, + {"getrecursionlimit", (PyCFunction)sys_getrecursionlimit, METH_NOARGS, + getrecursionlimit_doc}, + {"_getframe", sys_getframe, METH_VARARGS, getframe_doc}, +#ifdef MS_WINDOWS + {"getwindowsversion", (PyCFunction)sys_getwindowsversion, METH_NOARGS, + getwindowsversion_doc}, +#endif /* MS_WINDOWS */ +#ifdef USE_MALLOPT + {"mdebug", sys_mdebug, METH_VARARGS}, +#endif +#ifdef Py_USING_UNICODE + {"setdefaultencoding", sys_setdefaultencoding, METH_VARARGS, + setdefaultencoding_doc}, +#endif + {"setcheckinterval", sys_setcheckinterval, METH_VARARGS, + setcheckinterval_doc}, + {"getcheckinterval", sys_getcheckinterval, METH_NOARGS, + getcheckinterval_doc}, +#ifdef HAVE_DLOPEN + {"setdlopenflags", sys_setdlopenflags, METH_VARARGS, + setdlopenflags_doc}, +#endif + {"setprofile", sys_setprofile, METH_O, setprofile_doc}, + {"setrecursionlimit", sys_setrecursionlimit, METH_VARARGS, + setrecursionlimit_doc}, + {"settrace", sys_settrace, METH_O, settrace_doc}, + {"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +list_builtin_module_names(void) +{ + PyObject *list = PyList_New(0); + int i; + if (list == NULL) + return NULL; + for (i = 0; PyImport_Inittab[i].name != NULL; i++) { + PyObject *name = PyString_FromString( + PyImport_Inittab[i].name); + if (name == NULL) + break; + PyList_Append(list, name); + Py_DECREF(name); + } + if (PyList_Sort(list) != 0) { + Py_DECREF(list); + list = NULL; + } + if (list) { + PyObject *v = PyList_AsTuple(list); + Py_DECREF(list); + list = v; + } + return list; +} + +static PyObject *warnoptions = NULL; + +void +PySys_ResetWarnOptions(void) +{ + if (warnoptions == NULL || !PyList_Check(warnoptions)) + return; + PyList_SetSlice(warnoptions, 0, PyList_GET_SIZE(warnoptions), NULL); +} + +void +PySys_AddWarnOption(char *s) +{ + PyObject *str; + + if (warnoptions == NULL || !PyList_Check(warnoptions)) { + Py_XDECREF(warnoptions); + warnoptions = PyList_New(0); + if (warnoptions == NULL) + return; + } + str = PyString_FromString(s); + if (str != NULL) { + PyList_Append(warnoptions, str); + Py_DECREF(str); + } +} + +/* XXX This doc string is too long to be a single string literal in VC++ 5.0. + Two literals concatenated works just fine. If you have a K&R compiler + or other abomination that however *does* understand longer strings, + get rid of the !!! comment in the middle and the quotes that surround it. */ +PyDoc_VAR(sys_doc) = +PyDoc_STR( +"This module provides access to some objects used or maintained by the\n\ +interpreter and to functions that interact strongly with the interpreter.\n\ +\n\ +Dynamic objects:\n\ +\n\ +argv -- command line arguments; argv[0] is the script pathname if known\n\ +path -- module search path; path[0] is the script directory, else ''\n\ +modules -- dictionary of loaded modules\n\ +\n\ +displayhook -- called to show results in an interactive session\n\ +excepthook -- called to handle any uncaught exception other than SystemExit\n\ + To customize printing in an interactive session or to install a custom\n\ + top-level exception handler, assign other functions to replace these.\n\ +\n\ +exitfunc -- if sys.exitfunc exists, this routine is called when Python exits\n\ + Assigning to sys.exitfunc is deprecated; use the atexit module instead.\n\ +\n\ +stdin -- standard input file object; used by raw_input() and input()\n\ +stdout -- standard output file object; used by the print statement\n\ +stderr -- standard error object; used for error messages\n\ + By assigning other file objects (or objects that behave like files)\n\ + to these, it is possible to redirect all of the interpreter's I/O.\n\ +\n\ +last_type -- type of last uncaught exception\n\ +last_value -- value of last uncaught exception\n\ +last_traceback -- traceback of last uncaught exception\n\ + These three are only available in an interactive session after a\n\ + traceback has been printed.\n\ +\n\ +exc_type -- type of exception currently being handled\n\ +exc_value -- value of exception currently being handled\n\ +exc_traceback -- traceback of exception currently being handled\n\ + The function exc_info() should be used instead of these three,\n\ + because it is thread-safe.\n\ +" +) +/* concatenating string here */ +PyDoc_STR( +"\n\ +Static objects:\n\ +\n\ +maxint -- the largest supported integer (the smallest is -maxint-1)\n\ +maxunicode -- the largest supported character\n\ +builtin_module_names -- tuple of module names built into this interpreter\n\ +version -- the version of this interpreter as a string\n\ +version_info -- version information as a tuple\n\ +hexversion -- version information encoded as a single integer\n\ +copyright -- copyright notice pertaining to this interpreter\n\ +platform -- platform identifier\n\ +executable -- pathname of this Python interpreter\n\ +prefix -- prefix used to find the Python library\n\ +exec_prefix -- prefix used to find the machine-specific Python library\n\ +" +) +#ifdef MS_WINDOWS +/* concatenating string here */ +PyDoc_STR( +"dllhandle -- [Windows only] integer handle of the Python DLL\n\ +winver -- [Windows only] version number of the Python DLL\n\ +" +) +#endif /* MS_WINDOWS */ +PyDoc_STR( +"__stdin__ -- the original stdin; don't touch!\n\ +__stdout__ -- the original stdout; don't touch!\n\ +__stderr__ -- the original stderr; don't touch!\n\ +__displayhook__ -- the original displayhook; don't touch!\n\ +__excepthook__ -- the original excepthook; don't touch!\n\ +\n\ +Functions:\n\ +\n\ +displayhook() -- print an object to the screen, and save it in __builtin__._\n\ +excepthook() -- print an exception and its traceback to sys.stderr\n\ +exc_info() -- return thread-safe information about the current exception\n\ +exc_clear() -- clear the exception state for the current thread\n\ +exit() -- exit the interpreter by raising SystemExit\n\ +getdlopenflags() -- returns flags to be used for dlopen() calls\n\ +getrefcount() -- return the reference count for an object (plus one :-)\n\ +getrecursionlimit() -- return the max recursion depth for the interpreter\n\ +setcheckinterval() -- control how often the interpreter checks for events\n\ +setdlopenflags() -- set the flags to be used for dlopen() calls\n\ +setprofile() -- set the global profiling function\n\ +setrecursionlimit() -- set the max recursion depth for the interpreter\n\ +settrace() -- set the global debug tracing function\n\ +" +) +/* end of sys_doc */ ; + +PyObject * +_PySys_Init(void) +{ + PyObject *m, *v, *sysdict; + PyObject *sysin, *sysout, *syserr; + char *s; +#ifdef MS_WINDOWS + char buf[10]; +#endif + + m = Py_InitModule3("sys", sys_methods, sys_doc); + sysdict = PyModule_GetDict(m); + + sysin = PyFile_FromFile(stdin, "", "r", NULL); + sysout = PyFile_FromFile(stdout, "", "w", NULL); + syserr = PyFile_FromFile(stderr, "", "w", NULL); + if (PyErr_Occurred()) + return NULL; +#if defined(MS_WINDOWS) && !defined(MS_XBOX) + if(isatty(_fileno(stdin))){ + sprintf(buf, "cp%d", GetConsoleCP()); + if (!PyFile_SetEncoding(sysin, buf)) + return NULL; + } + if(isatty(_fileno(stdout))) { + sprintf(buf, "cp%d", GetConsoleOutputCP()); + if (!PyFile_SetEncoding(sysout, buf)) + return NULL; + } +#endif + + PyDict_SetItemString(sysdict, "stdin", sysin); + PyDict_SetItemString(sysdict, "stdout", sysout); + PyDict_SetItemString(sysdict, "stderr", syserr); + /* Make backup copies for cleanup */ + PyDict_SetItemString(sysdict, "__stdin__", sysin); + PyDict_SetItemString(sysdict, "__stdout__", sysout); + PyDict_SetItemString(sysdict, "__stderr__", syserr); + PyDict_SetItemString(sysdict, "__displayhook__", + PyDict_GetItemString(sysdict, "displayhook")); + PyDict_SetItemString(sysdict, "__excepthook__", + PyDict_GetItemString(sysdict, "excepthook")); + Py_XDECREF(sysin); + Py_XDECREF(sysout); + Py_XDECREF(syserr); + PyDict_SetItemString(sysdict, "version", + v = PyString_FromString(Py_GetVersion())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "hexversion", + v = PyInt_FromLong(PY_VERSION_HEX)); + Py_XDECREF(v); + /* + * These release level checks are mutually exclusive and cover + * the field, so don't get too fancy with the pre-processor! + */ +#if PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_ALPHA + s = "alpha"; +#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_BETA + s = "beta"; +#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_GAMMA + s = "candidate"; +#elif PY_RELEASE_LEVEL == PY_RELEASE_LEVEL_FINAL + s = "final"; +#endif + PyDict_SetItemString(sysdict, "version_info", + v = Py_BuildValue("iiisi", PY_MAJOR_VERSION, + PY_MINOR_VERSION, + PY_MICRO_VERSION, s, + PY_RELEASE_SERIAL)); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "api_version", + v = PyInt_FromLong(PYTHON_API_VERSION)); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "copyright", + v = PyString_FromString(Py_GetCopyright())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "platform", + v = PyString_FromString(Py_GetPlatform())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "executable", + v = PyString_FromString(Py_GetProgramFullPath())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "prefix", + v = PyString_FromString(Py_GetPrefix())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "exec_prefix", + v = PyString_FromString(Py_GetExecPrefix())); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "maxint", + v = PyInt_FromLong(PyInt_GetMax())); + Py_XDECREF(v); +#ifdef Py_USING_UNICODE + PyDict_SetItemString(sysdict, "maxunicode", + v = PyInt_FromLong(PyUnicode_GetMax())); + Py_XDECREF(v); +#endif + PyDict_SetItemString(sysdict, "builtin_module_names", + v = list_builtin_module_names()); + Py_XDECREF(v); + { + /* Assumes that longs are at least 2 bytes long. + Should be safe! */ + unsigned long number = 1; + char *value; + + s = (char *) &number; + if (s[0] == 0) + value = "big"; + else + value = "little"; + PyDict_SetItemString(sysdict, "byteorder", + v = PyString_FromString(value)); + Py_XDECREF(v); + } +#ifdef MS_COREDLL + PyDict_SetItemString(sysdict, "dllhandle", + v = PyLong_FromVoidPtr(PyWin_DLLhModule)); + Py_XDECREF(v); + PyDict_SetItemString(sysdict, "winver", + v = PyString_FromString(PyWin_DLLVersionString)); + Py_XDECREF(v); +#endif + if (warnoptions == NULL) { + warnoptions = PyList_New(0); + } + else { + Py_INCREF(warnoptions); + } + if (warnoptions != NULL) { + PyDict_SetItemString(sysdict, "warnoptions", warnoptions); + } + + if (PyErr_Occurred()) + return NULL; + return m; +} + +static PyObject * +makepathobject(char *path, int delim) +{ + int i, n; + char *p; + PyObject *v, *w; + + n = 1; + p = path; + while ((p = strchr(p, delim)) != NULL) { + n++; + p++; + } + v = PyList_New(n); + if (v == NULL) + return NULL; + for (i = 0; ; i++) { + p = strchr(path, delim); + if (p == NULL) + p = strchr(path, '\0'); /* End of string */ + w = PyString_FromStringAndSize(path, (int) (p - path)); + if (w == NULL) { + Py_DECREF(v); + return NULL; + } + PyList_SetItem(v, i, w); + if (*p == '\0') + break; + path = p+1; + } + return v; +} + +void +PySys_SetPath(char *path) +{ + PyObject *v; + if ((v = makepathobject(path, DELIM)) == NULL) + Py_FatalError("can't create sys.path"); + if (PySys_SetObject("path", v) != 0) + Py_FatalError("can't assign sys.path"); + Py_DECREF(v); +} + +static PyObject * +makeargvobject(int argc, char **argv) +{ + PyObject *av; + if (argc <= 0 || argv == NULL) { + /* Ensure at least one (empty) argument is seen */ + static char *empty_argv[1] = {""}; + argv = empty_argv; + argc = 1; + } + av = PyList_New(argc); + if (av != NULL) { + int i; + for (i = 0; i < argc; i++) { +#ifdef __VMS + PyObject *v; + + /* argv[0] is the script pathname if known */ + if (i == 0) { + char* fn = decc$translate_vms(argv[0]); + if ((fn == (char *)0) || fn == (char *)-1) + v = PyString_FromString(argv[0]); + else + v = PyString_FromString( + decc$translate_vms(argv[0])); + } else + v = PyString_FromString(argv[i]); +#else + PyObject *v = PyString_FromString(argv[i]); +#endif + if (v == NULL) { + Py_DECREF(av); + av = NULL; + break; + } + PyList_SetItem(av, i, v); + } + } + return av; +} + +void +PySys_SetArgv(int argc, char **argv) +{ +#if defined(HAVE_REALPATH) + char fullpath[MAXPATHLEN]; +#elif defined(MS_WINDOWS) + char fullpath[MAX_PATH]; +#endif + PyObject *av = makeargvobject(argc, argv); + PyObject *path = PySys_GetObject("path"); + if (av == NULL) + Py_FatalError("no mem for sys.argv"); + if (PySys_SetObject("argv", av) != 0) + Py_FatalError("can't assign sys.argv"); + if (path != NULL) { + char *argv0 = argv[0]; + char *p = NULL; + int n = 0; + PyObject *a; +#ifdef HAVE_READLINK + char link[MAXPATHLEN+1]; + char argv0copy[2*MAXPATHLEN+1]; + int nr = 0; + if (argc > 0 && argv0 != NULL) + nr = readlink(argv0, link, MAXPATHLEN); + if (nr > 0) { + /* It's a symlink */ + link[nr] = '\0'; + if (link[0] == SEP) + argv0 = link; /* Link to absolute path */ + else if (strchr(link, SEP) == NULL) + ; /* Link without path */ + else { + /* Must join(dirname(argv0), link) */ + char *q = strrchr(argv0, SEP); + if (q == NULL) + argv0 = link; /* argv0 without path */ + else { + /* Must make a copy */ + strcpy(argv0copy, argv0); + q = strrchr(argv0copy, SEP); + strcpy(q+1, link); + argv0 = argv0copy; + } + } + } +#endif /* HAVE_READLINK */ +#if SEP == '\\' /* Special case for MS filename syntax */ + if (argc > 0 && argv0 != NULL) { + char *q; +#if defined(MS_WINDOWS) && !defined(MS_XBOX) + char *ptemp; + if (GetFullPathName(argv0, + sizeof(fullpath), + fullpath, + &ptemp)) { + argv0 = fullpath; + } +#endif + p = strrchr(argv0, SEP); + /* Test for alternate separator */ + q = strrchr(p ? p : argv0, '/'); + if (q != NULL) + p = q; + if (p != NULL) { + n = p + 1 - argv0; + if (n > 1 && p[-1] != ':') + n--; /* Drop trailing separator */ + } + } +#else /* All other filename syntaxes */ + if (argc > 0 && argv0 != NULL) { +#if defined(HAVE_REALPATH) + if (realpath(argv0, fullpath)) { + argv0 = fullpath; + } +#endif + p = strrchr(argv0, SEP); + } + if (p != NULL) { +#ifndef RISCOS + n = p + 1 - argv0; +#else /* don't include trailing separator */ + n = p - argv0; +#endif /* RISCOS */ +#if SEP == '/' /* Special case for Unix filename syntax */ + if (n > 1) + n--; /* Drop trailing separator */ +#endif /* Unix */ + } +#endif /* All others */ + a = PyString_FromStringAndSize(argv0, n); + if (a == NULL) + Py_FatalError("no mem for sys.path insertion"); + if (PyList_Insert(path, 0, a) < 0) + Py_FatalError("sys.path.insert(0) failed"); + Py_DECREF(a); + } + Py_DECREF(av); +} + + +/* APIs to write to sys.stdout or sys.stderr using a printf-like interface. + Adapted from code submitted by Just van Rossum. + + PySys_WriteStdout(format, ...) + PySys_WriteStderr(format, ...) + + The first function writes to sys.stdout; the second to sys.stderr. When + there is a problem, they write to the real (C level) stdout or stderr; + no exceptions are raised. + + Both take a printf-style format string as their first argument followed + by a variable length argument list determined by the format string. + + *** WARNING *** + + The format should limit the total size of the formatted output string to + 1000 bytes. In particular, this means that no unrestricted "%s" formats + should occur; these should be limited using "%.s where is a + decimal number calculated so that plus the maximum size of other + formatted text does not exceed 1000 bytes. Also watch out for "%f", + which can print hundreds of digits for very large numbers. + + */ + +static void +mywrite(char *name, FILE *fp, const char *format, va_list va) +{ + PyObject *file; + PyObject *error_type, *error_value, *error_traceback; + + PyErr_Fetch(&error_type, &error_value, &error_traceback); + file = PySys_GetObject(name); + if (file == NULL || PyFile_AsFile(file) == fp) + vfprintf(fp, format, va); + else { + char buffer[1001]; + const int written = PyOS_vsnprintf(buffer, sizeof(buffer), + format, va); + if (PyFile_WriteString(buffer, file) != 0) { + PyErr_Clear(); + fputs(buffer, fp); + } + if (written < 0 || written >= sizeof(buffer)) { + const char *truncated = "... truncated"; + if (PyFile_WriteString(truncated, file) != 0) { + PyErr_Clear(); + fputs(truncated, fp); + } + } + } + PyErr_Restore(error_type, error_value, error_traceback); +} + +void +PySys_WriteStdout(const char *format, ...) +{ + va_list va; + + va_start(va, format); + mywrite("stdout", stdout, format, va); + va_end(va); +} + +void +PySys_WriteStderr(const char *format, ...) +{ + va_list va; + + va_start(va, format); + mywrite("stderr", stderr, format, va); + va_end(va); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread.c new file mode 100644 index 00000000..0e61581b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread.c @@ -0,0 +1,248 @@ + +/* Thread package. + This is intended to be usable independently from Python. + The implementation for system foobar is in a file thread_foobar.h + which is included by this file dependent on config settings. + Stuff shared by all thread_*.h files is collected here. */ + +#include "Python.h" + +#ifndef DONT_HAVE_STDIO_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#else +#ifdef Py_DEBUG +extern char *getenv(const char *); +#endif +#endif + +#ifdef __DGUX +#define _USING_POSIX4A_DRAFT6 +#endif + +#ifdef __sgi +#ifndef HAVE_PTHREAD_H /* XXX Need to check in configure.in */ +#undef _POSIX_THREADS +#endif +#endif + +#include "pythread.h" + +#ifndef _POSIX_THREADS + +#ifdef __sgi +#define SGI_THREADS +#endif + +#ifdef HAVE_THREAD_H +#define SOLARIS_THREADS +#endif + +#if defined(sun) && !defined(SOLARIS_THREADS) +#define SUN_LWP +#endif + +#if defined(__MWERKS__) && !defined(__BEOS__) +#define _POSIX_THREADS +#endif + +#endif /* _POSIX_THREADS */ + + +#ifdef Py_DEBUG +static int thread_debug = 0; +#define dprintf(args) (void)((thread_debug & 1) && printf args) +#define d2printf(args) ((thread_debug & 8) && printf args) +#else +#define dprintf(args) +#define d2printf(args) +#endif + +static int initialized; + +static void PyThread__init_thread(void); /* Forward */ + +void PyThread_init_thread(void) +{ +#ifdef Py_DEBUG + char *p = Py_GETENV("THREADDEBUG"); + + if (p) { + if (*p) + thread_debug = atoi(p); + else + thread_debug = 1; + } +#endif /* Py_DEBUG */ + if (initialized) + return; + initialized = 1; + dprintf(("PyThread_init_thread called\n")); + PyThread__init_thread(); +} + +#ifdef SGI_THREADS +#include "thread_sgi.h" +#endif + +#ifdef SOLARIS_THREADS +#include "thread_solaris.h" +#endif + +#ifdef SUN_LWP +#include "thread_lwp.h" +#endif + +#ifdef HAVE_PTH +#include "thread_pth.h" +#undef _POSIX_THREADS +#endif + +#ifdef _POSIX_THREADS +#include "thread_pthread.h" +#endif + +#ifdef C_THREADS +#include "thread_cthread.h" +#endif + +#ifdef NT_THREADS +#include "thread_nt.h" +#endif + +#ifdef OS2_THREADS +#include "thread_os2.h" +#endif + +#ifdef BEOS_THREADS +#include "thread_beos.h" +#endif + +#ifdef WINCE_THREADS +#include "thread_wince.h" +#endif + +#ifdef PLAN9_THREADS +#include "thread_plan9.h" +#endif + +#ifdef ATHEOS_THREADS +#include "thread_atheos.h" +#endif + +/* +#ifdef FOOBAR_THREADS +#include "thread_foobar.h" +#endif +*/ + +#ifndef Py_HAVE_NATIVE_TLS +/* If the platform has not supplied a platform specific + TLS implementation, provide our own. + + This code stolen from "thread_sgi.h", where it was the only + implementation of an existing Python TLS API. +*/ +/* + * Per-thread data ("key") support. + */ + +struct key { + struct key *next; + long id; + int key; + void *value; +}; + +static struct key *keyhead = NULL; +static int nkeys = 0; +static PyThread_type_lock keymutex = NULL; + +static struct key *find_key(int key, void *value) +{ + struct key *p; + long id = PyThread_get_thread_ident(); + for (p = keyhead; p != NULL; p = p->next) { + if (p->id == id && p->key == key) + return p; + } + if (value == NULL) + return NULL; + p = (struct key *)malloc(sizeof(struct key)); + if (p != NULL) { + p->id = id; + p->key = key; + p->value = value; + PyThread_acquire_lock(keymutex, 1); + p->next = keyhead; + keyhead = p; + PyThread_release_lock(keymutex); + } + return p; +} + +int PyThread_create_key(void) +{ + if (keymutex == NULL) + keymutex = PyThread_allocate_lock(); + return ++nkeys; +} + +void PyThread_delete_key(int key) +{ + struct key *p, **q; + PyThread_acquire_lock(keymutex, 1); + q = &keyhead; + while ((p = *q) != NULL) { + if (p->key == key) { + *q = p->next; + free((void *)p); + /* NB This does *not* free p->value! */ + } + else + q = &p->next; + } + PyThread_release_lock(keymutex); +} + +int PyThread_set_key_value(int key, void *value) +{ + struct key *p = find_key(key, value); + if (p == NULL) + return -1; + else + return 0; +} + +void *PyThread_get_key_value(int key) +{ + struct key *p = find_key(key, NULL); + if (p == NULL) + return NULL; + else + return p->value; +} + +void PyThread_delete_key_value(int key) +{ + long id = PyThread_get_thread_ident(); + struct key *p, **q; + PyThread_acquire_lock(keymutex, 1); + q = &keyhead; + while ((p = *q) != NULL) { + if (p->key == key && p->id == id) { + *q = p->next; + free((void *)p); + /* NB This does *not* free p->value! */ + break; + } + else + q = &p->next; + } + PyThread_release_lock(keymutex); +} + +#endif /* Py_HAVE_NATIVE_TLS */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_atheos.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_atheos.h new file mode 100644 index 00000000..e3dd7331 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_atheos.h @@ -0,0 +1,300 @@ +/* Threading for AtheOS. + Based on thread_beos.h. */ + +#include +#include +#include +#include +#include + +/* Missing decl from threads.h */ +extern int exit_thread(int); + + +/* Undefine FASTLOCK to play with simple semaphores. */ +#define FASTLOCK + + +#ifdef FASTLOCK + +/* Use an atomic counter and a semaphore for maximum speed. */ +typedef struct fastmutex { + sem_id sem; + atomic_t count; +} fastmutex_t; + + +static int fastmutex_create(const char *name, fastmutex_t * mutex); +static int fastmutex_destroy(fastmutex_t * mutex); +static int fastmutex_lock(fastmutex_t * mutex); +static int fastmutex_timedlock(fastmutex_t * mutex, bigtime_t timeout); +static int fastmutex_unlock(fastmutex_t * mutex); + + +static int fastmutex_create(const char *name, fastmutex_t * mutex) +{ + mutex->count = 0; + mutex->sem = create_semaphore(name, 0, 0); + return (mutex->sem < 0) ? -1 : 0; +} + + +static int fastmutex_destroy(fastmutex_t * mutex) +{ + if (fastmutex_timedlock(mutex, 0) == 0 || errno == EWOULDBLOCK) { + return delete_semaphore(mutex->sem); + } + return 0; +} + + +static int fastmutex_lock(fastmutex_t * mutex) +{ + atomic_t prev = atomic_add(&mutex->count, 1); + if (prev > 0) + return lock_semaphore(mutex->sem); + return 0; +} + + +static int fastmutex_timedlock(fastmutex_t * mutex, bigtime_t timeout) +{ + atomic_t prev = atomic_add(&mutex->count, 1); + if (prev > 0) + return lock_semaphore_x(mutex->sem, 1, 0, timeout); + return 0; +} + + +static int fastmutex_unlock(fastmutex_t * mutex) +{ + atomic_t prev = atomic_add(&mutex->count, -1); + if (prev > 1) + return unlock_semaphore(mutex->sem); + return 0; +} + + +#endif /* FASTLOCK */ + + +/* + * Initialization. + * + */ +static void PyThread__init_thread(void) +{ + /* Do nothing. */ + return; +} + + +/* + * Thread support. + * + */ + +static atomic_t thread_count = 0; + +long PyThread_start_new_thread(void (*func) (void *), void *arg) +{ + status_t success = -1; + thread_id tid; + char name[OS_NAME_LENGTH]; + atomic_t this_thread; + + dprintf(("PyThread_start_new_thread called\n")); + + this_thread = atomic_add(&thread_count, 1); + PyOS_snprintf(name, sizeof(name), "python thread (%d)", this_thread); + + tid = spawn_thread(name, func, NORMAL_PRIORITY, 0, arg); + if (tid < 0) { + dprintf(("PyThread_start_new_thread spawn_thread failed: %s\n", strerror(errno))); + } else { + success = resume_thread(tid); + if (success < 0) { + dprintf(("PyThread_start_new_thread resume_thread failed: %s\n", strerror(errno))); + } + } + + return (success < 0 ? -1 : tid); +} + + +long PyThread_get_thread_ident(void) +{ + return get_thread_id(NULL); +} + + +static void do_PyThread_exit_thread(int no_cleanup) +{ + dprintf(("PyThread_exit_thread called\n")); + + /* Thread-safe way to read a variable without a mutex: */ + if (atomic_add(&thread_count, 0) == 0) { + /* No threads around, so exit main(). */ + if (no_cleanup) + _exit(0); + else + exit(0); + } else { + /* We're a thread */ + exit_thread(0); + } +} + + +void PyThread_exit_thread(void) +{ + do_PyThread_exit_thread(0); +} + + +void PyThread__exit_thread(void) +{ + do_PyThread_exit_thread(1); +} + + +#ifndef NO_EXIT_PROG +static void do_PyThread_exit_prog(int status, int no_cleanup) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + + /* No need to do anything, the threads get torn down if main()exits. */ + if (no_cleanup) + _exit(status); + else + exit(status); +} + + +void PyThread_exit_prog(int status) +{ + do_PyThread_exit_prog(status, 0); +} + + +void PyThread__exit_prog(int status) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + + +/* + * Lock support. + * + */ + +static atomic_t lock_count = 0; + +PyThread_type_lock PyThread_allocate_lock(void) +{ +#ifdef FASTLOCK + fastmutex_t *lock; +#else + sem_id sema; +#endif + char name[OS_NAME_LENGTH]; + atomic_t this_lock; + + dprintf(("PyThread_allocate_lock called\n")); + +#ifdef FASTLOCK + lock = (fastmutex_t *) malloc(sizeof(fastmutex_t)); + if (lock == NULL) { + dprintf(("PyThread_allocate_lock failed: out of memory\n")); + return (PyThread_type_lock) NULL; + } +#endif + this_lock = atomic_add(&lock_count, 1); + PyOS_snprintf(name, sizeof(name), "python lock (%d)", this_lock); + +#ifdef FASTLOCK + if (fastmutex_create(name, lock) < 0) { + dprintf(("PyThread_allocate_lock failed: %s\n", + strerror(errno))); + free(lock); + lock = NULL; + } + dprintf(("PyThread_allocate_lock()-> %p\n", lock)); + return (PyThread_type_lock) lock; +#else + sema = create_semaphore(name, 1, 0); + if (sema < 0) { + dprintf(("PyThread_allocate_lock failed: %s\n", + strerror(errno))); + sema = 0; + } + dprintf(("PyThread_allocate_lock()-> %p\n", sema)); + return (PyThread_type_lock) sema; +#endif +} + + +void PyThread_free_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_free_lock(%p) called\n", lock)); + +#ifdef FASTLOCK + if (fastmutex_destroy((fastmutex_t *) lock) < 0) { + dprintf(("PyThread_free_lock(%p) failed: %s\n", lock, + strerror(errno))); + } + free(lock); +#else + if (delete_semaphore((sem_id) lock) < 0) { + dprintf(("PyThread_free_lock(%p) failed: %s\n", lock, + strerror(errno))); + } +#endif +} + + +int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag) +{ + int retval; + + dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, + waitflag)); + +#ifdef FASTLOCK + if (waitflag) + retval = fastmutex_lock((fastmutex_t *) lock); + else + retval = fastmutex_timedlock((fastmutex_t *) lock, 0); +#else + if (waitflag) + retval = lock_semaphore((sem_id) lock); + else + retval = lock_semaphore_x((sem_id) lock, 1, 0, 0); +#endif + if (retval < 0) { + dprintf(("PyThread_acquire_lock(%p, %d) failed: %s\n", + lock, waitflag, strerror(errno))); + } + dprintf(("PyThread_acquire_lock(%p, %d)-> %d\n", lock, waitflag, + retval)); + return retval < 0 ? 0 : 1; +} + + +void PyThread_release_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_release_lock(%p) called\n", lock)); + +#ifdef FASTLOCK + if (fastmutex_unlock((fastmutex_t *) lock) < 0) { + dprintf(("PyThread_release_lock(%p) failed: %s\n", lock, + strerror(errno))); + } +#else + if (unlock_semaphore((sem_id) lock) < 0) { + dprintf(("PyThread_release_lock(%p) failed: %s\n", lock, + strerror(errno))); + } +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_beos.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_beos.h new file mode 100644 index 00000000..7ac48034 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_beos.h @@ -0,0 +1,287 @@ +#include +#include +#include + +/* ---------------------------------------------------------------------- + * Fast locking mechanism described by Benoit Schillings (benoit@be.com) + * in the Be Developer's Newsletter, Issue #26 (http://www.be.com/). + */ +typedef struct benaphore { + sem_id _sem; + int32 _atom; +} benaphore_t; + +static status_t benaphore_create( const char *name, benaphore_t *ben ); +static status_t benaphore_destroy( benaphore_t *ben ); +static status_t benaphore_lock( benaphore_t *ben ); +static status_t benaphore_timedlock( benaphore_t *ben, bigtime_t micros ); +static status_t benaphore_unlock( benaphore_t *ben ); + +static status_t benaphore_create( const char *name, benaphore_t *ben ) +{ + if( ben != NULL ) { + ben->_atom = 0; + ben->_sem = create_sem( 0, name ); + + if( ben->_sem < B_NO_ERROR ) { + return B_BAD_SEM_ID; + } + } else { + return EFAULT; + } + + return EOK; +} + +static status_t benaphore_destroy( benaphore_t *ben ) +{ + if( ben->_sem >= B_NO_ERROR ) { + status_t retval = benaphore_timedlock( ben, 0 ); + + if( retval == EOK || retval == EWOULDBLOCK ) { + status_t del_retval = delete_sem( ben->_sem ); + + return del_retval; + } + } + + return B_BAD_SEM_ID; +} + +static status_t benaphore_lock( benaphore_t *ben ) +{ + int32 prev = atomic_add( &(ben->_atom), 1 ); + + if( prev > 0 ) { + return acquire_sem( ben->_sem ); + } + + return EOK; +} + +static status_t benaphore_timedlock( benaphore_t *ben, bigtime_t micros ) +{ + int32 prev = atomic_add( &(ben->_atom), 1 ); + + if( prev > 0 ) { + status_t retval = acquire_sem_etc( ben->_sem, 1, B_TIMEOUT, micros ); + + switch( retval ) { + case B_WOULD_BLOCK: /* Fall through... */ + case B_TIMED_OUT: + return EWOULDBLOCK; + break; + case B_OK: + return EOK; + break; + default: + return retval; + break; + } + } + + return EOK; +} + +static status_t benaphore_unlock( benaphore_t *ben ) +{ + int32 prev = atomic_add( &(ben->_atom), -1 ); + + if( prev > 1 ) { + return release_sem( ben->_sem ); + } + + return EOK; +} + +/* ---------------------------------------------------------------------- + * Initialization. + */ +static void PyThread__init_thread( void ) +{ + /* Do nothing. */ + return; +} + +/* ---------------------------------------------------------------------- + * Thread support. + * + * Only ANSI C, renamed functions here; you can't use K&R on BeOS, + * and there's no legacy thread module to support. + */ + +static int32 thread_count = 0; + +long PyThread_start_new_thread( void (*func)(void *), void *arg ) +{ + status_t success = 0; + thread_id tid; + char name[B_OS_NAME_LENGTH]; + int32 this_thread; + + dprintf(("PyThread_start_new_thread called\n")); + + /* We are so very thread-safe... */ + this_thread = atomic_add( &thread_count, 1 ); + PyOS_snprintf(name, sizeof(name), + "python thread (%d)", this_thread ); + + tid = spawn_thread( (thread_func)func, name, + B_NORMAL_PRIORITY, arg ); + if( tid > B_NO_ERROR ) { + success = resume_thread( tid ); + } + + return ( success == B_NO_ERROR ? tid : -1 ); +} + +long PyThread_get_thread_ident( void ) +{ + /* Presumed to return the current thread's ID... */ + thread_id tid; + tid = find_thread( NULL ); + + return ( tid != B_NAME_NOT_FOUND ? tid : -1 ); +} + +static void do_PyThread_exit_thread( int no_cleanup ) +{ + int32 threads; + + dprintf(("PyThread_exit_thread called\n")); + + /* Thread-safe way to read a variable without a mutex: */ + threads = atomic_add( &thread_count, 0 ); + + if( threads == 0 ) { + /* No threads around, so exit main(). */ + if( no_cleanup ) { + _exit(0); + } else { + exit(0); + } + } else { + /* Oh, we're a thread, let's try to exit gracefully... */ + exit_thread( B_NO_ERROR ); + } +} + +void PyThread_exit_thread( void ) +{ + do_PyThread_exit_thread(0); +} + +void PyThread__exit_thread( void ) +{ + do_PyThread_exit_thread(1); +} + +#ifndef NO_EXIT_PROG +static void do_PyThread_exit_prog( int status, int no_cleanup ) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + + /* No need to do anything, the threads get torn down if main() exits. */ + + if (no_cleanup) { + _exit(status); + } else { + exit(status); + } +} + +void PyThread_exit_prog( int status ) +{ + do_PyThread_exit_prog(status, 0); +} + +void PyThread__exit_prog( int status ) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + +/* ---------------------------------------------------------------------- + * Lock support. + */ + +static int32 lock_count = 0; + +PyThread_type_lock PyThread_allocate_lock( void ) +{ + benaphore_t *lock; + status_t retval; + char name[B_OS_NAME_LENGTH]; + int32 this_lock; + + dprintf(("PyThread_allocate_lock called\n")); + + lock = (benaphore_t *)malloc( sizeof( benaphore_t ) ); + if( lock == NULL ) { + /* TODO: that's bad, raise MemoryError */ + return (PyThread_type_lock)NULL; + } + + this_lock = atomic_add( &lock_count, 1 ); + PyOS_snprintf(name, sizeof(name), "python lock (%d)", this_lock); + + retval = benaphore_create( name, lock ); + if( retval != EOK ) { + /* TODO: that's bad, raise an exception */ + return (PyThread_type_lock)NULL; + } + + dprintf(("PyThread_allocate_lock() -> %p\n", lock)); + return (PyThread_type_lock) lock; +} + +void PyThread_free_lock( PyThread_type_lock lock ) +{ + status_t retval; + + dprintf(("PyThread_free_lock(%p) called\n", lock)); + + retval = benaphore_destroy( (benaphore_t *)lock ); + if( retval != EOK ) { + /* TODO: that's bad, raise an exception */ + return; + } +} + +int PyThread_acquire_lock( PyThread_type_lock lock, int waitflag ) +{ + int success; + status_t retval; + + dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag)); + + if( waitflag ) { + retval = benaphore_lock( (benaphore_t *)lock ); + } else { + retval = benaphore_timedlock( (benaphore_t *)lock, 0 ); + } + + if( retval == EOK ) { + success = 1; + } else { + success = 0; + + /* TODO: that's bad, raise an exception */ + } + + dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success)); + return success; +} + +void PyThread_release_lock( PyThread_type_lock lock ) +{ + status_t retval; + + dprintf(("PyThread_release_lock(%p) called\n", lock)); + + retval = benaphore_unlock( (benaphore_t *)lock ); + if( retval != EOK ) { + /* TODO: that's bad, raise an exception */ + return; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_cthread.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_cthread.h new file mode 100644 index 00000000..763599d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_cthread.h @@ -0,0 +1,156 @@ + +#ifdef MACH_C_THREADS +#include +#endif + +#ifdef HURD_C_THREADS +#include +#endif + +/* + * Initialization. + */ +static void +PyThread__init_thread(void) +{ +#ifndef HURD_C_THREADS + /* Roland McGrath said this should not be used since this is + done while linking to threads */ + cthread_init(); +#else +/* do nothing */ + ; +#endif +} + +/* + * Thread support. + */ +long +PyThread_start_new_thread(void (*func)(void *), void *arg) +{ + int success = 0; /* init not needed when SOLARIS_THREADS and */ + /* C_THREADS implemented properly */ + + dprintf(("PyThread_start_new_thread called\n")); + if (!initialized) + PyThread_init_thread(); + /* looks like solaris detaches the thread to never rejoin + * so well do it here + */ + cthread_detach(cthread_fork((cthread_fn_t) func, arg)); + return success < 0 ? -1 : 0; +} + +long +PyThread_get_thread_ident(void) +{ + if (!initialized) + PyThread_init_thread(); + return (long) cthread_self(); +} + +static void +do_PyThread_exit_thread(int no_cleanup) +{ + dprintf(("PyThread_exit_thread called\n")); + if (!initialized) + if (no_cleanup) + _exit(0); + else + exit(0); + cthread_exit(0); +} + +void +PyThread_exit_thread(void) +{ + do_PyThread_exit_thread(0); +} + +void +PyThread__exit_thread(void) +{ + do_PyThread_exit_thread(1); +} + +#ifndef NO_EXIT_PROG +static +void do_PyThread_exit_prog(int status, int no_cleanup) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + if (!initialized) + if (no_cleanup) + _exit(status); + else + exit(status); + if (no_cleanup) + _exit(status); + else + exit(status); +} + +void +PyThread_exit_prog(int status) +{ + do_PyThread_exit_prog(status, 0); +} + +void +PyThread__exit_prog(int status) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + +/* + * Lock support. + */ +PyThread_type_lock +PyThread_allocate_lock(void) +{ + mutex_t lock; + + dprintf(("PyThread_allocate_lock called\n")); + if (!initialized) + PyThread_init_thread(); + + lock = mutex_alloc(); + if (mutex_init(lock)) { + perror("mutex_init"); + free((void *) lock); + lock = 0; + } + dprintf(("PyThread_allocate_lock() -> %p\n", lock)); + return (PyThread_type_lock) lock; +} + +void +PyThread_free_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_free_lock(%p) called\n", lock)); + mutex_free(lock); +} + +int +PyThread_acquire_lock(PyThread_type_lock lock, int waitflag) +{ + int success = FALSE; + + dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag)); + if (waitflag) { /* blocking */ + mutex_lock((mutex_t)lock); + success = TRUE; + } else { /* non blocking */ + success = mutex_try_lock((mutex_t)lock); + } + dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success)); + return success; +} + +void +PyThread_release_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_release_lock(%p) called\n", lock)); + mutex_unlock((mutex_t )lock); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_foobar.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_foobar.h new file mode 100644 index 00000000..1cbc77bc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_foobar.h @@ -0,0 +1,115 @@ + +/* + * Initialization. + */ +static void +PyThread__init_thread(void) +{ +} + +/* + * Thread support. + */ +long +PyThread_start_new_thread(void (*func)(void *), void *arg) +{ + int success = 0; /* init not needed when SOLARIS_THREADS and */ + /* C_THREADS implemented properly */ + + dprintf(("PyThread_start_new_thread called\n")); + if (!initialized) + PyThread_init_thread(); + return success < 0 ? -1 : 0; +} + +long +PyThread_get_thread_ident(void) +{ + if (!initialized) + PyThread_init_thread(); +} + +static +void do_PyThread_exit_thread(int no_cleanup) +{ + dprintf(("PyThread_exit_thread called\n")); + if (!initialized) + if (no_cleanup) + _exit(0); + else + exit(0); +} + +void +PyThread_exit_thread(void) +{ + do_PyThread_exit_thread(0); +} + +void +PyThread__exit_thread(void) +{ + do_PyThread_exit_thread(1); +} + +#ifndef NO_EXIT_PROG +static +void do_PyThread_exit_prog(int status, int no_cleanup) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + if (!initialized) + if (no_cleanup) + _exit(status); + else + exit(status); +} + +void +PyThread_exit_prog(int status) +{ + do_PyThread_exit_prog(status, 0); +} + +void +PyThread__exit_prog(int status) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + +/* + * Lock support. + */ +PyThread_type_lock +PyThread_allocate_lock(void) +{ + + dprintf(("PyThread_allocate_lock called\n")); + if (!initialized) + PyThread_init_thread(); + + dprintf(("PyThread_allocate_lock() -> %p\n", lock)); + return (PyThread_type_lock) lock; +} + +void +PyThread_free_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_free_lock(%p) called\n", lock)); +} + +int +PyThread_acquire_lock(PyThread_type_lock lock, int waitflag) +{ + int success; + + dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag)); + dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success)); + return success; +} + +void +PyThread_release_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_release_lock(%p) called\n", lock)); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_lwp.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_lwp.h new file mode 100644 index 00000000..d24445b1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_lwp.h @@ -0,0 +1,149 @@ + +#include +#include +#include + +#define STACKSIZE 1000 /* stacksize for a thread */ +#define NSTACKS 2 /* # stacks to be put in cache initially */ + +struct lock { + int lock_locked; + cv_t lock_condvar; + mon_t lock_monitor; +}; + + +/* + * Initialization. + */ +static void PyThread__init_thread(void) +{ + lwp_setstkcache(STACKSIZE, NSTACKS); +} + +/* + * Thread support. + */ + + +long PyThread_start_new_thread(void (*func)(void *), void *arg) +{ + thread_t tid; + int success; + dprintf(("PyThread_start_new_thread called\n")); + if (!initialized) + PyThread_init_thread(); + success = lwp_create(&tid, func, MINPRIO, 0, lwp_newstk(), 1, arg); + return success < 0 ? -1 : 0; +} + +long PyThread_get_thread_ident(void) +{ + thread_t tid; + if (!initialized) + PyThread_init_thread(); + if (lwp_self(&tid) < 0) + return -1; + return tid.thread_id; +} + +static void do_PyThread_exit_thread(int no_cleanup) +{ + dprintf(("PyThread_exit_thread called\n")); + if (!initialized) + if (no_cleanup) + _exit(0); + else + exit(0); + lwp_destroy(SELF); +} + +void PyThread_exit_thread(void) +{ + do_PyThread_exit_thread(0); +} + +void PyThread__exit_thread(void) +{ + do_PyThread_exit_thread(1); +} + +#ifndef NO_EXIT_PROG +static void do_PyThread_exit_prog(int status, int no_cleanup) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + if (!initialized) + if (no_cleanup) + _exit(status); + else + exit(status); + pod_exit(status); +} + +void PyThread_exit_prog(int status) +{ + do_PyThread_exit_prog(status, 0); +} + +void PyThread__exit_prog(int status) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + +/* + * Lock support. + */ +PyThread_type_lock PyThread_allocate_lock(void) +{ + struct lock *lock; + extern char *malloc(size_t); + + dprintf(("PyThread_allocate_lock called\n")); + if (!initialized) + PyThread_init_thread(); + + lock = (struct lock *) malloc(sizeof(struct lock)); + lock->lock_locked = 0; + (void) mon_create(&lock->lock_monitor); + (void) cv_create(&lock->lock_condvar, lock->lock_monitor); + dprintf(("PyThread_allocate_lock() -> %p\n", lock)); + return (PyThread_type_lock) lock; +} + +void PyThread_free_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_free_lock(%p) called\n", lock)); + mon_destroy(((struct lock *) lock)->lock_monitor); + free((char *) lock); +} + +int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag) +{ + int success; + + dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag)); + success = 0; + + (void) mon_enter(((struct lock *) lock)->lock_monitor); + if (waitflag) + while (((struct lock *) lock)->lock_locked) + cv_wait(((struct lock *) lock)->lock_condvar); + if (!((struct lock *) lock)->lock_locked) { + success = 1; + ((struct lock *) lock)->lock_locked = 1; + } + cv_broadcast(((struct lock *) lock)->lock_condvar); + mon_exit(((struct lock *) lock)->lock_monitor); + dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success)); + return success; +} + +void PyThread_release_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_release_lock(%p) called\n", lock)); + (void) mon_enter(((struct lock *) lock)->lock_monitor); + ((struct lock *) lock)->lock_locked = 0; + cv_broadcast(((struct lock *) lock)->lock_condvar); + mon_exit(((struct lock *) lock)->lock_monitor); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_nt.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_nt.h new file mode 100644 index 00000000..23a311a4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_nt.h @@ -0,0 +1,324 @@ + +/* This code implemented by Dag.Gruneau@elsa.preseco.comm.se */ +/* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch@iso.ru */ +/* Eliminated some memory leaks, gsw@agere.com */ + +#ifdef MS_XBOX +#include +#else +#define WIN32_LEAN_AND_MEAN +#include +#endif +#include +#include + +typedef struct NRMUTEX { + LONG owned ; + DWORD thread_id ; + HANDLE hevent ; +} NRMUTEX, *PNRMUTEX ; + +typedef PVOID WINAPI interlocked_cmp_xchg_t(PVOID *dest, PVOID exc, PVOID comperand) ; + +/* Sorry mate, but we haven't got InterlockedCompareExchange in Win95! */ +static PVOID WINAPI interlocked_cmp_xchg(PVOID *dest, PVOID exc, PVOID comperand) +{ + static LONG spinlock = 0 ; + PVOID result ; + DWORD dwSleep = 0; + + /* Acqire spinlock (yielding control to other threads if cant aquire for the moment) */ + while(InterlockedExchange(&spinlock, 1)) + { + // Using Sleep(0) can cause a priority inversion. + // Sleep(0) only yields the processor if there's + // another thread of the same priority that's + // ready to run. If a high-priority thread is + // trying to acquire the lock, which is held by + // a low-priority thread, then the low-priority + // thread may never get scheduled and hence never + // free the lock. NT attempts to avoid priority + // inversions by temporarily boosting the priority + // of low-priority runnable threads, but the problem + // can still occur if there's a medium-priority + // thread that's always runnable. If Sleep(1) is used, + // then the thread unconditionally yields the CPU. We + // only do this for the second and subsequent even + // iterations, since a millisecond is a long time to wait + // if the thread can be scheduled in again sooner + // (~100,000 instructions). + // Avoid priority inversion: 0, 1, 0, 1,... + Sleep(dwSleep); + dwSleep = !dwSleep; + } + result = *dest ; + if (result == comperand) + *dest = exc ; + /* Release spinlock */ + spinlock = 0 ; + return result ; +} ; + +static interlocked_cmp_xchg_t *ixchg ; +BOOL InitializeNonRecursiveMutex(PNRMUTEX mutex) +{ +#ifndef MS_XBOX + if (!ixchg) + { + /* Sorely, Win95 has no InterlockedCompareExchange API (Win98 has), so we have to use emulation */ + HANDLE kernel = GetModuleHandle("kernel32.dll") ; + if (!kernel || (ixchg = (interlocked_cmp_xchg_t *)GetProcAddress(kernel, "InterlockedCompareExchange")) == NULL) + ixchg = interlocked_cmp_xchg ; + } +#endif + + mutex->owned = -1 ; /* No threads have entered NonRecursiveMutex */ + mutex->thread_id = 0 ; + mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ; + return mutex->hevent != NULL ; /* TRUE if the mutex is created */ +} + +#ifndef MS_XBOX +# ifdef InterlockedCompareExchange +# undef InterlockedCompareExchange +# endif +# define InterlockedCompareExchange(dest,exchange,comperand) (ixchg((dest), (exchange), (comperand))) +#endif // !MS_XBOX + +VOID DeleteNonRecursiveMutex(PNRMUTEX mutex) +{ + /* No in-use check */ + CloseHandle(mutex->hevent) ; + mutex->hevent = NULL ; /* Just in case */ +} + +DWORD EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait) +{ + /* Assume that the thread waits successfully */ + DWORD ret ; + + /* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */ + if (!wait) + { + if (InterlockedCompareExchange((PVOID *)&mutex->owned, (PVOID)0, (PVOID)-1) != (PVOID)-1) + return WAIT_TIMEOUT ; + ret = WAIT_OBJECT_0 ; + } + else + ret = InterlockedIncrement(&mutex->owned) ? + /* Some thread owns the mutex, let's wait... */ + WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ; + + mutex->thread_id = GetCurrentThreadId() ; /* We own it */ + return ret ; +} + +BOOL LeaveNonRecursiveMutex(PNRMUTEX mutex) +{ + /* We don't own the mutex */ + mutex->thread_id = 0 ; + return + InterlockedDecrement(&mutex->owned) < 0 || + SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */ +} + +PNRMUTEX AllocNonRecursiveMutex(void) +{ + PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ; + if (mutex && !InitializeNonRecursiveMutex(mutex)) + { + free(mutex) ; + mutex = NULL ; + } + return mutex ; +} + +void FreeNonRecursiveMutex(PNRMUTEX mutex) +{ + if (mutex) + { + DeleteNonRecursiveMutex(mutex) ; + free(mutex) ; + } +} + +long PyThread_get_thread_ident(void); + +/* + * Initialization of the C package, should not be needed. + */ +static void PyThread__init_thread(void) +{ +} + +/* + * Thread support. + */ + +typedef struct { + void (*func)(void*); + void *arg; + long id; + HANDLE done; +} callobj; + +static int +bootstrap(void *call) +{ + callobj *obj = (callobj*)call; + /* copy callobj since other thread might free it before we're done */ + void (*func)(void*) = obj->func; + void *arg = obj->arg; + + obj->id = PyThread_get_thread_ident(); + ReleaseSemaphore(obj->done, 1, NULL); + func(arg); + return 0; +} + +long +PyThread_start_new_thread(void (*func)(void *), void *arg) +{ + unsigned long rv; + callobj obj; + + dprintf(("%ld: PyThread_start_new_thread called\n", + PyThread_get_thread_ident())); + if (!initialized) + PyThread_init_thread(); + + obj.id = -1; /* guilty until proved innocent */ + obj.func = func; + obj.arg = arg; + obj.done = CreateSemaphore(NULL, 0, 1, NULL); + if (obj.done == NULL) + return -1; + + rv = _beginthread(bootstrap, 0, &obj); /* use default stack size */ + if (rv == (unsigned long)-1) { + /* I've seen errno == EAGAIN here, which means "there are + * too many threads". + */ + dprintf(("%ld: PyThread_start_new_thread failed: %p errno %d\n", + PyThread_get_thread_ident(), rv, errno)); + obj.id = -1; + } + else { + dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n", + PyThread_get_thread_ident(), rv)); + /* wait for thread to initialize, so we can get its id */ + WaitForSingleObject(obj.done, INFINITE); + assert(obj.id != -1); + } + CloseHandle((HANDLE)obj.done); + return obj.id; +} + +/* + * Return the thread Id instead of an handle. The Id is said to uniquely identify the + * thread in the system + */ +long PyThread_get_thread_ident(void) +{ + if (!initialized) + PyThread_init_thread(); + + return GetCurrentThreadId(); +} + +static void do_PyThread_exit_thread(int no_cleanup) +{ + dprintf(("%ld: PyThread_exit_thread called\n", PyThread_get_thread_ident())); + if (!initialized) + if (no_cleanup) + _exit(0); + else + exit(0); + _endthread(); +} + +void PyThread_exit_thread(void) +{ + do_PyThread_exit_thread(0); +} + +void PyThread__exit_thread(void) +{ + do_PyThread_exit_thread(1); +} + +#ifndef NO_EXIT_PROG +static void do_PyThread_exit_prog(int status, int no_cleanup) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + if (!initialized) + if (no_cleanup) + _exit(status); + else + exit(status); +} + +void PyThread_exit_prog(int status) +{ + do_PyThread_exit_prog(status, 0); +} + +void PyThread__exit_prog(int status) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + +/* + * Lock support. It has too be implemented as semaphores. + * I [Dag] tried to implement it with mutex but I could find a way to + * tell whether a thread already own the lock or not. + */ +PyThread_type_lock PyThread_allocate_lock(void) +{ + PNRMUTEX aLock; + + dprintf(("PyThread_allocate_lock called\n")); + if (!initialized) + PyThread_init_thread(); + + aLock = AllocNonRecursiveMutex() ; + + dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock)); + + return (PyThread_type_lock) aLock; +} + +void PyThread_free_lock(PyThread_type_lock aLock) +{ + dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); + + FreeNonRecursiveMutex(aLock) ; +} + +/* + * Return 1 on success if the lock was acquired + * + * and 0 if the lock was not acquired. This means a 0 is returned + * if the lock has already been acquired by this thread! + */ +int PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag) +{ + int success ; + + dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag)); + + success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag == 1 ? INFINITE : 0)) == WAIT_OBJECT_0 ; + + dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success)); + + return success; +} + +void PyThread_release_lock(PyThread_type_lock aLock) +{ + dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); + + if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock))) + dprintf(("%ld: Could not PyThread_release_lock(%p) error: %l\n", PyThread_get_thread_ident(), aLock, GetLastError())); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_os2.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_os2.h new file mode 100644 index 00000000..a22fcd0c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_os2.h @@ -0,0 +1,272 @@ +/* This code implemented by cvale@netcom.com */ + +#define INCL_DOSPROCESS +#define INCL_DOSSEMAPHORES +#include "os2.h" +#include "limits.h" + +#include "process.h" + +#if defined(PYCC_GCC) +#include +#include +#else +long PyThread_get_thread_ident(void); +#endif + +/* + * Initialization of the C package, should not be needed. + */ +static void +PyThread__init_thread(void) +{ +} + +/* + * Thread support. + */ +long +PyThread_start_new_thread(void (*func)(void *), void *arg) +{ + int aThread; + int success = 0; + + aThread = _beginthread(func,NULL,65536,arg); + + if (aThread == -1) { + success = -1; + fprintf(stderr, "aThread failed == %d", aThread); + dprintf(("_beginthread failed. return %ld\n", errno)); + } + + return success; +} + +long +PyThread_get_thread_ident(void) +{ +#if !defined(PYCC_GCC) + PPIB pib; + PTIB tib; +#endif + + if (!initialized) + PyThread_init_thread(); + +#if defined(PYCC_GCC) + return _gettid(); +#else + DosGetInfoBlocks(&tib, &pib); + return tib->tib_ptib2->tib2_ultid; +#endif +} + +static void +do_PyThread_exit_thread(int no_cleanup) +{ + dprintf(("%ld: PyThread_exit_thread called\n", + PyThread_get_thread_ident())); + if (!initialized) + if (no_cleanup) + _exit(0); + else + exit(0); + _endthread(); +} + +void +PyThread_exit_thread(void) +{ + do_PyThread_exit_thread(0); +} + +void +PyThread__exit_thread(void) +{ + do_PyThread_exit_thread(1); +} + +#ifndef NO_EXIT_PROG +static void +do_PyThread_exit_prog(int status, int no_cleanup) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + if (!initialized) + if (no_cleanup) + _exit(status); + else + exit(status); +} + +void +PyThread_exit_prog(int status) +{ + do_PyThread_exit_prog(status, 0); +} + +void +PyThread__exit_prog(int status) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + +/* + * Lock support. This is implemented with an event semaphore and critical + * sections to make it behave more like a posix mutex than its OS/2 + * counterparts. + */ + +typedef struct os2_lock_t { + int is_set; + HEV changed; +} *type_os2_lock; + +PyThread_type_lock +PyThread_allocate_lock(void) +{ +#if defined(PYCC_GCC) + _fmutex *sem = malloc(sizeof(_fmutex)); + if (!initialized) + PyThread_init_thread(); + dprintf(("%ld: PyThread_allocate_lock() -> %lx\n", + PyThread_get_thread_ident(), + (long)sem)); + if (_fmutex_create(sem, 0)) { + free(sem); + sem = NULL; + } + return (PyThread_type_lock)sem; +#else + APIRET rc; + type_os2_lock lock = (type_os2_lock)malloc(sizeof(struct os2_lock_t)); + + dprintf(("PyThread_allocate_lock called\n")); + if (!initialized) + PyThread_init_thread(); + + lock->is_set = 0; + + DosCreateEventSem(NULL, &lock->changed, 0, 0); + + dprintf(("%ld: PyThread_allocate_lock() -> %p\n", + PyThread_get_thread_ident(), + lock->changed)); + + return (PyThread_type_lock)lock; +#endif +} + +void +PyThread_free_lock(PyThread_type_lock aLock) +{ +#if !defined(PYCC_GCC) + type_os2_lock lock = (type_os2_lock)aLock; +#endif + + dprintf(("%ld: PyThread_free_lock(%p) called\n", + PyThread_get_thread_ident(),aLock)); + +#if defined(PYCC_GCC) + if (aLock) { + _fmutex_close((_fmutex *)aLock); + free((_fmutex *)aLock); + } +#else + DosCloseEventSem(lock->changed); + free(aLock); +#endif +} + +/* + * Return 1 on success if the lock was acquired + * + * and 0 if the lock was not acquired. + */ +int +PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag) +{ +#if !defined(PYCC_GCC) + int done = 0; + ULONG count; + PID pid = 0; + TID tid = 0; + type_os2_lock lock = (type_os2_lock)aLock; +#endif + + dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", + PyThread_get_thread_ident(), + aLock, + waitflag)); + +#if defined(PYCC_GCC) + /* always successful if the lock doesn't exist */ + if (aLock && + _fmutex_request((_fmutex *)aLock, waitflag ? 0 : _FMR_NOWAIT)) + return 0; +#else + while (!done) { + /* if the lock is currently set, we have to wait for + * the state to change + */ + if (lock->is_set) { + if (!waitflag) + return 0; + DosWaitEventSem(lock->changed, SEM_INDEFINITE_WAIT); + } + + /* enter a critical section and try to get the semaphore. If + * it is still locked, we will try again. + */ + if (DosEnterCritSec()) + return 0; + + if (!lock->is_set) { + lock->is_set = 1; + DosResetEventSem(lock->changed, &count); + done = 1; + } + + DosExitCritSec(); + } +#endif + + return 1; +} + +void PyThread_release_lock(PyThread_type_lock aLock) +{ +#if !defined(PYCC_GCC) + type_os2_lock lock = (type_os2_lock)aLock; +#endif + + dprintf(("%ld: PyThread_release_lock(%p) called\n", + PyThread_get_thread_ident(), + aLock)); + +#if defined(PYCC_GCC) + if (aLock) + _fmutex_release((_fmutex *)aLock); +#else + if (!lock->is_set) { + dprintf(("%ld: Could not PyThread_release_lock(%p) error: %l\n", + PyThread_get_thread_ident(), + aLock, + GetLastError())); + return; + } + + if (DosEnterCritSec()) { + dprintf(("%ld: Could not PyThread_release_lock(%p) error: %l\n", + PyThread_get_thread_ident(), + aLock, + GetLastError())); + return; + } + + lock->is_set = 0; + DosPostEventSem(lock->changed); + + DosExitCritSec(); +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_pth.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_pth.h new file mode 100644 index 00000000..afcfb03a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_pth.h @@ -0,0 +1,213 @@ + +/* GNU pth threads interface + http://www.gnu.org/software/pth + 2000-05-03 Andy Dustman + + Adapted from Posix threads interface + 12 May 1997 -- david arnold + */ + +#include +#include +#include + +/* A pth mutex isn't sufficient to model the Python lock type + * because pth mutexes can be acquired multiple times by the + * same thread. + * + * The pth_lock struct implements a Python lock as a "locked?" bit + * and a pair. In general, if the bit can be acquired + * instantly, it is, else the pair is used to block the thread until the + * bit is cleared. + */ + +typedef struct { + char locked; /* 0=unlocked, 1=locked */ + /* a pair to handle an acquire of a locked lock */ + pth_cond_t lock_released; + pth_mutex_t mut; +} pth_lock; + +#define CHECK_STATUS(name) if (status == -1) { printf("%d ", status); perror(name); error = 1; } + +pth_attr_t PyThread_attr; + +/* + * Initialization. + */ + +static void PyThread__init_thread(void) +{ + pth_init(); + PyThread_attr = pth_attr_new(); + pth_attr_set(PyThread_attr, PTH_ATTR_STACK_SIZE, 1<<18); + pth_attr_set(PyThread_attr, PTH_ATTR_JOINABLE, FALSE); +} + +/* + * Thread support. + */ + + +long PyThread_start_new_thread(void (*func)(void *), void *arg) +{ + pth_t th; + dprintf(("PyThread_start_new_thread called\n")); + if (!initialized) + PyThread_init_thread(); + + th = pth_spawn(PyThread_attr, + (void* (*)(void *))func, + (void *)arg + ); + + return th; +} + +long PyThread_get_thread_ident(void) +{ + volatile pth_t threadid; + if (!initialized) + PyThread_init_thread(); + /* Jump through some hoops for Alpha OSF/1 */ + threadid = pth_self(); + return (long) *(long *) &threadid; +} + +static void do_PyThread_exit_thread(int no_cleanup) +{ + dprintf(("PyThread_exit_thread called\n")); + if (!initialized) { + if (no_cleanup) + _exit(0); + else + exit(0); + } +} + +void PyThread_exit_thread(void) +{ + do_PyThread_exit_thread(0); +} + +void PyThread__exit_thread(void) +{ + do_PyThread_exit_thread(1); +} + +#ifndef NO_EXIT_PROG +static void do_PyThread_exit_prog(int status, int no_cleanup) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + if (!initialized) + if (no_cleanup) + _exit(status); + else + exit(status); +} + +void PyThread_exit_prog(int status) +{ + do_PyThread_exit_prog(status, 0); +} + +void PyThread__exit_prog(int status) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + +/* + * Lock support. + */ +PyThread_type_lock PyThread_allocate_lock(void) +{ + pth_lock *lock; + int status, error = 0; + + dprintf(("PyThread_allocate_lock called\n")); + if (!initialized) + PyThread_init_thread(); + + lock = (pth_lock *) malloc(sizeof(pth_lock)); + memset((void *)lock, '\0', sizeof(pth_lock)); + if (lock) { + lock->locked = 0; + status = pth_mutex_init(&lock->mut); + CHECK_STATUS("pth_mutex_init"); + status = pth_cond_init(&lock->lock_released); + CHECK_STATUS("pth_cond_init"); + if (error) { + free((void *)lock); + lock = NULL; + } + } + dprintf(("PyThread_allocate_lock() -> %p\n", lock)); + return (PyThread_type_lock) lock; +} + +void PyThread_free_lock(PyThread_type_lock lock) +{ + pth_lock *thelock = (pth_lock *)lock; + + dprintf(("PyThread_free_lock(%p) called\n", lock)); + + free((void *)thelock); +} + +int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag) +{ + int success; + pth_lock *thelock = (pth_lock *)lock; + int status, error = 0; + + dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag)); + + status = pth_mutex_acquire(&thelock->mut, !waitflag, NULL); + CHECK_STATUS("pth_mutex_acquire[1]"); + success = thelock->locked == 0; + if (success) thelock->locked = 1; + status = pth_mutex_release( &thelock->mut ); + CHECK_STATUS("pth_mutex_release[1]"); + + if ( !success && waitflag ) { + /* continue trying until we get the lock */ + + /* mut must be locked by me -- part of the condition + * protocol */ + status = pth_mutex_acquire( &thelock->mut, !waitflag, NULL ); + CHECK_STATUS("pth_mutex_acquire[2]"); + while ( thelock->locked ) { + status = pth_cond_await(&thelock->lock_released, + &thelock->mut, NULL); + CHECK_STATUS("pth_cond_await"); + } + thelock->locked = 1; + status = pth_mutex_release( &thelock->mut ); + CHECK_STATUS("pth_mutex_release[2]"); + success = 1; + } + if (error) success = 0; + dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success)); + return success; +} + +void PyThread_release_lock(PyThread_type_lock lock) +{ + pth_lock *thelock = (pth_lock *)lock; + int status, error = 0; + + dprintf(("PyThread_release_lock(%p) called\n", lock)); + + status = pth_mutex_acquire( &thelock->mut, 0, NULL ); + CHECK_STATUS("pth_mutex_acquire[3]"); + + thelock->locked = 0; + + status = pth_mutex_release( &thelock->mut ); + CHECK_STATUS("pth_mutex_release[3]"); + + /* wake up someone (anyone, if any) waiting on the lock */ + status = pth_cond_notify( &thelock->lock_released, 0 ); + CHECK_STATUS("pth_cond_notify"); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_pthread.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_pthread.h new file mode 100644 index 00000000..71b6805f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_pthread.h @@ -0,0 +1,545 @@ + +/* Posix threads interface */ + +#include +#include +#if defined(__APPLE__) || defined(HAVE_PTHREAD_DESTRUCTOR) +#define destructor xxdestructor +#endif +#include +#if defined(__APPLE__) || defined(HAVE_PTHREAD_DESTRUCTOR) +#undef destructor +#endif +#include + +/* The POSIX spec says that implementations supporting the sem_* + family of functions must indicate this by defining + _POSIX_SEMAPHORES. */ +#ifdef _POSIX_SEMAPHORES +#include +#include +#endif + + +/* try to determine what version of the Pthread Standard is installed. + * this is important, since all sorts of parameter types changed from + * draft to draft and there are several (incompatible) drafts in + * common use. these macros are a start, at least. + * 12 May 1997 -- david arnold + */ + +#if defined(__ultrix) && defined(__mips) && defined(_DECTHREADS_) +/* _DECTHREADS_ is defined in cma.h which is included by pthread.h */ +# define PY_PTHREAD_D4 +# error Systems with PY_PTHREAD_D4 are unsupported. See README. + +#elif defined(__osf__) && defined (__alpha) +/* _DECTHREADS_ is defined in cma.h which is included by pthread.h */ +# if !defined(_PTHREAD_ENV_ALPHA) || defined(_PTHREAD_USE_D4) || defined(PTHREAD_USE_D4) +# define PY_PTHREAD_D4 +# error Systems with PY_PTHREAD_D4 are unsupported. See README. +# else +# define PY_PTHREAD_STD +# endif + +#elif defined(_AIX) +/* SCHED_BG_NP is defined if using AIX DCE pthreads + * but it is unsupported by AIX 4 pthreads. Default + * attributes for AIX 4 pthreads equal to NULL. For + * AIX DCE pthreads they should be left unchanged. + */ +# if !defined(SCHED_BG_NP) +# define PY_PTHREAD_STD +# else +# define PY_PTHREAD_D7 +# error Systems with PY_PTHREAD_D7 are unsupported. See README. +# endif + +#elif defined(__DGUX) +# define PY_PTHREAD_D6 +# error Systems with PY_PTHREAD_D6 are unsupported. See README. + +#elif defined(__hpux) && defined(_DECTHREADS_) +# define PY_PTHREAD_D4 +# error Systems with PY_PTHREAD_D4 are unsupported. See README. + +#else /* Default case */ +# define PY_PTHREAD_STD + +#endif + +#ifdef USE_GUSI +/* The Macintosh GUSI I/O library sets the stackspace to +** 20KB, much too low. We up it to 64K. +*/ +#define THREAD_STACK_SIZE 0x10000 +#endif + + +/* set default attribute object for different versions */ + +#if defined(PY_PTHREAD_D4) || defined(PY_PTHREAD_D7) +#if !defined(pthread_attr_default) +# define pthread_attr_default pthread_attr_default +#endif +#if !defined(pthread_mutexattr_default) +# define pthread_mutexattr_default pthread_mutexattr_default +#endif +#if !defined(pthread_condattr_default) +# define pthread_condattr_default pthread_condattr_default +#endif +#elif defined(PY_PTHREAD_STD) || defined(PY_PTHREAD_D6) +#if !defined(pthread_attr_default) +# define pthread_attr_default ((pthread_attr_t *)NULL) +#endif +#if !defined(pthread_mutexattr_default) +# define pthread_mutexattr_default ((pthread_mutexattr_t *)NULL) +#endif +#if !defined(pthread_condattr_default) +# define pthread_condattr_default ((pthread_condattr_t *)NULL) +#endif +#endif + + +/* Whether or not to use semaphores directly rather than emulating them with + * mutexes and condition variables: + */ +#if defined(_POSIX_SEMAPHORES) && !defined(HAVE_BROKEN_POSIX_SEMAPHORES) +# define USE_SEMAPHORES +#else +# undef USE_SEMAPHORES +#endif + + +/* On platforms that don't use standard POSIX threads pthread_sigmask() + * isn't present. DEC threads uses sigprocmask() instead as do most + * other UNIX International compliant systems that don't have the full + * pthread implementation. + */ +#if defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK) +# define SET_THREAD_SIGMASK pthread_sigmask +#else +# define SET_THREAD_SIGMASK sigprocmask +#endif + + +/* A pthread mutex isn't sufficient to model the Python lock type + * because, according to Draft 5 of the docs (P1003.4a/D5), both of the + * following are undefined: + * -> a thread tries to lock a mutex it already has locked + * -> a thread tries to unlock a mutex locked by a different thread + * pthread mutexes are designed for serializing threads over short pieces + * of code anyway, so wouldn't be an appropriate implementation of + * Python's locks regardless. + * + * The pthread_lock struct implements a Python lock as a "locked?" bit + * and a pair. In general, if the bit can be acquired + * instantly, it is, else the pair is used to block the thread until the + * bit is cleared. 9 May 1994 tim@ksr.com + */ + +typedef struct { + char locked; /* 0=unlocked, 1=locked */ + /* a pair to handle an acquire of a locked lock */ + pthread_cond_t lock_released; + pthread_mutex_t mut; +} pthread_lock; + +#define CHECK_STATUS(name) if (status != 0) { perror(name); error = 1; } + +/* + * Initialization. + */ + +#ifdef _HAVE_BSDI +static +void _noop(void) +{ +} + +static void +PyThread__init_thread(void) +{ + /* DO AN INIT BY STARTING THE THREAD */ + static int dummy = 0; + pthread_t thread1; + pthread_create(&thread1, NULL, (void *) _noop, &dummy); + pthread_join(thread1, NULL); +} + +#else /* !_HAVE_BSDI */ + +static void +PyThread__init_thread(void) +{ +#if defined(_AIX) && defined(__GNUC__) + pthread_init(); +#endif +} + +#endif /* !_HAVE_BSDI */ + +/* + * Thread support. + */ + + +long +PyThread_start_new_thread(void (*func)(void *), void *arg) +{ + pthread_t th; + int status; + sigset_t oldmask, newmask; +#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED) + pthread_attr_t attrs; +#endif + dprintf(("PyThread_start_new_thread called\n")); + if (!initialized) + PyThread_init_thread(); + +#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED) + pthread_attr_init(&attrs); +#endif +#ifdef THREAD_STACK_SIZE + pthread_attr_setstacksize(&attrs, THREAD_STACK_SIZE); +#endif +#ifdef PTHREAD_SYSTEM_SCHED_SUPPORTED + pthread_attr_setscope(&attrs, PTHREAD_SCOPE_SYSTEM); +#endif + + /* Mask all signals in the current thread before creating the new + * thread. This causes the new thread to start with all signals + * blocked. + */ + sigfillset(&newmask); + SET_THREAD_SIGMASK(SIG_BLOCK, &newmask, &oldmask); + + status = pthread_create(&th, +#if defined(PY_PTHREAD_D4) + pthread_attr_default, + (pthread_startroutine_t)func, + (pthread_addr_t)arg +#elif defined(PY_PTHREAD_D6) + pthread_attr_default, + (void* (*)(void *))func, + arg +#elif defined(PY_PTHREAD_D7) + pthread_attr_default, + func, + arg +#elif defined(PY_PTHREAD_STD) +#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED) + &attrs, +#else + (pthread_attr_t*)NULL, +#endif + (void* (*)(void *))func, + (void *)arg +#endif + ); + + /* Restore signal mask for original thread */ + SET_THREAD_SIGMASK(SIG_SETMASK, &oldmask, NULL); + +#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED) + pthread_attr_destroy(&attrs); +#endif + if (status != 0) + return -1; + +#if defined(PY_PTHREAD_D4) || defined(PY_PTHREAD_D6) || defined(PY_PTHREAD_D7) + pthread_detach(&th); +#elif defined(PY_PTHREAD_STD) + pthread_detach(th); +#endif + +#if SIZEOF_PTHREAD_T <= SIZEOF_LONG + return (long) th; +#else + return (long) *(long *) &th; +#endif +} + +/* XXX This implementation is considered (to quote Tim Peters) "inherently + hosed" because: + - It does not guanrantee the promise that a non-zero integer is returned. + - The cast to long is inherently unsafe. + - It is not clear that the 'volatile' (for AIX?) and ugly casting in the + latter return statement (for Alpha OSF/1) are any longer necessary. +*/ +long +PyThread_get_thread_ident(void) +{ + volatile pthread_t threadid; + if (!initialized) + PyThread_init_thread(); + /* Jump through some hoops for Alpha OSF/1 */ + threadid = pthread_self(); +#if SIZEOF_PTHREAD_T <= SIZEOF_LONG + return (long) threadid; +#else + return (long) *(long *) &threadid; +#endif +} + +static void +do_PyThread_exit_thread(int no_cleanup) +{ + dprintf(("PyThread_exit_thread called\n")); + if (!initialized) { + if (no_cleanup) + _exit(0); + else + exit(0); + } +} + +void +PyThread_exit_thread(void) +{ + do_PyThread_exit_thread(0); +} + +void +PyThread__exit_thread(void) +{ + do_PyThread_exit_thread(1); +} + +#ifndef NO_EXIT_PROG +static void +do_PyThread_exit_prog(int status, int no_cleanup) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + if (!initialized) + if (no_cleanup) + _exit(status); + else + exit(status); +} + +void +PyThread_exit_prog(int status) +{ + do_PyThread_exit_prog(status, 0); +} + +void +PyThread__exit_prog(int status) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + +#ifdef USE_SEMAPHORES + +/* + * Lock support. + */ + +PyThread_type_lock +PyThread_allocate_lock(void) +{ + sem_t *lock; + int status, error = 0; + + dprintf(("PyThread_allocate_lock called\n")); + if (!initialized) + PyThread_init_thread(); + + lock = (sem_t *)malloc(sizeof(sem_t)); + + if (lock) { + status = sem_init(lock,0,1); + CHECK_STATUS("sem_init"); + + if (error) { + free((void *)lock); + lock = NULL; + } + } + + dprintf(("PyThread_allocate_lock() -> %p\n", lock)); + return (PyThread_type_lock)lock; +} + +void +PyThread_free_lock(PyThread_type_lock lock) +{ + sem_t *thelock = (sem_t *)lock; + int status, error = 0; + + dprintf(("PyThread_free_lock(%p) called\n", lock)); + + if (!thelock) + return; + + status = sem_destroy(thelock); + CHECK_STATUS("sem_destroy"); + + free((void *)thelock); +} + +/* + * As of February 2002, Cygwin thread implementations mistakenly report error + * codes in the return value of the sem_ calls (like the pthread_ functions). + * Correct implementations return -1 and put the code in errno. This supports + * either. + */ +static int +fix_status(int status) +{ + return (status == -1) ? errno : status; +} + +int +PyThread_acquire_lock(PyThread_type_lock lock, int waitflag) +{ + int success; + sem_t *thelock = (sem_t *)lock; + int status, error = 0; + + dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag)); + + do { + if (waitflag) + status = fix_status(sem_wait(thelock)); + else + status = fix_status(sem_trywait(thelock)); + } while (status == EINTR); /* Retry if interrupted by a signal */ + + if (waitflag) { + CHECK_STATUS("sem_wait"); + } else if (status != EAGAIN) { + CHECK_STATUS("sem_trywait"); + } + + success = (status == 0) ? 1 : 0; + + dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success)); + return success; +} + +void +PyThread_release_lock(PyThread_type_lock lock) +{ + sem_t *thelock = (sem_t *)lock; + int status, error = 0; + + dprintf(("PyThread_release_lock(%p) called\n", lock)); + + status = sem_post(thelock); + CHECK_STATUS("sem_post"); +} + +#else /* USE_SEMAPHORES */ + +/* + * Lock support. + */ +PyThread_type_lock +PyThread_allocate_lock(void) +{ + pthread_lock *lock; + int status, error = 0; + + dprintf(("PyThread_allocate_lock called\n")); + if (!initialized) + PyThread_init_thread(); + + lock = (pthread_lock *) malloc(sizeof(pthread_lock)); + memset((void *)lock, '\0', sizeof(pthread_lock)); + if (lock) { + lock->locked = 0; + + status = pthread_mutex_init(&lock->mut, + pthread_mutexattr_default); + CHECK_STATUS("pthread_mutex_init"); + + status = pthread_cond_init(&lock->lock_released, + pthread_condattr_default); + CHECK_STATUS("pthread_cond_init"); + + if (error) { + free((void *)lock); + lock = 0; + } + } + + dprintf(("PyThread_allocate_lock() -> %p\n", lock)); + return (PyThread_type_lock) lock; +} + +void +PyThread_free_lock(PyThread_type_lock lock) +{ + pthread_lock *thelock = (pthread_lock *)lock; + int status, error = 0; + + dprintf(("PyThread_free_lock(%p) called\n", lock)); + + status = pthread_mutex_destroy( &thelock->mut ); + CHECK_STATUS("pthread_mutex_destroy"); + + status = pthread_cond_destroy( &thelock->lock_released ); + CHECK_STATUS("pthread_cond_destroy"); + + free((void *)thelock); +} + +int +PyThread_acquire_lock(PyThread_type_lock lock, int waitflag) +{ + int success; + pthread_lock *thelock = (pthread_lock *)lock; + int status, error = 0; + + dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag)); + + status = pthread_mutex_lock( &thelock->mut ); + CHECK_STATUS("pthread_mutex_lock[1]"); + success = thelock->locked == 0; + + if ( !success && waitflag ) { + /* continue trying until we get the lock */ + + /* mut must be locked by me -- part of the condition + * protocol */ + while ( thelock->locked ) { + status = pthread_cond_wait(&thelock->lock_released, + &thelock->mut); + CHECK_STATUS("pthread_cond_wait"); + } + success = 1; + } + if (success) thelock->locked = 1; + status = pthread_mutex_unlock( &thelock->mut ); + CHECK_STATUS("pthread_mutex_unlock[1]"); + + if (error) success = 0; + dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success)); + return success; +} + +void +PyThread_release_lock(PyThread_type_lock lock) +{ + pthread_lock *thelock = (pthread_lock *)lock; + int status, error = 0; + + dprintf(("PyThread_release_lock(%p) called\n", lock)); + + status = pthread_mutex_lock( &thelock->mut ); + CHECK_STATUS("pthread_mutex_lock[3]"); + + thelock->locked = 0; + + status = pthread_mutex_unlock( &thelock->mut ); + CHECK_STATUS("pthread_mutex_unlock[3]"); + + /* wake up someone (anyone, if any) waiting on the lock */ + status = pthread_cond_signal( &thelock->lock_released ); + CHECK_STATUS("pthread_cond_signal"); +} + +#endif /* USE_SEMAPHORES */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_sgi.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_sgi.h new file mode 100644 index 00000000..04e6cbb8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_sgi.h @@ -0,0 +1,379 @@ + +#ifdef WITH_SGI_DL +#define USE_DL +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#define HDR_SIZE 2680 /* sizeof(ushdr_t) */ +#define MAXPROC 100 /* max # of threads that can be started */ + +static usptr_t *shared_arena; +static ulock_t count_lock; /* protection for some variables */ +static ulock_t wait_lock; /* lock used to wait for other threads */ +static int waiting_for_threads; /* protected by count_lock */ +static int nthreads; /* protected by count_lock */ +static int exit_status; +#ifndef NO_EXIT_PROG +static int do_exit; /* indicates that the program is to exit */ +#endif +static int exiting; /* we're already exiting (for maybe_exit) */ +static pid_t my_pid; /* PID of main thread */ +static struct pidlist { + pid_t parent; + pid_t child; +} pidlist[MAXPROC]; /* PIDs of other threads; protected by count_lock */ +static int maxpidindex; /* # of PIDs in pidlist */ + +#ifndef NO_EXIT_PROG +/* + * This routine is called as a signal handler when another thread + * exits. When that happens, we must see whether we have to exit as + * well (because of an PyThread_exit_prog()) or whether we should continue on. + */ +static void exit_sig(void) +{ + d2printf(("exit_sig called\n")); + if (exiting && getpid() == my_pid) { + d2printf(("already exiting\n")); + return; + } + if (do_exit) { + d2printf(("exiting in exit_sig\n")); +#ifdef Py_DEBUG + if ((thread_debug & 8) == 0) + thread_debug &= ~1; /* don't produce debug messages */ +#endif + PyThread_exit_thread(); + } +} + +/* + * This routine is called when a process calls exit(). If that wasn't + * done from the library, we do as if an PyThread_exit_prog() was intended. + */ +static void maybe_exit(void) +{ + dprintf(("maybe_exit called\n")); + if (exiting) { + dprintf(("already exiting\n")); + return; + } + PyThread_exit_prog(0); +} +#endif /* NO_EXIT_PROG */ + +/* + * Initialization. + */ +static void PyThread__init_thread(void) +{ +#ifndef NO_EXIT_PROG + struct sigaction s; +#endif /* NO_EXIT_PROG */ +#ifdef USE_DL + long addr, size; +#endif /* USE_DL */ + + +#ifdef USE_DL + if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0) + perror("usconfig - CONF_INITSIZE (check)"); + if (usconfig(CONF_INITSIZE, size) < 0) + perror("usconfig - CONF_INITSIZE (reset)"); + addr = (long) dl_getrange(size + HDR_SIZE); + dprintf(("trying to use addr %p-%p for shared arena\n", addr, addr+size)); + errno = 0; + if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 && errno != 0) + perror("usconfig - CONF_ATTACHADDR (set)"); +#endif /* USE_DL */ + if (usconfig(CONF_INITUSERS, 16) < 0) + perror("usconfig - CONF_INITUSERS"); + my_pid = getpid(); /* so that we know which is the main thread */ +#ifndef NO_EXIT_PROG + atexit(maybe_exit); + s.sa_handler = exit_sig; + sigemptyset(&s.sa_mask); + /*sigaddset(&s.sa_mask, SIGUSR1);*/ + s.sa_flags = 0; + sigaction(SIGUSR1, &s, 0); + if (prctl(PR_SETEXITSIG, SIGUSR1) < 0) + perror("prctl - PR_SETEXITSIG"); +#endif /* NO_EXIT_PROG */ + if (usconfig(CONF_ARENATYPE, US_SHAREDONLY) < 0) + perror("usconfig - CONF_ARENATYPE"); + usconfig(CONF_LOCKTYPE, US_DEBUG); /* XXX */ +#ifdef Py_DEBUG + if (thread_debug & 4) + usconfig(CONF_LOCKTYPE, US_DEBUGPLUS); + else if (thread_debug & 2) + usconfig(CONF_LOCKTYPE, US_DEBUG); +#endif /* Py_DEBUG */ + if ((shared_arena = usinit(tmpnam(0))) == 0) + perror("usinit"); +#ifdef USE_DL + if (usconfig(CONF_ATTACHADDR, addr) < 0) /* reset address */ + perror("usconfig - CONF_ATTACHADDR (reset)"); +#endif /* USE_DL */ + if ((count_lock = usnewlock(shared_arena)) == NULL) + perror("usnewlock (count_lock)"); + (void) usinitlock(count_lock); + if ((wait_lock = usnewlock(shared_arena)) == NULL) + perror("usnewlock (wait_lock)"); + dprintf(("arena start: %p, arena size: %ld\n", shared_arena, (long) usconfig(CONF_GETSIZE, shared_arena))); +} + +/* + * Thread support. + */ + +static void clean_threads(void) +{ + int i, j; + pid_t mypid, pid; + + /* clean up any exited threads */ + mypid = getpid(); + i = 0; + while (i < maxpidindex) { + if (pidlist[i].parent == mypid && (pid = pidlist[i].child) > 0) { + pid = waitpid(pid, 0, WNOHANG); + if (pid > 0) { + /* a thread has exited */ + pidlist[i] = pidlist[--maxpidindex]; + /* remove references to children of dead proc */ + for (j = 0; j < maxpidindex; j++) + if (pidlist[j].parent == pid) + pidlist[j].child = -1; + continue; /* don't increment i */ + } + } + i++; + } + /* clean up the list */ + i = 0; + while (i < maxpidindex) { + if (pidlist[i].child == -1) { + pidlist[i] = pidlist[--maxpidindex]; + continue; /* don't increment i */ + } + i++; + } +} + +long PyThread_start_new_thread(void (*func)(void *), void *arg) +{ +#ifdef USE_DL + long addr, size; + static int local_initialized = 0; +#endif /* USE_DL */ + int success = 0; /* init not needed when SOLARIS_THREADS and */ + /* C_THREADS implemented properly */ + + dprintf(("PyThread_start_new_thread called\n")); + if (!initialized) + PyThread_init_thread(); + switch (ussetlock(count_lock)) { + case 0: return 0; + case -1: perror("ussetlock (count_lock)"); + } + if (maxpidindex >= MAXPROC) + success = -1; + else { +#ifdef USE_DL + if (!local_initialized) { + if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0) + perror("usconfig - CONF_INITSIZE (check)"); + if (usconfig(CONF_INITSIZE, size) < 0) + perror("usconfig - CONF_INITSIZE (reset)"); + addr = (long) dl_getrange(size + HDR_SIZE); + dprintf(("trying to use addr %p-%p for sproc\n", + addr, addr+size)); + errno = 0; + if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 && + errno != 0) + perror("usconfig - CONF_ATTACHADDR (set)"); + } +#endif /* USE_DL */ + clean_threads(); + if ((success = sproc(func, PR_SALL, arg)) < 0) + perror("sproc"); +#ifdef USE_DL + if (!local_initialized) { + if (usconfig(CONF_ATTACHADDR, addr) < 0) + /* reset address */ + perror("usconfig - CONF_ATTACHADDR (reset)"); + local_initialized = 1; + } +#endif /* USE_DL */ + if (success >= 0) { + nthreads++; + pidlist[maxpidindex].parent = getpid(); + pidlist[maxpidindex++].child = success; + dprintf(("pidlist[%d] = %d\n", + maxpidindex-1, success)); + } + } + if (usunsetlock(count_lock) < 0) + perror("usunsetlock (count_lock)"); + return success; +} + +long PyThread_get_thread_ident(void) +{ + return getpid(); +} + +static void do_PyThread_exit_thread(int no_cleanup) +{ + dprintf(("PyThread_exit_thread called\n")); + if (!initialized) + if (no_cleanup) + _exit(0); + else + exit(0); + if (ussetlock(count_lock) < 0) + perror("ussetlock (count_lock)"); + nthreads--; + if (getpid() == my_pid) { + /* main thread; wait for other threads to exit */ + exiting = 1; +#ifndef NO_EXIT_PROG + if (do_exit) { + int i; + + /* notify other threads */ + clean_threads(); + if (nthreads >= 0) { + dprintf(("kill other threads\n")); + for (i = 0; i < maxpidindex; i++) + if (pidlist[i].child > 0) + (void) kill(pidlist[i].child, + SIGKILL); + _exit(exit_status); + } + } +#endif /* NO_EXIT_PROG */ + waiting_for_threads = 1; + if (ussetlock(wait_lock) < 0) + perror("ussetlock (wait_lock)"); + for (;;) { + if (nthreads < 0) { + dprintf(("really exit (%d)\n", exit_status)); + if (no_cleanup) + _exit(exit_status); + else + exit(exit_status); + } + if (usunsetlock(count_lock) < 0) + perror("usunsetlock (count_lock)"); + dprintf(("waiting for other threads (%d)\n", nthreads)); + if (ussetlock(wait_lock) < 0) + perror("ussetlock (wait_lock)"); + if (ussetlock(count_lock) < 0) + perror("ussetlock (count_lock)"); + } + } + /* not the main thread */ + if (waiting_for_threads) { + dprintf(("main thread is waiting\n")); + if (usunsetlock(wait_lock) < 0) + perror("usunsetlock (wait_lock)"); + } +#ifndef NO_EXIT_PROG + else if (do_exit) + (void) kill(my_pid, SIGUSR1); +#endif /* NO_EXIT_PROG */ + if (usunsetlock(count_lock) < 0) + perror("usunsetlock (count_lock)"); + _exit(0); +} + +void PyThread_exit_thread(void) +{ + do_PyThread_exit_thread(0); +} + +void PyThread__exit_thread(void) +{ + do_PyThread_exit_thread(1); +} + +#ifndef NO_EXIT_PROG +static void do_PyThread_exit_prog(int status, int no_cleanup) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + if (!initialized) + if (no_cleanup) + _exit(status); + else + exit(status); + do_exit = 1; + exit_status = status; + do_PyThread_exit_thread(no_cleanup); +} + +void PyThread_exit_prog(int status) +{ + do_PyThread_exit_prog(status, 0); +} + +void PyThread__exit_prog(int status) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + +/* + * Lock support. + */ +PyThread_type_lock PyThread_allocate_lock(void) +{ + ulock_t lock; + + dprintf(("PyThread_allocate_lock called\n")); + if (!initialized) + PyThread_init_thread(); + + if ((lock = usnewlock(shared_arena)) == NULL) + perror("usnewlock"); + (void) usinitlock(lock); + dprintf(("PyThread_allocate_lock() -> %p\n", lock)); + return (PyThread_type_lock) lock; +} + +void PyThread_free_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_free_lock(%p) called\n", lock)); + usfreelock((ulock_t) lock, shared_arena); +} + +int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag) +{ + int success; + + dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag)); + errno = 0; /* clear it just in case */ + if (waitflag) + success = ussetlock((ulock_t) lock); + else + success = uscsetlock((ulock_t) lock, 1); /* Try it once */ + if (success < 0) + perror(waitflag ? "ussetlock" : "uscsetlock"); + dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success)); + return success; +} + +void PyThread_release_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_release_lock(%p) called\n", lock)); + if (usunsetlock((ulock_t) lock) < 0) + perror("usunsetlock"); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_solaris.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_solaris.h new file mode 100644 index 00000000..fee210f4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_solaris.h @@ -0,0 +1,174 @@ + +#include +#include +#include +#include +#undef _POSIX_THREADS + + +/* + * Initialization. + */ +static void PyThread__init_thread(void) +{ +} + +/* + * Thread support. + */ +struct func_arg { + void (*func)(void *); + void *arg; +}; + +static void * +new_func(void *funcarg) +{ + void (*func)(void *); + void *arg; + + func = ((struct func_arg *) funcarg)->func; + arg = ((struct func_arg *) funcarg)->arg; + free(funcarg); + (*func)(arg); + return 0; +} + + +long +PyThread_start_new_thread(void (*func)(void *), void *arg) +{ + thread_t tid; + struct func_arg *funcarg; + + dprintf(("PyThread_start_new_thread called\n")); + if (!initialized) + PyThread_init_thread(); + funcarg = (struct func_arg *) malloc(sizeof(struct func_arg)); + funcarg->func = func; + funcarg->arg = arg; + if (thr_create(0, 0, new_func, funcarg, + THR_DETACHED | THR_NEW_LWP, &tid)) { + perror("thr_create"); + free((void *) funcarg); + return -1; + } + return tid; +} + +long +PyThread_get_thread_ident(void) +{ + if (!initialized) + PyThread_init_thread(); + return thr_self(); +} + +static void +do_PyThread_exit_thread(int no_cleanup) +{ + dprintf(("PyThread_exit_thread called\n")); + if (!initialized) + if (no_cleanup) + _exit(0); + else + exit(0); + thr_exit(0); +} + +void +PyThread_exit_thread(void) +{ + do_PyThread_exit_thread(0); +} + +void +PyThread__exit_thread(void) +{ + do_PyThread_exit_thread(1); +} + +#ifndef NO_EXIT_PROG +static void +do_PyThread_exit_prog(int status, int no_cleanup) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + if (!initialized) + if (no_cleanup) + _exit(status); + else + exit(status); + if (no_cleanup) + _exit(status); + else + exit(status); +} + +void +PyThread_exit_prog(int status) +{ + do_PyThread_exit_prog(status, 0); +} + +void +PyThread__exit_prog(int status) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + +/* + * Lock support. + */ +PyThread_type_lock +PyThread_allocate_lock(void) +{ + mutex_t *lock; + + dprintf(("PyThread_allocate_lock called\n")); + if (!initialized) + PyThread_init_thread(); + + lock = (mutex_t *) malloc(sizeof(mutex_t)); + if (mutex_init(lock, USYNC_THREAD, 0)) { + perror("mutex_init"); + free((void *) lock); + lock = 0; + } + dprintf(("PyThread_allocate_lock() -> %p\n", lock)); + return (PyThread_type_lock) lock; +} + +void +PyThread_free_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_free_lock(%p) called\n", lock)); + mutex_destroy((mutex_t *) lock); + free((void *) lock); +} + +int +PyThread_acquire_lock(PyThread_type_lock lock, int waitflag) +{ + int success; + + dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag)); + if (waitflag) + success = mutex_lock((mutex_t *) lock); + else + success = mutex_trylock((mutex_t *) lock); + if (success < 0) + perror(waitflag ? "mutex_lock" : "mutex_trylock"); + else + success = !success; /* solaris does it the other way round */ + dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success)); + return success; +} + +void +PyThread_release_lock(PyThread_type_lock lock) +{ + dprintf(("PyThread_release_lock(%p) called\n", lock)); + if (mutex_unlock((mutex_t *) lock)) + perror("mutex_unlock"); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_wince.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_wince.h new file mode 100644 index 00000000..44d5ee1a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/thread_wince.h @@ -0,0 +1,171 @@ + +/* This code implemented by Mark Hammond (MHammond@skippinet.com.au) */ + +#include +#include +#include + +long PyThread_get_thread_ident(void); + +/* + * Change all headers to pure ANSI as no one will use K&R style on an + * NT + */ + +/* + * Initialization of the C package, should not be needed. + */ +static void PyThread__init_thread(void) +{ +} + +/* + * Thread support. + */ +long PyThread_start_new_thread(void (*func)(void *), void *arg) +{ + long rv; + int success = -1; + + dprintf(("%ld: PyThread_start_new_thread called\n", PyThread_get_thread_ident())); + if (!initialized) + PyThread_init_thread(); + + rv = _beginthread(func, 0, arg); /* use default stack size */ + + if (rv != -1) { + success = 0; + dprintf(("%ld: PyThread_start_new_thread succeeded:\n", PyThread_get_thread_ident())); + } + + return success; +} + +/* + * Return the thread Id instead of an handle. The Id is said to uniquely identify the + * thread in the system + */ +long PyThread_get_thread_ident(void) +{ + if (!initialized) + PyThread_init_thread(); + + return GetCurrentThreadId(); +} + +static void do_PyThread_exit_thread(int no_cleanup) +{ + dprintf(("%ld: do_PyThread_exit_thread called\n", PyThread_get_thread_ident())); + if (!initialized) + if (no_cleanup) + exit(0); /* XXX - was _exit()!! */ + else + exit(0); + _endthread(); +} + +void PyThread_exit_thread(void) +{ + do_PyThread_exit_thread(0); +} + +void PyThread__exit_thread(void) +{ + do_PyThread_exit_thread(1); +} + +#ifndef NO_EXIT_PROG +static void do_PyThread_exit_prog(int status, int no_cleanup) +{ + dprintf(("PyThread_exit_prog(%d) called\n", status)); + if (!initialized) + if (no_cleanup) + _exit(status); + else + exit(status); +} + +void PyThread_exit_prog(int status) +{ + do_PyThread_exit_prog(status, 0); +} + +void PyThread__exit_prog(int status) +{ + do_PyThread_exit_prog(status, 1); +} +#endif /* NO_EXIT_PROG */ + +/* + * Lock support. It has to be implemented using Mutexes, as + * CE doesnt support semaphores. Therefore we use some hacks to + * simulate the non reentrant requirements of Python locks + */ +PyThread_type_lock PyThread_allocate_lock(void) +{ + HANDLE aLock; + + dprintf(("PyThread_allocate_lock called\n")); + if (!initialized) + PyThread_init_thread(); + + aLock = CreateEvent(NULL, /* Security attributes */ + 0, /* Manual-Reset */ + 1, /* Is initially signalled */ + NULL); /* Name of event */ + + dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock)); + + return (PyThread_type_lock) aLock; +} + +void PyThread_free_lock(PyThread_type_lock aLock) +{ + dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); + + CloseHandle(aLock); +} + +/* + * Return 1 on success if the lock was acquired + * + * and 0 if the lock was not acquired. This means a 0 is returned + * if the lock has already been acquired by this thread! + */ +int PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag) +{ + int success = 1; + DWORD waitResult; + + dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag)); + +#ifndef DEBUG + waitResult = WaitForSingleObject(aLock, (waitflag == 1 ? INFINITE : 0)); +#else + /* To aid in debugging, we regularly wake up. This allows us to + break into the debugger */ + while (TRUE) { + waitResult = WaitForSingleObject(aLock, waitflag ? 3000 : 0); + if (waitflag==0 || (waitflag==1 && waitResult == WAIT_OBJECT_0)) + break; + } +#endif + + if (waitResult != WAIT_OBJECT_0) { + success = 0; /* We failed */ + } + + dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success)); + + return success; +} + +void PyThread_release_lock(PyThread_type_lock aLock) +{ + dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); + + if (!SetEvent(aLock)) + dprintf(("%ld: Could not PyThread_release_lock(%p) error: %l\n", PyThread_get_thread_ident(), aLock, GetLastError())); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/traceback.c b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/traceback.c new file mode 100644 index 00000000..96333be2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/Python/traceback.c @@ -0,0 +1,277 @@ + +/* Traceback implementation */ + +#include "Python.h" + +#include "compile.h" +#include "frameobject.h" +#include "structmember.h" +#include "osdefs.h" + +typedef struct _tracebackobject { + PyObject_HEAD + struct _tracebackobject *tb_next; + PyFrameObject *tb_frame; + int tb_lasti; + int tb_lineno; +} tracebackobject; + +#define OFF(x) offsetof(tracebackobject, x) + +static struct memberlist tb_memberlist[] = { + {"tb_next", T_OBJECT, OFF(tb_next)}, + {"tb_frame", T_OBJECT, OFF(tb_frame)}, + {"tb_lasti", T_INT, OFF(tb_lasti)}, + {"tb_lineno", T_INT, OFF(tb_lineno)}, + {NULL} /* Sentinel */ +}; + +static PyObject * +tb_getattr(tracebackobject *tb, char *name) +{ + return PyMember_Get((char *)tb, tb_memberlist, name); +} + +static void +tb_dealloc(tracebackobject *tb) +{ + PyObject_GC_UnTrack(tb); + Py_TRASHCAN_SAFE_BEGIN(tb) + Py_XDECREF(tb->tb_next); + Py_XDECREF(tb->tb_frame); + PyObject_GC_Del(tb); + Py_TRASHCAN_SAFE_END(tb) +} + +static int +tb_traverse(tracebackobject *tb, visitproc visit, void *arg) +{ + int err = 0; + if (tb->tb_next) { + err = visit((PyObject *)tb->tb_next, arg); + if (err) + return err; + } + if (tb->tb_frame) + err = visit((PyObject *)tb->tb_frame, arg); + return err; +} + +static void +tb_clear(tracebackobject *tb) +{ + Py_XDECREF(tb->tb_next); + Py_XDECREF(tb->tb_frame); + tb->tb_next = NULL; + tb->tb_frame = NULL; +} + +PyTypeObject PyTraceBack_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "traceback", + sizeof(tracebackobject), + 0, + (destructor)tb_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)tb_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ + 0, /* tp_doc */ + (traverseproc)tb_traverse, /* tp_traverse */ + (inquiry)tb_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ +}; + +static tracebackobject * +newtracebackobject(tracebackobject *next, PyFrameObject *frame) +{ + tracebackobject *tb; + if ((next != NULL && !PyTraceBack_Check(next)) || + frame == NULL || !PyFrame_Check(frame)) { + PyErr_BadInternalCall(); + return NULL; + } + tb = PyObject_GC_New(tracebackobject, &PyTraceBack_Type); + if (tb != NULL) { + Py_XINCREF(next); + tb->tb_next = next; + Py_XINCREF(frame); + tb->tb_frame = frame; + tb->tb_lasti = frame->f_lasti; + tb->tb_lineno = PyCode_Addr2Line(frame->f_code, + frame->f_lasti); + PyObject_GC_Track(tb); + } + return tb; +} + +int +PyTraceBack_Here(PyFrameObject *frame) +{ + PyThreadState *tstate = frame->f_tstate; + tracebackobject *oldtb = (tracebackobject *) tstate->curexc_traceback; + tracebackobject *tb = newtracebackobject(oldtb, frame); + if (tb == NULL) + return -1; + tstate->curexc_traceback = (PyObject *)tb; + Py_XDECREF(oldtb); + return 0; +} + +static int +tb_displayline(PyObject *f, char *filename, int lineno, char *name) +{ + int err = 0; + FILE *xfp; + char linebuf[2000]; + int i; + if (filename == NULL || name == NULL) + return -1; +#ifdef MPW + /* This is needed by MPW's File and Line commands */ +#define FMT " File \"%.500s\"; line %d # in %.500s\n" +#else + /* This is needed by Emacs' compile command */ +#define FMT " File \"%.500s\", line %d, in %.500s\n" +#endif + xfp = fopen(filename, "r" PY_STDIOTEXTMODE); + if (xfp == NULL) { + /* Search tail of filename in sys.path before giving up */ + PyObject *path; + char *tail = strrchr(filename, SEP); + if (tail == NULL) + tail = filename; + else + tail++; + path = PySys_GetObject("path"); + if (path != NULL && PyList_Check(path)) { + int npath = PyList_Size(path); + size_t taillen = strlen(tail); + char namebuf[MAXPATHLEN+1]; + for (i = 0; i < npath; i++) { + PyObject *v = PyList_GetItem(path, i); + if (v == NULL) { + PyErr_Clear(); + break; + } + if (PyString_Check(v)) { + size_t len; + len = PyString_Size(v); + if (len + 1 + taillen >= MAXPATHLEN) + continue; /* Too long */ + strcpy(namebuf, PyString_AsString(v)); + if (strlen(namebuf) != len) + continue; /* v contains '\0' */ + if (len > 0 && namebuf[len-1] != SEP) + namebuf[len++] = SEP; + strcpy(namebuf+len, tail); + xfp = fopen(namebuf, "r" PY_STDIOTEXTMODE); + if (xfp != NULL) { + filename = namebuf; + break; + } + } + } + } + } + PyOS_snprintf(linebuf, sizeof(linebuf), FMT, filename, lineno, name); + err = PyFile_WriteString(linebuf, f); + if (xfp == NULL || err != 0) + return err; + for (i = 0; i < lineno; i++) { + char* pLastChar = &linebuf[sizeof(linebuf)-2]; + do { + *pLastChar = '\0'; + if (Py_UniversalNewlineFgets(linebuf, sizeof linebuf, xfp, NULL) == NULL) + break; + /* fgets read *something*; if it didn't get as + far as pLastChar, it must have found a newline + or hit the end of the file; if pLastChar is \n, + it obviously found a newline; else we haven't + yet seen a newline, so must continue */ + } while (*pLastChar != '\0' && *pLastChar != '\n'); + } + if (i == lineno) { + char *p = linebuf; + while (*p == ' ' || *p == '\t' || *p == '\014') + p++; + err = PyFile_WriteString(" ", f); + if (err == 0) { + err = PyFile_WriteString(p, f); + if (err == 0 && strchr(p, '\n') == NULL) + err = PyFile_WriteString("\n", f); + } + } + fclose(xfp); + return err; +} + +static int +tb_printinternal(tracebackobject *tb, PyObject *f, int limit) +{ + int err = 0; + int depth = 0; + tracebackobject *tb1 = tb; + while (tb1 != NULL) { + depth++; + tb1 = tb1->tb_next; + } + while (tb != NULL && err == 0) { + if (depth <= limit) { + err = tb_displayline(f, + PyString_AsString( + tb->tb_frame->f_code->co_filename), + tb->tb_lineno, + PyString_AsString(tb->tb_frame->f_code->co_name)); + } + depth--; + tb = tb->tb_next; + if (err == 0) + err = PyErr_CheckSignals(); + } + return err; +} + +int +PyTraceBack_Print(PyObject *v, PyObject *f) +{ + int err; + PyObject *limitv; + int limit = 1000; + if (v == NULL) + return 0; + if (!PyTraceBack_Check(v)) { + PyErr_BadInternalCall(); + return -1; + } + limitv = PySys_GetObject("tracebacklimit"); + if (limitv && PyInt_Check(limitv)) { + limit = PyInt_AsLong(limitv); + if (limit <= 0) + return 0; + } + err = PyFile_WriteString("Traceback (most recent call last):\n", f); + if (!err) + err = tb_printinternal((tracebackobject *)v, f, limit); + return err; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/README b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/README new file mode 100644 index 00000000..7f6b0edd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/README @@ -0,0 +1,1199 @@ +This is Python version 2.3.3 +============================ + +Copyright (c) 2001, 2002, 2003 Python Software Foundation. +All rights reserved. + +Copyright (c) 2000 BeOpen.com. +All rights reserved. + +Copyright (c) 1995-2001 Corporation for National Research Initiatives. +All rights reserved. + +Copyright (c) 1991-1995 Stichting Mathematisch Centrum. +All rights reserved. + + +License information +------------------- + +See the file "LICENSE" for information on the history of this +software, terms & conditions for usage, and a DISCLAIMER OF ALL +WARRANTIES. + +This Python distribution contains no GNU General Public Licensed +(GPLed) code so it may be used in proprietary projects just like prior +Python distributions. There are interfaces to some GNU code but these +are entirely optional. + +All trademarks referenced herein are property of their respective +holders. + + +What's new in this release? +--------------------------- + +See the file "Misc/NEWS". + + +If you don't read instructions +------------------------------ + +Congratulations on getting this far. :-) + +To start building right away (on UNIX): type "./configure" in the +current directory and when it finishes, type "make". This creates an +executable "./python"; to install in /usr/local, first do "su root" +and then "make install". + +The section `Build instructions' below is still recommended reading, +especially the part on customizing Modules/Setup. + + +What is Python anyway? +---------------------- + +Python is an interpreted object-oriented programming language suitable +(amongst other uses) for distributed application development, +scripting, numeric computing and system testing. Python is often +compared to Tcl, Perl, Java, JavaScript, Visual Basic or Scheme. To +find out more about what Python can do for you, point your browser to +http://www.python.org/. + + +How do I learn Python? +---------------------- + +The official tutorial is still a good place to start; see +http://www.python.org/doc/ for online and downloadable versions, as +well as a list of other introductions, and reference documentation. + +There's a quickly growing set of books on Python. See +http://www.python.org/cgi-bin/moinmoin/PythonBooks for a list. + + +Documentation +------------- + +All documentation is provided online in a variety of formats. In +order of importance for new users: Tutorial, Library Reference, +Language Reference, Extending & Embedding, and the Python/C API. The +Library Reference is especially of immense value since much of +Python's power is described there, including the built-in data types +and functions! + +All documentation is also available online at the Python web site +(http://www.python.org/doc/, see below). It is available online for +occasional reference, or can be downloaded in many formats for faster +access. The documentation is available in HTML, PostScript, PDF, and +LaTeX formats; the LaTeX version is primarily for documentation +authors, translators, and people with special formatting requirements. + +The best documentation for the new (in Python 2.2) type/class +unification features is Guido's tutorial introduction, at + + http://www.python.org/2.2.1/descrintro.html + + +Web sites +--------- + +New Python releases and related technologies are published at +http://www.python.org/. Come visit us! + +There's also a Python community web site at +http://starship.python.net/. + + +Newsgroups and Mailing Lists +---------------------------- + +Read comp.lang.python, a high-volume discussion newsgroup about +Python, or comp.lang.python.announce, a low-volume moderated newsgroup +for Python-related announcements. These are also accessible as +mailing lists: see http://www.python.org/psa/MailingLists.html for an +overview of the many Python-related mailing lists. + +Archives are accessible via the Google Groups usenet archive; see +http://groups.google.com/. The mailing lists are also archived, see +http://www.python.org/psa/MailingLists.html for details. + + +Bug reports +----------- + +To report or search for bugs, please use the Python Bug +Tracker at http://sourceforge.net/bugs/?group_id=5470. + + +Patches and contributions +------------------------- + +To submit a patch or other contribution, please use the Python Patch +Manager at http://sourceforge.net/patch/?group_id=5470. Guidelines +for patch submission may be found at http://www.python.org/patches/. + +If you have a proposal to change Python, it's best to submit a Python +Enhancement Proposal (PEP) first. All current PEPs, as well as +guidelines for submitting a new PEP, are listed at +http://python.sourceforge.net/peps/. + + +Questions +--------- + +For help, if you can't find it in the manuals or on the web site, it's +best to post to the comp.lang.python or the Python mailing list (see +above). If you specifically don't want to involve the newsgroup or +mailing list, send questions to help@python.org (a group of volunteers +who answer questions as they can). The newsgroup is the most +efficient way to ask public questions. + + +Build instructions +================== + +Before you can build Python, you must first configure it. +Fortunately, the configuration and build process has been automated +for Unix and Linux installations, so all you usually have to do is +type a few commands and sit back. There are some platforms where +things are not quite as smooth; see the platform specific notes below. +If you want to build for multiple platforms sharing the same source +tree, see the section on VPATH below. + +Start by running the script "./configure", which determines your +system configuration and creates the Makefile. (It takes a minute or +two -- please be patient!) You may want to pass options to the +configure script -- see the section below on configuration options and +variables. When it's done, you are ready to run make. + +To build Python, you normally type "make" in the toplevel directory. +If you have changed the configuration, the Makefile may have to be +rebuilt. In this case you may have to run make again to correctly +build your desired target. The interpreter executable is built in the +top level directory. + +Once you have built a Python interpreter, see the subsections below on +testing and installation. If you run into trouble, see the next +section. + +Previous versions of Python used a manual configuration process that +involved editing the file Modules/Setup. While this file still exists +and manual configuration is still supported, it is rarely needed any +more: almost all modules are automatically built as appropriate under +guidance of the setup.py script, which is run by Make after the +interpreter has been built. + + +Troubleshooting +--------------- + +See also the platform specific notes in the next section. + +If you run into other trouble, see section 3 of the FAQ +(http://www.python.org/cgi-bin/faqw.py or +http://www.python.org/doc/FAQ.html) for hints on what can go wrong, +and how to fix it. + +If you rerun the configure script with different options, remove all +object files by running "make clean" before rebuilding. Believe it or +not, "make clean" sometimes helps to clean up other inexplicable +problems as well. Try it before sending in a bug report! + +If the configure script fails or doesn't seem to find things that +should be there, inspect the config.log file. When you fix a +configure problem, be sure to remove config.cache! + +If you get a warning for every file about the -Olimit option being no +longer supported, you can ignore it. There's no foolproof way to know +whether this option is needed; all we can do is test whether it is +accepted without error. On some systems, e.g. older SGI compilers, it +is essential for performance (specifically when compiling ceval.c, +which has more basic blocks than the default limit of 1000). If the +warning bothers you, edit the Makefile to remove "-Olimit 1500" from +the OPT variable. + +If you get failures in test_long, or sys.maxint gets set to -1, you +are probably experiencing compiler bugs, usually related to +optimization. This is a common problem with some versions of gcc, and +some vendor-supplied compilers, which can sometimes be worked around +by turning off optimization. Consider switching to stable versions +(gcc 2.95.2, or contact your vendor.) + +From Python 2.0 onward, all Python C code is ANSI C. Compiling using +old K&R-C-only compilers is no longer possible. ANSI C compilers are +available for all modern systems, either in the form of updated +compilers from the vendor, or one of the free compilers (gcc). + + +Unsupported systems +------------------- + +A number of features are not supported in Python 2.3 anymore. Some +support code is still present, but will be removed in Python 2.4. +If you still need to use current Python versions on these systems, +please send a message to python-dev@python.org indicating that you +volunteer to support this system. + +More specifically, the following systems are not supported any +longer: +- SunOS 4 +- DYNIX +- dgux +- Minix +- Irix 4 and --with-sgi-dl +- Linux 1 +- Systems defining __d6_pthread_create (configure.in) +- Systems defining PY_PTHREAD_D4, PY_PTHREAD_D6, + or PY_PTHREAD_D7 in thread_pthread.h +- Systems using --with-dl-dld +- Systems using --without-universal-newlines + + +Platform specific notes +----------------------- + +(Some of these may no longer apply. If you find you can build Python +on these platforms without the special directions mentioned here, +submit a documentation bug report to SourceForge (see Bug Reports +above) so we can remove them!) + +Unix platforms: If your vendor still ships (and you still use) Berkeley DB + 1.85 you will need to edit Modules/Setup to build the bsddb185 + module and add a line to sitecustomize.py which makes it the + default. In Modules/Setup a line like + + bsddb185 bsddbmodule.c + + should work. (You may need to add -I, -L or -l flags to direct the + compiler and linker to your include files and libraries.) You can + then force it to be the version people import by adding + + import bsddb185 as bsddb + + in sitecustomize.py. + +64-bit platforms: The modules audioop, imageop and rgbimg don't work. + The setup.py script disables them on 64-bit installations. + Don't try to enable them in the Modules/Setup file. They + contain code that is quite wordsize sensitive. (If you have a + fix, let us know!) + +Solaris: When using Sun's C compiler with threads, at least on Solaris + 2.5.1, you need to add the "-mt" compiler option (the simplest + way is probably to specify the compiler with this option as + the "CC" environment variable when running the configure + script). + + When using GCC on Solaris, beware of binutils 2.13 or GCC + versions built using it. This mistakenly enables the + -zcombreloc option which creates broken shared libraries on + Solaris. binutils 2.12 works, and the binutils maintainers + are aware of the problem. Binutils 2.13.1 only partially + fixed things. It appears that 2.13.2 solves the problem + completely. This problem is known to occur with Solaris 2.7 + and 2.8, but may also affect earlier and later versions of the + OS. + + When the dynamic loader complains about errors finding shared + libraries, such as + + ld.so.1: ./python: fatal: libstdc++.so.5: open failed: + No such file or directory + + you need to first make sure that the library is available on + your system. Then, you need to instruct the dynamic loader how + to find it. You can choose any of the following strategies: + + 1. When compiling Python, set LD_RUN_PATH to the directories + containing missing libraries. + 2. When running Python, set LD_LIBRARY_PATH to these directories. + 3. Use crle(8) to extend the search path of the loader. + 4. Modify the installed GCC specs file, adding -R options into the + *link: section. + +Linux: A problem with threads and fork() was tracked down to a bug in + the pthreads code in glibc version 2.0.5; glibc version 2.0.7 + solves the problem. This causes the popen2 test to fail; + problem and solution reported by Pablo Bleyer. + + Under Linux systems using GNU libc 2 (aka libc6), the crypt + module now needs the -lcrypt option. The setup.py script + takes care of this automatically. + +Red Hat Linux: Red Hat 9 built Python2.2 in UCS-4 mode and hacked + Tcl to support it. To compile Python2.3 with Tkinter, you will + need to pass --enable-unicode=ucs4 flag to ./configure. + + There's an executable /usr/bin/python which is Python + 1.5.2 on most older Red Hat installations; several key Red Hat tools + require this version. Python 2.1.x may be installed as + /usr/bin/python2. The Makefile installs Python as + /usr/local/bin/python, which may or may not take precedence + over /usr/bin/python, depending on how you have set up $PATH. + +FreeBSD 3.x and probably platforms with NCurses that use libmytinfo or + similar: When using cursesmodule, the linking is not done in + the correct order with the defaults. Remove "-ltermcap" from + the readline entry in Setup, and use as curses entry: "curses + cursesmodule.c -lmytinfo -lncurses -ltermcap" - "mytinfo" (so + called on FreeBSD) should be the name of the auxiliary library + required on your platform. Normally, it would be linked + automatically, but not necessarily in the correct order. + +BSDI: BSDI versions before 4.1 have known problems with threads, + which can cause strange errors in a number of modules (for + instance, the 'test_signal' test script will hang forever.) + Turning off threads (with --with-threads=no) or upgrading to + BSDI 4.1 solves this problem. + +DEC Unix: Run configure with --with-dec-threads, or with + --with-threads=no if no threads are desired (threads are on by + default). When using GCC, it is possible to get an internal + compiler error if optimization is used. This was reported for + GCC 2.7.2.3 on selectmodule.c. Manually compile the affected + file without optimization to solve the problem. + +DEC Ultrix: compile with GCC to avoid bugs in the native compiler, + and pass SHELL=/bin/sh5 to Make when installing. + +AIX: A complete overhaul of the shared library support is now in + place. See Misc/AIX-NOTES for some notes on how it's done. + (The optimizer bug reported at this place in previous releases + has been worked around by a minimal code change.) If you get + errors about pthread_* functions, during compile or during + testing, try setting CC to a thread-safe (reentrant) compiler, + like "cc_r". For full C++ module support, set CC="xlC_r" (or + CC="xlC" without thread support). + +HP-UX: When using threading, you may have to add -D_REENTRANT to the + OPT variable in the top-level Makefile; reported by Pat Knight, + this seems to make a difference (at least for HP-UX 10.20) + even though pyconfig.h defines it. This seems unnecessary when + using HP/UX 11 and later - threading works "out of the box". + +HP-UX ia64: When building on the ia64 (Itanium) platform using HP's + compiler, some experience has shown that the compiler's + optimiser produces a completely broken version of python + (see http://www.python.org/sf/814976). To work around this, + edit the Makefile and remove -O from the OPT line. + +HP PA-RISC 2.0: A recent bug report (http://www.python.org/sf/546117) + suggests that the C compiler in this 64-bit system has bugs + in the optimizer that break Python. Compiling without + optimization solves the problems. + +SCO: The following apply to SCO 3 only; Python builds out of the box + on SCO 5 (or so we've heard). + + 1) Everything works much better if you add -U__STDC__ to the + defs. This is because all the SCO header files are broken. + Anything that isn't mentioned in the C standard is + conditionally excluded when __STDC__ is defined. + + 2) Due to the U.S. export restrictions, SCO broke the crypt + stuff out into a separate library, libcrypt_i.a so the LIBS + needed be set to: + + LIBS=' -lsocket -lcrypt_i' + +UnixWare: There are known bugs in the math library of the system, as well as + problems in the handling of threads (calling fork in one + thread may interrupt system calls in others). Therefore, test_math and + tests involving threads will fail until those problems are fixed. + +SunOS 4.x: When using the SunPro C compiler, you may want to use the + '-Xa' option instead of '-Xc', to enable some needed non-ANSI + Sunisms. + THIS SYSTEM IS NO LONGER SUPPORTED. + +NeXT: Not supported anymore. Start with the MacOSX/Darwin code if you + want to revive it. + +QNX: Chris Herborth (chrish@qnx.com) writes: + configure works best if you use GNU bash; a port is available on + ftp.qnx.com in /usr/free. I used the following process to build, + test and install Python 1.5.x under QNX: + + 1) CONFIG_SHELL=/usr/local/bin/bash CC=cc RANLIB=: \ + ./configure --verbose --without-gcc --with-libm="" + + 2) edit Modules/Setup to activate everything that makes sense for + your system... tested here at QNX with the following modules: + + array, audioop, binascii, cPickle, cStringIO, cmath, + crypt, curses, errno, fcntl, gdbm, grp, imageop, + _locale, math, md5, new, operator, parser, pcre, + posix, pwd, readline, regex, reop, rgbimg, rotor, + select, signal, socket, soundex, strop, struct, + syslog, termios, time, timing, zlib, audioop, imageop, rgbimg + + 3) make SHELL=/usr/local/bin/bash + + or, if you feel the need for speed: + + make SHELL=/usr/local/bin/bash OPT="-5 -Oil+nrt" + + 4) make SHELL=/usr/local/bin/bash test + + Using GNU readline 2.2 seems to behave strangely, but I + think that's a problem with my readline 2.2 port. :-\ + + 5) make SHELL=/usr/local/bin/bash install + + If you get SIGSEGVs while running Python (I haven't yet, but + I've only run small programs and the test cases), you're + probably running out of stack; the default 32k could be a + little tight. To increase the stack size, edit the Makefile + to read: LDFLAGS = -N 48k + +BeOS: See Misc/BeOS-NOTES for notes about compiling/installing + Python on BeOS R3 or later. Note that only the PowerPC + platform is supported for R3; both PowerPC and x86 are + supported for R4. + +Cray T3E: Mark Hadfield (m.hadfield@niwa.co.nz) writes: + Python can be built satisfactorily on a Cray T3E but based on + my experience with the NIWA T3E (2002-05-22, version 2.2.1) + there are a few bugs and gotchas. For more information see a + thread on comp.lang.python in May 2002 entitled "Building + Python on Cray T3E". + + 1) Use Cray's cc and not gcc. The latter was reported not to + work by Konrad Hinsen. It may work now, but it may not. + + 2) To set sys.platform to something sensible, pass the + following environment variable to the configure script: + + MACHDEP=unicosmk + + 2) Run configure with option "--enable-unicode=ucs4". + + 3) The Cray T3E does not support dynamic linking, so extension + modules have to be built by adding (or uncommenting) lines + in Modules/Setup. The minimum set of modules is + + posix, new, _sre, unicodedata + + On NIWA's vanilla T3E system the following have also been + included successfully: + + _codecs, _locale, _socket, _symtable, _testcapi, _weakref + array, binascii, cmath, cPickle, crypt, cStringIO, dbm + errno, fcntl, grp, math, md5, operator, parser, pcre, pwd + regex, rotor, select, struct, strop, syslog, termios + time, timing, xreadlines + + 4) Once the python executable and library have been built, make + will execute setup.py, which will attempt to build remaining + extensions and link them dynamically. Each of these attempts + will fail but should not halt the make process. This is + normal. + + 5) Running "make test" uses a lot of resources and causes + problems on our system. You might want to try running tests + singly or in small groups. + +SGI: SGI's standard "make" utility (/bin/make or /usr/bin/make) + does not check whether a command actually changed the file it + is supposed to build. This means that whenever you say "make" + it will redo the link step. The remedy is to use SGI's much + smarter "smake" utility (/usr/sbin/smake), or GNU make. If + you set the first line of the Makefile to #!/usr/sbin/smake + smake will be invoked by make (likewise for GNU make). + + WARNING: There are bugs in the optimizer of some versions of + SGI's compilers that can cause bus errors or other strange + behavior, especially on numerical operations. To avoid this, + try building with "make OPT=". + +OS/2: If you are running Warp3 or Warp4 and have IBM's VisualAge C/C++ + compiler installed, just change into the pc\os2vacpp directory + and type NMAKE. Threading and sockets are supported by default + in the resulting binaries of PYTHON15.DLL and PYTHON.EXE. + +Monterey (64-bit AIX): The current Monterey C compiler (Visual Age) + uses the OBJECT_MODE={32|64} environment variable to set the + compilation mode to either 32-bit or 64-bit (32-bit mode is + the default). Presumably you want 64-bit compilation mode for + this 64-bit OS. As a result you must first set OBJECT_MODE=64 + in your environment before configuring (./configure) or + building (make) Python on Monterey. + +Reliant UNIX: The thread support does not compile on Reliant UNIX, and + there is a (minor) problem in the configure script for that + platform as well. This should be resolved in time for a + future release. + +MacOSX: The tests will crash on both 10.1 and 10.2 with SEGV in + test_re and test_sre due to the small default stack size. If + you set the stack size to 2048 before doing a "make test" the + failure can be avoided. If you're using the tcsh (the default + on OSX), or csh shells use "limit stacksize 2048" and for the + bash shell, use "ulimit -s 2048". + + On naked Darwin you may want to add the configure option + "--disable-toolbox-glue" to disable the glue code for the Carbon + interface modules. The modules themselves are currently only built + if you add the --enable-framework option, see below. + + On a clean OSX /usr/local does not exist. Do a + "sudo mkdir -m 775 /usr/local" + before you do a make install. It is probably not a good idea to + do "sudo make install" which installs everything as superuser, + as this may later cause problems when installing distutils-based + additions. + + Some people have reported problems building Python after using "fink" + to install additional unix software. Disabling fink (remove all references + to /sw from your .profile or .login) should solve this. + + You may want to try the configure option "--enable-framework" + which installs Python as a framework. The location can be set + as argument to the --enable-framework option (default + /Library/Frameworks). A framework install is probably needed if you + want to use any Aqua-based GUI toolkit (whether Tkinter, wxPython, + Carbon, Cocoa or anything else). + + See Mac/OSX/README for more information on framework builds. + +Cygwin: With recent (relative to the time of writing, 2001-12-19) + Cygwin installations, there are problems with the interaction + of dynamic linking and fork(). This manifests itself in build + failures during the execution of setup.py. + + There are two workarounds that both enable Python (albeit + without threading support) to build and pass all tests on + NT/2000 (and most likely XP as well, though reports of testing + on XP would be appreciated). + + The workarounds: + + (a) the band-aid fix is to link the _socket module statically + rather than dynamically (which is the default). + + To do this, run "./configure --with-threads=no" including any + other options you need (--prefix, etc.). Then in Modules/Setup + uncomment the lines: + + #SSL=/usr/local/ssl + #_socket socketmodule.c \ + # -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \ + # -L$(SSL)/lib -lssl -lcrypto + + and remove "local/" from the SSL variable. Finally, just run + "make"! + + (b) The "proper" fix is to rebase the Cygwin DLLs to prevent + base address conflicts. Details on how to do this can be + found in the following mail: + + http://sources.redhat.com/ml/cygwin/2001-12/msg00894.html + + It is hoped that a version of this solution will be + incorporated into the Cygwin distribution fairly soon. + + Two additional problems: + + (1) Threading support should still be disabled due to a known + bug in Cygwin pthreads that causes test_threadedtempfile to + hang. + + (2) The _curses module does not build. This is a known + Cygwin ncurses problem that should be resolved the next time + that this package is released. + + On older versions of Cygwin, test_poll may hang and test_strftime + may fail. + + The situation on 9X/Me is not accurately known at present. + Some time ago, there were reports that the following + regression tests failed: + + test_pwd + test_select (hang) + test_socket + + Due to the test_select hang on 9X/Me, one should run the + regression test using the following: + + make TESTOPTS='-l -x test_select' test + + News regarding these platforms with more recent Cygwin + versions would be appreciated! + +AtheOS: From Octavian Cerna : + + Before building: + + Make sure you have shared versions of the libraries you + want to use with Python. You will have to compile them + yourself, or download precompiled packages. + + Recommended libraries: + + ncurses-4.2 + readline-4.2a + zlib-1.1.4 + + Build: + + $ ./configure --prefix=/usr/python + $ make + + Python is always built as a shared library, otherwise + dynamic loading would not work. + + Testing: + + $ make test + + Install: + + # make install + # pkgmanager -a /usr/python + + + AtheOS issues: + + - large file support: due to a stdio bug in glibc/libio, + access to large files may not work correctly. fseeko() + tries to seek to a negative offset. ftello() returns a + negative offset, it looks like a 32->64bit + sign-extension issue. The lowlevel functions (open, + lseek, etc) are OK. + - sockets: AF_UNIX is defined in the C library and in + Python, but not implemented in the system. + - select: poll is available in the C library, but does not + work (It does not return POLLNVAL for bad fds and + hangs). + - posix: statvfs and fstatvfs always return ENOSYS. + - disabled modules: + - mmap: not yet implemented in AtheOS + - nis: broken (on an unconfigured system + yp_get_default_domain() returns junk instead of + error) + - dl: dynamic loading doesn't work via dlopen() + - resource: getrimit and setrlimit are not yet + implemented + + - if you are getting segmentation faults, you probably are + low on memory. AtheOS doesn't handle very well an + out-of-memory condition and simply SEGVs the process. + + Tested on: + + AtheOS-0.3.7 + gcc-2.95 + binutils-2.10 + make-3.78 + + +Configuring the bsddb and dbm modules +------------------------------------- + +Beginning with Python version 2.3, the PyBsddb package + was adopted into Python as the bsddb package, +exposing a set of package-level functions which provide +backwards-compatible behavior. Only versions 3.1 through 4.1 of +Sleepycat's libraries provide the necessary API, so older versions +aren't supported through this interface. The old bsddb module has +been retained as bsddb185, though it is not built by default. Users +wishing to use it will have to tweak Modules/Setup to build it. The +dbm module will still be built against the Sleepycat libraries if +other preferred alternatives (ndbm, gdbm) are not found, though +versions of the Sleepycat library prior to 3.1 are not considered. + + +Configuring threads +------------------- + +As of Python 2.0, threads are enabled by default. If you wish to +compile without threads, or if your thread support is broken, pass the +--with-threads=no switch to configure. Unfortunately, on some +platforms, additional compiler and/or linker options are required for +threads to work properly. Below is a table of those options, +collected by Bill Janssen. We would love to automate this process +more, but the information below is not enough to write a patch for the +configure.in file, so manual intervention is required. If you patch +the configure.in file and are confident that the patch works, please +send in the patch. (Don't bother patching the configure script itself +-- it is regenerated each time the configure.in file changes.) + +Compiler switches for threads +............................. + +The definition of _REENTRANT should be configured automatically, if +that does not work on your system, or if _REENTRANT is defined +incorrectly, please report that as a bug. + + OS/Compiler/threads Switches for use with threads + (POSIX is draft 10, DCE is draft 4) compile & link + + SunOS 5.{1-5}/{gcc,SunPro cc}/solaris -mt + SunOS 5.5/{gcc,SunPro cc}/POSIX (nothing) + DEC OSF/1 3.x/cc/DCE -threads + (butenhof@zko.dec.com) + Digital UNIX 4.x/cc/DCE -threads + (butenhof@zko.dec.com) + Digital UNIX 4.x/cc/POSIX -pthread + (butenhof@zko.dec.com) + AIX 4.1.4/cc_r/d7 (nothing) + (buhrt@iquest.net) + AIX 4.1.4/cc_r4/DCE (nothing) + (buhrt@iquest.net) + IRIX 6.2/cc/POSIX (nothing) + (robertl@cwi.nl) + + +Linker (ld) libraries and flags for threads +........................................... + + OS/threads Libraries/switches for use with threads + + SunOS 5.{1-5}/solaris -lthread + SunOS 5.5/POSIX -lpthread + DEC OSF/1 3.x/DCE -lpthreads -lmach -lc_r -lc + (butenhof@zko.dec.com) + Digital UNIX 4.x/DCE -lpthreads -lpthread -lmach -lexc -lc + (butenhof@zko.dec.com) + Digital UNIX 4.x/POSIX -lpthread -lmach -lexc -lc + (butenhof@zko.dec.com) + AIX 4.1.4/{draft7,DCE} (nothing) + (buhrt@iquest.net) + IRIX 6.2/POSIX -lpthread + (jph@emilia.engr.sgi.com) + + +Building a shared libpython +--------------------------- + +Starting with Python 2.3, the majority of the interpreter can be built +into a shared library, which can then be used by the interpreter +executable, and by applications embedding Python. To enable this feature, +configure with --enable-shared. + +If you enable this feature, the same object files will be used to create +a static library. In particular, the static library will contain object +files using position-independent code (PIC) on platforms where PIC flags +are needed for the shared library. + + +Configuring additional built-in modules +--------------------------------------- + +Starting with Python 2.1, the setup.py script at the top of the source +distribution attempts to detect which modules can be built and +automatically compiles them. Autodetection doesn't always work, so +you can still customize the configuration by editing the Modules/Setup +file; but this should be considered a last resort. The rest of this +section only applies if you decide to edit the Modules/Setup file. +You also need this to enable static linking of certain modules (which +is needed to enable profiling on some systems). + +This file is initially copied from Setup.dist by the configure script; +if it does not exist yet, create it by copying Modules/Setup.dist +yourself (configure will never overwrite it). Never edit Setup.dist +-- always edit Setup or Setup.local (see below). Read the comments in +the file for information on what kind of edits are allowed. When you +have edited Setup in the Modules directory, the interpreter will +automatically be rebuilt the next time you run make (in the toplevel +directory). + +Many useful modules can be built on any Unix system, but some optional +modules can't be reliably autodetected. Often the quickest way to +determine whether a particular module works or not is to see if it +will build: enable it in Setup, then if you get compilation or link +errors, disable it -- you're either missing support or need to adjust +the compilation and linking parameters for that module. + +On SGI IRIX, there are modules that interface to many SGI specific +system libraries, e.g. the GL library and the audio hardware. These +modules will not be built by the setup.py script. + +In addition to the file Setup, you can also edit the file Setup.local. +(the makesetup script processes both). You may find it more +convenient to edit Setup.local and leave Setup alone. Then, when +installing a new Python version, you can copy your old Setup.local +file. + + +Setting the optimization/debugging options +------------------------------------------ + +If you want or need to change the optimization/debugging options for +the C compiler, assign to the OPT variable on the toplevel make +command; e.g. "make OPT=-g" will build a debugging version of Python +on most platforms. The default is OPT=-O; a value for OPT in the +environment when the configure script is run overrides this default +(likewise for CC; and the initial value for LIBS is used as the base +set of libraries to link with). + +When compiling with GCC, the default value of OPT will also include +the -Wall and -Wstrict-prototypes options. + +Additional debugging code to help debug memory management problems can +be enabled by using the --with-pydebug option to the configure script. + + +Profiling +--------- + +If you want C profiling turned on, the easiest way is to run configure +with the CC environment variable to the necessary compiler +invocation. For example, on Linux, this works for profiling using +gprof(1): + + CC="gcc -pg" ./configure + +Note that on Linux, gprof apparently does not work for shared +libraries. The Makefile/Setup mechanism can be used to compile and +link most extension modules statically. + + +Testing +------- + +To test the interpreter, type "make test" in the top-level directory. +This runs the test set twice (once with no compiled files, once with +the compiled files left by the previous test run). The test set +produces some output. You can generally ignore the messages about +skipped tests due to optional features which can't be imported. +If a message is printed about a failed test or a traceback or core +dump is produced, something is wrong. On some Linux systems (those +that are not yet using glibc 6), test_strftime fails due to a +non-standard implementation of strftime() in the C library. Please +ignore this, or upgrade to glibc version 6. + +IMPORTANT: If the tests fail and you decide to mail a bug report, +*don't* include the output of "make test". It is useless. Run the +failing test manually, as follows: + + ./python ./Lib/test/test_whatever.py + +(substituting the top of the source tree for '.' if you built in a +different directory). This runs the test in verbose mode. + + +Installing +---------- + +To install the Python binary, library modules, shared library modules +(see below), include files, configuration files, and the manual page, +just type + + make install + +This will install all platform-independent files in subdirectories of +the directory given with the --prefix option to configure or to the +`prefix' Make variable (default /usr/local). All binary and other +platform-specific files will be installed in subdirectories if the +directory given by --exec-prefix or the `exec_prefix' Make variable +(defaults to the --prefix directory) is given. + +If DESTDIR is set, it will be taken as the root directory of the +installation, and files will be installed into $(DESTDIR)$(prefix), +$(DESTDIR)$(exec_prefix), etc. + +All subdirectories created will have Python's version number in their +name, e.g. the library modules are installed in +"/usr/local/lib/python/" by default, where is the +. release number (e.g. "2.1"). The Python binary is +installed as "python" and a hard link named "python" is +created. The only file not installed with a version number in its +name is the manual page, installed as "/usr/local/man/man1/python.1" +by default. + +If you have a previous installation of Python that you don't +want to replace yet, use + + make altinstall + +This installs the same set of files as "make install" except it +doesn't create the hard link to "python" named "python" and +it doesn't install the manual page at all. + +The only thing you may have to install manually is the Python mode for +Emacs found in Misc/python-mode.el. (But then again, more recent +versions of Emacs may already have it.) Follow the instructions that +came with Emacs for installation of site-specific files. + +On Mac OS X, if you have configured Python with --enable-framework, you +should use "make frameworkinstall" to do the installation. Note that this +installs the Python executable in a place that is not normally on your +PATH, you may want to set up a symlink in /usr/local/bin. + + +Configuration options and variables +----------------------------------- + +Some special cases are handled by passing options to the configure +script. + +WARNING: if you rerun the configure script with different options, you +must run "make clean" before rebuilding. Exceptions to this rule: +after changing --prefix or --exec-prefix, all you need to do is remove +Modules/getpath.o. + +--with(out)-gcc: The configure script uses gcc (the GNU C compiler) if + it finds it. If you don't want this, or if this compiler is + installed but broken on your platform, pass the option + --without-gcc. You can also pass "CC=cc" (or whatever the + name of the proper C compiler is) in the environment, but the + advantage of using --without-gcc is that this option is + remembered by the config.status script for its --recheck + option. + +--prefix, --exec-prefix: If you want to install the binaries and the + Python library somewhere else than in /usr/local/{bin,lib}, + you can pass the option --prefix=DIRECTORY; the interpreter + binary will be installed as DIRECTORY/bin/python and the + library files as DIRECTORY/lib/python/*. If you pass + --exec-prefix=DIRECTORY (as well) this overrides the + installation prefix for architecture-dependent files (like the + interpreter binary). Note that --prefix=DIRECTORY also + affects the default module search path (sys.path), when + Modules/config.c is compiled. Passing make the option + prefix=DIRECTORY (and/or exec_prefix=DIRECTORY) overrides the + prefix set at configuration time; this may be more convenient + than re-running the configure script if you change your mind + about the install prefix. + +--with-readline: This option is no longer supported. GNU + readline is automatically enabled by setup.py when present. + +--with-threads: On most Unix systems, you can now use multiple + threads, and support for this is enabled by default. To + disable this, pass --with-threads=no. If the library required + for threads lives in a peculiar place, you can use + --with-thread=DIRECTORY. IMPORTANT: run "make clean" after + changing (either enabling or disabling) this option, or you + will get link errors! Note: for DEC Unix use + --with-dec-threads instead. + +--with-sgi-dl: On SGI IRIX 4, dynamic loading of extension modules is + supported by the "dl" library by Jack Jansen, which is + ftp'able from ftp://ftp.cwi.nl/pub/dynload/dl-1.6.tar.Z. + This is enabled (after you've ftp'ed and compiled the dl + library) by passing --with-sgi-dl=DIRECTORY where DIRECTORY + is the absolute pathname of the dl library. (Don't bother on + IRIX 5, it already has dynamic linking using SunOS style + shared libraries.) THIS OPTION IS UNSUPPORTED. + +--with-dl-dld: Dynamic loading of modules is rumored to be supported + on some other systems: VAX (Ultrix), Sun3 (SunOS 3.4), Sequent + Symmetry (Dynix), and Atari ST. This is done using a + combination of the GNU dynamic loading package + (ftp://ftp.cwi.nl/pub/dynload/dl-dld-1.1.tar.Z) and an + emulation of the SGI dl library mentioned above (the emulation + can be found at + ftp://ftp.cwi.nl/pub/dynload/dld-3.2.3.tar.Z). To + enable this, ftp and compile both libraries, then call + configure, passing it the option + --with-dl-dld=DL_DIRECTORY,DLD_DIRECTORY where DL_DIRECTORY is + the absolute pathname of the dl emulation library and + DLD_DIRECTORY is the absolute pathname of the GNU dld library. + (Don't bother on SunOS 4 or 5, they already have dynamic + linking using shared libraries.) THIS OPTION IS UNSUPPORTED. + +--with-libm, --with-libc: It is possible to specify alternative + versions for the Math library (default -lm) and the C library + (default the empty string) using the options + --with-libm=STRING and --with-libc=STRING, respectively. For + example, if your system requires that you pass -lc_s to the C + compiler to use the shared C library, you can pass + --with-libc=-lc_s. These libraries are passed after all other + libraries, the C library last. + +--with-libs='libs': Add 'libs' to the LIBS that the python interpreter + is linked against. + +--with-cxx=: Some C++ compilers require that main() is + compiled with the C++ if there is any C++ code in the application. + Specifically, g++ on a.out systems may require that to support + construction of global objects. With this option, the main() function + of Python will be compiled with ; use that only if you + plan to use C++ extension modules, and if your compiler requires + compilation of main() as a C++ program. + + +--with-pydebug: Enable additional debugging code to help track down + memory management problems. This allows printing a list of all + live objects when the interpreter terminates. + +--with(out)-universal-newlines: enable reading of text files with + foreign newline convention (default: enabled). In other words, + any of \r, \n or \r\n is acceptable as end-of-line character. + If enabled import and execfile will automatically accept any newline + in files. Python code can open a file with open(file, 'U') to + read it in universal newline mode. THIS OPTION IS UNSUPPORTED. + + +Building for multiple architectures (using the VPATH feature) +------------------------------------------------------------- + +If your file system is shared between multiple architectures, it +usually is not necessary to make copies of the sources for each +architecture you want to support. If the make program supports the +VPATH feature, you can create an empty build directory for each +architecture, and in each directory run the configure script (on the +appropriate machine with the appropriate options). This creates the +necessary subdirectories and the Makefiles therein. The Makefiles +contain a line VPATH=... which points to a directory containing the +actual sources. (On SGI systems, use "smake -J1" instead of "make" if +you use VPATH -- don't try gnumake.) + +For example, the following is all you need to build a minimal Python +in /usr/tmp/python (assuming ~guido/src/python is the toplevel +directory and you want to build in /usr/tmp/python): + + $ mkdir /usr/tmp/python + $ cd /usr/tmp/python + $ ~guido/src/python/configure + [...] + $ make + [...] + $ + +Note that configure copies the original Setup file to the build +directory if it finds no Setup file there. This means that you can +edit the Setup file for each architecture independently. For this +reason, subsequent changes to the original Setup file are not tracked +automatically, as they might overwrite local changes. To force a copy +of a changed original Setup file, delete the target Setup file. (The +makesetup script supports multiple input files, so if you want to be +fancy you can change the rules to create an empty Setup.local if it +doesn't exist and run it with arguments $(srcdir)/Setup Setup.local; +however this assumes that you only need to add modules.) + + +Building on non-UNIX systems +---------------------------- + +For Windows (2000/NT/ME/98/95), assuming you have MS VC++ 6.0, the +project files are in PCbuild, the workspace is pcbuild.dsw. See +PCbuild\readme.txt for detailed instructions. + +For other non-Unix Windows compilers, in particular Windows 3.1 and +for OS/2, enter the directory "PC" and read the file "readme.txt". + +For the Mac, a separate source distribution will be made available, +for use with the CodeWarrior compiler. If you are interested in Mac +development, join the PythonMac Special Interest Group +(http://www.python.org/sigs/pythonmac-sig/, or send email to +pythonmac-sig-request@python.org). + +Of course, there are also binary distributions available for these +platforms -- see http://www.python.org/. + +To port Python to a new non-UNIX system, you will have to fake the +effect of running the configure script manually (for Mac and PC, this +has already been done for you). A good start is to copy the file +pyconfig.h.in to pyconfig.h and edit the latter to reflect the actual +configuration of your system. Most symbols must simply be defined as +1 only if the corresponding feature is present and can be left alone +otherwise; however the *_t type symbols must be defined as some +variant of int if they need to be defined at all. + +For all platforms, it's important that the build arrange to define the +preprocessor symbol NDEBUG on the compiler command line in a release +build of Python (else assert() calls remain in the code, hurting +release-build performance). The Unix, Windows and Mac builds already +do this. + + +Miscellaneous issues +==================== + +Emacs mode +---------- + +There's an excellent Emacs editing mode for Python code; see the file +Misc/python-mode.el. Originally written by the famous Tim Peters, it +is now maintained by the equally famous Barry Warsaw (it's no +coincidence that they now both work on the same team). The latest +version, along with various other contributed Python-related Emacs +goodies, is online at http://www.python.org/emacs/python-mode. And +if you are planning to edit the Python C code, please pick up the +latest version of CC Mode http://www.python.org/emacs/cc-mode; it +contains a "python" style used throughout most of the Python C source +files. (Newer versions of Emacs or XEmacs may already come with the +latest version of python-mode.) + + +Tkinter +------- + +The setup.py script automatically configures this when it detects a +usable Tcl/Tk installation. This requires Tcl/Tk version 8.0 or +higher. + +For more Tkinter information, see the Tkinter Resource page: +http://www.python.org/topics/tkinter/ + +There are demos in the Demo/tkinter directory, in the subdirectories +guido, matt and www (the matt and guido subdirectories have been +overhauled to use more recent Tkinter coding conventions). + +Note that there's a Python module called "Tkinter" (capital T) which +lives in Lib/lib-tk/Tkinter.py, and a C module called "_tkinter" +(lower case t and leading underscore) which lives in +Modules/_tkinter.c. Demos and normal Tk applications import only the +Python Tkinter module -- only the latter imports the C _tkinter +module. In order to find the C _tkinter module, it must be compiled +and linked into the Python interpreter -- the setup.py script does +this. In order to find the Python Tkinter module, sys.path must be +set correctly -- normal installation takes care of this. + + +Distribution structure +---------------------- + +Most subdirectories have their own README files. Most files have +comments. + +.cvsignore Additional filename matching patterns for CVS to ignore +BeOS/ Files specific to the BeOS port +Demo/ Demonstration scripts, modules and programs +Doc/ Documentation sources (LaTeX) +Grammar/ Input for the parser generator +Include/ Public header files +LICENSE Licensing information +Lib/ Python library modules +Mac/ Macintosh specific resources +Makefile.pre.in Source from which config.status creates the Makefile.pre +Misc/ Miscellaneous useful files +Modules/ Implementation of most built-in modules +Objects/ Implementation of most built-in object types +PC/ Files specific to PC ports (DOS, Windows, OS/2) +PCbuild/ Build directory for Microsoft Visual C++ +Parser/ The parser and tokenizer and their input handling +Python/ The byte-compiler and interpreter +README The file you're reading now +Tools/ Some useful programs written in Python +pyconfig.h.in Source from which pyconfig.h is created (GNU autoheader output) +configure Configuration shell script (GNU autoconf output) +configure.in Configuration specification (input for GNU autoconf) +install-sh Shell script used to install files + +The following files will (may) be created in the toplevel directory by +the configuration and build processes: + +Makefile Build rules +Makefile.pre Build rules before running Modules/makesetup +buildno Keeps track of the build number +config.cache Cache of configuration variables +pyconfig.h Configuration header +config.log Log from last configure run +config.status Status from last run of the configure script +getbuildinfo.o Object file from Modules/getbuildinfo.c +libpython.a The library archive +python The executable interpreter +tags, TAGS Tags files for vi and Emacs + + +That's all, folks! +------------------ + + +--Guido van Rossum (home page: http://www.python.org/~guido/) diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/XboxBuild/_sre_static.vcproj b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/XboxBuild/_sre_static.vcproj new file mode 100644 index 00000000..885517c2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/XboxBuild/_sre_static.vcproj @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/XboxBuild/pythoncore_static.vcproj b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/XboxBuild/pythoncore_static.vcproj new file mode 100644 index 00000000..549e9127 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/XboxBuild/pythoncore_static.vcproj @@ -0,0 +1,562 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/Python.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/Python.h new file mode 100644 index 00000000..d2b9b613 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/Python.h @@ -0,0 +1,157 @@ +#ifndef Py_PYTHON_H +#define Py_PYTHON_H +/* Since this is a "meta-include" file, no #ifdef __cplusplus / extern "C" { */ + +/* Include nearly all Python header files */ + +#include "patchlevel.h" +#include "pyconfig.h" + +/* Cyclic gc is always enabled, starting with release 2.3a1. Supply the + * old symbol for the benefit of extension modules written before then + * that may be conditionalizing on it. The core doesn't use it anymore. + */ +#ifndef WITH_CYCLE_GC +#define WITH_CYCLE_GC 1 +#endif + +#ifdef HAVE_LIMITS_H +#include +#else +#error "limits.h is required by std C -- why isn't HAVE_LIMITS_H defined?" +#endif + +#if defined(__sgi) && defined(WITH_THREAD) && !defined(_SGI_MP_SOURCE) +#define _SGI_MP_SOURCE +#endif + +#include +#ifndef NULL +# error "Python.h requires that stdio.h define NULL." +#endif + +#include +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +/* CAUTION: Build setups should ensure that NDEBUG is defined on the + * compiler command line when building Python in release mode; else + * assert() calls won't be removed. + */ +#include + +#include "pyport.h" + +/* pyconfig.h or pyport.h may or may not define DL_IMPORT */ +#ifndef DL_IMPORT /* declarations for DLL import/export */ +#define DL_IMPORT(RTYPE) RTYPE +#endif +#ifndef DL_EXPORT /* declarations for DLL import/export */ +#define DL_EXPORT(RTYPE) RTYPE +#endif + +/* Debug-mode build with pymalloc implies PYMALLOC_DEBUG. + * PYMALLOC_DEBUG is in error if pymalloc is not in use. + */ +#if defined(Py_DEBUG) && defined(WITH_PYMALLOC) && !defined(PYMALLOC_DEBUG) +#define PYMALLOC_DEBUG +#endif +#if defined(PYMALLOC_DEBUG) && !defined(WITH_PYMALLOC) +#error "PYMALLOC_DEBUG requires WITH_PYMALLOC" +#endif +#include "pymem.h" + +#include "object.h" +#include "objimpl.h" + +#include "pydebug.h" + +#include "unicodeobject.h" +#include "intobject.h" +#include "boolobject.h" +#include "longobject.h" +#include "floatobject.h" +#ifndef WITHOUT_COMPLEX +#include "complexobject.h" +#endif +#include "rangeobject.h" +#include "stringobject.h" +#include "bufferobject.h" +#include "tupleobject.h" +#include "listobject.h" +#include "dictobject.h" +#include "enumobject.h" +#include "methodobject.h" +#include "moduleobject.h" +#include "funcobject.h" +#include "classobject.h" +#include "fileobject.h" +#include "cobject.h" +#include "traceback.h" +#include "sliceobject.h" +#include "cellobject.h" +#include "iterobject.h" +#include "descrobject.h" +#include "weakrefobject.h" + +#include "codecs.h" +#include "pyerrors.h" + +#include "pystate.h" + +#include "modsupport.h" +#include "pythonrun.h" +#include "ceval.h" +#include "sysmodule.h" +#include "intrcheck.h" +#include "import.h" + +#include "abstract.h" + +/* _Py_Mangle is defined in compile.c */ +PyAPI_FUNC(int) _Py_Mangle(char *p, char *name, \ + char *buffer, size_t maxlen); + +/* PyArg_GetInt is deprecated and should not be used, use PyArg_Parse(). */ +#define PyArg_GetInt(v, a) PyArg_Parse((v), "i", (a)) + +/* PyArg_NoArgs should not be necessary. + Set ml_flags in the PyMethodDef to METH_NOARGS. */ +#define PyArg_NoArgs(v) PyArg_Parse(v, "") + +/* Convert a possibly signed character to a nonnegative int */ +/* XXX This assumes characters are 8 bits wide */ +#ifdef __CHAR_UNSIGNED__ +#define Py_CHARMASK(c) (c) +#else +#define Py_CHARMASK(c) ((c) & 0xff) +#endif + +#include "pyfpe.h" + +/* These definitions must match corresponding definitions in graminit.h. + There's code in compile.c that checks that they are the same. */ +#define Py_single_input 256 +#define Py_file_input 257 +#define Py_eval_input 258 + +#ifdef HAVE_PTH +/* GNU pth user-space thread support */ +#include +#endif + +/* Define macros for inline documentation. */ +#define PyDoc_VAR(name) static char name[] +#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str) +#ifdef WITH_DOC_STRINGS +#define PyDoc_STR(str) str +#else +#define PyDoc_STR(str) "" +#endif + +#endif /* !Py_PYTHON_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/abstract.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/abstract.h new file mode 100644 index 00000000..d23a860c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/abstract.h @@ -0,0 +1,1217 @@ +#ifndef Py_ABSTRACTOBJECT_H +#define Py_ABSTRACTOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Abstract Object Interface (many thanks to Jim Fulton) */ + +/* + PROPOSAL: A Generic Python Object Interface for Python C Modules + +Problem + + Python modules written in C that must access Python objects must do + so through routines whose interfaces are described by a set of + include files. Unfortunately, these routines vary according to the + object accessed. To use these routines, the C programmer must check + the type of the object being used and must call a routine based on + the object type. For example, to access an element of a sequence, + the programmer must determine whether the sequence is a list or a + tuple: + + if(is_tupleobject(o)) + e=gettupleitem(o,i) + else if(is_listitem(o)) + e=getlistitem(o,i) + + If the programmer wants to get an item from another type of object + that provides sequence behavior, there is no clear way to do it + correctly. + + The persistent programmer may peruse object.h and find that the + _typeobject structure provides a means of invoking up to (currently + about) 41 special operators. So, for example, a routine can get an + item from any object that provides sequence behavior. However, to + use this mechanism, the programmer must make their code dependent on + the current Python implementation. + + Also, certain semantics, especially memory management semantics, may + differ by the type of object being used. Unfortunately, these + semantics are not clearly described in the current include files. + An abstract interface providing more consistent semantics is needed. + +Proposal + + I propose the creation of a standard interface (with an associated + library of routines and/or macros) for generically obtaining the + services of Python objects. This proposal can be viewed as one + components of a Python C interface consisting of several components. + + From the viewpoint of C access to Python services, we have (as + suggested by Guido in off-line discussions): + + - "Very high level layer": two or three functions that let you exec or + eval arbitrary Python code given as a string in a module whose name is + given, passing C values in and getting C values out using + mkvalue/getargs style format strings. This does not require the user + to declare any variables of type "PyObject *". This should be enough + to write a simple application that gets Python code from the user, + execs it, and returns the output or errors. (Error handling must also + be part of this API.) + + - "Abstract objects layer": which is the subject of this proposal. + It has many functions operating on objects, and lest you do many + things from C that you can also write in Python, without going + through the Python parser. + + - "Concrete objects layer": This is the public type-dependent + interface provided by the standard built-in types, such as floats, + strings, and lists. This interface exists and is currently + documented by the collection of include files provided with the + Python distributions. + + From the point of view of Python accessing services provided by C + modules: + + - "Python module interface": this interface consist of the basic + routines used to define modules and their members. Most of the + current extensions-writing guide deals with this interface. + + - "Built-in object interface": this is the interface that a new + built-in type must provide and the mechanisms and rules that a + developer of a new built-in type must use and follow. + + This proposal is a "first-cut" that is intended to spur + discussion. See especially the lists of notes. + + The Python C object interface will provide four protocols: object, + numeric, sequence, and mapping. Each protocol consists of a + collection of related operations. If an operation that is not + provided by a particular type is invoked, then a standard exception, + NotImplementedError is raised with a operation name as an argument. + In addition, for convenience this interface defines a set of + constructors for building objects of built-in types. This is needed + so new objects can be returned from C functions that otherwise treat + objects generically. + +Memory Management + + For all of the functions described in this proposal, if a function + retains a reference to a Python object passed as an argument, then the + function will increase the reference count of the object. It is + unnecessary for the caller to increase the reference count of an + argument in anticipation of the object's retention. + + All Python objects returned from functions should be treated as new + objects. Functions that return objects assume that the caller will + retain a reference and the reference count of the object has already + been incremented to account for this fact. A caller that does not + retain a reference to an object that is returned from a function + must decrement the reference count of the object (using + DECREF(object)) to prevent memory leaks. + + Note that the behavior mentioned here is different from the current + behavior for some objects (e.g. lists and tuples) when certain + type-specific routines are called directly (e.g. setlistitem). The + proposed abstraction layer will provide a consistent memory + management interface, correcting for inconsistent behavior for some + built-in types. + +Protocols + +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/ + +/* Object Protocol: */ + + /* Implemented elsewhere: + + int PyObject_Print(PyObject *o, FILE *fp, int flags); + + Print an object, o, on file, fp. Returns -1 on + error. The flags argument is used to enable certain printing + options. The only option currently supported is Py_Print_RAW. + + (What should be said about Py_Print_RAW?) + + */ + + /* Implemented elsewhere: + + int PyObject_HasAttrString(PyObject *o, char *attr_name); + + Returns 1 if o has the attribute attr_name, and 0 otherwise. + This is equivalent to the Python expression: + hasattr(o,attr_name). + + This function always succeeds. + + */ + + /* Implemented elsewhere: + + PyObject* PyObject_GetAttrString(PyObject *o, char *attr_name); + + Retrieve an attributed named attr_name form object o. + Returns the attribute value on success, or NULL on failure. + This is the equivalent of the Python expression: o.attr_name. + + */ + + /* Implemented elsewhere: + + int PyObject_HasAttr(PyObject *o, PyObject *attr_name); + + Returns 1 if o has the attribute attr_name, and 0 otherwise. + This is equivalent to the Python expression: + hasattr(o,attr_name). + + This function always succeeds. + + */ + + /* Implemented elsewhere: + + PyObject* PyObject_GetAttr(PyObject *o, PyObject *attr_name); + + Retrieve an attributed named attr_name form object o. + Returns the attribute value on success, or NULL on failure. + This is the equivalent of the Python expression: o.attr_name. + + */ + + + /* Implemented elsewhere: + + int PyObject_SetAttrString(PyObject *o, char *attr_name, PyObject *v); + + Set the value of the attribute named attr_name, for object o, + to the value, v. Returns -1 on failure. This is + the equivalent of the Python statement: o.attr_name=v. + + */ + + /* Implemented elsewhere: + + int PyObject_SetAttr(PyObject *o, PyObject *attr_name, PyObject *v); + + Set the value of the attribute named attr_name, for object o, + to the value, v. Returns -1 on failure. This is + the equivalent of the Python statement: o.attr_name=v. + + */ + + /* implemented as a macro: + + int PyObject_DelAttrString(PyObject *o, char *attr_name); + + Delete attribute named attr_name, for object o. Returns + -1 on failure. This is the equivalent of the Python + statement: del o.attr_name. + + */ +#define PyObject_DelAttrString(O,A) PyObject_SetAttrString((O),(A),NULL) + + /* implemented as a macro: + + int PyObject_DelAttr(PyObject *o, PyObject *attr_name); + + Delete attribute named attr_name, for object o. Returns -1 + on failure. This is the equivalent of the Python + statement: del o.attr_name. + + */ +#define PyObject_DelAttr(O,A) PyObject_SetAttr((O),(A),NULL) + + PyAPI_FUNC(int) PyObject_Cmp(PyObject *o1, PyObject *o2, int *result); + + /* + Compare the values of o1 and o2 using a routine provided by + o1, if one exists, otherwise with a routine provided by o2. + The result of the comparison is returned in result. Returns + -1 on failure. This is the equivalent of the Python + statement: result=cmp(o1,o2). + + */ + + /* Implemented elsewhere: + + int PyObject_Compare(PyObject *o1, PyObject *o2); + + Compare the values of o1 and o2 using a routine provided by + o1, if one exists, otherwise with a routine provided by o2. + Returns the result of the comparison on success. On error, + the value returned is undefined. This is equivalent to the + Python expression: cmp(o1,o2). + + */ + + /* Implemented elsewhere: + + PyObject *PyObject_Repr(PyObject *o); + + Compute the string representation of object, o. Returns the + string representation on success, NULL on failure. This is + the equivalent of the Python expression: repr(o). + + Called by the repr() built-in function and by reverse quotes. + + */ + + /* Implemented elsewhere: + + PyObject *PyObject_Str(PyObject *o); + + Compute the string representation of object, o. Returns the + string representation on success, NULL on failure. This is + the equivalent of the Python expression: str(o).) + + Called by the str() built-in function and by the print + statement. + + */ + + /* Implemented elsewhere: + + PyObject *PyObject_Unicode(PyObject *o); + + Compute the unicode representation of object, o. Returns the + unicode representation on success, NULL on failure. This is + the equivalent of the Python expression: unistr(o).) + + Called by the unistr() built-in function. + + */ + + PyAPI_FUNC(int) PyCallable_Check(PyObject *o); + + /* + Determine if the object, o, is callable. Return 1 if the + object is callable and 0 otherwise. + + This function always succeeds. + + */ + + + + PyAPI_FUNC(PyObject *) PyObject_Call(PyObject *callable_object, + PyObject *args, PyObject *kw); + + /* + Call a callable Python object, callable_object, with + arguments and keywords arguments. The 'args' argument can not be + NULL, but the 'kw' argument can be NULL. + + */ + + PyAPI_FUNC(PyObject *) PyObject_CallObject(PyObject *callable_object, + PyObject *args); + + /* + Call a callable Python object, callable_object, with + arguments given by the tuple, args. If no arguments are + needed, then args may be NULL. Returns the result of the + call on success, or NULL on failure. This is the equivalent + of the Python expression: apply(o,args). + + */ + + PyAPI_FUNC(PyObject *) PyObject_CallFunction(PyObject *callable_object, + char *format, ...); + + /* + Call a callable Python object, callable_object, with a + variable number of C arguments. The C arguments are described + using a mkvalue-style format string. The format may be NULL, + indicating that no arguments are provided. Returns the + result of the call on success, or NULL on failure. This is + the equivalent of the Python expression: apply(o,args). + + */ + + + PyAPI_FUNC(PyObject *) PyObject_CallMethod(PyObject *o, char *m, + char *format, ...); + + /* + Call the method named m of object o with a variable number of + C arguments. The C arguments are described by a mkvalue + format string. The format may be NULL, indicating that no + arguments are provided. Returns the result of the call on + success, or NULL on failure. This is the equivalent of the + Python expression: o.method(args). + */ + + + PyAPI_FUNC(PyObject *) PyObject_CallFunctionObjArgs(PyObject *callable, + ...); + + /* + Call a callable Python object, callable_object, with a + variable number of C arguments. The C arguments are provided + as PyObject * values, terminated by a NULL. Returns the + result of the call on success, or NULL on failure. This is + the equivalent of the Python expression: apply(o,args). + */ + + + PyAPI_FUNC(PyObject *) PyObject_CallMethodObjArgs(PyObject *o, + PyObject *m, ...); + + /* + Call the method named m of object o with a variable number of + C arguments. The C arguments are provided as PyObject * + values, terminated by NULL. Returns the result of the call + on success, or NULL on failure. This is the equivalent of + the Python expression: o.method(args). + */ + + + /* Implemented elsewhere: + + long PyObject_Hash(PyObject *o); + + Compute and return the hash, hash_value, of an object, o. On + failure, return -1. This is the equivalent of the Python + expression: hash(o). + + */ + + + /* Implemented elsewhere: + + int PyObject_IsTrue(PyObject *o); + + Returns 1 if the object, o, is considered to be true, 0 if o is + considered to be false and -1 on failure. This is equivalent to the + Python expression: not not o + + */ + + /* Implemented elsewhere: + + int PyObject_Not(PyObject *o); + + Returns 0 if the object, o, is considered to be true, 1 if o is + considered to be false and -1 on failure. This is equivalent to the + Python expression: not o + + */ + + PyAPI_FUNC(PyObject *) PyObject_Type(PyObject *o); + + /* + On success, returns a type object corresponding to the object + type of object o. On failure, returns NULL. This is + equivalent to the Python expression: type(o). + */ + + PyAPI_FUNC(int) PyObject_Size(PyObject *o); + + /* + Return the size of object o. If the object, o, provides + both sequence and mapping protocols, the sequence size is + returned. On error, -1 is returned. This is the equivalent + to the Python expression: len(o). + + */ + + /* For DLL compatibility */ +#undef PyObject_Length + PyAPI_FUNC(int) PyObject_Length(PyObject *o); +#define PyObject_Length PyObject_Size + + + PyAPI_FUNC(PyObject *) PyObject_GetItem(PyObject *o, PyObject *key); + + /* + Return element of o corresponding to the object, key, or NULL + on failure. This is the equivalent of the Python expression: + o[key]. + + */ + + PyAPI_FUNC(int) PyObject_SetItem(PyObject *o, PyObject *key, PyObject *v); + + /* + Map the object, key, to the value, v. Returns + -1 on failure. This is the equivalent of the Python + statement: o[key]=v. + */ + + PyAPI_FUNC(int) PyObject_DelItemString(PyObject *o, char *key); + + /* + Remove the mapping for object, key, from the object *o. + Returns -1 on failure. This is equivalent to + the Python statement: del o[key]. + */ + + PyAPI_FUNC(int) PyObject_DelItem(PyObject *o, PyObject *key); + + /* + Delete the mapping for key from *o. Returns -1 on failure. + This is the equivalent of the Python statement: del o[key]. + */ + + PyAPI_FUNC(int) PyObject_AsCharBuffer(PyObject *obj, + const char **buffer, + int *buffer_len); + + /* + Takes an arbitrary object which must support the (character, + single segment) buffer interface and returns a pointer to a + read-only memory location useable as character based input + for subsequent processing. + + 0 is returned on success. buffer and buffer_len are only + set in case no error occurs. Otherwise, -1 is returned and + an exception set. + + */ + + PyAPI_FUNC(int) PyObject_CheckReadBuffer(PyObject *obj); + + /* + Checks whether an arbitrary object supports the (character, + single segment) buffer interface. Returns 1 on success, 0 + on failure. + + */ + + PyAPI_FUNC(int) PyObject_AsReadBuffer(PyObject *obj, + const void **buffer, + int *buffer_len); + + /* + Same as PyObject_AsCharBuffer() except that this API expects + (readable, single segment) buffer interface and returns a + pointer to a read-only memory location which can contain + arbitrary data. + + 0 is returned on success. buffer and buffer_len are only + set in case no error occurrs. Otherwise, -1 is returned and + an exception set. + + */ + + PyAPI_FUNC(int) PyObject_AsWriteBuffer(PyObject *obj, + void **buffer, + int *buffer_len); + + /* + Takes an arbitrary object which must support the (writeable, + single segment) buffer interface and returns a pointer to a + writeable memory location in buffer of size buffer_len. + + 0 is returned on success. buffer and buffer_len are only + set in case no error occurrs. Otherwise, -1 is returned and + an exception set. + + */ + +/* Iterators */ + + PyAPI_FUNC(PyObject *) PyObject_GetIter(PyObject *); + /* Takes an object and returns an iterator for it. + This is typically a new iterator but if the argument + is an iterator, this returns itself. */ + +#define PyIter_Check(obj) \ + (PyType_HasFeature((obj)->ob_type, Py_TPFLAGS_HAVE_ITER) && \ + (obj)->ob_type->tp_iternext != NULL) + + PyAPI_FUNC(PyObject *) PyIter_Next(PyObject *); + /* Takes an iterator object and calls its tp_iternext slot, + returning the next value. If the iterator is exhausted, + this returns NULL without setting an exception. + NULL with an exception means an error occurred. */ + +/* Number Protocol:*/ + + PyAPI_FUNC(int) PyNumber_Check(PyObject *o); + + /* + Returns 1 if the object, o, provides numeric protocols, and + false otherwise. + + This function always succeeds. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Add(PyObject *o1, PyObject *o2); + + /* + Returns the result of adding o1 and o2, or null on failure. + This is the equivalent of the Python expression: o1+o2. + + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Subtract(PyObject *o1, PyObject *o2); + + /* + Returns the result of subtracting o2 from o1, or null on + failure. This is the equivalent of the Python expression: + o1-o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Multiply(PyObject *o1, PyObject *o2); + + /* + Returns the result of multiplying o1 and o2, or null on + failure. This is the equivalent of the Python expression: + o1*o2. + + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Divide(PyObject *o1, PyObject *o2); + + /* + Returns the result of dividing o1 by o2, or null on failure. + This is the equivalent of the Python expression: o1/o2. + + + */ + + PyAPI_FUNC(PyObject *) PyNumber_FloorDivide(PyObject *o1, PyObject *o2); + + /* + Returns the result of dividing o1 by o2 giving an integral result, + or null on failure. + This is the equivalent of the Python expression: o1//o2. + + + */ + + PyAPI_FUNC(PyObject *) PyNumber_TrueDivide(PyObject *o1, PyObject *o2); + + /* + Returns the result of dividing o1 by o2 giving a float result, + or null on failure. + This is the equivalent of the Python expression: o1/o2. + + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Remainder(PyObject *o1, PyObject *o2); + + /* + Returns the remainder of dividing o1 by o2, or null on + failure. This is the equivalent of the Python expression: + o1%o2. + + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Divmod(PyObject *o1, PyObject *o2); + + /* + See the built-in function divmod. Returns NULL on failure. + This is the equivalent of the Python expression: + divmod(o1,o2). + + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Power(PyObject *o1, PyObject *o2, + PyObject *o3); + + /* + See the built-in function pow. Returns NULL on failure. + This is the equivalent of the Python expression: + pow(o1,o2,o3), where o3 is optional. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Negative(PyObject *o); + + /* + Returns the negation of o on success, or null on failure. + This is the equivalent of the Python expression: -o. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Positive(PyObject *o); + + /* + Returns the (what?) of o on success, or NULL on failure. + This is the equivalent of the Python expression: +o. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Absolute(PyObject *o); + + /* + Returns the absolute value of o, or null on failure. This is + the equivalent of the Python expression: abs(o). + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Invert(PyObject *o); + + /* + Returns the bitwise negation of o on success, or NULL on + failure. This is the equivalent of the Python expression: + ~o. + + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Lshift(PyObject *o1, PyObject *o2); + + /* + Returns the result of left shifting o1 by o2 on success, or + NULL on failure. This is the equivalent of the Python + expression: o1 << o2. + + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Rshift(PyObject *o1, PyObject *o2); + + /* + Returns the result of right shifting o1 by o2 on success, or + NULL on failure. This is the equivalent of the Python + expression: o1 >> o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_And(PyObject *o1, PyObject *o2); + + /* + Returns the result of bitwise and of o1 and o2 on success, or + NULL on failure. This is the equivalent of the Python + expression: o1&o2. + + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Xor(PyObject *o1, PyObject *o2); + + /* + Returns the bitwise exclusive or of o1 by o2 on success, or + NULL on failure. This is the equivalent of the Python + expression: o1^o2. + + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Or(PyObject *o1, PyObject *o2); + + /* + Returns the result of bitwise or on o1 and o2 on success, or + NULL on failure. This is the equivalent of the Python + expression: o1|o2. + + */ + + /* Implemented elsewhere: + + int PyNumber_Coerce(PyObject **p1, PyObject **p2); + + This function takes the addresses of two variables of type + PyObject*. + + If the objects pointed to by *p1 and *p2 have the same type, + increment their reference count and return 0 (success). + If the objects can be converted to a common numeric type, + replace *p1 and *p2 by their converted value (with 'new' + reference counts), and return 0. + If no conversion is possible, or if some other error occurs, + return -1 (failure) and don't increment the reference counts. + The call PyNumber_Coerce(&o1, &o2) is equivalent to the Python + statement o1, o2 = coerce(o1, o2). + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Int(PyObject *o); + + /* + Returns the o converted to an integer object on success, or + NULL on failure. This is the equivalent of the Python + expression: int(o). + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Long(PyObject *o); + + /* + Returns the o converted to a long integer object on success, + or NULL on failure. This is the equivalent of the Python + expression: long(o). + + */ + + PyAPI_FUNC(PyObject *) PyNumber_Float(PyObject *o); + + /* + Returns the o converted to a float object on success, or NULL + on failure. This is the equivalent of the Python expression: + float(o). + */ + +/* In-place variants of (some of) the above number protocol functions */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceAdd(PyObject *o1, PyObject *o2); + + /* + Returns the result of adding o2 to o1, possibly in-place, or null + on failure. This is the equivalent of the Python expression: + o1 += o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceSubtract(PyObject *o1, PyObject *o2); + + /* + Returns the result of subtracting o2 from o1, possibly in-place or + null on failure. This is the equivalent of the Python expression: + o1 -= o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceMultiply(PyObject *o1, PyObject *o2); + + /* + Returns the result of multiplying o1 by o2, possibly in-place, or + null on failure. This is the equivalent of the Python expression: + o1 *= o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceDivide(PyObject *o1, PyObject *o2); + + /* + Returns the result of dividing o1 by o2, possibly in-place, or null + on failure. This is the equivalent of the Python expression: + o1 /= o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceFloorDivide(PyObject *o1, + PyObject *o2); + + /* + Returns the result of dividing o1 by o2 giving an integral result, + possibly in-place, or null on failure. + This is the equivalent of the Python expression: + o1 /= o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceTrueDivide(PyObject *o1, + PyObject *o2); + + /* + Returns the result of dividing o1 by o2 giving a float result, + possibly in-place, or null on failure. + This is the equivalent of the Python expression: + o1 /= o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceRemainder(PyObject *o1, PyObject *o2); + + /* + Returns the remainder of dividing o1 by o2, possibly in-place, or + null on failure. This is the equivalent of the Python expression: + o1 %= o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlacePower(PyObject *o1, PyObject *o2, + PyObject *o3); + + /* + Returns the result of raising o1 to the power of o2, possibly + in-place, or null on failure. This is the equivalent of the Python + expression: o1 **= o2, or pow(o1, o2, o3) if o3 is present. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceLshift(PyObject *o1, PyObject *o2); + + /* + Returns the result of left shifting o1 by o2, possibly in-place, or + null on failure. This is the equivalent of the Python expression: + o1 <<= o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceRshift(PyObject *o1, PyObject *o2); + + /* + Returns the result of right shifting o1 by o2, possibly in-place or + null on failure. This is the equivalent of the Python expression: + o1 >>= o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceAnd(PyObject *o1, PyObject *o2); + + /* + Returns the result of bitwise and of o1 and o2, possibly in-place, + or null on failure. This is the equivalent of the Python + expression: o1 &= o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceXor(PyObject *o1, PyObject *o2); + + /* + Returns the bitwise exclusive or of o1 by o2, possibly in-place, or + null on failure. This is the equivalent of the Python expression: + o1 ^= o2. + + */ + + PyAPI_FUNC(PyObject *) PyNumber_InPlaceOr(PyObject *o1, PyObject *o2); + + /* + Returns the result of bitwise or of o1 and o2, possibly in-place, + or null on failure. This is the equivalent of the Python + expression: o1 |= o2. + + */ + + +/* Sequence protocol:*/ + + PyAPI_FUNC(int) PySequence_Check(PyObject *o); + + /* + Return 1 if the object provides sequence protocol, and zero + otherwise. + + This function always succeeds. + + */ + + PyAPI_FUNC(int) PySequence_Size(PyObject *o); + + /* + Return the size of sequence object o, or -1 on failure. + + */ + + /* For DLL compatibility */ +#undef PySequence_Length + PyAPI_FUNC(int) PySequence_Length(PyObject *o); +#define PySequence_Length PySequence_Size + + + PyAPI_FUNC(PyObject *) PySequence_Concat(PyObject *o1, PyObject *o2); + + /* + Return the concatenation of o1 and o2 on success, and NULL on + failure. This is the equivalent of the Python + expression: o1+o2. + + */ + + PyAPI_FUNC(PyObject *) PySequence_Repeat(PyObject *o, int count); + + /* + Return the result of repeating sequence object o count times, + or NULL on failure. This is the equivalent of the Python + expression: o1*count. + + */ + + PyAPI_FUNC(PyObject *) PySequence_GetItem(PyObject *o, int i); + + /* + Return the ith element of o, or NULL on failure. This is the + equivalent of the Python expression: o[i]. + */ + + PyAPI_FUNC(PyObject *) PySequence_GetSlice(PyObject *o, int i1, int i2); + + /* + Return the slice of sequence object o between i1 and i2, or + NULL on failure. This is the equivalent of the Python + expression: o[i1:i2]. + + */ + + PyAPI_FUNC(int) PySequence_SetItem(PyObject *o, int i, PyObject *v); + + /* + Assign object v to the ith element of o. Returns + -1 on failure. This is the equivalent of the Python + statement: o[i]=v. + + */ + + PyAPI_FUNC(int) PySequence_DelItem(PyObject *o, int i); + + /* + Delete the ith element of object v. Returns + -1 on failure. This is the equivalent of the Python + statement: del o[i]. + */ + + PyAPI_FUNC(int) PySequence_SetSlice(PyObject *o, int i1, int i2, + PyObject *v); + + /* + Assign the sequence object, v, to the slice in sequence + object, o, from i1 to i2. Returns -1 on failure. This is the + equivalent of the Python statement: o[i1:i2]=v. + */ + + PyAPI_FUNC(int) PySequence_DelSlice(PyObject *o, int i1, int i2); + + /* + Delete the slice in sequence object, o, from i1 to i2. + Returns -1 on failure. This is the equivalent of the Python + statement: del o[i1:i2]. + */ + + PyAPI_FUNC(PyObject *) PySequence_Tuple(PyObject *o); + + /* + Returns the sequence, o, as a tuple on success, and NULL on failure. + This is equivalent to the Python expression: tuple(o) + */ + + + PyAPI_FUNC(PyObject *) PySequence_List(PyObject *o); + /* + Returns the sequence, o, as a list on success, and NULL on failure. + This is equivalent to the Python expression: list(o) + */ + + PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m); + /* + Returns the sequence, o, as a tuple, unless it's already a + tuple or list. Use PySequence_Fast_GET_ITEM to access the + members of this list, and PySequence_Fast_GET_SIZE to get its length. + + Returns NULL on failure. If the object does not support iteration, + raises a TypeError exception with m as the message text. + */ + +#define PySequence_Fast_GET_SIZE(o) \ + (PyList_Check(o) ? PyList_GET_SIZE(o) : PyTuple_GET_SIZE(o)) + /* + Return the size of o, assuming that o was returned by + PySequence_Fast and is not NULL. + */ + +#define PySequence_Fast_GET_ITEM(o, i)\ + (PyList_Check(o) ? PyList_GET_ITEM(o, i) : PyTuple_GET_ITEM(o, i)) + /* + Return the ith element of o, assuming that o was returned by + PySequence_Fast, and that i is within bounds. + */ + +#define PySequence_ITEM(o, i)\ + ( o->ob_type->tp_as_sequence->sq_item(o, i) ) + /* Assume tp_as_sequence and sq_item exist and that i does not + need to be corrected for a negative index + */ + + PyAPI_FUNC(int) PySequence_Count(PyObject *o, PyObject *value); + + /* + Return the number of occurrences on value on o, that is, + return the number of keys for which o[key]==value. On + failure, return -1. This is equivalent to the Python + expression: o.count(value). + */ + + PyAPI_FUNC(int) PySequence_Contains(PyObject *seq, PyObject *ob); + /* + Return -1 if error; 1 if ob in seq; 0 if ob not in seq. + Use __contains__ if possible, else _PySequence_IterSearch(). + */ + +#define PY_ITERSEARCH_COUNT 1 +#define PY_ITERSEARCH_INDEX 2 +#define PY_ITERSEARCH_CONTAINS 3 + PyAPI_FUNC(int) _PySequence_IterSearch(PyObject *seq, PyObject *obj, + int operation); + /* + Iterate over seq. Result depends on the operation: + PY_ITERSEARCH_COUNT: return # of times obj appears in seq; -1 if + error. + PY_ITERSEARCH_INDEX: return 0-based index of first occurrence of + obj in seq; set ValueError and return -1 if none found; + also return -1 on error. + PY_ITERSEARCH_CONTAINS: return 1 if obj in seq, else 0; -1 on + error. + */ + +/* For DLL-level backwards compatibility */ +#undef PySequence_In + PyAPI_FUNC(int) PySequence_In(PyObject *o, PyObject *value); + +/* For source-level backwards compatibility */ +#define PySequence_In PySequence_Contains + + /* + Determine if o contains value. If an item in o is equal to + X, return 1, otherwise return 0. On error, return -1. This + is equivalent to the Python expression: value in o. + */ + + PyAPI_FUNC(int) PySequence_Index(PyObject *o, PyObject *value); + + /* + Return the first index for which o[i]=value. On error, + return -1. This is equivalent to the Python + expression: o.index(value). + */ + +/* In-place versions of some of the above Sequence functions. */ + + PyAPI_FUNC(PyObject *) PySequence_InPlaceConcat(PyObject *o1, PyObject *o2); + + /* + Append o2 to o1, in-place when possible. Return the resulting + object, which could be o1, or NULL on failure. This is the + equivalent of the Python expression: o1 += o2. + + */ + + PyAPI_FUNC(PyObject *) PySequence_InPlaceRepeat(PyObject *o, int count); + + /* + Repeat o1 by count, in-place when possible. Return the resulting + object, which could be o1, or NULL on failure. This is the + equivalent of the Python expression: o1 *= count. + + */ + +/* Mapping protocol:*/ + + PyAPI_FUNC(int) PyMapping_Check(PyObject *o); + + /* + Return 1 if the object provides mapping protocol, and zero + otherwise. + + This function always succeeds. + */ + + PyAPI_FUNC(int) PyMapping_Size(PyObject *o); + + /* + Returns the number of keys in object o on success, and -1 on + failure. For objects that do not provide sequence protocol, + this is equivalent to the Python expression: len(o). + */ + + /* For DLL compatibility */ +#undef PyMapping_Length + PyAPI_FUNC(int) PyMapping_Length(PyObject *o); +#define PyMapping_Length PyMapping_Size + + + /* implemented as a macro: + + int PyMapping_DelItemString(PyObject *o, char *key); + + Remove the mapping for object, key, from the object *o. + Returns -1 on failure. This is equivalent to + the Python statement: del o[key]. + */ +#define PyMapping_DelItemString(O,K) PyObject_DelItemString((O),(K)) + + /* implemented as a macro: + + int PyMapping_DelItem(PyObject *o, PyObject *key); + + Remove the mapping for object, key, from the object *o. + Returns -1 on failure. This is equivalent to + the Python statement: del o[key]. + */ +#define PyMapping_DelItem(O,K) PyObject_DelItem((O),(K)) + + PyAPI_FUNC(int) PyMapping_HasKeyString(PyObject *o, char *key); + + /* + On success, return 1 if the mapping object has the key, key, + and 0 otherwise. This is equivalent to the Python expression: + o.has_key(key). + + This function always succeeds. + */ + + PyAPI_FUNC(int) PyMapping_HasKey(PyObject *o, PyObject *key); + + /* + Return 1 if the mapping object has the key, key, + and 0 otherwise. This is equivalent to the Python expression: + o.has_key(key). + + This function always succeeds. + + */ + + /* Implemented as macro: + + PyObject *PyMapping_Keys(PyObject *o); + + On success, return a list of the keys in object o. On + failure, return NULL. This is equivalent to the Python + expression: o.keys(). + */ +#define PyMapping_Keys(O) PyObject_CallMethod(O,"keys",NULL) + + /* Implemented as macro: + + PyObject *PyMapping_Values(PyObject *o); + + On success, return a list of the values in object o. On + failure, return NULL. This is equivalent to the Python + expression: o.values(). + */ +#define PyMapping_Values(O) PyObject_CallMethod(O,"values",NULL) + + /* Implemented as macro: + + PyObject *PyMapping_Items(PyObject *o); + + On success, return a list of the items in object o, where + each item is a tuple containing a key-value pair. On + failure, return NULL. This is equivalent to the Python + expression: o.items(). + + */ +#define PyMapping_Items(O) PyObject_CallMethod(O,"items",NULL) + + PyAPI_FUNC(PyObject *) PyMapping_GetItemString(PyObject *o, char *key); + + /* + Return element of o corresponding to the object, key, or NULL + on failure. This is the equivalent of the Python expression: + o[key]. + */ + + PyAPI_FUNC(int) PyMapping_SetItemString(PyObject *o, char *key, + PyObject *value); + + /* + Map the object, key, to the value, v. Returns + -1 on failure. This is the equivalent of the Python + statement: o[key]=v. + */ + + +PyAPI_FUNC(int) PyObject_IsInstance(PyObject *object, PyObject *typeorclass); + /* isinstance(object, typeorclass) */ + +PyAPI_FUNC(int) PyObject_IsSubclass(PyObject *object, PyObject *typeorclass); + /* issubclass(object, typeorclass) */ + + +#ifdef __cplusplus +} +#endif +#endif /* Py_ABSTRACTOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/bitset.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/bitset.h new file mode 100644 index 00000000..028acdf0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/bitset.h @@ -0,0 +1,32 @@ + +#ifndef Py_BITSET_H +#define Py_BITSET_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Bitset interface */ + +#define BYTE char + +typedef BYTE *bitset; + +bitset newbitset(int nbits); +void delbitset(bitset bs); +#define testbit(ss, ibit) (((ss)[BIT2BYTE(ibit)] & BIT2MASK(ibit)) != 0) +int addbit(bitset bs, int ibit); /* Returns 0 if already set */ +int samebitset(bitset bs1, bitset bs2, int nbits); +void mergebitset(bitset bs1, bitset bs2, int nbits); + +#define BITSPERBYTE (8*sizeof(BYTE)) +#define NBYTES(nbits) (((nbits) + BITSPERBYTE - 1) / BITSPERBYTE) + +#define BIT2BYTE(ibit) ((ibit) / BITSPERBYTE) +#define BIT2SHIFT(ibit) ((ibit) % BITSPERBYTE) +#define BIT2MASK(ibit) (1 << BIT2SHIFT(ibit)) +#define BYTE2BIT(ibyte) ((ibyte) * BITSPERBYTE) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_BITSET_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/boolobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/boolobject.h new file mode 100644 index 00000000..28da1d0b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/boolobject.h @@ -0,0 +1,32 @@ +/* Boolean object interface */ + +#ifndef Py_BOOLOBJECT_H +#define Py_BOOLOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + + +typedef PyIntObject PyBoolObject; + +PyAPI_DATA(PyTypeObject) PyBool_Type; + +#define PyBool_Check(x) ((x)->ob_type == &PyBool_Type) + +/* Py_False and Py_True are the only two bools in existence. +Don't forget to apply Py_INCREF() when returning either!!! */ + +/* Don't use these directly */ +PyAPI_DATA(PyIntObject) _Py_ZeroStruct, _Py_TrueStruct; + +/* Use these macros */ +#define Py_False ((PyObject *) &_Py_ZeroStruct) +#define Py_True ((PyObject *) &_Py_TrueStruct) + +/* Function to return a bool from a C long */ +PyAPI_FUNC(PyObject *) PyBool_FromLong(long); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_BOOLOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/bufferobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/bufferobject.h new file mode 100644 index 00000000..5d6a16b9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/bufferobject.h @@ -0,0 +1,33 @@ + +/* Buffer object interface */ + +/* Note: the object's structure is private */ + +#ifndef Py_BUFFEROBJECT_H +#define Py_BUFFEROBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + + +PyAPI_DATA(PyTypeObject) PyBuffer_Type; + +#define PyBuffer_Check(op) ((op)->ob_type == &PyBuffer_Type) + +#define Py_END_OF_BUFFER (-1) + +PyAPI_FUNC(PyObject *) PyBuffer_FromObject(PyObject *base, + int offset, int size); +PyAPI_FUNC(PyObject *) PyBuffer_FromReadWriteObject(PyObject *base, + int offset, + int size); + +PyAPI_FUNC(PyObject *) PyBuffer_FromMemory(void *ptr, int size); +PyAPI_FUNC(PyObject *) PyBuffer_FromReadWriteMemory(void *ptr, int size); + +PyAPI_FUNC(PyObject *) PyBuffer_New(int size); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_BUFFEROBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cStringIO.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cStringIO.h new file mode 100644 index 00000000..6754b13f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cStringIO.h @@ -0,0 +1,70 @@ +#ifndef Py_CSTRINGIO_H +#define Py_CSTRINGIO_H +#ifdef __cplusplus +extern "C" { +#endif +/* + + This header provides access to cStringIO objects from C. + Functions are provided for calling cStringIO objects and + macros are provided for testing whether you have cStringIO + objects. + + Before calling any of the functions or macros, you must initialize + the routines with: + + PycString_IMPORT + + This would typically be done in your init function. + +*/ +#define PycString_IMPORT \ + PycStringIO = (struct PycStringIO_CAPI*)PyCObject_Import("cStringIO", \ + "cStringIO_CAPI") + +/* Basic functions to manipulate cStringIO objects from C */ + +static struct PycStringIO_CAPI { + + /* Read a string from an input object. If the last argument + is -1, the remainder will be read. + */ + int(*cread)(PyObject *, char **, int); + + /* Read a line from an input object. Returns the length of the read + line as an int and a pointer inside the object buffer as char** (so + the caller doesn't have to provide its own buffer as destination). + */ + int(*creadline)(PyObject *, char **); + + /* Write a string to an output object*/ + int(*cwrite)(PyObject *, char *, int); + + /* Get the output object as a Python string (returns new reference). */ + PyObject *(*cgetvalue)(PyObject *); + + /* Create a new output object */ + PyObject *(*NewOutput)(int); + + /* Create an input object from a Python string + (copies the Python string reference). + */ + PyObject *(*NewInput)(PyObject *); + + /* The Python types for cStringIO input and output objects. + Note that you can do input on an output object. + */ + PyTypeObject *InputType, *OutputType; + +} *PycStringIO; + +/* These can be used to test if you have one */ +#define PycStringIO_InputCheck(O) \ + ((O)->ob_type==PycStringIO->InputType) +#define PycStringIO_OutputCheck(O) \ + ((O)->ob_type==PycStringIO->OutputType) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CSTRINGIO_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cellobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cellobject.h new file mode 100644 index 00000000..cd2a4566 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cellobject.h @@ -0,0 +1,28 @@ +/* Cell object interface */ + +#ifndef Py_CELLOBJECT_H +#define Py_CELLOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_HEAD + PyObject *ob_ref; +} PyCellObject; + +PyAPI_DATA(PyTypeObject) PyCell_Type; + +#define PyCell_Check(op) ((op)->ob_type == &PyCell_Type) + +PyAPI_FUNC(PyObject *) PyCell_New(PyObject *); +PyAPI_FUNC(PyObject *) PyCell_Get(PyObject *); +PyAPI_FUNC(int) PyCell_Set(PyObject *, PyObject *); + +#define PyCell_GET(op) (((PyCellObject *)(op))->ob_ref) +#define PyCell_SET(op, v) (((PyCellObject *)(op))->ob_ref = v) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_TUPLEOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/ceval.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/ceval.h new file mode 100644 index 00000000..1e7dba3e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/ceval.h @@ -0,0 +1,138 @@ +#ifndef Py_CEVAL_H +#define Py_CEVAL_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Interface to random parts in ceval.c */ + +PyAPI_FUNC(PyObject *) PyEval_CallObjectWithKeywords( + PyObject *, PyObject *, PyObject *); + +/* DLL-level Backwards compatibility: */ +#undef PyEval_CallObject +PyAPI_FUNC(PyObject *) PyEval_CallObject(PyObject *, PyObject *); + +/* Inline this */ +#define PyEval_CallObject(func,arg) \ + PyEval_CallObjectWithKeywords(func, arg, (PyObject *)NULL) + +PyAPI_FUNC(PyObject *) PyEval_CallFunction(PyObject *obj, char *format, ...); +PyAPI_FUNC(PyObject *) PyEval_CallMethod(PyObject *obj, + char *methodname, char *format, ...); + +PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *); +PyAPI_FUNC(void) PyEval_SetTrace(Py_tracefunc, PyObject *); + +struct _frame; /* Avoid including frameobject.h */ + +PyAPI_FUNC(PyObject *) PyEval_GetBuiltins(void); +PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void); +PyAPI_FUNC(PyObject *) PyEval_GetLocals(void); +PyAPI_FUNC(struct _frame *) PyEval_GetFrame(void); +PyAPI_FUNC(int) PyEval_GetRestricted(void); + +/* Look at the current frame's (if any) code's co_flags, and turn on + the corresponding compiler flags in cf->cf_flags. Return 1 if any + flag was set, else return 0. */ +PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf); + +PyAPI_FUNC(int) Py_FlushLine(void); + +PyAPI_FUNC(int) Py_AddPendingCall(int (*func)(void *), void *arg); +PyAPI_FUNC(int) Py_MakePendingCalls(void); + +PyAPI_FUNC(void) Py_SetRecursionLimit(int); +PyAPI_FUNC(int) Py_GetRecursionLimit(void); + +PyAPI_FUNC(char *) PyEval_GetFuncName(PyObject *); +PyAPI_FUNC(char *) PyEval_GetFuncDesc(PyObject *); + +PyAPI_FUNC(PyObject *) PyEval_GetCallStats(PyObject *); + +/* this used to be handled on a per-thread basis - now just two globals */ +PyAPI_DATA(volatile int) _Py_Ticker; +PyAPI_DATA(int) _Py_CheckInterval; + +/* Interface for threads. + + A module that plans to do a blocking system call (or something else + that lasts a long time and doesn't touch Python data) can allow other + threads to run as follows: + + ...preparations here... + Py_BEGIN_ALLOW_THREADS + ...blocking system call here... + Py_END_ALLOW_THREADS + ...interpret result here... + + The Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS pair expands to a + {}-surrounded block. + To leave the block in the middle (e.g., with return), you must insert + a line containing Py_BLOCK_THREADS before the return, e.g. + + if (...premature_exit...) { + Py_BLOCK_THREADS + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + + An alternative is: + + Py_BLOCK_THREADS + if (...premature_exit...) { + PyErr_SetFromErrno(PyExc_IOError); + return NULL; + } + Py_UNBLOCK_THREADS + + For convenience, that the value of 'errno' is restored across + Py_END_ALLOW_THREADS and Py_BLOCK_THREADS. + + WARNING: NEVER NEST CALLS TO Py_BEGIN_ALLOW_THREADS AND + Py_END_ALLOW_THREADS!!! + + The function PyEval_InitThreads() should be called only from + initthread() in "threadmodule.c". + + Note that not yet all candidates have been converted to use this + mechanism! +*/ + +PyAPI_FUNC(PyThreadState *) PyEval_SaveThread(void); +PyAPI_FUNC(void) PyEval_RestoreThread(PyThreadState *); + +#ifdef WITH_THREAD + +PyAPI_FUNC(void) PyEval_InitThreads(void); +PyAPI_FUNC(void) PyEval_AcquireLock(void); +PyAPI_FUNC(void) PyEval_ReleaseLock(void); +PyAPI_FUNC(void) PyEval_AcquireThread(PyThreadState *tstate); +PyAPI_FUNC(void) PyEval_ReleaseThread(PyThreadState *tstate); +PyAPI_FUNC(void) PyEval_ReInitThreads(void); + +#define Py_BEGIN_ALLOW_THREADS { \ + PyThreadState *_save; \ + _save = PyEval_SaveThread(); +#define Py_BLOCK_THREADS PyEval_RestoreThread(_save); +#define Py_UNBLOCK_THREADS _save = PyEval_SaveThread(); +#define Py_END_ALLOW_THREADS PyEval_RestoreThread(_save); \ + } + +#else /* !WITH_THREAD */ + +#define Py_BEGIN_ALLOW_THREADS { +#define Py_BLOCK_THREADS +#define Py_UNBLOCK_THREADS +#define Py_END_ALLOW_THREADS } + +#endif /* !WITH_THREAD */ + +PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, int *); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CEVAL_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/classobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/classobject.h new file mode 100644 index 00000000..7262ad7a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/classobject.h @@ -0,0 +1,81 @@ + +/* Class object interface */ + +/* Revealing some structures (not for general use) */ + +#ifndef Py_CLASSOBJECT_H +#define Py_CLASSOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_HEAD + PyObject *cl_bases; /* A tuple of class objects */ + PyObject *cl_dict; /* A dictionary */ + PyObject *cl_name; /* A string */ + /* The following three are functions or NULL */ + PyObject *cl_getattr; + PyObject *cl_setattr; + PyObject *cl_delattr; +} PyClassObject; + +typedef struct { + PyObject_HEAD + PyClassObject *in_class; /* The class object */ + PyObject *in_dict; /* A dictionary */ + PyObject *in_weakreflist; /* List of weak references */ +} PyInstanceObject; + +typedef struct { + PyObject_HEAD + PyObject *im_func; /* The callable object implementing the method */ + PyObject *im_self; /* The instance it is bound to, or NULL */ + PyObject *im_class; /* The class that asked for the method */ + PyObject *im_weakreflist; /* List of weak references */ +} PyMethodObject; + +PyAPI_DATA(PyTypeObject) PyClass_Type, PyInstance_Type, PyMethod_Type; + +#define PyClass_Check(op) ((op)->ob_type == &PyClass_Type) +#define PyInstance_Check(op) ((op)->ob_type == &PyInstance_Type) +#define PyMethod_Check(op) ((op)->ob_type == &PyMethod_Type) + +PyAPI_FUNC(PyObject *) PyClass_New(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyInstance_New(PyObject *, PyObject *, + PyObject *); +PyAPI_FUNC(PyObject *) PyInstance_NewRaw(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyMethod_New(PyObject *, PyObject *, PyObject *); + +PyAPI_FUNC(PyObject *) PyMethod_Function(PyObject *); +PyAPI_FUNC(PyObject *) PyMethod_Self(PyObject *); +PyAPI_FUNC(PyObject *) PyMethod_Class(PyObject *); + +/* Look up attribute with name (a string) on instance object pinst, using + * only the instance and base class dicts. If a descriptor is found in + * a class dict, the descriptor is returned without calling it. + * Returns NULL if nothing found, else a borrowed reference to the + * value associated with name in the dict in which name was found. + * The point of this routine is that it never calls arbitrary Python + * code, so is always "safe": all it does is dict lookups. The function + * can't fail, never sets an exception, and NULL is not an error (it just + * means "not found"). + */ +PyAPI_FUNC(PyObject *) _PyInstance_Lookup(PyObject *pinst, PyObject *name); + +/* Macros for direct access to these values. Type checks are *not* + done, so use with care. */ +#define PyMethod_GET_FUNCTION(meth) \ + (((PyMethodObject *)meth) -> im_func) +#define PyMethod_GET_SELF(meth) \ + (((PyMethodObject *)meth) -> im_self) +#define PyMethod_GET_CLASS(meth) \ + (((PyMethodObject *)meth) -> im_class) + +PyAPI_FUNC(int) PyClass_IsSubclass(PyObject *, PyObject *); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CLASSOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cobject.h new file mode 100644 index 00000000..b5f6a944 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/cobject.h @@ -0,0 +1,51 @@ + +/* C objects to be exported from one extension module to another. + + C objects are used for communication between extension modules. + They provide a way for an extension module to export a C interface + to other extension modules, so that extension modules can use the + Python import mechanism to link to one another. + +*/ + +#ifndef Py_COBJECT_H +#define Py_COBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyCObject_Type; + +#define PyCObject_Check(op) ((op)->ob_type == &PyCObject_Type) + +/* Create a PyCObject from a pointer to a C object and an optional + destructor function. If the second argument is non-null, then it + will be called with the first argument if and when the PyCObject is + destroyed. + +*/ +PyAPI_FUNC(PyObject *) PyCObject_FromVoidPtr( + void *cobj, void (*destruct)(void*)); + + +/* Create a PyCObject from a pointer to a C object, a description object, + and an optional destructor function. If the third argument is non-null, + then it will be called with the first and second arguments if and when + the PyCObject is destroyed. +*/ +PyAPI_FUNC(PyObject *) PyCObject_FromVoidPtrAndDesc( + void *cobj, void *desc, void (*destruct)(void*,void*)); + +/* Retrieve a pointer to a C object from a PyCObject. */ +PyAPI_FUNC(void *) PyCObject_AsVoidPtr(PyObject *); + +/* Retrieve a pointer to a description object from a PyCObject. */ +PyAPI_FUNC(void *) PyCObject_GetDesc(PyObject *); + +/* Import a pointer to a C object from a module using a PyCObject. */ +PyAPI_FUNC(void *) PyCObject_Import(char *module_name, char *cobject_name); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_COBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/codecs.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/codecs.h new file mode 100644 index 00000000..e61fc0bd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/codecs.h @@ -0,0 +1,153 @@ +#ifndef Py_CODECREGISTRY_H +#define Py_CODECREGISTRY_H +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------------------------------------------------------------ + + Python Codec Registry and support functions + + +Written by Marc-Andre Lemburg (mal@lemburg.com). + +Copyright (c) Corporation for National Research Initiatives. + + ------------------------------------------------------------------------ */ + +/* Register a new codec search function. + + As side effect, this tries to load the encodings package, if not + yet done, to make sure that it is always first in the list of + search functions. + + The search_function's refcount is incremented by this function. */ + +PyAPI_FUNC(int) PyCodec_Register( + PyObject *search_function + ); + +/* Codec register lookup API. + + Looks up the given encoding and returns a tuple (encoder, decoder, + stream reader, stream writer) of functions which implement the + different aspects of processing the encoding. + + The encoding string is looked up converted to all lower-case + characters. This makes encodings looked up through this mechanism + effectively case-insensitive. + + If no codec is found, a KeyError is set and NULL returned. + + As side effect, this tries to load the encodings package, if not + yet done. This is part of the lazy load strategy for the encodings + package. + + */ + +PyAPI_FUNC(PyObject *) _PyCodec_Lookup( + const char *encoding + ); + +/* Generic codec based encoding API. + + object is passed through the encoder function found for the given + encoding using the error handling method defined by errors. errors + may be NULL to use the default method defined for the codec. + + Raises a LookupError in case no encoder can be found. + + */ + +PyAPI_FUNC(PyObject *) PyCodec_Encode( + PyObject *object, + const char *encoding, + const char *errors + ); + +/* Generic codec based decoding API. + + object is passed through the decoder function found for the given + encoding using the error handling method defined by errors. errors + may be NULL to use the default method defined for the codec. + + Raises a LookupError in case no encoder can be found. + + */ + +PyAPI_FUNC(PyObject *) PyCodec_Decode( + PyObject *object, + const char *encoding, + const char *errors + ); + +/* --- Codec Lookup APIs -------------------------------------------------- + + All APIs return a codec object with incremented refcount and are + based on _PyCodec_Lookup(). The same comments w/r to the encoding + name also apply to these APIs. + +*/ + +/* Get an encoder function for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_Encoder( + const char *encoding + ); + +/* Get a decoder function for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_Decoder( + const char *encoding + ); + +/* Get a StreamReader factory function for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_StreamReader( + const char *encoding, + PyObject *stream, + const char *errors + ); + +/* Get a StreamWriter factory function for the given encoding. */ + +PyAPI_FUNC(PyObject *) PyCodec_StreamWriter( + const char *encoding, + PyObject *stream, + const char *errors + ); + +/* Unicode encoding error handling callback registry API */ + +/* Register the error handling callback function error under the name + name. This function will be called by the codec when it encounters + unencodable characters/undecodable bytes and doesn't know the + callback name, when name is specified as the error parameter + in the call to the encode/decode function. + Return 0 on success, -1 on error */ +PyAPI_FUNC(int) PyCodec_RegisterError(const char *name, PyObject *error); + +/* Lookup the error handling callback function registered under the + name error. As a special case NULL can be passed, in which case + the error handling callback for "strict" will be returned. */ +PyAPI_FUNC(PyObject *) PyCodec_LookupError(const char *name); + +/* raise exc as an exception */ +PyAPI_FUNC(PyObject *) PyCodec_StrictErrors(PyObject *exc); + +/* ignore the unicode error, skipping the faulty input */ +PyAPI_FUNC(PyObject *) PyCodec_IgnoreErrors(PyObject *exc); + +/* replace the unicode error with ? or U+FFFD */ +PyAPI_FUNC(PyObject *) PyCodec_ReplaceErrors(PyObject *exc); + +/* replace the unicode encode error with XML character references */ +PyAPI_FUNC(PyObject *) PyCodec_XMLCharRefReplaceErrors(PyObject *exc); + +/* replace the unicode encode error with backslash escapes (\x, \u and \U) */ +PyAPI_FUNC(PyObject *) PyCodec_BackslashReplaceErrors(PyObject *exc); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_CODECREGISTRY_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/compile.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/compile.h new file mode 100644 index 00000000..e9b44efa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/compile.h @@ -0,0 +1,92 @@ + +/* Definitions for bytecode */ + +#ifndef Py_COMPILE_H +#define Py_COMPILE_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Bytecode object */ +typedef struct { + PyObject_HEAD + int co_argcount; /* #arguments, except *args */ + int co_nlocals; /* #local variables */ + int co_stacksize; /* #entries needed for evaluation stack */ + int co_flags; /* CO_..., see below */ + PyObject *co_code; /* instruction opcodes */ + PyObject *co_consts; /* list (constants used) */ + PyObject *co_names; /* list of strings (names used) */ + PyObject *co_varnames; /* tuple of strings (local variable names) */ + PyObject *co_freevars; /* tuple of strings (free variable names) */ + PyObject *co_cellvars; /* tuple of strings (cell variable names) */ + /* The rest doesn't count for hash/cmp */ + PyObject *co_filename; /* string (where it was loaded from) */ + PyObject *co_name; /* string (name, for reference) */ + int co_firstlineno; /* first source line number */ + PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) */ +} PyCodeObject; + +/* Masks for co_flags above */ +#define CO_OPTIMIZED 0x0001 +#define CO_NEWLOCALS 0x0002 +#define CO_VARARGS 0x0004 +#define CO_VARKEYWORDS 0x0008 +#define CO_NESTED 0x0010 +#define CO_GENERATOR 0x0020 +/* The CO_NOFREE flag is set if there are no free or cell variables. + This information is redundant, but it allows a single flag test + to determine whether there is any extra work to be done when the + call frame it setup. +*/ +#define CO_NOFREE 0x0040 +/* XXX Temporary hack. Until generators are a permanent part of the + language, we need a way for a code object to record that generators + were *possible* when it was compiled. This is so code dynamically + compiled *by* a code object knows whether to allow yield stmts. In + effect, this passes on the "from __future__ import generators" state + in effect when the code block was compiled. */ +#define CO_GENERATOR_ALLOWED 0x1000 /* no longer used in an essential way */ +#define CO_FUTURE_DIVISION 0x2000 + +PyAPI_DATA(PyTypeObject) PyCode_Type; + +#define PyCode_Check(op) ((op)->ob_type == &PyCode_Type) +#define PyCode_GetNumFree(op) (PyTuple_GET_SIZE((op)->co_freevars)) + +#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */ + +/* Public interface */ +struct _node; /* Declare the existence of this type */ +PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *); +PyAPI_FUNC(PyCodeObject *) PyCode_New( + int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, + PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *); + /* same as struct above */ +PyAPI_FUNC(int) PyCode_Addr2Line(PyCodeObject *, int); + +/* Future feature support */ + +typedef struct { + int ff_found_docstring; + int ff_last_lineno; + int ff_features; +} PyFutureFeatures; + +PyAPI_FUNC(PyFutureFeatures *) PyNode_Future(struct _node *, const char *); +PyAPI_FUNC(PyCodeObject *) PyNode_CompileFlags(struct _node *, const char *, + PyCompilerFlags *); + +#define FUTURE_NESTED_SCOPES "nested_scopes" +#define FUTURE_GENERATORS "generators" +#define FUTURE_DIVISION "division" + +/* for internal use only */ +#define _PyCode_GETCODEPTR(co, pp) \ + ((*(co)->co_code->ob_type->tp_as_buffer->bf_getreadbuffer) \ + ((co)->co_code, 0, (void **)(pp))) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_COMPILE_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/complexobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/complexobject.h new file mode 100644 index 00000000..68216c45 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/complexobject.h @@ -0,0 +1,58 @@ +/* Complex number structure */ + +#ifndef Py_COMPLEXOBJECT_H +#define Py_COMPLEXOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + double real; + double imag; +} Py_complex; + +/* Operations on complex numbers from complexmodule.c */ + +#define c_sum _Py_c_sum +#define c_diff _Py_c_diff +#define c_neg _Py_c_neg +#define c_prod _Py_c_prod +#define c_quot _Py_c_quot +#define c_pow _Py_c_pow + +PyAPI_FUNC(Py_complex) c_sum(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) c_diff(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) c_neg(Py_complex); +PyAPI_FUNC(Py_complex) c_prod(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) c_quot(Py_complex, Py_complex); +PyAPI_FUNC(Py_complex) c_pow(Py_complex, Py_complex); + + +/* Complex object interface */ + +/* +PyComplexObject represents a complex number with double-precision +real and imaginary parts. +*/ + +typedef struct { + PyObject_HEAD + Py_complex cval; +} PyComplexObject; + +PyAPI_DATA(PyTypeObject) PyComplex_Type; + +#define PyComplex_Check(op) PyObject_TypeCheck(op, &PyComplex_Type) +#define PyComplex_CheckExact(op) ((op)->ob_type == &PyComplex_Type) + +PyAPI_FUNC(PyObject *) PyComplex_FromCComplex(Py_complex); +PyAPI_FUNC(PyObject *) PyComplex_FromDoubles(double real, double imag); + +PyAPI_FUNC(double) PyComplex_RealAsDouble(PyObject *op); +PyAPI_FUNC(double) PyComplex_ImagAsDouble(PyObject *op); +PyAPI_FUNC(Py_complex) PyComplex_AsCComplex(PyObject *op); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_COMPLEXOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/datetime.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/datetime.h new file mode 100644 index 00000000..df814751 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/datetime.h @@ -0,0 +1,150 @@ +/* datetime.h + */ + +#ifndef DATETIME_H +#define DATETIME_H + +/* Fields are packed into successive bytes, each viewed as unsigned and + * big-endian, unless otherwise noted: + * + * byte offset + * 0 year 2 bytes, 1-9999 + * 2 month 1 byte, 1-12 + * 3 day 1 byte, 1-31 + * 4 hour 1 byte, 0-23 + * 5 minute 1 byte, 0-59 + * 6 second 1 byte, 0-59 + * 7 usecond 3 bytes, 0-999999 + * 10 + */ + +/* # of bytes for year, month, and day. */ +#define _PyDateTime_DATE_DATASIZE 4 + +/* # of bytes for hour, minute, second, and usecond. */ +#define _PyDateTime_TIME_DATASIZE 6 + +/* # of bytes for year, month, day, hour, minute, second, and usecond. */ +#define _PyDateTime_DATETIME_DATASIZE 10 + + +typedef struct +{ + PyObject_HEAD + long hashcode; /* -1 when unknown */ + int days; /* -MAX_DELTA_DAYS <= days <= MAX_DELTA_DAYS */ + int seconds; /* 0 <= seconds < 24*3600 is invariant */ + int microseconds; /* 0 <= microseconds < 1000000 is invariant */ +} PyDateTime_Delta; + +typedef struct +{ + PyObject_HEAD /* a pure abstract base clase */ +} PyDateTime_TZInfo; + + +/* The datetime and time types have hashcodes, and an optional tzinfo member, + * present if and only if hastzinfo is true. + */ +#define _PyTZINFO_HEAD \ + PyObject_HEAD \ + long hashcode; \ + char hastzinfo; /* boolean flag */ + +/* No _PyDateTime_BaseTZInfo is allocated; it's just to have something + * convenient to cast to, when getting at the hastzinfo member of objects + * starting with _PyTZINFO_HEAD. + */ +typedef struct +{ + _PyTZINFO_HEAD +} _PyDateTime_BaseTZInfo; + +/* All time objects are of PyDateTime_TimeType, but that can be allocated + * in two ways, with or without a tzinfo member. Without is the same as + * tzinfo == None, but consumes less memory. _PyDateTime_BaseTime is an + * internal struct used to allocate the right amount of space for the + * "without" case. + */ +#define _PyDateTime_TIMEHEAD \ + _PyTZINFO_HEAD \ + unsigned char data[_PyDateTime_TIME_DATASIZE]; + +typedef struct +{ + _PyDateTime_TIMEHEAD +} _PyDateTime_BaseTime; /* hastzinfo false */ + +typedef struct +{ + _PyDateTime_TIMEHEAD + PyObject *tzinfo; +} PyDateTime_Time; /* hastzinfo true */ + + +/* All datetime objects are of PyDateTime_DateTimeType, but that can be + * allocated in two ways too, just like for time objects above. In addition, + * the plain date type is a base class for datetime, so it must also have + * a hastzinfo member (although it's unused there). + */ +typedef struct +{ + _PyTZINFO_HEAD + unsigned char data[_PyDateTime_DATE_DATASIZE]; +} PyDateTime_Date; + +#define _PyDateTime_DATETIMEHEAD \ + _PyTZINFO_HEAD \ + unsigned char data[_PyDateTime_DATETIME_DATASIZE]; + +typedef struct +{ + _PyDateTime_DATETIMEHEAD +} _PyDateTime_BaseDateTime; /* hastzinfo false */ + +typedef struct +{ + _PyDateTime_DATETIMEHEAD + PyObject *tzinfo; +} PyDateTime_DateTime; /* hastzinfo true */ + + +/* Apply for date and datetime instances. */ +#define PyDateTime_GET_YEAR(o) ((((PyDateTime_Date*)o)->data[0] << 8) | \ + ((PyDateTime_Date*)o)->data[1]) +#define PyDateTime_GET_MONTH(o) (((PyDateTime_Date*)o)->data[2]) +#define PyDateTime_GET_DAY(o) (((PyDateTime_Date*)o)->data[3]) + +#define PyDateTime_DATE_GET_HOUR(o) (((PyDateTime_DateTime*)o)->data[4]) +#define PyDateTime_DATE_GET_MINUTE(o) (((PyDateTime_DateTime*)o)->data[5]) +#define PyDateTime_DATE_GET_SECOND(o) (((PyDateTime_DateTime*)o)->data[6]) +#define PyDateTime_DATE_GET_MICROSECOND(o) \ + ((((PyDateTime_DateTime*)o)->data[7] << 16) | \ + (((PyDateTime_DateTime*)o)->data[8] << 8) | \ + ((PyDateTime_DateTime*)o)->data[9]) + +/* Apply for time instances. */ +#define PyDateTime_TIME_GET_HOUR(o) (((PyDateTime_Time*)o)->data[0]) +#define PyDateTime_TIME_GET_MINUTE(o) (((PyDateTime_Time*)o)->data[1]) +#define PyDateTime_TIME_GET_SECOND(o) (((PyDateTime_Time*)o)->data[2]) +#define PyDateTime_TIME_GET_MICROSECOND(o) \ + ((((PyDateTime_Time*)o)->data[3] << 16) | \ + (((PyDateTime_Time*)o)->data[4] << 8) | \ + ((PyDateTime_Time*)o)->data[5]) + +#define PyDate_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateType) +#define PyDate_CheckExact(op) ((op)->ob_type == &PyDateTime_DateType) + +#define PyDateTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateTimeType) +#define PyDateTime_CheckExact(op) ((op)->ob_type == &PyDateTime_DateTimeType) + +#define PyTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeType) +#define PyTime_CheckExact(op) ((op)->ob_type == &PyDateTime_TimeType) + +#define PyDelta_Check(op) PyObject_TypeCheck(op, &PyDateTime_DeltaType) +#define PyDelta_CheckExact(op) ((op)->ob_type == &PyDateTime_DeltaType) + +#define PyTZInfo_Check(op) PyObject_TypeCheck(op, &PyDateTime_TZInfoType) +#define PyTZInfo_CheckExact(op) ((op)->ob_type == &PyDateTime_TZInfoType) + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/descrobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/descrobject.h new file mode 100644 index 00000000..ae0503ec --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/descrobject.h @@ -0,0 +1,91 @@ +/* Descriptors */ +#ifndef Py_DESCROBJECT_H +#define Py_DESCROBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef PyObject *(*getter)(PyObject *, void *); +typedef int (*setter)(PyObject *, PyObject *, void *); + +typedef struct PyGetSetDef { + char *name; + getter get; + setter set; + char *doc; + void *closure; +} PyGetSetDef; + +typedef PyObject *(*wrapperfunc)(PyObject *self, PyObject *args, + void *wrapped); + +typedef PyObject *(*wrapperfunc_kwds)(PyObject *self, PyObject *args, + void *wrapped, PyObject *kwds); + +struct wrapperbase { + char *name; + int offset; + void *function; + wrapperfunc wrapper; + char *doc; + int flags; + PyObject *name_strobj; +}; + +/* Flags for above struct */ +#define PyWrapperFlag_KEYWORDS 1 /* wrapper function takes keyword args */ + +/* Various kinds of descriptor objects */ + +#define PyDescr_COMMON \ + PyObject_HEAD \ + PyTypeObject *d_type; \ + PyObject *d_name + +typedef struct { + PyDescr_COMMON; +} PyDescrObject; + +typedef struct { + PyDescr_COMMON; + PyMethodDef *d_method; +} PyMethodDescrObject; + +typedef struct { + PyDescr_COMMON; + struct PyMemberDef *d_member; +} PyMemberDescrObject; + +typedef struct { + PyDescr_COMMON; + PyGetSetDef *d_getset; +} PyGetSetDescrObject; + +typedef struct { + PyDescr_COMMON; + struct wrapperbase *d_base; + void *d_wrapped; /* This can be any function pointer */ +} PyWrapperDescrObject; + +PyAPI_DATA(PyTypeObject) PyWrapperDescr_Type; + +PyAPI_FUNC(PyObject *) PyDescr_NewMethod(PyTypeObject *, PyMethodDef *); +PyAPI_FUNC(PyObject *) PyDescr_NewClassMethod(PyTypeObject *, PyMethodDef *); +PyAPI_FUNC(PyObject *) PyDescr_NewMember(PyTypeObject *, + struct PyMemberDef *); +PyAPI_FUNC(PyObject *) PyDescr_NewGetSet(PyTypeObject *, + struct PyGetSetDef *); +PyAPI_FUNC(PyObject *) PyDescr_NewWrapper(PyTypeObject *, + struct wrapperbase *, void *); +#define PyDescr_IsData(d) ((d)->ob_type->tp_descr_set != NULL) + +PyAPI_FUNC(PyObject *) PyDictProxy_New(PyObject *); +PyAPI_FUNC(PyObject *) PyWrapper_New(PyObject *, PyObject *); + + +PyAPI_DATA(PyTypeObject) PyProperty_Type; +#ifdef __cplusplus +} +#endif +#endif /* !Py_DESCROBJECT_H */ + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/dictobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/dictobject.h new file mode 100644 index 00000000..85b77314 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/dictobject.h @@ -0,0 +1,132 @@ +#ifndef Py_DICTOBJECT_H +#define Py_DICTOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Dictionary object type -- mapping from hashable object to object */ + +/* The distribution includes a separate file, Objects/dictnotes.txt, + describing explorations into dictionary design and optimization. + It covers typical dictionary use patterns, the parameters for + tuning dictionaries, and several ideas for possible optimizations. +*/ + +/* +There are three kinds of slots in the table: + +1. Unused. me_key == me_value == NULL + Does not hold an active (key, value) pair now and never did. Unused can + transition to Active upon key insertion. This is the only case in which + me_key is NULL, and is each slot's initial state. + +2. Active. me_key != NULL and me_key != dummy and me_value != NULL + Holds an active (key, value) pair. Active can transition to Dummy upon + key deletion. This is the only case in which me_value != NULL. + +3. Dummy. me_key == dummy and me_value == NULL + Previously held an active (key, value) pair, but that was deleted and an + active pair has not yet overwritten the slot. Dummy can transition to + Active upon key insertion. Dummy slots cannot be made Unused again + (cannot have me_key set to NULL), else the probe sequence in case of + collision would have no way to know they were once active. + +Note: .popitem() abuses the me_hash field of an Unused or Dummy slot to +hold a search finger. The me_hash field of Unused or Dummy slots has no +meaning otherwise. +*/ + +/* PyDict_MINSIZE is the minimum size of a dictionary. This many slots are + * allocated directly in the dict object (in the ma_smalltable member). + * It must be a power of 2, and at least 4. 8 allows dicts with no more + * than 5 active entries to live in ma_smalltable (and so avoid an + * additional malloc); instrumentation suggested this suffices for the + * majority of dicts (consisting mostly of usually-small instance dicts and + * usually-small dicts created to pass keyword arguments). + */ +#define PyDict_MINSIZE 8 + +typedef struct { + long me_hash; /* cached hash code of me_key */ + PyObject *me_key; + PyObject *me_value; +} PyDictEntry; + +/* +To ensure the lookup algorithm terminates, there must be at least one Unused +slot (NULL key) in the table. +The value ma_fill is the number of non-NULL keys (sum of Active and Dummy); +ma_used is the number of non-NULL, non-dummy keys (== the number of non-NULL +values == the number of Active items). +To avoid slowing down lookups on a near-full table, we resize the table when +it's two-thirds full. +*/ +typedef struct _dictobject PyDictObject; +struct _dictobject { + PyObject_HEAD + int ma_fill; /* # Active + # Dummy */ + int ma_used; /* # Active */ + + /* The table contains ma_mask + 1 slots, and that's a power of 2. + * We store the mask instead of the size because the mask is more + * frequently needed. + */ + int ma_mask; + + /* ma_table points to ma_smalltable for small tables, else to + * additional malloc'ed memory. ma_table is never NULL! This rule + * saves repeated runtime null-tests in the workhorse getitem and + * setitem calls. + */ + PyDictEntry *ma_table; + PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, long hash); + PyDictEntry ma_smalltable[PyDict_MINSIZE]; +}; + +PyAPI_DATA(PyTypeObject) PyDict_Type; + +#define PyDict_Check(op) PyObject_TypeCheck(op, &PyDict_Type) + +PyAPI_FUNC(PyObject *) PyDict_New(void); +PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key); +PyAPI_FUNC(int) PyDict_SetItem(PyObject *mp, PyObject *key, PyObject *item); +PyAPI_FUNC(int) PyDict_DelItem(PyObject *mp, PyObject *key); +PyAPI_FUNC(void) PyDict_Clear(PyObject *mp); +PyAPI_FUNC(int) PyDict_Next( + PyObject *mp, int *pos, PyObject **key, PyObject **value); +PyAPI_FUNC(PyObject *) PyDict_Keys(PyObject *mp); +PyAPI_FUNC(PyObject *) PyDict_Values(PyObject *mp); +PyAPI_FUNC(PyObject *) PyDict_Items(PyObject *mp); +PyAPI_FUNC(int) PyDict_Size(PyObject *mp); +PyAPI_FUNC(PyObject *) PyDict_Copy(PyObject *mp); + +/* PyDict_Update(mp, other) is equivalent to PyDict_Merge(mp, other, 1). */ +PyAPI_FUNC(int) PyDict_Update(PyObject *mp, PyObject *other); + +/* PyDict_Merge updates/merges from a mapping object (an object that + supports PyMapping_Keys() and PyObject_GetItem()). If override is true, + the last occurrence of a key wins, else the first. The Python + dict.update(other) is equivalent to PyDict_Merge(dict, other, 1). +*/ +PyAPI_FUNC(int) PyDict_Merge(PyObject *mp, + PyObject *other, + int override); + +/* PyDict_MergeFromSeq2 updates/merges from an iterable object producing + iterable objects of length 2. If override is true, the last occurrence + of a key wins, else the first. The Python dict constructor dict(seq2) + is equivalent to dict={}; PyDict_MergeFromSeq(dict, seq2, 1). +*/ +PyAPI_FUNC(int) PyDict_MergeFromSeq2(PyObject *d, + PyObject *seq2, + int override); + +PyAPI_FUNC(PyObject *) PyDict_GetItemString(PyObject *dp, const char *key); +PyAPI_FUNC(int) PyDict_SetItemString(PyObject *dp, const char *key, PyObject *item); +PyAPI_FUNC(int) PyDict_DelItemString(PyObject *dp, const char *key); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_DICTOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/enumobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/enumobject.h new file mode 100644 index 00000000..4a48b96d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/enumobject.h @@ -0,0 +1,16 @@ +#ifndef Py_ENUMOBJECT_H +#define Py_ENUMOBJECT_H + +/* Enumerate Object */ + +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyEnum_Type; + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_ENUMOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/errcode.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/errcode.h new file mode 100644 index 00000000..a61ebb54 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/errcode.h @@ -0,0 +1,35 @@ +#ifndef Py_ERRCODE_H +#define Py_ERRCODE_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Error codes passed around between file input, tokenizer, parser and + interpreter. This is necessary so we can turn them into Python + exceptions at a higher level. Note that some errors have a + slightly different meaning when passed from the tokenizer to the + parser than when passed from the parser to the interpreter; e.g. + the parser only returns E_EOF when it hits EOF immediately, and it + never returns E_OK. */ + +#define E_OK 10 /* No error */ +#define E_EOF 11 /* End Of File */ +#define E_INTR 12 /* Interrupted */ +#define E_TOKEN 13 /* Bad token */ +#define E_SYNTAX 14 /* Syntax error */ +#define E_NOMEM 15 /* Ran out of memory */ +#define E_DONE 16 /* Parsing complete */ +#define E_ERROR 17 /* Execution error */ +#define E_TABSPACE 18 /* Inconsistent mixing of tabs and spaces */ +#define E_OVERFLOW 19 /* Node had too many children */ +#define E_TOODEEP 20 /* Too many indentation levels */ +#define E_DEDENT 21 /* No matching outer block for dedent */ +#define E_DECODE 22 /* Error in decoding into Unicode */ +#define E_EOFS 23 /* EOF in triple-quoted string */ +#define E_EOLS 24 /* EOL in single-quoted string */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_ERRCODE_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/eval.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/eval.h new file mode 100644 index 00000000..fe1d47ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/eval.h @@ -0,0 +1,25 @@ + +/* Interface to execute compiled code */ + +#ifndef Py_EVAL_H +#define Py_EVAL_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(PyObject *) PyEval_EvalCode(PyCodeObject *, PyObject *, PyObject *); + +PyAPI_FUNC(PyObject *) PyEval_EvalCodeEx(PyCodeObject *co, + PyObject *globals, + PyObject *locals, + PyObject **args, int argc, + PyObject **kwds, int kwdc, + PyObject **defs, int defc, + PyObject *closure); + +PyAPI_FUNC(PyObject *) _PyEval_CallTracing(PyObject *func, PyObject *args); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_EVAL_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/fileobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/fileobject.h new file mode 100644 index 00000000..9c78c6e7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/fileobject.h @@ -0,0 +1,70 @@ + +/* File object interface */ + +#ifndef Py_FILEOBJECT_H +#define Py_FILEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_HEAD + FILE *f_fp; + PyObject *f_name; + PyObject *f_mode; + int (*f_close)(FILE *); + int f_softspace; /* Flag used by 'print' command */ + int f_binary; /* Flag which indicates whether the file is + open in binary (1) or text (0) mode */ + char* f_buf; /* Allocated readahead buffer */ + char* f_bufend; /* Points after last occupied position */ + char* f_bufptr; /* Current buffer position */ + char *f_setbuf; /* Buffer for setbuf(3) and setvbuf(3) */ +#ifdef WITH_UNIVERSAL_NEWLINES + int f_univ_newline; /* Handle any newline convention */ + int f_newlinetypes; /* Types of newlines seen */ + int f_skipnextlf; /* Skip next \n */ +#endif + PyObject *f_encoding; +} PyFileObject; + +PyAPI_DATA(PyTypeObject) PyFile_Type; + +#define PyFile_Check(op) PyObject_TypeCheck(op, &PyFile_Type) +#define PyFile_CheckExact(op) ((op)->ob_type == &PyFile_Type) + +PyAPI_FUNC(PyObject *) PyFile_FromString(char *, char *); +PyAPI_FUNC(void) PyFile_SetBufSize(PyObject *, int); +PyAPI_FUNC(int) PyFile_SetEncoding(PyObject *, const char *); +PyAPI_FUNC(PyObject *) PyFile_FromFile(FILE *, char *, char *, + int (*)(FILE *)); +PyAPI_FUNC(FILE *) PyFile_AsFile(PyObject *); +PyAPI_FUNC(PyObject *) PyFile_Name(PyObject *); +PyAPI_FUNC(PyObject *) PyFile_GetLine(PyObject *, int); +PyAPI_FUNC(int) PyFile_WriteObject(PyObject *, PyObject *, int); +PyAPI_FUNC(int) PyFile_SoftSpace(PyObject *, int); +PyAPI_FUNC(int) PyFile_WriteString(const char *, PyObject *); +PyAPI_FUNC(int) PyObject_AsFileDescriptor(PyObject *); + +/* The default encoding used by the platform file system APIs + If non-NULL, this is different than the default encoding for strings +*/ +PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; + +#ifdef WITH_UNIVERSAL_NEWLINES +/* Routines to replace fread() and fgets() which accept any of \r, \n + or \r\n as line terminators. +*/ +#define PY_STDIOTEXTMODE "b" +char *Py_UniversalNewlineFgets(char *, int, FILE*, PyObject *); +size_t Py_UniversalNewlineFread(char *, size_t, FILE *, PyObject *); +#else +#define PY_STDIOTEXTMODE "" +#define Py_UniversalNewlineFgets(buf, len, fp, obj) fgets((buf), (len), (fp)) +#define Py_UniversalNewlineFread(buf, len, fp, obj) \ + fread((buf), 1, (len), (fp)) +#endif /* WITH_UNIVERSAL_NEWLINES */ +#ifdef __cplusplus +} +#endif +#endif /* !Py_FILEOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/floatobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/floatobject.h new file mode 100644 index 00000000..a0207976 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/floatobject.h @@ -0,0 +1,95 @@ + +/* Float object interface */ + +/* +PyFloatObject represents a (double precision) floating point number. +*/ + +#ifndef Py_FLOATOBJECT_H +#define Py_FLOATOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_HEAD + double ob_fval; +} PyFloatObject; + +PyAPI_DATA(PyTypeObject) PyFloat_Type; + +#define PyFloat_Check(op) PyObject_TypeCheck(op, &PyFloat_Type) +#define PyFloat_CheckExact(op) ((op)->ob_type == &PyFloat_Type) + +/* Return Python float from string PyObject. Second argument ignored on + input, and, if non-NULL, NULL is stored into *junk (this tried to serve a + purpose once but can't be made to work as intended). */ +PyAPI_FUNC(PyObject *) PyFloat_FromString(PyObject*, char** junk); + +/* Return Python float from C double. */ +PyAPI_FUNC(PyObject *) PyFloat_FromDouble(double); + +/* Extract C double from Python float. The macro version trades safety for + speed. */ +PyAPI_FUNC(double) PyFloat_AsDouble(PyObject *); +#define PyFloat_AS_DOUBLE(op) (((PyFloatObject *)(op))->ob_fval) + +/* Write repr(v) into the char buffer argument, followed by null byte. The + buffer must be "big enough"; >= 100 is very safe. + PyFloat_AsReprString(buf, x) strives to print enough digits so that + PyFloat_FromString(buf) then reproduces x exactly. */ +PyAPI_FUNC(void) PyFloat_AsReprString(char*, PyFloatObject *v); + +/* Write str(v) into the char buffer argument, followed by null byte. The + buffer must be "big enough"; >= 100 is very safe. Note that it's + unusual to be able to get back the float you started with from + PyFloat_AsString's result -- use PyFloat_AsReprString() if you want to + preserve precision across conversions. */ +PyAPI_FUNC(void) PyFloat_AsString(char*, PyFloatObject *v); + +/* _PyFloat_{Pack,Unpack}{4,8} + * + * The struct and pickle (at least) modules need an efficient platform- + * independent way to store floating-point values as byte strings. + * The Pack routines produce a string from a C double, and the Unpack + * routines produce a C double from such a string. The suffix (4 or 8) + * specifies the number of bytes in the string. + * + * Excepting NaNs and infinities (which aren't handled correctly), the 4- + * byte format is identical to the IEEE-754 single precision format, and + * the 8-byte format to the IEEE-754 double precision format. On non- + * IEEE platforms with more precision, or larger dynamic range, than + * 754 supports, not all values can be packed; on non-IEEE platforms with + * less precision, or smaller dynamic range, not all values can be + * unpacked. What happens in such cases is partly accidental (alas). + */ + +/* The pack routines write 4 or 8 bytes, starting at p. le is a bool + * argument, true if you want the string in little-endian format (exponent + * last, at p+3 or p+7), false if you want big-endian format (exponent + * first, at p). + * Return value: 0 if all is OK, -1 if error (and an exception is + * set, most likely OverflowError). + * Bug: What this does is undefined if x is a NaN or infinity. + * Bug: -0.0 and +0.0 produce the same string. + */ +PyAPI_FUNC(int) _PyFloat_Pack4(double x, unsigned char *p, int le); +PyAPI_FUNC(int) _PyFloat_Pack8(double x, unsigned char *p, int le); + +/* The unpack routines read 4 or 8 bytes, starting at p. le is a bool + * argument, true if the string is in little-endian format (exponent + * last, at p+3 or p+7), false if big-endian (exponent first, at p). + * Return value: The unpacked double. On error, this is -1.0 and + * PyErr_Occurred() is true (and an exception is set, most likely + * OverflowError). + * Bug: What this does is undefined if the string represents a NaN or + * infinity. + */ +PyAPI_FUNC(double) _PyFloat_Unpack4(const unsigned char *p, int le); +PyAPI_FUNC(double) _PyFloat_Unpack8(const unsigned char *p, int le); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_FLOATOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/frameobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/frameobject.h new file mode 100644 index 00000000..d25393dc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/frameobject.h @@ -0,0 +1,76 @@ + +/* Frame object interface */ + +#ifndef Py_FRAMEOBJECT_H +#define Py_FRAMEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int b_type; /* what kind of block this is */ + int b_handler; /* where to jump to find handler */ + int b_level; /* value stack level to pop to */ +} PyTryBlock; + +typedef struct _frame { + PyObject_VAR_HEAD + struct _frame *f_back; /* previous frame, or NULL */ + PyCodeObject *f_code; /* code segment */ + PyObject *f_builtins; /* builtin symbol table (PyDictObject) */ + PyObject *f_globals; /* global symbol table (PyDictObject) */ + PyObject *f_locals; /* local symbol table (PyDictObject) */ + PyObject **f_valuestack; /* points after the last local */ + /* Next free slot in f_valuestack. Frame creation sets to f_valuestack. + Frame evaluation usually NULLs it, but a frame that yields sets it + to the current stack top. */ + PyObject **f_stacktop; + PyObject *f_trace; /* Trace function */ + PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; + PyThreadState *f_tstate; + int f_lasti; /* Last instruction if called */ + /* As of 2.3 f_lineno is only valid when tracing is active (i.e. when + f_trace is set) -- at other times use PyCode_Addr2Line instead. */ + int f_lineno; /* Current line number */ + int f_restricted; /* Flag set if restricted operations + in this scope */ + int f_iblock; /* index in f_blockstack */ + PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ + int f_nlocals; /* number of locals */ + int f_ncells; + int f_nfreevars; + int f_stacksize; /* size of value stack */ + PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ +} PyFrameObject; + + +/* Standard object interface */ + +PyAPI_DATA(PyTypeObject) PyFrame_Type; + +#define PyFrame_Check(op) ((op)->ob_type == &PyFrame_Type) + +PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *, + PyObject *, PyObject *); + + +/* The rest of the interface is specific for frame objects */ + +/* Block management functions */ + +PyAPI_FUNC(void) PyFrame_BlockSetup(PyFrameObject *, int, int, int); +PyAPI_FUNC(PyTryBlock *) PyFrame_BlockPop(PyFrameObject *); + +/* Extend the value stack */ + +PyAPI_FUNC(PyObject **) PyFrame_ExtendStack(PyFrameObject *, int, int); + +/* Conversions between "fast locals" and locals in dictionary */ + +PyAPI_FUNC(void) PyFrame_LocalsToFast(PyFrameObject *, int); +PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_FRAMEOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/funcobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/funcobject.h new file mode 100644 index 00000000..4927da59 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/funcobject.h @@ -0,0 +1,59 @@ + +/* Function object interface */ + +#ifndef Py_FUNCOBJECT_H +#define Py_FUNCOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_HEAD + PyObject *func_code; + PyObject *func_globals; + PyObject *func_defaults; + PyObject *func_closure; + PyObject *func_doc; + PyObject *func_name; + PyObject *func_dict; + PyObject *func_weakreflist; + PyObject *func_module; +} PyFunctionObject; + +PyAPI_DATA(PyTypeObject) PyFunction_Type; + +#define PyFunction_Check(op) ((op)->ob_type == &PyFunction_Type) + +PyAPI_FUNC(PyObject *) PyFunction_New(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetCode(PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetGlobals(PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetModule(PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetDefaults(PyObject *); +PyAPI_FUNC(int) PyFunction_SetDefaults(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyFunction_GetClosure(PyObject *); +PyAPI_FUNC(int) PyFunction_SetClosure(PyObject *, PyObject *); + +/* Macros for direct access to these values. Type checks are *not* + done, so use with care. */ +#define PyFunction_GET_CODE(func) \ + (((PyFunctionObject *)func) -> func_code) +#define PyFunction_GET_GLOBALS(func) \ + (((PyFunctionObject *)func) -> func_globals) +#define PyFunction_GET_MODULE(func) \ + (((PyFunctionObject *)func) -> func_module) +#define PyFunction_GET_DEFAULTS(func) \ + (((PyFunctionObject *)func) -> func_defaults) +#define PyFunction_GET_CLOSURE(func) \ + (((PyFunctionObject *)func) -> func_closure) + +/* The classmethod and staticmethod types lives here, too */ +PyAPI_DATA(PyTypeObject) PyClassMethod_Type; +PyAPI_DATA(PyTypeObject) PyStaticMethod_Type; + +PyAPI_FUNC(PyObject *) PyClassMethod_New(PyObject *); +PyAPI_FUNC(PyObject *) PyStaticMethod_New(PyObject *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_FUNCOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/graminit.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/graminit.h new file mode 100644 index 00000000..e0d2cb6c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/graminit.h @@ -0,0 +1,68 @@ +#define single_input 256 +#define file_input 257 +#define eval_input 258 +#define funcdef 259 +#define parameters 260 +#define varargslist 261 +#define fpdef 262 +#define fplist 263 +#define stmt 264 +#define simple_stmt 265 +#define small_stmt 266 +#define expr_stmt 267 +#define augassign 268 +#define print_stmt 269 +#define del_stmt 270 +#define pass_stmt 271 +#define flow_stmt 272 +#define break_stmt 273 +#define continue_stmt 274 +#define return_stmt 275 +#define yield_stmt 276 +#define raise_stmt 277 +#define import_stmt 278 +#define import_as_name 279 +#define dotted_as_name 280 +#define dotted_name 281 +#define global_stmt 282 +#define exec_stmt 283 +#define assert_stmt 284 +#define compound_stmt 285 +#define if_stmt 286 +#define while_stmt 287 +#define for_stmt 288 +#define try_stmt 289 +#define except_clause 290 +#define suite 291 +#define test 292 +#define and_test 293 +#define not_test 294 +#define comparison 295 +#define comp_op 296 +#define expr 297 +#define xor_expr 298 +#define and_expr 299 +#define shift_expr 300 +#define arith_expr 301 +#define term 302 +#define factor 303 +#define power 304 +#define atom 305 +#define listmaker 306 +#define lambdef 307 +#define trailer 308 +#define subscriptlist 309 +#define subscript 310 +#define sliceop 311 +#define exprlist 312 +#define testlist 313 +#define testlist_safe 314 +#define dictmaker 315 +#define classdef 316 +#define arglist 317 +#define argument 318 +#define list_iter 319 +#define list_for 320 +#define list_if 321 +#define testlist1 322 +#define encoding_decl 323 diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/grammar.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/grammar.h new file mode 100644 index 00000000..369d4a34 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/grammar.h @@ -0,0 +1,93 @@ + +/* Grammar interface */ + +#ifndef Py_GRAMMAR_H +#define Py_GRAMMAR_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "bitset.h" /* Sigh... */ + +/* A label of an arc */ + +typedef struct { + int lb_type; + char *lb_str; +} label; + +#define EMPTY 0 /* Label number 0 is by definition the empty label */ + +/* A list of labels */ + +typedef struct { + int ll_nlabels; + label *ll_label; +} labellist; + +/* An arc from one state to another */ + +typedef struct { + short a_lbl; /* Label of this arc */ + short a_arrow; /* State where this arc goes to */ +} arc; + +/* A state in a DFA */ + +typedef struct { + int s_narcs; + arc *s_arc; /* Array of arcs */ + + /* Optional accelerators */ + int s_lower; /* Lowest label index */ + int s_upper; /* Highest label index */ + int *s_accel; /* Accelerator */ + int s_accept; /* Nonzero for accepting state */ +} state; + +/* A DFA */ + +typedef struct { + int d_type; /* Non-terminal this represents */ + char *d_name; /* For printing */ + int d_initial; /* Initial state */ + int d_nstates; + state *d_state; /* Array of states */ + bitset d_first; +} dfa; + +/* A grammar */ + +typedef struct { + int g_ndfas; + dfa *g_dfa; /* Array of DFAs */ + labellist g_ll; + int g_start; /* Start symbol of the grammar */ + int g_accel; /* Set if accelerators present */ +} grammar; + +/* FUNCTIONS */ + +grammar *newgrammar(int start); +dfa *adddfa(grammar *g, int type, char *name); +int addstate(dfa *d); +void addarc(dfa *d, int from, int to, int lbl); +dfa *PyGrammar_FindDFA(grammar *g, int type); + +int addlabel(labellist *ll, int type, char *str); +int findlabel(labellist *ll, int type, char *str); +char *PyGrammar_LabelRepr(label *lb); +void translatelabels(grammar *g); + +void addfirstsets(grammar *g); + +void PyGrammar_AddAccelerators(grammar *g); +void PyGrammar_RemoveAccelerators(grammar *); + +void printgrammar(grammar *g, FILE *fp); +void printnonterminals(grammar *g, FILE *fp); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GRAMMAR_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/import.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/import.h new file mode 100644 index 00000000..27bbf68c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/import.h @@ -0,0 +1,51 @@ + +/* Module definition and import interface */ + +#ifndef Py_IMPORT_H +#define Py_IMPORT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(long) PyImport_GetMagicNumber(void); +PyAPI_FUNC(PyObject *) PyImport_ExecCodeModule(char *name, PyObject *co); +PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleEx( + char *name, PyObject *co, char *pathname); +PyAPI_FUNC(PyObject *) PyImport_GetModuleDict(void); +PyAPI_FUNC(PyObject *) PyImport_AddModule(char *name); +PyAPI_FUNC(PyObject *) PyImport_ImportModule(char *name); +PyAPI_FUNC(PyObject *) PyImport_ImportModuleEx( + char *name, PyObject *globals, PyObject *locals, PyObject *fromlist); +PyAPI_FUNC(PyObject *) PyImport_Import(PyObject *name); +PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m); +PyAPI_FUNC(void) PyImport_Cleanup(void); +PyAPI_FUNC(int) PyImport_ImportFrozenModule(char *); + +PyAPI_FUNC(PyObject *)_PyImport_FindExtension(char *, char *); +PyAPI_FUNC(PyObject *)_PyImport_FixupExtension(char *, char *); + +struct _inittab { + char *name; + void (*initfunc)(void); +}; + +PyAPI_DATA(struct _inittab *) PyImport_Inittab; + +PyAPI_FUNC(int) PyImport_AppendInittab(char *name, void (*initfunc)(void)); +PyAPI_FUNC(int) PyImport_ExtendInittab(struct _inittab *newtab); + +struct _frozen { + char *name; + unsigned char *code; + int size; +}; + +/* Embedding apps may change this pointer to point to their favorite + collection of frozen modules: */ + +PyAPI_DATA(struct _frozen *) PyImport_FrozenModules; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_IMPORT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/intobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/intobject.h new file mode 100644 index 00000000..2551b39d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/intobject.h @@ -0,0 +1,61 @@ + +/* Integer object interface */ + +/* +PyIntObject represents a (long) integer. This is an immutable object; +an integer cannot change its value after creation. + +There are functions to create new integer objects, to test an object +for integer-ness, and to get the integer value. The latter functions +returns -1 and sets errno to EBADF if the object is not an PyIntObject. +None of the functions should be applied to nil objects. + +The type PyIntObject is (unfortunately) exposed here so we can declare +_Py_TrueStruct and _Py_ZeroStruct below; don't use this. +*/ + +#ifndef Py_INTOBJECT_H +#define Py_INTOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_HEAD + long ob_ival; +} PyIntObject; + +PyAPI_DATA(PyTypeObject) PyInt_Type; + +#define PyInt_Check(op) PyObject_TypeCheck(op, &PyInt_Type) +#define PyInt_CheckExact(op) ((op)->ob_type == &PyInt_Type) + +PyAPI_FUNC(PyObject *) PyInt_FromString(char*, char**, int); +#ifdef Py_USING_UNICODE +PyAPI_FUNC(PyObject *) PyInt_FromUnicode(Py_UNICODE*, int, int); +#endif +PyAPI_FUNC(PyObject *) PyInt_FromLong(long); +PyAPI_FUNC(long) PyInt_AsLong(PyObject *); +PyAPI_FUNC(unsigned long) PyInt_AsUnsignedLongMask(PyObject *); +#ifdef HAVE_LONG_LONG +PyAPI_FUNC(unsigned PY_LONG_LONG) PyInt_AsUnsignedLongLongMask(PyObject *); +#endif + +PyAPI_FUNC(long) PyInt_GetMax(void); + +/* Macro, trading safety for speed */ +#define PyInt_AS_LONG(op) (((PyIntObject *)(op))->ob_ival) + +/* These aren't really part of the Int object, but they're handy; the protos + * are necessary for systems that need the magic of PyAPI_FUNC and that want + * to have stropmodule as a dynamically loaded module instead of building it + * into the main Python shared library/DLL. Guido thinks I'm weird for + * building it this way. :-) [cjh] + */ +PyAPI_FUNC(unsigned long) PyOS_strtoul(char *, char **, int); +PyAPI_FUNC(long) PyOS_strtol(char *, char **, int); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/intrcheck.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/intrcheck.h new file mode 100644 index 00000000..11d759e4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/intrcheck.h @@ -0,0 +1,15 @@ + +#ifndef Py_INTRCHECK_H +#define Py_INTRCHECK_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(int) PyOS_InterruptOccurred(void); +PyAPI_FUNC(void) PyOS_InitInterrupts(void); +PyAPI_FUNC(void) PyOS_AfterFork(void); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTRCHECK_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/iterobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/iterobject.h new file mode 100644 index 00000000..ba507d30 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/iterobject.h @@ -0,0 +1,23 @@ +#ifndef Py_ITEROBJECT_H +#define Py_ITEROBJECT_H +/* Iterators (the basic kind, over a sequence) */ +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PySeqIter_Type; + +#define PySeqIter_Check(op) ((op)->ob_type == &PySeqIter_Type) + +PyAPI_FUNC(PyObject *) PySeqIter_New(PyObject *); + +PyAPI_DATA(PyTypeObject) PyCallIter_Type; + +#define PyCallIter_Check(op) ((op)->ob_type == &PyCallIter_Type) + +PyAPI_FUNC(PyObject *) PyCallIter_New(PyObject *, PyObject *); +#ifdef __cplusplus +} +#endif +#endif /* !Py_ITEROBJECT_H */ + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/listobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/listobject.h new file mode 100644 index 00000000..0e820ffa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/listobject.h @@ -0,0 +1,52 @@ + +/* List object interface */ + +/* +Another generally useful object type is an list of object pointers. +This is a mutable type: the list items can be changed, and items can be +added or removed. Out-of-range indices or non-list objects are ignored. + +*** WARNING *** PyList_SetItem does not increment the new item's reference +count, but does decrement the reference count of the item it replaces, +if not nil. It does *decrement* the reference count if it is *not* +inserted in the list. Similarly, PyList_GetItem does not increment the +returned item's reference count. +*/ + +#ifndef Py_LISTOBJECT_H +#define Py_LISTOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PyObject_VAR_HEAD + PyObject **ob_item; +} PyListObject; + +PyAPI_DATA(PyTypeObject) PyList_Type; + +#define PyList_Check(op) PyObject_TypeCheck(op, &PyList_Type) +#define PyList_CheckExact(op) ((op)->ob_type == &PyList_Type) + +PyAPI_FUNC(PyObject *) PyList_New(int size); +PyAPI_FUNC(int) PyList_Size(PyObject *); +PyAPI_FUNC(PyObject *) PyList_GetItem(PyObject *, int); +PyAPI_FUNC(int) PyList_SetItem(PyObject *, int, PyObject *); +PyAPI_FUNC(int) PyList_Insert(PyObject *, int, PyObject *); +PyAPI_FUNC(int) PyList_Append(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyList_GetSlice(PyObject *, int, int); +PyAPI_FUNC(int) PyList_SetSlice(PyObject *, int, int, PyObject *); +PyAPI_FUNC(int) PyList_Sort(PyObject *); +PyAPI_FUNC(int) PyList_Reverse(PyObject *); +PyAPI_FUNC(PyObject *) PyList_AsTuple(PyObject *); + +/* Macro, trading safety for speed */ +#define PyList_GET_ITEM(op, i) (((PyListObject *)(op))->ob_item[i]) +#define PyList_SET_ITEM(op, i, v) (((PyListObject *)(op))->ob_item[i] = (v)) +#define PyList_GET_SIZE(op) (((PyListObject *)(op))->ob_size) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_LISTOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/longintrepr.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/longintrepr.h new file mode 100644 index 00000000..9d375a90 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/longintrepr.h @@ -0,0 +1,58 @@ +#ifndef Py_LONGINTREPR_H +#define Py_LONGINTREPR_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* This is published for the benefit of "friend" marshal.c only. */ + +/* Parameters of the long integer representation. + These shouldn't have to be changed as C should guarantee that a short + contains at least 16 bits, but it's made changeable anyway. + Note: 'digit' should be able to hold 2*MASK+1, and 'twodigits' + should be able to hold the intermediate results in 'mul' + (at most MASK << SHIFT). + Also, x_sub assumes that 'digit' is an unsigned type, and overflow + is handled by taking the result mod 2**N for some N > SHIFT. + And, at some places it is assumed that MASK fits in an int, as well. */ + +typedef unsigned short digit; +typedef unsigned int wdigit; /* digit widened to parameter size */ +#define BASE_TWODIGITS_TYPE long +typedef unsigned BASE_TWODIGITS_TYPE twodigits; +typedef BASE_TWODIGITS_TYPE stwodigits; /* signed variant of twodigits */ + +#define SHIFT 15 +#define BASE ((digit)1 << SHIFT) +#define MASK ((int)(BASE - 1)) + +/* Long integer representation. + The absolute value of a number is equal to + SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i) + Negative numbers are represented with ob_size < 0; + zero is represented by ob_size == 0. + In a normalized number, ob_digit[abs(ob_size)-1] (the most significant + digit) is never zero. Also, in all cases, for all valid i, + 0 <= ob_digit[i] <= MASK. + The allocation function takes care of allocating extra memory + so that ob_digit[0] ... ob_digit[abs(ob_size)-1] are actually available. + + CAUTION: Generic code manipulating subtypes of PyVarObject has to + aware that longs abuse ob_size's sign bit. +*/ + +struct _longobject { + PyObject_VAR_HEAD + digit ob_digit[1]; +}; + +PyAPI_FUNC(PyLongObject *) _PyLong_New(int); + +/* Return a copy of src. */ +PyAPI_FUNC(PyObject *) _PyLong_Copy(PyLongObject *src); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_LONGINTREPR_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/longobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/longobject.h new file mode 100644 index 00000000..4647eda4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/longobject.h @@ -0,0 +1,109 @@ +#ifndef Py_LONGOBJECT_H +#define Py_LONGOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Long (arbitrary precision) integer object interface */ + +typedef struct _longobject PyLongObject; /* Revealed in longintrepr.h */ + +PyAPI_DATA(PyTypeObject) PyLong_Type; + +#define PyLong_Check(op) PyObject_TypeCheck(op, &PyLong_Type) +#define PyLong_CheckExact(op) ((op)->ob_type == &PyLong_Type) + +PyAPI_FUNC(PyObject *) PyLong_FromLong(long); +PyAPI_FUNC(PyObject *) PyLong_FromUnsignedLong(unsigned long); +PyAPI_FUNC(PyObject *) PyLong_FromDouble(double); +PyAPI_FUNC(long) PyLong_AsLong(PyObject *); +PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *); +PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *); + +/* _PyLong_AsScaledDouble returns a double x and an exponent e such that + the true value is approximately equal to x * 2**(SHIFT*e). e is >= 0. + x is 0.0 if and only if the input is 0 (in which case, e and x are both + zeroes). Overflow is impossible. Note that the exponent returned must + be multiplied by SHIFT! There may not be enough room in an int to store + e*SHIFT directly. */ +PyAPI_FUNC(double) _PyLong_AsScaledDouble(PyObject *vv, int *e); + +PyAPI_FUNC(double) PyLong_AsDouble(PyObject *); +PyAPI_FUNC(PyObject *) PyLong_FromVoidPtr(void *); +PyAPI_FUNC(void *) PyLong_AsVoidPtr(PyObject *); + +#ifdef HAVE_LONG_LONG +PyAPI_FUNC(PyObject *) PyLong_FromLongLong(PY_LONG_LONG); +PyAPI_FUNC(PyObject *) PyLong_FromUnsignedLongLong(unsigned PY_LONG_LONG); +PyAPI_FUNC(PY_LONG_LONG) PyLong_AsLongLong(PyObject *); +PyAPI_FUNC(unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLong(PyObject *); +PyAPI_FUNC(unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLongMask(PyObject *); +#endif /* HAVE_LONG_LONG */ + +PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int); +#ifdef Py_USING_UNICODE +PyAPI_FUNC(PyObject *) PyLong_FromUnicode(Py_UNICODE*, int, int); +#endif + +/* _PyLong_Sign. Return 0 if v is 0, -1 if v < 0, +1 if v > 0. + v must not be NULL, and must be a normalized long. + There are no error cases. +*/ +PyAPI_FUNC(int) _PyLong_Sign(PyObject *v); + + +PyAPI_FUNC(size_t) _PyLong_NumBits(PyObject *v); +/* _PyLong_NumBits. Return the number of bits needed to represent the + absolute value of a long. For example, this returns 1 for 1 and -1, 2 + for 2 and -2, and 2 for 3 and -3. It returns 0 for 0. + v must not be NULL, and must be a normalized long. + (size_t)-1 is returned and OverflowError set if the true result doesn't + fit in a size_t. +*/ +PyAPI_FUNC(size_t) _PyLong_NumBits(PyObject *v); + +/* _PyLong_FromByteArray: View the n unsigned bytes as a binary integer in + base 256, and return a Python long with the same numeric value. + If n is 0, the integer is 0. Else: + If little_endian is 1/true, bytes[n-1] is the MSB and bytes[0] the LSB; + else (little_endian is 0/false) bytes[0] is the MSB and bytes[n-1] the + LSB. + If is_signed is 0/false, view the bytes as a non-negative integer. + If is_signed is 1/true, view the bytes as a 2's-complement integer, + non-negative if bit 0x80 of the MSB is clear, negative if set. + Error returns: + + Return NULL with the appropriate exception set if there's not + enough memory to create the Python long. +*/ +PyAPI_FUNC(PyObject *) _PyLong_FromByteArray( + const unsigned char* bytes, size_t n, + int little_endian, int is_signed); + +/* _PyLong_AsByteArray: Convert the least-significant 8*n bits of long + v to a base-256 integer, stored in array bytes. Normally return 0, + return -1 on error. + If little_endian is 1/true, store the MSB at bytes[n-1] and the LSB at + bytes[0]; else (little_endian is 0/false) store the MSB at bytes[0] and + the LSB at bytes[n-1]. + If is_signed is 0/false, it's an error if v < 0; else (v >= 0) n bytes + are filled and there's nothing special about bit 0x80 of the MSB. + If is_signed is 1/true, bytes is filled with the 2's-complement + representation of v's value. Bit 0x80 of the MSB is the sign bit. + Error returns (-1): + + is_signed is 0 and v < 0. TypeError is set in this case, and bytes + isn't altered. + + n isn't big enough to hold the full mathematical value of v. For + example, if is_signed is 0 and there are more digits in the v than + fit in n; or if is_signed is 1, v < 0, and n is just 1 bit shy of + being large enough to hold a sign bit. OverflowError is set in this + case, but bytes holds the least-signficant n bytes of the true value. +*/ +PyAPI_FUNC(int) _PyLong_AsByteArray(PyLongObject* v, + unsigned char* bytes, size_t n, + int little_endian, int is_signed); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_LONGOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/marshal.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/marshal.h new file mode 100644 index 00000000..87fcf5f2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/marshal.h @@ -0,0 +1,23 @@ + +/* Interface for marshal.c */ + +#ifndef Py_MARSHAL_H +#define Py_MARSHAL_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(void) PyMarshal_WriteLongToFile(long, FILE *); +PyAPI_FUNC(void) PyMarshal_WriteObjectToFile(PyObject *, FILE *); +PyAPI_FUNC(PyObject *) PyMarshal_WriteObjectToString(PyObject *); + +PyAPI_FUNC(long) PyMarshal_ReadLongFromFile(FILE *); +PyAPI_FUNC(int) PyMarshal_ReadShortFromFile(FILE *); +PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromFile(FILE *); +PyAPI_FUNC(PyObject *) PyMarshal_ReadLastObjectFromFile(FILE *); +PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(char *, int); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_MARSHAL_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/metagrammar.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/metagrammar.h new file mode 100644 index 00000000..1fb471a0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/metagrammar.h @@ -0,0 +1,18 @@ +#ifndef Py_METAGRAMMAR_H +#define Py_METAGRAMMAR_H +#ifdef __cplusplus +extern "C" { +#endif + + +#define MSTART 256 +#define RULE 257 +#define RHS 258 +#define ALT 259 +#define ITEM 260 +#define ATOM 261 + +#ifdef __cplusplus +} +#endif +#endif /* !Py_METAGRAMMAR_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/methodobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/methodobject.h new file mode 100644 index 00000000..5298c5a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/methodobject.h @@ -0,0 +1,79 @@ + +/* Method object interface */ + +#ifndef Py_METHODOBJECT_H +#define Py_METHODOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyCFunction_Type; + +#define PyCFunction_Check(op) ((op)->ob_type == &PyCFunction_Type) + +typedef PyObject *(*PyCFunction)(PyObject *, PyObject *); +typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *, + PyObject *); +typedef PyObject *(*PyNoArgsFunction)(PyObject *); + +PyAPI_FUNC(PyCFunction) PyCFunction_GetFunction(PyObject *); +PyAPI_FUNC(PyObject *) PyCFunction_GetSelf(PyObject *); +PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *); + +/* Macros for direct access to these values. Type checks are *not* + done, so use with care. */ +#define PyCFunction_GET_FUNCTION(func) \ + (((PyCFunctionObject *)func) -> m_ml -> ml_meth) +#define PyCFunction_GET_SELF(func) \ + (((PyCFunctionObject *)func) -> m_self) +#define PyCFunction_GET_FLAGS(func) \ + (((PyCFunctionObject *)func) -> m_ml -> ml_flags) +PyAPI_FUNC(PyObject *) PyCFunction_Call(PyObject *, PyObject *, PyObject *); + +struct PyMethodDef { + char *ml_name; + PyCFunction ml_meth; + int ml_flags; + char *ml_doc; +}; +typedef struct PyMethodDef PyMethodDef; + +PyAPI_FUNC(PyObject *) Py_FindMethod(PyMethodDef[], PyObject *, char *); + +#define PyCFunction_New(ML, SELF) PyCFunction_NewEx((ML), (SELF), NULL) +PyAPI_FUNC(PyObject *) PyCFunction_NewEx(PyMethodDef *, PyObject *, + PyObject *); + +/* Flag passed to newmethodobject */ +#define METH_OLDARGS 0x0000 +#define METH_VARARGS 0x0001 +#define METH_KEYWORDS 0x0002 +/* METH_NOARGS and METH_O must not be combined with the flags above. */ +#define METH_NOARGS 0x0004 +#define METH_O 0x0008 + +/* METH_CLASS and METH_STATIC are a little different; these control + the construction of methods for a class. These cannot be used for + functions in modules. */ +#define METH_CLASS 0x0010 +#define METH_STATIC 0x0020 + +typedef struct PyMethodChain { + PyMethodDef *methods; /* Methods of this type */ + struct PyMethodChain *link; /* NULL or base type */ +} PyMethodChain; + +PyAPI_FUNC(PyObject *) Py_FindMethodInChain(PyMethodChain *, PyObject *, + char *); + +typedef struct { + PyObject_HEAD + PyMethodDef *m_ml; + PyObject *m_self; + PyObject *m_module; +} PyCFunctionObject; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_METHODOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/modsupport.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/modsupport.h new file mode 100644 index 00000000..f38ec203 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/modsupport.h @@ -0,0 +1,101 @@ + +#ifndef Py_MODSUPPORT_H +#define Py_MODSUPPORT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Module support interface */ + +#include + +PyAPI_FUNC(int) PyArg_Parse(PyObject *, char *, ...); +PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, char *, ...); +PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *, + char *, char **, ...); +PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, char *, int, int, ...); +PyAPI_FUNC(PyObject *) Py_BuildValue(char *, ...); + +PyAPI_FUNC(int) PyArg_VaParse(PyObject *, char *, va_list); +PyAPI_FUNC(PyObject *) Py_VaBuildValue(char *, va_list); + +PyAPI_FUNC(int) PyModule_AddObject(PyObject *, char *, PyObject *); +PyAPI_FUNC(int) PyModule_AddIntConstant(PyObject *, char *, long); +PyAPI_FUNC(int) PyModule_AddStringConstant(PyObject *, char *, char *); + +#define PYTHON_API_VERSION 1012 +#define PYTHON_API_STRING "1012" +/* The API version is maintained (independently from the Python version) + so we can detect mismatches between the interpreter and dynamically + loaded modules. These are diagnosed by an error message but + the module is still loaded (because the mismatch can only be tested + after loading the module). The error message is intended to + explain the core dump a few seconds later. + + The symbol PYTHON_API_STRING defines the same value as a string + literal. *** PLEASE MAKE SURE THE DEFINITIONS MATCH. *** + + Please add a line or two to the top of this log for each API + version change: + + 19-Aug-2002 GvR 1012 Changes to string object struct for + interning changes, saving 3 bytes. + + 17-Jul-2001 GvR 1011 Descr-branch, just to be on the safe side + + 25-Jan-2001 FLD 1010 Parameters added to PyCode_New() and + PyFrame_New(); Python 2.1a2 + + 14-Mar-2000 GvR 1009 Unicode API added + + 3-Jan-1999 GvR 1007 Decided to change back! (Don't reuse 1008!) + + 3-Dec-1998 GvR 1008 Python 1.5.2b1 + + 18-Jan-1997 GvR 1007 string interning and other speedups + + 11-Oct-1996 GvR renamed Py_Ellipses to Py_Ellipsis :-( + + 30-Jul-1996 GvR Slice and ellipses syntax added + + 23-Jul-1996 GvR For 1.4 -- better safe than sorry this time :-) + + 7-Nov-1995 GvR Keyword arguments (should've been done at 1.3 :-( ) + + 10-Jan-1995 GvR Renamed globals to new naming scheme + + 9-Jan-1995 GvR Initial version (incompatible with older API) +*/ + +#ifdef MS_WINDOWS +/* Special defines for Windows versions used to live here. Things + have changed, and the "Version" is now in a global string variable. + Reason for this is that this for easier branding of a "custom DLL" + without actually needing a recompile. */ +#endif /* MS_WINDOWS */ + +#ifdef Py_TRACE_REFS +/* When we are tracing reference counts, rename Py_InitModule4 so + modules compiled with incompatible settings will generate a + link-time error. */ +#define Py_InitModule4 Py_InitModule4TraceRefs +#endif + +PyAPI_FUNC(PyObject *) Py_InitModule4(char *name, PyMethodDef *methods, + char *doc, PyObject *self, + int apiver); + +#define Py_InitModule(name, methods) \ + Py_InitModule4(name, methods, (char *)NULL, (PyObject *)NULL, \ + PYTHON_API_VERSION) + +#define Py_InitModule3(name, methods, doc) \ + Py_InitModule4(name, methods, doc, (PyObject *)NULL, \ + PYTHON_API_VERSION) + +PyAPI_DATA(char *) _Py_PackageContext; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_MODSUPPORT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/moduleobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/moduleobject.h new file mode 100644 index 00000000..2047929f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/moduleobject.h @@ -0,0 +1,24 @@ + +/* Module object interface */ + +#ifndef Py_MODULEOBJECT_H +#define Py_MODULEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(PyTypeObject) PyModule_Type; + +#define PyModule_Check(op) PyObject_TypeCheck(op, &PyModule_Type) +#define PyModule_CheckExact(op) ((op)->ob_type == &PyModule_Type) + +PyAPI_FUNC(PyObject *) PyModule_New(char *); +PyAPI_FUNC(PyObject *) PyModule_GetDict(PyObject *); +PyAPI_FUNC(char *) PyModule_GetName(PyObject *); +PyAPI_FUNC(char *) PyModule_GetFilename(PyObject *); +PyAPI_FUNC(void) _PyModule_Clear(PyObject *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_MODULEOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/node.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/node.h new file mode 100644 index 00000000..ce9a97dd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/node.h @@ -0,0 +1,37 @@ + +/* Parse tree node interface */ + +#ifndef Py_NODE_H +#define Py_NODE_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _node { + short n_type; + char *n_str; + int n_lineno; + int n_nchildren; + struct _node *n_child; +} node; + +PyAPI_FUNC(node *) PyNode_New(int type); +PyAPI_FUNC(int) PyNode_AddChild(node *n, int type, + char *str, int lineno); +PyAPI_FUNC(void) PyNode_Free(node *n); + +/* Node access functions */ +#define NCH(n) ((n)->n_nchildren) +#define CHILD(n, i) (&(n)->n_child[i]) +#define TYPE(n) ((n)->n_type) +#define STR(n) ((n)->n_str) + +/* Assert that the type of a node is what we expect */ +#define REQ(n, type) assert(TYPE(n) == (type)) + +PyAPI_FUNC(void) PyNode_ListTree(node *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_NODE_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/object.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/object.h new file mode 100644 index 00000000..e0de808c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/object.h @@ -0,0 +1,783 @@ +#ifndef Py_OBJECT_H +#define Py_OBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Object and type object interface */ + +/* +Objects are structures allocated on the heap. Special rules apply to +the use of objects to ensure they are properly garbage-collected. +Objects are never allocated statically or on the stack; they must be +accessed through special macros and functions only. (Type objects are +exceptions to the first rule; the standard types are represented by +statically initialized type objects, although work on type/class unification +for Python 2.2 made it possible to have heap-allocated type objects too). + +An object has a 'reference count' that is increased or decreased when a +pointer to the object is copied or deleted; when the reference count +reaches zero there are no references to the object left and it can be +removed from the heap. + +An object has a 'type' that determines what it represents and what kind +of data it contains. An object's type is fixed when it is created. +Types themselves are represented as objects; an object contains a +pointer to the corresponding type object. The type itself has a type +pointer pointing to the object representing the type 'type', which +contains a pointer to itself!). + +Objects do not float around in memory; once allocated an object keeps +the same size and address. Objects that must hold variable-size data +can contain pointers to variable-size parts of the object. Not all +objects of the same type have the same size; but the size cannot change +after allocation. (These restrictions are made so a reference to an +object can be simply a pointer -- moving an object would require +updating all the pointers, and changing an object's size would require +moving it if there was another object right next to it.) + +Objects are always accessed through pointers of the type 'PyObject *'. +The type 'PyObject' is a structure that only contains the reference count +and the type pointer. The actual memory allocated for an object +contains other data that can only be accessed after casting the pointer +to a pointer to a longer structure type. This longer type must start +with the reference count and type fields; the macro PyObject_HEAD should be +used for this (to accommodate for future changes). The implementation +of a particular object type can cast the object pointer to the proper +type and back. + +A standard interface exists for objects that contain an array of items +whose size is determined when the object is allocated. +*/ + +/* Py_DEBUG implies Py_TRACE_REFS. */ +#if defined(Py_DEBUG) && !defined(Py_TRACE_REFS) +#define Py_TRACE_REFS +#endif + +/* Py_TRACE_REFS implies Py_REF_DEBUG. */ +#if defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) +#define Py_REF_DEBUG +#endif + +#ifdef Py_TRACE_REFS +/* Define pointers to support a doubly-linked list of all live heap objects. */ +#define _PyObject_HEAD_EXTRA \ + struct _object *_ob_next; \ + struct _object *_ob_prev; + +#define _PyObject_EXTRA_INIT 0, 0, + +#else +#define _PyObject_HEAD_EXTRA +#define _PyObject_EXTRA_INIT +#endif + +/* PyObject_HEAD defines the initial segment of every PyObject. */ +#define PyObject_HEAD \ + _PyObject_HEAD_EXTRA \ + int ob_refcnt; \ + struct _typeobject *ob_type; + +#define PyObject_HEAD_INIT(type) \ + _PyObject_EXTRA_INIT \ + 1, type, + +/* PyObject_VAR_HEAD defines the initial segment of all variable-size + * container objects. These end with a declaration of an array with 1 + * element, but enough space is malloc'ed so that the array actually + * has room for ob_size elements. Note that ob_size is an element count, + * not necessarily a byte count. + */ +#define PyObject_VAR_HEAD \ + PyObject_HEAD \ + int ob_size; /* Number of items in variable part */ + +/* Nothing is actually declared to be a PyObject, but every pointer to + * a Python object can be cast to a PyObject*. This is inheritance built + * by hand. Similarly every pointer to a variable-size Python object can, + * in addition, be cast to PyVarObject*. + */ +typedef struct _object { + PyObject_HEAD +} PyObject; + +typedef struct { + PyObject_VAR_HEAD +} PyVarObject; + + +/* +Type objects contain a string containing the type name (to help somewhat +in debugging), the allocation parameters (see PyObject_New() and +PyObject_NewVar()), +and methods for accessing objects of the type. Methods are optional, a +nil pointer meaning that particular kind of access is not available for +this type. The Py_DECREF() macro uses the tp_dealloc method without +checking for a nil pointer; it should always be implemented except if +the implementation can guarantee that the reference count will never +reach zero (e.g., for statically allocated type objects). + +NB: the methods for certain type groups are now contained in separate +method blocks. +*/ + +typedef PyObject * (*unaryfunc)(PyObject *); +typedef PyObject * (*binaryfunc)(PyObject *, PyObject *); +typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *); +typedef int (*inquiry)(PyObject *); +typedef int (*coercion)(PyObject **, PyObject **); +typedef PyObject *(*intargfunc)(PyObject *, int); +typedef PyObject *(*intintargfunc)(PyObject *, int, int); +typedef int(*intobjargproc)(PyObject *, int, PyObject *); +typedef int(*intintobjargproc)(PyObject *, int, int, PyObject *); +typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *); +typedef int (*getreadbufferproc)(PyObject *, int, void **); +typedef int (*getwritebufferproc)(PyObject *, int, void **); +typedef int (*getsegcountproc)(PyObject *, int *); +typedef int (*getcharbufferproc)(PyObject *, int, const char **); +typedef int (*objobjproc)(PyObject *, PyObject *); +typedef int (*visitproc)(PyObject *, void *); +typedef int (*traverseproc)(PyObject *, visitproc, void *); + +typedef struct { + /* For numbers without flag bit Py_TPFLAGS_CHECKTYPES set, all + arguments are guaranteed to be of the object's type (modulo + coercion hacks -- i.e. if the type's coercion function + returns other types, then these are allowed as well). Numbers that + have the Py_TPFLAGS_CHECKTYPES flag bit set should check *both* + arguments for proper type and implement the necessary conversions + in the slot functions themselves. */ + + binaryfunc nb_add; + binaryfunc nb_subtract; + binaryfunc nb_multiply; + binaryfunc nb_divide; + binaryfunc nb_remainder; + binaryfunc nb_divmod; + ternaryfunc nb_power; + unaryfunc nb_negative; + unaryfunc nb_positive; + unaryfunc nb_absolute; + inquiry nb_nonzero; + unaryfunc nb_invert; + binaryfunc nb_lshift; + binaryfunc nb_rshift; + binaryfunc nb_and; + binaryfunc nb_xor; + binaryfunc nb_or; + coercion nb_coerce; + unaryfunc nb_int; + unaryfunc nb_long; + unaryfunc nb_float; + unaryfunc nb_oct; + unaryfunc nb_hex; + /* Added in release 2.0 */ + binaryfunc nb_inplace_add; + binaryfunc nb_inplace_subtract; + binaryfunc nb_inplace_multiply; + binaryfunc nb_inplace_divide; + binaryfunc nb_inplace_remainder; + ternaryfunc nb_inplace_power; + binaryfunc nb_inplace_lshift; + binaryfunc nb_inplace_rshift; + binaryfunc nb_inplace_and; + binaryfunc nb_inplace_xor; + binaryfunc nb_inplace_or; + + /* Added in release 2.2 */ + /* The following require the Py_TPFLAGS_HAVE_CLASS flag */ + binaryfunc nb_floor_divide; + binaryfunc nb_true_divide; + binaryfunc nb_inplace_floor_divide; + binaryfunc nb_inplace_true_divide; +} PyNumberMethods; + +typedef struct { + inquiry sq_length; + binaryfunc sq_concat; + intargfunc sq_repeat; + intargfunc sq_item; + intintargfunc sq_slice; + intobjargproc sq_ass_item; + intintobjargproc sq_ass_slice; + objobjproc sq_contains; + /* Added in release 2.0 */ + binaryfunc sq_inplace_concat; + intargfunc sq_inplace_repeat; +} PySequenceMethods; + +typedef struct { + inquiry mp_length; + binaryfunc mp_subscript; + objobjargproc mp_ass_subscript; +} PyMappingMethods; + +typedef struct { + getreadbufferproc bf_getreadbuffer; + getwritebufferproc bf_getwritebuffer; + getsegcountproc bf_getsegcount; + getcharbufferproc bf_getcharbuffer; +} PyBufferProcs; + + +typedef void (*freefunc)(void *); +typedef void (*destructor)(PyObject *); +typedef int (*printfunc)(PyObject *, FILE *, int); +typedef PyObject *(*getattrfunc)(PyObject *, char *); +typedef PyObject *(*getattrofunc)(PyObject *, PyObject *); +typedef int (*setattrfunc)(PyObject *, char *, PyObject *); +typedef int (*setattrofunc)(PyObject *, PyObject *, PyObject *); +typedef int (*cmpfunc)(PyObject *, PyObject *); +typedef PyObject *(*reprfunc)(PyObject *); +typedef long (*hashfunc)(PyObject *); +typedef PyObject *(*richcmpfunc) (PyObject *, PyObject *, int); +typedef PyObject *(*getiterfunc) (PyObject *); +typedef PyObject *(*iternextfunc) (PyObject *); +typedef PyObject *(*descrgetfunc) (PyObject *, PyObject *, PyObject *); +typedef int (*descrsetfunc) (PyObject *, PyObject *, PyObject *); +typedef int (*initproc)(PyObject *, PyObject *, PyObject *); +typedef PyObject *(*newfunc)(struct _typeobject *, PyObject *, PyObject *); +typedef PyObject *(*allocfunc)(struct _typeobject *, int); + +typedef struct _typeobject { + PyObject_VAR_HEAD + char *tp_name; /* For printing, in format "." */ + int tp_basicsize, tp_itemsize; /* For allocation */ + + /* Methods to implement standard operations */ + + destructor tp_dealloc; + printfunc tp_print; + getattrfunc tp_getattr; + setattrfunc tp_setattr; + cmpfunc tp_compare; + reprfunc tp_repr; + + /* Method suites for standard classes */ + + PyNumberMethods *tp_as_number; + PySequenceMethods *tp_as_sequence; + PyMappingMethods *tp_as_mapping; + + /* More standard operations (here for binary compatibility) */ + + hashfunc tp_hash; + ternaryfunc tp_call; + reprfunc tp_str; + getattrofunc tp_getattro; + setattrofunc tp_setattro; + + /* Functions to access object as input/output buffer */ + PyBufferProcs *tp_as_buffer; + + /* Flags to define presence of optional/expanded features */ + long tp_flags; + + char *tp_doc; /* Documentation string */ + + /* Assigned meaning in release 2.0 */ + /* call function for all accessible objects */ + traverseproc tp_traverse; + + /* delete references to contained objects */ + inquiry tp_clear; + + /* Assigned meaning in release 2.1 */ + /* rich comparisons */ + richcmpfunc tp_richcompare; + + /* weak reference enabler */ + long tp_weaklistoffset; + + /* Added in release 2.2 */ + /* Iterators */ + getiterfunc tp_iter; + iternextfunc tp_iternext; + + /* Attribute descriptor and subclassing stuff */ + struct PyMethodDef *tp_methods; + struct PyMemberDef *tp_members; + struct PyGetSetDef *tp_getset; + struct _typeobject *tp_base; + PyObject *tp_dict; + descrgetfunc tp_descr_get; + descrsetfunc tp_descr_set; + long tp_dictoffset; + initproc tp_init; + allocfunc tp_alloc; + newfunc tp_new; + freefunc tp_free; /* Low-level free-memory routine */ + inquiry tp_is_gc; /* For PyObject_IS_GC */ + PyObject *tp_bases; + PyObject *tp_mro; /* method resolution order */ + PyObject *tp_cache; + PyObject *tp_subclasses; + PyObject *tp_weaklist; + destructor tp_del; + +#ifdef COUNT_ALLOCS + /* these must be last and never explicitly initialized */ + int tp_allocs; + int tp_frees; + int tp_maxalloc; + struct _typeobject *tp_next; +#endif +} PyTypeObject; + + +/* The *real* layout of a type object when allocated on the heap */ +typedef struct _heaptypeobject { + /* Note: there's a dependency on the order of these members + in slotptr() in typeobject.c . */ + PyTypeObject type; + PyNumberMethods as_number; + PyMappingMethods as_mapping; + PySequenceMethods as_sequence; /* as_sequence comes after as_mapping, + so that the mapping wins when both + the mapping and the sequence define + a given operator (e.g. __getitem__). + see add_operators() in typeobject.c . */ + PyBufferProcs as_buffer; + PyObject *name, *slots; + /* here are optional user slots, followed by the members. */ +} PyHeapTypeObject; + +/* access macro to the members which are floating "behind" the object */ +#define PyHeapType_GET_MEMBERS(etype) \ + ((PyMemberDef *)(((char *)etype) + (etype)->type.ob_type->tp_basicsize)) + + +/* Generic type check */ +PyAPI_FUNC(int) PyType_IsSubtype(PyTypeObject *, PyTypeObject *); +#define PyObject_TypeCheck(ob, tp) \ + ((ob)->ob_type == (tp) || PyType_IsSubtype((ob)->ob_type, (tp))) + +PyAPI_DATA(PyTypeObject) PyType_Type; /* built-in 'type' */ +PyAPI_DATA(PyTypeObject) PyBaseObject_Type; /* built-in 'object' */ +PyAPI_DATA(PyTypeObject) PySuper_Type; /* built-in 'super' */ + +#define PyType_Check(op) PyObject_TypeCheck(op, &PyType_Type) +#define PyType_CheckExact(op) ((op)->ob_type == &PyType_Type) + +PyAPI_FUNC(int) PyType_Ready(PyTypeObject *); +PyAPI_FUNC(PyObject *) PyType_GenericAlloc(PyTypeObject *, int); +PyAPI_FUNC(PyObject *) PyType_GenericNew(PyTypeObject *, + PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *); + +/* Generic operations on objects */ +PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int); +PyAPI_FUNC(void) _PyObject_Dump(PyObject *); +PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *); +PyAPI_FUNC(PyObject *) PyObject_Str(PyObject *); +#ifdef Py_USING_UNICODE +PyAPI_FUNC(PyObject *) PyObject_Unicode(PyObject *); +#endif +PyAPI_FUNC(int) PyObject_Compare(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyObject_RichCompare(PyObject *, PyObject *, int); +PyAPI_FUNC(int) PyObject_RichCompareBool(PyObject *, PyObject *, int); +PyAPI_FUNC(PyObject *) PyObject_GetAttrString(PyObject *, char *); +PyAPI_FUNC(int) PyObject_SetAttrString(PyObject *, char *, PyObject *); +PyAPI_FUNC(int) PyObject_HasAttrString(PyObject *, char *); +PyAPI_FUNC(PyObject *) PyObject_GetAttr(PyObject *, PyObject *); +PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *); +PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *); +PyAPI_FUNC(PyObject *) PyObject_SelfIter(PyObject *); +PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *); +PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *, + PyObject *, PyObject *); +PyAPI_FUNC(long) PyObject_Hash(PyObject *); +PyAPI_FUNC(int) PyObject_IsTrue(PyObject *); +PyAPI_FUNC(int) PyObject_Not(PyObject *); +PyAPI_FUNC(int) PyCallable_Check(PyObject *); +PyAPI_FUNC(int) PyNumber_Coerce(PyObject **, PyObject **); +PyAPI_FUNC(int) PyNumber_CoerceEx(PyObject **, PyObject **); + +PyAPI_FUNC(void) PyObject_ClearWeakRefs(PyObject *); + +/* A slot function whose address we need to compare */ +extern int _PyObject_SlotCompare(PyObject *, PyObject *); + + +/* PyObject_Dir(obj) acts like Python __builtin__.dir(obj), returning a + list of strings. PyObject_Dir(NULL) is like __builtin__.dir(), + returning the names of the current locals. In this case, if there are + no current locals, NULL is returned, and PyErr_Occurred() is false. +*/ +PyAPI_FUNC(PyObject *) PyObject_Dir(PyObject *); + + +/* Helpers for printing recursive container types */ +PyAPI_FUNC(int) Py_ReprEnter(PyObject *); +PyAPI_FUNC(void) Py_ReprLeave(PyObject *); + +/* Helpers for hash functions */ +PyAPI_FUNC(long) _Py_HashDouble(double); +PyAPI_FUNC(long) _Py_HashPointer(void*); + +/* Helper for passing objects to printf and the like */ +#define PyObject_REPR(obj) PyString_AS_STRING(PyObject_Repr(obj)) + +/* Flag bits for printing: */ +#define Py_PRINT_RAW 1 /* No string quotes etc. */ + +/* +`Type flags (tp_flags) + +These flags are used to extend the type structure in a backwards-compatible +fashion. Extensions can use the flags to indicate (and test) when a given +type structure contains a new feature. The Python core will use these when +introducing new functionality between major revisions (to avoid mid-version +changes in the PYTHON_API_VERSION). + +Arbitration of the flag bit positions will need to be coordinated among +all extension writers who publically release their extensions (this will +be fewer than you might expect!).. + +Python 1.5.2 introduced the bf_getcharbuffer slot into PyBufferProcs. + +Type definitions should use Py_TPFLAGS_DEFAULT for their tp_flags value. + +Code can use PyType_HasFeature(type_ob, flag_value) to test whether the +given type object has a specified feature. +*/ + +/* PyBufferProcs contains bf_getcharbuffer */ +#define Py_TPFLAGS_HAVE_GETCHARBUFFER (1L<<0) + +/* PySequenceMethods contains sq_contains */ +#define Py_TPFLAGS_HAVE_SEQUENCE_IN (1L<<1) + +/* This is here for backwards compatibility. Extensions that use the old GC + * API will still compile but the objects will not be tracked by the GC. */ +#define Py_TPFLAGS_GC 0 /* used to be (1L<<2) */ + +/* PySequenceMethods and PyNumberMethods contain in-place operators */ +#define Py_TPFLAGS_HAVE_INPLACEOPS (1L<<3) + +/* PyNumberMethods do their own coercion */ +#define Py_TPFLAGS_CHECKTYPES (1L<<4) + +/* tp_richcompare is defined */ +#define Py_TPFLAGS_HAVE_RICHCOMPARE (1L<<5) + +/* Objects which are weakly referencable if their tp_weaklistoffset is >0 */ +#define Py_TPFLAGS_HAVE_WEAKREFS (1L<<6) + +/* tp_iter is defined */ +#define Py_TPFLAGS_HAVE_ITER (1L<<7) + +/* New members introduced by Python 2.2 exist */ +#define Py_TPFLAGS_HAVE_CLASS (1L<<8) + +/* Set if the type object is dynamically allocated */ +#define Py_TPFLAGS_HEAPTYPE (1L<<9) + +/* Set if the type allows subclassing */ +#define Py_TPFLAGS_BASETYPE (1L<<10) + +/* Set if the type is 'ready' -- fully initialized */ +#define Py_TPFLAGS_READY (1L<<12) + +/* Set while the type is being 'readied', to prevent recursive ready calls */ +#define Py_TPFLAGS_READYING (1L<<13) + +/* Objects support garbage collection (see objimp.h) */ +#define Py_TPFLAGS_HAVE_GC (1L<<14) + +/* These two bits are preserved for Stackless Python, next after this is 16 */ +#ifdef STACKLESS +#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION (3L<<15) +#else +#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION 0 +#endif + +#define Py_TPFLAGS_DEFAULT ( \ + Py_TPFLAGS_HAVE_GETCHARBUFFER | \ + Py_TPFLAGS_HAVE_SEQUENCE_IN | \ + Py_TPFLAGS_HAVE_INPLACEOPS | \ + Py_TPFLAGS_HAVE_RICHCOMPARE | \ + Py_TPFLAGS_HAVE_WEAKREFS | \ + Py_TPFLAGS_HAVE_ITER | \ + Py_TPFLAGS_HAVE_CLASS | \ + Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | \ + 0) + +#define PyType_HasFeature(t,f) (((t)->tp_flags & (f)) != 0) + + +/* +The macros Py_INCREF(op) and Py_DECREF(op) are used to increment or decrement +reference counts. Py_DECREF calls the object's deallocator function when +the refcount falls to 0; for +objects that don't contain references to other objects or heap memory +this can be the standard function free(). Both macros can be used +wherever a void expression is allowed. The argument must not be a +NIL pointer. If it may be NIL, use Py_XINCREF/Py_XDECREF instead. +The macro _Py_NewReference(op) initialize reference counts to 1, and +in special builds (Py_REF_DEBUG, Py_TRACE_REFS) performs additional +bookkeeping appropriate to the special build. + +We assume that the reference count field can never overflow; this can +be proven when the size of the field is the same as the pointer size, so +we ignore the possibility. Provided a C int is at least 32 bits (which +is implicitly assumed in many parts of this code), that's enough for +about 2**31 references to an object. + +XXX The following became out of date in Python 2.2, but I'm not sure +XXX what the full truth is now. Certainly, heap-allocated type objects +XXX can and should be deallocated. +Type objects should never be deallocated; the type pointer in an object +is not considered to be a reference to the type object, to save +complications in the deallocation function. (This is actually a +decision that's up to the implementer of each new type so if you want, +you can count such references to the type object.) + +*** WARNING*** The Py_DECREF macro must have a side-effect-free argument +since it may evaluate its argument multiple times. (The alternative +would be to mace it a proper function or assign it to a global temporary +variable first, both of which are slower; and in a multi-threaded +environment the global variable trick is not safe.) +*/ + +/* First define a pile of simple helper macros, one set per special + * build symbol. These either expand to the obvious things, or to + * nothing at all when the special mode isn't in effect. The main + * macros can later be defined just once then, yet expand to different + * things depending on which special build options are and aren't in effect. + * Trust me : while painful, this is 20x easier to understand than, + * e.g, defining _Py_NewReference five different times in a maze of nested + * #ifdefs (we used to do that -- it was impenetrable). + */ +#ifdef Py_REF_DEBUG +PyAPI_DATA(long) _Py_RefTotal; +PyAPI_FUNC(void) _Py_NegativeRefcount(const char *fname, + int lineno, PyObject *op); +#define _Py_INC_REFTOTAL _Py_RefTotal++ +#define _Py_DEC_REFTOTAL _Py_RefTotal-- +#define _Py_REF_DEBUG_COMMA , +#define _Py_CHECK_REFCNT(OP) \ +{ if ((OP)->ob_refcnt < 0) \ + _Py_NegativeRefcount(__FILE__, __LINE__, \ + (PyObject *)(OP)); \ +} +#else +#define _Py_INC_REFTOTAL +#define _Py_DEC_REFTOTAL +#define _Py_REF_DEBUG_COMMA +#define _Py_CHECK_REFCNT(OP) /* a semicolon */; +#endif /* Py_REF_DEBUG */ + +#ifdef COUNT_ALLOCS +PyAPI_FUNC(void) inc_count(PyTypeObject *); +#define _Py_INC_TPALLOCS(OP) inc_count((OP)->ob_type) +#define _Py_INC_TPFREES(OP) (OP)->ob_type->tp_frees++ +#define _Py_DEC_TPFREES(OP) (OP)->ob_type->tp_frees-- +#define _Py_COUNT_ALLOCS_COMMA , +#else +#define _Py_INC_TPALLOCS(OP) +#define _Py_INC_TPFREES(OP) +#define _Py_DEC_TPFREES(OP) +#define _Py_COUNT_ALLOCS_COMMA +#endif /* COUNT_ALLOCS */ + +#ifdef Py_TRACE_REFS +/* Py_TRACE_REFS is such major surgery that we call external routines. */ +PyAPI_FUNC(void) _Py_NewReference(PyObject *); +PyAPI_FUNC(void) _Py_ForgetReference(PyObject *); +PyAPI_FUNC(void) _Py_Dealloc(PyObject *); +PyAPI_FUNC(void) _Py_PrintReferences(FILE *); +PyAPI_FUNC(void) _Py_PrintReferenceAddresses(FILE *); +PyAPI_FUNC(void) _Py_AddToAllObjects(PyObject *, int force); + +#else +/* Without Py_TRACE_REFS, there's little enough to do that we expand code + * inline. + */ +#define _Py_NewReference(op) ( \ + _Py_INC_TPALLOCS(op) _Py_COUNT_ALLOCS_COMMA \ + _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \ + (op)->ob_refcnt = 1) + +#define _Py_ForgetReference(op) _Py_INC_TPFREES(op) + +#define _Py_Dealloc(op) ( \ + _Py_INC_TPFREES(op) _Py_COUNT_ALLOCS_COMMA \ + (*(op)->ob_type->tp_dealloc)((PyObject *)(op))) +#endif /* !Py_TRACE_REFS */ + +#define Py_INCREF(op) ( \ + _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \ + (op)->ob_refcnt++) + +#define Py_DECREF(op) \ + if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \ + --(op)->ob_refcnt != 0) \ + _Py_CHECK_REFCNT(op) \ + else \ + _Py_Dealloc((PyObject *)(op)) + +/* Macros to use in case the object pointer may be NULL: */ +#define Py_XINCREF(op) if ((op) == NULL) ; else Py_INCREF(op) +#define Py_XDECREF(op) if ((op) == NULL) ; else Py_DECREF(op) + +/* +_Py_NoneStruct is an object of undefined type which can be used in contexts +where NULL (nil) is not suitable (since NULL often means 'error'). + +Don't forget to apply Py_INCREF() when returning this value!!! +*/ +PyAPI_DATA(PyObject) _Py_NoneStruct; /* Don't use this directly */ +#define Py_None (&_Py_NoneStruct) + +/* +Py_NotImplemented is a singleton used to signal that an operation is +not implemented for a given type combination. +*/ +PyAPI_DATA(PyObject) _Py_NotImplementedStruct; /* Don't use this directly */ +#define Py_NotImplemented (&_Py_NotImplementedStruct) + +/* Rich comparison opcodes */ +#define Py_LT 0 +#define Py_LE 1 +#define Py_EQ 2 +#define Py_NE 3 +#define Py_GT 4 +#define Py_GE 5 + +/* +Define staticforward and statichere for source compatibility with old +C extensions. + +The staticforward define was needed to support certain broken C +compilers (notably SCO ODT 3.0, perhaps early AIX as well) botched the +static keyword when it was used with a forward declaration of a static +initialized structure. Standard C allows the forward declaration with +static, and we've decided to stop catering to broken C compilers. +(In fact, we expect that the compilers are all fixed eight years later.) +*/ + +#define staticforward static +#define statichere static + + +/* +More conventions +================ + +Argument Checking +----------------- + +Functions that take objects as arguments normally don't check for nil +arguments, but they do check the type of the argument, and return an +error if the function doesn't apply to the type. + +Failure Modes +------------- + +Functions may fail for a variety of reasons, including running out of +memory. This is communicated to the caller in two ways: an error string +is set (see errors.h), and the function result differs: functions that +normally return a pointer return NULL for failure, functions returning +an integer return -1 (which could be a legal return value too!), and +other functions return 0 for success and -1 for failure. +Callers should always check for errors before using the result. If +an error was set, the caller must either explicitly clear it, or pass +the error on to its caller. + +Reference Counts +---------------- + +It takes a while to get used to the proper usage of reference counts. + +Functions that create an object set the reference count to 1; such new +objects must be stored somewhere or destroyed again with Py_DECREF(). +Some functions that 'store' objects, such as PyTuple_SetItem() and +PyList_SetItem(), +don't increment the reference count of the object, since the most +frequent use is to store a fresh object. Functions that 'retrieve' +objects, such as PyTuple_GetItem() and PyDict_GetItemString(), also +don't increment +the reference count, since most frequently the object is only looked at +quickly. Thus, to retrieve an object and store it again, the caller +must call Py_INCREF() explicitly. + +NOTE: functions that 'consume' a reference count, like +PyList_SetItem(), consume the reference even if the object wasn't +successfully stored, to simplify error handling. + +It seems attractive to make other functions that take an object as +argument consume a reference count; however, this may quickly get +confusing (even the current practice is already confusing). Consider +it carefully, it may save lots of calls to Py_INCREF() and Py_DECREF() at +times. +*/ + + +/* Trashcan mechanism, thanks to Christian Tismer. + +When deallocating a container object, it's possible to trigger an unbounded +chain of deallocations, as each Py_DECREF in turn drops the refcount on "the +next" object in the chain to 0. This can easily lead to stack faults, and +especially in threads (which typically have less stack space to work with). + +A container object that participates in cyclic gc can avoid this by +bracketing the body of its tp_dealloc function with a pair of macros: + +static void +mytype_dealloc(mytype *p) +{ + ... declarations go here ... + + PyObject_GC_UnTrack(p); // must untrack first + Py_TRASHCAN_SAFE_BEGIN(p) + ... The body of the deallocator goes here, including all calls ... + ... to Py_DECREF on contained objects. ... + Py_TRASHCAN_SAFE_END(p) +} + +CAUTION: Never return from the middle of the body! If the body needs to +"get out early", put a label immediately before the Py_TRASHCAN_SAFE_END +call, and goto it. Else the call-depth counter (see below) will stay +above 0 forever, and the trashcan will never get emptied. + +How it works: The BEGIN macro increments a call-depth counter. So long +as this counter is small, the body of the deallocator is run directly without +further ado. But if the counter gets large, it instead adds p to a list of +objects to be deallocated later, skips the body of the deallocator, and +resumes execution after the END macro. The tp_dealloc routine then returns +without deallocating anything (and so unbounded call-stack depth is avoided). + +When the call stack finishes unwinding again, code generated by the END macro +notices this, and calls another routine to deallocate all the objects that +may have been added to the list of deferred deallocations. In effect, a +chain of N deallocations is broken into N / PyTrash_UNWIND_LEVEL pieces, +with the call stack never exceeding a depth of PyTrash_UNWIND_LEVEL. +*/ + +PyAPI_FUNC(void) _PyTrash_deposit_object(PyObject*); +PyAPI_FUNC(void) _PyTrash_destroy_chain(void); +PyAPI_DATA(int) _PyTrash_delete_nesting; +PyAPI_DATA(PyObject *) _PyTrash_delete_later; + +#define PyTrash_UNWIND_LEVEL 50 + +#define Py_TRASHCAN_SAFE_BEGIN(op) \ + if (_PyTrash_delete_nesting < PyTrash_UNWIND_LEVEL) { \ + ++_PyTrash_delete_nesting; + /* The body of the deallocator is here. */ +#define Py_TRASHCAN_SAFE_END(op) \ + --_PyTrash_delete_nesting; \ + if (_PyTrash_delete_later && _PyTrash_delete_nesting <= 0) \ + _PyTrash_destroy_chain(); \ + } \ + else \ + _PyTrash_deposit_object((PyObject*)op); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_OBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/objimpl.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/objimpl.h new file mode 100644 index 00000000..19773097 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/objimpl.h @@ -0,0 +1,326 @@ +/* The PyObject_ memory family: high-level object memory interfaces. + See pymem.h for the low-level PyMem_ family. +*/ + +#ifndef Py_OBJIMPL_H +#define Py_OBJIMPL_H + +#include "pymem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* BEWARE: + + Each interface exports both functions and macros. Extension modules should + use the functions, to ensure binary compatibility across Python versions. + Because the Python implementation is free to change internal details, and + the macros may (or may not) expose details for speed, if you do use the + macros you must recompile your extensions with each Python release. + + Never mix calls to PyObject_ memory functions with calls to the platform + malloc/realloc/ calloc/free, or with calls to PyMem_. +*/ + +/* +Functions and macros for modules that implement new object types. + + - PyObject_New(type, typeobj) allocates memory for a new object of the given + type, and initializes part of it. 'type' must be the C structure type used + to represent the object, and 'typeobj' the address of the corresponding + type object. Reference count and type pointer are filled in; the rest of + the bytes of the object are *undefined*! The resulting expression type is + 'type *'. The size of the object is determined by the tp_basicsize field + of the type object. + + - PyObject_NewVar(type, typeobj, n) is similar but allocates a variable-size + object with room for n items. In addition to the refcount and type pointer + fields, this also fills in the ob_size field. + + - PyObject_Del(op) releases the memory allocated for an object. It does not + run a destructor -- it only frees the memory. PyObject_Free is identical. + + - PyObject_Init(op, typeobj) and PyObject_InitVar(op, typeobj, n) don't + allocate memory. Instead of a 'type' parameter, they take a pointer to a + new object (allocated by an arbitrary allocator), and initialize its object + header fields. + +Note that objects created with PyObject_{New, NewVar} are allocated using the +specialized Python allocator (implemented in obmalloc.c), if WITH_PYMALLOC is +enabled. In addition, a special debugging allocator is used if PYMALLOC_DEBUG +is also #defined. + +In case a specific form of memory management is needed (for example, if you +must use the platform malloc heap(s), or shared memory, or C++ local storage or +operator new), you must first allocate the object with your custom allocator, +then pass its pointer to PyObject_{Init, InitVar} for filling in its Python- +specific fields: reference count, type pointer, possibly others. You should +be aware that Python no control over these objects because they don't +cooperate with the Python memory manager. Such objects may not be eligible +for automatic garbage collection and you have to make sure that they are +released accordingly whenever their destructor gets called (cf. the specific +form of memory management you're using). + +Unless you have specific memory management requirements, use +PyObject_{New, NewVar, Del}. +*/ + +/* + * Raw object memory interface + * =========================== + */ + +/* Functions to call the same malloc/realloc/free as used by Python's + object allocator. If WITH_PYMALLOC is enabled, these may differ from + the platform malloc/realloc/free. The Python object allocator is + designed for fast, cache-conscious allocation of many "small" objects, + and with low hidden memory overhead. + + PyObject_Malloc(0) returns a unique non-NULL pointer if possible. + + PyObject_Realloc(NULL, n) acts like PyObject_Malloc(n). + PyObject_Realloc(p != NULL, 0) does not return NULL, or free the memory + at p. + + Returned pointers must be checked for NULL explicitly; no action is + performed on failure other than to return NULL (no warning it printed, no + exception is set, etc). + + For allocating objects, use PyObject_{New, NewVar} instead whenever + possible. The PyObject_{Malloc, Realloc, Free} family is exposed + so that you can exploit Python's small-block allocator for non-object + uses. If you must use these routines to allocate object memory, make sure + the object gets initialized via PyObject_{Init, InitVar} after obtaining + the raw memory. +*/ +PyAPI_FUNC(void *) PyObject_Malloc(size_t); +PyAPI_FUNC(void *) PyObject_Realloc(void *, size_t); +PyAPI_FUNC(void) PyObject_Free(void *); + + +/* Macros */ +#ifdef WITH_PYMALLOC +#ifdef PYMALLOC_DEBUG +PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes); +PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes); +PyAPI_FUNC(void) _PyObject_DebugFree(void *p); +PyAPI_FUNC(void) _PyObject_DebugDumpAddress(const void *p); +PyAPI_FUNC(void) _PyObject_DebugCheckAddress(const void *p); +PyAPI_FUNC(void) _PyObject_DebugMallocStats(void); +#define PyObject_MALLOC _PyObject_DebugMalloc +#define PyObject_Malloc _PyObject_DebugMalloc +#define PyObject_REALLOC _PyObject_DebugRealloc +#define PyObject_Realloc _PyObject_DebugRealloc +#define PyObject_FREE _PyObject_DebugFree +#define PyObject_Free _PyObject_DebugFree + +#else /* WITH_PYMALLOC && ! PYMALLOC_DEBUG */ +#define PyObject_MALLOC PyObject_Malloc +#define PyObject_REALLOC PyObject_Realloc +#define PyObject_FREE PyObject_Free +#endif + +#else /* ! WITH_PYMALLOC */ +#define PyObject_MALLOC PyMem_MALLOC +#define PyObject_REALLOC PyMem_REALLOC +/* This is an odd one! For backward compatability with old extensions, the + PyMem "release memory" functions have to invoke the object allocator's + free() function. When pymalloc isn't enabled, that leaves us using + the platform free(). */ +#define PyObject_FREE free + +#endif /* WITH_PYMALLOC */ + +#define PyObject_Del PyObject_Free +#define PyObject_DEL PyObject_FREE + +/* for source compatibility with 2.2 */ +#define _PyObject_Del PyObject_Free + +/* + * Generic object allocator interface + * ================================== + */ + +/* Functions */ +PyAPI_FUNC(PyObject *) PyObject_Init(PyObject *, PyTypeObject *); +PyAPI_FUNC(PyVarObject *) PyObject_InitVar(PyVarObject *, + PyTypeObject *, int); +PyAPI_FUNC(PyObject *) _PyObject_New(PyTypeObject *); +PyAPI_FUNC(PyVarObject *) _PyObject_NewVar(PyTypeObject *, int); + +#define PyObject_New(type, typeobj) \ + ( (type *) _PyObject_New(typeobj) ) +#define PyObject_NewVar(type, typeobj, n) \ + ( (type *) _PyObject_NewVar((typeobj), (n)) ) + +/* Macros trading binary compatibility for speed. See also pymem.h. + Note that these macros expect non-NULL object pointers.*/ +#define PyObject_INIT(op, typeobj) \ + ( (op)->ob_type = (typeobj), _Py_NewReference((PyObject *)(op)), (op) ) +#define PyObject_INIT_VAR(op, typeobj, size) \ + ( (op)->ob_size = (size), PyObject_INIT((op), (typeobj)) ) + +#define _PyObject_SIZE(typeobj) ( (typeobj)->tp_basicsize ) + +/* _PyObject_VAR_SIZE returns the number of bytes (as size_t) allocated for a + vrbl-size object with nitems items, exclusive of gc overhead (if any). The + value is rounded up to the closest multiple of sizeof(void *), in order to + ensure that pointer fields at the end of the object are correctly aligned + for the platform (this is of special importance for subclasses of, e.g., + str or long, so that pointers can be stored after the embedded data). + + Note that there's no memory wastage in doing this, as malloc has to + return (at worst) pointer-aligned memory anyway. +*/ +#if ((SIZEOF_VOID_P - 1) & SIZEOF_VOID_P) != 0 +# error "_PyObject_VAR_SIZE requires SIZEOF_VOID_P be a power of 2" +#endif + +#define _PyObject_VAR_SIZE(typeobj, nitems) \ + (size_t) \ + ( ( (typeobj)->tp_basicsize + \ + (nitems)*(typeobj)->tp_itemsize + \ + (SIZEOF_VOID_P - 1) \ + ) & ~(SIZEOF_VOID_P - 1) \ + ) + +#define PyObject_NEW(type, typeobj) \ +( (type *) PyObject_Init( \ + (PyObject *) PyObject_MALLOC( _PyObject_SIZE(typeobj) ), (typeobj)) ) + +#define PyObject_NEW_VAR(type, typeobj, n) \ +( (type *) PyObject_InitVar( \ + (PyVarObject *) PyObject_MALLOC(_PyObject_VAR_SIZE((typeobj),(n)) ),\ + (typeobj), (n)) ) + +/* This example code implements an object constructor with a custom + allocator, where PyObject_New is inlined, and shows the important + distinction between two steps (at least): + 1) the actual allocation of the object storage; + 2) the initialization of the Python specific fields + in this storage with PyObject_{Init, InitVar}. + + PyObject * + YourObject_New(...) + { + PyObject *op; + + op = (PyObject *) Your_Allocator(_PyObject_SIZE(YourTypeStruct)); + if (op == NULL) + return PyErr_NoMemory(); + + PyObject_Init(op, &YourTypeStruct); + + op->ob_field = value; + ... + return op; + } + + Note that in C++, the use of the new operator usually implies that + the 1st step is performed automatically for you, so in a C++ class + constructor you would start directly with PyObject_Init/InitVar +*/ + +/* + * Garbage Collection Support + * ========================== + */ + +/* C equivalent of gc.collect(). */ +long PyGC_Collect(void); + +/* Test if a type has a GC head */ +#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC) + +/* Test if an object has a GC head */ +#define PyObject_IS_GC(o) (PyType_IS_GC((o)->ob_type) && \ + ((o)->ob_type->tp_is_gc == NULL || (o)->ob_type->tp_is_gc(o))) + +PyAPI_FUNC(PyVarObject *) _PyObject_GC_Resize(PyVarObject *, int); +#define PyObject_GC_Resize(type, op, n) \ + ( (type *) _PyObject_GC_Resize((PyVarObject *)(op), (n)) ) + +/* for source compatibility with 2.2 */ +#define _PyObject_GC_Del PyObject_GC_Del + +/* GC information is stored BEFORE the object structure. */ +typedef union _gc_head { + struct { + union _gc_head *gc_next; + union _gc_head *gc_prev; + int gc_refs; + } gc; + long double dummy; /* force worst-case alignment */ +} PyGC_Head; + +extern PyGC_Head *_PyGC_generation0; + +#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1) + +#define _PyGC_REFS_UNTRACKED (-2) +#define _PyGC_REFS_REACHABLE (-3) +#define _PyGC_REFS_TENTATIVELY_UNREACHABLE (-4) + +/* Tell the GC to track this object. NB: While the object is tracked the + * collector it must be safe to call the ob_traverse method. */ +#define _PyObject_GC_TRACK(o) do { \ + PyGC_Head *g = _Py_AS_GC(o); \ + if (g->gc.gc_refs != _PyGC_REFS_UNTRACKED) \ + Py_FatalError("GC object already tracked"); \ + g->gc.gc_refs = _PyGC_REFS_REACHABLE; \ + g->gc.gc_next = _PyGC_generation0; \ + g->gc.gc_prev = _PyGC_generation0->gc.gc_prev; \ + g->gc.gc_prev->gc.gc_next = g; \ + _PyGC_generation0->gc.gc_prev = g; \ + } while (0); + +/* Tell the GC to stop tracking this object. + * gc_next doesn't need to be set to NULL, but doing so is a good + * way to provoke memory errors if calling code is confused. + */ +#define _PyObject_GC_UNTRACK(o) do { \ + PyGC_Head *g = _Py_AS_GC(o); \ + assert(g->gc.gc_refs != _PyGC_REFS_UNTRACKED); \ + g->gc.gc_refs = _PyGC_REFS_UNTRACKED; \ + g->gc.gc_prev->gc.gc_next = g->gc.gc_next; \ + g->gc.gc_next->gc.gc_prev = g->gc.gc_prev; \ + g->gc.gc_next = NULL; \ + } while (0); + +PyAPI_FUNC(PyObject *) _PyObject_GC_Malloc(size_t); +PyAPI_FUNC(PyObject *) _PyObject_GC_New(PyTypeObject *); +PyAPI_FUNC(PyVarObject *) _PyObject_GC_NewVar(PyTypeObject *, int); +PyAPI_FUNC(void) PyObject_GC_Track(void *); +PyAPI_FUNC(void) PyObject_GC_UnTrack(void *); +PyAPI_FUNC(void) PyObject_GC_Del(void *); + +#define PyObject_GC_New(type, typeobj) \ + ( (type *) _PyObject_GC_New(typeobj) ) +#define PyObject_GC_NewVar(type, typeobj, n) \ + ( (type *) _PyObject_GC_NewVar((typeobj), (n)) ) + + +/* This is here for the sake of backwards compatibility. Extensions that + * use the old GC API will still compile but the objects will not be + * tracked by the GC. */ +#define PyGC_HEAD_SIZE 0 +#define PyObject_GC_Init(op) +#define PyObject_GC_Fini(op) +#define PyObject_AS_GC(op) (op) +#define PyObject_FROM_GC(op) (op) + + +/* Test if a type supports weak references */ +#define PyType_SUPPORTS_WEAKREFS(t) \ + (PyType_HasFeature((t), Py_TPFLAGS_HAVE_WEAKREFS) \ + && ((t)->tp_weaklistoffset > 0)) + +#define PyObject_GET_WEAKREFS_LISTPTR(o) \ + ((PyObject **) (((char *) (o)) + (o)->ob_type->tp_weaklistoffset)) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_OBJIMPL_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/opcode.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/opcode.h new file mode 100644 index 00000000..684f5714 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/opcode.h @@ -0,0 +1,151 @@ +#ifndef Py_OPCODE_H +#define Py_OPCODE_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Instruction opcodes for compiled code */ + +#define STOP_CODE 0 +#define POP_TOP 1 +#define ROT_TWO 2 +#define ROT_THREE 3 +#define DUP_TOP 4 +#define ROT_FOUR 5 + +#define UNARY_POSITIVE 10 +#define UNARY_NEGATIVE 11 +#define UNARY_NOT 12 +#define UNARY_CONVERT 13 + +#define UNARY_INVERT 15 + +#define BINARY_POWER 19 + +#define BINARY_MULTIPLY 20 +#define BINARY_DIVIDE 21 +#define BINARY_MODULO 22 +#define BINARY_ADD 23 +#define BINARY_SUBTRACT 24 +#define BINARY_SUBSCR 25 +#define BINARY_FLOOR_DIVIDE 26 +#define BINARY_TRUE_DIVIDE 27 +#define INPLACE_FLOOR_DIVIDE 28 +#define INPLACE_TRUE_DIVIDE 29 + +#define SLICE 30 +/* Also uses 31-33 */ + +#define STORE_SLICE 40 +/* Also uses 41-43 */ + +#define DELETE_SLICE 50 +/* Also uses 51-53 */ + +#define INPLACE_ADD 55 +#define INPLACE_SUBTRACT 56 +#define INPLACE_MULTIPLY 57 +#define INPLACE_DIVIDE 58 +#define INPLACE_MODULO 59 +#define STORE_SUBSCR 60 +#define DELETE_SUBSCR 61 + +#define BINARY_LSHIFT 62 +#define BINARY_RSHIFT 63 +#define BINARY_AND 64 +#define BINARY_XOR 65 +#define BINARY_OR 66 +#define INPLACE_POWER 67 +#define GET_ITER 68 + +#define PRINT_EXPR 70 +#define PRINT_ITEM 71 +#define PRINT_NEWLINE 72 +#define PRINT_ITEM_TO 73 +#define PRINT_NEWLINE_TO 74 +#define INPLACE_LSHIFT 75 +#define INPLACE_RSHIFT 76 +#define INPLACE_AND 77 +#define INPLACE_XOR 78 +#define INPLACE_OR 79 +#define BREAK_LOOP 80 + +#define LOAD_LOCALS 82 +#define RETURN_VALUE 83 +#define IMPORT_STAR 84 +#define EXEC_STMT 85 +#define YIELD_VALUE 86 + +#define POP_BLOCK 87 +#define END_FINALLY 88 +#define BUILD_CLASS 89 + +#define HAVE_ARGUMENT 90 /* Opcodes from here have an argument: */ + +#define STORE_NAME 90 /* Index in name list */ +#define DELETE_NAME 91 /* "" */ +#define UNPACK_SEQUENCE 92 /* Number of sequence items */ +#define FOR_ITER 93 + +#define STORE_ATTR 95 /* Index in name list */ +#define DELETE_ATTR 96 /* "" */ +#define STORE_GLOBAL 97 /* "" */ +#define DELETE_GLOBAL 98 /* "" */ +#define DUP_TOPX 99 /* number of items to duplicate */ +#define LOAD_CONST 100 /* Index in const list */ +#define LOAD_NAME 101 /* Index in name list */ +#define BUILD_TUPLE 102 /* Number of tuple items */ +#define BUILD_LIST 103 /* Number of list items */ +#define BUILD_MAP 104 /* Always zero for now */ +#define LOAD_ATTR 105 /* Index in name list */ +#define COMPARE_OP 106 /* Comparison operator */ +#define IMPORT_NAME 107 /* Index in name list */ +#define IMPORT_FROM 108 /* Index in name list */ + +#define JUMP_FORWARD 110 /* Number of bytes to skip */ +#define JUMP_IF_FALSE 111 /* "" */ +#define JUMP_IF_TRUE 112 /* "" */ +#define JUMP_ABSOLUTE 113 /* Target byte offset from beginning of code */ + +#define LOAD_GLOBAL 116 /* Index in name list */ + +#define CONTINUE_LOOP 119 /* Start of loop (absolute) */ +#define SETUP_LOOP 120 /* Target address (absolute) */ +#define SETUP_EXCEPT 121 /* "" */ +#define SETUP_FINALLY 122 /* "" */ + +#define LOAD_FAST 124 /* Local variable number */ +#define STORE_FAST 125 /* Local variable number */ +#define DELETE_FAST 126 /* Local variable number */ + +#define RAISE_VARARGS 130 /* Number of raise arguments (1, 2 or 3) */ +/* CALL_FUNCTION_XXX opcodes defined below depend on this definition */ +#define CALL_FUNCTION 131 /* #args + (#kwargs<<8) */ +#define MAKE_FUNCTION 132 /* #defaults */ +#define BUILD_SLICE 133 /* Number of items */ + +#define MAKE_CLOSURE 134 /* #free vars */ +#define LOAD_CLOSURE 135 /* Load free variable from closure */ +#define LOAD_DEREF 136 /* Load and dereference from closure cell */ +#define STORE_DEREF 137 /* Store into cell */ + +/* The next 3 opcodes must be contiguous and satisfy + (CALL_FUNCTION_VAR - CALL_FUNCTION) & 3 == 1 */ +#define CALL_FUNCTION_VAR 140 /* #args + (#kwargs<<8) */ +#define CALL_FUNCTION_KW 141 /* #args + (#kwargs<<8) */ +#define CALL_FUNCTION_VAR_KW 142 /* #args + (#kwargs<<8) */ + +/* Support for opargs more than 16 bits long */ +#define EXTENDED_ARG 143 + + +enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE, PyCmp_GT=Py_GT, PyCmp_GE=Py_GE, + PyCmp_IN, PyCmp_NOT_IN, PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD}; + +#define HAS_ARG(op) ((op) >= HAVE_ARGUMENT) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_OPCODE_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/osdefs.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/osdefs.h new file mode 100644 index 00000000..c2411141 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/osdefs.h @@ -0,0 +1,58 @@ +#ifndef Py_OSDEFS_H +#define Py_OSDEFS_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Operating system dependencies */ + +#ifdef macintosh +#define SEP ':' +#define MAXPATHLEN 256 +/* Mod by Jack: newline is less likely to occur in filenames than space */ +#define DELIM '\n' +#endif + +/* Mod by chrish: QNX has WATCOM, but isn't DOS */ +#if !defined(__QNX__) +#if defined(MS_WINDOWS) || defined(__BORLANDC__) || defined(__WATCOMC__) || defined(__DJGPP__) || defined(PYOS_OS2) +#if defined(PYOS_OS2) && defined(PYCC_GCC) +#define MAXPATHLEN 260 +#define SEP '/' +#define ALTSEP '\\' +#else +#define SEP '\\' +#define ALTSEP '/' +#define MAXPATHLEN 256 +#endif +#define DELIM ';' +#endif +#endif + +#ifdef RISCOS +#define SEP '.' +#define MAXPATHLEN 256 +#define DELIM ',' +#endif + + +/* Filename separator */ +#ifndef SEP +#define SEP '/' +#endif + +/* Max pathname length */ +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +/* Search path entry delimiter */ +#ifndef DELIM +#define DELIM ':' +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_OSDEFS_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/parsetok.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/parsetok.h new file mode 100644 index 00000000..9e0ebb8c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/parsetok.h @@ -0,0 +1,48 @@ + +/* Parser-tokenizer link interface */ + +#ifndef Py_PARSETOK_H +#define Py_PARSETOK_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int error; + const char *filename; + int lineno; + int offset; + char *text; + int token; + int expected; +} perrdetail; + +#if 0 +#define PyPARSE_YIELD_IS_KEYWORD 0x0001 +#endif + +#define PyPARSE_DONT_IMPLY_DEDENT 0x0002 + +PyAPI_FUNC(node *) PyParser_ParseString(const char *, grammar *, int, + perrdetail *); +PyAPI_FUNC(node *) PyParser_ParseFile (FILE *, const char *, grammar *, int, + char *, char *, perrdetail *); + +PyAPI_FUNC(node *) PyParser_ParseStringFlags(const char *, grammar *, int, + perrdetail *, int); +PyAPI_FUNC(node *) PyParser_ParseFileFlags(FILE *, const char *, grammar *, + int, char *, char *, + perrdetail *, int); + +PyAPI_FUNC(node *) PyParser_ParseStringFlagsFilename(const char *, + const char *, + grammar *, int, + perrdetail *, int); + +/* Note that he following function is defined in pythonrun.c not parsetok.c. */ +PyAPI_FUNC(void) PyParser_SetError(perrdetail *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PARSETOK_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/patchlevel.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/patchlevel.h new file mode 100644 index 00000000..582674cf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/patchlevel.h @@ -0,0 +1,37 @@ + +/* Newfangled version identification scheme. + + This scheme was added in Python 1.5.2b2; before that time, only PATCHLEVEL + was available. To test for presence of the scheme, test for + defined(PY_MAJOR_VERSION). + + When the major or minor version changes, the VERSION variable in + configure.in must also be changed. + + There is also (independent) API version information in modsupport.h. +*/ + +/* Values for PY_RELEASE_LEVEL */ +#define PY_RELEASE_LEVEL_ALPHA 0xA +#define PY_RELEASE_LEVEL_BETA 0xB +#define PY_RELEASE_LEVEL_GAMMA 0xC +#define PY_RELEASE_LEVEL_FINAL 0xF /* Serial should be 0 here */ + /* Higher for patch releases */ + +/* Version parsed out into numeric values */ +#define PY_MAJOR_VERSION 2 +#define PY_MINOR_VERSION 3 +#define PY_MICRO_VERSION 3 +#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL +#define PY_RELEASE_SERIAL 0 + +/* Version as a string */ +#define PY_VERSION "2.3.3" + +/* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. + Use this for numeric comparisons, e.g. #if PY_VERSION_HEX >= ... */ +#define PY_VERSION_HEX ((PY_MAJOR_VERSION << 24) | \ + (PY_MINOR_VERSION << 16) | \ + (PY_MICRO_VERSION << 8) | \ + (PY_RELEASE_LEVEL << 4) | \ + (PY_RELEASE_SERIAL << 0)) diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pgen.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pgen.h new file mode 100644 index 00000000..af84852b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pgen.h @@ -0,0 +1,18 @@ +#ifndef Py_PGEN_H +#define Py_PGEN_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Parser generator interface */ + +extern grammar *meta_grammar(void); + +struct _node; +extern grammar *pgen(struct _node *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PGEN_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pgenheaders.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pgenheaders.h new file mode 100644 index 00000000..7aac4e2b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pgenheaders.h @@ -0,0 +1,42 @@ +#ifndef Py_PGENHEADERS_H +#define Py_PGENHEADERS_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Include files and extern declarations used by most of the parser. */ + +#include "Python.h" + +PyAPI_FUNC(void) PySys_WriteStdout(const char *format, ...) + Py_GCC_ATTRIBUTE((format(printf, 1, 2))); +PyAPI_FUNC(void) PySys_WriteStderr(const char *format, ...) + Py_GCC_ATTRIBUTE((format(printf, 1, 2))); + +#define addarc _Py_addarc +#define addbit _Py_addbit +#define adddfa _Py_adddfa +#define addfirstsets _Py_addfirstsets +#define addlabel _Py_addlabel +#define addstate _Py_addstate +#define delbitset _Py_delbitset +#define dumptree _Py_dumptree +#define findlabel _Py_findlabel +#define mergebitset _Py_mergebitset +#define meta_grammar _Py_meta_grammar +#define newbitset _Py_newbitset +#define newgrammar _Py_newgrammar +#define pgen _Py_pgen +#define printgrammar _Py_printgrammar +#define printnonterminals _Py_printnonterminals +#define printtree _Py_printtree +#define samebitset _Py_samebitset +#define showtree _Py_showtree +#define tok_dump _Py_tok_dump +#define translatelabels _Py_translatelabels + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PGENHEADERS_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/py_curses.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/py_curses.h new file mode 100644 index 00000000..71163db4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/py_curses.h @@ -0,0 +1,176 @@ + +#ifndef Py_CURSES_H +#define Py_CURSES_H + +#ifdef __APPLE__ +/* +** On Mac OS X 10.2 [n]curses.h and stdlib.h use different guards +** against multiple definition of wchar_t. +*/ +#ifdef _BSD_WCHAR_T_DEFINED_ +#define _WCHAR_T +#endif +#endif + +#ifdef __FreeBSD__ +/* +** On FreeBSD, [n]curses.h and stdlib.h/wchar.h use different guards +** against multiple definition of wchar_t and wint_t. +*/ +#ifdef _XOPEN_SOURCE_EXTENDED +#ifndef __FreeBSD_version +#include +#endif +#if __FreeBSD_version >= 500000 +#ifndef __wchar_t +#define __wchar_t +#endif +#ifndef __wint_t +#define __wint_t +#endif +#else +#ifndef _WCHAR_T +#define _WCHAR_T +#endif +#ifndef _WINT_T +#define _WINT_T +#endif +#endif +#endif +#endif + +#ifdef HAVE_NCURSES_H +#include +#else +#include +#ifdef HAVE_TERM_H +/* for tigetstr, which is not declared in SysV curses */ +#include +#endif +#endif + +#ifdef HAVE_NCURSES_H +/* configure was checking , but we will + use , which has all these features. */ +#ifndef WINDOW_HAS_FLAGS +#define WINDOW_HAS_FLAGS 1 +#endif +#ifndef MVWDELCH_IS_EXPRESSION +#define MVWDELCH_IS_EXPRESSION 1 +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define PyCurses_API_pointers 4 + +/* Type declarations */ + +typedef struct { + PyObject_HEAD + WINDOW *win; +} PyCursesWindowObject; + +#define PyCursesWindow_Check(v) ((v)->ob_type == &PyCursesWindow_Type) + +#ifdef CURSES_MODULE +/* This section is used when compiling _cursesmodule.c */ + +#else +/* This section is used in modules that use the _cursesmodule API */ + +static void **PyCurses_API; + +#define PyCursesWindow_Type (*(PyTypeObject *) PyCurses_API[0]) +#define PyCursesSetupTermCalled {if (! ((int (*)(void))PyCurses_API[1]) () ) return NULL;} +#define PyCursesInitialised {if (! ((int (*)(void))PyCurses_API[2]) () ) return NULL;} +#define PyCursesInitialisedColor {if (! ((int (*)(void))PyCurses_API[3]) () ) return NULL;} + +#define import_curses() \ +{ \ + PyObject *module = PyImport_ImportModule("_curses"); \ + if (module != NULL) { \ + PyObject *module_dict = PyModule_GetDict(module); \ + PyObject *c_api_object = PyDict_GetItemString(module_dict, "_C_API"); \ + if (PyCObject_Check(c_api_object)) { \ + PyCurses_API = (void **)PyCObject_AsVoidPtr(c_api_object); \ + } \ + } \ +} +#endif + +/* general error messages */ +static char *catchall_ERR = "curses function returned ERR"; +static char *catchall_NULL = "curses function returned NULL"; + +/* Function Prototype Macros - They are ugly but very, very useful. ;-) + + X - function name + TYPE - parameter Type + ERGSTR - format string for construction of the return value + PARSESTR - format string for argument parsing + */ + +#define NoArgNoReturnFunction(X) \ +static PyObject *PyCurses_ ## X (PyObject *self) \ +{ \ + PyCursesInitialised \ + return PyCursesCheckERR(X(), # X); } + +#define NoArgOrFlagNoReturnFunction(X) \ +static PyObject *PyCurses_ ## X (PyObject *self, PyObject *args) \ +{ \ + int flag = 0; \ + PyCursesInitialised \ + switch(PyTuple_Size(args)) { \ + case 0: \ + return PyCursesCheckERR(X(), # X); \ + case 1: \ + if (!PyArg_ParseTuple(args, "i;True(1) or False(0)", &flag)) return NULL; \ + if (flag) return PyCursesCheckERR(X(), # X); \ + else return PyCursesCheckERR(no ## X (), # X); \ + default: \ + PyErr_SetString(PyExc_TypeError, # X " requires 0 or 1 arguments"); \ + return NULL; } } + +#define NoArgReturnIntFunction(X) \ +static PyObject *PyCurses_ ## X (PyObject *self) \ +{ \ + PyCursesInitialised \ + return PyInt_FromLong((long) X()); } + + +#define NoArgReturnStringFunction(X) \ +static PyObject *PyCurses_ ## X (PyObject *self) \ +{ \ + PyCursesInitialised \ + return PyString_FromString(X()); } + +#define NoArgTrueFalseFunction(X) \ +static PyObject *PyCurses_ ## X (PyObject *self) \ +{ \ + PyCursesInitialised \ + if (X () == FALSE) { \ + Py_INCREF(Py_False); \ + return Py_False; \ + } \ + Py_INCREF(Py_True); \ + return Py_True; } + +#define NoArgNoReturnVoidFunction(X) \ +static PyObject *PyCurses_ ## X (PyObject *self) \ +{ \ + PyCursesInitialised \ + X(); \ + Py_INCREF(Py_None); \ + return Py_None; } + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(Py_CURSES_H) */ + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pydebug.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pydebug.h new file mode 100644 index 00000000..9308a8d6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pydebug.h @@ -0,0 +1,39 @@ + +#ifndef Py_PYDEBUG_H +#define Py_PYDEBUG_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(int) Py_DebugFlag; +PyAPI_DATA(int) Py_VerboseFlag; +PyAPI_DATA(int) Py_InteractiveFlag; +PyAPI_DATA(int) Py_OptimizeFlag; +PyAPI_DATA(int) Py_NoSiteFlag; +PyAPI_DATA(int) Py_UseClassExceptionsFlag; +PyAPI_DATA(int) Py_FrozenFlag; +PyAPI_DATA(int) Py_TabcheckFlag; +PyAPI_DATA(int) Py_UnicodeFlag; +PyAPI_DATA(int) Py_IgnoreEnvironmentFlag; +PyAPI_DATA(int) Py_DivisionWarningFlag; +/* _XXX Py_QnewFlag should go away in 3.0. It's true iff -Qnew is passed, + on the command line, and is used in 2.2 by ceval.c to make all "/" divisions + true divisions (which they will be in 3.0). */ +PyAPI_DATA(int) _Py_QnewFlag; + +/* this is a wrapper around getenv() that pays attention to + Py_IgnoreEnvironmentFlag. It should be used for getting variables like + PYTHONPATH and PYTHONHOME from the environment */ +#if _XBOX +// Don't even have getenv in the code for Xbox, it'll cause a link error in release mode +#define Py_GETENV(s) NULL +#else +#define Py_GETENV(s) (Py_IgnoreEnvironmentFlag ? NULL : getenv(s)) +#endif + +PyAPI_FUNC(void) Py_FatalError(const char *message); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYDEBUG_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyerrors.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyerrors.h new file mode 100644 index 00000000..38737127 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyerrors.h @@ -0,0 +1,233 @@ +#ifndef Py_ERRORS_H +#define Py_ERRORS_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Error handling definitions */ + +PyAPI_FUNC(void) PyErr_SetNone(PyObject *); +PyAPI_FUNC(void) PyErr_SetObject(PyObject *, PyObject *); +PyAPI_FUNC(void) PyErr_SetString(PyObject *, const char *); +PyAPI_FUNC(PyObject *) PyErr_Occurred(void); +PyAPI_FUNC(void) PyErr_Clear(void); +PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **); +PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *); + +/* Error testing and normalization */ +PyAPI_FUNC(int) PyErr_GivenExceptionMatches(PyObject *, PyObject *); +PyAPI_FUNC(int) PyErr_ExceptionMatches(PyObject *); +PyAPI_FUNC(void) PyErr_NormalizeException(PyObject**, PyObject**, PyObject**); + + +/* Predefined exceptions */ + +PyAPI_DATA(PyObject *) PyExc_Exception; +PyAPI_DATA(PyObject *) PyExc_StopIteration; +PyAPI_DATA(PyObject *) PyExc_StandardError; +PyAPI_DATA(PyObject *) PyExc_ArithmeticError; +PyAPI_DATA(PyObject *) PyExc_LookupError; + +PyAPI_DATA(PyObject *) PyExc_AssertionError; +PyAPI_DATA(PyObject *) PyExc_AttributeError; +PyAPI_DATA(PyObject *) PyExc_EOFError; +PyAPI_DATA(PyObject *) PyExc_FloatingPointError; +PyAPI_DATA(PyObject *) PyExc_EnvironmentError; +PyAPI_DATA(PyObject *) PyExc_IOError; +PyAPI_DATA(PyObject *) PyExc_OSError; +PyAPI_DATA(PyObject *) PyExc_ImportError; +PyAPI_DATA(PyObject *) PyExc_IndexError; +PyAPI_DATA(PyObject *) PyExc_KeyError; +PyAPI_DATA(PyObject *) PyExc_KeyboardInterrupt; +PyAPI_DATA(PyObject *) PyExc_MemoryError; +PyAPI_DATA(PyObject *) PyExc_NameError; +PyAPI_DATA(PyObject *) PyExc_OverflowError; +PyAPI_DATA(PyObject *) PyExc_RuntimeError; +PyAPI_DATA(PyObject *) PyExc_NotImplementedError; +PyAPI_DATA(PyObject *) PyExc_SyntaxError; +PyAPI_DATA(PyObject *) PyExc_IndentationError; +PyAPI_DATA(PyObject *) PyExc_TabError; +PyAPI_DATA(PyObject *) PyExc_ReferenceError; +PyAPI_DATA(PyObject *) PyExc_SystemError; +PyAPI_DATA(PyObject *) PyExc_SystemExit; +PyAPI_DATA(PyObject *) PyExc_TypeError; +PyAPI_DATA(PyObject *) PyExc_UnboundLocalError; +PyAPI_DATA(PyObject *) PyExc_UnicodeError; +PyAPI_DATA(PyObject *) PyExc_UnicodeEncodeError; +PyAPI_DATA(PyObject *) PyExc_UnicodeDecodeError; +PyAPI_DATA(PyObject *) PyExc_UnicodeTranslateError; +PyAPI_DATA(PyObject *) PyExc_ValueError; +PyAPI_DATA(PyObject *) PyExc_ZeroDivisionError; +#ifdef MS_WINDOWS +PyAPI_DATA(PyObject *) PyExc_WindowsError; +#endif +#ifdef __VMS +PyAPI_DATA(PyObject *) PyExc_VMSError; +#endif + +PyAPI_DATA(PyObject *) PyExc_MemoryErrorInst; + +/* Predefined warning categories */ +PyAPI_DATA(PyObject *) PyExc_Warning; +PyAPI_DATA(PyObject *) PyExc_UserWarning; +PyAPI_DATA(PyObject *) PyExc_DeprecationWarning; +PyAPI_DATA(PyObject *) PyExc_PendingDeprecationWarning; +PyAPI_DATA(PyObject *) PyExc_SyntaxWarning; +PyAPI_DATA(PyObject *) PyExc_OverflowWarning; +PyAPI_DATA(PyObject *) PyExc_RuntimeWarning; +PyAPI_DATA(PyObject *) PyExc_FutureWarning; + + +/* Convenience functions */ + +PyAPI_FUNC(int) PyErr_BadArgument(void); +PyAPI_FUNC(PyObject *) PyErr_NoMemory(void); +PyAPI_FUNC(PyObject *) PyErr_SetFromErrno(PyObject *); +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenameObject( + PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilename(PyObject *, char *); +#ifdef Py_WIN_WIDE_FILENAMES +PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithUnicodeFilename( + PyObject *, Py_UNICODE *); +#endif /* Py_WIN_WIDE_FILENAMES */ + +PyAPI_FUNC(PyObject *) PyErr_Format(PyObject *, const char *, ...) + Py_GCC_ATTRIBUTE((format(printf, 2, 3))); + +#ifdef MS_WINDOWS +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilenameObject( + int, const char *); +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename( + int, const char *); +#ifdef Py_WIN_WIDE_FILENAMES +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithUnicodeFilename( + int, const Py_UNICODE *); +#endif /* Py_WIN_WIDE_FILENAMES */ +PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErr(int); +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenameObject( + PyObject *,int, PyObject *); +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilename( + PyObject *,int, const char *); +#ifdef Py_WIN_WIDE_FILENAMES +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithUnicodeFilename( + PyObject *,int, const Py_UNICODE *); +#endif /* Py_WIN_WIDE_FILENAMES */ +PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErr(PyObject *, int); +#endif /* MS_WINDOWS */ + +/* Export the old function so that the existing API remains available: */ +PyAPI_FUNC(void) PyErr_BadInternalCall(void); +PyAPI_FUNC(void) _PyErr_BadInternalCall(char *filename, int lineno); +/* Mask the old API with a call to the new API for code compiled under + Python 2.0: */ +#define PyErr_BadInternalCall() _PyErr_BadInternalCall(__FILE__, __LINE__) + +/* Function to create a new exception */ +PyAPI_FUNC(PyObject *) PyErr_NewException(char *name, PyObject *base, + PyObject *dict); +PyAPI_FUNC(void) PyErr_WriteUnraisable(PyObject *); + +/* Issue a warning or exception */ +PyAPI_FUNC(int) PyErr_Warn(PyObject *, char *); +PyAPI_FUNC(int) PyErr_WarnExplicit(PyObject *, const char *, + const char *, int, + const char *, PyObject *); + +/* In sigcheck.c or signalmodule.c */ +PyAPI_FUNC(int) PyErr_CheckSignals(void); +PyAPI_FUNC(void) PyErr_SetInterrupt(void); + +/* Support for adding program text to SyntaxErrors */ +PyAPI_FUNC(void) PyErr_SyntaxLocation(const char *, int); +PyAPI_FUNC(PyObject *) PyErr_ProgramText(const char *, int); + +#ifdef Py_USING_UNICODE +/* The following functions are used to create and modify unicode + exceptions from C */ + +/* create a UnicodeDecodeError object */ +PyAPI_FUNC(PyObject *) PyUnicodeDecodeError_Create( + const char *, const char *, int, int, int, const char *); + +/* create a UnicodeEncodeError object */ +PyAPI_FUNC(PyObject *) PyUnicodeEncodeError_Create( + const char *, const Py_UNICODE *, int, int, int, const char *); + +/* create a UnicodeTranslateError object */ +PyAPI_FUNC(PyObject *) PyUnicodeTranslateError_Create( + const Py_UNICODE *, int, int, int, const char *); + +/* get the encoding attribute */ +PyAPI_FUNC(PyObject *) PyUnicodeEncodeError_GetEncoding(PyObject *); +PyAPI_FUNC(PyObject *) PyUnicodeDecodeError_GetEncoding(PyObject *); + +/* get the object attribute */ +PyAPI_FUNC(PyObject *) PyUnicodeEncodeError_GetObject(PyObject *); +PyAPI_FUNC(PyObject *) PyUnicodeDecodeError_GetObject(PyObject *); +PyAPI_FUNC(PyObject *) PyUnicodeTranslateError_GetObject(PyObject *); + +/* get the value of the start attribute (the int * may not be NULL) + return 0 on success, -1 on failure */ +PyAPI_FUNC(int) PyUnicodeEncodeError_GetStart(PyObject *, int *); +PyAPI_FUNC(int) PyUnicodeDecodeError_GetStart(PyObject *, int *); +PyAPI_FUNC(int) PyUnicodeTranslateError_GetStart(PyObject *, int *); + +/* assign a new value to the start attribute + return 0 on success, -1 on failure */ +PyAPI_FUNC(int) PyUnicodeEncodeError_SetStart(PyObject *, int); +PyAPI_FUNC(int) PyUnicodeDecodeError_SetStart(PyObject *, int); +PyAPI_FUNC(int) PyUnicodeTranslateError_SetStart(PyObject *, int); + +/* get the value of the end attribute (the int *may not be NULL) + return 0 on success, -1 on failure */ +PyAPI_FUNC(int) PyUnicodeEncodeError_GetEnd(PyObject *, int *); +PyAPI_FUNC(int) PyUnicodeDecodeError_GetEnd(PyObject *, int *); +PyAPI_FUNC(int) PyUnicodeTranslateError_GetEnd(PyObject *, int *); + +/* assign a new value to the end attribute + return 0 on success, -1 on failure */ +PyAPI_FUNC(int) PyUnicodeEncodeError_SetEnd(PyObject *, int); +PyAPI_FUNC(int) PyUnicodeDecodeError_SetEnd(PyObject *, int); +PyAPI_FUNC(int) PyUnicodeTranslateError_SetEnd(PyObject *, int); + +/* get the value of the reason attribute */ +PyAPI_FUNC(PyObject *) PyUnicodeEncodeError_GetReason(PyObject *); +PyAPI_FUNC(PyObject *) PyUnicodeDecodeError_GetReason(PyObject *); +PyAPI_FUNC(PyObject *) PyUnicodeTranslateError_GetReason(PyObject *); + +/* assign a new value to the reason attribute + return 0 on success, -1 on failure */ +PyAPI_FUNC(int) PyUnicodeEncodeError_SetReason( + PyObject *, const char *); +PyAPI_FUNC(int) PyUnicodeDecodeError_SetReason( + PyObject *, const char *); +PyAPI_FUNC(int) PyUnicodeTranslateError_SetReason( + PyObject *, const char *); +#endif + + +/* These APIs aren't really part of the error implementation, but + often needed to format error messages; the native C lib APIs are + not available on all platforms, which is why we provide emulations + for those platforms in Python/mysnprintf.c, + WARNING: The return value of snprintf varies across platforms; do + not rely on any particular behavior; eventually the C99 defn may + be reliable. +*/ +#if defined(MS_WIN32) && !defined(HAVE_SNPRINTF) +# define HAVE_SNPRINTF +# define snprintf _snprintf +# define vsnprintf _vsnprintf +#endif + +#include +PyAPI_FUNC(int) PyOS_snprintf(char *str, size_t size, const char *format, ...) + Py_GCC_ATTRIBUTE((format(printf, 3, 4))); +PyAPI_FUNC(int) PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) + Py_GCC_ATTRIBUTE((format(printf, 3, 0))); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_ERRORS_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyfpe.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyfpe.h new file mode 100644 index 00000000..0793d9eb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyfpe.h @@ -0,0 +1,176 @@ +#ifndef Py_PYFPE_H +#define Py_PYFPE_H +#ifdef __cplusplus +extern "C" { +#endif +/* + --------------------------------------------------------------------- + / Copyright (c) 1996. \ + | The Regents of the University of California. | + | All rights reserved. | + | | + | Permission to use, copy, modify, and distribute this software for | + | any purpose without fee is hereby granted, provided that this en- | + | tire notice is included in all copies of any software which is or | + | includes a copy or modification of this software and in all | + | copies of the supporting documentation for such software. | + | | + | This work was produced at the University of California, Lawrence | + | Livermore National Laboratory under contract no. W-7405-ENG-48 | + | between the U.S. Department of Energy and The Regents of the | + | University of California for the operation of UC LLNL. | + | | + | DISCLAIMER | + | | + | This software was prepared as an account of work sponsored by an | + | agency of the United States Government. Neither the United States | + | Government nor the University of California nor any of their em- | + | ployees, makes any warranty, express or implied, or assumes any | + | liability or responsibility for the accuracy, completeness, or | + | usefulness of any information, apparatus, product, or process | + | disclosed, or represents that its use would not infringe | + | privately-owned rights. Reference herein to any specific commer- | + | cial products, process, or service by trade name, trademark, | + | manufacturer, or otherwise, does not necessarily constitute or | + | imply its endorsement, recommendation, or favoring by the United | + | States Government or the University of California. The views and | + | opinions of authors expressed herein do not necessarily state or | + | reflect those of the United States Government or the University | + | of California, and shall not be used for advertising or product | + \ endorsement purposes. / + --------------------------------------------------------------------- +*/ + +/* + * Define macros for handling SIGFPE. + * Lee Busby, LLNL, November, 1996 + * busby1@llnl.gov + * + ********************************************* + * Overview of the system for handling SIGFPE: + * + * This file (Include/pyfpe.h) defines a couple of "wrapper" macros for + * insertion into your Python C code of choice. Their proper use is + * discussed below. The file Python/pyfpe.c defines a pair of global + * variables PyFPE_jbuf and PyFPE_counter which are used by the signal + * handler for SIGFPE to decide if a particular exception was protected + * by the macros. The signal handler itself, and code for enabling the + * generation of SIGFPE in the first place, is in a (new) Python module + * named fpectl. This module is standard in every respect. It can be loaded + * either statically or dynamically as you choose, and like any other + * Python module, has no effect until you import it. + * + * In the general case, there are three steps toward handling SIGFPE in any + * Python code: + * + * 1) Add the *_PROTECT macros to your C code as required to protect + * dangerous floating point sections. + * + * 2) Turn on the inclusion of the code by adding the ``--with-fpectl'' + * flag at the time you run configure. If the fpectl or other modules + * which use the *_PROTECT macros are to be dynamically loaded, be + * sure they are compiled with WANT_SIGFPE_HANDLER defined. + * + * 3) When python is built and running, import fpectl, and execute + * fpectl.turnon_sigfpe(). This sets up the signal handler and enables + * generation of SIGFPE whenever an exception occurs. From this point + * on, any properly trapped SIGFPE should result in the Python + * FloatingPointError exception. + * + * Step 1 has been done already for the Python kernel code, and should be + * done soon for the NumPy array package. Step 2 is usually done once at + * python install time. Python's behavior with respect to SIGFPE is not + * changed unless you also do step 3. Thus you can control this new + * facility at compile time, or run time, or both. + * + ******************************** + * Using the macros in your code: + * + * static PyObject *foobar(PyObject *self,PyObject *args) + * { + * .... + * PyFPE_START_PROTECT("Error in foobar", return 0) + * result = dangerous_op(somearg1, somearg2, ...); + * PyFPE_END_PROTECT(result) + * .... + * } + * + * If a floating point error occurs in dangerous_op, foobar returns 0 (NULL), + * after setting the associated value of the FloatingPointError exception to + * "Error in foobar". ``Dangerous_op'' can be a single operation, or a block + * of code, function calls, or any combination, so long as no alternate + * return is possible before the PyFPE_END_PROTECT macro is reached. + * + * The macros can only be used in a function context where an error return + * can be recognized as signaling a Python exception. (Generally, most + * functions that return a PyObject * will qualify.) + * + * Guido's original design suggestion for PyFPE_START_PROTECT and + * PyFPE_END_PROTECT had them open and close a local block, with a locally + * defined jmp_buf and jmp_buf pointer. This would allow recursive nesting + * of the macros. The Ansi C standard makes it clear that such local + * variables need to be declared with the "volatile" type qualifier to keep + * setjmp from corrupting their values. Some current implementations seem + * to be more restrictive. For example, the HPUX man page for setjmp says + * + * Upon the return from a setjmp() call caused by a longjmp(), the + * values of any non-static local variables belonging to the routine + * from which setjmp() was called are undefined. Code which depends on + * such values is not guaranteed to be portable. + * + * I therefore decided on a more limited form of nesting, using a counter + * variable (PyFPE_counter) to keep track of any recursion. If an exception + * occurs in an ``inner'' pair of macros, the return will apparently + * come from the outermost level. + * + */ + +#ifdef WANT_SIGFPE_HANDLER +#include +#include +#include +extern jmp_buf PyFPE_jbuf; +extern int PyFPE_counter; +extern double PyFPE_dummy(void *); + +#define PyFPE_START_PROTECT(err_string, leave_stmt) \ +if (!PyFPE_counter++ && setjmp(PyFPE_jbuf)) { \ + PyErr_SetString(PyExc_FloatingPointError, err_string); \ + PyFPE_counter = 0; \ + leave_stmt; \ +} + +/* + * This (following) is a heck of a way to decrement a counter. However, + * unless the macro argument is provided, code optimizers will sometimes move + * this statement so that it gets executed *before* the unsafe expression + * which we're trying to protect. That pretty well messes things up, + * of course. + * + * If the expression(s) you're trying to protect don't happen to return a + * value, you will need to manufacture a dummy result just to preserve the + * correct ordering of statements. Note that the macro passes the address + * of its argument (so you need to give it something which is addressable). + * If your expression returns multiple results, pass the last such result + * to PyFPE_END_PROTECT. + * + * Note that PyFPE_dummy returns a double, which is cast to int. + * This seeming insanity is to tickle the Floating Point Unit (FPU). + * If an exception has occurred in a preceding floating point operation, + * some architectures (notably Intel 80x86) will not deliver the interrupt + * until the *next* floating point operation. This is painful if you've + * already decremented PyFPE_counter. + */ +#define PyFPE_END_PROTECT(v) PyFPE_counter -= (int)PyFPE_dummy(&(v)); + +#else + +#define PyFPE_START_PROTECT(err_string, leave_stmt) +#define PyFPE_END_PROTECT(v) + +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYFPE_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pygetopt.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pygetopt.h new file mode 100644 index 00000000..8031346d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pygetopt.h @@ -0,0 +1,17 @@ + +#ifndef Py_PYGETOPT_H +#define Py_PYGETOPT_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_DATA(int) _PyOS_opterr; +PyAPI_DATA(int) _PyOS_optind; +PyAPI_DATA(char *) _PyOS_optarg; + +PyAPI_FUNC(int) _PyOS_GetOpt(int argc, char **argv, char *optstring); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYGETOPT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pymactoolbox.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pymactoolbox.h new file mode 100644 index 00000000..7770f945 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pymactoolbox.h @@ -0,0 +1,204 @@ +/* +** pymactoolbox.h - globals defined in mactoolboxglue.c +*/ +#ifndef Py_PYMACTOOLBOX_H +#define Py_PYMACTOOLBOX_H +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef WITHOUT_FRAMEWORKS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#endif + +/* +** Helper routines for error codes and such. +*/ +char *PyMac_getscript(void); /* Get the default encoding for our 8bit character set */ +char *PyMac_StrError(int); /* strerror with mac errors */ +PyObject *PyErr_Mac(PyObject *, int); /* Exception with a mac error */ +PyObject *PyMac_Error(OSErr); /* Uses PyMac_GetOSErrException */ +extern OSErr PyMac_GetFullPathname(FSSpec *, char *, int); /* convert fsspec->path */ +#ifdef WITH_NEXT_FRAMEWORK +extern char *PyMac_GetAppletScriptFile(void); /* Return applet script file or NULL */ +#endif +/* +** These conversion routines are defined in mactoolboxglue.c itself. +*/ +int PyMac_GetOSType(PyObject *, OSType *); /* argument parser for OSType */ +PyObject *PyMac_BuildOSType(OSType); /* Convert OSType to PyObject */ + +PyObject *PyMac_BuildNumVersion(NumVersion);/* Convert NumVersion to PyObject */ + +int PyMac_GetStr255(PyObject *, Str255); /* argument parser for Str255 */ +PyObject *PyMac_BuildStr255(Str255); /* Convert Str255 to PyObject */ +PyObject *PyMac_BuildOptStr255(Str255); /* Convert Str255 to PyObject, NULL to None */ + +int PyMac_GetRect(PyObject *, Rect *); /* argument parser for Rect */ +PyObject *PyMac_BuildRect(Rect *); /* Convert Rect to PyObject */ + +int PyMac_GetPoint(PyObject *, Point *); /* argument parser for Point */ +PyObject *PyMac_BuildPoint(Point); /* Convert Point to PyObject */ + +int PyMac_GetEventRecord(PyObject *, EventRecord *); /* argument parser for EventRecord */ +PyObject *PyMac_BuildEventRecord(EventRecord *); /* Convert EventRecord to PyObject */ + +int PyMac_GetFixed(PyObject *, Fixed *); /* argument parser for Fixed */ +PyObject *PyMac_BuildFixed(Fixed); /* Convert Fixed to PyObject */ +int PyMac_Getwide(PyObject *, wide *); /* argument parser for wide */ +PyObject *PyMac_Buildwide(wide *); /* Convert wide to PyObject */ + +/* +** The rest of the routines are implemented by extension modules. If they are +** dynamically loaded mactoolboxglue will contain a stub implementation of the +** routine, which imports the module, whereupon the module's init routine will +** communicate the routine pointer back to the stub. +** If USE_TOOLBOX_OBJECT_GLUE is not defined there is no glue code, and the +** extension modules simply declare the routine. This is the case for static +** builds (and could be the case for MacPython CFM builds, because CFM extension +** modules can reference each other without problems). +*/ + +#ifdef USE_TOOLBOX_OBJECT_GLUE +/* +** These macros are used in the module init code. If we use toolbox object glue +** it sets the function pointer to point to the real function. +*/ +#define PyMac_INIT_TOOLBOX_OBJECT_NEW(object, rtn) { \ + extern PyObject *(*PyMacGluePtr_##rtn)(object); \ + PyMacGluePtr_##rtn = _##rtn; \ +} +#define PyMac_INIT_TOOLBOX_OBJECT_CONVERT(object, rtn) { \ + extern int (*PyMacGluePtr_##rtn)(PyObject *, object *); \ + PyMacGluePtr_##rtn = _##rtn; \ +} +#else +/* +** If we don't use toolbox object glue the init macros are empty. Moreover, we define +** _xxx_New to be the same as xxx_New, and the code in mactoolboxglue isn't included. +*/ +#define PyMac_INIT_TOOLBOX_OBJECT_NEW(object, rtn) +#define PyMac_INIT_TOOLBOX_OBJECT_CONVERT(object, rtn) +#endif /* USE_TOOLBOX_OBJECT_GLUE */ + +/* macfs exports */ +int PyMac_GetFSSpec(PyObject *, FSSpec *); /* argument parser for FSSpec */ +PyObject *PyMac_BuildFSSpec(FSSpec *); /* Convert FSSpec to PyObject */ + +int PyMac_GetFSRef(PyObject *, FSRef *); /* argument parser for FSRef */ +PyObject *PyMac_BuildFSRef(FSRef *); /* Convert FSRef to PyObject */ + +/* AE exports */ +extern PyObject *AEDesc_New(AppleEvent *); /* XXXX Why passed by address?? */ +extern PyObject *AEDesc_NewBorrowed(AppleEvent *); +extern int AEDesc_Convert(PyObject *, AppleEvent *); + +/* Cm exports */ +extern PyObject *CmpObj_New(Component); +extern int CmpObj_Convert(PyObject *, Component *); +extern PyObject *CmpInstObj_New(ComponentInstance); +extern int CmpInstObj_Convert(PyObject *, ComponentInstance *); + +/* Ctl exports */ +extern PyObject *CtlObj_New(ControlHandle); +extern int CtlObj_Convert(PyObject *, ControlHandle *); + +/* Dlg exports */ +extern PyObject *DlgObj_New(DialogPtr); +extern int DlgObj_Convert(PyObject *, DialogPtr *); +extern PyObject *DlgObj_WhichDialog(DialogPtr); + +/* Drag exports */ +extern PyObject *DragObj_New(DragReference); +extern int DragObj_Convert(PyObject *, DragReference *); + +/* List exports */ +extern PyObject *ListObj_New(ListHandle); +extern int ListObj_Convert(PyObject *, ListHandle *); + +/* Menu exports */ +extern PyObject *MenuObj_New(MenuHandle); +extern int MenuObj_Convert(PyObject *, MenuHandle *); + +/* Qd exports */ +extern PyObject *GrafObj_New(GrafPtr); +extern int GrafObj_Convert(PyObject *, GrafPtr *); +extern PyObject *BMObj_New(BitMapPtr); +extern int BMObj_Convert(PyObject *, BitMapPtr *); +extern PyObject *QdRGB_New(RGBColor *); +extern int QdRGB_Convert(PyObject *, RGBColor *); + +/* Qdoffs exports */ +extern PyObject *GWorldObj_New(GWorldPtr); +extern int GWorldObj_Convert(PyObject *, GWorldPtr *); + +/* Qt exports */ +extern PyObject *TrackObj_New(Track); +extern int TrackObj_Convert(PyObject *, Track *); +extern PyObject *MovieObj_New(Movie); +extern int MovieObj_Convert(PyObject *, Movie *); +extern PyObject *MovieCtlObj_New(MovieController); +extern int MovieCtlObj_Convert(PyObject *, MovieController *); +extern PyObject *TimeBaseObj_New(TimeBase); +extern int TimeBaseObj_Convert(PyObject *, TimeBase *); +extern PyObject *UserDataObj_New(UserData); +extern int UserDataObj_Convert(PyObject *, UserData *); +extern PyObject *MediaObj_New(Media); +extern int MediaObj_Convert(PyObject *, Media *); + +/* Res exports */ +extern PyObject *ResObj_New(Handle); +extern int ResObj_Convert(PyObject *, Handle *); +extern PyObject *OptResObj_New(Handle); +extern int OptResObj_Convert(PyObject *, Handle *); + +/* TE exports */ +extern PyObject *TEObj_New(TEHandle); +extern int TEObj_Convert(PyObject *, TEHandle *); + +/* Win exports */ +extern PyObject *WinObj_New(WindowPtr); +extern int WinObj_Convert(PyObject *, WindowPtr *); +extern PyObject *WinObj_WhichWindow(WindowPtr); + +/* CF exports */ +extern PyObject *CFObj_New(CFTypeRef); +extern int CFObj_Convert(PyObject *, CFTypeRef *); +extern PyObject *CFTypeRefObj_New(CFTypeRef); +extern int CFTypeRefObj_Convert(PyObject *, CFTypeRef *); +extern PyObject *CFStringRefObj_New(CFStringRef); +extern int CFStringRefObj_Convert(PyObject *, CFStringRef *); +extern PyObject *CFMutableStringRefObj_New(CFMutableStringRef); +extern int CFMutableStringRefObj_Convert(PyObject *, CFMutableStringRef *); +extern PyObject *CFArrayRefObj_New(CFArrayRef); +extern int CFArrayRefObj_Convert(PyObject *, CFArrayRef *); +extern PyObject *CFMutableArrayRefObj_New(CFMutableArrayRef); +extern int CFMutableArrayRefObj_Convert(PyObject *, CFMutableArrayRef *); +extern PyObject *CFDictionaryRefObj_New(CFDictionaryRef); +extern int CFDictionaryRefObj_Convert(PyObject *, CFDictionaryRef *); +extern PyObject *CFMutableDictionaryRefObj_New(CFMutableDictionaryRef); +extern int CFMutableDictionaryRefObj_Convert(PyObject *, CFMutableDictionaryRef *); +extern PyObject *CFURLRefObj_New(CFURLRef); +extern int CFURLRefObj_Convert(PyObject *, CFURLRef *); +extern int OptionalCFURLRefObj_Convert(PyObject *, CFURLRef *); + +#ifdef __cplusplus + } +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pymem.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pymem.h new file mode 100644 index 00000000..a78fed55 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pymem.h @@ -0,0 +1,108 @@ +/* The PyMem_ family: low-level memory allocation interfaces. + See objimpl.h for the PyObject_ memory family. +*/ + +#ifndef Py_PYMEM_H +#define Py_PYMEM_H + +#include "pyport.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* BEWARE: + + Each interface exports both functions and macros. Extension modules should + use the functions, to ensure binary compatibility across Python versions. + Because the Python implementation is free to change internal details, and + the macros may (or may not) expose details for speed, if you do use the + macros you must recompile your extensions with each Python release. + + Never mix calls to PyMem_ with calls to the platform malloc/realloc/ + calloc/free. For example, on Windows different DLLs may end up using + different heaps, and if you use PyMem_Malloc you'll get the memory from the + heap used by the Python DLL; it could be a disaster if you free()'ed that + directly in your own extension. Using PyMem_Free instead ensures Python + can return the memory to the proper heap. As another example, in + PYMALLOC_DEBUG mode, Python wraps all calls to all PyMem_ and PyObject_ + memory functions in special debugging wrappers that add additional + debugging info to dynamic memory blocks. The system routines have no idea + what to do with that stuff, and the Python wrappers have no idea what to do + with raw blocks obtained directly by the system routines then. +*/ + +/* + * Raw memory interface + * ==================== + */ + +/* Functions + + Functions supplying platform-independent semantics for malloc/realloc/ + free. These functions make sure that allocating 0 bytes returns a distinct + non-NULL pointer (whenever possible -- if we're flat out of memory, NULL + may be returned), even if the platform malloc and realloc don't. + Returned pointers must be checked for NULL explicitly. No action is + performed on failure (no exception is set, no warning is printed, etc). +*/ + +PyAPI_FUNC(void *) PyMem_Malloc(size_t); +PyAPI_FUNC(void *) PyMem_Realloc(void *, size_t); +PyAPI_FUNC(void) PyMem_Free(void *); + +/* Starting from Python 1.6, the wrappers Py_{Malloc,Realloc,Free} are + no longer supported. They used to call PyErr_NoMemory() on failure. */ + +/* Macros. */ +#ifdef PYMALLOC_DEBUG +/* Redirect all memory operations to Python's debugging allocator. */ +#define PyMem_MALLOC PyObject_MALLOC +#define PyMem_REALLOC PyObject_REALLOC + +#else /* ! PYMALLOC_DEBUG */ + +/* PyMem_MALLOC(0) means malloc(1). Some systems would return NULL + for malloc(0), which would be treated as an error. Some platforms + would return a pointer with no memory behind it, which would break + pymalloc. To solve these problems, allocate an extra byte. */ +#define PyMem_MALLOC(n) malloc((n) ? (n) : 1) +#define PyMem_REALLOC(p, n) realloc((p), (n) ? (n) : 1) + +#endif /* PYMALLOC_DEBUG */ + +/* In order to avoid breaking old code mixing PyObject_{New, NEW} with + PyMem_{Del, DEL} and PyMem_{Free, FREE}, the PyMem "release memory" + functions have to be redirected to the object deallocator. */ +#define PyMem_FREE PyObject_FREE + +/* + * Type-oriented memory interface + * ============================== + * + * These are carried along for historical reasons. There's rarely a good + * reason to use them anymore (you can just as easily do the multiply and + * cast yourself). + */ + +#define PyMem_New(type, n) \ + ( (type *) PyMem_Malloc((n) * sizeof(type)) ) +#define PyMem_NEW(type, n) \ + ( (type *) PyMem_MALLOC((n) * sizeof(type)) ) + +#define PyMem_Resize(p, type, n) \ + ( (p) = (type *) PyMem_Realloc((p), (n) * sizeof(type)) ) +#define PyMem_RESIZE(p, type, n) \ + ( (p) = (type *) PyMem_REALLOC((p), (n) * sizeof(type)) ) + +/* In order to avoid breaking old code mixing PyObject_{New, NEW} with + PyMem_{Del, DEL} and PyMem_{Free, FREE}, the PyMem "release memory" + functions have to be redirected to the object deallocator. */ +#define PyMem_Del PyObject_Free +#define PyMem_DEL PyObject_FREE + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_PYMEM_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyport.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyport.h new file mode 100644 index 00000000..e60aa514 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pyport.h @@ -0,0 +1,572 @@ +#ifndef Py_PYPORT_H +#define Py_PYPORT_H + +#include "pyconfig.h" /* include for defines */ + +/************************************************************************** +Symbols and macros to supply platform-independent interfaces to basic +C language & library operations whose spellings vary across platforms. + +Please try to make documentation here as clear as possible: by definition, +the stuff here is trying to illuminate C's darkest corners. + +Config #defines referenced here: + +SIGNED_RIGHT_SHIFT_ZERO_FILLS +Meaning: To be defined iff i>>j does not extend the sign bit when i is a + signed integral type and i < 0. +Used in: Py_ARITHMETIC_RIGHT_SHIFT + +Py_DEBUG +Meaning: Extra checks compiled in for debug mode. +Used in: Py_SAFE_DOWNCAST + +HAVE_UINTPTR_T +Meaning: The C9X type uintptr_t is supported by the compiler +Used in: Py_uintptr_t + +HAVE_LONG_LONG +Meaning: The compiler supports the C type "long long" +Used in: PY_LONG_LONG + +**************************************************************************/ + + +/* For backward compatibility only. Obsolete, do not use. */ +#ifdef HAVE_PROTOTYPES +#define Py_PROTO(x) x +#else +#define Py_PROTO(x) () +#endif +#ifndef Py_FPROTO +#define Py_FPROTO(x) Py_PROTO(x) +#endif + +/* typedefs for some C9X-defined synonyms for integral types. + * + * The names in Python are exactly the same as the C9X names, except with a + * Py_ prefix. Until C9X is universally implemented, this is the only way + * to ensure that Python gets reliable names that don't conflict with names + * in non-Python code that are playing their own tricks to define the C9X + * names. + * + * NOTE: don't go nuts here! Python has no use for *most* of the C9X + * integral synonyms. Only define the ones we actually need. + */ + +#ifdef HAVE_LONG_LONG +#ifndef PY_LONG_LONG +#define PY_LONG_LONG long long +#endif +#endif /* HAVE_LONG_LONG */ + +/* uintptr_t is the C9X name for an unsigned integral type such that a + * legitimate void* can be cast to uintptr_t and then back to void* again + * without loss of information. Similarly for intptr_t, wrt a signed + * integral type. + */ +#ifdef HAVE_UINTPTR_T +typedef uintptr_t Py_uintptr_t; +typedef intptr_t Py_intptr_t; + +#elif SIZEOF_VOID_P <= SIZEOF_INT +typedef unsigned int Py_uintptr_t; +typedef int Py_intptr_t; + +#elif SIZEOF_VOID_P <= SIZEOF_LONG +typedef unsigned long Py_uintptr_t; +typedef long Py_intptr_t; + +#elif defined(HAVE_LONG_LONG) && (SIZEOF_VOID_P <= SIZEOF_LONG_LONG) +typedef unsigned PY_LONG_LONG Py_uintptr_t; +typedef PY_LONG_LONG Py_intptr_t; + +#else +# error "Python needs a typedef for Py_uintptr_t in pyport.h." +#endif /* HAVE_UINTPTR_T */ + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include /* Moved here from the math section, before extern "C" */ + +/******************************************** + * WRAPPER FOR and/or * + ********************************************/ + +#ifdef TIME_WITH_SYS_TIME +#include +#include +#else /* !TIME_WITH_SYS_TIME */ +#ifdef HAVE_SYS_TIME_H +#include +#else /* !HAVE_SYS_TIME_H */ +#include +#endif /* !HAVE_SYS_TIME_H */ +#endif /* !TIME_WITH_SYS_TIME */ + + +/****************************** + * WRAPPER FOR * + ******************************/ + +/* NB caller must include */ + +#ifdef HAVE_SYS_SELECT_H + +#include + +#endif /* !HAVE_SYS_SELECT_H */ + +/******************************* + * stat() and fstat() fiddling * + *******************************/ + +/* We expect that stat and fstat exist on most systems. + * It's confirmed on Unix, Mac and Windows. + * If you don't have them, add + * #define DONT_HAVE_STAT + * and/or + * #define DONT_HAVE_FSTAT + * to your pyconfig.h. Python code beyond this should check HAVE_STAT and + * HAVE_FSTAT instead. + * Also + * #define DONT_HAVE_SYS_STAT_H + * if doesn't exist on your platform, and + * #define HAVE_STAT_H + * if does (don't look at me -- ths mess is inherited). + */ +#ifndef DONT_HAVE_STAT +#define HAVE_STAT +#endif + +#ifndef DONT_HAVE_FSTAT +#define HAVE_FSTAT +#endif + +#ifdef RISCOS +#include +#include "unixstuff.h" +#endif + +#ifndef DONT_HAVE_SYS_STAT_H +#if defined(PYOS_OS2) && defined(PYCC_GCC) +#include +#endif +#include +#elif defined(HAVE_STAT_H) +#include +#endif + +#if defined(PYCC_VACPP) +/* VisualAge C/C++ Failed to Define MountType Field in sys/stat.h */ +#define S_IFMT (S_IFDIR|S_IFCHR|S_IFREG) +#endif + +#ifndef S_ISREG +#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) +#endif + +#ifndef S_ISDIR +#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) +#endif + + +#ifdef __cplusplus +/* Move this down here since some C++ #include's don't like to be included + inside an extern "C" */ +extern "C" { +#endif + + +/* Py_ARITHMETIC_RIGHT_SHIFT + * C doesn't define whether a right-shift of a signed integer sign-extends + * or zero-fills. Here a macro to force sign extension: + * Py_ARITHMETIC_RIGHT_SHIFT(TYPE, I, J) + * Return I >> J, forcing sign extension. + * Requirements: + * I is of basic signed type TYPE (char, short, int, long, or long long). + * TYPE is one of char, short, int, long, or long long, although long long + * must not be used except on platforms that support it. + * J is an integer >= 0 and strictly less than the number of bits in TYPE + * (because C doesn't define what happens for J outside that range either). + * Caution: + * I may be evaluated more than once. + */ +#ifdef SIGNED_RIGHT_SHIFT_ZERO_FILLS +#define Py_ARITHMETIC_RIGHT_SHIFT(TYPE, I, J) \ + ((I) < 0 ? ~((~(unsigned TYPE)(I)) >> (J)) : (I) >> (J)) +#else +#define Py_ARITHMETIC_RIGHT_SHIFT(TYPE, I, J) ((I) >> (J)) +#endif + +/* Py_FORCE_EXPANSION(X) + * "Simply" returns its argument. However, macro expansions within the + * argument are evaluated. This unfortunate trickery is needed to get + * token-pasting to work as desired in some cases. + */ +#define Py_FORCE_EXPANSION(X) X + +/* Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) + * Cast VALUE to type NARROW from type WIDE. In Py_DEBUG mode, this + * assert-fails if any information is lost. + * Caution: + * VALUE may be evaluated more than once. + */ +#ifdef Py_DEBUG +#define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) \ + (assert((WIDE)(NARROW)(VALUE) == (VALUE)), (NARROW)(VALUE)) +#else +#define Py_SAFE_DOWNCAST(VALUE, WIDE, NARROW) (NARROW)(VALUE) +#endif + +/* Py_IS_INFINITY(X) + * Return 1 if float or double arg is an infinity, else 0. + * Caution: + * X is evaluated more than once. + * This implementation may set the underflow flag if |X| is very small; + * it really can't be implemented correctly (& easily) before C99. + */ +#define Py_IS_INFINITY(X) ((X) && (X)*0.5 == (X)) + +/* HUGE_VAL is supposed to expand to a positive double infinity. Python + * uses Py_HUGE_VAL instead because some platforms are broken in this + * respect. We used to embed code in pyport.h to try to worm around that, + * but different platforms are broken in conflicting ways. If you're on + * a platform where HUGE_VAL is defined incorrectly, fiddle your Python + * config to #define Py_HUGE_VAL to something that works on your platform. + */ +#ifndef Py_HUGE_VAL +#define Py_HUGE_VAL HUGE_VAL +#endif + +/* Py_OVERFLOWED(X) + * Return 1 iff a libm function overflowed. Set errno to 0 before calling + * a libm function, and invoke this macro after, passing the function + * result. + * Caution: + * This isn't reliable. C99 no longer requires libm to set errno under + * any exceptional condition, but does require +- HUGE_VAL return + * values on overflow. A 754 box *probably* maps HUGE_VAL to a + * double infinity, and we're cool if that's so, unless the input + * was an infinity and an infinity is the expected result. A C89 + * system sets errno to ERANGE, so we check for that too. We're + * out of luck if a C99 754 box doesn't map HUGE_VAL to +Inf, or + * if the returned result is a NaN, or if a C89 box returns HUGE_VAL + * in non-overflow cases. + * X is evaluated more than once. + * Some platforms have better way to spell this, so expect some #ifdef'ery. + * + * OpenBSD uses 'isinf()' because a compiler bug on that platform causes + * the longer macro version to be mis-compiled. This isn't optimal, and + * should be removed once a newer compiler is available on that platform. + * The system that had the failure was running OpenBSD 3.2 on Intel, with + * gcc 2.95.3. + * + * According to Tim's checkin, the FreeBSD systems use isinf() to work + * around a FPE bug on that platform. + */ +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#define Py_OVERFLOWED(X) isinf(X) +#else +#define Py_OVERFLOWED(X) ((X) != 0.0 && (errno == ERANGE || \ + (X) == Py_HUGE_VAL || \ + (X) == -Py_HUGE_VAL)) +#endif + +/* Py_SET_ERANGE_ON_OVERFLOW(x) + * If a libm function did not set errno, but it looks like the result + * overflowed, set errno to ERANGE. Set errno to 0 before calling a libm + * function, and invoke this macro after, passing the function result. + * Caution: + * This isn't reliable. See Py_OVERFLOWED comments. + * X is evaluated more than once. + */ +#define Py_SET_ERANGE_IF_OVERFLOW(X) \ + do { \ + if (errno == 0 && ((X) == Py_HUGE_VAL || \ + (X) == -Py_HUGE_VAL)) \ + errno = ERANGE; \ + } while(0) + +/* Py_ADJUST_ERANGE1(x) + * Py_ADJUST_ERANGE2(x, y) + * Set errno to 0 before calling a libm function, and invoke one of these + * macros after, passing the function result(s) (Py_ADJUST_ERANGE2 is useful + * for functions returning complex results). This makes two kinds of + * adjustments to errno: (A) If it looks like the platform libm set + * errno=ERANGE due to underflow, clear errno. (B) If it looks like the + * platform libm overflowed but didn't set errno, force errno to ERANGE. In + * effect, we're trying to force a useful implementation of C89 errno + * behavior. + * Caution: + * This isn't reliable. See Py_OVERFLOWED comments. + * X and Y may be evaluated more than once. + */ +#define Py_ADJUST_ERANGE1(X) \ + do { \ + if (errno == 0) { \ + if ((X) == Py_HUGE_VAL || (X) == -Py_HUGE_VAL) \ + errno = ERANGE; \ + } \ + else if (errno == ERANGE && (X) == 0.0) \ + errno = 0; \ + } while(0) + +#define Py_ADJUST_ERANGE2(X, Y) \ + do { \ + if ((X) == Py_HUGE_VAL || (X) == -Py_HUGE_VAL || \ + (Y) == Py_HUGE_VAL || (Y) == -Py_HUGE_VAL) { \ + if (errno == 0) \ + errno = ERANGE; \ + } \ + else if (errno == ERANGE) \ + errno = 0; \ + } while(0) + +/* Py_DEPRECATED(version) + * Declare a variable, type, or function deprecated. + * Usage: + * extern int old_var Py_DEPRECATED(2.3); + * typedef int T1 Py_DEPRECATED(2.4); + * extern int x() Py_DEPRECATED(2.5); + */ +#if defined(__GNUC__) && (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) +#define Py_DEPRECATED(VERSION_UNUSED) __attribute__((__deprecated__)) +#else +#define Py_DEPRECATED(VERSION_UNUSED) +#endif + +/************************************************************************** +Prototypes that are missing from the standard include files on some systems +(and possibly only some versions of such systems.) + +Please be conservative with adding new ones, document them and enclose them +in platform-specific #ifdefs. +**************************************************************************/ + +#ifdef SOLARIS +/* Unchecked */ +extern int gethostname(char *, int); +#endif + +#ifdef __BEOS__ +/* Unchecked */ +/* It's in the libs, but not the headers... - [cjh] */ +int shutdown( int, int ); +#endif + +#ifdef HAVE__GETPTY +#include /* we need to import mode_t */ +extern char * _getpty(int *, int, mode_t, int); +#endif + +#if defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) +#if !defined(HAVE_PTY_H) && !defined(HAVE_LIBUTIL_H) +/* BSDI does not supply a prototype for the 'openpty' and 'forkpty' + functions, even though they are included in libutil. */ +#include +extern int openpty(int *, int *, char *, struct termios *, struct winsize *); +extern int forkpty(int *, char *, struct termios *, struct winsize *); +#endif /* !defined(HAVE_PTY_H) && !defined(HAVE_LIBUTIL_H) */ +#endif /* defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) */ + + +/* These are pulled from various places. It isn't obvious on what platforms + they are necessary, nor what the exact prototype should look like (which + is likely to vary between platforms!) If you find you need one of these + declarations, please move them to a platform-specific block and include + proper prototypes. */ +#if 0 + +/* From Modules/resource.c */ +extern int getrusage(); +extern int getpagesize(); + +/* From Python/sysmodule.c and Modules/posixmodule.c */ +extern int fclose(FILE *); + +/* From Modules/posixmodule.c */ +extern int fdatasync(int); +/* XXX These are supposedly for SunOS4.1.3 but "shouldn't hurt elsewhere" */ +extern int rename(const char *, const char *); +extern int pclose(FILE *); +extern int lstat(const char *, struct stat *); +extern int symlink(const char *, const char *); +extern int fsync(int fd); + +#endif /* 0 */ + + +/************************ + * WRAPPER FOR * + ************************/ + +#ifndef HAVE_HYPOT +extern double hypot(double, double); +#endif + +/* Declarations for symbol visibility. + + PyAPI_FUNC(type): Declares a public Python API function and return type + PyAPI_DATA(type): Declares public Python data and its type + PyMODINIT_FUNC: A Python module init function. If these functions are + inside the Python core, they are private to the core. + If in an extension module, it may be declared with + external linkage depending on the platform. + + As a number of platforms support/require "__declspec(dllimport/dllexport)", + we support a HAVE_DECLSPEC_DLL macro to save duplication. +*/ + +/* + All windows ports, except cygwin, are handled in PC/pyconfig.h. + + BeOS and cygwin are the only other autoconf platform requiring special + linkage handling and both of these use __declspec(). +*/ +#if defined(__CYGWIN__) || defined(__BEOS__) +# define HAVE_DECLSPEC_DLL +#endif + +#if defined(Py_ENABLE_SHARED) /* only get special linkage if built as shared */ +# if defined(HAVE_DECLSPEC_DLL) +# ifdef Py_BUILD_CORE +# define PyAPI_FUNC(RTYPE) __declspec(dllexport) RTYPE +# define PyAPI_DATA(RTYPE) extern __declspec(dllexport) RTYPE + /* module init functions inside the core need no external linkage */ +# define PyMODINIT_FUNC void +# else /* Py_BUILD_CORE */ + /* Building an extension module, or an embedded situation */ + /* public Python functions and data are imported */ + /* Under Cygwin, auto-import functions to prevent compilation */ + /* failures similar to http://python.org/doc/FAQ.html#3.24 */ +# if !defined(__CYGWIN__) +# define PyAPI_FUNC(RTYPE) __declspec(dllimport) RTYPE +# endif /* !__CYGWIN__ */ +# define PyAPI_DATA(RTYPE) extern __declspec(dllimport) RTYPE + /* module init functions outside the core must be exported */ +# if defined(__cplusplus) +# define PyMODINIT_FUNC extern "C" __declspec(dllexport) void +# else /* __cplusplus */ +# define PyMODINIT_FUNC __declspec(dllexport) void +# endif /* __cplusplus */ +# endif /* Py_BUILD_CORE */ +# endif /* HAVE_DECLSPEC */ +#endif /* Py_ENABLE_SHARED */ + +/* If no external linkage macros defined by now, create defaults */ +#ifndef PyAPI_FUNC +# define PyAPI_FUNC(RTYPE) RTYPE +#endif +#ifndef PyAPI_DATA +# define PyAPI_DATA(RTYPE) extern RTYPE +#endif +#ifndef PyMODINIT_FUNC +# if defined(__cplusplus) +# define PyMODINIT_FUNC extern "C" void +# else /* __cplusplus */ +# define PyMODINIT_FUNC void +# endif /* __cplusplus */ +#endif + +/* Deprecated DL_IMPORT and DL_EXPORT macros */ +#if defined(Py_ENABLE_SHARED) && defined (HAVE_DECLSPEC_DLL) +# if defined(Py_BUILD_CORE) +# define DL_IMPORT(RTYPE) __declspec(dllexport) RTYPE +# define DL_EXPORT(RTYPE) __declspec(dllexport) RTYPE +# else +# define DL_IMPORT(RTYPE) __declspec(dllimport) RTYPE +# define DL_EXPORT(RTYPE) __declspec(dllexport) RTYPE +# endif +#endif +#ifndef DL_EXPORT +# define DL_EXPORT(RTYPE) RTYPE +#endif +#ifndef DL_IMPORT +# define DL_IMPORT(RTYPE) RTYPE +#endif +/* End of deprecated DL_* macros */ + +/* If the fd manipulation macros aren't defined, + here is a set that should do the job */ + +#if 0 /* disabled and probably obsolete */ + +#ifndef FD_SETSIZE +#define FD_SETSIZE 256 +#endif + +#ifndef FD_SET + +typedef long fd_mask; + +#define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */ +#ifndef howmany +#define howmany(x, y) (((x)+((y)-1))/(y)) +#endif /* howmany */ + +typedef struct fd_set { + fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; +} fd_set; + +#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) memset((char *)(p), '\0', sizeof(*(p))) + +#endif /* FD_SET */ + +#endif /* fd manipulation macros */ + + +/* limits.h constants that may be missing */ + +#ifndef INT_MAX +#define INT_MAX 2147483647 +#endif + +#ifndef LONG_MAX +#if SIZEOF_LONG == 4 +#define LONG_MAX 0X7FFFFFFFL +#elif SIZEOF_LONG == 8 +#define LONG_MAX 0X7FFFFFFFFFFFFFFFL +#else +#error "could not set LONG_MAX in pyport.h" +#endif +#endif + +#ifndef LONG_MIN +#define LONG_MIN (-LONG_MAX-1) +#endif + +#ifndef LONG_BIT +#define LONG_BIT (8 * SIZEOF_LONG) +#endif + +#if LONG_BIT != 8 * SIZEOF_LONG +/* 04-Oct-2000 LONG_BIT is apparently (mis)defined as 64 on some recent + * 32-bit platforms using gcc. We try to catch that here at compile-time + * rather than waiting for integer multiplication to trigger bogus + * overflows. + */ +#error "LONG_BIT definition appears wrong for platform (bad gcc/glibc config?)." +#endif + +#ifdef __cplusplus +} +#endif + +/* + * Hide GCC attributes from compilers that don't support them. + */ +#if (!defined(__GNUC__) || __GNUC__ < 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ < 7) ) && \ + !defined(RISCOS) +#define Py_GCC_ATTRIBUTE(x) +#else +#define Py_GCC_ATTRIBUTE(x) __attribute__(x) +#endif + +#endif /* Py_PYPORT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pystate.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pystate.h new file mode 100644 index 00000000..70e5ac88 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pystate.h @@ -0,0 +1,172 @@ + +/* Thread and interpreter state structures and their interfaces */ + + +#ifndef Py_PYSTATE_H +#define Py_PYSTATE_H +#ifdef __cplusplus +extern "C" { +#endif + +/* State shared between threads */ + +struct _ts; /* Forward */ +struct _is; /* Forward */ + +typedef struct _is { + + struct _is *next; + struct _ts *tstate_head; + + PyObject *modules; + PyObject *sysdict; + PyObject *builtins; + + PyObject *codec_search_path; + PyObject *codec_search_cache; + PyObject *codec_error_registry; + +#ifdef HAVE_DLOPEN + int dlopenflags; +#endif + +} PyInterpreterState; + + +/* State unique per thread */ + +struct _frame; /* Avoid including frameobject.h */ + +/* Py_tracefunc return -1 when raising an exception, or 0 for success. */ +typedef int (*Py_tracefunc)(PyObject *, struct _frame *, int, PyObject *); + +/* The following values are used for 'what' for tracefunc functions: */ +#define PyTrace_CALL 0 +#define PyTrace_EXCEPTION 1 +#define PyTrace_LINE 2 +#define PyTrace_RETURN 3 + +typedef struct _ts { + + struct _ts *next; + PyInterpreterState *interp; + + struct _frame *frame; + int recursion_depth; + int tracing; + int use_tracing; + + Py_tracefunc c_profilefunc; + Py_tracefunc c_tracefunc; + PyObject *c_profileobj; + PyObject *c_traceobj; + + PyObject *curexc_type; + PyObject *curexc_value; + PyObject *curexc_traceback; + + PyObject *exc_type; + PyObject *exc_value; + PyObject *exc_traceback; + + PyObject *dict; + + int tick_counter; + int gilstate_counter; + + PyObject *async_exc; /* Asynchronous exception to raise */ + long thread_id; /* Thread id where this tstate was created */ + + /* XXX signal handlers should also be here */ + +} PyThreadState; + + +PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_New(void); +PyAPI_FUNC(void) PyInterpreterState_Clear(PyInterpreterState *); +PyAPI_FUNC(void) PyInterpreterState_Delete(PyInterpreterState *); + +PyAPI_FUNC(PyThreadState *) PyThreadState_New(PyInterpreterState *); +PyAPI_FUNC(void) PyThreadState_Clear(PyThreadState *); +PyAPI_FUNC(void) PyThreadState_Delete(PyThreadState *); +#ifdef WITH_THREAD +PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void); +#endif + +PyAPI_FUNC(PyThreadState *) PyThreadState_Get(void); +PyAPI_FUNC(PyThreadState *) PyThreadState_Swap(PyThreadState *); +PyAPI_FUNC(PyObject *) PyThreadState_GetDict(void); +PyAPI_FUNC(int) PyThreadState_SetAsyncExc(long, PyObject *); + + +/* Variable and macro for in-line access to current thread state */ + +PyAPI_DATA(PyThreadState *) _PyThreadState_Current; + +#ifdef Py_DEBUG +#define PyThreadState_GET() PyThreadState_Get() +#else +#define PyThreadState_GET() (_PyThreadState_Current) +#endif + +typedef + enum {PyGILState_LOCKED, PyGILState_UNLOCKED} + PyGILState_STATE; + +/* Ensure that the current thread is ready to call the Python + C API, regardless of the current state of Python, or of its + thread lock. This may be called as many times as desired + by a thread so long as each call is matched with a call to + PyGILState_Release(). In general, other thread-state APIs may + be used between _Ensure() and _Release() calls, so long as the + thread-state is restored to its previous state before the Release(). + For example, normal use of the Py_BEGIN_ALLOW_THREADS/ + Py_END_ALLOW_THREADS macros are acceptable. + + The return value is an opaque "handle" to the thread state when + PyGILState_Acquire() was called, and must be passed to + PyGILState_Release() to ensure Python is left in the same state. Even + though recursive calls are allowed, these handles can *not* be shared - + each unique call to PyGILState_Ensure must save the handle for its + call to PyGILState_Release. + + When the function returns, the current thread will hold the GIL. + + Failure is a fatal error. +*/ +PyAPI_FUNC(PyGILState_STATE) PyGILState_Ensure(void); + +/* Release any resources previously acquired. After this call, Python's + state will be the same as it was prior to the corresponding + PyGILState_Acquire call (but generally this state will be unknown to + the caller, hence the use of the GILState API.) + + Every call to PyGILState_Ensure must be matched by a call to + PyGILState_Release on the same thread. +*/ +PyAPI_FUNC(void) PyGILState_Release(PyGILState_STATE); + +/* Helper/diagnostic function - get the current thread state for + this thread. May return NULL if no GILState API has been used + on the current thread. Note the main thread always has such a + thread-state, even if no auto-thread-state call has been made + on the main thread. +*/ +PyAPI_FUNC(PyThreadState *) PyGILState_GetThisThreadState(void); + +/* Routines for advanced debuggers, requested by David Beazley. + Don't use unless you know what you are doing! */ +PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_Head(void); +PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_Next(PyInterpreterState *); +PyAPI_FUNC(PyThreadState *) PyInterpreterState_ThreadHead(PyInterpreterState *); +PyAPI_FUNC(PyThreadState *) PyThreadState_Next(PyThreadState *); + +typedef struct _frame *(*PyThreadFrameGetter)(PyThreadState *self_); + +/* hook for PyEval_GetFrame(), requested for Psyco */ +PyAPI_DATA(PyThreadFrameGetter) _PyThreadState_GetFrame; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYSTATE_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pythonrun.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pythonrun.h new file mode 100644 index 00000000..db1c1a4a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pythonrun.h @@ -0,0 +1,149 @@ + +/* Interfaces to parse and execute pieces of python code */ + +#ifndef Py_PYTHONRUN_H +#define Py_PYTHONRUN_H +#ifdef __cplusplus +extern "C" { +#endif + +#define PyCF_MASK (CO_FUTURE_DIVISION) +#define PyCF_MASK_OBSOLETE (CO_GENERATOR_ALLOWED | CO_NESTED) +#define PyCF_SOURCE_IS_UTF8 0x0100 +#define PyCF_DONT_IMPLY_DEDENT 0x0200 + +typedef struct { + int cf_flags; /* bitmask of CO_xxx flags relevant to future */ +} PyCompilerFlags; + +PyAPI_FUNC(void) Py_SetProgramName(char *); +PyAPI_FUNC(char *) Py_GetProgramName(void); + +PyAPI_FUNC(void) Py_SetPythonHome(char *); +PyAPI_FUNC(char *) Py_GetPythonHome(void); + +PyAPI_FUNC(void) Py_Initialize(void); +PyAPI_FUNC(void) Py_Finalize(void); +PyAPI_FUNC(int) Py_IsInitialized(void); +PyAPI_FUNC(PyThreadState *) Py_NewInterpreter(void); +PyAPI_FUNC(void) Py_EndInterpreter(PyThreadState *); + +PyAPI_FUNC(int) PyRun_AnyFile(FILE *, const char *); +PyAPI_FUNC(int) PyRun_AnyFileEx(FILE *, const char *, int); + +PyAPI_FUNC(int) PyRun_AnyFileFlags(FILE *, const char *, PyCompilerFlags *); +PyAPI_FUNC(int) PyRun_AnyFileExFlags(FILE *, const char *, int, PyCompilerFlags *); + +PyAPI_FUNC(int) PyRun_SimpleString(const char *); +PyAPI_FUNC(int) PyRun_SimpleStringFlags(const char *, PyCompilerFlags *); +PyAPI_FUNC(int) PyRun_SimpleFile(FILE *, const char *); +PyAPI_FUNC(int) PyRun_SimpleFileEx(FILE *, const char *, int); +PyAPI_FUNC(int) PyRun_SimpleFileExFlags(FILE *, const char *, int, PyCompilerFlags *); +PyAPI_FUNC(int) PyRun_InteractiveOne(FILE *, const char *); +PyAPI_FUNC(int) PyRun_InteractiveOneFlags(FILE *, const char *, PyCompilerFlags *); +PyAPI_FUNC(int) PyRun_InteractiveLoop(FILE *, const char *); +PyAPI_FUNC(int) PyRun_InteractiveLoopFlags(FILE *, const char *, PyCompilerFlags *); + +PyAPI_FUNC(struct _node *) PyParser_SimpleParseString(const char *, int); +PyAPI_FUNC(struct _node *) PyParser_SimpleParseFile(FILE *, const char *, int); +PyAPI_FUNC(struct _node *) PyParser_SimpleParseStringFlags(const char *, int, int); +PyAPI_FUNC(struct _node *) PyParser_SimpleParseStringFlagsFilename(const char *, + const char *, + int, + int); +PyAPI_FUNC(struct _node *) PyParser_SimpleParseFileFlags(FILE *, const char *, + int, int); + +PyAPI_FUNC(PyObject *) PyRun_String(const char *, int, PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyRun_File(FILE *, const char *, int, PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyRun_FileEx(FILE *, const char *, int, + PyObject *, PyObject *, int); +PyAPI_FUNC(PyObject *) PyRun_StringFlags(const char *, int, PyObject *, PyObject *, + PyCompilerFlags *); +PyAPI_FUNC(PyObject *) PyRun_FileFlags(FILE *, const char *, int, PyObject *, + PyObject *, PyCompilerFlags *); +PyAPI_FUNC(PyObject *) PyRun_FileExFlags(FILE *, const char *, int, PyObject *, + PyObject *, int, PyCompilerFlags *); + +PyAPI_FUNC(PyObject *) Py_CompileString(const char *, const char *, int); +PyAPI_FUNC(PyObject *) Py_CompileStringFlags(const char *, const char *, int, + PyCompilerFlags *); +PyAPI_FUNC(struct symtable *) Py_SymtableString(const char *, const char *, int); + +PyAPI_FUNC(void) PyErr_Print(void); +PyAPI_FUNC(void) PyErr_PrintEx(int); +PyAPI_FUNC(void) PyErr_Display(PyObject *, PyObject *, PyObject *); + +PyAPI_FUNC(int) Py_AtExit(void (*func)(void)); + +PyAPI_FUNC(void) Py_Exit(int); + +PyAPI_FUNC(int) Py_FdIsInteractive(FILE *, const char *); + +/* Bootstrap */ +PyAPI_FUNC(int) Py_Main(int argc, char **argv); + +/* In getpath.c */ +PyAPI_FUNC(char *) Py_GetProgramFullPath(void); +PyAPI_FUNC(char *) Py_GetPrefix(void); +PyAPI_FUNC(char *) Py_GetExecPrefix(void); +PyAPI_FUNC(char *) Py_GetPath(void); + +/* In their own files */ +PyAPI_FUNC(const char *) Py_GetVersion(void); +PyAPI_FUNC(const char *) Py_GetPlatform(void); +PyAPI_FUNC(const char *) Py_GetCopyright(void); +PyAPI_FUNC(const char *) Py_GetCompiler(void); +PyAPI_FUNC(const char *) Py_GetBuildInfo(void); + +/* Internal -- various one-time initializations */ +PyAPI_FUNC(PyObject *) _PyBuiltin_Init(void); +PyAPI_FUNC(PyObject *) _PySys_Init(void); +PyAPI_FUNC(void) _PyImport_Init(void); +PyAPI_FUNC(void) _PyExc_Init(void); +PyAPI_FUNC(void) _PyImportHooks_Init(void); +PyAPI_FUNC(int) _PyFrame_Init(void); +PyAPI_FUNC(int) _PyInt_Init(void); + +/* Various internal finalizers */ +PyAPI_FUNC(void) _PyExc_Fini(void); +PyAPI_FUNC(void) _PyImport_Fini(void); +PyAPI_FUNC(void) PyMethod_Fini(void); +PyAPI_FUNC(void) PyFrame_Fini(void); +PyAPI_FUNC(void) PyCFunction_Fini(void); +PyAPI_FUNC(void) PyTuple_Fini(void); +PyAPI_FUNC(void) PyString_Fini(void); +PyAPI_FUNC(void) PyInt_Fini(void); +PyAPI_FUNC(void) PyFloat_Fini(void); +PyAPI_FUNC(void) PyOS_FiniInterrupts(void); + +/* Stuff with no proper home (yet) */ +PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *); +PyAPI_DATA(int) (*PyOS_InputHook)(void); +PyAPI_DATA(char) *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, char *); + +/* Stack size, in "pointers" (so we get extra safety margins + on 64-bit platforms). On a 32-bit platform, this translates + to a 8k margin. */ +#define PYOS_STACK_MARGIN 2048 + +#if defined(WIN32) && !defined(MS_WIN64) && defined(_MSC_VER) +/* Enable stack checking under Microsoft C */ +#define USE_STACKCHECK +#endif + +#ifdef USE_STACKCHECK +/* Check that we aren't overflowing our stack */ +PyAPI_FUNC(int) PyOS_CheckStack(void); +#endif + +/* Signals */ +typedef void (*PyOS_sighandler_t)(int); +PyAPI_FUNC(PyOS_sighandler_t) PyOS_getsig(int); +PyAPI_FUNC(PyOS_sighandler_t) PyOS_setsig(int, PyOS_sighandler_t); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYTHONRUN_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pythread.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pythread.h new file mode 100644 index 00000000..e980f7ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/pythread.h @@ -0,0 +1,44 @@ + +#ifndef Py_PYTHREAD_H +#define Py_PYTHREAD_H + +#define NO_EXIT_PROG /* don't define PyThread_exit_prog() */ + /* (the result is no use of signals on SGI) */ + +typedef void *PyThread_type_lock; +typedef void *PyThread_type_sema; + +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(void) PyThread_init_thread(void); +PyAPI_FUNC(long) PyThread_start_new_thread(void (*)(void *), void *); +PyAPI_FUNC(void) PyThread_exit_thread(void); +PyAPI_FUNC(void) PyThread__PyThread_exit_thread(void); +PyAPI_FUNC(long) PyThread_get_thread_ident(void); + +PyAPI_FUNC(PyThread_type_lock) PyThread_allocate_lock(void); +PyAPI_FUNC(void) PyThread_free_lock(PyThread_type_lock); +PyAPI_FUNC(int) PyThread_acquire_lock(PyThread_type_lock, int); +#define WAIT_LOCK 1 +#define NOWAIT_LOCK 0 +PyAPI_FUNC(void) PyThread_release_lock(PyThread_type_lock); + +#ifndef NO_EXIT_PROG +PyAPI_FUNC(void) PyThread_exit_prog(int); +PyAPI_FUNC(void) PyThread__PyThread_exit_prog(int); +#endif + +/* Thread Local Storage (TLS) API */ +PyAPI_FUNC(int) PyThread_create_key(void); +PyAPI_FUNC(void) PyThread_delete_key(int); +PyAPI_FUNC(int) PyThread_set_key_value(int, void *); +PyAPI_FUNC(void *) PyThread_get_key_value(int); +PyAPI_FUNC(void) PyThread_delete_key_value(int key); + +#ifdef __cplusplus +} +#endif + +#endif /* !Py_PYTHREAD_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/rangeobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/rangeobject.h new file mode 100644 index 00000000..fc97d05b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/rangeobject.h @@ -0,0 +1,27 @@ + +/* Range object interface */ + +#ifndef Py_RANGEOBJECT_H +#define Py_RANGEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* +A range object represents an integer range. This is an immutable object; +a range cannot change its value after creation. + +Range objects behave like the corresponding tuple objects except that +they are represented by a start, stop, and step datamembers. +*/ + +PyAPI_DATA(PyTypeObject) PyRange_Type; + +#define PyRange_Check(op) ((op)->ob_type == &PyRange_Type) + +PyAPI_FUNC(PyObject *) PyRange_New(long, long, long, int); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_RANGEOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/sliceobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/sliceobject.h new file mode 100644 index 00000000..67049f48 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/sliceobject.h @@ -0,0 +1,42 @@ +#ifndef Py_SLICEOBJECT_H +#define Py_SLICEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* The unique ellipsis object "..." */ + +PyAPI_DATA(PyObject) _Py_EllipsisObject; /* Don't use this directly */ + +#define Py_Ellipsis (&_Py_EllipsisObject) + +/* Slice object interface */ + +/* + +A slice object containing start, stop, and step data members (the +names are from range). After much talk with Guido, it was decided to +let these be any arbitrary python type. +*/ + +typedef struct { + PyObject_HEAD + PyObject *start, *stop, *step; +} PySliceObject; + +PyAPI_DATA(PyTypeObject) PySlice_Type; + +#define PySlice_Check(op) ((op)->ob_type == &PySlice_Type) + +PyAPI_FUNC(PyObject *) PySlice_New(PyObject* start, PyObject* stop, + PyObject* step); +PyAPI_FUNC(int) PySlice_GetIndices(PySliceObject *r, int length, + int *start, int *stop, int *step); +PyAPI_FUNC(int) PySlice_GetIndicesEx(PySliceObject *r, int length, + int *start, int *stop, + int *step, int *slicelength); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_SLICEOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/stringobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/stringobject.h new file mode 100644 index 00000000..a013f47b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/stringobject.h @@ -0,0 +1,174 @@ + +/* String object interface */ + +#ifndef Py_STRINGOBJECT_H +#define Py_STRINGOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* +Type PyStringObject represents a character string. An extra zero byte is +reserved at the end to ensure it is zero-terminated, but a size is +present so strings with null bytes in them can be represented. This +is an immutable object type. + +There are functions to create new string objects, to test +an object for string-ness, and to get the +string value. The latter function returns a null pointer +if the object is not of the proper type. +There is a variant that takes an explicit size as well as a +variant that assumes a zero-terminated string. Note that none of the +functions should be applied to nil objects. +*/ + +/* Caching the hash (ob_shash) saves recalculation of a string's hash value. + Interning strings (ob_sstate) tries to ensure that only one string + object with a given value exists, so equality tests can be one pointer + comparison. This is generally restricted to strings that "look like" + Python identifiers, although the intern() builtin can be used to force + interning of any string. + Together, these sped the interpreter by up to 20%. */ + +typedef struct { + PyObject_VAR_HEAD + long ob_shash; + int ob_sstate; + char ob_sval[1]; +} PyStringObject; + +#define SSTATE_NOT_INTERNED 0 +#define SSTATE_INTERNED_MORTAL 1 +#define SSTATE_INTERNED_IMMORTAL 2 + +PyAPI_DATA(PyTypeObject) PyBaseString_Type; +PyAPI_DATA(PyTypeObject) PyString_Type; + +#define PyString_Check(op) PyObject_TypeCheck(op, &PyString_Type) +#define PyString_CheckExact(op) ((op)->ob_type == &PyString_Type) + +PyAPI_FUNC(PyObject *) PyString_FromStringAndSize(const char *, int); +PyAPI_FUNC(PyObject *) PyString_FromString(const char *); +PyAPI_FUNC(PyObject *) PyString_FromFormatV(const char*, va_list) + Py_GCC_ATTRIBUTE((format(printf, 1, 0))); +PyAPI_FUNC(PyObject *) PyString_FromFormat(const char*, ...) + Py_GCC_ATTRIBUTE((format(printf, 1, 2))); +PyAPI_FUNC(int) PyString_Size(PyObject *); +PyAPI_FUNC(char *) PyString_AsString(PyObject *); +PyAPI_FUNC(PyObject *) PyString_Repr(PyObject *, int); +PyAPI_FUNC(void) PyString_Concat(PyObject **, PyObject *); +PyAPI_FUNC(void) PyString_ConcatAndDel(PyObject **, PyObject *); +PyAPI_FUNC(int) _PyString_Resize(PyObject **, int); +PyAPI_FUNC(int) _PyString_Eq(PyObject *, PyObject*); +PyAPI_FUNC(PyObject *) PyString_Format(PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) _PyString_FormatLong(PyObject*, int, int, + int, char**, int*); +PyAPI_FUNC(PyObject *) PyString_DecodeEscape(const char *, int, + const char *, int, + const char *); + +PyAPI_FUNC(void) PyString_InternInPlace(PyObject **); +PyAPI_FUNC(void) PyString_InternImmortal(PyObject **); +PyAPI_FUNC(PyObject *) PyString_InternFromString(const char *); +PyAPI_FUNC(void) _Py_ReleaseInternedStrings(void); + +/* Use only if you know it's a string */ +#define PyString_CHECK_INTERNED(op) (((PyStringObject *)(op))->ob_sstate) + +/* Macro, trading safety for speed */ +#define PyString_AS_STRING(op) (((PyStringObject *)(op))->ob_sval) +#define PyString_GET_SIZE(op) (((PyStringObject *)(op))->ob_size) + +/* _PyString_Join(sep, x) is like sep.join(x). sep must be PyStringObject*, + x must be an iterable object. */ +PyAPI_FUNC(PyObject *) _PyString_Join(PyObject *sep, PyObject *x); + +/* --- Generic Codecs ----------------------------------------------------- */ + +/* Create an object by decoding the encoded string s of the + given size. */ + +PyAPI_FUNC(PyObject*) PyString_Decode( + const char *s, /* encoded string */ + int size, /* size of buffer */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Encodes a char buffer of the given size and returns a + Python object. */ + +PyAPI_FUNC(PyObject*) PyString_Encode( + const char *s, /* string char buffer */ + int size, /* number of chars to encode */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Encodes a string object and returns the result as Python + object. */ + +PyAPI_FUNC(PyObject*) PyString_AsEncodedObject( + PyObject *str, /* string object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Encodes a string object and returns the result as Python string + object. + + If the codec returns an Unicode object, the object is converted + back to a string using the default encoding. + + DEPRECATED - use PyString_AsEncodedObject() instead. */ + +PyAPI_FUNC(PyObject*) PyString_AsEncodedString( + PyObject *str, /* string object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Decodes a string object and returns the result as Python + object. */ + +PyAPI_FUNC(PyObject*) PyString_AsDecodedObject( + PyObject *str, /* string object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Decodes a string object and returns the result as Python string + object. + + If the codec returns an Unicode object, the object is converted + back to a string using the default encoding. + + DEPRECATED - use PyString_AsDecodedObject() instead. */ + +PyAPI_FUNC(PyObject*) PyString_AsDecodedString( + PyObject *str, /* string object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Provides access to the internal data buffer and size of a string + object or the default encoded version of an Unicode object. Passing + NULL as *len parameter will force the string buffer to be + 0-terminated (passing a string with embedded NULL characters will + cause an exception). */ + +PyAPI_FUNC(int) PyString_AsStringAndSize( + register PyObject *obj, /* string or Unicode object */ + register char **s, /* pointer to buffer variable */ + register int *len /* pointer to length variable or NULL + (only possible for 0-terminated + strings) */ + ); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_STRINGOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/structmember.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/structmember.h new file mode 100644 index 00000000..1a4942d8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/structmember.h @@ -0,0 +1,95 @@ +#ifndef Py_STRUCTMEMBER_H +#define Py_STRUCTMEMBER_H +#ifdef __cplusplus +extern "C" { +#endif + + +/* Interface to map C struct members to Python object attributes */ + +#ifdef HAVE_STDDEF_H +#include /* For offsetof */ +#endif + +/* The offsetof() macro calculates the offset of a structure member + in its structure. Unfortunately this cannot be written down + portably, hence it is provided by a Standard C header file. + For pre-Standard C compilers, here is a version that usually works + (but watch out!): */ + +#ifndef offsetof +#define offsetof(type, member) ( (int) & ((type*)0) -> member ) +#endif + +/* An array of memberlist structures defines the name, type and offset + of selected members of a C structure. These can be read by + PyMember_Get() and set by PyMember_Set() (except if their READONLY flag + is set). The array must be terminated with an entry whose name + pointer is NULL. */ + +struct memberlist { + /* Obsolete version, for binary backwards compatibility */ + char *name; + int type; + int offset; + int flags; +}; + +typedef struct PyMemberDef { + /* Current version, use this */ + char *name; + int type; + int offset; + int flags; + char *doc; +} PyMemberDef; + +/* Types */ +#define T_SHORT 0 +#define T_INT 1 +#define T_LONG 2 +#define T_FLOAT 3 +#define T_DOUBLE 4 +#define T_STRING 5 +#define T_OBJECT 6 +/* XXX the ordering here is weird for binary compatibility */ +#define T_CHAR 7 /* 1-character string */ +#define T_BYTE 8 /* 8-bit signed int */ +/* unsigned variants: */ +#define T_UBYTE 9 +#define T_USHORT 10 +#define T_UINT 11 +#define T_ULONG 12 + +/* Added by Jack: strings contained in the structure */ +#define T_STRING_INPLACE 13 +#ifdef macintosh +#define T_PSTRING 14 /* macintosh pascal-style counted string */ +#define T_PSTRING_INPLACE 15 +#endif /* macintosh */ + +#define T_OBJECT_EX 16 /* Like T_OBJECT, but raises AttributeError + when the value is NULL, instead of + converting to None. */ + +/* Flags */ +#define READONLY 1 +#define RO READONLY /* Shorthand */ +#define READ_RESTRICTED 2 +#define WRITE_RESTRICTED 4 +#define RESTRICTED (READ_RESTRICTED | WRITE_RESTRICTED) + + +/* Obsolete API, for binary backwards compatibility */ +PyAPI_FUNC(PyObject *) PyMember_Get(char *, struct memberlist *, char *); +PyAPI_FUNC(int) PyMember_Set(char *, struct memberlist *, char *, PyObject *); + +/* Current API, use this */ +PyAPI_FUNC(PyObject *) PyMember_GetOne(char *, struct PyMemberDef *); +PyAPI_FUNC(int) PyMember_SetOne(char *, struct PyMemberDef *, PyObject *); + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_STRUCTMEMBER_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/structseq.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/structseq.h new file mode 100644 index 00000000..74b1a881 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/structseq.h @@ -0,0 +1,41 @@ + +/* Tuple object interface */ + +#ifndef Py_STRUCTSEQ_H +#define Py_STRUCTSEQ_H +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct PyStructSequence_Field { + char *name; + char *doc; +} PyStructSequence_Field; + +typedef struct PyStructSequence_Desc { + char *name; + char *doc; + struct PyStructSequence_Field *fields; + int n_in_sequence; +} PyStructSequence_Desc; + +extern char* PyStructSequence_UnnamedField; + +PyAPI_FUNC(void) PyStructSequence_InitType(PyTypeObject *type, + PyStructSequence_Desc *desc); + +PyAPI_FUNC(PyObject *) PyStructSequence_New(PyTypeObject* type); + +typedef struct { + PyObject_VAR_HEAD + PyObject *ob_item[1]; +} PyStructSequence; + +/* Macro, *only* to be used to fill in brand new objects */ +#define PyStructSequence_SET_ITEM(op, i, v) \ + (((PyStructSequence *)(op))->ob_item[i] = v) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_STRUCTSEQ_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/symtable.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/symtable.h new file mode 100644 index 00000000..1c38e296 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/symtable.h @@ -0,0 +1,99 @@ +#ifndef Py_SYMTABLE_H +#define Py_SYMTABLE_H +#ifdef __cplusplus +extern "C" { +#endif + +/* A symbol table is constructed each time PyNode_Compile() is + called. The table walks the entire parse tree and identifies each + use or definition of a variable. + + The symbol table contains a dictionary for each code block in a + module: The symbol dictionary for the block. They keys of these + dictionaries are the name of all variables used or defined in the + block; the integer values are used to store several flags, + e.g. DEF_PARAM indicates that a variable is a parameter to a + function. +*/ + +struct _symtable_entry; + +struct symtable { + int st_pass; /* pass == 1 or 2 */ + const char *st_filename; /* name of file being compiled */ + struct _symtable_entry *st_cur; /* current symbol table entry */ + PyObject *st_symbols; /* dictionary of symbol table entries */ + PyObject *st_stack; /* stack of namespace info */ + PyObject *st_global; /* borrowed ref to MODULE in st_symbols */ + int st_nscopes; /* number of scopes */ + int st_errors; /* number of errors */ + char *st_private; /* name of current class or NULL */ + PyFutureFeatures *st_future; /* module's future features */ +}; + +typedef struct _symtable_entry { + PyObject_HEAD + PyObject *ste_id; /* int: key in st_symbols) */ + PyObject *ste_symbols; /* dict: name to flags) */ + PyObject *ste_name; /* string: name of scope */ + PyObject *ste_varnames; /* list of variable names */ + PyObject *ste_children; /* list of child ids */ + int ste_type; /* module, class, or function */ + int ste_lineno; /* first line of scope */ + int ste_optimized; /* true if namespace can't be optimized */ + int ste_nested; /* true if scope is nested */ + int ste_child_free; /* true if a child scope has free variables, + including free refs to globals */ + int ste_generator; /* true if namespace is a generator */ + int ste_opt_lineno; /* lineno of last exec or import * */ + int ste_tmpname; /* temporary name counter */ + struct symtable *ste_table; +} PySymtableEntryObject; + +PyAPI_DATA(PyTypeObject) PySymtableEntry_Type; + +#define PySymtableEntry_Check(op) ((op)->ob_type == &PySymtableEntry_Type) + +PyAPI_FUNC(PyObject *) PySymtableEntry_New(struct symtable *, + char *, int, int); + +PyAPI_FUNC(struct symtable *) PyNode_CompileSymtable(struct _node *, const char *); +PyAPI_FUNC(void) PySymtable_Free(struct symtable *); + + +#define TOP "global" + +/* Flags for def-use information */ + +#define DEF_GLOBAL 1 /* global stmt */ +#define DEF_LOCAL 2 /* assignment in code block */ +#define DEF_PARAM 2<<1 /* formal parameter */ +#define USE 2<<2 /* name is used */ +#define DEF_STAR 2<<3 /* parameter is star arg */ +#define DEF_DOUBLESTAR 2<<4 /* parameter is star-star arg */ +#define DEF_INTUPLE 2<<5 /* name defined in tuple in parameters */ +#define DEF_FREE 2<<6 /* name used but not defined in nested scope */ +#define DEF_FREE_GLOBAL 2<<7 /* free variable is actually implicit global */ +#define DEF_FREE_CLASS 2<<8 /* free variable from class's method */ +#define DEF_IMPORT 2<<9 /* assignment occurred via import */ + +#define DEF_BOUND (DEF_LOCAL | DEF_PARAM | DEF_IMPORT) + +#define TYPE_FUNCTION 1 +#define TYPE_CLASS 2 +#define TYPE_MODULE 3 + +#define LOCAL 1 +#define GLOBAL_EXPLICIT 2 +#define GLOBAL_IMPLICIT 3 +#define FREE 4 +#define CELL 5 + +#define OPT_IMPORT_STAR 1 +#define OPT_EXEC 2 +#define OPT_BARE_EXEC 4 + +#ifdef __cplusplus +} +#endif +#endif /* !Py_SYMTABLE_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/sysmodule.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/sysmodule.h new file mode 100644 index 00000000..449b5496 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/sysmodule.h @@ -0,0 +1,30 @@ + +/* System module interface */ + +#ifndef Py_SYSMODULE_H +#define Py_SYSMODULE_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(PyObject *) PySys_GetObject(char *); +PyAPI_FUNC(int) PySys_SetObject(char *, PyObject *); +PyAPI_FUNC(FILE *) PySys_GetFile(char *, FILE *); +PyAPI_FUNC(void) PySys_SetArgv(int, char **); +PyAPI_FUNC(void) PySys_SetPath(char *); + +PyAPI_FUNC(void) PySys_WriteStdout(const char *format, ...) + Py_GCC_ATTRIBUTE((format(printf, 1, 2))); +PyAPI_FUNC(void) PySys_WriteStderr(const char *format, ...) + Py_GCC_ATTRIBUTE((format(printf, 1, 2))); + +PyAPI_DATA(PyObject *) _PySys_TraceFunc, *_PySys_ProfileFunc; +PyAPI_DATA(int) _PySys_CheckInterval; + +PyAPI_FUNC(void) PySys_ResetWarnOptions(void); +PyAPI_FUNC(void) PySys_AddWarnOption(char *); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_SYSMODULE_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/token.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/token.h new file mode 100644 index 00000000..51f8cbfb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/token.h @@ -0,0 +1,82 @@ + +/* Token types */ + +#ifndef Py_TOKEN_H +#define Py_TOKEN_H +#ifdef __cplusplus +extern "C" { +#endif + +#define ENDMARKER 0 +#define NAME 1 +#define NUMBER 2 +#define STRING 3 +#define NEWLINE 4 +#define INDENT 5 +#define DEDENT 6 +#define LPAR 7 +#define RPAR 8 +#define LSQB 9 +#define RSQB 10 +#define COLON 11 +#define COMMA 12 +#define SEMI 13 +#define PLUS 14 +#define MINUS 15 +#define STAR 16 +#define SLASH 17 +#define VBAR 18 +#define AMPER 19 +#define LESS 20 +#define GREATER 21 +#define EQUAL 22 +#define DOT 23 +#define PERCENT 24 +#define BACKQUOTE 25 +#define LBRACE 26 +#define RBRACE 27 +#define EQEQUAL 28 +#define NOTEQUAL 29 +#define LESSEQUAL 30 +#define GREATEREQUAL 31 +#define TILDE 32 +#define CIRCUMFLEX 33 +#define LEFTSHIFT 34 +#define RIGHTSHIFT 35 +#define DOUBLESTAR 36 +#define PLUSEQUAL 37 +#define MINEQUAL 38 +#define STAREQUAL 39 +#define SLASHEQUAL 40 +#define PERCENTEQUAL 41 +#define AMPEREQUAL 42 +#define VBAREQUAL 43 +#define CIRCUMFLEXEQUAL 44 +#define LEFTSHIFTEQUAL 45 +#define RIGHTSHIFTEQUAL 46 +#define DOUBLESTAREQUAL 47 +#define DOUBLESLASH 48 +#define DOUBLESLASHEQUAL 49 +/* Don't forget to update the table _PyParser_TokenNames in tokenizer.c! */ +#define OP 50 +#define ERRORTOKEN 51 +#define N_TOKENS 52 + +/* Special definitions for cooperation with parser */ + +#define NT_OFFSET 256 + +#define ISTERMINAL(x) ((x) < NT_OFFSET) +#define ISNONTERMINAL(x) ((x) >= NT_OFFSET) +#define ISEOF(x) ((x) == ENDMARKER) + + +PyAPI_DATA(char *) _PyParser_TokenNames[]; /* Token names */ +PyAPI_FUNC(int) PyToken_OneChar(int); +PyAPI_FUNC(int) PyToken_TwoChars(int, int); +PyAPI_FUNC(int) PyToken_ThreeChars(int, int, int); + +#ifdef __cplusplus +} +#endif +#endif /* !Py_TOKEN_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/traceback.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/traceback.h new file mode 100644 index 00000000..94875951 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/traceback.h @@ -0,0 +1,22 @@ + +#ifndef Py_TRACEBACK_H +#define Py_TRACEBACK_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Traceback interface */ + +struct _frame; + +PyAPI_FUNC(int) PyTraceBack_Here(struct _frame *); +PyAPI_FUNC(int) PyTraceBack_Print(PyObject *, PyObject *); + +/* Reveal traceback type so we can typecheck traceback objects */ +PyAPI_DATA(PyTypeObject) PyTraceBack_Type; +#define PyTraceBack_Check(v) ((v)->ob_type == &PyTraceBack_Type) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_TRACEBACK_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/tupleobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/tupleobject.h new file mode 100644 index 00000000..251f4abe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/tupleobject.h @@ -0,0 +1,49 @@ + +/* Tuple object interface */ + +#ifndef Py_TUPLEOBJECT_H +#define Py_TUPLEOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + +/* +Another generally useful object type is an tuple of object pointers. +This is a mutable type: the tuple items can be changed (but not their +number). Out-of-range indices or non-tuple objects are ignored. + +*** WARNING *** PyTuple_SetItem does not increment the new item's reference +count, but does decrement the reference count of the item it replaces, +if not nil. It does *decrement* the reference count if it is *not* +inserted in the tuple. Similarly, PyTuple_GetItem does not increment the +returned item's reference count. +*/ + +typedef struct { + PyObject_VAR_HEAD + PyObject *ob_item[1]; +} PyTupleObject; + +PyAPI_DATA(PyTypeObject) PyTuple_Type; + +#define PyTuple_Check(op) PyObject_TypeCheck(op, &PyTuple_Type) +#define PyTuple_CheckExact(op) ((op)->ob_type == &PyTuple_Type) + +PyAPI_FUNC(PyObject *) PyTuple_New(int size); +PyAPI_FUNC(int) PyTuple_Size(PyObject *); +PyAPI_FUNC(PyObject *) PyTuple_GetItem(PyObject *, int); +PyAPI_FUNC(int) PyTuple_SetItem(PyObject *, int, PyObject *); +PyAPI_FUNC(PyObject *) PyTuple_GetSlice(PyObject *, int, int); +PyAPI_FUNC(int) _PyTuple_Resize(PyObject **, int); + +/* Macro, trading safety for speed */ +#define PyTuple_GET_ITEM(op, i) (((PyTupleObject *)(op))->ob_item[i]) +#define PyTuple_GET_SIZE(op) (((PyTupleObject *)(op))->ob_size) + +/* Macro, *only* to be used to fill in brand new tuples */ +#define PyTuple_SET_ITEM(op, i, v) (((PyTupleObject *)(op))->ob_item[i] = v) + +#ifdef __cplusplus +} +#endif +#endif /* !Py_TUPLEOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/ucnhash.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/ucnhash.h new file mode 100644 index 00000000..6d02989e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/ucnhash.h @@ -0,0 +1,29 @@ +/* Unicode name database interface */ + +#ifndef Py_UCNHASH_H +#define Py_UCNHASH_H +#ifdef __cplusplus +extern "C" { +#endif + +/* revised ucnhash CAPI interface (exported through a PyCObject) */ + +typedef struct { + + /* Size of this struct */ + int size; + + /* Get name for a given character code. Returns non-zero if + success, zero if not. Does not set Python exceptions. */ + int (*getname)(Py_UCS4 code, char* buffer, int buflen); + + /* Get character code for a given name. Same error handling + as for getname. */ + int (*getcode)(const char* name, int namelen, Py_UCS4* code); + +} _PyUnicode_Name_CAPI; + +#ifdef __cplusplus +} +#endif +#endif /* !Py_UCNHASH_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/unicodeobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/unicodeobject.h new file mode 100644 index 00000000..614b02e1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/unicodeobject.h @@ -0,0 +1,1138 @@ +#ifndef Py_UNICODEOBJECT_H +#define Py_UNICODEOBJECT_H + +/* + +Unicode implementation based on original code by Fredrik Lundh, +modified by Marc-Andre Lemburg (mal@lemburg.com) according to the +Unicode Integration Proposal (see file Misc/unicode.txt). + +Copyright (c) Corporation for National Research Initiatives. + + + Original header: + -------------------------------------------------------------------- + + * Yet another Unicode string type for Python. This type supports the + * 16-bit Basic Multilingual Plane (BMP) only. + * + * Written by Fredrik Lundh, January 1999. + * + * Copyright (c) 1999 by Secret Labs AB. + * Copyright (c) 1999 by Fredrik Lundh. + * + * fredrik@pythonware.com + * http://www.pythonware.com + * + * -------------------------------------------------------------------- + * This Unicode String Type is + * + * Copyright (c) 1999 by Secret Labs AB + * Copyright (c) 1999 by Fredrik Lundh + * + * By obtaining, using, and/or copying this software and/or its + * associated documentation, you agree that you have read, understood, + * and will comply with the following terms and conditions: + * + * Permission to use, copy, modify, and distribute this software and its + * associated documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appears in all + * copies, and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of Secret Labs + * AB or the author not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. + * + * SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * -------------------------------------------------------------------- */ + +#include + +/* === Internal API ======================================================= */ + +/* --- Internal Unicode Format -------------------------------------------- */ + +#ifndef Py_USING_UNICODE + +#define PyUnicode_Check(op) 0 +#define PyUnicode_CheckExact(op) 0 + +#else + +/* FIXME: MvL's new implementation assumes that Py_UNICODE_SIZE is + properly set, but the default rules below doesn't set it. I'll + sort this out some other day -- fredrik@pythonware.com */ + +#ifndef Py_UNICODE_SIZE +#error Must define Py_UNICODE_SIZE +#endif + +/* Setting Py_UNICODE_WIDE enables UCS-4 storage. Otherwise, Unicode + strings are stored as UCS-2 (with limited support for UTF-16) */ + +#if Py_UNICODE_SIZE >= 4 +#define Py_UNICODE_WIDE +#endif + +/* Set these flags if the platform has "wchar.h", "wctype.h" and the + wchar_t type is a 16-bit unsigned type */ +/* #define HAVE_WCHAR_H */ +/* #define HAVE_USABLE_WCHAR_T */ + +/* Defaults for various platforms */ +#ifndef PY_UNICODE_TYPE + +/* Windows has a usable wchar_t type (unless we're using UCS-4) */ +# if defined(MS_WIN32) && Py_UNICODE_SIZE == 2 +# define HAVE_USABLE_WCHAR_T +# define PY_UNICODE_TYPE wchar_t +# endif + +# if defined(Py_UNICODE_WIDE) +# define PY_UNICODE_TYPE Py_UCS4 +# endif + +#endif + +/* If the compiler provides a wchar_t type we try to support it + through the interface functions PyUnicode_FromWideChar() and + PyUnicode_AsWideChar(). */ + +#ifdef HAVE_USABLE_WCHAR_T +# ifndef HAVE_WCHAR_H +# define HAVE_WCHAR_H +# endif +#endif + +#ifdef HAVE_WCHAR_H +/* Work around a cosmetic bug in BSDI 4.x wchar.h; thanks to Thomas Wouters */ +# ifdef _HAVE_BSDI +# include +# endif +# include +#endif + +/* + * Use this typedef when you need to represent a UTF-16 surrogate pair + * as single unsigned integer. + */ +#if SIZEOF_INT >= 4 +typedef unsigned int Py_UCS4; +#elif SIZEOF_LONG >= 4 +typedef unsigned long Py_UCS4; +#endif + +typedef PY_UNICODE_TYPE Py_UNICODE; + +/* --- UCS-2/UCS-4 Name Mangling ------------------------------------------ */ + +/* Unicode API names are mangled to assure that UCS-2 and UCS-4 builds + produce different external names and thus cause import errors in + case Python interpreters and extensions with mixed compiled in + Unicode width assumptions are combined. */ + +#ifndef Py_UNICODE_WIDE + +# define PyUnicode_AsASCIIString PyUnicodeUCS2_AsASCIIString +# define PyUnicode_AsCharmapString PyUnicodeUCS2_AsCharmapString +# define PyUnicode_AsEncodedString PyUnicodeUCS2_AsEncodedString +# define PyUnicode_AsLatin1String PyUnicodeUCS2_AsLatin1String +# define PyUnicode_AsRawUnicodeEscapeString PyUnicodeUCS2_AsRawUnicodeEscapeString +# define PyUnicode_AsUTF16String PyUnicodeUCS2_AsUTF16String +# define PyUnicode_AsUTF8String PyUnicodeUCS2_AsUTF8String +# define PyUnicode_AsUnicode PyUnicodeUCS2_AsUnicode +# define PyUnicode_AsUnicodeEscapeString PyUnicodeUCS2_AsUnicodeEscapeString +# define PyUnicode_AsWideChar PyUnicodeUCS2_AsWideChar +# define PyUnicode_Compare PyUnicodeUCS2_Compare +# define PyUnicode_Concat PyUnicodeUCS2_Concat +# define PyUnicode_Contains PyUnicodeUCS2_Contains +# define PyUnicode_Count PyUnicodeUCS2_Count +# define PyUnicode_Decode PyUnicodeUCS2_Decode +# define PyUnicode_DecodeASCII PyUnicodeUCS2_DecodeASCII +# define PyUnicode_DecodeCharmap PyUnicodeUCS2_DecodeCharmap +# define PyUnicode_DecodeLatin1 PyUnicodeUCS2_DecodeLatin1 +# define PyUnicode_DecodeRawUnicodeEscape PyUnicodeUCS2_DecodeRawUnicodeEscape +# define PyUnicode_DecodeUTF16 PyUnicodeUCS2_DecodeUTF16 +# define PyUnicode_DecodeUTF8 PyUnicodeUCS2_DecodeUTF8 +# define PyUnicode_DecodeUnicodeEscape PyUnicodeUCS2_DecodeUnicodeEscape +# define PyUnicode_Encode PyUnicodeUCS2_Encode +# define PyUnicode_EncodeASCII PyUnicodeUCS2_EncodeASCII +# define PyUnicode_EncodeCharmap PyUnicodeUCS2_EncodeCharmap +# define PyUnicode_EncodeDecimal PyUnicodeUCS2_EncodeDecimal +# define PyUnicode_EncodeLatin1 PyUnicodeUCS2_EncodeLatin1 +# define PyUnicode_EncodeRawUnicodeEscape PyUnicodeUCS2_EncodeRawUnicodeEscape +# define PyUnicode_EncodeUTF16 PyUnicodeUCS2_EncodeUTF16 +# define PyUnicode_EncodeUTF8 PyUnicodeUCS2_EncodeUTF8 +# define PyUnicode_EncodeUnicodeEscape PyUnicodeUCS2_EncodeUnicodeEscape +# define PyUnicode_Find PyUnicodeUCS2_Find +# define PyUnicode_Format PyUnicodeUCS2_Format +# define PyUnicode_FromEncodedObject PyUnicodeUCS2_FromEncodedObject +# define PyUnicode_FromObject PyUnicodeUCS2_FromObject +# define PyUnicode_FromOrdinal PyUnicodeUCS2_FromOrdinal +# define PyUnicode_FromUnicode PyUnicodeUCS2_FromUnicode +# define PyUnicode_FromWideChar PyUnicodeUCS2_FromWideChar +# define PyUnicode_GetDefaultEncoding PyUnicodeUCS2_GetDefaultEncoding +# define PyUnicode_GetMax PyUnicodeUCS2_GetMax +# define PyUnicode_GetSize PyUnicodeUCS2_GetSize +# define PyUnicode_Join PyUnicodeUCS2_Join +# define PyUnicode_Replace PyUnicodeUCS2_Replace +# define PyUnicode_Resize PyUnicodeUCS2_Resize +# define PyUnicode_SetDefaultEncoding PyUnicodeUCS2_SetDefaultEncoding +# define PyUnicode_Split PyUnicodeUCS2_Split +# define PyUnicode_Splitlines PyUnicodeUCS2_Splitlines +# define PyUnicode_Tailmatch PyUnicodeUCS2_Tailmatch +# define PyUnicode_Translate PyUnicodeUCS2_Translate +# define PyUnicode_TranslateCharmap PyUnicodeUCS2_TranslateCharmap +# define _PyUnicode_AsDefaultEncodedString _PyUnicodeUCS2_AsDefaultEncodedString +# define _PyUnicode_Fini _PyUnicodeUCS2_Fini +# define _PyUnicode_Init _PyUnicodeUCS2_Init +# define _PyUnicode_IsAlpha _PyUnicodeUCS2_IsAlpha +# define _PyUnicode_IsDecimalDigit _PyUnicodeUCS2_IsDecimalDigit +# define _PyUnicode_IsDigit _PyUnicodeUCS2_IsDigit +# define _PyUnicode_IsLinebreak _PyUnicodeUCS2_IsLinebreak +# define _PyUnicode_IsLowercase _PyUnicodeUCS2_IsLowercase +# define _PyUnicode_IsNumeric _PyUnicodeUCS2_IsNumeric +# define _PyUnicode_IsTitlecase _PyUnicodeUCS2_IsTitlecase +# define _PyUnicode_IsUppercase _PyUnicodeUCS2_IsUppercase +# define _PyUnicode_IsWhitespace _PyUnicodeUCS2_IsWhitespace +# define _PyUnicode_ToDecimalDigit _PyUnicodeUCS2_ToDecimalDigit +# define _PyUnicode_ToDigit _PyUnicodeUCS2_ToDigit +# define _PyUnicode_ToLowercase _PyUnicodeUCS2_ToLowercase +# define _PyUnicode_ToNumeric _PyUnicodeUCS2_ToNumeric +# define _PyUnicode_ToTitlecase _PyUnicodeUCS2_ToTitlecase +# define _PyUnicode_ToUppercase _PyUnicodeUCS2_ToUppercase + +#else + +# define PyUnicode_AsASCIIString PyUnicodeUCS4_AsASCIIString +# define PyUnicode_AsCharmapString PyUnicodeUCS4_AsCharmapString +# define PyUnicode_AsEncodedString PyUnicodeUCS4_AsEncodedString +# define PyUnicode_AsLatin1String PyUnicodeUCS4_AsLatin1String +# define PyUnicode_AsRawUnicodeEscapeString PyUnicodeUCS4_AsRawUnicodeEscapeString +# define PyUnicode_AsUTF16String PyUnicodeUCS4_AsUTF16String +# define PyUnicode_AsUTF8String PyUnicodeUCS4_AsUTF8String +# define PyUnicode_AsUnicode PyUnicodeUCS4_AsUnicode +# define PyUnicode_AsUnicodeEscapeString PyUnicodeUCS4_AsUnicodeEscapeString +# define PyUnicode_AsWideChar PyUnicodeUCS4_AsWideChar +# define PyUnicode_Compare PyUnicodeUCS4_Compare +# define PyUnicode_Concat PyUnicodeUCS4_Concat +# define PyUnicode_Contains PyUnicodeUCS4_Contains +# define PyUnicode_Count PyUnicodeUCS4_Count +# define PyUnicode_Decode PyUnicodeUCS4_Decode +# define PyUnicode_DecodeASCII PyUnicodeUCS4_DecodeASCII +# define PyUnicode_DecodeCharmap PyUnicodeUCS4_DecodeCharmap +# define PyUnicode_DecodeLatin1 PyUnicodeUCS4_DecodeLatin1 +# define PyUnicode_DecodeRawUnicodeEscape PyUnicodeUCS4_DecodeRawUnicodeEscape +# define PyUnicode_DecodeUTF16 PyUnicodeUCS4_DecodeUTF16 +# define PyUnicode_DecodeUTF8 PyUnicodeUCS4_DecodeUTF8 +# define PyUnicode_DecodeUnicodeEscape PyUnicodeUCS4_DecodeUnicodeEscape +# define PyUnicode_Encode PyUnicodeUCS4_Encode +# define PyUnicode_EncodeASCII PyUnicodeUCS4_EncodeASCII +# define PyUnicode_EncodeCharmap PyUnicodeUCS4_EncodeCharmap +# define PyUnicode_EncodeDecimal PyUnicodeUCS4_EncodeDecimal +# define PyUnicode_EncodeLatin1 PyUnicodeUCS4_EncodeLatin1 +# define PyUnicode_EncodeRawUnicodeEscape PyUnicodeUCS4_EncodeRawUnicodeEscape +# define PyUnicode_EncodeUTF16 PyUnicodeUCS4_EncodeUTF16 +# define PyUnicode_EncodeUTF8 PyUnicodeUCS4_EncodeUTF8 +# define PyUnicode_EncodeUnicodeEscape PyUnicodeUCS4_EncodeUnicodeEscape +# define PyUnicode_Find PyUnicodeUCS4_Find +# define PyUnicode_Format PyUnicodeUCS4_Format +# define PyUnicode_FromEncodedObject PyUnicodeUCS4_FromEncodedObject +# define PyUnicode_FromObject PyUnicodeUCS4_FromObject +# define PyUnicode_FromOrdinal PyUnicodeUCS4_FromOrdinal +# define PyUnicode_FromUnicode PyUnicodeUCS4_FromUnicode +# define PyUnicode_FromWideChar PyUnicodeUCS4_FromWideChar +# define PyUnicode_GetDefaultEncoding PyUnicodeUCS4_GetDefaultEncoding +# define PyUnicode_GetMax PyUnicodeUCS4_GetMax +# define PyUnicode_GetSize PyUnicodeUCS4_GetSize +# define PyUnicode_Join PyUnicodeUCS4_Join +# define PyUnicode_Replace PyUnicodeUCS4_Replace +# define PyUnicode_Resize PyUnicodeUCS4_Resize +# define PyUnicode_SetDefaultEncoding PyUnicodeUCS4_SetDefaultEncoding +# define PyUnicode_Split PyUnicodeUCS4_Split +# define PyUnicode_Splitlines PyUnicodeUCS4_Splitlines +# define PyUnicode_Tailmatch PyUnicodeUCS4_Tailmatch +# define PyUnicode_Translate PyUnicodeUCS4_Translate +# define PyUnicode_TranslateCharmap PyUnicodeUCS4_TranslateCharmap +# define _PyUnicode_AsDefaultEncodedString _PyUnicodeUCS4_AsDefaultEncodedString +# define _PyUnicode_Fini _PyUnicodeUCS4_Fini +# define _PyUnicode_Init _PyUnicodeUCS4_Init +# define _PyUnicode_IsAlpha _PyUnicodeUCS4_IsAlpha +# define _PyUnicode_IsDecimalDigit _PyUnicodeUCS4_IsDecimalDigit +# define _PyUnicode_IsDigit _PyUnicodeUCS4_IsDigit +# define _PyUnicode_IsLinebreak _PyUnicodeUCS4_IsLinebreak +# define _PyUnicode_IsLowercase _PyUnicodeUCS4_IsLowercase +# define _PyUnicode_IsNumeric _PyUnicodeUCS4_IsNumeric +# define _PyUnicode_IsTitlecase _PyUnicodeUCS4_IsTitlecase +# define _PyUnicode_IsUppercase _PyUnicodeUCS4_IsUppercase +# define _PyUnicode_IsWhitespace _PyUnicodeUCS4_IsWhitespace +# define _PyUnicode_ToDecimalDigit _PyUnicodeUCS4_ToDecimalDigit +# define _PyUnicode_ToDigit _PyUnicodeUCS4_ToDigit +# define _PyUnicode_ToLowercase _PyUnicodeUCS4_ToLowercase +# define _PyUnicode_ToNumeric _PyUnicodeUCS4_ToNumeric +# define _PyUnicode_ToTitlecase _PyUnicodeUCS4_ToTitlecase +# define _PyUnicode_ToUppercase _PyUnicodeUCS4_ToUppercase + + +#endif + +/* --- Internal Unicode Operations ---------------------------------------- */ + +/* If you want Python to use the compiler's wctype.h functions instead + of the ones supplied with Python, define WANT_WCTYPE_FUNCTIONS or + configure Python using --with-ctype-functions. This reduces the + interpreter's code size. */ + +#if defined(HAVE_USABLE_WCHAR_T) && defined(WANT_WCTYPE_FUNCTIONS) + +#include + +#define Py_UNICODE_ISSPACE(ch) iswspace(ch) + +#define Py_UNICODE_ISLOWER(ch) iswlower(ch) +#define Py_UNICODE_ISUPPER(ch) iswupper(ch) +#define Py_UNICODE_ISTITLE(ch) _PyUnicode_IsTitlecase(ch) +#define Py_UNICODE_ISLINEBREAK(ch) _PyUnicode_IsLinebreak(ch) + +#define Py_UNICODE_TOLOWER(ch) towlower(ch) +#define Py_UNICODE_TOUPPER(ch) towupper(ch) +#define Py_UNICODE_TOTITLE(ch) _PyUnicode_ToTitlecase(ch) + +#define Py_UNICODE_ISDECIMAL(ch) _PyUnicode_IsDecimalDigit(ch) +#define Py_UNICODE_ISDIGIT(ch) _PyUnicode_IsDigit(ch) +#define Py_UNICODE_ISNUMERIC(ch) _PyUnicode_IsNumeric(ch) + +#define Py_UNICODE_TODECIMAL(ch) _PyUnicode_ToDecimalDigit(ch) +#define Py_UNICODE_TODIGIT(ch) _PyUnicode_ToDigit(ch) +#define Py_UNICODE_TONUMERIC(ch) _PyUnicode_ToNumeric(ch) + +#define Py_UNICODE_ISALPHA(ch) iswalpha(ch) + +#else + +#define Py_UNICODE_ISSPACE(ch) _PyUnicode_IsWhitespace(ch) + +#define Py_UNICODE_ISLOWER(ch) _PyUnicode_IsLowercase(ch) +#define Py_UNICODE_ISUPPER(ch) _PyUnicode_IsUppercase(ch) +#define Py_UNICODE_ISTITLE(ch) _PyUnicode_IsTitlecase(ch) +#define Py_UNICODE_ISLINEBREAK(ch) _PyUnicode_IsLinebreak(ch) + +#define Py_UNICODE_TOLOWER(ch) _PyUnicode_ToLowercase(ch) +#define Py_UNICODE_TOUPPER(ch) _PyUnicode_ToUppercase(ch) +#define Py_UNICODE_TOTITLE(ch) _PyUnicode_ToTitlecase(ch) + +#define Py_UNICODE_ISDECIMAL(ch) _PyUnicode_IsDecimalDigit(ch) +#define Py_UNICODE_ISDIGIT(ch) _PyUnicode_IsDigit(ch) +#define Py_UNICODE_ISNUMERIC(ch) _PyUnicode_IsNumeric(ch) + +#define Py_UNICODE_TODECIMAL(ch) _PyUnicode_ToDecimalDigit(ch) +#define Py_UNICODE_TODIGIT(ch) _PyUnicode_ToDigit(ch) +#define Py_UNICODE_TONUMERIC(ch) _PyUnicode_ToNumeric(ch) + +#define Py_UNICODE_ISALPHA(ch) _PyUnicode_IsAlpha(ch) + +#endif + +#define Py_UNICODE_ISALNUM(ch) \ + (Py_UNICODE_ISALPHA(ch) || \ + Py_UNICODE_ISDECIMAL(ch) || \ + Py_UNICODE_ISDIGIT(ch) || \ + Py_UNICODE_ISNUMERIC(ch)) + +#define Py_UNICODE_COPY(target, source, length)\ + (memcpy((target), (source), (length)*sizeof(Py_UNICODE))) + +#define Py_UNICODE_FILL(target, value, length) do\ + {int i; for (i = 0; i < (length); i++) (target)[i] = (value);}\ + while (0) + +#define Py_UNICODE_MATCH(string, offset, substring)\ + ((*((string)->str + (offset)) == *((substring)->str)) &&\ + !memcmp((string)->str + (offset), (substring)->str,\ + (substring)->length*sizeof(Py_UNICODE))) + +#ifdef __cplusplus +extern "C" { +#endif + +/* --- Unicode Type ------------------------------------------------------- */ + +typedef struct { + PyObject_HEAD + int length; /* Length of raw Unicode data in buffer */ + Py_UNICODE *str; /* Raw Unicode buffer */ + long hash; /* Hash value; -1 if not set */ + PyObject *defenc; /* (Default) Encoded version as Python + string, or NULL; this is used for + implementing the buffer protocol */ +} PyUnicodeObject; + +PyAPI_DATA(PyTypeObject) PyUnicode_Type; + +#define PyUnicode_Check(op) PyObject_TypeCheck(op, &PyUnicode_Type) +#define PyUnicode_CheckExact(op) ((op)->ob_type == &PyUnicode_Type) + +/* Fast access macros */ +#define PyUnicode_GET_SIZE(op) \ + (((PyUnicodeObject *)(op))->length) +#define PyUnicode_GET_DATA_SIZE(op) \ + (((PyUnicodeObject *)(op))->length * sizeof(Py_UNICODE)) +#define PyUnicode_AS_UNICODE(op) \ + (((PyUnicodeObject *)(op))->str) +#define PyUnicode_AS_DATA(op) \ + ((const char *)((PyUnicodeObject *)(op))->str) + +/* --- Constants ---------------------------------------------------------- */ + +/* This Unicode character will be used as replacement character during + decoding if the errors argument is set to "replace". Note: the + Unicode character U+FFFD is the official REPLACEMENT CHARACTER in + Unicode 3.0. */ + +#define Py_UNICODE_REPLACEMENT_CHARACTER ((Py_UNICODE) 0xFFFD) + +/* === Public API ========================================================= */ + +/* --- Plain Py_UNICODE --------------------------------------------------- */ + +/* Create a Unicode Object from the Py_UNICODE buffer u of the given + size. + + u may be NULL which causes the contents to be undefined. It is the + user's responsibility to fill in the needed data afterwards. Note + that modifying the Unicode object contents after construction is + only allowed if u was set to NULL. + + The buffer is copied into the new object. */ + +PyAPI_FUNC(PyObject*) PyUnicode_FromUnicode( + const Py_UNICODE *u, /* Unicode buffer */ + int size /* size of buffer */ + ); + +/* Return a read-only pointer to the Unicode object's internal + Py_UNICODE buffer. */ + +PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode( + PyObject *unicode /* Unicode object */ + ); + +/* Get the length of the Unicode object. */ + +PyAPI_FUNC(int) PyUnicode_GetSize( + PyObject *unicode /* Unicode object */ + ); + +/* Get the maximum ordinal for a Unicode character. */ +PyAPI_FUNC(Py_UNICODE) PyUnicode_GetMax(void); + +/* Resize an already allocated Unicode object to the new size length. + + *unicode is modified to point to the new (resized) object and 0 + returned on success. + + This API may only be called by the function which also called the + Unicode constructor. The refcount on the object must be 1. Otherwise, + an error is returned. + + Error handling is implemented as follows: an exception is set, -1 + is returned and *unicode left untouched. + +*/ + +PyAPI_FUNC(int) PyUnicode_Resize( + PyObject **unicode, /* Pointer to the Unicode object */ + int length /* New length */ + ); + +/* Coerce obj to an Unicode object and return a reference with + *incremented* refcount. + + Coercion is done in the following way: + + 1. String and other char buffer compatible objects are decoded + under the assumptions that they contain data using the current + default encoding. Decoding is done in "strict" mode. + + 2. All other objects (including Unicode objects) raise an + exception. + + The API returns NULL in case of an error. The caller is responsible + for decref'ing the returned objects. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_FromEncodedObject( + register PyObject *obj, /* Object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Coerce obj to an Unicode object and return a reference with + *incremented* refcount. + + Unicode objects are passed back as-is (subclasses are converted to + true Unicode objects), all other objects are delegated to + PyUnicode_FromEncodedObject(obj, NULL, "strict") which results in + using the default encoding as basis for decoding the object. + + The API returns NULL in case of an error. The caller is responsible + for decref'ing the returned objects. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_FromObject( + register PyObject *obj /* Object */ + ); + +/* --- wchar_t support for platforms which support it --------------------- */ + +#ifdef HAVE_WCHAR_H + +/* Create a Unicode Object from the whcar_t buffer w of the given + size. + + The buffer is copied into the new object. */ + +PyAPI_FUNC(PyObject*) PyUnicode_FromWideChar( + register const wchar_t *w, /* wchar_t buffer */ + int size /* size of buffer */ + ); + +/* Copies the Unicode Object contents into the whcar_t buffer w. At + most size wchar_t characters are copied. + + Returns the number of wchar_t characters copied or -1 in case of an + error. */ + +PyAPI_FUNC(int) PyUnicode_AsWideChar( + PyUnicodeObject *unicode, /* Unicode object */ + register wchar_t *w, /* wchar_t buffer */ + int size /* size of buffer */ + ); + +#endif + +/* --- Unicode ordinals --------------------------------------------------- */ + +/* Create a Unicode Object from the given Unicode code point ordinal. + + The ordinal must be in range(0x10000) on narrow Python builds + (UCS2), and range(0x110000) on wide builds (UCS4). A ValueError is + raised in case it is not. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_FromOrdinal(int ordinal); + +/* === Builtin Codecs ===================================================== + + Many of these APIs take two arguments encoding and errors. These + parameters encoding and errors have the same semantics as the ones + of the builtin unicode() API. + + Setting encoding to NULL causes the default encoding to be used. + + Error handling is set by errors which may also be set to NULL + meaning to use the default handling defined for the codec. Default + error handling for all builtin codecs is "strict" (ValueErrors are + raised). + + The codecs all use a similar interface. Only deviation from the + generic ones are documented. + +*/ + +/* --- Manage the default encoding ---------------------------------------- */ + +/* Return a Python string holding the default encoded value of the + Unicode object. + + The resulting string is cached in the Unicode object for subsequent + usage by this function. The cached version is needed to implement + the character buffer interface and will live (at least) as long as + the Unicode object itself. + + The refcount of the string is *not* incremented. + + *** Exported for internal use by the interpreter only !!! *** + +*/ + +PyAPI_FUNC(PyObject *) _PyUnicode_AsDefaultEncodedString( + PyObject *, const char *); + +/* Returns the currently active default encoding. + + The default encoding is currently implemented as run-time settable + process global. This may change in future versions of the + interpreter to become a parameter which is managed on a per-thread + basis. + + */ + +PyAPI_FUNC(const char*) PyUnicode_GetDefaultEncoding(void); + +/* Sets the currently active default encoding. + + Returns 0 on success, -1 in case of an error. + + */ + +PyAPI_FUNC(int) PyUnicode_SetDefaultEncoding( + const char *encoding /* Encoding name in standard form */ + ); + +/* --- Generic Codecs ----------------------------------------------------- */ + +/* Create a Unicode object by decoding the encoded string s of the + given size. */ + +PyAPI_FUNC(PyObject*) PyUnicode_Decode( + const char *s, /* encoded string */ + int size, /* size of buffer */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Encodes a Py_UNICODE buffer of the given size and returns a + Python string object. */ + +PyAPI_FUNC(PyObject*) PyUnicode_Encode( + const Py_UNICODE *s, /* Unicode char buffer */ + int size, /* number of Py_UNICODE chars to encode */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* Encodes a Unicode object and returns the result as Python string + object. */ + +PyAPI_FUNC(PyObject*) PyUnicode_AsEncodedString( + PyObject *unicode, /* Unicode object */ + const char *encoding, /* encoding */ + const char *errors /* error handling */ + ); + +/* --- UTF-7 Codecs ------------------------------------------------------- */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUTF7( + const char *string, /* UTF-7 encoded string */ + int length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_EncodeUTF7( + const Py_UNICODE *data, /* Unicode char buffer */ + int length, /* number of Py_UNICODE chars to encode */ + int encodeSetO, /* force the encoder to encode characters in + Set O, as described in RFC2152 */ + int encodeWhiteSpace, /* force the encoder to encode space, tab, + carriage return and linefeed characters */ + const char *errors /* error handling */ + ); + +/* --- UTF-8 Codecs ------------------------------------------------------- */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUTF8( + const char *string, /* UTF-8 encoded string */ + int length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsUTF8String( + PyObject *unicode /* Unicode object */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_EncodeUTF8( + const Py_UNICODE *data, /* Unicode char buffer */ + int length, /* number of Py_UNICODE chars to encode */ + const char *errors /* error handling */ + ); + +/* --- UTF-16 Codecs ------------------------------------------------------ */ + +/* Decodes length bytes from a UTF-16 encoded buffer string and returns + the corresponding Unicode object. + + errors (if non-NULL) defines the error handling. It defaults + to "strict". + + If byteorder is non-NULL, the decoder starts decoding using the + given byte order: + + *byteorder == -1: little endian + *byteorder == 0: native order + *byteorder == 1: big endian + + In native mode, the first two bytes of the stream are checked for a + BOM mark. If found, the BOM mark is analysed, the byte order + adjusted and the BOM skipped. In the other modes, no BOM mark + interpretation is done. After completion, *byteorder is set to the + current byte order at the end of input data. + + If byteorder is NULL, the codec starts in native order mode. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUTF16( + const char *string, /* UTF-16 encoded string */ + int length, /* size of string */ + const char *errors, /* error handling */ + int *byteorder /* pointer to byteorder to use + 0=native;-1=LE,1=BE; updated on + exit */ + ); + +/* Returns a Python string using the UTF-16 encoding in native byte + order. The string always starts with a BOM mark. */ + +PyAPI_FUNC(PyObject*) PyUnicode_AsUTF16String( + PyObject *unicode /* Unicode object */ + ); + +/* Returns a Python string object holding the UTF-16 encoded value of + the Unicode data. + + If byteorder is not 0, output is written according to the following + byte order: + + byteorder == -1: little endian + byteorder == 0: native byte order (writes a BOM mark) + byteorder == 1: big endian + + If byteorder is 0, the output string will always start with the + Unicode BOM mark (U+FEFF). In the other two modes, no BOM mark is + prepended. + + Note that Py_UNICODE data is being interpreted as UTF-16 reduced to + UCS-2. This trick makes it possible to add full UTF-16 capabilities + at a later point without compromising the APIs. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_EncodeUTF16( + const Py_UNICODE *data, /* Unicode char buffer */ + int length, /* number of Py_UNICODE chars to encode */ + const char *errors, /* error handling */ + int byteorder /* byteorder to use 0=BOM+native;-1=LE,1=BE */ + ); + +/* --- Unicode-Escape Codecs ---------------------------------------------- */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeUnicodeEscape( + const char *string, /* Unicode-Escape encoded string */ + int length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsUnicodeEscapeString( + PyObject *unicode /* Unicode object */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_EncodeUnicodeEscape( + const Py_UNICODE *data, /* Unicode char buffer */ + int length /* Number of Py_UNICODE chars to encode */ + ); + +/* --- Raw-Unicode-Escape Codecs ------------------------------------------ */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeRawUnicodeEscape( + const char *string, /* Raw-Unicode-Escape encoded string */ + int length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsRawUnicodeEscapeString( + PyObject *unicode /* Unicode object */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_EncodeRawUnicodeEscape( + const Py_UNICODE *data, /* Unicode char buffer */ + int length /* Number of Py_UNICODE chars to encode */ + ); + +/* --- Latin-1 Codecs ----------------------------------------------------- + + Note: Latin-1 corresponds to the first 256 Unicode ordinals. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeLatin1( + const char *string, /* Latin-1 encoded string */ + int length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsLatin1String( + PyObject *unicode /* Unicode object */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_EncodeLatin1( + const Py_UNICODE *data, /* Unicode char buffer */ + int length, /* Number of Py_UNICODE chars to encode */ + const char *errors /* error handling */ + ); + +/* --- ASCII Codecs ------------------------------------------------------- + + Only 7-bit ASCII data is excepted. All other codes generate errors. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeASCII( + const char *string, /* ASCII encoded string */ + int length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsASCIIString( + PyObject *unicode /* Unicode object */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_EncodeASCII( + const Py_UNICODE *data, /* Unicode char buffer */ + int length, /* Number of Py_UNICODE chars to encode */ + const char *errors /* error handling */ + ); + +/* --- Character Map Codecs ----------------------------------------------- + + This codec uses mappings to encode and decode characters. + + Decoding mappings must map single string characters to single + Unicode characters, integers (which are then interpreted as Unicode + ordinals) or None (meaning "undefined mapping" and causing an + error). + + Encoding mappings must map single Unicode characters to single + string characters, integers (which are then interpreted as Latin-1 + ordinals) or None (meaning "undefined mapping" and causing an + error). + + If a character lookup fails with a LookupError, the character is + copied as-is meaning that its ordinal value will be interpreted as + Unicode or Latin-1 ordinal resp. Because of this mappings only need + to contain those mappings which map characters to different code + points. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeCharmap( + const char *string, /* Encoded string */ + int length, /* size of string */ + PyObject *mapping, /* character mapping + (char ordinal -> unicode ordinal) */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsCharmapString( + PyObject *unicode, /* Unicode object */ + PyObject *mapping /* character mapping + (unicode ordinal -> char ordinal) */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_EncodeCharmap( + const Py_UNICODE *data, /* Unicode char buffer */ + int length, /* Number of Py_UNICODE chars to encode */ + PyObject *mapping, /* character mapping + (unicode ordinal -> char ordinal) */ + const char *errors /* error handling */ + ); + +/* Translate a Py_UNICODE buffer of the given length by applying a + character mapping table to it and return the resulting Unicode + object. + + The mapping table must map Unicode ordinal integers to Unicode + ordinal integers or None (causing deletion of the character). + + Mapping tables may be dictionaries or sequences. Unmapped character + ordinals (ones which cause a LookupError) are left untouched and + are copied as-is. + +*/ + +PyAPI_FUNC(PyObject *) PyUnicode_TranslateCharmap( + const Py_UNICODE *data, /* Unicode char buffer */ + int length, /* Number of Py_UNICODE chars to encode */ + PyObject *table, /* Translate table */ + const char *errors /* error handling */ + ); + +#ifdef MS_WIN32 + +/* --- MBCS codecs for Windows -------------------------------------------- */ + +PyAPI_FUNC(PyObject*) PyUnicode_DecodeMBCS( + const char *string, /* MBCS encoded string */ + int length, /* size of string */ + const char *errors /* error handling */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_AsMBCSString( + PyObject *unicode /* Unicode object */ + ); + +PyAPI_FUNC(PyObject*) PyUnicode_EncodeMBCS( + const Py_UNICODE *data, /* Unicode char buffer */ + int length, /* Number of Py_UNICODE chars to encode */ + const char *errors /* error handling */ + ); + +#endif /* MS_WIN32 */ + +/* --- Decimal Encoder ---------------------------------------------------- */ + +/* Takes a Unicode string holding a decimal value and writes it into + an output buffer using standard ASCII digit codes. + + The output buffer has to provide at least length+1 bytes of storage + area. The output string is 0-terminated. + + The encoder converts whitespace to ' ', decimal characters to their + corresponding ASCII digit and all other Latin-1 characters except + \0 as-is. Characters outside this range (Unicode ordinals 1-256) + are treated as errors. This includes embedded NULL bytes. + + Error handling is defined by the errors argument: + + NULL or "strict": raise a ValueError + "ignore": ignore the wrong characters (these are not copied to the + output buffer) + "replace": replaces illegal characters with '?' + + Returns 0 on success, -1 on failure. + +*/ + +PyAPI_FUNC(int) PyUnicode_EncodeDecimal( + Py_UNICODE *s, /* Unicode buffer */ + int length, /* Number of Py_UNICODE chars to encode */ + char *output, /* Output buffer; must have size >= length */ + const char *errors /* error handling */ + ); + +/* --- Methods & Slots ---------------------------------------------------- + + These are capable of handling Unicode objects and strings on input + (we refer to them as strings in the descriptions) and return + Unicode objects or integers as apporpriate. */ + +/* Concat two strings giving a new Unicode string. */ + +PyAPI_FUNC(PyObject*) PyUnicode_Concat( + PyObject *left, /* Left string */ + PyObject *right /* Right string */ + ); + +/* Split a string giving a list of Unicode strings. + + If sep is NULL, splitting will be done at all whitespace + substrings. Otherwise, splits occur at the given separator. + + At most maxsplit splits will be done. If negative, no limit is set. + + Separators are not included in the resulting list. + +*/ + +PyAPI_FUNC(PyObject*) PyUnicode_Split( + PyObject *s, /* String to split */ + PyObject *sep, /* String separator */ + int maxsplit /* Maxsplit count */ + ); + +/* Dito, but split at line breaks. + + CRLF is considered to be one line break. Line breaks are not + included in the resulting list. */ + +PyAPI_FUNC(PyObject*) PyUnicode_Splitlines( + PyObject *s, /* String to split */ + int keepends /* If true, line end markers are included */ + ); + +/* Translate a string by applying a character mapping table to it and + return the resulting Unicode object. + + The mapping table must map Unicode ordinal integers to Unicode + ordinal integers or None (causing deletion of the character). + + Mapping tables may be dictionaries or sequences. Unmapped character + ordinals (ones which cause a LookupError) are left untouched and + are copied as-is. + +*/ + +PyAPI_FUNC(PyObject *) PyUnicode_Translate( + PyObject *str, /* String */ + PyObject *table, /* Translate table */ + const char *errors /* error handling */ + ); + +/* Join a sequence of strings using the given separator and return + the resulting Unicode string. */ + +PyAPI_FUNC(PyObject*) PyUnicode_Join( + PyObject *separator, /* Separator string */ + PyObject *seq /* Sequence object */ + ); + +/* Return 1 if substr matches str[start:end] at the given tail end, 0 + otherwise. */ + +PyAPI_FUNC(int) PyUnicode_Tailmatch( + PyObject *str, /* String */ + PyObject *substr, /* Prefix or Suffix string */ + int start, /* Start index */ + int end, /* Stop index */ + int direction /* Tail end: -1 prefix, +1 suffix */ + ); + +/* Return the first position of substr in str[start:end] using the + given search direction or -1 if not found. -2 is returned in case + an error occurred and an exception is set. */ + +PyAPI_FUNC(int) PyUnicode_Find( + PyObject *str, /* String */ + PyObject *substr, /* Substring to find */ + int start, /* Start index */ + int end, /* Stop index */ + int direction /* Find direction: +1 forward, -1 backward */ + ); + +/* Count the number of occurrences of substr in str[start:end]. */ + +PyAPI_FUNC(int) PyUnicode_Count( + PyObject *str, /* String */ + PyObject *substr, /* Substring to count */ + int start, /* Start index */ + int end /* Stop index */ + ); + +/* Replace at most maxcount occurrences of substr in str with replstr + and return the resulting Unicode object. */ + +PyAPI_FUNC(PyObject *) PyUnicode_Replace( + PyObject *str, /* String */ + PyObject *substr, /* Substring to find */ + PyObject *replstr, /* Substring to replace */ + int maxcount /* Max. number of replacements to apply; + -1 = all */ + ); + +/* Compare two strings and return -1, 0, 1 for less than, equal, + greater than resp. */ + +PyAPI_FUNC(int) PyUnicode_Compare( + PyObject *left, /* Left string */ + PyObject *right /* Right string */ + ); + +/* Apply a argument tuple or dictionary to a format string and return + the resulting Unicode string. */ + +PyAPI_FUNC(PyObject *) PyUnicode_Format( + PyObject *format, /* Format string */ + PyObject *args /* Argument tuple or dictionary */ + ); + +/* Checks whether element is contained in container and return 1/0 + accordingly. + + element has to coerce to an one element Unicode string. -1 is + returned in case of an error. */ + +PyAPI_FUNC(int) PyUnicode_Contains( + PyObject *container, /* Container string */ + PyObject *element /* Element string */ + ); + +/* Externally visible for str.strip(unicode) */ +PyAPI_FUNC(PyObject *) _PyUnicode_XStrip( + PyUnicodeObject *self, + int striptype, + PyObject *sepobj + ); + +/* === Characters Type APIs =============================================== */ + +/* These should not be used directly. Use the Py_UNICODE_IS* and + Py_UNICODE_TO* macros instead. + + These APIs are implemented in Objects/unicodectype.c. + +*/ + +PyAPI_FUNC(int) _PyUnicode_IsLowercase( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsUppercase( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsTitlecase( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsWhitespace( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsLinebreak( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(Py_UNICODE) _PyUnicode_ToLowercase( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(Py_UNICODE) _PyUnicode_ToUppercase( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(Py_UNICODE) _PyUnicode_ToTitlecase( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_ToDecimalDigit( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_ToDigit( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(double) _PyUnicode_ToNumeric( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsDecimalDigit( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsDigit( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsNumeric( + Py_UNICODE ch /* Unicode character */ + ); + +PyAPI_FUNC(int) _PyUnicode_IsAlpha( + Py_UNICODE ch /* Unicode character */ + ); + +#ifdef __cplusplus +} +#endif +#endif /* Py_USING_UNICODE */ +#endif /* !Py_UNICODEOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/weakrefobject.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/weakrefobject.h new file mode 100644 index 00000000..e6e89d35 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/include/weakrefobject.h @@ -0,0 +1,50 @@ +/* Weak references objects for Python. */ + +#ifndef Py_WEAKREFOBJECT_H +#define Py_WEAKREFOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct _PyWeakReference PyWeakReference; + +struct _PyWeakReference { + PyObject_HEAD + PyObject *wr_object; + PyObject *wr_callback; + long hash; + PyWeakReference *wr_prev; + PyWeakReference *wr_next; +}; + +PyAPI_DATA(PyTypeObject) _PyWeakref_RefType; +PyAPI_DATA(PyTypeObject) _PyWeakref_ProxyType; +PyAPI_DATA(PyTypeObject) _PyWeakref_CallableProxyType; + +#define PyWeakref_CheckRef(op) \ + ((op)->ob_type == &_PyWeakref_RefType) +#define PyWeakref_CheckProxy(op) \ + (((op)->ob_type == &_PyWeakref_ProxyType) || \ + ((op)->ob_type == &_PyWeakref_CallableProxyType)) +#define PyWeakref_Check(op) \ + (PyWeakref_CheckRef(op) || PyWeakref_CheckProxy(op)) + + +PyAPI_FUNC(PyObject *) PyWeakref_NewRef(PyObject *ob, + PyObject *callback); +PyAPI_FUNC(PyObject *) PyWeakref_NewProxy(PyObject *ob, + PyObject *callback); +PyAPI_FUNC(PyObject *) PyWeakref_GetObject(PyObject *ref); + +PyAPI_FUNC(long) _PyWeakref_GetWeakrefCount(PyWeakReference *head); + +PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self); + +#define PyWeakref_GET_OBJECT(ref) (((PyWeakReference *)(ref))->wr_object) + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_WEAKREFOBJECT_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/pyconfig_dyn/pyconfig.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/pyconfig_dyn/pyconfig.h new file mode 100644 index 00000000..a328f275 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/pyconfig_dyn/pyconfig.h @@ -0,0 +1,596 @@ +#ifndef Py_CONFIG_H +#define Py_CONFIG_H + +/* pyconfig.h. NOT Generated automatically by configure. + +This is a manually maintained version used for the Watcom, +Borland and Microsoft Visual C++ compilers. It is a +standard part of the Python distribution. + +WINDOWS DEFINES: +The code specific to Windows should be wrapped around one of +the following #defines + +MS_WIN64 - Code specific to the MS Win64 API +MS_WIN32 - Code specific to the MS Win32 (and Win64) API (obsolete, this covers all supported APIs) +MS_WINDOWS - Code specific to Windows, but all versions. +Py_ENABLE_SHARED - Code if the Python core is built as a DLL. + +Also note that neither "_M_IX86" or "_MSC_VER" should be used for +any purpose other than "Windows Intel x86 specific" and "Microsoft +compiler specific". Therefore, these should be very rare. + + +NOTE: The following symbols are deprecated: +NT, WIN32, USE_DL_EXPORT, USE_DL_IMPORT, DL_EXPORT, DL_IMPORT +MS_CORE_DLL. + +*/ + +#include +#define HAVE_LIMITS_H +#define HAVE_SYS_UTIME_H +#define HAVE_HYPOT +#define HAVE_TEMPNAM +#define HAVE_TMPFILE +#define HAVE_TMPNAM +#define HAVE_CLOCK +#define HAVE_STRFTIME +#define HAVE_STRERROR +#define DONT_HAVE_SIG_ALARM +#define DONT_HAVE_SIG_PAUSE +#define LONG_BIT 32 +#define WORD_BIT 32 +#define PREFIX "" +#define EXEC_PREFIX "" + +#define MS_WIN32 /* only support win32 and greater. */ +#define MS_WINDOWS +#if _XBOX +#define MS_XBOX +#endif + +#ifndef PYTHONPATH +# define PYTHONPATH ".\\DLLs;.\\lib;.\\lib\\plat-win;.\\lib\\lib-tk" +#endif +#define NT_THREADS +#define WITH_THREAD +#ifndef NETSCAPE_PI +#define USE_SOCKET +#endif + +/* Compiler specific defines */ + +/* ------------------------------------------------------------------------*/ +/* Microsoft C defines _MSC_VER */ +#ifdef _MSC_VER + +/* We want COMPILER to expand to a string containing _MSC_VER's *value*. + * This is horridly tricky, because the stringization operator only works + * on macro arguments, and doesn't evaluate macros passed *as* arguments. + * Attempts simpler than the following appear doomed to produce "_MSC_VER" + * literally in the string. + */ +#define _Py_PASTE_VERSION(SUFFIX) \ + ("[MSC v." _Py_STRINGIZE(_MSC_VER) " " SUFFIX "]") +/* e.g., this produces, after compile-time string catenation, + * ("[MSC v.1200 32 bit (Intel)]") + * + * _Py_STRINGIZE(_MSC_VER) expands to + * _Py_STRINGIZE1((_MSC_VER)) expands to + * _Py_STRINGIZE2(_MSC_VER) but as this call is the result of token-pasting + * it's scanned again for macros and so further expands to (under MSVC 6) + * _Py_STRINGIZE2(1200) which then expands to + * "1200" + */ +#define _Py_STRINGIZE(X) _Py_STRINGIZE1((X)) +#define _Py_STRINGIZE1(X) _Py_STRINGIZE2 ## X +#define _Py_STRINGIZE2(X) #X + +/* MSVC defines _WINxx to differentiate the windows platform types + + Note that for compatibility reasons _WIN32 is defined on Win32 + *and* on Win64. For the same reasons, in Python, MS_WIN32 is + defined on Win32 *and* Win64. Win32 only code must therefore be + guarded as follows: + #if defined(MS_WIN32) && !defined(MS_WIN64) +*/ +#ifdef _WIN64 +#define MS_WIN64 +#endif + +/* set the COMPILER */ +#ifdef MS_WIN64 +#ifdef _M_IX86 +#define COMPILER _Py_PASTE_VERSION("64 bit (Intel)") +#else +#define COMPILER _Py_PASTE_VERSION("64 bit (Unknown)") +#endif +#endif /* MS_WIN64 */ + +#if defined(MS_WIN32) && !defined(MS_WIN64) +#ifdef _M_IX86 +#define COMPILER _Py_PASTE_VERSION("32 bit (Intel)") +#else +#define COMPILER _Py_PASTE_VERSION("32 bit (Unknown)") +#endif +#endif /* MS_WIN32 && !MS_WIN64 */ + +typedef int pid_t; +#define hypot _hypot + +#endif /* _MSC_VER */ + +/* define some ANSI types that are not defined in earlier Win headers */ +#if defined(_MSC_VER) && _MSC_VER >= 1200 +/* This file only exists in VC 6.0 or higher */ +#include +#endif + +/* ------------------------------------------------------------------------*/ +/* The Borland compiler defines __BORLANDC__ */ +/* XXX These defines are likely incomplete, but should be easy to fix. */ +#ifdef __BORLANDC__ +#define COMPILER "[Borland]" + +#ifdef _WIN32 +/* tested with BCC 5.5 (__BORLANDC__ >= 0x0550) + */ + +typedef int pid_t; +/* BCC55 seems to understand __declspec(dllimport), it is used in its + own header files (winnt.h, ...) - so we can do nothing and get the default*/ + +#undef HAVE_SYS_UTIME_H +#define HAVE_UTIME_H +#define HAVE_DIRENT_H + +/* rename a few functions for the Borland compiler */ +#include +#define _chsize chsize +#define _setmode setmode + +#else /* !_WIN32 */ +#error "Only Win32 and later are supported" +#endif /* !_WIN32 */ + +#endif /* BORLANDC */ + +/* ------------------------------------------------------------------------*/ +/* egcs/gnu-win32 defines __GNUC__ and _WIN32 */ +#if defined(__GNUC__) && defined(_WIN32) +/* XXX These defines are likely incomplete, but should be easy to fix. + They should be complete enough to build extension modules. */ +/* Suggested by Rene Liebscher to avoid a GCC 2.91.* + bug that requires structure imports. More recent versions of the + compiler don't exhibit this bug. +*/ +#if (__GNUC__==2) && (__GNUC_MINOR__<=91) +#warning "Please use an up-to-date version of gcc! (>2.91 recommended)" +#endif + +#define COMPILER "[gcc]" +#define hypot _hypot +#define PY_LONG_LONG long long +#endif /* GNUC */ + +/* ------------------------------------------------------------------------*/ +/* lcc-win32 defines __LCC__ */ +#if defined(__LCC__) +/* XXX These defines are likely incomplete, but should be easy to fix. + They should be complete enough to build extension modules. */ + +#define COMPILER "[lcc-win32]" +typedef int pid_t; +/* __declspec() is supported here too - do nothing to get the defaults */ + +#endif /* LCC */ + +/* ------------------------------------------------------------------------*/ +/* End of compilers - finish up */ + +#ifndef NO_STDIO_H +# include +#endif + +/* 64 bit ints are usually spelt __int64 unless compiler has overridden */ +#define HAVE_LONG_LONG 1 +#ifndef PY_LONG_LONG +# define PY_LONG_LONG __int64 +#endif + +/* For Windows the Python core is in a DLL by default. Test +Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */ +#if !defined(MS_NO_COREDLL) && !defined(Py_NO_ENABLE_SHARED) +# define Py_ENABLE_SHARED 1 /* standard symbol for shared library */ +# define MS_COREDLL /* deprecated old symbol */ +#endif /* !MS_NO_COREDLL && ... */ + +/* Deprecated USE_DL_EXPORT macro - please use Py_BUILD_CORE */ +#ifdef USE_DL_EXPORT +# define Py_BUILD_CORE +#endif /* USE_DL_EXPORT */ + +/* All windows compilers that use this header support __declspec */ +#define HAVE_DECLSPEC_DLL + +/* For an MSVC DLL, we can nominate the .lib files used by extensions */ +#ifdef MS_COREDLL +# ifndef Py_BUILD_CORE /* not building the core - must be an ext */ +# if defined(_MSC_VER) + /* So MSVC users need not specify the .lib file in + their Makefile (other compilers are generally + taken care of by distutils.) */ +# if 0 // we don't want help! +# ifdef _DEBUG +# pragma comment(lib,"python23_d.lib") +# else +# pragma comment(lib,"python23.lib") +# endif /* _DEBUG */ +# endif /* 0 */ +# endif /* _MSC_VER */ +# endif /* Py_BUILD_CORE */ +#endif /* MS_COREDLL */ + +#if defined(MS_WIN64) +/* maintain "win32" sys.platform for backward compatibility of Python code, + the Win64 API should be close enough to the Win32 API to make this + preferable */ +# define PLATFORM "win32" +# define SIZEOF_VOID_P 8 +# define SIZEOF_TIME_T 8 +# define SIZEOF_OFF_T 4 +# define SIZEOF_FPOS_T 8 +# define SIZEOF_HKEY 8 +/* configure.in defines HAVE_LARGEFILE_SUPPORT iff HAVE_LONG_LONG, + sizeof(off_t) > sizeof(long), and sizeof(PY_LONG_LONG) >= sizeof(off_t). + On Win64 the second condition is not true, but if fpos_t replaces off_t + then this is true. The uses of HAVE_LARGEFILE_SUPPORT imply that Win64 + should define this. */ +# define HAVE_LARGEFILE_SUPPORT +#elif defined(MS_WIN32) +# define PLATFORM "win32" +# define HAVE_LARGEFILE_SUPPORT +# define SIZEOF_VOID_P 4 +# define SIZEOF_TIME_T 4 +# define SIZEOF_OFF_T 4 +# define SIZEOF_FPOS_T 8 +# define SIZEOF_HKEY 4 +#endif + +#ifdef _DEBUG +# define Py_DEBUG +#endif + + +#ifdef MS_WIN32 + +#define SIZEOF_SHORT 2 +#define SIZEOF_INT 4 +#define SIZEOF_LONG 4 +#define SIZEOF_LONG_LONG 8 + +#endif + +/* Fairly standard from here! */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define to empty if the keyword does not work. */ +/* #define const */ + +/* Define if you have dirent.h. */ +/* #define DIRENT 1 */ + +/* Define to the type of elements in the array set by `getgroups'. + Usually this is either `int' or `gid_t'. */ +/* #undef GETGROUPS_T */ + +/* Define to `int' if doesn't define. */ +/* #undef gid_t */ + +/* Define if your struct tm has tm_zone. */ +/* #undef HAVE_TM_ZONE */ + +/* Define if you don't have tm_zone but do have the external array + tzname. */ +#define HAVE_TZNAME + +/* Define if on MINIX. */ +/* #undef _MINIX */ + +/* Define to `int' if doesn't define. */ +/* #undef mode_t */ + +/* Define if you don't have dirent.h, but have ndir.h. */ +/* #undef NDIR */ + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define if the system does not provide POSIX.1 features except + with this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define to `unsigned' if doesn't define. */ +/* #undef size_t */ + +/* Define to `int' if doesn't define. */ +#if _MSC_VER + 0 >= 1300 +/* VC.NET typedefs socklen_t in ws2tcpip.h. */ +#else +#define socklen_t int +#endif + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you don't have dirent.h, but have sys/dir.h. */ +/* #undef SYSDIR */ + +/* Define if you don't have dirent.h, but have sys/ndir.h. */ +/* #undef SYSNDIR */ + +/* Define if you can safely include both and . */ +/* #undef TIME_WITH_SYS_TIME */ + +/* Define if your declares struct tm. */ +/* #define TM_IN_SYS_TIME 1 */ + +/* Define to `int' if doesn't define. */ +/* #undef uid_t */ + +/* Define if the closedir function returns void instead of int. */ +/* #undef VOID_CLOSEDIR */ + +/* Define if your contains bad prototypes for exec*() + (as it does on SGI IRIX 4.x) */ +/* #undef BAD_EXEC_PROTOTYPES */ + +/* Define if getpgrp() must be called as getpgrp(0) + and (consequently) setpgrp() as setpgrp(0, 0). */ +/* #undef GETPGRP_HAVE_ARGS */ + +/* Define this if your time.h defines altzone */ +/* #define HAVE_ALTZONE */ + +/* Define if you have the putenv function. */ +#ifndef MS_XBOX +#define HAVE_PUTENV +#endif + +/* Define if your compiler supports function prototypes */ +#define HAVE_PROTOTYPES + +/* Define if you can safely include both and + (which you can't on SCO ODT 3.0). */ +/* #undef SYS_SELECT_WITH_SYS_TIME */ + +/* Define if you want to use SGI (IRIX 4) dynamic linking. + This requires the "dl" library by Jack Jansen, + ftp://ftp.cwi.nl/pub/dynload/dl-1.6.tar.Z. + Don't bother on IRIX 5, it already has dynamic linking using SunOS + style shared libraries */ +/* #undef WITH_SGI_DL */ + +/* Define if you want to emulate SGI (IRIX 4) dynamic linking. + This is rumoured to work on VAX (Ultrix), Sun3 (SunOS 3.4), + Sequent Symmetry (Dynix), and Atari ST. + This requires the "dl-dld" library, + ftp://ftp.cwi.nl/pub/dynload/dl-dld-1.1.tar.Z, + as well as the "GNU dld" library, + ftp://ftp.cwi.nl/pub/dynload/dld-3.2.3.tar.Z. + Don't bother on SunOS 4 or 5, they already have dynamic linking using + shared libraries */ +/* #undef WITH_DL_DLD */ + +/* Define if you want documentation strings in extension modules */ +#define WITH_DOC_STRINGS 1 + +/* Define if you want to compile in rudimentary thread support */ +/* #undef WITH_THREAD */ + +/* Define if you want to use the GNU readline library */ +/* #define WITH_READLINE 1 */ + +/* Define if you want to have a Unicode type. */ +#define Py_USING_UNICODE + +/* Define as the integral type used for Unicode representation. */ +#define PY_UNICODE_TYPE unsigned short + +/* Define as the size of the unicode type. */ +#define Py_UNICODE_SIZE SIZEOF_SHORT + +/* Define if you have a useable wchar_t type defined in wchar.h; useable + means wchar_t must be 16-bit unsigned type. (see + Include/unicodeobject.h). */ +#if Py_UNICODE_SIZE == 2 +#define HAVE_USABLE_WCHAR_T + +/* Define to indicate that the Python Unicode representation can be passed + as-is to Win32 Wide API. */ +#ifndef MS_XBOX +#define Py_WIN_WIDE_FILENAMES +#endif +#endif + +/* Use Python's own small-block memory-allocator. */ +#define WITH_PYMALLOC 1 + +/* Enable \n, \r, \r\n line ends on import; also the 'U' mode flag for open. */ +#define WITH_UNIVERSAL_NEWLINES 1 + +/* Define if you have clock. */ +/* #define HAVE_CLOCK */ + +/* Define when any dynamic module loading is enabled */ +#define HAVE_DYNAMIC_LOADING + +/* Define if you have ftime. */ +#define HAVE_FTIME + +/* Define if you have getpeername. */ +#define HAVE_GETPEERNAME + +/* Define if you have getpgrp. */ +/* #undef HAVE_GETPGRP */ + +/* Define if you have getpid. */ +#define HAVE_GETPID + +/* Define if you have gettimeofday. */ +/* #undef HAVE_GETTIMEOFDAY */ + +/* Define if you have getwd. */ +/* #undef HAVE_GETWD */ + +/* Define if you have lstat. */ +/* #undef HAVE_LSTAT */ + +/* Define if you have the mktime function. */ +#define HAVE_MKTIME + +/* Define if you have nice. */ +/* #undef HAVE_NICE */ + +/* Define if you have readlink. */ +/* #undef HAVE_READLINK */ + +/* Define if you have select. */ +/* #undef HAVE_SELECT */ + +/* Define if you have setpgid. */ +/* #undef HAVE_SETPGID */ + +/* Define if you have setpgrp. */ +/* #undef HAVE_SETPGRP */ + +/* Define if you have setsid. */ +/* #undef HAVE_SETSID */ + +/* Define if you have setvbuf. */ +#define HAVE_SETVBUF + +/* Define if you have siginterrupt. */ +/* #undef HAVE_SIGINTERRUPT */ + +/* Define if you have symlink. */ +/* #undef HAVE_SYMLINK */ + +/* Define if you have tcgetpgrp. */ +/* #undef HAVE_TCGETPGRP */ + +/* Define if you have tcsetpgrp. */ +/* #undef HAVE_TCSETPGRP */ + +/* Define if you have times. */ +/* #undef HAVE_TIMES */ + +/* Define if you have uname. */ +/* #undef HAVE_UNAME */ + +/* Define if you have waitpid. */ +/* #undef HAVE_WAITPID */ + +/* Define to 1 if you have the `wcscoll' function. */ +#define HAVE_WCSCOLL 1 + +/* Define if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define if you have the prototypes. */ +#define HAVE_STDARG_PROTOTYPES + +/* Define if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_AUDIOIO_H */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_PARAM_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_SELECT_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_TIME_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_TIMES_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_UN_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_UTIME_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_UTSNAME_H 1 */ + +/* Define if you have the header file. */ +/* #undef HAVE_THREAD_H */ + +/* Define if you have the header file. */ +/* #define HAVE_UNISTD_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_UTIME_H 1 */ + +/* Define if the compiler provides a wchar.h header file. */ +#define HAVE_WCHAR_H 1 + +/* Define if you have the dl library (-ldl). */ +/* #undef HAVE_LIBDL */ + +/* Define if you have the mpc library (-lmpc). */ +/* #undef HAVE_LIBMPC */ + +/* Define if you have the nsl library (-lnsl). */ +#define HAVE_LIBNSL 1 + +/* Define if you have the seq library (-lseq). */ +/* #undef HAVE_LIBSEQ */ + +/* Define if you have the socket library (-lsocket). */ +#define HAVE_LIBSOCKET 1 + +/* Define if you have the sun library (-lsun). */ +/* #undef HAVE_LIBSUN */ + +/* Define if you have the termcap library (-ltermcap). */ +/* #undef HAVE_LIBTERMCAP */ + +/* Define if you have the termlib library (-ltermlib). */ +/* #undef HAVE_LIBTERMLIB */ + +/* Define if you have the thread library (-lthread). */ +/* #undef HAVE_LIBTHREAD */ +#endif /* !Py_CONFIG_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/pyconfig_static/pyconfig.h b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/pyconfig_static/pyconfig.h new file mode 100644 index 00000000..39165f93 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/SDKs/XPlatform/Cypython-2.3.3/pyconfig_static/pyconfig.h @@ -0,0 +1,600 @@ +#ifndef Py_CONFIG_H +#define Py_CONFIG_H + +/* pyconfig.h. NOT Generated automatically by configure. + +This is a manually maintained version used for the Watcom, +Borland and Microsoft Visual C++ compilers. It is a +standard part of the Python distribution. + +WINDOWS DEFINES: +The code specific to Windows should be wrapped around one of +the following #defines + +MS_WIN64 - Code specific to the MS Win64 API +MS_WIN32 - Code specific to the MS Win32 (and Win64) API (obsolete, this covers all supported APIs) +MS_WINDOWS - Code specific to Windows, but all versions. +Py_ENABLE_SHARED - Code if the Python core is built as a DLL. + +Also note that neither "_M_IX86" or "_MSC_VER" should be used for +any purpose other than "Windows Intel x86 specific" and "Microsoft +compiler specific". Therefore, these should be very rare. + + +NOTE: The following symbols are deprecated: +NT, WIN32, USE_DL_EXPORT, USE_DL_IMPORT, DL_EXPORT, DL_IMPORT +MS_CORE_DLL. + +*/ + +#include +#define HAVE_LIMITS_H +#define HAVE_SYS_UTIME_H +#define HAVE_HYPOT +#define HAVE_TEMPNAM +#define HAVE_TMPFILE +#define HAVE_TMPNAM +#define HAVE_CLOCK +#define HAVE_STRFTIME +#define HAVE_STRERROR +#define DONT_HAVE_SIG_ALARM +#define DONT_HAVE_SIG_PAUSE +#define LONG_BIT 32 +#define WORD_BIT 32 +#define PREFIX "" +#define EXEC_PREFIX "" + +#define MS_WIN32 /* only support win32 and greater. */ +#define MS_WINDOWS +#if _XBOX +#define MS_XBOX +#endif + +#ifndef PYTHONPATH +# define PYTHONPATH ".\\DLLs;.\\lib;.\\lib\\plat-win;.\\lib\\lib-tk" +#endif +#define NT_THREADS +#define WITH_THREAD +#ifndef NETSCAPE_PI +#define USE_SOCKET +#endif + +/* Compiler specific defines */ + +/* ------------------------------------------------------------------------*/ +/* Microsoft C defines _MSC_VER */ +#ifdef _MSC_VER + +/* We want COMPILER to expand to a string containing _MSC_VER's *value*. + * This is horridly tricky, because the stringization operator only works + * on macro arguments, and doesn't evaluate macros passed *as* arguments. + * Attempts simpler than the following appear doomed to produce "_MSC_VER" + * literally in the string. + */ +#define _Py_PASTE_VERSION(SUFFIX) \ + ("[MSC v." _Py_STRINGIZE(_MSC_VER) " " SUFFIX "]") +/* e.g., this produces, after compile-time string catenation, + * ("[MSC v.1200 32 bit (Intel)]") + * + * _Py_STRINGIZE(_MSC_VER) expands to + * _Py_STRINGIZE1((_MSC_VER)) expands to + * _Py_STRINGIZE2(_MSC_VER) but as this call is the result of token-pasting + * it's scanned again for macros and so further expands to (under MSVC 6) + * _Py_STRINGIZE2(1200) which then expands to + * "1200" + */ +#define _Py_STRINGIZE(X) _Py_STRINGIZE1((X)) +#define _Py_STRINGIZE1(X) _Py_STRINGIZE2 ## X +#define _Py_STRINGIZE2(X) #X + +/* MSVC defines _WINxx to differentiate the windows platform types + + Note that for compatibility reasons _WIN32 is defined on Win32 + *and* on Win64. For the same reasons, in Python, MS_WIN32 is + defined on Win32 *and* Win64. Win32 only code must therefore be + guarded as follows: + #if defined(MS_WIN32) && !defined(MS_WIN64) +*/ +#ifdef _WIN64 +#define MS_WIN64 +#endif + +/* set the COMPILER */ +#ifdef MS_WIN64 +#ifdef _M_IX86 +#define COMPILER _Py_PASTE_VERSION("64 bit (Intel)") +#else +#define COMPILER _Py_PASTE_VERSION("64 bit (Unknown)") +#endif +#endif /* MS_WIN64 */ + +#if defined(MS_WIN32) && !defined(MS_WIN64) +#ifdef _M_IX86 +#define COMPILER _Py_PASTE_VERSION("32 bit (Intel)") +#else +#define COMPILER _Py_PASTE_VERSION("32 bit (Unknown)") +#endif +#endif /* MS_WIN32 && !MS_WIN64 */ + +typedef int pid_t; +#define hypot _hypot + +#endif /* _MSC_VER */ + +/* define some ANSI types that are not defined in earlier Win headers */ +#if defined(_MSC_VER) && _MSC_VER >= 1200 +/* This file only exists in VC 6.0 or higher */ +#include +#endif + +/* ------------------------------------------------------------------------*/ +/* The Borland compiler defines __BORLANDC__ */ +/* XXX These defines are likely incomplete, but should be easy to fix. */ +#ifdef __BORLANDC__ +#define COMPILER "[Borland]" + +#ifdef _WIN32 +/* tested with BCC 5.5 (__BORLANDC__ >= 0x0550) + */ + +typedef int pid_t; +/* BCC55 seems to understand __declspec(dllimport), it is used in its + own header files (winnt.h, ...) - so we can do nothing and get the default*/ + +#undef HAVE_SYS_UTIME_H +#define HAVE_UTIME_H +#define HAVE_DIRENT_H + +/* rename a few functions for the Borland compiler */ +#include +#define _chsize chsize +#define _setmode setmode + +#else /* !_WIN32 */ +#error "Only Win32 and later are supported" +#endif /* !_WIN32 */ + +#endif /* BORLANDC */ + +/* ------------------------------------------------------------------------*/ +/* egcs/gnu-win32 defines __GNUC__ and _WIN32 */ +#if defined(__GNUC__) && defined(_WIN32) +/* XXX These defines are likely incomplete, but should be easy to fix. + They should be complete enough to build extension modules. */ +/* Suggested by Rene Liebscher to avoid a GCC 2.91.* + bug that requires structure imports. More recent versions of the + compiler don't exhibit this bug. +*/ +#if (__GNUC__==2) && (__GNUC_MINOR__<=91) +#warning "Please use an up-to-date version of gcc! (>2.91 recommended)" +#endif + +#define COMPILER "[gcc]" +#define hypot _hypot +#define PY_LONG_LONG long long +#endif /* GNUC */ + +/* ------------------------------------------------------------------------*/ +/* lcc-win32 defines __LCC__ */ +#if defined(__LCC__) +/* XXX These defines are likely incomplete, but should be easy to fix. + They should be complete enough to build extension modules. */ + +#define COMPILER "[lcc-win32]" +typedef int pid_t; +/* __declspec() is supported here too - do nothing to get the defaults */ + +#endif /* LCC */ + +/* ------------------------------------------------------------------------*/ +/* End of compilers - finish up */ + +#ifndef NO_STDIO_H +# include +#endif + +/* 64 bit ints are usually spelt __int64 unless compiler has overridden */ +#define HAVE_LONG_LONG 1 +#ifndef PY_LONG_LONG +# define PY_LONG_LONG __int64 +#endif + +#if !defined(Py_NO_ENABLE_SHARED) +# define Py_NO_ENABLE_SHARED // HACK because this file seems to ignore our pre-processor definitions +#endif + +/* For Windows the Python core is in a DLL by default. Test +Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */ +#if !defined(MS_NO_COREDLL) && !defined(Py_NO_ENABLE_SHARED) +# define Py_ENABLE_SHARED 1 /* standard symbol for shared library */ +# define MS_COREDLL /* deprecated old symbol */ +#endif /* !MS_NO_COREDLL && ... */ + +/* Deprecated USE_DL_EXPORT macro - please use Py_BUILD_CORE */ +#ifdef USE_DL_EXPORT +# define Py_BUILD_CORE +#endif /* USE_DL_EXPORT */ + +/* All windows compilers that use this header support __declspec */ +#define HAVE_DECLSPEC_DLL + +/* For an MSVC DLL, we can nominate the .lib files used by extensions */ +#ifdef MS_COREDLL +# ifndef Py_BUILD_CORE /* not building the core - must be an ext */ +# if defined(_MSC_VER) + /* So MSVC users need not specify the .lib file in + their Makefile (other compilers are generally + taken care of by distutils.) */ +# if 0 // we don't want help! +# ifdef _DEBUG +# pragma comment(lib,"python23_d.lib") +# else +# pragma comment(lib,"python23.lib") +# endif /* _DEBUG */ +# endif /* 0 */ +# endif /* _MSC_VER */ +# endif /* Py_BUILD_CORE */ +#endif /* MS_COREDLL */ + +#if defined(MS_WIN64) +/* maintain "win32" sys.platform for backward compatibility of Python code, + the Win64 API should be close enough to the Win32 API to make this + preferable */ +# define PLATFORM "win32" +# define SIZEOF_VOID_P 8 +# define SIZEOF_TIME_T 8 +# define SIZEOF_OFF_T 4 +# define SIZEOF_FPOS_T 8 +# define SIZEOF_HKEY 8 +/* configure.in defines HAVE_LARGEFILE_SUPPORT iff HAVE_LONG_LONG, + sizeof(off_t) > sizeof(long), and sizeof(PY_LONG_LONG) >= sizeof(off_t). + On Win64 the second condition is not true, but if fpos_t replaces off_t + then this is true. The uses of HAVE_LARGEFILE_SUPPORT imply that Win64 + should define this. */ +# define HAVE_LARGEFILE_SUPPORT +#elif defined(MS_WIN32) +# define PLATFORM "win32" +# define HAVE_LARGEFILE_SUPPORT +# define SIZEOF_VOID_P 4 +# define SIZEOF_TIME_T 4 +# define SIZEOF_OFF_T 4 +# define SIZEOF_FPOS_T 8 +# define SIZEOF_HKEY 4 +#endif + +#ifdef _DEBUG +# define Py_DEBUG +#endif + + +#ifdef MS_WIN32 + +#define SIZEOF_SHORT 2 +#define SIZEOF_INT 4 +#define SIZEOF_LONG 4 +#define SIZEOF_LONG_LONG 8 + +#endif + +/* Fairly standard from here! */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define to empty if the keyword does not work. */ +/* #define const */ + +/* Define if you have dirent.h. */ +/* #define DIRENT 1 */ + +/* Define to the type of elements in the array set by `getgroups'. + Usually this is either `int' or `gid_t'. */ +/* #undef GETGROUPS_T */ + +/* Define to `int' if doesn't define. */ +/* #undef gid_t */ + +/* Define if your struct tm has tm_zone. */ +/* #undef HAVE_TM_ZONE */ + +/* Define if you don't have tm_zone but do have the external array + tzname. */ +#define HAVE_TZNAME + +/* Define if on MINIX. */ +/* #undef _MINIX */ + +/* Define to `int' if doesn't define. */ +/* #undef mode_t */ + +/* Define if you don't have dirent.h, but have ndir.h. */ +/* #undef NDIR */ + +/* Define to `long' if doesn't define. */ +/* #undef off_t */ + +/* Define to `int' if doesn't define. */ +/* #undef pid_t */ + +/* Define if the system does not provide POSIX.1 features except + with this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define to `unsigned' if doesn't define. */ +/* #undef size_t */ + +/* Define to `int' if doesn't define. */ +#if _MSC_VER + 0 >= 1300 +/* VC.NET typedefs socklen_t in ws2tcpip.h. */ +#else +#define socklen_t int +#endif + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you don't have dirent.h, but have sys/dir.h. */ +/* #undef SYSDIR */ + +/* Define if you don't have dirent.h, but have sys/ndir.h. */ +/* #undef SYSNDIR */ + +/* Define if you can safely include both and . */ +/* #undef TIME_WITH_SYS_TIME */ + +/* Define if your declares struct tm. */ +/* #define TM_IN_SYS_TIME 1 */ + +/* Define to `int' if doesn't define. */ +/* #undef uid_t */ + +/* Define if the closedir function returns void instead of int. */ +/* #undef VOID_CLOSEDIR */ + +/* Define if your contains bad prototypes for exec*() + (as it does on SGI IRIX 4.x) */ +/* #undef BAD_EXEC_PROTOTYPES */ + +/* Define if getpgrp() must be called as getpgrp(0) + and (consequently) setpgrp() as setpgrp(0, 0). */ +/* #undef GETPGRP_HAVE_ARGS */ + +/* Define this if your time.h defines altzone */ +/* #define HAVE_ALTZONE */ + +/* Define if you have the putenv function. */ +#ifndef MS_XBOX +#define HAVE_PUTENV +#endif + +/* Define if your compiler supports function prototypes */ +#define HAVE_PROTOTYPES + +/* Define if you can safely include both and + (which you can't on SCO ODT 3.0). */ +/* #undef SYS_SELECT_WITH_SYS_TIME */ + +/* Define if you want to use SGI (IRIX 4) dynamic linking. + This requires the "dl" library by Jack Jansen, + ftp://ftp.cwi.nl/pub/dynload/dl-1.6.tar.Z. + Don't bother on IRIX 5, it already has dynamic linking using SunOS + style shared libraries */ +/* #undef WITH_SGI_DL */ + +/* Define if you want to emulate SGI (IRIX 4) dynamic linking. + This is rumoured to work on VAX (Ultrix), Sun3 (SunOS 3.4), + Sequent Symmetry (Dynix), and Atari ST. + This requires the "dl-dld" library, + ftp://ftp.cwi.nl/pub/dynload/dl-dld-1.1.tar.Z, + as well as the "GNU dld" library, + ftp://ftp.cwi.nl/pub/dynload/dld-3.2.3.tar.Z. + Don't bother on SunOS 4 or 5, they already have dynamic linking using + shared libraries */ +/* #undef WITH_DL_DLD */ + +/* Define if you want documentation strings in extension modules */ +#define WITH_DOC_STRINGS 1 + +/* Define if you want to compile in rudimentary thread support */ +/* #undef WITH_THREAD */ + +/* Define if you want to use the GNU readline library */ +/* #define WITH_READLINE 1 */ + +/* Define if you want to have a Unicode type. */ +#define Py_USING_UNICODE + +/* Define as the integral type used for Unicode representation. */ +#define PY_UNICODE_TYPE unsigned short + +/* Define as the size of the unicode type. */ +#define Py_UNICODE_SIZE SIZEOF_SHORT + +/* Define if you have a useable wchar_t type defined in wchar.h; useable + means wchar_t must be 16-bit unsigned type. (see + Include/unicodeobject.h). */ +#if Py_UNICODE_SIZE == 2 +#define HAVE_USABLE_WCHAR_T + +/* Define to indicate that the Python Unicode representation can be passed + as-is to Win32 Wide API. */ +#ifndef MS_XBOX +#define Py_WIN_WIDE_FILENAMES +#endif +#endif + +/* Use Python's own small-block memory-allocator. */ +#define WITH_PYMALLOC 1 + +/* Enable \n, \r, \r\n line ends on import; also the 'U' mode flag for open. */ +#define WITH_UNIVERSAL_NEWLINES 1 + +/* Define if you have clock. */ +/* #define HAVE_CLOCK */ + +/* Define when any dynamic module loading is enabled */ +#define HAVE_DYNAMIC_LOADING + +/* Define if you have ftime. */ +#define HAVE_FTIME + +/* Define if you have getpeername. */ +#define HAVE_GETPEERNAME + +/* Define if you have getpgrp. */ +/* #undef HAVE_GETPGRP */ + +/* Define if you have getpid. */ +#define HAVE_GETPID + +/* Define if you have gettimeofday. */ +/* #undef HAVE_GETTIMEOFDAY */ + +/* Define if you have getwd. */ +/* #undef HAVE_GETWD */ + +/* Define if you have lstat. */ +/* #undef HAVE_LSTAT */ + +/* Define if you have the mktime function. */ +#define HAVE_MKTIME + +/* Define if you have nice. */ +/* #undef HAVE_NICE */ + +/* Define if you have readlink. */ +/* #undef HAVE_READLINK */ + +/* Define if you have select. */ +/* #undef HAVE_SELECT */ + +/* Define if you have setpgid. */ +/* #undef HAVE_SETPGID */ + +/* Define if you have setpgrp. */ +/* #undef HAVE_SETPGRP */ + +/* Define if you have setsid. */ +/* #undef HAVE_SETSID */ + +/* Define if you have setvbuf. */ +#define HAVE_SETVBUF + +/* Define if you have siginterrupt. */ +/* #undef HAVE_SIGINTERRUPT */ + +/* Define if you have symlink. */ +/* #undef HAVE_SYMLINK */ + +/* Define if you have tcgetpgrp. */ +/* #undef HAVE_TCGETPGRP */ + +/* Define if you have tcsetpgrp. */ +/* #undef HAVE_TCSETPGRP */ + +/* Define if you have times. */ +/* #undef HAVE_TIMES */ + +/* Define if you have uname. */ +/* #undef HAVE_UNAME */ + +/* Define if you have waitpid. */ +/* #undef HAVE_WAITPID */ + +/* Define to 1 if you have the `wcscoll' function. */ +#define HAVE_WCSCOLL 1 + +/* Define if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define if you have the prototypes. */ +#define HAVE_STDARG_PROTOTYPES + +/* Define if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_AUDIOIO_H */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_PARAM_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_SELECT_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_TIME_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_TIMES_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_UN_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_UTIME_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_SYS_UTSNAME_H 1 */ + +/* Define if you have the header file. */ +/* #undef HAVE_THREAD_H */ + +/* Define if you have the header file. */ +/* #define HAVE_UNISTD_H 1 */ + +/* Define if you have the header file. */ +/* #define HAVE_UTIME_H 1 */ + +/* Define if the compiler provides a wchar.h header file. */ +#define HAVE_WCHAR_H 1 + +/* Define if you have the dl library (-ldl). */ +/* #undef HAVE_LIBDL */ + +/* Define if you have the mpc library (-lmpc). */ +/* #undef HAVE_LIBMPC */ + +/* Define if you have the nsl library (-lnsl). */ +#define HAVE_LIBNSL 1 + +/* Define if you have the seq library (-lseq). */ +/* #undef HAVE_LIBSEQ */ + +/* Define if you have the socket library (-lsocket). */ +#define HAVE_LIBSOCKET 1 + +/* Define if you have the sun library (-lsun). */ +/* #undef HAVE_LIBSUN */ + +/* Define if you have the termcap library (-ltermcap). */ +/* #undef HAVE_LIBTERMCAP */ + +/* Define if you have the termlib library (-ltermlib). */ +/* #undef HAVE_LIBTERMLIB */ + +/* Define if you have the thread library (-lthread). */ +/* #undef HAVE_LIBTHREAD */ +#endif /* !Py_CONFIG_H */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Installer/LargeMOUL.nsi b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Installer/LargeMOUL.nsi new file mode 100644 index 00000000..e16fa902 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Installer/LargeMOUL.nsi @@ -0,0 +1,149 @@ +!include nsDialogs.nsh + +;-------------------------------- + +!macro BIMAGE IMAGE PARMS + Push $0 + GetTempFileName $0 + File /oname=$0 "${IMAGE}" + SetBrandingImage ${PARMS} $0 + Delete $0 + Pop $0 +!macroend + + +; The name of the installer +Name "Myst Online: Uru Live" + +; The file to write +OutFile "MOULInstaller.exe" + +XPStyle on + +; Add branding image to the installer (an image placeholder on the side). +; It is not enough to just add the placeholder, we must set the image too... +; We will later set the image in every pre-page function. +; We can also set just one persistent image in .onGUIInit +AddBrandingImage top 56 + +Function .onInit + ClearErrors + ReadRegStr $0 HKLM SOFTWARE\MOUL "Install_Dir" + IfErrors init.done + MessageBox MB_YESNO|MB_ICONQUESTION "Myst Online: Uru Live has already been installed on this system.$\nWould you like to proceed with this installation?" \ + IDYES init.done + Quit +init.done: +FunctionEnd + + +Function .onInstSuccess + + Exec "$INSTDIR\UruLauncher.exe" + +FunctionEnd + + +;LicenseText "Here is some text" +LicenseData "..\..\Docs\ReleaseNotes\TOS.txt" + +; The default installation directory +InstallDir "$PROGRAMFILES\Uru Live" +; Registry key to check for directory (so if you install again, it will +; overwrite the old one automatically) +InstallDirRegKey HKLM SOFTWARE\MOUL "Install_Dir" + +; Request application privileges for Windows Vista +RequestExecutionLevel admin + +; pages +Page custom nsDialogsWelcome +Page license licenseImage +Page directory dirImage +Page instfiles instImage + + + +Function nsDialogsWelcome + nsDialogs::Create 1018 + Pop $0 + + !insertmacro BIMAGE "..\..\Sources\Plasma\Apps\plClient\res\banner.bmp" /RESIZETOFIT + + ${NSD_CreateLabel} 0 0u 75% 100u "Welcome to Myst Online: Uru Live!$\r$\nCyan Worlds is proud to restore Myst Online: Uru Live back to the players!$\r$\n$\r$\nThis version is free for all to enjoy, explore, solve, and share with others.$\r$\n$\r$\nSign up at http://www.urulive.com $\r$\n$\r$\nEnjoy!" + Pop $0 + + nsDialogs::Show +FunctionEnd + +Function licenseImage + !insertmacro BIMAGE "..\..\Sources\Plasma\Apps\plClient\res\banner.bmp" /RESIZETOFIT +FunctionEnd + +Function dirImage + !insertmacro BIMAGE "..\..\Sources\Plasma\Apps\plClient\res\banner.bmp" /RESIZETOFIT +FunctionEnd + +Function instImage + !insertmacro BIMAGE "..\..\Sources\Plasma\Apps\plClient\res\banner.bmp" /RESIZETOFIT +FunctionEnd + + +; The stuff to install +Section "Myst Online: Uru Live (required)" + ; Set output path to the installation directory. + SetOutPath $INSTDIR + ; Put file there + File UruLauncher.exe + File /r avi + File /r sfx + File /r dat + + ; Write the installation path into the registry + WriteRegStr HKLM SOFTWARE\MOUL "Install_Dir" "$INSTDIR" + + ; Write the uninstall keys for Windows + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\MOUL" "DisplayName" "Myst Online: Uru Live (remove only)" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\MOUL" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteUninstaller "uninstall.exe" + + ; write the run as admin for UruLauncher.exe + WriteRegStr HKLM "Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\layers" "$INSTDIR\UruLauncher.exe" "RUNASADMIN" + + ; Create desktop shortcuts + CreateShortCut "$DESKTOP\Myst Online - Uru Live.lnk" "$INSTDIR\UruLauncher.exe" "" "$INSTDIR\UruLauncher.exe" 0 + + ; Create Start Menu items + CreateDirectory "$SMPROGRAMS\Uru Live" + CreateShortCut "$SMPROGRAMS\Uru Live\Myst Online - Uru Live.lnk" "$INSTDIR\UruLauncher.exe" "" "$INSTDIR\UruLauncher.exe" 0 + CreateShortCut "$SMPROGRAMS\Uru Live\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 +SectionEnd + +; uninstall stuff +UninstallText "This will remove all the Myst Online: Uru Live files from your computer.$\r$\nHowever, this will not remove your settings files in My Documents/Uru Live. Additionally, it will not remove your Myst Online: Uru Live account and your online progress will be preserved." + +; special uninstall section. +Section "Uninstall" + ; remove registry keys + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\MOUL" + DeleteRegKey HKLM SOFTWARE\MOUL + + ; remove files + Delete "$INSTDIR\avi\*.*" + RMDir "$INSTDIR\avi" + Delete "$INSTDIR\dat\*.*" + RMDir "$INSTDIR\dat" + Delete "$INSTDIR\sfx\StreamingCache\*.*" + RMDir "$INSTDIR\sfx\StreamingCache" + Delete "$INSTDIR\sfx\*.*" + RMDir "$INSTDIR\sfx" + ; remove shortcuts, if any. + Delete "$DESKTOP\Myst Online - Uru Live.lnk" + Delete "$SMPROGRAMS\Uru Live\*.*" + RMDir "$SMPROGRAMS\Uru Live" + ; remove all the files at the root of the program + Delete "$INSTDIR\*.*" + + + RMDir "$INSTDIR" +SectionEnd diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Installer/MOUL.nsi b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Installer/MOUL.nsi new file mode 100644 index 00000000..ae53c399 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Installer/MOUL.nsi @@ -0,0 +1,147 @@ +!include nsDialogs.nsh + +;-------------------------------- + +!macro BIMAGE IMAGE PARMS + Push $0 + GetTempFileName $0 + File /oname=$0 "${IMAGE}" + SetBrandingImage ${PARMS} $0 + Delete $0 + Pop $0 +!macroend + + +; The name of the installer +Name "Myst Online: Uru Live" + +; The file to write +OutFile "MOULInstaller.exe" + +XPStyle on + +; Add branding image to the installer (an image placeholder on the side). +; It is not enough to just add the placeholder, we must set the image too... +; We will later set the image in every pre-page function. +; We can also set just one persistent image in .onGUIInit +AddBrandingImage top 56 + +Function .onInit + ClearErrors + ReadRegStr $0 HKLM SOFTWARE\MOUL "Install_Dir" + IfErrors init.done + MessageBox MB_YESNO|MB_ICONQUESTION "Myst Online: Uru Live has already been installed on this system.$\nWould you like to proceed with this installation?" \ + IDYES init.done + Quit +init.done: +FunctionEnd + + +Function .onInstSuccess + + Exec "$INSTDIR\UruLauncher.exe" + +FunctionEnd + + +;LicenseText "Here is some text" +LicenseData "..\..\Docs\ReleaseNotes\TOS.txt" + +; The default installation directory +InstallDir "$PROGRAMFILES\Uru Live" +; Registry key to check for directory (so if you install again, it will +; overwrite the old one automatically) +InstallDirRegKey HKLM SOFTWARE\MOUL "Install_Dir" + +; Request application privileges for Windows Vista +RequestExecutionLevel admin + +; pages +Page custom nsDialogsWelcome +Page license licenseImage +Page directory dirImage +Page instfiles instImage + + + +Function nsDialogsWelcome + nsDialogs::Create 1018 + Pop $0 + + !insertmacro BIMAGE "..\..\Sources\Plasma\Apps\plClient\res\banner.bmp" /RESIZETOFIT + + ${NSD_CreateLabel} 0 0u 75% 100u "Welcome to Myst Online: Uru Live!$\r$\nCyan Worlds is proud to restore Myst Online: Uru Live back to the players!$\r$\n$\r$\nThis version is free for all to enjoy, explore, solve, and share with others.$\r$\n$\r$\nSign up at http://www.urulive.com $\r$\n$\r$\nEnjoy!" + Pop $0 + + nsDialogs::Show +FunctionEnd + +Function licenseImage + !insertmacro BIMAGE "..\..\Sources\Plasma\Apps\plClient\res\banner.bmp" /RESIZETOFIT +FunctionEnd + +Function dirImage + !insertmacro BIMAGE "..\..\Sources\Plasma\Apps\plClient\res\banner.bmp" /RESIZETOFIT +FunctionEnd + +Function instImage + !insertmacro BIMAGE "..\..\Sources\Plasma\Apps\plClient\res\banner.bmp" /RESIZETOFIT +FunctionEnd + + +; The stuff to install +Section "Myst Online: Uru Live (required)" + ; Set output path to the installation directory. + SetOutPath $INSTDIR + ; Put file there + File UruLauncher.exe + AddSize 680000 + + ; Write the installation path into the registry + WriteRegStr HKLM SOFTWARE\MOUL "Install_Dir" "$INSTDIR" + + ; Write the uninstall keys for Windows + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\MOUL" "DisplayName" "Myst Online: Uru Live (remove only)" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\MOUL" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteUninstaller "uninstall.exe" + + ; write the run as admin for UruLauncher.exe + WriteRegStr HKLM "Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\layers" "$INSTDIR\UruLauncher.exe" "RUNASADMIN" + + ; Create desktop shortcuts + CreateShortCut "$DESKTOP\Myst Online - Uru Live.lnk" "$INSTDIR\UruLauncher.exe" "" "$INSTDIR\UruLauncher.exe" 0 + + ; Create Start Menu items + CreateDirectory "$SMPROGRAMS\Uru Live" + CreateShortCut "$SMPROGRAMS\Uru Live\Myst Online - Uru Live.lnk" "$INSTDIR\UruLauncher.exe" "" "$INSTDIR\UruLauncher.exe" 0 + CreateShortCut "$SMPROGRAMS\Uru Live\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 +SectionEnd + +; uninstall stuff +UninstallText "This will remove all the Myst Online: Uru Live files from your computer.$\r$\nHowever, this will not remove your settings files in My Documents/Uru Live. Additionally, it will not remove your Myst Online: Uru Live account and your online progress will be preserved." + +; special uninstall section. +Section "Uninstall" + ; remove registry keys + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\MOUL" + DeleteRegKey HKLM SOFTWARE\MOUL + + ; remove files + Delete "$INSTDIR\avi\*.*" + RMDir "$INSTDIR\avi" + Delete "$INSTDIR\dat\*.*" + RMDir "$INSTDIR\dat" + Delete "$INSTDIR\sfx\StreamingCache\*.*" + RMDir "$INSTDIR\sfx\StreamingCache" + Delete "$INSTDIR\sfx\*.*" + RMDir "$INSTDIR\sfx" + ; remove shortcuts, if any. + Delete "$DESKTOP\Myst Online - Uru Live.lnk" + Delete "$SMPROGRAMS\Uru Live\*.*" + RMDir "$SMPROGRAMS\Uru Live" + ; remove all the files at the root of the program + Delete "$INSTDIR\*.*" + + + RMDir "$INSTDIR" +SectionEnd diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Installer/MOULInstaller.exe b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Installer/MOULInstaller.exe new file mode 100644 index 0000000000000000000000000000000000000000..ae6da08bd5b9153cdc1034292cae2c28d726d0f8 GIT binary patch literal 431262 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
    ht3uo|P}}k9EC?6@riv@Ike#pa<%h0h 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≪88i;F0j$r&8_W$(3oj(276u0d0HR)ph79=9pg83; zGBvBVhZO-hb=Q^UOUmK{^28}Nc0lbL(ApC~HjCM0vs!_HoB=cwj!Qfp01%b01td_z z3nH*dgAPLuh9w4KG0+bMGLL~WodxQ!)L>VE+M&X=_>|Cpogv^MW85Y)I;C6PQdeBT z+tjy-jSfn0Z!kLLRu9b5a7QbgVHq$mvmoxfB%OYZ>3xpreU)i{Oq^XOZ5jBhwc&y) zRNycH6fT(3E&3*m_Oz+OIc=gv2vK}F{RbYVqP ztNrHrmgj!vyzmpkyo1=fLlN&*r@Q32DaH7`?DPZjfjh*Jb|q|1|BpO^r7$RKkLu^iko=$M%h-z z-!4y2DJqM~&NX@Ky0o+|EgX;(mL=Ian2pgMlw>ExnQ?K`j3hTLZW^Og!*sfz%Jh)& z3K`0i{*=g{pqd7l$|6KzIuES`S8kE+yi>mKcjB?vw_o|kwo9LIGv6j`Ep?$D*{xsM z`oO2UyRRwt{!%gfZE^R#La3-FKSoQ)SSN-4KpN)fUiIKnUK8m)?mYBn_`W}=D(i~e z9(iuB65jjPW75_QrnoF_J0b0Vn64a?_TJBw&rrEzrjakk@Bi3-{CDc<4%ddD$+09R@!3AZLwyArc|9NU2n^YJRM}{7IMb~mAVz~UvU%WYfz=`Av23) zVwwoFi+rsBLs7U};D=t^Qs*cOfPvnDI(Kiqt4$Q2B(n=teh(QR5(PR$K(o|5>Il$O zk*k~Vc9S7Ywaij!*n38(7`%>CRAQF&_n_v*izj>oL=blI0Xj9!;9oCe7UM8LvLQOn z>%uAy7${-2=%GPDfGrRv7PS6ge&zv?7Nb3gJH>k2j@!TGx$Rq?wI3Red}zM+FAe^l z8=-#Ez&ZvcFn@@Egg9rep8y7Nd;Nqzs>~2D*nRumv(Nt~{G}fl#?FY0q5t=Dp9g`g zmo?TaxLTRIR;t2*TDVTi)k;{QLR+siY!)j*^IsZx?&IwJA6k#RvGohj)X9wkx}gE{ zEUcDdH8M_*uR@`e0|N<-6UH9C6A1f_KcQQC%5d-v*TtU(pZG)U;a|Xjp=o5DCY{+6 zqcv8sN*nh1>_7&+_J09TqiED{J9=E_e(bvNYx{*inpR#VSi=@`98A*y$yv>r)Tj}i zhN;7baD+DwSv<~w$7caJY0;?z&%<;ftJR9_(a8s1Ir4{Vec$@ncIsL4%;AQP>Ce;& z1tLPgnwcsFLY_bgEK_6X1NIjK8h%Ed7WB`KsSu;qX3{w=AbX&QT8q)(fIJK^)ak88 zqr+%asyXmyisfj06I41Ckj-FVby|oQdaGY=4=FLkVfO1RZkTVt00DqD8(-&Yfzl=u z?tl+&DC2Sp|KtE)QtWm79$!uOQMy1^+@t4H?kI80UmCb&O z$Q(qWRHwGr$W)ufax{K*(ULk;)TzXJbm{63K6vO8fV;7Yp=cP@9 zs#sZH8q+k-={uJ+#YtIWSe_YG=5Y+e#-ME3tLi+Yh7$%RnbICb^FEw3cCKx|>x1CS z*Ysz9rf4}+!#c#F0YzZ}L{T#UpW%wU<0vl>_ue6E-H=ueD9d}KFb5__fPNTc6Z0@Q zEiUe*%ky-m4=M&v0^@BYM37XEC{|(evrO||sxT=G4!|NJ-?`y^_*3g$f6*+yC%x&= zXGM*h^*go%I;r#lBDN&(^wg>yLRXxKR>(-1N)1x!0W#IcqX z@H6AcUGmI4TUk+7RwZ4h6ocm>--u!Jo_bl_bDy;5E^+HwGP_UTe=>gM|Jsgx)HwF8 zK)a*PS)p!%XxmgDcG9KYn$e5FFMp)!`-*5=fpj$s?FG;+45^XfT|@x2?GzoIpaQ)@ zdq!wWlHNAbKR^aYn8Y-^)&hI;W^Vfpa@!43^Nk9}W_6%Om#EtcFih21Fd&%)LW;I` zk~>Dp*zgulhrkJkOve^?%NB2&Xgj`3Lu6tc1+~x*^xIiyxe2zJdQYFw(<}7%@luYj zBJ#pd*e3Ax3j%{eZx4aj7KImmlupfJ1cu2kLnQ&?iTvGke4OS>;5NcpAYdAYsvha3 zz&t@>gF}qJkJp8D26$u8oX2H|q8OGH5wC!N_(=qm7*w!&Cx(S-`|$blmp%%A=`V(p zzt#_3tQQe5PUFE)kZY?43f}8gFi!%3c+gPD|359CJ*XjozFH~JPhBrl2~ig-b@d8utyH_2k-H{Ov^@OF(7E@we&KP$@*{N( znmU3JD>ZVB0h6=WC16-!q(+z27KXX5k!_S=>}&eo#@&w_j(l_5T|e;N|MLd3SEZ1m zMD|+s7#k6(Ot|9xE=QGv@ztnWXH;lStjXs({jTS}U%T)6x#`IJTegO2g&MQBn5ltu ztI=7dj#jzBZ5UlKGSiaJ(fcUfI_r&MFX17J1BHxmSvHu|@2Po4gcPv$=ONA%_2 zZoB=mZsK-9CJsxT{vpQ3w!KyY{8+BVzH*d4r zwgT}`=<9e_4W3G?H#F+>(7rh>gcz+v1uHuykin}P8zW(|KEf7*>^@1+44!_Ksg@FN- zpk5cLk#aR+X^mJ`Bh}R6$F0yQTyb5ZLljGt0QJJiW;E<3JnU6&1GA@)f8wqa;`Vhi*+m4JNO-3q3a5IQ(!40pwppq+E`HVj z&|kMdd(HpkwXKJLBvkwUZPVt@)d_A8NjEXt&2so-b%d{pj1&o9f=c$#*$IBwgl|js zDqGh)N4{&Exu_pL894oIb@Luoeo6_9ZP}2IKPDf(Ptti>(sM6AlUspL_#By-G~9B2 z$J76^o&SU#`fhDwsAlVSvI*R&e~Z~oIx^Ju4)fRv_24Noc?**qX97b^WLzAZW+DLj z-IB}_?eC`oJ*2N)XbOwWVbaq^`ugCkmhN0?Xx$*T_iZuXw8fGa*a~%q7!;*??e^M6 z*Jg!vlhn9LX4)jT+^BNaXuLHVPYvhY-00b)+;*edU#m~lJBk8#v%r-_m#Kpo-aHv#LIq!&umWqKm_+g}| zFs{Q_^CQxUrtUfPhI_e^8Q~~_k4Ma&i|QBwKa^Q9^6mHp&5umSOC)?BofXnP#S7w zJR$Uq9PGOCtMtP^-@5-X{hrIhWxvtUdSC|4yOPIE$DNBNNVxl1~wZvtvg4B@{+w&58TGx5V zd)LpmuD)$M@?G}k1sQN_ZaBbLg+s$3DZC#rIW>)3RS#8@l$*2*n4Qgxlu zU8k_(=Zg|Rgocd8rSc_==~i8$SsN>{5R1JDsm;q6txB^YHoe;QwO<5IziFI#Mx;Z( zF{De~tZyGzP7bLHPR<=xrv^0HAvV?p3{wD_=#=Cyp z*nd~O&M%8~v)Kt{VMbm;J8@poJST5ilxBBJl2a1CzMB;T26MO_m*giTm3c{NN?aI& zHJZv#(&<5Q^L}wwRN4En$P>&Vle2A;hZxbuC_J^!I? zKl=ZE{)W%j3(;b#k!yK*S{D>~3skb33?#{L6P4<&DkFX3^oX)~zp8!BvG)mm-*LagDZZQOK(oZ1 zlXz~TEO8;{66n1YOjAnprUvcjY4-DE;|+}V2C?Sz67KU7%?%B@&r9^5XSAQCji05r zeok!pyv%)r!m~-`snJJjbiNz8z$R@BAKu8?YB_KH*0|7?7TQxnM{|eL%D=BeKRwS3&71T*p8FAcr9ZHOI0ngR&Px912*Yl{^3{)cl3xYc(!@ zOF#EL?d&%jr{9nTM{4T@n27^d#C#m6Adi7nBm{4S2vSc03>CmYoC5=Eu2;d;mY_f^ zhsxaOi2!3joWIV!_Ho;lU%6MGP&N(y+vhfI5{U&o2C8c%syZplyJc8CtTJ_S{vK$= z*3S8sC;t$+{GopF5#!P$0+qEMGb8A#X&to2PN=OSwV8sq&=4ds3vY~J8drPdTdqfb zyZy;OZGY;IfeYW3TVsuupxhiXyCPb%M_@{;?NN`*XGU9059vYYcYtvjK`gh;JsLgr zUCnJTZC!s`|BvYpKJ~x&8|~8Fwl7?$bq8;t*e#q(taT`1!o~o#(O`rD zMyK_gbzY0!YQTW71|p4(e{tY`8dS$Nqu#9N`5VlE5WQ86YEgrthLPfyl**_E8So7v zxQLIfP>Dd*B&vFbtrsh|V4z0AZINn(4SJz$>lTGwr1B7&fJhgntUD=tD`mwX#%;>f ziZrrYk)BsJttoN`+1zSl`KY?%L0#{Y+Wtp${f`-XAJnv6>(EC%Tofc6Gc%o9WSId)VBpyzNf;(6h?vucKU5RrXlA zS1cWKhSI36ILPJtw3RUpj^+ujZMUj*nd@BUI`*hq_pohy)os(t+)!ilZP71(Km5uk z*2n%+Gxcz7qmA(u6`2w6D+RI_U@5GO%ZoG8CQQlglH~V_bBhw#q+x(5FG|V_Vx-FC z#>EwMpWqDaCsG|mx{FN1*Efi$1eCF4yn{^jP%!*QcIqZBMW6gj_~GAgJ^GV|Q2(}t zhn=TC*eo@CmT1_7pQ9xy^mWvOP+6iFucM+Z2%n+gDok{e(Ka%Se3$`5#IVI#%jCWK zjtzasx_$CaEjj~*y-a?IMhHo6iJ!6Ux}60X$+t7PabK4d5 z@F_*_arO8`n10#0r?;JZ-F5sW&BT40nM>B?M@&oin|I%}ZSIuu=6%ZiJQM7o{VlYw zNSMM~wBC9EoyJ?&xUEiUt5w=-M1$#xVorJ4|5}w|c=ly{OD{O&02~n$gD+pq()Eq{7F{h95UW}H? z3{GkLbGoT-=w{y3On!}>c!@NmMIu5flhYF3KO^E9pj2436!N%tT~lUwNdNFfwLp)mQgj-K{xYIc#q4VRb5UZz}KbZ|hjV-gT#?77D< zafO?{%H8@k?eKG^-LIPVy`x+Bo^j=UdecOK9+xFeBY}@yi>)`vIjrX>#e8+R>1OM&c_pW@?^M&s^ zm)>?QzvW;57Cv-uyyIH^j{COPUHe}3-Tn>dZ7EkwEl}A7ydzXkY7GR-Q7VpBvm~b_%t6wg5qp~C@qSIq2Af;pic7lE zetmp6x^!2^oBtQ@Xr|>i35~148ZxHZ42iNfUXtZYOd+L7wsDCLZMxf(>(w`PtD7c} zGpV=bg7jE`Bro>R;V2e5{?hQm=7HLp#~*FdyF4yvW1|y)szu^82K%M`f*W zR(!3-oV%~OET$$&ivC=(s%OcahLKZQ?-%9d5y%sWDrr>gcyc=!bcIF4^ zul_S|`PVQZd|vK0Ok55<^WU=c)MvIxZa}-B(OJX!h32%-9HkS$z%HmQXeaV;nnbG~ zRUU$~dcjUr`B`Jvil#6wc7^q=d!%_din}TJQksubg*B$@JY6`<4^yO;j1%`~Ui{a# z3;$$?zhA2ef^KbYwANd4-1wVJXkoLtjo1M-2VFyaPEU;B0Dl@C1+|782sk5XUzW#Z`%0++rW zzx<}}#8vMduLSOR(Y5l3ZO{GiBpb)hSm*9E58bXP&&!L`RZ|m0oB=90NL0Ns5>|sj zTD?xIz&wQlE{v~wohlYL68umO`$FN&yN~};I)QcZ&Rwyt5 zX66){t=qP^GwkAJ?d>nwpZwVU{NF+s-`K!vr!0cZw;PsXXy<;N%Co<(D+Gmv5B7FVvvM`-0I8aO2G?Dv5jSY}?wS#;#M! ziRU!gNzNICmTK-klskA;r(~c<x!EZSaKG@JX z{y70H;9L+afNu~wAaekU&ER_`V4wlFRwRQNc*+T{jKhd%1F%G$9Z!o{5sikkIHgvf z)SFVqIyAXaL+g_5mfM}vr?)R&46i;CJMvWQj#r{*UXP!BBXQ4ncij8E*j?XDKJae( z;U5&9_~p(Qe!KIbHSAernzG8ZZzYV3DGcP;mlQPiV|g`6(S*h(?k> z^e_WZDmA56lbnGlt}2Wvpg30+6m9d$&OM6G!-^h$woZ!Fqsjqkc|YG9gzK*`DQ+6T=%}P+ z4_%(2^P@DTgR{dt`X$=PRMp*wn8a{1wWCElaw2^7zx>Z%b3X7fyYq-3y`-FaCHc^Y z_S?Vlxh>Mqh@_hvU3KQHz+M#U{6w$}U6@Mr($NkwSRjLW98|c%WJbiK{ubJa*KxX2h&gexBH=gX>a=>+yAO|=55`= zyT;o;G#>hhaP@4~I*8OTOpHXRK!nRgyj>K@i$YDpNKq7Prb0y~*v5ET7-uu%?P8*n ze3LLfBaTj(Z+p=D#80=s_>=f+zmLB1QRt-)gD?Lk^X6}}ul&5}<)1gd`K#P3zbL-_ zhr(OGyXlSJ?0o01{XhJ)`@K&(zWZ_a2mk2*>3kouCF8P)(n8%Omd%n=vvZn#zHlY#7 zH;lWL&rsE9Oget}K*{nksxa%SF{GeU0We^)Lswj1oC_|#mpb)3+tFWhGglj%kJZ%F zG&D5Gt07vHlqZ4pKsH*6`C?Ln3m@MnwXXIC|EDyDS5U4ND722AsheN? zIDX|fwv|Uk#^C2_>v(guL{Tqg>t%{sV4#Gp5i0qshZxclRFGZv4tLc9*3v=#601tJRh5Rcr9gQ^VkgnKlI`R`hnX@@^X4 z+xN9!x4iOE;L7*RU${?t%hG3Rg+jSjtbvI}4+z9;vX;X;kp};S%glRVz|6J$&qY(M zi!n5(LvIjmKs>KetL0rx3N`FZ3XN7-l`|lTKsP~ak%PNXZ%`p40;sultKIE4dct~d z)E&Rco2djVy^+qb$j#HSf!Wl^p4{w#?C#^~r8^26544~9O6S>U+Rr}QdDqu_AN*F| zL+=bf^P}+>e?0!xU+ntEA8-5SCkx;HWaYhoEWi7=!_3&NZY;O&}{n zDZ`r`tv8c;7w8QU8WM(vrS138@p+*!EDrb8YaK$+duvSPN$XQB(dF}9Z-1Kn>ffvf z-lk1qSZ!Ok$+FU{V87y zPd*^fxn!|+wlJkcE)7otcPqPB6&?Fzt*fviO9~5;%96D80B$8^r1R}&APwgy#HCrf zyo=8Eli?g0&XA!dD%pn=LAtmLat4)y-)zQl;u|~Ox)!+l-_HAf4QCxuIzjKe+jrm@ z-{XJYVoiTW)L65%KqmJfvqfl15dIuw5s?ok8H%rnyFnwVc(1r=RG#Wk=X<%PPDQjN z3zx0k^YUE3DAY&B1~48X9lRvzIZx*g(v?$GYDRy{#pIR0M4$Yp`Siz{p%)y}4|~7x zboA6)p@;r1%dgg%V+7iOIYb?f5KSYZP(kR83jGOTAT11M1)-!clothxq_+q)g!1-^ z6Z4X$eUkKoecz*@=YQGs_Fu~HUn{=*&%$^9l>hD@OW*y&&ToCx_k+J4`1vQD-}&v$ z@BZ=T@Bg9s?T^~N{c->Me_r_MCv)%pzU%A1=zQZ>BX4}z|JsMcul%Uz`FC2byj6MV zwbn~lix-~DpM5%d;Iez^5%0>A@wG1p=ZU?8r3^rEy{BfJ@`8f|%l z5ve2yPf@BA5L*?jt^ZW^)bG<5J~bcu8F%ZG%KU-a+S&$5gB&oGLtjsORd(@dtluTYjKnd)sG`HbY7P zdOQrulz={**GQBCw6+u+auq`RzmhujZP_gkHBMbL?tf%6qZE)dJb}ta4f44fBoc6{p>1Fd$UW33D)2kOws+23UetohI5b+VR4Fw!Hpv=#OQO{d$G@Q)@D zYTjmY`0SprJ($4Tfjw1n=R1APxA@z}{N2;hTlPf9mJ_oF)3<%Gv~pL=kq6pNUg`eQ zGky2IH1y!tM;`yy#54asdG#l^eD#-=EAJ}s1W2 z22?!NDX61)U8YZ5z9MS-2@yNZMte==aeZ-&OZO1o3LPI77nfu$CzZv$%6NyNYoDQS zU0t3~q}xIN^u2quowJ&@8Lkb*@2skIp6xgQjwjCy$&lHVyE*pdZ>Qe+#P;wn8vD=H zX`C2RR#bM&E4!;aa2es!ijKYVwmEs*vb4M`E$x;z@0D~M7PqX5D~poW6$yf3n$U`e z^R0>Wh5=bXJf<+mG%txe4>9cV+fPBbOoWWtBJ4t zPCNO?XST4wK&I^im0CpYhPV$p-bF{@A?c!vyXn#bouB1Ll3~nFb}_MvG@KQOa*A}n zD%B~=43OD9Fej66?zNth3|?kB&N8KSrfIkFmUHPx|Kz{>54x3~iA*VsBFd9Di?b84 zFMZHBa7pONi?)}LC_*<)5(s)t_7Kr_QK&*h+eFE3QKDNE>kvkoMe%krj-ihpWYUuU zZty_g#uNEhKFodXw}n^#n0@BwnaAHt-2Z0eOE1RGJP|wnRQk*dk;9K~U%k(>@FnN& zd+fXKv(DV@oV{QhKBjG7Qt!M?RhUxbrscUgNoE&sBg`VDc0!ULmLOy--j8_`KA$1E zT8+ORq2t|zuS4MK5e6sfy@U16jyh|l9#MzEZjrxL;Hn7hIe{fXIMN7@rF{j;ohI!; z(w(5g&HOhI9nNY$5QF)MOM+uo!IKvwJZ=V!3gQT&pjm{e$`t;E$66l!SNg8M8uxvm zo_judjTAAOn; zOZ6%iDfAAc&?9}?;|aQ)n6EVushmZr%uz|aNWLf%#|H`%G<$tAD9_kpLh-FKeY*wl=0Q&g8 zolc`Ld8M|fo@2KcJBQ!?ay>^HHfcV_3m6II8R_5rVbj_?oZY8& z1~kr)#uZfYQGs?uK!s~6Pp~8MtW4O>s%j#uP0%CsJ9mdj-oVoSe84&gvVsbIdt8{t~_lHalxFisT~6& zmRxUe{#5&Ge{6c~AI>}8C$>f!EdIt&K^e}g<9S1>MVj76mS2`-PH3BkHLyJg^NiI; zXiS9GN*ZjW$tluXC?wk?IymUUt-G~7`wfGuhT#MHffZfvlBR1x)v*_eTk4)es-7dt z=DqUl2wNE1@xV9JZ~W1C;e*D(J8Lq zNKPp3lD4i%TKDtK!15k3!eQbSm;*rwd3~gaS?Qxp`?|PgAA`Vz@?I);t7*@b zl+KN#O-JYF=;9taH9#lYCCLud@?3sMmF-1BAclDnx zBU2+}4gso|_H7cnL!wZTNc4+>Erc6p!ZPjcq9bEWY>tV;pfXB&x(H`8>1xMp8j}4a zP4n{73M|~xEMj)%`M~Y$f;79t#3o5!2jQyF;eHCk!k8L%x1no?nQLifQQo#BD~*dY zy~yPz;mStn0iK*ssqH8IEdp0(oxP{l)>-T5sBlVM6j0#Vv3|$V2TK>aXL_8 z;$2LzOxPm?M%Ud5%3q{hSzsW@4-TLos18an_=FX#nnmW6mLI>xd1Up)uIH{LFZ^rk zfgf_SFDeRaz(7_Gw*|i+gx@40LDm)kP=z>sWG}!@hA|V!!6<=AnezI02&f>87QjGA z9(6F&N|Za6PWHd@=cXrqXbcj1na?A%(*xo7&G{>k98zncBlU)$dPblZdP!faEs z`9_8!wT&vL+3a;X15SsL2LYp{Ds66fR#(Zd%p29mpU`O+#zqOtG0NF%a z0`0XmHFAFFgnwVaB#1@F&vB?_V*mV!)VHryes-53jX<^H273ub56eS8H=I5*cvLZGk(P&#*;H*23Fk zINr8bjmCmm8VU$1Wj&ve0NSdrMyhfY79U{BTMaP8kfRzy#A>ZpiJ=&{#ML@rpjxL_ z==fWREvSom{4`xJkPRTa2L40vK{L$Hhz-PS2xf%Uytj}?LgZ;-bPl007@tuZtY|!H z9A3`pXRThP#UnGjq-Hm5bSUh6QkEQnfmR=D^K;H1pQMV(5nlm`qtZxk9cLBCMx@a} zIU4wQ7moCz0FgCsDElwb2wrZxLoxdfg^14d0Ym{){vm04zc@UsDjrf~_LAlVZH^#Y z)tc+G7693x1A5ak7Eq>MZoH~+6sJ=Fb1qZEiUqzF;ZTsczOS0A_MfW-mdE272 zJSQpcmgMJ=V9&!~=Mn4*VUUuVXrt5Z;AbB*V{%dmj@;MofcWNPP8E?JZ|NCwGbx2oxRTmm^p+ zbY_e%v|%qqAsla`pd1ma;T7-I;P0d^N!rN>Zk z=EQR?kNnbi=KIFwD_i$n7ASNy!$7~OCaIuZrbX10-D*peJBQ9ZJoE0S!B_sM+jEZg zr#IKs5L<*gwR)S?vdv-!L||qN;sR0;{B|?2UZKK-8b4;jC^b?}=h=~KfBN^)OTRI# zz1GmOQd3)F)59dG#C(VYQU%|o!(5r#>9D6xJ>LDF|17?JEj)ey;M~4asceTWS%rXm z4v%5=cBwVS=$#ss(q=U7oWFhX7uTBK{>!$zUN;@Taswq6H%QHT9i~gb_f%TWYSN)~ z2+oHu2;2=#StKP`_>e%Io;Smw$knolG(-X+;s;?M!FaWdMJ$+_|0{m!Qneq5*T*wJ zd?6}rb$J0FEQ5Jsh~Y-1)=)JfVo1cy2X-Md(8%X(V=NA%B1n=k^7F@hih{8^KnHU* z_P4NhkJ8~++At&R1_PuFPKm{>a`;sCfPxQ;_Nr_?ttY~{;z~!9wK&;kgxJ4YXGzE> z-jY%Iu}Beu*o_I3i+KOyZ6!0~vL&N#%{G(w$uQ zKHcbX{mglF-$Ax>L)Cpm(YnS~77};A9(eK>TMxgbxM_Y9ttLFBhMN{;Z5#5={j$oe ztbL!nV^zje!Tpjvrb0#~`CXFSn7j*{aaI_FA>>D;o$J!>V@zoQUT8i+3q>msthgv{ zS)*H4+4i;Q3;&2e|998jzvP;4ud%k$9T%n3-w+QxNhBtO_Hgp@kF94u*ethi;v%)? z)D{pba|+Tha1Vw<5|RoO;dlp~=#VxI^QJ@yF`%89eo3ZZnnr*_AHPirM|>2XVdSQw z@5&@6l!VA+$ z(%(!4@#H|lZYJKx#0Mz*4qza3QK!Z1v%!wb(*}M}%4)M?qKco)vS2nLx&Ct7BiB;r z{$bet9reU>Y;g^m2*pSsuJVzCd@wP(QhW+4X4BwItLMWO6aYX}!92CAW^lnhSO;vB ztLkw;?Ui$t2VT4R8~?rI`A=*I9=py1Z{&HP5)2SH36O+8kc8h)#q&T$t;^m1*x=Pa z-UM0j@Jr@H&u>v}6^kX+EImGI4^XJ%GgWW~nL(E-G!NeMT=zFV4m|%GtqPg_kW3_bAO8MIilCS$ey78MPjVFif+K~k<5F!0Z*hd;6%eVZM>uUM z{r&I%EBX3gUFTjjuH5~3q-r-b@Ow|H5Qofvfvj z0C5;9VbodxAm22Qv3xSVw)!P-d`l2)5EI1Udw3W+fn1dh@)!v6XGBN>T!) zFmelldo~3>3ydKWevHm|oeCm4hELOiPtm2ddo@lUC?67F_*;t$TSAm}Kj#Rjfq^z3 z3Sl)qgagnS#o{E5BcyeQrS137120P^ex_4bWdPXyL7dLvZs`rcX$`_=mQOg&ex8MH* zj$SG`v32Gvk!Sz1{mDN&&;6jTF}z8i6XlOG zLr>A|NU55vHTrD3?sMM%p|oiax`H>zEp?_OVL@Xbv8cVgtQ=|P54dY$%}lzRNwo4j zFgqkG%}Dbj(hOn`yQ>RLP#AXbN%+YHWT(*an;CyrRoolA|3A0g{gGz=I|8+TGtBU6 zi;%M+h)mscS-boDBCVfs#>q%40VEEC9K10G(^Whhlp!slhR@yNy)k?(EQZ}hiJ=DI8>-aqC*B?U=6@$&{KvM#&%>R? z*TQr|RSn@46SM>grH%jU;jDu17zV^{#H#Id9ywiKfMBl+^LOt(K`TD^BOQVtr# zRs#S`i)bthe;C2K!6Bi=qbrDhqPeOJ^X?ce|0OD`COof`dLRFr_M zMar5Ko5LDk)|MYYc9yZJ-;y74-@I%eKIc2~diP5|D?R%o|06%OEmq@oP z(xoo!(R8kBhfZ>1m((M7vpq*uNRNSsZR%0~+B41rUkyC+qvY3q6My+n+b;i@>pRXw zI)#p#kiD$zJ1p-xEp0z6Eidp@aA8KA?vs{LugxPDMGV%5`MA;y(~7>|KBjpd$werQ zDcGA6?NkOq&;#1Zv$jL8?|A-?l{Y`lzWnL-E5B2B-&yZyBg^L+hMs1+@5PeA#+wgB zF8(n7{9mkh{aiivm^eCIZ!eL~R?3|e`ICe{MffsgAVY@>bR0-nM4k-kPcrc~X=ac? zR(~92Y(EVTHacV2td!bC^YMl~^3t?nSn%X7=29B{}9)E z*|hv!`?=q5{ldEq&L&-IK)IunfR>s;>Pr_H?9p{Eb=HADbFC|p_m z@<%>*OGENqK{cjLuS>vnq(n&bc?%dMsYg*PRzei!m0wi5GIi@C-x_#}=YjS^RSd)$ z61w5K9LxhCYasc9CxKXmCP7`7XzO_4FPSI*%s!{=W6xHyg)~)*Agt@MGmtkX|5|vbs)`2Nn%@pbm&(0FW6B zj!?Gv=s)5Q{g-y-d+M1-Zj^8`sn~1MxD7}%0OF|emf!H{?2@Ik43N{rQPb8=>E8Mc{i2#tVlY|&No6R=zf0tRn8F5Pw$R%t{CfzXuNS zM0z)w-$&*TiIU6M9Vv?LBVzNId539VrEHP`QMoi#P} z8q(e7Y+q*e(#HTEzOu)FrOz!GZ<(f& z)8gi%lFoa8f#U26-L%Z)QIqatknYlQLfU>o-hN)$c1oTcXXE``epWkl*0At|ZtekY z>>h67GBNMmP+GHXv4tB-*H03&rD7 zPwK(_iEzw*Q46TdD#_2>Mve@b2XxN+<$>GU_Hqt7uN_mHVQi96pYKlx$tp${@& zez)oA-+7L_hM*N`VxG1q2~?)8DB(>L?l>6)Geo)CByxtRP>u@0D>x+PdxkT3VSqtC zwC5kB?)mq`UGFC^{wR6(598cZ&dnkqSnhg}$UHT!M+3E-Zo#(nz;1U~}jk z)MO$(MrNm}!W>mu#xmoIh&BX%j?1#{u1L26R47K$?A;` z)I>Tx?7y}oh-7=lehDqhJp^G0I5 z(}Yr2=dzeQ9^c>#KTAFTd-KWX)gy;sxhJ;>(4d3wj-Q1=b*bSMSYRMppjL~?X|vdZ zshv;#rRlN1Zaw@xZvM#|8#K}e#%1J(*Z9iRpo5N#UZM{!KC{sU_J*<-j^?U(f!Fds zjXO?P?Nf4SA)=(+7H+!fwf|dq^Apc2e>0qV-g@H6de8O-hTh6A2<8pP&w9jLp`hN;x7LFPKcvTIlHUKdu42P$xN(GV1h3=k( z&q_r&3_1Yt96}F5W`YG$2=*mm#U6I7V~aWhrJ<9X%zPVAZ$X5hMPspPY#xNYVA@yi zh+-p>#DbwtyWHl{xI-#u02v5s2RI?h;Q$7KwVt>d7zm2!j&PosDU!8D@`gZ~_LYgI z)%w5`zeplEE#SA%&I^Mx^)Bp`Y!y0S65d04M@Y}GI6NWtwuwz)wLfbsjMxgpmi&OZ zsn?e4b5=&3%@dC1Nqh5*t!36!9MLs(!w79IO&gmREUmj8J^PHUbGp)`2B}ufMh$BeLu+MRHW#G|QFe)Xmde{}JuIT5V9rFBtqkx1_o_ufw-{jYRXhV*8gS7h-D^iGi@ zCNTTqc@x>g$mSAZXKTDe@4^cIDlAadI-%5wtp?C1RO$+d-?-8f^q}c%=Ar&mwThjIBywKSu@^y=Ro#;YP zsZlD}F05i;wSR?my;eKIIB^Z2Uk-p^t2-^wmsfM)g~xY3buIVEr|P*^Yoi+tiQC{0 z#E=N8GdMfI0Qn7Z*CSY!oET$n|2r`UcQl;R1Os!Rd^4lKf$|L)2x}i;u<7h8oiF}n z#|xjhPQ1k3d;l1DBXSkQ3ap1isJ>8TBGuUCjgWkhK%GKkv{y#DzWV8ohkkEA{*A3m zS3oIgM%-9!zwuUL4n-=<(<;6n$jhmSnesaw2ngM|damp3e|Vq%wQdQv+3~n)Vm(7YfzgSzSer~dZ!=UC4KyjC!s_|jTw~?KUeH$y?EKpAc7Erd+phjed*bpB4yjjE<@{0|_}8 zBd-Wow+7&I`PLuMPodFW&%oi6u`B>O8{g~W16Pgw;GNN;(t+tg2nN}+TVWNqJ8d=x z%t9c6kVQBXUm6>1E-eaScNEAbw}-`Mtc`Pl2dZ2lwUf8U@clqX0F6Olpw<=9xnrCs zq4B2F?zqmMG6d7+NZuYV+G9m+tV^0+CGwYuraOh~6TcPE`WMf&Fn z*N`N(OX6u$I8zqh34=w?Jv;_x+O4^6XX})sdCF3rvNZz$Ck*)^eXiHmvKs(s>pA4; zUDH>l4eg7jo^@l_s-b;>!ysGfpseL04n^xZw(9{!^94oAdDP3Q@{+pkpt|=Y+rGp$ z@8bH8f&r?BFQ|vkX~rJV%s#6gyO*21qFa268@pH0yr3F5!%aQJb{~=FC(w;oROaO! z%kqu|Su;E^tJ3yEG7QIc9F$b{&`Hd%^&u^gz=aPd3FL#SjhRFz4XS0hL*y?By;-3v zEpn$tz9Mv0BHmG_vE6{^xq87SA+d>;Aw^0Qos;z6uUPz+V$a)j*IkPFuQOfuQkOWHb3labWlxjHjh0s)8Ytn*^P|L3fJ23j zw(tl1Avv(taXOAP(Iz@l5XahN(3KKBT%y}l7^9@ieMuQ7SV+u!tFv&N*Kry z`8jk8h5lTS@pSQPRen?WWqCXNhA!vnOp1#A!zorE^jq!BzWrZ7>Z%Gnqk>+>FTGl|(d8sJd zL!l)1cGg>(1dbM=y;;EL@V6t*8uQM;KoB4f7Y`4ESWRwrgABkji0L_w-z*6t7@9oW z{oJ+UmH#rXf53F!tH|!HtE+=q#KIR)4tS~&O9DcO1IC!D{81elW@HpUHo}v@8b-c} z<}r}}*@>$s61IHq)!t`+m%jQB_ZPpaxp@s3xQS$V43w#b3XKqc7|y_O2EGvH^fLHF zntHR3{oVWJKbj7{Zd!RxAk)$`&2P&@dbAqB4lF8B;E?5d4ys=3hPq?6y4{|e2BtAz z5W4y|``MR`U%0RcR_>SP?sRsMZJtHb$qz_eE5Pf@GeT!Hc{ zf2q84ZTm}qFy3+1e&ID?A}1G9@B>=-Vd1JA%;THJ#Jd)G4CFCY%cl|I542#GZ&&gW zt@tN+43sE&)~)6-Q&!bV(1Sz|P{HR>>p46UxMz1k?*h%USuIw+5QfBpfI%$Kw5v>Z zsT`k zoYr+6Fps@`L&fwStN3vACBw*OA`=ta%kbK1q%RKw>L zt$S4iC%LIB$k>-RFJlEKBJ>q7Q6oUL>#(f*jI8sRv>AgTYm(-5Oz%SMpwW$wVJg2Y zFbC?i);g`RUTYUvBlQMv4Qs5`*pL)aE9dH1eVxu($FE#Orkr#WRXs@Y4FcgNf%FCe zwOQ_}(8zlx)JqCD+!- zv;t!Q|Fp=Nq{1y!u!KGyj5IXBD&B)q9jOacAi*Euyd=@VW(KeY+tst2+ijP?kiq`;|IS(g^~i2;Mo0z@&Y z$_4?9io!D6d{Bmc1hHYzJY*P(<72Yi9x1%ju@NRb2w>!AJwm-m0u^-$1U zutYxCEsk}dDT;72NpVpMt*WVui6cD*5nJ6=v}_k3*UMf61qB8|3xpX0%_x`I7;;!c zb}J&4{dTL*X5(k*G#ckECrgiBOJDxi*7f%rZoUuWFu*`u?^W_(!&Y2=K1-{|d>kCn zBFXjiX+WQh<~xD>GUCnR>*&YV!keq*Fm4mKogRAO54o#7w#W4I4+~^Enx2V{uZleh9V-hvtzdNM-5L1B9ef@E`iIyg;xTEr zhf|fO{***5eDVW!^g?}oU86zGlPKig{8#CNYsvSoZM*ml`}tSul6k3^<|Cs`eD*Z@njDXWpp<+Y zlTVGtb812Tu;iEznpW}CasXH!>tKzpmncEJQOH*mL2iY@I#vT-AVigVYSJjF+21&m&S(G@F@Nrkc80>gi8tlXtFy@4mTXpSd7kj z;~HO@^QQRyab}Ok83YMLHa>O-^XH&~xOIiJ-W2Of04+IR1{i1vWpQZzS-ro>7OU8k z&Guy3ndy+H<^-8@MBxh6`Yc&|fNVNR7thn_qfGKB71|>SO|kxpB|oT7cNwyMn#=&E zKa7PDM`gm>I+f^O&JHbCM)wZwTAkZ>cxm;>-u;Ky){bnf9bDTux^d{(p^ZaFHx8aW zcOy-Yeo?ddb=~^=DqbQvq#8cM4xCnYA5(U$D|-$r2QMgkPRKz22JS{O zp|ov9Qr?T{U_c)qh?<=t5QA(>)Uw9SfGMC8R`5_rjbmyVah+IFFOk+Gr(MDyh}lHw z`X-*s@s|Tg!X`$AoWL5XXEUSQMC)!KCaLAECnS6351~x9W64^W<{!9 zR~+>ZZxjzc-hAxI%Eklvm3uP#E*94>l@HvV*?U)D`hsKlysht;t$Wqpv)|gWVC!14 zb?wvSvAw;5p+9l^Au=~fVTEXU8Qy7Oyj>V?5ye`8hoW!=nh5G**a$^Q>>p_t1`DEa zs|Y+WHAbYT$=n|3$V3F?Z8Jm=+SdwBNCD71W$f&e@wsHPlK2$u?S#>q4h)Es$S@od z!+3)=?w(dEfNAAnI?zoziv)xf#ANxq`1ojF2LnQw?2+c?WR(N5(jIwfRF)s$xv{@p z+Ng# ztJhp-K43@gSC;qJ)YfA&8TMvCb%ZCaI%LOJ!5E8!EwHKrORujd79uJE!K(b&FMm`k zV!jZ@iM4C4@bHJx=l<$B^QwMep2N-0+;FrL0_;f2iJ3-l|mv9Jt!~xflo`Y92Xth-yJ*sT%>#+Y{xlAf)S$`or%kChw~kZqc)H zJ8y~6IxJ>CqD@VF&CC0x4G8)(AbE!0R*C#)02dTkKH`AS(nl-LVld*t9<$DsC>7uM zwDRt?$ak+f?|#jC?rXxtO~624HPA_gK@T0j>Bw$^nxp3lC$CQFtBKJ7J-))_>s)^E zItU^E-GCyDZK1FWLe5rJZ7@J`H6Qek2@spv?sWhIT^_3g?^8Vf*?5T`jX?`0YgA?@ zh^f}@;;arWHUjWOk>`SD$i!HwX?K}i;CnHxKcV*U6)wtK1^mwrxL+bFtOcMpW7;u+|LhX}Hee>-TOC$5E^Ly8qR}KO04jnvx z^ziXxI1U~;wsGj>`oZIC2al{DJhXmjV{PN$>c-In>&I5tfPTkU4;)=RaC&|H-1_?A z{i}mh3&GAQd*__7Z_PAtSkt{BL4U8~u)6CoBJ?pF$F{GsU5DWb~guGxVziuMB-RZw(84n8jLzf&=Mue|S!v}1$s3U;hfnNcDP$+)RTp+h-S zON;BIa=;Wn4gmEEi@BB9AOKKmGx5e(&fOz|AAj% z>~)yM>rrThMvn*?)b0e~j*-3;=pW#VinofB9ol@Ktz$N~@1Dw`EBVz+;DP0}`--dg zmp2~FESz=?ZCLsanR_;@JsXzpRa?(KXYY!mbHUs;t0|6Zio?3nh_XD(w62Rgj$l#- z({@a8H#UF+1IhFN)`kkhMWMe51+pku5(NQ?C1EhnQ^Y`C1Uy8OFx&!(naGS0xp^`* z&cp_3KRkqa#@8;(%t--yV11r8k*$faqVF!YxKvjs!2Ae^2gXUDiLliLl-R(I z^l1?ffYtm2DZiziPnPE2v*gz?uo{iUN9BVMCNI94fBN@3p8u2g%uDM02rv*(DS{aW zTCfVP7&WQXNH_yr9I(2f-)e;n`z`!0>yg?4G=3l|$ z4Py+va47Atjo0OLIvozr#v}Q+|1160e}&%s*S59GpjBq0)@?Oft5#vaAa6v`Bg~zT zCV=o^_Q#>q1t0swaQK@I1E*>?-)LavV3=?f25e@piErQW>Jd*n`Q9V%I<6|XYQ7=J zCr;o6@BqlW^58q-u=~=P+S&2#Yn|_1%l`P9|A9AscfSM-geC&*2D-M4?@ICwOt4BG zJozs{UmcM%T$g;n{rI(~_*QH96Dc2_uVQ%;$Zu>%=Ao9Si_onxZ|pSMyat;GT+U*5 zAUhvlBievCvskJkh|L8IR9l=JWM7LNTTv{)K%31C`e(73Y)*sSYxYEKp|s9h_4}d1 zbti#=(0o~EOlglQY!SZrMP{iZto25D=V6o>HK-J^pGfZQskc(g_cC&1zmEeGB7hTyKi>){>A0>1FMIR ztRFsk=+NmyhfW?k4A47z@W_$%BZt-w9bP+lbZz6vf%T)SKs^2y$MFLP&aAGSTU|N7 zx*x}d)%|Bz_usj?a_51Sd)9CqII(YK@9w=_;|tNgx#pqyM9-|cJSi*hlD96&oA+P@ z7%<57p3?N+#dV!jmiMyQEi(R~YV;xX$R+jgId<^0ylq8V*$oedw6aS+a1KZ)DZvRd zEookW-XXBYH_NyiNE&0I{J^`M7040z2GHZjr7<+kPfRN?_{l3-7z7aVW7NC>td0jj z-Da_>+AUYrt75gv#Ab=5MiJN~F@i?cNcHu)Y^^H0S?bs-0#9Bq`~NUd_CNJ?6$yDlh^!K=P3Q=aRi^0dFX_mPt#?xV3HC zR328Q2N`G(Ei2OeB#S}C3S{E_WE0HN-GX?RDA7~ZN1BC^ve26py5r~-2tf!VsG5^P zZwx&{VF18b67iOs3ST}4A^5h5gWVEz3!|fquY-n*9{kYPM*G`HxM*xmq@zgr+n8`K zgKT_1kA%QO^bg^#Aw0mqLe+0m7TFT85@GATBr_q-?vfSv$P199lhBf|+j@(YpBD~z z+-|SWWaq>5pcrDmunKlyAe_l&Fgp+#TE6(sz_+gzp8ltKWyj&!<}gK?wm(q^UzO`mR`3Ken z6&7j)AkMfo462)EB(4ZB1=GKfHGHPp)O&zZQD%jp&1~*X_7TCKlVb zBEz2#esO`dqO#^4%=#+E>iL;IzP#p7IDY3Q|8~RYA!yOYL=O;Z2(J;d)zJ@rkCP1) z&aJomj1C`gjE_cy*kQHduYiIMyTbw}41$`m3EgB@nH^fIOK)}XLa-V5XmvswwmZyr zx6u`FgfsR~M(d4n?gUzVN>>adP~%RhT~UQStaimAf?!M-oKWYDqx+}!B=tW2R^v-* zgTTO&CEjd`Rm_osIg)i`I_#NlS8>FW>2nrG9EdPY5319Xazy(E`ICIl?ELQKV;hH0 z96XFl_V~fWN7oPYK)1Sf_yFK;Mf9PW$o0;>Rl`Q zFCEzT(Ax6lwPoBscwqUy)#bZa_nlkWcYbx>-3RtxTw6V}va+~)`S9|}>HYh115-qP zSl2#h?EwtV@;Wcl_S%;vuss)Nl$AxU=cKypIM;K6>p#ZzA5`^jpbaQ%o|hD-_+W(c zoVYS4ZC#RJK+NQ+rKn9pVGZwpz}vkXpajGtRV>M}6i@#!0uS28AAl?p00=PTnVVXG zAX%&rlc{f@B&5zM)OhO^&RVIXR_5Qt==oBbQ4o4(y*gYYvDZk=)!JExPpspKguI^5 zSyB)@*ea1gOR0Fs#lM2&B|-JN!-F^RLGCg%8#e);(Q7i;D401jU6z(Hcl(6BWyIb+ zpI?0ttgo2*kWjayZNdn)CbeTfJDnKAuo}Rfl_a~x=>bW4s=EDZiUK2rOVAYK zd7#_taCvPG4`2|)5AF+gvlS3E~w6tc!GThM)! zVT6reHwhvK9)wXp4d*a#bz8S0fL?8NnT|ajeD&`)eeYWOTYod|95QPgQPTnyP(0$& zhjI!qwD73~I;UBWWxo6naVXON{BO%I{kQjlUm8}g)+w-~NP$^GuLY}lIcT*WD;82h z_273fS@^ZTI@t6SkaR)Ss@Vr{?4hTJ&R(@J=Sicl)N}I(*Lr_^?WP}J3txKKcmAp% zc9VprRH{a#9PpDl;NLuK>aSP9RXv14m;v`Qz&By<#fI#V4%VgQds>M1HlGN))8av5zZNA?O_-KwLWOUX{|q_ z^Ji4RKz~Xb%o@WbL!@NHcBD|w1Q<+pSeyFog&}8Y%w8BXq1~5k)ucL%+1^~w^sc$( z<7*qI(bNOtt*jlwabW%M{~joBPaIf3x%NM|)nn~c_2krnwNnQUoLX5qxqs!3{rm4( z+4sJVLR=RgNcZ*`sJ%LNAeR*{y2lm(9salad^)Xk(Jn zE=grk+PYsH9}&aaZBGz-N1a4khuR0f-q55-MpPbyfH$C>Dux;D-vnBer9PLx4Qok|ZYpD#I znn#V531ewQlj%}t`qY_jb+VO9cNp^h=Hif{FvK#sEz5FQ zH`yy&wqwgqvMK9iQ@z>jZjwL#^Y6cRdC&V%PTQTEdFGuLA3$ox3=f}jo^$?g-S))4 zD|fvu37`J_z4xO_#wI(%eCU@#F%@|rnFtd%Ixb$K6Ie^9I)T*I8^sB1rt(1i$VlS? zDPQkb|9159$6vViQ{CRH($H?W%Ry?U4)Utt8MaB;LOB4C#cM;ViIwC=-qztuA3gN! z&&`*9A}d_h?|X4GYwP`0RZJ#xD~~JSaa;Kq_qGUHxLcdKyk;()$!vUJP`Y+S_2_rU z-u$17f4|;&_$dX%Qi;5PM6FZ+NkASUSWlMbFc1OP1>z~KL=vHuKYISoL$}w>mwzH% zxn9L=Hdxxs z6RyQ?{)hh`)*t+E-SUlZslR%yx_`2^x*F>DAh2L12PHAlN2U1c`1VpVrY0BUv}jKt z_roTRkm5*FMKNR^iB2E}B7FGLX-ueIWm=nD-yv3;MHp&n4HVCvOzuH?!H7hm0pb}1 zaxG7;Mj;ITvqS|{)QHJmF#gbi7jIDLts)%=ew`BJa$V!Hj!BVuSd67AT^E)95e#gB zvb`F-X!HiVgdoG1288BeiG36k5b*j$fX&WHS?8prYf{oVA?qBMbkB;q=j1~k^_X8Z z5|H*gu#t-2myWqeBw5_Oy8Ynx?FUxY(6!rBSwrHtcLhj@ZXRjt?OWT9{@x+P+V+D~ z99msHyaL=?JF>cZq_T2!rGhl?(Us-LR!dK;6|ZhDKDkzWY_)iKrFaQZDLzsuK3pkY zsFcoEisx5~=PJdsmExIl@nmKB#7g;axtvO7=EDV7v^?gH*q5TgNX8XN$Y;WmnS^L2 z**uMnk0@&*&cMFgR1Qjj%po_ce~vw{z#4FIz=$4%k?wFk_8oeo(0W2n0+sA4I0q12h(kyh|p-cq@b=NbvHrUdIGu48vsb7uoqzahY<_{ zX)2kaV9Qk^w=5*gj+}Zrs+x)GoNGGg&ffHisa+_CFQOSZv+MHs+WEfpL2GbL?WTv4CXM)7rlPoi0J|d~5GCU{Ev&lA#NZ1_T>> zruoox?Eqh8iar#fjb-W~ywJVS&@}^1LQt%sIYYXFD1kd>ND_#g(1wb57Th5?@sZTA z2g`04DEHOr1|V0+vM#VY+(1G$eAR7!8a7P~5cVI#<{i^I2a9L!c!@KZWP)1L(QGmWBp8gNAkBMijr)IG9)|H9>BR zM%^jDGt&JdFv#4fg#m!1MGS#x-;3YRe}Dap?|f>z_?{~Nwa;zZ^7+lxn;V#0nt1Hi zc1%M+iNOGr&TXMJa+pfvz~dj!Kl_shulz`V_zl(aW3{cSzq;qX`|rQMwyLVOx(c-I zx|*8Wx;mI^u#t+y_nEJKzxRbth93R?sZ{M^%uLqA`T9M`~3hlfqVHk32q> zgxmO(%npz29fR#m96$hQN>bN~u*iX)r73>oq4(EwKUx3s57r0YTetn~PkH5|wOi^N zIh=N~!%J4!6qKAk3gC^QFomCZNd7(n0|jklf^9|7hl~HEsRGPY# zrcRO8CPFQ&)+;qyl}1bUz7^mHVMKyauK`ssPp&1+Kslx%I=M!tQEMfXHi8^#iaL|R z)FrbHW3ItB_lxagGTXS!HX<<(l3G~X2}uOxeqcchsVPZ|ri+iF*n-)|s2HA@wsDbt ztj#(i?w*!)&q}(dq&+i|u9?=(Sz*t-V%RMkaJTg=^17FdQ(<4UxVF3keA~0SwyUzX zyRx>kyt=FMS(I1F%Q8@L^}q^1Z}kXu9z;}Dk@X#}R1TLbN6VGd<;NoU9J=_Rtk?)3J;eH4_68oDii>gi>E&eq=l!E9Ilp6lMm<46w-&X z>BlO$Bl&D4p7ewYW@p8+lo8CNp#rU&37O`jyh%v4#IXF!9D!o9i#_b&48q84vDWOW z1yOCN(h`4@7kz^>u^Y8mZL69LL@=}k3X!ysQ`^*33%3+fwB8MtfZxyzq6=`cnJp9n zrr2Vltz8Ej9f&5iifJMRytX$n`LH0YZ5A^`vKrLR%_72@5C}JZn^10}vaTonKrR9) zXgwE@3ovY^nuJ_539H$d9XD;vN;WgtuzoPRmTjSZ2IsQTQPjIu)Q(l7Z%;>L&*<{$ z$(;{R@4hs>`^rG!lr^y3;L2(ild4&?4Fl>Kl)ygalt(!Ug1TEi6_!uOgu@PA?<}up zmfti_cGUQZz2SCh!u_YLgw|-{$ zOU*0*fY<`TC>m}nt%MWPN{ueaMjIti0G=VSgp-l&6$l9EQ^#iCU(ft_efvMJZ~tUH z`;&FYkJce1u301_tmCcLF`~!s8n{AD{H07&gIIka%IiJ6gmDSXxLb?#kttE5CL~h8H=%4r7=R%gb zuzbodn+{24lE@F4qhZK;bde&Qx~;WPwW z>f{|YAP3^FKf4J+vUS|1`sNKVB$inBF*$SrA5#+KY-}Nrtr6fLZIb ze>i!-?Avbk6wKabgL_r)+pcq!@uW9%bYk`F3}A52ll}Qq>V>3gCa9eYt0p~)Nw;Fs zB_CUojVww>79`_d>10qiFfZttAuxy}uxCQhGs)|o6m%mYoNwu#Y3Ue)&MntE(rg*x zn)*1#9=53;iVZBFoOPTv5WqGKy<>)9=%pLF>1MM2jGVC6HVTBKkE9^URoAruNNnhF zHFPZk9_!7c4V_C319AF5jMd}kba_~o8K!BHZFfQzjAe6>HlSggVHjf=CdtJ-&NPqG zje~T(yqZ%%qU^>VbjEZrAcd`>s!Y z<;vpjOY>iO^ug3VL*ImP!ZDOS)W7_2?~beXn*8UV$-eTv=#4wBYj>ue`mpc(Yo_I|HOlmCczFvY zPzIoJTQo~w9yoc^@znRj&wW4o>~~|&emnX6w|!538I0AeDuXj`BI{SN{#V6ur!4I34#<|Zb$iP=Pg!R0WxfNKn|*-R)K zH<1giiPP3GGI;UB`6s{Se*C>JJ^rra$qyCb9qlk9R!H?Sk&&=s#MJZ|X`cY<{lF_J z#a|(wn1pGA`Wl;Y5-4aviBUmj8j=SlonJil%+kR}7xtZhXvfKi{^qNbYsWfWUlOag zX?usHSkVg|g@_@*QWJ;@u~Q%(xHHgl5fU*~v;f5#lgx7vCa0pZ!$i}|m}&?z?cq8x4kI~p7qeJ@Yf-D(+;_PN?@ zV*SmxdRDEBWQ4q0rzf#`0yu7o7nMHX&o|nr<0d$q+V-aR@I`c;$o_c2?Z15TFfa>FQbnK07OQ|5w_v?f(e;iKBmTe8hi&@VXjRE zi!3Tp#9O4D$R+JQQu*>)YDk@n+>i7d@em^zsw;>T5uHGaG5AOHBY_4ZK0Giymd!4^ zgPBoZw#!#AxC&bLn%0Y@o08s9u=rN`(}yQ_J~qAQN^g3wo(2IN2>bW@h_n2F zAIr$Jp(u8c>EjjHIn^D4Vl|0~_(Cx<<~H zMov9RXxU&ygEWZ65Kx^#_Cv#v&*L}q!0Bs~Q`IZ#Q&Pz)WIW`VsANekOqO%e%>!`B z2wxh5St3dXh91^EP$6rTaAs`P~ROW7y1lp1NO(;4i6`kX)hHj~KP+=Pa4F<9a5>vkdrLJj! zr|$(3y4BdvH}tgHC&WEYfo)tf;#H5jWxaEnQ4j8t?irqKtgUyUwRcfD>>c-H(up+E zyn{fna{0jW^6t{zI9OgjSt*^Uln#|kdy1u9#p2##=|Hh`s8j^X9W9kkluJj;c(-^M zv0OZ|Ts*p5Is*7AhnJeY&#pUe7%h_|K>;>v_VL3-#aOTdHa%YR# zQ^oAjV)ig~9w&I1J6_74E#>b% zUWwkSL~kxfUt5lxN(SQL)SNrsIp?2n1jm-VZ4=(65kJ_>ctrb2~ z4aoG+Y8HtJv6jKX%Lbl+$`#wd4n@ZgO~s8l2#Q}A)|0LyFpxq*YD~OQ3sadPmud`h znkWoJRn9fqY=v|t9gU|Wu~;Ow6i9Ztiy;1KJ*#@}s?oE<=-q7z?&(hK8O$CUT{}0q z<6?hupUIh$k2@vE07t-MUl0w=0|15nv!vtKGr{j3=XH$Z!?Ay#N2Z* z(f}T2zqg@xk=6@|!pSzzLRhg40O~VHRYCw1bp>pp;0)w6@_l&5SBor&bXcGl)7#VY?}6SM$jx!d7Uu zvzm$J9DY@yn2IVD2ED(pjk$%z0Y4Bf=WyGsWj1bR6Os~m?1UNE$lV~rP)T4j ze#FoT+|+%VBX)r+=OjAdLvP;1`adXn~81%Ycq|x3H&JX?p>ev4YQ{zyvt~$ zUb3pFC-`I)4aVZkD)M>8=6X7`uNy^jhE!WG(%s7yK^zO}scppki0E|T#KJOWyO?Ci zd%mvJ#0gBJuhl(|sMo+)Kc zQ;2t_m_3Jc8BemOi<#qv%%ObtU@m(&pFLX0A&wMsCknZf#mt#v>SQ5>51hD)V^i<^aXa{Md5+p z^WwK@_GlGPUDu|8$7nT^0Y`Oa3&y@pKsii}IYQ7Rc)%`H!uCDTVx)(*5KRFVESp9YfyAHnP-~2oK4nI4#G0d zw$5@o-7GW)Q5PHMSvDuzzQpcaVs|-MXc8h7)D2Ra2_X2vOT7g{0h>-^(`)o(sQ|wq zTIj1cLkXd=33{oG&09gw;2{}L-K zo2a6@@xFRG`R8iu?zx(mw1}6L;tEgHYxOaa|!#$g8?%7;*Z&mH4 zEsScIfNvYA*N@jLyK7t8v7y|=h7m505Ntpb6bPC)O|Wy?x|J_)$G}2_g);~cP;e(= zHuIG2?d@`6XC!NvfyhY5e&3w1b8GzKypELQfG6?hx5sYb8*C#LhNcG zdLFGwJA_ShUwuwt82!{!4XpSMTX8Y*O@W zYSq?u#+uhYWe0E8_m*p|$tvB#X4S}MS?^|LUyaGZS~w;+v@STf&R%?yGkt`&c#1n* zsA`cRqpN1n>)G(4rIR5qp9kzhMT}80s};0rP@`esDnOU1o=|RDs6rUC;AZOo^G)~w zsl2EFRFa`EXQLuU9td}DZZkZzHS&a5xtV=R=`*Fg))~4*1Fz z->TNTtPbqf2KJbOyDZ^7o$*71*^@(=WA@-0xH0^$am+i=@oVWE<#mqmyGC08fE}aF z9m6dh$N-0%tb-g2)dMtq7W!@u;tw#W{{swm0tP7z1Q>n>gBm+SWu>c3G_9Sc>!3lv zAG8@8+4uu0(mUocNogQP^G+IUK*tKu2cs)GoAsl7`&^S51^5Wt(8n?iHFeG*Sk_sV zd5&$JXLl@dx?F5tsn0f2`eqhNT<)d{qrVvWkL1w0Xgw=jyxK9`tjOph6x^poaAH7BXrfH7PPf zICw&bkhpm^Lwbu3vJK!4oKkjYv~A%;>qEO?e1Bg(`*Y;mHf&}#VxzXUd26F}uF2+Z zPz+Xcgh)O&b}9*P0XW&zc0aA@zM3sCdqS~X4{K5DBm+Y3+wcSR2nU00eL+^ci7g=q%;s9#7ZO` z(6~bb5S(O@zC#LeDnlpX!x%cGTC2ifS7Kj7Z36303f7-o4aSSfY}1;%TA}K!wIX%o z%k@0031j9DkpzksCs2O&guFWF$9sAx}~OGskvX(J+1DY1=mH4 z6tQbs(hCs-&>mu<{wS~8%kS}sM`DtZIL9{E+BK^ga*WIc!@(qw?RdF(q?q56%k9c% z_aghsXO989ka3lOW7z|R?5(3JbA&)v zmRz#rek7MZl1-n@r5-J$9x0?w=aPHU$pe|xv25yCE_EcEI+RTv$)%2G5$R*(OdZXp z_GeNDvgBRFiEQ#zHhGeYGx_9&eCm;W>fwCq0>M2*;(RuFK9{(VO+1{9J)Vm`nU7x1 z#x7=~k7T0{XQCIfv9sCO>2&l=I&wZ8c_b6Lkd2&8NAi(GFc42gq641&?6E_i*DT&A z&}_fZTA`ptB5aY$BOc#!ES>}Og(F#VhBBd0IuyzTgN2|!AM{83 zp=n>LE3jhpZ8wAu7~?0*(Zijw!#(k%gT?bbnUgRd5%$kFnMtFs$=ug$8*H`@x1jjN z6c`zxb%0~;XQKrEQw;nJ2HAg^3jPrT{}Kj~5UMQ%2`z&(SSss#=_)frX=3P6FOM`B zdl|+dBCI_aW6thr=n2qNU0mG|-#p2;&2cT0Z2bUJ-_N#Auq~r3%LK>1gnF56n`Yam zSTJSQlbHy#M_?;VmiDRTEHt;8_@omhmTxm<9#tLt=SGPU=9SG`+u9~7yz%3o`#ch* zx*8f==3F)3_?_%;UauD@>X|GWejE`0;R@|*k9WNI>9*CsgC1rzp#mZaghvw3Ub!^- z+^=-Oz1GyBUKqB{mOpp@=KC8MHJq)aB5vk4smzi9QgGSh@l7_D+kN(`a{{wyOgfY0!zWs)_a+9GRf&>k+J~V`DIegJ@ zz zy8>cj;)jau>1(tm@K*%eAkHk7SUtIc=YQON?Z+Kg{z1KvAF3Q*df`W6?E}cG+sTq5 zlwB0W$q3q3aB@L`j0In%hGc8#6B7K>sx3VdV~7*qAms`@eHT>$h+27Il3K0&`nH0akOVZ-Q=c{bNTAQP;JH}3Z%nX0X41TM=|8SLNx_+I3}lGt^oWwDkgr^`&R*QsKogkjXOS)1zX z?`xoKrqMTJKZQmHD|ZDG$VsisG12#9A%J`LG%)UMV3D~=a|?Qj455f7RNA`-EAdz^ z9L$D7`Ea-pM1+t8=0bs7hl(l9ud8rZ5HNN@=3 z^E}%E2Rv%SB+ER@GL5hdgKP|ut+P#bd@+!S9JHcWSuu&)>fpRj^_%(f{gyv`%lozq+QLfEDzz$iP?FJ2QKG zT^jku=Qe@F+=v9Pwi%}Cd~0}B@P&s+8<5GBj(fUa`lN<0zo(|Yx)E6-`2dz52M<5f z`Q%R4YZ|IZCtzgz|-pz=aKu7(NR84*c%$ye!2G> zzip8B{6F{JcmEa|D&j_|uIu4Xn5w=_b@lN3u0vOZ4Q_x?8oDYgy~8ps-CI>vhk*zl za@h9pSNfj$xAxiHY+8d=27hvi#a97K61-)(-tvXVek}X)Qx$px9PrQ}KlZxB1iVf|8FTMEv>9>BXoc?R@qG4?L zg*mr=XbKfBuoN5?&{R;sDo7GejY#B#vX0zPLl!u+I{SdWb3_7tDt(Vc*NNIzX6ynh zrVaM-l!Zr|(kN0P57a>s3!`C;sZ(j`BO7f>6aG_?p|DI%w&ygYOvXPZf#{jiK)f;y zp!q@dvfVBgu z`0i93&yQu2Co{mk_^C|nL?(VbjWc!%Ia?-nCPU6s)a5)Pn>d#tSodTu@l-yJWbbkg z1#k56T#W!> zYI_gY5ACYaj&0&8F;nK2-fLO@VcXI5mh8uhN7of6*7@Ei#nD&9q3eQ~-MoQ#+fYc* zy}*LIUz?QKDyn0nk4=N<);%N|K3_+}NxD5O(f}lzdd*~*A~ag1_C9!_Zh{n?rXv#w z6vLr(z@G~Q0C&Y;uoMiGLcvl9cYldI2^2!XGE_V}oIlS&nX$uIyvz$61CEreTO_7-s25IH-=dkvtHjVW6LYI^i@SBnA|c4e6$B zT@UIGf4{z|t)_v|I+8YDTUVX@h0yot=kD8FT}RWpik+|iANB)_n`*XTbO5c?YNF`a zg7kRk=Km}5U;eB6?#B9O-zoxdDzA>}~aA9K0W;SsfnyB2K#g#67<$=rpDlfc#PgN}x_|XO- zhE32as z@%yUCwh4#V0$a<$;P&2Yzu|ZLn^|1hG!e5BViv62rt7%!EAbakG8*c^jZqTc zbCGG}iU$T1H&mAx@=Uc=YeaZ+*(e z50t}ggN~mR9mMtxNh=BfiF_6O-vNC12><{^Qc^@~Nz2ZzwT~##i^Gzd%F?Pb3$=DJ z2zwf{#L$g#2HJyUIHEMR64DD)5e(qWK-82#oZ5t8F&_!4Qm4@wwBSx-ZA77HQ|V!Z z1cg9sg$Rw^z(C}I63ZY6fe?FA zsn}6OI(9f2JCukWNySd4V`tK_bD7xLOcdYYSSpGSoIsw2%q<%^PvviqWTKBzSO?s@ znu}g9kig~19Fjef?>&_Z<9sGh;%VxKIl z&&$J?Rlbwb$*i#7%{BBv=$j!@*7HQPHf0^ks3tyLq~e+ST03T&E#ocbK_1#)x>1JI z>YQ0vjfAs4Z`S9_`+dcLw-iM9O94cn23u^{_GTG-1m z_EFKp*~l36-K;;tVCNrUP`8l{{$)|jAlV>^4H%^1u0~pDr72BxrHQ66l2C47qluv~ zFy)peO+U{x(PExrYlrFbZemyi>LBc#lpQp6H$zGCz&4b?V5yM-B{A8N5HS@cp=}@` zkt+>{zeAHefF7XwD|d`fuk(k~+m8J`z1@l@#`M?hudLTgJ8B?az-lB~sjL=k*DB`I zeK*&osptR4z4wzOtyu^RtR=gRWW5o8p%}6U`mg`=3om`b?OY%`jE!XHv7XaX$L05D~)S^_ukjUiwE%! zaax)*3Nb7~27)X7PyCAiz|7W0hLGRPY~g|Qhc!B^3Af9Y*3;i<8_jL0ua~q6h=F>W z*f`>N;P5xua2XYd_<$_u?#?HE+W+h?Omm6BBQFZHU6^`6%pQ6L;4RM_zdrKDr_9!E znEN%h@G;qG-707ow{Kt|KoaUz1`YDQ4GdI5@EK!W1y(;aof7D=fRCrN3RPy*wnB}y zRb`T5J;XXlG@Q}#)7W@&Jv!Y`MZnMmdW8bD4N}h%orO50%XMn4L8H|X85v>}qH85e z_%nExt)+({JN;Ob7RN_RQX8W!P4y=nG zUKi|I7ayf!-@5F`x^&lXm3#kHUc4hq!xZUC+rn|d)IQPD=^CMNQ?nS(LG0c*f9X*! z*om6Kdz&Tq^0XtP^E<-+g4dJZ2%o3u_W%Zq0bjxI1tcO007k!eIpD2Ou^ik0NnbhW zT@88Gg5FBdn-BOCzF^ej_c?qHS7af(dv^8oZ29<1<>Ykv^howtM`WMgyInP(l27)8_~_ucd-m)R>IJBQSi6{gLi|1LHh;({s9T;e?UUy zgr5anWhF?cG&1BSj;gcSIMQsM;=4)w8GLiy^4KT$Llv#Qkw{gv5cUfuxj;Oc>;c1(_xoX_b>cSUGPV$S6p*N6uRuQv}@?y0J(X0e#4`tmPe6@7CV_SbgYUDOwzBzR@mON;gJ55Kxq`-Mhs3s^1K+5l&7a_=J} zul-N9yn_zydvi~#sgp-A7F}2Zf^roUUo{;jgFyqyQ@jQ)YSadh06WCG4hf+^cggi# zQoS7|Fld38bjU3IATXmtD1pkK%1i`dBx>Y-5<+O{Ccfut;HMd!K#>C9TMw!X01)Lh zMDj5!0o;jf6MW+!)`7*?F6}_ZVVdyG`{Mo>D&LepnhHgd5dwP2 zWRxIYoLm5Li6j-kIs$I-4bY3{$Qi>uMKREFkytSrD@Eh^Xe=9z731->M0{r=wj&W; zje}ki-4T!OO%Tw-xf>{#AheUC$;k0k^mr2Y$e~2!U?PkYahOE-KsDAV70|p+Yzhn4A%A* zYiv>4{BxY`>x|$((q=9*1FwNXvq@y#BMqZHPmL z#Y2cD3JE{!9QJH)eM#?xdjj1&aFhC-qyB_|V1N+RE=MM9!rHS>&XF9;S6wHM!1 zfBj$J{aTCBZ@aqdiJx=y1NO&%*!9$pfQoeu42*8kv#n_n)$k;P&wes+Dqi21pi#nkXhf6L;t2=*WSd%NJn~_4m#3kA3&h1hlmrHvh8G_A>Kz)h zvDH2^aQ#=~H$IV%1i*)}8cqFQ|Bz=KWOKQM(x)*ur##P!h~i>CG}Np}uHr{R&GzMj;yekkr^CB86~=%+!tP zEhZWmj$lTNfd~%*(MqG(G}vMo=4yI)D&&e5z7i{PD#&8UsR;`lV?-Jwc78?HaapfJ z*fGsB^oq>=66;`_WsGke;ai6J_Nlhs#jg3VBbW$86TV2o8;bkF@o*#&jmG2AL_Cs6 zL=qXKh7`obBQXSUuN053#N)+S9KFIqG?vH1NHiCX7NXHiB$|yx0f4yYqtWG9bS)O$ zMa7P2bZ3mhy*SD1cE=(+qLFa=tfn{x|b}y!ARF>wi7te>LlSCF^}L?R_ER zeKF&GIU9J9K%(!NwD0M(@9~uPQqp@Z<2e%#s``STGq^V!L-oD8YV?84(lIaw8J;T* zgPDfO9d(1*hKW7Q(7UYMIxGHTX5jVuv17EU)3otJ)w<~_W&dWWeN&6HO489VxWe*2 z$KUb4T92+b=YK#S-^ZAJxTW;Vmh2DMju*JTo7~V_u#&7d!Bn?Q?>|Z(I#6qk)OW;2 zTq~8Jui$lN-L9<5ne{j`9@l5#%z2$fudC#BEqmR|K6lyYUP1U>I9I(mNmTrp~MYi&Z@= zRX?MhU?8Zz_;p6*rP@$b$50VgvnmD1Fp$BA?(Vj){gd+i|KxkFQ~~Tj)oW_`!phf& zUihbuXMdr{Jbv#c&;yxusATzKO!GyHyZdhb=hopbYdot^`P)nbTaS+EJTP#JKxRGn zz2Tex+5h;DS*i}oN0{D9)ZS58gr=gkbMFIe~G&ObZc;O3A|L?8fSv53}x3;!vvd_}m ztrFj}`jh`Eojif;c3G>OnA9(gx>ZugurL8)> z*=+55>|>sJ4AMH#ACoGy&?X&OJ3scuZ@Jn&4qvFY^~koFh-4RGM2m^@hZ@9Y{P$Pu z)kM`#X+Z{P6B|ep2viiQjLmQfLDFcO zYB5bVY5KTwqW}XDg&snwG8JLVkXbOyf(@9L5uYPrmjmX!LQ3aKjI(P&V4Y|(k3h^B zgO5-+5e&z|p;$N^3xs0+P%IRVMZ$4JG#raYVhJRLR3OdEL}JS^qARPa(dcqtRSoB{L&AElmLP6r=Lhp3a3rPolRk_cSS z240}zrJVn@Lf~d0@Mfxgd3Y=9yP5F<24A8u@YSsMwG8f_7t-G6)4ppd@70v=iKOrOjO)>a z-!|;OS2eZSl&iI^)(;$NSa_j+_;j5;QQMWQwzz6c3w50l`r=cJ!28U=TUDCby8c~t z11p>5ouCwMYBO#YT5BziYTbC1s=r~N#4Y~3IrDK{XTGjyN5k;JhT%iC-389li!JdV zG`ViF=daPb_chq_wRQ;F1m~w+70*)9?I^gNIkz+Ga%5af8JB~)I5X5kytV9ct$0b4 z2^cz;y^f04vFdZK`CU{vSN%>PVTFRkRi7J|3WbdoAScNrU3s@7vpAcXoy^WoW@aan zGl=on?Cio~z&aaj9a`dcPjPIdLr8%k!NdOs2LF%{{sj#F0SR^g1qL-8pN)?R7}EYw zBLfDN6bz~?ECpb&n~S>GGS9Uvurz}VDe)I>62O)MhKfq4E~`{zzW}uuroxZ`M4}}g z^$-5ty5SqYtKqBaXe=7x>2X`+de?`)@4fkd1k=m+SJl+epd1cj5}&!Puj~0wA2|Eb z{d6uAeg;n6n<%okm|&@K{h5#&PLvbutseohAC{a+Qww}9(uO( z`Y-A7UQkx5X!K3>G-P`BWAx9y+w=oel9GAPlS8krv$X^FK+cs$t7~i|J8q4Q%yv`9 z*`IYh@=KP=jxh=m&}eSnmVZvQ`YrR>f054b1pv}pxClkNbod+pqV}Dk)zxamt+0)Q z9>QSi&;t*Dg8wp`>uOsCTcLjip%_d*h;@j;X8!sw_zxB^LTPMlh8TibEz|csWPj|J zwu?WsUi^t_IxoPoNW0vu)v68FfvZ2_nJ2)R6k@pz`)FF-(2lPUz4Qx?)J$h#?T@g{ z;n@dWEvV?D@I@z3ttOJrdMy@yp=J*;Kr?oGmF8Y!-=rLxRE9n|7Bq}KQbV`ajy9l; zuR=xKMKBOdPNg0OAFZZAfn}DDgkRg)tT6HwdZ@&RkO5+E4mrNw-f9DVbXwf+!HNgZ zIxDbG0{x(nAnF9cZ=t1Q&M+Bp2I8Rz!M$K877fKBKtF0Mgias@+7>SJPGfN zhId87J0c;(-e_olG;}B$+5kYr$$0o|B78m>I+qMxNQN$@Lg>X&g>CRD(vb^34$uQ6 zQgJyIcq|ooGVOmRPem>>Co^oAFx?agRpHBL^hrF9?rRv_J^#jM~OE+orZ`Th$-mq|^ZtQGb zXOuQtsvFMM_N1#c&brZ4^)pvC%SWpXuDYI;D)nq#XRdx|rLH4b-xF^b+sX93z;fPT z%s)+EdX7HzWW&gXdRw-kr%W5#PwQF6&z{ubbi6!PXmMSqIrv zUCjCasf$TzOfIZHXI5{_P5zQhsM(kx{|O9!hJ+hAAq&np>Q1gF zlu}MAVrZ^Y00=oxIDKPH3@a5)+d4;H`#-A6wW_*$5TOZOnazbT!tlV?DKE@ zMz{TSOf(Qk`D$o1)XwtgGnP}oR6BQXVKM1MPotU6YwkJn&Hg8Tr}P}dUNF|~7`$e< zxowk42QL4UeCcRIU9F;(4>1I@T-;awy6v%FH5wkeuclVO--;KmSHR=HdUy?AG4eEg67&_I9|-E0Wwfa++RhPu z|17rER2`$r4hXpnV6nGVW5u7LHjQ0k>}!R`2gDFi1=~j3Iu_COg%xHCtbsv_mJ+R0 zNW@?UE!B5Hy_w(Z;dL&M{f=&zz&s+fpd~meu#WLM=cOaQK?kXM{o$xL5cLP6;ZQUV z(8ClY6itNi5|Bu656K>KIbhl<%GfBGQ=pHB2?fw>nZi2i5-vw073vA#ZYKe{Fo14v zEQBWB9)fb=osrP)NN86C=oi`<4eg7D4#Y?t+z3ElGE|alOb_E<41}5O4g5*@6{YB zU~guHce1Xx)2>??*PB`Q8vw(M>-DtrwUq1Cl=IcJ^W~K5`K0T)r0cb` z^O?BYI+DI`C248r)krtuzFl)bJcc1tUlc^x~sM$Rju`GmXB>#Pgm(?v5Hcy9D<&n zsb@MkGq=k{J#pH-(%qGc#VC z0~n6`CMPOboc{?`fepH#>6Kf<7Gqe)07 zb+Ls_Xfyp>(?pZ*L86&JX-134DnD^n$4e-dRMVTVJ64TNZ9#kgl^=0nT1xGP5LpN6cJ0}^tzu+- zX#EYZo$ERC&6dtZUVqSh@(#V-f;OG@%liz6Ul%QGZ=L+>o-=Qb-u!oE{G#ZgT)Y3Y z{_uw|8r}>I1Ss)R#d1A5hZGWudnX4k{!mqXr3Kr6%1%Ll*s}Zf=p(=AN}VF>=WzE>5>F5!>#TL{us{AgY2{9nP|4%*v2Y8)DR`yLzw|F)(TfxY#Wt+- zs>ojusF#j^>4B#|ZJ#NEGXq&3tj=l8*7@gtIP}JE^?!X)?cCY5`^!i$fn7Eu1%Spjl~rcyS9OeIe@)vnqv#xik2%!*q?SQG{+vm4FgB9tyRkdW zm8zicC$KN_Z0=_L5D%`GDl6m%^L>|^s|3jq)~O;nBzR%uekfxjdt+om zag1u&9gK%j#;!%eK)mH}xEKys!r_$&iM415u^J(E#-JM>3BZjxS3jbI^yV_+q-GuRCryHhT zXqY@#ZSvMw0}Z1GXp1k@_3x-LE!Cn%HqTdS#;erBbsZ_%*zvl)QmxHbgW0jXt4d~v zX4k#CrF#_3RRRu9`eC$!C4d#8d^lSe(%hrF}fU2`kWxw3P%_ z3HRbM{zU?VlR(1!%w%?EJTr|LE6j|RXUEdhW6sgBfq@Zy_i&qih;12USOys80Vdfl zAux#D6V88!gqW55Nlw`Lr%3o;#wC9OgP-MuG&R*8L|v@w`8^4kX&2KQYZKK}9ph12ySO)V5kd#1*Y zJu`Cb`GEshNB3SD+y9NBuiofCaBc9b*UhC1z|RdA2J8qoKv#i^251Lyd5o>C`lY|N z<-gut`Nrs;8$Dk>Br*5Vo4G8gQ$n_v*TUkpG;QU#_dLXGX~xDe(n46a8RcTDRy7j( zo8iAcIuQMeu6s-gR8&d^M;;n29k&H`m>09{dK>hgDQ`F;=#1?TlcQh@?9zX6x!v|4 zSVML*R&*`e-f`P(Sg0_ytIcpF(rdK%b0So1>;6)%FTT?-6A&u55pO=R#5g!Uv*YCC z&NK7JpI$uoisRu|`btN!Zz6ykk5~%Pd!1H?2{tH&Z7LIVP(|Rt=sGnWvY`Gq8l^FZN(F2-(piE;L0zM2+Z4-1rq{Yv(%r&XI1WLJF5Xm00yQ}HYRaCFZ(4z?yu)!zOfIC#Rfs3iYrKF##YW`|xAOk)mi#}f_(-hyeq8bWaM||{%iiymeBUnlzE$+SU-Y5KeIJqczE354 z@8-z;gM#-?fxzGgKtenxFbLe+z(Dug8P|Ix&2znzalMrxCm`;ltn)iL*SB-753|k> z3G|UC-^w~Z%sKAlmOjWhKFBV;pILe@O+{+yoz&8;)Y2Qt#n+RIZ=@FAOfJ2VSb8n7 z^kRJJo#g!W$bxNp|E7fxYKNbx)#V#TuhjOOsvkO6KYWBX{{+qTD&6-^-N5cDGT|+$EAj>iUEkTC4pJC}|Q8^)J zqck@5+>MPU;a`kPbf3jtF3lebf+fk&ra;sT#F1X%IV&|ncKCO%uhZ{i~lfy%E%CC5+gR`>C~dlNqf)0em$fU?o>m`c!JDw}5X+=znT5{3Wy( zxF;#p_;UjQL_-jMA9yY$ZodW|c+MmYU;qNo^}RFtA*ZZ+L1Y<$$*<5fB!t0-zE3~^ zuuEYckfJMyrk`miylC_t&f5U#)n5S@C>Q@%*gp`^RPP$IIRylsx}X@_x7I{Z;{|=X*u( z_lllx=RJ4wq@njdT6|g3>wA|<1#hL@Z=?zCA?y1v=LQVkCKyOS?>jjlp7Xdhz|#0^;4|!tLb3t>nU6pT({C!t3#cmt%|X zB&Ht^%qgZ$|Bs;?_jO&OFT7trdcAJ=DZ2A*M)(Ib-?wRwTlGVGtF&_@RHHOB0J~%L z_CT%KjYi*QaeuAex5=2iPaRbEC9+QM5#PdL-~2(}{C@A;UiS>3Z>M8+I~5xk2mmZE z%>s@}i!+7A+58gDnbOiM9#$MwI1zIj_Z5;l&aXP>aZ&(?=ksL;K1}Y&D)E84xaXQj zYBYW%(}a5 z0)0Z;ABH9y%XbYT4cQZcxg@M-gKSkdN8Qz|?I9hi&Y3p*WV5D+B{S386iE5VN;o9Z0D}bg1auxjKL)=Q zftL*%cnA+KN>&$m$N&HW^aKJZS#kJ)K>nl6jQWVTzaHux6F)mDm1 zsWlsn7Nc1O8E2IS04Sp#ie<#Ml5qXd=7X@aS|cV#=W;1HDpDE#^zebKG6j}6pz8t} zj97~`5EBS3LT!iKI;4Xm8>|^R=LAq|>6{i@q2MQsG_s%^T!f^Rb~HG$7@6@TrrgQNMemZ^=MIFId?B|d z==TQE0nA`ylv)}^3PCT~I4emi1i}qbKxDP!#P*Y+-OldD9gQwysX#;!1 z!QGL-&Tw!wOrUQW+ossP3I@tpzz+q|et*&*Nc#LyUm)V~$9#S~DS(t40PWVdH|#qR z^IuH(A5VZ%g2r9oMa(bK{wGqtM-$$&G4HXc_gKVxB8l8VJiu=?# zpR;rR^rpe5|L?A=)zh~dCSPN?K4u1gL0f#Se)wGd=!x2{~81G9_Q>H$ILFr>@LUjj-~0f zrJ0qbnKHRdZzp%4*-Y6ng94c#9s)Q?eP(rOmc#~fI>~du9ff|gI4est6~_#DNWO$1 z;R=;r;sowdUouB9)3pJocZG9;$SYl#0 zF*=wS8%T{0<|c-+lf%i0k-+Hi!qCuA-=L*yKyL5nS^JO^!WOf^&v1Qn-wolE@ArPgkTpx51Oo#C1$d3Lx?EQvd3!QJI43{6TaSJTN+=;6u= z!>np9QKDw^0BC$VUqI)#F$HZ5K^u+N+Q8d@KG<~$fP}z9qN9s!kPjq;3pu}ufFZ#^ zl5LSpVJ{G5iq#uY4Uc#S*pRf>O zvm+hW4zs1h*41t6Fd-Gx*>nc8K@U%Q`1$G~#|JtzAWo+@U^$Jb^HJXAWatDrQ^?vY zRISjWM(L)@O|T}3}Q8uzvt(CF;u%V?&z#5 zyxk%2MF|0 zV~c1IDc>ooeRn(-Iv&F(1IJ?iJ>kGg$X_9=rep;*7w|)SWF_QV!`^++R|@#D0l=Uy z?j@1%`Eq`;fPjsI)u4BG$a^H>Js0zyk9w~pyiZYNl8Xu7rG)1qg@H#Rp2HE}-k^7T zz_TahJrVI-ihHgmJ$`d99jfMiD^227$_eOuJMF~f^`zs? zq~qPRYue09^u^a0!4K=lFVyws>pMbq z_7H373VrtBhMpbOrrc(8(K-;V%+Fs4%$@hooN&(`bI%@j&mM5i?gaokrVqKM54fgx zJEnIzruI0d_By6^E={d2O>HLupqmEV5kOm z!0FKxU~qaYH8q}?7>$k%g+>N~BLk7qq4*ey*w|2bbkIFCFpJh<|3F7ipAvXzK~C5Q zFl-LBhKW$hHRQHlm%?2EQH=MxZVHR-}LMA+2g^s}?A0 z1uFQ$)U@g9MaFu-9e_@1Z;*A+6x}pcFI_uG*A3CJf~Ujs-8dcgg8E5#deYIppdKW3$L`{Gx>|buExp0!PEQNbq>4*MQ<9N{XduAvm}xQ&aSa14#(@^|2wP?(C9Oyf zZ5+DVL{l0Y6qah4_1;#)eNAExm)iuV7@mM35HcVi4Yv`bg8WuGF&uAY;yDjkN6rmX zSCqpXDgb~0Ls(~OWg?9Xo|pv9WI_SBe`ck$TP?H5#aInyaCtnQU;_X_S%E?zWp5-V z5D1lfkjOT%deM$+0trXxY!{}T=>?{>T%^t%NBz0{%O2yPPAU5_%O@nglh|omYnwv)iwkafd z0>iM-I^Jd;MH1Mm?-%HLP%M*8VH@ll+|A}muByAuIMAx^Y6dS_)6uHNR-N89I6mSE zk3j3#8JKkXCmn$aDyE%*S!ZCu1uzVFJ%NxH>+nzr32qM~(+eJm1&>{|uA?5BpGeo zMkfOGn= zYwCz|@_=J{pJQsjW9oo&YTwe-ELxAroPRB~wSVSJVo-rYpT&fP{@sJlBiVf<;UP;0MiQ~Y1#fBzrh*aU#= z{eNIn)%ibwCrw%c1QH-%)vBG=4Oi4kL5l&`0x4A~BrQ$YfEG8`#m;RA3E&S9OpCp} z25}R0n{!myWa=iHC@_Du)U_atUplSAW>wTv%VrS<0V?^vp6A?~CTYrizu%wl_n+_3 zCb{>Xdwx9UInQ~{^Rx6jYYZlX!C=N;G-@zxHOPO3hOh9?XfO=D)e z|N6CA{@WI1FIf1KA1u7(N7>)I<;Oq%Ng(^X-_Kqc{BibeKhB;yvpoApKe_e$R}C35 zC`YT}zMsxo(t7Or9R2TaXUcP$@ZAy}pL3MI=j80up0)7x_j88eJH6)zIe+8(&YTH+ z|2sKP;`^JCTXWj@dOm-b-S#~{z1!aq+%AJ*x-r90_~0`)>-RbgHsfI9fI)^e=?23- z6W=?t6@#_o$GUV09r|sTa5ETszYPlp%4hhO#-DcW+8(=ak86MGFEb3A8Q>Pf#>q0^O&Mrc`%Wy^q}Rt2muHcE>ll!t+@giw z!*7NiV951#_bFFs`0u~}{`>F0|Ni^$zyJRG@4x^4`|rR1{`>F0|Ni^`^MB?3mp8Z! z(UJCbE`!=$chFY55_i4ocq?xBpT)EC=-YKofgeTp;qwRn4Y50O_)djS?SAcX+}uWI zuKcEBzpV-uX>UJ3Wn0sayg$ugjK1w(N|)=`ygSW+i(C03@D5+h;)}=l$@<%IuX0Yy zZuzf5J>sb`)EL699=D%P*|_o>hCx^{(m`Ez^{5}u(+lHYV{OMYLt!XlT2DWM18R(7 z%CC$DI=vg((zE|m%%gsCJ{s9>HtgQ*Ux!vvW%pBs29f!M(GYsw8dn{)A39Q~7F*RJ zx4uIq-TpN6!rz6nwI@0<>q(cv9UbY$S7SHE35b-aUTay{)#QI2qjHN1)Txf7XQ(+` z+Tstc^4v(ex; zBvkyFzi}D3Q(yD}3sfy3f>|-&A+7)wR{I&4zA;mu)-tO!YHLMbFcLD?(?C z<%@^-t<-#BxOA(hYp~xeFDkZ{iA%l)*jMy~Ubm~RNL^FaoWPatP{bH^ZCyOX?T>7r zg6a-Wmz7HUkMOlybY^RBIqD7s&MTv8{T+Cw?yo5<6T7krM;vu0f&&^{TWgzw=Xp_| z+y54o*ilpH5xc~RZYz(wl* z+YT7s_=PhUaB+T8L2x<0%;O)2L3UlaHS~`Qr{EP79!>=dC)*4t_~2}H=E+ZTzu5mT z(-+PEvU@%L(I_5vb<||L#b6rUNXDes9pTar4|nevx>uH>d$NC7K6TP;-1EPN38;x; zxjaCt#u$AE*cL2TFY0M@5zUC;tHwynl@{z_H`9qpvbAvOVM6`mC<3&z^W!(^W^`mX z?uC|#;epYymn#mJ(aXV>R80ndSvjXl!%JQtD^SM&`frGiWq~M!BGF)GjlAQ3fNy8w z_ShzR$LsUg(|vj<9(>$rke5_U?H0=)r@6D&olqmKt`&w>SCb(y5)-_F5aZro8Vz1C z`j@guG%(VK^-MEfjaxoplMlkdCX{cmH@tEEs9;v@&VZxAZN!Dw7#tdEN(;57)$R;- zjsN5^oltKw zGA(~{0X|xK-xtRnDPO$yu_OvF$Gctt7TgoHIe({8Vpd8lilwcP2(KX-J~dJQGecd5 zTN_z^e5-OfeS5r0X}4Nr4&PyRwzj!?=w2XQ{OS^{wqmn7RK3xxjtYHnHq>!8w^?!I z@lB=3td<_Hyj2-O#dUh1x~%11kSWz=_XW!^6&dO<)4ncK*ke{FptI`q0<$`HqV*2-D*gz(Wox_3N@;O z6pL!XAgIUanUhniszFufvPv2Z!Ri_K}as6s@*lkvZhi@|C)i&O!==X43wP^F> z^^c<++W%WZJU_{y#o~!_%{B$B;S!@_pk}(e((+HJ6&Al4CCb9ZR(D&mg+%sZbQU9t z$it$nOu|p5H}rgc($A))PnQ8)hD&=q{wUSEmRc#p?WIpLB=Kc<1_TS8RZtBTvgHr9^e0N+gI;@K~n!qB%zl7X+1|TuKBwAO{e@34oNuMO5s>h& z@dSw9UO+Q$u50oI28i>~g|2jQK=#BZd!ltH|AbBrFYpWOnb9|kLuW5u_?dRsRvRFk znX~Oy##$Q?WOpC>IIq+9Wi;A*?e&jI#@E&fUlCu4K8dC0OMG3?lM-KRru!{ica2^A zC|2vSefUGrXUaz0%Nw`#qdd!w2Ggsl6I*Hg7 z^&;Y3)aVIUw=o!0gEZwoR%ckEIpNzc0^uKb9Fv-_wu)!y$2TQ&21cE^pQFs2z3Gm= z1Maj{Wd0jP2H;24iDJ20Z0T(p|ywh~mp=%)gnE$AF`xIU*JziBgCyutS`82E0 z@GtzG#NRM<>H_=`y)U({JVm=y>LIV92o<3hq7$O?&tK{pm)O>PTGo z3+i!&rA*u)*FbM=F1xy;xzk>^Gk9n9q%=ctk-i*iB$o#!kmkX^0qe}89a74)%{=Z?K1Q)yhp(zw@C|Dz)d3E|}V zrwMQKSkcpobbF*6qW?5;uSW2_1w;(%3|RPnnrgRL4@D{e42S8iC%hK=_h^}zlWEjy zCH}mQZn_yskM}uRZJSSP;OW6egQx&j>&L&#{97flrX!v_dX~xKUGL#;+wu45lA57^ zI=fYgy87%FpjT(&>D9c>r;@JUNx1gs^pZ1>jML-JO)?A`$=H6fFZJ5J+v`(LotOrO z@Hpx>&V1kLlG0Hmk1V)uDpypcTgiMg9&PFZcSw;iaHRO!qxw z4~m~d)y)^-wRFD>)dBu)VZ2(uAEq1r4KHC0s{6~t(yM5sS}|FfM|{NX=AqrR=Z!TP z{*v{yTSvr&IYv5w;g=C7#hP^bHSJ;}t?}VpyMG1o*N3m-p^BIP2}*}eX?h0D3A5VD zNLoDN$$a3F>o}w~V5N7x zCf>M+8V`(+oq8NsZn5~U2HLM{jCAXFdI7$=n+pWB0%>%sUN68`-{Arqv;t5^oWNH%61s_8VDbu{s4SR_9DI?pKDx+T`u za;>e|u?jWnm7dsEm>zkO+R`h&jVm_mP1j?THB-Z`HQ3O^vTFcZd${zi8VITu;DQka z!$Qx>{L{JF1;;excelCLOeKcrejC*|2CrdJttn%YZ_y+NT%3oUM`NgHS4$76)0{*`c%6 zgo-F)&*n~ z*gK+Tkf_G4qTRyc3H(!x{45$>M-mdPfQt&ywN_x=0*&=~kf{22UV}V;HG`sWYYzj* zx&n1g%jf#Dfe&H|y3yN2@H@mxi9hUM#>rjgZ{L`-V&1ee?K|g+>2mlt<2~`|G2XiL zT{&_uiNN?wA$FULG42BY4KI^C5gyZyE`Dz!Ql(9L;{z^es*N z7Aw2);f`sN5eQ}|OQ&L2GlRnA=C#QB!3iv% zrM40?JK8G>xGBT$xhZF2Q`NF5Fcxaip=PLiqV8-k+-e1Am&d5GYE56m1JKzF^sjNlhhT(#kgJ&5>Y5an zN7vKcWf|@1>(I}}b+|Z~PM>v6`ptiwY9I1AzD4$GT9bdG!$+wVc;NAY=~)jEMpfm! z?G{5ayn%2kSWNdAdMM9ZNBf+&RvxjNNQuCpyH$d&z$?D|3xRI-9~g9GJJ$<3Tip^M zt_D6FX5vmVn5!(FCjY)GumDF-=glCgkS|GQz1!Dc2gv9#*Uw6%|;`^YSYIo@M(Mr&w&|y&*+1UVU|8+(~TSg&@@{xO# zfA{ZcF;lH>b&qz&i_F86om((`X)3#ezz&APrNm{o9bFjM>yuY=ToC(WQgGhg2lI4pL$abS9w9va1$ z-!;ZPwADTZxE5S&tNS&5UqnmWR{Jyjp1?h_)!lDk=A~j|d6{S)&Abbw7;fPD`HHeO*GA@Ec4kQK#dQQHjg6l2@)}EddATfERxbILx+Y?sZEIV^ zoK7MG-I9eNVgUZ7;s~@Ol?^R(lBHt|&xTUV*e2*0&i4yi7r!75-Q|D~!75hsdETOq zq|vCQc4b(K_v!ppYH^1h;RPSj5>mc2^y&#>&ri1ZmiR$$33owM~8tzM%ggMK%By9ZA;&DRFN`(j7^!tsfB?CfXRt@J9C$uPSaG4r(b@51~&B zxTBg|fxq;cc!Sy5%wGJR7ri_a4*)Mf(7)pK397k{AfwCkEp3MEAApl7G|&GoeOcVM z(3f=qf7$2rm%~s=Uzr`>p|7m@RrHnJ!CyI>=F!(^`yBcj({vks`GBxQ!cWhDWJ2M40(co)0i3b`9W)hC{~jVGLc zKRH$ZorLe@;<^c-DlUs#%$NzDh`O48ka+W!`io{CCK5mjMs>(rc;L-H;oRk0eu+AX zK%hqD$gqAr(RXBtV&~C&I;qt10_{*}exV)roh^Y&NvH-XqjpDQ%j9cH7T=?{_sB{; z@^O`N5nWX_&OOYP4AEXwE}|P^&b0=^oOzv&nNmT*rBEUhy=&30Nu5^{#a-%7A9+aV zh@~8RisX0I84id{cjOn6g5dqR21D27Zu}Z~EdkF;67C8t=&ljCcMAa8H2^;>*YWaK z_lni8qK0WXQ23Zy)wC0ZX6QVj=c&U0bL!FWxI+bqwl`Bd(7@nr1R%aEq^;Q>j5_`p4ub{g#3p}xU zuhWojTBf=q)M*G@d}CmQ+ovPlZgl?wv||h&L=?R zykhD1Bqq+0`e?CZ4#Q7gyM3a-9e0iAqbJ8*t9#puvuUQpHRz(e2e>Z8j6Hc3kYlJ_Yr)32(|Ou-ELUs>~<#dNTVV@hfK?7Xv%@IT;u6$pw;=)^xgU zl(Je(>UaY1dP3OX1&|E;H*LZuP(UB$q!hX3QcdQ-|3!5wvYt9CU&xHT;G-AvvDskul|kqgZbllKQLUw`@uf`vUc#7 zz55ruAFNrz`+@t1ydUhlllOz}yLdlX^CR95*4!4qA8h(D?+4v{Rj}zX-VfY$ydSs~ z-VX}7>XMEJc|T}c!~21IC4J4B&tJ3GJj(k)HorIDu(Hp7;1zAJq!G6RX_wt}R17@D zr&SP{95pJQp=Wz?chv1%J`&v5A)GcL1pYX}j0a}nAC-ORyAQ}POxy>VD1O+*o%jKg zkD2pEy%u$k8WF!8BWb`{VkfCg@Fdk|SKF$RSYEOhLxt1)9h+GGTp;ecxPM(5SRm|f zHFm`d4&Iwt)>dp~adff0t=Peuoatu`%z2`9|B7o_8DJ3eYC(!XiE`?q!7-99xVb@p z`zGGc9s%b0o9G62<2&Lvm#~yqS#G5+$DsuEw_JT!onpa((duklY*OyF07E8BF$ZR- zgT2iiX;9Pb5@yJ0)O=^F;_3-psord9{vb`=-`ts|MvnbPeIxXN>DUH!Fc#@h5~RIz z5s}IsqRGo>?^bR$E2hc`cUu-w?Nxkm*UpiW9>8#cpkZE9j{VJNGAawq$^wgehjoT> zhgCgaZB_4p{(cp1IKK#+oyxKZ`k@4m+iC$GL)`&&yS7K7*s>n=Uh7T!KU2p&l6%j^ z);qt;?>?f?>##CdWxA&BfKqHBqk`>FMd9AO9@sU^(chVSgLUa{6_yFXh%)Xz`r)|G zG)$RnAzJ}-l5111F`ZPmQ$K<|T^Z`K2t5fN56>EIbiTc4cKAC+=RX#WqOw^}pz?8# zsB@iGs`^gjWUWcuqgJ`SoIZWjdKf&eZkLrVe1=-_n3ajWeS*EhWEfV=i6eaSe5TI8 zp4XP){v%XO1Jz=177v5+_a4};&Fb&K#xGl;;CAi}oX`8ES)E$nqmH`KwB5N^S!!06 zT7sVu9u=FNZGk~P*sER0l_|^3xUDR=#A!ot{(B}G)6Gcx3Eq#}3|im7i;4&cdK^AN zxE{nay?y$rKq>6b>l;fB$zS~2}UkjAK6&A&@5|L_h-sR(7dl#C|Y%tcC5`8vcOqitvj9Kyl%}MW!S87w9 z-lI)9;B&%dH8Okx>FRPjC}Xr-n}0Z_f({Zz{C_z6cWtKGpA)#oZzEB{Z1hSLZ%Knz z_%I9CoNd8TngsUN9J2U(d%`4o6o5LN<$sO04)=Jpja(-pzs(u3hIp5{5 zncxp)x8(?xi}>OF^l*=uk2PQJ6*p!|HRV&XiMyUBal&KN<8P=V*AQIOQ2B*wq22A@ z0cbEt{7uAs{P=u%mYEa3UtoUCFh3nNp~M<6VWGIUJwWvi|C05myebB$ZfoxTrI%5W zi%|q~4wB?kC$1#Sww#VeNu<~TB&h0p9`cA!Uq~~QdqLuTYjj{#NMJw%dozH6%``6# z7*G8P<1Nl~CjtZN<-<~TEEfNX0W$MTvU2o_>&}x9d9v{_X3cE1yZC^`z__OFgst`^ zx-Pcnl5vpj!A-0pl*7E4Oe?&032tsrj856S8j0(`zyF92JBAgCMlmj1Z4L;Cx+841 zsmsmS#WN=q6EKUdUfIfDpnvKl-8+b}x<7ZHt^OD$)49V|+l6nGQx{v*X;=rFyf`du z0e4f5?+59vH%P{^BU+`i@A43+>w_c?R8w~zLP6QnZt;EGV4U>gc(~5b+;Z^Es{E$P zB!b3h3^uAlw{&Pt_s28Q>#369#X3`B`IC-MYY7Y&GvMw1!+&IX-288Wg?-}$u~OFc zRB?+&|NZY6w56GUu)HQmT+3#dcA)9S;gT$6wpp1C9(ERU(k5L(@cyz9cqy-Le zP)emknUN`e`8@c+3OnG44Qxl?a=_bp1^@)YgN*tB!u#9+UErhy;dJ0;{y{K6a)R@O z6O%78NSD9N9t{lE=I3%0(-zSn4R0n8<&xk%5AQfnVg`ak)R6>`ve*v8l8@PiimA(EIm zi7lA=ytx=yb}~G1ar37pwJp?S3U%~|vv-2AeBI~95)xmGASTn2zq48`abPur6j~tV z%Rf+eV)3OoF@~Dcus>F}6`ciCC;m+RmU3rWwPKtcYR&{V^(irI_hqrBF>wBh0z<&z zCl1oBZVxqOfnWMJAFRkYbsF*R!%?G0otfzk`?AVBB&4&Gydj$igHL=ioX-V=#Jny( z7lgmxt*t*kUu@gV^gw)$!8g8z-y4kqGqkSXRmZ<2iP~&+$ZjeVov_>*7}+rE8uN+m z#_-f@%)xdw55!hxLR@oi~TRaF_Yub)~oI_!Fx z^x;1mPS!tUki|w2N@m2)+gu$R|HN`oHlo~(S0ug|3@eg8aF>em2kFxs zY@oUR*#z?NFqO!pk9r~x(c!$z3FSO?kO6JkFkY$`Wj-nGtK4k0|0X77wmP0H|J6xk z!LP}WTw;)U|z5!g#JL*Us>&v810MPEw>LlxW zx&KS}UHdF;%QRPV;)-JvHxYydwP=&0j+C=A)*LpE1rvh#$xOkfBu-A}Gdwv%y`b9S zUQjbA>@|{H?A0z_Wtd8n;&QZR82qFK05f_>;^a5@rMC1#I`?t^?`S5xa#CfDw^EHA zTw`TmSd5%^Xs>Xg&Y8b6B-;;n{xI=8$NCR72N;o|U?ZMfh?)&j z_hR!O`V$K$PBHfN>T`(qSBv2@7Y%%aY%)joU|bC@2zzlnFD_JPj8jL_AmKga(ZE$1 z&yT`|;6_W>s)Vie&Sr1mVnU3dZqPDefhCwe!EFw}_r>9C4t@@nhJ+7lp|w)eS9>Ky z$~N+L4EPnu8$}y?fo}5>l{3e7MOotNjT)pX=XGATk&VIWAx_U-19S_$A?A;PFg=7F zS{K!K0;ABu2@CAO0qPewp}~=|Z=Y4o(QEq~6oKd>TJBO8Oq8wt@u}Ya4DRjEoAT*; zJe}6HhW1}_;85S4FvS94&J>G!vzfMHoabjjo)b2|ri*m0vGj^`@*vVd|IR7ZFz~p;!neqjm@8?(J}lQb?$Xm=R8Z;d|fbK^;jlUL~c?%mPMC_9X|~_ z9#O|CdFn#Qqs;0;3rVFGT9pM>wal)#?QqlU8VW(tO*2rl5{!gCcQ`$kh0pfNWF$WX z04%boQ)nWrVe1Vz^9Sk7U(RR#UE&>RWW5B4Js?s56)lLps?!uvn*j)bVTS|*o|Iav zOwBZ_uAa(N5Rb`!`K?tR_-mVF_hEFjj-1PPC&tIxiL-3{|2_>5&mvT z7&?Z?V;T5p=b1&rfF-%_mzyHK@QfU>nIN-AyaF@wsyUmCF+&uVXMfFlJcNsc3(LL zwUHg=g6UOs&c>eRWBJcyI{huy*8U6YlW37KnHH@_iK@y?#yRs+_={a^>*Fs*DmS<~ zbc^fpVz@X8GGnwdV~jE*r>!`TEXiokoks*;DJ}rymCA97 zccM~UsLYtEtll&j`{C-%gUKs`>7D@})12^S>`7ZZ%x}S!+qaE~q4>x|SSWu_!@0GQ z%Bv$KpeFbdl7#yZf<4td1Y}3Q6PlUJ&GMlw* zqhaavuscsxFH@l5b4?OHkId*FpBosT>;Io&astJXndRJ7o zLo$mU*fpM@i>}L^y8>U!ZFWQI11;+pFv^jo@CJBQIQH;W!Mv!gu7VeTA;|{jU}O?K zv?FCa?YzOi;(4Z>8!S=;E%~VIUX=18c)&Pyh-9F!0cfZCJ>sWK{gX|2dWpNOj)Y<~ z8MfMH*7yFJM2=(Ndt;%A8%vkPIpsd=%j%56aAgiv-b>?cas3Pq&&Pa?aYlj{V3!;H zZ2)7OPW3+kLvT~h|4FAfILLX70 ztukBP+o0&>KH;UhinFoMCaKG_F%j3Q(@0r*HPH%k&UF+pNz|Nk`yq+6aic5e|B3|c zw%Vn%TL3ZOcEZs!1|Wo@pJIG^+FZvu-si|oPijYltrCm>ksdvm5$Dc#!UdYK(ZUi8_+tC-Y-7gH*_A$V%CJ-nU5HRSIk zo3Q4x8!y}AkrYYY+t4oN50%oQtp=TmCD{`o_5)2W{c8iaQ#FS*wCaqhp`FIsgVt02K< zNhos3lCicI*NpSRDm-UxHoj2vyiPk&ar!~y=N~ilJ3;BcsYVB)@NoPY@OQGJ&~PFB zHk#xArI{1|nUd`ZaXxLnk`7A&cS+z|@O8wwdto-dq2pMTY1;yh2F7I|IhU}@mNmag z`Wo_WaG_-$cdwHoZi@k@$O1*b`uSP?N?%`7H4sIAZeZCLNziIWtKFKA%@Q@pU@R|V zB{a>)h?F3S)^p*nhH&gytwZWb34V=PXp zBr}6@phpJ3fUBpJhjHkOHxtVT|Dl~s#g#vg@u5;|ke48XkcBdTPEsm)0Ma1w@5f^A z1!p%Nl0U{Iimdz{&r2&^0AxLlKs@&ksT!d2>Ug>(YuoZzA9=>ZkCBCTd#ngww^p`D zdVh;8h5s*@0zNO)WS|c?33f+EzWk(&LWC_nX!DQR%&Y%KqqM-WW9<~Kr%yE!w6Yhd zc0~tKW22RRcl$p8+p_YL=(jWmZRH+9HynZdTR|(=j4g;smoYFyxu#Ww@UE1zFp6 z*W}}bal{P}ZPiJvLyqQdNn@}mYV007-z=YhO<<8Vk=Fq|GbU6^rW9W_);ayp1LVF9TGV;78r&UVXg<`ud}7 z8B|glKzwq_(2oIxTz^Yb@>_KJd?1~C)GXQ(Xv=sUb&97RCjN)C6?6cckwd4x;%vn` z8V$r9`{+phAL^GCVy@j<3sTTYEf!}puXQuGbzF|7FT*A6<%nvh$uTAkFcBY5_2T2S zgcWdp4E`}}{Gvy~44R60cGah0qqiKhRzfq3dAB-S10%?o&0}hTf9)yWa0v~gLA%CX zGp-JhMQ#3PWz~<$s;g4CNRfXu390=}n|W8r7Vn)j7+8dB2T7_2ONR};%^(9YSVWe^ zkZgbF&r5j!7yLe!R3^V{CBo+Bi7UyJbx9BFEU|AuVqLxX(U^ih!!i!fU z3(qa2o{kkC8I_VOIlZ(I)$i-gqHk$7mONS{ulN$kV0(-^FFx>#H08-E8-46mO zMCKB|xt^^+(S=>`2yG}PC(%sW5`3c(dIhF|R&}V`z+~6e1MJhXgz1omM{{a0v*Ko&b z-L>{ay8B}?DjL{G-(We0pGcp}cP?w3%Xb17$ahLtOBY;ubv3TKw)5}S^lW-TjKQtO zoR4FEt_;^nL>oW`EU#t}PgvX&`WKmSj40P!Ak++4*|c%eft6<+C=xs<)*prplqw?I zi8*8#_Kq^1u6ag5-Q^v+;5m%lp^IKCDzc;S}6BHGgaa!!)j^UsxL6Q|FuZb*JMo^3zUuJkoW{N0d zQ1>d=Dqb_<<4kiHgD!QZqi!c8q&Oc=vznUwm!h!EZFfwhnWjT{-jS{s!Z!mOJffy8 z4jV?OyEJSNKYhS(?$Br9nO0rk^4mY9h?kSf{xOs7A3GD`eF1(M_CD(cB#u}R*mxG( z*%ammNZ2tBSd|uAN%|3Xe~Cf4X-S2PGkJ*GiXzJZ#xa0fyrj}%3$>YjVnr)(jW7zs zEnH7OpnDDuktY%DlJ?!S#*&Y}nx1?5$BBAwzFsI>Ur!fex++CceL1gq{P~(a1^#&b z8%deJ^aV-M!7;kJSsm^FT-*4LBpiQ0*DbUa^TxM<)b;VSs`NOHHSv8g8{+M2agv>n zVr%5wuWB&F7Woo&Jg@Q-fgd%_`nazZX$p)RGCK(_vn5>C(>J92z$s&y>NWeRF?c>h z9x9&ikuscv|6+9P8YFvSZP4h0I%G5xTM8~2DWmgd1Ux;3leE=Mi|V4-KK?5jZGm#1 zj>s70tr(>wK8Wu}5ulh41iavUwRz>Vs(+35f1(DKBlVBM;>>P>zgcZ7$9WErlrfJhdbbGVx1Q_qZ~M^>1!tNOcVl zEr)suX7YLPjG?|hZ^U;0<^2?IB`~0>ax5`D4aJBU5y%eD%m%OE$ZrarGTGKPg}nu3 zt;Jb}LN_+}LXsXXcfblF|3D~;$J;%j?L*7NvDXh()_0U13E!XbzaT-O6S$>pQk#nvON+xJ$vQW?rXeNqVJ@dq2lw` zHP4{n)ipNpLG0>lC(l7)b%Ch4Rb-23;yt-DX+c$vPH}y28bC-Tw9$DI7ZCl5JLJKQ!~3sX0!EZu%qid|MA!=8HQ7!IQ^Nlgp8MBv=&b^!VSoq zMS=+sZHp&<@&@UX7aRm8*)0V-KqcKLa!*@v7H_+s*BUec|E-q9mi~{EjOPl@W!cCt zK3hv3(%#qOOd1pZCGFo+dH;ST-hUzC`E-8%Lh}AzVDCr10Mi6*`x4)MI$niyJjDBE zWeRy2>Ra4jeIUuilyDf(Va=p|a;r0@69?m+9%Er<74*vRgsNQri; zZsx2apE7fn_}+H1Q_oZ?ElQ>Dj@^5~E_Z8T1oKmnyV5442|oHAovTX6w1RNg|;4Zq0_wQru7Hb$;|lR9k- zVkOanHmSiLkmt(#q@8RHc^|KAW=hVnYv;t%FMAoV%n}~Oh@?9XQ1BI$of8VPSH?l%?xqw_NIpM2(H}mG zLo4XmHoBMh%^A(S@4HM6!_eVt%UN4S;o|8!t)#U;h9g3}!ZxOep7jc|>9iyEcgB?c zX|nf?KSNS4d9*L>2v>Zdly)c;ht>VqbU*tBUAPcE^AL3ownH-vy?JWY z^p$5QiQD4zm*~q`HC?G1r_AfNQB)p-Uo~jRKrvp0a@e5RVx+)@Y4-dB&g}tPSs90A zB&II^07-c0v_GxT)B+wJ;+|cmnTQNOV&+sj>Cc2ot3&Z-0wr`5@j`K&r_P+7y9;V8 z?$|c^!u%@U0vP0|Z<>6CBc@H2n6FMNQD#i1x=Rp^ENmW{`oKkd6=YY`*@_NWe^|)f z5H|rFcqNu1DryJR>*}=WruT}R?HImNDs1re(AL8qEId>N%LqVW^{npgQWj!{iG<7A|VVA$9r)YzHG`Z}Vck z9T#^zwCKpCPR4r4#Cpl3!s9YArKXnTSI{qdt5m3t32_!KxcH8lB2`bZt2d8R%S+0! zwYh{k#o<9R2W(?y?x00VX1g+^zJOT7Gum=8H2C zU*drm=!w2{ySm|CzVI`U7kyeUT*jiY&U~t~oOsyN#KVS~1}Ls}Wr{;iY&}PGJq*v= z3Sq0gi%@tnhK%X8W2WpJ`P**Vj)7&M63ad*_eIZ?l*w-%jA@j4>069JaNt&&l8N9>TqDDAUFtQTgVhbuddf9^X z4tZb3eeXZMljQ&JCiXFltPY0i_TiFm!^81+vqRlgC*`5{llP^jj`;_2cD1UN(6BjR z^=1tTQ#7aUp2daa%W2{eALCSS347LLc{PbXud7Q?z}!!PxCIxyQVWIAzVZyOH`o-B zzZ5(e*bep7DeYuL)|B(Cn9$=j(Q+wyE7#=q^lZ(sV4=aWxHmQD5nK1Q9SM0kJbY$i% z?8o5QonCdx80^B38aqM}3qsoY#II%eq^^OXNJh}?JO*ci_<$t?q+{luO4Ri*>S~xm zr@)bPx@?_-BVbD&h?;h!j5`8WC7mC!e}0fco|6^R$6yx)*F)9_g`|`z@72jf#F5L` zJWCDQl?+?$B%r0QS1)t5fA$tW%boau@x|Ac5vP}_B+r&UiW#3&%)oa9`CcZ)j)nQ+ zzRQTD*!|PFN+nG+YL)g|9TCW;!LTy#fJKqJJpVxX;5U3q>0zmzQHOAtwySl<*W&U)dztEVhGDT3(3tND81C1@)C#>by(^F@1>bcMyNC z5gDCPBJ<>_;CqzH>9Cc-G?zmz@aXe04B1{WZ!xjtTV9YXdGSQW^&hxy9jLz1aS#Rb zeFq|+n!)Bz8!gUX%-IdTAX_F^mQIP{^%4Fj62LS2y>l>(3>Zy?_WuYz=5xcxCYJn3 zhtH%SF>SNZwR@rE0>xU|>bisoIaw0>#PZDqxQPTi5QqOO;5q`djb2jQ1UpFp@Q;#? zgY3|`9_<>t!n;{mvyoIcQfd=4!)va^A7EmZU1YC;|_A8oEz$3HA3HhGN? zBuIgKyXk}Fb6+__m0WOB0(2+}rlYya7G@y;YqwVnmc4``xCO!%+&CSH-JpCh9OL_* zLO459Ef=-$dQYIYo|;VBL%%?Z5&Gp50>9Lov&hg9c9UKhiS@)=IYQ50FB!q-4oNZP zlq?dY`P{LBNA!XLS#XtBuIukSIjuncZxxt6b7oc}fsZshU1?{9Jx5npwiOQad3!*8|X`V7{T==58h z@T0rN0t1XA;=ntSOSY5IHacOKZfJREI6Lgx=q?vcZ}GZ|j@(Tla48m||A>T_R}!qw zRTNz&8k|7j4ZDQf3kcier(yt4MVC)4{R|-4ft$81$m^S)6XXl95}rpBaYUa81ndInF&+QX&jvH!j!nUlxGDI$ z263N%qU^$_?=rZxG3w(Ydyh_TxJbYsBw)W-^YkXW+p%7ot zn*JE;POffT_iy?`gea&yA{QLnRYO zuRRtr?eSQVF99CfWQuX4gDf*#RytPxn%#oK%N`EDa1V(Wvtjj+f5M9;sk}gWX_Ob1 z(r55lCAdimZdQU@MR+eU9>>X7yELLyybOmr#YIuTmD1O-UsUYCzM+)vR^g6Tu@6B- zl+w4<61xJ9#O+W+q+e7Z(6~7pel{K9wbTeK<_=R#EmJw=aS0w#ruf-gzZQAUV9M4a z2BBBwB1ew2k3=ix-wb%DQ~UsRCRHE)8C+HjWi{qrV7odQVSx^*h%^O3 z%VaAJWHQ(~2gxB^TalVZ{TYvV%B`SxCO9*8t3Tr=7iH(}qa<-|APvS{xM&w7iM z%WtR0i}G+pv;o*v$e{Nu&pp|fKk_Wcd?@zA#@oMCE~#(xHj{~h(P#96@%YiWiCGYD zz?y#osUc6`P)b#e-z52qLZn7QMBXWT3|gq}^a;yz;Mp={BQfg;>2Lx{>A!GPhrh+W zUOXC-EYo(Scy#Yg{roAZ%pZRUz=0i{XQ_#bfXpsZ#g7nwbHfh}x;|W%4*ifEyz%Q# z#@g+{sr(ZVazZ;ub5rh<@e;_YY(hyMxg*aCn(E!-)Jg3q*)Vs2x%R#A)B$D~RS-}0 z4m=9D;(47Dei%pL*k8y(L~DTNCv#4li^>%A2l{*Q?;oW>+}6`(Vp7||0JhcLzU)K;!EEv#CT4Nuj(7_-=57;s?KD0Zb>yE5U(&zPh|1?44L9Rx|Hs zCM&|?xxpL$SCtQvm3LjI7SG2Z2qosLP9Q~e@o?2U7gjG5h6OBzp=PtwwD35hRSgYA z3l|MkXUtWmhs;q3ULycj-CJjqRfBQo`$&rOeI~g^LSWOO%sZ^iI|L4kW1r3I5wmYk zgH=5p8o^fOBBX6x{+Sc$FB6k(t9y!L^wWj&n5~wAovX{|=Po;fU^~mYZ4ds`zyhOx zLTaGmP!dxC9>I;!nP_0VCb5bXI-8f6)#k3D#I||dB+%LldoLgVn(-W+sSeX|G;3uP zKSpj&ma*l<06Z>*&t~UzGz#{F#aPR_c~rtroeCa;d@`K%i1XN@tJE86%>LP+1{_*~ zeU^N3>-A}DMk}N*3)|_XW!)>IWZ1eS{Wbam0brB1w|2{Y8D=YORp&S`^Z8)G{Wu>G zPawX0G3k7u7L8FoqjiI>J4Q>3u7G7UJ-BgnpCzTq-&g^>0+C=F=2>eG0+V1?x;!hi z!>GX>IMwxy&|V5ad$CvwS$lfnCFt-DBOzOH7MK<3Wu)=;#Xn%FP9Wz$z=cn`$R|@* z$Ic)$qBGSbsM!1!bmZ#Dtx1B4tx{02p1w6i!(lFpfQyspLN@R;8gQOP%(jsPK}rvL z31y@C8AH+YRh-Z1nNw1oCXI99>Y!%cq3;Rmov+3wL{h+Ci64(qE=V`IE-=!n8g3OU6m$%+CK zBoNkq|34YKkp-Sy#9p&l{C&3CiznM8PY_Q$iDhyB0(#G_{W>}(#ZUCX9-ic)wz><*t&l{cwpt3{2kV`Ii1`sG z2es9mq^ejJ`avfpa1f9D05PDvd1`y^_DM}|84dT$Rv+C;g?8n>vGPM-TSC(rdkPM_f+6T?t9Mmg=yq{6u7>e`~%i;W-B=Um6w@{ zr@BQWC3itl5;j^*Z<=FnvHcv9xNuk`c#IjZU z<-e9RmYLf~a~*1$4o>CWw*Gb3KJm*Z6V=s_{S2gpD6mh8$&fgt_E*&6C#b2Vj#`}j zewslB6`)f9iK=?am12k@ro%;3Q{m6xAOn?CmC6$FsO)j5t-z-XtzaMYYdI5IW|+8m zWYW@BW80*<4u*8hu0;cZ^2UwKQ4~myqJTMyo?eaut{kK5$W~^ru(7(sCr-9X( zVvM0-S{A3l3S7>C=iwPenA9iO`eQrQy_Y)LDaH|Yp6&FG_fVjgBq9*FhERyUMyj*#1`;fw9`;b+S@$;C8h=EN+{_;yU5iyFFe|c^b5!s)W!&2J2hh5!L z`1KNj9K3LMU+I@qnwzWRiR}S9)m-sZv`+qm#hSs2A`r($Vw#mLMb1vklp zA4`g&>7ee=Nwmkm0JKY7w>@45)e2i8gh<>W){)A+J3-SVo}Ev#d|X+zl__(9@dm*Rg|n23rf_+7 z8BT6UW{<#OoANkC zNx1T>BfwMfFto$+`CWLvj%0u|#`kH@z_S+9Bx^lGWS)%riUqFFdoIH^`)RwY2@pj(jkOj7n)6 zGHS?et6R>eC{2@XU47G+SZpjZQwalE8pG;pFT<9C8@^3E3y;4Ga%&J)*yBYBYGT-FQ_fya%2aM&1s5hhh>DqkAH~gZ%=&6C09S zlNk&mubkq!?A}BU_~PE519*K$pdB$OGD)gaX4kJ|lf%AaFz9??%&wo;+eCRnTRxZ0 zRKvaE2iUO0!X9nqA@FnYY+6H#>vc#KAQ}V@0%A_$K=30%y$PbhBM=S3fkwT_LBTR` zj4Y!7G{}zk%&P|W+<%>|wxixz2>XxCYx7ZQsWFir8*9ws`A7JqnIb#)auQ&=tzPPN3Aw~r zA)tg97p69dp+nN&9`VEx9R}YP2RST9 z;R$(!E!SYbhI_|3gjYoIP>ePT7zx<(QO z<6MRLF0zm)xX4QLT}0Ag+PaHp!QKGI2p$Io$C@!)6J`a|x7xwWP<-S0%JN&`J6w_J zD@->;DAwzqDc7H!@-Ms-9FX6{2ZaX9 zic_})zvY7>r7hzYLKRB2fP@QHQA#NiO3BdzZe|J}pCC8^#zSEOZOQU%W*)hg(CJGS z{-HXw)Og9lr))3oREDZ<6Isla3{YlZl@3-!{V_QvJ3?`Hn@s zk!qTw++?+FDKu6kED3oo2t}qW$qCF4MaD0=+4f@n^v5aCOL!c<@|#+=($S)2D_Jy z%bJc~78*Xn-<7C84S${ZeG$G5>Bh8lQ+mJu%&Bgs^_d?`bzyuyHC#_kx-Ln&o}P5= zPP+CdY}v-0R&ft?6F zbbMv=A17g&*@dACTN1OkZk^eUGIlzGV#o$?WqP!hfH2dW*XJVtc8L>8<#TI$v z2Hdz_EwVacwZb>9#EmP$ciO`<>>i)^{6$Si61bQi+Wi|(Pa{LPccjusYODx0@;I3g zPqWnG6MNa_)UNJ;1;k~|VRK;^Sudu6U%`$U94`~LU!_j5POxSKhf{)FtWal^gDp~o zaCJb#ES4Eb&$pb)!cYCGfw1|F=c1}M`k$~1FTDt|dr zfob7w&r^p|1UvJ+VaLicF_g7*pwt}lwwO{X4F#H(%MiSK#2=v1gq}IW*&fIRXI_Q_ z)`d|m?F(oG<97z6DtdR8Z>h2Gmy2;jgQMM7sg{8pl+mf+Fw z5_@?$X>d52q&Q87r-O@g^A9NVENkG;4XJOF74*jlF_`*pZaaoPCA8gI0(Jyhl)ey+n{pdBbDF`t={P$Na~>jCaj)1XzyIkN?=#Vg1Et z(BvrjO>G&pGAJB688+I{mcs5M95lKnN8LjfRqVR~6;=Nc8+!Axdi<{=!>({LXJbu_ zkAtun;EDqyD68{;*9u>T;>h~iX!=TjEfZfPki3ZEBZZ64h9Xxkxz_gLihDK?lTdd% zeWlO(bef?nyET0(h?}wNyrg8@Z?>jS#{=+Hf4!M>`~JvF%zEv`>Rd;!V2OaHpI2j- z0h!ug(qH5Hm{r7CIvq_7!QUwSU5P(~$z(8^zT&^MIQx*plu|kzrT{{sYYT}6)`$^X z*j|Q>fRT+ki?d4){h5sPR!5g^DmM2;F7DCZ{?&^~Fz5O+$yG^xEIWe))n`JV!rbvh zi`iDYo`i~_CX3te{)7_zzD#MM#o1Vr>*pw1MOrvawlR4A-4Wm+kz1sBNx?!2GVOF7UvgC_Y77qZ$$EVrVaeuEp-X`g zpGgg+9m`7x78xUvz#Alb==6-`?~N}(J?%p_0}!HS7^SY5{VA!bkB5=H7-Gdt?P5({h8}0h?Lz>Tr(dIx(=rBGZnJE`9erq=%;hWQ z`~e59bm}ycSc=j=kmCnkspF|Rbw}$kY_>a6 z*7nmpSz9H!K~tmS;~Lc%l32X!l}E@-9BIgJ#k=B%wHgm~?_~eF^>Eey1?Ben_bQqa z|2_iDw&`g=69b`%fzf2+eUt{BZg~7#UuFio?oVu_ltMopmCcxI^(Y(nIveNngat-n zO(6xuo%h3xf^JFY>VJxQH0aL@QobKNlD(SsM}EIWkEcb6gLq9x*JNfS=6BW}h?P;6 z6td7^O;&uNWP7byplKS4*=pw*WuAGT*B^L=7Rcr=V+&-DO2K?(j_d+Dm9E{)Zq~PQ z!Dr9J3jRi==0^LczT%46S_jy}fg+odPEo!erguDgHQg_hTh}Ya2Ju@Yr$L@>8SgO@ z*;i_?+WdLMx(&jW{}4(dkSMt$M;{r@W6R^m6ckdcPM5gUXkBwtCuDN&!0qLFYLE6< zn`0><&X9snC|3$6=v`exm^^YEW4skYzeZ{S0z-8O{oueqRuE-B2@QNw)H#R2{fHw& ziy%fAK>$d)l?eYa7S3M`t`fID{}3Biu! zBm{?lV~|M*&U~4WgdnEB1WK5l-9Ef>GrXs~fcGDNW8g4wyF?qYN=}s$WobwN7G=Lo zl^~&xOoo7H>iH%AreS^kgUM~AvHnx3BhELnj+icK!Cyw()C!{Kv0%uiHg8IQ)UeM| z#@jQCgL#hzKbEjfTM#~jf65&d>zy*;m5FSkPIdHK7F2h5#IqEgpP$2DSo|1f7aR`4 zzd{nGTg8PNKFfUmO@Bm&?v-tXL_Tpp>!%#aulz@RM1R#)gj5iBwE^om zJoYc1VkFm4k#SDPKf*=v3uL2KR}tQ7`A2ViN;kEV=&YkQp?M$664{^3;i>s&ic&?4 zvbmR&!>LT?(3@5AwI_nfx$J?bHvu`7>W(S zCg0|elxA69xYll`Zc3ySSNvO7`PGG}JGSzb%>;WM8>M%2t{V&RQC` zmNStBfm2pIcfX#p)MrL&(I|e}w7SeiDTbuh+H-ILQ7UbWjd#nl_K7*->%SrGxi*aR zF$^SP3`NdgG=?IPou{q&`?!h+j8w(RcRl_9@hNjwgdcH zJ-_zwAbPFLzkpu)242FvA55n!q!oT|Jk_P~mBSIAp7bpP)cPzMJX{&zNx>{-Bb8T& z1eo`+JDZnhcsn^%e)Wd9{(pN2RS;3Pulx{;?Oy0P-v3dAkZuJhF^=Hs-Hv}W|2=&v z>Upp8{f-l_PqnEHoG&rHo?&0DojA_F9DH`6Fewxk91k{1c=cAstjZby-Z+Usc z5N6MI25xK^!o2^^;I~4N0k%3Trxbyt*Ze9`rtU8_F4ZGGT*-2EI@A4z@V#5|M6pvH z1x_91OQ2cUH^R#;zPMGws#Cd=#X~DKE|E`*w%Ykbfghm-tEqFvC11o=!W2Ag;FStN zu)G7ZB%*;bmY@?rhrJ>f?y7n*y9L{e^t$6-jxMd_)m8dVogjS$7<> zxRbR08ht!dF&^RvAL$*=Pg4#@4j6P!QZFM`Q3erB?uV~T)UkTWQI*^g;7TE_?Y}IE z!JbT;#sT1xqpjhH_!*CKs<<%S^!OuynrOLCbDMzprd50ly$TMoqx-isgw_ROA2GL) z*S~fe2zb3w$&t^-z3vvcK4Wz4(4t3Ve5)+2{c0Fgp8)j~0_hxJ3;y zktlsRKMI>GWm%K5>~&??%h5GNFZ>0BD`C3rk?5p8Pn(PSGa^+0 z0_{l^Zz;!i)~d4 zv38n$KoGtphY`eK@lZPayZI&GrDH z;)iE@`I@}tJPf)q4g>XwO&lyYi`?1NQK-SFHb~+-frzilRMzx`Tj3vK=4nPeI!Kgu z7p`n0YV8Dhve@n`2Il5Rg$*7?KAjwBZrVjcPq=E-mhm`c0}#rONy{09EuMo^`GEOntLBwz#ar55KhEy$XS!D2 z6JtZ4m2BvSGsK4eoI>{0Z8#IphF<=2VngX3v7w(rKjEiifJWf_W5`PBp>FimD+svaRU;5d)BpU?x{hn88=@s7b3A~&B#HiVj?x8Vj2 zz2;Yv(}gVa5Oca@`NeGRbx$X-xuYcMZq)D-*?Vi7 zl<~BA+iH)Rcvp9}+8$g+A+H|LXT<9-A-?YMUuvdl?ckB5Y1&XsYw#!;Z{SGMWx@w} zu=^x3d36EzB8ng~j{-sir_004xl^d*GxR3ZW2*?j`8z|AH02S3KjLWE?pr`Oeu>0v zgfnt=uTgP?*ps_!Yv+--2Au3{KAoX%Zx)xBw*NeL`~HYt5XKp^U?8NPfpv(aD0JnoHEH=C^cHgh+{ucxkf+`@3Dp8d59#|#Q|KrTS0lPR5mo?ojw0qy)J*)fw7&}~uX=;vjc zPO)$a$A452JW@!uQj|ZSdu20~C;{?N45T^=|AQuOJIJJw50R=NyxGJuDZG=a;p<}! zyS0WZw1!Qm`?mT7Z`^zWm4e3H7E?sI(?Ufm4!O(3g+N3~;J0owq@v&Wf?#uqflxKa zRvTjFEY6RQX_+q)Sd|#-Tt#}8seU(c6LqtD1-s8JW`OD}bC@1^w9FJ5O-l|}k3<*< zR9xovFW83u{9~&@CSX3^pF&95Tbk%t!psaa&a(MleqVfjG3Qd%xKAnJ)Yv~eZGRDi zc2=-GXkcB(yi5d~I;hb(+p2fiL?3xNqOl#5c$j$3e(jN--t#YH&EfmCGcDa06`v3% zp9v0dKDkqZEa8kp7ILnLt~{VDFLYPG{>Lfp!1p!Evru8B;4?BU4DZ00j$(~89JAf` z{b(%565!WRKa~y&J7xcoU|M63{dU@CBxpmtQ`q&F)-WsY?}2?w*WY6k`w>m7Q=8b> zS;#Hk4kS)2J@Sw^IALbxd_oiBl}*}ZlgaTxp^p)3|Ef*E@~;6Dnxw!im z{E~jiB41_bcwBb!Uw`d2o>q0W9Np-bXdBq{3Yz*2skKOvl%|ZIEfqD=ELMhymqxPD zMAO-8@N~Hx
    0ERB)h%aKX1V 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%0D{Cy*%Ou8dy(GX$kZ*|- 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

    r>ALqoP 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

    sPt#~e7svQq9Zs5(1bSwFzF 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�p}v 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 T1or_`zzpy=@;Y&aV#%CO%Arw4 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Max/PlasmaExport.ms b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Max/PlasmaExport.ms new file mode 100644 index 00000000..2d608dbc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Max/PlasmaExport.ms @@ -0,0 +1,39 @@ +-- Export function, takes an input dir (where max scene files are), +-- and an output dir, where the exported files should go +-- + +function ExportScenes = +( + scnInDir = GetSavePath caption:"Choose Max Source Directory" + if scnInDir == undefined then return "Cancel" + scnInDir = scnInDir + "\\" + + scnOutFile = GetSaveFileName caption:"Choose Dest File" types:"PRD(*.prd)|*.prd|" + if scnOutFile == undefined then return "Cancel" + scnOutDir = getFileNamePath(scnOutFile) + "dat\\" + + makeDir scnOutDir + files = getFiles (scnInDir + "*.max") + start = timeStamp() + for f in files do + ( + format "Exporting file % to %\n" f scnOutFile + loadMAXFile f + exportFile scnOutFile #noPrompt + ) + end = TimeStamp() + format "Exporting took % minutes\n" (( end - start) / 60000.0) +) + + +-- +-- main +-- +utility main_panel "Build Data" +( + button convert_scenes "Plasma Exporter" + on convert_scenes pressed do + ( + ExportScenes () + ) +) \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/Plasma.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/Plasma.py new file mode 100644 index 00000000..1b44a157 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/Plasma.py @@ -0,0 +1,9688 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +def PtAcceptInviteInGame(friendName,inviteKey): + """Sends a VaultTask to the server to perform the invite""" + pass + +def PtAmCCR(): + """Returns true if local player is a CCR""" + pass + +def PtAtTimeCallback(selfkey,time,id): + """This will create a timer callback that will call OnTimer when complete +- 'selfkey' is the ptKey of the PythonFile component +- 'time' is how much time from now (in seconds) to call back +- 'id' is an integer id that will be returned in the OnTimer call""" + pass + +def PtAttachObject(child,parent): + """Attach child to parent based on ptKey or ptSceneobject +- childKey is the ptKey or ptSceneobject of the one being attached +- parentKey is the ptKey or ptSceneobject of the one being attached to +(both arguments must be ptKeys or ptSceneobjects, you cannot mix types)""" + pass + +def PtAvatarEnterAFK(): + """Tells the local avatar to enter AwayFromKeyboard idle loop (netpropagated)""" + pass + +def PtAvatarEnterLookingAtKI(): + """Tells the local avatar to enter looking at KI idle loop (netpropagated)""" + pass + +def PtAvatarEnterUsePersBook(): + """Tells the local avatar to enter using their personal book idle loop (netpropagated)""" + pass + +def PtAvatarExitAFK(): + """Tells the local avatar to exit AwayFromKeyboard idle loop (netpropagated)""" + pass + +def PtAvatarExitLookingAtKI(): + """Tells the local avatar to exit looking at KI idle loop (netpropagated)""" + pass + +def PtAvatarExitUsePersBook(): + """Tells the local avatar to exit using their personal book idle loop (netpropagated)""" + pass + +def PtAvatarSitOnGround(): + """Tells the local avatar to sit on ground and enter sit idle loop (netpropagated)""" + pass + +def PtAvatarSpawnNext(): + """Send the avatar to the next spawn point""" + pass + +def PtCanShadowCast(): + """Can we cast shadows?""" + pass + +def PtChangeAvatar(gender): + """Change the local avatar's gender (or clothing type)""" + pass + +def PtChangePassword(password): + """Changes the current account's password""" + pass + +def PtChangePlayerName(name): + """Change the local avatar's name""" + pass + +def PtCheckVisLOS(startPoint,endPoint): + """Does LOS check from start to end""" + pass + +def PtCheckVisLOSFromCursor(): + """Does LOS check from where the mouse cursor is, into the screen""" + pass + +def PtClearCameraStack(): + """clears all cameras""" + pass + +def PtClearOfferBookMode(): + """Cancel the offer book interface""" + pass + +def PtClearPrivateChatList(memberKey): + """Remove the local avatar from private vox messaging, and / or clear members from his chat list""" + pass + +def PtClearTimerCallbacks(key): + """This will remove timer callbacks to the specified key""" + pass + +def PtConsole(command): + """This will execute 'command' as if it were typed into the Plasma console.""" + pass + +def PtConsoleNet(command,netForce): + """This will execute 'command' on the console, over the network, on all clients. +If 'netForce' is true then force command to be sent over the network.""" + pass + +def PtCreateDir(directory): + """Creates the directory and all parent folders. Returns false on failure""" + pass + +def PtCreatePlayer(playerName, avatarShape, invitation): + """Creates a new player""" + pass + +def PtCreatePlayerW(playerName, avatarShape, invitation): + """Unicode version of PtCreatePlayer""" + pass + +def PtCreatePublicAge(ageInfo, cbObject=None): + """Create a public instance of the given age. +cbObject, if supplied should have a member called publicAgeCreated(self,ageInfo)""" + pass + +def PtDebugAssert(cond, msg): + """Debug only: Assert if condition is false.""" + pass + +def PtDeletePlayer(playerInt): + """Deletes a player associated with the current account""" + pass + +def PtDetachObject(child,parent): + """Detach child from parent based on ptKey or ptSceneobject +- child is the ptKey or ptSceneobject of the one being detached +- parent is the ptKey or ptSceneobject of the one being detached from +(both arguments must be ptKeys or ptSceneobjects, you cannot mix types)""" + pass + +def PtDirtySynchClients(selfKey,SDLStateName,flags): + """DO NOT USE - handled by ptSDL""" + pass + +def PtDirtySynchState(selfKey,SDLStateName,flags): + """DO NOT USE - handled by ptSDL""" + pass + +def PtDisableAvatarCursorFade(): + """Disable the avatar cursor fade""" + pass + +def PtDisableAvatarJump(): + """Disable the ability of the avatar to jump""" + pass + +def PtDisableControlKeyEvents(selfKey): + """Disable the control key events from calling OnControlKeyEvent""" + pass + +def PtDisableForwardMovement(): + """Disable the ability of the avatar to move forward""" + pass + +def PtDisableMouseMovement(): + """Disable avatar mouse movement input""" + pass + +def PtDisableMovementKeys(): + """Disable avatar movement input""" + pass + +def PtDisableRenderScene(): + """UNKNOWN""" + pass + +def PtDisableShadows(): + """Turns shadows off""" + pass + +def PtDumpLogs(folder): + """Dumps all current log files to the specified folder (a sub-folder to the log folder)""" + pass + +def PtEmoteAvatar(emote): + """Play an emote on the local avatar (netpropagated)""" + pass + +def PtEnableAvatarCursorFade(): + """Enable the avatar cursor fade""" + pass + +def PtEnableAvatarJump(): + """Enable the ability of the avatar to jump""" + pass + +def PtEnableControlKeyEvents(selfKey): + """Enable control key events to call OnControlKeyEvent(controlKey,activateFlag)""" + pass + +def PtEnableForwardMovement(): + """Enable the ability of the avatar to move forward""" + pass + +def PtEnableMouseMovement(): + """Enable avatar mouse movement input""" + pass + +def PtEnableMovementKeys(): + """Enable avatar movement input""" + pass + +def PtEnablePlanarReflections(on): + """Enables/disables planar reflections""" + pass + +def PtEnableRenderScene(): + """UNKNOWN""" + pass + +def PtEnableShadows(): + """Turns shadows on""" + pass + +def PtExcludeRegionSet(senderKey,regionKey,state): + """This will set the state of an exclude region +- 'senderKey' is a ptKey of the PythonFile component +- 'regionKey' is a ptKey of the exclude region +- 'state' is either kExRegRelease or kExRegClear""" + pass + +def PtExcludeRegionSetNow(senderKey,regionKey,state): + """This will set the state of an exclude region immediately on the server +- 'senderKey' is a ptKey of the PythonFile component +- 'regionKey' is a ptKey of the exclude region +- 'state' is either kExRegRelease or kExRegClear""" + pass + +def PtFadeIn(lenTime, holdFlag, noSound=0): + """Fades screen in for lenTime seconds""" + pass + +def PtFadeLocalAvatar(fade): + """Fade (or unfade) the local avatar""" + pass + +def PtFadeOut(lenTime, holdFlag, noSound=0): + """Fades screen out for lenTime seconds""" + pass + +def PtFakeLinkAvatarToObject(avatar,object): + """Pseudo-links avatar to object within the same age +""" + pass + +def PtFileExists(filename): + """Returns true if the specified file exists""" + pass + +def PtFindSceneobject(name,ageName): + """This will try to find a sceneobject based on its name and what age its in +- it will return a ptSceneObject if found- if not found then a NameError exception will happen""" + pass + +def PtFirstPerson(): + """is the local avatar in first person mode""" + pass + +def PtFogSetDefColor(color): + """Sets default fog color""" + pass + +def PtFogSetDefExp(end,density): + """Set exp fog values""" + pass + +def PtFogSetDefExp2(end,density): + """Set exp2 fog values""" + pass + +def PtFogSetDefLinear(start,end,density): + """Set linear fog values""" + pass + +def PtForceCursorHidden(): + """Forces the cursor to hide, overriding everything. +Only call if other methods won't work. The only way to show the cursor after this call is PtForceMouseShown()""" + pass + +def PtForceCursorShown(): + """Forces the cursor to show, overriding everything. +Only call if other methods won't work. This is the only way to show the cursor after a call to PtForceMouseHidden()""" + pass + +def PtGMTtoDniTime(gtime): + """Converts GMT time (passed in) to D'Ni time""" + pass + +def PtGUICursorDimmed(): + """Dimms the GUI cursor""" + pass + +def PtGUICursorOff(): + """Turns the GUI cursor off""" + pass + +def PtGUICursorOn(): + """Turns the GUI cursor on""" + pass + +def PtGetAccountName(): + """Returns the account name for the current account""" + pass + +def PtGetAccountPlayerList(): + """Returns list of players associated with the current account""" + pass + +def PtGetAgeInfo(): + """Returns ptAgeInfoStruct of the current Age""" + pass + +def PtGetAgeName(): + """DEPRECIATED - use ptDniInfoSource instead""" + pass + +def PtGetAgeSDL(): + """Returns the global ptSDL for the current Age""" + pass + +def PtGetAgeTime(): + """DEPRECIATED - use ptDniInfoSource instead""" + pass + +def PtGetAgeTimeOfDayPercent(): + """Returns the current age time of day as a percent (0 to 1)""" + pass + +def PtGetAvatarKeyFromClientID(clientID): + """From an integer that is the clientID, find the avatar and return its ptKey""" + pass + +def PtGetCameraNumber(x): + """Returns camera x's name from stack""" + pass + +def PtGetClientIDFromAvatarKey(avatarKey): + """From a ptKey that points at an avatar, return the players clientID (integer)""" + pass + +def PtGetClientName(avatarKey=None): + """This will return the name of the client that is owned by the avatar +- avatarKey is the ptKey of the avatar to get the client name of. +If avatarKey is omitted then the local avatar is used""" + pass + +def PtGetControlEvents(on, key): + """Registers or unregisters for control event messages""" + pass + +def PtGetDefaultDisplayParams(): + """Returns the default resolution and display settings""" + pass + +def PtGetDefaultSpawnPoint(): + """Returns the default spawnpoint definition (as a ptSpawnPointInfo)""" + pass + +def PtGetDesktopColorDepth(): + """Returns desktop ColorDepth""" + pass + +def PtGetDesktopHeight(): + """Returns desktop height""" + pass + +def PtGetDesktopWidth(): + """Returns desktop width""" + pass + +def PtGetDialogFromString(dialogName): + """Get a ptGUIDialog from its name""" + pass + +def PtGetDialogFromTagID(tagID): + """Returns the dialog associated with the tagID""" + pass + +def PtGetDniTime(): + """Returns current D'Ni time""" + pass + +def PtGetFrameDeltaTime(): + """Returns the amount of time that has elapsed since last frame.""" + pass + +def PtGetGameTime(): + """Returns the system game time (frame based) in seconds.""" + pass + +def PtGetInitPath(): + """Returns the unicode path to the client's init directory. Do NOT convert to a standard string.""" + pass + +def PtGetLanguage(): + """Returns the current language as a PtLanguage enum""" + pass + +def PtGetLocalAvatar(): + """This will return a ptSceneobject of the local avatar +- if there is no local avatar a NameError exception will happen.""" + pass + +def PtGetLocalClientID(): + """Returns our local client ID number""" + pass + +def PtGetLocalKILevel(): + """returns local player's ki level""" + pass + +def PtGetLocalPlayer(): + """Returns a ptPlayer object of the local player""" + pass + +def PtGetLocalizedString(name, arguments=None): + """Returns the localized string specified by name (format is Age.Set.Name) and substitutes the arguments in the list of strings passed in as arguments.""" + pass + +def PtGetMouseTurnSensitivity(): + """Returns the sensitivity""" + pass + +def PtGetNumCameras(): + """returns camera stack size""" + pass + +def PtGetNumParticles(key): + """Key is the key of scene object host to particle system""" + pass + +def PtGetNumRemotePlayers(): + """Returns the number of remote players in this Age with you.""" + pass + +def PtGetPlayerList(): + """Returns a list of ptPlayer objects of all the remote players""" + pass + +def PtGetPlayerListDistanceSorted(): + """Returns a list of ptPlayers, sorted by distance""" + pass + +def PtGetPrevAgeInfo(): + """Returns ptAgeInfoStruct of previous age visited""" + pass + +def PtGetPrevAgeName(): + """Returns filename of previous age visited""" + pass + +def PtGetPublicAgeList(ageName, cbObject=None): + """Get list of public ages for the given age name. +cbObject, if supplied should have a method called gotPublicAgeList(self,ageList). ageList is a list of tuple(ptAgeInfoStruct,nPlayersInAge)""" + pass + +def PtGetPythonLoggingLevel(): + """Returns the current level of python logging""" + pass + +def PtGetServerTime(): + """Returns the current time on the server (which is GMT)""" + pass + +def PtGetShadowVisDistance(): + """Returns the maximum shadow visibility distance""" + pass + +def PtGetSupportedDisplayModes(): + """Returns a list of supported resolutions""" + pass + +def PtGetTime(): + """Returns the number of seconds since the game was started.""" + pass + +def PtGetUserPath(): + """Returns the unicode path to the client's root user directory. Do NOT convert to a standard string.""" + pass + +def PtHideDialog(dialogName): + """Hide a GUI dialog by name (does not unload dialog)""" + pass + +def PtIsActivePlayerSet(): + """Returns whether or not an active player is set""" + pass + +def PtIsCCRAway(): + """Returns current status of CCR dept""" + pass + +def PtIsClickToTurn(): + """Is click-to-turn on?""" + pass + +def PtIsCurrentBrainHuman(): + """Returns whether the local avatar current brain is the human brain""" + pass + +def PtIsDemoMode(): + """Returns whether the game is in Demo mode or not""" + pass + +def PtIsDialogLoaded(dialogName): + """Test to see if a GUI dialog is loaded, by name""" + pass + +def PtIsEnterChatModeKeyBound(): + """Returns whether the EnterChatMode is bound to a key""" + pass + +def PtIsGUIModal(): + """Returns true if the GUI is displaying a modal dialog and blocking input""" + pass + +def PtIsInternalRelease(): + """Returns whether the client is an internal build or not""" + pass + +def PtIsMouseInverted(): + """Is the mouse currently inverted?""" + pass + +def PtIsShadowsEnabled(): + """Returns whether shadows are currently turned on""" + pass + +def PtIsSinglePlayerMode(): + """Returns whether the game is in single player mode or not""" + pass + +def PtIsSubscriptionActive(): + """Returns true if the current player is a paying subscriber""" + pass + +def PtKillParticles(timeRemaining,pctToKill,particleSystem): + """Tells particleSystem to kill pctToKill percent of its particles""" + pass + +def PtLimitAvatarLOD(LODlimit): + """Sets avatar's LOD limit""" + pass + +def PtLoadAvatarModel(modelName, spawnPoint, userStr = ""): + """Loads an avatar model at the given spawn point. Assigns the user specified string to it.""" + pass + +def PtLoadBookGUI(guiName): + """Loads the gui specified, a gui must be loaded before it can be used. If the gui is already loaded, doesn't do anything""" + pass + +def PtLoadDialog(dialogName,selfKey=None,ageName=""): + """Loads a GUI dialog by name and optionally set the Notify proc key +If the dialog is already loaded then it won't load it again""" + pass + +def PtLoadJPEGFromDisk(filename,width,height): + """The image will be resized to fit the width and height arguments. Set to 0 if resizing is not desired. +Returns a pyImage of the specified file.""" + pass + +def PtLocalAvatarIsMoving(): + """Returns true if the local avatar is moving (a movement key is held down)""" + pass + +def PtLocalAvatarRunKeyDown(): + """Returns true if the run key is being held down for the local avatar""" + pass + +def PtMaxListenDistSq(): + """Returns the maximum distance (squared) of the listen range""" + pass + +def PtMaxListenListSize(): + """Returns the maximum listen number of players""" + pass + +def PtNotifyOffererLinkAccepted(offerer): + """Tell the offerer that we accepted the link offer""" + pass + +def PtNotifyOffererLinkCompleted(offerer): + """Tell the offerer that we completed the link""" + pass + +def PtNotifyOffererLinkRejected(offerer): + """Tell the offerer that we rejected the link offer""" + pass + +def PtPageInNode(nodeName, ageName=""): + """Pages in node, or a list of nodes""" + pass + +def PtPageOutNode(nodeName): + """Pages out a node""" + pass + +def PtPrintToScreen(message): + """Prints 'message' to the status log, for debug only.""" + pass + +def PtRateIt(chronicleName,dialogPrompt,onceFlag): + """Shows a dialog with dialogPrompt and stores user input rating into chronicleName""" + pass + +def PtRebuildCameraStack(name,ageName): + """Push camera with this name on the stack""" + pass + +def PtRecenterCamera(): + """re-centers the camera""" + pass + +def PtRemovePublicAge(ageInstanceGuid, cbObject=None): + """Remove a public instance of the given age. +cbObject, if supplied should have a member called publicAgeRemoved(self,ageInstanceGuid)""" + pass + +def PtRequestLOSScreen(selfKey,ID,xPos,yPos,distance,what,reportType): + """Request a LOS check from a point on the screen""" + pass + +def PtSaveScreenShot(fileName,width=640,height=480,quality=75): + """Takes a screenshot with the specified filename, size, and quality""" + pass + +def PtSendChatToCCR(message,CCRPlayerID): + """Sends a chat message to a CCR that has contacted this player""" + pass + +def PtSendKIGZMarkerMsg(markerNumber,sender): + """Same as PtSendKIMessageInt except 'sender' could get a notify message back +""" + pass + +def PtSendKIMessage(command,value): + """Sends a command message to the KI frontend. +See PlasmaKITypes.py for list of commands""" + pass + +def PtSendKIMessageInt(command,value): + """Same as PtSendKIMessage except the value is guaranteed to be a UInt32 +(for things like player IDs)""" + pass + +def PtSendPetitionToCCR(message,reason=0,title=""): + """Sends a petition with a message to the CCR group""" + pass + +def PtSendPrivateChatList(chatList): + """Lock the local avatar into private vox messaging, and / or add new members to his chat list""" + pass + +def PtSendRTChat(fromPlayer,toPlayerList,message,flags): + """Sends a realtime chat message to the list of ptPlayers +If toPlayerList is an empty list, it is a broadcast message""" + pass + +def PtSetActivePlayer(playerInt): + """Sets the active player associated with the current account""" + pass + +def PtSetAlarm(secs, cbObject, cbContext): + """secs is the amount of time before your alarm goes off. +cbObject is a python object with the method onAlarm(int context) +cbContext is an integer.""" + pass + +def PtSetBehaviorLoopCount(behaviorKey,stage,loopCount,netForce): + """This will set the loop count for a particular stage in a multistage behavior""" + pass + +def PtSetBehaviorNetFlags(behKey, netForce, netProp): + """Sets net flags on the associated behavior""" + pass + +def PtSetClearColor(red,green,blue): + """Set the clear color""" + pass + +def PtSetClickToTurn(state): + """Turns on click-to-turn""" + pass + +def PtSetGamma2(gamma): + """Set the gamma with gamma2 rules""" + pass + +def PtSetGlobalClickability(enable): + """Enable or disable all clickables on the local client""" + pass + +def PtSetGraphicsOptions(width, height, colordepth, windowed, numAAsamples, numAnisoSamples, VSync): + """Set the graphics options""" + pass + +def PtSetLightAnimStart(key,name,start): + """ Key is the key of scene object host to light, start is a bool. Name is the name of the light to manipulate""" + pass + +def PtSetLightValue(key,name,r,g,b,a): + """ Key is the key of scene object host to light. Name is the name of the light to manipulate""" + pass + +def PtSetMouseInverted(): + """Inverts the mouse""" + pass + +def PtSetMouseTurnSensitivity(sensitivity): + """Set the mouse sensitivity""" + pass + +def PtSetMouseUninverted(): + """Uninverts the mouse""" + pass + +def PtSetOfferBookMode(selfkey,ageFilename,ageInstanceName): + """Put us into the offer book interface""" + pass + +def PtSetParticleDissentPoint(x, y, z, particlesys): + """Sets the dissent point of the particlesys to x,y,z""" + pass + +def PtSetParticleOffset(x,y,z,particlesys): + """Sets the particlesys particle system's offset""" + pass + +def PtSetPythonLoggingLevel(level): + """Sets the current level of python logging""" + pass + +def PtSetShadowVisDistance(distance): + """Set the maximum shadow visibility distance""" + pass + +def PtSetShareSpawnPoint(spawnPoint): + """This sets the desired spawn point for the receiver to link to""" + pass + +def PtShootBulletFromObject(selfkey, gunObj, radius, range): + """Shoots a bullet from an object""" + pass + +def PtShootBulletFromScreen(selfkey, xPos, yPos, radius, range): + """Shoots a bullet from a position on the screen""" + pass + +def PtShowDialog(dialogName): + """Show a GUI dialog by name (does not load dialog)""" + pass + +def PtStartScreenCapture(selfKey,width=800,height=600): + """Starts a capture of the screen""" + pass + +def PtToggleAvatarClickability(on): + """Turns on and off our avatar's clickability""" + pass + +def PtTransferParticlesToObject(objFrom, objTo, num): + """Transfers num particles from objFrom to objTo""" + pass + +def PtUnLoadAvatarModel(avatarKey): + """Unloads the specified avatar model""" + pass + +def PtUnloadAllBookGUIs(): + """Unloads all loaded guis except for the default one""" + pass + +def PtUnloadBookGUI(guiName): + """Unloads the gui specified. If the gui isn't loaded, doesn't do anything""" + pass + +def PtUnloadDialog(dialogName): + """This will unload the GUI dialog by name. If not loaded then nothing will happen""" + pass + +def PtUpgradeVisitorToExplorer(playerInt): + """Upgrades the player to explorer status""" + pass + +def PtUsingUnicode(): + """Returns true if the current language is a unicode language (like Japanese)""" + pass + +def PtValidateKey(key): + """Returns true(1) if 'key' is valid and loaded, +otherwise returns false(0)""" + pass + +def PtWasLocallyNotified(selfKey): + """Returns 1 if the last notify was local or 0 if the notify originated on the network""" + pass + +def PtWearDefaultClothing(key): + """Forces the avatar to wear the default clothing set""" + pass + +def PtWearDefaultClothingType(key,type): + """Forces the avatar to wear the default clothing of the specified type""" + pass + +def PtWearMaintainerSuit(key,wearOrNot): + """Wears or removes the maintainer suit of clothes""" + pass + +def PtWhatGUIControlType(guiKey): + """Returns the control type of the key passed in""" + pass + +def PtYesNoDialog(selfkey,dialogMessage): + """This will display a Yes/No dialog to the user with the text dialogMessage +This dialog _has_ to be answered by the user. +And their answer will be returned in a Notify message.""" + pass + +class ptAgeInfoStruct: + """Class to hold AgeInfo struct data""" + def __init__(self): + """None""" + pass + + def copyFrom(self,other): + """Copies data from one ptAgeInfoStruct or ptAgeInfoStructRef to this one""" + pass + + def getAgeFilename(self): + """Gets the Age's filename""" + pass + + def getAgeInstanceGuid(self): + """Get the Age's instance GUID""" + pass + + def getAgeInstanceName(self): + """Get the instance name of the Age""" + pass + + def getAgeLanguage(self): + """Gets the age's language (integer)""" + pass + + def getAgeSequenceNumber(self): + """Gets the unique sequence number""" + pass + + def getAgeUserDefinedName(self): + """Gets the user defined part of the Age name""" + pass + + def getDisplayName(self): + """Returns a string that is the displayable name of the age instance""" + pass + + def setAgeFilename(self,filename): + """Sets the filename of the Age""" + pass + + def setAgeInstanceGuid(self,guid): + """Sets the Age instance's GUID""" + pass + + def setAgeInstanceName(self,instanceName): + """Sets the instance name of the Age""" + pass + + def setAgeLanguage(self,lang): + """Sets the age's language (integer)""" + pass + + def setAgeSequenceNumber(self,seqNumber): + """Sets the unique sequence number""" + pass + + def setAgeUserDefinedName(self,udName): + """Sets the user defined part of the Age""" + pass + +class ptAgeInfoStructRef: + """Class to hold AgeInfo struct data""" + def __init__(self): + """None""" + pass + + def copyFrom(self,other): + """Copies data from one ptAgeInfoStruct or ptAgeInfoStructRef to this one""" + pass + + def getAgeFilename(self): + """Gets the Age's filename""" + pass + + def getAgeInstanceGuid(self): + """Get the Age's instance GUID""" + pass + + def getAgeInstanceName(self): + """Get the instance name of the Age""" + pass + + def getAgeSequenceNumber(self): + """Gets the unique sequence number""" + pass + + def getAgeUserDefinedName(self): + """Gets the user defined part of the Age name""" + pass + + def getDisplayName(self): + """Returns a string that is the displayable name of the age instance""" + pass + + def setAgeFilename(self,filename): + """Sets the filename of the Age""" + pass + + def setAgeInstanceGuid(self,guid): + """Sets the Age instance's GUID""" + pass + + def setAgeInstanceName(self,instanceName): + """Sets the instance name of the Age""" + pass + + def setAgeSequenceNumber(self,seqNumber): + """Sets the unique sequence number""" + pass + + def setAgeUserDefinedName(self,udName): + """Sets the user defined part of the Age""" + pass + +class ptAgeLinkStruct: + """Class to hold the data of the AgeLink structure""" + def __init__(self): + """None""" + pass + + def copyFrom(self,other): + """Copies data from one ptAgeLinkStruct or ptAgeLinkStructRef to this one""" + pass + + def getAgeInfo(self): + """Returns a ptAgeInfoStructRef of the AgeInfo for this link""" + pass + + def getLinkingRules(self): + """Returns the linking rules of this link""" + pass + + def getParentAgeFilename(self): + """Returns a string of the parent age filename""" + pass + + def getSpawnPoint(self): + """Gets the spawn point ptSpawnPointInfoRef of this link""" + pass + + def setAgeInfo(self,ageInfo): + """Sets the AgeInfoStruct from the data in ageInfo (a ptAgeInfoStruct)""" + pass + + def setLinkingRules(self,rule): + """Sets the linking rules for this link""" + pass + + def setParentAgeFilename(self,filename): + """Sets the parent age filename for child age links""" + pass + + def setSpawnPoint(self,spawnPtInfo): + """Sets the spawn point of this link (a ptSpawnPointInfo or ptSpawnPointInfoRef)""" + pass + +class ptAgeLinkStructRef: + """Class to hold the data of the AgeLink structure""" + def __init__(self): + """None""" + pass + + def copyFrom(self,other): + """Copies data from one ptAgeLinkStruct or ptAgeLinkStructRef to this one""" + pass + + def getAgeInfo(self): + """Returns a ptAgeInfoStructRef of the AgeInfo for this link""" + pass + + def getLinkingRules(self): + """Returns the linking rules of this link""" + pass + + def getSpawnPoint(self): + """Gets the spawn point ptSpawnPointInfoRef of this link""" + pass + + def setAgeInfo(self,ageInfo): + """Sets the AgeInfoStruct from the data in ageInfo (a ptAgeInfoStruct)""" + pass + + def setLinkingRules(self,rule): + """Sets the linking rules for this link""" + pass + + def setSpawnPoint(self,spawnPtInfo): + """Sets the spawn point of this link (a ptSpawnPointInfo or ptSpawnPointInfoRef)""" + pass + +class ptAgeVault: + """Accessor class to the Age's vault""" + def __init__(self): + """None""" + pass + + def addChronicleEntry(self,name,type,value): + """Adds a chronicle entry with the specified type and value""" + pass + + def addDevice(self,deviceName,cb=None,cbContext=0): + """Adds a device to the age""" + pass + + def findChronicleEntry(self,entryName): + """Returns the named ptVaultChronicleNode""" + pass + + def getAgeDevicesFolder(self): + """Returns a ptVaultFolderNode of the inboxes for the devices in this Age.""" + pass + + def getAgeGuid(self): + """Returns the current Age's guid as a string.""" + pass + + def getAgeInfo(self): + """Returns a ptVaultAgeInfoNode of the this Age""" + pass + + def getAgeSDL(self): + """Returns the age's SDL (ptSDLStateDataRecord)""" + pass + + def getAgesIOwnFolder(self): + """(depreciated, use getBookshelfFolder) Returns a ptVaultFolderNode that contain the Ages I own""" + pass + + def getBookshelfFolder(self): + """Personal age only: Returns a ptVaultFolderNode that contains the owning player's AgesIOwn age list""" + pass + + def getChronicleFolder(self): + """Returns a ptVaultFolderNode""" + pass + + def getDevice(self,deviceName): + """Returns the specified device (ptVaultTextNoteNode)""" + pass + + def getDeviceInbox(self,deviceName): + """Returns a ptVaultFolderNode of the inbox for the named device in this age.""" + pass + + def getPeopleIKnowAboutFolder(self): + """Returns a ptVaultPlayerInfoListNode of the players the Age knows about(?).""" + pass + + def getPublicAgesFolder(self): + """Returns a ptVaultFolderNode that contains all the public Ages""" + pass + + def getSubAgeLink(self,ageInfo): + """Returns a ptVaultAgeLinkNode to 'ageInfo' (a ptAgeInfoStruct) for this Age.""" + pass + + def getSubAgesFolder(self): + """Returns a ptVaultFolderNode of sub Age's folder.""" + pass + + def hasDevice(self,deviceName): + """Does a device with this name exist?""" + pass + + def removeDevice(self,deviceName): + """Removes a device from the age""" + pass + + def setDeviceInbox(self,deviceName,inboxName,cb=None,cbContext=0): + """Set's the device's inbox""" + pass + + def updateAgeSDL(self,pyrec): + """Updates the age's SDL""" + pass + +class ptAnimation: + """Plasma animation class""" + def __init__(self,key=None): + """None""" + pass + + def addKey(self,key): + """Adds an animation modifier to the list of receiver keys""" + pass + + def backwards(self,backwardsFlag): + """Turn on and off playing the animation backwards""" + pass + + def getFirstKey(self): + """This will return a ptKey object that is the first receiver (target) +However, if the parent is not a modifier or not loaded, then None is returned.""" + pass + + def incrementBackward(self): + """Step the animation backward a frame""" + pass + + def incrementForward(self): + """Step the animation forward a frame""" + pass + + def looped(self,loopedFlag): + """Turn on and off looping of the animation""" + pass + + def netForce(self,forceFlag): + """Specify whether this object needs to use messages that are forced to the network +- This is to be used if your Python program is running on only one client +Such as a game master, only running on the client that owns a particular object""" + pass + + def play(self): + """Plays the animation""" + pass + + def playRange(self,start,end): + """Play the animation from start to end""" + pass + + def playToPercentage(self,zeroToOne): + """Play the animation to the specified percentage (0 to 1)""" + pass + + def playToTime(self,time): + """Play the animation to the specified time""" + pass + + def resume(self): + """Resumes the animation from where it was stopped last""" + pass + + def sender(self,selfKey): + """Sets the sender of the messages being sent to the animation modifier""" + pass + + def setAnimName(self,name): + """Sets the animation notetrack name (or (Entire Animation))""" + pass + + def setLoopEnd(self,loopEnd): + """Sets the loop ending position +- 'loopEnd' is the number of seconds from the absolute beginning of the animation""" + pass + + def setLoopStart(self,loopStart): + """Sets the loop starting position +- 'loopStart' is the number of seconds from the absolute beginning of the animation""" + pass + + def skipToBegin(self): + """Skip to the beginning of the animation (don't play)""" + pass + + def skipToEnd(self): + """Skip to the end of the animation (don't play)""" + pass + + def skipToLoopBegin(self): + """Skip to the beginning of the animation loop (don't play)""" + pass + + def skipToLoopEnd(self): + """Skip to the end of the animation loop (don't play)""" + pass + + def skipToTime(self,time): + """Skip the animation to time (don't play)""" + pass + + def speed(self,speed): + """Sets the animation playback speed""" + pass + + def stop(self): + """Stops the animation""" + pass + +class ptAudioControl: + """Accessor class to the Audio controls""" + def __init__(self): + """None""" + pass + + def canSetMicLevel(self): + """Can the microphone level be set? Returns 1 if true otherwise returns 0.""" + pass + + def disable(self): + """Disabled audio""" + pass + + def enable(self): + """Enables audio""" + pass + + def enableVoiceChat(self,state): + """Enables or disables voice chat.""" + pass + + def enableVoiceCompression(self,state): + """Enables or disables voice compression.""" + pass + + def enableVoiceNetBroadcast(self,state): + """Enables or disables voice over network broadcast.""" + pass + + def enableVoiceRecording(self,state): + """Enables or disables voice recording.""" + pass + + def getAmbienceVolume(self): + """Returns the volume (0.0 to 1.0) for the Ambiance.""" + pass + + def getAudioDeviceName(self,index): + """Gets the name of audio device for the given index""" + pass + + def getDeviceName(self): + """Gets the name for the device being used by the audio system""" + pass + + def getGUIVolume(self): + """Returns the volume (0.0 to 1.0) for the GUI dialogs.""" + pass + + def getHighestMode(self): + """Gets the highest possible audio system mode""" + pass + + def getMicLevel(self): + """Returns the microphone recording level (0.0 to 1.0).""" + pass + + def getMode(self): + """Gets the audio system mode""" + pass + + def getMusicVolume(self): + """Returns the volume (0.0 to 1.0) for the Music.""" + pass + + def getNPCVoiceVolume(self): + """Returns the volume (0.0 to 1.0) for the NPC's voice.""" + pass + + def getNumAudioDevices(self): + """Returns the number of available audio devices.""" + pass + + def getPriorityCutoff(self): + """Returns current sound priority""" + pass + + def getSoundFXVolume(self): + """Returns the volume (0.0 to 1.0) for the Sound FX.""" + pass + + def getVoiceVolume(self): + """Returns the volume (0.0 to 1.0) for the Voices.""" + pass + + def hideIcons(self): + """Hides (disables) the voice recording icons.""" + pass + + def isEnabled(self): + """Is the audio enabled? Returns 1 if true otherwise returns 0.""" + pass + + def isHardwareAccelerated(self): + """Is audio hardware acceleration enabled? Returns 1 if true otherwise returns 0.""" + pass + + def isMuted(self): + """Are all sounds muted? Returns 1 if true otherwise returns 0.""" + pass + + def isUsingEAXAcceleration(self): + """Is EAX sound acceleration enabled? Returns 1 if true otherwise returns 0.""" + pass + + def isVoiceCompressionEnabled(self): + """Is voice compression enabled? Returns 1 if true otherwise returns 0.""" + pass + + def isVoiceNetBroadcastEnabled(self): + """Is voice over net enabled? Returns 1 if true otherwise returns 0.""" + pass + + def isVoiceRecordingEnabled(self): + """Is voice recording enabled? Returns 1 if true otherwise returns 0.""" + pass + + def muteAll(self): + """Mutes all sounds.""" + pass + + def pushToTalk(self,state): + """Enables or disables 'push-to-talk'.""" + pass + + def recordFrame(self,size): + """Sets the voice packet frame size.""" + pass + + def recordSampleRate(self,sampleRate): + """Sets the recording sample rate.""" + pass + + def setAmbienceVolume(self,volume): + """Sets the Ambience volume (0.0 to 1.0) for the game. +This only sets the volume for this game session.""" + pass + + def setDeviceName(self,devicename,restart): + """Sets the device name for the audio system, and optionally restarts it""" + pass + + def setGUIVolume(self,volume): + """Sets the GUI dialog volume (0.0 to 1.0) for the game. +This only sets the volume for this game session.""" + pass + + def setLoadOnDemand(self,state): + """Enables or disables the load on demand for sounds.""" + pass + + def setMicLevel(self,level): + """Sets the microphone recording level (0.0 to 1.0).""" + pass + + def setMode(self,mode): + """Sets the audio system mode""" + pass + + def setMusicVolume(self,volume): + """Sets the Music volume (0.0 to 1.0) for the game. +This only sets the volume for this game session.""" + pass + + def setNPCVoiceVolume(self,volume): + """Sets the NPC's voice volume (0.0 to 1.0) for the game. +This only sets the volume for this game session.""" + pass + + def setPriorityCutoff(self,priority): + """Sets the sound priority""" + pass + + def setSoundFXVolume(self,volume): + """Sets the SoundFX volume (0.0 to 1.0) for the game. +This only sets the volume for this game session.""" + pass + + def setTwoStageLOD(self,state): + """Enables or disables two-stage LOD, where sounds can be loaded into RAM but not into sound buffers. +...Less of a performance hit, harder on memory.""" + pass + + def setVoiceVolume(self,volume): + """Sets the Voice volume (0.0 to 1.0) for the game. +This only sets the volume for this game session.""" + pass + + def showIcons(self): + """Shows (enables) the voice recording icons.""" + pass + + def squelchLevel(self,level): + """Sets the squelch level.""" + pass + + def supportsEAX(self): + """Returns true or false based on whether or not a the device specified supports EAX""" + pass + + def unmuteAll(self): + """Unmutes all sounds.""" + pass + + def useEAXAcceleration(self,state): + """Enables or disables EAX sound acceleration (requires hardware acceleration).""" + pass + + def useHardwareAcceleration(self,state): + """Enables or disables audio hardware acceleration.""" + pass + +class ptAvatar: + """Plasma avatar class""" + def __init__(self): + """None""" + pass + + def addWardrobeClothingItem(self,clothing_name,tint1,tint2): + """To add a clothing item to the avatar's wardrobe (closet)""" + pass + + def enterSubWorld(self,sceneobject): + """Places the avatar into the subworld of the ptSceneObject specified""" + pass + + def exitSubWorld(self): + """Exits the avatar from the subWorld where it was""" + pass + + def getAllWithSameMesh(self,clothing_name): + """Returns a lilst of all clothing items that use the same mesh as the specified one""" + pass + + def getAvatarClothingGroup(self): + """Returns what clothing group the avatar belongs to. +It is also a means to determine if avatar is male or female""" + pass + + def getAvatarClothingList(self): + """Returns a list of clothes that the avatar is currently wearing.""" + pass + + def getClosetClothingList(self,clothing_type): + """Returns a list of clothes for the avatar that are in specified clothing group.""" + pass + + def getCurrentMode(self): + """Returns current brain mode for avatar""" + pass + + def getEntireClothingList(self,clothing_type): + """Gets the entire list of clothing available. 'clothing_type' not used +NOTE: should use getClosetClothingList""" + pass + + def getMatchingClothingItem(self,clothingName): + """Finds the matching clothing item that goes with 'clothingName' +Used to find matching left and right gloves and shoes.""" + pass + + def getMorph(self,clothing_name,layer): + """Get the current morph value""" + pass + + def getSkinBlend(self,layer): + """Get the current skin blend value""" + pass + + def getTintClothingItem(self,clothing_name,layer=1): + """Returns a ptColor of a particular item of clothing that the avatar is wearing. +The color will be a ptColor object.""" + pass + + def getTintSkin(self): + """Returns a ptColor of the current skin tint for the avatar""" + pass + + def getUniqueMeshList(self,clothing_type): + """Returns a list of unique clothing items of the desired type (different meshes)""" + pass + + def getWardrobeClothingList(self): + """Return a list of items that are in the avatars closet""" + pass + + def gotoStage(self,behaviorKey,stage,transitionTime,setTimeFlag,newTime,SetDirectionFlag,isForward,netForce): + """Tells a multistage behavior to go to a particular stage""" + pass + + def netForce(self,forceFlag): + """Specify whether this object needs to use messages that are forced to the network +- This is to be used if your Python program is running on only one client +Such as a game master, only running on the client that owns a particular object""" + pass + + def nextStage(self,behaviorKey,transitionTime,setTimeFlag,newTime,SetDirectionFlag,isForward,netForce): + """Tells a multistage behavior to go to the next stage (Why does Matt like so many parameters?)""" + pass + + def oneShot(self,seekKey,duration,usePhysicsFlag,animationName,drivableFlag,reversibleFlag): + """Plays a one-shot animation on the avatar""" + pass + + def playSimpleAnimation(self,animName): + """Play simple animation on avatar""" + pass + + def previousStage(self,behaviorKey,transitionTime,setTimeFlag,newTime,SetDirectionFlag,isForward,netForce): + """Tells a multistage behavior to go to the previous stage""" + pass + + def registerForBehaviorNotify(self,selfKey): + """This will register for behavior notifies from the avatar""" + pass + + def removeClothingItem(self,clothing_name,update=1): + """Tells the avatar to remove a particular item of clothing.""" + pass + + def runBehavior(self,behaviorKey,netForceFlag): + """Runs a behavior on the avatar. Can be a single or multi-stage behavior.""" + pass + + def runBehaviorSetNotify(self,behaviorKey,replyKey,netForceFlag): + """Same as runBehavior, except send notifications to specified keyed object""" + pass + + def saveClothing(self): + """Saves the current clothing options (including morphs) to the vault""" + pass + + def setMorph(self,clothing_name,layer,value): + """Set the morph value (clipped between -1 and 1)""" + pass + + def setReplyKey(self,key): + """Sets the sender's key""" + pass + + def setSkinBlend(self,layer,value): + """Set the skin blend (value between 0 and 1)""" + pass + + def tintClothingItem(self,clothing_name,tint,update=1): + """Tells the avatar to tint(color) a particular item of clothing that they are already wearing. +'tint' is a ptColor object""" + pass + + def tintClothingItemLayer(self,clothing_name,tint,layer,update=1): + """Tells the avatar to tint(color) a particular layer of a particular item of clothing.""" + pass + + def tintSkin(self,tint,update=1): + """Tints all of the skin on the avatar, with the ptColor tint""" + pass + + def unRegisterForBehaviorNotify(self,selfKey): + """This will unregister behavior notifications""" + pass + + def wearClothingItem(self,clothing_name,update=1): + """Tells the avatar to wear a particular item of clothing. +And optionally hold update until later (for applying tinting before wearing).""" + pass + +class ptBook: + """Creates a new book""" + def __init__(self,esHTMLSource,coverImage=None,callbackKey=None,guiName=''): + """None""" + pass + + def allowPageTurning(self,allow): + """Turns on and off the ability to flip the pages in a book""" + pass + + def close(self): + """Closes the book""" + pass + + def closeAndHide(self): + """Closes the book and hides it once it finishes animating""" + pass + + def getCurrentPage(self): + """Returns the currently shown page""" + pass + + def getEditableText(self): + """Returns the editable text currently contained in the book.""" + pass + + def getMovie(self,index): + """Grabs a ptAnimation object representing the movie indexed by index. The index is the index of the movie in the source code""" + pass + + def goToPage(self,page): + """Flips the book to the specified page""" + pass + + def hide(self): + """Hides the book""" + pass + + def nextPage(self): + """Flips the book to the next page""" + pass + + def open(self,startingPage): + """Opens the book to the specified page""" + pass + + def previousPage(self): + """Flips the book to the previous page""" + pass + + def setEditable(self,editable): + """Turn book editing on or off. If the book GUI does not support editing, nothing will happen""" + pass + + def setEditableText(self,text): + """Sets the book's editable text.""" + pass + + def setGUI(self,guiName): + """Sets the gui to be used by the book, if the requested gui is not loaded, it will use the default +Do not call while the book is open!""" + pass + + def setPageMargin(self,margin): + """Sets the text margin for the book""" + pass + + def setSize(self,width,height): + """Sets the size of the book (width and height are floats from 0 to 1)""" + pass + + def show(self,startOpened): + """Shows the book closed, or open if the the startOpened flag is true""" + pass + +class ptCamera: + """Plasma camera class""" + def __init__(self): + """None""" + pass + + def controlKey(self,controlKey,activateFlag): + """Send a control key to the camera as if it was hit by the user. +This is for sending things like pan-up, pan-down, zoom-in, etc.""" + pass + + def disableFirstPersonOverride(self): + """Does _not_ allow the user to override the camera to go to first person camera.""" + pass + + def enableFirstPersonOverride(self): + """Allows the user to override the camera and go to a first person camera.""" + pass + + def getFOV(self): + """Returns the current camera's FOV(h)""" + pass + + def isSmootherCam(self): + """Returns true if we are using the faster cams thing""" + pass + + def isStayInFirstPerson(self): + """Are we staying in first person?""" + pass + + def isWalkAndVerticalPan(self): + """Returns true if we are walking and chewing gum""" + pass + + def restore(self,cameraKey): + """Restores camera to saved one""" + pass + + def save(self,cameraKey): + """Saves the current camera and sets the camera to cameraKey""" + pass + + def set(self,cameraKey,time,save): + """DO NOT USE""" + pass + + def setFOV(self,fov, time): + """Sets the current cameras FOV (based on h)""" + pass + + def setSmootherCam(self,state): + """Set the faster cams thing""" + pass + + def setStayInFirstPerson(self,state): + """Set Stay In First Person Always""" + pass + + def setWalkAndVerticalPan(self,state): + """Set Walk and chew gum""" + pass + + def undoFirstPerson(self): + """If the user has overridden the camera to be in first person, this will take them out of first person. +If the user didn't override the camera, then this will do nothing.""" + pass + +class ptCluster: + """Creates a new ptCluster""" + def __init__(self,ey): + """None""" + pass + + def setVisible(self,isible): + """Shows or hides the cluster object""" + pass + +class ptColor: + """Plasma color class""" + def __init__(self,red=0, green=0, blue=0, alpha=0): + """None""" + pass + + def black(self): + """Sets the color to be black +Example: black = ptColor().black()""" + pass + + def blue(self): + """Sets the color to be blue +Example: blue = ptColor().blue()""" + pass + + def brown(self): + """Sets the color to be brown +Example: brown = ptColor().brown()""" + pass + + def cyan(self): + """Sets the color to be cyan +Example: cyan = ptColor.cyan()""" + pass + + def darkbrown(self): + """Sets the color to be darkbrown +Example: darkbrown = ptColor().darkbrown()""" + pass + + def darkgreen(self): + """Sets the color to be darkgreen +Example: darkgreen = ptColor().darkgreen()""" + pass + + def darkpurple(self): + """Sets the color to be darkpurple +Example: darkpurple = ptColor().darkpurple()""" + pass + + def getAlpha(self): + """Get the alpha blend component of the color""" + pass + + def getBlue(self): + """Get the blue component of the color""" + pass + + def getGreen(self): + """Get the green component of the color""" + pass + + def getRed(self): + """Get the red component of the color""" + pass + + def gray(self): + """Sets the color to be gray +Example: gray = ptColor().gray()""" + pass + + def green(self): + """Sets the color to be green +Example: green = ptColor().green()""" + pass + + def magenta(self): + """Sets the color to be magenta +Example: magenta = ptColor().magenta()""" + pass + + def maroon(self): + """Sets the color to be maroon +Example: maroon = ptColor().maroon()""" + pass + + def navyblue(self): + """Sets the color to be navyblue +Example: navyblue = ptColor().navyblue()""" + pass + + def orange(self): + """Sets the color to be orange +Example: orange = ptColor().orange()""" + pass + + def pink(self): + """Sets the color to be pink +Example: pink = ptColor().pink()""" + pass + + def red(self): + """Sets the color to be red +Example: red = ptColor().red()""" + pass + + def setAlpha(self,alpha): + """Set the alpha blend component of the color. 0.0 to 1.0""" + pass + + def setBlue(self,blue): + """Set the blue component of the color. 0.0 to 1.0""" + pass + + def setGreen(self,green): + """Set the green component of the color. 0.0 to 1.0""" + pass + + def setRed(self,red): + """Set the red component of the color. 0.0 to 1.0""" + pass + + def slateblue(self): + """Sets the color to be slateblue +Example: slateblue = ptColor().slateblue()""" + pass + + def steelblue(self): + """Sets the color to be steelblue +Example: steelblue = ptColor().steelblue()""" + pass + + def tan(self): + """Sets the color to be tan +Example: tan = ptColor().tan()""" + pass + + def white(self): + """Sets the color to be white +Example: white = ptColor().white()""" + pass + + def yellow(self): + """Sets the color to be yellow +Example: yellow = ptColor().yellow()""" + pass + +class ptCritterBrain: + """Object to manipulate critter brains""" + def __init__(self): + """None""" + pass + + def addBehavior(self,animName, behaviorName, loop = 1, randomStartPos = 1, fadeInLen = 2.0, fadeOutLen = 2.0): + """Adds a new animation to the brain as a behavior with the specified name and parameters. If multiple animations are assigned to the same behavior, they will be randomly picked from when started.""" + pass + + def addReceiver(self,key): + """Tells the brain that the specified key wants AI messages""" + pass + + def animationName(self,behavior): + """Returns the animation name associated with the specified integral behavior.""" + pass + + def atGoal(self): + """Are we currently are our final destination?""" + pass + + def avoidingAvatars(self): + """Are we currently avoiding avatars while pathfinding?""" + pass + + def behaviorName(self,behavior): + """Returns the behavior name associated with the specified integral behavior.""" + pass + + def canHearAvatar(self,avatarID): + """Returns whether this brain can hear the avatar with the specified id.""" + pass + + def canSeeAvatar(self,avatarID): + """Returns whether this brain can see the avatar with the specified id.""" + pass + + def curBehavior(self): + """Returns the current integral behavior the brain is running.""" + pass + + def currentGoal(self): + """Returns the current ptPoint that the brain is running towards.""" + pass + + def getHearingDistance(self): + """Returns how far away the brain can hear.""" + pass + + def getLocallyControlled(self): + """Are we the one making AI decisions? NOTE: Not set automatically, some python script needs to tell the brain this using setLocallyControlled().""" + pass + + def getSightCone(self): + """Returns the width of the brain's field of view in radians.""" + pass + + def getSightDistance(self): + """Returns how far the brain can see.""" + pass + + def getStopDistance(self): + """Returns how far away from the goal we could be and still be considered there.""" + pass + + def goToGoal(self,newGoal, avoidingAvatars = 0): + """Tells the brain to start running towards the specified location, avoiding avatars it can see or hear if told to.""" + pass + + def idleBehaviorName(self): + """Returns the name of the brain's idle behavior.""" + pass + + def nextBehavior(self): + """Returns the behavior the brain will be switching to next frame. (-1 if no change)""" + pass + + def playersICanHear(self): + """Returns a list of player ids which this brain can hear.""" + pass + + def playersICanSee(self): + """Returns a list of player ids which this brain can see.""" + pass + + def removeReceiver(self,key): + """Tells the brain that the specified key no longer wants AI messages""" + pass + + def runBehaviorName(self): + """Returns the name of the brain's run behavior.""" + pass + + def runningBehavior(self,behaviorName): + """Returns true if the named behavior is running.""" + pass + + def setHearingDistance(self,dist): + """Set how far away the brain can hear (360 degree field of hearing).""" + pass + + def setLocallyControlled(self,local): + """Tells the brain that we are the ones making all the AI decisions, and to prop location and other information to the server.""" + pass + + def setSightCone(self,radians): + """Set how wide the brain's field of view is in radians. Note that it is the total angle of the cone, half on one side of the brain's line of sight, half on the other.""" + pass + + def setSightDistance(self,dist): + """Set how far away the brain can see.""" + pass + + def setStopDistance(self,dist): + """Set how far away from the goal we should be when we are considered there and stop running.""" + pass + + def startBehavior(self,behaviorName, fade = 1): + """Starts playing the named behavior. If fade is true, it will fade out the previous behavior and fade in the new one. If false, they will immediately switch.""" + pass + + def vectorToPlayer(self,avatarID): + """Returns the vector between us and the specified player.""" + pass + +class ptDniCoordinates: + """Constructor for a D'Ni coordinate""" + def __init__(self): + """None""" + pass + + def fromPoint(self,pt): + """Update these coordinates with the specified ptPoint3""" + pass + + def getHSpans(self): + """Returns the HSpans component of the coordinate""" + pass + + def getTorans(self): + """Returns the Torans component of the coordinate""" + pass + + def getVSpans(self): + """Returns the VSpans component of the coordinate""" + pass + + def update(self): + """Update these coordinates with the players current position""" + pass + +class ptDniInfoSource: + """DO NOT USE""" + def __init__(self): + """None""" + pass + + def getAgeCoords(self): + """Current coords of the player in current age as a ptDniCoordinates""" + pass + + def getAgeGuid(self): + """Unique identifier for this age instance""" + pass + + def getAgeName(self): + """Name of current age""" + pass + + def getAgeTime(self): + """Current time in current age (tbd)""" + pass + +class ptDraw: + """Plasma Draw class""" + def __init__(self): + """None""" + pass + + def disable(self): + """Disables the draw on the sceneobject attached +In other words, makes it invisible""" + pass + + def enable(self,state=1): + """Sets the draw enable for the sceneobject attached""" + pass + + def netForce(self,forceFlag): + """Specify whether this object needs to use messages that are forced to the network +- This is to be used if your Python program is running on only one client +Such as a game master, only running on the client that owns a particular object""" + pass + +class ptDynamicMap: + """Creates a ptDynamicMap object""" + def __init__(self,key=None): + """None""" + pass + + def addKey(self,key): + """Add a receiver... in other words a DynamicMap""" + pass + + def calcTextExtents(self,text): + """Calculates the extent of the specified text, returns it as a (width, height) tuple""" + pass + + def clearKeys(self): + """Clears the receiver list""" + pass + + def clearToColor(self,color): + """Clear the DynamicMap to the specified color +- 'color' is a ptColor object""" + pass + + def drawImage(self,x,y,image,respectAlphaFlag): + """Draws a ptImage object on the dynamicTextmap starting at the location x,y""" + pass + + def drawImageClipped(self,x,y,image,cx,cy,cw,ch,respectAlphaFlag): + """Draws a ptImage object clipped to cx,cy with cw(width),ch(height)""" + pass + + def drawText(self,x,y,text): + """Draw text at a specified location +- x,y is the point to start drawing the text +- 'text' is a string of the text to be drawn""" + pass + + def drawTextW(self,x,y,text): + """Unicode version of drawText""" + pass + + def fillRect(self,left,top,right,bottom,color): + """Fill in the specified rectangle with a color +- left,top,right,bottom define the rectangle +- 'color' is a ptColor object""" + pass + + def flush(self): + """Flush all the commands that were issued since the last flush()""" + pass + + def frameRect(self,left,top,right,bottom,color): + """Frame a rectangle with a specified color +- left,top,right,bottom define the rectangle +- 'color' is a ptColor object""" + pass + + def getHeight(self): + """Returns the height of the dynamicTextmap""" + pass + + def getImage(self): + """Returns a pyImage associated with the dynamicTextmap""" + pass + + def getWidth(self): + """Returns the width of the dynamicTextmap""" + pass + + def netForce(self,forceFlag): + """Specify whether this object needs to use messages that are forced to the network +- This is to be used if your Python program is running on only one client +Such as a game master, only running on the client that owns a particular object +This only applies when NetPropagate is set to true""" + pass + + def netPropagate(self,propagateFlag): + """Specify whether this object needs to use messages that are sent on the network +- The default is for this to be false.""" + pass + + def purgeImage(self): + """Purge the DynamicTextMap images""" + pass + + def sender(self,sender): + """Set the sender of the message being sent to the DynamicMap""" + pass + + def setClipping(self,clipLeft,clipTop,clipRight,clipBottom): + """Sets the clipping rectangle +- All drawtext will be clipped to this until the +unsetClipping() is called""" + pass + + def setFont(self,facename,size): + """Set the font of the text to be written +- 'facename' is a string with the name of the font +- 'size' is the point size of the font to use""" + pass + + def setJustify(self,justify): + """Sets the justification of the text. (justify is a PtJustify)""" + pass + + def setLineSpacing(self,spacing): + """Sets the line spacing (in pixels)""" + pass + + def setTextColor(self,color, blockRGB=0): + """Set the color of the text to be written +- 'color' is a ptColor object +- 'blockRGB' must be true if you're trying to render onto a transparent or semi-transparent color""" + pass + + def setWrapping(self,wrapWidth,wrapHeight): + """Set where text will be wrapped horizontally and vertically +- All drawtext commands will be wrapped until the +unsetWrapping() is called""" + pass + + def unsetClipping(self): + """Stop the clipping of text""" + pass + + def unsetWrapping(self): + """Stop text wrapping""" + pass + +class ptGUIControl: + """Base class for all GUI controls""" + def __init__(self,controlKey): + """None""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIControlButton(ptGUIControl): + """Plasma GUI Control Button class""" + def __init__(self,ctrlKey): + """None""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getNotifyType(self): + """Returns this button's notify type. See PtButtonNotifyTypes""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isButtonDown(self): + """Is the button down? Returns 1 for true otherwise returns 0""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setNotifyType(self,kind): + """Sets this button's notify type. See PtButtonNotifyTypes""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIControlCheckBox(ptGUIControl): + """Plasma GUI Control Checkbox class""" + def __init__(self,ctrlKey): + """None""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isChecked(self): + """Is this checkbox checked? Returns 1 for true otherwise returns 0""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setChecked(self,checkedState): + """Sets this checkbox to the 'checkedState'""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIControlClickMap(ptGUIControl): + """Plasma GUI control Click Map""" + def __init__(self,ctrlKey): + """None""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getLastMouseDragPoint(self): + """Returns the last point the mouse was dragged to""" + pass + + def getLastMousePoint(self): + """Returns the last point the mouse was at""" + pass + + def getLastMouseUpPoint(self): + """Returns the last point the mouse was released at""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIControlDragBar(ptGUIControl): + """Plasma GUI Control DragBar class""" + def __init__(self,ctrlKey): + """None""" + pass + + def anchor(self): + """Don't allow this dragbar object to be moved by the user. +Drop anchor!""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isAnchored(self): + """Is this dragbar control anchored? Returns 1 if true otherwise returns 0""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + + def unanchor(self): + """Allow the user to drag this control around the screen. +Raise anchor.""" + pass + +class ptGUIControlDraggable(ptGUIControl): + """Plasma GUI control for something draggable""" + def __init__(self,ctrlKey): + """None""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getLastMousePoint(self): + """Returns the last point we were dragged to""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def stopDragging(self,cancelFlag): + """UNKNOWN""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIControlDynamicText(ptGUIControl): + """Plasma GUI Control DynamicText class""" + def __init__(self,ctrlKey): + """None""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getMap(self,index): + """Returns a specific ptDynamicText attached to this contol +If there is no map at 'index' then a KeyError exception will be raised""" + pass + + def getNumMaps(self): + """Returns the number of ptDynamicText maps attached""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIControlEditBox(ptGUIControl): + """Plasma GUI Control Editbox class""" + def __init__(self,ctrlKey): + """None""" + pass + + def clearString(self): + """Clears the editbox.""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def end(self): + """Sets the cursor in the editbox to the after the last character.""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getLastKeyCaptured(self): + """Gets the last capture key""" + pass + + def getLastModifiersCaptured(self): + """Gets the last modifiers flags captured""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getString(self): + """Returns the sting that the user typed in.""" + pass + + def getStringW(self): + """Unicode version of getString.""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def home(self): + """Sets the cursor in the editbox to before the first character.""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setChatMode(self,state): + """Set the Chat mode on this control""" + pass + + def setColor(self,foreColor,backColor): + """Sets the fore and back color of the editbox.""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setLastKeyCapture(self,key, modifiers): + """Set last key captured""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setSelectionColor(self,foreColor,backColor): + """Sets the selection color of the editbox.""" + pass + + def setSpecialCaptureKeyMode(self,state): + """Set the Capture mode on this control""" + pass + + def setString(self,text): + """Pre-sets the editbox to a atring.""" + pass + + def setStringSize(self,size): + """Sets the maximum size of the string that can be inputted by the user.""" + pass + + def setStringW(self,text): + """Unicode version of setString.""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + + def wasEscaped(self): + """If the editbox was escaped then return 1 else return 0""" + pass + +class ptGUIControlValue(ptGUIControl): + """Plasma GUI Control Value class - knobs, spinners""" + def __init__(self,ctrlKey): + """None""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getMax(self): + """Returns the maximum of the control.""" + pass + + def getMin(self): + """Returns the minimum of the control.""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getStep(self): + """Returns the step increment of the control.""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def getValue(self): + """Returns the current value of the control.""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setRange(self,minimum,maximum): + """Sets the minimum and maximum range of the control.""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setStep(self,step): + """Sets the step increment of the control.""" + pass + + def setValue(self,value): + """Sets the current value of the control.""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIControlKnob(ptGUIControlValue): + """Plasma GUI control for knob""" + def __init__(self,ctrlKey): + """None""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getMax(self): + """Returns the maximum of the control.""" + pass + + def getMin(self): + """Returns the minimum of the control.""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getStep(self): + """Returns the step increment of the control.""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def getValue(self): + """Returns the current value of the control.""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setRange(self,minimum,maximum): + """Sets the minimum and maximum range of the control.""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setStep(self,step): + """Sets the step increment of the control.""" + pass + + def setValue(self,value): + """Sets the current value of the control.""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIControlListBox(ptGUIControl): + """Plasma GUI Control List Box class""" + def __init__(self,ctrlKey): + """None""" + pass + + def add2StringsWithColors(self,text1,color1,text2,color2,respectAlpha): + """Doesn't work right - DONT USE""" + pass + + def addBranch(self,name,initiallyOpen): + """UNKNOWN""" + pass + + def addBranchW(self,name,initiallyOpen): + """Unicode version of addBranch""" + pass + + def addImage(self,image,respectAlphaFlag): + """Appends an image item to the listbox""" + pass + + def addImageAndSwatchesInBox(self,image,x,y,width,height,respectAlpha,primary,secondary): + """Add the image and color swatches to the list""" + pass + + def addImageInBox(self,image,x,y,width,height,respectAlpha): + """Appends an image item to the listbox, centering within the box dimension.""" + pass + + def addSelection(self,item): + """Adds item to selection list""" + pass + + def addString(self,text): + """Appends a list item 'text' to the listbox.""" + pass + + def addStringInBox(self,text,min_width,min_height): + """Adds a text list item that has a minimum width and height""" + pass + + def addStringW(self,text): + """Unicode version of addString.""" + pass + + def addStringWithColor(self,text,color,inheritAlpha): + """Adds a colored string to the list box""" + pass + + def addStringWithColorWithSize(self,text,color,inheritAlpha,fontsize): + """Adds a text list item with a color and different font size""" + pass + + def allowNoSelect(self): + """Allows the listbox to have no selection""" + pass + + def clearAllElements(self): + """Removes all the items from the listbox, making it empty.""" + pass + + def clickable(self): + """Sets this listbox to be clickable by the user.""" + pass + + def closeBranch(self): + """UNKNOWN""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def disallowNoSelect(self): + """The listbox must always have a selection""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def findString(self,text): + """Finds and returns the index of the item that matches 'text' in the listbox.""" + pass + + def findStringW(self,text): + """Unicode version of findString.""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getBranchList(self): + """get a list of branches in this list (index,isShowingChildren)""" + pass + + def getElement(self,index): + """Get the string of the item at 'index' in the listbox.""" + pass + + def getElementW(self,index): + """Unicode version of getElement.""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getNumElements(self): + """Return the number of items in the listbox.""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getScrollPos(self): + """Returns the current scroll position in the listbox.""" + pass + + def getScrollRange(self): + """Returns the max scroll position""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getSelection(self): + """Returns the currently selected list item in the listbox.""" + pass + + def getSelectionList(self): + """Returns the current selection list""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def lock(self): + """Locks the updates to a listbox, so a number of operations can be performed +NOTE: an unlock() call must be made before the next lock() can be.""" + pass + + def refresh(self): + """Refresh the display of the listbox (after updating contents).""" + pass + + def removeElement(self,index): + """Removes element at 'index' in the listbox.""" + pass + + def removeSelection(self,item): + """Removes item from selection list""" + pass + + def scrollToBegin(self): + """Scrolls the listbox to the beginning of the list""" + pass + + def scrollToEnd(self): + """Scrolls the listbox to the end of the list""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setElement(self,index,text): + """Set a particular item in the listbox to a string.""" + pass + + def setElementW(self,index,text): + """Unicode version of setElement.""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setGlobalSwatchEdgeOffset(self,offset): + """Sets the edge offset of the color swatches""" + pass + + def setGlobalSwatchSize(self,size): + """Sets the size of the color swatches""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setScrollPos(self,pos): + """Sets the scroll position of the listbox to 'pos'""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setSelection(self,selectionIndex): + """Sets the current selection in the listbox.""" + pass + + def setStringJustify(self,index,justify): + """Sets the text justification""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + + def unclickable(self): + """Makes this listbox not clickable by the user. +Useful when just displaying a list that is not really selectable.""" + pass + + def unlock(self): + """Unlocks updates to a listbox and does any saved up changes""" + pass + +class ptGUIControlMultiLineEdit(ptGUIControl): + """Plasma GUI Control Multi-line edit class""" + def __init__(self,ctrlKey): + """None""" + pass + + def clearBuffer(self): + """Clears all text from the multi-line edit control.""" + pass + + def clickable(self): + """Sets this listbox to be clickable by the user.""" + pass + + def deleteChar(self): + """Deletes a character at the current cursor position.""" + pass + + def deleteLinesFromTop(self,numLines): + """Deletes the specified number of lines from the top of the text buffer""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def disableScrollControl(self): + """Disables the scroll control if there is one""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def enableScrollControl(self): + """Enables the scroll control if there is one""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getBufferLimit(self): + """Returns the current buffer limit""" + pass + + def getBufferSize(self): + """Returns the size of the buffer""" + pass + + def getEncodedBuffer(self): + """Returns the encoded buffer in a python buffer object. Do NOT use result with setEncodedBufferW.""" + pass + + def getEncodedBufferW(self): + """Unicode version of getEncodedBuffer. Do NOT use result with setEncodedBuffer.""" + pass + + def getFontSize(self): + """Returns the current default font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getString(self): + """Gets the string of the edit control.""" + pass + + def getStringW(self): + """Unicode version of getString.""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def insertChar(self,c): + """Inserts a character at the current cursor position.""" + pass + + def insertCharW(self,c): + """Unicode version of insertChar.""" + pass + + def insertColor(self,color): + """Inserts an encoded color object at the current cursor position. +'color' is a ptColor object.""" + pass + + def insertString(self,string): + """Inserts a string at the current cursor position.""" + pass + + def insertStringW(self,string): + """Unicode version of insertString""" + pass + + def insertStyle(self,style): + """Inserts an encoded font style at the current cursor position.""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isLocked(self): + """Is the multi-line edit control locked? Returns 1 if true otherwise returns 0""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def lock(self): + """Locks the multi-line edit control so the user cannot make changes.""" + pass + + def moveCursor(self,direction): + """Move the cursor in the specified direction (see PtGUIMultiLineDirection)""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setBufferLimit(self,bufferLimit): + """Sets the buffer max for the editbox""" + pass + + def setEncodedBuffer(self,bufferObject): + """Sets the edit control to the encoded buffer in the python buffer object. Do NOT use with a result from getEncodedBufferW.""" + pass + + def setEncodedBufferW(self,bufferObject): + """Unicode version of setEncodedBuffer. Do NOT use with a result from getEncodedBuffer.""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the default font size for the edit control""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setScrollPosition(self,topLine): + """Sets the what line is the top line.""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setString(self,asciiText): + """Sets the multi-line edit control string.""" + pass + + def setStringW(self,unicodeText): + """Unicode version of setString.""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + + def unclickable(self): + """Makes this listbox not clickable by the user. +Useful when just displaying a list that is not really selectable.""" + pass + + def unlock(self): + """Unlocks the multi-line edit control so that the user can make changes.""" + pass + +class ptGUIControlProgress(ptGUIControlValue): + """Plasma GUI control for progress bar""" + def __init__(self,ctrlKey): + """None""" + pass + + def animateToPercent(self,percent): + """Sets the value of the control and animates to that point.""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getMax(self): + """Returns the maximum of the control.""" + pass + + def getMin(self): + """Returns the minimum of the control.""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getStep(self): + """Returns the step increment of the control.""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def getValue(self): + """Returns the current value of the control.""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setRange(self,minimum,maximum): + """Sets the minimum and maximum range of the control.""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setStep(self,step): + """Sets the step increment of the control.""" + pass + + def setValue(self,value): + """Sets the current value of the control.""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIControlRadioGroup(ptGUIControl): + """Plasma GUI Control Radio Group class""" + def __init__(self,ctrlKey): + """None""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def getValue(self): + """Returns the current selection of the radio group.""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setValue(self,value): + """Sets the current selection to 'value'""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIControlTextBox(ptGUIControl): + """Plasma GUI Control Textbox class""" + def __init__(self,ctrlKey): + """None""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the current forecolor""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getString(self): + """Returns the string that the TextBox is set to (in case you forgot)""" + pass + + def getStringJustify(self): + """Returns current justify""" + pass + + def getStringW(self): + """Unicode version of getString""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,color): + """Sets the text backcolor to 'color', which is a ptColor object.""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,size): + """Don't use""" + pass + + def setForeColor(self,color): + """Sets the text forecolor to 'color', which is a ptColor object.""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setString(self,text): + """Sets the textbox string to 'text'""" + pass + + def setStringJustify(self,justify): + """Sets current justify""" + pass + + def setStringW(self,text): + """Unicode version of setString""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIControlUpDownPair(ptGUIControlValue): + """Plasma GUI control for up/down pair""" + def __init__(self,ctrlKey): + """None""" + pass + + def disable(self): + """Disables this GUI control""" + pass + + def enable(self,flag=1): + """Enables this GUI control""" + pass + + def focus(self): + """Gets focus for this GUI control""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns the ptKey for this GUI control""" + pass + + def getMax(self): + """Returns the maximum of the control.""" + pass + + def getMin(self): + """Returns the minimum of the control.""" + pass + + def getObjectCenter(self): + """Returns ptPoint3 of the center of the GUI control object""" + pass + + def getOwnerDialog(self): + """Returns a ptGUIDialog of the dialog that owns this GUI control""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getStep(self): + """Returns the step increment of the control.""" + pass + + def getTagID(self): + """Returns the Tag ID for this GUI control""" + pass + + def getValue(self): + """Returns the current value of the control.""" + pass + + def hide(self): + """Hides this GUI control""" + pass + + def isEnabled(self): + """Returns whether this GUI control is enabled""" + pass + + def isFocused(self): + """Returns whether this GUI control has focus""" + pass + + def isInteresting(self): + """Returns whether this GUI control is interesting at the moment""" + pass + + def isVisible(self): + """Returns whether this GUI control is visible""" + pass + + def refresh(self): + """UNKNOWN""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setFocus(self,state): + """Sets the state of the focus of this GUI control""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setNotifyOnInteresting(self,state): + """Sets whether this control should send interesting events or not""" + pass + + def setObjectCenter(self,point): + """Sets the GUI controls object center to 'point'""" + pass + + def setRange(self,minimum,maximum): + """Sets the minimum and maximum range of the control.""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def setStep(self,step): + """Sets the step increment of the control.""" + pass + + def setValue(self,value): + """Sets the current value of the control.""" + pass + + def setVisible(self,state): + """Sets the state of visibility of this GUI control""" + pass + + def show(self): + """Shows this GUI control""" + pass + + def unFocus(self): + """Releases focus for this GUI control""" + pass + +class ptGUIDialog: + """Plasma GUI dialog class""" + def __init__(self,dialogKey): + """None""" + pass + + def disable(self): + """Disables this dialog""" + pass + + def enable(self,enableFlag=1): + """Enable this dialog""" + pass + + def getBackColor(self): + """Returns the back color as a ptColor object""" + pass + + def getBackSelectColor(self): + """Returns the select back color as a ptColor object""" + pass + + def getControlFromIndex(self,index): + """Returns the ptKey of the control with the specified index (not tag ID!)""" + pass + + def getControlFromTag(self,tagID): + """Returns the ptKey of the control with the specified tag ID""" + pass + + def getFontSize(self): + """Returns the font size""" + pass + + def getForeColor(self): + """Returns the fore color as a ptColor object""" + pass + + def getKey(self): + """Returns this dialog's ptKey""" + pass + + def getName(self): + """Returns the dialog's name""" + pass + + def getNumControls(self): + """Returns the number of controls in this dialog""" + pass + + def getSelectColor(self): + """Returns the select color as a ptColor object""" + pass + + def getTagID(self): + """Returns this dialog's tag ID""" + pass + + def getVersion(self): + """UNKNOWN""" + pass + + def hide(self): + """Hides the dialog""" + pass + + def isEnabled(self): + """Is this dialog currently enabled?""" + pass + + def noFocus(self): + """Makes sure no control has input focus""" + pass + + def refreshAllControls(self): + """Tells the dialog to redraw all its controls""" + pass + + def setBackColor(self,red,green,blue,alpha): + """Sets the back color, -1 means don't change""" + pass + + def setBackSelectColor(self,red,green,blue,alpha): + """Sets the select back color, -1 means don't change""" + pass + + def setFocus(self,ctrlKey): + """Sets the control that has input focus""" + pass + + def setFontSize(self,fontSize): + """Sets the font size""" + pass + + def setForeColor(self,red,green,blue,alpha): + """Sets the fore color, -1 means don't change""" + pass + + def setSelectColor(self,red,green,blue,alpha): + """Sets the select color, -1 means don't change""" + pass + + def show(self): + """Shows the dialog""" + pass + + def showNoReset(self): + """Show dialog without resetting clickables""" + pass + + def updateAllBounds(self): + """Tells the dialog to recompute all the bounds for its controls""" + pass + +class ptGUIPopUpMenu: + """Takes three diferent argument lists: +gckey +name,screenOriginX,screenOriginY +name,parent,screenOriginX,screenOriginY""" + def __init__(self,arg1,arg2=None,arg3=None,arg4=None): + """None""" + pass + + def addConsoleCmdItem(self,name,consoleCmd): + """Adds a new item to the menu that fires a console command""" + pass + + def addConsoleCmdItemW(self,name,consoleCmd): + """Unicode version of addConsoleCmdItem""" + pass + + def addNotifyItem(self,name): + """Adds a new item ot the mneu""" + pass + + def addNotifyItemW(self,name): + """Unicode version of addNotifyItem""" + pass + + def addSubMenuItem(self,name,subMenu): + """Adds a submenu to this menu""" + pass + + def addSubMenuItemW(self,name,subMenu): + """Unicode version of addSubMenuItem""" + pass + + def disable(self): + """Disables this menu""" + pass + + def enable(self,state=1): + """Enables/disables this menu""" + pass + + def getBackColor(self): + """Returns the background color""" + pass + + def getBackSelectColor(self): + """Returns the background selection color""" + pass + + def getForeColor(self): + """Returns the foreground color""" + pass + + def getKey(self): + """Returns this menu's key""" + pass + + def getName(self): + """Returns this menu's name""" + pass + + def getSelectColor(self): + """Returns the selection color""" + pass + + def getTagID(self): + """Returns this menu's tag id""" + pass + + def getVersion(self): + """UNKNOWN""" + pass + + def hide(self): + """Hides this menu""" + pass + + def isEnabled(self): + """Returns whether this menu is enabled or not""" + pass + + def setBackColor(self,r,g,b,a): + """Sets the background color""" + pass + + def setBackSelectColor(self,r,g,b,a): + """Sets the selection background color""" + pass + + def setForeColor(self,r,g,b,a): + """Sets the foreground color""" + pass + + def setSelectColor(self,r,g,b,a): + """Sets the selection color""" + pass + + def show(self): + """Shows this menu""" + pass + +class ptGUISkin: + """Plasma GUI Skin object""" + def __init__(self,key): + """None""" + pass + + def getKey(self): + """Returns this object's ptKey""" + pass + +class ptGameScore: + """Game score manager""" + def __init__(self): + """None""" + pass + + def addPoints(self,numPoints): + """Adds points to the score""" + pass + + def getCreatedTime(self): + """Returns a the score creation time.""" + pass + + def getGameName(self): + """Returns a the score game name.""" + pass + + def getGameType(self): + """Returns a the score game type.""" + pass + + def getOwnerID(self): + """Returns a the score owner id.""" + pass + + def getScoreID(self): + """Returns the score id.""" + pass + + def getValue(self): + """Returns a the score owner value.""" + pass + + def setPoints(self,numPoints): + """Sets the number of points in the score +Don't use to add/remove points, use only to reset values!""" + pass + + def transferPoints(self,dest, numPoints): + """Transfers points from one score to another""" + pass + +class ptGrassShader: + """Plasma Grass Shader class""" + def __init__(self,key): + """None""" + pass + + def getWaveDirection(self,waveNum): + """Gets the wave waveNum's direction as a tuple of x,y. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive""" + pass + + def getWaveDistortion(self,waveNum): + """Gets the wave waveNum's distortion as a tuple of x,y,z. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive""" + pass + + def getWaveSpeed(self,waveNum): + """Gets the wave waveNum's speed as a float. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive""" + pass + + def resetWaves(self): + """Resets wave data to 0""" + pass + + def setWaveDirection(self,waveNum, direction): + """Sets the wave waveNum's direction as a tuple of x,y. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive""" + pass + + def setWaveDistortion(self,waveNum, distortion): + """Sets the wave waveNum's distortion as a tuple of x,y,z. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive""" + pass + + def setWaveSpeed(self,waveNum, speed): + """Sets the wave waveNum's speed as a float. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive""" + pass + +class ptImage: + """Plasma image class""" + def __init__(self,imgKey): + """None""" + pass + + def getColorLoc(self,color): + """Returns the ptPoint3 where the specified color is located""" + pass + + def getHeight(self): + """Returns the height of the image""" + pass + + def getPixelColor(self,x,y): + """Returns the ptColor at the specified location (float from 0 to 1)""" + pass + + def getWidth(self): + """Returns the width of the image""" + pass + + def saveAsJPEG(self,filename,quality=75): + """Saves this image to disk as a JPEG file""" + pass + +class ptInputInterface: + """Plasma input interface class""" + def __init__(self): + """None""" + pass + + def popTelescope(self): + """pops off the telescope interface and gos back to previous interface""" + pass + + def pushTelescope(self): + """pushes on the telescope interface""" + pass + +class ptKey: + """Plasma Key class""" + def __init__(self): + """None""" + pass + + def disable(self): + """Sends a disable message to whatever this ptKey is pointing to""" + pass + + def enable(self): + """Sends an enable message to whatever this ptKey is pointing to""" + pass + + def getName(self): + """Get the name of the object that this ptKey is pointing to""" + pass + + def getParentKey(self): + """This will return a ptKey object that is the parent of this modifer +However, if the parent is not a modifier or not loaded, then None is returned.""" + pass + + def getSceneObject(self): + """This will return a ptSceneobject object that is associated with this ptKey +However, if this ptKey is _not_ a sceneobject, then unpredicatable results will ensue""" + pass + + def isAttachedToClone(self): + """Returns whether the python file mod is attached to a clone""" + pass + + def netForce(self,forceFlag): + """Specify whether this object needs to use messages that are forced to the network +- This is to be used if your Python program is running on only one client +Such as a game master, only running on the client that owns a particular object""" + pass + +class ptKeyMap: + """Accessor class to the Key Mapping functions""" + def __init__(self): + """None""" + pass + + def bindKey(self): + """Params key1,key2,action +Bind keys to an action""" + pass + + def bindKeyToConsoleCommand(self,keyStr1, command): + """Binds key to console command""" + pass + + def convertCharToControlCode(self,controlCodeString): + """Convert string version of control code to number""" + pass + + def convertCharToFlags(self,charString): + """Convert char string to flags""" + pass + + def convertCharToVKey(self,charString): + """Convert char string to virtual key""" + pass + + def convertControlCodeToString(self): + """Params controlCode +Convert control code to character string""" + pass + + def convertVKeyToChar(self,virtualKey,flags): + """Convert virtual key and shift flags to string""" + pass + + def getBindingFlags1(self): + """Params controlCode +Returns modifier flags for controlCode""" + pass + + def getBindingFlags2(self): + """Params controlCode +Returns modifier flags for controlCode""" + pass + + def getBindingFlagsConsole(self,command): + """Returns modifier flags for the console command mapping""" + pass + + def getBindingKey1(self): + """Params controlCode +Returns key code for controlCode""" + pass + + def getBindingKey2(self): + """Params controlCode +Returns key code for controlCode""" + pass + + def getBindingKeyConsole(self,command): + """Returns key for console command mapping""" + pass + + def writeKeyMap(self): + """Forces write of the keymap file""" + pass + +class ptMarkerMgr: + """Marker manager accessor class""" + def __init__(self): + """None""" + pass + + def addMarker(self,x, y, z, id, justCreated): + """Add a marker in the specified location with the specified id""" + pass + + def areLocalMarkersShowing(self): + """Returns true if we are showing the markers on this local machine""" + pass + + def captureQuestMarker(self,id, captured): + """Sets a marker as captured or not""" + pass + + def captureTeamMarker(self,id, team): + """Sets a marker as captured by the specified team (0 = not captured)""" + pass + + def clearSelectedMarker(self): + """Unselects the selected marker""" + pass + + def getMarkersRespawn(self): + """Returns whether markers respawn after being captured, or not""" + pass + + def getSelectedMarker(self): + """Returns the id of the selected marker""" + pass + + def hideMarkersLocal(self): + """Hides the markers on your machine, so you can no longer see where they are""" + pass + + def removeAllMarkers(self): + """Removes all markers""" + pass + + def removeMarker(self,id): + """Removes the specified marker from the game""" + pass + + def setMarkersRespawn(self,respawn): + """Sets whether markers respawn after being captured, or not""" + pass + + def setSelectedMarker(self,id): + """Sets the selected marker to the one with the specified id""" + pass + + def showMarkersLocal(self): + """Shows the markers on your machine, so you can see where they are""" + pass + +class ptMatrix44: + """Plasma Matrix44 class""" + def __init__(self): + """None""" + pass + + def copy(self): + """Copies the matrix and returns the copy""" + pass + + def getAdjoint(self,adjointMat): + """Returns the adjoint of the matrix""" + pass + + def getData(self): + """Returns the matrix in tuple form""" + pass + + def getDeterminant(self): + """Get the matrix's determinant""" + pass + + def getInverse(self,inverseMat): + """Returns the inverse of the matrix""" + pass + + def getParity(self): + """Get the parity of the matrix""" + pass + + def getTranslate(self,vector): + """Returns the translate vector of the matrix (and sets vector to it as well)""" + pass + + def getTranspose(self,transposeMat): + """Returns the transpose of the matrix""" + pass + + def make(self,fromPt, atPt, upVec): + """Creates the matrix from from and at points, and the up vector""" + pass + + def makeRotateMat(self,axis,radians): + """Makes the matrix a rotation matrix""" + pass + + def makeScaleMat(self,scale): + """Makes the matrix a scaling matrix""" + pass + + def makeTranslateMat(self,trans): + """Makes the matrix a translation matrix""" + pass + + def makeUpPreserving(self,fromPt, atPt, upVec): + """Creates the matrix from from and at points, and the up vector (perserving the up vector)""" + pass + + def reset(self): + """Reset the matrix to identity""" + pass + + def right(self): + """Returns the right vector of the matrix""" + pass + + def rotate(self,axis,radians): + """Rotates the matrix by radians around the axis""" + pass + + def scale(self,scale): + """Scales the matrix by the vector""" + pass + + def setData(self,mat): + """Sets the matrix using tuples""" + pass + + def translate(self,vector): + """Translates the matrix by the vector""" + pass + + def up(self): + """Returns the up vector of the matrix""" + pass + + def view(self): + """Returns the view vector of the matrix""" + pass + +class ptMoviePlayer: + """Accessor class to play in the MoviePlayer""" + def __init__(self,movieName,selfKey): + """None""" + pass + + def pause(self): + """Pauses the movie""" + pass + + def play(self): + """Plays the movie""" + pass + + def playPaused(self): + """Plays movie, but pauses at first frame""" + pass + + def resume(self): + """Resumes movie after pausing""" + pass + + def setCenter(self,x,y): + """Sets the center of the movie""" + pass + + def setColor(self,color): + """Sets the color of the movie""" + pass + + def setOpacity(self,opacity): + """Sets the opacity of the movie""" + pass + + def setScale(self,width,height): + """Sets the width and height scale of the movie""" + pass + + def setVolume(self,volume): + """Set the volume of the movie""" + pass + + def stop(self): + """Stops the movie""" + pass + +class ptNetLinkingMgr: + """Constructor to get access to the net link manager""" + def __init__(self): + """None""" + pass + + def getCurrAgeLink(self): + """Get the ptAgeLinkStruct for the current age""" + pass + + def getPrevAgeLink(self): + """Get the ptAgeLinkStruct for the previous age""" + pass + + def isEnabled(self): + """True if linking is enabled.""" + pass + + def linkPlayerHere(self,pid): + """link player(pid) to where I am""" + pass + + def linkPlayerToAge(self,ageLink,pid): + """Link player(pid) to ageLink""" + pass + + def linkToAge(self,ageLink): + """Links to ageLink (ptAgeLinkStruct)""" + pass + + def linkToMyNeighborhoodAge(self): + """Link to my Neighborhood Age""" + pass + + def linkToMyPersonalAge(self): + """Link to my Personal Age""" + pass + + def linkToMyPersonalAgeWithYeeshaBook(self): + """Link to my Personal Age with the YeeshaBook""" + pass + + def linkToPlayersAge(self,pid): + """Link me to where player(pid) is""" + pass + + def setEnabled(self,enable): + """Enable/Disable linking.""" + pass + +class ptNotify: + """Creates a Notify message +- selfKey is ptKey of your PythonFile modifier""" + def __init__(self,selfKey): + """None""" + pass + + def addActivateEvent(self,activeFlag,activateFlag): + """Add an activate event record to the notify message""" + pass + + def addCallbackEvent(self,eventNumber): + """Add a callback event record to the notify message""" + pass + + def addCollisionEvent(self,enterFlag,hitterKey,hitteeKey): + """Add a collision event record to the Notify message""" + pass + + def addContainerEvent(self,enteringFlag,containerKey,containedKey): + """Add a container event record to the notify message""" + pass + + def addControlKeyEvent(self,keynumber,downFlag): + """Add a keyboard event record to the Notify message""" + pass + + def addFacingEvent(self,enabledFlag,facerKey, faceeKey, dotProduct): + """Add a facing event record to the Notify message""" + pass + + def addPickEvent(self,enabledFlag,pickerKey,pickeeKey,hitPoint): + """Add a pick event record to the Notify message""" + pass + + def addReceiver(self,key): + """Add a receivers key to receive this Notify message""" + pass + + def addResponderState(self,state): + """Add a responder state event record to the notify message""" + pass + + def addVarKey(self,name,key): + """Add a ptKey variable event record to the Notify message +This event record is used to pass a ptKey variable to another python program""" + pass + + def addVarNumber(self,name,number): + """Add a number variable event record to the Notify message +This event record is used to pass a number variable to another python program""" + pass + + def clearReceivers(self): + """Remove all the receivers that this Notify message has +- receivers are automatically added if from a ptAttribActivator""" + pass + + def netForce(self,forceFlag): + """Specify whether this object needs to use messages that are forced to the network +- This is to be used if your Python program is running on only one client +Such as a game master, only running on the client that owns a particular object""" + pass + + def netPropagate(self,netFlag): + """Sets the net propagate flag - default to set""" + pass + + def send(self): + """Send the notify message""" + pass + + def setActivate(self,state): + """Set the activate state to true(1.0) or false(0.0)""" + pass + + def setType(self,type): + """Sets the message type""" + pass + +class ptParticle: + """Plasma particle system class""" + def __init__(self): + """None""" + pass + + def netForce(self,forceFlag): + """Specify whether this object needs to use messages that are forced to the network +- This is to be used if your Python program is running on only one client +Such as a game master, only running on the client that owns a particular object""" + pass + + def setGeneratorLife(self,value): + """NEEDS DOCSTRING""" + pass + + def setHeightSize(self,value): + """NEEDS DOCSTRING""" + pass + + def setInitPitchRange(self,value): + """NEEDS DOCSTRING""" + pass + + def setInitYawRange(self,value): + """NEEDS DOCSTRING""" + pass + + def setParticleLifeMaximum(self,value): + """NEEDS DOCSTRING""" + pass + + def setParticleLifeMinimum(self,value): + """NEEDS DOCSTRING""" + pass + + def setParticlesPerSecond(self,value): + """NEEDS DOCSTRING""" + pass + + def setScaleMaximum(self,value): + """NEEDS DOCSTRING""" + pass + + def setScaleMinimum(self,value): + """NEEDS DOCSTRING""" + pass + + def setVelocityMaximum(self,value): + """NEEDS DOCSTRING""" + pass + + def setVelocityMinimum(self,value): + """NEEDS DOCSTRING""" + pass + + def setWidthSize(self,value): + """NEEDS DOCSTRING""" + pass + +class ptPhysics: + """Plasma physics class""" + def __init__(self): + """None""" + pass + + def angularImpulse(self,impulseVector): + """Add the given vector (representing a rotation axis and magnitude) to +the attached sceneobject's velocity""" + pass + + def damp(self,damp): + """Reduce all velocities on the object (0 = all stop, 1 = no effect)""" + pass + + def disable(self): + """Disables physics on the sceneobject attached""" + pass + + def disableCollision(self): + """Disables collision detection on the attached sceneobject""" + pass + + def enable(self,state=1): + """Sets the physics enable state for the sceneobject attached""" + pass + + def enableCollision(self): + """Enables collision detection on the attached sceneobject""" + pass + + def force(self,forceVector): + """Applies the specified force to the attached sceneobject""" + pass + + def forceWithOffset(self,forceVector,offsetPt): + """Applies the specified offsetted force to the attached sceneobject""" + pass + + def impulse(self,impulseVector): + """Adds the given vector to the attached sceneobject's velocity""" + pass + + def impulseWithOffset(self,impulseVector,offsetPt): + """Adds the given vector to the attached sceneobject's velocity +with the specified offset""" + pass + + def move(self,direction,distance): + """Moves the attached sceneobject the specified distance in the specified direction""" + pass + + def netForce(self,forceFlag): + """Specify whether this object needs to use messages that are forced to the network +- This is to be used if your Python program is running on only one client +Such as a game master, only running on the client that owns a particular object""" + pass + + def rotate(self,radians,axis): + """Rotates the attached sceneobject the specified radians around the specified axis""" + pass + + def shiftMass(self,offsetVector): + """Shifts the attached sceneobject's center to mass in the specified direction and distance""" + pass + + def suppress(self,doSuppress): + """Completely remove the physical, but keep it around so it +can be added back later.""" + pass + + def torque(self,torqueVector): + """Applies the specified torque to the attached sceneobject""" + pass + + def warp(self,position): + """Warps the sceneobject to a specified location. +'position' can be a ptPoint3 or a ptMatrix44""" + pass + + def warpObj(self,objkey): + """Warps the sceneobject to match the location and orientation of the specified object""" + pass + +class ptPlayer: + """And optionally __init__(name,playerID)""" + def __init__(self,avkey,name,playerID,distanceSq): + """None""" + pass + + def getDistanceSq(self): + """Returns the distance to remote player from local player""" + pass + + def getPlayerID(self): + """Returns the unique player ID""" + pass + + def getPlayerName(self): + """Returns the name of the player""" + pass + + def isCCR(self): + """Is this player a CCR?""" + pass + + def isServer(self): + """Is this player a server?""" + pass + +class ptPoint3: + """Plasma Point class""" + def __init__(self,x=0, y=0, z=0): + """None""" + pass + + def copy(self): + """Returns a copy of the point in another ptPoint3 object""" + pass + + def distance(self,other): + """Computes the distance from this point to 'other' point""" + pass + + def distanceSq(self,other): + """Computes the distance squared from this point to 'other' point +- this function is faster than distance(other)""" + pass + + def getX(self): + """Returns the 'x' component of the point""" + pass + + def getY(self): + """Returns the 'y' component of the point""" + pass + + def getZ(self): + """Returns the 'z' component of the point""" + pass + + def setX(self,x): + """Sets the 'x' component of the point""" + pass + + def setY(self,y): + """Sets the 'y' component of the point""" + pass + + def setZ(self,z): + """Sets the 'z' component of the point""" + pass + + def zero(self): + """Sets the 'x','y' and the 'z' component to zero""" + pass + +class ptSDL: + """SDL accessor""" + def __init__(self): + """None""" + pass + + def sendToClients(self,key): + """Sets it so changes to this key are sent to the +server AND the clients. (Normally it just goes +to the server.)""" + pass + + def setDefault(self,key,value): + """Like setitem, but doesn't broadcast over the net. +Only use for setting defaults that everyone will +already know (from reading it off disk)""" + pass + + def setFlags(self,name,sendImmediate,skipOwnershipCheck): + """Sets the flags for a variable in this SDL""" + pass + + def setIndex(self,key,idx,value): + """Sets the value at a specific index in the tuple, +so you don't have to pass the whole thing in""" + pass + + def setIndexNow(self,key,idx,value): + """Same as setIndex but sends immediately""" + pass + + def setNotify(self,selfkey,key,tolerance): + """Sets the OnSDLNotify to be called when 'key' +SDL variable changes by 'tolerance' (if number)""" + pass + + def setTagString(self,name,tag): + """Sets the tag string for a variable""" + pass + +class ptSDLStateDataRecord: + """Basic SDL state data record class""" + def __init__(self): + """None""" + pass + + def findVar(self,name): + """Finds and returns the specified ptSimpleStateVariable""" + pass + + def getName(self): + """Returns our record's name""" + pass + + def getVarList(self): + """Returns the names of the vars we hold as a list of strings""" + pass + + def setFromDefaults(self,timeStampNow): + """Sets all our vars to their defaults""" + pass + +class ptSceneobject: + """Plasma Sceneobject class""" + def __init__(self,objKey, selfKey): + """None""" + pass + + def addKey(self,key): + """Mostly used internally. +Add another sceneobject ptKey""" + pass + + def animate(self): + """If we can animate, start animating""" + pass + + def avatarVelocity(self): + """Returns the velocity of the first attached avatar scene object""" + pass + + def fastForwardAttachedResponder(self,state): + """Fast forward the attached responder to the specified state""" + pass + + def findObject(self,name): + """Find a particular object in just the sceneobjects that are attached""" + pass + + def getKey(self): + """Get the ptKey of this sceneobject +If there are more then one attached, get the first one""" + pass + + def getLocalToParent(self): + """Returns ptMatrix44 of the local to parent transform for this sceneobject +- If there is more than one sceneobject attached, returns just the first one""" + pass + + def getLocalToWorld(self): + """Returns ptMatrix44 of the local to world transform for this sceneobject +- If there is more than one sceneobject attached, returns just the first one""" + pass + + def getName(self): + """Returns the name of the sceneobject (Max name) +- If there are more than one sceneobject attached, return just the first one""" + pass + + def getParentToLocal(self): + """Returns ptMatrix44 of the parent to local transform for this sceneobject +- If there is more than one sceneobject attached, returns just the first one""" + pass + + def getPythonMods(self): + """Returns list of ptKeys of the python modifiers attached to this sceneobject""" + pass + + def getResponderState(self): + """Return the responder state (if we are a responder)""" + pass + + def getResponders(self): + """Returns list of ptKeys of the responders attached to this sceneobject""" + pass + + def getSoundIndex(self,sndComponentName): + """Get the index of the requested sound component""" + pass + + def getWorldToLocal(self): + """Returns ptMatrix44 of the world to local transform for this sceneobject +- If there is more than one sceneobject attached, returns just the first one""" + pass + + def isAvatar(self): + """Returns true if the scene object is an avatar""" + pass + + def isHuman(self): + """Returns true if the scene object is a human avatar""" + pass + + def isLocallyOwned(self): + """Returns true(1) if this object is locally owned by this client +or returns false(0) if it is not or don't know""" + pass + + def netForce(self,forceFlag): + """Specify whether this object needs to use messages that are forced to the network +- This is to be used if your Python program is running on only one client +Such as a game master, only running on the client that owns a particular object +- Setting the netForce flag on a sceneobject will also set the netForce flag on +its draw, physics, avatar, particle objects""" + pass + + def playAnimNamed(self,animName): + """Play the attached named animation""" + pass + + def popCamera(self,avKey): + """Pop the camera stack and go back to the previous camera""" + pass + + def popCutsceneCamera(self,avKey): + """Pop the camera stack and go back to previous camera.""" + pass + + def position(self): + """Returns the scene object's current position""" + pass + + def pushCamera(self,avKey): + """Switch to this object (if it is a camera)""" + pass + + def pushCameraCut(self,avKey): + """Switch to this object, cutting the view (if it is a camera)""" + pass + + def pushCutsceneCamera(self,cutFlag,avKey): + """Switch to this object (assuming that it is actually a camera)""" + pass + + def rewindAnimNamed(self,animName): + """Rewind the attached named animation""" + pass + + def right(self): + """Returns the scene object's current right vector""" + pass + + def runAttachedResponder(self,state): + """Run the attached responder to the specified state""" + pass + + def setSoundFilename(self,index, filename, isCompressed): + """Sets the sound attached to this sceneobject to use the specified sound file.""" + pass + + def setTransform(self,local2world,world2local): + """Set our current transforms""" + pass + + def stopAnimNamed(self,animName): + """Stop the attached named animation""" + pass + + def up(self): + """Returns the scene object's current up vector""" + pass + + def view(self): + """Returns the scene object's current view vector""" + pass + + def volumeSensorIgnoreExtraEnters(self,ignore): + """Tells the volume sensor attached to this object to ignore extra enters (default), or not (hack for garrison).""" + pass + +class ptScoreMgr: + """Game score manager""" + def __init__(self): + """None""" + pass + + def createGlobalScore(self,gameName, gameType, value): + """Creates a score and returns it""" + pass + + def createNeighborhoodScore(self,gameName, gameType, value): + """Creates a score and returns it""" + pass + + def createPlayerScore(self,gameName, gameType, value): + """Creates a score and returns it""" + pass + + def deleteScore(self,scoreId): + """Deletes the specified score""" + pass + + def getGlobalScores(self,gameName): + """Returns a list of scores associated with the specified game.""" + pass + + def getNeighborhoodScores(self,gameName): + """Returns a list of scores associated with the specified game.""" + pass + + def getPlayerScores(self,gameName): + """Returns a list of scores associated with the specified game.""" + pass + + def getRankList(self,ownerInfoId, scoreGroup, parentFolderId, gameName, timePeriod, numResults, pageNumber, sortDesc): + """Returns a list of scores and rank""" + pass + +class ptSimpleStateVariable: + """Basic SDL state data record class""" + def __init__(self): + """None""" + pass + + def getBool(self,idx=0): + """Returns a boolean variable's value""" + pass + + def getByte(self,idx=0): + """Returns a byte variable's value""" + pass + + def getDefault(self): + """Returns the variable's default""" + pass + + def getDisplayOptions(self): + """Returns the variable's display options""" + pass + + def getDouble(self,idx=0): + """Returns a double variable's value""" + pass + + def getFloat(self,idx=0): + """Returns a float variable's value""" + pass + + def getInt(self,idx=0): + """Returns an int variable's value""" + pass + + def getShort(self,idx=0): + """Returns a short variable's value""" + pass + + def getString(self,idx=0): + """Returns a string variable's value""" + pass + + def getType(self): + """Returns the variable's type""" + pass + + def isAlwaysNew(self): + """Is this variable always new?""" + pass + + def isInternal(self): + """Is this an internal variable?""" + pass + + def setBool(self,val,idx=0): + """Sets a boolean variable's value""" + pass + + def setByte(self,val,idx=0): + """Sets a byte variable's value""" + pass + + def setDouble(self,val,idx=0): + """Sets a double variable's value""" + pass + + def setFloat(self,val,idx=0): + """Sets a float variable's value""" + pass + + def setInt(self,val,idx=0): + """Sets an int variable's value""" + pass + + def setShort(self,val,idx=0): + """Sets a short variable's value""" + pass + + def setString(self,val,idx=0): + """Sets a string variable's value""" + pass + +class ptSpawnPointInfo: + """Class to hold spawn point data""" + def __init__(self,title=None,spawnPt=None): + """None""" + pass + + def getCameraStack(self): + """Returns the camera stack for this spawnpoint as a string""" + pass + + def getName(self): + """Returns the spawnpoint's name""" + pass + + def getTitle(self): + """Returns the spawnpoint's title""" + pass + + def setCameraStack(self,stack): + """Sets the spawnpoint's camera stack (as a string)""" + pass + + def setName(self,name): + """Sets the spawnpoint's name""" + pass + + def setTitle(self,title): + """Sets the spawnpoint's title""" + pass + +class ptSpawnPointInfoRef: + """Class to hold spawn point data""" + def __init__(self): + """None""" + pass + + def getCameraStack(self): + """Returns the camera stack for this spawnpoint as a string""" + pass + + def getName(self): + """Returns the spawnpoint's name""" + pass + + def getTitle(self): + """Returns the spawnpoint's title""" + pass + + def setCameraStack(self,stack): + """Sets the spawnpoint's camera stack (as a string)""" + pass + + def setName(self,name): + """Sets the spawnpoint's name""" + pass + + def setTitle(self,title): + """Sets the spawnpoint's title""" + pass + +class ptStatusLog: + """A status log class""" + def __init__(self): + """None""" + pass + + def close(self): + """Close the status log file""" + pass + + def isOpen(self): + """Returns whether the status log is currently opened""" + pass + + def open(self,logName,numLines,flags): + """Open a status log for writing to +'logname' is the name of the log file (example: special.log) +'numLines' is the number of lines to display on debug screen +'flags' is a PlasmaConstants.PtStatusLogFlags""" + pass + + def write(self,text,color=None): + """If the status log is open, write 'text' to log +'color' is the display color in debug screen""" + pass + +class ptStream: + """A basic stream class""" + def __init__(self): + """None""" + pass + + def close(self): + """Close the status log file""" + pass + + def isOpen(self): + """Returns whether the stream file is currently opened""" + pass + + def open(self,fileName,flags): + """Open a stream file for reading or writing""" + pass + + def readlines(self): + """Reads a list of strings from the file""" + pass + + def writelines(self,lines): + """Write a list of strings to the file""" + pass + +class ptSwimCurrentInterface: + """Creates a new ptSwimCurrentInterface""" + def __init__(self,key): + """None""" + pass + + def disable(self): + """UNKNOWN""" + pass + + def enable(self): + """UNKNOWN""" + pass + +class ptVault: + """Accessor class to the player's vault""" + def __init__(self): + """None""" + pass + + def addChronicleEntry(self,entryName,type,string): + """Adds an entry to the player's chronicle with a value of 'string'.""" + pass + + def amAgeCzar(self,ageInfo): + """Are we the czar (WTH is this?) of the specified age?""" + pass + + def amAgeOwner(self,ageInfo): + """Are we the owner of the specified age?""" + pass + + def amCzarOfCurrentAge(self): + """Are we the czar (WTH is this?) of the current age?""" + pass + + def amOwnerOfCurrentAge(self): + """Are we the owner of the current age?""" + pass + + def createNeighborhood(self): + """Creates a new neighborhood""" + pass + + def findChronicleEntry(self,entryName): + """Returns a ptVaultNode of type kNodeTypeChronicle of the current player's chronicle entry by entryName.""" + pass + + def findNode(self,templateNode): + """Find the node matching the template""" + pass + + def getAgeJournalsFolder(self): + """Returns a ptVaultFolderNode of the current player's age journals folder.""" + pass + + def getAgesICanVisitFolder(self): + """Returns a ptVaultFolderNode of ages I can visit""" + pass + + def getAgesIOwnFolder(self): + """Returns a ptVaultFolderNode of ages that I own""" + pass + + def getAvatarClosetFolder(self): + """Do not use. +Returns a ptVaultFolderNode of the avatars outfit in their closet.""" + pass + + def getAvatarOutfitFolder(self): + """Do not use. +Returns a ptVaultFolderNode of the avatars outfit.""" + pass + + def getBuddyListFolder(self): + """Returns a ptVaultPlayerInfoListNode of the current player's buddy list folder.""" + pass + + def getChronicleFolder(self): + """Returns a ptVaultFolderNode of the current player's chronicle folder.""" + pass + + def getGlobalInbox(self): + """Returns a ptVaultFolderNode of the global inbox folder.""" + pass + + def getIgnoreListFolder(self): + """Returns a ptVaultPlayerInfoListNode of the current player's ignore list folder.""" + pass + + def getInbox(self): + """Returns a ptVaultFolderNode of the current player's inbox folder.""" + pass + + def getInviteFolder(self): + """Returns a ptVaultFolderNode of invites""" + pass + + def getKIUsage(self): + """Returns a tuple with usage statistics of the KI (# of pics, # of text notes, # of marker games)""" + pass + + def getLinkToCity(self): + """Returns a ptVaultAgeLinkNode that will go to the city""" + pass + + def getLinkToMyNeighborhood(self): + """Returns a ptVaultAgeLinkNode that will go to my neighborhood""" + pass + + def getOwnedAgeLink(self,ageInfo): + """Returns a ptVaultAgeLinkNode to my owned age(ageInfo)""" + pass + + def getPeopleIKnowAboutFolder(self): + """Returns a ptVaultPlayerInfoListNode of the current player's people I know about (Recent) list folder.""" + pass + + def getPlayerInfo(self): + """Returns a ptVaultNode of type kNodeTypePlayerInfo of the current player""" + pass + + def getPsnlAgeSDL(self): + """Returns the personal age SDL""" + pass + + def getVisitAgeLink(self,ageInfo): + """Returns a ptVaultAgeLinkNode for a visitor to age(ageInfo)""" + pass + + def inMyNeighborhoodAge(self): + """Are we in the player's neighborhood age?""" + pass + + def inMyPersonalAge(self): + """Are we in the player's personal age?""" + pass + + def invitePlayerToAge(self,link,playerID): + """Sends an invitation to visit the age to the specified player""" + pass + + def offerLinkToPlayer(self,link,playerID): + """Offer a one-time link to the specified player""" + pass + + def registerMTStation(self,stationName,mtSpawnPoint): + """Registers this player at the specified mass-transit point""" + pass + + def registerOwnedAge(self,link): + """Registers the specified age as owned by the player""" + pass + + def registerVisitAge(self,link): + """Register this age as visitable by this player""" + pass + + def sendToDevice(self,node,deviceName): + """Sends a ptVaultNode object to an Age's device by deviceName.""" + pass + + def setAgePublic(self,ageInfo,makePublic): + """Makes the specified age public or private""" + pass + + def unInvitePlayerToAge(self,guid,playerID): + """Revokes the invitation to visit the age""" + pass + + def unRegisterOwnedAge(self,ageFilename): + """Unregisters the specified age so it's no longer owned by this player""" + pass + + def unRegisterVisitAge(self,guid): + """Unregisters the specified age so it can no longer be visited by this player""" + pass + + def updatePsnlAgeSDL(self,pyrec): + """Updates the personal age SDL to the specified data""" + pass + +class ptVaultNode: + """Vault node class""" + def __init__(self): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultFolderNode(ptVaultNode): + """Plasma vault folder node""" + def __init__(self,n=0): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def folderGetName(self): + """LEGACY +Returns the folder's name""" + pass + + def folderGetType(self): + """LEGACY +Returns the folder type (of the standard folder types)""" + pass + + def folderSetName(self,name): + """LEGACY +Set the folder name""" + pass + + def folderSetType(self,type): + """LEGACY +Set the folder type""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getFolderName(self): + """Returns the folder's name""" + pass + + def getFolderNameW(self): + """Unicode version of getFolerName""" + pass + + def getFolderType(self): + """Returns the folder type (of the standard folder types)""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setFolderName(self,name): + """Set the folder name""" + pass + + def setFolderNameW(self,name): + """Unicode version of setFolderName""" + pass + + def setFolderType(self,type): + """Set the folder type""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultAgeInfoListNode(ptVaultFolderNode): + """Plasma vault age info list node""" + def __init__(self,n=0): + """None""" + pass + + def addAge(self,ageID): + """Adds ageID to list of ages""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def folderGetName(self): + """LEGACY +Returns the folder's name""" + pass + + def folderGetType(self): + """LEGACY +Returns the folder type (of the standard folder types)""" + pass + + def folderSetName(self,name): + """LEGACY +Set the folder name""" + pass + + def folderSetType(self,type): + """LEGACY +Set the folder type""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getFolderName(self): + """Returns the folder's name""" + pass + + def getFolderNameW(self): + """Unicode version of getFolerName""" + pass + + def getFolderType(self): + """Returns the folder type (of the standard folder types)""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def hasAge(self,ageID): + """Returns whether ageID is in the list of ages""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def removeAge(self,ageID): + """Removes ageID from list of ages""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setFolderName(self,name): + """Set the folder name""" + pass + + def setFolderNameW(self,name): + """Unicode version of setFolderName""" + pass + + def setFolderType(self,type): + """Set the folder type""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultAgeInfoNode(ptVaultNode): + """Plasma vault age info node""" + def __init__(self,n=0): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def asAgeInfoStruct(self): + """Returns this ptVaultAgeInfoNode as a ptAgeInfoStruct""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def getAgeDescription(self): + """Returns the description of the age""" + pass + + def getAgeFilename(self): + """Returns the age filename""" + pass + + def getAgeID(self): + """Returns the age ID""" + pass + + def getAgeInstanceGuid(self): + """Returns the age instance guid""" + pass + + def getAgeInstanceName(self): + """Returns the instance name of the age""" + pass + + def getAgeLanguage(self): + """Returns the age's language (integer)""" + pass + + def getAgeOwnersFolder(self): + """Returns a ptVaultPlayerInfoList of the players that own this age""" + pass + + def getAgeSDL(self): + """Returns a ptVaultSDLNode of the age's SDL""" + pass + + def getAgeSequenceNumber(self): + """Returns the sequence number of this instance of the age""" + pass + + def getAgeUserDefinedName(self): + """Returns the user define part of the age name""" + pass + + def getCanVisitFolder(self): + """Returns a ptVaultPlayerInfoList of the players that can visit this age""" + pass + + def getChildAgesFolder(self): + """Returns a ptVaultFolderNode of the child ages of this age""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getCzar(self): + """Returns ptVaultPlayerInfoNode of the player that is the Czar""" + pass + + def getCzarID(self): + """Returns the ID of the age's czar""" + pass + + def getDisplayName(self): + """Returns the displayable version of the age name""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getParentAgeLink(self): + """Returns ptVaultAgeLinkNode of the age's parent age, or None if not a child age""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def isPublic(self): + """Returns whether the age is Public or Not""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setAgeDescription(self,description): + """Sets the description of the age""" + pass + + def setAgeFilename(self,fileName): + """Sets the filename""" + pass + + def setAgeID(self,ageID): + """Sets the age ID""" + pass + + def setAgeInstanceGuid(self,guid): + """Sets the age instance GUID""" + pass + + def setAgeInstanceName(self,instanceName): + """Sets the instance name""" + pass + + def setAgeLanguage(self,lang): + """Sets the age's language (integer)""" + pass + + def setAgeSequenceNumber(self,seqNumber): + """Sets the sequence number""" + pass + + def setAgeUserDefinedName(self,udname): + """Sets the user defined part of the name""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultAgeLinkNode(ptVaultNode): + """Plasma vault age link node""" + def __init__(self,n=0): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def addSpawnPoint(self,point): + """Adds the specified ptSpawnPointInfo or ptSpawnPointInfoRef""" + pass + + def asAgeLinkStruct(self): + """Returns this ptVaultAgeLinkNode as a ptAgeLinkStruct""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def getAgeInfo(self): + """Returns the ageInfo as a ptAgeInfoStruct""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getLocked(self): + """Returns whether the link is locked or not""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getSpawnPoints(self): + """Returns a list of ptSpawnPointInfo objects""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def getVolatile(self): + """Returns whether the link is volatile or not""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def hasSpawnPoint(self,spawnPtName): + """Returns true if this link has the specified spawn point""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def removeSpawnPoint(self,point): + """Removes the specified spawn point based on a ptSpawnPointInfo, ptSpawnPointInfoRef, or string""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setLocked(self,state): + """Sets whether the link is locked or not""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def setVolatile(self,state): + """Sets the state of the volitility of the link""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultChronicleNode(ptVaultNode): + """Plasma vault chronicle node""" + def __init__(self,n=0): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def chronicleGetName(self): + """LEGACY: Returns the name of the chronicle node.""" + pass + + def chronicleGetType(self): + """LEGACY: Returns the user defined type of the chronicle node.""" + pass + + def chronicleGetValue(self): + """LEGACY: Returns the value as a string of this chronicle node.""" + pass + + def chronicleSetName(self,name): + """LEGACY: Sets the name of the chronicle node.""" + pass + + def chronicleSetType(self,type): + """LEGACY: Sets this chronicle node to a user defined type.""" + pass + + def chronicleSetValue(self,value): + """LEGACY: Sets the chronicle to a value that is a string""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getEntryType(self): + """Returns the user defined type of the chronicle node.""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getName(self): + """Returns the name of the chronicle node.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def getValue(self): + """Returns the value as a string of this chronicle node.""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setEntryType(self,type): + """Sets this chronicle node to a user defined type.""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setName(self,name): + """Sets the name of the chronicle node.""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def setValue(self,value): + """Sets the chronicle to a value that is a string""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultImageNode(ptVaultNode): + """Plasma vault image node""" + def __init__(self,n=0): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getImage(self): + """Returns the image(ptImage) of this image node""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getTitle(self): + """Returns the title (caption) of this image node""" + pass + + def getTitleW(self): + """Unicode version of getTitle""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def imageGetImage(self): + """LEGACY +Returns the image(ptImage) of this image node""" + pass + + def imageGetTitle(self): + """LEGACY +Returns the title (caption) of this image node""" + pass + + def imageSetImage(self,image): + """LEGACY +Sets the image(ptImage) of this image node""" + pass + + def imageSetTitle(self,title): + """LEGACY +Sets the title (caption) of this image node""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setImage(self,image): + """Sets the image(ptImage) of this image node""" + pass + + def setImageFromBuf(self,buf): + """Sets our image from a buffer""" + pass + + def setImageFromScrShot(self): + """Grabs a screenshot and stuffs it into this node""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setTitle(self,title): + """Sets the title (caption) of this image node""" + pass + + def setTitleW(self,title): + """Unicode version of setTitle""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultMarkerGameNode(ptVaultNode): + """Plasma vault age info node""" + def __init__(self,n=0): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getGameGuid(self): + """Returns the marker game's guid""" + pass + + def getGameName(self): + """Returns the marker game's name""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setGameGuid(self,guid): + """Sets the marker game's guid""" + pass + + def setGameName(self,name): + """Sets marker game's name""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultNodeRef: + """Vault node relationship pseudo class""" + def __init__(self): + """None""" + pass + + def beenSeen(self): + """Returns true until we reimplement this""" + pass + + def getChild(self): + """Returns a ptVaultNode that is the child of this reference""" + pass + + def getChildID(self): + """Returns id of the child node""" + pass + + def getParent(self): + """Returns a ptVaultNode that is the parent of the reference""" + pass + + def getParentID(self): + """Returns id of the parent node""" + pass + + def getSaver(self): + """Returns a ptVaultPlayerInfoNode of player that created this relationship""" + pass + + def getSaverID(self): + """Returns id of player that created this relationship""" + pass + + def setSeen(self): + """Does nothing until we reimplement this""" + pass + +class ptVaultPlayerInfoListNode(ptVaultFolderNode): + """Plasma vault player info list node""" + def __init__(self,n=0): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def addPlayer(self,playerID): + """Adds playerID player to this player info list node.""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def folderGetName(self): + """LEGACY +Returns the folder's name""" + pass + + def folderGetType(self): + """LEGACY +Returns the folder type (of the standard folder types)""" + pass + + def folderSetName(self,name): + """LEGACY +Set the folder name""" + pass + + def folderSetType(self,type): + """LEGACY +Set the folder type""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getFolderName(self): + """Returns the folder's name""" + pass + + def getFolderNameW(self): + """Unicode version of getFolerName""" + pass + + def getFolderType(self): + """Returns the folder type (of the standard folder types)""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getPlayer(self,playerID): + """Gets the player info node for the specified player.""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def hasPlayer(self,playerID): + """Returns whether the 'playerID' is a member of this player info list node.""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def playerlistAddPlayer(self,playerID): + """LEGACY: Adds playerID player to this player info list node.""" + pass + + def playerlistGetPlayer(self,playerID): + """LEGACY: Gets the player info node for the specified player.""" + pass + + def playerlistHasPlayer(self,playerID): + """LEGACY: Returns whether the 'playerID' is a member of this player info list node.""" + pass + + def playerlistRemovePlayer(self,playerID): + """LEGACY: Removes playerID player from this player info list node.""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def removePlayer(self,playerID): + """Removes playerID player from this player info list node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setFolderName(self,name): + """Set the folder name""" + pass + + def setFolderNameW(self,name): + """Unicode version of setFolderName""" + pass + + def setFolderType(self,type): + """Set the folder type""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def sort(self): + """Sorts the player list by some means...?""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultPlayerInfoNode(ptVaultNode): + """Plasma vault folder node""" + def __init__(self): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def playerGetAgeGuid(self): + """Returns the guid as a string of where the player is for this player info node.""" + pass + + def playerGetAgeInstanceName(self): + """Returns the name of the Age where the player is for this player info node.""" + pass + + def playerGetCCRLevel(self): + """Returns the ccr level of the player for this player info node.""" + pass + + def playerGetID(self): + """Returns the player ID for this player info node.""" + pass + + def playerGetName(self): + """Returns the player name of this player info node.""" + pass + + def playerIsOnline(self): + """Returns the online status of the player for this player info node.""" + pass + + def playerSetAgeGuid(self,guidString): + """Not sure this should be used. Sets the guid for this player info node.""" + pass + + def playerSetAgeInstanceName(self,name): + """Not sure this should be used. Sets the name of the age where the player is for this player info node.""" + pass + + def playerSetID(self,playerID): + """Not sure this should be used. Sets the playerID for this player info node.""" + pass + + def playerSetName(self,name): + """Not sure this should be used. Sets the player name of this player info node.""" + pass + + def playerSetOnline(self,state): + """Not sure this should be used. Sets the state of the player online status for this player info node.""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultSDLNode(ptVaultNode): + """Plasma vault SDL node""" + def __init__(self): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getIdent(self): + """UNKNOWN""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getStateDataRecord(self): + """Returns the ptSDLStateDataRecord associated with this node""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def initStateDataRecord(self,filename,flags): + """Read the SDL Rec from File if needed""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setIdent(self,v): + """UNKNOWN""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setStateDataRecord(self,rec,writeOptions=0): + """Sets the ptSDLStateDataRecord""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultSystemNode(ptVaultNode): + """Plasma vault system node""" + def __init__(self): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getType(self): + """Returns the type of ptVaultNode this is. +See PlasmaVaultTypes.py""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setType(self,type): + """Set the type of ptVaultNode this is.""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVaultTextNoteNode(ptVaultNode): + """Plasma vault text note node""" + def __init__(self): + """None""" + pass + + def addNode(self,node,cb=None,cbContext=0): + """Adds 'node'(ptVaultNode) as a child to this node.""" + pass + + def findNode(self,templateNode): + """Returns ptVaultNode if child node found matching template, or None""" + pass + + def getChildNodeCount(self): + """Returns how many children this node has.""" + pass + + def getChildNodeRefList(self): + """Returns a list of ptVaultNodeRef that are the children of this node.""" + pass + + def getClientID(self): + """Returns the client's ID.""" + pass + + def getCreateAgeCoords(self): + """Returns the location in the Age where this node was created.""" + pass + + def getCreateAgeGuid(self): + """Returns the guid as a string of the Age where this node was created.""" + pass + + def getCreateAgeName(self): + """Returns the name of the Age where this node was created.""" + pass + + def getCreateAgeTime(self): + """Returns the time in the Age that the node was created...(?)""" + pass + + def getCreateTime(self): + """Returns the when this node was created, that is useable by python's time library.""" + pass + + def getCreatorNode(self): + """Returns the creator's node""" + pass + + def getCreatorNodeID(self): + """Returns the creator's node ID""" + pass + + def getDeviceInbox(self): + """Returns a ptVaultFolderNode""" + pass + + def getID(self): + """Returns the unique ID of this ptVaultNode.""" + pass + + def getModifyTime(self): + """Returns the modified time of this node, that is useable by python's time library.""" + pass + + def getNode(self,id): + """Returns ptVaultNodeRef if is a child node, or None""" + pass + + def getOwnerNode(self): + """Returns a ptVaultNode of the owner of this node""" + pass + + def getOwnerNodeID(self): + """Returns the node ID of the owner of this node""" + pass + + def getSubType(self): + """Returns the subtype of this text note node.""" + pass + + def getText(self): + """Returns the text of this text note node.""" + pass + + def getTextW(self): + """Unicode version of getText.""" + pass + + def getTitle(self): + """Returns the title of this text note node.""" + pass + + def getTitleW(self): + """Unicode version of getTitle""" + pass + + def getType(self): + """Returns the type of text note for this text note node.""" + pass + + def hasNode(self,id): + """Returns true if node if a child node""" + pass + + def linkToNode(self,nodeID,cb=None,cbContext=0): + """Adds a link to the node designated by nodeID""" + pass + + def noteGetSubType(self): + """LEGACY +Returns the subtype of this text note node.""" + pass + + def noteGetText(self): + """LEGACY +Returns the text of this text note node.""" + pass + + def noteGetTitle(self): + """LEGACY +Returns the title of this text note node.""" + pass + + def noteGetType(self): + """LEGACY +Returns the type of text note for this text note node.""" + pass + + def noteSetSubType(self,subType): + """LEGACY +Sets the subtype of the this text note node.""" + pass + + def noteSetText(self,text): + """LEGACY +Sets text of the this text note node.""" + pass + + def noteSetTitle(self,title): + """LEGACY +Sets the title of this text note node.""" + pass + + def noteSetType(self,type): + """LEGACY +Sets the type of text note for this text note node.""" + pass + + def removeAllNodes(self): + """Removes all the child nodes on this node.""" + pass + + def removeNode(self,node,cb=None,cbContext=0): + """Removes the child 'node'(ptVaultNode) from this node.""" + pass + + def save(self,cb=None,cbContext=0): + """Save the changes made to this node.""" + pass + + def saveAll(self,cb=None,cbContext=0): + """Saves this node and all its children nodes.""" + pass + + def sendTo(self,destID,cb=None,cbContext=0): + """Send this node to inbox at 'destID'""" + pass + + def setCreateAgeGuid(self,guid): + """Set guid as a string of the Age where this node was created.""" + pass + + def setCreateAgeName(self,name): + """Set name of the Age where this node was created.""" + pass + + def setCreatorNodeID(self,id): + """Set creator's node ID""" + pass + + def setDeviceInbox(self,inboxName,cb=None,cbContext=0): + """Sets the device inbox""" + pass + + def setID(self,id): + """Sets ID of this ptVaultNode.""" + pass + + def setOwnerNodeID(self,id): + """Set node ID of the owner of this node""" + pass + + def setSubType(self,subType): + """Sets the subtype of the this text note node.""" + pass + + def setText(self,text): + """Sets text of the this text note node.""" + pass + + def setTextW(self,text): + """Unicode version of setText""" + pass + + def setTitle(self,title): + """Sets the title of this text note node.""" + pass + + def setTitleW(self,title): + """Unicode version of setTitle""" + pass + + def setType(self,type): + """Sets the type of text note for this text note node.""" + pass + + def upcastToAgeInfoListNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoListNode""" + pass + + def upcastToAgeInfoNode(self): + """Returns this ptVaultNode as ptVaultAgeInfoNode""" + pass + + def upcastToAgeLinkNode(self): + """Returns this ptVaultNode as ptVaultAgeLinkNode""" + pass + + def upcastToChronicleNode(self): + """Returns this ptVaultNode as ptVaultChronicleNode""" + pass + + def upcastToFolderNode(self): + """Returns this ptVaultNode as ptVaultFolderNode""" + pass + + def upcastToImageNode(self): + """Returns this ptVaultNode as ptVaultImageNode""" + pass + + def upcastToMarkerGameNode(self): + """Returns this ptVaultNode as ptVaultMarkerNode""" + pass + + def upcastToPlayerInfoListNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoListNode""" + pass + + def upcastToPlayerInfoNode(self): + """Returns this ptVaultNode as ptVaultPlayerInfoNode""" + pass + + def upcastToPlayerNode(self): + """Returns this ptVaultNode as a ptVaultPlayerNode""" + pass + + def upcastToSDLNode(self): + """Returns this ptVaultNode as a ptVaultSDLNode""" + pass + + def upcastToSystemNode(self): + """Returns this ptVaultNode as a ptVaultSystemNode""" + pass + + def upcastToTextNoteNode(self): + """Returns this ptVaultNode as ptVaultTextNoteNode""" + pass + +class ptVector3: + """Plasma Point class""" + def __init__(self,x=0, y=0, z=0): + """None""" + pass + + def add(self,other): + """Adds other to the current vector""" + pass + + def copy(self): + """Copies the vector into another one (which it returns)""" + pass + + def crossProduct(self,other): + """Finds the cross product between other and this vector""" + pass + + def dotProduct(self,other): + """Finds the dot product between other and this vector""" + pass + + def getX(self): + """Returns the 'x' component of the vector""" + pass + + def getY(self): + """Returns the 'y' component of the vector""" + pass + + def getZ(self): + """Returns the 'z' component of the vector""" + pass + + def length(self): + """Returns the length of the vector""" + pass + + def lengthSq(self): + """Returns the length of the vector, squared +- this function is faster then length(other)""" + pass + + def normalize(self): + """Normalizes the vector to length 1""" + pass + + def scale(self,scale): + """Scale the vector by scale""" + pass + + def setX(self,x): + """Sets the 'x' component of the vector""" + pass + + def setY(self,y): + """Sets the 'y' component of the vector""" + pass + + def setZ(self,z): + """Sets the 'z' component of the vector""" + pass + + def subtract(self,other): + """Subtracts other from the current vector""" + pass + + def zero(self): + """Zeros the vector's components""" + pass + +class ptWaveSet: + """Creates a new ptWaveSet""" + def __init__(self,ey): + """None""" + pass + + def getDepthFalloff(self): + """Returns the attribute's value""" + pass + + def getEnvCenter(self): + """Returns the attribute's value""" + pass + + def getEnvRadius(self): + """Returns the attribute's value""" + pass + + def getGeoAmpOverLen(self): + """Returns the attribute's value""" + pass + + def getGeoAngleDev(self): + """Returns the attribute's value""" + pass + + def getGeoChop(self): + """Returns the attribute's value""" + pass + + def getGeoMaxLength(self): + """Returns the attribute's value""" + pass + + def getGeoMinLength(self): + """Returns the attribute's value""" + pass + + def getMaxAtten(self): + """Returns the attribute's value""" + pass + + def getMinAtten(self): + """Returns the attribute's value""" + pass + + def getOpacFalloff(self): + """Returns the attribute's value""" + pass + + def getOpacOffset(self): + """Returns the attribute's value""" + pass + + def getReflFalloff(self): + """Returns the attribute's value""" + pass + + def getReflOffset(self): + """Returns the attribute's value""" + pass + + def getRippleScale(self): + """Returns the attribute's value""" + pass + + def getSpecularEnd(self): + """Returns the attribute's value""" + pass + + def getSpecularMute(self): + """Returns the attribute's value""" + pass + + def getSpecularNoise(self): + """Returns the attribute's value""" + pass + + def getSpecularStart(self): + """Returns the attribute's value""" + pass + + def getSpecularTint(self): + """Returns the attribute's value""" + pass + + def getTexAmpOverLen(self): + """Returns the attribute's value""" + pass + + def getTexAngleDev(self): + """Returns the attribute's value""" + pass + + def getTexChop(self): + """Returns the attribute's value""" + pass + + def getTexMaxLength(self): + """Returns the attribute's value""" + pass + + def getTexMinLength(self): + """Returns the attribute's value""" + pass + + def getWaterHeight(self): + """Returns the attribute's value""" + pass + + def getWaterOffset(self): + """Returns the attribute's value""" + pass + + def getWaterOpacity(self): + """Returns the attribute's value""" + pass + + def getWaterTint(self): + """Returns the attribute's value""" + pass + + def getWaveFalloff(self): + """Returns the attribute's value""" + pass + + def getWaveOffset(self): + """Returns the attribute's value""" + pass + + def getWindDir(self): + """Returns the attribute's value""" + pass + + def setDepthFalloff(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setEnvCenter(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setEnvRadius(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setGeoAmpOverLen(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setGeoAngleDev(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setGeoChop(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setGeoMaxLength(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setGeoMinLength(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setMaxAtten(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setMinAtten(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setOpacFalloff(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setOpacOffset(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setReflFalloff(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setReflOffset(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setRippleScale(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setSpecularEnd(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setSpecularMute(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setSpecularNoise(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setSpecularStart(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setSpecularTint(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setTexAmpOverLen(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setTexAngleDev(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setTexChop(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setTexMaxLength(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setTexMinLength(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setWaterHeight(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setWaterOffset(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setWaterOpacity(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setWaterTint(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setWaveFalloff(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setWaveOffset(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + + def setWindDir(self,s, secs = 0): + """Sets the attribute to s over secs time""" + pass + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaConstants.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaConstants.py new file mode 100644 index 00000000..4e2ec11a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaConstants.py @@ -0,0 +1,256 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +class Enum: + """Enum base class""" + def __init__(self): + """None""" + pass + +class EnumValue: + """A basic enumeration value""" + def __init__(self): + """None""" + pass + +class PtAIMsgType: + """(none)""" + kUnknown = 0 + kBrainCreated = 1 + kArrivedAtGoal = 2 + +class PtAccountUpdateType: + """(none)""" + kCreatePlayer = 1 + kDeletePlayer = 2 + kUpgradePlayer = 3 + kActivePlayer = 4 + kChangePassword = 5 + +class PtBehaviorTypes: + """(none)""" + kBehaviorTypeFall = 8192 + kBehaviorTypeIdle = 32 + kBehaviorTypeWalkingJump = 2 + kBehaviorTypeSidestepRight = 4096 + kBehaviorTypeRunningJump = 4 + kBehaviorTypeLinkIn = 65536 + kBehaviorTypeAnyJump = 7 + kBehaviorTypeRunningImpact = 8 + kBehaviorTypeWalk = 64 + kBehaviorTypeRun = 128 + kBehaviorTypeTurnRight = 1024 + kBehaviorTypeWalkBack = 256 + kBehaviorTypeMovingTurnLeft = 16384 + kBehaviorTypeGroundImpact = 16 + kBehaviorTypeStandingJump = 1 + kBehaviorTypeTurnLeft = 512 + kBehaviorTypeAnyImpact = 24 + kBehaviorTypeSidestepLeft = 2048 + kBehaviorTypeMovingTurnRight = 32768 + kBehaviorTypeLinkOut = 131072 + +class PtBookEventTypes: + """(none)""" + kNotifyImageLink = 0 + kNotifyShow = 1 + kNotifyHide = 2 + kNotifyNextPage = 3 + kNotifyPreviousPage = 4 + kNotifyCheckUnchecked = 5 + kNotifyClose = 6 + +class PtBrainModes: + """(none)""" + kGeneric = 0 + kLadder = 1 + kSit = 2 + kEmote = 3 + kAFK = 4 + kNonGeneric = 5 + +class PtButtonNotifyTypes: + """(none)""" + kNotifyOnUp = 0 + kNotifyOnDown = 1 + kNotifyOnUpAndDown = 2 + +class PtCCRPetitionType: + """(none)""" + kGeneralHelp = 0 + kBug = 1 + kFeedback = 2 + kExploit = 3 + kHarass = 4 + kStuck = 5 + kTechnical = 6 + +class PtEventType: + """(none)""" + kCollision = 1 + kPicked = 2 + kControlKey = 3 + kVariable = 4 + kFacing = 5 + kContained = 6 + kActivate = 7 + kCallback = 8 + kResponderState = 9 + kMultiStage = 10 + kSpawned = 11 + kClickDrag = 12 + kOfferLinkingBook = 14 + kBook = 15 + +class PtGUIMultiLineDirection: + """(none)""" + kLineStart = 1 + kLineEnd = 2 + kBufferStart = 3 + kBufferEnd = 4 + kOneBack = 5 + kOneForward = 6 + kOneWordBack = 7 + kOneWordForward = 8 + kOneLineUp = 9 + kOneLineDown = 10 + kPageUp = 11 + kPageDown = 12 + +class PtGameScoreTypes: + """(none)""" + kFixed = 0 + kAccumulative = 1 + kAccumAllowNegative = 2 + +class PtJustify: + """(none)""" + kLeftJustify = 0 + kCenter = 1 + kRightJustify = 2 + +class PtLOSObjectType: + """(none)""" + kClickables = 0 + kCameraBlockers = 1 + kCustom = 2 + kShootable = 3 + +class PtLOSReportType: + """(none)""" + kReportHit = 0 + kReportMiss = 1 + kReportHitOrMiss = 2 + +class PtLanguage: + """(none)""" + kEnglish = 0 + kFrench = 1 + kGerman = 2 + kSpanish = 3 + kItalian = 4 + kJapanese = 5 + kNumLanguages = 6 + +class PtMarkerMsgType: + """(none)""" + kMarkerCaptured = 0 + +class PtMovieEventReason: + """(none)""" + kMovieDone = 0 + +class PtMultiStageEventType: + """(none)""" + kEnterStage = 1 + kBeginingOfLoop = 2 + kAdvanceNextStage = 3 + kRegressPrevStage = 4 + +class PtNotificationType: + """(none)""" + kActivator = 0 + kVarNotification = 1 + kNotifySelf = 2 + kResponderFF = 3 + kResponderChangeState = 4 + +class PtNotifyDataType: + """(none)""" + kNumber = 1 + kKey = 2 + +class PtSDLReadWriteOptions: + """(none)""" + kTimeStampOnRead = 16 + kDirtyOnly = 1 + kSkipNotificationInfo = 2 + kBroadcast = 4 + +class PtSDLVarType: + """(none)""" + kInt = 0 + kFloat = 1 + kBool = 2 + kString32 = 3 + kKey = 4 + kStateDescriptor = 5 + kCreatable = 6 + kDouble = 7 + kTime = 8 + kByte = 9 + kShort = 10 + kVector3 = 50 + kPoint3 = 51 + kRGB = 52 + kRGBA = 53 + kQuaternion = 54 + kNone = -1 + +class PtScoreRankGroups: + """(none)""" + kIndividual = 0 + kNeighborhood = 1 + +class PtScoreTimePeriods: + """(none)""" + kOverall = 0 + kYear = 1 + kMonth = 2 + kDay = 3 + +class PtStatusLogFlags: + """(none)""" + kDebugOutput = 32 + kFilledBackground = 1 + kAppendToLast = 2 + kDontWriteFile = 4 + kDeleteForMe = 8 + kTimestamp = 64 + kStdout = 128 + kTimeInSeconds = 256 + kAlignToTop = 16 + kTimeAsDouble = 512 + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaControlKeys.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaControlKeys.py new file mode 100644 index 00000000..11b56e69 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaControlKeys.py @@ -0,0 +1,108 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +""" +This module is the constants for the controlKeys that are is a parameter in the OnKeyEvent callback +""" + +# OnControlKeyEvent controlKey types +kKeyAction=0 +kKeyActionMouse=1 +kKeyJump=2 +kKeyMoveForward=3 +kKeyMoveBackward=4 +kKeyStrafeLeft=5 +kKeyStrafeRight=6 +kKeyMoveUp=7 +kKeyMoveDown=8 +kKeyRotateLeft=9 +kKeyRotateRight=10 +kKeyRotateUp=11 +kKeyRotateDown=12 +kKeyModFast=13 +kKeyAlwaysRun=14 +kKeyEquip=15 +kKeyDrop=16 +kKeyTurnTo=17 +kKeyDriveMode=18 +kKeyCamMoveForward=19 +kKeyCamMoveBackward=20 +kKeyCamMoveUp=21 +kKeyCamMoveDown=22 +kKeyCamMoveLeft=23 +kKeyCamMoveRight=24 +kKeyCamPanUp=25 +kKeyCamPanDown=26 +kKeyCamPanLeft=27 +kKeyCamPanRight=28 +kKeyCamMoveFast=29 +kKeyCamRotateRight=30 +kKeyCamRotateLeft=31 +kKeyCamRotateUp=32 +kKeyCamRotateDown=33 +kKeyCamRecenter=34 +kKeyCamSpeedUp=35 +kKeyCamSpeedDown=36 +kKeyCamZoomIn=37 +kKeyCamZoomOut=38 +kKeyCamConsoleMode=39 +kKeyConsoleCommand=40 +kKeyTogglePhysical=41 +kKeyPick=42 +# axis controls +kKeyMove=43 +kKeyTurn=44 +kKeyMouseX=45 +kKeyMouseY=46 +# special controls +kKeySetCursorUp=47 +kKeySetCursorDown=48 +kKeySetCursorRight=49 +kKeySetCursorLeft=50 +kKeySetCursorPoised=51 +kKeySetCursorHidden=52 +kKeySetCursorUnhidden=53 +kKeySetCursorArrow=54 +kKeySearchForPickable=55 +kKeyIncreaseMicVol=56 +kKeyDecreaseMicVol=57 +kKeyPushToTalk=58 +kKeySetThirdPersonMode=59 +kKeySetFirstPersonMode=60 +kKeySetWalkMode=61 +kKeySetFreeLook=62 +kKeySetConsoleSingle=63 +kKeySetConsoleHidden=64 +# Inventory controls +dead__kKeySetEquipedState=65 +dead__kKeyScrollUpList=66 +dead__kKeyScrollDownList=67 +dead__kKeySetInventoryActive=68 +dead__kKeySetInventoryDisActive=69 +dead__kKeyRemoveInventoryObject=70 +dead__kKeyEnableObject=71 +# Avatar emote controls +kKeyEmote=72 +kKeyExitMode=73 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaGame.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaGame.py new file mode 100644 index 00000000..8a709a95 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaGame.py @@ -0,0 +1,2615 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +def PtCreateMarkerGame(callbackKey, gameType, gameName = "", timeLimit = 0, templateId = ""): + """Creates a new Marker game with the specified callback key, game type (from PtMarkerGameTypes), time limit (in ms), and template id (guid string)""" + pass + +def PtCreateTTTGame(callbackKey, numPlayers): + """Creates a new TicTacToe game with the specified callback key and number of players (1 or 2)""" + pass + +def PtGetGameCli(gameID): + """Returns a ptGameCli associated with the specified id""" + pass + +def PtGetGameIDs(): + """Returns a list of game IDs that the player is currently joined to""" + pass + +def PtGetGameNameByTypeID(guid): + """Returns the name of the game represented by guid passed in as a string""" + pass + +def PtIsBlueSpiralGame(typeID): + """Returns true if the specifed typeID (guid as a string) is a BlueSpiral game""" + pass + +def PtIsClimbingWallGame(typeID): + """Returns true if the specifed typeID (guid as a string) is a ClimbingWall game""" + pass + +def PtIsHeekGame(typeID): + """Returns true if the specifed typeID (guid as a string) is a Heek game""" + pass + +def PtIsMarkerGame(typeID): + """Returns true if the specifed typeID (guid as a string) is a Marker game""" + pass + +def PtIsTTTGame(typeID): + """Returns true if the specifed typeID (guid as a string) is a TicTacToe game""" + pass + +def PtIsVarSyncGame(typeID): + """Returns true if the specifed typeID (guid as a string) is a VarSync game""" + pass + +def PtJoinCommonBlueSpiralGame(callbackKey, gameID): + """Joins a common BlueSpiral game with the specified ID. If one doesn't exist, it creates it""" + pass + +def PtJoinCommonClimbingWallGame(callbackKey, gameID): + """Joins a common ClimbingWall game with the specified ID. If one doesn't exist, it creates it""" + pass + +def PtJoinCommonHeekGame(callbackKey, gameID): + """Joins a common Heek game with the specified ID. If one doesn't exist, it creates it""" + pass + +def PtJoinCommonTTTGame(callbackKey, gameID, numPlayers): + """Joins a common TicTacToe game with the specified ID. If one doesn't exist, it creates it with the specified number of players""" + pass + +def PtJoinCommonVarSyncGame(callbackKey): + """Joins the common VarSync game. If one doesn't exist, it creates it""" + pass + +def PtJoinGame(callbackKey, gameID): + """Sends a join request to the specified game. Messages are sent to the callback key""" + pass + +class ptGameCliMsg: + """Message from the game server from a game""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptBlueSpiralMsg(ptGameCliMsg): + """Base class for BlueSpiral game messages""" + def __init__(self): + """None""" + pass + + def getBlueSpiralMsgType(self): + """Returns the type of the BlueSpiral message (see PtBlueSpiralMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalBlueSpiralMsg(self): + """Returns this message as the BlueSpiral message it really is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptBlueSpiralClothOrderMsg(ptBlueSpiralMsg): + """BlueSpiral message received when the game is started and the cloth order is set""" + def __init__(self): + """None""" + pass + + def getBlueSpiralMsgType(self): + """Returns the type of the BlueSpiral message (see PtBlueSpiralMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def order(self): + """Returns a list of numbers indicating the correct order to hit the clothes in""" + pass + + def upcastToFinalBlueSpiralMsg(self): + """Returns this message as the BlueSpiral message it really is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptGameCli: + """Base class for all game client interfaces""" + def __init__(self): + """None""" + pass + + def gameID(self): + """Returns the ID number for this game""" + pass + + def gameTypeID(self): + """Returns the game type ID for this game (as a guid string)""" + pass + + def invitePlayer(self,playerID): + """Invites the specified player to join the game""" + pass + + def leaveGame(self): + """Leaves this game""" + pass + + def name(self): + """Returns the name of the game""" + pass + + def playerCount(self): + """Returns the current number of players""" + pass + + def uninvitePlayer(self,playerID): + """Revokes the invitation for the specified player""" + pass + + def upcastToBlueSpiralGame(self): + """Returns this game client as a ptBlueSpiralGame""" + pass + + def upcastToClimbingWallGame(self): + """Returns this game client as a ptClimbingWallGame""" + pass + + def upcastToHeekGame(self): + """Returns this game client as a ptHeekGame""" + pass + + def upcastToMarkerGame(self): + """Returns this game client as a ptMarkerGame""" + pass + + def upcastToTTTGame(self): + """Returns this game client as a ptTTTGame""" + pass + + def upcastToVarSyncGame(self): + """Returns this game client as a ptVarSyncGame""" + pass + +class ptBlueSpiralGame(ptGameCli): + """Game client for the BlueSpiral game""" + def __init__(self): + """None""" + pass + + def gameID(self): + """Returns the ID number for this game""" + pass + + def gameTypeID(self): + """Returns the game type ID for this game (as a guid string)""" + pass + + def hitCloth(self,clothNum): + """Tells the server you hit the specified cloth""" + pass + + def invitePlayer(self,playerID): + """Invites the specified player to join the game""" + pass + + def leaveGame(self): + """Leaves this game""" + pass + + def name(self): + """Returns the name of the game""" + pass + + def playerCount(self): + """Returns the current number of players""" + pass + + def startGame(self): + """Starts a new game""" + pass + + def uninvitePlayer(self,playerID): + """Revokes the invitation for the specified player""" + pass + + def upcastToBlueSpiralGame(self): + """Returns this game client as a ptBlueSpiralGame""" + pass + + def upcastToClimbingWallGame(self): + """Returns this game client as a ptClimbingWallGame""" + pass + + def upcastToHeekGame(self): + """Returns this game client as a ptHeekGame""" + pass + + def upcastToMarkerGame(self): + """Returns this game client as a ptMarkerGame""" + pass + + def upcastToTTTGame(self): + """Returns this game client as a ptTTTGame""" + pass + + def upcastToVarSyncGame(self): + """Returns this game client as a ptVarSyncGame""" + pass + +class ptBlueSpiralGameOverMsg(ptBlueSpiralMsg): + """BlueSpiral message received when the timer runs out, someone hits the wrong cloth, or the game is restarted (before a game start msg in that last case)""" + def __init__(self): + """None""" + pass + + def getBlueSpiralMsgType(self): + """Returns the type of the BlueSpiral message (see PtBlueSpiralMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalBlueSpiralMsg(self): + """Returns this message as the BlueSpiral message it really is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptBlueSpiralGameStartedMsg(ptBlueSpiralMsg): + """BlueSpiral message received when someone starts the game (or when you join a game that is running)""" + def __init__(self): + """None""" + pass + + def getBlueSpiralMsgType(self): + """Returns the type of the BlueSpiral message (see PtBlueSpiralMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def startSpin(self): + """Returns true if you are supposed to start spinning the door thingy""" + pass + + def upcastToFinalBlueSpiralMsg(self): + """Returns this message as the BlueSpiral message it really is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptBlueSpiralGameWonMsg(ptBlueSpiralMsg): + """BlueSpiral message received when the last cloth is successfully hit""" + def __init__(self): + """None""" + pass + + def getBlueSpiralMsgType(self): + """Returns the type of the BlueSpiral message (see PtBlueSpiralMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalBlueSpiralMsg(self): + """Returns this message as the BlueSpiral message it really is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptBlueSpiralSuccessfulHitMsg(ptBlueSpiralMsg): + """BlueSpiral message received when a cloth is hit in the correct order""" + def __init__(self): + """None""" + pass + + def getBlueSpiralMsgType(self): + """Returns the type of the BlueSpiral message (see PtBlueSpiralMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalBlueSpiralMsg(self): + """Returns this message as the BlueSpiral message it really is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptClimbingWallMsg(ptGameCliMsg): + """Base class for ClimbingWall game messages""" + def __init__(self): + """None""" + pass + + def getClimbingWallMsgType(self): + """Returns the type of the ClimbingWall message (see PtClimbingWallMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalClimbingWallMsg(self): + """Returns this message as the ClimbingWall msg it is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptClimbingWallBlockersChangedMsg(ptClimbingWallMsg): + """ClimbingWall message received when the blocker state changes""" + def __init__(self): + """None""" + pass + + def blockersSet(self): + """Returns an array of blocker indicies denoting which blockers are set""" + pass + + def getClimbingWallMsgType(self): + """Returns the type of the ClimbingWall message (see PtClimbingWallMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def localOnly(self): + """Returns true if we are only supposed to adjust our stuff locally, and not net-prop it""" + pass + + def teamNumber(self): + """The team that this message is for""" + pass + + def upcastToFinalClimbingWallMsg(self): + """Returns this message as the ClimbingWall msg it is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptClimbingWallGame(ptGameCli): + """Game client for the ClimbingWall game""" + def __init__(self): + """None""" + pass + + def changeBlocker(self,teamNumber, blockerNumber, added): + """Changes the specified marker's state for the specified team""" + pass + + def changeNumBlockers(self,amountToAdjust): + """Adjusts the number of blockers we are playing with""" + pass + + def finishedGame(self): + """Tells the server you reached the top of the wall""" + pass + + def gameID(self): + """Returns the ID number for this game""" + pass + + def gameTypeID(self): + """Returns the game type ID for this game (as a guid string)""" + pass + + def invitePlayer(self,playerID): + """Invites the specified player to join the game""" + pass + + def leaveGame(self): + """Leaves this game""" + pass + + def name(self): + """Returns the name of the game""" + pass + + def panic(self): + """Tells the server you are panicking and want your blockers reset""" + pass + + def playerCount(self): + """Returns the current number of players""" + pass + + def playerEntered(self,teamNumber): + """Tells the server that you are trying to play the game for the specified team""" + pass + + def ready(self,readyType, teamNumber): + """Marks the specified team as ready for the specified type (See PtClimbingWallReadyTypes)""" + pass + + def reset(self): + """Attempts to reset the game's control panel""" + pass + + def uninvitePlayer(self,playerID): + """Revokes the invitation for the specified player""" + pass + + def upcastToBlueSpiralGame(self): + """Returns this game client as a ptBlueSpiralGame""" + pass + + def upcastToClimbingWallGame(self): + """Returns this game client as a ptClimbingWallGame""" + pass + + def upcastToHeekGame(self): + """Returns this game client as a ptHeekGame""" + pass + + def upcastToMarkerGame(self): + """Returns this game client as a ptMarkerGame""" + pass + + def upcastToTTTGame(self): + """Returns this game client as a ptTTTGame""" + pass + + def upcastToVarSyncGame(self): + """Returns this game client as a ptVarSyncGame""" + pass + +class ptClimbingWallGameOverMsg(ptClimbingWallMsg): + """ClimbingWall message received when the game is over""" + def __init__(self): + """None""" + pass + + def getClimbingWallMsgType(self): + """Returns the type of the ClimbingWall message (see PtClimbingWallMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def localOnly(self): + """Returns true if we are only supposed to adjust our stuff locally, and not net-prop it""" + pass + + def team1Blockers(self): + """Returns an array of blocker indicies denoting which blockers team 1 set""" + pass + + def team2Blockers(self): + """Returns an array of blocker indicies denoting which blockers team 2 set""" + pass + + def teamWon(self): + """The team that won the game""" + pass + + def upcastToFinalClimbingWallMsg(self): + """Returns this message as the ClimbingWall msg it is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptClimbingWallNumBlockersChangedMsg(ptClimbingWallMsg): + """ClimbingWall message received when the number of blockers is changed""" + def __init__(self): + """None""" + pass + + def getClimbingWallMsgType(self): + """Returns the type of the ClimbingWall message (see PtClimbingWallMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def localOnly(self): + """Returns true if we are only supposed to adjust our stuff locally, and not net-prop it""" + pass + + def newBlockerCount(self): + """Returns the number of blockers this game is current running with""" + pass + + def upcastToFinalClimbingWallMsg(self): + """Returns this message as the ClimbingWall msg it is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptClimbingWallPlayerEnteredMsg(ptClimbingWallMsg): + """ClimbingWall message received when you successfully enter the suit machine""" + def __init__(self): + """None""" + pass + + def getClimbingWallMsgType(self): + """Returns the type of the ClimbingWall message (see PtClimbingWallMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalClimbingWallMsg(self): + """Returns this message as the ClimbingWall msg it is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptClimbingWallReadyMsg(ptClimbingWallMsg): + """ClimbingWall message received when the ready state of the teams is changed""" + def __init__(self): + """None""" + pass + + def getClimbingWallMsgType(self): + """Returns the type of the ClimbingWall message (see PtClimbingWallMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def localOnly(self): + """Returns true if we are only supposed to adjust our stuff locally, and not net-prop it""" + pass + + def readyType(self): + """The type of ready message this represents (see PtClimbingWallReadyTypes)""" + pass + + def team1Ready(self): + """Whether team 1 is ready or not""" + pass + + def team2Ready(self): + """Whether team 2 is ready or not""" + pass + + def upcastToFinalClimbingWallMsg(self): + """Returns this message as the ClimbingWall msg it is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptClimbingWallSuitMachineLockedMsg(ptClimbingWallMsg): + """ClimbingWall message received when the locked state of the suit machines is changed""" + def __init__(self): + """None""" + pass + + def getClimbingWallMsgType(self): + """Returns the type of the ClimbingWall message (see PtClimbingWallMsgTypes)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def localOnly(self): + """Returns true if we are only supposed to adjust our stuff locally, and not net-prop it""" + pass + + def team1MachineLocked(self): + """Whether team 1's suit machine is locked or not""" + pass + + def team2MachineLocked(self): + """Whether team 2's suit machine is locked or not""" + pass + + def upcastToFinalClimbingWallMsg(self): + """Returns this message as the ClimbingWall msg it is""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptGameCliInviteFailedMsg(ptGameCliMsg): + """Game client message when an invite failed message is received""" + def __init__(self): + """None""" + pass + + def error(self): + """Returns the error value (See PtGameCliInviteErrors)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def inviteeID(self): + """Returns the invitee's ID number""" + pass + + def operationID(self): + """Returns the operation's ID number""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptGameCliOwnerChangeMsg(ptGameCliMsg): + """Game client message when a owner change message is received""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def ownerID(self): + """Returns the owner's ID number""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptGameCliPlayerJoinedMsg(ptGameCliMsg): + """Game client message when a player joined message is received""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def playerID(self): + """Returns the player's ID number""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptGameCliPlayerLeftMsg(ptGameCliMsg): + """Game client message when a player left message is received""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def playerID(self): + """Returns the player's ID number""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptGameMgrMsg: + """Message from the game manager""" + def __init__(self): + """None""" + pass + + def getType(self): + """Returns the type of the message (see PtGameMgrMsgTypes)""" + pass + + def upcastToInviteReceivedMsg(self): + """Returns this message as a ptGameMgrInviteReceivedMsg""" + pass + + def upcastToInviteRevokedMsg(self): + """Returns this message as a ptGameMgrInviteRevokedMsg""" + pass + +class ptGameMgrInviteReceivedMsg(ptGameMgrMsg): + """Game manager message when an invite is received""" + def __init__(self): + """None""" + pass + + def gameTypeID(self): + """Returns the game type ID (as a guid string)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameMgrMsgTypes)""" + pass + + def inviterID(self): + """Returns the inviter's ID number""" + pass + + def newGameID(self): + """Returns the new game's ID number""" + pass + + def upcastToInviteReceivedMsg(self): + """Returns this message as a ptGameMgrInviteReceivedMsg""" + pass + + def upcastToInviteRevokedMsg(self): + """Returns this message as a ptGameMgrInviteRevokedMsg""" + pass + +class ptGameMgrInviteRevokedMsg(ptGameMgrMsg): + """Game manager message when an invite is received""" + def __init__(self): + """None""" + pass + + def gameTypeID(self): + """Returns the game type ID (as a guid string)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameMgrMsgTypes)""" + pass + + def inviterID(self): + """Returns the inviter's ID number""" + pass + + def newGameID(self): + """Returns the new game's ID number""" + pass + + def upcastToInviteReceivedMsg(self): + """Returns this message as a ptGameMgrInviteReceivedMsg""" + pass + + def upcastToInviteRevokedMsg(self): + """Returns this message as a ptGameMgrInviteRevokedMsg""" + pass + +class ptHeekMsg(ptGameCliMsg): + """Base class for Heek game messages""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptHeekCountdownStateMsg(ptHeekMsg): + """Heek message received by game admin when the countdown state needs to change""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def state(self): + """Returns state the countdown should be switched to (see PtHeekCountdownStates)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptHeekDropMsg(ptHeekMsg): + """Heek message received when another player's position needs to be reset/modified""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def position(self): + """Returns player position to cleanup and dump""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptHeekGame(ptGameCli): + """Game client for the Heek game""" + def __init__(self): + """None""" + pass + + def choose(self,choice): + """Makes the specified move (see PtHeekGameChoice)""" + pass + + def gameID(self): + """Returns the ID number for this game""" + pass + + def gameTypeID(self): + """Returns the game type ID for this game (as a guid string)""" + pass + + def invitePlayer(self,playerID): + """Invites the specified player to join the game""" + pass + + def leaveGame(self): + """Leaves this game (puts us into "observer" mode""" + pass + + def name(self): + """Returns the name of the game""" + pass + + def playGame(self,position, points, name): + """Requests to start playing the game in the specified position""" + pass + + def playerCount(self): + """Returns the current number of players""" + pass + + def sequenceFinished(self,sequence): + """Tells the server that the specified animation finished (see PtHeekGameSeq)""" + pass + + def uninvitePlayer(self,playerID): + """Revokes the invitation for the specified player""" + pass + + def upcastToBlueSpiralGame(self): + """Returns this game client as a ptBlueSpiralGame""" + pass + + def upcastToClimbingWallGame(self): + """Returns this game client as a ptClimbingWallGame""" + pass + + def upcastToHeekGame(self): + """Returns this game client as a ptHeekGame""" + pass + + def upcastToMarkerGame(self): + """Returns this game client as a ptMarkerGame""" + pass + + def upcastToTTTGame(self): + """Returns this game client as a ptTTTGame""" + pass + + def upcastToVarSyncGame(self): + """Returns this game client as a ptVarSyncGame""" + pass + +class ptHeekGameWinMsg(ptHeekMsg): + """Heek message received by game admin when a game is won""" + def __init__(self): + """None""" + pass + + def choice(self): + """Returns the choice that won (see PtHeekGameChoice)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptHeekGoodbyeMsg(ptHeekMsg): + """Heek message received when the server processes leave request""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptHeekInterfaceStateMsg(ptHeekMsg): + """Heek message received when your interface buttons need to enable or disable""" + def __init__(self): + """None""" + pass + + def buttonsEnabled(self): + """Returns whether your buttons should be enabled""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptHeekLightStateMsg(ptHeekMsg): + """Heek message received when one of your local lights needs to change state""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def lightNum(self): + """Returns the index of the light this refers to""" + pass + + def state(self): + """Returns state the light should be switched to (see PtHeekLightStates)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptHeekPlayGameMsg(ptHeekMsg): + """Heek message received when the server processes your play game request""" + def __init__(self): + """None""" + pass + + def enableButtons(self): + """Returns true if we should enable the buttons at the place we sat down""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def isPlaying(self): + """Returns true if the server accepted the play game request""" + pass + + def isSinglePlayer(self): + """Returns true if you are the only player at the table""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptHeekPointUpdateMsg(ptHeekMsg): + """Heek message received when the number of points you have needs to be changed""" + def __init__(self): + """None""" + pass + + def displayUpdate(self): + """Returns whether you should display a message to the user""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def points(self): + """Returns your new amount of points""" + pass + + def rank(self): + """Returns your new rank""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptHeekSetupMsg(ptHeekMsg): + """Heek message for setting up each position's state""" + def __init__(self): + """None""" + pass + + def buttonState(self): + """Returns whether the buttons are enabled or not""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def lightOn(self): + """Returns a list of bools representing lights on or off""" + pass + + def position(self): + """Returns the position this message is for""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptHeekWelcomeMsg(ptHeekMsg): + """Heek message received when a new player sits down""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def name(self): + """Returns the new player's name""" + pass + + def points(self): + """Returns the new player's points""" + pass + + def rank(self): + """Returns the new player's rank""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptHeekWinLoseMsg(ptHeekMsg): + """Heek message received when the round is over and you won or lost""" + def __init__(self): + """None""" + pass + + def choice(self): + """Returns the choice that won or lost (see PtHeekGameChoice)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getHeekMsgType(self): + """Returns the type of the Heek message (see PtHeekMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalHeekMsg(self): + """Returns this message as the Heek message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + + def win(self): + """Returns true if you won""" + pass + +class ptMarkerGame(ptGameCli): + """Game client for the Marker game""" + def __init__(self): + """None""" + pass + + def addMarker(self,x, y, z, name = "", age = ""): + """Adds a marker to the game. Age is ignored in a non-quest game. Won't work if you're not the owner/creator""" + pass + + def captureMarker(self,markerId): + """Captures the specified marker""" + pass + + def changeGameName(self,newName): + """Changes the name of the game. Won't work if you're not the game owner/creator""" + pass + + def changeMarkerName(self,markerId, newName): + """Changes the name of the specified marker. Won't work if you're not the game owner/creator""" + pass + + def changeTimeLimit(self,newTimeLimit): + """Changes the time limit on the game (in ms). Won't work if you're not the game owner/creator, or if it's a quest game""" + pass + + def deleteGame(self): + """Tells the server to delete the game. Won't work if you're not the game owner/creator""" + pass + + def deleteMarker(self,markerId): + """Deletes the specified marker from the game. Won't work if you're not the game owner/creator""" + pass + + def gameID(self): + """Returns the ID number for this game""" + pass + + def gameTypeID(self): + """Returns the game type ID for this game (as a guid string)""" + pass + + def invitePlayer(self,playerID): + """Invites the specified player to join the game""" + pass + + def leaveGame(self): + """Leaves this game""" + pass + + def name(self): + """Returns the name of the game""" + pass + + def pauseGame(self): + """Pauses the game. Won't work on MP games if you're not the owner/creator""" + pass + + def playerCount(self): + """Returns the current number of players""" + pass + + def resetGame(self): + """Resets the game. Won't work on MP games if you're not the owner/creator""" + pass + + def startGame(self): + """Starts the game. Won't work on MP games if you're not the owner/creator""" + pass + + def uninvitePlayer(self,playerID): + """Revokes the invitation for the specified player""" + pass + + def upcastToBlueSpiralGame(self): + """Returns this game client as a ptBlueSpiralGame""" + pass + + def upcastToClimbingWallGame(self): + """Returns this game client as a ptClimbingWallGame""" + pass + + def upcastToHeekGame(self): + """Returns this game client as a ptHeekGame""" + pass + + def upcastToMarkerGame(self): + """Returns this game client as a ptMarkerGame""" + pass + + def upcastToTTTGame(self): + """Returns this game client as a ptTTTGame""" + pass + + def upcastToVarSyncGame(self): + """Returns this game client as a ptVarSyncGame""" + pass + +class ptMarkerMsg(ptGameCliMsg): + """Base class for Marker game messages""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerGameDeletedMsg(ptMarkerMsg): + """Marker message received when the game is deleted""" + def __init__(self): + """None""" + pass + + def failed(self): + """Returns whether the delete succeeded or not""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerGameNameChangedMsg(ptMarkerMsg): + """Marker message received when the game name is changed""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def name(self): + """Returns the new game name""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerGameOverMsg(ptMarkerMsg): + """Marker message received when the server determines the game is over (usually via timeout)""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerGamePausedMsg(ptMarkerMsg): + """Marker message received when the game is paused by the owner""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def timeLeft(self): + """Returns the amount of time left on the server clock""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerGameResetMsg(ptMarkerMsg): + """Marker message received when the game is reset by the owner""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerGameStartedMsg(ptMarkerMsg): + """Marker message received when the game is started by the owner""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerGameTypeMsg(ptMarkerMsg): + """Marker message received when you are assigned a team number""" + def __init__(self): + """None""" + pass + + def gameType(self): + """Returns the type of the game you just joined""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerMarkerAddedMsg(ptMarkerMsg): + """Marker message received when a marker is added to the game""" + def __init__(self): + """None""" + pass + + def age(self): + """Returns the age the marker was created in""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def markerId(self): + """Returns the id number of the marker""" + pass + + def name(self): + """Returns the name of the marker""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + + def x(self): + """Returns x coord of the marker""" + pass + + def y(self): + """Returns y coord of the marker""" + pass + + def z(self): + """Returns z coord of the marker""" + pass + +class ptMarkerMarkerCapturedMsg(ptMarkerMsg): + """Marker message received when a marker is captured""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def markerId(self): + """Returns id of the marker which was captured""" + pass + + def team(self): + """Returns the team number of the team that captured it (0 for no team, or a quest game)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerMarkerDeletedMsg(ptMarkerMsg): + """Marker message received when a marker is deleted""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def markerId(self): + """Returns id of the marker that was deleted""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerMarkerNameChangedMsg(ptMarkerMsg): + """Marker message received when the name of a marker is changed""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def markerId(self): + """Returns id of the marker who's name was changed""" + pass + + def name(self): + """Returns the new name""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerTeamAssignedMsg(ptMarkerMsg): + """Marker message received when you are assigned a team number""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def teamNumber(self): + """Returns the number of the team you were assigned to""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerTemplateCreatedMsg(ptMarkerMsg): + """Marker message received when a quest game template is created""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def templateID(self): + """Returns the ID number of the template that was created""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptMarkerTimeLimitChangedMsg(ptMarkerMsg): + """Marker message received when the game name is changed""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getMarkerMsgType(self): + """Returns the type of the Marker message (see PtMarkerMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def timeLimit(self): + """Returns the new time limit (in ms)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalMarkerMsg(self): + """Returns this message as the Marker message it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptTTTGame(ptGameCli): + """Game client for the TicTacToe game""" + def __init__(self): + """None""" + pass + + def gameID(self): + """Returns the ID number for this game""" + pass + + def gameTypeID(self): + """Returns the game type ID for this game (as a guid string)""" + pass + + def invitePlayer(self,playerID): + """Invites the specified player to join the game""" + pass + + def leaveGame(self): + """Leaves this game""" + pass + + def makeMove(self,row, col): + """Makes a move in the specified spot""" + pass + + def name(self): + """Returns the name of the game""" + pass + + def playerCount(self): + """Returns the current number of players""" + pass + + def showBoard(self): + """Prints the current board layout to the console""" + pass + + def uninvitePlayer(self,playerID): + """Revokes the invitation for the specified player""" + pass + + def upcastToBlueSpiralGame(self): + """Returns this game client as a ptBlueSpiralGame""" + pass + + def upcastToClimbingWallGame(self): + """Returns this game client as a ptClimbingWallGame""" + pass + + def upcastToHeekGame(self): + """Returns this game client as a ptHeekGame""" + pass + + def upcastToMarkerGame(self): + """Returns this game client as a ptMarkerGame""" + pass + + def upcastToTTTGame(self): + """Returns this game client as a ptTTTGame""" + pass + + def upcastToVarSyncGame(self): + """Returns this game client as a ptVarSyncGame""" + pass + +class ptTTTMsg(ptGameCliMsg): + """Base class for TicTacToe game messages""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getTTTMsgType(self): + """Returns the type of the TTT message (see PtTTTMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalTTTMsg(self): + """Returns this message as the TTT msg it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptTTTGameOverMsg(ptTTTMsg): + """TicTacToe message received when the game is over""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getTTTMsgType(self): + """Returns the type of the TTT message (see PtTTTMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def result(self): + """Returns the result of the game (see PtTTTGameResult)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalTTTMsg(self): + """Returns this message as the TTT msg it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + + def winnerID(self): + """Returns the winner's ID""" + pass + +class ptTTTGameStartedMsg(ptTTTMsg): + """TicTacToe message received when the game is started""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getTTTMsgType(self): + """Returns the type of the TTT message (see PtTTTMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalTTTMsg(self): + """Returns this message as the TTT msg it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + + def yourTurn(self): + """Returns true if you are the first player (and therefore it's your turn)""" + pass + +class ptTTTMoveMadeMsg(ptTTTMsg): + """TicTacToe message received when someone makes a move""" + def __init__(self): + """None""" + pass + + def col(self): + """Returns the col index of the move (1..3)""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getTTTMsgType(self): + """Returns the type of the TTT message (see PtTTTMsgTypes)""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def playerID(self): + """Returns the the ID of the player that just moved""" + pass + + def row(self): + """Returns the row index of the move (1..3)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalTTTMsg(self): + """Returns this message as the TTT msg it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptVarSyncMsg(ptGameCliMsg): + """Base class for VarSync game messages""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def getVarSyncMsgType(self): + """Returns the type of the VarSync message (see PtVarSyncMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalVarSyncMsg(self): + """Returns this message as the VarSync msg it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptVarSyncAllVarsSentMsg(ptVarSyncMsg): + """VarSync message received after the last var is sent to you when you join the game, or request a list of vars""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def getVarSyncMsgType(self): + """Returns the type of the VarSync message (see PtVarSyncMsgTypes)""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalVarSyncMsg(self): + """Returns this message as the VarSync msg it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + +class ptVarSyncGame(ptGameCli): + """Game client for the VarSync game""" + def __init__(self): + """None""" + pass + + def createNumericVar(self,varName, value): + """Attempts to create a new numeric variable and set it to the specified number (clipped to double)""" + pass + + def createStringVar(self,varName, value): + """Attempts to create a new string variable and set it to the specified string (clipped to 255 chars)""" + pass + + def gameID(self): + """Returns the ID number for this game""" + pass + + def gameTypeID(self): + """Returns the game type ID for this game (as a guid string)""" + pass + + def invitePlayer(self,playerID): + """Invites the specified player to join the game""" + pass + + def leaveGame(self): + """Leaves this game""" + pass + + def name(self): + """Returns the name of the game""" + pass + + def playerCount(self): + """Returns the current number of players""" + pass + + def requestAllVars(self): + """Requests all the vars the server knows about""" + pass + + def setNumericVar(self,varID, value): + """Attempts to set a numeric variable to the specified number (clipped to double)""" + pass + + def setStringVar(self,varID, value): + """Attempts to set a string variable to the specified string (clipped to 255 chars)""" + pass + + def uninvitePlayer(self,playerID): + """Revokes the invitation for the specified player""" + pass + + def upcastToBlueSpiralGame(self): + """Returns this game client as a ptBlueSpiralGame""" + pass + + def upcastToClimbingWallGame(self): + """Returns this game client as a ptClimbingWallGame""" + pass + + def upcastToHeekGame(self): + """Returns this game client as a ptHeekGame""" + pass + + def upcastToMarkerGame(self): + """Returns this game client as a ptMarkerGame""" + pass + + def upcastToTTTGame(self): + """Returns this game client as a ptTTTGame""" + pass + + def upcastToVarSyncGame(self): + """Returns this game client as a ptVarSyncGame""" + pass + +class ptVarSyncNumericVarChangedMsg(ptVarSyncMsg): + """VarSync message received when a numeric variable's value changes""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def getVarSyncMsgType(self): + """Returns the type of the VarSync message (see PtVarSyncMsgTypes)""" + pass + + def id(self): + """Returns the id of the var that changed""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalVarSyncMsg(self): + """Returns this message as the VarSync msg it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + + def value(self): + """Returns the variable's new value""" + pass + +class ptVarSyncNumericVarCreatedMsg(ptVarSyncMsg): + """VarSync message received when a numeric variable is created and assigned an id""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def getVarSyncMsgType(self): + """Returns the type of the VarSync message (see PtVarSyncMsgTypes)""" + pass + + def id(self): + """Returns the id assigned to this variable""" + pass + + def name(self): + """Returns the name of the var that was created""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalVarSyncMsg(self): + """Returns this message as the VarSync msg it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + + def value(self): + """Returns the variable's new value""" + pass + +class ptVarSyncStringVarChangedMsg(ptVarSyncMsg): + """VarSync message received when a string variable's value changes""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def getVarSyncMsgType(self): + """Returns the type of the VarSync message (see PtVarSyncMsgTypes)""" + pass + + def id(self): + """Returns the id of the var that changed""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalVarSyncMsg(self): + """Returns this message as the VarSync msg it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + + def value(self): + """Returns the variable's new value""" + pass + +class ptVarSyncStringVarCreatedMsg(ptVarSyncMsg): + """VarSync message received when a string variable is created and assigned an id""" + def __init__(self): + """None""" + pass + + def getGameCli(self): + """Returns the game client associated with this message""" + pass + + def getType(self): + """Returns the type of the message (see PtGameCliMsgTypes)""" + pass + + def getVarSyncMsgType(self): + """Returns the type of the VarSync message (see PtVarSyncMsgTypes)""" + pass + + def id(self): + """Returns the id that was assigned to this variable""" + pass + + def name(self): + """Returns the name of the var that was created""" + pass + + def upcastToFinalGameCliMsg(self): + """Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)""" + pass + + def upcastToFinalVarSyncMsg(self): + """Returns this message as the VarSync msg it is""" + pass + + def upcastToGameMsg(self): + """Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)""" + pass + + def value(self): + """Returns the variable's new value""" + pass + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaGameConstants.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaGameConstants.py new file mode 100644 index 00000000..064a6043 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaGameConstants.py @@ -0,0 +1,159 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +class PtBlueSpiralMsgTypes: + """(none)""" + kBlueSpiralGameStarted = 8 + kBlueSpiralClothOrder = 4 + kBlueSpiralSuccessfulHit = 5 + kBlueSpiralGameWon = 6 + kBlueSpiralGameOver = 7 + +class PtClimbingWallMsgTypes: + """(none)""" + kClimbingWallNumBlockersChanged = 4 + kClimbingWallReadyMsg = 5 + kClimbingWallBlockersChanged = 6 + kClimbingWallPlayerEntered = 7 + kClimbingWallSuitMachineLocked = 8 + kClimbingWallGameOver = 9 + +class PtClimbingWallReadyTypes: + """(none)""" + kClimbingWallReadyNumBlockers = 0 + kClimbingWallReadyBlockers = 1 + +class PtGameCliInviteErrors: + """(none)""" + kGameInviteSuccess = 0 + kGameInviteErrNotOwner = 1 + kGameInviteErrAlreadyInvited = 2 + kGameInviteErrAlreadyJoined = 3 + kGameInviteErrGameStarted = 4 + kGameInviteErrGameOver = 5 + kGameInviteErrGameFull = 6 + kGameInviteErrNoJoin = 7 + +class PtGameCliMsgTypes: + """(none)""" + kGameCliPlayerJoinedMsg = 0 + kGameCliPlayerLeftMsg = 1 + kGameCliInviteFailedMsg = 2 + kGameCliOwnerChangeMsg = 3 + kGameCliTTTMsg = 4 + kGameCliHeekMsg = 5 + kGameCliMarkerMsg = 6 + kGameCliBlueSpiralMsg = 7 + kGameCliClimbingWallMsg = 8 + kGameCliVarSyncMsg = 9 + +class PtGameMgrMsgTypes: + """(none)""" + kGameMgrInviteReceivedMsg = 1 + kGameMgrInviteRevokedMsg = 2 + +class PtHeekCountdownStates: + """(none)""" + kHeekCountdownStart = 0 + kHeekCountdownStop = 1 + kHeekCountdownIdle = 2 + +class PtHeekGameChoice: + """(none)""" + kHeekGameChoiceRock = 0 + kHeekGameChoicePaper = 1 + kHeekGameChoiceScissors = 2 + +class PtHeekGameSeq: + """(none)""" + kHeekGameSeqCountdown = 0 + kHeekGameSeqChoiceAnim = 1 + kHeekGameSeqGameWinAnim = 2 + +class PtHeekLightStates: + """(none)""" + kHeekLightOn = 0 + kHeekLightOff = 1 + kHeekLightFlash = 2 + +class PtHeekMsgTypes: + """(none)""" + kHeekPlayGame = 4 + kHeekGoodbye = 5 + kHeekWelcome = 6 + kHeekDrop = 7 + kHeekSetup = 8 + kHeekLightState = 9 + kHeekInterfaceState = 10 + kHeekCountdownState = 11 + kHeekWinLose = 12 + kHeekGameWin = 13 + kHeekPointUpdate = 14 + +class PtMarkerGameTypes: + """(none)""" + kMarkerGameQuest = 0 + kMarkerGameCGZ = 1 + kMarkerGameCapture = 2 + kMarkerGameCaptureAndHold = 3 + +class PtMarkerMsgTypes: + """(none)""" + kMarkerTemplateCreated = 4 + kMarkerTeamAssigned = 5 + kMarkerGameType = 6 + kMarkerGameStarted = 7 + kMarkerGamePaused = 8 + kMarkerGameReset = 9 + kMarkerGameOver = 10 + kMarkerGameNameChanged = 11 + kMarkerTimeLimitChanged = 12 + kMarkerGameDeleted = 13 + kMarkerMarkerAdded = 14 + kMarkerMarkerDeleted = 15 + kMarkerMarkerNameChanged = 16 + kMarkerMarkerCaptured = 17 + +class PtTTTGameResult: + """(none)""" + kTTTGameResultWinner = 0 + kTTTGameResultTied = 1 + kTTTGameResultGave = 2 + kTTTGameResultError = 3 + +class PtTTTMsgTypes: + """(none)""" + kTTTGameStarted = 4 + kTTTGameOver = 5 + kTTTMoveMade = 6 + +class PtVarSyncMsgTypes: + """(none)""" + kVarSyncNumericVarCreated = 8 + kVarSyncStringVarChanged = 4 + kVarSyncNumericVarChanged = 5 + kVarSyncAllVarsSent = 6 + kVarSyncStringVarCreated = 7 + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaKITypes.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaKITypes.py new file mode 100644 index 00000000..44f82e6d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaKITypes.py @@ -0,0 +1,469 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +""" +This module is contains the datatypes and constants for +interfacing with the KI subsytem +""" + +# OnKIMsg and PtSendKIMessage command types +kEnterChatMode=1 # start chat mode on KI, 'value' doesn't matter +kSetChatFadeDelay=2 # set the chat fade time, 'value' is the delay in seconds +kSetTextChatAdminMode=3 # set self to be admin mode, 'value' is 1 to go into Admin mode, 0 remove +kDisableKIandBB=4 # disable the KI and the blackbar, 'value' doesn't matter +kEnableKIandBB=5 # enable the KI and the blackbar, 'value' doesn't matter +kYesNoDialog=6 # request that the KI put up a yes/no dialog for someone else +kAddPlayerDevice=7 # add player interactive device to list of devices, 'value' is device name +kRemovePlayerDevice=8 # remove player interactive device from list, 'value' is device name +kUpgradeKILevel=9 # upgrade new level of KI (if already at that level, nevermind), 'value' is the new level +kDowngradeKILevel=10 # remove (drop) the KI level (if not at that level, nevermind), 'value' is the level to remove +kRateIt=11 # request for the KI to ask the user to Rate something +kSetPrivateChatChannel=12 # sets the private chat channel to number, for private rooms +kUnsetPrivateChatChannel=13 # undoes the the private chat channel. +kStartBookAlert=14 # start the book alert +kMiniBigKIToggle=15 # shortcut to toggling the miniKI/bigKI +kKIPutAway=16 # shortcut to hiding all of the KI +kChatAreaPageUp=17 # shortcut to paging up the chat area +kChatAreaPageDown=18 # shortcut to paging down the chat area +kChatAreaGoToBegin=19 # shortcut to going to the beginning of the chat area +kChatAreaGoToEnd=20 # shortcut to going to the end of the chat area +kKITakePicture=21 # shortcut to taking a picture in the KI +kKICreateJournalNote=22 # shortcut to creating a journal note in the KI +kKIToggleFade=23 # shortcut to toggle fade mode in the miniKI (only if miniKI only) +kKIToggleFadeEnable=24 # shortcut to toggling the enable flag for fading chat +kKIChatStatusMsg=25 # display a status message (net propagated) in the chat window +kKILocalChatStatusMsg=26 # display a status message (local only) in the chat window +kKIUpSizeFont=27 # up size the font in the KI (chatarea) +kKIDownSizeFont=28 # down size the font in the KI (chatarea) +kKIOpenYeehsaBook=29 # open the Yeehsa book, if not already open +kKIOpenKI=30 # open the KI a little at a time +kKIShowCCRHelp=31 # show the CCR help dialog +kKICreateMarker=32 # create a marker +kKICreateMarkerFolder=33 # create a marker folder(node) game in the current Age's journal folder +kKILocalChatErrorMsg=34 # display an error message (local only) in the chat window +kKIPhasedAllOn=35 # turn on all the phased KI functionality +kKIPhasedAllOff=36 # turn off all the phased KI functionality +kKIOKDialog=37 # display an OK dialog box (localized) +kDisableYeeshaBook=38 # don't allow linking with the Yeesha book (gameplay) +kEnableYeeshaBook=39 # re-allow linking with the Yeesha book +kQuitDialog=40 # put up Quit dialog +kTempDisableKIandBB=41 # temp disable KI and blackbar (done by av system) +kTempEnableKIandBB=42 # temp re-enable the KI and blackbar (done by av system) +kDisableEntireYeeshaBook=43 # disable the entire Yeeshabook, not for gameplay, but prevent linking +kEnableEntireYeeshaBook=44 # enable the entire Yeeshabook, not for gameplay +kKIOKDialogNoQuit=45 # display OK dialog in the KI without quiting afterwards +kGZUpdated=46 # the GZ game was updated +kGZInRange=47 # a GZ marker is in range +kGZOutRange=48 # GZ markers are out of range +kUpgradeKIMarkerLevel=49 # upgrade the KI marker level +kKIShowMiniKI=50 # force the miniKI up +kGZFlashUpdate=51 # flash update to the GZ display on the miniKI (without saving) +kStartJournalAlert = 52 # start the journal alert +kAddJournalBook = 53 # add the journal book to the BB +kRemoveJournalBook = 54 # remove the journal book from the BB +kKIOpenJournalBook = 55 # show the journal book +kMGStartCGZGame = 56 # Start CGZ Marker Game +kMGStopCGZGame = 57 # Stop CGZ Marker Game +kKICreateMarkerNode = 58 #Creates the marker game vault Node +kStartKIAlert = 59 # start the KI alert +kUpdatePelletScore = 60 # Updates the pellet score +kFriendInviteSent = 61 # Result of friend invite received +kRegisterImager = 62 # Imagers send to register themselves with the KI + +# kUpgradeKILevel and kDowngradeKILevel levels +kNanoKI=0 +kMicroKI=1 +kNormalKI=2 +kLowestKILevel = kNanoKI +kHighestKILevel = kNormalKI + +# Upgrade levels for the KI marker +kKIMarkerNotUpgraded = 0 +kKIMarkerFirstLevel = 1 # can play marker tag, but no GPS, can play first set of GZMarkers +kKIMarkerSecondLevel = 2 # can get to back room in GreatZero and play second set of GZmarkers +kKIMarkerNormalLevel = 3 # complete both GZmarker trials - has GPS + +# GZ Marker and Calibration GZ Marker status +kGZMarkerInactive = "0" +kGZMarkerAvailable = "1" +kGZMarkerCaptured = "2" +kGZMarkerUploaded = "3" + +# Calibration GZ Marker Games +kCGZMarkerInactive = "0" +kCGZMarkerAvailable = "1" +kCGZMarkerCaptured = "2" +kCGZMarkerUploaded = "3" +gCGZAllStates = [ kCGZMarkerInactive, kCGZMarkerAvailable, kCGZMarkerCaptured, kCGZMarkerUploaded ] +kCGZFirstGame = 0 +kCGZFinalGame = 3 +kCGZToransGame = 0 +kCGZHSpansGame = 1 +kCGZVSpansGame = 2 +kCGZActivateGZ = 3 + + +# -- chronicle names and types +kChronicleKILevel = "PlayerKILevel" +kChronicleKILevelType = 2 +kChronicleCensorLevel = "PlayerCensorLevel" +kChronicleCensorLevelType = 2 +kChronicleKIMarkerLevel = "KIMarkerLevel" +kChronicleKIMarkerLevelType = 2 +kChronicleGZGames = "GZGames" +kChronicleGZGamesType = 1 +kChronicleGZMarkersAquired = "GZMarkersAquired" +kChronicleGZMarkersAquiredType = 1 +kChronicleCalGZMarkersAquired = "CalGZMarkers" +kChronicleCalGZMarkersAquiredType = 1 + + +def PtDetermineKILevel(): + "Get the KILevel" + # assume that they have none... + import Plasma + import string + vault = Plasma.ptVault() + entry = vault.findChronicleEntry(kChronicleKILevel) + if type(entry) != type(None): + level = string.atoi(entry.chronicleGetValue()) + # make sure it is a valid level + if level >= kLowestKILevel and level <= kHighestKILevel: + return level + # if couldn't be determine... just assume lowest form + return kNanoKI + +def PtDetermineCensorLevel(): + "Get the KILevel" + # assume that they have none... + import Plasma + import string + vault = Plasma.ptVault() + entry = vault.findChronicleEntry(kChronicleCensorLevel) + if type(entry) != type(None): + level = string.atoi(entry.chronicleGetValue()) + return level + # if couldn't be determine... just assume lowest form + return 0 + +def PtDetermineKIMarkerLevel(): + "Get the KIMarkerLevel" + # assume that they have none... + import Plasma + import string + vault = Plasma.ptVault() + entry = vault.findChronicleEntry(kChronicleKIMarkerLevel) + if type(entry) != type(None): + level = string.atoi(entry.chronicleGetValue()) + return level + # if couldn't be determine... just assume lowest form + return kKIMarkerNotUpgraded + +def PtGetCGZGameState(whichGame): + "Get the CGZ Game level" + # assume that they have none... + import Plasma + import PlasmaTypes + if whichGame >= kCGZFirstGame and whichGame <= kCGZFinalGame: + vault = Plasma.ptVault() + entry = vault.findChronicleEntry(kChronicleCalGZMarkersAquired) + if type(entry) != type(None): + allStates = entry.chronicleGetValue() + PlasmaTypes.PtDebugPrint("PlasmaKITypes:PtGetCGZGameLevel current chronicle is %s"%(allStates),level=PlasmaTypes.kDebugDumpLevel) + state = kCGZMarkerInactive # assume inactive + try: + state = allStates[whichGame] + except LookupError: + PlasmaTypes.PtDebugPrint("PlasmaKITypes:PtGetCGZGameLevel - CGZ marker game not there? chron=%s"%(allStates),level=PlasmaTypes.kErrorLevel) + pass + return state + else: + PlasmaTypes.PtDebugPrint("PlasmaKITypes:PtGetCGZGameLevel no chronicle yet",level=PlasmaTypes.kDebugDumpLevel) + else: + PlasmaTypes.PtDebugPrint("PlasmaKITypes:PtGetCGZGameLevel - invalid CGZ game of %d"%(whichGame),level=PlasmaTypes.kErrorLevel) + pass + # if couldn't be determine... just assume lowest form + return kCGZMarkerInactive + +def PtSetCGZGameState(whichGame,state): + "Get the CGZ Game level" + # assume that they have none... + import Plasma + import PlasmaTypes + if whichGame >= kCGZFirstGame and whichGame <= kCGZFinalGame: + if type(state) == type("") and state in gCGZAllStates: + PlasmaTypes.PtDebugPrint("PlasmaKITypes:PtSetCGZGameLevel - setting game %d to %s"%(whichGame,state),level=PlasmaTypes.kDebugDumpLevel) + vault = Plasma.ptVault() + entry = vault.findChronicleEntry(kChronicleCalGZMarkersAquired) + if type(entry) != type(None): + allStates = entry.chronicleGetValue() + newStates = "" + for idx in range(kCGZFinalGame+1): + if idx == whichGame: + newStates += state + else: + try: + newStates += allStates[idx] + except LookupError: + newStates += kCGZMarkerInactive + # make sure we get whatever is beyond this + newStates += allStates[kCGZFinalGame+1:] + entry.chronicleSetValue(newStates) + entry.save() + else: + # create a new one + newStates = "" + for idx in range(kCGZFinalGame+1): + if idx == whichGame: + newStates += state + else: + newStates += kCGZMarkerInactive + vault.addChronicleEntry(kChronicleCalGZMarkersAquired,kChronicleCalGZMarkersAquiredType,newStates) + else: + PlasmaTypes.PtDebugPrint("PlasmaKITypes:PtSetCGZGameLevel - invalid CGZ game state of:",state,level=PlasmaTypes.kErrorLevel) + pass + else: + PlasmaTypes.PtDebugPrint("PlasmaKITypes:PtSetCGZGameLevel - invalid CGZ game of %d"%(whichGame),level=PlasmaTypes.kErrorLevel) + pass + +def PtWhichCGZPlaying(): + "Has the player completed the CGZ stuff" + import Plasma + whichGame = -1 + state = kCGZMarkerInactive + vault = Plasma.ptVault() + entry = vault.findChronicleEntry(kChronicleCalGZMarkersAquired) + if type(entry) != type(None): + allStates = entry.chronicleGetValue() + if len(allStates) > kCGZFinalGame: + state = kCGZMarkerUploaded + for i in range(kCGZFinalGame+1): + if allStates[i] == kCGZMarkerAvailable or allStates[i] == kCGZMarkerCaptured: + whichGame = i + state = allStates[i] + break + if allStates[i] != kCGZMarkerUploaded: + state = kCGZMarkerInactive + return (whichGame,state) + +def PtIsCGZDone(): + "Has the player completed the CGZ stuff" + import Plasma + isDone = 0 + vault = Plasma.ptVault() + entry = vault.findChronicleEntry(kChronicleCalGZMarkersAquired) + if type(entry) != type(None): + allStates = entry.chronicleGetValue() + if len(allStates) > kCGZFinalGame: + # assume that we are going to find them all + isDone = 1 + for i in range(kCGZFinalGame+1): + if allStates[i] != kCGZMarkerUploaded: + isDone = 0 + break + return isDone + +def PtDetermineGZ(): + "Get the current GZ states" + import Plasma + import PlasmaTypes + import string + GZPlaying = 0 + MarkerToGetColor = 'off' + MarkerGottenColor = 'off' + MarkerToGetNumber = 0 + MarkerGottenNumber = 0 + KIMarkerLevel = PtDetermineKIMarkerLevel() + if KIMarkerLevel > kKIMarkerNotUpgraded: + # see if they are playing a CGZ game + (whichGame,state) = PtWhichCGZPlaying() + if KIMarkerLevel < kKIMarkerNormalLevel or (KIMarkerLevel == kKIMarkerNormalLevel and whichGame != -1): + vault = Plasma.ptVault() + # is there a chronicle for the GZ games? + entry = vault.findChronicleEntry(kChronicleGZGames) + if type(entry) != type(None): + gameString = entry.chronicleGetValue() + PlasmaTypes.PtDebugPrint("PtDetermineGZ: - game string is %s" % (gameString),level=PlasmaTypes.kDebugDumpLevel) + args = gameString.split() + if len(args) == 3: + try: + GZPlaying = string.atoi(args[0]) + colors = args[1].split(':') + MarkerGottenColor = colors[0] + MarkerToGetColor = colors[1] + outof = args[2].split(':') + MarkerGottenNumber = string.atoi(outof[0]) + MarkerToGetNumber = string.atoi(outof[1]) + except ValueError: + PlasmaTypes.PtDebugPrint("xKI:GZ - error trying to read GZGames Chronicle",level=PlasmaTypes.kErrorLevel) + # we don't know which one it errored on, so just reset them all + GZPlaying = 0 + MarkerToGetColor = 'off' + MarkerGottenColor = 'off' + MarkerToGetNumber = 0 + MarkerGottenNumber = 0 + else: + PlasmaTypes.PtDebugPrint("xKI:GZ - error GZGames string formation error",level=PlasmaTypes.kErrorLevel) + pass + else: + # can't be playing a GZGame! + # ...might be a MarkerTag game... let the KI determine that. + pass + PlasmaTypes.PtDebugPrint("PtDetermineGZ: - returning game=%d colors=%s:%s markers=%d:%d" % (GZPlaying, MarkerGottenColor, MarkerToGetColor ,MarkerGottenNumber, MarkerToGetNumber),level=PlasmaTypes.kDebugDumpLevel) + return (GZPlaying,MarkerGottenColor,MarkerToGetColor,MarkerGottenNumber,MarkerToGetNumber) + +def PtCaptureGZMarker(GZMarkerInRange): + import Plasma + import PlasmaTypes + # get current GZ Game state + (GZPlaying,MarkerGottenColor,MarkerToGetColor,MarkerGottenNumber,MarkerToGetNumber) = PtDetermineGZ() + # make sure there is room for the capture marker + if GZPlaying and MarkerToGetNumber > MarkerGottenNumber: + # set the marker status to 'gotten' + # ...in the GZ marker chronicle + vault = Plasma.ptVault() + # is there a chronicle for the GZ games? + entry = vault.findChronicleEntry(kChronicleGZMarkersAquired) + if type(entry) != type(None): + markers = entry.chronicleGetValue() + markerIdx = GZMarkerInRange - 1 + if markerIdx >= 0 and markerIdx < len(markers): + # Set the marker to "captured" + PlasmaTypes.PtDebugPrint("PtCaptureGZMarker: starting with '%s' changing %d to '%s'" % (markers,GZMarkerInRange,kGZMarkerCaptured),level=PlasmaTypes.kDebugDumpLevel) + if len(markers)-(markerIdx+1) != 0: + markers = markers[:markerIdx] + kGZMarkerCaptured + markers[-(len(markers)-(markerIdx+1)):] + else: + markers = markers[:markerIdx] + kGZMarkerCaptured + #PlasmaTypes.PtDebugPrint("xKI: out string is '%s'" % (markers),level=PlasmaTypes.kDebugDumpLevel) + entry.chronicleSetValue(markers) + entry.save() + # update the marker Gotten count + totalGotten = markers.count(kGZMarkerCaptured) + KIMarkerLevel = PtDetermineKIMarkerLevel() + if KIMarkerLevel > kKIMarkerFirstLevel: + # if this is the second wave of markers (or beyond) + totalGotten -= 5 + if totalGotten < 0: + totalGotten = 0 + if totalGotten > MarkerToGetNumber: + totalGotten = MarkerToGetNumber + MarkerGottenNumber = totalGotten + # save update to chronicle + PtUpdateGZGamesChonicles(GZPlaying,MarkerGottenColor,MarkerToGetColor,MarkerGottenNumber,MarkerToGetNumber) + else: + PlasmaTypes.PtDebugPrint("PtCaptureGZMarker: invalid marker serial number of %d" % (gGZMarkerInRange),level=PlasmaTypes.kErrorLevel ) + pass + else: + PlasmaTypes.PtDebugPrint("PtCaptureGZMarker: no chronicle entry found",level=PlasmaTypes.kErrorLevel ) + pass + else: + PlasmaTypes.PtDebugPrint("PtCaptureGZMarker: no game or this game is complete",level=PlasmaTypes.kErrorLevel ) + pass + +def PtVerifyGZMarker(): + import Plasma + import PlasmaTypes + # get current GZ Game state + (GZPlaying,MarkerGottenColor,MarkerToGetColor,MarkerGottenNumber,MarkerToGetNumber) = PtDetermineGZ() + # make sure there is room for the capture marker + if GZPlaying: + # set the marker status to 'gotten' + # ...in the GZ marker chronicle + vault = Plasma.ptVault() + # is there a chronicle for the GZ games? + entry = vault.findChronicleEntry(kChronicleGZMarkersAquired) + if type(entry) != type(None): + markers = entry.chronicleGetValue() + # get what was really gotten + totalGotten = markers.count(kGZMarkerCaptured) + KIMarkerLevel = PtDetermineKIMarkerLevel() + if KIMarkerLevel > kKIMarkerFirstLevel: + # if this is the second wave of markers (or beyond) + totalGotten -= 5 + if totalGotten < 0: + totalGotten = 0 + if totalGotten > MarkerToGetNumber: + totalGotten = MarkerToGetNumber + if totalGotten != MarkerGottenNumber: + PlasmaTypes.PtDebugPrint("PtVerifyGZMarker: Error! Gotten different than real. They say=%d We say=%d"%(MarkerGottenNumber,totalGotten),level=PlasmaTypes.kErrorLevel ) + MarkerGottenNumber = totalGotten + # save update to chronicle + PtUpdateGZGamesChonicles(GZPlaying,MarkerGottenColor,MarkerToGetColor,MarkerGottenNumber,MarkerToGetNumber) + Plasma.PtSendKIMessage(kGZUpdated,0) + return (GZPlaying,MarkerGottenColor,MarkerToGetColor,MarkerGottenNumber,MarkerToGetNumber) + +def PtUpdateGZGamesChonicles(GZPlaying,MarkerGottenColor,MarkerToGetColor,MarkerGottenNumber,MarkerToGetNumber): + "Update the GZ chronicle variable" + import Plasma + import PlasmaTypes + vault = Plasma.ptVault() + # is there a chronicle for the GZ games? + entry = vault.findChronicleEntry(kChronicleGZGames) + try: + upstring = "%d %s:%s %d:%d" % (GZPlaying,MarkerGottenColor,MarkerToGetColor,MarkerGottenNumber,MarkerToGetNumber) + if type(entry) != type(None): + entry.chronicleSetValue(upstring) + entry.save() + else: + # if there is none, then just add another entry + vault.addChronicleEntry(kChronicleGZGames,kChronicleGZGamesType,upstring) + except TypeError: + if type(GZPlaying) != type(0): + PlasmaTypes.PtDebugPrint("PtUpdateGZGamesChronicle: GZPlaying wrong type (should be integer)",level=PlasmaTypes.kErrorLevel ) + pass + if type(MarkerToGetColor) != type(""): + PlasmaTypes.PtDebugPrint("PtUpdateGZGamesChronicle: GZPlaying wrong type (should be string)",level=PlasmaTypes.kErrorLevel ) + pass + if type(MarkerGottenColor) != type(""): + PlasmaTypes.PtDebugPrint("PtUpdateGZGamesChronicle: GZPlaying wrong type (should be string)",level=PlasmaTypes.kErrorLevel ) + pass + if type(MarkerToGetNumber) != type(0): + PlasmaTypes.PtDebugPrint("PtUpdateGZGamesChronicle: GZPlaying wrong type (should be integer)",level=PlasmaTypes.kErrorLevel ) + pass + if type(MarkerGottenNumber) != type(0): + PlasmaTypes.PtDebugPrint("PtUpdateGZGamesChronicle: GZPlaying wrong type (should be integer)",level=PlasmaTypes.kErrorLevel ) + pass + pass + + +# OnRTChat flags +# NOTE: kRTChatInterAge = 8 is being used in cyMisc.cpp SendRTChat (hard coded) so don't change here unless you change that too +kRTChatPrivate=1 +kRTChatAdmin=2 +kRTChatPrivateAdmin=3 +kRTChatInterAge=8 +kRTChatStatusMsg=16 +kRTChatNeighborsMsg=32 + +# flags channel mask +kRTChatFlagMask = 65535 +kRTChatChannelMask = 65280 +kRTChatNoChannel = 255 + +# OnCCRMsg flags +kCCRBeginCommunication=1 +kCCRChat=2 +kCCREndCommunication=3 +kCCRReturnChatMsg=4 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaNetConstants.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaNetConstants.py new file mode 100644 index 00000000..18136ee6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaNetConstants.py @@ -0,0 +1,34 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +class PtLinkingRules: + """(none)""" + kBasicLink = 0 + kOriginalBook = 1 + kSubAgeBook = 2 + kOwnedBook = 3 + kVisitBook = 4 + kChildAgeBook = 5 + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaTypes.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaTypes.py new file mode 100644 index 00000000..c62638a6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaTypes.py @@ -0,0 +1,820 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +""" +This module is contains the datatypes and constants for +interfacing with the Plasma 2.0 engine. +""" +from Plasma import * +from PlasmaConstants import * + +#################################### +# Utility functions +#################################### +false=0 +true=1 + +# OnNotify Event enums +kCollisionEvent=PtEventType.kCollision # [1]=enter flag, [2]=hitter(probably avatar), [3]=hittee +kPickedEvent=PtEventType.kPicked # [1]=enter flag, [2]=picker(probably avatar), [3]=pickee, [4]=hitpoint(world) [5]=hitpoint(local) +kControlKeyEvent=PtEventType.kControlKey # [1]=key id, [2]=down flag +kVariableEvent=PtEventType.kVariable # [1]=name, [2]=data type, [3]=data +kFacingEvent=PtEventType.kFacing # [1]=enabled flag, [2]=facer(probably avatar), [3]=facee, [4]=dot product +kContainedEvent=PtEventType.kContained # [1]=entering flag, [2]=contained(probably avatar), [3]=container +kActivateEvent=PtEventType.kActivate # [1]=active flag, [2]=activate flag +kCallbackEvent=PtEventType.kCallback # [1]=callback id +kResponderStateEvent=PtEventType.kResponderState # [1]=state id +kMultiStageEvent=PtEventType.kMultiStage # [1]=what stage, [2]=event(see below), [3]=avatar +kSpawnedEvent=PtEventType.kSpawned # [1]=spawner, [2]=spawnee (usually avatar) +kClickDragEvent=PtEventType.kClickDrag # not used yet +kOfferLinkingBook=PtEventType.kOfferLinkingBook # [1]=offerer, [2]=link panel ID of age offered + + +# OnNotify Var Event Data Types +kVarNumberType=PtNotifyDataType.kNumber +kVarKeyType=PtNotifyDataType.kKey +# OnNotify MultiStageEvent - what event types +kEnterStage=PtMultiStageEventType.kEnterStage +kBeginingOfLoop=PtMultiStageEventType.kBeginingOfLoop +kAdvanceNextStage=PtMultiStageEventType.kAdvanceNextStage +kRegressPrevStage=PtMultiStageEventType.kRegressPrevStage +# Behavior - gotoStage +kStageExitBrain=-1 # sending ptAttribBehavior to stage -1 will exit brain + +# OnGUINotify Control Types +kDialog=1 +kButton=2 +kDraggable=3 +kListBox=4 +kTextBox=5 +kEditBox=6 +kUpDownPair=7 +kKnob=8 +kDragBar=9 +kCheckBox=10 +kRadioGroup=11 +kDynamicTextControl=12 +kMultiLineEdit=13 +# GUIControlListBox String Justify Types +kLeftJustify=1 +kRightJustify=2 +# GUIControlListBox String inherit flag +kNoInherit=0 +kInheritFromNormal=1 +kInheritFromSelect=2 +kSelectDetermined=3 +kSelectUseGUIColor=4 +# GUIControlMultiLineEdit style flags (additive) +kFontBold=1 +kFontItalic=2 +kFontShadowed=4 + +# OnGUINotify Event Types +kShowHide=1 # show or hide change (only on kDialog) +kAction=2 # kButton clicked, kListBox item clicked on, kEditBox hit enter +kValueChanged=3 # value changed in control (could be from kUpDownPair,kKnob,kCheckBox,kRadioGroup +kDialogLoaded=4 # the dialog has just been loaded +kFocusChange=5 # the focus changed from one control to another, or none, within the dialog +kExitMode = 6 # Modal dialog received an exit mode command +kInterestingEvent = 7 # an interesting event happened + +# OnRoomLoad 'what' types +kLoaded=1 +kUnloaded=2 + +# Clothing group Types and clothing types +kMaleClothingGroup=0 +kFemaleClothingGroup=1 +kAnyClothingItem=-1 +kPantsClothingItem=0 +kShirtClothingItem=1 +kLeftHandClothingItem=2 +kRightHandClothingItem=3 +kFaceClothingItem=4 +kHairClothingItem=5 +kLeftFootClothingItem=6 +kRightFootClothingItem=7 +kAccessoryClothingItem=8 + +#Debug print levels +kDebugDumpLevel = 1 +kWarningLevel = 2 +kErrorLevel = 3 +kAssertLevel = 4 + + +def PtAssert(cond, msg): + "Plasma assert. Just like the Python one but we can set it to NOP in release" + assert cond,msg + +def PtDebugPrint(*msgs,**keywords): + "Plasma debug print. Will be NOP'd when released" + try: + level = keywords['level'] + except LookupError: + level = kErrorLevel + if level >= PtGetPythonLoggingLevel(): + if level == 4: + PtAssert(0,msgs[0]) + else: + for msg in msgs: + print msg + +def PtGetObjectName(obj): + "Given a ptSceneobject, return its name" + if isinstance(obj,ptSceneobject): + if type(obj.getKey()) != type(None): + return obj.getKey().getName() + return "nil" + +# add an event that is in the form of a list +# ...to a notify message +def PtAddEvent(notify,event): + "Add an event of any type to a ptNotify message object" + if type(event) != type([]): + print "Unrecognized event record structure" + return + if event[0] == kCollisionEvent: + notify.addCollisionEvent(event[1],event[2].getKey(),event[3].getKey()) + elif event[0] == kPickedEvent: + notify.addPickEvent(event[1],event[2].getKey(),event[3].getKey(),event[4]) + elif event[0] == kControlKeyEvent: + notify.addControlKeyEvent(event[1],event[2]) + elif event[0] == kVariableEvent: + if event[2] == kVarNumberType: + notify.addVarNumber(event[1],event[3]) + elif event[2] == kVarKeyType: + notify.addVarKey(event[1],event[3]) + elif event[0] == kFacingEvent: + notify.addFacingEvent(event[1],event[2].getKey(),event[3].getKey(),event[4]) + elif event[0] == kContainedEvent: + notify.addContainerEvent(event[1],event[2].getKey(),event[3].getKey()) + elif event[0] == kActivateEvent: + notify.addActivateEvent(event[1],event[2]) + elif event[0] == kCallbackEvent: + notify.addCallbackEvent(event[1]) + elif event[0] == kResponderStateEvent: + notify.addResponderState(event[1]) + else: + print "Unrecognized event type %d" % (event[0]) + +# add a list of events into a ptNotify message +def PtAddEvents(notify, events): + "Add a list of events to a ptNotify message object" + if type(events) == type([]): + for event in events: + PtAddEvent(notify,event) + +# find the avatar in event record list +def PtFindAvatar(events): + "Find the avatar in one of the event records" + for event in events: + if event[0]==kCollisionEvent or event[0]==kPickedEvent or event[0]==kFacingEvent or event[0]==kContainedEvent or event[0]==kSpawnedEvent: + return event[2] + if event[0] == kMultiStageEvent: + return event[3] + # didn't find one + return None + +#################################### +# Exceptions +#################################### +import exceptions +class ptResponderStateError(exceptions.Exception): + def __init__(self,args=None): + self.args = args + +# +# Attributes that will be exposed in Max to be filled in by + +#################################### +# base class for all attributes +#################################### +# (This makes it easy to find all the attributes in a module) +class ptAttribute: + def __init__(self,id,name, vislistid = -1, visliststates = []): + self.id = id + self.name = name + self.vis_id = vislistid + self.vis_states = visliststates + + def setVisInfo(self, id, stateslist): + self.vis_id = id + self.vis_states = stateslist + + def getVisInfo(self): + return (self.vis_id, self.vis_states) + +# base class for all list attributes +# (This makes it easy to find all the attributes that are a list) +class ptAttributeList(ptAttribute): + def __init__(self,id,name): + ptAttribute.__init__(self,id,name) + +# Boolean attribute (Checkbox) +class ptAttribBoolean(ptAttribute): + def __init__(self,id,name=None, default=0): + ptAttribute.__init__(self,id,name) + self.value = default + def getdef(self): + return (self.id,self.name,1,self.value) + +# Integer attribute (Spinner) +class ptAttribInt(ptAttribute): + def __init__(self,id,name=None,default=0,rang=None): + ptAttribute.__init__(self,id,name) + self.value = default + self.rang = rang + def getdef(self): + return (self.id,self.name,2,self.value,self.rang) + +# Floating point number attribute (Spinner) +class ptAttribFloat(ptAttribute): + def __init__(self,id,name=None,default=0.0,rang=None): + ptAttribute.__init__(self,id,name) + self.value = default + self.rang = rang + def getdef(self): + return (self.id,self.name,3,self.value,self.rang) + +# String attribute (Edit box) +class ptAttribString(ptAttribute): + def __init__(self,id,name=None,default=""): + ptAttribute.__init__(self,id,name) + self.value = default + def getdef(self): + return (self.id,self.name,4,self.value) + +# Drop-down list attribute (Drop-down list combo box) +class ptAttribDropDownList(ptAttribute): + def __init__(self,id,name=None,options=None): + ptAttribute.__init__(self,id,name) + self.options = options + def getdef(self): + return (self.id,self.name,20,self.options) + +# Sceneobject attribute (pick single sceneobject box) +class ptAttribSceneobject(ptAttribute): + def __init__(self,id,name=None,netForce=0): + ptAttribute.__init__(self,id,name) + self.value = None + self.sceneobject = None + self.netForce = netForce + def getdef(self): + return (self.id,self.name,5) + def __setvalue__(self,value): + if self.netForce: + value.netForce(1) + self.value = value + self.sceneobject = self.value + +# Sceneobject list attribute (pick multiple sceneobjects box) +class ptAttribSceneobjectList(ptAttributeList): + def __init__(self,id,name=None,byObject=0,netForce=0): + ptAttributeList.__init__(self,id,name) + self.value = [] # start as an empty list + self.sceneobject = self.value + self.netForce = netForce + if byObject: + self.byObject = {} + else: + self.byObject = None + def getdef(self): + return (self.id,self.name,6) + def __setvalue__(self,value): + if self.netForce: + value.netForce(1) + self.value.append(value) + if type(self.byObject) == type({}): + name = value.getName() + self.byObject[name] = value + +# attribute list of keys +class ptAttributeKeyList(ptAttributeList): + def __init__(self,id,name=None,byObject=0,netForce=0): + ptAttributeList.__init__(self,id,name) + self.value = [] + self.netForce = netForce + if byObject: + self.byObject = {} + else: + self.byObject = None + def enable(self,objectName=None): + if self.value != None: + if type(objectName) != type(None) and type(self.byObject) != type(None): + pkey = self.byObject[objectName] + if self.netForce: + pkey.netForce(1) + pkey.enable() + elif type(self.value)==type([]): + for pkey in self.value: + if self.netForce: + pkey.netForce(1) + pkey.enable() + else: + if self.netForce: + self.value.netForce(1) + self.value.enable() + def disable(self,objectName=None): + if self.value != None: + if type(objectName) != type(None) and type(self.byObject) != type(None): + pkey = self.byObject[objectName] + if self.netForce: + pkey.netForce(1) + pkey.disable() + elif type(self.value)==type([]): + for pkey in self.value: + if self.netForce: + pkey.netForce(1) + pkey.disable() + else: + if self.netForce: + self.value.netForce(1) + self.value.disable() + def __setvalue__(self,value): + if self.netForce: + value.netForce(1) + self.value.append(value) + if type(self.byObject) == type({}): + name = value.getName() + self.byObject[name] = value + +# Activator attribute (pick activator types box) +class ptAttribActivator(ptAttributeKeyList): + def getdef(self): + return (self.id,self.name,8) + def enableActivator(self): + for key in self.value: + key.getSceneObject().physics.enable() + def disableActivator(self): + for key in self.value: + key.getSceneObject().physics.disable() + def volumeSensorIgnoreExtraEnters(self,state): + for key in self.value: + key.getSceneObject().volumeSensorIgnoreExtraEnters(state) + +# Activator attribute (pick activator types box) +class ptAttribActivatorList(ptAttributeKeyList): + def getdef(self): + return (self.id,self.name,7) + +# Responder attribute (pick responder types box) +class ptAttribResponder(ptAttributeKeyList): + def __init__(self,id,name=None,statelist=None,byObject=0,netForce=0): + ptAttributeKeyList.__init__(self,id,name,byObject,netForce) + self.state_list = statelist + def getdef(self): + return (self.id,self.name,9) + def run(self,key,state=None,events=None,avatar=None,objectName=None,netForce=0,netPropagate=1,fastforward=0): + # has the value been set? + if type(self.value) != type(None): + nt = ptNotify(key) + nt.clearReceivers() + # see if the value is a list or byObject or a single + if type(objectName) != type(None) and type(self.byObject) != type(None): + nt.addReceiver(self.byObject[objectName]) + elif type(self.value)==type([]): + for resp in self.value: + nt.addReceiver(resp) + else: + nt.addReceiver(self.value) + if not netPropagate: + nt.netPropagate(0) + # ptNotify defaults to netPropagate=1 + if netForce or self.netForce: + nt.netForce(1) + # see if the state is specified + if type(state) == type(0): + raise ptResponderStateError,"Specifying state as a number is no longer supported" + elif type(state) == type(''): + if type(self.state_list) != type(None): + try: + idx = self.state_list.index(state) + nt.addResponderState(idx) + except ValueError: + raise ptResponderStateError, "There is no state called '%s'"%(state) + else: + raise ptResponderStateError,"There is no state list provided" + # see if there are events to pass on + if type(events) != type(None): + PtAddEvents(nt,events) + if type(avatar) != type(None): + nt.addCollisionEvent(1,avatar.getKey(),avatar.getKey()) + if fastforward: + nt.setType(PtNotificationType.kResponderFF) + # if fast forwarding, then only do it on the local client + nt.netPropagate(0) + nt.netForce(0) + nt.setActivate(1.0) + nt.send() + def setState(self,key,state,objectName=None,netForce=0,netPropagate=1): + # has the value been set? + if type(self.value) != type(None): + nt = ptNotify(key) + nt.clearReceivers() + # see if the value is a list or byObject or a single + if type(objectName) != type(None) and type(self.byObject) != type(None): + nt.addReceiver(self.byObject[objectName]) + elif type(self.value)==type([]): + for resp in self.value: + nt.addReceiver(resp) + else: + nt.addReceiver(self.value) + if not netPropagate: + nt.netPropagate(0) + # ptNotify defaults to netPropagate=1 + if netForce or self.netForce: + nt.netForce(1) + # see if the state is specified + if type(state) == type(0): + raise ptResponderStateError,"Specifying state as a number is no longer supported" + elif type(state) == type(''): + if type(self.state_list) != type(None): + try: + idx = self.state_list.index(state) + nt.addResponderState(idx) + except ValueError: + raise ptResponderStateError, "There is no state called '%s'"%(state) + else: + raise ptResponderStateError,"There is no state list provided" + # see if there are events to pass on + nt.setType(PtNotificationType.kResponderChangeState) + nt.setActivate(1.0) + nt.send() + + def getState(self): + if (type(self.value) != type(None)): + if type(self.value)==type([]): + for resp in self.value: + obj = resp.getSceneObject() + idx = obj.getResponderState() + curState = self.state_list[idx] + return curState + else: + obj = self.value.getSceneObject() + idx = obj.getResponderState() + curState = self.state_list[idx] + return curState + + +# Responder attribute List +class ptAttribResponderList(ptAttribResponder): + def getdef(self): + return (self.id,self.name,10) + +# Activator attribute (pick activator types box) +class ptAttribNamedActivator(ptAttribActivator): + def getdef(self): + # get attribute as a string, then we will turn it into an activator later + return (self.id,self.name,4,self.value) + +# Responder attribute (pick responder types box) +class ptAttribNamedResponder(ptAttribResponder): + def getdef(self): + # get attribute as a string, then we will turn it into an responder later + return (self.id,self.name,4,self.value) + +# DynamicText attribute pick button +class ptAttribDynamicMap(ptAttribute): + def __init__(self,id,name=None,netForce=0): + ptAttribute.__init__(self,id,name) + self.value = None + self.textmap = None + self.netForce = netForce + # this is to set the value via method (only called if defined) + def __setvalue__(self,value): + # has a ptDynamicText already been made + try: + self.textmap.addKey(value) + except AttributeError: + self.textmap = ptDynamicMap(value) + if self.netForce: + self.textmap.netForce(1) + self.value = self.textmap + def getdef(self): + return (self.id,self.name,11) + +# a GUI Dialogbox attribute +class ptAttribGUIDialog(ptAttribute): + def __init__(self,id,name=None): + ptAttribute.__init__(self,id,name) + self.dialog = None + self.value = None + def getdef(self): + return (self.id,self.name,12) + def __setvalue__(self,value): + self.dialog = ptGUIDialog(value) + self.value = self.dialog + +# a Exclude region attribute +kExRegRelease = 0 +kExRegClear = 1 +class ptAttribExcludeRegion(ptAttribute): + def __init__(self,id,name=None): + ptAttribute.__init__(self,id,name) + self.value = None + def getdef(self): + return (self.id,self.name,13) + def clear(self,sender): + if type(self.value) != type(None): + PtExcludeRegionSet(sender,self.value,kExRegClear) + def release(self,sender): + if type(self.value) != type(None): + PtExcludeRegionSet(sender,self.value,kExRegRelease) + def enable(self): + self.sceneobject.physics.enable() + def disable(self): + self.sceneobject.physics.disable() + def clearNow(self,sender): + if type(self.value) != type(None): + PtExcludeRegionSetNow(sender,self.value,kExRegClear) + def releaseNow(self,sender): + if type(self.value) != type(None): + PtExcludeRegionSetNow(sender,self.value,kExRegRelease) + +class ptAttribWaveSet(ptAttribute): + def __init__(self,id,name=None): + ptAttribute.__init__(self,id,name) + self.value = None + def getdef(self): + return (self.id,self.name,19) + def __setvalue__(self,value): + self.waveset = ptWaveSet(value) + self.value = self.waveset + +class ptAttribSwimCurrent(ptAttribute): + def __init__(self,id,name=None): + ptAttribute.__init__(self,id,name) + self.value = None + self.current = None + def getdef(self): + return (self.id,self.name,21) + def __setvalue__(self,value): + self.current = ptSwimCurrentInterface(value) + self.value = self.current + +class ptAttribClusterList(ptAttributeList): + def __init__(self,id,name=None): + ptAttribute.__init__(self,id,name) + self.value = [] + def getdef(self): + return (self.id,self.name,22) + def __setvalue__(self,value): + self.value.append(ptCluster(value)) + +# special class for byObject that gets the parents name of the values when someone first asks for them +class ptByAnimObject(dict): + def __init__(self): + dict.__init__(self) + self.gotParentKeys = 0 + def getParentKeys(self): + # if we haven't got the parent keys yet, then add them to the dict + if not self.gotParentKeys: + for anim in dict.values(self): + # get the animation target key + aKey = anim.getFirstKey() + if aKey: + # get its parent key + pKey = aKey.getParentKey() + if pKey: + # only add this key once + if not pKey.getName() in self: + dict.__setitem__(self,pKey.getName(),anim) + self.gotParentKeys = 1 + def __getitem__(self,key): + self.getParentKeys() + return dict.__getitem__(self,key) + def keys(self): + self.getParentKeys() + return dict.keys(self) + def get(self, key, *args): + self.getParentKeys() + return dict.get(self, key, *args) + +# an Animation attribute +kAnimEaseNoEase = 0 +kAnimEaseConstAccel = 1 +kAnimEaseSpline = 2 +class ptAttribAnimation(ptAttribute): + def __init__(self,id,name=None,byObject=0,netForce=0): + ptAttribute.__init__(self,id,name) + self.value = None + self.animName = "" + self.netForce = netForce + if byObject: + self.byObject = ptByAnimObject() + else: + self.byObject = None + # this is to set the value via method (only called if defined) + def __setvalue__(self,value): + # has a ptAnimation already been made + if type(value) == type(""): + self.animName = value + try: + self.animation.setAnimName(value) + # then if there are animations by object then set those, too + if isinstance(self.byObject,ptByAnimObject): + for anim in self.byObject.values(): + anim.setAnimName(value) + except AttributeError: + self.animation = ptAnimation() + self.animation.setAnimName(value) + if self.netForce: + self.animation.netForce(1) + self.value = self.animation + elif isinstance(value,ptKey): + try: + self.animation.addKey(value) + except AttributeError: + self.animation = ptAnimation() + self.animation.addKey(value) + if self.netForce: + self.animation.netForce(1) + self.value = self.animation + if isinstance(self.byObject,ptByAnimObject): + singleAnim = ptAnimation() + singleAnim.addKey(value) + if self.netForce: + singleAnim.netForce(1) + # set name if known + if self.animName != "": + singleAnim.setAnimName(self.animName) + name = value.getName() + self.byObject[name] = singleAnim + def getdef(self): + return (self.id,self.name,14) + +# a Behavior attribute +class ptAttribBehavior(ptAttribute): + "Attribute for specifying behaviors, including multistage Behaviors" + def __init__(self,id,name=None,netForce=1,netProp=1): + ptAttribute.__init__(self,id,name) + self.value = None + self.netForce = netForce + self.netProp = netProp + def __setvalue__(self,value): + self.value = value + PtSetBehaviorNetFlags(self.value, self.netForce, self.netProp) + def getdef(self): + return (self.id,self.name,15) + def run(self,avatar): + "This will run the behavior on said avatar" + if type(self.value) != type(None): + if self.netForce: + self.value.netForce(1) + avatar.avatar.netForce(1) + avatar.avatar.runBehavior(self.value,self.netForce,self.netProp) + def nextStage(self,avatar,transitionTime=1.0,setTimeFlag=1,newTime=0.0,dirFlag=0,isForward=1): + "This will go to the next stage in a multi-stage behavior" + if type(self.value) != type(None): + if self.netForce: + self.value.netForce(1) + avatar.avatar.netForce(1) + avatar.avatar.nextStage(self.value,transitionTime,setTimeFlag,newTime,dirFlag,isForward,self.netForce) + def previousStage(self,avatar,transitionTime=1.0,setTimeFlag=1,newTime=0.0,dirFlag=0,isForward=1): + "This will go to the next stage in a multi-stage behavior" + if type(self.value) != type(None): + if self.netForce: + self.value.netForce(1) + avatar.avatar.netForce(1) + avatar.avatar.previousStage(self.value,transitionTime,setTimeFlag,newTime,dirFlag,isForward,self.netForce) + def gotoStage(self,avatar,stage,transitionTime=1.0,setTimeFlag=1,newTime=0.0,dirFlag=0,isForward=1): + "This will go to the next stage in a multi-stage behavior" + if type(self.value) != type(None): + if self.netForce: + self.value.netForce(1) + avatar.avatar.netForce(1) + avatar.avatar.gotoStage(self.value,stage,transitionTime,setTimeFlag,newTime,dirFlag,isForward,self.netForce) + def setLoopCount(self,stage,loopCount): + "This will set the loop count for a stage" + if type(self.value) != type(None): + PtSetBehaviorLoopCount(self.value,stage,loopCount,self.netForce) + +# Material texture attribute pick button +class ptAttribMaterial(ptAttribute): + def __init__(self,id,name=None): + ptAttribute.__init__(self,id,name) + self.value = None + self.map = None + # this is to set the value via method (only called if defined) + def __setvalue__(self,value): + self.map = ptImage(value) + self.value = self.map + def getdef(self): + return (self.id,self.name,16) + +class ptAttribMaterialAnimation(ptAttribute): + def __init__(self, id, name = None): + ptAttribute.__init__(self, id, name) + self.value = None + self.animation = None + + def __setvalue__(self, value): + if type(self.animation) == type(None): + self.animation = ptAnimation() + self.animation.addKey(value) + self.value = self.animation + else: + self.animation.addKey(value) + + def getdef(self): + return (self.id, self.name, 23) + + +# Sceneobject list attribute (pick multiple sceneobjects box) +class ptAttribMaterialList(ptAttributeList): + def __init__(self,id,name=None,byObject=0,netForce=0): + ptAttributeList.__init__(self,id,name) + self.value = [] # start as an empty list + self.map = self.value + self.netForce = netForce + if byObject: + self.byObject = {} + else: + self.byObject = None + def getdef(self): + return (self.id,self.name,6) + def __setvalue__(self,value): + if self.netForce: + value.netForce(1) + self.value.append(value) + if type(self.byObject) == type({}): + name = value.getName() + self.byObject[name] = value + +# a GUI PopUpMenu attribute +class ptAttribGUIPopUpMenu(ptAttribute): + def __init__(self,id,name=None): + ptAttribute.__init__(self,id,name) + self.value = None + def getdef(self): + return (self.id,self.name,17) + def __setvalue__(self,value): + self.menu = ptGUIPopUpMenu(value) + self.value = self.menu + +# a GUI Skin attribute +class ptAttribGUISkin(ptAttribute): + def __init__(self,id,name=None): + ptAttribute.__init__(self,id,name) + self.value = None + def getdef(self): + return (self.id,self.name,18) + def __setvalue__(self,value): + self.skin = ptGUISkin(value) + self.value = self.skin + +# a Grass Shader attribute +class ptAttribGrassShader(ptAttribute): + def __init__(self,id,name=None): + ptAttribute.__init__(self,id,name) + self.value = None + def getdef(self): + return (self.id,self.name,24) + def __setvalue__(self,value): + self.shader = ptGrassShader(value) + self.value = self.shader + + +# +# ptModifier - class for creating a Plasma modifier, such as a responder + +# base class +class ptModifier: + def __init__(self): + self.key = None + self.SDL = None + self.version = 0 + +class ptResponder(ptModifier): + # this modifier will get a plNotifyMsg as an OnNotify + def __init__(self): + ptModifier.__init__(self) + self.sceneobject = None + +class ptMultiModifier(ptModifier): + # this modifier can be attached to multiple object, but only one module + def __init__(self): + ptModifier.__init__(self) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaVaultConstants.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaVaultConstants.py new file mode 100644 index 00000000..22e8edd0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/PlasmaVaultConstants.py @@ -0,0 +1,98 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +class PtVaultCallbackTypes: + """(none)""" + kVaultConnected = 1 + kVaultNodeSaved = 2 + kVaultNodeRefAdded = 3 + kVaultRemovingNodeRef = 4 + kVaultNodeRefRemoved = 5 + kVaultNodeInitialized = 6 + kVaultOperationFailed = 7 + kVaultNodeAdded = 8 + kVaultDisconnected = 9 + +class PtVaultNodeTypes: + """(none)""" + kInvalidNode = 0 + kAgeInfoNode = 33 + kAgeInfoListNode = 34 + kMarkerGameNode = 35 + kVNodeMgrPlayerNode = 2 + kVNodeMgrAgeNode = 3 + kFolderNode = 22 + kPlayerInfoNode = 23 + kImageNode = 25 + kTextNoteNode = 26 + kSDLNode = 27 + kAgeLinkNode = 28 + kChronicleNode = 29 + kPlayerInfoListNode = 30 + +class PtVaultNotifyTypes: + """(none)""" + kRegisteredOwnedAge = 9 + kUnRegisteredOwnedAge = 10 + kRegisteredVisitAge = 11 + kUnRegisteredVisitAge = 12 + kPublicAgeCreated = 13 + kPublicAgeRemoved = 14 + +class PtVaultStandardNodes: + """(none)""" + kUserDefinedNode = 0 + kInboxFolder = 1 + kBuddyListFolder = 2 + kIgnoreListFolder = 3 + kPeopleIKnowAboutFolder = 4 + kChronicleFolder = 6 + kAvatarOutfitFolder = 7 + kAgeTypeJournalFolder = 8 + kSubAgesFolder = 9 + kHoodMembersFolder = 11 + kAllPlayersFolder = 12 + kAgeMembersFolder = 13 + kAgeJournalsFolder = 14 + kAgeInstanceSDLNode = 16 + kCanVisitFolder = 18 + kAgeOwnersFolder = 19 + kAllAgeGlobalSDLNodesFolder = 20 + kPlayerInfoNode = 21 + kPublicAgesFolder = 22 + kAgesIOwnFolder = 23 + kAgesICanVisitFolder = 24 + kAvatarClosetFolder = 25 + kGlobalInboxFolder = 30 + +class PtVaultTextNoteSubTypes: + """(none)""" + kGeneric = 0 + +class PtVaultTextNoteTypes: + """(none)""" + kGeneric = 0 + kCCRPetition = 1 + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/glue.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/glue.py new file mode 100644 index 00000000..f9aa14cb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/glue.py @@ -0,0 +1,208 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +# glue code in python for the glue code in C++ +# This assumes that this will be loaded into the module that we are trying to do +# with an execfile('.\\python\\system\\glue.py') at the end of the module (after everything is defined) +# SPECIAL WARNING(1): This glue code returns the attributes in reverse ID order! +glue_cl = None # the class of the modifier +glue_inst = None # instance of the class modifier +glue_params = None # parameters dictionary: mapped id to instance +glue_paramKeys = None # this is the parameter ID list, that should be sorted +try: + x = glue_verbose +except NameError: + glue_verbose = 0 +def glue_getClass(): + global glue_cl + if glue_cl == None: + try: + cl = eval(glue_name) + if issubclass(cl,ptModifier): + glue_cl = cl + else: + if glue_verbose: + print "Class %s is not derived from modifier" % (cl.__name__) + except NameError: + if glue_verbose: + try: + print "Could not find class %s" % (glue_name) + except NameError: + print "Filename/classname not set!" + return glue_cl +def glue_getInst(): + global glue_inst + if type(glue_inst) == type(None): + cl = glue_getClass() + if cl != None: + glue_inst = cl() + return glue_inst +def glue_delInst(): + global glue_inst + global glue_cl + global glue_params + global glue_paramKeys + if type(glue_inst) != type(None): + del glue_inst + # remove our references + glue_cl = None + glue_params = None + glue_paramKeys = None +def glue_getVersion(): + inst = glue_getInst() + ver = inst.version + glue_delInst() + return ver +def glue_findAndAddAttribs(obj, glue_params): + if isinstance(obj,ptAttribute): + if glue_params.has_key(obj.id): + if glue_verbose: + print "WARNING: Duplicate attribute ids!" + print "%s has id %d which is already defined in %s" % (obj.name, obj.id, glue_params[obj.id].name) + else: + glue_params[obj.id] = obj + elif type(obj) == type([]): + for o in obj: + glue_findAndAddAttribs(o, glue_params) + elif type(obj) == type({}): + for o in obj.values(): + glue_findAndAddAttribs(o, glue_params) + elif type(obj) == type( () ): + for o in obj: + glue_findAndAddAttribs(o, glue_params) + +def glue_getParamDict(): + global glue_params + global glue_paramKeys + if type(glue_params) == type(None): + glue_params = {} + gd = globals() + for obj in gd.values(): + glue_findAndAddAttribs(obj, glue_params) + # rebuild the parameter sorted key list + glue_paramKeys = glue_params.keys() + glue_paramKeys.sort() + glue_paramKeys.reverse() # reserve the order because PlasmaMax will ask for them in reverse order + return glue_params +def glue_getClassName(): + cl = glue_getClass() + if cl != None: + return cl.__name__ + if glue_verbose: + print "Class not found in %s.py" % (glue_name) + return None +def glue_getBlockID(): + inst = glue_getInst() + if inst != None: + return inst.id + if glue_verbose: + print "Instance could not be created in %s.py" % (glue_name) + return None +def glue_getNumParams(): + pd = glue_getParamDict() + if pd != None: + return len(pd) + if glue_verbose: + print "No attributes found in %s.py" % (glue_name) + return 0 +def glue_getParam(number): + global glue_paramKeys + pd = glue_getParamDict() + if pd != None: + # see if there is a paramKey list + if type(glue_paramKeys) == type([]): + if number >= 0 and number < len(glue_paramKeys): + return pd[glue_paramKeys[number]].getdef() + else: + print "glue_getParam: Error! %d out of range of attribute list" % (number) + else: + pl = pd.values() + if number >= 0 and number < len(pl): + return pl[number].getdef() + else: + if glue_verbose: + print "glue_getParam: Error! %d out of range of attribute list" % (number) + if glue_verbose: + print "GLUE: Attribute list error" + return None +def glue_setParam(id,value): + pd = glue_getParamDict() + if pd != None: + if pd.has_key(id): + # first try to set the attribute via function call (if there is one) + try: + pd[id].__setvalue__(value) + except AttributeError: + if isinstance(pd[id],ptAttributeList): + try: + if type(pd[id].value) != type([]): + pd[id].value = [] # make sure that the value starts as an empty list + except AttributeError: + pd[id].value = [] # or if value hasn't been defined yet, then do it now + pd[id].value.append(value) # add in new value to list + else: + pd[id].value = value + else: + if glue_verbose: + print "setParam: can't find id=",id + else: + print "setParma: Something terribly has gone wrong. Head for the cover." +def glue_isNamedAttribute(id): + pd = glue_getParamDict() + if pd != None: + try: + if isinstance(pd[id],ptAttribNamedActivator): + return 1 + if isinstance(pd[id],ptAttribNamedResponder): + return 2 + except KeyError: + if glue_verbose: + print "Could not find id=%d attribute" % (id) + return 0 +def glue_isMultiModifier(): + inst = glue_getInst() + if isinstance(inst,ptMultiModifier): + return 1 + return 0 +def glue_getVisInfo(number): + global glue_paramKeys + pd = glue_getParamDict() + if pd != None: + # see if there is a paramKey list + if type(glue_paramKeys) == type([]): + if number >= 0 and number < len(glue_paramKeys): + return pd[glue_paramKeys[number]].getVisInfo() + else: + print "glue_getVisInfo: Error! %d out of range of attribute list" % (number) + else: + pl = pd.values() + if number >= 0 and number < len(pl): + return pl[number].getVisInfo() + else: + if glue_verbose: + print "glue_getVisInfo: Error! %d out of range of attribute list" % (number) + if glue_verbose: + print "GLUE: Attribute list error" + return None diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/pch.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/pch.py new file mode 100644 index 00000000..b113f645 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/pch.py @@ -0,0 +1,286 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +""" pch - Plasma Console Helper +This module aids in the using the plasma console to debug PythonFileComponents. +""" +# plasma console helper +import Plasma +import PlasmaTypes +import sys + +# globals for the outside and inside to grab +__pmods = [] +__sel = 0 +__selattr = 0 + +# help! +def help(): + "display help" + print "functions:" + print " getmods() - gets all the modules available" + print " showmods() - show the modules (and the current selected module)" + print " selmod(i) - selects module 'i'; returns module object" + print " showmod() - shows detail of the module selected" + print " showdoc() - shows the doc field of the module selected" + print " showattribs() - shows all the plasma attributes of the module selected" + print " selattrib(id) - selects attribute 'id' in the selected module;" + print " returns attribute object" + print " setattrib(value) - sets the selected attribute to 'value'" + print " showglobals() - shows the globals for the selected module" + print " setglobal(name,value) - set global 'name' to 'value' in selected module" + print " getglobal(name) - returns global object" + print " showinst() - shows the instance of the ptModifier class for the selected" + print " module" + print " getinst() - returns the instance object" + print " showvars(inst) - shows the instance variables of the ptModifier class of" + print " the selected module" + print " showmethods(inst) - shows the methods of the ptModifier class of the" + print " selected module" + print " showfunc(method) - decompiles a method or function and shows source" + print " setvar(name,value) - sets instance variable 'name' to 'value' in the" + print " selected module" + print " getvar(name) - returns the instance variable object (in selected module)" +# modules +def getmods(): + "get all the PythonFileComponent modules" + global __pmods,__sel + __pmods = [] # wipe the module list clean + print "Plasma modules:" + for modname in sys.modules.keys(): + mod = sys.modules[modname] + if hasattr(mod,"glue_inst"): + if __sel == len(__pmods): + print "*%d. %s" % (len(__pmods),modname[:-13]) + else: + print " %d. %s" % (len(__pmods),modname[:-13]) + __pmods.append([modname,mod]) +def showmods(): + "show all the PythonFileComponent modules" + global __pmods + global __sel + idx = 0 + print "Plasma modules:" + for mod in __pmods: + if idx == __sel: + print "*%d. %s" % (idx,mod[0][:-13]) + else: + print " %d. %s" % (idx,mod[0][:-13]) + idx += 1 +def selmod(idx=None): + "select a module from the list" + global __pmods + global __sel + if type(idx) == type(None): + idx = __sel + elif type(idx) == type(""): + # its a string, then find it by module name + i = 0 + for mod in __pmods: + if mod[0][:-13] == idx: + break + i += 1 + # if we didn't find the module + if i == len(__pmods): + print "Module %s not found" % idx + return + idx = i + if idx < len(__pmods): + __sel = idx + print "%s selected" % (__pmods[idx][0][:-13]) + return __pmods[__sel][1] + else: + print "Error: index not valid. There are %d modules" % (len(__pmods)) +# find attributes +def showmod(): + "show details of the selected module" + global __pmods + global __sel + print "Module: %s" % (__pmods[__sel][0][:-13]) + showdoc() + showattribs() + showglobals() + showinst() +def showdoc(): + "show the doc of the module selected" + global __pmods + global __sel + print "Doc:" + print __pmods[__sel][1].__doc__ +def showattribs(): + "show the plasma attributes of the module selected" + global __pmods + global __sel + global __selattr + print "Attributes in %s:" % (__pmods[__sel][0][:-13]) + for name in __pmods[__sel][1].__dict__.keys(): + ist = __pmods[__sel][1].__dict__[name] + if isinstance(ist,PlasmaTypes.ptAttribute): + if __selattr == ist.id: + print "*(%d) %s(%s) =" % (ist.id,name,ist.__class__.__name__),ist.value + else: + print " (%d) %s(%s) =" % (ist.id,name,ist.__class__.__name__),ist.value +def selattrib(id=None): + "select a plasma attribute by id in the selected module" + global __pmods + global __sel + global __selattr + if type(id) == type(None): + id = __selattr + for name in __pmods[__sel][1].__dict__.keys(): + ist = __pmods[__sel][1].__dict__[name] + if isinstance(ist,PlasmaTypes.ptAttribute): + if id == ist.id: + __selattr = ist.id + print "%s(%s) =" % (name,ist.__class__.__name__),ist.value + return ist + print "Error: Attribute ID %d not found" % (id) +def setattrib(value): + "set the value of the selected plasma attribute in the selected module" + global __pmods + global __sel + global __selattr + for name in __pmods[__sel][1].__dict__.keys(): + ist = __pmods[__sel][1].__dict__[name] + if isinstance(ist,PlasmaTypes.ptAttribute): + if __selattr == ist.id: + if type(ist.value) == type(None) or type(ist.value) == type(value): + # see if there is a __setvalue__ method + try: + ist.__setvalue__(value) + except AttributeError: + ist.value = value + else: + print "Error: value is not same type as attribute" + return + print "Error: Attribute ID %d not found" % (id) +# find globals +def showglobals(): + "show the global variables of the selected module" + global __pmods + global __sel + print "Globals:" + for name in __pmods[__sel][1].__dict__.keys(): + ist = __pmods[__sel][1].__dict__[name] + # make sure that its not something we already know about + if not hasattr(Plasma,name) and not hasattr(PlasmaTypes,name): + if not isinstance(ist,PlasmaTypes.ptAttribute) and not isinstance(ist,PlasmaTypes.ptModifier): + if name[:2] != '__' and name[:4] != 'glue': + if type(ist) != type(sys) and type(ist) != type(PlasmaTypes.ptAttribute): + print " %s =" % (name),ist +def setglobal(name,value): + "set a global variable to a value with in the selected module" + global __pmods + global __sel + # first see if there is already a glabal by that name + if not __pmods[__sel][1].__dict__.has_key(name): + print "Warning: creating new global!" + __pmods[__sel][1].__dict__[name] = value + print "%s = " % (name),__pmods[__sel][1].__dict__[name] +def getglobal(name): + "get a global variable with in the selected module" + global __pmods + global __sel + return __pmods[__sel][1].__dict__[name] +# find instance +def showinst(): + "show details of the instance of the ptModifier class in the selected module" + global __pmods + global __sel + for name in __pmods[__sel][1].__dict__.keys(): + ist = __pmods[__sel][1].__dict__[name] + if isinstance(ist,PlasmaTypes.ptModifier): + print "Instance of %s in module %s:" % (ist.__class__.__name__,__pmods[__sel][1].__name__[:-13]) + print " Doc: ",ist.__doc__ + showvars(ist) + showmethods(ist) +def getinst(): + "gets the instance of the ptModifier class in the selected module" + global __pmods + global __sel + for name in __pmods[__sel][1].__dict__.keys(): + ist = __pmods[__sel][1].__dict__[name] + if isinstance(ist,PlasmaTypes.ptModifier): + return ist +def showvars(instance): + "shows the variables of the instance" + print " Variables:" + if len(instance.__dict__) > 0: + for vname in instance.__dict__.keys(): + print " %s =" % (vname),instance.__dict__[vname] + else: + print " (none)" +def showmethods(instance): + "shows the methods of the instance" + print " Methods:" + for mname in instance.__class__.__dict__.keys(): + mist = instance.__class__.__dict__[mname] + # is it a function... see if it has code + if hasattr(mist,'func_code'): + # gather arguments + args = "(" + for i in range(mist.func_code.co_argcount): + args += mist.func_code.co_varnames[i] + if i+1 < mist.func_code.co_argcount: + args += "," + args += ")" + print " %s%s" % (mist.__name__,args) + print " Doc:", mist.__doc__ +def showfunc(f): + "decompiles function" + import decompyle + if hasattr(f,'func_code'): + # create the argument list + argstr = "(" + argcount = 0 + for arg in f.func_code.co_varnames[:f.func_code.co_argcount]: + argstr += arg + argcount += 1 + if argcount < f.func_code.co_argcount: + argstr += "," + argstr += ")" + print "%s%s" % (f.func_name,argstr) + print " Doc:",f.__doc__ + decompyle.decompyle(f.func_code) +def setvar(vname,value): + "set a variable within the instance of the ptModifier class in the selected module" + global __pmods + global __sel + for name in __pmods[__sel][1].__dict__.keys(): + ist = __pmods[__sel][1].__dict__[name] + if isinstance(ist,PlasmaTypes.ptModifier): + # first see if there is already a glabal by that name + if not ist.__dict__.has_key(vname): + print "Warning: creating new class variable!" + ist.__dict__[vname] = value + print "%s = " % (vname),ist.__dict__[vname] +def getvar(vname): + "get the variable in the instance of the ptModifier class in the selected module" + global __pmods + global __sel + for name in __pmods[__sel][1].__dict__.keys(): + ist = __pmods[__sel][1].__dict__[name] + if isinstance(ist,PlasmaTypes.ptModifier): + return ist.__dict__[vname] diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/ptWordFilter.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/ptWordFilter.py new file mode 100644 index 00000000..4fa59cfc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/plasma/ptWordFilter.py @@ -0,0 +1,140 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +""" +This module is the word filter to be used... +""" +import string +import re + +# Rating levels +xRatedG = 0 +xRatedPG = 1 +xRatedPG13 = 2 +xRatedR = 3 +xRatedX = 4 + +class LanguageFilter: + def __init__(self): + pass + def test(self,sentence): + "returns censored sentence" + return xRatedG + def censor(self,sentence,censorLevel): + "returns censored sentence" + return sentence + +class ExactMatchListFilter(LanguageFilter): + def __init__(self,wordlist): + self.wordlist = wordlist + def test(self,sentence): + "return the rating of sentence in question" + rated = xRatedG # assume rated lowest level + startidx = 0 + for endidx in range(len(sentence)): + if sentence[endidx] in string.whitespace or sentence[endidx] in string.punctuation: + if startidx != endidx: + try: + # find and get rating and substitute + rating = self.wordlist[string.lower(sentence[startidx:endidx])] + except LookupError: + # couldn't find word + rating = None + if rating != None and rating.rating > rated: + # substitute into string + rated = rating.rating + startidx = endidx + 1 + if startidx < len(sentence): + try: + # find and get rating and substitute + rating = self.wordlist[string.lower(sentence[startidx:])] + except LookupError: + # couldn't find word + rating = None + if rating != None and rating.rating > rated: + # substitute into string + rated = rating.rating + return rated + + def censor(self,sentence,censorLevel): + "censors a sentence to a rating" + # break into words, but perserve original punctuation + censored = "" + startidx = 0 + for endidx in range(len(sentence)): + if sentence[endidx] in string.whitespace or sentence[endidx] in string.punctuation: + if startidx != endidx: + try: + # find and get rating and substitute + rating = self.wordlist[string.lower(sentence[startidx:endidx])] + except LookupError: + # couldn't find word + rating = None + if rating != None and rating.rating > censorLevel: + # substitute into string + censored += rating.substitute + sentence[endidx] + else: + censored += sentence[startidx:endidx] + sentence[endidx] + else: + censored += sentence[startidx] + startidx = endidx + 1 + if startidx < len(sentence): + # Special after loop processing! + try: + # find and get rating and substitute + rating = self.wordlist[string.lower(sentence[startidx:])] + except LookupError: + # couldn't find word + rating = None + if rating != None and rating.rating > censorLevel: + # substitute into string + censored += rating.substitute + else: + censored += sentence[startidx:] + return censored + +class REFilter(LanguageFilter): + def __init__(self,regexp,rating): + self.compiledRE = re.compile(regexp, re.IGNORECASE | re.MULTILINE ) + if not isinstance(rating,Rating): + PtDebugPrint("ptWordFilter: rating for %s not of type Rating" % (regexp)) + self.rating = rating + def test(self,sentence): + "return the rating of sentence in question" + if self.compiledRE.search(sentence) != None: + return self.rating.rating + return xRatedG + def censor(self,sentence,censorLevel): + "censors a sentence to a rating" + if self.rating.rating > censorLevel: + if self.compiledRE.search(sentence) != None: + return self.compiledRE.sub(self.rating.substitute,sentence) + return sentence + +class Rating: + "substitute can be string for exact substitute or number of splat replacement" + def __init__(self,rating,subtitute="*****"): + self.rating = rating + self.substitute = subtitute diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/Bastion.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/Bastion.py new file mode 100644 index 00000000..ae2db74c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/Bastion.py @@ -0,0 +1,177 @@ +"""Bastionification utility. + +A bastion (for another object -- the 'original') is an object that has +the same methods as the original but does not give access to its +instance variables. Bastions have a number of uses, but the most +obvious one is to provide code executing in restricted mode with a +safe interface to an object implemented in unrestricted mode. + +The bastionification routine has an optional second argument which is +a filter function. Only those methods for which the filter method +(called with the method name as argument) returns true are accessible. +The default filter method returns true unless the method name begins +with an underscore. + +There are a number of possible implementations of bastions. We use a +'lazy' approach where the bastion's __getattr__() discipline does all +the work for a particular method the first time it is used. This is +usually fastest, especially if the user doesn't call all available +methods. The retrieved methods are stored as instance variables of +the bastion, so the overhead is only occurred on the first use of each +method. + +Detail: the bastion class has a __repr__() discipline which includes +the repr() of the original object. This is precomputed when the +bastion is created. + +""" + +__all__ = ["BastionClass", "Bastion"] + +from types import MethodType + + +class BastionClass: + + """Helper class used by the Bastion() function. + + You could subclass this and pass the subclass as the bastionclass + argument to the Bastion() function, as long as the constructor has + the same signature (a get() function and a name for the object). + + """ + + def __init__(self, get, name): + """Constructor. + + Arguments: + + get - a function that gets the attribute value (by name) + name - a human-readable name for the original object + (suggestion: use repr(object)) + + """ + self._get_ = get + self._name_ = name + + def __repr__(self): + """Return a representation string. + + This includes the name passed in to the constructor, so that + if you print the bastion during debugging, at least you have + some idea of what it is. + + """ + return "" % self._name_ + + def __getattr__(self, name): + """Get an as-yet undefined attribute value. + + This calls the get() function that was passed to the + constructor. The result is stored as an instance variable so + that the next time the same attribute is requested, + __getattr__() won't be invoked. + + If the get() function raises an exception, this is simply + passed on -- exceptions are not cached. + + """ + attribute = self._get_(name) + self.__dict__[name] = attribute + return attribute + + +def Bastion(object, filter = lambda name: name[:1] != '_', + name=None, bastionclass=BastionClass): + """Create a bastion for an object, using an optional filter. + + See the Bastion module's documentation for background. + + Arguments: + + object - the original object + filter - a predicate that decides whether a function name is OK; + by default all names are OK that don't start with '_' + name - the name of the object; default repr(object) + bastionclass - class used to create the bastion; default BastionClass + + """ + + raise RuntimeError, "This code is not secure in Python 2.2 and 2.3" + + # Note: we define *two* ad-hoc functions here, get1 and get2. + # Both are intended to be called in the same way: get(name). + # It is clear that the real work (getting the attribute + # from the object and calling the filter) is done in get1. + # Why can't we pass get1 to the bastion? Because the user + # would be able to override the filter argument! With get2, + # overriding the default argument is no security loophole: + # all it does is call it. + # Also notice that we can't place the object and filter as + # instance variables on the bastion object itself, since + # the user has full access to all instance variables! + + def get1(name, object=object, filter=filter): + """Internal function for Bastion(). See source comments.""" + if filter(name): + attribute = getattr(object, name) + if type(attribute) == MethodType: + return attribute + raise AttributeError, name + + def get2(name, get1=get1): + """Internal function for Bastion(). See source comments.""" + return get1(name) + + if name is None: + name = `object` + return bastionclass(get2, name) + + +def _test(): + """Test the Bastion() function.""" + class Original: + def __init__(self): + self.sum = 0 + def add(self, n): + self._add(n) + def _add(self, n): + self.sum = self.sum + n + def total(self): + return self.sum + o = Original() + b = Bastion(o) + testcode = """if 1: + b.add(81) + b.add(18) + print "b.total() =", b.total() + try: + print "b.sum =", b.sum, + except: + print "inaccessible" + else: + print "accessible" + try: + print "b._add =", b._add, + except: + print "inaccessible" + else: + print "accessible" + try: + print "b._get_.func_defaults =", map(type, b._get_.func_defaults), + except: + print "inaccessible" + else: + print "accessible" + \n""" + exec testcode + print '='*20, "Using rexec:", '='*20 + import rexec + r = rexec.RExec() + m = r.add_module('__main__') + m.b = b + r.r_exec(testcode) + + +if __name__ == '__main__': + _test() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/ConfigParser.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/ConfigParser.py new file mode 100644 index 00000000..d98993af --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/Python/system/ConfigParser.py @@ -0,0 +1,616 @@ +"""Configuration file parser. + +A setup file consists of sections, lead by a "[section]" header, +and followed by "name: value" entries, with continuations and such in +the style of RFC 822. + +The option values can contain format strings which refer to other values in +the same section, or values in a special [DEFAULT] section. + +For example: + + something: %(dir)s/whatever + +would resolve the "%(dir)s" to the value of dir. All reference +expansions are done late, on demand. + +Intrinsic defaults can be specified by passing them into the +ConfigParser constructor as a dictionary. + +class: + +ConfigParser -- responsible for parsing a list of + configuration files, and managing the parsed database. + + methods: + + __init__(defaults=None) + create the parser and specify a dictionary of intrinsic defaults. The + keys must be strings, the values must be appropriate for %()s string + interpolation. Note that `__name__' is always an intrinsic default; + it's value is the section's name. + + sections() + return all the configuration section names, sans DEFAULT + + has_section(section) + return whether the given section exists + + has_option(section, option) + return whether the given option exists in the given section + + options(section) + return list of configuration options for the named section + + read(filenames) + read and parse the list of named configuration files, given by + name. A single filename is also allowed. Non-existing files + are ignored. + + readfp(fp, filename=None) + read and parse one configuration file, given as a file object. + The filename defaults to fp.name; it is only used in error + messages (if fp has no `name' attribute, the string `' is used). + + get(section, option, raw=False, vars=None) + return a string value for the named option. All % interpolations are + expanded in the return values, based on the defaults passed into the + constructor and the DEFAULT section. Additional substitutions may be + provided using the `vars' argument, which must be a dictionary whose + contents override any pre-existing defaults. + + getint(section, options) + like get(), but convert value to an integer + + getfloat(section, options) + like get(), but convert value to a float + + getboolean(section, options) + like get(), but convert value to a boolean (currently case + insensitively defined as 0, false, no, off for False, and 1, true, + yes, on for True). Returns False or True. + + items(section, raw=False, vars=None) + return a list of tuples with (name, value) for each option + in the section. + + remove_section(section) + remove the given file section and all its options + + remove_option(section, option) + remove the given option from the given section + + set(section, option, value) + set the given option + + write(fp) + write the configuration state in .ini format +""" + +import re + +__all__ = ["NoSectionError", "DuplicateSectionError", "NoOptionError", + "InterpolationError", "InterpolationDepthError", + "InterpolationSyntaxError", "ParsingError", + "MissingSectionHeaderError", "ConfigParser", "SafeConfigParser", + "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"] + +DEFAULTSECT = "DEFAULT" + +MAX_INTERPOLATION_DEPTH = 10 + + + +# exception classes +class Error(Exception): + """Base class for ConfigParser exceptions.""" + + def __init__(self, msg=''): + self.message = msg + Exception.__init__(self, msg) + + def __repr__(self): + return self.message + + __str__ = __repr__ + +class NoSectionError(Error): + """Raised when no section matches a requested option.""" + + def __init__(self, section): + Error.__init__(self, 'No section: ' + `section`) + self.section = section + +class DuplicateSectionError(Error): + """Raised when a section is multiply-created.""" + + def __init__(self, section): + Error.__init__(self, "Section %r already exists" % section) + self.section = section + +class NoOptionError(Error): + """A requested option was not found.""" + + def __init__(self, option, section): + Error.__init__(self, "No option %r in section: %r" % + (option, section)) + self.option = option + self.section = section + +class InterpolationError(Error): + """Base class for interpolation-related exceptions.""" + + def __init__(self, option, section, msg): + Error.__init__(self, msg) + self.option = option + self.section = section + +class InterpolationMissingOptionError(InterpolationError): + """A string substitution required a setting which was not available.""" + + def __init__(self, option, section, rawval, reference): + msg = ("Bad value substitution:\n" + "\tsection: [%s]\n" + "\toption : %s\n" + "\tkey : %s\n" + "\trawval : %s\n" + % (section, option, reference, rawval)) + InterpolationError.__init__(self, option, section, msg) + self.reference = reference + +class InterpolationSyntaxError(InterpolationError): + """Raised when the source text into which substitutions are made + does not conform to the required syntax.""" + +class InterpolationDepthError(InterpolationError): + """Raised when substitutions are nested too deeply.""" + + def __init__(self, option, section, rawval): + msg = ("Value interpolation too deeply recursive:\n" + "\tsection: [%s]\n" + "\toption : %s\n" + "\trawval : %s\n" + % (section, option, rawval)) + InterpolationError.__init__(self, option, section, msg) + +class ParsingError(Error): + """Raised when a configuration file does not follow legal syntax.""" + + def __init__(self, filename): + Error.__init__(self, 'File contains parsing errors: %s' % filename) + self.filename = filename + self.errors = [] + + def append(self, lineno, line): + self.errors.append((lineno, line)) + self.message += '\n\t[line %2d]: %s' % (lineno, line) + +class MissingSectionHeaderError(ParsingError): + """Raised when a key-value pair is found before any section header.""" + + def __init__(self, filename, lineno, line): + Error.__init__( + self, + 'File contains no section headers.\nfile: %s, line: %d\n%s' % + (filename, lineno, line)) + self.filename = filename + self.lineno = lineno + self.line = line + + + +class RawConfigParser: + def __init__(self, defaults=None): + self._sections = {} + if defaults is None: + self._defaults = {} + else: + self._defaults = defaults + + def defaults(self): + return self._defaults + + def sections(self): + """Return a list of section names, excluding [DEFAULT]""" + # self._sections will never have [DEFAULT] in it + return self._sections.keys() + + def add_section(self, section): + """Create a new section in the configuration. + + Raise DuplicateSectionError if a section by the specified name + already exists. + """ + if section in self._sections: + raise DuplicateSectionError(section) + self._sections[section] = {} + + def has_section(self, section): + """Indicate whether the named section is present in the configuration. + + The DEFAULT section is not acknowledged. + """ + return section in self._sections + + def options(self, section): + """Return a list of option names for the given section name.""" + try: + opts = self._sections[section].copy() + except KeyError: + raise NoSectionError(section) + opts.update(self._defaults) + if '__name__' in opts: + del opts['__name__'] + return opts.keys() + + def read(self, filenames): + """Read and parse a filename or a list of filenames. + + Files that cannot be opened are silently ignored; this is + designed so that you can specify a list of potential + configuration file locations (e.g. current directory, user's + home directory, systemwide directory), and all existing + configuration files in the list will be read. A single + filename may also be given. + """ + if isinstance(filenames, basestring): + filenames = [filenames] + for filename in filenames: + try: + fp = open(filename) + except IOError: + continue + self._read(fp, filename) + fp.close() + + def readfp(self, fp, filename=None): + """Like read() but the argument must be a file-like object. + + The `fp' argument must have a `readline' method. Optional + second argument is the `filename', which if not given, is + taken from fp.name. If fp has no `name' attribute, `' is + used. + + """ + if filename is None: + try: + filename = fp.name + except AttributeError: + filename = '' + self._read(fp, filename) + + def get(self, section, option): + opt = self.optionxform(option) + if section not in self._sections: + if section != DEFAULTSECT: + raise NoSectionError(section) + if opt in self._defaults: + return self._defaults[opt] + else: + raise NoOptionError(option, section) + elif opt in self._sections[section]: + return self._sections[section][opt] + elif opt in self._defaults: + return self._defaults[opt] + else: + raise NoOptionError(option, section) + + def items(self, section): + try: + d2 = self._sections[section] + except KeyError: + if section != DEFAULTSECT: + raise NoSectionError(section) + d2 = {} + d = self._defaults.copy() + d.update(d2) + if "__name__" in d: + del d["__name__"] + return d.items() + + def _get(self, section, conv, option): + return conv(self.get(section, option)) + + def getint(self, section, option): + return self._get(section, int, option) + + def getfloat(self, section, option): + return self._get(section, float, option) + + _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True, + '0': False, 'no': False, 'false': False, 'off': False} + + def getboolean(self, section, option): + v = self.get(section, option) + if v.lower() not in self._boolean_states: + raise ValueError, 'Not a boolean: %s' % v + return self._boolean_states[v.lower()] + + def optionxform(self, optionstr): + return optionstr.lower() + + def has_option(self, section, option): + """Check for the existence of a given option in a given section.""" + if not section or section == DEFAULTSECT: + option = self.optionxform(option) + return option in self._defaults + elif section not in self._sections: + return False + else: + option = self.optionxform(option) + return (option in self._sections[section] + or option in self._defaults) + + def set(self, section, option, value): + """Set an option.""" + if not section or section == DEFAULTSECT: + sectdict = self._defaults + else: + try: + sectdict = self._sections[section] + except KeyError: + raise NoSectionError(section) + sectdict[self.optionxform(option)] = value + + def write(self, fp): + """Write an .ini-format representation of the configuration state.""" + if self._defaults: + fp.write("[%s]\n" % DEFAULTSECT) + for (key, value) in self._defaults.items(): + fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) + fp.write("\n") + for section in self._sections: + fp.write("[%s]\n" % section) + for (key, value) in self._sections[section].items(): + if key != "__name__": + fp.write("%s = %s\n" % + (key, str(value).replace('\n', '\n\t'))) + fp.write("\n") + + def remove_option(self, section, option): + """Remove an option.""" + if not section or section == DEFAULTSECT: + sectdict = self._defaults + else: + try: + sectdict = self._sections[section] + except KeyError: + raise NoSectionError(section) + option = self.optionxform(option) + existed = option in sectdict + if existed: + del sectdict[option] + return existed + + def remove_section(self, section): + """Remove a file section.""" + existed = section in self._sections + if existed: + del self._sections[section] + return existed + + # + # Regular expressions for parsing section headers and options. + # + SECTCRE = re.compile( + r'\[' # [ + r'(?P
    [^]]+)' # very permissive! + r'\]' # ] + ) + OPTCRE = re.compile( + r'(?P
    [^]]+)' # very permissive! + r'\]' # ] + ) + OPTCRE = re.compile( + r'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 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/atexit.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/atexit.py new file mode 100644 index 00000000..372876f2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/atexit.py @@ -0,0 +1,50 @@ +""" +atexit.py - allow programmer to define multiple exit functions to be executed +upon normal program termination. + +One public function, register, is defined. +""" + +__all__ = ["register"] + +_exithandlers = [] +def _run_exitfuncs(): + """run any registered exit functions + + _exithandlers is traversed in reverse order so functions are executed + last in, first out. + """ + + while _exithandlers: + func, targs, kargs = _exithandlers.pop() + apply(func, targs, kargs) + +def register(func, *targs, **kargs): + """register a function to be executed upon normal program termination + + func - function to be called at exit + targs - optional arguments to pass to func + kargs - optional keyword arguments to pass to func + """ + _exithandlers.append((func, targs, kargs)) + +import sys +if hasattr(sys, "exitfunc"): + # Assume it's another registered exit function - append it to our list + register(sys.exitfunc) +sys.exitfunc = _run_exitfuncs + +del sys + +if __name__ == "__main__": + def x1(): + print "running x1" + def x2(n): + print "running x2(%s)" % `n` + def x3(n, kwd=None): + print "running x3(%s, kwd=%s)" % (`n`, `kwd`) + + register(x1) + register(x2, 12) + register(x3, 5, "bar") + register(x3, "no kwd args") diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/bdb.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/bdb.py new file mode 100644 index 00000000..f4f83836 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/bdb.py @@ -0,0 +1,563 @@ +"""Debugger basics""" + +import sys +import os +import types + +__all__ = ["BdbQuit","Bdb","Breakpoint"] + +BdbQuit = 'bdb.BdbQuit' # Exception to give up completely + + +class Bdb: + + """Generic Python debugger base class. + + This class takes care of details of the trace facility; + a derived class should implement user interaction. + The standard debugger class (pdb.Pdb) is an example. + """ + + def __init__(self): + self.breaks = {} + self.fncache = {} + + def canonic(self, filename): + if filename == "<" + filename[1:-1] + ">": + return filename + canonic = self.fncache.get(filename) + if not canonic: + canonic = os.path.abspath(filename) + canonic = os.path.normcase(canonic) + self.fncache[filename] = canonic + return canonic + + def reset(self): + import linecache + linecache.checkcache() + self.botframe = None + self.stopframe = None + self.returnframe = None + self.quitting = 0 + + def trace_dispatch(self, frame, event, arg): + if self.quitting: + return # None + if event == 'line': + return self.dispatch_line(frame) + if event == 'call': + return self.dispatch_call(frame, arg) + if event == 'return': + return self.dispatch_return(frame, arg) + if event == 'exception': + return self.dispatch_exception(frame, arg) + print 'bdb.Bdb.dispatch: unknown debugging event:', `event` + return self.trace_dispatch + + def dispatch_line(self, frame): + if self.stop_here(frame) or self.break_here(frame): + self.user_line(frame) + if self.quitting: raise BdbQuit + return self.trace_dispatch + + def dispatch_call(self, frame, arg): + # XXX 'arg' is no longer used + if self.botframe is None: + # First call of dispatch since reset() + self.botframe = frame.f_back # (CT) Note that this may also be None! + return self.trace_dispatch + if not (self.stop_here(frame) or self.break_anywhere(frame)): + # No need to trace this function + return # None + self.user_call(frame, arg) + if self.quitting: raise BdbQuit + return self.trace_dispatch + + def dispatch_return(self, frame, arg): + if self.stop_here(frame) or frame == self.returnframe: + self.user_return(frame, arg) + if self.quitting: raise BdbQuit + return self.trace_dispatch + + def dispatch_exception(self, frame, arg): + if self.stop_here(frame): + self.user_exception(frame, arg) + if self.quitting: raise BdbQuit + return self.trace_dispatch + + # Normally derived classes don't override the following + # methods, but they may if they want to redefine the + # definition of stopping and breakpoints. + + def stop_here(self, frame): + # (CT) stopframe may now also be None, see dispatch_call. + # (CT) the former test for None is therefore removed from here. + if frame is self.stopframe: + return 1 + while frame is not None and frame is not self.stopframe: + if frame is self.botframe: + return 1 + frame = frame.f_back + return 0 + + def break_here(self, frame): + filename = self.canonic(frame.f_code.co_filename) + if not self.breaks.has_key(filename): + return 0 + lineno = frame.f_lineno + if not lineno in self.breaks[filename]: + return 0 + # flag says ok to delete temp. bp + (bp, flag) = effective(filename, lineno, frame) + if bp: + self.currentbp = bp.number + if (flag and bp.temporary): + self.do_clear(str(bp.number)) + return 1 + else: + return 0 + + def do_clear(self, arg): + raise NotImplementedError, "subclass of bdb must implement do_clear()" + + def break_anywhere(self, frame): + return self.breaks.has_key( + self.canonic(frame.f_code.co_filename)) + + # Derived classes should override the user_* methods + # to gain control. + + def user_call(self, frame, argument_list): + """This method is called when there is the remote possibility + that we ever need to stop in this function.""" + pass + + def user_line(self, frame): + """This method is called when we stop or break at this line.""" + pass + + def user_return(self, frame, return_value): + """This method is called when a return trap is set here.""" + pass + + def user_exception(self, frame, (exc_type, exc_value, exc_traceback)): + """This method is called if an exception occurs, + but only if we are to stop at or just below this level.""" + pass + + # Derived classes and clients can call the following methods + # to affect the stepping state. + + def set_step(self): + """Stop after one line of code.""" + self.stopframe = None + self.returnframe = None + self.quitting = 0 + + def set_next(self, frame): + """Stop on the next line in or below the given frame.""" + self.stopframe = frame + self.returnframe = None + self.quitting = 0 + + def set_return(self, frame): + """Stop when returning from the given frame.""" + self.stopframe = frame.f_back + self.returnframe = frame + self.quitting = 0 + + def set_trace(self): + """Start debugging from here.""" + frame = sys._getframe().f_back + self.reset() + while frame: + frame.f_trace = self.trace_dispatch + self.botframe = frame + frame = frame.f_back + self.set_step() + sys.settrace(self.trace_dispatch) + + def set_continue(self): + # Don't stop except at breakpoints or when finished + self.stopframe = self.botframe + self.returnframe = None + self.quitting = 0 + if not self.breaks: + # no breakpoints; run without debugger overhead + sys.settrace(None) + frame = sys._getframe().f_back + while frame and frame is not self.botframe: + del frame.f_trace + frame = frame.f_back + + def set_quit(self): + self.stopframe = self.botframe + self.returnframe = None + self.quitting = 1 + sys.settrace(None) + + # Derived classes and clients can call the following methods + # to manipulate breakpoints. These methods return an + # error message is something went wrong, None if all is well. + # Set_break prints out the breakpoint line and file:lineno. + # Call self.get_*break*() to see the breakpoints or better + # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint(). + + def set_break(self, filename, lineno, temporary=0, cond = None): + filename = self.canonic(filename) + import linecache # Import as late as possible + line = linecache.getline(filename, lineno) + if not line: + return 'Line %s:%d does not exist' % (filename, + lineno) + if not self.breaks.has_key(filename): + self.breaks[filename] = [] + list = self.breaks[filename] + if not lineno in list: + list.append(lineno) + bp = Breakpoint(filename, lineno, temporary, cond) + + def clear_break(self, filename, lineno): + filename = self.canonic(filename) + if not self.breaks.has_key(filename): + return 'There are no breakpoints in %s' % filename + if lineno not in self.breaks[filename]: + return 'There is no breakpoint at %s:%d' % (filename, + lineno) + # If there's only one bp in the list for that file,line + # pair, then remove the breaks entry + for bp in Breakpoint.bplist[filename, lineno][:]: + bp.deleteMe() + if not Breakpoint.bplist.has_key((filename, lineno)): + self.breaks[filename].remove(lineno) + if not self.breaks[filename]: + del self.breaks[filename] + + def clear_bpbynumber(self, arg): + try: + number = int(arg) + except: + return 'Non-numeric breakpoint number (%s)' % arg + try: + bp = Breakpoint.bpbynumber[number] + except IndexError: + return 'Breakpoint number (%d) out of range' % number + if not bp: + return 'Breakpoint (%d) already deleted' % number + self.clear_break(bp.file, bp.line) + + def clear_all_file_breaks(self, filename): + filename = self.canonic(filename) + if not self.breaks.has_key(filename): + return 'There are no breakpoints in %s' % filename + for line in self.breaks[filename]: + blist = Breakpoint.bplist[filename, line] + for bp in blist: + bp.deleteMe() + del self.breaks[filename] + + def clear_all_breaks(self): + if not self.breaks: + return 'There are no breakpoints' + for bp in Breakpoint.bpbynumber: + if bp: + bp.deleteMe() + self.breaks = {} + + def get_break(self, filename, lineno): + filename = self.canonic(filename) + return self.breaks.has_key(filename) and \ + lineno in self.breaks[filename] + + def get_breaks(self, filename, lineno): + filename = self.canonic(filename) + return self.breaks.has_key(filename) and \ + lineno in self.breaks[filename] and \ + Breakpoint.bplist[filename, lineno] or [] + + def get_file_breaks(self, filename): + filename = self.canonic(filename) + if self.breaks.has_key(filename): + return self.breaks[filename] + else: + return [] + + def get_all_breaks(self): + return self.breaks + + # Derived classes and clients can call the following method + # to get a data structure representing a stack trace. + + def get_stack(self, f, t): + stack = [] + if t and t.tb_frame is f: + t = t.tb_next + while f is not None: + stack.append((f, f.f_lineno)) + if f is self.botframe: + break + f = f.f_back + stack.reverse() + i = max(0, len(stack) - 1) + while t is not None: + stack.append((t.tb_frame, t.tb_lineno)) + t = t.tb_next + return stack, i + + # + + def format_stack_entry(self, frame_lineno, lprefix=': '): + import linecache, repr + frame, lineno = frame_lineno + filename = self.canonic(frame.f_code.co_filename) + s = filename + '(' + `lineno` + ')' + if frame.f_code.co_name: + s = s + frame.f_code.co_name + else: + s = s + "" + if frame.f_locals.has_key('__args__'): + args = frame.f_locals['__args__'] + else: + args = None + if args: + s = s + repr.repr(args) + else: + s = s + '()' + if frame.f_locals.has_key('__return__'): + rv = frame.f_locals['__return__'] + s = s + '->' + s = s + repr.repr(rv) + line = linecache.getline(filename, lineno) + if line: s = s + lprefix + line.strip() + return s + + # The following two methods can be called by clients to use + # a debugger to debug a statement, given as a string. + + def run(self, cmd, globals=None, locals=None): + if globals is None: + import __main__ + globals = __main__.__dict__ + if locals is None: + locals = globals + self.reset() + sys.settrace(self.trace_dispatch) + if not isinstance(cmd, types.CodeType): + cmd = cmd+'\n' + try: + try: + exec cmd in globals, locals + except BdbQuit: + pass + finally: + self.quitting = 1 + sys.settrace(None) + + def runeval(self, expr, globals=None, locals=None): + if globals is None: + import __main__ + globals = __main__.__dict__ + if locals is None: + locals = globals + self.reset() + sys.settrace(self.trace_dispatch) + if not isinstance(expr, types.CodeType): + expr = expr+'\n' + try: + try: + return eval(expr, globals, locals) + except BdbQuit: + pass + finally: + self.quitting = 1 + sys.settrace(None) + + def runctx(self, cmd, globals, locals): + # B/W compatibility + self.run(cmd, globals, locals) + + # This method is more useful to debug a single function call. + + def runcall(self, func, *args): + self.reset() + sys.settrace(self.trace_dispatch) + res = None + try: + try: + res = apply(func, args) + except BdbQuit: + pass + finally: + self.quitting = 1 + sys.settrace(None) + return res + + +def set_trace(): + Bdb().set_trace() + + +class Breakpoint: + + """Breakpoint class + + Implements temporary breakpoints, ignore counts, disabling and + (re)-enabling, and conditionals. + + Breakpoints are indexed by number through bpbynumber and by + the file,line tuple using bplist. The former points to a + single instance of class Breakpoint. The latter points to a + list of such instances since there may be more than one + breakpoint per line. + + """ + + # XXX Keeping state in the class is a mistake -- this means + # you cannot have more than one active Bdb instance. + + next = 1 # Next bp to be assigned + bplist = {} # indexed by (file, lineno) tuple + bpbynumber = [None] # Each entry is None or an instance of Bpt + # index 0 is unused, except for marking an + # effective break .... see effective() + + def __init__(self, file, line, temporary=0, cond = None): + self.file = file # This better be in canonical form! + self.line = line + self.temporary = temporary + self.cond = cond + self.enabled = 1 + self.ignore = 0 + self.hits = 0 + self.number = Breakpoint.next + Breakpoint.next = Breakpoint.next + 1 + # Build the two lists + self.bpbynumber.append(self) + if self.bplist.has_key((file, line)): + self.bplist[file, line].append(self) + else: + self.bplist[file, line] = [self] + + + def deleteMe(self): + index = (self.file, self.line) + self.bpbynumber[self.number] = None # No longer in list + self.bplist[index].remove(self) + if not self.bplist[index]: + # No more bp for this f:l combo + del self.bplist[index] + + def enable(self): + self.enabled = 1 + + def disable(self): + self.enabled = 0 + + def bpprint(self): + if self.temporary: + disp = 'del ' + else: + disp = 'keep ' + if self.enabled: + disp = disp + 'yes' + else: + disp = disp + 'no ' + print '%-4dbreakpoint %s at %s:%d' % (self.number, disp, + self.file, self.line) + if self.cond: + print '\tstop only if %s' % (self.cond,) + if self.ignore: + print '\tignore next %d hits' % (self.ignore) + if (self.hits): + if (self.hits > 1): ss = 's' + else: ss = '' + print ('\tbreakpoint already hit %d time%s' % + (self.hits, ss)) + +# -----------end of Breakpoint class---------- + +# Determines if there is an effective (active) breakpoint at this +# line of code. Returns breakpoint number or 0 if none +def effective(file, line, frame): + """Determine which breakpoint for this file:line is to be acted upon. + + Called only if we know there is a bpt at this + location. Returns breakpoint that was triggered and a flag + that indicates if it is ok to delete a temporary bp. + + """ + possibles = Breakpoint.bplist[file,line] + for i in range(0, len(possibles)): + b = possibles[i] + if b.enabled == 0: + continue + # Count every hit when bp is enabled + b.hits = b.hits + 1 + if not b.cond: + # If unconditional, and ignoring, + # go on to next, else break + if b.ignore > 0: + b.ignore = b.ignore -1 + continue + else: + # breakpoint and marker that's ok + # to delete if temporary + return (b,1) + else: + # Conditional bp. + # Ignore count applies only to those bpt hits where the + # condition evaluates to true. + try: + val = eval(b.cond, frame.f_globals, + frame.f_locals) + if val: + if b.ignore > 0: + b.ignore = b.ignore -1 + # continue + else: + return (b,1) + # else: + # continue + except: + # if eval fails, most conservative + # thing is to stop on breakpoint + # regardless of ignore count. + # Don't delete temporary, + # as another hint to user. + return (b,0) + return (None, None) + +# -------------------- testing -------------------- + +class Tdb(Bdb): + def user_call(self, frame, args): + name = frame.f_code.co_name + if not name: name = '???' + print '+++ call', name, args + def user_line(self, frame): + import linecache + name = frame.f_code.co_name + if not name: name = '???' + fn = self.canonic(frame.f_code.co_filename) + line = linecache.getline(fn, frame.f_lineno) + print '+++', fn, frame.f_lineno, name, ':', line.strip() + def user_return(self, frame, retval): + print '+++ return', retval + def user_exception(self, frame, exc_stuff): + print '+++ exception', exc_stuff + self.set_continue() + +def foo(n): + print 'foo(', n, ')' + x = bar(n*10) + print 'bar returned', x + +def bar(a): + print 'bar(', a, ')' + return a/2 + +def test(): + t = Tdb() + t.run('import bdb; bdb.foo(10)') + +# end diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/bisect.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/bisect.py new file mode 100644 index 00000000..5c1661ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/bisect.py @@ -0,0 +1,78 @@ +"""Bisection algorithms.""" + +def insort_right(a, x, lo=0, hi=None): + """Insert item x in list a, and keep it sorted assuming a is sorted. + + If x is already in a, insert it to the right of the rightmost x. + + Optional args lo (default 0) and hi (default len(a)) bound the + slice of a to be searched. + """ + + if hi is None: + hi = len(a) + while lo < hi: + mid = (lo+hi)//2 + if x < a[mid]: hi = mid + else: lo = mid+1 + a.insert(lo, x) + +insort = insort_right # backward compatibility + +def bisect_right(a, x, lo=0, hi=None): + """Return the index where to insert item x in list a, assuming a is sorted. + + The return value i is such that all e in a[:i] have e <= x, and all e in + a[i:] have e > x. So if x already appears in the list, i points just + beyond the rightmost x already there. + + Optional args lo (default 0) and hi (default len(a)) bound the + slice of a to be searched. + """ + + if hi is None: + hi = len(a) + while lo < hi: + mid = (lo+hi)//2 + if x < a[mid]: hi = mid + else: lo = mid+1 + return lo + +bisect = bisect_right # backward compatibility + +def insort_left(a, x, lo=0, hi=None): + """Insert item x in list a, and keep it sorted assuming a is sorted. + + If x is already in a, insert it to the left of the leftmost x. + + Optional args lo (default 0) and hi (default len(a)) bound the + slice of a to be searched. + """ + + if hi is None: + hi = len(a) + while lo < hi: + mid = (lo+hi)//2 + if a[mid] < x: lo = mid+1 + else: hi = mid + a.insert(lo, x) + + +def bisect_left(a, x, lo=0, hi=None): + """Return the index where to insert item x in list a, assuming a is sorted. + + The return value i is such that all e in a[:i] have e < x, and all e in + a[i:] have e >= x. So if x already appears in the list, i points just + before the leftmost x already there. + + Optional args lo (default 0) and hi (default len(a)) bound the + slice of a to be searched. + """ + + if hi is None: + hi = len(a) + while lo < hi: + mid = (lo+hi)//2 + if a[mid] < x: lo = mid+1 + else: hi = mid + return lo diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/calendar.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/calendar.py new file mode 100644 index 00000000..83e6274b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/calendar.py @@ -0,0 +1,246 @@ +"""Calendar printing functions + +Note when comparing these calendars to the ones printed by cal(1): By +default, these calendars have Monday as the first day of the week, and +Sunday as the last (the European convention). Use setfirstweekday() to +set the first day of the week (0=Monday, 6=Sunday).""" + +# Revision 2: uses functions from built-in time module + +# Import functions and variables from time module +from time import localtime, mktime, strftime +from types import SliceType + +__all__ = ["error","setfirstweekday","firstweekday","isleap", + "leapdays","weekday","monthrange","monthcalendar", + "prmonth","month","prcal","calendar","timegm", + "month_name", "month_abbr", "day_name", "day_abbr"] + +# Exception raised for bad input (with string parameter for details) +error = ValueError + +# Constants for months referenced later +January = 1 +February = 2 + +# Number of days per month (except for February in leap years) +mdays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + +# This module used to have hard-coded lists of day and month names, as +# English strings. The classes following emulate a read-only version of +# that, but supply localized names. Note that the values are computed +# fresh on each call, in case the user changes locale between calls. + +class _indexer: + def __getitem__(self, i): + if isinstance(i, SliceType): + return self.data[i.start : i.stop] + else: + # May raise an appropriate exception. + return self.data[i] + +class _localized_month(_indexer): + def __init__(self, format): + self.format = format + + def __getitem__(self, i): + self.data = [strftime(self.format, (2001, j, 1, 12, 0, 0, 1, 1, 0)) + for j in range(1, 13)] + self.data.insert(0, "") + return _indexer.__getitem__(self, i) + + def __len__(self): + return 13 + +class _localized_day(_indexer): + def __init__(self, format): + self.format = format + + def __getitem__(self, i): + # January 1, 2001, was a Monday. + self.data = [strftime(self.format, (2001, 1, j+1, 12, 0, 0, j, j+1, 0)) + for j in range(7)] + return _indexer.__getitem__(self, i) + + def __len__(self_): + return 7 + +# Full and abbreviated names of weekdays +day_name = _localized_day('%A') +day_abbr = _localized_day('%a') + +# Full and abbreviated names of months (1-based arrays!!!) +month_name = _localized_month('%B') +month_abbr = _localized_month('%b') + +# Constants for weekdays +(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7) + +_firstweekday = 0 # 0 = Monday, 6 = Sunday + +def firstweekday(): + return _firstweekday + +def setfirstweekday(weekday): + """Set weekday (Monday=0, Sunday=6) to start each week.""" + global _firstweekday + if not MONDAY <= weekday <= SUNDAY: + raise ValueError, \ + 'bad weekday number; must be 0 (Monday) to 6 (Sunday)' + _firstweekday = weekday + +def isleap(year): + """Return 1 for leap years, 0 for non-leap years.""" + return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) + +def leapdays(y1, y2): + """Return number of leap years in range [y1, y2). + Assume y1 <= y2.""" + y1 -= 1 + y2 -= 1 + return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400) + +def weekday(year, month, day): + """Return weekday (0-6 ~ Mon-Sun) for year (1970-...), month (1-12), + day (1-31).""" + secs = mktime((year, month, day, 0, 0, 0, 0, 0, 0)) + tuple = localtime(secs) + return tuple[6] + +def monthrange(year, month): + """Return weekday (0-6 ~ Mon-Sun) and number of days (28-31) for + year, month.""" + if not 1 <= month <= 12: + raise ValueError, 'bad month number' + day1 = weekday(year, month, 1) + ndays = mdays[month] + (month == February and isleap(year)) + return day1, ndays + +def monthcalendar(year, month): + """Return a matrix representing a month's calendar. + Each row represents a week; days outside this month are zero.""" + day1, ndays = monthrange(year, month) + rows = [] + r7 = range(7) + day = (_firstweekday - day1 + 6) % 7 - 5 # for leading 0's in first week + while day <= ndays: + row = [0, 0, 0, 0, 0, 0, 0] + for i in r7: + if 1 <= day <= ndays: row[i] = day + day = day + 1 + rows.append(row) + return rows + +def _center(str, width): + """Center a string in a field.""" + n = width - len(str) + if n <= 0: + return str + return ' '*((n+1)/2) + str + ' '*((n)/2) + +def prweek(theweek, width): + """Print a single week (no newline).""" + print week(theweek, width), + +def week(theweek, width): + """Returns a single week in a string (no newline).""" + days = [] + for day in theweek: + if day == 0: + s = '' + else: + s = '%2i' % day # right-align single-digit days + days.append(_center(s, width)) + return ' '.join(days) + +def weekheader(width): + """Return a header for a week.""" + if width >= 9: + names = day_name + else: + names = day_abbr + days = [] + for i in range(_firstweekday, _firstweekday + 7): + days.append(_center(names[i%7][:width], width)) + return ' '.join(days) + +def prmonth(theyear, themonth, w=0, l=0): + """Print a month's calendar.""" + print month(theyear, themonth, w, l), + +def month(theyear, themonth, w=0, l=0): + """Return a month's calendar string (multi-line).""" + w = max(2, w) + l = max(1, l) + s = (_center(month_name[themonth] + ' ' + `theyear`, + 7 * (w + 1) - 1).rstrip() + + '\n' * l + weekheader(w).rstrip() + '\n' * l) + for aweek in monthcalendar(theyear, themonth): + s = s + week(aweek, w).rstrip() + '\n' * l + return s[:-l] + '\n' + +# Spacing of month columns for 3-column year calendar +_colwidth = 7*3 - 1 # Amount printed by prweek() +_spacing = 6 # Number of spaces between columns + +def format3c(a, b, c, colwidth=_colwidth, spacing=_spacing): + """Prints 3-column formatting for year calendars""" + print format3cstring(a, b, c, colwidth, spacing) + +def format3cstring(a, b, c, colwidth=_colwidth, spacing=_spacing): + """Returns a string formatted from 3 strings, centered within 3 columns.""" + return (_center(a, colwidth) + ' ' * spacing + _center(b, colwidth) + + ' ' * spacing + _center(c, colwidth)) + +def prcal(year, w=0, l=0, c=_spacing): + """Print a year's calendar.""" + print calendar(year, w, l, c), + +def calendar(year, w=0, l=0, c=_spacing): + """Returns a year's calendar as a multi-line string.""" + w = max(2, w) + l = max(1, l) + c = max(2, c) + colwidth = (w + 1) * 7 - 1 + s = _center(`year`, colwidth * 3 + c * 2).rstrip() + '\n' * l + header = weekheader(w) + header = format3cstring(header, header, header, colwidth, c).rstrip() + for q in range(January, January+12, 3): + s = (s + '\n' * l + + format3cstring(month_name[q], month_name[q+1], month_name[q+2], + colwidth, c).rstrip() + + '\n' * l + header + '\n' * l) + data = [] + height = 0 + for amonth in range(q, q + 3): + cal = monthcalendar(year, amonth) + if len(cal) > height: + height = len(cal) + data.append(cal) + for i in range(height): + weeks = [] + for cal in data: + if i >= len(cal): + weeks.append('') + else: + weeks.append(week(cal[i], w)) + s = s + format3cstring(weeks[0], weeks[1], weeks[2], + colwidth, c).rstrip() + '\n' * l + return s[:-l] + '\n' + +EPOCH = 1970 +def timegm(tuple): + """Unrelated but handy function to calculate Unix timestamp from GMT.""" + year, month, day, hour, minute, second = tuple[:6] + assert year >= EPOCH + assert 1 <= month <= 12 + days = 365*(year-EPOCH) + leapdays(EPOCH, year) + for i in range(1, month): + days = days + mdays[i] + if month > 2 and isleap(year): + days = days + 1 + days = days + day - 1 + hours = days*24 + hour + minutes = hours*60 + minute + seconds = minutes*60 + second + return seconds diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/cmd.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/cmd.py new file mode 100644 index 00000000..1fb752d4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/cmd.py @@ -0,0 +1,330 @@ +"""A generic class to build line-oriented command interpreters. + +Interpreters constructed with this class obey the following conventions: + +1. End of file on input is processed as the command 'EOF'. +2. A command is parsed out of each line by collecting the prefix composed + of characters in the identchars member. +3. A command `foo' is dispatched to a method 'do_foo()'; the do_ method + is passed a single argument consisting of the remainder of the line. +4. Typing an empty line repeats the last command. (Actually, it calls the + method `emptyline', which may be overridden in a subclass.) +5. There is a predefined `help' method. Given an argument `topic', it + calls the command `help_topic'. With no arguments, it lists all topics + with defined help_ functions, broken into up to three topics; documented + commands, miscellaneous help topics, and undocumented commands. +6. The command '?' is a synonym for `help'. The command '!' is a synonym + for `shell', if a do_shell method exists. +7. If completion is enabled, completing commands will be done automatically, + and completing of commands args is done by calling complete_foo() with + arguments text, line, begidx, endidx. text is string we are matching + against, all returned matches must begin with it. line is the current + input line (lstripped), begidx and endidx are the beginning and end + indexes of the text being matched, which could be used to provide + different completion depending upon which position the argument is in. + +The `default' method may be overridden to intercept commands for which there +is no do_ method. + +The `completedefault' method may be overridden to intercept completions for +commands that have no complete_ method. + +The data member `self.ruler' sets the character used to draw separator lines +in the help messages. If empty, no ruler line is drawn. It defaults to "=". + +If the value of `self.intro' is nonempty when the cmdloop method is called, +it is printed out on interpreter startup. This value may be overridden +via an optional argument to the cmdloop() method. + +The data members `self.doc_header', `self.misc_header', and +`self.undoc_header' set the headers used for the help function's +listings of documented functions, miscellaneous topics, and undocumented +functions respectively. + +These interpreters use raw_input; thus, if the readline module is loaded, +they automatically support Emacs-like command history and editing features. +""" + +import string, sys + +__all__ = ["Cmd"] + +PROMPT = '(Cmd) ' +IDENTCHARS = string.ascii_letters + string.digits + '_' + +class Cmd: + """A simple framework for writing line-oriented command interpreters. + + These are often useful for test harnesses, administrative tools, and + prototypes that will later be wrapped in a more sophisticated interface. + + A Cmd instance or subclass instance is a line-oriented interpreter + framework. There is no good reason to instantiate Cmd itself; rather, + it's useful as a superclass of an interpreter class you define yourself + in order to inherit Cmd's methods and encapsulate action methods. + + """ + prompt = PROMPT + identchars = IDENTCHARS + ruler = '=' + lastcmd = '' + cmdqueue = [] + intro = None + doc_leader = "" + doc_header = "Documented commands (type help ):" + misc_header = "Miscellaneous help topics:" + undoc_header = "Undocumented commands:" + nohelp = "*** No help on %s" + use_rawinput = 1 + + def __init__(self, completekey='tab'): + """Instantiate a line-oriented interpreter framework. + + The optional argument is the readline name of a completion key; + it defaults to the Tab key. If completekey is not None and the + readline module is available, command completion is done + automatically. + + """ + if completekey: + try: + import readline + readline.set_completer(self.complete) + readline.parse_and_bind(completekey+": complete") + except ImportError: + pass + + def cmdloop(self, intro=None): + """Repeatedly issue a prompt, accept input, parse an initial prefix + off the received input, and dispatch to action methods, passing them + the remainder of the line as argument. + + """ + + self.preloop() + if intro is not None: + self.intro = intro + if self.intro: + print self.intro + stop = None + while not stop: + if self.cmdqueue: + line = self.cmdqueue[0] + del self.cmdqueue[0] + else: + if self.use_rawinput: + try: + line = raw_input(self.prompt) + except EOFError: + line = 'EOF' + else: + sys.stdout.write(self.prompt) + sys.stdout.flush() + line = sys.stdin.readline() + if not len(line): + line = 'EOF' + else: + line = line[:-1] # chop \n + line = self.precmd(line) + stop = self.onecmd(line) + stop = self.postcmd(stop, line) + self.postloop() + + def precmd(self, line): + """Hook method executed just before the command line is + interpreted, but after the input prompt is generated and issued. + + """ + return line + + def postcmd(self, stop, line): + """Hook method executed just after a command dispatch is finished.""" + return stop + + def preloop(self): + """Hook method executed once when the cmdloop() method is called.""" + pass + + def postloop(self): + """Hook method executed once when the cmdloop() method is about to + return. + + """ + pass + + def parseline(self, line): + line = line.strip() + if not line: + return None, None, line + elif line[0] == '?': + line = 'help ' + line[1:] + elif line[0] == '!': + if hasattr(self, 'do_shell'): + line = 'shell ' + line[1:] + else: + return None, None, line + i, n = 0, len(line) + while i < n and line[i] in self.identchars: i = i+1 + cmd, arg = line[:i], line[i:].strip() + return cmd, arg, line + + def onecmd(self, line): + """Interpret the argument as though it had been typed in response + to the prompt. + + This may be overridden, but should not normally need to be; + see the precmd() and postcmd() methods for useful execution hooks. + The return value is a flag indicating whether interpretation of + commands by the interpreter should stop. + + """ + cmd, arg, line = self.parseline(line) + if not line: + return self.emptyline() + if cmd is None: + return self.default(line) + self.lastcmd = line + if cmd == '': + return self.default(line) + else: + try: + func = getattr(self, 'do_' + cmd) + except AttributeError: + return self.default(line) + return func(arg) + + def emptyline(self): + """Called when an empty line is entered in response to the prompt. + + If this method is not overridden, it repeats the last nonempty + command entered. + + """ + if self.lastcmd: + return self.onecmd(self.lastcmd) + + def default(self, line): + """Called on an input line when the command prefix is not recognized. + + If this method is not overridden, it prints an error message and + returns. + + """ + print '*** Unknown syntax:', line + + def completedefault(self, *ignored): + """Method called to complete an input line when no command-specific + complete_*() method is available. + + By default, it returns an empty list. + + """ + return [] + + def completenames(self, text, *ignored): + dotext = 'do_'+text + return [a[3:] for a in self.get_names() if a.startswith(dotext)] + + def complete(self, text, state): + """Return the next possible completion for 'text'. + + If a command has not been entered, then complete against command list. + Otherwise try to call complete_ to get list of completions. + """ + if state == 0: + import readline + origline = readline.get_line_buffer() + line = origline.lstrip() + stripped = len(origline) - len(line) + begidx = readline.get_begidx() - stripped + endidx = readline.get_endidx() - stripped + if begidx>0: + cmd, args, foo = self.parseline(line) + if cmd == '': + compfunc = self.completedefault + else: + try: + compfunc = getattr(self, 'complete_' + cmd) + except AttributeError: + compfunc = self.completedefault + else: + compfunc = self.completenames + self.completion_matches = compfunc(text, line, begidx, endidx) + try: + return self.completion_matches[state] + except IndexError: + return None + + def get_names(self): + # Inheritance says we have to look in class and + # base classes; order is not important. + names = [] + classes = [self.__class__] + while classes: + aclass = classes[0] + if aclass.__bases__: + classes = classes + list(aclass.__bases__) + names = names + dir(aclass) + del classes[0] + return names + + def complete_help(self, *args): + return self.completenames(*args) + + def do_help(self, arg): + if arg: + # XXX check arg syntax + try: + func = getattr(self, 'help_' + arg) + except: + try: + doc=getattr(self, 'do_' + arg).__doc__ + if doc: + print doc + return + except: + pass + print self.nohelp % (arg,) + return + func() + else: + names = self.get_names() + cmds_doc = [] + cmds_undoc = [] + help = {} + for name in names: + if name[:5] == 'help_': + help[name[5:]]=1 + names.sort() + # There can be duplicates if routines overridden + prevname = '' + for name in names: + if name[:3] == 'do_': + if name == prevname: + continue + prevname = name + cmd=name[3:] + if help.has_key(cmd): + cmds_doc.append(cmd) + del help[cmd] + elif getattr(self, name).__doc__: + cmds_doc.append(cmd) + else: + cmds_undoc.append(cmd) + print self.doc_leader + self.print_topics(self.doc_header, cmds_doc, 15,80) + self.print_topics(self.misc_header, help.keys(),15,80) + self.print_topics(self.undoc_header, cmds_undoc, 15,80) + + def print_topics(self, header, cmds, cmdlen, maxcol): + if cmds: + print header + if self.ruler: + print self.ruler * len(header) + (cmds_per_line,junk)=divmod(maxcol,cmdlen) + col=cmds_per_line + for cmd in cmds: + if col==0: print + print (("%-"+`cmdlen`+"s") % cmd), + col = (col+1) % cmds_per_line + print "\n" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/codecs.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/codecs.py new file mode 100644 index 00000000..c81cfff9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/codecs.py @@ -0,0 +1,636 @@ +""" codecs -- Python Codec Registry, API and helpers. + + +Written by Marc-Andre Lemburg (mal@lemburg.com). + +(c) Copyright CNRI, All Rights Reserved. NO WARRANTY. + +"""#" + +import struct, __builtin__ + +### Registry and builtin stateless codec functions + +try: + from _codecs import * +except ImportError, why: + raise SystemError,\ + 'Failed to load the builtin codecs: %s' % why + +__all__ = ["register", "lookup", "open", "EncodedFile", "BOM", "BOM_BE", + "BOM_LE", "BOM32_BE", "BOM32_LE", "BOM64_BE", "BOM64_LE"] + +### Constants + +# +# Byte Order Mark (BOM) and its possible values (BOM_BE, BOM_LE) +# +BOM = struct.pack('=H', 0xFEFF) +# +BOM_BE = BOM32_BE = '\376\377' +# corresponds to Unicode U+FEFF in UTF-16 on big endian +# platforms == ZERO WIDTH NO-BREAK SPACE +BOM_LE = BOM32_LE = '\377\376' +# corresponds to Unicode U+FFFE in UTF-16 on little endian +# platforms == defined as being an illegal Unicode character + +# +# 64-bit Byte Order Marks +# +BOM64_BE = '\000\000\376\377' +# corresponds to Unicode U+0000FEFF in UCS-4 +BOM64_LE = '\377\376\000\000' +# corresponds to Unicode U+0000FFFE in UCS-4 + + +### Codec base classes (defining the API) + +class Codec: + + """ Defines the interface for stateless encoders/decoders. + + The .encode()/.decode() methods may implement different error + handling schemes by providing the errors argument. These + string values are defined: + + 'strict' - raise a ValueError error (or a subclass) + 'ignore' - ignore the character and continue with the next + 'replace' - replace with a suitable replacement character; + Python will use the official U+FFFD REPLACEMENT + CHARACTER for the builtin Unicode codecs. + + """ + def encode(self, input, errors='strict'): + + """ Encodes the object input and returns a tuple (output + object, length consumed). + + errors defines the error handling to apply. It defaults to + 'strict' handling. + + The method may not store state in the Codec instance. Use + StreamCodec for codecs which have to keep state in order to + make encoding/decoding efficient. + + The encoder must be able to handle zero length input and + return an empty object of the output object type in this + situation. + + """ + raise NotImplementedError + + def decode(self, input, errors='strict'): + + """ Decodes the object input and returns a tuple (output + object, length consumed). + + input must be an object which provides the bf_getreadbuf + buffer slot. Python strings, buffer objects and memory + mapped files are examples of objects providing this slot. + + errors defines the error handling to apply. It defaults to + 'strict' handling. + + The method may not store state in the Codec instance. Use + StreamCodec for codecs which have to keep state in order to + make encoding/decoding efficient. + + The decoder must be able to handle zero length input and + return an empty object of the output object type in this + situation. + + """ + raise NotImplementedError + +# +# The StreamWriter and StreamReader class provide generic working +# interfaces which can be used to implement new encoding submodules +# very easily. See encodings/utf_8.py for an example on how this is +# done. +# + +class StreamWriter(Codec): + + def __init__(self, stream, errors='strict'): + + """ Creates a StreamWriter instance. + + stream must be a file-like object open for writing + (binary) data. + + The StreamWriter may implement different error handling + schemes by providing the errors keyword argument. These + parameters are defined: + + 'strict' - raise a ValueError (or a subclass) + 'ignore' - ignore the character and continue with the next + 'replace'- replace with a suitable replacement character + + """ + self.stream = stream + self.errors = errors + + def write(self, object): + + """ Writes the object's contents encoded to self.stream. + """ + data, consumed = self.encode(object, self.errors) + self.stream.write(data) + + def writelines(self, list): + + """ Writes the concatenated list of strings to the stream + using .write(). + """ + self.write(''.join(list)) + + def reset(self): + + """ Flushes and resets the codec buffers used for keeping state. + + Calling this method should ensure that the data on the + output is put into a clean state, that allows appending + of new fresh data without having to rescan the whole + stream to recover state. + + """ + pass + + def __getattr__(self, name, + getattr=getattr): + + """ Inherit all other methods from the underlying stream. + """ + return getattr(self.stream, name) + +### + +class StreamReader(Codec): + + def __init__(self, stream, errors='strict'): + + """ Creates a StreamReader instance. + + stream must be a file-like object open for reading + (binary) data. + + The StreamReader may implement different error handling + schemes by providing the errors keyword argument. These + parameters are defined: + + 'strict' - raise a ValueError (or a subclass) + 'ignore' - ignore the character and continue with the next + 'replace'- replace with a suitable replacement character; + + """ + self.stream = stream + self.errors = errors + + def read(self, size=-1): + + """ Decodes data from the stream self.stream and returns the + resulting object. + + size indicates the approximate maximum number of bytes to + read from the stream for decoding purposes. The decoder + can modify this setting as appropriate. The default value + -1 indicates to read and decode as much as possible. size + is intended to prevent having to decode huge files in one + step. + + The method should use a greedy read strategy meaning that + it should read as much data as is allowed within the + definition of the encoding and the given size, e.g. if + optional encoding endings or state markers are available + on the stream, these should be read too. + + """ + # Unsliced reading: + if size < 0: + return self.decode(self.stream.read(), self.errors)[0] + + # Sliced reading: + read = self.stream.read + decode = self.decode + data = read(size) + i = 0 + while 1: + try: + object, decodedbytes = decode(data, self.errors) + except ValueError, why: + # This method is slow but should work under pretty much + # all conditions; at most 10 tries are made + i = i + 1 + newdata = read(1) + if not newdata or i > 10: + raise + data = data + newdata + else: + return object + + def readline(self, size=None): + + """ Read one line from the input stream and return the + decoded data. + + Note: Unlike the .readlines() method, this method inherits + the line breaking knowledge from the underlying stream's + .readline() method -- there is currently no support for + line breaking using the codec decoder due to lack of line + buffering. Sublcasses should however, if possible, try to + implement this method using their own knowledge of line + breaking. + + size, if given, is passed as size argument to the stream's + .readline() method. + + """ + if size is None: + line = self.stream.readline() + else: + line = self.stream.readline(size) + return self.decode(line, self.errors)[0] + + + def readlines(self, sizehint=None): + + """ Read all lines available on the input stream + and return them as list of lines. + + Line breaks are implemented using the codec's decoder + method and are included in the list entries. + + sizehint, if given, is passed as size argument to the + stream's .read() method. + + """ + if sizehint is None: + data = self.stream.read() + else: + data = self.stream.read(sizehint) + return self.decode(data, self.errors)[0].splitlines(1) + + def reset(self): + + """ Resets the codec buffers used for keeping state. + + Note that no stream repositioning should take place. + This method is primarily intended to be able to recover + from decoding errors. + + """ + pass + + def __getattr__(self, name, + getattr=getattr): + + """ Inherit all other methods from the underlying stream. + """ + return getattr(self.stream, name) + +### + +class StreamReaderWriter: + + """ StreamReaderWriter instances allow wrapping streams which + work in both read and write modes. + + The design is such that one can use the factory functions + returned by the codec.lookup() function to construct the + instance. + + """ + # Optional attributes set by the file wrappers below + encoding = 'unknown' + + def __init__(self, stream, Reader, Writer, errors='strict'): + + """ Creates a StreamReaderWriter instance. + + stream must be a Stream-like object. + + Reader, Writer must be factory functions or classes + providing the StreamReader, StreamWriter interface resp. + + Error handling is done in the same way as defined for the + StreamWriter/Readers. + + """ + self.stream = stream + self.reader = Reader(stream, errors) + self.writer = Writer(stream, errors) + self.errors = errors + + def read(self, size=-1): + + return self.reader.read(size) + + def readline(self, size=None): + + return self.reader.readline(size) + + def readlines(self, sizehint=None): + + return self.reader.readlines(sizehint) + + def write(self, data): + + return self.writer.write(data) + + def writelines(self, list): + + return self.writer.writelines(list) + + def reset(self): + + self.reader.reset() + self.writer.reset() + + def __getattr__(self, name, + getattr=getattr): + + """ Inherit all other methods from the underlying stream. + """ + return getattr(self.stream, name) + +### + +class StreamRecoder: + + """ StreamRecoder instances provide a frontend - backend + view of encoding data. + + They use the complete set of APIs returned by the + codecs.lookup() function to implement their task. + + Data written to the stream is first decoded into an + intermediate format (which is dependent on the given codec + combination) and then written to the stream using an instance + of the provided Writer class. + + In the other direction, data is read from the stream using a + Reader instance and then return encoded data to the caller. + + """ + # Optional attributes set by the file wrappers below + data_encoding = 'unknown' + file_encoding = 'unknown' + + def __init__(self, stream, encode, decode, Reader, Writer, + errors='strict'): + + """ Creates a StreamRecoder instance which implements a two-way + conversion: encode and decode work on the frontend (the + input to .read() and output of .write()) while + Reader and Writer work on the backend (reading and + writing to the stream). + + You can use these objects to do transparent direct + recodings from e.g. latin-1 to utf-8 and back. + + stream must be a file-like object. + + encode, decode must adhere to the Codec interface, Reader, + Writer must be factory functions or classes providing the + StreamReader, StreamWriter interface resp. + + encode and decode are needed for the frontend translation, + Reader and Writer for the backend translation. Unicode is + used as intermediate encoding. + + Error handling is done in the same way as defined for the + StreamWriter/Readers. + + """ + self.stream = stream + self.encode = encode + self.decode = decode + self.reader = Reader(stream, errors) + self.writer = Writer(stream, errors) + self.errors = errors + + def read(self, size=-1): + + data = self.reader.read(size) + data, bytesencoded = self.encode(data, self.errors) + return data + + def readline(self, size=None): + + if size is None: + data = self.reader.readline() + else: + data = self.reader.readline(size) + data, bytesencoded = self.encode(data, self.errors) + return data + + def readlines(self, sizehint=None): + + if sizehint is None: + data = self.reader.read() + else: + data = self.reader.read(sizehint) + data, bytesencoded = self.encode(data, self.errors) + return data.splitlines(1) + + def write(self, data): + + data, bytesdecoded = self.decode(data, self.errors) + return self.writer.write(data) + + def writelines(self, list): + + data = ''.join(list) + data, bytesdecoded = self.decode(data, self.errors) + return self.writer.write(data) + + def reset(self): + + self.reader.reset() + self.writer.reset() + + def __getattr__(self, name, + getattr=getattr): + + """ Inherit all other methods from the underlying stream. + """ + return getattr(self.stream, name) + +### Shortcuts + +def open(filename, mode='rb', encoding=None, errors='strict', buffering=1): + + """ Open an encoded file using the given mode and return + a wrapped version providing transparent encoding/decoding. + + Note: The wrapped version will only accept the object format + defined by the codecs, i.e. Unicode objects for most builtin + codecs. Output is also codec dependent and will usually by + Unicode as well. + + Files are always opened in binary mode, even if no binary mode + was specified. Thisis done to avoid data loss due to encodings + using 8-bit values. The default file mode is 'rb' meaning to + open the file in binary read mode. + + encoding specifies the encoding which is to be used for the + the file. + + errors may be given to define the error handling. It defaults + to 'strict' which causes ValueErrors to be raised in case an + encoding error occurs. + + buffering has the same meaning as for the builtin open() API. + It defaults to line buffered. + + The returned wrapped file object provides an extra attribute + .encoding which allows querying the used encoding. This + attribute is only available if an encoding was specified as + parameter. + + """ + if encoding is not None and \ + 'b' not in mode: + # Force opening of the file in binary mode + mode = mode + 'b' + file = __builtin__.open(filename, mode, buffering) + if encoding is None: + return file + (e, d, sr, sw) = lookup(encoding) + srw = StreamReaderWriter(file, sr, sw, errors) + # Add attributes to simplify introspection + srw.encoding = encoding + return srw + +def EncodedFile(file, data_encoding, file_encoding=None, errors='strict'): + + """ Return a wrapped version of file which provides transparent + encoding translation. + + Strings written to the wrapped file are interpreted according + to the given data_encoding and then written to the original + file as string using file_encoding. The intermediate encoding + will usually be Unicode but depends on the specified codecs. + + Strings are read from the file using file_encoding and then + passed back to the caller as string using data_encoding. + + If file_encoding is not given, it defaults to data_encoding. + + errors may be given to define the error handling. It defaults + to 'strict' which causes ValueErrors to be raised in case an + encoding error occurs. + + The returned wrapped file object provides two extra attributes + .data_encoding and .file_encoding which reflect the given + parameters of the same name. The attributes can be used for + introspection by Python programs. + + """ + if file_encoding is None: + file_encoding = data_encoding + encode, decode = lookup(data_encoding)[:2] + Reader, Writer = lookup(file_encoding)[2:] + sr = StreamRecoder(file, + encode, decode, Reader, Writer, + errors) + # Add attributes to simplify introspection + sr.data_encoding = data_encoding + sr.file_encoding = file_encoding + return sr + +### Helpers for codec lookup + +def getencoder(encoding): + + """ Lookup up the codec for the given encoding and return + its encoder function. + + Raises a LookupError in case the encoding cannot be found. + + """ + return lookup(encoding)[0] + +def getdecoder(encoding): + + """ Lookup up the codec for the given encoding and return + its decoder function. + + Raises a LookupError in case the encoding cannot be found. + + """ + return lookup(encoding)[1] + +def getreader(encoding): + + """ Lookup up the codec for the given encoding and return + its StreamReader class or factory function. + + Raises a LookupError in case the encoding cannot be found. + + """ + return lookup(encoding)[2] + +def getwriter(encoding): + + """ Lookup up the codec for the given encoding and return + its StreamWriter class or factory function. + + Raises a LookupError in case the encoding cannot be found. + + """ + return lookup(encoding)[3] + +### Helpers for charmap-based codecs + +def make_identity_dict(rng): + + """ make_identity_dict(rng) -> dict + + Return a dictionary where elements of the rng sequence are + mapped to themselves. + + """ + res = {} + for i in rng: + res[i]=i + return res + +def make_encoding_map(decoding_map): + + """ Creates an encoding map from a decoding map. + + If a target mapping in the decoding map occurrs multiple + times, then that target is mapped to None (undefined mapping), + causing an exception when encountered by the charmap codec + during translation. + + One example where this happens is cp875.py which decodes + multiple character to \u001a. + + """ + m = {} + for k,v in decoding_map.items(): + if not m.has_key(v): + m[v] = k + else: + m[v] = None + return m + +# Tell modulefinder that using codecs probably needs the encodings +# package +_false = 0 +if _false: + import encodings + +### Tests + +if __name__ == '__main__': + + import sys + + # Make stdout translate Latin-1 output into UTF-8 output + sys.stdout = EncodedFile(sys.stdout, 'latin-1', 'utf-8') + + # Have stdin translate Latin-1 input into UTF-8 input + sys.stdin = EncodedFile(sys.stdin, 'utf-8', 'latin-1') diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/colorsys.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/colorsys.py new file mode 100644 index 00000000..4e8c13c9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/colorsys.py @@ -0,0 +1,123 @@ +"""Conversion functions between RGB and other color systems. + +This modules provides two functions for each color system ABC: + + rgb_to_abc(r, g, b) --> a, b, c + abc_to_rgb(a, b, c) --> r, g, b + +All inputs and outputs are triples of floats in the range [0.0...1.0]. +Inputs outside this range may cause exceptions or invalid outputs. + +Supported color systems: +RGB: Red, Green, Blue components +YIQ: used by composite video signals +HLS: Hue, Luminance, Saturation +HSV: Hue, Saturation, Value +""" +# References: +# XXX Where's the literature? + +__all__ = ["rgb_to_yiq","yiq_to_rgb","rgb_to_hls","hls_to_rgb", + "rgb_to_hsv","hsv_to_rgb"] + +# Some floating point constants + +ONE_THIRD = 1.0/3.0 +ONE_SIXTH = 1.0/6.0 +TWO_THIRD = 2.0/3.0 + + +# YIQ: used by composite video signals (linear combinations of RGB) +# Y: perceived grey level (0.0 == black, 1.0 == white) +# I, Q: color components + +def rgb_to_yiq(r, g, b): + y = 0.30*r + 0.59*g + 0.11*b + i = 0.60*r - 0.28*g - 0.32*b + q = 0.21*r - 0.52*g + 0.31*b + return (y, i, q) + +def yiq_to_rgb(y, i, q): + r = y + 0.948262*i + 0.624013*q + g = y - 0.276066*i - 0.639810*q + b = y - 1.105450*i + 1.729860*q + if r < 0.0: r = 0.0 + if g < 0.0: g = 0.0 + if b < 0.0: b = 0.0 + if r > 1.0: r = 1.0 + if g > 1.0: g = 1.0 + if b > 1.0: b = 1.0 + return (r, g, b) + + +# HLS: Hue, Luminance, S??? +# H: position in the spectrum +# L: ??? +# S: ??? + +def rgb_to_hls(r, g, b): + maxc = max(r, g, b) + minc = min(r, g, b) + # XXX Can optimize (maxc+minc) and (maxc-minc) + l = (minc+maxc)/2.0 + if minc == maxc: return 0.0, l, 0.0 + if l <= 0.5: s = (maxc-minc) / (maxc+minc) + else: s = (maxc-minc) / (2.0-maxc-minc) + rc = (maxc-r) / (maxc-minc) + gc = (maxc-g) / (maxc-minc) + bc = (maxc-b) / (maxc-minc) + if r == maxc: h = bc-gc + elif g == maxc: h = 2.0+rc-bc + else: h = 4.0+gc-rc + h = (h/6.0) % 1.0 + return h, l, s + +def hls_to_rgb(h, l, s): + if s == 0.0: return l, l, l + if l <= 0.5: m2 = l * (1.0+s) + else: m2 = l+s-(l*s) + m1 = 2.0*l - m2 + return (_v(m1, m2, h+ONE_THIRD), _v(m1, m2, h), _v(m1, m2, h-ONE_THIRD)) + +def _v(m1, m2, hue): + hue = hue % 1.0 + if hue < ONE_SIXTH: return m1 + (m2-m1)*hue*6.0 + if hue < 0.5: return m2 + if hue < TWO_THIRD: return m1 + (m2-m1)*(TWO_THIRD-hue)*6.0 + return m1 + + +# HSV: Hue, Saturation, Value(?) +# H: position in the spectrum +# S: ??? +# V: ??? + +def rgb_to_hsv(r, g, b): + maxc = max(r, g, b) + minc = min(r, g, b) + v = maxc + if minc == maxc: return 0.0, 0.0, v + s = (maxc-minc) / maxc + rc = (maxc-r) / (maxc-minc) + gc = (maxc-g) / (maxc-minc) + bc = (maxc-b) / (maxc-minc) + if r == maxc: h = bc-gc + elif g == maxc: h = 2.0+rc-bc + else: h = 4.0+gc-rc + h = (h/6.0) % 1.0 + return h, s, v + +def hsv_to_rgb(h, s, v): + if s == 0.0: return v, v, v + i = int(h*6.0) # XXX assume int() truncates! + f = (h*6.0) - i + p = v*(1.0 - s) + q = v*(1.0 - s*f) + t = v*(1.0 - s*(1.0-f)) + if i%6 == 0: return v, t, p + if i == 1: return q, v, p + if i == 2: return p, v, t + if i == 3: return p, q, v + if i == 4: return t, p, v + if i == 5: return v, p, q + # Cannot get here diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/commands.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/commands.py new file mode 100644 index 00000000..02b4e5ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/commands.py @@ -0,0 +1,84 @@ +"""Execute shell commands via os.popen() and return status, output. + +Interface summary: + + import commands + + outtext = commands.getoutput(cmd) + (exitstatus, outtext) = commands.getstatusoutput(cmd) + outtext = commands.getstatus(file) # returns output of "ls -ld file" + +A trailing newline is removed from the output string. + +Encapsulates the basic operation: + + pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r') + text = pipe.read() + sts = pipe.close() + + [Note: it would be nice to add functions to interpret the exit status.] +""" + +__all__ = ["getstatusoutput","getoutput","getstatus"] + +# Module 'commands' +# +# Various tools for executing commands and looking at their output and status. +# +# NB This only works (and is only relevant) for UNIX. + + +# Get 'ls -l' status for an object into a string +# +def getstatus(file): + """Return output of "ls -ld " in a string.""" + return getoutput('ls -ld' + mkarg(file)) + + +# Get the output from a shell command into a string. +# The exit status is ignored; a trailing newline is stripped. +# Assume the command will work with '{ ... ; } 2>&1' around it.. +# +def getoutput(cmd): + """Return output (stdout or stderr) of executing cmd in a shell.""" + return getstatusoutput(cmd)[1] + + +# Ditto but preserving the exit status. +# Returns a pair (sts, output) +# +def getstatusoutput(cmd): + """Return (status, output) of executing cmd in a shell.""" + import os + pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r') + text = pipe.read() + sts = pipe.close() + if sts is None: sts = 0 + if text[-1:] == '\n': text = text[:-1] + return sts, text + + +# Make command argument from directory and pathname (prefix space, add quotes). +# +def mk2arg(head, x): + import os + return mkarg(os.path.join(head, x)) + + +# Make a shell command argument from a string. +# Return a string beginning with a space followed by a shell-quoted +# version of the argument. +# Two strategies: enclose in single quotes if it contains none; +# otherwise, enclose in double quotes and prefix quotable characters +# with backslash. +# +def mkarg(x): + if '\'' not in x: + return ' \'' + x + '\'' + s = ' "' + for c in x: + if c in '\\$"`': + s = s + '\\' + s = s + c + s = s + '"' + return s diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/compileall.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/compileall.py new file mode 100644 index 00000000..60e04791 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/compileall.py @@ -0,0 +1,148 @@ +"""Module/script to "compile" all .py files to .pyc (or .pyo) file. + +When called as a script with arguments, this compiles the directories +given as arguments recursively; the -l option prevents it from +recursing into directories. + +Without arguments, if compiles all modules on sys.path, without +recursing into subdirectories. (Even though it should do so for +packages -- for now, you'll have to deal with packages separately.) + +See module py_compile for details of the actual byte-compilation. + +""" + +import os +import stat +import sys +import py_compile + +__all__ = ["compile_dir","compile_path"] + +def compile_dir(dir, maxlevels=10, ddir=None, force=0, rx=None): + """Byte-compile all modules in the given directory tree. + + Arguments (only dir is required): + + dir: the directory to byte-compile + maxlevels: maximum recursion level (default 10) + ddir: if given, purported directory name (this is the + directory name that will show up in error messages) + force: if 1, force compilation, even if timestamps are up-to-date + + """ + print 'Listing', dir, '...' + try: + names = os.listdir(dir) + except os.error: + print "Can't list", dir + names = [] + names.sort() + success = 1 + for name in names: + fullname = os.path.join(dir, name) + if ddir: + dfile = os.path.join(ddir, name) + else: + dfile = None + if rx: + mo = rx.search(fullname) + if mo: + continue + if os.path.isfile(fullname): + head, tail = name[:-3], name[-3:] + if tail == '.py': + cfile = fullname + (__debug__ and 'c' or 'o') + ftime = os.stat(fullname)[stat.ST_MTIME] + try: ctime = os.stat(cfile)[stat.ST_MTIME] + except os.error: ctime = 0 + if (ctime > ftime) and not force: continue + print 'Compiling', fullname, '...' + try: + ok = py_compile.compile(fullname, None, dfile) + except KeyboardInterrupt: + raise KeyboardInterrupt + except: + # XXX py_compile catches SyntaxErrors + if type(sys.exc_type) == type(''): + exc_type_name = sys.exc_type + else: exc_type_name = sys.exc_type.__name__ + print 'Sorry:', exc_type_name + ':', + print sys.exc_value + success = 0 + else: + if ok == 0: + success = 0 + elif maxlevels > 0 and \ + name != os.curdir and name != os.pardir and \ + os.path.isdir(fullname) and \ + not os.path.islink(fullname): + if not compile_dir(fullname, maxlevels - 1, dfile, force, rx): + success = 0 + return success + +def compile_path(skip_curdir=1, maxlevels=0, force=0): + """Byte-compile all module on sys.path. + + Arguments (all optional): + + skip_curdir: if true, skip current directory (default true) + maxlevels: max recursion level (default 0) + force: as for compile_dir() (default 0) + + """ + success = 1 + for dir in sys.path: + if (not dir or dir == os.curdir) and skip_curdir: + print 'Skipping current directory' + else: + success = success and compile_dir(dir, maxlevels, None, force) + return success + +def main(): + """Script main program.""" + import getopt + try: + opts, args = getopt.getopt(sys.argv[1:], 'lfd:x:') + except getopt.error, msg: + print msg + print "usage: python compileall.py [-l] [-f] [-d destdir] " \ + "[-s regexp] [directory ...]" + print "-l: don't recurse down" + print "-f: force rebuild even if timestamps are up-to-date" + print "-d destdir: purported directory name for error messages" + print " if no directory arguments, -l sys.path is assumed" + print "-x regexp: skip files matching the regular expression regexp" + print " the regexp is search for in the full path of the file" + sys.exit(2) + maxlevels = 10 + ddir = None + force = 0 + rx = None + for o, a in opts: + if o == '-l': maxlevels = 0 + if o == '-d': ddir = a + if o == '-f': force = 1 + if o == '-x': + import re + rx = re.compile(a) + if ddir: + if len(args) != 1: + print "-d destdir require exactly one directory argument" + sys.exit(2) + success = 1 + try: + if args: + for dir in args: + if not compile_dir(dir, maxlevels, ddir, force, rx): + success = 0 + else: + success = compile_path() + except KeyboardInterrupt: + print "\n[interrupt]" + success = 0 + return success + +if __name__ == '__main__': + exit_status = not main() + sys.exit(exit_status) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/copy.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/copy.py new file mode 100644 index 00000000..17b40337 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/copy.py @@ -0,0 +1,378 @@ +"""Generic (shallow and deep) copying operations. + +Interface summary: + + import copy + + x = copy.copy(y) # make a shallow copy of y + x = copy.deepcopy(y) # make a deep copy of y + +For module specific errors, copy.error is raised. + +The difference between shallow and deep copying is only relevant for +compound objects (objects that contain other objects, like lists or +class instances). + +- A shallow copy constructs a new compound object and then (to the + extent possible) inserts *the same objects* into in that the + original contains. + +- A deep copy constructs a new compound object and then, recursively, + inserts *copies* into it of the objects found in the original. + +Two problems often exist with deep copy operations that don't exist +with shallow copy operations: + + a) recursive objects (compound objects that, directly or indirectly, + contain a reference to themselves) may cause a recursive loop + + b) because deep copy copies *everything* it may copy too much, e.g. + administrative data structures that should be shared even between + copies + +Python's deep copy operation avoids these problems by: + + a) keeping a table of objects already copied during the current + copying pass + + b) letting user-defined classes override the copying operation or the + set of components copied + +This version does not copy types like module, class, function, method, +nor stack trace, stack frame, nor file, socket, window, nor array, nor +any similar types. + +Classes can use the same interfaces to control copying that they use +to control pickling: they can define methods called __getinitargs__(), +__getstate__() and __setstate__(). See the documentation for module +"pickle" for information on these methods. +""" + +# XXX need to support copy_reg here too... + +import types + +class Error(Exception): + pass +error = Error # backward compatibility + +try: + from org.python.core import PyStringMap +except ImportError: + PyStringMap = None + +__all__ = ["Error", "error", "copy", "deepcopy"] + +def copy(x): + """Shallow copy operation on arbitrary Python objects. + + See the module's __doc__ string for more info. + """ + + try: + copierfunction = _copy_dispatch[type(x)] + except KeyError: + try: + copier = x.__copy__ + except AttributeError: + try: + reductor = x.__reduce__ + except AttributeError: + raise error, \ + "un(shallow)copyable object of type %s" % type(x) + else: + y = _reconstruct(x, reductor(), 0) + else: + y = copier() + else: + y = copierfunction(x) + return y + +_copy_dispatch = d = {} + +def _copy_atomic(x): + return x +d[types.NoneType] = _copy_atomic +d[types.IntType] = _copy_atomic +d[types.LongType] = _copy_atomic +d[types.FloatType] = _copy_atomic +try: + d[types.ComplexType] = _copy_atomic +except AttributeError: + pass +d[types.StringType] = _copy_atomic +try: + d[types.UnicodeType] = _copy_atomic +except AttributeError: + pass +try: + d[types.CodeType] = _copy_atomic +except AttributeError: + pass +d[types.TypeType] = _copy_atomic +d[types.XRangeType] = _copy_atomic +d[types.ClassType] = _copy_atomic + +def _copy_list(x): + return x[:] +d[types.ListType] = _copy_list + +def _copy_tuple(x): + return x[:] +d[types.TupleType] = _copy_tuple + +def _copy_dict(x): + return x.copy() +d[types.DictionaryType] = _copy_dict +if PyStringMap is not None: + d[PyStringMap] = _copy_dict + +def _copy_inst(x): + if hasattr(x, '__copy__'): + return x.__copy__() + if hasattr(x, '__getinitargs__'): + args = x.__getinitargs__() + y = apply(x.__class__, args) + else: + y = _EmptyClass() + y.__class__ = x.__class__ + if hasattr(x, '__getstate__'): + state = x.__getstate__() + else: + state = x.__dict__ + if hasattr(y, '__setstate__'): + y.__setstate__(state) + else: + y.__dict__.update(state) + return y +d[types.InstanceType] = _copy_inst + +del d + +def deepcopy(x, memo = None): + """Deep copy operation on arbitrary Python objects. + + See the module's __doc__ string for more info. + """ + + if memo is None: + memo = {} + d = id(x) + if memo.has_key(d): + return memo[d] + try: + copierfunction = _deepcopy_dispatch[type(x)] + except KeyError: + try: + issc = issubclass(type(x), type) + except TypeError: + issc = 0 + if issc: + y = _deepcopy_dispatch[type](x, memo) + else: + try: + copier = x.__deepcopy__ + except AttributeError: + try: + reductor = x.__reduce__ + except AttributeError: + raise error, \ + "un-deep-copyable object of type %s" % type(x) + else: + y = _reconstruct(x, reductor(), 1, memo) + else: + y = copier(memo) + else: + y = copierfunction(x, memo) + memo[d] = y + _keep_alive(x, memo) # Make sure x lives at least as long as d + return y + +_deepcopy_dispatch = d = {} + +def _deepcopy_atomic(x, memo): + return x +d[types.NoneType] = _deepcopy_atomic +d[types.IntType] = _deepcopy_atomic +d[types.LongType] = _deepcopy_atomic +d[types.FloatType] = _deepcopy_atomic +try: + d[types.ComplexType] = _deepcopy_atomic +except AttributeError: + pass +d[types.StringType] = _deepcopy_atomic +try: + d[types.UnicodeType] = _deepcopy_atomic +except AttributeError: + pass +try: + d[types.CodeType] = _deepcopy_atomic +except AttributeError: + pass +d[types.TypeType] = _deepcopy_atomic +d[types.XRangeType] = _deepcopy_atomic + +def _deepcopy_list(x, memo): + y = [] + memo[id(x)] = y + for a in x: + y.append(deepcopy(a, memo)) + return y +d[types.ListType] = _deepcopy_list + +def _deepcopy_tuple(x, memo): + y = [] + for a in x: + y.append(deepcopy(a, memo)) + d = id(x) + try: + return memo[d] + except KeyError: + pass + for i in range(len(x)): + if x[i] is not y[i]: + y = tuple(y) + break + else: + y = x + memo[d] = y + return y +d[types.TupleType] = _deepcopy_tuple + +def _deepcopy_dict(x, memo): + y = {} + memo[id(x)] = y + for key in x.keys(): + y[deepcopy(key, memo)] = deepcopy(x[key], memo) + return y +d[types.DictionaryType] = _deepcopy_dict +if PyStringMap is not None: + d[PyStringMap] = _deepcopy_dict + +def _keep_alive(x, memo): + """Keeps a reference to the object x in the memo. + + Because we remember objects by their id, we have + to assure that possibly temporary objects are kept + alive by referencing them. + We store a reference at the id of the memo, which should + normally not be used unless someone tries to deepcopy + the memo itself... + """ + try: + memo[id(memo)].append(x) + except KeyError: + # aha, this is the first one :-) + memo[id(memo)]=[x] + +def _deepcopy_inst(x, memo): + if hasattr(x, '__deepcopy__'): + return x.__deepcopy__(memo) + if hasattr(x, '__getinitargs__'): + args = x.__getinitargs__() + args = deepcopy(args, memo) + y = apply(x.__class__, args) + else: + y = _EmptyClass() + y.__class__ = x.__class__ + memo[id(x)] = y + if hasattr(x, '__getstate__'): + state = x.__getstate__() + else: + state = x.__dict__ + state = deepcopy(state, memo) + if hasattr(y, '__setstate__'): + y.__setstate__(state) + else: + y.__dict__.update(state) + return y +d[types.InstanceType] = _deepcopy_inst + +def _reconstruct(x, info, deep, memo=None): + if isinstance(info, str): + return x + assert isinstance(info, tuple) + if memo is None: + memo = {} + n = len(info) + assert n in (2, 3) + callable, args = info[:2] + if n > 2: + state = info[2] + else: + state = {} + if deep: + args = deepcopy(args, memo) + y = callable(*args) + if state: + if deep: + state = deepcopy(state, memo) + if hasattr(y, '__setstate__'): + y.__setstate__(state) + else: + y.__dict__.update(state) + return y + +del d + +del types + +# Helper for instance creation without calling __init__ +class _EmptyClass: + pass + +def _test(): + l = [None, 1, 2L, 3.14, 'xyzzy', (1, 2L), [3.14, 'abc'], + {'abc': 'ABC'}, (), [], {}] + l1 = copy(l) + print l1==l + l1 = map(copy, l) + print l1==l + l1 = deepcopy(l) + print l1==l + class C: + def __init__(self, arg=None): + self.a = 1 + self.arg = arg + if __name__ == '__main__': + import sys + file = sys.argv[0] + else: + file = __file__ + self.fp = open(file) + self.fp.close() + def __getstate__(self): + return {'a': self.a, 'arg': self.arg} + def __setstate__(self, state): + for key in state.keys(): + setattr(self, key, state[key]) + def __deepcopy__(self, memo = None): + new = self.__class__(deepcopy(self.arg, memo)) + new.a = self.a + return new + c = C('argument sketch') + l.append(c) + l2 = copy(l) + print l == l2 + print l + print l2 + l2 = deepcopy(l) + print l == l2 + print l + print l2 + l.append({l[1]: l, 'xyz': l[2]}) + l3 = copy(l) + import repr + print map(repr.repr, l) + print map(repr.repr, l1) + print map(repr.repr, l2) + print map(repr.repr, l3) + l3 = deepcopy(l) + import repr + print map(repr.repr, l) + print map(repr.repr, l1) + print map(repr.repr, l2) + print map(repr.repr, l3) + +if __name__ == '__main__': + _test() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/copy_reg.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/copy_reg.py new file mode 100644 index 00000000..93fab1d6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/copy_reg.py @@ -0,0 +1,72 @@ +"""Helper to provide extensibility for pickle/cPickle. + +This is only useful to add pickle support for extension types defined in +C, not for instances of user-defined classes. +""" + +from types import ClassType as _ClassType + +__all__ = ["pickle","constructor"] + +dispatch_table = {} +safe_constructors = {} + +def pickle(ob_type, pickle_function, constructor_ob=None): + if type(ob_type) is _ClassType: + raise TypeError("copy_reg is not intended for use with classes") + + if not callable(pickle_function): + raise TypeError("reduction functions must be callable") + dispatch_table[ob_type] = pickle_function + + if constructor_ob is not None: + constructor(constructor_ob) + +def constructor(object): + if not callable(object): + raise TypeError("constructors must be callable") + safe_constructors[object] = 1 + +# Example: provide pickling support for complex numbers. + +def pickle_complex(c): + return complex, (c.real, c.imag) + +pickle(type(1j), pickle_complex, complex) + +# Support for picking new-style objects + +def _reconstructor(cls, base, state): + obj = base.__new__(cls, state) + base.__init__(obj, state) + return obj +_reconstructor.__safe_for_unpickling__ = 1 + +_HEAPTYPE = 1<<9 + +def _reduce(self): + for base in self.__class__.__mro__: + if hasattr(base, '__flags__') and not base.__flags__ & _HEAPTYPE: + break + else: + base = object # not really reachable + if base is object: + state = None + else: + if base is self.__class__: + raise TypeError, "can't pickle %s objects" % base.__name__ + state = base(self) + args = (self.__class__, base, state) + try: + getstate = self.__getstate__ + except AttributeError: + try: + dict = self.__dict__ + except AttributeError: + dict = None + else: + dict = getstate() + if dict: + return _reconstructor, args, dict + else: + return _reconstructor, args diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/decompyle.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/decompyle.py new file mode 100644 index 00000000..ec63f1b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/decompyle.py @@ -0,0 +1,1500 @@ +# Copyright (c) 1999 John Aycock +# Copyright (c) 2000 by hartmut Goebel +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# See 'CHANGES' for a list of changes +# +# NB. This is not a masterpiece of software, but became more like a hack. +# Probably a complete write would be sensefull. hG/2000-12-27 +# + +import re, sys, os, types +import dis, imp, marshal +import string +import cStringIO + + +def _load_file(filename): + """ + load a Python source file and compile it to byte-code + _load_module(filename: string): code_object + + filename: name of file containing Python source code + (normally a .py) + code_object: code_object compiled from this source code + + This function does NOT write any file! + """ + fp = open(filename, 'rb') + source = fp.read()+'\n' + try: + co = compile(source, filename, 'exec') + except SyntaxError: + sys.stderr.writelines( ['>>Syntax error in ', filename, '\n'] ) + raise + fp.close() + return co + +def _load_module(filename): + """ + load a module without importing it + _load_module(filename: string): code_object + + filename: name of file containing Python byte-code object + (normally a .pyc) + code_object: code_object from this file + """ + fp = open(filename, 'rb') + if fp.read(4) != imp.get_magic(): + raise ImportError, "Bad magic number in %s" % filename + fp.read(4) + co = marshal.load(fp) + fp.close() + return co + + +#-- start of (de-)compiler + +# +# Scanning +# + +class Code: + """Class for representing code-objects. + + This is similar to the original code object, but additionally + the diassembled code is stored in the attribute '_tokens'. + """ + def __init__(self, co): + for i in dir(co): + exec 'self.%s = co.%s' % (i, i) + self._tokens, self._customize = disassemble(co) + +class Token: + """Class representing a byte-code token. + A byte-code token is equivalent to the contents of one line + as output by dis.dis(). + """ + def __init__(self, type, attr=None, pattr=None, offset=-1): + self.type = intern(type) + self.attr = attr + self.pattr = pattr + self.offset = offset + + def __cmp__(self, o): + if isinstance(o, Token): + # both are tokens: compare type and pattr + return cmp(self.type, o.type) \ + or cmp(self.pattr, o.pattr) + else: + return cmp(self.type, o) + + def __repr__(self): return str(self.type) + def __str__(self): + if self.pattr: pattr = self.pattr + else: pattr = '' + return '%s\t%-17s %s' % (self.offset, self.type, pattr) + def __hash__(self): return hash(self.type) + def __getitem__(self, i): raise IndexError + +_JUMP_OPS_ = map(lambda op: dis.opname[op], dis.hasjrel + dis.hasjabs) + +def disassemble(co): + """Disassemble a code object, returning a list of Token. + + The main part of this procedure is modelled after + dis.diaassemble(). + """ + rv = [] + customize = {} + + code = co.co_code + cf = find_jump_targets(code) + n = len(code) + i = 0 + while i < n: + offset = i + if cf.has_key(offset): + for j in range(cf[offset]): + rv.append(Token('COME_FROM', + offset="%s_%d" % (offset, j) )) + + c = code[i] + op = ord(c) + opname = dis.opname[op] + i = i+1 + oparg = None; pattr = None + if op >= dis.HAVE_ARGUMENT: + oparg = ord(code[i]) + ord(code[i+1])*256 + i = i+2 + if op in dis.hasconst: + const = co.co_consts[oparg] + if type(const) == types.CodeType: + oparg = const + if const.co_name == '': + assert opname == 'LOAD_CONST' + opname = 'LOAD_LAMBDA' + # verify uses 'pattr' for + # comparism, since 'attr' now + # hold Code(const) and thus + # can not be used for + # comparism (todo: thinkg + # about changing this) + #pattr = 'code_object @ 0x%x %s->%s' %\ + # (id(const), const.co_filename, const.co_name) + pattr = 'code_object ' + const.co_name + else: + pattr = `const` + elif op in dis.hasname: + pattr = co.co_names[oparg] + elif op in dis.hasjrel: + pattr = `i + oparg` + elif op in dis.haslocal: + pattr = co.co_varnames[oparg] + elif op in dis.hascompare: + pattr = dis.cmp_op[oparg] + + if opname == 'SET_LINENO': + continue + elif opname in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SLICE', + 'UNPACK_LIST', 'UNPACK_TUPLE', + 'UNPACK_SEQUENCE', + 'MAKE_FUNCTION', 'CALL_FUNCTION', + 'CALL_FUNCTION_VAR', 'CALL_FUNCTION_KW', + 'CALL_FUNCTION_VAR_KW', 'DUP_TOPX', + ): + opname = '%s_%d' % (opname, oparg) + customize[opname] = oparg + + rv.append(Token(opname, oparg, pattr, offset)) + + return rv, customize + + +def find_jump_targets(code): + """Detect all offsets in a byte code which are jump targets. + + Return the list of offsets. + + This procedure is modelled after dis.findlables(), but here + for each target the number of jumps are counted. + """ + targets = {} + n = len(code) + i = 0 + while i < n: + c = code[i] + op = ord(c) + i = i+1 + if op >= dis.HAVE_ARGUMENT: + oparg = ord(code[i]) + ord(code[i+1])*256 + i = i+2 + label = -1 + if op in dis.hasjrel: + label = i+oparg + # todo: absolut jumps + #elif op in dis.hasjabs: + # label = oparg + if label >= 0: + targets[label] = targets.get(label, 0) + 1 + return targets + + +# +# Parsing +# + +class AST: + def __init__(self, type, kids=None): + self.type = intern(type) + if kids == None: kids = [] + self._kids = kids + + def append(self, o): self._kids.append(o) + def pop(self): return self._kids.pop() + def __getitem__(self, i): return self._kids[i] + def __setitem__(self, i, val): self._kids[i] = val + def __delitem__(self, i): del self._kids[i] + def __len__(self): return len(self._kids) + def __getslice__(self, low, high): return self._kids[low:high] + def __setslice__(self, low, high, seq): self._kids[low:high] = seq + def __delslice__(self, low, high): del self._kids[low:high] + def __cmp__(self, o): + if isinstance(o, AST): + return cmp(self.type, o.type) \ + or cmp(self._kids, o._kids) + else: + return cmp(self.type, o) + + def __hash__(self): return hash(self.type) + + def __repr__(self): + rv = str(self.type) + for k in self._kids: + rv = rv + '\n' + string.replace(str(k), '\n', '\n ') + return rv + +# Some ASTs used for comparing code fragments (like 'return None' at +# the end of functions). + +RETURN_LOCALS = AST('stmt', + [ AST('return_stmt', + [ AST('expr', [ Token('LOAD_LOCALS') ]), + Token('RETURN_VALUE')]) ]) + +RETURN_NONE = AST('stmt', + [ AST('return_stmt', + [ AST('expr', [ Token('LOAD_CONST', pattr='None') ]), + Token('RETURN_VALUE')]) ]) + +ASSIGN_DOC_STRING = lambda doc_string: \ + AST('stmt', + [ AST('assign', + [ AST('expr', [ Token('LOAD_CONST', pattr=`doc_string`) ]), + AST('designator', [ Token('STORE_NAME', pattr='__doc__')]) + ])]) + +BUILD_TUPLE_0 = AST('expr', + [ Token('BUILD_TUPLE_0') ] ) + +from spark import GenericASTBuilder, GenericASTMatcher + +class Parser(GenericASTBuilder): + def __init__(self): + GenericASTBuilder.__init__(self, AST, 'code') + self.customized = {} + + def cleanup(self): + """ + Remove recursive references to allow garbage + collector to collect this object. + """ + for dict in (self.rule2func, self.rules, self.rule2name, self.first): + for i in dict.keys(): + dict[i] = None + for i in dir(self): + setattr(self, i, None) + + def error(self, token): + # output offset, too + print "Syntax error at or near `%s' token at offset %s" % \ + (`token`, token.offset) + raise SystemExit + + def typestring(self, token): + return token.type + + def p_funcdef(self, args): + ''' + stmt ::= funcdef + funcdef ::= mkfunc STORE_FAST + funcdef ::= mkfunc STORE_NAME + ''' + +# new for Python2.0 +# +# UNPACK_SEQUENCE # number of tuple items +# EXTENDED_ARG + + def p_list_comprehension(self, args): + ''' + expr ::= list_compr + list_compr ::= lc_prep lc_for lc_cleanup + lc_prep ::= BUILD_LIST_0 DUP_TOP LOAD_ATTR STORE_NAME + lc_prep ::= BUILD_LIST_0 DUP_TOP LOAD_ATTR STORE_FAST + + lc_for ::= expr LOAD_CONST + FOR_LOOP designator + lc_for JUMP_ABSOLUTE + COME_FROM + lc_for ::= expr LOAD_CONST + FOR_LOOP designator + lc_if JUMP_ABSOLUTE + COME_FROM + lc_for ::= expr LOAD_CONST + FOR_LOOP designator + lc_body JUMP_ABSOLUTE + COME_FROM + lc_if ::= expr condjmp lc_body + JUMP_FORWARD COME_FROM POP_TOP + COME_FROM + lc_body ::= LOAD_NAME expr CALL_FUNCTION_1 POP_TOP + lc_body ::= LOAD_FAST expr CALL_FUNCTION_1 POP_TOP + lc_cleanup ::= DELETE_NAME + lc_cleanup ::= DELETE_FAST + ''' + + def p_augmented_assign(self, args): + ''' + stmt ::= augassign1 + stmt ::= augassign2 + augassign1 ::= expr expr inplace_op designator + augassign1 ::= expr expr inplace_op ROT_THREE STORE_SUBSCR + augassign1 ::= expr expr inplace_op ROT_TWO STORE_SLICE+0 + augassign1 ::= expr expr inplace_op ROT_THREE STORE_SLICE+1 + augassign1 ::= expr expr inplace_op ROT_THREE STORE_SLICE+2 + augassign1 ::= expr expr inplace_op ROT_FOUR STORE_SLICE+3 + augassign2 ::= expr DUP_TOP LOAD_ATTR expr + inplace_op ROT_TWO STORE_ATTR + + inplace_op ::= INPLACE_ADD + inplace_op ::= INPLACE_SUBTRACT + inplace_op ::= INPLACE_MULTIPLY + inplace_op ::= INPLACE_DIVIDE + inplace_op ::= INPLACE_MODULO + inplace_op ::= INPLACE_POWER + inplace_op ::= INPLACE_LSHIFT + inplace_op ::= INPLACE_RSHIFT + inplace_op ::= INPLACE_AND + inplace_op ::= INPLACE_XOR + inplace_op ::= INPLACE_OR + ''' + + def p_assign(self, args): + ''' + stmt ::= assign + assign ::= expr DUP_TOP designList + assign ::= expr designator + ''' + + def p_print(self, args): + ''' + stmt ::= print_stmt + stmt ::= print_stmt_nl + stmt ::= print_nl_stmt + print_stmt ::= expr PRINT_ITEM + print_nl_stmt ::= PRINT_NEWLINE + print_stmt_nl ::= print_stmt print_nl_stmt + ''' + + def p_print_to(self, args): + ''' + stmt ::= print_to + stmt ::= print_to_nl + stmt ::= print_nl_to + print_to ::= expr print_to_items POP_TOP + print_to_nl ::= expr print_to_items PRINT_NEWLINE_TO + print_nl_to ::= expr PRINT_NEWLINE_TO + print_to_items ::= print_to_items print_to_item + print_to_items ::= print_to_item + print_to_item ::= DUP_TOP expr ROT_TWO PRINT_ITEM_TO + ''' + # expr print_to* POP_TOP + # expr { print_to* } PRINT_NEWLINE_TO + + def p_import15(self, args): + ''' + stmt ::= importstmt + stmt ::= importfrom + + importstmt ::= IMPORT_NAME STORE_FAST + importstmt ::= IMPORT_NAME STORE_NAME + + importfrom ::= IMPORT_NAME importlist POP_TOP + importlist ::= importlist IMPORT_FROM + importlist ::= IMPORT_FROM + ''' + + def p_import20(self, args): + ''' + stmt ::= importstmt2 + stmt ::= importfrom2 + stmt ::= importstar2 + + importstmt2 ::= LOAD_CONST import_as + importstar2 ::= LOAD_CONST IMPORT_NAME IMPORT_STAR + + importfrom2 ::= LOAD_CONST IMPORT_NAME importlist2 POP_TOP + importlist2 ::= importlist2 import_as + importlist2 ::= import_as + import_as ::= IMPORT_NAME STORE_FAST + import_as ::= IMPORT_NAME STORE_NAME + import_as ::= IMPORT_NAME LOAD_ATTR STORE_FAST + import_as ::= IMPORT_NAME LOAD_ATTR STORE_NAME + import_as ::= IMPORT_FROM STORE_FAST + import_as ::= IMPORT_FROM STORE_NAME + ''' + # 'import_as' can't use designator, since n_import_as() + # needs to compare both kids' pattr + + def p_grammar(self, args): + ''' + code ::= stmts + code ::= + + stmts ::= stmts stmt + stmts ::= stmt + + stmts_opt ::= stmts + stmts_opt ::= passstmt + passstmt ::= + + designList ::= designator designator + designList ::= designator DUP_TOP designList + + designator ::= STORE_FAST + designator ::= STORE_NAME + designator ::= STORE_GLOBAL + designator ::= expr STORE_ATTR + designator ::= expr STORE_SLICE+0 + designator ::= expr expr STORE_SLICE+1 + designator ::= expr expr STORE_SLICE+2 + designator ::= expr expr expr STORE_SLICE+3 + designator ::= store_subscr + store_subscr ::= expr expr STORE_SUBSCR + designator ::= unpack + designator ::= unpack_list + + stmt ::= classdef + stmt ::= call_stmt + call_stmt ::= expr POP_TOP + + stmt ::= return_stmt + return_stmt ::= expr RETURN_VALUE + + stmt ::= break_stmt + break_stmt ::= BREAK_LOOP + + stmt ::= continue_stmt + continue_stmt ::= JUMP_ABSOLUTE + + stmt ::= raise_stmt + raise_stmt ::= exprlist RAISE_VARARGS + raise_stmt ::= nullexprlist RAISE_VARARGS + + stmt ::= exec_stmt + exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT + exec_stmt ::= expr exprlist EXEC_STMT + + stmt ::= assert + stmt ::= assert2 + stmt ::= ifstmt + stmt ::= ifelsestmt + stmt ::= whilestmt + stmt ::= whileelsestmt + stmt ::= forstmt + stmt ::= forelsestmt + stmt ::= trystmt + stmt ::= tryfinallystmt + + stmt ::= DELETE_FAST + stmt ::= DELETE_NAME + stmt ::= DELETE_GLOBAL + stmt ::= expr DELETE_SLICE+0 + stmt ::= expr expr DELETE_SLICE+1 + stmt ::= expr expr DELETE_SLICE+2 + stmt ::= expr expr expr DELETE_SLICE+3 + stmt ::= delete_subscr + delete_subscr ::= expr expr DELETE_SUBSCR + stmt ::= expr DELETE_ATTR + + kwarg ::= LOAD_CONST expr + + classdef ::= LOAD_CONST expr mkfunc + CALL_FUNCTION_0 BUILD_CLASS STORE_NAME + classdef ::= LOAD_CONST expr mkfunc + CALL_FUNCTION_0 BUILD_CLASS STORE_FAST + + condjmp ::= JUMP_IF_FALSE POP_TOP + condjmp ::= JUMP_IF_TRUE POP_TOP + + assert ::= expr JUMP_IF_FALSE POP_TOP + expr JUMP_IF_TRUE POP_TOP + LOAD_GLOBAL RAISE_VARARGS + COME_FROM COME_FROM POP_TOP + assert2 ::= expr JUMP_IF_FALSE POP_TOP + expr JUMP_IF_TRUE POP_TOP + LOAD_GLOBAL expr RAISE_VARARGS + COME_FROM COME_FROM POP_TOP + + ifstmt ::= expr condjmp stmts_opt + JUMP_FORWARD COME_FROM POP_TOP + COME_FROM + + ifelsestmt ::= expr condjmp stmts_opt + JUMP_FORWARD COME_FROM + POP_TOP stmts COME_FROM + + trystmt ::= SETUP_EXCEPT stmts_opt + POP_BLOCK JUMP_FORWARD + COME_FROM except_stmt + + try_end ::= END_FINALLY COME_FROM + try_end ::= except_else + except_else ::= END_FINALLY COME_FROM stmts + + except_stmt ::= except_cond except_stmt COME_FROM + except_stmt ::= except_conds try_end COME_FROM + except_stmt ::= except try_end COME_FROM + except_stmt ::= try_end + + except_conds ::= except_cond except_conds COME_FROM + except_conds ::= + + except_cond ::= except_cond1 + except_cond ::= except_cond2 + except_cond1 ::= DUP_TOP expr COMPARE_OP + JUMP_IF_FALSE + POP_TOP POP_TOP POP_TOP POP_TOP + stmts_opt JUMP_FORWARD COME_FROM + POP_TOP + except_cond2 ::= DUP_TOP expr COMPARE_OP + JUMP_IF_FALSE + POP_TOP POP_TOP designator POP_TOP + stmts_opt JUMP_FORWARD COME_FROM + POP_TOP + except ::= POP_TOP POP_TOP POP_TOP + stmts_opt JUMP_FORWARD + + tryfinallystmt ::= SETUP_FINALLY stmts_opt + POP_BLOCK LOAD_CONST + COME_FROM stmts_opt END_FINALLY + + whilestmt ::= SETUP_LOOP + expr JUMP_IF_FALSE POP_TOP + stmts_opt JUMP_ABSOLUTE + COME_FROM POP_TOP POP_BLOCK COME_FROM + whileelsestmt ::= SETUP_LOOP + expr JUMP_IF_FALSE POP_TOP + stmts_opt JUMP_ABSOLUTE + COME_FROM POP_TOP POP_BLOCK + stmts COME_FROM + + forstmt ::= SETUP_LOOP expr LOAD_CONST + FOR_LOOP designator + stmts_opt JUMP_ABSOLUTE + COME_FROM POP_BLOCK COME_FROM + forelsestmt ::= SETUP_LOOP expr LOAD_CONST + FOR_LOOP designator + stmts_opt JUMP_ABSOLUTE + COME_FROM POP_BLOCK stmts COME_FROM + ''' + + def p_expr(self, args): + ''' + expr ::= mklambda + expr ::= mkfunc + expr ::= SET_LINENO + expr ::= LOAD_FAST + expr ::= LOAD_NAME + expr ::= LOAD_CONST + expr ::= LOAD_GLOBAL + expr ::= LOAD_LOCALS + expr ::= expr LOAD_ATTR + expr ::= binary_expr + + binary_expr ::= expr expr binary_op + binary_op ::= BINARY_ADD + binary_op ::= BINARY_SUBTRACT + binary_op ::= BINARY_MULTIPLY + binary_op ::= BINARY_DIVIDE + binary_op ::= BINARY_MODULO + binary_op ::= BINARY_LSHIFT + binary_op ::= BINARY_RSHIFT + binary_op ::= BINARY_AND + binary_op ::= BINARY_OR + binary_op ::= BINARY_XOR + binary_op ::= BINARY_POWER + + expr ::= binary_subscr + binary_subscr ::= expr expr BINARY_SUBSCR + expr ::= expr expr DUP_TOPX_2 BINARY_SUBSCR + expr ::= cmp + expr ::= expr UNARY_POSITIVE + expr ::= expr UNARY_NEGATIVE + expr ::= expr UNARY_CONVERT + expr ::= expr UNARY_INVERT + expr ::= expr UNARY_NOT + expr ::= mapexpr + expr ::= expr SLICE+0 + expr ::= expr expr SLICE+1 + expr ::= expr expr SLICE+2 + expr ::= expr expr expr SLICE+3 + expr ::= expr DUP_TOP SLICE+0 + expr ::= expr expr DUP_TOPX_2 SLICE+1 + expr ::= expr expr DUP_TOPX_2 SLICE+2 + expr ::= expr expr expr DUP_TOPX_3 SLICE+3 + expr ::= and + expr ::= or + or ::= expr JUMP_IF_TRUE POP_TOP expr COME_FROM + and ::= expr JUMP_IF_FALSE POP_TOP expr COME_FROM + + cmp ::= cmp_list + cmp ::= compare + compare ::= expr expr COMPARE_OP + cmp_list ::= expr cmp_list1 ROT_TWO POP_TOP + COME_FROM + cmp_list1 ::= expr DUP_TOP ROT_THREE + COMPARE_OP JUMP_IF_FALSE POP_TOP + cmp_list1 COME_FROM + cmp_list1 ::= expr DUP_TOP ROT_THREE + COMPARE_OP JUMP_IF_FALSE POP_TOP + cmp_list2 COME_FROM + cmp_list2 ::= expr COMPARE_OP JUMP_FORWARD + + mapexpr ::= BUILD_MAP kvlist + + kvlist ::= kvlist kv + kvlist ::= + + kv ::= DUP_TOP expr ROT_TWO expr STORE_SUBSCR + + exprlist ::= exprlist expr + exprlist ::= expr + + nullexprlist ::= + ''' + + def nonterminal(self, nt, args): + collect = ('stmts', 'exprlist', 'kvlist') + + if nt in collect and len(args) > 1: + # + # Collect iterated thingies together. + # + rv = args[0] + rv.append(args[1]) + else: + rv = GenericASTBuilder.nonterminal(self, nt, args) + return rv + + def __ambiguity(self, children): + # only for debugging! to be removed hG/2000-10-15 + print children + return GenericASTBuilder.ambiguity(self, children) + + def resolve(self, list): + if len(list) == 2 and 'funcdef' in list and 'assign' in list: + return 'funcdef' + #sys.stderr.writelines( ['resolve ', str(list), '\n'] ) + return GenericASTBuilder.resolve(self, list) + +nop = lambda self, args: None + +def parse(tokens, customize): + p = Parser() + # + # Special handling for opcodes that take a variable number + # of arguments -- we add a new rule for each: + # + # expr ::= {expr}^n BUILD_LIST_n + # expr ::= {expr}^n BUILD_TUPLE_n + # expr ::= {expr}^n BUILD_SLICE_n + # unpack_list ::= UNPACK_LIST {expr}^n + # unpack ::= UNPACK_TUPLE {expr}^n + # unpack ::= UNPACK_SEQEUENE {expr}^n + # mkfunc ::= {expr}^n LOAD_CONST MAKE_FUNCTION_n + # expr ::= expr {expr}^n CALL_FUNCTION_n + # expr ::= expr {expr}^n CALL_FUNCTION_VAR_n POP_TOP + # expr ::= expr {expr}^n CALL_FUNCTION_VAR_KW_n POP_TOP + # expr ::= expr {expr}^n CALL_FUNCTION_KW_n POP_TOP + # + for k, v in customize.items(): + ## avoid adding the same rule twice to this parser + #if p.customized.has_key(k): + # continue + #p.customized[k] = None + + #nop = lambda self, args: None + op = k[:string.rfind(k, '_')] + if op in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SLICE'): + rule = 'expr ::= ' + 'expr '*v + k + elif op in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'): + rule = 'unpack ::= ' + k + ' designator'*v + elif op == 'UNPACK_LIST': + rule = 'unpack_list ::= ' + k + ' designator'*v + elif op == 'DUP_TOPX': + # no need to add a rule + pass + #rule = 'dup_topx ::= ' + 'expr '*v + k + elif op == 'MAKE_FUNCTION': + p.addRule('mklambda ::= %s LOAD_LAMBDA %s' % + ('expr '*v, k), nop) + rule = 'mkfunc ::= %s LOAD_CONST %s' % ('expr '*v, k) + elif op in ('CALL_FUNCTION', 'CALL_FUNCTION_VAR', + 'CALL_FUNCTION_VAR_KW', 'CALL_FUNCTION_KW'): + na = (v & 0xff) # positional parameters + nk = (v >> 8) & 0xff # keyword parameters + # number of apply equiv arguments: + nak = ( len(op)-len('CALL_FUNCTION') ) / 3 + rule = 'expr ::= expr ' + 'expr '*na + 'kwarg '*nk \ + + 'expr ' * nak + k + else: + raise 'unknown customize token %s' % k + p.addRule(rule, nop) + ast = p.parse(tokens) + p.cleanup() + return ast + +# +# Decompilation (walking AST) +# +# All table-driven. Step 1 determines a table (T) and a path to a +# table key (K) from the node type (N) (other nodes are shown as O): +# +# N N N&K +# / | ... \ / | ... \ / | ... \ +# O O O O O K O O O +# | +# K +# +# MAP_R0 (TABLE_R0) MAP_R (TABLE_R) MAP_DIRECT (TABLE_DIRECT) +# +# The default is a direct mapping. The key K is then extracted from the +# subtree and used to find a table entry T[K], if any. The result is a +# format string and arguments (a la printf()) for the formatting engine. +# Escapes in the format string are: +# +# %c evaluate N[A] recursively* +# %C evaluate N[A[0]]..N[A[1]] recursively, separate by A[2]* +# %, print ',' if last %C only printed one item (for tuples) +# %| tab to current indentation level +# %+ increase current indentation level +# %- decrease current indentation level +# %{...} evaluate ... in context of N +# %% literal '%' +# +# * indicates an argument (A) required. +# +# The '%' may optionally be followed by a number (C) in square brackets, which +# makes the engine walk down to N[C] before evaluating the escape code. +# + +from spark import GenericASTTraversal + +#TAB = '\t' # as God intended +TAB = ' ' *4 # is less spacy than "\t" + +TABLE_R = { + 'build_tuple2': ( '%C', (0,-1,', ') ), + 'POP_TOP': ( '%|%c\n', 0 ), + 'STORE_ATTR': ( '%c.%[1]{pattr}', 0), +# 'STORE_SUBSCR': ( '%c[%c]', 0, 1 ), + 'STORE_SLICE+0':( '%c[:]', 0 ), + 'STORE_SLICE+1':( '%c[%c:]', 0, 1 ), + 'STORE_SLICE+2':( '%c[:%c]', 0, 1 ), + 'STORE_SLICE+3':( '%c[%c:%c]', 0, 1, 2 ), + 'JUMP_ABSOLUTE':( '%|continue\n', ), + 'DELETE_SLICE+0':( '%|del %c[:]\n', 0 ), + 'DELETE_SLICE+1':( '%|del %c[%c:]\n', 0, 1 ), + 'DELETE_SLICE+2':( '%|del %c[:%c]\n', 0, 1 ), + 'DELETE_SLICE+3':( '%|del %c[%c:%c]\n', 0, 1, 2 ), + 'DELETE_ATTR': ( '%|del %c.%[-1]{pattr}\n', 0 ), + #'EXEC_STMT': ( '%|exec %c in %[1]C\n', 0, (0,sys.maxint,', ') ), + 'BINARY_SUBSCR':( '%c[%c]', 0, 1), # required for augmented assign + 'UNARY_POSITIVE':( '+%c', 0 ), + 'UNARY_NEGATIVE':( '-%c', 0 ), + 'UNARY_CONVERT':( '`%c`', 0 ), + 'UNARY_INVERT': ( '~%c', 0 ), + 'UNARY_NOT': ( '(not %c)', 0 ), + 'SLICE+0': ( '%c[:]', 0 ), + 'SLICE+1': ( '%c[%c:]', 0, 1 ), + 'SLICE+2': ( '%c[:%c]', 0, 1 ), + 'SLICE+3': ( '%c[%c:%c]', 0, 1, 2 ), +} +TABLE_R0 = { +# 'BUILD_LIST': ( '[%C]', (0,-1,', ') ), +# 'BUILD_TUPLE': ( '(%C)', (0,-1,', ') ), +# 'CALL_FUNCTION':( '%c(%C)', 0, (1,-1,', ') ), +} +TABLE_DIRECT = { + 'BINARY_ADD': ( '+' ,), + 'BINARY_SUBTRACT': ( '-' ,), + 'BINARY_MULTIPLY': ( '*' ,), + 'BINARY_DIVIDE': ( '/' ,), + 'BINARY_MODULO': ( '%%',), + 'BINARY_POWER': ( '**',), + 'BINARY_LSHIFT': ( '<<',), + 'BINARY_RSHIFT': ( '>>',), + 'BINARY_AND': ( '&' ,), + 'BINARY_OR': ( '|' ,), + 'BINARY_XOR': ( '^' ,), + 'INPLACE_ADD': ( '+=' ,), + 'INPLACE_SUBTRACT': ( '-=' ,), + 'INPLACE_MULTIPLY': ( '*=' ,), + 'INPLACE_DIVIDE': ( '/=' ,), + 'INPLACE_MODULO': ( '%%=',), + 'INPLACE_POWER': ( '**=',), + 'INPLACE_LSHIFT': ( '<<=',), + 'INPLACE_RSHIFT': ( '>>=',), + 'INPLACE_AND': ( '&=' ,), + 'INPLACE_OR': ( '|=' ,), + 'INPLACE_XOR': ( '^=' ,), + 'binary_expr': ( '(%c %c %c)', 0, -1, 1 ), + + 'IMPORT_FROM': ( '%{pattr}', ), + 'LOAD_ATTR': ( '.%{pattr}', ), + 'LOAD_FAST': ( '%{pattr}', ), + 'LOAD_NAME': ( '%{pattr}', ), + 'LOAD_GLOBAL': ( '%{pattr}', ), + 'LOAD_LOCALS': ( 'locals()', ), + #'LOAD_CONST': ( '%{pattr}', ), handled below + 'DELETE_FAST': ( '%|del %{pattr}\n', ), + 'DELETE_NAME': ( '%|del %{pattr}\n', ), + 'DELETE_GLOBAL':( '%|del %{pattr}\n', ), + 'delete_subscr':( '%|del %c[%c]\n', 0, 1,), + 'binary_subscr':( '%c[%c]', 0, 1), + 'store_subscr': ( '%c[%c]', 0, 1), + 'STORE_FAST': ( '%{pattr}', ), + 'STORE_NAME': ( '%{pattr}', ), + 'STORE_GLOBAL': ( '%{pattr}', ), + 'unpack': ( '(%C,)', (1, sys.maxint, ', ') ), + 'unpack_list': ( '[%C]', (1, sys.maxint, ', ') ), + + 'list_compr': ( '[ %c ]', 1), +# 'lc_for': ( ' for %c in %c', 3, 0 ), + 'lc_for_nest': ( ' for %c in %c%c', 3, 0, 4 ), + 'lc_if': ( ' if %c', 0 ), + 'lc_body': ( '%c', 1), + 'lc_body__': ( '', ), + + 'assign': ( '%|%c = %c\n', -1, 0 ), + 'augassign1': ( '%|%c %c %c\n', 0, 2, 1), + 'augassign2': ( '%|%c%c %c %c\n', 0, 2, -3, -4), + #'dup_topx': ('%c', 0), + 'designList': ( '%c = %c', 0, -1 ), + 'and': ( '(%c and %c)', 0, 3 ), + 'or': ( '(%c or %c)', 0, 3 ), + 'compare': ( '(%c %[-1]{pattr} %c)', 0, 1 ), + 'cmp_list': ('%c %c', 0, 1), + 'cmp_list1': ('%[3]{pattr} %c %c', 0, -2), + 'cmp_list2': ('%[1]{pattr} %c', 0), + 'classdef': ( '\n%|class %[0]{pattr[1:-1]}%c:\n%+%{build_class}%-', 1 ), + 'funcdef': ( '\n%|def %c\n', 0), + 'kwarg': ( '%[0]{pattr[1:-1]}=%c', 1), + 'importstmt': ( '%|import %[0]{pattr}\n', ), + 'importfrom': ( '%|from %[0]{pattr} import %c\n', 1 ), + 'importlist': ( '%C', (0, sys.maxint, ', ') ), + 'importstmt2': ( '%|import %c\n', 1), + 'importstar2': ( '%|from %[1]{pattr} import *\n', ), + 'importfrom2': ( '%|from %[1]{pattr} import %c\n', 2 ), + 'importlist2': ( '%C', (0, sys.maxint, ', ') ), + 'assert': ( '%|assert %c\n' , 3 ), + 'assert2': ( '%|assert %c, %c\n' , 3, -5 ), + + 'print_stmt': ( '%|print %c,\n', 0 ), + 'print_stmt_nl': ( '%|print %[0]C\n', (0,1, None) ), + 'print_nl_stmt': ( '%|print\n', ), + 'print_to': ( '%|print >> %c, %c,\n', 0, 1 ), + 'print_to_nl': ( '%|print >> %c, %c\n', 0, 1 ), + 'print_nl_to': ( '%|print >> %c\n', 0 ), + 'print_to_items': ( '%C', (0, 2, ', ') ), + + 'call_stmt': ( '%|%c\n', 0), + 'break_stmt': ( '%|break\n', ), + 'continue_stmt':( '%|continue\n', ), + 'raise_stmt': ( '%|raise %[0]C\n', (0,sys.maxint,', ') ), + 'return_stmt': ( '%|return %c\n', 0), + 'return_lambda': ( '%c', 0), + + 'ifstmt': ( '%|if %c:\n%+%c%-', 0, 2 ), + 'ifelsestmt': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 2, -2 ), + 'ifelifstmt': ( '%|if %c:\n%+%c%-%c', 0, 2, -2 ), + 'elifelifstmt': ( '%|elif %c:\n%+%c%-%c', 0, 2, -2 ), + 'elifstmt': ( '%|elif %c:\n%+%c%-', 0, 2 ), + 'elifelsestmt': ( '%|elif %c:\n%+%c%-%|else:\n%+%c%-', 0, 2, -2 ), + + 'whilestmt': ( '%|while %c:\n%+%c%-\n', 1, 4 ), + 'whileelsestmt':( '%|while %c:\n%+%c%-\n%|else:\n%+%c%-\n', 1, 4, 9 ), + 'forstmt': ( '%|for %c in %c:\n%+%c%-\n', 4, 1, 5 ), + 'forelsestmt': ( + '%|for %c in %c:\n%+%c%-\n%|else:\n%+%c%-\n', 4, 1, 5, 9 + ), + 'trystmt': ( '%|try:\n%+%c%-%c', 1, 5 ), + 'except': ( '%|except:\n%+%c%-', 3 ), + 'except_cond1': ( '%|except %c:\n%+%c%-', 1, 8 ), + 'except_cond2': ( '%|except %c, %c:\n%+%c%-', 1, 6, 8 ), + 'except_else': ( '%|else:\n%+%c%-', 2 ), + 'tryfinallystmt':( '%|try:\n%+%c%-\n%|finally:\n%+%c%-\n', 1, 5 ), + 'passstmt': ( '%|pass\n', ), + 'STORE_FAST': ( '%{pattr}', ), + 'kv': ( '%c: %c', 3, 1 ), + 'mapexpr': ( '{%[1]C}', (0,sys.maxint,', ') ), +} + + +MAP_DIRECT = (TABLE_DIRECT, ) +MAP_R0 = (TABLE_R0, -1, 0) +MAP_R = (TABLE_R, -1) + +MAP = { + 'stmt': MAP_R, + 'designator': MAP_R, + 'expr': MAP_R, + 'exprlist': MAP_R0, +} + + +ASSIGN_TUPLE_PARAM = lambda param_name: \ + AST('expr', [ Token('LOAD_FAST', pattr=param_name) ]) + + +def get_tuple_parameter(ast, name): + """ + If the name of the formal parameter starts with dot, + it's a tuple parameter, like this: + def MyFunc(xx, (a,b,c), yy): + print a, b*2, c*42 + In byte-code, the whole tuple is assigned to parameter '.1' and + then the tuple gets unpacked to 'a', 'b' and 'c'. + + Since identifiers starting with a dot are illegal in Python, + we can search for the byte-code equivalent to '(a,b,c) = .1' + """ + assert ast == 'code' and ast[0] == 'stmts' + for i in xrange(len(ast[0])): + # search for an assign-statement + assert ast[0][i] == 'stmt' + node = ast[0][i][0] + if node == 'assign' \ + and node[0] == ASSIGN_TUPLE_PARAM(name): + # okay, this assigns '.n' to something + del ast[0][i] + # walk lhs; this + # returns a tuple of identifiers as used + # within the function definition + assert node[1] == 'designator' + # if lhs is not a UNPACK_TUPLE (or equiv.), + # add parenteses to make this a tuple + if node[1][0] not in ('unpack', 'unpack_list'): + return '(' + walk(node[1]) + ')' + return walk(node[1]) + raise "Can't find tuple parameter" % name + +def make_function(self, code, defparams, isLambda, nested=1): + """Dump function defintion, doc string, and function body.""" + + def build_param(ast, name, default): + """build parameters: + - handle defaults + - handle format tuple parameters + """ + # if formal parameter is a tuple, the paramater name + # starts with a dot (eg. '.1', '.2') + if name[0] == '.': + # replace the name with the tuple-string + name = get_tuple_parameter(ast, name) + + if default: + if Showast: + print '--', name + print default + print '--' + result = '%s = %s' % ( name, walk(default, indent=0) ) + ##w = Walk(default, 0) + ##result = '%s = %s' % ( name, w.traverse() ) + ##del w # hg/2000-09-03 + if result[-2:] == '= ': # default was 'LOAD_CONST None' + result = result + 'None' + return result + else: + return name + + def writeParams(self, params): + for i in range(len(params)): + if i > 0: self.f.write(', ') + self.f.write(params[i]) + + assert type(code) == types.CodeType + code = Code(code) + #assert isinstance(code, Code) + + ast = _build_ast(self.f, code._tokens, code._customize) + code._tokens = None # save memory + assert ast == 'code' and ast[0] == 'stmts' + if isLambda: + # convert 'return' statement to expression + #assert len(ast[0]) == 1 wrong, see 'lambda (r,b): r,b,g' + assert ast[-1][-1] == 'stmt' + assert len(ast[-1][-1]) == 1 + assert ast[-1][-1][0] == 'return_stmt' + ast[-1][-1][0].type = 'return_lambda' + else: + if ast[0][-1] == RETURN_NONE: + # Python adds a 'return None' to the + # end of any function; remove it + ast[0].pop() # remove last node + + # add defaults values to parameter names + argc = code.co_argcount + paramnames = list(code.co_varnames[:argc]) + + # defaults are for last n parameters, thus reverse + paramnames.reverse(); defparams.reverse() + + # build parameters + # + ##This would be a nicer piece of code, but I can't get this to work + ## now, have to find a usable lambda constuct hG/2000-09-05 + ##params = map(lambda name, default: build_param(ast, name, default), + ## paramnames, defparams) + params = [] + for name, default in map(lambda a,b: (a,b), paramnames, defparams): + params.append( build_param(ast, name, default) ) + + params.reverse() # back to correct order + + if 4 & code.co_flags: # flag 2 -> variable number of args + params.append('*%s' % code.co_varnames[argc]) + argc = argc +1 + if 8 & code.co_flags: # flag 3 -> keyword args + params.append('**%s' % code.co_varnames[argc]) + argc = argc +1 + + # dump parameter list (with default values) + indent = TAB * self.indent + if isLambda: + self.f.write('lambda ') + writeParams(self, params) + self.f.write(': ') + else: + self.f.write('(') + writeParams(self, params) + self.f.write('):\n') + #self.f.write('%s#flags:\t%i\n' % (indent, code.co_flags)) + + if code.co_consts[0] != None: # docstring exists, dump it + self.f.writelines([indent, `code.co_consts[0]`, '\n']) + + _gen_source(self.f, ast, code._customize, self.indent, + isLambda=isLambda) + code._tokens = None; code._customize = None # save memory + + +def build_class(self, code): + """Dump class definition, duc string and class body.""" + + assert type(code) == types.CodeType + code = Code(code) + #assert isinstance(code, Code) + + indent = TAB * self.indent + #self.f.write('%s#flags:\t%i\n' % (indent, code.co_flags)) + ast = _build_ast(self.f, code._tokens, code._customize) + code._tokens = None # save memory + assert ast == 'code' and ast[0] == 'stmts' + + # if docstring exists, dump it + if code.co_consts[0] != None \ + and ast[0][0] == ASSIGN_DOC_STRING(code.co_consts[0]): + #print '\n\n>>-->>doc string set\n\n' + self.f.writelines( [indent,repr(code.co_consts[0]), '\n'] ) + del ast[0][0] + + # the function defining a class normally returns locals(); we + # don't want this to show up in the source, thus remove the node + if ast[0][-1] == RETURN_LOCALS: + ast[0].pop() # remove last node + + _gen_source(self.f, ast, code._customize, self.indent) + code._tokens = None; code._customize = None # save memory + +__globals_tokens__ = ('STORE_GLOBAL', 'DELETE_GLOBAL') # 'LOAD_GLOBAL' + +def find_globals(node, globals): + """Find globals in this statement.""" + for n in node: + if isinstance(n, AST): + if n != 'stmt': # skip nested statements + globals = find_globals(n, globals) + elif n.type in __globals_tokens__: + globals[n.pattr] = None + return globals + + +class Walk(GenericASTTraversal): + def __init__(self, ast, indent=0, isLambda=0): + GenericASTTraversal.__init__(self, ast) + self._globals = {} + self.f = cStringIO.StringIO() + self.f.seek(0) + self.indent = indent + self.isLambda = isLambda + + def __del__(self): + self.f.close() + + def traverse(self, node=None): + self.preorder(node) + return self.f.getvalue() + + def n_LOAD_CONST(self, node): + data = node.pattr + if data == 'None': + # LOAD_CONST 'None' only occurs, when None is + # implicit eg. in 'return' w/o params + pass + elif data == 'Ellipsis': + self.f.write('...') + elif data[0] == '-': # assume negative integer constant + # convert to hex, since decimal representation + # would result in 'LOAD_CONST; UNARY_NEGATIVE' + self.f.write('0x%x' % int(data)) + else: + self.f.write(data) + + def n_delete_subscr(self, node): + #print >>self.f, '>#', node + #print >>self.f, '---' + maybe_tuple = node[-2][-1] + #print >>self.f, '##', maybe_tuple, maybe_tuple.type[:11] + if maybe_tuple.type[:11] == 'BUILD_TUPLE': + maybe_tuple.type = 'build_tuple2' + #print >>self.f, '##', node + #print >>self.f, '##', maybe_tuple.type + self.default(node) + + n_store_subscr = n_binary_subscr = n_delete_subscr + + def __n_stmts(self, node): + # optimize "print 1, ; print" + last = None; i = 0 + while i < len(node): + n = node[i] + assert(n == 'stmt') + if n[0] == 'print_nl_stmt' and \ + last is not None and \ + last[0] == 'print_stmt': + last[0].type = 'print_stmt_nl' + del node[i] + last = None + else: + last = n + i = i + 1 + self.default(node) + + def n_stmt(self, node): + if not self.isLambda: + indent = TAB * self.indent + for g in find_globals(node, {}).keys(): + self.f.writelines( [indent, + 'global ', + g, '\n'] ) + ## nice output does not work since engine() + ## creates a new Walk instance when recursing + ## TODO: reconsider this: engine() no longer + ## creates a new Walk instancew hG/2000-12-31 + ## if not self._globals.has_key(g): + ## self._globals[g] = None + ## self.f.writelines( [TAB * self.indent, + ## 'global ', + ## g, '\n'] ) + self.default(node) + + def n_exec_stmt(self, node): + """ + exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT + exec_stmt ::= expr exprlist EXEC_STMT + """ + w = Walk(node, indent=self.indent) + w.engine(( '%|exec %c in %[1]C', 0, (0,sys.maxint,', ') ), + node) + s = w.f.getvalue() + del w + if s[-3:] == 'in ': + s = s[:-3] + self.f.writelines( [s, '\n'] ) + node[:] = [] # avoid print out when recursive descenting + + def n_ifelsestmt(self, node, preprocess=0): + if len(node[-2]) == 1: + ifnode = node[-2][0][0] + if ifnode == 'ifelsestmt': + node.type = 'ifelifstmt' + self.n_ifelsestmt(ifnode, preprocess=1) + if ifnode == 'ifelifstmt': + ifnode.type = 'elifelifstmt' + elif ifnode == 'ifelsestmt': + ifnode.type = 'elifelsestmt' + elif ifnode == 'ifstmt': + node.type = 'ifelifstmt' + ifnode.type = 'elifstmt' + if not preprocess: + self.default(node) + + def n_import_as(self, node): + iname = node[0].pattr; sname = node[-1].pattr + if iname == sname \ + or iname[:len(sname)+1] == (sname+'.'): + self.f.write(iname) + else: + self.f.writelines([iname, ' as ', sname]) + node[:] = [] # avoid print out when recursive descenting + + def n_mkfunc(self, node): + defparams = node[0:-2] + code = node[-2].attr + node[:] = [] # avoid print out when recursive descenting + self.indent = self.indent + 1 + self.f.write(code.co_name) + make_function(self, code, defparams, isLambda=0) + self.indent = self.indent - 1 + + def n_mklambda(self, node): + defparams = node[0:-2] + code = node[-2].attr + node[:] = [] # avoid print out when recursive descenting + make_function(self, code, defparams, isLambda=1) + + def n_classdef(self, node): + self.f.writelines(['\n', TAB * self.indent, 'class ']) + self.f.write(node[0].pattr[1:-1]) + node._code = node[-4][0].attr + # avoid print out when recursive descenting + if node[1] == BUILD_TUPLE_0: + node[:] = [] + else: + node[:] = [ node[1] ] + + def n_classdef_exit(self, node): + self.f.write(':\n') + self.indent = self.indent +1 + # '\n%|class %[0]{pattr[1:-1]}%c:\n%+%{build_class}%-', 1 ), + # -4 -> MAKE_FUNCTION; -2 -> LOAD_CONST (code) + build_class(self,node._code) + self.indent = self.indent -1 + node._code = None # save memory + + def n_lc_for(self, node): + node.type = 'lc_for_nest' + content = node[4] + while content == 'lc_for': + content.type = 'lc_for_nest' + content = content[4] + while content == 'lc_if': + content = content[2] + assert content == 'lc_body' + self.preorder(content) + content.type = 'lc_body__' + self.default(node) + + def engine(self, entry, startnode): + #self.f.write("-----\n") + #self.f.write(str(startnode.__dict__)); self.f.write('\n') + escape = re.compile(r''' + % ( \[ (?P -? \d+ ) \] )? + ((?P [^{] ) | + ( [{] (?P [^}]* ) [}] )) + ''', re.VERBOSE) + + fmt = entry[0] + n = len(fmt) + lastC = 0 + arg = 1 + i = 0 + + while i < n: + m = escape.match(fmt, i) + if m is None: + self.f.write(fmt[i]) + i = i + 1 + continue + + i = m.end() + typ = m.group('type') or '{' + + node = startnode + try: + if m.group('child'): + node = node[string.atoi(m.group('child'))] + except: + print node.__dict__ + raise + + if typ == '%': + self.f.write('%') + elif typ == '+': + self.indent = self.indent + 1 + elif typ == '-': + self.indent = self.indent - 1 + elif typ == '|': + self.f.write(TAB * self.indent) + elif typ == ',': + if lastC == 1: + self.f.write(',') + elif typ == 'c': + self.traverse(node[entry[arg]]) + ##w = Walk(node[entry[arg]], self.indent) + ##self.f.write(w.traverse()) + ##del w # hg/2000-09-03 + arg = arg + 1 + elif typ == 'C': + low, high, sep = entry[arg] + lastC = remaining = len(node[low:high]) + for subnode in node[low:high]: + self.traverse(subnode) + ##w = Walk(subnode, self.indent) + ##self.f.write(w.traverse()) + ##del w # hg/2000-09-03 + remaining = remaining - 1 + if remaining > 0: + self.f.write(sep) + arg = arg + 1 + elif typ == '{': + d = node.__dict__ + expr = m.group('expr') + if expr == 'build_class': + # -4 -> MAKE_FUNCTION; -2 -> LOAD_CONST (code) + build_class(self,node[-4][-2].attr) + else: + try: + self.f.write(eval(expr, d, d)) + except: + print node + raise + + def default(self, node): + mapping = MAP.get(node, MAP_DIRECT) + table = mapping[0] + key = node + + for i in mapping[1:]: + key = key[i] + + if table.has_key(key): + self.engine(table[key], node) + self.prune() + +def walk(ast, customize={}, indent=0, isLambda=0): + w = Walk(ast, indent, isLambda=isLambda) + # + # Special handling for opcodes that take a variable number + # of arguments -- we add a new entry for each in TABLE_R. + # + for k, v in customize.items(): + op = k[:string.rfind(k, '_')] + if op == 'BUILD_LIST': + TABLE_R[k] = ( '[%C]', (0,-1,', ') ) + elif op == 'BUILD_SLICE': + TABLE_R[k] = ( '%C', (0,-1,':') ) + elif op == 'BUILD_TUPLE': + TABLE_R[k] = ( '(%C%,)', (0,-1,', ') ) + elif op == 'CALL_FUNCTION': + TABLE_R[k] = ( '%c(%C)', 0, (1,-1,', ') ) + elif op in ('CALL_FUNCTION_VAR', + 'CALL_FUNCTION_VAR_KW', 'CALL_FUNCTION_KW'): + if v == 0: + str = '%c(%C' # '%C' is a dummy here ... + p2 = (0, 0, None) # .. because of this + else: + str = '%c(%C, ' + p2 = (1,-2, ', ') + if op == 'CALL_FUNCTION_VAR': + str = str + '*%c)' + entry = (str, 0, p2, -2) + elif op == 'CALL_FUNCTION_KW': + str = str + '**%c)' + entry = (str, 0, p2, -2) + else: + str = str + '*%c, **%c)' + if p2[2]: p2 = (1,-3, ', ') + entry = (str, 0, p2, -3, -2) + TABLE_R[k] = entry + result = w.traverse() + return result + +#-- end of (de-)compiler --- + +#-- start + +Showasm = 0 +Showast = 0 +__real_out = None + +def _tokenize(out, co): + """Disassemble code object into a token list""" + assert type(co) == types.CodeType + + tokens, customize = disassemble(co) + # See the disassembly.. + if Showasm and out is not None: + for t in tokens: + out.write('%s\n' % t) + out.write('\n') + return tokens, customize + +def _build_ast(out, tokens, customize): + assert type(tokens) == types.ListType + assert isinstance(tokens[0], Token) + + # Build AST from disassembly. + try: + ast = parse(tokens, customize) + except: # parser failed, dump disassembly + #if not Showasm: + __real_out.write('--- This code section failed: ---\n') + for t in tokens: + __real_out.write('%s\n' % t) + __real_out.write('\n') + raise + return ast + +def _gen_source(out, ast, customize, indent=0, isLambda=0): + """convert AST to source code""" + if Showast: + out.write(`ast`) + + # if code would be empty, append 'pass' + if len(ast[0]) == 0: + out.write(indent * TAB) + out.write('pass\n') + else: + out.write(walk(ast, customize, indent, isLambda=isLambda)) + + +def decompyle(co, out=None, indent=0, showasm=0, showast=0): + """ + diassembles a given code block 'co' + """ + assert type(co) == types.CodeType + + global Showasm, Showast + Showasm = showasm + Showast = showast + + if not out: + out = sys.stdout + global __real_out + __real_out = out # store final output stream for case of error + + tokens, customize = _tokenize(out, co) + ast = _build_ast(out, tokens, customize) + tokens = None # save memory + + assert ast == 'code' and ast[0] == 'stmts' + # convert leading '__doc__ = "..." into doc string + if ast[0][0] == ASSIGN_DOC_STRING(co.co_consts[0]): + out.writelines( [repr(co.co_consts[0]), '\n'] ) + del ast[0][0] + if ast[0][-1] == RETURN_NONE: + ast[0].pop() # remove last node + #todo: if empty, add 'pass' + + _gen_source(out, ast, customize, indent) + + +def decompyle_file(filename, outstream=None, showasm=0, showast=0): + """ + decompile Python byte-code file (.pyc) + """ + co = _load_module(filename) + decompyle(co, out=outstream, showasm=showasm, showast=showast) + co = None diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/difflib.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/difflib.py new file mode 100644 index 00000000..01dc2037 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/difflib.py @@ -0,0 +1,1083 @@ +#! /usr/bin/env python + +from __future__ import generators + +""" +Module difflib -- helpers for computing deltas between objects. + +Function get_close_matches(word, possibilities, n=3, cutoff=0.6): + Use SequenceMatcher to return list of the best "good enough" matches. + +Function ndiff(a, b): + Return a delta: the difference between `a` and `b` (lists of strings). + +Function restore(delta, which): + Return one of the two sequences that generated an ndiff delta. + +Class SequenceMatcher: + A flexible class for comparing pairs of sequences of any type. + +Class Differ: + For producing human-readable deltas from sequences of lines of text. +""" + +__all__ = ['get_close_matches', 'ndiff', 'restore', 'SequenceMatcher', + 'Differ'] + +class SequenceMatcher: + + """ + SequenceMatcher is a flexible class for comparing pairs of sequences of + any type, so long as the sequence elements are hashable. The basic + algorithm predates, and is a little fancier than, an algorithm + published in the late 1980's by Ratcliff and Obershelp under the + hyperbolic name "gestalt pattern matching". The basic idea is to find + the longest contiguous matching subsequence that contains no "junk" + elements (R-O doesn't address junk). The same idea is then applied + recursively to the pieces of the sequences to the left and to the right + of the matching subsequence. This does not yield minimal edit + sequences, but does tend to yield matches that "look right" to people. + + SequenceMatcher tries to compute a "human-friendly diff" between two + sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the + longest *contiguous* & junk-free matching subsequence. That's what + catches peoples' eyes. The Windows(tm) windiff has another interesting + notion, pairing up elements that appear uniquely in each sequence. + That, and the method here, appear to yield more intuitive difference + reports than does diff. This method appears to be the least vulnerable + to synching up on blocks of "junk lines", though (like blank lines in + ordinary text files, or maybe "

    " lines in HTML files). That may be + because this is the only method of the 3 that has a *concept* of + "junk" . + + Example, comparing two strings, and considering blanks to be "junk": + + >>> s = SequenceMatcher(lambda x: x == " ", + ... "private Thread currentThread;", + ... "private volatile Thread currentThread;") + >>> + + .ratio() returns a float in [0, 1], measuring the "similarity" of the + sequences. As a rule of thumb, a .ratio() value over 0.6 means the + sequences are close matches: + + >>> print round(s.ratio(), 3) + 0.866 + >>> + + If you're only interested in where the sequences match, + .get_matching_blocks() is handy: + + >>> for block in s.get_matching_blocks(): + ... print "a[%d] and b[%d] match for %d elements" % block + a[0] and b[0] match for 8 elements + a[8] and b[17] match for 6 elements + a[14] and b[23] match for 15 elements + a[29] and b[38] match for 0 elements + + Note that the last tuple returned by .get_matching_blocks() is always a + dummy, (len(a), len(b), 0), and this is the only case in which the last + tuple element (number of elements matched) is 0. + + If you want to know how to change the first sequence into the second, + use .get_opcodes(): + + >>> for opcode in s.get_opcodes(): + ... print "%6s a[%d:%d] b[%d:%d]" % opcode + equal a[0:8] b[0:8] + insert a[8:8] b[8:17] + equal a[8:14] b[17:23] + equal a[14:29] b[23:38] + + See the Differ class for a fancy human-friendly file differencer, which + uses SequenceMatcher both to compare sequences of lines, and to compare + sequences of characters within similar (near-matching) lines. + + See also function get_close_matches() in this module, which shows how + simple code building on SequenceMatcher can be used to do useful work. + + Timing: Basic R-O is cubic time worst case and quadratic time expected + case. SequenceMatcher is quadratic time for the worst case and has + expected-case behavior dependent in a complicated way on how many + elements the sequences have in common; best case time is linear. + + Methods: + + __init__(isjunk=None, a='', b='') + Construct a SequenceMatcher. + + set_seqs(a, b) + Set the two sequences to be compared. + + set_seq1(a) + Set the first sequence to be compared. + + set_seq2(b) + Set the second sequence to be compared. + + find_longest_match(alo, ahi, blo, bhi) + Find longest matching block in a[alo:ahi] and b[blo:bhi]. + + get_matching_blocks() + Return list of triples describing matching subsequences. + + get_opcodes() + Return list of 5-tuples describing how to turn a into b. + + ratio() + Return a measure of the sequences' similarity (float in [0,1]). + + quick_ratio() + Return an upper bound on .ratio() relatively quickly. + + real_quick_ratio() + Return an upper bound on ratio() very quickly. + """ + + def __init__(self, isjunk=None, a='', b=''): + """Construct a SequenceMatcher. + + Optional arg isjunk is None (the default), or a one-argument + function that takes a sequence element and returns true iff the + element is junk. None is equivalent to passing "lambda x: 0", i.e. + no elements are considered to be junk. For example, pass + lambda x: x in " \\t" + if you're comparing lines as sequences of characters, and don't + want to synch up on blanks or hard tabs. + + Optional arg a is the first of two sequences to be compared. By + default, an empty string. The elements of a must be hashable. See + also .set_seqs() and .set_seq1(). + + Optional arg b is the second of two sequences to be compared. By + default, an empty string. The elements of b must be hashable. See + also .set_seqs() and .set_seq2(). + """ + + # Members: + # a + # first sequence + # b + # second sequence; differences are computed as "what do + # we need to do to 'a' to change it into 'b'?" + # b2j + # for x in b, b2j[x] is a list of the indices (into b) + # at which x appears; junk elements do not appear + # b2jhas + # b2j.has_key + # fullbcount + # for x in b, fullbcount[x] == the number of times x + # appears in b; only materialized if really needed (used + # only for computing quick_ratio()) + # matching_blocks + # a list of (i, j, k) triples, where a[i:i+k] == b[j:j+k]; + # ascending & non-overlapping in i and in j; terminated by + # a dummy (len(a), len(b), 0) sentinel + # opcodes + # a list of (tag, i1, i2, j1, j2) tuples, where tag is + # one of + # 'replace' a[i1:i2] should be replaced by b[j1:j2] + # 'delete' a[i1:i2] should be deleted + # 'insert' b[j1:j2] should be inserted + # 'equal' a[i1:i2] == b[j1:j2] + # isjunk + # a user-supplied function taking a sequence element and + # returning true iff the element is "junk" -- this has + # subtle but helpful effects on the algorithm, which I'll + # get around to writing up someday <0.9 wink>. + # DON'T USE! Only __chain_b uses this. Use isbjunk. + # isbjunk + # for x in b, isbjunk(x) == isjunk(x) but much faster; + # it's really the has_key method of a hidden dict. + # DOES NOT WORK for x in a! + + self.isjunk = isjunk + self.a = self.b = None + self.set_seqs(a, b) + + def set_seqs(self, a, b): + """Set the two sequences to be compared. + + >>> s = SequenceMatcher() + >>> s.set_seqs("abcd", "bcde") + >>> s.ratio() + 0.75 + """ + + self.set_seq1(a) + self.set_seq2(b) + + def set_seq1(self, a): + """Set the first sequence to be compared. + + The second sequence to be compared is not changed. + + >>> s = SequenceMatcher(None, "abcd", "bcde") + >>> s.ratio() + 0.75 + >>> s.set_seq1("bcde") + >>> s.ratio() + 1.0 + >>> + + SequenceMatcher computes and caches detailed information about the + second sequence, so if you want to compare one sequence S against + many sequences, use .set_seq2(S) once and call .set_seq1(x) + repeatedly for each of the other sequences. + + See also set_seqs() and set_seq2(). + """ + + if a is self.a: + return + self.a = a + self.matching_blocks = self.opcodes = None + + def set_seq2(self, b): + """Set the second sequence to be compared. + + The first sequence to be compared is not changed. + + >>> s = SequenceMatcher(None, "abcd", "bcde") + >>> s.ratio() + 0.75 + >>> s.set_seq2("abcd") + >>> s.ratio() + 1.0 + >>> + + SequenceMatcher computes and caches detailed information about the + second sequence, so if you want to compare one sequence S against + many sequences, use .set_seq2(S) once and call .set_seq1(x) + repeatedly for each of the other sequences. + + See also set_seqs() and set_seq1(). + """ + + if b is self.b: + return + self.b = b + self.matching_blocks = self.opcodes = None + self.fullbcount = None + self.__chain_b() + + # For each element x in b, set b2j[x] to a list of the indices in + # b where x appears; the indices are in increasing order; note that + # the number of times x appears in b is len(b2j[x]) ... + # when self.isjunk is defined, junk elements don't show up in this + # map at all, which stops the central find_longest_match method + # from starting any matching block at a junk element ... + # also creates the fast isbjunk function ... + # note that this is only called when b changes; so for cross-product + # kinds of matches, it's best to call set_seq2 once, then set_seq1 + # repeatedly + + def __chain_b(self): + # Because isjunk is a user-defined (not C) function, and we test + # for junk a LOT, it's important to minimize the number of calls. + # Before the tricks described here, __chain_b was by far the most + # time-consuming routine in the whole module! If anyone sees + # Jim Roskind, thank him again for profile.py -- I never would + # have guessed that. + # The first trick is to build b2j ignoring the possibility + # of junk. I.e., we don't call isjunk at all yet. Throwing + # out the junk later is much cheaper than building b2j "right" + # from the start. + b = self.b + self.b2j = b2j = {} + self.b2jhas = b2jhas = b2j.has_key + for i in xrange(len(b)): + elt = b[i] + if b2jhas(elt): + b2j[elt].append(i) + else: + b2j[elt] = [i] + + # Now b2j.keys() contains elements uniquely, and especially when + # the sequence is a string, that's usually a good deal smaller + # than len(string). The difference is the number of isjunk calls + # saved. + isjunk, junkdict = self.isjunk, {} + if isjunk: + for elt in b2j.keys(): + if isjunk(elt): + junkdict[elt] = 1 # value irrelevant; it's a set + del b2j[elt] + + # Now for x in b, isjunk(x) == junkdict.has_key(x), but the + # latter is much faster. Note too that while there may be a + # lot of junk in the sequence, the number of *unique* junk + # elements is probably small. So the memory burden of keeping + # this dict alive is likely trivial compared to the size of b2j. + self.isbjunk = junkdict.has_key + + def find_longest_match(self, alo, ahi, blo, bhi): + """Find longest matching block in a[alo:ahi] and b[blo:bhi]. + + If isjunk is not defined: + + Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where + alo <= i <= i+k <= ahi + blo <= j <= j+k <= bhi + and for all (i',j',k') meeting those conditions, + k >= k' + i <= i' + and if i == i', j <= j' + + In other words, of all maximal matching blocks, return one that + starts earliest in a, and of all those maximal matching blocks that + start earliest in a, return the one that starts earliest in b. + + >>> s = SequenceMatcher(None, " abcd", "abcd abcd") + >>> s.find_longest_match(0, 5, 0, 9) + (0, 4, 5) + + If isjunk is defined, first the longest matching block is + determined as above, but with the additional restriction that no + junk element appears in the block. Then that block is extended as + far as possible by matching (only) junk elements on both sides. So + the resulting block never matches on junk except as identical junk + happens to be adjacent to an "interesting" match. + + Here's the same example as before, but considering blanks to be + junk. That prevents " abcd" from matching the " abcd" at the tail + end of the second sequence directly. Instead only the "abcd" can + match, and matches the leftmost "abcd" in the second sequence: + + >>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd") + >>> s.find_longest_match(0, 5, 0, 9) + (1, 0, 4) + + If no blocks match, return (alo, blo, 0). + + >>> s = SequenceMatcher(None, "ab", "c") + >>> s.find_longest_match(0, 2, 0, 1) + (0, 0, 0) + """ + + # CAUTION: stripping common prefix or suffix would be incorrect. + # E.g., + # ab + # acab + # Longest matching block is "ab", but if common prefix is + # stripped, it's "a" (tied with "b"). UNIX(tm) diff does so + # strip, so ends up claiming that ab is changed to acab by + # inserting "ca" in the middle. That's minimal but unintuitive: + # "it's obvious" that someone inserted "ac" at the front. + # Windiff ends up at the same place as diff, but by pairing up + # the unique 'b's and then matching the first two 'a's. + + a, b, b2j, isbjunk = self.a, self.b, self.b2j, self.isbjunk + besti, bestj, bestsize = alo, blo, 0 + # find longest junk-free match + # during an iteration of the loop, j2len[j] = length of longest + # junk-free match ending with a[i-1] and b[j] + j2len = {} + nothing = [] + for i in xrange(alo, ahi): + # look at all instances of a[i] in b; note that because + # b2j has no junk keys, the loop is skipped if a[i] is junk + j2lenget = j2len.get + newj2len = {} + for j in b2j.get(a[i], nothing): + # a[i] matches b[j] + if j < blo: + continue + if j >= bhi: + break + k = newj2len[j] = j2lenget(j-1, 0) + 1 + if k > bestsize: + besti, bestj, bestsize = i-k+1, j-k+1, k + j2len = newj2len + + # Now that we have a wholly interesting match (albeit possibly + # empty!), we may as well suck up the matching junk on each + # side of it too. Can't think of a good reason not to, and it + # saves post-processing the (possibly considerable) expense of + # figuring out what to do with it. In the case of an empty + # interesting match, this is clearly the right thing to do, + # because no other kind of match is possible in the regions. + while besti > alo and bestj > blo and \ + isbjunk(b[bestj-1]) and \ + a[besti-1] == b[bestj-1]: + besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 + while besti+bestsize < ahi and bestj+bestsize < bhi and \ + isbjunk(b[bestj+bestsize]) and \ + a[besti+bestsize] == b[bestj+bestsize]: + bestsize = bestsize + 1 + + return besti, bestj, bestsize + + def get_matching_blocks(self): + """Return list of triples describing matching subsequences. + + Each triple is of the form (i, j, n), and means that + a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in + i and in j. + + The last triple is a dummy, (len(a), len(b), 0), and is the only + triple with n==0. + + >>> s = SequenceMatcher(None, "abxcd", "abcd") + >>> s.get_matching_blocks() + [(0, 0, 2), (3, 2, 2), (5, 4, 0)] + """ + + if self.matching_blocks is not None: + return self.matching_blocks + self.matching_blocks = [] + la, lb = len(self.a), len(self.b) + self.__helper(0, la, 0, lb, self.matching_blocks) + self.matching_blocks.append( (la, lb, 0) ) + return self.matching_blocks + + # builds list of matching blocks covering a[alo:ahi] and + # b[blo:bhi], appending them in increasing order to answer + + def __helper(self, alo, ahi, blo, bhi, answer): + i, j, k = x = self.find_longest_match(alo, ahi, blo, bhi) + # a[alo:i] vs b[blo:j] unknown + # a[i:i+k] same as b[j:j+k] + # a[i+k:ahi] vs b[j+k:bhi] unknown + if k: + if alo < i and blo < j: + self.__helper(alo, i, blo, j, answer) + answer.append(x) + if i+k < ahi and j+k < bhi: + self.__helper(i+k, ahi, j+k, bhi, answer) + + def get_opcodes(self): + """Return list of 5-tuples describing how to turn a into b. + + Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple + has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the + tuple preceding it, and likewise for j1 == the previous j2. + + The tags are strings, with these meanings: + + 'replace': a[i1:i2] should be replaced by b[j1:j2] + 'delete': a[i1:i2] should be deleted. + Note that j1==j2 in this case. + 'insert': b[j1:j2] should be inserted at a[i1:i1]. + Note that i1==i2 in this case. + 'equal': a[i1:i2] == b[j1:j2] + + >>> a = "qabxcd" + >>> b = "abycdf" + >>> s = SequenceMatcher(None, a, b) + >>> for tag, i1, i2, j1, j2 in s.get_opcodes(): + ... print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" % + ... (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2])) + delete a[0:1] (q) b[0:0] () + equal a[1:3] (ab) b[0:2] (ab) + replace a[3:4] (x) b[2:3] (y) + equal a[4:6] (cd) b[3:5] (cd) + insert a[6:6] () b[5:6] (f) + """ + + if self.opcodes is not None: + return self.opcodes + i = j = 0 + self.opcodes = answer = [] + for ai, bj, size in self.get_matching_blocks(): + # invariant: we've pumped out correct diffs to change + # a[:i] into b[:j], and the next matching block is + # a[ai:ai+size] == b[bj:bj+size]. So we need to pump + # out a diff to change a[i:ai] into b[j:bj], pump out + # the matching block, and move (i,j) beyond the match + tag = '' + if i < ai and j < bj: + tag = 'replace' + elif i < ai: + tag = 'delete' + elif j < bj: + tag = 'insert' + if tag: + answer.append( (tag, i, ai, j, bj) ) + i, j = ai+size, bj+size + # the list of matching blocks is terminated by a + # sentinel with size 0 + if size: + answer.append( ('equal', ai, i, bj, j) ) + return answer + + def ratio(self): + """Return a measure of the sequences' similarity (float in [0,1]). + + Where T is the total number of elements in both sequences, and + M is the number of matches, this is 2,0*M / T. + Note that this is 1 if the sequences are identical, and 0 if + they have nothing in common. + + .ratio() is expensive to compute if you haven't already computed + .get_matching_blocks() or .get_opcodes(), in which case you may + want to try .quick_ratio() or .real_quick_ratio() first to get an + upper bound. + + >>> s = SequenceMatcher(None, "abcd", "bcde") + >>> s.ratio() + 0.75 + >>> s.quick_ratio() + 0.75 + >>> s.real_quick_ratio() + 1.0 + """ + + matches = reduce(lambda sum, triple: sum + triple[-1], + self.get_matching_blocks(), 0) + return 2.0 * matches / (len(self.a) + len(self.b)) + + def quick_ratio(self): + """Return an upper bound on ratio() relatively quickly. + + This isn't defined beyond that it is an upper bound on .ratio(), and + is faster to compute. + """ + + # viewing a and b as multisets, set matches to the cardinality + # of their intersection; this counts the number of matches + # without regard to order, so is clearly an upper bound + if self.fullbcount is None: + self.fullbcount = fullbcount = {} + for elt in self.b: + fullbcount[elt] = fullbcount.get(elt, 0) + 1 + fullbcount = self.fullbcount + # avail[x] is the number of times x appears in 'b' less the + # number of times we've seen it in 'a' so far ... kinda + avail = {} + availhas, matches = avail.has_key, 0 + for elt in self.a: + if availhas(elt): + numb = avail[elt] + else: + numb = fullbcount.get(elt, 0) + avail[elt] = numb - 1 + if numb > 0: + matches = matches + 1 + return 2.0 * matches / (len(self.a) + len(self.b)) + + def real_quick_ratio(self): + """Return an upper bound on ratio() very quickly. + + This isn't defined beyond that it is an upper bound on .ratio(), and + is faster to compute than either .ratio() or .quick_ratio(). + """ + + la, lb = len(self.a), len(self.b) + # can't have more matches than the number of elements in the + # shorter sequence + return 2.0 * min(la, lb) / (la + lb) + +def get_close_matches(word, possibilities, n=3, cutoff=0.6): + """Use SequenceMatcher to return list of the best "good enough" matches. + + word is a sequence for which close matches are desired (typically a + string). + + possibilities is a list of sequences against which to match word + (typically a list of strings). + + Optional arg n (default 3) is the maximum number of close matches to + return. n must be > 0. + + Optional arg cutoff (default 0.6) is a float in [0, 1]. Possibilities + that don't score at least that similar to word are ignored. + + The best (no more than n) matches among the possibilities are returned + in a list, sorted by similarity score, most similar first. + + >>> get_close_matches("appel", ["ape", "apple", "peach", "puppy"]) + ['apple', 'ape'] + >>> import keyword as _keyword + >>> get_close_matches("wheel", _keyword.kwlist) + ['while'] + >>> get_close_matches("apple", _keyword.kwlist) + [] + >>> get_close_matches("accept", _keyword.kwlist) + ['except'] + """ + + if not n > 0: + raise ValueError("n must be > 0: " + `n`) + if not 0.0 <= cutoff <= 1.0: + raise ValueError("cutoff must be in [0.0, 1.0]: " + `cutoff`) + result = [] + s = SequenceMatcher() + s.set_seq2(word) + for x in possibilities: + s.set_seq1(x) + if s.real_quick_ratio() >= cutoff and \ + s.quick_ratio() >= cutoff and \ + s.ratio() >= cutoff: + result.append((s.ratio(), x)) + # Sort by score. + result.sort() + # Retain only the best n. + result = result[-n:] + # Move best-scorer to head of list. + result.reverse() + # Strip scores. + return [x for score, x in result] + + +def _count_leading(line, ch): + """ + Return number of `ch` characters at the start of `line`. + + Example: + + >>> _count_leading(' abc', ' ') + 3 + """ + + i, n = 0, len(line) + while i < n and line[i] == ch: + i += 1 + return i + +class Differ: + r""" + Differ is a class for comparing sequences of lines of text, and + producing human-readable differences or deltas. Differ uses + SequenceMatcher both to compare sequences of lines, and to compare + sequences of characters within similar (near-matching) lines. + + Each line of a Differ delta begins with a two-letter code: + + '- ' line unique to sequence 1 + '+ ' line unique to sequence 2 + ' ' line common to both sequences + '? ' line not present in either input sequence + + Lines beginning with '? ' attempt to guide the eye to intraline + differences, and were not present in either input sequence. These lines + can be confusing if the sequences contain tab characters. + + Note that Differ makes no claim to produce a *minimal* diff. To the + contrary, minimal diffs are often counter-intuitive, because they synch + up anywhere possible, sometimes accidental matches 100 pages apart. + Restricting synch points to contiguous matches preserves some notion of + locality, at the occasional cost of producing a longer diff. + + Example: Comparing two texts. + + First we set up the texts, sequences of individual single-line strings + ending with newlines (such sequences can also be obtained from the + `readlines()` method of file-like objects): + + >>> text1 = ''' 1. Beautiful is better than ugly. + ... 2. Explicit is better than implicit. + ... 3. Simple is better than complex. + ... 4. Complex is better than complicated. + ... '''.splitlines(1) + >>> len(text1) + 4 + >>> text1[0][-1] + '\n' + >>> text2 = ''' 1. Beautiful is better than ugly. + ... 3. Simple is better than complex. + ... 4. Complicated is better than complex. + ... 5. Flat is better than nested. + ... '''.splitlines(1) + + Next we instantiate a Differ object: + + >>> d = Differ() + + Note that when instantiating a Differ object we may pass functions to + filter out line and character 'junk'. See Differ.__init__ for details. + + Finally, we compare the two: + + >>> result = list(d.compare(text1, text2)) + + 'result' is a list of strings, so let's pretty-print it: + + >>> from pprint import pprint as _pprint + >>> _pprint(result) + [' 1. Beautiful is better than ugly.\n', + '- 2. Explicit is better than implicit.\n', + '- 3. Simple is better than complex.\n', + '+ 3. Simple is better than complex.\n', + '? ++\n', + '- 4. Complex is better than complicated.\n', + '? ^ ---- ^\n', + '+ 4. Complicated is better than complex.\n', + '? ++++ ^ ^\n', + '+ 5. Flat is better than nested.\n'] + + As a single multi-line string it looks like this: + + >>> print ''.join(result), + 1. Beautiful is better than ugly. + - 2. Explicit is better than implicit. + - 3. Simple is better than complex. + + 3. Simple is better than complex. + ? ++ + - 4. Complex is better than complicated. + ? ^ ---- ^ + + 4. Complicated is better than complex. + ? ++++ ^ ^ + + 5. Flat is better than nested. + + Methods: + + __init__(linejunk=None, charjunk=None) + Construct a text differencer, with optional filters. + + compare(a, b) + Compare two sequences of lines; generate the resulting delta. + """ + + def __init__(self, linejunk=None, charjunk=None): + """ + Construct a text differencer, with optional filters. + + The two optional keyword parameters are for filter functions: + + - `linejunk`: A function that should accept a single string argument, + and return true iff the string is junk. The module-level function + `IS_LINE_JUNK` may be used to filter out lines without visible + characters, except for at most one splat ('#'). + + - `charjunk`: A function that should accept a string of length 1. The + module-level function `IS_CHARACTER_JUNK` may be used to filter out + whitespace characters (a blank or tab; **note**: bad idea to include + newline in this!). + """ + + self.linejunk = linejunk + self.charjunk = charjunk + + def compare(self, a, b): + r""" + Compare two sequences of lines; generate the resulting delta. + + Each sequence must contain individual single-line strings ending with + newlines. Such sequences can be obtained from the `readlines()` method + of file-like objects. The delta generated also consists of newline- + terminated strings, ready to be printed as-is via the writeline() + method of a file-like object. + + Example: + + >>> print ''.join(Differ().compare('one\ntwo\nthree\n'.splitlines(1), + ... 'ore\ntree\nemu\n'.splitlines(1))), + - one + ? ^ + + ore + ? ^ + - two + - three + ? - + + tree + + emu + """ + + cruncher = SequenceMatcher(self.linejunk, a, b) + for tag, alo, ahi, blo, bhi in cruncher.get_opcodes(): + if tag == 'replace': + g = self._fancy_replace(a, alo, ahi, b, blo, bhi) + elif tag == 'delete': + g = self._dump('-', a, alo, ahi) + elif tag == 'insert': + g = self._dump('+', b, blo, bhi) + elif tag == 'equal': + g = self._dump(' ', a, alo, ahi) + else: + raise ValueError, 'unknown tag ' + `tag` + + for line in g: + yield line + + def _dump(self, tag, x, lo, hi): + """Generate comparison results for a same-tagged range.""" + for i in xrange(lo, hi): + yield '%s %s' % (tag, x[i]) + + def _plain_replace(self, a, alo, ahi, b, blo, bhi): + assert alo < ahi and blo < bhi + # dump the shorter block first -- reduces the burden on short-term + # memory if the blocks are of very different sizes + if bhi - blo < ahi - alo: + first = self._dump('+', b, blo, bhi) + second = self._dump('-', a, alo, ahi) + else: + first = self._dump('-', a, alo, ahi) + second = self._dump('+', b, blo, bhi) + + for g in first, second: + for line in g: + yield line + + def _fancy_replace(self, a, alo, ahi, b, blo, bhi): + r""" + When replacing one block of lines with another, search the blocks + for *similar* lines; the best-matching pair (if any) is used as a + synch point, and intraline difference marking is done on the + similar pair. Lots of work, but often worth it. + + Example: + + >>> d = Differ() + >>> d._fancy_replace(['abcDefghiJkl\n'], 0, 1, ['abcdefGhijkl\n'], 0, 1) + >>> print ''.join(d.results), + - abcDefghiJkl + ? ^ ^ ^ + + abcdefGhijkl + ? ^ ^ ^ + """ + + # don't synch up unless the lines have a similarity score of at + # least cutoff; best_ratio tracks the best score seen so far + best_ratio, cutoff = 0.74, 0.75 + cruncher = SequenceMatcher(self.charjunk) + eqi, eqj = None, None # 1st indices of equal lines (if any) + + # search for the pair that matches best without being identical + # (identical lines must be junk lines, & we don't want to synch up + # on junk -- unless we have to) + for j in xrange(blo, bhi): + bj = b[j] + cruncher.set_seq2(bj) + for i in xrange(alo, ahi): + ai = a[i] + if ai == bj: + if eqi is None: + eqi, eqj = i, j + continue + cruncher.set_seq1(ai) + # computing similarity is expensive, so use the quick + # upper bounds first -- have seen this speed up messy + # compares by a factor of 3. + # note that ratio() is only expensive to compute the first + # time it's called on a sequence pair; the expensive part + # of the computation is cached by cruncher + if cruncher.real_quick_ratio() > best_ratio and \ + cruncher.quick_ratio() > best_ratio and \ + cruncher.ratio() > best_ratio: + best_ratio, best_i, best_j = cruncher.ratio(), i, j + if best_ratio < cutoff: + # no non-identical "pretty close" pair + if eqi is None: + # no identical pair either -- treat it as a straight replace + for line in self._plain_replace(a, alo, ahi, b, blo, bhi): + yield line + return + # no close pair, but an identical pair -- synch up on that + best_i, best_j, best_ratio = eqi, eqj, 1.0 + else: + # there's a close pair, so forget the identical pair (if any) + eqi = None + + # a[best_i] very similar to b[best_j]; eqi is None iff they're not + # identical + + # pump out diffs from before the synch point + for line in self._fancy_helper(a, alo, best_i, b, blo, best_j): + yield line + + # do intraline marking on the synch pair + aelt, belt = a[best_i], b[best_j] + if eqi is None: + # pump out a '-', '?', '+', '?' quad for the synched lines + atags = btags = "" + cruncher.set_seqs(aelt, belt) + for tag, ai1, ai2, bj1, bj2 in cruncher.get_opcodes(): + la, lb = ai2 - ai1, bj2 - bj1 + if tag == 'replace': + atags += '^' * la + btags += '^' * lb + elif tag == 'delete': + atags += '-' * la + elif tag == 'insert': + btags += '+' * lb + elif tag == 'equal': + atags += ' ' * la + btags += ' ' * lb + else: + raise ValueError, 'unknown tag ' + `tag` + for line in self._qformat(aelt, belt, atags, btags): + yield line + else: + # the synch pair is identical + yield ' ' + aelt + + # pump out diffs from after the synch point + for line in self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi): + yield line + + def _fancy_helper(self, a, alo, ahi, b, blo, bhi): + g = [] + if alo < ahi: + if blo < bhi: + g = self._fancy_replace(a, alo, ahi, b, blo, bhi) + else: + g = self._dump('-', a, alo, ahi) + elif blo < bhi: + g = self._dump('+', b, blo, bhi) + + for line in g: + yield line + + def _qformat(self, aline, bline, atags, btags): + r""" + Format "?" output and deal with leading tabs. + + Example: + + >>> d = Differ() + >>> d._qformat('\tabcDefghiJkl\n', '\t\tabcdefGhijkl\n', + ... ' ^ ^ ^ ', '+ ^ ^ ^ ') + >>> for line in d.results: print repr(line) + ... + '- \tabcDefghiJkl\n' + '? \t ^ ^ ^\n' + '+ \t\tabcdefGhijkl\n' + '? \t ^ ^ ^\n' + """ + + # Can hurt, but will probably help most of the time. + common = min(_count_leading(aline, "\t"), + _count_leading(bline, "\t")) + common = min(common, _count_leading(atags[:common], " ")) + atags = atags[common:].rstrip() + btags = btags[common:].rstrip() + + yield "- " + aline + if atags: + yield "? %s%s\n" % ("\t" * common, atags) + + yield "+ " + bline + if btags: + yield "? %s%s\n" % ("\t" * common, btags) + +# With respect to junk, an earlier version of ndiff simply refused to +# *start* a match with a junk element. The result was cases like this: +# before: private Thread currentThread; +# after: private volatile Thread currentThread; +# If you consider whitespace to be junk, the longest contiguous match +# not starting with junk is "e Thread currentThread". So ndiff reported +# that "e volatil" was inserted between the 't' and the 'e' in "private". +# While an accurate view, to people that's absurd. The current version +# looks for matching blocks that are entirely junk-free, then extends the +# longest one of those as far as possible but only with matching junk. +# So now "currentThread" is matched, then extended to suck up the +# preceding blank; then "private" is matched, and extended to suck up the +# following blank; then "Thread" is matched; and finally ndiff reports +# that "volatile " was inserted before "Thread". The only quibble +# remaining is that perhaps it was really the case that " volatile" +# was inserted after "private". I can live with that . + +import re + +def IS_LINE_JUNK(line, pat=re.compile(r"\s*#?\s*$").match): + r""" + Return 1 for ignorable line: iff `line` is blank or contains a single '#'. + + Examples: + + >>> IS_LINE_JUNK('\n') + 1 + >>> IS_LINE_JUNK(' # \n') + 1 + >>> IS_LINE_JUNK('hello\n') + 0 + """ + + return pat(line) is not None + +def IS_CHARACTER_JUNK(ch, ws=" \t"): + r""" + Return 1 for ignorable character: iff `ch` is a space or tab. + + Examples: + + >>> IS_CHARACTER_JUNK(' ') + 1 + >>> IS_CHARACTER_JUNK('\t') + 1 + >>> IS_CHARACTER_JUNK('\n') + 0 + >>> IS_CHARACTER_JUNK('x') + 0 + """ + + return ch in ws + +del re + +def ndiff(a, b, linejunk=IS_LINE_JUNK, charjunk=IS_CHARACTER_JUNK): + r""" + Compare `a` and `b` (lists of strings); return a `Differ`-style delta. + + Optional keyword parameters `linejunk` and `charjunk` are for filter + functions (or None): + + - linejunk: A function that should accept a single string argument, and + return true iff the string is junk. The default is module-level function + IS_LINE_JUNK, which filters out lines without visible characters, except + for at most one splat ('#'). + + - charjunk: A function that should accept a string of length 1. The + default is module-level function IS_CHARACTER_JUNK, which filters out + whitespace characters (a blank or tab; note: bad idea to include newline + in this!). + + Tools/scripts/ndiff.py is a command-line front-end to this function. + + Example: + + >>> diff = ndiff('one\ntwo\nthree\n'.splitlines(1), + ... 'ore\ntree\nemu\n'.splitlines(1)) + >>> print ''.join(diff), + - one + ? ^ + + ore + ? ^ + - two + - three + ? - + + tree + + emu + """ + return Differ(linejunk, charjunk).compare(a, b) + +def restore(delta, which): + r""" + Generate one of the two sequences that generated a delta. + + Given a `delta` produced by `Differ.compare()` or `ndiff()`, extract + lines originating from file 1 or 2 (parameter `which`), stripping off line + prefixes. + + Examples: + + >>> diff = ndiff('one\ntwo\nthree\n'.splitlines(1), + ... 'ore\ntree\nemu\n'.splitlines(1)) + >>> diff = list(diff) + >>> print ''.join(restore(diff, 1)), + one + two + three + >>> print ''.join(restore(diff, 2)), + ore + tree + emu + """ + try: + tag = {1: "- ", 2: "+ "}[int(which)] + except KeyError: + raise ValueError, ('unknown delta choice (must be 1 or 2): %r' + % which) + prefixes = (" ", tag) + for line in delta: + if line[:2] in prefixes: + yield line[2:] + +def _test(): + import doctest, difflib + return doctest.testmod(difflib) + +if __name__ == "__main__": + _test() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dircache.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dircache.py new file mode 100644 index 00000000..b95cb98a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dircache.py @@ -0,0 +1,44 @@ +"""Read and cache directory listings. + +The listdir() routine returns a sorted list of the files in a directory, +using a cache to avoid reading the directory more often than necessary. +The annotate() routine appends slashes to directories.""" + +import os + +__all__ = ["listdir", "opendir", "annotate", "reset"] + +cache = {} + +def reset(): + """Reset the cache completely.""" + global cache + cache = {} + +def listdir(path): + """List directory contents, using cache.""" + try: + cached_mtime, list = cache[path] + del cache[path] + except KeyError: + cached_mtime, list = -1, [] + try: + mtime = os.stat(path)[8] + except os.error: + return [] + if mtime != cached_mtime: + try: + list = os.listdir(path) + except os.error: + return [] + list.sort() + cache[path] = mtime, list + return list + +opendir = listdir # XXX backward compatibility + +def annotate(head, list): + """Add '/' suffixes to directories.""" + for i in range(len(list)): + if os.path.isdir(os.path.join(head, list[i])): + list[i] = list[i] + '/' diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dis.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dis.py new file mode 100644 index 00000000..e1e78ef6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dis.py @@ -0,0 +1,327 @@ +"""Disassembler of Python byte code into mnemonics.""" + +import sys +import types + +__all__ = ["dis","disassemble","distb","disco","opname","cmp_op", + "hasconst","hasname","hasjrel","hasjabs","haslocal", + "hascompare", "hasfree"] + +def dis(x=None): + """Disassemble classes, methods, functions, or code. + + With no argument, disassemble the last traceback. + + """ + if not x: + distb() + return + if type(x) is types.InstanceType: + x = x.__class__ + if hasattr(x, 'im_func'): + x = x.im_func + if hasattr(x, 'func_code'): + x = x.func_code + if hasattr(x, '__dict__'): + items = x.__dict__.items() + items.sort() + for name, x1 in items: + if type(x1) in (types.MethodType, + types.FunctionType, + types.CodeType): + print "Disassembly of %s:" % name + try: + dis(x1) + except TypeError, msg: + print "Sorry:", msg + print + elif hasattr(x, 'co_code'): + disassemble(x) + else: + raise TypeError, \ + "don't know how to disassemble %s objects" % \ + type(x).__name__ + +def distb(tb=None): + """Disassemble a traceback (default: last traceback).""" + if not tb: + try: + tb = sys.last_traceback + except AttributeError: + raise RuntimeError, "no last traceback to disassemble" + while tb.tb_next: tb = tb.tb_next + disassemble(tb.tb_frame.f_code, tb.tb_lasti) + +def disassemble(co, lasti=-1): + """Disassemble a code object.""" + code = co.co_code + labels = findlabels(code) + n = len(code) + i = 0 + extended_arg = 0 + free = None + while i < n: + c = code[i] + op = ord(c) + if op == SET_LINENO and i > 0: print # Extra blank line + if i == lasti: print '-->', + else: print ' ', + if i in labels: print '>>', + else: print ' ', + print `i`.rjust(4), + print opname[op].ljust(20), + i = i+1 + if op >= HAVE_ARGUMENT: + oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg + extended_arg = 0 + i = i+2 + if op == EXTENDED_ARG: + extended_arg = oparg*65536L + print `oparg`.rjust(5), + if op in hasconst: + print '(' + `co.co_consts[oparg]` + ')', + elif op in hasname: + print '(' + co.co_names[oparg] + ')', + elif op in hasjrel: + print '(to ' + `i + oparg` + ')', + elif op in haslocal: + print '(' + co.co_varnames[oparg] + ')', + elif op in hascompare: + print '(' + cmp_op[oparg] + ')', + elif op in hasfree: + if free is None: + free = co.co_cellvars + co.co_freevars + print '(' + free[oparg] + ')', + print + +disco = disassemble # XXX For backwards compatibility + +def findlabels(code): + """Detect all offsets in a byte code which are jump targets. + + Return the list of offsets. + + """ + labels = [] + n = len(code) + i = 0 + while i < n: + c = code[i] + op = ord(c) + i = i+1 + if op >= HAVE_ARGUMENT: + oparg = ord(code[i]) + ord(code[i+1])*256 + i = i+2 + label = -1 + if op in hasjrel: + label = i+oparg + elif op in hasjabs: + label = oparg + if label >= 0: + if label not in labels: + labels.append(label) + return labels + +cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', + 'is not', 'exception match', 'BAD') + +hasconst = [] +hasname = [] +hasjrel = [] +hasjabs = [] +haslocal = [] +hascompare = [] +hasfree = [] + +opname = [''] * 256 +for op in range(256): opname[op] = '<' + `op` + '>' + +def def_op(name, op): + opname[op] = name + +def name_op(name, op): + opname[op] = name + hasname.append(op) + +def jrel_op(name, op): + opname[op] = name + hasjrel.append(op) + +def jabs_op(name, op): + opname[op] = name + hasjabs.append(op) + +# Instruction opcodes for compiled code + +def_op('STOP_CODE', 0) +def_op('POP_TOP', 1) +def_op('ROT_TWO', 2) +def_op('ROT_THREE', 3) +def_op('DUP_TOP', 4) +def_op('ROT_FOUR', 5) + +def_op('UNARY_POSITIVE', 10) +def_op('UNARY_NEGATIVE', 11) +def_op('UNARY_NOT', 12) +def_op('UNARY_CONVERT', 13) + +def_op('UNARY_INVERT', 15) + +def_op('BINARY_POWER', 19) + +def_op('BINARY_MULTIPLY', 20) +def_op('BINARY_DIVIDE', 21) +def_op('BINARY_MODULO', 22) +def_op('BINARY_ADD', 23) +def_op('BINARY_SUBTRACT', 24) +def_op('BINARY_SUBSCR', 25) +def_op('BINARY_FLOOR_DIVIDE', 26) +def_op('BINARY_TRUE_DIVIDE', 27) +def_op('INPLACE_FLOOR_DIVIDE', 28) +def_op('INPLACE_TRUE_DIVIDE', 29) + +def_op('SLICE+0', 30) +def_op('SLICE+1', 31) +def_op('SLICE+2', 32) +def_op('SLICE+3', 33) + +def_op('STORE_SLICE+0', 40) +def_op('STORE_SLICE+1', 41) +def_op('STORE_SLICE+2', 42) +def_op('STORE_SLICE+3', 43) + +def_op('DELETE_SLICE+0', 50) +def_op('DELETE_SLICE+1', 51) +def_op('DELETE_SLICE+2', 52) +def_op('DELETE_SLICE+3', 53) + +def_op('INPLACE_ADD', 55) +def_op('INPLACE_SUBTRACT', 56) +def_op('INPLACE_MULTIPLY', 57) +def_op('INPLACE_DIVIDE', 58) +def_op('INPLACE_MODULO', 59) +def_op('STORE_SUBSCR', 60) +def_op('DELETE_SUBSCR', 61) + +def_op('BINARY_LSHIFT', 62) +def_op('BINARY_RSHIFT', 63) +def_op('BINARY_AND', 64) +def_op('BINARY_XOR', 65) +def_op('BINARY_OR', 66) +def_op('INPLACE_POWER', 67) +def_op('GET_ITER', 68) + +def_op('PRINT_EXPR', 70) +def_op('PRINT_ITEM', 71) +def_op('PRINT_NEWLINE', 72) +def_op('PRINT_ITEM_TO', 73) +def_op('PRINT_NEWLINE_TO', 74) +def_op('INPLACE_LSHIFT', 75) +def_op('INPLACE_RSHIFT', 76) +def_op('INPLACE_AND', 77) +def_op('INPLACE_XOR', 78) +def_op('INPLACE_OR', 79) +def_op('BREAK_LOOP', 80) + +def_op('LOAD_LOCALS', 82) +def_op('RETURN_VALUE', 83) +def_op('IMPORT_STAR', 84) +def_op('EXEC_STMT', 85) +def_op('YIELD_STMT', 86) + +def_op('POP_BLOCK', 87) +def_op('END_FINALLY', 88) +def_op('BUILD_CLASS', 89) + +HAVE_ARGUMENT = 90 # Opcodes from here have an argument: + +name_op('STORE_NAME', 90) # Index in name list +name_op('DELETE_NAME', 91) # "" +def_op('UNPACK_SEQUENCE', 92) # Number of tuple items +jrel_op('FOR_ITER', 93) + +name_op('STORE_ATTR', 95) # Index in name list +name_op('DELETE_ATTR', 96) # "" +name_op('STORE_GLOBAL', 97) # "" +name_op('DELETE_GLOBAL', 98) # "" +def_op('DUP_TOPX', 99) # number of items to duplicate +def_op('LOAD_CONST', 100) # Index in const list +hasconst.append(100) +name_op('LOAD_NAME', 101) # Index in name list +def_op('BUILD_TUPLE', 102) # Number of tuple items +def_op('BUILD_LIST', 103) # Number of list items +def_op('BUILD_MAP', 104) # Always zero for now +name_op('LOAD_ATTR', 105) # Index in name list +def_op('COMPARE_OP', 106) # Comparison operator +hascompare.append(106) +name_op('IMPORT_NAME', 107) # Index in name list +name_op('IMPORT_FROM', 108) # Index in name list + +jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip +jrel_op('JUMP_IF_FALSE', 111) # "" +jrel_op('JUMP_IF_TRUE', 112) # "" +jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code +jrel_op('FOR_LOOP', 114) # Number of bytes to skip + +name_op('LOAD_GLOBAL', 116) # Index in name list + +jabs_op('CONTINUE_LOOP', 119) # Target address +jrel_op('SETUP_LOOP', 120) # Distance to target address +jrel_op('SETUP_EXCEPT', 121) # "" +jrel_op('SETUP_FINALLY', 122) # "" + +def_op('LOAD_FAST', 124) # Local variable number +haslocal.append(124) +def_op('STORE_FAST', 125) # Local variable number +haslocal.append(125) +def_op('DELETE_FAST', 126) # Local variable number +haslocal.append(126) + +def_op('SET_LINENO', 127) # Current line number +SET_LINENO = 127 + +def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) +def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) +def_op('MAKE_FUNCTION', 132) # Number of args with default values +def_op('BUILD_SLICE', 133) # Number of items + +def_op('MAKE_CLOSURE', 134) +def_op('LOAD_CLOSURE', 135) +hasfree.append(135) +def_op('LOAD_DEREF', 136) +hasfree.append(136) +def_op('STORE_DEREF', 137) +hasfree.append(137) + +def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) +def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) +def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) + +def_op('EXTENDED_ARG', 143) +EXTENDED_ARG = 143 + +def _test(): + """Simple test program to disassemble a file.""" + if sys.argv[1:]: + if sys.argv[2:]: + sys.stderr.write("usage: python dis.py [-|file]\n") + sys.exit(2) + fn = sys.argv[1] + if not fn or fn == "-": + fn = None + else: + fn = None + if not fn: + f = sys.stdin + else: + f = open(fn) + source = f.read() + if fn: + f.close() + else: + fn = "" + code = compile(source, fn, "exec") + dis(code) + +if __name__ == "__main__": + _test() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dospath.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dospath.py new file mode 100644 index 00000000..83c40385 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/dospath.py @@ -0,0 +1,341 @@ +"""Common operations on DOS pathnames.""" + +import os +import stat + +__all__ = ["normcase","isabs","join","splitdrive","split","splitext", + "basename","dirname","commonprefix","getsize","getmtime", + "getatime","islink","exists","isdir","isfile","ismount", + "walk","expanduser","expandvars","normpath","abspath"] + +def normcase(s): + """Normalize the case of a pathname. + On MS-DOS it maps the pathname to lowercase, turns slashes into + backslashes. + Other normalizations (such as optimizing '../' away) are not allowed + (this is done by normpath). + Previously, this version mapped invalid consecutive characters to a + single '_', but this has been removed. This functionality should + possibly be added as a new function.""" + + return s.replace("/", "\\").lower() + + +def isabs(s): + """Return whether a path is absolute. + Trivial in Posix, harder on the Mac or MS-DOS. + For DOS it is absolute if it starts with a slash or backslash (current + volume), or if a pathname after the volume letter and colon starts with + a slash or backslash.""" + + s = splitdrive(s)[1] + return s != '' and s[:1] in '/\\' + + +def join(a, *p): + """Join two (or more) paths.""" + + path = a + for b in p: + if isabs(b): + path = b + elif path == '' or path[-1:] in '/\\:': + path = path + b + else: + path = path + "\\" + b + return path + + +def splitdrive(p): + """Split a path into a drive specification (a drive letter followed + by a colon) and path specification. + It is always true that drivespec + pathspec == p.""" + + if p[1:2] == ':': + return p[0:2], p[2:] + return '', p + + +def split(p): + """Split a path into head (everything up to the last '/') and tail + (the rest). After the trailing '/' is stripped, the invariant + join(head, tail) == p holds. + The resulting head won't end in '/' unless it is the root.""" + + d, p = splitdrive(p) + # set i to index beyond p's last slash + i = len(p) + while i and p[i-1] not in '/\\': + i = i - 1 + head, tail = p[:i], p[i:] # now tail has no slashes + # remove trailing slashes from head, unless it's all slashes + head2 = head + while head2 and head2[-1] in '/\\': + head2 = head2[:-1] + head = head2 or head + return d + head, tail + + +def splitext(p): + """Split a path into root and extension. + The extension is everything starting at the first dot in the last + pathname component; the root is everything before that. + It is always true that root + ext == p.""" + + root, ext = '', '' + for c in p: + if c in '/\\': + root, ext = root + ext + c, '' + elif c == '.' or ext: + ext = ext + c + else: + root = root + c + return root, ext + + +def basename(p): + """Return the tail (basename) part of a path.""" + + return split(p)[1] + + +def dirname(p): + """Return the head (dirname) part of a path.""" + + return split(p)[0] + + +def commonprefix(m): + """Return the longest prefix of all list elements.""" + + if not m: return '' + prefix = m[0] + for item in m: + for i in range(len(prefix)): + if prefix[:i+1] != item[:i+1]: + prefix = prefix[:i] + if i == 0: return '' + break + return prefix + + +# Get size, mtime, atime of files. + +def getsize(filename): + """Return the size of a file, reported by os.stat().""" + st = os.stat(filename) + return st[stat.ST_SIZE] + +def getmtime(filename): + """Return the last modification time of a file, reported by os.stat().""" + st = os.stat(filename) + return st[stat.ST_MTIME] + +def getatime(filename): + """Return the last access time of a file, reported by os.stat().""" + st = os.stat(filename) + return st[stat.ST_ATIME] + + +def islink(path): + """Is a path a symbolic link? + This will always return false on systems where posix.lstat doesn't exist.""" + + return 0 + + +def exists(path): + """Does a path exist? + This is false for dangling symbolic links.""" + + try: + st = os.stat(path) + except os.error: + return 0 + return 1 + + +def isdir(path): + """Is a path a dos directory?""" + + try: + st = os.stat(path) + except os.error: + return 0 + return stat.S_ISDIR(st[stat.ST_MODE]) + + +def isfile(path): + """Is a path a regular file?""" + + try: + st = os.stat(path) + except os.error: + return 0 + return stat.S_ISREG(st[stat.ST_MODE]) + + +def ismount(path): + """Is a path a mount point?""" + # XXX This degenerates in: 'is this the root?' on DOS + + return isabs(splitdrive(path)[1]) + + +def walk(top, func, arg): + """Directory tree walk with callback function. + + For each directory in the directory tree rooted at top (including top + itself, but excluding '.' and '..'), call func(arg, dirname, fnames). + dirname is the name of the directory, and fnames a list of the names of + the files and subdirectories in dirname (excluding '.' and '..'). func + may modify the fnames list in-place (e.g. via del or slice assignment), + and walk will only recurse into the subdirectories whose names remain in + fnames; this can be used to implement a filter, or to impose a specific + order of visiting. No semantics are defined for, or required of, arg, + beyond that arg is always passed to func. It can be used, e.g., to pass + a filename pattern, or a mutable object designed to accumulate + statistics. Passing None for arg is common.""" + + try: + names = os.listdir(top) + except os.error: + return + func(arg, top, names) + exceptions = ('.', '..') + for name in names: + if name not in exceptions: + name = join(top, name) + if isdir(name): + walk(name, func, arg) + + +def expanduser(path): + """Expand paths beginning with '~' or '~user'. + '~' means $HOME; '~user' means that user's home directory. + If the path doesn't begin with '~', or if the user or $HOME is unknown, + the path is returned unchanged (leaving error reporting to whatever + function is called with the expanded path as argument). + See also module 'glob' for expansion of *, ? and [...] in pathnames. + (A function should also be defined to do full *sh-style environment + variable expansion.)""" + + if path[:1] != '~': + return path + i, n = 1, len(path) + while i < n and path[i] not in '/\\': + i = i+1 + if i == 1: + if not os.environ.has_key('HOME'): + return path + userhome = os.environ['HOME'] + else: + return path + return userhome + path[i:] + + +def expandvars(path): + """Expand paths containing shell variable substitutions. + The following rules apply: + - no expansion within single quotes + - no escape character, except for '$$' which is translated into '$' + - ${varname} is accepted. + - varnames can be made out of letters, digits and the character '_'""" + # XXX With COMMAND.COM you can use any characters in a variable name, + # XXX except '^|<>='. + + if '$' not in path: + return path + import string + varchars = string.ascii_letters + string.digits + "_-" + res = '' + index = 0 + pathlen = len(path) + while index < pathlen: + c = path[index] + if c == '\'': # no expansion within single quotes + path = path[index + 1:] + pathlen = len(path) + try: + index = path.index('\'') + res = res + '\'' + path[:index + 1] + except ValueError: + res = res + path + index = pathlen -1 + elif c == '$': # variable or '$$' + if path[index + 1:index + 2] == '$': + res = res + c + index = index + 1 + elif path[index + 1:index + 2] == '{': + path = path[index+2:] + pathlen = len(path) + try: + index = path.index('}') + var = path[:index] + if os.environ.has_key(var): + res = res + os.environ[var] + except ValueError: + res = res + path + index = pathlen - 1 + else: + var = '' + index = index + 1 + c = path[index:index + 1] + while c != '' and c in varchars: + var = var + c + index = index + 1 + c = path[index:index + 1] + if os.environ.has_key(var): + res = res + os.environ[var] + if c != '': + res = res + c + else: + res = res + c + index = index + 1 + return res + + +def normpath(path): + """Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B. + Also, components of the path are silently truncated to 8+3 notation.""" + + path = path.replace("/", "\\") + prefix, path = splitdrive(path) + while path[:1] == "\\": + prefix = prefix + "\\" + path = path[1:] + comps = path.split("\\") + i = 0 + while i < len(comps): + if comps[i] == '.': + del comps[i] + elif comps[i] == '..' and i > 0 and \ + comps[i-1] not in ('', '..'): + del comps[i-1:i+1] + i = i - 1 + elif comps[i] == '' and i > 0 and comps[i-1] != '': + del comps[i] + elif '.' in comps[i]: + comp = comps[i].split('.') + comps[i] = comp[0][:8] + '.' + comp[1][:3] + i = i + 1 + elif len(comps[i]) > 8: + comps[i] = comps[i][:8] + i = i + 1 + else: + i = i + 1 + # If the path is now empty, substitute '.' + if not prefix and not comps: + comps.append('.') + return prefix + "\\".join(comps) + + + +def abspath(path): + """Return an absolute path.""" + if not isabs(path): + path = join(os.getcwd(), path) + return normpath(path) + +# realpath is a no-op on systems without islink support +realpath = abspath diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/filecmp.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/filecmp.py new file mode 100644 index 00000000..1d18fac2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/filecmp.py @@ -0,0 +1,331 @@ +"""Utilities for comparing files and directories. + +Classes: + dircmp + +Functions: + cmp(f1, f2, shallow=1, use_statcache=0) -> int + cmpfiles(a, b, common) -> ([], [], []) + +""" + +import os +import stat +import statcache + +__all__ = ["cmp","dircmp","cmpfiles"] + +_cache = {} +BUFSIZE=8*1024 + +def cmp(f1, f2, shallow=1, use_statcache=0): + """Compare two files. + + Arguments: + + f1 -- First file name + + f2 -- Second file name + + shallow -- Just check stat signature (do not read the files). + defaults to 1. + + use_statcache -- Do not stat() each file directly: go through + the statcache module for more efficiency. + + Return value: + + integer -- 1 if the files are the same, 0 otherwise. + + This function uses a cache for past comparisons and the results, + with a cache invalidation mechanism relying on stale signatures. + Of course, if 'use_statcache' is true, this mechanism is defeated, + and the cache will never grow stale. + + """ + if use_statcache: + stat_function = statcache.stat + else: + stat_function = os.stat + s1 = _sig(stat_function(f1)) + s2 = _sig(stat_function(f2)) + if s1[0] != stat.S_IFREG or s2[0] != stat.S_IFREG: + return 0 + if shallow and s1 == s2: + return 1 + if s1[1] != s2[1]: + return 0 + + result = _cache.get((f1, f2)) + if result and (s1, s2) == result[:2]: + return result[2] + outcome = _do_cmp(f1, f2) + _cache[f1, f2] = s1, s2, outcome + return outcome + +def _sig(st): + return (stat.S_IFMT(st[stat.ST_MODE]), + st[stat.ST_SIZE], + st[stat.ST_MTIME]) + +def _do_cmp(f1, f2): + bufsize = BUFSIZE + fp1 = open(f1, 'rb') + fp2 = open(f2, 'rb') + while 1: + b1 = fp1.read(bufsize) + b2 = fp2.read(bufsize) + if b1 != b2: + return 0 + if not b1: + return 1 + +# Directory comparison class. +# +class dircmp: + """A class that manages the comparison of 2 directories. + + dircmp(a,b,ignore=None,hide=None) + A and B are directories. + IGNORE is a list of names to ignore, + defaults to ['RCS', 'CVS', 'tags']. + HIDE is a list of names to hide, + defaults to [os.curdir, os.pardir]. + + High level usage: + x = dircmp(dir1, dir2) + x.report() -> prints a report on the differences between dir1 and dir2 + or + x.report_partial_closure() -> prints report on differences between dir1 + and dir2, and reports on common immediate subdirectories. + x.report_full_closure() -> like report_partial_closure, + but fully recursive. + + Attributes: + left_list, right_list: The files in dir1 and dir2, + filtered by hide and ignore. + common: a list of names in both dir1 and dir2. + left_only, right_only: names only in dir1, dir2. + common_dirs: subdirectories in both dir1 and dir2. + common_files: files in both dir1 and dir2. + common_funny: names in both dir1 and dir2 where the type differs between + dir1 and dir2, or the name is not stat-able. + same_files: list of identical files. + diff_files: list of filenames which differ. + funny_files: list of files which could not be compared. + subdirs: a dictionary of dircmp objects, keyed by names in common_dirs. + """ + + def __init__(self, a, b, ignore=None, hide=None): # Initialize + self.left = a + self.right = b + if hide is None: + self.hide = [os.curdir, os.pardir] # Names never to be shown + else: + self.hide = hide + if ignore is None: + self.ignore = ['RCS', 'CVS', 'tags'] # Names ignored in comparison + else: + self.ignore = ignore + + def phase0(self): # Compare everything except common subdirectories + self.left_list = _filter(os.listdir(self.left), + self.hide+self.ignore) + self.right_list = _filter(os.listdir(self.right), + self.hide+self.ignore) + self.left_list.sort() + self.right_list.sort() + + __p4_attrs = ('subdirs',) + __p3_attrs = ('same_files', 'diff_files', 'funny_files') + __p2_attrs = ('common_dirs', 'common_files', 'common_funny') + __p1_attrs = ('common', 'left_only', 'right_only') + __p0_attrs = ('left_list', 'right_list') + + def __getattr__(self, attr): + if attr in self.__p4_attrs: + self.phase4() + elif attr in self.__p3_attrs: + self.phase3() + elif attr in self.__p2_attrs: + self.phase2() + elif attr in self.__p1_attrs: + self.phase1() + elif attr in self.__p0_attrs: + self.phase0() + else: + raise AttributeError, attr + return getattr(self, attr) + + def phase1(self): # Compute common names + a_only, b_only = [], [] + common = {} + b = {} + for fnm in self.right_list: + b[fnm] = 1 + for x in self.left_list: + if b.get(x, 0): + common[x] = 1 + else: + a_only.append(x) + for x in self.right_list: + if common.get(x, 0): + pass + else: + b_only.append(x) + self.common = common.keys() + self.left_only = a_only + self.right_only = b_only + + def phase2(self): # Distinguish files, directories, funnies + self.common_dirs = [] + self.common_files = [] + self.common_funny = [] + + for x in self.common: + a_path = os.path.join(self.left, x) + b_path = os.path.join(self.right, x) + + ok = 1 + try: + a_stat = statcache.stat(a_path) + except os.error, why: + # print 'Can\'t stat', a_path, ':', why[1] + ok = 0 + try: + b_stat = statcache.stat(b_path) + except os.error, why: + # print 'Can\'t stat', b_path, ':', why[1] + ok = 0 + + if ok: + a_type = stat.S_IFMT(a_stat[stat.ST_MODE]) + b_type = stat.S_IFMT(b_stat[stat.ST_MODE]) + if a_type != b_type: + self.common_funny.append(x) + elif stat.S_ISDIR(a_type): + self.common_dirs.append(x) + elif stat.S_ISREG(a_type): + self.common_files.append(x) + else: + self.common_funny.append(x) + else: + self.common_funny.append(x) + + def phase3(self): # Find out differences between common files + xx = cmpfiles(self.left, self.right, self.common_files) + self.same_files, self.diff_files, self.funny_files = xx + + def phase4(self): # Find out differences between common subdirectories + # A new dircmp object is created for each common subdirectory, + # these are stored in a dictionary indexed by filename. + # The hide and ignore properties are inherited from the parent + self.subdirs = {} + for x in self.common_dirs: + a_x = os.path.join(self.left, x) + b_x = os.path.join(self.right, x) + self.subdirs[x] = dircmp(a_x, b_x, self.ignore, self.hide) + + def phase4_closure(self): # Recursively call phase4() on subdirectories + self.phase4() + for x in self.subdirs.keys(): + self.subdirs[x].phase4_closure() + + def report(self): # Print a report on the differences between a and b + # Output format is purposely lousy + print 'diff', self.left, self.right + if self.left_only: + self.left_only.sort() + print 'Only in', self.left, ':', self.left_only + if self.right_only: + self.right_only.sort() + print 'Only in', self.right, ':', self.right_only + if self.same_files: + self.same_files.sort() + print 'Identical files :', self.same_files + if self.diff_files: + self.diff_files.sort() + print 'Differing files :', self.diff_files + if self.funny_files: + self.funny_files.sort() + print 'Trouble with common files :', self.funny_files + if self.common_dirs: + self.common_dirs.sort() + print 'Common subdirectories :', self.common_dirs + if self.common_funny: + self.common_funny.sort() + print 'Common funny cases :', self.common_funny + + def report_partial_closure(self): # Print reports on self and on subdirs + self.report() + for x in self.subdirs.keys(): + print + self.subdirs[x].report() + + def report_full_closure(self): # Report on self and subdirs recursively + self.report() + for x in self.subdirs.keys(): + print + self.subdirs[x].report_full_closure() + + +def cmpfiles(a, b, common, shallow=1, use_statcache=0): + """Compare common files in two directories. + + a, b -- directory names + common -- list of file names found in both directories + shallow -- if true, do comparison based solely on stat() information + use_statcache -- if true, use statcache.stat() instead of os.stat() + + Returns a tuple of three lists: + files that compare equal + files that are different + filenames that aren't regular files. + + """ + res = ([], [], []) + for x in common: + ax = os.path.join(a, x) + bx = os.path.join(b, x) + res[_cmp(ax, bx, shallow, use_statcache)].append(x) + return res + + +# Compare two files. +# Return: +# 0 for equal +# 1 for different +# 2 for funny cases (can't stat, etc.) +# +def _cmp(a, b, sh, st): + try: + return not abs(cmp(a, b, sh, st)) + except os.error: + return 2 + + +# Return a copy with items that occur in skip removed. +# +def _filter(list, skip): + result = [] + for item in list: + if item not in skip: result.append(item) + return result + + +# Demonstration and testing. +# +def demo(): + import sys + import getopt + options, args = getopt.getopt(sys.argv[1:], 'r') + if len(args) != 2: + raise getopt.error, 'need exactly two args' + dd = dircmp(args[0], args[1]) + if ('-r', '') in options: + dd.report_full_closure() + else: + dd.report() + +if __name__ == '__main__': + demo() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fileinput.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fileinput.py new file mode 100644 index 00000000..ebd873e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fileinput.py @@ -0,0 +1,349 @@ +"""Helper class to quickly write a loop over all standard input files. + +Typical use is: + + import fileinput + for line in fileinput.input(): + process(line) + +This iterates over the lines of all files listed in sys.argv[1:], +defaulting to sys.stdin if the list is empty. If a filename is '-' it +is also replaced by sys.stdin. To specify an alternative list of +filenames, pass it as the argument to input(). A single file name is +also allowed. + +Functions filename(), lineno() return the filename and cumulative line +number of the line that has just been read; filelineno() returns its +line number in the current file; isfirstline() returns true iff the +line just read is the first line of its file; isstdin() returns true +iff the line was read from sys.stdin. Function nextfile() closes the +current file so that the next iteration will read the first line from +the next file (if any); lines not read from the file will not count +towards the cumulative line count; the filename is not changed until +after the first line of the next file has been read. Function close() +closes the sequence. + +Before any lines have been read, filename() returns None and both line +numbers are zero; nextfile() has no effect. After all lines have been +read, filename() and the line number functions return the values +pertaining to the last line read; nextfile() has no effect. + +All files are opened in text mode. If an I/O error occurs during +opening or reading a file, the IOError exception is raised. + +If sys.stdin is used more than once, the second and further use will +return no lines, except perhaps for interactive use, or if it has been +explicitly reset (e.g. using sys.stdin.seek(0)). + +Empty files are opened and immediately closed; the only time their +presence in the list of filenames is noticeable at all is when the +last file opened is empty. + +It is possible that the last line of a file doesn't end in a newline +character; otherwise lines are returned including the trailing +newline. + +Class FileInput is the implementation; its methods filename(), +lineno(), fileline(), isfirstline(), isstdin(), nextfile() and close() +correspond to the functions in the module. In addition it has a +readline() method which returns the next input line, and a +__getitem__() method which implements the sequence behavior. The +sequence must be accessed in strictly sequential order; sequence +access and readline() cannot be mixed. + +Optional in-place filtering: if the keyword argument inplace=1 is +passed to input() or to the FileInput constructor, the file is moved +to a backup file and standard output is directed to the input file. +This makes it possible to write a filter that rewrites its input file +in place. If the keyword argument backup="." is also +given, it specifies the extension for the backup file, and the backup +file remains around; by default, the extension is ".bak" and it is +deleted when the output file is closed. In-place filtering is +disabled when standard input is read. XXX The current implementation +does not work for MS-DOS 8+3 filesystems. + +Performance: this module is unfortunately one of the slower ways of +processing large numbers of input lines. Nevertheless, a significant +speed-up has been obtained by using readlines(bufsize) instead of +readline(). A new keyword argument, bufsize=N, is present on the +input() function and the FileInput() class to override the default +buffer size. + +XXX Possible additions: + +- optional getopt argument processing +- specify open mode ('r' or 'rb') +- fileno() +- isatty() +- read(), read(size), even readlines() + +""" + +import sys, os, stat + +__all__ = ["input","close","nextfile","filename","lineno","filelineno", + "isfirstline","isstdin","FileInput"] + +_state = None + +DEFAULT_BUFSIZE = 8*1024 + +def input(files=None, inplace=0, backup="", bufsize=0): + """input([files[, inplace[, backup]]]) + + Create an instance of the FileInput class. The instance will be used + as global state for the functions of this module, and is also returned + to use during iteration. The parameters to this function will be passed + along to the constructor of the FileInput class. + """ + global _state + if _state and _state._file: + raise RuntimeError, "input() already active" + _state = FileInput(files, inplace, backup, bufsize) + return _state + +def close(): + """Close the sequence.""" + global _state + state = _state + _state = None + if state: + state.close() + +def nextfile(): + """ + Close the current file so that the next iteration will read the first + line from the next file (if any); lines not read from the file will + not count towards the cumulative line count. The filename is not + changed until after the first line of the next file has been read. + Before the first line has been read, this function has no effect; + it cannot be used to skip the first file. After the last line of the + last file has been read, this function has no effect. + """ + if not _state: + raise RuntimeError, "no active input()" + return _state.nextfile() + +def filename(): + """ + Return the name of the file currently being read. + Before the first line has been read, returns None. + """ + if not _state: + raise RuntimeError, "no active input()" + return _state.filename() + +def lineno(): + """ + Return the cumulative line number of the line that has just been read. + Before the first line has been read, returns 0. After the last line + of the last file has been read, returns the line number of that line. + """ + if not _state: + raise RuntimeError, "no active input()" + return _state.lineno() + +def filelineno(): + """ + Return the line number in the current file. Before the first line + has been read, returns 0. After the last line of the last file has + been read, returns the line number of that line within the file. + """ + if not _state: + raise RuntimeError, "no active input()" + return _state.filelineno() + +def isfirstline(): + """ + Returns true the line just read is the first line of its file, + otherwise returns false. + """ + if not _state: + raise RuntimeError, "no active input()" + return _state.isfirstline() + +def isstdin(): + """ + Returns true if the last line was read from sys.stdin, + otherwise returns false. + """ + if not _state: + raise RuntimeError, "no active input()" + return _state.isstdin() + +class FileInput: + """class FileInput([files[, inplace[, backup]]]) + + Class FileInput is the implementation of the module; its methods + filename(), lineno(), fileline(), isfirstline(), isstdin(), nextfile() + and close() correspond to the functions of the same name in the module. + In addition it has a readline() method which returns the next + input line, and a __getitem__() method which implements the + sequence behavior. The sequence must be accessed in strictly + sequential order; random access and readline() cannot be mixed. + """ + + def __init__(self, files=None, inplace=0, backup="", bufsize=0): + if type(files) == type(''): + files = (files,) + else: + if files is None: + files = sys.argv[1:] + if not files: + files = ('-',) + else: + files = tuple(files) + self._files = files + self._inplace = inplace + self._backup = backup + self._bufsize = bufsize or DEFAULT_BUFSIZE + self._savestdout = None + self._output = None + self._filename = None + self._lineno = 0 + self._filelineno = 0 + self._file = None + self._isstdin = 0 + self._backupfilename = None + self._buffer = [] + self._bufindex = 0 + + def __del__(self): + self.close() + + def close(self): + self.nextfile() + self._files = () + + def __getitem__(self, i): + try: + line = self._buffer[self._bufindex] + except IndexError: + pass + else: + self._bufindex += 1 + self._lineno += 1 + self._filelineno += 1 + return line + if i != self._lineno: + raise RuntimeError, "accessing lines out of order" + line = self.readline() + if not line: + raise IndexError, "end of input reached" + return line + + def nextfile(self): + savestdout = self._savestdout + self._savestdout = 0 + if savestdout: + sys.stdout = savestdout + + output = self._output + self._output = 0 + if output: + output.close() + + file = self._file + self._file = 0 + if file and not self._isstdin: + file.close() + + backupfilename = self._backupfilename + self._backupfilename = 0 + if backupfilename and not self._backup: + try: os.unlink(backupfilename) + except: pass + + self._isstdin = 0 + self._buffer = [] + self._bufindex = 0 + + def readline(self): + try: + line = self._buffer[self._bufindex] + except IndexError: + pass + else: + self._bufindex += 1 + self._lineno += 1 + self._filelineno += 1 + return line + if not self._file: + if not self._files: + return "" + self._filename = self._files[0] + self._files = self._files[1:] + self._filelineno = 0 + self._file = None + self._isstdin = 0 + self._backupfilename = 0 + if self._filename == '-': + self._filename = '' + self._file = sys.stdin + self._isstdin = 1 + else: + if self._inplace: + self._backupfilename = ( + self._filename + (self._backup or os.extsep+"bak")) + try: os.unlink(self._backupfilename) + except os.error: pass + # The next few lines may raise IOError + os.rename(self._filename, self._backupfilename) + self._file = open(self._backupfilename, "r") + try: + perm = os.fstat(self._file.fileno())[stat.ST_MODE] + except: + self._output = open(self._filename, "w") + else: + fd = os.open(self._filename, + os.O_CREAT | os.O_WRONLY | os.O_TRUNC, + perm) + self._output = os.fdopen(fd, "w") + try: + os.chmod(self._filename, perm) + except: + pass + self._savestdout = sys.stdout + sys.stdout = self._output + else: + # This may raise IOError + self._file = open(self._filename, "r") + self._buffer = self._file.readlines(self._bufsize) + self._bufindex = 0 + if not self._buffer: + self.nextfile() + # Recursive call + return self.readline() + + def filename(self): + return self._filename + + def lineno(self): + return self._lineno + + def filelineno(self): + return self._filelineno + + def isfirstline(self): + return self._filelineno == 1 + + def isstdin(self): + return self._isstdin + +def _test(): + import getopt + inplace = 0 + backup = 0 + opts, args = getopt.getopt(sys.argv[1:], "ib:") + for o, a in opts: + if o == '-i': inplace = 1 + if o == '-b': backup = a + for line in input(args, inplace=inplace, backup=backup): + if line[-1:] == '\n': line = line[:-1] + if line[-1:] == '\r': line = line[:-1] + print "%d: %s[%d]%s %s" % (lineno(), filename(), filelineno(), + isfirstline() and "*" or "", line) + print "%d: %s[%d]" % (lineno(), filename(), filelineno()) + +if __name__ == '__main__': + _test() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fnmatch.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fnmatch.py new file mode 100644 index 00000000..e3393ce4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fnmatch.py @@ -0,0 +1,107 @@ +"""Filename matching with shell patterns. + +fnmatch(FILENAME, PATTERN) matches according to the local convention. +fnmatchcase(FILENAME, PATTERN) always takes case in account. + +The functions operate by translating the pattern into a regular +expression. They cache the compiled regular expressions for speed. + +The function translate(PATTERN) returns a regular expression +corresponding to PATTERN. (It does not compile it.) +""" + +import re + +__all__ = ["fnmatch","fnmatchcase","translate"] + +_cache = {} + +def fnmatch(name, pat): + """Test whether FILENAME matches PATTERN. + + Patterns are Unix shell style: + + * matches everything + ? matches any single character + [seq] matches any character in seq + [!seq] matches any char not in seq + + An initial period in FILENAME is not special. + Both FILENAME and PATTERN are first case-normalized + if the operating system requires it. + If you don't want this, use fnmatchcase(FILENAME, PATTERN). + """ + + import os + name = os.path.normcase(name) + pat = os.path.normcase(pat) + return fnmatchcase(name, pat) + +def filter(names, pat): + """Return the subset of the list NAMES that match PAT""" + import os,posixpath + result=[] + pat=os.path.normcase(pat) + if not _cache.has_key(pat): + res = translate(pat) + _cache[pat] = re.compile(res) + match=_cache[pat].match + if os.path is posixpath: + # normcase on posix is NOP. Optimize it away from the loop. + for name in names: + if match(name): + result.append(name) + else: + for name in names: + if match(os.path.normcase(name)): + result.append(name) + return result + +def fnmatchcase(name, pat): + """Test whether FILENAME matches PATTERN, including case. + + This is a version of fnmatch() which doesn't case-normalize + its arguments. + """ + + if not _cache.has_key(pat): + res = translate(pat) + _cache[pat] = re.compile(res) + return _cache[pat].match(name) is not None + +def translate(pat): + """Translate a shell PATTERN to a regular expression. + + There is no way to quote meta-characters. + """ + + i, n = 0, len(pat) + res = '' + while i < n: + c = pat[i] + i = i+1 + if c == '*': + res = res + '.*' + elif c == '?': + res = res + '.' + elif c == '[': + j = i + if j < n and pat[j] == '!': + j = j+1 + if j < n and pat[j] == ']': + j = j+1 + while j < n and pat[j] != ']': + j = j+1 + if j >= n: + res = res + '\\[' + else: + stuff = pat[i:j].replace('\\','\\\\') + i = j+1 + if stuff[0] == '!': + stuff = '^' + stuff[1:] + elif stuff[0] == '^': + stuff = '\\' + stuff + res = '%s[%s]' % (res, stuff) + else: + res = res + re.escape(c) + return res + "$" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fpformat.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fpformat.py new file mode 100644 index 00000000..21d2489e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/fpformat.py @@ -0,0 +1,142 @@ +"""General floating point formatting functions. + +Functions: +fix(x, digits_behind) +sci(x, digits_behind) + +Each takes a number or a string and a number of digits as arguments. + +Parameters: +x: number to be formatted; or a string resembling a number +digits_behind: number of digits behind the decimal point +""" + +import re + +__all__ = ["fix","sci","NotANumber"] + +# Compiled regular expression to "decode" a number +decoder = re.compile(r'^([-+]?)0*(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$') +# \0 the whole thing +# \1 leading sign or empty +# \2 digits left of decimal point +# \3 fraction (empty or begins with point) +# \4 exponent part (empty or begins with 'e' or 'E') + +try: + class NotANumber(ValueError): + pass +except TypeError: + NotANumber = 'fpformat.NotANumber' + +def extract(s): + """Return (sign, intpart, fraction, expo) or raise an exception: + sign is '+' or '-' + intpart is 0 or more digits beginning with a nonzero + fraction is 0 or more digits + expo is an integer""" + res = decoder.match(s) + if res is None: raise NotANumber, s + sign, intpart, fraction, exppart = res.group(1,2,3,4) + if sign == '+': sign = '' + if fraction: fraction = fraction[1:] + if exppart: expo = int(exppart[1:]) + else: expo = 0 + return sign, intpart, fraction, expo + +def unexpo(intpart, fraction, expo): + """Remove the exponent by changing intpart and fraction.""" + if expo > 0: # Move the point left + f = len(fraction) + intpart, fraction = intpart + fraction[:expo], fraction[expo:] + if expo > f: + intpart = intpart + '0'*(expo-f) + elif expo < 0: # Move the point right + i = len(intpart) + intpart, fraction = intpart[:expo], intpart[expo:] + fraction + if expo < -i: + fraction = '0'*(-expo-i) + fraction + return intpart, fraction + +def roundfrac(intpart, fraction, digs): + """Round or extend the fraction to size digs.""" + f = len(fraction) + if f <= digs: + return intpart, fraction + '0'*(digs-f) + i = len(intpart) + if i+digs < 0: + return '0'*-digs, '' + total = intpart + fraction + nextdigit = total[i+digs] + if nextdigit >= '5': # Hard case: increment last digit, may have carry! + n = i + digs - 1 + while n >= 0: + if total[n] != '9': break + n = n-1 + else: + total = '0' + total + i = i+1 + n = 0 + total = total[:n] + chr(ord(total[n]) + 1) + '0'*(len(total)-n-1) + intpart, fraction = total[:i], total[i:] + if digs >= 0: + return intpart, fraction[:digs] + else: + return intpart[:digs] + '0'*-digs, '' + +def fix(x, digs): + """Format x as [-]ddd.ddd with 'digs' digits after the point + and at least one digit before. + If digs <= 0, the point is suppressed.""" + if type(x) != type(''): x = `x` + try: + sign, intpart, fraction, expo = extract(x) + except NotANumber: + return x + intpart, fraction = unexpo(intpart, fraction, expo) + intpart, fraction = roundfrac(intpart, fraction, digs) + while intpart and intpart[0] == '0': intpart = intpart[1:] + if intpart == '': intpart = '0' + if digs > 0: return sign + intpart + '.' + fraction + else: return sign + intpart + +def sci(x, digs): + """Format x as [-]d.dddE[+-]ddd with 'digs' digits after the point + and exactly one digit before. + If digs is <= 0, one digit is kept and the point is suppressed.""" + if type(x) != type(''): x = `x` + sign, intpart, fraction, expo = extract(x) + if not intpart: + while fraction and fraction[0] == '0': + fraction = fraction[1:] + expo = expo - 1 + if fraction: + intpart, fraction = fraction[0], fraction[1:] + expo = expo - 1 + else: + intpart = '0' + else: + expo = expo + len(intpart) - 1 + intpart, fraction = intpart[0], intpart[1:] + fraction + digs = max(0, digs) + intpart, fraction = roundfrac(intpart, fraction, digs) + if len(intpart) > 1: + intpart, fraction, expo = \ + intpart[0], intpart[1:] + fraction[:-1], \ + expo + len(intpart) - 1 + s = sign + intpart + if digs > 0: s = s + '.' + fraction + e = `abs(expo)` + e = '0'*(3-len(e)) + e + if expo < 0: e = '-' + e + else: e = '+' + e + return s + 'e' + e + +def test(): + """Interactive test run.""" + try: + while 1: + x, digs = input('Enter (x, digs): ') + print x, fix(x, digs), sci(x, digs) + except (EOFError, KeyboardInterrupt): + pass diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/getopt.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/getopt.py new file mode 100644 index 00000000..961915df --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/getopt.py @@ -0,0 +1,144 @@ +"""Parser for command line options. + +This module helps scripts to parse the command line arguments in +sys.argv. It supports the same conventions as the Unix getopt() +function (including the special meanings of arguments of the form `-' +and `--'). Long options similar to those supported by GNU software +may be used as well via an optional third argument. This module +provides a single function and an exception: + +getopt() -- Parse command line options +GetoptError -- exception (class) raised with 'opt' attribute, which is the +option involved with the exception. +""" + +# Long option support added by Lars Wirzenius . + +# Gerrit Holl moved the string-based exceptions +# to class-based exceptions. + +__all__ = ["GetoptError","error","getopt"] + +class GetoptError(Exception): + opt = '' + msg = '' + def __init__(self, msg, opt): + self.msg = msg + self.opt = opt + Exception.__init__(self, msg, opt) + + def __str__(self): + return self.msg + +error = GetoptError # backward compatibility + +def getopt(args, shortopts, longopts = []): + """getopt(args, options[, long_options]) -> opts, args + + Parses command line options and parameter list. args is the + argument list to be parsed, without the leading reference to the + running program. Typically, this means "sys.argv[1:]". shortopts + is the string of option letters that the script wants to + recognize, with options that require an argument followed by a + colon (i.e., the same format that Unix getopt() uses). If + specified, longopts is a list of strings with the names of the + long options which should be supported. The leading '--' + characters should not be included in the option name. Options + which require an argument should be followed by an equal sign + ('='). + + The return value consists of two elements: the first is a list of + (option, value) pairs; the second is the list of program arguments + left after the option list was stripped (this is a trailing slice + of the first argument). Each option-and-value pair returned has + the option as its first element, prefixed with a hyphen (e.g., + '-x'), and the option argument as its second element, or an empty + string if the option has no argument. The options occur in the + list in the same order in which they were found, thus allowing + multiple occurrences. Long and short options may be mixed. + + """ + + opts = [] + if type(longopts) == type(""): + longopts = [longopts] + else: + longopts = list(longopts) + while args and args[0].startswith('-') and args[0] != '-': + if args[0] == '--': + args = args[1:] + break + if args[0].startswith('--'): + opts, args = do_longs(opts, args[0][2:], longopts, args[1:]) + else: + opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:]) + + return opts, args + +def do_longs(opts, opt, longopts, args): + try: + i = opt.index('=') + except ValueError: + optarg = None + else: + opt, optarg = opt[:i], opt[i+1:] + + has_arg, opt = long_has_args(opt, longopts) + if has_arg: + if optarg is None: + if not args: + raise GetoptError('option --%s requires argument' % opt, opt) + optarg, args = args[0], args[1:] + elif optarg: + raise GetoptError('option --%s must not have an argument' % opt, opt) + opts.append(('--' + opt, optarg or '')) + return opts, args + +# Return: +# has_arg? +# full option name +def long_has_args(opt, longopts): + possibilities = [o for o in longopts if o.startswith(opt)] + if not possibilities: + raise GetoptError('option --%s not recognized' % opt, opt) + # Is there an exact match? + if opt in possibilities: + return 0, opt + elif opt + '=' in possibilities: + return 1, opt + # No exact match, so better be unique. + if len(possibilities) > 1: + # XXX since possibilities contains all valid continuations, might be + # nice to work them into the error msg + raise GetoptError('option --%s not a unique prefix' % opt, opt) + assert len(possibilities) == 1 + unique_match = possibilities[0] + has_arg = unique_match.endswith('=') + if has_arg: + unique_match = unique_match[:-1] + return has_arg, unique_match + +def do_shorts(opts, optstring, shortopts, args): + while optstring != '': + opt, optstring = optstring[0], optstring[1:] + if short_has_arg(opt, shortopts): + if optstring == '': + if not args: + raise GetoptError('option -%s requires argument' % opt, + opt) + optstring, args = args[0], args[1:] + optarg, optstring = optstring, '' + else: + optarg = '' + opts.append(('-' + opt, optarg)) + return opts, args + +def short_has_arg(opt, shortopts): + for i in range(len(shortopts)): + if opt == shortopts[i] != ':': + return shortopts.startswith(':', i+1) + raise GetoptError('option -%s not recognized' % opt, opt) + +if __name__ == '__main__': + import sys + print getopt(sys.argv[1:], "a:b", ["alpha=", "beta"]) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/gettext.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/gettext.py new file mode 100644 index 00000000..afa50ef9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/gettext.py @@ -0,0 +1,304 @@ +"""Internationalization and localization support. + +This module provides internationalization (I18N) and localization (L10N) +support for your Python programs by providing an interface to the GNU gettext +message catalog library. + +I18N refers to the operation by which a program is made aware of multiple +languages. L10N refers to the adaptation of your program, once +internationalized, to the local language and cultural habits. + +""" + +# This module represents the integration of work, contributions, feedback, and +# suggestions from the following people: +# +# Martin von Loewis, who wrote the initial implementation of the underlying +# C-based libintlmodule (later renamed _gettext), along with a skeletal +# gettext.py implementation. +# +# Peter Funk, who wrote fintl.py, a fairly complete wrapper around intlmodule, +# which also included a pure-Python implementation to read .mo files if +# intlmodule wasn't available. +# +# James Henstridge, who also wrote a gettext.py module, which has some +# interesting, but currently unsupported experimental features: the notion of +# a Catalog class and instances, and the ability to add to a catalog file via +# a Python API. +# +# Barry Warsaw integrated these modules, wrote the .install() API and code, +# and conformed all C and Python code to Python's coding standards. +# +# Francois Pinard and Marc-Andre Lemburg also contributed valuably to this +# module. +# +# TODO: +# - Lazy loading of .mo files. Currently the entire catalog is loaded into +# memory, but that's probably bad for large translated programs. Instead, +# the lexical sort of original strings in GNU .mo files should be exploited +# to do binary searches and lazy initializations. Or you might want to use +# the undocumented double-hash algorithm for .mo files with hash tables, but +# you'll need to study the GNU gettext code to do this. +# +# - Support Solaris .mo file formats. Unfortunately, we've been unable to +# find this format documented anywhere. + +import os +import sys +import struct +from errno import ENOENT + +__all__ = ["bindtextdomain","textdomain","gettext","dgettext", + "find","translation","install","Catalog"] + +_default_localedir = os.path.join(sys.prefix, 'share', 'locale') + + + +def _expand_lang(locale): + from locale import normalize + locale = normalize(locale) + COMPONENT_CODESET = 1 << 0 + COMPONENT_TERRITORY = 1 << 1 + COMPONENT_MODIFIER = 1 << 2 + # split up the locale into its base components + mask = 0 + pos = locale.find('@') + if pos >= 0: + modifier = locale[pos:] + locale = locale[:pos] + mask |= COMPONENT_MODIFIER + else: + modifier = '' + pos = locale.find('.') + if pos >= 0: + codeset = locale[pos:] + locale = locale[:pos] + mask |= COMPONENT_CODESET + else: + codeset = '' + pos = locale.find('_') + if pos >= 0: + territory = locale[pos:] + locale = locale[:pos] + mask |= COMPONENT_TERRITORY + else: + territory = '' + language = locale + ret = [] + for i in range(mask+1): + if not (i & ~mask): # if all components for this combo exist ... + val = language + if i & COMPONENT_TERRITORY: val += territory + if i & COMPONENT_CODESET: val += codeset + if i & COMPONENT_MODIFIER: val += modifier + ret.append(val) + ret.reverse() + return ret + + + +class NullTranslations: + def __init__(self, fp=None): + self._info = {} + self._charset = None + if fp: + self._parse(fp) + + def _parse(self, fp): + pass + + def gettext(self, message): + return message + + def ugettext(self, message): + return unicode(message) + + def info(self): + return self._info + + def charset(self): + return self._charset + + def install(self, unicode=0): + import __builtin__ + __builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext + + +class GNUTranslations(NullTranslations): + # Magic number of .mo files + LE_MAGIC = 0x950412de + BE_MAGIC = 0xde120495 + + def _parse(self, fp): + """Override this method to support alternative .mo formats.""" + # We need to & all 32 bit unsigned integers with 0xffffffff for + # portability to 64 bit machines. + MASK = 0xffffffff + unpack = struct.unpack + filename = getattr(fp, 'name', '') + # Parse the .mo file header, which consists of 5 little endian 32 + # bit words. + self._catalog = catalog = {} + buf = fp.read() + buflen = len(buf) + # Are we big endian or little endian? + magic = unpack('4i', buf[4:20]) + ii = '>ii' + else: + raise IOError(0, 'Bad magic number', filename) + # more unsigned ints + msgcount &= MASK + masteridx &= MASK + transidx &= MASK + # Now put all messages from the .mo file buffer into the catalog + # dictionary. + for i in xrange(0, msgcount): + mlen, moff = unpack(ii, buf[masteridx:masteridx+8]) + moff &= MASK + mend = moff + (mlen & MASK) + tlen, toff = unpack(ii, buf[transidx:transidx+8]) + toff &= MASK + tend = toff + (tlen & MASK) + if mend < buflen and tend < buflen: + tmsg = buf[toff:tend] + catalog[buf[moff:mend]] = tmsg + else: + raise IOError(0, 'File is corrupt', filename) + # See if we're looking at GNU .mo conventions for metadata + if mlen == 0 and tmsg.lower().startswith('project-id-version:'): + # Catalog description + for item in tmsg.split('\n'): + item = item.strip() + if not item: + continue + k, v = item.split(':', 1) + k = k.strip().lower() + v = v.strip() + self._info[k] = v + if k == 'content-type': + self._charset = v.split('charset=')[1] + # advance to next entry in the seek tables + masteridx += 8 + transidx += 8 + + def gettext(self, message): + return self._catalog.get(message, message) + + def ugettext(self, message): + tmsg = self._catalog.get(message, message) + return unicode(tmsg, self._charset) + + + +# Locate a .mo file using the gettext strategy +def find(domain, localedir=None, languages=None): + # Get some reasonable defaults for arguments that were not supplied + if localedir is None: + localedir = _default_localedir + if languages is None: + languages = [] + for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): + val = os.environ.get(envar) + if val: + languages = val.split(':') + break + if 'C' not in languages: + languages.append('C') + # now normalize and expand the languages + nelangs = [] + for lang in languages: + for nelang in _expand_lang(lang): + if nelang not in nelangs: + nelangs.append(nelang) + # select a language + for lang in nelangs: + if lang == 'C': + break + mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain) + if os.path.exists(mofile): + return mofile + return None + + + +# a mapping between absolute .mo file path and Translation object +_translations = {} + +def translation(domain, localedir=None, languages=None, + class_=None, fallback=0): + if class_ is None: + class_ = GNUTranslations + mofile = find(domain, localedir, languages) + if mofile is None: + if fallback: + return NullTranslations() + raise IOError(ENOENT, 'No translation file found for domain', domain) + key = os.path.abspath(mofile) + # TBD: do we need to worry about the file pointer getting collected? + # Avoid opening, reading, and parsing the .mo file after it's been done + # once. + t = _translations.get(key) + if t is None: + t = _translations.setdefault(key, class_(open(mofile, 'rb'))) + return t + + + +def install(domain, localedir=None, unicode=0): + translation(domain, localedir, fallback=1).install(unicode) + + + +# a mapping b/w domains and locale directories +_localedirs = {} +# current global domain, `messages' used for compatibility w/ GNU gettext +_current_domain = 'messages' + + +def textdomain(domain=None): + global _current_domain + if domain is not None: + _current_domain = domain + return _current_domain + + +def bindtextdomain(domain, localedir=None): + global _localedirs + if localedir is not None: + _localedirs[domain] = localedir + return _localedirs.get(domain, _default_localedir) + + +def dgettext(domain, message): + try: + t = translation(domain, _localedirs.get(domain, None)) + except IOError: + return message + return t.gettext(message) + + +def gettext(message): + return dgettext(_current_domain, message) + + +# dcgettext() has been deemed unnecessary and is not implemented. + +# James Henstridge's Catalog constructor from GNOME gettext. Documented usage +# was: +# +# import gettext +# cat = gettext.Catalog(PACKAGE, localedir=LOCALEDIR) +# _ = cat.gettext +# print _('Hello World') + +# The resulting catalog object currently don't support access through a +# dictionary API, which was supported (but apparently unused) in GNOME +# gettext. + +Catalog = translation diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/glob.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/glob.py new file mode 100644 index 00000000..62fd8f5f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/glob.py @@ -0,0 +1,56 @@ +"""Filename globbing utility.""" + +import os +import fnmatch +import re + +__all__ = ["glob"] + +def glob(pathname): + """Return a list of paths matching a pathname pattern. + + The pattern may contain simple shell-style wildcards a la fnmatch. + + """ + if not has_magic(pathname): + if os.path.exists(pathname): + return [pathname] + else: + return [] + dirname, basename = os.path.split(pathname) + if not dirname: + return glob1(os.curdir, basename) + elif has_magic(dirname): + list = glob(dirname) + else: + list = [dirname] + if not has_magic(basename): + result = [] + for dirname in list: + if basename or os.path.isdir(dirname): + name = os.path.join(dirname, basename) + if os.path.exists(name): + result.append(name) + else: + result = [] + for dirname in list: + sublist = glob1(dirname, basename) + for name in sublist: + result.append(os.path.join(dirname, name)) + return result + +def glob1(dirname, pattern): + if not dirname: dirname = os.curdir + try: + names = os.listdir(dirname) + except os.error: + return [] + if pattern[0]!='.': + names=filter(lambda x: x[0]!='.',names) + return fnmatch.filter(names,pattern) + + +magic_check = re.compile('[*?[]') + +def has_magic(s): + return magic_check.search(s) is not None diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/ihooks.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/ihooks.py new file mode 100644 index 00000000..fa58cda2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/ihooks.py @@ -0,0 +1,511 @@ +"""Import hook support. + +Consistent use of this module will make it possible to change the +different mechanisms involved in loading modules independently. + +While the built-in module imp exports interfaces to the built-in +module searching and loading algorithm, and it is possible to replace +the built-in function __import__ in order to change the semantics of +the import statement, until now it has been difficult to combine the +effect of different __import__ hacks, like loading modules from URLs +by rimport.py, or restricted execution by rexec.py. + +This module defines three new concepts: + +1) A "file system hooks" class provides an interface to a filesystem. + +One hooks class is defined (Hooks), which uses the interface provided +by standard modules os and os.path. It should be used as the base +class for other hooks classes. + +2) A "module loader" class provides an interface to to search for a +module in a search path and to load it. It defines a method which +searches for a module in a single directory; by overriding this method +one can redefine the details of the search. If the directory is None, +built-in and frozen modules are searched instead. + +Two module loader class are defined, both implementing the search +strategy used by the built-in __import__ function: ModuleLoader uses +the imp module's find_module interface, while HookableModuleLoader +uses a file system hooks class to interact with the file system. Both +use the imp module's load_* interfaces to actually load the module. + +3) A "module importer" class provides an interface to import a +module, as well as interfaces to reload and unload a module. It also +provides interfaces to install and uninstall itself instead of the +default __import__ and reload (and unload) functions. + +One module importer class is defined (ModuleImporter), which uses a +module loader instance passed in (by default HookableModuleLoader is +instantiated). + +The classes defined here should be used as base classes for extended +functionality along those lines. + +If a module importer class supports dotted names, its import_module() +must return a different value depending on whether it is called on +behalf of a "from ... import ..." statement or not. (This is caused +by the way the __import__ hook is used by the Python interpreter.) It +would also do wise to install a different version of reload(). + +""" + + +import __builtin__ +import imp +import os +import sys + +__all__ = ["BasicModuleLoader","Hooks","ModuleLoader","FancyModuleLoader", + "BasicModuleImporter","ModuleImporter","install","uninstall"] + +VERBOSE = 0 + + +from imp import C_EXTENSION, PY_SOURCE, PY_COMPILED +from imp import C_BUILTIN, PY_FROZEN, PKG_DIRECTORY +BUILTIN_MODULE = C_BUILTIN +FROZEN_MODULE = PY_FROZEN + + +class _Verbose: + + def __init__(self, verbose = VERBOSE): + self.verbose = verbose + + def get_verbose(self): + return self.verbose + + def set_verbose(self, verbose): + self.verbose = verbose + + # XXX The following is an experimental interface + + def note(self, *args): + if self.verbose: + apply(self.message, args) + + def message(self, format, *args): + if args: + print format%args + else: + print format + + +class BasicModuleLoader(_Verbose): + + """Basic module loader. + + This provides the same functionality as built-in import. It + doesn't deal with checking sys.modules -- all it provides is + find_module() and a load_module(), as well as find_module_in_dir() + which searches just one directory, and can be overridden by a + derived class to change the module search algorithm when the basic + dependency on sys.path is unchanged. + + The interface is a little more convenient than imp's: + find_module(name, [path]) returns None or 'stuff', and + load_module(name, stuff) loads the module. + + """ + + def find_module(self, name, path = None): + if path is None: + path = [None] + self.default_path() + for dir in path: + stuff = self.find_module_in_dir(name, dir) + if stuff: return stuff + return None + + def default_path(self): + return sys.path + + def find_module_in_dir(self, name, dir): + if dir is None: + return self.find_builtin_module(name) + else: + try: + return imp.find_module(name, [dir]) + except ImportError: + return None + + def find_builtin_module(self, name): + # XXX frozen packages? + if imp.is_builtin(name): + return None, '', ('', '', BUILTIN_MODULE) + if imp.is_frozen(name): + return None, '', ('', '', FROZEN_MODULE) + return None + + def load_module(self, name, stuff): + file, filename, info = stuff + try: + return imp.load_module(name, file, filename, info) + finally: + if file: file.close() + + +class Hooks(_Verbose): + + """Hooks into the filesystem and interpreter. + + By deriving a subclass you can redefine your filesystem interface, + e.g. to merge it with the URL space. + + This base class behaves just like the native filesystem. + + """ + + # imp interface + def get_suffixes(self): return imp.get_suffixes() + def new_module(self, name): return imp.new_module(name) + def is_builtin(self, name): return imp.is_builtin(name) + def init_builtin(self, name): return imp.init_builtin(name) + def is_frozen(self, name): return imp.is_frozen(name) + def init_frozen(self, name): return imp.init_frozen(name) + def get_frozen_object(self, name): return imp.get_frozen_object(name) + def load_source(self, name, filename, file=None): + return imp.load_source(name, filename, file) + def load_compiled(self, name, filename, file=None): + return imp.load_compiled(name, filename, file) + def load_dynamic(self, name, filename, file=None): + return imp.load_dynamic(name, filename, file) + def load_package(self, name, filename, file=None): + return imp.load_module(name, file, filename, ("", "", PKG_DIRECTORY)) + + def add_module(self, name): + d = self.modules_dict() + if d.has_key(name): return d[name] + d[name] = m = self.new_module(name) + return m + + # sys interface + def modules_dict(self): return sys.modules + def default_path(self): return sys.path + + def path_split(self, x): return os.path.split(x) + def path_join(self, x, y): return os.path.join(x, y) + def path_isabs(self, x): return os.path.isabs(x) + # etc. + + def path_exists(self, x): return os.path.exists(x) + def path_isdir(self, x): return os.path.isdir(x) + def path_isfile(self, x): return os.path.isfile(x) + def path_islink(self, x): return os.path.islink(x) + # etc. + + def openfile(self, *x): return apply(open, x) + openfile_error = IOError + def listdir(self, x): return os.listdir(x) + listdir_error = os.error + # etc. + + +class ModuleLoader(BasicModuleLoader): + + """Default module loader; uses file system hooks. + + By defining suitable hooks, you might be able to load modules from + other sources than the file system, e.g. from compressed or + encrypted files, tar files or (if you're brave!) URLs. + + """ + + def __init__(self, hooks = None, verbose = VERBOSE): + BasicModuleLoader.__init__(self, verbose) + self.hooks = hooks or Hooks(verbose) + + def default_path(self): + return self.hooks.default_path() + + def modules_dict(self): + return self.hooks.modules_dict() + + def get_hooks(self): + return self.hooks + + def set_hooks(self, hooks): + self.hooks = hooks + + def find_builtin_module(self, name): + # XXX frozen packages? + if self.hooks.is_builtin(name): + return None, '', ('', '', BUILTIN_MODULE) + if self.hooks.is_frozen(name): + return None, '', ('', '', FROZEN_MODULE) + return None + + def find_module_in_dir(self, name, dir, allow_packages=1): + if dir is None: + return self.find_builtin_module(name) + if allow_packages: + fullname = self.hooks.path_join(dir, name) + if self.hooks.path_isdir(fullname): + stuff = self.find_module_in_dir("__init__", fullname, 0) + if stuff: + file = stuff[0] + if file: file.close() + return None, fullname, ('', '', PKG_DIRECTORY) + for info in self.hooks.get_suffixes(): + suff, mode, type = info + fullname = self.hooks.path_join(dir, name+suff) + try: + fp = self.hooks.openfile(fullname, mode) + return fp, fullname, info + except self.hooks.openfile_error: + pass + return None + + def load_module(self, name, stuff): + file, filename, info = stuff + (suff, mode, type) = info + try: + if type == BUILTIN_MODULE: + return self.hooks.init_builtin(name) + if type == FROZEN_MODULE: + return self.hooks.init_frozen(name) + if type == C_EXTENSION: + m = self.hooks.load_dynamic(name, filename, file) + elif type == PY_SOURCE: + m = self.hooks.load_source(name, filename, file) + elif type == PY_COMPILED: + m = self.hooks.load_compiled(name, filename, file) + elif type == PKG_DIRECTORY: + m = self.hooks.load_package(name, filename, file) + else: + raise ImportError, "Unrecognized module type (%s) for %s" % \ + (`type`, name) + finally: + if file: file.close() + m.__file__ = filename + return m + + +class FancyModuleLoader(ModuleLoader): + + """Fancy module loader -- parses and execs the code itself.""" + + def load_module(self, name, stuff): + file, filename, (suff, mode, type) = stuff + realfilename = filename + path = None + + if type == PKG_DIRECTORY: + initstuff = self.find_module_in_dir("__init__", filename, 0) + if not initstuff: + raise ImportError, "No __init__ module in package %s" % name + initfile, initfilename, initinfo = initstuff + initsuff, initmode, inittype = initinfo + if inittype not in (PY_COMPILED, PY_SOURCE): + if initfile: initfile.close() + raise ImportError, \ + "Bad type (%s) for __init__ module in package %s" % ( + `inittype`, name) + path = [filename] + file = initfile + realfilename = initfilename + type = inittype + + if type == FROZEN_MODULE: + code = self.hooks.get_frozen_object(name) + elif type == PY_COMPILED: + import marshal + file.seek(8) + code = marshal.load(file) + elif type == PY_SOURCE: + data = file.read() + code = compile(data, realfilename, 'exec') + else: + return ModuleLoader.load_module(self, name, stuff) + + m = self.hooks.add_module(name) + if path: + m.__path__ = path + m.__file__ = filename + exec code in m.__dict__ + return m + + +class BasicModuleImporter(_Verbose): + + """Basic module importer; uses module loader. + + This provides basic import facilities but no package imports. + + """ + + def __init__(self, loader = None, verbose = VERBOSE): + _Verbose.__init__(self, verbose) + self.loader = loader or ModuleLoader(None, verbose) + self.modules = self.loader.modules_dict() + + def get_loader(self): + return self.loader + + def set_loader(self, loader): + self.loader = loader + + def get_hooks(self): + return self.loader.get_hooks() + + def set_hooks(self, hooks): + return self.loader.set_hooks(hooks) + + def import_module(self, name, globals={}, locals={}, fromlist=[]): + if self.modules.has_key(name): + return self.modules[name] # Fast path + stuff = self.loader.find_module(name) + if not stuff: + raise ImportError, "No module named %s" % name + return self.loader.load_module(name, stuff) + + def reload(self, module, path = None): + name = module.__name__ + stuff = self.loader.find_module(name, path) + if not stuff: + raise ImportError, "Module %s not found for reload" % name + return self.loader.load_module(name, stuff) + + def unload(self, module): + del self.modules[module.__name__] + # XXX Should this try to clear the module's namespace? + + def install(self): + self.save_import_module = __builtin__.__import__ + self.save_reload = __builtin__.reload + if not hasattr(__builtin__, 'unload'): + __builtin__.unload = None + self.save_unload = __builtin__.unload + __builtin__.__import__ = self.import_module + __builtin__.reload = self.reload + __builtin__.unload = self.unload + + def uninstall(self): + __builtin__.__import__ = self.save_import_module + __builtin__.reload = self.save_reload + __builtin__.unload = self.save_unload + if not __builtin__.unload: + del __builtin__.unload + + +class ModuleImporter(BasicModuleImporter): + + """A module importer that supports packages.""" + + def import_module(self, name, globals=None, locals=None, fromlist=None): + parent = self.determine_parent(globals) + q, tail = self.find_head_package(parent, name) + m = self.load_tail(q, tail) + if not fromlist: + return q + if hasattr(m, "__path__"): + self.ensure_fromlist(m, fromlist) + return m + + def determine_parent(self, globals): + if not globals or not globals.has_key("__name__"): + return None + pname = globals['__name__'] + if globals.has_key("__path__"): + parent = self.modules[pname] + assert globals is parent.__dict__ + return parent + if '.' in pname: + i = pname.rfind('.') + pname = pname[:i] + parent = self.modules[pname] + assert parent.__name__ == pname + return parent + return None + + def find_head_package(self, parent, name): + if '.' in name: + i = name.find('.') + head = name[:i] + tail = name[i+1:] + else: + head = name + tail = "" + if parent: + qname = "%s.%s" % (parent.__name__, head) + else: + qname = head + q = self.import_it(head, qname, parent) + if q: return q, tail + if parent: + qname = head + parent = None + q = self.import_it(head, qname, parent) + if q: return q, tail + raise ImportError, "No module named " + qname + + def load_tail(self, q, tail): + m = q + while tail: + i = tail.find('.') + if i < 0: i = len(tail) + head, tail = tail[:i], tail[i+1:] + mname = "%s.%s" % (m.__name__, head) + m = self.import_it(head, mname, m) + if not m: + raise ImportError, "No module named " + mname + return m + + def ensure_fromlist(self, m, fromlist, recursive=0): + for sub in fromlist: + if sub == "*": + if not recursive: + try: + all = m.__all__ + except AttributeError: + pass + else: + self.ensure_fromlist(m, all, 1) + continue + if sub != "*" and not hasattr(m, sub): + subname = "%s.%s" % (m.__name__, sub) + submod = self.import_it(sub, subname, m) + if not submod: + raise ImportError, "No module named " + subname + + def import_it(self, partname, fqname, parent, force_load=0): + if not partname: + raise ValueError, "Empty module name" + if not force_load: + try: + return self.modules[fqname] + except KeyError: + pass + try: + path = parent and parent.__path__ + except AttributeError: + return None + stuff = self.loader.find_module(partname, path) + if not stuff: + return None + m = self.loader.load_module(fqname, stuff) + if parent: + setattr(parent, partname, m) + return m + + def reload(self, module): + name = module.__name__ + if '.' not in name: + return self.import_it(name, name, None, force_load=1) + i = name.rfind('.') + pname = name[:i] + parent = self.modules[pname] + return self.import_it(name[i+1:], name, parent, force_load=1) + + +default_importer = None +current_importer = None + +def install(importer = None): + global current_importer + current_importer = importer or default_importer or ModuleImporter() + current_importer.install() + +def uninstall(): + global current_importer + current_importer.uninstall() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/imghdr.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/imghdr.py new file mode 100644 index 00000000..5fd57817 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/imghdr.py @@ -0,0 +1,154 @@ +"""Recognize image file formats based on their first few bytes.""" + +__all__ = ["what"] + +#-------------------------# +# Recognize image headers # +#-------------------------# + +def what(file, h=None): + if h is None: + if type(file) == type(''): + f = open(file, 'rb') + h = f.read(32) + else: + location = file.tell() + h = file.read(32) + file.seek(location) + f = None + else: + f = None + try: + for tf in tests: + res = tf(h, f) + if res: + return res + finally: + if f: f.close() + return None + + +#---------------------------------# +# Subroutines per image file type # +#---------------------------------# + +tests = [] + +def test_rgb(h, f): + """SGI image library""" + if h[:2] == '\001\332': + return 'rgb' + +tests.append(test_rgb) + +def test_gif(h, f): + """GIF ('87 and '89 variants)""" + if h[:6] in ('GIF87a', 'GIF89a'): + return 'gif' + +tests.append(test_gif) + +def test_pbm(h, f): + """PBM (portable bitmap)""" + if len(h) >= 3 and \ + h[0] == 'P' and h[1] in '14' and h[2] in ' \t\n\r': + return 'pbm' + +tests.append(test_pbm) + +def test_pgm(h, f): + """PGM (portable graymap)""" + if len(h) >= 3 and \ + h[0] == 'P' and h[1] in '25' and h[2] in ' \t\n\r': + return 'pgm' + +tests.append(test_pgm) + +def test_ppm(h, f): + """PPM (portable pixmap)""" + if len(h) >= 3 and \ + h[0] == 'P' and h[1] in '36' and h[2] in ' \t\n\r': + return 'ppm' + +tests.append(test_ppm) + +def test_tiff(h, f): + """TIFF (can be in Motorola or Intel byte order)""" + if h[:2] in ('MM', 'II'): + return 'tiff' + +tests.append(test_tiff) + +def test_rast(h, f): + """Sun raster file""" + if h[:4] == '\x59\xA6\x6A\x95': + return 'rast' + +tests.append(test_rast) + +def test_xbm(h, f): + """X bitmap (X10 or X11)""" + s = '#define ' + if h[:len(s)] == s: + return 'xbm' + +tests.append(test_xbm) + +def test_jpeg(h, f): + """JPEG data in JFIF format""" + if h[6:10] == 'JFIF': + return 'jpeg' + +tests.append(test_jpeg) + +def test_bmp(h, f): + if h[:2] == 'BM': + return 'bmp' + +tests.append(test_bmp) + +def test_png(h, f): + if h[:8] == "\211PNG\r\n\032\n": + return 'png' + +tests.append(test_png) + +#--------------------# +# Small test program # +#--------------------# + +def test(): + import sys + recursive = 0 + if sys.argv[1:] and sys.argv[1] == '-r': + del sys.argv[1:2] + recursive = 1 + try: + if sys.argv[1:]: + testall(sys.argv[1:], recursive, 1) + else: + testall(['.'], recursive, 1) + except KeyboardInterrupt: + sys.stderr.write('\n[Interrupted]\n') + sys.exit(1) + +def testall(list, recursive, toplevel): + import sys + import os + for filename in list: + if os.path.isdir(filename): + print filename + '/:', + if recursive or toplevel: + print 'recursing down:' + import glob + names = glob.glob(os.path.join(filename, '*')) + testall(names, recursive, 0) + else: + print '*** directory (use -r) ***' + else: + print filename + ':', + sys.stdout.flush() + try: + print what(filename) + except IOError: + print '*** not found ***' diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/imputil.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/imputil.py new file mode 100644 index 00000000..2628e19c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/imputil.py @@ -0,0 +1,720 @@ +""" +Import utilities + +Exported classes: + ImportManager Manage the import process + + Importer Base class for replacing standard import functions + BuiltinImporter Emulate the import mechanism for builtin and frozen modules + + DynLoadSuffixImporter +""" + +# note: avoid importing non-builtin modules +import imp ### not available in JPython? +import sys +import __builtin__ + +# for the DirectoryImporter +import struct +import marshal + +__all__ = ["ImportManager","Importer","BuiltinImporter"] + +_StringType = type('') +_ModuleType = type(sys) ### doesn't work in JPython... + +class ImportManager: + "Manage the import process." + + def install(self, namespace=vars(__builtin__)): + "Install this ImportManager into the specified namespace." + + if isinstance(namespace, _ModuleType): + namespace = vars(namespace) + + # Note: we have no notion of "chaining" + + # Record the previous import hook, then install our own. + self.previous_importer = namespace['__import__'] + self.namespace = namespace + namespace['__import__'] = self._import_hook + + ### fix this + #namespace['reload'] = self._reload_hook + + def uninstall(self): + "Restore the previous import mechanism." + self.namespace['__import__'] = self.previous_importer + + def add_suffix(self, suffix, importFunc): + assert callable(importFunc) + self.fs_imp.add_suffix(suffix, importFunc) + + ###################################################################### + # + # PRIVATE METHODS + # + + clsFilesystemImporter = None + + def __init__(self, fs_imp=None): + # we're definitely going to be importing something in the future, + # so let's just load the OS-related facilities. + if not _os_stat: + _os_bootstrap() + + # This is the Importer that we use for grabbing stuff from the + # filesystem. It defines one more method (import_from_dir) for our use. + if not fs_imp: + cls = self.clsFilesystemImporter or _FilesystemImporter + fs_imp = cls() + self.fs_imp = fs_imp + + # Initialize the set of suffixes that we recognize and import. + # The default will import dynamic-load modules first, followed by + # .py files (or a .py file's cached bytecode) + for desc in imp.get_suffixes(): + if desc[2] == imp.C_EXTENSION: + self.add_suffix(desc[0], + DynLoadSuffixImporter(desc).import_file) + self.add_suffix('.py', py_suffix_importer) + + def _import_hook(self, fqname, globals=None, locals=None, fromlist=None): + """Python calls this hook to locate and import a module.""" + + parts = fqname.split('.') + + # determine the context of this import + parent = self._determine_import_context(globals) + + # if there is a parent, then its importer should manage this import + if parent: + module = parent.__importer__._do_import(parent, parts, fromlist) + if module: + return module + + # has the top module already been imported? + try: + top_module = sys.modules[parts[0]] + except KeyError: + + # look for the topmost module + top_module = self._import_top_module(parts[0]) + if not top_module: + # the topmost module wasn't found at all. + raise ImportError, 'No module named ' + fqname + + # fast-path simple imports + if len(parts) == 1: + if not fromlist: + return top_module + + if not top_module.__dict__.get('__ispkg__'): + # __ispkg__ isn't defined (the module was not imported by us), + # or it is zero. + # + # In the former case, there is no way that we could import + # sub-modules that occur in the fromlist (but we can't raise an + # error because it may just be names) because we don't know how + # to deal with packages that were imported by other systems. + # + # In the latter case (__ispkg__ == 0), there can't be any sub- + # modules present, so we can just return. + # + # In both cases, since len(parts) == 1, the top_module is also + # the "bottom" which is the defined return when a fromlist + # exists. + return top_module + + importer = top_module.__dict__.get('__importer__') + if importer: + return importer._finish_import(top_module, parts[1:], fromlist) + + # Grrr, some people "import os.path" + if len(parts) == 2 and hasattr(top_module, parts[1]): + return top_module + + # If the importer does not exist, then we have to bail. A missing + # importer means that something else imported the module, and we have + # no knowledge of how to get sub-modules out of the thing. + raise ImportError, 'No module named ' + fqname + + def _determine_import_context(self, globals): + """Returns the context in which a module should be imported. + + The context could be a loaded (package) module and the imported module + will be looked for within that package. The context could also be None, + meaning there is no context -- the module should be looked for as a + "top-level" module. + """ + + if not globals or not globals.get('__importer__'): + # globals does not refer to one of our modules or packages. That + # implies there is no relative import context (as far as we are + # concerned), and it should just pick it off the standard path. + return None + + # The globals refer to a module or package of ours. It will define + # the context of the new import. Get the module/package fqname. + parent_fqname = globals['__name__'] + + # if a package is performing the import, then return itself (imports + # refer to pkg contents) + if globals['__ispkg__']: + parent = sys.modules[parent_fqname] + assert globals is parent.__dict__ + return parent + + i = parent_fqname.rfind('.') + + # a module outside of a package has no particular import context + if i == -1: + return None + + # if a module in a package is performing the import, then return the + # package (imports refer to siblings) + parent_fqname = parent_fqname[:i] + parent = sys.modules[parent_fqname] + assert parent.__name__ == parent_fqname + return parent + + def _import_top_module(self, name): + # scan sys.path looking for a location in the filesystem that contains + # the module, or an Importer object that can import the module. + for item in sys.path: + if isinstance(item, _StringType): + module = self.fs_imp.import_from_dir(item, name) + else: + module = item.import_top(name) + if module: + return module + return None + + def _reload_hook(self, module): + "Python calls this hook to reload a module." + + # reloading of a module may or may not be possible (depending on the + # importer), but at least we can validate that it's ours to reload + importer = module.__dict__.get('__importer__') + if not importer: + ### oops. now what... + pass + + # okay. it is using the imputil system, and we must delegate it, but + # we don't know what to do (yet) + ### we should blast the module dict and do another get_code(). need to + ### flesh this out and add proper docco... + raise SystemError, "reload not yet implemented" + + +class Importer: + "Base class for replacing standard import functions." + + def import_top(self, name): + "Import a top-level module." + return self._import_one(None, name, name) + + ###################################################################### + # + # PRIVATE METHODS + # + def _finish_import(self, top, parts, fromlist): + # if "a.b.c" was provided, then load the ".b.c" portion down from + # below the top-level module. + bottom = self._load_tail(top, parts) + + # if the form is "import a.b.c", then return "a" + if not fromlist: + # no fromlist: return the top of the import tree + return top + + # the top module was imported by self. + # + # this means that the bottom module was also imported by self (just + # now, or in the past and we fetched it from sys.modules). + # + # since we imported/handled the bottom module, this means that we can + # also handle its fromlist (and reliably use __ispkg__). + + # if the bottom node is a package, then (potentially) import some + # modules. + # + # note: if it is not a package, then "fromlist" refers to names in + # the bottom module rather than modules. + # note: for a mix of names and modules in the fromlist, we will + # import all modules and insert those into the namespace of + # the package module. Python will pick up all fromlist names + # from the bottom (package) module; some will be modules that + # we imported and stored in the namespace, others are expected + # to be present already. + if bottom.__ispkg__: + self._import_fromlist(bottom, fromlist) + + # if the form is "from a.b import c, d" then return "b" + return bottom + + def _import_one(self, parent, modname, fqname): + "Import a single module." + + # has the module already been imported? + try: + return sys.modules[fqname] + except KeyError: + pass + + # load the module's code, or fetch the module itself + result = self.get_code(parent, modname, fqname) + if result is None: + return None + + module = self._process_result(result, fqname) + + # insert the module into its parent + if parent: + setattr(parent, modname, module) + return module + + def _process_result(self, (ispkg, code, values), fqname): + # did get_code() return an actual module? (rather than a code object) + is_module = isinstance(code, _ModuleType) + + # use the returned module, or create a new one to exec code into + if is_module: + module = code + else: + module = imp.new_module(fqname) + + ### record packages a bit differently?? + module.__importer__ = self + module.__ispkg__ = ispkg + + # insert additional values into the module (before executing the code) + module.__dict__.update(values) + + # the module is almost ready... make it visible + sys.modules[fqname] = module + + # execute the code within the module's namespace + if not is_module: + exec code in module.__dict__ + + # fetch from sys.modules instead of returning module directly. + # also make module's __name__ agree with fqname, in case + # the "exec code in module.__dict__" played games on us. + module = sys.modules[fqname] + module.__name__ = fqname + return module + + def _load_tail(self, m, parts): + """Import the rest of the modules, down from the top-level module. + + Returns the last module in the dotted list of modules. + """ + for part in parts: + fqname = "%s.%s" % (m.__name__, part) + m = self._import_one(m, part, fqname) + if not m: + raise ImportError, "No module named " + fqname + return m + + def _import_fromlist(self, package, fromlist): + 'Import any sub-modules in the "from" list.' + + # if '*' is present in the fromlist, then look for the '__all__' + # variable to find additional items (modules) to import. + if '*' in fromlist: + fromlist = list(fromlist) + \ + list(package.__dict__.get('__all__', [])) + + for sub in fromlist: + # if the name is already present, then don't try to import it (it + # might not be a module!). + if sub != '*' and not hasattr(package, sub): + subname = "%s.%s" % (package.__name__, sub) + submod = self._import_one(package, sub, subname) + if not submod: + raise ImportError, "cannot import name " + subname + + def _do_import(self, parent, parts, fromlist): + """Attempt to import the module relative to parent. + + This method is used when the import context specifies that + imported the parent module. + """ + top_name = parts[0] + top_fqname = parent.__name__ + '.' + top_name + top_module = self._import_one(parent, top_name, top_fqname) + if not top_module: + # this importer and parent could not find the module (relatively) + return None + + return self._finish_import(top_module, parts[1:], fromlist) + + ###################################################################### + # + # METHODS TO OVERRIDE + # + def get_code(self, parent, modname, fqname): + """Find and retrieve the code for the given module. + + parent specifies a parent module to define a context for importing. It + may be None, indicating no particular context for the search. + + modname specifies a single module (not dotted) within the parent. + + fqname specifies the fully-qualified module name. This is a + (potentially) dotted name from the "root" of the module namespace + down to the modname. + If there is no parent, then modname==fqname. + + This method should return None, or a 3-tuple. + + * If the module was not found, then None should be returned. + + * The first item of the 2- or 3-tuple should be the integer 0 or 1, + specifying whether the module that was found is a package or not. + + * The second item is the code object for the module (it will be + executed within the new module's namespace). This item can also + be a fully-loaded module object (e.g. loaded from a shared lib). + + * The third item is a dictionary of name/value pairs that will be + inserted into new module before the code object is executed. This + is provided in case the module's code expects certain values (such + as where the module was found). When the second item is a module + object, then these names/values will be inserted *after* the module + has been loaded/initialized. + """ + raise RuntimeError, "get_code not implemented" + + +###################################################################### +# +# Some handy stuff for the Importers +# + +# byte-compiled file suffix character +_suffix_char = __debug__ and 'c' or 'o' + +# byte-compiled file suffix +_suffix = '.py' + _suffix_char + +def _compile(pathname, timestamp): + """Compile (and cache) a Python source file. + + The file specified by is compiled to a code object and + returned. + + Presuming the appropriate privileges exist, the bytecodes will be + saved back to the filesystem for future imports. The source file's + modification timestamp must be provided as a Long value. + """ + codestring = open(pathname, 'r').read() + if codestring and codestring[-1] != '\n': + codestring = codestring + '\n' + code = __builtin__.compile(codestring, pathname, 'exec') + + # try to cache the compiled code + try: + f = open(pathname + _suffix_char, 'wb') + except IOError: + pass + else: + f.write('\0\0\0\0') + f.write(struct.pack('= t_py: + f = open(file, 'rb') + if f.read(4) == imp.get_magic(): + t = struct.unpack('>> import foo +# >>> foo +# +# +# ---- revamped import mechanism +# >>> import imputil +# >>> imputil._test_revamp() +# >>> import foo +# >>> foo +# +# +# +# from MAL: +# should BuiltinImporter exist in sys.path or hard-wired in ImportManager? +# need __path__ processing +# performance +# move chaining to a subclass [gjs: it's been nuked] +# deinstall should be possible +# query mechanism needed: is a specific Importer installed? +# py/pyc/pyo piping hooks to filter/process these files +# wish list: +# distutils importer hooked to list of standard Internet repositories +# module->file location mapper to speed FS-based imports +# relative imports +# keep chaining so that it can play nice with other import hooks +# +# from Gordon: +# push MAL's mapper into sys.path[0] as a cache (hard-coded for apps) +# +# from Guido: +# need to change sys.* references for rexec environs +# need hook for MAL's walk-me-up import strategy, or Tim's absolute strategy +# watch out for sys.modules[...] is None +# flag to force absolute imports? (speeds _determine_import_context and +# checking for a relative module) +# insert names of archives into sys.path (see quote below) +# note: reload does NOT blast module dict +# shift import mechanisms and policies around; provide for hooks, overrides +# (see quote below) +# add get_source stuff +# get_topcode and get_subcode +# CRLF handling in _compile +# race condition in _compile +# refactoring of os.py to deal with _os_bootstrap problem +# any special handling to do for importing a module with a SyntaxError? +# (e.g. clean up the traceback) +# implement "domain" for path-type functionality using pkg namespace +# (rather than FS-names like __path__) +# don't use the word "private"... maybe "internal" +# +# +# Guido's comments on sys.path caching: +# +# We could cache this in a dictionary: the ImportManager can have a +# cache dict mapping pathnames to importer objects, and a separate +# method for coming up with an importer given a pathname that's not yet +# in the cache. The method should do a stat and/or look at the +# extension to decide which importer class to use; you can register new +# importer classes by registering a suffix or a Boolean function, plus a +# class. If you register a new importer class, the cache is zapped. +# The cache is independent from sys.path (but maintained per +# ImportManager instance) so that rearrangements of sys.path do the +# right thing. If a path is dropped from sys.path the corresponding +# cache entry is simply no longer used. +# +# My/Guido's comments on factoring ImportManager and Importer: +# +# > However, we still have a tension occurring here: +# > +# > 1) implementing policy in ImportManager assists in single-point policy +# > changes for app/rexec situations +# > 2) implementing policy in Importer assists in package-private policy +# > changes for normal, operating conditions +# > +# > I'll see if I can sort out a way to do this. Maybe the Importer class will +# > implement the methods (which can be overridden to change policy) by +# > delegating to ImportManager. +# +# Maybe also think about what kind of policies an Importer would be +# likely to want to change. I have a feeling that a lot of the code +# there is actually not so much policy but a *necessity* to get things +# working given the calling conventions for the __import__ hook: whether +# to return the head or tail of a dotted name, or when to do the "finish +# fromlist" stuff. +# diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/inspect.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/inspect.py new file mode 100644 index 00000000..f7ddd144 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/inspect.py @@ -0,0 +1,779 @@ +"""Get useful information from live Python objects. + +This module encapsulates the interface provided by the internal special +attributes (func_*, co_*, im_*, tb_*, etc.) in a friendlier fashion. +It also provides some help for examining source code and class layout. + +Here are some of the useful functions provided by this module: + + ismodule(), isclass(), ismethod(), isfunction(), istraceback(), + isframe(), iscode(), isbuiltin(), isroutine() - check object types + getmembers() - get members of an object that satisfy a given condition + + getfile(), getsourcefile(), getsource() - find an object's source code + getdoc(), getcomments() - get documentation on an object + getmodule() - determine the module that an object came from + getclasstree() - arrange classes so as to represent their hierarchy + + getargspec(), getargvalues() - get info about function arguments + formatargspec(), formatargvalues() - format an argument spec + getouterframes(), getinnerframes() - get info about frames + currentframe() - get the current stack frame + stack(), trace() - get info about frames on the stack or in a traceback +""" + +# This module is in the public domain. No warranties. + +__author__ = 'Ka-Ping Yee ' +__date__ = '1 Jan 2001' + +import sys, os, types, string, re, dis, imp, tokenize + +# ----------------------------------------------------------- type-checking +def ismodule(object): + """Return true if the object is a module. + + Module objects provide these attributes: + __doc__ documentation string + __file__ filename (missing for built-in modules)""" + return isinstance(object, types.ModuleType) + +def isclass(object): + """Return true if the object is a class. + + Class objects provide these attributes: + __doc__ documentation string + __module__ name of module in which this class was defined""" + return isinstance(object, types.ClassType) or hasattr(object, '__bases__') + +def ismethod(object): + """Return true if the object is an instance method. + + Instance method objects provide these attributes: + __doc__ documentation string + __name__ name with which this method was defined + im_class class object in which this method belongs + im_func function object containing implementation of method + im_self instance to which this method is bound, or None""" + return isinstance(object, types.MethodType) + +def ismethoddescriptor(object): + """Return true if the object is a method descriptor. + + But not if ismethod() or isclass() or isfunction() are true. + + This is new in Python 2.2, and, for example, is true of int.__add__. + An object passing this test has a __get__ attribute but not a __set__ + attribute, but beyond that the set of attributes varies. __name__ is + usually sensible, and __doc__ often is. + + Methods implemented via descriptors that also pass one of the other + tests return false from the ismethoddescriptor() test, simply because + the other tests promise more -- you can, e.g., count on having the + im_func attribute (etc) when an object passes ismethod().""" + return (hasattr(object, "__get__") + and not hasattr(object, "__set__") # else it's a data descriptor + and not ismethod(object) # mutual exclusion + and not isfunction(object) + and not isclass(object)) + +def isfunction(object): + """Return true if the object is a user-defined function. + + Function objects provide these attributes: + __doc__ documentation string + __name__ name with which this function was defined + func_code code object containing compiled function bytecode + func_defaults tuple of any default values for arguments + func_doc (same as __doc__) + func_globals global namespace in which this function was defined + func_name (same as __name__)""" + return isinstance(object, types.FunctionType) + +def istraceback(object): + """Return true if the object is a traceback. + + Traceback objects provide these attributes: + tb_frame frame object at this level + tb_lasti index of last attempted instruction in bytecode + tb_lineno current line number in Python source code + tb_next next inner traceback object (called by this level)""" + return isinstance(object, types.TracebackType) + +def isframe(object): + """Return true if the object is a frame object. + + Frame objects provide these attributes: + f_back next outer frame object (this frame's caller) + f_builtins built-in namespace seen by this frame + f_code code object being executed in this frame + f_exc_traceback traceback if raised in this frame, or None + f_exc_type exception type if raised in this frame, or None + f_exc_value exception value if raised in this frame, or None + f_globals global namespace seen by this frame + f_lasti index of last attempted instruction in bytecode + f_lineno current line number in Python source code + f_locals local namespace seen by this frame + f_restricted 0 or 1 if frame is in restricted execution mode + f_trace tracing function for this frame, or None""" + return isinstance(object, types.FrameType) + +def iscode(object): + """Return true if the object is a code object. + + Code objects provide these attributes: + co_argcount number of arguments (not including * or ** args) + co_code string of raw compiled bytecode + co_consts tuple of constants used in the bytecode + co_filename name of file in which this code object was created + co_firstlineno number of first line in Python source code + co_flags bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg + co_lnotab encoded mapping of line numbers to bytecode indices + co_name name with which this code object was defined + co_names tuple of names of local variables + co_nlocals number of local variables + co_stacksize virtual machine stack space required + co_varnames tuple of names of arguments and local variables""" + return isinstance(object, types.CodeType) + +def isbuiltin(object): + """Return true if the object is a built-in function or method. + + Built-in functions and methods provide these attributes: + __doc__ documentation string + __name__ original name of this function or method + __self__ instance to which a method is bound, or None""" + return isinstance(object, types.BuiltinFunctionType) + +def isroutine(object): + """Return true if the object is any kind of function or method.""" + return (isbuiltin(object) + or isfunction(object) + or ismethod(object) + or ismethoddescriptor(object)) + +def getmembers(object, predicate=None): + """Return all members of an object as (name, value) pairs sorted by name. + Optionally, only return members that satisfy a given predicate.""" + results = [] + for key in dir(object): + value = getattr(object, key) + if not predicate or predicate(value): + results.append((key, value)) + results.sort() + return results + +def classify_class_attrs(cls): + """Return list of attribute-descriptor tuples. + + For each name in dir(cls), the return list contains a 4-tuple + with these elements: + + 0. The name (a string). + + 1. The kind of attribute this is, one of these strings: + 'class method' created via classmethod() + 'static method' created via staticmethod() + 'property' created via property() + 'method' any other flavor of method + 'data' not a method + + 2. The class which defined this attribute (a class). + + 3. The object as obtained directly from the defining class's + __dict__, not via getattr. This is especially important for + data attributes: C.data is just a data object, but + C.__dict__['data'] may be a data descriptor with additional + info, like a __doc__ string. + """ + + mro = getmro(cls) + names = dir(cls) + result = [] + for name in names: + # Get the object associated with the name. + # Getting an obj from the __dict__ sometimes reveals more than + # using getattr. Static and class methods are dramatic examples. + if name in cls.__dict__: + obj = cls.__dict__[name] + else: + obj = getattr(cls, name) + + # Figure out where it was defined. + homecls = getattr(obj, "__objclass__", None) + if homecls is None: + # search the dicts. + for base in mro: + if name in base.__dict__: + homecls = base + break + + # Get the object again, in order to get it from the defining + # __dict__ instead of via getattr (if possible). + if homecls is not None and name in homecls.__dict__: + obj = homecls.__dict__[name] + + # Also get the object via getattr. + obj_via_getattr = getattr(cls, name) + + # Classify the object. + if isinstance(obj, staticmethod): + kind = "static method" + elif isinstance(obj, classmethod): + kind = "class method" + elif isinstance(obj, property): + kind = "property" + elif (ismethod(obj_via_getattr) or + ismethoddescriptor(obj_via_getattr)): + kind = "method" + else: + kind = "data" + + result.append((name, kind, homecls, obj)) + + return result + +# ----------------------------------------------------------- class helpers +def _searchbases(cls, accum): + # Simulate the "classic class" search order. + if cls in accum: + return + accum.append(cls) + for base in cls.__bases__: + _searchbases(base, accum) + +def getmro(cls): + "Return tuple of base classes (including cls) in method resolution order." + if hasattr(cls, "__mro__"): + return cls.__mro__ + else: + result = [] + _searchbases(cls, result) + return tuple(result) + +# -------------------------------------------------- source code extraction +def indentsize(line): + """Return the indent size, in spaces, at the start of a line of text.""" + expline = string.expandtabs(line) + return len(expline) - len(string.lstrip(expline)) + +def getdoc(object): + """Get the documentation string for an object. + + All tabs are expanded to spaces. To clean up docstrings that are + indented to line up with blocks of code, any whitespace than can be + uniformly removed from the second line onwards is removed.""" + try: + doc = object.__doc__ + except AttributeError: + return None + if not isinstance(doc, (str, unicode)): + return None + try: + lines = string.split(string.expandtabs(doc), '\n') + except UnicodeError: + return None + else: + margin = None + for line in lines[1:]: + content = len(string.lstrip(line)) + if not content: continue + indent = len(line) - content + if margin is None: margin = indent + else: margin = min(margin, indent) + if margin is not None: + for i in range(1, len(lines)): lines[i] = lines[i][margin:] + return string.join(lines, '\n') + +def getfile(object): + """Work out which source or compiled file an object was defined in.""" + if ismodule(object): + if hasattr(object, '__file__'): + return object.__file__ + raise TypeError, 'arg is a built-in module' + if isclass(object): + object = sys.modules.get(object.__module__) + if hasattr(object, '__file__'): + return object.__file__ + raise TypeError, 'arg is a built-in class' + if ismethod(object): + object = object.im_func + if isfunction(object): + object = object.func_code + if istraceback(object): + object = object.tb_frame + if isframe(object): + object = object.f_code + if iscode(object): + return object.co_filename + raise TypeError, 'arg is not a module, class, method, ' \ + 'function, traceback, frame, or code object' + +def getmoduleinfo(path): + """Get the module name, suffix, mode, and module type for a given file.""" + filename = os.path.basename(path) + suffixes = map(lambda (suffix, mode, mtype): + (-len(suffix), suffix, mode, mtype), imp.get_suffixes()) + suffixes.sort() # try longest suffixes first, in case they overlap + for neglen, suffix, mode, mtype in suffixes: + if filename[neglen:] == suffix: + return filename[:neglen], suffix, mode, mtype + +def getmodulename(path): + """Return the module name for a given file, or None.""" + info = getmoduleinfo(path) + if info: return info[0] + +def getsourcefile(object): + """Return the Python source file an object was defined in, if it exists.""" + filename = getfile(object) + if string.lower(filename[-4:]) in ['.pyc', '.pyo']: + filename = filename[:-4] + '.py' + for suffix, mode, kind in imp.get_suffixes(): + if 'b' in mode and string.lower(filename[-len(suffix):]) == suffix: + # Looks like a binary file. We want to only return a text file. + return None + if os.path.exists(filename): + return filename + +def getabsfile(object): + """Return an absolute path to the source or compiled file for an object. + + The idea is for each object to have a unique origin, so this routine + normalizes the result as much as possible.""" + return os.path.normcase( + os.path.abspath(getsourcefile(object) or getfile(object))) + +modulesbyfile = {} + +def getmodule(object): + """Return the module an object was defined in, or None if not found.""" + if ismodule(object): + return object + if isclass(object): + return sys.modules.get(object.__module__) + try: + file = getabsfile(object) + except TypeError: + return None + if modulesbyfile.has_key(file): + return sys.modules[modulesbyfile[file]] + for module in sys.modules.values(): + if hasattr(module, '__file__'): + modulesbyfile[getabsfile(module)] = module.__name__ + if modulesbyfile.has_key(file): + return sys.modules[modulesbyfile[file]] + main = sys.modules['__main__'] + if hasattr(main, object.__name__): + mainobject = getattr(main, object.__name__) + if mainobject is object: + return main + builtin = sys.modules['__builtin__'] + if hasattr(builtin, object.__name__): + builtinobject = getattr(builtin, object.__name__) + if builtinobject is object: + return builtin + +def findsource(object): + """Return the entire source file and starting line number for an object. + + The argument may be a module, class, method, function, traceback, frame, + or code object. The source code is returned as a list of all the lines + in the file and the line number indexes a line in that list. An IOError + is raised if the source code cannot be retrieved.""" + try: + file = open(getsourcefile(object)) + except (TypeError, IOError): + raise IOError, 'could not get source code' + lines = file.readlines() + file.close() + + if ismodule(object): + return lines, 0 + + if isclass(object): + name = object.__name__ + pat = re.compile(r'^\s*class\s*' + name + r'\b') + for i in range(len(lines)): + if pat.match(lines[i]): return lines, i + else: raise IOError, 'could not find class definition' + + if ismethod(object): + object = object.im_func + if isfunction(object): + object = object.func_code + if istraceback(object): + object = object.tb_frame + if isframe(object): + object = object.f_code + if iscode(object): + if not hasattr(object, 'co_firstlineno'): + raise IOError, 'could not find function definition' + lnum = object.co_firstlineno - 1 + pat = re.compile(r'^\s*def\s') + while lnum > 0: + if pat.match(lines[lnum]): break + lnum = lnum - 1 + return lines, lnum + raise IOError, 'could not find code object' + +def getcomments(object): + """Get lines of comments immediately preceding an object's source code.""" + try: lines, lnum = findsource(object) + except IOError: return None + + if ismodule(object): + # Look for a comment block at the top of the file. + start = 0 + if lines and lines[0][:2] == '#!': start = 1 + while start < len(lines) and string.strip(lines[start]) in ['', '#']: + start = start + 1 + if start < len(lines) and lines[start][:1] == '#': + comments = [] + end = start + while end < len(lines) and lines[end][:1] == '#': + comments.append(string.expandtabs(lines[end])) + end = end + 1 + return string.join(comments, '') + + # Look for a preceding block of comments at the same indentation. + elif lnum > 0: + indent = indentsize(lines[lnum]) + end = lnum - 1 + if end >= 0 and string.lstrip(lines[end])[:1] == '#' and \ + indentsize(lines[end]) == indent: + comments = [string.lstrip(string.expandtabs(lines[end]))] + if end > 0: + end = end - 1 + comment = string.lstrip(string.expandtabs(lines[end])) + while comment[:1] == '#' and indentsize(lines[end]) == indent: + comments[:0] = [comment] + end = end - 1 + if end < 0: break + comment = string.lstrip(string.expandtabs(lines[end])) + while comments and string.strip(comments[0]) == '#': + comments[:1] = [] + while comments and string.strip(comments[-1]) == '#': + comments[-1:] = [] + return string.join(comments, '') + +class ListReader: + """Provide a readline() method to return lines from a list of strings.""" + def __init__(self, lines): + self.lines = lines + self.index = 0 + + def readline(self): + i = self.index + if i < len(self.lines): + self.index = i + 1 + return self.lines[i] + else: return '' + +class EndOfBlock(Exception): pass + +class BlockFinder: + """Provide a tokeneater() method to detect the end of a code block.""" + def __init__(self): + self.indent = 0 + self.started = 0 + self.last = 0 + + def tokeneater(self, type, token, (srow, scol), (erow, ecol), line): + if not self.started: + if type == tokenize.NAME: self.started = 1 + elif type == tokenize.NEWLINE: + self.last = srow + elif type == tokenize.INDENT: + self.indent = self.indent + 1 + elif type == tokenize.DEDENT: + self.indent = self.indent - 1 + if self.indent == 0: raise EndOfBlock, self.last + +def getblock(lines): + """Extract the block of code at the top of the given list of lines.""" + try: + tokenize.tokenize(ListReader(lines).readline, BlockFinder().tokeneater) + except EndOfBlock, eob: + return lines[:eob.args[0]] + +def getsourcelines(object): + """Return a list of source lines and starting line number for an object. + + The argument may be a module, class, method, function, traceback, frame, + or code object. The source code is returned as a list of the lines + corresponding to the object and the line number indicates where in the + original source file the first line of code was found. An IOError is + raised if the source code cannot be retrieved.""" + lines, lnum = findsource(object) + + if ismodule(object): return lines, 0 + else: return getblock(lines[lnum:]), lnum + 1 + +def getsource(object): + """Return the text of the source code for an object. + + The argument may be a module, class, method, function, traceback, frame, + or code object. The source code is returned as a single string. An + IOError is raised if the source code cannot be retrieved.""" + lines, lnum = getsourcelines(object) + return string.join(lines, '') + +# --------------------------------------------------- class tree extraction +def walktree(classes, children, parent): + """Recursive helper function for getclasstree().""" + results = [] + classes.sort(lambda a, b: cmp(a.__name__, b.__name__)) + for c in classes: + results.append((c, c.__bases__)) + if children.has_key(c): + results.append(walktree(children[c], children, c)) + return results + +def getclasstree(classes, unique=0): + """Arrange the given list of classes into a hierarchy of nested lists. + + Where a nested list appears, it contains classes derived from the class + whose entry immediately precedes the list. Each entry is a 2-tuple + containing a class and a tuple of its base classes. If the 'unique' + argument is true, exactly one entry appears in the returned structure + for each class in the given list. Otherwise, classes using multiple + inheritance and their descendants will appear multiple times.""" + children = {} + roots = [] + for c in classes: + if c.__bases__: + for parent in c.__bases__: + if not children.has_key(parent): + children[parent] = [] + children[parent].append(c) + if unique and parent in classes: break + elif c not in roots: + roots.append(c) + for parent in children.keys(): + if parent not in classes: + roots.append(parent) + return walktree(roots, children, None) + +# ------------------------------------------------ argument list extraction +# These constants are from Python's compile.h. +CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 1, 2, 4, 8 + +def getargs(co): + """Get information about the arguments accepted by a code object. + + Three things are returned: (args, varargs, varkw), where 'args' is + a list of argument names (possibly containing nested lists), and + 'varargs' and 'varkw' are the names of the * and ** arguments or None.""" + if not iscode(co): raise TypeError, 'arg is not a code object' + + code = co.co_code + nargs = co.co_argcount + names = co.co_varnames + args = list(names[:nargs]) + step = 0 + + # The following acrobatics are for anonymous (tuple) arguments. + for i in range(nargs): + if args[i][:1] in ['', '.']: + stack, remain, count = [], [], [] + while step < len(code): + op = ord(code[step]) + step = step + 1 + if op >= dis.HAVE_ARGUMENT: + opname = dis.opname[op] + value = ord(code[step]) + ord(code[step+1])*256 + step = step + 2 + if opname in ['UNPACK_TUPLE', 'UNPACK_SEQUENCE']: + remain.append(value) + count.append(value) + elif opname == 'STORE_FAST': + stack.append(names[value]) + remain[-1] = remain[-1] - 1 + while remain[-1] == 0: + remain.pop() + size = count.pop() + stack[-size:] = [stack[-size:]] + if not remain: break + remain[-1] = remain[-1] - 1 + if not remain: break + args[i] = stack[0] + + varargs = None + if co.co_flags & CO_VARARGS: + varargs = co.co_varnames[nargs] + nargs = nargs + 1 + varkw = None + if co.co_flags & CO_VARKEYWORDS: + varkw = co.co_varnames[nargs] + return args, varargs, varkw + +def getargspec(func): + """Get the names and default values of a function's arguments. + + A tuple of four things is returned: (args, varargs, varkw, defaults). + 'args' is a list of the argument names (it may contain nested lists). + 'varargs' and 'varkw' are the names of the * and ** arguments or None. + 'defaults' is an n-tuple of the default values of the last n arguments.""" + if not isfunction(func): raise TypeError, 'arg is not a Python function' + args, varargs, varkw = getargs(func.func_code) + return args, varargs, varkw, func.func_defaults + +def getargvalues(frame): + """Get information about arguments passed into a particular frame. + + A tuple of four things is returned: (args, varargs, varkw, locals). + 'args' is a list of the argument names (it may contain nested lists). + 'varargs' and 'varkw' are the names of the * and ** arguments or None. + 'locals' is the locals dictionary of the given frame.""" + args, varargs, varkw = getargs(frame.f_code) + return args, varargs, varkw, frame.f_locals + +def joinseq(seq): + if len(seq) == 1: + return '(' + seq[0] + ',)' + else: + return '(' + string.join(seq, ', ') + ')' + +def strseq(object, convert, join=joinseq): + """Recursively walk a sequence, stringifying each element.""" + if type(object) in [types.ListType, types.TupleType]: + return join(map(lambda o, c=convert, j=join: strseq(o, c, j), object)) + else: + return convert(object) + +def formatargspec(args, varargs=None, varkw=None, defaults=None, + formatarg=str, + formatvarargs=lambda name: '*' + name, + formatvarkw=lambda name: '**' + name, + formatvalue=lambda value: '=' + repr(value), + join=joinseq): + """Format an argument spec from the 4 values returned by getargspec. + + The first four arguments are (args, varargs, varkw, defaults). The + other four arguments are the corresponding optional formatting functions + that are called to turn names and values into strings. The ninth + argument is an optional function to format the sequence of arguments.""" + specs = [] + if defaults: + firstdefault = len(args) - len(defaults) + for i in range(len(args)): + spec = strseq(args[i], formatarg, join) + if defaults and i >= firstdefault: + spec = spec + formatvalue(defaults[i - firstdefault]) + specs.append(spec) + if varargs: + specs.append(formatvarargs(varargs)) + if varkw: + specs.append(formatvarkw(varkw)) + return '(' + string.join(specs, ', ') + ')' + +def formatargvalues(args, varargs, varkw, locals, + formatarg=str, + formatvarargs=lambda name: '*' + name, + formatvarkw=lambda name: '**' + name, + formatvalue=lambda value: '=' + repr(value), + join=joinseq): + """Format an argument spec from the 4 values returned by getargvalues. + + The first four arguments are (args, varargs, varkw, locals). The + next four arguments are the corresponding optional formatting functions + that are called to turn names and values into strings. The ninth + argument is an optional function to format the sequence of arguments.""" + def convert(name, locals=locals, + formatarg=formatarg, formatvalue=formatvalue): + return formatarg(name) + formatvalue(locals[name]) + specs = [] + for i in range(len(args)): + specs.append(strseq(args[i], convert, join)) + if varargs: + specs.append(formatvarargs(varargs) + formatvalue(locals[varargs])) + if varkw: + specs.append(formatvarkw(varkw) + formatvalue(locals[varkw])) + return '(' + string.join(specs, ', ') + ')' + +# -------------------------------------------------- stack frame extraction +def getframeinfo(frame, context=1): + """Get information about a frame or traceback object. + + A tuple of five things is returned: the filename, the line number of + the current line, the function name, a list of lines of context from + the source code, and the index of the current line within that list. + The optional second argument specifies the number of lines of context + to return, which are centered around the current line.""" + if istraceback(frame): + frame = frame.tb_frame + if not isframe(frame): + raise TypeError, 'arg is not a frame or traceback object' + + filename = getsourcefile(frame) + lineno = getlineno(frame) + if context > 0: + start = lineno - 1 - context//2 + try: + lines, lnum = findsource(frame) + except IOError: + lines = index = None + else: + start = max(start, 1) + start = min(start, len(lines) - context) + lines = lines[start:start+context] + index = lineno - 1 - start + else: + lines = index = None + + return (filename, lineno, frame.f_code.co_name, lines, index) + +def getlineno(frame): + """Get the line number from a frame object, allowing for optimization.""" + # Written by Marc-André Lemburg; revised by Jim Hugunin and Fredrik Lundh. + lineno = frame.f_lineno + code = frame.f_code + if hasattr(code, 'co_lnotab'): + table = code.co_lnotab + lineno = code.co_firstlineno + addr = 0 + for i in range(0, len(table), 2): + addr = addr + ord(table[i]) + if addr > frame.f_lasti: break + lineno = lineno + ord(table[i+1]) + return lineno + +def getouterframes(frame, context=1): + """Get a list of records for a frame and all higher (calling) frames. + + Each record contains a frame object, filename, line number, function + name, a list of lines of context, and index within the context.""" + framelist = [] + while frame: + framelist.append((frame,) + getframeinfo(frame, context)) + frame = frame.f_back + return framelist + +def getinnerframes(tb, context=1): + """Get a list of records for a traceback's frame and all lower frames. + + Each record contains a frame object, filename, line number, function + name, a list of lines of context, and index within the context.""" + framelist = [] + while tb: + framelist.append((tb.tb_frame,) + getframeinfo(tb, context)) + tb = tb.tb_next + return framelist + +def currentframe(): + """Return the frame object for the caller's stack frame.""" + try: + raise 'catch me' + except: + return sys.exc_traceback.tb_frame.f_back + +if hasattr(sys, '_getframe'): currentframe = sys._getframe + +def stack(context=1): + """Return a list of records for the stack above the caller's frame.""" + return getouterframes(currentframe().f_back, context) + +def trace(context=1): + """Return a list of records for the stack below the current exception.""" + return getinnerframes(sys.exc_traceback, context) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/keyword.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/keyword.py new file mode 100644 index 00000000..54b68a79 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/keyword.py @@ -0,0 +1,97 @@ +#! /usr/bin/env python + +"""Keywords (from "graminit.c") + +This file is automatically generated; please don't muck it up! + +To update the symbols in this file, 'cd' to the top directory of +the python source tree after building the interpreter and run: + + python Lib/keyword.py +""" + +__all__ = ["iskeyword"] + +kwlist = [ +#--start keywords-- + 'and', + 'assert', + 'break', + 'class', + 'continue', + 'def', + 'del', + 'elif', + 'else', + 'except', + 'exec', + 'finally', + 'for', + 'from', + 'global', + 'if', + 'import', + 'in', + 'is', + 'lambda', + 'not', + 'or', + 'pass', + 'print', + 'raise', + 'return', + 'try', + 'while', + 'yield', +#--end keywords-- + ] + +kwdict = {} +for keyword in kwlist: + kwdict[keyword] = 1 + +iskeyword = kwdict.has_key + +def main(): + import sys, re + + args = sys.argv[1:] + iptfile = args and args[0] or "Python/graminit.c" + if len(args) > 1: optfile = args[1] + else: optfile = "Lib/keyword.py" + + # scan the source file for keywords + fp = open(iptfile) + strprog = re.compile('"([^"]+)"') + lines = [] + while 1: + line = fp.readline() + if not line: break + if line.find('{1, "') > -1: + match = strprog.search(line) + if match: + lines.append(" '" + match.group(1) + "',\n") + fp.close() + lines.sort() + + # load the output skeleton from the target + fp = open(optfile) + format = fp.readlines() + fp.close() + + # insert the lines of keywords + try: + start = format.index("#--start keywords--\n") + 1 + end = format.index("#--end keywords--\n") + format[start:end] = lines + except ValueError: + sys.stderr.write("target does not contain format markers\n") + sys.exit(1) + + # write the output file + fp = open(optfile, 'w') + fp.write(''.join(format)) + fp.close() + +if __name__ == "__main__": + main() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/knee.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/knee.py new file mode 100644 index 00000000..63dd2446 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/knee.py @@ -0,0 +1,126 @@ +"""An Python re-implementation of hierarchical module import. + +This code is intended to be read, not executed. However, it does work +-- all you need to do to enable it is "import knee". + +(The name is a pun on the klunkier predecessor of this module, "ni".) + +""" + +import sys, imp, __builtin__ + + +# Replacement for __import__() +def import_hook(name, globals=None, locals=None, fromlist=None): + parent = determine_parent(globals) + q, tail = find_head_package(parent, name) + m = load_tail(q, tail) + if not fromlist: + return q + if hasattr(m, "__path__"): + ensure_fromlist(m, fromlist) + return m + +def determine_parent(globals): + if not globals or not globals.has_key("__name__"): + return None + pname = globals['__name__'] + if globals.has_key("__path__"): + parent = sys.modules[pname] + assert globals is parent.__dict__ + return parent + if '.' in pname: + i = pname.rfind('.') + pname = pname[:i] + parent = sys.modules[pname] + assert parent.__name__ == pname + return parent + return None + +def find_head_package(parent, name): + if '.' in name: + i = name.find('.') + head = name[:i] + tail = name[i+1:] + else: + head = name + tail = "" + if parent: + qname = "%s.%s" % (parent.__name__, head) + else: + qname = head + q = import_module(head, qname, parent) + if q: return q, tail + if parent: + qname = head + parent = None + q = import_module(head, qname, parent) + if q: return q, tail + raise ImportError, "No module named " + qname + +def load_tail(q, tail): + m = q + while tail: + i = tail.find('.') + if i < 0: i = len(tail) + head, tail = tail[:i], tail[i+1:] + mname = "%s.%s" % (m.__name__, head) + m = import_module(head, mname, m) + if not m: + raise ImportError, "No module named " + mname + return m + +def ensure_fromlist(m, fromlist, recursive=0): + for sub in fromlist: + if sub == "*": + if not recursive: + try: + all = m.__all__ + except AttributeError: + pass + else: + ensure_fromlist(m, all, 1) + continue + if sub != "*" and not hasattr(m, sub): + subname = "%s.%s" % (m.__name__, sub) + submod = import_module(sub, subname, m) + if not submod: + raise ImportError, "No module named " + subname + +def import_module(partname, fqname, parent): + try: + return sys.modules[fqname] + except KeyError: + pass + try: + fp, pathname, stuff = imp.find_module(partname, + parent and parent.__path__) + except ImportError: + return None + try: + m = imp.load_module(fqname, fp, pathname, stuff) + finally: + if fp: fp.close() + if parent: + setattr(parent, partname, m) + return m + + +# Replacement for reload() +def reload_hook(module): + name = module.__name__ + if '.' not in name: + return import_module(name, name, None) + i = name.rfind('.') + pname = name[:i] + parent = sys.modules[pname] + return import_module(name[i+1:], name, parent) + + +# Save the original hooks +original_import = __builtin__.__import__ +original_reload = __builtin__.reload + +# Now install our hooks +__builtin__.__import__ = import_hook +__builtin__.reload = reload_hook diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/linecache.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/linecache.py new file mode 100644 index 00000000..0be8320f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/linecache.py @@ -0,0 +1,101 @@ +"""Cache lines from files. + +This is intended to read lines from modules imported -- hence if a filename +is not found, it will look down the module search path for a file by +that name. +""" + +import sys +import os +from stat import * + +__all__ = ["getline","clearcache","checkcache"] + +def getline(filename, lineno): + lines = getlines(filename) + if 1 <= lineno <= len(lines): + return lines[lineno-1] + else: + return '' + + +# The cache + +cache = {} # The cache + + +def clearcache(): + """Clear the cache entirely.""" + + global cache + cache = {} + + +def getlines(filename): + """Get the lines for a file from the cache. + Update the cache if it doesn't contain an entry for this file already.""" + + if cache.has_key(filename): + return cache[filename][2] + else: + return updatecache(filename) + + +def checkcache(): + """Discard cache entries that are out of date. + (This is not checked upon each call!)""" + + for filename in cache.keys(): + size, mtime, lines, fullname = cache[filename] + try: + stat = os.stat(fullname) + except os.error: + del cache[filename] + continue + if size != stat[ST_SIZE] or mtime != stat[ST_MTIME]: + del cache[filename] + + +def updatecache(filename): + """Update a cache entry and return its list of lines. + If something's wrong, print a message, discard the cache entry, + and return an empty list.""" + + if cache.has_key(filename): + del cache[filename] + if not filename or filename[0] + filename[-1] == '<>': + return [] + fullname = filename + try: + stat = os.stat(fullname) + except os.error, msg: + # Try looking through the module search path. + basename = os.path.split(filename)[1] + for dirname in sys.path: + # When using imputil, sys.path may contain things other than + # strings; ignore them when it happens. + try: + fullname = os.path.join(dirname, basename) + except (TypeError, AttributeError): + # Not sufficiently string-like to do anything useful with. + pass + else: + try: + stat = os.stat(fullname) + break + except os.error: + pass + else: + # No luck +## print '*** Cannot stat', filename, ':', msg + return [] + try: + fp = open(fullname, 'r') + lines = fp.readlines() + fp.close() + except IOError, msg: +## print '*** Cannot open', fullname, ':', msg + return [] + size, mtime = stat[ST_SIZE], stat[ST_MTIME] + cache[filename] = size, mtime, lines, fullname + return lines diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/locale.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/locale.py new file mode 100644 index 00000000..d077372c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/locale.py @@ -0,0 +1,734 @@ +""" Locale support. + + The module provides low-level access to the C lib's locale APIs + and adds high level number formatting APIs as well as a locale + aliasing engine to complement these. + + The aliasing engine includes support for many commonly used locale + names and maps them to values suitable for passing to the C lib's + setlocale() function. It also includes default encodings for all + supported locale names. + +""" + +import sys + +# Try importing the _locale module. +# +# If this fails, fall back on a basic 'C' locale emulation. + +# Yuck: LC_MESSAGES is non-standard: can't tell whether it exists before +# trying the import. So __all__ is also fiddled at the end of the file. +__all__ = ["setlocale","Error","localeconv","strcoll","strxfrm", + "format","str","atof","atoi","LC_CTYPE","LC_COLLATE", + "LC_TIME","LC_MONETARY","LC_NUMERIC", "LC_ALL","CHAR_MAX"] + +try: + + from _locale import * + +except ImportError: + + # Locale emulation + + CHAR_MAX = 127 + LC_ALL = 6 + LC_COLLATE = 3 + LC_CTYPE = 0 + LC_MESSAGES = 5 + LC_MONETARY = 4 + LC_NUMERIC = 1 + LC_TIME = 2 + Error = ValueError + + def localeconv(): + """ localeconv() -> dict. + Returns numeric and monetary locale-specific parameters. + """ + # 'C' locale default values + return {'grouping': [127], + 'currency_symbol': '', + 'n_sign_posn': 127, + 'p_cs_precedes': 127, + 'n_cs_precedes': 127, + 'mon_grouping': [], + 'n_sep_by_space': 127, + 'decimal_point': '.', + 'negative_sign': '', + 'positive_sign': '', + 'p_sep_by_space': 127, + 'int_curr_symbol': '', + 'p_sign_posn': 127, + 'thousands_sep': '', + 'mon_thousands_sep': '', + 'frac_digits': 127, + 'mon_decimal_point': '', + 'int_frac_digits': 127} + + def setlocale(category, value=None): + """ setlocale(integer,string=None) -> string. + Activates/queries locale processing. + """ + if value is not None and value != 'C': + raise Error, '_locale emulation only supports "C" locale' + return 'C' + + def strcoll(a,b): + """ strcoll(string,string) -> int. + Compares two strings according to the locale. + """ + return cmp(a,b) + + def strxfrm(s): + """ strxfrm(string) -> string. + Returns a string that behaves for cmp locale-aware. + """ + return s + +### Number formatting APIs + +# Author: Martin von Loewis + +#perform the grouping from right to left +def _group(s): + conv=localeconv() + grouping=conv['grouping'] + if not grouping:return (s, 0) + result="" + seps = 0 + spaces = "" + if s[-1] == ' ': + sp = s.find(' ') + spaces = s[sp:] + s = s[:sp] + while s and grouping: + # if grouping is -1, we are done + if grouping[0]==CHAR_MAX: + break + # 0: re-use last group ad infinitum + elif grouping[0]!=0: + #process last group + group=grouping[0] + grouping=grouping[1:] + if result: + result=s[-group:]+conv['thousands_sep']+result + seps += 1 + else: + result=s[-group:] + s=s[:-group] + if s and s[-1] not in "0123456789": + # the leading string is only spaces and signs + return s+result+spaces,seps + if not result: + return s+spaces,seps + if s: + result=s+conv['thousands_sep']+result + seps += 1 + return result+spaces,seps + +def format(f,val,grouping=0): + """Formats a value in the same way that the % formatting would use, + but takes the current locale into account. + Grouping is applied if the third parameter is true.""" + result = f % val + fields = result.split(".") + seps = 0 + if grouping: + fields[0],seps=_group(fields[0]) + if len(fields)==2: + result = fields[0]+localeconv()['decimal_point']+fields[1] + elif len(fields)==1: + result = fields[0] + else: + raise Error, "Too many decimal points in result string" + + while seps: + # If the number was formatted for a specific width, then it + # might have been filled with spaces to the left or right. If + # so, kill as much spaces as there where separators. + # Leading zeroes as fillers are not yet dealt with, as it is + # not clear how they should interact with grouping. + sp = result.find(" ") + if sp==-1:break + result = result[:sp]+result[sp+1:] + seps -= 1 + + return result + +def str(val): + """Convert float to integer, taking the locale into account.""" + return format("%.12g",val) + +def atof(str,func=float): + "Parses a string as a float according to the locale settings." + #First, get rid of the grouping + ts = localeconv()['thousands_sep'] + if ts: + s=str.split(ts) + str="".join(s) + #next, replace the decimal point with a dot + dd = localeconv()['decimal_point'] + if dd: + s=str.split(dd) + str='.'.join(s) + #finally, parse the string + return func(str) + +def atoi(str): + "Converts a string to an integer according to the locale settings." + return atof(str, int) + +def _test(): + setlocale(LC_ALL, "") + #do grouping + s1=format("%d", 123456789,1) + print s1, "is", atoi(s1) + #standard formatting + s1=str(3.14) + print s1, "is", atof(s1) + +### Locale name aliasing engine + +# Author: Marc-Andre Lemburg, mal@lemburg.com +# Various tweaks by Fredrik Lundh + +# store away the low-level version of setlocale (it's +# overridden below) +_setlocale = setlocale + +def normalize(localename): + + """ Returns a normalized locale code for the given locale + name. + + The returned locale code is formatted for use with + setlocale(). + + If normalization fails, the original name is returned + unchanged. + + If the given encoding is not known, the function defaults to + the default encoding for the locale code just like setlocale() + does. + + """ + # Normalize the locale name and extract the encoding + fullname = localename.lower() + if ':' in fullname: + # ':' is sometimes used as encoding delimiter. + fullname = fullname.replace(':', '.') + if '.' in fullname: + langname, encoding = fullname.split('.')[:2] + fullname = langname + '.' + encoding + else: + langname = fullname + encoding = '' + + # First lookup: fullname (possibly with encoding) + code = locale_alias.get(fullname, None) + if code is not None: + return code + + # Second try: langname (without encoding) + code = locale_alias.get(langname, None) + if code is not None: + if '.' in code: + langname, defenc = code.split('.') + else: + langname = code + defenc = '' + if encoding: + encoding = encoding_alias.get(encoding, encoding) + else: + encoding = defenc + if encoding: + return langname + '.' + encoding + else: + return langname + + else: + return localename + +def _parse_localename(localename): + + """ Parses the locale code for localename and returns the + result as tuple (language code, encoding). + + The localename is normalized and passed through the locale + alias engine. A ValueError is raised in case the locale name + cannot be parsed. + + The language code corresponds to RFC 1766. code and encoding + can be None in case the values cannot be determined or are + unknown to this implementation. + + """ + code = normalize(localename) + if '.' in code: + return code.split('.')[:2] + elif code == 'C': + return None, None + raise ValueError, 'unknown locale: %s' % localename + +def _build_localename(localetuple): + + """ Builds a locale code from the given tuple (language code, + encoding). + + No aliasing or normalizing takes place. + + """ + language, encoding = localetuple + if language is None: + language = 'C' + if encoding is None: + return language + else: + return language + '.' + encoding + +def getdefaultlocale(envvars=('LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LANG')): + + """ Tries to determine the default locale settings and returns + them as tuple (language code, encoding). + + According to POSIX, a program which has not called + setlocale(LC_ALL, "") runs using the portable 'C' locale. + Calling setlocale(LC_ALL, "") lets it use the default locale as + defined by the LANG variable. Since we don't want to interfere + with the current locale setting we thus emulate the behavior + in the way described above. + + To maintain compatibility with other platforms, not only the + LANG variable is tested, but a list of variables given as + envvars parameter. The first found to be defined will be + used. envvars defaults to the search path used in GNU gettext; + it must always contain the variable name 'LANG'. + + Except for the code 'C', the language code corresponds to RFC + 1766. code and encoding can be None in case the values cannot + be determined. + + """ + + try: + # check if it's supported by the _locale module + import _locale + code, encoding = _locale._getdefaultlocale() + except (ImportError, AttributeError): + pass + else: + # make sure the code/encoding values are valid + if sys.platform == "win32" and code and code[:2] == "0x": + # map windows language identifier to language name + code = windows_locale.get(int(code, 0)) + # ...add other platform-specific processing here, if + # necessary... + return code, encoding + + # fall back on POSIX behaviour + import os + lookup = os.environ.get + for variable in envvars: + localename = lookup(variable,None) + if localename is not None: + break + else: + localename = 'C' + return _parse_localename(localename) + + +def getlocale(category=LC_CTYPE): + + """ Returns the current setting for the given locale category as + tuple (language code, encoding). + + category may be one of the LC_* value except LC_ALL. It + defaults to LC_CTYPE. + + Except for the code 'C', the language code corresponds to RFC + 1766. code and encoding can be None in case the values cannot + be determined. + + """ + localename = _setlocale(category) + if category == LC_ALL and ';' in localename: + raise TypeError, 'category LC_ALL is not supported' + return _parse_localename(localename) + +def setlocale(category, locale=None): + + """ Set the locale for the given category. The locale can be + a string, a locale tuple (language code, encoding), or None. + + Locale tuples are converted to strings the locale aliasing + engine. Locale strings are passed directly to the C lib. + + category may be given as one of the LC_* values. + + """ + if locale and type(locale) is not type(""): + # convert to string + locale = normalize(_build_localename(locale)) + return _setlocale(category, locale) + +def resetlocale(category=LC_ALL): + + """ Sets the locale for category to the default setting. + + The default setting is determined by calling + getdefaultlocale(). category defaults to LC_ALL. + + """ + _setlocale(category, _build_localename(getdefaultlocale())) + +### Database +# +# The following data was extracted from the locale.alias file which +# comes with X11 and then hand edited removing the explicit encoding +# definitions and adding some more aliases. The file is usually +# available as /usr/lib/X11/locale/locale.alias. +# + +# +# The encoding_alias table maps lowercase encoding alias names to C +# locale encoding names (case-sensitive). +# +encoding_alias = { + '437': 'C', + 'c': 'C', + 'iso8859': 'ISO8859-1', + '8859': 'ISO8859-1', + '88591': 'ISO8859-1', + 'ascii': 'ISO8859-1', + 'en': 'ISO8859-1', + 'iso88591': 'ISO8859-1', + 'iso_8859-1': 'ISO8859-1', + '885915': 'ISO8859-15', + 'iso885915': 'ISO8859-15', + 'iso_8859-15': 'ISO8859-15', + 'iso8859-2': 'ISO8859-2', + 'iso88592': 'ISO8859-2', + 'iso_8859-2': 'ISO8859-2', + 'iso88595': 'ISO8859-5', + 'iso88596': 'ISO8859-6', + 'iso88597': 'ISO8859-7', + 'iso88598': 'ISO8859-8', + 'iso88599': 'ISO8859-9', + 'iso-2022-jp': 'JIS7', + 'jis': 'JIS7', + 'jis7': 'JIS7', + 'sjis': 'SJIS', + 'tis620': 'TACTIS', + 'ajec': 'eucJP', + 'eucjp': 'eucJP', + 'ujis': 'eucJP', + 'utf-8': 'utf', + 'utf8': 'utf', + 'utf8@ucs4': 'utf', +} + +# +# The locale_alias table maps lowercase alias names to C locale names +# (case-sensitive). Encodings are always separated from the locale +# name using a dot ('.'); they should only be given in case the +# language name is needed to interpret the given encoding alias +# correctly (CJK codes often have this need). +# +locale_alias = { + 'american': 'en_US.ISO8859-1', + 'ar': 'ar_AA.ISO8859-6', + 'ar_aa': 'ar_AA.ISO8859-6', + 'ar_sa': 'ar_SA.ISO8859-6', + 'arabic': 'ar_AA.ISO8859-6', + 'bg': 'bg_BG.ISO8859-5', + 'bg_bg': 'bg_BG.ISO8859-5', + 'bulgarian': 'bg_BG.ISO8859-5', + 'c-french': 'fr_CA.ISO8859-1', + 'c': 'C', + 'c_c': 'C', + 'cextend': 'en_US.ISO8859-1', + 'chinese-s': 'zh_CN.eucCN', + 'chinese-t': 'zh_TW.eucTW', + 'croatian': 'hr_HR.ISO8859-2', + 'cs': 'cs_CZ.ISO8859-2', + 'cs_cs': 'cs_CZ.ISO8859-2', + 'cs_cz': 'cs_CZ.ISO8859-2', + 'cz': 'cz_CZ.ISO8859-2', + 'cz_cz': 'cz_CZ.ISO8859-2', + 'czech': 'cs_CS.ISO8859-2', + 'da': 'da_DK.ISO8859-1', + 'da_dk': 'da_DK.ISO8859-1', + 'danish': 'da_DK.ISO8859-1', + 'de': 'de_DE.ISO8859-1', + 'de_at': 'de_AT.ISO8859-1', + 'de_ch': 'de_CH.ISO8859-1', + 'de_de': 'de_DE.ISO8859-1', + 'dutch': 'nl_BE.ISO8859-1', + 'ee': 'ee_EE.ISO8859-4', + 'el': 'el_GR.ISO8859-7', + 'el_gr': 'el_GR.ISO8859-7', + 'en': 'en_US.ISO8859-1', + 'en_au': 'en_AU.ISO8859-1', + 'en_ca': 'en_CA.ISO8859-1', + 'en_gb': 'en_GB.ISO8859-1', + 'en_ie': 'en_IE.ISO8859-1', + 'en_nz': 'en_NZ.ISO8859-1', + 'en_uk': 'en_GB.ISO8859-1', + 'en_us': 'en_US.ISO8859-1', + 'eng_gb': 'en_GB.ISO8859-1', + 'english': 'en_EN.ISO8859-1', + 'english_uk': 'en_GB.ISO8859-1', + 'english_united-states': 'en_US.ISO8859-1', + 'english_us': 'en_US.ISO8859-1', + 'es': 'es_ES.ISO8859-1', + 'es_ar': 'es_AR.ISO8859-1', + 'es_bo': 'es_BO.ISO8859-1', + 'es_cl': 'es_CL.ISO8859-1', + 'es_co': 'es_CO.ISO8859-1', + 'es_cr': 'es_CR.ISO8859-1', + 'es_ec': 'es_EC.ISO8859-1', + 'es_es': 'es_ES.ISO8859-1', + 'es_gt': 'es_GT.ISO8859-1', + 'es_mx': 'es_MX.ISO8859-1', + 'es_ni': 'es_NI.ISO8859-1', + 'es_pa': 'es_PA.ISO8859-1', + 'es_pe': 'es_PE.ISO8859-1', + 'es_py': 'es_PY.ISO8859-1', + 'es_sv': 'es_SV.ISO8859-1', + 'es_uy': 'es_UY.ISO8859-1', + 'es_ve': 'es_VE.ISO8859-1', + 'et': 'et_EE.ISO8859-4', + 'et_ee': 'et_EE.ISO8859-4', + 'fi': 'fi_FI.ISO8859-1', + 'fi_fi': 'fi_FI.ISO8859-1', + 'finnish': 'fi_FI.ISO8859-1', + 'fr': 'fr_FR.ISO8859-1', + 'fr_be': 'fr_BE.ISO8859-1', + 'fr_ca': 'fr_CA.ISO8859-1', + 'fr_ch': 'fr_CH.ISO8859-1', + 'fr_fr': 'fr_FR.ISO8859-1', + 'fre_fr': 'fr_FR.ISO8859-1', + 'french': 'fr_FR.ISO8859-1', + 'french_france': 'fr_FR.ISO8859-1', + 'ger_de': 'de_DE.ISO8859-1', + 'german': 'de_DE.ISO8859-1', + 'german_germany': 'de_DE.ISO8859-1', + 'greek': 'el_GR.ISO8859-7', + 'hebrew': 'iw_IL.ISO8859-8', + 'hr': 'hr_HR.ISO8859-2', + 'hr_hr': 'hr_HR.ISO8859-2', + 'hu': 'hu_HU.ISO8859-2', + 'hu_hu': 'hu_HU.ISO8859-2', + 'hungarian': 'hu_HU.ISO8859-2', + 'icelandic': 'is_IS.ISO8859-1', + 'id': 'id_ID.ISO8859-1', + 'id_id': 'id_ID.ISO8859-1', + 'is': 'is_IS.ISO8859-1', + 'is_is': 'is_IS.ISO8859-1', + 'iso-8859-1': 'en_US.ISO8859-1', + 'iso-8859-15': 'en_US.ISO8859-15', + 'iso8859-1': 'en_US.ISO8859-1', + 'iso8859-15': 'en_US.ISO8859-15', + 'iso_8859_1': 'en_US.ISO8859-1', + 'iso_8859_15': 'en_US.ISO8859-15', + 'it': 'it_IT.ISO8859-1', + 'it_ch': 'it_CH.ISO8859-1', + 'it_it': 'it_IT.ISO8859-1', + 'italian': 'it_IT.ISO8859-1', + 'iw': 'iw_IL.ISO8859-8', + 'iw_il': 'iw_IL.ISO8859-8', + 'ja': 'ja_JP.eucJP', + 'ja.jis': 'ja_JP.JIS7', + 'ja.sjis': 'ja_JP.SJIS', + 'ja_jp': 'ja_JP.eucJP', + 'ja_jp.ajec': 'ja_JP.eucJP', + 'ja_jp.euc': 'ja_JP.eucJP', + 'ja_jp.eucjp': 'ja_JP.eucJP', + 'ja_jp.iso-2022-jp': 'ja_JP.JIS7', + 'ja_jp.jis': 'ja_JP.JIS7', + 'ja_jp.jis7': 'ja_JP.JIS7', + 'ja_jp.mscode': 'ja_JP.SJIS', + 'ja_jp.sjis': 'ja_JP.SJIS', + 'ja_jp.ujis': 'ja_JP.eucJP', + 'japan': 'ja_JP.eucJP', + 'japanese': 'ja_JP.SJIS', + 'japanese-euc': 'ja_JP.eucJP', + 'japanese.euc': 'ja_JP.eucJP', + 'jp_jp': 'ja_JP.eucJP', + 'ko': 'ko_KR.eucKR', + 'ko_kr': 'ko_KR.eucKR', + 'ko_kr.euc': 'ko_KR.eucKR', + 'korean': 'ko_KR.eucKR', + 'lt': 'lt_LT.ISO8859-4', + 'lv': 'lv_LV.ISO8859-4', + 'mk': 'mk_MK.ISO8859-5', + 'mk_mk': 'mk_MK.ISO8859-5', + 'nl': 'nl_NL.ISO8859-1', + 'nl_be': 'nl_BE.ISO8859-1', + 'nl_nl': 'nl_NL.ISO8859-1', + 'no': 'no_NO.ISO8859-1', + 'no_no': 'no_NO.ISO8859-1', + 'norwegian': 'no_NO.ISO8859-1', + 'pl': 'pl_PL.ISO8859-2', + 'pl_pl': 'pl_PL.ISO8859-2', + 'polish': 'pl_PL.ISO8859-2', + 'portuguese': 'pt_PT.ISO8859-1', + 'portuguese_brazil': 'pt_BR.ISO8859-1', + 'posix': 'C', + 'posix-utf2': 'C', + 'pt': 'pt_PT.ISO8859-1', + 'pt_br': 'pt_BR.ISO8859-1', + 'pt_pt': 'pt_PT.ISO8859-1', + 'ro': 'ro_RO.ISO8859-2', + 'ro_ro': 'ro_RO.ISO8859-2', + 'ru': 'ru_RU.ISO8859-5', + 'ru_ru': 'ru_RU.ISO8859-5', + 'rumanian': 'ro_RO.ISO8859-2', + 'russian': 'ru_RU.ISO8859-5', + 'serbocroatian': 'sh_YU.ISO8859-2', + 'sh': 'sh_YU.ISO8859-2', + 'sh_hr': 'sh_HR.ISO8859-2', + 'sh_sp': 'sh_YU.ISO8859-2', + 'sh_yu': 'sh_YU.ISO8859-2', + 'sk': 'sk_SK.ISO8859-2', + 'sk_sk': 'sk_SK.ISO8859-2', + 'sl': 'sl_CS.ISO8859-2', + 'sl_cs': 'sl_CS.ISO8859-2', + 'sl_si': 'sl_SI.ISO8859-2', + 'slovak': 'sk_SK.ISO8859-2', + 'slovene': 'sl_CS.ISO8859-2', + 'sp': 'sp_YU.ISO8859-5', + 'sp_yu': 'sp_YU.ISO8859-5', + 'spanish': 'es_ES.ISO8859-1', + 'spanish_spain': 'es_ES.ISO8859-1', + 'sr_sp': 'sr_SP.ISO8859-2', + 'sv': 'sv_SE.ISO8859-1', + 'sv_se': 'sv_SE.ISO8859-1', + 'swedish': 'sv_SE.ISO8859-1', + 'th_th': 'th_TH.TACTIS', + 'tr': 'tr_TR.ISO8859-9', + 'tr_tr': 'tr_TR.ISO8859-9', + 'turkish': 'tr_TR.ISO8859-9', + 'univ': 'en_US.utf', + 'universal': 'en_US.utf', + 'zh': 'zh_CN.eucCN', + 'zh_cn': 'zh_CN.eucCN', + 'zh_cn.big5': 'zh_TW.eucTW', + 'zh_cn.euc': 'zh_CN.eucCN', + 'zh_tw': 'zh_TW.eucTW', + 'zh_tw.euc': 'zh_TW.eucTW', +} + +# +# this maps windows language identifiers (as used on Windows 95 and +# earlier) to locale strings. +# +# NOTE: this mapping is incomplete. If your language is missing, send +# a note with the missing language identifier and the suggested locale +# code to Fredrik Lundh . Thanks /F + +windows_locale = { + 0x0404: "zh_TW", # Chinese (Taiwan) + 0x0804: "zh_CN", # Chinese (PRC) + 0x0406: "da_DK", # Danish + 0x0413: "nl_NL", # Dutch (Netherlands) + 0x0409: "en_US", # English (United States) + 0x0809: "en_UK", # English (United Kingdom) + 0x0c09: "en_AU", # English (Australian) + 0x1009: "en_CA", # English (Canadian) + 0x1409: "en_NZ", # English (New Zealand) + 0x1809: "en_IE", # English (Ireland) + 0x1c09: "en_ZA", # English (South Africa) + 0x040b: "fi_FI", # Finnish + 0x040c: "fr_FR", # French (Standard) + 0x080c: "fr_BE", # French (Belgian) + 0x0c0c: "fr_CA", # French (Canadian) + 0x100c: "fr_CH", # French (Switzerland) + 0x0407: "de_DE", # German (Standard) + 0x0408: "el_GR", # Greek + 0x040d: "iw_IL", # Hebrew + 0x040f: "is_IS", # Icelandic + 0x0410: "it_IT", # Italian (Standard) + 0x0411: "ja_JA", # Japanese + 0x0414: "no_NO", # Norwegian (Bokmal) + 0x0816: "pt_PT", # Portuguese (Standard) + 0x0c0a: "es_ES", # Spanish (Modern Sort) + 0x0441: "sw_KE", # Swahili (Kenya) + 0x041d: "sv_SE", # Swedish + 0x081d: "sv_FI", # Swedish (Finland) + 0x041f: "tr_TR", # Turkish +} + +def _print_locale(): + + """ Test function. + """ + categories = {} + def _init_categories(categories=categories): + for k,v in globals().items(): + if k[:3] == 'LC_': + categories[k] = v + _init_categories() + del categories['LC_ALL'] + + print 'Locale defaults as determined by getdefaultlocale():' + print '-'*72 + lang, enc = getdefaultlocale() + print 'Language: ', lang or '(undefined)' + print 'Encoding: ', enc or '(undefined)' + print + + print 'Locale settings on startup:' + print '-'*72 + for name,category in categories.items(): + print name, '...' + lang, enc = getlocale(category) + print ' Language: ', lang or '(undefined)' + print ' Encoding: ', enc or '(undefined)' + print + + print + print 'Locale settings after calling resetlocale():' + print '-'*72 + resetlocale() + for name,category in categories.items(): + print name, '...' + lang, enc = getlocale(category) + print ' Language: ', lang or '(undefined)' + print ' Encoding: ', enc or '(undefined)' + print + + try: + setlocale(LC_ALL, "") + except: + print 'NOTE:' + print 'setlocale(LC_ALL, "") does not support the default locale' + print 'given in the OS environment variables.' + else: + print + print 'Locale settings after calling setlocale(LC_ALL, ""):' + print '-'*72 + for name,category in categories.items(): + print name, '...' + lang, enc = getlocale(category) + print ' Language: ', lang or '(undefined)' + print ' Encoding: ', enc or '(undefined)' + print + +### + +try: + LC_MESSAGES +except: + pass +else: + __all__.append("LC_MESSAGES") + +if __name__=='__main__': + print 'Locale aliasing:' + print + _print_locale() + print + print 'Number formatting:' + print + _test() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/mutex.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/mutex.py new file mode 100644 index 00000000..9b8b1a0c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/mutex.py @@ -0,0 +1,51 @@ +"""Mutual exclusion -- for use with module sched + +A mutex has two pieces of state -- a 'locked' bit and a queue. +When the mutex is not locked, the queue is empty. +Otherwise, the queue contains 0 or more (function, argument) pairs +representing functions (or methods) waiting to acquire the lock. +When the mutex is unlocked while the queue is not empty, +the first queue entry is removed and its function(argument) pair called, +implying it now has the lock. + +Of course, no multi-threading is implied -- hence the funny interface +for lock, where a function is called once the lock is aquired. +""" + +class mutex: + def __init__(self): + """Create a new mutex -- initially unlocked.""" + self.locked = 0 + self.queue = [] + + def test(self): + """Test the locked bit of the mutex.""" + return self.locked + + def testandset(self): + """Atomic test-and-set -- grab the lock if it is not set, + return true if it succeeded.""" + if not self.locked: + self.locked = 1 + return 1 + else: + return 0 + + def lock(self, function, argument): + """Lock a mutex, call the function with supplied argument + when it is acquired. If the mutex is already locked, place + function and argument in the queue.""" + if self.testandset(): + function(argument) + else: + self.queue.append((function, argument)) + + def unlock(self): + """Unlock a mutex. If the queue is not empty, call the next + function with its argument.""" + if self.queue: + function, argument = self.queue[0] + del self.queue[0] + function(argument) + else: + self.locked = 0 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/netrc.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/netrc.py new file mode 100644 index 00000000..c4182ba8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/netrc.py @@ -0,0 +1,108 @@ +"""An object-oriented interface to .netrc files.""" + +# Module and documentation by Eric S. Raymond, 21 Dec 1998 + +import os, shlex + +__all__ = ["netrc", "NetrcParseError"] + + +class NetrcParseError(Exception): + """Exception raised on syntax errors in the .netrc file.""" + def __init__(self, msg, filename=None, lineno=None): + self.filename = filename + self.lineno = lineno + self.msg = msg + Exception.__init__(self, msg) + + def __str__(self): + return "%s (%s, line %s)" % (self.msg, self.filename, self.lineno) + + +class netrc: + def __init__(self, file=None): + if not file: + file = os.path.join(os.environ['HOME'], ".netrc") + fp = open(file) + self.hosts = {} + self.macros = {} + lexer = shlex.shlex(fp) + # Allows @ in hostnames. Not a big deal... + lexer.wordchars = lexer.wordchars + '.-@' + while 1: + # Look for a machine, default, or macdef top-level keyword + toplevel = tt = lexer.get_token() + if not tt: + break + elif tt == 'machine': + entryname = lexer.get_token() + elif tt == 'default': + entryname = 'default' + elif tt == 'macdef': # Just skip to end of macdefs + entryname = lexer.get_token() + self.macros[entryname] = [] + lexer.whitespace = ' \t' + while 1: + line = lexer.instream.readline() + if not line or line == '\012': + lexer.whitespace = ' \t\r\n' + break + self.macros[entryname].append(line) + continue + else: + raise NetrcParseError( + "bad toplevel token %r" % tt, file, lexer.lineno) + + # We're looking at start of an entry for a named machine or default. + login = account = password = None + self.hosts[entryname] = {} + while 1: + tt = lexer.get_token() + if (tt=='' or tt == 'machine' or + tt == 'default' or tt =='macdef'): + if login and password: + self.hosts[entryname] = (login, account, password) + lexer.push_token(tt) + break + else: + raise NetrcParseError( + "malformed %s entry %s terminated by %s" + % (toplevel, entryname, repr(tt)), + file, lexer.lineno) + elif tt == 'login' or tt == 'user': + login = lexer.get_token() + elif tt == 'account': + account = lexer.get_token() + elif tt == 'password': + password = lexer.get_token() + else: + raise NetrcParseError("bad follower token %r" % tt, + file, lexer.lineno) + + def authenticators(self, host): + """Return a (user, account, password) tuple for given host.""" + if self.hosts.has_key(host): + return self.hosts[host] + elif self.hosts.has_key('default'): + return self.hosts['default'] + else: + return None + + def __repr__(self): + """Dump the class data in the format of a .netrc file.""" + rep = "" + for host in self.hosts.keys(): + attrs = self.hosts[host] + rep = rep + "machine "+ host + "\n\tlogin " + repr(attrs[0]) + "\n" + if attrs[1]: + rep = rep + "account " + repr(attrs[1]) + rep = rep + "\tpassword " + repr(attrs[2]) + "\n" + for macro in self.macros.keys(): + rep = rep + "macdef " + macro + "\n" + for line in self.macros[macro]: + rep = rep + line + rep = rep + "\n" + return rep + +if __name__ == '__main__': + print netrc() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/ntpath.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/ntpath.py new file mode 100644 index 00000000..bc7774a1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/ntpath.py @@ -0,0 +1,481 @@ +# Module 'ntpath' -- common operations on WinNT/Win95 pathnames +"""Common pathname manipulations, WindowsNT/95 version. + +Instead of importing this module directly, import os and refer to this +module as os.path. +""" + +import os +import stat + +__all__ = ["normcase","isabs","join","splitdrive","split","splitext", + "basename","dirname","commonprefix","getsize","getmtime", + "getatime","islink","exists","isdir","isfile","ismount", + "walk","expanduser","expandvars","normpath","abspath","splitunc"] + +# Normalize the case of a pathname and map slashes to backslashes. +# Other normalizations (such as optimizing '../' away) are not done +# (this is done by normpath). + +def normcase(s): + """Normalize case of pathname. + + Makes all characters lowercase and all slashes into backslashes.""" + return s.replace("/", "\\").lower() + + +# Return whether a path is absolute. +# Trivial in Posix, harder on the Mac or MS-DOS. +# For DOS it is absolute if it starts with a slash or backslash (current +# volume), or if a pathname after the volume letter and colon / UNC resource +# starts with a slash or backslash. + +def isabs(s): + """Test whether a path is absolute""" + s = splitdrive(s)[1] + return s != '' and s[:1] in '/\\' + + +# Join two (or more) paths. + +def join(a, *p): + """Join two or more pathname components, inserting "\\" as needed""" + path = a + for b in p: + b_wins = 0 # set to 1 iff b makes path irrelevant + if path == "": + b_wins = 1 + + elif isabs(b): + # This probably wipes out path so far. However, it's more + # complicated if path begins with a drive letter: + # 1. join('c:', '/a') == 'c:/a' + # 2. join('c:/', '/a') == 'c:/a' + # But + # 3. join('c:/a', '/b') == '/b' + # 4. join('c:', 'd:/') = 'd:/' + # 5. join('c:/', 'd:/') = 'd:/' + if path[1:2] != ":" or b[1:2] == ":": + # Path doesn't start with a drive letter, or cases 4 and 5. + b_wins = 1 + + # Else path has a drive letter, and b doesn't but is absolute. + elif len(path) > 3 or (len(path) == 3 and + path[-1] not in "/\\"): + # case 3 + b_wins = 1 + + if b_wins: + path = b + else: + # Join, and ensure there's a separator. + assert len(path) > 0 + if path[-1] in "/\\": + if b and b[0] in "/\\": + path += b[1:] + else: + path += b + elif path[-1] == ":": + path += b + elif b: + if b[0] in "/\\": + path += b + else: + path += "\\" + b + else: + # path is not empty and does not end with a backslash, + # but b is empty; since, e.g., split('a/') produces + # ('a', ''), it's best if join() adds a backslash in + # this case. + path += '\\' + + return path + + +# Split a path in a drive specification (a drive letter followed by a +# colon) and the path specification. +# It is always true that drivespec + pathspec == p +def splitdrive(p): + """Split a pathname into drive and path specifiers. Returns a 2-tuple +"(drive,path)"; either part may be empty""" + if p[1:2] == ':': + return p[0:2], p[2:] + return '', p + + +# Parse UNC paths +def splitunc(p): + """Split a pathname into UNC mount point and relative path specifiers. + + Return a 2-tuple (unc, rest); either part may be empty. + If unc is not empty, it has the form '//host/mount' (or similar + using backslashes). unc+rest is always the input path. + Paths containing drive letters never have an UNC part. + """ + if p[1:2] == ':': + return '', p # Drive letter present + firstTwo = p[0:2] + if firstTwo == '//' or firstTwo == '\\\\': + # is a UNC path: + # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter + # \\machine\mountpoint\directories... + # directory ^^^^^^^^^^^^^^^ + normp = normcase(p) + index = normp.find('\\', 2) + if index == -1: + ##raise RuntimeError, 'illegal UNC path: "' + p + '"' + return ("", p) + index = normp.find('\\', index + 1) + if index == -1: + index = len(p) + return p[:index], p[index:] + return '', p + + +# Split a path in head (everything up to the last '/') and tail (the +# rest). After the trailing '/' is stripped, the invariant +# join(head, tail) == p holds. +# The resulting head won't end in '/' unless it is the root. + +def split(p): + """Split a pathname. + + Return tuple (head, tail) where tail is everything after the final slash. + Either part may be empty.""" + + d, p = splitdrive(p) + # set i to index beyond p's last slash + i = len(p) + while i and p[i-1] not in '/\\': + i = i - 1 + head, tail = p[:i], p[i:] # now tail has no slashes + # remove trailing slashes from head, unless it's all slashes + head2 = head + while head2 and head2[-1] in '/\\': + head2 = head2[:-1] + head = head2 or head + return d + head, tail + + +# Split a path in root and extension. +# The extension is everything starting at the last dot in the last +# pathname component; the root is everything before that. +# It is always true that root + ext == p. + +def splitext(p): + """Split the extension from a pathname. + + Extension is everything from the last dot to the end. + Return (root, ext), either part may be empty.""" + root, ext = '', '' + for c in p: + if c in ['/','\\']: + root, ext = root + ext + c, '' + elif c == '.': + if ext: + root, ext = root + ext, c + else: + ext = c + elif ext: + ext = ext + c + else: + root = root + c + return root, ext + + +# Return the tail (basename) part of a path. + +def basename(p): + """Returns the final component of a pathname""" + return split(p)[1] + + +# Return the head (dirname) part of a path. + +def dirname(p): + """Returns the directory component of a pathname""" + return split(p)[0] + + +# Return the longest prefix of all list elements. + +def commonprefix(m): + "Given a list of pathnames, returns the longest common leading component" + if not m: return '' + prefix = m[0] + for item in m: + for i in range(len(prefix)): + if prefix[:i+1] != item[:i+1]: + prefix = prefix[:i] + if i == 0: return '' + break + return prefix + + +# Get size, mtime, atime of files. + +def getsize(filename): + """Return the size of a file, reported by os.stat()""" + st = os.stat(filename) + return st[stat.ST_SIZE] + +def getmtime(filename): + """Return the last modification time of a file, reported by os.stat()""" + st = os.stat(filename) + return st[stat.ST_MTIME] + +def getatime(filename): + """Return the last access time of a file, reported by os.stat()""" + st = os.stat(filename) + return st[stat.ST_ATIME] + + +# Is a path a symbolic link? +# This will always return false on systems where posix.lstat doesn't exist. + +def islink(path): + """Test for symbolic link. On WindowsNT/95 always returns false""" + return 0 + + +# Does a path exist? +# This is false for dangling symbolic links. + +def exists(path): + """Test whether a path exists""" + try: + st = os.stat(path) + except os.error: + return 0 + return 1 + + +# Is a path a dos directory? +# This follows symbolic links, so both islink() and isdir() can be true +# for the same path. + +def isdir(path): + """Test whether a path is a directory""" + try: + st = os.stat(path) + except os.error: + return 0 + return stat.S_ISDIR(st[stat.ST_MODE]) + + +# Is a path a regular file? +# This follows symbolic links, so both islink() and isdir() can be true +# for the same path. + +def isfile(path): + """Test whether a path is a regular file""" + try: + st = os.stat(path) + except os.error: + return 0 + return stat.S_ISREG(st[stat.ST_MODE]) + + +# Is a path a mount point? Either a root (with or without drive letter) +# or an UNC path with at most a / or \ after the mount point. + +def ismount(path): + """Test whether a path is a mount point (defined as root of drive)""" + unc, rest = splitunc(path) + if unc: + return rest in ("", "/", "\\") + p = splitdrive(path)[1] + return len(p) == 1 and p[0] in '/\\' + + +# Directory tree walk. +# For each directory under top (including top itself, but excluding +# '.' and '..'), func(arg, dirname, filenames) is called, where +# dirname is the name of the directory and filenames is the list +# files files (and subdirectories etc.) in the directory. +# The func may modify the filenames list, to implement a filter, +# or to impose a different order of visiting. + +def walk(top, func, arg): + """Directory tree walk with callback function. + + For each directory in the directory tree rooted at top (including top + itself, but excluding '.' and '..'), call func(arg, dirname, fnames). + dirname is the name of the directory, and fnames a list of the names of + the files and subdirectories in dirname (excluding '.' and '..'). func + may modify the fnames list in-place (e.g. via del or slice assignment), + and walk will only recurse into the subdirectories whose names remain in + fnames; this can be used to implement a filter, or to impose a specific + order of visiting. No semantics are defined for, or required of, arg, + beyond that arg is always passed to func. It can be used, e.g., to pass + a filename pattern, or a mutable object designed to accumulate + statistics. Passing None for arg is common.""" + + try: + names = os.listdir(top) + except os.error: + return + func(arg, top, names) + exceptions = ('.', '..') + for name in names: + if name not in exceptions: + name = join(top, name) + if isdir(name): + walk(name, func, arg) + + +# Expand paths beginning with '~' or '~user'. +# '~' means $HOME; '~user' means that user's home directory. +# If the path doesn't begin with '~', or if the user or $HOME is unknown, +# the path is returned unchanged (leaving error reporting to whatever +# function is called with the expanded path as argument). +# See also module 'glob' for expansion of *, ? and [...] in pathnames. +# (A function should also be defined to do full *sh-style environment +# variable expansion.) + +def expanduser(path): + """Expand ~ and ~user constructs. + + If user or $HOME is unknown, do nothing.""" + if path[:1] != '~': + return path + i, n = 1, len(path) + while i < n and path[i] not in '/\\': + i = i + 1 + if i == 1: + if os.environ.has_key('HOME'): + userhome = os.environ['HOME'] + elif not os.environ.has_key('HOMEPATH'): + return path + else: + try: + drive = os.environ['HOMEDRIVE'] + except KeyError: + drive = '' + userhome = join(drive, os.environ['HOMEPATH']) + else: + return path + return userhome + path[i:] + + +# Expand paths containing shell variable substitutions. +# The following rules apply: +# - no expansion within single quotes +# - no escape character, except for '$$' which is translated into '$' +# - ${varname} is accepted. +# - varnames can be made out of letters, digits and the character '_' +# XXX With COMMAND.COM you can use any characters in a variable name, +# XXX except '^|<>='. + +def expandvars(path): + """Expand shell variables of form $var and ${var}. + + Unknown variables are left unchanged.""" + if '$' not in path: + return path + import string + varchars = string.ascii_letters + string.digits + '_-' + res = '' + index = 0 + pathlen = len(path) + while index < pathlen: + c = path[index] + if c == '\'': # no expansion within single quotes + path = path[index + 1:] + pathlen = len(path) + try: + index = path.index('\'') + res = res + '\'' + path[:index + 1] + except ValueError: + res = res + path + index = pathlen - 1 + elif c == '$': # variable or '$$' + if path[index + 1:index + 2] == '$': + res = res + c + index = index + 1 + elif path[index + 1:index + 2] == '{': + path = path[index+2:] + pathlen = len(path) + try: + index = path.index('}') + var = path[:index] + if os.environ.has_key(var): + res = res + os.environ[var] + except ValueError: + res = res + path + index = pathlen - 1 + else: + var = '' + index = index + 1 + c = path[index:index + 1] + while c != '' and c in varchars: + var = var + c + index = index + 1 + c = path[index:index + 1] + if os.environ.has_key(var): + res = res + os.environ[var] + if c != '': + res = res + c + else: + res = res + c + index = index + 1 + return res + + +# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B. +# Previously, this function also truncated pathnames to 8+3 format, +# but as this module is called "ntpath", that's obviously wrong! + +def normpath(path): + """Normalize path, eliminating double slashes, etc.""" + path = path.replace("/", "\\") + prefix, path = splitdrive(path) + while path[:1] == "\\": + prefix = prefix + "\\" + path = path[1:] + comps = path.split("\\") + i = 0 + while i < len(comps): + if comps[i] in ('.', ''): + del comps[i] + elif comps[i] == '..': + if i > 0 and comps[i-1] != '..': + del comps[i-1:i+1] + i -= 1 + elif i == 0 and prefix.endswith("\\"): + del comps[i] + else: + i += 1 + else: + i += 1 + # If the path is now empty, substitute '.' + if not prefix and not comps: + comps.append('.') + return prefix + "\\".join(comps) + + +# Return an absolute path. +def abspath(path): + """Return the absolute version of a path""" + try: + from nt import _getfullpathname + except ImportError: # Not running on Windows - mock up something sensible. + global abspath + def _abspath(path): + if not isabs(path): + path = join(os.getcwd(), path) + return normpath(path) + abspath = _abspath + return _abspath(path) + + if path: # Empty path must return current working directory. + try: + path = _getfullpathname(path) + except WindowsError: + pass # Bad path - return unchanged. + else: + path = os.getcwd() + return normpath(path) + +# realpath is a no-op on systems without islink support +realpath = abspath diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/os.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/os.py new file mode 100644 index 00000000..96a9fe5d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/os.py @@ -0,0 +1,612 @@ +r"""OS routines for Mac, DOS, NT, or Posix depending on what system we're on. + +This exports: + - all functions from posix, nt, dos, os2, mac, or ce, e.g. unlink, stat, etc. + - os.path is one of the modules posixpath, ntpath, macpath, or dospath + - os.name is 'posix', 'nt', 'dos', 'os2', 'mac', 'ce' or 'riscos' + - os.curdir is a string representing the current directory ('.' or ':') + - os.pardir is a string representing the parent directory ('..' or '::') + - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\') + - os.extsep is the extension separator ('.' or '/') + - os.altsep is the alternate pathname separator (None or '/') + - os.pathsep is the component separator used in $PATH etc + - os.linesep is the line separator in text files ('\r' or '\n' or '\r\n') + - os.defpath is the default search path for executables + +Programs that import and use 'os' stand a better chance of being +portable between different platforms. Of course, they must then +only use functions that are defined by all platforms (e.g., unlink +and opendir), and leave all pathname manipulation to os.path +(e.g., split and join). +""" + +#' + +import sys + +_names = sys.builtin_module_names + +altsep = None + +__all__ = ["altsep", "curdir", "pardir", "sep", "pathsep", "linesep", + "defpath", "name"] + +def _get_exports_list(module): + try: + return list(module.__all__) + except AttributeError: + return [n for n in dir(module) if n[0] != '_'] + +if 'posix' in _names: + name = 'posix' + linesep = '\n' + curdir = '.'; pardir = '..'; sep = '/'; pathsep = ':' + defpath = ':/bin:/usr/bin' + from posix import * + try: + from posix import _exit + except ImportError: + pass + import posixpath + path = posixpath + del posixpath + + import posix + __all__.extend(_get_exports_list(posix)) + del posix + +elif 'nt' in _names: + name = 'nt' + linesep = '\r\n' + curdir = '.'; pardir = '..'; sep = '\\'; pathsep = ';' + defpath = '.;C:\\bin' + from nt import * + for i in ['_exit']: + try: + exec "from nt import " + i + except ImportError: + pass + import ntpath + path = ntpath + del ntpath + + import nt + __all__.extend(_get_exports_list(nt)) + del nt + +elif 'dos' in _names: + name = 'dos' + linesep = '\r\n' + curdir = '.'; pardir = '..'; sep = '\\'; pathsep = ';' + defpath = '.;C:\\bin' + from dos import * + try: + from dos import _exit + except ImportError: + pass + import dospath + path = dospath + del dospath + + import dos + __all__.extend(_get_exports_list(dos)) + del dos + +elif 'os2' in _names: + name = 'os2' + linesep = '\r\n' + curdir = '.'; pardir = '..'; sep = '\\'; pathsep = ';' + defpath = '.;C:\\bin' + from os2 import * + try: + from os2 import _exit + except ImportError: + pass + import ntpath + path = ntpath + del ntpath + + import os2 + __all__.extend(_get_exports_list(os2)) + del os2 + +elif 'mac' in _names: + name = 'mac' + linesep = '\r' + curdir = ':'; pardir = '::'; sep = ':'; pathsep = '\n' + defpath = ':' + from mac import * + try: + from mac import _exit + except ImportError: + pass + import macpath + path = macpath + del macpath + + import mac + __all__.extend(_get_exports_list(mac)) + del mac + +elif 'ce' in _names: + name = 'ce' + linesep = '\r\n' + curdir = '.'; pardir = '..'; sep = '\\'; pathsep = ';' + defpath = '\\Windows' + from ce import * + for i in ['_exit']: + try: + exec "from ce import " + i + except ImportError: + pass + # We can use the standard Windows path. + import ntpath + path = ntpath + del ntpath + + import ce + __all__.extend(_get_exports_list(ce)) + del ce + +elif 'riscos' in _names: + name = 'riscos' + linesep = '\n' + curdir = '@'; pardir = '^'; sep = '.'; pathsep = ',' + defpath = '' + from riscos import * + try: + from riscos import _exit + except ImportError: + pass + import riscospath + path = riscospath + del riscospath + + import riscos + __all__.extend(_get_exports_list(riscos)) + del riscos + +else: + raise ImportError, 'no os specific module found' + + +if sep=='.': + extsep = '/' +else: + extsep = '.' + +__all__.append("path") + +del _names + +sys.modules['os.path'] = path + +#' + +# Super directory utilities. +# (Inspired by Eric Raymond; the doc strings are mostly his) + +def makedirs(name, mode=0777): + """makedirs(path [, mode=0777]) -> None + + Super-mkdir; create a leaf directory and all intermediate ones. + Works like mkdir, except that any intermediate path segment (not + just the rightmost) will be created if it does not exist. This is + recursive. + + """ + head, tail = path.split(name) + if not tail: + head, tail = path.split(head) + if head and tail and not path.exists(head): + makedirs(head, mode) + mkdir(name, mode) + +def removedirs(name): + """removedirs(path) -> None + + Super-rmdir; remove a leaf directory and empty all intermediate + ones. Works like rmdir except that, if the leaf directory is + successfully removed, directories corresponding to rightmost path + segments will be pruned way until either the whole path is + consumed or an error occurs. Errors during this latter phase are + ignored -- they generally mean that a directory was not empty. + + """ + rmdir(name) + head, tail = path.split(name) + if not tail: + head, tail = path.split(head) + while head and tail: + try: + rmdir(head) + except error: + break + head, tail = path.split(head) + +def renames(old, new): + """renames(old, new) -> None + + Super-rename; create directories as necessary and delete any left + empty. Works like rename, except creation of any intermediate + directories needed to make the new pathname good is attempted + first. After the rename, directories corresponding to rightmost + path segments of the old name will be pruned way until either the + whole path is consumed or a nonempty directory is found. + + Note: this function can fail with the new directory structure made + if you lack permissions needed to unlink the leaf directory or + file. + + """ + head, tail = path.split(new) + if head and tail and not path.exists(head): + makedirs(head) + rename(old, new) + head, tail = path.split(old) + if head and tail: + try: + removedirs(head) + except error: + pass + +__all__.extend(["makedirs", "removedirs", "renames"]) + +# Make sure os.environ exists, at least +try: + environ +except NameError: + environ = {} + +def execl(file, *args): + """execl(file, *args) + + Execute the executable file with argument list args, replacing the + current process. """ + execv(file, args) + +def execle(file, *args): + """execle(file, *args, env) + + Execute the executable file with argument list args and + environment env, replacing the current process. """ + env = args[-1] + execve(file, args[:-1], env) + +def execlp(file, *args): + """execlp(file, *args) + + Execute the executable file (which is searched for along $PATH) + with argument list args, replacing the current process. """ + execvp(file, args) + +def execlpe(file, *args): + """execlpe(file, *args, env) + + Execute the executable file (which is searched for along $PATH) + with argument list args and environment env, replacing the current + process. """ + env = args[-1] + execvpe(file, args[:-1], env) + +def execvp(file, args): + """execp(file, args) + + Execute the executable file (which is searched for along $PATH) + with argument list args, replacing the current process. + args may be a list or tuple of strings. """ + _execvpe(file, args) + +def execvpe(file, args, env): + """execvpe(file, args, env) + + Execute the executable file (which is searched for along $PATH) + with argument list args and environment env , replacing the + current process. + args may be a list or tuple of strings. """ + _execvpe(file, args, env) + +__all__.extend(["execl","execle","execlp","execlpe","execvp","execvpe"]) + +def _execvpe(file, args, env=None): + from errno import ENOENT, ENOTDIR + + if env is not None: + func = execve + argrest = (args, env) + else: + func = execv + argrest = (args,) + env = environ + + head, tail = path.split(file) + if head: + apply(func, (file,) + argrest) + return + if env.has_key('PATH'): + envpath = env['PATH'] + else: + envpath = defpath + PATH = envpath.split(pathsep) + saved_exc = None + saved_tb = None + for dir in PATH: + fullname = path.join(dir, file) + try: + apply(func, (fullname,) + argrest) + except error, e: + tb = sys.exc_info()[2] + if (e.errno != ENOENT and e.errno != ENOTDIR + and saved_exc is None): + saved_exc = e + saved_tb = tb + if saved_exc: + raise error, saved_exc, saved_tb + raise error, e, tb + +# Change environ to automatically call putenv() if it exists +try: + # This will fail if there's no putenv + putenv +except NameError: + pass +else: + import UserDict + + # Fake unsetenv() for Windows + # not sure about os2 and dos here but + # I'm guessing they are the same. + + if name in ('os2', 'nt', 'dos'): + def unsetenv(key): + putenv(key, "") + + if name == "riscos": + # On RISC OS, all env access goes through getenv and putenv + from riscosenviron import _Environ + elif name in ('os2', 'nt', 'dos'): # Where Env Var Names Must Be UPPERCASE + # But we store them as upper case + class _Environ(UserDict.IterableUserDict): + def __init__(self, environ): + UserDict.UserDict.__init__(self) + data = self.data + for k, v in environ.items(): + data[k.upper()] = v + def __setitem__(self, key, item): + putenv(key, item) + self.data[key.upper()] = item + def __getitem__(self, key): + return self.data[key.upper()] + try: + unsetenv + except NameError: + def __delitem__(self, key): + del self.data[key.upper()] + else: + def __delitem__(self, key): + unsetenv(key) + del self.data[key.upper()] + def has_key(self, key): + return self.data.has_key(key.upper()) + def get(self, key, failobj=None): + return self.data.get(key.upper(), failobj) + def update(self, dict): + for k, v in dict.items(): + self[k] = v + + else: # Where Env Var Names Can Be Mixed Case + class _Environ(UserDict.IterableUserDict): + def __init__(self, environ): + UserDict.UserDict.__init__(self) + self.data = environ + def __setitem__(self, key, item): + putenv(key, item) + self.data[key] = item + def update(self, dict): + for k, v in dict.items(): + self[k] = v + try: + unsetenv + except NameError: + pass + else: + def __delitem__(self, key): + unsetenv(key) + del self.data[key] + + + environ = _Environ(environ) + + def getenv(key, default=None): + """Get an environment variable, return None if it doesn't exist. + The optional second argument can specify an alternate default.""" + return environ.get(key, default) + __all__.append("getenv") + +def _exists(name): + try: + eval(name) + return 1 + except NameError: + return 0 + +# Supply spawn*() (probably only for Unix) +if _exists("fork") and not _exists("spawnv") and _exists("execv"): + + P_WAIT = 0 + P_NOWAIT = P_NOWAITO = 1 + + # XXX Should we support P_DETACH? I suppose it could fork()**2 + # and close the std I/O streams. Also, P_OVERLAY is the same + # as execv*()? + + def _spawnvef(mode, file, args, env, func): + # Internal helper; func is the exec*() function to use + pid = fork() + if not pid: + # Child + try: + if env is None: + func(file, args) + else: + func(file, args, env) + except: + _exit(127) + else: + # Parent + if mode == P_NOWAIT: + return pid # Caller is responsible for waiting! + while 1: + wpid, sts = waitpid(pid, 0) + if WIFSTOPPED(sts): + continue + elif WIFSIGNALED(sts): + return -WTERMSIG(sts) + elif WIFEXITED(sts): + return WEXITSTATUS(sts) + else: + raise error, "Not stopped, signaled or exited???" + + def spawnv(mode, file, args): + """spawnv(mode, file, args) -> integer + +Execute file with arguments from args in a subprocess. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + return _spawnvef(mode, file, args, None, execv) + + def spawnve(mode, file, args, env): + """spawnve(mode, file, args, env) -> integer + +Execute file with arguments from args in a subprocess with the +specified environment. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + return _spawnvef(mode, file, args, env, execve) + + # Note: spawnvp[e] is't currently supported on Windows + + def spawnvp(mode, file, args): + """spawnvp(mode, file, args) -> integer + +Execute file (which is looked for along $PATH) with arguments from +args in a subprocess. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + return _spawnvef(mode, file, args, None, execvp) + + def spawnvpe(mode, file, args, env): + """spawnvpe(mode, file, args, env) -> integer + +Execute file (which is looked for along $PATH) with arguments from +args in a subprocess with the supplied environment. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + return _spawnvef(mode, file, args, env, execvpe) + +if _exists("spawnv"): + # These aren't supplied by the basic Windows code + # but can be easily implemented in Python + + def spawnl(mode, file, *args): + """spawnl(mode, file, *args) -> integer + +Execute file with arguments from args in a subprocess. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + return spawnv(mode, file, args) + + def spawnle(mode, file, *args): + """spawnle(mode, file, *args, env) -> integer + +Execute file with arguments from args in a subprocess with the +supplied environment. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + env = args[-1] + return spawnve(mode, file, args[:-1], env) + +if _exists("spawnvp"): + # At the moment, Windows doesn't implement spawnvp[e], + # so it won't have spawnlp[e] either. + def spawnlp(mode, file, *args): + """spawnlp(mode, file, *args, env) -> integer + +Execute file (which is looked for along $PATH) with arguments from +args in a subprocess with the supplied environment. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + return spawnvp(mode, file, args) + + def spawnlpe(mode, file, *args): + """spawnlpe(mode, file, *args, env) -> integer + +Execute file (which is looked for along $PATH) with arguments from +args in a subprocess with the supplied environment. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + env = args[-1] + return spawnvpe(mode, file, args[:-1], env) + + + __all__.extend(["spawnlp","spawnlpe","spawnv", "spawnve","spawnvp", + "spawnvpe","spawnl","spawnle",]) + + +# Supply popen2 etc. (for Unix) +if _exists("fork"): + if not _exists("popen2"): + def popen2(cmd, mode="t", bufsize=-1): + import popen2 + stdout, stdin = popen2.popen2(cmd, bufsize) + return stdin, stdout + __all__.append("popen2") + + if not _exists("popen3"): + def popen3(cmd, mode="t", bufsize=-1): + import popen2 + stdout, stdin, stderr = popen2.popen3(cmd, bufsize) + return stdin, stdout, stderr + __all__.append("popen3") + + if not _exists("popen4"): + def popen4(cmd, mode="t", bufsize=-1): + import popen2 + stdout, stdin = popen2.popen4(cmd, bufsize) + return stdin, stdout + __all__.append("popen4") + +import copy_reg as _copy_reg + +def _make_stat_result(tup, dict): + return stat_result(tup, dict) + +def _pickle_stat_result(sr): + (type, args) = sr.__reduce__() + return (_make_stat_result, args) + +try: + _copy_reg.pickle(stat_result, _pickle_stat_result, _make_stat_result) +except NameError: # stat_result may not exist + pass + +def _make_statvfs_result(tup, dict): + return statvfs_result(tup, dict) + +def _pickle_statvfs_result(sr): + (type, args) = sr.__reduce__() + return (_make_statvfs_result, args) + +try: + _copy_reg.pickle(statvfs_result, _pickle_statvfs_result, + _make_statvfs_result) +except NameError: # statvfs_result may not exist + pass diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pdb.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pdb.py new file mode 100644 index 00000000..4a2e9f6a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pdb.py @@ -0,0 +1,957 @@ +#! /usr/bin/env python + +"""A Python debugger.""" + +# (See pdb.doc for documentation.) + +import sys +import linecache +import cmd +import bdb +from repr import Repr +import os +import re + +# Create a custom safe Repr instance and increase its maxstring. +# The default of 30 truncates error messages too easily. +_repr = Repr() +_repr.maxstring = 200 +_saferepr = _repr.repr + +__all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace", + "post_mortem", "help"] + +def find_function(funcname, filename): + cre = re.compile(r'def\s+%s\s*[(]' % funcname) + try: + fp = open(filename) + except IOError: + return None + # consumer of this info expects the first line to be 1 + lineno = 1 + answer = None + while 1: + line = fp.readline() + if line == '': + break + if cre.match(line): + answer = funcname, filename, lineno + break + lineno = lineno + 1 + fp.close() + return answer + + +# Interaction prompt line will separate file and call info from code +# text using value of line_prefix string. A newline and arrow may +# be to your liking. You can set it once pdb is imported using the +# command "pdb.line_prefix = '\n% '". +# line_prefix = ': ' # Use this to get the old situation back +line_prefix = '\n-> ' # Probably a better default + +class Pdb(bdb.Bdb, cmd.Cmd): + + def __init__(self): + bdb.Bdb.__init__(self) + cmd.Cmd.__init__(self) + self.prompt = '(Pdb) ' + self.aliases = {} + # Try to load readline if it exists + try: + import readline + except ImportError: + pass + + # Read $HOME/.pdbrc and ./.pdbrc + self.rcLines = [] + if os.environ.has_key('HOME'): + envHome = os.environ['HOME'] + try: + rcFile = open(os.path.join(envHome, ".pdbrc")) + except IOError: + pass + else: + for line in rcFile.readlines(): + self.rcLines.append(line) + rcFile.close() + try: + rcFile = open(".pdbrc") + except IOError: + pass + else: + for line in rcFile.readlines(): + self.rcLines.append(line) + rcFile.close() + + def reset(self): + bdb.Bdb.reset(self) + self.forget() + + def forget(self): + self.lineno = None + self.stack = [] + self.curindex = 0 + self.curframe = None + + def setup(self, f, t): + self.forget() + self.stack, self.curindex = self.get_stack(f, t) + self.curframe = self.stack[self.curindex][0] + self.execRcLines() + + # Can be executed earlier than 'setup' if desired + def execRcLines(self): + if self.rcLines: + # Make local copy because of recursion + rcLines = self.rcLines + # executed only once + self.rcLines = [] + for line in rcLines: + line = line[:-1] + if len (line) > 0 and line[0] != '#': + self.onecmd (line) + + # Override Bdb methods (except user_call, for now) + + def user_line(self, frame): + """This function is called when we stop or break at this line.""" + self.interaction(frame, None) + + def user_return(self, frame, return_value): + """This function is called when a return trap is set here.""" + frame.f_locals['__return__'] = return_value + print '--Return--' + self.interaction(frame, None) + + def user_exception(self, frame, (exc_type, exc_value, exc_traceback)): + """This function is called if an exception occurs, + but only if we are to stop at or just below this level.""" + frame.f_locals['__exception__'] = exc_type, exc_value + if type(exc_type) == type(''): + exc_type_name = exc_type + else: exc_type_name = exc_type.__name__ + print exc_type_name + ':', _saferepr(exc_value) + self.interaction(frame, exc_traceback) + + # General interaction function + + def interaction(self, frame, traceback): + self.setup(frame, traceback) + self.print_stack_entry(self.stack[self.curindex]) + self.cmdloop() + self.forget() + + def default(self, line): + if line[:1] == '!': line = line[1:] + locals = self.curframe.f_locals + globals = self.curframe.f_globals + try: + code = compile(line + '\n', '', 'single') + exec code in globals, locals + except: + t, v = sys.exc_info()[:2] + if type(t) == type(''): + exc_type_name = t + else: exc_type_name = t.__name__ + print '***', exc_type_name + ':', v + + def precmd(self, line): + """Handle alias expansion and ';;' separator.""" + if not line.strip(): + return line + args = line.split() + while self.aliases.has_key(args[0]): + line = self.aliases[args[0]] + ii = 1 + for tmpArg in args[1:]: + line = line.replace("%" + str(ii), + tmpArg) + ii = ii + 1 + line = line.replace("%*", ' '.join(args[1:])) + args = line.split() + # split into ';;' separated commands + # unless it's an alias command + if args[0] != 'alias': + marker = line.find(';;') + if marker >= 0: + # queue up everything after marker + next = line[marker+2:].lstrip() + self.cmdqueue.append(next) + line = line[:marker].rstrip() + return line + + # Command definitions, called by cmdloop() + # The argument is the remaining string on the command line + # Return true to exit from the command loop + + do_h = cmd.Cmd.do_help + + def do_EOF(self, arg): + return 0 # Don't die on EOF + + def do_break(self, arg, temporary = 0): + # break [ ([filename:]lineno | function) [, "condition"] ] + if not arg: + if self.breaks: # There's at least one + print "Num Type Disp Enb Where" + for bp in bdb.Breakpoint.bpbynumber: + if bp: + bp.bpprint() + return + # parse arguments; comma has lowest precedence + # and cannot occur in filename + filename = None + lineno = None + cond = None + comma = arg.find(',') + if comma > 0: + # parse stuff after comma: "condition" + cond = arg[comma+1:].lstrip() + arg = arg[:comma].rstrip() + # parse stuff before comma: [filename:]lineno | function + colon = arg.rfind(':') + if colon >= 0: + filename = arg[:colon].rstrip() + f = self.lookupmodule(filename) + if not f: + print '*** ', `filename`, + print 'not found from sys.path' + return + else: + filename = f + arg = arg[colon+1:].lstrip() + try: + lineno = int(arg) + except ValueError, msg: + print '*** Bad lineno:', arg + return + else: + # no colon; can be lineno or function + try: + lineno = int(arg) + except ValueError: + try: + func = eval(arg, + self.curframe.f_globals, + self.curframe.f_locals) + except: + func = arg + try: + if hasattr(func, 'im_func'): + func = func.im_func + code = func.func_code + lineno = code.co_firstlineno + filename = code.co_filename + except: + # last thing to try + (ok, filename, ln) = self.lineinfo(arg) + if not ok: + print '*** The specified object', + print `arg`, + print 'is not a function' + print ('or was not found ' + 'along sys.path.') + return + lineno = int(ln) + if not filename: + filename = self.defaultFile() + # Check for reasonable breakpoint + line = self.checkline(filename, lineno) + if line: + # now set the break point + err = self.set_break(filename, line, temporary, cond) + if err: print '***', err + else: + bp = self.get_breaks(filename, line)[-1] + print "Breakpoint %d at %s:%d" % (bp.number, + bp.file, + bp.line) + + # To be overridden in derived debuggers + def defaultFile(self): + """Produce a reasonable default.""" + filename = self.curframe.f_code.co_filename + if filename == '' and mainpyfile: + filename = mainpyfile + return filename + + do_b = do_break + + def do_tbreak(self, arg): + self.do_break(arg, 1) + + def lineinfo(self, identifier): + failed = (None, None, None) + # Input is identifier, may be in single quotes + idstring = identifier.split("'") + if len(idstring) == 1: + # not in single quotes + id = idstring[0].strip() + elif len(idstring) == 3: + # quoted + id = idstring[1].strip() + else: + return failed + if id == '': return failed + parts = id.split('.') + # Protection for derived debuggers + if parts[0] == 'self': + del parts[0] + if len(parts) == 0: + return failed + # Best first guess at file to look at + fname = self.defaultFile() + if len(parts) == 1: + item = parts[0] + else: + # More than one part. + # First is module, second is method/class + f = self.lookupmodule(parts[0]) + if f: + fname = f + item = parts[1] + answer = find_function(item, fname) + return answer or failed + + def checkline(self, filename, lineno): + """Return line number of first line at or after input + argument such that if the input points to a 'def', the + returned line number is the first + non-blank/non-comment line to follow. If the input + points to a blank or comment line, return 0. At end + of file, also return 0.""" + + line = linecache.getline(filename, lineno) + if not line: + print 'End of file' + return 0 + line = line.strip() + # Don't allow setting breakpoint at a blank line + if ( not line or (line[0] == '#') or + (line[:3] == '"""') or line[:3] == "'''" ): + print '*** Blank or comment' + return 0 + # When a file is read in and a breakpoint is at + # the 'def' statement, the system stops there at + # code parse time. We don't want that, so all breakpoints + # set at 'def' statements are moved one line onward + if line[:3] == 'def': + instr = '' + brackets = 0 + while 1: + skipone = 0 + for c in line: + if instr: + if skipone: + skipone = 0 + elif c == '\\': + skipone = 1 + elif c == instr: + instr = '' + elif c == '#': + break + elif c in ('"',"'"): + instr = c + elif c in ('(','{','['): + brackets = brackets + 1 + elif c in (')','}',']'): + brackets = brackets - 1 + lineno = lineno+1 + line = linecache.getline(filename, lineno) + if not line: + print 'end of file' + return 0 + line = line.strip() + if not line: continue # Blank line + if brackets <= 0 and line[0] not in ('#','"',"'"): + break + return lineno + + def do_enable(self, arg): + args = arg.split() + for i in args: + bp = bdb.Breakpoint.bpbynumber[int(i)] + if bp: + bp.enable() + + def do_disable(self, arg): + args = arg.split() + for i in args: + bp = bdb.Breakpoint.bpbynumber[int(i)] + if bp: + bp.disable() + + def do_condition(self, arg): + # arg is breakpoint number and condition + args = arg.split(' ', 1) + bpnum = int(args[0].strip()) + try: + cond = args[1] + except: + cond = None + bp = bdb.Breakpoint.bpbynumber[bpnum] + if bp: + bp.cond = cond + if not cond: + print 'Breakpoint', bpnum, + print 'is now unconditional.' + + def do_ignore(self,arg): + """arg is bp number followed by ignore count.""" + args = arg.split() + bpnum = int(args[0].strip()) + try: + count = int(args[1].strip()) + except: + count = 0 + bp = bdb.Breakpoint.bpbynumber[bpnum] + if bp: + bp.ignore = count + if (count > 0): + reply = 'Will ignore next ' + if (count > 1): + reply = reply + '%d crossings' % count + else: + reply = reply + '1 crossing' + print reply + ' of breakpoint %d.' % bpnum + else: + print 'Will stop next time breakpoint', + print bpnum, 'is reached.' + + def do_clear(self, arg): + """Three possibilities, tried in this order: + clear -> clear all breaks, ask for confirmation + clear file:lineno -> clear all breaks at file:lineno + clear bpno bpno ... -> clear breakpoints by number""" + if not arg: + try: + reply = raw_input('Clear all breaks? ') + except EOFError: + reply = 'no' + reply = reply.strip().lower() + if reply in ('y', 'yes'): + self.clear_all_breaks() + return + if ':' in arg: + # Make sure it works for "clear C:\foo\bar.py:12" + i = arg.rfind(':') + filename = arg[:i] + arg = arg[i+1:] + try: + lineno = int(arg) + except: + err = "Invalid line number (%s)" % arg + else: + err = self.clear_break(filename, lineno) + if err: print '***', err + return + numberlist = arg.split() + for i in numberlist: + err = self.clear_bpbynumber(i) + if err: + print '***', err + else: + print 'Deleted breakpoint %s ' % (i,) + do_cl = do_clear # 'c' is already an abbreviation for 'continue' + + def do_where(self, arg): + self.print_stack_trace() + do_w = do_where + do_bt = do_where + + def do_up(self, arg): + if self.curindex == 0: + print '*** Oldest frame' + else: + self.curindex = self.curindex - 1 + self.curframe = self.stack[self.curindex][0] + self.print_stack_entry(self.stack[self.curindex]) + self.lineno = None + do_u = do_up + + def do_down(self, arg): + if self.curindex + 1 == len(self.stack): + print '*** Newest frame' + else: + self.curindex = self.curindex + 1 + self.curframe = self.stack[self.curindex][0] + self.print_stack_entry(self.stack[self.curindex]) + self.lineno = None + do_d = do_down + + def do_step(self, arg): + self.set_step() + return 1 + do_s = do_step + + def do_next(self, arg): + self.set_next(self.curframe) + return 1 + do_n = do_next + + def do_return(self, arg): + self.set_return(self.curframe) + return 1 + do_r = do_return + + def do_continue(self, arg): + self.set_continue() + return 1 + do_c = do_cont = do_continue + + def do_quit(self, arg): + self.set_quit() + return 1 + do_q = do_quit + do_exit = do_quit + + def do_args(self, arg): + f = self.curframe + co = f.f_code + dict = f.f_locals + n = co.co_argcount + if co.co_flags & 4: n = n+1 + if co.co_flags & 8: n = n+1 + for i in range(n): + name = co.co_varnames[i] + print name, '=', + if dict.has_key(name): print dict[name] + else: print "*** undefined ***" + do_a = do_args + + def do_retval(self, arg): + if self.curframe.f_locals.has_key('__return__'): + print self.curframe.f_locals['__return__'] + else: + print '*** Not yet returned!' + do_rv = do_retval + + def do_p(self, arg): + try: + value = eval(arg, self.curframe.f_globals, + self.curframe.f_locals) + except: + t, v = sys.exc_info()[:2] + if type(t) == type(''): + exc_type_name = t + else: exc_type_name = t.__name__ + print '***', exc_type_name + ':', `v` + return + + print `value` + + def do_list(self, arg): + self.lastcmd = 'list' + last = None + if arg: + try: + x = eval(arg, {}, {}) + if type(x) == type(()): + first, last = x + first = int(first) + last = int(last) + if last < first: + # Assume it's a count + last = first + last + else: + first = max(1, int(x) - 5) + except: + print '*** Error in argument:', `arg` + return + elif self.lineno is None: + first = max(1, self.curframe.f_lineno - 5) + else: + first = self.lineno + 1 + if last is None: + last = first + 10 + filename = self.curframe.f_code.co_filename + breaklist = self.get_file_breaks(filename) + try: + for lineno in range(first, last+1): + line = linecache.getline(filename, lineno) + if not line: + print '[EOF]' + break + else: + s = `lineno`.rjust(3) + if len(s) < 4: s = s + ' ' + if lineno in breaklist: s = s + 'B' + else: s = s + ' ' + if lineno == self.curframe.f_lineno: + s = s + '->' + print s + '\t' + line, + self.lineno = lineno + except KeyboardInterrupt: + pass + do_l = do_list + + def do_whatis(self, arg): + try: + value = eval(arg, self.curframe.f_globals, + self.curframe.f_locals) + except: + t, v = sys.exc_info()[:2] + if type(t) == type(''): + exc_type_name = t + else: exc_type_name = t.__name__ + print '***', exc_type_name + ':', `v` + return + code = None + # Is it a function? + try: code = value.func_code + except: pass + if code: + print 'Function', code.co_name + return + # Is it an instance method? + try: code = value.im_func.func_code + except: pass + if code: + print 'Method', code.co_name + return + # None of the above... + print type(value) + + def do_alias(self, arg): + args = arg.split() + if len(args) == 0: + keys = self.aliases.keys() + keys.sort() + for alias in keys: + print "%s = %s" % (alias, self.aliases[alias]) + return + if self.aliases.has_key(args[0]) and len (args) == 1: + print "%s = %s" % (args[0], self.aliases[args[0]]) + else: + self.aliases[args[0]] = ' '.join(args[1:]) + + def do_unalias(self, arg): + args = arg.split() + if len(args) == 0: return + if self.aliases.has_key(args[0]): + del self.aliases[args[0]] + + # Print a traceback starting at the top stack frame. + # The most recently entered frame is printed last; + # this is different from dbx and gdb, but consistent with + # the Python interpreter's stack trace. + # It is also consistent with the up/down commands (which are + # compatible with dbx and gdb: up moves towards 'main()' + # and down moves towards the most recent stack frame). + + def print_stack_trace(self): + try: + for frame_lineno in self.stack: + self.print_stack_entry(frame_lineno) + except KeyboardInterrupt: + pass + + def print_stack_entry(self, frame_lineno, prompt_prefix=line_prefix): + frame, lineno = frame_lineno + if frame is self.curframe: + print '>', + else: + print ' ', + print self.format_stack_entry(frame_lineno, prompt_prefix) + + + # Help methods (derived from pdb.doc) + + def help_help(self): + self.help_h() + + def help_h(self): + print """h(elp) +Without argument, print the list of available commands. +With a command name as argument, print help about that command +"help pdb" pipes the full documentation file to the $PAGER +"help exec" gives help on the ! command""" + + def help_where(self): + self.help_w() + + def help_w(self): + print """w(here) +Print a stack trace, with the most recent frame at the bottom. +An arrow indicates the "current frame", which determines the +context of most commands. 'bt' is an alias for this command.""" + + help_bt = help_w + + def help_down(self): + self.help_d() + + def help_d(self): + print """d(own) +Move the current frame one level down in the stack trace +(to an older frame).""" + + def help_up(self): + self.help_u() + + def help_u(self): + print """u(p) +Move the current frame one level up in the stack trace +(to a newer frame).""" + + def help_break(self): + self.help_b() + + def help_b(self): + print """b(reak) ([file:]lineno | function) [, condition] +With a line number argument, set a break there in the current +file. With a function name, set a break at first executable line +of that function. Without argument, list all breaks. If a second +argument is present, it is a string specifying an expression +which must evaluate to true before the breakpoint is honored. + +The line number may be prefixed with a filename and a colon, +to specify a breakpoint in another file (probably one that +hasn't been loaded yet). The file is searched for on sys.path; +the .py suffix may be omitted.""" + + def help_clear(self): + self.help_cl() + + def help_cl(self): + print "cl(ear) filename:lineno" + print """cl(ear) [bpnumber [bpnumber...]] +With a space separated list of breakpoint numbers, clear +those breakpoints. Without argument, clear all breaks (but +first ask confirmation). With a filename:lineno argument, +clear all breaks at that line in that file. + +Note that the argument is different from previous versions of +the debugger (in python distributions 1.5.1 and before) where +a linenumber was used instead of either filename:lineno or +breakpoint numbers.""" + + def help_tbreak(self): + print """tbreak same arguments as break, but breakpoint is +removed when first hit.""" + + def help_enable(self): + print """enable bpnumber [bpnumber ...] +Enables the breakpoints given as a space separated list of +bp numbers.""" + + def help_disable(self): + print """disable bpnumber [bpnumber ...] +Disables the breakpoints given as a space separated list of +bp numbers.""" + + def help_ignore(self): + print """ignore bpnumber count +Sets the ignore count for the given breakpoint number. A breakpoint +becomes active when the ignore count is zero. When non-zero, the +count is decremented each time the breakpoint is reached and the +breakpoint is not disabled and any associated condition evaluates +to true.""" + + def help_condition(self): + print """condition bpnumber str_condition +str_condition is a string specifying an expression which +must evaluate to true before the breakpoint is honored. +If str_condition is absent, any existing condition is removed; +i.e., the breakpoint is made unconditional.""" + + def help_step(self): + self.help_s() + + def help_s(self): + print """s(tep) +Execute the current line, stop at the first possible occasion +(either in a function that is called or in the current function).""" + + def help_next(self): + self.help_n() + + def help_n(self): + print """n(ext) +Continue execution until the next line in the current function +is reached or it returns.""" + + def help_return(self): + self.help_r() + + def help_r(self): + print """r(eturn) +Continue execution until the current function returns.""" + + def help_continue(self): + self.help_c() + + def help_cont(self): + self.help_c() + + def help_c(self): + print """c(ont(inue)) +Continue execution, only stop when a breakpoint is encountered.""" + + def help_list(self): + self.help_l() + + def help_l(self): + print """l(ist) [first [,last]] +List source code for the current file. +Without arguments, list 11 lines around the current line +or continue the previous listing. +With one argument, list 11 lines starting at that line. +With two arguments, list the given range; +if the second argument is less than the first, it is a count.""" + + def help_args(self): + self.help_a() + + def help_a(self): + print """a(rgs) +Print the arguments of the current function.""" + + def help_p(self): + print """p expression +Print the value of the expression.""" + + def help_exec(self): + print """(!) statement +Execute the (one-line) statement in the context of +the current stack frame. +The exclamation point can be omitted unless the first word +of the statement resembles a debugger command. +To assign to a global variable you must always prefix the +command with a 'global' command, e.g.: +(Pdb) global list_options; list_options = ['-l'] +(Pdb)""" + + def help_quit(self): + self.help_q() + + def help_q(self): + print """q(uit) or exit - Quit from the debugger. +The program being executed is aborted.""" + + help_exit = help_q + + def help_whatis(self): + print """whatis arg +Prints the type of the argument.""" + + def help_EOF(self): + print """EOF +Handles the receipt of EOF as a command.""" + + def help_alias(self): + print """alias [name [command [parameter parameter ...] ]] +Creates an alias called 'name' the executes 'command'. The command +must *not* be enclosed in quotes. Replaceable parameters are +indicated by %1, %2, and so on, while %* is replaced by all the +parameters. If no command is given, the current alias for name +is shown. If no name is given, all aliases are listed. + +Aliases may be nested and can contain anything that can be +legally typed at the pdb prompt. Note! You *can* override +internal pdb commands with aliases! Those internal commands +are then hidden until the alias is removed. Aliasing is recursively +applied to the first word of the command line; all other words +in the line are left alone. + +Some useful aliases (especially when placed in the .pdbrc file) are: + +#Print instance variables (usage "pi classInst") +alias pi for k in %1.__dict__.keys(): print "%1.",k,"=",%1.__dict__[k] + +#Print instance variables in self +alias ps pi self +""" + + def help_unalias(self): + print """unalias name +Deletes the specified alias.""" + + def help_pdb(self): + help() + + def lookupmodule(self, filename): + """Helper function for break/clear parsing -- may be overridden.""" + root, ext = os.path.splitext(filename) + if ext == '': + filename = filename + '.py' + if os.path.isabs(filename): + return filename + for dirname in sys.path: + while os.path.islink(dirname): + dirname = os.readlink(dirname) + fullname = os.path.join(dirname, filename) + if os.path.exists(fullname): + return fullname + return None + +# Simplified interface + +def run(statement, globals=None, locals=None): + Pdb().run(statement, globals, locals) + +def runeval(expression, globals=None, locals=None): + return Pdb().runeval(expression, globals, locals) + +def runctx(statement, globals, locals): + # B/W compatibility + run(statement, globals, locals) + +def runcall(*args): + return apply(Pdb().runcall, args) + +def set_trace(): + Pdb().set_trace() + +# Post-Mortem interface + +def post_mortem(t): + p = Pdb() + p.reset() + while t.tb_next is not None: + t = t.tb_next + p.interaction(t.tb_frame, t) + +def pm(): + post_mortem(sys.last_traceback) + + +# Main program for testing + +TESTCMD = 'import x; x.main()' + +def test(): + run(TESTCMD) + +# print help +def help(): + for dirname in sys.path: + fullname = os.path.join(dirname, 'pdb.doc') + if os.path.exists(fullname): + sts = os.system('${PAGER-more} '+fullname) + if sts: print '*** Pager exit status:', sts + break + else: + print 'Sorry, can\'t find the help file "pdb.doc"', + print 'along the Python search path' + +mainmodule = '' +mainpyfile = '' + +# When invoked as main program, invoke the debugger on a script +if __name__=='__main__': + if not sys.argv[1:]: + print "usage: pdb.py scriptfile [arg] ..." + sys.exit(2) + + mainpyfile = filename = sys.argv[1] # Get script filename + if not os.path.exists(filename): + print 'Error:', `filename`, 'does not exist' + sys.exit(1) + mainmodule = os.path.basename(filename) + del sys.argv[0] # Hide "pdb.py" from argument list + + # Insert script directory in front of module search path + sys.path.insert(0, os.path.dirname(filename)) + + run('execfile(' + `filename` + ')') diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pickle.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pickle.py new file mode 100644 index 00000000..369dd749 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pickle.py @@ -0,0 +1,986 @@ +"""Create portable serialized representations of Python objects. + +See module cPickle for a (much) faster implementation. +See module copy_reg for a mechanism for registering custom picklers. + +Classes: + + Pickler + Unpickler + +Functions: + + dump(object, file) + dumps(object) -> string + load(file) -> object + loads(string) -> object + +Misc variables: + + __version__ + format_version + compatible_formats + +""" + +__version__ = "$Revision: 1.56.4.4 $" # Code version + +from types import * +from copy_reg import dispatch_table, safe_constructors +import marshal +import sys +import struct +import re + +__all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler", + "Unpickler", "dump", "dumps", "load", "loads"] + +format_version = "1.3" # File format version we write +compatible_formats = ["1.0", "1.1", "1.2"] # Old format versions we can read + +mdumps = marshal.dumps +mloads = marshal.loads + +class PickleError(Exception): pass +class PicklingError(PickleError): pass +class UnpicklingError(PickleError): pass + +class _Stop(Exception): + def __init__(self, value): + self.value = value + +try: + from org.python.core import PyStringMap +except ImportError: + PyStringMap = None + +try: + UnicodeType +except NameError: + UnicodeType = None + + +MARK = '(' +STOP = '.' +POP = '0' +POP_MARK = '1' +DUP = '2' +FLOAT = 'F' +INT = 'I' +BININT = 'J' +BININT1 = 'K' +LONG = 'L' +BININT2 = 'M' +NONE = 'N' +PERSID = 'P' +BINPERSID = 'Q' +REDUCE = 'R' +STRING = 'S' +BINSTRING = 'T' +SHORT_BINSTRING = 'U' +UNICODE = 'V' +BINUNICODE = 'X' +APPEND = 'a' +BUILD = 'b' +GLOBAL = 'c' +DICT = 'd' +EMPTY_DICT = '}' +APPENDS = 'e' +GET = 'g' +BINGET = 'h' +INST = 'i' +LONG_BINGET = 'j' +LIST = 'l' +EMPTY_LIST = ']' +OBJ = 'o' +PUT = 'p' +BINPUT = 'q' +LONG_BINPUT = 'r' +SETITEM = 's' +TUPLE = 't' +EMPTY_TUPLE = ')' +SETITEMS = 'u' +BINFLOAT = 'G' + +__all__.extend([x for x in dir() if re.match("[A-Z][A-Z0-9_]+$",x)]) + +class Pickler: + + def __init__(self, file, bin = 0): + self.write = file.write + self.memo = {} + self.bin = bin + + def dump(self, object): + self.save(object) + self.write(STOP) + + def put(self, i): + if self.bin: + s = mdumps(i)[1:] + if i < 256: + return BINPUT + s[0] + + return LONG_BINPUT + s + + return PUT + `i` + '\n' + + def get(self, i): + if self.bin: + s = mdumps(i)[1:] + + if i < 256: + return BINGET + s[0] + + return LONG_BINGET + s + + return GET + `i` + '\n' + + def save(self, object, pers_save = 0): + memo = self.memo + + if not pers_save: + pid = self.persistent_id(object) + if pid is not None: + self.save_pers(pid) + return + + d = id(object) + + t = type(object) + + if (t is TupleType) and (len(object) == 0): + if self.bin: + self.save_empty_tuple(object) + else: + self.save_tuple(object) + return + + if memo.has_key(d): + self.write(self.get(memo[d][0])) + return + + try: + f = self.dispatch[t] + except KeyError: + pid = self.inst_persistent_id(object) + if pid is not None: + self.save_pers(pid) + return + + try: + issc = issubclass(t, TypeType) + except TypeError: # t is not a class + issc = 0 + if issc: + self.save_global(object) + return + + try: + reduce = dispatch_table[t] + except KeyError: + try: + reduce = object.__reduce__ + except AttributeError: + raise PicklingError, \ + "can't pickle %s object: %s" % (`t.__name__`, + `object`) + else: + tup = reduce() + else: + tup = reduce(object) + + if type(tup) is StringType: + self.save_global(object, tup) + return + + if type(tup) is not TupleType: + raise PicklingError, "Value returned by %s must be a " \ + "tuple" % reduce + + l = len(tup) + + if (l != 2) and (l != 3): + raise PicklingError, "tuple returned by %s must contain " \ + "only two or three elements" % reduce + + callable = tup[0] + arg_tup = tup[1] + + if l > 2: + state = tup[2] + else: + state = None + + if type(arg_tup) is not TupleType and arg_tup is not None: + raise PicklingError, "Second element of tuple returned " \ + "by %s must be a tuple" % reduce + + self.save_reduce(callable, arg_tup, state) + memo_len = len(memo) + self.write(self.put(memo_len)) + memo[d] = (memo_len, object) + return + + f(self, object) + + def persistent_id(self, object): + return None + + def inst_persistent_id(self, object): + return None + + def save_pers(self, pid): + if not self.bin: + self.write(PERSID + str(pid) + '\n') + else: + self.save(pid, 1) + self.write(BINPERSID) + + def save_reduce(self, callable, arg_tup, state = None): + write = self.write + save = self.save + + save(callable) + save(arg_tup) + write(REDUCE) + + if state is not None: + save(state) + write(BUILD) + + dispatch = {} + + def save_none(self, object): + self.write(NONE) + dispatch[NoneType] = save_none + + def save_int(self, object): + if self.bin: + # If the int is small enough to fit in a signed 4-byte 2's-comp + # format, we can store it more efficiently than the general + # case. + high_bits = object >> 31 # note that Python shift sign-extends + if high_bits == 0 or high_bits == -1: + # All high bits are copies of bit 2**31, so the value + # fits in a 4-byte signed int. + i = mdumps(object)[1:] + assert len(i) == 4 + if i[-2:] == '\000\000': # fits in 2-byte unsigned int + if i[-3] == '\000': # fits in 1-byte unsigned int + self.write(BININT1 + i[0]) + else: + self.write(BININT2 + i[:2]) + else: + self.write(BININT + i) + return + # Text pickle, or int too big to fit in signed 4-byte format. + self.write(INT + `object` + '\n') + dispatch[IntType] = save_int + + def save_long(self, object): + self.write(LONG + `object` + '\n') + dispatch[LongType] = save_long + + def save_float(self, object, pack=struct.pack): + if self.bin: + self.write(BINFLOAT + pack('>d', object)) + else: + self.write(FLOAT + `object` + '\n') + dispatch[FloatType] = save_float + + def save_string(self, object): + d = id(object) + memo = self.memo + + if self.bin: + l = len(object) + s = mdumps(l)[1:] + if l < 256: + self.write(SHORT_BINSTRING + s[0] + object) + else: + self.write(BINSTRING + s + object) + else: + self.write(STRING + `object` + '\n') + + memo_len = len(memo) + self.write(self.put(memo_len)) + memo[d] = (memo_len, object) + dispatch[StringType] = save_string + + def save_unicode(self, object): + d = id(object) + memo = self.memo + + if self.bin: + encoding = object.encode('utf-8') + l = len(encoding) + s = mdumps(l)[1:] + self.write(BINUNICODE + s + encoding) + else: + object = object.replace("\\", "\\u005c") + object = object.replace("\n", "\\u000a") + self.write(UNICODE + object.encode('raw-unicode-escape') + '\n') + + memo_len = len(memo) + self.write(self.put(memo_len)) + memo[d] = (memo_len, object) + dispatch[UnicodeType] = save_unicode + + if StringType == UnicodeType: + # This is true for Jython + def save_string(self, object): + d = id(object) + memo = self.memo + unicode = object.isunicode() + + if self.bin: + if unicode: + object = object.encode("utf-8") + l = len(object) + s = mdumps(l)[1:] + if l < 256 and not unicode: + self.write(SHORT_BINSTRING + s[0] + object) + else: + if unicode: + self.write(BINUNICODE + s + object) + else: + self.write(BINSTRING + s + object) + else: + if unicode: + object = object.replace("\\", "\\u005c") + object = object.replace("\n", "\\u000a") + object = object.encode('raw-unicode-escape') + self.write(UNICODE + object + '\n') + else: + self.write(STRING + `object` + '\n') + + memo_len = len(memo) + self.write(self.put(memo_len)) + memo[d] = (memo_len, object) + dispatch[StringType] = save_string + + def save_tuple(self, object): + + write = self.write + save = self.save + memo = self.memo + + d = id(object) + + write(MARK) + + for element in object: + save(element) + + if len(object) and memo.has_key(d): + if self.bin: + write(POP_MARK + self.get(memo[d][0])) + return + + write(POP * (len(object) + 1) + self.get(memo[d][0])) + return + + memo_len = len(memo) + self.write(TUPLE + self.put(memo_len)) + memo[d] = (memo_len, object) + dispatch[TupleType] = save_tuple + + def save_empty_tuple(self, object): + self.write(EMPTY_TUPLE) + + def save_list(self, object): + d = id(object) + + write = self.write + save = self.save + memo = self.memo + + if self.bin: + write(EMPTY_LIST) + else: + write(MARK + LIST) + + memo_len = len(memo) + write(self.put(memo_len)) + memo[d] = (memo_len, object) + + using_appends = (self.bin and (len(object) > 1)) + + if using_appends: + write(MARK) + + for element in object: + save(element) + + if not using_appends: + write(APPEND) + + if using_appends: + write(APPENDS) + dispatch[ListType] = save_list + + def save_dict(self, object): + d = id(object) + + write = self.write + save = self.save + memo = self.memo + + if self.bin: + write(EMPTY_DICT) + else: + write(MARK + DICT) + + memo_len = len(memo) + self.write(self.put(memo_len)) + memo[d] = (memo_len, object) + + using_setitems = (self.bin and (len(object) > 1)) + + if using_setitems: + write(MARK) + + items = object.items() + for key, value in items: + save(key) + save(value) + + if not using_setitems: + write(SETITEM) + + if using_setitems: + write(SETITEMS) + + dispatch[DictionaryType] = save_dict + if not PyStringMap is None: + dispatch[PyStringMap] = save_dict + + def save_inst(self, object): + d = id(object) + cls = object.__class__ + + memo = self.memo + write = self.write + save = self.save + + if hasattr(object, '__getinitargs__'): + args = object.__getinitargs__() + len(args) # XXX Assert it's a sequence + _keep_alive(args, memo) + else: + args = () + + write(MARK) + + if self.bin: + save(cls) + + for arg in args: + save(arg) + + memo_len = len(memo) + if self.bin: + write(OBJ + self.put(memo_len)) + else: + write(INST + cls.__module__ + '\n' + cls.__name__ + '\n' + + self.put(memo_len)) + + memo[d] = (memo_len, object) + + try: + getstate = object.__getstate__ + except AttributeError: + stuff = object.__dict__ + else: + stuff = getstate() + _keep_alive(stuff, memo) + save(stuff) + write(BUILD) + dispatch[InstanceType] = save_inst + + def save_global(self, object, name = None): + write = self.write + memo = self.memo + + if name is None: + name = object.__name__ + + try: + module = object.__module__ + except AttributeError: + module = whichmodule(object, name) + + try: + __import__(module) + mod = sys.modules[module] + klass = getattr(mod, name) + except (ImportError, KeyError, AttributeError): + raise PicklingError( + "Can't pickle %r: it's not found as %s.%s" % + (object, module, name)) + else: + if klass is not object: + raise PicklingError( + "Can't pickle %r: it's not the same object as %s.%s" % + (object, module, name)) + + memo_len = len(memo) + write(GLOBAL + module + '\n' + name + '\n' + + self.put(memo_len)) + memo[id(object)] = (memo_len, object) + dispatch[ClassType] = save_global + dispatch[FunctionType] = save_global + dispatch[BuiltinFunctionType] = save_global + dispatch[TypeType] = save_global + + +def _keep_alive(x, memo): + """Keeps a reference to the object x in the memo. + + Because we remember objects by their id, we have + to assure that possibly temporary objects are kept + alive by referencing them. + We store a reference at the id of the memo, which should + normally not be used unless someone tries to deepcopy + the memo itself... + """ + try: + memo[id(memo)].append(x) + except KeyError: + # aha, this is the first one :-) + memo[id(memo)]=[x] + + +classmap = {} # called classmap for backwards compatibility + +def whichmodule(func, funcname): + """Figure out the module in which a function occurs. + + Search sys.modules for the module. + Cache in classmap. + Return a module name. + If the function cannot be found, return __main__. + """ + if classmap.has_key(func): + return classmap[func] + + for name, module in sys.modules.items(): + if module is None: + continue # skip dummy package entries + if name != '__main__' and \ + hasattr(module, funcname) and \ + getattr(module, funcname) is func: + break + else: + name = '__main__' + classmap[func] = name + return name + + +class Unpickler: + + def __init__(self, file): + self.readline = file.readline + self.read = file.read + self.memo = {} + + def load(self): + self.mark = object() # any new unique object + self.stack = [] + self.append = self.stack.append + read = self.read + dispatch = self.dispatch + try: + while 1: + key = read(1) + dispatch[key](self) + except _Stop, stopinst: + return stopinst.value + + def marker(self): + stack = self.stack + mark = self.mark + k = len(stack)-1 + while stack[k] is not mark: k = k-1 + return k + + dispatch = {} + + def load_eof(self): + raise EOFError + dispatch[''] = load_eof + + def load_persid(self): + pid = self.readline()[:-1] + self.append(self.persistent_load(pid)) + dispatch[PERSID] = load_persid + + def load_binpersid(self): + stack = self.stack + + pid = stack[-1] + del stack[-1] + + self.append(self.persistent_load(pid)) + dispatch[BINPERSID] = load_binpersid + + def load_none(self): + self.append(None) + dispatch[NONE] = load_none + + def load_int(self): + data = self.readline() + try: + self.append(int(data)) + except ValueError: + self.append(long(data)) + dispatch[INT] = load_int + + def load_binint(self): + self.append(mloads('i' + self.read(4))) + dispatch[BININT] = load_binint + + def load_binint1(self): + self.append(mloads('i' + self.read(1) + '\000\000\000')) + dispatch[BININT1] = load_binint1 + + def load_binint2(self): + self.append(mloads('i' + self.read(2) + '\000\000')) + dispatch[BININT2] = load_binint2 + + def load_long(self): + self.append(long(self.readline()[:-1], 0)) + dispatch[LONG] = load_long + + def load_float(self): + self.append(float(self.readline()[:-1])) + dispatch[FLOAT] = load_float + + def load_binfloat(self, unpack=struct.unpack): + self.append(unpack('>d', self.read(8))[0]) + dispatch[BINFLOAT] = load_binfloat + + def load_string(self): + rep = self.readline()[:-1] + if not self._is_string_secure(rep): + raise ValueError, "insecure string pickle" + self.append(eval(rep, + {'__builtins__': {}})) # Let's be careful + dispatch[STRING] = load_string + + def _is_string_secure(self, s): + """Return true if s contains a string that is safe to eval + + The definition of secure string is based on the implementation + in cPickle. s is secure as long as it only contains a quoted + string and optional trailing whitespace. + """ + q = s[0] + if q not in ("'", '"'): + return 0 + # find the closing quote + offset = 1 + i = None + while 1: + try: + i = s.index(q, offset) + except ValueError: + # if there is an error the first time, there is no + # close quote + if offset == 1: + return 0 + if s[i-1] != '\\': + break + # check to see if this one is escaped + nslash = 0 + j = i - 1 + while j >= offset and s[j] == '\\': + j = j - 1 + nslash = nslash + 1 + if nslash % 2 == 0: + break + offset = i + 1 + for c in s[i+1:]: + if ord(c) > 32: + return 0 + return 1 + + def load_binstring(self): + len = mloads('i' + self.read(4)) + self.append(self.read(len)) + dispatch[BINSTRING] = load_binstring + + def load_unicode(self): + self.append(unicode(self.readline()[:-1],'raw-unicode-escape')) + dispatch[UNICODE] = load_unicode + + def load_binunicode(self): + len = mloads('i' + self.read(4)) + self.append(unicode(self.read(len),'utf-8')) + dispatch[BINUNICODE] = load_binunicode + + def load_short_binstring(self): + len = mloads('i' + self.read(1) + '\000\000\000') + self.append(self.read(len)) + dispatch[SHORT_BINSTRING] = load_short_binstring + + def load_tuple(self): + k = self.marker() + self.stack[k:] = [tuple(self.stack[k+1:])] + dispatch[TUPLE] = load_tuple + + def load_empty_tuple(self): + self.stack.append(()) + dispatch[EMPTY_TUPLE] = load_empty_tuple + + def load_empty_list(self): + self.stack.append([]) + dispatch[EMPTY_LIST] = load_empty_list + + def load_empty_dictionary(self): + self.stack.append({}) + dispatch[EMPTY_DICT] = load_empty_dictionary + + def load_list(self): + k = self.marker() + self.stack[k:] = [self.stack[k+1:]] + dispatch[LIST] = load_list + + def load_dict(self): + k = self.marker() + d = {} + items = self.stack[k+1:] + for i in range(0, len(items), 2): + key = items[i] + value = items[i+1] + d[key] = value + self.stack[k:] = [d] + dispatch[DICT] = load_dict + + def load_inst(self): + k = self.marker() + args = tuple(self.stack[k+1:]) + del self.stack[k:] + module = self.readline()[:-1] + name = self.readline()[:-1] + klass = self.find_class(module, name) + instantiated = 0 + if (not args and type(klass) is ClassType and + not hasattr(klass, "__getinitargs__")): + try: + value = _EmptyClass() + value.__class__ = klass + instantiated = 1 + except RuntimeError: + # In restricted execution, assignment to inst.__class__ is + # prohibited + pass + if not instantiated: + try: + if not hasattr(klass, '__safe_for_unpickling__'): + raise UnpicklingError('%s is not safe for unpickling' % + klass) + value = apply(klass, args) + except TypeError, err: + raise TypeError, "in constructor for %s: %s" % ( + klass.__name__, str(err)), sys.exc_info()[2] + self.append(value) + dispatch[INST] = load_inst + + def load_obj(self): + stack = self.stack + k = self.marker() + klass = stack[k + 1] + del stack[k + 1] + args = tuple(stack[k + 1:]) + del stack[k:] + instantiated = 0 + if (not args and type(klass) is ClassType and + not hasattr(klass, "__getinitargs__")): + try: + value = _EmptyClass() + value.__class__ = klass + instantiated = 1 + except RuntimeError: + # In restricted execution, assignment to inst.__class__ is + # prohibited + pass + if not instantiated: + value = apply(klass, args) + self.append(value) + dispatch[OBJ] = load_obj + + def load_global(self): + module = self.readline()[:-1] + name = self.readline()[:-1] + klass = self.find_class(module, name) + self.append(klass) + dispatch[GLOBAL] = load_global + + def find_class(self, module, name): + __import__(module) + mod = sys.modules[module] + klass = getattr(mod, name) + return klass + + def load_reduce(self): + stack = self.stack + + callable = stack[-2] + arg_tup = stack[-1] + del stack[-2:] + + if type(callable) is not ClassType: + if not safe_constructors.has_key(callable): + try: + safe = callable.__safe_for_unpickling__ + except AttributeError: + safe = None + + if not safe: + raise UnpicklingError, "%s is not safe for " \ + "unpickling" % callable + + if arg_tup is None: + value = callable.__basicnew__() + else: + value = apply(callable, arg_tup) + self.append(value) + dispatch[REDUCE] = load_reduce + + def load_pop(self): + del self.stack[-1] + dispatch[POP] = load_pop + + def load_pop_mark(self): + k = self.marker() + del self.stack[k:] + dispatch[POP_MARK] = load_pop_mark + + def load_dup(self): + self.append(self.stack[-1]) + dispatch[DUP] = load_dup + + def load_get(self): + self.append(self.memo[self.readline()[:-1]]) + dispatch[GET] = load_get + + def load_binget(self): + i = mloads('i' + self.read(1) + '\000\000\000') + self.append(self.memo[`i`]) + dispatch[BINGET] = load_binget + + def load_long_binget(self): + i = mloads('i' + self.read(4)) + self.append(self.memo[`i`]) + dispatch[LONG_BINGET] = load_long_binget + + def load_put(self): + self.memo[self.readline()[:-1]] = self.stack[-1] + dispatch[PUT] = load_put + + def load_binput(self): + i = mloads('i' + self.read(1) + '\000\000\000') + self.memo[`i`] = self.stack[-1] + dispatch[BINPUT] = load_binput + + def load_long_binput(self): + i = mloads('i' + self.read(4)) + self.memo[`i`] = self.stack[-1] + dispatch[LONG_BINPUT] = load_long_binput + + def load_append(self): + stack = self.stack + value = stack[-1] + del stack[-1] + list = stack[-1] + list.append(value) + dispatch[APPEND] = load_append + + def load_appends(self): + stack = self.stack + mark = self.marker() + list = stack[mark - 1] + for i in range(mark + 1, len(stack)): + list.append(stack[i]) + + del stack[mark:] + dispatch[APPENDS] = load_appends + + def load_setitem(self): + stack = self.stack + value = stack[-1] + key = stack[-2] + del stack[-2:] + dict = stack[-1] + dict[key] = value + dispatch[SETITEM] = load_setitem + + def load_setitems(self): + stack = self.stack + mark = self.marker() + dict = stack[mark - 1] + for i in range(mark + 1, len(stack), 2): + dict[stack[i]] = stack[i + 1] + + del stack[mark:] + dispatch[SETITEMS] = load_setitems + + def load_build(self): + stack = self.stack + value = stack[-1] + del stack[-1] + inst = stack[-1] + try: + setstate = inst.__setstate__ + except AttributeError: + try: + inst.__dict__.update(value) + except RuntimeError: + # XXX In restricted execution, the instance's __dict__ is not + # accessible. Use the old way of unpickling the instance + # variables. This is a semantic different when unpickling in + # restricted vs. unrestricted modes. + for k, v in value.items(): + setattr(inst, k, v) + else: + setstate(value) + dispatch[BUILD] = load_build + + def load_mark(self): + self.append(self.mark) + dispatch[MARK] = load_mark + + def load_stop(self): + value = self.stack[-1] + del self.stack[-1] + raise _Stop(value) + dispatch[STOP] = load_stop + +# Helper class for load_inst/load_obj + +class _EmptyClass: + pass + +# Shorthands + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +def dump(object, file, bin = 0): + Pickler(file, bin).dump(object) + +def dumps(object, bin = 0): + file = StringIO() + Pickler(file, bin).dump(object) + return file.getvalue() + +def load(file): + return Unpickler(file).load() + +def loads(str): + file = StringIO(str) + return Unpickler(file).load() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/popen2.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/popen2.py new file mode 100644 index 00000000..1d067ce7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/popen2.py @@ -0,0 +1,199 @@ +"""Spawn a command with pipes to its stdin, stdout, and optionally stderr. + +The normal os.popen(cmd, mode) call spawns a shell command and provides a +file interface to just the input or output of the process depending on +whether mode is 'r' or 'w'. This module provides the functions popen2(cmd) +and popen3(cmd) which return two or three pipes to the spawned command. +""" + +import os +import sys +import types + +__all__ = ["popen2", "popen3", "popen4"] + +MAXFD = 256 # Max number of file descriptors (os.getdtablesize()???) + +_active = [] + +def _cleanup(): + for inst in _active[:]: + inst.poll() + +class Popen3: + """Class representing a child process. Normally instances are created + by the factory functions popen2() and popen3().""" + + sts = -1 # Child not completed yet + + def __init__(self, cmd, capturestderr=0, bufsize=-1): + """The parameter 'cmd' is the shell command to execute in a + sub-process. The 'capturestderr' flag, if true, specifies that + the object should capture standard error output of the child process. + The default is false. If the 'bufsize' parameter is specified, it + specifies the size of the I/O buffers to/from the child process.""" + _cleanup() + p2cread, p2cwrite = os.pipe() + c2pread, c2pwrite = os.pipe() + if capturestderr: + errout, errin = os.pipe() + self.pid = os.fork() + if self.pid == 0: + # Child + os.dup2(p2cread, 0) + os.dup2(c2pwrite, 1) + if capturestderr: + os.dup2(errin, 2) + self._run_child(cmd) + os.close(p2cread) + self.tochild = os.fdopen(p2cwrite, 'w', bufsize) + os.close(c2pwrite) + self.fromchild = os.fdopen(c2pread, 'r', bufsize) + if capturestderr: + os.close(errin) + self.childerr = os.fdopen(errout, 'r', bufsize) + else: + self.childerr = None + _active.append(self) + + def _run_child(self, cmd): + if isinstance(cmd, types.StringTypes): + cmd = ['/bin/sh', '-c', cmd] + for i in range(3, MAXFD): + try: + os.close(i) + except: + pass + try: + os.execvp(cmd[0], cmd) + finally: + os._exit(1) + + def poll(self): + """Return the exit status of the child process if it has finished, + or -1 if it hasn't finished yet.""" + if self.sts < 0: + try: + pid, sts = os.waitpid(self.pid, os.WNOHANG) + if pid == self.pid: + self.sts = sts + _active.remove(self) + except os.error: + pass + return self.sts + + def wait(self): + """Wait for and return the exit status of the child process.""" + pid, sts = os.waitpid(self.pid, 0) + if pid == self.pid: + self.sts = sts + _active.remove(self) + return self.sts + + +class Popen4(Popen3): + childerr = None + + def __init__(self, cmd, bufsize=-1): + _cleanup() + p2cread, p2cwrite = os.pipe() + c2pread, c2pwrite = os.pipe() + self.pid = os.fork() + if self.pid == 0: + # Child + os.dup2(p2cread, 0) + os.dup2(c2pwrite, 1) + os.dup2(c2pwrite, 2) + self._run_child(cmd) + os.close(p2cread) + self.tochild = os.fdopen(p2cwrite, 'w', bufsize) + os.close(c2pwrite) + self.fromchild = os.fdopen(c2pread, 'r', bufsize) + _active.append(self) + + +if sys.platform[:3] == "win": + # Some things don't make sense on non-Unix platforms. + del Popen3, Popen4 + + def popen2(cmd, bufsize=-1, mode='t'): + """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is + specified, it sets the buffer size for the I/O pipes. The file objects + (child_stdout, child_stdin) are returned.""" + w, r = os.popen2(cmd, mode, bufsize) + return r, w + + def popen3(cmd, bufsize=-1, mode='t'): + """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is + specified, it sets the buffer size for the I/O pipes. The file objects + (child_stdout, child_stdin, child_stderr) are returned.""" + w, r, e = os.popen3(cmd, mode, bufsize) + return r, w, e + + def popen4(cmd, bufsize=-1, mode='t'): + """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is + specified, it sets the buffer size for the I/O pipes. The file objects + (child_stdout_stderr, child_stdin) are returned.""" + w, r = os.popen4(cmd, mode, bufsize) + return r, w +else: + def popen2(cmd, bufsize=-1, mode='t'): + """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is + specified, it sets the buffer size for the I/O pipes. The file objects + (child_stdout, child_stdin) are returned.""" + inst = Popen3(cmd, 0, bufsize) + return inst.fromchild, inst.tochild + + def popen3(cmd, bufsize=-1, mode='t'): + """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is + specified, it sets the buffer size for the I/O pipes. The file objects + (child_stdout, child_stdin, child_stderr) are returned.""" + inst = Popen3(cmd, 1, bufsize) + return inst.fromchild, inst.tochild, inst.childerr + + def popen4(cmd, bufsize=-1, mode='t'): + """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is + specified, it sets the buffer size for the I/O pipes. The file objects + (child_stdout_stderr, child_stdin) are returned.""" + inst = Popen4(cmd, bufsize) + return inst.fromchild, inst.tochild + + __all__.extend(["Popen3", "Popen4"]) + +def _test(): + cmd = "cat" + teststr = "ab cd\n" + if os.name == "nt": + cmd = "more" + # "more" doesn't act the same way across Windows flavors, + # sometimes adding an extra newline at the start or the + # end. So we strip whitespace off both ends for comparison. + expected = teststr.strip() + print "testing popen2..." + r, w = popen2(cmd) + w.write(teststr) + w.close() + got = r.read() + if got.strip() != expected: + raise ValueError("wrote %s read %s" % (`teststr`, `got`)) + print "testing popen3..." + try: + r, w, e = popen3([cmd]) + except: + r, w, e = popen3(cmd) + w.write(teststr) + w.close() + got = r.read() + if got.strip() != expected: + raise ValueError("wrote %s read %s" % (`teststr`, `got`)) + got = e.read() + if got: + raise ValueError("unexected %s on stderr" % `got`) + for inst in _active[:]: + inst.wait() + if _active: + raise ValueError("_active not empty") + print "All OK" + +if __name__ == '__main__': + _test() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/posixpath.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/posixpath.py new file mode 100644 index 00000000..b165aef5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/posixpath.py @@ -0,0 +1,414 @@ +"""Common operations on Posix pathnames. + +Instead of importing this module directly, import os and refer to +this module as os.path. The "os.path" name is an alias for this +module on Posix systems; on other systems (e.g. Mac, Windows), +os.path provides the same operations in a manner specific to that +platform, and is an alias to another module (e.g. macpath, ntpath). + +Some of this can actually be useful on non-Posix systems too, e.g. +for manipulation of the pathname component of URLs. +""" + +import os +import stat + +__all__ = ["normcase","isabs","join","splitdrive","split","splitext", + "basename","dirname","commonprefix","getsize","getmtime", + "getatime","islink","exists","isdir","isfile","ismount", + "walk","expanduser","expandvars","normpath","abspath", + "samefile","sameopenfile","samestat"] + +# Normalize the case of a pathname. Trivial in Posix, string.lower on Mac. +# On MS-DOS this may also turn slashes into backslashes; however, other +# normalizations (such as optimizing '../' away) are not allowed +# (another function should be defined to do that). + +def normcase(s): + """Normalize case of pathname. Has no effect under Posix""" + return s + + +# Return whether a path is absolute. +# Trivial in Posix, harder on the Mac or MS-DOS. + +def isabs(s): + """Test whether a path is absolute""" + return s[:1] == '/' + + +# Join pathnames. +# Ignore the previous parts if a part is absolute. +# Insert a '/' unless the first part is empty or already ends in '/'. + +def join(a, *p): + """Join two or more pathname components, inserting '/' as needed""" + path = a + for b in p: + if b[:1] == '/': + path = b + elif path == '' or path[-1:] == '/': + path = path + b + else: + path = path + '/' + b + return path + + +# Split a path in head (everything up to the last '/') and tail (the +# rest). If the path ends in '/', tail will be empty. If there is no +# '/' in the path, head will be empty. +# Trailing '/'es are stripped from head unless it is the root. + +def split(p): + """Split a pathname. Returns tuple "(head, tail)" where "tail" is + everything after the final slash. Either part may be empty.""" + i = p.rfind('/') + 1 + head, tail = p[:i], p[i:] + if head and head != '/'*len(head): + while head[-1] == '/': + head = head[:-1] + return head, tail + + +# Split a path in root and extension. +# The extension is everything starting at the last dot in the last +# pathname component; the root is everything before that. +# It is always true that root + ext == p. + +def splitext(p): + """Split the extension from a pathname. Extension is everything from the + last dot to the end. Returns "(root, ext)", either part may be empty.""" + root, ext = '', '' + for c in p: + if c == '/': + root, ext = root + ext + c, '' + elif c == '.': + if ext: + root, ext = root + ext, c + else: + ext = c + elif ext: + ext = ext + c + else: + root = root + c + return root, ext + + +# Split a pathname into a drive specification and the rest of the +# path. Useful on DOS/Windows/NT; on Unix, the drive is always empty. + +def splitdrive(p): + """Split a pathname into drive and path. On Posix, drive is always + empty.""" + return '', p + + +# Return the tail (basename) part of a path. + +def basename(p): + """Returns the final component of a pathname""" + return split(p)[1] + + +# Return the head (dirname) part of a path. + +def dirname(p): + """Returns the directory component of a pathname""" + return split(p)[0] + + +# Return the longest prefix of all list elements. + +def commonprefix(m): + "Given a list of pathnames, returns the longest common leading component" + if not m: return '' + prefix = m[0] + for item in m: + for i in range(len(prefix)): + if prefix[:i+1] != item[:i+1]: + prefix = prefix[:i] + if i == 0: return '' + break + return prefix + + +# Get size, mtime, atime of files. + +def getsize(filename): + """Return the size of a file, reported by os.stat().""" + st = os.stat(filename) + return st[stat.ST_SIZE] + +def getmtime(filename): + """Return the last modification time of a file, reported by os.stat().""" + st = os.stat(filename) + return st[stat.ST_MTIME] + +def getatime(filename): + """Return the last access time of a file, reported by os.stat().""" + st = os.stat(filename) + return st[stat.ST_ATIME] + + +# Is a path a symbolic link? +# This will always return false on systems where os.lstat doesn't exist. + +def islink(path): + """Test whether a path is a symbolic link""" + try: + st = os.lstat(path) + except (os.error, AttributeError): + return 0 + return stat.S_ISLNK(st[stat.ST_MODE]) + + +# Does a path exist? +# This is false for dangling symbolic links. + +def exists(path): + """Test whether a path exists. Returns false for broken symbolic links""" + try: + st = os.stat(path) + except os.error: + return 0 + return 1 + + +# Is a path a directory? +# This follows symbolic links, so both islink() and isdir() can be true +# for the same path. + +def isdir(path): + """Test whether a path is a directory""" + try: + st = os.stat(path) + except os.error: + return 0 + return stat.S_ISDIR(st[stat.ST_MODE]) + + +# Is a path a regular file? +# This follows symbolic links, so both islink() and isfile() can be true +# for the same path. + +def isfile(path): + """Test whether a path is a regular file""" + try: + st = os.stat(path) + except os.error: + return 0 + return stat.S_ISREG(st[stat.ST_MODE]) + + +# Are two filenames really pointing to the same file? + +def samefile(f1, f2): + """Test whether two pathnames reference the same actual file""" + s1 = os.stat(f1) + s2 = os.stat(f2) + return samestat(s1, s2) + + +# Are two open files really referencing the same file? +# (Not necessarily the same file descriptor!) + +def sameopenfile(fp1, fp2): + """Test whether two open file objects reference the same file""" + s1 = os.fstat(fp1) + s2 = os.fstat(fp2) + return samestat(s1, s2) + + +# Are two stat buffers (obtained from stat, fstat or lstat) +# describing the same file? + +def samestat(s1, s2): + """Test whether two stat buffers reference the same file""" + return s1[stat.ST_INO] == s2[stat.ST_INO] and \ + s1[stat.ST_DEV] == s2[stat.ST_DEV] + + +# Is a path a mount point? +# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?) + +def ismount(path): + """Test whether a path is a mount point""" + try: + s1 = os.stat(path) + s2 = os.stat(join(path, '..')) + except os.error: + return 0 # It doesn't exist -- so not a mount point :-) + dev1 = s1[stat.ST_DEV] + dev2 = s2[stat.ST_DEV] + if dev1 != dev2: + return 1 # path/.. on a different device as path + ino1 = s1[stat.ST_INO] + ino2 = s2[stat.ST_INO] + if ino1 == ino2: + return 1 # path/.. is the same i-node as path + return 0 + + +# Directory tree walk. +# For each directory under top (including top itself, but excluding +# '.' and '..'), func(arg, dirname, filenames) is called, where +# dirname is the name of the directory and filenames is the list +# of files (and subdirectories etc.) in the directory. +# The func may modify the filenames list, to implement a filter, +# or to impose a different order of visiting. + +def walk(top, func, arg): + """Directory tree walk with callback function. + + For each directory in the directory tree rooted at top (including top + itself, but excluding '.' and '..'), call func(arg, dirname, fnames). + dirname is the name of the directory, and fnames a list of the names of + the files and subdirectories in dirname (excluding '.' and '..'). func + may modify the fnames list in-place (e.g. via del or slice assignment), + and walk will only recurse into the subdirectories whose names remain in + fnames; this can be used to implement a filter, or to impose a specific + order of visiting. No semantics are defined for, or required of, arg, + beyond that arg is always passed to func. It can be used, e.g., to pass + a filename pattern, or a mutable object designed to accumulate + statistics. Passing None for arg is common.""" + + try: + names = os.listdir(top) + except os.error: + return + func(arg, top, names) + for name in names: + name = join(top, name) + try: + st = os.lstat(name) + except os.error: + continue + if stat.S_ISDIR(st[stat.ST_MODE]): + walk(name, func, arg) + + +# Expand paths beginning with '~' or '~user'. +# '~' means $HOME; '~user' means that user's home directory. +# If the path doesn't begin with '~', or if the user or $HOME is unknown, +# the path is returned unchanged (leaving error reporting to whatever +# function is called with the expanded path as argument). +# See also module 'glob' for expansion of *, ? and [...] in pathnames. +# (A function should also be defined to do full *sh-style environment +# variable expansion.) + +def expanduser(path): + """Expand ~ and ~user constructions. If user or $HOME is unknown, + do nothing.""" + if path[:1] != '~': + return path + i, n = 1, len(path) + while i < n and path[i] != '/': + i = i + 1 + if i == 1: + if not os.environ.has_key('HOME'): + import pwd + userhome = pwd.getpwuid(os.getuid())[5] + else: + userhome = os.environ['HOME'] + else: + import pwd + try: + pwent = pwd.getpwnam(path[1:i]) + except KeyError: + return path + userhome = pwent[5] + if userhome[-1:] == '/': i = i + 1 + return userhome + path[i:] + + +# Expand paths containing shell variable substitutions. +# This expands the forms $variable and ${variable} only. +# Non-existent variables are left unchanged. + +_varprog = None + +def expandvars(path): + """Expand shell variables of form $var and ${var}. Unknown variables + are left unchanged.""" + global _varprog + if '$' not in path: + return path + if not _varprog: + import re + _varprog = re.compile(r'\$(\w+|\{[^}]*\})') + i = 0 + while 1: + m = _varprog.search(path, i) + if not m: + break + i, j = m.span(0) + name = m.group(1) + if name[:1] == '{' and name[-1:] == '}': + name = name[1:-1] + if os.environ.has_key(name): + tail = path[j:] + path = path[:i] + os.environ[name] + i = len(path) + path = path + tail + else: + i = j + return path + + +# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B. +# It should be understood that this may change the meaning of the path +# if it contains symbolic links! + +def normpath(path): + """Normalize path, eliminating double slashes, etc.""" + if path == '': + return '.' + initial_slashes = path.startswith('/') + # POSIX allows one or two initial slashes, but treats three or more + # as single slash. + if (initial_slashes and + path.startswith('//') and not path.startswith('///')): + initial_slashes = 2 + comps = path.split('/') + new_comps = [] + for comp in comps: + if comp in ('', '.'): + continue + if (comp != '..' or (not initial_slashes and not new_comps) or + (new_comps and new_comps[-1] == '..')): + new_comps.append(comp) + elif new_comps: + new_comps.pop() + comps = new_comps + path = '/'.join(comps) + if initial_slashes: + path = '/'*initial_slashes + path + return path or '.' + + +def abspath(path): + """Return an absolute path.""" + if not isabs(path): + path = join(os.getcwd(), path) + return normpath(path) + + +# Return a canonical path (i.e. the absolute location of a file on the +# filesystem). + +def realpath(filename): + """Return the canonical path of the specified filename, eliminating any +symbolic links encountered in the path.""" + filename = abspath(filename) + + bits = ['/'] + filename.split('/')[1:] + for i in range(2, len(bits)+1): + component = join(*bits[0:i]) + if islink(component): + resolved = os.readlink(component) + (dir, file) = split(component) + resolved = normpath(join(dir, resolved)) + newpath = join(*([resolved] + bits[i:])) + return realpath(newpath) + + return filename diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pre.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pre.py new file mode 100644 index 00000000..a4c38e4c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pre.py @@ -0,0 +1,656 @@ +# module 're' -- A collection of regular expression operations + +r"""Support for regular expressions (RE). + +This module provides regular expression matching operations similar to +those found in Perl. It's 8-bit clean: the strings being processed may +contain both null bytes and characters whose high bit is set. Regular +expression pattern strings may not contain null bytes, but can specify +the null byte using the \\number notation. Characters with the high +bit set may be included. + +Regular expressions can contain both special and ordinary +characters. Most ordinary characters, like "A", "a", or "0", are the +simplest regular expressions; they simply match themselves. You can +concatenate ordinary characters, so last matches the string 'last'. + +The special characters are: + "." Matches any character except a newline. + "^" Matches the start of the string. + "$" Matches the end of the string. + "*" Matches 0 or more (greedy) repetitions of the preceding RE. + Greedy means that it will match as many repetitions as possible. + "+" Matches 1 or more (greedy) repetitions of the preceding RE. + "?" Matches 0 or 1 (greedy) of the preceding RE. + *?,+?,?? Non-greedy versions of the previous three special characters. + {m,n} Matches from m to n repetitions of the preceding RE. + {m,n}? Non-greedy version of the above. + "\\" Either escapes special characters or signals a special sequence. + [] Indicates a set of characters. + A "^" as the first character indicates a complementing set. + "|" A|B, creates an RE that will match either A or B. + (...) Matches the RE inside the parentheses. + The contents can be retrieved or matched later in the string. + (?iLmsx) Set the I, L, M, S, or X flag for the RE. + (?:...) Non-grouping version of regular parentheses. + (?P...) The substring matched by the group is accessible by name. + (?P=name) Matches the text matched earlier by the group named name. + (?#...) A comment; ignored. + (?=...) Matches if ... matches next, but doesn't consume the string. + (?!...) Matches if ... doesn't match next. + +The special sequences consist of "\\" and a character from the list +below. If the ordinary character is not on the list, then the +resulting RE will match the second character. + \\number Matches the contents of the group of the same number. + \\A Matches only at the start of the string. + \\Z Matches only at the end of the string. + \\b Matches the empty string, but only at the start or end of a word. + \\B Matches the empty string, but not at the start or end of a word. + \\d Matches any decimal digit; equivalent to the set [0-9]. + \\D Matches any non-digit character; equivalent to the set [^0-9]. + \\s Matches any whitespace character; equivalent to [ \\t\\n\\r\\f\\v]. + \\S Matches any non-whitespace character; equiv. to [^ \\t\\n\\r\\f\\v]. + \\w Matches any alphanumeric character; equivalent to [a-zA-Z0-9_]. + With LOCALE, it will match the set [0-9_] plus characters defined + as letters for the current locale. + \\W Matches the complement of \\w. + \\\\ Matches a literal backslash. + +This module exports the following functions: + match Match a regular expression pattern to the beginning of a string. + search Search a string for the presence of a pattern. + sub Substitute occurrences of a pattern found in a string. + subn Same as sub, but also return the number of substitutions made. + split Split a string by the occurrences of a pattern. + findall Find all occurrences of a pattern in a string. + compile Compile a pattern into a RegexObject. + escape Backslash all non-alphanumerics in a string. + +This module exports the following classes: + RegexObject Holds a compiled regular expression pattern. + MatchObject Contains information about pattern matches. + +Some of the functions in this module takes flags as optional parameters: + I IGNORECASE Perform case-insensitive matching. + L LOCALE Make \w, \W, \b, \B, dependent on the current locale. + M MULTILINE "^" matches the beginning of lines as well as the string. + "$" matches the end of lines as well as the string. + S DOTALL "." matches any character at all, including the newline. + X VERBOSE Ignore whitespace and comments for nicer looking RE's. + +This module also defines an exception 'error'. + +""" + + +import sys +from pcre import * + +__all__ = ["match","search","sub","subn","split","findall","escape","compile", + "I","L","M","S","X","IGNORECASE","LOCALE","MULTILINE","DOTALL", + "VERBOSE","error"] + +# +# First, the public part of the interface: +# + +# pcre.error and re.error should be the same, since exceptions can be +# raised from either module. + +# compilation flags + +I = IGNORECASE +L = LOCALE +M = MULTILINE +S = DOTALL +X = VERBOSE + + +# +# +# + +_cache = {} +_MAXCACHE = 20 + +def _cachecompile(pattern, flags=0): + key = (pattern, flags) + try: + return _cache[key] + except KeyError: + pass + value = compile(pattern, flags) + if len(_cache) >= _MAXCACHE: + _cache.clear() + _cache[key] = value + return value + +def match(pattern, string, flags=0): + """match (pattern, string[, flags]) -> MatchObject or None + + If zero or more characters at the beginning of string match the + regular expression pattern, return a corresponding MatchObject + instance. Return None if the string does not match the pattern; + note that this is different from a zero-length match. + + Note: If you want to locate a match anywhere in string, use + search() instead. + + """ + + return _cachecompile(pattern, flags).match(string) + +def search(pattern, string, flags=0): + """search (pattern, string[, flags]) -> MatchObject or None + + Scan through string looking for a location where the regular + expression pattern produces a match, and return a corresponding + MatchObject instance. Return None if no position in the string + matches the pattern; note that this is different from finding a + zero-length match at some point in the string. + + """ + return _cachecompile(pattern, flags).search(string) + +def sub(pattern, repl, string, count=0): + """sub(pattern, repl, string[, count=0]) -> string + + Return the string obtained by replacing the leftmost + non-overlapping occurrences of pattern in string by the + replacement repl. If the pattern isn't found, string is returned + unchanged. repl can be a string or a function; if a function, it + is called for every non-overlapping occurrence of pattern. The + function takes a single match object argument, and returns the + replacement string. + + The pattern may be a string or a regex object; if you need to + specify regular expression flags, you must use a regex object, or + use embedded modifiers in a pattern; e.g. + sub("(?i)b+", "x", "bbbb BBBB") returns 'x x'. + + The optional argument count is the maximum number of pattern + occurrences to be replaced; count must be a non-negative integer, + and the default value of 0 means to replace all occurrences. + + """ + if type(pattern) == type(''): + pattern = _cachecompile(pattern) + return pattern.sub(repl, string, count) + +def subn(pattern, repl, string, count=0): + """subn(pattern, repl, string[, count=0]) -> (string, num substitutions) + + Perform the same operation as sub(), but return a tuple + (new_string, number_of_subs_made). + + """ + if type(pattern) == type(''): + pattern = _cachecompile(pattern) + return pattern.subn(repl, string, count) + +def split(pattern, string, maxsplit=0): + """split(pattern, string[, maxsplit=0]) -> list of strings + + Split string by the occurrences of pattern. If capturing + parentheses are used in pattern, then the text of all groups in + the pattern are also returned as part of the resulting list. If + maxsplit is nonzero, at most maxsplit splits occur, and the + remainder of the string is returned as the final element of the + list. + + """ + if type(pattern) == type(''): + pattern = _cachecompile(pattern) + return pattern.split(string, maxsplit) + +def findall(pattern, string): + """findall(pattern, string) -> list + + Return a list of all non-overlapping matches of pattern in + string. If one or more groups are present in the pattern, return a + list of groups; this will be a list of tuples if the pattern has + more than one group. Empty matches are included in the result. + + """ + if type(pattern) == type(''): + pattern = _cachecompile(pattern) + return pattern.findall(string) + +def escape(pattern): + """escape(string) -> string + + Return string with all non-alphanumerics backslashed; this is + useful if you want to match an arbitrary literal string that may + have regular expression metacharacters in it. + + """ + result = list(pattern) + for i in range(len(pattern)): + char = pattern[i] + if not char.isalnum(): + if char=='\000': result[i] = '\\000' + else: result[i] = '\\'+char + return ''.join(result) + +def compile(pattern, flags=0): + """compile(pattern[, flags]) -> RegexObject + + Compile a regular expression pattern into a regular expression + object, which can be used for matching using its match() and + search() methods. + + """ + groupindex={} + code=pcre_compile(pattern, flags, groupindex) + return RegexObject(pattern, flags, code, groupindex) + + +# +# Class definitions +# + +class RegexObject: + """Holds a compiled regular expression pattern. + + Methods: + match Match the pattern to the beginning of a string. + search Search a string for the presence of the pattern. + sub Substitute occurrences of the pattern found in a string. + subn Same as sub, but also return the number of substitutions made. + split Split a string by the occurrences of the pattern. + findall Find all occurrences of the pattern in a string. + + """ + + def __init__(self, pattern, flags, code, groupindex): + self.code = code + self.flags = flags + self.pattern = pattern + self.groupindex = groupindex + + def search(self, string, pos=0, endpos=None): + """search(string[, pos][, endpos]) -> MatchObject or None + + Scan through string looking for a location where this regular + expression produces a match, and return a corresponding + MatchObject instance. Return None if no position in the string + matches the pattern; note that this is different from finding + a zero-length match at some point in the string. The optional + pos and endpos parameters have the same meaning as for the + match() method. + + """ + if endpos is None or endpos>len(string): + endpos=len(string) + if endpos MatchObject or None + + If zero or more characters at the beginning of string match + this regular expression, return a corresponding MatchObject + instance. Return None if the string does not match the + pattern; note that this is different from a zero-length match. + + Note: If you want to locate a match anywhere in string, use + search() instead. + + The optional second parameter pos gives an index in the string + where the search is to start; it defaults to 0. This is not + completely equivalent to slicing the string; the '' pattern + character matches at the real beginning of the string and at + positions just after a newline, but not necessarily at the + index where the search is to start. + + The optional parameter endpos limits how far the string will + be searched; it will be as if the string is endpos characters + long, so only the characters from pos to endpos will be + searched for a match. + + """ + if endpos is None or endpos>len(string): + endpos=len(string) + if endpos string + + Return the string obtained by replacing the leftmost + non-overlapping occurrences of the compiled pattern in string + by the replacement repl. If the pattern isn't found, string is + returned unchanged. + + Identical to the sub() function, using the compiled pattern. + + """ + return self.subn(repl, string, count)[0] + + def subn(self, repl, source, count=0): + """subn(repl, string[, count=0]) -> tuple + + Perform the same operation as sub(), but return a tuple + (new_string, number_of_subs_made). + + """ + if count < 0: + raise error, "negative substitution count" + if count == 0: + count = sys.maxint + n = 0 # Number of matches + pos = 0 # Where to start searching + lastmatch = -1 # End of last match + results = [] # Substrings making up the result + end = len(source) + + if type(repl) is type(''): + # See if repl contains group references (if it does, + # pcre_expand will attempt to call _Dummy.group, which + # results in a TypeError) + try: + repl = pcre_expand(_Dummy, repl) + except (error, TypeError): + m = MatchObject(self, source, 0, end, []) + repl = lambda m, repl=repl, expand=pcre_expand: expand(m, repl) + else: + m = None + else: + m = MatchObject(self, source, 0, end, []) + + match = self.code.match + append = results.append + while n < count and pos <= end: + regs = match(source, pos, end, 0) + if not regs: + break + self._num_regs = len(regs) + i, j = regs[0] + if i == j == lastmatch: + # Empty match adjacent to previous match + pos = pos + 1 + append(source[lastmatch:pos]) + continue + if pos < i: + append(source[pos:i]) + if m: + m.pos = pos + m.regs = regs + append(repl(m)) + else: + append(repl) + pos = lastmatch = j + if i == j: + # Last match was empty; don't try here again + pos = pos + 1 + append(source[lastmatch:pos]) + n = n + 1 + append(source[pos:]) + return (''.join(results), n) + + def split(self, source, maxsplit=0): + """split(source[, maxsplit=0]) -> list of strings + + Split string by the occurrences of the compiled pattern. If + capturing parentheses are used in the pattern, then the text + of all groups in the pattern are also returned as part of the + resulting list. If maxsplit is nonzero, at most maxsplit + splits occur, and the remainder of the string is returned as + the final element of the list. + + """ + if maxsplit < 0: + raise error, "negative split count" + if maxsplit == 0: + maxsplit = sys.maxint + n = 0 + pos = 0 + lastmatch = 0 + results = [] + end = len(source) + match = self.code.match + append = results.append + while n < maxsplit: + regs = match(source, pos, end, 0) + if not regs: + break + i, j = regs[0] + if i == j: + # Empty match + if pos >= end: + break + pos = pos+1 + continue + append(source[lastmatch:i]) + rest = regs[1:] + if rest: + for a, b in rest: + if a == -1 or b == -1: + group = None + else: + group = source[a:b] + append(group) + pos = lastmatch = j + n = n + 1 + append(source[lastmatch:]) + return results + + def findall(self, source): + """findall(source) -> list + + Return a list of all non-overlapping matches of the compiled + pattern in string. If one or more groups are present in the + pattern, return a list of groups; this will be a list of + tuples if the pattern has more than one group. Empty matches + are included in the result. + + """ + pos = 0 + end = len(source) + results = [] + match = self.code.match + append = results.append + while pos <= end: + regs = match(source, pos, end, 0) + if not regs: + break + i, j = regs[0] + rest = regs[1:] + if not rest: + gr = source[i:j] + elif len(rest) == 1: + a, b = rest[0] + gr = source[a:b] + else: + gr = [] + for (a, b) in rest: + gr.append(source[a:b]) + gr = tuple(gr) + append(gr) + pos = max(j, pos+1) + return results + + # The following 3 functions were contributed by Mike Fletcher, and + # allow pickling and unpickling of RegexObject instances. + def __getinitargs__(self): + return (None,None,None,None) # any 4 elements, to work around + # problems with the + # pickle/cPickle modules not yet + # ignoring the __init__ function + def __getstate__(self): + return self.pattern, self.flags, self.groupindex + def __setstate__(self, statetuple): + self.pattern = statetuple[0] + self.flags = statetuple[1] + self.groupindex = statetuple[2] + self.code = apply(pcre_compile, statetuple) + +class _Dummy: + # Dummy class used by _subn_string(). Has 'group' to avoid core dump. + group = None + +class MatchObject: + """Holds a compiled regular expression pattern. + + Methods: + start Return the index of the start of a matched substring. + end Return the index of the end of a matched substring. + span Return a tuple of (start, end) of a matched substring. + groups Return a tuple of all the subgroups of the match. + group Return one or more subgroups of the match. + groupdict Return a dictionary of all the named subgroups of the match. + + """ + + def __init__(self, re, string, pos, endpos, regs): + self.re = re + self.string = string + self.pos = pos + self.endpos = endpos + self.regs = regs + + def start(self, g = 0): + """start([group=0]) -> int or None + + Return the index of the start of the substring matched by + group; group defaults to zero (meaning the whole matched + substring). Return -1 if group exists but did not contribute + to the match. + + """ + if type(g) == type(''): + try: + g = self.re.groupindex[g] + except (KeyError, TypeError): + raise IndexError, 'group %s is undefined' % `g` + return self.regs[g][0] + + def end(self, g = 0): + """end([group=0]) -> int or None + + Return the indices of the end of the substring matched by + group; group defaults to zero (meaning the whole matched + substring). Return -1 if group exists but did not contribute + to the match. + + """ + if type(g) == type(''): + try: + g = self.re.groupindex[g] + except (KeyError, TypeError): + raise IndexError, 'group %s is undefined' % `g` + return self.regs[g][1] + + def span(self, g = 0): + """span([group=0]) -> tuple + + Return the 2-tuple (m.start(group), m.end(group)). Note that + if group did not contribute to the match, this is (-1, + -1). Group defaults to zero (meaning the whole matched + substring). + + """ + if type(g) == type(''): + try: + g = self.re.groupindex[g] + except (KeyError, TypeError): + raise IndexError, 'group %s is undefined' % `g` + return self.regs[g] + + def groups(self, default=None): + """groups([default=None]) -> tuple + + Return a tuple containing all the subgroups of the match, from + 1 up to however many groups are in the pattern. The default + argument is used for groups that did not participate in the + match. + + """ + result = [] + for g in range(1, self.re._num_regs): + a, b = self.regs[g] + if a == -1 or b == -1: + result.append(default) + else: + result.append(self.string[a:b]) + return tuple(result) + + def group(self, *groups): + """group([group1, group2, ...]) -> string or tuple + + Return one or more subgroups of the match. If there is a + single argument, the result is a single string; if there are + multiple arguments, the result is a tuple with one item per + argument. Without arguments, group1 defaults to zero (i.e. the + whole match is returned). If a groupN argument is zero, the + corresponding return value is the entire matching string; if + it is in the inclusive range [1..99], it is the string + matching the the corresponding parenthesized group. If a group + number is negative or larger than the number of groups defined + in the pattern, an IndexError exception is raised. If a group + is contained in a part of the pattern that did not match, the + corresponding result is None. If a group is contained in a + part of the pattern that matched multiple times, the last + match is returned. + + If the regular expression uses the (?P...) syntax, the + groupN arguments may also be strings identifying groups by + their group name. If a string argument is not used as a group + name in the pattern, an IndexError exception is raised. + + """ + if len(groups) == 0: + groups = (0,) + result = [] + for g in groups: + if type(g) == type(''): + try: + g = self.re.groupindex[g] + except (KeyError, TypeError): + raise IndexError, 'group %s is undefined' % `g` + if g >= len(self.regs): + raise IndexError, 'group %s is undefined' % `g` + a, b = self.regs[g] + if a == -1 or b == -1: + result.append(None) + else: + result.append(self.string[a:b]) + if len(result) > 1: + return tuple(result) + elif len(result) == 1: + return result[0] + else: + return () + + def groupdict(self, default=None): + """groupdict([default=None]) -> dictionary + + Return a dictionary containing all the named subgroups of the + match, keyed by the subgroup name. The default argument is + used for groups that did not participate in the match. + + """ + dict = {} + for name, index in self.re.groupindex.items(): + a, b = self.regs[index] + if a == -1 or b == -1: + dict[name] = default + else: + dict[name] = self.string[a:b] + return dict diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/profile.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/profile.py new file mode 100644 index 00000000..2d999efc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/profile.py @@ -0,0 +1,556 @@ +#! /usr/bin/env python +# +# Class for profiling python code. rev 1.0 6/2/94 +# +# Based on prior profile module by Sjoerd Mullender... +# which was hacked somewhat by: Guido van Rossum +# +# See profile.doc for more information + +"""Class for profiling Python code.""" + +# Copyright 1994, by InfoSeek Corporation, all rights reserved. +# Written by James Roskind +# +# Permission to use, copy, modify, and distribute this Python software +# and its associated documentation for any purpose (subject to the +# restriction in the following sentence) without fee is hereby granted, +# provided that the above copyright notice appears in all copies, and +# that both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of InfoSeek not be used in +# advertising or publicity pertaining to distribution of the software +# without specific, written prior permission. This permission is +# explicitly restricted to the copying and modification of the software +# to remain in Python, compiled Python, or other languages (such as C) +# wherein the modified or derived code is exclusively imported into a +# Python module. +# +# INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY +# SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER +# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + +import sys +import os +import time +import marshal + +__all__ = ["run","help","Profile"] + +# Sample timer for use with +#i_count = 0 +#def integer_timer(): +# global i_count +# i_count = i_count + 1 +# return i_count +#itimes = integer_timer # replace with C coded timer returning integers + +#************************************************************************** +# The following are the static member functions for the profiler class +# Note that an instance of Profile() is *not* needed to call them. +#************************************************************************** + +def run(statement, filename=None): + """Run statement under profiler optionally saving results in filename + + This function takes a single argument that can be passed to the + "exec" statement, and an optional file name. In all cases this + routine attempts to "exec" its first argument and gather profiling + statistics from the execution. If no file name is present, then this + function automatically prints a simple profiling report, sorted by the + standard name string (file/line/function-name) that is presented in + each line. + """ + prof = Profile() + try: + prof = prof.run(statement) + except SystemExit: + pass + if filename is not None: + prof.dump_stats(filename) + else: + return prof.print_stats() + +# print help +def help(): + for dirname in sys.path: + fullname = os.path.join(dirname, 'profile.doc') + if os.path.exists(fullname): + sts = os.system('${PAGER-more} ' + fullname) + if sts: print '*** Pager exit status:', sts + break + else: + print 'Sorry, can\'t find the help file "profile.doc"', + print 'along the Python search path.' + + +if os.name == "mac": + import MacOS + def _get_time_mac(timer=MacOS.GetTicks): + return timer() / 60.0 + +if hasattr(os, "times"): + def _get_time_times(timer=os.times): + t = timer() + return t[0] + t[1] + + +class Profile: + """Profiler class. + + self.cur is always a tuple. Each such tuple corresponds to a stack + frame that is currently active (self.cur[-2]). The following are the + definitions of its members. We use this external "parallel stack" to + avoid contaminating the program that we are profiling. (old profiler + used to write into the frames local dictionary!!) Derived classes + can change the definition of some entries, as long as they leave + [-2:] intact (frame and previous tuple). In case an internal error is + detected, the -3 element is used as the function name. + + [ 0] = Time that needs to be charged to the parent frame's function. + It is used so that a function call will not have to access the + timing data for the parent frame. + [ 1] = Total time spent in this frame's function, excluding time in + subfunctions (this latter is tallied in cur[2]). + [ 2] = Total time spent in subfunctions, excluding time executing the + frame's function (this latter is tallied in cur[1]). + [-3] = Name of the function that corresponds to this frame. + [-2] = Actual frame that we correspond to (used to sync exception handling). + [-1] = Our parent 6-tuple (corresponds to frame.f_back). + + Timing data for each function is stored as a 5-tuple in the dictionary + self.timings[]. The index is always the name stored in self.cur[-3]. + The following are the definitions of the members: + + [0] = The number of times this function was called, not counting direct + or indirect recursion, + [1] = Number of times this function appears on the stack, minus one + [2] = Total time spent internal to this function + [3] = Cumulative time that this function was present on the stack. In + non-recursive functions, this is the total execution time from start + to finish of each invocation of a function, including time spent in + all subfunctions. + [4] = A dictionary indicating for each function name, the number of times + it was called by us. + """ + + bias = 0 # calibration constant + + def __init__(self, timer=None, bias=None): + self.timings = {} + self.cur = None + self.cmd = "" + + if bias is None: + bias = self.bias + self.bias = bias # Materialize in local dict for lookup speed. + + if not timer: + if os.name == 'mac': + self.timer = MacOS.GetTicks + self.dispatcher = self.trace_dispatch_mac + self.get_time = _get_time_mac + elif hasattr(time, 'clock'): + self.timer = self.get_time = time.clock + self.dispatcher = self.trace_dispatch_i + elif hasattr(os, 'times'): + self.timer = os.times + self.dispatcher = self.trace_dispatch + self.get_time = _get_time_times + else: + self.timer = self.get_time = time.time + self.dispatcher = self.trace_dispatch_i + else: + self.timer = timer + t = self.timer() # test out timer function + try: + length = len(t) + except TypeError: + self.get_time = timer + self.dispatcher = self.trace_dispatch_i + else: + if length == 2: + self.dispatcher = self.trace_dispatch + else: + self.dispatcher = self.trace_dispatch_l + # This get_time() implementation needs to be defined + # here to capture the passed-in timer in the parameter + # list (for performance). Note that we can't assume + # the timer() result contains two values in all + # cases. + import operator + def get_time_timer(timer=timer, + reduce=reduce, reducer=operator.add): + return reduce(reducer, timer(), 0) + self.get_time = get_time_timer + self.t = self.get_time() + self.simulate_call('profiler') + + # Heavily optimized dispatch routine for os.times() timer + + def trace_dispatch(self, frame, event, arg): + timer = self.timer + t = timer() + t = t[0] + t[1] - self.t - self.bias + + if self.dispatch[event](self, frame,t): + t = timer() + self.t = t[0] + t[1] + else: + r = timer() + self.t = r[0] + r[1] - t # put back unrecorded delta + + # Dispatch routine for best timer program (return = scalar, fastest if + # an integer but float works too -- and time.clock() relies on that). + + def trace_dispatch_i(self, frame, event, arg): + timer = self.timer + t = timer() - self.t - self.bias + if self.dispatch[event](self, frame,t): + self.t = timer() + else: + self.t = timer() - t # put back unrecorded delta + + # Dispatch routine for macintosh (timer returns time in ticks of + # 1/60th second) + + def trace_dispatch_mac(self, frame, event, arg): + timer = self.timer + t = timer()/60.0 - self.t - self.bias + if self.dispatch[event](self, frame, t): + self.t = timer()/60.0 + else: + self.t = timer()/60.0 - t # put back unrecorded delta + + # SLOW generic dispatch routine for timer returning lists of numbers + + def trace_dispatch_l(self, frame, event, arg): + get_time = self.get_time + t = get_time() - self.t - self.bias + + if self.dispatch[event](self, frame, t): + self.t = get_time() + else: + self.t = get_time() - t # put back unrecorded delta + + # In the event handlers, the first 3 elements of self.cur are unpacked + # into vrbls w/ 3-letter names. The last two characters are meant to be + # mnemonic: + # _pt self.cur[0] "parent time" time to be charged to parent frame + # _it self.cur[1] "internal time" time spent directly in the function + # _et self.cur[2] "external time" time spent in subfunctions + + def trace_dispatch_exception(self, frame, t): + rpt, rit, ret, rfn, rframe, rcur = self.cur + if (rframe is not frame) and rcur: + return self.trace_dispatch_return(rframe, t) + self.cur = rpt, rit+t, ret, rfn, rframe, rcur + return 1 + + + def trace_dispatch_call(self, frame, t): + if self.cur and frame.f_back is not self.cur[-2]: + rpt, rit, ret, rfn, rframe, rcur = self.cur + if not isinstance(rframe, Profile.fake_frame): + assert rframe.f_back is frame.f_back, ("Bad call", rfn, + rframe, rframe.f_back, + frame, frame.f_back) + self.trace_dispatch_return(rframe, 0) + assert (self.cur is None or \ + frame.f_back is self.cur[-2]), ("Bad call", + self.cur[-3]) + fcode = frame.f_code + fn = (fcode.co_filename, fcode.co_firstlineno, fcode.co_name) + self.cur = (t, 0, 0, fn, frame, self.cur) + timings = self.timings + if timings.has_key(fn): + cc, ns, tt, ct, callers = timings[fn] + timings[fn] = cc, ns + 1, tt, ct, callers + else: + timings[fn] = 0, 0, 0, 0, {} + return 1 + + def trace_dispatch_return(self, frame, t): + if frame is not self.cur[-2]: + assert frame is self.cur[-2].f_back, ("Bad return", self.cur[-3]) + self.trace_dispatch_return(self.cur[-2], 0) + + # Prefix "r" means part of the Returning or exiting frame. + # Prefix "p" means part of the Previous or Parent or older frame. + + rpt, rit, ret, rfn, frame, rcur = self.cur + rit = rit + t + frame_total = rit + ret + + ppt, pit, pet, pfn, pframe, pcur = rcur + self.cur = ppt, pit + rpt, pet + frame_total, pfn, pframe, pcur + + timings = self.timings + cc, ns, tt, ct, callers = timings[rfn] + if not ns: + # This is the only occurrence of the function on the stack. + # Else this is a (directly or indirectly) recursive call, and + # its cumulative time will get updated when the topmost call to + # it returns. + ct = ct + frame_total + cc = cc + 1 + + if callers.has_key(pfn): + callers[pfn] = callers[pfn] + 1 # hack: gather more + # stats such as the amount of time added to ct courtesy + # of this specific call, and the contribution to cc + # courtesy of this call. + else: + callers[pfn] = 1 + + timings[rfn] = cc, ns - 1, tt + rit, ct, callers + + return 1 + + + dispatch = { + "call": trace_dispatch_call, + "exception": trace_dispatch_exception, + "return": trace_dispatch_return, + } + + + # The next few functions play with self.cmd. By carefully preloading + # our parallel stack, we can force the profiled result to include + # an arbitrary string as the name of the calling function. + # We use self.cmd as that string, and the resulting stats look + # very nice :-). + + def set_cmd(self, cmd): + if self.cur[-1]: return # already set + self.cmd = cmd + self.simulate_call(cmd) + + class fake_code: + def __init__(self, filename, line, name): + self.co_filename = filename + self.co_line = line + self.co_name = name + self.co_firstlineno = 0 + + def __repr__(self): + return repr((self.co_filename, self.co_line, self.co_name)) + + class fake_frame: + def __init__(self, code, prior): + self.f_code = code + self.f_back = prior + + def simulate_call(self, name): + code = self.fake_code('profile', 0, name) + if self.cur: + pframe = self.cur[-2] + else: + pframe = None + frame = self.fake_frame(code, pframe) + self.dispatch['call'](self, frame, 0) + + # collect stats from pending stack, including getting final + # timings for self.cmd frame. + + def simulate_cmd_complete(self): + get_time = self.get_time + t = get_time() - self.t + while self.cur[-1]: + # We *can* cause assertion errors here if + # dispatch_trace_return checks for a frame match! + self.dispatch['return'](self, self.cur[-2], t) + t = 0 + self.t = get_time() - t + + + def print_stats(self): + import pstats + pstats.Stats(self).strip_dirs().sort_stats(-1). \ + print_stats() + + def dump_stats(self, file): + f = open(file, 'wb') + self.create_stats() + marshal.dump(self.stats, f) + f.close() + + def create_stats(self): + self.simulate_cmd_complete() + self.snapshot_stats() + + def snapshot_stats(self): + self.stats = {} + for func in self.timings.keys(): + cc, ns, tt, ct, callers = self.timings[func] + callers = callers.copy() + nc = 0 + for func_caller in callers.keys(): + nc = nc + callers[func_caller] + self.stats[func] = cc, nc, tt, ct, callers + + + # The following two methods can be called by clients to use + # a profiler to profile a statement, given as a string. + + def run(self, cmd): + import __main__ + dict = __main__.__dict__ + return self.runctx(cmd, dict, dict) + + def runctx(self, cmd, globals, locals): + self.set_cmd(cmd) + sys.setprofile(self.dispatcher) + try: + exec cmd in globals, locals + finally: + sys.setprofile(None) + return self + + # This method is more useful to profile a single function call. + def runcall(self, func, *args, **kw): + self.set_cmd(`func`) + sys.setprofile(self.dispatcher) + try: + return apply(func, args, kw) + finally: + sys.setprofile(None) + + + #****************************************************************** + # The following calculates the overhead for using a profiler. The + # problem is that it takes a fair amount of time for the profiler + # to stop the stopwatch (from the time it receives an event). + # Similarly, there is a delay from the time that the profiler + # re-starts the stopwatch before the user's code really gets to + # continue. The following code tries to measure the difference on + # a per-event basis. + # + # Note that this difference is only significant if there are a lot of + # events, and relatively little user code per event. For example, + # code with small functions will typically benefit from having the + # profiler calibrated for the current platform. This *could* be + # done on the fly during init() time, but it is not worth the + # effort. Also note that if too large a value specified, then + # execution time on some functions will actually appear as a + # negative number. It is *normal* for some functions (with very + # low call counts) to have such negative stats, even if the + # calibration figure is "correct." + # + # One alternative to profile-time calibration adjustments (i.e., + # adding in the magic little delta during each event) is to track + # more carefully the number of events (and cumulatively, the number + # of events during sub functions) that are seen. If this were + # done, then the arithmetic could be done after the fact (i.e., at + # display time). Currently, we track only call/return events. + # These values can be deduced by examining the callees and callers + # vectors for each functions. Hence we *can* almost correct the + # internal time figure at print time (note that we currently don't + # track exception event processing counts). Unfortunately, there + # is currently no similar information for cumulative sub-function + # time. It would not be hard to "get all this info" at profiler + # time. Specifically, we would have to extend the tuples to keep + # counts of this in each frame, and then extend the defs of timing + # tuples to include the significant two figures. I'm a bit fearful + # that this additional feature will slow the heavily optimized + # event/time ratio (i.e., the profiler would run slower, fur a very + # low "value added" feature.) + #************************************************************** + + def calibrate(self, m, verbose=0): + if self.__class__ is not Profile: + raise TypeError("Subclasses must override .calibrate().") + + saved_bias = self.bias + self.bias = 0 + try: + return self._calibrate_inner(m, verbose) + finally: + self.bias = saved_bias + + def _calibrate_inner(self, m, verbose): + get_time = self.get_time + + # Set up a test case to be run with and without profiling. Include + # lots of calls, because we're trying to quantify stopwatch overhead. + # Do not raise any exceptions, though, because we want to know + # exactly how many profile events are generated (one call event, + + # one return event, per Python-level call). + + def f1(n): + for i in range(n): + x = 1 + + def f(m, f1=f1): + for i in range(m): + f1(100) + + f(m) # warm up the cache + + # elapsed_noprofile <- time f(m) takes without profiling. + t0 = get_time() + f(m) + t1 = get_time() + elapsed_noprofile = t1 - t0 + if verbose: + print "elapsed time without profiling =", elapsed_noprofile + + # elapsed_profile <- time f(m) takes with profiling. The difference + # is profiling overhead, only some of which the profiler subtracts + # out on its own. + p = Profile() + t0 = get_time() + p.runctx('f(m)', globals(), locals()) + t1 = get_time() + elapsed_profile = t1 - t0 + if verbose: + print "elapsed time with profiling =", elapsed_profile + + # reported_time <- "CPU seconds" the profiler charged to f and f1. + total_calls = 0.0 + reported_time = 0.0 + for (filename, line, funcname), (cc, ns, tt, ct, callers) in \ + p.timings.items(): + if funcname in ("f", "f1"): + total_calls += cc + reported_time += tt + + if verbose: + print "'CPU seconds' profiler reported =", reported_time + print "total # calls =", total_calls + if total_calls != m + 1: + raise ValueError("internal error: total calls = %d" % total_calls) + + # reported_time - elapsed_noprofile = overhead the profiler wasn't + # able to measure. Divide by twice the number of calls (since there + # are two profiler events per call in this test) to get the hidden + # overhead per event. + mean = (reported_time - elapsed_noprofile) / 2.0 / total_calls + if verbose: + print "mean stopwatch overhead per profile event =", mean + return mean + +#**************************************************************************** +def Stats(*args): + print 'Report generating functions are in the "pstats" module\a' + + +# When invoked as main program, invoke the profiler on a script +if __name__ == '__main__': + if not sys.argv[1:]: + print "usage: profile.py scriptfile [arg] ..." + sys.exit(2) + + filename = sys.argv[1] # Get script filename + + del sys.argv[0] # Hide "profile.py" from argument list + + # Insert script directory in front of module search path + sys.path.insert(0, os.path.dirname(filename)) + + run('execfile(' + `filename` + ')') diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pstats.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pstats.py new file mode 100644 index 00000000..bdbaa6d6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pstats.py @@ -0,0 +1,641 @@ +"""Class for printing reports on profiled python code.""" + +# Class for printing reports on profiled python code. rev 1.0 4/1/94 +# +# Based on prior profile module by Sjoerd Mullender... +# which was hacked somewhat by: Guido van Rossum +# +# see profile.doc and profile.py for more info. + +# Copyright 1994, by InfoSeek Corporation, all rights reserved. +# Written by James Roskind +# +# Permission to use, copy, modify, and distribute this Python software +# and its associated documentation for any purpose (subject to the +# restriction in the following sentence) without fee is hereby granted, +# provided that the above copyright notice appears in all copies, and +# that both that copyright notice and this permission notice appear in +# supporting documentation, and that the name of InfoSeek not be used in +# advertising or publicity pertaining to distribution of the software +# without specific, written prior permission. This permission is +# explicitly restricted to the copying and modification of the software +# to remain in Python, compiled Python, or other languages (such as C) +# wherein the modified or derived code is exclusively imported into a +# Python module. +# +# INFOSEEK CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS. IN NO EVENT SHALL INFOSEEK CORPORATION BE LIABLE FOR ANY +# SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER +# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +import os +import time +import marshal +import re + +__all__ = ["Stats"] + +class Stats: + """This class is used for creating reports from data generated by the + Profile class. It is a "friend" of that class, and imports data either + by direct access to members of Profile class, or by reading in a dictionary + that was emitted (via marshal) from the Profile class. + + The big change from the previous Profiler (in terms of raw functionality) + is that an "add()" method has been provided to combine Stats from + several distinct profile runs. Both the constructor and the add() + method now take arbitrarily many file names as arguments. + + All the print methods now take an argument that indicates how many lines + to print. If the arg is a floating point number between 0 and 1.0, then + it is taken as a decimal percentage of the available lines to be printed + (e.g., .1 means print 10% of all available lines). If it is an integer, + it is taken to mean the number of lines of data that you wish to have + printed. + + The sort_stats() method now processes some additional options (i.e., in + addition to the old -1, 0, 1, or 2). It takes an arbitrary number of quoted + strings to select the sort order. For example sort_stats('time', 'name') + sorts on the major key of "internal function time", and on the minor + key of 'the name of the function'. Look at the two tables in sort_stats() + and get_sort_arg_defs(self) for more examples. + + All methods now return "self", so you can string together commands like: + Stats('foo', 'goo').strip_dirs().sort_stats('calls').\ + print_stats(5).print_callers(5) + """ + + def __init__(self, *args): + if not len(args): + arg = None + else: + arg = args[0] + args = args[1:] + self.init(arg) + apply(self.add, args) + + def init(self, arg): + self.all_callees = None # calc only if needed + self.files = [] + self.fcn_list = None + self.total_tt = 0 + self.total_calls = 0 + self.prim_calls = 0 + self.max_name_len = 0 + self.top_level = {} + self.stats = {} + self.sort_arg_dict = {} + self.load_stats(arg) + trouble = 1 + try: + self.get_top_level_stats() + trouble = 0 + finally: + if trouble: + print "Invalid timing data", + if self.files: print self.files[-1], + print + + def load_stats(self, arg): + if not arg: self.stats = {} + elif type(arg) == type(""): + f = open(arg, 'rb') + self.stats = marshal.load(f) + f.close() + try: + file_stats = os.stat(arg) + arg = time.ctime(file_stats[8]) + " " + arg + except: # in case this is not unix + pass + self.files = [ arg ] + elif hasattr(arg, 'create_stats'): + arg.create_stats() + self.stats = arg.stats + arg.stats = {} + if not self.stats: + raise TypeError, "Cannot create or construct a " \ + + `self.__class__` \ + + " object from '" + `arg` + "'" + return + + def get_top_level_stats(self): + for func, (cc, nc, tt, ct, callers) in self.stats.items(): + self.total_calls += nc + self.prim_calls += cc + self.total_tt += tt + if callers.has_key(("jprofile", 0, "profiler")): + self.top_level[func] = None + if len(func_std_string(func)) > self.max_name_len: + self.max_name_len = len(func_std_string(func)) + + def add(self, *arg_list): + if not arg_list: return self + if len(arg_list) > 1: apply(self.add, arg_list[1:]) + other = arg_list[0] + if type(self) != type(other) or self.__class__ != other.__class__: + other = Stats(other) + self.files += other.files + self.total_calls += other.total_calls + self.prim_calls += other.prim_calls + self.total_tt += other.total_tt + for func in other.top_level.keys(): + self.top_level[func] = None + + if self.max_name_len < other.max_name_len: + self.max_name_len = other.max_name_len + + self.fcn_list = None + + for func in other.stats.keys(): + if self.stats.has_key(func): + old_func_stat = self.stats[func] + else: + old_func_stat = (0, 0, 0, 0, {},) + self.stats[func] = add_func_stats(old_func_stat, other.stats[func]) + return self + + # list the tuple indices and directions for sorting, + # along with some printable description + sort_arg_dict_default = { + "calls" : (((1,-1), ), "call count"), + "cumulative": (((3,-1), ), "cumulative time"), + "file" : (((4, 1), ), "file name"), + "line" : (((5, 1), ), "line number"), + "module" : (((4, 1), ), "file name"), + "name" : (((6, 1), ), "function name"), + "nfl" : (((6, 1),(4, 1),(5, 1),), "name/file/line"), + "pcalls" : (((0,-1), ), "call count"), + "stdname" : (((7, 1), ), "standard name"), + "time" : (((2,-1), ), "internal time"), + } + + def get_sort_arg_defs(self): + """Expand all abbreviations that are unique.""" + if not self.sort_arg_dict: + self.sort_arg_dict = dict = {} + bad_list = {} + for word in self.sort_arg_dict_default.keys(): + fragment = word + while fragment: + if not fragment: + break + if dict.has_key(fragment): + bad_list[fragment] = 0 + break + dict[fragment] = self.sort_arg_dict_default[word] + fragment = fragment[:-1] + for word in bad_list.keys(): + del dict[word] + return self.sort_arg_dict + + def sort_stats(self, *field): + if not field: + self.fcn_list = 0 + return self + if len(field) == 1 and type(field[0]) == type(1): + # Be compatible with old profiler + field = [ {-1: "stdname", + 0:"calls", + 1:"time", + 2: "cumulative" } [ field[0] ] ] + + sort_arg_defs = self.get_sort_arg_defs() + sort_tuple = () + self.sort_type = "" + connector = "" + for word in field: + sort_tuple = sort_tuple + sort_arg_defs[word][0] + self.sort_type += connector + sort_arg_defs[word][1] + connector = ", " + + stats_list = [] + for func in self.stats.keys(): + cc, nc, tt, ct, callers = self.stats[func] + stats_list.append((cc, nc, tt, ct) + func + + (func_std_string(func), func)) + + stats_list.sort(TupleComp(sort_tuple).compare) + + self.fcn_list = fcn_list = [] + for tuple in stats_list: + fcn_list.append(tuple[-1]) + return self + + def reverse_order(self): + if self.fcn_list: + self.fcn_list.reverse() + return self + + def strip_dirs(self): + oldstats = self.stats + self.stats = newstats = {} + max_name_len = 0 + for func in oldstats.keys(): + cc, nc, tt, ct, callers = oldstats[func] + newfunc = func_strip_path(func) + if len(func_std_string(newfunc)) > max_name_len: + max_name_len = len(func_std_string(newfunc)) + newcallers = {} + for func2 in callers.keys(): + newcallers[func_strip_path(func2)] = callers[func2] + + if newstats.has_key(newfunc): + newstats[newfunc] = add_func_stats( + newstats[newfunc], + (cc, nc, tt, ct, newcallers)) + else: + newstats[newfunc] = (cc, nc, tt, ct, newcallers) + old_top = self.top_level + self.top_level = new_top = {} + for func in old_top.keys(): + new_top[func_strip_path(func)] = None + + self.max_name_len = max_name_len + + self.fcn_list = None + self.all_callees = None + return self + + def calc_callees(self): + if self.all_callees: return + self.all_callees = all_callees = {} + for func in self.stats.keys(): + if not all_callees.has_key(func): + all_callees[func] = {} + cc, nc, tt, ct, callers = self.stats[func] + for func2 in callers.keys(): + if not all_callees.has_key(func2): + all_callees[func2] = {} + all_callees[func2][func] = callers[func2] + return + + #****************************************************************** + # The following functions support actual printing of reports + #****************************************************************** + + # Optional "amount" is either a line count, or a percentage of lines. + + def eval_print_amount(self, sel, list, msg): + new_list = list + if type(sel) == type(""): + new_list = [] + for func in list: + if re.search(sel, func_std_string(func)): + new_list.append(func) + else: + count = len(list) + if type(sel) == type(1.0) and 0.0 <= sel < 1.0: + count = int(count * sel + .5) + new_list = list[:count] + elif type(sel) == type(1) and 0 <= sel < count: + count = sel + new_list = list[:count] + if len(list) != len(new_list): + msg = msg + " List reduced from " + `len(list)` \ + + " to " + `len(new_list)` + \ + " due to restriction <" + `sel` + ">\n" + + return new_list, msg + + def get_print_list(self, sel_list): + width = self.max_name_len + if self.fcn_list: + list = self.fcn_list[:] + msg = " Ordered by: " + self.sort_type + '\n' + else: + list = self.stats.keys() + msg = " Random listing order was used\n" + + for selection in sel_list: + list, msg = self.eval_print_amount(selection, list, msg) + + count = len(list) + + if not list: + return 0, list + print msg + if count < len(self.stats): + width = 0 + for func in list: + if len(func_std_string(func)) > width: + width = len(func_std_string(func)) + return width+2, list + + def print_stats(self, *amount): + for filename in self.files: + print filename + if self.files: print + indent = ' ' * 8 + for func in self.top_level.keys(): + print indent, func_get_function_name(func) + + print indent, self.total_calls, "function calls", + if self.total_calls != self.prim_calls: + print "(%d primitive calls)" % self.prim_calls, + print "in %.3f CPU seconds" % self.total_tt + print + width, list = self.get_print_list(amount) + if list: + self.print_title() + for func in list: + self.print_line(func) + print + print + return self + + def print_callees(self, *amount): + width, list = self.get_print_list(amount) + if list: + self.calc_callees() + + self.print_call_heading(width, "called...") + for func in list: + if self.all_callees.has_key(func): + self.print_call_line(width, func, self.all_callees[func]) + else: + self.print_call_line(width, func, {}) + print + print + return self + + def print_callers(self, *amount): + width, list = self.get_print_list(amount) + if list: + self.print_call_heading(width, "was called by...") + for func in list: + cc, nc, tt, ct, callers = self.stats[func] + self.print_call_line(width, func, callers) + print + print + return self + + def print_call_heading(self, name_size, column_title): + print "Function ".ljust(name_size) + column_title + + def print_call_line(self, name_size, source, call_dict): + print func_std_string(source).ljust(name_size), + if not call_dict: + print "--" + return + clist = call_dict.keys() + clist.sort() + name_size = name_size + 1 + indent = "" + for func in clist: + name = func_std_string(func) + print indent*name_size + name + '(' \ + + `call_dict[func]`+')', \ + f8(self.stats[func][3]) + indent = " " + + def print_title(self): + print ' ncalls tottime percall cumtime percall', \ + 'filename:lineno(function)' + + def print_line(self, func): # hack : should print percentages + cc, nc, tt, ct, callers = self.stats[func] + c = str(nc) + if nc != cc: + c = c + '/' + str(cc) + print c.rjust(9), + print f8(tt), + if nc == 0: + print ' '*8, + else: + print f8(tt/nc), + print f8(ct), + if cc == 0: + print ' '*8, + else: + print f8(ct/cc), + print func_std_string(func) + + def ignore(self): + # Deprecated since 1.5.1 -- see the docs. + pass # has no return value, so use at end of line :-) + +class TupleComp: + """This class provides a generic function for comparing any two tuples. + Each instance records a list of tuple-indices (from most significant + to least significant), and sort direction (ascending or decending) for + each tuple-index. The compare functions can then be used as the function + argument to the system sort() function when a list of tuples need to be + sorted in the instances order.""" + + def __init__(self, comp_select_list): + self.comp_select_list = comp_select_list + + def compare (self, left, right): + for index, direction in self.comp_select_list: + l = left[index] + r = right[index] + if l < r: + return -direction + if l > r: + return direction + return 0 + +#************************************************************************** +# func_name is a triple (file:string, line:int, name:string) + +def func_strip_path(func_name): + file, line, name = func_name + return os.path.basename(file), line, name + +def func_get_function_name(func): + return func[2] + +def func_std_string(func_name): # match what old profile produced + return "%s:%d(%s)" % func_name + +#************************************************************************** +# The following functions combine statists for pairs functions. +# The bulk of the processing involves correctly handling "call" lists, +# such as callers and callees. +#************************************************************************** + +def add_func_stats(target, source): + """Add together all the stats for two profile entries.""" + cc, nc, tt, ct, callers = source + t_cc, t_nc, t_tt, t_ct, t_callers = target + return (cc+t_cc, nc+t_nc, tt+t_tt, ct+t_ct, + add_callers(t_callers, callers)) + +def add_callers(target, source): + """Combine two caller lists in a single list.""" + new_callers = {} + for func in target.keys(): + new_callers[func] = target[func] + for func in source.keys(): + if new_callers.has_key(func): + new_callers[func] = source[func] + new_callers[func] + else: + new_callers[func] = source[func] + return new_callers + +def count_calls(callers): + """Sum the caller statistics to get total number of calls received.""" + nc = 0 + for func in callers.keys(): + nc += callers[func] + return nc + +#************************************************************************** +# The following functions support printing of reports +#************************************************************************** + +def f8(x): + return "%8.3f" % x + +#************************************************************************** +# Statistics browser added by ESR, April 2001 +#************************************************************************** + +if __name__ == '__main__': + import cmd + try: + import readline + except ImportError: + pass + + class ProfileBrowser(cmd.Cmd): + def __init__(self, profile=None): + cmd.Cmd.__init__(self) + self.prompt = "% " + if profile: + self.stats = Stats(profile) + else: + self.stats = None + + def generic(self, fn, line): + args = line.split() + processed = [] + for term in args: + try: + processed.append(int(term)) + continue + except ValueError: + pass + try: + frac = float(term) + if frac > 1 or frac < 0: + print "Fraction argument mus be in [0, 1]" + continue + processed.append(frac) + continue + except ValueError: + pass + processed.append(term) + if self.stats: + apply(getattr(self.stats, fn), processed) + else: + print "No statistics object is loaded." + return 0 + def generic_help(self): + print "Arguments may be:" + print "* An integer maximum number of entries to print." + print "* A decimal fractional number between 0 and 1, controlling" + print " what fraction of selected entries to print." + print "* A regular expression; only entries with function names" + print " that match it are printed." + + def do_add(self, line): + self.stats.add(line) + return 0 + def help_add(self): + print "Add profile info from given file to current statistics object." + + def do_callees(self, line): + return self.generic('print_callees', line) + def help_callees(self): + print "Print callees statistics from the current stat object." + self.generic_help() + + def do_callers(self, line): + return self.generic('print_callers', line) + def help_callers(self): + print "Print callers statistics from the current stat object." + self.generic_help() + + def do_EOF(self, line): + print "" + return 1 + def help_EOF(self): + print "Leave the profile brower." + + def do_quit(self, line): + return 1 + def help_quit(self): + print "Leave the profile brower." + + def do_read(self, line): + if line: + try: + self.stats = Stats(line) + except IOError, args: + print args[1] + return + self.prompt = line + "% " + elif len(self.prompt) > 2: + line = self.prompt[-2:] + else: + print "No statistics object is current -- cannot reload." + return 0 + def help_read(self): + print "Read in profile data from a specified file." + + def do_reverse(self, line): + self.stats.reverse_order() + return 0 + def help_reverse(self): + print "Reverse the sort order of the profiling report." + + def do_sort(self, line): + abbrevs = self.stats.get_sort_arg_defs().keys() + if line and not filter(lambda x,a=abbrevs: x not in a,line.split()): + apply(self.stats.sort_stats, line.split()) + else: + print "Valid sort keys (unique prefixes are accepted):" + for (key, value) in Stats.sort_arg_dict_default.items(): + print "%s -- %s" % (key, value[1]) + return 0 + def help_sort(self): + print "Sort profile data according to specified keys." + print "(Typing `sort' without arguments lists valid keys.)" + def complete_sort(self, text, *args): + return [a for a in Stats.sort_arg_dict_default.keys() if a.startswith(text)] + + def do_stats(self, line): + return self.generic('print_stats', line) + def help_stats(self): + print "Print statistics from the current stat object." + self.generic_help() + + def do_strip(self, line): + self.stats.strip_dirs() + return 0 + def help_strip(self): + print "Strip leading path information from filenames in the report." + + def postcmd(self, stop, line): + if stop: + return stop + return None + + import sys + print "Welcome to the profile statistics browser." + if len(sys.argv) > 1: + initprofile = sys.argv[1] + else: + initprofile = None + try: + ProfileBrowser(initprofile).cmdloop() + print "Goodbye." + except KeyboardInterrupt: + pass + +# That's all, folks. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/py_compile.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/py_compile.py new file mode 100644 index 00000000..af473363 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/py_compile.py @@ -0,0 +1,82 @@ +"""Routine to "compile" a .py file to a .pyc (or .pyo) file. + +This module has intimate knowledge of the format of .pyc files. +""" + +import imp +MAGIC = imp.get_magic() + +__all__ = ["compile"] + +def wr_long(f, x): + """Internal; write a 32-bit int to a file in little-endian order.""" + f.write(chr( x & 0xff)) + f.write(chr((x >> 8) & 0xff)) + f.write(chr((x >> 16) & 0xff)) + f.write(chr((x >> 24) & 0xff)) + +def compile(file, cfile=None, dfile=None): + """Byte-compile one Python source file to Python bytecode. + + Arguments: + + file: source filename + cfile: target filename; defaults to source with 'c' or 'o' appended + ('c' normally, 'o' in optimizing mode, giving .pyc or .pyo) + dfile: purported filename; defaults to source (this is the filename + that will show up in error messages) + + Note that it isn't necessary to byte-compile Python modules for + execution efficiency -- Python itself byte-compiles a module when + it is loaded, and if it can, writes out the bytecode to the + corresponding .pyc (or .pyo) file. + + However, if a Python installation is shared between users, it is a + good idea to byte-compile all modules upon installation, since + other users may not be able to write in the source directories, + and thus they won't be able to write the .pyc/.pyo file, and then + they would be byte-compiling every module each time it is loaded. + This can slow down program start-up considerably. + + See compileall.py for a script/module that uses this module to + byte-compile all installed files (or all files in selected + directories). + + """ + import os, marshal, __builtin__ + f = open(file) + try: + timestamp = long(os.fstat(f.fileno())[8]) + except AttributeError: + timestamp = long(os.stat(file)[8]) + codestring = f.read() + # If parsing from a string, line breaks are \n (see parsetok.c:tok_nextc) + # Replace will return original string if pattern is not found, so + # we don't need to check whether it is found first. + codestring = codestring.replace("\r\n","\n") + codestring = codestring.replace("\r","\n") + f.close() + if codestring and codestring[-1] != '\n': + codestring = codestring + '\n' + try: + codeobject = __builtin__.compile(codestring, dfile or file, 'exec') + except SyntaxError, detail: + import traceback, sys + lines = traceback.format_exception_only(SyntaxError, detail) + for line in lines: + sys.stderr.write(line.replace('File ""', + 'File "%s"' % (dfile or file))) + return + if not cfile: + cfile = file + (__debug__ and 'c' or 'o') + fc = open(cfile, 'wb') + fc.write('\0\0\0\0') + wr_long(fc, timestamp) + marshal.dump(codeobject, fc) + fc.flush() + fc.seek(0, 0) + fc.write(MAGIC) + fc.close() + if os.name == 'mac': + import macfs + macfs.FSSpec(cfile).SetCreatorType('Pyth', 'PYC ') diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pyclbr.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pyclbr.py new file mode 100644 index 00000000..90e5dc35 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/pyclbr.py @@ -0,0 +1,337 @@ +"""Parse a Python file and retrieve classes and methods. + +Parse enough of a Python file to recognize class and method +definitions and to find out the superclasses of a class. + +The interface consists of a single function: + readmodule(module, path) +module is the name of a Python module, path is an optional list of +directories where the module is to be searched. If present, path is +prepended to the system search path sys.path. +The return value is a dictionary. The keys of the dictionary are +the names of the classes defined in the module (including classes +that are defined via the from XXX import YYY construct). The values +are class instances of the class Class defined here. + +A class is described by the class Class in this module. Instances +of this class have the following instance variables: + name -- the name of the class + super -- a list of super classes (Class instances) + methods -- a dictionary of methods + file -- the file in which the class was defined + lineno -- the line in the file on which the class statement occurred +The dictionary of methods uses the method names as keys and the line +numbers on which the method was defined as values. +If the name of a super class is not recognized, the corresponding +entry in the list of super classes is not a class instance but a +string giving the name of the super class. Since import statements +are recognized and imported modules are scanned as well, this +shouldn't happen often. + +BUGS +- Continuation lines are not dealt with at all, except inside strings. +- Nested classes and functions can confuse it. +- Code that doesn't pass tabnanny or python -t will confuse it, unless + you set the module TABWIDTH vrbl (default 8) to the correct tab width + for the file. + +PACKAGE RELATED BUGS +- If you have a package and a module inside that or another package + with the same name, module caching doesn't work properly since the + key is the base name of the module/package. +- The only entry that is returned when you readmodule a package is a + __path__ whose value is a list which confuses certain class browsers. +- When code does: + from package import subpackage + class MyClass(subpackage.SuperClass): + ... + It can't locate the parent. It probably needs to have the same + hairy logic that the import locator already does. (This logic + exists coded in Python in the freeze package.) +""" + +import sys +import imp +import re +import string + +__all__ = ["readmodule"] + +TABWIDTH = 8 + +_getnext = re.compile(r""" + (?P + \""" [^"\\]* (?: + (?: \\. | "(?!"") ) + [^"\\]* + )* + \""" + + | ''' [^'\\]* (?: + (?: \\. | '(?!'') ) + [^'\\]* + )* + ''' + + | " [^"\\\n]* (?: \\. [^"\\\n]*)* " + + | ' [^'\\\n]* (?: \\. [^'\\\n]*)* ' + ) + +| (?P + ^ + (?P [ \t]* ) + def [ \t]+ + (?P [a-zA-Z_] \w* ) + [ \t]* \( + ) + +| (?P + ^ + (?P [ \t]* ) + class [ \t]+ + (?P [a-zA-Z_] \w* ) + [ \t]* + (?P \( [^)\n]* \) )? + [ \t]* : + ) + +| (?P + ^ import [ \t]+ + (?P [^#;\n]+ ) + ) + +| (?P + ^ from [ \t]+ + (?P + [a-zA-Z_] \w* + (?: + [ \t]* \. [ \t]* [a-zA-Z_] \w* + )* + ) + [ \t]+ + import [ \t]+ + (?P [^#;\n]+ ) + ) +""", re.VERBOSE | re.DOTALL | re.MULTILINE).search + +_modules = {} # cache of modules we've seen + +# each Python class is represented by an instance of this class +class Class: + '''Class to represent a Python class.''' + def __init__(self, module, name, super, file, lineno): + self.module = module + self.name = name + if super is None: + super = [] + self.super = super + self.methods = {} + self.file = file + self.lineno = lineno + + def _addmethod(self, name, lineno): + self.methods[name] = lineno + +class Function(Class): + '''Class to represent a top-level Python function''' + def __init__(self, module, name, file, lineno): + Class.__init__(self, module, name, None, file, lineno) + def _addmethod(self, name, lineno): + assert 0, "Function._addmethod() shouldn't be called" + +def readmodule(module, path=[], inpackage=0): + '''Backwards compatible interface. + + Like readmodule_ex() but strips Function objects from the + resulting dictionary.''' + + dict = readmodule_ex(module, path, inpackage) + res = {} + for key, value in dict.items(): + if not isinstance(value, Function): + res[key] = value + return res + +def readmodule_ex(module, path=[], inpackage=0): + '''Read a module file and return a dictionary of classes. + + Search for MODULE in PATH and sys.path, read and parse the + module and return a dictionary with one entry for each class + found in the module.''' + + dict = {} + + i = module.rfind('.') + if i >= 0: + # Dotted module name + package = module[:i].strip() + submodule = module[i+1:].strip() + parent = readmodule_ex(package, path, inpackage) + child = readmodule_ex(submodule, parent['__path__'], 1) + return child + + if _modules.has_key(module): + # we've seen this module before... + return _modules[module] + if module in sys.builtin_module_names: + # this is a built-in module + _modules[module] = dict + return dict + + # search the path for the module + f = None + if inpackage: + try: + f, file, (suff, mode, type) = \ + imp.find_module(module, path) + except ImportError: + f = None + if f is None: + fullpath = list(path) + sys.path + f, file, (suff, mode, type) = imp.find_module(module, fullpath) + if type == imp.PKG_DIRECTORY: + dict['__path__'] = [file] + _modules[module] = dict + path = [file] + path + f, file, (suff, mode, type) = \ + imp.find_module('__init__', [file]) + if type != imp.PY_SOURCE: + # not Python source, can't do anything with this module + f.close() + _modules[module] = dict + return dict + + _modules[module] = dict + classstack = [] # stack of (class, indent) pairs + src = f.read() + f.close() + + # To avoid having to stop the regexp at each newline, instead + # when we need a line number we simply string.count the number of + # newlines in the string since the last time we did this; i.e., + # lineno = lineno + \ + # string.count(src, '\n', last_lineno_pos, here) + # last_lineno_pos = here + countnl = string.count + lineno, last_lineno_pos = 1, 0 + i = 0 + while 1: + m = _getnext(src, i) + if not m: + break + start, i = m.span() + + if m.start("Method") >= 0: + # found a method definition or function + thisindent = _indent(m.group("MethodIndent")) + meth_name = m.group("MethodName") + lineno = lineno + \ + countnl(src, '\n', + last_lineno_pos, start) + last_lineno_pos = start + # close all classes indented at least as much + while classstack and \ + classstack[-1][1] >= thisindent: + del classstack[-1] + if classstack: + # it's a class method + cur_class = classstack[-1][0] + cur_class._addmethod(meth_name, lineno) + else: + # it's a function + f = Function(module, meth_name, + file, lineno) + dict[meth_name] = f + + elif m.start("String") >= 0: + pass + + elif m.start("Class") >= 0: + # we found a class definition + thisindent = _indent(m.group("ClassIndent")) + # close all classes indented at least as much + while classstack and \ + classstack[-1][1] >= thisindent: + del classstack[-1] + lineno = lineno + \ + countnl(src, '\n', last_lineno_pos, start) + last_lineno_pos = start + class_name = m.group("ClassName") + inherit = m.group("ClassSupers") + if inherit: + # the class inherits from other classes + inherit = inherit[1:-1].strip() + names = [] + for n in inherit.split(','): + n = n.strip() + if dict.has_key(n): + # we know this super class + n = dict[n] + else: + c = n.split('.') + if len(c) > 1: + # super class + # is of the + # form module.class: + # look in + # module for class + m = c[-2] + c = c[-1] + if _modules.has_key(m): + d = _modules[m] + if d.has_key(c): + n = d[c] + names.append(n) + inherit = names + # remember this class + cur_class = Class(module, class_name, inherit, + file, lineno) + dict[class_name] = cur_class + classstack.append((cur_class, thisindent)) + + elif m.start("Import") >= 0: + # import module + for n in m.group("ImportList").split(','): + n = n.strip() + try: + # recursively read the imported module + d = readmodule_ex(n, path, inpackage) + except: + ##print 'module', n, 'not found' + pass + + elif m.start("ImportFrom") >= 0: + # from module import stuff + mod = m.group("ImportFromPath") + names = m.group("ImportFromList").split(',') + try: + # recursively read the imported module + d = readmodule_ex(mod, path, inpackage) + except: + ##print 'module', mod, 'not found' + continue + # add any classes that were defined in the + # imported module to our name space if they + # were mentioned in the list + for n in names: + n = n.strip() + if d.has_key(n): + dict[n] = d[n] + elif n == '*': + # only add a name if not + # already there (to mimic what + # Python does internally) + # also don't add names that + # start with _ + for n in d.keys(): + if n[0] != '_' and \ + not dict.has_key(n): + dict[n] = d[n] + else: + assert 0, "regexp _getnext found something unexpected" + + return dict + +def _indent(ws, _expandtabs=string.expandtabs): + return len(_expandtabs(ws, TABWIDTH)) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/random.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/random.py new file mode 100644 index 00000000..09e750c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/random.py @@ -0,0 +1,777 @@ +"""Random variable generators. + + integers + -------- + uniform within range + + sequences + --------- + pick random element + generate random permutation + + distributions on the real line: + ------------------------------ + uniform + normal (Gaussian) + lognormal + negative exponential + gamma + beta + + distributions on the circle (angles 0 to 2pi) + --------------------------------------------- + circular uniform + von Mises + +Translated from anonymously contributed C/C++ source. + +Multi-threading note: the random number generator used here is not thread- +safe; it is possible that two calls return the same random value. However, +you can instantiate a different instance of Random() in each thread to get +generators that don't share state, then use .setstate() and .jumpahead() to +move the generators to disjoint segments of the full period. For example, + +def create_generators(num, delta, firstseed=None): + ""\"Return list of num distinct generators. + Each generator has its own unique segment of delta elements from + Random.random()'s full period. + Seed the first generator with optional arg firstseed (default is + None, to seed from current time). + ""\" + + from random import Random + g = Random(firstseed) + result = [g] + for i in range(num - 1): + laststate = g.getstate() + g = Random() + g.setstate(laststate) + g.jumpahead(delta) + result.append(g) + return result + +gens = create_generators(10, 1000000) + +That creates 10 distinct generators, which can be passed out to 10 distinct +threads. The generators don't share state so can be called safely in +parallel. So long as no thread calls its g.random() more than a million +times (the second argument to create_generators), the sequences seen by +each thread will not overlap. + +The period of the underlying Wichmann-Hill generator is 6,953,607,871,644, +and that limits how far this technique can be pushed. + +Just for fun, note that since we know the period, .jumpahead() can also be +used to "move backward in time": + +>>> g = Random(42) # arbitrary +>>> g.random() +0.25420336316883324 +>>> g.jumpahead(6953607871644L - 1) # move *back* one +>>> g.random() +0.25420336316883324 +""" +# XXX The docstring sucks. + +from math import log as _log, exp as _exp, pi as _pi, e as _e +from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin +from math import floor as _floor + +__all__ = ["Random","seed","random","uniform","randint","choice", + "randrange","shuffle","normalvariate","lognormvariate", + "cunifvariate","expovariate","vonmisesvariate","gammavariate", + "stdgamma","gauss","betavariate","paretovariate","weibullvariate", + "getstate","setstate","jumpahead","whseed"] + +def _verify(name, computed, expected): + if abs(computed - expected) > 1e-7: + raise ValueError( + "computed value for %s deviates too much " + "(computed %g, expected %g)" % (name, computed, expected)) + +NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0) +_verify('NV_MAGICCONST', NV_MAGICCONST, 1.71552776992141) + +TWOPI = 2.0*_pi +_verify('TWOPI', TWOPI, 6.28318530718) + +LOG4 = _log(4.0) +_verify('LOG4', LOG4, 1.38629436111989) + +SG_MAGICCONST = 1.0 + _log(4.5) +_verify('SG_MAGICCONST', SG_MAGICCONST, 2.50407739677627) + +del _verify + +# Translated by Guido van Rossum from C source provided by +# Adrian Baddeley. + +class Random: + """Random number generator base class used by bound module functions. + + Used to instantiate instances of Random to get generators that don't + share state. Especially useful for multi-threaded programs, creating + a different instance of Random for each thread, and using the jumpahead() + method to ensure that the generated sequences seen by each thread don't + overlap. + + Class Random can also be subclassed if you want to use a different basic + generator of your own devising: in that case, override the following + methods: random(), seed(), getstate(), setstate() and jumpahead(). + + """ + + VERSION = 1 # used by getstate/setstate + + def __init__(self, x=None): + """Initialize an instance. + + Optional argument x controls seeding, as for Random.seed(). + """ + + self.seed(x) + +## -------------------- core generator ------------------- + + # Specific to Wichmann-Hill generator. Subclasses wishing to use a + # different core generator should override the seed(), random(), + # getstate(), setstate() and jumpahead() methods. + + def seed(self, a=None): + """Initialize internal state from hashable object. + + None or no argument seeds from current time. + + If a is not None or an int or long, hash(a) is used instead. + + If a is an int or long, a is used directly. Distinct values between + 0 and 27814431486575L inclusive are guaranteed to yield distinct + internal states (this guarantee is specific to the default + Wichmann-Hill generator). + """ + + if a is None: + # Initialize from current time + import time + a = long(time.time() * 256) + + if type(a) not in (type(3), type(3L)): + a = hash(a) + + a, x = divmod(a, 30268) + a, y = divmod(a, 30306) + a, z = divmod(a, 30322) + self._seed = int(x)+1, int(y)+1, int(z)+1 + + self.gauss_next = None + + def random(self): + """Get the next random number in the range [0.0, 1.0).""" + + # Wichman-Hill random number generator. + # + # Wichmann, B. A. & Hill, I. D. (1982) + # Algorithm AS 183: + # An efficient and portable pseudo-random number generator + # Applied Statistics 31 (1982) 188-190 + # + # see also: + # Correction to Algorithm AS 183 + # Applied Statistics 33 (1984) 123 + # + # McLeod, A. I. (1985) + # A remark on Algorithm AS 183 + # Applied Statistics 34 (1985),198-200 + + # This part is thread-unsafe: + # BEGIN CRITICAL SECTION + x, y, z = self._seed + x = (171 * x) % 30269 + y = (172 * y) % 30307 + z = (170 * z) % 30323 + self._seed = x, y, z + # END CRITICAL SECTION + + # Note: on a platform using IEEE-754 double arithmetic, this can + # never return 0.0 (asserted by Tim; proof too long for a comment). + return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0 + + def getstate(self): + """Return internal state; can be passed to setstate() later.""" + return self.VERSION, self._seed, self.gauss_next + + def setstate(self, state): + """Restore internal state from object returned by getstate().""" + version = state[0] + if version == 1: + version, self._seed, self.gauss_next = state + else: + raise ValueError("state with version %s passed to " + "Random.setstate() of version %s" % + (version, self.VERSION)) + + def jumpahead(self, n): + """Act as if n calls to random() were made, but quickly. + + n is an int, greater than or equal to 0. + + Example use: If you have 2 threads and know that each will + consume no more than a million random numbers, create two Random + objects r1 and r2, then do + r2.setstate(r1.getstate()) + r2.jumpahead(1000000) + Then r1 and r2 will use guaranteed-disjoint segments of the full + period. + """ + + if not n >= 0: + raise ValueError("n must be >= 0") + x, y, z = self._seed + x = int(x * pow(171, n, 30269)) % 30269 + y = int(y * pow(172, n, 30307)) % 30307 + z = int(z * pow(170, n, 30323)) % 30323 + self._seed = x, y, z + + def __whseed(self, x=0, y=0, z=0): + """Set the Wichmann-Hill seed from (x, y, z). + + These must be integers in the range [0, 256). + """ + + if not type(x) == type(y) == type(z) == type(0): + raise TypeError('seeds must be integers') + if not (0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256): + raise ValueError('seeds must be in range(0, 256)') + if 0 == x == y == z: + # Initialize from current time + import time + t = long(time.time() * 256) + t = int((t&0xffffff) ^ (t>>24)) + t, x = divmod(t, 256) + t, y = divmod(t, 256) + t, z = divmod(t, 256) + # Zero is a poor seed, so substitute 1 + self._seed = (x or 1, y or 1, z or 1) + + self.gauss_next = None + + def whseed(self, a=None): + """Seed from hashable object's hash code. + + None or no argument seeds from current time. It is not guaranteed + that objects with distinct hash codes lead to distinct internal + states. + + This is obsolete, provided for compatibility with the seed routine + used prior to Python 2.1. Use the .seed() method instead. + """ + + if a is None: + self.__whseed() + return + a = hash(a) + a, x = divmod(a, 256) + a, y = divmod(a, 256) + a, z = divmod(a, 256) + x = (x + a) % 256 or 1 + y = (y + a) % 256 or 1 + z = (z + a) % 256 or 1 + self.__whseed(x, y, z) + +## ---- Methods below this point do not need to be overridden when +## ---- subclassing for the purpose of using a different core generator. + +## -------------------- pickle support ------------------- + + def __getstate__(self): # for pickle + return self.getstate() + + def __setstate__(self, state): # for pickle + self.setstate(state) + +## -------------------- integer methods ------------------- + + def randrange(self, start, stop=None, step=1, int=int, default=None): + """Choose a random item from range(start, stop[, step]). + + This fixes the problem with randint() which includes the + endpoint; in Python this is usually not what you want. + Do not supply the 'int' and 'default' arguments. + """ + + # This code is a bit messy to make it fast for the + # common case while still doing adequate error checking. + istart = int(start) + if istart != start: + raise ValueError, "non-integer arg 1 for randrange()" + if stop is default: + if istart > 0: + return int(self.random() * istart) + raise ValueError, "empty range for randrange()" + + # stop argument supplied. + istop = int(stop) + if istop != stop: + raise ValueError, "non-integer stop for randrange()" + if step == 1 and istart < istop: + try: + return istart + int(self.random()*(istop - istart)) + except OverflowError: + # This can happen if istop-istart > sys.maxint + 1, and + # multiplying by random() doesn't reduce it to something + # <= sys.maxint. We know that the overall result fits + # in an int, and can still do it correctly via math.floor(). + # But that adds another function call, so for speed we + # avoided that whenever possible. + return int(istart + _floor(self.random()*(istop - istart))) + if step == 1: + raise ValueError, "empty range for randrange()" + + # Non-unit step argument supplied. + istep = int(step) + if istep != step: + raise ValueError, "non-integer step for randrange()" + if istep > 0: + n = (istop - istart + istep - 1) / istep + elif istep < 0: + n = (istop - istart + istep + 1) / istep + else: + raise ValueError, "zero step for randrange()" + + if n <= 0: + raise ValueError, "empty range for randrange()" + return istart + istep*int(self.random() * n) + + def randint(self, a, b): + """Return random integer in range [a, b], including both end points. + """ + + return self.randrange(a, b+1) + +## -------------------- sequence methods ------------------- + + def choice(self, seq): + """Choose a random element from a non-empty sequence.""" + return seq[int(self.random() * len(seq))] + + def shuffle(self, x, random=None, int=int): + """x, random=random.random -> shuffle list x in place; return None. + + Optional arg random is a 0-argument function returning a random + float in [0.0, 1.0); by default, the standard random.random. + + Note that for even rather small len(x), the total number of + permutations of x is larger than the period of most random number + generators; this implies that "most" permutations of a long + sequence can never be generated. + """ + + if random is None: + random = self.random + for i in xrange(len(x)-1, 0, -1): + # pick an element in x[:i+1] with which to exchange x[i] + j = int(random() * (i+1)) + x[i], x[j] = x[j], x[i] + +## -------------------- real-valued distributions ------------------- + +## -------------------- uniform distribution ------------------- + + def uniform(self, a, b): + """Get a random number in the range [a, b).""" + return a + (b-a) * self.random() + +## -------------------- normal distribution -------------------- + + def normalvariate(self, mu, sigma): + """Normal distribution. + + mu is the mean, and sigma is the standard deviation. + + """ + # mu = mean, sigma = standard deviation + + # Uses Kinderman and Monahan method. Reference: Kinderman, + # A.J. and Monahan, J.F., "Computer generation of random + # variables using the ratio of uniform deviates", ACM Trans + # Math Software, 3, (1977), pp257-260. + + random = self.random + while 1: + u1 = random() + u2 = random() + z = NV_MAGICCONST*(u1-0.5)/u2 + zz = z*z/4.0 + if zz <= -_log(u2): + break + return mu + z*sigma + +## -------------------- lognormal distribution -------------------- + + def lognormvariate(self, mu, sigma): + """Log normal distribution. + + If you take the natural logarithm of this distribution, you'll get a + normal distribution with mean mu and standard deviation sigma. + mu can have any value, and sigma must be greater than zero. + + """ + return _exp(self.normalvariate(mu, sigma)) + +## -------------------- circular uniform -------------------- + + def cunifvariate(self, mean, arc): + """Circular uniform distribution. + + mean is the mean angle, and arc is the range of the distribution, + centered around the mean angle. Both values must be expressed in + radians. Returned values range between mean - arc/2 and + mean + arc/2 and are normalized to between 0 and pi. + + Deprecated in version 2.3. Use: + (mean + arc * (Random.random() - 0.5)) % Math.pi + + """ + # mean: mean angle (in radians between 0 and pi) + # arc: range of distribution (in radians between 0 and pi) + + return (mean + arc * (self.random() - 0.5)) % _pi + +## -------------------- exponential distribution -------------------- + + def expovariate(self, lambd): + """Exponential distribution. + + lambd is 1.0 divided by the desired mean. (The parameter would be + called "lambda", but that is a reserved word in Python.) Returned + values range from 0 to positive infinity. + + """ + # lambd: rate lambd = 1/mean + # ('lambda' is a Python reserved word) + + random = self.random + u = random() + while u <= 1e-7: + u = random() + return -_log(u)/lambd + +## -------------------- von Mises distribution -------------------- + + def vonmisesvariate(self, mu, kappa): + """Circular data distribution. + + mu is the mean angle, expressed in radians between 0 and 2*pi, and + kappa is the concentration parameter, which must be greater than or + equal to zero. If kappa is equal to zero, this distribution reduces + to a uniform random angle over the range 0 to 2*pi. + + """ + # mu: mean angle (in radians between 0 and 2*pi) + # kappa: concentration parameter kappa (>= 0) + # if kappa = 0 generate uniform random angle + + # Based upon an algorithm published in: Fisher, N.I., + # "Statistical Analysis of Circular Data", Cambridge + # University Press, 1993. + + # Thanks to Magnus Kessler for a correction to the + # implementation of step 4. + + random = self.random + if kappa <= 1e-6: + return TWOPI * random() + + a = 1.0 + _sqrt(1.0 + 4.0 * kappa * kappa) + b = (a - _sqrt(2.0 * a))/(2.0 * kappa) + r = (1.0 + b * b)/(2.0 * b) + + while 1: + u1 = random() + + z = _cos(_pi * u1) + f = (1.0 + r * z)/(r + z) + c = kappa * (r - f) + + u2 = random() + + if not (u2 >= c * (2.0 - c) and u2 > c * _exp(1.0 - c)): + break + + u3 = random() + if u3 > 0.5: + theta = (mu % TWOPI) + _acos(f) + else: + theta = (mu % TWOPI) - _acos(f) + + return theta + +## -------------------- gamma distribution -------------------- + + def gammavariate(self, alpha, beta): + """Gamma distribution. Not the gamma function! + + Conditions on the parameters are alpha > 0 and beta > 0. + + """ + + # alpha > 0, beta > 0, mean is alpha*beta, variance is alpha*beta**2 + + # Warning: a few older sources define the gamma distribution in terms + # of alpha > -1.0 + if alpha <= 0.0 or beta <= 0.0: + raise ValueError, 'gammavariate: alpha and beta must be > 0.0' + + random = self.random + if alpha > 1.0: + + # Uses R.C.H. Cheng, "The generation of Gamma + # variables with non-integral shape parameters", + # Applied Statistics, (1977), 26, No. 1, p71-74 + + ainv = _sqrt(2.0 * alpha - 1.0) + bbb = alpha - LOG4 + ccc = alpha + ainv + + while 1: + u1 = random() + u2 = random() + v = _log(u1/(1.0-u1))/ainv + x = alpha*_exp(v) + z = u1*u1*u2 + r = bbb+ccc*v-x + if r + SG_MAGICCONST - 4.5*z >= 0.0 or r >= _log(z): + return x * beta + + elif alpha == 1.0: + # expovariate(1) + u = random() + while u <= 1e-7: + u = random() + return -_log(u) * beta + + else: # alpha is between 0 and 1 (exclusive) + + # Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle + + while 1: + u = random() + b = (_e + alpha)/_e + p = b*u + if p <= 1.0: + x = pow(p, 1.0/alpha) + else: + # p > 1 + x = -_log((b-p)/alpha) + u1 = random() + if not (((p <= 1.0) and (u1 > _exp(-x))) or + ((p > 1) and (u1 > pow(x, alpha - 1.0)))): + break + return x * beta + + + def stdgamma(self, alpha, ainv, bbb, ccc): + # This method was (and shall remain) undocumented. + # This method is deprecated + # for the following reasons: + # 1. Returns same as .gammavariate(alpha, 1.0) + # 2. Requires caller to provide 3 extra arguments + # that are functions of alpha anyway + # 3. Can't be used for alpha < 0.5 + + # ainv = sqrt(2 * alpha - 1) + # bbb = alpha - log(4) + # ccc = alpha + ainv + import warnings + warnings.warn("The stdgamma function is deprecated; " + "use gammavariate() instead", + DeprecationWarning) + return self.gammavariate(alpha, 1.0) + + + +## -------------------- Gauss (faster alternative) -------------------- + + def gauss(self, mu, sigma): + """Gaussian distribution. + + mu is the mean, and sigma is the standard deviation. This is + slightly faster than the normalvariate() function. + + Not thread-safe without a lock around calls. + + """ + + # When x and y are two variables from [0, 1), uniformly + # distributed, then + # + # cos(2*pi*x)*sqrt(-2*log(1-y)) + # sin(2*pi*x)*sqrt(-2*log(1-y)) + # + # are two *independent* variables with normal distribution + # (mu = 0, sigma = 1). + # (Lambert Meertens) + # (corrected version; bug discovered by Mike Miller, fixed by LM) + + # Multithreading note: When two threads call this function + # simultaneously, it is possible that they will receive the + # same return value. The window is very small though. To + # avoid this, you have to use a lock around all calls. (I + # didn't want to slow this down in the serial case by using a + # lock here.) + + random = self.random + z = self.gauss_next + self.gauss_next = None + if z is None: + x2pi = random() * TWOPI + g2rad = _sqrt(-2.0 * _log(1.0 - random())) + z = _cos(x2pi) * g2rad + self.gauss_next = _sin(x2pi) * g2rad + + return mu + z*sigma + +## -------------------- beta -------------------- +## See +## http://sourceforge.net/bugs/?func=detailbug&bug_id=130030&group_id=5470 +## for Ivan Frohne's insightful analysis of why the original implementation: +## +## def betavariate(self, alpha, beta): +## # Discrete Event Simulation in C, pp 87-88. +## +## y = self.expovariate(alpha) +## z = self.expovariate(1.0/beta) +## return z/(y+z) +## +## was dead wrong, and how it probably got that way. + + def betavariate(self, alpha, beta): + """Beta distribution. + + Conditions on the parameters are alpha > -1 and beta} > -1. + Returned values range between 0 and 1. + + """ + + # This version due to Janne Sinkkonen, and matches all the std + # texts (e.g., Knuth Vol 2 Ed 3 pg 134 "the beta distribution"). + y = self.gammavariate(alpha, 1.) + if y == 0: + return 0.0 + else: + return y / (y + self.gammavariate(beta, 1.)) + +## -------------------- Pareto -------------------- + + def paretovariate(self, alpha): + """Pareto distribution. alpha is the shape parameter.""" + # Jain, pg. 495 + + u = self.random() + return 1.0 / pow(u, 1.0/alpha) + +## -------------------- Weibull -------------------- + + def weibullvariate(self, alpha, beta): + """Weibull distribution. + + alpha is the scale parameter and beta is the shape parameter. + + """ + # Jain, pg. 499; bug fix courtesy Bill Arms + + u = self.random() + return alpha * pow(-_log(u), 1.0/beta) + +## -------------------- test program -------------------- + +def _test_generator(n, funccall): + import time + print n, 'times', funccall + code = compile(funccall, funccall, 'eval') + sum = 0.0 + sqsum = 0.0 + smallest = 1e10 + largest = -1e10 + t0 = time.time() + for i in range(n): + x = eval(code) + sum = sum + x + sqsum = sqsum + x*x + smallest = min(x, smallest) + largest = max(x, largest) + t1 = time.time() + print round(t1-t0, 3), 'sec,', + avg = sum/n + stddev = _sqrt(sqsum/n - avg*avg) + print 'avg %g, stddev %g, min %g, max %g' % \ + (avg, stddev, smallest, largest) + +def _test(N=20000): + print 'TWOPI =', TWOPI + print 'LOG4 =', LOG4 + print 'NV_MAGICCONST =', NV_MAGICCONST + print 'SG_MAGICCONST =', SG_MAGICCONST + _test_generator(N, 'random()') + _test_generator(N, 'normalvariate(0.0, 1.0)') + _test_generator(N, 'lognormvariate(0.0, 1.0)') + _test_generator(N, 'cunifvariate(0.0, 1.0)') + _test_generator(N, 'expovariate(1.0)') + _test_generator(N, 'vonmisesvariate(0.0, 1.0)') + _test_generator(N, 'gammavariate(0.01, 1.0)') + _test_generator(N, 'gammavariate(0.1, 1.0)') + _test_generator(N, 'gammavariate(0.1, 2.0)') + _test_generator(N, 'gammavariate(0.5, 1.0)') + _test_generator(N, 'gammavariate(0.9, 1.0)') + _test_generator(N, 'gammavariate(1.0, 1.0)') + _test_generator(N, 'gammavariate(2.0, 1.0)') + _test_generator(N, 'gammavariate(20.0, 1.0)') + _test_generator(N, 'gammavariate(200.0, 1.0)') + _test_generator(N, 'gauss(0.0, 1.0)') + _test_generator(N, 'betavariate(3.0, 3.0)') + _test_generator(N, 'paretovariate(1.0)') + _test_generator(N, 'weibullvariate(1.0, 1.0)') + + # Test jumpahead. + s = getstate() + jumpahead(N) + r1 = random() + # now do it the slow way + setstate(s) + for i in range(N): + random() + r2 = random() + if r1 != r2: + raise ValueError("jumpahead test failed " + `(N, r1, r2)`) + +# Create one instance, seeded from current time, and export its methods +# as module-level functions. The functions are not threadsafe, and state +# is shared across all uses (both in the user's code and in the Python +# libraries), but that's fine for most programs and is easier for the +# casual user than making them instantiate their own Random() instance. +_inst = Random() +seed = _inst.seed +random = _inst.random +uniform = _inst.uniform +randint = _inst.randint +choice = _inst.choice +randrange = _inst.randrange +shuffle = _inst.shuffle +normalvariate = _inst.normalvariate +lognormvariate = _inst.lognormvariate +cunifvariate = _inst.cunifvariate +expovariate = _inst.expovariate +vonmisesvariate = _inst.vonmisesvariate +gammavariate = _inst.gammavariate +stdgamma = _inst.stdgamma +gauss = _inst.gauss +betavariate = _inst.betavariate +paretovariate = _inst.paretovariate +weibullvariate = _inst.weibullvariate +getstate = _inst.getstate +setstate = _inst.setstate +jumpahead = _inst.jumpahead +whseed = _inst.whseed + +if __name__ == '__main__': + _test() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/re.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/re.py new file mode 100644 index 00000000..21839a13 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/re.py @@ -0,0 +1,33 @@ +"""Minimal "re" compatibility wrapper""" + +# If your regexps don't work well under 2.0b1, you can switch +# to the old engine ("pre") down below. +# +# To help us fix any remaining bugs in the new engine, please +# report what went wrong. You can either use the following web +# page: +# +# http://sourceforge.net/bugs/?group_id=5470 +# +# or send a mail to SRE's author: +# +# Fredrik Lundh +# +# Make sure to include the pattern, the string SRE failed to +# match, and what result you expected. +# +# thanks /F +# + +engine = "sre" +# engine = "pre" + +if engine == "sre": + # New unicode-aware engine + from sre import * + from sre import __all__ +else: + # Old 1.5.2 engine. This one supports 8-bit strings only, + # and will be removed in 2.0 final. + from pre import * + from pre import __all__ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/regex_syntax.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/regex_syntax.py new file mode 100644 index 00000000..47406930 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/regex_syntax.py @@ -0,0 +1,53 @@ +"""Constants for selecting regexp syntaxes for the obsolete regex module. + +This module is only for backward compatibility. "regex" has now +been replaced by the new regular expression module, "re". + +These bits are passed to regex.set_syntax() to choose among +alternative regexp syntaxes. +""" + +# 1 means plain parentheses serve as grouping, and backslash +# parentheses are needed for literal searching. +# 0 means backslash-parentheses are grouping, and plain parentheses +# are for literal searching. +RE_NO_BK_PARENS = 1 + +# 1 means plain | serves as the "or"-operator, and \| is a literal. +# 0 means \| serves as the "or"-operator, and | is a literal. +RE_NO_BK_VBAR = 2 + +# 0 means plain + or ? serves as an operator, and \+, \? are literals. +# 1 means \+, \? are operators and plain +, ? are literals. +RE_BK_PLUS_QM = 4 + +# 1 means | binds tighter than ^ or $. +# 0 means the contrary. +RE_TIGHT_VBAR = 8 + +# 1 means treat \n as an _OR operator +# 0 means treat it as a normal character +RE_NEWLINE_OR = 16 + +# 0 means that a special characters (such as *, ^, and $) always have +# their special meaning regardless of the surrounding context. +# 1 means that special characters may act as normal characters in some +# contexts. Specifically, this applies to: +# ^ - only special at the beginning, or after ( or | +# $ - only special at the end, or before ) or | +# *, +, ? - only special when not after the beginning, (, or | +RE_CONTEXT_INDEP_OPS = 32 + +# ANSI sequences (\n etc) and \xhh +RE_ANSI_HEX = 64 + +# No GNU extensions +RE_NO_GNU_EXTENSIONS = 128 + +# Now define combinations of bits for the standard possibilities. +RE_SYNTAX_AWK = (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS) +RE_SYNTAX_EGREP = (RE_SYNTAX_AWK | RE_NEWLINE_OR) +RE_SYNTAX_GREP = (RE_BK_PLUS_QM | RE_NEWLINE_OR) +RE_SYNTAX_EMACS = 0 + +# (Python's obsolete "regexp" module used a syntax similar to awk.) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/repr.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/repr.py new file mode 100644 index 00000000..d5edda54 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/repr.py @@ -0,0 +1,95 @@ +"""Redo the `...` (representation) but with limits on most sizes.""" + +__all__ = ["Repr","repr"] + +class Repr: + def __init__(self): + self.maxlevel = 6 + self.maxtuple = 6 + self.maxlist = 6 + self.maxdict = 4 + self.maxstring = 30 + self.maxlong = 40 + self.maxother = 20 + def repr(self, x): + return self.repr1(x, self.maxlevel) + def repr1(self, x, level): + typename = type(x).__name__ + if ' ' in typename: + parts = typename.split() + typename = '_'.join(parts) + if hasattr(self, 'repr_' + typename): + return getattr(self, 'repr_' + typename)(x, level) + else: + s = `x` + if len(s) > self.maxother: + i = max(0, (self.maxother-3)//2) + j = max(0, self.maxother-3-i) + s = s[:i] + '...' + s[len(s)-j:] + return s + def repr_tuple(self, x, level): + n = len(x) + if n == 0: return '()' + if level <= 0: return '(...)' + s = '' + for i in range(min(n, self.maxtuple)): + if s: s = s + ', ' + s = s + self.repr1(x[i], level-1) + if n > self.maxtuple: s = s + ', ...' + elif n == 1: s = s + ',' + return '(' + s + ')' + def repr_list(self, x, level): + n = len(x) + if n == 0: return '[]' + if level <= 0: return '[...]' + s = '' + for i in range(min(n, self.maxlist)): + if s: s = s + ', ' + s = s + self.repr1(x[i], level-1) + if n > self.maxlist: s = s + ', ...' + return '[' + s + ']' + def repr_dict(self, x, level): + n = len(x) + if n == 0: return '{}' + if level <= 0: return '{...}' + s = '' + keys = x.keys() + keys.sort() + for i in range(min(n, self.maxdict)): + if s: s = s + ', ' + key = keys[i] + s = s + self.repr1(key, level-1) + s = s + ': ' + self.repr1(x[key], level-1) + if n > self.maxdict: s = s + ', ...' + return '{' + s + '}' + def repr_str(self, x, level): + s = `x[:self.maxstring]` + if len(s) > self.maxstring: + i = max(0, (self.maxstring-3)//2) + j = max(0, self.maxstring-3-i) + s = `x[:i] + x[len(x)-j:]` + s = s[:i] + '...' + s[len(s)-j:] + return s + def repr_long(self, x, level): + s = `x` # XXX Hope this isn't too slow... + if len(s) > self.maxlong: + i = max(0, (self.maxlong-3)//2) + j = max(0, self.maxlong-3-i) + s = s[:i] + '...' + s[len(s)-j:] + return s + def repr_instance(self, x, level): + try: + s = `x` + # Bugs in x.__repr__() can cause arbitrary + # exceptions -- then make up something + except: + return '<' + x.__class__.__name__ + ' instance at ' + \ + hex(id(x))[2:] + '>' + if len(s) > self.maxstring: + i = max(0, (self.maxstring-3)//2) + j = max(0, self.maxstring-3-i) + s = s[:i] + '...' + s[len(s)-j:] + return s + +aRepr = Repr() +repr = aRepr.repr diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/shlex.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/shlex.py new file mode 100644 index 00000000..5d91c380 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/shlex.py @@ -0,0 +1,209 @@ +"""A lexical analyzer class for simple shell-like syntaxes.""" + +# Module and documentation by Eric S. Raymond, 21 Dec 1998 +# Input stacking and error message cleanup added by ESR, March 2000 +# push_source() and pop_source() made explicit by ESR, January 2001. + +import os.path +import sys + +__all__ = ["shlex"] + +class shlex: + "A lexical analyzer class for simple shell-like syntaxes." + def __init__(self, instream=None, infile=None): + if instream: + self.instream = instream + self.infile = infile + else: + self.instream = sys.stdin + self.infile = None + self.commenters = '#' + self.wordchars = ('abcdfeghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_') + self.whitespace = ' \t\r\n' + self.quotes = '\'"' + self.state = ' ' + self.pushback = [] + self.lineno = 1 + self.debug = 0 + self.token = '' + self.filestack = [] + self.source = None + if self.debug: + print 'shlex: reading from %s, line %d' \ + % (self.instream, self.lineno) + + def push_token(self, tok): + "Push a token onto the stack popped by the get_token method" + if self.debug >= 1: + print "shlex: pushing token " + `tok` + self.pushback = [tok] + self.pushback + + def push_source(self, newstream, newfile=None): + "Push an input source onto the lexer's input source stack." + self.filestack.insert(0, (self.infile, self.instream, self.lineno)) + self.infile = newfile + self.instream = newstream + self.lineno = 1 + if self.debug: + if newfile: + print 'shlex: pushing to file %s' % (self.infile,) + else: + print 'shlex: pushing to stream %s' % (self.instream,) + + def pop_source(self): + "Pop the input source stack." + self.instream.close() + (self.infile, self.instream, self.lineno) = self.filestack[0] + self.filestack = self.filestack[1:] + if self.debug: + print 'shlex: popping to %s, line %d' \ + % (self.instream, self.lineno) + self.state = ' ' + + def get_token(self): + "Get a token from the input stream (or from stack if it's nonempty)" + if self.pushback: + tok = self.pushback[0] + self.pushback = self.pushback[1:] + if self.debug >= 1: + print "shlex: popping token " + `tok` + return tok + # No pushback. Get a token. + raw = self.read_token() + # Handle inclusions + while raw == self.source: + spec = self.sourcehook(self.read_token()) + if spec: + (newfile, newstream) = spec + self.push_source(newstream, newfile) + raw = self.get_token() + # Maybe we got EOF instead? + while raw == "": + if len(self.filestack) == 0: + return "" + else: + self.pop_source() + raw = self.get_token() + # Neither inclusion nor EOF + if self.debug >= 1: + if raw: + print "shlex: token=" + `raw` + else: + print "shlex: token=EOF" + return raw + + def read_token(self): + "Read a token from the input stream (no pushback or inclusions)" + while 1: + nextchar = self.instream.read(1) + if nextchar == '\n': + self.lineno = self.lineno + 1 + if self.debug >= 3: + print "shlex: in state", repr(self.state), \ + "I see character:", repr(nextchar) + if self.state is None: + self.token = '' # past end of file + break + elif self.state == ' ': + if not nextchar: + self.state = None # end of file + break + elif nextchar in self.whitespace: + if self.debug >= 2: + print "shlex: I see whitespace in whitespace state" + if self.token: + break # emit current token + else: + continue + elif nextchar in self.commenters: + self.instream.readline() + self.lineno = self.lineno + 1 + elif nextchar in self.wordchars: + self.token = nextchar + self.state = 'a' + elif nextchar in self.quotes: + self.token = nextchar + self.state = nextchar + else: + self.token = nextchar + if self.token: + break # emit current token + else: + continue + elif self.state in self.quotes: + self.token = self.token + nextchar + if nextchar == self.state: + self.state = ' ' + break + elif not nextchar: # end of file + if self.debug >= 2: + print "shlex: I see EOF in quotes state" + # XXX what error should be raised here? + raise ValueError, "No closing quotation" + elif self.state == 'a': + if not nextchar: + self.state = None # end of file + break + elif nextchar in self.whitespace: + if self.debug >= 2: + print "shlex: I see whitespace in word state" + self.state = ' ' + if self.token: + break # emit current token + else: + continue + elif nextchar in self.commenters: + self.instream.readline() + self.lineno = self.lineno + 1 + elif nextchar in self.wordchars or nextchar in self.quotes: + self.token = self.token + nextchar + else: + self.pushback = [nextchar] + self.pushback + if self.debug >= 2: + print "shlex: I see punctuation in word state" + self.state = ' ' + if self.token: + break # emit current token + else: + continue + result = self.token + self.token = '' + if self.debug > 1: + if result: + print "shlex: raw token=" + `result` + else: + print "shlex: raw token=EOF" + return result + + def sourcehook(self, newfile): + "Hook called on a filename to be sourced." + if newfile[0] == '"': + newfile = newfile[1:-1] + # This implements cpp-like semantics for relative-path inclusion. + if type(self.infile) == type("") and not os.path.isabs(newfile): + newfile = os.path.join(os.path.dirname(self.infile), newfile) + return (newfile, open(newfile, "r")) + + def error_leader(self, infile=None, lineno=None): + "Emit a C-compiler-like, Emacs-friendly error-message leader." + if not infile: + infile = self.infile + if not lineno: + lineno = self.lineno + return "\"%s\", line %d: " % (infile, lineno) + + +if __name__ == '__main__': + if len(sys.argv) == 1: + lexer = shlex() + else: + file = sys.argv[1] + lexer = shlex(open(file), file) + while 1: + tt = lexer.get_token() + if tt: + print "Token: " + repr(tt) + else: + break diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/shutil.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/shutil.py new file mode 100644 index 00000000..c11d46be --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/shutil.py @@ -0,0 +1,138 @@ +"""Utility functions for copying files and directory trees. + +XXX The functions here don't copy the resource fork or other metadata on Mac. + +""" + +import os +import sys +import stat + +__all__ = ["copyfileobj","copyfile","copymode","copystat","copy","copy2", + "copytree","rmtree"] + +def copyfileobj(fsrc, fdst, length=16*1024): + """copy data from file-like object fsrc to file-like object fdst""" + while 1: + buf = fsrc.read(length) + if not buf: + break + fdst.write(buf) + + +def copyfile(src, dst): + """Copy data from src to dst""" + fsrc = None + fdst = None + try: + fsrc = open(src, 'rb') + fdst = open(dst, 'wb') + copyfileobj(fsrc, fdst) + finally: + if fdst: + fdst.close() + if fsrc: + fsrc.close() + +def copymode(src, dst): + """Copy mode bits from src to dst""" + if hasattr(os, 'chmod'): + st = os.stat(src) + mode = stat.S_IMODE(st[stat.ST_MODE]) + os.chmod(dst, mode) + +def copystat(src, dst): + """Copy all stat info (mode bits, atime and mtime) from src to dst""" + st = os.stat(src) + mode = stat.S_IMODE(st[stat.ST_MODE]) + if hasattr(os, 'utime'): + os.utime(dst, (st[stat.ST_ATIME], st[stat.ST_MTIME])) + if hasattr(os, 'chmod'): + os.chmod(dst, mode) + + +def copy(src, dst): + """Copy data and mode bits ("cp src dst"). + + The destination may be a directory. + + """ + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + copyfile(src, dst) + copymode(src, dst) + +def copy2(src, dst): + """Copy data and all stat info ("cp -p src dst"). + + The destination may be a directory. + + """ + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + copyfile(src, dst) + copystat(src, dst) + + +def copytree(src, dst, symlinks=0): + """Recursively copy a directory tree using copy2(). + + The destination directory must not already exist. + Error are reported to standard output. + + If the optional symlinks flag is true, symbolic links in the + source tree result in symbolic links in the destination tree; if + it is false, the contents of the files pointed to by symbolic + links are copied. + + XXX Consider this example code rather than the ultimate tool. + + """ + names = os.listdir(src) + os.mkdir(dst) + for name in names: + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if symlinks and os.path.islink(srcname): + linkto = os.readlink(srcname) + os.symlink(linkto, dstname) + elif os.path.isdir(srcname): + copytree(srcname, dstname, symlinks) + else: + copy2(srcname, dstname) + # XXX What about devices, sockets etc.? + except (IOError, os.error), why: + print "Can't copy %s to %s: %s" % (`srcname`, `dstname`, str(why)) + +def rmtree(path, ignore_errors=0, onerror=None): + """Recursively delete a directory tree. + + If ignore_errors is set, errors are ignored; otherwise, if + onerror is set, it is called to handle the error; otherwise, an + exception is raised. + + """ + cmdtuples = [] + _build_cmdtuple(path, cmdtuples) + for cmd in cmdtuples: + try: + apply(cmd[0], (cmd[1],)) + except: + exc = sys.exc_info() + if ignore_errors: + pass + elif onerror: + onerror(cmd[0], cmd[1], exc) + else: + raise exc[0], (exc[1][0], exc[1][1] + ' removing '+cmd[1]) + +# Helper for rmtree() +def _build_cmdtuple(path, cmdtuples): + for f in os.listdir(path): + real_f = os.path.join(path,f) + if os.path.isdir(real_f) and not os.path.islink(real_f): + _build_cmdtuple(real_f, cmdtuples) + else: + cmdtuples.append((os.remove, real_f)) + cmdtuples.append((os.rmdir, path)) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/site.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/site.py new file mode 100644 index 00000000..751b650d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/site.py @@ -0,0 +1,330 @@ +"""Append module search paths for third-party packages to sys.path. + +**************************************************************** +* This module is automatically imported during initialization. * +**************************************************************** + +In earlier versions of Python (up to 1.5a3), scripts or modules that +needed to use site-specific modules would place ``import site'' +somewhere near the top of their code. Because of the automatic +import, this is no longer necessary (but code that does it still +works). + +This will append site-specific paths to to the module search path. On +Unix, it starts with sys.prefix and sys.exec_prefix (if different) and +appends lib/python/site-packages as well as lib/site-python. +On other platforms (mainly Mac and Windows), it uses just sys.prefix +(and sys.exec_prefix, if different, but this is unlikely). The +resulting directories, if they exist, are appended to sys.path, and +also inspected for path configuration files. + +A path configuration file is a file whose name has the form +.pth; its contents are additional directories (one per line) +to be added to sys.path. Non-existing directories (or +non-directories) are never added to sys.path; no directory is added to +sys.path more than once. Blank lines and lines beginning with +'#' are skipped. Lines starting with 'import' are executed. + +For example, suppose sys.prefix and sys.exec_prefix are set to +/usr/local and there is a directory /usr/local/lib/python1.5/site-packages +with three subdirectories, foo, bar and spam, and two path +configuration files, foo.pth and bar.pth. Assume foo.pth contains the +following: + + # foo package configuration + foo + bar + bletch + +and bar.pth contains: + + # bar package configuration + bar + +Then the following directories are added to sys.path, in this order: + + /usr/local/lib/python1.5/site-packages/bar + /usr/local/lib/python1.5/site-packages/foo + +Note that bletch is omitted because it doesn't exist; bar precedes foo +because bar.pth comes alphabetically before foo.pth; and spam is +omitted because it is not mentioned in either path configuration file. + +After these path manipulations, an attempt is made to import a module +named sitecustomize, which can perform arbitrary additional +site-specific customizations. If this import fails with an +ImportError exception, it is silently ignored. + +""" + +import sys, os + + +def makepath(*paths): + dir = os.path.abspath(os.path.join(*paths)) + return dir, os.path.normcase(dir) + +for m in sys.modules.values(): + if hasattr(m, "__file__") and m.__file__: + m.__file__ = os.path.abspath(m.__file__) +del m + +# This ensures that the initial path provided by the interpreter contains +# only absolute pathnames, even if we're running from the build directory. +L = [] +_dirs_in_sys_path = {} +for dir in sys.path: + # Filter out paths that don't exist, but leave in the empty string + # since it's a special case. We also need to special-case the Mac, + # as file names are allowed on sys.path there. + if sys.platform != 'mac': + if dir and not os.path.isdir(dir): + continue + else: + if dir and not os.path.exists(dir): + continue + dir, dircase = makepath(dir) + if not _dirs_in_sys_path.has_key(dircase): + L.append(dir) + _dirs_in_sys_path[dircase] = 1 +sys.path[:] = L +del dir, L + +# Append ./build/lib. in case we're running in the build dir +# (especially for Guido :-) +if (os.name == "posix" and sys.path and + os.path.basename(sys.path[-1]) == "Modules"): + from distutils.util import get_platform + s = "build/lib.%s-%.3s" % (get_platform(), sys.version) + s = os.path.join(os.path.dirname(sys.path[-1]), s) + sys.path.append(s) + del get_platform, s + +def _init_pathinfo(): + global _dirs_in_sys_path + _dirs_in_sys_path = d = {} + for dir in sys.path: + if dir and not os.path.isdir(dir): + continue + dir, dircase = makepath(dir) + d[dircase] = 1 + +def addsitedir(sitedir): + global _dirs_in_sys_path + if _dirs_in_sys_path is None: + _init_pathinfo() + reset = 1 + else: + reset = 0 + sitedir, sitedircase = makepath(sitedir) + if not _dirs_in_sys_path.has_key(sitedircase): + sys.path.append(sitedir) # Add path component + try: + names = os.listdir(sitedir) + except os.error: + return + names.sort() + for name in names: + if name[-4:] == os.extsep + "pth": + addpackage(sitedir, name) + if reset: + _dirs_in_sys_path = None + +def addpackage(sitedir, name): + global _dirs_in_sys_path + if _dirs_in_sys_path is None: + _init_pathinfo() + reset = 1 + else: + reset = 0 + fullname = os.path.join(sitedir, name) + try: + f = open(fullname) + except IOError: + return + while 1: + dir = f.readline() + if not dir: + break + if dir[0] == '#': + continue + if dir.startswith("import"): + exec dir + continue + if dir[-1] == '\n': + dir = dir[:-1] + dir, dircase = makepath(sitedir, dir) + if not _dirs_in_sys_path.has_key(dircase) and os.path.exists(dir): + sys.path.append(dir) + _dirs_in_sys_path[dircase] = 1 + if reset: + _dirs_in_sys_path = None + +prefixes = [sys.prefix] +if sys.exec_prefix != sys.prefix: + prefixes.append(sys.exec_prefix) +for prefix in prefixes: + if prefix: + if os.sep == '/': + sitedirs = [os.path.join(prefix, + "lib", + "python" + sys.version[:3], + "site-packages"), + os.path.join(prefix, "lib", "site-python")] + else: + sitedirs = [prefix, os.path.join(prefix, "lib", "site-packages")] + for sitedir in sitedirs: + if os.path.isdir(sitedir): + addsitedir(sitedir) + +_dirs_in_sys_path = None + + +# Define new built-ins 'quit' and 'exit'. +# These are simply strings that display a hint on how to exit. +if os.sep == ':': + exit = 'Use Cmd-Q to quit.' +elif os.sep == '\\': + exit = 'Use Ctrl-Z plus Return to exit.' +else: + exit = 'Use Ctrl-D (i.e. EOF) to exit.' +import __builtin__ +__builtin__.quit = __builtin__.exit = exit +del exit + +# interactive prompt objects for printing the license text, a list of +# contributors and the copyright notice. +class _Printer: + MAXLINES = 23 + + def __init__(self, name, data, files=(), dirs=()): + self.__name = name + self.__data = data + self.__files = files + self.__dirs = dirs + self.__lines = None + + def __setup(self): + if self.__lines: + return + data = None + for dir in self.__dirs: + for file in self.__files: + file = os.path.join(dir, file) + try: + fp = open(file) + data = fp.read() + fp.close() + break + except IOError: + pass + if data: + break + if not data: + data = self.__data + self.__lines = data.split('\n') + self.__linecnt = len(self.__lines) + + def __repr__(self): + self.__setup() + if len(self.__lines) <= self.MAXLINES: + return "\n".join(self.__lines) + else: + return "Type %s() to see the full %s text" % ((self.__name,)*2) + + def __call__(self): + self.__setup() + prompt = 'Hit Return for more, or q (and Return) to quit: ' + lineno = 0 + while 1: + try: + for i in range(lineno, lineno + self.MAXLINES): + print self.__lines[i] + except IndexError: + break + else: + lineno += self.MAXLINES + key = None + while key is None: + key = raw_input(prompt) + if key not in ('', 'q'): + key = None + if key == 'q': + break + +__builtin__.copyright = _Printer("copyright", sys.copyright) +if sys.platform[:4] == 'java': + __builtin__.credits = _Printer( + "credits", + "Jython is maintained by the Jython developers (www.jython.org).") +else: + __builtin__.credits = _Printer("credits", """\ +Thanks to CWI, CNRI, BeOpen.com, Digital Creations and a cast of thousands +for supporting Python development. See www.python.org for more information.""") +here = os.path.dirname(os.__file__) +__builtin__.license = _Printer( + "license", "See http://www.python.org/%.3s/license.html" % sys.version, + ["LICENSE.txt", "LICENSE"], + [os.path.join(here, os.pardir), here, os.curdir]) + + +# Define new built-in 'help'. +# This is a wrapper around pydoc.help (with a twist). + +class _Helper: + def __repr__(self): + return "Type help() for interactive help, " \ + "or help(object) for help about object." + def __call__(self, *args, **kwds): + import pydoc + return pydoc.help(*args, **kwds) + +__builtin__.help = _Helper() + + +# Set the string encoding used by the Unicode implementation. The +# default is 'ascii', but if you're willing to experiment, you can +# change this. + +encoding = "ascii" # Default value set by _PyUnicode_Init() + +if 0: + # Enable to support locale aware default string encodings. + import locale + loc = locale.getdefaultlocale() + if loc[1]: + encoding = loc[1] + +if 0: + # Enable to switch off string to Unicode coercion and implicit + # Unicode to string conversion. + encoding = "undefined" + +if encoding != "ascii": + # On Non-Unicode builds this will raise an AttributeError... + sys.setdefaultencoding(encoding) # Needs Python Unicode build ! + +# +# Run custom site specific code, if available. +# +try: + import sitecustomize +except ImportError: + pass + +# +# Remove sys.setdefaultencoding() so that users cannot change the +# encoding after initialization. The test for presence is needed when +# this module is run as a script, because this code is executed twice. +# +if hasattr(sys, "setdefaultencoding"): + del sys.setdefaultencoding + +def _test(): + print "sys.path = [" + for dir in sys.path: + print " %s," % `dir` + print "]" + +if __name__ == '__main__': + _test() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/spark.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/spark.py new file mode 100644 index 00000000..a0c5bc84 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/spark.py @@ -0,0 +1,566 @@ +# Copyright (c) 1998-2000 John Aycock +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__version__ = 'SPARK-0.6.1' + +import re +import sys +import string + +def _namelist(instance): + namelist, namedict, classlist = [], {}, [instance.__class__] + for c in classlist: + for b in c.__bases__: + classlist.append(b) + for name in dir(c): + if not namedict.has_key(name): + namelist.append(name) + namedict[name] = 1 + return namelist + +class GenericScanner: + def __init__(self): + pattern = self.reflect() + self.re = re.compile(pattern, re.VERBOSE) + + self.index2func = {} + for name, number in self.re.groupindex.items(): + self.index2func[number-1] = getattr(self, 't_' + name) + + def makeRE(self, name): + doc = getattr(self, name).__doc__ + rv = '(?P<%s>%s)' % (name[2:], doc) + return rv + + def reflect(self): + rv = [] + for name in _namelist(self): + if name[:2] == 't_' and name != 't_default': + rv.append(self.makeRE(name)) + + rv.append(self.makeRE('t_default')) + return string.join(rv, '|') + + def error(self, s, pos): + print "Lexical error at position %s" % pos + raise SystemExit + + def tokenize(self, s): + pos = 0 + n = len(s) + while pos < n: + m = self.re.match(s, pos) + if m is None: + self.error(s, pos) + + groups = m.groups() + for i in range(len(groups)): + if groups[i] and self.index2func.has_key(i): + self.index2func[i](groups[i]) + pos = m.end() + + def t_default(self, s): + r'( . | \n )+' + pass + +class GenericParser: + def __init__(self, start): + self.rules = {} + self.rule2func = {} + self.rule2name = {} + self.collectRules() + self.startRule = self.augment(start) + self.ruleschanged = 1 + + _START = 'START' + _EOF = 'EOF' + + # + # A hook for GenericASTBuilder and GenericASTMatcher. + # + def preprocess(self, rule, func): return rule, func + + def addRule(self, doc, func): + rules = string.split(doc) + + index = [] + for i in range(len(rules)): + if rules[i] == '::=': + index.append(i-1) + index.append(len(rules)) + + for i in range(len(index)-1): + lhs = rules[index[i]] + rhs = rules[index[i]+2:index[i+1]] + rule = (lhs, tuple(rhs)) + + rule, fn = self.preprocess(rule, func) + + if self.rules.has_key(lhs): + self.rules[lhs].append(rule) + else: + self.rules[lhs] = [ rule ] + self.rule2func[rule] = fn + self.rule2name[rule] = func.__name__[2:] + self.ruleschanged = 1 + + def collectRules(self): + for name in _namelist(self): + if name[:2] == 'p_': + func = getattr(self, name) + doc = func.__doc__ + self.addRule(doc, func) + + def augment(self, start): + # + # Tempting though it is, this isn't made into a call + # to self.addRule() because the start rule shouldn't + # be subject to preprocessing. + # + startRule = (self._START, ( start, self._EOF )) + self.rule2func[startRule] = lambda args: args[0] + self.rules[self._START] = [ startRule ] + self.rule2name[startRule] = '' + return startRule + + def makeFIRST(self): + union = {} + self.first = {} + + for rulelist in self.rules.values(): + for lhs, rhs in rulelist: + if not self.first.has_key(lhs): + self.first[lhs] = {} + + if len(rhs) == 0: + self.first[lhs][None] = 1 + continue + + sym = rhs[0] + if not self.rules.has_key(sym): + self.first[lhs][sym] = 1 + else: + union[(sym, lhs)] = 1 + changes = 1 + while changes: + changes = 0 + for src, dest in union.keys(): + destlen = len(self.first[dest]) + self.first[dest].update(self.first[src]) + if len(self.first[dest]) != destlen: + changes = 1 + + # + # An Earley parser, as per J. Earley, "An Efficient Context-Free + # Parsing Algorithm", CACM 13(2), pp. 94-102. Also J. C. Earley, + # "An Efficient Context-Free Parsing Algorithm", Ph.D. thesis, + # Carnegie-Mellon University, August 1968, p. 27. + # + + def typestring(self, token): + return None + + def error(self, token): + print "Syntax error at or near `%s' token" % token + raise SystemExit + + def parse(self, tokens): + tree = {} + tokens.append(self._EOF) + states = { 0: [ (self.startRule, 0, 0) ] } + + if self.ruleschanged: + self.makeFIRST() + + for i in xrange(len(tokens)): + states[i+1] = [] + + if states[i] == []: + break + self.buildState(tokens[i], states, i, tree) + + #_dump(tokens, states) + + if i < len(tokens)-1 or states[i+1] != [(self.startRule, 2, 0)]: + del tokens[-1] + self.error(tokens[i-1]) + rv = self.buildTree(tokens, tree, ((self.startRule, 2, 0), i+1)) + del tokens[-1] + return rv + + def buildState(self, token, states, i, tree): + needsCompletion = {} + state = states[i] + predicted = {} + + for item in state: + rule, pos, parent = item + lhs, rhs = rule + + # + # A -> a . (completer) + # + if pos == len(rhs): + if len(rhs) == 0: + needsCompletion[lhs] = (item, i) + + for pitem in states[parent]: + if pitem is item: + break + + prule, ppos, pparent = pitem + plhs, prhs = prule + + if prhs[ppos:ppos+1] == (lhs,): + new = (prule, + ppos+1, + pparent) + if new not in state: + state.append(new) + tree[(new, i)] = [(item, i)] + else: + tree[(new, i)].append((item, i)) + continue + + nextSym = rhs[pos] + + # + # A -> a . B (predictor) + # + if self.rules.has_key(nextSym): + # + # Work on completer step some more; for rules + # with empty RHS, the "parent state" is the + # current state we're adding Earley items to, + # so the Earley items the completer step needs + # may not all be present when it runs. + # + if needsCompletion.has_key(nextSym): + new = (rule, pos+1, parent) + olditem_i = needsCompletion[nextSym] + if new not in state: + state.append(new) + tree[(new, i)] = [olditem_i] + else: + tree[(new, i)].append(olditem_i) + + # + # Has this been predicted already? + # + if predicted.has_key(nextSym): + continue + predicted[nextSym] = 1 + + ttype = token is not self._EOF and \ + self.typestring(token) or \ + None + if ttype is not None: + # + # Even smarter predictor, when the + # token's type is known. The code is + # grungy, but runs pretty fast. Three + # cases are looked for: rules with + # empty RHS; first symbol on RHS is a + # terminal; first symbol on RHS is a + # nonterminal (and isn't nullable). + # + for prule in self.rules[nextSym]: + new = (prule, 0, i) + prhs = prule[1] + if len(prhs) == 0: + state.append(new) + continue + prhs0 = prhs[0] + if not self.rules.has_key(prhs0): + if prhs0 != ttype: + continue + else: + state.append(new) + continue + first = self.first[prhs0] + if not first.has_key(None) and \ + not first.has_key(ttype): + continue + state.append(new) + continue + + for prule in self.rules[nextSym]: + # + # Smarter predictor, as per Grune & + # Jacobs' _Parsing Techniques_. Not + # as good as FIRST sets though. + # + prhs = prule[1] + if len(prhs) > 0 and \ + not self.rules.has_key(prhs[0]) and \ + token != prhs[0]: + continue + state.append((prule, 0, i)) + + # + # A -> a . c (scanner) + # + elif token == nextSym: + #assert new not in states[i+1] + states[i+1].append((rule, pos+1, parent)) + + def buildTree(self, tokens, tree, root): + stack = [] + self.buildTree_r(stack, tokens, -1, tree, root) + return stack[0] + + def buildTree_r(self, stack, tokens, tokpos, tree, root): + (rule, pos, parent), state = root + + while pos > 0: + want = ((rule, pos, parent), state) + if not tree.has_key(want): + # + # Since pos > 0, it didn't come from closure, + # and if it isn't in tree[], then there must + # be a terminal symbol to the left of the dot. + # (It must be from a "scanner" step.) + # + pos = pos - 1 + state = state - 1 + stack.insert(0, tokens[tokpos]) + tokpos = tokpos - 1 + else: + # + # There's a NT to the left of the dot. + # Follow the tree pointer recursively (>1 + # tree pointers from it indicates ambiguity). + # Since the item must have come about from a + # "completer" step, the state where the item + # came from must be the parent state of the + # item the tree pointer points to. + # + children = tree[want] + if len(children) > 1: + child = self.ambiguity(children) + else: + child = children[0] + + tokpos = self.buildTree_r(stack, + tokens, tokpos, + tree, child) + pos = pos - 1 + (crule, cpos, cparent), cstate = child + state = cparent + + lhs, rhs = rule + result = self.rule2func[rule](stack[:len(rhs)]) + stack[:len(rhs)] = [result] + return tokpos + + def ambiguity(self, children): + # + # XXX - problem here and in collectRules() if the same + # rule appears in >1 method. But in that case the + # user probably gets what they deserve :-) Also + # undefined results if rules causing the ambiguity + # appear in the same method. + # + sortlist = [] + name2index = {} + for i in range(len(children)): + ((rule, pos, parent), index) = children[i] + lhs, rhs = rule + name = self.rule2name[rule] + sortlist.append((len(rhs), name)) + name2index[name] = i + sortlist.sort() + list = map(lambda (a,b): b, sortlist) + return children[name2index[self.resolve(list)]] + + def resolve(self, list): + # + # Resolve ambiguity in favor of the shortest RHS. + # Since we walk the tree from the top down, this + # should effectively resolve in favor of a "shift". + # + return list[0] + +# +# GenericASTBuilder automagically constructs a concrete/abstract syntax tree +# for a given input. The extra argument is a class (not an instance!) +# which supports the "__setslice__" and "__len__" methods. +# +# XXX - silently overrides any user code in methods. +# + +class GenericASTBuilder(GenericParser): + def __init__(self, AST, start): + GenericParser.__init__(self, start) + self.AST = AST + + def preprocess(self, rule, func): + rebind = lambda lhs, self=self: \ + lambda args, lhs=lhs, self=self: \ + self.buildASTNode(args, lhs) + lhs, rhs = rule + return rule, rebind(lhs) + + def buildASTNode(self, args, lhs): + children = [] + for arg in args: + if isinstance(arg, self.AST): + children.append(arg) + else: + children.append(self.terminal(arg)) + return self.nonterminal(lhs, children) + + def terminal(self, token): return token + + def nonterminal(self, type, args): + rv = self.AST(type) + rv[:len(args)] = args + return rv + +# +# GenericASTTraversal is a Visitor pattern according to Design Patterns. For +# each node it attempts to invoke the method n_, falling +# back onto the default() method if the n_* can't be found. The preorder +# traversal also looks for an exit hook named n__exit (no default +# routine is called if it's not found). To prematurely halt traversal +# of a subtree, call the prune() method -- this only makes sense for a +# preorder traversal. Node type is determined via the typestring() method. +# + +class GenericASTTraversalPruningException: + pass + +class GenericASTTraversal: + def __init__(self, ast): + self.ast = ast + + def typestring(self, node): + return node.type + + def prune(self): + raise GenericASTTraversalPruningException + + def preorder(self, node=None): + if node is None: + node = self.ast + + try: + name = 'n_' + self.typestring(node) + if hasattr(self, name): + func = getattr(self, name) + func(node) + else: + self.default(node) + except GenericASTTraversalPruningException: + return + + for kid in node: + self.preorder(kid) + + name = name + '_exit' + if hasattr(self, name): + func = getattr(self, name) + func(node) + + def postorder(self, node=None): + if node is None: + node = self.ast + + for kid in node: + self.postorder(kid) + + name = 'n_' + self.typestring(node) + if hasattr(self, name): + func = getattr(self, name) + func(node) + else: + self.default(node) + + + def default(self, node): + pass + +# +# GenericASTMatcher. AST nodes must have "__getitem__" and "__cmp__" +# implemented. +# +# XXX - makes assumptions about how GenericParser walks the parse tree. +# + +class GenericASTMatcher(GenericParser): + def __init__(self, start, ast): + GenericParser.__init__(self, start) + self.ast = ast + + def preprocess(self, rule, func): + rebind = lambda func, self=self: \ + lambda args, func=func, self=self: \ + self.foundMatch(args, func) + lhs, rhs = rule + rhslist = list(rhs) + rhslist.reverse() + + return (lhs, tuple(rhslist)), rebind(func) + + def foundMatch(self, args, func): + func(args[-1]) + return args[-1] + + def match_r(self, node): + self.input.insert(0, node) + children = 0 + + for child in node: + if children == 0: + self.input.insert(0, '(') + children = children + 1 + self.match_r(child) + + if children > 0: + self.input.insert(0, ')') + + def match(self, ast=None): + if ast is None: + ast = self.ast + self.input = [] + + self.match_r(ast) + self.parse(self.input) + + def resolve(self, list): + # + # Resolve ambiguity in favor of the longest RHS. + # + return list[-1] + +def _dump(tokens, states): + for i in range(len(states)): + print 'state', i + for (lhs, rhs), pos, parent in states[i]: + print '\t', lhs, '::=', + print string.join(rhs[:pos]), + print '.', + print string.join(rhs[pos:]), + print ',', parent + if i < len(tokens): + print + print 'token', str(tokens[i]) + print diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre.py new file mode 100644 index 00000000..5cfb754b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre.py @@ -0,0 +1,311 @@ +# +# Secret Labs' Regular Expression Engine +# +# re-compatible interface for the sre matching engine +# +# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved. +# +# This version of the SRE library can be redistributed under CNRI's +# Python 1.6 license. For any other use, please contact Secret Labs +# AB (info@pythonware.com). +# +# Portions of this engine have been developed in cooperation with +# CNRI. Hewlett-Packard provided funding for 1.6 integration and +# other compatibility work. +# + +r"""Support for regular expressions (RE). + +This module provides regular expression matching operations similar to +those found in Perl. It supports both 8-bit and Unicode strings; both +the pattern and the strings being processed can contain null bytes and +characters outside the US ASCII range. + +Regular expressions can contain both special and ordinary characters. +Most ordinary characters, like "A", "a", or "0", are the simplest +regular expressions; they simply match themselves. You can +concatenate ordinary characters, so last matches the string 'last'. + +The special characters are: + "." Matches any character except a newline. + "^" Matches the start of the string. + "$" Matches the end of the string. + "*" Matches 0 or more (greedy) repetitions of the preceding RE. + Greedy means that it will match as many repetitions as possible. + "+" Matches 1 or more (greedy) repetitions of the preceding RE. + "?" Matches 0 or 1 (greedy) of the preceding RE. + *?,+?,?? Non-greedy versions of the previous three special characters. + {m,n} Matches from m to n repetitions of the preceding RE. + {m,n}? Non-greedy version of the above. + "\\" Either escapes special characters or signals a special sequence. + [] Indicates a set of characters. + A "^" as the first character indicates a complementing set. + "|" A|B, creates an RE that will match either A or B. + (...) Matches the RE inside the parentheses. + The contents can be retrieved or matched later in the string. + (?iLmsux) Set the I, L, M, S, U, or X flag for the RE (see below). + (?:...) Non-grouping version of regular parentheses. + (?P...) The substring matched by the group is accessible by name. + (?P=name) Matches the text matched earlier by the group named name. + (?#...) A comment; ignored. + (?=...) Matches if ... matches next, but doesn't consume the string. + (?!...) Matches if ... doesn't match next. + +The special sequences consist of "\\" and a character from the list +below. If the ordinary character is not on the list, then the +resulting RE will match the second character. + \number Matches the contents of the group of the same number. + \A Matches only at the start of the string. + \Z Matches only at the end of the string. + \b Matches the empty string, but only at the start or end of a word. + \B Matches the empty string, but not at the start or end of a word. + \d Matches any decimal digit; equivalent to the set [0-9]. + \D Matches any non-digit character; equivalent to the set [^0-9]. + \s Matches any whitespace character; equivalent to [ \t\n\r\f\v]. + \S Matches any non-whitespace character; equiv. to [^ \t\n\r\f\v]. + \w Matches any alphanumeric character; equivalent to [a-zA-Z0-9_]. + With LOCALE, it will match the set [0-9_] plus characters defined + as letters for the current locale. + \W Matches the complement of \w. + \\ Matches a literal backslash. + +This module exports the following functions: + match Match a regular expression pattern to the beginning of a string. + search Search a string for the presence of a pattern. + sub Substitute occurrences of a pattern found in a string. + subn Same as sub, but also return the number of substitutions made. + split Split a string by the occurrences of a pattern. + findall Find all occurrences of a pattern in a string. + compile Compile a pattern into a RegexObject. + purge Clear the regular expression cache. + escape Backslash all non-alphanumerics in a string. + +Some of the functions in this module takes flags as optional parameters: + I IGNORECASE Perform case-insensitive matching. + L LOCALE Make \w, \W, \b, \B, dependent on the current locale. + M MULTILINE "^" matches the beginning of lines as well as the string. + "$" matches the end of lines as well as the string. + S DOTALL "." matches any character at all, including the newline. + X VERBOSE Ignore whitespace and comments for nicer looking RE's. + U UNICODE Make \w, \W, \b, \B, dependent on the Unicode locale. + +This module also defines an exception 'error'. + +""" + +import sys +import sre_compile +import sre_parse + +# public symbols +__all__ = [ "match", "search", "sub", "subn", "split", "findall", + "compile", "purge", "template", "escape", "I", "L", "M", "S", "X", + "U", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "VERBOSE", + "UNICODE", "error" ] + +__version__ = "2.2.1" + +# this module works under 1.5.2 and later. don't use string methods +import string + +# flags +I = IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case +L = LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale +U = UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode locale +M = MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline +S = DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline +X = VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments + +# sre extensions (experimental, don't rely on these) +T = TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking +DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation + +# sre exception +error = sre_compile.error + +# -------------------------------------------------------------------- +# public interface + +def match(pattern, string, flags=0): + """Try to apply the pattern at the start of the string, returning + a match object, or None if no match was found.""" + return _compile(pattern, flags).match(string) + +def search(pattern, string, flags=0): + """Scan through string looking for a match to the pattern, returning + a match object, or None if no match was found.""" + return _compile(pattern, flags).search(string) + +def sub(pattern, repl, string, count=0): + """Return the string obtained by replacing the leftmost + non-overlapping occurrences of the pattern in string by the + replacement repl""" + return _compile(pattern, 0).sub(repl, string, count) + +def subn(pattern, repl, string, count=0): + """Return a 2-tuple containing (new_string, number). + new_string is the string obtained by replacing the leftmost + non-overlapping occurrences of the pattern in the source + string by the replacement repl. number is the number of + substitutions that were made.""" + return _compile(pattern, 0).subn(repl, string, count) + +def split(pattern, string, maxsplit=0): + """Split the source string by the occurrences of the pattern, + returning a list containing the resulting substrings.""" + return _compile(pattern, 0).split(string, maxsplit) + +def findall(pattern, string): + """Return a list of all non-overlapping matches in the string. + + If one or more groups are present in the pattern, return a + list of groups; this will be a list of tuples if the pattern + has more than one group. + + Empty matches are included in the result.""" + return _compile(pattern, 0).findall(string) + +if sys.hexversion >= 0x02020000: + __all__.append("finditer") + def finditer(pattern, string): + """Return an iterator over all non-overlapping matches in the + string. For each match, the iterator returns a match object. + + Empty matches are included in the result.""" + return _compile(pattern, 0).finditer(string) + +def compile(pattern, flags=0): + "Compile a regular expression pattern, returning a pattern object." + return _compile(pattern, flags) + +def purge(): + "Clear the regular expression cache" + _cache.clear() + _cache_repl.clear() + +def template(pattern, flags=0): + "Compile a template pattern, returning a pattern object" + return _compile(pattern, flags|T) + +def escape(pattern): + "Escape all non-alphanumeric characters in pattern." + s = list(pattern) + for i in range(len(pattern)): + c = pattern[i] + if not ("a" <= c <= "z" or "A" <= c <= "Z" or "0" <= c <= "9"): + if c == "\000": + s[i] = "\\000" + else: + s[i] = "\\" + c + return _join(s, pattern) + +# -------------------------------------------------------------------- +# internals + +_cache = {} +_cache_repl = {} + +_pattern_type = type(sre_compile.compile("", 0)) + +_MAXCACHE = 100 + +def _join(seq, sep): + # internal: join into string having the same type as sep + return string.join(seq, sep[:0]) + +def _compile(*key): + # internal: compile pattern + p = _cache.get(key) + if p is not None: + return p + pattern, flags = key + if type(pattern) is _pattern_type: + return pattern + if type(pattern) not in sre_compile.STRING_TYPES: + raise TypeError, "first argument must be string or compiled pattern" + try: + p = sre_compile.compile(pattern, flags) + except error, v: + raise error, v # invalid expression + if len(_cache) >= _MAXCACHE: + _cache.clear() + _cache[key] = p + return p + +def _compile_repl(*key): + # internal: compile replacement pattern + p = _cache_repl.get(key) + if p is not None: + return p + repl, pattern = key + try: + p = sre_parse.parse_template(repl, pattern) + except error, v: + raise error, v # invalid expression + if len(_cache_repl) >= _MAXCACHE: + _cache_repl.clear() + _cache_repl[key] = p + return p + +def _expand(pattern, match, template): + # internal: match.expand implementation hook + template = sre_parse.parse_template(template, pattern) + return sre_parse.expand_template(template, match) + +def _subx(pattern, template): + # internal: pattern.sub/subn implementation helper + template = _compile_repl(template, pattern) + if not template[0] and len(template[1]) == 1: + # literal replacement + return template[1][0] + def filter(match, template=template): + return sre_parse.expand_template(template, match) + return filter + +# register myself for pickling + +import copy_reg + +def _pickle(p): + return _compile, (p.pattern, p.flags) + +copy_reg.pickle(_pattern_type, _pickle, _compile) + +# -------------------------------------------------------------------- +# experimental stuff (see python-dev discussions for details) + +class Scanner: + def __init__(self, lexicon, flags=0): + from sre_constants import BRANCH, SUBPATTERN + self.lexicon = lexicon + # combine phrases into a compound pattern + p = [] + s = sre_parse.Pattern() + s.flags = flags + for phrase, action in lexicon: + p.append(sre_parse.SubPattern(s, [ + (SUBPATTERN, (len(p)+1, sre_parse.parse(phrase, flags))), + ])) + p = sre_parse.SubPattern(s, [(BRANCH, (None, p))]) + s.groups = len(p) + self.scanner = sre_compile.compile(p) + def scan(self, string): + result = [] + append = result.append + match = self.scanner.scanner(string).match + i = 0 + while 1: + m = match() + if not m: + break + j = m.end() + if i == j: + break + action = self.lexicon[m.lastindex-1][1] + if callable(action): + self.match = m + action = action(self, m.group()) + if action is not None: + append(action) + i = j + return result, string[i:] diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_compile.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_compile.py new file mode 100644 index 00000000..1768a504 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_compile.py @@ -0,0 +1,455 @@ +# +# Secret Labs' Regular Expression Engine +# +# convert template to internal format +# +# Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved. +# +# See the sre.py file for information on usage and redistribution. +# + +"""Internal support module for sre""" + +import _sre, sys + +from sre_constants import * + +assert _sre.MAGIC == MAGIC, "SRE module mismatch" + +MAXCODE = 65535 + +def _compile(code, pattern, flags): + # internal: compile a (sub)pattern + emit = code.append + for op, av in pattern: + if op in (LITERAL, NOT_LITERAL): + if flags & SRE_FLAG_IGNORECASE: + emit(OPCODES[OP_IGNORE[op]]) + emit(_sre.getlower(av, flags)) + else: + emit(OPCODES[op]) + emit(av) + elif op is IN: + if flags & SRE_FLAG_IGNORECASE: + emit(OPCODES[OP_IGNORE[op]]) + def fixup(literal, flags=flags): + return _sre.getlower(literal, flags) + else: + emit(OPCODES[op]) + fixup = lambda x: x + skip = len(code); emit(0) + _compile_charset(av, flags, code, fixup) + code[skip] = len(code) - skip + elif op is ANY: + if flags & SRE_FLAG_DOTALL: + emit(OPCODES[ANY_ALL]) + else: + emit(OPCODES[ANY]) + elif op in (REPEAT, MIN_REPEAT, MAX_REPEAT): + if flags & SRE_FLAG_TEMPLATE: + raise error, "internal: unsupported template operator" + emit(OPCODES[REPEAT]) + skip = len(code); emit(0) + emit(av[0]) + emit(av[1]) + _compile(code, av[2], flags) + emit(OPCODES[SUCCESS]) + code[skip] = len(code) - skip + elif _simple(av) and op == MAX_REPEAT: + emit(OPCODES[REPEAT_ONE]) + skip = len(code); emit(0) + emit(av[0]) + emit(av[1]) + _compile(code, av[2], flags) + emit(OPCODES[SUCCESS]) + code[skip] = len(code) - skip + else: + emit(OPCODES[REPEAT]) + skip = len(code); emit(0) + emit(av[0]) + emit(av[1]) + _compile(code, av[2], flags) + code[skip] = len(code) - skip + if op == MAX_REPEAT: + emit(OPCODES[MAX_UNTIL]) + else: + emit(OPCODES[MIN_UNTIL]) + elif op is SUBPATTERN: + if av[0]: + emit(OPCODES[MARK]) + emit((av[0]-1)*2) + # _compile_info(code, av[1], flags) + _compile(code, av[1], flags) + if av[0]: + emit(OPCODES[MARK]) + emit((av[0]-1)*2+1) + elif op in (SUCCESS, FAILURE): + emit(OPCODES[op]) + elif op in (ASSERT, ASSERT_NOT): + emit(OPCODES[op]) + skip = len(code); emit(0) + if av[0] >= 0: + emit(0) # look ahead + else: + lo, hi = av[1].getwidth() + if lo != hi: + raise error, "look-behind requires fixed-width pattern" + emit(lo) # look behind + _compile(code, av[1], flags) + emit(OPCODES[SUCCESS]) + code[skip] = len(code) - skip + elif op is CALL: + emit(OPCODES[op]) + skip = len(code); emit(0) + _compile(code, av, flags) + emit(OPCODES[SUCCESS]) + code[skip] = len(code) - skip + elif op is AT: + emit(OPCODES[op]) + if flags & SRE_FLAG_MULTILINE: + av = AT_MULTILINE.get(av, av) + if flags & SRE_FLAG_LOCALE: + av = AT_LOCALE.get(av, av) + elif flags & SRE_FLAG_UNICODE: + av = AT_UNICODE.get(av, av) + emit(ATCODES[av]) + elif op is BRANCH: + emit(OPCODES[op]) + tail = [] + for av in av[1]: + skip = len(code); emit(0) + # _compile_info(code, av, flags) + _compile(code, av, flags) + emit(OPCODES[JUMP]) + tail.append(len(code)); emit(0) + code[skip] = len(code) - skip + emit(0) # end of branch + for tail in tail: + code[tail] = len(code) - tail + elif op is CATEGORY: + emit(OPCODES[op]) + if flags & SRE_FLAG_LOCALE: + av = CH_LOCALE[av] + elif flags & SRE_FLAG_UNICODE: + av = CH_UNICODE[av] + emit(CHCODES[av]) + elif op is GROUPREF: + if flags & SRE_FLAG_IGNORECASE: + emit(OPCODES[OP_IGNORE[op]]) + else: + emit(OPCODES[op]) + emit(av-1) + else: + raise ValueError, ("unsupported operand type", op) + +def _compile_charset(charset, flags, code, fixup=None): + # compile charset subprogram + emit = code.append + if not fixup: + fixup = lambda x: x + for op, av in _optimize_charset(charset, fixup): + emit(OPCODES[op]) + if op is NEGATE: + pass + elif op is LITERAL: + emit(fixup(av)) + elif op is RANGE: + emit(fixup(av[0])) + emit(fixup(av[1])) + elif op is CHARSET: + code.extend(av) + elif op is BIGCHARSET: + code.extend(av) + elif op is CATEGORY: + if flags & SRE_FLAG_LOCALE: + emit(CHCODES[CH_LOCALE[av]]) + elif flags & SRE_FLAG_UNICODE: + emit(CHCODES[CH_UNICODE[av]]) + else: + emit(CHCODES[av]) + else: + raise error, "internal: unsupported set operator" + emit(OPCODES[FAILURE]) + +def _optimize_charset(charset, fixup): + # internal: optimize character set + out = [] + charmap = [0]*256 + try: + for op, av in charset: + if op is NEGATE: + out.append((op, av)) + elif op is LITERAL: + charmap[fixup(av)] = 1 + elif op is RANGE: + for i in range(fixup(av[0]), fixup(av[1])+1): + charmap[i] = 1 + elif op is CATEGORY: + # XXX: could append to charmap tail + return charset # cannot compress + except IndexError: + if sys.maxunicode != 65535: + # XXX: big charsets don't work in UCS-4 builds + return charset + # character set contains unicode characters + return _optimize_unicode(charset, fixup) + # compress character map + i = p = n = 0 + runs = [] + for c in charmap: + if c: + if n == 0: + p = i + n = n + 1 + elif n: + runs.append((p, n)) + n = 0 + i = i + 1 + if n: + runs.append((p, n)) + if len(runs) <= 2: + # use literal/range + for p, n in runs: + if n == 1: + out.append((LITERAL, p)) + else: + out.append((RANGE, (p, p+n-1))) + if len(out) < len(charset): + return out + else: + # use bitmap + data = _mk_bitmap(charmap) + out.append((CHARSET, data)) + return out + return charset + +def _mk_bitmap(bits): + data = [] + m = 1; v = 0 + for c in bits: + if c: + v = v + m + m = m << 1 + if m > MAXCODE: + data.append(v) + m = 1; v = 0 + return data + +# To represent a big charset, first a bitmap of all characters in the +# set is constructed. Then, this bitmap is sliced into chunks of 256 +# characters, duplicate chunks are eliminitated, and each chunk is +# given a number. In the compiled expression, the charset is +# represented by a 16-bit word sequence, consisting of one word for +# the number of different chunks, a sequence of 256 bytes (128 words) +# of chunk numbers indexed by their original chunk position, and a +# sequence of chunks (16 words each). + +# Compression is normally good: in a typical charset, large ranges of +# Unicode will be either completely excluded (e.g. if only cyrillic +# letters are to be matched), or completely included (e.g. if large +# subranges of Kanji match). These ranges will be represented by +# chunks of all one-bits or all zero-bits. + +# Matching can be also done efficiently: the more significant byte of +# the Unicode character is an index into the chunk number, and the +# less significant byte is a bit index in the chunk (just like the +# CHARSET matching). + +def _optimize_unicode(charset, fixup): + charmap = [0]*65536 + negate = 0 + for op, av in charset: + if op is NEGATE: + negate = 1 + elif op is LITERAL: + charmap[fixup(av)] = 1 + elif op is RANGE: + for i in range(fixup(av[0]), fixup(av[1])+1): + charmap[i] = 1 + elif op is CATEGORY: + # XXX: could expand category + return charset # cannot compress + if negate: + for i in range(65536): + charmap[i] = not charmap[i] + comps = {} + mapping = [0]*256 + block = 0 + data = [] + for i in range(256): + chunk = tuple(charmap[i*256:(i+1)*256]) + new = comps.setdefault(chunk, block) + mapping[i] = new + if new == block: + block = block + 1 + data = data + _mk_bitmap(chunk) + header = [block] + assert MAXCODE == 65535 + for i in range(128): + if sys.byteorder == 'big': + header.append(256*mapping[2*i]+mapping[2*i+1]) + else: + header.append(mapping[2*i]+256*mapping[2*i+1]) + data[0:0] = header + return [(BIGCHARSET, data)] + +def _simple(av): + # check if av is a "simple" operator + lo, hi = av[2].getwidth() + if lo == 0 and hi == MAXREPEAT: + raise error, "nothing to repeat" + return lo == hi == 1 and av[2][0][0] != SUBPATTERN + +def _compile_info(code, pattern, flags): + # internal: compile an info block. in the current version, + # this contains min/max pattern width, and an optional literal + # prefix or a character map + lo, hi = pattern.getwidth() + if lo == 0: + return # not worth it + # look for a literal prefix + prefix = [] + prefix_skip = 0 + charset = [] # not used + if not (flags & SRE_FLAG_IGNORECASE): + # look for literal prefix + for op, av in pattern.data: + if op is LITERAL: + if len(prefix) == prefix_skip: + prefix_skip = prefix_skip + 1 + prefix.append(av) + elif op is SUBPATTERN and len(av[1]) == 1: + op, av = av[1][0] + if op is LITERAL: + prefix.append(av) + else: + break + else: + break + # if no prefix, look for charset prefix + if not prefix and pattern.data: + op, av = pattern.data[0] + if op is SUBPATTERN and av[1]: + op, av = av[1][0] + if op is LITERAL: + charset.append((op, av)) + elif op is BRANCH: + c = [] + for p in av[1]: + if not p: + break + op, av = p[0] + if op is LITERAL: + c.append((op, av)) + else: + break + else: + charset = c + elif op is BRANCH: + c = [] + for p in av[1]: + if not p: + break + op, av = p[0] + if op is LITERAL: + c.append((op, av)) + else: + break + else: + charset = c + elif op is IN: + charset = av +## if prefix: +## print "*** PREFIX", prefix, prefix_skip +## if charset: +## print "*** CHARSET", charset + # add an info block + emit = code.append + emit(OPCODES[INFO]) + skip = len(code); emit(0) + # literal flag + mask = 0 + if prefix: + mask = SRE_INFO_PREFIX + if len(prefix) == prefix_skip == len(pattern.data): + mask = mask + SRE_INFO_LITERAL + elif charset: + mask = mask + SRE_INFO_CHARSET + emit(mask) + # pattern length + if lo < MAXCODE: + emit(lo) + else: + emit(MAXCODE) + prefix = prefix[:MAXCODE] + if hi < MAXCODE: + emit(hi) + else: + emit(0) + # add literal prefix + if prefix: + emit(len(prefix)) # length + emit(prefix_skip) # skip + code.extend(prefix) + # generate overlap table + table = [-1] + ([0]*len(prefix)) + for i in range(len(prefix)): + table[i+1] = table[i]+1 + while table[i+1] > 0 and prefix[i] != prefix[table[i+1]-1]: + table[i+1] = table[table[i+1]-1]+1 + code.extend(table[1:]) # don't store first entry + elif charset: + _compile_charset(charset, 0, code) + code[skip] = len(code) - skip + +STRING_TYPES = [type("")] + +try: + STRING_TYPES.append(type(unicode(""))) +except NameError: + pass + +def _code(p, flags): + + flags = p.pattern.flags | flags + code = [] + + # compile info block + _compile_info(code, p, flags) + + # compile the pattern + _compile(code, p.data, flags) + + code.append(OPCODES[SUCCESS]) + + return code + +def compile(p, flags=0): + # internal: convert pattern list to internal format + + if type(p) in STRING_TYPES: + import sre_parse + pattern = p + p = sre_parse.parse(p, flags) + else: + pattern = None + + code = _code(p, flags) + + # print code + + # XXX: get rid of this limitation! + assert p.pattern.groups <= 100,\ + "sorry, but this version only supports 100 named groups" + + # map in either direction + groupindex = p.pattern.groupdict + indexgroup = [None] * p.pattern.groups + for k, i in groupindex.items(): + indexgroup[i] = k + + return _sre.compile( + pattern, flags, code, + p.pattern.groups-1, + groupindex, indexgroup + ) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_constants.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_constants.py new file mode 100644 index 00000000..1155f355 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_constants.py @@ -0,0 +1,259 @@ +# +# Secret Labs' Regular Expression Engine +# +# various symbols used by the regular expression engine. +# run this script to update the _sre include files! +# +# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved. +# +# See the sre.py file for information on usage and redistribution. +# + +"""Internal support module for sre""" + +# update when constants are added or removed + +MAGIC = 20010701 + +# max code word in this release + +MAXREPEAT = 65535 + +# SRE standard exception (access as sre.error) +# should this really be here? + +class error(Exception): + pass + +# operators + +FAILURE = "failure" +SUCCESS = "success" + +ANY = "any" +ANY_ALL = "any_all" +ASSERT = "assert" +ASSERT_NOT = "assert_not" +AT = "at" +BIGCHARSET = "bigcharset" +BRANCH = "branch" +CALL = "call" +CATEGORY = "category" +CHARSET = "charset" +GROUPREF = "groupref" +GROUPREF_IGNORE = "groupref_ignore" +IN = "in" +IN_IGNORE = "in_ignore" +INFO = "info" +JUMP = "jump" +LITERAL = "literal" +LITERAL_IGNORE = "literal_ignore" +MARK = "mark" +MAX_REPEAT = "max_repeat" +MAX_UNTIL = "max_until" +MIN_REPEAT = "min_repeat" +MIN_UNTIL = "min_until" +NEGATE = "negate" +NOT_LITERAL = "not_literal" +NOT_LITERAL_IGNORE = "not_literal_ignore" +RANGE = "range" +REPEAT = "repeat" +REPEAT_ONE = "repeat_one" +SUBPATTERN = "subpattern" + +# positions +AT_BEGINNING = "at_beginning" +AT_BEGINNING_LINE = "at_beginning_line" +AT_BEGINNING_STRING = "at_beginning_string" +AT_BOUNDARY = "at_boundary" +AT_NON_BOUNDARY = "at_non_boundary" +AT_END = "at_end" +AT_END_LINE = "at_end_line" +AT_END_STRING = "at_end_string" +AT_LOC_BOUNDARY = "at_loc_boundary" +AT_LOC_NON_BOUNDARY = "at_loc_non_boundary" +AT_UNI_BOUNDARY = "at_uni_boundary" +AT_UNI_NON_BOUNDARY = "at_uni_non_boundary" + +# categories +CATEGORY_DIGIT = "category_digit" +CATEGORY_NOT_DIGIT = "category_not_digit" +CATEGORY_SPACE = "category_space" +CATEGORY_NOT_SPACE = "category_not_space" +CATEGORY_WORD = "category_word" +CATEGORY_NOT_WORD = "category_not_word" +CATEGORY_LINEBREAK = "category_linebreak" +CATEGORY_NOT_LINEBREAK = "category_not_linebreak" +CATEGORY_LOC_WORD = "category_loc_word" +CATEGORY_LOC_NOT_WORD = "category_loc_not_word" +CATEGORY_UNI_DIGIT = "category_uni_digit" +CATEGORY_UNI_NOT_DIGIT = "category_uni_not_digit" +CATEGORY_UNI_SPACE = "category_uni_space" +CATEGORY_UNI_NOT_SPACE = "category_uni_not_space" +CATEGORY_UNI_WORD = "category_uni_word" +CATEGORY_UNI_NOT_WORD = "category_uni_not_word" +CATEGORY_UNI_LINEBREAK = "category_uni_linebreak" +CATEGORY_UNI_NOT_LINEBREAK = "category_uni_not_linebreak" + +OPCODES = [ + + # failure=0 success=1 (just because it looks better that way :-) + FAILURE, SUCCESS, + + ANY, ANY_ALL, + ASSERT, ASSERT_NOT, + AT, + BRANCH, + CALL, + CATEGORY, + CHARSET, BIGCHARSET, + GROUPREF, GROUPREF_IGNORE, + IN, IN_IGNORE, + INFO, + JUMP, + LITERAL, LITERAL_IGNORE, + MARK, + MAX_UNTIL, + MIN_UNTIL, + NOT_LITERAL, NOT_LITERAL_IGNORE, + NEGATE, + RANGE, + REPEAT, + REPEAT_ONE, + SUBPATTERN + +] + +ATCODES = [ + AT_BEGINNING, AT_BEGINNING_LINE, AT_BEGINNING_STRING, AT_BOUNDARY, + AT_NON_BOUNDARY, AT_END, AT_END_LINE, AT_END_STRING, + AT_LOC_BOUNDARY, AT_LOC_NON_BOUNDARY, AT_UNI_BOUNDARY, + AT_UNI_NON_BOUNDARY +] + +CHCODES = [ + CATEGORY_DIGIT, CATEGORY_NOT_DIGIT, CATEGORY_SPACE, + CATEGORY_NOT_SPACE, CATEGORY_WORD, CATEGORY_NOT_WORD, + CATEGORY_LINEBREAK, CATEGORY_NOT_LINEBREAK, CATEGORY_LOC_WORD, + CATEGORY_LOC_NOT_WORD, CATEGORY_UNI_DIGIT, CATEGORY_UNI_NOT_DIGIT, + CATEGORY_UNI_SPACE, CATEGORY_UNI_NOT_SPACE, CATEGORY_UNI_WORD, + CATEGORY_UNI_NOT_WORD, CATEGORY_UNI_LINEBREAK, + CATEGORY_UNI_NOT_LINEBREAK +] + +def makedict(list): + d = {} + i = 0 + for item in list: + d[item] = i + i = i + 1 + return d + +OPCODES = makedict(OPCODES) +ATCODES = makedict(ATCODES) +CHCODES = makedict(CHCODES) + +# replacement operations for "ignore case" mode +OP_IGNORE = { + GROUPREF: GROUPREF_IGNORE, + IN: IN_IGNORE, + LITERAL: LITERAL_IGNORE, + NOT_LITERAL: NOT_LITERAL_IGNORE +} + +AT_MULTILINE = { + AT_BEGINNING: AT_BEGINNING_LINE, + AT_END: AT_END_LINE +} + +AT_LOCALE = { + AT_BOUNDARY: AT_LOC_BOUNDARY, + AT_NON_BOUNDARY: AT_LOC_NON_BOUNDARY +} + +AT_UNICODE = { + AT_BOUNDARY: AT_UNI_BOUNDARY, + AT_NON_BOUNDARY: AT_UNI_NON_BOUNDARY +} + +CH_LOCALE = { + CATEGORY_DIGIT: CATEGORY_DIGIT, + CATEGORY_NOT_DIGIT: CATEGORY_NOT_DIGIT, + CATEGORY_SPACE: CATEGORY_SPACE, + CATEGORY_NOT_SPACE: CATEGORY_NOT_SPACE, + CATEGORY_WORD: CATEGORY_LOC_WORD, + CATEGORY_NOT_WORD: CATEGORY_LOC_NOT_WORD, + CATEGORY_LINEBREAK: CATEGORY_LINEBREAK, + CATEGORY_NOT_LINEBREAK: CATEGORY_NOT_LINEBREAK +} + +CH_UNICODE = { + CATEGORY_DIGIT: CATEGORY_UNI_DIGIT, + CATEGORY_NOT_DIGIT: CATEGORY_UNI_NOT_DIGIT, + CATEGORY_SPACE: CATEGORY_UNI_SPACE, + CATEGORY_NOT_SPACE: CATEGORY_UNI_NOT_SPACE, + CATEGORY_WORD: CATEGORY_UNI_WORD, + CATEGORY_NOT_WORD: CATEGORY_UNI_NOT_WORD, + CATEGORY_LINEBREAK: CATEGORY_UNI_LINEBREAK, + CATEGORY_NOT_LINEBREAK: CATEGORY_UNI_NOT_LINEBREAK +} + +# flags +SRE_FLAG_TEMPLATE = 1 # template mode (disable backtracking) +SRE_FLAG_IGNORECASE = 2 # case insensitive +SRE_FLAG_LOCALE = 4 # honour system locale +SRE_FLAG_MULTILINE = 8 # treat target as multiline string +SRE_FLAG_DOTALL = 16 # treat target as a single string +SRE_FLAG_UNICODE = 32 # use unicode locale +SRE_FLAG_VERBOSE = 64 # ignore whitespace and comments +SRE_FLAG_DEBUG = 128 # debugging + +# flags for INFO primitive +SRE_INFO_PREFIX = 1 # has prefix +SRE_INFO_LITERAL = 2 # entire pattern is literal (given by prefix) +SRE_INFO_CHARSET = 4 # pattern starts with character from given set + +if __name__ == "__main__": + import string + def dump(f, d, prefix): + items = d.items() + items.sort(lambda a, b: cmp(a[1], b[1])) + for k, v in items: + f.write("#define %s_%s %s\n" % (prefix, string.upper(k), v)) + f = open("sre_constants.h", "w") + f.write("""\ +/* + * Secret Labs' Regular Expression Engine + * + * regular expression matching engine + * + * NOTE: This file is generated by sre_constants.py. If you need + * to change anything in here, edit sre_constants.py and run it. + * + * Copyright (c) 1997-2001 by Secret Labs AB. All rights reserved. + * + * See the _sre.c file for information on usage and redistribution. + */ + +""") + + f.write("#define SRE_MAGIC %d\n" % MAGIC) + + dump(f, OPCODES, "SRE_OP") + dump(f, ATCODES, "SRE") + dump(f, CHCODES, "SRE") + + f.write("#define SRE_FLAG_TEMPLATE %d\n" % SRE_FLAG_TEMPLATE) + f.write("#define SRE_FLAG_IGNORECASE %d\n" % SRE_FLAG_IGNORECASE) + f.write("#define SRE_FLAG_LOCALE %d\n" % SRE_FLAG_LOCALE) + f.write("#define SRE_FLAG_MULTILINE %d\n" % SRE_FLAG_MULTILINE) + f.write("#define SRE_FLAG_DOTALL %d\n" % SRE_FLAG_DOTALL) + f.write("#define SRE_FLAG_UNICODE %d\n" % SRE_FLAG_UNICODE) + f.write("#define SRE_FLAG_VERBOSE %d\n" % SRE_FLAG_VERBOSE) + + f.write("#define SRE_INFO_PREFIX %d\n" % SRE_INFO_PREFIX) + f.write("#define SRE_INFO_LITERAL %d\n" % SRE_INFO_LITERAL) + f.write("#define SRE_INFO_CHARSET %d\n" % SRE_INFO_CHARSET) + + f.close() + print "done" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_parse.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_parse.py new file mode 100644 index 00000000..931a0317 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/sre_parse.py @@ -0,0 +1,738 @@ +# +# Secret Labs' Regular Expression Engine +# +# convert re-style regular expression to sre pattern +# +# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved. +# +# See the sre.py file for information on usage and redistribution. +# + +"""Internal support module for sre""" + +# XXX: show string offset and offending character for all errors + +# this module works under 1.5.2 and later. don't use string methods +import string, sys + +from sre_constants import * + +SPECIAL_CHARS = ".\\[{()*+?^$|" +REPEAT_CHARS = "*+?{" + +DIGITS = tuple("0123456789") + +OCTDIGITS = tuple("01234567") +HEXDIGITS = tuple("0123456789abcdefABCDEF") + +WHITESPACE = tuple(" \t\n\r\v\f") + +ESCAPES = { + r"\a": (LITERAL, ord("\a")), + r"\b": (LITERAL, ord("\b")), + r"\f": (LITERAL, ord("\f")), + r"\n": (LITERAL, ord("\n")), + r"\r": (LITERAL, ord("\r")), + r"\t": (LITERAL, ord("\t")), + r"\v": (LITERAL, ord("\v")), + r"\\": (LITERAL, ord("\\")) +} + +CATEGORIES = { + r"\A": (AT, AT_BEGINNING_STRING), # start of string + r"\b": (AT, AT_BOUNDARY), + r"\B": (AT, AT_NON_BOUNDARY), + r"\d": (IN, [(CATEGORY, CATEGORY_DIGIT)]), + r"\D": (IN, [(CATEGORY, CATEGORY_NOT_DIGIT)]), + r"\s": (IN, [(CATEGORY, CATEGORY_SPACE)]), + r"\S": (IN, [(CATEGORY, CATEGORY_NOT_SPACE)]), + r"\w": (IN, [(CATEGORY, CATEGORY_WORD)]), + r"\W": (IN, [(CATEGORY, CATEGORY_NOT_WORD)]), + r"\Z": (AT, AT_END_STRING), # end of string +} + +FLAGS = { + # standard flags + "i": SRE_FLAG_IGNORECASE, + "L": SRE_FLAG_LOCALE, + "m": SRE_FLAG_MULTILINE, + "s": SRE_FLAG_DOTALL, + "x": SRE_FLAG_VERBOSE, + # extensions + "t": SRE_FLAG_TEMPLATE, + "u": SRE_FLAG_UNICODE, +} + +# figure out best way to convert hex/octal numbers to integers +try: + int("10", 8) + atoi = int # 2.0 and later +except TypeError: + atoi = string.atoi # 1.5.2 + +class Pattern: + # master pattern object. keeps track of global attributes + def __init__(self): + self.flags = 0 + self.open = [] + self.groups = 1 + self.groupdict = {} + def opengroup(self, name=None): + gid = self.groups + self.groups = gid + 1 + if name: + ogid = self.groupdict.get(name, None) + if ogid is not None: + raise error, ("redefinition of group name %s as group %d; " + "was group %d" % (repr(name), gid, ogid)) + self.groupdict[name] = gid + self.open.append(gid) + return gid + def closegroup(self, gid): + self.open.remove(gid) + def checkgroup(self, gid): + return gid < self.groups and gid not in self.open + +class SubPattern: + # a subpattern, in intermediate form + def __init__(self, pattern, data=None): + self.pattern = pattern + if not data: + data = [] + self.data = data + self.width = None + def dump(self, level=0): + nl = 1 + for op, av in self.data: + print level*" " + op,; nl = 0 + if op == "in": + # member sublanguage + print; nl = 1 + for op, a in av: + print (level+1)*" " + op, a + elif op == "branch": + print; nl = 1 + i = 0 + for a in av[1]: + if i > 0: + print level*" " + "or" + a.dump(level+1); nl = 1 + i = i + 1 + elif type(av) in (type(()), type([])): + for a in av: + if isinstance(a, SubPattern): + if not nl: print + a.dump(level+1); nl = 1 + else: + print a, ; nl = 0 + else: + print av, ; nl = 0 + if not nl: print + def __repr__(self): + return repr(self.data) + def __len__(self): + return len(self.data) + def __delitem__(self, index): + del self.data[index] + def __getitem__(self, index): + return self.data[index] + def __setitem__(self, index, code): + self.data[index] = code + def __getslice__(self, start, stop): + return SubPattern(self.pattern, self.data[start:stop]) + def insert(self, index, code): + self.data.insert(index, code) + def append(self, code): + self.data.append(code) + def getwidth(self): + # determine the width (min, max) for this subpattern + if self.width: + return self.width + lo = hi = 0L + for op, av in self.data: + if op is BRANCH: + i = sys.maxint + j = 0 + for av in av[1]: + l, h = av.getwidth() + i = min(i, l) + j = max(j, h) + lo = lo + i + hi = hi + j + elif op is CALL: + i, j = av.getwidth() + lo = lo + i + hi = hi + j + elif op is SUBPATTERN: + i, j = av[1].getwidth() + lo = lo + i + hi = hi + j + elif op in (MIN_REPEAT, MAX_REPEAT): + i, j = av[2].getwidth() + lo = lo + long(i) * av[0] + hi = hi + long(j) * av[1] + elif op in (ANY, RANGE, IN, LITERAL, NOT_LITERAL, CATEGORY): + lo = lo + 1 + hi = hi + 1 + elif op == SUCCESS: + break + self.width = int(min(lo, sys.maxint)), int(min(hi, sys.maxint)) + return self.width + +class Tokenizer: + def __init__(self, string): + self.string = string + self.index = 0 + self.__next() + def __next(self): + if self.index >= len(self.string): + self.next = None + return + char = self.string[self.index] + if char[0] == "\\": + try: + c = self.string[self.index + 1] + except IndexError: + raise error, "bogus escape (end of line)" + char = char + c + self.index = self.index + len(char) + self.next = char + def match(self, char, skip=1): + if char == self.next: + if skip: + self.__next() + return 1 + return 0 + def get(self): + this = self.next + self.__next() + return this + def tell(self): + return self.index, self.next + def seek(self, index): + self.index, self.next = index + +def isident(char): + return "a" <= char <= "z" or "A" <= char <= "Z" or char == "_" + +def isdigit(char): + return "0" <= char <= "9" + +def isname(name): + # check that group name is a valid string + if not isident(name[0]): + return 0 + for char in name: + if not isident(char) and not isdigit(char): + return 0 + return 1 + +def _group(escape, groups): + # check if the escape string represents a valid group + try: + gid = atoi(escape[1:]) + if gid and gid < groups: + return gid + except ValueError: + pass + return None # not a valid group + +def _class_escape(source, escape): + # handle escape code inside character class + code = ESCAPES.get(escape) + if code: + return code + code = CATEGORIES.get(escape) + if code: + return code + try: + if escape[1:2] == "x": + # hexadecimal escape (exactly two digits) + while source.next in HEXDIGITS and len(escape) < 4: + escape = escape + source.get() + escape = escape[2:] + if len(escape) != 2: + raise error, "bogus escape: %s" % repr("\\" + escape) + return LITERAL, atoi(escape, 16) & 0xff + elif str(escape[1:2]) in OCTDIGITS: + # octal escape (up to three digits) + while source.next in OCTDIGITS and len(escape) < 5: + escape = escape + source.get() + escape = escape[1:] + return LITERAL, atoi(escape, 8) & 0xff + if len(escape) == 2: + return LITERAL, ord(escape[1]) + except ValueError: + pass + raise error, "bogus escape: %s" % repr(escape) + +def _escape(source, escape, state): + # handle escape code in expression + code = CATEGORIES.get(escape) + if code: + return code + code = ESCAPES.get(escape) + if code: + return code + try: + if escape[1:2] == "x": + # hexadecimal escape + while source.next in HEXDIGITS and len(escape) < 4: + escape = escape + source.get() + if len(escape) != 4: + raise ValueError + return LITERAL, atoi(escape[2:], 16) & 0xff + elif escape[1:2] == "0": + # octal escape + while source.next in OCTDIGITS and len(escape) < 4: + escape = escape + source.get() + return LITERAL, atoi(escape[1:], 8) & 0xff + elif escape[1:2] in DIGITS: + # octal escape *or* decimal group reference (sigh) + here = source.tell() + if source.next in DIGITS: + escape = escape + source.get() + if (escape[1] in OCTDIGITS and escape[2] in OCTDIGITS and + source.next in OCTDIGITS): + # got three octal digits; this is an octal escape + escape = escape + source.get() + return LITERAL, atoi(escape[1:], 8) & 0xff + # got at least one decimal digit; this is a group reference + group = _group(escape, state.groups) + if group: + if not state.checkgroup(group): + raise error, "cannot refer to open group" + return GROUPREF, group + raise ValueError + if len(escape) == 2: + return LITERAL, ord(escape[1]) + except ValueError: + pass + raise error, "bogus escape: %s" % repr(escape) + +def _parse_sub(source, state, nested=1): + # parse an alternation: a|b|c + + items = [] + while 1: + items.append(_parse(source, state)) + if source.match("|"): + continue + if not nested: + break + if not source.next or source.match(")", 0): + break + else: + raise error, "pattern not properly closed" + + if len(items) == 1: + return items[0] + + subpattern = SubPattern(state) + + # check if all items share a common prefix + while 1: + prefix = None + for item in items: + if not item: + break + if prefix is None: + prefix = item[0] + elif item[0] != prefix: + break + else: + # all subitems start with a common "prefix". + # move it out of the branch + for item in items: + del item[0] + subpattern.append(prefix) + continue # check next one + break + + # check if the branch can be replaced by a character set + for item in items: + if len(item) != 1 or item[0][0] != LITERAL: + break + else: + # we can store this as a character set instead of a + # branch (the compiler may optimize this even more) + set = [] + for item in items: + set.append(item[0]) + subpattern.append((IN, set)) + return subpattern + + subpattern.append((BRANCH, (None, items))) + return subpattern + +def _parse(source, state): + # parse a simple pattern + + subpattern = SubPattern(state) + + while 1: + + if source.next in ("|", ")"): + break # end of subpattern + this = source.get() + if this is None: + break # end of pattern + + if state.flags & SRE_FLAG_VERBOSE: + # skip whitespace and comments + if this in WHITESPACE: + continue + if this == "#": + while 1: + this = source.get() + if this in (None, "\n"): + break + continue + + if this and this[0] not in SPECIAL_CHARS: + subpattern.append((LITERAL, ord(this))) + + elif this == "[": + # character set + set = [] +## if source.match(":"): +## pass # handle character classes + if source.match("^"): + set.append((NEGATE, None)) + # check remaining characters + start = set[:] + while 1: + this = source.get() + if this == "]" and set != start: + break + elif this and this[0] == "\\": + code1 = _class_escape(source, this) + elif this: + code1 = LITERAL, ord(this) + else: + raise error, "unexpected end of regular expression" + if source.match("-"): + # potential range + this = source.get() + if this == "]": + if code1[0] is IN: + code1 = code1[1][0] + set.append(code1) + set.append((LITERAL, ord("-"))) + break + else: + if this[0] == "\\": + code2 = _class_escape(source, this) + else: + code2 = LITERAL, ord(this) + if code1[0] != LITERAL or code2[0] != LITERAL: + raise error, "bad character range" + lo = code1[1] + hi = code2[1] + if hi < lo: + raise error, "bad character range" + set.append((RANGE, (lo, hi))) + else: + if code1[0] is IN: + code1 = code1[1][0] + set.append(code1) + + # XXX: should move set optimization to compiler! + if len(set)==1 and set[0][0] is LITERAL: + subpattern.append(set[0]) # optimization + elif len(set)==2 and set[0][0] is NEGATE and set[1][0] is LITERAL: + subpattern.append((NOT_LITERAL, set[1][1])) # optimization + else: + # XXX: should add charmap optimization here + subpattern.append((IN, set)) + + elif this and this[0] in REPEAT_CHARS: + # repeat previous item + if this == "?": + min, max = 0, 1 + elif this == "*": + min, max = 0, MAXREPEAT + + elif this == "+": + min, max = 1, MAXREPEAT + elif this == "{": + here = source.tell() + min, max = 0, MAXREPEAT + lo = hi = "" + while source.next in DIGITS: + lo = lo + source.get() + if source.match(","): + while source.next in DIGITS: + hi = hi + source.get() + else: + hi = lo + if not source.match("}"): + subpattern.append((LITERAL, ord(this))) + source.seek(here) + continue + if lo: + min = atoi(lo) + if hi: + max = atoi(hi) + if max < min: + raise error, "bad repeat interval" + else: + raise error, "not supported" + # figure out which item to repeat + if subpattern: + item = subpattern[-1:] + else: + item = None + if not item or (len(item) == 1 and item[0][0] == AT): + raise error, "nothing to repeat" + if item[0][0] in (MIN_REPEAT, MAX_REPEAT): + raise error, "multiple repeat" + if source.match("?"): + subpattern[-1] = (MIN_REPEAT, (min, max, item)) + else: + subpattern[-1] = (MAX_REPEAT, (min, max, item)) + + elif this == ".": + subpattern.append((ANY, None)) + + elif this == "(": + group = 1 + name = None + if source.match("?"): + group = 0 + # options + if source.match("P"): + # python extensions + if source.match("<"): + # named group: skip forward to end of name + name = "" + while 1: + char = source.get() + if char is None: + raise error, "unterminated name" + if char == ">": + break + name = name + char + group = 1 + if not isname(name): + raise error, "bad character in group name" + elif source.match("="): + # named backreference + name = "" + while 1: + char = source.get() + if char is None: + raise error, "unterminated name" + if char == ")": + break + name = name + char + if not isname(name): + raise error, "bad character in group name" + gid = state.groupdict.get(name) + if gid is None: + raise error, "unknown group name" + subpattern.append((GROUPREF, gid)) + continue + else: + char = source.get() + if char is None: + raise error, "unexpected end of pattern" + raise error, "unknown specifier: ?P%s" % char + elif source.match(":"): + # non-capturing group + group = 2 + elif source.match("#"): + # comment + while 1: + if source.next is None or source.next == ")": + break + source.get() + if not source.match(")"): + raise error, "unbalanced parenthesis" + continue + elif source.next in ("=", "!", "<"): + # lookahead assertions + char = source.get() + dir = 1 + if char == "<": + if source.next not in ("=", "!"): + raise error, "syntax error" + dir = -1 # lookbehind + char = source.get() + p = _parse_sub(source, state) + if not source.match(")"): + raise error, "unbalanced parenthesis" + if char == "=": + subpattern.append((ASSERT, (dir, p))) + else: + subpattern.append((ASSERT_NOT, (dir, p))) + continue + else: + # flags + if not FLAGS.has_key(source.next): + raise error, "unexpected end of pattern" + while FLAGS.has_key(source.next): + state.flags = state.flags | FLAGS[source.get()] + if group: + # parse group contents + if group == 2: + # anonymous group + group = None + else: + group = state.opengroup(name) + p = _parse_sub(source, state) + if not source.match(")"): + raise error, "unbalanced parenthesis" + if group is not None: + state.closegroup(group) + subpattern.append((SUBPATTERN, (group, p))) + else: + while 1: + char = source.get() + if char is None: + raise error, "unexpected end of pattern" + if char == ")": + break + raise error, "unknown extension" + + elif this == "^": + subpattern.append((AT, AT_BEGINNING)) + + elif this == "$": + subpattern.append((AT, AT_END)) + + elif this and this[0] == "\\": + code = _escape(source, this, state) + subpattern.append(code) + + else: + raise error, "parser error" + + return subpattern + +def parse(str, flags=0, pattern=None): + # parse 're' pattern into list of (opcode, argument) tuples + + source = Tokenizer(str) + + if pattern is None: + pattern = Pattern() + pattern.flags = flags + pattern.str = str + + p = _parse_sub(source, pattern, 0) + + tail = source.get() + if tail == ")": + raise error, "unbalanced parenthesis" + elif tail: + raise error, "bogus characters at end of regular expression" + + if flags & SRE_FLAG_DEBUG: + p.dump() + + if not (flags & SRE_FLAG_VERBOSE) and p.pattern.flags & SRE_FLAG_VERBOSE: + # the VERBOSE flag was switched on inside the pattern. to be + # on the safe side, we'll parse the whole thing again... + return parse(str, p.pattern.flags) + + return p + +def parse_template(source, pattern): + # parse 're' replacement string into list of literals and + # group references + s = Tokenizer(source) + p = [] + a = p.append + def literal(literal, p=p): + if p and p[-1][0] is LITERAL: + p[-1] = LITERAL, p[-1][1] + literal + else: + p.append((LITERAL, literal)) + sep = source[:0] + if type(sep) is type(""): + makechar = chr + else: + makechar = unichr + while 1: + this = s.get() + if this is None: + break # end of replacement string + if this and this[0] == "\\": + # group + if this == "\\g": + name = "" + if s.match("<"): + while 1: + char = s.get() + if char is None: + raise error, "unterminated group name" + if char == ">": + break + name = name + char + if not name: + raise error, "bad group name" + try: + index = atoi(name) + except ValueError: + if not isname(name): + raise error, "bad character in group name" + try: + index = pattern.groupindex[name] + except KeyError: + raise IndexError, "unknown group name" + a((MARK, index)) + elif len(this) > 1 and this[1] in DIGITS: + code = None + while 1: + group = _group(this, pattern.groups+1) + if group: + if (s.next not in DIGITS or + not _group(this + s.next, pattern.groups+1)): + code = MARK, group + break + elif s.next in OCTDIGITS: + this = this + s.get() + else: + break + if not code: + this = this[1:] + code = LITERAL, makechar(atoi(this[-6:], 8) & 0xff) + if code[0] is LITERAL: + literal(code[1]) + else: + a(code) + else: + try: + this = makechar(ESCAPES[this][1]) + except KeyError: + pass + literal(this) + else: + literal(this) + # convert template to groups and literals lists + i = 0 + groups = [] + literals = [] + for c, s in p: + if c is MARK: + groups.append((i, s)) + literals.append(None) + else: + literals.append(s) + i = i + 1 + return groups, literals + +def expand_template(template, match): + g = match.group + sep = match.string[:0] + groups, literals = template + literals = literals[:] + try: + for index, group in groups: + literals[index] = s = g(group) + if s is None: + raise IndexError + except IndexError: + raise error, "empty group" + return string.join(literals, sep) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/stat.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/stat.py new file mode 100644 index 00000000..8bccf98e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/stat.py @@ -0,0 +1,86 @@ +"""Constants/functions for interpreting results of os.stat() and os.lstat(). + +Suggested usage: from stat import * +""" + +# XXX Strictly spoken, this module may have to be adapted for each POSIX +# implementation; in practice, however, the numeric constants used by +# stat() are almost universal (even for stat() emulations on non-UNIX +# systems like MS-DOS). + +# Indices for stat struct members in tuple returned by os.stat() + +ST_MODE = 0 +ST_INO = 1 +ST_DEV = 2 +ST_NLINK = 3 +ST_UID = 4 +ST_GID = 5 +ST_SIZE = 6 +ST_ATIME = 7 +ST_MTIME = 8 +ST_CTIME = 9 + +# Extract bits from the mode + +def S_IMODE(mode): + return mode & 07777 + +def S_IFMT(mode): + return mode & 0170000 + +# Constants used as S_IFMT() for various file types +# (not all are implemented on all systems) + +S_IFDIR = 0040000 +S_IFCHR = 0020000 +S_IFBLK = 0060000 +S_IFREG = 0100000 +S_IFIFO = 0010000 +S_IFLNK = 0120000 +S_IFSOCK = 0140000 + +# Functions to test for each file type + +def S_ISDIR(mode): + return S_IFMT(mode) == S_IFDIR + +def S_ISCHR(mode): + return S_IFMT(mode) == S_IFCHR + +def S_ISBLK(mode): + return S_IFMT(mode) == S_IFBLK + +def S_ISREG(mode): + return S_IFMT(mode) == S_IFREG + +def S_ISFIFO(mode): + return S_IFMT(mode) == S_IFIFO + +def S_ISLNK(mode): + return S_IFMT(mode) == S_IFLNK + +def S_ISSOCK(mode): + return S_IFMT(mode) == S_IFSOCK + +# Names for permission bits + +S_ISUID = 04000 +S_ISGID = 02000 +S_ENFMT = S_ISGID +S_ISVTX = 01000 +S_IREAD = 00400 +S_IWRITE = 00200 +S_IEXEC = 00100 +S_IRWXU = 00700 +S_IRUSR = 00400 +S_IWUSR = 00200 +S_IXUSR = 00100 +S_IRWXG = 00070 +S_IRGRP = 00040 +S_IWGRP = 00020 +S_IXGRP = 00010 +S_IRWXO = 00007 +S_IROTH = 00004 +S_IWOTH = 00002 +S_IXOTH = 00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/statcache.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/statcache.py new file mode 100644 index 00000000..6d62ac34 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/statcache.py @@ -0,0 +1,77 @@ +"""Maintain a cache of stat() information on files. + +There are functions to reset the cache or to selectively remove items. +""" + +import os as _os +from stat import * + +__all__ = ["stat","reset","forget","forget_prefix","forget_dir", + "forget_except_prefix","isdir"] + +# The cache. Keys are pathnames, values are os.stat outcomes. +# Remember that multiple threads may be calling this! So, e.g., that +# cache.has_key(path) returns 1 doesn't mean the cache will still contain +# path on the next line. Code defensively. + +cache = {} + +def stat(path): + """Stat a file, possibly out of the cache.""" + ret = cache.get(path, None) + if ret is None: + cache[path] = ret = _os.stat(path) + return ret + +def reset(): + """Clear the cache.""" + cache.clear() + +# For thread saftey, always use forget() internally too. +def forget(path): + """Remove a given item from the cache, if it exists.""" + try: + del cache[path] + except KeyError: + pass + +def forget_prefix(prefix): + """Remove all pathnames with a given prefix.""" + for path in cache.keys(): + if path.startswith(prefix): + forget(path) + +def forget_dir(prefix): + """Forget a directory and all entries except for entries in subdirs.""" + + # Remove trailing separator, if any. This is tricky to do in a + # x-platform way. For example, Windows accepts both / and \ as + # separators, and if there's nothing *but* a separator we want to + # preserve that this is the root. Only os.path has the platform + # knowledge we need. + from os.path import split, join + prefix = split(join(prefix, "xxx"))[0] + forget(prefix) + for path in cache.keys(): + # First check that the path at least starts with the prefix, so + # that when it doesn't we can avoid paying for split(). + if path.startswith(prefix) and split(path)[0] == prefix: + forget(path) + +def forget_except_prefix(prefix): + """Remove all pathnames except with a given prefix. + + Normally used with prefix = '/' after a chdir(). + """ + + for path in cache.keys(): + if not path.startswith(prefix): + forget(path) + +def isdir(path): + """Return 1 if directory, else 0.""" + try: + st = stat(path) + except _os.error: + return 0 + return S_ISDIR(st[ST_MODE]) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/string.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/string.py new file mode 100644 index 00000000..f25d94b8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/string.py @@ -0,0 +1,382 @@ +"""A collection of string operations (most are no longer used in Python 1.6). + +Warning: most of the code you see here isn't normally used nowadays. With +Python 1.6, many of these functions are implemented as methods on the +standard string object. They used to be implemented by a built-in module +called strop, but strop is now obsolete itself. + +Public module variables: + +whitespace -- a string containing all characters considered whitespace +lowercase -- a string containing all characters considered lowercase letters +uppercase -- a string containing all characters considered uppercase letters +letters -- a string containing all characters considered letters +digits -- a string containing all characters considered decimal digits +hexdigits -- a string containing all characters considered hexadecimal digits +octdigits -- a string containing all characters considered octal digits +punctuation -- a string containing all characters considered punctuation +printable -- a string containing all characters considered printable + +""" + +# Some strings for ctype-style character classification +whitespace = ' \t\n\r\v\f' +lowercase = 'abcdefghijklmnopqrstuvwxyz' +uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +letters = lowercase + uppercase +ascii_lowercase = lowercase +ascii_uppercase = uppercase +ascii_letters = ascii_lowercase + ascii_uppercase +digits = '0123456789' +hexdigits = digits + 'abcdef' + 'ABCDEF' +octdigits = '01234567' +punctuation = """!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" +printable = digits + letters + punctuation + whitespace + +# Case conversion helpers +_idmap = '' +for i in range(256): _idmap = _idmap + chr(i) +del i + +# Backward compatible names for exceptions +index_error = ValueError +atoi_error = ValueError +atof_error = ValueError +atol_error = ValueError + +# convert UPPER CASE letters to lower case +def lower(s): + """lower(s) -> string + + Return a copy of the string s converted to lowercase. + + """ + return s.lower() + +# Convert lower case letters to UPPER CASE +def upper(s): + """upper(s) -> string + + Return a copy of the string s converted to uppercase. + + """ + return s.upper() + +# Swap lower case letters and UPPER CASE +def swapcase(s): + """swapcase(s) -> string + + Return a copy of the string s with upper case characters + converted to lowercase and vice versa. + + """ + return s.swapcase() + +# Strip leading and trailing tabs and spaces +def strip(s): + """strip(s) -> string + + Return a copy of the string s with leading and trailing + whitespace removed. + + """ + return s.strip() + +# Strip leading tabs and spaces +def lstrip(s): + """lstrip(s) -> string + + Return a copy of the string s with leading whitespace removed. + + """ + return s.lstrip() + +# Strip trailing tabs and spaces +def rstrip(s): + """rstrip(s) -> string + + Return a copy of the string s with trailing whitespace + removed. + + """ + return s.rstrip() + + +# Split a string into a list of space/tab-separated words +def split(s, sep=None, maxsplit=-1): + """split(s [,sep [,maxsplit]]) -> list of strings + + Return a list of the words in the string s, using sep as the + delimiter string. If maxsplit is given, splits at no more than + maxsplit places (resulting in at most maxsplit+1 words). If sep + is not specified, any whitespace string is a separator. + + (split and splitfields are synonymous) + + """ + return s.split(sep, maxsplit) +splitfields = split + +# Join fields with optional separator +def join(words, sep = ' '): + """join(list [,sep]) -> string + + Return a string composed of the words in list, with + intervening occurrences of sep. The default separator is a + single space. + + (joinfields and join are synonymous) + + """ + return sep.join(words) +joinfields = join + +# Find substring, raise exception if not found +def index(s, *args): + """index(s, sub [,start [,end]]) -> int + + Like find but raises ValueError when the substring is not found. + + """ + return s.index(*args) + +# Find last substring, raise exception if not found +def rindex(s, *args): + """rindex(s, sub [,start [,end]]) -> int + + Like rfind but raises ValueError when the substring is not found. + + """ + return s.rindex(*args) + +# Count non-overlapping occurrences of substring +def count(s, *args): + """count(s, sub[, start[,end]]) -> int + + Return the number of occurrences of substring sub in string + s[start:end]. Optional arguments start and end are + interpreted as in slice notation. + + """ + return s.count(*args) + +# Find substring, return -1 if not found +def find(s, *args): + """find(s, sub [,start [,end]]) -> in + + Return the lowest index in s where substring sub is found, + such that sub is contained within s[start,end]. Optional + arguments start and end are interpreted as in slice notation. + + Return -1 on failure. + + """ + return s.find(*args) + +# Find last substring, return -1 if not found +def rfind(s, *args): + """rfind(s, sub [,start [,end]]) -> int + + Return the highest index in s where substring sub is found, + such that sub is contained within s[start,end]. Optional + arguments start and end are interpreted as in slice notation. + + Return -1 on failure. + + """ + return s.rfind(*args) + +# for a bit of speed +_float = float +_int = int +_long = long +try: + _StringTypes = (str, unicode) +except NameError: + _StringTypes = (str,) + +# Convert string to float +def atof(s): + """atof(s) -> float + + Return the floating point number represented by the string s. + + """ + return _float(s) + + +# Convert string to integer +def atoi(s , base=10): + """atoi(s [,base]) -> int + + Return the integer represented by the string s in the given + base, which defaults to 10. The string s must consist of one + or more digits, possibly preceded by a sign. If base is 0, it + is chosen from the leading characters of s, 0 for octal, 0x or + 0X for hexadecimal. If base is 16, a preceding 0x or 0X is + accepted. + + """ + return _int(s, base) + + +# Convert string to long integer +def atol(s, base=10): + """atol(s [,base]) -> long + + Return the long integer represented by the string s in the + given base, which defaults to 10. The string s must consist + of one or more digits, possibly preceded by a sign. If base + is 0, it is chosen from the leading characters of s, 0 for + octal, 0x or 0X for hexadecimal. If base is 16, a preceding + 0x or 0X is accepted. A trailing L or l is not accepted, + unless base is 0. + + """ + return _long(s, base) + + +# Left-justify a string +def ljust(s, width): + """ljust(s, width) -> string + + Return a left-justified version of s, in a field of the + specified width, padded with spaces as needed. The string is + never truncated. + + """ + return s.ljust(width) + +# Right-justify a string +def rjust(s, width): + """rjust(s, width) -> string + + Return a right-justified version of s, in a field of the + specified width, padded with spaces as needed. The string is + never truncated. + + """ + return s.rjust(width) + +# Center a string +def center(s, width): + """center(s, width) -> string + + Return a center version of s, in a field of the specified + width. padded with spaces as needed. The string is never + truncated. + + """ + return s.center(width) + +# Zero-fill a number, e.g., (12, 3) --> '012' and (-3, 3) --> '-03' +# Decadent feature: the argument may be a string or a number +# (Use of this is deprecated; it should be a string as with ljust c.s.) +def zfill(x, width): + """zfill(x, width) -> string + + Pad a numeric string x with zeros on the left, to fill a field + of the specified width. The string x is never truncated. + + """ + if not isinstance(x, _StringTypes): + x = repr(x) + return x.zfill(width) + +# Expand tabs in a string. +# Doesn't take non-printing chars into account, but does understand \n. +def expandtabs(s, tabsize=8): + """expandtabs(s [,tabsize]) -> string + + Return a copy of the string s with all tab characters replaced + by the appropriate number of spaces, depending on the current + column, and the tabsize (default 8). + + """ + return s.expandtabs(tabsize) + +# Character translation through look-up table. +def translate(s, table, deletions=""): + """translate(s,table [,deletions]) -> string + + Return a copy of the string s, where all characters occurring + in the optional argument deletions are removed, and the + remaining characters have been mapped through the given + translation table, which must be a string of length 256. The + deletions argument is not allowed for Unicode strings. + + """ + if deletions: + return s.translate(table, deletions) + else: + # Add s[:0] so that if s is Unicode and table is an 8-bit string, + # table is converted to Unicode. This means that table *cannot* + # be a dictionary -- for that feature, use u.translate() directly. + return s.translate(table + s[:0]) + +# Capitalize a string, e.g. "aBc dEf" -> "Abc def". +def capitalize(s): + """capitalize(s) -> string + + Return a copy of the string s with only its first character + capitalized. + + """ + return s.capitalize() + +# Capitalize the words in a string, e.g. " aBc dEf " -> "Abc Def". +# See also regsub.capwords(). +def capwords(s, sep=None): + """capwords(s, [sep]) -> string + + Split the argument into words using split, capitalize each + word using capitalize, and join the capitalized words using + join. Note that this replaces runs of whitespace characters by + a single space. + + """ + return join(map(capitalize, s.split(sep)), sep or ' ') + +# Construct a translation string +_idmapL = None +def maketrans(fromstr, tostr): + """maketrans(frm, to) -> string + + Return a translation table (a string of 256 bytes long) + suitable for use in string.translate. The strings frm and to + must be of the same length. + + """ + if len(fromstr) != len(tostr): + raise ValueError, "maketrans arguments must have same length" + global _idmapL + if not _idmapL: + _idmapL = map(None, _idmap) + L = _idmapL[:] + fromstr = map(ord, fromstr) + for i in range(len(fromstr)): + L[fromstr[i]] = tostr[i] + return join(L, "") + +# Substring replacement (global) +def replace(s, old, new, maxsplit=-1): + """replace (str, old, new[, maxsplit]) -> string + + Return a copy of string str with all occurrences of substring + old replaced by new. If the optional argument maxsplit is + given, only the first maxsplit occurrences are replaced. + + """ + return s.replace(old, new, maxsplit) + + +# Try importing optional built-in module "strop" -- if it exists, +# it redefines some string operations that are 100-1000 times faster. +# It also defines values for whitespace, lowercase and uppercase +# that match 's definitions. + +try: + from strop import maketrans, lowercase, uppercase, whitespace + letters = lowercase + uppercase +except ImportError: + pass # Use the original versions diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/symbol.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/symbol.py new file mode 100644 index 00000000..0e8d7a03 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/symbol.py @@ -0,0 +1,95 @@ +#! /usr/bin/env python + +"""Non-terminal symbols of Python grammar (from "graminit.h").""" + +# This file is automatically generated; please don't muck it up! +# +# To update the symbols in this file, 'cd' to the top directory of +# the python source tree after building the interpreter and run: +# +# python Lib/symbol.py + +#--start constants-- +single_input = 256 +file_input = 257 +eval_input = 258 +funcdef = 259 +parameters = 260 +varargslist = 261 +fpdef = 262 +fplist = 263 +stmt = 264 +simple_stmt = 265 +small_stmt = 266 +expr_stmt = 267 +augassign = 268 +print_stmt = 269 +del_stmt = 270 +pass_stmt = 271 +flow_stmt = 272 +break_stmt = 273 +continue_stmt = 274 +return_stmt = 275 +yield_stmt = 276 +raise_stmt = 277 +import_stmt = 278 +import_as_name = 279 +dotted_as_name = 280 +dotted_name = 281 +global_stmt = 282 +exec_stmt = 283 +assert_stmt = 284 +compound_stmt = 285 +if_stmt = 286 +while_stmt = 287 +for_stmt = 288 +try_stmt = 289 +except_clause = 290 +suite = 291 +test = 292 +and_test = 293 +not_test = 294 +comparison = 295 +comp_op = 296 +expr = 297 +xor_expr = 298 +and_expr = 299 +shift_expr = 300 +arith_expr = 301 +term = 302 +factor = 303 +power = 304 +atom = 305 +listmaker = 306 +lambdef = 307 +trailer = 308 +subscriptlist = 309 +subscript = 310 +sliceop = 311 +exprlist = 312 +testlist = 313 +testlist_safe = 314 +dictmaker = 315 +classdef = 316 +arglist = 317 +argument = 318 +list_iter = 319 +list_for = 320 +list_if = 321 +#--end constants-- + +sym_name = {} +for _name, _value in globals().items(): + if type(_value) is type(0): + sym_name[_value] = _name + + +def main(): + import sys + import token + if len(sys.argv) == 1: + sys.argv = sys.argv + ["Include/graminit.h", "Lib/symbol.py"] + token.main() + +if __name__ == "__main__": + main() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/tabnanny.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/tabnanny.py new file mode 100644 index 00000000..a883e591 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/tabnanny.py @@ -0,0 +1,327 @@ +#! /usr/bin/env python + +"""The Tab Nanny despises ambiguous indentation. She knows no mercy. + +tabnanny -- Detection of ambiguous indentation + +For the time being this module is intended to be called as a script. +However it is possible to import it into an IDE and use the function +check() described below. + +Warning: The API provided by this module is likely to change in future +releases; such changes may not be backward compatible. +""" + +# Released to the public domain, by Tim Peters, 15 April 1998. + +# XXX Note: this is now a standard library module. +# XXX The API needs to undergo changes however; the current code is too +# XXX script-like. This will be addressed later. + +__version__ = "6" + +import os +import sys +import getopt +import tokenize +if not hasattr(tokenize, 'NL'): + raise ValueError("tokenize.NL doesn't exist -- tokenize module too old") + +__all__ = ["check", "NannyNag", "process_tokens"] + +verbose = 0 +filename_only = 0 + +def errprint(*args): + sep = "" + for arg in args: + sys.stderr.write(sep + str(arg)) + sep = " " + sys.stderr.write("\n") + +def main(): + global verbose, filename_only + try: + opts, args = getopt.getopt(sys.argv[1:], "qv") + except getopt.error, msg: + errprint(msg) + return + for o, a in opts: + if o == '-q': + filename_only = filename_only + 1 + if o == '-v': + verbose = verbose + 1 + if not args: + errprint("Usage:", sys.argv[0], "[-v] file_or_directory ...") + return + for arg in args: + check(arg) + +class NannyNag: + """ + Raised by tokeneater() if detecting an ambiguous indent. + Captured and handled in check(). + """ + def __init__(self, lineno, msg, line): + self.lineno, self.msg, self.line = lineno, msg, line + def get_lineno(self): + return self.lineno + def get_msg(self): + return self.msg + def get_line(self): + return self.line + +def check(file): + """check(file_or_dir) + + If file_or_dir is a directory and not a symbolic link, then recursively + descend the directory tree named by file_or_dir, checking all .py files + along the way. If file_or_dir is an ordinary Python source file, it is + checked for whitespace related problems. The diagnostic messages are + written to standard output using the print statement. + """ + + if os.path.isdir(file) and not os.path.islink(file): + if verbose: + print "%s: listing directory" % `file` + names = os.listdir(file) + for name in names: + fullname = os.path.join(file, name) + if (os.path.isdir(fullname) and + not os.path.islink(fullname) or + os.path.normcase(name[-3:]) == ".py"): + check(fullname) + return + + try: + f = open(file) + except IOError, msg: + errprint("%s: I/O Error: %s" % (`file`, str(msg))) + return + + if verbose > 1: + print "checking", `file`, "..." + + try: + process_tokens(tokenize.generate_tokens(f.readline)) + + except tokenize.TokenError, msg: + errprint("%s: Token Error: %s" % (`file`, str(msg))) + return + + except NannyNag, nag: + badline = nag.get_lineno() + line = nag.get_line() + if verbose: + print "%s: *** Line %d: trouble in tab city! ***" % ( + `file`, badline) + print "offending line:", `line` + print nag.get_msg() + else: + if ' ' in file: file = '"' + file + '"' + if filename_only: print file + else: print file, badline, `line` + return + + if verbose: + print "%s: Clean bill of health." % `file` + +class Whitespace: + # the characters used for space and tab + S, T = ' \t' + + # members: + # raw + # the original string + # n + # the number of leading whitespace characters in raw + # nt + # the number of tabs in raw[:n] + # norm + # the normal form as a pair (count, trailing), where: + # count + # a tuple such that raw[:n] contains count[i] + # instances of S * i + T + # trailing + # the number of trailing spaces in raw[:n] + # It's A Theorem that m.indent_level(t) == + # n.indent_level(t) for all t >= 1 iff m.norm == n.norm. + # is_simple + # true iff raw[:n] is of the form (T*)(S*) + + def __init__(self, ws): + self.raw = ws + S, T = Whitespace.S, Whitespace.T + count = [] + b = n = nt = 0 + for ch in self.raw: + if ch == S: + n = n + 1 + b = b + 1 + elif ch == T: + n = n + 1 + nt = nt + 1 + if b >= len(count): + count = count + [0] * (b - len(count) + 1) + count[b] = count[b] + 1 + b = 0 + else: + break + self.n = n + self.nt = nt + self.norm = tuple(count), b + self.is_simple = len(count) <= 1 + + # return length of longest contiguous run of spaces (whether or not + # preceding a tab) + def longest_run_of_spaces(self): + count, trailing = self.norm + return max(len(count)-1, trailing) + + def indent_level(self, tabsize): + # count, il = self.norm + # for i in range(len(count)): + # if count[i]: + # il = il + (i/tabsize + 1)*tabsize * count[i] + # return il + + # quicker: + # il = trailing + sum (i/ts + 1)*ts*count[i] = + # trailing + ts * sum (i/ts + 1)*count[i] = + # trailing + ts * sum i/ts*count[i] + count[i] = + # trailing + ts * [(sum i/ts*count[i]) + (sum count[i])] = + # trailing + ts * [(sum i/ts*count[i]) + num_tabs] + # and note that i/ts*count[i] is 0 when i < ts + + count, trailing = self.norm + il = 0 + for i in range(tabsize, len(count)): + il = il + i/tabsize * count[i] + return trailing + tabsize * (il + self.nt) + + # return true iff self.indent_level(t) == other.indent_level(t) + # for all t >= 1 + def equal(self, other): + return self.norm == other.norm + + # return a list of tuples (ts, i1, i2) such that + # i1 == self.indent_level(ts) != other.indent_level(ts) == i2. + # Intended to be used after not self.equal(other) is known, in which + # case it will return at least one witnessing tab size. + def not_equal_witness(self, other): + n = max(self.longest_run_of_spaces(), + other.longest_run_of_spaces()) + 1 + a = [] + for ts in range(1, n+1): + if self.indent_level(ts) != other.indent_level(ts): + a.append( (ts, + self.indent_level(ts), + other.indent_level(ts)) ) + return a + + # Return true iff self.indent_level(t) < other.indent_level(t) + # for all t >= 1. + # The algorithm is due to Vincent Broman. + # Easy to prove it's correct. + # XXXpost that. + # Trivial to prove n is sharp (consider T vs ST). + # Unknown whether there's a faster general way. I suspected so at + # first, but no longer. + # For the special (but common!) case where M and N are both of the + # form (T*)(S*), M.less(N) iff M.len() < N.len() and + # M.num_tabs() <= N.num_tabs(). Proof is easy but kinda long-winded. + # XXXwrite that up. + # Note that M is of the form (T*)(S*) iff len(M.norm[0]) <= 1. + def less(self, other): + if self.n >= other.n: + return 0 + if self.is_simple and other.is_simple: + return self.nt <= other.nt + n = max(self.longest_run_of_spaces(), + other.longest_run_of_spaces()) + 1 + # the self.n >= other.n test already did it for ts=1 + for ts in range(2, n+1): + if self.indent_level(ts) >= other.indent_level(ts): + return 0 + return 1 + + # return a list of tuples (ts, i1, i2) such that + # i1 == self.indent_level(ts) >= other.indent_level(ts) == i2. + # Intended to be used after not self.less(other) is known, in which + # case it will return at least one witnessing tab size. + def not_less_witness(self, other): + n = max(self.longest_run_of_spaces(), + other.longest_run_of_spaces()) + 1 + a = [] + for ts in range(1, n+1): + if self.indent_level(ts) >= other.indent_level(ts): + a.append( (ts, + self.indent_level(ts), + other.indent_level(ts)) ) + return a + +def format_witnesses(w): + import string + firsts = map(lambda tup: str(tup[0]), w) + prefix = "at tab size" + if len(w) > 1: + prefix = prefix + "s" + return prefix + " " + string.join(firsts, ', ') + +def process_tokens(tokens): + INDENT = tokenize.INDENT + DEDENT = tokenize.DEDENT + NEWLINE = tokenize.NEWLINE + JUNK = tokenize.COMMENT, tokenize.NL + indents = [Whitespace("")] + check_equal = 0 + + for (type, token, start, end, line) in tokens: + if type == NEWLINE: + # a program statement, or ENDMARKER, will eventually follow, + # after some (possibly empty) run of tokens of the form + # (NL | COMMENT)* (INDENT | DEDENT+)? + # If an INDENT appears, setting check_equal is wrong, and will + # be undone when we see the INDENT. + check_equal = 1 + + elif type == INDENT: + check_equal = 0 + thisguy = Whitespace(token) + if not indents[-1].less(thisguy): + witness = indents[-1].not_less_witness(thisguy) + msg = "indent not greater e.g. " + format_witnesses(witness) + raise NannyNag(start[0], msg, line) + indents.append(thisguy) + + elif type == DEDENT: + # there's nothing we need to check here! what's important is + # that when the run of DEDENTs ends, the indentation of the + # program statement (or ENDMARKER) that triggered the run is + # equal to what's left at the top of the indents stack + + # Ouch! This assert triggers if the last line of the source + # is indented *and* lacks a newline -- then DEDENTs pop out + # of thin air. + # assert check_equal # else no earlier NEWLINE, or an earlier INDENT + check_equal = 1 + + del indents[-1] + + elif check_equal and type not in JUNK: + # this is the first "real token" following a NEWLINE, so it + # must be the first token of the next program statement, or an + # ENDMARKER; the "line" argument exposes the leading whitespace + # for this statement; in the case of ENDMARKER, line is an empty + # string, so will properly match the empty string with which the + # "indents" stack was seeded + check_equal = 0 + thisguy = Whitespace(line) + if not indents[-1].equal(thisguy): + witness = indents[-1].not_equal_witness(thisguy) + msg = "indent not equal e.g. " + format_witnesses(witness) + raise NannyNag(start[0], msg, line) + + +if __name__ == '__main__': + main() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/token.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/token.py new file mode 100644 index 00000000..f1e94b7f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/token.py @@ -0,0 +1,140 @@ +#! /usr/bin/env python + +"""Token constants (from "token.h").""" + +# This file is automatically generated; please don't muck it up! +# +# To update the symbols in this file, 'cd' to the top directory of +# the python source tree after building the interpreter and run: +# +# python Lib/token.py + +#--start constants-- +ENDMARKER = 0 +NAME = 1 +NUMBER = 2 +STRING = 3 +NEWLINE = 4 +INDENT = 5 +DEDENT = 6 +LPAR = 7 +RPAR = 8 +LSQB = 9 +RSQB = 10 +COLON = 11 +COMMA = 12 +SEMI = 13 +PLUS = 14 +MINUS = 15 +STAR = 16 +SLASH = 17 +VBAR = 18 +AMPER = 19 +LESS = 20 +GREATER = 21 +EQUAL = 22 +DOT = 23 +PERCENT = 24 +BACKQUOTE = 25 +LBRACE = 26 +RBRACE = 27 +EQEQUAL = 28 +NOTEQUAL = 29 +LESSEQUAL = 30 +GREATEREQUAL = 31 +TILDE = 32 +CIRCUMFLEX = 33 +LEFTSHIFT = 34 +RIGHTSHIFT = 35 +DOUBLESTAR = 36 +PLUSEQUAL = 37 +MINEQUAL = 38 +STAREQUAL = 39 +SLASHEQUAL = 40 +PERCENTEQUAL = 41 +AMPEREQUAL = 42 +VBAREQUAL = 43 +CIRCUMFLEXEQUAL = 44 +LEFTSHIFTEQUAL = 45 +RIGHTSHIFTEQUAL = 46 +DOUBLESTAREQUAL = 47 +DOUBLESLASH = 48 +DOUBLESLASHEQUAL = 49 +OP = 50 +ERRORTOKEN = 51 +N_TOKENS = 52 +NT_OFFSET = 256 +#--end constants-- + +tok_name = {} +for _name, _value in globals().items(): + if type(_value) is type(0): + tok_name[_value] = _name + + +def ISTERMINAL(x): + return x < NT_OFFSET + +def ISNONTERMINAL(x): + return x >= NT_OFFSET + +def ISEOF(x): + return x == ENDMARKER + + +def main(): + import re + import sys + args = sys.argv[1:] + inFileName = args and args[0] or "Include/token.h" + outFileName = "Lib/token.py" + if len(args) > 1: + outFileName = args[1] + try: + fp = open(inFileName) + except IOError, err: + sys.stdout.write("I/O error: %s\n" % str(err)) + sys.exit(1) + lines = fp.read().split("\n") + fp.close() + prog = re.compile( + "#define[ \t][ \t]*([A-Z][A-Z_]*)[ \t][ \t]*([0-9][0-9]*)", + re.IGNORECASE) + tokens = {} + for line in lines: + match = prog.match(line) + if match: + name, val = match.group(1, 2) + val = int(val) + tokens[val] = name # reverse so we can sort them... + keys = tokens.keys() + keys.sort() + # load the output skeleton from the target: + try: + fp = open(outFileName) + except IOError, err: + sys.stderr.write("I/O error: %s\n" % str(err)) + sys.exit(2) + format = fp.read().split("\n") + fp.close() + try: + start = format.index("#--start constants--") + 1 + end = format.index("#--end constants--") + except ValueError: + sys.stderr.write("target does not contain format markers") + sys.exit(3) + lines = [] + for val in keys: + lines.append("%s = %d" % (tokens[val], val)) + format[start:end] = lines + try: + fp = open(outFileName, 'w') + except IOError, err: + sys.stderr.write("I/O error: %s\n" % str(err)) + sys.exit(4) + fp.write("\n".join(format)) + fp.close() + + +if __name__ == "__main__": + main() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/tokenize.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/tokenize.py new file mode 100644 index 00000000..abeffd5d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/tokenize.py @@ -0,0 +1,286 @@ +"""Tokenization help for Python programs. + +generate_tokens(readline) is a generator that breaks a stream of +text into Python tokens. It accepts a readline-like method which is called +repeatedly to get the next line of input (or "" for EOF). It generates +5-tuples with these members: + + the token type (see token.py) + the token (a string) + the starting (row, column) indices of the token (a 2-tuple of ints) + the ending (row, column) indices of the token (a 2-tuple of ints) + the original line (string) + +It is designed to match the working of the Python tokenizer exactly, except +that it produces COMMENT tokens for comments and gives type OP for all +operators + +Older entry points + tokenize_loop(readline, tokeneater) + tokenize(readline, tokeneater=printtoken) +are the same, except instead of generating tokens, tokeneater is a callback +function to which the 5 fields described above are passed as 5 arguments, +each time a new token is found.""" + +from __future__ import generators + +__author__ = 'Ka-Ping Yee ' +__credits__ = \ + 'GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, Skip Montanaro' + +import string, re +from token import * + +import token +__all__ = [x for x in dir(token) if x[0] != '_'] + ["COMMENT", "tokenize", "NL"] +del token + +COMMENT = N_TOKENS +tok_name[COMMENT] = 'COMMENT' +NL = N_TOKENS + 1 +tok_name[NL] = 'NL' +N_TOKENS += 2 + +def group(*choices): return '(' + '|'.join(choices) + ')' +def any(*choices): return apply(group, choices) + '*' +def maybe(*choices): return apply(group, choices) + '?' + +Whitespace = r'[ \f\t]*' +Comment = r'#[^\r\n]*' +Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment) +Name = r'[a-zA-Z_]\w*' + +Hexnumber = r'0[xX][\da-fA-F]*[lL]?' +Octnumber = r'0[0-7]*[lL]?' +Decnumber = r'[1-9]\d*[lL]?' +Intnumber = group(Hexnumber, Octnumber, Decnumber) +Exponent = r'[eE][-+]?\d+' +Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent) +Expfloat = r'\d+' + Exponent +Floatnumber = group(Pointfloat, Expfloat) +Imagnumber = group(r'\d+[jJ]', Floatnumber + r'[jJ]') +Number = group(Imagnumber, Floatnumber, Intnumber) + +# Tail end of ' string. +Single = r"[^'\\]*(?:\\.[^'\\]*)*'" +# Tail end of " string. +Double = r'[^"\\]*(?:\\.[^"\\]*)*"' +# Tail end of ''' string. +Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''" +# Tail end of """ string. +Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""' +Triple = group("[uU]?[rR]?'''", '[uU]?[rR]?"""') +# Single-line ' or " string. +String = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*'", + r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*"') + +# Because of leftmost-then-longest match semantics, be sure to put the +# longest operators first (e.g., if = came before ==, == would get +# recognized as two instances of =). +Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"<>", r"!=", + r"//=?", + r"[+\-*/%&|^=<>]=?", + r"~") + +Bracket = '[][(){}]' +Special = group(r'\r?\n', r'[:;.,`]') +Funny = group(Operator, Bracket, Special) + +PlainToken = group(Number, Funny, String, Name) +Token = Ignore + PlainToken + +# First (or only) line of ' or " string. +ContStr = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" + + group("'", r'\\\r?\n'), + r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*' + + group('"', r'\\\r?\n')) +PseudoExtras = group(r'\\\r?\n', Comment, Triple) +PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name) + +tokenprog, pseudoprog, single3prog, double3prog = map( + re.compile, (Token, PseudoToken, Single3, Double3)) +endprogs = {"'": re.compile(Single), '"': re.compile(Double), + "'''": single3prog, '"""': double3prog, + "r'''": single3prog, 'r"""': double3prog, + "u'''": single3prog, 'u"""': double3prog, + "ur'''": single3prog, 'ur"""': double3prog, + "R'''": single3prog, 'R"""': double3prog, + "U'''": single3prog, 'U"""': double3prog, + "uR'''": single3prog, 'uR"""': double3prog, + "Ur'''": single3prog, 'Ur"""': double3prog, + "UR'''": single3prog, 'UR"""': double3prog, + 'r': None, 'R': None, 'u': None, 'U': None} + +tabsize = 8 + +class TokenError(Exception): pass + +class StopTokenizing(Exception): pass + +def printtoken(type, token, (srow, scol), (erow, ecol), line): # for testing + print "%d,%d-%d,%d:\t%s\t%s" % \ + (srow, scol, erow, ecol, tok_name[type], repr(token)) + +def tokenize(readline, tokeneater=printtoken): + """ + The tokenize() function accepts two parameters: one representing the + input stream, and one providing an output mechanism for tokenize(). + + The first parameter, readline, must be a callable object which provides + the same interface as the readline() method of built-in file objects. + Each call to the function should return one line of input as a string. + + The second parameter, tokeneater, must also be a callable object. It is + called once for each token, with five arguments, corresponding to the + tuples generated by generate_tokens(). + """ + try: + tokenize_loop(readline, tokeneater) + except StopTokenizing: + pass + +# backwards compatible interface +def tokenize_loop(readline, tokeneater): + for token_info in generate_tokens(readline): + apply(tokeneater, token_info) + +def generate_tokens(readline): + """ + The generate_tokens() generator requires one argment, readline, which + must be a callable object which provides the same interface as the + readline() method of built-in file objects. Each call to the function + should return one line of input as a string. + + The generator produces 5-tuples with these members: the token type; the + token string; a 2-tuple (srow, scol) of ints specifying the row and + column where the token begins in the source; a 2-tuple (erow, ecol) of + ints specifying the row and column where the token ends in the source; + and the line on which the token was found. The line passed is the + logical line; continuation lines are included. + """ + lnum = parenlev = continued = 0 + namechars, numchars = string.ascii_letters + '_', '0123456789' + contstr, needcont = '', 0 + contline = None + indents = [0] + + while 1: # loop over lines in stream + line = readline() + lnum = lnum + 1 + pos, max = 0, len(line) + + if contstr: # continued string + if not line: + raise TokenError, ("EOF in multi-line string", strstart) + endmatch = endprog.match(line) + if endmatch: + pos = end = endmatch.end(0) + yield (STRING, contstr + line[:end], + strstart, (lnum, end), contline + line) + contstr, needcont = '', 0 + contline = None + elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n': + yield (ERRORTOKEN, contstr + line, + strstart, (lnum, len(line)), contline) + contstr = '' + contline = None + continue + else: + contstr = contstr + line + contline = contline + line + continue + + elif parenlev == 0 and not continued: # new statement + if not line: break + column = 0 + while pos < max: # measure leading whitespace + if line[pos] == ' ': column = column + 1 + elif line[pos] == '\t': column = (column/tabsize + 1)*tabsize + elif line[pos] == '\f': column = 0 + else: break + pos = pos + 1 + if pos == max: break + + if line[pos] in '#\r\n': # skip comments or blank lines + yield ((NL, COMMENT)[line[pos] == '#'], line[pos:], + (lnum, pos), (lnum, len(line)), line) + continue + + if column > indents[-1]: # count indents or dedents + indents.append(column) + yield (INDENT, line[:pos], (lnum, 0), (lnum, pos), line) + while column < indents[-1]: + indents = indents[:-1] + yield (DEDENT, '', (lnum, pos), (lnum, pos), line) + + else: # continued statement + if not line: + raise TokenError, ("EOF in multi-line statement", (lnum, 0)) + continued = 0 + + while pos < max: + pseudomatch = pseudoprog.match(line, pos) + if pseudomatch: # scan for tokens + start, end = pseudomatch.span(1) + spos, epos, pos = (lnum, start), (lnum, end), end + token, initial = line[start:end], line[start] + + if initial in numchars or \ + (initial == '.' and token != '.'): # ordinary number + yield (NUMBER, token, spos, epos, line) + elif initial in '\r\n': + yield (parenlev > 0 and NL or NEWLINE, + token, spos, epos, line) + elif initial == '#': + yield (COMMENT, token, spos, epos, line) + elif token in ("'''", '"""', # triple-quoted + "r'''", 'r"""', "R'''", 'R"""', + "u'''", 'u"""', "U'''", 'U"""', + "ur'''", 'ur"""', "Ur'''", 'Ur"""', + "uR'''", 'uR"""', "UR'''", 'UR"""'): + endprog = endprogs[token] + endmatch = endprog.match(line, pos) + if endmatch: # all on one line + pos = endmatch.end(0) + token = line[start:pos] + yield (STRING, token, spos, (lnum, pos), line) + else: + strstart = (lnum, start) # multiple lines + contstr = line[start:] + contline = line + break + elif initial in ("'", '"') or \ + token[:2] in ("r'", 'r"', "R'", 'R"', + "u'", 'u"', "U'", 'U"') or \ + token[:3] in ("ur'", 'ur"', "Ur'", 'Ur"', + "uR'", 'uR"', "UR'", 'UR"' ): + if token[-1] == '\n': # continued string + strstart = (lnum, start) + endprog = (endprogs[initial] or endprogs[token[1]] or + endprogs[token[2]]) + contstr, needcont = line[start:], 1 + contline = line + break + else: # ordinary string + yield (STRING, token, spos, epos, line) + elif initial in namechars: # ordinary name + yield (NAME, token, spos, epos, line) + elif initial == '\\': # continued stmt + continued = 1 + else: + if initial in '([{': parenlev = parenlev + 1 + elif initial in ')]}': parenlev = parenlev - 1 + yield (OP, token, spos, epos, line) + else: + yield (ERRORTOKEN, line[pos], + (lnum, pos), (lnum, pos+1), line) + pos = pos + 1 + + for indent in indents[1:]: # pop remaining indent levels + yield (DEDENT, '', (lnum, 0), (lnum, 0), '') + yield (ENDMARKER, '', (lnum, 0), (lnum, 0), '') + +if __name__ == '__main__': # testing + import sys + if len(sys.argv) > 1: tokenize(open(sys.argv[1]).readline) + else: tokenize(sys.stdin.readline) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/traceback.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/traceback.py new file mode 100644 index 00000000..993252aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/traceback.py @@ -0,0 +1,301 @@ +"""Extract, format and print information about Python stack traces.""" + +import linecache +import sys +import types + +__all__ = ['extract_stack', 'extract_tb', 'format_exception', + 'format_exception_only', 'format_list', 'format_stack', + 'format_tb', 'print_exc', 'print_exception', 'print_last', + 'print_stack', 'print_tb', 'tb_lineno'] + +def _print(file, str='', terminator='\n'): + file.write(str+terminator) + + +def print_list(extracted_list, file=None): + """Print the list of tuples as returned by extract_tb() or + extract_stack() as a formatted stack trace to the given file.""" + if not file: + file = sys.stderr + for filename, lineno, name, line in extracted_list: + _print(file, + ' File "%s", line %d, in %s' % (filename,lineno,name)) + if line: + _print(file, ' %s' % line.strip()) + +def format_list(extracted_list): + """Format a list of traceback entry tuples for printing. + + Given a list of tuples as returned by extract_tb() or + extract_stack(), return a list of strings ready for printing. + Each string in the resulting list corresponds to the item with the + same index in the argument list. Each string ends in a newline; + the strings may contain internal newlines as well, for those items + whose source text line is not None. + """ + list = [] + for filename, lineno, name, line in extracted_list: + item = ' File "%s", line %d, in %s\n' % (filename,lineno,name) + if line: + item = item + ' %s\n' % line.strip() + list.append(item) + return list + + +def print_tb(tb, limit=None, file=None): + """Print up to 'limit' stack trace entries from the traceback 'tb'. + + If 'limit' is omitted or None, all entries are printed. If 'file' + is omitted or None, the output goes to sys.stderr; otherwise + 'file' should be an open file or file-like object with a write() + method. + """ + if not file: + file = sys.stderr + if limit is None: + if hasattr(sys, 'tracebacklimit'): + limit = sys.tracebacklimit + n = 0 + while tb is not None and (limit is None or n < limit): + f = tb.tb_frame + lineno = tb_lineno(tb) + co = f.f_code + filename = co.co_filename + name = co.co_name + _print(file, + ' File "%s", line %d, in %s' % (filename,lineno,name)) + line = linecache.getline(filename, lineno) + if line: _print(file, ' ' + line.strip()) + tb = tb.tb_next + n = n+1 + +def format_tb(tb, limit = None): + """A shorthand for 'format_list(extract_stack(f, limit)).""" + return format_list(extract_tb(tb, limit)) + +def extract_tb(tb, limit = None): + """Return list of up to limit pre-processed entries from traceback. + + This is useful for alternate formatting of stack traces. If + 'limit' is omitted or None, all entries are extracted. A + pre-processed stack trace entry is a quadruple (filename, line + number, function name, text) representing the information that is + usually printed for a stack trace. The text is a string with + leading and trailing whitespace stripped; if the source is not + available it is None. + """ + if limit is None: + if hasattr(sys, 'tracebacklimit'): + limit = sys.tracebacklimit + list = [] + n = 0 + while tb is not None and (limit is None or n < limit): + f = tb.tb_frame + lineno = tb_lineno(tb) + co = f.f_code + filename = co.co_filename + name = co.co_name + line = linecache.getline(filename, lineno) + if line: line = line.strip() + else: line = None + list.append((filename, lineno, name, line)) + tb = tb.tb_next + n = n+1 + return list + + +def print_exception(etype, value, tb, limit=None, file=None): + """Print exception up to 'limit' stack trace entries from 'tb' to 'file'. + + This differs from print_tb() in the following ways: (1) if + traceback is not None, it prints a header "Traceback (most recent + call last):"; (2) it prints the exception type and value after the + stack trace; (3) if type is SyntaxError and value has the + appropriate format, it prints the line where the syntax error + occurred with a caret on the next line indicating the approximate + position of the error. + """ + if not file: + file = sys.stderr + if tb: + _print(file, 'Traceback (most recent call last):') + print_tb(tb, limit, file) + lines = format_exception_only(etype, value) + for line in lines[:-1]: + _print(file, line, ' ') + _print(file, lines[-1], '') + +def format_exception(etype, value, tb, limit = None): + """Format a stack trace and the exception information. + + The arguments have the same meaning as the corresponding arguments + to print_exception(). The return value is a list of strings, each + ending in a newline and some containing internal newlines. When + these lines are concatenated and printed, exactly the same text is + printed as does print_exception(). + """ + if tb: + list = ['Traceback (most recent call last):\n'] + list = list + format_tb(tb, limit) + else: + list = [] + list = list + format_exception_only(etype, value) + return list + +def format_exception_only(etype, value): + """Format the exception part of a traceback. + + The arguments are the exception type and value such as given by + sys.last_type and sys.last_value. The return value is a list of + strings, each ending in a newline. Normally, the list contains a + single string; however, for SyntaxError exceptions, it contains + several lines that (when printed) display detailed information + about where the syntax error occurred. The message indicating + which exception occurred is the always last string in the list. + """ + list = [] + if type(etype) == types.ClassType: + stype = etype.__name__ + else: + stype = etype + if value is None: + list.append(str(stype) + '\n') + else: + if etype is SyntaxError: + try: + msg, (filename, lineno, offset, line) = value + except: + pass + else: + if not filename: filename = "" + list.append(' File "%s", line %d\n' % + (filename, lineno)) + if line is not None: + i = 0 + while i < len(line) and line[i].isspace(): + i = i+1 + list.append(' %s\n' % line.strip()) + if offset is not None: + s = ' ' + for c in line[i:offset-1]: + if c.isspace(): + s = s + c + else: + s = s + ' ' + list.append('%s^\n' % s) + value = msg + s = _some_str(value) + if s: + list.append('%s: %s\n' % (str(stype), s)) + else: + list.append('%s\n' % str(stype)) + return list + +def _some_str(value): + try: + return str(value) + except: + return '' % type(value).__name__ + + +def print_exc(limit=None, file=None): + """Shorthand for 'print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file)'. + (In fact, it uses sys.exc_info() to retrieve the same information + in a thread-safe way.)""" + if not file: + file = sys.stderr + try: + etype, value, tb = sys.exc_info() + print_exception(etype, value, tb, limit, file) + finally: + etype = value = tb = None + +def print_last(limit=None, file=None): + """This is a shorthand for 'print_exception(sys.last_type, + sys.last_value, sys.last_traceback, limit, file)'.""" + if not file: + file = sys.stderr + print_exception(sys.last_type, sys.last_value, sys.last_traceback, + limit, file) + + +def print_stack(f=None, limit=None, file=None): + """Print a stack trace from its invocation point. + + The optional 'f' argument can be used to specify an alternate + stack frame at which to start. The optional 'limit' and 'file' + arguments have the same meaning as for print_exception(). + """ + if f is None: + try: + raise ZeroDivisionError + except ZeroDivisionError: + f = sys.exc_info()[2].tb_frame.f_back + print_list(extract_stack(f, limit), file) + +def format_stack(f=None, limit=None): + """Shorthand for 'format_list(extract_stack(f, limit))'.""" + if f is None: + try: + raise ZeroDivisionError + except ZeroDivisionError: + f = sys.exc_info()[2].tb_frame.f_back + return format_list(extract_stack(f, limit)) + +def extract_stack(f=None, limit = None): + """Extract the raw traceback from the current stack frame. + + The return value has the same format as for extract_tb(). The + optional 'f' and 'limit' arguments have the same meaning as for + print_stack(). Each item in the list is a quadruple (filename, + line number, function name, text), and the entries are in order + from oldest to newest stack frame. + """ + if f is None: + try: + raise ZeroDivisionError + except ZeroDivisionError: + f = sys.exc_info()[2].tb_frame.f_back + if limit is None: + if hasattr(sys, 'tracebacklimit'): + limit = sys.tracebacklimit + list = [] + n = 0 + while f is not None and (limit is None or n < limit): + lineno = f.f_lineno # XXX Too bad if -O is used + co = f.f_code + filename = co.co_filename + name = co.co_name + line = linecache.getline(filename, lineno) + if line: line = line.strip() + else: line = None + list.append((filename, lineno, name, line)) + f = f.f_back + n = n+1 + list.reverse() + return list + +def tb_lineno(tb): + """Calculate correct line number of traceback given in tb. + + Even works with -O on. + """ + # Coded by Marc-Andre Lemburg from the example of PyCode_Addr2Line() + # in compile.c. + # Revised version by Jim Hugunin to work with JPython too. + + c = tb.tb_frame.f_code + if not hasattr(c, 'co_lnotab'): + return tb.tb_lineno + + tab = c.co_lnotab + line = c.co_firstlineno + stopat = tb.tb_lasti + addr = 0 + for i in range(0, len(tab), 2): + addr = addr + ord(tab[i]) + if addr > stopat: + break + line = line + ord(tab[i+1]) + return line diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/types.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/types.py new file mode 100644 index 00000000..e151e1b6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/types.py @@ -0,0 +1,86 @@ +"""Define names for all type symbols known in the standard interpreter. + +Types that are part of optional modules (e.g. array) are not listed. +""" +from __future__ import generators + +import sys + +# Iterators in Python aren't a matter of type but of protocol. A large +# and changing number of builtin types implement *some* flavor of +# iterator. Don't check the type! Use hasattr to check for both +# "__iter__" and "next" attributes instead. + +NoneType = type(None) +TypeType = type +ObjectType = object + +IntType = int +LongType = long +FloatType = float +try: + ComplexType = complex +except NameError: + pass + +StringType = str +try: + UnicodeType = unicode + StringTypes = (StringType, UnicodeType) +except NameError: + StringTypes = (StringType,) + +BufferType = type(buffer('')) + +TupleType = tuple +ListType = list +DictType = DictionaryType = dict + +def _f(): pass +FunctionType = type(_f) +LambdaType = type(lambda: None) # Same as FunctionType +try: + CodeType = type(_f.func_code) +except RuntimeError: + # Execution in restricted environment + pass + +def g(): + yield 1 +GeneratorType = type(g()) +del g + +class _C: + def _m(self): pass +ClassType = type(_C) +UnboundMethodType = type(_C._m) # Same as MethodType +_x = _C() +InstanceType = type(_x) +MethodType = type(_x._m) + +BuiltinFunctionType = type(len) +BuiltinMethodType = type([].append) # Same as BuiltinFunctionType + +ModuleType = type(sys) +FileType = file +XRangeType = type(xrange(0)) + +try: + raise TypeError +except TypeError: + try: + tb = sys.exc_info()[2] + TracebackType = type(tb) + FrameType = type(tb.tb_frame) + except AttributeError: + # In the restricted environment, exc_info returns (None, None, + # None) Then, tb.tb_frame gives an attribute error + pass + tb = None; del tb + +SliceType = type(slice(0)) +EllipsisType = type(Ellipsis) + +DictProxyType = type(TypeType.__dict__) + +del sys, _f, _C, _x, generators # Not for export diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/verify.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/verify.py new file mode 100644 index 00000000..b4890bd9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/verify.py @@ -0,0 +1,182 @@ +# +# (C) Copyright 2000 by hartmut Goebel +# +# byte-code verifier for decompyle +# + +import types +import decompyle + +#--- exceptions --- + +class VerifyCmpError(Exception): + pass + +class CmpErrorConsts(VerifyCmpError): + """Exception to be raised when consts differ.""" + def __init__(self, name, index): + self.name = name + self.index = index + + def __str__(self): + return 'Compare Error within Consts of %s at index %i' % \ + (repr(self.name), self.index) + +class CmpErrorConstsLen(VerifyCmpError): + """Exception to be raised when length of co_consts differs.""" + def __init__(self, name, consts1, consts2): + self.name = name + self.consts = (consts1, consts2) + + def __str__(self): + return 'Consts length differs in %s:\n\n%i:\t%s\n\n%i:\t%s\n\n' % \ + (repr(self.name), + len(self.consts[0]), `self.consts[0]`, + len(self.consts[1]), `self.consts[1]`) + +class CmpErrorCode(VerifyCmpError): + """Exception to be raised when code differs.""" + def __init__(self, name, index, token1, token2): + self.name = name + self.index = index + self.token1 = token1 + self.token2 = token2 + + def __str__(self): + return 'Code differs in %s at offset %i [%s] != [%s]' % \ + (repr(self.name), self.index, + repr(self.token1), repr(self.token2)) #\ + # + ('%s %s') % (self.token1.pattr, self.token2.pattr) + +class CmpErrorCodeLen(VerifyCmpError): + """Exception to be raised when code length differs.""" + def __init__(self, name, tokens1, tokens2): + self.name = name + self.tokens = [tokens1, tokens2] + + def __str__(self): + return reduce(lambda s,t: "%s%-37s\t%-37s\n" % (s, t[0], t[1]), + map(lambda a,b: (a,b), + self.tokens[0], + self.tokens[1]), + 'Code len differs in %s\n' % str(self.name)) + +class CmpErrorMember(VerifyCmpError): + """Exception to be raised when other members differ.""" + def __init__(self, name, member, data1, data2): + self.name = name + self.member = member + self.data = (data1, data2) + + def __str__(self): + return 'Member %s differs in %s:\n\t%s\n\t%s\n' % \ + (repr(self.member), repr(self.name), + repr(self.data[0]), repr(self.data[1])) + +#--- compare --- + +# these members are ignored +__IGNORE_CODE_MEMBERS__ = ['co_filename', 'co_firstlineno', 'co_lnotab'] + +def cmp_code_objects(code_obj1, code_obj2, name=''): + """ + Compare two code-objects. + + This is the main part of this module. + """ + assert type(code_obj1) == types.CodeType + assert type(code_obj2) == types.CodeType + assert dir(code_obj1) == code_obj1.__members__ + assert dir(code_obj2) == code_obj2.__members__ + assert code_obj1.__members__ == code_obj2.__members__ + if name == '__main__': + name = code_obj1.co_name + else: + name = '%s.%s' % (name, code_obj1.co_name) + if name == '.?': name = '__main__' + + members = code_obj1.__members__; members.sort(); #members.reverse() + tokens1 = None + for member in members: + if member in __IGNORE_CODE_MEMBERS__: + pass + elif member == 'co_code': + # use changed Token class + __Token = decompyle.Token + decompyle.Token = Token + # tokenize both code-objects + tokens1, customize = decompyle._tokenize(None, code_obj1) + tokens2, customize = decompyle._tokenize(None, code_obj2) + del customize + decompyle.Token = __Token # restore Token class + + # compare length + if len(tokens1) != len(tokens2): + raise CmpErrorCodeLen(name, tokens1, tokens2) + # compare contents + #print len(tokens1), type(tokens1), type(tokens2) + for i in xrange(len(tokens1)): + if tokens1[i] != tokens2[i]: + #print '-->', i, type(tokens1[i]), type(tokens2[i]) + raise CmpErrorCode(name, i, tokens1[i], + tokens2[i]) + elif member == 'co_consts': + # compare length + if len(code_obj1.co_consts) != len(code_obj2.co_consts): + raise CmpErrorConstsLen(name, code_obj1.co_consts ,code_obj2.co_consts) + # compare contents + for idx in xrange(len(code_obj1.co_consts)): + const1 = code_obj1.co_consts[idx] + const2 = code_obj2.co_consts[idx] + # same type? + if type(const1) != type(const2): + raise CmpErrorContType(name, idx) + if type(const1) == types.CodeType: + # code object -> recursive compare + cmp_code_objects(const1, const2, + name) + elif cmp(const1, const2) != 0: + # content differs + raise CmpErrorConsts(name, idx) + else: + # all other members must be equal + if eval('code_obj1.%s != code_obj2.%s' % (member, member)): + data1 = eval('code_obj1.%s' % member) + data2 = eval('code_obj2.%s' % member) + raise CmpErrorMember(name, member, data1,data2) + + +class Token(decompyle.Token): + """Token class with changed semantics for 'cmp()'.""" + + def __cmp__(self, o): + if self.type in decompyle._JUMP_OPS_: + # ignore offset + return cmp(self.type, o.type) + else: + return cmp(self.type, o.type) \ + or cmp(self.pattr, o.pattr) + + def __repr__(self): + return '%s %s (%s)' % (str(self.type), str(self.attr), + str(self.pattr)) + + +def compare_code_with_srcfile(pyc_filename, src_filename): + """Compare a .pyc with a source code file.""" + code_obj1 = decompyle._load_module(pyc_filename) + code_obj2 = decompyle._load_file(src_filename) + cmp_code_objects(code_obj1, code_obj2) + +def compare_files(pyc_filename1, pyc_filename2): + """Compare two .pyc files.""" + code_obj1 = decompyle._load_module(pyc_filename1) + code_obj2 = decompyle._load_module(pyc_filename2) + cmp_code_objects(code_obj1, code_obj2) + +if __name__ == '__main__': + t1 = Token('LOAD_CONST', None, 'code_object _expandLang', 52) + t2 = Token('LOAD_CONST', -421, 'code_object _expandLang', 55) + print `t1` + print `t2` + print cmp(t1, t2), cmp(t1.type, t2.type), cmp(t1.attr, t2.attr) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/warnings.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/warnings.py new file mode 100644 index 00000000..658e1a59 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/warnings.py @@ -0,0 +1,258 @@ +"""Python part of the warnings subsystem.""" + +import sys, re, types + +__all__ = ["warn", "showwarning", "formatwarning", "filterwarnings", + "resetwarnings"] + +defaultaction = "default" +filters = [] +onceregistry = {} + +def warn(message, category=None, stacklevel=1): + """Issue a warning, or maybe ignore it or raise an exception.""" + # Check category argument + if category is None: + category = UserWarning + assert issubclass(category, Warning) + # Get context information + try: + caller = sys._getframe(stacklevel) + except ValueError: + globals = sys.__dict__ + lineno = 1 + else: + globals = caller.f_globals + lineno = caller.f_lineno + if globals.has_key('__name__'): + module = globals['__name__'] + else: + module = "" + filename = globals.get('__file__') + if filename: + fnl = filename.lower() + if fnl.endswith(".pyc") or fnl.endswith(".pyo"): + filename = filename[:-1] + else: + if module == "__main__": + filename = sys.argv[0] + if not filename: + filename = module + registry = globals.setdefault("__warningregistry__", {}) + warn_explicit(message, category, filename, lineno, module, registry) + +def warn_explicit(message, category, filename, lineno, + module=None, registry=None): + if module is None: + module = filename + if module[-3:].lower() == ".py": + module = module[:-3] # XXX What about leading pathname? + if registry is None: + registry = {} + key = (message, category, lineno) + # Quick test for common case + if registry.get(key): + return + # Search the filters + for item in filters: + action, msg, cat, mod, ln = item + if (msg.match(message) and + issubclass(category, cat) and + mod.match(module) and + (ln == 0 or lineno == ln)): + break + else: + action = defaultaction + # Early exit actions + if action == "ignore": + registry[key] = 1 + return + if action == "error": + raise category(message) + # Other actions + if action == "once": + registry[key] = 1 + oncekey = (message, category) + if onceregistry.get(oncekey): + return + onceregistry[oncekey] = 1 + elif action == "always": + pass + elif action == "module": + registry[key] = 1 + altkey = (message, category, 0) + if registry.get(altkey): + return + registry[altkey] = 1 + elif action == "default": + registry[key] = 1 + else: + # Unrecognized actions are errors + raise RuntimeError( + "Unrecognized action (%s) in warnings.filters:\n %s" % + (`action`, str(item))) + # Print message and context + showwarning(message, category, filename, lineno) + +def showwarning(message, category, filename, lineno, file=None): + """Hook to write a warning to a file; replace if you like.""" + if file is None: + file = sys.stderr + try: + file.write(formatwarning(message, category, filename, lineno)) + except IOError: + pass # the file (probably stderr) is invalid - this warning gets lost. + +def formatwarning(message, category, filename, lineno): + """Function to format a warning the standard way.""" + import linecache + s = "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message) + line = linecache.getline(filename, lineno).strip() + if line: + s = s + " " + line + "\n" + return s + +def filterwarnings(action, message="", category=Warning, module="", lineno=0, + append=0): + """Insert an entry into the list of warnings filters (at the front). + + Use assertions to check that all arguments have the right type.""" + assert action in ("error", "ignore", "always", "default", "module", + "once"), "invalid action: %s" % `action` + assert isinstance(message, types.StringType), "message must be a string" + assert isinstance(category, types.ClassType), "category must be a class" + assert issubclass(category, Warning), "category must be a Warning subclass" + assert type(module) is types.StringType, "module must be a string" + assert type(lineno) is types.IntType and lineno >= 0, \ + "lineno must be an int >= 0" + item = (action, re.compile(message, re.I), category, + re.compile(module), lineno) + if append: + filters.append(item) + else: + filters.insert(0, item) + +def resetwarnings(): + """Clear the list of warning filters, so that no filters are active.""" + filters[:] = [] + +class _OptionError(Exception): + """Exception used by option processing helpers.""" + pass + +# Helper to process -W options passed via sys.warnoptions +def _processoptions(args): + for arg in args: + try: + _setoption(arg) + except _OptionError, msg: + print >>sys.stderr, "Invalid -W option ignored:", msg + +# Helper for _processoptions() +def _setoption(arg): + parts = arg.split(':') + if len(parts) > 5: + raise _OptionError("too many fields (max 5): %s" % `arg`) + while len(parts) < 5: + parts.append('') + action, message, category, module, lineno = [s.strip() + for s in parts] + action = _getaction(action) + message = re.escape(message) + category = _getcategory(category) + module = re.escape(module) + if module: + module = module + '$' + if lineno: + try: + lineno = int(lineno) + if lineno < 0: + raise ValueError + except (ValueError, OverflowError): + raise _OptionError("invalid lineno %s" % `lineno`) + else: + lineno = 0 + filterwarnings(action, message, category, module, lineno) + +# Helper for _setoption() +def _getaction(action): + if not action: + return "default" + if action == "all": return "always" # Alias + for a in ['default', 'always', 'ignore', 'module', 'once', 'error']: + if a.startswith(action): + return a + raise _OptionError("invalid action: %s" % `action`) + +# Helper for _setoption() +def _getcategory(category): + if not category: + return Warning + if re.match("^[a-zA-Z0-9_]+$", category): + try: + cat = eval(category) + except NameError: + raise _OptionError("unknown warning category: %s" % `category`) + else: + i = category.rfind(".") + module = category[:i] + klass = category[i+1:] + try: + m = __import__(module, None, None, [klass]) + except ImportError: + raise _OptionError("invalid module name: %s" % `module`) + try: + cat = getattr(m, klass) + except AttributeError: + raise _OptionError("unknown warning category: %s" % `category`) + if (not isinstance(cat, types.ClassType) or + not issubclass(cat, Warning)): + raise _OptionError("invalid warning category: %s" % `category`) + return cat + +# Self-test +def _test(): + import getopt + testoptions = [] + try: + opts, args = getopt.getopt(sys.argv[1:], "W:") + except getopt.error, msg: + print >>sys.stderr, msg + return + for o, a in opts: + testoptions.append(a) + try: + _processoptions(testoptions) + except _OptionError, msg: + print >>sys.stderr, msg + return + for item in filters: print item + hello = "hello world" + warn(hello); warn(hello); warn(hello); warn(hello) + warn(hello, UserWarning) + warn(hello, DeprecationWarning) + for i in range(3): + warn(hello) + filterwarnings("error", "", Warning, "", 0) + try: + warn(hello) + except Exception, msg: + print "Caught", msg.__class__.__name__ + ":", msg + else: + print "No exception" + resetwarnings() + try: + filterwarnings("booh", "", Warning, "", 0) + except Exception, msg: + print "Caught", msg.__class__.__name__ + ":", msg + else: + print "No exception" + +# Module initialization +if __name__ == "__main__": + import __main__ + sys.modules['warnings'] = __main__ + _test() +else: + _processoptions(sys.warnoptions) + filterwarnings("ignore", category=OverflowWarning, append=1) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/weakref.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/weakref.py new file mode 100644 index 00000000..d3121baf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/weakref.py @@ -0,0 +1,284 @@ +"""Weak reference support for Python. + +This module is an implementation of PEP 205: + +http://python.sourceforge.net/peps/pep-0205.html +""" + +# Naming convention: Variables named "wr" are weak reference objects; +# they are called this instead of "ref" to avoid name collisions with +# the module-global ref() function imported from _weakref. + +import UserDict + +from _weakref import \ + getweakrefcount, \ + getweakrefs, \ + ref, \ + proxy, \ + CallableProxyType, \ + ProxyType, \ + ReferenceType + +from exceptions import ReferenceError + + +ProxyTypes = (ProxyType, CallableProxyType) + +__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs", + "WeakKeyDictionary", "ReferenceType", "ProxyType", + "CallableProxyType", "ProxyTypes", "WeakValueDictionary"] + + +class WeakValueDictionary(UserDict.UserDict): + """Mapping class that references values weakly. + + Entries in the dictionary will be discarded when no strong + reference to the value exists anymore + """ + # We inherit the constructor without worrying about the input + # dictionary; since it uses our .update() method, we get the right + # checks (if the other dictionary is a WeakValueDictionary, + # objects are unwrapped on the way out, and we always wrap on the + # way in). + + def __getitem__(self, key): + o = self.data[key]() + if o is None: + raise KeyError, key + else: + return o + + def __repr__(self): + return "" % id(self) + + def __setitem__(self, key, value): + self.data[key] = ref(value, self.__makeremove(key)) + + def copy(self): + new = WeakValueDictionary() + for key, wr in self.data.items(): + o = wr() + if o is not None: + new[key] = o + return new + + def get(self, key, default=None): + try: + wr = self.data[key] + except KeyError: + return default + else: + o = wr() + if o is None: + # This should only happen + return default + else: + return o + + def items(self): + L = [] + for key, wr in self.data.items(): + o = wr() + if o is not None: + L.append((key, o)) + return L + + def iteritems(self): + return WeakValuedItemIterator(self) + + def iterkeys(self): + return self.data.iterkeys() + __iter__ = iterkeys + + def itervalues(self): + return WeakValuedValueIterator(self) + + def popitem(self): + while 1: + key, wr = self.data.popitem() + o = wr() + if o is not None: + return key, o + + def setdefault(self, key, default): + try: + wr = self.data[key] + except KeyError: + self.data[key] = ref(default, self.__makeremove(key)) + return default + else: + return wr() + + def update(self, dict): + d = self.data + for key, o in dict.items(): + d[key] = ref(o, self.__makeremove(key)) + + def values(self): + L = [] + for wr in self.data.values(): + o = wr() + if o is not None: + L.append(o) + return L + + def __makeremove(self, key): + def remove(o, selfref=ref(self), key=key): + self = selfref() + if self is not None: + del self.data[key] + return remove + + +class WeakKeyDictionary(UserDict.UserDict): + """ Mapping class that references keys weakly. + + Entries in the dictionary will be discarded when there is no + longer a strong reference to the key. This can be used to + associate additional data with an object owned by other parts of + an application without adding attributes to those objects. This + can be especially useful with objects that override attribute + accesses. + """ + + def __init__(self, dict=None): + self.data = {} + def remove(k, selfref=ref(self)): + self = selfref() + if self is not None: + del self.data[k] + self._remove = remove + if dict is not None: self.update(dict) + + def __delitem__(self, key): + for ref in self.data.iterkeys(): + o = ref() + if o == key: + del self.data[ref] + return + + def __getitem__(self, key): + return self.data[ref(key)] + + def __repr__(self): + return "" % id(self) + + def __setitem__(self, key, value): + self.data[ref(key, self._remove)] = value + + def copy(self): + new = WeakKeyDictionary() + for key, value in self.data.items(): + o = key() + if o is not None: + new[o] = value + return new + + def get(self, key, default=None): + return self.data.get(ref(key),default) + + def has_key(self, key): + try: + wr = ref(key) + except TypeError: + return 0 + return self.data.has_key(wr) + + def items(self): + L = [] + for key, value in self.data.items(): + o = key() + if o is not None: + L.append((o, value)) + return L + + def iteritems(self): + return WeakKeyedItemIterator(self) + + def iterkeys(self): + return WeakKeyedKeyIterator(self) + __iter__ = iterkeys + + def itervalues(self): + return self.data.itervalues() + + def keys(self): + L = [] + for wr in self.data.keys(): + o = wr() + if o is not None: + L.append(o) + return L + + def popitem(self): + while 1: + key, value = self.data.popitem() + o = key() + if o is not None: + return o, value + + def setdefault(self, key, default): + return self.data.setdefault(ref(key, self._remove),default) + + def update(self, dict): + d = self.data + for key, value in dict.items(): + d[ref(key, self._remove)] = value + + +class BaseIter: + def __iter__(self): + return self + + +class WeakKeyedKeyIterator(BaseIter): + def __init__(self, weakdict): + self._next = weakdict.data.iterkeys().next + + def next(self): + while 1: + wr = self._next() + obj = wr() + if obj is not None: + return obj + + +class WeakKeyedItemIterator(BaseIter): + def __init__(self, weakdict): + self._next = weakdict.data.iteritems().next + + def next(self): + while 1: + wr, value = self._next() + key = wr() + if key is not None: + return key, value + + +class WeakValuedValueIterator(BaseIter): + def __init__(self, weakdict): + self._next = weakdict.data.itervalues().next + + def next(self): + while 1: + wr = self._next() + obj = wr() + if obj is not None: + return obj + + +class WeakValuedItemIterator(BaseIter): + def __init__(self, weakdict): + self._next = weakdict.data.iteritems().next + + def next(self): + while 1: + key, wr = self._next() + value = wr() + if value is not None: + return key, value + + +# no longer needed +del UserDict diff --git a/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/whrandom.py b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/whrandom.py new file mode 100644 index 00000000..f841e2d6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Scripts/ServerPython/python/system/whrandom.py @@ -0,0 +1,140 @@ +"""Wichman-Hill random number generator. + +Wichmann, B. A. & Hill, I. D. (1982) +Algorithm AS 183: +An efficient and portable pseudo-random number generator +Applied Statistics 31 (1982) 188-190 + +see also: + Correction to Algorithm AS 183 + Applied Statistics 33 (1984) 123 + + McLeod, A. I. (1985) + A remark on Algorithm AS 183 + Applied Statistics 34 (1985),198-200 + + +USE: +whrandom.random() yields double precision random numbers + uniformly distributed between 0 and 1. + +whrandom.seed(x, y, z) must be called before whrandom.random() + to seed the generator + +There is also an interface to create multiple independent +random generators, and to choose from other ranges. + + + +Multi-threading note: the random number generator used here is not +thread-safe; it is possible that nearly simultaneous calls in +different theads return the same random value. To avoid this, you +have to use a lock around all calls. (I didn't want to slow this +down in the serial case by using a lock here.) +""" + +# Translated by Guido van Rossum from C source provided by +# Adrian Baddeley. + + +class whrandom: + def __init__(self, x = 0, y = 0, z = 0): + """Initialize an instance. + Without arguments, initialize from current time. + With arguments (x, y, z), initialize from them.""" + self.seed(x, y, z) + + def seed(self, x = 0, y = 0, z = 0): + """Set the seed from (x, y, z). + These must be integers in the range [0, 256).""" + if not type(x) == type(y) == type(z) == type(0): + raise TypeError, 'seeds must be integers' + if not (0 <= x < 256 and 0 <= y < 256 and 0 <= z < 256): + raise ValueError, 'seeds must be in range(0, 256)' + if 0 == x == y == z: + # Initialize from current time + import time + t = long(time.time() * 256) + t = int((t&0xffffff) ^ (t>>24)) + t, x = divmod(t, 256) + t, y = divmod(t, 256) + t, z = divmod(t, 256) + # Zero is a poor seed, so substitute 1 + self._seed = (x or 1, y or 1, z or 1) + + def random(self): + """Get the next random number in the range [0.0, 1.0).""" + # This part is thread-unsafe: + # BEGIN CRITICAL SECTION + x, y, z = self._seed + # + x = (171 * x) % 30269 + y = (172 * y) % 30307 + z = (170 * z) % 30323 + # + self._seed = x, y, z + # END CRITICAL SECTION + # + return (x/30269.0 + y/30307.0 + z/30323.0) % 1.0 + + def uniform(self, a, b): + """Get a random number in the range [a, b).""" + return a + (b-a) * self.random() + + def randint(self, a, b): + """Get a random integer in the range [a, b] including + both end points. + + (Deprecated; use randrange below.)""" + return self.randrange(a, b+1) + + def choice(self, seq): + """Choose a random element from a non-empty sequence.""" + return seq[int(self.random() * len(seq))] + + def randrange(self, start, stop=None, step=1, int=int, default=None): + """Choose a random item from range(start, stop[, step]). + + This fixes the problem with randint() which includes the + endpoint; in Python this is usually not what you want. + Do not supply the 'int' and 'default' arguments.""" + # This code is a bit messy to make it fast for the + # common case while still doing adequate error checking + istart = int(start) + if istart != start: + raise ValueError, "non-integer arg 1 for randrange()" + if stop is default: + if istart > 0: + return int(self.random() * istart) + raise ValueError, "empty range for randrange()" + istop = int(stop) + if istop != stop: + raise ValueError, "non-integer stop for randrange()" + if step == 1: + if istart < istop: + return istart + int(self.random() * + (istop - istart)) + raise ValueError, "empty range for randrange()" + istep = int(step) + if istep != step: + raise ValueError, "non-integer step for randrange()" + if istep > 0: + n = (istop - istart + istep - 1) / istep + elif istep < 0: + n = (istop - istart + istep + 1) / istep + else: + raise ValueError, "zero step for randrange()" + + if n <= 0: + raise ValueError, "empty range for randrange()" + return istart + istep*int(self.random() * n) + + +# Initialize from the current time +_inst = whrandom() +seed = _inst.seed +random = _inst.random +uniform = _inst.uniform +randint = _inst.randint +choice = _inst.choice +randrange = _inst.randrange diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/pfAllCreatables.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/pfAllCreatables.cpp new file mode 100644 index 00000000..c1d09430 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/pfAllCreatables.cpp @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pfAllCreatables.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plAllCreatables.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plAllCreatables.cpp new file mode 100644 index 00000000..90183c9c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plAllCreatables.cpp @@ -0,0 +1,27 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsWindows.h" +#include "plAllCreatables.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClient.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClient.cpp new file mode 100644 index 00000000..e82b92aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClient.cpp @@ -0,0 +1,2538 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#pragma warning(disable: 4284) +#include "HeadSpin.h" +#include "hsTypes.h" +#include "hsWindowHndl.h" +#include "plClient.h" +#include "hsStream.h" +#include "../plResMgr/plResManager.h" +#include "../plResMgr/plKeyFinder.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plScene/plSceneNode.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plClientMsg.h" +#include "../pfCamera/plVirtualCamNeu.h" +#include "hsTimer.h" +#include "../plPipeline/hsG3DDeviceSelector.h" +#include "../plFile/plEncryptedStream.h" +#include "../plFile/plFileUtils.h" +#include "../plInputCore/plInputManager.h" +#include "../plInputCore/plInputInterfaceMgr.h" +#include "../plInputCore/plInputDevice.h" +#include "../plPhysX/plSimulationMgr.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plScene/plRelevanceMgr.h" +#include "../pnTimer/plTimerCallbackManager.h" +#include "../pfAudio/plListener.h" +#include "../pnMessage/plCmdIfaceModMsg.h" +#include "../plMessage/plRoomLoadNotifyMsg.h" +#include "../pnMessage/plPlayerPageMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../plMessage/plTransitionMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../plMessage/plPreloaderMsg.h" +#include "../plMessage/plNetCommMsgs.h" +#include "../plMessage/plAgeLoadedMsg.h" + +#include "../pfConsole/pfConsoleEngine.h" +#include "../pfConsole/pfConsole.h" +#include "../pfConsole/pfConsoleDirSrc.h" +#include "../plScene/plPageTreeMgr.h" +#include "../plScene/plVisMgr.h" +#include "../plFile/hsFiles.h" + +#include "../pfKI/pfKI.h" + +#include "../plAudio/plAudioSystem.h" +#include "../plAudio/plAudioCaps.h" + +#include "../plStatGather/plProfileManagerFull.h" + +#include "plPipeline.h" +#include "../plPipeline/plPipelineCreate.h" +#include "../plPipeline/plPipeDebugFlags.h" +#include "../plPipeline/plTransitionMgr.h" +#include "../plPipeline/plCaptureRender.h" +#include "../plPipeline/plDynamicEnvMap.h" +#include "../plNetClient/plLinkEffectsMgr.h" +#include "../plAvatar/plAvatarClothing.h" +#include "../plAvatar/plArmatureMod.h" +#include "../pnMessage/plProxyDrawMsg.h" + +#include "../plScene/plRenderRequest.h" +#include "../plDrawable/plAccessGeometry.h" +#include "plPipeResReq.h" +#include "../plDrawable/plVisLOSMgr.h" + +#include "../plGImage/plBitmap.h" + +#include "../plStatusLog/plStatusLog.h" +#include "../plProgressMgr/plProgressMgr.h" +#include "../plPipeline/plDTProgressMgr.h" +#include "../plPipeline/plBinkPlayer.h" +#include "../plMessage/plMovieMsg.h" + +#include "../plSDL/plSDL.h" + +#include "../pnDispatch/plDispatch.h" +#include "../pnDispatch/plDispatchLogBase.h" +#include "../pfGameGUIMgr/pfGameGUIMgr.h" +#include "../pfPython/cyMisc.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plRenderRequestMsg.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../plModifier/plSimpleModifier.h" +#include "plAudible.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../pnMessage/plSoundMsg.h" +#include "../pnMessage/plAudioSysMsg.h" +#include "../plMessage/plRenderMsg.h" +#include "../plAgeLoader/plResPatcher.h" +#include "../pfPython/cyPythonInterface.h" +#include "../plUnifiedTime/plClientUnifiedTime.h" +#include "../pfAnimation/plAnimDebugList.h" +#include "../pfGameGUIMgr/pfGUICtrlGenerator.h" + +#include "../plGImage/plWinFontCache.h" +#include "../plGImage/plFontCache.h" + +#include "../pfJournalBook/pfJournalBook.h" + +#include "../plAvatar/plAGAnimInstance.h" +#include "../plAgeLoader/plAgeLoader.h" +#include "../plClientKey/plClientKey.h" + +#include "../CoreLib/plQuality.h" +#include "../plGLight/plShadowCaster.h" + +#include "../plNetClient/plNetLinkingMgr.h" +#include "../plNetCommon/plNetCommonConstants.h" +#include "../plNetGameLib/plNetGameLib.h" + +#include "../pfSecurePreloader/pfSecurePreloader.h" +#include "../pfLocalizationMgr/pfLocalizationMgr.h" + +#include "../pfCsrSrv/pfCsrSrv.h" + +#include "plTweak.h" + +#define MSG_LOADING_BAR + +// static hsVector3 gAbsDown(0,0,-hsScalar1); + +static plDispatchBase* gDisp = nil; +static plTimerCallbackManager* gTimerMgr = nil; +static plAudioSystem* gAudio = nil; + +hsBool plClient::fDelayMS = false; + +plClient* plClient::fInstance=nil; + +static hsTArray fLoadedDLLs; + +plClient::plClient() +: fPipeline(nil), + fDone(false), + fQuitIntro(false), + fWindowHndl(nil), + fInputManager(nil), + fConsole(nil), + fCurrentNode(nil), + fNewCamera(nil), + fpAuxInitDir(nil), + fTransitionMgr(nil), + fLinkEffectsMgr(nil), + fProgressBar(nil), + fGameGUIMgr(nil), + fKIGUIGlue(nil), + fWindowActive(false), + fAnimDebugList(nil), + fClampCap(-1), + fQuality(0), + fPageMgr(nil), + fFontCache(nil), + fHoldLoadRequests(false), + fNumLoadingRooms(0), + fNumPostLoadMsgs(0), + fPostLoadMsgInc(0.f), + fPatchGlobalAges(false) +{ +#ifndef PLASMA_EXTERNAL_RELEASE + bPythonDebugConnected = false; +#endif + + hsStatusMessage("Constructing client\n"); + plClient::SetInstance(this); + // gNextRoom[0] = '\0'; + + // Setup the timer. These can be overriden with console commands. + hsTimer::SetRealTime(true); +#ifdef HS_DEBUGGING +// hsTimer::SetRealTime(false); + hsTimer::SetTimeClamp(0.1f); +#else // HS_DEBUGGING +// hsTimer::SetRealTime(true); + hsTimer::SetTimeClamp(0); +#endif // HS_DEBUGGING + + IDetectAudioVideoSettings(); // need to do this before the console is created + + /// allow console commands to start working early + // Create the console engine + fConsoleEngine = TRACKED_NEW pfConsoleEngine(); + + // create network mgr before console runs + plNetClientMgr::SetInstance(TRACKED_NEW plNetClientMgr); + plAgeLoader::SetInstance(TRACKED_NEW plAgeLoader); + + // Use it to parse the init directory + wchar initFolder[MAX_PATH]; + PathGetInitDirectory(initFolder, arrsize(initFolder)); + pfConsoleDirSrc dirSrc( fConsoleEngine, initFolder, L"*.ini" ); + +#ifndef PLASMA_EXTERNAL_RELEASE + // internal builds also parse the local init folder + dirSrc.ParseDirectory( L"init", L"*.ini" ); +#endif + + /// End of console stuff +} + +plClient::~plClient() +{ + hsStatusMessage("Destructing client\n"); + + plClient::SetInstance( nil ); + + delete fPageMgr; + delete [] fpAuxInitDir; +} + +#include "../plGImage/plAVIWriter.h" +#include "../pfCharacter/pfMarkerMgr.h" + +hsBool plClient::Shutdown() +{ + plSynchEnabler ps(false); // disable dirty state tracking during shutdown + delete fProgressBar; + + CsrSrvShutdown(); + + // Just in case, clear this out (trying to fix a crash bug where this is still active at shutdown) + plDispatch::SetMsgRecieveCallback(nil); + + // Let the resmanager know we're going to be shutting down. + hsgResMgr::ResMgr()->BeginShutdown(); + + // Must kill off all movies before shutting down audio. + IKillMovies(); + + plgAudioSys::Activate(false); + plBinkPlayer::DeInit(); + // + // Get any proxies to commit suicide. + plProxyDrawMsg* nuke = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kAllTypes + | plProxyDrawMsg::kDestroy); + plgDispatch::MsgSend(nuke); + + if (plAVIWriter::IsInitialized()) + plAVIWriter::Instance().Shutdown(); + + hsStatusMessage( "Shutting down client...\n" ); + + // First, before anybody else goes away, write out our key mappings + if( plInputInterfaceMgr::GetInstance() ) + plInputInterfaceMgr::GetInstance()->WriteKeyMap(); + + // tell Python that its ok to shutdown + PythonInterface::WeAreInShutdown(); + + // Shutdown the journalBook API + pfJournalBook::SingletonShutdown(); + + /// Take down the KI + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + if( mgr ) + mgr->UnloadDialog( "KIBlackBar" ); // unload the blackbar which will bootstrap in the rest of the KI dialogs + + // Take down our GUI control generator + pfGUICtrlGenerator::Instance().Shutdown(); + + if (plNetClientMgr::GetInstance()) + { + plNetClientMgr::GetInstance()->Shutdown(); + plNetClientMgr::GetInstance()->UnRegisterAs(kNetClientMgr_KEY); // deletes NetClientMgr instance + plNetClientMgr::SetInstance(nil); + } + + if (plAgeLoader::GetInstance()) + { + plAgeLoader::GetInstance()->Shutdown(); + plAgeLoader::GetInstance()->UnRegisterAs(kAgeLoader_KEY); // deletes instance + plAgeLoader::SetInstance(nil); + } + + if (pfSecurePreloader::IsInstanced()) + { + pfSecurePreloader::GetInstance()->Shutdown(); + // pfSecurePreloader handles its own fixed key unregistration + } + + if (fInputManager) + { + fInputManager->UnRegisterAs(kInput_KEY); + fInputManager = nil; + } + + if( fGameGUIMgr != nil ) + { + fGameGUIMgr->UnRegisterAs( kGameGUIMgr_KEY ); + fGameGUIMgr = nil; + } + + for (int i = 0; i < fRooms.Count(); i++) + { + plSceneNode *sn = fRooms[i].fNode; + GetKey()->Release(sn->GetKey()); + } + fRooms.Reset(); + fRoomsLoading.clear(); + + // Shutdown plNetClientMgr + + plAccessGeometry::DeInit(); + + delete fPipeline; + fPipeline = nil; + + if (plSimulationMgr::GetInstance()) + plSimulationMgr::Shutdown(); + plAvatarMgr::ShutDown(); + plRelevanceMgr::DeInit(); + + if (fPageMgr) + fPageMgr->Reset(); + + if( fKIGUIGlue != nil ) + { + fKIGUIGlue->UnRegisterAs( kKIGUIGlue_KEY ); + fKIGUIGlue = nil; + } + + if( fTransitionMgr != nil ) + { + fTransitionMgr->UnRegisterAs( kTransitionMgr_KEY ); + fTransitionMgr = nil; + } + + delete fConsoleEngine; + fConsoleEngine = nil; + + if (fLinkEffectsMgr) + { + fLinkEffectsMgr->UnRegisterAs( kLinkEffectsMgr_KEY); + fLinkEffectsMgr=nil; + } + + plClothingMgr::DeInit(); + + if( fFontCache != nil ) + { + fFontCache->UnRegisterAs( kFontCache_KEY ); + fFontCache = nil; + } + + pfMarkerMgr::Shutdown(); + + delete fAnimDebugList; + +//#ifndef PLASMA_EXTERNAL_RELEASE + if( fConsole != nil ) + { + // UnRegisterAs destroys the object for us + fConsole->UnRegisterAs( kConsoleObject_KEY ); + fConsole = nil; + } +//#endif + + PythonInterface::finiPython(); + + if (fNewCamera) + fNewCamera->UnRegisterAs( kVirtualCamera1_KEY ); + + // mark the listener for death. + // there's no need to keep this around... + plUoid lu(kListenerMod_KEY); + plKey pLKey = hsgResMgr::ResMgr()->FindKey(lu); + if (pLKey) + { + plListener* pLMod = plListener::ConvertNoRef(pLKey->GetObjectPtr()); + if (pLMod) + pLMod->UnRegisterAs(kListenerMod_KEY); + } + + plgAudioSys::Shutdown(); + + if (pfLocalizationMgr::InstanceValid()) + pfLocalizationMgr::Shutdown(); + + ShutdownDLLs(); + + plVisLOSMgr::DeInit(); + + delete fPageMgr; + fPageMgr = nil; + plGlobalVisMgr::DeInit(); + +#ifdef TRACK_AG_ALLOCS + DumpAGAllocs(); +#endif // TRACK_AG_ALLOCS + + // This will destruct the client. Do it last. + UnRegisterAs(kClient_KEY); + + + return false; +} + +void plClient::InitAuxInits() +{ + // Use another init directory specified in Command line Arg -i + if (fpAuxInitDir) + pfConsoleDirSrc dirSrc( fConsoleEngine, fpAuxInitDir, "*.ini" ); +} + +void plClient::InitInputs() +{ + hsStatusMessage("InitInputs client\n"); + fInputManager = TRACKED_NEW plInputManager( fWindowHndl ); + fInputManager->CreateInterfaceMod(fPipeline); + fInputManager->RegisterAs( kInput_KEY ); + plgDispatch::Dispatch()->RegisterForExactType(plIMouseXEventMsg::Index(), fInputManager->GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plIMouseYEventMsg::Index(), fInputManager->GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plIMouseBEventMsg::Index(), fInputManager->GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), fInputManager->GetKey()); + plInputDevice* pKeyboard = TRACKED_NEW plKeyboardDevice(); + fInputManager->AddInputDevice(pKeyboard); + + plInputDevice* pMouse = TRACKED_NEW plMouseDevice(); + fInputManager->AddInputDevice(pMouse); + + if( fWindowActive ) + fInputManager->Activate( true ); +} + +void plClient::ISetGraphicsDefaults() +{ + // couldn't find display mode set defaults write to ini file + wchar graphicsIniFile[MAX_PATH]; + PathGetInitDirectory(graphicsIniFile, arrsize(graphicsIniFile)); + PathAddFilename(graphicsIniFile, graphicsIniFile, L"graphics.ini", arrsize(graphicsIniFile)); + IWriteDefaultGraphicsSettings(graphicsIniFile); + plPipeline::fInitialPipeParams.Windowed = plPipeline::fDefaultPipeParams.Windowed; + plPipeline::fInitialPipeParams.AntiAliasingAmount = plPipeline::fDefaultPipeParams.AntiAliasingAmount; + plPipeline::fInitialPipeParams.AnisotropicLevel = plPipeline::fDefaultPipeParams.AnisotropicLevel; + plPipeline::fInitialPipeParams.TextureQuality = plPipeline::fDefaultPipeParams.TextureQuality; + plPipeline::fInitialPipeParams.VSync = plPipeline::fDefaultPipeParams.VSync; + plShadowCaster::EnableShadowCast(plPipeline::fDefaultPipeParams.Shadows ? true : false); + plQuality::SetQuality(plPipeline::fDefaultPipeParams.VideoQuality); + if( (fClampCap >= 0) && (fClampCap < plQuality::GetCapability()) ) + plQuality::SetCapability(fClampCap); + plDynamicCamMap::SetEnabled(plPipeline::fDefaultPipeParams.PlanarReflections ? true : false); +} + +hsBool plClient::InitPipeline() +{ + hsStatusMessage("InitPipeline client\n"); + HWND hWnd = fWindowHndl; + + hsG3DDeviceModeRecord dmr; + hsG3DDeviceSelector devSel; + devSel.Enumerate(hWnd); + devSel.RemoveUnusableDevModes(true); + + if (!devSel.GetDefault(&dmr)) + { + hsMessageBox("No suitable rendering devices found.","Plasma", hsMessageBoxNormal, hsMessageBoxIconError); + return true; + } + + hsG3DDeviceRecord *rec = (hsG3DDeviceRecord *)dmr.GetDevice(); + int res = -1; + + if(!plPipeline::fInitialPipeParams.Windowed) + { + // find our resolution if we're not in windowed mode + for ( int i = 0; i < rec->GetModes().GetCount(); i++ ) + { + hsG3DDeviceMode *mode = rec->GetMode(i); + if ((mode->GetWidth() == plPipeline::fInitialPipeParams.Width) && + (mode->GetHeight() == plPipeline::fInitialPipeParams.Height) && + (mode->GetColorDepth() == plPipeline::fInitialPipeParams.ColorDepth)) + { + res = i; + break; + } + } + if(res != -1) + { + // found it set it as the current mode. + dmr = hsG3DDeviceModeRecord(*rec, *rec->GetMode(res)); + } + else + { + ISetGraphicsDefaults(); + } + } + + if(plPipeline::fInitialPipeParams.TextureQuality == -1) + { + plPipeline::fInitialPipeParams.TextureQuality = dmr.GetDevice()->GetCap(hsG3DDeviceSelector::kCapsPixelShader) ? 2 : 1; + } + else + { + // clamp value to range + if(plPipeline::fInitialPipeParams.TextureQuality > 2) plPipeline::fInitialPipeParams.TextureQuality = 2; + if(plPipeline::fInitialPipeParams.TextureQuality < 0) plPipeline::fInitialPipeParams.TextureQuality = 0; + plBitmap::SetGlobalLevelChopCount(2 - plPipeline::fInitialPipeParams.TextureQuality); + } + + plPipeline *pipe = plPipelineCreate::CreatePipeline( hWnd, &dmr ); + if( pipe->GetErrorString() != nil ) + { + ISetGraphicsDefaults(); +#ifdef PLASMA_EXTERNAL_RELEASE + hsMessageBox("There was an error initializing the video card.\nSetting defaults.", "Error", hsMessageBoxNormal); +#else + hsMessageBox( pipe->GetErrorString(), "Error creating pipeline", hsMessageBoxNormal ); +#endif + delete pipe; + devSel.GetDefault(&dmr); + pipe = plPipelineCreate::CreatePipeline( hWnd, &dmr ); + if(pipe->GetErrorString() != nil) + { + // not much else we can do + return true; + } + } + fPipeline = pipe; + + hsVector3 up; + hsPoint3 from, at; + from.Set(0, 0, 10.f); + at.Set(0, 20.f, 10.f); + up.Set(0,0,-1.f); + hsMatrix44 cam; + cam.MakeCamera(&from,&at,&up); + + float yon = 500.0f; + + pipe->SetFOV( 60.f, hsIntToScalar( 60.f * pipe->Height() / pipe->Width() ) ); + pipe->SetDepth( 0.3f, yon ); + + hsMatrix44 id; + id.Reset(); + + pipe->SetWorldToCamera( cam, id ); + pipe->RefreshMatrices(); + + // Do this so we're still black before we show progress bars, but the correct color coming out of 'em + fClearColor.Set( 0.f, 0.f, 0.f, 1.f ); + pipe->SetClear(&fClearColor); + pipe->ClearRenderTarget(); + + plAccessGeometry::Init(pipe); + + if( fPipeline ) + fPipeline->LoadResources(); + + return false; +} + +//============================================================================ +void plClient::SetClearColor( hsColorRGBA &color ) +{ + fClearColor = color; + if( fPipeline != nil ) + { + fPipeline->SetClear(&fClearColor, nil); + } +} + +//============================================================================ +void plClient::IDispatchMsgReceiveCallback() +{ + if (fInstance->fProgressBar) + fInstance->fProgressBar->Increment(1); + + static char buf[30]; + sprintf(buf, "Msg %d", fInstance->fNumPostLoadMsgs); + fInstance->IIncProgress(fInstance->fPostLoadMsgInc, buf); + + fInstance->fNumPostLoadMsgs++; +} + + +//============================================================================ +hsBool plClient::MsgReceive(plMessage* msg) +{ + if (plGenRefMsg * genRefMsg = plGenRefMsg::ConvertNoRef(msg)) { + // do nothing, we just use the client's key to ref vault image nodes. + return true; + } + + plClientRefMsg* pRefMsg = plClientRefMsg::ConvertNoRef(msg); + if (pRefMsg) + { + switch(pRefMsg->fType) + { + case plClientRefMsg::kLoadRoom : + #ifndef PLASMA_EXTERNAL_RELEASE + plStatusLog::AddLineS( "pageouts.log", ".. ClientRefMsg received for room %s", pRefMsg->GetRef() != nil ? pRefMsg->GetRef()->GetKey()->GetUoid().GetObjectName() : "nilref" ); + #endif + + // was it that the room was loaded? + if (hsCheckBits(pRefMsg->GetContext(), plRefMsg::kOnCreate)) + IRoomLoaded(plSceneNode::Convert(pRefMsg->GetRef()), false); + // or was it that the room was unloaded? + else if (hsCheckBits(pRefMsg->GetContext(), plRefMsg::kOnDestroy)) + IRoomUnloaded(plSceneNode::Convert(pRefMsg->GetRef())); + #ifndef PLASMA_EXTERNAL_RELEASE + else + plStatusLog::AddLineS("pageouts.log", ".. refMsg is UNHANDLED"); + #endif + break; + + case plClientRefMsg::kLoadRoomHold: + if (hsCheckBits(pRefMsg->GetContext(), plRefMsg::kOnCreate)) + IRoomLoaded(plSceneNode::Convert(pRefMsg->GetRef()), true); + break; + + // + // Manually add room. + // Add to pageMgr, but don't load the entire room. + // + case plClientRefMsg::kManualRoom: + { + if (pRefMsg->GetContext() & plRefMsg::kOnCreate || + pRefMsg->GetContext() & plRefMsg::kOnRequest) + { + hsBool found=false; + plSceneNode *pNode = plSceneNode::ConvertNoRef(pRefMsg->GetRef()); + int i; + for (i = 0; i < fRooms.Count(); i++) + { + if (fRooms[i].fNode->GetKey() == pRefMsg->GetSender()) + { + found=true; + break; + } + } + if (!found) + { + if (pNode) + { + fRooms.Append( plRoomRec( pNode, 0 ) ); + fPageMgr->AddNode(pNode); + } + } + } + else + { + plSceneNode* node = plSceneNode::ConvertNoRef(pRefMsg->GetRef()); + if(node) + { + int i; + for (i = 0; i < fRooms.Count(); i++) + { + if (fRooms[i].fNode->GetKey() == node->GetKey()) + { + fRooms.Remove(i); + break; + } + } + fPageMgr->RemoveNode(node); + } + } + } + break; + + } + } + + plClientMsg* pMsg = plClientMsg::ConvertNoRef(msg); + if (pMsg) + { + switch(pMsg->GetClientMsgFlag()) + { + case plClientMsg::kQuit: + SetDone(true); + break; + + case plClientMsg::kLoadRoom: + case plClientMsg::kLoadRoomHold: + { + IQueueRoomLoad(pMsg->GetRoomLocs(), (pMsg->GetClientMsgFlag() == plClientMsg::kLoadRoomHold)); + if (!fHoldLoadRequests) + ILoadNextRoom(); + } + break; + + case plClientMsg::kUnloadRoom: + IUnloadRooms(pMsg->GetRoomLocs()); + break; + + case plClientMsg::kLoadNextRoom: + ILoadNextRoom(); + break; + + // Load optimizations: messages to pre-load and un-load all the keys in a given age + case plClientMsg::kLoadAgeKeys: + { + plResManager *mgr = (plResManager *)hsgResMgr::ResMgr(); + mgr->LoadAgeKeys( pMsg->GetAgeName() ); + } + break; + + case plClientMsg::kReleaseAgeKeys: + { + plResManager *mgr = (plResManager *)hsgResMgr::ResMgr(); + mgr->DropAgeKeys( pMsg->GetAgeName() ); + } + break; + + case plClientMsg::kDisableRenderScene: + { + plClient::GetInstance()->SetFlag( plClient::kFlagDBGDisableRender, true ); + } + break; + case plClientMsg::kEnableRenderScene: + { + plClient::GetInstance()->SetFlag( plClient::kFlagDBGDisableRender, false ); + } + break; + + case plClientMsg::kResetGraphicsDevice: + { + ResetDisplayDevice(pMsg->fGraphicsSettings.fWidth, pMsg->fGraphicsSettings.fHeight, pMsg->fGraphicsSettings.fColorDepth, pMsg->fGraphicsSettings.fWindowed, pMsg->fGraphicsSettings.fNumAASamples, pMsg->fGraphicsSettings.fMaxAnisoSamples, pMsg->fGraphicsSettings.fVSync); + } + break; + + case plClientMsg::kSetGraphicsDefaults: + { + ISetGraphicsDefaults(); + ResetDisplayDevice(plPipeline::fDefaultPipeParams.Width, plPipeline::fDefaultPipeParams.Height, plPipeline::fDefaultPipeParams.ColorDepth, plPipeline::fDefaultPipeParams.Windowed, + plPipeline::fDefaultPipeParams.AntiAliasingAmount, plPipeline::fDefaultPipeParams.AnisotropicLevel, plPipeline::fDefaultPipeParams.VSync, true); + } + break; + + } + return true; + } + plRenderRequestMsg* rendReq = plRenderRequestMsg::ConvertNoRef(msg); + if( rendReq ) + { + IAddRenderRequest(rendReq->Request()); + return true; + } + plEventCallbackMsg* callback = plEventCallbackMsg::ConvertNoRef(msg); + if( callback ) + { + char str[256]; + sprintf(str, "Callback event from %s\n", callback->GetSender() + ? callback->GetSender()->GetName() + : "Unknown"); + hsStatusMessage(str); + static int gotten = 0; + if( ++gotten > 5 ) + { + plSimpleModifier* simpMod = plSimpleModifier::ConvertNoRef(callback->GetSender()->ObjectIsLoaded()); + plAudible* aud = plAudible::ConvertNoRef(callback->GetSender()->ObjectIsLoaded()); + if( simpMod ) + { + plAnimCmdMsg* cmd = TRACKED_NEW plAnimCmdMsg; + cmd->AddReceiver(simpMod->GetKey()); + cmd->SetCmd(plAnimCmdMsg::kRemoveCallbacks); + cmd->AddCallback(callback); + plgDispatch::MsgSend(cmd); + hsRefCnt_SafeUnRef(callback); + } + else if( aud ) + { + plSoundMsg* cmd = TRACKED_NEW plSoundMsg; + cmd->AddReceiver(aud->GetKey()); + cmd->SetCmd(plSoundMsg::kRemoveCallbacks); + cmd->AddCallback(callback); + plgDispatch::MsgSend(cmd); + hsRefCnt_SafeUnRef(callback); + } + hsStatusMessage("Removed\n"); + gotten = 0; + } + return true; + } + plMovieMsg* mov = plMovieMsg::ConvertNoRef(msg); + if( mov ) + { + return IHandleMovieMsg(mov); + } + + plLinkEffectsTriggerMsg* linkFX = plLinkEffectsTriggerMsg::ConvertNoRef(msg); + if (linkFX) + { + if (!linkFX->IsLeavingAge()) + { +#ifdef MSG_LOADING_BAR + // Temporary stat gathering stuff + #if 0//ndef PLASMA_EXTERNAL_RELEASE + hsUNIXStream s; + s.Open("Messages.txt", "at"); + static bool firstLog = true; + if (firstLog) + { + firstLog = false; + s.WriteString("------------------------------------\n"); + } + char buf[256]; + sprintf(buf, "%s %d\n", plAgeLoader::GetInstance()->GetCurrAgeFilename(), fNumPostLoadMsgs); + s.WriteString(buf); + s.Close(); + #endif +#endif + } + return true; + } + + //============================================================================ + // plNetCommAuthMsg + //============================================================================ + if (plNetCommAuthMsg * authCommMsg = plNetCommAuthMsg::ConvertNoRef(msg)) { + IHandleNetCommAuthMsg(authCommMsg); + return true; + } + + //============================================================================ + // plPreloaderMsg + //============================================================================ + if (plPreloaderMsg * preloaderMsg = plPreloaderMsg::ConvertNoRef(msg)) { + IHandlePreloaderMsg(preloaderMsg); + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + +//============================================================================ +hsBool plClient::IHandleMovieMsg(plMovieMsg* mov) +{ + if( !(mov->GetFileName() && *mov->GetFileName()) ) + return true; + + int i; + i = fMovies.GetCount(); + if( !(mov->GetCmd() & plMovieMsg::kMake) ) + { + for( i = 0; i < fMovies.GetCount(); i++ ) + { + if( !stricmp(mov->GetFileName(), fMovies[i]->GetFileName()) ) + break; + } + } + if( i == fMovies.GetCount() ) + { + + fMovies.Append(TRACKED_NEW plBinkPlayer); + fMovies[i]->SetFileName(mov->GetFileName()); + } + + if( mov->GetCmd() & plMovieMsg::kAddCallbacks ) + { + int j; + for( j = 0; j < mov->GetNumCallbacks(); j++ ) + fMovies[i]->AddCallback(mov->GetCallback(j)); + } + if( mov->GetCmd() & plMovieMsg::kMove ) + fMovies[i]->SetPosition(mov->GetCenter()); + if( mov->GetCmd() & plMovieMsg::kScale ) + fMovies[i]->SetScale(mov->GetScale()); + if( mov->GetCmd() & plMovieMsg::kColorAndOpacity ) + fMovies[i]->SetColor(mov->GetColor()); + if( mov->GetCmd() & plMovieMsg::kColor ) + { + hsColorRGBA c = fMovies[i]->GetColor(); + c.Set(mov->GetColor().r, mov->GetColor().g, mov->GetColor().b, c.a); + fMovies[i]->SetColor(c); + } + if( mov->GetCmd() & plMovieMsg::kOpacity ) + { + hsColorRGBA c = fMovies[i]->GetColor(); + c.a = mov->GetColor().a; + fMovies[i]->SetColor(c); + } + if( mov->GetCmd() & plMovieMsg::kFadeIn ) + { + fMovies[i]->SetFadeFromColor(mov->GetFadeInColor()); + fMovies[i]->SetFadeFromTime(mov->GetFadeInSecs()); + } + if( mov->GetCmd() & plMovieMsg::kFadeOut ) + { + fMovies[i]->SetFadeToColor(mov->GetFadeOutColor()); + fMovies[i]->SetFadeToTime(mov->GetFadeOutSecs()); + } + if( mov->GetCmd() & plMovieMsg::kVolume ) + fMovies[i]->SetVolume(mov->GetVolume()); + + if( mov->GetCmd() & plMovieMsg::kStart ) + fMovies[i]->Start(fPipeline, fWindowHndl); + if( mov->GetCmd() & plMovieMsg::kPause ) + fMovies[i]->Pause(true); + if( mov->GetCmd() & plMovieMsg::kResume ) + fMovies[i]->Pause(false); + if( mov->GetCmd() & plMovieMsg::kStop ) + fMovies[i]->Stop(); + + // If a movie has lost its filename, it means something went horribly wrong + // with playing it and it has shutdown. Or we just stopped it. Either way, + // we need to clear it out of our list. + if( !(fMovies[i]->GetFileName() && *fMovies[i]->GetFileName()) ) + { + delete fMovies[i]; + fMovies.Remove(i); + } + + return true; +} + +int plClient::IFindRoomByLoc(const plLocation& loc) +{ + for (int i = 0; i < fRooms.Count(); i++) + { + if (fRooms[i].fNode->GetKey()->GetUoid().GetLocation() == loc) + return i; + } + + return -1; +} + +bool plClient::IIsRoomLoading(const plLocation& loc) +{ + for (int i = 0; i < fRoomsLoading.size(); i++) + { + if (fRoomsLoading[i] == loc) + return true; + } + return false; +} + +void plClient::SetHoldLoadRequests(bool hold) +{ + fHoldLoadRequests = hold; + if (!fHoldLoadRequests) + ILoadNextRoom(); +} + +#include "../plResMgr/plPageInfo.h" + +void plClient::IQueueRoomLoad(const std::vector& locs, bool hold) +{ + bool allSameAge = true; + const char* lastAgeName = nil; + + UInt32 numRooms = 0; + for (int i = 0; i < locs.size(); i++) + { + const plLocation& loc = locs[i]; + + const plPageInfo* info = plKeyFinder::Instance().GetLocationInfo(loc); + bool alreadyLoaded = (IFindRoomByLoc(loc) != -1); + bool isLoading = IIsRoomLoading(loc); + if (!info || alreadyLoaded || isLoading) + { + #ifdef HS_DEBUGGING + if (!info) + hsStatusMessageF("Ignoring LoadRoom request for location 0x%x because we can't find the location", loc.GetSequenceNumber()); + else if (alreadyLoaded) + hsStatusMessageF("Ignoring LoadRoom request for %s-%s, since room is already loaded", info->GetAge(), info->GetPage()); + else if (isLoading) + hsStatusMessageF("Ignoring LoadRoom request for %s-%s, since room is currently loading", info->GetAge(), info->GetPage()); + #endif + + continue; + } + + fLoadRooms.push_back(TRACKED_NEW LoadRequest(loc, hold)); + + if (!lastAgeName || hsStrEQ(info->GetAge(), lastAgeName)) + lastAgeName = info->GetAge(); + else + allSameAge = false; + +// hsStatusMessageF("+++ Loading room %s-%s", info.GetAge(), info.GetPage()); + numRooms++; + } + + if (numRooms == 0) + return; + + fNumLoadingRooms += numRooms; +} + +void plClient::ILoadNextRoom() +{ + LoadRequest* req = nil; + + while (!fLoadRooms.empty()) + { + req = fLoadRooms.front(); + fLoadRooms.pop_front(); + + bool alreadyLoaded = (IFindRoomByLoc(req->loc) != -1); + hsBool isLoading = IIsRoomLoading(req->loc); + if (alreadyLoaded || isLoading) + { + delete req; + req = nil; + fNumLoadingRooms--; + } + else + break; + } + + if (req) + { + plClientRefMsg* pRefMsg = TRACKED_NEW plClientRefMsg(GetKey(), + plRefMsg::kOnCreate, -1, + req->hold ? plClientRefMsg::kLoadRoomHold : plClientRefMsg::kLoadRoom); + + fRoomsLoading.push_back(req->loc); // flag the location as currently loading + + // PageInPage is not guaranteed to finish synchronously, just FYI + plResManager *mgr = (plResManager *)hsgResMgr::ResMgr(); + mgr->PageInRoom(req->loc, plSceneNode::Index(), pRefMsg); + + delete req; + + plClientMsg* nextRoom = TRACKED_NEW plClientMsg(plClientMsg::kLoadNextRoom); + nextRoom->Send(GetKey()); + } +} + +void plClient::IUnloadRooms(const std::vector& locs) +{ + for (int i = 0; i < locs.size(); i++) + { + const plLocation& loc = locs[i]; + + if (!loc.IsValid()) + continue; + + plKey nodeKey = nil; + + // First, look in our room list. It *should* be there, which allows us to avoid a + // potential nasty reload-find in the resMgr. + int roomIdx = IFindRoomByLoc(loc); + if (roomIdx != -1) + nodeKey = fRooms[roomIdx].fNode->GetKey(); + + if (nodeKey == nil) + { + nodeKey = plKeyFinder::Instance().FindSceneNodeKey(loc); + } + + if (nodeKey != nil) + { + plSceneNode* node = plSceneNode::ConvertNoRef(nodeKey->ObjectIsLoaded()); + if (node) + { + #ifndef PLASMA_EXTERNAL_RELEASE + plStatusLog::AddLineS("pageouts.log", "SceneNode for %s loaded; Removing node", node->GetKey()->GetUoid().GetObjectName()); + #endif + fPageMgr->RemoveNode(node); + } + else + { + #ifndef PLASMA_EXTERNAL_RELEASE + plStatusLog::AddLineS("pageouts.log", "SceneNode for %s NOT loaded", nodeKey->GetUoid().GetObjectName()); + #endif + } + GetKey()->Release(nodeKey); // release notify interest in scene node + + UInt32 recFlags = 0; + if (roomIdx != -1) + { + recFlags = fRooms[roomIdx].fFlags; + fRooms.Remove(roomIdx); + } + + if (node == fCurrentNode) + fCurrentNode = nil; + + #ifndef PLASMA_EXTERNAL_RELEASE + plStatusLog::AddLineS("pageouts.log", "Telling netClientMgr about paging out %s", nodeKey->GetUoid().GetObjectName()); + #endif + + if (plNetClientMgr::GetInstance() != nil) + { + // Don't care really about the message that just came in, we care whether it was really held or not + if (!hsCheckBits(recFlags, plRoomRec::kHeld)) + plAgeLoader::GetInstance()->StartPagingOutRoom(&nodeKey, 1); + // Tell NetClientManager not to expect any pageout info on this guy, since he was held + else + plAgeLoader::GetInstance()->IgnorePagingOutRoom(&nodeKey, 1); + } + } + else + { + #ifndef PLASMA_EXTERNAL_RELEASE +// plStatusLog::AddLineS("pageouts.log", "++ Can't find node key for paging out room %s, loc 0x%x", +// pMsg->GetRoomName() != nil ? pMsg->GetRoomName() : "", +// loc.GetSequenceNumber()); + #endif + } + } +} + +void plClient::IRoomLoaded(plSceneNode* node, bool hold) +{ + fCurrentNode = node; + // make sure we don't already have this room in the list: + hsBool bAppend = true; + for (int i = 0; i < fRooms.Count(); i++) + { + if (fRooms[i].fNode == fCurrentNode) + { + bAppend = false; + break; + } + } + if (bAppend) + { + if (hold) + { + fRooms.Append(plRoomRec(fCurrentNode, plRoomRec::kHeld)); + } + else + { + fRooms.Append(plRoomRec(fCurrentNode, 0)); + fPageMgr->AddNode(fCurrentNode); + } + } + + fNumLoadingRooms--; + + // Shut down the progress bar if that was the last room + if (fProgressBar != nil && fNumLoadingRooms <= 0) + { +#ifdef MSG_LOADING_BAR + if (!hold) + { + struct AgeMsgCount { const char* AgeName; int NumMsgs; }; + static AgeMsgCount ageMsgCount[] = + { + { "BahroCave", 2600 }, + { "BaronCityOffice", 670 }, + { "city", 269000 }, + { "Cleft", 11000 }, + { "Garden", 19700 }, + { "Garrison", 28800 }, + { "Gira", 3300 }, + { "Kadish", 19700 }, + { "Neighborhood", 19900 }, + { "Nexus", 1400 }, + { "Personal", 20300 }, + { "Teledahn", 48000 } + }; + + char name[256]; + strcpy(name, &fProgressBar->GetTitle()[strlen("Loading ")]); + name[strlen(name)-3] = '\0'; + + // Get the precalculated value for how many messages will be + // sent out before the screen actually fades in + int numMsgs = 0; + for (int i = 0; i < sizeof(ageMsgCount)/sizeof(AgeMsgCount); i++) + { + if (hsStrEQ(ageMsgCount[i].AgeName, name)) + { + numMsgs = ageMsgCount[i].NumMsgs; + break; + } + } + + fNumPostLoadMsgs = 0; + + // The last 10% of the age loading bar is for messages, so adjust + // our progress bar increment to fill the bar fully when all + // messages have been sent + float max = fProgressBar->GetMax(); + float amtLeft = max - (max * 0.9f); + fPostLoadMsgInc = (numMsgs != 0) ? amtLeft / numMsgs : 0; + +#ifndef PLASMA_EXTERNAL_RELEASE + if (plDispatchLogBase::IsLogging()) + plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle(), "displaying messages"); +#endif // PLASMA_EXTERNAL_RELEASE +#endif + } + } + + hsRefCnt_SafeUnRef(fCurrentNode); + plKey pRmKey = fCurrentNode->GetKey(); + plAgeLoader::GetInstance()->FinishedPagingInRoom(&pRmKey, 1); + // *** this used to call "ActivateNode" (in physics) which wasn't implemented. + // *** we should make this "turn on" physics for the selected node + // *** depending on what guarantees we can make about the load state -- anything useful? + + // now tell all those who are interested that a room was loaded + if (!hold) + { + plRoomLoadNotifyMsg* loadmsg = TRACKED_NEW plRoomLoadNotifyMsg; + loadmsg->SetRoom(pRmKey); + loadmsg->SetWhatHappen(plRoomLoadNotifyMsg::kLoaded); + plgDispatch::MsgSend(loadmsg); + } + else + hsStatusMessageF("Done loading hold room %s, t=%f\n", pRmKey->GetName(), hsTimer::GetSeconds()); + + plLocation loc = pRmKey->GetUoid().GetLocation(); + for (int i = 0; i < fRoomsLoading.size(); i++) + { + if (fRoomsLoading[i] == loc) + { + fRoomsLoading.erase(fRoomsLoading.begin() + i); + break; + } + } + + if (!fNumLoadingRooms) + IStopProgress(); +} + +//============================================================================ +void plClient::IRoomUnloaded(plSceneNode* node) +{ + #ifndef PLASMA_EXTERNAL_RELEASE + plStatusLog::AddLineS("pageouts.log", ".. refMsg is onDestroy"); + #endif + + fCurrentNode = node; + hsRefCnt_SafeUnRef(fCurrentNode); + plKey pRmKey = fCurrentNode->GetKey(); + if (plAgeLoader::GetInstance()) + plAgeLoader::GetInstance()->FinishedPagingOutRoom(&pRmKey, 1); + + // tell all those who are interested that a room was unloaded + plRoomLoadNotifyMsg* loadmsg = TRACKED_NEW plRoomLoadNotifyMsg; + loadmsg->SetRoom(pRmKey); + loadmsg->SetWhatHappen(plRoomLoadNotifyMsg::kUnloaded); + plgDispatch::MsgSend(loadmsg); +} + +void plClient::IReadKeyedObjCallback(plKey key) +{ + fInstance->IIncProgress(1, key->GetName()); +} + +//============================================================================ +void plClient::IProgressMgrCallbackProc(plOperationProgress * progress) +{ + if(!fInstance) + return; + + fInstance->fMessagePumpProc(); + fInstance->IDraw(); +} + +//============================================================================ +void plClient::IIncProgress (hsScalar byHowMuch, const char * text) +{ + if (fProgressBar) { +#ifndef PLASMA_EXTERNAL_RELEASE + fProgressBar->SetStatusText( text ); +#endif + fProgressBar->Increment( byHowMuch ); + } +} + +//============================================================================ +void plClient::IStartProgress( const char *title, hsScalar len ) +{ + if (fProgressBar) + { + fProgressBar->SetLength(fProgressBar->GetMax()+len); + } + else + { + fProgressBar = plProgressMgr::GetInstance()->RegisterOperation(len, title, plProgressMgr::kNone, false, true); +#ifndef PLASMA_EXTERNAL_RELEASE + if (plDispatchLogBase::IsLogging()) + plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle(), "starting"); +#endif // PLASMA_EXTERNAL_RELEASE + + ((plResManager*)hsgResMgr::ResMgr())->SetProgressBarProc(IReadKeyedObjCallback); + plDispatch::SetMsgRecieveCallback(IDispatchMsgReceiveCallback); + + fLastProgressUpdate = 0.f; + } + // Workaround for NVidia driver bug, showing up as BCO not there first time. + // See Mantis bug 0014590. + if( fPipeline ) + fPipeline->LoadResources(); +} + + +//============================================================================ +void plClient::IStopProgress( void ) +{ + if (fProgressBar) + { +#ifndef PLASMA_EXTERNAL_RELEASE + if (plDispatchLogBase::IsLogging()) + plDispatchLogBase::GetInstance()->LogStatusBarChange(fProgressBar->GetTitle(), "done"); +#endif // PLASMA_EXTERNAL_RELEASE + + plDispatch::SetMsgRecieveCallback(nil); + ((plResManager*)hsgResMgr::ResMgr())->SetProgressBarProc(IReadKeyedObjCallback); + delete fProgressBar; + fProgressBar = nil; + + plPipeResReq::Request(); + + fFlags.SetBit(kFlagGlobalDataLoaded); + if (fFlags.IsBitSet(kFlagAsyncInitComplete)) + ICompleteInit(); + } +} + +/***************************************************************************** +* +* +* +***/ + +extern hsBool gDataServerLocal; + +#include "plQuality.h" +#include "plLoadMask.h" + +#if 0 +class LoginNetClientCommCallback : public plNetClientComm::Callback +{ +public: + enum Op {kAuth, kCreatePlayer, kGetPlayerList, kLeave, kDeletePlayer}; + + LoginNetClientCommCallback() : plNetClientComm::Callback(), fNumCurrentOps(0) + {} + + virtual void OperationStarted( UInt32 context ) + { + fNumCurrentOps++; + } + virtual void OperationComplete( UInt32 context, int resultCode ) + { + if (context == kAuth) + { + if (hsSucceeded(resultCode)) + { + plClient::GetInstance()->fAuthPassed = true; + } + } + else if (context == kGetPlayerList) + { + if ( hsSucceeded( resultCode ) ) + { + UInt32 numPlayers = fCbArgs.GetInt(0); + UInt32 pId = -1; + std::string pName; + + for (UInt32 i = 0; i < numPlayers; i++) + { + pId = fCbArgs.GetInt((UInt16)(i*3+1)); + pName = fCbArgs.GetString((UInt16)(i*3+2)); + + if (pName == plClient::GetInstance()->fUsername) + { + plClient::GetInstance()->fPlayerID = pId; + break; + } + } + } + } + else if (context == kCreatePlayer) + { + if (hsSucceeded(resultCode)) + plClient::GetInstance()->fPlayerID = fCbArgs.GetInt(0); + } + else if (context == kDeletePlayer) + { + if (hsSucceeded(resultCode)) + plClient::GetInstance()->fPlayerID = -1; + } + + fNumCurrentOps--; + } + + bool IsActive() + { + return fNumCurrentOps > 0; + } + +private: + int fNumCurrentOps; +}; +#endif + +//============================================================================ +hsBool plClient::StartInit() +{ + hsStatusMessage("Init client\n"); + fFlags.SetBit( kFlagIniting ); + + pfLocalizationMgr::Initialize("dat"); + + plQuality::SetQuality(fQuality); + if( (GetClampCap() >= 0) && (GetClampCap() < plQuality::GetCapability()) ) + plQuality::SetCapability(GetClampCap()); + + /// 2.16.2001 mcn - Moved console engine init to constructor, + /// so we could use console commands even before the pipeline init + + plDTProgressMgr::DeclareThyself(); + + // Set our callback for the progress manager so everybody else can use it + fLastProgressUpdate = 0.f; + plProgressMgr::GetInstance()->SetCallbackProc( IProgressMgrCallbackProc ); + + // Check the registry, which deletes data files that are either corrupt or + // have old version numbers. If the file still exists on the file server + // then it will be patched on-the-fly as needed (unless you're running with + // local data of course). + ((plResManager *)hsgResMgr::ResMgr())->VerifyPages(); + + // the dx8 audio system MUST be initialized + // before the database is loaded + HWND hWnd = fWindowHndl; + SetForegroundWindow(fWindowHndl); + + plgAudioSys::Init(hWnd); + gAudio = plgAudioSys::Sys(); + + RegisterAs( kClient_KEY ); + + InitDLLs(); + + plGlobalVisMgr::Init(); + fPageMgr = TRACKED_NEW plPageTreeMgr; + + plVisLOSMgr::Init(fPipeline, fPageMgr); + + // init globals + plAvatarMgr::GetInstance(); + plRelevanceMgr::Init(); + + gDisp = plgDispatch::Dispatch(); + gTimerMgr = plgTimerCallbackMgr::Mgr(); + + // + // initialize input system + // + InitInputs(); + + /// Init the console object + /// Note: this can be done last because the console engine was inited first, and + /// everything in code that works with the console does so through the console engine + + fConsole = TRACKED_NEW pfConsole(); + pfConsole::SetPipeline( fPipeline ); + fConsole->RegisterAs( kConsoleObject_KEY ); // fixedKey from plFixedKey.h + fConsole->Init( fConsoleEngine ); + + /// Init the font cache + fFontCache = TRACKED_NEW plFontCache(); + + /// Init the transition manager + fTransitionMgr = TRACKED_NEW plTransitionMgr(); + fTransitionMgr->RegisterAs( kTransitionMgr_KEY ); // fixedKey from plFixedKey.h + fTransitionMgr->Init(); + + // Init the Age Linking effects manager + fLinkEffectsMgr = TRACKED_NEW plLinkEffectsMgr(); + fLinkEffectsMgr->RegisterAs( kLinkEffectsMgr_KEY ); // fixedKey from plFixedKey.h + fLinkEffectsMgr->Init(); + + /// Init the in-game GUI manager + fGameGUIMgr = TRACKED_NEW pfGameGUIMgr(); + fGameGUIMgr->RegisterAs( kGameGUIMgr_KEY ); + fGameGUIMgr->Init(); + + plgAudioSys::Activate(true); + + plConst(hsScalar) delay(2.f); + //commenting out publisher splash for MORE + //IPlayIntroBink("avi/intro0.bik", delay, 0.f, 0.f, 1.f, 1.f, 0.75); + //if( GetDone() ) return false; + IPlayIntroBink("avi/intro1.bik", 0.f, 0.f, 0.f, 1.f, 1.f, 0.75); + if( GetDone() ) return false; + plgDispatch::Dispatch()->RegisterForExactType(plMovieMsg::Index(), GetKey()); + + // + // Init Net before loading things + // + plgDispatch::Dispatch()->RegisterForExactType(plNetCommAuthMsg::Index(), GetKey()); + plNetClientMgr::GetInstance()->Init(); + plAgeLoader::GetInstance()->Init(); + pfSecurePreloader::GetInstance()->Init(); + + plCmdIfaceModMsg* pModMsg2 = TRACKED_NEW plCmdIfaceModMsg; + pModMsg2->SetBCastFlag(plMessage::kBCastByExactType); + pModMsg2->SetSender(fConsole->GetKey()); + pModMsg2->SetCmd(plCmdIfaceModMsg::kAdd); + plgDispatch::MsgSend(pModMsg2); + + // create new the virtual camera + fNewCamera = TRACKED_NEW plVirtualCam1; + fNewCamera->RegisterAs( kVirtualCamera1_KEY ); + fNewCamera->Init(); + fNewCamera->SetPipeline( GetPipeline() ); + + float aspectratio = (float)fPipeline->Width() / (float)fPipeline->Height(); + plVirtualCam1::SetAspectRatio(aspectratio); + plVirtualCam1::SetFOV(plVirtualCam1::GetFOVw(), plVirtualCam1::GetFOVh()); + + pfGameGUIMgr::GetInstance()->SetAspectRatio( aspectratio ); + plMouseDevice::Instance()->SetDisplayResolution((float)fPipeline->Width(), (float)fPipeline->Height()); + + // create the listener for the audio system: + plListener* pLMod = TRACKED_NEW plListener; + pLMod->RegisterAs(kListenerMod_KEY ); + + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), pLMod->GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAudioSysMsg::Index(), pLMod->GetKey()); + + plSynchedObject::PushSynchDisabled(false); // enable dirty tracking + + if (StrCmp(NetCommGetStartupAge()->ageDatasetName, "StartUp") == 0) + { + plNetCommAuthMsg * msg = NEW(plNetCommAuthMsg); + msg->result = kNetSuccess; + msg->param = nil; + msg->Send(); + } + + // 2nd half of plClient initialization occurs after + // all network events have completed. Async events: + // + // 1) Download secure files + // + // Continue plClient init via IOnAsyncInitComplete(). + + return true; +} + +//============================================================================ +void plClient::IPatchGlobalAgeFiles( void ) +{ + const char * ageFiles[] = { + "GlobalAnimations", + "GlobalAvatars", + "GlobalClothing", + "GlobalMarkers", + "GUI", + "CustomAvatars" + }; + + for (unsigned i = 0; i < arrsize(ageFiles); ++i) { + plResPatcher myPatcher(ageFiles[i], true); + + if (gDataServerLocal) + break; + + if (!myPatcher.Update()) { + SetDone(true); + break; + } + } +} + +void plClient::InitDLLs() +{ + hsStatusMessage("Init dlls client\n"); + char str[255]; + typedef void (*PInitGlobalsFunc) (hsResMgr *, plFactory *, plTimerCallbackManager *, plTimerShare*, + plNetClientApp*); + + hsFolderIterator modDllFolder("ModDLL\\"); + while (modDllFolder.NextFileSuffix(".dll")) + { + modDllFolder.GetPathAndName(str); + HMODULE hMod = LoadLibrary(str); + if (hMod) + { + PInitGlobalsFunc initGlobals = (PInitGlobalsFunc)GetProcAddress(hMod, "InitGlobals"); + initGlobals(hsgResMgr::ResMgr(), plFactory::GetTheFactory(), plgTimerCallbackMgr::Mgr(), + hsTimer::GetTheTimer(), plNetClientApp::GetInstance()); + fLoadedDLLs.Append(hMod); + } + } +} + +void plClient::ShutdownDLLs() +{ + int j; + for( j = 0; j < fLoadedDLLs.GetCount(); j++ ) + { + BOOL ret = FreeLibrary(fLoadedDLLs[j]); + if( !ret ) + hsStatusMessage("Failed to free lib\n"); + } + fLoadedDLLs.Reset(); +} + +hsBool plClient::MainLoop() +{ +#ifndef PLASMA_EXTERNAL_RELEASE + if (PythonInterface::UsePythonDebugger()) + { + PythonInterface::PythonDebugger()->Update(); + + if (PythonInterface::PythonDebugger()->IsConnected()) + { + bPythonDebugConnected = true; + if (PythonInterface::DebuggerRequestedExit() && PythonInterface::PythonDebugger()->ExitOnStop()) + SetDone(true); // debugger requested that we stop running, so exit nicely + } + else + bPythonDebugConnected = false; + } +#endif + +#ifdef PLASMA_EXTERNAL_RELEASE + if (DebugIsDebuggerPresent()) + { + NetCliAuthLogClientDebuggerConnect(); + SetDone(true); + } +#endif + + if(plClient::fDelayMS) + Sleep(5); + + // Reset our stats + plProfileManager::Instance().BeginFrame(); + + if (IUpdate()) + return true; + + if (IDraw()) + return true; + + plProfileManagerFull::Instance().EndFrame(); + plProfileManager::Instance().EndFrame(); + + // Draw the stats + plProfileManagerFull::Instance().Update(); + + return false; +} + +#include "plProfile.h" + +plProfile_Extern(DrawTime); +plProfile_Extern(UpdateTime); +plProfile_CreateTimer("ResMgr", "Update", ResMgr); +plProfile_CreateTimer("DispatchQueue", "Update", DispatchQueue); +plProfile_CreateTimer("RenderSetup", "Update", RenderMsg); +plProfile_CreateTimer("Simulation", "Update", Simulation); +plProfile_CreateTimer("NetTime", "Update", UpdateNetTime); +plProfile_Extern(TimeMsg); +plProfile_Extern(EvalMsg); +plProfile_Extern(TransformMsg); +plProfile_Extern(CameraMsg); +plProfile_Extern(AnimatingPhysicals); +plProfile_Extern(StoppedAnimPhysicals); + +plProfile_CreateTimer("BeginRender", "Render", BeginRender); +plProfile_CreateTimer("ClearRender", "Render", ClearRender); +plProfile_CreateTimer("PreRender", "Render", PreRender); +plProfile_CreateTimer("MainRender", "Render", MainRender); +plProfile_CreateTimer("PostRender", "Render", PostRender); +plProfile_CreateTimer("Movies", "Render", Movies); +plProfile_CreateTimer("Console", "Render", Console); +plProfile_CreateTimer("StatusLog", "Render", StatusLog); +plProfile_CreateTimer("ProgressMgr", "Render", ProgressMgr); +plProfile_CreateTimer("ScreenElem", "Render", ScreenElem); +plProfile_CreateTimer("EndRender", "Render", EndRender); + + +hsBool plClient::IUpdate() +{ + plProfile_BeginTiming(UpdateTime); + + // reset timer on first frame if realtime and not clamping, to avoid initial large delta + if (hsTimer::GetSysSeconds()==0 && hsTimer::IsRealTime() && hsTimer::GetTimeClamp()==0) + hsTimer::SetRealTime(true); + + plProfile_BeginTiming(DispatchQueue); + plgDispatch::Dispatch()->MsgQueueProcess(); + plProfile_EndTiming(DispatchQueue); + + const char *inputUpdate = "Update"; + if (fInputManager) // Is this used anymore? Seems to always be nil. + fInputManager->Update(); + + hsTimer::IncSysSeconds(); + plClientUnifiedTime::SetSysTime(); // keep a unified time, based on sysSeconds + // Time may have been clamped in IncSysSeconds, depending on hsTimer's current mode. + + double currTime = hsTimer::GetSysSeconds(); + hsScalar delSecs = hsTimer::GetDelSysSeconds(); + + // do not change this ordering + + plProfile_BeginTiming(UpdateNetTime); + plNetClientMgr::GetInstance()->Update(currTime); + plProfile_EndTiming(UpdateNetTime); + + // update python + //plCaptureRender::Update(fPipeline); + plCaptureRender::Update(); + cyMisc::Update(currTime); + + // This TimeMsg doesn't really do much, except somehow it flushes the dispatch + // after the NetClientMgr updates, delivering any SelfDestruct messages in the + // queue. This is important to prevent objects that are about to go away from + // starting trouble during their update. So to get rid of this message, some + // other way of flushing the dispatch after NegClientMgr's update is needed. mf + plProfile_BeginTiming(TimeMsg); + plTimeMsg* msg = TRACKED_NEW plTimeMsg(nil, nil, nil, nil); + plgDispatch::MsgSend(msg); + plProfile_EndTiming(TimeMsg); + + plProfile_BeginTiming(EvalMsg); + plEvalMsg* eval = TRACKED_NEW plEvalMsg(nil, nil, nil, nil); + plgDispatch::MsgSend(eval); + plProfile_EndTiming(EvalMsg); + + char *xFormLap1 = "Main"; + plProfile_BeginLap(TransformMsg, xFormLap1); + plTransformMsg* xform = TRACKED_NEW plTransformMsg(nil, nil, nil, nil); + plgDispatch::MsgSend(xform); + plProfile_EndLap(TransformMsg, xFormLap1); + + plCoordinateInterface::SetTransformPhase(plCoordinateInterface::kTransformPhaseDelayed); + + if (fAnimDebugList) + fAnimDebugList->ShowReport(); + + plProfile_BeginTiming(Simulation); + plSimulationMgr::GetInstance()->Advance(delSecs); + plProfile_EndTiming(Simulation); + + // At this point, we just register for a plDelayedTransformMsg when dirtied. + if (!plCoordinateInterface::GetDelayedTransformsEnabled()) + { + char *xFormLap2 = "Simulation"; + plProfile_BeginLap(TransformMsg, xFormLap2); + xform = TRACKED_NEW plTransformMsg(nil, nil, nil, nil); + plgDispatch::MsgSend(xform); + plProfile_EndLap(TransformMsg, xFormLap2); + } + else + { + char *xFormLap3 = "Delayed"; + plProfile_BeginLap(TransformMsg, xFormLap3); + xform = TRACKED_NEW plDelayedTransformMsg(nil, nil, nil, nil); + plgDispatch::MsgSend(xform); + plProfile_EndLap(TransformMsg, xFormLap3); + } + + plCoordinateInterface::SetTransformPhase(plCoordinateInterface::kTransformPhaseNormal); + + plProfile_BeginTiming(CameraMsg); + plCameraMsg* cameras = TRACKED_NEW plCameraMsg; + cameras->SetCmd(plCameraMsg::kUpdateCameras); + cameras->SetBCastFlag(plMessage::kBCastByExactType); + plgDispatch::MsgSend(cameras); + plProfile_EndTiming(CameraMsg); + + if (fPatchGlobalAges) + { + // Download or patch our global ages, if necessary + IPatchGlobalAgeFiles(); + IOnAsyncInitComplete(); + + fPatchGlobalAges = false; + } + + return false; +} + +hsBool plClient::IDrawProgress() { + // HACK: Don't draw while we're caching some room loads, otherwise the + // progress bar will jump around while we're calculating the size + if (fHoldLoadRequests) + return false; + + // Reset our stats + plProfileManager::Instance().BeginFrame(); + + plProfile_BeginTiming(DrawTime); + if( fPipeline->BeginRender() ) + { + return IFlushRenderRequests(); + } + // Override the clear color to black. + fPipeline->ClearRenderTarget(&hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + +#ifndef PLASMA_EXTERNAL_RELEASE + fConsole->Draw( fPipeline ); +#endif + + plStatusLogMgr::GetInstance().Draw(); + plProgressMgr::GetInstance()->Draw( fPipeline ); + fPipeline->RenderScreenElements(); + fPipeline->EndRender(); + plProfile_EndTiming(DrawTime); + + plProfileManager::Instance().EndFrame(); + + return false; +} + +hsBool plClient::IDraw() +{ + // Limit framerate + static float lastDrawTime; + static const float kMaxFrameRate = 1.f/30.f; + float currTime = (float) hsTimer::GetSeconds(); + if (!fPipeline->IsDebugFlagSet(plPipeDbg::kFlagNVPerfHUD)) + { + // If we're using NVPerfHUD to step through draw calls, + // We're going to have a frame delta of zero. In that + // case we need to draw no matter what, and we don't + // care as much about starving other threads because + // we're presumably just debugging a graphics glitch. + if ((currTime - lastDrawTime) < kMaxFrameRate) + return true; + } + lastDrawTime = currTime; + + // If we're shutting down, don't attempt to draw. Doing so + // tends to cause a device reload each frame. + if (fDone) + return true; + + if (plProgressMgr::GetInstance()->IsActive()) + return IDrawProgress(); + + plProfile_Extern(VisEval); + plProfile_BeginTiming(VisEval); + plGlobalVisMgr::Instance()->Eval(fPipeline->GetViewPositionWorld()); + plProfile_EndTiming(VisEval); + + plProfile_BeginTiming(RenderMsg); + plRenderMsg* rendMsg = TRACKED_NEW plRenderMsg(fPipeline); + plgDispatch::MsgSend(rendMsg); + plProfile_EndTiming(RenderMsg); + + plPreResourceMsg* preMsg = TRACKED_NEW plPreResourceMsg(fPipeline); + plgDispatch::MsgSend(preMsg); + + // This might not be the ideal place for this, but it + // needs to be AFTER the plRenderMsg is sent, and + // BEFORE BeginRender. (plRenderMsg causes construction of + // Dynamic objects (e.g. RT's), BeginRender uses them (e.g. shadows). + if( plPipeResReq::Check() || fPipeline->CheckResources() ) + { + fPipeline->LoadResources(); + } + + plProfile_EndTiming(UpdateTime); + + plProfile_BeginTiming(DrawTime); + + plProfile_BeginTiming(BeginRender); + if( fPipeline->BeginRender() ) + { + plProfile_EndTiming(BeginRender); + return IFlushRenderRequests(); + } + plProfile_EndTiming(BeginRender); + + plProfile_BeginTiming(ClearRender); + fPipeline->ClearRenderTarget(); + plProfile_EndTiming(ClearRender); + + plProfile_BeginTiming(PreRender); + if( !fFlags.IsBitSet( kFlagDBGDisableRRequests ) ) + IProcessPreRenderRequests(); + plProfile_EndTiming(PreRender); + + plProfile_BeginTiming(MainRender); + if( !fFlags.IsBitSet( kFlagDBGDisableRender ) ) + fPageMgr->Render(fPipeline); + plProfile_EndTiming(MainRender); + + plProfile_BeginTiming(PostRender); + if( !fFlags.IsBitSet( kFlagDBGDisableRRequests ) ) + IProcessPostRenderRequests(); + plProfile_EndTiming(PostRender); + + plProfile_BeginTiming(Movies); + IServiceMovies(); + plProfile_EndTiming(Movies); + +#ifndef PLASMA_EXTERNAL_RELEASE + plProfile_BeginTiming(Console); + fConsole->Draw( fPipeline ); + plProfile_EndTiming(Console); +#endif + + plProfile_BeginTiming(StatusLog); + plStatusLogMgr::GetInstance().Draw(); + plProfile_EndTiming(StatusLog); + + plProfile_BeginTiming(ProgressMgr); + plProgressMgr::GetInstance()->Draw( fPipeline ); + plProfile_EndTiming(ProgressMgr); + + fLastProgressUpdate = hsTimer::GetSeconds(); + + plProfile_BeginTiming(ScreenElem); + fPipeline->RenderScreenElements(); + plProfile_EndTiming(ScreenElem); + + plProfile_BeginTiming(EndRender); + fPipeline->EndRender(); + plProfile_EndTiming(EndRender); + + plProfile_EndTiming(DrawTime); + + return false; +} + +void plClient::IServiceMovies() +{ + int i; + for( i = 0; i < fMovies.GetCount(); i++ ) + { + hsAssert(fMovies[i]->GetFileName() && *fMovies[i]->GetFileName(), "Lost our movie"); + if( !fMovies[i]->NextFrame() ) + { + delete fMovies[i]; + fMovies.Remove(i); + i--; + } + } +} + +void plClient::IKillMovies() +{ + int i; + for( i = 0; i < fMovies.GetCount(); i++ ) + delete fMovies[i]; + fMovies.Reset(); +} + +hsBool plClient::IPlayIntroBink(const char* movieName, hsScalar endDelay, hsScalar posX, hsScalar posY, hsScalar scaleX, hsScalar scaleY, hsScalar volume /* = 1.0 */) +{ + SetQuitIntro(false); + plBinkPlayer player; + player.SetPosition(posX, posY); + player.SetScale(scaleX, scaleY); + player.SetFileName(movieName); + player.SetFadeToTime(endDelay); + player.SetFadeToColor(hsColorRGBA().Set(0, 0, 0, 1.f)); + player.SetVolume(volume); + bool firstTry = true; // flag to make sure that we don't quit before we even start + + if( player.Start(fPipeline, fWindowHndl) ) + { + while( true ) + { + if( fInstance ) + fInstance->fMessagePumpProc(); + + if( GetDone() ) + return true; + if (firstTry) + { + firstTry = false; + SetQuitIntro(false); + } + else + { + if( GetQuitIntro() ) + return true; + } + + hsBool done = false; + if( !fPipeline->BeginRender() ) + { + fPipeline->ClearRenderTarget(); + done = !player.NextFrame(); + + fPipeline->EndRender(); + } + + if( done ) + return true; + } + return true; + } + return false; +} + +hsBool plClient::IFlushRenderRequests() +{ + // For those requesting ack's, we could go through and send them + // mail telling them their request was ill-timed. But hopefully, + // the lack of an acknowledgement will serve as notice. + int i; + for( i = 0; i < fPreRenderRequests.GetCount(); i++ ) + hsRefCnt_SafeUnRef(fPreRenderRequests[i]); + fPreRenderRequests.Reset(); + + for( i = 0; i < fPostRenderRequests.GetCount(); i++ ) + hsRefCnt_SafeUnRef(fPostRenderRequests[i]); + fPostRenderRequests.Reset(); + + return false; +} + +void plClient::IProcessRenderRequests(hsTArray& reqs) +{ + int i; + for( i = 0; i < reqs.GetCount(); i++ ) + { + reqs[i]->Render(fPipeline, fPageMgr); + hsRefCnt_SafeUnRef(reqs[i]); + } + reqs.SetCount(0); +} + +void plClient::IProcessPreRenderRequests() +{ + IProcessRenderRequests(fPreRenderRequests); +} + +void plClient::IProcessPostRenderRequests() +{ + IProcessRenderRequests(fPostRenderRequests); +} + +void plClient::IAddRenderRequest(plRenderRequest* req) +{ + if( req->GetPriority() < 0 ) + { + int i; + for( i = 0; i < fPreRenderRequests.GetCount(); i++ ) + { + if( req->GetPriority() < fPreRenderRequests[i]->GetPriority() ) + break; + } + fPreRenderRequests.Insert(i, req); + hsRefCnt_SafeRef(req); + } + else + { + int i; + for( i = 0; i < fPostRenderRequests.GetCount(); i++ ) + { + if( req->GetPriority() < fPostRenderRequests[i]->GetPriority() ) + break; + } + fPostRenderRequests.Insert(i, req); + hsRefCnt_SafeRef(req); + } +} + +hsG3DDeviceModeRecord plClient::ILoadDevMode(const char* devModeFile) +{ + hsStatusMessage("Load DevMode client\n"); + HWND hWnd = fWindowHndl; + + hsUNIXStream stream; + hsBool gottaCreate = false; + + // If DevModeFind is specified, use the old method +// if ((GetGameFlags() & kDevModeFind)) +// FindAndSaveDevMode(hWnd, devModeFile); + // Otherwise, use the new method + hsG3DDeviceModeRecord dmr; + if (stream.Open(devModeFile, "rb")) + { + /// It's there, but is the device record valid? + hsG3DDeviceRecord selRec; + hsG3DDeviceMode selMode; + + selRec.Read(&stream); + if( selRec.IsInvalid() ) + { + hsStatusMessage( "WARNING: Old DeviceRecord found on file. Setting defaults..." ); + gottaCreate = true; + } + else + { + /// Read the rest in + selMode.Read(&stream); + + UInt16 performance = stream.ReadSwap16(); + + if( performance < 25 ) + plBitmap::SetGlobalLevelChopCount( 2 ); + else if( performance < 75 ) + plBitmap::SetGlobalLevelChopCount( 1 ); + else + plBitmap::SetGlobalLevelChopCount( 0 ); + } + stream.Close(); + + dmr = hsG3DDeviceModeRecord(selRec, selMode); + } + else + gottaCreate = true; + + if( gottaCreate ) + { + + hsG3DDeviceSelector devSel; + devSel.Enumerate(hWnd); + devSel.RemoveUnusableDevModes(true); + + if (!devSel.GetDefault(&dmr)) + { + //hsAssert(0, "plGame::LoadDevMode - No acceptable hardware found"); + hsMessageBox("No suitable rendering devices found.","realMYST",hsMessageBoxNormal); + return dmr; + } + + if (stream.Open(devModeFile, "wb")) + { + dmr.GetDevice()->Write(&stream); + dmr.GetMode()->Write(&stream); + stream.WriteSwap16((UInt16)(0*100)); + stream.Close(); + } + + } + return dmr; +} + +void plClient::ResetDisplayDevice(int Width, int Height, int ColorDepth, hsBool Windowed, int NumAASamples, int MaxAnisotropicSamples, hsBool VSync, hsBool windowOnly) +{ + if(!fPipeline) return; + int BorderWidth = 0, BorderHeight = 0, CaptionHeight = 0; + + WindowActivate(false); + int ActualWidth; + int ActualHeight; + + if( Windowed ) + { + BorderWidth = GetSystemMetrics( SM_CXSIZEFRAME ); + BorderHeight = GetSystemMetrics( SM_CYSIZEFRAME ); + CaptionHeight = GetSystemMetrics( SM_CYCAPTION ); + ActualWidth = Width + BorderWidth * 2; + ActualHeight = Height + BorderHeight * 2 + CaptionHeight; + SetWindowLong( fWindowHndl, GWL_STYLE, + WS_POPUP | WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE); + SetWindowLong(fWindowHndl, GWL_EXSTYLE, 0); + } + else + { + SetWindowLong(fWindowHndl, GWL_STYLE, + WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZE | WS_VISIBLE); + + SetWindowLong(fWindowHndl, GWL_EXSTYLE, WS_EX_APPWINDOW); + } + + if(!windowOnly) + fPipeline->ResetDisplayDevice(Width, Height, ColorDepth, Windowed, NumAASamples, MaxAnisotropicSamples, VSync); + + float aspectratio = (float)Width / (float)Height; + plMouseDevice::Instance()->SetDisplayResolution((float)Width, (float)Height); + pfGameGUIMgr::GetInstance()->SetAspectRatio( aspectratio ); + + UINT flags = SWP_NOCOPYBITS | SWP_NOMOVE | SWP_SHOWWINDOW; + if(Windowed) + { + SetWindowPos( fWindowHndl, HWND_NOTOPMOST, 0, 0, ActualWidth, ActualHeight, flags ); + } + else + { + SetWindowPos( fWindowHndl, HWND_TOP, 0, 0, Width, Height, flags ); + ::ClipCursor(nil); + } + + WindowActivate(true); +} + + +void WriteBool(hsStream *stream, char *name, hsBool on ) +{ + char command[256]; + sprintf(command, "%s %s\r\n", name, on ? "true" : "false"); + stream->WriteString(command); +} + +void WriteInt(hsStream *stream, char *name, int val ) +{ + char command[256]; + sprintf(command, "%s %d\r\n", name, val); + stream->WriteString(command); +} + +void WriteString(hsStream *stream, const char *name, const char *val) +{ + char command[256]; + sprintf(command, "%s %s\r\n", name, val); + stream->WriteString(command); +} + +// Detect audio/video settings and save them to their respective ini file, if ini files don't exist +void plClient::IDetectAudioVideoSettings() +{ + // Setup default pipeline settings + hsBool devmode = true; + hsG3DDeviceModeRecord dmr; + hsG3DDeviceSelector devSel; + devSel.Enumerate(fWindowHndl); + devSel.RemoveUnusableDevModes(true); + + if (!devSel.GetDefault(&dmr)) + devmode = false; + hsG3DDeviceRecord *rec = (hsG3DDeviceRecord *)dmr.GetDevice(); + const hsG3DDeviceMode *mode = dmr.GetMode(); + + hsBool pixelshaders = rec->GetCap(hsG3DDeviceSelector::kCapsPixelShader); + int psMajor = 0, psMinor = 0; + rec->GetPixelShaderVersion(psMajor, psMinor); + hsBool refDevice = false; + if(rec->GetG3DHALorHEL() == hsG3DDeviceSelector::kHHD3DRefDev) + refDevice = true; + + plPipeline::fDefaultPipeParams.Width = hsG3DDeviceSelector::kDefaultWidth; + plPipeline::fDefaultPipeParams.Height = hsG3DDeviceSelector::kDefaultHeight; + plPipeline::fDefaultPipeParams.ColorDepth = hsG3DDeviceSelector::kDefaultDepth; +#if defined(HS_DEBUGGING) || defined(DEBUG) + plPipeline::fDefaultPipeParams.Windowed = true; +#else + plPipeline::fDefaultPipeParams.Windowed = false; +#endif + + plPipeline::fDefaultPipeParams.Shadows = 0; + // enable shadows if TnL is available, meaning not an intel extreme. + if(rec->GetG3DHALorHEL() == hsG3DDeviceSelector::kHHD3DTnLHalDev) + plPipeline::fDefaultPipeParams.Shadows = 1; + + // enable planar reflections if pixelshaders are available + if(pixelshaders && !refDevice) + { + plPipeline::fDefaultPipeParams.PlanarReflections = 1; + } + else + { + plPipeline::fDefaultPipeParams.PlanarReflections = 0; + } + + // enable 2x antialiasing and anisotropic to 2 samples if pixelshader version is greater that 2.0 + if(psMajor >= 2 && !refDevice) + { + plPipeline::fDefaultPipeParams.AntiAliasingAmount = rec->GetMaxAnisotropicSamples() ? 2 : 0; + plPipeline::fDefaultPipeParams.AnisotropicLevel = mode->GetNumFSAATypes() ? 2 : 0; + } + else + { + plPipeline::fDefaultPipeParams.AntiAliasingAmount = 0; + plPipeline::fDefaultPipeParams.AnisotropicLevel = 0; + } + + if(refDevice) + { + plPipeline::fDefaultPipeParams.TextureQuality = 0; + plPipeline::fDefaultPipeParams.VideoQuality = 0; + + } + else + { + plPipeline::fDefaultPipeParams.TextureQuality = psMajor >= 2 ? 2 : 1; + plPipeline::fDefaultPipeParams.VideoQuality = pixelshaders ? 2 : 1; + } + plPipeline::fDefaultPipeParams.VSync = false; + + // card specific overrides + if(strstr(rec->GetDriverDesc(), "FX 5200")) + { + plPipeline::fDefaultPipeParams.AntiAliasingAmount = 0; + } + + + int val = 0; + hsStream *stream = nil; + hsUNIXStream s; + wchar audioIniFile[MAX_PATH], graphicsIniFile[MAX_PATH]; + PathGetInitDirectory(audioIniFile, arrsize(audioIniFile)); + StrCopy(graphicsIniFile, audioIniFile, arrsize(audioIniFile)); + PathAddFilename(audioIniFile, audioIniFile, L"audio.ini", arrsize(audioIniFile)); + PathAddFilename(graphicsIniFile, graphicsIniFile, L"graphics.ini", arrsize(graphicsIniFile)); + +#ifndef PLASMA_EXTERNAL_RELEASE + // internal builds can use the local dir + if (PathDoesFileExist(L"init//audio.ini")) + StrCopy(audioIniFile, L"init//audio.ini", arrsize(audioIniFile)); + if (PathDoesFileExist(L"init//graphics.ini")) + StrCopy(graphicsIniFile, L"init//graphics.ini", arrsize(audioIniFile)); +#endif + + //check to see if audio.ini exists + if (s.Open(audioIniFile)) + { + s.Close(); + } + else + { + stream = plEncryptedStream::OpenEncryptedFileWrite(audioIniFile); + + plAudioCaps caps = plAudioCapsDetector::Detect(false, true); + val = 6; + if( (hsPhysicalMemory() < 256) || plProfileManager::Instance().GetProcessorSpeed() < 1350000000) + { + val = 3; + } + + char deviceName[256]; + sprintf(deviceName, "\"%s\"", DEFAULT_AUDIO_DEVICE_NAME); + + WriteBool(stream, "Audio.Initialize", caps.IsAvailable()); + WriteBool(stream, "Audio.UseEAX", false); + WriteInt(stream, "Audio.SetPriorityCutoff", val); + WriteInt(stream, "Audio.MuteAll", false); + WriteInt(stream, "Audio.SetChannelVolume SoundFX", 1); + WriteInt(stream, "Audio.SetChannelVolume BgndMusic", 1); + WriteInt(stream, "Audio.SetChannelVolume Ambience", 1); + WriteInt(stream, "Audio.SetChannelVolume NPCVoice", 1); + WriteInt(stream, "Audio.EnableVoiceRecording", 1); + WriteString(stream, "Audio.SetDeviceName", deviceName ); + stream->Close(); + delete stream; + stream = nil; + } + + // check to see if graphics.ini exists + if (s.Open(graphicsIniFile)) + { + s.Close(); + } + else + { + IWriteDefaultGraphicsSettings(graphicsIniFile); + } +} + +void plClient::IWriteDefaultGraphicsSettings(const wchar* destFile) +{ + hsStream *stream = plEncryptedStream::OpenEncryptedFileWrite(destFile); + + WriteInt(stream, "Graphics.Width", plPipeline::fDefaultPipeParams.Width); + WriteInt(stream, "Graphics.Height", plPipeline::fDefaultPipeParams.Height); + WriteInt(stream, "Graphics.ColorDepth", plPipeline::fDefaultPipeParams.ColorDepth); + WriteBool(stream, "Graphics.Windowed", plPipeline::fDefaultPipeParams.Windowed); + WriteInt(stream, "Graphics.AntiAliasAmount", plPipeline::fDefaultPipeParams.AntiAliasingAmount); + WriteInt(stream, "Graphics.AnisotropicLevel", plPipeline::fDefaultPipeParams.AnisotropicLevel ); + WriteInt(stream, "Graphics.TextureQuality",plPipeline::fDefaultPipeParams.TextureQuality); + WriteInt(stream, "Quality.Level", plPipeline::fDefaultPipeParams.VideoQuality); + WriteInt(stream, "Graphics.Shadow.Enable", plPipeline::fDefaultPipeParams.Shadows); + WriteInt(stream, "Graphics.EnablePlanarReflections", plPipeline::fDefaultPipeParams.PlanarReflections); + WriteBool(stream, "Graphics.EnableVSync", plPipeline::fDefaultPipeParams.VSync); + stream->Close(); + delete stream; + stream = nil; +} + + +void plClient::WindowActivate(bool active) +{ + if (GetDone()) + return; + + if( !fWindowActive != !active ) + { + if( fInputManager != nil ) + fInputManager->Activate( active ); + + plArmatureMod::WindowActivate( active ); + } + fWindowActive = active; +} + + +//============================================================================ +void plClient::IOnAsyncInitComplete () { + // Init State Desc Language (files should now be downloaded and in place) + plSDLMgr::GetInstance()->SetNetApp(plNetClientMgr::GetInstance()); + plSDLMgr::GetInstance()->Init( plSDL::kDisallowTimeStamping ); + + PythonInterface::initPython(); + // set the pipeline for the python cyMisc module so that it can do a screen capture + cyMisc::SetPipeline( fPipeline ); + + // Load our custom fonts from our current dat directory + fFontCache->LoadCustomFonts("dat"); + plWinFontCache::GetInstance().LoadCustomFonts("dat"); + + // We'd like to do a SetHoldLoadRequests here, but the GUI stuff doesn't draw right + // if you try to delay the loading for it. To work around that, we allocate a + // global loading bar in advance and set it to a big enough range that when the GUI's + // are done loading about the right amount of it is filled. + fNumLoadingRooms++; + IStartProgress("Loading Global...", 0); + + /// Init the KI + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + mgr->LoadDialog( "KIBlackBar" ); // load the blackbar which will bootstrap in the rest of the KI dialogs + + // Init the journal book API + pfJournalBook::SingletonInit(); + + SetHoldLoadRequests(true); + fProgressBar->SetLength(fProgressBar->GetProgress()); + + plClothingMgr::Init(); + // Load in any clothing data + ((plResManager*)hsgResMgr::ResMgr())->PageInAge("GlobalClothing"); + + pfMarkerMgr::Instance(); + + fAnimDebugList = TRACKED_NEW plAnimDebugList(); + + /// Now parse final init files (*.fni). These are files just like ini files, only to be run + /// after all hell has broken loose in the client. + wchar initFolder[MAX_PATH]; + PathGetInitDirectory(initFolder, arrsize(initFolder)); + pfConsoleDirSrc dirSrc( fConsoleEngine, initFolder, L"net*.fni" ); // connect to net first +#ifndef PLASMA_EXTERNAL_RELEASE + // internal builds also parse the local init folder + dirSrc.ParseDirectory( L"init", L"net*.fni" ); +#endif + + dirSrc.ParseDirectory( initFolder, L"*.fni" ); +#ifndef PLASMA_EXTERNAL_RELEASE + // internal builds also parse the local init folder + dirSrc.ParseDirectory( L"init", L"*.fni" ); +#endif + + // run fni in the Aux Init dir + if (fpAuxInitDir) + { + dirSrc.ParseDirectory(fpAuxInitDir, "net*.fni" ); // connect to net first + dirSrc.ParseDirectory(fpAuxInitDir, "*.fni" ); + } + + fNumLoadingRooms--; + + ((plResManager*)hsgResMgr::ResMgr())->PageInAge("GlobalAnimations"); + SetHoldLoadRequests(false); + + // Tell the transition manager to start faded out. This is so we don't + // get a frame or two of non-faded drawing before we do our initial fade in + (void)(TRACKED_NEW plTransitionMsg( plTransitionMsg::kFadeOut, 0.0f, true ))->Send(); + + fFlags.SetBit(kFlagAsyncInitComplete); + if (fFlags.IsBitSet(kFlagGlobalDataLoaded)) + ICompleteInit(); +} + +//============================================================================ +void plClient::ICompleteInit () { + // Reset clear color on the pipeline +// fPipeline->ClearRenderTarget( &fClearColor, &depth ); + + plSimulationMgr::GetInstance()->Resume(); // start the sim at the last possible minute + + fFlags.SetBit( kFlagIniting, false ); + hsStatusMessage("Client init complete."); + + // Tell everyone we're ready to rock. + plClientMsg* clientMsg = TRACKED_NEW plClientMsg(plClientMsg::kInitComplete); + clientMsg->SetBCastFlag(plMessage::kBCastByType); + clientMsg->Send(); + + CsrSrvInitialize(); +} + +//============================================================================ +void plClient::IHandlePreloaderMsg (plPreloaderMsg * msg) { + + plgDispatch::Dispatch()->UnRegisterForExactType(plPreloaderMsg::Index(), GetKey()); + + if (!msg->fSuccess) { + char str[1024]; + StrPrintf( + str, + arrsize(str), + "Secure file preloader failed" + ); + plNetClientApp::GetInstance()->QueueDisableNet(true, str); + return; + } + + fPatchGlobalAges = true; +} + +//============================================================================ +void plClient::IHandleNetCommAuthMsg (plNetCommAuthMsg * msg) { + + plgDispatch::Dispatch()->UnRegisterForExactType(plNetCommAuthMsg::Index(), GetKey()); + + if (IS_NET_ERROR(msg->result)) { + char str[1024]; + StrPrintf( + str, + arrsize(str), + // fmt + "Authentication failed: NetError %u, %S.\n" + ,// values + msg->result, + NetErrorToString(msg->result) + ); + plNetClientApp::GetInstance()->QueueDisableNet(true, str); + return; + } + + plgDispatch::Dispatch()->RegisterForExactType(plPreloaderMsg::Index(), GetKey()); + + // Precache our secure files + pfSecurePreloader::GetInstance()->RequestFileGroup(L"Python", L"pak"); + pfSecurePreloader::GetInstance()->RequestFileGroup(L"SDL", L"sdl"); + pfSecurePreloader::GetInstance()->Start(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClient.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClient.h new file mode 100644 index 00000000..d228d577 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClient.h @@ -0,0 +1,295 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#pragma once +#ifndef plClient_inc +#define plClient_inc + + +//#define NEW_CAMERA_CODE + +#include "hsWindowHndl.h" +#include "hsBitVector.h" +#include "hsTemplates.h" +#include "hsUtils.h" +#include "hsStlUtils.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plUoid.h" +#include "../plScene/plRenderRequest.h" + +class plSceneNode; +class plPipeline; +class hsG3DDeviceModeRecord; +class plInputManager; +class plInputController; +class plSceneObject; +class pfConsoleEngine; +class pfConsole; +class plAudioSystem; +class plVirtualCam1; +class plKey; +class plPageTreeMgr; +class plTransitionMgr; +class plLinkEffectsMgr; +class plOperationProgress; +class pfGameGUIMgr; +class pfKI; +class plAnimDebugList; +class plFontCache; +class plClientMsg; +class plLocation; +class plMovieMsg; +class plBinkPlayer; +class plPreloaderMsg; +class plNetCommAuthMsg; +class plAgeLoaded2Msg; + + +typedef void (*plMessagePumpProc)( void ); + +class plClient : public hsKeyedObject +{ +protected: + + class plRoomRec + { + public: + plSceneNode *fNode; + UInt32 fFlags; + + plRoomRec() { fNode = nil; fFlags = 0; } + plRoomRec( plSceneNode *n, UInt32 f ) : fNode( n ), fFlags( f ) {} + + enum Flags + { + kHeld = 0x00000001 + }; + }; + + hsBitVector fFlags; + + plInputManager* fInputManager; + + plPageTreeMgr* fPageMgr; + hsTArray fRooms; + plSceneNode* fCurrentNode; + + plPipeline* fPipeline; + hsColorRGBA fClearColor; + plTransitionMgr *fTransitionMgr; + plLinkEffectsMgr *fLinkEffectsMgr; + plFontCache *fFontCache; + + pfConsoleEngine* fConsoleEngine; + pfConsole* fConsole; + + pfKI *fKIGUIGlue; + + hsBool fDone; + hsBool fWindowActive; + + hsWindowHndl fWindowHndl; + + double fLastProgressUpdate; + plOperationProgress *fProgressBar; + + pfGameGUIMgr *fGameGUIMgr; + + virtual hsG3DDeviceModeRecord ILoadDevMode(const char* devModeFile); + + hsBool IUpdate(); + hsBool IDraw(); + hsBool IDrawProgress(); + + plVirtualCam1* fNewCamera; + + static plClient* fInstance; + char * fpAuxInitDir; + static hsBool fDelayMS; + + int fClampCap; + int fQuality; + + hsBool fQuitIntro; + hsTArray fMovies; + + hsBool fPatchGlobalAges; + + plMessagePumpProc fMessagePumpProc; + +#ifndef PLASMA_EXTERNAL_RELEASE + bool bPythonDebugConnected; +#endif + + hsTArray fPreRenderRequests; + hsTArray fPostRenderRequests; + + bool fHoldLoadRequests; + class LoadRequest + { + public: + LoadRequest(const plLocation& loc, bool hold) { this->loc = loc; this->hold = hold; } + plLocation loc; + bool hold; + }; + typedef std::list LoadList; + LoadList fLoadRooms; + int fNumLoadingRooms; // Number of rooms we're waiting for load callbacks on + std::vector fRoomsLoading; // the locations we are currently in the middle of loading + + int fNumPostLoadMsgs; + float fPostLoadMsgInc; + + void ICompleteInit (); + void IOnAsyncInitComplete (); + void IHandlePreloaderMsg (plPreloaderMsg * msg); + void IHandleNetCommAuthMsg (plNetCommAuthMsg * msg); + bool IHandleAgeLoaded2Msg (plAgeLoaded2Msg * msg); + + hsBool IFlushRenderRequests(); + void IProcessPreRenderRequests(); + void IProcessPostRenderRequests(); + void IProcessRenderRequests(hsTArray& reqs); + void IAddRenderRequest(plRenderRequest* req); + + hsBool IPlayIntroBink(const char* movieName, hsScalar endDelay, hsScalar posX, hsScalar posY, hsScalar scaleX, hsScalar scaleY, hsScalar volume = 1.0); + hsBool IHandleMovieMsg(plMovieMsg* mov); + void IKillMovies(); + void IServiceMovies(); + + void IStartProgress( const char *title, hsScalar len ); + void IIncProgress( hsScalar byHowMuch, const char *text ); + void IStopProgress( void ); + + static void IDispatchMsgReceiveCallback(); + static void IReadKeyedObjCallback(plKey key); + static void IProgressMgrCallbackProc( plOperationProgress *progress ); + + void IPatchGlobalAgeFiles( void ); + + int IFindRoomByLoc(const plLocation& loc); + bool IIsRoomLoading(const plLocation& loc); + void IQueueRoomLoad(const std::vector& locs, bool hold); + void ILoadNextRoom(); + void IUnloadRooms(const std::vector& locs); + void IRoomLoaded(plSceneNode* node, bool hold); + void IRoomUnloaded(plSceneNode* node); + void ISetGraphicsDefaults(); + +public: + + plClient(); + virtual ~plClient(); + + CLASSNAME_REGISTER( plClient ); + GETINTERFACE_ANY( plClient, hsKeyedObject ); + + static plClient* GetInstance() { return fInstance; } + static void SetInstance(plClient* v) { fInstance=v; } + + virtual hsBool MsgReceive(plMessage* msg); + + hsBool InitPipeline(); + + void InitInputs(); + + void InitDLLs(); + void ShutdownDLLs(); + + void InitAuxInits(); + + virtual hsBool StartInit(); + virtual hsBool Shutdown(); + virtual hsBool MainLoop(); + + plClient& SetDone(hsBool done) { fDone = done; return *this; } + hsBool GetDone() { return fDone; } + + // Set this to true to queue any room load requests that come in. Set it to false to process them. + void SetHoldLoadRequests(bool hold); + + enum + { + kFlagIniting, + kFlagDBGDisableRender, + kFlagDBGDisableRRequests, + kFlagAsyncInitComplete, + kFlagGlobalDataLoaded, + }; + + hsBool HasFlag(int f) const { return fFlags.IsBitSet(f); } + void SetFlag(int f, hsBool on=true) { fFlags.SetBit(f, on); } + + virtual plClient& SetWindowHandle(hsWindowHndl hndl) { fWindowHndl=hndl; return *this; } + hsWindowHndl GetWindowHandle() { return fWindowHndl; } + + plInputManager* GetInputManager() { return fInputManager; } + + plPipeline* GetPipeline() { return fPipeline; } + + plSceneNode* GetCurrentScene() { return fCurrentNode; } + + pfConsoleEngine *GetConsoleEngine() { return fConsoleEngine; } + + void SetAuxInitDir(const char *p) { delete [] fpAuxInitDir; fpAuxInitDir = hsStrcpy(p); } + + static void EnableClientDelay() { plClient::fDelayMS = true; } + + // These are a hack to let the console fake a lesser capabile board and test out quality settings. + // They should go away once we have this built into ClientSetup et.al. + void SetClampCap(int c) { fClampCap = c; } + int GetClampCap() const { return fClampCap; } + void SetQuality(int q) { fQuality = q; } + int GetQuality() const { return fQuality; } + + hsBool GetQuitIntro() const { return fQuitIntro; } + void SetQuitIntro(hsBool on) { fQuitIntro = on; } + + void SetClearColor( hsColorRGBA &color ); + hsColorRGBA GetClearColor() const { return fClearColor; } + + // The client window has focus (true) or lost it (false) + virtual void WindowActivate(bool active); + virtual hsBool WindowActive() const { return fWindowActive; } + + void SetMessagePumpProc( plMessagePumpProc proc ) { fMessagePumpProc = proc; } + void ResetDisplayDevice(int Width, int Height, int ColorDepth, hsBool Windowed, int NumAASamples, int MaxAnisotropicSamples, hsBool VSync = false, hsBool windowOnly = false); + void IDetectAudioVideoSettings(); + void IWriteDefaultGraphicsSettings(const wchar* destFile); + + plAnimDebugList *fAnimDebugList; + +#if 0 + std::string fUsername; + std::string fPasswordDigest; + std::string fServer; + int fPlayerID; + bool fRecreatePlayer; + bool fAuthPassed; +#endif +}; + +#endif // plClient_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClientCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClientCreatable.h new file mode 100644 index 00000000..baf0c0c6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClientCreatable.h @@ -0,0 +1,36 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plClientCreatable_inc +#define plClientCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plClient.h" + +REGISTER_NONCREATABLE( plClient ); + +#endif // plClientCreatable diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClientUpdateFormat.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClientUpdateFormat.h new file mode 100644 index 00000000..345ef7c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plClientUpdateFormat.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plClientUpdateFormat_h_inc +#define plClientUpdateFormat_h_inc + +namespace ClientUpdate +{ + enum + { + kUpdate, + kShutdown, + }; +} + +// +// Format of the update stream +// +// UInt8 - type (kUpdate, kShutdown) +// +// If type is kUpdate: +// UInt32 - number of deleted keys +// plUoid - uoid of deleted key (* num) +// +// UInt32 - number of new creatables +// plCreatable - new creatable (* num) +// + +#endif // plClientUpdateFormat_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plSimStateMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plSimStateMsg.h new file mode 100644 index 00000000..501500de --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/plSimStateMsg.h @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/pnAllCreatables.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/pnAllCreatables.cpp new file mode 100644 index 00000000..5a6cef6f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/pnAllCreatables.cpp @@ -0,0 +1,28 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#pragma warning( disable : 4305 4503 4018 4786 4284) +#include "pnAllCreatables.h" + \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/Dirt.ICO b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/Dirt.ICO new file mode 100644 index 0000000000000000000000000000000000000000..43e0da3f0bf65ca9907679a1b10ed350fdfeee8f GIT binary patch literal 2238 zcmd5-eOS|F7rtSPfxy_t93KM|OcpwXk-mZ-AOag>K_&=-f)dPLk&g*XL{SjKBn^fL zs99vZg%l_tDvBsbW=V?SAQb#86`2)%`K{{!y>^0Pynz+}!~e5fBptL_`1% z4uCQqP^$oACSXP(4GaNSS0joKEK>p*>AREdlYd@zO1Fysqi7P1UZ zFWJMI@;OZ8!rhUcoY;Ml-|=Lkn95mb5aqOA4=>QCQ5d*6?^__H37v8k}L z1LXnMW*T8)s1L02K7p~@blR{XX~zK9+znG_W8K(-gMt7{wl_SyHzOo^Clc4zBd_cvwpE|Qp5{x~_r(n~p1+0W-n$47 z2bL^mf$u5;C%Xy;R18Z_68P4Em>W`rr72a&c(Vyj+FNjyNRhZ4m_LsRTL(W_+XcbP zuMpA7-6*L#kBX*?*xzvjo)W4jhlKE40>hc1uyb&O_na(DpS=}q%gKmHZpMbPb~KzF z#L@2Wapulb#8O|_+LG=f7lbW+1HOt<_{dj5ncV@+hn-Nwq`-*!ZDk6;Hl|X!MS!C! zfjRX|e(_%HpuOd3T9CHB9=@_Q$SkPEs;#FHo%Sg#`A)F4my;I7P(-N_r!J=X0xXD$ zhkSlC_Uvm!>Hbr26atHssW^D%Dk={4!r3(#)Bnumabg z>5-ZWv|Jc~shI^2=&MvJVXc5rRn;5j1@EE7`{zIOnI zhK9&5t)zSdXzIKPjfTE;bJGz`s51XYW?;xLVj9!B$C#PJf^GTS(l{J0i^t=cTk%Zz ztg!-X8$O*^e;UWuPH6AoIBvX?^Mn_;6J10v@?8I9!7-dDgit6FiY84KyNRc`dss?_ z`|@}5d}-{Iscyn)|C&C@%i4R!OpjT!eXM-{%hKmP=LNCfTnKCh{sDnfsVqnytYD5F ztK{@@Na!o#d9Th#Song7$c0hSix$Vk%9bpR`#nIMv%T0wD0mHm`1l0n!ey`jdwJrD z6%!<*{G6oZl*y@cRj*N9)8f-J)T8{W%+;g3Z&sFgsu*kH*J6QteD*r^vh_L36Jv8{ z@`riGhOE4ees6f^Z+dgHe?h?7>;&ao>vOhj&3#)?NPLm0d9p~X(iCSTYzr&dp6wo= zt}J~$M-OM-%Kv=$(YC6+)gPCAQd7Hce;n8nNtswx zSKkn&Rv&0=ICv;MY{7xUnwqA%BS)J-2gk7%o8yWTt!*dUKRxx?>C(@?IJ1WMjxW!i z>pcI}F`z5yf;OOAUf=e?shawphN!Q%p;vRU?rdjg=cT$~dAj;8X!|dBUkR)`(sMQF z+Vw*>zWMgzrJJ{I58P=Sk&iJbaqZX12ky#sI^CiuWrAQ+rluzPE7<_uGs9N&-SE~a*{v2xR=`}>0xjk7L44k}fSuIqW1!KcsqXWu(4>uJr2?0TLWd1FXI z{riLOH8x+BNB#{Bum;&$tv^1yEdN`Ygu?Lseg6^N82@As|B3tyN9W@6_5ck1H5lky z3AqHiJ)aSt5FQYQ2-gU$gj@mz`~&pPpn1kM0{Jn>Kj#UBdq&U?BbU(1((At|qMTPp G^8N+{d0p}V literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/Microphone.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/Microphone.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c1e2ad0068272a7921dcedb042f492ae6a83a94e GIT binary patch literal 630 zcmZuvOKa3n6h4|UF^#B25kwGO_*@N~%#3wr5lqYw#f{bn#q)6H^eKp+FT>Fzhv6w%gtl!#x4Q~6 zXMe%5lgluB!UX&unlS(~|5oZEY9|o>Nr3XX>Fy1oWCY4R#1vZ(l?kB%H&LgGiPwy@ zNCXcAq;Pu!LvUZ~0#a8;$uSMZf2;yx;b@yN3bwJz_B&WT*%_p`0^L5_}u#8p_m;H@`DV7P2p?UfHcIJ${|%m#9b5_mU2p zykc+W`42TFup#M(YQ52;)jYrAQ9t}375$@*|MMG+C3V$g+fi))!(eo zTTg~E8e(2t7~+7f_zbW)*+xTi(Sby$JD3U@JQH2Jh@Z+0)ZuoB8)$)C$SdH$i&1|F V`@3|CzoAZxQ)^k}vi?mL{%@=Ju0;R< literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/Speaker.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/Speaker.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1b49e354c1087ebbccf3490e2d9470838d04f91e GIT binary patch literal 630 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 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bitmap1.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bitmap1.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c851b15ef9ddb047020011e8bbea504ba08041cb GIT binary patch literal 630 zcmZutF&4rw2t}XomCY+SdJcE*(y6+Yt!!SYN9vYt_#u6-q)Gze2Lhiwp2L|!2G~3F zw&{s3@E}{ff^3Bdn?&jX3zz13#_0izO=j*vvHZ%-e_?6wSzD~yB=9e89w;OgKsC+< zIK}?Dla;-n(gfy3Hjb=3zR9(=P~ig9trR`MtXn^3T7U(8Rj$iu)?4@>28?=3#fLaK z(fz%XW9gzk>Qzom(Z7$s_Y6Y2Oql3pCC?v8$;AA!Uw}mOUtr#1_EC}?aUH!$NWfj$ F@E40Ya=ZWl literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bitmap2.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bitmap2.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3540155b665f910235b246d0dc8c1828c7034d4b GIT binary patch literal 630 zcmZutyAi`63{`TuL!DPhnK{yD7gtU?)0r+Sc_cdADG%_0GC@CZ z?)$mW4HjgNSD>{g!Y1$Q1r3wdb;X?nQZ|WX3kNH^vN{*`*3ZG>5KIF9V^a_yiU4e^ z30TEW%|()aZ^6%%gXa(N99tzPc-+>o+rAXFJvG9ilqfi|SO6 zRIAIEf5_$v#KkVvP2GD18#}LAI)a>%h(}(nHbtql!il~0uxk$Iu39S9h`>&lC;N$t zg&>U3Cf>8z8YTHS0Yu}w-ew3f7=Fb%8i2hYZTHp!L4Cu(s~qiwE7HVHqyc!5 ztnS+{?5F@%)h^vpnt3CctAzFJ=hK0-nHf_Fm}L@)m4Hj+)77`T#C}P~ZT*ZlrGM>* wGGF}|$7z1RAUPiN{+;svQ1dp&IMfheLp2UCEkeJL5gj-%Q zl}a^iun3sxusz+a289sL>+C*ya!bv6YtvtO@KX0a8i)>ApY=2}1lfPgzt0HUxogq} KAMlBAJBXy%wvDGX)HhU(F}X}K_=}je$T{eOnUfcZO^P+t UpZwpuLQ~p0ZSXaJr~jUQ0P)ObcmMzZ literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bmp00004.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bmp00004.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9b2857b5d5bd23fbc9f6934fcaecb3947da12368 GIT binary patch literal 2166 zcmeHH%W(rC3{_@whz}mxa2c80(~i$uLay7*mOujxEzrP@@+5v*FT|OggBK8b(vuM2 zkI(xXX}e>*p}&{@La&g}3Z55sT!`?|vg8RPMB26uvjD6gF^hzbpQtc|{}zHbMetX% zD55iE^67+oXLKW+EzaivWOU9N*HArxXJcF8;G5DVI`fB63A$nPRiWyu-4Vqq12)m4 zi4yox?!`_BTpHz;4L~qw7ZXbM7z(SI4yR!BRdnp;(Lv8N-8?8{Yqdy~(augzU`{}s z@Y<#XUd||tPUU!SKF8NELANHs}J0Z9S)hRN*mwCpb9SMJAZJR|4ztAPd3V9ma8ZwBOqh Bs@4Di literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bmp00005.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/bmp00005.bmp new file mode 100644 index 0000000000000000000000000000000000000000..28c52b980439c3492ef1e3fce9d8fd20e92c5fac GIT binary patch literal 630 zcmd6jI}!sS2!=@}GhQQQ&XGQMv0Ck*bC2b%D9P`-v#?2@3CiOGWYp(txzZ*J>;rn= z^+Gp9ki9-3S|P$Fk?CL&Qtusp6l~jMHX<^gi_ZtI?BckNZr~J?14Xq$g|42IpR5^TLf$DX5m@4|$#pkxaR%9XeUEU3o_OXdD{LMfH1s;a?`Kff@_ z1h3W+;U(wN0SlM@Eo{Z0>)!Xz@HXcJsFt_X8#YDcH2*6H}i;&KXKgDcO%E}?3X*iWo2$- PBa08VP5YA~0{P}2awn%( literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/book_cli.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/book_cli.bmp new file mode 100644 index 0000000000000000000000000000000000000000..150c1e2ff01d08cfc3f87eb421920bf74537bc6d GIT binary patch literal 630 zcmbu6F%H5o3`Gq>YB#RH$T`@#3lqvxC0nvoxl(V@E)mB_7i7NOmb6F=2sd`}?Bv%` zF4y~>)b5C9_-XbX9k7DU^$FI|5n+&X(}aj6bzO&93ao>eWd+A4DTMUZ2Fs=`RUoq| z>~ovM7mE$Z7Pf(TjLlL`+%xCLv+x6YYyPNzp0}!B^m_fj$^~g_fEao+522hQ<*Ufu+c THFL;~G~U}V^-qdO%rE}}gx07G literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/book_hig.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/book_hig.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a0c29ab5d0767bc98980954d2255d02991c55fa7 GIT binary patch literal 630 zcmbu6F%H5o3`Gq>YB#RH$T`@#3lqvxIa{(+xl(V@E)mCQ7i7NOmbOR?2sd`}>^S{V zF4y~>%zm-QCXRij!ds4`elkR2difXbXQ!tC literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cnsl1.bin b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cnsl1.bin new file mode 100644 index 00000000..772705ce --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cnsl1.bin @@ -0,0 +1 @@ +=GQK[V4KUæ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_clicked.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_clicked.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6ccdf94bc0fea3da1a0d2a1894e090c9bc825559 GIT binary patch literal 630 zcmdT^DH&dO-~pO>oQeX v4~ek8RF_1@m%RS0f4>iV3>=(sS5*owbxsJFm1yqGP)_KoaOId*(qN3H^bt5%%t S{ftoWc?0^p00+Lw@8AREvvXqr literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_down.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_down.bmp new file mode 100644 index 0000000000000000000000000000000000000000..90b25795db4c5381b25d0118e4b94861d29c3d96 GIT binary patch literal 630 zcmd5&F%H5o3^WL--tq-T-oegan5u5c7B;?=C$W{yoSjOsQ6*N~_~Nr``;_Z#K9boC zyui}tDVm9X!Lm-7oV=H;Py(p?69*rZx`2uLr@kGKEDzx!>JCHs7~{Yxi` N74m{$=zol|k3N2dMi2l1 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_grab.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_grab.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3540155b665f910235b246d0dc8c1828c7034d4b GIT binary patch literal 630 zcmZutyAi`63{`TuL!DPhnK{yD7gtU?)0r+Sc_cdADG%_0GC@CZ z?)$mW4HjgNSD>{g!Y1$Q1r3wdb;X?nQZ|WX3kNH^vN{*`*3ZG>5KIF9V^a_yiU4e^ z30TEW%|()aZ^6%%gXa(N99tzPc-+>o+rA;$Y)a8X!mjVCpZ7$LK{ES6h0{;KW@7!~I)n)`jo` D(!NHH literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_open.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_open.bmp new file mode 100644 index 0000000000000000000000000000000000000000..03f7efb564a6f8db9a48776fd08ab1fbb78e3f35 GIT binary patch literal 630 zcmah_F%kkX2wWX!j?OPw`VM>lVs+Y?&UAk1o@94+aUsbZ=*>t1*(E>-r}Mal$w+&o z9-3~jBcinXO1w<~H&UtxEg`09;;9$KHcC-MTLP7Ygh>Hu;(wqpmFmrU@A_bn=~PL4$(HLsNpX-RZ&KR zDGEVCniL!fA=BS`2d=v$RTbU4x4Z9~H}m~=-tK&{GgdoxM$%jEUO$ajdJ3Tb`B+N) zwOp<*oVnF%wbJUxO1IxXYcv{ZJRWOvb5mPeTbfKJnog(M-rm-1Hq-w8z7~sx=JUCB zcXzdXm)hG)ebBk`XXxAcFP&ds>;0Fn7#YJEg%Rtip&6Q?S=DmA#L*qy(H-5xi>G8ha7gTY{ENH7=-27|$1Ff>dU z3T8J5b?WSk+pMcY zS}-Uu6}$_VMrJfhJ=CA40IS0hJ+yjNc@9hNn*0dso~5W zj%>B-8rK_r(!s%j4i67?babTS<71tjo$2)SR2LT)y1cy9)zy`5Zfy zKAxQL9WUu_{RZlVdTmzg)@kbTFOIGoPU?mibQ>Fk1-kxlEj*Ntb2pIw*gb5#6bG&! wpL~w~1<&!m{`WHk^tVg$I zRTYI2SmO#4XV1M!o2&#jG1)J% z9xobdx|&X>n*NyT{`+TjIvw?TJ#BAqYiDOiySuyE+uPIr{=WMCzK)KLG#ZUG91b-Y z3^ab1Iy_8$(4}$~`gZwKm$$ci|K%%2MsZ4^#A0Y@hGuA1wOlN5bVqk|M|bqPt)Lh5 z!l0lRbQ%SEpa*&&1bQ+~#u?4os11K3kg>^>YW7qHgTY`h7z_;w27|$1Fc=Jmh6#hg zU@#aA217%K!C){L3({S``3(Cd+5am*bfC#b{H(!iwCaabId zmKHc14u`|xa5!43;BXim28W@h&f#zv9EQLvsap#1S^OBc^w#V^g?C|9OEumFrh;j~ zpukk{E=(4J6ub-G?K+w_I{pR!f`7fDf`1`51%?7cArJN1$Vxjs3N3ZLMsg7N1#AIF z;McCUa*%_7BPs_6YuC-DfF)oFAOgeo3cwO<3>F1Kz`~zG9PT7wiJFhbQs0Jp3urJT z3<*QRkT4_+?RA17U`QAehJb-E6xPF#FeD5K1EDGCFeD5KLjsWa2f>n!$s#9)Gj=$# z#ja~yZ}dsW$HzK3IniV?(dp@_E-o%~etxd2t1De!U+d=PMt662n$PEYczDqLuhhr0 zGrr?7*{$C|ZHU)KHEtcIwqBn9mVs`UjCNie1wm#jB3z%*bkL~jyv+ORVZf**& zQdcY2V`M&!*1AvH)|$B1{rCC5`E$HwuvKpro!;8{|Ii-?vqko(HyyvOY8l>~-Nzs= I?XwL01_G$&761SM literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_upward.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/cursor_upward.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3069371266c0ac7ec591e823fef01eb2f30a4949 GIT binary patch literal 630 zcmc&vI}XAy41FM^X3G^AIR`s;VXD}gt!!MWM`8<`d3F-wR!EFEvHkqF&zErzqmlAN zK33l0Kt!tXVtAPVexy_sB_Wn&q1J%dhlnCFpSo0hrCIXb1!iXE^l^mg2IWQ}6AiCs z7HH1mici&j)-T&yx%$EStEgo2ugwA;+)q-s!Tr7IA_l|85qQt+3nt#{bD~H;kAbir S2TYrJah1*jx2sCp~XNvNNJ1adY&Iz+Zx?{oN^{(;%t=QyW!V6TIn zJinL3v2gB$QB^Na?fc2OSIlql3jPj1xo1S@CUD0#fdn&6HX&l^wr#_#gv8H@=n9UZ zDaFS|^fymSmalYsq`1jLX!0Uqo=d1{z9&4CqpmoQlknFnp0M_Y{av`OKY}5Vxt0D* z;c}K}oQ-@17XvbTDIBT3jr?8+ue^kD|BSfPH2dXdD(S3D7P*Wnm=!Y$6g?GKW0$SK zQ5i80g-HL41-B%O^Y&Ks7UR1@v7Typ*ViIY!rBd*3sht06E#@|4W;|9Bl%TpV5`qm ximd_H)CU>teSM8ml} + + +plClient + + + + + + \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/plClient.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/plClient.rc new file mode 100644 index 00000000..8d834c1f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/plClient.rc @@ -0,0 +1,380 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\r\n" + "#pragma code_page(1252)\r\n" + "#endif //_WIN32\r\n" + "#endif\r\n" + "\r\n" + "CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST ""plClient.exe.manifest""\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_CURSOR_UP BITMAP "cursor_up.bmp" +IDB_CURSOR_DOWN BITMAP "cursor_down.bmp" +IDB_CURSOR_LEFT BITMAP "cursor_right.bmp" +IDB_CURSOR_RIGHT BITMAP "cursor_left.bmp" +IDB_CURSOR_OPEN BITMAP "cursor_open.bmp" +IDB_CURSOR_GRAB BITMAP "cursor_grab.bmp" +IDB_CURSOR_CLICKED BITMAP "cursor_clicked.bmp" +IDB_CURSOR_POISED BITMAP "cursor_poised.bmp" +IDB_CURSOR_ARROW BITMAP "cursor_u.bmp" +IDB_CURSOR_4WAYOPEN BITMAP "bitmap1.bmp" +IDB_CURSOR_UPDOWNCLOSED BITMAP "bmp00001.bmp" +IDB_CURSOR_UPDOWNOPEN BITMAP "bitmap2.bmp" +IDB_CURSOR_4WAYCLOSED BITMAP "bitmap3.bmp" +IDB_CURSOR_LEFTRIGHTCLOSED BITMAP "bmp00003.bmp" +IDB_CURSOR_LEFTRIGHTOPEN BITMAP "bmp00002.bmp" +IDB_MICROPHONE BITMAP "Microphone.bmp" +IDB_TALKING BITMAP "Speaker.bmp" +IDB_CURSOR_BOOK_HIGHLIGHT BITMAP "book_hig.bmp" +IDB_CURSOR_BOOK BITMAP "book.bmp" +IDB_CURSOR_BOOK_CLICKED BITMAP "book_cli.bmp" +IDB_CURSOR_DISABLED BITMAP "cursor_disabled.bmp" +IDB_CURSOR_HAND BITMAP "bmp00005.bmp" +IDB_CURSOR_UPWARD BITMAP "cursor_upward.bmp" +IDB_BANNER BITMAP "banner.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON_DIRT ICON "Dirt.ICO" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_LOADING DIALOGEX 0, 0, 161, 26 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_CENTER | + WS_POPUP | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + LTEXT "Starting URU. Please wait...",IDC_STARTING_TEXT,32,9, + 127,8 + ICON IDI_ICON_DIRT,IDC_STATIC,5,3,20,20 +END + +IDD_EXCEPTION DIALOGEX 0, 0, 296, 183 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Error" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,239,7,50,14 + ICON IDI_ICON_DIRT,IDC_STATIC,7,7,20,20 + LTEXT " URU has experienced a serious error. Please report the information below.\n\nWe apologize for any inconvenience. ", + IDC_MSG,38,7,189,37 + EDITTEXT IDC_CRASHINFO,7,45,282,131,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Copy",IDC_COPY,239,26,50,14 +END + +IDD_URUTAP_LOGIN DIALOGEX 0, 0, 188, 111 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION +CAPTION "Enter Login Info" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Enter",IDOK,33,90,50,14 + PUSHBUTTON "Cancel",IDCANCEL,107,90,50,14 + LTEXT "Username",IDC_STATIC,7,7,38,11 + EDITTEXT IDC_URUTAP_USERNAME,52,7,129,13,ES_AUTOHSCROLL + LTEXT "Password",IDC_STATIC,7,30,38,12 + EDITTEXT IDC_URUTAP_PASSWORD,52,28,129,12,ES_PASSWORD | + ES_AUTOHSCROLL + LTEXT "Server",IDC_STATIC,7,54,35,12 + EDITTEXT IDC_URUTAP_SERVER,51,51,130,13,ES_AUTOHSCROLL + CONTROL "Destroy and recreate player",IDC_URUTAP_DESTROY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,51,70,130,12 +END + +IDD_URU_LOGIN DIALOGEX 0, 0, 200, 133 +STYLE DS_SETFONT | DS_3DLOOK | WS_POPUP | WS_SYSMENU +EXSTYLE WS_EX_STATICEDGE +FONT 12, "Hobo Std", 500, 0, 0x0 +BEGIN + DEFPUSHBUTTON "Submit",IDOK,139,114,50,14 + PUSHBUTTON "Quit",IDCANCEL,77,114,50,14 + EDITTEXT IDC_USERNAME,65,55,110,12,ES_AUTOHSCROLL + EDITTEXT IDC_PASSWORD,65,71,110,12,ES_PASSWORD | ES_AUTOHSCROLL + LTEXT "Username:",IDC_TEXT_USER,25,58,36,8 + LTEXT "Password:",IDC_TEXT_PASS,27,73,34,8 + LTEXT "Welcome to URU!",IDC_TEXT_WELCOME,72,45,57,8 + CONTROL "Remember Password:",IDC_REMEMBER_PASS,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,23,87,85,10 + GROUPBOX "",IDC_BOX_01,5,30,190,79 + GROUPBOX "",IDC_BOX_02,14,38,172,64 + CONTROL 151,IDC_IMAGE_BANNER,"Static",SS_BITMAP | + SS_REALSIZEIMAGE | WS_BORDER,4,4,192,21,WS_EX_CLIENTEDGE +END + +IDD_URU_EULA DIALOGEX 0, 0, 201, 158 +STYLE DS_SETFONT | WS_POPUP | WS_SYSMENU +FONT 12, "Hobo Std", 500, 0, 0x0 +BEGIN + DEFPUSHBUTTON "Accept",IDOK,141,138,50,14 + PUSHBUTTON "Decline",IDCANCEL,79,138,50,14 + CONTROL 151,IDC_IMAGE_BANNER,"Static",SS_BITMAP | + SS_REALSIZEIMAGE | WS_BORDER,4,4,192,21,WS_EX_CLIENTEDGE + GROUPBOX "",IDC_BOX_03,5,30,190,103 + LTEXT "Terms of Use Agreement",IDC_TEXT_TERMS,59,36,83,8 + CONTROL "Custom1",IDC_DYN_EULA,"",WS_TABSTOP,13,46,174,80 +END + +IDD_URULOGIN_MAIN DIALOGEX 0, 0, 302, 230 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | + DS_CENTER | WS_POPUP +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Login",IDOK,87,209,50,14 + PUSHBUTTON "Quit",IDCANCEL,163,209,50,14 + CONTROL 151,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE | + WS_BORDER,7,7,289,36 + LTEXT "Account name:",IDC_STATIC,69,119,49,10 + LTEXT "Password:",IDC_STATIC,69,135,49,10 + EDITTEXT IDC_URULOGIN_USERNAME,123,119,108,12,ES_AUTOHSCROLL + EDITTEXT IDC_URULOGIN_PASSWORD,123,135,108,12,ES_PASSWORD | + ES_AUTOHSCROLL + GROUPBOX "",IDC_STATIC,14,99,274,96 + CONTROL "Remember Password:",IDC_URULOGIN_REMEMBERPASS,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,69,151,100,10 + GROUPBOX "",IDC_STATIC,14,54,274,43 + LTEXT "Welcome to URU",IDC_STATUS_TEXT,17,62,267,20 + RTEXT "Product String",IDC_PRODUCTSTRING,88,86,194,8 + CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME | SS_SUNKEN,7,49, + 288,152 + PUSHBUTTON "Need an account?",IDC_URULOGIN_GAMETAPLINK,95,175,110, + 14,BS_FLAT +END + +IDD_URULOGIN_EULA DIALOGEX 0, 0, 300, 318 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Accept",IDOK,89,297,50,14 + PUSHBUTTON "Decline",IDCANCEL,160,297,50,14 + CONTROL 151,IDC_STATIC,"Static",SS_BITMAP,7,7,287,36, + WS_EX_CLIENTEDGE + CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME | SS_SUNKEN,9,46, + 284,242 + CTEXT "End User License Agreement",IDC_STATIC,46,55,207,10 + GROUPBOX "",IDC_STATIC,17,66,266,212 + CONTROL "",IDC_URULOGIN_EULATEXT,"RichEdit20A",ES_MULTILINE | + ES_AUTOHSCROLL | ES_READONLY | WS_BORDER | WS_VSCROLL | + WS_HSCROLL | WS_TABSTOP,25,75,250,197 +END + +IDD_AUTHFAILED DIALOGEX 0, 0, 177, 89 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_CENTER | + WS_POPUP | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + LTEXT "Authentication failed. Please try again.", + IDC_AUTH_TEXT,38,17,127,8 + ICON IDI_ICON_DIRT,IDC_STATIC,6,11,21,20 + PUSHBUTTON "Ok",IDOK,67,73,42,14 + CTEXT "",IDC_AUTH_MESSAGE,0,37,175,32 +END + +IDD_AUTHENTICATING DIALOGEX 0, 0, 177, 60 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_CENTER | + WS_POPUP | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + ICON IDI_ICON_DIRT,IDC_STATIC,6,11,21,20 + LTEXT "Logging in to URU. Please wait...",IDC_AUTH_TEXT,38,17, + 137,8 + PUSHBUTTON "Cancel",IDCANCEL,64,42,49,15 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_LOADING, DIALOG + BEGIN + RIGHTMARGIN, 159 + END + + IDD_EXCEPTION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 289 + TOPMARGIN, 7 + BOTTOMMARGIN, 176 + END + + IDD_URUTAP_LOGIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 181 + TOPMARGIN, 7 + BOTTOMMARGIN, 104 + END + + IDD_URU_LOGIN, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 168 + TOPMARGIN, 7 + BOTTOMMARGIN, 128 + END + + IDD_URU_EULA, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 194 + TOPMARGIN, 7 + BOTTOMMARGIN, 151 + END + + IDD_URULOGIN_MAIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 295 + TOPMARGIN, 7 + BOTTOMMARGIN, 223 + END + + IDD_URULOGIN_EULA, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 293 + TOPMARGIN, 7 + BOTTOMMARGIN, 311 + END + + IDD_AUTHFAILED, DIALOG + BEGIN + RIGHTMARGIN, 175 + BOTTOMMARGIN, 87 + END + + IDD_AUTHENTICATING, DIALOG + BEGIN + RIGHTMARGIN, 175 + BOTTOMMARGIN, 57 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// CNSL +// + +IDR_CNSL1 CNSL "cnsl1.bin" + +///////////////////////////////////////////////////////////////////////////// +// +// JPEG +// + +IDR_LOADING_01 JPEG "xLoading_Linking.01.jpg" +IDR_LOADING_02 JPEG "xLoading_Linking.02.jpg" +IDR_LOADING_03 JPEG "xLoading_Linking.03.jpg" +IDR_LOADING_04 JPEG "xLoading_Linking.04.jpg" +IDR_LOADING_05 JPEG "xLoading_Linking.05.jpg" +IDR_LOADING_06 JPEG "xLoading_Linking.06.jpg" +IDR_LOADING_07 JPEG "xLoading_Linking.07.jpg" +IDR_LOADING_08 JPEG "xLoading_Linking.08.jpg" +IDR_LOADING_09 JPEG "xLoading_Linking.09.jpg" +IDR_LOADING_10 JPEG "xLoading_Linking.10.jpg" +IDR_LOADING_11 JPEG "xLoading_Linking.11.jpg" +IDR_LOADING_12 JPEG "xLoading_Linking.12.jpg" +IDR_LOADING_13 JPEG "xLoading_Linking.13.jpg" +IDR_LOADING_14 JPEG "xLoading_Linking.14.jpg" +IDR_LOADING_15 JPEG "xLoading_Linking.15.jpg" +IDR_LOADING_16 JPEG "xLoading_Linking.16.jpg" +IDR_LOADING_17 JPEG "xLoading_Linking.17.jpg" +IDR_LOADING_18 JPEG "xLoading_Linking.18.jpg" +IDR_LOADING_LINKTEXT JPEG "xLoading_Linking_Text.jpg" +IDR_LOADING_UPDATETEXT JPEG "xLoading_Updating_Text.jpg" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 +#endif + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "plClient.exe.manifest" +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/resource.h new file mode 100644 index 00000000..63f30b0b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/resource.h @@ -0,0 +1,103 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by plClient.rc +// +#define IDB_CURSOR_UP 112 +#define IDB_CURSOR_DOWN 113 +#define IDB_CURSOR_RIGHT 114 +#define IDB_CURSOR_LEFT 115 +#define IDB_CURSOR_OPEN 116 +#define IDB_CURSOR_GRAB 117 +#define IDB_CURSOR_CLICKED 118 +#define IDB_CURSOR_POISED 119 +#define IDB_MICROPHONE 121 +#define IDB_TALKING 122 +#define IDB_CURSOR_ARROW 123 +#define IDB_CURSOR_4WAYOPEN 124 +#define IDB_CURSOR_UPDOWNCLOSED 125 +#define IDB_CURSOR_UPDOWNOPEN 126 +#define IDB_CURSOR_4WAYCLOSED 127 +#define IDB_CURSOR_LEFTRIGHTCLOSED 128 +#define IDB_CURSOR_LEFTRIGHTOPEN 129 +#define IDI_ICON_DIRT 135 +#define IDD_LOADING 136 +#define IDD_EXCEPTION 138 +#define IDR_CNSL1 139 +#define IDB_CURSOR_BOOK_HIGHLIGHT 141 +#define IDB_CURSOR_BOOK 142 +#define IDB_CURSOR_BOOK_CLICKED 143 +#define IDB_CURSOR_DISABLED 144 +#define IDB_CURSOR_HAND 145 +#define IDB_CURSOR_UPWARD 147 +#define IDD_URUTAP_LOGIN 148 +#define IDD_URU_LOGIN 149 +#define IDB_BANNER 151 +#define IDD_URU_EULA 152 +#define IDD_URULOGIN_MAIN 153 +#define IDD_URULOGIN_EULA 154 +#define IDD_AUTHFAILED 155 +#define IDD_AUTHENTICATING 156 +#define IDR_LOADING_01 177 +#define IDR_LOADING_02 178 +#define IDR_LOADING_03 179 +#define IDR_LOADING_04 180 +#define IDR_LOADING_05 181 +#define IDR_LOADING_06 182 +#define IDR_LOADING_07 183 +#define IDR_LOADING_08 184 +#define IDR_LOADING_09 185 +#define IDR_LOADING_10 186 +#define IDR_LOADING_11 187 +#define IDR_LOADING_12 188 +#define IDR_LOADING_13 189 +#define IDR_LOADING_14 190 +#define IDR_LOADING_15 191 +#define IDR_LOADING_16 192 +#define IDR_LOADING_17 193 +#define IDR_LOADING_18 194 +#define IDR_LOADING_LINKTEXT 195 +#define IDR_LOADING_UPDATETEXT 196 +#define IDC_CRASHINFO 1001 +#define IDC_COPY 1002 +#define IDC_MSG 1003 +#define IDC_STARTING_TEXT 1004 +#define IDC_URUTAP_USERNAME 1005 +#define IDC_URUTAP_PASSWORD 1006 +#define IDC_URUTAP_SERVER 1008 +#define IDC_URUTAP_DESTROY 1009 +#define IDC_USERNAME 1010 +#define IDC_PASSWORD 1011 +#define IDC_REMEMBER_PASS 1012 +#define IDC_IMAGE_BANNER 1013 +#define IDC_BOX_01 1014 +#define IDC_BOX_02 1015 +#define IDC_TEXT_WELCOME 1016 +#define IDC_TEXT_USER 1017 +#define IDC_TEXT_PASS 1018 +#define IDC_CUSTOM1 1019 +#define IDC_DYN_EULA 1019 +#define IDC_BOX_03 1020 +#define IDC_TEXT_TERMS 1021 +#define IDC_URULOGIN_REMEMBERPASS 1022 +#define IDC_URULOGIN_USERNAME 1023 +#define IDC_URULOGIN_PASSWORD 1024 +#define IDC_URULOGIN_EULATEXT 1025 +#define IDC_RICHEDIT22 1028 +#define IDC_AUTH_TEXT 1029 +#define IDC_BUTTON1 1030 +#define IDC_URULOGIN_GAMETAPLINK 1030 +#define IDC_AUTH_MESSAGE 1031 +#define IDC_PRODUCTSTRING 1033 +#define IDC_STATUS_TEXT 1034 +#define IDC_HOTKEY1 1035 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 197 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1036 +#define _APS_NEXT_SYMED_VALUE 103 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.01.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.01.jpg new file mode 100644 index 0000000000000000000000000000000000000000..204ab4aabfe370d4c309162e3845a382920f5aba GIT binary patch literal 6506 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 Koqy%acmD?0wqzp! literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.02.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.02.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7a5dca29a0ab6f023e24219592bbc115d84a2ab3 GIT binary patch literal 6493 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 R-(8*2;BRhs1;O~y{{h@Zg?9h| literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.03.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.03.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4e7ed7421ab64139a568ba279f0ec1d188591e86 GIT binary patch literal 6464 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 cj;ZtYaxmJT33+(@X(f|Me literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.05.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.05.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ffc63e0f038a64c66eb9109d4ff428adc4182369 GIT binary patch literal 6437 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 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.06.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.06.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ea4af1110c9a43279f99b2a602d1cd666e65cd83 GIT binary patch literal 6566 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 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.07.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.07.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a71c70ec5020d9f8591049a34b5f2165588dd091 GIT binary patch literal 6414 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 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.09.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.09.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f64abe9a8d9fa59d1c72020842b2cf5cd63e8bf5 GIT binary patch literal 6440 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 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.10.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.10.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0723892fbdb9db29f8be40e2f5c746bdc855c3ea GIT binary patch literal 6501 zcmbVQ2V7H2vp*0Z^oS&gbmV>8bFXDf`k%4iWKR+M|z2XR7Fub zA|f56D5xM(U+})~e)rw)d%ssE=by7@&+g97oY~o#J)Jt81I}rwX{rH4#6*Aw;Rl?a z5D^1^p-Mv8ncE5=A^A%sp?FsRj({)%BpR)OfeTi^IR-1BmT*_PXho>~gHBIRLw=Wt z2xAlItehZf{O4$$I4j_&gwUsy(EqZ{@1}qM3-Yu4d)4pfGv2wh06=YHOcEOk{2sGb z1M%nBcL_W%5!%cVxSsJQGzD_S{>l679R~qf_W_bK{GkCt+fIOvoCqKSKt!AXF&zO#yvW4E%f~MuC?P3zRr;FD^&6^c>Kd9_28KomV-r&|q`iZqle3Gfw~wzM+CL!h zLBzwzs7KM5q{qoAscBErGx7@xi;7E1%gUeC)i*RYHMg|B=mPXeYGib5d}4Cy z{q+38;?nZU>e}b^op0ZN?C$;C|8;Pt<$ut>$U{evhnR!}L;^mOhlm(W0Emu+^pY6a zd1XDYttUO_l`wLsN@54m^UcjZZO(CkvkF}c{Qilv!$8_l9%e%>9oJoB2Q~~J!2!zk^l~u#6>m@I~ z4cz1ZtYS>u0N96u=gkgEA{w-r*riT}M?}8HD^))EFz9QGO@5%=+ph+;gmkka+{Cv6 zqHlj(;Jj}KPxKLMF=lj-<{H7PDaJ5F7m%3nT!>-REevYu4Tu;?Uk+fez#*}L6O}ee zOGv*pUv`L`GIIV{%T!WHr4^_q^UJ_4kaf5%(6~5`PPLPVU8jr9Sm7@-V#4w%Tj%Pj z!k&;q^^{nzb{_|R=spT_$$c)L6cAj#S)Jm|zc=n}XEN~3xVfdjU zwpL*lpXOa?@kEl`>>@BU@uR#xqcQZ~E8nVY`xNMG)c>q!y`IR^lXNstl8pFfe@c zB9|8n9p?S04|gVO<-MA?lJF1R7y8!XDjymKW{=OM_#K))A4?YA9UJ9etDG?s%-Bs) zs|;&<2-Pzo=g-Fge+T?i&_xM)fA=2KdIy9k`R)GasLt|_sZ{~R6~b|^Ya`d`YH8e5 zYUf$9GIt-vxCcjSq3!L)4YY`;|&%rgbU@5ER9Jt2c_GAywnu*GUjf1pCf@#*D?V49*_GE;NsH{_J#o9wi5MlRk3-L~FBcW0EO%5i=3TPA2l z@E6W!pFI4NZj;?|xC?nVnKMW~eS3I-QuCsq(x$2hTOjQg1UY+jeE}s5(Mi%QjvX+J zP`$=I8TLjX+bJ=WbP5NWOZhPvtSe7b`9mYhgw1D%uIR2`4*dk zT1fYhNg`WG&Azr)3a%IXI}pZq2kw__)b z=1lyGW(s1rl#e#bUdn4*@Rz~E)zfMscga4xC?4<=Ib z($bZQkS>?1dAxGS@<_&1#zFn{Pui``+tum(Aiv z&`{jyJJEjF2B!}n73Y1jmK@gW$@OWYUhMD2w8FIrv+<-#;pVbI_iMS`19xBRXgIoB zYBI}ybGvRwH-9fFs>OEL#-Sk!!%tSD^$<)xBJg2=Q#>?oLYtz>pr ze+==`J=`fkWT*gnA8^8%;Uygo!j^G_2#xDJG7drQ`2MnPW0;CMB7tmVtcNe>AHBbM z1GO*1E~1L)zao2ee)ItG(O{V4Pzvk#RE&{tJ)k0`LDD&wuwIQ~;j%rbHkGzLy`x^| zan>OBVUfFv%h@iHxlp0idm`X|Ari30GYZyg)kI(2xxL=X6=A*l+WMJ;prIje>5ewu zqT;Kg1JA!C9qaTI%K155uyy4Wcx$TowLov!+ruM%~Sls0+I(F{Z1(R#F zNBG`V&s#j0%$h7v>(<|XfPB1nSMeNpAMwMtJt#6-fzI+*AbuAoeZG?g;;T4^O4@mo zHYI0TcB8;G9UUsqoU>sT0&zcixv)X(Z;HMVGIt$23G-MaO-o$h=hN%ihP=^aAB2U# z*wt8R3Cpxv?`-oX^=oDCHf^Y)@fcY~I_c}nIZ5^!D;N2%8Vzrf7G;%l=&FRb$4(^A z82oG`vDKkyN?!@p;n{bP9|fpXg>sL*z~5l2Of(%i6{daNT>gC9E3NBr zgIKLKthz>zb7?y%K=dO&`TImt6QiCgv{Nv^K9OSE5J)wGBO$y%z5 zc~7*`8q}{n?zYS>5bmq^*f@A6d)R*T$3}BM1sAV+0X25nf(`GnvtRLd{rV=WXRo4v zCGdkLg7nF-+anX>_YppkLXgg!WeddzHs2-VdOTU~e7Zi;28o&El(oD(EK20~n61+$ zMBL(MSl%gsZFzLE4q3Hq&^Lk_-@x2^stwgMr#|B&_`FcO_#d40dx|;#~xZ%j(kS&^n#&hedmvm;FL--5V6L12VF0k z4F)pVckyQjYVly{#^PRse?C5#yuKZ|q=_nbEwzQ|mvwuMRV7p02}PQibZD?I^!dCY*~(U&P3{`%#MPjr6VXA!6r~ zm*}QLWscGF4J%Pa_>H&J(M;j5WH5^x#bYNNu4aw#K8J}HQaj0fSrXvsRf1mDGt5m& z`53qusgEM0>i+n`@iosB^;z|I^vCe*>QD31O<2n$RGDqe;4S{d@TCOxixq`6++hsQ z;g-K=!xF;ZkuTrM?H zV#exa1t!$q;!-2#uHtHPc}OAWFC1s!3|`WSVmROAt?` z8RXMvbSUkD9CoD6p)qwPOzukm=9=pD%1LF5AK0y%BEJrsPJ!pWe0%tKQ4!%&;499` zneuU1ynv^{)+d{mbxiqZ^_nJVBe~R4X)qkmGVRE zWq7jPjlTZ!ct{2kxWb*d68v3UqrZ(M<90bllXNpsX?#e&Hm37QZ8IS5-+>eL9&TmZhj`4SR{Qg|VNaJ=Y z&JXuF~!k`Fq@yHfN@;_jz&!uMY; z9IGn5sEY#bg`x;wexbAJv1L5U)Hdz)Ez!8JE`Zk4Le=osIcI^8w=v=E(M{BH*Qlwybd>qEI`aU>$7ilF@HVqcBqIyU0crLi=G_*Ap`fc?Wq1GLQxt&pg+Mn46v z=v|I5$`r(XOfaf$|7qFhx-}K)rKXJKtN;g5t@zz~0RiP#hcvLnQ7VF%vBpaf~881qX9-N&QS^2^1O{cHl|v3>WNw?#HN641^Tc}MXm zugQfkaeiVU9^Mp+vq}r?UYKd=@|e;uZ@sZ*+xMHgrII&8BAs8bk5L9?6K~HJW>#$# zSeyc^^@Sl8#t~zwKMl+pH%#rLqx`4Jjs+(K6fyj0c}Q;uO=MKb%iyA!mvUm>-1?<6 z73<_1nE7!`tw0w`xU6x(k<3np>qJfdexx3EY!Na*=U`mkFvkpY3e1xXin9tH&{HT1 z-Y&d?y%k@-tf?(vpp4oKnA>=RR-oIzBZ*glZ|SdSTuOVg5UsXit*@iiP;zuB>vaj@ zd4vk&bpo*o_EvU^NRRc!;>WHTB2h(j1LX|r5vM@8-mvm+J~ZHhq-rjY+fcX&k-x_+ zrAdb2M#cCQ{g7&bB$O3oSXyl)F>1S;(fa^obxD3~QoD7`JW25b?5R6Q z-?_|-P`9G2?Sid$Q0`jF&|KV_HOX(GHSJEIV8vOWdv0#X65_K#-VCDzeuVFuIsVRP z9#VMxG|L-8V)^LzPQu5P0ax$#qbS=*MU0*T6-i3Wgwut0)SEUw3z!2~yLb`4=KXBn zOm8gG=EQgYgntw{;=3_u9aD;@s1IzM3ki?$YcW zA#~vQXF^FxETyuXz_6luyo|iDMO{PIR=g&_H;o_LM{7%UI7#`P=vg*TiDPEtzy`KO z-m9Tn$FmB_h@9y9{d&CLw~4Wn^{bbH@fBBa$pMxTe-55BF~9kRD+AQ9X!9zVymT#> z_9(~-f5_YALZ0a^*GVvAdbq{<&m=8`=4!$hw*o#%GknQ>b(Dwg{Jwjta;xTpRQVt!7l0xQXarfM%;FT6;P;p{pvzcy!y!Vz_w6xOYL)O z@y6dIjPo`(X-qJG*V|up_`7UcKYICV*CDLEj;K%1TlYin>RT2h=pO0YiRY2S5}Cp5 zwTk5?Z<{g$ZO@fY)Z@?YZ z@ap~O+z+lYq`lqc+}ku8yw?^R{GS>w3epbWi|lDa>swpAl<#&=QWNSQ8#C0i`0$6Z z{O3sod{_>=L-s)vXHH`wqn0kfzlyzO{B+p(wma<_LsX4_%z|GIX-t0st~pzkLzI!q zN}F2T+TzYbHq$~yUo}lh7&?S}VrIi2{n7V`kT&pcuk*@=Ca?Su;uPp^jZZuOGT2E% zEIW3v*U5NTW;HM%F9_NG5XV0vfxq6XVnb@|oG97Hb~q zx;ARt7TEW#p4aHU$Tq&3tY>Oiy7Hy>>@WiOdl(E8t{RNb@4(-%>@9xf=z4?-OF z%2XHK;lQ`2YT&{PcMm3lvp5&l8MC&GZHn+$Tm`|bidt?>&1~-j=Hy`#yx7gPV(Fh0 zX&xX5;{kqGBVQB$t9;v$2fta&$D1R-bZ`8{Q0^X*BU`i@CcU;c$8 zp4fzEY~r`~6CH|8T@#99xo#EPn;d|q>kVsY?>0tp#f@~s7d5(?iUd}?W z!OhU?$N(Wv$Q&3m$j6I$wTD*WX=zRU=9s#!ma4Y?+RIgtzwm6Y{GR|+>V`*z%VLbZ uw$ga8W!DP-aXOVB3gdQ;M>-m;mp~&@ui0ttO8nul{$=42oEZD*`~Lvea#lwG literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.11.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.11.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ba36ed190593b9973e098c055e1726f45ca8f2fc GIT binary patch literal 6450 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 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.12.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.12.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8fcaea0f1b707053fde589666c98873b08b9ebc0 GIT binary patch literal 6510 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 Mksto$%X%>UUnjO>s{jB1 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.13.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.13.jpg new file mode 100644 index 0000000000000000000000000000000000000000..61dbd92a20e853db800d457591a091262119a7fe GIT binary patch literal 6393 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 C2OUfR literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.14.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.14.jpg new file mode 100644 index 0000000000000000000000000000000000000000..13a7146408647b626fe07a83f4896c8d000b6228 GIT binary patch literal 6547 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 UsueuW)cqsaod>+X1OM^A06V5JkpKVy literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.16.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.16.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bf85fe391c51d9223083ddc4d55a4ce55fe1baff GIT binary patch literal 6495 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 RhF*Xc?Akx8nJE#c{tMzUOr8J$ literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.18.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Linking.18.jpg new file mode 100644 index 0000000000000000000000000000000000000000..997543340eb50bbb8784045fcc52c4302b68ac6b GIT binary patch literal 6519 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 W>Sxg?Pxi*x?>H0~jAy#5G2XF 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 UY%Kh7u&HWgS1s#Z#K=GY3)Ez+sQ>@~ literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Updating_Text.jpg b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClient/res/xLoading_Updating_Text.jpg new file mode 100644 index 0000000000000000000000000000000000000000..90c02ed4da057509c99ff24cb8b7e14b947f1184 GIT binary patch literal 30050 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. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include +#include // windows directory handling fxns (for chdir) +#include + +//#define DETACH_EXE // Microsoft trick to force loading of exe to memory +#ifdef DETACH_EXE + #include "dmdfm.h" // Windows Load EXE into memory suff +#endif + + +#include "HeadSpin.h" +#include "hsStream.h" +#include "hsUtils.h" +#include "plClient.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetClient/plNetLinkingMgr.h" +#include "../plInputCore/plInputManager.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "plPipeline.h" +#include "../plResMgr/plResManager.h" +#include "../plResMgr/plLocalization.h" +#include "../plFile/plEncryptedStream.h" + +#include "../plStatusLog/plStatusLog.h" +#include "../pnProduct/pnProduct.h" +#include "../plNetGameLib/plNetGameLib.h" +#include "../plFile/plFileUtils.h" + +#include "../plPhysX/plSimulationMgr.h" + +#include "res\resource.h" + +#include +#include "WinHttp.h" +// +// Defines +// + +#define CLASSNAME "Plasma" // Used in WinInit() +#define PARABLE_NORMAL_EXIT 0 // i.e. exited WinMain normally + +#define TIMER_UNITS_PER_SEC (float)1e3 +#define UPDATE_STATUSMSG_SECONDS 30 +#define WM_USER_SETSTATUSMSG WM_USER+1 + +#if BUILD_TYPE == BUILD_TYPE_DEV + #define STATUS_PATH L"www2.cyanworlds.com" +#else + #define STATUS_PATH L"support.cyanworlds.com" +#endif + +// +// Globals +// +hsBool gHasMouse = false; + +extern hsBool gDataServerLocal; +extern hsBool gUseBackgroundDownloader; + +enum +{ + kArgToDni, + kArgSkipLoginDialog, + kArgAuthSrv, + kArgFileSrv, + kArgGateKeeperSrv, + kArgLocalData, + kArgBackgroundDownloader, +}; + +static const CmdArgDef s_cmdLineArgs[] = { + { kCmdArgFlagged | kCmdTypeBool, L"ToDni", kArgToDni }, + { kCmdArgFlagged | kCmdTypeBool, L"SkipLoginDialog", kArgSkipLoginDialog }, + { kCmdArgFlagged | kCmdTypeString, L"AuthSrv", kArgAuthSrv }, + { kCmdArgFlagged | kCmdTypeString, L"FileSrv", kArgFileSrv }, + { kCmdArgFlagged | kCmdTypeString, L"GateKeeperSrv", kArgGateKeeperSrv }, + { kCmdArgFlagged | kCmdTypeBool, L"LocalData", kArgLocalData }, + { kCmdArgFlagged | kCmdTypeBool, L"BGDownload", kArgBackgroundDownloader }, +}; + +/// Made globals now, so we can set them to zero if we take the border and +/// caption styles out ala fullscreen (8.11.2000 mcn) +int gWinBorderDX = GetSystemMetrics( SM_CXSIZEFRAME ); +int gWinBorderDY = GetSystemMetrics( SM_CYSIZEFRAME ); +int gWinMenuDY = GetSystemMetrics( SM_CYCAPTION ); + +//#include "global.h" +plClient *gClient; +bool gPendingActivate = false; +bool gPendingActivateFlag = false; + +static bool s_loginDlgRunning = false; +static CEvent s_statusEvent(kEventManualReset); + +FILE *errFP = nil; +HINSTANCE gHInst = NULL; // Instance of this app + +static const unsigned AUTH_LOGIN_TIMER = 1; +static const unsigned AUTH_FAILED_TIMER = 2; + +#define FAKE_PASS_STRING "********" + +//============================================================================ +// External patcher file +//============================================================================ +#ifdef PLASMA_EXTERNAL_RELEASE + +static wchar s_patcherExeName[] = L"UruLauncher.exe"; + +//============================================================================ +// Internal patcher file +//============================================================================ +#else + +static wchar s_patcherExeName[] = L"plUruLauncher.exe"; + +#endif // PLASMA_EXTERNAL_RELEASE + +//============================================================================ +// PhysX installer +//============================================================================ +static wchar s_physXSetupExeName[] = L"PhysX_Setup.exe"; + +//============================================================================ +// TRANSGAMING detection & dialog replacement +//============================================================================ +typedef BOOL (WINAPI *IsTransgaming) (void); +typedef const char * (WINAPI *TGGetOS) (void); +typedef LPVOID (WINAPI *TGLaunchUNIXApp) (const char *pPath, const char *pMode); +typedef BOOL (WINAPI *TGUNIXAppReadLine) (LPVOID pApp, char *pBuf, int bufSize); +typedef BOOL (WINAPI *TGUNIXAppWriteLine) (LPVOID pApp, const char *pLine); +typedef BOOL (WINAPI *TGUNIXAppClose) (LPVOID pApp); + +static bool TGIsCider = false; +static TGLaunchUNIXApp pTGLaunchUNIXApp; +static TGUNIXAppReadLine pTGUNIXAppReadLine; +static TGUNIXAppWriteLine pTGUNIXAppWriteLine; +static TGUNIXAppClose pTGUNIXAppClose; + +#define TG_NEW_LOGIN_PATH "C:\\Program Files\\Uru Live\\Cider\\URU Live Login.app" +#define TG_NEW_LOGIN_POPEN_PATH "/transgaming/c_drive/Program Files/Uru Live/Cider/URU Live Login.app/Contents/MacOS/URU Live Login" +#define TG_OLD_LOGIN_POPEN_PATH "/URU Live Login.app/Contents/MacOS/URU Live Login" + +#define TG_NEW_EULA_PATH "C:\\Program Files\\Uru Live\\Cider\\URU Live EULA.app" +#define TG_NEW_EULA_POPEN_PATH "/transgaming/c_drive/Program Files/Uru Live/Cider/URU Live EULA.app/Contents/MacOS/URU Live EULA" +#define TG_OLD_EULA_POPEN_PATH "/URU Live EULA.app/Contents/MacOS/URU Live EULA" + +//============================================================================ +// LoginDialogParam +//============================================================================ +struct LoginDialogParam { + bool fromGT; + ENetError authError; + wchar accountName[kMaxAccountNameLength]; +}; + +bool AuthenticateNetClientComm(ENetError* result, HWND parentWnd); +bool IsExpired(); +void GetCryptKey(UInt32* cryptKey, unsigned size); +static void SaveUserPass (char *username, char *password, ShaDigest *pNamePassHash, bool remember_password, + bool fromGT); +static void LoadUserPass (const wchar *accountName, char *username, ShaDigest *pNamePassHash, bool *pRemember, + bool fromGT, int *pFocus); +static void AuthFailedStrings (ENetError authError, bool fromGT, + const char **ppStr1, const char **ppStr2, + const wchar **ppWStr); + +#if 0 +// For networking +const GUID NEXUS_GUID = { + 0x5bfdb060, 0x6a4, 0x11d0, 0x9c, 0x4f, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e}; +#endif + + +// Detect whether we're running under TRANSGAMING Cider +//============================================================================== +static void TGDoCiderDetection () +{ + HMODULE hMod = GetModuleHandle ("ntdll"); + if (!hMod) + return; + + IsTransgaming pIsTg = (IsTransgaming)GetProcAddress (hMod, "IsTransgaming"); + if (!pIsTg || !pIsTg ()) + return; + + TGGetOS pTGOS = (TGGetOS)GetProcAddress (hMod, "TGGetOS"); + const char *pOS = NULL; + if (pTGOS) + pOS = pTGOS (); + if (!pOS || strcmp (pOS, "MacOSX")) + return; + + TGIsCider = true; + pTGLaunchUNIXApp = (TGLaunchUNIXApp)GetProcAddress (hMod, "TGLaunchUNIXApp"); + pTGUNIXAppReadLine = (TGUNIXAppReadLine)GetProcAddress (hMod, "TGUNIXAppReadLine"); + pTGUNIXAppWriteLine = (TGUNIXAppWriteLine)GetProcAddress (hMod, "TGUNIXAppWriteLine"); + pTGUNIXAppClose = (TGUNIXAppClose)GetProcAddress (hMod, "TGUNIXAppClose"); +} + +static bool TGRunLoginDialog (const wchar *accountName, bool fromGT) +{ + ShaDigest NamePassHash; + char Username[kMaxAccountNameLength + 5]; + int Focus; + bool bRemember = false; + + LoadUserPass (accountName, Username, &NamePassHash, &bRemember, fromGT, &Focus); + + while (true) + { + LPVOID pApp; + if (GetFileAttributes (TG_NEW_LOGIN_PATH) != INVALID_FILE_ATTRIBUTES) + pApp = pTGLaunchUNIXApp (TG_NEW_LOGIN_POPEN_PATH, "r+"); + else + pApp = pTGLaunchUNIXApp (TG_OLD_LOGIN_POPEN_PATH, "r+"); + + if (!pApp) + { + hsMessageBox ("Incomplete or corrupted installation!\nUnable to locate Login dialog", + "Error", hsMessageBoxNormal); + return false; + } + + // Send user/pwd/remember + pTGUNIXAppWriteLine (pApp, Username); + if (bRemember) + pTGUNIXAppWriteLine (pApp, FAKE_PASS_STRING); + else + pTGUNIXAppWriteLine (pApp, ""); + if (bRemember) + pTGUNIXAppWriteLine (pApp, "y"); + else + pTGUNIXAppWriteLine (pApp, "n"); + + if (!pTGUNIXAppReadLine (pApp, Username, sizeof (Username))) + { + pTGUNIXAppClose (pApp); + hsMessageBox ("Incomplete or corrupted installation!\nUnable to locate Login dialog", + "Error", hsMessageBoxNormal); + return false; + } + + // Check if user selected 'Cancel' + if (StrCmp (Username, "text:", 5) != 0) + { + pTGUNIXAppClose (pApp); + return false; + } + memmove (Username, Username + 5, StrLen (Username) - 5); + Username[StrLen (Username) - 5] = '\0'; + + char Password[kMaxPasswordLength]; + if (!pTGUNIXAppReadLine (pApp, Password, sizeof (Password))) + { + pTGUNIXAppClose (pApp); + hsMessageBox ("Incomplete or corrupted installation!\nLogin dialog not found or working", + "Error", hsMessageBoxNormal); + return false; + } + + char Remember[16]; + if (!pTGUNIXAppReadLine (pApp, Remember, sizeof (Remember))) + { + pTGUNIXAppClose (pApp); + hsMessageBox ("Incomplete or corrupted installation!\nLogin dialog not found or working", + "Error", hsMessageBoxNormal); + return false; + } + + pTGUNIXAppClose (pApp); + + bRemember = false; + if (Remember[0] == 'y') + bRemember = true; + + SaveUserPass (Username, Password, &NamePassHash, bRemember, fromGT); + + // Do login & see if it failed + ENetError auth; + bool cancelled = AuthenticateNetClientComm(&auth, NULL); + + if (IS_NET_SUCCESS (auth) && !cancelled) + break; + + if (!cancelled) + { + const char *pStr1, *pStr2; + const wchar *pWStr; + unsigned int Len; + char *pTmpStr; + + AuthFailedStrings (auth, fromGT, &pStr1, &pStr2, &pWStr); + + Len = StrLen (pStr1) + 1; + if (pStr2) + Len += StrLen (pStr2) + 2; + if (pWStr) + Len += StrLen (pWStr) + 2; + + pTmpStr = TRACKED_NEW char[Len]; + StrCopy (pTmpStr, pStr1, StrLen (pStr1)); + if (pStr2) + { + StrCopy (pTmpStr + StrLen (pTmpStr), "\n\n", 2); + StrCopy (pTmpStr + StrLen (pTmpStr), pStr2, StrLen (pStr2)); + } + if (pWStr) + { + StrCopy (pTmpStr + StrLen (pTmpStr), "\n\n", 2); + StrToAnsi (pTmpStr + StrLen (pTmpStr), pWStr, StrLen (pWStr)); + } + + hsMessageBox (pTmpStr, "Error", hsMessageBoxNormal); + delete [] pTmpStr; + } + else + NetCommDisconnect(); + }; + + return true; +} + +bool TGRunTOSDialog () +{ + char Buf[16]; + LPVOID pApp; + + if (GetFileAttributes (TG_NEW_EULA_PATH) != INVALID_FILE_ATTRIBUTES) + pApp = pTGLaunchUNIXApp (TG_NEW_EULA_POPEN_PATH, "r"); + else + pApp = pTGLaunchUNIXApp (TG_OLD_EULA_POPEN_PATH, "r"); + + if (!pApp) + { + hsMessageBox ("Incomplete or corrupted installation!\nTOS dialog not found or working", + "Error", hsMessageBoxNormal); + return false; + } + + if (!pTGUNIXAppReadLine (pApp, Buf, sizeof (Buf))) + { + hsMessageBox ("Incomplete or corrupted installation!\nTOS dialog not found or working", + "Error", hsMessageBoxNormal); + pTGUNIXAppClose (pApp); + return false; + } + + pTGUNIXAppClose (pApp); + + return (StrCmp (Buf, "accepted") == 0); +} + +void GetMouseCoords(HWND hWnd, WPARAM wParam, LPARAM lParam, int* xPos, int* yPos, int* fwKeys) +{ + POINT pt; + pt.x=LOWORD(lParam); + pt.y=HIWORD(lParam); +#if 0 + if (ClientToScreen(hWnd, &pt) == false) + HSDebugProc("Error converting client mouse coords to screen"); +#endif + + if (xPos) + *xPos = pt.x; // horizontal position of cursor + if (yPos) + *yPos = pt.y; // vertical position of cursor + +#if 0 + char str[128]; + sprintf(str, "mx=%d my=%d\n", pt.x, pt.y); + hsStatusMessage(str); +#endif + + if (fwKeys) + *fwKeys = wParam; // key flags + + // key flag bits + // MK_CONTROL Set if the CTRL key is down. + // MK_LBUTTON Set if the left mouse button is down. + // MK_MBUTTON Set if the middle mouse button is down. + // MK_RBUTTON Set if the right mouse button is down. + // MK_SHIFT Set if the SHIFT key is down. +} + +void DebugMsgF(const char* format, ...); + +// Handles all the windows messages we might receive +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static bool gDragging = false; + static UInt32 keyState=0; + + // Handle messages + switch (message) { + case WM_KEYDOWN : + case WM_LBUTTONDOWN : + case WM_RBUTTONDOWN : + case WM_LBUTTONDBLCLK : // The left mouse button was double-clicked. + case WM_MBUTTONDBLCLK : // The middle mouse button was double-clicked. + case WM_MBUTTONDOWN : // The middle mouse button was pressed. + case WM_RBUTTONDBLCLK : // The right mouse button was double-clicked. + // If they did anything but move the mouse, quit any intro movie playing. + { + if( gClient ) + gClient->SetQuitIntro(true); + } + // Fall through to other events + case WM_KEYUP : + case WM_LBUTTONUP : + case WM_RBUTTONUP : + case WM_MBUTTONUP : // The middle mouse button was released. + case WM_MOUSEMOVE : + case 0x020A: // fuc&ing windows b.s... + { + if (gClient && gClient->WindowActive() && gClient->GetInputManager()) + { + gClient->GetInputManager()->HandleWin32ControlEvent(message, wParam, lParam, hWnd); + } + } + break; + +#if 0 + case WM_KILLFOCUS: + SetForegroundWindow(hWnd); + break; +#endif + + case WM_SYSKEYUP: + case WM_SYSKEYDOWN: + { + if (gClient && gClient->WindowActive() && gClient->GetInputManager()) + { + gClient->GetInputManager()->HandleWin32ControlEvent(message, wParam, lParam, hWnd); + } + //DefWindowProc(hWnd, message, wParam, lParam); + } + break; + + case WM_SYSCOMMAND: + switch (wParam) { + // Trap ALT so it doesn't pause the app + case SC_KEYMENU : + //// disable screensavers and monitor power downs too. + case SC_SCREENSAVE: + case SC_MONITORPOWER: + return 0; + case SC_CLOSE : + // kill game if window is closed + gClient->SetDone(TRUE); + if (plNetClientMgr * mgr = plNetClientMgr::GetInstance()) + mgr->QueueDisableNet(false, nil); + DestroyWindow(gClient->GetWindowHandle()); + break; + } + break; + + case WM_ACTIVATE: + { + bool active = (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE); + bool minimized = (HIWORD(wParam) != 0); + + DebugMsgF("Got WM_ACTIVATE, active=%s, minimized=%s, clicked=%s", + active ? "true" : "false", + minimized ? "true" : "false", + (LOWORD(wParam) == WA_CLICKACTIVE) ? "true" : "false"); + + if (gClient && !minimized && !gClient->GetDone()) + { + if (LOWORD(wParam) == WA_CLICKACTIVE) + { + // See if they've clicked on the frame, in which case they just want to + // move, not activate, us. + POINT pt; + GetCursorPos(&pt); + ScreenToClient(hWnd, &pt); + + RECT rect; + GetClientRect(hWnd, &rect); + + if( (pt.x < rect.left) + ||(pt.x >= rect.right) + ||(pt.y < rect.top) + ||(pt.y >= rect.bottom) ) + { + active = false; + } + } + gClient->WindowActivate(active); + } + else + { + gPendingActivate = true; + gPendingActivateFlag = active; + } + } + break; + + // Let go of the mouse if the window is being moved. + case WM_ENTERSIZEMOVE: + DebugMsgF("Got WM_ENTERSIZEMOVE%s", gClient ? "" : ", no client, ignoring"); + gDragging = true; + if( gClient ) + gClient->WindowActivate(false); + break; + + // Redo the mouse capture if the window gets moved + case WM_EXITSIZEMOVE: + DebugMsgF("Got WM_EXITSIZEMOVE%s", gClient ? "" : ", no client, ignoring"); + gDragging = false; + if( gClient ) + gClient->WindowActivate(true); + break; + + // Redo the mouse capture if the window gets moved (special case for Colin + // and his cool program that bumps windows out from under the taskbar) + case WM_MOVE: + if (!gDragging && gClient && gClient->GetInputManager()) + { + gClient->GetInputManager()->Activate(true); + DebugMsgF("Got WM_MOVE"); + } + else + DebugMsgF("Got WM_MOVE, but ignoring"); + break; + + /// Resize the window + // (we do WM_SIZING here instead of WM_SIZE because, for some reason, WM_SIZE is + // sent to the window when we do fullscreen, and what's more, it gets sent BEFORE + // the fullscreen flag is sent. How does *that* happen? Anyway, WM_SIZING acts + // just like WM_SIZE, except that it ONLY gets sent when the user changes the window + // size, not when the window is minimized or restored) + case WM_SIZING: + { + DebugMsgF("Got WM_SIZING"); + RECT r; + ::GetClientRect(hWnd, &r); + gClient->GetPipeline()->Resize(r.right - r.left, r.bottom - r.top); + } + break; + + case WM_SIZE: + // Let go of the mouse if the window is being minimized + if (wParam == SIZE_MINIMIZED) + { + DebugMsgF("Got WM_SIZE, SIZE_MINIMIZED%s", gClient ? "" : ", but no client, ignoring"); + if (gClient) + gClient->WindowActivate(false); + } + // Redo the mouse capture if the window gets restored + else if (wParam == SIZE_RESTORED) + { + DebugMsgF("Got WM_SIZE, SIZE_RESTORED%s", gClient ? "" : ", but no client, ignoring"); + if (gClient) + gClient->WindowActivate(true); + } + break; + + case WM_CLOSE: + gClient->SetDone(TRUE); + if (plNetClientMgr * mgr = plNetClientMgr::GetInstance()) + mgr->QueueDisableNet(false, nil); + DestroyWindow(gClient->GetWindowHandle()); + return TRUE; + case WM_DESTROY: + gClient->SetDone(TRUE); + if (plNetClientMgr * mgr = plNetClientMgr::GetInstance()) + mgr->QueueDisableNet(false, nil); + PostQuitMessage(0); + return TRUE; + case WM_CREATE: + // Create renderer + break; + } + return DefWindowProc(hWnd, message, wParam, lParam); +} + +void PumpMessageQueueProc( void ) +{ + MSG msg; + + // Look for a message + while (PeekMessage( &msg, NULL, 0, 0, PM_REMOVE )) + { + // Handle the message + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } +} + +void InitNetClientComm() +{ + NetCommStartup(); +} + +BOOL CALLBACK AuthDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + static bool* cancelled = NULL; + + switch( uMsg ) + { + case WM_INITDIALOG: + cancelled = (bool*)lParam; + SetTimer(hwndDlg, AUTH_LOGIN_TIMER, 10, NULL); + return TRUE; + + case WM_TIMER: + if (wParam == AUTH_LOGIN_TIMER) + { + if (NetCommIsLoginComplete()) + EndDialog(hwndDlg, 1); + else + NetCommUpdate(); + + return TRUE; + } + + return FALSE; + + case WM_NCHITTEST: + SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION); + return TRUE; + + case WM_DESTROY: + KillTimer(hwndDlg, AUTH_LOGIN_TIMER); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) + { + *cancelled = true; + EndDialog(hwndDlg, 1); + } + return TRUE; + } + + return DefWindowProc(hwndDlg, uMsg, wParam, lParam); +} + +bool AuthenticateNetClientComm(ENetError* result, HWND parentWnd) +{ + if (!NetCliAuthQueryConnected()) + NetCommConnect(); + + bool cancelled = false; + NetCommAuthenticate(nil); + + ::DialogBoxParam(gHInst, MAKEINTRESOURCE( IDD_AUTHENTICATING ), parentWnd, AuthDialogProc, (LPARAM)&cancelled); + + if (!cancelled) + *result = NetCommGetAuthResult(); + else + *result = kNetSuccess; + + return cancelled; +} + +void DeInitNetClientComm() +{ + NetCommShutdown(); +} + +BOOL CALLBACK WaitingForPhysXDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + switch( uMsg ) + { + case WM_INITDIALOG: + ::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "Waiting for PhysX install..."); + return true; + + } + return 0; +} + +bool InitPhysX() +{ + bool physXInstalled = false; + while (!physXInstalled) + { + plSimulationMgr::Init(); + if (!plSimulationMgr::GetInstance()) + { + int ret = hsMessageBox("PhysX is not installed, or an older version is installed.\nInstall new version? (Game will exit if you click \"No\")", + "Missing PhysX", hsMessageBoxYesNo); + if (ret == hsMBoxNo) // exit if no + return false; + + // launch the PhysX installer + STARTUPINFOW startupInfo; + PROCESS_INFORMATION processInfo; + ZERO(startupInfo); + ZERO(processInfo); + startupInfo.cb = sizeof(startupInfo); + if(!CreateProcessW(NULL, s_physXSetupExeName, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) + { + hsMessageBox("Failed to launch PhysX installer.\nPlease re-run URU to ensure you have the latest version.", "Error", hsMessageBoxNormal); + return false; + } + + // let the user know what's going on + HWND waitingDialog = ::CreateDialog(gHInst, MAKEINTRESOURCE(IDD_LOADING), NULL, WaitingForPhysXDialogProc); + + // run a loop to wait for it to quit, pumping the windows message queue intermittently + DWORD waitRet = WaitForSingleObject(processInfo.hProcess, 100); + MSG msg; + while (waitRet == WAIT_TIMEOUT) + { + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + waitRet = WaitForSingleObject(processInfo.hProcess, 100); + } + + // cleanup + CloseHandle(processInfo.hThread); + CloseHandle(processInfo.hProcess); + ::DestroyWindow(waitingDialog); + } + else + { + plSimulationMgr::GetInstance()->Suspend(); + physXInstalled = true; + } + } + return true; +} + +bool InitClient( HWND hWnd ) +{ + plResManager *resMgr = TRACKED_NEW plResManager; + resMgr->SetDataPath("dat"); + hsgResMgr::Init(resMgr); + gClient = TRACKED_NEW plClient; + if( gClient == nil ) + return false; + + if (!InitPhysX()) + return false; + + gClient->SetWindowHandle( hWnd ); + +#ifdef DETACH_EXE + hInstance = ((LPCREATESTRUCT) lParam)->hInstance; +#endif + // If in fullscreen mode, get rid of the window borders. Note: this won't take + // effect until the next SetWindowPos call + +#ifdef DETACH_EXE + + // This Function loads the EXE into Virtual memory...supposedly + HRESULT hr = DetachFromMedium(hInstance, DMDFM_ALWAYS | DMDFM_ALLPAGES); +#endif + + if( gClient->InitPipeline() ) + gClient->SetDone(true); + else + { + if( gClient->GetPipeline()->IsFullScreen() ) + { + SetWindowLong(hWnd, GWL_STYLE, WS_POPUP); + SetWindowLong(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST); + SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + gWinBorderDX = gWinBorderDY = gWinMenuDY = 0; + } + else { + SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPED | WS_CAPTION); + SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + + int goodWidth = gClient->GetPipeline()->Width() + gWinBorderDX * 2; + int goodHeight = gClient->GetPipeline()->Height() + gWinBorderDY * 2 + gWinMenuDY; + + SetWindowPos( + hWnd, + nil, + 0, + 0, + goodWidth, + goodHeight, + SWP_NOCOPYBITS + | SWP_NOMOVE + | SWP_NOOWNERZORDER + | SWP_NOZORDER + | SWP_FRAMECHANGED + ); + } + + if( gPendingActivate ) + { + // We need this because the window gets a WM_ACTIVATE before we get to this function, so + // the above flag lets us know that we need to fake a late activate msg to the client + gClient->WindowActivate( gPendingActivateFlag ); + } + + gClient->SetMessagePumpProc( PumpMessageQueueProc ); + + return true; +} + +// Initializes all that windows junk, creates class then shows main window +BOOL WinInit(HINSTANCE hInst, int nCmdShow) +{ + // Fill out WNDCLASS info + WNDCLASS wndClass; + wndClass.style = CS_DBLCLKS; // CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = WndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = hInst; + wndClass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_DIRT)); + + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = (struct HBRUSH__*) (GetStockObject(BLACK_BRUSH)); + wndClass.lpszMenuName = CLASSNAME; + wndClass.lpszClassName = CLASSNAME; + + // can only run one at a time anyway, so just quit if another is running + if (!RegisterClass(&wndClass)) + return FALSE; + + /// 8.11.2000 - Test for OpenGL fullscreen, and if so use no border, no caption; + /// else, use our normal styles + + char windowName[256]; + wchar productString[256]; +//#ifdef PLASMA_EXTERNAL_RELEASE +#if 0 // Show the full product string in external build window title until we roll it into the options dialog -eap + StrCopy(productString, ProductLongName(), arrsize(productString)); +#else + ProductString(productString, arrsize(productString)); +#endif + StrToAnsi(windowName, productString, arrsize(windowName)); + + // Create a window + HWND hWnd = CreateWindow( + CLASSNAME, windowName, + WS_OVERLAPPEDWINDOW, + 0, 0, + 800 + gWinBorderDX * 2, + 600 + gWinBorderDY * 2 + gWinMenuDY, + NULL, NULL, hInst, NULL + ); +// gClient->SetWindowHandle((hsWindowHndl) + + if( !InitClient( hWnd ) ) + return FALSE; + + // Return false if window creation failed + if (!gClient->GetWindowHandle()) + { + OutputDebugString("Create window failed\n"); + return FALSE; + } + else + { + OutputDebugString("Create window OK\n"); + } + return TRUE; +} + +// +// For error logging +// +static FILE* gDebugFile=NULL; +void DebugMessageProc(const char* msg) +{ + OutputDebugString(msg); + OutputDebugString("\n"); + if (gDebugFile != NULL) + { + fprintf(gDebugFile, "%s\n", msg); + fflush(gDebugFile); + } +} + +void DebugMsgF(const char* format, ...) +{ +#ifndef PLASMA_EXTERNAL_RELEASE + va_list args; + va_start(args, format); + + char buf[256]; + int numWritten = _vsnprintf(buf, sizeof(buf), format, args); + hsAssert(numWritten > 0, "Buffer too small"); + + va_end(args); + + DebugMessageProc(buf); +#endif +} + +static bool IsMachineLittleEndian() { + int i = 1; + char *p = (char *) &i; + if (p[0] == 1) // Lowest address contains the least significant byte + return true; + else + return false; +} + +inline static dword ToBigEndian (dword value) { + return ((value) << 24) | ((value & 0x0000ff00) << 8) | ((value & 0x00ff0000) >> 8) | ((value) >> 24); +} + +static void AuthFailedStrings (ENetError authError, bool fromGT, + const char **ppStr1, const char **ppStr2, + const wchar **ppWStr) +{ + *ppStr1 = NULL; + *ppStr2 = NULL; + *ppWStr = NULL; + + switch (plLocalization::GetLanguage()) + { + case plLocalization::kFrench: + case plLocalization::kGerman: + case plLocalization::kJapanese: + *ppStr1 = "Authentication Failed. Please try again."; + break; + + default: + *ppStr1 = "Authentication Failed. Please try again."; + + switch (authError) + { + case kNetErrAccountNotFound: + *ppStr2 = "Account Not Found."; + break; + case kNetErrAccountNotActivated: + *ppStr2 = "Account Not Activated."; + break; + case kNetErrConnectFailed: + *ppStr2 = "Unable to connect to Myst Online."; + break; + case kNetErrDisconnected: + *ppStr2 = "Disconnected from Myst Online."; + break; + case kNetErrAuthenticationFailed: + if (fromGT) + *ppStr2 = "GameTap authentication failed, please enter your GameTap username and password."; + else + *ppStr2 = "Incorrect password.\n\nMake sure CAPS LOCK is not on."; + break; + case kNetErrGTServerError: + case kNetErrGameTapConnectionFailed: + *ppStr2 = "Unable to connect to GameTap, please try again in a few minutes."; + break; + case kNetErrAccountBanned: + *ppStr2 = "Your account has been banned from accessing Myst Online. If you are unsure as to why this happened please contact customer support."; + break; + default: + *ppWStr = NetErrorToString (authError); + break; + } + break; + } +} + + +BOOL CALLBACK AuthFailedDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + switch( uMsg ) + { + case WM_INITDIALOG: + { + LoginDialogParam* loginParam = (LoginDialogParam*)lParam; + const char *pStr1, *pStr2; + const wchar *pWStr; + + AuthFailedStrings (loginParam->authError, loginParam->fromGT, + &pStr1, &pStr2, &pWStr); + + if (pStr1) + ::SetDlgItemText( hwndDlg, IDC_AUTH_TEXT, pStr1); + if (pStr2) + ::SetDlgItemText( hwndDlg, IDC_AUTH_MESSAGE, pStr2); + if (pWStr) + ::SetDlgItemTextW( hwndDlg, IDC_AUTH_MESSAGE, pWStr); + } + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK) + { + EndDialog(hwndDlg, 1); + } + return TRUE; + + case WM_NCHITTEST: + SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION); + return TRUE; + + } + return FALSE; +} + +BOOL CALLBACK UruTOSDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + switch( uMsg ) + { + case WM_INITDIALOG: + { + SetWindowText(hwndDlg, "End User License Agreement"); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon((HINSTANCE)lParam, MAKEINTRESOURCE(IDI_ICON_DIRT))); + + hsUNIXStream stream; + if (stream.Open("TOS.txt", "rt")) + { + char* eulaData = NULL; + unsigned dataLen = stream.GetSizeLeft(); + + eulaData = TRACKED_NEW char[dataLen + 1]; + ZeroMemory(eulaData, dataLen + 1); + + stream.Read(dataLen, eulaData); + + SetDlgItemText(hwndDlg, IDC_URULOGIN_EULATEXT, eulaData); + delete [] eulaData; + } + + break; + } + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) + { + bool ok = (LOWORD(wParam) == IDOK); + EndDialog(hwndDlg, ok); + return TRUE; + } + break; + + case WM_NCHITTEST: + SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION); + return TRUE; + } + return FALSE; +} + +static void SaveUserPass (char *username, char *password, ShaDigest *pNamePassHash, bool remember_password, + bool fromGT) +{ + UInt32 cryptKey[4]; + ZeroMemory(cryptKey, sizeof(cryptKey)); + GetCryptKey(cryptKey, arrsize(cryptKey)); + + wchar wusername[kMaxAccountNameLength]; + wchar wpassword[kMaxPasswordLength]; + + StrToUnicode(wusername, username, arrsize(wusername)); + + // if the password field is the fake string then we've already + // loaded the namePassHash from the file + if (StrCmp(password, FAKE_PASS_STRING) != 0) + { + StrToUnicode(wpassword, password, arrsize(wpassword)); + + wchar domain[15]; + PathSplitEmail(wusername, nil, 0, domain, arrsize(domain), nil, 0, nil, 0, 0); + + if (StrLen(domain) == 0 || StrCmpI(domain, L"gametap") == 0) { + CryptDigest( + kCryptSha1, + pNamePassHash, + StrLen(password) * sizeof(password[0]), + password + ); + + if (IsMachineLittleEndian()) { + pNamePassHash->data[0] = ToBigEndian(pNamePassHash->data[0]); + pNamePassHash->data[1] = ToBigEndian(pNamePassHash->data[1]); + pNamePassHash->data[2] = ToBigEndian(pNamePassHash->data[2]); + pNamePassHash->data[3] = ToBigEndian(pNamePassHash->data[3]); + pNamePassHash->data[4] = ToBigEndian(pNamePassHash->data[4]); + } + } + else + CryptHashPassword(wusername, wpassword, pNamePassHash); + } + + NetCommSetAccountUsernamePassword(wusername, *pNamePassHash); + if (TGIsCider) + NetCommSetAuthTokenAndOS(nil, L"mac"); + else + NetCommSetAuthTokenAndOS(nil, L"win"); + + if (!fromGT) { + wchar fileAndPath[MAX_PATH]; + PathGetInitDirectory(fileAndPath, arrsize(fileAndPath)); + PathAddFilename(fileAndPath, fileAndPath, L"login.dat", arrsize(fileAndPath)); +#ifndef PLASMA_EXTERNAL_RELEASE + // internal builds can use the local init directory + wchar localFileAndPath[MAX_PATH]; + StrCopy(localFileAndPath, L"init\\login.dat", arrsize(localFileAndPath)); + if (PathDoesFileExist(localFileAndPath)) + StrCopy(fileAndPath, localFileAndPath, arrsize(localFileAndPath)); +#endif + hsStream* stream = plEncryptedStream::OpenEncryptedFileWrite(fileAndPath, cryptKey); + if (stream) + { + stream->Write(sizeof(cryptKey), cryptKey); + stream->WriteSafeString(username); + stream->Writebool(remember_password); + if (remember_password) + stream->Write(sizeof(pNamePassHash->data), pNamePassHash->data); + stream->Close(); + delete stream; + } + } +} + + +static void LoadUserPass (const wchar *accountName, char *username, ShaDigest *pNamePassHash, bool *pRemember, + bool fromGT, int *pFocus) +{ + UInt32 cryptKey[4]; + ZeroMemory(cryptKey, sizeof(cryptKey)); + GetCryptKey(cryptKey, arrsize(cryptKey)); + + char* temp; + *pRemember = false; + username[0] = '\0'; + + if (!fromGT) + { + wchar fileAndPath[MAX_PATH]; + PathGetInitDirectory(fileAndPath, arrsize(fileAndPath)); + PathAddFilename(fileAndPath, fileAndPath, L"login.dat", arrsize(fileAndPath)); +#ifndef PLASMA_EXTERNAL_RELEASE + // internal builds can use the local init directory + wchar localFileAndPath[MAX_PATH]; + StrCopy(localFileAndPath, L"init\\login.dat", arrsize(localFileAndPath)); + if (PathDoesFileExist(localFileAndPath)) + StrCopy(fileAndPath, localFileAndPath, arrsize(localFileAndPath)); +#endif + hsStream* stream = plEncryptedStream::OpenEncryptedFile(fileAndPath, true, cryptKey); + if (stream && !stream->AtEnd()) + { + UInt32 savedKey[4]; + stream->Read(sizeof(savedKey), savedKey); + + if (memcmp(cryptKey, savedKey, sizeof(savedKey)) == 0) + { + temp = stream->ReadSafeString(); + + if (temp) + { + StrCopy(username, temp, kMaxAccountNameLength); + delete temp; + } + else + username[0] = '\0'; + + *pRemember = stream->Readbool(); + + if (*pRemember) + { + stream->Read(sizeof(pNamePassHash->data), pNamePassHash->data); + *pFocus = IDOK; + } + else + *pFocus = IDC_URULOGIN_PASSWORD; + } + + stream->Close(); + delete stream; + } + } + else + { + StrToAnsi (username, accountName, kMaxAccountNameLength); + *pFocus = IDC_URULOGIN_PASSWORD; + } +} + +void StatusCallback(void *param) +{ + HWND hwnd = (HWND)param; + + while(s_loginDlgRunning) + { + // get status message from webpage and display in status area. + const wchar *path = BuildTypeServerStatusPath(); + if(path) + { + HINTERNET hSession = 0; + HINTERNET hConnect = 0; + HINTERNET hRequest = 0; + + hSession = WinHttpOpen( + L"UruClient/1.0", + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0 + ); + if(hSession) + { + HINTERNET hConnect = WinHttpConnect( hSession, STATUS_PATH, INTERNET_DEFAULT_HTTP_PORT, 0); + if(hConnect) + { + HINTERNET hRequest = WinHttpOpenRequest( + hConnect, + L"GET", + path, + NULL, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + 0 + ); + if(hRequest) + { + static char data[256] = {0}; + DWORD bytesRead; + WinHttpSendRequest( + hRequest, + WINHTTP_NO_ADDITIONAL_HEADERS, + 0, + WINHTTP_NO_REQUEST_DATA, + 0, + 0, + 0 + ); + WinHttpReceiveResponse(hRequest, 0); + WinHttpReadData(hRequest, data, 255, &bytesRead); + data[bytesRead] = 0; + if(bytesRead) + PostMessage(hwnd, WM_USER_SETSTATUSMSG, 0, (LPARAM) data); + } + } + } + WinHttpCloseHandle(hRequest); + WinHttpCloseHandle(hConnect); + WinHttpCloseHandle(hSession); + } + else + break; // no status message + + for(unsigned i = 0; i < UPDATE_STATUSMSG_SECONDS && s_loginDlgRunning; ++i) + { + Sleep(1000); + } + } + s_statusEvent.Signal(); +} + +BOOL CALLBACK UruLoginDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + static ShaDigest namePassHash; + static LoginDialogParam* loginParam; + static showAuthFailed = false; + + switch( uMsg ) + { + case WM_INITDIALOG: + { + s_loginDlgRunning = true; + _beginthread(StatusCallback, 0, hwndDlg); + loginParam = (LoginDialogParam*)lParam; + + SetWindowText(hwndDlg, "Login"); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(gHInst, MAKEINTRESOURCE(IDI_ICON_DIRT))); + + EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); + + char username[kMaxAccountNameLength]; + bool remember_password = false; + + int focus_control = IDC_URULOGIN_USERNAME; + + LoadUserPass (loginParam->accountName, username, &namePassHash, &remember_password, loginParam->fromGT, &focus_control); + + SetDlgItemText(hwndDlg, IDC_URULOGIN_USERNAME, username); + CheckDlgButton(hwndDlg, IDC_URULOGIN_REMEMBERPASS, remember_password ? BST_CHECKED : BST_UNCHECKED); + if (remember_password) + SetDlgItemText(hwndDlg, IDC_URULOGIN_PASSWORD, FAKE_PASS_STRING); + if (loginParam->fromGT) + EnableWindow(GetDlgItem(hwndDlg, IDC_URULOGIN_REMEMBERPASS), FALSE); + + SetFocus(GetDlgItem(hwndDlg, focus_control)); + + if (IS_NET_ERROR(loginParam->authError)) + { + showAuthFailed = true; + } + + char windowName[256]; + wchar productString[256]; + ProductString(productString, arrsize(productString)); + StrToAnsi(windowName, productString, arrsize(windowName)); + SendMessage(GetDlgItem(hwndDlg, IDC_PRODUCTSTRING), WM_SETTEXT, 0, (LPARAM) windowName); + + SetTimer(hwndDlg, AUTH_LOGIN_TIMER, 10, NULL); + return FALSE; + } + + case WM_USER_SETSTATUSMSG: + SendMessage(GetDlgItem(hwndDlg, IDC_STATUS_TEXT), WM_SETTEXT, 0, lParam); + return TRUE; + + case WM_DESTROY: + { + s_loginDlgRunning = false; + s_statusEvent.Wait(kEventWaitForever); + KillTimer(hwndDlg, AUTH_LOGIN_TIMER); + return TRUE; + } + + case WM_NCHITTEST: + { + SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION); + return TRUE; + } + + case WM_PAINT: + { + if (showAuthFailed) + { + SetTimer(hwndDlg, AUTH_FAILED_TIMER, 10, NULL); + showAuthFailed = false; + } + return FALSE; + } + + case WM_COMMAND: + { + if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) + { + bool ok = (LOWORD(wParam) == IDOK); + if (ok) + { + char username[kMaxAccountNameLength]; + char password[kMaxPasswordLength]; + bool remember_password = false; + + GetDlgItemText(hwndDlg, IDC_URULOGIN_USERNAME, username, kMaxAccountNameLength); + GetDlgItemText(hwndDlg, IDC_URULOGIN_PASSWORD, password, kMaxPasswordLength); + remember_password = (IsDlgButtonChecked(hwndDlg, IDC_URULOGIN_REMEMBERPASS) == BST_CHECKED); + + SaveUserPass (username, password, &namePassHash, remember_password, loginParam->fromGT); + + LoginDialogParam loginParam; + MemSet(&loginParam, 0, sizeof(loginParam)); + bool cancelled = AuthenticateNetClientComm(&loginParam.authError, hwndDlg); + + if (IS_NET_SUCCESS(loginParam.authError) && !cancelled) + EndDialog(hwndDlg, ok); + else { + if (!cancelled) + ::DialogBoxParam(gHInst, MAKEINTRESOURCE( IDD_AUTHFAILED ), hwndDlg, AuthFailedDialogProc, (LPARAM)&loginParam); + else + { + NetCommDisconnect(); + } + } + } + else + EndDialog(hwndDlg, ok); + + return TRUE; + } + else if (HIWORD(wParam) == EN_CHANGE && LOWORD(wParam) == IDC_URULOGIN_USERNAME) + { + char username[kMaxAccountNameLength]; + GetDlgItemText(hwndDlg, IDC_URULOGIN_USERNAME, username, kMaxAccountNameLength); + + if (StrLen(username) == 0) + EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); + else + EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE); + + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_URULOGIN_GAMETAPLINK) + { + ShellExecute(NULL, "open", "http://www.mystonline.com/signup.html", NULL, NULL, SW_SHOWNORMAL); + + return TRUE; + } + break; + } + + case WM_TIMER: + { + switch(wParam) + { + case AUTH_FAILED_TIMER: + KillTimer(hwndDlg, AUTH_FAILED_TIMER); + ::DialogBoxParam(gHInst, MAKEINTRESOURCE( IDD_AUTHFAILED ), hwndDlg, AuthFailedDialogProc, (LPARAM)loginParam); + return TRUE; + + case AUTH_LOGIN_TIMER: + NetCommUpdate(); + return TRUE; + } + return FALSE; + } + } + return FALSE; +} + +BOOL CALLBACK SplashDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + switch( uMsg ) + { + case WM_INITDIALOG: + switch (plLocalization::GetLanguage()) + { + case plLocalization::kFrench: + ::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "Démarrage d'URU. Veuillez patienter..."); + break; + case plLocalization::kGerman: + ::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "Starte URU, bitte warten ..."); + break; +/* case plLocalization::kSpanish: + ::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "Iniciando URU, por favor espera..."); + break; + case plLocalization::kItalian: + ::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "Avvio di URU, attendere..."); + break; +*/ // default is English + case plLocalization::kJapanese: + ::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "..."); + break; + default: + ::SetDlgItemText( hwndDlg, IDC_STARTING_TEXT, "Starting URU. Please wait..."); + break; + } + return true; + + } + return 0; +} + +static char sStackTraceMsg[ 10240 ] = ""; +void printStackTrace( char* buffer, int bufferSize, unsigned long stackPtr = 0, unsigned long opPtr = 0 ); +//void StackTraceFromContext( HANDLE hThread, CONTEXT *context, char *outputBuffer ); + +BOOL CALLBACK ExceptionDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + static char *sLastMsg = nil; + + switch( uMsg ) + { + case WM_INITDIALOG: + sLastMsg = (char *)lParam; + ::SetDlgItemText( hwndDlg, IDC_CRASHINFO, sLastMsg ); + return true; + + case WM_COMMAND: + if( wParam == IDC_COPY && sLastMsg != nil ) + { + HGLOBAL copyText = GlobalAlloc( GMEM_DDESHARE, sizeof( TCHAR ) * ( strlen( sLastMsg ) + 1 ) ); + if( copyText != nil ) + { + LPTSTR copyPtr = (LPTSTR)GlobalLock( copyText ); + memcpy( copyPtr, sLastMsg, ( strlen( sLastMsg ) + 1 ) * sizeof( TCHAR ) ); + GlobalUnlock( copyText ); + + ::OpenClipboard( hwndDlg ); + ::EmptyClipboard(); + ::SetClipboardData( CF_TEXT, copyText ); + ::CloseClipboard(); + } + return true; + } + else if( wParam == IDOK ) + EndDialog( hwndDlg, IDOK ); + else + break; + } + return 0; +} + + +LONG WINAPI plCustomUnhandledExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo ) +{ + const char *type = nil; + switch( ExceptionInfo->ExceptionRecord->ExceptionCode ) + { + case EXCEPTION_ACCESS_VIOLATION: type = "Access violation"; break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: type = "Array bounds exceeded"; break; + case EXCEPTION_BREAKPOINT: type = "Breakpoint"; break; + case EXCEPTION_DATATYPE_MISALIGNMENT: type = "Datatype misalignment"; break; + case EXCEPTION_FLT_DENORMAL_OPERAND: type = "Floating operand denormal"; break; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: type = "Floating-point divide-by-zero"; break; + case EXCEPTION_FLT_INEXACT_RESULT: type = "Floating-point inexact result"; break; + case EXCEPTION_FLT_INVALID_OPERATION: type = "Floating-point invalid operation"; break; + case EXCEPTION_FLT_OVERFLOW: type = "Floating-point overflow"; break; + case EXCEPTION_FLT_STACK_CHECK: type = "Floating-point stack error"; break; + case EXCEPTION_FLT_UNDERFLOW: type = "Floating-point underflow"; break; + case EXCEPTION_ILLEGAL_INSTRUCTION: type = "Illegal instruction"; break; + case EXCEPTION_IN_PAGE_ERROR: type = "Exception in page"; break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: type = "Integer divide-by-zero"; break; + case EXCEPTION_INT_OVERFLOW: type = "Integer overflow"; break; + case EXCEPTION_INVALID_DISPOSITION: type = "Invalid disposition"; break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: type = "Noncontinuable exception"; break; + case EXCEPTION_PRIV_INSTRUCTION: type = "Private instruction"; break; + case EXCEPTION_SINGLE_STEP: type = "Single-step"; break; + case EXCEPTION_STACK_OVERFLOW: type = "Stack overflow"; break; + } + + char prodName[256]; + wchar productString[256]; + ProductString(productString, arrsize(productString)); + StrToAnsi(prodName, productString, arrsize(prodName)); + + sprintf( sStackTraceMsg, "%s\r\nException type: %s\r\n", prodName, ( type != nil ) ? type : "(unknown)" ); + + printStackTrace( sStackTraceMsg, sizeof( sStackTraceMsg ), ExceptionInfo->ContextRecord->Ebp, (unsigned long)ExceptionInfo->ExceptionRecord->ExceptionAddress ); + + /// Print the info out to a log file as well + hsUNIXStream log; + wchar fileAndPath[MAX_PATH]; + PathGetLogDirectory(fileAndPath, arrsize(fileAndPath)); + PathAddFilename(fileAndPath, fileAndPath, L"stackDump.log", arrsize(fileAndPath)); + if( log.Open( fileAndPath, L"wt" ) ) + { + log.WriteString( sStackTraceMsg ); + log.Close(); + } + + /// Hopefully we can access this resource, even given the exception (i.e. very-bad-error) we just experienced + if(TGIsCider || (::DialogBoxParam( gHInst, MAKEINTRESOURCE( IDD_EXCEPTION ), ( gClient != nil ) ? gClient->GetWindowHandle() : nil, + ExceptionDialogProc, (LPARAM)sStackTraceMsg ) == -1) ) + { + // The dialog failed, so just fallback to a standard message box + hsMessageBox( sStackTraceMsg, "UruExplorer Exception", hsMessageBoxNormal ); + } + return EXCEPTION_EXECUTE_HANDLER; +} + +int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) +{ + // Set global handle + gHInst = hInst; + + CCmdParser cmdParser(s_cmdLineArgs, arrsize(s_cmdLineArgs)); + cmdParser.Parse(); + + bool doIntroDialogs = true; +#ifndef PLASMA_EXTERNAL_RELEASE + if(cmdParser.IsSpecified(kArgSkipLoginDialog)) + doIntroDialogs = false; + if(cmdParser.IsSpecified(kArgLocalData)) + gDataServerLocal = true; + if(cmdParser.IsSpecified(kArgBackgroundDownloader)) + gUseBackgroundDownloader = true; +#endif + if(cmdParser.IsSpecified(kArgAuthSrv)) + { + SetAuthSrvHostname(cmdParser.GetString(kArgAuthSrv)); + } + + if(cmdParser.IsSpecified(kArgFileSrv)) + { + SetFileSrvHostname(cmdParser.GetString(kArgFileSrv)); + } + + if(cmdParser.IsSpecified(kArgGateKeeperSrv)) + { + SetGateKeeperSrvHostname(cmdParser.GetString(kArgGateKeeperSrv)); + } + + // check to see if we were launched from the patcher + bool eventExists = false; + // we check to see if the event exists that the patcher should have created + HANDLE hPatcherEvent = CreateEventW(nil, TRUE, FALSE, L"UruPatcherEvent"); + if (hPatcherEvent != NULL) + { + // successfully created it, check to see if it was already created + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + // it already existed, so the patcher is waiting, signal it so the patcher can die + SetEvent(hPatcherEvent); + eventExists = true; + } + } + + TGDoCiderDetection (); + +#ifdef PLASMA_EXTERNAL_RELEASE + // if the client was started directly, run the patcher, and shutdown + STARTUPINFOW si; + PROCESS_INFORMATION pi; + ZERO(si); + ZERO(pi); + si.cb = sizeof(si); + wchar cmdLine[MAX_PATH]; + const wchar ** addrs; + unsigned count; + + if (!eventExists) // if it is missing, assume patcher wasn't launched + { + StrCopy(cmdLine, s_patcherExeName, arrsize(cmdLine)); + count = GetAuthSrvHostnames(&addrs); + if(count && AuthSrvHostnameOverride()) + StrPrintf(cmdLine, arrsize(cmdLine), L"%ws /AuthSrv=%ws", cmdLine, addrs[0]); + + count = GetFileSrvHostnames(&addrs); + if(count && FileSrvHostnameOverride()) + StrPrintf(cmdLine, arrsize(cmdLine), L"%ws /FileSrv=%ws", cmdLine, addrs[0]); + + count = GetGateKeeperSrvHostnames(&addrs); + if(count && GateKeeperSrvHostnameOverride()) + StrPrintf(cmdLine, arrsize(cmdLine), L"%ws /GateKeeperSrv=%ws", cmdLine, addrs[0]); + + if(!CreateProcessW(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) + { + hsMessageBox("Failed to launch patcher", "Error", hsMessageBoxNormal); + } + CloseHandle( pi.hThread ); + CloseHandle( pi.hProcess ); + return PARABLE_NORMAL_EXIT; + } +#endif + + plLocalization::SetDefaultLanguage(); + // If another instance is running, exit. We'll automatically release our + // lock on the mutex when our process exits + HANDLE hOneInstance = CreateMutex(nil, FALSE, "UruExplorer"); + if (WaitForSingleObject(hOneInstance,0) != WAIT_OBJECT_0) + { + switch (plLocalization::GetLanguage()) + { + case plLocalization::kFrench: + hsMessageBox("Une autre copie d'URU est déjà en cours d'exécution", "Erreur", hsMessageBoxNormal); + break; + case plLocalization::kGerman: + hsMessageBox("URU wird bereits in einer anderen Instanz ausgeführt", "Fehler", hsMessageBoxNormal); + break; +/* case plLocalization::kSpanish: + hsMessageBox("En estos momentos se está ejecutando otra copia de URU", "Error", hsMessageBoxNormal); + break; + case plLocalization::kItalian: + hsMessageBox("Un'altra copia di URU è già aperta", "Errore", hsMessageBoxNormal); + break; +*/ // default is English + default: + hsMessageBox("Another copy of URU is already running", "Error", hsMessageBoxNormal); + break; + } + return PARABLE_NORMAL_EXIT; + } + + if (IsExpired()) + { + hsMessageBox("This client is over 30 days old. You need to get a new one.", "Error", hsMessageBoxNormal); + return PARABLE_NORMAL_EXIT; + } + + NetCliAuthAutoReconnectEnable(false); + + NetCommSetReadIniAccountInfo(!doIntroDialogs); + InitNetClientComm(); + + wchar acctName[kMaxAccountNameLength]; + + // if we're being launched from gametap then don't use the intro dialogs + if (StrStrI(lpCmdLine, "screenname=")) { + doIntroDialogs = false; + + wchar authToken[kMaxPublisherAuthKeyLength]; + wchar os[kMaxGTOSIdLength]; + ShaDigest emptyDigest; + + MemSet(acctName, 0, sizeof(acctName)); + MemSet(authToken, 0, sizeof(authToken)); + MemSet(os, 0, sizeof(os)); + + const char* temp = lpCmdLine; + char token[128]; + while (StrTokenize(&temp, token, arrsize(token), " =")) { + if (StrCmpI(token, "screenname") == 0) { + if (!StrTokenize(&temp, token, arrsize(token), " =")) + break; + + StrToUnicode(acctName, token, arrsize(acctName)); + } + else if (StrCmpI(token, "authtoken") == 0) { + if (!StrTokenize(&temp, token, arrsize(token), " =")) + break; + + StrToUnicode(authToken, token, arrsize(authToken)); + } + else if (StrCmpI(token, "os") == 0) { + if (!StrTokenize(&temp, token, arrsize(token), " =")) + break; + + StrToUnicode(os, token, arrsize(os)); + } + } + + NetCommSetAccountUsernamePassword(acctName, emptyDigest); + NetCommSetAuthTokenAndOS(authToken, os); + } + + bool needExit = false; + LoginDialogParam loginParam; + MemSet(&loginParam, 0, sizeof(loginParam)); + + if (!doIntroDialogs) { + ENetError auth; + + bool cancelled = AuthenticateNetClientComm(&auth, NULL); + + if (IS_NET_ERROR(auth) || cancelled) { + doIntroDialogs = true; + + loginParam.fromGT = true; + loginParam.authError = auth; + StrCopy(loginParam.accountName, acctName, arrsize(loginParam.accountName)); + + if (cancelled) + { + NetCommDisconnect(); + } + } + } + + if (doIntroDialogs) { + if (TGIsCider) + needExit = !TGRunLoginDialog (loginParam.accountName, loginParam.fromGT); + else if (::DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_URULOGIN_MAIN ), NULL, UruLoginDialogProc, (LPARAM)&loginParam ) <= 0) + needExit = true; + } + + if (doIntroDialogs && !needExit) { + if (TGIsCider) + needExit = !TGRunTOSDialog (); + else + { + HINSTANCE hRichEdDll = LoadLibrary("RICHED20.DLL"); + INT_PTR val = ::DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_URULOGIN_EULA ), NULL, UruTOSDialogProc, (LPARAM)hInst); + FreeLibrary(hRichEdDll); + if (val <= 0) { + DWORD error = GetLastError(); + needExit = true; + } + } + } + + if (needExit) { + DeInitNetClientComm(); + return PARABLE_NORMAL_EXIT; + } + + NetCliAuthAutoReconnectEnable(true); + + // VERY VERY FIRST--throw up our splash screen + HWND splashDialog = ::CreateDialog( hInst, MAKEINTRESOURCE( IDD_LOADING ), NULL, SplashDialogProc ); + + // Install our unhandled exception filter for trapping all those nasty crashes in release build +#ifndef HS_DEBUGGING + LPTOP_LEVEL_EXCEPTION_FILTER oldFilter; + oldFilter = SetUnhandledExceptionFilter( plCustomUnhandledExceptionFilter ); +#endif + + // + // Set up to log errors by using hsDebugMessage + // + gDebugFile = NULL; + if ( !plStatusLog::fLoggingOff ) + { + wchar fileAndPath[MAX_PATH]; + PathGetLogDirectory(fileAndPath, arrsize(fileAndPath)); + PathAddFilename(fileAndPath, fileAndPath, L"plasmalog.txt", arrsize(fileAndPath)); + gDebugFile = _wfopen(fileAndPath, L"wt"); + hsAssert(gDebugFile != NULL, "Error creating debug file plasmalog.txt"); + hsSetDebugMessageProc(DebugMessageProc); + if (gDebugFile != NULL) + { + char prdName[256]; + wchar prdString[256]; + ProductString(prdString, arrsize(prdString)); + StrToAnsi(prdName, prdString, arrsize(prdName)); + fprintf(gDebugFile, "%s\n", prdName); + fflush(gDebugFile); + } + } + + // log stackdump.log text if the log exists + char stackDumpText[1024]; + wchar stackDumpTextW[1024]; + memset(stackDumpText, 0, arrsize(stackDumpText)); + memset(stackDumpTextW, 0, arrsize(stackDumpTextW) * sizeof(wchar)); + wchar fileAndPath[MAX_PATH]; + PathGetLogDirectory(fileAndPath, arrsize(fileAndPath)); + PathAddFilename(fileAndPath, fileAndPath, L"stackDump.log", arrsize(fileAndPath)); + FILE *stackDumpLog = _wfopen(fileAndPath, L"r"); + if(stackDumpLog) + { + fread(stackDumpText, 1, arrsize(stackDumpText) - 1, stackDumpLog); + StrToUnicode(stackDumpTextW, stackDumpText, arrsize(stackDumpText)); + NetCliAuthLogStackDump (stackDumpTextW); + fclose(stackDumpLog); + plFileUtils::RemoveFile(fileAndPath); + } + + for (;;) { + // Create Window + if (!WinInit(hInst, nCmdShow) || gClient->GetDone()) + break; + + // We don't have multiplayer localized assets for Italian or Spanish, so force them to English in that case. + /* if (!plNetClientMgr::GetInstance()->InOfflineMode() && + (plLocalization::GetLanguage() == plLocalization::kItalian || + plLocalization::GetLanguage() == plLocalization::kSpanish)) + { + plLocalization::SetLanguage(plLocalization::kEnglish); + } + */ + + // Done with our splash now + ::DestroyWindow( splashDialog ); + + if (!gClient) + break; + + // Show the main window + ShowWindow(gClient->GetWindowHandle(), SW_SHOW); + + gHasMouse = GetSystemMetrics(SM_MOUSEPRESENT); + + // Be really REALLY forceful about being in the front + BringWindowToTop( gClient->GetWindowHandle() ); + + // Update the window + UpdateWindow(gClient->GetWindowHandle()); + + // + // Init Application here + // + if( !gClient->StartInit() ) + break; + + // I want it on top! I mean it! + BringWindowToTop( gClient->GetWindowHandle() ); + + // initialize dinput here: + if (gClient && gClient->GetInputManager()) + gClient->GetInputManager()->InitDInput(hInst, (HWND)gClient->GetWindowHandle()); + + // Seriously! + BringWindowToTop( gClient->GetWindowHandle() ); + + // + // Main loop + // + MSG msg; + do + { + gClient->MainLoop(); + + if( gClient->GetDone() ) + break; + + // Look for a message + while (PeekMessage( &msg, NULL, 0, 0, PM_REMOVE )) + { + // Handle the message + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + } while (WM_QUIT != msg.message); + + break; + } + +#ifndef _DEBUG + try + { +#endif + // + // Cleanup + // + if (gClient) + { + gClient->Shutdown(); // shuts down PhysX for us + gClient = nil; + } + hsAssert(hsgResMgr::ResMgr()->RefCnt()==1, "resMgr has too many refs, expect mem leaks"); + hsgResMgr::Shutdown(); // deletes fResMgr + DeInitNetClientComm(); +#ifndef _DEBUG + } catch (...) + { + // just catch all the crashes on exit... just to keep GameTap from complaining + if (gDebugFile) + fprintf(gDebugFile, "Crashed on shutdown.\n"); + } +#endif + + if (gDebugFile) + fclose(gDebugFile); + + // Uninstall our unhandled exception filter, if we installed one +#ifndef HS_DEBUGGING + SetUnhandledExceptionFilter( oldFilter ); +#endif + + // Exit WinMain and terminate the app.... +// return msg.wParam; + return PARABLE_NORMAL_EXIT; +} + +bool IsExpired() +{ + bool expired = false; + +#ifndef PLASMA_EXTERNAL_RELEASE + char ourPath[MAX_PATH]; + GetModuleFileName(NULL, ourPath, sizeof(ourPath)); + DWORD ok = 0; + DWORD size = GetFileVersionInfoSize(ourPath, &ok); + if (size > 0) + { + void* data = TRACKED_NEW UInt8[size]; + GetFileVersionInfo(ourPath, ok, size, data); + + unsigned int descLen = 0; + void* desc = nil; + if (VerQueryValue(data, "\\StringFileInfo\\040904B0\\FileDescription", &desc, &descLen)) + { + char* buildDateStart = strstr((const char*)desc, " - Built "); + if (buildDateStart) + { + buildDateStart += strlen(" - Built "); + char* buildDateEnd = strstr(buildDateStart, " at"); + if (buildDateEnd) + { + int len = buildDateEnd-buildDateStart; + + char buf[32]; + strncpy(buf, buildDateStart, len); + buf[len] = '\0'; + + int month = atoi(strtok(buf, "/")); + int day = atoi(strtok(nil, "/")); + int year = atoi(strtok(nil, "/")); + + SYSTEMTIME curTime, buildTime; + GetLocalTime(&buildTime); + GetLocalTime(&curTime); + buildTime.wDay = day; + buildTime.wMonth = month; + buildTime.wYear = year; + + ULARGE_INTEGER iCurTime, iBuildTime; + FILETIME ft; + + SystemTimeToFileTime(&curTime, &ft); + iCurTime.LowPart = ft.dwLowDateTime; + iCurTime.HighPart = ft.dwHighDateTime; + + SystemTimeToFileTime(&buildTime, &ft); + iBuildTime.LowPart = ft.dwLowDateTime; + iBuildTime.HighPart = ft.dwHighDateTime; + + int secsOld = (int)((iCurTime.QuadPart - iBuildTime.QuadPart) / 10000000); + int daysOld = secsOld / (60 * 60 * 24); + + if (daysOld > 30) + expired = true; + } + } + } + + delete [] data; + } +#endif + + return expired; +} + +void GetCryptKey(UInt32* cryptKey, unsigned numElements) +{ + char volName[] = "C:\\"; + int index = 0; + DWORD logicalDrives = GetLogicalDrives(); + + for (int i = 0; i < 32; ++i) + { + if (logicalDrives & (1 << i)) + { + volName[0] = ('C' + i); + + DWORD volSerialNum = 0; + BOOL result = GetVolumeInformation( + volName, //LPCTSTR lpRootPathName, + NULL, //LPTSTR lpVolumeNameBuffer, + 0, //DWORD nVolumeNameSize, + &volSerialNum, //LPDWORD lpVolumeSerialNumber, + NULL, //LPDWORD lpMaximumComponentLength, + NULL, //LPDWORD lpFileSystemFlags, + NULL, //LPTSTR lpFileSystemNameBuffer, + 0 //DWORD nFileSystemNameSize + ); + + cryptKey[index] = (cryptKey[index] ^ volSerialNum); + + index = (++index) % numElements; + } + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/DllMain.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/DllMain.cpp new file mode 100644 index 00000000..df6c0c8f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/DllMain.cpp @@ -0,0 +1,35 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include + +__declspec(dllexport) const unsigned long* GetKey() +{ + // So the crackers know who the tard is + static const char* tauntText = "My copy protection is uncrackable! -Brice"; + + static const unsigned long kDefaultKey[4] = { 0x6c0a5452, 0x3827d0f, 0x3a170b92, 0x16db7fc2 }; + return kDefaultKey; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.cpp new file mode 100644 index 00000000..69e1b9cb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.cpp @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plClientKey.h" +#include "hsWindows.h" + +typedef const UInt32* (*GETKEY)(); + +const UInt32* plClientKey::GetKey() +{ + HMODULE hDll = LoadLibrary("sp.dll"); + if (hDll) + { + GETKEY getKey = (GETKEY)GetProcAddress(hDll, "GetKey"); + if (getKey) + { + static UInt32 key[4]; + memcpy(key, getKey(), sizeof(key)); + FreeLibrary(hDll); + return key; + } + } + + return nil; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.def b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.def new file mode 100644 index 00000000..1bc5fa0b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.def @@ -0,0 +1,5 @@ +LIBRARY +EXPORTS + GetKey @1 +SECTIONS + .data READ WRITE diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.h new file mode 100644 index 00000000..76a9f559 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientKey/plClientKey.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plClientKey_h_inc +#define plClientKey_h_inc + +#include "hsTypes.h" + +// +// For getting the "SafeDisc protected" encryption key in single player mode +// +// Include plClientKey.cpp/h in your project and call plClientKey::GetKey +// It will load the Dll and get the key for you +// Returns nil if it fails +// +namespace plClientKey +{ + const UInt32* GetKey(); +} + +#endif // plClientKey_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/Intern.h new file mode 100644 index 00000000..0f981de3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/Intern.h @@ -0,0 +1,42 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_INTERN_H + + +/***************************************************************************** +* +* SelfPatcher.cpp +* +***/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/Pch.h new file mode 100644 index 00000000..f79e6646 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/Pch.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_PCH_H +#error "Header $/Plasma20/Sources/Plasma/Apps/plClientPatcher/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_PCH_H + +#include +#include +#include "pnUtils/pnUtils.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" +#include "pnProduct/pnProduct.h" +#include "pnNetCli/pnNetCli.h" +#include "plNetGameLib/plNetGameLib.h" +#include "plEncryption/plChecksum.h" +#include "plAgeDescription/plAgeManifest.h" +#include "plAudioCore/plAudioFileReader.h" + +#define USES_PROTOCOL_CLI2AUTH +#include "pnNetProtocol/pnNetProtocol.h" + +#include "UruPlayer.h" + +#include "plCompression/plZlibStream.h" +#include "Intern.h" +#include "../plUruLauncher/plLauncherInfo.h" + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.cpp new file mode 100644 index 00000000..9516a3c2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.cpp @@ -0,0 +1,1023 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ +#ifndef PLASMA_EXTERNAL_RELEASE + static const wchar s_manifest[] = L"Internal"; + static const wchar s_macmanifest[] = L"macInternal"; + static const wchar s_thinmanifest[] = L"ThinInternal"; +#else + static const wchar s_manifest[] = L"External"; + static const wchar s_macmanifest[] = L"macExternal"; + static const wchar s_thinmanifest[] = L"ThinExternal"; +#endif + +struct ManifestFile +{ + LINK(ManifestFile) link; + ManifestFile(const wchar clientName[], const wchar downloadName[], const wchar md5val[], int flags, plLauncherInfo *info) + { + StrCopy(filename, clientName, arrsize(filename)); + StrCopy(zipName, downloadName, arrsize(zipName)); + StrCopy(md5, md5val, arrsize(md5)); + this->flags = flags; + this->info = info; + md5failed = false; + } + + wchar filename[MAX_PATH]; + wchar zipName[MAX_PATH]; + wchar md5[MAX_PATH]; + int flags; + bool md5failed; + plLauncherInfo *info; +}; + + +class ProgressStream : public plZlibStream { +public: + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + static plLauncherInfo *info; + static long totalBytes; + static unsigned progress; + + // for updating bytes per second + static dword startTime; +}; + +struct ProcessManifestEntryParam { + struct ManifestResult * mr; + unsigned index; + static long totalSize; + static long progress; + static double startTime; + bool exists; // marked as true if the file exists before MD5 check +}; + +struct ManifestResult { + wchar group[MAX_PATH]; + ARRAY(NetCliFileManifestEntry) manifest; + long * indicator; + plLauncherInfo * info; + + CCritSect critsect; + ARRAY(unsigned) indices; +}; + + +static void DownloadCallback ( + ENetError result, + void * param, + const wchar filename[], + hsStream * writer +); + + +/***************************************************************************** +* +* Private data +* +***/ + +static const unsigned kMaxManifestFileRequests = 5; +static const unsigned kMinThreads = 16; +static const unsigned kMaxThreads = 64; + + +static unsigned s_fileRequests; +static unsigned s_fileListRequests; +static bool s_patchComplete; +static PROCESS_INFORMATION s_pi; +static long s_numFiles; +static CCritSect s_critsect; +static char s_workingDir[MAX_PATH]; +static bool s_patchError; +static long s_asyncCoreInitCount; +static long s_numConnectFailures; +static bool s_running; +static LISTDECL(ManifestFile, link) s_manifestQueue; +//static AsyncThreadTaskList * s_taskList; + +// error strings +static const char s_fileOpenError[] = "Unable to create file. Hard drive may be full."; +static const char s_md5CheckError[] = "Error downloading file from server, exiting..."; + +enum { + kPerfThreadTaskCount, + kNumPerf +}; + +static long s_perf[kNumPerf]; + + +long ProgressStream::totalBytes; +unsigned ProgressStream::progress; +plLauncherInfo * ProgressStream::info; +dword ProgressStream::startTime = 0; +long ProcessManifestEntryParam::progress = 0; +long ProcessManifestEntryParam::totalSize = 0; +double ProcessManifestEntryParam::startTime = 0; + + +/***************************************************************************** +* +* Exported data +* +***/ + +// IMPORTANT: This string may NEVER change. Doing so will break self-patching, +// leaving clients with older patchers "dead in the water", without +// a way to play Uru. +const wchar kPatcherExeFilename[] = L"UruLauncher.exe"; + + + +//============================================================================ +// External client file list +//============================================================================ +#ifdef PLASMA_EXTERNAL_RELEASE +#ifdef HS_DEBUGGING +static wchar s_clientExeName[] = L"plClient_dbg.exe"; +#else +static wchar s_clientExeName[] = L"UruExplorer.exe"; +#endif // HS_DEBUGGING + +//============================================================================ +// Internal client file list +//============================================================================ +#else +#ifdef HS_DEBUGGING +static wchar s_clientExeName[] = L"plClient_dbg.exe"; +#else +static wchar s_clientExeName[] = L"plClient.exe"; +#endif // HS_DEBUGGING +#endif // PLASMA_EXTERNAL_RELEASE + + +/***************************************************************************** +* +* Private Functions +* +***/ + +//============================================================================ +static void LogHandler (ELogSeverity severity, const wchar msg[]) { + AsyncLogWriteMsg(L"UruPlayer", severity, msg); +} + +//============================================================================ +static void NetErrorHandler (ENetProtocol protocol, ENetError error) { + + const wchar * srv; + switch (protocol) { + case kNetProtocolNil: srv = L"Notify"; break; + case kNetProtocolCli2File: srv = L"File"; break; + case kNetProtocolCli2GateKeeper: srv = L"GateKeeper"; break; + DEFAULT_FATAL(protocol); + } + + switch (error) { + case kNetErrConnectFailed: + case kNetErrTimeout: + ++s_numConnectFailures; + break; + + case kNetErrDisconnected: + s_patchError = true; + break; + + case kNetErrServerBusy: + MessageBox(0, "Due to the high demand, the server is currently busy. Please try again later, or for alternative download options visit: http://www.mystonline.com/play/", "UruLauncher", MB_OK); + s_running = false; + break; + } + + LogMsg(kLogError, L"NetErr: %s: %s", srv, NetErrorToString(error)); + + // Notify GameTap something bad happened. + if (!s_patchError) { + MessageBox( + nil, + "Unable to connect to server.", + "Error", + MB_ICONERROR + ); + s_patchError = true; + } + + /*AsyncAppCallback( + kPlayerNotifyFailed, + kCmdResultFailed, + (void *)NetErrorToString(error) + );*/ +} + +/* +//============================================================================ +static void WaitUruExitProc (void * param) { + plLauncherInfo *info = (plLauncherInfo *) param; + WaitForSingleObject(s_pi.hProcess, INFINITE); + DWORD exitcode; + GetExitCodeProcess(s_pi.hProcess, &exitcode); + CloseHandle( s_pi.hThread ); + CloseHandle( s_pi.hProcess ); + + if(exitcode == kExitCodeTerminated) { + info->stopCallback(kStatusOk, nil); // notify of succesful stop + } + else { + info->exitCallback(kStatusOk, nil); + } +} +*/ + +//============================================================================ +static bool MD5Check (const char filename[], const wchar md5[]) { + // Do md5 check + char md5copy[MAX_PATH]; + plMD5Checksum existingMD5(filename); + plMD5Checksum latestMD5; + + StrToAnsi(md5copy, md5, arrsize(md5copy)); + latestMD5.SetFromHexString(md5copy); + return (existingMD5 == latestMD5); +} + +//============================================================================ +static void DecompressOgg (ManifestFile *mf) { + unsigned flags = mf->flags; + for(;;) + { + // decompress ogg if necessary + if ( (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit) || hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) ) + { + char path[MAX_PATH]; + StrPrintf(path, arrsize(path), "%s%S", s_workingDir, mf->filename); + + plAudioFileReader* reader = plAudioFileReader::CreateReader(path, plAudioCore::kAll, plAudioFileReader::kStreamNative); + if (!reader) + { + break; + } + + UInt32 size = reader->GetDataSize(); + delete reader; + + ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, neededBytes; + if (GetDiskFreeSpaceEx(NULL, &freeBytesAvailable, &totalNumberOfBytes, NULL)) + { + neededBytes.HighPart = 0; + neededBytes.LowPart = size; + + if (neededBytes.QuadPart > freeBytesAvailable.QuadPart) + { + //PatcherLog(kInfo, "Not enough disk space (asked for %d bytes)", bytesNeeded); + break; + } + } + + if (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit)) + plAudioFileReader::CacheFile(path, true, true); + if (hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) + plAudioFileReader::CacheFile(path, false, true); + } + break; + } +} + +//============================================================================ +void Shutdown(plLauncherInfo *info) { + info->SetText("Shutting Down..."); + s_patchError = true; + s_running = false; +} + +//============================================================================ +static void RequestNextManifestFile () { + bool success = true; + ManifestFile *nextfile = s_manifestQueue.Head(); + if(!nextfile) + return; + s_manifestQueue.Unlink(nextfile); + char path[MAX_PATH]; + wchar basePath[MAX_PATH]; + StrPrintf(path, arrsize(path), "%s%S", s_workingDir, nextfile->filename); + StrToUnicode(basePath, path, arrsize(basePath)); + PathRemoveFilename(basePath, basePath, arrsize(basePath)); + PathCreateDirectory(basePath, kPathCreateDirFlagEntireTree); + + ProgressStream *writer = NEW(ProgressStream); // optimization: dont delete and recreate. Doesn't seem to be working currently, ZLibStream is breaking + if(!writer->Open(path, "wb")) + { + writer->Close(); + delete writer; + success = false; + } + + if(success) + { +#ifndef PLASMA_EXTERNAL_RELEASE + char text[256]; + StrPrintf(text, arrsize(text), "Updating URU... %S", nextfile->filename); + nextfile->info->SetText(text); +#endif + NetCliFileDownloadRequest(nextfile->zipName, writer, DownloadCallback, nextfile, nextfile->info->buildId); + } +} + +//============================================================================ +static void DownloadCallback ( + ENetError result, + void * param, + const wchar filename[], + hsStream * writer +) { + s_numConnectFailures = 0; + + ref(result); + ref(param); + ref(filename); + + ManifestFile *mf = (ManifestFile *)param; + if (IS_NET_ERROR(result) && s_running && !s_patchError) { + if (result == kNetErrFileNotFound) { + char str[256]; + StrPrintf(str, arrsize(str), "File not found on server: %S", filename); + MessageBox(nil, str, "URU Launcher", MB_ICONERROR); + s_patchError = true; + } + else if (result == kNetErrRemoteShutdown) { + s_patchError = true; + } + else { + // failed, re-queue the file to be downloaded + // (after rewinding the stream) + writer->Rewind(); + plLauncherInfo *info = mf->info; + NetCliFileDownloadRequest(filename, writer, DownloadCallback, mf, info->buildId); + return; + } + } + + writer->Close(); + delete writer; // delete our stream + + char path[MAX_PATH]; + StrPrintf( + path, + arrsize(path), + "%s%S", + s_workingDir, + mf->filename + ); + if(s_running) + { + if(!MD5Check(path, mf->md5)) { + if(mf->md5failed) + { +#ifdef PLASMA_EXTERNAL_RELEASE + MessageBox(nil, s_md5CheckError, "URU Launcher", MB_ICONERROR); +#else + char str[256]; + StrPrintf(str, arrsize(str), "%s %s ", path, s_md5CheckError); + MessageBox(nil, str, "URU Launcher", MB_ICONERROR); +#endif // PLASMA_EXTERNAL_RELEASE + Shutdown(mf->info); + } + writer = NEW(ProgressStream); + if (!writer->Open(path, "wb")) { +#ifdef PLASMA_EXTERNAL_RELEASE + MessageBox(nil, s_fileOpenError, "URU Launcher", MB_ICONERROR); +#else + char str[256]; + StrPrintf(str, arrsize(str), "%s %s", s_fileOpenError, path); + MessageBox(nil, str, "URU Launcher", MB_ICONERROR); +#endif // PLASMA_EXTERNAL_RELEASE + Shutdown(mf->info); + } + mf->md5failed = true; + plLauncherInfo *info = mf->info; + NetCliFileDownloadRequest(filename, writer, DownloadCallback, mf, info->buildId); + return; + } + } + + AtomicAdd(&s_numFiles, -1); + + if(s_running) + { + wchar ext[MAX_EXT]; + PathSplitPath(mf->filename, nil, nil, nil, ext); + if(!StrCmpI(L".ogg", ext)) + { + DecompressOgg(mf); + } + } + + delete mf; // delete manifest file entry + + // if we are not still running don't request any more file downloads + if(s_running) + { + if(!s_numFiles) { + s_patchComplete = true; + } + else + { + RequestNextManifestFile(); + } + } +} + +//============================================================================ +static void ProcessManifestEntry (void * param, ENetError error) { + ProcessManifestEntryParam * p = (ProcessManifestEntryParam*)param; + +#ifndef PLASMA_EXTERNAL_RELEASE + char text[256]; + StrPrintf(text, arrsize(text), "Checking for updates... %S", p->mr->manifest[p->index].clientName); + p->mr->info->SetText(text); +#endif + char path[MAX_PATH]; + StrPrintf( + path, + arrsize(path), + "%s%S", + s_workingDir, + p->mr->manifest[p->index].clientName + ); + dword start = TimeGetTime() / kTimeIntervalsPerMs; + if(!MD5Check(path, p->mr->manifest[p->index].md5)) { + p->mr->critsect.Enter(); + p->mr->indices.Add(p->index); + p->mr->critsect.Leave(); + AtomicAdd(&ProgressStream::totalBytes, p->mr->manifest[p->index].zipSize); + } + + // if we have a file that was cached the MD5 check will be really fast throwing off our approx time remaining. + dword t = TimeGetTime() / kTimeIntervalsPerMs - start; + if(t < 25) + { + // cached file + AtomicAdd(&ProcessManifestEntryParam::totalSize, -p->mr->manifest[p->index].zipSize); + p->exists = false; + } + + // p->mr->info->SetBytesRemaining(ProcessManifestEntryParam::totalSize); // for testing purposes only + if(p->exists) + { + AtomicAdd(&ProcessManifestEntryParam::progress, p->mr->manifest[p->index].zipSize); + + PatchInfo patchInfo; + patchInfo.stage = 0; + patchInfo.progressStage = 0; + patchInfo.progress = (unsigned)((float)(ProcessManifestEntryParam::progress) / (float)ProcessManifestEntryParam::totalSize * 1000.0f); + p->mr->info->progressCallback(kStatusPending, &patchInfo); + if(ProcessManifestEntryParam::progress > ProcessManifestEntryParam::totalSize) + { + p->mr->info->SetTimeRemaining(0); + } + else + { + if(TimeGetTime() / kTimeIntervalsPerMs != ProcessManifestEntryParam::startTime) + { + double timeElapsed = (TimeGetTime() / kTimeIntervalsPerMs - ProcessManifestEntryParam::startTime) / 1000; + double bytesPerSec = (float)(ProcessManifestEntryParam::progress ) / timeElapsed; + p->mr->info->SetTimeRemaining(bytesPerSec ? (int)((ProcessManifestEntryParam::totalSize - ProcessManifestEntryParam::progress) / bytesPerSec) : 0); + } + } + } +} + +//============================================================================ +static void ProcessManifest (void * param) { + wchar basePath[MAX_PATH]; + char path[MAX_PATH]; + AtomicAdd(&s_perf[kPerfThreadTaskCount], 1); + + ManifestResult * mr = (ManifestResult *)param; + + PatchInfo patchInfo; + patchInfo.stage = 0; + patchInfo.progressStage = 0; + patchInfo.progress = 0; + mr->info->progressCallback(kStatusPending, &patchInfo); + + char text[256]; + StrPrintf(text, arrsize(text), "Checking for updates..."); + mr->info->SetText(text); + + unsigned entryCount = mr->manifest.Count(); + NetCliFileManifestEntry * manifest = mr->manifest.Ptr(); + + FILE *fd = nil; + ARRAY(ProcessManifestEntryParam) params; + params.Reserve(mr->manifest.Count()); + for (unsigned i = 0; i < entryCount; ++i) { + ProcessManifestEntryParam * p = params.New(); + p->index = i; + p->mr = mr; + p->exists = false; + StrPrintf(path, arrsize(path), "%s%S", s_workingDir, mr->manifest[i].clientName); + fd = fopen(path, "r"); + if(fd) + { + p->exists = true; + p->totalSize += p->mr->manifest[i].zipSize; + fclose(fd); + + } + } + + ProcessManifestEntryParam::startTime = TimeGetTime() / kTimeIntervalsPerMs; + + for (unsigned i = 0; i < entryCount && s_running; ++i){ + ProcessManifestEntry(¶ms[i], kNetSuccess); + } + + if(s_running) + { + PatchInfo patchInfo; + patchInfo.stage = 0; + patchInfo.progressStage = 0; + patchInfo.progress = 1000; + mr->info->progressCallback(kStatusPending, &patchInfo); + + AtomicAdd(&s_numFiles, mr->indices.Count()); + if(!s_numFiles || !s_running) { + s_patchComplete = true; + } + else { + mr->info->SetText("Updating URU..."); + + PatchInfo patchInfo; + patchInfo.stage = 0; + patchInfo.progressStage = 0; + patchInfo.progress = 0; + mr->info->progressCallback(kStatusPending, &patchInfo); + + for (unsigned i = 0; i < mr->indices.Count(); ++i) + { + if(s_running) + { + unsigned index = mr->indices[i]; + StrPrintf(path, arrsize(path), "%s%S", s_workingDir, manifest[index].clientName); + StrToUnicode(basePath, path, arrsize(basePath)); + PathRemoveFilename(basePath, basePath, arrsize(basePath)); + PathCreateDirectory(basePath, kPathCreateDirFlagEntireTree); + + ManifestFile * mf = NEW(ManifestFile)( + manifest[index].clientName, + manifest[index].downloadName, + manifest[index].md5, + manifest[index].flags, + mr->info + ); + + if (i < kMaxManifestFileRequests) { + ProgressStream * stream; + stream = NEWZERO(ProgressStream); + if (!stream->Open(path, "wb")) { +#ifdef PLASMA_EXTERNAL_RELEASE + MessageBox(nil, s_fileOpenError, "URU Launcher", MB_ICONERROR); +#else + char str[256]; + StrPrintf(str, arrsize(str), "%s %s", path, s_fileOpenError); + MessageBox(nil, str, "URU Launcher", MB_ICONERROR); +#endif + Shutdown(mr->info); + } +#ifndef PLASMA_EXTERNAL_RELEASE + char text[256]; + StrPrintf(text, arrsize(text), "Updating URU... %S", manifest[i].clientName); + mr->info->SetText(text); +#endif + // fire off our initial requests. The remaining will be added as files are downloaded + NetCliFileDownloadRequest(mf->zipName, stream, DownloadCallback, mf, mr->info->buildId); + } + else { + // queue up this file download + s_manifestQueue.Link(mf); + } + } + } + } + } + DEL(mr); + AtomicAdd(&s_perf[kPerfThreadTaskCount], -1); +} + +//============================================================================ +static void ManifestCallback ( + ENetError result, + void * param, + const wchar group[], + const NetCliFileManifestEntry manifest[], + unsigned entryCount +){ + s_numConnectFailures = 0; + + plLauncherInfo * info = (plLauncherInfo *) param; + + if(!s_running || IS_NET_ERROR(result)) { + if (s_running && !s_patchError) { + switch (result) { + case kNetErrTimeout: + NetCliFileManifestRequest(ManifestCallback, param, group); + break; + + default: { + char str[256]; + StrPrintf(str, arrsize(str), "Failed to download manifest from server"); + MessageBox(nil, str, "URU Launcher", MB_ICONERROR); + s_patchError = true; + } + break; + } + } + return; + } + + ManifestResult * mr = NEW(ManifestResult); + StrCopy(mr->group, group, arrsize(mr->group)); + mr->manifest.Set(manifest, entryCount); + mr->info = info; + + // sort our requests by size(this must be done for the next step to work) + QSORT( + NetCliFileManifestEntry, + mr->manifest.Ptr(), + mr->manifest.Count(), + elem1.fileSize > elem2.fileSize + ); + + // remove duplicate entries. This can cause some bad problems if not done. It will cause MD5 checks to fail, since it can be writing a file while MD5 checking it. + ARRAY(NetCliFileManifestEntry) noDuplicates; + noDuplicates.Reserve(mr->manifest.Count()); + for(unsigned i = 0; i < entryCount - 1; ++i) + { + if(StrCmp(mr->manifest[i].clientName, mr->manifest[i+1].clientName)) + { + noDuplicates.Add(mr->manifest[i]); + } + } + noDuplicates.Add(mr->manifest[entryCount - 1]); + + // adjust our array and set data + mr->manifest.ShrinkBy(mr->manifest.Count() - noDuplicates.Count()); + mr->manifest.Set(noDuplicates.Ptr(), noDuplicates.Count()); + + (void)_beginthread(ProcessManifest, 0, mr); +} + +//============================================================================ +static void ThinManifestCallback ( + ENetError result, + void * param, + const wchar group[], + const NetCliFileManifestEntry manifest[], + unsigned entryCount +){ + s_numConnectFailures = 0; + + ref(group); + + plLauncherInfo * info = (plLauncherInfo *) param; + char text[256]; + StrPrintf(text, arrsize(text), "Checking for updates..."); + info->SetText(text); + + if(!s_running || IS_NET_ERROR(result)) { + if (s_running && !s_patchError) { + switch (result) { + case kNetErrTimeout: + NetCliFileManifestRequest(ManifestCallback, param, group); + break; + + default: { + char str[256]; + StrPrintf(str, arrsize(str), "Failed to download manifest from server"); + MessageBox(nil, str, "URU Launcher", MB_ICONERROR); + s_patchError = true; + } + break; + } + } + return; + } + s_patchComplete = true; + char path[MAX_PATH]; + for (unsigned i = 0; i < entryCount; ++i) { + if(!s_running) return; + StrPrintf(path, arrsize(path), "%s%S", s_workingDir, manifest[i].clientName); + if(!MD5Check(path, manifest[i].md5)){ + s_patchComplete = false; + if (info->IsTGCider) + NetCliFileManifestRequest(ManifestCallback, info, s_macmanifest, info->buildId); + else + NetCliFileManifestRequest(ManifestCallback, info, s_manifest, info->buildId); + break; + } + PatchInfo patchInfo; + patchInfo.stage = 0; + patchInfo.progressStage = 0; + patchInfo.progress = (unsigned)((float)i / (float)entryCount * 1000.0f); + info->progressCallback(kStatusPending, &patchInfo); +#ifndef PLASMA_EXTERNAL_RELEASE + char text[256]; + StrPrintf(text, arrsize(text), "Checking for updates... %S", manifest[i].clientName); + info->SetText(text); +#endif + } +} + + +/***************************************************************************** +* +* ProgressStream Functions +* +***/ + +//============================================================================ +UInt32 ProgressStream::Write(UInt32 byteCount, const void* buffer) { + if(!s_running || s_patchError) + return 0; + if(!startTime) { + startTime = TimeGetSecondsSince2001Utc(); + } + progress += byteCount; + float p = (float)progress / (float)totalBytes * 1000; // progress + + PatchInfo patchInfo; + patchInfo.stage = 1; + patchInfo.progress = (unsigned) p; + patchInfo.progressStage = 50; + info->progressCallback(kStatusPending, (void *)&patchInfo); + + // there seems to, sometimes, be a slight discrepency in progress and totalBytes. + if(progress > totalBytes) + { + info->SetBytesRemaining(0); + info->SetTimeRemaining(0); + } + else + { + info->SetBytesRemaining(totalBytes - progress); + if(TimeGetSecondsSince2001Utc() != startTime) + { + dword bytesPerSec = (progress ) / (TimeGetSecondsSince2001Utc() - startTime); + info->SetTimeRemaining(bytesPerSec ? (totalBytes - progress) / bytesPerSec : 0); + } + } + return plZlibStream::Write(byteCount, buffer); +} + + +//============================================================================ +static void FileSrvIpAddressCallback ( + ENetError result, + void * param, + const wchar addr[] +) { + ref(param); + + NetCliGateKeeperDisconnect(); + + if (IS_NET_ERROR(result)) { + LogMsg(kLogDebug, L"FileSrvIpAddressRequest failed: %s", NetErrorToString(result)); + s_patchError = true; + return; + } + + plLauncherInfo *info = (plLauncherInfo *) param; + + // Start connecting to the server + NetCliFileStartConnect(&addr, 1, true); + + NetCliFileManifestRequest(ThinManifestCallback, info, s_thinmanifest, info->buildId); + + ProgressStream::info = info; + PatchInfo patchInfo; + patchInfo.stage = 0; + patchInfo.progressStage = 0; + patchInfo.progress = 0; + info->progressCallback(kStatusPending, &patchInfo); +} + + +/***************************************************************************** +* +* Public Functions +* +***/ + +//============================================================================ +void InitAsyncCore () { + if(AtomicAdd(&s_asyncCoreInitCount, 1) > 0) + return; + LogRegisterHandler(LogHandler); + AsyncCoreInitialize(); + AsyncLogInitialize(L"Log", false); + + wchar productString[256]; + ProductString(productString, arrsize(productString)); + LogMsg(kLogPerf, L"Patcher: %s", productString); +} + +//============================================================================ +void ShutdownAsyncCore () { + if(AtomicAdd(&s_asyncCoreInitCount, -1) > 1) + return; + ASSERT(s_asyncCoreInitCount >= 0); + + while (s_perf[kPerfThreadTaskCount]) + AsyncSleep(10); + + AsyncLogDestroy(); + AsyncCoreDestroy(30 * 1000); + LogUnregisterHandler(LogHandler); +} + +//============================================================================ +// param = URU_PreparationRequest +void UruPrepProc (void * param) { + s_running = true; + + plLauncherInfo *info = (plLauncherInfo *) param; + + StrToAnsi(s_workingDir, info->path, arrsize(s_workingDir)); + + InitAsyncCore(); + NetClientInitialize(); + NetClientSetErrorHandler(NetErrorHandler); + NetClientSetTransTimeoutMs(5 * 60 * 1000); // five minute timeout + + s_patchComplete = false; + s_patchError = false; + + const wchar ** addrs; + unsigned count; + + count = GetGateKeeperSrvHostnames(&addrs); + + // Start connecting to the server + NetCliGateKeeperStartConnect(addrs, count); + + // request a file server ip address + NetCliGateKeeperFileSrvIpAddressRequest(FileSrvIpAddressCallback, param, true); + + do { + NetClientUpdate(); + AsyncSleep(10); + } while ((!s_patchComplete && !s_patchError && s_running) || s_perf[kPerfThreadTaskCount]); + + while(ManifestFile *mf = s_manifestQueue.Head()) + { + DEL(mf); + } + // If s_patchError, we don't wait around for s_numFiles + // to drop to zero because it never does for reasons + // I'm not willing to debug at the moment, so we just + // bail on them. This causes a race condition with + // the outstanding file object cancel/deletion and + // subsequently a memory leak. -eap + + if (s_patchError) { + info->SetText("Exiting..."); + } + else { + PatchInfo patchInfo; + patchInfo.stage = 2; + patchInfo.progressStage = 100; + patchInfo.progress = 1000; + info->progressCallback(kStatusOk, &patchInfo); + } + + ProgressStream::info = nil; + + NetCliFileDisconnect (); + NetClientUpdate(); + + // Shutdown the client/server networking subsystem + NetClientDestroy(); + + info->prepCallback(s_patchError ? kStatusError : kStatusOk, nil); +} + +//============================================================================ +void PlayerStopProc (void * param) { + s_running = false; + plLauncherInfo *info = (plLauncherInfo *) param; + ref(param); + //TerminateProcess(s_pi.hProcess, kExitCodeTerminated); + info->stopCallback(kStatusOk, nil); +} + +//============================================================================ +void PlayerTerminateProc (void * param) { + s_running = false; + plLauncherInfo *info = (plLauncherInfo *) param; + ShutdownAsyncCore(); + info->terminateCallback(kStatusOk, nil); +} + +//============================================================================ +void UruStartProc (void * param) { + if(!s_running) + return; + + plLauncherInfo *info = (plLauncherInfo *) param; + + wchar workDir[MAX_PATH]; + StrPrintf(workDir, arrsize(workDir), L"%s", info->path); + //fprintf(stderr, "URUPlayer StartProc gamePath is:%ws\n", workDir); + + wchar cmdLine[MAX_PATH]; + StrPrintf(cmdLine, arrsize(cmdLine), L"%s%s %s", workDir, s_clientExeName, info->cmdLine); + + // Create the named event so the client won't restart us (Windows will clean it up when we exit) + HANDLE hPatcherEvent = CreateEventW(nil, TRUE, FALSE, L"UruPatcherEvent"); + if (hPatcherEvent == NULL) { + info->startCallback(kStatusError, nil); + return; + } + + fprintf(stderr, "URUPlayer StartProc, running game process at dir:%ws, cmd:%ws for application:%ws\n", workDir, cmdLine, s_clientExeName); + + STARTUPINFOW si; + ZERO(si); + ZERO(s_pi); + si.cb = sizeof(si); + + info->SetText("Launching URU..."); + BOOL success = CreateProcessW( + NULL, + cmdLine, + NULL, // plProcessAttributes + NULL, // plThreadAttributes + FALSE, // bInheritHandles + 0, // dwCreationFlags + NULL, // lpEnvironment + workDir, // lpCurrentDirectory + &si, + &s_pi + ); + + if (success) + { + fprintf(stderr, "%d", GetLastError()); + info->returnCode = s_pi.dwProcessId; + CloseHandle( s_pi.hThread ); + CloseHandle( s_pi.hProcess ); + // This may smooth the visual transition from GameTap to Uru, or it may make it worse. + WaitForInputIdle(s_pi.hProcess, INFINITE); + //_beginthread(WaitUruExitProc, 0, param); + + // wait for the event to signal (give the client 10 seconds to start up, then die) + DWORD wait = WaitForSingleObject(hPatcherEvent, 10000); + if (wait == WAIT_TIMEOUT) + info->startCallback(kStatusOk, nil); + else + info->startCallback(kStatusOk, nil); + } + else + { + info->startCallback(kStatusError, nil); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h new file mode 100644 index 00000000..f9ec460d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_URUPLAYER_H +#error "Header $/Plasma20/Sources/Plasma/Apps/plClientPatcher/UruPlayer.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_APPS_PLCLIENTPATCHER_URUPLAYER_H + + +/***************************************************************************** +* +* UruPlayer.cpp +* +***/ + +void InitAsyncCore (); +void ShutdownAsyncCore () ; +void UruPrepProc (void * param); +void UruStartProc (void * param); +void PlayerTerminateProc (void * param); +void PlayerStopProc (void * param); + +extern const wchar kPatcherExeFilename[]; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/plLauncherCallback.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/plLauncherCallback.h new file mode 100644 index 00000000..3a249b91 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plClientPatcher/plLauncherCallback.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H +#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H + +enum EStatus { + kStatusOk, +}; + +typedef void (*launcherCallback)(int status, void *param); +struct plLauncherCallback { + launcherCallback prepCallback; + launcherCallback initCallback; + launcherCallback startCallback; + launcherCallback stopCallback; + launcherCallback terminateCallback; + +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plFileEncrypt/main.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plFileEncrypt/main.cpp new file mode 100644 index 00000000..b533b350 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plFileEncrypt/main.cpp @@ -0,0 +1,109 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../plFile/hsFiles.h" +#include "../plFile/plEncryptedStream.h" +#include "../pnUtils/pnUtils.h" +#include "../pnProduct/pnProduct.h" +#include "hsUtils.h" + +void EncryptFiles(const char* dir, const char* ext, bool encrypt); + +void print_version(){ + wchar productString[256]; + ProductString(productString, arrsize(productString)); + printf("%S\n\n", productString); +} + +void print_help() { + printf("plFileEncrypt - Encrypts and Decrypts Uru Files.\n\n"); + print_version(); + printf("Usage: plFileEncrypt \t[(encrypt|-e)|(decrypt|-d|)|(--help|-h|-?|/h)|(-v)]\n"); + printf("\tencrypt|-e\t - Encrypts All .age, .fni, .ini, .csv, and .sdl files in the current folder.\n"); + printf("\tdecrypt|-d\t - Decrypts All .age, .fni, .ini, .csv, and .sdl files in the current folder.\n"); + printf("\t--help|-h|-?|/h\t - Prints Help. This Screen.\n"); + printf("\t-v|--version\t - Prints build version information\n"); +} + + +int main(int argc, char *argv[]) +{ + bool encrypt = true; + const char* dir = "."; + + if (argc > 1) + { + if (hsStrEQ(argv[1], "encrypt") || hsStrEQ(argv[1], "-e") ) + { + if (argc > 2) + dir = argv[2]; + encrypt = true; + } + else if (hsStrEQ(argv[1], "decrypt") || hsStrEQ(argv[1], "-d")) + { + if (argc > 2) + dir = argv[2]; + encrypt = false; + } + else if(hsStrEQ(argv[1], "--help") || hsStrEQ(argv[1], "-h") || hsStrEQ(argv[1], "-?") || hsStrEQ(argv[1], "/?")) + { + print_help(); + return 0; + } + else if (hsStrEQ(argv[1], "-v") || hsStrEQ(argv[1], "--version")) + { + print_version(); + return 0; + } + } + + EncryptFiles(dir, ".age", encrypt); + EncryptFiles(dir, ".fni", encrypt); + EncryptFiles(dir, ".ini", encrypt); + EncryptFiles(dir, ".sdl", encrypt); + EncryptFiles(dir, ".csv", encrypt); + return 0; +} + +void EncryptFiles(const char* dir, const char* ext, bool encrypt) +{ + char filePath[256]; + + hsFolderIterator folder(dir); + while (folder.NextFileSuffix(ext)) + { + folder.GetPathAndName(filePath); + if (encrypt) + { + printf("encrypting: %s\n", folder.GetFileName()); + plEncryptedStream::FileEncrypt(filePath); + } + else + { + printf("decrypting: %s\n", folder.GetFileName()); + plEncryptedStream::FileDecrypt(filePath); + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plFileSecure/main.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plFileSecure/main.cpp new file mode 100644 index 00000000..4a3cb30e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plFileSecure/main.cpp @@ -0,0 +1,196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../plFile/hsFiles.h" +#include "../plFile/plFileUtils.h" +#include "../plFile/plSecureStream.h" +#include "../pnUtils/pnUtils.h" +#include "../pnProduct/pnProduct.h" +#include "hsUtils.h" + +#include +#include + +void print_version() { + wchar productString[256]; + ProductString(productString, arrsize(productString)); + printf("%S\n\n", productString); +} + +void print_help() { + printf("plFileSecure - Secures Uru files and generates encryption.key files.\n\n"); + print_version(); + printf("Usage:\n"); + printf("\tplFileSecure ( )|[/generate /default]\n"); + printf("\n"); + printf(" : The directory and extension of files to secure. Cannot\n"); + printf(" be used with /generate. Uses the %s file in\n", plFileUtils::kKeyFilename); + printf(" the current directory (or default key if no file exists)\n"); + printf("/generate : Generates a random key and writes it to a %s\n", plFileUtils::kKeyFilename); + printf(" file in the current directory. Cannot be used with\n"); + printf(" \n"); + printf("/default : If used with /generate, creates a %s file\n", plFileUtils::kKeyFilename); + printf(" with the default key. If used with , it\n"); + printf(" secures with the default key instead of the\n"); + printf(" %s file's key\n", plFileUtils::kKeyFilename); + printf("\n"); +} + +void GenerateKey(bool useDefault) +{ + UInt32 key[4]; + if (useDefault) + { + unsigned memSize = min(arrsize(key), arrsize(plSecureStream::kDefaultKey)); + memSize *= sizeof(UInt32); + memcpy(key, plSecureStream::kDefaultKey, memSize); + } + else + { + srand((unsigned)time(nil)); + double randNum = (double)rand() / (double)RAND_MAX; // converts to 0..1 + UInt32 keyNum = (UInt32)(randNum * (double)0xFFFFFFFF); // multiply it by the max unsigned 32-bit int + key[0] = keyNum; + + randNum = (double)rand() / (double)RAND_MAX; + keyNum = (UInt32)(randNum * (double)0xFFFFFFFF); + key[1] = keyNum; + + randNum = (double)rand() / (double)RAND_MAX; + keyNum = (UInt32)(randNum * (double)0xFFFFFFFF); + key[2] = keyNum; + + randNum = (double)rand() / (double)RAND_MAX; + keyNum = (UInt32)(randNum * (double)0xFFFFFFFF); + key[3] = keyNum; + } + + hsUNIXStream out; + out.Open(plFileUtils::kKeyFilename, "wb"); + out.Write(sizeof(UInt32) * arrsize(key), (void*)key); + out.Close(); +} + +void SecureFiles(std::string dir, std::string ext, UInt32* key) +{ + char filePath[256]; + + hsFolderIterator folder(dir.c_str()); + while (folder.NextFileSuffix(ext.c_str())) + { + folder.GetPathAndName(filePath); + printf("securing: %s\n", folder.GetFileName()); + plSecureStream::FileEncrypt(filePath, key); + } +} + +int main(int argc, char *argv[]) +{ + bool generatingKey = false; + bool useDefault = false; + std::string directory; + std::string ext; + + if (argc > 1) + { + for (int i = 1; i < argc; i++) + { + std::string arg = argv[i]; + if ((arg[0] == '-')||(arg[0] == '/')) + { + // this arg is a flag of some kind + arg = arg.substr(1, arg.length()); // trim the dash or slash + if ((stricmp(arg.c_str(), "g") == 0) || (stricmp(arg.c_str(), "generate") == 0)) + { + if (!generatingKey) + generatingKey = true; + else + { + print_help(); + return 0; + } + } + else if ((stricmp(arg.c_str(), "d") == 0) || (stricmp(arg.c_str(), "default") == 0)) + { + if (!useDefault) + useDefault = true; + else + { + print_help(); + return 0; + } + } + else + { + print_help(); + return 0; + } + } + else + { + // else it is a directory or extension + if (directory == "") + directory = argv[i]; + else if (ext == "") + ext = argv[i]; + else + { + print_help(); + return 0; + } + } + } + + if (generatingKey && ((directory != "") || (ext != ""))) + { + print_help(); + return 0; + } + } + else + { + print_help(); + return 0; + } + + if (generatingKey) + { + GenerateKey(useDefault); + return 0; + } + + if (ext[0] != '.') + ext = "." + ext; // tack on the dot if necessary + + if (useDefault) + SecureFiles(directory, ext, nil); + else + { + UInt32 key[4]; + plFileUtils::GetSecureEncryptionKey(plFileUtils::kKeyFilename, key, arrsize(key)); + SecureFiles(directory, ext, key); + } + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plLogDecrypt/plLogDecrypt.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plLogDecrypt/plLogDecrypt.cpp new file mode 100644 index 00000000..1186587b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plLogDecrypt/plLogDecrypt.cpp @@ -0,0 +1,110 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* plLogDecrypt - Used by mantis to decrypt log files +* +***/ + +#include +#include "hsTypes.h" +#include "../plStatusLog/plEncryptLogLine.h" + +void IProcessFile(const char *path) +{ + char out_path[512]; + strcpy(out_path, path); + strcat(out_path, ".decrypt"); + + FILE * fpIn = fopen(path, "rb"); + FILE * fpOut = fopen(out_path, "w"); + + if( fpIn != nil && fpOut != nil) + { + UInt8 line[ 2048 ]; + while( !feof( fpIn ) ) + { + // Read next string + long pos = ftell(fpIn); + if( pos == -1L ) + break; + UInt8 hint = (UInt8)pos; + UInt16 sizeHint = (UInt16)pos; + UInt16 size; + + if( stricmp( path + strlen( path ) - 4, ".log" ) == 0 ) + { + int i; + for( i = 0; i < 511; i++ ) + { + int c = fgetc( fpIn ); + if( c == EOF || c == hint ) + break; + line[ i ] = (UInt8)c; + } + line[ i ] = 0; + size = i; + } + else + { + // UInt16 line length is encoded first + int c = fgetc( fpIn ); + if( c == EOF ) + break; + size = ( c & 0xff ) | ( fgetc( fpIn ) << 8 ); + + size = size ^ sizeHint; + + if( size > sizeof( line ) ) + { + hsAssert( size <= sizeof( line ) - 1, "Invalid line size" ); + break; + } + + fread( line, 1, size, fpIn ); + line[ size ] = 0; + } + + plStatusEncrypt::Decrypt( line, size, hint ); + fprintf(fpOut, "%s\n", (const char *)line); + } + } + + if (fpIn) + fclose(fpIn); + if (fpOut) + fclose(fpOut); +} + +int main(int argc, const char * argv[]) +{ + if (argc == 2) + { + IProcessFile(argv[1]); + } + + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Intern.h new file mode 100644 index 00000000..5887046d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Intern.h @@ -0,0 +1,35 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plMD5/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLMD5_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/Apps/plMD5/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_APPS_PLMD5_INTERN_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Main.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Main.cpp new file mode 100644 index 00000000..de1e4e0e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Main.cpp @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plMD5/Main.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +int __cdecl main (int argc, char ** argv) { + + if (argc < 2) { + fprintf(stderr, "ERROR: Please specify filename.\n"); + return 1; + } + + plMD5Checksum md5(argv[1]); + if (!md5.IsValid()) { + fprintf(stderr, "ERROR: MD5 failed.\n"); + return 1; + } + + fprintf(stdout, "%s\n", md5.GetAsHexString()); + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Pch.h new file mode 100644 index 00000000..ed2119f4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plMD5/Pch.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plMD5/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLMD5_PCH_H +#error "Header $/Plasma20/Sources/Plasma/Apps/plMD5/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_APPS_PLMD5_PCH_H + +#include "pnUtils/pnUtils.h" +#include "pnProduct/pnProduct.h" +#include "plEncryption/plChecksum.h" + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageInfo/plAllCreatables.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageInfo/plAllCreatables.cpp new file mode 100644 index 00000000..81347d84 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageInfo/plAllCreatables.cpp @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" + +#include "../pnFactory/plCreator.h" + +#include "plAudible.h" +REGISTER_NONCREATABLE( plAudible ); + +#include "plDrawable.h" +REGISTER_NONCREATABLE( plDrawable ); + +#include "plPhysical.h" +REGISTER_NONCREATABLE( plPhysical ); + +#include "plgDispatch.h" +REGISTER_NONCREATABLE( plDispatchBase ); + +#include "../pnDispatch/pnDispatchCreatable.h" +#include "../pnKeyedObject/pnKeyedObjectCreatable.h" +#include "../pnMessage/pnMessageCreatable.h" +#include "../pnModifier/pnModifierCreatable.h" +#include "../pnNetCommon/pnNetCommonCreatable.h" +#include "../pnTimer/pnTimerCreatable.h" + +#include "../plResMgr/plResMgrCreatable.h" + +#include "../plMessage/plResMgrHelperMsg.h" +REGISTER_CREATABLE(plResMgrHelperMsg); + +#include "../plAudioCore/plAudioCoreCreatable.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageInfo/plPageInfo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageInfo/plPageInfo.cpp new file mode 100644 index 00000000..d722ed71 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageInfo/plPageInfo.cpp @@ -0,0 +1,254 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsUtils.h" +#include "hsTimer.h" +#include "../plFile/hsFiles.h" +#include "../plFile/plFileUtils.h" +#include "../plResMgr/plResManager.h" +#include "../plResMgr/plResMgrSettings.h" + +#include "../plAgeDescription/plAgeManifest.h" + +#include "../plResMgr/plRegistryHelpers.h" +#include "../plResMgr/plRegistryNode.h" + +#include "../plAudioCore/plSoundBuffer.h" +#include "hsStream.h" + +#include "../pnUtils/pnUtils.h" +#include "../pnProduct/pnProduct.h" + + +//// Globals ///////////////////////////////////////////////////////////////// + +plResManager* gResMgr = nil; + +bool DumpStats(const char* patchDir); +bool DumpSounds(); + +//// PrintVersion /////////////////////////////////////////////////////////////// +void PrintVersion() +{ + wchar productString[256]; + ProductString(productString, arrsize(productString)); + _putws(productString); +} + +//// PrintHelp /////////////////////////////////////////////////////////////// + +int PrintHelp( void ) +{ + puts(""); + PrintVersion(); + puts(""); + puts("Usage: plPageInfo [-s -i] pageFile"); + puts(" plPageInfo -v"); + puts("Where:" ); + puts(" -v print version and exit."); + puts(" -s dump sounds in page to the console"); + puts(" -i dump object size info to .csv files"); + puts(" pageFile is the path to the .prp file"); + puts(""); + + return -1; +} + +//// main //////////////////////////////////////////////////////////////////// + +int main(int argc, char* argv[]) +{ + if (argc >= 1 && hsStrEQ(argv[1], "-v")) + { + PrintVersion(); + return 0; + } + + if (argc < 3) + return PrintHelp(); + + bool sounds = false; + bool stats = false; + + int arg = 1; + for (arg = 1; arg < argc; arg++) + { + if (hsStrEQ(argv[arg], "-s")) + sounds = true; + else if (hsStrEQ(argv[arg], "-i")) + stats = true; + else + break; + } + + // Make sure we have 1 arg left after getting the options + char* pageFile = nil; + if (arg < argc) + pageFile = argv[arg]; + else + return PrintHelp(); + + // Init our special resMgr + plResMgrSettings::Get().SetFilterNewerPageVersions(false); + plResMgrSettings::Get().SetFilterOlderPageVersions(false); + plResMgrSettings::Get().SetLoadPagesOnInit(false); + gResMgr = TRACKED_NEW plResManager; + hsgResMgr::Init(gResMgr); + gResMgr->AddSinglePage(pageFile); + + if (sounds) + DumpSounds(); + if (stats) + { + char path[256]; + strcpy(path, pageFile); + plFileUtils::StripFile(path); + DumpStats(path); + } + + hsgResMgr::Shutdown(); + + return 0; +} + +//// plSoundBufferCollector ////////////////////////////////////////////////// +// Page iterator that collects all the plSoundBuffers in all of our pages + +class plSoundBufferCollector : public plRegistryPageIterator, public plKeyCollector +{ +public: + plSoundBufferCollector(hsTArray& keyArray) + : plKeyCollector(keyArray) {} + + hsBool EatPage(plRegistryPageNode* page) + { + page->LoadKeys(); + return page->IterateKeys(this, plSoundBuffer::Index()); + return true; + } +}; + + +bool DumpSounds() +{ + hsTArray soundKeys; + + plSoundBufferCollector soundCollector(soundKeys); + gResMgr->IterateAllPages(&soundCollector); + + for (int i = 0; i < soundKeys.GetCount(); i++) + { + plSoundBuffer* buffer = plSoundBuffer::ConvertNoRef(soundKeys[i]->VerifyLoaded()); + if (buffer) + { + // Ref it... + buffer->GetKey()->RefObject(); + + // Get the filename from it and add that file if necessary + const char* filename = buffer->GetFileName(); + if (filename) + { + UInt32 flags = 0; + + if (stricmp(plFileUtils::GetFileExt(filename), "wav") != 0) + { + if (buffer->HasFlag(plSoundBuffer::kOnlyLeftChannel) || + buffer->HasFlag(plSoundBuffer::kOnlyRightChannel)) + hsSetBits(flags, plManifestFile::kSndFlagCacheSplit); + else if (buffer->HasFlag(plSoundBuffer::kStreamCompressed)) + hsSetBits(flags, plManifestFile::kSndFlagStreamCompressed); + else + hsSetBits(flags, plManifestFile::kSndFlagCacheStereo); + } + + printf("%s,%u\n", filename, flags); + } + + // Unref the object so it goes away + buffer->GetKey()->UnRefObject(); + } + } + + soundKeys.Reset(); + plIndirectUnloadIterator iter; + gResMgr->IterateAllPages(&iter); + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +#include "../pnKeyedObject/plKeyImp.h" + +class plStatDumpIterator : public plRegistryPageIterator, public plRegistryKeyIterator +{ +protected: + const char* fOutputDir; + hsUNIXStream fStream; + +public: + plStatDumpIterator(const char* outputDir) : fOutputDir(outputDir) {} + + hsBool EatKey(const plKey& key) + { + plKeyImp* imp = (plKey)key; + + fStream.WriteString(imp->GetName()); + fStream.WriteString(","); + + fStream.WriteString(plFactory::GetNameOfClass(imp->GetUoid().GetClassType())); + fStream.WriteString(","); + + char buf[30]; + sprintf(buf, "%u", imp->GetDataLen()); + fStream.WriteString(buf); + fStream.WriteString("\n"); + + return true; + } + + hsBool EatPage(plRegistryPageNode* page) + { + const plPageInfo& info = page->GetPageInfo(); + + char fileName[256]; + sprintf(fileName, "%s%s_%s.csv", fOutputDir, info.GetAge(), info.GetPage()); + fStream.Open(fileName, "wt"); + + page->LoadKeys(); + page->IterateKeys(this); + + fStream.Close(); + + return true; + } +}; + +bool DumpStats(const char* patchDir) +{ + plStatDumpIterator statDump(patchDir); + gResMgr->IterateAllPages(&statDump); + return true; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/main.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/main.cpp new file mode 100644 index 00000000..1127126e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/main.cpp @@ -0,0 +1,105 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../plResMgr/plResManager.h" +#include "../pfPython/plPythonFileMod.h" +#include "../plGImage/plFontCache.h" +#include "../plPhysX/plSimulationMgr.h" +#include "../plAvatar/plAvatarMgr.h" + +#include "plPageOptimizer.h" +#include "../plFile/plFileUtils.h" + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + printf("plPageOptimizer: wrong number of arguments"); + return 1; + } + + printf("Optimizing %s...", plFileUtils::GetFileName(argv[1])); + + plFontCache* fontCache; +#ifndef _DEBUG + try + { +#endif + plResManager* resMgr = TRACKED_NEW plResManager; + hsgResMgr::Init(resMgr); + + // Setup all the crap that needs to be around to load + plSimulationMgr::Init(); + fontCache = TRACKED_NEW plFontCache; + plPythonFileMod::SetAtConvertTime(); +#ifndef _DEBUG + } catch (...) + { + printf(" ***crashed on init"); + return 2; + } +#endif + +#ifndef _DEBUG + try +#endif + { + plPageOptimizer optimizer(argv[1]); + optimizer.Optimize(); + } +#ifndef _DEBUG + catch (...) + { + printf(" ***crashed on optimizing"); + return 2; + } +#endif + +#ifndef _DEBUG + try + { +#endif + // Deinit the crap + fontCache->UnRegisterAs(kFontCache_KEY); + fontCache = nil; + plSimulationMgr::Shutdown(); + + // Reading in objects may have generated dirty state which we're obviously + // not sending out. Clear it so that we don't have leaked keys before the + // ResMgr goes away. + std::vector carryOvers; + plSynchedObject::ClearDirtyState(carryOvers); + + hsgResMgr::Shutdown(); +#ifndef _DEBUG + } catch (...) + { + printf(" ***crashed on shutdown"); + return 2; + } +#endif + + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/pfAllCreatables.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/pfAllCreatables.cpp new file mode 100644 index 00000000..df169f17 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/pfAllCreatables.cpp @@ -0,0 +1,38 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../pfCharacter/pfCharacterCreatable.h" +#include "../pfCamera/pfCameraCreatable.h" +#include "../pfAnimation/pfAnimationCreatable.h" +#include "../pfConditional/plConditionalObjectCreatable.h" +//#include "../pfConsole/pfConsoleCreatable.h" +#include "../pfSurface/pfSurfaceCreatable.h" +#include "../pfMessage/pfMessageCreatable.h" +#include "../pfAudio/pfAudioCreatable.h" +#include "../pfPython/pfPythonCreatable.h" +#include "../pfGameGUIMgr/pfGameGUIMgrCreatable.h" +#include "../pfCCR/plCCRCreatable.h" +#include "../pfJournalBook/pfJournalBookCreatable.h" +#include "../pfGameMgr/pfGameMgrCreatables.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/plPageOptimizer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/plPageOptimizer.cpp new file mode 100644 index 00000000..68082f10 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/plPageOptimizer.cpp @@ -0,0 +1,269 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPageOptimizer.h" + +#include "../pnKeyedObject/plUoid.h" +#include "../plResMgr/plResManager.h" +#include "../plResMgr/plRegistryHelpers.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plResMgr/plRegistryNode.h" + +#include "../pnFactory/plFactory.h" +#include "../pnKeyedObject/plKeyImp.h" + +#include "../plFile/plFileUtils.h" +#include "hsStream.h" + +plPageOptimizer* plPageOptimizer::fInstance = nil; + +plPageOptimizer::plPageOptimizer(const char* pagePath) : + fOptimized(true), + fPageNode(nil), + fPagePath(pagePath) +{ + fInstance = this; + + strcpy(fTempPagePath, fPagePath); + plFileUtils::StripExt(fTempPagePath); + strcat(fTempPagePath, "_opt.prp"); + + fResMgr = (plResManager*)hsgResMgr::ResMgr(); +} + +void plPageOptimizer::IFindLoc() +{ + class plPageIt : public plRegistryPageIterator + { + public: + plLocation fLoc; + + virtual hsBool EatPage(plRegistryPageNode* keyNode) + { + fLoc = keyNode->GetPageInfo().GetLocation(); + return true; + } + }; + + plPageIt it; + fResMgr->IterateAllPages(&it); + fLoc = it.fLoc; +} + +void plPageOptimizer::Optimize() +{ + fResMgr->AddSinglePage(fPagePath); + + // Get the location of the page we're optimizing + IFindLoc(); + + hsBool loaded = true; + + // Get the key for the scene node, we'll load it to force a load on all the objects + plKey snKey = plKeyFinder::Instance().FindSceneNodeKey(fLoc); + if (snKey) + { + // Load all the keys + fPageNode = fResMgr->FindPage(fLoc); + fResMgr->LoadPageKeys(fPageNode); + + // Put all the keys in a vector, so they won't get unreffed + class plVecKeyCollector : public plRegistryKeyIterator + { + public: + KeyVec& fKeys; + plVecKeyCollector(KeyVec& keys) : fKeys(keys) {} + virtual hsBool EatKey(const plKey& key) { fKeys.push_back(key); return true; } + }; + plVecKeyCollector keyIt(fAllKeys); + fResMgr->IterateKeys(&keyIt); + + // Set our load proc, which will track the order that objects are loaded + fResMgr->SetProgressBarProc(KeyedObjectProc); + + // Load the page + snKey->VerifyLoaded(); + + // Unload everything + snKey->RefObject(); + snKey->UnRefObject(); + snKey = nil; + } + else + { + loaded = false; + } + + if (loaded) + IRewritePage(); + + UInt32 oldSize = plFileUtils::GetFileSize(fPagePath); + UInt32 newSize = plFileUtils::GetFileSize(fTempPagePath); + + if (!loaded) + { + printf("no scene node.\n"); + } + else if (fOptimized) + { + plFileUtils::RemoveFile(fTempPagePath); + printf("already optimized.\n"); + } + else if (oldSize == newSize) + { + plFileUtils::RemoveFile(fPagePath, true); + plFileUtils::FileMove(fTempPagePath, fPagePath); + + printf("complete\n"); + } + else + { + plFileUtils::RemoveFile(fTempPagePath); + printf("failed. File sizes different\n"); + } +} + +void plPageOptimizer::KeyedObjectProc(plKey key) +{ + const char* keyName = key->GetName(); + const char* className = plFactory::GetNameOfClass(key->GetUoid().GetClassType()); + + // For now, ignore any key that isn't in the location we're looking at. That means stuff like textures. + if (fInstance->fLoc != key->GetUoid().GetLocation()) + return; + + KeySet& loadedKeys = fInstance->fLoadedKeys; + KeyVec& loadOrder = fInstance->fKeyLoadOrder; + + KeySet::iterator it = loadedKeys.lower_bound(key); + if (it != loadedKeys.end() && *it == key) + { + printf("Keyed object %s(%s) loaded more than once\n", keyName, className); + } + else + { + loadedKeys.insert(it, key); + loadOrder.push_back(key); + } +} + +void plPageOptimizer::IWriteKeyData(hsStream* oldPage, hsStream* newPage, plKey key) +{ + class plUpdateKeyImp : public plKeyImp + { + public: + void SetStartPos(UInt32 startPos) { fStartPos = startPos; } + }; + + plUpdateKeyImp* keyImp = (plUpdateKeyImp*)(plKeyImp*)key; + UInt32 startPos = keyImp->GetStartPos(); + UInt32 len = keyImp->GetDataLen(); + + oldPage->SetPosition(startPos); + if (len > fBuf.size()) + fBuf.resize(len); + oldPage->Read(len, &fBuf[0]); + + UInt32 newStartPos = newPage->GetPosition(); + + // If we move any buffers, this page wasn't optimized already + if (newStartPos != startPos) + fOptimized = false; + + keyImp->SetStartPos(newStartPos); + newPage->Write(len, &fBuf[0]); +} + +void plPageOptimizer::IRewritePage() +{ + hsUNIXStream newPage; + + if (newPage.Open(fTempPagePath, "wb")) + { + hsUNIXStream oldPage; + oldPage.Open(fPagePath); + + const plPageInfo& pageInfo = fPageNode->GetPageInfo(); + + UInt32 dataStart = pageInfo.GetDataStart(); + + fBuf.resize(dataStart); + + oldPage.Read(dataStart, &fBuf[0]); + newPage.Write(dataStart, &fBuf[0]); + + int size = (int)fKeyLoadOrder.size(); + for (int i = 0; i < size; i++) + IWriteKeyData(&oldPage, &newPage, fKeyLoadOrder[i]); + + // If there are any objects that we didn't write (because they didn't load for + // some reason), put them at the end + for (int i = 0; i < fAllKeys.size(); i++) + { + hsBool found = (fLoadedKeys.find(fAllKeys[i]) != fLoadedKeys.end()); + if (!found) + IWriteKeyData(&oldPage, &newPage, fAllKeys[i]); + } + + UInt32 newKeyStart = newPage.GetPosition(); + UInt32 oldKeyStart = pageInfo.GetIndexStart(); + oldPage.SetPosition(oldKeyStart); + + UInt32 numTypes = oldPage.ReadSwap32(); + newPage.WriteSwap32(numTypes); + + for (UInt32 i = 0; i < numTypes; i++) + { + UInt16 classType = oldPage.ReadSwap16(); + UInt32 len = oldPage.ReadSwap32(); + UInt8 flags = oldPage.ReadByte(); + UInt32 numKeys = oldPage.ReadSwap32(); + + newPage.WriteSwap16(classType); + newPage.WriteSwap32(len); + newPage.WriteByte(flags); + newPage.WriteSwap32(numKeys); + + for (UInt32 j = 0; j < numKeys; j++) + { + plUoid uoid; + uoid.Read(&oldPage); + UInt32 startPos = oldPage.ReadSwap32(); + UInt32 dataLen = oldPage.ReadSwap32(); + + // Get the new start pos + plKeyImp* key = (plKeyImp*)fResMgr->FindKey(uoid); + startPos = key->GetStartPos(); + + uoid.Write(&newPage); + newPage.WriteSwap32(startPos); + newPage.WriteSwap32(dataLen); + } + } + + newPage.Close(); + oldPage.Close(); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/plPageOptimizer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/plPageOptimizer.h new file mode 100644 index 00000000..ef326445 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPageOptimizer/plPageOptimizer.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPageOptimizer_h_inc +#define plPageOptimizer_h_inc + +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plUoid.h" +#include +#include + +class plRegistryPageNode; +class plResManager; + +class plPageOptimizer +{ +protected: + typedef std::vector KeyVec; + typedef std::set KeySet; + + KeyVec fKeyLoadOrder; // The order objects were loaded in + KeySet fLoadedKeys; // Keys we've loaded objects for, for quick lookup + KeyVec fAllKeys; // All the keys in the page + std::vector fBuf; + + bool fOptimized; // True after optimization if the page was already optimized + + const char* fPagePath; // Path to our page + char fTempPagePath[512]; // Path to the temp output page + plLocation fLoc; // Location of our page + plRegistryPageNode* fPageNode; // PageNode for our page + + plResManager* fResMgr; + + static plPageOptimizer* fInstance; + static void KeyedObjectProc(plKey key); + + void IWriteKeyData(hsStream* oldPage, hsStream* newPage, plKey key); + void IFindLoc(); + void IRewritePage(); + +public: + plPageOptimizer(const char* pagePath); + + void Optimize(); +}; + +#endif // plPageOptimizer_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/Dirt.ICO b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/Dirt.ICO new file mode 100644 index 0000000000000000000000000000000000000000..057c608327e942bc63b2ca831c4b7bc791e545b0 GIT binary patch literal 2550 zcmd5;eOQy%75_oVhYcTrUNA;OYs`cCDCnl5-3IISxV%oy4#|e|sekT|-Q;)Ax#xG@ zbMHC#=Dq=JU<)4~SlVeGu!uB)Kwud!wgE=TwwOjfgf|H`AOkgxoke`F<8lFCU%|`ZM_Pc^D@q?|a1pK62_=bAIjpp-O=!~V_Bo{ct&&v)=7TJOGnj0|B69`F#P}2^? zZbw+O3xqBJFHr&fC?D*sDzIGK!He2Rv$7Bt7J&l?06%|6sJ7ccR5JkQqGNC=`!yVk zjzF-t#gf#@h(yfy^v8Qmow)SiJIwcZ14%yyL?XbsvIo5I1ULx9aB%U4x33fh6(69X z^8(IZybJx!zvBAe%-E2<10Eird4N5oc37Ak0b#`_-~_Fpgl(yIEI8W-VcDuuByOm| zro0A}9UR0jPh6x}SJCqMO`JE*5FX)+n={~29Sf+Q@$rC@V>p7ttB{oOKJxaQMMcAx zINUyp=7AeH^Sis~y>cG|<9|YGDzJGI7p?(P2+G@FL!03wP{GwL25XZJL6P5v5^W#) z4EGTri$&hgfpsZtxO+vx%_AORQ8ma=Hln`m3Yz+^Y8f(&|p^eD@+U zihc_xS6{e$%BdE0NK9AbXX-k#S7UwF7Rc9Sp!rNMj($1}ZwZj8+<|kKCeUUCh# zULJoR@Ls^dg=^p93&Xd_QtR+W@Fp}Kufm->Mr_Lis!w#`^1XkefAk^wJiyWRZ=n9w z5M0E8@R7a+duJ&s_Z1>DI~$$7{V1<#!ky=4?AQSeUY)``z5@yif#M?Cv)A~DPE5s@ z7VGnF=ZBco&h31<=-`G9W*3xg?ex9W!%>P&8 zvKY32%TpQnLWW_zyf%S=;qv)>p^(pW<<1wmiCtgf1@0aaPcQEUKE8hbuQLl%q>K3h z|K%XyE|f45iFEc8=n=St31(!17ko(Q{3XjgB+GxeLK5c2tXvhmdQF5d@&(Thcs(#` zEhBOlMMuZP#>U0V6B60H|L}&;Tn7cJolecY~zsx#8fQpwd4O`2+UC1y++Ts$mm7gdLwepcFRp7EfX18_^v!2nG z?^WmO>G^EUD%V+_<*KSU5cLjI*{rRKu8!VQo}(oGpi%Rlh)29ZCFDs1ncBLNoWo50 zk#a^`tkiGKjEk*{YfusIdc5((^2QH7Z2D+#Gjp<~_0(?;x1DbPxZ#tI&NGHch9#3V z1TtOy-RWv|PjC0R)?#LT&v0!=UswP60Y=6!>_Ks3;sxW-R{hE0uMX;4S|fH7Kl1y_ zqhnXTX4CpqR}IlddH>MK;f{V|cY5n#X1w-#*X6OXu^U~M`h{LKm~I(w$8_}@C*m_G zK+mb`H}2hkF!iw4!m~nU0Vbn-YFchI8Z*RZNmz0$g_7v!uK`fS>?=J9YGEpfxkjCI74B}bbQ9`_DR$Y04b+)$Rm5Y2pb zOa7|I=WNqY=G6a%Ligg!%>38Uo$ybD%Mc?5s%w z`LW1<-%|?roM4`XhM?n`&3`yVb52<1T}%N41RsJ8tbb5+q`$gp=uZqW8jV#@Pbzde z9m!dOR!d*H*sOwjQZ4r!S-;;DidkDQz4%CMVTD4Y0LLHV(ozK!!2+?CB#XrpYxgM- zR>fmgX_2cf)9&Z(6L6f$N;TXnKv7&;CZ-HHPTaEDXi&_QS|kTri?nvubpTJPS|-8_ aVUnODXb6. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plPlasmaInstaller.h" +#include "jvCoreUtil.h" + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + jvCoreUtil::SetHInstance(hInstance); + plPlasmaInstaller installer; + installer.Create(); + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + if (!IsDialogMessage(installer.GetHWnd(), &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return msg.wParam; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plInstallerReg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plInstallerReg.cpp new file mode 100644 index 00000000..6c23b359 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plInstallerReg.cpp @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plInstallerReg.h" +#include + +static HKEY GetInstallerKey() +{ + HKEY hSoftKey = NULL; + HKEY hCompanyKey = NULL; + HKEY hAppKey = NULL; + + if(RegOpenKeyEx(HKEY_CURRENT_USER, "software", 0, KEY_WRITE|KEY_READ, + &hSoftKey) == ERROR_SUCCESS) + { + DWORD dw; + if(RegCreateKeyEx(hSoftKey, "Cyan", 0, REG_NONE, + REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, + &hCompanyKey, &dw) == ERROR_SUCCESS) + { + RegCreateKeyEx(hCompanyKey, "PlasmaInstaller", 0, REG_NONE, + REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, + &hAppKey, &dw); + } + } + + if (hSoftKey != NULL) + RegCloseKey(hSoftKey); + if (hCompanyKey != NULL) + RegCloseKey(hCompanyKey); + + return hAppKey; +} + +static void WriteRegString(const char* valueName, const char* value) +{ + HKEY installKey = GetInstallerKey(); + RegSetValueEx(installKey, valueName, 0, REG_SZ, (const BYTE*)value, strlen(value)+1); + RegCloseKey(installKey); +} + +static bool ReadRegString(const char* valueName, char* value, DWORD size) +{ + HKEY installKey = GetInstallerKey(); + bool ret = (RegQueryValueEx(installKey, valueName, NULL, NULL, (LPBYTE)value, &size) == ERROR_SUCCESS); + RegCloseKey(installKey); + return ret; +} + +void plInstallerReg::SetClientDir(const char* dir) +{ + WriteRegString("Client", dir); +} + +void plInstallerReg::SetMaxDir(const char* dir) +{ + WriteRegString("3dsmax", dir); +} + +const char* plInstallerReg::GetClientDir() +{ + static char dir[MAX_PATH]; + if (!ReadRegString("Client", dir, sizeof(dir))) + strcpy(dir, "C:\\PlasmaClient"); + return dir; +} + +const char* plInstallerReg::GetMaxDir() +{ + static char dir[MAX_PATH]; + dir[0] = '\0'; + ReadRegString("3dsmax", dir, sizeof(dir)); + return dir; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plInstallerReg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plInstallerReg.h new file mode 100644 index 00000000..8834a5e1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plInstallerReg.h @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +namespace plInstallerReg +{ + void SetClientDir(const char* dir); + void SetMaxDir(const char* dir); + + const char* GetClientDir(); + const char* GetMaxDir(); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.cpp new file mode 100644 index 00000000..5225dfc7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.cpp @@ -0,0 +1,392 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plPlasmaInstaller.h" +#include "resource.h" +#include +#include + +#include "../plFile/hsFiles.h" +#include "plUnzip.h" +#include "plInstallerReg.h" +#include "../plFile/plBrowseFolder.h" +#include "plSetPlasmaPath.h" + +plPlasmaInstaller::plPlasmaInstaller() +{ + fDailyDir[0] = '\0'; + fDidGet = false; + fStatusList = nil; + + INITCOMMONCONTROLSEX icc = {0}; + icc.dwSize = sizeof(INITCOMMONCONTROLSEX); + icc.dwICC = ICC_DATE_CLASSES; + InitCommonControlsEx(&icc); +} + +void plPlasmaInstaller::Create() +{ + ICreateDialog(IDD_INSTALLER, NULL); +} + +static const char* kAllClientExes = "AllClientExes.zip"; +static const char* kAllDllsRelease = "AllDllsRelease.zip"; +static const char* kScripts = "Scripts.zip"; +static const char* kTools = "AllToolsRelease.zip"; + +bool FileExists(const char* path, const char* filename) +{ + char fullpath[MAX_PATH]; + sprintf(fullpath, "%s%s", path, filename); + HANDLE hFile = CreateFile(fullpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + CloseHandle(hFile); + return true; + } + + return false; +} + +bool plPlasmaInstaller::IGetDailyDir() +{ + // Get the branch + HWND hBuild = GetDlgItem(fDlg, IDC_BUILD_COMBO); + int idx = ComboBox_GetCurSel(hBuild); + int buildServer = ComboBox_GetItemData(hBuild, idx); + + HWND hTime = GetDlgItem(fDlg, IDC_TIME_COMBO); + idx = ComboBox_GetCurSel(hTime); + int time = ComboBox_GetItemData(hTime, idx); + + // Get the build date + SYSTEMTIME date; + DateTime_GetSystemtime(GetDlgItem(fDlg, IDC_BRANCH_DATE), &date); + char dateStr[] = "xx-xx-xxxx"; + sprintf(dateStr, "%02d-%02d-%04d", date.wMonth, date.wDay, date.wYear); + + fDailyDir[0] = '\0'; + + IAddStatusLine("Searching for %s build...", dateStr); + + + char buildDir[MAX_PATH]; + + static const char* kMainBuild = "\\\\Plasmabuild\\Output\\"; + static const char* kBranchBuild = "\\\\Branchbuild\\Output\\"; + static const char* kActiveBuild = "\\\\Activebuild\\Output\\"; + static const char* kInternalMain = "Main-Internal\\"; + static const char* kInternalBranch = "Branch-Internal\\"; + static const char* kInternalActive = "Active-Internal\\"; + + switch (buildServer) + { + case kBuildMain: strcpy(buildDir, kMainBuild); break; + case kBuildBranch: strcpy(buildDir, kBranchBuild); break; + case kBuildActive: strcpy(buildDir, kActiveBuild); break; + } + + switch (time) + { + case kNightly: + strcat(buildDir, "Nightly\\"); + break; + case kAfternoon: + strcat(buildDir, "Afternoon\\"); + break; + case kEvening: + strcat(buildDir, "Evening\\"); + break; + } + + strcat(buildDir, dateStr); + strcat(buildDir, "\\"); + + switch (buildServer) + { + case kBuildMain: strcat(buildDir, kInternalMain); break; + case kBuildBranch: strcat(buildDir, kInternalBranch); break; + case kBuildActive: strcat(buildDir, kInternalActive); break; + } + + if (FileExists(buildDir, kAllClientExes) && FileExists(buildDir, kAllDllsRelease) && FileExists(buildDir, kScripts)) + { + strcpy(fDailyDir, buildDir); + + const char* serverName = nil; + switch (buildServer) + { + case kBuildMain: serverName = "Main"; break; + case kBuildBranch: serverName = "Branch"; break; + case kBuildActive: serverName = "Active"; break; + } + IAddStatusLine("Found %s at %s", serverName, fDailyDir); + + EnableWindow(GetDlgItem(fDlg, IDC_GET_BUTTON), TRUE); + return true; + } + + IAddStatusLine("Couldn't find build"); + EnableWindow(GetDlgItem(fDlg, IDC_GET_BUTTON), FALSE); + return false; +} + +void plPlasmaInstaller::IInit() +{ + const char* clientDir = plInstallerReg::GetClientDir(); + SetDlgItemText(fDlg, IDC_CLIENT_EDIT, clientDir); + + const char* maxDir = plInstallerReg::GetMaxDir(); + SetDlgItemText(fDlg, IDC_3DSMAX_EDIT, maxDir); + + fStatusList = GetDlgItem(fDlg, IDC_STATUS_LIST); + + HWND hCombo = GetDlgItem(fDlg, IDC_BUILD_COMBO); + int idx = ComboBox_AddString(hCombo, "Main"); + ComboBox_SetItemData(hCombo, idx, kBuildMain); + ComboBox_SetCurSel(hCombo, idx); + idx = ComboBox_AddString(hCombo, "Branch"); + ComboBox_SetItemData(hCombo, idx, kBuildBranch); + idx = ComboBox_AddString(hCombo, "Active"); + ComboBox_SetItemData(hCombo, idx, kBuildActive); + + HWND hTime = GetDlgItem(fDlg, IDC_TIME_COMBO); + idx = ComboBox_AddString(hTime, "Nightly"); + ComboBox_SetItemData(hTime, idx, kNightly); + ComboBox_SetCurSel(hTime, idx); + idx = ComboBox_AddString(hTime, "Afternoon"); + ComboBox_SetItemData(hTime, idx, kAfternoon); + idx = ComboBox_AddString(hTime, "Evening"); + ComboBox_SetItemData(hTime, idx, kEvening); + + CheckDlgButton(fDlg, IDC_CLIENT_CHECK, BST_CHECKED); + CheckDlgButton(fDlg, IDC_SCRIPTS_CHECK, BST_CHECKED); + CheckDlgButton(fDlg, IDC_PLUGINS_CHECK, BST_CHECKED); + + ShowWindow(fDlg, SW_SHOW); + + IGetDailyDir(); +} + +BOOL plPlasmaInstaller::IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + IInit(); + SetFocus(GetDlgItem(fDlg, IDC_GET_BUTTON)); + return FALSE; + + case WM_CLOSE: + DestroyWindow(hDlg); + return TRUE; + + case WM_DESTROY: + PostQuitMessage(0); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + switch (LOWORD(wParam)) + { + case IDCANCEL: + PostMessage(hDlg, WM_CLOSE, 0, 0); + return TRUE; + + case IDC_BROWSE_3DSMAX: + case IDC_BROWSE_CLIENT: + IGetFolder(LOWORD(wParam) == IDC_BROWSE_CLIENT); + return TRUE; + + case IDC_GET_BUTTON: + if (fDidGet) + PostMessage(hDlg, WM_CLOSE, 0, 0); + else + IGet(); + return TRUE; + } + } + else if (HIWORD(wParam) == CBN_SELCHANGE && (LOWORD(wParam) == IDC_TIME_COMBO || LOWORD(wParam) == IDC_BUILD_COMBO)) + { + IGetDailyDir(); + return TRUE; + } + break; + + case WM_NOTIFY: + { + NMHDR* nmhdr = (NMHDR*)lParam; + if (nmhdr->idFrom == IDC_BRANCH_DATE && nmhdr->code == DTN_CLOSEUP/*DTN_DATETIMECHANGE*/) + { + IGetDailyDir(); + return TRUE; + } + } + break; + } + + return FALSE; +} + +void plPlasmaInstaller::IExtractZip(const char* filename, const char* dest) +{ + plUnzip unzip; + if (unzip.Open(filename)) + { + IAddStatusLine("Extracting %s...", filename); + + char buf[MAX_PATH]; + while (unzip.ExtractNext(dest, buf)) + IAddStatusLine(" %s", buf); + IAddStatusLine(" %s", buf); + + unzip.Close(); + } +} + +void plPlasmaInstaller::IGet() +{ + bool getClient = (IsDlgButtonChecked(fDlg, IDC_CLIENT_CHECK) == BST_CHECKED); + bool getScripts = (IsDlgButtonChecked(fDlg, IDC_SCRIPTS_CHECK) == BST_CHECKED); + bool getPlugins = (IsDlgButtonChecked(fDlg, IDC_PLUGINS_CHECK) == BST_CHECKED); + bool getTools = (IsDlgButtonChecked(fDlg, IDC_TOOLS_CHECK) == BST_CHECKED); + + const char* clientDir = plInstallerReg::GetClientDir(); + if (*clientDir == '\0' && (getClient || getScripts)) + { + MessageBox(fDlg, "You need to set your client directory", "Plasma Installer", MB_OK | MB_ICONASTERISK); + return; + } + const char* maxDir = plInstallerReg::GetMaxDir(); + if (*maxDir == '\0' && getPlugins) + { + MessageBox(fDlg, "You need to set your 3dsmax directory", "Plasma Installer", MB_OK | MB_ICONASTERISK); + return; + } + + HWND hGetButton = GetDlgItem(fDlg, IDC_GET_BUTTON); + EnableWindow(hGetButton, FALSE); + HCURSOR hOldCursor = SetCursor(LoadCursor(NULL, IDC_WAIT)); + + char buf[MAX_PATH]; + + if (getScripts) + { + sprintf(buf, "%s%s", fDailyDir, kScripts); + IExtractZip(buf, clientDir); + } + + if (getClient) + { + sprintf(buf, "%s%s", fDailyDir, kAllClientExes); + IExtractZip(buf, clientDir); + } + + if (getPlugins) + { + sprintf(buf, "%s%s", fDailyDir, kAllDllsRelease); + char pluginDir[MAX_PATH]; + sprintf(pluginDir, "%s\\plugins", maxDir); + IExtractZip(buf, pluginDir); + + IAddStatusLine("Updating PlasmaMAX2.ini..."); + sprintf(buf, "%s\\plugcfg\\PlasmaMAX2.ini", maxDir); + WritePrivateProfileString("SceneViewer", "Directory", clientDir, buf); + } + + if (getTools) + { + sprintf(buf, "%s%s", fDailyDir, kTools); + + char toolBuf[MAX_PATH]; + sprintf(toolBuf, "%s\\Tools", clientDir); + IExtractZip(buf, toolBuf); + } + + IAddStatusLine("Updating path..."); + SetPlasmaPath(clientDir); + + IAddStatusLine("Done"); + + SetCursor(hOldCursor); + + fDidGet = true; + SetWindowText(hGetButton, "Close"); + EnableWindow(hGetButton, TRUE); +} + +void plPlasmaInstaller::IGetFolder(bool client) +{ + char path[MAX_PATH]; + if (client) + strcpy(path, plInstallerReg::GetClientDir()); + else + strcpy(path, plInstallerReg::GetMaxDir()); + + if (plBrowseFolder::GetFolder(path, path)) + { + if (client) + { + SetDlgItemText(fDlg, IDC_CLIENT_EDIT, path); + plInstallerReg::SetClientDir(path); + } + else + { + SetDlgItemText(fDlg, IDC_3DSMAX_EDIT, path); + plInstallerReg::SetMaxDir(path); + } + } +} + +void plPlasmaInstaller::IAddStatusLine(const char* format, ...) +{ + if (!format || *format == '\0') + return; + + va_list args; + va_start(args, format); + + char buf[2048]; + int numWritten = _vsnprintf(buf, sizeof(buf), format, args); + hsAssert(numWritten > 0, "Buffer too small"); + + va_end(args); + + int idx = ListBox_AddString(fStatusList, buf); + ListBox_SetCurSel(fStatusList, idx); + + // Pump the message queue + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + if (!IsDialogMessage(&msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.h new file mode 100644 index 00000000..5710dd50 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "jvBaseDlg.h" + +class plPlasmaInstaller : public jvBaseDlg +{ +protected: + char fDailyDir[MAX_PATH]; + bool fDidGet; + + HWND fStatusList; + + enum { kBuildMain, kBuildBranch, kBuildActive }; + enum { kNightly, kAfternoon, kEvening }; + + virtual BOOL IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + bool IGetDailyDir(); + void IGetFolder(bool client); + + void IGet(); + + void IInit(); + void IExtractZip(const char* filename, const char* dest); + + void IAddStatusLine(const char* format, ...); + +public: + plPlasmaInstaller(); + + void Create(); + + HWND GetHWnd() { return fDlg; } +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.rc new file mode 100644 index 00000000..9a62ae13 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plPlasmaInstaller.rc @@ -0,0 +1,129 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_INSTALLER DIALOG DISCARDABLE 0, 0, 241, 170 +STYLE DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Plasma Installer" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Build:",IDC_STATIC,7,8,18,8 + COMBOBOX IDC_TIME_COMBO,27,6,46,167,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "3dsmax Directory:",IDC_STATIC,7,26,57,8 + EDITTEXT IDC_3DSMAX_EDIT,7,36,173,12,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Browse...",IDC_BROWSE_3DSMAX,183,35,50,14 + LTEXT "Client Directory:",IDC_STATIC,7,53,50,8 + EDITTEXT IDC_CLIENT_EDIT,7,63,173,12,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Browse...",IDC_BROWSE_CLIENT,183,62,50,14 + DEFPUSHBUTTON "Get",IDC_GET_BUTTON,93,146,56,16,WS_DISABLED + LISTBOX IDC_STATUS_LIST,7,85,226,54,LBS_NOSEL | WS_VSCROLL | + WS_TABSTOP + CONTROL "DateTimePicker1",IDC_BRANCH_DATE,"SysDateTimePick32", + DTS_RIGHTALIGN | WS_TABSTOP,125,6,55,12 + COMBOBOX IDC_BUILD_COMBO,76,6,46,167,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Client",IDC_CLIENT_CHECK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,159,144,33,10 + CONTROL "Scripts",IDC_SCRIPTS_CHECK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,159,154,37,10 + CONTROL "Plugins",IDC_PLUGINS_CHECK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,197,144,39,10 + CONTROL "Tools",IDC_TOOLS_CHECK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,197,154,33,10 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_INSTALLER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 234 + TOPMARGIN, 7 + BOTTOMMARGIN, 163 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON DISCARDABLE "Dirt.ICO" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plSetPlasmaPath.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plSetPlasmaPath.cpp new file mode 100644 index 00000000..0e97fdd1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plSetPlasmaPath.cpp @@ -0,0 +1,104 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include + +static HKEY GetEnvironKey() +{ + HKEY hSystemKey = NULL; + HKEY hControlSetKey = NULL; + HKEY hControlKey = NULL; + HKEY hSessionKey = NULL; + HKEY hEnvironKey = NULL; + + if ((RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM", 0, KEY_READ, &hSystemKey) == ERROR_SUCCESS) && + (RegOpenKeyEx(hSystemKey, "CurrentControlSet", 0, KEY_READ, &hControlSetKey) == ERROR_SUCCESS) && + (RegOpenKeyEx(hControlSetKey, "Control", 0, KEY_READ, &hControlKey) == ERROR_SUCCESS) && + (RegOpenKeyEx(hControlKey, "Session Manager", 0, KEY_READ, &hSessionKey) == ERROR_SUCCESS)) + { + RegOpenKeyEx(hSessionKey, "Environment", 0, KEY_READ | KEY_WRITE, &hEnvironKey); + } + + if (hSystemKey != NULL) + RegCloseKey(hSystemKey); + if (hControlSetKey != NULL) + RegCloseKey(hControlSetKey); + if (hControlKey != NULL) + RegCloseKey(hControlKey); + if (hSessionKey != NULL) + RegCloseKey(hSessionKey); + + return hEnvironKey; +} + +void SetPlasmaPath(const char* plasmaPath) +{ + bool pathSet = false; + + HKEY hEnvironKey = GetEnvironKey(); + if (hEnvironKey) + { + // Make sure the PlasmaGameDir var is in the path + DWORD size = 0; + if (ERROR_SUCCESS == RegQueryValueEx(hEnvironKey, "Path", NULL, NULL, NULL, &size)) + { + char* oldPath = new char[size]; + static const char* kPlasmaVar = "%PlasmaGameDir%"; + + if (ERROR_SUCCESS == RegQueryValueEx(hEnvironKey, "Path", NULL, NULL, (BYTE*)oldPath, &size)) + { + pathSet = (strstr(oldPath, kPlasmaVar) != NULL); + + if (!pathSet) + { + char* newPath = new char[size+strlen(kPlasmaVar)+1]; + strcpy(newPath, oldPath); + strcat(newPath, ";"); + strcat(newPath, kPlasmaVar); + + RegSetValueEx(hEnvironKey, "Path", 0, REG_EXPAND_SZ, (BYTE*)newPath, strlen(newPath)+1); + + delete [] newPath; + } + } + + delete [] oldPath; + } + + // Set the PlasmaGameDir var + RegSetValueEx(hEnvironKey, "PlasmaGameDir", 0, REG_SZ, (BYTE*)plasmaPath, strlen(plasmaPath)+1); + + // Notify command prompts and stuff that environ changed + DWORD ret; + SendMessageTimeout(HWND_BROADCAST, + WM_SETTINGCHANGE, + 0, + (LPARAM)"Environment", + SMTO_ABORTIFHUNG, + 5000, + &ret); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plSetPlasmaPath.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plSetPlasmaPath.h new file mode 100644 index 00000000..1856734d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plSetPlasmaPath.h @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +void SetPlasmaPath(const char* plasmaPath); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plUnzip.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plUnzip.cpp new file mode 100644 index 00000000..b626db72 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plUnzip.cpp @@ -0,0 +1,153 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plUnzip.h" +#include "hsTypes.h" +#include "hsWindows.h" +#include "hsStream.h" + +plUnzip::plUnzip() : fFile(nil) +{ +} + +bool plUnzip::Open(const char* filename) +{ + fFile = unzOpen(filename); + return (fFile != nil); +} + +bool plUnzip::Close() +{ + bool ret = false; + + if (fFile != nil) + { + ret = (UNZ_OK == unzClose(fFile)); + fFile = nil; + } + + return ret; +} + +void plUnzip::IGetFullPath(const char* destDir, const char* filename, char* outFilename) +{ + // Make sure the dest ends with a slash + strcpy(outFilename, destDir); + char lastChar = outFilename[strlen(outFilename)-1]; + if (lastChar != '\\' && lastChar != '/') + strcat(outFilename, "\\"); + + // Check if the output filename has any directories in it + const char* forward = strrchr(filename, '/'); + const char* backward = strrchr(filename, '\\'); + + if (!forward && !backward) + { + CreateDirectory(outFilename, NULL); + strcat(outFilename, filename); + } + else + { + const char* fileOnly = (forward > backward) ? forward+1 : backward+1; + strncat(outFilename, filename, fileOnly-filename); + CreateDirectory(outFilename, NULL); + + strcat(outFilename, fileOnly); + } +} + +void plUnzip::IExtractCurrent(const char* destDir, char* fileName) +{ + char filename[MAX_PATH]; + if (unzGetCurrentFileInfo(fFile, nil, filename, sizeof(filename), nil, 0, nil, 0) == UNZ_OK) + { + strcpy(fileName, filename); + + if (unzOpenCurrentFile(fFile) == UNZ_OK) + { + char outFilename[MAX_PATH]; + IGetFullPath(destDir, filename, outFilename); + + // Make sure to take off the read-only flag if the file exists, and is RO + DWORD attrs = GetFileAttributes(outFilename); + if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_READONLY)) + SetFileAttributes(outFilename, attrs & ~FILE_ATTRIBUTE_READONLY); + + hsUNIXStream outFile; + if (outFile.Open(outFilename, "wb")) + { + char buf[2048]; + int numRead; + while ((numRead = unzReadCurrentFile(fFile, buf, sizeof(buf))) > 0) + { + outFile.Write(numRead, buf); + } + + outFile.Close(); + + unz_file_info_s info; + unzGetCurrentFileInfo(fFile, &info, NULL, 0, NULL, 0, NULL, 0); + + SYSTEMTIME sysTime = {0}; + sysTime.wDay = info.tmu_date.tm_mday; + sysTime.wMonth = info.tmu_date.tm_mon+1; + sysTime.wYear = info.tmu_date.tm_year; + sysTime.wHour = info.tmu_date.tm_hour; + sysTime.wMinute = info.tmu_date.tm_min; + sysTime.wSecond = info.tmu_date.tm_sec; + + FILETIME localFileTime, utcFileTime; + SystemTimeToFileTime(&sysTime, &localFileTime); + + LocalFileTimeToFileTime(&localFileTime, &utcFileTime); + + HANDLE hFile = CreateFile(outFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + SetFileTime(hFile, NULL, NULL, &utcFileTime); + CloseHandle(hFile); + } + + unzCloseCurrentFile(fFile); + } + } +} + +void plUnzip::ExtractAll(const char* destDir) +{ + if (unzGoToFirstFile(fFile) != UNZ_OK) + return; + + do + { + IExtractCurrent(destDir); + } + while (unzGoToNextFile(fFile) == UNZ_OK); +} + +bool plUnzip::ExtractNext(const char* destDir, char* fileName) +{ + IExtractCurrent(destDir, fileName); + return (unzGoToNextFile(fFile) == UNZ_OK); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plUnzip.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plUnzip.h new file mode 100644 index 00000000..984edb1f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/plUnzip.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plUnzip_h_inc +#define plUnzip_h_inc + +#include "hsTypes.h" +#include "../src/contrib/minizip/unzip.h" + +class plUnzip +{ +protected: + unzFile fFile; + + void IGetFullPath(const char* destDir, const char* filename, char* outFilename); + void IExtractCurrent(const char* destDir, char* fileName=nil); + +public: + plUnzip(); + + bool Open(const char* filename); + bool Close(); + + void ExtractAll(const char* destDir); + bool ExtractNext(const char* destDir, char* fileName); +}; + +#endif // plUnzip_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/resource.h new file mode 100644 index 00000000..d0bada2c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaInstaller/resource.h @@ -0,0 +1,30 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by plPlasmaInstaller.rc +// +#define IDD_INSTALLER 101 +#define IDI_ICON1 102 +#define IDC_3DSMAX_EDIT 1000 +#define IDC_BROWSE_3DSMAX 1001 +#define IDC_TIME_COMBO 1002 +#define IDC_CLIENT_EDIT 1003 +#define IDC_BROWSE_CLIENT 1004 +#define IDC_GET_BUTTON 1005 +#define IDC_STATUS_LIST 1006 +#define IDC_BRANCH_DATE 1007 +#define IDC_BUILD_COMBO 1008 +#define IDC_CLIENT_CHECK 1009 +#define IDC_SCRIPTS_CHECK 1010 +#define IDC_PLUGINS_CHECK 1011 +#define IDC_TOOLS_CHECK 1012 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1013 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/Dirt.ICO b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/Dirt.ICO new file mode 100644 index 0000000000000000000000000000000000000000..057c608327e942bc63b2ca831c4b7bc791e545b0 GIT binary patch literal 2550 zcmd5;eOQy%75_oVhYcTrUNA;OYs`cCDCnl5-3IISxV%oy4#|e|sekT|-Q;)Ax#xG@ zbMHC#=Dq=JU<)4~SlVeGu!uB)Kwud!wgE=TwwOjfgf|H`AOkgxoke`F<8lFCU%|`ZM_Pc^D@q?|a1pK62_=bAIjpp-O=!~V_Bo{ct&&v)=7TJOGnj0|B69`F#P}2^? zZbw+O3xqBJFHr&fC?D*sDzIGK!He2Rv$7Bt7J&l?06%|6sJ7ccR5JkQqGNC=`!yVk zjzF-t#gf#@h(yfy^v8Qmow)SiJIwcZ14%yyL?XbsvIo5I1ULx9aB%U4x33fh6(69X z^8(IZybJx!zvBAe%-E2<10Eird4N5oc37Ak0b#`_-~_Fpgl(yIEI8W-VcDuuByOm| zro0A}9UR0jPh6x}SJCqMO`JE*5FX)+n={~29Sf+Q@$rC@V>p7ttB{oOKJxaQMMcAx zINUyp=7AeH^Sis~y>cG|<9|YGDzJGI7p?(P2+G@FL!03wP{GwL25XZJL6P5v5^W#) z4EGTri$&hgfpsZtxO+vx%_AORQ8ma=Hln`m3Yz+^Y8f(&|p^eD@+U zihc_xS6{e$%BdE0NK9AbXX-k#S7UwF7Rc9Sp!rNMj($1}ZwZj8+<|kKCeUUCh# zULJoR@Ls^dg=^p93&Xd_QtR+W@Fp}Kufm->Mr_Lis!w#`^1XkefAk^wJiyWRZ=n9w z5M0E8@R7a+duJ&s_Z1>DI~$$7{V1<#!ky=4?AQSeUY)``z5@yif#M?Cv)A~DPE5s@ z7VGnF=ZBco&h31<=-`G9W*3xg?ex9W!%>P&8 zvKY32%TpQnLWW_zyf%S=;qv)>p^(pW<<1wmiCtgf1@0aaPcQEUKE8hbuQLl%q>K3h z|K%XyE|f45iFEc8=n=St31(!17ko(Q{3XjgB+GxeLK5c2tXvhmdQF5d@&(Thcs(#` zEhBOlMMuZP#>U0V6B60H|L}&;Tn7cJolecY~zsx#8fQpwd4O`2+UC1y++Ts$mm7gdLwepcFRp7EfX18_^v!2nG z?^WmO>G^EUD%V+_<*KSU5cLjI*{rRKu8!VQo}(oGpi%Rlh)29ZCFDs1ncBLNoWo50 zk#a^`tkiGKjEk*{YfusIdc5((^2QH7Z2D+#Gjp<~_0(?;x1DbPxZ#tI&NGHch9#3V z1TtOy-RWv|PjC0R)?#LT&v0!=UswP60Y=6!>_Ks3;sxW-R{hE0uMX;4S|fH7Kl1y_ zqhnXTX4CpqR}IlddH>MK;f{V|cY5n#X1w-#*X6OXu^U~M`h{LKm~I(w$8_}@C*m_G zK+mb`H}2hkF!iw4!m~nU0Vbn-YFchI8Z*RZNmz0$g_7v!uK`fS>?=J9YGEpfxkjCI74B}bbQ9`_DR$Y04b+)$Rm5Y2pb zOa7|I=WNqY=G6a%Ligg!%>38Uo$ybD%Mc?5s%w z`LW1<-%|?roM4`XhM?n`&3`yVb52<1T}%N41RsJ8tbb5+q`$gp=uZqW8jV#@Pbzde z9m!dOR!d*H*sOwjQZ4r!S-;;DidkDQz4%CMVTD4Y0LLHV(ozK!!2+?CB#XrpYxgM- zR>fmgX_2cf)9&Z(6L6f$N;TXnKv7&;CZ-HHPTaEDXi&_QS|kTri?nvubpTJPS|-8_ aVUnODXb6. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plPlasmaUpdate.h" +#include "jvCoreUtil.h" +#include "hsUtils.h" + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + jvCoreUtil::SetHInstance(hInstance); + plPlasmaUpdate installer; + if (!installer.Create()) + return 0; + + if (!stricmp(lpCmdLine, "AutoDownload")) + installer.SetAutoDownload(); + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + if (!jvBaseDlg::IsDialogMessage(&msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plFileGrabber.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plFileGrabber.cpp new file mode 100644 index 00000000..8b7adc7c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plFileGrabber.cpp @@ -0,0 +1,170 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plFileGrabber.h" + +/* Not needed currently - if we want it again we'll have to reimplement HTTP comm +plHttpFileGrabber::plHttpFileGrabber() +{ + fRequestMgr.SetHostname(""); +} + +bool plHttpFileGrabber::FileToStream(const char* path, hsStream* stream) +{ + std::string pathStr(path); + bool retVal = fRequestMgr.GetFileToStream(path, stream); + stream->SetPosition(0); + + return retVal; +} + +void plHttpFileGrabber::SetServer(const char* server) +{ + std::string serverPath(server); + + fRequestMgr.SetHostname(serverPath); +} + +void plHttpFileGrabber::MakeProperPath(char* path) +{ + char* slash = NULL; + do { + slash = strchr(path, '\\'); + if (slash) + *slash = '/'; + } while(slash != NULL); +} + +void plHttpFileGrabber::SetUsernamePassword(const std::string& username, const std::string& password) +{ + fRequestMgr.SetUsername(username); + fRequestMgr.SetPassword(password); +} + +bool plHttpFileGrabber::IsServerAvailable(const char* serverName) +{ + bool retVal = false; + + HINTERNET hInternet = InternetOpen("Parable Patcher",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0); + if (hInternet) + { + HINTERNET hHttp = InternetConnect(hInternet,serverName,8080,fUserName.c_str(),fPassword.c_str(),INTERNET_SERVICE_HTTP,0,0); + if (hHttp) + { + HINTERNET hRequest = HttpOpenRequest(hHttp, "GET", "/Current/Current.txt", NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_KEEP_CONNECTION, 0); + if (hRequest) + { + DWORD dwCode; + DWORD dwSize = sizeof(dwCode); + HttpSendRequest(hRequest, NULL, 0, NULL, 0); + HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwCode, &dwSize, NULL); + if (dwCode >= 200 && dwCode < 300) + { + retVal = true; + } + + InternetCloseHandle(hRequest); + } + + InternetCloseHandle(hHttp); + } + InternetCloseHandle(hInternet); + } + + return retVal; +} +*/ + +plNetShareFileGrabber::plNetShareFileGrabber() +{ +} + +#define BUFFER_SIZE 1024*1024 +bool plNetShareFileGrabber::FileToStream(const char* path, hsStream* stream) +{ + hsUNIXStream fileStream; + std::string filePath = fServerName + path; + + if (fileStream.Open(filePath.c_str())) + { + char* buffer = new char[BUFFER_SIZE]; + UInt32 streamSize = fileStream.GetSizeLeft(); + while (streamSize > (BUFFER_SIZE)) + { + fileStream.Read(BUFFER_SIZE, buffer); + stream->Write(BUFFER_SIZE, buffer); + + streamSize = fileStream.GetSizeLeft(); + } + + if (streamSize > 0) + { + fileStream.Read(streamSize, buffer); + stream->Write(streamSize, buffer); + } + + stream->Rewind(); + + fileStream.Close(); + delete [] buffer; + + return true; + } + + return false; +} + +void plNetShareFileGrabber::SetServer(const char* server) +{ + fServerName = "\\\\"; + fServerName += server; +} + +void plNetShareFileGrabber::MakeProperPath(char* path) +{ + char* slash = NULL; + do { + slash = strchr(path, '/'); + if (slash) + *slash = '\\'; + } while(slash != NULL); +} + +bool plNetShareFileGrabber::IsServerAvailable(const char* serverName, const char* currentDir) +{ + bool retVal = false; + + char serverPath[MAX_PATH]; + sprintf(serverPath, "\\\\%s\\%s\\Current.txt", serverName, currentDir); + + hsUNIXStream si; + if (si.Open(serverPath, "rb")) + { + retVal = true; + si.Close(); + } + + return retVal; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plFileGrabber.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plFileGrabber.h new file mode 100644 index 00000000..0e624cc6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plFileGrabber.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plFileGrabber_h_inc +#define plFileGrabber_h_inc + +#include +#include "hsStream.h" + +class plFileGrabber +{ +public: + virtual bool IsServerAvailable(const char* serverName, const char* currentDir) = 0; + virtual bool FileToStream(const char* path, hsStream* stream) = 0; + virtual void SetServer(const char* server) = 0; + virtual void MakeProperPath(char* path) = 0; + virtual bool NeedsAuth() { return false; } + virtual void SetUsernamePassword(const std::string& username, const std::string& password) {} +}; + +/* Not needed currently - if we want it again we'll have to reimplement HTTP comm +class plHttpFileGrabber : public plFileGrabber +{ +private: + plHttpDiverseRequestMgr fRequestMgr; + +public: + plHttpFileGrabber(); + virtual bool IsServerAvailable(const char* serverName); + virtual bool FileToStream(const char* path, hsStream* stream); + virtual void SetServer(const char* server); + virtual void MakeProperPath(char* path); + virtual bool NeedsAuth() { return true; } + virtual void SetUsernamePassword(const std::string& username, const std::string& password); +}; +*/ + +class plNetShareFileGrabber : public plFileGrabber +{ +private: + std::string fServerName; + +public: + plNetShareFileGrabber(); + virtual bool IsServerAvailable(const char* serverName, const char* currentDir); + virtual bool FileToStream(const char* path, hsStream* stream); + virtual void SetServer(const char* server); + virtual void MakeProperPath(char* path); +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plManifest.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plManifest.cpp new file mode 100644 index 00000000..d48e5c91 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plManifest.cpp @@ -0,0 +1,346 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "hsUtils.h" +#include "plManifest.h" + +#include "../plEncryption/plChecksum.h" +#include "../plCompression/plZlibStream.h" +#include "../plFile/plEncryptedStream.h" +#include "../plFile/plFileUtils.h" +#include "../plUnifiedTime/plUnifiedTime.h" + +class plManifestFile +{ +public: + char* fFilename; + plMD5Checksum fSum; + plMD5Checksum fLocalSum; + UInt32 fSize; + UInt32 fCompressedSize; + UInt32 fFlags; +}; + +plManifest::plManifest(LogFunc log) : + fDownloadFiles(0), + fDownloadBytes(0), + fDirtySums(false), + fLog(log) +{ +} + +plManifest::~plManifest() +{ + if (fDirtySums) + IWriteCache(); + + delete [] fManifestName; + + for (int i = 0; i < fFiles.size(); i++) + { + delete [] fFiles[i]->fFilename; + delete fFiles[i]; + } +} + +bool plManifest::Read(hsStream* mfsStream, const char* basePath, const char* mfsName) +{ + fBasePath = basePath; + fManifestName = hsStrcpy(mfsName); + + fLog("--- Reading manifest for %s", fManifestName); + + char buf[256]; + while (mfsStream->ReadLn(buf, sizeof(buf))) + { + plManifestFile* file = new plManifestFile; + + char* tok = strtok(buf, "\t"); + file->fFilename = hsStrcpy(tok); + + tok = strtok(nil, "\t"); + file->fSum.SetFromHexString(tok); + + tok = strtok(nil, "\t"); + file->fSize = atoi(tok); + + tok = strtok(nil, "\t"); + file->fCompressedSize = atoi(tok); + + tok = strtok(nil, "\t"); + file->fFlags = atoi(tok); + + fFiles.push_back(file); + } + + return true; +} + +void plManifest::ValidateFiles(ProgressFunc progress) +{ + if (fFiles.empty()) + return; + + fLog("--- Validating files for %s", fManifestName); + + IReadCache(progress); + + fDownloadFiles = 0; + fDownloadBytes = 0; + + for (int i = 0; i < fFiles.size(); i++) + { + plManifestFile* file = fFiles[i]; + + // If the local checksum is invalid, this file wasn't in our cache. + // Get the sum, and update the progress bar. + if (!file->fLocalSum.IsValid()) + { + fLog(" No sum for %s, calculating", file->fFilename); + file->fLocalSum.CalcFromFile(file->fFilename); + fDirtySums = true; + progress(file->fFilename, 1); + } + + if (file->fLocalSum != file->fSum) + { + fLog(" Incorrect sum for %s", file->fFilename); + fDownloadFiles++; + fDownloadBytes += file->fCompressedSize; + } + } + + fLog("--- Need to download %d files, %.1f MB", fDownloadFiles, float(fDownloadBytes) / (1024.f*1024.f)); +} + +void plManifest::DownloadUpdates(ProgressFunc progress, plFileGrabber* grabber) +{ + for (int i = 0; i < fFiles.size(); i++) + { + plManifestFile* file = fFiles[i]; + if (file->fLocalSum != file->fSum) + { + char serverPath[MAX_PATH]; + + sprintf(serverPath, "%s%s.gz", fBasePath.c_str(), file->fFilename); + grabber->MakeProperPath(serverPath); + + hsRAMStream serverStream; + if (grabber->FileToStream(serverPath, &serverStream)) + { + plFileUtils::EnsureFilePathExists(file->fFilename); + + plFileUtils::RemoveFile(file->fFilename, true); + + plZlibStream localStream; + if (localStream.Open(file->fFilename, "wb")) + { + char dataBuf[1024]; + UInt32 sizeLeft = serverStream.GetSizeLeft(); + while (UInt32 amtRead = serverStream.Read( (sizeof(dataBuf) > sizeLeft) ? sizeLeft : sizeof(dataBuf), dataBuf)) + { + progress(file->fFilename, amtRead); + + localStream.Write(amtRead, dataBuf); + sizeLeft = serverStream.GetSizeLeft(); + } + + localStream.Close(); + + // FIXME - Should we recalc this? + file->fLocalSum = file->fSum; + fDirtySums = true; + + if (file->fFlags != 0) + IDecompressSound(file); + } + } + } + } +} + +plManifestFile* plManifest::IFindFile(const char* name) +{ + // FIXME + for (int i = 0; i < fFiles.size(); i++) + { + if (hsStrEQ(fFiles[i]->fFilename, name)) + return fFiles[i]; + } + + return nil; +} + +// KLUDGE - Put age checksums in the dat dir, for backwards compatability +const char* plManifest::IGetCacheDir() +{ + const char* prefix = ""; + if (strncmp(fFiles[0]->fFilename, "dat\\", strlen("dat\\")) == 0) + return "dat\\"; + else + return ""; +} + +#define kCacheFileVersion 1 + +void plManifest::IWriteCache() +{ + plEncryptedStream s; + + bool openedFile = false; + + UInt32 numFiles = 0; + for (int i = 0; i < fFiles.size(); i++) + { + plManifestFile* file = fFiles[i]; + + plUnifiedTime modifiedTime; + if (file->fLocalSum.IsValid() && + plFileUtils::GetFileTimes(file->fFilename, nil, &modifiedTime)) + { + if (!openedFile) + { + openedFile = true; + char buf[256]; + sprintf(buf, "%s%s.sum", IGetCacheDir(), fManifestName); + s.Open(buf, "wb"); + s.WriteSwap32(0); + s.WriteSwap32(kCacheFileVersion); + } + + s.WriteSafeString(file->fFilename); + + plMD5Checksum& checksum = file->fLocalSum; + s.Write(checksum.GetSize(), checksum.GetValue()); + + modifiedTime.Write(&s); + + numFiles++; + } + } + + if (openedFile) + { + s.Rewind(); + s.WriteSwap32(numFiles); + + s.Close(); + } +} + +void plManifest::IReadCache(ProgressFunc progress) +{ + // + // Load valid cached checksums + // + char buf[256]; + sprintf(buf, "%s%s.sum", IGetCacheDir(), fManifestName); + hsStream* s = plEncryptedStream::OpenEncryptedFile(buf); + + if (s) + { + UInt32 numCached = s->ReadSwap32(); + UInt32 cacheFileVersion = s->ReadSwap32(); + + if (cacheFileVersion != kCacheFileVersion) + { + s->Close(); + delete s; + return; + } + + fLog(" Reading cache...found %d cached sums", numCached); + + for (int i = 0; i < numCached; i++) + { + char* name = s->ReadSafeString(); + + UInt8 checksumBuf[MD5_DIGEST_LENGTH]; + s->Read(sizeof(checksumBuf), checksumBuf); + plMD5Checksum checksum; + checksum.SetValue(checksumBuf); + + plUnifiedTime modifiedTime; + modifiedTime.Read(s); + + plManifestFile* file = IFindFile(name); + if (file) + { + plUnifiedTime curModifiedTime; + if (plFileUtils::GetFileTimes(file->fFilename, nil, &curModifiedTime)) + { + if (curModifiedTime == modifiedTime) + file->fLocalSum = checksum; + else + fLog(" Invalid modified time for %s", name); + } + else + fLog(" Couldn't get modified time for %s", name); + + progress(file->fFilename, 1); + } + else + fLog(" Couldn't find cached file '%s' in manifest, discarding", name); + + + delete [] name; + } + + s->Close(); + delete s; + } +} + +#include "../plAudioCore/plAudioFileReader.h" +#include "../plAudio/plOGGCodec.h" +#include "../plAudio/plWavFile.h" + + +bool plManifest::IDecompressSound(plManifestFile* file) +{ + enum + { + kSndFlagCacheSplit = 1<<0, + kSndFlagCacheStereo = 1<<2, + }; + + if (hsCheckBits(file->fFlags, kSndFlagCacheSplit) || + hsCheckBits(file->fFlags, kSndFlagCacheStereo)) + { + plAudioFileReader* reader = plAudioFileReader::CreateReader(file->fFilename, plAudioCore::kAll, plAudioFileReader::kStreamNative); + if (!reader) + return false; + UInt32 size = reader->GetDataSize(); + delete reader; + + if (hsCheckBits(file->fFlags, kSndFlagCacheSplit)) + plAudioFileReader::CacheFile(file->fFilename, true); + if (hsCheckBits(file->fFlags, kSndFlagCacheStereo)) + plAudioFileReader::CacheFile(file->fFilename, false); + } + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plManifest.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plManifest.h new file mode 100644 index 00000000..86dd4421 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plManifest.h @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plManifest_h_inc +#define plManifest_h_inc + +#include "hsTypes.h" +#include "plFileGrabber.h" +#include +#include + +class plManifestFile; + +typedef void (*ProgressFunc)(const char* name, int progDelta); +typedef void (*LogFunc)(const char* format, ...); + +class plManifest +{ +protected: + std::string fBasePath; + char* fManifestName; + + typedef std::vector FileVec; + FileVec fFiles; + + UInt32 fDownloadFiles; + UInt32 fDownloadBytes; + + bool fDirtySums; + + LogFunc fLog; + + bool IDecompressSound(plManifestFile* file); + + plManifestFile* IFindFile(const char* name); + + const char* IGetCacheDir(); + void IReadCache(ProgressFunc progress); + void IWriteCache(); + +public: + plManifest(LogFunc log); + ~plManifest(); + + bool Read(hsStream* mfsStream, const char* basePath, const char* mfsName); + + void ValidateFiles(ProgressFunc progress); + void DownloadUpdates(ProgressFunc progress, plFileGrabber* grabber); + + int NumFiles() { return fFiles.size(); } + UInt32 NumDownloadFiles() { return fDownloadFiles; } + UInt32 DownloadSize() { return fDownloadBytes; } +}; + +#endif // plManifest_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaServers.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaServers.cpp new file mode 100644 index 00000000..0327b0f6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaServers.cpp @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPlasmaServers.h" +#include "hsStream.h" +#include "hsUtils.h" + +bool plPlasmaServers::GetServerInfo() +{ + bool ret = true; + + hsUNIXStream si; + if (si.Open("\\\\dirtcake\\ServerInfo\\ServerInfo.txt", "rb")) + { + char line[256]; + + // Make sure we've got the latest version + if (si.ReadLn(line, sizeof(line))) + { + int version = atoi(line); + si.ReadLn(line, sizeof(line)); + if (version != 4) + { + char errorMsg[512]; + sprintf(errorMsg, "This installer is out of date.\nPlease get the latest version from:\n\n%s", line); + hsMessageBox(errorMsg, "Error", hsMessageBoxNormal, hsMessageBoxIconError); + ret = false; + } + } + else + ret = false; + + // Read in the servers, one per line + while (ret && si.ReadLn(line, sizeof(line))) + { + ServerInfo info; + + info.fServerAddress = strtok(line, ","); + info.fServerName = strtok(nil, ","); + info.fURLBase = strtok(nil, ","); + info.fOutputDir = strtok(nil, ","); + info.fCurrentDir = strtok(nil, ","); + info.fCodeDir = strtok(nil, ","); + + fServers.push_back(info); + } + + si.Close(); + } + else + { + hsMessageBox("Couldn't find server info", "Error", hsMessageBoxNormal, hsMessageBoxIconError); + ret = false; + } + + return ret; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaServers.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaServers.h new file mode 100644 index 00000000..f2fc4295 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaServers.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPlasmaServers_h_inc +#define plPlasmaServers_h_inc + +#include "hsTypes.h" +#include + +class plPlasmaServers +{ +protected: + class ServerInfo + { + public: + std::string fServerAddress; + std::string fServerName; + std::string fURLBase; + std::string fOutputDir; + std::string fCurrentDir; + std::string fCodeDir; + }; + std::vector fServers; + +public: + bool GetServerInfo(); + + int GetNumServers() { return fServers.size(); } + std::string& GetServerAddress(int i) { return fServers[i].fServerAddress; } + std::string& GetServerName(int i) { return fServers[i].fServerName; } + std::string& GetServerURLBase(int i) { return fServers[i].fURLBase; } + std::string& GetServerOutputDir(int i) { return fServers[i].fOutputDir; } + std::string& GetServerCurrentDir(int i) { return fServers[i].fCurrentDir; } + std::string& GetServerCodeDir(int i) { return fServers[i].fCodeDir; } +}; + +#endif // plPlasmaServers_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.cpp new file mode 100644 index 00000000..1618dad0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.cpp @@ -0,0 +1,499 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plPlasmaUpdate.h" +#include "resource.h" +#include +#include +#include +#include "jvCoreUtil.h" +#include "jvDialogResizer.h" + +#include "hsTypes.h" +#include "../plFile/plFileUtils.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "hsStream.h" +#include "plManifest.h" +#include "hsUtils.h" +#include "../plStatusLog/plStatusLog.h" + +static plPlasmaUpdate* gInst = nil; + +#define WM_UPDATE_SERVER WM_APP+1 + +std::string plPlasmaUpdate::fUserName = "dataserver"; +std::string plPlasmaUpdate::fPassword = "parabledata"; + +plPlasmaUpdate::plPlasmaUpdate() : fCanExit(true), fProgressType(kValidating), fResizer(nil), fAutoDownload(false) +{ + INITCOMMONCONTROLSEX icc = {0}; + icc.dwSize = sizeof(INITCOMMONCONTROLSEX); + icc.dwICC = ICC_PROGRESS_CLASS; + InitCommonControlsEx(&icc); + gInst = this; + + _getcwd(fIniPath, sizeof(fIniPath)); + char lastChar = fIniPath[strlen(fIniPath)]; + if (lastChar != '\\' && lastChar != '/') + strcat(fIniPath, "\\"); + strcat(fIniPath, "ParableUpdate.ini"); + + fFileGrabber = new plNetShareFileGrabber; +} + +plPlasmaUpdate::~plPlasmaUpdate() +{ + delete fResizer; + if (fFileGrabber) + delete fFileGrabber; +} + +bool plPlasmaUpdate::Create() +{ + if (!fServers.GetServerInfo()) + return false; + + ICreateDialog(IDD_UPDATE, NULL); + return true; +} + +BOOL CALLBACK plPlasmaUpdate::ILoginWinProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch( msg ) + { + case WM_INITDIALOG: + SetFocus(GetDlgItem(hDlg, IDC_USERNAME)); + break; + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) + { + bool ok = (LOWORD(wParam) == IDOK); + if (ok) + { + char username[25]; + char password[25]; + + GetDlgItemText(hDlg, IDC_USERNAME, username, 25); + GetDlgItemText(hDlg, IDC_PASSWORD, password, 25); + + fUserName = username; + hsAssert(false, "who uses this program?"); + // plChallengeResponse::HashPassword(password, fPassword); + } + EndDialog(hDlg, ok); + return TRUE; + } + break; + } + return FALSE; +} + +void plPlasmaUpdate::IInit() +{ + char curServerAddress[256]; + GetPrivateProfileString("PlasmaUpdate", "ServerAddress", "", curServerAddress, sizeof(curServerAddress), fIniPath); + bool external = (GetPrivateProfileInt("PlasmaUpdate", "External", 0, fIniPath) != 0); + + HWND hCombo = GetDlgItem(fDlg, IDC_BUILD_COMBO); + + for (int i = 0; i < fServers.GetNumServers(); i++) + { + std::string& serverAddress = fServers.GetServerAddress(i); + std::string& serverName = fServers.GetServerName(i); + std::string& currentDir = fServers.GetServerCurrentDir(i); + + if (!fFileGrabber->IsServerAvailable(serverAddress.c_str(), currentDir.c_str())) + continue; + + bool thisServer = (serverAddress == curServerAddress); + + int idx = ComboBox_AddString(hCombo, serverName.c_str()); + ComboBox_SetItemData(hCombo, idx, MAKELPARAM(i, 0)); + if (thisServer && !external) + ComboBox_SetCurSel(hCombo, idx); + + std::string extName = serverName + " (External)"; + idx = ComboBox_AddString(hCombo, extName.c_str()); + ComboBox_SetItemData(hCombo, idx, MAKELPARAM(i, 1)); + if (thisServer && external) + ComboBox_SetCurSel(hCombo, idx); + } + + if (ComboBox_GetCurSel(hCombo) == -1) + ComboBox_SetCurSel(hCombo, 0); + + SendMessage(fDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(jvCoreUtil::GetHInstance(), MAKEINTRESOURCE(IDI_ICON))); + SendMessage(fDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(jvCoreUtil::GetHInstance(), MAKEINTRESOURCE(IDI_ICON))); + + fResizer = new jvDialogResizer(fDlg); + fResizer->AddControl(IDC_BUILD_COMBO, jvDialogResizer::kResizeX); + fResizer->AddControl(IDC_STATUS_LIST, jvDialogResizer::kResizeX | jvDialogResizer::kResizeY); + fResizer->AddControl(IDC_PROGRESS, jvDialogResizer::kLockBottom | jvDialogResizer::kResizeX); + fResizer->AddControl(IDC_DL_TEXT, jvDialogResizer::kLockBottom | jvDialogResizer::kResizeX); + fResizer->AddControl(IDC_DL_BUTTON, jvDialogResizer::kLockBottom | jvDialogResizer::kCenterX); + fResizer->SetSize(360, 320); + fResizer->LoadPosAndSize("PlasmaUpdate"); + + bool goTime = true; + if (fFileGrabber->NeedsAuth()) + { + /* + if (!DialogBox(NULL, MAKEINTRESOURCE(IDD_PLASMAUPDATE_LOGIN), fDlg, ILoginWinProc)) + goTime = false; + else + */ + fFileGrabber->SetUsernamePassword(fUserName, fPassword); + } + + if (goTime) + { + ShowWindow(fDlg, SW_SHOW); + PostMessage(fDlg, WM_UPDATE_SERVER, 0, 0); + } + else + PostQuitMessage(0); +} + +void plPlasmaUpdate::IShutdown() +{ + fResizer->SavePosAndSize("PlasmaUpdate"); + delete fResizer; + fResizer = NULL; + + IDeleteManifests(); +} + +void plPlasmaUpdate::IEnableCtrls(bool enable) +{ + fCanExit = enable; + EnableWindow(GetDlgItem(fDlg, IDC_BUILD_COMBO), enable); + + HWND hDlButton = GetDlgItem(fDlg, IDC_DL_BUTTON); + + if (fManifests.empty()) + SetWindowText(hDlButton, "Close"); + else + SetWindowText(hDlButton, "Download"); + + EnableWindow(hDlButton, enable); + + if (enable) + SetFocus(hDlButton); +} + +void plPlasmaUpdate::IDeleteManifests() +{ + for (int i = 0; i < fManifests.size(); i++) + delete fManifests[i]; + fManifests.clear(); +} + +bool plPlasmaUpdate::IGetManifests(const char* serverRoot, bool external) +{ + IDeleteManifests(); + + char filePath[MAX_PATH]; + sprintf(filePath, "%sCurrent.txt", serverRoot); + + enum Sections + { + kVersion, + kInternal, + kExternal, + kAll + }; + int curSection = kVersion; + + hsRAMStream s; + hsRAMStream manifestStream; + + if (fFileGrabber->FileToStream(filePath, &s)) + { + char buf[256]; + while (s.ReadLn(buf, sizeof(buf))) + { + if (buf[0] == '[') + { + if (hsStrEQ(buf, "[Version]")) + curSection = kVersion; + else if (hsStrEQ(buf, "[Internal]")) + curSection = kInternal; + else if (hsStrEQ(buf, "[External]")) + curSection = kExternal; + else if (hsStrEQ(buf, "[All]")) + curSection = kAll; + } + else + { + if (curSection == kVersion) + { + int version = atoi(buf); + if (version != 1) + { + hsMessageBox("Your copy of PlasmaUpdate is out of date.\nPlease get the latest version.", "Error", hsMessageBoxNormal, hsMessageBoxIconError); + return false; + } + } + else if ((!external && curSection == kInternal) + || (external && curSection == kExternal) + || curSection == kAll) + { + //if (curSection == kAll && !(!strcmp(buf, "Data\\Movies.mfs") || !strcmp(buf, "Data\\Sounds.mfs"))) + // continue; + + sprintf(filePath, "%s%s", serverRoot, buf); + + fFileGrabber->MakeProperPath(filePath); + + manifestStream.Reset(); + fFileGrabber->FileToStream(filePath, &manifestStream); + + plFileUtils::StripFile(filePath); + + plManifest* manifest = new plManifest(ILog); + manifest->Read(&manifestStream, filePath, buf); + fManifests.push_back(manifest); + } + } + } + + return true; + } + + return false; +} + +void plPlasmaUpdate::IUpdateServer() +{ + char buf[256]; + + IEnableCtrls(false); + + SetDlgItemText(fDlg, IDC_DL_TEXT, "Checking for updates..."); + + // + // Figure out what server we're checking + // + bool external = false; + char serverRoot[MAX_PATH]; + + { + HWND hCombo = GetDlgItem(fDlg, IDC_BUILD_COMBO); + int idx = ComboBox_GetCurSel(hCombo); + LPARAM data = ComboBox_GetItemData(hCombo, idx); + int server = LOWORD(data); + external = (HIWORD(data) != 0); + + sprintf(serverRoot, "/%s/", fServers.GetServerCurrentDir(server).c_str()); + const char* serverName = fServers.GetServerAddress(server).c_str(); + + ILog("===== Server set to %s %s =====", serverName, external ? "external" : "internal"); + + WritePrivateProfileString("PlasmaUpdate", "ServerAddress", serverName, fIniPath); + WritePrivateProfileString("PlasmaUpdate", "External", external ? "1" : "0", fIniPath); + + fFileGrabber->SetServer(serverName); + } + + // + // Get the latest publish notes + // + { + HWND hList = GetDlgItem(fDlg, IDC_STATUS_LIST); + ListBox_ResetContent(hList); + + char updateFile[MAX_PATH]; + if (external) + sprintf(updateFile, "%sUpdates-External.txt", serverRoot); + else + sprintf(updateFile, "%sUpdates-Internal.txt", serverRoot); + + hsRAMStream updates; + fFileGrabber->MakeProperPath(updateFile); + if (fFileGrabber->FileToStream(updateFile, &updates)) + { + while (updates.ReadLn(buf, sizeof(buf))) + ListBox_InsertString(hList, 0, buf); + } + } + + // + // Get the manifests + // + bool gotManifests = IGetManifests(serverRoot, external); + UInt32 dlSize = 0; + + fProgressType = kValidating; + + if (gotManifests) + { + int i; + + UInt32 numFiles = 0; + for (i = 0; i < fManifests.size(); i++) + numFiles += fManifests[i]->NumFiles(); + + HWND hProgress = GetDlgItem(fDlg, IDC_PROGRESS); + SendMessage(hProgress, PBM_SETRANGE32, 0, numFiles); + + for (i = 0; i < fManifests.size(); i++) + { + fManifests[i]->ValidateFiles(ProgressFunc); + dlSize += fManifests[i]->DownloadSize(); + } + + SendMessage(hProgress, PBM_SETPOS, 0, 0); + } + + // Print how many megs there are to download + if (dlSize == 0) + { + strcpy(buf, "No updates to download"); + IDeleteManifests(); + } + else + { + float dlMegs = float(dlSize) / (1024.f*1024.f); + if (dlMegs < .1) + dlMegs = .1; + sprintf(buf, "%.1f MB of updates to download", dlMegs); + } + SetDlgItemText(fDlg, IDC_DL_TEXT, buf); + + IEnableCtrls(true); + + if (fAutoDownload) + PostMessage(fDlg, WM_COMMAND, MAKEWPARAM(IDC_DL_BUTTON, BN_CLICKED), LPARAM(GetDlgItem(fDlg, IDC_DL_BUTTON))); +} + +void plPlasmaUpdate::IDownloadUpdates() +{ + fProgressType = kDownloading; + + IEnableCtrls(false); + + int i; + + UInt32 dlSize = 0; + for (i = 0; i < fManifests.size(); i++) + dlSize += fManifests[i]->DownloadSize(); + + HWND hProgress = GetDlgItem(fDlg, IDC_PROGRESS); + SendMessage(hProgress, PBM_SETRANGE32, 0, dlSize); + + for (i = 0; i < fManifests.size(); i++) + fManifests[i]->DownloadUpdates(ProgressFunc, fFileGrabber); + + SendMessage(hProgress, PBM_SETPOS, 0, 0); + + EnableWindow(GetDlgItem(fDlg, IDC_DL_BUTTON), false); + SetDlgItemText(fDlg, IDC_DL_TEXT, "No updates to download"); + + IDeleteManifests(); + + IEnableCtrls(true); + + if (fAutoDownload) + PostMessage(fDlg, WM_COMMAND, MAKEWPARAM(IDC_DL_BUTTON, BN_CLICKED), LPARAM(GetDlgItem(fDlg, IDC_DL_BUTTON))); +} + +void plPlasmaUpdate::ProgressFunc(const char* name, int delta) +{ + static const char* lastName = nil; + if (lastName != name) + { + lastName = name; + + char buf[256]; + if (gInst->fProgressType == kValidating) + strcpy(buf, "Checking "); + else + strcpy(buf, "Downloading "); + strcat(buf, name); + + SetDlgItemText(gInst->fDlg, IDC_DL_TEXT, buf); + } + + SendDlgItemMessage(gInst->fDlg, IDC_PROGRESS, PBM_DELTAPOS, delta, 0); + + jvBaseDlg::PumpQueue(); +} + +void plPlasmaUpdate::ILog(const char* format, ...) +{ + static plStatusLog* log = nil; + + if (!log) + log = plStatusLogMgr::GetInstance().CreateStatusLog(0, "PlasmaUpdate.log"); + + va_list args; + va_start(args, format); + log->AddLineV(format, args); + va_end(args); +} + +BOOL plPlasmaUpdate::IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + IInit(); + SetFocus(GetDlgItem(fDlg, IDC_DL_BUTTON)); + return FALSE; + + case WM_CLOSE: + if (fCanExit) + DestroyWindow(hDlg); + return TRUE; + + case WM_DESTROY: + IShutdown(); + PostQuitMessage(0); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_DL_BUTTON) + { + if (fManifests.empty()) + SendMessage(fDlg, WM_CLOSE, 0, 0); + else + IDownloadUpdates(); + return TRUE; + } + else if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_BUILD_COMBO) + { + IUpdateServer(); + return TRUE; + } + break; + + case WM_UPDATE_SERVER: + IUpdateServer(); + return TRUE; + } + + return FALSE; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.h new file mode 100644 index 00000000..35da8e82 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "jvBaseDlg.h" +#include +#include +#include "plPlasmaServers.h" +#include "plFileGrabber.h" + +class plManifest; +class jvDialogResizer; + +class plPlasmaUpdate : public jvBaseDlg +{ +protected: + static BOOL CALLBACK ILoginWinProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + static std::string fUserName; + static std::string fPassword; + + std::vector fManifests; + char fIniPath[MAX_PATH]; + bool fCanExit; + enum ProgressType { kValidating, kDownloading }; + ProgressType fProgressType; + jvDialogResizer* fResizer; + plPlasmaServers fServers; + bool fAutoDownload; + plFileGrabber* fFileGrabber; + + void IInit(); + void IShutdown(); + + bool IReadServerInfo(); + + void IEnableCtrls(bool enable); + + BOOL IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + static void ProgressFunc(const char* name, int delta); + static void ILog(const char* format, ...); + + void IUpdateServer(); + + void IDeleteManifests(); + bool IGetManifests(const char* serverRoot, bool external); + void IDownloadUpdates(); + +public: + plPlasmaUpdate(); + virtual ~plPlasmaUpdate(); + + bool Create(); + + void SetAutoDownload() { fAutoDownload = true; } +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.rc new file mode 100644 index 00000000..5ad8dcaf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/plPlasmaUpdate.rc @@ -0,0 +1,138 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_UPDATE DIALOGEX 0, 0, 128, 105 +STYLE DS_SETFONT | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | + WS_THICKFRAME +CAPTION "Parable Update" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + LTEXT "Build:",IDC_STATIC,7,8,18,8 + COMBOBOX IDC_BUILD_COMBO,27,6,90,167,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "Download",IDC_DL_BUTTON,34,86,56,16,WS_DISABLED + LISTBOX IDC_STATUS_LIST,7,36,110,11,LBS_NOINTEGRALHEIGHT | + LBS_NOSEL | WS_VSCROLL | WS_TABSTOP + CONTROL "Progress1",IDC_PROGRESS,"msctls_progress32",WS_BORDER | + 0x1,7,54,110,16 + LTEXT "Latest Updates:",IDC_STATIC,7,26,51,8 + CONTROL "xx.x MB of updates to download",IDC_DL_TEXT,"Static", + SS_LEFTNOWORDWRAP | WS_GROUP,7,74,110,8 +END + +IDD_PLASMAUPDATE_LOGIN DIALOGEX 0, 0, 150, 102 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Login" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,48,62,50,14 + PUSHBUTTON "Cancel",IDCANCEL,48,80,50,14 + EDITTEXT IDC_USERNAME,7,17,135,12,ES_AUTOHSCROLL + EDITTEXT IDC_PASSWORD,7,45,135,12,ES_PASSWORD | ES_AUTOHSCROLL + LTEXT "Username",IDC_STATIC,7,7,134,8 + LTEXT "Password",IDC_STATIC,7,35,136,10 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_UPDATE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 121 + TOPMARGIN, 7 + BOTTOMMARGIN, 98 + END + + IDD_PLASMAUPDATE_LOGIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 143 + TOPMARGIN, 7 + BOTTOMMARGIN, 95 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON ICON "Dirt.ICO" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/resource.h new file mode 100644 index 00000000..8f207b0f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPlasmaUpdate/resource.h @@ -0,0 +1,27 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by plPlasmaUpdate.rc +// +#define IDD_UPDATE 101 +#define IDI_ICON1 102 +#define IDI_ICON 102 +#define IDD_PLASMAUPDATE_LOGIN 103 +#define IDC_DL_BUTTON 1005 +#define IDC_STATUS_LIST 1006 +#define IDC_BUILD_COMBO 1008 +#define IDC_PROGRESS 1014 +#define IDC_DL_TEXT 1015 +#define IDC_EDIT1 1016 +#define IDC_USERNAME 1016 +#define IDC_PASSWORD 1017 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1018 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp new file mode 100644 index 00000000..159d4eae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/PythonInterface.cpp @@ -0,0 +1,281 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "PythonInterface.h" + +#include "compile.h" +#include "eval.h" +#include "marshal.h" +#include "cStringIO.h" + +static PyObject* stdFile; // python object of the stdout and err file + +void PythonInterface::initPython(std::string rootDir) +{ + // if haven't been initialized then do it + if ( Py_IsInitialized() == 0 ) + { + // initialize the Python stuff + // let Python do some intialization... + Py_SetProgramName("plasma"); + Py_Initialize(); + + // intialize any of our special plasma python modules +// initP2PInterface(); + // save object to the Plasma module +// plasmaMod = PyImport_ImportModule("Plasma"); + + // create the StringIO for the stdout and stderr file + PycStringIO = (struct PycStringIO_CAPI*)PyCObject_Import("cStringIO", "cStringIO_CAPI"); + stdFile = (*PycStringIO->NewOutput)(20000); + // if we need the builtins then find the builtin module + PyObject* sysmod = PyImport_ImportModule("sys"); + // then add the builtin dicitionary to our module's dictionary + if (sysmod != NULL ) + { + // get the sys's dictionary to find the stdout and stderr + PyObject* sys_dict = PyModule_GetDict(sysmod); + if (stdFile != nil) + { + PyDict_SetItemString(sys_dict,"stdout", stdFile); + PyDict_SetItemString(sys_dict,"stderr", stdFile); + } + // NOTE: we will reset the path to not include paths + // ...that Python may have found in the registery + PyObject* path_list = PyList_New(0); + printf("Setting up include dirs:\n"); + printf("%s\n",rootDir.c_str()); + PyObject* more_path = PyString_FromString(rootDir.c_str()); + PyList_Append(path_list, more_path); + // make sure that our plasma libraries are gotten before the system ones + std::string temp = rootDir + "plasma"; + printf("%s\n",temp.c_str()); + PyObject* more_path3 = PyString_FromString(temp.c_str()); + PyList_Append(path_list, more_path3); + temp = rootDir + "system"; + printf("%s\n\n",temp.c_str()); + PyObject* more_path2 = PyString_FromString("system"); + PyList_Append(path_list, more_path2); + // set the path to be this one + PyDict_SetItemString(sys_dict,"path",path_list); + + + Py_DECREF(sysmod); + } + } +// initialized++; +} + +void PythonInterface::addPythonPath(std::string path) +{ + PyObject* sysmod = PyImport_ImportModule("sys"); + if (sysmod != NULL) + { + PyObject* sys_dict = PyModule_GetDict(sysmod); + PyObject* path_list = PyDict_GetItemString(sys_dict, "path"); + + printf("Adding path %s\n", path.c_str()); + PyObject* more_path = PyString_FromString(path.c_str()); + PyList_Append(path_list, more_path); + + Py_DECREF(sysmod); + } +} + +void PythonInterface::finiPython() +{ + if (Py_IsInitialized() != 0) + { + Py_Finalize(); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : CompileString +// PARAMETERS : command - string of commands to execute in the... +// : filename - filename to say where to code came from +// +// PURPOSE : run a python string in a specific module name +// +PyObject* PythonInterface::CompileString(char *command, char* filename) +{ + PyObject* pycode = Py_CompileString(command, filename, Py_file_input); + return pycode; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : DumpObject +// PARAMETERS : pyobject - string of commands to execute in the... +// +// PURPOSE : marshals an object into a char string +// +hsBool PythonInterface::DumpObject(PyObject* pyobj, char** pickle, Int32* size) +{ + PyObject *s; // the python string object where the marsalled object wil go + // convert object to a marshalled string python object + s = PyMarshal_WriteObjectToString(pyobj); + // did it actually do it? + if ( s != NULL ) + { + // yes, then get the size and the string address + *size = PyString_Size(s); + *pickle = PyString_AsString(s); + return true; + } + else // otherwise, there was an error + { + // Yikes! errors! + PyErr_Print(); // FUTURE: we may have to get the string to display in max...later + return false; + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : getOutputAndReset +// PARAMETERS : none +// +// PURPOSE : get the Output to the error file to be displayed +// +int PythonInterface::getOutputAndReset(char** line) +{ + PyObject* pyStr = (*PycStringIO->cgetvalue)(stdFile); + char *str = PyString_AsString( pyStr ); + int size = PyString_Size( pyStr ); + + // reset the file back to zero + PyObject_CallMethod(stdFile,"reset",""); +/* + // check to see if the debug python module is loaded + if ( dbgOut != nil ) + { + // then send it the new text + if ( PyObject_CallFunction(dbgOut,"s",str) == nil ) + { + // for some reason this function didn't, remember that and not call it again + dbgOut = nil; + // if there was an error make sure that the stderr gets flushed so it can be seen + PyErr_Print(); // make sure the error is printed + PyErr_Clear(); // clear the error + } + } +*/ + if (line) + *line = str; + return size; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : CreateModule +// PARAMETERS : module - module name to create +// +// PURPOSE : create a new module with built-ins +// +PyObject* PythonInterface::CreateModule(char* module) +{ + PyObject *m, *d; +// first we must get rid of any old modules of the same name, we'll replace it + PyObject *modules = PyImport_GetModuleDict(); + if ((m = PyDict_GetItemString(modules, module)) != NULL && PyModule_Check(m)) + // clear it + _PyModule_Clear(m); + +// create the module + m = PyImport_AddModule(module); + if (m == NULL) + return nil; + d = PyModule_GetDict(m); +// add in the built-ins + // first make sure that we don't already have the builtins + if (PyDict_GetItemString(d, "__builtins__") == NULL) + { + // if we need the builtins then find the builtin module + PyObject *bimod = PyImport_ImportModule("__builtin__"); + // then add the builtin dicitionary to our module's dictionary + if (bimod == NULL || PyDict_SetItemString(d, "__builtins__", bimod) != 0) + return nil; + Py_DECREF(bimod); + } + return m; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : RunPYC +// PARAMETERS : code - compiled code +// : module - module name to run the code in +// +// PURPOSE : run a compiled python code in a specific module name +// +hsBool PythonInterface::RunPYC(PyObject* code, PyObject* module) +{ + PyObject *d, *v; + // make sure that we're given a good module... or at least one with an address + if ( !module ) + { + // if no module was given then use just use the main module + module = PyImport_AddModule("__main__"); + if (module == NULL) + return false; + } + // get the dictionaries for this module + d = PyModule_GetDict(module); + // run the string + v = PyEval_EvalCode((PyCodeObject*)code, d, d); + // check for errors and print them + if (v == NULL) + { + // Yikes! errors! + PyErr_Print(); + return false; + } + Py_DECREF(v); + if (Py_FlushLine()) + PyErr_Clear(); + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetModuleItem +// PARAMETERS : item - what item in the plasma module to get +// +// PURPOSE : get an item (probably a function) from a specific module +// +PyObject* PythonInterface::GetModuleItem(char* item, PyObject* module) +{ + if ( module ) + { + PyObject* d = PyModule_GetDict(module); + return PyDict_GetItemString(d, item); + } + return nil; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/PythonInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/PythonInterface.h new file mode 100644 index 00000000..3d1b211e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/PythonInterface.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "Python.h" + +#include + +namespace PythonInterface +{ + void initPython(std::string rootDir); + void finiPython(); + // So the Python packer can add extra paths + void addPythonPath(std::string dir); + + PyObject* CompileString(char *command, char* filename); + hsBool DumpObject(PyObject* pyobj, char** pickle, Int32* size); + int getOutputAndReset(char** line=nil); + PyObject* CreateModule(char* module); + hsBool RunPYC(PyObject* code, PyObject* module); + PyObject* GetModuleItem(char* item, PyObject* module); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/main.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/main.cpp new file mode 100644 index 00000000..941f1fab --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plPythonPack/main.cpp @@ -0,0 +1,451 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "../plFile/hsFiles.h" + +#include "PythonInterface.h" + +#include +#include + +#include + +static const char* kPackFileName = "python.pak"; +static const char* kGlueFile = ".\\plasma\\glue.py"; +static char* glueFile = (char*)kGlueFile; + +void WritePythonFile(const char *fileName, const char* path, hsStream *s) +{ + hsUNIXStream pyStream, glueStream; + char* pathAndFile = new char[strlen(fileName)+strlen(path)+2]; + strcpy(pathAndFile,path); + char lastchar = pathAndFile[strlen(pathAndFile)-1]; + if (lastchar != '\\' && lastchar != '/') + strcat(pathAndFile, "\\"); + strcat(pathAndFile,fileName); + if (!pyStream.Open(pathAndFile) || !glueStream.Open(glueFile)) + { + printf("Unable to open path %s, ",pathAndFile); + return; + } + + printf("==Packing %s, ",fileName); + + pyStream.FastFwd(); + UInt32 pyFileSize = pyStream.GetPosition(); + pyStream.Rewind(); + + glueStream.FastFwd(); + UInt32 glueFileSize = glueStream.GetPosition(); + glueStream.Rewind(); + + UInt32 totalSize = pyFileSize + glueFileSize + 2; + + char *code = new char[totalSize]; + + UInt32 amountRead = pyStream.Read(pyFileSize, code); + hsAssert(amountRead == pyFileSize, "Bad read"); + + code[pyFileSize] = '\n'; + + amountRead = glueStream.Read(glueFileSize, code+pyFileSize+1); + hsAssert(amountRead == glueFileSize, "Bad read"); + + code[totalSize-1] = '\0'; + + // remove the CRs, they seem to give Python heartburn + int k = 0; + for (int i = 0; i < totalSize; i++) + { + if (code[i] != '\r') // is it not a CR? + code[k++] = code[i]; + // else + // skip the CRs + } + + PyObject* pythonCode = PythonInterface::CompileString(code, (char*)fileName); + if (pythonCode) + { + // we need to find out if this is PythonFile module + // create a module name... with the '.' as an X + // and create a python file name that is without the ".py" + char* modulename = new char[strlen(fileName)+1]; + char* pythonfilename = new char[strlen(fileName)+1]; + int j; + for (j=0; jWriteSwap32(0); + + char* errmsg; + int chars_read = PythonInterface::getOutputAndReset(&errmsg); + if (chars_read > 0) + { + printf(errmsg); + printf("\n"); + } + } + delete [] pythonfilename; + } + + // make sure that we have code to save + if (pythonCode) + { + Int32 size; + char* pycode; + PythonInterface::DumpObject(pythonCode,&pycode,&size); + + printf("\n"); + // print any message after each module + char* errmsg; + int chars_read = PythonInterface::getOutputAndReset(&errmsg); + if (chars_read > 0) + { + printf(errmsg); + printf("\n"); + } + s->WriteSwap32(size); + s->Write(size, pycode); + } + else + { + printf("......blast! Compile error!\n"); + s->WriteSwap32(0); + + PyErr_Print(); + PyErr_Clear(); + + char* errmsg; + int chars_read = PythonInterface::getOutputAndReset(&errmsg); + if (chars_read > 0) + { + printf(errmsg); + printf("\n"); + } + } + + delete [] code; + delete [] pathAndFile; // all done with the path and filename as one + + pyStream.Close(); + glueStream.Close(); +} + +void FindFiles(std::vector &filenames, std::vector &pathnames, const char* path) +{ + // Get the names of all the python files + hsFolderIterator folder; + + // if there is a path... set it + if ( path ) + folder.SetPath(path); + + while (folder.NextFileSuffix(".py")) + { + const char *fileName = folder.GetFileName(); + filenames.push_back(fileName); + if ( path ) + pathnames.push_back(path); + else + pathnames.push_back(""); + } +} + +std::string ToLowerCase(std::string str) +{ + std::string retVal = ""; + for (int i=0; i='A')&&(str[i]<='Z')) + retVal += (char)tolower(str[i]); + else + retVal += str[i]; + } + return retVal; +} + +void FindSubDirs(std::vector &dirnames, std::vector &pakNames, char *path) +{ + hsFolderIterator folder; + if (path) + folder.SetPath(path); + + while (folder.NextFile()) + { + if (folder.IsDirectory()) + { + std::string dirName = folder.GetFileName(); + if ((dirName != ".")&&(dirName != "..")&&(ToLowerCase(dirName) != "system") && (ToLowerCase(dirName) != "plasma")) + { + dirnames.push_back(dirName); + pakNames.push_back(dirName+".pak"); + } + } + } +} + +// adds or removes the ending slash in a path as necessary +std::string AdjustEndingSlash(std::string path, bool endingSlash = false) +{ + std::string retVal = path; + bool endSlashExists = false; + char temp = path[path.length()-1]; + if (temp == '\\') + endSlashExists = true; + + if (endingSlash) + { + if (!endSlashExists) + retVal += "\\"; + } + else + { + if (endSlashExists) + { + std::string temp = ""; + for (int i=0; i& extraDirs, bool packSysAndPlasma = false) +{ + // make sure the dir ends in a slash + dir = AdjustEndingSlash(dir,true); + + printf("\nCreating %s using the contents of %s\n",pakName.c_str(),dir.c_str()); + printf("Changing working directory to %s\n",rootPath.c_str()); + if (_chdir(rootPath.c_str())) + { + printf("ERROR: Directory change to %s failed for some reason\n",rootPath.c_str()); + printf("Unable to continue with the packing of this directory, aborting...\n"); + return; + } + else + printf("Directory changed to %s\n",rootPath.c_str()); + + std::vector fileNames; + std::vector pathNames; + + FindFiles(fileNames,pathNames,dir.c_str()); + if (packSysAndPlasma) + { + printf("Adding the system and plasma directories to this pack file\n"); + std::string tempPath; + tempPath = dir + "system"; + FindFiles(fileNames,pathNames,tempPath.c_str()); + tempPath = dir + "plasma"; + FindFiles(fileNames,pathNames,tempPath.c_str()); + } + + // ok, we know how many files we're gonna pack, so make a fake index (we'll fill in later) + hsUNIXStream s; + if (!s.Open(pakName.c_str(), "wb")) + return; + + s.WriteSwap32(fileNames.size()); + + int i; + for (i = 0; i < fileNames.size(); i++) + { + s.WriteSafeString(fileNames[i].c_str()); + s.WriteSwap32(0); + } + + PythonInterface::initPython(rootPath); + for (i = 0; i < extraDirs.size(); i++) + PythonInterface::addPythonPath(rootPath + extraDirs[i]); + + // set to maximum optimization (includes removing __doc__ strings) + Py_OptimizeFlag = 2; + + std::vector filePositions; + filePositions.resize(fileNames.size()); + + for (i = 0; i < fileNames.size(); i++) + { + UInt32 initialPos = s.GetPosition(); + WritePythonFile(fileNames[i].c_str(), pathNames[i].c_str(), &s); + UInt32 endPos = s.GetPosition(); + + filePositions[i] = initialPos; + } + + s.SetPosition(sizeof(UInt32)); + for (i = 0; i < fileNames.size(); i++) + { + s.WriteSafeString(fileNames[i].c_str()); + s.WriteSwap32(filePositions[i]); + } + + s.Close(); + + PythonInterface::finiPython(); +} + +void PrintUsage() +{ + printf("Usage:\n"); + printf("plPythonPack [directory to pack...]\n"); + printf("NOTE: the directory to pack must have full system and plasma dirs and\n"); + printf(" must be a relative path to the current working directory\n"); +} + +void main(int argc, char *argv[]) +{ + printf("The Python Pack Utility\n"); + + char buffer[_MAX_PATH]; + _getcwd(buffer,_MAX_PATH); + std::string baseWorkingDir = buffer; + + // are they asking for usage? + if (argc == 2) + { + std::string temp = argv[1]; + temp = ToLowerCase(temp); + if ((temp == "?") || (temp == "-?") || (temp == "/?") || (temp == "-help") || (temp == "/help") + || (temp == "-h") || (temp == "/h")) + { + PrintUsage(); + return; + } + } + // wrong number of args, print usage + if (argc > 2) + { + PrintUsage(); + return; + } + + std::vector dirNames; + std::vector pakNames; + std::string rootPath; + + if (argc == 1) + { + FindSubDirs(dirNames,pakNames,nil); + rootPath = AdjustEndingSlash(baseWorkingDir,true); + } + else + { + std::string path = argv[1]; + FindSubDirs(dirNames,pakNames,argv[1]); + rootPath = ConcatDirs(baseWorkingDir,path); + rootPath = AdjustEndingSlash(rootPath,true); + } + + PackDirectory(rootPath,rootPath,rootPath+kPackFileName,dirNames,true); + for (int i=0; id(U(4@BKY* zejm^K-W$Myu7Cj8$5B2YA?pP~0O2Kq1IRfsoF*Uky93=PK!e8v0s{e8S8|arIvSWa z511nZ($j(DWWdi4(7z5Ca{y;9AfS*Aj(}Y5WDm1W4;17B3l;z|5dha&0B(XF1#gML zl?K64EQKI`9RkCA;Ys=YX1imaKRsuIC zEsE4(hVLxASKER!cfQ9ApBEATGHFF(xtF$s9}x#vp%+{|{NW!cLr%#DsBSrh7O>N%-9ScA5b1(d!Z9%CqzP2@+q8y7gEEHq#XzDF2R_;xDdLP64p z;CF{kQ>+W9|KbWxGUJ4M`0~m)c%)+v=@}afOm~YwNW?D0r@oJ@?Z;74{T22$okLys zWgPqCO>~^Ujqc(5NJ;|MtmZ*1mqA$62nX7X=|Tg573r56CTd{EI zyKtNS3nXvq##`0>XghlkColdTXYM{m2AvCUZ_-^h2d}Pu8&SIbh}3R@zGx8UBSX-o zXTyokTW&Vs=1f}^oD5-36P8E4Ovm8j z<^{vn5`0em7L@lQCFc~}J>`gwQ9+#$kGlGgF#hjxJbri&<2Du{OO}I6dwKAEz<;JQ zPF?&CUs}FHy0I58hO9=-!Ckm021z>4S%t0s*pD z;NmVr>6^Jo%g8`WM;D4JYH{O<4VyOuJr_nXP2h^$Tp&M>_Ut(URJtU5H9SVO{uKwB zE!euJAMRek2wfBd9)B7tK5U1hqa#Z9*HgYxbPnBu*-Y=c%QU+t?9XfPI4oDl;~Ojj z5zBI(txYIoc>;kzBogq&ycrTtFYz*qf+ATVgw^X%+I*$V>s|1A*mW=mPA zR5o!bd=zup5LPLC%7=!{m^t~rhe7B^=YQ8 zjT>hvClkmG{A^kHGWNAxmdz{6FECE>BZXm;e3ZeUn6F^Bm2GENuw_L%jO+S0n6ffV zi^UT>M{FqB8T~d}T35D9Rj%4zl&L3vH)DEF!YAHh5bxnVfNy2`X#cgO2VI?T_*-FN|ZcsMxQsYJcqKQ@;BT zr_7(ipzq?d!wQb=Ivi}h1QUV&J)aOB5v+u9!gWF~!9<`h`xI!-AwTCjf!1+o{hN;{ W+!KOr0wzK)&u06}9?Cgl&--6koh^+3 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Intern.h new file mode 100644 index 00000000..08fd15c4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Intern.h @@ -0,0 +1,45 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_INTERN_H + + + +/***************************************************************************** +* +* SelfPatcher.cpp +* +***/ +bool SelfPatch (bool noSelfPatch, bool * abort, ENetError * result, plLauncherInfo *info); +void SetReturnCode (DWORD retCode); \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Main.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Main.cpp new file mode 100644 index 00000000..290254bd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Main.cpp @@ -0,0 +1,1121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Main.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +#include "resource.h" +#include +#define WIN32_LEAN_AND_MEAN +#define WHITESPACE L" \"\t\r\n\x1A" +#define UPDATE_STATUSMSG_SECONDS 30 // Must be an int + +#if BUILD_TYPE == BUILD_TYPE_DEV + #define STATUS_PATH L"www2.cyanworlds.com" +#else + #define STATUS_PATH L"support.cyanworlds.com" +#endif + + +#if BUILD_TYPE == BUILD_TYPE_BETA + static const char s_postKey[] = ""; //"betakey=6C5DC90EFD7AF8892D2A65CDE5DF46D55A2777EC3D196ED83F912B62185A74DD"; +#else + static const char s_postKey[] = ""; +#endif + + +/***************************************************************************** +* +* Private +* +***/ + +enum ELogSev { + kLogInfo, + kLogErr, + kNumLogSev +}; + +enum { + kEventTimer = 1, +}; + +enum EEventType { + kEventSetProgress, + kEventSetText, + kEventSetStatusText, + kEventSetTimeRemaining, + kEventSetBytesRemaining, +}; + +// base window event +struct WndEvent { + LINK(WndEvent) link; + EEventType type; +}; + +struct SetProgressEvent : WndEvent { + int progress; +}; + +struct SetTextEvent : WndEvent { + char text[MAX_PATH]; +}; + +struct SetStatusTextEvent : WndEvent { + char text[MAX_PATH]; +}; + +struct SetTimeRemainingEvent : WndEvent { + unsigned seconds; +}; + +struct SetBytesRemainingEvent : WndEvent { + unsigned bytes; +}; + + +//============================================================================ +// TRANSGAMING detection & dialog replacement +//============================================================================ +typedef BOOL (WINAPI *IsTransgaming) (void); +typedef const char * (WINAPI *TGGetOS) (void); +typedef LPVOID (WINAPI *TGLaunchUNIXApp) (const char *pPath, const char *pMode); +typedef BOOL (WINAPI *TGUNIXAppReadLine) (LPVOID pApp, char *pBuf, int bufSize); +typedef BOOL (WINAPI *TGUNIXAppWriteLine) (LPVOID pApp, const char *pLine); +typedef BOOL (WINAPI *TGUNIXAppClose) (LPVOID pApp); + +static bool TGIsCider = false; +static void *pTGApp = NULL; +static TGLaunchUNIXApp pTGLaunchUNIXApp; +static TGUNIXAppReadLine pTGUNIXAppReadLine; +static TGUNIXAppWriteLine pTGUNIXAppWriteLine; +static TGUNIXAppClose pTGUNIXAppClose; + +#define TG_NEW_DIALOG_PATH "C:\\Program Files\\Uru Live\\Cider\\URU Live Updater.app" +#define TG_NEW_DIALOG_POPEN_PATH "/transgaming/c_drive/Program Files/Uru Live/Cider/URU Live Updater.app/Contents/MacOS/URU Live Updater" +#define TG_OLD_DIALOG_POPEN_PATH "/URU Live Updater.app/Contents/MacOS/URU Live Updater" +#define TG_CUR_FRAMEWORK_FILE "C:\\Program Files\\Uru Live\\Cider\\current.txt" +#define TG_LATEST_FRAMEWORK_FILE "C:\\Program Files\\Uru Live\\Cider\\Frameworks\\version.txt" + + +/***************************************************************************** +* +* Private data +* +***/ + +static bool s_shutdown; +static bool s_prepared; +static int s_retCode = 1; +static long s_terminationIssued; +static bool s_terminated; +static plLauncherInfo s_launcherInfo; +static HANDLE s_thread; +static HANDLE s_event; +static HINSTANCE s_hInstance; +static HWND s_dialog; +static CEvent s_dialogCreateEvent(kEventManualReset); +static CCritSect s_critsect; +static LISTDECL(WndEvent, link) s_eventQ; +static CEvent s_shutdownEvent(kEventManualReset); +static wchar s_workingDir[MAX_PATH]; +static CEvent s_statusEvent(kEventManualReset); + + +// List of files we need to delete from the client directory +#ifdef PLASMA_EXTERNAL_RELEASE + +static const wchar * s_deleteFiles[] = { + L"NetDiag.exe", + L"UruExplorer.pdb", + L"UruExplorer.map", +}; + +#else + +static const wchar * s_deleteFiles[] = { + L"NetDiag.exe", +}; + +#endif + + +/***************************************************************************** +* +* Local functions +* +***/ + +// Detect whether we're running under TRANSGAMING Cider +//============================================================================== +static void TGDoCiderDetection () { + + HMODULE hMod = GetModuleHandle ("ntdll"); + if (!hMod) + return; + + IsTransgaming pIsTg = (IsTransgaming)GetProcAddress (hMod, "IsTransgaming"); + if (!pIsTg || !pIsTg ()) + return; + + TGGetOS pTGOS = (TGGetOS)GetProcAddress (hMod, "TGGetOS"); + const char *pOS = NULL; + if (pTGOS) + pOS = pTGOS (); + if (!pOS || strcmp (pOS, "MacOSX")) + return; + + TGIsCider = true; + pTGLaunchUNIXApp = (TGLaunchUNIXApp)GetProcAddress (hMod, "TGLaunchUNIXApp"); + pTGUNIXAppReadLine = (TGUNIXAppReadLine)GetProcAddress (hMod, "TGUNIXAppReadLine"); + pTGUNIXAppWriteLine = (TGUNIXAppWriteLine)GetProcAddress (hMod, "TGUNIXAppWriteLine"); + pTGUNIXAppClose = (TGUNIXAppClose)GetProcAddress (hMod, "TGUNIXAppClose"); +} + +//============================================================================ +static void Abort () { + s_retCode = 0; + s_shutdown = true; +} + +//============================================================================ +static void PostEvent (WndEvent *event) { + s_critsect.Enter(); + s_eventQ.Link(event); + s_critsect.Leave(); +} + +//============================================================================ +static void LogV (ELogSev sev, const wchar fmt[], va_list args) { + static struct { FILE * file; const wchar * pre; } s_log[] = { + { stdout, L"Inf" }, + { stderr, L"Err" }, + }; + COMPILER_ASSERT(arrsize(s_log) == kNumLogSev); + + fwprintf (s_log[sev].file, L"%s: ", s_log[sev].pre); + vfwprintf(s_log[sev].file, fmt, args); + fwprintf (s_log[sev].file, L"\n"); + + if (sev >= kLogErr) + Abort(); +} + +//============================================================================ +static void Log (ELogSev sev, const wchar fmt[], ...) { + va_list args; + va_start(args, fmt); + LogV(sev, fmt, args); + va_end(args); +} + +//============================================================================ +// NOTE: Must use LocalFree() on the return value of this function when finished with the string +static wchar *TranslateErrorCode(DWORD errorCode) { + LPVOID lpMsgBuf; + + FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (wchar *) &lpMsgBuf, + 0, + NULL + ); + return (wchar *)lpMsgBuf; +} + +//============================================================================ +static BOOL WINAPI CtrlHandler (DWORD) { + static unsigned s_ctrlCount; + if (++s_ctrlCount == 3) + _exit(1); // exit process immediately upon 3rd Ctrl-C. + Abort(); + return TRUE; +} + +//============================================================================ +static void PrepareGame () { + SetText("Connecting to server..."); + (void)_beginthread(UruPrepProc, 0, (void *) &s_launcherInfo); +} + +//============================================================================ +static void InitGame () { + s_launcherInfo.initCallback(kStatusOk, nil); +} + +//============================================================================ +static void StartGame () { + (void)_beginthread(UruStartProc, 0, (void *) &s_launcherInfo); +} + +//============================================================================ +static void StopGame () { + (void)_beginthread(PlayerStopProc, 0, (void *) &s_launcherInfo); +} + +//============================================================================ +static void TerminateGame () { + if (!AtomicSet(&s_terminationIssued, 1)) + _beginthread(PlayerTerminateProc, 0, (void *) &s_launcherInfo); +} + +//============================================================================ +static void Recv_SetProgress (HWND hwnd, const SetProgressEvent &event) { + SendMessage(GetDlgItem(s_dialog, IDC_PROGRESS), PBM_SETPOS, event.progress, NULL); + + if (pTGApp) + { + char buf[64]; + + sprintf (buf, "bar:%d", event.progress); + if (!pTGUNIXAppWriteLine (pTGApp, buf)) + { + pTGUNIXAppClose (pTGApp); + pTGApp = NULL; + PostQuitMessage (0); + } + } +} + +//============================================================================ +static void Recv_SetText (HWND hwnd, const SetTextEvent &event) { + bool b = SendMessage(GetDlgItem(s_dialog, IDC_TEXT), WM_SETTEXT, 0, (LPARAM) event.text); + + if (pTGApp) + { + char buf[MAX_PATH + 5]; + + sprintf (buf, "text:%s", event.text); + if (!pTGUNIXAppWriteLine (pTGApp, buf)) + { + pTGUNIXAppClose (pTGApp); + pTGApp = NULL; + PostQuitMessage (0); + } + } +} + +//============================================================================ +static void Recv_SetStatusText (HWND hwnd, const SetStatusTextEvent &event) { + bool b = SendMessage(GetDlgItem(s_dialog, IDC_STATUS_TEXT), WM_SETTEXT, 0, (LPARAM) event.text); +} + +//============================================================================ +static void Recv_SetTimeRemaining (HWND hwnd, const SetTimeRemainingEvent &event) { + unsigned days; + unsigned hours; + unsigned minutes; + unsigned seconds; + + if(event.seconds == 0xffffffff) + { + SendMessage(GetDlgItem(s_dialog, IDC_TIMEREMAINING), WM_SETTEXT, 0, (LPARAM) "estimating..."); + return; + } + + seconds = event.seconds; + + days = seconds / (60 * 60 * 24); + seconds -= (days * 60 * 60 * 24); + hours = seconds / (60 * 60); + seconds -= hours * 60 * 60; + minutes = seconds / 60; + seconds -= minutes * 60; + seconds = seconds; + + char text[64] = {0}; + if(days) + { + if(days > 1) + StrPrintf(text, arrsize(text), "%d days ", days); + else + StrPrintf(text, arrsize(text), "%d day ", days); + } + if(hours) + { + if(hours > 1) + StrPrintf(text, arrsize(text), "%s%d hours ", text, hours); + else + StrPrintf(text, arrsize(text), "%s%d hour ", text, hours); + } + if(minutes) + StrPrintf(text, arrsize(text), "%s%d min ", text, minutes); + if( seconds || !text[0]) + StrPrintf(text, arrsize(text), "%s%d sec", text, seconds); + bool b = SendMessage(GetDlgItem(s_dialog, IDC_TIMEREMAINING), WM_SETTEXT, 0, (LPARAM) text); +} + +//============================================================================ +static void Recv_SetBytesRemaining (HWND hwnd, const SetBytesRemainingEvent &event) { + char text[32]; + unsigned MB; + unsigned decimal; + unsigned bytes = event.bytes; + + unsigned GB = bytes / 1000000000; + if(GB) + { + bytes -= GB * 1000000000; + decimal = bytes / 100000000; // to two decimal places + StrPrintf(text, arrsize(text), "%d.%d GB", GB, decimal); + } + else + { + MB = bytes / 1000000; + bytes -= MB * 1000000; + decimal = bytes / 100000; // to one decimal place + StrPrintf(text, arrsize(text), "%d.%d MB", MB, decimal); + } + bool b = SendMessage(GetDlgItem(s_dialog, IDC_BYTESREMAINING), WM_SETTEXT, 0, (LPARAM) text); +} + +//============================================================================ +static void DispatchEvents (HWND hwnd) { + LISTDECL(WndEvent, link) eventQ; + + s_critsect.Enter(); + { + eventQ.Link(&s_eventQ); + } + s_critsect.Leave(); + +#define DISPATCH(a) case kEvent##a: Recv_##a(hwnd, *(const a##Event *) event); break + while (WndEvent *event = eventQ.Head()) { + switch (event->type) { + DISPATCH(SetProgress); + DISPATCH(SetText); + DISPATCH(SetStatusText); + DISPATCH(SetTimeRemaining); + DISPATCH(SetBytesRemaining); + DEFAULT_FATAL(event->type); + } + DEL(event); // unlinks from list + } +#undef DISPATCH +} + +//============================================================================ +static void OnTimer(HWND hwnd, unsigned int timerId) { + if(s_shutdown) return; + switch (timerId) { + case kEventTimer: + DispatchEvents(hwnd); + break; + + DEFAULT_FATAL(timerId); + } +} + +//=========================================================================== +static void MessagePump (HWND hwnd) { + for (;;) { + // wait for a message or the shutdown event + const DWORD result = MsgWaitForMultipleObjects( + 1, + &s_event, + false, + INFINITE, + QS_ALLEVENTS + ); + if (result == WAIT_OBJECT_0) + return; + + // process windows messages + MSG msg; + + while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + if (!IsDialogMessage(s_dialog, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + if (msg.message == WM_QUIT) { + return; + } + } + } +} + +//============================================================================ +BOOL CALLBACK SplashDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + switch( uMsg ) + { + case WM_INITDIALOG: + { + PostMessage( GetDlgItem(hwndDlg, IDC_PROGRESS), PBM_SETRANGE, 0, MAKELPARAM(0, 1000)); + } + break; + + case WM_COMMAND: + if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) { + // we dont shutdown the window here, but instead let the patcher know it needs to shutdown, and display our shutting down message. + // setting s_shutdown also wont allow any more Set text messages. + if(!s_shutdown) + { + s_shutdown = true; + SendMessage(GetDlgItem(s_dialog, IDC_TEXT), WM_SETTEXT, 0, (LPARAM) "Shutting Down..."); + EnableWindow(GetDlgItem(s_dialog, IDCANCEL), false); + } + } + break; + + case WM_KEYDOWN: + break; + + case WM_NCHITTEST: + SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, (LONG_PTR)HTCAPTION); + return TRUE; + + case WM_TIMER: + OnTimer(hwndDlg, wParam); + break; + + case WM_QUIT: + ::DestroyWindow(hwndDlg); + break; + + case WM_DESTROY: + PostQuitMessage(0); + break; + + default: + return DefWindowProc(hwndDlg, uMsg, wParam, lParam); + } + return TRUE; +} + +//============================================================================ +static void WindowThreadProc(void *) { + + InitCommonControls(); + s_event = CreateEvent( + (LPSECURITY_ATTRIBUTES) 0, + false, // auto reset + false, // initial state off + (LPCTSTR) 0 // name + ); + + if (TGIsCider) + { + if (GetFileAttributes (TG_NEW_DIALOG_PATH) != INVALID_FILE_ATTRIBUTES) + pTGApp = pTGLaunchUNIXApp (TG_NEW_DIALOG_POPEN_PATH, "w"); + else + pTGApp = pTGLaunchUNIXApp (TG_OLD_DIALOG_POPEN_PATH, "w"); + } + + s_dialog = ::CreateDialog( s_hInstance, MAKEINTRESOURCE( IDD_DIALOG ), NULL, SplashDialogProc ); + SetWindowText(s_dialog, "URU Launcher"); + + + ::SetDlgItemText( s_dialog, IDC_TEXT, "Initializing patcher..."); + SetTimer(s_dialog, kEventTimer, 250, 0); + + char productString[256]; + wchar productStringW[256]; + ProductString(productStringW, arrsize(productStringW)); + StrToAnsi(productString, productStringW, arrsize(productString)); + SendMessage(GetDlgItem(s_dialog, IDC_PRODUCTSTRING), WM_SETTEXT, 0, (LPARAM) productString); + + s_dialogCreateEvent.Signal(); + + MessagePump(s_dialog); + + if (pTGApp) + { + pTGUNIXAppWriteLine (pTGApp, "done"); + pTGUNIXAppClose (pTGApp); + pTGApp = NULL; + } + + s_dialog = 0; + s_shutdown = true; + s_shutdownEvent.Signal(); +} + +//============================================================================ +static bool TGCheckForFrameworkUpdate () +{ + // If current.txt doesn't exist, then this is the first time we've been + // run. Copy version.txt to current.txt and continue starting up + if (GetFileAttributes (TG_CUR_FRAMEWORK_FILE) == INVALID_FILE_ATTRIBUTES) + { + CopyFile (TG_LATEST_FRAMEWORK_FILE, TG_CUR_FRAMEWORK_FILE, FALSE); + return false; + } + + // If it does exist, then compare its contents to the contents of the latest version + // If they match, continue starting up + FILE *CurFile, *LatestFile; + CurFile = fopen (TG_CUR_FRAMEWORK_FILE, "rt"); + LatestFile = fopen (TG_LATEST_FRAMEWORK_FILE, "rt"); + + char CurVer[64], LatestVer[64]; + CurVer[0] = '\0'; + LatestVer[0] = '\0'; + if (CurFile) + { + fgets (CurVer, sizeof (CurVer), CurFile); + fclose (CurFile); + } + if (LatestFile) + { + fgets (LatestVer, sizeof (LatestVer), LatestFile); + fclose (LatestFile); + } + + if (strcmp (CurVer, LatestVer) == 0) + return false; + + // Contents don't match. Copy the latest to the current, put up a message box + // informing the user to restart the game, and exit + CopyFile (TG_LATEST_FRAMEWORK_FILE, TG_CUR_FRAMEWORK_FILE, FALSE); + MessageBox (nil, "Game framework requires updating. Please restart URU", + "URU Launcher", MB_ICONINFORMATION); + return true; +} + +//============================================================================ +static void HttpRequestPost(HINTERNET hConnect) +{ + const wchar *path = BuildTypeServerStatusPath(); + HINTERNET hRequest = 0; + char data[256] = {0}; + DWORD bytesRead; + + hRequest = WinHttpOpenRequest( + hConnect, + L"POST", + path, + NULL, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + 0 + ); + if(hRequest) + { + BOOL b = WinHttpSendRequest( + hRequest, + L"Content-Type: application/x-www-form-urlencoded\r\n", + 0, + (void *)s_postKey, + sizeof(s_postKey), + sizeof(s_postKey), + 0 + ); + DWORD err = GetLastError(); + WinHttpReceiveResponse(hRequest, 0); + WinHttpReadData(hRequest, data, arrsize(data)-1, &bytesRead); + data[bytesRead] = 0; + } + if(bytesRead) + SetStatusText(data); + WinHttpCloseHandle(hRequest); +} + +//============================================================================ +static void HttpRequestGet(HINTERNET hConnect) +{ + const wchar *path = BuildTypeServerStatusPath(); + HINTERNET hRequest = 0; + char data[256] = {0}; + DWORD bytesRead; + + hRequest = WinHttpOpenRequest( + hConnect, + L"GET", + path, + NULL, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + 0 + ); + if(hRequest) + { + BOOL b = WinHttpSendRequest( + hRequest, + WINHTTP_NO_ADDITIONAL_HEADERS, + 0, + WINHTTP_NO_REQUEST_DATA, + 0, + 0, + 0 + ); + if(b) + { + DWORD err = GetLastError(); + WinHttpReceiveResponse(hRequest, 0); + WinHttpReadData(hRequest, data, arrsize(data)-1, &bytesRead); + data[bytesRead] = 0; + } + } + if(bytesRead) + SetStatusText(data); + WinHttpCloseHandle(hRequest); +} + +//============================================================================ +static void StatusCallback(void *) +{ + HINTERNET hSession = 0; + HINTERNET hConnect = 0; + + // update while we are running + while(!s_shutdown) + { + if(BuildTypeServerStatusPath()) + { + hSession = WinHttpOpen( + L"UruClient/1.0", + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0 + ); + + if(hSession) + { + hConnect = WinHttpConnect( + hSession, + STATUS_PATH, + INTERNET_DEFAULT_HTTP_PORT, + 0 + ); + if(hConnect) + { + if(StrLen(s_postKey)) + HttpRequestPost(hConnect); + else + HttpRequestGet(hConnect); + } + } + + WinHttpCloseHandle(hConnect); + WinHttpCloseHandle(hSession); + } + + for(unsigned i = 0; i < UPDATE_STATUSMSG_SECONDS && !s_shutdown; ++i) + { + Sleep(1000); + } + } + + s_statusEvent.Signal(); +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void PrepCallback (int id, void *param) { + s_prepared = true; + if (id) + s_shutdown = true; + else if (TGIsCider && TGCheckForFrameworkUpdate ()) + s_shutdown = true; + + if (!s_shutdown) + InitGame(); +} + +//============================================================================ +void InitCallback (int id, void *param) { + if (id) + s_shutdown = true; + if (!s_shutdown) + StartGame(); +} + +//============================================================================= +void StartCallback( int id, void *param) { + if(id == kStatusError) { + MessageBox(nil, "Failed to launch URU", "URU Launcher", MB_ICONERROR); + } + StopGame(); +} + +//============================================================================ +void StopCallback (int id, void *param) { + s_shutdown = true; + TerminateGame(); +} + +//============================================================================ +void TerminateCallback (int id, void *param) { + s_shutdown = true; + s_terminated = true; +} + +//============================================================================ +void ExitCallback (int id, void *param) { + TerminateGame(); +} + +//============================================================================ +void ProgressCallback (int id, void *param) { + PatchInfo *patchInfo = (PatchInfo *)param; + SetProgress(patchInfo->progress); +} + +//============================================================================ +void SetTextCallback (const char text[]) { + SetText(text); +} + +//============================================================================ +void SetStatusTextCallback (const char text[]) { + SetStatusText(text); +} + +//============================================================================ +void SetTimeRemainingCallback (unsigned seconds) { + SetTimeRemaining(seconds); +} + +//============================================================================ +void SetBytesRemainingCallback (unsigned bytes) { + SetBytesRemaining(bytes); +} + + +enum { + kArgAuthSrv, + kArgFileSrv, + kArgGateKeeperSrv, + kArgNoSelfPatch, + kArgBuildId, + kArgCwd, +}; + +static const CmdArgDef s_cmdLineArgs[] = { + { kCmdArgFlagged | kCmdTypeString, L"AuthSrv", kArgAuthSrv }, + { kCmdArgFlagged | kCmdTypeString, L"FileSrv", kArgFileSrv }, + { kCmdArgFlagged | kCmdTypeString, L"GateKeeperSrv", kArgGateKeeperSrv }, + { kCmdArgFlagged | kCmdTypeBool, L"NoSelfPatch", kArgNoSelfPatch }, + { kCmdArgFlagged | kCmdTypeInt, L"BuildId", kArgBuildId }, + { kCmdArgFlagged | kCmdTypeBool, L"Cwd", kArgCwd }, +}; + +//============================================================================ +int __stdcall WinMain ( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow +){ + wchar token[256]; + const wchar *appCmdLine = AppGetCommandLine(); + StrTokenize(&appCmdLine, token, arrsize(token), WHITESPACE); + while(!StrStr(token, L".exe") && !StrStr(token, L".tmp")) + { + StrTokenize(&appCmdLine, token, arrsize(token), WHITESPACE); + } + while (*appCmdLine == L' ') + ++appCmdLine; + + wchar curPatcherFile[MAX_PATH]; + wchar newPatcherFile[MAX_PATH]; + bool isTempPatcher = false; + + PathGetProgramName(curPatcherFile, arrsize(curPatcherFile)); + PathRemoveFilename(newPatcherFile, curPatcherFile, arrsize(newPatcherFile)); + PathAddFilename(newPatcherFile, newPatcherFile, kPatcherExeFilename, arrsize(newPatcherFile)); + + // If our exe name doesn't match the "real" patcher exe name, then we are a newly + // downloaded patcher that needs to be copied over to the "real" exe.. so do that, + // exec it, and exit. + if (0 != StrCmpI(curPatcherFile, newPatcherFile)) { + isTempPatcher = true; + } + + CCmdParser cmdParser(s_cmdLineArgs, arrsize(s_cmdLineArgs)); + cmdParser.Parse(); + + if (!cmdParser.IsSpecified(kArgCwd)) + PathGetProgramDirectory(s_workingDir, arrsize(s_workingDir)); + + TGDoCiderDetection (); + + s_hInstance = hInstance; + ZERO(s_launcherInfo); + StrPrintf(s_launcherInfo.cmdLine, arrsize(s_launcherInfo.cmdLine), appCmdLine); + s_launcherInfo.returnCode = 0; + + if(!isTempPatcher) + { + // create window thread + s_thread = (HANDLE)_beginthread( + WindowThreadProc, + 0, + nil + ); + if (cmdParser.IsSpecified(kArgAuthSrv)) + SetAuthSrvHostname(cmdParser.GetString(kArgAuthSrv)); + if (cmdParser.IsSpecified(kArgFileSrv)) + SetFileSrvHostname(cmdParser.GetString(kArgFileSrv)); + if (cmdParser.IsSpecified(kArgGateKeeperSrv)) + SetGateKeeperSrvHostname(cmdParser.GetString(kArgGateKeeperSrv)); + if(cmdParser.IsSpecified(kArgBuildId)) + s_launcherInfo.buildId = cmdParser.GetInt(kArgBuildId); + + // Wait for the dialog to be created + s_dialogCreateEvent.Wait(kEventWaitForever); + _beginthread(StatusCallback, 0, nil); // get status + } + + for (;;) { + // Wait for previous process to exit. This will happen if we just patched. + HANDLE mutex = CreateMutexW(NULL, TRUE, kPatcherExeFilename); + DWORD wait = WaitForSingleObject(mutex, 0); + while(!s_shutdown && wait != WAIT_OBJECT_0) + wait = WaitForSingleObject(mutex, 100); + + // User canceled + if (s_shutdown) + break; + + // If our exe name doesn't match the "real" patcher exe name, then we are a newly + // downloaded patcher that needs to be copied over to the "real" exe.. so do that, + // exec it, and exit. + if (isTempPatcher) { +// MessageBox(nil, "Replacing patcher file", "Msg", MB_OK); + + // Wait for the other process to exit + Sleep(1000); + + if (!PathDeleteFile(newPatcherFile)) { + wchar error[256]; + DWORD errorCode = GetLastError(); + wchar *msg = TranslateErrorCode(errorCode); + + StrPrintf(error, arrsize(error), L"Failed to delete old patcher executable. %s", msg); + MessageBoxW(GetTopWindow(nil), error, L"Error", MB_OK); + LocalFree(msg); + break; + } + if (!PathMoveFile(curPatcherFile, newPatcherFile)) { + wchar error[256]; + DWORD errorCode = GetLastError(); + wchar *msg = TranslateErrorCode(errorCode); + + StrPrintf(error, arrsize(error), L"Failed to replace old patcher executable. %s", msg); + MessageBoxW(GetTopWindow(nil), error, L"Error", MB_OK); + // attempt to clean up this tmp file + PathDeleteFile(curPatcherFile); + LocalFree(msg); + break; + } + + // launch new patcher + STARTUPINFOW si; + PROCESS_INFORMATION pi; + ZERO(si); + ZERO(pi); + si.cb = sizeof(si); + + wchar cmdline[MAX_PATH]; + StrPrintf(cmdline, arrsize(cmdline), L"%s %s", newPatcherFile, s_launcherInfo.cmdLine); + + // we have only successfully patched if we actually launch the new version of the patcher + (void)CreateProcessW( + NULL, + cmdline, + NULL, + NULL, + FALSE, + DETACHED_PROCESS, + NULL, + NULL, + &si, + &pi + ); + + SetReturnCode( pi.dwProcessId ); + CloseHandle( pi.hThread ); + CloseHandle( pi.hProcess ); + + // We're done. + break; + } + + // Clean up old temp files + ARRAY(PathFind) paths; + wchar fileSpec[MAX_PATH]; + PathGetProgramDirectory(fileSpec, arrsize(fileSpec)); + PathAddFilename(fileSpec, fileSpec, L"*.tmp", arrsize(fileSpec)); + PathFindFiles(&paths, fileSpec, kPathFlagFile); + for (PathFind * path = paths.Ptr(); path != paths.Term(); ++path) + PathDeleteFile(path->name); + + // Delete specified files + for (unsigned i = 0; i < arrsize(s_deleteFiles); ++i) { + StrCopy(fileSpec, s_workingDir, arrsize(fileSpec)); + PathAddFilename(fileSpec, fileSpec, s_deleteFiles[i], arrsize(fileSpec)); + PathDeleteFile(fileSpec); + } + + + SetConsoleCtrlHandler(CtrlHandler, TRUE); + InitAsyncCore(); // must do this before self patch, since it needs to connect to the file server + + // check to see if the patcher needs to be updated, and do it if so. + ENetError selfPatchResult; + if (false == (SelfPatch(cmdParser.IsSpecified(kArgNoSelfPatch), &s_shutdown, &selfPatchResult, &s_launcherInfo)) && IS_NET_SUCCESS(selfPatchResult)) { + // We didn't self-patch, so check for client updates and download them, then exec the client + StrCopy(s_launcherInfo.path, s_workingDir, arrsize(s_launcherInfo.path)); + s_launcherInfo.prepCallback = PrepCallback; + s_launcherInfo.initCallback = InitCallback; + s_launcherInfo.startCallback = StartCallback; + s_launcherInfo.stopCallback = StopCallback; + s_launcherInfo.terminateCallback = TerminateCallback; + s_launcherInfo.progressCallback = ProgressCallback; + s_launcherInfo.exitCallback = ExitCallback; + s_launcherInfo.SetText = SetTextCallback; + s_launcherInfo.SetStatusText = SetStatusTextCallback; + s_launcherInfo.SetTimeRemaining = SetTimeRemainingCallback; + s_launcherInfo.SetBytesRemaining = SetBytesRemainingCallback; + s_launcherInfo.IsTGCider = TGIsCider; + PrepareGame(); + + while (!s_shutdown) // wait for window to be closed + AsyncSleep(10); + + StopGame(); + + // Wait for the PrepareGame thread to exit + while (!s_prepared) + AsyncSleep(10); + + // Wait for the StopGame thread to exit + while (!s_terminated) + Sleep(10); + } + else if (IS_NET_ERROR(selfPatchResult)) { + // Self-patch failed + SetText("Self-patch failed. Exiting..."); + if (!s_shutdown) { + wchar str[256]; + StrPrintf(str, arrsize(str), L"Patcher update failed. Error %u, %s", selfPatchResult, NetErrorToString(selfPatchResult)); + MessageBoxW(GetTopWindow(nil), str, L"Error", MB_OK); + } + } + else { + // We self-patched, so just exit (self-patcher already launched the new patcher. + // it is now waiting for our process to shutdown and release the shared mutex). + SetText("Patcher updated. Restarting..."); + s_shutdown = true; + } + + ShutdownAsyncCore(); + s_statusEvent.Wait(kEventWaitForever); + + PostMessage(s_dialog, WM_QUIT, 0, 0); // tell our window to shutdown + s_shutdownEvent.Wait(kEventWaitForever); // wait for our window to shutdown + + SetConsoleCtrlHandler(CtrlHandler, FALSE); + + if (s_event) + CloseHandle(s_event); + + s_eventQ.Clear(); + break; + } + + if (pTGApp) + { + pTGUNIXAppWriteLine (pTGApp, "done"); + pTGUNIXAppClose (pTGApp); + pTGApp = NULL; + } + + return s_launcherInfo.returnCode; +} + +//============================================================================ +void SetReturnCode (DWORD retCode) { + s_launcherInfo.returnCode = retCode; +} + + +/***************************************************************************** +* +* Window Events +* +***/ + +//============================================================================ +void SetProgress (unsigned progress) { + SetProgressEvent *event = NEW(SetProgressEvent); + event->type = kEventSetProgress; + event->progress = progress; + PostEvent(event); +} + +//============================================================================ +void SetText (const char text[]) { + SetTextEvent *event = NEW(SetTextEvent); + event->type = kEventSetText; + StrCopy(event->text, text, arrsize(event->text)); + PostEvent(event); +} + +//============================================================================ +void SetStatusText (const char text[]) { + SetTextEvent *event = NEW(SetTextEvent); + event->type = kEventSetStatusText; + StrCopy(event->text, text, arrsize(event->text)); + PostEvent(event); +} + +//============================================================================ +void SetTimeRemaining (unsigned seconds) { + SetTimeRemainingEvent *event = new SetTimeRemainingEvent; + event->type = kEventSetTimeRemaining; + event->seconds = seconds; + PostEvent(event); +} + +//============================================================================ +void SetBytesRemaining (unsigned bytes) { + SetBytesRemainingEvent *event = new SetBytesRemainingEvent; + event->type = kEventSetBytesRemaining; + event->bytes = bytes; + PostEvent(event); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Pch.h new file mode 100644 index 00000000..08cc9564 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/Pch.h @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PCH_H +#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PCH_H + +#include +#include +#include "pnUtils/pnUtils.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" +#include "pnProduct/pnProduct.h" +#include "pnNetCli/pnNetCli.h" +#include "plNetGameLib/plNetGameLib.h" +#include "plEncryption/plChecksum.h" + +#include "plCompression/plZlibStream.h" +#include "../plClientPatcher/UruPlayer.h" + +#include "plLauncherInfo.h" +#include "Intern.h" +#include "WinHttp.h" + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp new file mode 100644 index 00000000..8bcfbfd5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp @@ -0,0 +1,314 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private Data +* +***/ + +#ifndef PLASMA_EXTERNAL_RELEASE + static const wchar s_manifest[] = L"InternalPatcher"; +#else + static const wchar s_manifest[] = L"ExternalPatcher"; +#endif + +class SelfPatcherStream : public plZlibStream { + public: + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + static plLauncherInfo *info; + static unsigned totalBytes; + static unsigned progress; +}; + +unsigned SelfPatcherStream::totalBytes = 0; +unsigned SelfPatcherStream::progress = 0; + +static bool s_downloadComplete; +static long s_numFiles; +static ENetError s_patchResult; +static bool s_updated; +static wchar s_newPatcherFile[MAX_PATH]; + + +/***************************************************************************** +* +* Private Functions +* +***/ + +//============================================================================ +static void NetErrorHandler (ENetProtocol protocol, ENetError error) { + ref(protocol); + LogMsg(kLogError, L"NetErr: %s", NetErrorToString(error)); + if (IS_NET_SUCCESS(s_patchResult)) + s_patchResult = error; + s_downloadComplete = true; + + switch(error) { + case kNetErrServerBusy: + MessageBox(0, "Due to the high demand, the server is currently busy. Please try again later, or for alternative download options visit: http://www.mystonline.com/play/", "UruLauncher", MB_OK); + s_patchResult = kNetErrServerBusy; + s_downloadComplete = true; + break; + } +} + +//============================================================================ +static void DownloadCallback ( + ENetError result, + void * param, + const wchar filename[], + hsStream * writer +) { + ref(param); + ref(filename); + + if(IS_NET_ERROR(result)) { + switch (result) { + case kNetErrTimeout: + writer->Rewind(); + NetCliFileDownloadRequest(filename, writer, DownloadCallback, param); + break; + + default: + LogMsg(kLogError, L"Error getting patcher file: %s", NetErrorToString(result)); + if (IS_NET_SUCCESS(s_patchResult)) + s_patchResult = result; + break; + } + return; + } + + writer->Close(); + delete writer; + AtomicAdd(&s_numFiles, -1); + + if(!s_numFiles) { + s_downloadComplete = true; + s_updated = true; + } +} + +//============================================================================ +static bool MD5Check (const char filename[], const wchar md5[]) { + // Do md5 check + char md5copy[MAX_PATH]; + plMD5Checksum existingMD5(filename); + plMD5Checksum latestMD5; + + StrToAnsi(md5copy, md5, arrsize(md5copy)); + latestMD5.SetFromHexString(md5copy); + return (existingMD5 == latestMD5); +} + +//============================================================================ +static void ManifestCallback ( + ENetError result, + void * param, + const wchar group[], + const NetCliFileManifestEntry manifest[], + unsigned entryCount +) { + ref(param); + ref(group); + + if(IS_NET_ERROR(result)) { + switch (result) { + case kNetErrTimeout: + NetCliFileManifestRequest(ManifestCallback, nil, s_manifest); + break; + + default: + LogMsg(kLogError, L"Error getting patcher manifest: %s", NetErrorToString(result)); + if (IS_NET_SUCCESS(s_patchResult)) + s_patchResult = result; + break; + } + return; + } + + char ansi[MAX_PATH]; + + // MD5 check current patcher against value in manifest + ASSERT(entryCount == 1); + wchar curPatcherFile[MAX_PATH]; + PathGetProgramName(curPatcherFile, arrsize(curPatcherFile)); + StrToAnsi(ansi, curPatcherFile, arrsize(ansi)); + if (!MD5Check(ansi, manifest[0].md5)) { +// MessageBox(GetTopWindow(nil), "MD5 failed", "Msg", MB_OK); + SelfPatcherStream::totalBytes += manifest[0].zipSize; + + AtomicAdd(&s_numFiles, 1); + SetText("Downloading new patcher..."); + + StrToAnsi(ansi, s_newPatcherFile, arrsize(ansi)); + SelfPatcherStream * stream = NEWZERO(SelfPatcherStream); + if (!stream->Open(ansi, "wb")) + ErrorFatal(__LINE__, __FILE__, "Failed to create file: %s, errno: %u", ansi, errno); + + NetCliFileDownloadRequest(manifest[0].downloadName, stream, DownloadCallback, nil); + } + else { + s_downloadComplete = true; + } +} + +//============================================================================ +static void FileSrvIpAddressCallback ( + ENetError result, + void * param, + const wchar addr[] +) { + ref(param); + + NetCliGateKeeperDisconnect(); + + if (IS_NET_ERROR(result)) { + LogMsg(kLogDebug, L"FileSrvIpAddressRequest failed: %s", NetErrorToString(result)); + s_patchResult = result; + s_downloadComplete = true; + } + + // Start connecting to the server + NetCliFileStartConnect(&addr, 1, true); + + PathGetProgramDirectory(s_newPatcherFile, arrsize(s_newPatcherFile)); + GetTempFileNameW(s_newPatcherFile, kPatcherExeFilename, 0, s_newPatcherFile); + PathDeleteFile(s_newPatcherFile); + + NetCliFileManifestRequest(ManifestCallback, nil, s_manifest); +} + +//============================================================================ +static bool SelfPatcherProc (bool * abort, plLauncherInfo *info) { + + bool patched = false; + s_downloadComplete = false; + s_patchResult = kNetSuccess; + + NetClientInitialize(); + NetClientSetErrorHandler(NetErrorHandler); + + const wchar ** addrs; + unsigned count; + + count = GetGateKeeperSrvHostnames(&addrs); + + // Start connecting to the server + NetCliGateKeeperStartConnect(addrs, count); + + // request a file server ip address + NetCliGateKeeperFileSrvIpAddressRequest(FileSrvIpAddressCallback, nil, true); + + while(!s_downloadComplete && !*abort) { + NetClientUpdate(); + AsyncSleep(10); + } + + NetCliFileDisconnect(); + NetClientUpdate(); + + // Shutdown the client/server networking subsystem + NetClientDestroy(); + + if (s_downloadComplete && !*abort && s_updated && IS_NET_SUCCESS(s_patchResult)) { + + // launch new patcher + STARTUPINFOW si; + PROCESS_INFORMATION pi; + ZERO(si); + ZERO(pi); + si.cb = sizeof(si); + + wchar cmdline[MAX_PATH]; + StrPrintf(cmdline, arrsize(cmdline), L"%s %s", s_newPatcherFile, info->cmdLine); + + // we have only successfully patched if we actually launch the new version of the patcher + patched = CreateProcessW( + NULL, + cmdline, + NULL, + NULL, + FALSE, + DETACHED_PROCESS, + NULL, + NULL, + &si, + &pi + ); + SetReturnCode(pi.dwProcessId); + CloseHandle( pi.hThread ); + CloseHandle( pi.hProcess ); + ASSERT(patched); + } + + return patched; +} + + +/***************************************************************************** +* +* ProgressStream Functions +* +***/ + +//============================================================================ +UInt32 SelfPatcherStream::Write(UInt32 byteCount, const void* buffer) { + progress += byteCount; + float p = (float)progress / (float)totalBytes * 100; // progress + SetProgress( (int)p ); + return plZlibStream::Write(byteCount, buffer); +} + + +/***************************************************************************** +* +* Protected Functions +* +***/ + +//============================================================================ +// if return value is true, there was an update and the patcher should be shutdown, so the new patcher can take over +bool SelfPatch (bool noSelfPatch, bool * abort, ENetError * result, plLauncherInfo *info) { + bool patched = false; + if (!noSelfPatch) { + SetText("Checking for patcher update..."); + patched = SelfPatcherProc(abort, info); + } + *result = s_patchResult; + return patched; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/banner.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/banner.bmp new file mode 100644 index 0000000000000000000000000000000000000000..23ed118156dab707f16d5f186188aa78c94b2aa3 GIT binary patch literal 71960 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 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherInfo.h new file mode 100644 index 00000000..8849050b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherInfo.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H +#error "Header $/Plasma20/Sources/Plasma/Apps/plUruLauncher/plLauncherCallback.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_APPS_PLURULAUNCHER_PLLAUNCHERCALLBACK_H + +enum EStatus { + kStatusOk, + kStatusError, + kStatusPending, +}; + +struct PatchInfo { + unsigned progress; + unsigned stage; + unsigned progressStage; +}; + +typedef void (*launcherCallback)(int status, void *param); +typedef void (*setTextCallback)(const char text[]); +typedef void (*setStatusTextCallback)(const char text[]); +typedef void (*setTimeRemainingCallback)(unsigned seconds); +typedef void (*setBytesRemainingCallback)(unsigned bytes); + +struct plLauncherInfo { + wchar path[MAX_PATH]; + wchar cmdLine[512]; + unsigned buildId; // buildId override + launcherCallback prepCallback; + launcherCallback initCallback; + launcherCallback startCallback; + launcherCallback stopCallback; + launcherCallback terminateCallback; + launcherCallback progressCallback; + launcherCallback exitCallback; + setTextCallback SetText; + setStatusTextCallback SetStatusText; + setTimeRemainingCallback SetTimeRemaining; + setBytesRemainingCallback SetBytesRemaining; + + PatchInfo patchInfo; + bool IsTGCider; + DWORD returnCode; // used so we can pass a new process id back to gametap. That way gametap wont think uru has exited when the patcher quits. +}; + + +/***************************************************************************** +* +* Main.cpp +* +***/ + +void SetProgress (unsigned progress) ; +void SetText (const char text[]); +void SetStatusText (const char text[]); +void SetTimeRemaining(unsigned seconds); +void SetBytesRemaining(unsigned bytes); \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc new file mode 100644 index 00000000..dcc6778e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc @@ -0,0 +1,125 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DIALOG DIALOGEX 0, 0, 301, 180 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | + DS_CENTER | WS_POPUP | WS_VISIBLE | WS_SYSMENU +EXSTYLE WS_EX_APPWINDOW +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL 109,IDB_BITMAP,"Static",SS_BITMAP | SS_SUNKEN,7,7,288,36 + CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER | 0x1,7, + 162,234,11 + LTEXT "Static",IDC_TEXT,10,152,266,8 + PUSHBUTTON "Cancel",IDCANCEL,243,162,51,11 + LTEXT "Welcome to URU",IDC_STATUS_TEXT,19,57,266,17 + GROUPBOX "",IDC_STATIC,7,46,287,49 + GROUPBOX "Status",IDC_STATIC,7,103,287,44 + LTEXT "Approx. Time Remaining:",IDC_STATIC,19,117,88,11 + LTEXT "Update Remaining:",IDC_STATIC,20,130,73,9 + LTEXT "...",IDC_TIMEREMAINING,111,117,165,12 + LTEXT "...",IDC_BYTESREMAINING,111,130,42,12 + RTEXT "Product String",IDC_PRODUCTSTRING,19,85,272,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 294 + TOPMARGIN, 7 + BOTTOMMARGIN, 173 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_BITMAP BITMAP "banner.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON "Dirt.ICO" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/resource.h new file mode 100644 index 00000000..c4c01f1f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/resource.h @@ -0,0 +1,25 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by plUruLauncher.rc +// +#define IDD_DIALOG 106 +#define IDB_BITMAP 109 +#define IDI_ICON1 111 +#define IDC_PROGRESS 1003 +#define IDC_TEXT 1006 +#define IDC_STATUS_TEXT 1009 +#define IDC_TIMEREMAINING 1010 +#define IDC_BYTESREMAINING 1011 +#define IDC_PRODUCTSTRING 1012 +#define IDC_FILESREMAINING 1013 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 112 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1014 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HS_POINT2.inc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HS_POINT2.inc new file mode 100644 index 00000000..77006eef --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HS_POINT2.inc @@ -0,0 +1,61 @@ +struct HS_POINT2_NAME { + HS_POINT2_TYPE fX, fY; + + HS_POINT2_NAME& Set(HS_POINT2_TYPE x, HS_POINT2_TYPE y) + { + fX = x; + fY = y; + return *this; + } + HS_POINT2_NAME& operator+=(const HS_POINT2_NAME& s) + { + this->fX += s.fX; + this->fY += s.fY; + return *this; + } + HS_POINT2_NAME& operator-=(const HS_POINT2_NAME& s) + { + this->fX -= s.fX; + this->fY -= s.fY; + return *this; + } + +#if 0 // Havok reeks + friend int operator==(const HS_POINT2_NAME& s, const HS_POINT2_NAME& t) + { + return (s.fX == t.fX && s.fY == t.fY); + } + friend int operator!=(const HS_POINT2_NAME& s, const HS_POINT2_NAME& t) + { + return !(s == t); + } +#else // Havok reeks + int operator==(const HS_POINT2_NAME& ss) const + { + return (ss.fX == fX && ss.fY == fY); + } + int operator!=(const HS_POINT2_NAME& ss) + { + return !(ss == *this); + } +#endif // Havok reeks + friend HS_POINT2_NAME operator+(const HS_POINT2_NAME& s, const HS_POINT2_NAME& t) + { + HS_POINT2_NAME result; + result.Set(s.fX + t.fX, s.fY + t.fY); + return result; + } + friend HS_POINT2_NAME operator-(const HS_POINT2_NAME& s, const HS_POINT2_NAME& t) + { + HS_POINT2_NAME result; + result.Set(s.fX - t.fX, s.fY - t.fY); + return result; + } + friend HS_POINT2_NAME operator-(const HS_POINT2_NAME& s) + { + HS_POINT2_NAME result = { -s.fX, -s.fY }; + return result; + } + +#undef HS_POINT2_NAME +#undef HS_POINT2_TYPE diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HeadSpin.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HeadSpin.cpp new file mode 100644 index 00000000..fb418a38 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HeadSpin.cpp @@ -0,0 +1,174 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "hsRefCnt.h" +#include "hsUtils.h" +#include "hsStlUtils.h" +#include "hsExceptions.h" + + +#if HS_BUILD_FOR_MAC + #include + #include + #include +#endif + +#if HS_BUILD_FOR_WIN32 +# include /* for _RPT_BASE */ +# define WIN32_LEAN_AND_MEAN +# define WIN32_EXTRA_LEAN +# include // For OutputDebugString() +#endif + + +/////////////////////////////////////////////////////////////////////////// +/////////////////// For Status Messages /////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +hsDebugMessageProc gHSStatusProc = nil; + +hsDebugMessageProc hsSetStatusMessageProc(hsDebugMessageProc newProc) +{ + hsDebugMessageProc oldProc = gHSStatusProc; + + gHSStatusProc = newProc; + + return oldProc; +} + +////////////////////////////////////////////////////////////////////////// + +hsDebugMessageProc gHSDebugProc = nil; + +hsDebugMessageProc hsSetDebugMessageProc(hsDebugMessageProc newProc) +{ + hsDebugMessageProc oldProc = gHSDebugProc; + + gHSDebugProc = newProc; + + return oldProc; +} + +#ifdef HS_DEBUGGING +void hsDebugMessage (const char message[], long val) +{ + char s[1024]; + +#if HS_BUILD_FOR_WIN32 + #define strfmt _snprintf +#else + #define strfmt snprintf +#endif + + if (val) + s[0] = strfmt(&s[1], 1022, "%s: %ld", message, val); + else + s[0] = strfmt(&s[1], 1022, "%s", message); + + if (gHSDebugProc) + gHSDebugProc(&s[1]); + else +#if HS_BUILD_FOR_MAC + DebugStr((unsigned char*)s); +#elif HS_BUILD_FOR_WIN32 + { OutputDebugString(&s[1]); + OutputDebugString("\n"); + } +#elif (HS_BUILD_FOR_BE || HS_BUILD_FOR_UNIX ) + { fprintf(stderr, "%s\n", &s[1]); +// hsThrow(&s[1]); + } +#elif HS_BUILD_FOR_PS2 + fprintf(stderr, "%s\n", &s[1]); +#else + hsThrow(&s[1]); +#endif +} +#endif + + +/////////////////////////////////////////////////////////////////// + + +hsRefCnt::~hsRefCnt() +{ + hsDebugCode(hsThrowIfFalse(fRefCnt == 1);) +} + +void hsRefCnt::Ref() +{ + fRefCnt++; +} + +void hsRefCnt::UnRef() +{ + hsDebugCode(hsThrowIfFalse(fRefCnt >= 1);) + + if (fRefCnt == 1) // don't decrement if we call delete + delete this; + else + --fRefCnt; +} + + +//////////////////////////////////////////////////////////////////////////// + +#ifndef PLASMA_EXTERNAL_RELEASE + +void hsStatusMessage(const char message[]) +{ + if (gHSStatusProc) { + gHSStatusProc(message); + } else { +#if HS_BUILD_FOR_PS2 || HS_BUILD_FOR_UNIX + printf("%s",message); + int len = strlen(message); + if (len>0 && message[len-1]!='\n') + printf("\n"); +#elif HS_BUILD_FOR_WIN32 + OutputDebugString(message); + int len = strlen(message); + if (len>0 && message[len-1]!='\n') + OutputDebugString("\n"); +#endif // MAC ?????? TODO + } +} + +void hsStatusMessageV(const char * fmt, va_list args) +{ + char buffer[2000]; + vsprintf(buffer, fmt, args); + hsStatusMessage(buffer); +} + +void hsStatusMessageF(const char * fmt, ...) +{ + va_list args; + va_start(args,fmt); + hsStatusMessageV(fmt,args); + va_end(args); +} + +#endif // not PLASMA_EXTERNAL_RELEASE diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HeadSpin.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HeadSpin.h new file mode 100644 index 00000000..19723285 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/HeadSpin.h @@ -0,0 +1,35 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef HeadSpinHDefined +#define HeadSpinHDefined + +#include "hsConfig.h" +// Winsock2 compatibility; winsock2.h must always be included before windows.h =( +#include "hsWindows.h" +#include "hsTypes.h" +#include "hsMalloc.h" + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBiExpander.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBiExpander.h new file mode 100644 index 00000000..f73e860d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBiExpander.h @@ -0,0 +1,477 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsBiExpander_inc +#define hsBiExpander_inc + +#include "hsMemory.h" +#include "hsTemplates.h" + +/////////////////////////////////////////////////////////////////////////////// +////////////// Expander /////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +template class hsExpander { +private: + Int32 fNumPost; + Int32 fNumPostAlloc; + T* fArray; + + Int32 fGrowBy; // default = 0, to double + Int32 fMinSize; // default = 1, min == 1 + + Int32 fCurrent; + + hsExpander(const hsExpander& x); // make it passed as ref or pointer + + void IExpand(int newSize); +public: + enum { kMissingIndex = -1 }; + + hsExpander(Int32 minSize = 1, Int32 growBy = 0); + virtual ~hsExpander(); + + hsExpander& operator=(const hsExpander&orig) { return Copy(orig); } + hsExpander& Copy(const hsExpander& orig); + + void SetCount(int cnt) { if( cnt >= fNumPostAlloc )IExpand(cnt); fNumPost = cnt; } + Int32 GetCount() const { return fNumPost; } + hsBool Empty() const { return GetCount() == 0; } + const T& Get(Int32 index) const; + Int32 Get(Int32 index, Int32 count, T data[]) const; + Int32 Find(const T&) const; // returns kMissingIndex if not found + + void SetArray(T* a, Int32 cnt); + T* GetArray() { return fArray; } + T& operator[]( Int32 index ); + Int32 Append(const T&); // returns t's index + T* Append(); + Int32 Push(const T& t) { return Append(t); } + T* Push() { return Append(); } + T* Top() { return fNumPost ? fArray + fNumPost-1 : nil; } + Int32 Pop(T* t); // returns count of remaining + Int32 Pop(); + void Reset(); // clears out everything + + T& Head() { return fArray[0]; } + T& Tail() { return fArray[fNumPost-1]; } + T& Current() { return fArray[fCurrent]; } + void First(); + void Last(); + void Plus() { ++fCurrent; } + hsBool More() { return (fCurrent < fNumPost); } +}; + +template +hsExpander& hsExpander::Copy(const hsExpander& orig) +{ + SetCount(orig.GetCount()); + int i; + for( i = 0; i < GetCount(); i++ ) + fArray[i] = orig.fArray[i]; + return *this; +} + +template +void hsExpander::SetArray(T* a, Int32 cnt) +{ + delete [] fArray; + if( a ) + fArray = a; + fNumPost = fNumPostAlloc = cnt; +} + +template +void hsExpander::IExpand(int newSize) +{ + Int32 newPostAlloc = fNumPostAlloc; + if( !newPostAlloc ) + newPostAlloc++; + while( newPostAlloc <= newSize ) + newPostAlloc = fGrowBy ? newPostAlloc + fGrowBy : newPostAlloc << 1; + T* newArray = TRACKED_NEW T[newPostAlloc]; + int i; + for( i = 0; i < fNumPost; i++ ) + newArray[i] = fArray[i]; + delete [] (fArray); + fArray = newArray; + fNumPostAlloc = newPostAlloc; +} + +template +hsExpander::hsExpander(Int32 minSize, Int32 growBy) +{ + hsThrowIfBadParam(minSize < 0); + hsThrowIfBadParam(growBy < 0); + + fMinSize = minSize+1; + fGrowBy = growBy; + + fArray = TRACKED_NEW T[fMinSize]; + fNumPostAlloc = fMinSize; + + fNumPost = 0; +} + +template +hsExpander::~hsExpander() +{ + delete [] fArray; +} + +template +void hsExpander::First() +{ + fCurrent = 0; +} + +template +void hsExpander::Last() +{ + fCurrent = fNumPost-1; +} + +template +T& hsExpander::operator[]( Int32 index ) +{ + hsDebugCode(hsThrowIfBadParam((index < 0)||(index >= fNumPost));) + + return fArray[index]; +} + +template +const T& hsExpander::Get( Int32 index ) const +{ + hsDebugCode(hsThrowIfBadParam((index < 0)||(index >= fNumPost));) + + return fArray[index]; +} + +template +Int32 hsExpander::Get(Int32 index, Int32 count, T data[]) const +{ + if( count > 0 ) + { hsThrowIfNilParam(data); + hsThrowIfBadParam((index < 0)||(index >= fNumPost)); + + if (index + count > fNumPost) + count = fNumPost - index; + for (int i = 0; i < count; i++) + data[i] = fArray[i + index]; + } + return count; +} + +template +Int32 hsExpander::Find(const T& obj) const +{ + for (int i = 0; i < fNumPost; i++) + if (fArray[i] == obj) + return i; + return kMissingIndex; +} + +template +Int32 hsExpander::Append(const T& obj) +{ + hsAssert(!(fNumPost >= fNumPostAlloc), "Must be less"); + if( fNumPost == fNumPostAlloc-1 ) + IExpand(fNumPostAlloc); + fArray[fNumPost] = obj; + return fNumPost++; +} + +template +T* hsExpander::Append() +{ + hsAssert(!(fNumPost >= fNumPostAlloc), "Must be less"); + if( fNumPost == fNumPostAlloc-1 ) + IExpand(fNumPostAlloc); + return fArray + fNumPost++; +} + +template +Int32 hsExpander::Pop(T*t) +{ + hsThrowIfBadParam(Empty()); + --fNumPost; + if( t ) + *t = fArray[fNumPost]; + return GetCount(); +} + +template +Int32 hsExpander::Pop() +{ + hsThrowIfBadParam(Empty()); + --fNumPost; + return GetCount(); +} + +template +void hsExpander::Reset() +{ + fNumPost = 0; +} + +/////////////////////////////////////////////////////////////////////////////// +////////////// BiExpander ///////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +template class hsBiExpander { +private: + Int32 fNumPre; + Int32 fNumPost; + Int32 fNumPreAlloc; + Int32 fNumPostAlloc; + T* fArray; + + Int32 fGrowBy; // default = 0, to double + Int32 fMinSize; // default = 1, min == 1 + + Int32 fCurrent; + + hsBiExpander& operator=(const hsBiExpander&); // don't allow assignment + hsBiExpander(const hsBiExpander&); // make it passed as ref or pointer + + void IExpand(int newSize, hsBool towardEnd = true); +public: + enum { kMissingIndex = -1 }; + + hsBiExpander(Int32 minSize = 1, Int32 growBy = 0); + virtual ~hsBiExpander(); + + Int32 GetFirst() const { return -fNumPre; } + Int32 GetCount() const { return fNumPre + fNumPost; } + hsBool Empty() const { return GetCount() == 0; } + const T& Get(Int32 index) const; + Int32 Get(Int32 index, Int32 count, T data[]) const; + Int32 Find(const T&) const; // returns kMissingIndex if not found + + void SetArray(T* a, Int32 cnt, Int32 numPre=0); + T** GetArray() { return fArray - fNumPre; } + T& operator[]( Int32 index ); + T* Append(); // returns t's index + T* Push(); // returns t's index + Int32 Append(const T&); // returns t's index + Int32 Push(const T&); // returns t's index + Int32 Pop(T*t = nil) { return PopHead(t); } // returns count of remaining + Int32 PopHead(T*t = nil); // returns count of remaining + Int32 PopTail(T*t = nil); // returns count of remaining + void Reset(); // clears out everything + + T& Head() { return fArray[-fNumPre]; } + T& Tail() { return fArray[fNumPost-1]; } + T& Current() { return fArray[fCurrent]; } + void First(); + void Last(); + void Plus() { ++fCurrent; } + void Minus() { --fCurrent; } + hsBool More() { return (fCurrent < fNumPost)&&(fCurrent >= -fNumPre); } +}; + +template +void hsBiExpander::SetArray(T* a, Int32 cnt, Int32 numPre) +{ + if( !numPre ) + Reset(); + else + { + fNumPreAlloc = fNumPre = numPre; + fNumPostAlloc = fNumPost = cnt - numPre; + fArray = a + numPre; + } +} + +template +void hsBiExpander::IExpand(int newSize, hsBool towardEnd) +{ + Int32 newPreAlloc = fNumPreAlloc; + Int32 newPostAlloc = fNumPostAlloc; + if( towardEnd ) + { + if( !newPostAlloc ) + newPostAlloc++; + while( newPostAlloc <= newSize ) + newPostAlloc = fGrowBy ? newPostAlloc + fGrowBy : newPostAlloc << 1; + } + else + { + if( !newPreAlloc ) + newPreAlloc++; + while( newPreAlloc <= newSize ) + newPreAlloc = fGrowBy ? newPreAlloc + fGrowBy : newPreAlloc << 1; + } + T* newArray = TRACKED_NEW T[newPreAlloc + newPostAlloc]; + newArray += newPreAlloc; + int i; + for( i = -fNumPre; i < fNumPost; i++ ) + newArray[i] = fArray[i]; +// HSMemory::BlockMove(fArray-fNumPre, newArray-fNumPre, +// (fNumPre+fNumPost)*sizeof(*fArray)); + delete [] (fArray-fNumPreAlloc); + fArray = newArray; + fNumPreAlloc = newPreAlloc; + fNumPostAlloc = newPostAlloc; +} + +template +hsBiExpander::hsBiExpander(Int32 minSize, Int32 growBy) +{ + hsThrowIfBadParam(minSize < 0); + hsThrowIfBadParam(growBy < 0); + + fMinSize = minSize+1; + fGrowBy = growBy; + + fArray = TRACKED_NEW T[fMinSize << 1]; + fNumPreAlloc = fNumPostAlloc = fMinSize; + fArray += fNumPreAlloc; + + fNumPre = fNumPost = 0; +} + +template +hsBiExpander::~hsBiExpander() +{ + delete [] (fArray - fNumPreAlloc); +} + +template +void hsBiExpander::First() +{ + fCurrent = -fNumPre; +} + +template +void hsBiExpander::Last() +{ + fCurrent = fNumPost-1; +} + +template +T& hsBiExpander::operator[]( Int32 index ) +{ + hsDebugCode(hsThrowIfBadParam((index < -fNumPre)||(index >= fNumPost));) + + return fArray[index]; +} + +template +const T& hsBiExpander::Get( Int32 index ) const +{ + hsDebugCode(hsThrowIfBadParam((index < -fNumPre)||(index >= fNumPost));) + + return fArray[index]; +} + +template +Int32 hsBiExpander::Get(Int32 index, Int32 count, T data[]) const +{ + if( count > 0 ) + { hsThrowIfNilParam(data); + hsThrowIfBadParam((index < -fNumPre)||(index >= fNumPost)); + + if (index + count > fNumPost) + count = fNumPost - index; + for (int i = 0; i < count; i++) + data[i] = fArray[i + index]; + } + return count; +} + +template +Int32 hsBiExpander::Find(const T& obj) const +{ + for (int i = -fNumPre; i < fNumPost; i++) + if (fArray[i] == obj) + return i; + return kMissingIndex; +} + +template +T* hsBiExpander::Append() +{ + hsAssert(!(fNumPost >= fNumPostAlloc), "Must be less"); + if( fNumPost == fNumPostAlloc-1 ) + IExpand(fNumPostAlloc, true); + return fArray + fNumPost++; +} + +template +T* hsBiExpander::Push() +{ + hsAssert(!(fNumPre >= fNumPreAlloc), "Must be less"); + if( ++fNumPre == fNumPreAlloc ) + IExpand(fNumPreAlloc, false); + return fArray - fNumPre; +} + +template +Int32 hsBiExpander::Append(const T& obj) +{ + hsAssert(!(fNumPost >= fNumPostAlloc), "Must be less"); + if( fNumPost == fNumPostAlloc-1 ) + IExpand(fNumPostAlloc, true); + fArray[fNumPost] = obj; + return fNumPost++; +} + +template +Int32 hsBiExpander::Push(const T& obj) +{ + hsAssert(!(fNumPre >= fNumPreAlloc), "Must be less"); + if( ++fNumPre == fNumPreAlloc ) + IExpand(fNumPreAlloc, false); + fArray[-fNumPre] = obj; + return -fNumPre; +} + +template +Int32 hsBiExpander::PopHead(T*t) +{ + hsThrowIfBadParam(Empty()); + if( t ) + *t = fArray[-fNumPre]; + --fNumPre; + return GetCount(); +} + +template +Int32 hsBiExpander::PopTail(T*t) +{ + hsThrowIfBadParam(Empty()); + --fNumPost; + if( t ) + *t = fArray[fNumPost]; + return GetCount(); +} + +template +void hsBiExpander::Reset() +{ + fNumPre = fNumPost = 0; +} + +#endif // hsBiExpander_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBitVector.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBitVector.cpp new file mode 100644 index 00000000..503bc2b8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBitVector.cpp @@ -0,0 +1,205 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsStream.h" +#include "hsBitVector.h" +#include "hsTemplates.h" + +#include + +hsBitVector::hsBitVector(int b, ...) +: fBitVectors(nil), + fNumBitVectors(0) +{ + va_list vl; + + va_start( vl, b ); + + do { + SetBit( b, true ); + } while( (b = va_arg( vl, int )) >= 0 ); + + va_end( vl ); +} + +hsBitVector::hsBitVector(const hsTArray& src) +: fBitVectors(nil), + fNumBitVectors(0) +{ + FromList(src); +} + +void hsBitVector::IGrow(UInt32 newNumBitVectors) +{ + hsAssert(newNumBitVectors > fNumBitVectors, "Growing smaller"); + UInt32 *old = fBitVectors; + fBitVectors = TRACKED_NEW UInt32[newNumBitVectors]; + int i; + for( i = 0; i < fNumBitVectors; i++ ) + fBitVectors[i] = old[i]; + for( ; i < newNumBitVectors; i++ ) + fBitVectors[i] = 0; + delete [] old; + fNumBitVectors = newNumBitVectors; +} + +hsBitVector& hsBitVector::Compact() +{ + if( !fBitVectors ) + return *this; + + if( fBitVectors[fNumBitVectors-1] ) + return *this; + + int hiVec = 0; + for( hiVec = fNumBitVectors-1; (hiVec >= 0)&& !fBitVectors[hiVec]; --hiVec ); + if( hiVec >= 0 ) + { + UInt32 *old = fBitVectors; + fBitVectors = TRACKED_NEW UInt32[++hiVec]; + int i; + for( i = 0; i < hiVec; i++ ) + fBitVectors[i] = old[i]; + fNumBitVectors = hiVec; + delete [] old; + } + else + { + Reset(); + } + return *this; +} + + +void hsBitVector::Read(hsStream* s) +{ + Reset(); + + s->LogReadSwap(&fNumBitVectors,"NumBitVectors"); + if( fNumBitVectors ) + { + delete [] fBitVectors; + fBitVectors = TRACKED_NEW UInt32[fNumBitVectors]; + int i; + for( i = 0; i < fNumBitVectors; i++ ) + s->LogReadSwap(&fBitVectors[i],"BitVector"); + } +} + +void hsBitVector::Write(hsStream* s) const +{ + s->WriteSwap32(fNumBitVectors); + + int i; + for( i = 0; i < fNumBitVectors; i++ ) + s->WriteSwap32(fBitVectors[i]); +} + +hsTArray& hsBitVector::Enumerate(hsTArray& dst) const +{ + dst.SetCount(0); + hsBitIterator iter(*this); + int i = iter.Begin(); + while( i >= 0 ) + { + dst.Append(i); + i = iter.Advance(); + } + return dst; +} + +hsBitVector& hsBitVector::FromList(const hsTArray& src) +{ + Clear(); + int i; + for( i = 0; i < src.GetCount(); i++ ) + SetBit(src[i]); + return *this; +} + +////////////////////////////////////////////////////////////////////////// + +int hsBitIterator::IAdvanceVec() +{ + hsAssert((fCurrVec >= 0) && (fCurrVec < fBits.fNumBitVectors), "Invalid state to advance from"); + + while( (++fCurrVec < fBits.fNumBitVectors) && !fBits.fBitVectors[fCurrVec] ); + + return fCurrVec < fBits.fNumBitVectors; +} + +int hsBitIterator::IAdvanceBit() +{ + do + { + if( ++fCurrBit > 31 ) + { + if( !IAdvanceVec() ) + return false; + fCurrBit = 0; + } + } while( !(fBits.fBitVectors[fCurrVec] & (1 << fCurrBit)) ); + + return true; +} + +int hsBitIterator::Advance() +{ + if( End() ) + return -1; + + if( !IAdvanceBit() ) + return fCurrVec = -1; + + return fCurrent = (fCurrVec << 5) + fCurrBit; +} + +int hsBitIterator::Begin() +{ + fCurrent = -1; + fCurrVec = -1; + int i; + for( i = 0; i < fBits.fNumBitVectors; i++ ) + { + if( fBits.fBitVectors[i] ) + { + int j; + for( j = 0; j < 32; j++ ) + { + if( fBits.fBitVectors[i] & (1 << j) ) + { + fCurrVec = i; + fCurrBit = j; + + return fCurrent = (fCurrVec << 5) + fCurrBit; + } + } + } + } + return fCurrent; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBitVector.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBitVector.h new file mode 100644 index 00000000..bc098309 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBitVector.h @@ -0,0 +1,394 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsBitVector_inc +#define hsBitVector_inc + +#include "hsTypes.h" + +template class hsTArray; +class hsStream; + +class hsBitVector { + +protected: + UInt32* fBitVectors; + UInt32 fNumBitVectors; + + void IGrow(UInt32 newNumBitVectors); + + friend class hsBitIterator; +public: + hsBitVector(const hsBitVector& other); + hsBitVector(UInt32 which) : fBitVectors(nil), fNumBitVectors(0) { SetBit(which); } + hsBitVector(int b, ...); // list of one or more integer bits to set. -1 (or any negative) terminates the list (e.g. hsBitVector(0,1,4,-1); + hsBitVector(const hsTArray& list); // sets bit for each int in list + hsBitVector() : fBitVectors(nil), fNumBitVectors(0) {} + virtual ~hsBitVector() { Reset(); } + + hsBitVector& Reset() { delete [] fBitVectors; fBitVectors = nil; fNumBitVectors = 0; return *this; } + hsBitVector& Clear(); // everyone clear, but no dealloc + hsBitVector& Set(int upToBit=-1); // WARNING - see comments at function + + int operator==(const hsBitVector& other) const; // unset (ie uninitialized) bits are clear, + int operator!=(const hsBitVector& other) const { return !(*this == other); } + hsBitVector& operator=(const hsBitVector& other); // will wind up identical + + hsBool ClearBit(UInt32 which) { return SetBit(which, 0); } // returns previous state + hsBool SetBit(UInt32 which, hsBool on = true); // returns previous state + hsBool IsBitSet(UInt32 which) const; // returns current state + hsBool ToggleBit(UInt32 which); // returns previous state + hsBitVector& RemoveBit(UInt32 which); // removes bit, sliding higher bits down to fill the gap. + + friend inline int Overlap(const hsBitVector& lhs, const hsBitVector& rhs) { return lhs.Overlap(rhs); } + hsBool Overlap(const hsBitVector& other) const; + hsBool Empty() const; + + hsBool operator[](UInt32 which) const { return IsBitSet(which); } + + friend inline hsBitVector operator&(const hsBitVector& lhs, const hsBitVector& rhs); // See Overlap() + friend inline hsBitVector operator|(const hsBitVector& lhs, const hsBitVector& rhs); + friend inline hsBitVector operator^(const hsBitVector& lhs, const hsBitVector& rhs); + friend inline hsBitVector operator-(const hsBitVector& lhs, const hsBitVector& rhs); // return lhs w/ rhs's bits turned off + + hsBitVector& operator&=(const hsBitVector& other); // See Overlap() + hsBitVector& operator|=(const hsBitVector& other); + hsBitVector& operator^=(const hsBitVector& other); + hsBitVector& operator-=(const hsBitVector& other); // return me w/ other's bits turned off + + hsBitVector& Compact(); + hsBitVector& SetSize(UInt32 numBits) { ClearBit(numBits+1); return *this; } + UInt32 GetSize() { return fNumBitVectors << 5; } + + // integer level access + UInt32 GetNumBitVectors() const { return fNumBitVectors; } + UInt32 GetBitVector(int i) const { return fBitVectors[i]; } + void SetNumBitVectors(UInt32 n) { Reset(); fNumBitVectors=n; fBitVectors = TRACKED_NEW UInt32[n]; } + void SetBitVector(int i, UInt32 val) { fBitVectors[i]=val; } + + // Do dst.SetCount(0), then add each set bit's index into dst, returning dst. + hsTArray& Enumerate(hsTArray& dst) const; + // this->Clear(), then set all bits listed in src, returning *this. + hsBitVector& FromList(const hsTArray& src); + + void Read(hsStream* s); + void Write(hsStream* s) const; +}; + +inline hsBitVector::hsBitVector(const hsBitVector& other) +{ + if( 0 != (fNumBitVectors = other.fNumBitVectors) ) + { + fBitVectors = TRACKED_NEW UInt32[fNumBitVectors]; + int i; + for( i = 0; i < fNumBitVectors; i++ ) + fBitVectors[i] = other.fBitVectors[i]; + } + else + fBitVectors = nil; +} + +inline hsBool hsBitVector::Empty() const +{ + int i; + for( i = 0; i < fNumBitVectors; i++ ) + { + if( fBitVectors[i] ) + return false; + } + return true; +} + +inline hsBool hsBitVector::Overlap(const hsBitVector& other) const +{ + if( fNumBitVectors > other.fNumBitVectors ) + return other.Overlap(*this); + + int i; + for( i = 0; i < fNumBitVectors; i++ ) + { + if( fBitVectors[i] & other.fBitVectors[i] ) + return true; + } + return false; +} + +inline hsBitVector& hsBitVector::operator=(const hsBitVector& other) +{ + if( this != &other ) + { + if( fNumBitVectors < other.fNumBitVectors ) + { + Reset(); + fNumBitVectors = other.fNumBitVectors; + fBitVectors = TRACKED_NEW UInt32[fNumBitVectors]; + } + else + { + Clear(); + } + + int i; + for( i = 0; i < other.fNumBitVectors; i++ ) + fBitVectors[i] = other.fBitVectors[i]; + } + return *this; +} + +inline int hsBitVector::operator==(const hsBitVector& other) const +{ + if( fNumBitVectors < other.fNumBitVectors ) + return other.operator==(*this); + int i; + for( i = 0; i < other.fNumBitVectors; i++ ) + if( fBitVectors[i] ^ other.fBitVectors[i] ) + return false; + for( ; i < fNumBitVectors; i++ ) + if( fBitVectors[i] ) + return false; + return true; +} + +inline hsBitVector& hsBitVector::operator&=(const hsBitVector& other) +{ + if( this == &other ) + return *this; + + if( fNumBitVectors > other.fNumBitVectors ) + { + fNumBitVectors = other.fNumBitVectors; + } + int i; + for( i = 0; i < fNumBitVectors; i++ ) + fBitVectors[i] &= other.fBitVectors[i]; + return *this; +} + +inline hsBitVector& hsBitVector::operator|=(const hsBitVector& other) +{ + if( this == &other ) + return *this; + + if( fNumBitVectors < other.fNumBitVectors ) + { + IGrow(other.fNumBitVectors); + } + int i; + for( i = 0; i < other.fNumBitVectors; i++ ) + fBitVectors[i] |= other.fBitVectors[i]; + return *this; +} + +inline hsBitVector& hsBitVector::operator^=(const hsBitVector& other) +{ + if( this == &other ) + { + Clear(); + return *this; + } + + if( fNumBitVectors < other.fNumBitVectors ) + { + IGrow(other.fNumBitVectors); + } + int i; + for( i = 0; i < other.fNumBitVectors; i++ ) + fBitVectors[i] ^= other.fBitVectors[i]; + return *this; +} + +inline hsBitVector& hsBitVector::operator-=(const hsBitVector& other) +{ + if( this == &other ) + { + Clear(); + return *this; + } + + int minNum = fNumBitVectors < other.fNumBitVectors ? fNumBitVectors : other.fNumBitVectors; + int i; + for( i = 0; i < minNum; i++ ) + fBitVectors[i] &= ~other.fBitVectors[i]; + return *this; +} + +inline hsBitVector operator&(const hsBitVector& rhs, const hsBitVector& lhs) +{ + hsBitVector ret(rhs); + return ret &= lhs; +} + +inline hsBitVector operator|(const hsBitVector& rhs, const hsBitVector& lhs) +{ + hsBitVector ret(rhs); + return ret |= lhs; +} + +inline hsBitVector operator^(const hsBitVector& rhs, const hsBitVector& lhs) +{ + hsBitVector ret(rhs); + return ret ^= lhs; +} + +inline hsBitVector operator-(const hsBitVector& rhs, const hsBitVector& lhs) +{ + hsBitVector ret(rhs); + return ret -= lhs; +} + +inline hsBitVector& hsBitVector::Clear() +{ + int i; + for( i = 0; i < fNumBitVectors; i++ ) + fBitVectors[i] = 0; + return *this; +} + +// WARNING - since the bitvector is conceptually infinitely long, +// we can't actually set all the bits. If you pass in a non-negative +// upToBit, this sets all bits up to and including that one, otherwise +// it just sets however many bits are currently allocated. You can +// assure this is as many as you want by first calling SetSize, but +// if there are more bits than the requested size, these will also +// get set. Calling Set with a non-negative upToBit will only set +// the bits from 0 to upToBit, but won't clear any higher bits. +inline hsBitVector& hsBitVector::Set(int upToBit) +{ + if( upToBit >= 0 ) + { + UInt32 major = upToBit >> 5; + UInt32 minor = 1 << (upToBit & 0x1f); + if( major >= fNumBitVectors ) + IGrow(major+1); + + UInt32 i; + for( i = 0; i < major; i++ ) + fBitVectors[i] = 0xffffffff; + for( i = 1; i <= minor && i > 0; i <<= 1 ) + fBitVectors[major] |= i; + } + else + { + int i; + for( i = 0; i < fNumBitVectors; i++ ) + fBitVectors[i] = 0xffffffff; + } + return *this; +} + +inline hsBool hsBitVector::IsBitSet(UInt32 which) const +{ + UInt32 major = which >> 5; + return + (major < fNumBitVectors) + && (0 != (fBitVectors[major] & 1 << (which & 0x1f))); +} + +inline hsBool hsBitVector::SetBit(UInt32 which, hsBool on) +{ + UInt32 major = which >> 5; + UInt32 minor = 1 << (which & 0x1f); + if( major >= fNumBitVectors ) + IGrow(major+1); + hsBool ret = 0 != (fBitVectors[major] & minor); + if( ret != on ) + { + if( on ) + fBitVectors[major] |= minor; + else + fBitVectors[major] &= ~minor; + } + + return ret; +} + +inline hsBool hsBitVector::ToggleBit(UInt32 which) +{ + UInt32 major = which >> 5; + UInt32 minor = 1 << (which & 0x1f); + if( major >= fNumBitVectors ) + IGrow(major); + hsBool ret = 0 != (fBitVectors[major] & minor); + if( ret ) + fBitVectors[major] &= ~minor; + else + fBitVectors[major] |= minor; + return ret; +} + +inline hsBitVector& hsBitVector::RemoveBit(UInt32 which) +{ + UInt32 major = which >> 5; + if( major >= fNumBitVectors ) + return *this; + UInt32 minor = 1 << (which & 0x1f); + UInt32 lowMask = minor-1; + UInt32 hiMask = ~(lowMask); + + fBitVectors[major] = (fBitVectors[major] & lowMask) + | ((fBitVectors[major] >> 1) & hiMask); + + while( major < fNumBitVectors-1 ) + { + if( fBitVectors[major+1] & 0x1 ) + fBitVectors[major] |= 0x80000000; + else + fBitVectors[major] &= ~0x80000000; + + major++; + + fBitVectors[major] >>= 1; + } + fBitVectors[major] &= ~0x80000000; + + return *this; +} + +class hsBitIterator +{ +protected: + const hsBitVector& fBits; + + int fCurrent; + + int fCurrVec; + int fCurrBit; + + int IAdvanceBit(); + int IAdvanceVec(); + +public: + // Must call begin after instanciating. + hsBitIterator(const hsBitVector& bits) : fBits(bits) {} + + int Begin(); + int Current() const { return fCurrent; } + int Advance(); + int End() const { return fCurrVec < 0; } +}; + + +#endif // hsBitVector_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBounds.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBounds.cpp new file mode 100644 index 00000000..b3cb2142 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBounds.cpp @@ -0,0 +1,3108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsBounds.h" +#include "hsStream.h" + +#include "hsFastMath.h" + +#if defined(__MWERKS__) && !defined(HS_DEBUGGING) +#pragma optimization_level 2 +#endif + +const hsScalar hsBounds::kRealSmall = 1.0e-5f; + +/////////////////////////////////////////////////////////////////////////////////////// +// +// hsBounds +// +///////////////////////////////////////////////////////////////////////////////////////// + +void hsBounds::Read(hsStream *s) +{ + fType =(hsBoundsType) s->ReadSwap32(); +} + +void hsBounds::Write(hsStream *s) +{ + s->WriteSwap32((Int32)fType); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// hsBounds3 +// +///////////////////////////////////////////////////////////////////////////////////////// + +#if 0 // MESH_GEN_DEFER +void hsBounds3::Draw(hsGView3* v, hsG3DDevice* d, + hsScalar r, hsScalar g, hsScalar b, hsScalar a, + hsBool spheric) +{ + + hsGViewClipState* clipState = v->SaveClipDisabled(); + if( hsGClip3::kClipCulled & v->ClipTestBounds(this) ) + { + v->RestoreClipDisabled(clipState); + return; + } + + // Setup Material + hsGMaterial *mat = TRACKED_NEW hsGMaterial; + hsGLayer* lay = mat->MakeBaseLayer(); + lay->SetAmbientColor(r,g,b,a); + lay->SetMiscFlags(hsGMatState::kMiscWireFrame | hsGMatState::kMiscTwoSided); + lay->SetShadeFlags(hsGMatState::kShadeNoShade); + mat->SetLayer(lay, 0); + + // Setup tMesh + hsGTriMesh tMesh; + if( spheric ) + MakeTriMeshSphere(&tMesh); + else + MakeTriMesh(&tMesh, hsTriangle3::kTwoSided); + + + tMesh.SetMaterial(mat); + hsRefCnt_SafeUnRef(mat); + + tMesh.Render(v,d); + + v->RestoreClipDisabled(clipState); +} +#endif // MESH_GEN_DEFER + +void hsBounds3::Transform(const hsMatrix44 *mat) +{ +#if 0 // IDENT + if( mat->fFlags & hsMatrix44::kIsIdent ) + return; +#endif // IDENT + + hsAssert(fType != kBoundsUninitialized, "Can't transform an unitialized bound"); + if(fType == kBoundsNormal) + { + hsPoint3 corners[8]; + this->GetCorners(corners); + + mat->MapPoints(8, corners); + this->Reset(8,corners); + fBounds3Flags &= ~kCenterValid; + } +} + +void hsBounds3::Reset(const hsPoint3 *p) +{ + fType = kBoundsNormal; + fMins = fMaxs = *p; + fBounds3Flags |= kCenterValid; + fCenter = *p; +} + +void hsBounds3::Reset(const hsBounds3 *b) +{ + if( kBoundsNormal == b->fType ) + { + fType = kBoundsNormal; + fMins = b->fMins; + fMaxs = b->fMaxs; + if( b->fBounds3Flags & kCenterValid ) + { + fBounds3Flags |= kCenterValid; + fCenter = b->fCenter; + } + else + fBounds3Flags &= ~kCenterValid; + } + else + fType = b->fType; +} + +void hsBounds3::Reset(int n, const hsPoint3 *p) +{ + fType = kBoundsNormal; + fMins = fMaxs = *p; + for(int i = 1; i < n ; i++) + this->Union(&p[i]); + fBounds3Flags &= ~kCenterValid; +} + +void hsBounds3::Union(const hsPoint3 *p) +{ + if(fType == kBoundsNormal) // Add this point if bounds is normal + { + for (int i = 0; i < 3; i++) + { + if ((*p)[i] > fMaxs[i]) + fMaxs[i] =(*p)[i]; + else if ((*p)[i] < fMins[i]) + fMins[i] =(*p)[i]; + } + fBounds3Flags &= ~kCenterValid; + } + else + { + if(fType != kBoundsFull) // Otherwise re-init unless bounds is full already + this->Reset(p); + } +} + +void hsBounds3::Union(const hsVector3 *v) +{ + if(fType == kBoundsNormal) // Add this point if bounds is normal + { + for (int i = 0; i < 3; i++) + { + if( (*v)[i] > 0 ) + fMaxs[i] += (*v)[i]; + else + fMins[i] += (*v)[i]; + } + fBounds3Flags &= ~kCenterValid; + } +} + + +void hsBounds3::Union(const hsBounds3 *p) +{ + if(fType == kBoundsNormal && p->GetType() == kBoundsNormal) // Add this point if bounds is normal + { + for (int i = 0; i < 3; i++) + { + if (p->fMaxs[i] > fMaxs[i]) + fMaxs[i] = p->fMaxs[i]; + if (p->fMins[i] < fMins[i]) + fMins[i] = p->fMins[i]; + } + fBounds3Flags &= ~kCenterValid; + } + else if(fType == kBoundsEmpty || fType == kBoundsUninitialized) + { + *this = *p; + } + // If fType is kBoundsFull don't do anything +} + +void hsBounds3::MakeSymmetric(const hsPoint3* p) +{ + if( fType != kBoundsNormal ) + return; + + hsScalar delMax = 0; + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar delUp; + + delUp = fMaxs[i] - (*p)[i]; + delMax = hsMaximum(delMax, delUp); + delUp = (*p)[i] - fMins[i]; + delMax = hsMaximum(delMax, delUp); + } + const hsScalar sqrtTwo = 1.41421f; + delMax *= sqrtTwo; + hsAssert((delMax > -1.e6f)&&(delMax < 1.e6f), "MakeSymmetric going out to sea"); + fCenter = *p; + fMaxs.Set(delMax, delMax, delMax); + fMaxs += fCenter; + fMins.Set(-delMax, -delMax, -delMax); + fMins += fCenter; + fBounds3Flags |= kCenterValid; +} + +void hsBounds3::InscribeSphere() +{ + if( fType != kBoundsNormal ) + return; + + const hsScalar ooSix = hsScalarInvert(2.f * 3.f); + hsScalar a = GetMaxDim() * ooSix; + hsPoint3 p = GetCenter(); + p.fX += a; + p.fY += a; + p.fZ += a; + fMaxs = p; + a *= -2.f; + p.fX += a; + p.fY += a; + p.fZ += a; + fMins = p; + + // Center still valid, type still normal +} + +// neg, pos, zero == disjoint, I contain other, overlap +Int32 hsBounds3::TestBound(const hsBounds3& other) const +{ + Int32 retVal = 1; + int i; + for( i = 0; i < 3; i++ ) + { + if( GetMins()[i] > other.GetMaxs()[i] ) + return -1; + if( GetMaxs()[i] < other.GetMins()[i] ) + return -1; + + if( GetMaxs()[i] < other.GetMaxs()[i] ) + retVal = 0; + if( GetMins()[i] > other.GetMins()[i] ) + retVal = 0; + } + return retVal; +} + +hsBool hsBounds3::IsInside(const hsPoint3* pos) const +{ + hsAssert(fType != kBoundsUninitialized, "Invalid bounds type for hsBounds3::IsInside() "); + if(fType == kBoundsEmpty) + return false; + if(fType == kBoundsFull) + return true; + return !(pos->fX>fMaxs.fX || pos->fY>fMaxs.fY || pos->fZ>fMaxs.fZ || + pos->fXfYfZAllocatePointers(nFaces /*faces*/, nPts /*pts*/, 0 /*uvs*/, 0 /*colors*/); + tMesh->SetNumTriVertex(nPts); + + int iCenter = nPts - 3; + int iNorthPole = nPts - 2; + int iSouthPole = nPts - 1; + hsPoint3 pt; + pt = center; + tMesh->SetPoint(iCenter, &pt); + pt.fZ += radius; + tMesh->SetPoint(iNorthPole, &pt); + pt.fZ -= 2.f * radius; + tMesh->SetPoint(iSouthPole, &pt); + + int i, j; + for( i = 0; i < nLong; i++ ) + { + for( j = 0; j < nLati; j++ ) + { + hsScalar theta = (hsScalar(i) / nLong) * 2.f * hsScalarPI; + hsScalar cosTheta = hsCosine(theta); + hsScalar sinTheta = hsSine(theta); + + hsScalar phi = (hsScalar(j+1) / (nLati+1)) * hsScalarPI; + hsScalar cosPhi = hsCosine(phi); + hsScalar sinPhi = hsSine(phi); + + pt.fX = center.fX + radius * sinPhi * cosTheta; + pt.fY = center.fY + radius * sinPhi * sinTheta; + pt.fZ = center.fZ + radius * cosPhi; + tMesh->SetPoint(j + i * nLati, &pt); + } + } + + hsTriangle3* tri; + int nTris = 0; + int iNext; + for( i = 0; i < nLong; i++ ) + { + if( (iNext = i + 1) >= nLong ) + iNext = 0; + + tri = tMesh->GetTriFromPool(nTris); + tri->Zero(); + tri->fFlags |= hsTriangle3::kTwoSided; + tMesh->SetTriangle(nTris++, tri); + tri->SetQuickMeshVerts(i * nLati, iNext * nLati, iNorthPole); + + tri = tMesh->GetTriFromPool(i); + tri->Zero(); + tri->fFlags |= hsTriangle3::kTwoSided; + tMesh->SetTriangle(nTris++, tri); + tri->SetQuickMeshVerts(nLati-1 + iNext * nLati, nLati-1 + i * nLati, iSouthPole); + + int jNext; + for( j = 0; j < nLati-1; j++ ) + { + jNext = j + 1; + + tri = tMesh->GetTriFromPool(nTris); + tri->Zero(); + tri->fFlags |= hsTriangle3::kTwoSided; + tMesh->SetTriangle(nTris++, tri); + tri->SetQuickMeshVerts(j + i * nLati, j + iNext * nLati, jNext + i * nLati); + + tri = tMesh->GetTriFromPool(nTris); + tri->Zero(); + tri->fFlags |= hsTriangle3::kTwoSided; + tMesh->SetTriangle(nTris++, tri); + tri->SetQuickMeshVerts(jNext + iNext * nLati, jNext + i * nLati, j + iNext * nLati); + } + } +} + +// +// Allocate and create mesh from bounding box +// +void hsBounds3::MakeTriMesh(hsGTriMesh* tMesh, UInt32 triFlags, hsPoint3* cornersIn) const +{ + hsAssert(cornersIn || fType == kBoundsNormal, + "Invalid bounds type for hsBounds3::MakeTriMesh "); + + const int maxNew= 12; + // Setup tMesh + tMesh->AllocatePointers(maxNew /*faces*/, 8 /*pts*/, 0 /*uvs*/, 0 /*colors*/); + tMesh->SetNumTriVertex(8); + int i; + hsPoint3 corners[8]; + // Set Points + if( !cornersIn ) + { + GetCorners(corners); + cornersIn = corners; + } + for(i=0; i<8; i++) + { + tMesh->SetPoint(i, &cornersIn[i]); + } + tMesh->GetVertexPool()->SetCount(8); + + // Set faces + hsTriangle3 *tri; + int triNum=0; + + + static int verts[maxNew * 3] = { + /* -Y */ 6,2,3, + /* -Y */ 6,3,7, + /* Y */ 5,1,0, + /* Y */ 5,0,4, + /* -X */ 7,3,1, + /* -X */ 7,1,5, + /* X */ 4,0,2, + /* X */ 4,2,6, + /* Z */ 3,0,1, + /* Z */ 3,2,0, + /* -Z */ 7,4,6, + /* -Z */ 7,5,4 + }; + int v=0; + for (;triNum < maxNew;triNum++) + { + tri = tMesh->GetTriFromPool(triNum); + tri->Zero(); + tri->fFlags |= triFlags; + tMesh->SetTriangle(triNum, tri); + tri->SetQuickMeshVerts(verts[v + 0],verts[v + 1],verts[v + 2]); + v += 3; + } + tMesh->SetTrianglePointers(); +} +#endif // MESH_GEN_DEFER + +void hsBounds3::TestPlane(const hsPlane3 *p, hsPoint2 &depth) const +{ + TestPlane(p->fN, depth); +} +void hsBounds3::TestPlane(const hsVector3 &n, hsPoint2 &depth) const +{ + hsAssert(fType == kBoundsNormal, "TestPlane only valid for kBoundsNormal filled bounds"); + + hsScalar dmax = fMins.InnerProduct(n); + hsScalar dmin = dmax; + + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar dd; + dd = fMaxs[i] - fMins[i]; + dd *= n[i]; + + if( dd < 0 ) + dmin += dd; + else + dmax += dd; + } + + depth.fX = dmin; + depth.fY = dmax; +} + +hsScalar hsBounds3::ClosestPointToLine(const hsPoint3 *p, const hsPoint3 *v0, const hsPoint3 *v1, hsPoint3 *out) +{ + hsVector3 del(v1, v0); + hsScalar magSq = del.MagnitudeSquared(); + hsScalar t = 0.f; + if( magSq < hsBounds::kRealSmall ) + { + *out = *v0; + } + else + { + t = del.InnerProduct(hsVector3(p, v0)) * hsScalarInvert(magSq); + if( t >= hsScalar1 ) + *out = *v1; + else if( t <= 0 ) + *out = *v0; + else + *out = *v0 + del * t; + } + return t; +} + +hsScalar hsBounds3::ClosestPointToInfiniteLine(const hsPoint3* p, const hsVector3* v, hsPoint3* out) +{ + hsScalar magSq = v->MagnitudeSquared(); + hsScalar t = 0.f; + hsPoint3 origin(0,0,0); + if( magSq < hsBounds::kRealSmall ) + { + *out = origin; + } + else + { + t = v->InnerProduct(hsVector3(*p)) * hsScalarInvert(magSq); + *out = hsPoint3(*v * t); + } + return t; +} + +hsBool hsBounds3::ClosestPoint(const hsPoint3& p, hsPoint3& inner, hsPoint3& outer) const +{ + // Look for axis intervals p is within + int nSect = 0; + int i; + for( i = 0; i < 3; i++ ) + { + if( p[i] < fMins[i] ) + { + inner[i] = fMins[i]; + outer[i] = fMaxs[i]; + } + else if( p[i] > fMaxs[i] ) + { + inner[i] = fMaxs[i]; + outer[i] = fMins[i]; + } + else + { + inner[i] = p[i]; + outer[i] = (p[i] - fMins[i] > fMaxs[i] - p[i]) ? fMins[i] : fMaxs[i]; + nSect++; + } + } + + return nSect == 3; +} + +void hsBounds3::Read(hsStream *stream) +{ + hsBounds::Read(stream); + fMins.Read(stream); + fMaxs.Read(stream); + fBounds3Flags = 0; +} + +void hsBounds3::Write(hsStream *stream) +{ + hsBounds::Write(stream); + fMins.Write(stream); + fMaxs.Write(stream); +} + +////////////////////////////////// +////////////////////////////////////////////////// +// Plane Bounds util class +////////////////////////////////////////////////// + +hsPoint3 hsBoundsOriented::GetCenter() const +{ + hsAssert(fCenterValid==true, "Unset center for hsBoundsOriented::GetCenter()"); + return fCenter; +} + +void hsBoundsOriented::TestPlane(const hsVector3 &n, hsPoint2 &depth) const +{ + hsAssert(false, "TestPlane not a valid operation for hsBounsOriented"); +} +// +// Return true if inside all the planes +// +hsBool hsBoundsOriented::IsInside(const hsPoint3* pos) const +{ + hsAssert(fType == kBoundsNormal, "Invalid bounds type for hsBounds3::IsInside() "); + if(fType == kBoundsEmpty) + return false; + if(fType == kBoundsFull) + return true; + int i; + for( i = 0; i < fNumPlanes; i++ ) + { + hsScalar dis = fPlanes[i].fN.InnerProduct(pos); + dis += fPlanes[i].fD; + if( dis > 0.f ) + return false; + } + + return true; +} + +void hsBoundsOriented::SetNumberPlanes(UInt32 n) +{ + delete [] fPlanes; + fPlanes = TRACKED_NEW hsPlane3[fNumPlanes = n]; +} + +void hsBoundsOriented::SetPlane(UInt32 i, hsPlane3 *pln) +{ + fType = kBoundsNormal; + if( i >= fNumPlanes ) + { + hsPlane3 *newPlanes = TRACKED_NEW hsPlane3[i+1]; + if( fPlanes ) + { + int k; + for( k = 0; k < fNumPlanes; k++ ) + *newPlanes++ = *fPlanes++; + delete [] fPlanes; + } + fPlanes = newPlanes; + fNumPlanes = i+1; + } + fPlanes[i] = *pln; +} + +// +// Make mesh from bounds3. Make boundsOriented from mesh tris. +// +void hsBoundsOriented::Reset(const hsBounds3* bounds) +{ +#if 0 // MESH_GEN_DEFER + hsGTriMesh tMesh; + bounds->MakeTriMesh(&tMesh, 0 /* triFlags */); + Reset(&tMesh); +#endif // MESH_GEN_DEFER +} + +#if 0 +// +// Make mesh from bounds3. Make boundsOriented from mesh tris. +// +void hsBoundsOriented::Union(const hsBounds3 *b) +{ +#if 0 // MESH_GEN_DEFER + hsGTriMesh tMesh; + bounds->MakeTriMesh(&tMesh); + int i; + hsTriangle3 tri; + for (i=0; iGetNumTriangles()]; + UInt32 nPlanes = 0; + + int i; + for( i = 0; i < tMesh->GetNumTriangles(); i++ ) + { + hsTriangle3 *tri; + tri = tMesh->GetTriangle(i); + hsPlane3 pln; + tri->ComputePlane(&pln); + + hsScalar norm = hsFastMath::InvSqrRoot(pln.fN.MagnitudeSquared()); + pln.fN *= norm; + + int j; + for( j = 0; j < nPlanes; j++ ) + { + if( (pln.fN.InnerProduct(planes[j].fN)> OBJCVT_ABOUT_ONE) + &&((pln.fD/planes[j].fD) >= 1.0-OBJCVT_ABOUT_ZERO) + &&((pln.fD/planes[j].fD) <= 1.0+OBJCVT_ABOUT_ZERO) ) + break; + } + if( j == nPlanes ) + planes[nPlanes++] = pln; + } + + SetNumberPlanes(nPlanes); + for( i = 0; i < nPlanes; i++ ) + SetPlane(i, planes+i); + + delete [] planes; + + // Compute center + hsPoint3 centroid(0,0,0); + for(i=0; iGetNumPoints(); i++) + { + centroid = centroid + *tMesh->GetPoint(i); + } + centroid = centroid / (hsScalar)tMesh->GetNumPoints(); + SetCenter(¢roid); +#endif // MESH_GEN_DEFER +} + + +void hsBoundsOriented::Write(hsStream *stream) +{ + hsBounds::Write(stream); + fCenter.Write(stream); + stream->WriteSwap32(fCenterValid); + stream->WriteSwap32(fNumPlanes); + int i; + for( i = 0; i < fNumPlanes; i++ ) + { + fPlanes[i].Write(stream); + } +} + +void hsBoundsOriented::Read(hsStream *stream) +{ + hsBounds::Read(stream); + fCenter.Read(stream); + fCenterValid = (hsBool)stream->ReadSwap32(); + fNumPlanes = stream->ReadSwap32(); + if (fPlanes) + delete [] fPlanes; + fPlanes = TRACKED_NEW hsPlane3[fNumPlanes]; + int i; + for( i = 0; i < fNumPlanes; i++ ) + { + fPlanes[i].Read(stream); + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +hsBounds3Ext::hsBounds3Ext(const hsBounds3 &b) +{ + Reset(&b); +} +hsBounds3Ext &hsBounds3Ext::operator=(const hsBounds3 &b) +{ + Reset(&b); + return *this; +} + +void hsBounds3Ext::IMakeMinsMaxs() +{ + hsAssert(!(fExtFlags & kAxisAligned), "Axis aligned box defined by min and max"); + fMins = fMaxs = fCorner; + int i; + for( i = 0; i < 3; i++ ) + { + if(!IAxisIsZero(i) ) + { + int j; + for( j = 0; j < 3; j++ ) + { + if( fAxes[i][j] < 0 ) + fMins[j] += fAxes[i][j]; + else + fMaxs[j] += fAxes[i][j]; + } + } + } +} + +void hsBounds3Ext::IMakeDists() const +{ + hsAssert(!(fExtFlags & kAxisAligned), "Dists only useful for transformed BB"); + + int i; + for( i = 0; i < 3; i++ ) + { + fDists[i].fX = fCorner.InnerProduct(fAxes[i]); + if( !IAxisIsZero(i) ) + { + fDists[i].fY = fDists[i].fX + fAxes[i].InnerProduct(fAxes[i]); + if( fDists[i].fX > fDists[i].fY ) + { + hsScalar t = fDists[i].fX; + fDists[i].fX = fDists[i].fY; + fDists[i].fY = t; + } + } + else + fDists[i].fY = fDists[i].fX; + } + fExtFlags |= kDistsSet; +} + +void hsBounds3Ext::IMakeSphere() const +{ + if(!(fBounds3Flags & kCenterValid) ) + ICalcCenter(); + if( fExtFlags & kAxisAligned ) + { + if( fBounds3Flags & kIsSphere ) + { + fRadius = fMaxs[0] - fMins[0]; + int i; + for( i = 1; i < 3; i++ ) + { + hsScalar dist = fMaxs[i] - fMins[i]; + if( dist < fRadius ) + fRadius = dist; + } + fRadius *= 0.5f; + } + else + { + fRadius = hsSquareRoot(hsVector3(&fMaxs, &fCenter).MagnitudeSquared()); + } + } + else + { + if( fBounds3Flags & kIsSphere ) + { + hsScalar minMagSq = fAxes[0].MagnitudeSquared(); + hsScalar magSq = fAxes[1].MagnitudeSquared(); + if( magSq < minMagSq ) + magSq = minMagSq; + magSq = fAxes[2].MagnitudeSquared(); + if( magSq < minMagSq ) + magSq = minMagSq; + fRadius = hsSquareRoot(magSq); + } + else + { + hsVector3 accum; + accum.Set(0,0,0); + int i; + for( i = 0; i < 3; i++ ) + { + if( !IAxisIsZero(i) ) + accum += fAxes[i]; + } + fRadius = hsSquareRoot((accum * 0.5f).MagnitudeSquared()); + } + } + fExtFlags |= kSphereSet; +} + +void hsBounds3Ext::Reset(const hsBounds3 *b) +{ + fExtFlags = kAxisAligned; + hsBounds3::Reset(b); +} + +void hsBounds3Ext::Reset(const hsPoint3 *p) +{ + fExtFlags = kAxisAligned | kSphereSet; + hsBounds3::Reset(p); + fRadius = 0; +} + +void hsBounds3Ext::Reset(const hsBounds3Ext *b) +{ + hsBounds3::Reset(b); + fExtFlags = b->fExtFlags; + if (!(fExtFlags & kAxisAligned)) + { + fCorner = b->fCorner; + fAxes[0] = b->fAxes[0]; + fAxes[1] = b->fAxes[1]; + fAxes[2] = b->fAxes[2]; + } + if (fExtFlags & kDistsSet) + { + fDists[0] = b->fDists[0]; + fDists[1] = b->fDists[1]; + fDists[2] = b->fDists[2]; + } + if (fExtFlags & kSphereSet) + fRadius = b->fRadius; +} + + +void hsBounds3Ext::GetCorners(hsPoint3 *b) const +{ + if( fExtFlags & kAxisAligned ) + { + hsBounds3::GetCorners(b); + } + else + { + int i; + for( i = 0; i < 8; i++ ) + { + b[i] = fCorner; + if( !(i & 0x1) && !(fExtFlags & kAxisZeroZero) )b[i] += fAxes[0]; + if( !(i & 0x2) && !(fExtFlags & kAxisOneZero) )b[i] += fAxes[1]; + if( !(i & 0x4) && !(fExtFlags & kAxisTwoZero) )b[i] += fAxes[2]; + } + } +} + +void hsBounds3Ext::GetAxes(hsVector3 *fAxis0, hsVector3 *fAxis1, hsVector3 *fAxis2) const +{ + if( !(fExtFlags & kAxisAligned) ) + { + *fAxis0 = fAxes[0]; + *fAxis1 = fAxes[1]; + *fAxis2 = fAxes[2]; + } + else + { + fAxis0->Set(fMaxs.fX - fMins.fX, 0, 0); + fAxis1->Set(0, fMaxs.fY - fMins.fY, 0); + fAxis2->Set(0, 0, fMaxs.fZ - fMins.fZ); + } +} + +void hsBounds3Ext::Reset(int n, const hsPoint3 *p) +{ + fExtFlags = kAxisAligned; + hsBounds3::Reset(n, p); +} + +// mf horse - could union in a point preserving axes... +void hsBounds3Ext::Union(const hsPoint3 *p) +{ + fExtFlags = kAxisAligned; + hsBounds3::Union(p); +} +void hsBounds3Ext::Union(const hsVector3 *v) +{ +#if 0 // smarter union + fExtFlags = kAxisAligned; + hsBounds3::Union(v); +#else // smarter union + if( fExtFlags & kAxisAligned ) + { + hsBounds3::Union(v); + } + else + { + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar dot = fAxes[i].InnerProduct(v); + dot /= fAxes[i].MagnitudeSquared(); + if( dot > 0 ) + { + fAxes[i] += dot * fAxes[i]; + fExtFlags &= ~(1 << (20+i)); // axis not zero no more + } + else if( dot < 0 ) + { + hsVector3 del = dot * fAxes[i]; + fCorner += del; + del = -del; + fAxes[i] += del; + fExtFlags &= ~(1 << (20+i)); // axis not zero no more + } + } + fExtFlags &= ~(kSphereSet | kDistsSet); + fBounds3Flags &= ~kCenterValid; + } +#endif // smarter union +} + +void hsBounds3Ext::Union(const hsBounds3 *b) +{ + fExtFlags = kAxisAligned; + hsBounds3::Union(b); +} + +void hsBounds3Ext::MakeSymmetric(const hsPoint3* p) +{ + if( fType != kBoundsNormal ) + return; + + if( fExtFlags & kAxisAligned ) + { + fExtFlags = kAxisAligned; + hsBounds3::MakeSymmetric(p); + return; + } + + // Can do this preserving axes, but may not be worth it. + fExtFlags = kAxisAligned; + hsBounds3::MakeSymmetric(p); +} + +void hsBounds3Ext::InscribeSphere() +{ + fBounds3Flags |= kIsSphere; + fExtFlags |= kAxisAligned; + IMakeSphere(); + return; +#if 0 + if( fType != kBoundsNormal ) + return; + + if( fExtFlags & kAxisAligned ) + { + hsBounds3::InscribeSphere(); + return; + } + + const hsScalar oneThird = hsScalarInvert(3.f); +// hsScalar a = GetMaxDim() * hsScalarInvert(6.f); + hsScalar a = GetRadius() * oneThird; + hsPoint3 p = GetCenter(); + p.fX += a; + p.fY += a; + p.fZ += a; + fMaxs = p; + a *= -2.f; + p.fX += a; + p.fY += a; + p.fZ += a; + fMins = p; + + // Center still valid, type still normal + fExtFlags = kAxisAligned; +#endif +} + +void hsBounds3Ext::Transform(const hsMatrix44 *m) +{ + if( fType != kBoundsNormal ) + return; + + if( fExtFlags & kAxisAligned ) + { + fExtFlags = 0; + + fCorner = *m * fMins; + hsVector3 v; + hsScalar span; + span = fMaxs.fX - fMins.fX; + if( span < kRealSmall ) + { + fExtFlags |= kAxisZeroZero; + span = hsScalar1; + } + v.Set(span, 0, 0); + fAxes[0] = *m * v; + span = fMaxs.fY - fMins.fY; + if( span < kRealSmall ) + { + fExtFlags |= kAxisOneZero; + span = hsScalar1; + } + v.Set(0, span, 0); + fAxes[1] = *m * v; + span = fMaxs.fZ - fMins.fZ; + if( span < kRealSmall ) + { + fExtFlags |= kAxisTwoZero; + span = hsScalar1; + } + v.Set(0, 0, span); + fAxes[2] = *m * v; + + } + else + { +#if 0 // IDENT + if( m->fFlags & hsMatrix44::kIsIdent ) + return; +#endif // IDENT + + fCorner = *m * fCorner; + fAxes[0] = *m * fAxes[0]; + fAxes[1] = *m * fAxes[1]; + fAxes[2] = *m * fAxes[2]; + + fExtFlags &= kAxisZeroZero|kAxisOneZero|kAxisTwoZero; + } + IMakeMinsMaxs(); + fBounds3Flags &= ~kCenterValid; +} + +void hsBounds3Ext::Translate(const hsVector3 &v) +{ + if( fType != kBoundsNormal ) + return; + + fMins += v; + fMaxs += v; + if( fBounds3Flags & kCenterValid ) + fCenter += v; + if( !(fExtFlags & kAxisAligned) ) + { + fCorner += v; + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar d; + d = fAxes[i].InnerProduct(v); + fDists[i].fX += d; + fDists[i].fY += d; + } + } +} + +hsBool hsBounds3Ext::IsInside(const hsPoint3 *p) const +{ + if( fExtFlags & kAxisAligned ) + return hsBounds3::IsInside(p); + + if( !(fExtFlags & kDistsSet) ) + IMakeDists(); + + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar diss = p->InnerProduct(fAxes[i]); + if( (diss < fDists[i].fX) + ||(diss > fDists[i].fY) ) + return false; + } + return true; + +} + +// neg, pos, zero == disjoint, I contain other, overlap +Int32 hsBounds3Ext::TestBound(const hsBounds3Ext& other) const +{ + if( fExtFlags & kAxisAligned ) + return hsBounds3::TestBound(other); + + if( !(fExtFlags & kDistsSet) ) + IMakeDists(); + + Int32 retVal = 1; + int i; + for( i = 0; i < 3; i++ ) + { + hsPoint2 depth; + other.TestPlane(fAxes[i], depth); + + if( fDists[i].fX > depth.fY ) + return -1; + if( fDists[i].fY < depth.fX ) + return -1; + + if( fDists[i].fY < depth.fY ) + retVal = 0; + if( fDists[i].fX > depth.fX ) + retVal = 0; + } + return retVal; +} + + +void hsBounds3Ext::TestPlane(const hsVector3 &n, hsPoint2 &depth) const +{ + hsAssert(fType == kBoundsNormal, "TestPlane only valid for kBoundsNormal filled bounds"); + if( fExtFlags & kAxisAligned ) + { + hsBounds3::TestPlane(n, depth); + } + else + { + hsScalar dmax = fCorner.InnerProduct(n); + hsScalar dmin = dmax; + + int i; + for( i = 0; i < 3; i++ ) + { + if( !IAxisIsZero(i) ) + { + hsScalar d; + d = fAxes[i].InnerProduct(n); + if( d < 0 ) + dmin += d; + else + dmax += d; + } + } + + depth.fX = dmin; + depth.fY = dmax; + } +} + +void hsBounds3Ext::TestPlane(const hsPlane3 *p, const hsVector3 &myVel, hsPoint2 &depth) const +{ + TestPlane(p->fN, myVel, depth); +} +void hsBounds3Ext::TestPlane(const hsVector3 &n, const hsVector3 &myVel, hsPoint2 &depth) const +{ + if( fExtFlags & kAxisAligned ) + { + hsScalar dmax = fMins.InnerProduct(n); + hsScalar dmin = dmax; + hsScalar dvel = myVel.InnerProduct(n); + if( dvel < 0 ) + dmin += dvel; + else + dmax += dvel; + + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar dd; + dd = fMaxs[i] - fMins[i]; + dd *= n[i]; + + if( dd < 0 ) + dmin += dd; + else + dmax += dd; + } + + depth.fX = dmin; + depth.fY = dmax; + } + else + { + hsScalar dmax = fCorner.InnerProduct(n); + hsScalar dmin = dmax; + hsScalar dvel = myVel.InnerProduct(n); + if( dvel < 0 ) + dmin += dvel; + else + dmax += dvel; + + int i; + for( i = 0; i < 3; i++ ) + { + if( !IAxisIsZero(i) ) + { + hsScalar d; + d = fAxes[i].InnerProduct(n); + if( d < 0 ) + dmin += d; + else + dmax += d; + } + } + + depth.fX = dmin; + depth.fY = dmax; + } +} + +Int32 hsBounds3Ext::TestPoints(int n, const hsPoint3 *pList, const hsVector3 &ptVel) const +{ + if( fExtFlags & kAxisAligned ) + { + Int32 retVal = -1; + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar effMax = fMaxs[i]; + hsScalar effMin = fMins[i]; + if( ptVel[i] < 0 ) + effMax -= ptVel[i]; + else + effMin -= ptVel[i]; + + int j; + const UInt32 low = 0x1, hi = 0x2; + UInt32 mask = low | hi; + for( j = 0; j < n; j++ ) + { + if( pList[j][i] > effMin ) + mask &= ~low; + if( pList[j][i] < effMax ) + mask &= ~hi; + if( mask ) + retVal = 0; + } + if( mask ) + return 1; + } + return retVal; + } + else // non-axis aligned case + { + Int32 retVal = -1; // all inside + if( !(fExtFlags & kDistsSet) ) + IMakeDists(); + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar diff = fAxes[i].InnerProduct(ptVel); + hsBool someLow = false; + hsBool someHi = false; + hsBool someIn = false; + int j; + for( j = 0; j < n; j++ ) + { + hsScalar d = fAxes[i].InnerProduct(pList[j]); + hsScalar ddiff = d + diff; + if( d < fDists[i].fX ) + someLow = true; + else if( d > fDists[i].fY ) + someHi = true; + else + someIn = true; + + if( ddiff < fDists[i].fX ) + someLow = true; + else if( ddiff > fDists[i].fY ) + someHi = true; + else + someIn = true; + + if( someIn &&(someHi || someLow) ) + break; + } + if( someHi && !(someLow || someIn) ) + return 1; + if( someLow && !(someHi || someIn) ) + return 1; + if( someHi || someLow ) + retVal = 0; + } + return retVal; + } +} + +Int32 hsBounds3Ext::TestPoints(int n, const hsPoint3 *pList) const +{ + hsBool someIn = false; + hsBool someOut = false; + int i; + for( i = 0; i < n; i++ ) + { + if( IsInside(pList+i) ) + someIn = true; + else + someOut = true; + if( someIn && someOut ) + return 0; + } + if( someIn ) + return -1; + return 1; +} + +hsBool hsBounds3Ext::ClosestPoint(const hsPoint3& p, hsPoint3& inner, hsPoint3& outer) const +{ + if( fExtFlags & kAxisAligned ) + return hsBounds3::ClosestPoint(p, inner, outer); + + if( !(fExtFlags & kDistsSet) ) + IMakeDists(); + + int nSect = 0; + inner = outer = fCorner; + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar dist = fAxes[i].InnerProduct(p); + if( dist < fDists[i].fX ) + { + outer += fAxes[i]; + } + else if( dist > fDists[i].fY ) + { + inner += fAxes[i]; + } + else + { + hsScalar t = (dist - fDists[i].fX) / (fDists[i].fY - fDists[i].fX); + inner += t * fAxes[i]; + if( t > 0.5f ) + outer += fAxes[i]; + + nSect++; + } + } + return nSect == 3; +} + +hsBool hsBounds3Ext::ISectBB(const hsBounds3Ext &other, const hsVector3 &myVel) const +{ + if( fExtFlags & kAxisAligned ) + { + if( other.fExtFlags & kAxisAligned ) + return ISectABB(other, myVel); + + return other.ISectBB(*this, -myVel); + } + hsAssert(!(fExtFlags & kAxisAligned), "Other can be axis-aligned, but not me!"); + hsPoint2 depth; + + if( !(fExtFlags & kDistsSet) ) + IMakeDists(); + if( !(other.fExtFlags & (kDistsSet|kAxisAligned)) ) + other.IMakeDists(); + + int i; + for( i = 0; i < 3; i++ ) + { + other.TestPlane(fAxes[i], -myVel, depth); + if( (depth.fX > fDists[i].fY) + ||(depth.fY < fDists[i].fX) ) + return false; + + if( other.fExtFlags & kAxisAligned ) + { + hsScalar myMin = fMins[i]; + hsScalar myMax = fMaxs[i]; + if( myVel[i] < 0 ) + myMin += myVel[i]; + else + myMax += myVel[i]; + if( (other.fMins[i] > myMax) + ||(other.fMaxs[i] < myMin) ) + return false; + } + else + { + TestPlane(other.fAxes[i], myVel, depth); + if( (depth.fX > other.fDists[i].fY) + ||(depth.fY < other.fDists[i].fX) ) + return false; + } + + // still leaves the 3 axes of origAxis.Cross(myVel) + hsVector3 ax = fAxes[i] % myVel; + hsScalar dmax = fCorner.InnerProduct(ax); + hsScalar dmin = dmax; + int j = i+1; + if( 3 == j )j = 0; + hsScalar d; + d = fAxes[j].InnerProduct(ax); + if( d < 0 ) + dmin += d; + else + dmax += d; + j = ( j == 2 ? 0 : j+1 ); + d = fAxes[j].InnerProduct(ax); + if( d < 0 ) + dmin += d; + else + dmax += d; + other.TestPlane(ax, depth); + if( (depth.fX > dmax) + ||(depth.fY < dmin) ) + return false; + } + + + return true; +} + + +static hsBool ISectInterval(const hsPoint2& other, const hsPoint2& mine) +{ + if( other.fY - mine.fX <= 0 ) + return false; + if( mine.fY - other.fX <= 0 ) + return false; + + return true; +} + +static hsBool ITestDepth(const hsPoint2& other, const hsPoint2& mine, + const hsVector3& inAx, + hsVector3 &outAx, hsScalar& depth) +{ + depth = 0; + hsScalar d0, d1; + d0 = other.fY - mine.fX; + if( d0 <= 0 ) + return false; + d1 = mine.fY - other.fX; + if( d1 <= 0 ) + return false; + + // if one interval is proper subset of other, skip + if( (mine.fX < other.fX)^(mine.fY < other.fY) ) + { + depth = 0; + return true; + } + if( d0 < d1 ) + { + outAx = inAx; + depth = d0; + return true; + } + + outAx = -inAx; + depth = d1; + return true; +} + +Int32 hsBounds3Ext::IClosestISect(const hsBounds3Ext& other, const hsVector3& myVel, + hsScalar* tClose, hsScalar* tImpact) const +{ + // Should assert both have their spheres set. + + hsVector3 meToOt(&other.GetCenter(), &GetCenter()); + + // cTerm = (myCenter - otCenter)^2 - (myRad + otRad)^2 + hsScalar cTerm; + + cTerm = GetRadius() + other.GetRadius(); + cTerm *= -cTerm; + + hsScalar meToOtLen = meToOt.MagnitudeSquared(); + cTerm += meToOtLen; + if( cTerm <= 0 ) + { + *tClose = *tImpact = 0; + return -1; // started off in contact + } + + hsScalar ooATerm = myVel.InnerProduct(myVel); + if( ooATerm < hsBounds::kRealSmall ) + { + *tClose = *tImpact = 0; + return 0; + } + ooATerm = hsScalarInvert(ooATerm); + + hsScalar bTerm = myVel.InnerProduct(meToOt); + bTerm *= ooATerm; + hsScalar bSqTerm = bTerm * bTerm; + // bTerm is t for closest point to line + + hsScalar det = bSqTerm - ooATerm * cTerm; + if( det < 0 ) + { + *tClose = *tImpact = bTerm; + return 0; + } + + det = hsSquareRoot(det); + *tClose = bTerm; + *tImpact = bTerm - det; + + return 1; +} + +void hsBounds3Ext::Unalign() +{ + fExtFlags = 0; + + fCorner = fMins; + hsVector3 v; + hsScalar span; + span = fMaxs.fX - fMins.fX; + if( span < kRealSmall ) + { + fExtFlags |= kAxisZeroZero; + span = hsScalar1; + } + fAxes[0].Set(span, 0, 0); + span = fMaxs.fY - fMins.fY; + if( span < kRealSmall ) + { + fExtFlags |= kAxisOneZero; + span = hsScalar1; + } + fAxes[1].Set(0, span, 0); + span = fMaxs.fZ - fMins.fZ; + if( span < kRealSmall ) + { + fExtFlags |= kAxisTwoZero; + span = hsScalar1; + } + fAxes[2].Set(0, 0, span); +} + +hsBool hsBounds3Ext::ISectBB(const hsBounds3Ext &other, const hsVector3 &myVel, hsHitInfoExt *hit) const +{ + if( fExtFlags & kAxisAligned ) + { + hsBounds3Ext meUnalign(*this); + meUnalign.Unalign(); + return meUnalign.ISectBB(other, myVel, hit); + } + hsAssert(!(fExtFlags & kAxisAligned), "Other can be axis-aligned, but not me!"); + hsPoint2 depth; + + if( !(fExtFlags & kDistsSet) ) + IMakeDists(); + if( !(other.fExtFlags & (kDistsSet|kAxisAligned)) ) + other.IMakeDists(); + + const hsScalar kRealBig = 1.e30f; + hsScalar tstDepths[9]; + hsVector3 tstAxes[9]; + hsScalar totDepth = 0; + int nDeep = 0; + int i; + for( i = 0; i < 3; i++ ) + { + const hsScalar kFavorConstant = 0.01f; // smaller is favored + + other.TestPlane(fAxes[i], -myVel, depth); + + if( !ITestDepth(depth, fDists[i], fAxes[i], tstAxes[i], tstDepths[i]) ) + return false; + + other.TestPlane(fAxes[i], depth); + if( !ISectInterval(depth, fDists[i]) ) + tstDepths[i] *= kFavorConstant; + + if( tstDepths[i] > 0 ) + { + totDepth += tstDepths[i]; + nDeep++; + } + + if( other.fExtFlags & kAxisAligned ) + { + hsPoint2 mine; + mine.fX = fMins[i]; + mine.fY = fMaxs[i]; + if( myVel[i] > 0 )mine.fY += myVel[i]; + else mine.fX += myVel[i]; + depth.fX = other.fMins[i]; + depth.fY = other.fMaxs[i]; + + hsVector3 ax; + ax.Set( 0 == i ? hsScalar1 : 0, + 1 == i ? hsScalar1 : 0, + 2 == i ? hsScalar1 : 0); + + if( !ITestDepth(depth, mine, ax, tstAxes[i+3], tstDepths[i+3]) ) + return false; + + mine.fX = fMins[i]; + mine.fY = fMaxs[i]; + if( !ISectInterval(depth, mine) ) + tstDepths[i+3] *= kFavorConstant; + + if( tstDepths[i+3] ) + { + totDepth += tstDepths[i+3]; + nDeep++; + } + } + else + { + TestPlane(other.fAxes[i], myVel, depth); + + if( !ITestDepth(other.fDists[i], depth, other.fAxes[i], tstAxes[i+3], tstDepths[i+3]) ) + return false; + + TestPlane(other.fAxes[i], depth); + + if( !ISectInterval(other.fDists[i], depth) ) + tstDepths[i+3] *= kFavorConstant; + + if( tstDepths[i+3] ) + { + totDepth += tstDepths[i+3]; + nDeep++; + } + } + +#if 0 + // still leaves the 3 axes of origAxis.Cross(myVel) + hsVector3 ax = fAxes[i] % myVel; + if( ax.MagnitudeSquared() > kRealSmall ) + { + hsPoint2 myDepth; + myDepth.fX = myDepth.fY = fCorner.InnerProduct(ax); + hsScalar d; + int j = i == 2 ? 0 : i+1; + if( !IAxisIsZero(j) ) + { + d = fAxes[j].InnerProduct(ax); + if( d < 0 ) + myDepth.fX += d; + else + myDepth.fY += d; + } + j = ( j == 2 ? 0 : j+1 ); + if( !IAxisIsZero(j) ) + { + d = fAxes[j].InnerProduct(ax); + if( d < 0 ) + myDepth.fX += d; + else + myDepth.fY += d; + } + other.TestPlane(ax, depth); + + if( !ITestDepth(depth, myDepth, ax, tstAxes[i+6], tstDepths[i+6]) ) + return false; + totDepth += tstDepths[i+6]; + } + else + tstDepths[i+6] = 0; +#endif; + } + + hsVector3 norm; + if( totDepth <= 0 ) + { + hsScalar t, tIgnore; + IClosestISect(other, myVel, &tIgnore, &t); + if( t < 0 ) + t = 0; + else if( t > 1.f ) + t = 1.f; + hsPoint3 hitPt = GetCenter() + myVel * t; + norm.Set(&hitPt, &other.GetCenter()); + } + else + { + // now do a weighted average of the axes + hsAssert(totDepth > 0, "nobody home"); + norm.Set(0,0,0); + for( i =0; i < 6; i++ ) + { + if( tstDepths[i] > 0 ) + norm += tstAxes[i] / tstDepths[i]; +// norm += tstAxes[i] * (1.f - tstDepths[i] / totDepth); + } + } + hsPoint2 otherDepth; + norm.Normalize(); + other.TestPlane(norm, otherDepth); + TestPlane(norm, myVel, depth); + + hit->Set(this, &other, norm, otherDepth.fY - depth.fX); + + // mf horse hack test + if( hit->fDepth < 0 ) + return false; + hsAssert(hit->fDepth >= 0, "Negative Depth"); + + return true; +} + +hsBool hsBounds3Ext::ISectABB(const hsBounds3Ext &other, const hsVector3 &myVel) const +{ + hsPoint3 effMaxs = fMaxs; + hsPoint3 effMins = fMins; + + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar effMax = fMaxs[i]; + hsScalar effMin = fMins[i]; + if( myVel[i] > 0 ) + effMax += myVel[i]; + else + effMin += myVel[i]; + + if( (effMax < other.fMins[i]) + ||(effMin > other.fMaxs[i]) ) + return false; + } + return true; +} + +hsBool hsBounds3Ext::ISectBS(const hsBounds3Ext &other, const hsVector3 &myVel) const +{ + if( !(fExtFlags & kSphereSet) ) + IMakeSphere(); + if( !(other.fExtFlags & kSphereSet) ) + other.IMakeSphere(); + + hsPoint3 closestPt = GetCenter(); + // we should know whether we have a useful velocity or not... + // having the speed cached away would get rid of several + // such uglies... + if( myVel.MagnitudeSquared() > 0 ) + { + hsScalar parm = hsVector3(&other.GetCenter(), &fCenter).InnerProduct(myVel) + / myVel.InnerProduct(myVel); + if( parm > 0 ) + { + if( parm > hsScalar1 ) + parm = hsScalar1; + closestPt += myVel * parm; + } + } + + hsScalar combRad = fRadius + other.fRadius; + + return hsVector3(&closestPt, &other.GetCenter()).MagnitudeSquared() < combRad*combRad; +} + +#if 0 // Commenting out this which will be made redundant and/or obsolete by Havok integration +hsBool hsBounds3Ext::ISectTriABB(hsBounds3Tri &tri, const hsVector3 &myVel) const +{ + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar effMax = fMaxs[i]; + hsScalar effMin = fMins[i]; + if( myVel[i] < 0 ) + effMin += myVel[i]; + else + effMax += myVel[i]; + + int j; + const UInt32 low = 0x1, hi = 0x2; + UInt32 mask = low | hi; + for( j = 0; j < 3; j++ ) + { + if( tri.fVerts[j][i] > effMin ) + mask &= ~low; + if( tri.fVerts[j][i] < effMax ) + mask &= ~hi; + } + if( mask ) + return false; + } + return true; +} + +hsBool hsBounds3Ext::TriBSHitInfo(hsBounds3Tri& tri, const hsVector3& myVel, hsHitInfoExt* hit) const +{ + hsPoint3 myPt = GetCenter(); + myPt += myVel; + + hsPoint3 closePt; + hsBool onTri = tri.ClosestTriPoint(&myPt, &closePt); + + hsVector3 repel; + repel.Set(&myPt, &closePt); + + hsScalar myDepth; + hsScalar repelMagSq = repel.MagnitudeSquared(); + if( repelMagSq < hsBounds::kRealSmall ) + { + repel = tri.fNormal; + myDepth = GetRadius(); + } + else + { + myDepth = hsFastMath::InvSqrt(repelMagSq); + repel *= myDepth; + myDepth = 1.f / myDepth; + myDepth = GetRadius() - myDepth; + if( myDepth < 0 ) + myDepth = 0; + } + + if( tri.fNormal.InnerProduct(myPt) < tri.fDist ) + { + repel += tri.fNormal * (-2.f * repel.InnerProduct(tri.fNormal)); + myDepth = GetRadius() * 2.f - myDepth; + if( myDepth < 0 ) + myDepth = 0; + } + + hit->Set(this, &tri, &repel, myDepth); + + return true; +} + +#if 0 // TOCENTER +hsBool hsBounds3Ext::TriBBHitInfo(hsBounds3Tri& tri, const hsVector3& myVel, hsHitInfoExt* hit) const +{ + // Find our closest point (after movement) + hsPoint3 myPt = fCorner; + myPt += myVel; + + const hsScalar kMinDist = 1.f; // Huge min dist because world is really big right now. mf horse + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar axDot = fAxes[i].InnerProduct(tri.fNormal); + if( axDot < -kMinDist ) + { + // moving towards + myPt += fAxes[i]; + } + else if( axDot < kMinDist ) + { + // need to interp + axDot /= -(kMinDist*2.f); + axDot += 0.5f; + myPt += fAxes[i] * axDot; + } + // else moving away, skip it + } + + // Find closest point on tri to our closest corner + hsPoint3 closePt; + hsBool onTri = tri.ClosestTriPoint(&myPt, &closePt); + + // Repel vector is from closest corner to closest point on tri + hsVector3 repel; + repel.Set(&myPt, &closePt); + repel += (-2.f * repel.InnerProduct(tri.fNormal)) * tri.fNormal; + + hsScalar repelMag = hsFastMath::InvSqrt(repel.MagnitudeSquared()); + + if( repelMag < hsBounds::kRealSmall ) + { + hsPoint2 faceDepth; + TestPlane(tri.fNormal, myVel, faceDepth); + hit->Set(this, &tri, &tri.fNormal, tri.fDist - faceDepth.fX); + return true; + } + + repel *= repelMag; + repelMag = 1.f / repelMag; + + hit->Set(this, &tri, &repel, repelMag); + + // Return true of our closest corner projects on to tri (along normal or myVel?) + return onTri; +} +#else // TOCENTER + +hsBool hsBounds3Ext::TriBBHitInfo(hsBounds3Tri& tri, const hsVector3& myVel, hsHitInfoExt* hit) const +{ + hsPoint3 myPt = GetCenter(); + myPt += myVel; + + hsPoint3 closePt; + hsBool onTri = tri.ClosestTriPoint(&myPt, &closePt); + + hsVector3 repel; + repel.Set(&myPt, &closePt); + hsScalar repelDotNorm = repel.InnerProduct(tri.fNormal); + if( repelDotNorm < 0 ) + { + repel += (-2.f * repelDotNorm) * tri.fNormal; + } + + hsScalar repelMagSq = repel.MagnitudeSquared(); + if( repelMagSq < hsBounds::kRealSmall ) + repel = tri.fNormal; + else + { + hsScalar repelMag = hsFastMath::InvSqrt(repelMagSq); + repel *= repelMag; + } + + + hsPoint2 triDepth; + tri.TestPlane(repel, triDepth); + + hsPoint2 myDepth; + TestPlane(repel, myVel, myDepth); + + hit->Set(this, &tri, &repel, triDepth.fY - myDepth.fX); + + return true; +} + +#endif // TOCENTER + +hsBool hsBounds3Ext::ISectTriBB(hsBounds3Tri &tri, const hsVector3 &myVel) const +{ + hsPoint2 faceDepth; + // first test box against the triangle plane + TestPlane(tri.fNormal, myVel, faceDepth); + + if( (tri.fDist > faceDepth.fY) + ||(tri.fDist < faceDepth.fX) ) + return false; + + // now test tri against box planes + if( TestPoints(3, tri.fVerts, -myVel) > 0 ) + return false; + + if( !(tri.fTriFlags & hsBounds3Tri::kAxesSet) ) + tri.SetAxes(); + + hsScalar depth = tri.fDist - faceDepth.fX; + hsVector3 norm = tri.fNormal; + + // that only leaves the planes of triEdge.Cross(vel) + int i; + for( i = 0; i < 3; i++ ) + { + hsPoint2 depths; + TestPlane(tri.fPerpAxes[i], myVel, depths); + if( (tri.fPerpDists[i].fY < depths.fX) + ||(tri.fPerpDists[i].fX > depths.fY) ) + return false; + +#if 0 + hsScalar testDepth = tri.fPerpDists[i].fY - depths.fX; + if( testDepth < depth ) + { + depth = testDepth; + norm = tri.fPerpAxes[i]; + } +#endif + } + hsScalar vDotN = myVel.InnerProduct(tri.fNormal); + if( vDotN > 0 ) + depth -= vDotN; + + if( depth <= 0 ) + return false; + + return true; +} + +hsBool hsBounds3Ext::ISectTriBB(hsBounds3Tri &tri, const hsVector3 &myVel, hsHitInfoExt *hit) const +{ + hsPoint2 faceDepth; + // first test box against the triangle plane + TestPlane(tri.fNormal, myVel, faceDepth); + + if( (tri.fDist > faceDepth.fY) + ||(tri.fDist < faceDepth.fX) ) + return false; + + hsScalar centDist = tri.fNormal.InnerProduct(hit->fRootCenter); + if( centDist < tri.fDist ) + return false; + + // now test tri against box planes + if( TestPoints(3, tri.fVerts, -myVel) > 0 ) + return false; + + if( !(tri.fTriFlags & hsBounds3Tri::kAxesSet) ) + tri.SetAxes(); + + hsScalar depth = tri.fDist - faceDepth.fX; + hsVector3 norm = tri.fNormal; + + // that only leaves the planes of triEdge.Cross(vel) + int i; + for( i = 0; i < 3; i++ ) + { + hsPoint2 depths; + TestPlane(tri.fPerpAxes[i], myVel, depths); + if( (tri.fPerpDists[i].fY < depths.fX) + ||(tri.fPerpDists[i].fX > depths.fY) ) + return false; + +#if 0 + hsScalar testDepth = tri.fPerpDists[i].fY - depths.fX; + if( testDepth < depth ) + { + depth = testDepth; + norm = tri.fPerpAxes[i]; + } +#endif + } + hsScalar vDotN = myVel.InnerProduct(tri.fNormal); + if( vDotN > 0 ) + depth -= vDotN; + + if( (tri.fTriFlags & hsBounds3Tri::kDoubleSide) ) + { + if( tri.fNormal.InnerProduct(hit->fRootCenter) - tri.fDist < 0 ) + { + depth = -tri.fDist + faceDepth.fY; + if( vDotN < 0 ) + depth += vDotN; + + tri.fNormal = -tri.fNormal; + tri.fDist = -tri.fDist; + } + } + if( depth <= 0 ) + return false; + + // printf("ATTRIBUTE triBnd addr %x\n",&tri.fNormal); /* Takashi Nakata TEST Add */ + hit->Set(this, &tri, &norm, depth); + + return hit->fDepth > hsBounds::kRealSmall; +} + +hsBool hsBounds3Ext::ISectTriBS(hsBounds3Tri &tri, const hsVector3 &myVel) const +{ + if( !(fExtFlags & kSphereSet) ) + IMakeSphere(); + + hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (TriBS)"); + hsScalar radScaled = fRadius * tri.fNormal.Magnitude(); + hsScalar centerDist = tri.fNormal.InnerProduct(fCenter); + hsScalar velDist = tri.fNormal.InnerProduct(myVel); + hsScalar effMin = centerDist; + hsScalar effMax = centerDist; + + if( velDist > 0 ) + effMax += velDist; + else + effMin += velDist; + effMax += radScaled; + effMin -= radScaled; + + if( tri.fDist <= effMin ) + return false; + if( tri.fDist >= effMax ) + return false; + + // mf horse + hsScalar normDepth = tri.fDist - (centerDist - radScaled + velDist); + if( normDepth <= 0 ) + { + // we'll report a depth of zero to (hopefully) neutralize any effects + if( tri.fTriFlags & hsBounds3Tri::kDoubleSide ) + { + normDepth = -tri.fDist + (centerDist + radScaled + velDist); + if( normDepth > 0 ) + { + tri.fDist = -tri.fDist; + tri.fNormal = -tri.fNormal; + } + else + normDepth = 0; + } + else + normDepth = 0; + } + hsAssert(normDepth >= 0, "NegativeDepth"); + + if( !(tri.fTriFlags & hsBounds3Tri::kAxesSet) ) + tri.SetAxes(); + + hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (TriBS)"); + int i; + for( i = 0; i < 3; i++ ) + { + centerDist = tri.fPerpAxes[i].InnerProduct(fCenter); + velDist = tri.fPerpAxes[i].InnerProduct(myVel); + effMin = centerDist; + effMax = centerDist; + + if( velDist > 0 ) + effMax += velDist; + else + effMin += velDist; + + hsScalar radScale = fRadius * tri.fPerpAxes[i].Magnitude(); + effMax += radScale; + effMin -= radScale; + if( tri.fPerpDists[i].fY <= effMin ) + return false; + if( tri.fPerpDists[i].fX >= effMax ) + return false; + + } + + return true; +} + +hsBool hsBounds3Ext::ISectTriBS(hsBounds3Tri &tri, const hsVector3 &myVel, hsHitInfoExt *hit) const +{ + if( !(fExtFlags & kSphereSet) ) + IMakeSphere(); + + hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (TriBS)"); + hsScalar radScaled = fRadius * tri.fNormal.Magnitude(); + hsScalar centerDist = tri.fNormal.InnerProduct(fCenter); + hsScalar velDist = tri.fNormal.InnerProduct(myVel); + hsScalar effMin = centerDist; + hsScalar effMax = centerDist; + + if( velDist > 0 ) + effMax += velDist; + else + effMin += velDist; + effMax += radScaled; + effMin -= radScaled; + + if( tri.fDist <= effMin ) + return false; + if( tri.fDist >= effMax ) + return false; + + // mf horse + hsScalar normDepth = tri.fDist - (centerDist - radScaled + velDist); + if( normDepth <= 0 ) + { +#if 0 // need to report the collision even if the object is leaving the tri + // we'll report a depth of zero to (hopefully) neutralize any effects + if(!(tri.fTriFlags & hsBounds3Tri::kDoubleSide) ) + return false; + normDepth = -tri.fDist + (centerDist + radScaled + velDist); + if( normDepth <= 0 ) + return false; + tri.fDist = -tri.fDist; + tri.fNormal = -tri.fNormal; +#else + // we'll report a depth of zero to (hopefully) neutralize any effects + if( tri.fTriFlags & hsBounds3Tri::kDoubleSide ) + { + normDepth = -tri.fDist + (centerDist + radScaled + velDist); + if( normDepth > 0 ) + { + tri.fDist = -tri.fDist; + tri.fNormal = -tri.fNormal; + } + else + normDepth = 0; + } + else + normDepth = 0; +#endif + } + hsAssert(normDepth >= 0, "NegativeDepth"); + + if( !(tri.fTriFlags & hsBounds3Tri::kAxesSet) ) + tri.SetAxes(); + + hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (TriBS)"); + int i; + for( i = 0; i < 3; i++ ) + { + centerDist = tri.fPerpAxes[i].InnerProduct(fCenter); + velDist = tri.fPerpAxes[i].InnerProduct(myVel); + effMin = centerDist; + effMax = centerDist; + + if( velDist > 0 ) + effMax += velDist; + else + effMin += velDist; + + hsScalar radScale = fRadius * tri.fPerpAxes[i].Magnitude(); + effMax += radScale; + effMin -= radScale; + if( tri.fPerpDists[i].fY <= effMin ) + return false; + if( tri.fPerpDists[i].fX >= effMax ) + return false; + + } + + hsScalar invLen = hsScalarInvert(tri.fNormal.Magnitude()); + hit->Set(this, &tri, &tri.fNormal, normDepth); + + // mf horse - move this into Set()? + hit->fNormal *= invLen; + hit->fDepth *= invLen; + + return true; +} + +#endif // Commenting out this which will be made redundant and/or obsolete by Havok integration + +hsBool hsBounds3Ext::ISectBSBS(const hsBounds3Ext& other, const hsVector3& myVel, hsHitInfoExt *hit) const +{ + if(!(fExtFlags & kSphereSet) ) + IMakeSphere(); + if(!(other.fExtFlags & kSphereSet) ) + other.IMakeSphere(); + + hsScalar tClose, tImpact; + if( !IClosestISect(other, myVel, &tClose, &tImpact) ) + return false; + if( (tImpact < 0) || (tImpact > 1.f) ) + return false; + if( tClose < 0 ) + tClose = 0; + if( tClose > 1.f ) + tClose = 1.f; + + hsPoint3 closePt = GetCenter() + myVel * tClose; + hsVector3 del; + del.Set(&closePt, &other.GetCenter()); + + hsScalar mag = del.Magnitude(); + hsScalar depth = GetRadius() + other.GetRadius() - mag; + if( depth <= 0 ) + return false; + + hsPoint3 hitPt = GetCenter() + myVel * tImpact; + hsVector3 norm; + norm.Set(&hitPt, &other.GetCenter()); + norm.Normalize(); + + hit->Set(this, &other, norm, depth); + return true; +} + +hsBool hsBounds3Ext::ISectBSBox(const hsBounds3Ext &other, const hsVector3 &myVel, hsHitInfoExt *hit) const +{ + hit->fDelPos = -myVel; + if( other.ISectBoxBS(*this, hit->fDelPos, hit) ) + { + hit->fNormal = -hit->fNormal; + hit->fBoxBnd = this; + hit->fOtherBoxBnd = &other; + hit->fDelPos = myVel; + + return true; + } + + hit->fDelPos = myVel; + return false; +} + +hsBool hsBounds3Ext::ISectBoxBS(const hsBounds3Ext &other, const hsVector3 &myVel, hsHitInfoExt *hit) const +{ + if(!(fExtFlags & kSphereSet) ) + IMakeSphere(); + hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (BoxBS(vel))"); + + hsVector3 minAxis; + hsScalar minDepth; + hsBool haveAxis = false; + hsVector3 tstAxis; + hsScalar tstDepth; + int i; + for( i = 0; i < 3; i++ ) + { + hsBool tryAxis; + if( other.fExtFlags & kAxisAligned ) + { + // first try the other box axes + hsScalar effMin = fCenter[i]; + hsScalar effMax = effMin; + hsScalar velDist = myVel[i]; + if( velDist > 0 ) + effMax += velDist; + else + effMin += velDist; + effMax += fRadius; + effMin -= fRadius; + + if( effMax < other.fMins[i] ) + return false; + if( effMin > other.fMaxs[i] ) + return false; + + if( (other.fMins[i] <= effMin) + &&(other.fMaxs[i] <= effMax) ) + { + tstDepth = other.fMaxs[i] - effMin; + hsAssert(tstDepth > -kRealSmall, "Late to be finding sep axis"); + tstAxis.Set(i == 0 ? hsScalar1 : 0, i & 1 ? hsScalar1 : 0, i & 2 ? hsScalar1 : 0); + tryAxis = true; + } + else + if( (other.fMins[i] >= effMin) + &&(other.fMaxs[i] >= effMax) ) + { + tstDepth = effMax - other.fMins[i]; + hsAssert(tstDepth > -kRealSmall, "Late to be finding sep axis"); + tstAxis.Set(i == 0 ? -hsScalar1 : 0, i & 1 ? -hsScalar1 : 0, i & 2 ? -hsScalar1 : 0); + tryAxis = true; + } + else + tryAxis = false; + } + else + { + // first try the other box axes + hsScalar radScaled = fRadius * other.fAxes[i].Magnitude(); + hsScalar centerDist = other.fAxes[i].InnerProduct(fCenter); + hsScalar effMin = centerDist; + hsScalar effMax = centerDist; + hsScalar velDist = other.fAxes[i].InnerProduct(myVel); + if( velDist > 0 ) + effMax += velDist; + else + effMin += velDist; + effMax += radScaled; + effMin -= radScaled; + + if( !(other.fExtFlags & kDistsSet) ) + other.IMakeDists(); + + if( effMax < other.fDists[i].fX ) + return false; + if( effMin > other.fDists[i].fY ) + return false; + + if( centerDist <= other.fDists[i].fX ) + { + tstDepth = effMax - other.fDists[i].fX; + tstAxis = -other.fAxes[i]; + hsAssert(tstDepth > -kRealSmall, "Late to be finding sep axis"); + } + else + if( centerDist >= other.fDists[i].fY ) + { + tstDepth = other.fDists[i].fY - effMin; + tstAxis = other.fAxes[i]; + hsAssert(tstDepth > -kRealSmall, "Late to be finding sep axis"); + } + else + tryAxis = false; + + } + if( tryAxis ) + { + hsScalar magSq = tstAxis.MagnitudeSquared(); + if( magSq > kRealSmall ) + { + tstDepth *= tstDepth * hsScalarInvert(magSq); + if( !haveAxis||(tstDepth < minDepth) ) + { + minDepth = tstDepth; + minAxis = tstAxis; + haveAxis = true; + } + hsAssert(!haveAxis || (minAxis.MagnitudeSquared() > kRealSmall), "Bogus"); + } + } + } + // now try the axis between the center of sphere and center of other box + hsVector3 diag(&fCenter, &other.GetCenter()); + if( !haveAxis && (diag.MagnitudeSquared() < kRealSmall) ) + diag.Set(1.f, 0, 0); + hsScalar effMin = diag.InnerProduct(fCenter); + hsScalar effMax = effMin; + hsScalar velDist = diag.InnerProduct(myVel); + if( velDist > 0 ) + effMax += velDist; + else + effMin += velDist; + hsScalar radDist = fRadius * diag.Magnitude(); + effMax += radDist; + effMin -= radDist; + hsPoint2 otherDepth; + other.TestPlane(diag, otherDepth); + if( effMax < otherDepth.fX ) + return false; + if( effMin > otherDepth.fY ) + return false; + + tstAxis = diag; + tstDepth = otherDepth.fY - effMin; + hsScalar magSq = tstAxis.MagnitudeSquared(); + if( magSq > 0 ) + { + tstDepth *= tstDepth * hsScalarInvert(magSq); + if( !haveAxis ||(tstDepth < minDepth) ) + { + minDepth = tstDepth; + minAxis = tstAxis; + } + } + + hsScalar invMag = hsScalarInvert(minAxis.Magnitude()); + minAxis *= invMag; + hsAssert(minDepth >= 0, "Late to find sep plane"); + minDepth = hsSquareRoot(minDepth); + hit->Set(this, &other, minAxis, minDepth); + + return true; +} + +hsBool hsBounds3Ext::ISectBoxBS(const hsBounds3Ext &other, const hsVector3 &myVel) const +{ + if( !(fExtFlags & kSphereSet) ) + IMakeSphere(); + hsAssert(fBounds3Flags & kCenterValid, "Sphere set but not center (BoxBS)"); + + if( other.fExtFlags & kAxisAligned ) + { + // first try the other box axes + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar effMin = fCenter[i]; + hsScalar effMax = effMin; + hsScalar velDist = myVel[i]; + if( velDist > 0 ) + effMax += velDist; + else + effMin += velDist; + effMax += fRadius; + effMin -= fRadius; + + if( effMax < other.fMins[i] ) + return false; + if( effMin > other.fMaxs[i] ) + return false; + } + } + else + { + // first try the other box axes + if( !(other.fExtFlags & kDistsSet) ) + other.IMakeDists(); + + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar effMin = other.fAxes[i].InnerProduct(fCenter); + hsScalar effMax = effMin; + hsScalar velDist = other.fAxes[i].InnerProduct(myVel); + if( velDist > 0 ) + effMax += velDist; + else + effMin += velDist; + hsScalar radScaled = fRadius * other.fAxes[i].Magnitude(); + effMax += radScaled; + effMin -= radScaled; + + if( effMax < other.fDists[i].fX ) + return false; + if( effMin > other.fDists[i].fY ) + return false; + } + } + + // now try the axis between the center of sphere and center of other box + hsVector3 diag(&fCenter, &other.GetCenter()); + hsScalar effMin = diag.InnerProduct(fCenter); + hsScalar effMax = effMin; + hsScalar velDist = diag.InnerProduct(myVel); + if( velDist > 0 ) + effMax += velDist; + else + effMin += velDist; + hsScalar radDist = fRadius * diag.Magnitude(); + effMax += radDist; + effMin -= radDist; + hsPoint2 otherDepth; + other.TestPlane(diag, otherDepth); + if( effMax < otherDepth.fX ) + return false; + if( effMin > otherDepth.fY ) + return false; + + return true; +} + +hsBool hsBounds3Ext::ISectLine(const hsPoint3* from, const hsPoint3* at) const +{ + if( !(fExtFlags & kSphereSet) ) + IMakeSphere(); + + hsPoint3 onLine; + hsScalar z = ClosestPointToLine(&fCenter, from, at, &onLine); + + hsScalar distSq = hsVector3(&onLine, &fCenter).MagnitudeSquared(); + if( distSq >= fRadius*fRadius ) + return false; + + if( fExtFlags & kAxisAligned ) + { + int i; + for( i = 0; i < 3; i++ ) + { + if( ((*from)[i] < fMins[i])&&((*at)[i] < fMins[i]) ) + return false; + if( ((*from)[i] > fMaxs[i])&&((*at)[i] > fMaxs[i]) ) + return false; + } + } + else + { + if( !(fExtFlags & kDistsSet) ) + IMakeDists(); + + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar d0 = fAxes[i].InnerProduct(from); + hsScalar d1 = fAxes[i].InnerProduct(at); + if( d0 < d1 ) + { + if( d1 < fDists[i].fX ) + return false; + if( d0 > fDists[i].fY ) + return false; + } + else + { + if( d0 < fDists[i].fX ) + return false; + if( d1 > fDists[i].fY ) + return false; + } + } + } + return true; +} + +hsBool hsBounds3Ext::ISectCone(const hsPoint3* from, const hsPoint3* at, hsScalar radius) const +{ + if( !(fExtFlags & kSphereSet) ) + IMakeSphere(); + + // expensive + hsPoint3 onLine; + ClosestPointToLine(&fCenter, from, at, &onLine); + + hsScalar distSq = hsVector3(&onLine, &fCenter).MagnitudeSquared(); + hsScalar radiusSq = fRadius * fRadius; + if (distSq - radius*radius >= radiusSq) + return false; + + hsScalar dist = hsVector3(from, &onLine).Magnitude(); + hsScalar len = hsVector3(from, at).Magnitude(); + hsScalar partRadius = radius/len * dist; + if (distSq - fRadius*fRadius - partRadius*partRadius >= 0) + { + hsVector3 rayToCenter(&fCenter,&onLine); + rayToCenter.Normalize(); + + hsPoint3 atEdge = *at + rayToCenter*radius; + + ClosestPointToLine(&fCenter, from, &atEdge, &onLine); + + distSq = hsVector3(&onLine, &fCenter).MagnitudeSquared(); + if( distSq >= radiusSq ) + return false; + } + + // incorrect + if( fExtFlags & kAxisAligned ) + { + int i; + for( i = 0; i < 3; i++ ) + { + if( ((*from)[i] < fMins[i])&&((*at)[i]+radius < fMins[i]) ) + return false; + if( ((*from)[i] > fMaxs[i])&&((*at)[i]-radius > fMaxs[i]) ) + return false; + } + } + else + { + if( !(fExtFlags & kDistsSet) ) + IMakeDists(); + + int i; + for( i = 0; i < 3; i++ ) + { + ClosestPointToInfiniteLine(at, &fAxes[i], &onLine); + hsVector3 atLine(&onLine,at); + atLine.Normalize(); + hsPoint3 atEdge = *at + atLine * radius; + + hsScalar d0 = fAxes[i].InnerProduct(*from); + hsScalar d1 = fAxes[i].InnerProduct(atEdge); + if( d0 < d1 ) + { + if( d1 < fDists[i].fX ) + return false; + if( d0 > fDists[i].fY ) + return false; + } + else + { + if( d0 < fDists[i].fX ) + return false; + if( d1 > fDists[i].fY ) + return false; + } + } + } + return true; +} + + +hsBool hsBounds3Ext::ISectRayBS(const hsPoint3& from, const hsPoint3& to, hsPoint3& at) const +{ + hsVector3 c2f(&from,&GetCenter()); + hsVector3 f2t(&to,&from); + hsScalar a = f2t.MagnitudeSquared(); + hsScalar b = 2 * (c2f.InnerProduct(f2t)); + hsScalar c = c2f.MagnitudeSquared() - GetRadius()*GetRadius(); + + hsScalar disc = b*b - 4*a*c; + if (disc < 0) + return false; + else + { + hsScalar discSqrt = hsSquareRoot(disc); + hsScalar denom = 1.f/(2*a); + hsScalar t = (-b - discSqrt) * denom; + + if (t<1 && t>0) + at = from + (f2t * t); + else + return false; +#if 0 + { + t = (-b + discSqrt) * denom; + if (t > 1) + return false; + at = from + (f2t * t); + } +#endif + return true; + } +} + +void hsBounds3Ext::Read(hsStream *s) +{ + fExtFlags = s->ReadSwap32(); + hsBounds3::Read(s); + if( !(fExtFlags & kAxisAligned) ) + { + fCorner.Read(s); + int i; + for( i = 0; i < 3; i++ ) + { + fAxes[i].Read(s); + fDists[i].fX = s->ReadSwapScalar(); + fDists[i].fY = s->ReadSwapScalar(); + } + IMakeMinsMaxs(); + IMakeDists(); + } + IMakeSphere(); +} +void hsBounds3Ext::Write(hsStream *s) +{ + s->WriteSwap32(fExtFlags); + hsBounds3::Write(s); + if( !(fExtFlags & kAxisAligned) ) + { + fCorner.Write(s); + int i; + for( i = 0; i < 3; i++ ) + { + fAxes[i].Write(s); + if( fExtFlags & kDistsSet ) + { + s->WriteSwapScalar(fDists[i].fX); + s->WriteSwapScalar(fDists[i].fY); + } + else + { + // Playing nice with binary patches--writing uninited values BAD! + s->WriteSwapScalar( 0.f ); + s->WriteSwapScalar( 0.f ); + } + } + } +} + +#if 0 // Commenting out this which will be made redundant and/or obsolete by Havok integration +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void hsBounds3Tri::TestPlane(const hsVector3 &n, hsPoint2 &depth) const +{ + depth.fX = depth.fY = n.InnerProduct(fVerts[0]); + + hsScalar d1, d2; + + d1 = n.InnerProduct(fVerts[1]); + d2 = n.InnerProduct(fVerts[2]); + + if( d1 > d2 ) + { + if( d1 > depth.fY ) + depth.fY = d1; + if( d2 < depth.fX ) + depth.fX = d2; + } + else + { + if( d2 > depth.fY ) + depth.fY = d2; + if( d1 < depth.fX ) + depth.fX = d1; + } +} +hsBool hsBounds3Tri::ClosestTriPoint(const hsPoint3 *p, hsPoint3 *out, const hsVector3 *ax) const +{ + // project point onto tri plane + hsPoint3 pPln; + if( ax ) + { + hsScalar t; + + t = fNormal.InnerProduct(fVerts[0] - *p); + hsScalar s = fNormal.InnerProduct(ax); + if( (s > hsBounds::kRealSmall)||(s < -hsBounds::kRealSmall) ) + { + t /= s; + + pPln = *p; + pPln += *ax * t; + } + else + { + return ClosestTriPoint(p, out); + } + } + else + { + hsScalar t; + + t = fNormal.InnerProduct(fVerts[0] - *p); + t /= fNormal.MagnitudeSquared(); + + pPln = *p; + pPln += fNormal * t; + } + + if( !(fTriFlags & kAxesSet) ) + SetAxes(); + + int nIn = 0; + int firstIn, secondIn; + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar tst = fPerpAxes[i].InnerProduct(pPln); + hsBool in = false; + if( fOnIsMax & (1 << i) ) + { + if( tst <= fPerpDists[i].fY ) + in = true; + } + else + { + if( tst >= fPerpDists[i].fX ) + in = true; + } + if( in ) + { + if( nIn++ ) + secondIn = i; + else + firstIn = i; + } + } + switch( nIn ) + { + case 3: + *out = pPln; + break; + case 1: + { + int k, kPlus; + k = firstIn == 2 ? 0 : firstIn+1; + kPlus = k == 2 ? 0 : k+1; + + hsPoint3 pTmp; + hsScalar z; + z = hsBounds3::ClosestPointToLine(&pPln, fVerts+k, fVerts+kPlus, &pTmp); + if( z <= hsScalar1 ) + *out = pTmp; + else + { + k = kPlus; + kPlus = k == 2 ? 0 : k+1; + z = hsBounds3::ClosestPointToLine(&pPln, fVerts+k, fVerts+kPlus, out); + } + } + break; + case 2: + { + int k, kPlus; + k = secondIn == 2 ? 0 : secondIn+1; + if( k == firstIn ) + k++; + kPlus = k == 2 ? 0 : k+1; + + hsBounds3::ClosestPointToLine(&pPln, fVerts+k, fVerts+kPlus, out); + break; + } + + case 0: + hsAssert(false, "Extreme bogosity, inverted tri?!?"); + *out = pPln; + return false; + } + +#ifdef HS_DEBUGGING // mf horse testing +#if 0 + if( 0 ) + { + hsVector3 ndeb = hsVector3(fVerts+1, fVerts) % hsVector3(fVerts+2, fVerts); + hsScalar dis; + dis = fNormal.InnerProduct(pPln) - fDist; + if( (fDist > hsBounds::kRealSmall)||(fDist < -hsBounds::kRealSmall) ) + dis /= fDist; + hsAssert((dis < hsBounds::kRealSmall)&&(dis > -hsBounds::kRealSmall), "Non-planar pPln"); + dis = hsVector3(&pPln, out).MagnitudeSquared(); + hsScalar vDis; + vDis = hsVector3(&pPln, fVerts+0).MagnitudeSquared(); + hsAssert( vDis - dis > -hsBounds::kRealSmall, "Bad closest point"); + vDis = hsVector3(&pPln, fVerts+1).MagnitudeSquared(); + hsAssert( vDis - dis > -hsBounds::kRealSmall, "Bad closest point"); + vDis = hsVector3(&pPln, fVerts+2).MagnitudeSquared(); + hsAssert( vDis - dis > -hsBounds::kRealSmall, "Bad closest point"); + hsBool dork = false; + if( dork ) + { + hsScalar zn[3]; + hsScalar zf[3]; + hsScalar z[3]; + int i; + for( i = 0; i < 3; i++ ) + { + z[i] = fPerpAxes[i].InnerProduct(fVerts[i]); + int j; + j = i == 0 ? 2 : i-1; + zf[i] = fPerpAxes[i].InnerProduct(fVerts[j]); + j = i == 2 ? 0 : i+1; + zn[i] = fPerpAxes[i].InnerProduct(fVerts[j]); + } + return ClosestTriPoint(p, out, ax); + } + } +#endif +#endif + + return 3 == nIn; +} + +void hsBounds3Tri::SetAxes() const +{ + fOnIsMax = 0; + + hsVector3 edge[3]; + edge[0].Set(fVerts, fVerts+1); + edge[1].Set(fVerts+1, fVerts+2); + edge[2].Set(fVerts+2, fVerts); + hsVector3 perp = edge[2] % edge[0]; + + int i; + for( i = 0; i < 3; i++ ) + { + int j = i == 2 ? 0 : i+1; + int k = j == 2 ? 0 : j+1; + fPerpAxes[i] = edge[i] % perp; + fPerpAxes[i].Normalize(); + + fPerpDists[i].fX = fPerpAxes[i].InnerProduct(fVerts[i]); + fPerpDists[i].fY = fPerpAxes[i].InnerProduct(fVerts[k]); + if( fPerpDists[i].fX > fPerpDists[i].fY ) + { + fOnIsMax |= 1 << i; + hsScalar d = fPerpDists[i].fX; + fPerpDists[i].fX = fPerpDists[i].fY; + fPerpDists[i].fY = d; + } + } + fTriFlags |= kAxesSet; +} + +hsBounds3Tri* hsBounds3Tri::Transform(const hsMatrix44& x) +{ +#if 0 // IDENT + if( x.fFlags & hsMatrix44::kIsIdent ) + return this; +#endif // IDENT + + fVerts[0] = x * fVerts[0]; + fVerts[1] = x * fVerts[1]; + fVerts[2] = x * fVerts[2]; + + hsVector3 v1, v2; + v1.Set(&fVerts[1], &fVerts[0]); + v2.Set(&fVerts[2], &fVerts[0]); + fNormal = v1 % v2; + // mf horse - do we need to normalize here? + // fNormal.Normalize(); + + fDist = fNormal.InnerProduct(fVerts[0]); + + fTriFlags &= ~kAxesSet; + + SetAxes(); + + return this; +} + +hsBounds3Tri* hsBounds3Tri::Translate(const hsVector3& v) +{ + fVerts[0] += v; + fVerts[1] += v; + fVerts[2] += v; + + fDist = fNormal.InnerProduct(fVerts[0]); + + int i; + for( i = 0; i < 3; i++ ) + { + int j = i == 2 ? 0 : i+1; + int k = j == 2 ? 0 : j+1; + + hsScalar del = fPerpAxes[i].InnerProduct(v); + fPerpDists[i].fX += del; + fPerpDists[i].fY += del; + } + + return this; +} + +void hsBounds3Tri::Set(const hsPoint3& v0, + const hsPoint3& v1, + const hsPoint3& v2, + hsTriangle3* t, + const hsMatrix44& x) +{ + fVerts[0] = v0; + fVerts[1] = v1; + fVerts[2] = v2; + + fOnIsMax = 0; + fTriangle = t; + + if( t->fFlags & hsTriangle3::kTwoSided ) + fTriFlags |= kDoubleSide; + +#if 0 // IDENT + if( x.fFlags & hsMatrix44::kIsIdent ) + { + hsVector3 v1, v2; + v1.Set(&fVerts[1], &fVerts[0]); + v2.Set(&fVerts[2], &fVerts[0]); + fNormal = v1 % v2; + // mf horse - do we need to normalize here? + // fNormal.Normalize(); + + fDist = fNormal.InnerProduct(fVerts[0]); + + fTriFlags &= ~kAxesSet; + + SetAxes(); + } + else +#endif // IDENT + Transform(x); +} + +hsBounds3Tri::hsBounds3Tri(const hsPoint3& v0, + const hsPoint3& v1, + const hsPoint3& v2, + hsTriangle3* t, + const hsMatrix44& x) +{ + Set(v0, v1, v2, t, x); +} + +hsBounds3Tri::hsBounds3Tri(hsTriangle3* t, const hsMatrix44& x) +{ + Set(t->fVert[0]->fVtx->fLocalPos, + t->fVert[2]->fVtx->fLocalPos, + t->fVert[2]->fVtx->fLocalPos, + t, x); +} + +void hsBounds3Tri::Set(hsPoint3 *v0, hsPoint3 *v1, hsPoint3 *v2, hsVector3 *n, UInt32 triFlags, hsTriangle3 *t) +{ + fTriFlags = 0; + + if( triFlags & hsTriangle3::kTwoSided ) + fTriFlags |= kDoubleSide; + + fNormal = *n; + fVerts[0] = *v0; + fVerts[1] = *v1; + fVerts[2] = *v2; + + fOnIsMax = 0; + fTriangle = t; + + fDist = fNormal.InnerProduct(fVerts[0]); +} + +hsBounds3Tri::hsBounds3Tri(hsPoint3 *v0, hsPoint3 *v1, hsPoint3 *v2, hsVector3 *n, UInt32 triFlags, hsTriangle3 *t) +{ + Set(v0, v1, v2, n, triFlags, t); +} + +hsBounds3Tri::hsBounds3Tri(hsTriangle3* t) +{ + Set(&t->fVert[0]->fVtx->fLocalPos, + &t->fVert[1]->fVtx->fLocalPos, + &t->fVert[2]->fVtx->fLocalPos, + &t->fNormal, t->fFlags, t); +} + +hsBounds3Tri::~hsBounds3Tri() +{ +} + + +// Finds closest intersection vertex or triangle/center-line intersection +hsBool hsBounds3Tri::ISectCone(const hsPoint3& from, const hsPoint3& to, hsScalar cosThetaSq, hsBool ignoreFacing, hsPoint3& at, hsBool& backSide) const +{ + hsScalar d0 = from.InnerProduct(fNormal); + hsScalar d1 = at.InnerProduct(fNormal); + hsScalar dt = fNormal.InnerProduct(fVerts[0]); + backSide = d0 < dt; + if( !ignoreFacing && backSide ) + return false; + if ( (d0 < dt || d1 < dt) && + (d0 > dt || d1 > dt) && + ClosestTriPoint(&from, &at, &hsVector3(&to,&from)) ) + return true; + + hsVector3 av(&to,&from); + hsScalar distASq = av.MagnitudeSquared(); + hsScalar radiusSq = distASq * (1-cosThetaSq)/cosThetaSq; + + hsScalar minDistSq = 0; + Int32 minVert = 0; + hsBool sect = false; + for (Int32 i=0; i<3; i++) + { + hsPoint3 onLine; + hsScalar t = hsBounds3::ClosestPointToLine(&fVerts[i], &from, &to, &onLine); + + // outside the cap of the cylinder + if (t<0 || t>1) + continue; + + // outside the edge of the cylinder + if (hsVector3(&onLine, &fVerts[i]).MagnitudeSquared() >= radiusSq) + continue; + + hsVector3 bv(&fVerts[i],&from); + + hsScalar distBSq = bv.MagnitudeSquared(); + + hsScalar cosMuSquared = (av * bv) / (distASq * distBSq); + + // outside the angle of the cone + if (cosMuSquared > cosThetaSq) + continue; + + if (!sect || distBSq < minDistSq) + { + minVert = i; + minDistSq = distBSq; + sect = true; + } + } + at = fVerts[minVert]; + return sect; +} +#endif // Commenting out this which will be made redundant and/or obsolete by Havok integration diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBounds.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBounds.h new file mode 100644 index 00000000..0e6a573c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsBounds.h @@ -0,0 +1,423 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsBounds_inc +#define hsBounds_inc + +#include "hsGeometry3.h" +#include "hsPoint2.h" +#include "hsMatrix44.h" + +class hsTriangle3; +class hsGView3; +class hsG3DDevice; + +/////////////////////////////////////////////////////////////////////////////// +// BOUNDS +/////////////////////////////////////////////////////////////////////////////// + +enum hsBoundsType +{ + kBoundsNormal, + kBoundsFull, + kBoundsEmpty, + kBoundsUninitialized +}; + +// +// Abstract base class +// +class hsBounds3; +class hsBounds +{ +protected: + hsBoundsType fType; +public: + static const hsScalar kRealSmall; + + hsBounds() : fType(kBoundsUninitialized) { }; + + hsBounds& MakeEmpty() { fType = kBoundsEmpty; return *this; } + hsBounds& MakeFull() { fType = kBoundsFull; return *this; } + hsBoundsType GetType() const { return fType; } + + // + // These set type to kBounds Normal + // + virtual void Reset(const hsBounds3*) = 0; + + virtual hsBool IsInside(const hsPoint3* pos) const =0; // Only valid for kBounds Normal + + virtual void Read(hsStream*); + virtual void Write(hsStream*); +}; + +// +// +// +class hsGTriMesh; +struct hsMatrix44; +class hsBounds3 : public hsBounds +{ +public: + enum { + kCenterValid = 0x1, + kIsSphere = 0x2 + }; +protected: + mutable UInt32 fBounds3Flags; + hsPoint3 fMins; + hsPoint3 fMaxs; + mutable hsPoint3 fCenter; + + void ICalcCenter() const; +public: + hsBounds3() : fBounds3Flags(0) {} + hsBounds3(const hsBounds3 &pRHS) : fBounds3Flags(0) { Reset(&pRHS); } + hsBounds3 &operator=(const hsBounds3 &pRHS ) + { if (&pRHS != this) Reset(&pRHS); return *this; } + + // + // These set type to kBounds Normal + // + virtual void Reset(const hsBounds3*); + virtual void Reset(const hsPoint3 *p); + virtual void Reset(int n, const hsPoint3 *p); + virtual void Union(const hsPoint3 *p); + virtual void Union(const hsBounds3 *b); + virtual void Union(const hsVector3 *v); // smears the bounds in given direction + virtual void MakeSymmetric(const hsPoint3* p); // Expands bounds to be symmetric about p + virtual void InscribeSphere(); + + virtual void Transform(const hsMatrix44*); + + // + // Only valid for kBounds Normal + // + void Draw(hsGView3* v, hsG3DDevice* d, hsScalar r, hsScalar g, hsScalar b, hsScalar a, hsBool spheric=false); + virtual void GetCorners(hsPoint3 *b) const; + const hsPoint3& GetMins() const; + const hsPoint3& GetMaxs() const; + hsScalar GetMaxDim() const; // Computes the answer + const hsPoint3& GetCenter() const; // Computes the answer if not already there +// void MakeTriMesh(hsGTriMesh* tMesh, UInt32 triFlags, hsPoint3* cornersIn=nil) const; +// void MakeTriMeshSphere(hsGTriMesh* tMesh, hsPoint3* cornersIn=nil) const; + virtual hsBool IsInside(const hsPoint3* pos) const; // ok for full/empty + virtual void TestPlane(const hsVector3 &n, hsPoint2 &depth) const; + virtual void TestPlane(const hsPlane3 *p, hsPoint2 &depth) const; + virtual hsBool ClosestPoint(const hsPoint3& p, hsPoint3& inner, hsPoint3& outer) const; + + // Test according to my axes only, doesn't check other's axes + // neg, pos, zero == disjoint, I contain other, overlap + virtual Int32 TestBound(const hsBounds3& other) const; + + static hsScalar ClosestPointToLine(const hsPoint3 *p, const hsPoint3 *v0, const hsPoint3 *v1, hsPoint3 *out); + static hsScalar ClosestPointToInfiniteLine(const hsPoint3* p, const hsVector3* v, hsPoint3* out); + + virtual void Read(hsStream*); + virtual void Write(hsStream*); +}; + +inline void hsBounds3::ICalcCenter() const +{ + hsAssert(kBoundsNormal == fType, "Invalid type for ICalcCenter"); + fCenter = ((fMins + fMaxs) / 2.0); + fBounds3Flags |= kCenterValid; +} +inline void hsBounds3::GetCorners(hsPoint3 *b) const +{ + hsAssert(kBoundsNormal == fType, "Invalid type for GetCorners"); + for(int i = 0; i < 8; i++) + { + b[i][0] = (i & 0x1) ? fMins[0] : fMaxs[0]; + b[i][1] = (i & 0x2) ? fMins[1] : fMaxs[1]; + b[i][2] = (i & 0x4) ? fMins[2] : fMaxs[2]; + } +} +inline const hsPoint3& hsBounds3::GetMins() const +{ + hsAssert(kBoundsNormal == fType, "Invalid type for GetMins"); + return fMins; +} +inline const hsPoint3& hsBounds3::GetMaxs() const +{ + hsAssert(kBoundsNormal == fType, "Invalid type for GetMaxs"); + return fMaxs; +} + +inline const hsPoint3& hsBounds3::GetCenter() const +{ + hsAssert(kBoundsNormal == fType, "Invalid type for GetCenter"); + if(!(fBounds3Flags & kCenterValid)) + ICalcCenter(); + return fCenter; +} + +inline hsScalar hsBounds3::GetMaxDim() const +{ + hsAssert(kBoundsNormal == fType, "Invalid type for GetMaxDim"); + return hsMaximum(hsMaximum(fMaxs.fX-fMins.fX, fMaxs.fY-fMins.fY), fMaxs.fZ-fMins.fZ); +} + +// +// A convex region specified by a series of planes. +// +class hsBoundsOriented : public hsBounds +{ +private: + hsBool fCenterValid; + hsPoint3 fCenter; + hsPlane3 *fPlanes; + UInt32 fNumPlanes; +public: + hsBoundsOriented() : fPlanes(nil),fNumPlanes(0),fCenterValid(false) {} + virtual ~hsBoundsOriented() { if (fPlanes) delete [] fPlanes; } + + // Center is not computed by the class, it must be set by the creator of the class. + void SetCenter(const hsPoint3* c) { fCenter=*c; fCenterValid = true; } + void SetCenter(const hsBounds3* b) { hsBounds3 bb=*b; fCenter=bb.GetCenter(); fCenterValid = true; } + void SetCenter(const hsBoundsOriented* b) { fCenter=b->GetCenter(); fCenterValid = true; } + hsPoint3 GetCenter() const; + + void SetNumberPlanes(UInt32 n); + + hsPlane3* GetPlane(int i) { return &fPlanes[i]; } + int GetNumPlanes() { return fNumPlanes; } + + // + // These set type to kBounds Normal + // + virtual void Reset(const hsBounds3*); + void Reset(hsGTriMesh *tMesh); + void SetPlane(UInt32 i, hsPlane3 *p); + + // + // Only valid for kBounds Normal + // + virtual hsBool IsInside(const hsPoint3* pos) const; + virtual void TestPlane(const hsVector3 &n, hsPoint2 &depth) const; // Complain and refuse + + virtual void Write(hsStream *stream); + virtual void Read(hsStream *stream); +}; + +//class hsBounds3Tri; +class hsHitInfoExt; +class hsBounds3Ext : public hsBounds3 { +protected: + enum { + kAxisAligned =0x1, + kSphereSet =0x2, + kDistsSet =0x4, + kAxisZeroZero =(1<<20), + kAxisOneZero =(1<<21), + kAxisTwoZero =(1<<22) + }; + mutable UInt32 fExtFlags; + hsPoint3 fCorner; + hsVector3 fAxes[3]; + mutable hsPoint2 fDists[3]; + mutable hsScalar fRadius; + + hsBool IAxisIsZero(UInt32 i) const { return (fExtFlags & (1 << (20+i))) != 0; }; + void IMakeSphere() const; + void IMakeDists() const; + void IMakeMinsMaxs(); +public: + hsBounds3Ext() : fExtFlags(kAxisAligned) {}; + + hsBounds3Ext(const hsBounds3 &b); + hsBounds3Ext &operator=(const hsBounds3 &b); + hsBounds3Ext(const hsBounds3Ext &pRHS) { Reset(&pRHS); } + hsBounds3Ext &operator=(const hsBounds3Ext &pRHS ) + { if (&pRHS != this) Reset(&pRHS); return *this; } + + virtual void Reset(const hsBounds3Ext *b); + virtual void Reset(const hsBounds3 *b); + virtual void Reset(const hsPoint3 *p); + virtual void Reset(int n, const hsPoint3 *p); + + virtual void Union(const hsPoint3 *p); + virtual void Union(const hsBounds3 *b); + + virtual void Union(const hsVector3 *v); // smears the bounds in given direction + virtual void MakeSymmetric(const hsPoint3* p); // Expands bounds to be symmetric about p + virtual void InscribeSphere(); + virtual void Unalign(); + + virtual void Transform(const hsMatrix44 *m); + virtual void Translate(const hsVector3 &v); + + virtual hsScalar GetRadius() const; + virtual void GetAxes(hsVector3 *fAxis0, hsVector3 *fAxis1, hsVector3 *fAxis2) const; + virtual hsPoint3 *GetCorner(hsPoint3 *c) const { *c = (fExtFlags & kAxisAligned ? fMins : fCorner); return c; } + virtual void GetCorners(hsPoint3 *b) const; + virtual hsBool ClosestPoint(const hsPoint3& p, hsPoint3& inner, hsPoint3& outer) const; + + virtual hsBool IsInside(const hsPoint3* pos) const; // ok for full/empty + + virtual void TestPlane(const hsVector3 &n, hsPoint2 &depth) const; + virtual Int32 TestPoints(int n, const hsPoint3 *pList) const; // pos,neg,zero == allout, allin, cut + + // Test according to my axes only, doesn't check other's axes + // neg, pos, zero == disjoint, I contain other, overlap + virtual Int32 TestBound(const hsBounds3Ext& other) const; + + virtual void TestPlane(const hsVector3 &n, const hsVector3 &myVel, hsPoint2 &depth) const; + virtual void TestPlane(const hsPlane3 *p, const hsVector3 &myVel, hsPoint2 &depth) const; + virtual Int32 TestPoints(int n, const hsPoint3 *pList, const hsVector3 &ptVel) const; // pos,neg,zero == allout, allin, cut + virtual hsBool ISectBB(const hsBounds3Ext &other, const hsVector3 &myVel) const; + virtual hsBool ISectBB(const hsBounds3Ext &other, const hsVector3 &myVel, hsHitInfoExt *hit) const; + virtual hsBool ISectABB(const hsBounds3Ext &other, const hsVector3 &myVel) const; + virtual hsBool ISectBS(const hsBounds3Ext &other, const hsVector3 &myVel) const; +#if 0 // Commenting out this which will be made redundant and/or obsolete by Havok integration + virtual hsBool ISectTriABB(hsBounds3Tri &tri, const hsVector3 &myVel) const; + virtual hsBool ISectTriBB(hsBounds3Tri &tri, const hsVector3 &myVel) const; + virtual hsBool ISectTriBB(hsBounds3Tri &tri, const hsVector3 &myVel, hsHitInfoExt *hit) const; + + virtual hsBool TriBSHitInfo(hsBounds3Tri& tri, const hsVector3& myVel, hsHitInfoExt* hit) const; + virtual hsBool TriBBHitInfo(hsBounds3Tri& tri, const hsVector3& myVel, hsHitInfoExt* hit) const; +#endif // Commenting out this which will be made redundant and/or obsolete by Havok integration + + virtual Int32 IClosestISect(const hsBounds3Ext& other, const hsVector3& myVel, + hsScalar* tClose, hsScalar* tImpact) const; +#if 0 // Commenting out this which will be made redundant and/or obsolete by Havok integration + virtual hsBool ISectTriBS(hsBounds3Tri &tri, const hsVector3 &myVel) const; + virtual hsBool ISectTriBS(hsBounds3Tri &tri, const hsVector3 &myVel, hsHitInfoExt *hit) const; +#endif // Commenting out this which will be made redundant and/or obsolete by Havok integration + virtual hsBool ISectBoxBS(const hsBounds3Ext &other, const hsVector3 &myVel, hsHitInfoExt *hit) const; + virtual hsBool ISectBSBox(const hsBounds3Ext &other, const hsVector3 &myVel, hsHitInfoExt *hit) const; + virtual hsBool ISectBoxBS(const hsBounds3Ext &other, const hsVector3 &myVel) const; + virtual hsBool ISectBSBS(const hsBounds3Ext &other, const hsVector3 &myVel, hsHitInfoExt *hit) const; + + virtual hsBool ISectLine(const hsPoint3* from, const hsPoint3* to) const; + virtual hsBool ISectCone(const hsPoint3* from, const hsPoint3* to, hsScalar radius) const; + virtual hsBool ISectRayBS(const hsPoint3& from, const hsPoint3& to, hsPoint3& at) const; + + virtual void Read(hsStream *s); + virtual void Write(hsStream *s); +}; + +inline hsScalar hsBounds3Ext::GetRadius() const +{ + if( !(fExtFlags & kSphereSet) ) + IMakeSphere(); + return fRadius; +} + +#if 0 // Commenting out this which will be made redundant and/or obsolete by Havok integration +class hsBounds3Tri { +protected: + enum { + kOnEdge0 = (1 << 0), + kOnEdge1 = (1 << 1), + kOnEdge2 = (1 << 2), + kOnTriPlane = (1 << 3) + }; + +public: + enum { + kAxesSet = 0x1, + kDoubleSide = 0x2 + }; + hsVector3 fNormal; +// hsVector3 fNormal; + hsScalar fDist; + hsPoint3 fVerts[3]; +// hsVector3 fPerpAxes[3]; + mutable UInt32 fTriFlags; + mutable hsVector3 fPerpAxes[3]; + mutable hsPoint2 fPerpDists[3]; + mutable UInt32 fOnIsMax; + hsTriangle3* fTriangle; + + void TestPlane(const hsVector3 &n, hsPoint2 &depth) const; + hsBool PointOutsideTriPlane(const hsPoint3 *p) const { return fNormal.InnerProduct(p) > fDist; }; + hsBool ClosestTriPoint(const hsPoint3 *p, hsPoint3 *out, const hsVector3 *ax=nil) const; // sets out, true if out = p + t*fNormal + hsBool ISectCone(const hsPoint3& from, const hsPoint3& to, hsScalar cosThetaSq, hsBool32 ignoreBackFacing, hsPoint3& at, hsBool32& backSide) const; + void SetAxes() const; + hsBounds3Tri* Transform(const hsMatrix44& x); + hsBounds3Tri* Translate(const hsVector3& v); + void Set(const hsPoint3& v0, + const hsPoint3& v1, + const hsPoint3& v2, + hsTriangle3* t, + const hsMatrix44& x); + hsBounds3Tri(const hsPoint3& v0, + const hsPoint3& v1, + const hsPoint3& v2, + hsTriangle3* t, + const hsMatrix44& x); + hsBounds3Tri(hsTriangle3* t, + const hsMatrix44& x); + void Set(hsPoint3 *v0, + hsPoint3 *v1, + hsPoint3 *v2, + hsVector3 *n, + UInt32 triFlags, + hsTriangle3 *t=nil); + hsBounds3Tri(hsPoint3 *v0, + hsPoint3 *v1, + hsPoint3 *v2, + hsVector3 *n, + UInt32 triFlags, + hsTriangle3 *t=nil); + hsBounds3Tri(hsTriangle3* t); + hsBounds3Tri() {} + ~hsBounds3Tri(); + + friend class hsBounds3Ext; +} ATTRIBUTE_FOR_PS2; /* SUNSOFT */ +#endif // Commenting out this which will be made redundant and/or obsolete by Havok integration + +class hsHitInfoExt { +public: + hsScalar fDepth; + hsVector3 fNormal; + hsVector3 fDelPos; + +#if 0 // Commenting out this which will be made redundant and/or obsolete by Havok integration + const hsBounds3Tri* fTriBnd; +#endif // Commenting out this which will be made redundant and/or obsolete by Havok integration + const hsBounds3Ext* fBoxBnd; + const hsBounds3Ext* fOtherBoxBnd; + const hsPoint3* fRootCenter; + + hsHitInfoExt(const hsPoint3 *ctr, const hsVector3& offset) { fRootCenter=ctr; fDelPos=offset; }; + +#if 0 // Commenting out this which will be made redundant and/or obsolete by Havok integration + void Set(const hsBounds3Ext *m, const hsBounds3Tri *t, const hsVector3* n, hsScalar d) + { fDepth = d; fTriBnd = t; fBoxBnd = m; fNormal = *n; fOtherBoxBnd = nil; } + void Set(const hsBounds3Ext *m, const hsBounds3Ext *o, const hsVector3 &norm, hsScalar d) + { fDepth = d; fBoxBnd = m, fOtherBoxBnd = o; fNormal = norm; fTriBnd = nil; } +#else // Commenting out this which will be made redundant and/or obsolete by Havok integration + void Set(const hsBounds3Ext *m, const hsVector3* n, hsScalar d) + { fDepth = d; fBoxBnd = m; fNormal = *n; fOtherBoxBnd = nil; } + void Set(const hsBounds3Ext *m, const hsBounds3Ext *o, const hsVector3 &norm, hsScalar d) + { fDepth = d; fBoxBnd = m, fOtherBoxBnd = o; fNormal = norm; } +#endif // Commenting out this which will be made redundant and/or obsolete by Havok integration +}; +#endif // hsBounds_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsColorRGBA.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsColorRGBA.h new file mode 100644 index 00000000..16bed96d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsColorRGBA.h @@ -0,0 +1,171 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsColorRGBA_inc +#define hsColorRGBA_inc + +#include "hsScalar.h" +#include "hsStream.h" + +struct hsColorRGBA { + hsScalar r,g,b,a; + + hsRGBAColor32 ToRGBA32() const; + + hsColorRGBA& Set(hsScalar red, hsScalar grn, hsScalar blu, hsScalar alp) { r = red; g = grn; b = blu; a = alp; return *this; } + + hsBool operator==(const hsColorRGBA&c) const { return (r==c.r)&&(g==c.g)&&(b==c.b)&&(a==c.a); } + hsBool operator!=(const hsColorRGBA&c) const { return !(c == *this); } + + friend inline hsColorRGBA operator+(const hsColorRGBA& s, const hsColorRGBA& t); + hsColorRGBA& operator+=(const hsColorRGBA& s); + + friend inline hsColorRGBA operator*(const hsColorRGBA& s, const hsColorRGBA& t); + hsColorRGBA& operator*=(const hsColorRGBA& s); + + friend inline hsColorRGBA operator-(const hsColorRGBA& s, const hsColorRGBA& t); + hsColorRGBA& operator-=(const hsColorRGBA& s); + + friend inline hsColorRGBA operator*(const hsColorRGBA& c, const hsScalar s); + friend inline hsColorRGBA operator*(const hsScalar s, const hsColorRGBA& c); + hsColorRGBA& operator*=(const hsScalar s); + + hsColorRGBA& FromARGB32(UInt32 c); + UInt32 ToARGB32() const; + + void Read(hsStream *stream); + void Write(hsStream *stream) const; +}; + +inline void hsColorRGBA::Read(hsStream *s) +{ + r = s->ReadSwapScalar(); + g = s->ReadSwapScalar(); + b = s->ReadSwapScalar(); + a = s->ReadSwapScalar(); +} +inline void hsColorRGBA::Write(hsStream *s) const +{ + s->WriteSwapScalar(r); + s->WriteSwapScalar(g); + s->WriteSwapScalar(b); + s->WriteSwapScalar(a); +} + +inline hsColorRGBA& hsColorRGBA::FromARGB32(UInt32 c) +{ + const hsScalar oo255 = 1.f / 255.f; + a = hsScalar((c >> 24) & 0xff) * oo255; + r = hsScalar((c >> 16) & 0xff) * oo255; + g = hsScalar((c >> 8) & 0xff) * oo255; + b = hsScalar((c >> 0) & 0xff) * oo255; + return *this; +} + +inline UInt32 hsColorRGBA::ToARGB32() const +{ + return (UInt32(a * 255.99f) << 24) + | (UInt32(r * 255.99f) << 16) + | (UInt32(g * 255.99f) << 8) + | (UInt32(b * 255.99f) << 0); +} + +inline hsColorRGBA operator+(const hsColorRGBA& s, const hsColorRGBA& t) +{ + hsColorRGBA res; + return res.Set(s.r + t.r, s.g + t.g, s.b + t.b, s.a + t.a); +} +inline hsColorRGBA& hsColorRGBA::operator+=(const hsColorRGBA& s) +{ + r += s.r; + g += s.g; + b += s.b; + a += s.a; + return *this; +} + +inline hsColorRGBA operator*(const hsColorRGBA& s, const hsColorRGBA& t) +{ + hsColorRGBA res; + return res.Set(s.r * t.r, s.g * t.g, s.b * t.b, s.a * t.a); +} +inline hsColorRGBA& hsColorRGBA::operator*=(const hsColorRGBA& s) +{ + r *= s.r; + g *= s.g; + b *= s.b; + a *= s.a; + return *this; +} + +inline hsColorRGBA operator-(const hsColorRGBA& s, const hsColorRGBA& t) +{ + hsColorRGBA res; + return res.Set(s.r - t.r, s.g - t.g, s.b - t.b, s.a - t.a); +} +inline hsColorRGBA& hsColorRGBA::operator-=(const hsColorRGBA& s) +{ + r -= s.r; + g -= s.g; + b -= s.b; + a -= s.a; + return *this; +} + +inline hsColorRGBA operator*(const hsColorRGBA& t, const hsScalar s) +{ + hsColorRGBA res; + return res.Set(s * t.r, s * t.g, s * t.b, s * t.a); +} +inline hsColorRGBA operator*(const hsScalar s, const hsColorRGBA&t) +{ + return t * s; +} +inline hsColorRGBA& hsColorRGBA::operator*=(const hsScalar s) +{ + r *= s; + g *= s; + b *= s; + a *= s; + return *this; +} + +class hsColorOverride +{ +public: + enum { + kNone, + kModColor, + kModAlpha, + kModShade + }; + hsColorRGBA fShade; + hsColorRGBA fColor; + hsBool fFlags; +}; + + +#endif // hsColorRGBA_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsConfig.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsConfig.h new file mode 100644 index 00000000..11bbe655 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsConfig.h @@ -0,0 +1,169 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#ifndef hsConfigDefined +#define hsConfigDefined + + +#ifndef SERVER +# define CLIENT +#endif + + +#ifdef BUILDPS2 +#define HS_BUILD_FOR_PS2 1 +#define PLASMA_NO_NETWORK 1 +#define PLASMA_NO_KEYBOARD 1 +#define PLASMA_NO_GLIDE 1 +#define PLASMA_NO_DDRAW 1 +#define HS_BUILD_PLASMA 1 +#define NEXUS_NO_2D 1 +#endif + +//////////////////// Change the 1s and 0s ////////////////////// + +#define HS_CAN_USE_FLOAT 1 +#define HS_SCALAR_IS_FLOAT 1 + +#define HS_PIN_MATH_OVERFLOW 0 // This forces hsWide versions of FixMath routines +#define HS_DEBUG_MATH_OVERFLOW 0 // This calls hsDebugMessage on k[Pos,Neg]Infinity + + + +///////////////////////Impulse Defines//////////////////////////////////////////////// + +#define HS_IMPULSE_SUPPORT_GRAY4 0 + +///////////////////////Plasma Defines ////////////////////////////////////////////////// + +#ifdef HS_BUILD_PLASMA + + #define HS_IGNORE_T2K 1 + #define HS_SUPPORT_NFNT_FONTS 1 + +#endif // HS_BUILD_PLASMA + + + +//////////////////// Specific Compiler Stuff This Section is computed //////////// + +#if defined(macintosh) && defined(__POWERPC__) + #define HS_BUILD_FOR_MACPPC 1 + #define HS_CPU_BENDIAN 1 +#elif defined(macintosh) + #define HS_BUILD_FOR_MAC68K 1 +#elif defined(_M_IX86) && defined(_WIN32) + #define HS_BUILD_FOR_WIN32 1 + #define HS_CPU_LENDIAN 1 +#elif defined(__unix__) + #define HS_BUILD_FOR_UNIX 1 + #if defined(__intel__) || defined(__i386__) + #define HS_CPU_LENDIAN 1 + #elif defined(__mips__) + #define HS_CPU_BENDIAN 1 + #endif +#elif !HS_BUILD_FOR_PS2 + #define HS_BUILD_FOR_REFERENCE 1 +#endif + +#if defined(HS_BUILD_FOR_MAC68K) || defined(HS_BUILD_FOR_MACPPC) + #define HS_BUILD_FOR_MAC 1 +#endif + +#if defined(__INTEL__) && defined(HS_BUILD_FOR_MAC) + #error "Can't have HS_BUILD_FOR_MAC defined" +#endif +#if (defined(GENERATING68K) || defined(GENERATINGPOWERPC)) && defined(HS_BUILD_FOR_WIN32) + #define "Can't define HS_BUILD_FOR_WIN32" +#endif + +#define HS_SCALAR_IS_FIXED !(HS_SCALAR_IS_FLOAT) +#define HS_NEVER_USE_FLOAT !(HS_CAN_USE_FLOAT) + +#if HS_DEBUG_MATH_OVERFLOW && !(HS_PIN_MATH_OVERFLOW) + #error "Can't debug overflow unless HS_PIN_MATH_OVERFLOW is ON" +#endif + + +///////////////////////Windows Specific Defines ///////////////////////////// + +#if HS_BUILD_FOR_WIN32 + +// 4244: Conversion +// 4305: Truncation +// 4503: 'identifier' : decorated name length exceeded, name was truncated +// 4018: signed/unsigned mismatch +// 4786: 255 character debug limit +// 4284: STL template defined operator-> for a class it doesn't make sense for (int, etc) +#if !__MWERKS__ +#pragma warning( disable : 4305 4503 4018 4786 4284) +#endif + +// VC++ version greater than 6.0, must be building for .NET +#if defined(_MSC_VER) && (_MSC_VER > 1200) +#define HS_BUILD_FOR_WIN32_NET +#endif + +#pragma optimize( "y", off ) + +#endif + + +/////////////////////Debugging Defines /////////////////////////////////// + +#if (defined(_DEBUG)||defined(UNIX_DEBUG)) && !defined(HS_DISABLE_ASSERT) +#define HS_DEBUGGING +#if (!defined(HS_NO_MEM_TRACKER)) +#define HS_FIND_MEM_LEAKS +#endif +#endif + + +#if HS_BUILD_FOR_PS2 +#define ATTRIBUTE_FOR_PS2 __attribute__((aligned (16))) /* SUNSOFT */ +#else +#define ATTRIBUTE_FOR_PS2 +#endif + + +/////////////////////Myst3D Defines ///////////////////////////////////// + +#ifdef M3DRELEASE +#define PLASMA_NO_NETWORK 1 +#define NEXUS_NO_2D 1 +#define NO_LOAD_MSG 1 +#define PLASMA_NO_CONSOLE 1 +#define NEXUS_NO_DEBUG 1 +#endif + + +///////////////////// Required facilities /////////////////////////////// +#ifndef HeadSpinHDefined +#include "HeadSpin.h" +#endif + +#endif // hsConfigDefined diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsCritSect.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsCritSect.cpp new file mode 100644 index 00000000..f67296cc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsCritSect.cpp @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/CoreLib/hsCritSect.cpp +* +* +* By Eric Anderson (10/23/2005) +* Copyright 2005 Cyan Worlds, Inc. +* +***/ + +#include "HeadSpin.h" +#include "hsCritSect.h" +#pragma hdrstop + + +/**************************************************************************** +* +* Critical section implementation +* +***/ + +#ifdef HS_BUILD_FOR_WIN32 +//=========================================================================== +CCritSect::CCritSect () { + InitializeCriticalSection(&m_handle); +} + +//=========================================================================== +CCritSect::~CCritSect () { + DeleteCriticalSection(&m_handle); +} + +//=========================================================================== +void CCritSect::Enter () { + EnterCriticalSection(&m_handle); +} + +//=========================================================================== +void CCritSect::Leave () { + LeaveCriticalSection(&m_handle); +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsCritSect.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsCritSect.h new file mode 100644 index 00000000..ad20d830 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsCritSect.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/CoreLib/hsCritSect.h +* +* +* By Eric Anderson (10/23/2005) +* Copyright 2005 Cyan Worlds, Inc. +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_CORELIB_HSCRITSECT_H +#define PLASMA20_SOURCES_PLASMA_CORELIB_HSCRITSECT_H + +/**************************************************************************** +* +* Critical section +* +***/ + + +#ifdef HS_BUILD_FOR_WIN32 +typedef CRITICAL_SECTION CritSectHandle; +#else +# error "CCritSect: Not implemented on this platform" +#endif + +class CCritSect { +protected: + CritSectHandle m_handle; +public: + CCritSect (); + ~CCritSect (); + void Enter (); + void Leave (); +}; + + +#endif // PLASMA20_SOURCES_PLASMA_CORELIB_HSCRITSECT_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptionStack.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptionStack.cpp new file mode 100644 index 00000000..f2dc6c21 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptionStack.cpp @@ -0,0 +1,35 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsExceptionStack.h" + +hsExceptionStack* hsExceptionStack::fExceptionStack; +hsExceptionStackDestroyer hsExceptionStack::fExceptionStackDestroyer; + +void hsExceptionStack::FreeInstance() +{ + delete fExceptionStack; + fExceptionStack = nil; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptionStack.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptionStack.h new file mode 100644 index 00000000..8dde4446 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptionStack.h @@ -0,0 +1,110 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsExceptionStack_inc +#define hsExceptionStack_inc + +#include "hsUtils.h" +#include "hsTypes.h" +#include "hsTemplates.h" + +class hsExceptionStackDestroyer; + +// +// hsExceptionStack - logs function/scope IDs added by hsStackToken +// + +class hsExceptionStack +{ + friend class hsExceptionStackDestroyer; +private: + hsExceptionStack() { } +public: + ~hsExceptionStack() { } + + static hsExceptionStack& Instance(); + + Int32 GetNumEntries() const { return fEntries.Count(); } + const char* GetEntry(Int32 i) const { return fEntries[i]; } + + void Push(const char* str); + + // After an exception is caught and stack has been displayed, + // call continue to flush stack + void Continue() { fEntries.Reset(); } + +private: + static void FreeInstance(); + + hsTArray fEntries; + + static hsExceptionStack* fExceptionStack; + static hsExceptionStackDestroyer fExceptionStackDestroyer; +}; + +inline hsExceptionStack& hsExceptionStack::Instance() +{ + if (!fExceptionStack) + { + fExceptionStack = TRACKED_NEW hsExceptionStack; + } + + return *fExceptionStack; +} + +inline void hsExceptionStack::Push(const char* str) +{ + fEntries.Append(str); +} + +// +// hsExceptionStackDestroyer - removes the hsExceptionStack instance +// +class hsExceptionStackDestroyer +{ +public: + ~hsExceptionStackDestroyer() + { + hsExceptionStack::FreeInstance(); + } +}; + +#ifdef HS_DEBUGGING +#define HS_NO_TRY +#endif + +#ifdef HS_NO_TRY + +#define hsGuardBegin(X) +#define hsGuardEnd + +#else // HS_NO_TRY + +#define hsGuardBegin(X) { const char* guardToken = X; try { +#define hsGuardEnd } catch(...) { hsExceptionStack::Instance().Push(guardToken); throw; } } + +#endif // HS_NO_TRY + +#endif // hsExceptionStack_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptions.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptions.h new file mode 100644 index 00000000..d27a8b39 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsExceptions.h @@ -0,0 +1,196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsExceptionDefined +#define hsExceptionDefined + +#include "hsTypes.h" + +// #define HS_NO_EXCEPTIONS -- this will turn off execptions you might want +// to do it with -D or equivalent instead of here since who knows who includes this. + + + +enum hsErrorEnum { + kNo_hsError, + kBadAlloc_hsError, + kNilParam_hsError, + kBadParam_hsError, + kInternal_hsError, + kOS_hsError +}; + +////////////////////////////////////////////////////////////////////////////// + +class hsException { +public: + hsErrorEnum fError; + long fParam; + + hsException(hsErrorEnum error, long param = 0) : fError(error), fParam(param) {} +}; + +class hsBadAllocException : public hsException { +public: + hsBadAllocException() : hsException(kBadAlloc_hsError) {} +}; + +class hsNilParamException : public hsException { +public: + hsNilParamException() : hsException(kNilParam_hsError) {} +}; + +class hsBadParamException : public hsException { +public: + hsBadParamException() : hsException(kBadParam_hsError) {} +}; + +class hsInternalException : public hsException { +public: + hsInternalException() : hsException(kInternal_hsError) {} +}; + +class hsOSException : public hsException { +public: + hsOSException(long error) : hsException(kOS_hsError, error) {} +}; + +///////////////////////////////////////////////////////////////////////////////// + +#ifndef HS_NO_EXCEPTIONS +#define hsThrow(a) {hsAssert(0,#a);throw a;} +#define hsCatch(a) catch (a) +#define hsCatch2(a,b) catch (a b) + +#define hsTry try + +inline void hsThrowIfNilParam(const void* p) +{ + if (p == nil) + { + hsAssert(0,"hsNilParamException"); + throw hsNilParamException(); + } +} + +inline void hsThrowIfBadParam(hsBool trueIfBadParam) +{ + if (trueIfBadParam) + { + hsAssert(0,"hsBadParamException"); + throw hsBadParamException(); + } +} + +inline void hsThrowIfOSErr(long osErr) +{ + if (osErr != 0) + { + hsAssert(0,"hsOSException"); + throw hsOSException(osErr); + } +} + +inline void hsThrowIfTrue(hsBool condition) +{ + if (condition) + { + hsAssert(0,"hsThrowIfTrue"); + throw hsInternalException(); + } +} + +inline void hsThrowIfFalse(hsBool condition) +{ + if (condition == false) + { + hsAssert(0,"hsThrowIfFalse"); + throw hsInternalException(); + } +} + +inline void hsThrowIfTrue(hsBool condition, const char message[]) +{ + if (condition) + { + hsAssert(0,message); + throw message; + } +} + +inline void hsThrowIfFalse(hsBool condition, const char message[]) +{ + if (condition == false) + { + hsAssert(0,message); + throw message; + } +} + +#else +#define hsThrow(a) {hsAssert(0,#a);} +#define hsCatch(a) if(0) +#define hsCatch2(a,b) if(0) +#define hsTry + +inline void hsThrowIfNilParam(const void* p) +{ + hsAssert(p!=nil,"hsThrowIfNilParam"); +} + +inline void hsThrowIfBadParam(hsBool trueIfBadParam) +{ + hsAssert(!trueIfBadParam,"hsThrowIfBadParam"); +} + +inline void hsThrowIfOSErr(long osErr) +{ + hsAssert(osErr==0,"hsThrowIfOSErr"); +} + +inline void hsThrowIfTrue(hsBool condition) +{ + hsAssert(!condition,"hsThrowIfTrue"); +} + +inline void hsThrowIfFalse(hsBool condition) +{ + hsAssert(condition,"hsThrowIfFalse"); +} + +inline void hsThrowIfTrue(hsBool condition, const char message[]) +{ + hsAssert(!condition,message); +} + +inline void hsThrowIfFalse(hsBool condition, const char message[]) +{ + hsAssert(condition,message); +} + + +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFastMath.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFastMath.cpp new file mode 100644 index 00000000..a21b71c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFastMath.cpp @@ -0,0 +1,626 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsScalar.h" +#include "hsGeometry3.h" +#include "hsFastMath.h" + +const hsScalar hsFastMath::kSqrtTwo = hsSquareRoot(2.f); +const hsScalar hsFastMath::kInvSqrtTwo = hsScalarInvert(hsFastMath::kSqrtTwo); +const hsScalar hsFastMath::kTwoPI = hsScalarPI * 2.f; + +hsPoint2 statCosSinTable[9] = // must match length in inline +{ + { 1.f, 0.f }, + { hsFastMath::kInvSqrtTwo, hsFastMath::kInvSqrtTwo }, + { 0.f, 1.f }, + { -hsFastMath::kInvSqrtTwo, hsFastMath::kInvSqrtTwo }, + { -1.f, 0.f }, + { -hsFastMath::kInvSqrtTwo, -hsFastMath::kInvSqrtTwo }, + { 0.f, -1.f }, + { hsFastMath::kInvSqrtTwo, -hsFastMath::kInvSqrtTwo }, + { 1.f, 0.f } +}; + +const hsPoint2* hsFastMath::fCosSinTable = statCosSinTable; + +unsigned char statSeedTable[] = { + 0x69, + 0x69, + 0x68, + 0x67, + 0x67, + 0x66, + 0x65, + 0x65, + 0x64, + 0x63, + 0x63, + 0x62, + 0x61, + 0x61, + 0x60, + 0x5f, + 0x5f, + 0x5e, + 0x5d, + 0x5d, + 0x5c, + 0x5b, + 0x5b, + 0x5a, + 0x5a, + 0x59, + 0x58, + 0x58, + 0x57, + 0x57, + 0x56, + 0x55, + 0x55, + 0x54, + 0x54, + 0x53, + 0x52, + 0x52, + 0x51, + 0x51, + 0x50, + 0x50, + 0x4f, + 0x4e, + 0x4e, + 0x4d, + 0x4d, + 0x4c, + 0x4c, + 0x4b, + 0x4b, + 0x4a, + 0x4a, + 0x49, + 0x48, + 0x48, + 0x47, + 0x47, + 0x46, + 0x46, + 0x45, + 0x45, + 0x44, + 0x44, + 0x43, + 0x43, + 0x42, + 0x42, + 0x41, + 0x41, + 0x40, + 0x40, + 0x3f, + 0x3f, + 0x3e, + 0x3e, + 0x3d, + 0x3d, + 0x3c, + 0x3c, + 0x3c, + 0x3b, + 0x3b, + 0x3a, + 0x3a, + 0x39, + 0x39, + 0x38, + 0x38, + 0x37, + 0x37, + 0x36, + 0x36, + 0x36, + 0x35, + 0x35, + 0x34, + 0x34, + 0x33, + 0x33, + 0x33, + 0x32, + 0x32, + 0x31, + 0x31, + 0x30, + 0x30, + 0x30, + 0x2f, + 0x2f, + 0x2e, + 0x2e, + 0x2e, + 0x2d, + 0x2d, + 0x2c, + 0x2c, + 0x2b, + 0x2b, + 0x2b, + 0x2a, + 0x2a, + 0x29, + 0x29, + 0x29, + 0x28, + 0x28, + 0x28, + 0x27, + 0x27, + 0x26, + 0x26, + 0x26, + 0x25, + 0x25, + 0x25, + 0x24, + 0x24, + 0x23, + 0x23, + 0x23, + 0x22, + 0x22, + 0x22, + 0x21, + 0x21, + 0x20, + 0x20, + 0x20, + 0x1f, + 0x1f, + 0x1f, + 0x1e, + 0x1e, + 0x1e, + 0x1d, + 0x1d, + 0x1d, + 0x1c, + 0x1c, + 0x1c, + 0x1b, + 0x1b, + 0x1b, + 0x1a, + 0x1a, + 0x1a, + 0x19, + 0x19, + 0x19, + 0x18, + 0x18, + 0x18, + 0x17, + 0x17, + 0x17, + 0x16, + 0x16, + 0x16, + 0x15, + 0x15, + 0x15, + 0x14, + 0x14, + 0x14, + 0x13, + 0x13, + 0x13, + 0x13, + 0x12, + 0x12, + 0x12, + 0x11, + 0x11, + 0x11, + 0x10, + 0x10, + 0x10, + 0xf, + 0xf, + 0xf, + 0xf, + 0xe, + 0xe, + 0xe, + 0xd, + 0xd, + 0xd, + 0xd, + 0xc, + 0xc, + 0xc, + 0xb, + 0xb, + 0xb, + 0xb, + 0xa, + 0xa, + 0xa, + 0x9, + 0x9, + 0x9, + 0x9, + 0x8, + 0x8, + 0x8, + 0x7, + 0x7, + 0x7, + 0x7, + 0x6, + 0x6, + 0x6, + 0x6, + 0x5, + 0x5, + 0x5, + 0x5, + 0x4, + 0x4, + 0x4, + 0x3, + 0x3, + 0x3, + 0x3, + 0x2, + 0x2, + 0x2, + 0x2, + 0x1, + 0x1, + 0x1, + 0x1, + 0x0, + 0x0, + 0x0, + 0xff, + 0xfe, + 0xfd, + 0xfc, + 0xfb, + 0xfa, + 0xf9, + 0xf8, + 0xf7, + 0xf7, + 0xf6, + 0xf5, + 0xf4, + 0xf3, + 0xf2, + 0xf1, + 0xf0, + 0xef, + 0xee, + 0xed, + 0xec, + 0xec, + 0xeb, + 0xea, + 0xe9, + 0xe8, + 0xe7, + 0xe6, + 0xe5, + 0xe5, + 0xe4, + 0xe3, + 0xe2, + 0xe1, + 0xe0, + 0xe0, + 0xdf, + 0xde, + 0xdd, + 0xdc, + 0xdb, + 0xdb, + 0xda, + 0xd9, + 0xd8, + 0xd8, + 0xd7, + 0xd6, + 0xd5, + 0xd4, + 0xd4, + 0xd3, + 0xd2, + 0xd1, + 0xd1, + 0xd0, + 0xcf, + 0xce, + 0xce, + 0xcd, + 0xcc, + 0xcb, + 0xcb, + 0xca, + 0xc9, + 0xc9, + 0xc8, + 0xc7, + 0xc7, + 0xc6, + 0xc5, + 0xc4, + 0xc4, + 0xc3, + 0xc2, + 0xc2, + 0xc1, + 0xc0, + 0xc0, + 0xbf, + 0xbe, + 0xbe, + 0xbd, + 0xbc, + 0xbc, + 0xbb, + 0xba, + 0xba, + 0xb9, + 0xb8, + 0xb8, + 0xb7, + 0xb7, + 0xb6, + 0xb5, + 0xb5, + 0xb4, + 0xb3, + 0xb3, + 0xb2, + 0xb2, + 0xb1, + 0xb0, + 0xb0, + 0xaf, + 0xaf, + 0xae, + 0xad, + 0xad, + 0xac, + 0xac, + 0xab, + 0xaa, + 0xaa, + 0xa9, + 0xa9, + 0xa8, + 0xa8, + 0xa7, + 0xa7, + 0xa6, + 0xa5, + 0xa5, + 0xa4, + 0xa4, + 0xa3, + 0xa3, + 0xa2, + 0xa2, + 0xa1, + 0xa0, + 0xa0, + 0x9f, + 0x9f, + 0x9e, + 0x9e, + 0x9d, + 0x9d, + 0x9c, + 0x9c, + 0x9b, + 0x9b, + 0x9a, + 0x9a, + 0x99, + 0x99, + 0x98, + 0x98, + 0x97, + 0x97, + 0x96, + 0x96, + 0x95, + 0x95, + 0x94, + 0x94, + 0x93, + 0x93, + 0x92, + 0x92, + 0x91, + 0x91, + 0x90, + 0x90, + 0x8f, + 0x8f, + 0x8e, + 0x8e, + 0x8d, + 0x8d, + 0x8c, + 0x8c, + 0x8b, + 0x8b, + 0x8b, + 0x8a, + 0x8a, + 0x89, + 0x89, + 0x88, + 0x88, + 0x87, + 0x87, + 0x87, + 0x86, + 0x86, + 0x85, + 0x85, + 0x84, + 0x84, + 0x83, + 0x83, + 0x83, + 0x82, + 0x82, + 0x81, + 0x81, + 0x80, + 0x80, + 0x80, + 0x7f, + 0x7f, + 0x7e, + 0x7e, + 0x7d, + 0x7d, + 0x7d, + 0x7c, + 0x7c, + 0x7b, + 0x7b, + 0x7b, + 0x7a, + 0x7a, + 0x79, + 0x79, + 0x79, + 0x78, + 0x78, + 0x77, + 0x77, + 0x77, + 0x76, + 0x76, + 0x75, + 0x75, + 0x75, + 0x74, + 0x74, + 0x74, + 0x73, + 0x73, + 0x72, + 0x72, + 0x72, + 0x71, + 0x71, + 0x71, + 0x70, + 0x70, + 0x6f, + 0x6f, + 0x6f, + 0x6e, + 0x6e, + 0x6e, + 0x6d, + 0x6d, + 0x6c, + 0x6c, + 0x6c, + 0x6b, + 0x6b, + 0x6b, + 0x6a, + 0x6a +}; + +hsScalar hsFastMath::IATan2OverTwoPi(hsScalar y, hsScalar x) +{ + const int tabSize = 16; // pad with one extra because hi can go hi + const int tabMax = tabSize-1; + static hsScalar tab[tabSize+1] = { + 0.f, + 0.0105947f, + 0.0210962f, + 0.0314165f, + 0.0414762f, + 0.0512082f, + 0.0605595f, + 0.0694914f, + 0.0779791f, + 0.0860104f, + 0.0935835f, + 0.100705f, + 0.107388f, + 0.113651f, + 0.119514f, + 0.125f, + 0 }; + + if( (x == 0)&&(y == 0) ) + return 0; + + hsBool xNeg, yNeg; + if( yNeg = (y < 0) )y = -y; + if( xNeg = (x < 0) )x = -x; + hsBool yBigger = y >= x; + hsScalar div = yBigger ? x / y : y / x; + + hsScalar fInd = div * tabMax; + int lo = int(fInd); + int hi = lo+1; + hsScalar frac = fInd - lo; + + hsScalar res = tab[lo]; + res += frac * (tab[hi] - res); + + // now move to proper half quadrant + hsAssert((res >= 0)&&(res <= 0.25f), "Lookup atan2 out of bounds"); + if( yBigger ) + res = 0.25f - res; + switch( (yNeg << 1)|xNeg ) + { + case 0: + break; + case 1: + res = 0.5f - res; + break; + case 3: + res += 0.5f; + break; + case 2: + res = 1.f - res; + break; + } + return res; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFastMath.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFastMath.h new file mode 100644 index 00000000..72ed711c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFastMath.h @@ -0,0 +1,290 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsFastMath_inc +#define hsFastMath_inc + +#include "hsPoint2.h" +#include "hsGeometry3.h" + +class hsFastMath { +protected: + static const hsPoint2* fCosSinTable; + +public: + static const hsScalar kSqrtTwo; + static const hsScalar kInvSqrtTwo; + static const hsScalar kTwoPI; + + static hsScalar IATan2OverTwoPi(hsScalar y, hsScalar x); + + static inline hsScalar InvSqrtAppr(hsScalar x); + static inline hsScalar InvSqrt(hsScalar x); + static inline hsVector3& Normalize(hsVector3& v) { return (v *= InvSqrt(v.MagnitudeSquared())); } + static inline hsVector3& NormalizeAppr(hsVector3& v) { return (v *= InvSqrtAppr(v.MagnitudeSquared())); } + + static inline void SinCosAppr(hsScalar rads, hsScalar& sinRads, hsScalar& cosRads); + static inline void SinCosInRangeAppr(hsScalar rads, hsScalar& sinRads, hsScalar& cosRads); + + static inline void SinCos(hsScalar rads, hsScalar& sinRads, hsScalar& cosRads); + static inline void SinCosInRange(hsScalar ang, hsScalar& sinRads, hsScalar& cosRads); + + static inline hsScalar Sin(hsScalar rads); + static inline hsScalar Cos(hsScalar rads); + static inline hsScalar SinInRange(hsScalar rads); + static inline hsScalar CosInRange(hsScalar rads); +}; + + +// One over Square Root - from Graphics Gems +// Interesting combo's are +// NUM_ITER LOOKUP_BITS err frac us per call +// 0 8 5e-3 0.045 +// 1 8 3e-5 0.082 +// 0 6 1e-2 0.045 +// 1 6 1e-4 0.082 +// 2 6 1e-7 0.11 +// 1 4 2e-3 0.082 +// 2 4 5e-6 0.11 +// 2 3 8e-5 0.11 +// Tested on 5000 random numbers from [1.e-6..1.e3] over several runs +// These are tight loops, though, so they don't weigh in a bigger +// table trashing the cache. +#define NUM_ITER 0 +#define LOOKUP_BITS 8 +#define EXP_POS 23 +#define EXP_BIAS 127 + +#define LOOKUP_POS (EXP_POS - LOOKUP_BITS) +#define SEED_POS (EXP_POS - 8) +#define TABLE_SIZE (2 << LOOKUP_BITS) +#define LOOKUP_MASK (TABLE_SIZE - 1) +#define GET_EXP(a) (((a) >> EXP_POS) & 0xff) +#define SET_EXP(a) ((a) << EXP_POS) +#define GET_EMANT(a) (((a) >> LOOKUP_POS) & LOOKUP_MASK) + +#define SET_MANTSEED(a) (((unsigned long) (a)) << SEED_POS) + +inline hsScalar hsFastMath::InvSqrtAppr(hsScalar x) +{ + register unsigned long a = *(long*)&x; + register float arg = x; + union { + long i; + float f; + } seed; + register float r; + + extern unsigned char statSeedTable[]; + + seed.i = SET_EXP(((3*EXP_BIAS - 1) - GET_EXP(a)) >> 1) | SET_MANTSEED(statSeedTable[GET_EMANT(a)]); + + r = seed.f; + +#if NUM_ITER > 0 + r = (3.0f - r * r * arg) * r * 0.5f; + +#if NUM_ITER > 1 + r = (3.0f - r * r * arg) * r * 0.5f; +#endif +#endif + + return r; +} + +inline hsScalar hsFastMath::InvSqrt(hsScalar x) +{ + register unsigned long a = *(long*)&x; + register float arg = x; + union { + long i; + float f; + } seed; + register float r; + + extern unsigned char statSeedTable[]; + + seed.i = SET_EXP(((3*EXP_BIAS - 1) - GET_EXP(a)) >> 1) | SET_MANTSEED(statSeedTable[GET_EMANT(a)]); + + r = seed.f; + + r = (3.0f - r * r * arg) * r * 0.5f; + + r = (3.0f - r * r * arg) * r * 0.5f; + + return r; +} + + +inline void hsFastMath::SinCosAppr(hsScalar rads, hsScalar& sinRads, hsScalar& cosRads) +{ + rads = fmodf(rads, kTwoPI); + if( rads < 0 ) + rads += kTwoPI; + SinCosInRangeAppr(rads, sinRads, cosRads); +} + +inline void hsFastMath::SinCosInRangeAppr(hsScalar rads, hsScalar& sinRads, hsScalar& cosRads) +{ + const int kNumSinCosEntries = 8; + const hsScalar kNumEntriesOverTwoPI = kNumSinCosEntries * 0.5f / hsScalarPI; + hsScalar t = rads * kNumEntriesOverTwoPI; + int iLo = (int)t; + t -= iLo; + + const hsPoint2* p = &fCosSinTable[iLo + 1]; + cosRads = p->fX; + sinRads = p->fY; + p--; + cosRads -= p->fX; + sinRads -= p->fY; + cosRads *= t; + sinRads *= t; + cosRads += p->fX; + sinRads += p->fY; + +} + +inline hsScalar hsFastMath::Sin(hsScalar rads) +{ + rads = fmodf(rads, kTwoPI); + if( rads < 0 ) + rads += kTwoPI; + + return SinInRange(rads); +} + +inline hsScalar hsFastMath::Cos(hsScalar rads) +{ + rads = fmodf(rads, kTwoPI); + if( rads < 0 ) + rads += kTwoPI; + + return CosInRange(rads); +} + +inline hsScalar hsFastMath::SinInRange(hsScalar ang) +{ + float sgn = 1.f; + + if(ang >= (0.75f * kTwoPI)) + ang -= kTwoPI; + else if(ang >= (0.25f * kTwoPI)) + { + ang -= 3.141592654f; + sgn = -1.0f; + } + + return (ang - (ang*ang*ang) * (1.0f/6.0f) + (ang*ang*ang*ang*ang) / 120.0f) * sgn; +} + +inline hsScalar hsFastMath::CosInRange(hsScalar ang) +{ + float sgn = 1.f; + + if(ang >= (0.75f * kTwoPI)) + ang -= kTwoPI; + else if(ang >= (0.25f * kTwoPI)) + { + ang -= 3.141592654f; + sgn = -1.0f; + } + + return (1.0f - (ang*ang / 2.0f) + (ang*ang*ang*ang) / 24.0f) *sgn; +} + +inline void hsFastMath::SinCos(hsScalar rads, hsScalar& sinRads, hsScalar& cosRads) +{ + rads = fmodf(rads, kTwoPI); + if( rads < 0 ) + rads += kTwoPI; + SinCosInRange(rads, sinRads, cosRads); +} + +inline void hsFastMath::SinCosInRange(hsScalar ang, hsScalar& sinRads, hsScalar& cosRads) +{ + float sgn = 1.f; + + if(ang >= (0.75f * kTwoPI)) + ang -= kTwoPI; + else if(ang >= (0.25f * kTwoPI)) + { + ang -= 3.141592654f; + sgn = -1.0f; + } + + sinRads = (ang - (ang*ang*ang) * (1.0f/6.0f) + (ang*ang*ang*ang*ang) / 120.0f) * sgn; + cosRads = (1.0f - (ang*ang / 2.0f) + (ang*ang*ang*ang) / 24.0f) *sgn; +} +// +// Here's an interesting one from GDalgorithms, which doesn't need a LUT +// Not sure how the accuracy compares, but it's probably fine for this purpose. +#if 0 // For future reference +/* +From: "Jason Dorie" +To: "GDAlgorithms" +Date: Wed, 14 Mar 2001 11:43:48 -0800 +Subject: [Algorithms] Fast simultaneous Sin() and Cos() +Reply-To: gdalgorithms-list@lists.sourceforge.net + + + I know someone (Jason Zisk?) was looking for fast rotation matrix +generation code. I realize that a Sin/Cos lookup table is the way to go for +absolute speed, but if storage is a concern and the accuracy isn't, this +code is about 5x faster than using the built-in sin and cos instructions, +and accurate to about 4 decimal places. + + If you really want speed, and don't care about accuracy, drop the 2nd +polynomial from each term. It's less accurate and faster still. It could +probably be made even faster by replacing the if/else with branchless code, +but I haven't bothered to figure out how yet. + + + My angles are 0-65535 so that they can be masked into range easily, stored +as shorts, and converted to normalized floats where necessary using SIMD +instructions. +*/ + +void FastSinCos(long Angle, float *pSin, float *pCos) +{ +float ang, sgn; + + ang = (Angle & 65535) * ((1.0f/65536.0f) * TwoPI); + + sgn = 1.0f; + if(ang >= (0.75f * TwoPI)) + ang -= TwoPI; + else if(ang >= (0.25f * TwoPI)) + { + ang -= 3.141592654f; + sgn = -1.0f; + } + + *pSin = (ang - (ang*ang*ang) * (1.0f/6.0f) + (ang*ang*ang*ang*ang) / 120.0f) * sgn; + *pCos = (1.0f - (ang*ang / 2.0f) + (ang*ang*ang*ang) / 24.0f) *sgn; +} +#endif // For future reference +#endif // hsFastMath_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFixedTypes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFixedTypes.h new file mode 100644 index 00000000..d73a3609 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsFixedTypes.h @@ -0,0 +1,118 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsFixedTypesDefined +#define hsFixedTypesDefined + +#include "hsTypes.h" + +#if HS_BUILD_FOR_MAC + #include + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define hsIntToFixed(x) ((hsFixed)(x) << 16) +#define hsFixedToInt(x) ((x) >> 16) +#define hsFixedRound(x) (((x) + 0x8000) >> 16) +#define hsFixed1 hsIntToFixed(1) +#define hsFixedPI (0x3243F) +#define hsFixedPiOver2 (0x1921F) + +#define hsFixedToFract(x) ((hsFract)(x) << 14) +#define hsFractToFixed(x) ((hsFixed)(x) >> 14) +#define hsFract1 hsFixedToFract(hsFixed1) +#define hsFractPiOver2 (0x6487ED34) /* needs some work */ + +#define hsFixFloor(x) \ + (hsFixed)((x) < 0 ? -(hsFixed)((-(x) + 0xFFFF) & 0xFFFF0000) : (x) & 0xFFFF0000) + +#define hsFixedToFloorInt(x) \ + (int)((x) < 0 ? -(int)((-(x) + 0xFFFF) >> 16) : ((x) >> 16)) + +#define hsFixCeiling(x) \ + (hsFixed)((x) < 0 ? -(hsFixed)(-(x) & 0xFFFF0000) : ((x) + 0xFFFF) & 0xFFFF0000) + +#define hsFixedToCeilingInt(x) \ + (int)((x) < 0 ? -(int)(-(x) >> 16) : (((x) + 0xFFFF) >> 16)) + + +#if HS_CAN_USE_FLOAT + #define hsFixedToFloat(x) ((x) / float(hsFixed1)) + #define hsFloatToFixed(x) hsFixed((x) * hsFixed1) + + #define hsFractToFloat(x) ((x) / float(hsFract1)) + #define hsFloatToFract(x) hsFract((x) * hsFract1) +#endif + +#if HS_BUILD_FOR_MAC68K && !(HS_PIN_MATH_OVERFLOW) + #define hsFixMul(a, b) FixMul(a, b) +#else + hsFixed hsFixMul(hsFixed a, hsFixed b); +#endif + +#if HS_BUILD_FOR_MAC && !(HS_PIN_MATH_OVERFLOW) && !(HS_MP_SAFE) + #define hsFixDiv(a, b) FixDiv(a, b) + #define hsFracMul(a, b) FracMul(a, b) + #define hsFracDiv(a, b) FracDiv(a, b) +#else + hsFract hsFixDiv(hsFixed a, hsFixed b); + hsFract hsFracMul(hsFract a, hsFract b); + hsFract hsFracDiv(hsFract a, hsFract b); +#endif + +hsFract hsFracSqrt(hsFract value); +#define hsFixSqrt(value) (hsFracSqrt(value) >> 7) +hsFract hsFracCubeRoot(hsFract value); +hsFixed hsFixedSin(hsFixed s); +hsFixed hsFixedCos(hsFixed s); +hsFixed hsFixedASin(hsFixed s); +hsFixed hsFixedACos(hsFixed s); + +UInt16 hsSqrt32(UInt32 value); +UInt16 hsCubeRoot32(UInt32 value); +Int32 hsMulDiv32(Int32 numer1, Int32 numer2, Int32 denom); +Int32 hsMagnitude32(Int32 x, Int32 y); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus + struct hsFixedPlane { + hsFixed fA, fB, fC; + + void Set(hsFixed a, hsFixed b, hsFixed c) { fA = a; fB = b; fC = c; } + + hsFixed FixEval(hsFixed x, hsFixed y) const { return hsFixMul(fA, x) + hsFixMul(fB, y) + fC; } + Int32 IntEval(Int32 x, Int32 y) const { return fA * x + fB * y + fC; } + void ShiftDown(UInt32 i) { fA >>= i; fB >>= i; fC >>= i;} + }; +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsGeometry3.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsGeometry3.cpp new file mode 100644 index 00000000..521ba0e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsGeometry3.cpp @@ -0,0 +1,117 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsGeometry3.h" +#include "hsStream.h" + +hsVector3 operator%(const hsVector3& t, const hsVector3& s) +{ + hsVector3 result; + + return *result.Set( hsScalarMul(t.fY, s.fZ) - hsScalarMul(s.fY, t.fZ), + -hsScalarMul(t.fX, s.fZ) + hsScalarMul(s.fX, t.fZ), + hsScalarMul(t.fX, s.fY) - hsScalarMul(s.fX, t.fY)); +} + + + + +////////////////////////////////// +///////////////////////////////// +#if HS_SCALAR_IS_FIXED +hsScalar hsScalarTriple::Magnitude() const +{ + hsWide result, temp; + + result.Mul(fCoord[0], fCoord[0]); + temp.Mul(fCoord[1], fCoord[1]); + result.Add(&temp); + temp.Mul(fCoord[2], fCoord[2]); + result.Add(&temp); + + return result.Sqrt(); +} + +hsScalar hsScalarTriple::MagnitudeSquared() const +{ + hsWide result, temp; + + result.Mul(fCoord[0], fCoord[0]); + temp.Mul(fCoord[1], fCoord[1]); + result.Add(&temp); + temp.Mul(fCoord[2], fCoord[2]); + result.Add(&temp); + + return result.AsFixed(); +} +#endif + +void hsScalarTriple::Read(hsStream *stream) +{ + + // DANGER for speed read directly into these variables...ASSUMES fX,fY, and fZ are in contiguous order (PBG) + stream->Read12Bytes(&fX); +#if HS_BUILD_FOR_MAC + fX = hsSwapEndianFloat(fX); + fY = hsSwapEndianFloat(fY); + fZ = hsSwapEndianFloat(fZ); +#endif + +} + +void hsScalarTriple::Write(hsStream *stream) const +{ + stream->WriteSwapScalar(fX); + stream->WriteSwapScalar(fY); + stream->WriteSwapScalar(fZ); +} + +hsPlane3::hsPlane3(const hsPoint3* pt1, const hsPoint3* pt2, const hsPoint3* pt3) +{ + // convert into a point with two vectors + hsVector3 v1(pt2, pt1); + hsVector3 v2(pt3, pt1); + + // calculate the normal (wedge) + fN.fX = (v1.fY * v2.fZ) - (v2.fY * v1.fZ); + fN.fY = (v1.fZ * v2.fX) - (v2.fZ * v1.fX); + fN.fZ = (v1.fX * v2.fY) - (v2.fX * v1.fY); + fN.Normalize(); + + fD = -pt1->InnerProduct(&fN); +} + + +void hsPlane3::Read(hsStream *stream) +{ + fN.Read(stream); + fD=stream->ReadSwapScalar(); +} + +void hsPlane3::Write(hsStream *stream) const +{ + fN.Write(stream); + stream->WriteSwapScalar(fD); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsGeometry3.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsGeometry3.h new file mode 100644 index 00000000..ac1308fe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsGeometry3.h @@ -0,0 +1,496 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsGGeometry3Defined +#define hsGGeometry3Defined + +#include "hsTypes.h" +struct hsVector3; +struct hsPoint3; +struct hsScalarTriple; +class hsStream; + +#if HS_BUILD_FOR_PS2 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/**** vu0 inline ****/ +#if 1 +#define inline_asm inline /* inline */ +#else +#define inline_asm /* not inline */ +#endif + +/******* HeadSpin *******/ +typedef float hsScalar; + +/* -------------------------------------------------------------------------------- */ +/* return(sqrt(x)) */ +inline_asm hsScalar SqrtVU0(hsScalar x) +{ + register hsScalar ret; + + asm volatile(" \ + mfc1 $8,%1 \ + qmtc2 $8,vf4 \ + vsqrt Q,vf4x \ + vwaitq \ + cfc2 $2,$vi22 \ + mtc1 $2,%0 \ + " :"=f" (ret) : "f" (x), "0" (ret) : "$2", "$8", "memory"); + + return ret; +} + +/* -------------------------------------------------------------------------------- */ +/* return(1 / a) */ +inline_asm hsScalar ScalarInvertVU0(hsScalar a) +{ + register hsScalar ret; + + asm volatile(" \ + mfc1 $8,%1 \ + qmtc2 $8,vf2 \ + vdiv Q,vf0w,vf2x \ + vwaitq \ + cfc2 $2,$vi22 \ + mtc1 $2,%0 \ + " :"=f" (ret) : "f" (a), "0" (ret) : "$2", "$8", "memory"); + + return ret; +} + +/* -------------------------------------------------------------------------------- */ +/* return(a * b) */ +inline_asm hsScalar ScalarMulVU0(hsScalar a, hsScalar b) +{ + register hsScalar ret; + + asm volatile(" \ + mfc1 $8,%1 \ + qmtc2 $8,vf2 \ + mfc1 $9,%2 \ + qmtc2 $9,vf3 \ + vmul.x vf3,vf2,vf3 \ + qmfc2 $2 ,vf3 \ + mtc1 $2,%0 \ + " :"=f" (ret) : "f" (a), "f" (b), "0" (ret) : "$2", "$8", "$9", "memory"); + + return ret; +} + +#endif // PS2 + +/* + If value is already close to hsScalar1, then this is a good approx. of 1/sqrt(value) +*/ +static inline hsScalar hsInvSqrt(hsScalar value) +{ + hsScalar guess; + hsScalar threeOverTwo = hsScalar1 + hsScalarHalf; + + value = hsScalarDiv2(value); + guess = threeOverTwo - value; // with initial guess = 1.0 + + // repeat this line for better approx + guess = hsScalarMul(guess, threeOverTwo - hsScalarMul(hsScalarMul(value, guess), guess)); + guess = hsScalarMul(guess, threeOverTwo - hsScalarMul(hsScalarMul(value, guess), guess)); + + return guess; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +struct hsScalarTriple +{ +//protected: +// hsScalarTriple() : fX(privateData[0]), fY(privateData[1]), fZ(privateData[2]) {} +// hsScalarTriple(hsScalar x, hsScalar y, hsScalar z) +// : fX(privateData[0]), fY(privateData[1]), fZ(privateData[2]) { fX = x, fY = y, fZ = z; } +// +// union { +// u_long128 privateTemp; +// hsScalar privateData[4]; +// }; +//public: +// +// int operator=(const hsScalarTriple& o) { privateTemp = o.privateTemp; } +// hsScalarTriple(const hsScalarTriple& o) : fX(privateData[0]), fY(privateData[1]), fZ(privateData[2]) +// { *this = o; } +// +// hsScalar& fX; +// hsScalar& fY; +// hsScalar& fZ; +protected: + hsScalarTriple() {} + hsScalarTriple(hsScalar x, hsScalar y, hsScalar z) : fX(x), fY(y), fZ(z) {} +public: + hsScalar fX, fY, fZ; + + hsScalarTriple* Set(hsScalar x, hsScalar y, hsScalar z) { fX= x; fY = y; fZ = z; return this;} + hsScalarTriple* Set(const hsScalarTriple *p) { fX = p->fX; fY = p->fY; fZ = p->fZ; return this;} + + hsScalar InnerProduct(const hsScalarTriple &p) const; + hsScalar InnerProduct(const hsScalarTriple *p) const; + +// hsScalarTriple LERP(hsScalarTriple &other, hsScalar t); +#if HS_SCALAR_IS_FIXED + hsScalar Magnitude() const; + hsScalar MagnitudeSquared() const; +#else + +#if HS_BUILD_FOR_PS2 + hsScalar Magnitude() const; +#else + hsScalar Magnitude() const { return hsSquareRoot(MagnitudeSquared()); } +#endif + hsScalar MagnitudeSquared() const { return (fX * fX + fY * fY + fZ * fZ); } +#endif + + hsBool IsEmpty() const { return fX == 0 && fY == 0 && fZ == 0; } + + hsScalar operator[](int i) const; + hsScalar& operator[](int i); + + void Read(hsStream *stream); + void Write(hsStream *stream) const; + +} ATTRIBUTE_FOR_PS2; /* SUNSOFT */ + + +#if HS_BUILD_FOR_PS2 +inline hsScalar hsScalarTriple::Magnitude() const +{ + MATRIX4 m; + m[0] = fX; + m[1] = fY; + m[2] = fZ; + return MagnitudeVU0(m); +} +#endif + + +/////////////////////////////////////////////////////////////////////////// +inline hsScalar& hsScalarTriple::operator[] (int i) +{ + hsAssert(i >=0 && i <3, "Bad index for hsScalarTriple::operator[]"); + return *(&fX + i); +} +inline hsScalar hsScalarTriple::operator[] (int i) const +{ + hsAssert(i >=0 && i <3, "Bad index for hsScalarTriple::operator[]"); + return *(&fX + i); +} +inline hsScalar hsScalarTriple::InnerProduct(const hsScalarTriple &p) const +{ + hsScalar tmp = fX*p.fX; + tmp += fY*p.fY; + tmp += fZ*p.fZ; + return tmp; +} +inline hsScalar hsScalarTriple::InnerProduct(const hsScalarTriple *p) const +{ + hsScalar tmp = fX*p->fX; + tmp += fY*p->fY; + tmp += fZ*p->fZ; + return tmp; +} + +//inline hsScalarTriple hsScalarTriple::LERP(hsScalarTriple &other, hsScalar t) +//{ +// hsScalarTriple p = other - this; +// p = p / t; +// return this + p; +//} + + + +///////////////////////////////////////////////////////////////////////////////////////////// +struct hsPoint3 : public hsScalarTriple { + hsPoint3() {}; + hsPoint3(hsScalar x, hsScalar y, hsScalar z) : hsScalarTriple(x,y,z) {} + explicit hsPoint3(const hsScalarTriple& p) : hsScalarTriple(p) {} + + hsPoint3* Set(hsScalar x, hsScalar y, hsScalar z) { return (hsPoint3*)this->hsScalarTriple::Set(x,y,z);} + hsPoint3* Set(const hsScalarTriple* p) { return (hsPoint3*)this->hsScalarTriple::Set(p) ;} + + friend inline hsPoint3 operator+(const hsPoint3& s, const hsPoint3& t); + friend inline hsPoint3 operator+(const hsPoint3& s, const hsVector3& t); + friend inline hsPoint3 operator-(const hsPoint3& s, const hsPoint3& t); + friend inline hsPoint3 operator-(const hsPoint3& s); + friend inline hsPoint3 operator*(const hsScalar& s, const hsPoint3& t); + friend inline hsPoint3 operator*(const hsPoint3& t, const hsScalar& s); + friend inline hsPoint3 operator/(const hsPoint3& t, const hsScalar& s); + hsBool operator==(const hsPoint3& ss) const + { + return (ss.fX == fX && ss.fY == fY && ss.fZ == fZ); + } + hsBool operator!=(const hsPoint3& ss) const { return !(*this == ss); } + hsPoint3 &operator+=(const hsScalarTriple &s) { fX += s.fX; fY += s.fY; fZ += s.fZ; return *this; } + hsPoint3 &operator*=(const hsScalar s) { fX *= s; fY *= s; fZ *= s; return *this; } +} ATTRIBUTE_FOR_PS2; /* SUNSOFT */ + + +///////////////////////////////////////////////////////////////////////////////////////////// + +struct hsVector3 : public hsScalarTriple { + + hsVector3() {}; + hsVector3(hsScalar x, hsScalar y, hsScalar z) : hsScalarTriple(x,y,z) {} + explicit hsVector3(const hsScalarTriple& p) : hsScalarTriple(p) { } + hsVector3(const hsPoint3 *p1, const hsPoint3 *p2) { + fX = p1->fX - p2->fX, fY= p1->fY - p2->fY, fZ = p1->fZ - p2->fZ; } + + hsVector3* Set(hsScalar x, hsScalar y, hsScalar z) { return (hsVector3*)hsScalarTriple::Set(x,y,z); } + hsVector3* Set(const hsScalarTriple* p) { return (hsVector3*)hsScalarTriple::Set(p) ;} + hsVector3* Set(const hsScalarTriple* p1, const hsScalarTriple* p2) { return Set(p1->fX-p2->fX,p1->fY-p2->fY,p1->fZ-p2->fZ);} + + void Normalize() + { +#if HS_BUILD_FOR_PS2 + hsScalar length = this->Magnitude(); + hsIfDebugMessage(length == 0, "Err: Normalizing hsVector3 of length 0", 0); + if (length == 0) + return; + NormalizeVU0(length, (MATRIX4)this); +#else + hsScalar length = this->Magnitude(); +// hsIfDebugMessage(length == 0, "Err: Normalizing hsVector3 of length 0", 0); + if (length == 0) + return; + hsScalar invMag = hsScalarInvert(length); + + fX = hsScalarMul(fX, invMag); + fY = hsScalarMul(fY, invMag); + fZ = hsScalarMul(fZ, invMag); +#endif + } + inline void Renormalize() // if the vector is already close to unit length + { + hsScalar mag2 = *this * *this; + hsIfDebugMessage(mag2 == 0, "Err: Renormalizing hsVector3 of length 0", 0); + if (mag2 == 0) + return; + hsScalar invMag = hsInvSqrt(mag2); + + fX = hsScalarMul(fX, invMag); + fY = hsScalarMul(fY, invMag); + fZ = hsScalarMul(fZ, invMag); + } + +// hsVector3 &Sub(const hsPoint3& s, const hsPoint3& t) +// { Set(s.fX - t.fX, s.fY - t.fY, s.fZ - t.fZ); +// return *this; }; + friend inline hsVector3 operator+(const hsVector3& s, const hsVector3& t); + friend inline hsVector3 operator-(const hsVector3& s, const hsVector3& t); + friend inline hsVector3 operator-(const hsVector3& s); + friend inline hsVector3 operator*(const hsScalar& s, const hsVector3& t); + friend inline hsVector3 operator*(const hsVector3& t, const hsScalar& s); + friend inline hsVector3 operator/(const hsVector3& t, const hsScalar& s); + friend inline hsScalar operator*(const hsVector3& t, const hsVector3& s); + friend hsVector3 operator%(const hsVector3& t, const hsVector3& s); +#if 0 // Havok reeks + friend hsBool32 operator==(const hsVector3& s, const hsVector3& t) + { + return (s.fX == t.fX && s.fY == t.fY && s.fZ == t.fZ); + } +#else // Havok reeks + hsBool operator==(const hsVector3& ss) const + { + return (ss.fX == fX && ss.fY == fY && ss.fZ == fZ); + } +#endif // Havok reeks + hsVector3 &operator+=(const hsScalarTriple &s) { fX += s.fX; fY += s.fY; fZ += s.fZ; return *this; } + hsVector3 &operator-=(const hsScalarTriple &s) { fX -= s.fX; fY -= s.fY; fZ -= s.fZ; return *this; } + hsVector3 &operator*=(const hsScalar s) { fX *= s; fY *= s; fZ *= s; return *this; } + hsVector3 &operator/=(const hsScalar s) { fX /= s; fY /= s; fZ /= s; return *this; } +} ATTRIBUTE_FOR_PS2; /* SUNSOFT */ + +struct hsPoint4 { + hsScalar fX, fY, fZ, fW; + hsPoint4() {} + hsPoint4(hsScalar x, hsScalar y, hsScalar z, hsScalar w) : fX(x), fY(y), fZ(z), fW(w) {} + hsScalar& operator[](int i); + hsScalar operator[](int i) const; + + hsPoint4& operator=(const hsPoint3&p) { Set(p.fX, p.fY, p.fZ, hsScalar1); return *this; } + + hsPoint4* Set(hsScalar x, hsScalar y, hsScalar z, hsScalar w) + { fX = x; fY = y; fZ = z; fW = w; return this; } +} ATTRIBUTE_FOR_PS2; /* SUNSOFT */ + + +inline hsVector3 operator+(const hsVector3& s, const hsVector3& t) +{ + hsVector3 result; + + return *result.Set(s.fX + t.fX, s.fY + t.fY, s.fZ + t.fZ); +} + +inline hsVector3 operator-(const hsVector3& s, const hsVector3& t) +{ + hsVector3 result; + + return *result.Set(s.fX - t.fX, s.fY - t.fY, s.fZ - t.fZ); +} + +// unary minus +inline hsVector3 operator-(const hsVector3& s) +{ + hsVector3 result; + return *result.Set(-s.fX, -s.fY, -s.fZ); +} + +inline hsVector3 operator*(const hsVector3& s, const hsScalar& t) +{ + hsVector3 result; + return *result.Set(hsScalarMul(s.fX, t), hsScalarMul(s.fY, t), hsScalarMul(s.fZ, t)); +} + +inline hsVector3 operator/(const hsVector3& s, const hsScalar& t) +{ + hsVector3 result; + return *result.Set(hsScalarDiv(s.fX, t), hsScalarDiv(s.fY, t), hsScalarDiv(s.fZ, t)); +} + +inline hsVector3 operator*(const hsScalar& t, const hsVector3& s) +{ + hsVector3 result; + + return *result.Set(hsScalarMul(s.fX, t), hsScalarMul(s.fY, t), hsScalarMul(s.fZ, t)); +} + +inline hsScalar operator*(const hsVector3& t, const hsVector3& s) +{ + return hsScalarMul(t.fX, s.fX) + hsScalarMul(t.fY, s.fY) + hsScalarMul(t.fZ, s.fZ); +} + +//////////////////////////////////////////////////////////////////////////// + +inline hsPoint3 operator+(const hsPoint3& s, const hsPoint3& t) +{ + hsPoint3 result; + + return *result.Set(s.fX + t.fX, s.fY + t.fY, s.fZ + t.fZ); +} + +inline hsPoint3 operator+(const hsPoint3& s, const hsVector3& t) +{ + hsPoint3 result; + + return *result.Set(s.fX + t.fX, s.fY + t.fY, s.fZ + t.fZ); +} + +inline hsPoint3 operator-(const hsPoint3& s, const hsPoint3& t) +{ + hsPoint3 result; + + return *result.Set(s.fX - t.fX, s.fY - t.fY, s.fZ - t.fZ); +} + +// unary - +inline hsPoint3 operator-(const hsPoint3& s) +{ + hsPoint3 result; + return *result.Set(-s.fX, -s.fY, -s.fZ); +} + +inline hsPoint3 operator-(const hsPoint3& s, const hsVector3& t) +{ + hsPoint3 result; + + return *result.Set(s.fX - t.fX, s.fY - t.fY, s.fZ - t.fZ); +} + +inline hsPoint3 operator*(const hsPoint3& s, const hsScalar& t) +{ + hsPoint3 result; + return *result.Set(hsScalarMul(s.fX, t), hsScalarMul(s.fY, t), hsScalarMul(s.fZ, t)); +} + +inline hsPoint3 operator/(const hsPoint3& s, const hsScalar& t) +{ + hsPoint3 result; + return *result.Set(hsScalarDiv(s.fX, t), hsScalarDiv(s.fY, t), hsScalarDiv(s.fZ, t)); +} + +inline hsPoint3 operator*(const hsScalar& t, const hsPoint3& s) +{ + hsPoint3 result; + + return *result.Set(hsScalarMul(s.fX, t), hsScalarMul(s.fY, t), hsScalarMul(s.fZ, t)); +} + +inline hsScalar hsPoint4::operator[] (int i) const +{ + hsAssert(i >=0 && i <4, "Bad index for hsPoint4::operator[]"); + return *(&fX + i); +} + +inline hsScalar& hsPoint4::operator[] (int i) +{ + hsAssert(i >=0 && i <4, "Bad index for hsPoint4::operator[]"); + return *(&fX + i); +} + +typedef hsPoint3 hsGUv; + + + +struct hsPointNorm { + hsPoint3 fPos; + hsVector3 fNorm; + + void Read(hsStream* s) { fPos.Read(s); fNorm.Read(s); } + void Write(hsStream* s) const { fPos.Write(s); fNorm.Write(s); } +} ATTRIBUTE_FOR_PS2; /* SUNSOFT */ + + +struct hsPlane3 { + hsVector3 fN; + hsScalar fD; + + hsPlane3() { } + hsPlane3(const hsVector3* nrml, hsScalar d) + { fN = *nrml; fD=d; } + hsPlane3(const hsPoint3* pt, const hsVector3* nrml) + { fN = *nrml; fD = -pt->InnerProduct(nrml); } + + // create plane from a triangle (assumes clockwise winding of vertices) + hsPlane3(const hsPoint3* pt1, const hsPoint3* pt2, const hsPoint3* pt3); + + hsVector3 GetNormal() const { return fN; } + + void Read(hsStream *stream); + void Write(hsStream *stream) const; +} ATTRIBUTE_FOR_PS2; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsHashTable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsHashTable.h new file mode 100644 index 00000000..82ac8887 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsHashTable.h @@ -0,0 +1,183 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// hsHashTable.h + +#ifndef _hsHashTable_Included_ +#define _hsHashTable_Included_ + +#include "hsTemplates.h" + +template +class hsHashTableIterator +{ +public: + hsHashTableIterator() : fList(nil), fIndex(-1) { } + explicit hsHashTableIterator(hsTArray* list, UInt32 idx) : fList(list), fIndex(idx) { } + + T* operator->() const { return &((*fList)[fIndex]); } + T& operator*() const { return (*fList)[fIndex]; } + + hsHashTableIterator& operator++() { fIndex--; return *this; } + const hsHashTableIterator& operator++(int) { hsHashTableIterator temp(*this); --(*this); return temp; } + + hsHashTableIterator& operator--() { fIndex++; return *this; } + const hsHashTableIterator& operator--(int) { hsHashTableIterator temp(*this); ++(*this); return temp; } + + hsBool operator==(const hsHashTableIterator& other) const { return fList==other.fList && fIndex==other.fIndex; } + hsBool operator!=(const hsHashTableIterator& other) const { return !(*this == other); } + +private: + hsTArray* fList; + UInt32 fIndex; +}; + + +template +class hsHashTable +{ +public: + hsHashTable(UInt32 size=150001, UInt32 step=1); + ~hsHashTable(); + + typedef hsHashTableIterator iterator; + + iterator begin() { return iterator(&fItemList,fItemList.Count()-1); } + iterator end() { return iterator(&fItemList,0); } + void clear(); + + UInt32 count() { return fItemList.Count()-1; } + UInt32 size() { return fSize; } + + UInt32 CollisionCount() { return fCollisionCount; } + + inline void insert(T& item); + inline void erase(T& item); + inline iterator find(const T& item); + + iterator GetItem(UInt32 i); + +private: + hsTArray fItemList; + hsTArray fClearList; + + UInt32* fHashTable; + UInt32 fSize; + UInt32 fCollisionStep; + UInt32 fCollisionCount; + + // No copy or assignment + hsHashTable(const hsHashTable&); + hsHashTable &operator=(const hsHashTable&); +}; + +template +hsHashTable::hsHashTable(UInt32 size, UInt32 step) : +fSize(size), +fCollisionStep(step), +fCollisionCount(0) +{ + fItemList.SetCount(1); + fHashTable = TRACKED_NEW UInt32[fSize]; + memset(fHashTable,0,fSize*sizeof(UInt32)); +} + +template +hsHashTable::~hsHashTable() +{ + delete [] fHashTable; +} + +template +void hsHashTable::clear() +{ + fItemList.SetCount(1); + for (Int32 i=0; i +void hsHashTable::insert(T& item) +{ + hsAssert(fClearList.Count() < fSize,"Hash table overflow! Increase the table size."); + UInt32 h = item.GetHash(); + h %= fSize; + while (UInt32 it = fHashTable[h]) + { + if ( fItemList[it] == item) + { + fItemList[it] = item; + return; + } + h += fCollisionStep; + h %= fSize; + fCollisionCount++; + } + fHashTable[h] = fItemList.Count(); + fItemList.Append(item); + fClearList.Append(h); +} + +template +void hsHashTable::erase(T& item) +{ + UInt32 h = item.GetHash(); + h %= fSize; + while (UInt32 it = fHashTable[h]) + { + if ( fItemList[it] == item ) + { + fHashTable[h] = 0; + return; + } + } +} + +template +hsHashTableIterator hsHashTable::find(const T& item) +{ + UInt32 h = item.GetHash(); + h %= fSize; + while (UInt32 it = fHashTable[h]) + { + if ( fItemList[it] == item ) + { + return iterator(&fItemList,it); + } + h += fCollisionStep; + h %= fSize; + fCollisionCount++; + } + return end(); +} + +template +hsHashTableIterator hsHashTable::GetItem(UInt32 i) +{ + return iterator(&fItemList,i+1); +} + +#endif // _hsHashTable_Included_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsLOD.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsLOD.h new file mode 100644 index 00000000..16f789c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsLOD.h @@ -0,0 +1,40 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsLOD_inc +#define hsLOD_inc + +namespace hsLOD +{ + enum + { + kLODNone = 0x00000000, + kLODLow = 0x00000001, + kLODHigh = 0x00000002, + kLODAll = kLODLow | kLODHigh + }; +} + +#endif // hsLOD_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMMIOStream.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMMIOStream.cpp new file mode 100644 index 00000000..b70dd341 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMMIOStream.cpp @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsMMIOStream.h" + +////////////////////////////////////////////////////////////////////////////////////// + +#if HS_BUILD_FOR_WIN32 + +#include "hsExceptions.h" + +UInt32 hsMMIOStream::Read(UInt32 bytes, void* buffer) +{ + fBytesRead += bytes; + fPosition += bytes; + int numItems = ::mmioRead(fHmfr, (char*)buffer, bytes); + if ((unsigned)numItems < bytes) + { + if (numItems>=0 && ::mmioSeek(fHmfr,0,SEEK_CUR)==::mmioSeek(fHmfr,0,SEEK_END)) { + // EOF ocurred + char str[128]; + sprintf(str, "Hit EOF on MMIO Read, only read %d out of requested %d bytes\n", numItems, bytes); + hsDebugMessage(str, 0); + } + else + { + hsDebugMessage("Error on MMIO Read",0); + } + } + return numItems; +} + +hsBool hsMMIOStream::AtEnd() +{ + return (::mmioSeek(fHmfr,0,SEEK_CUR)==::mmioSeek(fHmfr,0,SEEK_END)); +} + +UInt32 hsMMIOStream::Write(UInt32 bytes, const void* buffer) +{ + fPosition += bytes; + return ::mmioWrite(fHmfr,(const char*)buffer,bytes); +} + +void hsMMIOStream::Skip(UInt32 delta) +{ + fBytesRead += delta; + fPosition += delta; + (void)::mmioSeek(fHmfr, delta, SEEK_CUR); +} + +void hsMMIOStream::Rewind() +{ + fBytesRead = 0; + fPosition = 0; + (void)::mmioSeek(fHmfr, 0, SEEK_SET); +} + +void hsMMIOStream::FastFwd() +{ + fBytesRead = fPosition = ::mmioSeek(fHmfr, 0, SEEK_END); +} + +void hsMMIOStream::Truncate() +{ + hsThrow("Truncate unimplemented by subclass of stream"); +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMMIOStream.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMMIOStream.h new file mode 100644 index 00000000..c45e8424 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMMIOStream.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsMMIOStream_inc +#define hsMMIOStream_inc + +#include "hsWindows.h" +#include "hsStream.h" + +#if HS_BUILD_FOR_WIN32 +#include +#endif + +class hsMMIOStream: public hsStream +{ + HMMIO fHmfr; +public: + + virtual hsBool Open(const char *, const char *) { hsAssert(0, "hsMMIOStream::Open NotImplemented"); return false; } + virtual hsBool Close() { hsAssert(0, "hsMMIOStream::Close NotImplemented"); return false; } + + virtual hsBool AtEnd(); + virtual UInt32 Read(UInt32 byteCount, void* buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void FastFwd(); + virtual void Truncate(); + + HMMIO GetHandle() { return fHmfr; } + void SetHandle(HMMIO handle) { fHmfr = handle; } +}; + +#endif // hsMMIOStream_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMalloc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMalloc.cpp new file mode 100644 index 00000000..f10a72fb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMalloc.cpp @@ -0,0 +1,40 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/CoreLib/hsMalloc.cpp +* +***/ + +#include "HeadSpin.h" + + +/***************************************************************************** +* +* Exports +* +***/ + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMalloc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMalloc.h new file mode 100644 index 00000000..d5bfb882 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMalloc.h @@ -0,0 +1,167 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/CoreLib/hsMalloc.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_CORELIB_HSMALLOC_H +#define PLASMA20_SOURCES_PLASMA_CORELIB_HSMALLOC_H + + +/**************************************************************************** +* +* Allocation functions +* +***/ + +#ifdef __cplusplus +extern "C" { +#endif + +// MemAlloc flags +extern const unsigned kMemReallocInPlaceOnly; // use _expand when realloc'ing +extern const unsigned kMemZero; // fill allocated memory with zeros +extern const unsigned kMemIgnoreBlock; // don't track this allocation + + +void * MemAlloc (unsigned bytes, unsigned flags, const char file[], int line); +void * MemDup (const void * ptr, unsigned bytes, unsigned flags, const char file[], int line); +void MemFree (void * ptr, unsigned flags); +void * MemRealloc (void * ptr, unsigned bytes, unsigned flags, const char file[], int line); +unsigned MemSize (void * ptr); + + +/**************************************************************************** +* +* Manipulation functions +* +***/ + +int MemCmp (const void * buf1, const void * buf2, unsigned bytes); +void MemCopy (void * dest, const void * source, unsigned bytes); +void MemMove (void * dest, const void * source, unsigned bytes); +void MemSet (void * dest, unsigned value, unsigned bytes); +void MemZero (void * dest, unsigned bytes); + + +/***************************************************************************** +* +* Debugging functions +* +***/ + +void MemDumpAllocReport (); +void MemDumpUsageReport (); +void MemValidateNow (); +void MemSetValidation (unsigned on); +void MemPushDisableTracking (); +void MemPopDisableTracking (); +void MemSetColor (unsigned short color); + + +#ifdef __cplusplus +} +#endif + + +/**************************************************************************** +* +* C++ Operators +* +***/ + +#ifdef __cplusplus + +// standard new and delete +inline void * __cdecl operator new (size_t bytes) { return MemAlloc((unsigned)bytes, 0, __FILE__, __LINE__); } +inline void __cdecl operator delete (void * ptr) { MemFree(ptr, 0); } + +// memcheck-friendly new +inline void * __cdecl operator new (size_t bytes, const char file[], unsigned line) { return MemAlloc((unsigned)bytes, 0, file, line); } +inline void __cdecl operator delete (void * ptr, const char [] , unsigned) { return MemFree(ptr, 0); } +#define TRACKED_NEW new(__FILE__, __LINE__) + + +// placement new +#ifndef __PLACEMENT_NEW_INLINE +#define __PLACEMENT_NEW_INLINE +inline void * __cdecl operator new (size_t, void * ptr) { return ptr; } +inline void __cdecl operator delete (void *, void *) {} +#endif // ifndef __PLACEMENT_NEW_INLINE + +#endif // ifdef __cplusplus + + +/**************************************************************************** +* +* Macros +* +***/ + +#define ALLOC(b) MemAlloc(b, 0, __FILE__, __LINE__) +#define ALLOCZERO(b) MemAlloc(b, kMemZero, __FILE__, __LINE__) +#define ALLOCFLAGS(b, f) MemAlloc(b, (f), __FILE__, __LINE__) +#define FREE(p) MemFree(p, 0) +#define FREEFLAGS(p, f) MemFree(p, (f)) +#define REALLOC(p, b) MemRealloc(p, b, 0, __FILE__, __LINE__) +#define REALLOCFLAGS(p, b, f) MemRealloc(p, b, (f), __FILE__, __LINE__) +#define CALLOC(n, s) MemAlloc((n)*(s), kMemZero, __FILE__, __LINE__) +#define MEMDUP(s, b) MemDup(s, b, 0, __FILE__, __LINE__) +#define ZERO(s) MemSet(&s, 0, sizeof(s)) +#define ZEROPTR(p) MemSet(p, 0, sizeof(*p)) +// Client must #include +#define ALLOCA(t, n) (t *)_alloca((n) * sizeof(t)) + + +#ifdef __cplusplus + +#define NEW(t) new(MemAlloc(sizeof(t), 0, __FILE__, __LINE__)) t +#define NEWFLAGS(t, f) new(MemAlloc(sizeof(t), (f), __FILE__, __LINE__)) t +#define NEWZERO(t) new(MemAlloc(sizeof(t), kMemZero, __FILE__, __LINE__)) t +#define DEL(t) delete (t) + +#endif // __cplusplus + + + +/**************************************************************************** +* +* TypeInfo +* (needed for memory leak reporting) +* +***/ + +#ifdef __cplusplus + +#if !defined(HS_NO_TYPEINFO) +#include +#endif + +#endif // ifdef __cplusplus + +#endif // PLASMA20_SOURCES_PLASMA_CORELIB_HSMALLOC_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix33.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix33.cpp new file mode 100644 index 00000000..648b8a51 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix33.cpp @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsMatrix33.h" +#include "hsStream.h" + +hsMatrix33* hsMatrix33::Reset() +{ + static const hsMatrix33 gIdentity = { 1, 0, 0, 0, 1, 0, 0, 0, 1 }; + + *this = gIdentity; + return this; +} + + +hsMatrix33* hsMatrix33::SetConcat(const hsMatrix33* a, const hsMatrix33* b) +{ + hsMatrix33 tmpMatrix; + hsMatrix33* c; + + c = this; + if (this == a || this == b) + c = &tmpMatrix; + + c->fMap[0][0] = a->fMap[0][0] * b->fMap[0][0] + a->fMap[0][1] * b->fMap[1][0] + a->fMap[0][2] * b->fMap[2][0]; + c->fMap[0][1] = a->fMap[0][0] * b->fMap[0][1] + a->fMap[0][1] * b->fMap[1][1] + a->fMap[0][2] * b->fMap[2][1]; + c->fMap[0][2] = a->fMap[0][0] * b->fMap[0][2] + a->fMap[0][1] * b->fMap[1][2] + a->fMap[0][2] * b->fMap[2][2]; + + c->fMap[1][0] = a->fMap[1][0] * b->fMap[0][0] + a->fMap[1][1] * b->fMap[1][0] + a->fMap[1][2] * b->fMap[2][0]; + c->fMap[1][1] = a->fMap[1][0] * b->fMap[0][1] + a->fMap[1][1] * b->fMap[1][1] + a->fMap[1][2] * b->fMap[2][1]; + c->fMap[1][2] = a->fMap[1][0] * b->fMap[0][2] + a->fMap[1][1] * b->fMap[1][2] + a->fMap[1][2] * b->fMap[2][2]; + + c->fMap[2][0] = a->fMap[2][0] * b->fMap[0][0] + a->fMap[2][1] * b->fMap[1][0] + a->fMap[2][2] * b->fMap[2][0]; + c->fMap[2][1] = a->fMap[2][0] * b->fMap[0][1] + a->fMap[2][1] * b->fMap[1][1] + a->fMap[2][2] * b->fMap[2][1]; + c->fMap[2][2] = a->fMap[2][0] * b->fMap[0][2] + a->fMap[2][1] * b->fMap[1][2] + a->fMap[2][2] * b->fMap[2][2]; + + if (this != c) + *this = *c; + return this; +} + +hsMatrix33 operator*(const hsMatrix33& a, const hsMatrix33& b) +{ + hsMatrix33 c; + + (void)c.SetConcat(&a, &b); + + return c; +} + +void hsMatrix33::Read(hsStream* s) +{ + int i, j; + for( i = 0; i < 3; i++ ) + { + for( j = 0; j < 3; j++ ) + { + fMap[i][j] = s->ReadSwapScalar(); + } + } +} + +void hsMatrix33::Write(hsStream* s) +{ + int i, j; + for( i = 0; i < 3; i++ ) + { + for( j = 0; j < 3; j++ ) + { + s->WriteSwapScalar(fMap[i][j]); + } + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix33.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix33.h new file mode 100644 index 00000000..934d194c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix33.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsMatrix33_inc +#define hsMatrix33_inc + +class hsStream; + +struct hsMatrix33 +{ + hsScalar fMap[3][3]; + + hsMatrix33* Reset(); + + int operator==(const hsMatrix33& aa) const + { + return aa.fMap[0][0] == fMap[0][0] && aa.fMap[0][1] == fMap[0][1] && aa.fMap[0][2] == fMap[0][2] && + aa.fMap[1][0] == fMap[1][0] && aa.fMap[1][1] == fMap[1][1] && aa.fMap[1][2] == fMap[1][2] && + aa.fMap[2][0] == fMap[2][0] && aa.fMap[2][1] == fMap[2][1] && aa.fMap[2][2] == fMap[2][2]; + } + int operator!=(const hsMatrix33& aa) const + { + return !(aa == *this); + } + hsMatrix33* SetConcat(const hsMatrix33* a, const hsMatrix33* b); + friend hsMatrix33 operator*(const hsMatrix33& a, const hsMatrix33& b); + + + void Read(hsStream* s); + void Write(hsStream* s); +}; + +#endif // hsMatrix33_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix44.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix44.cpp new file mode 100644 index 00000000..43edd3eb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix44.cpp @@ -0,0 +1,954 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "hsGeometry3.h" +#include "hsQuat.h" +#include "hsMatrix44.h" +#include "hsStream.h" + +static hsMatrix44 myIdent = hsMatrix44().Reset(); +const hsMatrix44& hsMatrix44::IdentityMatrix() { return myIdent; } + +/* + For the rotation: + ¦ 2 2 ¦ + ¦ 1 - (2Y + 2Z ) 2XY + 2ZW 2XZ - 2YW ¦ + ¦ ¦ + ¦ 2 2 ¦ + M = ¦ 2XY - 2ZW 1 - (2X + 2Z ) 2YZ + 2XW ¦ + ¦ ¦ + ¦ 2 2 ¦ + ¦ 2XZ + 2YW 2YZ - 2XW 1 - (2X + 2Y ) ¦ + ¦ ¦ + + The translation is far too complex to discuss here. ;^) +*/ +hsMatrix44::hsMatrix44(const hsScalarTriple &translate, const hsQuat &rotate) +{ + float xx = rotate.fX * rotate.fX; + float xy = rotate.fX * rotate.fY; + float xz = rotate.fX * rotate.fZ; + float xw = rotate.fX * rotate.fW; + + float yy = rotate.fY * rotate.fY; + float yz = rotate.fY * rotate.fZ; + float yw = rotate.fY * rotate.fW; + + float zz = rotate.fZ * rotate.fZ; + float zw = rotate.fZ * rotate.fW; + + fMap[0][0] = 1 - 2 * ( yy + zz ); fMap[0][1] = 2 * ( xy - zw ); fMap[0][2] = 2 * ( xz + yw ); fMap[0][3] = translate.fX; + fMap[1][0] = 2 * ( xy + zw ); fMap[1][1] = 1 - 2 * ( xx + zz ); fMap[1][2] = 2 * ( yz - xw ); fMap[1][3] = translate.fY; + fMap[2][0] = 2 * ( xz - yw ); fMap[2][1] = 2 * ( yz + xw ); fMap[2][2] = 1 - 2 * ( xx + yy ); fMap[2][3] = translate.fZ; + fMap[3][0] = fMap[3][1] = fMap[3][2] = 0.0f; + fMap[3][3] = 1.0f; + NotIdentity(); +} + +void hsMatrix44::DecompRigid(hsScalarTriple &translate, hsQuat &rotate) const +{ + translate = GetTranslate(); + rotate.QuatFromMatrix44(*this); +} + + + +#if 0 + +hsMatrix44& hsMatrix44::Reset() +{ + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + fMap[i][j] = (i==j) ? hsScalar1 : 0; + } + } + return *this; +} +#endif + +#if 0 // Havok reeks +hsMatrix44 operator*(const hsMatrix44& a, const hsMatrix44& b) +{ + hsMatrix44 c; + + if( a.fFlags & b.fFlags & hsMatrix44::kIsIdent ) + { + c.Reset(); + return c; + } + + if( a.fFlags & hsMatrix44::kIsIdent ) + return b; + if( b.fFlags & hsMatrix44::kIsIdent ) + return a; + +#if HS_BUILD_FOR_PS2 + MulMatrixVU0(a.fMap, b.fMap, c.fMap); +#else + c.fMap[0][0] = hsScalarMul(a.fMap[0][0], b.fMap[0][0]) + hsScalarMul(a.fMap[0][1], b.fMap[1][0]) + hsScalarMul(a.fMap[0][2], b.fMap[2][0]) + hsScalarMul(a.fMap[0][3], b.fMap[3][0]); + c.fMap[0][1] = hsScalarMul(a.fMap[0][0], b.fMap[0][1]) + hsScalarMul(a.fMap[0][1], b.fMap[1][1]) + hsScalarMul(a.fMap[0][2], b.fMap[2][1]) + hsScalarMul(a.fMap[0][3], b.fMap[3][1]); + c.fMap[0][2] = hsScalarMul(a.fMap[0][0], b.fMap[0][2]) + hsScalarMul(a.fMap[0][1], b.fMap[1][2]) + hsScalarMul(a.fMap[0][2], b.fMap[2][2]) + hsScalarMul(a.fMap[0][3], b.fMap[3][2]); + c.fMap[0][3] = hsScalarMul(a.fMap[0][0], b.fMap[0][3]) + hsScalarMul(a.fMap[0][1], b.fMap[1][3]) + hsScalarMul(a.fMap[0][2], b.fMap[2][3]) + hsScalarMul(a.fMap[0][3], b.fMap[3][3]); + + c.fMap[1][0] = hsScalarMul(a.fMap[1][0], b.fMap[0][0]) + hsScalarMul(a.fMap[1][1], b.fMap[1][0]) + hsScalarMul(a.fMap[1][2], b.fMap[2][0]) + hsScalarMul(a.fMap[1][3], b.fMap[3][0]); + c.fMap[1][1] = hsScalarMul(a.fMap[1][0], b.fMap[0][1]) + hsScalarMul(a.fMap[1][1], b.fMap[1][1]) + hsScalarMul(a.fMap[1][2], b.fMap[2][1]) + hsScalarMul(a.fMap[1][3], b.fMap[3][1]); + c.fMap[1][2] = hsScalarMul(a.fMap[1][0], b.fMap[0][2]) + hsScalarMul(a.fMap[1][1], b.fMap[1][2]) + hsScalarMul(a.fMap[1][2], b.fMap[2][2]) + hsScalarMul(a.fMap[1][3], b.fMap[3][2]); + c.fMap[1][3] = hsScalarMul(a.fMap[1][0], b.fMap[0][3]) + hsScalarMul(a.fMap[1][1], b.fMap[1][3]) + hsScalarMul(a.fMap[1][2], b.fMap[2][3]) + hsScalarMul(a.fMap[1][3], b.fMap[3][3]); + + c.fMap[2][0] = hsScalarMul(a.fMap[2][0], b.fMap[0][0]) + hsScalarMul(a.fMap[2][1], b.fMap[1][0]) + hsScalarMul(a.fMap[2][2], b.fMap[2][0]) + hsScalarMul(a.fMap[2][3], b.fMap[3][0]); + c.fMap[2][1] = hsScalarMul(a.fMap[2][0], b.fMap[0][1]) + hsScalarMul(a.fMap[2][1], b.fMap[1][1]) + hsScalarMul(a.fMap[2][2], b.fMap[2][1]) + hsScalarMul(a.fMap[2][3], b.fMap[3][1]); + c.fMap[2][2] = hsScalarMul(a.fMap[2][0], b.fMap[0][2]) + hsScalarMul(a.fMap[2][1], b.fMap[1][2]) + hsScalarMul(a.fMap[2][2], b.fMap[2][2]) + hsScalarMul(a.fMap[2][3], b.fMap[3][2]); + c.fMap[2][3] = hsScalarMul(a.fMap[2][0], b.fMap[0][3]) + hsScalarMul(a.fMap[2][1], b.fMap[1][3]) + hsScalarMul(a.fMap[2][2], b.fMap[2][3]) + hsScalarMul(a.fMap[2][3], b.fMap[3][3]); + + c.fMap[3][0] = hsScalarMul(a.fMap[3][0], b.fMap[0][0]) + hsScalarMul(a.fMap[3][1], b.fMap[1][0]) + hsScalarMul(a.fMap[3][2], b.fMap[2][0]) + hsScalarMul(a.fMap[3][3], b.fMap[3][0]); + c.fMap[3][1] = hsScalarMul(a.fMap[3][0], b.fMap[0][1]) + hsScalarMul(a.fMap[3][1], b.fMap[1][1]) + hsScalarMul(a.fMap[3][2], b.fMap[2][1]) + hsScalarMul(a.fMap[3][3], b.fMap[3][1]); + c.fMap[3][2] = hsScalarMul(a.fMap[3][0], b.fMap[0][2]) + hsScalarMul(a.fMap[3][1], b.fMap[1][2]) + hsScalarMul(a.fMap[3][2], b.fMap[2][2]) + hsScalarMul(a.fMap[3][3], b.fMap[3][2]); + c.fMap[3][3] = hsScalarMul(a.fMap[3][0], b.fMap[0][3]) + hsScalarMul(a.fMap[3][1], b.fMap[1][3]) + hsScalarMul(a.fMap[3][2], b.fMap[2][3]) + hsScalarMul(a.fMap[3][3], b.fMap[3][3]); +#endif + + return c; +} + +hsVector3 operator*(const hsMatrix44& m, const hsVector3& p) +{ + if( m.fFlags & hsMatrix44::kIsIdent ) + return p; + + hsVector3 rVal; + +#if HS_BUILD_FOR_PS2 + MulVectorVU0(m.fMap, (MATRIX4) &p, (MATRIX4) &rVal); +#else + rVal.fX = hsScalarMul(p.fX, m.fMap[0][0]) + hsScalarMul(p.fY, m.fMap[0][1]) + hsScalarMul(p.fZ, m.fMap[0][2]); + rVal.fY = hsScalarMul(p.fX, m.fMap[1][0]) + hsScalarMul(p.fY, m.fMap[1][1]) + hsScalarMul(p.fZ, m.fMap[1][2]); + rVal.fZ = hsScalarMul(p.fX, m.fMap[2][0]) + hsScalarMul(p.fY, m.fMap[2][1]) + hsScalarMul(p.fZ, m.fMap[2][2]); +#endif + + return rVal; +} +#else // Havok reeks +hsMatrix44 hsMatrix44::operator*(const hsMatrix44& b) const +{ + hsMatrix44 c; + + if( fFlags & b.fFlags & hsMatrix44::kIsIdent ) + { + c.Reset(); + return c; + } + + if( fFlags & hsMatrix44::kIsIdent ) + return b; + if( b.fFlags & hsMatrix44::kIsIdent ) + return *this; + +#if HS_BUILD_FOR_PS2 + MulMatrixVU0(fMap, b.fMap, c.fMap); +#else + c.fMap[0][0] = hsScalarMul(fMap[0][0], b.fMap[0][0]) + hsScalarMul(fMap[0][1], b.fMap[1][0]) + hsScalarMul(fMap[0][2], b.fMap[2][0]) + hsScalarMul(fMap[0][3], b.fMap[3][0]); + c.fMap[0][1] = hsScalarMul(fMap[0][0], b.fMap[0][1]) + hsScalarMul(fMap[0][1], b.fMap[1][1]) + hsScalarMul(fMap[0][2], b.fMap[2][1]) + hsScalarMul(fMap[0][3], b.fMap[3][1]); + c.fMap[0][2] = hsScalarMul(fMap[0][0], b.fMap[0][2]) + hsScalarMul(fMap[0][1], b.fMap[1][2]) + hsScalarMul(fMap[0][2], b.fMap[2][2]) + hsScalarMul(fMap[0][3], b.fMap[3][2]); + c.fMap[0][3] = hsScalarMul(fMap[0][0], b.fMap[0][3]) + hsScalarMul(fMap[0][1], b.fMap[1][3]) + hsScalarMul(fMap[0][2], b.fMap[2][3]) + hsScalarMul(fMap[0][3], b.fMap[3][3]); + + c.fMap[1][0] = hsScalarMul(fMap[1][0], b.fMap[0][0]) + hsScalarMul(fMap[1][1], b.fMap[1][0]) + hsScalarMul(fMap[1][2], b.fMap[2][0]) + hsScalarMul(fMap[1][3], b.fMap[3][0]); + c.fMap[1][1] = hsScalarMul(fMap[1][0], b.fMap[0][1]) + hsScalarMul(fMap[1][1], b.fMap[1][1]) + hsScalarMul(fMap[1][2], b.fMap[2][1]) + hsScalarMul(fMap[1][3], b.fMap[3][1]); + c.fMap[1][2] = hsScalarMul(fMap[1][0], b.fMap[0][2]) + hsScalarMul(fMap[1][1], b.fMap[1][2]) + hsScalarMul(fMap[1][2], b.fMap[2][2]) + hsScalarMul(fMap[1][3], b.fMap[3][2]); + c.fMap[1][3] = hsScalarMul(fMap[1][0], b.fMap[0][3]) + hsScalarMul(fMap[1][1], b.fMap[1][3]) + hsScalarMul(fMap[1][2], b.fMap[2][3]) + hsScalarMul(fMap[1][3], b.fMap[3][3]); + + c.fMap[2][0] = hsScalarMul(fMap[2][0], b.fMap[0][0]) + hsScalarMul(fMap[2][1], b.fMap[1][0]) + hsScalarMul(fMap[2][2], b.fMap[2][0]) + hsScalarMul(fMap[2][3], b.fMap[3][0]); + c.fMap[2][1] = hsScalarMul(fMap[2][0], b.fMap[0][1]) + hsScalarMul(fMap[2][1], b.fMap[1][1]) + hsScalarMul(fMap[2][2], b.fMap[2][1]) + hsScalarMul(fMap[2][3], b.fMap[3][1]); + c.fMap[2][2] = hsScalarMul(fMap[2][0], b.fMap[0][2]) + hsScalarMul(fMap[2][1], b.fMap[1][2]) + hsScalarMul(fMap[2][2], b.fMap[2][2]) + hsScalarMul(fMap[2][3], b.fMap[3][2]); + c.fMap[2][3] = hsScalarMul(fMap[2][0], b.fMap[0][3]) + hsScalarMul(fMap[2][1], b.fMap[1][3]) + hsScalarMul(fMap[2][2], b.fMap[2][3]) + hsScalarMul(fMap[2][3], b.fMap[3][3]); + + c.fMap[3][0] = hsScalarMul(fMap[3][0], b.fMap[0][0]) + hsScalarMul(fMap[3][1], b.fMap[1][0]) + hsScalarMul(fMap[3][2], b.fMap[2][0]) + hsScalarMul(fMap[3][3], b.fMap[3][0]); + c.fMap[3][1] = hsScalarMul(fMap[3][0], b.fMap[0][1]) + hsScalarMul(fMap[3][1], b.fMap[1][1]) + hsScalarMul(fMap[3][2], b.fMap[2][1]) + hsScalarMul(fMap[3][3], b.fMap[3][1]); + c.fMap[3][2] = hsScalarMul(fMap[3][0], b.fMap[0][2]) + hsScalarMul(fMap[3][1], b.fMap[1][2]) + hsScalarMul(fMap[3][2], b.fMap[2][2]) + hsScalarMul(fMap[3][3], b.fMap[3][2]); + c.fMap[3][3] = hsScalarMul(fMap[3][0], b.fMap[0][3]) + hsScalarMul(fMap[3][1], b.fMap[1][3]) + hsScalarMul(fMap[3][2], b.fMap[2][3]) + hsScalarMul(fMap[3][3], b.fMap[3][3]); +#endif + + return c; +} + +hsVector3 hsMatrix44::operator*(const hsVector3& p) const +{ + if( fFlags & hsMatrix44::kIsIdent ) + return p; + + hsVector3 rVal; + +#if HS_BUILD_FOR_PS2 + MulVectorVU0(fMap, (MATRIX4) &p, (MATRIX4) &rVal); +#else + rVal.fX = hsScalarMul(p.fX, fMap[0][0]) + hsScalarMul(p.fY, fMap[0][1]) + hsScalarMul(p.fZ, fMap[0][2]); + rVal.fY = hsScalarMul(p.fX, fMap[1][0]) + hsScalarMul(p.fY, fMap[1][1]) + hsScalarMul(p.fZ, fMap[1][2]); + rVal.fZ = hsScalarMul(p.fX, fMap[2][0]) + hsScalarMul(p.fY, fMap[2][1]) + hsScalarMul(p.fZ, fMap[2][2]); +#endif + + return rVal; +} +#endif // Havok reeks + +#if 0 // Havok reeks +int operator==(const hsMatrix44& s, const hsMatrix44& t) +{ + if( s.fFlags & t.fFlags & hsMatrix44::kIsIdent ) + { + return true; + } + + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + if (s.fMap[i][j] != t.fMap[i][j]) + return false; + } + } + + return true; +} +#else // Havok reeks +int hsMatrix44::operator==(const hsMatrix44& ss) const +{ + if( ss.fFlags & fFlags & hsMatrix44::kIsIdent ) + { + return true; + } + + for(int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + if (ss.fMap[i][j] != fMap[i][j]) + return false; + } + } + + return true; +} +#endif // Havok reeks + +hsMatrix44& hsMatrix44::Scale(const hsVector3* scale) +{ +#if HS_BUILD_FOR_PS2 + MulScaleVU0(fMap, (MATRIX4 *) scale); +#else + fMap[0][0] *= scale->fX; + fMap[0][1] *= scale->fX; + fMap[0][2] *= scale->fX; + fMap[0][3] *= scale->fX; + + fMap[1][0] *= scale->fY; + fMap[1][1] *= scale->fY; + fMap[1][2] *= scale->fY; + fMap[1][3] *= scale->fY; + + fMap[2][0] *= scale->fZ; + fMap[2][1] *= scale->fZ; + fMap[2][2] *= scale->fZ; + fMap[2][3] *= scale->fZ; +#endif + NotIdentity(); + return *this; +} + +hsVector3 hsMatrix44::RemoveScale() +{ + hsVector3 xCol(fMap[0][0], fMap[0][1], fMap[0][2]); + float xLen = xCol.Magnitude(); + hsVector3 yCol(fMap[1][0], fMap[1][1], fMap[1][2]); + float yLen = yCol.Magnitude(); + hsVector3 zCol(fMap[2][0], fMap[2][1], fMap[2][2]); + float zLen = zCol.Magnitude(); + + fMap[0][0] /= xLen; + fMap[0][1] /= xLen; + fMap[0][2] /= xLen; + fMap[1][0] /= yLen; + fMap[1][1] /= yLen; + fMap[1][2] /= yLen; + fMap[2][0] /= zLen; + fMap[2][1] /= zLen; + fMap[2][2] /= zLen; + + hsVector3 oldScale(xLen, yLen, zLen); + return oldScale; +} + +hsMatrix44& hsMatrix44::Translate(const hsVector3* pt) +{ +#if HS_BUILD_FOR_PS2 + TranslateVU0(fMap, (MATRIX4 *) pt); /* SUNSOFT */ +#else + for (int i =0; i < 3; i++) + { + fMap[i][3] += (*pt)[i]; + } +#endif + NotIdentity(); + return *this; +} +hsMatrix44& hsMatrix44::SetTranslate(const hsScalarTriple* pt) +{ + for (int i =0; i < 3; i++) + { + fMap[i][3] = (*pt)[i]; + } + NotIdentity(); + return *this; +} +hsMatrix44& hsMatrix44::MakeRotateMat(int axis, hsScalar radians) +{ + Reset(); + SetRotate(axis, radians); + NotIdentity(); + return *this; +} + +hsMatrix44& hsMatrix44::Rotate(int axis, hsScalar radians) +{ + hsMatrix44 rMat; + rMat.MakeRotateMat(axis, radians); + *this = rMat * *this; + return *this; +} + +hsMatrix44& hsMatrix44::SetRotate(int axis, hsScalar radians) +{ + hsScalar s = hsSine(radians); + hsScalar c = hsCosine(radians); + int c1,c2; + switch (axis) + { + case 0: + c1 = 1; + c2 = 2; + break; + case 1: + c1 = 0; + c2 = 2; + break; + case 2: + c1 = 0; + c2 = 1; + break; + } + fMap[c1][c1] = c; + fMap[c2][c2] = c; + fMap[c1][c2] = s; + fMap[c2][c1] = -s; + + NotIdentity(); + + return *this; +} + +void hsMatrix44::MakeXRotation(hsScalar radians) +{ + Reset(); + hsScalar s = hsSine(radians); + hsScalar c = hsCosine(radians); + + fMap[1][1] = c; + fMap[2][2] = c; + fMap[1][2] = s; + fMap[2][1] = -s; + NotIdentity(); +} + +void hsMatrix44::MakeYRotation(hsScalar radians) +{ + Reset(); + hsScalar s = hsSine(radians); + hsScalar c = hsCosine(radians); + fMap[0][0] = c; + fMap[2][2] = c; + fMap[0][2] = -s; + fMap[2][0] = s; + NotIdentity(); +} + +void hsMatrix44::MakeZRotation(hsScalar radians) +{ + Reset(); + hsScalar s = hsSine(radians); + hsScalar c = hsCosine(radians); + fMap[0][0] = c; + fMap[1][1] = c; + fMap[0][1] = s; + fMap[1][0] = -s; + NotIdentity(); +} + + +// +// Not a camera matrix +// +hsMatrix44& hsMatrix44::Make(const hsPoint3* f, const hsPoint3* at, const hsVector3* up) +{ + MakeTranslateMat(&hsVector3(f->fX, f->fY, f->fZ)); + + hsVector3 back (f,at); // Z + back.Normalize(); + + hsVector3 leftEar = *up % back; // X, LHS + leftEar.Normalize(); + +#if 1 + // Ignore actual up vector + hsVector3 topHead = leftEar % back; // Y, RHS (should be flipped) +#else + hsVector3 topHead = *up; +#endif + topHead.Normalize(); + + for(int i = 0; i < 3; i++) + { + fMap[i][0] = leftEar[i]; + fMap[i][1] = back[i]; + fMap[i][2] = topHead[i]; + } + NotIdentity(); + return *this; +} + +// +// Not a camera matrix +// +hsMatrix44& hsMatrix44::MakeUpPreserving(const hsPoint3* f, const hsPoint3* at, const hsVector3* up) +{ + MakeTranslateMat(&hsVector3(f->fX, f->fY, f->fZ)); + + hsVector3 topHead = *up; + topHead.Normalize(); + + hsVector3 back (f,at); // Z + back = -back; // really front + + hsVector3 leftEar = *up % back; // X + leftEar.Normalize(); + + back = (topHead % leftEar); + back.Normalize(); + + for(int i = 0; i < 3; i++) + { + fMap[i][0] = leftEar[i]; + fMap[i][1] = back[i]; + fMap[i][2] = topHead[i]; + } + NotIdentity(); + return *this; +} + +// +// Get info from a non-camera matrix. Vectors are normalized. +// +void hsMatrix44::GetAxis(hsVector3* view, hsVector3 *up, hsVector3* right) +{ + if (view) + view->Set(-fMap[0][1],-fMap[1][1],-fMap[2][1]); + if (right) + right->Set(-fMap[0][0],-fMap[1][0],-fMap[2][0]); + if (up) + up->Set(fMap[0][2], fMap[1][2], fMap[2][2]); +} + +const hsVector3 hsMatrix44::GetAxis(int i) const +{ + hsVector3 ret; + switch(i) + { + case kView: + { + ret.Set(-fMap[0][1],-fMap[1][1],-fMap[2][1]); + break; + } + case kUp: + { + ret.Set(fMap[0][2], fMap[1][2], fMap[2][2]); + break; + } + case kRight: + { + ret.Set(-fMap[0][0],-fMap[1][0],-fMap[2][0]); + break; + } + } + return ret; +} + + +// +// Camera matrix +// +hsMatrix44& hsMatrix44::MakeCamera(const hsPoint3* from, const hsPoint3* at, + const hsVector3* up) +{ + hsVector3 dirZ(at, from); + hsVector3 trans( -from->fX, -from->fY, -from->fZ ); + hsVector3 dirY, dirX; + hsMatrix44 rmat; + + dirX = (*up) % dirZ; // Stop passing in down!!! // mf_flip_up - mf + if (dirX.MagnitudeSquared()) + dirX.Normalize(); + + if (dirZ.MagnitudeSquared()) + dirZ.Normalize(); + dirY = dirZ % dirX; + if (dirY.MagnitudeSquared()) + dirY.Normalize(); + + this->Reset(); + this->Translate(&trans); + rmat.Reset(); + for(int i = 0; i < 3; i++) + { + rmat.fMap[0][i] = -dirX[i]; + rmat.fMap[1][i] = dirY[i]; + rmat.fMap[2][i] = dirZ[i]; + } + rmat.NotIdentity(); + *this = rmat * *this; + return *this; +} + +void hsMatrix44::MakeCameraMatrices(const hsPoint3& from, const hsPoint3& at, const hsVector3& up, hsMatrix44& worldToCamera, hsMatrix44& cameraToWorld) +{ + hsVector3 dirZ(&at, &from); + + hsVector3 dirX(dirZ % up); + dirX.Normalize(); + + hsVector3 dirY(dirX % dirZ); + dirY.Normalize(); + + worldToCamera.Reset(false); + cameraToWorld.Reset(false); + int i; + for( i = 0; i < 3; i++ ) + { + worldToCamera.fMap[0][i] = dirX[i]; + worldToCamera.fMap[1][i] = dirY[i]; + worldToCamera.fMap[2][i] = dirZ[i]; + + cameraToWorld.fMap[i][0] = dirX[i]; + cameraToWorld.fMap[i][1] = dirY[i]; + cameraToWorld.fMap[i][2] = dirZ[i]; + } + hsPoint3 trans = -from; + worldToCamera.fMap[0][3] = dirX.InnerProduct(trans); + worldToCamera.fMap[1][3] = dirY.InnerProduct(trans); + worldToCamera.fMap[2][3] = dirZ.InnerProduct(trans); + + cameraToWorld.fMap[0][3] = from.fX; + cameraToWorld.fMap[1][3] = from.fY; + cameraToWorld.fMap[2][3] = from.fZ; +} + +void hsMatrix44::MakeEnvMapMatrices(const hsPoint3& pos, hsMatrix44* worldToCameras, hsMatrix44* cameraToWorlds) +{ + MakeCameraMatrices(pos, hsPoint3(pos.fX - 1.f, pos.fY, pos.fZ), hsVector3(0, 0, 1.f), worldToCameras[0], cameraToWorlds[0]); + + MakeCameraMatrices(pos, hsPoint3(pos.fX + 1.f, pos.fY, pos.fZ), hsVector3(0, 0, 1.f), worldToCameras[1], cameraToWorlds[1]); + + MakeCameraMatrices(pos, hsPoint3(pos.fX, pos.fY + 1.f, pos.fZ), hsVector3(0, 0, 1.f), worldToCameras[2], cameraToWorlds[2]); + + MakeCameraMatrices(pos, hsPoint3(pos.fX, pos.fY - 1.f, pos.fZ), hsVector3(0, 0, 1.f), worldToCameras[3], cameraToWorlds[3]); + + MakeCameraMatrices(pos, hsPoint3(pos.fX, pos.fY, pos.fZ + 1.f), hsVector3(0, -1.f, 0), worldToCameras[4], cameraToWorlds[4]); + + MakeCameraMatrices(pos, hsPoint3(pos.fX, pos.fY, pos.fZ - 1.f), hsVector3(0, 1.f, 0), worldToCameras[5], cameraToWorlds[5]); +} + +// +// Vectors are normalized. +// +void hsMatrix44::GetAxisFromCamera(hsVector3* view, hsVector3 *up, hsVector3* right) +{ + if (view) + view->Set(fMap[2][0],fMap[2][1],fMap[2][2]); + if (right) + right->Set(fMap[0][0],fMap[0][1],fMap[0][2]); + if (up) + up->Set(fMap[1][0], fMap[1][1], fMap[1][2]); +} + +// +// Camera matrix +// +hsMatrix44& hsMatrix44::MakeCameraUpPreserving(const hsPoint3* from, const hsPoint3* at, + const hsVector3* up) +{ + hsVector3 dirZ(at, from); + hsVector3 trans( -from->fX, -from->fY, -from->fZ ); + hsVector3 dirY( up->fX, up->fY, up->fZ ); + hsVector3 dirX; + hsMatrix44 rmat; + + dirX = dirY % dirZ; + dirX.Normalize(); + + dirY.Normalize(); + dirZ = dirX % dirY; + dirZ.Normalize(); + + this->Reset(); + this->Translate(&trans); + rmat.Reset(); + for(int i = 0; i < 3; i++) + { + rmat.fMap[0][i] = -dirX[i]; + rmat.fMap[1][i] = dirY[i]; + rmat.fMap[2][i] = dirZ[i]; + } + rmat.NotIdentity(); + *this = rmat * *this; + return *this; +} + +/////////////////////////////////////////////////////// + +static hsScalar GetDeterminant33(const hsMatrix44* mat) +{ + return hsScalarMul(hsScalarMul(mat->fMap[0][0], mat->fMap[1][1]), mat->fMap[2][2]) + + hsScalarMul(hsScalarMul(mat->fMap[0][1], mat->fMap[1][2]), mat->fMap[2][0]) + + hsScalarMul(hsScalarMul(mat->fMap[0][2], mat->fMap[1][0]), mat->fMap[2][1]) - + hsScalarMul(hsScalarMul(mat->fMap[0][2], mat->fMap[1][1]), mat->fMap[2][0]) - + hsScalarMul(hsScalarMul(mat->fMap[0][1], mat->fMap[1][0]), mat->fMap[2][2]) - + hsScalarMul(hsScalarMul(mat->fMap[0][0], mat->fMap[1][2]), mat->fMap[2][1]); +} + +hsMatrix44* hsMatrix44::GetTranspose(hsMatrix44* transp) const +{ + for(int i = 0 ; i < 4; i++) + for(int j=0; j < 4; j++) + transp->fMap[i][j] = fMap[j][i]; + return transp; +} + + +static inline hsScalar Determinant2(hsScalar a, hsScalar b,hsScalar c, hsScalar d) +{ + return hsScalarMul(a,d) - hsScalarMul(c,b); +} + +static inline hsScalar Determinant3(hsScalar a, hsScalar b, hsScalar c, + hsScalar d, hsScalar e, hsScalar f, + hsScalar g, hsScalar h, hsScalar i) +{ + return hsScalarMul(a, Determinant2(e, f, h, i)) + - hsScalarMul(b, Determinant2(d, f, g, i)) + + hsScalarMul(c, Determinant2(d, e, g, h)); +} + +hsScalar hsMatrix44::GetDeterminant() const +{ +#if HS_BUILD_FOR_PS2 + return (GetDeterminantVU0(fMap)); +#else + return (fMap[0][0]*Determinant3(fMap[1][1], fMap[2][1], fMap[3][1], + fMap[1][2], fMap[2][2], fMap[3][2], + fMap[1][3], fMap[2][3], fMap[3][3]) - + fMap[1][0]*Determinant3(fMap[0][1], fMap[2][1], fMap[3][1], + fMap[0][2], fMap[2][2], fMap[3][2], + fMap[0][3], fMap[2][3], fMap[3][3]) + + fMap[2][0]*Determinant3(fMap[0][1], fMap[1][1], fMap[3][1], + fMap[0][2], fMap[1][2], fMap[3][2], + fMap[0][3], fMap[1][3], fMap[3][3]) - + fMap[3][0]*Determinant3(fMap[0][1], fMap[1][1], fMap[2][1], + fMap[0][2], fMap[1][2], fMap[2][2], + fMap[0][3], fMap[1][3], fMap[2][3])); +#endif +} + + +hsMatrix44 *hsMatrix44::GetAdjoint(hsMatrix44 *adj) const +{ +#if HS_BUILD_FOR_PS2 + GetAdjointVU0(fMap, adj->fMap); +#else + float a1, a2, a3, a4, b1, b2, b3, b4; + float c1, c2, c3, c4, d1, d2, d3, d4; +/* + * calculate the adjoint of a 4x4 matrix + * + * Let a denote the minor determinant of matrix A obtained by + * ij + * + * deleting the ith row and jth column from A. + * + * i+j + * Let b = (-1) a + * ij ji + * + * The matrix B = (b ) is the adjoint of A + * ij + */ + + /* assign to individual variable names to aid */ + /* selecting correct values */ + + a1 = fMap[0][0]; + b1 = fMap[0][1]; + c1 = fMap[0][2]; + d1 = fMap[0][3]; + + a2 = fMap[1][0]; + b2 = fMap[1][1]; + c2 = fMap[1][2]; + d2 = fMap[1][3]; + + a3 = fMap[2][0]; + b3 = fMap[2][1]; + c3 = fMap[2][2]; + d3 = fMap[2][3]; + + a4 = fMap[3][0]; + b4 = fMap[3][1]; + c4 = fMap[3][2]; + d4 = fMap[3][3]; + + /* + * row column labeling reversed since we transpose rows & columns + */ + + adj->fMap[0][0] = Determinant3(b2, b3, b4, c2, c3, c4, d2, d3, d4); + adj->fMap[1][0] = -Determinant3(a2, a3, a4, c2, c3, c4, d2, d3, d4); + adj->fMap[2][0] = Determinant3(a2, a3, a4, b2, b3, b4, d2, d3, d4); + adj->fMap[3][0] = -Determinant3(a2, a3, a4, b2, b3, b4, c2, c3, c4); + + adj->fMap[0][1] = -Determinant3(b1, b3, b4, c1, c3, c4, d1, d3, d4); + adj->fMap[1][1] = Determinant3(a1, a3, a4, c1, c3, c4, d1, d3, d4); + adj->fMap[2][1] = -Determinant3(a1, a3, a4, b1, b3, b4, d1, d3, d4); + adj->fMap[3][1] = Determinant3(a1, a3, a4, b1, b3, b4, c1, c3, c4); + + adj->fMap[0][2] = Determinant3(b1, b2, b4, c1, c2, c4, d1, d2, d4); + adj->fMap[1][2] = -Determinant3(a1, a2, a4, c1, c2, c4, d1, d2, d4); + adj->fMap[2][2] = Determinant3(a1, a2, a4, b1, b2, b4, d1, d2, d4); + adj->fMap[3][2] = -Determinant3(a1, a2, a4, b1, b2, b4, c1, c2, c4); + + adj->fMap[0][3] = -Determinant3(b1, b2, b3, c1, c2, c3, d1, d2, d3); + adj->fMap[1][3] = Determinant3(a1, a2, a3, c1, c2, c3, d1, d2, d3); + adj->fMap[2][3] = -Determinant3(a1, a2, a3, b1, b2, b3, d1, d2, d3); + adj->fMap[3][3] = Determinant3(a1, a2, a3, b1, b2, b3, c1, c2, c3); +#endif + adj->NotIdentity(); + return adj; +} + +hsMatrix44* hsMatrix44::GetInverse(hsMatrix44* inverse) const +{ + hsScalar det = GetDeterminant(); + int i,j; + + if (det == 0.0f) + { + inverse->Reset(); + return inverse; + } + + det = hsScalarInvert(det); + GetAdjoint(inverse); +#if HS_BUILD_FOR_PS2 + MatMulVU0(inverse->fMap, det); +#else + for (i=0; i<4; i++) + for (j=0; j<4; j++) + inverse->fMap[i][j] *= det; +#endif + return inverse; +} + +hsMatrix44& hsMatrix44::SetScale(const hsVector3* pt) +{ + for (int i =0; i < 3; i++) + { + fMap[i][i] = (*pt)[i]; + } + NotIdentity(); + return *this; +} + +hsMatrix44& hsMatrix44::MakeScaleMat(const hsVector3* pt) +{ + Reset(); + SetScale(pt); + return *this; +} + +hsMatrix44 &hsMatrix44::MakeTranslateMat(const hsVector3 *trans) +{ + Reset(); + SetTranslate(trans); + return *this; +} + +hsVector3* hsMatrix44::GetTranslate(hsVector3 *pt) const +{ + for (int i =0; i < 3; i++) + { + (*pt)[i] = fMap[i][3]; + } + return pt; +} + +const hsPoint3 hsMatrix44::GetTranslate() const +{ + hsPoint3 pt; + for (int i =0; i < 3; i++) + { + (pt)[i] = fMap[i][3]; + } + return pt; +} + + +hsPoint3* hsMatrix44::MapPoints(long count, hsPoint3 points[]) const +{ + if( !(fFlags & hsMatrix44::kIsIdent) ) + { + int i; + for(i = 0; i < count; i++) + { + points[i] = *this * points[i]; + } + } + return points; +} + +hsBool hsMatrix44::IsIdentity(void) +{ + hsBool retVal = true; + int i, j; + for( i = 0; i < 4; i++ ) + { + for( j = 0; j < 4; j++ ) + { +#if 0 // IDENTITY_CRISIS + if( i == j) + { + if (fMap[i][j] != hsScalar1) + { + NotIdentity(); + retVal = false; + } + } + else + { + if( fMap[i][j] != 0 ) + { + NotIdentity(); + retVal = false; + } + } +#else // IDENTITY_CRISIS + const hsScalar kEPS = 1.e-5f; + if( i == j) + { + if( (fMap[i][j] < hsScalar1-kEPS) || (fMap[i][j] > hsScalar1+kEPS) ) + { + NotIdentity(); + retVal = false; + } + else + { + fMap[i][j] = hsScalar1; + } + } + else + { + if( (fMap[i][j] < -kEPS) || (fMap[i][j] > kEPS) ) + { + NotIdentity(); + retVal = false; + } + else + { + fMap[i][j] = 0; + } + } +#endif // IDENTITY_CRISIS + } + } + if( retVal ) + fFlags |= kIsIdent; + return retVal; +} + +hsBool hsMatrix44::GetParity() const +{ + if( fFlags & kIsIdent ) + return false; + + hsVector3* rows[3]; + rows[0] = (hsVector3*)&fMap[0][0]; + rows[1] = (hsVector3*)&fMap[1][0]; + rows[2] = (hsVector3*)&fMap[2][0]; + + hsVector3 zeroXone = *rows[0] % *rows[1]; + return zeroXone.InnerProduct(rows[2]) < 0; +} + +void hsMatrix44::Read(hsStream *stream) +{ + if (stream->ReadBool()) + { + int i,j; + for(i=0; i<4; i++) + for(j=0; j<4; j++) +#if HS_SCALAR_IS_FIXED + fMap[i][j] = stream->ReadSwap32(); +#endif +#if HS_SCALAR_IS_FLOAT + fMap[i][j] = stream->ReadSwapFloat(); +#endif + IsIdentity(); + } + else + Reset(); +} + +void hsMatrix44::Write(hsStream *stream) +{ + hsBool ident = IsIdentity(); + stream->WriteBool(!ident); + if (!ident) + { + int i,j; + for(i=0; i<4; i++) + for(j=0; j<4; j++) +#if HS_SCALAR_IS_FIXED + stream->WriteSwap32(fMap[i][j]); +#endif +#if HS_SCALAR_IS_FLOAT + stream->WriteSwapFloat(fMap[i][j]); +#endif + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix44.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix44.h new file mode 100644 index 00000000..1584319f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMatrix44.h @@ -0,0 +1,170 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef HSMATRIX44_inc +#define HSMATRIX44_inc + +#include "hsTypes.h" +#include "hsGeometry3.h" + +class hsQuat; + +//////////////////////////////////////////////////////////////////////////// +class hsStream; +struct hsMatrix44 { + enum { + kIsIdent = 0x1 + }; + + enum { + kRight = 0, + kUp, + kView + }; + hsScalar fMap[4][4]; + UInt32 fFlags; + + hsMatrix44() : fFlags(0) {} + hsMatrix44(const hsScalarTriple &translate, const hsQuat &rotate); + + void DecompRigid(hsScalarTriple &translate, hsQuat &rotate) const; + + static const hsMatrix44& IdentityMatrix(); + + // worldToCameras and cameraToWorlds are arrays of 6 matrices. Returned are LEFT,RIGHT,FRONT,BACK,TOP,BOTTOM. + static void MakeEnvMapMatrices(const hsPoint3& pos, hsMatrix44* worldToCameras, hsMatrix44* cameraToWorlds); + static void MakeCameraMatrices(const hsPoint3& from, const hsPoint3& at, const hsVector3& up, hsMatrix44& worldToCamera, hsMatrix44& cameraToWorld); + + // Concat transform + hsMatrix44& Translate(const hsVector3 *); + hsMatrix44& Scale(const hsVector3 *); + hsMatrix44& Rotate(int axis, hsScalar radians); + + hsMatrix44& Reset(hsBool asIdent=true) + { + fMap[0][0] = 1.0f; fMap[0][1] = 0.0f; fMap[0][2] = 0.0f; fMap[0][3] = 0.0f; + fMap[1][0] = 0.0f; fMap[1][1] = 1.0f; fMap[1][2] = 0.0f; fMap[1][3] = 0.0f; + fMap[2][0] = 0.0f; fMap[2][1] = 0.0f; fMap[2][2] = 1.0f; fMap[2][3] = 0.0f; + fMap[3][0] = 0.0f; fMap[3][1] = 0.0f; fMap[3][2] = 0.0f; fMap[3][3] = 1.0f; + + fFlags = asIdent ? kIsIdent : 0; + return *this; + } + + // Create matrix from scratch + hsMatrix44& MakeTranslateMat(const hsVector3 *trans); + hsMatrix44& MakeScaleMat(const hsVector3 *scale); + hsMatrix44& MakeRotateMat(int axis, hsScalar radians); + hsMatrix44& Make(const hsPoint3* from, const hsPoint3* at, + const hsVector3* up); // Not a camera matrix + hsMatrix44& MakeUpPreserving(const hsPoint3* from, const hsPoint3* at, + const hsVector3* up); // Not a camera matrix + // Camera matrix + hsMatrix44& MakeCamera(const hsPoint3* from, const hsPoint3* at, + const hsVector3* up); + hsMatrix44& MakeCameraUpPreserving(const hsPoint3* from, const hsPoint3* at, + const hsVector3* up); + + hsBool GetParity() const; + hsScalar GetDeterminant() const; + hsMatrix44* GetInverse(hsMatrix44* inverse) const; + hsMatrix44* GetTranspose(hsMatrix44* inverse) const; + hsMatrix44* GetAdjoint(hsMatrix44* adjoint) const; + hsVector3* GetTranslate(hsVector3 *pt) const; + hsPoint3* GetTranslate(hsPoint3 *pt) const + { return (hsPoint3*)GetTranslate((hsVector3*)pt); } + const hsPoint3 GetTranslate() const; + void GetAxis(hsVector3* view, hsVector3 *up, hsVector3* right); + void GetAxisFromCamera(hsVector3* view, hsVector3 *up, hsVector3* right); + + const hsVector3 GetAxis(int i) const; + + // Change component of matrix + hsMatrix44& SetTranslate(const hsScalarTriple *); + hsMatrix44& SetScale(const hsVector3 *); + hsMatrix44& SetRotate(int axis, hsScalar radians); + + hsVector3 RemoveScale(); // returns old scale + void MakeXRotation(hsScalar radians); + void MakeYRotation(hsScalar radians); + void MakeZRotation(hsScalar radians); + + +#if 0 // Havok reeks + friend hsPoint3 operator*(const hsMatrix44& m, const hsPoint3& p) + { + if( m.fFlags & hsMatrix44::kIsIdent ) + return p; + + hsPoint3 rVal; + rVal.fX = hsScalarMul(p.fX, m.fMap[0][0]) + hsScalarMul(p.fY, m.fMap[0][1]) + hsScalarMul(p.fZ, m.fMap[0][2]) + m.fMap[0][3]; + rVal.fY = hsScalarMul(p.fX, m.fMap[1][0]) + hsScalarMul(p.fY, m.fMap[1][1]) + hsScalarMul(p.fZ, m.fMap[1][2]) + m.fMap[1][3]; + rVal.fZ = hsScalarMul(p.fX, m.fMap[2][0]) + hsScalarMul(p.fY, m.fMap[2][1]) + hsScalarMul(p.fZ, m.fMap[2][2]) + m.fMap[2][3]; + return rVal; + } + friend hsVector3 operator*(const hsMatrix44& m, const hsVector3& p); + friend hsMatrix44 operator*(const hsMatrix44& a, const hsMatrix44& b); +#else // Havok reeks + hsPoint3 operator*(const hsPoint3& p) const + { + if( fFlags & hsMatrix44::kIsIdent ) + return p; + + hsPoint3 rVal; + rVal.fX = hsScalarMul(p.fX, fMap[0][0]) + hsScalarMul(p.fY, fMap[0][1]) + hsScalarMul(p.fZ, fMap[0][2]) + fMap[0][3]; + rVal.fY = hsScalarMul(p.fX, fMap[1][0]) + hsScalarMul(p.fY, fMap[1][1]) + hsScalarMul(p.fZ, fMap[1][2]) + fMap[1][3]; + rVal.fZ = hsScalarMul(p.fX, fMap[2][0]) + hsScalarMul(p.fY, fMap[2][1]) + hsScalarMul(p.fZ, fMap[2][2]) + fMap[2][3]; + return rVal; + } + hsVector3 operator*(const hsVector3& p) const; + hsMatrix44 operator*(const hsMatrix44& b) const; +#endif // Havok reeks + + hsPoint3* MapPoints(long count, hsPoint3 points[]) const; + + hsBool IsIdentity(void); + void NotIdentity() { fFlags &= ~kIsIdent; } + +#if 0 // Havok reeks + friend int operator==(const hsMatrix44& s, const hsMatrix44& t); + friend int operator!=(const hsMatrix44& s, const hsMatrix44& t); +#else // Havok reeks + hsBool operator==(const hsMatrix44& ss) const; + hsBool operator!=(const hsMatrix44& ss) const { return !(ss == *this); } +#endif // Havok reeks + + void Read(hsStream *stream); + void Write(hsStream *stream); +} ATTRIBUTE_FOR_PS2; /* SUNSOFT */ + +#if 0 // Havok reeks +inline int operator!=(const hsMatrix44& s, const hsMatrix44& t) +{ + return (!(s==t)); +} +#endif // Havok reeks + +//////////////////////////////////////////////////////////////////////////// +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMemory.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMemory.cpp new file mode 100644 index 00000000..c8d490c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMemory.cpp @@ -0,0 +1,858 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#if HS_BUILD_FOR_MAC + #include +#else + #include +#endif + +#include "hsMemory.h" +#include "hsExceptions.h" + +#define DO_MEMORY_REPORTS // dumps memory reports upon start up of engine + +/////////////////////////////////////////////////////////////////////////////////////////// + +#if HS_BUILD_FOR_MAC + void HSMemory::BlockMove(const void* src, void* dst, UInt32 length) + { + ::BlockMoveData(src, dst, length); + } +#else + void HSMemory::BlockMove(const void* src, void* dst, UInt32 length) + { + memmove(dst, src, length); + } +#endif + +hsBool HSMemory::EqualBlocks(const void* block1, const void* block2, UInt32 length) +{ + const Byte* byte1 = (Byte*)block1; + const Byte* byte2 = (Byte*)block2; + + while (length--) + if (*byte1++ != *byte2++) + return false; + return true; +} + +void* HSMemory::New(UInt32 size) +{ + return TRACKED_NEW UInt32[(size + 3) >> 2]; +} + +void HSMemory::Delete(void* block) +{ + delete[] (UInt32*)block; +} + +void* HSMemory::Copy(UInt32 length, const void* source) +{ + void* destination = HSMemory::New(length); + + HSMemory::BlockMove(source, destination, length); + return destination; +} + +void HSMemory::Clear(void* m, UInt32 byteLen) +{ + UInt8* mem = (UInt8*)m; + UInt8* memStop = mem + byteLen; + + if (byteLen > 8) + { while (UInt32(mem) & 3) + *mem++ = 0; + + UInt32* mem32 = (UInt32*)mem; + UInt32* mem32Stop = (UInt32*)(UInt32(memStop) & ~3); + do { + *mem32++ = 0; + } while (mem32 < mem32Stop); + + mem = (UInt8*)mem32; + // fall through to finish any remaining bytes (0..3) + } + while (mem < memStop) + *mem++ = 0; + + hsAssert(mem == memStop, "oops"); +} + +////////////////////////////////////////////////////////////////////////////////////// + +#if 0 +template T* hsSoftNew(T*& obj) +{ + try { + obj = TRACKED_NEW T; + } + catch (...) { + obj = nil; + } + return obj; +} + +inline template T* hsSoftNew(T*& obj, unsigned count) +{ + try { + obj = TRACKED_NEW T[count]; + } + catch (...) { + obj = nil; + } + return obj; +} +#endif + +void* HSMemory::SoftNew(UInt32 size) +{ + UInt32* p; + + hsTry { + p = TRACKED_NEW UInt32[(size + 3) >> 2]; + } hsCatch(...) { + p = nil; + } + return p; +} + +////////////////////////////////////////////////////////////////////////////////////// + +struct hsPrivateChunk { + hsPrivateChunk* fNext; + char* fAvailableAddr; + UInt32 fAvailableSize; + + hsDebugCode(UInt32 fSize;) + hsDebugCode(UInt32 fCount;) + + static hsPrivateChunk* NewPrivateChunk(hsPrivateChunk* next, UInt32 chunkSize); +}; + +hsPrivateChunk* hsPrivateChunk::NewPrivateChunk(hsPrivateChunk* next, UInt32 chunkSize) +{ + hsPrivateChunk* chunk = (hsPrivateChunk*)HSMemory::New(sizeof(hsPrivateChunk) + chunkSize); + + chunk->fNext = next; + chunk->fAvailableAddr = (char*)chunk + sizeof(hsPrivateChunk); + chunk->fAvailableSize = chunkSize; + hsDebugCode(chunk->fSize = chunkSize;) + hsDebugCode(chunk->fCount = 0;) + + return chunk; +} + +hsChunkAllocator::hsChunkAllocator(UInt32 chunkSize) : fChunkSize(chunkSize), fChunk(nil) +{ + hsDebugCode(fChunkCount = 0;) +} + +hsChunkAllocator::~hsChunkAllocator() +{ + this->Reset(); +} + +void hsChunkAllocator::Reset() +{ + hsPrivateChunk* chunk = fChunk; + + while (chunk) + { hsPrivateChunk* next = chunk->fNext; + HSMemory::Delete(chunk); + chunk = next; + } + fChunk = nil; + hsDebugCode(fChunkCount = 0;) +} + +void hsChunkAllocator::SetChunkSize(UInt32 chunkSize) +{ + fChunkSize = chunkSize; +} + +void* hsChunkAllocator::Allocate(UInt32 size, const void* data) +{ + void* addr; + + if (fChunk == nil || fChunk->fAvailableSize < size) + { if (size > fChunkSize) + fChunkSize = size; + fChunk = hsPrivateChunk::NewPrivateChunk(fChunk, fChunkSize); + hsDebugCode(fChunkCount += 1;) + } + + addr = fChunk->fAvailableAddr; + fChunk->fAvailableAddr += size; + fChunk->fAvailableSize -= size; + hsDebugCode(fChunk->fCount += 1;) + + if (data) + HSMemory::BlockMove(data, addr, size); + + return addr; +} + +void* hsChunkAllocator::SoftAllocate(UInt32 size, const void* data) +{ + void* addr; + + hsTry { + addr = this->Allocate(size, data); + } + hsCatch(...) { + addr = nil; + } + return addr; +} + +////////////////////////////////////////////////////////////////////////////////////// + +struct hsAppenderHead { + struct hsAppenderHead* fNext; + struct hsAppenderHead* fPrev; + void* fFirst; + void* fStop; + void* fBottom; + + void* GetTop() const { return (char*)this + sizeof(*this); } + void* GetBottom() const { return fBottom; } + void* GetStop() const { return fStop; } + + void* GetFirst() const { return fFirst; } + void* GetLast(UInt32 elemSize) const { return (char*)fStop - elemSize; } + UInt32 GetSize() const { return (char*)fStop - (char*)fFirst; } + + hsBool CanPrepend() const { return fFirst != this->GetTop(); } + int PrependSize() const { return (char*)fFirst - (char*)this->GetTop(); } + hsBool CanAppend() const { return fStop != this->GetBottom(); } + int AppendSize() const { return (char*)this->GetBottom() - (char*)fStop; } + + void* Prepend(UInt32 elemSize) + { + hsAssert(this->CanPrepend(), "bad prepend"); + fFirst = (char*)fFirst - elemSize; + hsAssert((char*)fFirst >= (char*)this->GetTop(), "bad elemSize"); + return fFirst; + } + void* Append(UInt32 elemSize) + { + hsAssert(this->CanAppend(), "bad append"); + void* data = fStop; + fStop = (char*)fStop + elemSize; + hsAssert((char*)fStop <= (char*)fBottom, "bad elemSize"); + return data; + } + hsBool PopHead(UInt32 elemSize, void* data) + { + hsAssert(fFirst != fStop, "Empty"); + if( data ) + HSMemory::BlockMove(fFirst, data, elemSize); + fFirst = (char*)fFirst + elemSize; + return fFirst == fStop; + } + hsBool PopTail(UInt32 elemSize, void* data) + { + hsAssert(fFirst != fStop, "Empty"); + fStop = (char*)fStop - elemSize; + if( data ) + HSMemory::BlockMove(fStop, data, elemSize); + return fFirst == fStop; + } + + static hsAppenderHead* NewAppend(UInt32 elemSize, UInt32 elemCount, hsAppenderHead* prev) + { + UInt32 dataSize = elemSize * elemCount; + hsAppenderHead* head = (hsAppenderHead*)HSMemory::New(sizeof(hsAppenderHead) + dataSize); + + head->fNext = nil; + head->fPrev = prev; + head->fFirst = head->GetTop(); + head->fStop = head->fFirst; + head->fBottom = (char*)head->fFirst + dataSize; + return head; + } + static hsAppenderHead* NewPrepend(UInt32 elemSize, UInt32 elemCount, hsAppenderHead* next) + { + UInt32 dataSize = elemSize * elemCount; + hsAppenderHead* head = (hsAppenderHead*)HSMemory::New(sizeof(hsAppenderHead) + dataSize); + + head->fNext = next; + head->fPrev = nil; + head->fBottom = (char*)head->GetTop() + dataSize; + head->fFirst = head->fBottom; + head->fStop = head->fBottom; + return head; + } +}; + +//////////////////////////////////////////////////////////////////////////////////////// + +hsAppender::hsAppender(UInt32 elemSize, UInt32 elemCount) + : fFirstBlock(nil), fElemSize(elemSize), fElemCount(elemCount), fCount(0) +{ +} + +hsAppender::~hsAppender() +{ + this->Reset(); +} + +UInt32 hsAppender::CopyInto(void* data) const +{ + if (data) + { const hsAppenderHead* head = fFirstBlock; + hsDebugCode(UInt32 totalSize = 0;) + + while (head != nil) + { UInt32 size = head->GetSize(); + HSMemory::BlockMove(head->GetFirst(), data, size); + + data = (char*)data + size; + head = head->fNext; + hsDebugCode(totalSize += size;) + } + hsAssert(totalSize == fCount * fElemSize, "bad size"); + } + return fCount * fElemSize; +} + +void hsAppender::Reset() +{ + hsAppenderHead* head = fFirstBlock; + + while (head != nil) + { hsAppenderHead* next = head->fNext; + HSMemory::Delete(head); + head = next; + } + + fCount = 0; + fFirstBlock = nil; + fLastBlock = nil; +} + +void* hsAppender::PushHead() +{ + if (fFirstBlock == nil) + { fFirstBlock = hsAppenderHead::NewPrepend(fElemSize, fElemCount, nil); + fLastBlock = fFirstBlock; + } + else if (fFirstBlock->CanPrepend() == false) + fFirstBlock = hsAppenderHead::NewPrepend(fElemSize, fElemCount, fFirstBlock); + + fCount += 1; + return fFirstBlock->Prepend(fElemSize); +} + +void hsAppender::PushHead(const void* data) +{ + void* addr = this->PushHead(); + if (data) + HSMemory::BlockMove(data, addr, fElemSize); +} + +void* hsAppender::PeekHead() const +{ + if (fFirstBlock) + return (char*)fFirstBlock->fFirst; + else + return nil; +} + +hsBool hsAppender::PopHead(void* data) +{ + if (fCount == 0) + return false; + + fCount -= 1; + + if (fFirstBlock->PopHead(fElemSize, data)) + { hsAppenderHead* next = fFirstBlock->fNext; + if (next) + next->fPrev = nil; + HSMemory::Delete(fFirstBlock); + fFirstBlock = next; + if (next == nil) + fLastBlock = nil; + } + return true; +} + +int hsAppender::PopHead(int count, void* data) +{ + hsThrowIfBadParam(count >= 0); + + int sizeNeeded = count * fElemSize; + int origCount = fCount; + + while (fCount > 0) + { int size = fFirstBlock->GetSize(); + if (size > sizeNeeded) + size = sizeNeeded; + + if (fFirstBlock->PopHead(size, data)) + { hsAppenderHead* next = fFirstBlock->fNext; + if (next) + next->fPrev = nil; + HSMemory::Delete(fFirstBlock); + fFirstBlock = next; + if (next == nil) + fLastBlock = nil; + } + + if (data) + data = (void*)((char*)data + size); + sizeNeeded -= size; + fCount -= size / fElemSize; + hsAssert(int(fCount) >= 0, "bad fElemSize"); + } + return origCount - fCount; // return number of elements popped +} + +void* hsAppender::PushTail() +{ + if (fFirstBlock == nil) + { fFirstBlock = hsAppenderHead::NewAppend(fElemSize, fElemCount, nil); + fLastBlock = fFirstBlock; + } + else if (fLastBlock->CanAppend() == false) + { fLastBlock->fNext = hsAppenderHead::NewAppend(fElemSize, fElemCount, fLastBlock); + fLastBlock = fLastBlock->fNext; + } + + fCount += 1; + return fLastBlock->Append(fElemSize); +} + +void hsAppender::PushTail(const void* data) +{ + void* addr = this->PushTail(); + if (data) + HSMemory::BlockMove(data, addr, fElemSize); +} + +void hsAppender::PushTail(int count, const void* data) +{ + hsThrowIfBadParam(count < 0); + + int sizeNeeded = count * fElemSize; + + while (sizeNeeded > 0) + { if (fFirstBlock == nil) + { hsAssert(fCount == 0, "uninited count"); + fFirstBlock = hsAppenderHead::NewAppend(fElemSize, fElemCount, nil); + fLastBlock = fFirstBlock; + } + else if (fLastBlock->CanAppend() == false) + { fLastBlock->fNext = hsAppenderHead::NewAppend(fElemSize, fElemCount, fLastBlock); + fLastBlock = fLastBlock->fNext; + } + + int size = fLastBlock->AppendSize(); + hsAssert(size > 0, "bad appendsize"); + if (size > sizeNeeded) + size = sizeNeeded; + void* dst = fLastBlock->Append(size); + + if (data) + { HSMemory::BlockMove(data, dst, size); + data = (char*)data + size; + } + sizeNeeded -= size; + } + fCount += count; +} + +void* hsAppender::PeekTail() const +{ + if (fLastBlock) + return (char*)fLastBlock->fStop - fElemSize; + else + return nil; +} + +hsBool hsAppender::PopTail(void* data) +{ + if (fCount == 0) + return false; + + fCount -= 1; + + if (fLastBlock->PopTail(fElemSize, data)) + { hsAppenderHead* prev = fLastBlock->fPrev; + if (prev) + prev->fNext = nil; + HSMemory::Delete(fLastBlock); + fLastBlock = prev; + if (prev == nil) + fFirstBlock = nil; + } + return true; +} + +////////////////////////////////////////////////////////////////////////// + +hsAppenderIterator::hsAppenderIterator(const hsAppender* list) +{ + this->ResetToHead(list); +} + +void hsAppenderIterator::ResetToHead(const hsAppender* list) +{ + fAppender = list; + fCurrBlock = nil; + + if (fAppender) + { fCurrBlock = fAppender->fFirstBlock; + if (fCurrBlock) + fCurrItem = fCurrBlock->GetFirst(); + } +} + +void hsAppenderIterator::ResetToTail(const hsAppender* list) +{ + fAppender = list; + fCurrBlock = nil; + + if (fAppender) + { fCurrBlock = fAppender->fLastBlock; + if (fCurrBlock) + fCurrItem = fCurrBlock->GetLast(fAppender->fElemSize); + } +} + +void* hsAppenderIterator::Next() +{ + void* item = nil; + + if (fCurrBlock) + { item = fCurrItem; + fCurrItem = (char*)fCurrItem + fAppender->fElemSize; + if (fCurrItem == fCurrBlock->GetBottom()) + { fCurrBlock = fCurrBlock->fNext; + if (fCurrBlock) + fCurrItem = fCurrBlock->GetFirst(); + } + else if (fCurrItem == fCurrBlock->GetStop()) + { hsAssert(fCurrBlock->fNext == nil, "oops"); + fCurrBlock = nil; + } + } + return item; +} + +hsBool hsAppenderIterator::Next(void* data) +{ + void* addr = this->Next(); + if (addr) + { if (data) + HSMemory::BlockMove(addr, data, fAppender->fElemSize); + return true; + } + return false; +} + +int hsAppenderIterator::Next(int count, void* data) +{ + int origCount = count; + + while (count > 0 && this->Next(data)) + { if (data) + data = (void*)((char*)data + fAppender->fElemSize); + count -= 1; + } + return origCount - count; +} + +void* hsAppenderIterator::Prev() +{ + void* item = nil; + + if (fCurrBlock) + { item = fCurrItem; + fCurrItem = (char*)fCurrItem - fAppender->fElemSize; + if (item == fCurrBlock->GetTop()) + { fCurrBlock = fCurrBlock->fPrev; + if (fCurrBlock) + fCurrItem = fCurrBlock->GetLast(fAppender->fElemSize); + } + else if (item == fCurrBlock->GetFirst()) + { hsAssert(fCurrBlock->fPrev == nil, "oops"); + fCurrBlock = nil; + } + } + return item; +} + +hsBool hsAppenderIterator::Prev(void* data) +{ + void* addr = this->Prev(); + if (addr) + { if (data) + HSMemory::BlockMove(addr, data, fAppender->fElemSize); + return true; + } + return false; +} + +//------------------------------------------------------------- +// +// MEMORY USE REPORTING CODE +// +//------------------------------------------------------------- + +#if 1//!(defined(HS_DEBUGGING)&&(HS_BUILD_FOR_WIN32)&& defined(HS_FIND_MEM_LEAKS)) + // EMPTY STUB +void SortNDumpUnfreedMemory(const char *, bool ) // file name base, and FULL report indicator +{ +} + +#else + +typedef struct _CrtMemBlockHeader +{ +// Pointer to the block allocated just before this one: + struct _CrtMemBlockHeader *pBlockHeaderNext; +// Pointer to the block allocated just after this one: + struct _CrtMemBlockHeader *pBlockHeaderPrev; + char *szFileName; // File name + int nLine; // Line number + size_t nDataSize; // Size of user block + int nBlockUse; // Type of block + long lRequest; // Allocation number +// Buffer just before (lower than) the user's memory: + unsigned char gap[4]; +} _CrtMemBlockHeader; + +/* In an actual memory block in the debug heap, + * this structure is followed by: + * unsigned char data[nDataSize]; + * unsigned char anotherGap[4]; + */ + +// +// Dump formatted string to OutputDebugString +// +void __cdecl DebugMsg( LPSTR fmt, ... ) +{ + + char buff[256]; + wvsprintf(buff, fmt, (char *)(&fmt+1)); + hsStatusMessage(buff); + +} + +char *TrimFileName(char *name) // Trim file name of leading Directories +{ + int len = 0; + char *ptr; + if (!name) return NULL; + + len = strlen(name); + ptr = name + len; + for ( ptr--; ptr > name; ptr--) + { + if (*ptr == '\\') + { + ptr++; + break; + } + } + return ptr; +} + +// +// Loop thru all unfreed blocks in the heap and dump out detailed info +// + +struct looktbl { + char * fName; // Name of file +// long fAllocs; // Number of Alloc calls + long fBytes; // Total Bytes Alloc'd +}; +#define LTBLMAX 300 + +//--------------------------------------------------------------------------- +// This routine will report on the memory used in the engine. +// If argument full is true, it gives a full dump from the start of the program +// if !full, then each time the routine is called it remembers where it finishes off, then the next +// call with !full, it will (attempt) to report on the newest allocations, backward to the last checkpoint +//-------------------------------------------------------------------------- + +void SortNDumpUnfreedMemory(const char *nm, bool full) // file name base, and FULL report indicator +{ +#ifndef DO_MEMORY_REPORTS + if (!full) // full is launched by control M...partials are called each time the engine starts + return; +#endif + + char fname[512]; + sprintf(fname,"%s_dmp.txt",nm); + char *errStr = ""; + + + _CrtMemState heap_state; +static UInt32 GrandTotal =0; +static _CrtMemBlockHeader *cmbh_last; // Remember this header for next incremental check DANGER this + // could break if this is freed...(gives bad report) + _CrtMemBlockHeader *cmbh_last_good; + + _CrtMemBlockHeader *cmbh; + // Get Current heap state + + _CrtMemCheckpoint(&heap_state); + + cmbh = heap_state.pBlockHeader; + + long totsize= 0; // Track Total Bytes + long normsize = 0; // Track total of NORMAL Blocks + + looktbl *ltb = TRACKED_NEW looktbl[LTBLMAX]; + long tblEnd=1; // first is "NULL"; + + memset((void *)ltb,0,sizeof(looktbl) * LTBLMAX); // clear table area + + char *ftrim; + + + ltb[0].fName = "NULL"; // Use first Table Pos for NULL + + long tblpos; + while (cmbh != NULL) // Accumulate Stats to table + { + if (cmbh == cmbh_last && !full) // full indicates ignore last "checkpoint", stop at last checkpoint if !full + break; + cmbh_last_good = cmbh; + totsize += cmbh->nDataSize; + if (cmbh->nBlockUse == _NORMAL_BLOCK) + { + normsize += cmbh->nDataSize; + + if (cmbh->szFileName != NULL) // Shorten to just the file name, looks better, and strcmps faster + { + ftrim = TrimFileName(cmbh->szFileName); + for (tblpos = 1; tblpos < tblEnd; tblpos++) // find the name in the table + { + if (!strcmp(ftrim,ltb[tblpos].fName)) + break; // found it + } + } + else + { + tblpos = 0; // Use "NULL", first pos of table + } + + if (tblpos == tblEnd) // Did not find it...add it + { + tblEnd++; + if (tblEnd >= LTBLMAX) + { DebugMsg("DumpUnfreedMemoryInfo: EXCEED MAX TABLE LENGTH\n"); + tblEnd--; + break; + } + ltb[tblpos].fName = ftrim; // Add name + } + // Add Stats +// ltb[tblpos].fAllocs++; + ltb[tblpos].fBytes += cmbh->nDataSize; + + + } + cmbh = cmbh->pBlockHeaderNext; + + } + // This Code relies on the _CrtMemBlockHeader *cmbh_last_good for the "last" checkpoint to still be around... + // If the following occurs, that chunk has been deleted. we could fix this by allocating our own + // chunk and keeping it (watch for mem leaks though) or figuring out an "approximat" re syncying routine + // that works before we run thru collecting data. PBG + + if (cmbh_last && !full && cmbh == NULL) + { + //hsAssert(0,"Stats error: incremental mem check point has been deleted"); + errStr = "CHECK POINT ERROR, Results Inacurate"; + } + + if (normsize) // Don't write out about nothing + { + + CreateDirectory("Reports",NULL); // stick em in a sub directory + char fnm[512]; + sprintf(fnm,"Reports\\%s",fname); + + FILE * DumpLogFile = fopen( fnm, "w" ); +// long allocs=0; + if ( DumpLogFile != NULL ) + { + // Print Stats + fprintf(DumpLogFile, "Filename Total=%ld(k) %s\n",(normsize + 500)/1000,errStr); + for (int i = 0; i < tblEnd; i++) + { //fprintf(DumpLogFile,"%s\t%ld\n",ltb[i].fName, (ltb[i].fBytes+500)/1000);//,ltb[i].fAllocs); + fprintf(DumpLogFile,"%s ",ltb[i].fName); + int len = strlen(ltb[i].fName); + + for(int x=len; x < 25; x++) + fputc(' ',DumpLogFile); // make even columns + fprintf(DumpLogFile,"%5ld K\n",(UInt32)( ltb[i].fBytes+500)/1000);//,ltb[i].fAllocs); + + //allocs += ltb[i].fAllocs; + } + + DebugMsg("MEMORY USE FILE DUMPED TO %s \n",fname); + DebugMsg("MEMORY Check: Total size %ld, Normal Size: %ld\n",totsize,normsize); + + fclose(DumpLogFile); + } + static int first=1; + if (!full) // if this is a partial mem dump, write to the ROOMS.txt file a summary + { + sprintf(fnm,"Reports\\%s","ROOMS.txt"); + + if (first) + { DumpLogFile = fopen( fnm, "w" ); // first time clobber the old + if (DumpLogFile) + fprintf(DumpLogFile, "Filename Memory-Used(K) RunningTotal\n");// \tAllocation Calls \n" ); + first = 0; + } + else + DumpLogFile = fopen( fnm, "a+" ); + if( DumpLogFile) + { fprintf(DumpLogFile,"%s ",nm); + int len = strlen(nm); + GrandTotal += (UInt32)(normsize+500)/1000; + + for(int x=len; x < 25; x++) + fputc(' ',DumpLogFile); // make even columns + fprintf(DumpLogFile,"%5ld K %5ld %s\n",(UInt32)(normsize+500)/1000,GrandTotal,errStr);//, allocs); + fclose(DumpLogFile); + } + } + } + + + cmbh_last = heap_state.pBlockHeader; + delete ltb; +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMemory.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMemory.h new file mode 100644 index 00000000..d4cf0113 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsMemory.h @@ -0,0 +1,235 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsMemoryDefined +#define hsMemoryDefined + +#include "hsTypes.h" +//#include "hsTemplates.h" + +class HSMemory { +public: +#if HS_BUILD_FOR_MAC + static void BlockMove(const void* src, void* dst, UInt32 length); +#else + static void BlockMove(const void* src, void* dst, UInt32 length); +#endif + static void Clear(void *m, UInt32 byteLen); + static void ClearMemory(void *m, UInt32 byteLen) { HSMemory::Clear(m, byteLen); } + + static hsBool EqualBlocks(const void* block1, const void* block2, UInt32 length); + + static void* New(UInt32 size); + static void Delete(void* block); + static void* Copy(UInt32 length, const void* source); + + static void* SoftNew(UInt32 size); // returns nil if can't allocate +}; + +/////////////////////////////////////////////////////////////////////////////////////////// + +class hsAllocator { +public: + virtual void* Alloc(UInt32 size) = 0; + virtual void Free(void* addr) = 0; +}; + +class hsScratchMem { + enum { + kBufferSize = 32 + }; + UInt8* fMem; + UInt8 fMemBuffer[kBufferSize]; + UInt32 fLength; +public: + hsScratchMem() : fLength(kBufferSize) + { + fMem = fMemBuffer; + } + ~hsScratchMem() + { + if (fMem != fMemBuffer) + delete[] fMem; + } + UInt8* GetMem(UInt32 length) + { + if (length > fLength) + { if (fMem != fMemBuffer) + delete[] fMem; + fMem = TRACKED_NEW UInt8[length]; + fLength = length; + } + return fMem; + } +}; + +class hsChunkAllocator { + enum { + kDefaultChunkSize = 4096 + }; + UInt32 fChunkSize; + struct hsPrivateChunk* fChunk; + hsDebugCode(UInt32 fChunkCount;) +public: + hsChunkAllocator(UInt32 chunkSize = kDefaultChunkSize); + ~hsChunkAllocator(); + + void Reset(); + void SetChunkSize(UInt32 size); + void* Allocate(UInt32 size, const void* data = nil); // throws if fails + void* SoftAllocate(UInt32 size, const void* data = nil); // returns nil if fails +}; + +/////////////////////////////////////////////////////////////////////////////////////////// + +class hsAppender { + struct hsAppenderHead* fFirstBlock, *fLastBlock; + UInt32 fElemSize, fElemCount, fCount; + + friend class hsAppenderIterator; +public: + hsAppender(UInt32 elemSize, UInt32 minCount = 16); + ~hsAppender(); + + UInt32 ElemSize() const { return fElemSize; } + UInt32 Count() const { return fCount; } + hsBool IsEmpty() const { return fCount == 0; } + void Reset(); + + UInt32 CopyInto(void* data = nil) const; // return size of data array in bytes + + void* PushHead(); + void PushHead(const void* data); + void* PushTail(); + void PushTail(const void* data); + void PushTail(int count, const void* data); // data[] = count * fElemSize + void* PeekHead() const; + void* PeekTail() const; + hsBool PopHead(void* data = nil); + int PopHead(int count, void* data = nil); // data[] = count * fElemSize + hsBool PopTail(void* data = nil); + + // Alternate interfaces + + void* Prepend() { return this->PushHead(); } + void* Append() { return this->PushTail(); } + + void* Push() { return this->PushHead(); } + void Push(const void* data) { this->PushHead(data); } + hsBool Pop(void* data = nil) { return this->PopHead(data); } + + void* Enqueue() { return this->PushTail(); }; + void Enqueue(const void* data) { this->PushTail(data); } + void Enqueue(int count, const void* data) { this->PushTail(count, data); } + hsBool Dequeue(void* data = nil) { return this->PopHead(data); } + int Dequeue(int count, void* data = nil) { return this->PopHead(count, data); } +}; + +class hsAppenderIterator { + const hsAppender* fAppender; + const struct hsAppenderHead* fCurrBlock; + void* fCurrItem; +public: + hsAppenderIterator(const hsAppender* list = nil); + + void ResetToHead(const hsAppender* list = nil); + void ResetToTail(const hsAppender* list = nil); + void* Next(); + hsBool Next(void* data); + int Next(int count, void* data); + void* Prev(); + hsBool Prev(void* data); + + // Obsolete interface + + void Reset(const hsAppender* list = nil) { this->ResetToHead(list); } +}; + +/////////////////////////////////////////////////////////////////////////////// + +template class hsTAppender : hsAppender { +public: + hsTAppender() : hsAppender(sizeof(T)) {} + hsTAppender(UInt32 minCount) : hsAppender(sizeof(T), minCount) {} + + hsAppender* GetAppender() { return this; } + const hsAppender* GetAppender() const { return this; } + + UInt32 Count() const { return hsAppender::Count(); } + hsBool IsEmpty() const { return hsAppender::IsEmpty(); } + void Reset() { hsAppender::Reset(); } + + UInt32 CopyInto(T copy[]) const { return hsAppender::CopyInto(copy); } + + T* PushHead() { return (T*)hsAppender::PushHead(); } + void PushHead(const T& item) { *this->PushHead() = item; } + T* PushTail() { return (T*)hsAppender::PushTail(); } + void PushTail(const T& item) { *this->PushTail() = item; }; + void PushTail(int count, const T item[]) { this->hsAppender::PushTail(count, item); }; + T* PeekHead() const { return (T*)hsAppender::PeekHead(); } + T* PeekTail() const { return (T*)hsAppender::PeekTail(); } + hsBool PopHead(T* item = nil) { return hsAppender::PopHead(item); } + int PopHead(int count, T item[] = nil) { return hsAppender::PopHead(count, item); } + hsBool PopTail(T* item = nil) { return hsAppender::PopTail(item); } + + // Alternate intefaces + + T* Prepend() { return this->PushHead(); } + T* Append() { return this->PushTail(); } + void PrependItem(const T& item) { this->PushHead(item); } + void AppendItem(const T& item) { this->PushTail(item); } + + T* Push() { return this->PushHead(); } + void Push(const T& item) { this->PushHead(item); } + hsBool Pop(T* item = nil) { return this->PopHead(item); } + + T* Enqueue() { return this->PushTail(); }; + void Enqueue(const T& item) { this->PushTail(item); } + void Enqueue(int count, const T item[]) { this->PushTail(count, item); } + hsBool Dequeue(T* item = nil) { return this->PopHead(item); } + int Dequeue(int count, T item[] = nil) { return this->PopHead(count, item); } +}; + +template class hsTAppenderIterator : hsAppenderIterator { +public: + hsTAppenderIterator() : hsAppenderIterator() {} + hsTAppenderIterator(const hsTAppender* list) : hsAppenderIterator(list->GetAppender()) {} + + void ResetToHead() { hsAppenderIterator::ResetToHead(nil); } + void ResetToHead(const hsTAppender* list) { hsAppenderIterator::ResetToHead(list->GetAppender()); } + void ResetToTail() { hsAppenderIterator::ResetToTail(nil); } + void ResetToTail(const hsTAppender* list) { hsAppenderIterator::ResetToTail(list->GetAppender()); } + T* Next() { return (T*)hsAppenderIterator::Next(); } + hsBool Next(T* item) { return hsAppenderIterator::Next(item); } + T* Prev() { return (T*)hsAppenderIterator::Prev(); } + hsBool Prev(T* item) { return hsAppenderIterator::Prev(item); } + + // Obsolete interfaces + + void Reset() { this->ResetToHead(); } + void Reset(const hsTAppender* list) { this->ResetToHead(list); } +}; +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsPoint2.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsPoint2.h new file mode 100644 index 00000000..09a5dd20 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsPoint2.h @@ -0,0 +1,121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsPoint2_Defined +#define hsPoint2_Defined + +#include "hsScalar.h" + +#if __MWERKS__ + // This guy disables MetroWerks' desire to only include a file once, which obviously gets + // in the way of our little HS_POINT2.inc trick + #pragma once off +#endif + +#define HS_POINT2_NAME hsIntPoint2 +#define HS_POINT2_TYPE Int32 +#include "HS_POINT2.inc" +}; + +#define HS_POINT2_NAME hsFixedPoint2 +#define HS_POINT2_TYPE hsFixed +#include "HS_POINT2.inc" + + hsFixedPoint2& operator=(const hsIntPoint2& src) + { + this->fX = hsIntToFixed(src.fX); + this->fY = hsIntToFixed(src.fY); + return *this; + } + + hsFixed Magnitude() const { return hsMagnitude32(fX, fY); } + + static hsFixed Magnitude(hsFixed x, hsFixed y) + { + return hsMagnitude32(x, y); + } + static hsFixed Distance(const hsFixedPoint2& p1, const hsFixedPoint2& p2) + { + return hsMagnitude32(p2.fX - p1.fX, p2.fY - p1.fY); + } +}; + +#if HS_CAN_USE_FLOAT + struct hsPolar { + float fRadius; + float fAngle; + }; + + #define HS_POINT2_NAME hsFloatPoint2 + #define HS_POINT2_TYPE float + #include "HS_POINT2.inc" + + hsFloatPoint2& operator=(const hsIntPoint2& src) + { + this->fX = float(src.fX); + this->fY = float(src.fY); + return *this; + } + + friend hsFloatPoint2 operator*(const hsFloatPoint2& s, float t) + { + hsFloatPoint2 result; + result.Set(s.fX * t, s.fY * t); + return result; + } + friend hsFloatPoint2 operator*(float t, const hsFloatPoint2& s) + { + hsFloatPoint2 result; + result.Set(s.fX * t, s.fY * t); + return result; + } + + hsFloatPoint2* Grid(float period); + hsBool CloseEnough(const hsFloatPoint2* p, float tolerance) const; + + float Magnitude() const { return hsFloatPoint2::Magnitude(fX, fY); } + float MagnitudeSquared() const { return fX * fX + fY * fY; } + hsPolar* ToPolar(hsPolar* polar) const; + + static float Magnitude(float x, float y) { return hsSquareRoot(x * x + y * y); } + static hsScalar Distance(const hsFloatPoint2& p1, const hsFloatPoint2& p2); + static hsFloatPoint2 Average(const hsFloatPoint2& a, const hsFloatPoint2& b) + { + hsFloatPoint2 result; + result.Set((a.fX + b.fX) * float(0.5), (a.fY + b.fY) * float(0.5)); + return result; + } + static hsScalar ComputeAngle(const hsFloatPoint2& a, const hsFloatPoint2& b, const hsFloatPoint2& c); + }; +#endif + +#if HS_SCALAR_IS_FIXED + typedef hsFixedPoint2 hsPoint2; +#else + typedef hsFloatPoint2 hsPoint2; +#endif + +#endif // hsPoint2_Defined + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQuat.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQuat.cpp new file mode 100644 index 00000000..80eab938 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQuat.cpp @@ -0,0 +1,411 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "hsQuat.h" +#include "hsMatrix44.h" +#include "hsStream.h" +#include "hsFastMath.h" + +// +// Quaternion class. +// For conversion to and from euler angles, see hsEuler.cpp,h. +// + +// +// Construct quat from angle (in radians) and axis of rotation +// +hsQuat::hsQuat(hsScalar rad, const hsVector3* axis) +{ + // hsAssert(rad >= -hsScalarPI && rad <= hsScalarPI, "Quat: Angle should be between -PI and PI"); + + fW = hsCosine(rad*0.5f); + + hsScalar s = hsSine(rad*0.5f); + fX = axis->fX*s; + fY = axis->fY*s; + fZ = axis->fZ*s; +} + +hsQuat hsQuat::Inverse() +{ + hsQuat q2 = Conjugate(); + hsScalar msInv = 1.0f/q2.MagnitudeSquared(); + return (q2 * msInv); +} + +hsPoint3 hsQuat::Rotate(const hsScalarTriple* v) +{ + hsQuat qInv = Inverse(); + hsQuat qVec(v->fX, v->fY, v->fZ, 0); + hsQuat t = qInv * qVec; + hsQuat res = (t * (*this)); + //hsAssert(hsABS(res.fW)<1e-5, "Error rotating vector"); + return hsPoint3(res.fX, res.fY, res.fZ); +} + +void hsQuat::SetAngleAxis(const hsScalar rad, const hsVector3 &axis) +{ + fW = hsCosine(rad*0.5f); + + hsScalar s = hsSine(rad*0.5f); + fX = axis.fX*s; + fY = axis.fY*s; + fZ = axis.fZ*s; +} + +// +// Might want to Normalize before calling this +// +void hsQuat::GetAngleAxis(hsScalar *rad, hsVector3 *axis) const +{ + hsAssert((fW >= -1) && (fW <= 1), "Invalid acos argument"); + hsScalar ac = hsACosine(fW); + *rad = 2.0f * ac; + + hsScalar s = hsSine(ac); + if (s != 0.0f) + { + hsScalar invS = 1.0f/s; + axis->Set(fX*invS, fY*invS, fZ*invS); + } + else + axis->Set(0,0,0); +} + +// +// +// +hsScalar hsQuat::MagnitudeSquared() +{ + return (fX*fX + fY*fY + fZ*fZ + fW*fW); +} + +// +// +// +hsScalar hsQuat::Magnitude() +{ + return hsSquareRoot(MagnitudeSquared()); +} + +// +// +// +void hsQuat::Normalize() +{ + hsScalar invMag = 1.0f/Magnitude(); + fX *= invMag; + fY *= invMag; + fZ *= invMag; + fW *= invMag; +} + +// +// +// +void hsQuat::NormalizeIfNeeded() +{ + + hsScalar magSquared = MagnitudeSquared(); + if (magSquared == 1.0f) + return; + + hsScalar invMag = 1.0f/hsSquareRoot(magSquared); + fX *= invMag; + fY *= invMag; + fZ *= invMag; + fW *= invMag; +} + +// +// This is for a RHS. +// The quat should be normalized first. +// +void hsQuat::MakeMatrix(hsMatrix44 *mat) const +{ + // mf horse - this is transpose of both what + // Gems says and what i'm expecting to come + // out of it, so i'm flipping it. + mat->fMap[0][0] = 1.0f - 2.0f*fY*fY - 2.0f*fZ*fZ; + mat->fMap[0][1] = 2.0f*fX*fY - 2.0f*fW*fZ; + mat->fMap[0][2] = 2.0f*fX*fZ + 2.0f*fW*fY; + mat->fMap[0][3] = 0.0f; + + mat->fMap[1][0] = 2.0f*fX*fY + 2.0f*fW*fZ; + mat->fMap[1][1] = 1.0f - 2.0f*fX*fX - 2.0f*fZ*fZ; + mat->fMap[1][2] = 2.0f*fY*fZ - 2.0f*fW*fX; + mat->fMap[1][3] = 0.0f; + + mat->fMap[2][0] = 2.0f*fX*fZ - 2.0f*fW*fY; + mat->fMap[2][1] = 2.0f*fY*fZ + 2.0f*fW*fX; + mat->fMap[2][2] = 1.0f - 2.0f*fX*fX - 2.0f*fY*fY; + mat->fMap[2][3] = 0.0f; + + mat->fMap[3][0] = 0.0f; + mat->fMap[3][1] = 0.0f; + mat->fMap[3][2] = 0.0f; + mat->fMap[3][3] = 1.0f; + +#if 0 + mat->fMap[0][0] = fW*fW + fX*fX - fY*fY - fZ*fZ; + mat->fMap[1][0] = 2.0f*fX*fY - 2.0f*fW*fZ; + mat->fMap[2][0] = 2.0f*fX*fZ + 2.0f*fW*fY; + mat->fMap[3][0] = 0.0f; + + mat->fMap[0][1] = 2.0f*fX*fY + 2.0f*fW*fZ; + mat->fMap[1][1] = fW*fW - fX*fX + fY*fY - fZ*fZ; + mat->fMap[2][1] = 2.0f*fY*fZ - 2.0f*fW*fX; + mat->fMap[3][1] = 0.0f; + + mat->fMap[0][2] = 2.0f*fX*fZ - 2.0f*fW*fY; + mat->fMap[1][2] = 2.0f*fY*fZ + 2.0f*fW*fX; + mat->fMap[2][2] = fW*fW - fX*fX - fY*fY + fZ*fZ; + mat->fMap[3][2] = 0.0f; + + mat->fMap[0][3] = 0.0f; + mat->fMap[1][3] = 0.0f; + mat->fMap[2][3] = 0.0f; + mat->fMap[3][3] = fW*fW + fX*fX + fY*fY + fZ*fZ; +#endif + + mat->NotIdentity(); +} + +// Binary operators +hsQuat hsQuat::operator-(const hsQuat &in) const +{ + return hsQuat(fX-in.fX, fY-in.fY, fZ-in.fZ, fW-in.fW); +} + +hsQuat hsQuat::operator+(const hsQuat &in) const +{ + return hsQuat(fX+in.fX, fY+in.fY, fZ+in.fZ, fW+in.fW); +} + +// +// Return quaternion product (this * in). Note: order is important! +// To combine rotations, use the product (qSecond * qFirst), +// which gives the effect of rotating by qFirst then qSecond. +// +hsQuat hsQuat::operator*(const hsQuat &in) const +{ + hsQuat ret; + ret.fW = (fW*in.fW - fX*in.fX - fY*in.fY - fZ*in.fZ); + ret.fX = (fY*in.fZ - in.fY*fZ + fW*in.fX + in.fW*fX); + ret.fY = (fZ*in.fX - in.fZ*fX + fW*in.fY + in.fW*fY); + ret.fZ = (fX*in.fY - in.fX*fY + fW*in.fZ + in.fW*fZ); + return ret; +} + + +// I/O +void hsQuat::Read(hsStream *stream) +{ + fX = stream->ReadSwapFloat(); + fY = stream->ReadSwapFloat(); + fZ = stream->ReadSwapFloat(); + fW = stream->ReadSwapFloat(); +} + +void hsQuat::Write(hsStream *stream) +{ + stream->WriteSwapFloat(fX); + stream->WriteSwapFloat(fY); + stream->WriteSwapFloat(fZ); + stream->WriteSwapFloat(fW); +} + + +#if 0 +// +// Interpolate on a sphere. +// +void hsQuat::SetFromSlerp(hsQuat *q1, hsQuat *q2, hsScalar t) +{ + hsAssert(t>=0.0 && t<= 1.0, "Quat slerp param must be between 0 an 1"); + hsScalar theta = hsACosine(q1->Dot(*q2)); + + hsScalar st = hsSine(theta); + assert(st != 0.0); + + hsScalar s1 = hsSine(1.0-t)*theta / st; + + hsScalar s2 = hsSine(t)*theta / st; + + *this = (*q1) * s1 + (*q2) * s2; +} +#else +/* + * Spherical linear interpolation of unit quaternions with spins + */ + +#define EPSILON 1.0E-6 /* a tiny number */ + +void hsQuat::SetFromSlerp(const hsQuat &a, const hsQuat &b, hsScalar alpha, int spin) +// double alpha; /* interpolation parameter (0 to 1) */ +// Quaternion *a, *b; /* start and end unit quaternions */ +// int spin; /* number of extra spin rotations */ +{ + hsScalar beta; /* complementary interp parameter */ + hsScalar theta; /* angle between A and B */ + hsScalar sin_t, cos_t; /* sine, cosine of theta */ + hsScalar phi; /* theta plus spins */ + int bflip; /* use negation of B? */ + + /* cosine theta = dot product of A and B */ + cos_t = a.Dot(b); + + /* if B is on opposite hemisphere from A, use -B instead */ + if (cos_t < 0.0) + { + cos_t = -cos_t; + bflip = true; + } + else + bflip = false; + + /* if B is (within precision limits) the same as A, + * just linear interpolate between A and B. + * Can't do spins, since we don't know what direction to spin. + */ + if (1.0 - cos_t < EPSILON) + { + beta = 1.0f - alpha; + } else + { /* normal case */ +// hsAssert((cos_t >= -1) && (cos_t <= 1), "Invalid acos argument"); + theta = hsACosine(cos_t); + phi = theta + spin * hsScalarPI; + sin_t = hsSine(theta); + hsAssert(sin_t != 0.0, "Invalid sin value in quat slerp"); + beta = hsSine(theta - alpha*phi) / sin_t; + alpha = hsSine(alpha*phi) / sin_t; + } + + if (bflip) + alpha = -alpha; + + /* interpolate */ + fX = beta*a.fX + alpha*b.fX; + fY = beta*a.fY + alpha*b.fY; + fZ = beta*a.fZ + alpha*b.fZ; + fW = beta*a.fW + alpha*b.fW; +} +#endif + +// +// +// +void hsQuat::SetFromMatrix(const hsMatrix44* mat) +{ + hsScalar wSq = 0.25f*(1 + mat->fMap[0][0] + mat->fMap[1][1] + mat->fMap[2][2]); + if (wSq > EPSILON) + { + fW = hsSquareRoot(wSq); + hsScalar iw4 = 1.0f/(4.0f*fW); + fX = (mat->fMap[2][1] - mat->fMap[1][2]) * iw4; + fY = (mat->fMap[0][2] - mat->fMap[2][0]) * iw4; + fZ = (mat->fMap[1][0] - mat->fMap[0][1]) * iw4; + return; + } + + fW = 0; + hsScalar xSq = -0.5f*(mat->fMap[1][1] + mat->fMap[2][2]); + if (xSq > EPSILON) + { + fX = hsSquareRoot(xSq); + hsScalar ix2 = 1.0f/(2.0f*fX); + fY = mat->fMap[1][0] * ix2; + fZ = mat->fMap[2][0] * ix2; + return; + } + + fX = 0; + hsScalar ySq = 0.5f * (1 - mat->fMap[2][2]); + if (ySq > EPSILON) + { + fY = hsSquareRoot(ySq); + fZ = mat->fMap[2][1] / (2.0f*fY); + return; + } + + fY = 0; + fZ = 1; +} + +// 9/15/03 - Colin +// Changed to not use hsFastMath::InvSqrt, due to errors occuring at some +// specific angles that caused Havok to blow up. +hsQuat hsQuat::QuatFromMatrix44(const hsMatrix44& mat) +{ + /* This algorithm avoids near-zero divides by looking for a large component + * - first w, then x, y, or z. When the trace is greater than zero, + * |w| is greater than 1/2, which is as small as a largest component can be. + * Otherwise, the largest diagonal entry corresponds to the largest of |x|, + * |y|, or |z|, one of which must be larger than |w|, and at least 1/2. */ + hsQuat qu; + float tr, s; + + const int X = 0; + const int Y = 1; + const int Z = 2; + const int W = 3; + tr = mat.fMap[X][X] + mat.fMap[Y][Y]+ mat.fMap[Z][Z]; + if (tr >= 0.0) { + s = float(sqrt(tr + 1.f)); + qu.fW = 0.5f * s; + s = 0.5f / s; + qu.fX = (mat.fMap[Z][Y] - mat.fMap[Y][Z]) * s; + qu.fY = (mat.fMap[X][Z] - mat.fMap[Z][X]) * s; + qu.fZ = (mat.fMap[Y][X] - mat.fMap[X][Y]) * s; + } else { + int h = X; + if (mat.fMap[Y][Y] > mat.fMap[X][X]) + h = Y; + if (mat.fMap[Z][Z] > mat.fMap[h][h]) + h = Z; + switch (h) { +#define caseMacro(i,j,k,I,J,K) \ + case I:\ + s = float(sqrt( (mat.fMap[I][I] - (mat.fMap[J][J]+mat.fMap[K][K])) + 1.f )); \ + qu.i = 0.5f * s; \ + s = 0.5f / s; \ + qu.j = (mat.fMap[I][J] + mat.fMap[J][I]) * s; \ + qu.k = (mat.fMap[K][I] + mat.fMap[I][K]) * s; \ + qu.fW = (mat.fMap[K][J] - mat.fMap[J][K]) * s; \ + break + caseMacro(fX,fY,fZ,X,Y,Z); + caseMacro(fY,fZ,fX,Y,Z,X); + caseMacro(fZ,fX,fY,Z,X,Y); + } + } + return (qu); +} + +hsQuat& hsQuat::SetFromMatrix44(const hsMatrix44& mat) +{ + return (*this = QuatFromMatrix44(mat)); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQuat.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQuat.h new file mode 100644 index 00000000..6208735b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQuat.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef HSQUAT_inc +#define HSQUAT_inc + +#include "hsGeometry3.h" + +struct hsMatrix44; + +// +// Quaternion class. +// For conversion to and from euler angles, see hsEuler.cpp,h. +// +class hsQuat { + +public: + hsScalar fX,fY,fZ,fW; + + // Constructors + hsQuat(){} + hsQuat(hsScalar X, hsScalar Y, hsScalar Z, hsScalar W) : + fX(X), fY(Y), fZ(Z), fW(W) {} + hsQuat(const hsQuat& a) { fX = a.fX; fY = a.fY; fZ = a.fZ; fW = a.fW; } + hsQuat(hsScalar af[4]) { fX = af[0]; fY = af[1]; fZ = af[2]; fW = af[3]; } + hsQuat(hsScalar rad, const hsVector3* axis); + + static hsQuat QuatFromMatrix44(const hsMatrix44& mat); + hsQuat& SetFromMatrix44(const hsMatrix44& mat); + void SetFromMatrix(const hsMatrix44 *mat); + void SetFromSlerp(const hsQuat &q1, const hsQuat &q2, hsScalar t, int spin=0); + void Set(hsScalar X, hsScalar Y, hsScalar Z, hsScalar W) + { fX = X; fY = Y; fZ = Z; fW = W; } + void GetAngleAxis(hsScalar *rad, hsVector3 *axis) const; + void SetAngleAxis(const hsScalar rad, const hsVector3 &axis); + hsPoint3 Rotate(const hsScalarTriple* v); + + // Access operators + hsScalar& operator[](int i) { return (&fX)[i]; } + const hsScalar& operator[](int i) const { return (&fX)[i]; } + + // Unary operators + hsQuat operator-() const { return(hsQuat(-fX,-fY,-fZ,-fW)); } + hsQuat operator+() const { return *this; } + + // Comparison + int operator==(const hsQuat& a) const + { return (fX==a.fX && fY==a.fY && fZ==a.fZ && fW==a.fW); } + void Identity() { fX = fY = fZ = (hsScalar)0.0; fW = (hsScalar) 1.0; } + int IsIdentity() const + { return (fX==0.0 && fY==0.0 && fZ==0.0 && fW==1.0); } + void Normalize(); + void NormalizeIfNeeded(); + void MakeMatrix(hsMatrix44 *mat) const; + hsScalar Magnitude(); + hsScalar MagnitudeSquared(); + hsQuat Conjugate() const + { return hsQuat(-fX,-fY,-fZ,fW); } + hsQuat Inverse(); + // Binary operators + hsQuat operator-(const hsQuat&) const; + hsQuat operator+(const hsQuat&) const; + hsQuat operator*(const hsQuat&) const; + hsQuat operator*(hsScalar f) const + { return hsQuat(fX*f,fY*f,fZ*f,fW*f); } + hsQuat operator/(hsScalar f) const + { return hsQuat(fX/f,fY/f,fZ/f,fW/f); } + hsQuat operator/(const hsQuat&) const; + + hsScalar Dot(const hsQuat &q2) const + { return (fX*q2.fX + fY*q2.fY + fZ*q2.fZ + fW*q2.fW); } + + // I/O + void Read(hsStream *stream); + void Write(hsStream *stream); + + }; +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQueue.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQueue.h new file mode 100644 index 00000000..543e63d7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsQueue.h @@ -0,0 +1,353 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsQueue_Defined +#define hsQueue_Defined + +#include "hsTypes.h" + +template class hsQueue { +private: + + int fArraySize; + T *fArray; + int fHead; // Index of first element in the queue + int fTail; // Index of next free spot in the queue + int fLook; // Index of look pointer + hsBool fFull; // Is queue full? + hsBool fEmpty; // Is queue empty? + + void Inc(int *index); + int Inc(int index); + + void Dec(int *index); + int Dec(int index); + +public: + hsQueue( int size ); + ~hsQueue(); + hsBool Append(const T &newTail); // Add to end of line + hsBool Remove(const T &someElement); // Find and remove element in the line + hsBool Pop(T *headElement); // Remove and return the head of the line + hsBool StartLook(T *headElement); // Return the head of the line w/out removing it + hsBool NextLook(T *nextElement); // Return the head of the line w/out removing it + hsBool IsEmpty(void) { return fEmpty; } + hsBool IsFull(void) { return fFull; } +}; + +// +// Constructor +// Allocate array, init head/tail indices +// +template hsQueue::hsQueue( int size ) +{ + fArraySize = size; + fArray = TRACKED_NEW T[ size ]; + fHead = -1; + fTail = -1; + fLook = -1; + fEmpty = true; + fFull = false; +} + +// +// Destructor. free array +// +template hsQueue::~hsQueue() +{ + delete [] fArray; +} + +// +// Wrap index on increment +// +template void hsQueue::Inc( int *index ) +{ + (*index) ++; + if ((*index) == fArraySize) { + *index = 0; + } +} + +// +// Wrap index on increment +// +template int hsQueue::Inc( int index ) +{ + (index) ++; + if ((index) == fArraySize) { + index = 0; + } + return index; +} + +// +// Wrap index on decrement +// +template void hsQueue::Dec( int *index ) +{ + (*index) --; + if ((*index) < 0) { + *index = fArraySize-1; + } +} + +// +// Wrap index on decrement +// +template int hsQueue::Dec( int index ) +{ + (index) --; + if ((index) < 0) { + index = fArraySize-1; + } + return index; +} + +// +// Add copy of item to the array. +// +template hsBool hsQueue::Append(const T &thing) +{ + if (fHead == -1 && fTail == -1) { + // init case + fHead = 0; + fTail = 0; + } + + if (fFull) { + // Queue is full + return false; + } + + if ( (fHead<0 || fHead>=fArraySize) ) { + hsIfDebugMessage( (fHead<0 || fHead>=fArraySize), "Append: Illegal head pointer", fHead); + } + + hsIfDebugMessage( (fTail<0 || fTail>=fArraySize), "Append: Illegal tail pointer", fTail); + + // Copy + fArray[fTail] = thing; + fEmpty = false; + + // increment tail pointer + Inc(&fTail); + if (fTail == fHead) { + fFull = true; + } + + return true; +} + +// +// Get a copy of the head of the array +// +template hsBool hsQueue::Pop(T *thing) +{ + if (fEmpty) { + return false; + } + + hsIfDebugMessage( (fHead<0 || fHead>=fArraySize), "Pop: Illegal head pointer", fHead); + hsIfDebugMessage( (fTail<0 || fTail>=fArraySize), "Pop: Illegal tail pointer", fTail); + + // Copy + *thing = fArray[fHead]; + fFull = false; + + // Increment head pointer + Inc(&fHead); + if (fHead == fTail) { + fEmpty = true; + } + + return true; +} + +// +// Remove item from list +// +template hsBool hsQueue::Remove(const T &thing) +{ + if (fEmpty) { + return false; + } + + hsIfDebugMessage( (fHead<0 || fHead>=fArraySize), "Remove: Illegal head pointer", fHead); + hsIfDebugMessage( (fTail<0 || fTail>=fArraySize), "Remove: Illegal tail pointer", fTail); + + // loop through list, find item + int i = fHead; + do { + if (fArray[i] == thing) { + // Found it - now remove it by sliding everything down 1 + int j=Inc(i); + while(j!= fTail) { + if (fLook==j) + Dec(&fLook); + fArray[Dec(j)] = fArray[j]; + Inc(&j); + } + if (fLook==fTail) + Dec(&fLook); + Dec(&fTail); + if (fTail == fHead) { + fEmpty = true; + } + return true; + } + + Inc(&i); + if (i==fTail) { + return false; + } + + } while(true); +} + +// +// Return pointer to first item in list, without popping it. +// Return false if nothing there. +// +template hsBool hsQueue::StartLook(T *thing) +{ + if (fEmpty) { + return false; + } + + hsIfDebugMessage( (fHead<0 || fHead>=fArraySize), "StartLook: Illegal head pointer", fHead); + hsIfDebugMessage( (fTail<0 || fTail>=fArraySize), "StartLook: Illegal tail pointer", fTail); + + fLook = fHead; + *thing = fArray[fLook]; + + // inc look pointer + Inc(&fLook); + + // success + return true; +} + +// +// Return pointer to next item in list, without popping it. Doesn't change head or tail. +// Should be called immediately after StartLook. +// Return false when at end of list. +// +template hsBool hsQueue::NextLook(T *thing) +{ + if (fEmpty || fLook == fTail) { + return false; + } + + hsAssert(fLook != -1, "Must call StartLook first\n"); + hsIfDebugMessage( (fHead<0 || fHead>=fArraySize), "NextLook: Illegal head pointer", fHead); + hsIfDebugMessage( (fTail<0 || fTail>=fArraySize), "NextLook: Illegal tail pointer", fTail); + + // Return copy of item without removing it + *thing = fArray[fLook]; + Inc(&fLook); + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// Code for threaded message queues - move to another file +// +#ifdef MQUEUE + +#include "hsThread.h" +#if HS_BUILD_FOR_UNIX + #include +#endif + +class hsListQue { +public: + struct Elem { + Elem* fNext; + }; +private: + Elem* fHead; + Elem* fTail; + int fCount; +public: + hsListQue(); + virtual ~hsListQue(); + + virtual int Count(); + virtual void Enqueue(Elem* newItem); + virtual Elem* Dequeue(); +}; + +class hsMutexQueue : public hsListQue { + hsMutex fMutex; +public: + hsMutexQueue() {} + + virtual int Count(); + virtual void Enqueue(Elem* newItem); + virtual Elem* Dequeue(); // will return nil if the queue is empty +}; + +class hsSemaphoreQueue : public hsMutexQueue { + hsSemaphore fSema; +public: + hsSemaphoreQueue() {} + + virtual void Enqueue(Elem* newItem); + virtual Elem* Dequeue(); // never returns nil, it just waits +}; + +class hsMsgQueue { + int fMaxSize; +#if HS_BUILD_FOR_UNIX + mqd_t fMQ; +#else + class hsPrivateMQ* fMQ; + UInt32 fAccess; +#endif +public: + enum { + kRead = 0x0001, + kWrite = 0x0002, + kBlock = 0x0004 + }; + + hsMsgQueue(); + virtual ~hsMsgQueue(); + + hsBool Create(const char name[], int maxSize, UInt32 access); + hsBool Open(const char name[], UInt32 access); + void Close(); + + int GetMaxSize() const { return fMaxSize; } + hsBool Send(const void* data, int size = 0); + int Receive(void* data); // returns actual size or 0 + + static void Delete(const char name[]); +}; +#endif // MQUEUE + +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsRefCnt.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsRefCnt.h new file mode 100644 index 00000000..05ea70ca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsRefCnt.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsRefCnt_Defiend +#define hsRefCnt_Defiend + +class hsRefCnt { +private: + int fRefCnt; +public: + hsRefCnt() : fRefCnt(1) {} + virtual ~hsRefCnt(); + + virtual int RefCnt() const { return fRefCnt; } + virtual void UnRef(); + virtual void Ref(); +}; + +#define hsRefCnt_SafeRef(obj) do { if (obj) (obj)->Ref(); } while (0) +#define hsRefCnt_SafeUnRef(obj) do { if (obj) (obj)->UnRef(); } while (0) + +#define hsRefCnt_SafeAssign(dst, src) \ + do { \ + hsRefCnt_SafeRef(src); \ + hsRefCnt_SafeUnRef(dst); \ + dst = src; \ + } while (0) + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSTLStream.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSTLStream.cpp new file mode 100644 index 00000000..d3a21a66 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSTLStream.cpp @@ -0,0 +1,377 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsSTLStream.h" + +hsVectorStream::hsVectorStream() : fEnd(0) +{ +} + +hsVectorStream::hsVectorStream(UInt32 chunkSize) +{ + fVector.reserve(chunkSize); +} + +hsVectorStream::~hsVectorStream() +{ +} + +hsBool hsVectorStream::AtEnd() +{ + return (fBytesRead >= fEnd); +} + +UInt32 hsVectorStream::Read(UInt32 byteCount, void *buffer) +{ + if (fBytesRead + byteCount > fEnd) + { +// hsStatusMessageF("Reading past end of hsVectorStream (read %u of %u requested bytes)", fEnd-fBytesRead, byteCount); + byteCount = fEnd - fBytesRead; + } + + memcpy(buffer, &fVector[fBytesRead], byteCount); + + fBytesRead += byteCount; + fPosition += byteCount; + + return byteCount; +} + +UInt32 hsVectorStream::Write(UInt32 byteCount, const void* buffer) +{ + // If we are at the end of the vector, we can just do a block insert of the data + if (fPosition == fVector.size()) + fVector.insert(fVector.end(), (Byte*)buffer, (Byte*)buffer+byteCount); + // If we are in the middle, I don't know how to just overwrite a block of the vector. + // So, we make sure there is enough space and copy the elements one by one + else + { + fVector.reserve(fPosition+byteCount); + for (UInt32 i = 0; i < byteCount; i++) + fVector[fPosition+i] = ((Byte*)buffer)[i]; + } + + fPosition += byteCount; + + if (fPosition > fEnd) + fEnd = fPosition; + + return byteCount; +} + +void hsVectorStream::Skip(UInt32 deltaByteCount) +{ + fBytesRead += deltaByteCount; + fPosition += deltaByteCount; +} + +void hsVectorStream::Rewind() +{ + fBytesRead = 0; + fPosition = 0; +} + +void hsVectorStream::FastFwd() +{ + fBytesRead = fPosition = fEnd; +} + +void hsVectorStream::Truncate() +{ + fVector.erase(fVector.begin()+fPosition, fVector.end()); + fEnd = fPosition-1; +} + +UInt32 hsVectorStream::GetEOF() +{ + return fEnd; +} + +void hsVectorStream::CopyToMem(void* mem) +{ + memcpy(mem, &fVector[0], fEnd); +} + +void hsVectorStream::Erase(UInt32 bytes) +{ + hsAssert(fPosition+bytes <= fEnd, "Erasing past end of stream"); + + fVector.erase(fVector.begin()+fPosition, fVector.begin()+fPosition+bytes); + fEnd -= bytes; +} + +void hsVectorStream::Reset() +{ + fBytesRead = 0; + fPosition = 0; + fEnd = 0; + fVector.clear(); +} + +const void *hsVectorStream::GetData() +{ + if (fVector.size() > 0) + return &fVector[0]; + else + return nil; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#ifdef HS_BUILD_FOR_WIN32 + +hsNamedPipeStream::hsNamedPipeStream(UInt8 flags, UInt32 timeout) : + fFlags(flags), + fPipe(INVALID_HANDLE_VALUE), + fReadMode(false), + fTimeout(timeout) +{ + memset(&fOverlap, 0, sizeof(OVERLAPPED)); + fOverlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); +} + +hsNamedPipeStream::~hsNamedPipeStream() +{ + CloseHandle(fOverlap.hEvent); + fOverlap.hEvent = INVALID_HANDLE_VALUE; +} + +hsBool hsNamedPipeStream::WaitForClientConnect() +{ + // Look for a client connect (this should return zero since it's overlapped) + BOOL ret = ConnectNamedPipe(fPipe, &fOverlap); + if (ret) + return true; + else + { + switch (GetLastError()) + { + // Waiting for client to connect + case ERROR_IO_PENDING: + if (WaitForSingleObject(fOverlap.hEvent, fTimeout) == WAIT_OBJECT_0) + return true; + break; + + // Client is already connected + case ERROR_PIPE_CONNECTED: +// if (SetEvent(fOverlap.hEvent)) + return true; + break; + } + } + + return false; +} + +hsBool hsNamedPipeStream::Open(const char *name, const char *mode) +{ + wchar* wName = hsStringToWString(name); + wchar* wMode = hsStringToWString(mode); + hsBool ret = Open(wName, wMode); + delete [] wName; + delete [] wMode; + return ret; +} + +hsBool hsNamedPipeStream::Open(const wchar *name, const wchar *mode) +{ + if (wcschr(mode, L'w')) + { + fReadMode = false; + + // Try to create the pipe + fPipe = CreateNamedPipeW(name, + PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE, + 1, + 1024, + 1024, + fTimeout, + NULL); + + if (fPipe != INVALID_HANDLE_VALUE) + return true; + } + else if (wcschr(mode, L'r')) + { + fReadMode = true; + + fPipe = CreateFileW(name, + GENERIC_READ, + 0, // no sharing + NULL, // no security attributes + OPEN_EXISTING, // opens existing pipe + FILE_FLAG_OVERLAPPED, // default attributes + NULL); // no template file + + if (fPipe != INVALID_HANDLE_VALUE) + return true; + } + + return false; +} + +hsBool hsNamedPipeStream::Close() +{ + if (fPipe == INVALID_HANDLE_VALUE) + return false; + + if (fReadMode) + { + CloseHandle(fPipe); // Close our end of the pipe + fPipe = INVALID_HANDLE_VALUE; + } + else + { + FlushFileBuffers(fPipe); // Make sure the client is done reading + DisconnectNamedPipe(fPipe); // Disconnect the pipe from the client + CloseHandle(fPipe); // Close our end of the pipe + fPipe = INVALID_HANDLE_VALUE; + } + + return true; +} + +hsBool hsNamedPipeStream::ICheckOverlappedResult(BOOL result, UInt32 &numTransferred) +{ + // Read/Write succeeded, return now + if (result) + return true; + // Read failed because the operation is taking a while. Wait for it + else if (GetLastError() == ERROR_IO_PENDING) + { + if (WaitForSingleObject(fOverlap.hEvent, fTimeout) == WAIT_OBJECT_0) + { + BOOL oResult = GetOverlappedResult(fPipe, &fOverlap, &numTransferred, FALSE); + if (oResult) + return true; + hsAssert(oResult, "GetOverlappedResult failed"); + } + else + hsAssert(0, "Wait failed"); + } + else + hsAssert(0, "Read/Write failed"); + + return false; +} + +hsBool hsNamedPipeStream::IRead(UInt32 byteCount, void *buffer, UInt32 &numRead) +{ + numRead = 0; + + if (fPipe != INVALID_HANDLE_VALUE && fReadMode) + { + BOOL result = ReadFile(fPipe, buffer, byteCount, &numRead, &fOverlap); + if (ICheckOverlappedResult(result, numRead)) + return true; + } + + // If we got here, the pipe is probably broken. Throw if it is enabled. + if (fFlags & kThrowOnError) + throw this; + + return false; +} + +hsBool hsNamedPipeStream::IWrite(UInt32 byteCount, const void *buffer, UInt32 &numWritten) +{ + numWritten = 0; + + if (fPipe != INVALID_HANDLE_VALUE && !fReadMode) + { + BOOL result = WriteFile(fPipe, buffer, byteCount, &numWritten, &fOverlap); + if (ICheckOverlappedResult(result, numWritten)) + return true; + } + + // If we got here, the pipe is probably broken. Throw if it is enabled. + if (fFlags & kThrowOnError) + throw this; + + return false; +} + +UInt32 hsNamedPipeStream::Read(UInt32 byteCount, void *buffer) +{ + UInt32 totalRead = 0; + + // Read until we get all our data or an error + UInt32 numRead = 0; + while (IRead(byteCount-totalRead, (void*)((UInt32)buffer+totalRead), numRead)) + { + totalRead += numRead; + + if (totalRead >= byteCount) + return totalRead; + } + + return totalRead; +} + +UInt32 hsNamedPipeStream::Write(UInt32 byteCount, const void *buffer) +{ + UInt32 totalWritten = 0; + + // Write until we get all our data or an error + UInt32 numWritten = 0; + while (IWrite(byteCount-totalWritten, (const void*)((UInt32)buffer+totalWritten), numWritten)) + { + totalWritten += numWritten; + + if (totalWritten >= byteCount) + return totalWritten; + } + + return totalWritten; +} + +#ifdef __SGI_STL_PORT +using std::min; +#endif + +void hsNamedPipeStream::Skip(UInt32 deltaByteCount) +{ + char buf[256]; + + // Read until we get all our data or an error + UInt32 totalRead = 0; + UInt32 numRead = 0; + while (IRead(min((UInt32)256L, deltaByteCount-totalRead), buf, numRead)) + { + totalRead += numRead; + + if (totalRead >= deltaByteCount) + return; + } + +} + +void hsNamedPipeStream::Rewind() +{ + hsAssert(0, "Rewind not allowed on a pipe"); +} + +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSTLStream.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSTLStream.h new file mode 100644 index 00000000..423f8daa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSTLStream.h @@ -0,0 +1,113 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "hsStlUtils.h" + +// +// In-memory only +// Erase function lets you cut a chunk out of the middle of the stream +// +class hsVectorStream : public hsStream +{ +protected: + std::vector fVector; + UInt32 fEnd; // End of file (one past the last byte) + +public: + hsVectorStream(); + hsVectorStream(UInt32 chunkSize); + virtual ~hsVectorStream(); + + virtual hsBool Open(const char *, const char *) { hsAssert(0, "hsVectorStream::Open Not Implemented"); return false; } + virtual hsBool Open(const wchar *, const wchar *) { hsAssert(0, "hsVectorStream::Open Not Implemented"); return false; } + virtual hsBool Close() { hsAssert(0, "hsVectorStream::Close Not Implemented"); return false; } + + virtual hsBool AtEnd(); + virtual UInt32 Read(UInt32 byteCount, void * buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void FastFwd(); + virtual void Truncate(); + + virtual UInt32 GetEOF(); + virtual void CopyToMem(void* mem); + + virtual void Reset(); // clears the buffers + + // Erase number of bytes at the current position + virtual void Erase(UInt32 bytes); + // A pointer to the beginning of the data in the stream. This is only valid + // until someone modifies the stream. + const void *GetData(); + // In case you want to try and be efficient with your memory allocations + void Reserve(UInt32 bytes) { fVector.reserve(bytes); } +}; + +#ifdef HS_BUILD_FOR_WIN32 + +#include "hsWindows.h" + +class hsNamedPipeStream : public hsStream +{ +protected: + HANDLE fPipe; + OVERLAPPED fOverlap; + hsBool fReadMode; // True for read, false for write + UInt8 fFlags; + UInt32 fTimeout; + + hsBool ICheckOverlappedResult(BOOL result, UInt32 &numTransferred); + hsBool IRead(UInt32 byteCount, void *buffer, UInt32 &numRead); + hsBool IWrite(UInt32 byteCount, const void *buffer, UInt32 &numWritten); + +public: + enum { kThrowOnError = 1 }; // Throws if a read or write operation fails + + hsNamedPipeStream(UInt8 flags=0, UInt32 timeout=INFINITE); + virtual ~hsNamedPipeStream(); + + // The server (writer) and client (reader) need to open the same file. + // The format is "\\.\pipe\pipeName". The '.' can be replaced with a + // computer name to do it over the network. 'pipeName' is whatever you + // want. + virtual hsBool Open(const char *name, const char *mode); + virtual hsBool Open(const wchar *name, const wchar *mode); + virtual hsBool Close(); + + virtual UInt32 Read(UInt32 byteCount, void *buffer); + virtual UInt32 Write(UInt32 byteCount, const void *buffer); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + + // - For the server (writer) only - + // After calling open, signal your client to start reading and call this function. + // If a client connects, this will return true and you can start writing. If it + // returns false, close the pipe, it ain't happening. + hsBool WaitForClientConnect(); +}; + +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSafeRefCnt.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSafeRefCnt.cpp new file mode 100644 index 00000000..4edc2c93 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSafeRefCnt.cpp @@ -0,0 +1,28 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsSafeRefCnt.h" + +hsMutex hsSafeRefCnt::fMutex; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSafeRefCnt.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSafeRefCnt.h new file mode 100644 index 00000000..16dc6a3a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsSafeRefCnt.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef HS_SAFE_REF_CNT_H +#define HS_SAFE_REF_CNT_H + +#include "hsRefCnt.h" +#include "hsThread.h" + +// +// Thread Safe RefCounter +// + +class hsSafeRefCnt : public hsRefCnt +{ +private: + static hsMutex fMutex; +protected: + virtual void IRef() { } + virtual void IUnRef() { }; +public: + virtual int RefCnt() const { hsTempMutexLock temp(fMutex); return hsRefCnt::RefCnt(); } + void UnRef() { hsTempMutexLock temp(fMutex); IUnRef(); hsRefCnt::UnRef(); } + void Ref() { hsTempMutexLock temp(fMutex); IRef(); hsRefCnt::Ref(); } +}; + +#endif //HS_SAFE_REF_CNT_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsScalar.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsScalar.h new file mode 100644 index 00000000..ab6273f4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsScalar.h @@ -0,0 +1,213 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsScalarMacrosDefined +#define hsScalarMacrosDefined + +#include "hsFixedTypes.h" + +#ifndef HS_SCALAR_IS_FLOAT + #define HS_SCALAR_IS_FIXED 0 + #define HS_SCALAR_IS_FLOAT 1 + #define HS_NEVER_USE_FLOAT 0 +#endif + +#if HS_SCALAR_IS_FLOAT && HS_NEVER_USE_FLOAT + #error "can't define HS_SCALAR_IS_FLOAT and HS_NEVER_USE_FLOAT" +#endif + +#if HS_SCALAR_IS_FLOAT + #include +#endif + +#define hsScalarDegToRad(deg) hsScalarMul(deg, hsScalarPI / 180) +#define hsScalarRadToDeg(rad) hsScalarMul(rad, 180 / hsScalarPI) + +#if HS_SCALAR_IS_FIXED + typedef hsFixed hsScalar; + + #define hsScalar1 hsFixed1 + #define hsScalarHalf (hsFixed1 >> 1) + #define hsScalarPI (hsFixedPI) + #define hsScalarMax (0x7fffffff) + + #if HS_CAN_USE_FLOAT + #define hsFloatToScalar(x) hsFixed((x) * float(hsFixed1)) + #define hsScalarToFloat(x) ((x) / float(hsFixed1)) + #endif + + #define hsIntToScalar(x) hsIntToFixed(x) + #define hsScalarToInt(x) hsFixedToInt(x) + #define hsScalarRound(x) hsFixedRound(x) + + #define hsFixedToScalar(x) (x) + #define hsScalarToFixed(x) (x) + + #define hsFractToScalar(x) hsFractToFixed(x) + #define hsScalarToFract(x) hsFixedToFract(x) + + #define hsScalarMul(a, b) hsFixMul(a, b) + #define hsScalarMul2(a) ((a) << 1) + #define hsScalarDiv(a, b) hsFixDiv(a, b) + #define hsScalarDiv2(a) ((a) >> 1) + #define hsScalarInvert(a) hsFixDiv(hsFixed1, a) + #define hsScalarMod(a,b) ((a) % (b)) + #define hsScalarMulDiv(n1, n2, d) hsMulDiv32(n1, n2, d) + #define hsScalarMulAdd(a, b, c) (hsFixMul(a, b) + (c)) + + #define hsSquareRoot(scalar) hsFixSqrt(scalar) + #define hsSine(angle) hsFixedSin(angle) + #define hsCosine(angle) hsFixedCos(angle) + #define hsTan(angle) (hsSine(angle)/hsCosine(angle)) + #define hsASine(value) hsFixedASin(value) + #define hsACosine(value) hsFixedACos(value) + +#ifdef __cplusplus + inline hsScalar hsScalarAverage(hsScalar a, hsScalar b) { return a + b >> 1; } + inline hsScalar hsScalarAverage(hsScalar a, hsScalar b, hsScalar t) + { + return a + hsFixMul(t, b - a); + } + + #if HS_CAN_USE_FLOAT + inline hsScalar hsPow(hsScalar base, hsScalar exponent) + { + return hsFloatToScalar(powf(hsScalarToFloat(base), hsScalarToFloat(exponent))); + } + inline hsScalar hsATan2(hsScalar y, hsScalar x) + { + return hsFloatToScalar(atan2f(hsScalarToFloat(y), hsScalarToFloat(x))); + } + #endif + inline hsScalar hsCeil(hsScalar x) { return (x + 0xFFFF) & 0xFFFF0000; } + inline hsScalar hsFloor(hsScalar x) { return x & 0xFFFF0000; } +#endif +#endif +#if HS_SCALAR_IS_FLOAT + typedef float hsScalar; + + #define hsScalar1 float(1) + #define hsScalarHalf float(0.5) + #define hsScalarPI float(HS_PI) + #define hsScalarMax float(3.402823466e+38F) + + #define hsFloatToScalar(x) float(x) + #define hsScalarToFloat(x) float(x) + + #define hsIntToScalar(x) float(x) + #define hsScalarToInt(x) Int32(x) + + + #define hsFixedToScalar(x) ((hsScalar)(x) / float(hsFixed1)) + #define hsScalarToFixed(x) hsFixed((x) * float(hsFixed1)) + + #define hsFractToScalar(x) ((x) / float(hsFract1)) + #define hsScalarToFract(x) hsFract((x) * float(hsFract1)) +#ifdef __cplusplus + + #define hsScalarMod(a,b) fmodf(a, b) + #define hsScalarMulAdd(a, b, c) ((a) * (b) + (c)) + #define hsScalarMul(a,b) ((a) * (b)) + #define hsScalarMul2(a) ((a) * 2) + #define hsScalarDiv(a,b) ((a) / (b)) + #define hsScalarDiv2(a) ((a) * float(0.5)) + #define hsScalarInvert(a) (float(1) / (a)) + #define hsScalarMulDiv(n1,n2,d) ((n1) * (n2) / (d)) + +#ifndef HS_DEBUGGING /* mf horse testing defines vs inlines for VC++5.0 performance */ + + #define hsScalarRound(x) Int32((x) + ((x) < 0 ? -hsScalarHalf : hsScalarHalf)) + +#else /* HS_DEBUGGING - use inlines for type-checking etc...and all */ + inline Int32 hsScalarRound(float x) + { + float half = hsScalarHalf; + if (x < 0) + half = -half; + return Int32(x + half); + } +#endif /* HS_DEBUGGING - use inlines for type-checking etc...and all */ + + inline float hsScalarAverage(float a, float b) { return (a + b) * float(0.5); } + inline float hsScalarAverage(float a, float b, float t) { return a + t * (b - a); } + +#if (HS_BUILD_FOR_BE || (HS_BUILD_FOR_UNIX && !HS_BUILD_FOR_GCC322)) + #define acosf(x) (float)acos(x) + #define asinf(x) (float)asin(x) + #define atanf(x) (float)atan(x) + #define atan2f(x, y) (float)atan2(x, y) + #define ceilf(x) (float)ceil(x) + #define cosf(x) (float)cos(x) + #define coshf(x) (float)cosh(x) + #define expf(x) (float)exp(x) + #define fabsf(x) (float)fabs(x) + #define floorf(x) (float)floor(x) + #define fmodf(x, y) (float)fmod(x, y) + #define logf(x) (float)log(x) + #define log10f(x) (float)log10(x) + #define powf(x, y) (float)pow(x, y) + #define sinf(x) (float)sin(x) + #define sinhf(x) (float)sinh(x) + #define sqrtf(x) (float)sqrt(x) + #define tanf(x) (float)tan(x) + #define tanhf(x) (float)tanh(x) + + inline float modff(float x, float* y) + { + double _Di, _Df = modf(x, &_Di); + *y = (float)_Di; + return ((float)_Df); + } +#endif + + inline hsScalar hsSquareRoot(hsScalar scalar) { return sqrtf(scalar); } + inline hsScalar hsSine(hsScalar angle) { return sinf(angle); } + inline hsScalar hsCosine(hsScalar angle) { return cosf(angle); } + inline hsScalar hsTan(hsScalar rads) { return tanf(rads); } + inline hsScalar hsASine(hsScalar value) { return asinf(value); } + inline hsScalar hsACosine(hsScalar value) { return acosf(value); } + inline hsScalar hsPow(hsScalar base, hsScalar exponent) { return powf(base, exponent); } + inline hsScalar hsATan2(hsScalar y, hsScalar x) { return atan2f(y, x); } + inline hsScalar hsCeil(hsScalar x) { return ceilf(x); } + inline hsScalar hsFloor(hsScalar x) { return floorf(x); } +#endif /* HS_SCALAR_IS_FLOAT */ +#endif /* __CPLUSPLUS */ + +// +// Macros for enabling double precision math ops +// require #include +// +#if HS_BUILD_FOR_WIN32 +#define hsDoublePrecBegin \ + unsigned int fpc=_controlfp( 0, 0); \ + _controlfp( _PC_64, MCW_PC ); +#define hsDoublePrecEnd \ + _controlfp( fpc, 0xfffff ); +#else +#define hsDoublePrecBegin +#define hsDoublePrecEnd +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlSortUtils.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlSortUtils.h new file mode 100644 index 00000000..f9b8d45f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlSortUtils.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsStlSortUtils_inc +#define hsStlSortUtils_inc + +// These get used a lot in char * STL maps, so lets just have them in one place + +class stringSorter +{ +public: + bool operator() (const char *s1, const char *s2) const + { + return (strcmp(s1,s2) < 0); + } +}; + +class stringISorter +{ +public: + bool operator() (const char *s1, const char *s2) const + { + return (stricmp(s1,s2) < 0); + } +}; + +#endif // hsStlSortUtils_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlUtils.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlUtils.cpp new file mode 100644 index 00000000..d4f1b04f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlUtils.cpp @@ -0,0 +1,431 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStlUtils.h" + +// stl extensions +namespace xtl { + +//std::string +std::string & trimleft(std::string & s, const char * charset) +{ + s.erase(0, s.find_first_not_of(charset)); + return s; +} + +std::wstring & trimleft(std::wstring & s, const wchar_t * charset) +{ + s.erase(0, s.find_first_not_of(charset)); + return s; +} + +std::string & trimright(std::string & s, const char * charset) +{ + int idx = s.find_last_not_of(charset); + + if (std::string::npos == idx) + { + s.erase(); + } + else + { + char c = s.at(idx); + s.erase(idx, std::string::npos); + s.append(1, c); + } + + return s; +} + +std::wstring & trimright(std::wstring & s, const wchar_t * charset) +{ + int idx = s.find_last_not_of(charset); + + if (std::wstring::npos == idx) + { + s.erase(); + } + else + { + wchar_t c = s.at(idx); + s.erase(idx, std::string::npos); + s.append(1, c); + } + + return s; +} + +std::string & trim(std::string & s, const char * charset) +{ + trimleft(s,charset); + trimright(s,charset); + return s; +} + +std::wstring & trim(std::wstring & s, const wchar_t * charset) +{ + trimleft(s,charset); + trimright(s,charset); + return s; +} + +//xtl::istring +xtl::istring & trimleft(xtl::istring & s, const char * charset) +{ + s.erase(0, s.find_first_not_of(charset)); + return s; +} + +xtl::iwstring & trimleft(xtl::iwstring & s, const wchar_t * charset) +{ + s.erase(0, s.find_first_not_of(charset)); + return s; +} + +xtl::istring & trimright(xtl::istring & s, const char * charset) +{ + int idx = s.find_last_not_of(charset); + + if (xtl::istring::npos == idx) + { + s.erase(); + } + else + { + char c = s.at(idx); + s.erase(idx, xtl::istring::npos); + s.append(1, c); + } + + return s; +} + +xtl::iwstring & trimright(xtl::iwstring & s, const wchar_t * charset) +{ + int idx = s.find_last_not_of(charset); + + if (xtl::iwstring::npos == idx) + { + s.erase(); + } + else + { + wchar_t c = s.at(idx); + s.erase(idx, xtl::iwstring::npos); + s.append(1, c); + } + + return s; +} + +xtl::istring & trim(xtl::istring & s, const char * charset) +{ + trimleft(s,charset); + trimright(s,charset); + return s; +} + +xtl::iwstring & trim(xtl::iwstring & s, const wchar_t * charset) +{ + trimleft(s,charset); + trimright(s,charset); + return s; +} + +// c-string +std::string trim(const char * s, const char * charset) +{ + std::string result = s; + trimleft(result,charset); + trimright(result,charset); + return result; +} + +std::wstring trim(const wchar_t * s, const wchar_t * charset) +{ + std::wstring result = s; + trimleft(result,charset); + trimright(result,charset); + return result; +} + + +// format +std::string format(const char * fmt, ...) +{ + std::string result; + va_list args; + va_start(args,fmt); + formatv(result,fmt,args); + va_end(args); + return result; +} + +std::wstring format(const wchar_t * fmt, ...) +{ + std::wstring result; + va_list args; + va_start(args,fmt); + formatv(result,fmt,args); + va_end(args); + return result; +} + +std::string formatv(const char * fmt, va_list args) +{ + std::string result; + formatv( result, fmt, args ); + return result; +} + +std::wstring formatv(const wchar_t * fmt, va_list args) +{ + std::wstring result; + formatv( result, fmt, args ); + return result; +} + +bool format(std::string & out, const char * fmt, ...) +{ + va_list args; + va_start(args,fmt); + bool r = formatv(out,fmt,args); + va_end(args); + return r; +} + +bool format(std::wstring & out, const wchar_t * fmt, ...) +{ + va_list args; + va_start(args,fmt); + bool r = formatv(out,fmt,args); + va_end(args); + return r; +} + +bool formatv(std::string & out, const char * fmt, va_list args) +{ +#define kBufSz 2048 + + char buf[kBufSz]; + char * pbuf = buf; + int len = 0; + int attempts = 0; + bool success = false; + const int kMaxAttempts = 40; + + do + { + int maxlen = kBufSz*attempts+kBufSz-1; + len = hsVsnprintf(pbuf,maxlen,fmt,args); + attempts++; + success = (len>=0 && len=0 && len StringVector; +typedef std::vector WStringVector; +typedef std::list StringList; +typedef std::list WStringList; +typedef std::set StringSet; +typedef std::set WStringSet; + +template bool GetStringGroup(const std::string& s, StringList& group, char sep); +template bool GetStringGroup(const std::wstring& s, WStringList& group, wchar_t sep); +template bool GetStringGroup(const std::string& s, StringVector& group, char sep); +template bool GetStringGroup(const std::wstring& s, WStringVector& group, wchar_t sep); +template bool GetStringGroup(const std::string& s, StringSet& group, char sep); +template bool GetStringGroup(const std::wstring& s, WStringSet& group, wchar_t sep); + +template bool GetStringGroup(const std::string& s, T& group, char sep) +{ + bool ret = false; + std::string::size_type oldpos = 0, newpos = 0; + + if (!(s.empty())) + { + do + { + newpos = s.find(',',oldpos); + group.insert(group.end(),s.substr(oldpos,newpos)); + if (newpos != s.npos) + oldpos = newpos+1; + } + while(newpos != s.npos); + ret = true; + } + + return ret; +} + +template bool GetStringGroup(const std::wstring& s, T& group, wchar_t sep) +{ + bool ret = false; + std::wstring::size_type oldpos = 0, newpos = 0; + + if (!(s.empty())) + { + do + { + newpos = s.find(L',',oldpos); + group.insert(group.end(),s.substr(oldpos,newpos)); + if (newpos != s.npos) + oldpos = newpos+1; + } while(newpos != s.npos); + ret = true; + } + + return ret; +} + + +template bool GetStringGroupAsString(const StringList& group, std::string& s, char sep); +template bool GetStringGroupAsString(const WStringList& group, std::wstring& s, wchar_t sep); +template bool GetStringGroupAsString(const StringVector& group, std::string& s, char sep); +template bool GetStringGroupAsString(const WStringVector& group, std::wstring& s, wchar_t sep); +template bool GetStringGroupAsString(const StringSet& group, std::string& s, char sep); +template bool GetStringGroupAsString(const WStringSet& group, std::wstring& s, wchar_t sep); + +template bool GetStringGroupAsString(const T& group, std::string& s, char sep) +{ + typename T::const_iterator it = group.begin(); + bool fst = true; + while (it != group.end()) + { + if (!fst) + s += ","; + else + fst = false; + s+= (*it).c_str(); + it++; + } + + return true; +} + +template bool GetStringGroupAsString(const T& group, std::wstring& s, wchar_t sep) +{ + typename T::const_iterator it = group.begin(); + bool fst = true; + while (it != group.end()) + { + if (!fst) + s += L","; + else + fst = false; + s+= (*it).c_str(); + it++; + } + + return true; +} + + +} // namespace std + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlUtils.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlUtils.h new file mode 100644 index 00000000..0b35ae18 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStlUtils.h @@ -0,0 +1,373 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsStlUtils_h_inc +#define hsStlUtils_h_inc + +#include "hsUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +/***************************************************************************** +* +* derived stl classes that use our heap manager +* +***/ + + // TEMPLATE CLASS cyallocator +template + class cyallocator + : public std::_Allocator_base<_Ty> + { // generic cyallocator for objects of class _Ty +public: + typedef std::_Allocator_base<_Ty> _Mybase; + typedef typename _Mybase::value_type value_type; + + + typedef value_type _FARQ *pointer; + typedef value_type _FARQ& reference; + typedef const value_type _FARQ *const_pointer; + typedef const value_type _FARQ& const_reference; + + typedef _SIZT size_type; + typedef _PDFT difference_type; + + template + struct rebind + { // convert an cyallocator<_Ty> to an cyallocator <_Other> + typedef cyallocator<_Other> other; + }; + + pointer address(reference _Val) const + { // return address of mutable _Val + return (&_Val); + } + + const_pointer address(const_reference _Val) const + { // return address of nonmutable _Val + return (&_Val); + } + + cyallocator() + { // construct default cyallocator (do nothing) + } + + cyallocator(const cyallocator<_Ty>&) + { // construct by copying (do nothing) + } + + template + cyallocator(const cyallocator<_Other>&) + { // construct from a related cyallocator (do nothing) + } + + template + cyallocator<_Ty>& operator=(const cyallocator<_Other>&) + { // assign from a related cyallocator (do nothing) + return (*this); + } + + void deallocate(pointer _Ptr, size_type) + { // deallocate object at _Ptr, ignore size + FREE(_Ptr); + } + + pointer allocate(size_type _Count) + { // allocate array of _Count elements + return (pointer)ALLOC(_Count * sizeof(_Ty)); + } + + pointer allocate(size_type _Count, const void _FARQ *) + { // allocate array of _Count elements, ignore hint + return (allocate(_Count)); + } + + void construct(pointer _Ptr, const _Ty& _Val) + { // construct object at _Ptr with value _Val + std::_Construct(_Ptr, _Val); + } + + void destroy(pointer _Ptr) + { // destroy object at _Ptr + std::_Destroy(_Ptr); + } + + _SIZT max_size() const + { // estimate maximum array size + _SIZT _Count = (_SIZT)(-1) / sizeof (_Ty); + return (0 < _Count ? _Count : 1); + } + }; + + // cyallocator TEMPLATE OPERATORS +template inline + bool operator==(const cyallocator<_Ty>&, const cyallocator<_Other>&) + { // test for cyallocator equality (always true) + return (true); + } + +template inline + bool operator!=(const cyallocator<_Ty>&, const cyallocator<_Other>&) + { // test for cyallocator inequality (always false) + return (false); + } + + // CLASS cyallocator +template<> class _CRTIMP2 cyallocator + { // generic cyallocator for type void +public: + typedef void _Ty; + typedef _Ty _FARQ *pointer; + typedef const _Ty _FARQ *const_pointer; + typedef _Ty value_type; + + template + struct rebind + { // convert an cyallocator to an cyallocator <_Other> + typedef cyallocator<_Other> other; + }; + + cyallocator() + { // construct default cyallocator (do nothing) + } + + cyallocator(const cyallocator<_Ty>&) + { // construct by copying (do nothing) + } + + template + cyallocator(const cyallocator<_Other>&) + { // construct from related cyallocator (do nothing) + } + + template + cyallocator<_Ty>& operator=(const cyallocator<_Other>&) + { // assign from a related cyallocator (do nothing) + return (*this); + } + }; + + +/***************************************************************************** +* +* Drop-in replacements for stl classes. Uses our allocator instead of the default one. +* +***/ + +typedef std::basic_string, cyallocator > cystring; +typedef std::basic_string, cyallocator > cywstring; +// cyistring and cyiwstring declared later in this file + + // TEMPLATE CLASS cyvector +template +class cyvector : public std::vector<_Ty, cyallocator<_Ty> > { +}; + + // TEMPLATE CLASS cymap +template > +class cymap : public std::map<_Kty, _Ty, _Pr, cyallocator > > { +}; + + // TEMPLATE CLASS cylist +template +class cylist : public std::list<_Ty, cyallocator<_Ty> > { +}; + + // TEMPLATE CLASS cyset +template > +class cyset : public std::set<_Kty, _Pr, cyallocator< _Kty > > { +}; + + + +/***************************************************************************** +* +* stl extensions +* +***/ +namespace xtl +{ + +// Why oh why doesn't stl have copy_if? +// See Effective STL [Meyers 2001] Item 36. +template< typename InIt, typename OutIt, typename Pred > +OutIt copy_if( InIt srcBegin, InIt srcEnd, OutIt dstBegin, Pred pred ) +{ + while ( srcBegin!=srcEnd ) + { + if ( pred( *srcBegin ) ) + *dstBegin++ = *srcBegin; + ++srcBegin; + } + return dstBegin; +} + + + +// useful when clearing a vector/list/set of pointers that need to be deleted. +// use like: +// std::vector vec; +// std::for_each(vec.begin(),vec.end(),xtl::delete_ptr()); +// vec.clear(); + +struct delete_ptr +{ + template< class T > void operator()( T * p ) const { delete p;} +}; + +// useful when clearing a map of pointers that need to be deleted. +// use like: +// typedef std::map foomap; +// foomap m; +// std::for_each(m.begin(),m.end(),xtl::delete_map_ptr_T()); +// m.clear(); + +template< class A > +struct delete_map_ptr_T +{ + void operator()( typename A::value_type & pair ) const { delete pair.second;} +}; + +// case insensitive string comparer +// useful in maps that use strings +struct stricmp_less : public std::binary_function +{ + bool operator()(const std::string & _X, const std::string & _Y) const + {return ( _stricmp(_X.c_str(),_Y.c_str()) < 0); } +}; +struct wstricmp_less : public std::binary_function +{ + bool operator()(const std::wstring & _X, const std::wstring & _Y) const + {return ( _wcsicmp(_X.c_str(),_Y.c_str()) < 0); } +}; + +// struct stricmp_char_traits +// case insensitive char_traits. used in creating istring class below +#ifdef __SGI_STL_PORT +struct stricmp_char_traits : public __std_alias::char_traits< char > +#else +struct stricmp_char_traits : public std::char_traits< char > +#endif +{ + static int compare(const char * A, const char * B, size_t N) + { + for (size_t I=0; I +#else +struct wstricmp_char_traits : public std::char_traits< wchar_t > +#endif +{ + static int compare(const wchar_t * A, const wchar_t * B, size_t N) + { + for (size_t I=0; I istring; +typedef std::basic_string iwstring; + +// cyallocator version of istring +typedef std::basic_string > cyistring; +typedef std::basic_string > cyiwstring; + + +// std::string trim +std::string & trimleft(std::string & s, const char * charset=" \t\n\r"); +std::wstring & trimleft(std::wstring & s, const wchar_t * charset=L" \t\n\r"); +std::string & trimright(std::string & s, const char * charset=" \t\n\r"); +std::wstring & trimright(std::wstring & s, const wchar_t * charset=L" \t\n\r"); +std::string & trim(std::string & s, const char * charset=" \t\n\r"); +std::wstring & trim(std::wstring & s, const wchar_t * charset=L" \t\n\r"); +// xtl::istring trim +xtl::istring & trimleft(xtl::istring & s, const char * charset=" \t\n\r"); +xtl::iwstring & trimleft(xtl::iwstring & s, const wchar_t * charset=L" \t\n\r"); +xtl::istring & trimright(xtl::istring & s, const char * charset=" \t\n\r"); +xtl::iwstring & trimright(xtl::iwstring & s, const wchar_t * charset=L" \t\n\r"); +xtl::istring & trim(xtl::istring & s, const char * charset=" \t\n\r"); +xtl::iwstring & trim(xtl::iwstring & s, const wchar_t * charset=L" \t\n\r"); +// c-string trim +std::string trim(const char * s, const char * charset=" \t\n\r"); +std::wstring trim(const wchar_t * s, const wchar_t * charset=L" \t\n\r"); +// format +std::string format(const char * fmt, ...); +std::wstring format(const wchar_t * fmt, ...); +std::string formatv(const char * fmt, va_list args); +std::wstring formatv(const wchar_t * fmt, va_list args); +bool format(std::string & out, const char * fmt, ...); +bool format(std::wstring & out, const wchar_t * fmt, ...); +bool formatv(std::string & out, const char * fmt, va_list args); +bool formatv(std::wstring & out, const wchar_t * fmt, va_list args); + + +template bool GetStringGroup(const std::string& s, T& group, char sep = ','); +template bool GetStringGroup(const std::wstring& s, T& group, wchar_t sep = L','); +template bool GetStringGroupAsString(const T& group, std::string& s, char sep = ','); +template bool GetStringGroupAsString(const T& group, std::wstring& s, wchar_t sep = L','); + + +} // namespace xtd + + + +#endif // hsStlUtils_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStream.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStream.cpp new file mode 100644 index 00000000..83298ba0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStream.cpp @@ -0,0 +1,1890 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "hsStream.h" +#include "hsMemory.h" +#include "hsUtils.h" + +#include "hsTemplates.h" +#include "hsStlUtils.h" + +#if HS_BUILD_FOR_UNIX +#include +#endif + +#if HS_BUILD_FOR_MAC + #include + #include + #include +#endif + +#if HS_BUILD_FOR_PS2 +#include +#include +#endif + +#include "hsWindows.h" +#if HS_BUILD_FOR_WIN32 +#include +#endif +#include "hsStlUtils.h" + +////////////////////////////////////////////////////////////////////////////////// + +#if HS_CPU_BENDIAN + static void swapIt(Int32 *swap) + { + Byte* c = (Byte*)swap; + Byte t = c[0]; + + c[0] = c[3]; + c[3] = t; + t = c[1]; + c[1] = c[2]; + c[2] = t; + } + static void swapIt(int *swap) + { + swapIt((Int32*)swap); + } + static void swapIt(float *swap) + { + swapIt((Int32*)swap); + } + + static void swapIt(double *swap) + { + float* a = (float*)&swap; + float* b = (float*)(((Byte*)&swap)+4); + swapIt(a); + swapIt(b); + } + + static void swapIt(Int16 *swap) + { + Byte *c = (Byte*)swap; + Byte t; + t = c[0]; + c[0] = c[1]; + c[1] = t; + } + #define unswapIt(value) +#else + #define swapIt(value) + static void unswapIt(Int32 *swap) + { + Byte* c = (Byte*)swap; + Byte t = c[0]; + + c[0] = c[3]; + c[3] = t; + t = c[1]; + c[1] = c[2]; + c[2] = t; + } + static void unswapIt(int *swap) + { + unswapIt((Int32*)swap); + } + static void unswapIt(float *swap) + { + unswapIt((Int32*)swap); + } + + static void unswapIt(double *swap) + { + float* a = (float*)&swap; + float* b = (float*)(((Byte*)&swap)+4); + swapIt(a); + swapIt(b); + } + + static void unswapIt(Int16 *swap) + { + Byte *c = (Byte*)swap; + Byte t; + t = c[0]; + c[0] = c[1]; + c[1] = t; + } +#endif + +////////////////////////////////////////////////////////////////////////////////// + +void hsStream::FastFwd() +{ + hsThrow("FastFwd unimplemented by subclass of stream"); +} + +UInt32 hsStream::GetPosition() const +{ + return fPosition; +} + +void hsStream::SetPosition(UInt32 position) +{ + if (position == fPosition) + return; + Rewind(); + Skip(position); +} + +void hsStream::Truncate() +{ + hsThrow("Truncate unimplemented by subclass of stream"); +} + +UInt32 hsStream::GetSizeLeft() +{ + UInt32 ret = 0; + if (GetPosition() > GetEOF()) + { + hsThrow("Position is beyond EOF"); + } + else + { + ret = GetEOF() - GetPosition(); + } + + return ret; +} + +////////////////////////////////////////////////////////////////////////////////// + +UInt32 hsStream::GetEOF() +{ + hsThrow( "GetEOF() unimplemented by subclass of stream"); + return 0; +} + +void hsStream::CopyToMem(void* mem) +{ + hsThrow( "CopyToMem unimplemented by subclass of stream"); +} + +////////////////////////////////////////////////////////////////////////////////// + +hsStream::~hsStream() +{ +} + +UInt32 hsStream::WriteString(const char cstring[]) +{ + return Write(hsStrlen(cstring), cstring); +} + +UInt32 hsStream::WriteFmt(const char * fmt, ...) +{ + va_list av; + va_start( av, fmt ); + UInt32 n = WriteFmtV( fmt, av ); + va_end( av ); + return n; +} + +UInt32 hsStream::WriteFmtV(const char * fmt, va_list av) +{ + std::string buf; + xtl::formatv( buf, fmt, av ); + return Write( buf.length(), buf.data() ); +} + +UInt32 hsStream::WriteSafeStringLong(const char *string) +{ + UInt32 len = hsStrlen(string); + WriteSwap32(len); + if (len > 0) + { + char *buff = TRACKED_NEW char[len+1]; + int i; + for (i = 0; i < len; i++) + { + buff[i] = ~string[i]; + } + buff[len] = '\0'; + UInt32 result = Write(len, buff); + delete [] buff; + return result; + } + else + return 0; +} + +UInt32 hsStream::WriteSafeWStringLong(const wchar_t *string) +{ + UInt32 len = wcslen(string); + WriteSwap32(len); + if (len > 0) + { + int i; + for (i=0; i 0 && numChars <= GetSizeLeft()) + { + name = TRACKED_NEW char[numChars+1]; + Read(numChars, name); + name[numChars] = '\0'; + + // if the high bit is set, flip the bits. Otherwise it's a normal string, do nothing. + if (name[0] & 0x80) + { + int i; + for (i = 0; i < numChars; i++) + name[i] = ~name[i]; + } + } + + return name; +} + +wchar_t *hsStream::ReadSafeWStringLong() +{ + wchar_t *retVal = nil; + UInt32 numChars = ReadSwap32(); + if (numChars > 0 && numChars <= (GetSizeLeft()/2)) // divide by two because each char is two bytes + { + retVal = TRACKED_NEW wchar_t[numChars+1]; + int i; + for (i=0; i 0) + { + char *buff = TRACKED_NEW char[len+1]; + int i; + for (i = 0; i < len; i++) + { + buff[i] = ~string[i]; + } + buff[len] = '\0'; + UInt32 result = Write(len, buff); + delete [] buff; + return result; + } + else + return 0; +} + +UInt32 hsStream::WriteSafeWString(const wchar_t *string) +{ + int len = wcslen(string); + hsAssert(len<0xf000, xtl::format("string len of %d is too long for WriteSafeWString, use WriteSafeWStringLong", + len).c_str() ); + + WriteSwap16(len | 0xf000); + if (len > 0) + { + int i; + for (i=0; i 0 && numChars <= GetSizeLeft()) + { + name = TRACKED_NEW char[numChars+1]; + Read(numChars, name); + name[numChars] = '\0'; + + // if the high bit is set, flip the bits. Otherwise it's a normal string, do nothing. + if (name[0] & 0x80) + { + int i; + for (i = 0; i < numChars; i++) + name[i] = ~name[i]; + } + } + + return name; +} + +wchar_t *hsStream::ReadSafeWString() +{ + wchar_t *retVal = nil; + UInt32 numChars = ReadSwap16(); + + numChars &= ~0xf000; + hsAssert(numChars <= GetSizeLeft()/2, "Bad string"); + if (numChars > 0 && numChars <= (GetSizeLeft()/2)) // divide by two because each char is two bytes + { + retVal = TRACKED_NEW wchar_t[numChars+1]; + int i; + for (i=0; iRead(sizeof(UInt32), pv); + if (knt != 4) + return false; + return true; +} + +hsBool hsStream::Read12Bytes(void *buffer) // Reads 12 bytes, return true if success +{ + int knt = this->Read(12,buffer); + if (knt != 12) + return false; + return true; +} + +hsBool hsStream::Read8Bytes(void *buffer) // Reads 12 bytes, return true if success +{ + int knt = this->Read(8,buffer); + if (knt !=8) + return false; + return true; +} + +hsBool hsStream::ReadBool() // Virtual, faster version in sub classes +{ + return this->ReadByte(); +} + +bool hsStream::Readbool() // Virtual, faster version in sub classes +{ + return this->ReadByte() ? true : false; +} + +void hsStream::ReadBool(int count, hsBool values[]) +{ + this->Read(count, values); + + if (sizeof(hsBool) > 1) + { const UInt8* src = (UInt8*)values; + + // go backwards so we don't overwrite ourselves + for (int i = count - 1; i >= 0; --i) + values[i] = src[i]; + } +} + +UInt8 hsStream::ReadByte() +{ + UInt8 value; + + this->Read(sizeof(UInt8), &value); + return value; +} + +hsBool hsStream::AtEnd() +{ + hsAssert(0,"No hsStream::AtEnd() implemented for this stream class"); + return false; +} + +hsBool hsStream::IsTokenSeparator(char c) +{ + return (isspace(c) || c==',' || c=='='); +} + +hsBool hsStream::GetToken(char *s, UInt32 maxLen, const char beginComment, const char endComment) +{ + char c; + char endCom; + endCom = endComment; + + while( true ) + { + while( !AtEnd() && IsTokenSeparator(c = ReadByte()) ) + c = c; + ; + if( AtEnd() ) + return false; + + if( beginComment != c ) + break; + + // skip to end of comment + while( !AtEnd() && (endCom != (c = ReadByte())) ) + c= c; + ; + } + + s[0] = c; + UInt32 k = 1; + while( !AtEnd() && !IsTokenSeparator(c = ReadByte()) ) + { + if( k < maxLen ) + s[k++] = c; + } + s[k] = 0; + + + if( (k > 0)&&!_stricmp(s, "skip") ) + { + int depth = 1; + while( depth && GetToken(s, maxLen, beginComment, endCom) ) + { + if( !_stricmp(s, "skip") ) + depth++; + else + if( !_stricmp(s, "piks") ) + depth--; + } + return GetToken(s, maxLen, beginComment, endCom); + } + + return true; +} + +hsBool hsStream::ReadLn(char *s, UInt32 maxLen, const char beginComment, const char endComment) +{ + char c; + char endCom; + endCom = endComment; + + while( true ) + { + while( !AtEnd() && strchr("\r\n",c = ReadByte()) ) + c = c; + ; + if( AtEnd() ) + return false; + + if( beginComment != c ) + break; + + // skip to end of comment + while( !AtEnd() && (endCom != (c = ReadByte())) ) + c= c; + ; + } + + s[0] = c; + UInt32 k = 1; + while( !AtEnd() && !strchr("\r\n",c = ReadByte()) ) + { + if( k < maxLen ) + s[k++] = c; + } + s[k] = 0; + + + if( (k > 0)&&!_stricmp(s, "skip") ) + { + int depth = 1; + while( depth && ReadLn(s, maxLen, beginComment, endCom) ) + { + if( !_stricmp(s, "skip") ) + depth++; + else + if( !_stricmp(s, "piks") ) + depth--; + } + return ReadLn(s, maxLen, beginComment, endCom); + } + + return true; +} + +UInt16 hsStream::ReadSwap16() +{ + UInt16 value; + this->Read(sizeof(UInt16), &value); + swapIt((Int16*)&value); + return value; +} + +void hsStream::ReadSwap16(int count, UInt16 values[]) +{ + this->Read(count * sizeof(UInt16), values); +#if HS_CPU_BENDIAN + for (int i = 0; i < count; i++) + swapIt((Int16*)&values[i]); +#endif +} + +UInt32 hsStream::ReadSwap32() +{ + UInt32 value; + Read4Bytes(&value); + swapIt((Int32*)&value); + return value; +} + +void hsStream::ReadSwap32(int count, UInt32 values[]) +{ + this->Read(count * sizeof(UInt32), values); +#if HS_CPU_BENDIAN + for (int i = 0; i < count; i++) + swapIt((Int32*)&values[i]); +#endif +} + +UInt32 hsStream::ReadUnswap32() +{ + UInt32 value; + Read4Bytes(&value); + unswapIt((Int32*)&value); + return value; +} + +#if HS_CAN_USE_FLOAT + double hsStream::ReadSwapDouble() + { + double ival; + Read8Bytes(&ival); + double *pval = (double *)&ival; // all in the name of speed, + swapIt(pval); + return *pval; + } + + void hsStream::ReadSwapDouble(int count, double values[]) + { + this->Read(count * sizeof(double), values); +#if HS_CPU_BENDIAN + for (int i = 0; i < count; i++) + swapIt(&values[i]); +#endif + } + + + float hsStream::ReadSwapFloat() + { + UInt32 ival; + Read4Bytes(&ival); + float *pval = (float *)&ival; // all in the name of speed, + swapIt(pval); + return *pval; + } + + void hsStream::ReadSwapFloat(int count, float values[]) + { + this->Read(count * sizeof(float), values); +#if HS_CPU_BENDIAN + for (int i = 0; i < count; i++) + swapIt(&values[i]); +#endif + } + + float hsStream::ReadUnswapFloat() + { + float value; + this->Read(sizeof(float), &value); + unswapIt(&value); + return value; + } +#endif + + +void hsStream::WriteBool(hsBool value) +{ + UInt8 dst = (value != 0); + + this->Write(sizeof(UInt8), &dst); +} + +void hsStream::Writebool(bool value) +{ + UInt8 dst = (value != 0); + + this->Write(sizeof(UInt8), &dst); +} + +void hsStream::WriteBool(int count, const hsBool values[]) +{ + if (sizeof(hsBool) > 1) + { hsTempArray storage(count); + UInt8* dst = (UInt8*)values; + + for (int i = 0; i < count; i++) + dst[i] = (values[i] != 0); + this->Write(count, dst); + } + else + this->Write(count, values); +} + +void hsStream::WriteByte(UInt8 value) +{ + this->Write(sizeof(UInt8), &value); +} + +void hsStream::WriteSwap16(UInt16 value) +{ + swapIt((Int16*)&value); + this->Write(sizeof(Int16), &value); +} + +void hsStream::WriteSwap16(int count, const UInt16 values[]) +{ + for (int i = 0; i < count; i++) + this->WriteSwap16(values[i]); +} + +void hsStream::WriteSwap32(UInt32 value) +{ + swapIt((Int32*)&value); + this->Write(sizeof(Int32), &value); +} + +void hsStream::WriteSwap32(int count, const UInt32 values[]) +{ + for (int i = 0; i < count; i++) + this->WriteSwap32(values[i]); +} + +void hsStream::WriteUnswap32(UInt32 value) +{ + unswapIt((Int32*)&value); + this->Write(sizeof(Int32), &value); +} + +#if HS_CAN_USE_FLOAT + void hsStream::WriteSwapDouble(double value) + { + swapIt(&value); + this->Write(sizeof(double), &value); + } + + void hsStream::WriteSwapDouble(int count, const double values[]) + { + for (int i = 0; i < count; i++) + this->WriteSwapDouble(values[i]); + } + + void hsStream::WriteSwapFloat(float value) + { + swapIt(&value); + this->Write(sizeof(float), &value); + } + + void hsStream::WriteSwapFloat(int count, const float values[]) + { + for (int i = 0; i < count; i++) + this->WriteSwapFloat(values[i]); + } + + void hsStream::WriteUnswapFloat(float value) + { + unswapIt(&value); + this->Write(sizeof(float), &value); + } +#endif + +void hsStream::WriteSwapAtom(UInt32 tag, UInt32 size) +{ + this->WriteSwap32(tag); + this->WriteSwap32(size); +} + +UInt32 hsStream::ReadSwapAtom(UInt32* sizePtr) +{ + UInt32 tag = this->ReadSwap32(); + UInt32 size = this->ReadSwap32(); + + if (sizePtr) + *sizePtr = size; + return tag; +} + +////////////////////////////////////////////////////////////////////////////////// + +#define kFileStream_Uninitialized ~0 + +hsBool hsFileStream::Open(const char *name, const char *mode) +{ +#ifdef HS_BUILD_FOR_PS2 + hsAssert(fRef == kFileStream_Uninitialized, "hsFileStream:Open Stream already opened"); + + Int32 ref = hsPS2Open(name, mode); + if (ref == -1) + return false; + + fRef = (UInt32) ref; + fFileSize = sceLseek(fRef, 0, SCE_SEEK_END); + sceLseek(fRef, 0, SCE_SEEK_SET); + fBufferIsEmpty = true; + fWriteBufferUsed = false; + fVirtualFilePointer = 0; + fBufferBase = 0; + + return true; +#else + hsAssert(0, "hsFileStream::Open NotImplemented"); + return false; +#endif +} + +hsBool hsFileStream::Open(const wchar *name, const wchar *mode) +{ + hsAssert(0, "hsFileStream::Open NotImplemented"); + return false; +} + +hsBool hsFileStream::Close () +{ +#ifdef HS_BUILD_FOR_PS2 + if (fRef != kFileStream_Uninitialized) + { + hsPS2Close(fRef); + fRef = kFileStream_Uninitialized; + } + return true; +#else + hsAssert(0, "hsFileStream::Close NotImplemented"); + return false; +#endif +} + +UInt32 hsFileStream::GetFileRef() +{ + return fRef; +} + +void hsFileStream::SetFileRef(UInt32 ref) +{ + hsAssert(ref != kFileStream_Uninitialized, "bad ref"); + fRef = ref; +#if HS_BUILD_FOR_PS2 + fFileSize = sceLseek(fRef, 0, SCE_SEEK_END); + sceLseek(fRef, 0, SCE_SEEK_SET); + fBufferIsEmpty= true; + fWriteBufferUsed= false; + fVirtualFilePointer= 0; + fBufferBase= 0; +#endif +} + +hsFileStream::hsFileStream() +{ + fRef = kFileStream_Uninitialized; +#if HS_BUILD_FOR_PS2 + fBufferIsEmpty= true; + fWriteBufferUsed= false; +#endif +} + +hsFileStream::~hsFileStream() +{ +} + +UInt32 hsFileStream::Read(UInt32 bytes, void* buffer) +{ + hsAssert(fRef != kFileStream_Uninitialized, "fRef uninitialized"); + + fBytesRead += bytes; + fPosition += bytes; + +#if HS_BUILD_FOR_MAC + Int16 err; + + err = FSRead(fRef, (long*)&bytes, buffer); + if (err == noErr) + return bytes; + else + return 0; +#elif HS_BUILD_FOR_PS2 + Int32 ret; + Int32 nReadBytes= 0; + while(bytes){ + if( !fBufferIsEmpty ){ // read at already chatched. + Int32 DataBytesInBuffer= fBufferBase + kBufferSize - fVirtualFilePointer; + Int32 ChatchedReadSize= DataBytesInBuffer < bytes ? DataBytesInBuffer : bytes; + memcpy( buffer, &fBuffer[fVirtualFilePointer-fBufferBase], ChatchedReadSize ); + nReadBytes += ChatchedReadSize; + buffer= (void *)(((char*)buffer) + ChatchedReadSize); + fVirtualFilePointer += ChatchedReadSize; + bytes -= ChatchedReadSize; + fBufferIsEmpty= (fBufferBase + kBufferSize <= fVirtualFilePointer); + } + if( kBufferSize <= bytes ){ // read directry, for Large block read. + hsAssert( fBufferIsEmpty, "read buffer was not used."); + Int32 DirectReadSize= bytes - bytes % kBufferSize; + ret= sceRead(fRef, buffer, DirectReadSize); + if( ret == -1 ){ + return 0; + } + hsAssert( ret == DirectReadSize, "require read size != return size"); + nReadBytes += DirectReadSize; + buffer= (void *)(((char*)buffer) + DirectReadSize); + fVirtualFilePointer += DirectReadSize; + bytes -= DirectReadSize; + } + if( 0 < bytes && fBufferIsEmpty ){ // fill buffer + hsAssert( fVirtualFilePointer % kBufferSize == 0 , "read buffer is not alignment."); + ret= sceRead(fRef, fBuffer, kBufferSize ); + if( ret == -1 ){ + return 0; + } + fBufferBase= fVirtualFilePointer; + fBufferIsEmpty= false; + } + } + return nReadBytes; + +#elif HS_BUILD_FOR_WIN32 + UInt32 rBytes; + ReadFile((HANDLE)fRef, buffer, bytes, &rBytes, nil); + if(bytes == rBytes) + return bytes; + else + return 0; +#else + return 0; +#endif +} + +UInt32 hsFileStream::Write(UInt32 bytes, const void* buffer) +{ + hsAssert(fRef != kFileStream_Uninitialized, "fRef uninitialized"); + + fBytesRead += bytes; + fPosition += bytes; + +#if HS_BUILD_FOR_MAC + Int16 err; + + err = FSWrite(fRef, (long*)&bytes, buffer); + if (err == noErr) + return bytes; + else + { + hsDebugMessage("hsFileStream::Write failed", err); + return 0; + } +#elif HS_BUILD_FOR_PS2 + Int32 ret; + + fWriteBufferUsed =true; // buffered write was not implement, not yet. + + ret = sceWrite(fRef, (void*)buffer ,bytes); + if(ret != -1) + return ret; + else + return 0; +#elif HS_BUILD_FOR_WIN32 + UInt32 wBytes; + WriteFile((HANDLE)fRef, buffer, bytes, &wBytes, nil); + if(bytes == wBytes) + return bytes; + else + { + char str[128]; + sprintf(str, "hsFileStream::Write failed. err %d", GetLastError()); + hsAssert(false, str); + return 0; + } +#else + return 0; +#endif +} + + +hsBool hsFileStream::AtEnd() +{ +#if HS_BUILD_FOR_MAC + Int32 eof; + Int32 pos; + ::GetEOF(fRef, &eof); + ::GetFPos(fRef, &pos); + return pos >= eof; +#elif HS_BUILD_FOR_PS2 + Int32 rVal = 0; + if( fWriteBufferUsed || fVirtualFilePointer == 0 ){ + // bufferd write was not implement, yiet. + rVal = sceLseek(fRef, 0, SCE_SEEK_CUR); + return rVal >= fFileSize; + } + else{ // bufferd read + return fVirtualFilePointer >= fFileSize; + } + +#elif HS_BUILD_FOR_WIN32 + UInt32 bytes; + PeekNamedPipe((void*)fRef, nil, 0, nil, &bytes, nil); + return bytes>0; +#else + hsAssert(0,"No hsStream::AtEnd() implemented for this stream class"); + return false; +#endif +} + +void hsFileStream::Skip(UInt32 delta) +{ + fBytesRead += delta; + fPosition += delta; + +#if HS_BUILD_FOR_MAC + short err = SetFPos(fRef, fsFromMark, delta); + hsAssert(err == noErr, "SetFPos failed"); +#elif HS_BUILD_FOR_PS2 + const Int32 NewPointer= fVirtualFilePointer+delta; + if( fWriteBufferUsed || fVirtualFilePointer == 0 ){ + // bufferd write was not implement, yiet. + sceLseek(fRef, delta, SCE_SEEK_CUR); + } + else{ // bufferd read. + if( !fBufferIsEmpty ){ + Int32 CurBlock= fVirtualFilePointer / kBufferSize; + Int32 NewBlock= NewPointer / kBufferSize; + if( CurBlock == NewBlock ){ + fVirtualFilePointer += delta; + return; + } + fBufferIsEmpty= false; + } + Int32 NewBaseMod= NewPointer % kBufferSize; + Int32 NewBase= NewPointer - NewBaseMod; + if( NewBaseMod ){ + sceLseek( fRef, NewBase, SCE_SEEK_SET ); + sceRead( fRef, fBuffer, kBufferSize ); + fVirtualFilePointer= NewPointer; + fBufferBase= NewBase; + fBufferIsEmpty= false; + } + else{ + // just block border. + fVirtualFilePointer= NewPointer; + fBufferBase= NewBase; + } + } +#elif HS_BUILD_FOR_WIN32 + hsDebugMessage("hsFileStream::Skip unimplemented", 0); +#endif +} + +void hsFileStream::Rewind() +{ + fBytesRead = 0; + fPosition = 0; + +#if HS_BUILD_FOR_MAC + short err = SetFPos(fRef, fsFromStart, 0); + hsAssert(err == noErr, "SetFPos failed"); +#elif HS_BUILD_FOR_PS2 + if( fWriteBufferUsed || fVirtualFilePointer == 0 ){ + // bufferd write was not implement, yiet. + sceLseek(fRef,0,SCE_SEEK_SET); + } + else{ // bufferd read. + sceLseek(fRef, 0, SCE_SEEK_SET); + fBufferIsEmpty= true; + fVirtualFilePointer= 0; + fBufferBase= 0; + } +#elif HS_BUILD_FOR_WIN32 + hsDebugMessage("hsFileStream::Rewind unimplemented", 0); +#endif +} + +void hsFileStream::Truncate() +{ + hsDebugMessage("hsFileStream::Truncate unimplemented", 0); +} + +////////////////////////////////////////////////////////////////////////////////////// + +#if !HS_BUILD_FOR_PS2 +#if !(HS_BUILD_FOR_REFERENCE) + +hsUNIXStream::~hsUNIXStream() +{ + // Don't Close here, because Sub classes Don't always want that behaviour! +} + +hsBool hsUNIXStream::Open(const char *name, const char *mode) +{ + fPosition = 0; + fRef = hsFopen(name, mode); + return (fRef) ? true : false; +} + +hsBool hsUNIXStream::Open(const wchar *name, const wchar *mode) +{ + fPosition = 0; + fRef = _wfopen(name, mode); + return (fRef) ? true : false; +} + +hsBool hsUNIXStream::Close() +{ + int rtn = true; + if (fRef) + rtn = fclose(fRef); + fRef = nil; + delete [] fBuff; + fBuff = nil; + + return !rtn; +} + +UInt32 hsUNIXStream::Read(UInt32 bytes, void* buffer) +{ + if (!fRef || !bytes) + return 0; + int numItems = ::fread(buffer, 1 /*size*/, bytes /*count*/, fRef); + fBytesRead += numItems; + fPosition += numItems; + if ((unsigned)numItems < bytes) { + if (feof(fRef)) { + // EOF ocurred + char str[128]; + sprintf(str, "Hit EOF on UNIX Read, only read %d out of requested %d bytes\n", numItems, bytes); + hsDebugMessage(str, 0); + } + else { + hsDebugMessage("Error on UNIX Read", ferror(fRef)); + } + } + return numItems; +} + +hsBool hsUNIXStream::AtEnd() +{ + if (!fRef) + return 1; + hsBool rVal; + int x = getc(fRef); + rVal = feof(fRef) != 0; + ungetc(x, fRef); + return rVal; +} + +UInt32 hsUNIXStream::Write(UInt32 bytes, const void* buffer) +{ + if (!fRef) + return 0; + fPosition += bytes; + return fwrite(buffer, bytes, 1, fRef); +} + +void hsUNIXStream::SetPosition(UInt32 position) +{ + if (!fRef || (position == fPosition)) + return; + fBytesRead = position; + fPosition = position; + (void)::fseek(fRef, position, SEEK_SET); +} + +void hsUNIXStream::Skip(UInt32 delta) +{ + if (!fRef) + return; + fBytesRead += delta; + fPosition += delta; + (void)::fseek(fRef, delta, SEEK_CUR); +} + +void hsUNIXStream::Rewind() +{ + if (!fRef) + return; + fBytesRead = 0; + fPosition = 0; + (void)::fseek(fRef, 0, SEEK_SET); +} + +void hsUNIXStream::FastFwd() +{ + if (!fRef) + return; + (void)::fseek(fRef, 0, SEEK_END); + fBytesRead = fPosition = ftell(fRef); +} + +UInt32 hsUNIXStream::GetEOF() +{ + if( !fRef ) + return 0; + + long oldPos = ftell( fRef ); + (void)::fseek( fRef, 0, SEEK_END ); + UInt32 end = (UInt32)ftell( fRef ); + (void)::fseek( fRef, oldPos, SEEK_SET ); + + return end; +} + +void hsUNIXStream::Truncate() +{ + if (!fRef) + return; +#if! __MWERKS__ + int handle = _fileno(fRef); +#if !HS_BUILD_FOR_UNIX + _chsize(handle, fPosition); +#else + ftruncate(handle, fPosition); +#endif +#else +#if 1 + UInt32 handle = (UInt32)fRef->handle; + OSErr err = ::SetEOF(handle, fPosition); + if(err != noErr) + { + hsThrow("Truncate error!"); + } +#endif +#endif +} + +void hsUNIXStream::Flush() +{ + if (!fRef) + return; + (void)::fflush(fRef); +} + +#endif +#endif + +////////////////////////////////////////////////////////////////////////////////////// + +plReadOnlySubStream::~plReadOnlySubStream() +{ +} + +void plReadOnlySubStream::Open( hsStream *base, UInt32 offset, UInt32 length ) +{ + fBase = base; + fOffset = offset; + fLength = length; + + fBase->SetPosition( fOffset ); + IFixPosition(); +} + +void plReadOnlySubStream::IFixPosition( void ) +{ + fPosition = fBase->GetPosition() - fOffset; +} + +hsBool plReadOnlySubStream::AtEnd() +{ + if( fPosition >= fLength ) + return true; + return false; +} + +UInt32 plReadOnlySubStream::Read(UInt32 byteCount, void* buffer) +{ + if( byteCount > GetSizeLeft() ) + { + hsThrow("Attempting to read past end of stream"); + byteCount = GetSizeLeft(); + } + + UInt32 read = fBase->Read( byteCount, buffer ); + IFixPosition(); + return read; +} + +UInt32 plReadOnlySubStream::Write(UInt32 byteCount, const void* buffer) +{ + hsAssert( false, "Write not allowed on an plReadOnlySubStream" ); + return 0; +} + +void plReadOnlySubStream::Skip(UInt32 deltaByteCount) +{ + fBase->Skip( deltaByteCount ); + IFixPosition(); +} + +void plReadOnlySubStream::Rewind() +{ + fBase->SetPosition( fOffset ); + IFixPosition(); +} + +void plReadOnlySubStream::FastFwd() +{ + fBase->SetPosition( fOffset + fLength ); + IFixPosition(); +} + +void plReadOnlySubStream::Truncate() +{ + hsAssert( false, "Can't truncate a read-only stream" ); +} + +UInt32 plReadOnlySubStream::GetEOF() +{ + return fLength; +} + +////////////////////////////////////////////////////////////////////////////////////// + +#define kRAMStreamChunkSize 1024 + +hsRAMStream::hsRAMStream() : fAppender(1, kRAMStreamChunkSize) +{ + fIter.ResetToHead(&fAppender); +} + +hsRAMStream::hsRAMStream(UInt32 chunkSize) : fAppender(1, chunkSize) +{ + fIter.ResetToHead(&fAppender); +} + +hsRAMStream::~hsRAMStream() +{ +} + +void hsRAMStream::Reset() +{ + fBytesRead = 0; + fPosition = 0; + + fAppender.Reset(); + fIter.ResetToHead(&fAppender); +} + +hsBool hsRAMStream::AtEnd() +{ + return (fBytesRead >= fAppender.Count() * fAppender.ElemSize()); +} + +UInt32 hsRAMStream::Read(UInt32 byteCount, void * buffer) +{ + if (fBytesRead + byteCount > fAppender.Count() * fAppender.ElemSize()) + { + hsThrow("Attempting to read past end of stream"); + byteCount = (fAppender.Count() * fAppender.ElemSize()) - fBytesRead; + } + + fBytesRead += byteCount; + fPosition += byteCount; + + fIter.Next(byteCount, buffer); + + return byteCount; +} + +UInt32 hsRAMStream::Write(UInt32 byteCount, const void* buffer) +{ + fPosition += byteCount; + + fAppender.PushTail(byteCount, buffer); + + return byteCount; +} + +void hsRAMStream::Skip(UInt32 deltaByteCount) +{ + fPosition += deltaByteCount; + fIter.Next(deltaByteCount, nil); +} + +void hsRAMStream::Rewind() +{ + fBytesRead = 0; + fPosition = 0; + fIter.ResetToHead(&fAppender); +} + +void hsRAMStream::Truncate() +{ + Reset(); +} + +UInt32 hsRAMStream::GetEOF() +{ + return fAppender.Count() * fAppender.ElemSize(); +} + +void hsRAMStream::CopyToMem(void* mem) +{ + (void)fAppender.CopyInto(mem); +} + +////////////////////////////////////////////////////////////////////// + +UInt32 hsNullStream::Read(UInt32 byteCount, void * buffer) +{ + hsThrow("hsNullStream: Can't read from this stream!"); + return 0; +} + +UInt32 hsNullStream::Write(UInt32 byteCount, const void* buffer) +{ + fBytesRead += byteCount; + fPosition += byteCount; + + return byteCount; +} + +void hsNullStream::Skip(UInt32 deltaByteCount) +{ + fBytesRead += deltaByteCount; + fPosition += deltaByteCount; +} + +void hsNullStream::Rewind() +{ + fBytesRead = 0; + fPosition = 0; +} + +void hsNullStream::Truncate() +{ +} + +///////////////////////////////////////////////////////////////////////////////// + +hsBool hsReadOnlyStream::AtEnd() +{ + return fData >= fStop; +} + +UInt32 hsReadOnlyStream::Read(UInt32 byteCount, void* buffer) +{ + if (fData + byteCount > fStop) + { + hsThrow("Attempting to read past end of stream"); + byteCount = GetSizeLeft(); + } + + HSMemory::BlockMove(fData, buffer, byteCount); + fData += byteCount; + fBytesRead += byteCount; + fPosition += byteCount; + return byteCount; +} + +UInt32 hsReadOnlyStream::Write(UInt32 byteCount, const void* buffer) +{ + hsThrow( "can't write to a readonly stream"); + return 0; +} + +void hsReadOnlyStream::Skip(UInt32 deltaByteCount) +{ + fBytesRead += deltaByteCount; + fPosition += deltaByteCount; + fData += deltaByteCount; + if (fData > fStop) + hsThrow( "Skip went past end of stream"); +} + +void hsReadOnlyStream::Rewind() +{ + fBytesRead = 0; + fPosition = 0; + fData = fStart; +} + +void hsReadOnlyStream::Truncate() +{ + hsThrow( "can't write to a readonly stream"); +} + +void hsReadOnlyStream::CopyToMem(void* mem) +{ + if (fData < fStop) + HSMemory::BlockMove(fData, mem, fStop-fData); +} + + +//////////////////////////////////////////////////////////////////////////////////// +UInt32 hsWriteOnlyStream::Read(UInt32 byteCount, void* buffer) +{ + hsThrow( "can't read to a writeonly stream"); + return 0; +} + +UInt32 hsWriteOnlyStream::Write(UInt32 byteCount, const void* buffer) +{ + if (fData + byteCount > fStop) + hsThrow("Write past end of stream"); + HSMemory::BlockMove(buffer, fData, byteCount); + fData += byteCount; + fBytesRead += byteCount; + fPosition += byteCount; + return byteCount; +} + + +/////////////////////////////////////////////////////////////////////////////////// + +hsQueueStream::hsQueueStream(Int32 size) : + fSize(size), + fReadCursor(0), + fWriteCursor(0) +{ + fQueue = TRACKED_NEW char[fSize]; +} + +hsQueueStream::~hsQueueStream() +{ + delete [] fQueue; +} + +UInt32 hsQueueStream::Read(UInt32 byteCount, void * buffer) +{ + hsAssert(fWriteCursor >= 0 && fWriteCursor < fSize,"hsQueueStream: WriteCursor out of range."); + hsAssert(fReadCursor >= 0 && fReadCursor < fSize,"hsQueueStream: ReadCursor out of range."); + + Int32 limit, length, total; + + limit = fWriteCursor >= fReadCursor ? fWriteCursor : fSize; + length = hsMinimum(limit-fReadCursor,byteCount); + HSMemory::BlockMove(fQueue+fReadCursor,buffer,length); + fReadCursor += length; + fReadCursor %= fSize; + total = length; + + if (length < byteCount && limit != fWriteCursor) + { + limit = fWriteCursor; + length = hsMinimum(limit,byteCount-length); + HSMemory::BlockMove(fQueue,static_cast(buffer)+total,length); + fReadCursor = length; + total += length; + } + + return total; +} + +UInt32 hsQueueStream::Write(UInt32 byteCount, const void* buffer) +{ + hsAssert(fWriteCursor >= 0 && fWriteCursor < fSize,"hsQueueStream: WriteCursor out of range."); + hsAssert(fReadCursor >= 0 && fReadCursor < fSize,"hsQueueStream: ReadCursor out of range."); + + Int32 length; + + length = hsMinimum(fSize-fWriteCursor,byteCount); + HSMemory::BlockMove(buffer,fQueue+fWriteCursor,length); + if (fReadCursor > fWriteCursor) + { +#if 0 + if (fReadCursor < fWriteCursor+length+1) + hsStatusMessage("ReadCursor wrapped\n"); +#endif + fReadCursor = hsMaximum(fReadCursor,fWriteCursor+length+1); + fReadCursor %= fSize; + } + fWriteCursor += length; + fWriteCursor %= fSize; + + if (length < byteCount) + { + Write(byteCount - length,static_cast(buffer)+length); + } + + return byteCount; +} + +void hsQueueStream::Skip(UInt32 deltaByteCount) +{ + Int32 limit, length; + + limit = fWriteCursor >= fReadCursor ? fWriteCursor : fSize; + length = hsMinimum(limit-fReadCursor,deltaByteCount); + fReadCursor += length; + + if (length < deltaByteCount && limit != fWriteCursor) + { + limit = fWriteCursor; + length = hsMinimum(limit,deltaByteCount-length); + fReadCursor = length; + } + else + { + fReadCursor %= fSize; +} +} + +void hsQueueStream::Rewind() +{ + fReadCursor = fWriteCursor+1; + fReadCursor %= fSize; +} + +void hsQueueStream::FastFwd() +{ + fReadCursor = fWriteCursor; +} + +hsBool hsQueueStream::AtEnd() +{ + return fReadCursor == fWriteCursor; +} + +/////////////////////////////////////////////////////////////////////////////// +// hsBufferedStream +/////////////////////////////////////////////////////////////////////////////// + +inline void FastByteCopy(void* dest, const void* src, UInt32 bytes) +{ + // Don't use memcpy if the read is 4 bytes or less, it's faster to just do a + // direct copy + switch (bytes) + { + case 4: + *((UInt32*)dest) = *((const UInt32*)src); + break; + case 2: + *((UInt16*)dest) = *((const UInt16*)src); + break; + case 1: + *((UInt8*)dest) = *((const UInt8*)src); + break; + default: + memcpy(dest, src, bytes); + } +} + +//#define LOG_BUFFERED + +hsBufferedStream::hsBufferedStream() +: fRef(nil) +, fFileSize(0) +, fBufferLen(0) +, fWriteBufferUsed(false) +#ifdef HS_DEBUGGING +, fBufferHits(0) +, fBufferMisses(0) +, fBufferReadIn(0) +, fBufferReadOut(0) +, fReadDirect(0) +, fLastReadPos(0) +, fFilename(nil) +, fCloseReason(nil) +#endif +{ +} + +hsBufferedStream::~hsBufferedStream() +{ +#ifdef LOG_BUFFERED + delete [] fFilename; +#endif // LOG_BUFFERED +} + +hsBool hsBufferedStream::Open(const char* name, const char* mode) +{ + hsAssert(!fRef, "hsBufferedStream:Open Stream already opened"); + fRef = hsFopen(name, mode); + if (!fRef) + return false; + + SetFileRef(fRef); + +#ifdef LOG_BUFFERED + fBufferHits = fBufferMisses = 0; + fBufferReadIn = fBufferReadOut = fReadDirect = fLastReadPos = 0; + delete [] fFilename; + fFilename = hsStrdup(name); + fCloseReason = nil; +#endif // LOG_BUFFERED + + return true; +} + +hsBool hsBufferedStream::Open(const wchar *name, const wchar *mode) +{ + hsAssert(0, "hsFileStream::Open NotImplemented for wchar"); + return false; +} + +hsBool hsBufferedStream::Close() +{ + int rtn = true; + if (fRef) + rtn = fclose(fRef); + fRef = nil; + +#ifdef LOG_BUFFERED + hsUNIXStream s; + static bool firstClose = true; + if (firstClose) + { + firstClose = false; + s.Open("log\\BufferedStream.csv", "wt"); + s.WriteString("File,Hits,Misses,Read In,Read Out,Read Direct,% Wasted,Reason\n"); + } + else + s.Open("log\\BufferedStream.csv", "at"); + + int wasted = 100; + if (fBufferReadIn + fReadDirect > 0) + wasted -= int((float(fBufferReadOut+fReadDirect) / float(fBufferReadIn+fReadDirect)) * 100.f); + + s.WriteFmt("%s,%d,%d,%u,%u,%u,%d,%s\n", + fFilename, fBufferHits, fBufferMisses, fBufferReadIn, fBufferReadOut, fReadDirect, + wasted, + fCloseReason ? fCloseReason : "Unknown"); + + s.Close(); +#endif // LOG_BUFFERED + + return !rtn; +} + +FILE* hsBufferedStream::GetFileRef() +{ + return fRef; +} + +void hsBufferedStream::SetFileRef(FILE* ref) +{ + hsAssert(ref, "bad ref"); + fRef = ref; + + fseek(fRef, 0, SEEK_END); + fFileSize = ftell(fRef); + fseek(fRef, 0, SEEK_SET); + + fBufferLen = 0; + fPosition = 0; + fWriteBufferUsed = false; +} + +UInt32 hsBufferedStream::Read(UInt32 bytes, void* buffer) +{ + hsAssert(fRef, "fRef uninitialized"); + if (!fRef || bytes == 0) + return 0; + + UInt32 numReadBytes = 0; + + while (bytes > 0 && fPosition < fFileSize) + { + // First, see if we've got anything in the buffer + if (fBufferLen > 0) + { + // Figure out how much we can copy out of the buffer + UInt32 bufferPos = fPosition % kBufferSize; + UInt32 bytesInBuffer = fBufferLen - bufferPos; + UInt32 cachedReadSize = bytesInBuffer < bytes ? bytesInBuffer : bytes; + + FastByteCopy(buffer, &fBuffer[bufferPos], cachedReadSize); + + fPosition += cachedReadSize; + numReadBytes += cachedReadSize; + bytes -= cachedReadSize; + buffer = (void*)(((char*)buffer) + cachedReadSize); + + // If we read all the data out of the buffer, set it to empty + if ((bufferPos + cachedReadSize) == fBufferLen) + fBufferLen = 0; + +#ifdef HS_DEBUGGING + fLastReadPos = fPosition; + fBufferHits++; + fBufferReadOut += cachedReadSize; +#endif + } + + // Now see if the remaining read (if any) is the size of the buffer or larger. + // If it is, read as many complete blocks as possible directly into the output buffer. + if (bytes >= kBufferSize && fPosition % kBufferSize == 0) + { + UInt32 directReadSize = bytes - (bytes % kBufferSize); + hsAssert(ftell(fRef) % kBufferSize == 0 , "read buffer is not in alignment."); + int amtRead = ::fread(buffer, 1, directReadSize, fRef); + fPosition += amtRead; + numReadBytes += amtRead; + bytes -= amtRead; + buffer = (void*)(((char*)buffer) + amtRead); +#ifdef HS_DEBUGGING + fLastReadPos = fPosition; + fReadDirect += directReadSize; +#endif + } + + // If we've got bytes left to read and we didn't pass the end of the file, buffer a new block + if (bytes > 0 && fPosition < fFileSize) + { + hsAssert(ftell(fRef) % kBufferSize == 0 , "read buffer is not in alignment."); + fBufferLen = ::fread(fBuffer, 1, kBufferSize, fRef); + +#ifdef HS_DEBUGGING + // If our last read wasn't at the start of the new buffer, it's a miss. + if (fLastReadPos != fPosition) + { + fBufferMisses++; + fBufferHits--; + } + + fBufferReadIn += fBufferLen; +#endif + } + } + + return numReadBytes; +} + +UInt32 hsBufferedStream::Write(UInt32 bytes, const void* buffer) +{ + hsAssert(fRef, "fRef uninitialized"); + fWriteBufferUsed = true; + int amtWritten = fwrite((void*)buffer, 1, bytes, fRef); + fPosition += amtWritten; + return amtWritten; +} + +hsBool hsBufferedStream::AtEnd() +{ + if (fWriteBufferUsed) + { + if (!fRef) + return true; + bool rVal; + int x = getc(fRef); + rVal = feof(fRef) != 0; + ungetc(x, fRef); + return rVal; + } + else + { + // buffered read + return fPosition >= fFileSize; + } +} + +void hsBufferedStream::Skip(UInt32 delta) +{ + if (fWriteBufferUsed) + { + // buffered write not implemented yet. + fseek(fRef, delta, SEEK_CUR); + } + else + { + UInt32 blockStart = ((fPosition + delta) / kBufferSize) * kBufferSize; + + // We've got data in the buffer, see if we can just skip in that + if (fBufferLen > 0) + { + Int32 newBufferPos = Int32(fPosition % kBufferSize) + Int32(delta); + + // If we skipped outside of our buffer, invalidate it + if (newBufferPos < 0 || UInt32(newBufferPos) >= fBufferLen) + { + fBufferLen = 0; + fseek(fRef, blockStart, SEEK_SET); + } + } + else + fseek(fRef, blockStart, SEEK_SET); + } + + fPosition += delta; +} + +void hsBufferedStream::Rewind() +{ + if (fWriteBufferUsed) + { + // buffered write not implemented yet. + fseek(fRef, 0, SEEK_SET); + } + // If the currently buffered block isn't the first one, invalidate our buffer + else if (fPosition >= kBufferSize) + fBufferLen = 0; + + fPosition = 0; +} + +UInt32 hsBufferedStream::GetEOF() +{ + if (fWriteBufferUsed) + { + if (!fRef) + return 0; + + long oldPos = ftell(fRef); + fseek(fRef, 0, SEEK_END); + UInt32 end = (UInt32)ftell(fRef); + fseek(fRef, oldPos, SEEK_SET); + + return end; + } + else + return fFileSize; +} + +void hsBufferedStream::Truncate() +{ + hsAssert(0, "hsBufferedStream::Truncate unimplemented"); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStream.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStream.h new file mode 100644 index 00000000..4b5a119c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStream.h @@ -0,0 +1,568 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsStream_Defined +#define hsStream_Defined + +#include // Included for GCC 3.2.2+ + +#include "hsTypes.h" +#include "hsMemory.h" + +namespace hsPackFileSys { +struct FileEntry; +} + +// Define this for use of Streams with Logging (commonly used w/ a packet sniffer) +// These streams log their reads to an event list +//#define STREAM_LOGGER + +#ifndef STREAM_LOGGER +#define hsReadOnlyLoggingStream hsReadOnlyStream +#define LogRead(byteCount, buffer, desc) Read(byteCount, buffer) +#define LogReadSafeString() ReadSafeString(); +#define LogReadSafeStringLong() ReadSafeStringLong(); +#define LogSkip(deltaByteCount, desc) Skip(deltaByteCount) +#define LogReadSwap(value, desc) ReadSwap(value) +#define LogReadSwapArray(count, values, desc) ReadSwap(count, values) +#define LogSubStreamStart(desc) LogVoidFunc() +#define LogSubStreamPushDesc(desc) LogVoidFunc() +#define LogSubStreamEnd() LogVoidFunc() +#define LogStringString(s) LogVoidFunc() +#endif + +class hsStream { +public: +enum { + kEolnCode = '\n', + kComment = '#' + }; +enum VDB_Type {// Virtual Database type + kVDB_GroupObject, + kVDB_Mesh + }; +protected: + UInt32 fBytesRead; + UInt32 fPosition; + + hsBool IsTokenSeparator(char c); +public: + hsStream() : fBytesRead(0), fPosition(0) {} + virtual ~hsStream(); + + virtual hsBool Open(const char *, const char * = "rb")=0; + virtual hsBool Open(const wchar *, const wchar * = L"rb")=0; + virtual hsBool Close()=0; + virtual hsBool AtEnd(); + virtual UInt32 Read(UInt32 byteCount, void * buffer) = 0; + virtual UInt32 Write(UInt32 byteCount, const void* buffer) = 0; + virtual void Skip(UInt32 deltaByteCount) = 0; + virtual void Rewind() = 0; + virtual void FastFwd(); + virtual UInt32 GetPosition() const; + virtual void SetPosition(UInt32 position); + virtual void Truncate(); + virtual void Flush() {} + +#ifdef STREAM_LOGGER + // Logging Reads & Skips + virtual UInt32 LogRead(UInt32 byteCount, void * buffer, const char* desc) { return Read(byteCount,buffer); } + virtual char* LogReadSafeString() { return ReadSafeString(); } + virtual char* LogReadSafeStringLong() { return ReadSafeStringLong(); } + virtual void LogSkip(UInt32 deltaByteCount, const char* desc) { Skip(deltaByteCount); } + + // Stream Notes for Logging + virtual void LogStringString(const char* s) { } + virtual void LogSubStreamStart(const char* desc) { } + virtual void LogSubStreamEnd() { } + virtual void LogSubStreamPushDesc(const char* desc) { } +#endif + void LogVoidFunc() { } + + // Optimization for small Reads + virtual UInt8 ReadByte(); + virtual hsBool Read4Bytes(void *buffer); // Reads 4 bytes, return true if success + virtual hsBool Read8Bytes(void *buffer); // Reads 8 bytes, return true if success + virtual hsBool Read12Bytes(void *buffer); // Reads 12 bytes, return true if success + + virtual UInt32 GetEOF(); + UInt32 GetSizeLeft(); + virtual void CopyToMem(void* mem); + virtual hsBool IsCompressed() { return false; } + + UInt32 WriteString(const char cstring[]); + UInt32 WriteFmt(const char * fmt, ...); + UInt32 WriteFmtV(const char * fmt, va_list av); + + UInt32 WriteSafeStringLong(const char *string); // uses 4 bytes for length + UInt32 WriteSafeWStringLong(const wchar_t *string); + char * ReadSafeStringLong(); + wchar_t * ReadSafeWStringLong(); + + UInt32 WriteSafeString(const char *string); // uses 2 bytes for length + UInt32 WriteSafeWString(const wchar_t *string); + char * ReadSafeString(); + wchar_t * ReadSafeWString(); + + hsBool GetToken(char *s, UInt32 maxLen=UInt32(-1), const char beginComment=kComment, const char endComment=kEolnCode); + hsBool ReadLn(char* s, UInt32 maxLen=UInt32(-1), const char beginComment=kComment, const char endComment=kEolnCode); + + bool Readbool(); + hsBool ReadBool(); + void ReadBool(int count, hsBool values[]); + UInt16 ReadSwap16(); + void ReadSwap16(int count, UInt16 values[]); + UInt32 ReadSwap32(); + void ReadSwap32(int count, UInt32 values[]); + UInt32 ReadUnswap32(); + + void Writebool(bool value); + void WriteBool(hsBool value); + void WriteBool(int count, const hsBool values[]); + void WriteByte(UInt8 value); + void WriteSwap16(UInt16 value); + void WriteSwap16(int count, const UInt16 values[]); + void WriteSwap32(UInt32 value); + void WriteSwap32(int count, const UInt32 values[]); + void WriteUnswap32(UInt32 value); + + + /* Overloaded Begin (8 & 16 & 32 int)*/ + /* yes, swapping an 8 bit value does nothing, just useful*/ + void ReadSwap(bool* value) { *value = this->ReadByte() ? true : false; } + void ReadSwap(UInt8* value) { *value = this->ReadByte(); } + void ReadSwap(int count, UInt8 values[]) { this->Read(count, values); } + void ReadSwap(UInt16* value) { *value = this->ReadSwap16(); } + void ReadSwap(int count, UInt16 values[]) { this->ReadSwap16(count, values); } + void ReadSwap(UInt32* value) { *value = this->ReadSwap32(); } + void ReadSwap(int count, UInt32 values[]) { this->ReadSwap32(count, values); } +#ifdef STREAM_LOGGER + // Begin LogReadSwaps + virtual void LogReadSwap(bool* value, const char* desc) { this->ReadSwap(value); } + virtual void LogReadSwap(UInt8* value, const char* desc) { this->ReadSwap(value); } + virtual void LogReadSwapArray(int count, UInt8 values[], const char* desc) { this->ReadSwap(count, values); } + virtual void LogReadSwap(UInt16* value, const char* desc) { this->ReadSwap(value); } + virtual void LogReadSwapArray(int count, UInt16 values[], const char* desc) { this->ReadSwap(count, values); } + virtual void LogReadSwap(UInt32* value, const char* desc) { this->ReadSwap(value); } + virtual void LogReadSwapArray(int count, UInt32 values[], const char* desc) { this->ReadSwap(count, values); } + // End LogReadSwaps +#endif + void WriteSwap(bool value) { this->Write(1,&value); } + void WriteSwap(UInt8 value) { this->Write(1,&value); } + void WriteSwap(int count, const UInt8 values[]) { this->Write(count, values); } + void WriteSwap(UInt16 value) { this->WriteSwap16(value); } + void WriteSwap(int count, const UInt16 values[]) { this->WriteSwap16(count, values); } + void WriteSwap(UInt32 value) { this->WriteSwap32(value); } + void WriteSwap(int count, const UInt32 values[]) { this->WriteSwap32(count, values); } + void ReadSwap(Int8* value) { *value = this->ReadByte(); } + void ReadSwap(int count, Int8 values[]) { this->Read(count, values); } + void ReadSwap(char* value) { *value = (char)this->ReadByte(); } + void ReadSwap(int count, char values[]) { this->Read(count, values); } + void ReadSwap(Int16* value) { *value = (Int16)this->ReadSwap16(); } + void ReadSwap(int count, Int16 values[]) { this->ReadSwap16(count, (UInt16*)values); } + void ReadSwap(Int32* value) { *value = (Int32)this->ReadSwap32(); } + void ReadSwap(int count, Int32 values[]) { this->ReadSwap32(count, (UInt32*)values); } + void ReadSwap(int* value) { *value = (Int32)this->ReadSwap32(); } + void ReadSwap(int count, int values[]) { this->ReadSwap32(count, (UInt32*)values); } +#ifdef STREAM_LOGGER + // Begin LogReadSwaps + virtual void LogReadSwap(Int8* value, const char* desc) { this->ReadSwap(value); } + virtual void LogReadSwapArray(int count, Int8 values[], const char* desc) { this->ReadSwap(count, values); } + virtual void LogReadSwap(char* value, const char* desc) { this->ReadSwap(value); } + virtual void LogReadSwapArray(int count, char values[], const char* desc) { this->ReadSwap(count, values); } + virtual void LogReadSwap(Int16* value, const char* desc) { this->ReadSwap(value); } + virtual void LogReadSwapArray(int count, Int16 values[], const char* desc) { this->ReadSwap(count, (UInt16*)values); } + virtual void LogReadSwap(Int32* value, const char* desc) { this->ReadSwap(value); } + virtual void LogReadSwapArray(int count, Int32 values[], const char* desc) { this->ReadSwap(count, (UInt32*)values); } + virtual void LogReadSwap(int* value, const char* desc) { this->ReadSwap(value); } + virtual void LogReadSwapArray(int count, int values[], const char* desc) { this->ReadSwap(count, (UInt32*)values); } + // End LogReadSwaps +#endif + void WriteSwap(Int8 value) { this->Write(1,&value); } + void WriteSwap(int count, const Int8 values[]) { this->Write(count, values); } + void WriteSwap(char value) { this->Write(1,(UInt8*)&value); } + void WriteSwap(int count, const char values[]) { this->Write(count, (UInt8*)values); } + void WriteSwap(Int16 value) { this->WriteSwap16((UInt16)value); } + void WriteSwap(int count, const Int16 values[]) { this->WriteSwap16(count, (UInt16*)values); } + void WriteSwap(Int32 value) { this->WriteSwap32((UInt32)value); } + void WriteSwap(int count, const Int32 values[]) { this->WriteSwap32(count, (UInt32*)values); } + void WriteSwap(int value) { this->WriteSwap32((UInt32)value); } + void WriteSwap(int count, const int values[]) { this->WriteSwap32(count, (UInt32*)values); } + /* Overloaded End */ + + +#if HS_CAN_USE_FLOAT + float ReadSwapFloat(); + void ReadSwapFloat(int count, float values[]); + double ReadSwapDouble(); + void ReadSwapDouble(int count, double values[]); + float ReadUnswapFloat(); + void WriteSwapFloat(float value); + void WriteSwapFloat(int count, const float values[]); + void WriteSwapDouble(double value); + void WriteSwapDouble(int count, const double values[]); + void WriteUnswapFloat(float value); + + + /* Overloaded Begin (Float)*/ + void ReadSwap(float* value) { *value = ReadSwapFloat(); } + void ReadSwap(int count, float values[]) { ReadSwapFloat(count, values); } + void ReadSwap(double* value) { *value = ReadSwapDouble(); } + void ReadSwap(int count, double values[]) { ReadSwapDouble(count, values); } +#ifdef STREAM_LOGGER + // Begin LogReadSwaps + virtual void LogReadSwap(float* value, const char* desc) { ReadSwap(value); } + virtual void LogReadSwapArray(int count, float values[], const char* desc) { ReadSwap(count, values); } + virtual void LogReadSwap(double* value, const char* desc) { ReadSwap(value); } + virtual void LogReadSwapArray(int count, double values[], const char* desc) { ReadSwap(count, values); } + // End LogReadSwaps +#endif + void WriteSwap(float value) { WriteSwapFloat(value); } + void WriteSwap(int count, const float values[]) { WriteSwapFloat(count, values); } + void WriteSwap(double value) { WriteSwapDouble(value); } + void WriteSwap(int count, const double values[]) { WriteSwapDouble(count, values); } + /* Overloaded End */ +#endif + +#if HS_SCALAR_IS_FIXED + hsFixed ReadSwapScalar() { return (hsFixed)this->ReadSwap32(); } + void ReadSwapScalar(int count, hsFixed values[]) + { + this->ReadSwap32(count, (UInt32*)values); + } + hsFixed ReadUnswapScalar() { return (hsFixed)this->ReadUnswap32(); } + + + void WriteSwapScalar(hsFixed value) { this->WriteSwap32(value); } + void WriteSwapScalar(int count, const hsFixed values[]) + { + this->WriteSwap32(count, (UInt32*)values); + } + void WriteUnswapScalar(hsFixed value) { this->WriteUnswap32(value); } + + + /* Overloaded Begin (Scalar) */ + void ReadSwap(hsFixed* value) { this->ReadSwap((UInt32*)value); } + void ReadSwap(int count, hsFixed values[]) { this->ReadSwap(count, (UInt32*)values); } + void WriteSwap(hsFixed value) { this->WriteSwap((UInt32)value); } + void WriteSwap(int count, const hsFixed values[]) { this->WriteSwap(count, (UInt32*)values); } + /* Overloaded End */ + +#else + float ReadSwapScalar() { return (float)this->ReadSwapFloat(); } + void ReadSwapScalar(int count, float values[]) + { + this->ReadSwapFloat(count, (float*)values); + } + float ReadUnswapScalar() { return (float)this->ReadUnswapFloat(); } + void WriteSwapScalar(float value) { this->WriteSwapFloat(value); } + void WriteSwapScalar(int count, const float values[]) + { + this->WriteSwapFloat(count, (float*)values); + } + void WriteUnswapScalar(float value) { this->WriteUnswapFloat(value); } +#endif + + void WriteSwapAtom(UInt32 tag, UInt32 size); + UInt32 ReadSwapAtom(UInt32* size); + + + /* Overloaded Begin (Atom)*/ + void WriteSwap(UInt32* tag, UInt32 size) { WriteSwapAtom(*tag, size); } + void ReadSwap(UInt32* tag, UInt32 *size) { *tag = ReadSwapAtom(size); } + /* Overloaded End */ + virtual void VirtualSetPosition(UInt32 pos, VDB_Type ){ SetPosition(pos); }; + virtual hsPackFileSys::FileEntry *GetFileEntry() { return nil; } // Streams from Packfiles can return a FileEntry + +}; + +class hsStreamable { +public: + virtual void Read(hsStream* stream) = 0; + virtual void Write(hsStream* stream) = 0; + virtual UInt32 GetStreamSize() = 0; +}; + +class hsFileStream: public hsStream +{ + UInt32 fRef; +#if HS_BUILD_FOR_PS2 + enum { + kBufferSize = 2*1024 + }; + UInt32 fFileSize; + char fBuffer[kBufferSize]; + Int32 fVirtualFilePointer; + Int32 fBufferBase; // offset to top of fBuffer + hsBool fBufferIsEmpty; + hsBool fWriteBufferUsed; // In write mode. fBuffer must be flush, when file was closed. +#endif + +public: + hsFileStream(); + virtual ~hsFileStream(); + virtual hsBool Open(const char *name, const char *mode = "rb"); + virtual hsBool Open(const wchar *name, const wchar *mode = L"rb"); + virtual hsBool Close(); + + virtual hsBool AtEnd(); + virtual UInt32 Read(UInt32 byteCount, void* buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void Truncate(); + + virtual UInt32 GetFileRef(); + virtual void SetFileRef(UInt32 refNum); +}; + +#if !HS_BUILD_FOR_PS2 +#if !(HS_BUILD_FOR_REFERENCE) + +class hsUNIXStream: public hsStream +{ + FILE* fRef; + char* fBuff; + +public: + hsUNIXStream(): fRef(0), fBuff(nil) {} + ~hsUNIXStream(); + virtual hsBool Open(const char* name, const char* mode = "rb"); + virtual hsBool Open(const wchar *name, const wchar *mode = L"rb"); + virtual hsBool Close(); + + virtual hsBool AtEnd(); + virtual UInt32 Read(UInt32 byteCount, void* buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual void SetPosition(UInt32 position); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void FastFwd(); + virtual void Truncate(); + virtual void Flush(); + + FILE* GetFILE() { return fRef; } + void SetFILE(FILE* file) { fRef = file; } + + virtual UInt32 GetEOF(); +}; + +// Small substream class: give it a base stream, an offset and a length, and it'll +// treat all ops as if you had a chunk from the base stream as a separate, vanilla +// stream of the given length. + +class plReadOnlySubStream: public hsStream +{ + hsStream *fBase; + UInt32 fOffset, fLength; + + void IFixPosition( void ); + +public: + plReadOnlySubStream(): fBase( nil ), fOffset( 0 ), fLength( 0 ) {} + ~plReadOnlySubStream(); + + virtual hsBool Open(const char *, const char *) { hsAssert(0, "plReadOnlySubStream::Open NotImplemented"); return false; } + virtual hsBool Open(const wchar *, const wchar *) { hsAssert(0, "plReadOnlySubStream::Open NotImplemented"); return false; } + void Open( hsStream *base, UInt32 offset, UInt32 length ); + virtual hsBool Close() { fBase = nil; fOffset = 0; fLength = 0; return true; } + virtual hsBool AtEnd(); + virtual UInt32 Read(UInt32 byteCount, void* buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void FastFwd(); + virtual void Truncate(); + + virtual UInt32 GetEOF(); +}; + +#endif +#endif + +class hsRAMStream : public hsStream { + hsAppender fAppender; + hsAppenderIterator fIter; +public: + hsRAMStream(); + hsRAMStream(UInt32 chunkSize); + virtual ~hsRAMStream(); + + virtual hsBool Open(const char *, const char *) { hsAssert(0, "hsRAMStream::Open NotImplemented"); return false; } + virtual hsBool Open(const wchar *, const wchar *) { hsAssert(0, "hsRAMStream::Open NotImplemented"); return false; } + virtual hsBool Close() { hsAssert(0, "hsRAMStream::Close NotImplemented"); return false; } + + + virtual hsBool AtEnd(); + virtual UInt32 Read(UInt32 byteCount, void * buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void Truncate(); + + virtual UInt32 GetEOF(); + virtual void CopyToMem(void* mem); + + void Reset(); // clears the buffers +}; + +class hsNullStream : public hsStream { +public: + + virtual hsBool Open(const char *, const char *) { return true; } + virtual hsBool Open(const wchar *, const wchar *) { return true; } + virtual hsBool Close() { return true; } + + virtual UInt32 Read(UInt32 byteCount, void * buffer); // throw's exception + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void Truncate(); + + UInt32 GetBytesWritten() const { return fBytesRead; } + void Reset( ) { fBytesRead = 0; } +}; + +// read only mem stream +class hsReadOnlyStream : public hsStream { +protected: + char* fStart; + char* fData; + char* fStop; +public: + hsReadOnlyStream(int size, const void* data) { Init(size, data); } + hsReadOnlyStream() {} + + virtual void Init(int size, const void* data) { fStart=((char*)data); fData=((char*)data); fStop=((char*)data + size); } + virtual hsBool Open(const char *, const char *) { hsAssert(0, "hsReadOnlyStream::Open NotImplemented"); return false; } + virtual hsBool Open(const wchar *, const wchar *) { hsAssert(0, "hsReadOnlyStream::Open NotImplemented"); return false; } + virtual hsBool Close() { hsAssert(0, "hsReadOnlyStream::Close NotImplemented"); return false; } + virtual hsBool AtEnd(); + virtual UInt32 Read(UInt32 byteCount, void * buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); // throws exception + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void Truncate(); + virtual UInt32 GetBytesRead() const { return fBytesRead; } + virtual UInt32 GetEOF() { return (UInt32)(fStop-fStart); } + virtual void CopyToMem(void* mem); +}; + +// write only mem stream +class hsWriteOnlyStream : public hsReadOnlyStream { +public: + hsWriteOnlyStream(int size, const void* data) : hsReadOnlyStream(size, data) {} + hsWriteOnlyStream() {} + + virtual hsBool Open(const char *, const char *) { hsAssert(0, "hsWriteOnlyStream::Open NotImplemented"); return false; } + virtual hsBool Open(const wchar *, const wchar *) { hsAssert(0, "hsWriteOnlyStream::Open NotImplemented"); return false; } + virtual hsBool Close() { hsAssert(0, "hsWriteOnlyStream::Close NotImplemented"); return false; } + virtual UInt32 Read(UInt32 byteCount, void * buffer); // throws exception + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual UInt32 GetBytesRead() const { return 0; } + virtual UInt32 GetBytesWritten() const { return fBytesRead; } +}; + +// circular queue stream +class hsQueueStream : public hsStream { +private: + char* fQueue; + UInt32 fReadCursor; + UInt32 fWriteCursor; + UInt32 fSize; + +public: + hsQueueStream(Int32 size); + ~hsQueueStream(); + + virtual hsBool Open(const char *, const char *) { hsAssert(0, "hsQueueStream::Open NotImplemented"); return false; } + virtual hsBool Open(const wchar *, const wchar *) { hsAssert(0, "hsQueueStream::Open NotImplemented"); return false; } + virtual hsBool Close() { hsAssert(0, "hsQueueStream::Close NotImplemented"); return false; } + + virtual UInt32 Read(UInt32 byteCount, void * buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void FastFwd(); + virtual hsBool AtEnd(); + + UInt32 GetSize() { return fSize; } + const char* GetQueue() { return fQueue; } + UInt32 GetReadCursor() { return fReadCursor; } + UInt32 GetWriteCursor() { return fWriteCursor; } +}; + +class hsBufferedStream : public hsStream +{ + FILE* fRef; + UInt32 fFileSize; + + enum { kBufferSize = 2*1024 }; + char fBuffer[kBufferSize]; + // If the buffer is empty, this is zero. Otherwise it is the size of the + // buffer (if we read a full block), or something less than that if we read + // a partial block at the end of the file. + UInt32 fBufferLen; + + hsBool fWriteBufferUsed; + +#ifdef HS_DEBUGGING + // For doing statistics on how efficient we are + int fBufferHits, fBufferMisses; + UInt32 fBufferReadIn, fBufferReadOut, fReadDirect, fLastReadPos; + char* fFilename; + const char* fCloseReason; +#endif + +public: + hsBufferedStream(); + virtual ~hsBufferedStream(); + + virtual hsBool Open(const char* name, const char* mode = "rb"); + virtual hsBool Open(const wchar* name, const wchar* mode = L"rb"); + virtual hsBool Close(); + + virtual hsBool AtEnd(); + virtual UInt32 Read(UInt32 byteCount, void* buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void Truncate(); + virtual UInt32 GetEOF(); + + FILE* GetFileRef(); + void SetFileRef(FILE* file); + + // Something optional for when we're doing stats. Will log the reason why + // the file was closed. Really just for plRegistryPageNode. + void SetCloseReason(const char* reason) + { +#ifdef HS_DEBUGGING + fCloseReason = reason; +#endif + } +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStringTokenizer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStringTokenizer.cpp new file mode 100644 index 00000000..8fb83fcf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsStringTokenizer.cpp @@ -0,0 +1,293 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// hsStringTokenizer.cpp + +#include "hsStringTokenizer.h" +#include "hsUtils.h" + +// String Tokenizer routines +hsStringTokenizer::hsStringTokenizer(const char *string, const char *seps) : +fQAsTok(true), +fInQuote(false), +fString(nil), +fSeps(nil), +fLastTerminator(nil) +{ + Reset(string,seps); +} + +hsStringTokenizer::~hsStringTokenizer() +{ + delete [] fString; + delete [] fSeps; +} + +hsBool hsStringTokenizer::HasMoreTokens() +{ + return (*fTok != 0); +} + +inline hsBool hsStringTokenizer::IsSep(char c) +{ + if (!fQAsTok || !fInQuote) + { + if ( fCheckAlphaNum || !isalnum(c) ) + { + for (Int32 i=0; i. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// hsStringTokenizer.h +#ifndef _hsStringTokenizer_Included_ +#define _hsStringTokenizer_Included_ + +#include "hsTypes.h" + +class hsStringTokenizer +{ +private: + char *fSeps; + char *fTok; + char *fLastTerminator; + char fLastRep; + + Int32 fNumSeps; + hsBool fQAsTok; + hsBool fInQuote; + hsBool fCheckAlphaNum; +public: + hsStringTokenizer(const char *string=nil, const char *seps=nil); + ~hsStringTokenizer(); + char *next(); + hsBool Next( char *token, UInt32 maxTokLen ); + hsBool HasMoreTokens(); + void Reset(const char *string, const char *seps); + void ParseQuotes(hsBool qAsTok); + + char *GetRestOfString( void ) const { return fTok; } + + char *fString; + + void RestoreLastTerminator( void ); + +private: + hsBool IsSep(char c); +}; + +class hsWStringTokenizer +{ +private: + wchar *fSeps; + wchar *fTok; + wchar *fLastTerminator; + wchar fLastRep; + + Int32 fNumSeps; + hsBool fQAsTok; + hsBool fInQuote; + hsBool fCheckAlphaNum; +public: + hsWStringTokenizer(const wchar *string=nil, const wchar *seps=nil); + ~hsWStringTokenizer(); + wchar *next(); + hsBool Next( wchar *token, UInt32 maxTokLen ); + hsBool HasMoreTokens(); + void Reset(const wchar *string, const wchar *seps); + void ParseQuotes(hsBool qAsTok); + + wchar *GetRestOfString( void ) const { return fTok; } + + wchar *fString; + + void RestoreLastTerminator( void ); + +private: + hsBool IsSep(wchar c); +}; + +#endif // _hsStringTokenizer_Included_ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTempPointer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTempPointer.h new file mode 100644 index 00000000..ce0bc1f1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTempPointer.h @@ -0,0 +1,158 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsTempPointer_inc +#define hsTempPointer_inc + +#include "hsMemory.h" +#include "hsExceptions.h" + +template class hsTempPointer { +private: + T** fArray; + + UInt32 fCurrBlock; + UInt32 fNumBlockAlloc; + + UInt32 fCurrElem; + UInt32 fNumElemAlloc; + + UInt32 fGrowBy; // def = 0, to double + UInt32 fMinSize; // def = 1 + + hsTempPointer& operator=(const hsTempPointer&); + + void IConsolidate(); + void IGrow(); + +public: + hsTempPointer(UInt32 minSize = 1, UInt32 growBy = 0); + ~hsTempPointer(); + + void Reset(); + + T* Next(); + T* Array(int n); +}; + +template +hsTempPointer::~hsTempPointer() +{ + int i; + for( i = 0; i <= fCurrBlock; i++ ) + delete [] fArray[i]; + delete [] fArray; +} + +template +hsTempPointer::hsTempPointer(UInt32 minSize, UInt32 growBy) +{ + fGrowBy = growBy; + fMinSize = minSize; + + fArray = TRACKED_NEW T*[2]; + fNumBlockAlloc = 2; + fCurrBlock = 0; + + fArray[fCurrBlock] = TRACKED_NEW T[fMinSize]; + fNumElemAlloc = minSize; + + fCurrElem = 0; +} + + +template +void hsTempPointer::IConsolidate() +{ + hsAssert(fCurrBlock > 0, "Shouldn't consolidate when nothing to do"); + + UInt32 numUsed = fCurrBlock * fNumElemAlloc + fCurrElem; + + UInt32 newSize = fNumElemAlloc; + if( !fGrowBy ) + { + while( newSize <= numUsed ) + newSize <<= 1; + } + else + { + while( newSize <= numUsed ) + newSize += fGrowBy; + } + int i; + for( i = 0; i <= fCurrBlock; i++ ) + delete [] fArray[i]; + + fArray[0] = TRACKED_NEW T[newSize]; + fNumElemAlloc = newSize; + fCurrElem = 0; + fCurrBlock = 0; +} + +template +void hsTempPointer::IGrow() +{ + if( ++fCurrBlock >= fNumBlockAlloc ) + { + T** newBlockArray = TRACKED_NEW T*[fNumBlockAlloc <<= 1]; + HSMemory::BlockMove(fArray, newBlockArray, fCurrBlock * sizeof(*fArray)); + delete [] fArray; + fArray = newBlockArray; + } + fArray[fCurrBlock] = TRACKED_NEW T[fNumElemAlloc]; + fCurrElem = 0; + +} + +template +T* hsTempPointer::Next() +{ + if( fCurrElem >= fNumElemAlloc ) + IGrow(); + return fArray[fCurrBlock] + fCurrElem++; +} + +template +T* hsTempPointer::Array(int n) +{ + // minSize (on constructor) should be greater than max n + hsDebugCode(hsThrowIfBadParam((UInt32)n > (UInt32)fNumElemAlloc);) + if( fCurrElem + n >= fNumElemAlloc ) + IGrow(); + int idx = fCurrElem; + fCurrElem += n; + return fArray[fCurrBlock] + idx; +} + +template +void hsTempPointer::Reset() +{ + if( fCurrBlock > 0 ) + IConsolidate(); + fCurrElem = 0; +} + +#endif // hsTempPointer_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTemplates.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTemplates.cpp new file mode 100644 index 00000000..7f3f5454 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTemplates.cpp @@ -0,0 +1,320 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTemplates.h" +#include "hsUtils.h" + + +//////////////////////////////////////////////////////////////////////////////// +// +//hsTempString::hsTempString(KStringFormatConstructor, char * fmt, ...) +//{ +// va_list args; +// va_start(args,fmt); +// fStr = hsFormatStrV(fmt,args); +// va_end(args); +//} +// +//hsTempString::hsTempString(KStringFormatVConstructor, char * fmt, va_list args) +//{ +// fStr = hsFormatStrV(fmt,args); +//} +// +//////////////////////////////////////////////////////////////////////////////// +//void hsTempStringF::Format(char * fmt, ...) +//{ +// delete [] fStr; +// va_list args; +// va_start(args,fmt); +// fStr = hsFormatStrV(fmt,args); +// va_end(args); +//} +// +//hsTempStringF::hsTempStringF(char * fmt, ...) +//{ +// va_list args; +// va_start(args,fmt); +// fStr = hsFormatStrV(fmt,args); +// va_end(args); +//} + +////////////////////////////////////////////////////////////////////////////// + +#ifdef HS_DEBUGTARRAY + + +hsDlistNode *hsDlistNode::fpFirst=0; +hsDlistNode *hsDlistNode::fpLast=0; +UInt32 hsDlistNode::fcreated=0; +UInt32 hsDlistNode::fdestroyed=0; +static int NodeKnt = 0; + +void RemoveNode(void *pthing) +{ + + + hsDlistNode * pNode = hsDlistNode::fpFirst; + + while (pNode) + { + if (pNode->fpThing == pthing) + { + pNode->RemoveNode();// + delete pNode; + return; + + } + pNode = pNode->GetNext(); + } + +} + +void hsDlistNode::AddNode() +{ + fcreated++; + if (!fpFirst) fpFirst = this; + fpPrev = fpLast; + if (fpLast) + fpLast->fpNext = this; + fpLast = this; +} + +void hsDlistNode::RemoveNode() +{ + fdestroyed++; +/* + if (!NodeKnt) + { fpFirst = 0; + fpLast = 0; + return; + } +*/ + if (fpPrev) + fpPrev->fpNext = fpNext; + if (fpNext) + fpNext->fpPrev = fpPrev; + if (this == fpFirst) + fpFirst = fpNext; + if (this == fpLast) + fpLast = fpPrev; +/* + if (NodeKnt == 1) + { + if (fpLast) fpFirst = fpLast; + if (fpFirst) fpLast = fpFirst; + fpFirst->fpNext = 0; + fpFirst->fpPrev = 0; + } +*/ +} + + +void TArrayStats() +{ + + char *GetTypeName(); + char *GetSizeOf(); + + hsDlistNode * pNode = hsDlistNode::fpFirst; + char fnm[512]; + sprintf(fnm,"Reports\\%s.txt","TArray"); + FILE * DumpLogFile = fopen( fnm, "w" ); + if (!DumpLogFile) return; + int i=0; + int totWaste=0; + int totUse =0; + fprintf(DumpLogFile,"TArray Stats, Total Created: %d, Currently Used %d\n-----------------------\n", hsDlistNode::fcreated , hsDlistNode::fcreated - hsDlistNode::fdestroyed); + int notUsed =0; + int used = 0; + int totCount=0; + while (pNode) + { + i++; + if (pNode->fpThing) + { + if (((hsTArrayBase *)(pNode->fpThing))->fTotalCount) + { + used++; + totCount += ((hsTArrayBase *)(pNode->fpThing))->fUseCount; + int siz = ((hsTArrayBase *)(pNode->fpThing))->GetSizeOf(); + int use = ((hsTArrayBase *)(pNode->fpThing))->fUseCount; + int tot = ((hsTArrayBase *)(pNode->fpThing))->fTotalCount; + + int waste =0; + + waste = (tot - use) * siz; + totUse += (use * siz); + totWaste += waste; + fprintf(DumpLogFile,"[%d] SizeObject %d, Uses %d, Allocs %d, Waste %d\n", i, siz, use, tot, waste); + } + else + notUsed++; + + } + pNode = pNode->GetNext(); +// if (pNode ==hsDlistNode::fpFirst) // dont loop + } + fprintf(DumpLogFile,"TOTAL use %d, waste %d\n", totUse,totWaste); + fprintf(DumpLogFile,"Empty Ones %d, waste %d\n", notUsed, notUsed * 12 ); // 12 aprox size of TArray + if (used) + fprintf(DumpLogFile,"Average Use %d\n", totCount / used); + + fclose(DumpLogFile); + +} + +void LargeArrayStats() +{ + + char *GetTypeName(); + char *GetSizeOf(); + + hsDlistNode * pNode = hsDlistNode::fpFirst; + char fnm[512]; + sprintf(fnm,"Reports\\%s.txt","TArray"); + FILE * DumpLogFile = fopen( fnm, "w" ); + if (!DumpLogFile) return; + int i=0; + int totWaste=0; + int totUse =0; + fprintf(DumpLogFile,"TArray Stats, Total Created: %d, Currently Used %d\n-----------------------\n", hsDlistNode::fcreated , hsDlistNode::fcreated - hsDlistNode::fdestroyed); + int notUsed =0; + int used = 0; + int totCount=0; + while (pNode) + { + i++; + if (pNode->fpThing) + { + if (((hsLargeArrayBase *)(pNode->fpThing))->fTotalCount) + { + used++; + totCount += ((hsLargeArrayBase *)(pNode->fpThing))->fUseCount; + int siz = ((hsLargeArrayBase *)(pNode->fpThing))->GetSizeOf(); + int use = ((hsLargeArrayBase *)(pNode->fpThing))->fUseCount; + int tot = ((hsLargeArrayBase *)(pNode->fpThing))->fTotalCount; + + int waste =0; + + waste = (tot - use) * siz; + totUse += (use * siz); + totWaste += waste; + fprintf(DumpLogFile,"[%d] SizeObject %d, Uses %d, Allocs %d, Waste %d\n", i, siz, use, tot, waste); + } + else + notUsed++; + + } + pNode = pNode->GetNext(); +// if (pNode ==hsDlistNode::fpFirst) // dont loop + } + fprintf(DumpLogFile,"TOTAL use %d, waste %d\n", totUse,totWaste); + fprintf(DumpLogFile,"Empty Ones %d, waste %d\n", notUsed, notUsed * 12 ); // 12 aprox size of TArray + if (used) + fprintf(DumpLogFile,"Average Use %d\n", totCount / used); + + fclose(DumpLogFile); + +} + +char * hsTArrayBase::GetTypeName() { return ""; } + +int hsTArrayBase::GetSizeOf(void) { return 0; } + +hsTArrayBase::hsTArrayBase():fUseCount(0), fTotalCount(0) +{ + self = TRACKED_NEW hsDlistNode(this); +} + +hsTArrayBase::~hsTArrayBase() +{ + if (self) + { self->RemoveNode(); + delete self; + } + else + RemoveNode(this); // Self got clobbered find it the hard way +} + +char * hsLargeArrayBase::GetTypeName() { return ""; } + +int hsLargeArrayBase::GetSizeOf(void) { return 0; } + +hsLargeArrayBase::hsLargeArrayBase():fUseCount(0), fTotalCount(0) +{ + self = TRACKED_NEW hsDlistNode(this); +} + +hsLargeArrayBase::~hsLargeArrayBase() +{ + if (self) + { self->RemoveNode(); + delete self; + } + else + RemoveNode(this); // Self got clobbered find it the hard way +} + +#else + +void TArrayStats() {} +void LargeArrayStats() {} + +#endif //HS_DEBUGTARRAY + + + +void hsTArrayBase::GrowArraySize(UInt16 newCount) +{ +#if 1 + if (newCount < 8) + fTotalCount = newCount; // Hey its small don't loose sleep over the copy time + else if( newCount & 0x8000 ) // Hey, its huge, give it half way to maxed out + fTotalCount = newCount + ((0xffff - newCount) >> 1); + else + fTotalCount = newCount + (newCount /2); // Give it Half again as much +#endif + +#if 0 + do { + fTotalCount <<= 1; + } while (fTotalCount < newCount); + +#endif +} + +void hsLargeArrayBase::GrowArraySize(UInt32 newCount) +{ +#if 1 + if (newCount < 8) + fTotalCount = newCount; // Hey its small don't loose sleep over the copy time + else + fTotalCount = newCount + (newCount >> 1); // Give it Half again as much +#endif + +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTemplates.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTemplates.h new file mode 100644 index 00000000..43fa86d5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTemplates.h @@ -0,0 +1,1252 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsTemplatesDefined +#define hsTemplatesDefined + +#include "hsExceptions.h" +#include "hsMemory.h" +#include "hsRefCnt.h" +#include "hsUtils.h" + +#include + + +#ifdef HS_DEBUGGING +// #define HS_DEBUGTARRAY +#endif + +#ifdef HS_DEBUGTARRAY + + // just a quickie d-link list class for debugging +class hsDlistNode +{ +public: + static hsDlistNode *fpFirst; + static hsDlistNode *fpLast; + static UInt32 fcreated; + static UInt32 fdestroyed; + + void *fpThing; + hsDlistNode *fpPrev; + hsDlistNode *fpNext; + hsDlistNode(void *tng): fpThing(tng), fpNext(0), fpPrev(0) { AddNode(); } + void AddNode(); + void RemoveNode(); + hsDlistNode *GetNext() { return fpNext; } +}; + +#endif + +// Use this for a pointer to a single object of class T allocated with new +template class hsTempObject { + T* fObject; +public: + hsTempObject(): fObject(nil){} + hsTempObject(T* p) : fObject(p) {} + hsTempObject(const hsTempObject & that) + {*this=that;} + ~hsTempObject() { delete fObject; } + hsTempObject & operator=(const hsTempObject & src) + { + if (fObject!=src.fObject) + { + delete fObject; + fObject=src.fObject; + } + return *this; + } + hsTempObject & operator=(T * ptr) + { + if (fObject!=ptr) + { + delete fObject; + fObject=ptr; + } + return *this; + } + operator T*() const { return fObject; } + operator T*&() { return fObject; } + operator const T&() const { return *fObject; } + operator bool() const { return fObject!=nil;} + T * operator->() const { return fObject; } + T * operator *() const { return fObject; } +}; + +// Use this for subclasses of hsRefCnt, where UnRef should be called at the end +template class hsTempRef { + T* fObject; +public: + hsTempRef(T* object = nil) : fObject(object) {} + ~hsTempRef() { if (fObject) fObject->UnRef(); } + + operator T*() const { return fObject; } + T* operator->() const { return fObject; } + + T* operator=(T* src) { hsRefCnt_SafeUnRef(fObject); fObject = src; return fObject; } +}; + +// Use this for an array of objects of class T allocated with new[] +template class hsTempArray { + T* fArray; + UInt32 fCount; + hsTempArray& operator=(const hsTempArray&); +public: + hsTempArray(long count) : fArray(TRACKED_NEW T[count]), fCount(count) + { + } + hsTempArray(long count, T initValue) : fArray(TRACKED_NEW T[count]), fCount(count) + { + for (int i = 0; i < count; i++) + fArray[i] = initValue; + } + hsTempArray(T* p) : fArray(p), fCount(1) + { + } + hsTempArray() : fArray(nil), fCount(0) + { + } + ~hsTempArray() + { + delete[] fArray; + } + + operator T*() const { return fArray; } + T* GetArray() const { return fArray; } + void Accomodate(UInt32 count) + { + if (count > fCount) + { delete[] fArray; + fCount = count; + fArray = TRACKED_NEW T[count]; + } + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// +//// Like hsTempArray, but more useful when working with char * type arrays. +//enum KStringFormatConstructor {kFmtCtor}; +//enum KStringFormatVConstructor {kFmtVCtor}; +//class hsTempString +//{ +//public: +// char * fStr; +// hsTempString(): fStr(nil){} +// hsTempString(char * p) : fStr(p) {} +// hsTempString(const char * p) { fStr=hsStrcpy(p); } +// hsTempString(KStringFormatConstructor, char * fmt, ...); +// hsTempString(KStringFormatVConstructor, char * fmt, va_list args); +// hsTempString(const hsTempString & other):fStr(hsStrcpy(other.fStr)){} +// virtual ~hsTempString() { delete [] fStr; } +// hsTempString & operator=(char * ptr) +// { +// if (fStr!=ptr) +// { +// delete [] fStr; +// fStr=ptr; +// } +// return *this; +// } +// hsTempString & operator=(const hsTempString & other) +// { +// delete [] fStr; +// fStr=hsStrcpy(other.fStr); +// return *this; +// } +// operator char *() const { return fStr; } +// operator char *&() { return fStr; } +// operator const char *() const { return fStr; } +// operator bool() const { return fStr!=nil;} +// char * operator *() const { return fStr; } +// const char* c_str() const { return fStr; } +// char* c_str() { return fStr; } +//}; +// +//// shorthand +//typedef hsTempString tmpstr_t; +// +//class hsTempStringF : public hsTempString +//{ +//public: +// hsTempStringF(char * fmt, ...); +// void Format(char * fmt, ...); +// +// hsTempString & operator=(char * ptr) { return hsTempString::operator=(ptr); } +// hsTempString & operator=(const hsTempString & other) { return hsTempString::operator=(other); } +// hsTempString & operator=(const hsTempStringF & other) { return hsTempString::operator=(other); } +// operator char *() const { return fStr; } +// operator char *&() { return fStr; } +// operator const char *() const { return fStr; } +// operator bool() const { return fStr!=nil;} +// char * operator *() const { return fStr; } +//}; + +////////////////////////////////////////////////////////////////////////////// + +template class hsDynamicArray { +private: + Int32 fCount; + T* fArray; + + hsDynamicArray& operator=(const hsDynamicArray&); // don't allow assignment +public: + enum { kMissingIndex = -1 }; + + hsDynamicArray(Int32 count = 0); + virtual ~hsDynamicArray(); + + Int32 GetCount() const { return fCount; } + hsBool IsEmpty() const { return fCount == 0; } + const T& Get(Int32 index) const; + Int32 Get(Int32 index, Int32 count, T data[]) const; + Int32 Find(const T&) const; // returns kMissingIndex if not found + + void SetCount(Int32 count); + T& operator[]( Int32 index ); + Int32 Append(const T&); + Int32 InsertAtIndex(UInt32 index, const T& obj); + Int32 Push(const T&); + Int32 Pop(T*); + void Remove(Int32); + void Reset(); // clears out everything + + T* AcquireArray() { return fArray; } + T* DetachArray() { T* t = fArray; fCount = 0; fArray = nil; return t; } + void ReleaseArray(T*) {} + hsDynamicArray* Copy(hsDynamicArray* dst = nil) const; + + T* ForEach(Boolean (*proc)(T&)); + T* ForEach(Boolean (*proc)(T&, void* p1), void* p1); + T* ForEach(Boolean (*proc)(T&, void* p1, void* p2), void* p1, void* p2); +}; + +// Use this for block of memory allocated with HSMemory::New() +template class hsDynamicArrayAccess { + T* fArray; + hsDynamicArray *fArrayObj; + hsDynamicArrayAccess& operator=(const hsDynamicArrayAccess&); +public: + hsDynamicArrayAccess(hsDynamicArray *array) : fArrayObj(array) { fArray = array->AcquireArray();} + ~hsDynamicArrayAccess() { fArrayObj->ReleaseArray(fArray); } + + operator T*() const { return fArray; } + T* operator->() const { return fArray; } +}; + + +template + hsDynamicArray::hsDynamicArray(Int32 count) +{ + fCount = count; + fArray = nil; + if (count) + fArray = TRACKED_NEW T[ count ]; +} + +template +hsDynamicArray::~hsDynamicArray() +{ + this->Reset(); +} + +template +void hsDynamicArray::SetCount(Int32 count) +{ + if (fCount != count) + { if (count == 0) + this->Reset(); + else + { T* newArray = TRACKED_NEW T[count]; + + if (fArray) + { int copyCount = hsMinimum(count, fCount); + + for (int i = 0; i < copyCount; i++) + newArray[i] = fArray[i]; + delete[] fArray; + } + fCount = count; + fArray = newArray; + } + } +} + +template T& hsDynamicArray::operator[]( Int32 index ) +{ + hsDebugCode(hsThrowIfBadParam((UInt32)index >= (UInt32)fCount);) + + return fArray[index]; +} + +template const T& hsDynamicArray::Get( Int32 index ) const +{ + hsDebugCode(hsThrowIfBadParam((UInt32)index >= (UInt32)fCount);) + + return fArray[index]; +} + +template +Int32 hsDynamicArray::Get(Int32 index, Int32 count, T data[]) const +{ + if (count > 0) + { hsThrowIfNilParam(data); + hsThrowIfBadParam((UInt32)index >= fCount); + + if (index + count > fCount) + count = fCount - index; + for (int i = 0; i < count; i++) + data[i] = fArray[i + index]; + } + return count; +} + +template +Int32 hsDynamicArray::Find(const T& obj) const +{ + for (int i = 0; i < fCount; i++) + if (fArray[i] == obj) + return i; + return kMissingIndex; +} + +template +void hsDynamicArray::Remove(Int32 index) +{ + hsThrowIfBadParam((UInt32)index >= (UInt32)fCount); + + T rVal = fArray[index]; + + if (--fCount > 0) + { + int i; + T* newList = TRACKED_NEW T[fCount]; + for(i = 0 ; i < index;i++) + newList[i] = fArray[i]; + for (i = index; i < fCount; i++) + newList[i] = fArray[i + 1]; + delete [] fArray; + fArray = newList; + } + else + { delete[] fArray; + fArray = nil; + } +} + +template +Int32 hsDynamicArray::Pop(T *obj) +{ + hsThrowIfBadParam(this->IsEmpty()); + + *obj = fArray[0]; + Remove(0); + return fCount; +} + + +template +Int32 hsDynamicArray::Push(const T& obj) +{ + if (fArray) + { + T* newList = TRACKED_NEW T[fCount+1]; + for(int i = 0 ; i < fCount; i++) + newList[i+1] = fArray[i]; + newList[0] = obj; + delete [] fArray; + fArray = newList; + } + else + { hsAssert(fCount == 0, "mismatch"); + fArray = TRACKED_NEW T[1]; + fArray[0] = obj; + } + return ++fCount; +} + +template +Int32 hsDynamicArray::Append(const T& obj) +{ + if (fArray) + { T* newList = TRACKED_NEW T[fCount + 1]; + + for (int i = 0; i < fCount; i++) + newList[i] = fArray[i]; + newList[fCount] = obj; + delete [] fArray; + fArray = newList; + } + else + { hsAssert(fCount == 0, "mismatch"); + fArray = TRACKED_NEW T[1]; + fArray[0] = obj; + } + return ++fCount; +} + + +template +Int32 hsDynamicArray::InsertAtIndex(UInt32 index, const T& obj) +{ + if (fArray) + { + hsAssert(UInt32(fCount) >= index, "Index too large for array"); + T* newList = TRACKED_NEW T[fCount + 1]; + unsigned i; + for ( i = 0; i < index; i++) + newList[i] = fArray[i]; + newList[index] = obj; + for ( i = index; i < UInt32(fCount); i++) + newList[i+1] = fArray[i]; + + delete [] fArray; + fArray = newList; + } + else + { + hsAssert(fCount == 0, "mismatch"); + hsAssert(index ==0,"Can't insert at non zero index in empty array"); + fArray = TRACKED_NEW T[1]; + fArray[0] = obj; + } + return ++fCount; +} + +template void hsDynamicArray::Reset() +{ + if (fArray) + { delete[] fArray; + fArray = nil; + fCount = 0; + } +} + +template +hsDynamicArray* hsDynamicArray::Copy(hsDynamicArray* dst) const +{ + if (dst == nil) + dst = TRACKED_NEW hsDynamicArray; + else + dst->Reset(); + + dst->SetCount(this->fCount); + for (int i = 0; i < this->fCount; i++) + dst->fArray[i] = this->fArray[i]; + + return dst; +} + +template T* hsDynamicArray::ForEach(Boolean (*proc)(T&)) +{ + for (int i = 0; i < fCount; i++) + if (proc(fArray[i])) + return &fArray[i]; + return nil; +} + +template T* hsDynamicArray::ForEach(Boolean (*proc)(T&, void* p1), void * p1) +{ + for (int i = 0; i < fCount; i++) + if (proc(fArray[i], p1)) + return &fArray[i]; + return nil; +} + +template T* hsDynamicArray::ForEach(Boolean (*proc)(T&, void* p1, void* p2), void *p1, void *p2) +{ + for (int i = 0; i < fCount; i++) + if (proc(fArray[i], p1, p2)) + return &fArray[i]; + return nil; +} + +//////////////////////////////////////////////////////////////////////////////// + +class hsTArrayBase +{ +protected: + UInt16 fUseCount; + UInt16 fTotalCount; + + void GrowArraySize(UInt16 nSize); + +#ifdef HS_DEBUGTARRAY + hsTArrayBase(); + virtual char *GetTypeName(); + virtual int GetSizeOf(); + hsDlistNode *self; + friend void TArrayStats(); + virtual ~hsTArrayBase(); +#else + hsTArrayBase():fUseCount(0), fTotalCount(0){} +#endif + +public: + UInt16 GetNumAlloc() const { return fTotalCount; } +}; + + +template class hsTArray : public hsTArrayBase +{ + T* fArray; + + inline void IncCount(int index, int count); + inline void DecCount(int index, int count); + +#ifdef HS_DEBUGGING + #define hsTArray_ValidateCount(count) hsAssert(((count) >= 0)&&((count) <= 0xffffL), "bad count") + #define hsTArray_ValidateIndex(index) hsAssert(unsigned(index) < fUseCount, "bad index") + #define hsTArray_ValidateInsertIndex(index) hsAssert(unsigned(index) <= fUseCount, "bad index") + #define hsTArray_Validate(condition) hsAssert(condition, "oops") + + #ifdef HS_DEBUGTARRAY + virtual int GetSizeOf() { return sizeof(T); } + #endif +#else + #define hsTArray_ValidateCount(count) + #define hsTArray_ValidateIndex(index) + #define hsTArray_ValidateInsertIndex(index) + #define hsTArray_Validate(condition) +#endif +public: + hsTArray() : fArray(nil) {} + inline hsTArray(int count); + inline hsTArray(const hsTArray& src); + ~hsTArray() { if (fArray) delete[] fArray; + } + inline void Expand(int NewTotal); + + inline hsTArray& operator=(const hsTArray& src); + bool operator==(const hsTArray& src) const; // checks sizes and contents + + // Swaps the internal data (including the fArray POINTER) with the data from the array given + void Swap( hsTArray& src ); + + void Set(int index, const T& item) { hsTArray_ValidateIndex(index); fArray[index]=item; } + const T& Get(int index) const { hsTArray_ValidateIndex(index); return fArray[index]; } + T& operator[](int index) const { hsTArray_ValidateIndex(index); return fArray[index]; } + + T* FirstIter() { return &fArray[0]; } + T* StopIter() { return &fArray[fUseCount]; } + + int Count() const { return fUseCount; } + int GetCount() const { return fUseCount; } + inline void SetCount(int count); + + inline void SetCountAndZero(int count); // does block clear, don't use for types with vtbl + inline void ExpandAndZero(int count); // Same as set count and zero except won't decrease + // usecount + inline void Reset(); + + T* Insert(int index) + { + hsTArray_ValidateInsertIndex(index); + this->IncCount(index, 1); + return &fArray[index]; + } + void Insert(int index, const T& item) + { + hsTArray_ValidateInsertIndex(index); + this->IncCount(index, 1); + fArray[index] = item; + } + void Insert(int index, int count, T item[]) + { + hsTArray_ValidateCount(count); + if (count > 0) + { hsTArray_ValidateInsertIndex(index); + this->IncCount(index, count); + hsTArray_CopyForward(item, &fArray[index], count); + } + } + // This guy is a duplicate for compatibility with the older hsDynamicArray<> + void InsertAtIndex(int index, const T& item) { this->Insert(index, item); } + + void Remove(int index) + { + hsTArray_ValidateIndex(index); + this->DecCount(index, 1); + } + void Remove(int index, int count) + { + hsTArray_ValidateCount(count); + hsTArray_ValidateIndex(index); + hsTArray_ValidateIndex(index + count - 1); + this->DecCount(index, count); + } + hsBool RemoveItem(const T& item); + + T* Push() + { + this->IncCount(fUseCount, 1); + return &fArray[fUseCount - 1]; + } + void Push(const T& item) + { + this->IncCount(fUseCount, 1); + fArray[fUseCount - 1] = item; + } + void Append(const T& item) + { + this->IncCount(fUseCount, 1); + fArray[fUseCount - 1] = item; + } + inline T Pop(); + inline const T& Peek() const; + + enum { + kMissingIndex = -1 + }; + int Find(const T& item) const; // returns kMissingIndex if not found + inline T* ForEach(hsBool (*proc)(T&)); + inline T* ForEach(hsBool (*proc)(T&, void* p1), void* p1); + inline T* ForEach(hsBool (*proc)(T&, void* p1, void* p2), void* p1, void* p2); + + T* DetachArray() + { + T* array = fArray; + fUseCount = fTotalCount = 0; + fArray = nil; + return array; + } + T* AcquireArray() { return fArray; } +}; + +////////////// Public hsTArray methods + +template hsTArray::hsTArray(int count) : fArray(nil) +{ + hsTArray_ValidateCount(count); + fUseCount = fTotalCount = count; + if (count > 0) + fArray = TRACKED_NEW T[count]; +} + +template hsTArray::hsTArray(const hsTArray& src) : fArray(nil) +{ + int count = src.Count(); + fUseCount = fTotalCount = count; + + if (count > 0) + { + fArray = TRACKED_NEW T[count]; + hsTArray_CopyForward(src.fArray, fArray, count); + } +} + +template hsTArray& hsTArray::operator=(const hsTArray& src) +{ + if (this->Count() != src.Count()) + this->SetCount(src.Count()); + hsTArray_CopyForward(src.fArray, fArray, src.Count()); + return *this; +} + +// checks sizes and contents +template +bool hsTArray::operator==(const hsTArray& src) const +{ + if (&src==this) + return true; // it's me + + if (GetCount() != src.GetCount()) + return false; // different sizes + + int i; + for(i=0;i void hsTArray::Swap( hsTArray& src ) +{ + UInt16 use, tot; + T *array; + + + use = fUseCount; + tot = fTotalCount; + array = fArray; + + fUseCount = src.fUseCount; + fTotalCount = src.fTotalCount; + fArray = src.fArray; + + src.fUseCount = use; + src.fTotalCount = tot; + src.fArray = array; +} + +template void hsTArray::SetCountAndZero(int count) +{ + if( fTotalCount <= count ) + { + int n = fTotalCount; + Expand(count); + } + int i; + for( i = 0; i < fTotalCount; i++ ) + fArray[i] = nil; + fUseCount = count; +} + +template void hsTArray::ExpandAndZero(int count) +{ + if( fTotalCount <= count ) + { + int n = fTotalCount; + Expand(count); + int i; + for( i = n; i < count; i++ ) + fArray[i] = nil; + } + if( fUseCount < count ) + fUseCount = count; +} + +template void hsTArray::SetCount(int count) +{ + hsTArray_ValidateCount(count); + if (count > fTotalCount) + { + if (fArray) + delete[] fArray; + fArray = TRACKED_NEW T[count]; + fUseCount = fTotalCount = count; + } + fUseCount = count; +} + +template void hsTArray::Expand(int NewCount) // New Count is Absolute not additional +{ + hsTArray_ValidateCount(NewCount); + if (NewCount > fTotalCount) // This is Expand not Shrink + { + T* newArray = TRACKED_NEW T[NewCount]; + + if (fArray != nil) + { hsTArray_CopyForward(fArray, newArray, fUseCount); +// hsTArray_CopyForward(&fArray[index], &newArray[index + count], fUseCount - index); + delete[] fArray; + } + fArray = newArray; + fTotalCount = NewCount; + } +} + +template void hsTArray::Reset() +{ + if (fArray) + { + delete[] fArray; + fArray = nil; + fUseCount = fTotalCount = 0; + } +} + +template T hsTArray::Pop() +{ + hsTArray_Validate(fUseCount > 0); + fUseCount -= 1; + return fArray[fUseCount]; +} + +template const T& hsTArray::Peek() const +{ + hsTArray_Validate(fUseCount > 0); + return fArray[fUseCount-1]; +} + +template int hsTArray::Find(const T& item) const +{ + for (int i = 0; i < fUseCount; i++) + if (fArray[i] == item) + return i; + return kMissingIndex; +} + +template hsBool hsTArray::RemoveItem(const T& item) +{ + for (int i = 0; i < fUseCount; i++) + if (fArray[i] == item) + { this->DecCount(i, 1); + return true; + } + return false; +} + +////////// These are the private methods for hsTArray + +template void hsTArray_CopyForward(const T src[], T dst[], int count) +{ + for (int i = 0; i < count; i++) + dst[i] = src[i]; +} + +template void hsTArray_CopyBackward(const T src[], T dst[], int count) +{ + for (int i = count - 1; i >= 0; --i) + dst[i] = src[i]; +} + +template void hsTArray::IncCount(int index, int count) +{ + int newCount = fUseCount + count; + + if (newCount > fTotalCount) + { if (fTotalCount == 0) + fTotalCount = newCount; + + GrowArraySize(newCount); // Sets new fTotalCount + T* newArray = TRACKED_NEW T[fTotalCount]; + + if (fArray != nil) + { hsTArray_CopyForward(fArray, newArray, index); + hsTArray_CopyForward(&fArray[index], &newArray[index + count], fUseCount - index); + delete[] fArray; + } + fArray = newArray; + } + else + hsTArray_CopyBackward(&fArray[index], &fArray[index + count], fUseCount - index); + fUseCount = newCount; +} + +template void hsTArray::DecCount(int index, int count) +{ + if (fUseCount == count) + this->Reset(); + else + { hsTArray_CopyForward(&fArray[index + count], &fArray[index], fUseCount - index - count); + fUseCount -= count; + } +} + +template T* hsTArray::ForEach(hsBool (*proc)(T&)) +{ + for (int i = 0; i < fUseCount; i++) + if (proc(fArray[i])) + return &fArray[i]; + return nil; +} + +template T* hsTArray::ForEach(hsBool (*proc)(T&, void* p1), void* p1) +{ + for (int i = 0; i < fUseCount; i++) + if (proc(fArray[i], p1)) + return &fArray[i]; + return nil; +} + +template T* hsTArray::ForEach(hsBool (*proc)(T&, void* p1, void* p2), void* p1, void* p2) +{ + for (int i = 0; i < fUseCount; i++) + if (proc(fArray[i], p1, p2)) + return &fArray[i]; + return nil; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +// +// hsTArray's big brother. Only to be used when expecting more than 64K of elements. The +// only difference between hsTArray and hsLargeArray is LargeArray uses 32 bit counters, +// vs 16 bit counters for hsTArray. + +class hsLargeArrayBase +{ +protected: + UInt32 fUseCount; + UInt32 fTotalCount; + + void GrowArraySize(UInt32 nSize); + +#ifdef HS_DEBUGTARRAY + hsLargeArrayBase(); + virtual char *GetTypeName(); + virtual int GetSizeOf(); + hsDlistNode *self; + friend void LargeArrayStats(); + virtual ~hsLargeArrayBase(); +#else + hsLargeArrayBase():fUseCount(0), fTotalCount(0){} +#endif + +public: + UInt32 GetNumAlloc() const { return fTotalCount; } +}; + + +template class hsLargeArray : public hsLargeArrayBase +{ + T* fArray; + + inline void IncCount(int index, int count); + inline void DecCount(int index, int count); + +#ifdef HS_DEBUGGING + #define hsLargeArray_ValidateCount(count) hsAssert((count) >= 0, "bad count") + #define hsLargeArray_ValidateIndex(index) hsAssert(unsigned(index) < fUseCount, "bad index") + #define hsLargeArray_ValidateInsertIndex(index) hsAssert(unsigned(index) <= fUseCount, "bad index") + #define hsLargeArray_Validate(condition) hsAssert(condition, "oops") + + #ifdef HS_DEBUGTARRAY + virtual int GetSizeOf() { return sizeof(T); } + #endif +#else + #define hsLargeArray_ValidateCount(count) + #define hsLargeArray_ValidateIndex(index) + #define hsLargeArray_ValidateInsertIndex(index) + #define hsLargeArray_Validate(condition) +#endif +public: + hsLargeArray() : fArray(nil) {} + inline hsLargeArray(int count); + inline hsLargeArray(const hsLargeArray& src); + ~hsLargeArray() { if (fArray) delete[] fArray; + } + inline void Expand(int NewTotal); + + inline hsLargeArray& operator=(const hsLargeArray& src); + + // Swaps the internal data (including the fArray POINTER) with the data from the array given + void Swap( hsLargeArray& src ); + + void Set(int index, const T& item) { hsLargeArray_ValidateIndex(index); fArray[index]=item; } + const T& Get(int index) const { hsLargeArray_ValidateIndex(index); return fArray[index]; } + T& operator[](int index) const { hsLargeArray_ValidateIndex(index); return fArray[index]; } + + T* FirstIter() { return &fArray[0]; } + T* StopIter() { return &fArray[fUseCount]; } + + int Count() const { return fUseCount; } + int GetCount() const { return fUseCount; } + inline void SetCount(int count); + + inline void SetCountAndZero(int count); // does block clear, don't use for types with vtbl + inline void ExpandAndZero(int count); // Same as set count and zero except won't decrease + // usecount + inline void Reset(); + + T* Insert(int index) + { + hsLargeArray_ValidateInsertIndex(index); + this->IncCount(index, 1); + return &fArray[index]; + } + void Insert(int index, const T& item) + { + hsLargeArray_ValidateInsertIndex(index); + this->IncCount(index, 1); + fArray[index] = item; + } + void Insert(int index, int count, T item[]) + { + hsLargeArray_ValidateCount(count); + if (count > 0) + { hsLargeArray_ValidateInsertIndex(index); + this->IncCount(index, count); + hsLargeArray_CopyForward(item, &fArray[index], count); + } + } + // This guy is a duplicate for compatibility with the older hsDynamicArray<> + void InsertAtIndex(int index, const T& item) { this->Insert(index, item); } + + void Remove(int index) + { + hsLargeArray_ValidateIndex(index); + this->DecCount(index, 1); + } + void Remove(int index, int count) + { + hsLargeArray_ValidateCount(count); + hsLargeArray_ValidateIndex(index); + hsLargeArray_ValidateIndex(index + count - 1); + this->DecCount(index, count); + } + hsBool RemoveItem(const T& item); + + T* Push() + { + this->IncCount(fUseCount, 1); + return &fArray[fUseCount - 1]; + } + void Push(const T& item) + { + this->IncCount(fUseCount, 1); + fArray[fUseCount - 1] = item; + } + void Append(const T& item) + { + this->IncCount(fUseCount, 1); + fArray[fUseCount - 1] = item; + } + inline T Pop(); + inline const T& Peek() const; + + enum { + kMissingIndex = -1 + }; + int Find(const T& item) const; // returns kMissingIndex if not found + inline T* ForEach(hsBool (*proc)(T&)); + inline T* ForEach(hsBool (*proc)(T&, void* p1), void* p1); + inline T* ForEach(hsBool (*proc)(T&, void* p1, void* p2), void* p1, void* p2); + + T* DetachArray() + { + T* array = fArray; + fUseCount = fTotalCount = 0; + fArray = nil; + return array; + } + T* AcquireArray() { return fArray; } +}; + +////////////// Public hsLargeArray methods + +template hsLargeArray::hsLargeArray(int count) : fArray(nil) +{ + hsLargeArray_ValidateCount(count); + fUseCount = fTotalCount = count; + if (count > 0) + fArray = TRACKED_NEW T[count]; +} + +template hsLargeArray::hsLargeArray(const hsLargeArray& src) : fArray(nil) +{ + int count = src.Count(); + fUseCount = fTotalCount = count; + + if (count > 0) + { + fArray = TRACKED_NEW T[count]; + hsLargeArray_CopyForward(src.fArray, fArray, count); + } +} + +template hsLargeArray& hsLargeArray::operator=(const hsLargeArray& src) +{ + if (this->Count() != src.Count()) + this->SetCount(src.Count()); + hsLargeArray_CopyForward(src.fArray, fArray, src.Count()); + return *this; +} + +template void hsLargeArray::Swap( hsLargeArray& src ) +{ + UInt32 use, tot; + T *array; + + + use = fUseCount; + tot = fTotalCount; + array = fArray; + + fUseCount = src.fUseCount; + fTotalCount = src.fTotalCount; + fArray = src.fArray; + + src.fUseCount = use; + src.fTotalCount = tot; + src.fArray = array; +} + +template void hsLargeArray::SetCountAndZero(int count) +{ + if( fTotalCount <= count ) + { + int n = fTotalCount; + Expand(count); + } + HSMemory::Clear(fArray, fTotalCount * sizeof( T )); + fUseCount = count; +} + +template void hsLargeArray::ExpandAndZero(int count) +{ + if( fTotalCount <= count ) + { + int n = fTotalCount; + Expand(count); + HSMemory::Clear(fArray+n, (count - n) * sizeof( T )); + } + if( fUseCount < count ) + fUseCount = count; +} + +template void hsLargeArray::SetCount(int count) +{ + hsLargeArray_ValidateCount(count); + if (count > fTotalCount) + { + if (fArray) + delete[] fArray; + fArray = TRACKED_NEW T[count]; + fUseCount = fTotalCount = count; + } + fUseCount = count; +} + +template void hsLargeArray::Expand(int NewCount) // New Count is Absolute not additional +{ + hsLargeArray_ValidateCount(NewCount); + if (NewCount > fTotalCount) // This is Expand not Shrink + { + T* newArray = TRACKED_NEW T[NewCount]; + + if (fArray != nil) + { hsLargeArray_CopyForward(fArray, newArray, fUseCount); +// hsLargeArray_CopyForward(&fArray[index], &newArray[index + count], fUseCount - index); + delete[] fArray; + } + fArray = newArray; + fTotalCount = NewCount; + } +} + +template void hsLargeArray::Reset() +{ + if (fArray) + { + delete[] fArray; + fArray = nil; + fUseCount = fTotalCount = 0; + } +} + +template T hsLargeArray::Pop() +{ + hsLargeArray_Validate(fUseCount > 0); + fUseCount -= 1; + return fArray[fUseCount]; +} + +template const T& hsLargeArray::Peek() const +{ + hsLargeArray_Validate(fUseCount > 0); + return fArray[fUseCount-1]; +} + +template int hsLargeArray::Find(const T& item) const +{ + for (int i = 0; i < fUseCount; i++) + if (fArray[i] == item) + return i; + return kMissingIndex; +} + +template hsBool hsLargeArray::RemoveItem(const T& item) +{ + for (int i = 0; i < fUseCount; i++) + if (fArray[i] == item) + { this->DecCount(i, 1); + return true; + } + return false; +} + +////////// These are the private methods for hsLargeArray + +template void hsLargeArray_CopyForward(const T src[], T dst[], int count) +{ + for (int i = 0; i < count; i++) + dst[i] = src[i]; +} + +template void hsLargeArray_CopyBackward(const T src[], T dst[], int count) +{ + for (int i = count - 1; i >= 0; --i) + dst[i] = src[i]; +} + +template void hsLargeArray::IncCount(int index, int count) +{ + int newCount = fUseCount + count; + + if (newCount > fTotalCount) + { if (fTotalCount == 0) + fTotalCount = newCount; + + GrowArraySize(newCount); // Sets new fTotalCount + T* newArray = TRACKED_NEW T[fTotalCount]; + + if (fArray != nil) + { hsLargeArray_CopyForward(fArray, newArray, index); + hsLargeArray_CopyForward(&fArray[index], &newArray[index + count], fUseCount - index); + delete[] fArray; + } + fArray = newArray; + } + else + hsLargeArray_CopyBackward(&fArray[index], &fArray[index + count], fUseCount - index); + fUseCount = newCount; +} + +template void hsLargeArray::DecCount(int index, int count) +{ + if (fUseCount == count) + this->Reset(); + else + { hsLargeArray_CopyForward(&fArray[index + count], &fArray[index], fUseCount - index - count); + fUseCount -= count; + } +} + +template T* hsLargeArray::ForEach(hsBool (*proc)(T&)) +{ + for (int i = 0; i < fUseCount; i++) + if (proc(fArray[i])) + return &fArray[i]; + return nil; +} + +template T* hsLargeArray::ForEach(hsBool (*proc)(T&, void* p1), void* p1) +{ + for (int i = 0; i < fUseCount; i++) + if (proc(fArray[i], p1)) + return &fArray[i]; + return nil; +} + +template T* hsLargeArray::ForEach(hsBool (*proc)(T&, void* p1, void* p2), void* p1, void* p2) +{ + for (int i = 0; i < fUseCount; i++) + if (proc(fArray[i], p1, p2)) + return &fArray[i]; + return nil; +} + + + +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread.cpp new file mode 100644 index 00000000..e94eb6cf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread.cpp @@ -0,0 +1,99 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef CoreLib_Thread +#define CoreLib_Thread + +#include "hsThread.h" +#include "hsUtils.h" + + +////////////////////////////////////////////////////////////////////////////// +hsReaderWriterLock::hsReaderWriterLock( const char * name, Callback * cb ) +: fReaderCount( 0 ) +, fWriterSema( 1 ) +, fCallback( cb ) +, fName( nil ) +{ + fName = hsStrcpy( name ); +} + +hsReaderWriterLock::~hsReaderWriterLock() +{ + delete [] fName; +} + +void hsReaderWriterLock::LockForReading() +{ + if ( fCallback ) + fCallback->OnLockingForRead( this ); + fReaderCountLock.Lock(); + fReaderLock.Lock(); + fReaderCount++; + if ( fReaderCount==1 ) + fWriterSema.Wait(); + fReaderLock.Unlock(); + fReaderCountLock.Unlock(); + if ( fCallback ) + fCallback->OnLockedForRead( this ); +} + +void hsReaderWriterLock::UnlockForReading() +{ + if ( fCallback ) + fCallback->OnUnlockingForRead( this ); + fReaderLock.Lock(); + fReaderCount--; + if ( fReaderCount==0 ) + fWriterSema.Signal(); + fReaderLock.Unlock(); + if ( fCallback ) + fCallback->OnUnlockedForRead( this ); +} + +void hsReaderWriterLock::LockForWriting() +{ + if ( fCallback ) + fCallback->OnLockingForWrite( this ); + fReaderCountLock.Lock(); + fWriterSema.Wait(); + hsAssert( fReaderCount==0, "Locked for writing, but fReaderCount>0" ); + if ( fCallback ) + fCallback->OnLockedForWrite( this ); +} + +void hsReaderWriterLock::UnlockForWriting() +{ + if ( fCallback ) + fCallback->OnUnlockingForWrite( this ); + fWriterSema.Signal(); + fReaderCountLock.Unlock(); + if ( fCallback ) + fCallback->OnUnlockedForWrite( this ); +} + + + +#endif // CoreLib_Thread diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread.h new file mode 100644 index 00000000..b3060b65 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread.h @@ -0,0 +1,285 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsThread_Defined +#define hsThread_Defined + +#include "hsTypes.h" + +typedef UInt32 hsMilliseconds; + + +#if HS_BUILD_FOR_MAC + #include +#elif HS_BUILD_FOR_WIN32 + #include "hsWindows.h" +#elif HS_BUILD_FOR_UNIX + #include + #include + // We can't wait with a timeout with semas + #define USE_SEMA + // Linux kernel 2.4 w/ NTPL threading patch and O(1) scheduler + // seems to have a problem in it's cond_t implementation that + // causes a hang under heavy load. This is a workaround that + // uses select() and pipes. +// #define PSEUDO_EVENT +#endif + +class hsThread +{ +public: +#if HS_BUILD_FOR_MAC + typedef MPTaskId ThreadId; +#elif HS_BUILD_FOR_WIN32 + typedef DWORD ThreadId; +#elif HS_BUILD_FOR_UNIX + typedef pthread_t ThreadId; +#endif +private: + hsBool fQuit; + UInt32 fStackSize; +#if HS_BUILD_FOR_MAC + ThreadId fTaskId; + MPQueueId fNotifyQ; +#elif HS_BUILD_FOR_WIN32 + ThreadId fThreadId; + HANDLE fThreadH; + HANDLE fQuitSemaH; +#elif HS_BUILD_FOR_UNIX + ThreadId fPThread; + hsBool fIsValid; + pthread_mutex_t fMutex; +#endif +protected: + hsBool GetQuit() const { return hsBool(fQuit); } + void SetQuit(hsBool value) { fQuit = value; } +public: + hsThread(UInt32 stackSize = 0); + virtual ~hsThread(); // calls Stop() +#if HS_BUILD_FOR_MAC + ThreadId GetThreadId() { return fTaskId; } +#error "Mac is Depricated" +#elif HS_BUILD_FOR_WIN32 + ThreadId GetThreadId() { return fThreadId; } + static ThreadId GetMyThreadId() { return GetCurrentThreadId(); } +#elif HS_BUILD_FOR_UNIX + ThreadId GetThreadId() { return fPThread; } + static ThreadId GetMyThreadId() { return pthread_self(); } + pthread_mutex_t* GetStartupMutex() { return &fMutex; } +#endif + + virtual hsError Run() = 0; // override this to do your work + virtual void Start(); // initializes stuff and calls your Run() method + virtual void Stop(); // sets fQuit = true and the waits for the thread to stop + + // Static functions + static void* Alloc(size_t size); // does not call operator::new(), may return nil + static void Free(void* p); // does not call operator::delete() + static void ThreadYield(); + +#if HS_BUILD_FOR_WIN32 + DWORD WinRun(); +#endif +}; + +////////////////////////////////////////////////////////////////////////////// + +class hsMutex { +#if HS_BUILD_FOR_MAC + MPCriticalRegionId fCriticalRegion; +#elif HS_BUILD_FOR_WIN32 + HANDLE fMutexH; +#elif HS_BUILD_FOR_UNIX + pthread_mutex_t fPMutex; +#endif +public: + hsMutex(); + virtual ~hsMutex(); + + void Lock(); + hsBool TryLock(); + void Unlock(); +}; + +class hsTempMutexLock { + hsMutex* fMutex; +public: + hsTempMutexLock(hsMutex* mutex) : fMutex(mutex) + { + fMutex->Lock(); + } + hsTempMutexLock(hsMutex& mutex) : fMutex(&mutex) + { + fMutex->Lock(); + } + ~hsTempMutexLock() + { + fMutex->Unlock(); + } +}; + +////////////////////////////////////////////////////////////////////////////// + +class hsSemaphore { +#if HS_BUILD_FOR_MAC + MPSemaphoreId fSemaId; +#elif HS_BUILD_FOR_WIN32 + HANDLE fSemaH; +#elif HS_BUILD_FOR_UNIX +#ifdef USE_SEMA + sem_t fPSema; +#else + pthread_mutex_t fPMutex; + pthread_cond_t fPCond; + Int32 fCounter; +#endif +#endif +public: +#ifdef HS_BUILD_FOR_WIN32 + hsSemaphore(int initialValue=0, const char *name=nil); +#else + hsSemaphore(int initialValue=0); +#endif + ~hsSemaphore(); + + hsBool Wait(hsMilliseconds timeToWait = kPosInfinity32); + void Signal(); +}; + +////////////////////////////////////////////////////////////////////////////// +#if !HS_BUILD_FOR_MAC +class hsEvent +{ +#if HS_BUILD_FOR_UNIX +#ifndef PSEUDO_EVENT + pthread_mutex_t fMutex; + pthread_cond_t fCond; + hsBool fTriggered; +#else + enum { kRead, kWrite }; + int fFds[2]; + hsMutex fWaitLock; + hsMutex fSignalLock; +#endif // PSEUDO_EVENT +#elif HS_BUILD_FOR_WIN32 + HANDLE fEvent; +#endif +public: + hsEvent(); + ~hsEvent(); + + hsBool Wait(hsMilliseconds timeToWait = kPosInfinity32); + void Signal(); +}; +#endif // HS_BUILD_FOR_MAC + +////////////////////////////////////////////////////////////////////////////// +#if !HS_BUILD_FOR_MAC +class hsSleep +{ +public: +#if HS_BUILD_FOR_UNIX + static void Sleep(UInt32 millis); + +#elif HS_BUILD_FOR_WIN32 + static void Sleep(UInt32 millis) { ::Sleep(millis); } + +#endif +}; +#endif // HS_BUILD_FOR_MAC + + +////////////////////////////////////////////////////////////////////////////// +// Allows multiple readers, locks out readers for writing. + +class hsReaderWriterLock +{ +public: + struct Callback + { + virtual void OnLockingForRead( hsReaderWriterLock * lock ) {} + virtual void OnLockedForRead( hsReaderWriterLock * lock ) {} + virtual void OnUnlockingForRead( hsReaderWriterLock * lock ) {} + virtual void OnUnlockedForRead( hsReaderWriterLock * lock ) {} + virtual void OnLockingForWrite( hsReaderWriterLock * lock ) {} + virtual void OnLockedForWrite( hsReaderWriterLock * lock ) {} + virtual void OnUnlockingForWrite( hsReaderWriterLock * lock ) {} + virtual void OnUnlockedForWrite( hsReaderWriterLock * lock ) {} + }; + hsReaderWriterLock( const char * name="", Callback * cb=nil ); + ~hsReaderWriterLock(); + void LockForReading(); + void UnlockForReading(); + void LockForWriting(); + void UnlockForWriting(); + const char * GetName() const { return fName; } + +private: + int fReaderCount; + hsMutex fReaderCountLock; + hsMutex fReaderLock; + hsSemaphore fWriterSema; + Callback * fCallback; + char * fName; +}; + +class hsLockForReading +{ + hsReaderWriterLock * fLock; +public: + hsLockForReading( hsReaderWriterLock & lock ): fLock( &lock ) + { + fLock->LockForReading(); + } + hsLockForReading( hsReaderWriterLock * lock ): fLock( lock ) + { + fLock->LockForReading(); + } + ~hsLockForReading() + { + fLock->UnlockForReading(); + } +}; + +class hsLockForWriting +{ + hsReaderWriterLock * fLock; +public: + hsLockForWriting( hsReaderWriterLock & lock ): fLock( &lock ) + { + fLock->LockForWriting(); + } + hsLockForWriting( hsReaderWriterLock * lock ): fLock( lock ) + { + fLock->LockForWriting(); + } + ~hsLockForWriting() + { + fLock->UnlockForWriting(); + } +}; + +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Mac.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Mac.cpp new file mode 100644 index 00000000..92875e2a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Mac.cpp @@ -0,0 +1,163 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsThread.h" +#include "hsExceptions.h" + +extern "C" { + static OSStatus gEntryPoint(void* param) + { + return ((hsThread*)param)->Run(); + } +} + +hsThread::hsThread(UInt32 stackSize) : fTaskId(0), fStackSize(stackSize), fQuit(false) +{ + if (MPLibraryIsLoaded() == false) + throw "MPLibraryIsLoaded() returned false"; +} + +hsThread::~hsThread() +{ + this->Stop(); +} + +void hsThread::Start() +{ + if (fTaskId == 0) + { OSStatus status = ::MPCreateQueue(&fNotifyQ); + hsThrowIfOSErr(status); + + status = ::MPCreateTask(gEntryPoint, this, fStackSize, fNotifyQ, nil, nil, 0, &fTaskId); + if (status) + { ::MPDeleteQueue(fNotifyQ); + throw hsOSException(status); + } + } + else + hsDebugMessage("Calling hsThread::Start() more than once", 0); +} + +void hsThread::Stop() +{ + if (fTaskId) + { this->fQuit = true; + + OSStatus status = ::MPTerminateTask(fTaskId, 0); + hsThrowIfOSErr(status); + + // Wait for the task to tell us that its actually quit + status = ::MPWaitOnQueue(fNotifyQ, nil, nil, nil, kDurationForever); + hsThrowIfOSErr(status); + + status = ::MPDeleteQueue(fNotifyQ); + hsThrowIfOSErr(status); + + fTaskId = 0; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void* hsThread::Alloc(size_t size) +{ + return ::MPAllocate(size); +} + +void hsThread::Free(void* p) +{ + if (p) + ::MPFree(p); +} + +void hsThread::ThreadYield() +{ + ::MPYield(); +} + +////////////////////////////////////////////////////////////////////////////// + +hsMutex::hsMutex() +{ + OSStatus status = ::MPCreateCriticalRegion(&fCriticalRegion); + hsThrowIfOSErr(status); +} + +hsMutex::~hsMutex() +{ + OSStatus status = ::MPDeleteCriticalRegion(fCriticalRegion); + hsThrowIfOSErr(status); +} + +void hsMutex::Lock() +{ + OSStatus status = ::MPEnterCriticalRegion(fCriticalRegion, kDurationForever); + hsThrowIfOSErr(status); +} + +void hsMutex::Unlock() +{ + OSStatus status = ::MPExitCriticalRegion(fCriticalRegion); + hsThrowIfOSErr(status); +} + +////////////////////////////////////////////////////////////////////////////// + +hsSemaphore::hsSemaphore(int initialValue) +{ + OSStatus status = MPCreateSemaphore(kPosInfinity32, initialValue, &fSemaId); + hsThrowIfOSErr(status); +} + +hsSemaphore::~hsSemaphore() +{ + OSStatus status = MPDeleteSemaphore(fSemaId); + hsThrowIfOSErr(status); +} + +hsBool hsSemaphore::Wait(hsMilliseconds timeToWait) +{ + Duration duration; + + if (timeToWait == kPosInfinity32) + duration = kDurationForever; + else + duration = 0; // THEY DON'T IMPLEMENT delay times yet !!! + + OSStatus status = MPWaitOnSemaphore(fSemaId, duration); +/* + if (status == kMPTimeoutErr) + return false; +*/ + hsThrowIfOSErr(status); + return true; +} + +void hsSemaphore::Signal() +{ + OSStatus status = MPSignalSemaphore(fSemaId); + hsThrowIfOSErr(status); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Unix.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Unix.cpp new file mode 100644 index 00000000..542eb399 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Unix.cpp @@ -0,0 +1,537 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsThread.h" +#include "hsExceptions.h" +#include +#include + +#define NO_POSIX_CLOCK 1 + +#if NO_POSIX_CLOCK +#include +#include +#define CLOCK_REALTIME 0 + +// +// A linux hack b/c we're not quite POSIX +// +int clock_gettime(int clocktype, struct timespec* ts) +{ + struct timezone tz; + struct timeval tv; + + int result = gettimeofday(&tv, &tz); + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000 + 500; // sice we're losing accuracy round up by 500 nanos + + return result; +} + +#endif + +extern "C" { + static void* gEntryPoint(void* param) + { + pthread_mutex_lock(((hsThread*)param)->GetStartupMutex()); + void* ret = (void*)((hsThread*)param)->Run(); + pthread_mutex_unlock(((hsThread*)param)->GetStartupMutex()); + pthread_exit(ret); + return ret; + } +} + +#define kInvalidStackSize UInt32(~0) + +hsThread::hsThread(UInt32 stackSize) : fStackSize(stackSize), fQuit(false) +{ + fIsValid = false; + pthread_mutex_init(&fMutex,nil); +} + +hsThread::~hsThread() +{ + this->Stop(); +} + +void hsThread::Start() +{ + if (fIsValid == false) + { + pthread_mutex_lock(GetStartupMutex()); + + int status = ::pthread_create(&fPThread, nil, gEntryPoint, this); + pthread_mutex_unlock(GetStartupMutex()); + + hsThrowIfOSErr(status); + + fIsValid = true; + } + else + hsDebugMessage("Calling hsThread::Start() more than once", 0); +} + +void hsThread::Stop() +{ + if (fIsValid) + { this->fQuit = true; + + int status = ::pthread_join(fPThread, nil); + hsThrowIfOSErr(status); + + fIsValid = false; + } +} + +////////////////////////////////////////////////////////////////////////////// + +void* hsThread::Alloc(size_t size) +{ + return ::malloc(size); +} + +void hsThread::Free(void* p) +{ + if (p) + ::free(p); +} + +void hsThread::ThreadYield() +{ +// ::sched_yield(); +} + +////////////////////////////////////////////////////////////////////////////// + +//#define MUTEX_TIMING +#ifdef MUTEX_TIMING + +#include +#include +#include +#include "hsWide.h" + + +static FILE * gMutexTimerFile = nil; +static void InitMutexTimerFile() +{ + if ( !gMutexTimerFile ) + { + gMutexTimerFile = fopen( "log/MutexTimes.log", "wt" ); + if ( gMutexTimerFile ) + fprintf( gMutexTimerFile, "------------------------------------\n" ); + } +} + +#endif + +//#define EVENT_LOGGING +#ifdef EVENT_LOGGING + +#include +#include +#include +#include "../NucleusLib/inc/hsTimer.h" + + +static FILE * gEventLoggingFile = nil; +static void InitEventLoggingFile() +{ + if ( !gEventLoggingFile ) + { + char fname[256]; + sprintf(fname,"log/Events-%u.log",getpid()); + gEventLoggingFile = fopen( fname, "wt" ); + if ( gEventLoggingFile ) + fprintf( gEventLoggingFile, "------------------------------------\n" ); + } +} + +#endif + +hsMutex::hsMutex() +{ + +#ifdef MUTEX_TIMING + InitMutexTimerFile(); +#endif + + // create mutex attributes + pthread_mutexattr_t attr; + int status = ::pthread_mutexattr_init(&attr); + hsThrowIfOSErr(status); + + // make the mutex attributes recursive + status = ::pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE); + hsThrowIfOSErr(status); + + //init the mutex + status = ::pthread_mutex_init(&fPMutex, &attr); + hsThrowIfOSErr(status); + + // destroy the attributes + status = ::pthread_mutexattr_destroy(&attr); + hsThrowIfOSErr(status); +} + +hsMutex::~hsMutex() +{ + int status = ::pthread_mutex_destroy(&fPMutex); + hsThrowIfOSErr(status); +} + +void hsMutex::Lock() +{ +#ifdef MUTEX_TIMING +# ifndef HS_DEBUGGING + timeval tv; + hsWide start; + gettimeofday( &tv, nil ); + start.Mul( tv.tv_sec, 1000000 )->Add( tv.tv_usec ); +# endif +#endif + + int status = ::pthread_mutex_lock(&fPMutex); + hsThrowIfOSErr(status); + +#ifdef MUTEX_TIMING +# ifndef HS_DEBUGGING + hsWide diff; + gettimeofday( &tv, nil ); + diff.Mul( tv.tv_sec, 1000000 )->Add( tv.tv_usec )->Sub( &start )->Div( 1000000 ); + double duration = diff.AsDouble(); + if ( gMutexTimerFile && duration>0.005 ) + { + time_t t; + time( &t ); + struct tm *now = localtime( &t ); + char tmp[30]; + strftime( tmp, 30, "%c", now ); + fprintf( gMutexTimerFile, "[%s] [%lu:%lu] %f\n", tmp, getpid(), hsThread::GetMyThreadId(), duration ); + } +# endif +#endif +} + +hsBool hsMutex::TryLock() +{ + int status = ::pthread_mutex_trylock(&fPMutex); + hsThrowIfOSErr(status); + return status==EBUSY?false:true; +} + +void hsMutex::Unlock() +{ + int status = ::pthread_mutex_unlock(&fPMutex); + hsThrowIfOSErr(status); +} + +///////////////////////////////////////////////////////////////////////////// + +hsSemaphore::hsSemaphore(int initialValue) +{ +#ifdef USE_SEMA + int shared = 0; // 1 if sharing between processes + int status = ::sem_init(&fPSema, shared, initialValue); + hsThrowIfOSErr(status); +#else + int status = ::pthread_mutex_init(&fPMutex, nil); + hsThrowIfOSErr(status); + + status = ::pthread_cond_init(&fPCond, nil); + hsThrowIfOSErr(status); + + fCounter = initialValue; +#endif +} + +hsSemaphore::~hsSemaphore() +{ +#ifdef USE_SEMA + int status = ::sem_destroy(&fPSema); + hsThrowIfOSErr(status); +#else + int status = ::pthread_cond_destroy(&fPCond); + hsThrowIfOSErr(status); + + status = ::pthread_mutex_destroy(&fPMutex); + hsThrowIfOSErr(status); +#endif +} + +hsBool hsSemaphore::Wait(hsMilliseconds timeToWait) +{ +#ifdef USE_SEMA // SHOULDN'T THIS USE timeToWait??!?!? -rje + hsAssert( timeToWait==kPosInfinity32, "sem_t does not support wait with timeout. #undef USE_SEMA and recompile." ); + int status = ::sem_wait(&fPSema); + hsThrowIfOSErr(status); + return true; +#else + hsBool retVal = true; + int status = ::pthread_mutex_lock(&fPMutex); + hsThrowIfOSErr(status); + + if (timeToWait == kPosInfinity32) + { while (fCounter == 0) + { status = ::pthread_cond_wait(&fPCond, &fPMutex); + hsThrowIfOSErr(status); + } + } + else + { timespec spec; + int result; + + result = ::clock_gettime(CLOCK_REALTIME, &spec); + hsThrowIfFalse(result == 0); + + spec.tv_sec += timeToWait / 1000; + spec.tv_nsec += (timeToWait % 1000) * 1000 * 1000; + while (spec.tv_nsec >= 1000 * 1000 * 1000) + { spec.tv_sec += 1; + spec.tv_nsec -= 1000 * 1000 * 1000; + } + + while (fCounter == 0) + { status = ::pthread_cond_timedwait(&fPCond, &fPMutex, &spec); + if (status == ETIMEDOUT) + { retVal = false; + goto EXIT; + } + hsThrowIfOSErr(status); + } + } + + hsAssert(fCounter > 0, "oops"); + fCounter -= 1; +EXIT: + status = ::pthread_mutex_unlock(&fPMutex); + hsThrowIfOSErr(status); + return retVal; +#endif +} + +void hsSemaphore::Signal() +{ +#ifdef USE_SEMA + int status = ::sem_post(&fPSema); + hsThrowIfOSErr(status); +#else + int status = ::pthread_mutex_lock(&fPMutex); + hsThrowIfOSErr(status); + + fCounter += 1; + + status = ::pthread_mutex_unlock(&fPMutex); + hsThrowIfOSErr(status); + + status = ::pthread_cond_signal(&fPCond); + hsThrowIfOSErr(status); +#endif +} + + +/////////////////////////////////////////////////////////////// + +#ifndef PSEUDO_EVENT + +hsEvent::hsEvent() : fTriggered(false) +{ +#ifdef EVENT_LOGGING + InitEventLoggingFile(); +#endif + int status = ::pthread_mutex_init(&fMutex, nil); + hsAssert(status == 0, "hsEvent Mutex Init"); + hsThrowIfOSErr(status); + + // fCond = PTHREAD_COND_INITIALIZER; + status = ::pthread_cond_init(&fCond, nil); + hsAssert(status == 0, "hsEvent Cond Init"); + hsThrowIfOSErr(status); +} + +hsEvent::~hsEvent() +{ + int status = ::pthread_cond_destroy(&fCond); + hsAssert(status == 0, "hsEvent Cond De-Init"); + hsThrowIfOSErr(status); + + status = ::pthread_mutex_destroy(&fMutex); + hsAssert(status == 0, "hsEvent Mutex De-Init"); + hsThrowIfOSErr(status); +} + +hsBool hsEvent::Wait(hsMilliseconds timeToWait) +{ + hsBool retVal = true; + int status = ::pthread_mutex_lock(&fMutex); + hsAssert(status == 0, "hsEvent Mutex Lock"); + hsThrowIfOSErr(status); + +#ifdef EVENT_LOGGING + fprintf(gEventLoggingFile,"Event: %p - In Wait (pre trig check), Triggered: %d, t=%f\n",this,fTriggered,hsTimer::GetSeconds()); +#endif + + if ( !fTriggered ) + { + if (timeToWait == kPosInfinity32) + { + status = ::pthread_cond_wait(&fCond, &fMutex); + hsAssert(status == 0, "hsEvent Cond Wait"); + hsThrowIfOSErr(status); + } + else + { timespec spec; + int result; + + result = ::clock_gettime(CLOCK_REALTIME, &spec); + hsThrowIfFalse(result == 0); + + spec.tv_sec += timeToWait / 1000; + spec.tv_nsec += (timeToWait % 1000) * 1000 * 1000; + while (spec.tv_nsec >= 1000 * 1000 * 1000) + { spec.tv_sec += 1; + spec.tv_nsec -= 1000 * 1000 * 1000; + } + + status = ::pthread_cond_timedwait(&fCond, &fMutex, &spec); + + if (status == ETIMEDOUT) + { + // It's a conditional paired with a variable! + // Pthread docs all use a variable in conjunction with the conditional + retVal = fTriggered; + status = 0; +#ifdef EVENT_LOGGING + fprintf(gEventLoggingFile,"Event: %p - In Wait (wait timed out), Triggered: %d, t=%f\n",this,fTriggered,hsTimer::GetSeconds()); +#endif + } + else + { +#ifdef EVENT_LOGGING + fprintf(gEventLoggingFile,"Event: %p - In Wait (wait recvd signal), Triggered: %d, t=%f\n",this,fTriggered,hsTimer::GetSeconds()); +#endif + } + + hsAssert(status == 0, "hsEvent Cond Wait"); + hsThrowIfOSErr(status); + } + } + else + { +#ifdef EVENT_LOGGING + fprintf(gEventLoggingFile,"Event: %p - In Wait (post triggerd), Triggered: %d, t=%f\n",this,fTriggered,hsTimer::GetSeconds()); +#endif + } + + fTriggered = false; + status = ::pthread_mutex_unlock(&fMutex); + hsAssert(status == 0, "hsEvent Mutex Unlock"); + hsThrowIfOSErr(status); + return retVal; +} + +void hsEvent::Signal() +{ + int status = ::pthread_mutex_lock(&fMutex); + hsAssert(status == 0, "hsEvent Mutex Lock"); + hsThrowIfOSErr(status); +#ifdef EVENT_LOGGING + fprintf(gEventLoggingFile,"Event: %p - In Signal, Triggered: %d, t=%f\n",this,fTriggered,hsTimer::GetSeconds()); +#endif + fTriggered = true; + status = ::pthread_cond_broadcast(&fCond); + hsAssert(status == 0, "hsEvent Cond Broadcast"); + hsThrowIfOSErr(status); + status = ::pthread_mutex_unlock(&fMutex); + hsAssert(status == 0, "hsEvent Mutex Unlock"); + hsThrowIfOSErr(status); +} + +#else + +hsEvent::hsEvent() +{ + pipe( fFds ); +} + +hsEvent::~hsEvent() +{ + close( fFds[kRead] ); + close( fFds[kWrite] ); +} + +hsBool hsEvent::Wait( hsMilliseconds timeToWait ) +{ + hsTempMutexLock lock( fWaitLock ); + + fd_set fdset; + FD_ZERO( &fdset ); + FD_SET( fFds[kRead], &fdset ); + + int ans; + if( timeToWait==kPosInfinity32 ) + { + ans = select( fFds[kRead]+1, &fdset, nil, nil, nil ); + } + else + { + struct timeval tv; + tv.tv_sec = timeToWait / 1000; + tv.tv_usec = ( timeToWait % 1000 ) * 1000; + + ans = select( fFds[kRead]+1, &fdset, nil, nil, &tv ); + } + + bool signaled = false; + + if ( ans>0 ) + { + char buf[2]; + int n = read( fFds[kRead], buf, 1 ); + signaled = ( n==1 ); + } + + return signaled; +} + +void hsEvent::Signal() +{ + hsTempMutexLock lock( fSignalLock ); + write( fFds[kWrite], "*", 1 ); +} + + +#endif + +void hsSleep::Sleep(UInt32 millis) +{ + UInt32 secs = millis / 1000; + if (secs > 0) + { + millis %= 1000; + ::sleep(secs); + } + usleep(millis*1000); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Win.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Win.cpp new file mode 100644 index 00000000..df82413a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsThread_Win.cpp @@ -0,0 +1,218 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include + +#include "hsThread.h" +#include "hsExceptions.h" +#include "hsMemory.h" + +// typedef unsigned int __stdcall (*EntryPtCB)(void*); + +static DWORD __stdcall gEntryPoint(void* param) +{ + return ((hsThread*)param)->WinRun(); +} + +static unsigned int __stdcall gEntryPointBT(void* param) +{ + return ((hsThread*)param)->WinRun(); +} + +hsThread::hsThread(UInt32 stackSize) : fStackSize(stackSize), fQuit(false), fThreadH(nil), fQuitSemaH(nil) +{ +} + +hsThread::~hsThread() +{ + this->Stop(); +} + +void hsThread::Start() +{ + if (fThreadH == nil) + { + fQuitSemaH = ::CreateSemaphore(nil, 0, kPosInfinity32, nil); + if (fQuitSemaH == nil) + throw hsOSException(-1); + +#if 0 + fThreadH = ::CreateThread(nil, fStackSize, gEntryPoint, this, 0, &fThreadId); +#else + fThreadH = (HANDLE)_beginthreadex(nil, fStackSize, gEntryPointBT, this, 0, (unsigned int*)&fThreadId); +#endif + if (fThreadH == nil) + throw hsOSException(-1); + } +} + +void hsThread::Stop() +{ + if (fThreadH != nil) + { this->fQuit = true; + + if (fQuitSemaH != nil) + ::WaitForSingleObject(fQuitSemaH, INFINITE); // wait for the thread to quit + + ::CloseHandle(fThreadH); + fThreadH = nil; + ::CloseHandle(fQuitSemaH); + fQuitSemaH = nil; + } +} + +DWORD hsThread::WinRun() +{ + DWORD result = this->Run(); + + ::ReleaseSemaphore(fQuitSemaH, 1, nil); // signal that we've quit + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void* hsThread::Alloc(size_t size) +{ + return HSMemory::New(size); +} + +void hsThread::Free(void* p) +{ + HSMemory::Delete(p); +} + +void hsThread::ThreadYield() +{ + // Don't know how to explicitly yield on WIN32 +} + +////////////////////////////////////////////////////////////////////////////// + +hsMutex::hsMutex() +{ + fMutexH = ::CreateMutex(nil, false, nil); + if (fMutexH == nil) + throw hsOSException(-1); +} + +hsMutex::~hsMutex() +{ + ::CloseHandle(fMutexH); +} + +void hsMutex::Lock() +{ + DWORD state = ::WaitForSingleObject(fMutexH, INFINITE); + hsAssert(state != WAIT_FAILED,"hsMutex::Lock -> Wait Failed"); + hsAssert(state != WAIT_ABANDONED,"hsMutex::Lock -> Abandoned Mutex"); + hsAssert(state != WAIT_TIMEOUT,"hsMutex::Lock -> Infinite Timeout expired?"); +} + +hsBool hsMutex::TryLock() +{ + DWORD state = ::WaitForSingleObject(fMutexH, 0); + hsAssert(state != WAIT_ABANDONED,"hsMutex::TryLock -> Abandoned Mutex"); + return state == WAIT_OBJECT_0?true:false; +} + +void hsMutex::Unlock() +{ + BOOL result = ::ReleaseMutex(fMutexH); + hsAssert(result != 0, "hsMutex::Unlock Failed!"); + +} + +////////////////////////////////////////////////////////////////////////////// + +hsSemaphore::hsSemaphore(int initialValue, const char *name) +{ + fSemaH = ::CreateSemaphore(nil, initialValue, kPosInfinity32, name); + if (fSemaH == nil) + throw hsOSException(-1); +} + +hsSemaphore::~hsSemaphore() +{ + ::CloseHandle(fSemaH); +} + +hsBool hsSemaphore::Wait(hsMilliseconds timeToWait) +{ + if (timeToWait == kPosInfinity32) + timeToWait = INFINITE; + + DWORD result =::WaitForSingleObject(fSemaH, timeToWait); + + if (result == WAIT_OBJECT_0) + return true; + else + { hsThrowIfFalse(result == WAIT_TIMEOUT); + return false; + } +} + +void hsSemaphore::Signal() +{ + ::ReleaseSemaphore(fSemaH, 1, nil); +} + +/////////////////////////////////////////////////////////////// + +hsEvent::hsEvent() +{ + fEvent = ::CreateEvent(nil,true,false,nil); + if (fEvent == nil) + throw hsOSException(-1); +} + +hsEvent::~hsEvent() +{ + ::CloseHandle(fEvent); +} + +hsBool hsEvent::Wait(hsMilliseconds timeToWait) +{ + if (timeToWait == kPosInfinity32) + timeToWait = INFINITE; + + DWORD result =::WaitForSingleObject(fEvent, timeToWait); + + if (result == WAIT_OBJECT_0) + { + ::ResetEvent(fEvent); + return true; + } + else + { hsThrowIfFalse(result == WAIT_TIMEOUT); + return false; + } +} + +void hsEvent::Signal() +{ + ::SetEvent(fEvent); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTypes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTypes.h new file mode 100644 index 00000000..da932d2d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsTypes.h @@ -0,0 +1,594 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#ifndef hsTypes_Defined +#define hsTypes_Defined + + +/************************** Other Includes *****************************/ + +#if !(HS_BUILD_FOR_REFERENCE) + #if HS_BUILD_FOR_MAC + #include + #include + #include + #include + #endif + #include + #include + +#endif +#if HS_CAN_USE_FLOAT + #include +#endif + + +/************************** Basic Macros *****************************/ + +#ifdef __cplusplus + #define hsCTypeDefStruct(foo) +#else + #define hsCTypeDefStruct(foo) typedef struct foo foo; +#endif + +/************************** Basic Types *****************************/ + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long dword; + +#ifdef _MSC_VER +typedef unsigned __int64 qword; +#else +typedef unsigned long long qword; +#endif + +typedef size_t unsigned_ptr; + +typedef unsigned short wchar_t; +typedef wchar_t wchar; + +#define kPosInfinity16 (32767) +#define kNegInfinity16 (-32768) + +#define kPosInfinity32 (0x7fffffff) +#define kNegInfinity32 (0x80000000) + +#if HS_BUILD_FOR_PS2 +typedef int Int32; +#else +typedef long Int32; +#endif + +typedef short Int16; +typedef signed char Int8; + +#if !(HS_BUILD_FOR_MAC) + typedef unsigned char UInt8; + typedef unsigned short UInt16; +#if HS_BUILD_FOR_PS2 + typedef unsigned int UInt32; +#else + typedef unsigned long UInt32; +#endif + + #ifndef Byte + typedef UInt8 Byte; + #endif + + #ifndef false + #define false 0 + #endif + #ifndef true + #define true 1 + #endif + #ifndef Boolean + #if HS_BUILD_FOR_UNIX + typedef char Boolean; + #else + typedef UInt8 Boolean; + #endif + #endif +#endif + +#if HS_BUILD_FOR_WIN32 + typedef __int64 Int64; + typedef unsigned __int64 UInt64; +#else + typedef long long Int64; + typedef unsigned long long UInt64; +#endif + +typedef Int32 hsFixed; +typedef Int32 hsFract; + +#ifdef __cplusplus + +typedef int hsBool; + +#endif + +#include "hsScalar.h" + +#if HS_CAN_USE_FLOAT + #define HS_PI 3.1415927 +#endif + +#ifndef nil +#define nil (0) +#endif + +typedef Int32 hsError; +typedef UInt32 hsGSeedValue; + +#define hsOK 0 +#define hsFail -1 +#define hsFailed(r) ((hsError)(r)=hsOK) + +#define hsLongAlign(n) (((n) + 3) & ~3L) + +#define hsMaximum(a, b) ((a) > (b) ? (a) : (b)) +#define hsMinimum(a, b) ((a) < (b) ? (a) : (b)) +#define hsABS(x) ((x) < 0 ? -(x) : (x)) +#define hsSGN(x) (((x) < 0) ? -1 : ( ((x) > 0) ? 1 : 0 )) + +#define hsBitTst2Bool(value, mask) (((value) & (mask)) != 0) + +#define hsFourByteTag(a, b, c, d) (((UInt32)(a) << 24) | ((UInt32)(b) << 16) | ((UInt32)(c) << 8) | (d)) + + +/************************** Swap Macros *****************************/ + +#ifdef __cplusplus + inline UInt16 hsSwapEndian16(UInt16 value) + { + return (value >> 8) | (value << 8); + } + inline UInt32 hsSwapEndian32(UInt32 value) + { + return (value << 24) | + ((value & 0xFF00) << 8) | + ((value >> 8) & 0xFF00) | + (value >> 24); + } + #if HS_CAN_USE_FLOAT + inline float hsSwapEndianFloat(float fvalue) + { + UInt32 value = *(UInt32*)&fvalue; + value = hsSwapEndian32(value); + return *(float*)&value; + } + #endif + + #if HS_CPU_LENDIAN + #define hsUNSWAP16(n) hsSwapEndian16(n) + #define hsUNSWAP32(n) hsSwapEndian32(n) + #define hsSWAP16(n) (n) + #define hsSWAP32(n) (n) + #else + #define hsUNSWAP16(n) (n) + #define hsUNSWAP32(n) (n) + #define hsSWAP16(n) hsSwapEndian16(n) + #define hsSWAP32(n) hsSwapEndian32(n) + #endif + + inline void hsSwap(Int32& a, Int32& b) + { + Int32 c = a; + a = b; + b = c; + } + + inline void hsSwap(UInt32& a, UInt32& b) + { + UInt32 c = a; + a = b; + b = c; + } + + #if HS_CAN_USE_FLOAT + inline void hsSwap(float& a, float& b) + { + float c = a; + a = b; + b = c; + } + #endif +#endif + +/************************** Color32 Type *****************************/ + +struct hsColor32 { + +#if 1 // hsColor32 fixed-format optimization + UInt8 b, g, r, a; +#else +#if (HS_BUILD_FOR_WIN32 || HS_BUILD_FOR_BE) + UInt8 b, g, r, a; +#elif (HS_BUILD_FOR_UNIX && HS_CPU_BENDIAN) + UInt8 a, b, g, r; +#else + UInt8 a, r, g, b; +#endif +#endif + +#ifdef __cplusplus + void SetARGB(UInt8 aa, UInt8 rr, UInt8 gg, UInt8 bb) + { + this->a = aa; this->r = rr; this->g = gg; this->b = bb; + } + + // Compatibility inlines, should be depricated + void Set(UInt8 rr, UInt8 gg, UInt8 bb) + { + this->r = rr; this->g = gg; this->b = bb; + } + void Set(UInt8 aa, UInt8 rr, UInt8 gg, UInt8 bb) + { + this->SetARGB(aa, rr, gg, bb); + } + +#if 0 + friend int operator==(const hsColor32& a, const hsColor32& b) + { + return *(UInt32*)&a == *(UInt32*)&b; + } + friend int operator!=(const hsColor32& a, const hsColor32& b) { return !(a == b); } +#else + int operator==(const hsColor32& aa) const + { + return *(UInt32*)&aa == *(UInt32*)this; + } + int operator!=(const hsColor32& aa) { return !(aa == *this); } +#endif +#endif +}; +hsCTypeDefStruct(hsColor32) + +typedef hsColor32 hsRGBAColor32; + + +/**************************************************************************** +* +* NULL_STMT +* Declares a null statement +* +* Example: +* +* for (; *str && (*str != ch); ++str) +* NULL_STMT; +* +***/ + +#if _MSC_VER >= 7 +# define NULL_STMT __noop +#else +# define NULL_STMT ((void)0) +#endif + + +/**************************************************************************** +* +* ref +* References a variable to prevent compiler warnings +* +***/ + +#define ref(a) ((void *)&(a)) + + +/**************************************************************************** +* +* UNIQUE_SYMBOL +* Creates a symbol that is unique within a file +* +***/ + +#define UNIQUE_SYMBOL_EXPAND_1(prefix,line) UNIQUE_SYMBOL_##prefix##_##line +#define UNIQUE_SYMBOL_EXPAND_0(prefix,line) UNIQUE_SYMBOL_EXPAND_1(prefix,line) +#define UNIQUE_SYMBOL(prefix) UNIQUE_SYMBOL_EXPAND_0(prefix,__LINE__) + + + +/**************************************************************************** +* +* COMPILER_ASSERT +* Performs a "compile-time" assertion +* Can be used at function or file scope +* Upon assertion failure, creates a negative subscript error +* Use COMPILER_ASSERT_HEADER in header files to prevent symbol collision +* +***/ + +#define COMPILER_ASSERT(expr) static char UNIQUE_SYMBOL(a)[(expr) ? 1 : -1] +#define COMPILER_ASSERT_HEADER(prefix,expr) static char UNIQUE_SYMBOL(prefix)[(expr) ? 1 : -1] +#define COMPILER_ASSERT_SYMBOL(symbol,expr) static char symbol[(expr) ? 1 : -1] + + +/**************************************************************************** +* +* max/min inline functions +* +***/ + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +//=========================================================================== +template +inline T max (const T & a, const T & b) { + return (a > b) ? a : b; +} + +//=========================================================================== +inline unsigned max (int a, unsigned b) { + return ((unsigned)a > b) ? a : b; +} + +//=========================================================================== +inline unsigned max (unsigned a, int b) { + return (a > (unsigned)b) ? a : b; +} + +//=========================================================================== +template +inline T min (const T & a, const T & b) { + return (a < b) ? a : b; +} + +//=========================================================================== +inline unsigned min (int a, unsigned b) { + return ((unsigned)a < b) ? a : b; +} + +//=========================================================================== +inline unsigned min (unsigned a, int b) { + return (a < (unsigned)b) ? a : b; +} + + +/**************************************************************************** +* +* MAX/MIN macros +* These are less safe than the inline function versions, since they +* evaluate parameters twice. However, they can be used to produce +* compile-time constants. +* +***/ + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + + +/**************************************************************************** +* +* SWAP +* Swaps the values of two variables +* +***/ + +//=========================================================================== +template +void SWAP (T & a, T & b) { + T temp = a; + a = b; + b = temp; +} + + +/**************************************************************************** +* +* AUTO_INIT_FUNC +* Declares a function that is automatically called at program startup time +* +* Example: +* +* AUTO_INIT_FUNC(BuildLookupTables) { +* ... +* } +* +***/ + +#define AUTO_INIT_FUNC(name) namespace { struct name { name (); } name##_instance; } name::name () + + +/**************************************************************************** +* +* Calculate the address to the base of a structure given its type, and the +* address of a field within the structure. +* +* Example: +* +* CONTAINING_STRUCT(trans, INetTrans, m_trans); +* +***/ + +#define CONTAINING_STRUCT(a, t, f) ((t *) ((byte *)(a) - (unsigned_ptr)(&((t *)0)->f))) + + +/**************************************************************************** +* +* arrsize/marrsize +* arrsize returns the number of elements in an array variable +* marrsize returns the number of elements in an array field in a structure +* +* Example: +* +* StrPrintf(buffer, arrsize(buffer), "%u", value); +* +***/ + +#define arrsize(a) (sizeof(a) / sizeof((a)[0])) +#define marrsize(c,a) (arrsize(((c *)0)->a)) + + +/**************************************************************************** +* +* offsetof/moffsetof +* offsetof returns the offset in bytes of a field inside a structure based on a type +* moffsetof returns the offset in bytes of a field inside a structure based on a variable +* +***/ + +#ifndef offsetof +#define offsetof(s,m) (size_t)&(((s *)0)->m) +#endif // ifndef offsetof + +#define moffsetof(v,f) (((byte *) &((v)->f)) - ((byte *) v)) + + +/**************************************************************************** +* +* msizeof +* Returns the size of a field in a structure +* +* Example: +* +* unsigned bufferSize = msizeof(CommandStruct, buffer); +* +***/ + +#define msizeof(c,a) (sizeof(((c *)0)->a)) + + +/**************************************************************************** +* +* ONCE +* Shortcut to create a 'for' loop that executes only once +* +* for (ONCE) { +* ... +* } +* +***/ + +#ifndef ONCE +#define ONCE bool UNIQUE_SYMBOL(ONCE) = true; UNIQUE_SYMBOL(ONCE); UNIQUE_SYMBOL(ONCE) = false +#endif + + +/**************************************************************************** +* +* IS_POW2 +* +***/ + +#define IS_POW2(val) (!((val) & ((val) - 1))) + + +/************************ Debug/Error Macros **************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*hsDebugMessageProc)(const char message[]); +extern hsDebugMessageProc gHSDebugProc; +#define HSDebugProc(m) { if (gHSDebugProc) gHSDebugProc(m); } +hsDebugMessageProc hsSetDebugMessageProc(hsDebugMessageProc newProc); + +extern hsDebugMessageProc gHSStatusProc; +hsDebugMessageProc hsSetStatusMessageProc(hsDebugMessageProc newProc); + +void __cdecl ErrorAssert (int line, const char file[], const char fmt[], ...); +void __cdecl ErrorFatal (int line, const char file[], const char fmt[], ...); +void ErrorMinimizeAppWindow (); + +enum EErrorOption { + kErrOptNonGuiAsserts, + kErrOptDisableMemLeakChecking, + kNumErrorOptions +}; +bool ErrorSetOption (EErrorOption option, bool on); +bool ErrorGetOption (EErrorOption option); + +bool DebugIsDebuggerPresent (); +void DebugBreakIfDebuggerPresent (); +void DebugMsg (const char fmt[], ...); +void DebugMsgV (const char fmt[], va_list args); + + +#ifdef HS_DEBUGGING + + void hsDebugMessage(const char message[], long refcon); + #define hsDebugCode(code) code + #define hsIfDebugMessage(expr, msg, ref) (void)( ((expr) != 0) || (hsDebugMessage(msg, ref), 0) ) + #define hsAssert(expr, msg) (void)( ((expr) != 0) || (ErrorAssert(__LINE__, __FILE__, msg), 0) ) + #define ASSERT(expr) (void)( ((expr) != 0) || (ErrorAssert(__LINE__, __FILE__, #expr), 0) ) + #define ASSERTMSG(expr, msg) (void)( ((expr) != 0) || (ErrorAssert(__LINE__, __FILE__, msg), 0) ) + #define FATAL(msg) ErrorAssert(__LINE__, __FILE__, msg) + #define DEBUG_MSG DebugMsg + #define DEBUG_MSGV DebugMsgV + #define DEBUG_BREAK_IF_DEBUGGER_PRESENT DebugBreakIfDebuggerPresent + +#else /* Not debugging */ + + #define hsDebugMessage(message, refcon) NULL_STMT + #define hsDebugCode(code) /* empty */ + #define hsIfDebugMessage(expr, msg, ref) NULL_STMT + #define hsAssert(expr, msg) NULL_STMT + #define ASSERT(expr) NULL_STMT + #define ASSERTMSG(expr, msg) NULL_STMT + #define FATAL(msg) NULL_STMT + #define DEBUG_MSG NULL_STMT + #define DEBUG_MSGV NULL_STMT + #define DEBUG_BREAK_IF_DEBUGGER_PRESENT NULL_STMT + +#endif // HS_DEBUGGING + + +#ifdef _MSC_VER +#define DEFAULT_FATAL(var) default: FATAL("No valid case for switch variable '" #var "'"); __assume(0); break; +#else +#define DEFAULT_FATAL(var) default: FATAL("No valid case for switch variable '" #var "'"); break; +#endif + + +#ifdef PLASMA_EXTERNAL_RELEASE + + #define hsStatusMessage(x) NULL_STMT + #define hsStatusMessageF(x,y) NULL_STMT + +#else /* Not external release */ + + void hsStatusMessage(const char message[]); + void hsStatusMessageF(const char * fmt, ...); + +#endif // PLASMA_EXTERNAL_RELEASE + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsUtils.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsUtils.cpp new file mode 100644 index 00000000..93556fde --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsUtils.cpp @@ -0,0 +1,752 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsUtils.h" +#if HS_BUILD_FOR_MAC +#include +#endif +#if HS_BUILD_FOR_WIN32 +extern "C" { +#endif +#include +#include +#if HS_BUILD_FOR_WIN32 +}; +#endif +#if __MWERKS__ +#include +#endif +#if HS_BUILD_FOR_PS2 +#include +#include "eekernel.h" +#include "sifdev.h" +#endif + +#if HS_BUILD_FOR_WIN32 +#include +#include +#endif +#include "hsStlUtils.h" +#include "hsTemplates.h" + + +char * hsFormatStr(const char * fmt, ...) +{ + va_list args; + va_start(args,fmt); + char * result = hsFormatStrV(fmt,args); + va_end(args); + return result; +} + +char * hsFormatStrV(const char * fmt, va_list args) +{ + std::string buf; + xtl::formatv(buf,fmt,args); + return hsStrcpy(buf.c_str()); +} + +static char hsStrBuf[100]; + +char *hsScalarToStr(hsScalar s) +{ +#if !(HS_BUILD_FOR_REFERENCE) + if (s == hsIntToScalar(hsScalarToInt(s))) + sprintf(hsStrBuf, "%d", hsScalarToInt(s)); + else + #if HS_CAN_USE_FLOAT + sprintf(hsStrBuf, "%f", hsScalarToFloat(s)); + #else + sprintf(hsStrBuf, "%d:%lu", hsFixedToInt(s), (UInt16)s); + #endif +#endif + return hsStrBuf; +} + +bool hsMessageBox_SuppressPrompts = false; + +int hsMessageBoxWithOwner(void * owner, const char message[], const char caption[], int kind, int icon) +{ + if (hsMessageBox_SuppressPrompts) + return hsMBoxOk; + +#if HS_BUILD_FOR_WIN32 + UInt32 flags = 0; + + if (kind == hsMessageBoxNormal) + flags |= MB_OK; + else if (kind == hsMessageBoxAbortRetyIgnore) + flags |= MB_ABORTRETRYIGNORE; + else if (kind == hsMessageBoxOkCancel) + flags |= MB_OKCANCEL; + else if (kind == hsMessageBoxRetryCancel) + flags |= MB_RETRYCANCEL; + else if (kind == hsMessageBoxYesNo) + flags |= MB_YESNO; + else if (kind == hsMessageBoxYesNoCancel) + flags |= MB_YESNOCANCEL; + else + flags |= MB_OK; + + if (icon == hsMessageBoxIconError) + flags |= MB_ICONERROR; + else if (icon == hsMessageBoxIconQuestion) + flags |= MB_ICONQUESTION; + else if (icon == hsMessageBoxIconExclamation) + flags |= MB_ICONEXCLAMATION; + else if (icon == hsMessageBoxIconAsterisk) + flags |= MB_ICONASTERISK; + else + flags |= MB_ICONERROR; + +#ifdef CLIENT + ErrorMinimizeAppWindow(); +#endif + int ans = MessageBox((HWND)owner, message, caption, flags); + + switch (ans) + { + case IDOK: return hsMBoxOk; + case IDCANCEL: return hsMBoxCancel; + case IDABORT: return hsMBoxAbort; + case IDRETRY: return hsMBoxRetry; + case IDIGNORE: return hsMBoxIgnore; + case IDYES: return hsMBoxYes; + case IDNO: return hsMBoxNo; + default: return hsMBoxCancel; + } + +#endif +#if HS_BUILD_FOR_MACPPC + DebugStr(message); +#endif +#if HS_BUILD_FOR_PS2 + printf("Cap:%s Message:%s\n",caption, message); +#endif +} + +int hsMessageBoxWithOwner(void * owner, const wchar_t message[], const wchar_t caption[], int kind, int icon) +{ + if (hsMessageBox_SuppressPrompts) + return hsMBoxOk; + +#if HS_BUILD_FOR_WIN32 + UInt32 flags = 0; + + if (kind == hsMessageBoxNormal) + flags |= MB_OK; + else if (kind == hsMessageBoxAbortRetyIgnore) + flags |= MB_ABORTRETRYIGNORE; + else if (kind == hsMessageBoxOkCancel) + flags |= MB_OKCANCEL; + else if (kind == hsMessageBoxRetryCancel) + flags |= MB_RETRYCANCEL; + else if (kind == hsMessageBoxYesNo) + flags |= MB_YESNO; + else if (kind == hsMessageBoxYesNoCancel) + flags |= MB_YESNOCANCEL; + else + flags |= MB_OK; + + if (icon == hsMessageBoxIconError) + flags |= MB_ICONERROR; + else if (icon == hsMessageBoxIconQuestion) + flags |= MB_ICONQUESTION; + else if (icon == hsMessageBoxIconExclamation) + flags |= MB_ICONEXCLAMATION; + else if (icon == hsMessageBoxIconAsterisk) + flags |= MB_ICONASTERISK; + else + flags |= MB_ICONERROR; + +#ifdef CLIENT + ErrorMinimizeAppWindow(); +#endif + int ans = MessageBoxW((HWND)owner, message, caption, flags); + + switch (ans) + { + case IDOK: return hsMBoxOk; + case IDCANCEL: return hsMBoxCancel; + case IDABORT: return hsMBoxAbort; + case IDRETRY: return hsMBoxRetry; + case IDIGNORE: return hsMBoxIgnore; + case IDYES: return hsMBoxYes; + case IDNO: return hsMBoxNo; + default: return hsMBoxCancel; + } + +#endif +#if HS_BUILD_FOR_MACPPC + DebugStr(message); +#endif +#if HS_BUILD_FOR_PS2 + printf("Cap:%s Message:%s\n",caption, message); +#endif +} + +int hsMessageBox(const char message[], const char caption[], int kind, int icon) +{ +#if HS_BUILD_FOR_WIN32 + return hsMessageBoxWithOwner(nil/*GetActiveWindow()*/,message,caption,kind,icon); +#else + return hsMessageBoxWithOwner(nil,message,caption,kind,icon); +#endif +} + +int hsMessageBox(const wchar_t message[], const wchar_t caption[], int kind, int icon) +{ +#if HS_BUILD_FOR_WIN32 + return hsMessageBoxWithOwner(nil/*GetActiveWindow()*/,message,caption,kind,icon); +#else + return hsMessageBoxWithOwner(nil,message,caption,kind,icon); +#endif +} + + +/* Generic psuedo RNG used in ANSI C. */ +static unsigned long SEED = 1; +int hsRand() +{ + register int temp; + SEED = SEED * 1103515245 + 12345; + temp = (int)((SEED/65536)&32767); + return (temp); +} + +void hsRandSeed(int seed) +{ + SEED = seed; +} +/**************************************/ +int hsStrlen(const char src[]) +{ + if (src==nil) + return 0; + + int i = 0; + while (src[i]) + i++; + return i; +} + +char* hsStrcpy(char dst[], const char src[]) +{ + if (src) + { + if (dst == nil) + { + int count = hsStrlen(src); + dst = (char *)ALLOC(count + 1); + memcpy(dst, src, count); + dst[count] = 0; + return dst; + } + + Int32 i; + for (i = 0; src[i] != 0; i++) + dst[i] = src[i]; + dst[i] = 0; + } + + return dst; +} + +hsBool hsStrEQ(const char s1[], const char s2[]) +{ + if (s1 && s2) + { + while (*s1) + if(*s1++ != *s2++) + return false; + return *s2 == 0; + } + + return (!s1 && !s2); +} + +hsBool hsStrCaseEQ(const char* s1, const char* s2) +{ + if (s1 && s2) + { + while (*s1) + if(tolower(*s1++) != tolower(*s2++)) + return false; + return *s2 == 0; + } + + return (!s1 && !s2); +} + +void hsStrcat(char dst[], const char src[]) +{ + if (src && dst) + { + dst += hsStrlen(dst); + while(*src) + *dst++ = *src++; + *dst = 0; + } +} + +void hsStrLower(char *s) +{ + if (s) + { + int i; + for (i = 0; i < hsStrlen(s); i++) + s[i] = tolower(s[i]); + } +} + +char* hsP2CString(const UInt8 pstring[], char cstring[]) +{ + char* cstr = cstring; + const UInt8* stop = &pstring[1] + pstring[0]; + + pstring += 1; // skip length byte + while (pstring < stop) + *cstr++ = *pstring++; + *cstr = 0; + return cstring; +} + +UInt8* hsC2PString(const char cstring[], UInt8 pstring[]) +{ + int i; + + for (i = 1; *cstring; i++) + pstring[i] = *cstring++; + pstring[0] = i - 1; + return pstring; +} + +//// IStringToWString ///////////////////////////////////////////////////////// +// Converts a char * string to a wchar_t * string + +wchar_t *hsStringToWString( const char *str ) +{ + // convert the char string to a wchar_t string + int len = strlen(str); + wchar_t *wideString = TRACKED_NEW wchar_t[len+1]; + for (int i=0; i c2) return 1; + if (c1 == '\0') return 0; + } + } + return !s1 ? -1 : 1; +} + +// Compare lexigraphically two strings up to a max length + +int hsStrncasecmp(const char *s1, const char *s2, int n) +{ + if (s1 && s2) + { + int i; + char c1, c2; + for (i=0; i c2) return 1; + if (!c1) return 0; + } + return 0; + } + return !s1 ? -1 : 1; +} +#endif + +// +// Microsoft SAMPLE CODE +// returns array of allocated version info strings or nil +// +char** DisplaySystemVersion() +{ +#if HS_BUILD_FOR_WIN32 +#ifndef VER_SUITE_PERSONAL +#define VER_SUITE_PERSONAL 0x200 +#endif + hsTArray versionStrs; + OSVERSIONINFOEX osvi; + BOOL bOsVersionInfoEx; + + // Try calling GetVersionEx using the OSVERSIONINFOEX structure. + // + // If that fails, try using the OSVERSIONINFO structure. + + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) ) + { + // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. + + osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) + return FALSE; + } + + switch (osvi.dwPlatformId) + { + case VER_PLATFORM_WIN32_NT: + + // Test for the product. + + if ( osvi.dwMajorVersion <= 4 ) + versionStrs.Append(hsStrcpy("Microsoft Windows NT ")); + + if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) + versionStrs.Append(hsStrcpy ("Microsoft Windows 2000 ")); + + if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 ) + versionStrs.Append(hsStrcpy ("Microsoft Windows XP ")); + + // Test for product type. + + if( bOsVersionInfoEx ) + { + if ( osvi.wProductType == VER_NT_WORKSTATION ) + { + if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) + versionStrs.Append(hsStrcpy ( "Personal " )); + else + versionStrs.Append(hsStrcpy ( "Professional " )); + } + + else if ( osvi.wProductType == VER_NT_SERVER ) + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + versionStrs.Append(hsStrcpy ( "DataCenter Server " )); + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + versionStrs.Append(hsStrcpy ( "Advanced Server " )); + else + versionStrs.Append(hsStrcpy ( "Server " )); + } + } + else + { + HKEY hKey; + char szProductType[80]; + DWORD dwBufLen; + + RegOpenKeyEx( HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\ProductOptions", + 0, KEY_QUERY_VALUE, &hKey ); + RegQueryValueEx( hKey, "ProductType", NULL, NULL, + (LPBYTE) szProductType, &dwBufLen); + RegCloseKey( hKey ); + if ( lstrcmpi( "WINNT", szProductType) == 0 ) + versionStrs.Append(hsStrcpy( "Professional " )); + if ( lstrcmpi( "LANMANNT", szProductType) == 0 ) + versionStrs.Append(hsStrcpy( "Server " )); + if ( lstrcmpi( "SERVERNT", szProductType) == 0 ) + versionStrs.Append(hsStrcpy( "Advanced Server " )); + } + + // Display version, service pack (if any), and build number. + + if ( osvi.dwMajorVersion <= 4 ) + { + versionStrs.Append(hsStrcpy (xtl::format("version %d.%d %s (Build %d)\n", + osvi.dwMajorVersion, + osvi.dwMinorVersion, + osvi.szCSDVersion, + osvi.dwBuildNumber & 0xFFFF).c_str())); + } + else + { + versionStrs.Append(hsStrcpy (xtl::format("%s (Build %d)\n", + osvi.szCSDVersion, + osvi.dwBuildNumber & 0xFFFF).c_str())); + } + break; + + case VER_PLATFORM_WIN32_WINDOWS: + + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) + { + versionStrs.Append(hsStrcpy ("Microsoft Windows 95 ")); + if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' ) + versionStrs.Append(hsStrcpy("OSR2 " )); + } + + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) + { + versionStrs.Append(hsStrcpy ("Microsoft Windows 98 ")); + if ( osvi.szCSDVersion[1] == 'A' ) + versionStrs.Append(hsStrcpy("SE " )); + } + + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) + { + versionStrs.Append(hsStrcpy ("Microsoft Windows Me ")); + } + break; + + case VER_PLATFORM_WIN32s: + + versionStrs.Append(hsStrcpy ("Microsoft Win32s ")); + break; + } + + versionStrs.Append(nil); // terminator + + return versionStrs.DetachArray(); +#else + return nil; +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsUtils.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsUtils.h new file mode 100644 index 00000000..e657298b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsUtils.h @@ -0,0 +1,188 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsUtils_Defined +#define hsUtils_Defined + +#include "HeadSpin.h" + +#include +#include +#include + +int hsStrlen(const char src[]); +char* hsStrcpy(char dstOrNil[], const char src[]); +void hsStrcat(char dst[], const char src[]); +hsBool hsStrEQ(const char s1[], const char s2[]); +hsBool hsStrCaseEQ(const char* s1, const char* s2); +char* hsScalarToStr(hsScalar); +int hsRemove(const char* filename); +void hsCPathToMacPath(char* dst, char* fname); +void hsStrLower(char *s); +char * hsFormatStr(const char * fmt, ...); // You are responsible for returned memory. +char * hsFormatStrV(const char * fmt, va_list args); // You are responsible for returned memory. + + +// A pstring has a length byte at the beginning, and no trailing 0 +char* hsP2CString(const UInt8 pstring[], char cstring[]); +UInt8* hsC2PString(const char cstring[], UInt8 pstring[]); + +inline char* hsStrcpy(const char src[]) +{ + return hsStrcpy(nil, src); +} + +inline char *hsStrncpy(char *strDest, const char *strSource, size_t count) +{ + char *temp = strncpy(strDest, strSource, count-1); + strDest[count-1] = 0; + return temp; +} + +wchar_t *hsStringToWString( const char *str ); +void hsStringToWString( wchar_t *dst, const char *src ); +char *hsWStringToString( const wchar_t *str ); +void hsWStringToString( char *dst, const wchar_t *src); + +enum { // Kind of MessageBox...passed to hsMessageBox + hsMessageBoxAbortRetyIgnore, + hsMessageBoxNormal, // Just Ok + hsMessageBoxOkCancel, + hsMessageBoxRetryCancel, + hsMessageBoxYesNo, + hsMessageBoxYesNoCancel, +}; + +enum { + hsMessageBoxIconError, + hsMessageBoxIconQuestion, + hsMessageBoxIconExclamation, + hsMessageBoxIconAsterisk, +}; + +enum { // RETURN VALUES FROM hsMessageBox + hsMBoxOk = 1, // OK button was selected. + hsMBoxCancel, // Cancel button was selected. + hsMBoxAbort, // Abort button was selected. + hsMBoxRetry, // Retry button was selected. + hsMBoxIgnore, // Ignore button was selected. + hsMBoxYes, // Yes button was selected. + hsMBoxNo // No button was selected. +}; + +extern bool hsMessageBox_SuppressPrompts; +int hsMessageBox(const char message[], const char caption[], int kind, int icon=hsMessageBoxIconAsterisk); +int hsMessageBox(const wchar_t message[], const wchar_t caption[], int kind, int icon=hsMessageBoxIconAsterisk); +int hsMessageBoxWithOwner(void* owner, const char message[], const char caption[], int kind, int icon=hsMessageBoxIconAsterisk); +int hsMessageBoxWithOwner(void* owner, const wchar_t message[], const wchar_t caption[], int kind, int icon=hsMessageBoxIconAsterisk); + +inline hsBool hsCompare(hsScalar a, hsScalar b, hsScalar delta=0.0001) +{ + return (fabs(a - b) < delta); +} + +// flag testing / clearing +#define hsCheckBits(f,c) ((f & c)==c) +#define hsTestBits(f,b) ( (f) & (b) ) +#define hsSetBits(f,b) ( (f) |= (b) ) +#define hsClearBits(f,b) ( (f) &= ~(b) ) +#define hsToggleBits(f,b) ( (f) ^= (b) ) +#define hsChangeBits(f,b,t) ( t ? hsSetBits(f,b) : hsClearBits(f,b) ) + + +#if HS_BUILD_FOR_WIN32 +#define hsVsnprintf _vsnprintf +#define hsVsnwprintf _vsnwprintf +#define snprintf _snprintf +#define snwprintf _snwprintf +#define hsSnprintf snprintf +#define hsSnwprintf snwprintf +#else +#define hsVsnprintf vsnprintf +#define hsWvnwprintf vsnwprintf +#define hsSnprintf snprintf +#define hsSnwprintf snwprintf +#define _snprintf snprintf +#define _snwprintf snwprintf +#endif + + +#if HS_BUILD_FOR_UNIX || HS_BUILD_FOR_PS2 + +#define _stricmp(s1, s2) strcasecmp(s1, s2) +#define _strnicmp(s1, s2, n) strncasecmp(s1, s2, n) +#define stricmp(s1, s2) strcasecmp(s1, s2) +#define strnicmp(s1, s2, n) strncasecmp(s1, s2, n) + +#define _fileno(n) fileno(n) + + +#elif HS_BUILD_FOR_MAC // HS_BUILD_FOR_UNIX || HS_BUILD_FOR_PS2 + +int hsStrcasecmp(const char s1[], const char s2[]); +int hsStrncasecmp(const char s1[], const char s2[], int n); + +#define _stricmp(s1, s2) hsStrcasecmp(s1, s2) +#define _strnicmp(s1, s2, n) hsStrncasecmp(s1, s2, n) + +#endif // HS_BUILD_FOR_UNIX || HS_BUILD_FOR_PS2 + +///////////////////////////// +// Physical memory functions +///////////////////////////// +enum MemSpec +{ + kBlows = 0, // Less than 128 + kAcceptable, // Less than 256 + kOptimal // 256 or greater +}; + +UInt32 hsPhysicalMemory(); +MemSpec hsMemorySpec(); + +inline int hsRandMax() { return 32767; } +inline float hsRandNorm() { return 1.f / 32767.f; } // multiply by hsRand to get randoms ranged [0..1] +int hsRand(void); +void hsRandSeed(int seed); + + +#if HS_BUILD_FOR_MAC +FILE* hsFopen(const char name[], const char mode[]); // handles path names with /s + +#elif HS_BUILD_FOR_PS2 // HS_BUILD_FOR_MAC + +int hsPS2Open(const char name[], const char mode[]); +void hsPS2Close( int file ); + +#else // HS_BUILD_FOR_MAC + +#define hsFopen(name, mode) fopen(name, mode) + +#endif // HS_BUILD_FOR_MAC + +char** DisplaySystemVersion(); + +#endif // hsUtils_Defined + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWide.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWide.cpp new file mode 100644 index 00000000..d0f5535f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWide.cpp @@ -0,0 +1,337 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsWide.h" + +///////////////////////////////////////////////////////////////////////// + +inline hsBool OverflowAdd(UInt32* sum, UInt32 a, UInt32 b) +{ + *sum = a + b; + + return (a | b) > *sum; // true if overflow +} + +/* + Return the overflow from adding the three longs into a signed-wide + + wide = (high << 32) + (middle << 16) + low +*/ +inline hsBool SetWide3(hsWide* target, Int32 high, UInt32 middle, UInt32 low) +{ + hsAssert(high >= 0, "high is neg"); + + target->fLo = low + (middle << 16); + target->fHi = high + (middle >> 16) + (((low >> 16) + (UInt16)middle) >> 16); + + return target->fHi < 0; // true if overflow +} + +///////////////////////////////////////////////////////////////////////// + +hsWide* hsWide::Mul(Int32 src1, Int32 src2) +{ + int neg = 0; + + if (src1 < 0) + { src1 = -src1; + neg = ~0; + } + if (src2 < 0) + { src2 = -src2; + neg = ~neg; + } + + UInt32 a = src1 >> 16; + UInt32 b = (UInt16)src1; + UInt32 c = src2 >> 16; + UInt32 d = (UInt16)src2; + + (void)SetWide3(this, a * c, a * d + c * b, b * d); + + if (neg) + this->Negate(); + return this; +} + +hsWide* hsWide::Mul(Int32 A) +{ + int neg = 0; + UInt32 B = fLo; + Int32 C = fHi; + Int32 tmp; + UInt32 clo,blo,bhi,alo; + + if (A < 0) + { A = -A; + neg = ~0; + } + if (WIDE_ISNEG(C, B)) + { WIDE_NEGATE(C, B); + neg = ~neg; + } + + UInt32 ahi = A >> 16; + UInt32 chi = C >> 16; + if (ahi != 0 && chi != 0) + goto OVER_FLOW; + + alo = (UInt16)A; + bhi = B >> 16; + blo = (UInt16)B; + clo = (UInt16)C; + + tmp = alo * clo; + if (tmp < 0 || SetWide3(this, tmp, alo * bhi, alo * blo)) + goto OVER_FLOW; + + if (chi != 0) + { UInt32 Vh = alo * chi; + if (Vh >> 15) + goto OVER_FLOW; + if (((this->fHi >> 16) + (UInt16)Vh) >> 15) + goto OVER_FLOW; + this->fHi += Vh << 16; + } + else // ahi != 0 && chi == 0 + { hsWide w; + UInt32 Vh = ahi * clo; + if (Vh >> 16) + goto OVER_FLOW; + tmp = ahi * bhi; + if (tmp < 0 || SetWide3(&w, tmp, ahi * blo, 0)) + goto OVER_FLOW; + if (((w.fHi >> 16) + (UInt16)Vh) >> 15) + goto OVER_FLOW; + w.fHi += Vh << 16; + this->Add(&w); + } + + if (neg) + this->Negate(); + return this; + +OVER_FLOW: + *this = neg ? kNegInfinity64 : kPosInfinity64; + return this; +} + +hsWide* hsWide::Div(Int32 denom) +{ + if (denom == 0) + { if (this->IsNeg()) + { hsSignalMathUnderflow(); + *this = kNegInfinity64; + } + else + { hsSignalMathOverflow(); + *this = kPosInfinity64; + } + return this; + } + + int neg = 0; + Int32 resultH = 0; + UInt32 resultL = 0; + Int32 numerH = this->fHi; + UInt32 numerL = this->fLo; + + if (denom < 0) + { denom = -denom; + neg = ~0; + } + if (WIDE_ISNEG(numerH, numerL)) + { WIDE_NEGATE(numerH, numerL); + neg = ~neg; + } + + WIDE_ADDPOS(numerH, numerL, denom >> 1); // add denom/2 to get a round result + + UInt32 curr = (UInt32)numerH >> 31; + + for (int i = 0; i < 64; i++) + { + WIDE_SHIFTLEFT(resultH, resultL, resultH, resultL, 1); + if (UInt32(denom) <= curr) + { + resultL |= 1; + curr -= denom; + } + WIDE_SHIFTLEFT(numerH, numerL, numerH, numerL, 1); + curr = (curr << 1) | ((UInt32)numerH >> 31); + } + + if (neg) + WIDE_NEGATE(resultH, resultL); + return this->Set(resultH, resultL); +} + +hsWide* hsWide::Div(const hsWide* denom) +{ + hsWide d = *denom; + int shift = 0; + + while (d.IsWide()) + { (void)d.ShiftRight(1); + shift += 1; + } + if (shift) + { d = *denom; + (void)this->RoundRight(shift); + (void)d.RoundRight(shift); + } + return this->Div(d.AsLong()); +} + +inline int MaxLeftShift(const hsWide* w) +{ + Int32 hi = w->fHi; + + if (hi == 0) + return 31; + else + { int shift = -1; + + if (hi < 0) hi = -hi; + + do { + hi <<= 1; + shift += 1; + } while (hi > 0); + return shift; + } +} + +hsFixed hsWide::FixDiv(const hsWide* denom) const +{ + hsWide num = *this; + hsWide den = *denom; + int maxShift = MaxLeftShift(this); + + if (maxShift >= 16) // easy case + (void)num.ShiftLeft(16); + else + { (void)num.ShiftLeft(maxShift); + (void)den.RoundRight(16 - maxShift); + } + + return num.Div(&den)->AsLong(); +} + +hsFract hsWide::FracDiv(const hsWide* denom) const +{ + hsWide num = *this; + hsWide den = *denom; + int maxShift = MaxLeftShift(this); + + if (maxShift >= 30) // easy case + (void)num.ShiftLeft(30); + else + { (void)num.ShiftLeft(maxShift); + (void)den.RoundRight(30 - maxShift); + } + + return num.Div(&den)->AsLong(); +} + +//////////////////////////////////////////////////////////////////////////////////// + +Int32 hsWide::Sqrt() const +{ + int bits = 32; + UInt32 root = 0; + UInt32 valueH = (UInt32)fHi; + UInt32 valueL = fLo; + UInt32 currH = 0; + UInt32 currL = 0; + UInt32 guessH, guessL; + + do { + WIDE_SHIFTLEFT(currH, currL, currH, currL, 2); + currL |= TOP2BITS(valueH); + WIDE_SHIFTLEFT(valueH, valueL, valueH, valueL, 2); + WIDE_SHIFTLEFT(guessH, guessL, 0, root, 2); + root <<= 1; + if (WIDE_LESSTHAN(guessH, guessL, currH, currL)) + { WIDE_ADDPOS(guessH, guessL, 1); + WIDE_SUBWIDE(currH, currL, guessH, guessL); + root |= 1; + } + } while (--bits); + +#if HS_PIN_MATH_OVERFLOW + if ((Int32)root < 0) + return kPosInfinity32; +#endif + return (Int32)root; +} + +Int32 hsWide::CubeRoot() const +{ + int bits = 21; + UInt32 root = 0; + UInt32 valueH = (UInt32)fHi; + UInt32 valueL = fLo; + UInt32 currH, currL; + UInt32 guessH, guessL; + hsBool neg = false; + + if (WIDE_ISNEG(valueH, valueL)) + { neg = true; + WIDE_NEGATE(valueH, valueL); + } + + currH = currL = 0; + WIDE_SHIFTLEFT(valueH, valueL, valueH, valueL, 1); + do { + WIDE_SHIFTLEFT(currH, currL, currH, currL, 3); + currL |= TOP3BITS(valueH); + WIDE_SHIFTLEFT(valueH, valueL, valueH, valueL, 3); + + root <<= 1; + + hsWide w; + w.Mul(root, root)->Add(root); + #if 0 + w.Mul(3); + #else + hsWide w2 = w; + w.ShiftLeft(1)->Add(&w2); + #endif + guessH = (UInt32)w.fHi; + guessL = w.fLo; + + if (WIDE_LESSTHAN(guessH, guessL, currH, currL)) + { WIDE_ADDPOS(guessH, guessL, 1); + WIDE_SUBWIDE(currH, currL, guessH, guessL); + root |= 1; + } + } while (--bits); + + if (neg) + root = -Int32(root); + return (Int32)root; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWide.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWide.h new file mode 100644 index 00000000..4fbc0100 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWide.h @@ -0,0 +1,205 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsWideDefined +#define hsWideDefined + +#include "hsTypes.h" + +struct hsWide { + Int32 fHi; + UInt32 fLo; + + hsWide* Set(Int32 lo) { fLo = lo; if (lo < 0) fHi = -1L; else fHi = 0; return this; } + hsWide* Set(Int32 hi, UInt32 lo) { fHi = hi; fLo = lo; return this; } + + inline hsBool IsNeg() const { return fHi < 0; } + inline hsBool IsPos() const { return fHi > 0 || (fHi == 0 && fLo != 0); } + inline hsBool IsZero() const { return fHi == 0 && fLo == 0; } + inline hsBool IsWide() const; + + + hsBool operator==(const hsWide& b) const { return fHi == b.fHi && fLo == b.fLo; } + hsBool operator<(const hsWide& b) const { return fHi < b.fHi || fHi == b.fHi && fLo < b.fLo; } + hsBool operator>( const hsWide& b) const { return fHi > b.fHi || fHi == b.fHi && fLo > b.fLo; } + hsBool operator!=( const hsWide& b) const { return !( *this == b); } + hsBool operator<=(const hsWide& b) const { return !(*this > b); } + hsBool operator>=(const hsWide& b) const { return !(*this < b); } + + inline hsWide* Negate(); + inline hsWide* Add(Int32 scaler); + inline hsWide* Add(const hsWide* a); + inline hsWide* Sub(const hsWide* a); + inline hsWide* ShiftLeft(unsigned shift); + inline hsWide* ShiftRight(unsigned shift); + inline hsWide* RoundRight(unsigned shift); + + inline Int32 AsLong() const; // return bits 31-0, checking for over/under flow + inline hsFixed AsFixed() const; // return bits 47-16, checking for over/under flow + inline hsFract AsFract() const; // return bits 61-30, checking for over/under flow + + hsWide* Mul(Int32 a); // this updates the wide + hsWide* Mul(Int32 a, Int32 b); // this sets the wide + hsWide* Div(Int32 denom); // this updates the wide + hsWide* Div(const hsWide* denom); // this updates the wide + + hsFixed FixDiv(const hsWide* denom) const; + hsFract FracDiv(const hsWide* denom) const; + + Int32 Sqrt() const; + Int32 CubeRoot() const; + +#if HS_CAN_USE_FLOAT + double AsDouble() const { return fHi * double(65536) * double(65536) + fLo; } + hsWide* Set(double d) + { + Int32 hi = Int32(d / double(65536) / double(65536)); + Int32 lo = Int32(d - double(hi)); + return Set(hi, lo); + } +#endif + +}; + +const hsWide kPosInfinity64 = { kPosInfinity32, 0xffffffff }; +const hsWide kNegInfinity64 = { kNegInfinity32, 0 }; + +/////////////////////// Inline implementations /////////////////////// + +#define TOP2BITS(n) (UInt32(n) >> 30) +#define TOP3BITS(n) (UInt32(n) >> 29) + +#if HS_PIN_MATH_OVERFLOW && HS_DEBUG_MATH_OVERFLOW + #define hsSignalMathOverflow() hsDebugMessage("Math overflow", 0) + #define hsSignalMathUnderflow() hsDebugMessage("Math underflow", 0) +#else + #define hsSignalMathOverflow() + #define hsSignalMathUnderflow() +#endif + +#define WIDE_ISNEG(hi, lo) (Int32(hi) < 0) +#define WIDE_LESSTHAN(hi, lo, hi2, lo2) ((hi) < (hi2) || (hi) == (hi2) && (lo) < (lo2)) +#define WIDE_SHIFTLEFT(outH, outL, inH, inL, shift) do { (outH) = ((inH) << (shift)) | ((inL) >> (32 - (shift))); (outL) = (inL) << (shift); } while (0) +#define WIDE_NEGATE(hi, lo) do { (hi) = ~(hi); if (((lo) = -Int32(lo)) == 0) (hi) += 1; } while (0) +#define WIDE_ADDPOS(hi, lo, scaler) do { UInt32 tmp = (lo) + (scaler); if (tmp < (lo)) (hi) += 1; (lo) = tmp; } while (0) +#define WIDE_SUBWIDE(hi, lo, subhi, sublo) do { (hi) -= (subhi); if ((lo) < (sublo)) (hi) -= 1; (lo) -= (sublo); } while (0) + +/////////////////////// Inline implementations /////////////////////// + +inline hsWide* hsWide::Negate() +{ + WIDE_NEGATE(fHi, fLo); + + return this; +} + +inline hsWide* hsWide::Add(Int32 scaler) +{ + if (scaler >= 0) + WIDE_ADDPOS(fHi, fLo, scaler); + else + { scaler = -scaler; + if (fLo < UInt32(scaler)) + fHi--; + fLo -= scaler; + } + + return this; +} + +inline hsWide* hsWide::Add(const hsWide* a) +{ + UInt32 newLo = fLo + a->fLo; + + fHi += a->fHi; + if (newLo < (fLo | a->fLo)) + fHi++; + fLo = newLo; + + return this; +} + +inline hsWide* hsWide::Sub(const hsWide* a) +{ + WIDE_SUBWIDE(fHi, fLo, a->fHi, a->fLo); + + return this; +} + +inline hsWide* hsWide::ShiftLeft(unsigned shift) +{ + WIDE_SHIFTLEFT(fHi, fLo, fHi, fLo, shift); + + return this; +} + +inline hsWide* hsWide::ShiftRight(unsigned shift) +{ + fLo = (fLo >> shift) | (fHi << (32 - shift)); + fHi = fHi >> shift; // fHi >>= shift; Treated as logical shift on CW9-WIN32, which breaks for fHi < 0 + + return this; +} + +inline hsWide* hsWide::RoundRight(unsigned shift) +{ + return this->Add(1L << (shift - 1))->ShiftRight(shift); +} + +inline Int32 hsWide::AsLong() const +{ +#if HS_PIN_MATH_OVERFLOW + if (fHi > 0 || fHi == 0 && (Int32)fLo < 0) + { hsSignalMathOverflow(); + return kPosInfinity32; + } + if (fHi < -1L || fHi == -1L && (Int32)fLo >= 0) + { hsSignalMathOverflow(); + return kNegInfinity32; + } +#endif + return (Int32)fLo; +} + +inline hsBool hsWide::IsWide() const +{ + return (fHi > 0 || fHi == 0 && (Int32)fLo < 0) || (fHi < -1L || fHi == -1L && (Int32)fLo >= 0); +} + +inline hsFixed hsWide::AsFixed() const +{ + hsWide tmp = *this; + + return tmp.RoundRight(16)->AsLong(); +} + +inline hsFract hsWide::AsFract() const +{ + hsWide tmp = *this; + + return tmp.RoundRight(30)->AsLong(); +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWindowHndl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWindowHndl.h new file mode 100644 index 00000000..f95c5edf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWindowHndl.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef HSWINDOWHNDL_inc +#define HSWINDOWHNDL_inc + +#include "HeadSpin.h" + +// decide what hsWindowHndl is +#if HS_BUILD_FOR_MAC +#include +#ifdef HS_MAC_CARBON +typedef WindowPtr hsWindowHndl; +#else +typedef GrafPtr hsWindowHndl; +#endif +#endif +#if HS_BUILD_FOR_WIN32 +typedef struct HWND__ * hsWindowHndl; +#elif HS_BUILD_FOR_PS2 || HS_BUILD_FOR_UNIX +typedef int* hsWindowHndl; +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWindows.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWindows.h new file mode 100644 index 00000000..72ba374a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/hsWindows.h @@ -0,0 +1,36 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" + +#if HS_BUILD_FOR_WIN32 + +#ifndef __AFX_H__ // MFC apps won't let us include windows from here. =( +#define _WIN32_WINNT 0x0400 +#include +#include +#endif // __AFX_H__ + +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/notes.txt b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/notes.txt new file mode 100644 index 00000000..e136a858 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/notes.txt @@ -0,0 +1,3 @@ +-Removed code for matrices, planes, xformTriangles, and lights from hsGeometry3. + +Would like something generic here for consistent error handling. m&p \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/pcSmallRect.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/pcSmallRect.cpp new file mode 100644 index 00000000..523cf411 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/pcSmallRect.cpp @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// pcSmallRect - A tiny Int16-based 2D rectangle class // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "pcSmallRect.h" +#include "hsStream.h" + +void pcSmallRect::Read( hsStream *s ) +{ + s->ReadSwap( &fX ); + s->ReadSwap( &fY ); + s->ReadSwap( &fWidth ); + s->ReadSwap( &fHeight ); +} + +void pcSmallRect::Write( hsStream *s ) +{ + s->WriteSwap( fX ); + s->WriteSwap( fY ); + s->WriteSwap( fWidth ); + s->WriteSwap( fHeight ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/pcSmallRect.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/pcSmallRect.h new file mode 100644 index 00000000..3fa72b5f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/pcSmallRect.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// pcSmallRect - A tiny Int16-based 2D rectangle class // +// // +/////////////////////////////////////////////////////////////////////////////// + +// Note: I'm introducing the concept here that new-style coreLib files/classes +// are named with the "pc" prefix, just as featureLib uses "pf" and nucleusLib +// uses "pn". I have no clue if this is kosher or good, but I'm doing it anyway. +// Feel free to change if desired + +#ifndef _pcSmallRect_h +#define _pcSmallRect_h + +#include "hsTypes.h" + +class hsStream; +class pcSmallRect +{ + public: + Int16 fX, fY, fWidth, fHeight; + + pcSmallRect() { Empty(); } + pcSmallRect( Int16 x, Int16 y, Int16 w, Int16 h ) { Set( x, y, w, h ); } + + void Set( Int16 x, Int16 y, Int16 w, Int16 h ) { fX = x; fY = y; fWidth = w; fHeight = h; } + void Empty( void ) { fX = fY = fWidth = fHeight = 0; } + + Int16 GetRight( void ) const { return fX + fWidth; } + Int16 GetBottom( void ) const { return fY + fHeight; } + + void Read( hsStream *s ); + void Write( hsStream *s ); + + hsBool Contains( Int16 x, Int16 y ) { if( x >= fX && x <= fX + fWidth && y >= fY && y <= fY + fHeight ) return true; return false; } +}; + + +#endif // _pcSmallRect_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plGeneric.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plGeneric.cpp new file mode 100644 index 00000000..99295981 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plGeneric.cpp @@ -0,0 +1,187 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsUtils.h" +#include "plGeneric.h" + +plGeneric::plGeneric(): fType(kNull), fBoolVal(false), fIntVal(0), fFloatVal(0.0) {} + +plGeneric::plGeneric(const bool& val): fType(kBool), fBoolVal(val), fIntVal(0), fFloatVal(0.0) {} + +plGeneric::plGeneric(const int& val): fType(kInt), fBoolVal(false), fIntVal(val), fFloatVal(0.0) {} + +plGeneric::plGeneric(const double& val): fType(kFloat), fBoolVal(false), fIntVal(0), fFloatVal(val) {} + +plGeneric::plGeneric(const char* val): fType(kString), fBoolVal(false), fIntVal(0), fFloatVal(0.0) +{ + wchar_t* temp = hsStringToWString(val); + fStringVal = temp; + delete [] temp; +} + +plGeneric::plGeneric(const std::string& val): fType(kString), fBoolVal(false), fIntVal(0), fFloatVal(0.0) +{ + wchar_t* temp = hsStringToWString(val.c_str()); + fStringVal = temp; + delete [] temp; +} + +plGeneric::plGeneric(const wchar_t* val): fType(kString), fBoolVal(false), fIntVal(0), fFloatVal(0.0), +fStringVal(val) {} + +plGeneric::plGeneric(const std::wstring& val): fType(kString), fBoolVal(false), fIntVal(0), fFloatVal(0.0), +fStringVal(val) {} + +void plGeneric::IReset() +{ + fType = kNull; + fBoolVal = false; + fIntVal = 0; + fFloatVal = 0.0; + fStringVal = L""; +} + +plGeneric& plGeneric::operator=(const bool& val) +{ + IReset(); + fType = kBool; + fBoolVal = val; + return *this; +} + +plGeneric& plGeneric::operator=(const int& val) +{ + IReset(); + fType = kInt; + fIntVal = val; + return *this; +} + +plGeneric& plGeneric::operator=(const double& val) +{ + IReset(); + fType = kFloat; + fFloatVal = val; + return *this; +} + +plGeneric& plGeneric::operator=(const char* val) +{ + IReset(); + fType = kString; + wchar_t* temp = hsStringToWString(val); + fStringVal = temp; + delete [] temp; + return *this; +} + +plGeneric& plGeneric::operator=(const std::string& val) +{ + IReset(); + fType = kString; + wchar_t* temp = hsStringToWString(val.c_str()); + fStringVal = temp; + delete [] temp; + return *this; +} + +plGeneric& plGeneric::operator=(const wchar_t* val) +{ + IReset(); + fType = kString; + fStringVal = val; + return *this; +} + +plGeneric& plGeneric::operator=(const std::wstring& val) +{ + IReset(); + fType = kString; + fStringVal = val; + return *this; +} + +int plGeneric::Write(hsStream* stream) +{ + stream->WriteByte((UInt8)fType); + + switch (fType) + { + case kNull: + break; // nothing to write + + case kBool: + stream->WriteBool(fBoolVal); + break; + + case kInt: + stream->WriteSwap(fIntVal); + break; + + case kFloat: + stream->WriteSwap(fFloatVal); + break; + + case kString: + stream->WriteSafeWString(fStringVal.c_str()); + break; + } + return stream->GetPosition(); +} + +int plGeneric::Read(hsStream* stream) +{ + IReset(); + fType = (GenericType)stream->ReadByte(); + switch (fType) + { + case kNull: + break; // nothing to read + + case kBool: + fBoolVal = (stream->ReadBool() != 0); + break; + + case kInt: + stream->ReadSwap(&fIntVal); + break; + + case kFloat: + stream->ReadSwap(&fFloatVal); + break; + + case kString: + { + wchar_t* temp = stream->ReadSafeWString(); + if (temp) + { + fStringVal = temp; + delete [] temp; + } + } + break; + } + return stream->GetPosition(); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plGeneric.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plGeneric.h new file mode 100644 index 00000000..095084e0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plGeneric.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __PLGENERIC_H__ +#define __PLGENERIC_H__ + +#include "hsStream.h" +#include "hsStlUtils.h" + +class plGeneric +{ +public: + enum GenericType + { + kNull, + kBool, + kInt, + kFloat, + kString + }; + +private: + GenericType fType; + bool fBoolVal; + int fIntVal; + double fFloatVal; + std::wstring fStringVal; + + void IReset(); + +public: + plGeneric(); + plGeneric(const bool& val); + plGeneric(const int& val); + plGeneric(const double& val); + plGeneric(const char* val); + plGeneric(const std::string& val); + plGeneric(const wchar_t* val); + plGeneric(const std::wstring& val); + + void SetToNull() {IReset();} + plGeneric& operator=(const bool& val); + plGeneric& operator=(const int& val); + plGeneric& operator=(const double& val); + plGeneric& operator=(const char* val); + plGeneric& operator=(const std::string& val); + plGeneric& operator=(const wchar_t* val); + plGeneric& operator=(const std::wstring& val); + + // the cast functions will NOT cast from one type to another, use + // GetType() to determine the type of parameter, then cast it to that type + GenericType GetType() const {return fType;} + operator const bool() const {return fBoolVal;} + operator const int() const {return fIntVal;} + operator const double() const {return fFloatVal;} + operator const wchar_t*() const {return fStringVal.c_str();} + operator const std::wstring() const {return fStringVal;} + + int Write(hsStream* stream); + int Read(hsStream* stream); +}; + +#endif // __PLGENERIC_H__ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plLoadMask.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plLoadMask.cpp new file mode 100644 index 00000000..f5c34864 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plLoadMask.cpp @@ -0,0 +1,170 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLoadMask.h" +#include "hsStream.h" +#include "hsTemplates.h" +#include "plQuality.h" + +/////////////////////////////////////////////////////////////////// +// Global settings first. Implemented here for convenience (mine). +// They could go in pnSingletons, but they require LoadMask to link +// (and compile) anyway. Mostly, I just wanted the plQuality interface +// in its own header file so you would know to include plQuality.h +// to get plQuality::Func, rather than the less obvious plLoadMask.h. +/////////////////////////////////////////////////////////////////// +int plQuality::fQuality = 0; +int plQuality::fCapability = 0; + +void plQuality::SetQuality(int q) +{ + fQuality = q; + plLoadMask::SetGlobalQuality(q); +} + +// Set by the pipeline according to platform capabilities. +void plQuality::SetCapability(int c) +{ + fCapability = c; + plLoadMask::SetGlobalCapability(c); +} + +/////////////////////////////////////////////////////////////////// +// Now the LoadMask implementation. +/////////////////////////////////////////////////////////////////// + +const plLoadMask plLoadMask::kAlways; + +UInt8 plLoadMask::fGlobalQuality = UInt8(1); +UInt8 plLoadMask::fGlobalCapability = UInt8(0); + +void plLoadMask::Read(hsStream* s) +{ + // read as packed byte + UInt8 qc; + s->LogReadSwap(&qc,"Quality|Capabilty"); + + fQuality[0] = (qc & 0xf0) >> 4; + fQuality[1] = (qc & 0x0f); + + // Or in the bits we stripped on write, or else IsUsed() won't work. + fQuality[0] |= 0xf0; + fQuality[1] |= 0xf0; +} + +void plLoadMask::Write(hsStream* s) const +{ + // write packed into 1 byte + UInt8 qc = (fQuality[0]<<4) | (fQuality[1] & 0xf); + s->WriteSwap(qc); +} + +UInt32 plLoadMask::ValidateReps(int num, const int quals[], const int caps[]) +{ + UInt32 retVal = 0; + int i; + for( i = 1; i < num; i++ ) + { + int j; + for( j = 0; j < i; j++ ) + { + if( (quals[i] >= quals[j]) && (caps[i] >= caps[j]) ) + { + // Bogus, this would double load. + retVal |= (1 << i); + } + } + } + return retVal; +} + +UInt32 plLoadMask::ValidateMasks(int num, plLoadMask masks[]) +{ + UInt32 retVal = 0; + int i; + for( i = 0; i < num; i++ ) + { + if( !masks[i].fQuality[0] && !masks[i].fQuality[1] ) + retVal |= (1 << i); + + int j; + for( j = 0; j < i; j++ ) + { + int k; + for( k = 0; k <= kMaxCap; k++ ) + { + if( masks[i].fQuality[k] & masks[j].fQuality[k] ) + { + masks[i].fQuality[k] &= ~masks[j].fQuality[k]; + retVal |= (1 << i); + } + } + } + } + return retVal; +} + +hsBool plLoadMask::ComputeRepMasks( + int num, + const int quals[], + const int caps[], + plLoadMask masks[]) +{ + hsBool retVal = false; // Okay till proven otherwise. + + int i; + for( i = 0; i < num; i++ ) + { + int k; + for( k = 0; k <= kMaxCap; k++ ) + { + // Q starts off the bits higher than or equal to 1 << qual. + // I.e. we just turned off all lower quality bits. + UInt8 q = ~( (1 << quals[i]) - 1 ); + + // For this cap level, if we require higher caps, + // turn off our quality (i.e. we won't load at this + // cap for any quality setting. + UInt8 c = caps[i] > kMaxCap ? kMaxCap : caps[i]; + if( c > k ) + q = 0; + + // Turn off all bits already covered for this cap level + // so we never double load. + int j; + for( j = 0; j < i; j++ ) + { + q &= ~masks[j].fQuality[k]; + } + masks[i].fQuality[k] = q; + } + if( masks[i].NeverLoads() ) + retVal = true; + } + + return retVal; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plLoadMask.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plLoadMask.h new file mode 100644 index 00000000..b73b760d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plLoadMask.h @@ -0,0 +1,119 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLoadMask_inc +#define plLoadMask_inc + +#include "hsTypes.h" + +template class hsTArray; +class hsStream; + +class plLoadMask +{ +public: + enum { + kMaxCap = 1 + }; +protected: + static UInt8 fGlobalQuality; + static UInt8 fGlobalCapability; + union { + UInt8 fQuality[kMaxCap+1]; + UInt16 fMask; + }; + + static void SetGlobalQuality(int q) { fGlobalQuality = IBitToMask(q); } + static void SetGlobalCapability(int c) { if( c > kMaxCap ) c = kMaxCap; else if( c < 0 ) c = 0; fGlobalCapability = c; } + + static UInt8 IBitToMask(int b) { hsAssert(b<8, "LoadMask: bit too large for byte"); return (1 << b); } + + friend class plQuality; +public: + // Change this to a for loop on kMaxCap+1 if we ever get more caps. + plLoadMask() { fQuality[0] = fQuality[1] = 0xff; } + plLoadMask(UInt8 qLo, UInt8 qHi) { fQuality[0] = qLo; fQuality[1] = qHi; } + ~plLoadMask() {} + + hsBool DontLoad() const { return !(fQuality[fGlobalCapability] & fGlobalQuality); } + + hsBool NeverLoads() const { return !(fQuality[0] && fQuality[1]); } + + hsBool IsUsed() const { return (fQuality[0] != UInt8(-1)) || (fQuality[1] != UInt8(-1)); } + + hsBool MatchesQuality(int q) const { return (IBitToMask(q) & (fQuality[0] | fQuality[1])) != 0; } + hsBool MatchesCapability(int c) const { return fQuality[c] != 0; } + hsBool MatchesQualityAndCapability(int q, int c) const { return IBitToMask(q) & fQuality[c]; } + + hsBool MatchesCurrentQuality() const { return MatchesQuality(fGlobalQuality); } + hsBool MatchesCurrentCapability() const { return MatchesCapability(fGlobalCapability); } + hsBool MatchesCurrent() const { return !DontLoad(); } + + UInt8 GetQualityMask(int cap) const { return fQuality[cap]; } + + plLoadMask& SetMask(UInt8 lo, UInt8 hi) { fQuality[0] = lo; fQuality[1] = hi; return *this; } + plLoadMask& SetNever() { return SetMask(0,0); } + plLoadMask& SetAlways() { return SetMask(UInt8(-1), UInt8(-1)); } + + plLoadMask& operator|=(const plLoadMask& m) { fMask |= m.fMask; return *this; } + plLoadMask& operator&=(const plLoadMask& m) { fMask &= m.fMask; return *this; } + + int operator==(const plLoadMask& m) const { return fMask == m.fMask; } + int operator!=(const plLoadMask& m) const { return !(*this == m); } + + // Only useful for sorting. + int operator<(const plLoadMask& m) const { return fMask < m.fMask; } + + friend plLoadMask operator|(const plLoadMask& lhs, const plLoadMask& rhs) { plLoadMask r(lhs); r |= rhs; return r; } + friend plLoadMask operator&(const plLoadMask& lhs, const plLoadMask& rhs) { plLoadMask r(lhs); r &= rhs; return r; } + + void Read(hsStream* s); + void Write(hsStream* s) const; + + static const plLoadMask kAlways; + + + // Input lists are in order of preference, i.e. if rep[0] and rep[1] are both loadable based + // on the current settings, only rep[0] will be loaded. This implies some rules + // to avoid wasted reps (reps that would never get loaded). Basically: + // if( i < j ) then + // (quals[i] > quals[j]) || (caps[i] > caps[j]) + // + // It doesn't break anything if that condition isn't met, it just means + // the latter one will never get loaded (have a load mask of zero). + // So, just to be a pal, we'll detect that condition and return based on it. + // i.e. Return true on invalid input (something will never get loaded). + // + // You can also pre-validate your input with ValidateReps, and/or validate + // the output with ValidateMasks. The return value is a bitmask of which + // items in the list had problems, so return of zero means A-OK. + // + static hsBool ComputeRepMasks(int num, const int quals[], const int caps[], plLoadMask masks[]); + static UInt32 ValidateReps(int num, const int quals[], const int caps[]); + static UInt32 ValidateMasks(int num, plLoadMask masks[]); +}; + +#endif // plLoadMask_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plQuality.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plQuality.h new file mode 100644 index 00000000..2e9a103b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plQuality.h @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plQuality_inc +#define plQuality_inc + +class plQuality +{ +public: + enum + { + kMinimum = 0, + + kPS_1_1 = 2, + kPS_1_4 = 3, + kPS_2_Plus = 4 + }; +protected: + // These two are instanciated in plLoadMask.cpp, as well as + // function implementations. + static int fQuality; + static int fCapability; + + friend class plClient; + friend class plQualitySlider; + friend class plDXPipeline; + + // Set by the app according to user preference. + static void SetQuality(int q); + + // Set by the pipeline according to platform capabilities. + static void SetCapability(int c); + +public: + // Set by the app according to user preference. + static int GetQuality() { return fQuality; } + + // Set by the pipeline according to platform capabilities. + static int GetCapability() { return fCapability; } + +}; + + +#endif // plQuality_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plRefCnt.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plRefCnt.h new file mode 100644 index 00000000..d211f724 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plRefCnt.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plRefCnt_Defined +#define plRefCnt_Defined + +#include "hsTypes.h" + +// plRef count addes refcount abilities to any plCreatable + +class plRefCnt +{ UInt32 fRefCnt; +public: + plRefCnt() : fRefCnt(1){} + ~plRefCnt(){} + hsBool TimeToDelete() { return (fRefCnt == 1); } + void Incr() { fRefCnt++; } + void Decr() { fRefCnt--; } +}; + + +#define DEFINE_REF_COUNT plRefCnt fMyRef;\ + virtual void UnRef() { /*hsDebugCode(hsThrowIfFalse(fRefCnt >= 1);)*/if (fMyRef.TimeToDelete()) delete this; else fMyRef.Decr(); }\ + virtual void Ref() { fMyRef.Incr(); } +/* +class hsRefCnt { +private: + Int32 fRefCnt; +public: + hsRefCnt() : fRefCnt(1) {} + virtual ~hsRefCnt(); + + Int32 RefCnt() const { return fRefCnt; } + virtual void UnRef(); + virtual void Ref(); +}; + +#define hsRefCnt_SafeRef(obj) do { if (obj) (obj)->Ref(); } while (0) +#define hsRefCnt_SafeUnRef(obj) do { if (obj) (obj)->UnRef(); } while (0) + +#define hsRefCnt_SafeAssign(dst, src) \ + do { \ + hsRefCnt_SafeRef(src); \ + hsRefCnt_SafeUnRef(dst); \ + dst = src; \ + } while (0) + +*/ + +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plRenderLevel.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plRenderLevel.h new file mode 100644 index 00000000..74ec7f6e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plRenderLevel.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRenderLevel_inc +#define plRenderLevel_inc + +class plRenderLevel +{ +public: + // A renderlevel is [Major bits 32..8]|[Minor bits 7..0] + // The major render level is further broken into 3 ranges. + // Range [0x00..0xff] - Blend onto the frame buffer (before even opaque objects) + // Range [0x0100..0xffff] - Opaque and nearly opaque objects + // Range [0x010000..0xffffff] - For blending objects (typically sorted amongst each other) + // The minor bits denote a slight difference in draw order. For example, a decal wants + // to be drawn after the opaque object it is applied to, but hopefully not very long after. + // The avatar gets a render priority of kDefRendMajorLevel,kAvatarRendMinorLevel. This puts + // it in the group of normal opaque objects with no render dependencies, but with the maximum + // permitted minor level. So it will be drawn after the opaque background, and the opaque background's + // decals, but before the first thing with a render dependency on the background (e.g. plants). + + // Removed kAvatarBlendRendMinorLevel, not being used anywhere. mf + + enum { + kOpaqueMajorLevel = 0x0, + kFBMajorLevel = 0x1, + kDefRendMajorLevel = 0x2, + kBlendRendMajorLevel = 0x4, + kLateRendMajorLevel = 0x8 + + }; + enum { + kMajorShift = 28 + }; + enum { + kDefRendMinorLevel = 0x00, + kOpaqueMinorLevel = 0x0, + kMinorLevelMask = ((1 << kMajorShift) - 1), + kAvatarRendMinorLevel = kMinorLevelMask-1 + }; +public: + plRenderLevel() { Set(kDefRendMajorLevel, kDefRendMinorLevel); } + plRenderLevel(UInt32 l) : fLevel(l) {} + plRenderLevel(UInt32 major, UInt32 minor) { Set(major, minor); } + + int operator==(const plRenderLevel& l) const { return fLevel == l.fLevel; } + int operator!=(const plRenderLevel& l) const { return fLevel != l.fLevel; } + int operator>(const plRenderLevel& l) const { return fLevel > l.fLevel; } + int operator<(const plRenderLevel& l) const { return fLevel < l.fLevel; } + int operator>=(const plRenderLevel& l) const { return fLevel >= l.fLevel; } + int operator<=(const plRenderLevel& l) const { return fLevel <= l.fLevel; } + + UInt32 Level() const { return fLevel; } + + UInt32 Minor() const { return UInt32(fLevel & kMinorLevelMask); } + UInt32 Major() const { return UInt32(fLevel >> kMajorShift); } + + plRenderLevel& Set(UInt32 l) { fLevel = l; return *this; } + plRenderLevel& Set(UInt32 major, UInt32 minor) { fLevel = (UInt32(major) << kMajorShift) | UInt32(minor); return *this; } + + UInt32 fLevel; + + static plRenderLevel OpaqueRenderLevel() { return plRenderLevel(kOpaqueMajorLevel, kOpaqueMinorLevel); } +}; + +#endif // plRenderLevel_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plTweak.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plTweak.h new file mode 100644 index 00000000..28cffc65 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plTweak.h @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plTweak_inc +#define plTweak_inc + +template class plTweak +{ + public: + plTweak() {} + plTweak(const T& init) : fVal(init) {}; + + + T fVal; + + plTweak& operator=(const T& v) { fVal = v; return *this; } + + operator T () const + { + return fVal; + } + +}; + +#ifndef HS_DEBUGGING + +#define plCONST(T) const T + +#else // HS_DEBUGGING + +#define plCONST(T) static plTweak + +#endif // HS_DEBUGGING + +#define plConst(T) plCONST(T) + +#endif // plTweak_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plViewTransform.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plViewTransform.cpp new file mode 100644 index 00000000..d122c257 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plViewTransform.cpp @@ -0,0 +1,373 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsBounds.h" +#include "hsStream.h" +#include "plViewTransform.h" + +const hsScalar plViewTransform::kMinHither = 0.25f; + +plViewTransform::plViewTransform() +: fFlags(kViewPortRelative), + fWidth(0), + fHeight(0) +{ + fCameraToWorld.Reset(); + fWorldToCamera.Reset(); + + fViewPortX.Set(0,1.f,1.f); + fViewPortY.Set(0,1.f,1.f); + + fMapMin.Set(0.f, 0.f, 0.f); + fMapMax.Set(1.f, 1.f, 1.f); +} + +void plViewTransform::Reset() +{ + fFlags = kViewPortRelative; + fCameraToWorld.Reset(); + fWorldToCamera.Reset(); + + fViewPortX.Set(0,1.f,1.f); + fViewPortY.Set(0,1.f,1.f); +} + +void plViewTransform::ISetCameraToNDC() const +{ + fCameraToNDC.Reset(); + fCameraToNDC.NotIdentity(); + + if( GetOrthogonal() ) + { + hsPoint3 worldSizeInv; + + worldSizeInv.fX = hsScalarInvert( fMax.fX - fMin.fX ) * 2.f; + worldSizeInv.fY = hsScalarInvert( fMax.fY - fMin.fY ) * 2.f; + worldSizeInv.fZ = hsScalarInvert( fMax.fZ - fMin.fZ ); + + fCameraToNDC.fMap[0][0] = worldSizeInv.fX; + fCameraToNDC.fMap[0][3] = -fMin.fX * worldSizeInv.fX - hsScalar1; + + fCameraToNDC.fMap[1][1] = worldSizeInv.fY; + fCameraToNDC.fMap[1][3] = -fMin.fY * worldSizeInv.fY - hsScalar1; + + // Map Screen Z to range 0 (at hither) to 1 (at yon) + fCameraToNDC.fMap[2][2] = worldSizeInv.fZ; + fCameraToNDC.fMap[2][3] = -fMin.fZ * worldSizeInv.fZ; + } + else + { + + fCameraToNDC.fMap[0][0] = 2.f / (fMax.fX - fMin.fX); + fCameraToNDC.fMap[0][2] = (fMax.fX + fMin.fX) / (fMax.fX - fMin.fX); + + fCameraToNDC.fMap[1][1] = 2.f / (fMax.fY - fMin.fY); + fCameraToNDC.fMap[1][2] = (fMax.fY + fMin.fY) / (fMax.fY - fMin.fY); + + fCameraToNDC.fMap[2][2] = fMax.fZ / (fMax.fZ - fMin.fZ); + fCameraToNDC.fMap[2][3] = -fMax.fZ * fMin.fZ / (fMax.fZ - fMin.fZ); + + fCameraToNDC.fMap[3][2] = 1.f; + fCameraToNDC.fMap[3][3] = 0.f; + + } + ISetFlag(kCameraToNDCSet); +} + +void plViewTransform::SetViewPort(const hsPoint2& mins, const hsPoint2& maxs, hsBool relative) +{ + fViewPortX.Set(mins.fX, maxs.fX, 1.f / (maxs.fX - mins.fX)); + fViewPortY.Set(mins.fY, maxs.fY, 1.f / (maxs.fY - mins.fY)); + ISetFlag(kViewPortRelative, relative); +} + +hsScalarTriple plViewTransform::ScreenToNDC(const hsScalarTriple& scrP) const +{ + hsPoint2 vpMin, vpMax; + GetViewPort(vpMin, vpMax); + hsPoint3 ndc; + + ndc.fX = (scrP.fX - vpMin.fX) / (vpMax.fX - vpMin.fX) * 2.f - 1.f; + + ndc.fY = (vpMax.fY - scrP.fY) / (vpMax.fY - vpMin.fY) * 2.f - 1.f; + + ndc.fZ = scrP.fZ; + + return ndc; +} + +hsScalarTriple plViewTransform::NDCToScreen(const hsScalarTriple& ndc) const +{ + hsPoint2 vpMin, vpMax; + GetViewPort(vpMin, vpMax); + + hsPoint3 scrP; + scrP.fX = (ndc.fX + 1.f) * 0.5f * (vpMax.fX - vpMin.fX) + vpMin.fX; + scrP.fY = (-ndc.fY + 1.f) * 0.5f * (vpMax.fY - vpMin.fY) + vpMin.fY; + scrP.fZ = ndc.fZ; + + return scrP; +} + +hsScalarTriple plViewTransform::NDCToCamera(const hsScalarTriple& ndc) const +{ + hsPoint3 camP; + hsScalar w = ndc.fZ; + + const hsMatrix44& c2NDC = GetCameraToNDC(); + + camP.fX = (ndc.fX - c2NDC.fMap[0][2]) * w / c2NDC.fMap[0][0]; + + camP.fY = (ndc.fY - c2NDC.fMap[1][2]) * w / c2NDC.fMap[1][1]; + + camP.fZ = ndc.fZ; + + return camP; +} + +hsScalarTriple plViewTransform::CameraToNDC(const hsScalarTriple& camP) const +{ + const hsMatrix44& c2NDC = GetCameraToNDC(); + +#ifdef MF_FLIP_SPARSE + // We count on the fact that we set up CameratToNDC, so we know where the + // zeros are. Also, note that the proper "* camP.fZ"'s are missing off the + // c2NDC.fMap[i][2] terms, because they just get cancelled out by the invW. + + hsPoint3 ndc; + if( GetOrthogonal() ) + { + ndc.fX = c2NDC.fMap[0][0] * camP.fX + + c2NDC.fMap[0][2]; + + ndc.fY = c2NDC.fMap[1][1] * camP.fY + + c2NDC.fMap[1][2]; + + ndc.fZ = c2NDC.fMap[2][2] * camP.fZ + + c2NDC.fMap[2][3]; + } + else + { + hsScalar invW = 1.f / camP.fZ; + ndc.fX = c2NDC.fMap[0][0] * camP.fX * invW + + c2NDC.fMap[0][2]; + + ndc.fY = c2NDC.fMap[1][1] * camP.fY * invW + + c2NDC.fMap[1][2]; + + ndc.fZ = c2NDC.fMap[2][2] * camP.fZ + + c2NDC.fMap[2][3]; + ndc.fZ *= invW; + } +#else // MF_FLIP_SPARSE + hsPoint3 ndc = c2NDC * hsPoint3(camP); + if( !GetOrthogonal() ) + { + hsScalar invW = 1.f / camP.fZ; + ndc *= invW; + } +#endif // MF_FLIP_SPARSE + + return ndc; +} + +hsScalarTriple plViewTransform::NDCToMap(const hsScalarTriple& ndcP) const +{ + hsPoint3 map; + map.fX = fMapMin.fX + (ndcP.fX + 1.f) * 0.5f * (fMapMax.fX - fMapMin.fX); + map.fY = fMapMin.fY + (ndcP.fY + 1.f) * 0.5f * (fMapMax.fY - fMapMin.fY); + map.fZ = fMapMin.fZ + (ndcP.fZ + 1.f) * 0.5f * (fMapMax.fZ - fMapMin.fZ); + + return map; +} + +hsBool plViewTransform::SetProjection(const hsBounds3& bnd) +{ + hsPoint3 maxs; + hsPoint3 mins; + if( IGetMaxMinsFromBnd(bnd, mins, maxs) ) + { + SetView(mins, maxs); + return true; + } + return false; +} + +hsBool plViewTransform::SetProjectionWorld(const hsBounds3& wBnd) +{ + hsBounds3Ext cBnd = wBnd; + cBnd.Transform(&GetWorldToCamera()); + return SetProjection(cBnd); +} + +hsBool plViewTransform::IGetMaxMinsFromBnd(const hsBounds3& bnd, hsPoint3& mins, hsPoint3& maxs) const +{ + if( bnd.GetMaxs().fZ <= kMinHither ) + return false; + + hsPoint3 minBnd = bnd.GetMins(); + hsPoint3 maxBnd = bnd.GetMaxs(); + // If the box intersects the hither plane, we'll need to chop it + // off. + if( minBnd.fZ < kMinHither ) + { + minBnd.fZ = kMinHither; + } + mins.Set(minBnd.fX / minBnd.fZ, minBnd.fY / minBnd.fZ, minBnd.fZ); + maxs.Set(maxBnd.fX / minBnd.fZ, maxBnd.fY / minBnd.fZ, maxBnd.fZ); + + return true; +} + +hsBool plViewTransform::Intersect(const plViewTransform& view) +{ + hsPoint3 mins; + hsPoint3 maxs; + + hsBool retVal = true; + int i; + for( i = 0; i < 3; i++ ) + { + mins[i] = hsMaximum(fMin[i], view.fMin[i]); + + maxs[i] = hsMinimum(fMax[i], view.fMax[i]); + + if( mins[i] >= maxs[i] ) + { + mins[i] = maxs[i] = (mins[i] + maxs[i]) * 0.5f; + retVal = false; + } + } + SetView(mins, maxs); + return retVal; +} + +hsBool plViewTransform::Union(const plViewTransform& view) +{ + hsPoint3 mins; + hsPoint3 maxs; + + int i; + for( i = 0; i < 3; i++ ) + { + mins[i] = hsMinimum(fMin[i], view.fMin[i]); + + maxs[i] = hsMaximum(fMax[i], view.fMax[i]); + + } + SetView(mins, maxs); + return true; +} + +hsScalar plViewTransform::GetFovX() const +{ + hsScalar minAng = hsATan2(fMin.fX, 1.f); + hsScalar maxAng = hsATan2(fMax.fX, 1.f); + + return maxAng - minAng; +} + +hsScalar plViewTransform::GetFovY() const +{ + hsScalar minAng = hsATan2(fMin.fY, 1.f); + hsScalar maxAng = hsATan2(fMax.fY, 1.f); + + return maxAng - minAng; +} + +void plViewTransform::GetViewPort(hsPoint2& mins, hsPoint2& maxs) const +{ + if( GetViewPortRelative() ) + { + mins.Set(fViewPortX.fX * fWidth, fViewPortY.fX * fHeight); + maxs.Set(fViewPortX.fY * fWidth, fViewPortY.fY * fHeight); + } + else + { + mins.Set(fViewPortX.fX, fViewPortY.fX); + maxs.Set(fViewPortX.fY, fViewPortY.fY); + } +} + +void plViewTransform::GetViewPort(int& loX, int& loY, int& hiX, int& hiY) const +{ + if( GetViewPortRelative() ) + { + loX = int(fViewPortX.fX * fWidth); + loY = int(fViewPortY.fX * fHeight); + hiX = int(fViewPortX.fY * fHeight); + hiY = int(fViewPortY.fY * fWidth); + } + else + { + loX = int(fViewPortX.fX); + loY = int(fViewPortY.fX); + hiX = int(fViewPortX.fY); + hiY = int(fViewPortY.fY); + } +} + +void plViewTransform::Read(hsStream* s) +{ + fFlags = s->ReadSwap32(); + fFlags &= ~kSetMask; + + fCameraToWorld.Read(s); + fWorldToCamera.Read(s); + + fMin.Read(s); + fMax.Read(s); + + fWidth = s->ReadSwap16(); + fHeight = s->ReadSwap16(); + + fViewPortX.Read(s); + fViewPortY.Read(s); + + fMapMin.Read(s); + fMapMin.Read(s); +} + +void plViewTransform::Write(hsStream* s) +{ + s->WriteSwap32(fFlags & ~kSetMask); + + fCameraToWorld.Write(s); + fWorldToCamera.Write(s); + + fMin.Write(s); + fMax.Write(s); + + s->WriteSwap16(fWidth); + s->WriteSwap16(fHeight); + + fViewPortX.Write(s); + fViewPortY.Write(s); + + fMapMin.Write(s); + fMapMin.Write(s); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plViewTransform.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plViewTransform.h new file mode 100644 index 00000000..3e3ff156 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLib/plViewTransform.h @@ -0,0 +1,326 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plViewTransform_inc +#define plViewTransform_inc + +#include "hsMatrix44.h" +#include "hsGeometry3.h" +#include "hsPoint2.h" + +class hsBounds3; +class hsStream; + +// There's a lot here, but there's a lot one might want to do with view transforms. +// It's easiest to grab the structure thinking of it in terms of the different +// spaces you might want a point in. The ones supported here are: +// Screen - this is actual pixel values +// NDC - Normalized Device Coordinates, these are post W divide, so the +// valid range is x = [-1..1], y = [-1..1], z = [0..1] +// Camera - relative to the camera, with (0,0,-1) directly in front of the camera, +// and (0, 1, 0) directly above the camera. +// World - Universal world space. +// Map - arbitrary mapping of NDC. Like from [(-1,-1,0)..(1,1,1)] => [(0,0,0)..(1,1,1)] (default). +// Note that there is no object space here. There could be, but I wanted something more constant, more +// world independent, so the ViewTransform remains constant unless the view changes. Whatever. +// +// So we're broken into functional sections: +// 1) Queries on the state of this view transform, properties, matrix values, whatever. Note that you +// generally shouldn't be reading a value (e.g. matrix) out of the ViewTransform, but let the +// ViewTransform perform the operation you would with the matrix. +// 2) Setting state, properties, matrix values, whatever. There's a couple of really bizarre variants +// (like the union and intersection of view frustums). Full support is available for perspective +// or orthogonal views. An additional capability (not necessary) is offset transforms, useful +// for rendering textures. If you don't what good they are, they probably aren't any good to you. +// 3) Conversions of points from one space to another. You may notice that there are a whole lot of them. +// There is a conversion from each of the spaces above to each of the other spaces. That's 12 +// transformations right there. But Points and Vectors actually transform differently, so there +// are different versions for those. Where they could be treated the same, there is an hsScalarTriple +// version that does the actual work, and then casting versions to come and go from the right type. +// 4) Read and write (note these are NOT virtual). +// +// More Notes: +// This class has no virtual functions. +// You must set the width and height for Screen queries to work (duh!). +// ViewPort defaults to cover the entire width and height. Viewport only affects mapping, not clipping +// (i.e. reducing the viewport width will still render the same stuff, just skinnier). +// The actual data here is very small, this is mostly a collection of functions, so where possible, +// just keep one of these to pass around, (e.g. rather than keeping track of FOV etc and passing +// those around). +// +class plViewTransform +{ +public: + plViewTransform(); + ~plViewTransform() {} + + void Reset(); // resets to default state + + // Queries + hsBool GetOrthogonal() const { return IHasFlag(kOrthogonal); } + hsBool GetPerspective() const { return !GetOrthogonal(); } + hsBool GetViewPortRelative() const { return IHasFlag(kViewPortRelative); } + + // Next, all our matrices. + const hsMatrix44& GetCameraToWorld() const { return fCameraToWorld; } + const hsMatrix44& GetWorldToCamera() const { return fWorldToCamera; } + const hsMatrix44& GetCameraToNDC() const { return ICheckCameraToNDC(); } + const hsMatrix44& GetWorldToNDC() const { return ICheckWorldToNDC(); } + + hsPoint3 GetPosition() const { return GetCameraToWorld().GetTranslate(); } + hsVector3 GetDirection() const { return *((hsVector3 *)&GetWorldToCamera().fMap[2]); } + hsVector3 GetUp() const { return *((hsVector3*)&GetWorldToCamera().fMap[1]); } + hsVector3 GetAcross() const { return *((hsVector3*)&GetWorldToCamera().fMap[0]); } + + UInt16 GetScreenWidth() const { return fWidth; } + UInt16 GetScreenHeight() const { return fHeight; } + + void GetViewPort(hsPoint2& mins, hsPoint2& maxs) const; + void GetViewPort(int& loX, int& loY, int& hiX, int& hiY) const; + int GetViewPortWidth() const { return GetViewPortRight() - GetViewPortLeft(); } + int GetViewPortHeight() const { return GetViewPortBottom() - GetViewPortTop(); } + int GetViewPortLeft() const { return int(GetViewPortLoX()); } + int GetViewPortTop() const { return int(GetViewPortLoY()); } + int GetViewPortRight() const { return int(GetViewPortHiX()); } + int GetViewPortBottom() const { return int(GetViewPortHiY()); } + float GetViewPortLoX() const { return GetViewPortRelative() ? fViewPortX.fX * fWidth : fViewPortX.fX; } + float GetViewPortLoY() const { return GetViewPortRelative() ? fViewPortY.fX * fHeight : fViewPortY.fX; } + float GetViewPortHiX() const { return GetViewPortRelative() ? fViewPortX.fY * fWidth : fViewPortX.fY; } + float GetViewPortHiY() const { return GetViewPortRelative() ? fViewPortY.fY * fHeight : fViewPortY.fY; } + + hsPoint3 GetMapMin() const { return fMapMin; } + hsPoint3 GetMapMax() const { return fMapMax; } + void GetMapping(hsPoint3& mapMin, hsPoint3& mapMax) const { mapMin = fMapMin; mapMax = fMapMax; } + + hsScalar GetFovX() const; + hsScalar GetFovY() const; + hsScalar GetFovXDeg() const { return hsScalarRadToDeg(GetFovX()); } + hsScalar GetFovYDeg() const { return hsScalarRadToDeg(GetFovY()); } + hsScalar GetOrthoWidth() const { return fMax.fX - fMin.fX; } + hsScalar GetOrthoHeight() const { return fMax.fY - fMin.fY; } + hsScalar GetHither() const { return fMin.fZ; } + hsScalar GetYon() const { return fMax.fZ; } + void GetDepth(hsScalar& hither, hsScalar& yon) const { hither = GetHither(); yon = GetYon(); } + + // Setup. + // First, our world to camera and back again. + void SetCameraTransform(const hsMatrix44& w2c, const hsMatrix44& c2w) { fWorldToCamera = w2c; fCameraToWorld = c2w; ISetFlag(kWorldToNDCSet, false); } + + // Next, what kind of projection. + void SetOrthogonal(hsBool on) { ISetFlag(kOrthogonal, on); InvalidateTransforms(); } + void SetPerspective(hsBool on) { SetOrthogonal(!on); } + + // Next, setting the scree/window/rendertarget size + void SetWidth(UInt16 w) { fWidth = w; } + void SetHeight(UInt16 h) { fHeight = h; } + void SetScreenSize(UInt16 w, UInt16 h) { SetWidth(w); SetHeight(h); } + + // Next, setting the viewport. You only need to do this if you want to use the screen functions above. + // If you're passing in and getting out normalized device coordinates, skip this. If you don't set viewport, + // Defaults to 0,0,width,height (i.e. the whole screen). + void SetViewPort(const hsPoint2& mins, const hsPoint2& maxs, hsBool relative=true); + void SetViewPort(float loX, float loY, float hiX, float hiY, hsBool relative=true) { SetViewPort(hsPoint2().Set(loX, loY), hsPoint2().Set(hiX, hiY), relative); } + void SetViewPort(UInt16 left, UInt16 top, UInt16 right, UInt16 bottom) { SetViewPort(hsScalar(left), hsScalar(top), hsScalar(right), hsScalar(bottom), false); } + + void SetMapping(const hsPoint3& mins, const hsPoint3& maxs) { SetMapMin(mins); SetMapMax(maxs); } + void SetMapMin(const hsPoint3& mins) { fMapMin = mins; } + void SetMapMax(const hsPoint3& maxs) { fMapMax = maxs; } + + // Next, variants on setting up our projection matrix. + // Depth is pretty uniform. + void SetDepth(hsScalar hither, hsScalar yon) { fMin.fZ = hither; fMax.fZ = yon; InvalidateTransforms(); } + void SetDepth(const hsPoint2& d) { SetDepth(d.fX, d.fY); } + void SetHither(hsScalar hither) { fMin.fZ = hither; InvalidateTransforms(); } + void SetYon(hsScalar yon) { fMax.fZ = yon; InvalidateTransforms(); } + + // Garden variety symmetric fov uses either of this first batch. Unless you're doing some funky projection, you don't even + // need to look through the rest. + // Degrees - all are full angles, < 180 degrees + void SetFovDeg(const hsPoint2& deg) { SetFovDeg(deg.fX, deg.fY); } + void SetFovDeg(hsScalar degX, hsScalar degY) { SetFovXDeg(degX); SetFovYDeg(degY); } + void SetFovXDeg(hsScalar deg) { SetFovX(hsScalarDegToRad(deg)); } + void SetFovYDeg(hsScalar deg) { SetFovY(hsScalarDegToRad(deg)); } + + // Radians - all are full angles, < PI + void SetFov(const hsPoint2& rad) { SetFov(rad.fX, rad.fY); } + void SetFov(hsScalar radX, hsScalar radY) { SetFovX(radX); SetFovY(radY); } + void SetFovX(hsScalar rad) { SetHalfWidth(hsTan(rad * 0.5f)); } + void SetFovY(hsScalar rad) { SetHalfHeight(hsTan(rad * 0.5f)); } + + // For orthogonal projection, don't call SetWidth(hsTan(fovRads)), because hsTan(f)/2 != hsTan(f/2) + // For non-centered, call SetWidths/Heights() directly. + void SetWidth(hsScalar w) { SetHalfWidth(w * 0.5f); } + void SetHeight(hsScalar h) { SetHalfHeight(h * 0.5f); } + + // The rest do no interpretation, just stuff the values passed in. + void SetHalfWidth(hsScalar hw) { SetWidths(-hw, hw); } + void SetHalfHeight(hsScalar hh) { SetHeights(-hh, hh); } + void SetWidths(hsScalar minW, hsScalar maxW) { fMin.fX = minW; fMax.fX = maxW; InvalidateTransforms(); } + void SetHeights(hsScalar minH, hsScalar maxH) { fMin.fY = minH; fMax.fY = maxH; InvalidateTransforms(); } + void SetWidths(const hsPoint2& w) { SetWidths(w.fX, w.fY); } + void SetHeights(const hsPoint2& h) { SetHeights(h.fX, h.fY); } + void SetView(const hsPoint3& mins, const hsPoint3& maxs) { fMax = maxs; fMin = mins; InvalidateTransforms(); } + + // Take a CAMERA SPACE bounding box and sets up the projection to just surround it. + // Note this doesn't swivel the camera around to see the box, it offsets the projection. + // Return false if there isn't a projection that will capture any of the bnd. This + // can be from the bnd being behind the camera. + hsBool SetProjection(const hsBounds3& cBnd); + hsBool SetProjectionWorld(const hsBounds3& wBnd); + + // This lets you create insane projection matrices. Note that it won't change the answer on anything like + // GetFov(). + void PreMultCameraToNDC(const hsMatrix44& m) { fCameraToNDC = m * GetCameraToNDC(); } + void PostMultCameraToNDC(const hsMatrix44& m) { fCameraToNDC = GetCameraToNDC() * m; } + void Recalc() { InvalidateTransforms(); } + + // These do the obvious, constructing a single view that encompasses either the intersection or union + // of what the two views will see. The boolean is performed in axis aligned camera space, which lines + // up nicely with screen space. Note that this only makes sense for two ViewTransforms with identical + // CameraToWorld's (which isn't checked). + hsBool Intersect(const plViewTransform& view); + hsBool Union(const plViewTransform& view); + + // Convenience to move from one space to another. + // Screen means pixels - Default is mapping NDC -> [0..1]. Z value of pixel is NDC Z. + // NDC is the ([-1..1],[-1..1],[0..1]) Normalized device coordinates. + // Camera is camera space. + // World is world space. + // Past that, you're on your own. + hsScalarTriple ScreenToNDC(const hsScalarTriple& scrP) const; + hsScalarTriple ScreenToCamera(const hsScalarTriple& scrP) const { return NDCToCamera(ScreenToNDC(scrP)); } + + hsPoint3 ScreenToNDC(const hsPoint3& scrP) const { return hsPoint3(ScreenToNDC(hsScalarTriple(scrP))); } + hsPoint3 ScreenToCamera(const hsPoint3& scrP) const { return hsPoint3(ScreenToCamera(hsScalarTriple(scrP))); } + hsPoint3 ScreenToWorld(const hsPoint3& scrP) const { return CameraToWorld(ScreenToCamera(scrP)); } + + hsVector3 ScreenToNDC(const hsVector3& scrV) const { return hsVector3(ScreenToNDC(hsScalarTriple(scrV))); } + hsVector3 ScreenToCamera(const hsVector3& scrV) const { return hsVector3(ScreenToCamera(hsScalarTriple(scrV))); } + hsVector3 ScreenToWorld(const hsVector3& scrV) const { return CameraToWorld(ScreenToCamera(scrV)); } + + hsScalarTriple NDCToScreen(const hsScalarTriple& ndc) const; + hsScalarTriple NDCToCamera(const hsScalarTriple& ndc) const; + + hsPoint3 NDCToScreen(const hsPoint3& ndc) const { return hsPoint3(NDCToScreen(hsScalarTriple(ndc))); } + hsPoint3 NDCToCamera(const hsPoint3& ndc) const { return hsPoint3(NDCToCamera(hsScalarTriple(ndc))); } + hsPoint3 NDCToWorld(const hsPoint3& ndc) const { return CameraToWorld(NDCToCamera(ndc)); } + + hsVector3 NDCToScreen(const hsVector3& ndc) const { return hsVector3(NDCToScreen(hsScalarTriple(ndc))); } + hsVector3 NDCToCamera(const hsVector3& ndc) const { return hsVector3(NDCToCamera(hsScalarTriple(ndc))); } + hsVector3 NDCToWorld(const hsVector3& ndc) const { return CameraToWorld(NDCToCamera(ndc)); } + + hsScalarTriple CameraToScreen(const hsScalarTriple& camP) const { return NDCToScreen(CameraToNDC(camP)); } + hsScalarTriple CameraToNDC(const hsScalarTriple& camP) const; + + hsPoint3 CameraToScreen(const hsPoint3& camP) const { return hsPoint3(CameraToScreen(hsScalarTriple(camP))); } + hsPoint3 CameraToNDC(const hsPoint3& camP) const { return hsPoint3(CameraToNDC(hsScalarTriple(camP))); } + hsPoint3 CameraToWorld(const hsPoint3& camP) const { return GetCameraToWorld() * camP; } + + hsVector3 CameraToScreen(const hsVector3& camP) const { return hsVector3(CameraToScreen(hsScalarTriple(camP))); } + hsVector3 CameraToNDC(const hsVector3& camP) const { return hsVector3(CameraToNDC(hsScalarTriple(camP))); } + hsVector3 CameraToWorld(const hsVector3& camV) const { return GetCameraToWorld() * camV; } + + hsPoint3 WorldToScreen(const hsPoint3& worldP) const { return (hsPoint3)CameraToScreen(WorldToCamera(worldP)); } + hsPoint3 WorldToNDC(const hsPoint3& worldP) const { return CameraToNDC(WorldToCamera(worldP)); } + hsPoint3 WorldToCamera(const hsPoint3& worldP) const { return GetWorldToCamera() * worldP; } + + hsVector3 WorldToScreen(const hsVector3& worldV) const { return (hsVector3)CameraToScreen(WorldToCamera(worldV)); } + hsVector3 WorldToNDC(const hsVector3& worldP) const { return CameraToNDC(WorldToCamera(worldP)); } + hsVector3 WorldToCamera(const hsVector3& worldV) const { return GetWorldToCamera() * worldV; } + + hsScalarTriple NDCToMap(const hsScalarTriple& ndcP) const; + hsScalarTriple CameraToMap(const hsScalarTriple& camP) const { return NDCToMap(CameraToNDC(camP)); } + + hsPoint3 NDCToMap(const hsPoint3& ndcP) const { return hsPoint3(NDCToMap(hsScalarTriple(ndcP))); } + hsPoint3 CameraToMap(const hsPoint3& camP) const { return hsPoint3(CameraToMap(hsScalarTriple(camP))); } + hsPoint3 WorldToMap(const hsPoint3& worldP) const { return CameraToMap(WorldToCamera(worldP)); } + + hsVector3 NDCToMap(const hsVector3& ndcP) const { return hsVector3(NDCToMap(hsScalarTriple(ndcP))); } + hsVector3 CameraToMap(const hsVector3& camP) const { return hsVector3(CameraToMap(hsScalarTriple(camP))); } + hsVector3 WorldToMap(const hsVector3& worldP) const { return CameraToMap(WorldToCamera(worldP)); } + + void Read(hsStream* s); + void Write(hsStream* s); + +protected: + enum + { + kNone = 0x0, + kOrthogonal = 0x1, + kSymmetric = 0x2, + kCameraToNDCSet = 0x4, + kWorldToNDCSet = 0x8, + kSetMask = kCameraToNDCSet | kWorldToNDCSet, + kViewPortRelative = 0x10 + }; + + mutable UInt32 fFlags; + + hsMatrix44 fCameraToWorld; + hsMatrix44 fWorldToCamera; + + hsPoint3 fMin; // minTanX/X, minTanY/Y, hither + hsPoint3 fMax; // maxTanX/X, maxTanY/Y, yon + + // Screen (or rendertarget) dimensions in pixels. + UInt16 fWidth; + UInt16 fHeight; + + // Viewport can be stored as fraction of screen size, so the view transform's viewport + // can be set up independent of the size of the window it's applied to. + hsPoint3 fViewPortX; // min, max, 1 / (max-min) + hsPoint3 fViewPortY; // min, max, 1 / (max-min) + + // For arbitrary mapping (unconfined to pixel coords or NDC), just set what you want + // to map to. + hsPoint3 fMapMin; + hsPoint3 fMapMax; + + // Some mutables. These are just the calculated from the above (e.g. fov, depth, perspective, etc). + mutable hsMatrix44 fCameraToNDC; + mutable hsMatrix44 fWorldToNDC; + + // Have to set a limit here on the smallest the hither plane can be. + static const hsScalar kMinHither; + + void ISetCameraToNDC() const; + hsBool ICameraToNDCSet() const { return IHasFlag(kCameraToNDCSet); } + const hsMatrix44& ICheckCameraToNDC() const { if( !ICameraToNDCSet() ) ISetCameraToNDC(); return fCameraToNDC; } + + void ISetWorldToNDC() const { fWorldToNDC = GetCameraToNDC() * fWorldToCamera; ISetFlag(kWorldToNDCSet); } + hsBool IWorldToNDCSet() const { return IHasFlag(kWorldToNDCSet); } + const hsMatrix44& ICheckWorldToNDC() const { if( !IWorldToNDCSet() ) ISetWorldToNDC(); return fWorldToNDC; } + + hsBool IGetMaxMinsFromBnd(const hsBounds3& bnd, hsPoint3& mins, hsPoint3& maxs) const; + + void InvalidateTransforms() { ISetFlag(kCameraToNDCSet|kWorldToNDCSet, false); } + + // Flags - generic + hsBool IHasFlag(UInt32 f) const { return 0 != (fFlags & f); } + void ISetFlag(UInt32 f, hsBool on=true) const { if(on) fFlags |= f; else fFlags &= ~f; } + +}; + +#endif // plViewTransform_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/Intern.h new file mode 100644 index 00000000..a36ee50a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/Intern.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/CoreLibExe/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_CORELIBEXE_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/CoreLibExe/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_CORELIBEXE_INTERN_H + + +namespace ExeMalloc { +/***************************************************************************** +* +* hsExeMalloc +* +***/ + +void MemSetLeakChecking (bool on); + + + +} using namespace ExeMalloc; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/Pch.h new file mode 100644 index 00000000..965832cf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/Pch.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/CoreLibExe/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_CORELIBEXE_PCH_H +#error "Header $/Plasma20/Sources/Plasma/CoreLibExe/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_CORELIBEXE_PCH_H + +#pragma warning(push, 3) + +#include "../CoreLib/HeadSpin.h" +#include "../CoreLib/hsConfig.h" +#include "../CoreLib/hsTypes.h" +#include "../CoreLib/hsWindows.h" +#include "../CoreLib/hsMalloc.h" +#include "../CoreLib/hsCritSect.h" +#include "../CoreLib/hsUtils.h" +#include "../CoreLib/hsWindows.h" + +#pragma warning(pop) + +#include "Intern.h" + +#include + +#ifdef HS_BUILD_FOR_WIN32 +#include +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/hsExeError.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/hsExeError.cpp new file mode 100644 index 00000000..c2865177 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/hsExeError.cpp @@ -0,0 +1,259 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/CoreLibExe/hsExeError.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + +static bool s_skipBreak; +static CCritSect * s_critsect; + +// User options +static bool s_options[kNumErrorOptions]; + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +AUTO_INIT_FUNC(hsExeErrorInit) { + // The critical section has to be initialized + // before program startup and never freed + static byte rawMemory[sizeof CCritSect]; + s_critsect = new(rawMemory) CCritSect; +} + +//============================================================================ +static void DoAssert (int line, const char file[], const char msg[]) { + + ref(line); + ref(file); + + ErrorMinimizeAppWindow(); + + #ifdef HS_BUILD_FOR_WIN32 + + if (!s_options[kErrOptNonGuiAsserts]) { + #ifdef HS_DEBUGGING + bool wasLeakChecking = ErrorSetOption(kErrOptDisableMemLeakChecking, true); + if (s_critsect) + s_critsect->Enter(); + if (_CrtDbgReport(_CRT_ASSERT, file, line, NULL, msg)) + DebugBreak(); + if (s_critsect) + s_critsect->Leave(); + (void) ErrorSetOption(kErrOptDisableMemLeakChecking, wasLeakChecking); + #else + DebugBreakIfDebuggerPresent(); + #endif + } + else { + DebugMsg(msg); + DebugBreakIfDebuggerPresent(); + } + + #else // !HS_BUILD_FOR_WIN32 + + DebugMsg(msg); + DebugBreakIfDebuggerPresent(); + + #endif +} + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +#pragma auto_inline(off) +void __cdecl ErrorFatal (int line, const char file[], const char fmt[], ...) { + char buffer[256]; + va_list args; + va_start(args, fmt); + hsVsnprintf(buffer, arrsize(buffer), fmt, args); + va_end(args); + + ErrorSetOption(kErrOptDisableMemLeakChecking, true); + DoAssert(line, file, buffer); + + // Ensure thread crashes immediately by writing to invalid memory + * (int *) 0 = 0; +} +#pragma auto_inline() + +//============================================================================ +#pragma auto_inline(off) +void __cdecl ErrorAssert (int line, const char file[], const char fmt[], ...) { + char buffer[256]; + va_list args; + va_start(args, fmt); + hsVsnprintf(buffer, arrsize(buffer), fmt, args); + va_end(args); + + DoAssert(line, file, buffer); +} +#pragma auto_inline() + +//============================================================================ +void ErrorMinimizeAppWindow () { + #ifdef HS_BUILD_FOR_WIN32 + // If the application's topmost window is a fullscreen + // popup window, minimize it before displaying an error + HWND appWindow = GetActiveWindow(); + if ( ((GetWindowLong(appWindow, GWL_STYLE) & WS_POPUP) != 0) && + ((GetWindowLong(appWindow, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0) ) + SetWindowPos( + appWindow, + HWND_NOTOPMOST, + 0, 0, // position + 0, 0, // size + SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE + ); + #endif +} + +//============================================================================ +bool ErrorSetOption (EErrorOption option, bool on) { + SWAP(s_options[option], on); + if (option == kErrOptDisableMemLeakChecking) + MemSetLeakChecking(on); // reverse logic, so use prev value + return on; +} + +//============================================================================ +bool ErrorGetOption (EErrorOption option) { + return s_options[option]; +} + +//============================================================================ +bool DebugIsDebuggerPresent () { + bool status = false; + +#ifdef HS_BUILD_FOR_WIN32 + status = 0 != IsDebuggerPresent(); +#endif + + return status; +} + +//============================================================================ +void DebugBreakIfDebuggerPresent () { +#ifdef HS_DEBUGGING + // try breakpoint? + if (s_skipBreak) + return; + + __try { + // break into debugger + #ifdef _M_IX86 + __asm int 3 + #else + __debugbreak(); + #endif + } + __except(EXCEPTION_EXECUTE_HANDLER) { + // debugger not present, stop attempting breaks + s_skipBreak = true; + } +#endif +} + +//============================================================================ +void DebugMsgV (const char fmt[], va_list args) { +#ifdef HS_DEBUGGING + + // Don't bother with debug output if no debugger attached + if (!DebugIsDebuggerPresent()) + return; + + char msg[512]; + hsVsnprintf(msg, arrsize(msg), fmt, args); + + // MsDev trashes strings with colons in them; replace with period instead + for (char * ptr = msg; *ptr; ++ptr) { + if (*ptr == ':') + *ptr = '.'; + } + + // Too many threads printing to OutputDebugString causes bizarre + // results in developer studio; use critsect to serialize writes + if (s_critsect) + s_critsect->Enter(); + + #ifdef HS_BUILD_FOR_WIN32 + + OutputDebugStringA(msg); + OutputDebugStringA("\n"); + + #else + + fprintf(stdout, msg); + fprintf(stdout, "\n"); + + #endif + + if (s_critsect) + s_critsect->Leave(); + +#else + + ref(fmt); + ref(args); + +#endif +} + +//============================================================================ +void __cdecl DebugMsg (const char fmt[], ...) { +#ifdef HS_DEBUGGING + + va_list args; + va_start(args, fmt); + DebugMsgV(fmt, args); + va_end(args); + +#else + + ref(fmt); + +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/hsExeMalloc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/hsExeMalloc.cpp new file mode 100644 index 00000000..30beb1fd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/CoreLibExe/hsExeMalloc.cpp @@ -0,0 +1,642 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/CoreLibExe/hsExeMalloc.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/**************************************************************************** +* +* Local constants +* +***/ + +#if defined(NO_MEM_TRACKER) || !defined(HS_FIND_MEM_LEAKS) || !defined(HS_BUILD_FOR_WIN32) || !defined(_MSC_VER) + // no mem debugging +#else +# undef MEM_DEBUG +# define MEM_DEBUG +#endif + +const unsigned kMemReallocInPlaceOnly = 1<<0; +const unsigned kMemZero = 1<<1; +const unsigned kMemIgnoreBlock = 1<<2; // don't track this allocation + +#ifndef MEM_DEBUG + +# define _malloc_dbg(s, t, f, l) malloc(s) +# define _calloc_dbg(c, s, t, f, l) calloc(c, s) +# define _realloc_dbg(p, s, t, f, l) realloc(p, s) +# define _expand_dbg(p, s, t, f, l) _expand(p, s) +# define _free_dbg(p, t) free(p) +# define _msize_dbg(p, t) _msize(p) + +# ifndef _CLIENT_BLOCK +# define _CLIENT_BLOCK 0 +# endif + +# ifndef _IGNORE_BLOCK +# define _IGNORE_BLOCK 0 +# endif + +# ifndef _CRTDBG_ALLOC_MEM_DF +# define _CRTDBG_ALLOC_MEM_DF 0 +# endif + +# define SET_CRT_DEBUG_FIELD(a) +# define CLEAR_CRT_DEBUG_FIELD(a) + +#endif // !MEM_DEBUG + + +/***************************************************************************** +* +* Private data +* +***/ + +#ifdef MEM_DEBUG + #define SET_CRT_DEBUG_FIELD(a) \ + _CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)) + #define CLEAR_CRT_DEBUG_FIELD(a) \ + _CrtSetDbgFlag(~(a) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)) + + // From dbgint.h + #define nNoMansLandSize 4 + typedef struct _CrtMemBlockHeader + { + struct _CrtMemBlockHeader * pBlockHeaderNext; + struct _CrtMemBlockHeader * pBlockHeaderPrev; + char * szFileName; + int nLine; + #ifdef _WIN64 + /* These items are reversed on Win64 to eliminate gaps in the struct + * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is + * maintained in the debug heap. + */ + int nBlockUse; + size_t nDataSize; + #else /* _WIN64 */ + size_t nDataSize; + int nBlockUse; + #endif /* _WIN64 */ + long lRequest; + unsigned char gap[nNoMansLandSize]; + /* followed by: + * unsigned char data[nDataSize]; + * unsigned char anotherGap[nNoMansLandSize]; + */ + } _CrtMemBlockHeader; + #define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1)) + #define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1) + + + enum EMemFile { + kMemErr, + kMemLeaks, + kMemUsage, + kMemAllocs, + kNumMemFiles + }; + + static char * s_memFilename[kNumMemFiles] = { + "MemErr.log", + "MemLeaks.log", + "MemUsage.log", + "MemAllocs.log", + }; + + static char * s_memDlgTitle[kNumMemFiles] = { + "Memory error", + "Memory leak", + nil, + nil, + }; + + static HANDLE s_memFile[kNumMemFiles] = { + INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE, + }; + + struct MemDumpParam { + EMemFile file; + bool showDialog; + }; + + static unsigned s_memColor; + static unsigned s_memCheckOff; + + static CCritSect * s_critsect; + +#endif // MEM_DEBUG + + + +namespace ExeMalloc { +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +#ifdef MEM_DEBUG +static void ConvertFilename ( + const char src[], + unsigned chars, + char * dst +) { + + // Because the filename field may point into a DLL that has been + // unloaded, this code validates and converts the string into a + // reasonable value + __try { + unsigned pos = 0; + for (;;) { + // If the file name is too long then assume it is bogus + if (pos >= chars) { + pos = 0; + break; + } + + // Get the next character + unsigned chr = src[pos]; + if (!chr) + break; + + // If the character isn't valid low-ASCII + // then assume that the name is bogus + if ((chr < 32) || (chr > 127)) { + pos = 0; + break; + } + + // Store character + dst[pos++] = (char) chr; + } + + // Ensure that name is terminated + dst[pos] = 0; + } + __except(EXCEPTION_EXECUTE_HANDLER) { + dst[0] = 0; + } + + // Print the address of the filename; it may be of some + // use given the load address and the map file of the DLL + if (!dst[0]) + snprintf(dst, chars, "@%p", src); +} +#endif // MEM_DEBUG + +//=========================================================================== +#ifdef MEM_DEBUG +static void OpenErrorFile (EMemFile file) { + ASSERT(INVALID_HANDLE_VALUE == s_memFile[file]); + s_memFile[file] = CreateFile( + s_memFilename[file], + GENERIC_WRITE, + FILE_SHARE_READ, + (LPSECURITY_ATTRIBUTES) NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL + ); +} +#endif // MEM_DEBUG + +//=========================================================================== +#ifdef MEM_DEBUG +static void __cdecl ReportMem (EMemFile file, bool showDialog, const char fmt[], ...) { + + if (s_memFile[file] == INVALID_HANDLE_VALUE) { + DebugBreakIfDebuggerPresent(); + OpenErrorFile(file); + ErrorMinimizeAppWindow(); + } + + char buffer[512]; + va_list args; + va_start(args, fmt); + DWORD length = hsVsnprintf(buffer, arrsize(buffer), fmt, args); + va_end(args); + + #ifdef HS_BUILD_FOR_WIN32 + + OutputDebugStringA(buffer); + + if (s_memFile[file] != INVALID_HANDLE_VALUE) + WriteFile(s_memFile[file], buffer, length, &length, NULL); + + static bool s_skip; + if (showDialog && !s_skip && !ErrorGetOption(kErrOptNonGuiAsserts)) { + s_skip = IDOK != MessageBox( + NULL, + buffer, + s_memDlgTitle[file], + MB_ICONSTOP | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL + ); + } + + #else + + fputs(buffer, stderr); + + #endif +} +#endif + +//============================================================================ +#ifdef MEM_DEBUG +static void __cdecl MemDumpCallback (void * mem, void * param) { + ref(MemDumpCallback); + + const _CrtMemBlockHeader * pHead = pHdr(mem); + MemDumpParam * dumpParam = (MemDumpParam *) param; + + char filename[MAX_PATH]; + ConvertFilename( + pHead->szFileName, + arrsize(filename), + filename + ); + + // HACK: Don't report array memory leaks since these underly the hash + // table type and may not be cleaned up until after the mem leak + // checker runs. =( + if (strstr(filename, "pnUtArray")) + return; + + ReportMem( + dumpParam->file, + dumpParam->showDialog, + "Offset %p size %u at %s:%d\r\n", + mem, + pHead->nDataSize, + filename, + pHead->nLine + ); +} +#endif // MEM_DEBUG + +//============================================================================ +#ifdef MEM_DEBUG +static void __cdecl OnExitMemDumpCallback (void * mem, size_t) { + static MemDumpParam param = { kMemLeaks, true }; + if (!ErrorGetOption(kErrOptDisableMemLeakChecking)) + MemDumpCallback(mem, ¶m); +} +#endif // MEM_DEBUG + +//=========================================================================== +#ifdef MEM_DEBUG +static void __cdecl CheckLeaksOnExit () { + ref(CheckLeaksOnExit); + if (!ErrorGetOption(kErrOptDisableMemLeakChecking)) { + MemDumpParam param; + param.file = kMemLeaks; + param.showDialog = true; + _CrtDoForAllClientObjects(MemDumpCallback, ¶m); + } +} +#endif // MEM_DEBUG + +//============================================================================ +static int __cdecl CrtAllocHook ( + int method, + void * pUserData, + size_t nSize, + int nBlockUse, + long lRequest, + const unsigned char * szFileName, + int nLine +) { + ref(method); + ref(pUserData); + ref(nSize); + ref(nBlockUse); + ref(lRequest); + ref(szFileName); + ref(nLine); + + if (nBlockUse == _NORMAL_BLOCK) { + int xx = 0; + ref(xx); + } + + return 1; +} + +//=========================================================================== +#ifdef MEM_DEBUG +AUTO_INIT_FUNC(hsExeMallocInit) { + // The critical section has to be initialized + // before program startup and never freed + static byte rawMemory[sizeof CCritSect]; + s_critsect = new(rawMemory) CCritSect; + SET_CRT_DEBUG_FIELD(_CRTDBG_LEAK_CHECK_DF); + _CrtSetAllocHook(CrtAllocHook); + _CrtSetDumpClient(OnExitMemDumpCallback); +// atexit(CheckLeaksOnExit); +} +#endif // MEM_DEBUG + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void MemSetLeakChecking (bool on) { + if (on) + SET_CRT_DEBUG_FIELD(_CRTDBG_LEAK_CHECK_DF); + else + CLEAR_CRT_DEBUG_FIELD(_CRTDBG_LEAK_CHECK_DF); +} + + +} using namespace ExeMalloc; +/**************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void MemDumpAllocReport () { +#ifdef MEM_DEBUG + MemDumpParam param; + param.file = kMemAllocs; + param.showDialog = true; + _CrtDoForAllClientObjects(MemDumpCallback, ¶m); +#endif // MEM_DEBUG +} + +//============================================================================ +void MemDumpUsageReport () { +#ifdef MEM_DEBUG +#endif // MEM_DEBUG +} + +//============================================================================ +void MemValidateNow () { +#ifdef MEM_DEBUG +#endif // MEM_DEBUG +} + +//============================================================================ +void MemSetValidation (unsigned on) { + ref(on); + +#ifdef MEM_DEBUG +#endif // MEM_DEBUG +} + +//============================================================================ +void MemPushDisableTracking () { + +#ifdef MEM_DEBUG + ++s_memCheckOff; +#endif // MEM_DEBUG +} + +//============================================================================ +void MemPopDisableTracking () { + +#ifdef MEM_DEBUG + ASSERT(s_memCheckOff); + --s_memCheckOff; +#endif // MEM_DEBUG +} + +//============================================================================ +void MemSetColor (unsigned short color) { + ref(color); + +#ifdef MEM_DEBUG + s_memColor = color & 0xFFFF; +#endif // MEM_DEBUG +} + +//=========================================================================== +void * MemAlloc (unsigned bytes, unsigned flags, const char file[], int line) { + + ref(file); + ref(line); + +#ifdef MEM_DEBUG + unsigned block; + if (flags & kMemIgnoreBlock || s_memCheckOff) + block = _IGNORE_BLOCK; + else + block = _CLIENT_BLOCK | (s_memColor << 16); +#endif // MEM_DEBUG + +#ifdef MEM_DEBUG + if (s_critsect) + s_critsect->Enter(); + if (block == _IGNORE_BLOCK) + CLEAR_CRT_DEBUG_FIELD(_CRTDBG_ALLOC_MEM_DF); +#endif + + void * ptr = (flags & kMemZero) + ? _calloc_dbg(bytes, 1, block, file, line) + : _malloc_dbg(bytes, block, file, line); + +#ifdef MEM_DEBUG + if (block == _IGNORE_BLOCK) + SET_CRT_DEBUG_FIELD(_CRTDBG_ALLOC_MEM_DF); + if (s_critsect) + s_critsect->Leave(); +#endif + + if (!ptr) + ErrorFatal(__LINE__, __FILE__, "Out of memory"); + + // In debug mode ensure that memory is initialized to some freaky value + #ifdef HS_DEBUGGING + if (! (flags & kMemZero)) + MemSet(ptr, (byte) ((unsigned_ptr)ptr >> 4), bytes); + #endif + +#ifdef _MSC_VER + // Compiler specific: + // Adding this line causes MSVC to stop assuming that memory allocation + // can fail thus producing more efficient assembler code. + __assume(ptr); +#endif + + // return the allocated buffer + return ptr; +} + +//============================================================================ +void MemFree (void * ptr, unsigned flags) { + ref(flags); + + if (!ptr) + return; + +#ifdef MEM_DEBUG + const _CrtMemBlockHeader * pHead = pHdr(ptr); + unsigned block = pHead->nBlockUse; +#endif // MEM_DEBUG + + _free_dbg(ptr, block); +} + +//=========================================================================== +void * MemRealloc (void * ptr, unsigned bytes, unsigned flags, const char file[], int line) { + ref(file); + ref(line); + + #ifdef HS_DEBUGGING + unsigned oldBytes = ptr ? MemSize(ptr) : 0; + #endif + +#ifdef MEM_DEBUG + unsigned block; + if (flags & kMemIgnoreBlock || s_memCheckOff) + block = _IGNORE_BLOCK; + else + block = _CLIENT_BLOCK | (s_memColor << 16); +#endif + + void * newPtr = nil; + +#ifdef MEM_DEBUG + if (s_critsect) + s_critsect->Enter(); + if (block == _IGNORE_BLOCK) + CLEAR_CRT_DEBUG_FIELD(_CRTDBG_ALLOC_MEM_DF); +#endif + + for (;;) { + if (flags & kMemReallocInPlaceOnly) { +#ifndef MEM_DEBUG + break; +#else + newPtr = _expand_dbg(ptr, bytes, block, file, line); + + // expand can succeed without making the block big enough -- check for this case! + if (!newPtr || _msize_dbg(newPtr, block) < bytes) + break; +#endif // MEM_DEBUG + } + else if (!bytes) { + newPtr = _malloc_dbg(0, block, file, line); + _free_dbg(ptr, block); + } + else { + newPtr = _realloc_dbg(ptr, bytes, block, file, line); + } + + if (!newPtr) + ErrorFatal(__LINE__, __FILE__, "Out of memory"); + + break; + } + +#ifdef MEM_DEBUG + if (block == _IGNORE_BLOCK) + SET_CRT_DEBUG_FIELD(_CRTDBG_ALLOC_MEM_DF); + if (s_critsect) + s_critsect->Leave(); +#endif + + /* This code doesn't work because the memory manager may have "rounded" the size + * of a previous allocation upward to keep it aligned. Therefore, the tail of + * the memory block may be initialized with garbage instead of zeroes, and the + * realloc call actually copied that memory. + if ((bytes > oldBytes) && (flags & kMemZero)) + MemZero((byte *)newPtr + oldBytes, bytes - oldBytes); + */ + ASSERT(!(flags & kMemZero)); + + // In debug mode ensure that memory is initialized to some freaky value + #ifdef HS_DEBUGGING + if ((bytes > oldBytes) && !(flags & kMemZero)) + MemSet((byte *)newPtr + oldBytes, (byte) ((unsigned_ptr) newPtr >> 4), bytes - oldBytes); + #endif + + return newPtr; +} + +//=========================================================================== +unsigned MemSize (void * ptr) { + ASSERT(ptr); + unsigned result; + +#ifdef MEM_DEBUG + const _CrtMemBlockHeader * pHead = pHdr(ptr); + unsigned block = pHead->nBlockUse; +#endif + + result = (unsigned)_msize_dbg(ptr, block); + return result; +} + + +//=========================================================================== +int MemCmp (const void * buf1, const void * buf2, unsigned bytes) { + return memcmp(buf1, buf2, bytes); +} + +//=========================================================================== +void MemCopy (void * dest, const void * source, unsigned bytes) { + memcpy(dest, source, bytes); +} + +//=========================================================================== +void MemMove (void * dest, const void * source, unsigned bytes) { + memmove(dest, source, bytes); +} + +//=========================================================================== +void MemSet (void * dest, unsigned value, unsigned bytes) { + memset(dest, value, bytes); +} + +//=========================================================================== +void MemZero (void * dest, unsigned bytes) { + memset(dest, 0, bytes); +} + +//=========================================================================== +void * MemDup (const void * ptr, unsigned bytes, unsigned flags, const char file[], int line) { + void * dst = MemAlloc(bytes, flags, file, line); + MemCopy(dst, ptr, bytes); + return dst; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/inc/pfAllCreatables.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/inc/pfAllCreatables.h new file mode 100644 index 00000000..e95313ee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/inc/pfAllCreatables.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pfAllCreatables_inc +#define pfAllCreatables_inc + + +#include "../pfCharacter/pfCharacterCreatable.h" +#include "../pfCamera/pfCameraCreatable.h" +#include "../pfAnimation/pfAnimationCreatable.h" +#include "../pfConditional/plConditionalObjectCreatable.h" +#include "../pfConsole/pfConsoleCreatable.h" +#include "../pfSurface/pfSurfaceCreatable.h" +#include "../pfMessage/pfMessageCreatable.h" +#include "../pfAudio/pfAudioCreatable.h" +#include "../pfPython/pfPythonCreatable.h" +#include "../pfGameGUIMgr/pfGameGUIMgrCreatable.h" +#include "../pfCCR/plCCRCreatable.h" +#include "../pfJournalBook/pfJournalBookCreatable.h" +#include "../pfGameMgr/pfGameMgrCreatables.h" +#include "../pfSecurePreloader/pfSecurePreloaderCreatable.h" + +#endif // pfAllCreatables_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfAnimationCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfAnimationCreatable.h new file mode 100644 index 00000000..9ca9d0d7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfAnimationCreatable.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pfAnimationCreatable_inc +#define pfAnimationCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plViewFaceModifier.h" + +REGISTER_CREATABLE( plViewFaceModifier ); + +#include "plLineFollowMod.h" + +REGISTER_CREATABLE( plLineFollowMod ); +REGISTER_CREATABLE( plRailCameraMod ); + +#include "plLightModifier.h" + +REGISTER_CREATABLE( plLightModifier ); +REGISTER_CREATABLE( plOmniModifier ); +REGISTER_CREATABLE( plSpotModifier ); +REGISTER_CREATABLE( plLtdDirModifier ); + +#include "plRandomCommandMod.h" + +REGISTER_NONCREATABLE( plRandomCommandMod ); + +#include "plFollowMod.h" + +REGISTER_CREATABLE( plFollowMod ); + +#include "plBlower.h" + +REGISTER_CREATABLE( plBlower ); + +#include "plFilterCoordInterface.h" + +REGISTER_CREATABLE( plFilterCoordInterface ); + +#include "plStereizer.h" + +REGISTER_CREATABLE( plStereizer ); + +#include "pfObjectFlocker.h" + +REGISTER_CREATABLE( pfObjectFlocker ); + +#endif // pfAnimationCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfObjectFlocker.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfObjectFlocker.cpp new file mode 100644 index 00000000..9a53de74 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfObjectFlocker.cpp @@ -0,0 +1,1060 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsMatrix44.h" +#include "hsGeometry3.h" +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plRefMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../pnSceneObject/plSceneObject.h" +#include "hsTimer.h" +#include "../plMath/plRandom.h" +#include "../pnMessage/plEnableMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plLoadCloneMsg.h" + +//#include "../plPipeline/plDebugGeometry.h" + +#include +#include + +#include "pfObjectFlocker.h" + +#define PI 3.14159f +#define HALF_PI (PI/2) +#define GRAVITY 9.806650f // meters/second +#ifdef INFINITY +#undef INFINITY +#endif +#define INFINITY 999999.0f + +#define RAND() (float) (rand()/(RAND_MAX * 1.0)) +#define SIGN(x) (((x) < 0) ? -1 : 1) + +const int pfObjectFlocker::fFileVersion = 1; + +#define FLOCKER_SHOW_DEBUG_LINES 0 + +#if FLOCKER_SHOW_DEBUG_LINES +// make a few easy-to-use colors for the debug lines +#define DEBUG_COLOR_RED 255, 0, 0 +#define DEBUG_COLOR_GREEN 0, 255, 0 +#define DEBUG_COLOR_BLUE 0, 0, 255 +#define DEBUG_COLOR_YELLOW 255, 255, 0 +#define DEBUG_COLOR_CYAN 0, 255, 255 +#define DEBUG_COLOR_PINK 255, 0, 255 +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Some quick utility functions +/////////////////////////////////////////////////////////////////////////////// + +// Basic linear interpolation +template +inline T Interpolate(float alpha, const T& x0, const T& x1) +{ + return x0 + ((x1 - x0) * alpha); +} + +// Clip a value to the min and max, if necessary +inline float Clip(const float x, const float min, const float max) +{ + if (x < min) return min; + if (x > max) return max; + return x; +} + +inline float ScalarRandomWalk(const float initial, const float walkspeed, const float min, const float max) +{ + const float next = initial + (((RAND() * 2) - 1) * walkspeed); + if (next < min) return min; + if (next > max) return max; + return next; +} + +// Classify a value relative to the interval between two bounds: +// returns -1 when below the lower bound +// returns 0 when between the bounds (inside the interval) +// returns +1 when above the upper bound +inline int IntervalComparison (float x, float lowerBound, float upperBound) +{ + if (x < lowerBound) return -1; + if (x > upperBound) return +1; + return 0; +} + +// Blend the new value into the accumulator using the smooth rate +// If smoothRate is 0 the accumulator will not change. +// If smoothRate is 1 the accumulator will be set to the new value with no smoothing. +// Useful values are "near zero". +template +inline void BlendIntoAccumulator(const float smoothRate, const T &newValue, T &smoothedAccumulator) +{ + smoothedAccumulator = Interpolate(Clip(smoothRate, 0, 1), smoothedAccumulator, newValue); +} + +// return component of vector parallel to a unit basis vector +// (IMPORTANT NOTE: assumes "basis" has unit magnitude (length==1)) +inline hsVector3 ParallelComponent(const hsVector3 &vec, const hsVector3 &unitBasis) +{ + const float projection = vec * unitBasis; + return unitBasis * projection; +} + +// return component of vector perpendicular to a unit basis vector +// (IMPORTANT NOTE: assumes "basis" has unit magnitude (length==1)) +inline hsVector3 PerpendicularComponent(const hsVector3 &vec, const hsVector3& unitBasis) +{ + return vec - ParallelComponent(vec, unitBasis); +} + +// clamps the length of a given vector to maxLength. If the vector is +// shorter its value is returned unaltered, if the vector is longer +// the value returned has length of maxLength and is parallel to the +// original input. +hsVector3 TruncateLength (const hsVector3 &vec, const float maxLength) +{ + const float maxLengthSquared = maxLength * maxLength; + const float vecLengthSquared = vec.MagnitudeSquared(); + if (vecLengthSquared <= maxLengthSquared) + return vec; + else + return vec * (maxLength / sqrt(vecLengthSquared)); +} + +// Enforce an upper bound on the angle by which a given arbitrary vector +// diviates from a given reference direction (specified by a unit basis +// vector). The effect is to clip the "source" vector to be inside a cone +// defined by the basis and an angle. +hsVector3 LimitMaxDeviationAngle(const hsVector3 &vec, const float cosineOfConeAngle, const hsVector3 &basis) +{ + // immediately return zero length input vectors + float sourceLength = vec.Magnitude(); + if (sourceLength == 0) return vec; + + // measure the angular diviation of "source" from "basis" + const hsVector3 direction = vec / sourceLength; + float cosineOfSourceAngle = direction * basis; + + // Simply return "source" if it already meets the angle criteria. + if (cosineOfSourceAngle >= cosineOfConeAngle) return vec; + + // find the portion of "source" that is perpendicular to "basis" + const hsVector3 perp = PerpendicularComponent(vec, basis); + + // normalize that perpendicular + hsVector3 unitPerp = perp; + unitPerp.Normalize(); + + // construct a new vector whose length equals the source vector, + // and lies on the intersection of a plane (formed the source and + // basis vectors) and a cone (whose axis is "basis" and whose + // angle corresponds to cosineOfConeAngle) + float perpDist = sqrt(1 - (cosineOfConeAngle * cosineOfConeAngle)); + const hsVector3 c0 = basis * cosineOfConeAngle; + const hsVector3 c1 = unitPerp * perpDist; + return (c0 + c1) * sourceLength; +} + +/////////////////////////////////////////////////////////////////////////////// +// pfVehicle functions +/////////////////////////////////////////////////////////////////////////////// + +void pfVehicle::IMeasurePathCurvature(const float elapsedTime) +{ + if (elapsedTime > 0) + { + const hsVector3 deltaPosition(&fLastPos, &Position()); + const hsVector3 deltaForward = (fLastForward - Forward()) / deltaPosition.Magnitude(); + const hsVector3 lateral = PerpendicularComponent(deltaForward, Forward()); + const float sign = ((lateral * Side()) < 0) ? 1.0f : -1.0f; + fCurvature = lateral.Magnitude() * sign; + BlendIntoAccumulator(elapsedTime * 4.0f, fCurvature, fSmoothedCurvature); + fLastForward = Forward(); + fLastPos = Position(); + } +} + +// Reset functions +void pfVehicle::Reset() +{ + ResetLocalSpace(); + + SetMass(1); // defaults to 1 so acceleration = force + SetSpeed(0); // speed along the forward direction + + SetMaxForce(10.0f); // steering force is clipped to this magnitude + SetMaxSpeed(5.0f); // velocity is clipped to this magnitude + + SetRadius(0.5f); // size of bounding sphere + + // Reset bookkeeping for our averages + ResetSmoothedPosition(); + ResetSmoothedCurvature(); + ResetSmoothedAcceleration(); +} + +float pfVehicle::ResetSmoothedCurvature(float value /* = 0 */) +{ + fLastForward.Set(0, 0, 0); + fLastPos.Set(0, 0, 0); + return fSmoothedCurvature = fCurvature = value; +} + +hsVector3 pfVehicle::ResetSmoothedAcceleration(const hsVector3 &value /* = hsVector3(0,0,0) */) +{ + return fSmoothedAcceleration = value; +} + +hsPoint3 pfVehicle::ResetSmoothedPosition(const hsPoint3 &value /* = hsPoint3(0,0,0) */) +{ + return fSmoothedPosition = value; +} + +void pfVehicle::ResetLocalSpace() +{ + fSide.Set(1, 0, 0); + fForward.Set(0, 1, 0); + fUp.Set(0, 0, 1); + fPos.Set(0, 0, 0); +} + +// Geometry functions +void pfVehicle::SetUnitSideFromForwardAndUp() +{ + // derive new unit side vector from forward and up + fSide = fForward % fUp; + fSide.Normalize(); +} + +void pfVehicle::RegenerateOrthonormalBasisUF(const hsVector3 &newUnitForward) +{ + fForward = newUnitForward; + + // derive new side vector from NEW forward and OLD up + SetUnitSideFromForwardAndUp(); + + // derive new up vector from new side and new forward (should have unit length since side and forward are + // perpendicular and unit length) + fUp = fSide % fForward; +} + +void pfVehicle::RegenerateLocalSpace(const hsVector3 &newVelocity, const float /*elapsedTime*/) +{ + // adjust orthonormal basis vectors to be aligned with new velocity + if (Speed() > 0) + RegenerateOrthonormalBasisUF(newVelocity / Speed()); +} + +void pfVehicle::RegenerateLocalSpaceForBanking(const hsVector3 &newVelocity, const float elapsedTime) +{ + // the length of this global-upward-pointing vector controls the vehicle's + // tendency to right itself as it is rolled over from turning acceleration + const hsVector3 globalUp(0, 0, 0.2f); + + // acceleration points toward the center of local path curvature, the + // length determines how much the vehicle will roll while turning + const hsVector3 accelUp = fSmoothedAcceleration * 0.05f; + + // combined banking, sum of up due to turning and global up + const hsVector3 bankUp = accelUp + globalUp; + + // blend bankUp into vehicle's up vector + const float smoothRate = elapsedTime * 3; + hsVector3 tempUp = Up(); + BlendIntoAccumulator(smoothRate, bankUp, tempUp); + tempUp.Normalize(); + SetUp(tempUp); + + // adjust orthonormal basis vectors to be aligned with new velocity + if (Speed() > 0) + RegenerateOrthonormalBasisUF(newVelocity / Speed()); +} + +// Physics functions +void pfVehicle::ApplySteeringForce(const hsVector3 &force, const float deltaTime) +{ + const hsVector3 adjustedForce = AdjustRawSteeringForce(force, deltaTime); + + // enforce limit on magnitude of steering force + const hsVector3 clippedForce = TruncateLength(adjustedForce, MaxForce()); + +#if FLOCKER_SHOW_DEBUG_LINES + // Draw the adjusted force vector + plDebugGeometry::Instance()->DrawLine(Position(), Position() + clippedForce, DEBUG_COLOR_GREEN); +#endif + + // compute acceleration and velocity + hsVector3 newAcceleration = (clippedForce / Mass()); + hsVector3 newVelocity = Velocity(); + + // damp out abrupt changes and oscillations in steering acceleration + // (rate is proportional to time step, then clipped into useful range) + if (deltaTime > 0) + { + const float smoothRate = Clip(9 * deltaTime, 0.15f, 0.4f); + BlendIntoAccumulator(smoothRate, newAcceleration, fSmoothedAcceleration); + } + + // Euler integrate (per frame) acceleration into velocity + newVelocity += fSmoothedAcceleration * deltaTime; + + // enforce speed limit + newVelocity = TruncateLength(newVelocity, MaxSpeed()); + + // update Speed + SetSpeed(newVelocity.Magnitude()); + + // Euler integrate (per frame) velocity into position + SetPosition(Position() + (newVelocity * deltaTime)); + + // regenerate local space (by default: align vehicle's forward axis with + // new velocity, but this behavior may be overridden by derived classes.) + RegenerateLocalSpace(newVelocity, deltaTime); + + // maintain path curvature information + IMeasurePathCurvature(deltaTime); + + // running average of recent positions + BlendIntoAccumulator(deltaTime * 0.06f, Position(), fSmoothedPosition); +} + +hsVector3 pfVehicle::AdjustRawSteeringForce(const hsVector3 &force, const float deltaTime) +{ + const float maxAdjustedSpeed = 0.2f * MaxSpeed(); + + if ((Speed() > maxAdjustedSpeed) || (force == hsVector3(0,0,0))) + return force; // no adjustment needed if they are going above 20% of max speed + else + { + const float range = Speed() / maxAdjustedSpeed; // make sure they don't turn too much if below 20% of max speed + const float cosine = Interpolate(pow(range, 20), 1.0f, -1.0f); + return LimitMaxDeviationAngle(force, cosine, Forward()); + } +} + +void pfVehicle::ApplyBrakingForce(const float rate, const float deltaTime) +{ + const float rawBraking = Speed() * rate; + const float clipBraking = ((rawBraking < MaxForce()) ? rawBraking : MaxForce()); + + SetSpeed(Speed() - (clipBraking * deltaTime)); +} + +hsPoint3 pfVehicle::PredictFuturePosition(const float predictionTime) +{ + return Position() + (Velocity() * predictionTime); +} + +/////////////////////////////////////////////////////////////////////////////// +// pfBoidGoal functions +/////////////////////////////////////////////////////////////////////////////// + +pfBoidGoal::pfBoidGoal() +{ + fLastPos.Set(0, 0, 0); + fCurPos.Set(0, 0, 0); + fSpeed = 0; + fHasLastPos = false; // our last pos doesn't make sense yet +} + +void pfBoidGoal::Update(plSceneObject *goal, float deltaTime) +{ + if (!fHasLastPos) // the last pos is invalid, so we need to init now + { + fLastPos = fCurPos = goal->GetLocalToWorld().GetTranslate(); + fSpeed = 0; + fForward.Set(1,0,0); // make a unit vector, it shouldn't matter that it's incorrect as this is only for one frame + fHasLastPos = true; + return; + } + + fLastPos = fCurPos; + fCurPos = goal->GetLocalToWorld().GetTranslate(); + hsVector3 change(&fCurPos, &fLastPos); + float unadjustedSpeed = change.Magnitude(); + fSpeed = unadjustedSpeed / deltaTime; // update speed (mag is in meters, time is in seconds) + if (unadjustedSpeed == 0) + return; // if our speed is zero, don't recalc our forward vector (leave it as it was last time) + fForward = change / unadjustedSpeed; + +#if FLOCKER_SHOW_DEBUG_LINES + // Show where we are predicting the location to be in 1 second + plDebugGeometry::Instance()->DrawLine(Position(), PredictFuturePosition(1), DEBUG_COLOR_BLUE); +#endif +} + +hsPoint3 pfBoidGoal::PredictFuturePosition(const float predictionTime) +{ + return fCurPos + (fForward * fSpeed * predictionTime); +} + +/////////////////////////////////////////////////////////////////////////////// +// pfBoid functions +/////////////////////////////////////////////////////////////////////////////// + +pfBoid::pfBoid(pfProximityDatabase& pd, pfObjectFlocker *flocker, plKey &key) +{ + // allocate a token for this boid in the proximity database + fProximityToken = NULL; + ISetupToken(pd); + + Reset(); + + fFlockerPtr = flocker; + fObjKey = key; + + IFlockDefaults(); + + fProximityToken->UpdateWithNewPosition(Position()); +} + +pfBoid::pfBoid(pfProximityDatabase& pd, pfObjectFlocker *flocker, plKey &key, hsPoint3 &pos) +{ + // allocate a token for this boid in the proximity database + fProximityToken = NULL; + ISetupToken(pd); + + Reset(); + + fFlockerPtr = flocker; + fObjKey = key; + + SetPosition(pos); + + IFlockDefaults(); + + fProximityToken->UpdateWithNewPosition(Position()); +} + +pfBoid::pfBoid(pfProximityDatabase& pd, pfObjectFlocker *flocker, plKey &key, hsPoint3 &pos, float speed, hsVector3 &forward, hsVector3 &side, hsVector3 &up) +{ + // allocate a token for this boid in the proximity database + fProximityToken = NULL; + ISetupToken(pd); + + Reset(); + + fFlockerPtr = flocker; + fObjKey = key; + + SetPosition(pos); + SetSpeed(speed); + SetForward(forward); + SetSide(side); + SetUp(up); + + IFlockDefaults(); + + fProximityToken->UpdateWithNewPosition(Position()); +} + +pfBoid::~pfBoid() +{ + // delete this boid's token in the proximity database + delete fProximityToken; + + plLoadCloneMsg* msg = TRACKED_NEW plLoadCloneMsg(fObjKey, fFlockerPtr->GetKey(), 0, false); + msg->Send(); +} + +void pfBoid::IFlockDefaults() +{ + fSeparationRadius = 5.0f; + fSeparationAngle = -0.707f; + fSeparationWeight = 12.0f; + + fCohesionRadius = 9.0f; + fCohesionAngle = -0.15f; + fCohesionWeight = 8.0f; + + fGoalWeight = 8.0f; + fRandomWeight = 12.0f; +} + +void pfBoid::ISetupToken(pfProximityDatabase &pd) +{ + // delete this boid's token in the old proximity database + delete fProximityToken; + + // allocate a token for this boid in the proximity database + fProximityToken = pd.MakeToken(this); +} + +hsBool pfBoid::IInBoidNeighborhood(const pfVehicle &other, const float minDistance, const float maxDistance, const float cosMaxAngle) +{ + if (&other == this) // abort if we're looking at ourselves + return false; + else + { + const hsVector3 offset(&(other.Position()), &Position()); + const float distanceSquared = offset.MagnitudeSquared(); + + // definitely in neighborhood if inside minDistance sphere + if (distanceSquared < (minDistance * minDistance)) + return true; + else + { + // definitely not in neighborhood if outside maxDistance sphere + if (distanceSquared > (maxDistance * maxDistance)) + return false; + else + { + // otherwise, test angular offset from forward axis (can we "see" it?) + const hsVector3 unitOffset = offset / sqrt(distanceSquared); + const float forwardness = Forward() * unitOffset; + return forwardness > cosMaxAngle; + } + } + } +} + +// Steering functions +hsVector3 pfBoid::ISteerForWander(float timeDelta) +{ + // random walk the fWanderSide and fWanderUp variables between -1 and +1 + const float speed = 10 * timeDelta; // the 10 value found through experimentation + fWanderSide = ScalarRandomWalk(fWanderSide, speed, -1, +1); + fWanderUp = ScalarRandomWalk(fWanderUp, speed, -1, +1); + + hsVector3 force = (Side() * fWanderSide) + (Up() * fWanderUp); + +#if FLOCKER_SHOW_DEBUG_LINES + // Draw the random walk component + plDebugGeometry::Instance()->DrawLine(Position(), Position()+force, DEBUG_COLOR_PINK); +#endif + + return force; +} + +hsVector3 pfBoid::ISteerForSeek(const hsPoint3 &target) +{ +#if FLOCKER_SHOW_DEBUG_LINES + // Draw to where we are steering towards + plDebugGeometry::Instance()->DrawLine(Position(), target, DEBUG_COLOR_RED); +#endif + + const hsVector3 desiredVelocity(&target, &Position()); + return desiredVelocity - Velocity(); +} + +hsVector3 pfBoid::ISteerToGoal(pfBoidGoal &goal, float maxPredictionTime) +{ + // offset from this to quarry, that distance, unit vector toward quarry + const hsVector3 offset(&goal.Position(), &Position()); + const float distance = offset.Magnitude(); + if (distance == 0) // nowhere to go + return hsVector3(0, 0, 0); + const hsVector3 unitOffset = offset / distance; + + // how parallel are the paths of "this" and the goal + // (1 means parallel, 0 is pependicular, -1 is anti-parallel after later calculations) + const float parallelness = Forward() * goal.Forward(); + + // how "forward" is the direction to the quarry + // (1 means dead ahead, 0 is directly to the side, -1 is straight back after later calculations) + const float forwardness = Forward() * unitOffset; + + float speed = Speed(); + if (speed == 0) + speed = 0.00001; // make it really small in case we start out not moving + const float directTravelTime = distance / speed; + const int f = IntervalComparison(forwardness, -0.707f, 0.707f); // -1 if below -0.707f, 0 if between, and +1 if above 0.707f) + const int p = IntervalComparison(parallelness, -0.707f, 0.707f); // 0.707 is basically cos(45deg) (45deg = PI/4) + + float timeFactor = 0; // to be filled in below - how far ahead to predict position so it looks good + + // Break the pursuit into nine cases, the cross product of the + // quarry being [ahead, aside, or behind] us and heading + // [parallel, perpendicular, or anti-parallel] to us. + switch (f) + { + case +1: + switch (p) + { + case +1: // ahead, parallel + timeFactor = 4; + break; + case 0: // ahead, perpendicular + timeFactor = 1.8f; + break; + case -1: // ahead, anti-parallel + timeFactor = 0.85f; + break; + } + break; + case 0: + switch (p) + { + case +1: // aside, parallel + timeFactor = 1; + break; + case 0: // aside, perpendicular + timeFactor = 0.8f; + break; + case -1: // aside, anti-parallel + timeFactor = 4; + break; + } + break; + case -1: + switch (p) + { + case +1: // behind, parallel + timeFactor = 0.5f; + break; + case 0: // behind, perpendicular + timeFactor = 2; + break; + case -1: // behind, anti-parallel + timeFactor = 2; + break; + } + break; + } + + // estimated time until intercept of quarry + const float et = directTravelTime * timeFactor; + const float etl = (et > maxPredictionTime) ? maxPredictionTime : et; + + // estimated position of quarry at intercept + const hsPoint3 target = goal.PredictFuturePosition(etl); + + // steer directly for our target (which is a point ahead of the object we are pursuing) + return ISteerForSeek(target); +} + +hsVector3 pfBoid::ISteerForSeparation(const float maxDistance, const float cosMaxAngle, const std::vector &flock) +{ + // steering accumulator and count of neighbors, both initially zero + hsVector3 steering(0, 0, 0); + int neighbors = 0; + + // for each of the other vehicles... + for (std::vector::const_iterator other = flock.begin(); other != flock.end(); other++) + { + if (IInBoidNeighborhood(**other, Radius() * 3, maxDistance, cosMaxAngle)) + { + // add in steering contribution + // (opposite of the offset direction, divided once by distance + // to normalize, divided another time to get 1/d falloff) + const hsVector3 offset(&((**other).Position()), &Position()); + const float distanceSquared = offset * offset; + steering += (offset / -distanceSquared); + + // count neighbors + neighbors++; + } + } + + // divide by neighbors, then normalize to pure direction + if (neighbors > 0) + { + steering = (steering / (float)neighbors); + steering.Normalize(); + } + +#if FLOCKER_SHOW_DEBUG_LINES + // Draw the random walk component + plDebugGeometry::Instance()->DrawLine(Position(), Position()+steering, DEBUG_COLOR_CYAN); +#endif + + return steering; +} + +hsVector3 pfBoid::ISteerForCohesion(const float maxDistance, const float cosMaxAngle, const std::vector &flock) +{ + // steering accumulator and count of neighbors, both initially zero + hsVector3 steering(0, 0, 0); + int neighbors = 0; + + // for each of the other vehicles... + for (std::vector::const_iterator other = flock.begin(); other != flock.end(); other++) + { + if (IInBoidNeighborhood(**other, Radius() * 3, maxDistance, cosMaxAngle)) + { + // accumulate sum of neighbor's positions + steering += (**other).Position(); + + // count neighbors + neighbors++; + } + } + + // divide by neighbors, subtract off current position to get error- + // correcting direction, then normalize to pure direction + if (neighbors > 0) + { + hsVector3 posVector(&(Position()), &(hsPoint3(0,0,0))); // quick hack to turn a point into a vector + steering = ((steering / (float)neighbors) - posVector); + steering.Normalize(); + } + +#if FLOCKER_SHOW_DEBUG_LINES + // Draw the random walk component + plDebugGeometry::Instance()->DrawLine(Position(), Position()+steering, DEBUG_COLOR_YELLOW); +#endif + + return steering; +} + +// Used for frame-by-frame updates; no time deltas on positions. +void pfBoid::Update(pfBoidGoal &goal, float deltaTime) +{ + const float maxTime = 20; // found by testing + + // find all flockmates within maxRadius using proximity database + fNeighbors.clear(); + fProximityToken->FindNeighbors(Position(), fSeparationRadius, fNeighbors); + + hsVector3 goalVector = ISteerToGoal(goal, maxTime); + hsVector3 randomVector = ISteerForWander(deltaTime); + hsVector3 separationVector = ISteerForSeparation(fSeparationRadius, fSeparationAngle, fNeighbors); + hsVector3 cohesionVector = ISteerForCohesion(fCohesionRadius, fCohesionAngle, fNeighbors); + + hsVector3 steeringVector = (fGoalWeight * goalVector) + (fRandomWeight * randomVector) + + (fSeparationWeight * separationVector) + (fCohesionWeight * cohesionVector); + + ApplySteeringForce(steeringVector, deltaTime); + + fProximityToken->UpdateWithNewPosition(Position()); +} + +void pfBoid::RegenerateLocalSpace(const hsVector3 &newVelocity, const float elapsedTime) +{ + RegenerateLocalSpaceForBanking(newVelocity, elapsedTime); +} + +/////////////////////////////////////////////////////////////////////////////// +// pfFlock functions +/////////////////////////////////////////////////////////////////////////////// +pfFlock::pfFlock() : +fGoalWeight(8.0f), +fRandomWeight(12.0f), +fSeparationRadius(5.0f), +fSeparationWeight(12.0f), +fCohesionRadius(9.0f), +fCohesionWeight(8.0f), +fMaxForce(10.0f), +fMaxSpeed(5.0f), +fMinSpeed(4.0f) +{ + fDatabase = TRACKED_NEW pfBasicProximityDatabase(); +} + +pfFlock::~pfFlock() +{ + int flock_size = fBoids.size(); + for (int i = 0; i < flock_size; i++) + { + delete fBoids[i]; + fBoids[i] = nil; + } + fBoids.clear(); + + delete fDatabase; + fDatabase = NULL; +} + +void pfFlock::SetGoalWeight(float goalWeight) +{ + for (int i = 0; i < fBoids.size(); i++) + fBoids[i]->SetGoalWeight(goalWeight); + fGoalWeight = goalWeight; +} + +void pfFlock::SetWanderWeight(float wanderWeight) +{ + for (int i = 0; i < fBoids.size(); i++) + fBoids[i]->SetWanderWeight(wanderWeight); + fRandomWeight = wanderWeight; +} + +void pfFlock::SetSeparationWeight(float weight) +{ + for (int i = 0; i < fBoids.size(); i++) + fBoids[i]->SetSeparationWeight(weight); + fSeparationWeight = weight; +} + +void pfFlock::SetSeparationRadius(float radius) +{ + for (int i = 0; i < fBoids.size(); i++) + fBoids[i]->SetSeparationRadius(radius); + fSeparationRadius = radius; +} + +void pfFlock::SetCohesionWeight(float weight) +{ + for (int i = 0; i < fBoids.size(); i++) + fBoids[i]->SetCohesionWeight(weight); + fCohesionWeight = weight; +} + +void pfFlock::SetCohesionRadius(float radius) +{ + for (int i = 0; i < fBoids.size(); i++) + fBoids[i]->SetCohesionRadius(radius); + fCohesionRadius = radius; +} + +void pfFlock::SetMaxForce(float force) +{ + for (int i = 0; i < fBoids.size(); i++) + fBoids[i]->SetMaxForce(force); + fMaxForce = force; +} + +void pfFlock::SetMaxSpeed(float speed) +{ + for (int i = 0; i < fBoids.size(); i++) + { + float speedAdjust = (fMaxSpeed - fMinSpeed) * RAND(); + fBoids[i]->SetMaxSpeed(speed - speedAdjust); + } + fMaxSpeed = speed; +} + +void pfFlock::SetMinSpeed(float minSpeed) +{ + fMinSpeed = minSpeed; +} + +void pfFlock::Update(plSceneObject *goal, float deltaTime) +{ + // update the goal data + fBoidGoal.Update(goal, deltaTime); + + // update the flock + float delta = (deltaTime > 0.3f) ? 0.3f : deltaTime; + std::vector::iterator i; + + for (i = fBoids.begin(); i != fBoids.end(); i++) + (*i)->Update(fBoidGoal, delta); +} + +void pfFlock::AddBoid(pfObjectFlocker *flocker, plKey &key, hsPoint3 &pos) +{ + pfBoid *newBoid = TRACKED_NEW pfBoid(*fDatabase, flocker, key, pos); + + newBoid->SetGoalWeight(fGoalWeight); + newBoid->SetWanderWeight(fRandomWeight); + + newBoid->SetSeparationWeight(fSeparationWeight); + newBoid->SetSeparationRadius(fSeparationRadius); + + newBoid->SetCohesionWeight(fCohesionWeight); + newBoid->SetCohesionRadius(fCohesionRadius); + + newBoid->SetMaxForce(fMaxForce); + float speedAdjust = (fMaxSpeed - fMinSpeed) * RAND(); + newBoid->SetMaxSpeed(fMaxSpeed - speedAdjust); + + fBoids.push_back(newBoid); +} + +pfBoid *pfFlock::GetBoid(int i) +{ + if (i >= 0 && i < fBoids.size()) + return fBoids[i]; + else + return nil; +} + +pfObjectFlocker::pfObjectFlocker() : +fUseTargetRotation(false), +fRandomizeAnimationStart(true), +fNumBoids(0) +{ +} + +pfObjectFlocker::~pfObjectFlocker() +{ + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +void pfObjectFlocker::SetNumBoids(UInt8 val) +{ + fNumBoids = val; +} + +hsBool pfObjectFlocker::MsgReceive(plMessage* msg) +{ + plInitialAgeStateLoadedMsg* loadMsg = plInitialAgeStateLoadedMsg::ConvertNoRef(msg); + if (loadMsg) + { + plEnableMsg* pMsg = TRACKED_NEW plEnableMsg; + pMsg->AddReceiver(fBoidKey); + pMsg->SetCmd(plEnableMsg::kDrawable); + pMsg->AddType(plEnableMsg::kDrawable); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers | plMessage::kPropagateToChildren); + pMsg->SetCmd(plEnableMsg::kDisable); + pMsg->Send(); + + hsPoint3 pos(fTarget->GetLocalToWorld().GetTranslate()); + for (int i = 0; i < fNumBoids; i++) + { + plLoadCloneMsg* cloneMsg = TRACKED_NEW plLoadCloneMsg(fBoidKey->GetUoid(), GetKey(), 0); + plKey newKey = cloneMsg->GetCloneKey(); + cloneMsg->Send(); + + hsScalar xAdjust = (2 * RAND()) - 1; // produces a random number between -1 and 1 + hsScalar yAdjust = (2 * RAND()) - 1; + hsScalar zAdjust = (2 * RAND()) - 1; + hsPoint3 boidPos(pos.fX + xAdjust, pos.fY + yAdjust, pos.fZ + zAdjust); + fFlock.AddBoid(this, newKey, boidPos); + } + plgDispatch::Dispatch()->UnRegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + + return true; + } + + plLoadCloneMsg* lcMsg = plLoadCloneMsg::ConvertNoRef(msg); + if (lcMsg && lcMsg->GetIsLoading()) + { + if (fRandomizeAnimationStart) + { + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->SetSender(GetKey()); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers | plMessage::kPropagateToChildren); + pMsg->AddReceiver( lcMsg->GetCloneKey() ); + pMsg->SetCmd(plAnimCmdMsg::kGoToPercent); + pMsg->fTime = RAND(); + pMsg->Send(); + } + } + + return plSingleModifier::MsgReceive(msg); +} + +hsBool pfObjectFlocker::IEval(double secs, hsScalar del, UInt32 dirty) +{ + fFlock.Update(fTarget, del); + + plSceneObject* boidSO = nil; + for (int i = 0; i < fNumBoids; i++) + { + pfBoid* boid = fFlock.GetBoid(i); + boidSO = plSceneObject::ConvertNoRef(boid->GetKey()->VerifyLoaded()); + + hsMatrix44 l2w; + hsMatrix44 w2l; + + if (fUseTargetRotation) + l2w = fTarget->GetLocalToWorld(); + else + { + l2w = boidSO->GetLocalToWorld(); + hsVector3 forward = boid->Forward(); + hsVector3 up = boid->Up(); + hsVector3 side = boid->Side(); + + // copy the vectors over + for(int i = 0; i < 3; i++) + { + l2w.fMap[i][0] = side[i]; + l2w.fMap[i][1] = forward[i]; + l2w.fMap[i][2] = up[i]; + } + } + + hsScalarTriple pos = boid->Position(); + l2w.SetTranslate(&pos); + l2w.GetInverse(&w2l); + + boidSO->SetTransform(l2w, w2l); + } + + return true; +} + + +void pfObjectFlocker::SetTarget(plSceneObject* so) +{ + plSingleModifier::SetTarget(so); + + if( fTarget ) + { + plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + } +} + +void pfObjectFlocker::Read(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Read(s, mgr); + + int version = s->ReadByte(); + hsAssert(version <= fFileVersion, "Flocker data is newer then client, please update your client"); + + SetNumBoids(s->ReadByte()); + fBoidKey = mgr->ReadKey(s); + + fFlock.SetGoalWeight(s->ReadSwapScalar()); + fFlock.SetWanderWeight(s->ReadSwapScalar()); + + fFlock.SetSeparationWeight(s->ReadSwapScalar()); + fFlock.SetSeparationRadius(s->ReadSwapScalar()); + + fFlock.SetCohesionWeight(s->ReadSwapScalar()); + fFlock.SetCohesionRadius(s->ReadSwapScalar()); + + fFlock.SetMaxForce(s->ReadSwapScalar()); + fFlock.SetMaxSpeed(s->ReadSwapScalar()); + fFlock.SetMinSpeed(s->ReadSwapScalar()); + + fUseTargetRotation = s->ReadBool(); + fRandomizeAnimationStart = s->ReadBool(); +} + +void pfObjectFlocker::Write(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Write(s, mgr); + + s->WriteByte(fFileVersion); + + s->WriteByte(fNumBoids); + mgr->WriteKey(s, fBoidKey); + + s->WriteSwapScalar(fFlock.GoalWeight()); + s->WriteSwapScalar(fFlock.WanderWeight()); + + s->WriteSwapScalar(fFlock.SeparationWeight()); + s->WriteSwapScalar(fFlock.SeparationRadius()); + + s->WriteSwapScalar(fFlock.CohesionWeight()); + s->WriteSwapScalar(fFlock.CohesionRadius()); + + s->WriteSwapScalar(fFlock.MaxForce()); + s->WriteSwapScalar(fFlock.MaxSpeed()); + s->WriteSwapScalar(fFlock.MinSpeed()); + + s->WriteBool(fUseTargetRotation); + s->WriteBool(fRandomizeAnimationStart); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfObjectFlocker.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfObjectFlocker.h new file mode 100644 index 00000000..a7c09573 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/pfObjectFlocker.h @@ -0,0 +1,438 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef OBJECT_FLOCKER_H +#define OBJECT_FLOCKER_H + +#include "../pnModifier/plSingleModifier.h" + +class hsStream; +class hsResMgr; +class plRandom; +class pfObjectFlocker; + +// Database tokens for our prox database +template +class pfTokenForProximityDatabase +{ +public: + virtual ~pfTokenForProximityDatabase() {} + + // call this when your position changes + virtual void UpdateWithNewPosition(const hsPoint3 &newPos) = 0; + + // find all close-by objects (determined by center and radius) + virtual void FindNeighbors(const hsPoint3 ¢er, const float radius, std::vector &results) = 0; +}; + +// A basic prox database (might need to be optimized in the future) +template +class pfBasicProximityDatabase +{ +public: + class tokenType; + typedef std::vector tokenVector; + typedef typename tokenVector::const_iterator tokenIterator; + + // "token" to represent objects stored in the database + class tokenType: public pfTokenForProximityDatabase + { + private: + tokenVector& fTokens; + T fParent; + hsPoint3 fPosition; + + public: + // constructor + tokenType(T parentObject, tokenVector& tokens) : fParent(parentObject), fTokens(tokens) + { + fTokens.push_back(this); + } + + // destructor + virtual ~tokenType() + { + // remove this token from the database's vector + fTokens.erase(std::find(fTokens.begin(), fTokens.end(), this)); + } + + // call this when your position changes + void UpdateWithNewPosition(const hsPoint3 &newPosition) {fPosition = newPosition;} + + // find all close-by objects (determined by center and radius) + void FindNeighbors(const hsPoint3 ¢er, const float radius, std::vector & results) + { + // take the slow way, loop and check every one + const float radiusSquared = radius * radius; + for (tokenIterator i = fTokens.begin(); i != fTokens.end(); i++) + { + const hsVector3 offset(¢er, &((**i).fPosition)); + const float distanceSquared = offset.MagnitudeSquared(); + + // push onto result vector when within given radius + if (distanceSquared < radiusSquared) + results.push_back((**i).fParent); + } + } + }; + +private: + // STL vector containing all tokens in database + tokenVector fGroup; + +public: + // constructor + pfBasicProximityDatabase(void) {} + + // destructor + virtual ~pfBasicProximityDatabase() {} + + // allocate a token to represent a given client object in this database + tokenType *MakeToken(T parentObject) {return TRACKED_NEW tokenType(parentObject, fGroup);} + + // return the number of tokens currently in the database + int Size(void) {return group.size();} +}; + +// A basic vehicle class that handles accelleration, braking, and turning +class pfVehicle +{ +private: + hsPoint3 fPos; // position in meters + hsPoint3 fLastPos; // the last position we had + hsPoint3 fSmoothedPosition; + + hsVector3 fVel; // velocity in meters/second + hsVector3 fSmoothedAcceleration; + + hsVector3 fForward; // forward vector (unit length) + hsVector3 fLastForward; // the last forward vector we had + hsVector3 fSide; // side vector (unit length) + hsVector3 fUp; // up vector (unit length) + + float fSpeed; // speed (length of velocity vector) + float fMass; // mass of the object (defaults to 1) + float fMaxForce; // the maximum steering force that can be applied + float fMaxSpeed; // the maximum speed of this vehicle + + float fCurvature; + float fSmoothedCurvature; + + float fRadius; + + // measure the path curvature (1/turning radius), maintain smoothed version + void IMeasurePathCurvature(const float elapsedTime); +public: + pfVehicle() {Reset();} + virtual ~pfVehicle() {} + + void Reset(); + + // get/set attributes + float Mass() const {return fMass;} + float SetMass(float m) {return fMass = m;} + + hsVector3 Forward() const {return fForward;} + hsVector3 SetForward(hsVector3 forward) {return fForward = forward;} + hsVector3 Side() const {return fSide;} + hsVector3 SetSide(hsVector3 side) {return fSide = side;} + hsVector3 Up() const {return fUp;} + hsVector3 SetUp(hsVector3 up) {return fUp = up;} + + hsPoint3 Position() const {return fPos;} + hsPoint3 SetPosition(hsPoint3 pos) {return fPos = pos;} + + hsVector3 Velocity() const {return Forward() * fSpeed;} + + float Speed() const {return fSpeed;} + float SetSpeed(float speed) {return fSpeed = speed;} + + float MaxForce() const {return fMaxForce;} + float SetMaxForce(float maxForce) {return fMaxForce = maxForce;} + + float MaxSpeed() const {return fMaxSpeed;} + float SetMaxSpeed(float maxSpeed) {return fMaxSpeed = maxSpeed;} + + float Curvature() const {return fCurvature;} + + float SmoothedCurvature() {return fSmoothedCurvature;} + float ResetSmoothedCurvature(float value = 0); + + hsVector3 SmoothedAcceleration() {return fSmoothedAcceleration;} + hsVector3 ResetSmoothedAcceleration(const hsVector3 &value = hsVector3(0,0,0)); + + hsPoint3 SmoothedPosition() {return fSmoothedPosition;} + hsPoint3 ResetSmoothedPosition(const hsPoint3 &value = hsPoint3(0,0,0)); + + float Radius() const {return fRadius;} + float SetRadius(float radius) {return fRadius = radius;} + + // Basic geometry functions + + // Reset local space to identity + void ResetLocalSpace(); + + // Set the side vector to a normalized cross product of forward and up + void SetUnitSideFromForwardAndUp(); + + // Regenerate orthonormal basis vectors given a new forward vector (unit length) + void RegenerateOrthonormalBasisUF(const hsVector3 &newUnitForward); + + // If the new forward is NOT known to have unit length + void RegenerateOrthonormalBasis(const hsVector3 &newForward) + {hsVector3 temp = newForward; temp.Normalize(); RegenerateOrthonormalBasisUF(temp);} + + // For supplying both a new forward, and a new up + void RegenerateOrthonormalBasis(const hsVector3 &newForward, const hsVector3 &newUp) + {fUp = newUp; RegenerateOrthonormalBasis(newForward);} + + // Keep forward parallel to velocity, change up as little as possible + virtual void RegenerateLocalSpace(const hsVector3 &newVelocity, const float elapsedTime); + + // Keep forward parallel to velocity, but "bank" the up vector + void RegenerateLocalSpaceForBanking(const hsVector3 &newVelocity, const float elapsedTime); + + // Vehicle physics functions + + // apply a steering force to our momentum and adjust our + // orientation to match our velocity vector + void ApplySteeringForce(const hsVector3 &force, const float deltaTime); + + // adjust the steering force passed to ApplySteeringForce (so sub-classes can refine) + // by default, we won't allow backward-facing steering at a low speed + virtual hsVector3 AdjustRawSteeringForce(const hsVector3 &force, const float deltaTime); + + // apply a braking force + void ApplyBrakingForce(const float rate, const float deltaTime); + + // predict the position of the vehicle (assumes constant velocity) + hsPoint3 PredictFuturePosition(const float predictionTime); +}; + +// A goal object, basically keeps track of a scene object so we can get velocity from it +class pfBoidGoal +{ +private: + hsPoint3 fLastPos; + hsPoint3 fCurPos; + hsVector3 fForward; + float fSpeed; // in meters/sec + hsBool fHasLastPos; // does the last position make sense? +public: + pfBoidGoal(); + ~pfBoidGoal() {} + + void Update(plSceneObject *goal, float deltaTime); + + hsPoint3 Position() const {return fCurPos;} + float Speed() const {return fSpeed;} + hsVector3 Forward() const {return fForward;} + hsPoint3 PredictFuturePosition(const float predictionTime); +}; + +typedef pfTokenForProximityDatabase pfProximityToken; +typedef pfBasicProximityDatabase pfProximityDatabase; + +// The actual "flocking following" (not really a boid, but whatever) +class pfBoid: public pfVehicle +{ +private: + plKey fObjKey; + + float fWanderSide; + float fWanderUp; + + float fGoalWeight; + float fRandomWeight; + + float fSeparationRadius; + float fSeparationAngle; + float fSeparationWeight; + + float fCohesionRadius; + float fCohesionAngle; + float fCohesionWeight; + + pfProximityToken* fProximityToken; + std::vector fNeighbors; + + // Set our flocking settings to default + void IFlockDefaults(); + + // Setup our prox database token + void ISetupToken(pfProximityDatabase &pd); + + // Are we in the neighborhood of another boid? + hsBool IInBoidNeighborhood(const pfVehicle &other, const float minDistance, const float maxDistance, const float cosMaxAngle); + // Wander steering + hsVector3 ISteerForWander(float timeDelta); + // Seek the target point + hsVector3 ISteerForSeek(const hsPoint3 &target); + // Steer the boid toward our goal + hsVector3 ISteerToGoal(pfBoidGoal &goal, float maxPredictionTime); + // Steer to keep separation + hsVector3 ISteerForSeparation(const float maxDistance, const float cosMaxAngle, const std::vector &flock); + // Steer to keep the flock together + hsVector3 ISteerForCohesion(const float maxDistance, const float cosMaxAngle, const std::vector &flock); + +public: + pfObjectFlocker *fFlockerPtr; + + pfBoid(pfProximityDatabase &pd, pfObjectFlocker *flocker, plKey &key); + pfBoid(pfProximityDatabase &pd, pfObjectFlocker *flocker, plKey &key, hsPoint3 &pos); + pfBoid(pfProximityDatabase &pd, pfObjectFlocker *flocker, plKey &key, hsPoint3 &pos, float speed, hsVector3 &forward, hsVector3 &side, hsVector3 &up); + virtual ~pfBoid(); + + // Get/set functions + float GoalWeight() const {return fGoalWeight;} + float SetGoalWeight(float goalWeight) {return fGoalWeight = goalWeight;} + float WanderWeight() const {return fRandomWeight;} + float SetWanderWeight(float wanderWeight) {return fRandomWeight = wanderWeight;} + + float SeparationWeight() const {return fSeparationWeight;} + float SetSeparationWeight(float weight) {return fSeparationWeight = weight;} + float SeparationRadius() const {return fSeparationRadius;} + float SetSeparationRadius(float radius) {return fSeparationRadius = radius;} + + float CohesionWeight() const {return fCohesionWeight;} + float SetCohesionWeight(float weight) {return fCohesionWeight = weight;} + float CohesionRadius() const {return fCohesionRadius;} + float SetCohesionRadius(float radius) {return fCohesionRadius = radius;} + + // Update the boid's data based on the goal and time delta + void Update(pfBoidGoal &goal, float deltaTime); + plKey &GetKey() {return fObjKey;} + + // We're redirecting this to the "banking" function + virtual void RegenerateLocalSpace(const hsVector3 &newVelocity, const float elapsedTime); +}; + +class pfFlock +{ +private: + std::vector fBoids; + pfBoidGoal fBoidGoal; + pfProximityDatabase *fDatabase; + + // global values so when we add a boid we can set it's parameters + float fGoalWeight, fRandomWeight; + float fSeparationWeight, fSeparationRadius; + float fCohesionWeight, fCohesionRadius; + float fMaxForce; // max steering force + float fMaxSpeed, fMinSpeed; +public: + pfFlock(); + ~pfFlock(); + + // Get/set functions (affect the whole flock, and any new boids added) + float GoalWeight() const {return fGoalWeight;} + void SetGoalWeight(float goalWeight); + float WanderWeight() const {return fRandomWeight;} + void SetWanderWeight(float wanderWeight); + + float SeparationWeight() const {return fSeparationWeight;} + void SetSeparationWeight(float weight); + float SeparationRadius() const {return fSeparationRadius;} + void SetSeparationRadius(float radius); + + float CohesionWeight() const {return fCohesionWeight;} + void SetCohesionWeight(float weight); + float CohesionRadius() const {return fCohesionRadius;} + void SetCohesionRadius(float radius); + + float MaxForce() const {return fMaxForce;} + void SetMaxForce(float force); + float MaxSpeed() const {return fMaxSpeed;} + void SetMaxSpeed(float speed); + float MinSpeed() const {return fMinSpeed;} + void SetMinSpeed(float minSpeed); + + // setup/run functions + void AddBoid(pfObjectFlocker *flocker, plKey &key, hsPoint3 &pos); + void Update(plSceneObject *goal, float deltaTime); + pfBoid *GetBoid(int i); + + friend class pfObjectFlocker; +}; + +class pfObjectFlocker : public plSingleModifier +{ +public: + pfObjectFlocker(); + ~pfObjectFlocker(); + + CLASSNAME_REGISTER( pfObjectFlocker ); + GETINTERFACE_ANY( pfObjectFlocker, plSingleModifier ); + + virtual void SetTarget(plSceneObject* so); + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void SetNumBoids(UInt8 val); + void SetBoidKey(plKey key) { fBoidKey = key; } + + float GoalWeight() const {return fFlock.GoalWeight();} + void SetGoalWeight(float goalWeight) {fFlock.SetGoalWeight(goalWeight);} + float WanderWeight() const {return fFlock.WanderWeight();} + void SetWanderWeight(float wanderWeight) {fFlock.SetWanderWeight(wanderWeight);} + + float SeparationWeight() const {return fFlock.SeparationWeight();} + void SetSeparationWeight(float weight) {fFlock.SetSeparationWeight(weight);} + float SeparationRadius() const {return fFlock.SeparationRadius();} + void SetSeparationRadius(float radius) {fFlock.SetSeparationRadius(radius);} + + float CohesionWeight() const {return fFlock.CohesionWeight();} + void SetCohesionWeight(float weight) {fFlock.SetCohesionWeight(weight);} + float CohesionRadius() const {return fFlock.CohesionRadius();} + void SetCohesionRadius(float radius) {fFlock.SetCohesionRadius(radius);} + + float MaxForce() const {return fFlock.MaxForce();} + void SetMaxForce(float force) {fFlock.SetMaxForce(force);} + float MaxSpeed() const {return fFlock.MaxSpeed();} + void SetMaxSpeed(float speed) {fFlock.SetMaxSpeed(speed);} + float MinSpeed() const {return fFlock.MinSpeed();} + void SetMinSpeed(float minSpeed) {fFlock.SetMinSpeed(minSpeed);} + + hsBool RandomizeAnimStart() const {return fRandomizeAnimationStart;} + void SetRandomizeAnimStart(hsBool val) {fRandomizeAnimationStart = val;} + hsBool UseTargetRotation() const {return fUseTargetRotation;} + void SetUseTargetRotation(hsBool val) {fUseTargetRotation = val;} + +protected: + const static int fFileVersion; // so we don't have to update the global version number when we change + + pfFlock fFlock; + int fNumBoids; + plKey fBoidKey; + + hsBool fUseTargetRotation; + hsBool fRandomizeAnimationStart; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plAnimDebugList.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plAnimDebugList.cpp new file mode 100644 index 00000000..b7c3de2f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plAnimDebugList.cpp @@ -0,0 +1,149 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStlUtils.h" +#include "hsResMgr.h" +#include "hsTemplates.h" +#include "hsTimer.h" +#include "plAnimDebugList.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerAnimation.h" +#include "../plAvatar/plAGMasterMod.h" +#include "../plAvatar/plAGAnimInstance.h" +#include "../plAvatar/plAGAnim.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plPipeline/plDebugText.h" + +void plAnimDebugList::AddObjects(char *subString) +{ + std::vector keys; + std::vector::iterator i; + + plKeyFinder::Instance().ReallyStupidSubstringSearch(subString, hsGMaterial::Index(), keys); + for (i = keys.begin(); i != keys.end(); i++) + { + if (fMaterialKeys.Find((*i)) == fMaterialKeys.kMissingIndex) + fMaterialKeys.Append((*i)); + } + + keys.clear(); + plKeyFinder::Instance().ReallyStupidSubstringSearch(subString, plSceneObject::Index(), keys); + for (i = keys.begin(); i != keys.end(); i++) + { + plSceneObject *so = plSceneObject::ConvertNoRef((*i)->ObjectIsLoaded()); + if (so) + { + const plAGMasterMod *agMod = plAGMasterMod::ConvertNoRef(so->GetModifierByType(plAGMasterMod::Index())); + if (agMod && fSOKeys.Find(so->GetKey()) == fSOKeys.kMissingIndex) + fSOKeys.Append(so->GetKey()); + } + } +} + +void plAnimDebugList::RemoveObjects(char *subString) +{ + int i; + for (i = fMaterialKeys.GetCount() - 1; i >= 0; i--) + { + if (strstr(fMaterialKeys[i]->GetName(), subString)) + fMaterialKeys.Remove(i); + } + + for (i = fSOKeys.GetCount() - 1; i >= 0; i--) + { + if (strstr(fSOKeys[i]->GetName(), subString)) + fSOKeys.Remove(i); + } +} + +void plAnimDebugList::ShowReport() +{ + if (!fEnabled) + return; + + plDebugText &txt = plDebugText::Instance(); + + int y,x,i,j; + const int yOff=10, startY=40, startX=10; + char str[256]; + + x = startX; + y = startY; + txt.DrawString(x, y, "Material Animations:", 255, 255, 255, 255, plDebugText::kStyleBold); + y += yOff; + for (i = 0; i < fMaterialKeys.GetCount(); i++) + { + hsGMaterial *mat = hsGMaterial::ConvertNoRef(fMaterialKeys[i]->ObjectIsLoaded()); + if (!mat) + continue; + + for (j = 0; j < mat->GetNumLayers(); j++) + { + plLayerInterface *layer = mat->GetLayer(j)->BottomOfStack(); + while (layer != nil) + { + plLayerAnimation *layerAnim = plLayerAnimation::ConvertNoRef(layer); + if (layerAnim) + { + sprintf(str, "%s: %s %.3f (%.3f)", mat->GetKeyName(), layerAnim->GetKeyName(), + layerAnim->GetTimeConvert().CurrentAnimTime(), + layerAnim->GetTimeConvert().WorldToAnimTimeNoUpdate(hsTimer::GetSysSeconds())); + txt.DrawString(x, y, str); + y += yOff; + } + layer = layer->GetOverLay(); + } + } + } + y += yOff; + txt.DrawString(x, y, "AGMaster Anims", 255, 255, 255, 255, plDebugText::kStyleBold); + y += yOff; + + for (i = 0; i < fSOKeys.GetCount(); i++) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fSOKeys[i]->ObjectIsLoaded()); + if (!so) + continue; + + plAGMasterMod *mod = const_cast(plAGMasterMod::ConvertNoRef(so->GetModifierByType(plAGMasterMod::Index()))); + if (!mod) + continue; + + sprintf(str, " %s", so->GetKeyName()); + txt.DrawString(x, y, str); + y += yOff; + + for (j = 0; j < mod->GetNumATCAnimations(); j++) + { + plAGAnimInstance *anim = mod->GetATCAnimInstance(j); + sprintf(str, " %s: %.3f (%.3f)", anim->GetAnimation()->GetName(), + anim->GetTimeConvert()->CurrentAnimTime(), + anim->GetTimeConvert()->WorldToAnimTimeNoUpdate(hsTimer::GetSysSeconds())); + txt.DrawString(x, y, str); + y += yOff; + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plAnimDebugList.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plAnimDebugList.h new file mode 100644 index 00000000..a73d233b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plAnimDebugList.h @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAnimDebugList_inc +#define plAnimDebugList_inc + +// Simple debugging tool, everything is public +// This class collects a list of keyed objects that deal with +// animation, to report info on them when requested. +class plAnimDebugList +{ +public: + hsBool fEnabled; + hsTArray fSOKeys; + hsTArray fMaterialKeys; + + plAnimDebugList() : fEnabled(false) {} + ~plAnimDebugList() {} + + void AddObjects(char *subString); + void RemoveObjects(char *subString); + void ShowReport(); +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plBlower.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plBlower.cpp new file mode 100644 index 00000000..8b8c3a07 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plBlower.cpp @@ -0,0 +1,224 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plBlower.h" +#include "plgDispatch.h" +#include "hsFastMath.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnMessage/plTimeMsg.h" +#include "hsTimer.h" + +plRandom plBlower::fRandom; + +static const hsScalar kDefaultMasterPower = 20.f; +static const hsScalar kDefaultMasterFrequency = 2.f; +static const hsScalar kDefaultDirectRate = 1.f; +static const hsScalar kDefaultImpulseRate = 1.e2f; +static const hsScalar kDefaultSpringKonst = 20.f; +static const hsScalar kDefaultBias = 0.25f; +static const hsScalar kInitialMaxOffDist = 1.f; + +plBlower::plBlower() +: + fMasterPower(kDefaultMasterPower), + fMasterFrequency(kDefaultMasterFrequency), + fDirectRate(kDefaultDirectRate), + fImpulseRate(kDefaultImpulseRate), + fSpringKonst(kDefaultSpringKonst), + fBias(kDefaultBias), + fMaxOffsetDist(kInitialMaxOffDist), + fAccumTime(0) +{ + fRestPos.Set(0,0,0); + fLocalRestPos.Set(0,0,0); + fCurrDel.Set(0,0,0); + + fDirection.Set(fRandom.RandMinusOneToOne(), fRandom.RandMinusOneToOne(), 0); + hsFastMath::NormalizeAppr(fDirection); +} + +plBlower::~plBlower() +{ +} + +void plBlower::IBlow(double secs, hsScalar delSecs) +{ + hsPoint3 worldPos = fTarget->GetLocalToWorld().GetTranslate(); + hsPoint3 localPos = fTarget->GetLocalToParent().GetTranslate(); + + // fast oscillation vs slow + + // Completely random walk in the rotation + + // Strength = Strength + rnd01 * (MaxStrength - Strength) + + hsScalar t = (fAccumTime += delSecs); + + hsScalar strength = 0; + int i; + for( i = 0; i < fOscillators.GetCount(); i++ ) + { + hsScalar c, s; + t *= fOscillators[i].fFrequency * fMasterFrequency; + t += fOscillators[i].fPhase; + hsFastMath::SinCosAppr(t, s, c); + c += fBias; + strength += c * fOscillators[i].fPower; + } + strength *= fMasterPower; + + if( strength < 0 ) + strength = 0; + + fDirection.fX += fRandom.RandMinusOneToOne() * delSecs * fDirectRate; + fDirection.fY += fRandom.RandMinusOneToOne() * delSecs * fDirectRate; + + hsFastMath::NormalizeAppr(fDirection); + + hsScalar offDist = hsVector3(&fRestPos, &worldPos).Magnitude(); + if( offDist > fMaxOffsetDist ) + fMaxOffsetDist = offDist; + + hsVector3 force = fDirection * strength; + + static hsScalar kOffsetDistFrac = 0.5f; // make me const + if( offDist > fMaxOffsetDist * kOffsetDistFrac ) + { + offDist /= fMaxOffsetDist; + offDist *= fMasterPower; + + hsScalar impulse = offDist * delSecs * fImpulseRate; + + force.fX += impulse * fRandom.RandMinusOneToOne(); + force.fY += impulse * fRandom.RandMinusOneToOne(); + force.fZ += impulse * fRandom.RandMinusOneToOne(); + } + const hsScalar kOffsetDistDecay = 0.999f; + fMaxOffsetDist *= kOffsetDistDecay; + + hsVector3 accel = force; + accel += fSpringKonst * hsVector3(&fLocalRestPos, &localPos); + + hsVector3 del = accel * (delSecs * delSecs); + + fCurrDel = del; +} + +hsBool plBlower::IEval(double secs, hsScalar delSecs, UInt32 dirty) +{ + const hsScalar kMaxDelSecs = 0.1f; + if( delSecs > kMaxDelSecs ) + delSecs = kMaxDelSecs; + IBlow(secs, delSecs); + + ISetTargetTransform(); + + return true; +} + +void plBlower::ISetTargetTransform() +{ + plCoordinateInterface* ci = IGetTargetCoordinateInterface(0); + if( ci ) + { + hsMatrix44 l2p = ci->GetLocalToParent(); + hsMatrix44 p2l = ci->GetParentToLocal(); + + hsPoint3 pos = l2p.GetTranslate(); + pos += fCurrDel; + + l2p.SetTranslate(&pos); + p2l.SetTranslate(&-pos); + + ci->SetLocalToParent(l2p, p2l); + } +} + +void plBlower::SetTarget(plSceneObject* so) +{ + plSingleModifier::SetTarget(so); + + if( fTarget ) + { + fRestPos = fTarget->GetLocalToWorld().GetTranslate(); + fLocalRestPos = fTarget->GetLocalToParent().GetTranslate(); + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + } + + IInitOscillators(); +} + +void plBlower::Read(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Read(s, mgr); + + fMasterPower = s->ReadSwapScalar(); + fDirectRate = s->ReadSwapScalar(); + fImpulseRate = s->ReadSwapScalar(); + fSpringKonst = s->ReadSwapScalar(); +} + +void plBlower::Write(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Write(s, mgr); + + s->WriteSwapScalar(fMasterPower); + s->WriteSwapScalar(fDirectRate); + s->WriteSwapScalar(fImpulseRate); + s->WriteSwapScalar(fSpringKonst); +} + +void plBlower::IInitOscillators() +{ + const hsScalar kBasePower = 5.f; + fOscillators.SetCount(5); + int i; + for( i = 0; i < fOscillators.GetCount(); i++ ) + { + hsScalar fi = hsScalar(i+1); + fOscillators[i].fFrequency = fi / hsScalarPI * fRandom.RandRangeF(0.75f, 1.25f); +// fOscillators[i].fFrequency = 1.f / hsScalarPI * fRandom.RandRangeF(0.5f, 1.5f); + fOscillators[i].fPhase = fRandom.RandZeroToOne(); + fOscillators[i].fPower = kBasePower * fRandom.RandRangeF(0.75f, 1.25f); + } +} + +void plBlower::SetConstancy(hsScalar f) +{ + if( f < 0 ) + f = 0; + else if( f > 1.f ) + f = 1.f; + + fBias = f; +} + +hsScalar plBlower::GetConstancy() const +{ + return fBias; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plBlower.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plBlower.h new file mode 100644 index 00000000..e53b0328 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plBlower.h @@ -0,0 +1,101 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plBlower_inc +#define plBlower_inc + +#include "../pnModifier/plSingleModifier.h" +#include "hsGeometry3.h" +#include "../plMath/plRandom.h" +#include "hsTemplates.h" + +class plSceneObject; +class hsStream; +class hsResMgr; + +class plBlower : public plSingleModifier +{ +protected: + class Oscillator + { + public: + hsScalar fFrequency; + hsScalar fPhase; + hsScalar fPower; + }; + static plRandom fRandom; + + // Parameters + hsScalar fMasterPower; + hsScalar fMasterFrequency; + hsScalar fDirectRate; + hsScalar fImpulseRate; + hsScalar fSpringKonst; + hsScalar fBias; + + hsScalar fAccumTime; + hsTArray fOscillators; + + // CurrentState + hsVector3 fDirection; + hsPoint3 fRestPos; + hsPoint3 fLocalRestPos; + hsVector3 fCurrDel; + hsScalar fMaxOffsetDist; + + void IInitOscillators(); + void ISetTargetTransform(); + void IBlow(double secs, hsScalar delSecs); + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); +public: + ~plBlower(); + plBlower(); + + CLASSNAME_REGISTER( plBlower ); + GETINTERFACE_ANY( plBlower, plSingleModifier ); + + virtual void SetTarget(plSceneObject* so); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void SetMasterPower(hsScalar f) { fMasterPower = f; } + void SetMasterFrequency(hsScalar f) { fMasterFrequency = f; } + void SetDirectRate(hsScalar f) { fDirectRate = f; } + void SetImpulseRate(hsScalar f) { fImpulseRate = f; } + void SetSpringKonst(hsScalar f) { fSpringKonst = f; } + void SetConstancy(hsScalar f); + + hsScalar GetMasterPower() const { return fMasterPower; } + hsScalar GetMasterFrequency() const { return fMasterFrequency; } + hsScalar GetDirectRate() const { return fDirectRate; } + hsScalar GetImpulseRate() const { return fImpulseRate; } + hsScalar GetSpringKonst() const { return fSpringKonst; } + hsScalar GetConstancy() const; +}; + +#endif // plBlower_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFilterCoordInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFilterCoordInterface.cpp new file mode 100644 index 00000000..45b3afb6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFilterCoordInterface.cpp @@ -0,0 +1,133 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plFilterCoordInterface.h" + +#include "hsMatrix44.h" +#include "hsStream.h" + +static hsMatrix44* InvTRS(const hsMatrix44& trs, hsMatrix44& inv) +{ + inv.NotIdentity(); + + hsScalar invSSq[3]; + invSSq[0] = 1.f / (trs.fMap[0][0] * trs.fMap[0][0] + trs.fMap[1][0] * trs.fMap[1][0] + trs.fMap[2][0] * trs.fMap[2][0]); + invSSq[1] = 1.f / (trs.fMap[0][1] * trs.fMap[0][1] + trs.fMap[1][1] * trs.fMap[1][1] + trs.fMap[2][1] * trs.fMap[2][1]); + invSSq[2] = 1.f / (trs.fMap[0][2] * trs.fMap[0][2] + trs.fMap[1][2] * trs.fMap[1][2] + trs.fMap[2][2] * trs.fMap[2][2]); + + inv.fMap[0][0] = invSSq[0] * trs.fMap[0][0]; + inv.fMap[0][1] = invSSq[0] * trs.fMap[1][0]; + inv.fMap[0][2] = invSSq[0] * trs.fMap[2][0]; + + inv.fMap[0][3] = -(inv.fMap[0][0] * trs.fMap[0][3] + inv.fMap[0][1] * trs.fMap[1][3] + inv.fMap[0][2] * trs.fMap[2][3]); + + inv.fMap[1][0] = invSSq[1] * trs.fMap[0][1]; + inv.fMap[1][1] = invSSq[1] * trs.fMap[1][1]; + inv.fMap[1][2] = invSSq[1] * trs.fMap[2][1]; + + inv.fMap[1][3] = -(inv.fMap[1][0] * trs.fMap[0][3] + inv.fMap[1][1] * trs.fMap[1][3] + inv.fMap[1][2] * trs.fMap[2][3]); + + inv.fMap[2][0] = invSSq[2] * trs.fMap[0][2]; + inv.fMap[2][1] = invSSq[2] * trs.fMap[1][2]; + inv.fMap[2][2] = invSSq[2] * trs.fMap[2][2]; + + inv.fMap[2][3] = -(inv.fMap[2][0] * trs.fMap[0][3] + inv.fMap[2][1] * trs.fMap[1][3] + inv.fMap[2][2] * trs.fMap[2][3]); + + inv.fMap[3][0] = inv.fMap[3][1] = inv.fMap[3][2] = 0; + inv.fMap[3][3] = 1.f; + + return &inv; +} + + +plFilterCoordInterface::plFilterCoordInterface() +: fFilterMask(kNoRotation) +{ + fRefParentLocalToWorld.Reset(); +} + +plFilterCoordInterface::~plFilterCoordInterface() +{ +} + +void plFilterCoordInterface::Read(hsStream* stream, hsResMgr* mgr) +{ + plCoordinateInterface::Read(stream, mgr); + + fFilterMask = stream->ReadSwap32(); + fRefParentLocalToWorld.Read(stream); +} + +void plFilterCoordInterface::Write(hsStream* stream, hsResMgr* mgr) +{ + plCoordinateInterface::Write(stream, mgr); + + stream->WriteSwap32(fFilterMask); + fRefParentLocalToWorld.Write(stream); +} + +void plFilterCoordInterface::IRecalcTransforms() +{ + if( !(fFilterMask && fParent) ) + { + plCoordinateInterface::IRecalcTransforms(); + return; + } + + hsMatrix44 origL2W = fRefParentLocalToWorld * fLocalToParent; + fLocalToWorld = fParent->GetLocalToWorld() * fLocalToParent; + + // Filter out the stuff we're discarding. Nothing fancy here, + // we're taking the simple (and fast) form and just stuffing in + // what we want to preserve based on our reference matrix. + if( fFilterMask & kNoTransX ) + { + fLocalToWorld.fMap[0][3] = origL2W.fMap[0][3]; + } + if( fFilterMask & kNoTransY ) + { + fLocalToWorld.fMap[1][3] = origL2W.fMap[1][3]; + } + if( fFilterMask & kNoTransZ ) + { + fLocalToWorld.fMap[2][3] = origL2W.fMap[2][3]; + } + if( fFilterMask & kNoRotation ) + { + int i; + for( i = 0; i < 3; i++ ) + { + int j; + for( j = 0; j < 3; j++ ) + { + fLocalToWorld.fMap[i][j] = origL2W.fMap[i][j]; + } + } + } + // Construct the inverse of local to world for world to local. + InvTRS(fLocalToWorld, fWorldToLocal); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFilterCoordInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFilterCoordInterface.h new file mode 100644 index 00000000..424b5c27 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFilterCoordInterface.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plFilterCoordInterface_inc +#define plFilterCoordInterface_inc + +#include "../pnSceneObject/plCoordinateInterface.h" + +class plFilterCoordInterface : public plCoordinateInterface +{ +public: + enum + { + kNoRotation = 0x1, + kNoTransX = 0x2, + kNoTransY = 0x4, + kNoTransZ = 0x8, + kNoMove = kNoTransX | kNoTransY | kNoTransZ, + kNoNothing = kNoRotation | kNoMove + }; +protected: + UInt32 fFilterMask; + hsMatrix44 fRefParentLocalToWorld; + + virtual void IRecalcTransforms(); +public: + plFilterCoordInterface(); + ~plFilterCoordInterface(); + + CLASSNAME_REGISTER( plFilterCoordInterface ); + GETINTERFACE_ANY( plFilterCoordInterface, plCoordinateInterface ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + + void SetFilterMask(UInt32 f) { fFilterMask = f; } + UInt32 GetFilterMask() const { return fFilterMask; } + + void SetRefLocalToWorld(const hsMatrix44& m) { fRefParentLocalToWorld = m; } + const hsMatrix44& GetRefLocalToWorld() const { return fRefParentLocalToWorld; } + +}; + +#endif // plFilterCoordInterface_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFollowMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFollowMod.cpp new file mode 100644 index 00000000..8e925da5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFollowMod.cpp @@ -0,0 +1,282 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plFollowMod.h" +#include "plgDispatch.h" +#include "../pnNetCommon/plNetApp.h" +#include "../plMessage/plListenerMsg.h" +#include "../plMessage/plRenderMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plRefMsg.h" +#include "hsResMgr.h" +#include "plPipeline.h" + +plFollowMod::plFollowMod() +: fLeader(nil), fMode(kPosition), fLeaderType(kLocalPlayer), fLeaderSet(false) +{ +} + +plFollowMod::~plFollowMod() +{ +} + +#include "plProfile.h" +plProfile_CreateTimer("FollowMod", "RenderSetup", FollowMod); + +hsBool plFollowMod::MsgReceive(plMessage* msg) +{ + plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg); + if( rend ) + { + plProfile_BeginLap(FollowMod, this->GetKey()->GetUoid().GetObjectName()); + fLeaderL2W = rend->Pipeline()->GetCameraToWorld(); + fLeaderW2L = rend->Pipeline()->GetWorldToCamera(); + fLeaderSet = true; + plProfile_EndLap(FollowMod, this->GetKey()->GetUoid().GetObjectName()); + return true; + } + plListenerMsg* list = plListenerMsg::ConvertNoRef(msg); + if( list ) + { + hsVector3 pos; + pos.Set(list->GetPosition().fX, list->GetPosition().fY, list->GetPosition().fZ); + fLeaderL2W.MakeTranslateMat(&pos); + fLeaderW2L.MakeTranslateMat(&-pos); + fLeaderSet = true; + + return true; + } + + plGenRefMsg* ref = plGenRefMsg::ConvertNoRef(msg); + if( ref ) + { + switch( ref->fType ) + { + case kRefLeader: + + if( ref->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fLeader = plSceneObject::ConvertNoRef(ref->GetRef()); + else if( ref->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + fLeader = nil; + return true; + default: + hsAssert(false, "Unknown ref type to FollowMod"); + break; + } + } + + return plSingleModifier::MsgReceive(msg); +} + +hsBool plFollowMod::ICheckLeader() +{ + switch( fLeaderType ) + { + case kLocalPlayer: + { + plSceneObject* player = plSceneObject::ConvertNoRef(plNetClientApp::GetInstance()->GetLocalPlayer()); + if( player ) + { + fLeaderL2W = player->GetLocalToWorld(); + fLeaderW2L = player->GetWorldToLocal(); + fLeaderSet = true; + } + else + fLeaderSet = false; + } + break; + case kObject: + if( fLeader ) + { + fLeaderL2W = fLeader->GetLocalToWorld(); + fLeaderW2L = fLeader->GetWorldToLocal(); + fLeaderSet = true; + } + else + fLeaderSet = false; + break; + case kCamera: + break; + case kListener: + break; + } + return fLeaderSet; +} + +void plFollowMod::IMoveTarget() +{ + if( fMode == kFullTransform ) + { + GetTarget()->SetTransform(fLeaderL2W, fLeaderW2L); + return; + } + + hsMatrix44 l2w = GetTarget()->GetLocalToWorld(); + hsMatrix44 w2l = GetTarget()->GetWorldToLocal(); + + if( fMode & kRotate ) + { + int i, j; + for( i = 0; i < 3; i++ ) + { + for( j = 0; j < 3; j++ ) + { + l2w.fMap[i][j] = fLeaderL2W.fMap[i][j]; + w2l.fMap[i][j] = fLeaderW2L.fMap[i][j]; + } + } + } + + if( fMode & kPosition ) + { + hsMatrix44 invMove; + invMove.Reset(); + + hsPoint3 newPos = fLeaderL2W.GetTranslate(); + hsPoint3 newInvPos = fLeaderW2L.GetTranslate(); + + hsPoint3 oldPos = l2w.GetTranslate(); + + // l2w = newPosMat * -oldPosMat * l2w + // so w2l = w2l * inv-oldPosMat * invNewPosMat + if( fMode & kPositionX ) + { + l2w.fMap[0][3] = newPos.fX; + invMove.fMap[0][3] = oldPos.fX - newPos.fX; + } + + if( fMode & kPositionY ) + { + l2w.fMap[1][3] = newPos.fY; + invMove.fMap[1][3] = oldPos.fY - newPos.fY; + } + + if( fMode & kPositionZ ) + { + l2w.fMap[2][3] = newPos.fZ; + invMove.fMap[2][3] = oldPos.fZ - newPos.fZ; + } + + invMove.NotIdentity(); + + // InvMove must happen after rotation. + w2l = w2l * invMove; + + } + + l2w.NotIdentity(); + w2l.NotIdentity(); + +#ifdef HS_DEBUGGING + //MFHORSE hackola + hsMatrix44 inv; + l2w.GetInverse(&inv); +#endif // HS_DEBUGGING + + GetTarget()->SetTransform(l2w, w2l); +} + +hsBool plFollowMod::IEval(double secs, hsScalar del, UInt32 dirty) +{ + if( ICheckLeader() ) + IMoveTarget(); + return true; +} + +void plFollowMod::SetTarget(plSceneObject* so) +{ + plSingleModifier::SetTarget(so); + if( fTarget ) + Activate(); + else + Deactivate(); +} + +void plFollowMod::Activate() +{ + switch( fLeaderType ) + { + case kLocalPlayer: + break; + case kObject: + break; + case kCamera: + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + break; + case kListener: + plgDispatch::Dispatch()->RegisterForExactType(plListenerMsg::Index(), GetKey()); + break; + } + if( fTarget ) + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +void plFollowMod::Deactivate() +{ + switch( fLeaderType ) + { + case kLocalPlayer: + if( fTarget ) + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + break; + case kObject: + if( fTarget ) + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + break; + case kCamera: + plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); + break; + case kListener: + plgDispatch::Dispatch()->UnRegisterForExactType(plListenerMsg::Index(), GetKey()); + break; + } +} + +void plFollowMod::Read(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Read(stream, mgr); + + fLeaderType = FollowLeaderType(stream->ReadByte()); + fMode = stream->ReadByte(); + + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefLeader), plRefFlags::kActiveRef); + + // If active? + Activate(); +} + +void plFollowMod::Write(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Write(stream, mgr); + + stream->WriteByte(fLeaderType); + stream->WriteByte(fMode); + + mgr->WriteKey(stream, fLeader); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFollowMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFollowMod.h new file mode 100644 index 00000000..a302299d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plFollowMod.h @@ -0,0 +1,101 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plFollowMod_inc +#define plFollowMod_inc + +#include "hsMatrix44.h" +#include "../pnModifier/plSingleModifier.h" + +class plSceneObject; +class plMessage; +class hsStream; +class hsResMgr; + +class plFollowMod : public plSingleModifier +{ +public: + enum FollowRefs + { + kRefLeader + }; + enum FollowLeaderType + { + kLocalPlayer, + kObject, + kCamera, + kListener + }; + enum FollowModMode + { + kPositionX = 0x1, + kPositionY = 0x2, + kPositionZ = 0x4, + kPosition = (kPositionX | kPositionY | kPositionZ), + kRotate = 0x8, + kFullTransform = kPosition | kRotate + }; +protected: + FollowLeaderType fLeaderType; + UInt8 fMode; + UInt8 fLeaderSet; + + plSceneObject* fLeader; // may be nil if Leader isn't a sceneobject + + hsMatrix44 fLeaderL2W; + hsMatrix44 fLeaderW2L; + + hsBool ICheckLeader(); + void IMoveTarget(); + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + +public: + plFollowMod(); + ~plFollowMod(); + + CLASSNAME_REGISTER( plFollowMod ); + GETINTERFACE_ANY( plFollowMod, plSingleModifier ); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual void SetTarget(plSceneObject* so); + + void SetType(FollowLeaderType t) { fLeaderType = t; } + FollowLeaderType GetType() const { return fLeaderType; } + + void SetMode(UInt8 m) { fMode = m; } + UInt8 GetMode() const { return fMode; } + + void Activate(); + void Deactivate(); + +}; + +#endif // plFollowMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLightModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLightModifier.cpp new file mode 100644 index 00000000..468dc881 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLightModifier.cpp @@ -0,0 +1,413 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLightModifier.h" +#include "../plGLight/plLightInfo.h" +#include "../plInterp/plController.h" + +#include "hsStream.h" +#include "hsResMgr.h" + +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// +// Basic light +plLightModifier::plLightModifier() +: fLight(nil), + fColorCtl(nil), + fAmbientCtl(nil), + fSpecularCtl(nil) +{ +} + +plLightModifier::~plLightModifier() +{ + delete fColorCtl; + fColorCtl = nil; + delete fAmbientCtl; + fAmbientCtl = nil; + delete fSpecularCtl; + fSpecularCtl = nil; +} + +void plLightModifier::IClearCtls() +{ + delete fColorCtl; + fColorCtl = nil; + delete fAmbientCtl; + fAmbientCtl = nil; + delete fSpecularCtl; + fSpecularCtl = nil; +} + +void plLightModifier::AddTarget(plSceneObject* so) +{ + plSimpleModifier::AddTarget(so); + if( so ) + fLight = plLightInfo::ConvertNoRef(so->GetGenericInterface(plLightInfo::Index())); + else + fLight = nil; +} + +void plLightModifier::RemoveTarget(plSceneObject* so) +{ + if( so = fTarget ) + fLight = nil; + plSimpleModifier::RemoveTarget(so); +} + +void plLightModifier::Read(hsStream* s, hsResMgr* mgr) +{ + plSimpleModifier::Read(s, mgr); + + fColorCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fAmbientCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fSpecularCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); +} + +void plLightModifier::Write(hsStream* s, hsResMgr* mgr) +{ + plSimpleModifier::Write(s, mgr); + + mgr->WriteCreatable(s, fColorCtl); + mgr->WriteCreatable(s, fAmbientCtl); + mgr->WriteCreatable(s, fSpecularCtl); +} + +void plLightModifier::IApplyDynamic() +{ + hsColorRGBA col; + if( fLight != nil ) + { + if( fColorCtl ) + { + col.Set(0,0,0,1.f); + fColorCtl->Interp(fCurrentTime, &col); + fLight->SetDiffuse(col); + } + if( fAmbientCtl ) + { + col.Set(0,0,0,1.f); + fAmbientCtl->Interp(fCurrentTime, &col); + fLight->SetAmbient(col); + } + if( fSpecularCtl ) + { + col.Set(0,0,0,1.f); + fSpecularCtl->Interp(fCurrentTime, &col); + fLight->SetSpecular(col); + } + } +} + +void plLightModifier::DefaultAnimation() +{ + hsScalar len = MaxAnimLength(0); + fTimeConvert.SetBegin(0); + fTimeConvert.SetEnd(len); + fTimeConvert.SetLoopPoints(0, len); + fTimeConvert.Loop(); + fTimeConvert.Start(); +} + +hsScalar plLightModifier::MaxAnimLength(hsScalar len) const +{ + if( fColorCtl && (fColorCtl->GetLength() > len) ) + len = fColorCtl->GetLength(); + if( fAmbientCtl && (fAmbientCtl->GetLength() > len) ) + len = fAmbientCtl->GetLength(); + if( fSpecularCtl && (fSpecularCtl->GetLength() > len) ) + len = fSpecularCtl->GetLength(); + return len; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// +// Omni Lights +plOmniModifier::plOmniModifier() +: fOmni(nil), + fAttenCtl(nil) +{ +} + +plOmniModifier::~plOmniModifier() +{ + delete fAttenCtl; + fAttenCtl = nil; +} + +void plOmniModifier::AddTarget(plSceneObject* so) +{ + plLightModifier::AddTarget(so); + if( fLight ) + fOmni = plOmniLightInfo::ConvertNoRef(fLight); + else + fOmni = nil; +} + +void plOmniModifier::RemoveTarget(plSceneObject* so) +{ + plLightModifier::RemoveTarget(so); + if( !fLight ) + fOmni = nil; +} + +void plOmniModifier::IClearCtls() +{ + plLightModifier::IClearCtls(); + + delete fAttenCtl; + fAttenCtl = nil; +} + +void plOmniModifier::Read(hsStream* s, hsResMgr* mgr) +{ + plLightModifier::Read(s, mgr); + + fAttenCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fInitAtten.Read(s); +} + +void plOmniModifier::Write(hsStream* s, hsResMgr* mgr) +{ + plLightModifier::Write(s, mgr); + + mgr->WriteCreatable(s, fAttenCtl); + fInitAtten.Write(s); +} + +void plOmniModifier::IApplyDynamic() +{ + plLightModifier::IApplyDynamic(); + + if( fAttenCtl ) + { + hsPoint3 p = fInitAtten; + fAttenCtl->Interp(fCurrentTime, &p); + fOmni->SetConstantAttenuation(p.fX); + fOmni->SetLinearAttenuation(p.fY); + fOmni->SetQuadraticAttenuation(p.fZ); + } +} + +hsScalar plOmniModifier::MaxAnimLength(hsScalar len) const +{ + len = plLightModifier::MaxAnimLength(len); + if( fAttenCtl && (fAttenCtl->GetLength() > len) ) + len = fAttenCtl->GetLength(); + + return len; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// +// Spot Lights +plSpotModifier::plSpotModifier() +: fSpot(nil), + fInnerCtl(nil), + fOuterCtl(nil) +{ +} + +plSpotModifier::~plSpotModifier() +{ + delete fInnerCtl; + fInnerCtl = nil; + delete fOuterCtl; + fOuterCtl = nil; +} + +void plSpotModifier::AddTarget(plSceneObject* so) +{ + plOmniModifier::AddTarget(so); + if( fLight ) + fSpot = plSpotLightInfo::ConvertNoRef(fLight); + else + fSpot = nil; +} + +void plSpotModifier::RemoveTarget(plSceneObject* so) +{ + plOmniModifier::RemoveTarget(so); + if( !fLight ) + fSpot = nil; +} + +void plSpotModifier::IClearCtls() +{ + plOmniModifier::IClearCtls(); + + delete fInnerCtl; + fInnerCtl = nil; + delete fOuterCtl; + fOuterCtl = nil; +} + +void plSpotModifier::Read(hsStream* s, hsResMgr* mgr) +{ + plOmniModifier::Read(s, mgr); + + fInnerCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fOuterCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); +} + +void plSpotModifier::Write(hsStream* s, hsResMgr* mgr) +{ + plOmniModifier::Write(s, mgr); + + mgr->WriteCreatable(s, fInnerCtl); + mgr->WriteCreatable(s, fOuterCtl); +} + +void plSpotModifier::IApplyDynamic() +{ + plOmniModifier::IApplyDynamic(); + + hsScalar f; + if( fInnerCtl ) + { + fInnerCtl->Interp(fCurrentTime, &f); + fSpot->SetSpotInner(hsScalarDegToRad(f)*0.5f); + } + if( fOuterCtl ) + { + fOuterCtl->Interp(fCurrentTime, &f); + fSpot->SetSpotOuter(hsScalarDegToRad(f)*0.5f); + } +} + +hsScalar plSpotModifier::MaxAnimLength(hsScalar len) const +{ + len = plOmniModifier::MaxAnimLength(len); + if( fInnerCtl && (fInnerCtl->GetLength() > len) ) + len = fInnerCtl->GetLength(); + if( fOuterCtl && (fOuterCtl->GetLength() > len) ) + len = fOuterCtl->GetLength(); + + return len; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////// +// LtdDir Lights +plLtdDirModifier::plLtdDirModifier() +: fLtdDir(nil), + fWidthCtl(nil), + fHeightCtl(nil), + fDepthCtl(nil) +{ +} + +plLtdDirModifier::~plLtdDirModifier() +{ + delete fWidthCtl; + fWidthCtl = nil; + delete fHeightCtl; + fHeightCtl = nil; + delete fDepthCtl; + fDepthCtl = nil; +} + +void plLtdDirModifier::AddTarget(plSceneObject* so) +{ + plLightModifier::AddTarget(so); + if( fLight ) + fLtdDir = plLimitedDirLightInfo::ConvertNoRef(fLight); + else + fLtdDir = nil; +} + +void plLtdDirModifier::RemoveTarget(plSceneObject* so) +{ + plLightModifier::RemoveTarget(so); + if( !fLight ) + fLtdDir = nil; +} + +void plLtdDirModifier::IClearCtls() +{ + plLightModifier::IClearCtls(); + + delete fWidthCtl; + fWidthCtl = nil; + delete fHeightCtl; + fHeightCtl = nil; + delete fDepthCtl; + fDepthCtl = nil; +} + +void plLtdDirModifier::Read(hsStream* s, hsResMgr* mgr) +{ + plLightModifier::Read(s, mgr); + + fWidthCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fHeightCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fDepthCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); +} + +void plLtdDirModifier::Write(hsStream* s, hsResMgr* mgr) +{ + plLightModifier::Write(s, mgr); + + mgr->WriteCreatable(s, fWidthCtl); + mgr->WriteCreatable(s, fHeightCtl); + mgr->WriteCreatable(s, fDepthCtl); +} + +void plLtdDirModifier::IApplyDynamic() +{ + plLightModifier::IApplyDynamic(); + + hsScalar f; + if( fWidthCtl ) + { + fWidthCtl->Interp(fCurrentTime, &f); + fLtdDir->SetWidth(f); + } + if( fHeightCtl ) + { + fHeightCtl->Interp(fCurrentTime, &f); + fLtdDir->SetHeight(f); + } + if( fDepthCtl ) + { + fDepthCtl->Interp(fCurrentTime, &f); + fLtdDir->SetDepth(f); + } +} + +hsScalar plLtdDirModifier::MaxAnimLength(hsScalar len) const +{ + len = plLightModifier::MaxAnimLength(len); + if( fWidthCtl && (fWidthCtl->GetLength() > len) ) + len = fWidthCtl->GetLength(); + if( fHeightCtl && (fHeightCtl->GetLength() > len) ) + len = fHeightCtl->GetLength(); + if( fDepthCtl && (fDepthCtl->GetLength() > len) ) + len = fDepthCtl->GetLength(); + + return len; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLightModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLightModifier.h new file mode 100644 index 00000000..cee90893 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLightModifier.h @@ -0,0 +1,181 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLightModifier_inc +#define plLightModifier_inc + +#include "../../PubUtilLib/plModifier/plSimpleModifier.h" +#include "hsGeometry3.h" + +class plController; +class plController; +class plLightInfo; +class plOmniLightInfo; +class plSpotLightInfo; +class plLimitedDirLightInfo; + +class plLightModifier : public plSimpleModifier +{ +protected: + + plController* fColorCtl; + plController* fAmbientCtl; + plController* fSpecularCtl; + + plLightInfo* fLight; + + virtual void IApplyDynamic(); + + virtual void IClearCtls(); +public: + plLightModifier(); + virtual ~plLightModifier(); + + CLASSNAME_REGISTER( plLightModifier ); + GETINTERFACE_ANY( plLightModifier, plSimpleModifier ); + + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool HasAnima() const { return fColorCtl || fAmbientCtl || fSpecularCtl; } + + // Export only + void SetColorCtl(plController* ctl) { fColorCtl = ctl; } + void SetAmbientCtl(plController* ctl) { fAmbientCtl = ctl; } + void SetSpecularCtl(plController* ctl) { fSpecularCtl = ctl; } + + virtual void DefaultAnimation(); + virtual hsScalar MaxAnimLength(hsScalar len) const; +}; + +class plOmniModifier : public plLightModifier +{ +protected: + + plOmniLightInfo* fOmni; + + plController* fAttenCtl; + hsPoint3 fInitAtten; + + virtual void IApplyDynamic(); + + virtual void IClearCtls(); +public: + plOmniModifier(); + virtual ~plOmniModifier(); + + CLASSNAME_REGISTER( plOmniModifier ); + GETINTERFACE_ANY( plOmniModifier, plLightModifier ); + + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool HasAnima() const { return plLightModifier::HasAnima() || fAttenCtl; } + + // Export Only + void SetAttenCtl(plController* ctl) { fAttenCtl = ctl; } + void SetInitAtten(const hsPoint3& p) { fInitAtten = p; } + + virtual hsScalar MaxAnimLength(hsScalar len) const; +}; + +class plSpotModifier : public plOmniModifier +{ +protected: + + plSpotLightInfo* fSpot; + + plController* fInnerCtl; + plController* fOuterCtl; + + virtual void IApplyDynamic(); + + virtual void IClearCtls(); +public: + plSpotModifier(); + virtual ~plSpotModifier(); + + CLASSNAME_REGISTER( plSpotModifier ); + GETINTERFACE_ANY( plSpotModifier, plLightModifier ); + + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool HasAnima() const { return plOmniModifier::HasAnima() || fInnerCtl || fOuterCtl; } + + // Export Only + void SetInnerCtl(plController* ctl) { fInnerCtl = ctl; } + void SetOuterCtl(plController* ctl) { fOuterCtl = ctl; } + + virtual hsScalar MaxAnimLength(hsScalar len) const; +}; + +class plLtdDirModifier : public plLightModifier +{ +protected: + + plLimitedDirLightInfo* fLtdDir; + + plController* fWidthCtl; + plController* fHeightCtl; + plController* fDepthCtl; + + virtual void IApplyDynamic(); + + virtual void IClearCtls(); +public: + plLtdDirModifier(); + virtual ~plLtdDirModifier(); + + CLASSNAME_REGISTER( plLtdDirModifier ); + GETINTERFACE_ANY( plLtdDirModifier, plLightModifier ); + + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool HasAnima() const { return plLightModifier::HasAnima() || fWidthCtl || fHeightCtl || fDepthCtl; } + + // Export Only + void SetWidthCtl(plController* ctl) { fWidthCtl = ctl; } + void SetHeightCtl(plController* ctl) { fHeightCtl = ctl; } + void SetDepthCtl(plController* ctl) { fDepthCtl = ctl; } + + virtual hsScalar MaxAnimLength(hsScalar len) const; +}; + +#endif // plLightModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLineFollowMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLineFollowMod.cpp new file mode 100644 index 00000000..1f461c66 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLineFollowMod.cpp @@ -0,0 +1,687 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLineFollowMod.h" +#include "plStereizer.h" +#include "../plInterp/plAnimPath.h" +#include "hsResMgr.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "plgDispatch.h" +#include "../plMessage/plListenerMsg.h" +#include "../plMessage/plRenderMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "hsBounds.h" +#include "plPipeline.h" +#include "hsFastMath.h" +#include "../pnMessage/plPlayerPageMsg.h" +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClient/plNetClientMgr.h" +#include "hsTimer.h" + +plLineFollowMod::plLineFollowMod() +: fPath(nil), + fPathParent(nil), + fRefObj(nil), + fFollowMode(kFollowListener), + fFollowFlags(kNone), + fOffset(0), + fOffsetClamp(0), + fSpeedClamp(0) +{ + fSearchPos.Set(0,0,0); +} + +plLineFollowMod::~plLineFollowMod() +{ + delete fPath; +} + +void plLineFollowMod::SetSpeedClamp(hsScalar fps) +{ + fSpeedClamp = fps; + if( fSpeedClamp > 0 ) + { + fFollowFlags |= kSpeedClamp; + } + else + { + fFollowFlags &= ~kSpeedClamp; + } + +} + +void plLineFollowMod::SetOffsetFeet(hsScalar f) +{ + fOffset = f; + if( fOffset != 0 ) + { + fFollowFlags &= ~kOffsetAng; + fFollowFlags |= kOffsetFeet; + } + else + { + fFollowFlags &= ~kOffset; + } +} + +void plLineFollowMod::SetForceToLine(hsBool on) +{ + if( on ) + fFollowFlags |= kForceToLine; + else + fFollowFlags &= ~kForceToLine; +} + +void plLineFollowMod::SetOffsetDegrees(hsScalar f) +{ + fOffset = hsScalarDegToRad(f); + if( fOffset != 0 ) + { + fFollowFlags &= ~kOffsetFeet; + fFollowFlags |= kOffsetAng; + fTanOffset = tanf(f); + } + else + { + fFollowFlags &= ~kOffset; + } +} + +void plLineFollowMod::SetOffsetClamp(hsScalar f) +{ + fOffsetClamp = f; + if( fOffsetClamp > 0 ) + { + fFollowFlags |= kOffsetClamp; + } + else + { + fFollowFlags &= ~kOffsetClamp; + } +} + +void plLineFollowMod::SetPath(plAnimPath* path) +{ + delete fPath; + fPath = path; +} + +void plLineFollowMod::Read(hsStream* stream, hsResMgr* mgr) +{ + plMultiModifier::Read(stream, mgr); + + fPath = plAnimPath::ConvertNoRef(mgr->ReadCreatable(stream)); + + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefParent), plRefFlags::kPassiveRef); + + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefObject), plRefFlags::kPassiveRef); + + int n = stream->ReadSwap32(); + while(n--) + { + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefStereizer), plRefFlags::kPassiveRef); + } + + UInt32 f = stream->ReadSwap32(); + SetFollowMode(FollowMode(f & 0xffff)); + + fFollowFlags = (UInt16)((f >> 16) & 0xffff); + + if( fFollowFlags & kOffset ) + { + fOffset = stream->ReadSwapScalar(); + } + if( fFollowFlags & kOffsetAng ) + { + fTanOffset = tanf(fOffset); + } + if( fFollowFlags & kOffsetClamp ) + { + fOffsetClamp = stream->ReadSwapScalar(); + } + if( fFollowFlags & kSpeedClamp ) + { + fSpeedClamp = stream->ReadSwapScalar(); + } +} + +void plLineFollowMod::Write(hsStream* stream, hsResMgr* mgr) +{ + plMultiModifier::Write(stream, mgr); + + mgr->WriteCreatable(stream, fPath); + + mgr->WriteKey(stream, fPathParent); + + mgr->WriteKey(stream, fRefObj); + + stream->WriteSwap32(fStereizers.GetCount()); + int i; + for( i = 0; i < fStereizers.GetCount(); i++ ) + mgr->WriteKey(stream, fStereizers[i]->GetKey()); + + UInt32 f = UInt32(fFollowMode) | (UInt32(fFollowFlags) << 16); + stream->WriteSwap32(f); + + if( fFollowFlags & kOffset ) + stream->WriteSwapScalar(fOffset); + if( fFollowFlags & kOffsetClamp ) + stream->WriteSwapScalar(fOffsetClamp); + if( fFollowFlags & kSpeedClamp ) + stream->WriteSwapScalar(fSpeedClamp); +} + + +#include "plProfile.h" +plProfile_CreateTimer("LineFollow", "RenderSetup", LineFollow); + +hsBool plLineFollowMod::MsgReceive(plMessage* msg) +{ + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + plSceneObject* obj = plSceneObject::ConvertNoRef(refMsg->GetRef()); + if( kRefParent == refMsg->fType ) + fPathParent = obj; + else if( kRefObject == refMsg->fType ) + fRefObj = obj; + else if( kRefStereizer == refMsg->fType ) + { + plStereizer* ster = plStereizer::ConvertNoRef(refMsg->GetRef()); + int idx = fStereizers.Find(ster); + if( idx == fStereizers.kMissingIndex ) + fStereizers.Append(ster); + } + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + if( kRefParent == refMsg->fType ) + fPathParent = nil; + else if( kRefObject == refMsg->fType ) + fRefObj = nil; + else if( kRefStereizer == refMsg->fType ) + { + plStereizer* ster = (plStereizer*)(refMsg->GetRef()); + int idx = fStereizers.Find(ster); + if( idx != fStereizers.kMissingIndex ) + fStereizers.Remove(idx); + } + } + return true; + } + plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg); + if( rend ) + { + plProfile_BeginLap(LineFollow, this->GetKey()->GetUoid().GetObjectName()); + hsPoint3 oldPos = fSearchPos; + fSearchPos = rend->Pipeline()->GetViewPositionWorld(); + ICheckForPop(oldPos, fSearchPos); + plProfile_EndLap(LineFollow, this->GetKey()->GetUoid().GetObjectName()); + return true; + } + plListenerMsg* list = plListenerMsg::ConvertNoRef(msg); + if( list ) + { + hsPoint3 oldPos = fSearchPos; + fSearchPos = list->GetPosition(); + ICheckForPop(oldPos, fSearchPos); + + ISetupStereizers(list); + return true; + } + plPlayerPageMsg* pPMsg = plPlayerPageMsg::ConvertNoRef(msg); + if (pPMsg) + { + if (pPMsg->fPlayer == plNetClientMgr::GetInstance()->GetLocalPlayerKey() && !pPMsg->fUnload) + { + fRefObj = (plSceneObject*)pPMsg->fPlayer->GetObjectPtr(); + } + return true; + } + + return plMultiModifier::MsgReceive(msg); +} + +void plLineFollowMod::SetFollowMode(FollowMode f) +{ + IUnRegister(); + fFollowMode = f; + IRegister(); + + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +void plLineFollowMod::IUnRegister() +{ + switch( fFollowMode ) + { + case kFollowObject: + break; + case kFollowListener: + plgDispatch::Dispatch()->UnRegisterForExactType(plListenerMsg::Index(), GetKey()); + break; + case kFollowCamera: + plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); + break; + case kFollowLocalAvatar: + plgDispatch::Dispatch()->UnRegisterForExactType(plPlayerPageMsg::Index(), GetKey()); + break; + + } +} + +void plLineFollowMod::IRegister() +{ + switch( fFollowMode ) + { + case kFollowObject: + break; + case kFollowListener: + plgDispatch::Dispatch()->RegisterForExactType(plListenerMsg::Index(), GetKey()); + break; + case kFollowCamera: + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + break; + case kFollowLocalAvatar: + { + if (plNetClientApp::GetInstance() && plNetClientApp::GetInstance()->GetLocalPlayer()) + fRefObj = ((plSceneObject*)plNetClientApp::GetInstance()->GetLocalPlayer()); + plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); + break; + } + } +} + +hsBool plLineFollowMod::IEval(double secs, hsScalar del, UInt32 dirty) +{ + if( !fPath ) + return false; + + ISetPathTransform(); + + if( !IGetSearchPos() ) + return false; + + hsMatrix44 tgtXfm; + IGetTargetTransform(fSearchPos, tgtXfm); + + if( fFollowFlags & kOffset ) + IOffsetTargetTransform(tgtXfm); + + int i; + for( i = 0; i < GetNumTargets(); i++ ) + { + ISetTargetTransform(i, tgtXfm); + } + return true; +} + +hsBool plLineFollowMod::IOffsetTargetTransform(hsMatrix44& tgtXfm) +{ + hsPoint3 tgtPos = tgtXfm.GetTranslate(); + + hsVector3 tgt2src(&fSearchPos, &tgtPos); + hsScalar t2sLen = tgt2src.Magnitude(); + hsFastMath::NormalizeAppr(tgt2src); + + hsVector3 out; + out.Set(-tgt2src.fY, tgt2src.fX, 0); // (0,0,1) X (tgt2src) + + if( fFollowFlags & kOffsetAng ) + { + hsScalar del = t2sLen * fTanOffset; + if( fFollowFlags & kOffsetClamp ) + { + if( del > fOffsetClamp ) + del = fOffsetClamp; + else if( del < -fOffsetClamp ) + del = -fOffsetClamp; + } + out *= del; + } + else if( fFollowFlags & kOffsetFeet ) + { + out *= fOffset; + } + else + out.Set(0,0,0); + + if( fFollowFlags & kForceToLine ) + { + hsPoint3 newSearch = tgtPos; + newSearch += out; + IGetTargetTransform(newSearch, tgtXfm); + } + else + { + tgtXfm.fMap[0][3] += out[0]; + tgtXfm.fMap[1][3] += out[1]; + tgtXfm.fMap[2][3] += out[2]; + } + + return true; +} + +hsBool plLineFollowMod::IGetTargetTransform(hsPoint3& searchPos, hsMatrix44& tgtXfm) +{ + hsScalar t = fPath->GetExtremePoint(searchPos); + if( fFollowFlags & kFullMatrix ) + { + fPath->SetCurTime(t, plAnimPath::kNone); + fPath->GetMatrix44(&tgtXfm); + } + else + { + fPath->SetCurTime(t, plAnimPath::kCalcPosOnly); + hsPoint3 pos; + fPath->GetPosition(&pos); + tgtXfm.MakeTranslateMat((hsVector3*)&pos); + } + + return true; +} + +void plLineFollowMod::ISetPathTransform() +{ + if( fPathParent && fPathParent->GetCoordinateInterface() ) + { + hsMatrix44 l2w = fPathParent->GetCoordinateInterface()->GetLocalToWorld(); + hsMatrix44 w2l = fPathParent->GetCoordinateInterface()->GetWorldToLocal(); + + fPath->SetTransform(l2w, w2l); + } +} + +void plLineFollowMod::ICheckForPop(const hsPoint3& oldPos, const hsPoint3& newPos) +{ + hsVector3 del(&oldPos, &newPos); + + hsScalar elapsed = hsTimer::GetDelSysSeconds(); + hsScalar speedSq = 0.f; + if (elapsed > 0.f) + speedSq = del.MagnitudeSquared() / elapsed; + + const hsScalar kMaxSpeedSq = 30.f * 30.f; // (feet per sec)^2 + if( speedSq > kMaxSpeedSq ) + fFollowFlags |= kSearchPosPop; + else + fFollowFlags &= ~kSearchPosPop; +} + +hsBool plLineFollowMod::IGetSearchPos() +{ + hsPoint3 oldPos = fSearchPos; + if( kFollowObject == fFollowMode ) + { + if( !fRefObj ) + return false; + + if( fRefObj->GetCoordinateInterface() ) + { + fSearchPos = fRefObj->GetCoordinateInterface()->GetWorldPos(); + ICheckForPop(oldPos, fSearchPos); + return true; + } + else if( fRefObj->GetDrawInterface() ) + { + fSearchPos = fRefObj->GetDrawInterface()->GetWorldBounds().GetCenter(); + ICheckForPop(oldPos, fSearchPos); + return true; + } + return false; + } + else + if (fFollowMode == kFollowLocalAvatar) + { + if (!fRefObj) + return false; + if( fRefObj->GetCoordinateInterface() ) + { + fSearchPos = fRefObj->GetCoordinateInterface()->GetWorldPos(); + ICheckForPop(oldPos, fSearchPos); + return true; + } + else if( fRefObj->GetDrawInterface() ) + { + fSearchPos = fRefObj->GetDrawInterface()->GetWorldBounds().GetCenter(); + ICheckForPop(oldPos, fSearchPos); + return true; + } + return false; + } + return true; +} + +hsMatrix44 plLineFollowMod::IInterpMatrices(const hsMatrix44& m0, const hsMatrix44& m1, hsScalar parm) +{ + hsMatrix44 retVal; + int i, j; + for( i = 0; i < 3; i++ ) + { + for( j = 0; j < 4; j++ ) + { + retVal.fMap[i][j] = m0.fMap[i][j] * (1.f - parm) + m1.fMap[i][j] * parm; + } + } + retVal.fMap[3][0] = retVal.fMap[3][1] = retVal.fMap[3][2] = 0; + retVal.fMap[3][3] = 1.f; + retVal.NotIdentity(); + return retVal; +} + +hsMatrix44 plLineFollowMod::ISpeedClamp(plCoordinateInterface* ci, const hsMatrix44& unclTgtXfm) +{ + // If our search position has popped, or delsysseconds is zero, just return as is. + if( (fFollowFlags & kSearchPosPop) || !(hsTimer::GetDelSysSeconds() > 0) ) + return unclTgtXfm; + + const hsMatrix44 currL2W = ci->GetLocalToWorld(); + const hsPoint3 oldPos = currL2W.GetTranslate(); + const hsPoint3 newPos = unclTgtXfm.GetTranslate(); + const hsVector3 del(&newPos, &oldPos); + hsScalar elapsed = hsTimer::GetDelSysSeconds(); + hsScalar speed = 0.f; + if (elapsed > 0.f) + speed = del.Magnitude() / elapsed; + + if( speed > fSpeedClamp ) + { + hsScalar parm = fSpeedClamp / speed; + + hsMatrix44 clTgtXfm = IInterpMatrices(currL2W, unclTgtXfm, parm); + + return clTgtXfm; + } + + return unclTgtXfm; +} + +void plLineFollowMod::ISetTargetTransform(int iTarg, const hsMatrix44& unclTgtXfm) +{ + plCoordinateInterface* ci = IGetTargetCoordinateInterface(iTarg); + if( ci ) + { + hsMatrix44 tgtXfm = fFollowFlags & kSpeedClamp ? ISpeedClamp(ci, unclTgtXfm) : unclTgtXfm; + if( fFollowFlags & kFullMatrix ) + { + // This branch currently never gets taken. If it ever does, + // we should probably optimize out this GetInverse() (depending + // on how often it gets taken). + const hsMatrix44& l2w = tgtXfm; + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + + ci->SetTransform(l2w, w2l); + } + else + { + hsMatrix44 l2w = ci->GetLocalToWorld(); + hsMatrix44 w2l = ci->GetWorldToLocal(); + + hsPoint3 pos = tgtXfm.GetTranslate(); + hsPoint3 oldPos = l2w.GetTranslate(); + + l2w.SetTranslate(&pos); + + hsMatrix44 xlate; + xlate.Reset(); + xlate.SetTranslate(&oldPos); + w2l = w2l * xlate; + xlate.SetTranslate(&-pos); + w2l = w2l * xlate; + + ci->SetTransform(l2w, w2l); + } + hsPoint3 newPos = tgtXfm.GetTranslate(); + int i; + for( i = 0; i < fStereizers.GetCount(); i++ ) + { + if( fStereizers[i] ) + { + fStereizers[i]->SetWorldInitPos(newPos); + fStereizers[i]->Stereize(); + } + } + } +} + +void plLineFollowMod::ISetupStereizers(const plListenerMsg* listMsg) +{ + int i; + for( i = 0; i < fStereizers.GetCount(); i++ ) + { + if( fStereizers[i] ) + fStereizers[i]->SetFromListenerMsg(listMsg); + } +} + +void plLineFollowMod::AddTarget(plSceneObject* so) +{ + plMultiModifier::AddTarget(so); + + if( so ) + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +void plLineFollowMod::RemoveTarget(plSceneObject* so) +{ + plMultiModifier::RemoveTarget(so); +} + +void plLineFollowMod::AddStereizer(const plKey& key) +{ + hsgResMgr::ResMgr()->SendRef(plKey(key), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefStereizer), plRefFlags::kPassiveRef); +} + +void plLineFollowMod::RemoveStereizer(const plKey& key) +{ + hsgResMgr::ResMgr()->SendRef(plKey(key), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRemove, 0, kRefStereizer), plRefFlags::kPassiveRef); +} + +// derived version of this class for rail cameras +// the difference is the rail camera just calculates +// the desired position but does not move the target to +// it. + +plRailCameraMod::plRailCameraMod() : +fCurrentTime(0.0f), +fTargetTime(0.0f), +fFarthest(false) +{ + plLineFollowMod::plLineFollowMod(); + fGoal.Set(0,0,0); +} + +plRailCameraMod::~plRailCameraMod() +{ +} + +hsBool plRailCameraMod::IGetTargetTransform(hsPoint3& searchPos, hsMatrix44& tgtXfm) +{ + if (fPath->GetFarthest()) + { + fFarthest = true; + fPath->SetFarthest(false); + } + fTargetTime = fPath->GetExtremePoint(searchPos); + fPath->SetCurTime(fTargetTime, plAnimPath::kCalcPosOnly); + hsPoint3 pos; + fPath->GetPosition(&pos); + tgtXfm.MakeTranslateMat((hsVector3*)&pos); + + return true; +} + +hsPoint3 plRailCameraMod::GetGoal(double secs, hsScalar speed) +{ + hsScalar delTime; + int dir; + if (fTargetTime == fCurrentTime) + return fGoal; + + if (fTargetTime > fCurrentTime) + { + dir = 1; + delTime = fTargetTime - fCurrentTime; + } + else + { + dir = -1; + delTime = fCurrentTime - fTargetTime; + } + if (fPath->GetWrap() && delTime > fPath->GetLength() * 0.5f) + dir *= -1; + + if (delTime <= (secs * speed)) + fCurrentTime = fTargetTime; + else + fCurrentTime += (hsScalar)((secs * speed) * dir); + + if (fPath->GetWrap()) + { + if (fCurrentTime > fPath->GetLength()) + fCurrentTime = (fCurrentTime - fPath->GetLength()); + else + if (fCurrentTime < 0.0f) + fCurrentTime = fPath->GetLength() - fCurrentTime; + } + if (fFarthest) + fPath->SetCurTime((fPath->GetLength() - fCurrentTime), plAnimPath::kCalcPosOnly); + else + fPath->SetCurTime(fCurrentTime, plAnimPath::kCalcPosOnly); + + fPath->GetPosition(&fGoal); + + fPath->SetCurTime(fTargetTime, plAnimPath::kCalcPosOnly); + + return fGoal; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLineFollowMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLineFollowMod.h new file mode 100644 index 00000000..506443d6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plLineFollowMod.h @@ -0,0 +1,175 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLineFollowMod_inc +#define plLineFollowMod_inc + +#include "../pnModifier/plMultiModifier.h" +#include "hsGeometry3.h" +#include "hsMatrix44.h" + +class plAnimPath; +class plSceneObject; +class plStereizer; +class plListenerMsg; + +class plLineFollowMod : public plMultiModifier +{ +public: + enum FollowMode { + kFollowObject, + kFollowListener, + kFollowCamera, + kFollowLocalAvatar, + }; + enum RefType { + kRefParent, + kRefObject, + kRefStereizer + }; + + + enum { + kNone = 0x0, + kFullMatrix = 0x1, + kOffsetFeet = 0x2, + kOffsetAng = 0x4, + kOffset = kOffsetFeet | kOffsetAng, + kOffsetClamp = 0x8, + kForceToLine = 0x10, + kSpeedClamp = 0x20, + kSearchPosPop = 0x40 // Temp flag, gets set every time the target pops in position + }; +protected: + FollowMode fFollowMode; + UInt16 fFollowFlags; + + plAnimPath* fPath; + plSceneObject* fPathParent; + + plSceneObject* fRefObj; + + mutable hsPoint3 fSearchPos; + + hsTArray fStereizers; + + hsScalar fTanOffset; + hsScalar fOffset; + hsScalar fOffsetClamp; + hsScalar fSpeedClamp; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + + virtual hsBool IGetSearchPos(); + virtual void ISetTargetTransform(int iTarg, const hsMatrix44& tgtXfm); + virtual void ISetPathTransform(); + + virtual hsBool IGetTargetTransform(hsPoint3& searchPos, hsMatrix44& tgtXfm); + virtual hsBool IOffsetTargetTransform(hsMatrix44& tgtXfm); + virtual hsMatrix44 ISpeedClamp(plCoordinateInterface* ci, const hsMatrix44& unclTgtXfm); + hsMatrix44 IInterpMatrices(const hsMatrix44& m0, const hsMatrix44& m1, hsScalar parm); + void ICheckForPop(const hsPoint3& oldPos, const hsPoint3& newPos); + + void ISetupStereizers(const plListenerMsg* listMsg); + + void IUnRegister(); + void IRegister(); + +public: + + plLineFollowMod(); + ~plLineFollowMod(); + + CLASSNAME_REGISTER( plLineFollowMod ); + GETINTERFACE_ANY( plLineFollowMod, plMultiModifier ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + // Export time stuff + void SetPath(plAnimPath* path); + const plAnimPath* GetPath() const { return fPath; } + + void SetFollowMode(FollowMode f); + FollowMode GetFollowMode() const { return fFollowMode; } + + hsBool HasOffsetFeet() const { return 0 != (fFollowFlags & kOffsetFeet); } + hsBool HasOffsetDegrees() const { return 0 != (fFollowFlags & kOffsetAng); } + hsBool HasOffset() const { return 0 != (fFollowFlags & kOffset); } + hsBool HasOffsetClamp() const { return 0 != (fFollowFlags & kOffsetClamp); } + hsBool HasSpeedClamp() const { return 0 != (fFollowFlags & kSpeedClamp); } + + void SetOffsetFeet(hsScalar f); + hsScalar GetOffsetFeet() const { return fOffset; } + + void SetOffsetDegrees(hsScalar f); + hsScalar GetOffsetDegrees() const { return hsScalarRadToDeg(fOffset); } + + void SetOffsetClamp(hsScalar f); + hsScalar GetOffsetClamp() const { return fOffsetClamp; } + + void SetForceToLine(hsBool on); + hsBool GetForceToLine() const { return 0 != (fFollowFlags & kForceToLine); } + + void SetSpeedClamp(hsScalar feetPerSec); + hsScalar GetSpeedClamp() const { return fSpeedClamp; } + + hsBool MsgReceive(plMessage* msg); + + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + + void AddStereizer(const plKey& sterKey); + void RemoveStereizer(const plKey& sterKey); +}; + + +class plRailCameraMod : public plLineFollowMod +{ + +public: + + plRailCameraMod(); + ~plRailCameraMod(); + + CLASSNAME_REGISTER( plRailCameraMod ); + GETINTERFACE_ANY( plRailCameraMod, plLineFollowMod ); + + void Init() { fCurrentTime = -1; } // twiddle ourselves so we get ready to go... + hsPoint3 GetGoal(double secs, hsScalar speed); + +protected: + + virtual void ISetTargetTransform(int iTarg, const hsMatrix44& tgtXfm) {fDesiredMatrix = tgtXfm;} + virtual hsBool IGetTargetTransform(hsPoint3& searchPos, hsMatrix44& tgtXfm); + + hsMatrix44 fDesiredMatrix; + hsScalar fCurrentTime; + hsScalar fTargetTime; + hsPoint3 fGoal; + hsBool fFarthest; +}; +#endif // plLineFollowMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plRandomCommandMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plRandomCommandMod.cpp new file mode 100644 index 00000000..a8273d31 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plRandomCommandMod.cpp @@ -0,0 +1,290 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include + +#include "hsTypes.h" +#include "plRandomCommandMod.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "plgDispatch.h" +#include "hsTimer.h" +#include "hsUtils.h" + +static const hsScalar kRandNormalize = 1.f / 32767.f; + +plRandomCommandMod::plRandomCommandMod() +{ + fState = 0; + fMode = kNormal; + fCurrent = -1; + + fMinDelay = 0; + fMaxDelay = 0; +} + +plRandomCommandMod::~plRandomCommandMod() +{ +} + +// return how many are left to choose from +int plRandomCommandMod::IExcludeSelections(int ncmds) +{ + if( fMode & kCoverall ) + { + int nLeft = ncmds; + + fExcluded.SetBit(fCurrent); + + // Count how many haven't been played. + int i; + for( i = ncmds-1; i >= 0; --i ) + { + if( fExcluded.IsBitSet(i) ) + nLeft--; + } + + // If we're out of cmds, and OneCycle is set, + // we're out of cmds until the next play. + // Go ahead and reset for that. + // If we're out and OneCycle isn't set, then go ahead + // and set up for a new cycle. + if( !nLeft ) + { + fExcluded.Clear(); + if( fMode & kNoRepeats ) + fExcluded.SetBit(fCurrent); + + if( fMode & kOneCycle ) + return 0; + + nLeft = ncmds; + if( ( fMode & kNoRepeats ) && ncmds > 1 ) + nLeft--; + } + + return nLeft; + } + double currTime = hsTimer::GetSysSeconds(); + fExcluded.Clear(); + int i; + for( i = 0; i < fEndTimes.GetCount(); i++ ) + { + if( fEndTimes[i] > currTime ) + { + ncmds--; + fExcluded.SetBit(i); + } + } + if( fMode & kNoRepeats ) + { + ncmds--; + fExcluded.SetBit(fCurrent); + return ncmds; + } + + return ncmds; +} + +hsScalar plRandomCommandMod::IGetDelay(hsScalar len) const +{ + hsScalar r = float(hsRand() * kRandNormalize); + + hsScalar delay = fMinDelay + (fMaxDelay - fMinDelay) * r; + + if( fMode & kDelayFromEnd ) + delay += len; + + if( delay < 0 ) + delay = fmodf(len, -delay); + + return delay; +} + +hsBool plRandomCommandMod::ISelectNext(int ncmds) +{ + if( fMode & kSequential ) + { + if( ++fCurrent >= ncmds ) + { + if( fMode & kOneCycle ) + { + fCurrent = -1; + return false; + } + fCurrent = 0; + } + return true; + } + hsScalar r = float(hsRand() * kRandNormalize); + + int nSelect = ncmds; + + if( fCurrent >= 0 ) + nSelect = IExcludeSelections(ncmds); + + if( !nSelect ) + return false; + + int nth = int(r * (float(nSelect)-1.e-3f)); + + int iNext = 0; + int i; + for( i = 0; i < ncmds; i++ ) + { + if( !fExcluded.IsBitSet(i) ) + { + if( !nth-- ) + { + iNext = i; + break; + } + } + } + fCurrent = iNext; + + return true; +} + +void plRandomCommandMod::IStart() +{ + if( !IStopped() ) + return; + + fState &= ~kStopped; + IPlayNextIfMaster(); +} + +void plRandomCommandMod::IStop() +{ + fState |= kStopped; +} + +hsBool plRandomCommandMod::IStopped() const +{ + return 0 != (fState & kStopped); +} + +void plRandomCommandMod::IPlayNextIfMaster() +{ + if( !fTarget ) + IRetry(2.f); + + if( fTarget->IsLocallyOwned() == plSynchedObject::kNo ) // if this object is a proxy, it should just wait for network cmds + return; + + if( IStopped() ) + return; + + IPlayNext(); +} + +hsBool plRandomCommandMod::MsgReceive(plMessage* msg) +{ + // plAnimCmdMsg - interpret start/stop appropriately. + // could overinterpret set loop points to limit range of + // cmds we use to a window of the total set. + plAnimCmdMsg* anim = plAnimCmdMsg::ConvertNoRef(msg); + if( anim ) + { + if( anim->GetSender() != GetKey() ) + { +#if 0 + hsStatusMessageF("someone triggered me, remote=%d\n", + msg->HasBCastFlag(plMessage::kNetNonLocal)); +#endif + if( anim->Cmd(plAnimCmdMsg::kContinue) ) + IStart(); + if( anim->Cmd(plAnimCmdMsg::kStop) ) + IStop(); + if( anim->Cmd(plAnimCmdMsg::kToggleState) ) + { + if( IStopped() ) + IStart(); + else + IStop(); + } + } + else + { + +#if 0 + hsStatusMessageF("play next if master, remote=%d\n", + msg->HasBCastFlag(plMessage::kNetNonLocal)); +#endif + IPlayNextIfMaster(); + } + return true; + } + + // Don't understand, pass on to base class. + return plSingleModifier::MsgReceive(msg); +} + +void plRandomCommandMod::IReset() +{ + fCurrent = -1; + fExcluded.Clear(); + + if( !IStopped() ) + IRetry(0); +} + +void plRandomCommandMod::Read(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Read(s, mgr); + + fMode = s->ReadByte(); + fState = s->ReadByte(); + + fMinDelay = s->ReadSwapScalar(); + fMaxDelay = s->ReadSwapScalar(); + + IReset(); +} + +void plRandomCommandMod::Write(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Write(s, mgr); + + s->WriteByte(fMode); + s->WriteByte(fState); + + s->WriteSwapScalar(fMinDelay); + s->WriteSwapScalar(fMaxDelay); +} + +void plRandomCommandMod::IRetry(hsScalar secs) +{ + IStop(); + + double t = hsTimer::GetSysSeconds() + secs; + + plAnimCmdMsg* msg = TRACKED_NEW plAnimCmdMsg(nil, GetKey(), &t); + msg->SetCmd(plAnimCmdMsg::kContinue); + plgDispatch::MsgSend(msg); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plRandomCommandMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plRandomCommandMod.h new file mode 100644 index 00000000..f445c836 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plRandomCommandMod.h @@ -0,0 +1,111 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRandomCommandMod_inc +#define plRandomCommandMod_inc + +#include "../pnModifier/plSingleModifier.h" +#include "hsTemplates.h" + +class plRandomCommandMod : public plSingleModifier +{ +public: + enum { + kNormal = 0x0, // randomly select the next + kNoRepeats = 0x1, // random, but no cmd twice in a row + kCoverall = 0x2, // random, but no cmd played twice till all cmds played + kOneCycle = 0x4, // after playing through all cmds, stop + kOneCmd = 0x8, // after playing a random cmd, stop until started again. + kDelayFromEnd = 0x10, + kSequential = 0x20 + }; + + enum { + kStopped = 0x1 + }; +protected: + + // These are only lightly synched, the only synched state is whether + // they are currently active. + UInt8 fState; + + hsBitVector fExcluded; + Int8 fCurrent; + UInt8 fMode; // static, if it becomes dynamic, move to SynchedValue + hsTArray fEndTimes; + + hsScalar fMinDelay; + hsScalar fMaxDelay; + + void IStart(); + virtual void IStop(); + hsBool IStopped() const; + void IRetry(hsScalar secs); + virtual void IPlayNextIfMaster(); + + void IReset(); + + hsScalar IGetDelay(hsScalar len) const; + + int IExcludeSelections(int ncmds); + hsBool ISelectNext(int nAnim); // return false if we should stop, else set fCurrent to next index + + // Once fCurrent is set to the next animation index to play, + // IPlayNext() does whatever it takes to actually play it. + virtual void IPlayNext() = 0; + + // We only act in response to messages. + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return false; } + +public: + plRandomCommandMod(); + ~plRandomCommandMod(); + + CLASSNAME_REGISTER( plRandomCommandMod ); + GETINTERFACE_ANY( plRandomCommandMod, plSingleModifier ); + + virtual hsBool MsgReceive(plMessage* pMsg); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + // Export only + void SetMode(UInt8 m) { fMode = m; } + UInt8 GetMode() const { return fMode; } + + void SetState(UInt8 s) { fState = s; } + UInt8 GetState() const { return fState; } + + void SetMinDelay(hsScalar f) { fMinDelay = f; } + hsScalar GetMinDelay() const { return fMinDelay; } + + void SetMaxDelay(hsScalar f) { fMaxDelay = f; } + hsScalar GetMaxDelay() const { return fMaxDelay; } +}; + + +#endif // plRandomCommandMod_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plStereizer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plStereizer.cpp new file mode 100644 index 00000000..7494781d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plStereizer.cpp @@ -0,0 +1,294 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#include + +#include "hsTypes.h" + +#include "plStereizer.h" +#include "plLineFollowMod.h" + +#include "../plMessage/plListenerMsg.h" +#include "plgDispatch.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +#include "hsFastMath.h" + +#include "hsGeometry3.h" +#include "hsMatrix44.h" +#include "hsStream.h" + +plStereizer::plStereizer() +: fInitPos(0,0,0), + fListPos(0,0,0), + fListDirection(0,1.f,0), + fListUp(0,0,1.f) +{ +} + +plStereizer::~plStereizer() +{ + if( !HasMaster() ) + plgDispatch::Dispatch()->UnRegisterForExactType(plListenerMsg::Index(), GetKey()); +} + +void plStereizer::Read(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Read(stream, mgr); + + fAmbientDist = stream->ReadSwapScalar(); + fTransition = stream->ReadSwapScalar(); + + fMaxSepDist = stream->ReadSwapScalar(); + fMinSepDist = stream->ReadSwapScalar(); + + fTanAng = stream->ReadSwapScalar(); + + fInitPos.Read(stream); + + if( !HasMaster() ) + plgDispatch::Dispatch()->RegisterForExactType(plListenerMsg::Index(), GetKey()); +} + +void plStereizer::Write(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Write(stream, mgr); + + stream->WriteSwapScalar(fAmbientDist); + stream->WriteSwapScalar(fTransition); + + stream->WriteSwapScalar(fMaxSepDist); + stream->WriteSwapScalar(fMinSepDist); + + stream->WriteSwapScalar(fTanAng); + + fInitPos.Write(stream); +} + +hsBool plStereizer::MsgReceive(plMessage* msg) +{ + plListenerMsg* listenMsg = plListenerMsg::ConvertNoRef(msg); + if( listenMsg ) + { + SetFromListenerMsg(listenMsg); + return Stereize(); + } + + return plSingleModifier::MsgReceive(msg); +} + +hsBool plStereizer::IEval(double secs, hsScalar del, UInt32 dirty) +{ + return false; +} + +hsBool plStereizer::Stereize() +{ + plSceneObject* targ = GetTarget(); + if( !targ ) + return true; + + targ->FlushTransform(); + + // Find distance to listener + hsPoint3 pos = IGetUnStereoPos(); + hsVector3 posToList(&fListPos, &pos); + hsScalar dist = posToList.Magnitude(); + + // If distance less than ambient distance + // setup as pure ambient + + // Else if distance greater than ambient distance + transition + // setup as pure localized + + // Else + // Calc pure ambient position + // Calc pure localized position + // Interpolate between the two. + if( dist <= fAmbientDist ) + { + ISetNewPos(IGetAmbientPos()); + } + else if( dist >= fAmbientDist + fTransition ) + { + ISetNewPos(IGetLocalizedPos(posToList, dist)); + } + else + { + hsPoint3 ambPos = IGetAmbientPos(); + hsPoint3 localizePos = IGetLocalizedPos(posToList, dist); + + hsPoint3 newPos(ambPos); + newPos += (localizePos - ambPos) * ((dist - fAmbientDist) / fTransition); + + ISetNewPos(newPos); + } + + return true; +} + +void plStereizer::ISetNewPos(const hsPoint3& newPos) +{ + hsMatrix44 l2w = GetTarget()->GetLocalToWorld(); + hsMatrix44 w2l = GetTarget()->GetWorldToLocal(); + + l2w.NotIdentity(); + l2w.fMap[0][3] = newPos[0]; + l2w.fMap[1][3] = newPos[1]; + l2w.fMap[2][3] = newPos[2]; + + hsPoint3 invPos = -newPos; + + w2l.fMap[0][3] = ((hsVector3*)&w2l.fMap[0][0])->InnerProduct(invPos); + w2l.fMap[1][3] = ((hsVector3*)&w2l.fMap[1][0])->InnerProduct(invPos); + w2l.fMap[2][3] = ((hsVector3*)&w2l.fMap[2][0])->InnerProduct(invPos); + + IGetTargetCoordinateInterface(0)->SetTransform(l2w, w2l); +} + +void plStereizer::SetFromListenerMsg(const plListenerMsg* listMsg) +{ + fListPos = listMsg->GetPosition(); + fListDirection = listMsg->GetDirection(); + fListUp = listMsg->GetUp(); +} + +hsPoint3 plStereizer::IGetAmbientPos() const +{ + hsPoint3 pos = fListPos; + hsVector3 axOut = fListDirection % fListUp; + hsFastMath::NormalizeAppr(axOut); + if( IsLeftChannel() ) + axOut *= -fMinSepDist; + else + axOut *= fMinSepDist; + + pos += axOut; + + return pos; +} + +hsPoint3 plStereizer::IGetLocalizedPos(const hsVector3& posToList, hsScalar distToList) const +{ + hsPoint3 pos = IGetUnStereoPos(); + + hsVector3 axOut(-posToList.fY, posToList.fX, 0); + hsFastMath::NormalizeAppr(axOut); + + hsScalar distOut = distToList * fTanAng; + if( distOut > fMaxSepDist ) + distOut = fMaxSepDist; + else if( distOut < fMinSepDist ) + distOut = fMinSepDist; + if( IsLeftChannel() ) + distOut = -distOut; + + axOut *= distOut; + + pos += axOut; + + return pos; +} + +void plStereizer::SetSepAngle(hsScalar rads) +{ + fTanAng = hsScalar(tan(rads)); +} + +hsScalar plStereizer::GetSepAngle() const +{ + return atan(fTanAng); +} + +hsPoint3 plStereizer::IGetUnStereoPos() const +{ + return GetWorldInitPos(); +} + +void plStereizer::SetWorldInitPos(const hsPoint3& pos) +{ + plCoordinateInterface* parent = IGetParent(); + if( parent ) + fInitPos = parent->GetWorldToLocal() * pos; + else + fInitPos = pos; +} + +hsPoint3 plStereizer::GetWorldInitPos() const +{ + plCoordinateInterface* parent = IGetParent(); + if( parent ) + return parent->GetLocalToWorld() * fInitPos; + + return fInitPos; +} + +plCoordinateInterface* plStereizer::IGetParent() const +{ + plCoordinateInterface* coord = IGetTargetCoordinateInterface(0); + if( coord ) + { + return coord->GetParent(); + } + return nil; +} + +// Note that (along with it's many other hacky defects), this +// will go down in flames if there are two potential masters. +// Of course, two line follow mods doesn't really make sense +// now anyway, but the point is that this is a simplified placeholder +// to get the job done. If and when a need is shown for sequencing of +// modifiers, this should be updated to follow that protocol. But +// the rationale is that one simple example of a need for sequencing +// doesn't give enough basis to decide what that protocol should be. +// Or in simpler terms, I want to do it one way, Brice wants to do +// it another, and since either would work for this, we're waiting +// for a tie breaker case that gives one way or the other an advantage. +hsBool plStereizer::CheckForMaster() +{ + ISetHasMaster(false); + plSceneObject* targ = GetTarget(); + if( !targ ) + return false; + + int n = targ->GetNumModifiers(); + int i; + for( i = 0; i < n; i++ ) + { + plLineFollowMod* line = plLineFollowMod::ConvertNoRef(IGetTargetModifier(0, i)); + if( line ) + { + ISetHasMaster(true); + line->AddStereizer(GetKey()); + + return true; + } + } + return false; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plStereizer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plStereizer.h new file mode 100644 index 00000000..5a9baba8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plStereizer.h @@ -0,0 +1,124 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plStereizer_inc +#define plStereizer_inc + +#include "../pnModifier/plSingleModifier.h" +#include "hsGeometry3.h" +#include "hsMatrix44.h" + +class plListenerMsg; +class plMessage; + +class plCoordinateInterface; + +class hsStream; +class hsResMgr; + +class plStereizer : public plSingleModifier +{ +protected: + + // Flags - in a plSingleModifier::hsBitVector. + enum + { + kLeftChannel, + kHasMaster + }; + + // Static properties + hsScalar fAmbientDist; + hsScalar fTransition; + + hsScalar fMaxSepDist; + hsScalar fMinSepDist; + + hsScalar fTanAng; + + hsPoint3 fInitPos; + + // Environmental properties, namely of the current listener + hsPoint3 fListPos; + hsVector3 fListDirection; + hsVector3 fListUp; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + + hsPoint3 IGetLocalizedPos(const hsVector3& posToList, hsScalar distToList) const; + hsPoint3 IGetAmbientPos() const; + void ISetNewPos(const hsPoint3& newPos); + + hsPoint3 IGetUnStereoPos() const; + + plCoordinateInterface* IGetParent() const; + + void ISetHasMaster(hsBool on) { if(on)SetFlag(kHasMaster); else ClearFlag(kHasMaster); } + +public: + plStereizer(); + virtual ~plStereizer(); + + CLASSNAME_REGISTER( plStereizer ); + GETINTERFACE_ANY( plStereizer, plSingleModifier ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + hsBool Stereize(); + void SetFromListenerMsg(const plListenerMsg* listMsg); + + void SetAmbientDist(hsScalar d) { fAmbientDist = d; } + hsScalar GetAmbientDist() const { return fAmbientDist; } + + void SetTransition(hsScalar d) { fTransition = d; } + hsScalar GetTransition() const { return fTransition; } + + void SetMaxSepDist(hsScalar d) { fMaxSepDist = d; } + hsScalar GetMaxSepDist() const { return fMaxSepDist; } + + void SetMinSepDist(hsScalar d) { fMinSepDist = d; } + hsScalar GetMinSepDist() const { return fMinSepDist; } + + void SetSepAngle(hsScalar rads); + hsScalar GetSepAngle() const; + + void SetAsLeftChannel(hsBool on) { if(on)SetFlag(kLeftChannel); else ClearFlag(kLeftChannel); } + hsBool IsLeftChannel() const { return HasFlag(kLeftChannel); } + + void SetParentInitPos(const hsPoint3& pos) { fInitPos = pos; } + const hsPoint3& GetParentInitPos() const { return fInitPos; } + + void SetWorldInitPos(const hsPoint3& pos); + hsPoint3 GetWorldInitPos() const; + + hsBool CheckForMaster(); + hsBool HasMaster() const { return HasFlag(kHasMaster); } +}; + +#endif // plStereizer_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plViewFaceModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plViewFaceModifier.cpp new file mode 100644 index 00000000..846ba623 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plViewFaceModifier.cpp @@ -0,0 +1,432 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plViewFaceModifier.h" +#include "plgDispatch.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "hsFastMath.h" +#include "plPipeline.h" + +#include "../plMessage/plRenderMsg.h" +#include "../plMessage/plListenerMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plAvatar/plArmatureMod.h" + +plViewFaceModifier::plViewFaceModifier() +: fFacePoint(0,0,0), + fLastDirY(0,1.f,0), + fScale(1.f,1.f,1.f), + fOffset(0,0,0) +{ + fOrigLocalToParent.Reset(); + fOrigParentToLocal.Reset(); + + SetFlag(kFaceCam); // default +} + +plViewFaceModifier::~plViewFaceModifier() +{ +} + +void plViewFaceModifier::SetOrigTransform(const hsMatrix44& l2p, const hsMatrix44& p2l) +{ + fOrigLocalToParent = l2p; + fOrigParentToLocal = p2l; +} + +void plViewFaceModifier::Read(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Read(s, mgr); + + fScale.Read(s); + + fOrigLocalToParent.Read(s); + fOrigParentToLocal.Read(s); + + if( HasFlag(kFaceObj) ) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefFaceObj), plRefFlags::kPassiveRef); + + fOffset.Read(s); + + if( HasFlag(kMaxBounds) ) + fMaxBounds.Read(s); +} + +void plViewFaceModifier::Write(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Write(s, mgr); + + fScale.Write(s); + + fOrigLocalToParent.Write(s); + fOrigParentToLocal.Write(s); + + if( HasFlag(kFaceObj) ) + mgr->WriteKey(s, fFaceObj); + + fOffset.Write(s); + + if( HasFlag(kMaxBounds) ) + fMaxBounds.Write(s); +} + +void plViewFaceModifier::SetMaxBounds(const hsBounds3Ext& bnd) +{ + SetFlag(kMaxBounds); + fMaxBounds = bnd; +} + +void plViewFaceModifier::SetTarget(plSceneObject* so) +{ + plSingleModifier::SetTarget(so); + + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + if( HasFlag(kFaceList) ) + plgDispatch::Dispatch()->RegisterForExactType(plListenerMsg::Index(), GetKey()); + if( HasFlag(kFacePlay) ) + plgDispatch::Dispatch()->RegisterForExactType(plArmatureUpdateMsg::Index(), GetKey()); +} + +hsBool plViewFaceModifier::IEval(double secs, hsScalar del, UInt32 dirty) +{ + return false; +} + +hsBool plViewFaceModifier::IFacePoint(plPipeline* pipe, const hsPoint3& at) +{ +#if 1 // BOUNDSTEST + extern int mfCurrentTest; + if( mfCurrentTest != 101 ) + if( HasFlag(kMaxBounds) ) + { + if( !pipe->TestVisibleWorld(fMaxBounds) ) + return false; + } +#endif // BOUNDSTEST + + if( !(GetTarget() && GetTarget()->GetCoordinateInterface()) ) + return false; + + hsMatrix44 worldToLocal = fOrigParentToLocal; // parentToLocal + if( GetTarget()->GetCoordinateInterface()->GetParent() && GetTarget()->GetCoordinateInterface()->GetParent() ) + { + hsMatrix44 m; + worldToLocal = worldToLocal * GetTarget()->GetCoordinateInterface()->GetParent()->GetWorldToLocal(); + } + + hsPoint3 localAt = worldToLocal * at; + hsScalar len = localAt.MagnitudeSquared(); + if( len <= 0 ) + return false; + len = -hsFastMath::InvSqrtAppr(len); + + hsVector3 dirX, dirY, dirZ; + dirZ.Set(localAt.fX * len, localAt.fY * len, localAt.fZ * len); + + if( HasFlag(kPivotFace) ) + { + dirY.Set(0.f, 0.f, 1.f); + dirX = dirY % dirZ; + dirX = hsFastMath::NormalizeAppr(dirX); + dirY = dirZ % dirX; + } + else if( HasFlag(kPivotFavorY) ) + { + dirY.Set(0.f, 1.f, 0.f); + dirX = dirY % dirZ; + dirX = hsFastMath::NormalizeAppr(dirX); + dirY = dirZ % dirX; + } + else if( HasFlag(kPivotY) ) + { + dirY.Set(0.f, 1.f, 0.f); + dirX = dirY % dirZ; + dirX = hsFastMath::NormalizeAppr(dirX); + dirZ = dirX % dirY; + } + else if( HasFlag(kPivotTumble) ) + { + dirY = fLastDirY; + dirX = dirY % dirZ; + dirX = hsFastMath::NormalizeAppr(dirX); + dirY = dirZ % dirX; + fLastDirY = dirY; + } + else + { + hsAssert(false, "I've no idea what you're getting at here in ViewFace land"); + } + + hsMatrix44 x; + hsMatrix44 xInv; + + xInv.fMap[0][0] = x.fMap[0][0] = dirX[0]; + xInv.fMap[1][0] = x.fMap[0][1] = dirY[0]; + xInv.fMap[2][0] = x.fMap[0][2] = dirZ[0]; + xInv.fMap[3][0] = x.fMap[0][3] = 0; + + xInv.fMap[0][1] = x.fMap[1][0] = dirX[1]; + xInv.fMap[1][1] = x.fMap[1][1] = dirY[1]; + xInv.fMap[2][1] = x.fMap[1][2] = dirZ[1]; + xInv.fMap[3][1] = x.fMap[1][3] = 0; + + xInv.fMap[0][2] = x.fMap[2][0] = dirX[2]; + xInv.fMap[1][2] = x.fMap[2][1] = dirY[2]; + xInv.fMap[2][2] = x.fMap[2][2] = dirZ[2]; + xInv.fMap[3][2] = x.fMap[2][3] = 0; + + x.fMap[3][0] = x.fMap[3][1] = x.fMap[3][2] = 0; + xInv.fMap[0][3] = xInv.fMap[1][3] = xInv.fMap[2][3] = 0; + + xInv.fMap[3][3] = x.fMap[3][3] = hsScalar1; + + x.NotIdentity(); + xInv.NotIdentity(); + + if( HasFlag(kScale) ) + { + x.fMap[0][0] *= fScale.fX; + x.fMap[0][1] *= fScale.fX; + x.fMap[0][2] *= fScale.fX; + + x.fMap[1][0] *= fScale.fY; + x.fMap[1][1] *= fScale.fY; + x.fMap[1][2] *= fScale.fY; + + x.fMap[2][0] *= fScale.fZ; + x.fMap[2][1] *= fScale.fZ; + x.fMap[2][2] *= fScale.fZ; + + hsScalar inv = 1.f / fScale.fX; + xInv.fMap[0][0] *= inv; + xInv.fMap[1][0] *= inv; + xInv.fMap[2][0] *= inv; + + inv = 1.f / fScale.fY; + xInv.fMap[0][1] *= inv; + xInv.fMap[1][1] *= inv; + xInv.fMap[2][1] *= inv; + + inv = 1.f / fScale.fZ; + xInv.fMap[0][2] *= inv; + xInv.fMap[1][2] *= inv; + xInv.fMap[2][2] *= inv; + } + + hsMatrix44 l2p = fOrigLocalToParent * x; + hsMatrix44 p2l = xInv * fOrigParentToLocal; + + if( l2p != IGetTargetCoordinateInterface(0)->GetLocalToParent() ) // TERRORDAN + { + IGetTargetCoordinateInterface(0)->SetLocalToParent(l2p, p2l); + IGetTargetCoordinateInterface(0)->FlushTransform(false); + } + + return true; +} + + + +#include "plProfile.h" +plProfile_CreateTimer("ViewFacing", "RenderSetup", ViewFace); + +hsBool plViewFaceModifier::MsgReceive(plMessage* msg) +{ + plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg); + + if( rend ) + { + plProfile_BeginLap(ViewFace, this->GetKey()->GetUoid().GetObjectName()); + + if( HasFlag(kFaceCam) ) + { + fFacePoint = rend->Pipeline()->GetViewPositionWorld(); + if( HasFlag(kOffset) ) + { + if( HasFlag(kOffsetLocal) ) + { + fFacePoint += rend->Pipeline()->GetViewAcrossWorld() * fOffset.fX; + fFacePoint += rend->Pipeline()->GetViewUpWorld() * fOffset.fY; + fFacePoint += rend->Pipeline()->GetViewDirWorld() * fOffset.fZ; + } + else + { + fFacePoint += fOffset; + } + } + } + else + if( HasFlag(kFaceObj) ) + { + if( !fFaceObj ) + return true; + fFacePoint = fFaceObj->GetLocalToWorld().GetTranslate(); + if( HasFlag(kOffset) ) + { + if( HasFlag(kOffsetLocal) ) + fFacePoint += fFaceObj->GetLocalToWorld() * fOffset; + else + fFacePoint += fOffset; + } + } + + IFacePoint(rend->Pipeline(), fFacePoint); + + plProfile_EndLap(ViewFace, this->GetKey()->GetUoid().GetObjectName()); + return true; + } + plArmatureUpdateMsg* armMsg = plArmatureUpdateMsg::ConvertNoRef(msg); + if( armMsg && armMsg->IsLocal() ) + { + const plSceneObject* head = armMsg->fArmature->FindBone(plAvBrainHuman::Head); + if( head ) + { + fFacePoint = head->GetLocalToWorld().GetTranslate(); + if( HasFlag(kOffset) ) + { + if( HasFlag(kOffsetLocal) ) + fFacePoint += head->GetLocalToWorld() * fOffset; + else + fFacePoint += fOffset; + } + } + + return true; + } + plListenerMsg* list = plListenerMsg::ConvertNoRef(msg); + if( list ) + { + fFacePoint = list->GetPosition(); + + if( HasFlag(kOffset) ) + { + if( HasFlag(kOffsetLocal) ) + { + fFacePoint += (list->GetDirection() % list->GetUp()) * fOffset.fX; + fFacePoint += list->GetDirection() * fOffset.fY; + fFacePoint += list->GetUp() * fOffset.fZ; + } + else + { + fFacePoint += fOffset; + } + } + + return true; + } + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + IOnReceive(refMsg); + else + IOnRemove(refMsg); + return true; + } + return plSingleModifier::MsgReceive(msg); +} + +void plViewFaceModifier::IOnReceive(plGenRefMsg* refMsg) +{ + switch(refMsg->fType) + { + case kRefFaceObj: + fFaceObj = plSceneObject::ConvertNoRef(refMsg->GetRef()); + break; + } +} + +void plViewFaceModifier::IOnRemove(plGenRefMsg* refMsg) +{ + switch(refMsg->fType) + { + case kRefFaceObj: + fFaceObj = nil; + break; + } +} + +void plViewFaceModifier::ISetObject(plKey soKey) +{ + hsgResMgr::ResMgr()->SendRef(soKey, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefFaceObj), plRefFlags::kPassiveRef); +} + +void plViewFaceModifier::SetFollowMode(FollowMode m, plKey soKey) +{ + ClearFlag(kFaceCam); + ClearFlag(kFaceList); + ClearFlag(kFacePlay); + ClearFlag(kFaceObj); + + switch(m) + { + case kFollowCamera: + SetFlag(kFaceCam); + break; + case kFollowListener: + SetFlag(kFaceList); + break; + case kFollowPlayer: + SetFlag(kFacePlay); + break; + case kFollowObject: + SetFlag(kFaceObj); + ISetObject(soKey); + break; + default: + hsAssert(false, "Unknown follow mode"); + SetFlag(kFaceCam); + break; + } +} + +plViewFaceModifier::FollowMode plViewFaceModifier::GetFollowMode() const +{ + if( HasFlag(kFaceCam) ) + return kFollowCamera; + if( HasFlag(kFaceList) ) + return kFollowListener; + if( HasFlag(kFacePlay) ) + return kFollowPlayer; + if( HasFlag(kFaceObj) ) + return kFollowObject; + + hsAssert(false, "Have no follow mode"); + return kFollowCamera; + +} + +void plViewFaceModifier::SetOffset(const hsVector3& off, hsBool local) +{ + fOffset = off; + if( local ) + SetFlag(kOffsetLocal); + else + ClearFlag(kOffsetLocal); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plViewFaceModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plViewFaceModifier.h new file mode 100644 index 00000000..deb2a7a5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAnimation/plViewFaceModifier.h @@ -0,0 +1,124 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plViewFaceModifier_inc +#define plViewFaceModifier_inc + +#include "hsMatrix44.h" +#include "hsBounds.h" +#include "../pnModifier/plSingleModifier.h" + +class plGenRefMsg; +class plPipeline; + +class plViewFaceModifier : public plSingleModifier +{ +public: + enum plVFFlags { + kPivotFace = 0, + kPivotFavorY, + kPivotY, + kPivotTumble, + kScale, + kFaceCam, + kFaceList, + kFacePlay, + kFaceObj, + kOffset, + kOffsetLocal, + kMaxBounds + }; +protected: + + hsVector3 fLastDirY; + + hsVector3 fScale; + + hsMatrix44 fOrigLocalToParent; + hsMatrix44 fOrigParentToLocal; + + hsPoint3 fFacePoint; + + plSceneObject* fFaceObj; + + hsVector3 fOffset; + + hsBounds3Ext fMaxBounds; + + virtual hsBool IFacePoint(plPipeline* pipe, const hsPoint3& at); + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + + enum RefType + { + kRefFaceObj + }; + void IOnReceive(plGenRefMsg* refMsg); + void IOnRemove(plGenRefMsg* refMsg); + void ISetObject(plKey soKey); + +public: + plViewFaceModifier(); + virtual ~plViewFaceModifier(); + + CLASSNAME_REGISTER( plViewFaceModifier ); + GETINTERFACE_ANY( plViewFaceModifier, plSingleModifier ); + + virtual void SetTarget(plSceneObject* so); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + // ViewFace specific + void SetScale(const hsVector3& s) { fScale = s; } + const hsVector3& GetScale() const { return fScale; } + + void SetOrigTransform(const hsMatrix44& l2p, const hsMatrix44& p2l); + + void SetMaxBounds(const hsBounds3Ext& bnd); + const hsBounds3Ext& GetMaxBounds() const { return fMaxBounds; } + hsBool HaveMaxBounds() const { return HasFlag(kMaxBounds); } + + enum FollowMode + { + kFollowCamera = 0, // Follow the camera + kFollowListener, + kFollowPlayer, + kFollowObject + }; + void SetFollowMode(FollowMode m, plKey soKey=nil); // For follow object, set obj, else it's ignored. + FollowMode GetFollowMode() const; + plSceneObject* GetFollowObject() const { return fFaceObj; } + + void SetOffsetActive(hsBool on) { if(on) SetFlag(kOffset); else ClearFlag(kOffset); } + hsBool GetOffsetActive() const { return HasFlag(kOffset); } + + void SetOffset(const hsVector3& off, hsBool local=true); + const hsVector3& GetOffset() const { return fOffset; } + hsBool GetOffsetLocal() const { return HasFlag(kOffsetLocal); } +}; + +#endif // plViewFaceModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/pfAudioCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/pfAudioCreatable.h new file mode 100644 index 00000000..3b27c120 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/pfAudioCreatable.h @@ -0,0 +1,40 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pfAudioCreatable_inc +#define pfAudioCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plListener.h" + +REGISTER_CREATABLE( plListener ); + +#include "plRandomSoundMod.h" + +REGISTER_CREATABLE( plRandomSoundMod ); + +#endif // pfCharacterCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp new file mode 100644 index 00000000..7fe4fc20 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plListener.cpp @@ -0,0 +1,312 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsMatrix44.h" +#include "hsTypes.h" +#include "plListener.h" +#include "plgDispatch.h" +#include "../plAudio/plAudioSystem.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plAudioSysMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pfCamera/plVirtualCamNeu.h" +#include "../plMessage/plListenerMsg.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plPipeline/plDebugText.h" + +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvCallbackAction.h" + +hsBool plListener::fPrintDbgInfo = false; + +hsBool plListener::IEval(double secs, hsScalar del, UInt32 dirty) +{ +// if (!plgAudioSys::Active()) +// return true; + plSceneObject *pRefObject = nil; + + int y = 16 + 12, x = 400; + if( fPrintDbgInfo ) + plDebugText::Instance().DrawString( x, 16, "Listener:", (UInt32)0xffffffff, plDebugText::kStyleBold ); + + // Get the avatar's SceneObject + plKey key = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + if(key) + pRefObject = plSceneObject::ConvertNoRef(key->ObjectIsLoaded()); + + if( pRefObject == nil && fVCam == nil ) + { + // We don't have a position to init by, so do NOT eval yet!!! + if( fPrintDbgInfo ) + plDebugText::Instance().DrawString( x, y, "Not eval-ing yet", (UInt32)0xffffffff ); + return true; + } + + // Changed 2.19.02 mcn - Basing it off the head bone isn't what we really want, esp. since + // it isn't what the camera uses. What we *really* want is a head-ish-positioned non-bobbing node + // that we base both off of. Until then, we're just going to have to use the avatar's root (i.e. his + // feet) and add in an appropriate height. See plAvBrain.cpp::BindAudioListener() for the other half + // of the hack. + // Note the 2nd: since GetAxis() is buggy, we'll just add in a constant vector. Of course, this implies + // that the avatar is always oriented up, but then it also implies he's always of constant height, so + // there. + const hsVector3 kAvatarHeightVector = hsVector3( 0, 0, 6.33f ); // isn't *everyone* 6'4"? + + /// Collect the current values for our parameters + hsPoint3 position; + hsVector3 velocity, dir, up; + + enum + { + kInvalid = 0, + kVCam, + kObject + } facingType = kInvalid, posType = kInvalid, velType = kInvalid; + + // Facing + if( fFacingRatio == 1.f ) + { + if( pRefObject != nil && pRefObject->GetCoordinateInterface() ) + { + hsMatrix44 facingL2W = pRefObject->GetCoordinateInterface()->GetLocalToWorld(); + dir = facingL2W.GetAxis( hsMatrix44::kView ); + up = facingL2W.GetAxis( hsMatrix44::kUp ); + + facingType = kObject; + } + } + else if( fVCam != nil ) + { + dir = hsVector3( fVCam->GetCameraPOA() - fVCam->GetCameraPos() ); + up = fVCam->GetCameraUp(); + facingType = kVCam; + } + + // Position + if( fPosRatio == 1.f ) + { + if( pRefObject != nil && pRefObject->GetCoordinateInterface() ) + { + position = pRefObject->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + position += kAvatarHeightVector; + posType = kObject; + } + } + else if( fVCam != nil ) + { + position = fVCam->GetCameraPos(); + posType = kVCam; + } + + // Velocity + if( fVelRatio == 1.f ) + { + if( pRefObject != nil ) + { + plArmatureMod* arm = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (arm) + { + plPhysicalControllerCore* controller = arm->GetController(); + if (controller) + { + velocity = controller->GetLinearVelocity(); + velType = kObject; + } + } + } + } + else if( fVCam != nil ) + { + // Darn, can't do it + } + + if( facingType == kInvalid || posType == kInvalid || velType == kInvalid ) + { + if( fPrintDbgInfo ) + plDebugText::Instance().DrawString( x, y, "Not eval-ing: missing one or more parameter bases", (UInt32)0xff0000ff ); + return true; + } + + // Got the params, now construct and send out the message, as well as update the audio system + plListenerMsg* msg = TRACKED_NEW plListenerMsg; + msg->SetDirection( dir ); + msg->SetUp( up ); + msg->SetPosition( position ); + msg->SetVelocity( velocity ); + + plgAudioSys::SetListenerOrientation( dir, up ); + plgAudioSys::SetListenerPos( position ); + plgAudioSys::SetListenerVelocity( velocity ); + + if( fPrintDbgInfo ) + { + char str[ 256 ]; + sprintf( str, "Direction: (%3.2f,%3.2f,%3.2f) from %s", dir.fX, dir.fY, dir.fZ, ( facingType == kObject ) ? pRefObject->GetKey()->GetUoid().GetObjectName() : "VCam" ); + plDebugText::Instance().DrawString( x, y, str, (UInt32)0xffffffff ); + y += 12; + + sprintf( str, "Up: (%3.2f,%3.2f,%3.2f) from %s", up.fX, up.fY, up.fZ, ( facingType == kObject ) ? pRefObject->GetKey()->GetUoid().GetObjectName() : "VCam" ); + plDebugText::Instance().DrawString( x, y, str, (UInt32)0xffffffff ); + y += 12; + + sprintf( str, "Position: (%3.2f,%3.2f,%3.2f) from %s", position.fX, position.fY, position.fZ, ( posType == kObject ) ? pRefObject->GetKey()->GetUoid().GetObjectName() : "VCam" ); + plDebugText::Instance().DrawString( x, y, str, (UInt32)0xffffffff ); + y += 12; + + sprintf( str, "Velocity: (%3.2f,%3.2f,%3.2f) from %s", velocity.fX, velocity.fY, velocity.fZ, ( velType == kObject ) ? pRefObject->GetKey()->GetUoid().GetObjectName() : "VCam" ); + plDebugText::Instance().DrawString( x, y, str, (UInt32)0xffffffff ); + y += 12; + } + plgDispatch::MsgSend( msg ); + + return true; +} + +void plListener::ISetRef( const plKey &ref, hsBool binding, int type ) +{ + if( binding ) + hsgResMgr::ResMgr()->AddViaNotify( ref, TRACKED_NEW plGenRefMsg( GetKey(), plGenRefMsg::kOnReplace, -1, type ), plRefFlags::kPassiveRef ); + else + GetKey()->Release( ref ); +} + +void plListener::IEnsureVCamValid( void ) +{ + if( fPosRatio == 1.f && fFacingRatio == 1.f && fVelRatio == 1.f ) + { + // All of our params are purely using objects, so we don't need a virtual camera pointer at all + if( fVCam != nil ) + ISetRef( fVCam->GetKey(), false, kRefVCam ); + } + else + { + // One or more of our params are using the vcam as a basis, so make sure we have it + if( fVCam == nil ) + { + plVirtualCam1 *vCam = plVirtualCam1::Instance(); + if( vCam == nil ) + { + hsAssert( false, "Unable to grab virtual camera instance; no basis for listener!!!" ); + return; + } + + ISetRef( vCam->GetKey(), true, kRefVCam ); + } + } +} + +void plListener::ICheckAudio( void ) const +{ + if( ( fPosRatio < 1.f || fFacingRatio < 1.f || fVelRatio < 1.f ) && fVCam == nil ) + plgAudioSys::SetMuted( true ); +} + +hsBool plListener::MsgReceive(plMessage* msg) +{ + plSetListenerMsg *setMsg = plSetListenerMsg::ConvertNoRef( msg ); + if( setMsg != nil ) + { + hsBool useVCam; + + if( setMsg->GetType() & plSetListenerMsg::kVCam ) + { + // Reset any ratios + if( setMsg->GetType() & plSetListenerMsg::kPosition ) + fPosRatio = 0.f; + + if( setMsg->GetType() & plSetListenerMsg::kVelocity ) + fVelRatio = 0.f; + + if( setMsg->GetType() & plSetListenerMsg::kFacing ) + fFacingRatio = 0.f; + + IEnsureVCamValid(); + } + else + { + useVCam = setMsg->IsBinding(); + + if( setMsg->GetType() & plSetListenerMsg::kPosition ) + fPosRatio = 1.f; + + if( setMsg->GetType() & plSetListenerMsg::kVelocity ) + fVelRatio = 1.f; + + if( setMsg->GetType() & plSetListenerMsg::kFacing ) + fFacingRatio = 1.f; + + if( fPosRatio > 0.f || fVelRatio > 0.f || fFacingRatio > 0.f ) + // Need this, so store it now + ISetRef( setMsg->GetSrcKey(), setMsg->IsBinding(), kRefObject ); + } + + return true; + } + + plEvalMsg* pEMsg = plEvalMsg::ConvertNoRef(msg); + if (pEMsg) + { + IEval(pEMsg->GetTimeStamp(), pEMsg->DelSeconds(), true); + + if( fInitMe ) + { + // By default, position and orientation are camera based + plSetListenerMsg *set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVCam | plSetListenerMsg::kFacing, nil, true ); + set->Send(); + set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVCam | plSetListenerMsg::kPosition, nil, true ); + set->Send(); + + fInitMe = false; + } + + return true; + } + + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef( msg ); + if( refMsg != nil ) + { + if( refMsg->fType == kRefVCam ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fVCam = plVirtualCam1::ConvertNoRef( refMsg->GetRef() ); + } + else if( refMsg->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) ) + { + if( plVirtualCam1::ConvertNoRef( refMsg->GetRef() ) == fVCam ) + fVCam = nil; + } + ICheckAudio(); + } + + return true; + } + + return plSingleModifier::MsgReceive(msg); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plListener.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plListener.h new file mode 100644 index 00000000..b4122730 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plListener.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plListener_h +#define plListener_h + +#include "../pnModifier/plSingleModifier.h" + + +class plSceneObject; +class plVirtualCam1; + +class plListener : public plSingleModifier +{ +public: + + plListener() : fVCam(nil), fInitMe(true){;} + ~plListener(){;} + + CLASSNAME_REGISTER( plListener ); + GETINTERFACE_ANY( plListener, plSingleModifier ); + + virtual hsBool MsgReceive(plMessage* msg); + + static void ShowDebugInfo( hsBool s ) { fPrintDbgInfo = s; } + + // Get info for which object these things are attached to - camera or refObject + UInt8 GetAttachedPosType() { return (UInt8)fPosRatio; } + UInt8 GetAttachedFacingType() { return (UInt8)fFacingRatio; } + UInt8 GetAttachedVelType() { return (UInt8)fVelRatio; } + + enum + { + kCamera = 0, + kAvatar = 1 + }; + +protected: + + enum Refs + { + kRefObject, + kRefVCam + }; + + plVirtualCam1* fVCam; + + hsScalar fPosRatio, fFacingRatio, fVelRatio; // 0 is vCam, 1 is refObject + hsBool fInitMe; + + static hsBool fPrintDbgInfo; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + void ISetRef( const plKey &ref, hsBool binding, int type ); + void ICheckAudio( void ) const; + + void IEnsureVCamValid( void ); +}; + +#endif //plWin32Sound_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plRandomSoundMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plRandomSoundMod.cpp new file mode 100644 index 00000000..b652c7f0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plRandomSoundMod.cpp @@ -0,0 +1,415 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plRandomSoundMod.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../pnMessage/plSoundMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plAudio/plAudioSystem.h" +#include "../plAudio/plSound.h" +#include "../plAudio/plWin32GroupedSound.h" // EEK BAD +#include "plgDispatch.h" +#include "hsTimer.h" +#include "../plStatusLog/plStatusLog.h" + +plRandomSoundModGroup::plRandomSoundModGroup() : fNumSounds(0), fIndices(nil), fGroupedIdx(-1), fCurrent(-1) +{ +} + +plRandomSoundModGroup::~plRandomSoundModGroup() +{ + delete [] fIndices; +} + +void plRandomSoundModGroup::Read(hsStream *s) +{ + fNumSounds = s->ReadSwap16(); + fGroupedIdx = s->ReadSwap16(); + fIndices = TRACKED_NEW UInt16[fNumSounds]; + + int i; + for (i = 0; i < fNumSounds; i++) + fIndices[i] = s->ReadSwap16(); +} + +void plRandomSoundModGroup::Write(hsStream *s) +{ + s->WriteSwap16(fNumSounds); + s->WriteSwap16(fGroupedIdx); + + int i; + for (i = 0; i < fNumSounds; i++) + s->WriteSwap16(fIndices[i]); +} + +/////////////////////////////////////////////////////////////////////////////////////// + +plRandomSoundMod::plRandomSoundMod() : fCurrentGroup(0), fNumGroups(0), fGroups(nil), fOldPriority(-1), fFirstTimePlay(true) +{ +} + +plRandomSoundMod::~plRandomSoundMod() +{ + delete [] fGroups; +} + +void plRandomSoundMod::IPlayNextIfMaster() +{ + if( !fTarget ) + IRetry(2.f); + + if( IStopped() ) + return; + + IPlayNext(); +} + +// If we recieve a stop message, actually stop the sound +void plRandomSoundMod::IStop() +{ + plRandomCommandMod::IStop(); + + plAudioInterface *ai = nil; + + if( !plgAudioSys::Active() ) return; + + ai = IGetTargetAudioInterface(0); + if(!ai) return; + + if( fGroups != nil && fGroups[ fCurrentGroup ].fGroupedIdx != -1 ) + { + plSoundMsg *msg = TRACKED_NEW plSoundMsg(); + msg->SetCmd(plSoundMsg::kStop); + msg->fIndex = fGroups[ fCurrentGroup ].fIndices[ fCurrent ]; + plgDispatch::MsgSend(msg); + } + else + { + if(fCurrent == -1) return; + UInt16 currentSndIdx = ( fGroups != nil ) ? fGroups[fCurrentGroup].fIndices[fCurrent] : fActiveList[fCurrent]; + plSoundMsg* snd = TRACKED_NEW plSoundMsg(GetKey(), GetTarget()->GetKey(), nil); + snd->SetCmd(plSoundMsg::kStop); + snd->fIndex = currentSndIdx; + plgDispatch::MsgSend(snd); + } +} + +void plRandomSoundMod::IPlayNext() +{ + if( !plgAudioSys::Active() ) + { + IRetry(10.f); + return; + } + + plAudioInterface* ai = IGetTargetAudioInterface(0); + if( !ai ) + { + IRetry(2.f); + return; + } + + int i; + UInt16 currentSndIdx; + int nSounds = (fGroups == nil ? ai->GetNumSounds() : fGroups[fCurrentGroup].fNumSounds); + fEndTimes.ExpandAndZero(nSounds); + plSound *pSound = nil; + + // The global sound priority has changed, update the active random sounds list + if(fOldPriority != plgAudioSys::GetPriorityCutoff() && fGroups == nil) + { + fActiveList.clear(); + fOldPriority = plgAudioSys::GetPriorityCutoff(); + for(i = 0; i < nSounds; i++) + { + pSound = ai->GetSound(i); + if(pSound && pSound->GetPriority() <= plgAudioSys::GetPriorityCutoff()) + { + fActiveList.push_back(i); + } + } + // There are no sounds that should play + if(fGroups == nil && fActiveList.empty() && nSounds) + { + // If no sounds in this component even attempt to play this component gets mad and will never play any sounds again. + // So, give it a zero to make it happy. This sound will still be rejected when it tries to play which is exactly what + // we want since if we get here no sounds in this component should play. + fActiveList.push_back(0); + } + } + + // if this is the first time this component is going to play a sound check to see if it has a delay time + if(fFirstTimePlay) + { + fFirstTimePlay = false; + if( !(fMode & kOneCmd) ) + { + hsScalar delay = IGetDelay(0); + double t = hsTimer::GetSysSeconds() + delay; + + plAnimCmdMsg* anim = TRACKED_NEW plAnimCmdMsg(GetKey(), GetKey(), &t); + anim->SetCmd(plAnimCmdMsg::kContinue); + plgDispatch::MsgSend(anim); + return; + } + } + + if( !ISelectNext(fGroups == nil ? fActiveList.size() : nSounds) ) + { + plRandomCommandMod::IStop(); + return; + } + + // We don't want random sounds to synch, since we don't synch the randomness. So force this next + // sound to not synch + hsScalar currLen; + if( fGroups != nil && fGroups[ fCurrentGroup ].fGroupedIdx != -1 ) + { + currentSndIdx = fGroups[ fCurrentGroup ].fIndices[ fCurrent ]; + plWin32GroupedSound *sound = plWin32GroupedSound::ConvertNoRef( ai->GetSound( fGroups[ fCurrentGroup ].fGroupedIdx ) ); + + if (!sound) + { + hsAssert( sound != nil, "Invalid sound type in plRandomSoundMod" ); + return; + } + sound->SetLocalOnly(true); + + // Send msg to the grouped sound to switch sounds + plSoundMsg *snd = TRACKED_NEW plSoundMsg(); + snd->SetCmd( plSoundMsg::kSelectFromGroup ); + snd->fIndex = currentSndIdx; + snd->Send( sound->GetKey() ); + + // Now tell the audio interface to play the sound (probably should change this....) + snd = TRACKED_NEW plSoundMsg(GetKey(), GetTarget()->GetKey(), nil); + snd->SetCmd(plSoundMsg::kGoToTime); + snd->fTime = (0); + snd->SetCmd(plSoundMsg::kStop); + snd->SetCmd(plSoundMsg::kPlay); + snd->fIndex = fGroups[ fCurrentGroup ].fGroupedIdx; + plgDispatch::MsgSend(snd); + + currLen = sound->GetSoundLength( currentSndIdx ); + } + else + { + currentSndIdx = ( fGroups != nil ) ? fGroups[fCurrentGroup].fIndices[fCurrent] : fActiveList[fCurrent]; + if (ai->GetSound(currentSndIdx)) + { + ai->GetSound( currentSndIdx )->SetLocalOnly(true); + + ai->GetSound(currentSndIdx)->Stop(); + ai->GetSound(currentSndIdx)->Play(); + } + + if (ai->GetSound(currentSndIdx)) + currLen = (hsScalar)(ai->GetSound(currentSndIdx)->GetLength()); + else + currLen = 0; + } + + if (plgAudioSys::AreExtendedLogsEnabled()) + { + if (fGroups) + plStatusLog::AddLineS("audio.log", "%s: Playing sound #%d from group %d", GetTarget(0)->GetKeyName(), fCurrent, fCurrentGroup); + else + plStatusLog::AddLineS("audio.log", "%s: Playing sound #%d", GetTarget(0)->GetKeyName(), fCurrent); + } + + fEndTimes[fCurrent] = hsTimer::GetSysSeconds() + currLen; + + if( !(fMode & kOneCmd) ) + { + hsScalar delay = IGetDelay(currLen); + + double t = hsTimer::GetSysSeconds() + delay; + + plAnimCmdMsg* anim = TRACKED_NEW plAnimCmdMsg(GetKey(), GetKey(), &t); + anim->SetCmd(plAnimCmdMsg::kContinue); + plgDispatch::MsgSend(anim); + } + else + { + plRandomCommandMod::IStop(); + } +} + +void plRandomSoundMod::SetCurrentGroup(UInt16 group) +{ + hsAssert(group < fNumGroups, "Setting an invalid group on a random sound modifier"); + + if (group != fCurrentGroup && group < fNumGroups) + { + fGroups[fCurrentGroup].fExcluded = fExcluded; + fGroups[fCurrentGroup].fCurrent = fCurrent; + fExcluded = fGroups[group].fExcluded; + fCurrent = fGroups[group].fCurrent; + fCurrentGroup = group; + } +} + +void plRandomSoundMod::Read(hsStream *s, hsResMgr *mgr) +{ + plRandomCommandMod::Read(s, mgr); + + fNumGroups = s->ReadSwap16(); + if (fNumGroups > 0) + { + fGroups = TRACKED_NEW plRandomSoundModGroup[fNumGroups]; + int i; + for (i = 0; i < fNumGroups; i++) + fGroups[i].Read(s); + } +} + +void plRandomSoundMod::Write(hsStream *s, hsResMgr *mgr) +{ + plRandomCommandMod::Write(s, mgr); + + s->WriteSwap16(fNumGroups); + if (fNumGroups > 0) + { + int i; + for (i = 0; i < fNumGroups; i++) + fGroups[i].Write(s); + } +} + +void plRandomSoundMod::ForceSoundLoadState( hsBool loaded ) +{ + UInt16 i, j; + + plAudioInterface* ai = IGetTargetAudioInterface(0); + if( ai == nil ) + return; + + if( fGroups != nil ) + { + for( i = 0; i < fNumGroups; i++ ) + { + if( fGroups[ i ].fGroupedIdx != -1 ) + { + plSound *sound = ai->GetSound( fGroups[ i ].fGroupedIdx ); + if (!sound) + return; + if( loaded ) + sound->ForceLoad(); + else + sound->ForceUnload(); + } + else + { + for( j = 0; j < fGroups[ i ].fNumSounds; j++ ) + { + plSound *sound = ai->GetSound( fGroups[ i ].fIndices[ j ] ); + if (!sound) + return; + if( loaded ) + sound->ForceLoad(); + else + sound->ForceUnload(); + } + } + } + } + else + { + for( i = 0; i < ai->GetNumSounds(); i++ ) + { + plSound *sound = ai->GetSound( i ); + if (!sound) + return; + if( loaded ) + sound->ForceLoad(); + else + sound->ForceUnload(); + } + } +} + +// Overload this to handle volume changes +hsBool plRandomSoundMod::MsgReceive(plMessage* msg) +{ + plAnimCmdMsg* anim = plAnimCmdMsg::ConvertNoRef(msg); + if( anim ) + { + // Actually sets the volume + if( anim->Cmd(plAnimCmdMsg::kSetSpeed) ) + { + ISetVolume(anim->fSpeed); + } + } + + // Don't understand, pass on to base class. + return plRandomCommandMod::MsgReceive(msg); +} + +void plRandomSoundMod::ISetVolume(hsScalar volume) +{ + plSound *pSound = nil; + + pSound = IGetSoundPtr(); + if(pSound) + pSound->SetVolume(volume); +} + +float plRandomSoundMod::GetVolume() +{ + float volume = 1.0; + plSound *pSound; + + pSound = IGetSoundPtr(); + if(pSound) + volume = pSound->GetMaxVolume(); + return volume; +} + +void plRandomSoundMod::ISetPosition(hsPoint3 pos) +{ + plSound *pSound = IGetSoundPtr(); + if(pSound) + { + pSound->SetPosition(pos); + } +} + +plSound *plRandomSoundMod::IGetSoundPtr() +{ + plSound *pSound = nil; + if(fGroups != nil) return nil; + if(fCurrent == -1) return nil; // sound list hasn't been initialized yet, don't try and access it + + int currentSndIdx = fActiveList[fCurrent]; + plAudioInterface* ai = IGetTargetAudioInterface(0); + if( !ai ) + return nil; + + pSound = ai->GetSound( currentSndIdx ); + return pSound; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plRandomSoundMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plRandomSoundMod.h new file mode 100644 index 00000000..7549f619 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfAudio/plRandomSoundMod.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRandomSoundMod_inc +#define plRandomSoundMod_inc + +#include "../pfAnimation/plRandomCommandMod.h" +class plSound; +struct hsPoint3; + +class plRandomSoundModGroup +{ +public: + hsBitVector fExcluded; + Int8 fCurrent; + UInt16 fNumSounds; + UInt16 *fIndices; + Int16 fGroupedIdx; // Only used if we point to a groupedSound, in which case fIndices are indices into + // that sound. -1 if unused. + + plRandomSoundModGroup(); + ~plRandomSoundModGroup(); + + void Read(hsStream *s); + void Write(hsStream *s); +}; + +class plRandomSoundMod : public plRandomCommandMod +{ +protected: + UInt16 fCurrentGroup; + UInt16 fNumGroups; + plRandomSoundModGroup *fGroups; + std::vector fActiveList; // list of sounds we're allowed to choose + int fOldPriority; // old sound priority + hsBool fFirstTimePlay; + + virtual void IPlayNext(); + virtual void IPlayNextIfMaster(); + virtual void IStop(); + void ISetVolume(hsScalar volume); + void ISetPosition(hsPoint3); + plSound *IGetSoundPtr(); + +public: + plRandomSoundMod(); + ~plRandomSoundMod(); + + CLASSNAME_REGISTER( plRandomSoundMod ); + GETINTERFACE_ANY( plRandomSoundMod, plRandomCommandMod ); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + void SetCurrentGroup(UInt16 group); + + void ForceSoundLoadState( hsBool loaded ); + hsBool MsgReceive(plMessage* msg); + float GetVolume(); + + // EXPORT ONLY + void SetGroupInfo(UInt16 numGroups, plRandomSoundModGroup *groups) { fNumGroups = numGroups; fGroups = groups; } +}; + +#endif // plRandomSoundMod_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRCreatable.h new file mode 100644 index 00000000..57e90880 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRCreatable.h @@ -0,0 +1,32 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plCCRCreatable_inc +#define plCCRCreatable_inc + +#include "plCCRMgr.h" + + +#endif // plCCRCreatable_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRMgr.cpp new file mode 100644 index 00000000..bc0e1a51 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRMgr.cpp @@ -0,0 +1,31 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plCCRMgrBase.h" +#include "plCCRMgr.h" + +// static +plCCRMgrBase* plCCRMgrBase::fBaseInstance=nil; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRMgr.h new file mode 100644 index 00000000..0daec744 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRMgr.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plCCRMgr_h +#define plCCRMgr_h + +// +// Implementation for CCR commands +// + +#include "hsTypes.h" + +// Error constants and conversion are outside of the CCR_RELEASE define, +// So that non-CCR code can report CCR errors, and the plCCRMgr can +// share this code. +namespace plCCRError +{ + enum Errors + { + kError = hsFail, + kNotAuthorized = -2, + kNilLocalAvatar = -3, + kCCRAlreadyAllocated = -4, + kNetworkingIsDisabled = -5, + kCantFindPlayer = -6, + kInvalidLevel = -7, + kPlayerNotInAge = -8, + kVaultTimedOut = -9, + kVaultFetchFailed = -10, + kAuthTimedOut = -11 + }; +} + +#endif // plCCRMgr_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRVault.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRVault.cpp new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRVault.cpp @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRVault.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRVault.h new file mode 100644 index 00000000..a357b483 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCCR/plCCRVault.h @@ -0,0 +1,30 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plCCRVault_h +#define plCCRVault_h + + +#endif // plCCRVault_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraCreatable.h new file mode 100644 index 00000000..fa61f419 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraCreatable.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pfCameraCreatable_inc +#define pfCameraCreatable_inc + +#include "../pnFactory/plCreatable.h" + +#include "plCameraBrain.h" + +REGISTER_CREATABLE( plCameraBrain1 ); +REGISTER_CREATABLE( plCameraBrain1_Drive ); +REGISTER_CREATABLE( plCameraBrain1_Avatar ); +REGISTER_CREATABLE( plCameraBrain1_FirstPerson); +REGISTER_CREATABLE( plCameraBrain1_Fixed ); +REGISTER_CREATABLE( plCameraBrain1_Circle ); + +#include "plCameraModifier.h" + +REGISTER_CREATABLE( plCameraModifier1 ); + +#include "plInterestingModifier.h" + +REGISTER_CREATABLE( plInterestingModifier ); + +#include "plVirtualCamNeu.h" + +REGISTER_CREATABLE( plVirtualCam1 ); + + +#endif // pfCameraCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraProxy.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraProxy.cpp new file mode 100644 index 00000000..b0a316cc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraProxy.cpp @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "pfCameraProxy.h" +#include "plVirtualCamNeu.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../pnMessage/plProxyDrawMsg.h" +#include "../plScene/plSceneNode.h" + +plCameraProxy::plCameraProxy() +: plProxyGen(hsColorRGBA().Set(0.2f,0.2f,0.8f,1.f), hsColorRGBA().Set(1.f,0.5f,0.5f,1.f), 0.2f), + fOwner(nil), node(nil) +{ +} + +plCameraProxy::~plCameraProxy() +{ +} + +hsBool plCameraProxy::Init(plVirtualCam1* aud) +{ + plProxyGen::Init(aud); + + fOwner = aud; + fProxyMsgType = plProxyDrawMsg::kCamera; + + return fOwner != nil; +} + +plKey plCameraProxy::IGetNode() const +{ + if (node) + return node->GetKey(); + return nil; +} + +plDrawableSpans* plCameraProxy::ICreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + if( fOwner ) + { +// return fOwner->CreateProxy(mat, idx, addTo); + } + return nil; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraProxy.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraProxy.h new file mode 100644 index 00000000..6679d3ac --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/pfCameraProxy.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCameraProxy_inc +#define plCameraProxy_inc + +#include "../plDrawable/plProxyGen.h" + +class plVirtualCam1; +class plSceneNode; + +class plCameraProxy : public plProxyGen +{ +protected: + plVirtualCam1* fOwner; + + + virtual plDrawableSpans* ICreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo=nil); + virtual plKey IGetNode() const; +public: + plCameraProxy(); + virtual ~plCameraProxy(); + + hsBool Init(plVirtualCam1* aud); + plSceneNode* node; +}; + +#endif // plCameraProxy_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraBrain.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraBrain.cpp new file mode 100644 index 00000000..95b7e71c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraBrain.cpp @@ -0,0 +1,1846 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "plCameraBrain.h" +#include "hsTimer.h" +#include "hsResMgr.h" +#include "plRefFlags.h" +#include "plCameraModifier.h" +#include "plVirtualCamNeu.h" +#include "plgDispatch.h" +#include "../plInterp/plController.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../plScene/plSceneNode.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pnMessage/plPlayerPageMsg.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plLOSRequestMsg.h" +#include "../plMessage/plLOSHitMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "../plInputCore/plInputDevice.h" +#include "../plInputCore/plInputManager.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pfAnimation/plLineFollowMod.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plNetClient/plNetClientMgr.h" +//#define aspect_HDTV // maybe someday we'll be on the xbox... + +#ifdef aspect_HDTV +#define FOV_RATIO 1.78 +#else +#define FOV_RATIO 1.33333333 +#endif + +hsBool plCameraBrain1_FirstPerson::fDontFade = false; +hsScalar plCameraBrain1::fFallAccel = 20.0f; +hsScalar plCameraBrain1::fFallDecel = 5.0f; +hsScalar plCameraBrain1::fFallVelocity = 50.0f; +hsScalar plCameraBrain1::fFallPOAAccel = 10.0f; +hsScalar plCameraBrain1::fFallPOADecel = 10.0f; +hsScalar plCameraBrain1::fFallPOAVelocity = 50.0f; + +// basic camera brain now is a fixed brain by default. +// if it doesn't have a subject (an object) it will just look straight ahead. +// if there's a subject it will follow it. + +// the avatar and drive cameras are subclasses of the basic brain. + +plCameraBrain1::plCameraBrain1() : +fCurCamSpeed(0.0f), +fCurViewSpeed(0.0f), +fVelocity(30.0f), +fAccel(30.0f), +fDecel(30.0f), +fPOAVelocity(30.0f), +fPOAAccel(30.0f), +fPOADecel(30.0f), +fSubjectKey(nil), +fRail(nil), +fXPanLimit(0.0f), +fZPanLimit(0.0f), +fPanSpeed(0.5f), +fZoomMin(0.0f), +fZoomMax(0.0f), +fZoomRate(0.0f), +fOffsetLength(0.0f), +fOffsetPct(1.0f) +{ +} + +plCameraBrain1::plCameraBrain1(plCameraModifier1* pMod) : +fCurCamSpeed(0.0f), +fCurViewSpeed(0.0f), +fVelocity(30.0f), +fAccel(30.0f), +fDecel(30.0f), +fPOAVelocity(30.0f), +fPOAAccel(30.0f), +fPOADecel(30.0f), +fSubjectKey(nil), +fRail(nil), +fXPanLimit(0.0f), +fZPanLimit(0.0f), +fZoomMin(0.0f), +fZoomMax(0.0f), +fZoomRate(0.0f), +fOffsetLength(0.0f), +fOffsetPct(1.0f) +{ + fCamera = pMod; + pMod->SetBrain(this); + fPOAGoal.Set(0,0,0); + fGoal.Set(1,1,1); + fPOAOffset.Set(0,0,0); + fTargetMatrix.Make(&fGoal, &fPOAGoal, &hsVector3(0,0,1)); + fFlags.Clear(); +} + +plCameraBrain1::~plCameraBrain1() +{ +} + +void plCameraBrain1::AddTarget() +{ + fTargetMatrix = fCamera->GetTarget()->GetCoordinateInterface()->GetLocalToWorld(); + hsVector3 view; + fTargetMatrix.GetAxis(0, &view, 0); + fGoal = fTargetMatrix.GetTranslate(); + fPOAGoal = fGoal - view; + fCamera->SetTargetPos(fGoal); + fCamera->SetTargetPOA(fPOAGoal); +} + +// called when we are pushed on top of the camera stack (or re-activated by another popping off directly above) +void plCameraBrain1::Push(hsBool recenter) +{ + if (fFlags.IsBitSet(kRailComponent)) + { + fRail->Init(); + } + if (recenter) + plInputManager::SetRecenterMouse(false); + fOffsetPct = 1.0f; + // force update once to pop into position before we render + SetFlags(kCutPOAOnce); + SetFlags(kCutPosOnce); + Update(true); + +} + +// called when we pop off the camera stack - only if we are the current camera +void plCameraBrain1::Pop() +{ + ClearMovementFlag(S_SET_FREELOOK); +} + +// set the goal to which we want to animate the fov +void plCameraBrain1::SetFOVGoal(hsScalar h, double t) +{ + if (fFOVGoal == h || h == fCamera->GetFOVh()) + return; + + hsScalar dif = h - fCamera->GetFOVh(); + fFOVAnimRate = dif / ((hsScalar)t); + + fFOVGoal = h; + fFOVStartTime = hsTimer::GetSysSeconds(); + fFOVEndTime = fFOVStartTime + t; + + fFlags.SetBit(kAnimateFOV); +} + +// set parameters for how this camera zooms FOV based on user input (mostly for telescopes) +void plCameraBrain1::SetZoomParams(hsScalar max, hsScalar min, hsScalar rate) +{ + fZoomRate = rate; + fZoomMax = max; + fZoomMin = min; + fFlags.SetBit(kZoomEnabled); +} + + +// periodic update - forced means we are forcing an update at a non-normal time to "prime" the camera +// into position before it renders the first time (hence the fake 10 second frame delta) +void plCameraBrain1::Update(hsBool forced) +{ + double secs = hsTimer::GetDelSysSeconds(); + if (forced) + secs = 10.0f; + // is there a subject we are following? + if (GetSubject()) + { + fTargetMatrix = fCamera->GetTarget()->GetCoordinateInterface()->GetLocalToWorld(); + fGoal = fTargetMatrix.GetTranslate(); + fPOAGoal = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + fPOAGoal += fPOAOffset; + } + else + { + // get view based on current orientation (we could be animated) + if (fCamera->GetTarget()) + { + fTargetMatrix = fCamera->GetTarget()->GetCoordinateInterface()->GetLocalToWorld(); + hsVector3 view; + fTargetMatrix.GetAxis(0, &view, 0); + fGoal = fTargetMatrix.GetTranslate(); + fPOAGoal = fGoal - (view * 10); + } + } + AdjustForInput(secs); + + IMoveTowardGoal(secs); + IPointTowardGoal(secs); + if (fFlags.IsBitSet(kAnimateFOV)) + IAnimateFOV(secs); + +} + +// adjust FOV based on elapsed time +void plCameraBrain1::IAnimateFOV(double time) +{ + hsScalar dH = fFOVAnimRate * hsTimer::GetDelSysSeconds(); + + dH += fCamera->GetFOVh(); + + if ( (fFOVAnimRate < 0.0f && dH <= fFOVGoal) || + (fFOVAnimRate > 0.0f && dH >= fFOVGoal) ) + { + fFlags.ClearBit(kAnimateFOV); + dH = fFOVGoal; + } + + fCamera->SetFOVw( (hsScalar)(dH * FOV_RATIO) ); + fCamera->SetFOVh( dH ); + +} + +// move the camera's origin point (not where it is looking) toward where it is going +void plCameraBrain1::IMoveTowardGoal(double elapsedTime) +{ + hsBool current = plVirtualCam1::Instance()->IsCurrentCamera(GetCamera()); + + if (fFlags.IsBitSet(kCutPos) || fFlags.IsBitSet(kNonPhys) || !current) + { + fCamera->SetTargetPos(fGoal); + return; + } + + if (fFlags.IsBitSet(kCutPosOnce)) + { + fCamera->SetTargetPos(fGoal); + fFlags.ClearBit(kCutPosOnce); + return; + } + hsVector3 dir(fGoal - fCamera->GetTargetPos()); + hsScalar distToGoal=dir.Magnitude(); + + //smooth out stoppage... + hsScalar adjMaxVel = fVelocity; + if (distToGoal <= 5.0f && distToGoal > 0.1f) + { + hsScalar mult = (distToGoal - 5.0f)*0.1f; + adjMaxVel = fVelocity - hsABS(fVelocity*mult); + } + + + if (distToGoal > 0.0f) + dir.Normalize(); + + hsVector3 vel( dir * fCurCamSpeed ); + + if (fFlags.IsBitSet(kFalling)) + IAdjustVelocity(plCameraBrain1::fFallAccel, plCameraBrain1::fFallDecel, &dir, &vel, plCameraBrain1::fFallVelocity, distToGoal, elapsedTime); + else + if (plVirtualCam1::Instance()->fUseAccelOverride) + IAdjustVelocity(plVirtualCam1::Instance()->fAccel, plVirtualCam1::Instance()->fDecel, &dir, &vel, plVirtualCam1::Instance()->fVel, distToGoal, elapsedTime); + else + if (fFlags.IsBitSet(kPanicVelocity)) + IAdjustVelocity(fAccel, fDecel, &dir, &vel, 1000.0f, distToGoal, elapsedTime); + else + if (fFlags.IsBitSet(kRunning) && fVelocity < 16.0f) // speed up when we run if necessary + IAdjustVelocity(fAccel, fDecel, &dir, &vel, 16.0f, distToGoal, elapsedTime); + else + IAdjustVelocity(fAccel, fDecel, &dir, &vel, adjMaxVel, distToGoal, elapsedTime); + + fCurCamSpeed = vel.Magnitude(); + + hsScalar distMoved; + if (fFlags.IsBitSet(kPanicVelocity)) + distMoved = IClampVelocity(&vel, 1000.0f, elapsedTime); + else + if (fFlags.IsBitSet(kFalling)) + distMoved = IClampVelocity(&vel, plCameraBrain1::fFallVelocity, elapsedTime); + else + if (fFlags.IsBitSet(kRunning) && fVelocity < 16.0f) + distMoved = IClampVelocity(&vel, 16.0f, elapsedTime); + else + distMoved = IClampVelocity(&vel, fVelocity, elapsedTime); + + // compute final pos + if (distMoved > distToGoal) + fCamera->SetTargetPos(fGoal); + else + fCamera->SetTargetPos(fCamera->GetTargetPos() + vel); + +} + +void plCameraBrain1::SetMovementFlag(int f) +{ + fMoveFlags.SetBit(f); +} + + +void plCameraBrain1::IPointTowardGoal(double elapsedTime) +{ + hsBool current = plVirtualCam1::Instance()->IsCurrentCamera(GetCamera()); + + if (fFlags.IsBitSet(kCutPOA) || fFlags.IsBitSet(kNonPhys) || !current) + { + fCamera->SetTargetPOA(fPOAGoal); + return; + } + if (fFlags.IsBitSet(kCutPOAOnce)) + { + fCamera->SetTargetPOA(fPOAGoal); + fFlags.ClearBit(kCutPOAOnce); + return; + } + + + hsVector3 dir(fPOAGoal - fCamera->GetTargetPOA()); + hsScalar distToGoal=dir.Magnitude(); + + if (distToGoal > 0.0f) + dir.Normalize(); + + // smooth out stoppage + hsScalar adjMaxVel = fPOAVelocity; + if (distToGoal <= 5.0f && distToGoal > 0.1f) + { + hsScalar mult = (distToGoal - 5.0f)*0.1f; + adjMaxVel = fPOAVelocity - hsABS(fPOAVelocity*mult); + } + + + hsVector3 vel( dir * fCurViewSpeed ); + + if (fFlags.IsBitSet(kFalling)) + IAdjustVelocity(plCameraBrain1::fFallPOAAccel, plCameraBrain1::fFallPOADecel, &dir, &vel, plCameraBrain1::fFallPOAVelocity, distToGoal, elapsedTime); + else + if (plVirtualCam1::Instance()->fUseAccelOverride) + IAdjustVelocity(plVirtualCam1::Instance()->fAccel, plVirtualCam1::Instance()->fDecel, &dir, &vel, plVirtualCam1::Instance()->fVel, distToGoal, elapsedTime); + else + if (fFlags.IsBitSet(kPanicVelocity)) + IAdjustVelocity(fPOAAccel, fPOADecel, &dir, &vel, 1000.0f, distToGoal, elapsedTime); + else + if (fFlags.IsBitSet(kRunning) && fPOAVelocity < 16.0f) + IAdjustVelocity(fPOAAccel, fPOADecel, &dir, &vel, 16.0f, distToGoal, elapsedTime); + else + IAdjustVelocity(fPOAAccel, fPOADecel, &dir, &vel, adjMaxVel, distToGoal, elapsedTime); + + fCurViewSpeed = vel.Magnitude(); + + hsScalar distMoved; + if (fFlags.IsBitSet(kPanicVelocity)) + distMoved = IClampVelocity(&vel, 1000.0f, elapsedTime); + else + if (fFlags.IsBitSet(kFalling)) + distMoved = IClampVelocity(&vel, plCameraBrain1::fFallPOAVelocity, elapsedTime); + else + if (fFlags.IsBitSet(kRunning) && fPOAVelocity < 16.0f) + distMoved = IClampVelocity(&vel, 16.0f, elapsedTime); + else + distMoved = IClampVelocity(&vel, fPOAVelocity, elapsedTime); + + // compute final pos + if (distMoved > distToGoal) + fCamera->SetTargetPOA(fPOAGoal); + else + fCamera->SetTargetPOA(fCamera->GetTargetPOA() + vel); +} + + +void plCameraBrain1::IAdjustVelocity(hsScalar adjAccelRate, hsScalar adjDecelRate, + hsVector3* dir, hsVector3* vel, hsScalar maxSpeed, + hsScalar distToGoal, double elapsedTime) +{ + hsScalar speed = vel->Magnitude(); // save current speed + *vel = *dir * speed; // change vel to correct dir + + // compute accel/decel + hsScalar finalAccelRate; + + if (IShouldDecelerate(adjDecelRate, speed, distToGoal)) + { + finalAccelRate = -adjDecelRate; + } + else + { + finalAccelRate = adjAccelRate; + } + + if (finalAccelRate != 0) + { + // compute accel vector in the direction of the goal + hsVector3 accelVec = *dir * finalAccelRate; + accelVec = accelVec * (hsScalar)elapsedTime; + + // add acceleration to velocity + *vel = *vel + accelVec; + } + else + { + *vel = *dir * maxSpeed; + } +} + +hsScalar plCameraBrain1::IClampVelocity(hsVector3* vel, hsScalar maxSpeed, double elapsedTime) +{ + *vel = *vel * (hsScalar)elapsedTime; + maxSpeed *= (hsScalar)elapsedTime; + + // clamp speed (clamp if going negative?) + hsScalar distMoved = vel->Magnitude(); + if (distMoved > maxSpeed) + { + vel->Normalize(); + *vel = *vel * maxSpeed; + return maxSpeed; + } + return distMoved; +} + +hsBool plCameraBrain1::IShouldDecelerate(hsScalar decelSpeed, hsScalar curSpeed, hsScalar distToGoal) +{ + if (decelSpeed == 0) + // no deceleration + return false; + + // compute distance required to stop, given decel speed (in units/sec sq) + hsScalar stopTime = curSpeed / decelSpeed; + hsScalar avgSpeed = curSpeed * .5f; + hsScalar stopDist = avgSpeed * stopTime; + + return (hsABS(distToGoal) <= hsABS(stopDist)); // stopDist+avgSpeed? +} + +// +// Make adjustments to camera position based on +// user input - NOTE this is for mouse-cursor based adjustment +// +void plCameraBrain1::AdjustForInput(double secs) +{ + // move closer to camera target based on user input + if (fOffsetPct < 1.0f) + { + hsVector3 v(fPOAGoal - fGoal); + hsScalar len = v.Magnitude(); + len = len - (len * fOffsetPct); + v.Normalize(); + fGoal = fGoal + (v * len); + } +} + +void plCameraBrain1::Read(hsStream* stream, hsResMgr* mgr) +{ + hsKeyedObject::Read(stream, mgr); + fPOAOffset.Read(stream); + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kSubject ); // SceneObject + mgr->ReadKeyNotifyMe( stream, msg, plRefFlags::kActiveRef ); + + plGenRefMsg* msg2 = TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnRequest, 0, kRailComponent ); // SceneObject + mgr->ReadKeyNotifyMe( stream, msg2, plRefFlags::kActiveRef ); + + fFlags.Read(stream); + + if (fFlags.IsBitSet(kFollowLocalAvatar)) + { + if (plNetClientApp::GetInstance() && plNetClientApp::GetInstance()->GetLocalPlayer()) + SetSubject((plSceneObject*)plNetClientApp::GetInstance()->GetLocalPlayer()); + + plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); + } + fAccel = stream->ReadSwapFloat(); + fDecel = stream->ReadSwapFloat(); + fVelocity = stream->ReadSwapFloat(); + fPOAAccel = stream->ReadSwapFloat(); + fPOADecel = stream->ReadSwapFloat(); + fPOAVelocity = stream->ReadSwapFloat(); + fXPanLimit = stream->ReadSwapFloat(); + fZPanLimit = stream->ReadSwapFloat(); + fZoomRate = stream->ReadSwapFloat(); + fZoomMin = stream->ReadSwapFloat(); + fZoomMax = stream->ReadSwapFloat(); + +} + +void plCameraBrain1::Write(hsStream* stream, hsResMgr* mgr) +{ + hsKeyedObject::Write(stream, mgr); + fPOAOffset.Write(stream); + mgr->WriteKey(stream, GetSubject()); + mgr->WriteKey(stream, fRail); + fFlags.Write(stream); + stream->WriteSwapFloat(fAccel); + stream->WriteSwapFloat(fDecel); + stream->WriteSwapFloat(fVelocity); + stream->WriteSwapFloat(fPOAAccel); + stream->WriteSwapFloat(fPOADecel); + stream->WriteSwapFloat(fPOAVelocity); + stream->WriteSwapFloat(fXPanLimit); + stream->WriteSwapFloat(fZPanLimit); + stream->WriteSwapFloat(fZoomRate); + stream->WriteSwapFloat(fZoomMin); + stream->WriteSwapFloat(fZoomMax); +} +hsBool plCameraBrain1::MsgReceive(plMessage* msg) +{ + plCameraMsg* pCamMsg = plCameraMsg::ConvertNoRef(msg); + if (pCamMsg) + { + if (pCamMsg->Cmd(plCameraMsg::kStartZoomIn)) + { + fFlags.SetBit(kAnimateFOV); + fFOVGoal = fZoomMin; + fFOVAnimRate = -1*fZoomRate; + return true; + } + else + if (pCamMsg->Cmd(plCameraMsg::kStartZoomOut)) + { + fFlags.SetBit(kAnimateFOV); + fFOVGoal = fZoomMax; + fFOVAnimRate = fZoomRate; + return true; + } + else + if (pCamMsg->Cmd(plCameraMsg::kStopZoom)) + { + fFlags.ClearBit(kAnimateFOV); + return true; + } + else + if (pCamMsg->Cmd(plCameraMsg::kNonPhysOn)) + { + fFlags.SetBit(kNonPhys); + return true; + } + else + if (pCamMsg->Cmd(plCameraMsg::kNonPhysOff)) + { + fFlags.ClearBit(kNonPhys); + return true; + } + } + plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); + if (pRefMsg) + { + if (pRefMsg->fType == kSubject) + { + if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + SetSubject((plSceneObject*)pRefMsg->GetRef()); + else + SetSubject(nil); + return true; + } + else + if (pRefMsg->fType == kRailComponent) + { + if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fFlags.SetBit(kRailComponent); + fRail = (plRailCameraMod*)pRefMsg->GetRef(); + } + else + { + fRail = nil; + fFlags.ClearBit(kRailComponent); + } + return true; + } + } + plPlayerPageMsg* pPMsg = plPlayerPageMsg::ConvertNoRef(msg); + if (pPMsg) + { + if (pPMsg->fPlayer == plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + { + if (pPMsg->fUnload) + { + if (fFlags.IsBitSet(kFollowLocalAvatar)) + SetSubject(nil); + } + else + { + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kSubject ); // SceneObject + hsgResMgr::ResMgr()->AddViaNotify(pPMsg->fPlayer, msg, plRefFlags::kPassiveRef); + + fFlags.SetBit(kCutPosOnce); + fFlags.SetBit(kCutPOAOnce); + } + } + return true; + } + plControlEventMsg* pCMsg = plControlEventMsg::ConvertNoRef(msg); + if (pCMsg) + { + if (pCMsg->ControlActivated()) + { + SetMovementFlag(pCMsg->GetControlCode()); + if (pCMsg->GetControlCode() == S_SET_FREELOOK) + { + plInputManager::SetRecenterMouse(true); + plMouseDevice::HideCursor(); + } + if (pCMsg->GetControlCode() == B_CAMERA_ZOOM_IN && fFlags.IsBitSet(kZoomEnabled)) + { + fFlags.SetBit(kAnimateFOV); + fFOVGoal = fZoomMin; + fFOVAnimRate = -1*fZoomRate; + fFOVEndTime = hsTimer::GetSysSeconds() + 60; + return true; + } + else + if (pCMsg->GetControlCode() == B_CAMERA_ZOOM_OUT && fFlags.IsBitSet(kZoomEnabled)) + { + fFlags.SetBit(kAnimateFOV); + fFOVGoal = fZoomMax; + fFOVAnimRate = fZoomRate; + fFOVEndTime = hsTimer::GetSysSeconds() + 60; + return true; + } + else + if (pCMsg->GetControlCode() == B_CAMERA_RECENTER) + { + if (fFlags.IsBitSet(kZoomEnabled)) + { + fFlags.SetBit(kAnimateFOV); + fFOVGoal = fZoomMin + ((fZoomMax - fZoomMin) / 2); + if (fCamera->GetFOVw() >= fFOVGoal) + fFOVAnimRate = -1*fZoomRate; + else + fFOVAnimRate = fZoomRate; + fFOVEndTime = hsTimer::GetSysSeconds() + 60; + } + return true; + } + } + else + { + ClearMovementFlag(pCMsg->GetControlCode()); + if (pCMsg->GetControlCode() == S_SET_FREELOOK) + { + plInputManager::SetRecenterMouse(false); + plMouseDevice::ShowCursor(); + } + else + if ( (pCMsg->GetControlCode() == B_CAMERA_ZOOM_IN || pCMsg->GetControlCode() == B_CAMERA_ZOOM_OUT) + && fFlags.IsBitSet(kZoomEnabled) ) + { + fFlags.ClearBit(kAnimateFOV); + return true; + } + + } + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + +plSceneObject* plCameraBrain1::GetSubject() +{ + if (fSubjectKey) + return plSceneObject::ConvertNoRef(fSubjectKey->ObjectIsLoaded()); + return nil; +} + +void plCameraBrain1::SetSubject(plSceneObject* sub) +{ + if (sub) + fSubjectKey = sub->GetKey(); + else + fSubjectKey = nil; +} + +// +// +// new drive mode brain +// + + +hsScalar plCameraBrain1_Drive::fTurnRate = 100.0f; +hsScalar plCameraBrain1_Drive::fAcceleration = 200.0f; +hsScalar plCameraBrain1_Drive::fDeceleration = 200.0f; +hsScalar plCameraBrain1_Drive::fMaxVelocity = 100.0f; + + +// constructor +plCameraBrain1_Drive::plCameraBrain1_Drive() : plCameraBrain1() +{ + fGoal.Set(100,100,100); + fPOAGoal.Set(0,0,0); + fUp.Set(0,0,1); + fCamera->SetTargetPos(fGoal); + fCamera->SetTargetPOA(fPOAGoal); + fLastTime = 0.f; +} + +plCameraBrain1_Drive::plCameraBrain1_Drive(plCameraModifier1* pMod) : plCameraBrain1(pMod) +{ + fGoal.Set(100,100,100); + fPOAGoal.Set(0,0,0); + fUp.Set(0,0,1); + fCamera->SetTargetPos(fGoal); + fCamera->SetTargetPOA(fPOAGoal); + fLastTime = 0.f; +} + +// destructor +plCameraBrain1_Drive::~plCameraBrain1_Drive() +{ +} + + +void plCameraBrain1_Drive::Push(hsBool recenter) +{ + plCameraBrain1::Push(recenter); + plInputManager::SetRecenterMouse(true); + fLastTime = hsTimer::GetSeconds(); +} +void plCameraBrain1_Drive::Pop() +{ + plInputManager::SetRecenterMouse(false); +} + +// +// Update Method +// +void plCameraBrain1_Drive::Update(hsBool forced) +{ + + fTargetMatrix.Make(&fGoal, &fPOAGoal, &(-1*fUp)); + + // update our desired position: + double time = hsTimer::GetSeconds(); + hsScalar eTime = (hsScalar)(time-fLastTime); + if(eTime > 0.01f) + eTime = 0.01f; + fLastTime = time; + hsPoint3 cameraPos = fCamera->GetTargetPos(); + hsVector3 view, up, right; + + fTargetMatrix.GetAxis(&view,&up,&right); + hsScalar delta = 5.0f * eTime; + + // adjust speed + if (HasMovementFlag(B_CAMERA_DRIVE_SPEED_UP)) + { + plCameraBrain1_Drive::fAcceleration += delta; + plCameraBrain1_Drive::fDeceleration += delta; + plCameraBrain1_Drive::fMaxVelocity += delta; + } + if (HasMovementFlag(B_CAMERA_DRIVE_SPEED_DOWN)) + { + plCameraBrain1_Drive::fAcceleration -= delta; + plCameraBrain1_Drive::fDeceleration -= delta; + plCameraBrain1_Drive::fMaxVelocity -= delta; + + if (plCameraBrain1_Drive::fAcceleration <= 0.0f) + plCameraBrain1_Drive::fAcceleration = 0.0f; + + if (plCameraBrain1_Drive::fTurnRate <= 0.0f) + plCameraBrain1_Drive::fTurnRate = 0.0f; + + if (plCameraBrain1_Drive::fDeceleration <= 0.0f) + plCameraBrain1_Drive::fDeceleration = 0.0f; + + if (plCameraBrain1_Drive::fMaxVelocity <= 0.0f) + plCameraBrain1_Drive::fMaxVelocity = 0.0f; + + } + if (plVirtualCam1::Instance()->fUseAccelOverride) + { + fMaxVelocity = plVirtualCam1::Instance()->fVel; + } + + hsScalar speed = fMaxVelocity; + hsScalar turn = 1.0f; + + if (HasMovementFlag(B_CONTROL_MODIFIER_FAST)) + { + turn *= 0.25; + speed *= 10; + } + if (HasMovementFlag(B_CAMERA_MOVE_FORWARD)) + { + cameraPos += view * speed * eTime; + } + if (HasMovementFlag(B_CAMERA_MOVE_BACKWARD)) + { + cameraPos += view * speed * eTime * -1; + } + if (HasMovementFlag(B_CAMERA_MOVE_LEFT)) + { + cameraPos += right * speed * eTime * -1; + } + if (HasMovementFlag(B_CAMERA_MOVE_RIGHT)) + { + cameraPos += right * speed * eTime; + } + if (HasMovementFlag(B_CAMERA_MOVE_DOWN)) + { + cameraPos += up * speed * eTime * -1; + } + if (HasMovementFlag(B_CAMERA_MOVE_UP)) + { + cameraPos += up * speed * eTime; + } + fGoal = cameraPos; + + IMoveTowardGoal(eTime); + hsMatrix44 rot; + + // make sure camera is perpindicular to absolute up + fTargetMatrix.fMap[0][2] = 0.0f; + fTargetMatrix.fMap[1][2] = 0.0f; + + fTargetMatrix.GetAxis(&view,&up,&right); + + if ( HasMovementFlag( B_CAMERA_ROTATE_RIGHT ) || HasMovementFlag( B_CAMERA_ROTATE_LEFT ) ) + { + hsQuat q(turn * fTurnRate * eTime * deltaX, &up); + q.NormalizeIfNeeded(); + q.MakeMatrix(&rot); + ClearMovementFlag( B_CAMERA_ROTATE_RIGHT ); + ClearMovementFlag( B_CAMERA_ROTATE_LEFT ); + fTargetMatrix = rot * fTargetMatrix; + } + rot.Reset(); + + if ( HasMovementFlag( B_CAMERA_ROTATE_UP ) || HasMovementFlag(B_CAMERA_ROTATE_DOWN) ) + { + hsQuat q(turn * fTurnRate* eTime * deltaY, &right); + q.NormalizeIfNeeded(); + q.MakeMatrix(&rot); + ClearMovementFlag( B_CAMERA_ROTATE_UP ); + ClearMovementFlag( B_CAMERA_ROTATE_DOWN ); + fTargetMatrix = rot * fTargetMatrix; + } + + fTargetMatrix.GetAxis(&view,&up,&right); + hsPoint3 at(fGoal + (view * 2.0f)); + + // Now passing in Up for up parameter, instead of down. mf_flip_up - mf + fTargetMatrix.MakeCamera(&fGoal, &at, &up); + + fPOAGoal = at; + fUp = up; + + fCamera->SetTargetPos(fGoal); + fCamera->SetTargetPOA(fPOAGoal); +} + +hsBool plCameraBrain1_Drive::MsgReceive(plMessage* msg) +{ + plMouseEventMsg* pMouseMsg = plMouseEventMsg::ConvertNoRef(msg); + if( pMouseMsg ) + { + if (pMouseMsg->GetDX() > 0.4 || pMouseMsg->GetDX() < -0.4) + { + bDisregardY = true; + return true; + } + else + if (pMouseMsg->GetDY() > 0.4 || pMouseMsg->GetDY() < -0.4) + { + bDisregardX = true; + return true; + } + + if (bDisregardY && pMouseMsg->GetDY() != 0.0) + { + bDisregardY = false; + return true; + } + + if (bDisregardX && pMouseMsg->GetDX() != 0.0) + { + bDisregardX = false; + return true; + } + if (pMouseMsg->GetDX() < 0) + { + SetMovementFlag( B_CAMERA_ROTATE_RIGHT ); + deltaX = pMouseMsg->GetDX(); + } + else + if (pMouseMsg->GetDX() > 0) + { + SetMovementFlag( B_CAMERA_ROTATE_LEFT ); + deltaX = pMouseMsg->GetDX(); + } + else + if (pMouseMsg->GetDY() > 0) + { + SetMovementFlag( B_CAMERA_ROTATE_DOWN ); + deltaY = pMouseMsg->GetDY(); + } + else + if (pMouseMsg->GetDY() < 0) + { + deltaY = pMouseMsg->GetDY(); + SetMovementFlag( B_CAMERA_ROTATE_UP ); + } + + return true; + } + + return plCameraBrain1::MsgReceive(msg); +} + + + +// +// +// +// new simplified avatar camera + +// constructor +plCameraBrain1_Avatar::plCameraBrain1_Avatar() : plCameraBrain1() +{ + bObscured = false; + fOffset.Set(0,0,0); + fPOAOffset.Set(0,0,0); + fFaded = false; +} + +plCameraBrain1_Avatar::plCameraBrain1_Avatar(plCameraModifier1* pMod) : plCameraBrain1(pMod) +{ + bObscured = false; + fOffset.Set(0,0,0); + fPOAOffset.Set(0,0,0); + fFaded = false; +} + + +// destructor +plCameraBrain1_Avatar::~plCameraBrain1_Avatar() +{ + if (!plNetClientMgr::GetInstance()) + return; + + if (fFaded) + { + plCameraTargetFadeMsg* pMsg = TRACKED_NEW plCameraTargetFadeMsg; + pMsg->SetFadeOut(false); + pMsg->SetSubjectKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE); + pMsg->AddReceiver(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + plgDispatch::MsgSend(pMsg); + } + +} + +void plCameraBrain1_Avatar::Pop() +{ + bObscured = false; + if (fFaded) + ISendFadeMsg(false); + plCameraBrain1::Pop(); +} + +void plCameraBrain1_Avatar::Push(hsBool recenter) +{ + bObscured = false; + fFallTimer = 0.0f; + plCameraBrain1::Push(recenter); +} + + +// +// Update Method +// +void plCameraBrain1_Avatar::Update(hsBool forced) +{ + if (!GetSubject()) + return; + if (HasFlag(kBeginFalling) && hsTimer::GetSysSeconds() >= fFallTimer) + { + fFallTimer = 0.0f; + + fFlags.SetBit(kFalling); + fFlags.ClearBit(kBeginFalling); + plVirtualCam1::Instance()->SetFlags(plVirtualCam1::kFalling); + plVirtualCam1::Instance()->StartUnPan(); + fOffsetPct = 1.0f; + + } + // determine elapsed time; + double secs = hsTimer::GetDelSysSeconds(); + if (forced) + secs = 10.0f; + + if (fFlags.IsBitSet(kIsTransitionCamera)) + { + if (GetKey()) + hsStatusMessageF("%s thinks it's the transition camera\n",GetKeyName()); + } + else + { + CalculatePosition(); + AdjustForInput(secs); + } + + IMoveTowardGoal(secs); + IPointTowardGoal(secs); + + if (fFlags.IsBitSet(kAnimateFOV)) + IAnimateFOV(secs); +} + +// +// Calculate the best position for the camera in basic follow mode: +// +void plCameraBrain1_Avatar::CalculatePosition() +{ + if (!GetSubject() || !GetSubject()->GetCoordinateInterface()) + return; + + // get target pos + hsPoint3 targetFrom = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + + // get view normal + hsVector3 view = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + hsVector3 right = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kRight); + + // compute camera position and interest + if (HasFlag(kWorldspacePos)) + { + fGoal = targetFrom + GetOffset(); + } + else + { + fGoal = targetFrom; + if (!fFlags.IsBitSet(kFalling)) + { + fGoal = fGoal - view * GetOffset().fY; + fGoal = fGoal - right * GetOffset().fX; + fGoal.fZ += GetOffset().fZ; + } + else + { + fGoal = fGoal - view * 0.5; + fGoal.fZ += 20; + } + } + + if (HasFlag(kWorldspacePOA)) + { + fPOAGoal = targetFrom + GetPOAOffset(); + } + else + { + fPOAGoal = targetFrom; + //if (!fFlags.IsBitSet(kFalling)) + { + fPOAGoal = fPOAGoal - view * GetPOAOffset().fY; + fPOAGoal = fPOAGoal - right * GetPOAOffset().fX; + fPOAGoal.fZ += GetPOAOffset().fZ; + } + } + + // check to see if we've lagged too far behind + hsVector3 dist(fCamera->GetTargetPos() - fGoal); + if (dist.MagnitudeSquared() > (4*fOffset.MagnitudeSquared())) + SetFlags(kPanicVelocity); + else + ClearFlags(kPanicVelocity); + + // check LOS + if (GetCamera()->GetKey() && fFlags.IsBitSet(kMaintainLOS) && (plVirtualCam1::Instance()->GetCurrentStackCamera() == GetCamera())) + { + plLOSRequestMsg* pMsg = TRACKED_NEW plLOSRequestMsg( GetCamera()->GetKey(), fPOAGoal, fGoal, plSimDefs::kLOSDBCameraBlockers, + plLOSRequestMsg::kTestClosest, plLOSRequestMsg::kReportHitOrMiss); + plgDispatch::MsgSend( pMsg ); + } + + if (bObscured) + { + fGoal = fHitPoint + (fHitNormal * 0.5f); + + hsVector3 newOff(fGoal - fPOAGoal); + hsVector3 actualOff(fCamera->GetTargetPos() - fPOAGoal); + if (newOff.MagnitudeSquared() <= 16.0f && actualOff.MagnitudeSquared() <= 16.0f && !fFaded) + { + ISendFadeMsg(true); + fFaded = true; + } + else + if (newOff.MagnitudeSquared() >= 16.0f && fFaded) + { + ISendFadeMsg(false); + fFaded = false; + } + } + else + if (fFaded && !bObscured) + { + ISendFadeMsg(false); + fFaded = false; + } +} + + +void plCameraBrain1_Avatar::IHandleObstacle() +{ + // swing around the 'obstacle' +} + +void plCameraBrain1_Avatar::ISendFadeMsg(hsBool fade) +{ + if (plVirtualCam1::IsCurrentCamera(GetCamera())) + { + if (fade) + plVirtualCam1::AddMsgToLog("current camera sending Fade Out message to Avatar"); + else + plVirtualCam1::AddMsgToLog("current camera sending Fade In message to Avatar"); + + plCameraTargetFadeMsg* pMsg = TRACKED_NEW plCameraTargetFadeMsg; + pMsg->SetFadeOut(fade); + pMsg->SetSubjectKey(GetSubject()->GetKey()); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE); + pMsg->AddReceiver(GetSubject()->GetKey()); + plgDispatch::MsgSend(pMsg); + } +} + +hsBool plCameraBrain1_Avatar::MsgReceive(plMessage* msg) +{ + plLOSHitMsg *pLOSMsg = plLOSHitMsg::ConvertNoRef( msg ); + if( pLOSMsg ) + { + bObscured = !pLOSMsg->fNoHit; + fHitPoint = pLOSMsg->fHitPoint; + fHitNormal = pLOSMsg->fNormal; + if (pLOSMsg->fHitFlags & plSimDefs::kLOS_CameraAvoidObject) + fObstacle = (plSceneObject*)pLOSMsg->fObj->GetObjectPtr(); + else + fObstacle = nil; + return true; + } + plMouseEventMsg* pMouseMsg = plMouseEventMsg::ConvertNoRef(msg); + if( pMouseMsg ) + { + if (pMouseMsg->GetButton() == kWheelPos || pMouseMsg->GetButton() == kWheelNeg) + { + if (fFlags.IsBitSet(kFalling)) + return true; + + fOffsetPct += -1 * ( (pMouseMsg->GetWheelDelta() / 24) * 0.01f); + if (fOffsetPct > 1.0f) + fOffsetPct = 1.0f; + else + if (fOffsetPct < 0.1f) + fOffsetPct = 0.1f; + return true; + } + } + plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); + if (pRefMsg) + { + if (pRefMsg->fType == kSubject) + { + if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + SetSubject((plSceneObject*)pRefMsg->GetRef()); + plSceneObject* avSO = nil; + if (plNetClientMgr::GetInstance()) + avSO = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); + + if (GetSubject() == avSO) + { + plArmatureMod* avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + avMod->RegisterForBehaviorNotify(GetKey()); + } + } + else + SetSubject(nil); + return true; + } + } + plAvatarBehaviorNotifyMsg *behNotifymsg = plAvatarBehaviorNotifyMsg::ConvertNoRef(msg); + if (behNotifymsg) + { + if (behNotifymsg->fType == plHBehavior::kBehaviorTypeFall && fFlags.IsBitSet(kVerticalWhenFalling)) + { + if (behNotifymsg->state) + { + SetFlags(kBeginFalling); + fFallTimer = hsTimer::GetSysSeconds() + plVirtualCam1::fFallTimerDelay; + } + else + if (!behNotifymsg->state) + { + if (fFlags.IsBitSet(kFalling)) + { plVirtualCam1::Instance()->ClearFlags(plVirtualCam1::kFalling); + fFlags.ClearBit(kFalling); + } + else + { + ClearFlags(kBeginFalling); + fFallTimer = 0.0f; + } + } + } + else + if (behNotifymsg->fType == plHBehavior::kBehaviorTypeFall) + { + if (behNotifymsg->state && fFlags.IsBitSet(kSpeedUpWhenRunning)) + fFlags.SetBit(kRunning); + else + fFlags.ClearBit(kRunning); + } + return true; + } + return plCameraBrain1::MsgReceive(msg); +} + +void plCameraBrain1_Avatar::Read(hsStream* stream, hsResMgr* mgr) +{ + plCameraBrain1::Read(stream, mgr); + fOffset.Read(stream); + SetFlags(kCutPOA); + + if (fFlags.IsBitSet(kFollowLocalAvatar) && GetSubject()) + { + plSceneObject* avSO = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); + if (GetSubject() == avSO) + { + plArmatureMod* avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + avMod->RegisterForBehaviorNotify(GetKey()); + } + } + +} + +void plCameraBrain1_Avatar::Write(hsStream* stream, hsResMgr* mgr) +{ + plCameraBrain1::Write(stream, mgr); + fOffset.Write(stream); +} + + +// +// first person cam, derived from avatar cam - +// only difference is push() & pop() fade avatar in/out +// + +plCameraBrain1_FirstPerson::plCameraBrain1_FirstPerson() : plCameraBrain1_Avatar() +{ + fPosNode = nil; +} + +plCameraBrain1_FirstPerson::plCameraBrain1_FirstPerson(plCameraModifier1* pMod) : plCameraBrain1_Avatar(pMod) +{ +} + +// destructor +plCameraBrain1_FirstPerson::~plCameraBrain1_FirstPerson() +{ +} + +hsBool plCameraBrain1_FirstPerson::MsgReceive(plMessage* msg) +{ + + plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); + if (pRefMsg) + { + if (pRefMsg->fType == kSubject) + { + if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fPosNode = nil; + SetSubject((plSceneObject*)pRefMsg->GetRef()); + // are we the built-in 1st person camera? If we are, change our subject pointer to FPCameraOrigin node on the avatar + plUoid U(kDefaultCameraMod1_KEY); + plKey pKey = hsgResMgr::ResMgr()->FindKey(U); + if (pKey && pKey == fCamera->GetKey()) + { + const plCoordinateInterface* ci = GetSubject()->GetCoordinateInterface(); + for (int i = 0; i < ci->GetNumChildren(); i++) + { + plSceneObject* child = (plSceneObject*)ci->GetChild(i)->GetOwner(); + if (child) + { + const char* name = child->GetKeyName(); + if (stricmp(name, "FPCameraOrigin") == 0) + { + fPosNode = child; + SetOffset(hsVector3(0,0,0)); + SetPOAOffset(hsVector3(0,-10,0)); + return true; + } + } + } + } + } + else + { + fPosNode = nil; + SetSubject(nil); + } + return true; + } + } + plMouseEventMsg* pMouseMsg = plMouseEventMsg::ConvertNoRef(msg); + if( pMouseMsg ) + { + if (pMouseMsg->GetButton() == kWheelPos || pMouseMsg->GetButton() == kWheelNeg) + { + return true; + } + } + return plCameraBrain1_Avatar::MsgReceive(msg); +} + +void plCameraBrain1_FirstPerson::CalculatePosition() +{ + // get target pos + hsPoint3 targetFrom; + if (fPosNode) + targetFrom = fPosNode->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + else + targetFrom = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + + // get view normal + hsVector3 view = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + hsVector3 right = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kRight); + + // compute camera position and interest + if (HasFlag(kWorldspacePos)) + { + fGoal = targetFrom + GetOffset(); + } + else + { + fGoal = targetFrom; + fGoal = fGoal - view * GetOffset().fY; + fGoal = fGoal - right * GetOffset().fX; + fGoal.fZ += GetOffset().fZ; + } + + if (HasFlag(kWorldspacePOA)) + { + fPOAGoal = targetFrom + GetPOAOffset(); + } + else + { + fPOAGoal = targetFrom; + fPOAGoal = fPOAGoal - view * GetPOAOffset().fY; + fPOAGoal = fPOAGoal - right * GetPOAOffset().fX; + fPOAGoal.fZ += GetPOAOffset().fZ; + } + + // check to see if we've lagged too far behind + hsVector3 dist(fCamera->GetTargetPos() - fGoal); + if (dist.MagnitudeSquared() > (4*fOffset.MagnitudeSquared())) + SetFlags(kPanicVelocity); + else + ClearFlags(kPanicVelocity); + + // check LOS + if (GetCamera()->GetKey() && fFlags.IsBitSet(kMaintainLOS) && (plVirtualCam1::Instance()->GetCurrentStackCamera() == GetCamera())) + { + plLOSRequestMsg* pMsg = TRACKED_NEW plLOSRequestMsg( GetCamera()->GetKey(), fPOAGoal, fGoal, plSimDefs::kLOSDBCameraBlockers, + plLOSRequestMsg::kTestClosest, plLOSRequestMsg::kReportHitOrMiss); + plgDispatch::MsgSend( pMsg ); + } + + if (bObscured) + { + fGoal = fHitPoint + (fHitNormal * 0.5f); + + hsVector3 newOff(fGoal - fPOAGoal); + hsVector3 actualOff(fCamera->GetTargetPos() - fPOAGoal); + if (newOff.MagnitudeSquared() <= 16.0f && actualOff.MagnitudeSquared() <= 16.0f && !fFaded) + { + ISendFadeMsg(true); + fFaded = true; + } + else + if (newOff.MagnitudeSquared() >= 16.0f && fFaded) + { + ISendFadeMsg(false); + fFaded = false; + } + } + else + if (fFaded && !bObscured) + { + ISendFadeMsg(false); + fFaded = false; + } +} + + +void plCameraBrain1_FirstPerson::Push(hsBool recenter) +{ + if (!GetSubject()) + return; + + if (plCameraBrain1_FirstPerson::fDontFade) + return; + plEnableMsg* pMsg = TRACKED_NEW plEnableMsg; + pMsg->SetCmd(plEnableMsg::kDisable); + pMsg->AddType(plEnableMsg::kDrawable); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + pMsg->AddReceiver(GetSubject()->GetKey()); + pMsg->SetSender(GetCamera()->GetKey()); + plgDispatch::MsgSend(pMsg); + plCameraBrain1::Push(recenter); + + ISendFadeMsg(true); +} + +void plCameraBrain1_FirstPerson::Pop() +{ + + plCameraBrain1::Pop(); + if (!GetSubject()) + return; + + ISendFadeMsg(false); +} + +// +// true fixed cam, derived from basic cam - +// Has a separate interest point object it maintains +// + +plCameraBrain1_Fixed::plCameraBrain1_Fixed() : plCameraBrain1() +{ + fTargetPoint= nil; +} + +plCameraBrain1_Fixed::plCameraBrain1_Fixed(plCameraModifier1* pMod) : plCameraBrain1(pMod) +{ + fTargetPoint = nil; +} + +// destructor +plCameraBrain1_Fixed::~plCameraBrain1_Fixed() +{ +} + +void plCameraBrain1_Fixed::Read(hsStream* stream, hsResMgr* mgr) +{ + plCameraBrain1::Read(stream, mgr); + mgr->ReadKeyNotifyMe( stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, 99), plRefFlags::kPassiveRef); +} + +void plCameraBrain1_Fixed::Write(hsStream* stream, hsResMgr* mgr) +{ + plCameraBrain1::Write(stream, mgr); + mgr->WriteKey(stream, fTargetPoint); +} + +void plCameraBrain1_Fixed::Update(hsBool forced) +{ + double secs = hsTimer::GetDelSysSeconds(); + if (forced) + secs = 10.0f; + + if (!fFlags.IsBitSet(kIsTransitionCamera)) + { + if(fTargetPoint) + fTargetPoint->GetBrain()->Update(); + + if (GetSubject()) + { + fTargetMatrix = fCamera->GetTarget()->GetCoordinateInterface()->GetLocalToWorld(); + fGoal = fTargetMatrix.GetTranslate(); + + // get target pos + hsPoint3 targetFrom = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + + // get view normal + hsVector3 view = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + hsVector3 right = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kRight); + + // compute camera position and interest + if (HasFlag(kWorldspacePOA)) + { + fPOAGoal = targetFrom + GetPOAOffset(); + } + else + { + fPOAGoal = targetFrom; + fPOAGoal = fPOAGoal - view * GetPOAOffset().fY; + fPOAGoal = fPOAGoal - right * GetPOAOffset().fX; + fPOAGoal.fZ += GetPOAOffset().fZ; + } + + } + else + { + if (fCamera->GetTarget()) + { + fTargetMatrix = fCamera->GetTarget()->GetCoordinateInterface()->GetLocalToWorld(); + hsVector3 view; + hsVector3 up; + fTargetMatrix.GetAxis(0, &view, &up); + fGoal = fTargetMatrix.GetTranslate(); + if (fTargetPoint) + fPOAGoal = fTargetPoint->GetBrain()->GetGoal(); + else + fPOAGoal = fGoal - (view * 10); + } + } + if (fFlags.IsBitSet(kRailComponent) && fRail) + { + if (fCurCamSpeed == 0) + fCurCamSpeed = 1.0f; + if (forced) + fGoal = fRail->GetGoal(10, 10); + else + fGoal = fRail->GetGoal(secs, fCurCamSpeed); + } + AdjustForInput(secs); + } + IMoveTowardGoal(secs); + IPointTowardGoal(secs); + if (fFlags.IsBitSet(kAnimateFOV)) + IAnimateFOV(secs); +} + +void plCameraBrain1_Fixed::CalculatePosition() +{ +} + +hsBool plCameraBrain1_Fixed::MsgReceive(plMessage* msg) +{ + plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); + if (pRefMsg) + { + if (pRefMsg->GetContext() == plRefMsg::kOnCreate && + pRefMsg->fType == 99) + { + fTargetPoint = (plCameraModifier1*)(pRefMsg->GetRef()); + } + } + return (plCameraBrain1::MsgReceive(msg)); +} + + + +// +// +// +// circle camera crap +static const hsScalar kTwoPI = 2.0f*hsScalarPI; + +plCameraBrain1_Circle::plCameraBrain1_Circle() : plCameraBrain1_Fixed() +{ + fCircleFlags = 0; + fCenterObject = nil; + fCenter.Set(0,0,0); + fCurRad = fGoalRad = 1; + fPOAObj = nil; + fCirPerSec = 0.25f; +} +plCameraBrain1_Circle::plCameraBrain1_Circle(plCameraModifier1* pMod) : plCameraBrain1_Fixed(pMod) +{ + fCircleFlags = 0; + fCenterObject = nil; + fCenter.Set(0,0,0); + fCurRad = fGoalRad = 1; + fPOAObj = nil; + fCirPerSec = 0.25f; +} + +plCameraBrain1_Circle::~plCameraBrain1_Circle() +{ +} + +// +// +// +void plCameraBrain1_Circle::Update(hsBool forced) +{ + double secs = hsTimer::GetDelSysSeconds(); + if (forced) + secs = 10.0f; + + if (!GetSubject() || !GetSubject()->GetCoordinateInterface()) + return; + hsPoint3 sub = GetSubject()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + fGoal = IGetClosestPointOnCircle(&sub); + + if (fPOAObj && fPOAObj->GetCoordinateInterface()) + fPOAGoal = fPOAObj->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + else + fPOAGoal = GetCenterPoint(); + + fPOAGoal += fPOAOffset; + + if (HasFlag(kCutPosOnce)) + { + fGoal = MoveTowardsFromGoal(&fCamera->GetTargetPos(), secs, true); + fFlags.ClearBit(kCutPos); + } + else + { + fGoal = MoveTowardsFromGoal(&fCamera->GetTargetPos(), secs); + fFlags.SetBit(kCutPos); + } + AdjustForInput(secs); + + IMoveTowardGoal(secs); + IPointTowardGoal(secs); + if (fFlags.IsBitSet(kAnimateFOV)) + IAnimateFOV(secs); +} + +// +// keep us on the circle +// +hsPoint3 plCameraBrain1_Circle::MoveTowardsFromGoal(const hsPoint3* fromGoal, double secs, hsBool warp) +{ + + if (fCurRad != fGoalRad) + { + hsScalar dist = hsABS(fGoalRad-fCurRad); + + hsAssert(dist>=0 && dist<=kTwoPI, "illegal radian diff"); + hsBool mustWrap = (dist > hsScalarPI); // go opposite direction for shortcut and wrap + + // compute speed + hsScalar speed; + if (warp) + speed = (hsScalar)(kTwoPI * 100 * secs); + else + speed = (hsScalar)(kTwoPI * fCirPerSec * secs); + + // move towards goalRad + hsAssert(fCurRad>=0 && fCurRad<=kTwoPI, "illegal radian value"); + + if (fCurRadfGoalRad) + fCurRad=fGoalRad; + } + } + else + { + if (mustWrap) + { + fCurRad+=speed; + hsBool didWrap=false; + while(fCurRad>kTwoPI) + { + didWrap=true; + fCurRad-=kTwoPI; + } + if (fCurRad>fGoalRad && didWrap) + fCurRad=fGoalRad; + } + else + { + fCurRad-=speed; + if (fCurRad=0 && fCurRad<=kTwoPI, "illegal radian value"); + + hsPoint3 x; + x = GetCenterPoint() + hsVector3((hsScalar)hsCosine(fCurRad)*fRadius, (hsScalar)hsSine(fCurRad)*fRadius, 0.0f); + x.fZ = fCamera->GetTargetPos().fZ; + return x; +} + +hsPoint3 plCameraBrain1_Circle::IGetClosestPointOnCircle(const hsPoint3* toThis) +{ + hsPoint3 center=GetCenterPoint(); + hsPoint3 p(toThis->fX, toThis->fY, center.fZ); // Move to plane of circle + hsVector3 v; + if (!(GetCircleFlags() & kFarthest) ) + { + v = hsVector3(&p, ¢er); + } + else + { + // farthest + v = hsVector3(¢er, &p); + } + v.Normalize(); + fGoalRad = (hsScalar)atan2(v.fY, v.fX); // -pi to pi + hsAssert(fGoalRad>=-hsScalarPI && fGoalRad<=hsScalarPI, "Illegal atan2 val"); + if (fGoalRad<0) + fGoalRad = kTwoPI + fGoalRad; // 0 to 2pi + hsAssert(fGoalRad>=0 && fGoalRad<=kTwoPI, "Illegal atan2 val"); + v = v * fRadius; + center = center + v; + center.fZ = fCamera->GetTargetPos().fZ; + return (center); +} + + +hsPoint3 plCameraBrain1_Circle::GetCenterPoint() +{ + if (fCenterObject && fCenterObject->GetCoordinateInterface()) + { + return fCenterObject->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + } + return fCenter; +} + +void plCameraBrain1_Circle::Write(hsStream* stream, hsResMgr* mgr) +{ + plCameraBrain1::Write(stream, mgr); + stream->WriteSwap32(GetCircleFlags()); + fCenter.Write(stream); + stream->WriteSwapScalar(GetRadius()); + mgr->WriteKey(stream, fCenterObject); + mgr->WriteKey(stream, fPOAObj); + stream->WriteSwapScalar(fCirPerSec); +} + +void plCameraBrain1_Circle::Read(hsStream* stream, hsResMgr* mgr) +{ + plCameraBrain1::Read(stream, mgr); + SetCircleFlags(stream->ReadSwap32()); + if (GetCircleFlags() & kCircleLocalAvatar) + { + if (plNetClientApp::GetInstance() && plNetClientApp::GetInstance()->GetLocalPlayer()) + SetPOAObject((plSceneObject*)plNetClientApp::GetInstance()->GetLocalPlayer()); + + plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); + } + + fCenter.Read(stream); + SetRadius(stream->ReadSwapScalar()); + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kCircleTarget ); // SceneObject + mgr->ReadKeyNotifyMe( stream, msg, plRefFlags::kActiveRef ); + plGenRefMsg* msg2 = TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnRequest, 0, kPOAObject ); // SceneObject + mgr->ReadKeyNotifyMe( stream, msg2, plRefFlags::kActiveRef ); + fCirPerSec = stream->ReadSwapScalar(); + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +hsBool plCameraBrain1_Circle::MsgReceive(plMessage* msg) +{ + plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); + if (pRefMsg) + { + if (pRefMsg->fType == kCircleTarget) + { + if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fCenterObject = (plSceneObject*)pRefMsg->GetRef(); + else + fCenterObject = nil; + return true; + } + else + if (pRefMsg->fType == kPOAObject) + { + if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fPOAObj = (plSceneObject*)pRefMsg->GetRef(); + else + fPOAObj = nil; + return true; + } + else + if (pRefMsg->fType == kSubject) + { + if( pRefMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + SetSubject((plSceneObject*)pRefMsg->GetRef()); + else + SetSubject(nil); + return true; + } + } + plPlayerPageMsg* pPMsg = plPlayerPageMsg::ConvertNoRef(msg); + if (pPMsg && pPMsg->fPlayer == plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + { + if (pPMsg->fUnload) + { + if (GetCircleFlags() & kCircleLocalAvatar) + fPOAObj = nil; + if (fFlags.IsBitSet(kFollowLocalAvatar)) + SetSubject(nil); + } + else + { + if (GetCircleFlags() & kCircleLocalAvatar) + { + SetPOAObject((plSceneObject*)pPMsg->fPlayer->GetObjectPtr()); + fFlags.SetBit(kCutPOA); + } + if (fFlags.IsBitSet(kFollowLocalAvatar)) + { + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kSubject ); // SceneObject + hsgResMgr::ResMgr()->AddViaNotify(pPMsg->fPlayer, msg, plRefFlags::kPassiveRef); + + fFlags.SetBit(kCutPosOnce); + fFlags.SetBit(kCutPOA); + } + } + return true; + } + return (plCameraBrain1_Fixed::MsgReceive(msg)); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraBrain.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraBrain.h new file mode 100644 index 00000000..e87ef16e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraBrain.h @@ -0,0 +1,390 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plCameraBrain_inc +#define plCameraBrain_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsMatrix44.h" +#include "hsBitVector.h" +#include "hsTemplates.h" + +class plMessage; +class plCameraModifier1; +class plSceneObject; +class plRailCameraMod; + +class plCameraBrain1 : public hsKeyedObject +{ + +public: + enum + { + kCutPos = 0, + kCutPosOnce, + kCutPOA, + kCutPOAOnce, + kAnimateFOV, + kFollowLocalAvatar, + kPanicVelocity, + kRailComponent, + kSubject, + kCircleTarget, + kMaintainLOS, + kZoomEnabled, + kIsTransitionCamera, + kWorldspacePOA, + kWorldspacePos, + kCutPosWhilePan, + kCutPOAWhilePan, + kNonPhys, + kNeverAnimateFOV, + kIgnoreSubworldMovement, + kFalling, + kRunning, + kVerticalWhenFalling, + kSpeedUpWhenRunning, + kFallingStopped, + kBeginFalling, + }; + plCameraBrain1(plCameraModifier1* pMod); + plCameraBrain1(); + ~plCameraBrain1(); + + CLASSNAME_REGISTER( plCameraBrain1 ); + GETINTERFACE_ANY( plCameraBrain1, hsKeyedObject ); + + void SetCamera(plCameraModifier1* pMod) { fCamera = pMod; } + + void SetAccel (hsScalar f) { fAccel = f; } + void SetDecel (hsScalar f) { fDecel = f; } + void SetVelocity (hsScalar f) { fVelocity = f; } + void SetPOAAccel (hsScalar f) { fPOAAccel = f; } + void SetPOADecel (hsScalar f) { fPOADecel = f; } + void SetPOAVelocity (hsScalar f) { fPOAVelocity = f; } + + const plCameraModifier1* GetCamera() { return fCamera; } + + virtual void Update(hsBool forced = false); + virtual hsBool MsgReceive(plMessage* msg); + + virtual plSceneObject* GetSubject(); + virtual void SetSubject(plSceneObject* sub); + + virtual hsVector3 GetPOAOffset() { return fPOAOffset; } + void SetPOAOffset(hsVector3 pt) { fPOAOffset = pt; } + void AddTarget(); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool GetFaded() { return false; } + virtual hsBool SetFaded(hsBool b) { return false; } + + hsBool HasMovementFlag(int f) { return fMoveFlags.IsBitSet(f); } + void SetMovementFlag(int f); + void ClearMovementFlag(int which) { fMoveFlags.ClearBit( which ); } + void SetFlags(int i) { fFlags.SetBit(i); } + void ClearFlags(int which) { fFlags.ClearBit( which ); } + hsBool HasFlag(int f) { return fFlags.IsBitSet(f); } + + void SetGoal(hsPoint3 pt) { fGoal = pt; } + void SetPOAGoal(hsPoint3 pt) { fPOAGoal = pt; } + void SetFOVGoal(hsScalar h, double t); + void SetZoomParams(hsScalar max, hsScalar min, hsScalar rate); + + void SetXPanLimit(hsScalar x) {fXPanLimit = x;} + void SetZPanLimit(hsScalar y) {fZPanLimit = y;} + hsScalar GetXPanLimit() {return fXPanLimit;} + hsScalar GetZPanLimit() {return fZPanLimit;} + + void SetRail(plRailCameraMod* m) { fRail = m; } + + hsPoint3 GetGoal() { return fGoal; } + hsPoint3 GetPOAGoal() { return fPOAGoal; } + + virtual void Push(hsBool recenter = true); + virtual void Pop(); + + hsScalar GetVelocity() { return fVelocity; } + hsScalar GetAccel() { return fAccel; } + hsScalar GetDecel() { return fDecel; } + hsScalar GetPOAAccel() { return fPOAAccel; } + hsScalar GetPOAVelocity() { return fPOAVelocity; } + hsScalar GetPOADecel() { return fPOADecel; } + + hsScalar GetCurrentCamSpeed() { return fCurCamSpeed; } + hsScalar GetCurrentViewSpeed() { return fCurViewSpeed; } + + void SetCurrentCamSpeed(hsScalar s) { fCurCamSpeed = s; } + void SetCurrentViewSpeed(hsScalar s) { fCurViewSpeed = s; } + + hsMatrix44 GetTargetMatrix() { return fTargetMatrix; } + + static hsScalar fFallVelocity; + static hsScalar fFallAccel; + static hsScalar fFallDecel; + + static hsScalar fFallPOAVelocity; + static hsScalar fFallPOAAccel; + static hsScalar fFallPOADecel; + +protected: + + virtual void AdjustForInput(double secs); + void IMoveTowardGoal(double time); + void IPointTowardGoal(double time); + void IAnimateFOV(double time); + void IAdjustVelocity(hsScalar adjAccelRate, + hsScalar adjDecelRate, + hsVector3* dir, + hsVector3* vel, + hsScalar maxSpeed, + hsScalar distToGoal, + double elapsedTime); + + hsScalar IClampVelocity(hsVector3* vel, hsScalar maxSpeed, double elapsedTime); + hsBool IShouldDecelerate(hsScalar decelSpeed, hsScalar curSpeed, hsScalar distToGoal); + + plCameraModifier1* fCamera; + plKey fSubjectKey; + plRailCameraMod* fRail; + hsScalar fCurCamSpeed; + hsScalar fCurViewSpeed; + double fLastTime; + + hsScalar fVelocity; + hsScalar fAccel; + hsScalar fDecel; + hsScalar fPOAVelocity; + hsScalar fPOAAccel; + hsScalar fPOADecel; + hsVector3 fPOAOffset; + hsPoint3 fGoal; + hsPoint3 fPOAGoal; + hsScalar fXPanLimit; + hsScalar fZPanLimit; + hsScalar fPanSpeed; + hsScalar fFOVGoal; + double fFOVStartTime; + double fFOVEndTime; + hsScalar fFOVAnimRate; + hsScalar fZoomRate; + hsScalar fZoomMax; + hsScalar fZoomMin; + hsBitVector fMoveFlags; + hsBitVector fFlags; + hsMatrix44 fTargetMatrix; + hsScalar fOffsetLength; + hsScalar fOffsetPct; + double fFallTimer; +}; + +class plControlEventMsg; + +class plCameraBrain1_Drive : public plCameraBrain1 +{ +protected: + hsPoint3 fDesiredPosition; + hsPoint3 fFacingTarget; + hsBool bUseDesiredFacing; + hsScalar deltaX; + hsScalar deltaY; + hsBool bDisregardY; // these are here to prevent + hsBool bDisregardX; // the camera from jumping when the mouse cursor recenters / wraps around. + hsVector3 fUp; + +public: + + plCameraBrain1_Drive(); + plCameraBrain1_Drive(plCameraModifier1* pMod); + ~plCameraBrain1_Drive(); + + static SetSensitivity(hsScalar f) { fTurnRate = f; } + + CLASSNAME_REGISTER( plCameraBrain1_Drive ); + GETINTERFACE_ANY( plCameraBrain1_Drive, plCameraBrain1 ); + + virtual void Update(hsBool forced = false); + virtual hsBool MsgReceive(plMessage* msg); + virtual void Push(hsBool recenter = true); + virtual void Pop(); + + static hsScalar fAcceleration; + static hsScalar fDeceleration; + static hsScalar fMaxVelocity; + static hsScalar fTurnRate; +}; + + +class plCameraBrain1_Avatar : public plCameraBrain1 +{ +public: + + plCameraBrain1_Avatar(); + plCameraBrain1_Avatar(plCameraModifier1* pMod); + ~plCameraBrain1_Avatar(); + + CLASSNAME_REGISTER( plCameraBrain1_Avatar ); + GETINTERFACE_ANY( plCameraBrain1_Avatar, plCameraBrain1 ); + + + virtual void Update(hsBool forced = false); + virtual hsBool MsgReceive(plMessage* msg); + + virtual void CalculatePosition(); + hsVector3 GetOffset() { return fOffset; } + void SetOffset(hsVector3 pt) { fOffset = pt; } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool GetFaded() { return fFaded; } + virtual hsBool SetFaded(hsBool b) { fFaded = b; return true; } + + virtual void Pop(); + virtual void Push(hsBool recenter = true); + +protected: + void ISendFadeMsg(hsBool fade); + void IHandleObstacle(); + + hsPoint3 fHitPoint; + hsVector3 fOffset; + hsVector3 fHitNormal; + hsBool bObscured; + hsBool fFaded; + plSceneObject* fObstacle; +}; + +class plCameraBrain1_FirstPerson : public plCameraBrain1_Avatar +{ +public: + + plCameraBrain1_FirstPerson(); + plCameraBrain1_FirstPerson(plCameraModifier1* pMod); + ~plCameraBrain1_FirstPerson(); + + CLASSNAME_REGISTER( plCameraBrain1_FirstPerson ); + GETINTERFACE_ANY( plCameraBrain1_FirstPerson, plCameraBrain1_Avatar ); + + virtual void CalculatePosition(); + virtual void Push(hsBool recenter = true); + virtual void Pop(); + virtual hsBool MsgReceive(plMessage* msg); + + // for console hack + static hsBool fDontFade; +protected: + plSceneObject* fPosNode; + + +}; + + +class plCameraBrain1_Fixed : public plCameraBrain1 +{ +public: + + plCameraBrain1_Fixed(); + plCameraBrain1_Fixed(plCameraModifier1* pMod); + ~plCameraBrain1_Fixed(); + + CLASSNAME_REGISTER( plCameraBrain1_Fixed ); + GETINTERFACE_ANY( plCameraBrain1_Fixed, plCameraBrain1 ); + + void SetTargetPoint(plCameraModifier1* pt) { fTargetPoint = pt; } + + virtual void Update(hsBool forced = false); + void CalculatePosition(); + virtual hsBool MsgReceive(plMessage* msg); + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + +private: + plCameraModifier1* fTargetPoint; + +}; + +// +// circle cam brain +// +class plCameraBrain1_Circle : public plCameraBrain1_Fixed +{ + +public: + enum CircleFlags + { + kLagged = 0x01, + kAbsoluteLag = (0x02 | kLagged), + kFarthest = 0x04, + kTargetted = 0x08, + kHasCenterObject = 0x10, + kPOAObject = 0x20, + kCircleLocalAvatar = 0x40, + }; +protected: + UInt32 fCircleFlags; + hsPoint3 fCenter; + plSceneObject* fCenterObject; // optional, use instead of fCenter + hsScalar fRadius; + hsScalar fCurRad, fGoalRad; // Radians + plSceneObject* fPOAObj; // in this case the subject is who we stay close to/away from + hsScalar fCirPerSec; + + hsPoint3 IGetClosestPointOnCircle(const hsPoint3* toThisPt); +public: + plCameraBrain1_Circle(); + plCameraBrain1_Circle(plCameraModifier1* pMod); + ~plCameraBrain1_Circle(); + + CLASSNAME_REGISTER( plCameraBrain1_Circle ); + GETINTERFACE_ANY( plCameraBrain1_Circle, plCameraBrain1_Fixed ); + + + virtual void Read(hsStream *stream, hsResMgr* mgr); + virtual void Write(hsStream *stream, hsResMgr* mgr); + virtual hsPoint3 MoveTowardsFromGoal(const hsPoint3* fromGoal, double secs, hsBool warp = false); + virtual void Update(hsBool forced = false); + virtual hsBool MsgReceive(plMessage* msg); + + UInt32 GetCircleFlags() { return fCircleFlags; } + hsPoint3* GetCenter() { return &fCenter; } // use GetCenterPoint + hsPoint3 GetCenterPoint(); + hsScalar GetRadius() { return fRadius; } + plSceneObject* GetCenterObject() { return fCenterObject; } + + void SetCircumferencePerSec(hsScalar h) { fCirPerSec = h; } + void SetCircleFlags(UInt32 f) { fCircleFlags|=f; } + void SetCenter(hsPoint3* ctr) { fCenter = *ctr; } // Circle lies in the plane z = ctr->z + void SetRadius(hsScalar radius) { fRadius = radius; } + void SetFarCircleCam(hsBool farType) { if (farType) fCircleFlags |= kFarthest; else fCircleFlags &= ~kFarthest; } + void SetCenterObjectKey(plKey k); + void SetPOAObject(plSceneObject* pObj) { fPOAObj = pObj; } + +}; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp new file mode 100644 index 00000000..79875182 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.cpp @@ -0,0 +1,478 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "plCameraModifier.h" +#include "plCameraBrain.h" +#include "plVirtualCamNeu.h" +#include "hsTimer.h" +#include "plgDispatch.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../plInputCore/plInputDevice.h" +#include "../plInputCore/plInputManager.h" +#include "hsResMgr.h" +#include "../pnMessage/plCameraMsg.h" +#include "../plPhysical/plSimDefs.h" + +#include "plPhysical.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvCallbackAction.h" + +// new stuff + +plCameraModifier1::plCameraModifier1() : +fBrain(nil), +fSubObj(nil), +fFOVw(45.0f), +fFOVh(33.75f), +fAnimated(false), +fStartAnimOnPush(false), +fStopAnimOnPop(false), +fResetAnimOnPop(false), +fInSubLastUpdate(false), +fUpdateBrainTarget(false) +{ + fFrom.Set(0,0,0); + fAt.Set(0,1,0); +} + + +plCameraModifier1::~plCameraModifier1() +{ + int i; + for (i = 0; i < GetNumTrans(); i++) + delete(GetTrans(i)); + fTrans.SetCountAndZero(0); + + for (i = 0; i < fMessageQueue.Count(); i++) + hsRefCnt_SafeUnRef(fMessageQueue[i]); + fMessageQueue.SetCountAndZero(0); + + for (i = 0; i < fFOVInstructions.Count(); i++) + hsRefCnt_SafeUnRef(fFOVInstructions[i]); + fFOVInstructions.SetCountAndZero(0); + +} + + +void plCameraModifier1::AddTarget(plSceneObject* so) +{ + fTarget = so; + if( plVirtualCam1::Instance() ) + plVirtualCam1::Instance()->AddCameraLoaded(so); + fFrom = (so->GetWorldToLocal().GetTranslate()); + if (GetBrain()) + { + if (fTarget->GetCoordinateInterface()) + GetBrain()->AddTarget(); + else + fUpdateBrainTarget = true; // update the brain later + } + if (GetKey()) + { + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + } +} + +void plCameraModifier1::SetSubject(plSceneObject* pObj) +{ + if (GetBrain()) + GetBrain()->SetSubject(pObj); + else + fSubObj = pObj; +} + +plSceneObject* plCameraModifier1::GetSubject() +{ + if (GetBrain()) + return GetBrain()->GetSubject(); + else + return fSubObj; +} + +void plCameraModifier1::SetFOVw(hsScalar f, hsBool fUpdateVCam) +{ + fFOVw = f; + if (plVirtualCam1::Instance() && fUpdateVCam) + plVirtualCam1::SetFOV(fFOVw, fFOVh, this); +} + +void plCameraModifier1::SetFOVh(hsScalar f, hsBool fUpdateVCam) +{ + fFOVh = f; + if (plVirtualCam1::Instance() && fUpdateVCam) + plVirtualCam1::SetFOV(fFOVw, fFOVh, this); +} + +hsBool plCameraModifier1::SetFaded(hsBool b) +{ + if (GetBrain()) + return GetBrain()->SetFaded(b); + return false; +} + +hsBool plCameraModifier1::GetFaded() +{ + if (GetBrain()) + return GetBrain()->GetFaded(); + return false; +} +hsBool plCameraModifier1::MsgReceive(plMessage* msg) +{ + if (GetBrain()) + GetBrain()->MsgReceive(msg); + + plCameraMsg* pCamMsg = plCameraMsg::ConvertNoRef(msg); + if (pCamMsg) + { + if (pCamMsg->Cmd(plCameraMsg::kAddFOVKeyframe)) + { + hsRefCnt_SafeRef(msg); + fFOVInstructions.Append(pCamMsg); + return true; + } + else + if (pCamMsg->Cmd(plCameraMsg::kSetAnimated)) + { + fAnimated = true; + return true; + } + } + plEventCallbackMsg* pEventMsg = plEventCallbackMsg::ConvertNoRef(msg); + if (pEventMsg) + { + double time = (double)fFOVInstructions[pEventMsg->fIndex]->GetConfig()->fAccel; + double time2 = (double)pEventMsg->fEventTime; + time = hsABS(time - time2); + hsScalar h = fFOVInstructions[pEventMsg->fIndex]->GetConfig()->fFOVh; + if (GetBrain()) + GetBrain()->SetFOVGoal(h, time); + } + + plAnimCmdMsg* pAnimMsg = plAnimCmdMsg::ConvertNoRef(msg); + if (pAnimMsg) + { + hsRefCnt_SafeRef(msg); + msg->ClearReceivers(); + msg->AddReceiver(msg->GetSender()); + fMessageQueue.Append(msg); + return true; + } + plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); + if (pRefMsg ) + { + if( pRefMsg->GetContext() & (plRefMsg::kOnCreate | plRefMsg::kOnRequest) ) + { + if (pRefMsg->fType == kRefBrain) + { + plCameraBrain1* pBrain = plCameraBrain1::ConvertNoRef(pRefMsg->GetRef()); + if (pBrain) + { + pBrain->SetCamera(this); + fBrain = pBrain; + if (fSubObj) + fBrain->SetSubject(fSubObj); + } + } + else + if (pRefMsg->fType == kRefCallbackMsg && fMessageQueue[pRefMsg->fWhich] != nil) + { + + plgDispatch::MsgSend(fMessageQueue[pRefMsg->fWhich]); + fMessageQueue[pRefMsg->fWhich] = nil; + } + } + else if( pRefMsg->GetContext() & (plRefMsg::kOnDestroy | plRefMsg::kOnRemove) ) + { + plCameraBrain1* pBrain = (plCameraBrain1*)(pRefMsg->GetRef()); + if (fBrain == pBrain) + fBrain = nil; + } + return true; + } + return plSingleModifier::MsgReceive(msg); +} + + +void plCameraModifier1::Update() +{ + // update the brain + + // this freeze thing is a useful debugging tool... + if (plVirtualCam1::Instance()->freeze) + return; + + if (GetBrain()) + { + if (fUpdateBrainTarget && fTarget->GetCoordinateInterface()) // if we need to update the brain and the target is loaded + { + fUpdateBrainTarget = false; + GetBrain()->AddTarget(); // update the brain's target + } + + hsBool moveInSub = !(GetBrain()->HasFlag(plCameraBrain1::kIgnoreSubworldMovement)); + + if (moveInSub && GetBrain()->GetSubject()) + { + plKey worldKey = nil; + + // First check if this is a physical. If so, grab the subworld from that + if (GetBrain()->GetSubject()->GetSimulationInterface()) + { + plPhysical* phys = GetBrain()->GetSubject()->GetSimulationInterface()->GetPhysical(); + if (phys) + worldKey = phys->GetWorldKey(); + } + // Also, check if this is an avatar. They don't have physicals, you + // have to ask the avatar controller for the subworld key. + if (!worldKey) + { + plArmatureMod* armMod = plAvatarMgr::FindAvatar(plKey(GetBrain()->GetSubject()->GetKey())); + if (armMod && armMod->GetController() ) + worldKey = armMod->GetController()->GetSubworld(); + } + + if (worldKey) + { + // this picks up and moves the camera to it's previous subworld coordinate (so the subworld isn't moving out from underneath us) + hsMatrix44 l2w, w2l; + plSceneObject* so = plSceneObject::ConvertNoRef(worldKey->ObjectIsLoaded()); + if (so) + { + l2w = so->GetLocalToWorld(); + w2l = so->GetWorldToLocal(); + + if (fInSubLastUpdate) + { + if (!(fLastSubPos == fFrom && fLastSubPOA == fAt)) + { + SetTargetPos(l2w * fLastSubPos); + SetTargetPOA(l2w * fLastSubPOA); + } + } + else + { + fInSubLastUpdate = true; + } + GetBrain()->Update(); + fLastSubPos = w2l * GetTargetPos(); + fLastSubPOA = w2l * GetTargetPOA(); + } + return; + } + else + { + fInSubLastUpdate = false; + } + } + GetBrain()->Update(); + fLastSubPos = GetTargetPos(); + fLastSubPOA = GetTargetPOA(); + } +} + +void plCameraModifier1::Read(hsStream* stream, hsResMgr* mgr) +{ + hsKeyedObject::Read(stream, mgr); + fBrain = nil; + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefBrain), plRefFlags::kActiveRef); + int count = stream->ReadSwap32(); + int i; + for (i = 0; i < count; i++) + { + + plKey key = mgr->ReadKey(stream); + hsBool cutpos = stream->ReadBool(); + hsBool cutpoa = stream->ReadBool(); + hsBool ignore = stream->ReadBool(); + hsScalar v = stream->ReadSwapScalar(); + hsScalar a = stream->ReadSwapScalar(); + hsScalar d = stream->ReadSwapScalar(); + hsScalar pV = stream->ReadSwapScalar(); + hsScalar pA = stream->ReadSwapScalar(); + hsScalar pD = stream->ReadSwapScalar(); + + CamTrans* camTrans = TRACKED_NEW CamTrans(key); + camTrans->fAccel = a; + camTrans->fDecel = d; + camTrans->fVelocity = v; + camTrans->fPOAAccel = pA; + camTrans->fPOADecel = pD; + camTrans->fPOAVelocity = pV; + camTrans->fCutPos = cutpos; + camTrans->fCutPOA = cutpoa; + camTrans->fIgnore = ignore; + + fTrans.Append(camTrans); + } + fFOVw = stream->ReadSwapFloat(); + fFOVh = stream->ReadSwapFloat(); + int n = stream->ReadSwap32(); + fMessageQueue.SetCountAndZero(n); + for(i = 0; i < n; i++ ) + { + plMessage* pMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); + fMessageQueue[i] = pMsg; + } + for(i = 0; i < n; i++ ) + { + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, i, kRefCallbackMsg), plRefFlags::kActiveRef); + } + + n = stream->ReadSwap32(); + fFOVInstructions.SetCountAndZero(n); + for(i = 0; i < n; i++ ) + { + plCameraMsg* pMsg = plCameraMsg::ConvertNoRef(mgr->ReadCreatable(stream)); + fFOVInstructions[i] = pMsg; + } + fAnimated = stream->ReadBool(); + fStartAnimOnPush = stream->ReadBool(); + fStopAnimOnPop = stream->ReadBool(); + fResetAnimOnPop = stream->ReadBool(); + +} + +void plCameraModifier1::Write(hsStream* stream, hsResMgr* mgr) +{ + hsKeyedObject::Write(stream, mgr); + if (fBrain) + mgr->WriteKey(stream, fBrain ); + + int i = fTrans.Count(); + stream->WriteSwap32(i); + for (i = 0; i < fTrans.Count(); i++) + { + mgr->WriteKey(stream, fTrans[i]->fTransTo); + stream->WriteBool(fTrans[i]->fCutPos); + stream->WriteBool(fTrans[i]->fCutPOA); + stream->WriteBool(fTrans[i]->fIgnore); + stream->WriteSwapScalar(fTrans[i]->fVelocity); + stream->WriteSwapScalar(fTrans[i]->fAccel); + stream->WriteSwapScalar(fTrans[i]->fDecel); + stream->WriteSwapScalar(fTrans[i]->fPOAVelocity); + stream->WriteSwapScalar(fTrans[i]->fPOAAccel); + stream->WriteSwapScalar(fTrans[i]->fPOADecel); + } + stream->WriteSwapFloat(fFOVw); + stream->WriteSwapFloat(fFOVh); + stream->WriteSwap32(fMessageQueue.Count()); + for (i = 0; i < fMessageQueue.Count(); i++) + { + mgr->WriteCreatable(stream, fMessageQueue[i]); + } + for (i = 0; i < fMessageQueue.Count(); i++) + { + mgr->WriteKey(stream, fMessageQueue[i]->GetSender()); + } + stream->WriteSwap32(fFOVInstructions.Count()); + for (i = 0; i < fFOVInstructions.Count(); i++) + { + mgr->WriteCreatable(stream, fFOVInstructions[i]); + } + stream->WriteBool(fAnimated); + stream->WriteBool(fStartAnimOnPush); + stream->WriteBool(fStopAnimOnPop); + stream->WriteBool(fResetAnimOnPop); +} + +void plCameraModifier1::Push(hsBool recenter) +{ + if (fAnimated) + { + if (fStartAnimOnPush) + { + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->SetCmd(plAnimCmdMsg::kRunForward); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + pMsg->AddReceiver(GetTarget()->GetKey()); + if (GetBrain() && GetBrain()->GetSubject()) + pMsg->AddReceiver(GetBrain()->GetSubject()->GetKey()); + pMsg->Send(); + } + } + if (fBrain) + fBrain->Push(recenter); + + + if (GetKey()) + { + plgDispatch::Dispatch()->RegisterForExactType(plMouseEventMsg::Index(), GetKey()); + } +} + +void plCameraModifier1::Pop() +{ + if (fAnimated) + { + if (fStopAnimOnPop) + { + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->SetCmd(plAnimCmdMsg::kStop); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + pMsg->AddReceiver(GetTarget()->GetKey()); + if (GetBrain() && GetBrain()->GetSubject()) + pMsg->AddReceiver(GetBrain()->GetSubject()->GetKey()); + pMsg->Send(); + } + if (fResetAnimOnPop) + { + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->SetCmd(plAnimCmdMsg::kGoToBegin); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + pMsg->AddReceiver(GetTarget()->GetKey()); + if (GetBrain() && GetBrain()->GetSubject()) + pMsg->AddReceiver(GetBrain()->GetSubject()->GetKey()); + pMsg->Send(); + } + } + if (fBrain) + fBrain->Pop(); + if (GetKey()) // the reason we might not have a key is a special run-time POA which doesn't need to receive messages... + { + plgDispatch::Dispatch()->UnRegisterForExactType(plMouseEventMsg::Index(), GetKey()); + } +} + +void plCameraModifier1::SetTransform(hsPoint3 at) +{ + if (!GetTarget()) + return; + hsMatrix44 l2w; + hsMatrix44 w2l; + hsVector3 up(0,0,1); + l2w.Make(&fFrom, &at, &up); + l2w.GetInverse(&w2l); + IGetTargetCoordinateInterface(0)->SetTransform( l2w, w2l ); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.h new file mode 100644 index 00000000..7fe0aecc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.h @@ -0,0 +1,157 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCameraModifier_inc +#define plCameraModifier_inc + +#include "../pnModifier/plSingleModifier.h" +#include "hsBitVector.h" +#include "hsGeometry3.h" +#include "hsTemplates.h" + +class plPipeline; +class plKey; +class plCameraBrain1; +class plCameraMsg; +struct CamTrans +{ + // used when creating default track transitions at runtime + CamTrans(plKey to) + { + fTransTo = to; + + fAccel = 60.0f; + fDecel = 60.0f; + fVelocity = 60.0f; + fPOADecel = 60.0f; + fPOAAccel = 60.0f; + fPOAVelocity = 60.0f; + + fCutPos = false; + fCutPOA = false; + fIgnore = false; + } + plKey fTransTo; + + hsBool fCutPos; + hsBool fCutPOA; + hsBool fIgnore; + hsScalar fAccel; + hsScalar fDecel; + hsScalar fVelocity; + hsScalar fPOAAccel; + hsScalar fPOADecel; + hsScalar fPOAVelocity; + +}; + +class plCameraModifier1 : public plSingleModifier +{ + enum + { + kRefBrain, + kRefCut, + kRefTrack, + kRefCallbackMsg, + }; +protected: + + void Output(); + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return true; } + +public: + + plCameraModifier1(); + virtual ~plCameraModifier1(); + + CLASSNAME_REGISTER( plCameraModifier1 ); + GETINTERFACE_ANY( plCameraModifier1, plSingleModifier ); + + virtual hsBool MsgReceive(plMessage* msg); + + void Initialize(); + virtual void Update(); + + virtual void AddTarget(plSceneObject* so); + + void SetBrain(plCameraBrain1* brain) { fBrain = brain; } + + plCameraBrain1* GetBrain() { return fBrain;} + + hsPoint3 GetTargetPos() { return fFrom; } + hsPoint3 GetTargetPOA() { return fAt; } + hsPoint3 GetSubworldPos() { return fLastSubPos; } + hsPoint3 GetSubworldPOA() { return fLastSubPOA; } + + + void SetTransform(hsPoint3 at); + void SetTargetPos(hsPoint3 pos) { fFrom = pos; } + void SetTargetPOA(hsPoint3 pos) { fAt = pos; } + void SetSubworldPos(hsPoint3 pos) { fLastSubPos = pos; } + void SetSubworldPOA(hsPoint3 pos) { fLastSubPOA = pos; } + hsScalar GetFOVw() { return fFOVw; } + hsScalar GetFOVh() { return fFOVh; } + void SetFOVw(hsScalar f, hsBool fUpdateVCam = true); + void SetFOVh(hsScalar f, hsBool fUpdateVCam = true); + hsBool GetInSubworld() { return fInSubLastUpdate; } + void InSubworld(hsBool b) { fInSubLastUpdate = b; } + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + void AddTrans(CamTrans* t) { fTrans.Append(t); } + int GetNumTrans() { return fTrans.Count(); } + CamTrans* GetTrans(int i) { return fTrans[i]; } + void SetSubject(plSceneObject* pObj); + plSceneObject* GetSubject(); + + virtual void Push(hsBool recenter = true); + virtual void Pop(); + + virtual hsBool GetFaded(); + virtual hsBool SetFaded(hsBool b); + + hsBool IsAnimated() { return fAnimated; } + void SetAnimCommands(hsBool a, hsBool b, hsBool c) { fStartAnimOnPush = a; fStopAnimOnPop = b; fResetAnimOnPop = c; } + +private: + hsPoint3 fFrom; + hsPoint3 fAt; + plCameraBrain1* fBrain; // the 'logic' portion of the camera + hsTArray fTrans; + plSceneObject* fSubObj; + hsScalar fFOVw; + hsScalar fFOVh; + hsTArray fMessageQueue; + hsTArray fFOVInstructions; + hsBool fAnimated, fStartAnimOnPush, fStopAnimOnPop, fResetAnimOnPop; + hsPoint3 fLastSubPos; + hsPoint3 fLastSubPOA; + hsBool fInSubLastUpdate; + hsBool fUpdateBrainTarget; // sometimes our target isn't loaded yet, so wait to update the brain til later +}; + + + +#endif plCameraModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plInterestingModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plInterestingModifier.cpp new file mode 100644 index 00000000..144be0d7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plInterestingModifier.cpp @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsGeometry3.h" +#include "plgDispatch.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../plMessage/plInterestingPing.h" +#include "hsBounds.h" +#include "plInterestingModifier.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnKeyedObject/plKey.h" + + +hsScalar plInterestingModifier::fInterestRadius = 100.0f; +hsScalar plInterestingModifier::fInterestWeight = 1.0f; + + +hsBool plInterestingModifier::IEval(double secs, hsScalar del, UInt32 dirty) +{ + for (int i=0; i < GetNumTargets(); i++) + { + if( GetTarget(i) && GetTarget(i)->GetDrawInterface() ) + { + const hsBounds3Ext& targBnd = GetTarget(i)->GetDrawInterface()->GetWorldBounds(); + if( targBnd.GetType() == kBoundsNormal ) + { + plInterestingModMsg* pMsg = TRACKED_NEW plInterestingModMsg; + pMsg->fPos= GetTarget(i)->GetDrawInterface()->GetWorldBounds().GetCenter(); + pMsg->fSize = GetTarget(i)->GetDrawInterface()->GetWorldBounds().GetMaxDim(); + pMsg->fRadius = fInterestRadius; + pMsg->fWeight = fInterestWeight; + pMsg->fObj = GetTarget(i)->GetKey(); + pMsg->fType = GetType(); + pMsg->SetBCastFlag( plMessage::kPropagateToModifiers ); + plgDispatch::MsgSend( pMsg ); + } + } + } + return true; +} + +void plInterestingModifier::AddTarget(plSceneObject* so) +{ + fTarget = so; + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plInterestingModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plInterestingModifier.h new file mode 100644 index 00000000..62f1f55c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plInterestingModifier.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plInterestingModifier_inc +#define plInterestingModifier_inc + +#include "../pnModifier/plSingleModifier.h" +#include "../pnMessage/plMessage.h" +#include "hsResMgr.h" +#include "hsGeometry3.h" +#include "hsStream.h" + +class plInputEventMsg; + +class plInterestingModifier : public plSingleModifier +{ +protected: + + enum + { + kTypeInteresting = 0, + kTypeLookAtMod, + }; + + UInt8 fType; + hsScalar fView; + + static hsScalar fInterestRadius; + static hsScalar fInterestWeight; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + +public: + plInterestingModifier(){ fType = kTypeInteresting;} + virtual ~plInterestingModifier(){;} + + virtual hsBool MsgReceive(plMessage* msg) {return false;} + + CLASSNAME_REGISTER( plInterestingModifier ); + GETINTERFACE_ANY( plInterestingModifier, plSingleModifier ); + + hsScalar GetInterestWeight() { return fInterestWeight; } + hsScalar GetInterestRadius() { return fInterestRadius; } + + void SetInterestWeight(hsScalar _InterestRadius) { fInterestWeight =_InterestRadius; } + void SetInterestRadius(hsScalar _InterestWeight) { fInterestRadius =_InterestWeight; } + + virtual void AddTarget(plSceneObject* so); + + void SetType(UInt8 type) { fType = type; } + UInt8 GetType() { return fType; } +}; + + + +#endif plInterestingModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.cpp new file mode 100644 index 00000000..a5c3ec95 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.cpp @@ -0,0 +1,2085 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plVirtualCamNeu.h" +#include "plCameraModifier.h" +#include "plCameraBrain.h" +#include "plPipeline.h" +#include "pfCameraProxy.h" +#include "plgDispatch.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "hsMatrix44.h" +#include "../pnSceneObject/plSceneObject.h" +#include "hsResMgr.h" +#include "hsTimer.h" +#include "../plAudio/plAudioSystem.h" +#include "../plInputCore/plInputDevice.h" +#include "../plInputCore/plInputManager.h" +#include "../pnInputCore/plKeyDef.h" +#include "../plScene/plSceneNode.h" +#include "../plMessage/plAvatarMsg.h" +#include "../pnMessage/plWarpMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plCmdIfaceModMsg.h" +#include "../pnMessage/plPlayerPageMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../plScene/plSceneNode.h" +#include "../plInputCore/plDebugInputInterface.h" +#include "../plInputCore/plAvatarInputInterface.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../plStatusLog/plStatusLog.h" +#include "../plPipeline/plPlates.h" +#include "../plGImage/plMipmap.h" +#include "../plSurface/plLayer.h" +#include "../plSurface/hsGMaterial.h" +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plAvatar/plAvatarMgr.h" + +#include "hsGeometry3.h" +#include "hsConfig.h" +#include "hsQuat.h" + +hsScalar plVirtualCam1::fFOVw = 45.0f; +hsScalar plVirtualCam1::fFOVh = 33.75f; +hsScalar plVirtualCam1::fHither = 0.3f; +hsScalar plVirtualCam1::fYon = 500.0f; +hsBool plVirtualCam1::printFOV = false; +hsBool plVirtualCam1::fUseAccelOverride = 1; +hsBool plVirtualCam1::freeze = 0; +//hsScalar plVirtualCam1::fAccel = 5.0f; +//hsScalar plVirtualCam1::fDecel = 5.0f; +//hsScalar plVirtualCam1::fVel = 10.0f; +hsScalar plVirtualCam1::fAccel = 50.0f; +hsScalar plVirtualCam1::fDecel = 50.0f; +hsScalar plVirtualCam1::fVel = 100.0f; +hsScalar plVirtualCam1::fPanResponseTime = 3.0f; +hsScalar plVirtualCam1::fFallTimerDelay = 0.25f; +hsBool plVirtualCam1::alwaysCutForColin = false; +hsBool plVirtualCam1::WalkPan3rdPerson = false; +hsBool plVirtualCam1::StayInFirstPersonForever = false; +float plVirtualCam1::fAspectRatio = fFOVw / fFOVh; + +// #define STATUS_LOG + +#ifdef STATUS_LOG +static plStatusLog *camLog = nil; +#endif + +// static functions +void plVirtualCam1::AddMsgToLog(const char* msg) +{ +#ifdef STATUS_LOG + if (camLog) + camLog->AddLine(msg); +#endif +} + +hsBool plVirtualCam1::IsCurrentCamera(const plCameraModifier1* mod) +{ + if (plVirtualCam1::Instance()) + { + if (plVirtualCam1::Instance()->InTransition()) + return(plVirtualCam1::Instance()->GetTransitionCamera() == mod); + + return (plVirtualCam1::Instance()->GetCurrentCamera() == mod); + } + return false; +} + +plVirtualCam1* plVirtualCam1::fInstance = nil; + +void plVirtualCam1::Deactivate() +{ +} + + +plVirtualCam1::plVirtualCam1() +{ + fFlags.Clear(); + fPythonOverride = nil; + fFirstPersonOverride = nil; + fThirdPersonCam = nil; + fTransPos = POS_TRANS_OFF; + fPrevCam = nil; + fTransitionCamera = TRACKED_NEW plCameraModifier1; + fTransitionCamera->RegisterAs(kTransitionCamera_KEY); + // set initial view position + fOutputPos.Set(100,100,100); + fOutputPOA.Set(0,0,0); + fEffectPlate = nil; + fFreezeCounter = 0; + fFadeCounter = 0; + fX = fY = 0.5f; + fXPanLimit = 0; + fZPanLimit = 0; + fRetainedFY = 0.5f; + // create built-in drive mode camera + fCameraDriveInterface = plDebugInputInterface::GetInstance(); + hsRefCnt_SafeRef( fCameraDriveInterface ); + + fDriveCamera = TRACKED_NEW plCameraModifier1; + plCameraBrain1* pDriveBrain = TRACKED_NEW plCameraBrain1_Drive(fDriveCamera); + + PushCamera(fDriveCamera); + fForceCutOnce=false; + + // static accessor hack + plVirtualCam1::fInstance = this; + + #ifdef STATUS_LOG + if (!camLog) + camLog = plStatusLogMgr::GetInstance().CreateStatusLog(40, "Camera.log", plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | plStatusLog::kAlignToTop); +// camLog = plStatusLogMgr::GetInstance().CreateStatusLog(40, "Camera", plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | plStatusLog::kDontWriteFile | plStatusLog::kAlignToTop); + #endif + + ICreatePlate(); + + foutLog = nil; +#ifndef PLASMA_EXTERNAL_RELEASE + // only open log file if logging is on + if ( !plStatusLog::fLoggingOff ) + { + wchar fileAndPath[MAX_PATH]; + PathGetLogDirectory(fileAndPath, arrsize(fileAndPath)); + PathAddFilename(fileAndPath, fileAndPath, L"camLog.txt", arrsize(fileAndPath)); + foutLog = _wfopen( fileAndPath, L"wt" ); + } +#endif + + SetFlags(kFirstPersonEnabled); + +} + +plVirtualCam1::~plVirtualCam1() +{ + if(fTransitionCamera->GetBrain()) + { + delete(fTransitionCamera->GetBrain()); + } + fTransitionCamera->UnRegisterAs(kTransitionCamera_KEY); + delete(fDriveCamera->GetBrain()); + delete(fDriveCamera); + hsRefCnt_SafeUnRef( fCameraDriveInterface ); + + if(fThirdPersonCam) + { + delete(fThirdPersonCam->GetBrain()); + fThirdPersonCam->UnRegisterAs(kBuiltIn3rdPersonCamera_KEY); + } + plUoid U(kDefaultCameraMod1_KEY); + plKey pKey = hsgResMgr::ResMgr()->FindKey(U); + if (pKey) + { + delete((plCameraModifier1*)pKey->GetObjectPtr())->GetBrain(); + pKey->GetObjectPtr()->UnRegisterAs(kDefaultCameraMod1_KEY); + } + if (fEffectPlate) + plPlateManager::Instance().DestroyPlate(fEffectPlate); +} + +// for saving camera stack +plCameraModifier1* plVirtualCam1::GetCameraNumber(int camNumber) +{ + return (fCameraStack[camNumber]); +} +// for rebuilding camera stack +void plVirtualCam1::RebuildStack(const plKey& key) +{ + if (fCameraStack.Count() == 1) + { + plUoid U(kDefaultCameraMod1_KEY); + plKey pKey = hsgResMgr::ResMgr()->FindKey(U); + if (pKey) + { + if (fCameraStack[0]->GetKey() == pKey) + fCameraStack.SetCountAndZero(0); + } + } + plSceneObject* pObj = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if (pObj) + { + + plCameraModifier1* pMod = (plCameraModifier1*)pObj->GetModifierByType(plCameraModifier1::Index()); + if (pMod) + AddCameraToStack(pMod); + else + PushThirdPerson(); + } + else + { + plCameraModifier1* pMod = plCameraModifier1::ConvertNoRef(key->GetObjectPtr()); + if (pMod) + AddCameraToStack(pMod); + else + PushThirdPerson(); + } + if (!HasFlags(kFirstPersonAtLinkOut)) + { + plEnableMsg* pMsg = TRACKED_NEW plEnableMsg; + pMsg->SetSender(GetKey()); + pMsg->SetCmd(plEnableMsg::kEnable); + pMsg->AddType(plEnableMsg::kDrawable); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + pMsg->AddReceiver(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + plgDispatch::MsgSend(pMsg); + } + + fForceCutOnce=true; + +} + +// hack for console command to force offset +void plVirtualCam1::SetOffset(float x, float y, float z) +{ + plCameraModifier1* pCam = plVirtualCam1::Instance()->GetCurrentCamera(); + if (!pCam) + return; + hsVector3 pt(x,y,z); + if (plCameraBrain1_Avatar::ConvertNoRef(pCam->GetBrain())) + ((plCameraBrain1_Avatar*)(pCam->GetBrain()))->SetOffset(pt); +} + +// static function +void plVirtualCam1::SetFOV(hsScalar x, hsScalar y) +{ + + float fovW = y * fAspectRatio; + + fFOVw = fovW; + fFOVh = y; + + if (! plVirtualCam1::Instance()->fPipe) + return; + + plVirtualCam1::Instance()->SetFlags(plVirtualCam1::kSetFOV); + + #ifdef STATUS_LOG +if (printFOV) + camLog->AddLineF("FOV changed by console command to %f", fFOVw); +#endif + +} +// static function +void plVirtualCam1::SetFOV(hsScalar x, hsScalar y, plCameraModifier1* pCam) +{ + if (plVirtualCam1::Instance()->GetCurrentCamera() != pCam) + return; + + hsScalar diff = hsABS(fFOVw - x); + if (diff > 10.0f) + { +#ifdef STATUS_LOG + camLog->AddLineF("Radical FOV change of %f", diff); +#endif + + } + + float fovW = y * fAspectRatio; + + fFOVw = fovW; + fFOVh = y; + + if (! plVirtualCam1::Instance()->fPipe) + return; + + plVirtualCam1::Instance()->SetFlags(plVirtualCam1::kSetFOV); + +#ifdef STATUS_LOG + if (printFOV) + camLog->AddLineF("FOV changed to %f", fFOVw); +#endif +} + +// static function + +void plVirtualCam1::SetDepth(hsScalar h, hsScalar y) +{ + return; + fHither = h; + fYon = y; + if (! plVirtualCam1::Instance()->fPipe) + return; + plVirtualCam1::Instance()->fPipe->SetDepth(fHither, fYon); + plVirtualCam1::Instance()->fPipe->RefreshMatrices(); + #ifdef STATUS_LOG + camLog->AddLineF("Hither, Yon changed to %f %f", fHither, fYon); + #endif +} + +// force drive mode from console +void plVirtualCam1::Drive() +{ + if (GetCurrentCamera() == fDriveCamera) + { + fCameraDriveInterface->SetEnabled( false ); + PopCamera(fDriveCamera); + + #ifdef STATUS_LOG + camLog->AddLineF("Camera Drive Mode Disabled"); + #endif + } + else + { + // set it to the current camera position - + fDriveCamera->GetBrain()->SetGoal(GetCurrentCamera()->GetTargetPos()); + fDriveCamera->GetBrain()->SetPOAGoal(GetCurrentCamera()->GetTargetPOA()); + fDriveCamera->SetTargetPos(GetCurrentCamera()->GetTargetPos()); + fDriveCamera->SetTargetPOA(GetCurrentCamera()->GetTargetPOA()); + + PushCamera(fDriveCamera); + + // push the interface on + fCameraDriveInterface->SetEnabled( true ); + + #ifdef STATUS_LOG + camLog->AddLineF("Camera Drive Mode Enabled"); + #endif + } +} + + +void plVirtualCam1::SetPipeline(plPipeline* p) +{ + fPipe = p; + SetFOV(plVirtualCam1::fFOVw, plVirtualCam1::fFOVh); + SetRender(false); +} + +void plVirtualCam1::Reset(hsBool bRender) +{ + if (fPythonOverride) + fPythonOverride = nil; + if (fFirstPersonOverride) + fFirstPersonOverride = nil; + fCamerasLoaded.SetCountAndZero(0); + fCameraStack.SetCountAndZero(0); + fCameraStack.Append(fDriveCamera); + plUoid U(kDefaultCameraMod1_KEY); + plKey pKey = hsgResMgr::ResMgr()->FindKey(U); + if (pKey) + PushCamera((plCameraModifier1*)pKey->GetObjectPtr()); + //fCameraStack.Append((plCameraModifier1*)pKey->GetObjectPtr()); + + if (fThirdPersonCam) + PushCamera(fThirdPersonCam); + SetRender(bRender); + + fX = fY = 0.5f; + fRetainedFY = 0.5f; + ClearFlags(kAvatarWalking); + ClearFlags(kUnPanCamera); + ClearFlags(kInterpPanLimits); + ClearFlags(kResponderForced3rd); + ClearFlags(kScriptsDisabled1st); + ClearFlags(kFalling); + ClearFlags(kFirstPersonEnabled); + + #ifdef STATUS_LOG + camLog->AddLineF("Virtual Camera Reset"); + #endif + +} + +void plVirtualCam1::ClearStack() +{ + fCameraStack.SetCountAndZero(0); + plUoid U(kDefaultCameraMod1_KEY); + plKey pKey = hsgResMgr::ResMgr()->FindKey(U); + if (pKey) + PushCamera((plCameraModifier1*)pKey->GetObjectPtr()); + + #ifdef STATUS_LOG + camLog->AddLineF("Camera Stack Cleared"); + #endif + +} + +plCameraModifier1* plVirtualCam1::GetCurrentCamera() +{ + if (GetCurrentStackCamera() == fDriveCamera) + return fDriveCamera; + if (fPythonOverride) + return (fPythonOverride); + if (fFirstPersonOverride) + return(fFirstPersonOverride); + if (fTransPos == POS_TRANS_FOLLOW) + return(fTransitionCamera); + + if (fCameraStack.Count()) + return fCameraStack[fCameraStack.Count() - 1]; + else return nil; +} + +hsBool plVirtualCam1::Is1stPersonCamera() +{ + if (GetCurrentStackCamera() == fDriveCamera) + return false; + if (fPythonOverride) + return false; + if (fFirstPersonOverride) + return true; + + else return false; +} + +plCameraModifier1* plVirtualCam1::GetCurrentStackCamera() +{ + if (fCameraStack.Count()) + return fCameraStack[fCameraStack.Count() - 1]; + else return nil; +} + +void plVirtualCam1::ICreatePlate() +{ + int x, y; + + + fEffectPlate = nil; + + // +0.01 to deal with the half-pixel antialiasing stuff + plPlateManager::Instance().CreatePlate( &fEffectPlate, 0, 0, 2.01, 2.01 ); + + // hack for now--create a black layer that we will animate the opacity on + plMipmap *ourMip = fEffectPlate->CreateMaterial( 16, 16, true ); + for( y = 0; y < ourMip->GetHeight(); y++ ) + { + UInt32 *pixels = ourMip->GetAddr32( 0, y ); + for( x = 0; x < ourMip->GetWidth(); x++ ) + pixels[ x ] = 0xff000000; + } + if( fEffectPlate == nil ) + ICreatePlate(); + fEffectPlate->SetVisible( false ); +} + + +void plVirtualCam1::SetCutNextTrans() +{ + SetFlags(kCutNextTrans); + SetRender(true); +#ifdef STATUS_LOG + camLog->AddLineF("Set Camera to cut on next transition"); +#endif +} + +void plVirtualCam1::SetRender(hsBool render) +{ + fFlags.SetBit(kRender,render); + if (render) + { + #ifdef STATUS_LOG + camLog->AddLineF("Virtual Camera Render Updates Enabled"); + if (fEffectPlate) + fEffectPlate->SetVisible(false); + #endif + } + else + { + #ifdef STATUS_LOG + camLog->AddLineF("Virtual Camera Render Updates Disabled"); + if (fEffectPlate) + fEffectPlate->SetVisible(true); + #endif + } + + +} + +// hack, hack, hack +hsBool plVirtualCam1::RestoreFromName(const char* name) +{ + for(int i = 0; i < fCamerasLoaded.Count(); i++) + { + if (strcmp(name, fCamerasLoaded[i]->GetKeyName()) == 0) + { + RebuildStack(fCamerasLoaded[i]->GetKey()); + return true; + } + } + return false; +} +void plVirtualCam1::Next() +{ +} + +void plVirtualCam1::Prev() +{ +} + +void plVirtualCam1::PushThirdPerson() +{ + if (fThirdPersonCam) + { +#ifdef STATUS_LOG + camLog->AddLineF("Restore failed, forcing built-in 3rd person camera"); +#endif + ClearStack(); + SetCutNextTrans(); + PushCamera(fThirdPersonCam); + return; + } +#ifdef STATUS_LOG + camLog->AddLineF("Restore failed, 3rd person camera not available for switch: attempting to force 1st person"); +#endif + FirstPersonOverride(); + +} + + +// +// Make adjustments to camera position based on +// user input - NOTE this is for mouse-cursor based adjustment +// +void plVirtualCam1::StartInterpPanLimits() +{ + plCameraBrain1* pBrain = 0; + if (fPythonOverride && fPythonOverride->GetBrain()) + pBrain = fPythonOverride->GetBrain(); + if (pBrain == 0 && GetCurrentStackCamera() && GetCurrentStackCamera()->GetBrain()) + pBrain = GetCurrentStackCamera()->GetBrain(); + + if (pBrain) + { + if (pBrain->GetXPanLimit() == fXPanLimit && + pBrain->GetZPanLimit() == fZPanLimit ) + { + ClearFlags(kInterpPanLimits); + return; + } + fXPanLimitGoal = pBrain->GetXPanLimit(); + fZPanLimitGoal = pBrain->GetZPanLimit(); + fXPanInterpRate = fXPanLimitGoal - fXPanLimit; + fZPanInterpRate = fZPanLimitGoal - fZPanLimit; + SetFlags(kInterpPanLimits); + fInterpPanLimitTime = hsTimer::GetSysSeconds() + 1.0f; + } +} + +void plVirtualCam1::InterpPanLimits() +{ + if (fXPanLimitGoal == fXPanLimit && fZPanLimitGoal == fZPanLimit) + { + ClearFlags(kInterpPanLimits); + return; + } + fXPanLimit += fXPanInterpRate * hsTimer::GetDelSysSeconds(); + fZPanLimit += fZPanInterpRate * hsTimer::GetDelSysSeconds(); + if ((fZPanInterpRate > 0 && fZPanLimit >= fZPanLimitGoal) || + (fZPanInterpRate < 0 && fZPanLimit <= fZPanLimitGoal) ) + { + fZPanLimit = fZPanLimitGoal; + fZPanInterpRate = 0; + } + if ((fXPanInterpRate > 0 && fXPanLimit >= fXPanLimitGoal) || + (fXPanInterpRate < 0 && fXPanLimit <= fXPanLimitGoal) ) + { + fXPanLimit = fXPanLimitGoal; + fXPanInterpRate = 0; + } + if (fXPanInterpRate == 0) + fXPanLimit = fXPanLimitGoal; + if (fZPanInterpRate == 0) + fZPanLimit = fZPanLimitGoal; +} + +void plVirtualCam1::StartUnPan() +{ + if (!HasFlags(kUnPanCamera)) + { + SetFlags(kUnPanCamera); + if (HasFlags(kFalling)) + { + fUnPanEndTime = hsTimer::GetSysSeconds() + 0.5; + fXUnPanRate = (0.5f - fX) / 0.5f; + fZUnPanRate = (0.5f - fY) / 0.5f; + + } + else + { + fUnPanEndTime = hsTimer::GetSysSeconds() + fPanResponseTime; + fXUnPanRate = (0.5f - fX) / fPanResponseTime; + fZUnPanRate = (0.5f - fY) / fPanResponseTime; + } + } +} + +void plVirtualCam1::UnPanIfNeeded() +{ + if (HasFlags(kUnPanCamera)) + { + if (fX == 0.5f && fY == 0.5f) + { + ClearFlags(kUnPanCamera); + return; + } + fX += fXUnPanRate * hsTimer::GetDelSysSeconds(); + fY += fZUnPanRate * hsTimer::GetDelSysSeconds(); + if ((fXUnPanRate > 0 && fX >= 0.5f) || (fXUnPanRate < 0 && fX <= 0.5f)) + { + fX = 0.5f; + fXUnPanRate = 0; + } + if ((fZUnPanRate > 0 && fY >= 0.5f) || (fZUnPanRate < 0 && fY <= 0.5f)) + { + fY = 0.5f; + fZUnPanRate = 0; + } + } +} + + +void plVirtualCam1::AdjustForInput() +{ + if (!fFirstPersonOverride) + { + if (HasFlags(kInterpPanLimits)) + InterpPanLimits(); + + UnPanIfNeeded(); + + hsScalar panSpeed = 0.5f; + double secs = hsTimer::GetDelSysSeconds(); + + if (HasMovementFlag(B_CAMERA_PAN_UP)) + fY -= (hsScalar)(panSpeed * secs); + if (HasMovementFlag(B_CAMERA_PAN_DOWN)) + fY += (hsScalar)(panSpeed * secs); + if (HasMovementFlag(B_CAMERA_PAN_LEFT)) + fX -= (hsScalar)(panSpeed * secs); + if (HasMovementFlag(B_CAMERA_PAN_RIGHT)) + fX += (hsScalar)(panSpeed * secs); + } + if ((fY == 0.5f && fX == 0.5f) && + fFirstPersonOverride == nil) + return; + + if (fY > 1.0f) + fY = 1.0f; + if (fY < 0.0f) + fY = 0.0f; + if (fX > 1.0f) + fX = 1.0f; + if (fX < 0.0f) + fX = 0.0f; + + if ((fXPanLimit == 0.0f && fZPanLimit == 0.0f) && + fFirstPersonOverride == nil) + return; + + hsMatrix44 m; + + hsVector3 v1(fOutputPOA - fOutputPos); + hsVector3 v2(0,0,1); + v1.Normalize(); + hsVector3 up = (v2 % v1) % v1; + + m.Make(&fOutputPos, &fOutputPOA, &up); + + + // scale maximum angle by % mouse input + + hsScalar scaledX; + if (fFirstPersonOverride) + scaledX = 3.14159; + else + scaledX = (hsScalar)(3.14159 - (fXPanLimit * ( (fX - 0.5f) / 0.5f))); + + hsScalar scaledZ; + if (fFirstPersonOverride) + scaledZ = (hsScalar)(3.14159 - (0.872f * ( (fY - 0.5f) / 0.5f))); + else + scaledZ = (hsScalar)(3.14159 - (fZPanLimit * ( (fY - 0.5f) / 0.5f))); + + hsMatrix44 mX; + hsMatrix44 mZ; + + hsVector3 right = m.GetAxis(hsMatrix44::kRight); + up = m.GetAxis(hsMatrix44::kUp); + up *= -1; + hsQuat qX(scaledX, &up); + hsQuat qZ(scaledZ, &right); + + qX.MakeMatrix(&mX); + qZ.MakeMatrix(&mZ); + + m = mX * m; + m = mZ * m; + + hsVector3 view = m.GetAxis(hsMatrix44::kView); + fOutputPOA = fOutputPos + (view * 15); + +} + + +void plVirtualCam1::IUpdate() +{ + if (!HasFlags(kRender)) + return; + + + if (fDriveCamera) + fDriveCamera->Update(); + if (fPythonOverride) + fPythonOverride->Update(); + if (fFirstPersonOverride) + fFirstPersonOverride->Update(); + if (fTransitionCamera) + fTransitionCamera->Update(); + + RunTransition(); + + for (int i=0; i < fCameraStack.Count(); i++) + { + hsBool update = true; + for (int j=i+1; jGetBrain()) + { + fCameraStack[i]->GetBrain()->SetFlags(plCameraBrain1::kCutPos); + fCameraStack[i]->GetBrain()->SetFlags(plCameraBrain1::kCutPOA); + } + if(fForceCutOnce) + { + fCameraStack[i]->GetBrain()->SetFlags(plCameraBrain1::kCutPosOnce); + fCameraStack[i]->GetBrain()->SetFlags(plCameraBrain1::kCutPOAOnce); + } + fCameraStack[i]->Update(); + } + } + if(fForceCutOnce)fForceCutOnce=false; + + Output(); +} + + +void plVirtualCam1::Output() +{ + if (fFreezeCounter) + { + fFreezeCounter-=1; + GetCurrentCamera()->GetBrain()->SetFlags(plCameraBrain1::kCutPOAOnce); + GetCurrentCamera()->GetBrain()->SetFlags(plCameraBrain1::kCutPosOnce); + return; + } + if (fFadeCounter) + { + fFadeCounter-=1; + if (fFadeCounter == 0 && fFirstPersonOverride == nil) + { + plEnableMsg* pMsg = TRACKED_NEW plEnableMsg; + pMsg->SetSender(GetKey()); + pMsg->SetCmd(plEnableMsg::kEnable); + pMsg->AddType(plEnableMsg::kDrawable); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + pMsg->AddReceiver(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + plgDispatch::MsgSend(pMsg); + } + } + hsMatrix44 targetMatrix; + hsMatrix44 inverse; + if (GetCurrentCamera()) + { + fOutputPos = GetCurrentCamera()->GetTargetPos(); + fOutputPOA = GetCurrentCamera()->GetTargetPOA(); + AdjustForInput(); + } + else + { + return; + } + // construct output matrix + hsVector3 abUp(0,0,1); + hsVector3 view(fOutputPos - fOutputPOA); + view.Normalize(); + // Now passing in Up for up parameter to MakeCamera. Negates sense of up. mf_flip_up - mf + hsVector3 up = (view % abUp) % view; + if (GetCurrentCamera()->IsAnimated()) + up = -GetCurrentCamera()->GetTarget()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + + fOutputUp = up; + targetMatrix.MakeCamera(&fOutputPos,&fOutputPOA, &up); + targetMatrix.GetInverse(&inverse); + fPipe->SetWorldToCamera( targetMatrix, inverse ); + if (HasFlags(kSetFOV)) // are we changing the field of view? + { + ClearFlags(kSetFOV); + fPipe->SetFOV(fFOVw,fFOVh); + fPipe->RefreshMatrices(); + if (foutLog) + { + fprintf(foutLog, "****************************************************************\n"); + fprintf(foutLog, "FOV changed to %f %f\n",fFOVh, fFOVw); + fprintf(foutLog, "****************************************************************\n"); + } + + } +/* if (foutLog) + { + fprintf(foutLog, "output pos %f %f %f\n", fOutputPos.fX,fOutputPos.fY,fOutputPos.fZ); + fprintf(foutLog, "output poa %f %f %f\n", fOutputPOA.fX,fOutputPOA.fY,fOutputPOA.fZ); + fprintf(foutLog, "\n"); + } */ +} + +void plVirtualCam1::Init() +{ + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plCameraMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plMouseEventMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plIfaceFadeAvatarMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); + + // register for control messages + plCmdIfaceModMsg* pModMsg = TRACKED_NEW plCmdIfaceModMsg; + pModMsg->SetBCastFlag(plMessage::kBCastByExactType); + pModMsg->SetSender(GetKey()); + pModMsg->SetCmd(plCmdIfaceModMsg::kAdd); + plgDispatch::MsgSend(pModMsg); +} + +// +// toggle between player-enforced 1st person mode and back +// +void plVirtualCam1::FirstPersonOverride() +{ + if (!HasFlags(kRender)) + return; + + if (fFirstPersonOverride) + { + fFirstPersonOverride->Pop(); + GetCurrentStackCamera()->Push(!HasFlags(kAvatarWalking)); + GetCurrentStackCamera()->GetBrain()->SetFlags(plCameraBrain1::kCutPosOnce); + GetCurrentStackCamera()->GetBrain()->SetFlags(plCameraBrain1::kCutPOAOnce); + fFirstPersonOverride = nil; +#ifdef STATUS_LOG + camLog->AddLineF("Built-In First Person Camera Disabled"); +#endif + SetFOV(GetCurrentStackCamera()->GetFOVw(), GetCurrentStackCamera()->GetFOVh(), GetCurrentStackCamera()); + GetCurrentStackCamera()->Push(!HasFlags(kAvatarWalking)); + plAvatarInputInterface::GetInstance()->CameraInThirdPerson(true); + FreezeOutput(2); + UnFadeAvatarIn(1); + fRetainedFY = fY; + fX = fY = 0.5f; + } + else + if (HasFlags(kFirstPersonEnabled)) + { +// plCameraBrain1* pBrain = nil; +// if (GetCurrentStackCamera() && GetCurrentStackCamera()->GetBrain()) +// { +// pBrain = plCameraBrain1_FirstPerson::ConvertNoRef(GetCurrentStackCamera()->GetBrain()); +// if (pBrain) // already in 1st person mode, don't use override +// return; +// } + plUoid U(kDefaultCameraMod1_KEY); + plKey pKey = hsgResMgr::ResMgr()->FindKey(U); + if (pKey) + { + fFirstPersonOverride = (plCameraModifier1*)pKey->GetObjectPtr(); + GetCurrentStackCamera()->Pop(); + fFirstPersonOverride->Push(!HasFlags(kAvatarWalking)); + SetFOV(fFirstPersonOverride->GetFOVw(), fFirstPersonOverride->GetFOVh(), fFirstPersonOverride); + plAvatarInputInterface::GetInstance()->CameraInThirdPerson(false); + // no need to keep transitioning if we are currently... + if (fTransPos == POS_TRANS_FOLLOW) + FinishTransition(); + fY = fRetainedFY; + + +#ifdef STATUS_LOG + camLog->AddLineF("Built-In First Person Camera Enabled"); +#endif + } + } +} + +hsBool plVirtualCam1::MsgReceive(plMessage* msg) +{ + plPlayerPageMsg* pPMsg = plPlayerPageMsg::ConvertNoRef(msg); + if (pPMsg) + { + if (!pPMsg->fUnload && pPMsg->fPlayer == plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + { + /*if (HasFlags(kRegisteredForBehaviors)) + return true;*/ // not reliable anymore since we have a dummy avatar in the startup age + plSceneObject* avSO = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); + if (avSO) + { + + plArmatureMod* avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + { + avMod->RegisterForBehaviorNotify(GetKey()); + // SetFlags(kRegisteredForBehaviors); // not reliable anymore since we have a dummy avatar in the startup age + } + } + } + return true; + } + plAvatarBehaviorNotifyMsg *behNotifymsg = plAvatarBehaviorNotifyMsg::ConvertNoRef(msg); + if (behNotifymsg) + { + if (behNotifymsg->fType == plHBehavior::kBehaviorTypeLinkIn && behNotifymsg->state == true) + { + if (!HasFlags(kScriptsForced3rd) && !HasFlags(kScriptsDisabled1st)) + SetFlags(kFirstPersonEnabled); + + if (HasFlags(kFirstPersonUserSelected)) + { + ClearFlags(kFirstPersonAtLinkOut); + ClearFlags(kScriptsForced3rd); + if (!HasFlags(kScriptsForced3rd)) + { + FirstPersonOverride(); + //SetFlags(kJustLinkedIn); + } + else + plAvatarInputInterface::GetInstance()->CameraInThirdPerson(true); + } + else + if (fFirstPersonOverride == nil) + { + plAvatarInputInterface::GetInstance()->CameraInThirdPerson(true); + } + } + else + if (behNotifymsg->fType == plHBehavior::kBehaviorTypeLinkOut && behNotifymsg->state == true) + { + ClearFlags(kFirstPersonEnabled); + if (fFirstPersonOverride || HasFlags(kResponderForced3rd)) + SetFlags(kFirstPersonAtLinkOut); + } + return true; + } + plControlEventMsg* pCtrlMsg = plControlEventMsg::ConvertNoRef(msg); + if (pCtrlMsg) + { + SetMovementFlag(pCtrlMsg->GetControlCode(), pCtrlMsg->ControlActivated()); + + if (pCtrlMsg->GetControlCode() == S_SET_FREELOOK && pCtrlMsg->ControlActivated()) + ClearFlags(kUnPanCamera); + + if (pCtrlMsg->GetControlCode() == B_CONTROL_MOVE_FORWARD || + pCtrlMsg->GetControlCode() == B_CONTROL_MOVE_BACKWARD || + pCtrlMsg->GetControlCode() == B_CONTROL_ROTATE_RIGHT || + pCtrlMsg->GetControlCode() == B_CONTROL_ROTATE_LEFT ) + { + if (!HasMovementFlag(S_SET_FREELOOK)) + StartUnPan(); + } + + if (pCtrlMsg->GetControlCode() == B_CONTROL_MOVE_FORWARD || pCtrlMsg->GetControlCode() == B_CONTROL_MOVE_BACKWARD) + { + fFlags.SetBit(kAvatarWalking, pCtrlMsg->ControlActivated()); + } + else + if (pCtrlMsg->GetControlCode() == S_SET_FIRST_PERSON_MODE && pCtrlMsg->ControlActivated()) + { + if (HasFlags(kFirstPersonEnabled)) + { + if (HasFlags(kFirstPersonUserSelected)) + ClearFlags(kFirstPersonUserSelected); + else + SetFlags(kFirstPersonUserSelected); + } + FirstPersonOverride(); + return true; + } + else + if (pCtrlMsg->GetControlCode() == B_CAMERA_RECENTER && pCtrlMsg->ControlActivated()) + { + fX = fY = fRetainedFY = 0.5f; + } + if (pCtrlMsg->GetControlCode() == B_TOGGLE_DRIVE_MODE && pCtrlMsg->ControlActivated()) + Drive(); + else + { + if (fPythonOverride) + fPythonOverride->MsgReceive(msg); + else + if (fFirstPersonOverride) + fFirstPersonOverride->MsgReceive(msg); + GetCurrentStackCamera()->MsgReceive(msg); + } + return true; + } + plMouseEventMsg* pMouseMsg = plMouseEventMsg::ConvertNoRef(msg); + if( pMouseMsg ) + { + if (!HasFlags(kFalling)) + { + hsScalar dX = pMouseMsg->GetDX(); + hsScalar dY = pMouseMsg->GetDY(); + if (plMouseDevice::GetInverted()) + { + dX *= -1.f; + dY *= -1.f; + } + if (HasMovementFlag(S_SET_FREELOOK)) + { + if (pMouseMsg->GetDX() < 0.4 && pMouseMsg->GetDX() > -0.4) + { + fX -= dX; + } + if (pMouseMsg->GetDY() < 0.4 && pMouseMsg->GetDY() > -0.4) + { + fY -= dY; + } + } + if ((HasMovementFlag(B_CONTROL_CAMERA_WALK_PAN)) && + (fFirstPersonOverride || WalkPan3rdPerson == true)) + { + if (pMouseMsg->GetDY() < 0.4 && pMouseMsg->GetDY() > -0.4) + { + fY -= dY; + } + } + } + fDriveCamera->MsgReceive(msg); + return true; + } + plWarpMsg* pWarpMsg = plWarpMsg::ConvertNoRef(msg); + if (pWarpMsg) + { + SetCutNextTrans(); + return true; + } + + plCameraMsg* pCam = plCameraMsg::ConvertNoRef(msg); + if (pCam) + { + if (pCam->Cmd(plCameraMsg::kResetPanning)) + { + fX = fY = fRetainedFY = 0.5f; + return true; + } + else + if (pCam->Cmd(plCameraMsg::kUpdateCameras)) + { + IUpdate(); + return true; + } + else + if (pCam->Cmd(plCameraMsg::kResetOnEnter)) + { + // don't send this message to the virtual camera! + Reset(false); + // this only happens when the player links into the world + return true; + } + else + if (pCam->Cmd(plCameraMsg::kResetOnExit)) + { + /* + Kind of an ugly hack, but it works. The avatar is being loaded/unloaded when + the player enters/leaves the age, and the following Reset starts a camera transition + which may reference the avatar's sceneobject and crash the client when the next + update happens. With the kCutNextTrans flag set it doesn't bother with the + transition so there isn't any reading of invalid memory. + */ + SetFlags(kCutNextTrans); + + // don't send this message to the virtual camera! + Reset(false); + // this only happens when the player links out of the world + return true; + } + else + if (pCam->Cmd(plCameraMsg::kCreateNewDefaultCam)) + { + if (pCam->GetSubject() == plNetClientMgr::GetInstance()->GetLocalPlayer()) + CreateDefaultCamera(pCam->GetSubject()); + return true; + } + else + if (pCam->Cmd(plCameraMsg::kPythonOverridePop)) + { + if (pCam->GetTriggerer() && pCam->GetTriggerer() != plNetClientApp::GetInstance()->GetLocalPlayerKey()) + return true; + { + if (fPythonOverride) + { + fPythonOverride->Pop(); + FinishTransition(); + if (plCameraBrain1_FirstPerson::ConvertNoRef(fPythonOverride->GetBrain()) && + !fFirstPersonOverride) + { + FreezeOutput(2); + UnFadeAvatarIn(1); + } + GetCurrentStackCamera()->GetBrain()->SetFlags(plCameraBrain1::kCutPosOnce); + GetCurrentStackCamera()->GetBrain()->SetFlags(plCameraBrain1::kCutPOAOnce); + fX = fY = 0.5f; + } + fPythonOverride = nil; + SetFlags(kFirstPersonEnabled); +#ifdef STATUS_LOG + camLog->AddLineF("Override Python Camera Disabled"); +#endif + if (fFirstPersonOverride) + { + SetFOV(fFirstPersonOverride->GetFOVw(), fFirstPersonOverride->GetFOVh(), fFirstPersonOverride); + fFirstPersonOverride->Push(!HasFlags(kAvatarWalking)); + } + else + { + SetFOV(GetCurrentStackCamera()->GetFOVw(), GetCurrentStackCamera()->GetFOVh(), GetCurrentStackCamera()); + GetCurrentStackCamera()->Push(!HasFlags(kAvatarWalking)); + } + + StartInterpPanLimits(); + if (fFirstPersonOverride) + plAvatarInputInterface::GetInstance()->CameraInThirdPerson(false); + + if (foutLog) + { + fprintf(foutLog, "********************************************\n"); + fprintf(foutLog, "popped python camera\n"); + fprintf(foutLog, "********************************************\n"); + } + + } + } + else + if (pCam->Cmd(plCameraMsg::kPythonOverridePush) || pCam->Cmd(plCameraMsg::kPythonOverridePushCut)) + { + if (pCam->GetTriggerer() && pCam->GetTriggerer() != plNetClientApp::GetInstance()->GetLocalPlayerKey()) + return true; + { + plCameraModifier1* pCamMod = plCameraModifier1::ConvertNoRef(pCam->GetNewCam()->GetObjectPtr()); + if (pCamMod) + { + + if (fPythonOverride) + { + fPythonOverride->Pop(); + if (plCameraBrain1_FirstPerson::ConvertNoRef(fPythonOverride->GetBrain())) + { + FreezeOutput(2); + UnFadeAvatarIn(1); + } + +#ifdef STATUS_LOG + camLog->AddLineF("Override Python Camera Popped because new one coming in"); +#endif + } + fPythonOverride = pCamMod; +#ifdef STATUS_LOG + camLog->AddLineF("Override Python Camera Pushing onto stack"); +#endif + if (foutLog) + { + fprintf(foutLog, "********************************************\n"); + fprintf(foutLog, "changed to new camera\n"); + fprintf(foutLog, "********************************************\n"); + } + if (fFirstPersonOverride) + plAvatarInputInterface::GetInstance()->CameraInThirdPerson(true); + + fPythonOverride->Push(!HasFlags(kAvatarWalking)); + + CamTrans* pTrans = TRACKED_NEW CamTrans(fPythonOverride->GetKey()); + if (pCam->Cmd(plCameraMsg::kPythonOverridePushCut)) + pTrans->fCutPOA = pTrans->fCutPos = true; + StartTransition(pTrans); + delete(pTrans); + SetFOV(fPythonOverride->GetFOVw(), fPythonOverride->GetFOVh(), fPythonOverride); + ClearFlags(kFirstPersonEnabled); + } + } + } + else + if ( pCam->Cmd(plCameraMsg::kPythonSetFirstPersonOverrideEnable)) + { + if (!pCam->HasBCastFlag(plMessage::kNetNonLocal)) + { + // make sure it was locally sent + if (pCam->GetActivated()) + { + SetFlags(kFirstPersonEnabled); + ClearFlags(kScriptsDisabled1st); + if (HasFlags(kScriptsForced3rd)) + { + ClearFlags(kScriptsForced3rd); + FirstPersonOverride(); + } + } + else + { + ClearFlags(kFirstPersonEnabled); + SetFlags(kScriptsDisabled1st); + } + } + } + else + if ( pCam->Cmd(plCameraMsg::kPythonUndoFirstPerson)) + { + if (!pCam->HasBCastFlag(plMessage::kNetNonLocal)) + { + // make sure it was locally sent + if (HasFlags(kFirstPersonAtLinkOut)) + { + SetFlags(kScriptsForced3rd); + } + else + if (fFirstPersonOverride) + { + SetFlags(kScriptsForced3rd); + FirstPersonOverride(); +#ifdef STATUS_LOG + camLog->AddLineF("Forcing 3rd Person from scripts"); +#endif + SetFOV(GetCurrentStackCamera()->GetFOVw(), GetCurrentStackCamera()->GetFOVh(), GetCurrentStackCamera()); + } + } + } + else + if ( pCam->Cmd(plCameraMsg::kResponderSetThirdPerson)) + { + if (plVirtualCam1::StayInFirstPersonForever) + return true; + + if (HasFlags(kJustLinkedIn)) + { + ClearFlags(kJustLinkedIn); + plCameraTargetFadeMsg* pMsg = TRACKED_NEW plCameraTargetFadeMsg; + pMsg->SetFadeOut(true); + pMsg->SetSubjectKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE); + pMsg->AddReceiver(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + plgDispatch::MsgSend(pMsg); + return true; + } + if (HasFlags(kScriptsForced3rd)) + return true; + if (fFirstPersonOverride) + { + SetFlags(kResponderForced3rd); + FirstPersonOverride(); +#ifdef STATUS_LOG + camLog->AddLineF("Forcing 3rd Person from code"); +#endif + SetFOV(GetCurrentStackCamera()->GetFOVw(), GetCurrentStackCamera()->GetFOVh(), GetCurrentStackCamera()); + } + ClearFlags(kFirstPersonEnabled); +#ifdef STATUS_LOG + camLog->AddLineF("1st person override disabled"); +#endif + } + else + if ( pCam->Cmd(plCameraMsg::kResponderUndoThirdPerson)) + { + if (HasFlags(kScriptsForced3rd) || HasFlags(kScriptsDisabled1st)) + return true; + SetFlags(kFirstPersonEnabled); +#ifdef STATUS_LOG + camLog->AddLineF("1st person override enabled"); +#endif + + if (HasFlags(kResponderForced3rd)) + { + FirstPersonOverride(); + ClearFlags(kResponderForced3rd); +#ifdef STATUS_LOG + camLog->AddLineF("Restoring 1st Person from code"); +#endif + } + + } + else + if (pCam->Cmd(plCameraMsg::kRegionPushCamera)) + { + if (HasFlags(kRegionIgnore)) + return true; + if (pCam->GetTriggerer() && pCam->GetTriggerer() != plNetClientApp::GetInstance()->GetLocalPlayerKey()) + return true; + { + hsBool bDef = pCam->Cmd(plCameraMsg::kSetAsPrimary); + plKey pCamKey = pCam->GetNewCam(); + if (pCamKey) + { + plCameraModifier1* pCamMod = plCameraModifier1::ConvertNoRef(pCamKey->GetObjectPtr()); + if (!pCamMod) + { + plSceneObject* pObj = plSceneObject::ConvertNoRef( pCamKey->GetObjectPtr() ); + if (!pObj) + return true; + for (int i = 0; i < pObj->GetNumModifiers(); i++) + { + plKey pModKey = pObj->GetModifier(i)->GetKey(); + pCamMod = plCameraModifier1::ConvertNoRef( pModKey->GetObjectPtr() ); + if ( pCamMod ) + break; + } + } + if (!pCamMod) + return true; + if (pCam->Cmd(plCameraMsg::kEntering) || pCam->Cmd(plCameraMsg::kResponderTrigger)) + PushCamera(pCamMod, bDef); + else + PopCamera(pCamMod); + if (pCam->Cmd(plCameraMsg::kCut)) + SetFlags(kCutNextTrans); + } + } + } + } + plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); + if (pRefMsg ) + { + if( pRefMsg->GetContext() & (plRefMsg::kOnDestroy | plRefMsg::kOnRemove) ) + { + // unfortunately we cannot rely on the ref message to point at a valid camera + // (the pointer will match our stack, but it might not be pointing at a valid + // chunk of memory). Therefore, we cannot use the ConvertNoRef() call, and we + // cannot call PopCamera. We simply have to settle for removing it from our + // array. Since this message indicates it was destroyed anyway, this should be + // ok. + plCameraModifier1* pMod = (plCameraModifier1*)(pRefMsg->GetRef()); + // we go in reverse so that removes don't mess up our index + for (int i = fCameraStack.Count() - 1; i >= 0; --i) + { + if (fCameraStack[i] == pMod) + fCameraStack.Remove(i); + } + } + return true; + } + return hsKeyedObject::MsgReceive(msg); +} + +void plVirtualCam1::CreateDefaultCamera(plSceneObject* subject) +{ + // If a default cam already exists, we just want to replace the subject (unless it's the same) + plUoid U(kDefaultCameraMod1_KEY); + plKey pKey = hsgResMgr::ResMgr()->FindKey(U); + if (pKey) + { + plCameraModifier1* mod = plCameraModifier1::ConvertNoRef(pKey->GetObjectPtr()); + if (mod->GetSubject() == subject) + return; + + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(mod->GetKey(), plRefMsg::kOnReplace, 0, plCameraBrain1::kSubject ); + msg->SetOldRef(mod->GetSubject()); + hsgResMgr::ResMgr()->AddViaNotify(subject->GetKey(), msg, plRefFlags::kPassiveRef); + } + else + { + plCameraModifier1* pMod = TRACKED_NEW plCameraModifier1; + plCameraBrain1_FirstPerson* pBrain = TRACKED_NEW plCameraBrain1_FirstPerson(pMod); + pMod->RegisterAs( kDefaultCameraMod1_KEY ); + //pBrain->SetSubject(subject); + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(pMod->GetKey(), plRefMsg::kOnCreate, 0, plCameraBrain1::kSubject ); // SceneObject + hsgResMgr::ResMgr()->AddViaNotify(subject->GetKey(), msg, plRefFlags::kPassiveRef); + + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), pMod->GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plCameraMsg::Index(), pMod->GetKey()); + + pMod->SetFOVw(90.0f); + pMod->SetFOVh(66.7f); + // set up the brain and to be first-person + hsVector3 pt(0,0.0f,5.5); + pBrain->SetOffset(pt); + pt.Set(0,-10,5.5); + pBrain->SetPOAOffset(pt); + pBrain->SetZPanLimit(0.872f); //radians, == 50 degrees as Decreed By Rand! + pBrain->SetXPanLimit(0.872f); + pBrain->SetFlags(plCameraBrain1::kCutPOA); + pBrain->SetFlags(plCameraBrain1::kCutPos); + PushCamera(pMod); + } + // now deal with the third person camera + // If a default 3rd person cam already exists, we just want to replace the subject (unless it's the same) + plUoid Ux(kBuiltIn3rdPersonCamera_KEY); + plKey pKeyx = hsgResMgr::ResMgr()->FindKey(Ux); + if (pKeyx) + { + plCameraModifier1* mod = plCameraModifier1::ConvertNoRef(pKeyx->GetObjectPtr()); + if (mod->GetSubject() == subject) + return; + + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(mod->GetKey(), plRefMsg::kOnReplace, 0, plCameraBrain1::kSubject ); + msg->SetOldRef(mod->GetSubject()); + hsgResMgr::ResMgr()->AddViaNotify(subject->GetKey(), msg, plRefFlags::kPassiveRef); + } + else + { + plCameraModifier1* pModx = TRACKED_NEW plCameraModifier1; + plCameraBrain1_Avatar* pBrainx = TRACKED_NEW plCameraBrain1_Avatar(pModx); + pModx->RegisterAs( kBuiltIn3rdPersonCamera_KEY ); + plGenRefMsg* msgx = TRACKED_NEW plGenRefMsg(pModx->GetKey(), plRefMsg::kOnCreate, 0, plCameraBrain1::kSubject ); // SceneObject + hsgResMgr::ResMgr()->AddViaNotify(subject->GetKey(), msgx, plRefFlags::kPassiveRef); + + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), pModx->GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plCameraMsg::Index(), pModx->GetKey()); + + pModx->SetFOVw(90.0f); + pModx->SetFOVh(66.7f); + // set up the brain and to be third-person + hsVector3 ptx(0, 15, 10); + pBrainx->SetOffset(ptx); + ptx.Set(0,0,5.5); + pBrainx->SetPOAOffset(ptx); + pBrainx->SetZPanLimit(0.872f); + pBrainx->SetXPanLimit(0.872f); + pBrainx->SetVelocity(20.0f); + pBrainx->SetDecel(10.0f); + pBrainx->SetAccel(5.0f); + pBrainx->SetFlags(plCameraBrain1::kCutPOA); + pBrainx->SetFlags(plCameraBrain1::kMaintainLOS); + PushCamera(pModx); + fThirdPersonCam = pModx; + } +} + +void plVirtualCam1::AddCameraToStack(plCameraModifier1* pCam) +{ + fCameraStack.Append(pCam); + if (pCam->GetBrain()) + { + if (HasMovementFlag(B_CONTROL_CAMERA_WALK_PAN)) + pCam->GetBrain()->SetMovementFlag(B_CONTROL_CAMERA_WALK_PAN); + } + + if (pCam->GetKey()) + hsgResMgr::ResMgr()->AddViaNotify(pCam->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefCamera), plRefFlags::kPassiveRef); +} + +void plVirtualCam1::PushCamera(plCameraModifier1* pCam, hsBool bDefault) +{ + // pushing the same camera, folks? + if (pCam == GetCurrentStackCamera()) + { + AddCameraToStack(pCam); + return; + } + // make sure that we don't keep adding the default camera if we're already in it + if (bDefault && pCam == GetCurrentStackCamera()) + return; + + // look up whatever transition we might have specified + CamTrans* pTrans = nil; + if (pCam->GetNumTrans()) + { + for (int i = 0; i < pCam->GetNumTrans(); i++) + { + if (pCam->GetTrans(i)->fTransTo == GetCurrentStackCamera()->GetKey()) + { // if it is specifically for this camera, this is the one to use + pTrans = pCam->GetTrans(i); + break; + } + else // if it's generic, assign it but keep looking for a specific one... + if (pCam->GetTrans(i)->fTransTo == nil) + pTrans = pCam->GetTrans(i); + } + } + // bail out if we are supposed to be ignoring this camera + if (pTrans && pTrans->fIgnore) + return; + + // lose the drive camera if that's where we are at + if (GetCurrentStackCamera() == fDriveCamera) + PopCamera(fDriveCamera); + + if (plCameraBrain1_Drive::ConvertNoRef(pCam->GetBrain()) || + !GetCurrentStackCamera()) + { + // special camera mode (like drive) just add it + if (GetCurrentStackCamera()) + GetCurrentStackCamera()->Pop(); + pCam->Push(!HasFlags(kAvatarWalking)); + AddCameraToStack(pCam); + return; + } + +#ifdef STATUS_LOG + IHandleCameraStatusLog(pCam, kPush); +#endif + + // are we mouse-looking? + if (GetCurrentStackCamera() && GetCurrentStackCamera()->GetBrain() && GetCurrentStackCamera()->GetBrain()->HasMovementFlag(S_SET_FREELOOK)) + pCam->GetBrain()->SetMovementFlag(S_SET_FREELOOK); + + // do anything special upon activating this camera + if (GetCurrentStackCamera()) + { + // is the avatar faded out? + if (GetCurrentStackCamera()->GetFaded() &&!fFirstPersonOverride) + { + if (!pCam->SetFaded(true)) + { + // new camera doesn't support fading, fade him back in + plCameraTargetFadeMsg* pMsg = TRACKED_NEW plCameraTargetFadeMsg; + pMsg->SetFadeOut(false); + pMsg->SetSubjectKey(GetCurrentStackCamera()->GetBrain()->GetSubject()->GetKey()); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + plgDispatch::MsgSend(pMsg); + } + } + else + { + // check the rare instance that maybe we have fallen down to the bottom of the stack and hit the + // built-in first person cam, and are now pushing something on top of it... + plUoid U(kDefaultCameraMod1_KEY); + plKey pKey = hsgResMgr::ResMgr()->FindKey(U); + if ( (pKey && pKey == GetCurrentStackCamera()->GetKey()) || + (plCameraBrain1_FirstPerson::ConvertNoRef(GetCurrentStackCamera()->GetBrain())) ) + { + FreezeOutput(2); + UnFadeAvatarIn(1); + } + + } + GetCurrentStackCamera()->Pop(); + } + pCam->Push(!HasFlags(kAvatarWalking)); + + // handle transition between the cameras + + // if the player is warping, just cut + plUoid U(kDefaultCameraMod1_KEY); + plKey pDefKey = hsgResMgr::ResMgr()->FindKey(U); + + if (HasFlags(kCutNextTrans)) + { + if (fTransPos == POS_TRANS_FOLLOW) + FinishTransition(); + if (pCam->GetKey() != pDefKey) + ClearFlags(kCutNextTrans); + AddCameraToStack(pCam); + if (pCam->GetBrain()) + { + pCam->GetBrain()->SetFlags(plCameraBrain1::kCutPosOnce); + pCam->GetBrain()->SetFlags(plCameraBrain1::kCutPOAOnce); + } + StartInterpPanLimits(); + } + else + if (!fPythonOverride) + { + // check to see if the new camera has a transition override for the current camera + if (!pTrans) + { + // do a stock transition + if (plCameraBrain1_Avatar::ConvertNoRef(GetCurrentStackCamera()->GetBrain()) || + plCameraBrain1_Avatar::ConvertNoRef(pCam->GetBrain()) ) + { + // do a track transition here; + fPrevCam = GetCurrentStackCamera(); + AddCameraToStack(pCam); + pTrans = TRACKED_NEW CamTrans(pCam->GetKey()); + StartTransition(pTrans); + delete(pTrans); +#ifdef STATUS_LOG + camLog->AddLineF("Performing stock track transition between %s and %s",fPrevCam->GetKeyName(), pCam->GetKeyName()); +#endif + } + else + { + // both fixed brains, cut between them + AddCameraToStack(pCam); + pTrans = TRACKED_NEW CamTrans(pCam->GetKey()); + pTrans->fCutPOA = true; + pTrans->fCutPos = true; + StartTransition(pTrans); + delete(pTrans); +#ifdef STATUS_LOG + camLog->AddLineF("Performing stock cut transition between %s and %s",fPrevCam->GetKeyName(), pCam->GetKeyName()); +#endif + } + } + else + { + // do the specified transition + fPrevCam = GetCurrentStackCamera(); + AddCameraToStack(pCam); + StartTransition(pTrans); +#ifdef STATUS_LOG + camLog->AddLineF("Performing custom transition between %s and %s",fPrevCam->GetKeyName(), pCam->GetKeyName()); +#endif + + } + } + else + { + // just push it on, since we have a python camera present. + AddCameraToStack(pCam); +#ifdef STATUS_LOG + camLog->AddLineF("No transition between %s and %s, python camera is currently displaying",fPrevCam->GetKeyName(), pCam->GetKeyName()); +#endif + } + // make this the default camera if that's what we want... + if (fCameraStack.Count() > 0 && bDefault) + { + fCameraStack.SetCountAndZero(0); + AddCameraToStack(pCam); +#ifdef STATUS_LOG + camLog->AddLineF("Camera %s is now the DEFAULT camera for this age", pCam->GetKeyName()); +#endif + } + SetFOV(GetCurrentStackCamera()->GetFOVw(), GetCurrentStackCamera()->GetFOVh(), GetCurrentStackCamera()); +} + +void plVirtualCam1::PopCamera(plCameraModifier1* pCam) +{ + // sanity / new default camera check + if (fCameraStack.Count() <= 1) + return; + + // is it the current camera AND the same camera we would otherwise switch to? + if (pCam==GetCurrentStackCamera() && pCam == fCameraStack[fCameraStack.Count() - 2]) + { + // pop but don't transition to a new camera + // or do anything else: + fCameraStack.Remove(fCameraStack.Count() - 1); + return; + } + + // are we mouse-looking? + hsBool mLook = false; + if (pCam->GetBrain() && pCam->GetBrain()->HasMovementFlag(S_SET_FREELOOK)) + mLook = true; + + // do anything special upon de-activating this camera + pCam->Pop(); + + if (plCameraBrain1_FirstPerson::ConvertNoRef(pCam->GetBrain())) + { + FreezeOutput(2); + UnFadeAvatarIn(1); + } + else + if (plCameraBrain1_Drive::ConvertNoRef(pCam->GetBrain()) || + !GetCurrentStackCamera()) + { + // special camera mode (like drive) just pop it + fCameraStack.Remove(fCameraStack.Count() - 1); + GetCurrentStackCamera()->Push(!HasFlags(kAvatarWalking)); + return; + } + + if (pCam == GetCurrentStackCamera()) + { +#ifdef STATUS_LOG + IHandleCameraStatusLog(pCam, kPop); +#endif + + // pop and actually transition to a new camera + fCameraStack.Remove(fCameraStack.Count() - 1); + + if (GetCurrentStackCamera()) + { + // update this camera + GetCurrentStackCamera()->Push(!HasFlags(kAvatarWalking)); + + +#ifdef STATUS_LOG + IHandleCameraStatusLog(GetCurrentStackCamera(), kReplacement); +#endif + + if (mLook) + GetCurrentStackCamera()->GetBrain()->SetMovementFlag(S_SET_FREELOOK); + + // is the avatar faded out? + if (pCam->GetFaded()) + { + if (!GetCurrentStackCamera()->SetFaded(true)) + { + // new camera doesn't support fading, fade him back in + plCameraTargetFadeMsg* pMsg = TRACKED_NEW plCameraTargetFadeMsg; + pMsg->SetFadeOut(false); + pMsg->SetSubjectKey(pCam->GetBrain()->GetSubject()->GetKey()); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + plgDispatch::MsgSend(pMsg); + } + } + + + // handle transition between the cameras + // check to see if the new camera has a transition override for the current camera + CamTrans* pTrans = nil; + if (GetCurrentStackCamera()->GetNumTrans()) + { + for (int i = 0; i < GetCurrentStackCamera()->GetNumTrans(); i++) + { + if (GetCurrentStackCamera()->GetTrans(i)->fTransTo == pCam->GetKey()) + { + pTrans = GetCurrentStackCamera()->GetTrans(i); + break; + } + else + if (GetCurrentStackCamera()->GetTrans(i)->fTransTo == nil) + pTrans = GetCurrentStackCamera()->GetTrans(i); + } + } + if (!pTrans) + { + // do a stock transition + if (plCameraBrain1_Avatar::ConvertNoRef(GetCurrentStackCamera()->GetBrain()) || + plCameraBrain1_Avatar::ConvertNoRef(pCam->GetBrain()) ) + { + // do a track transition here; + fPrevCam = pCam; + pTrans = TRACKED_NEW CamTrans(GetCurrentStackCamera()->GetKey()); + StartTransition(pTrans); + delete(pTrans); + } + else + { + fPrevCam = pCam; + pTrans = TRACKED_NEW CamTrans(GetCurrentStackCamera()->GetKey()); + pTrans->fCutPOA = true; + pTrans->fCutPos = true; + StartTransition(pTrans); + delete(pTrans); + } + } + else + { + // do the specified transition + fPrevCam = pCam; + StartTransition(pTrans); + } + } + } + else + { +#ifdef STATUS_LOG + IHandleCameraStatusLog(pCam, kBackgroundPop); +#endif + // just remove this from the stack + for (int i = 0; i < fCameraStack.Count(); i++) + { + if (fCameraStack[i] == pCam) + { + fCameraStack.Remove(i); + break; + } + } + } + if (!InTransition()) + SetFOV(GetCurrentStackCamera()->GetFOVw(), GetCurrentStackCamera()->GetFOVh(), GetCurrentStackCamera()); +} + +void plVirtualCam1::PopAll() +{ +} + +void plVirtualCam1::StartTransition(CamTrans* transition) +{ + + if ((transition->fCutPos && transition->fCutPOA) || GetCurrentStackCamera()->IsAnimated() || alwaysCutForColin ) + { + if (fTransPos == POS_TRANS_FOLLOW) + FinishTransition(); + // we want to cut, set new camera to cut to current pos and FOV + if (GetCurrentStackCamera()->GetBrain()) + { + GetCurrentStackCamera()->GetBrain()->SetFlags(plCameraBrain1::kCutPOAOnce); + GetCurrentStackCamera()->GetBrain()->SetFlags(plCameraBrain1::kCutPosOnce); + SetFOV(GetCurrentStackCamera()->GetFOVw(), GetCurrentStackCamera()->GetFOVh(), GetCurrentStackCamera()); + fXPanLimit = GetCurrentStackCamera()->GetBrain()->GetXPanLimit(); + fZPanLimit = GetCurrentStackCamera()->GetBrain()->GetZPanLimit(); + StartInterpPanLimits(); + } + +#ifdef STATUS_LOG + camLog->AddLineF("Camera Cut Transition Completed"); +#endif + return; + } + + if (fFirstPersonOverride) + { + FinishTransition(); + return; + } + + plCameraModifier1* pCam = GetCurrentStackCamera(); + plCameraBrain1* pBrain = 0; + +#ifdef STATUS_LOG + if (fPrevCam->GetKey() && pCam->GetKey()) + camLog->AddLineF("Starting Camera Transition from %s to %s",fPrevCam->GetKeyName(), pCam->GetKeyName()); +#endif + + if ( (fPythonOverride && plCameraBrain1_Avatar::ConvertNoRef(fPythonOverride->GetBrain())) || + (plCameraBrain1_Avatar::ConvertNoRef(pCam->GetBrain()) && !fPythonOverride) ) + { + plCameraBrain1_Avatar* pAvBrain = TRACKED_NEW plCameraBrain1_Avatar; + + pAvBrain->SetOffset(((plCameraBrain1_Avatar*)pCam->GetBrain())->GetOffset()); + pAvBrain->SetPOAOffset(pCam->GetBrain()->GetPOAOffset()); + if (pCam->GetBrain()->HasFlag(plCameraBrain1::kMaintainLOS)) + pAvBrain->SetFlags(plCameraBrain1::kMaintainLOS); + + if (((plCameraBrain1_Avatar*)pCam->GetBrain())->HasFlag(plCameraBrain1::kWorldspacePOA)) + pAvBrain->SetFlags(plCameraBrain1::kWorldspacePOA); + if (((plCameraBrain1_Avatar*)pCam->GetBrain())->HasFlag(plCameraBrain1::kWorldspacePos)) + pAvBrain->SetFlags(plCameraBrain1::kWorldspacePos); + +// if (plCameraBrain1_Avatar::ConvertNoRef(fPrevCam->GetBrain()) && pCam->GetBrain()->GetPOAOffset() == fPrevCam->GetBrain()->GetPOAOffset()) +// { +// pAvBrain->SetFlags(plCameraBrain1::kCutPOA); +// } + pAvBrain->SetSubject(pCam->GetBrain()->GetSubject()); + pBrain = pAvBrain; + } + else + { + pBrain = TRACKED_NEW plCameraBrain1; + } + pBrain->SetFlags(plCameraBrain1::kIsTransitionCamera); + + // set up transition speeds + pBrain->SetAccel(transition->fAccel); + pBrain->SetDecel(transition->fDecel); + pBrain->SetVelocity(transition->fVelocity); + +// if (!pBrain->HasFlag(plCameraBrain1::kCutPOA)) + if (0) + { + // see if the transition between POA's is going to swing the camera more than 90 degrees + hsVector3 curVec(fPrevCam->GetTargetPos() - fPrevCam->GetTargetPOA()); + hsVector3 transVec(pCam->GetTargetPOA() - fPrevCam->GetTargetPOA()); + curVec.fZ = transVec.fZ = 0; + transVec.Normalize(); + curVec.Normalize(); + hsScalar dot = curVec * transVec; + if (dot <= 0.5f || transVec.MagnitudeSquared() != 0.0f) + { + pBrain->SetPOAAccel(100); + pBrain->SetPOADecel(100); + pBrain->SetPOAVelocity(200); +#ifdef STATUS_LOG + camLog->AddLineF("Congradulations you triggered the dont-swing-the-POA-more-than-90-degrees override\n"); + camLog->AddLineF("If you don't like this transition then you need to redesign the cameras involved\n"); +#endif + + } + else + { + pBrain->SetPOAAccel(transition->fPOAAccel); + pBrain->SetPOADecel(transition->fPOADecel); + pBrain->SetPOAVelocity(transition->fPOAVelocity); + } + } + // make sure that the new camera is where it should be + pCam->SetTargetPos(pCam->GetBrain()->GetGoal()); + pCam->SetTargetPOA(pCam->GetBrain()->GetPOAGoal()); + // and the transition is trying to go where it should + pBrain->SetGoal(pCam->GetTargetPos()); + pBrain->SetPOAGoal(pCam->GetTargetPOA()); + + // set transition camera parameters + if (fTransPos != POS_TRANS_FOLLOW) + { + fTransitionCamera->SetTargetPos(fPrevCam->GetTargetPos()); + fTransitionCamera->SetTargetPOA(fPrevCam->GetTargetPOA()); + fTransitionCamera->SetTransform(fPrevCam->GetTargetPOA()); + if (fPrevCam->GetInSubworld()) + { + fTransitionCamera->SetSubworldPos(fPrevCam->GetSubworldPos()); + fTransitionCamera->SetSubworldPOA(fPrevCam->GetSubworldPOA()); + fTransitionCamera->InSubworld(true); + } + } + else + // we're already in transition... + { + plCameraBrain1* pOldBrain = fTransitionCamera->GetBrain(); + // match speeds to the old brain so we don't stop mid-transition + pBrain->SetCurrentCamSpeed(pOldBrain->GetCurrentCamSpeed()); + pBrain->SetCurrentViewSpeed(pOldBrain->GetCurrentViewSpeed()); + delete(pOldBrain); + fTransitionCamera->SetBrain(nil); +#ifdef STATUS_LOG + camLog->AddLineF("Stopping in-progress camera transition"); +#endif + } + + if (transition->fCutPos) + { + pCam->GetBrain()->SetFlags(plCameraBrain1::kCutPosOnce); + pBrain->SetFlags(plCameraBrain1::kCutPos); + } + if (transition->fCutPOA) + { + pBrain->SetFlags(plCameraBrain1::kCutPOA); + pCam->GetBrain()->SetFlags(plCameraBrain1::kCutPOAOnce); + } + fTransitionCamera->SetBrain(pBrain); + pBrain->SetCamera(fTransitionCamera); + + // deal with FOV - + hsScalar diffH = hsABS(pCam->GetFOVh() - fPrevCam->GetFOVh()); + if ( diffH ) + { + double time = 0; + hsVector3 dist; + // figure out transition time + if (transition->fCutPos) + dist.Set(&(fTransitionCamera->GetTargetPOA() - pCam->GetTargetPOA())); + else + dist.Set(&(fTransitionCamera->GetTargetPos() - pCam->GetTargetPos())); + + time = (double)(dist.Magnitude() / pBrain->GetVelocity()); + + // set up the transition camera to the current FOV + fTransitionCamera->SetFOVh(GetFOVh(), false); + fTransitionCamera->SetFOVw(GetFOVw(), false); + fTransitionCamera->GetBrain()->SetFOVGoal(pCam->GetFOVh(), time); + + } + StartInterpPanLimits(); + fTransPos = POS_TRANS_FOLLOW; +} + +void plVirtualCam1::RunTransition() +{ + if (fTransPos != POS_TRANS_FOLLOW) + return; + + plCameraModifier1* pToCam = fPythonOverride; + if (!pToCam) + pToCam = GetCurrentStackCamera(); + + hsVector3 v1(fTransitionCamera->GetTargetPos() - pToCam->GetTargetPos()); + hsVector3 v2(fTransitionCamera->GetBrain()->GetPOAGoal() - fTransitionCamera->GetTargetPOA()); + + if ( v1.MagnitudeSquared() <= 0.0001f && v2.MagnitudeSquared() <= 0.0001f && + !fTransitionCamera->GetBrain()->HasFlag(plCameraBrain1::kAnimateFOV)) + { + FinishTransition(); + } + else + { + pToCam->Update(); + fTransitionCamera->GetBrain()->SetGoal(pToCam->GetTargetPos()); + fTransitionCamera->GetBrain()->SetPOAGoal(pToCam->GetBrain()->GetPOAGoal()); + // check for panic velocity + plCameraBrain1* pBrain = pToCam->GetBrain(); + plCameraBrain1_Avatar* pAvBr = plCameraBrain1_Avatar::ConvertNoRef(pBrain); + if (pAvBr) + { + hsScalar off = pAvBr->GetOffset().MagnitudeSquared(); + hsVector3 dist(pToCam->GetTargetPos() - fTransitionCamera->GetTargetPos()); + if (dist.MagnitudeSquared() > off) + fTransitionCamera->GetBrain()->SetFlags(plCameraBrain1::kPanicVelocity); + else + fTransitionCamera->GetBrain()->ClearFlags(plCameraBrain1::kPanicVelocity); + + } + + } +} + +void plVirtualCam1::FinishTransition() +{ + plCameraBrain1* pBrain = fTransitionCamera->GetBrain(); + delete(pBrain); + fTransitionCamera->SetBrain(nil); + + fTransPos = POS_TRANS_OFF; +#ifdef STATUS_LOG + camLog->AddLineF("Finished Camera Transition"); +#endif + +} + + +void plVirtualCam1::IHandleCameraStatusLog(plCameraModifier1* pMod, int action) +{ +#ifdef STATUS_LOG + + if (!pMod->GetKey()) + return; + camLog->AddLineF(".."); + plCameraBrain1* pBrain = pMod->GetBrain(); + switch(action) + { + case kPop: + camLog->AddLineF("Popped Camera %s from top of stack", pMod->GetKeyName()); + break; + case kBackgroundPop: + camLog->AddLineF("Popped Camera %s from background", pMod->GetKeyName()); + break; + case kPush: + camLog->AddLineF("Pushed Camera %s", pMod->GetKeyName()); + break; + case kReplacement: + camLog->AddLineF("Camera %s replacing popped camera", pMod->GetKeyName()); + break; + } + if (pBrain) + { + if (plCameraBrain1_Circle::ConvertNoRef(pBrain)) + { + camLog->AddLineF("Brain type Circle"); + } + else + if (plCameraBrain1_Fixed::ConvertNoRef(pBrain)) + { + camLog->AddLineF("Brain type Fixed"); + camLog->AddLineF("POAOffset %f %f %f", pBrain->GetPOAOffset().fX,pBrain->GetPOAOffset().fY,pBrain->GetPOAOffset().fZ); + } + else + if (plCameraBrain1_FirstPerson::ConvertNoRef(pBrain)) + { + camLog->AddLineF("Brain type 1st Person"); + } + else + if (plCameraBrain1_Avatar::ConvertNoRef(pBrain)) + { + camLog->AddLineF("Brain type 3rd Person"); + } + camLog->AddLineF("FOV %f",pMod->GetFOVw()); + camLog->AddLineF(".."); + } +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.h new file mode 100644 index 00000000..208e754f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCamera/plVirtualCamNeu.h @@ -0,0 +1,235 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plVirtualCam1_inc +#define plVirtualCam1_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsMatrix44.h" +#include "hsBitVector.h" + +class plPipeline; +class plCameraModifier1; +class plCameraBrain1; +class plSceneObject; +class plKey; +class hsGMaterial; +class plDrawableSpans; +class plCameraProxy; +class plSceneNode; +class plDebugInputInterface; +class plPlate; + +#include "hsTemplates.h" + +struct CamTrans; +struct hsColorRGBA; + +#define POS_TRANS_OFF 0 +#define POS_TRANS_FIXED 1 +#define POS_TRANS_FOLLOW 2 +#define POA_TRANS_OFF 3 +#define POA_TRANS_FIXED 4 +#define POA_TRANS_FOLLOW 5 + +class plVirtualCam1 : public hsKeyedObject +{ + +protected: + + + void Output(); + void IUpdate(); + void INext(); + +public: + enum flags + { + kSetFOV, + kCutNextTrans, + kRender, + kRegionIgnore, + kFirstPersonEnabled, + kResponderForced3rd, + kScriptsForced3rd, + kScriptsDisabled1st, + kAvatarWalking, + kUnPanCamera, + kInterpPanLimits, + kFalling, + //kRegisteredForBehaviors, // not reliable anymore since we have a dummy avatar in the startup age + kFirstPersonAtLinkOut, + kJustLinkedIn, + kFirstPersonUserSelected, + }; + + enum action + { + kPush = 0, + kPop, + kReplacement, + kBackgroundPop, + kRefCamera, + }; + plVirtualCam1(); + virtual ~plVirtualCam1(); + + CLASSNAME_REGISTER( plVirtualCam1 ); + GETINTERFACE_ANY( plVirtualCam1, hsKeyedObject ); + + void SetPipeline(plPipeline* p); + void Init(); + + virtual hsBool MsgReceive(plMessage* msg); + static void SetFOV(hsScalar w, hsScalar h); + static void SetFOV(hsScalar w, hsScalar h, plCameraModifier1* pCam); + static void SetDepth(hsScalar h, hsScalar y); + static hsScalar GetFOVw() { return fFOVw; } + static hsScalar GetFOVh() { return fFOVh; } + static hsScalar GetHither() { return fHither; } + static hsScalar GetYon() { return fYon; } + static void SetOffset(float x, float y, float z); + static void SetAspectRatio(float aspect) { fAspectRatio = aspect; } + static float GetAspectRatio() { return fAspectRatio; } + + hsBool InTransition() { return fTransPos != POS_TRANS_OFF; } + plCameraModifier1* GetCurrentCamera(); + plCameraModifier1* GetCurrentStackCamera(); + plCameraModifier1* GetTransitionCamera(){return fTransitionCamera;} + hsBool Is1stPersonCamera(); + + hsBool HasMovementFlag(int f) { return fMoveFlags.IsBitSet(f); } + void SetMovementFlag(int f, hsBool on = true) { fMoveFlags.SetBit(f, on);} + + hsPoint3 GetCameraPos() { return fOutputPos; } + hsPoint3 GetCameraPOA() { return fOutputPOA; } + hsVector3 GetCameraUp() { return fOutputUp; } + void SetCutNextTrans(); // used when player warps into a new camera region + + const hsMatrix44 GetCurrentMatrix() { return fMatrix; } + static plVirtualCam1* Instance() { return fInstance; } + + int GetNumCameras() { return fCameraStack.Count(); } + plCameraModifier1* GetCameraNumber(int camNumber); + void RebuildStack(const plKey& key); + + void SetFlags(int flag) { fFlags.SetBit(flag); } + hsBool HasFlags(int flag) { return fFlags.IsBitSet(flag); } + void ClearFlags(int flag) { fFlags.ClearBit(flag); } + + // console command stuff + static void Next(); + static void Prev(); + static void Deactivate(); + void CameraRegions(hsBool b) { fFlags.SetBit(kRegionIgnore,b); } + void LogFOV(hsBool b) { printFOV = b; } + void Drive(); + void PushThirdPerson(); + + static void AddMsgToLog(const char* msg); + static hsBool IsCurrentCamera(const plCameraModifier1* mod); + void ClearStack(); + + void AddCameraLoaded(plSceneObject* pCam) { fCamerasLoaded.Append(pCam); } + hsBool RestoreFromName(const char* name); + void StartUnPan(); + // these are for console access + static hsBool fUseAccelOverride, freeze, alwaysCutForColin, WalkPan3rdPerson,StayInFirstPersonForever; + static hsScalar fDecel, fAccel, fVel; + static hsScalar fFallTimerDelay; + +private: + + void Reset(hsBool bRender); + void PushCamera(plCameraModifier1* pCam, hsBool bDefault = false); + void PopCamera(plCameraModifier1* pCam); + void AddCameraToStack(plCameraModifier1* pCam); + void PopAll(); + void CreateDefaultCamera(plSceneObject* subject); + void StartTransition(CamTrans* transition); + void RunTransition(); + void FinishTransition(); + void SetRender(hsBool render); + void IHandleCameraStatusLog(plCameraModifier1* pMod, int action); + void ICreatePlate(); + void FreezeOutput(int frames) { fFreezeCounter = frames; } // I hate this and I hate myself for doing it + void UnFadeAvatarIn(int frames) { fFadeCounter = frames; } // ditto + void FirstPersonOverride(); + + void AdjustForInput(); + void UnPanIfNeeded(); + void StartInterpPanLimits(); + void InterpPanLimits(); + + plPipeline* fPipe; + hsMatrix44 fMatrix; + hsPoint3 fOutputPos; + hsPoint3 fOutputPOA; + hsVector3 fOutputUp; + int fTransPos; + plDebugInputInterface* fCameraDriveInterface; + plPlate* fEffectPlate; + FILE* foutLog; + hsTArray fCameraStack; + int fFreezeCounter; + int fFadeCounter; + hsBitVector fFlags; + hsTArray fCamerasLoaded; + hsBitVector fMoveFlags; + hsScalar fX; + hsScalar fY; + hsScalar fXPanLimit; + hsScalar fZPanLimit; + hsScalar fXPanLimitGoal; + hsScalar fZPanLimitGoal; + hsScalar fXUnPanRate; + hsScalar fZUnPanRate; + hsScalar fXPanInterpRate; + hsScalar fZPanInterpRate; + double fUnPanEndTime; + double fInterpPanLimitTime; + hsScalar fRetainedFY; + + // built-in cameras + plCameraModifier1* fDriveCamera; // for driving around + plCameraModifier1* fTransitionCamera; // transitions between cameras placed in scenes + plCameraModifier1* fPythonOverride; // a special camera pushed by python + plCameraModifier1* fFirstPersonOverride; // the built-in first person camera + plCameraModifier1* fPrevCam; // the last camera we were displaying + plCameraModifier1* fThirdPersonCam; // built in third person cam for ccr's when they jump about + + static hsScalar fFOVh, fFOVw; + static hsScalar fHither, fYon; + static plVirtualCam1* fInstance; + static hsBool printFOV; + static hsScalar fPanResponseTime; + static float fAspectRatio; + hsBool fForceCutOnce; + +}; + + +#endif plVirtualCam1_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfCharacterCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfCharacterCreatable.h new file mode 100644 index 00000000..cf404df4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfCharacterCreatable.h @@ -0,0 +1,35 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pfCharacterCreatable_inc +#define pfCharacterCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "pfMarkerMgr.h" +REGISTER_NONCREATABLE(pfMarkerMgr); + +#endif // pfCharacterCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerInfo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerInfo.cpp new file mode 100644 index 00000000..b1cafd57 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerInfo.cpp @@ -0,0 +1,250 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pfMarkerInfo.h" +#include "pfMarkerMgr.h" + +#include "../plModifier/plGameMarkerModifier.h" + +#include "../plMessage/plLoadCloneMsg.h" +#include "../pnMessage/plWarpMsg.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plSoundMsg.h" +#include "../pnSceneObject/plAudioInterface.h" + +// For Init +#include "../pnMessage/plClientMsg.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plResMgr/plResManager.h" +#include "../plResMgr/plKeyFinder.h" + +plUoid pfMarkerInfo::fMarkerUoid; + +static const int kFreezeLen = 10; // How long a marker is frozen after you hit it + +void pfMarkerInfo::Init() +{ + plResManager* resMgr = (plResManager*)hsgResMgr::ResMgr(); + + // Force the client to keep the GlobalMarkers keys loaded, so we don't load them every time we clone + plClientMsg* loadAgeKeysMsg = TRACKED_NEW plClientMsg(plClientMsg::kLoadAgeKeys); + loadAgeKeysMsg->SetAgeName("GlobalMarkers"); + loadAgeKeysMsg->Send(resMgr->FindKey(kClient_KEY)); + + // + // Get the Uoid for the markers + // + plLocation markerLoc = plKeyFinder::Instance().FindLocation("GlobalMarkers", "Markers"); + + if (markerLoc.IsValid()) + fMarkerUoid = plUoid(markerLoc, plSceneObject::Index(), "MarkerRoot"); + else + fMarkerUoid.Invalidate(); +} + +pfMarkerInfo::pfMarkerInfo(const hsPoint3& pos, bool isNew) : + fMod(nil), + fPosition(pos), + fType(kMarkerOpen), + fLastChange(0), + fVisible(true), + fIsNew(isNew), + fSpawned(false) +{ +} + +void pfMarkerInfo::Spawn(MarkerType type) +{ + if (!fMarkerUoid.IsValid()) + { + plResManager* resMgr = (plResManager*)hsgResMgr::ResMgr(); + plLocation markerLoc = plKeyFinder::Instance().FindLocation("GlobalMarkers", "Markers"); + + if (markerLoc.IsValid()) + fMarkerUoid = plUoid(markerLoc, plSceneObject::Index(), "MarkerRoot"); + else + { + hsAssert(false, "Unable to spawn markers because the marker age was not loaded or found"); + return; + } + } + + fType = type; + fLastChange = 0; + + plLoadCloneMsg* cloneMsg = TRACKED_NEW plLoadCloneMsg(fMarkerUoid, pfMarkerMgr::Instance()->GetKey(), 0); + cloneMsg->SetBCastFlag(plMessage::kNetPropagate, false); + fKey = cloneMsg->GetCloneKey(); + + cloneMsg->Send(); +} + +void pfMarkerInfo::InitSpawned(plKey markerKey) +{ + fKey = markerKey; + fSpawned = true; + + plSceneObject* so = plSceneObject::ConvertNoRef(fKey->GetObjectPtr()); + fMod = (plGameMarkerModifier*)so->GetModifierByType(plGameMarkerModifier::Index()); + hsAssert(fMod, "Couldn't find marker modifier"); + fMod->FixupAnimKeys(); + + // Warp it into position + hsMatrix44 pos; + pos.Reset(); + pos.SetTranslate(&fPosition); + plWarpMsg* warpMsg = TRACKED_NEW plWarpMsg(pfMarkerMgr::Instance()->GetKey(), fKey, plWarpMsg::kFlushTransform, pos); + warpMsg->Send(); + + // update its state + Show(fVisible); + IPlayColor(true); + if (fType == kMarkerLocalSelected) + IPlayBounce(true); +} + +void pfMarkerInfo::Show(bool show) +{ + fVisible = show; + + if (fSpawned) { + plEnableMsg* msg = TRACKED_NEW plEnableMsg; + msg->SetBCastFlag(plMessage::kPropagateToChildren); + msg->SetCmd(plEnableMsg::kDrawable); + msg->SetCmd(show ? plEnableMsg::kEnable : plEnableMsg::kDisable); + msg->SetSender(pfMarkerMgr::Instance()->GetKey()); + msg->Send(fKey); + } +} + +void pfMarkerInfo::SetFrozen(double freezeStartTime) +{ + fLastChange = freezeStartTime; + IPlayBounce(true); +} + +void pfMarkerInfo::Update(double curTime) +{ + if (fLastChange != 0 && (curTime - fLastChange) > kFreezeLen) + { + fLastChange = 0; + IPlayBounce(false); + } + + if (fIsNew) + { + IPlaySound(true); + fIsNew = false; + } +} + +void pfMarkerInfo::Remove() +{ + if (fKey) + { + plLoadCloneMsg* cloneMsg = TRACKED_NEW plLoadCloneMsg(fKey, pfMarkerMgr::Instance()->GetKey(), 0, false); + cloneMsg->SetBCastFlag(plMessage::kNetPropagate, false); + cloneMsg->Send(); + + fKey = nil; + fMod = nil; + } +} + +void pfMarkerInfo::SetType(pfMarkerInfo::MarkerType type) +{ + if (fType == kMarkerLocalSelected) + IPlayBounce(false); + + IPlayColor(false); + fType = type; + IPlayColor(true); + + if (fType == kMarkerLocalSelected) + IPlayBounce(true); +} + +void pfMarkerInfo::IPlayBounce(bool play) +{ + if (fMod && fSpawned) + { + // Send anim start/stop msg + plAnimCmdMsg* animMsg = TRACKED_NEW plAnimCmdMsg; + animMsg->SetCmd(play ? plAnimCmdMsg::kContinue : plAnimCmdMsg::kStop); + animMsg->SetCmd(plAnimCmdMsg::kSetLooping); + animMsg->SetCmd(plAnimCmdMsg::kGoToBegin); + animMsg->SetSender(pfMarkerMgr::Instance()->GetKey()); + animMsg->Send(fMod->fBounceAnimKey); + } +} + +void pfMarkerInfo::IPlayColor(bool play) +{ + if (fMod && fSpawned) + { + // Play the correct color anim + plKey key = nil; + switch (fType) + { + case kMarkerOpen: + case kMarkerLocal: + case kMarkerLocalSelected: + key = fMod->fOpenAnimKey; + break; + + case kMarkerGreen: + key = fMod->fGreenAnimKey; + break; + + case kMarkerRed: + key = fMod->fRedAnimKey; + break; + } + + plAnimCmdMsg* animMsg = TRACKED_NEW plAnimCmdMsg; + animMsg->SetCmd(play ? plAnimCmdMsg::kContinue : plAnimCmdMsg::kStop); + animMsg->SetCmd(plAnimCmdMsg::kSetLooping); + animMsg->SetCmd(plAnimCmdMsg::kGoToBegin); + animMsg->SetSender(pfMarkerMgr::Instance()->GetKey()); + animMsg->AddReceiver(key); + animMsg->Send(); + } +} + +void pfMarkerInfo::IPlaySound(bool place) +{ + if (fMod && fSpawned) + { + const plAudioInterface* ai = fMod->GetTarget()->GetAudioInterface(); + + plSoundMsg* msg = TRACKED_NEW plSoundMsg; + msg->fIndex = place ? fMod->fPlaceSndIdx : fMod->fHitSndIdx; + msg->SetCmd(plSoundMsg::kPlay); + msg->SetSender(pfMarkerMgr::Instance()->GetKey()); + msg->Send(ai->GetKey()); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerInfo.h new file mode 100644 index 00000000..5d6aea1c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerInfo.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pfMarkerInfo_h_inc +#define pfMarkerInfo_h_inc + +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plUoid.h" +#include "hsGeometry3.h" + +class plMessage; +class plGameMarkerModifier; + +class pfMarkerInfo +{ +public: + enum MarkerType { kMarkerOpen, kMarkerGreen, kMarkerRed, kMarkerLocal, kMarkerLocalSelected }; + +protected: + // MarkerMgr will set this up + static plUoid fMarkerUoid; + + plKey fKey; + plGameMarkerModifier* fMod; + hsPoint3 fPosition; + MarkerType fType; + double fLastChange; // Last time this marker changed hands + bool fVisible; + bool fIsNew; + bool fSpawned; + + void IPlayBounce(bool play); + void IPlayColor(bool play); + void IPlaySound(bool place); + +public: + pfMarkerInfo(const hsPoint3& pos, bool isNew); + ~pfMarkerInfo() {} + + static void Init(); + + plKey GetKey() { return fKey; } + + void Spawn(MarkerType type); + void InitSpawned(plKey markerKey); + void Remove(); + + void Update(double curTime); + + void Show(bool show); + bool IsVisible() { return fVisible; } + + void SetType(pfMarkerInfo::MarkerType type); + pfMarkerInfo::MarkerType GetType() { return fType; } + + void SetFrozen(double freezeStartTime); + bool IsFrozen() { return fLastChange != 0; } + + void PlayHitSound() { IPlaySound(false); } +}; + +#endif // pfMarkerInfo_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerMgr.cpp new file mode 100644 index 00000000..747d9c4c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerMgr.cpp @@ -0,0 +1,313 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pfMarkerMgr.h" +#include "../pfMessage/pfMarkerMsg.h" +#include "pfMarkerInfo.h" + +#include "../plModifier/plCloneSpawnModifier.h" +#include "../plStatusLog/plStatusLog.h" + +#include "../plMessage/plLoadCloneMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plNotifyMsg.h" + +#include "../plNetClient/plNetClientMgr.h" +#include "plgDispatch.h" + +//////////////////////////////////////////////////////////////////////////////// + +pfMarkerMgr* pfMarkerMgr::fInstance = nil; +const UInt32 pfMarkerMgr::kNoMarkerSelected = (UInt32)(-1); + +pfMarkerMgr* pfMarkerMgr::Instance() +{ + if (!pfMarkerMgr::fInstance) + { + pfMarkerMgr::fInstance = TRACKED_NEW pfMarkerMgr; + pfMarkerMgr::fInstance->IInit(); + } + + return pfMarkerMgr::fInstance; +} + +void pfMarkerMgr::Shutdown() +{ + if (pfMarkerMgr::fInstance) + { + pfMarkerMgr::fInstance->IShutdown(); + pfMarkerMgr::fInstance = nil; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +pfMarkerMgr::pfMarkerMgr(): fSelectedMarker(kNoMarkerSelected), fShowingLocalMarkers(false), fMarkersRespawn(false) +{ + fLog = plStatusLogMgr::GetInstance().CreateStatusLog(20, "Marker.log", plStatusLog::kAlignToTop | plStatusLog::kFilledBackground); + pfMarkerInfo::Init(); +} + +pfMarkerMgr::~pfMarkerMgr() +{ + delete fLog; +} + +void pfMarkerMgr::IInit() +{ + fMyKey = RegisterAs(kMarkerMgr_KEY); + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +void pfMarkerMgr::IShutdown() +{ + std::map::iterator curMarker = fMarkers.begin(); + while (curMarker != fMarkers.end()) + { + curMarker->second->Remove(); + DEL(curMarker->second); + ++curMarker; + } + fMarkers.clear(); + + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + UnRegisterAs(kMarkerMgr_KEY); +} + +pfMarkerInfo* pfMarkerMgr::IFindMarker(plKey markerKey, UInt32& id) +{ + std::map::iterator curMarker = fMarkers.begin(); + while (curMarker != fMarkers.end()) + { + if (curMarker->second->GetKey() == markerKey) + { + id = curMarker->first; + return curMarker->second; + } + ++curMarker; + } + id = kNoMarkerSelected; + return nil; +} + +void pfMarkerMgr::IUpdate() +{ + // Update all markers + std::map::iterator curMarker = fMarkers.begin(); + while (curMarker != fMarkers.end()) + { + curMarker->second->Update(hsTimer::GetSeconds()); + ++curMarker; + } +} + +void pfMarkerMgr::IMarkerHit(plKey markerKey, plKey playerKey) +{ + if (playerKey != plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + return; // not the local player, abort + + // make sure the marker isn't frozen + UInt32 id; + pfMarkerInfo* hitMarker = IFindMarker(markerKey, id); + if (!hitMarker) + return; // abort, something weird is going on + if (hitMarker->IsFrozen()) + return; // marker frozen, abort + + // tell people about it + pfMarkerMsg* msg = TRACKED_NEW pfMarkerMsg; + msg->fType = pfMarkerMsg::kMarkerCaptured; + msg->fMarkerID = id; + msg->Send(); +} + +void pfMarkerMgr::AddMarker(double x, double y, double z, UInt32 id, bool justCreated) +{ + if (fMarkers.find(id) != fMarkers.end()) + { + // delete existing one if we're changing its location + fMarkers[id]->Remove(); + DEL(fMarkers[id]); + } + + hsPoint3 pos((hsScalar)x, (hsScalar)y, (hsScalar)z); + fMarkers[id] = TRACKED_NEW pfMarkerInfo(pos, justCreated); + fMarkers[id]->Spawn(pfMarkerInfo::kMarkerOpen); +} + +void pfMarkerMgr::RemoveMarker(UInt32 id) +{ + if (fMarkers.find(id) == fMarkers.end()) + return; + fMarkers[id]->Remove(); + DEL(fMarkers[id]); + fMarkers.erase(id); +} + +void pfMarkerMgr::RemoveAllMarkers() +{ + std::map::iterator curMarker = fMarkers.begin(); + while (curMarker != fMarkers.end()) + { + curMarker->second->Remove(); + DEL(curMarker->second); + ++curMarker; + } + fMarkers.clear(); +} + +void pfMarkerMgr::ClearSelectedMarker() +{ + if (fSelectedMarker != kNoMarkerSelected) + { + if (fMarkers.find(fSelectedMarker) != fMarkers.end()) + fMarkers[fSelectedMarker]->SetType(pfMarkerInfo::kMarkerOpen); + fSelectedMarker = kNoMarkerSelected; + } +} + +void pfMarkerMgr::SetSelectedMarker(UInt32 id) +{ + ClearSelectedMarker(); + + if (id != kNoMarkerSelected) + { + if (fMarkers.find(id) != fMarkers.end()) + { + fMarkers[id]->SetType(pfMarkerInfo::kMarkerLocalSelected); + fSelectedMarker = id; + } + } +} + +UInt32 pfMarkerMgr::GetSelectedMarker() +{ + return fSelectedMarker; +} + +// for QUEST games (no teams) +void pfMarkerMgr::CaptureMarker(UInt32 id, bool captured) +{ + if (fMarkers.find(id) == fMarkers.end()) + return; + + if (fMarkersRespawn) + fMarkers[id]->SetFrozen(hsTimer::GetSeconds()); + else + fMarkers[id]->Show(!captured); + + fMarkers[id]->PlayHitSound(); + fMarkers[id]->SetType(captured ? pfMarkerInfo::kMarkerGreen : pfMarkerInfo::kMarkerOpen); +} + +// for TEAM games (0 = not captured) +void pfMarkerMgr::CaptureMarker(UInt32 id, int team) +{ + if (fMarkers.find(id) == fMarkers.end()) + return; + + if (fMarkersRespawn) + fMarkers[id]->SetFrozen(hsTimer::GetSeconds()); + else + fMarkers[id]->Show(team == 0); // 0 = uncaptured + + fMarkers[id]->PlayHitSound(); + if (team == 0) + fMarkers[id]->SetType(pfMarkerInfo::kMarkerOpen); + else if (team == 1) + fMarkers[id]->SetType(pfMarkerInfo::kMarkerGreen); + else + fMarkers[id]->SetType(pfMarkerInfo::kMarkerRed); +} + +void pfMarkerMgr::LocalShowMarkers(bool show) +{ + fShowingLocalMarkers = show; + if (show) + { + std::map::iterator curMarker = fMarkers.begin(); + while (curMarker != fMarkers.end()) + curMarker->second->Show(true); + } + else + { + std::map::iterator curMarker = fMarkers.begin(); + while (curMarker != fMarkers.end()) + curMarker->second->Show(false); + } +} + +bool pfMarkerMgr::AreLocalMarkersShowing() +{ + return fShowingLocalMarkers; +} + +hsBool pfMarkerMgr::MsgReceive(plMessage* msg) +{ + plEvalMsg* evalMsg = plEvalMsg::ConvertNoRef(msg); + if (evalMsg) + { + IUpdate(); + return true; + } + + // Somebody hit a marker + plNotifyMsg* notify = plNotifyMsg::ConvertNoRef(msg); + if (notify) + { + proCollisionEventData* cEvent = (proCollisionEventData*)notify->FindEventRecord(proEventData::kCollision); + if (cEvent) + { + plKey markerKey = cEvent->fHittee; + plKey playerKey = cEvent->fHitter; + if (plNetClientMgr::GetInstance()->IsAPlayerKey(cEvent->fHittee)) + { + // swap the above, since the hittee is actually the player + playerKey = cEvent->fHittee; + markerKey = cEvent->fHitter; + } + + IMarkerHit(markerKey, playerKey); + } + + return true; + } + + plLoadCloneMsg* cloneMsg = plLoadCloneMsg::ConvertNoRef(msg); + if (cloneMsg) + { + plKey cloneKey = cloneMsg->GetCloneKey(); + if (cloneMsg->GetIsLoading() && cloneKey) + { + UInt32 id; + pfMarkerInfo* marker = IFindMarker(cloneKey, id); + marker->InitSpawned(cloneKey); + } + + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerMgr.h new file mode 100644 index 00000000..598399b7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/pfMarkerMgr.h @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pfMarkerMgr_h_inc +#define pfMarkerMgr_h_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include + +class plStatusLog; + +//////////////////////////////////////////////////////////////////////////////// + +class pfMarkerMsg; +class pfMarkerGame; +class pfMarkerInfo; + +class pfMarkerMgr : public hsKeyedObject +{ +protected: + friend class pfMarkerInfo; + friend class pfMarkerInfoOwned; + + // Because for some reason if I ask for my key, the refs hit zero when I release it (0 initial refs?) + plKey fMyKey; + + plStatusLog* fLog; + + static pfMarkerMgr* fInstance; + + bool fShowingLocalMarkers; + bool fMarkersRespawn; + UInt32 fSelectedMarker; + static const UInt32 kNoMarkerSelected; + std::map fMarkers; // key is marker id number + + void IInit(); + void IShutdown(); + + pfMarkerInfo* IFindMarker(plKey markerKey, UInt32& id); + void IUpdate(); + void IMarkerHit(plKey markerKey, plKey playerKey); + + pfMarkerMgr(); + ~pfMarkerMgr(); + +public: + CLASSNAME_REGISTER(pfMarkerMgr); + GETINTERFACE_ANY(pfMarkerMgr, hsKeyedObject); + + static pfMarkerMgr* Instance(); + static void Shutdown(); + + hsBool MsgReceive(plMessage* msg); + + void AddMarker(double x, double y, double z, UInt32 id, bool justCreated); + void RemoveMarker(UInt32 id); + void RemoveAllMarkers(); + + void ClearSelectedMarker(); + void SetSelectedMarker(UInt32 id); + UInt32 GetSelectedMarker(); + + void SetMarkersRespawn(bool respawn) {fMarkersRespawn = respawn;} + bool GetMarkersRespawn() {return fMarkersRespawn;} + + void CaptureMarker(UInt32 id, bool captured); // for QUEST games (no teams) + void CaptureMarker(UInt32 id, int team); // for TEAM games (0 = not captured) + + // Shows your markers locally, so you can see where they are + void LocalShowMarkers(bool show = true); + bool AreLocalMarkersShowing(); +}; + +#endif // pfMarkerMgr_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/plPlayerModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/plPlayerModifier.cpp new file mode 100644 index 00000000..993d2536 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/plPlayerModifier.cpp @@ -0,0 +1,610 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +//#pragma warning(disable: 4503 4786) +//#define HK_HARDCORE +// +//#include // for havok Vector3 +////#include <.//gpi/math/quaternion.h> // for havok Vector3 +//#include // for havok Vertex +// +// +//#include "hsTypes.h" +//#include "../plInterp/plController.h" +//#include "plPlayerModifier.h" +//#include "hsTimer.h" +//#include "../pnSceneObject/plSceneObject.h" +//#include "../pnSceneObject/plSimulationInterface.h" +//#include "../pnInputCore/plControlEventCodes.h" +//#include "../pnMessage/plTimeMsg.h" +//#include "../pnMessage/plWarpMsg.h" +//#include "../pnMessage/plCameraMsg.h" +//#include "../pnSceneObject/plCoordinateInterface.h" +//#include "plgDispatch.h" +//#include "../pfCamera/plCameraModifier.h" +//#include "hsResMgr.h" +//#include "../pnKeyedObject/plKey.h" +//#include "../plNetClient/plNetClientMgr.h" +//#include "../plModifier/plSpawnModifier.h" +//#include "../plMessage/plMatrixUpdateMsg.h" +// +//#include "../pnTimer/plTimerCallbackManager.h" +//#include "../plAudio/plAudioSystem.h" +//#include "../plMessage/plInputEventMsg.h" +//#include "../plMessage/plSpawnRequestMsg.h" +//#include "../plMessage/plSpawnModMsg.h" +//#include "../plMessage/plPlayerMsg.h" +//#include "../pnMessage/plAudioSysMsg.h" +//#include "../pfCamera/plCameraBrain.h" +// +//#include "../plHavok1/plHKPhysical.h" +// +//hsScalar plPlayerModifier::fTurnRate = 1.0f; +//hsScalar plPlayerModifier::fAcceleration = 80.0f; +//hsScalar plPlayerModifier::fDeceleration = 80.0f; +//hsScalar plPlayerModifier::fMaxVelocity = 200.0f; +// +//plPlayerModifier::plPlayerModifier() : +//bUseDesiredFacing(false), +//bUseDesiredMatrix(false) +//{ +// fCurSpeed = 0.0f; +// fLastTime = 0.0; +// bMoving = false; +// fRotationScalar = 1.0f; +// bIgnoreDesiredMatrix = false; +// SetFlag( kWantsToSpawn ); +//} +// +//plPlayerModifier::~plPlayerModifier() +//{ +// for (int i = 0; i < fSpawnPoints.Count(); i++) +// delete fSpawnPoints[i]; +// fSpawnPoints.SetCount(0); +//} +// +//// Adding RemoveTarget override of plSingleModifier to tell everyone we +//// told in AddTarget about our subject that he's gone now. +//void plPlayerModifier::RemoveTarget(plSceneObject* so) +//{ +// if( fTarget && fTarget->IsLocallyOwned()==plSynchedObject::kYes ) +// { +// plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; +// pMsg->SetCmd(plCameraMsg::kSetSubject); +// pMsg->SetSubject(nil); +// pMsg->SetBCastFlag( plMessage::kBCastByExactType ); +// plgDispatch::MsgSend(pMsg); +// +// plAudioSysMsg* pAudMsg1 = TRACKED_NEW plAudioSysMsg(plAudioSysMsg::kSetListenerCoordinateRefCamera); +// plAudioSysMsg* pAudMsg2 = TRACKED_NEW plAudioSysMsg(plAudioSysMsg::kSetListenerVelocityRefCamera); +// plAudioSysMsg* pAudMsg3 = TRACKED_NEW plAudioSysMsg(plAudioSysMsg::kSetListenerFacingRefCamera); +// plgDispatch::MsgSend(pAudMsg1); +// plgDispatch::MsgSend(pAudMsg2); +// plgDispatch::MsgSend(pAudMsg3); +// } +// plSingleModifier::RemoveTarget(so); +//} +// +//void plPlayerModifier::AddTarget(plSceneObject* so) +//{ +// fTarget = so; +// plSimulationInterface * pSI = IGetTargetSimulationInterface(0); // so->GetSimulationInterface(); // +// +// plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +// +// // set the desired rotation vector... +//// hsAssert(fTarget->GetCoordinateInterface(), "Player modifier target has no coordinate interface"); +// +// // These are now set in the component +//// if(pSI) +//// { +//// pSI->SetProperty(plSimulationInterface::kAffectLOS, false); +//// pSI->SetProperty(kUpright, true); +//// } +// +// // +// // setup for local player if necessary +// // +// int locallyOwned=so->IsLocallyOwned(); +// if (locallyOwned==plSynchedObject::kMaybe) // don't know since we're still loading, defer +// SetFlag(kNeedsLocalSetup); +// else if (locallyOwned==plSynchedObject::kYes) +// IDoLocalSetup(so); +//} +// +//void plPlayerModifier::IDoLocalSetup(plSceneObject* so) +//{ +// plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; +// pMsg->SetCmd(plCameraMsg::kSetSubject); +// pMsg->SetSubject(so); +// pMsg->SetBCastFlag( plMessage::kBCastByExactType ); +// plgDispatch::MsgSend(pMsg); +// +// // this is to solve the problem of physical vs nonphysical players... +//// plCameraMsg* pMsg2 = TRACKED_NEW plCameraMsg; +//// pMsg2->SetBCastFlag(plMessage::kBCastByExactType); +//// pMsg2->SetCmd(plCameraMsg::kSetOffset); +//// pMsg2->SetCmd(plCameraMsg::kEntering); +//// pMsg2->SetTriggerer(so->GetKey()); +//// pMsg2->SetOffsetY(50); +//// pMsg2->SetOffsetZ(10); +//// plgDispatch::MsgSend(pMsg2); +//} +// +//void plPlayerModifier::IMakeUsListener( plSceneObject *so ) +//{ +// // set the listener to use us... +// plAudioSysMsg* pAudMsg1 = TRACKED_NEW plAudioSysMsg(plAudioSysMsg::kSetListenerFacingRef); +// pAudMsg1->SetSceneObject(so->GetKey()); +// plAudioSysMsg* pAudMsg2 = TRACKED_NEW plAudioSysMsg(plAudioSysMsg::kSetListenerCoordinateRef); +// pAudMsg2->SetSceneObject(so->GetKey()); +// plAudioSysMsg* pAudMsg3 = TRACKED_NEW plAudioSysMsg(plAudioSysMsg::kSetListenerVelocityRef); +// pAudMsg3->SetSceneObject(so->GetKey()); +// plgDispatch::MsgSend(pAudMsg1); +// plgDispatch::MsgSend(pAudMsg2); +// plgDispatch::MsgSend(pAudMsg3); +// +// // Now that we have a valid listener, unmute the audio system +// plgAudioSys::SetMuted( false ); +//} +// +//hsBool plPlayerModifier::MsgReceive(plMessage* msg) +//{ +// plControlEventMsg* pCommandMsg = plControlEventMsg::ConvertNoRef(msg); +// if (pCommandMsg) +// return(HandleControlInput(pCommandMsg)); +// +// plMatrixUpdateMsg* pMMsg = plMatrixUpdateMsg::ConvertNoRef( msg ); +// if (pMMsg && HasFlag(kHasSpawned)) +// { +// hsAssert(GetTarget()->IsLocallyOwned()==plSynchedObject::kNo, "master objects should not get correction msgs"); +// fDesiredMatrix = pMMsg->fMatrix; +// if (bIgnoreDesiredMatrix) +// bIgnoreDesiredMatrix = false; +// else +// { +// bUseDesiredMatrix = true; +// } +// return true; +// } +// +// plSpawnModMsg* pSpawn = plSpawnModMsg::ConvertNoRef(msg); +// if (pSpawn && HasFlag(kWantsToSpawn)) +// { +// spawnPt* pt = TRACKED_NEW spawnPt; +// pt->pt = pSpawn->fPos; +// +// hsVector3 temp(fTarget->GetCoordinateInterface()->GetLocalToWorld().GetTranslate() - pt->pt); +// pt->dist = temp.MagnitudeSquared(); +// fSpawnPoints.Append(pt); +// } +// plPlayerMsg* pPMsg = plPlayerMsg::ConvertNoRef(msg); +// if (pPMsg) +// { +// if (pPMsg->Cmd(plPlayerMsg::kWarpToSpawnPoint)) +// { +// WarpToSpawnPoint(); +// return true; +// } +// } +// return plSingleModifier::MsgReceive(msg); +//} +// +//hsBool plPlayerModifier::HandleControlInput(plControlEventMsg* pMsg) +//{ +// hsBool ret=false; +// +// if (pMsg->ControlActivated() && (pMsg->GetControlCode() == B_CONTROL_ROTATE_RIGHT || pMsg->GetControlCode() == B_CONTROL_ROTATE_LEFT || pMsg->GetControlCode() == A_CONTROL_TURN)) +// { +// fRotationScalar = pMsg->GetPct(); +// if ( HasMovementFlag( pMsg->GetControlCode() ) ) +// bIgnoreDesiredMatrix = true; +// } +// if (pMsg->ControlActivated() && !HasMovementFlag( pMsg->GetControlCode() ) ) +// { +// SetMovementFlag( pMsg->GetControlCode() ); +// if ( pMsg->GetControlCode() == B_CONTROL_TURN_TO ) +// { +// //fFacingTarget = pMsg->GetTurnToPt(); +// } +// } +// else +// if ( !pMsg->ControlActivated() && HasMovementFlag( pMsg->GetControlCode() ) ) +// { +// ClearMovementFlag( pMsg->GetControlCode() ); +// } +// +// ret = true; +// return ret; +//} +// +//void plPlayerModifier::SetMoving(hsBool b) +//{ +// if (b != bMoving) +// { +// plPlayerMsg* pMsg = TRACKED_NEW plPlayerMsg; +// +// if (b) +// pMsg->SetCmd( plPlayerMsg::kMovementStarted ); +// else +// pMsg->SetCmd( plPlayerMsg::kMovementStopped ); +// +// plgDispatch::MsgSend( pMsg ); +// bMoving = b; +// } +//} +// +// +//hsPoint3 forceForward(0,-200,0); +//hsPoint3 forceRight(-200,0,0); +//hsPoint3 forceUp(0,0,15); +// +//hsBool plPlayerModifier::IEval(double secs, hsScalar del, UInt32 dirty) +//{ +// // setup for local player if necessary +// if (HasFlag(kNeedsLocalSetup)) +// { +// int locallyOwned=fTarget->IsLocallyOwned(); +// if (locallyOwned==plSynchedObject::kYes) +// IDoLocalSetup(fTarget); +// else +// if (locallyOwned==plSynchedObject::kNo) +// ClearFlag(kNeedsLocalSetup); +// } +// +// if (HasFlag(kWantsToSpawn)) +// { +// if (fTarget->IsLocallyOwned()==plSynchedObject::kNo) +// { +// // if our target is a proxy player, don't warp him to a spawn point; +// // we will receive his location as a state update. +// ClearFlag(kWantsToSpawn); +// } +// else +// if (fSpawnPoints.Count() +// // if MP game, make sure we're connected before spawning +// && (!plNetClientMgr::GetInstance()->IsEnabled() || +// plNetClientMgr::GetInstance()->HasJoined()) +// ) +// { +// int i; +//#if 0 +// for (i = 0; i < fSpawnPoints.Count(); i++) +// { +// for (int j = i + 1; j < fSpawnPoints.Count(); j++) +// { +// if (fSpawnPoints[j]->dist < fSpawnPoints[i]->dist) +// { +// spawnPt* pt; +// pt = fSpawnPoints[j]; +// fSpawnPoints[j] = fSpawnPoints[i]; +// fSpawnPoints[i] = pt; +// } +// } +// } +// hsPoint3 warpPoint = fSpawnPoints[0]->pt; +//#else +// // choose spawnPoint based on netID, not distance +// int netID = plNetClientMgr::GetInstance()->GetClientNum(); +// if (netID==-1) +// netID=0; +// hsPoint3 warpPoint = netID>=fSpawnPoints.Count() ? +// fSpawnPoints[fSpawnPoints.Count()-1]->pt : fSpawnPoints[netID]->pt; +//#endif +// // Send msg for net synchronization +// plWarpMsg* warpMsg = TRACKED_NEW plWarpMsg; +// warpMsg->fPos = warpPoint; +// warpMsg->AddReceiver( fTarget->GetKey() ); +// warpMsg->SetWarpFlags(warpMsg->GetWarpFlags() | plWarpMsg::kFlushTransform | plWarpMsg::kZeroVelocity ); +// plgDispatch::MsgSend( warpMsg ); +//#ifdef HS_DEBUGGING +// char str[256]; +// sprintf(str, "%s has %d spawnPoints. Using pt %f %f %f\n", +// GetKeyName(), fSpawnPoints.GetCount(), +// fSpawnPoints[0]->pt.fX,fSpawnPoints[0]->pt.fY,fSpawnPoints[0]->pt.fZ); +// hsStatusMessage(str); +//#endif +// for (i = 0; i < fSpawnPoints.Count(); i++) +// delete fSpawnPoints[i]; +// +// fSpawnPoints.SetCount(0); +// ClearFlag(kWantsToSpawn); +// } +// else +// { +// plSpawnRequestMsg* pMsg = TRACKED_NEW plSpawnRequestMsg; +// pMsg->SetSender(GetKey()); +// plgDispatch::MsgSend( pMsg ); +// } +// bIgnoreDesiredMatrix = true; +// return true; +// } +// else +// { +// if( !HasFlag( kHasSpawned ) ) +// { +// // Don't make us listener until we have actually spawned +// IMakeUsListener( fTarget ); +// SetFlag(kHasSpawned); +// } +// } +// +// if (!fTarget->GetCoordinateInterface()) +// return true; +// +// // update our desired position: +//// hsScalar eTime = secs - fLastTime; +// hsScalar eTime = hsTimer::GetDelSysSeconds(); +// +// hsPoint3 newLinearForce(0,0,0); +// +// hsMatrix44 targetMatrix; +// if (bUseDesiredMatrix) +// targetMatrix = fDesiredMatrix; +// else +// targetMatrix = fTarget->GetCoordinateInterface()->GetLocalToWorld(); +// hsPoint3 playerPos = targetMatrix.GetTranslate(); +// hsVector3 view, up, right; +// targetMatrix.GetAxis(&view, &up, &right); +// +// hsScalar speed = fMaxVelocity; +// hsScalar turn = fTurnRate; +// +// if (HasMovementFlag(B_CONTROL_MODIFIER_FAST)) +// { +// turn *= 0.25; +// speed *= 3.5; +// } +// if (HasMovementFlag(B_CONTROL_MOVE_FORWARD)) +// { +// playerPos += view * speed * eTime; +// newLinearForce = newLinearForce + forceForward * speed * eTime; // calc force for physics +// } +// if (HasMovementFlag(B_CONTROL_MOVE_BACKWARD)) +// { +// playerPos += view * speed * eTime * -1; +// newLinearForce = newLinearForce + forceForward * speed * eTime * -1; // calc force for physics +// } +// if (HasMovementFlag(B_CONTROL_STRAFE_LEFT)) +// { +// playerPos += right * speed * eTime * -1; +// +// newLinearForce = newLinearForce + forceRight * speed * eTime * -1; +// } +// if (HasMovementFlag(B_CONTROL_STRAFE_RIGHT)) +// { +// playerPos += right * speed * eTime; +// +// newLinearForce = newLinearForce + forceRight * speed * eTime; +// } +// if (HasMovementFlag(B_CONTROL_MOVE_DOWN)) +// { +// playerPos += up * speed * eTime * -1; +// +// newLinearForce = newLinearForce + forceUp * speed * eTime * -1; +// } +// if (HasMovementFlag(B_CONTROL_MOVE_UP)) +// { +// playerPos += up * speed * eTime; +// +// newLinearForce = newLinearForce + forceUp * speed * eTime; +// } +// +// +// fDesiredPosition = playerPos; +// +// // move toward our desired position... +// +// hsPoint3 curPos = targetMatrix.GetTranslate(); +// hsPoint3 newPos; +// +// hsVector3 dir(fDesiredPosition - curPos); +// hsScalar distToGoal=dir.Magnitude(); +// +// if (dir.MagnitudeSquared() > 0.0f) +// dir.Normalize(); +// +// hsVector3 vel( view * fCurSpeed ); +// +// IAdjustVelocity(fAcceleration, fDeceleration, &dir, &vel, fMaxVelocity, distToGoal, eTime); +// fCurSpeed = vel.Magnitude(); +// +// hsScalar distMoved = IClampVelocity(&vel, fMaxVelocity, eTime); +// +// // compute final pos +// if (distMoved > distToGoal) +// newPos = fDesiredPosition; +// else +// newPos = curPos + vel; +// +// // calculate rotation matrix +// +// hsVector3 rotUp(0,0,1); +// hsVector3 rotRight(1,0,0); +// hsMatrix44 rot; +// +// if ( HasMovementFlag( B_CONTROL_TURN_TO ) ) +// { +// // compute view goal +// +// hsVector3 fPlayerViewGoal(&fFacingTarget,&curPos); +// fPlayerViewGoal.fZ = 0; +// fPlayerViewGoal.Normalize(); +// +// // compute degrees needed to turn left/right +// hsVector3 cross = fPlayerViewGoal % view; +// hsScalar dot = fPlayerViewGoal * view; +// hsScalar rad = hsACosine(dot); +// fRotationScalar = 1.0f; +// +// if (cross.fZ<0) +// { +// SetMovementFlag( B_CONTROL_ROTATE_LEFT ); +// } +// else +// { +// SetMovementFlag( B_CONTROL_ROTATE_RIGHT ); +// } +// if (dot >= 0.999f) +// { +// ClearMovementFlag( B_CONTROL_TURN_TO ); +// ClearMovementFlag( B_CONTROL_ROTATE_RIGHT ); +// ClearMovementFlag( B_CONTROL_ROTATE_LEFT ); +// } +// } +// +// hsScalar angle = 0; +// +// if ( HasMovementFlag( B_CONTROL_ROTATE_RIGHT ) ) +// { +// angle = fTurnRate * eTime * -1 * fRotationScalar; +// } +// +// if ( HasMovementFlag( B_CONTROL_ROTATE_LEFT ) || HasMovementFlag( A_CONTROL_TURN ) ) +// { +// angle = fTurnRate * eTime * fRotationScalar; +// } +// +// hsMatrix44 justRot(targetMatrix); +// hsPoint3 zero(0,0,0); +// justRot.SetTranslate(&zero); +// +// if(angle) { +// hsQuat q(angle, &rotUp); +// q.NormalizeIfNeeded(); +// q.MakeMatrix(&rot); +// +// justRot = rot * justRot; +// +// targetMatrix = rot * targetMatrix; +// } +// +// // use the desired rotation matrix to set position and rotation: +// +// +// plSimulationInterface * SI = IGetTargetSimulationInterface(0); +// +// if(SI) +// { +// Havok::Vector3 hkLocalForce(newLinearForce.fX, newLinearForce.fY, newLinearForce.fZ); +// if (bUseDesiredMatrix) +// { +// hsMatrix44 inv; +// +// fDesiredMatrix.GetInverse(&inv); +// +// // we're just going to set the position on the simulation interface directly +// // because it will then be further modified by the simulation and its final position +// // will *then* be sent to the coordinate interface +// SI->SetTransform(fDesiredMatrix, inv); +// } +// +// SI->SetRotation(justRot);//rot); +// SI->ApplyForce(plSimulationInterface::kForce, hkLocalForce); +// } else { +// hsMatrix44 inv; +// targetMatrix.SetTranslate(&newPos); +// targetMatrix.GetInverse(&inv); +// +// plCoordinateInterface* pCI = pCI = IGetTargetCoordinateInterface(0); +// pCI->SetTransform(targetMatrix, inv); +// +// +// } +// +// fLastTime = secs; +// SetMoving(fCurSpeed); +// +// if (bUseDesiredMatrix) +// bUseDesiredMatrix = false; +// return true; +//} +// +//// +//// vector version. dir vector should be normalized +//// +//void plPlayerModifier::IAdjustVelocity(hsScalar adjAccelRate, hsScalar adjDecelRate, +// hsVector3* dir, hsVector3* vel, hsScalar maxSpeed, +// hsScalar distToGoal, double elapsedTime) +//{ +// hsScalar speed = vel->Magnitude(); // save current speed +// *vel = *dir * speed; // change vel to correct dir +// +// // compute accel/decel +// hsScalar finalAccelRate; +// if (IShouldDecelerate(adjDecelRate, speed, distToGoal)) +// { +// finalAccelRate = -adjDecelRate; +// } +// else +// { +// finalAccelRate = adjAccelRate; +// } +// +// if (finalAccelRate != 0) +// { +// // compute accel vector in the direction of the goal +// hsVector3 accelVec = *dir * finalAccelRate; +// accelVec = accelVec * elapsedTime; +// +// // add acceleration to velocity +// *vel = *vel + accelVec; +// } +// else +// { +// *vel = *dir * maxSpeed; +// } +//} +// +//hsScalar plPlayerModifier::IClampVelocity(hsVector3* vel, hsScalar maxSpeed, double elapsedTime) +//{ +// *vel = *vel * elapsedTime; +// maxSpeed *= elapsedTime; +// +// // clamp speed (clamp if going negative?) +// hsScalar distMoved = vel->Magnitude(); +// if (distMoved > maxSpeed) +// { +// vel->Normalize(); +// *vel = *vel * maxSpeed; +// return maxSpeed; +// } +// return distMoved; +//} +// +//hsBool32 plPlayerModifier::IShouldDecelerate(hsScalar decelSpeed, hsScalar curSpeed, hsScalar distToGoal) +//{ +// if (decelSpeed == 0) +// // no deceleration +// return false; +// +// // compute distance required to stop, given decel speed (in units/sec sq) +// hsScalar stopTime = curSpeed / decelSpeed; +// hsScalar avgSpeed = curSpeed * .5f; +// hsScalar stopDist = avgSpeed * stopTime; +// +// return (hsABS(distToGoal) <= hsABS(stopDist)); // stopDist+avgSpeed? +//} +// diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/plPlayerModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/plPlayerModifier.h new file mode 100644 index 00000000..286fac9b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCharacter/plPlayerModifier.h @@ -0,0 +1,133 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// +//#ifndef plPlayerModifier_inc +//#define plPlayerModifier_inc +// +//#include "../pnModifier/plSingleModifier.h" +//#include "../pnSceneObject/plSimulationInterface.h" +//#include "hsMatrix44.h" +// +//class plControlEventMsg; +// +//namespace Havok { +// class Vector3; +//} +// +//class plPlayerModifier : public plSingleModifier +//{ +//protected: +// +// enum +// { +// kWantsToSpawn = 0, +// kTimerSet, +// kHasSpawned, +// kNeedsLocalSetup +// }; +// +// struct spawnPt +// { +// hsPoint3 pt; +// hsScalar dist; +// }; +// +// static hsScalar fTurnRate; +// +// static hsScalar fAcceleration; +// static hsScalar fDeceleration; +// static hsScalar fMaxVelocity; +// hsScalar fCurSpeed; +// +// +// double fLastTime; +// hsMatrix44 fDesiredMatrix; +// +// hsPoint3 fDesiredPosition; +// hsPoint3 fFacingTarget; +// bool bUseDesiredFacing; +// bool bUseDesiredMatrix; +// bool bIgnoreDesiredMatrix; +// +// hsScalar fRotationScalar; +// hsTArray fSpawnPoints; +// +// void IAdjustVelocity(hsScalar adjAccelRate, +// hsScalar adjDecelRate, +// hsVector3* dir, +// hsVector3* vel, +// hsScalar maxSpeed, +// hsScalar distToGoal, +// double elapsedTime); +// +// hsScalar IClampVelocity(hsVector3* vel, hsScalar maxSpeed, double elapsedTime); +// hsBool32 IShouldDecelerate(hsScalar decelSpeed, hsScalar curSpeed, hsScalar distToGoal); +// +// hsBool HasMovementFlag(int f) const { return fMoveFlags.IsBitSet(f); } +// void SetMovementFlag(int f) { fMoveFlags.SetBit(f); } +// void ClearMovementFlag(int which) { fMoveFlags.ClearBit( which ); } +// +// hsBitVector fMoveFlags; +// hsBitVector fFlags; +// +// void WarpToSpawnPoint() { SetFlag( kWantsToSpawn ); } +// +// hsBool bMoving; +// +// void IApplyForce(plSimulationInterface::plSimpleForce type, const Havok::Vector3 &vec); +// void IDoLocalSetup(plSceneObject*); +// void IMakeUsListener( plSceneObject *so ); +// +//public: +// plPlayerModifier(); +// virtual ~plPlayerModifier(); +// +// CLASSNAME_REGISTER( plPlayerModifier ); +// GETINTERFACE_ANY( plPlayerModifier, plSingleModifier ); +// +// virtual hsBool MsgReceive(plMessage* msg); +// virtual void AddTarget(plSceneObject* so); +// virtual void RemoveTarget(plSceneObject* so); +// +// hsBool HandleControlInput(plControlEventMsg* pMsg); +// virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); +// +// void SetMoving(hsBool b); +// hsBool IsMoving() { return bMoving; } +// +// hsBool HasFlag(int f) const { return fFlags.IsBitSet(f); } +// void SetFlag(int f) { fFlags.SetBit(f); } +// void ClearFlag(int which) { fFlags.ClearBit( which ); } +// +// static void SetTurnRate(float f) {fTurnRate = f;} +// static void SetAcceleration(float f) {fAcceleration = f;} +// static void SetDeceleration(float f) {fDeceleration = f;} +// static void SetVelocity(float f) {fMaxVelocity = f;} +// +// +//}; +// +//#endif plPlayerModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plANDConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plANDConditionalObject.cpp new file mode 100644 index 00000000..a2a0f1bd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plANDConditionalObject.cpp @@ -0,0 +1,107 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plANDConditionalObject.h" +#include "../plPhysical/plDetectorModifier.h" +#include "hsResMgr.h" +#include "../../NucleusLib/pnModifier/plConditionalObject.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" +#include "../plMessage/plCondRefMsg.h" + +plANDConditionalObject::plANDConditionalObject() +{ + +} + +plANDConditionalObject::~plANDConditionalObject() +{ + for (int i = 0; i < fChildren.Count(); i++) + delete (fChildren[i]); +} + +hsBool plANDConditionalObject::MsgReceive(plMessage* msg) +{ + plCondRefMsg* pCondMsg = plCondRefMsg::ConvertNoRef(msg); + if (pCondMsg) + { + fChildren[pCondMsg->fWhich] = plConditionalObject::ConvertNoRef( pCondMsg->GetRef() ); + return true; + } + return plConditionalObject::MsgReceive(msg); +} + +void plANDConditionalObject::Evaluate() +{ + if (!Satisfied()) + { + for (int i = 0; i < fChildren.Count(); i++) + { + if (!fChildren[i]->Satisfied()) + return; + } + SetSatisfied(true); + fLogicMod->RequestTrigger(); + } + else + { + for (int i = 0; i < fChildren.Count(); i++) + { + if (fChildren[i]->Satisfied()) + return; + } + SetSatisfied(false); + } +} + +void plANDConditionalObject::Reset() +{ + for (int i = 0; i < fChildren.Count(); i++) + fChildren[i]->Reset(); +} + +void plANDConditionalObject::Read(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Read(stream, mgr); + + plCondRefMsg* refMsg; + int n = stream->ReadSwap32(); + fChildren.SetCountAndZero(n); + for(int i = 0; i < n; i++ ) + { + refMsg = TRACKED_NEW plCondRefMsg(GetKey(), i); + mgr->ReadKeyNotifyMe(stream,refMsg, plRefFlags::kActiveRef); + } +} + +void plANDConditionalObject::Write(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Write(stream, mgr); + + stream->WriteSwap32(fChildren.GetCount()); + for( int i = 0; i < fChildren.GetCount(); i++ ) + fChildren[i]->Write(stream, mgr); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plANDConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plANDConditionalObject.h new file mode 100644 index 00000000..daa9a741 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plANDConditionalObject.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plANDConditionalObject_inc +#define plANDConditionalObject_inc + +#include "../../NucleusLib/pnModifier/plConditionalObject.h" +#include "hsTemplates.h" + +class plANDConditionalObject : public plConditionalObject +{ +protected: + hsTArray fChildren; + +public: + + plANDConditionalObject(); + ~plANDConditionalObject(); + + CLASSNAME_REGISTER( plANDConditionalObject ); + GETINTERFACE_ANY( plANDConditionalObject, plConditionalObject ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + hsBool MsgReceive(plMessage* msg); + void Evaluate(); + void Reset(); + +}; + +#endif // plANDConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plActivatorConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plActivatorConditionalObject.cpp new file mode 100644 index 00000000..be11f0da --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plActivatorConditionalObject.cpp @@ -0,0 +1,205 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plActivatorConditionalObject.h" +#include "../plPhysical/plDetectorModifier.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" +#include "../plMessage/plActivatorMsg.h" +#include "../pnMessage/plNotifyMsg.h" + + +plActivatorConditionalObject::plActivatorConditionalObject() +{ + SetFlag(kLocalElement); // since it relies on user input +} + +hsBool plActivatorConditionalObject::MsgReceive(plMessage* msg) +{ + plActivatorMsg* pDetectorMsg = plActivatorMsg::ConvertNoRef(msg); + if (pDetectorMsg) + { + if (pDetectorMsg->fTriggerType == plActivatorMsg::kVolumeEnter || pDetectorMsg->fTriggerType == plActivatorMsg::kVolumeExit) + return plConditionalObject::MsgReceive(msg); + + for (int i = 0; i < fActivators.Count(); i++) + { + if (pDetectorMsg && pDetectorMsg->GetSender() == fActivators[i]) + { + if (pDetectorMsg->fTriggerType == plActivatorMsg::kUnPickedTrigger || + pDetectorMsg->fTriggerType == plActivatorMsg::kExitUnTrigger || + pDetectorMsg->fTriggerType == plActivatorMsg::kEnterUnTrigger || + pDetectorMsg->fTriggerType == plActivatorMsg::kCollideUnTrigger ) + + { + // is this a toggle activator? + if (IsToggle()) + return true; + + // are we in a triggered state? + if (fLogicMod->HasFlag(plLogicModBase::kTriggered)) + { + if (pDetectorMsg->fTriggerType == plActivatorMsg::kUnPickedTrigger) + fLogicMod->GetNotify()->AddPickEvent(pDetectorMsg->fHitterObj, pDetectorMsg->fPickedObj, false, pDetectorMsg->fHitPoint); + + if ((pDetectorMsg->fTriggerType == plActivatorMsg::kCollideExit) || + (pDetectorMsg->fTriggerType == plActivatorMsg::kVolumeExit) ) + fLogicMod->GetNotify()->AddCollisionEvent(true, pDetectorMsg->fHitterObj, pDetectorMsg->fHiteeObj); + + if ((pDetectorMsg->fTriggerType == plActivatorMsg::kCollideEnter) || + (pDetectorMsg->fTriggerType == plActivatorMsg::kVolumeEnter) ) + fLogicMod->GetNotify()->AddCollisionEvent(false, pDetectorMsg->fHitterObj, pDetectorMsg->fHiteeObj); + + fLogicMod->RequestUnTrigger(); + } + } + else + { + // are we a toggle that has been triggered? + // are we in a triggered state? + if (fLogicMod->HasFlag(plLogicModBase::kTriggered) && IsToggle()) + { + if (pDetectorMsg->fTriggerType == plActivatorMsg::kUnPickedTrigger) + fLogicMod->GetNotify()->AddPickEvent(pDetectorMsg->fHitterObj, pDetectorMsg->fPickedObj, false, pDetectorMsg->fHitPoint); + + if ((pDetectorMsg->fTriggerType == plActivatorMsg::kCollideExit) || + (pDetectorMsg->fTriggerType == plActivatorMsg::kVolumeExit) ) + fLogicMod->GetNotify()->AddCollisionEvent(true, pDetectorMsg->fHitterObj, pDetectorMsg->fHiteeObj); + + if ((pDetectorMsg->fTriggerType == plActivatorMsg::kCollideEnter) || + (pDetectorMsg->fTriggerType == plActivatorMsg::kVolumeEnter) ) + fLogicMod->GetNotify()->AddCollisionEvent(false, pDetectorMsg->fHitterObj, pDetectorMsg->fHiteeObj); + + fLogicMod->RequestUnTrigger(); + return true; + } + + + if (!fLogicMod->VerifyConditions( pDetectorMsg )) + return false; + // this is used as part of a picked detector sometimes... + if (pDetectorMsg->fTriggerType == plActivatorMsg::kPickedTrigger) + fLogicMod->GetNotify()->AddPickEvent(pDetectorMsg->fHitterObj, pDetectorMsg->fPickedObj, true, pDetectorMsg->fHitPoint); + + if ((pDetectorMsg->fTriggerType == plActivatorMsg::kCollideExit) || + (pDetectorMsg->fTriggerType == plActivatorMsg::kVolumeExit) ) + { + fLogicMod->GetNotify()->AddCollisionEvent(false, pDetectorMsg->fHitterObj, pDetectorMsg->fHiteeObj); + } + if ((pDetectorMsg->fTriggerType == plActivatorMsg::kCollideEnter) || + (pDetectorMsg->fTriggerType == plActivatorMsg::kVolumeEnter) ) + { + fLogicMod->GetNotify()->AddCollisionEvent(true, pDetectorMsg->fHitterObj, pDetectorMsg->fHiteeObj); + } + SetSatisfied(true); + //hsBool netRequest = msg->HasBCastFlag(plMessage::kNetNonLocal); + //fLogicMod->RequestTrigger(netRequest); + fLogicMod->RequestTrigger(false); + } + return true; + } + } + } + return plConditionalObject::MsgReceive(msg); +} + +void plActivatorConditionalObject::Read(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Read(stream, mgr); + fActivators.Reset(); + int n = stream->ReadSwap32(); + for (int i = 0; i < n; i++) + fActivators.Append(mgr->ReadKey(stream)); +} +void plActivatorConditionalObject::Write(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Write(stream, mgr); + stream->WriteSwap32(fActivators.Count()); + for (int i = 0; i < fActivators.Count(); i++) + mgr->WriteKey(stream, fActivators[i]); +} + +void plActivatorConditionalObject::SetActivatorKey(plKey k) +{ + fActivators.Append(k); +} + +// +// plActivatorActivatorConditional +// + +hsBool plActivatorActivatorConditionalObject::MsgReceive(plMessage* msg) +{ + plNotifyMsg* pDetectorMsg = plNotifyMsg::ConvertNoRef(msg); + if (pDetectorMsg) + { + if (!pDetectorMsg->fState && fLogicMod->HasFlag(plLogicModBase::kTriggered)) + { + fLogicMod->RequestUnTrigger(); + } + else + if (pDetectorMsg->fState && !fLogicMod->HasFlag(plLogicModBase::kTriggered)) + { + if (!fLogicMod->VerifyConditions( pDetectorMsg )) + return false; + + SetSatisfied(true); + fLogicMod->RequestTrigger(false); + } + return true; + } + return plConditionalObject::MsgReceive(msg); +} + + +hsBool plVolActivatorConditionalObject::MsgReceive(plMessage* msg) +{ + plActivatorMsg* pDetectorMsg = plActivatorMsg::ConvertNoRef(msg); + if (pDetectorMsg) + { + for (int i = 0; i < fActivators.Count(); i++) + { + if (!fLogicMod->VerifyConditions( pDetectorMsg )) + return false; + + if ((pDetectorMsg->fTriggerType == plActivatorMsg::kCollideExit) || + (pDetectorMsg->fTriggerType == plActivatorMsg::kVolumeExit) ) + { + fLogicMod->GetNotify()->AddCollisionEvent(false, pDetectorMsg->fHitterObj, pDetectorMsg->fHiteeObj); + } + if ((pDetectorMsg->fTriggerType == plActivatorMsg::kCollideEnter) || + (pDetectorMsg->fTriggerType == plActivatorMsg::kVolumeEnter) ) + { + fLogicMod->GetNotify()->AddCollisionEvent(true, pDetectorMsg->fHitterObj, pDetectorMsg->fHiteeObj); + } + SetSatisfied(true); + fLogicMod->RequestTrigger(false); + } + return true; + } + return plConditionalObject::MsgReceive(msg); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plActivatorConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plActivatorConditionalObject.h new file mode 100644 index 00000000..d51c98c7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plActivatorConditionalObject.h @@ -0,0 +1,90 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plActivatorConditionalObject_inc +#define plActivatorConditionalObject_inc + +#include "../../NucleusLib/pnModifier/plConditionalObject.h" +#include "hsTemplates.h" + +class plKey; + +class plActivatorConditionalObject : public plConditionalObject +{ +protected: + + hsTArray fActivators; + +public: + + plActivatorConditionalObject(); + ~plActivatorConditionalObject(){;} + + CLASSNAME_REGISTER( plActivatorConditionalObject ); + GETINTERFACE_ANY( plActivatorConditionalObject, plConditionalObject ); + + virtual hsBool MsgReceive(plMessage* msg); + + void Evaluate(){;} + void SetActivatorKey(plKey k); + void Reset() { SetSatisfied(false); } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + +}; + +class plActivatorActivatorConditionalObject : public plActivatorConditionalObject +{ +public: + + plActivatorActivatorConditionalObject(){;} + ~plActivatorActivatorConditionalObject(){;} + + CLASSNAME_REGISTER( plActivatorActivatorConditionalObject ); + GETINTERFACE_ANY( plActivatorActivatorConditionalObject, plActivatorConditionalObject ); + + virtual hsBool MsgReceive(plMessage* msg); + + +}; + +class plVolActivatorConditionalObject : public plActivatorConditionalObject +{ +public: + + plVolActivatorConditionalObject(){;} + ~plVolActivatorConditionalObject(){;} + + CLASSNAME_REGISTER( plVolActivatorConditionalObject ); + GETINTERFACE_ANY( plVolActivatorConditionalObject, plActivatorConditionalObject ); + + virtual hsBool MsgReceive(plMessage* msg); + + +}; + +#endif // plActivatorConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plAnimationEventConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plAnimationEventConditionalObject.cpp new file mode 100644 index 00000000..410fdce2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plAnimationEventConditionalObject.cpp @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plAnimationEventConditionalObject.h" +#include "../pnInputCore/plKeyDef.h" +#include "../plModifier/plSimpleModifier.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "../plMessage/plAnimCmdMsg.h" + +plAnimationEventConditionalObject::plAnimationEventConditionalObject(plKey pTargetModifier) : +fTarget(pTargetModifier), +fAction(kEventEnd) +{ +} + +hsBool plAnimationEventConditionalObject::MsgReceive(plMessage* msg) +{ + plEventCallbackMsg* pMsg = plEventCallbackMsg::ConvertNoRef(msg); + if (pMsg) + { + SetSatisfied(true); +// fLogicMod->RequestTrigger(); + return true; + } + return plConditionalObject::MsgReceive(msg); +} + + +void plAnimationEventConditionalObject::SetEvent(const CallbackEvent b, hsScalar time) +{ + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->AddReceiver(fTarget); + pMsg->SetSender( GetKey() ); + + plEventCallbackMsg* cb = TRACKED_NEW plEventCallbackMsg(GetKey(), b, 0, time); + + pMsg->AddCallback( cb ); + hsRefCnt_SafeUnRef(cb); + pMsg->SetCmd( plAnimCmdMsg::kAddCallbacks ); + + plgDispatch::MsgSend( pMsg ); +} + +void plAnimationEventConditionalObject::Read(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Read(stream, mgr); + fTarget = mgr->ReadKey(stream); + fAction = (CallbackEvent)stream->ReadSwap32(); +} + +void plAnimationEventConditionalObject::Write(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Write(stream, mgr); + mgr->WriteKey(stream, fTarget); + stream->WriteSwap32(fAction); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plAnimationEventConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plAnimationEventConditionalObject.h new file mode 100644 index 00000000..236c1c08 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plAnimationEventConditionalObject.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAnimationEventConditionalObject_inc +#define plAnimationEventConditionalObject_inc + +#include "../pnModifier/plConditionalObject.h" +#include "../pnInputCore/plKeyDef.h" +#include "../pnMessage/plEventCallbackMsg.h" // AnimationEvent's defined here + + +class plAnimationEventConditionalObject : public plConditionalObject +{ +protected: + CallbackEvent fAction; + plKey fTarget; +public: + + + plAnimationEventConditionalObject(){;} + plAnimationEventConditionalObject(plKey pTargetModifier); + ~plAnimationEventConditionalObject(){;} + + CLASSNAME_REGISTER( plAnimationEventConditionalObject ); + GETINTERFACE_ANY( plAnimationEventConditionalObject, plConditionalObject ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + hsBool MsgReceive(plMessage* msg); + + void Evaluate(){;} + void Reset() { SetSatisfied(false); } + + void SetEvent(const CallbackEvent b, hsScalar time = 0.0f); + +}; + + + + +#endif // plAnimationEventConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plConditionalObjectCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plConditionalObjectCreatable.h new file mode 100644 index 00000000..8a3342b3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plConditionalObjectCreatable.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plConditionalObjectCreatable_inc +#define plConditionalObjectCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plANDConditionalObject.h" + +REGISTER_CREATABLE( plANDConditionalObject ); + +#include "plORConditionalObject.h" + +REGISTER_CREATABLE( plORConditionalObject ); + +#include "plPickedConditionalObject.h" + +REGISTER_CREATABLE( plPickedConditionalObject ); + +#include "plActivatorConditionalObject.h" + +REGISTER_CREATABLE( plActivatorConditionalObject ); +REGISTER_CREATABLE( plActivatorActivatorConditionalObject ); +REGISTER_CREATABLE( plVolActivatorConditionalObject ); + +#include "plKeyPressConditionalObject.h" + +REGISTER_CREATABLE( plKeyPressConditionalObject ); + +#include "plAnimationEventConditionalObject.h" + +REGISTER_CREATABLE( plAnimationEventConditionalObject ); + +#include "plControlEventConditionalObject.h" + +REGISTER_CREATABLE( plControlEventConditionalObject ); + +#include "plObjectInBoxConditionalObject.h" + +REGISTER_CREATABLE( plObjectInBoxConditionalObject ); +REGISTER_CREATABLE( plVolumeSensorConditionalObject ); +REGISTER_CREATABLE( plVolumeSensorConditionalObjectNoArbitration ); + +#include "plLocalPlayerInBoxConditionalObject.h" + +REGISTER_CREATABLE( plLocalPlayerInBoxConditionalObject ); + +#include "plObjectIntersectPlaneConditionalObject.h" + +REGISTER_CREATABLE( plObjectIntersectPlaneConditionalObject ); + +#include "plLocalPlayerIntersectPlaneConditionalObject.h" + +REGISTER_CREATABLE( plLocalPlayerIntersectPlaneConditionalObject ); + +#include "plFacingConditionalObject.h" + +REGISTER_CREATABLE( plFacingConditionalObject ); + +#endif // plConditionalObjectCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plControlEventConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plControlEventConditionalObject.cpp new file mode 100644 index 00000000..20dc8414 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plControlEventConditionalObject.cpp @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plControlEventConditionalObject.h" +#include "../plPhysical/plDetectorModifier.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" +#include "../plMessage/plInputEventMsg.h" + +plControlEventConditionalObject::plControlEventConditionalObject() +{ + +} + +hsBool plControlEventConditionalObject::MsgReceive(plMessage* msg) +{ + plControlEventMsg* pControlMsg = plControlEventMsg::ConvertNoRef(msg); + if( pControlMsg ) + { + if (pControlMsg->GetControlCode() == fControlEvent && pControlMsg->ControlActivated() && !Satisfied() ) + { + SetSatisfied(true); + // fLogicMod->RequestTrigger(); + } + else + if (pControlMsg->GetControlCode() == fControlEvent && !pControlMsg->ControlActivated() && Satisfied() ) + { + SetSatisfied(false); + } + return true; + } + return plConditionalObject::MsgReceive(msg); +} + +void plControlEventConditionalObject::Read(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Read(stream, mgr); + fControlEvent = (ControlEventCode)stream->ReadSwap32(); +} +void plControlEventConditionalObject::Write(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Write(stream, mgr); + stream->WriteSwap32((UInt32)fControlEvent); + +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plControlEventConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plControlEventConditionalObject.h new file mode 100644 index 00000000..e6f6303e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plControlEventConditionalObject.h @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plControlEventConditionalObject_inc +#define plControlEventConditionalObject_inc + +#include "../../NucleusLib/pnModifier/plConditionalObject.h" +#include "../../NucleusLib/pnInputCore/plControlEventCodes.h" + + +class plControlEventConditionalObject : public plConditionalObject +{ +protected: + + +public: + + plControlEventConditionalObject(); + ~plControlEventConditionalObject(){;} + + CLASSNAME_REGISTER( plControlEventConditionalObject ); + GETINTERFACE_ANY( plControlEventConditionalObject, plConditionalObject ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + + hsBool MsgReceive(plMessage* msg); + + void Evaluate(){;} + void Reset() { SetSatisfied(false); } + + ControlEventCode fControlEvent; + +}; + +#endif // plControlEventConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plFacingConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plFacingConditionalObject.cpp new file mode 100644 index 00000000..c8254ca3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plFacingConditionalObject.cpp @@ -0,0 +1,133 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plFacingConditionalObject.h" +#include "plgDispatch.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" +#include "../plMessage/plActivatorMsg.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnMessage/plFakeOutMsg.h" +#include "../pnNetCommon/plNetApp.h" + +plFacingConditionalObject::plFacingConditionalObject() : +fTolerance(-1.0f), +fDirectional(false) +{ + SetSatisfied(true); +} + +hsBool plFacingConditionalObject::MsgReceive(plMessage* msg) +{ + return plConditionalObject::MsgReceive(msg); +} + + +void plFacingConditionalObject::Write(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Write(stream, mgr); + stream->WriteSwap(fTolerance); + stream->WriteBool(fDirectional); +} + +void plFacingConditionalObject::Read(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Read(stream, mgr); + stream->ReadSwap(&fTolerance); + fDirectional = stream->ReadBool(); +} + +hsBool plFacingConditionalObject::Verify(plMessage* msg) +{ + plActivatorMsg* pActivateMsg = plActivatorMsg::ConvertNoRef(msg); + if (pActivateMsg && pActivateMsg->fHitterObj) + { + plSceneObject* pPlayer = plSceneObject::ConvertNoRef(pActivateMsg->fHitterObj->ObjectIsLoaded()); + if (pPlayer) + { + hsVector3 playerView = pPlayer->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + hsVector3 ourView; + if (fDirectional) + ourView = fLogicMod->GetTarget()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + else + { + hsVector3 v(fLogicMod->GetTarget()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate() - pPlayer->GetCoordinateInterface()->GetLocalToWorld().GetTranslate()); + ourView = v; + ourView.Normalize(); + } + hsScalar dot = playerView * ourView; + if (dot >= fTolerance) + { + fLogicMod->GetNotify()->AddFacingEvent( pActivateMsg->fHitterObj, fLogicMod->GetTarget()->GetKey(), dot, true); + return true; + } + else + { + return false; + } + } + } + plFakeOutMsg* pFakeMsg = plFakeOutMsg::ConvertNoRef(msg); + if (pFakeMsg && plNetClientApp::GetInstance()->GetLocalPlayerKey()) + { + // sanity check + if (!fLogicMod->GetTarget()->GetCoordinateInterface()) + return false; + + plSceneObject* pPlayer = plSceneObject::ConvertNoRef(plNetClientApp::GetInstance()->GetLocalPlayerKey()->ObjectIsLoaded()); + if (pPlayer) + { + hsVector3 playerView = pPlayer->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + hsVector3 ourView; + if (fDirectional) + ourView = fLogicMod->GetTarget()->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + else + { + hsVector3 v(fLogicMod->GetTarget()->GetCoordinateInterface()->GetLocalToWorld().GetTranslate() - pPlayer->GetCoordinateInterface()->GetLocalToWorld().GetTranslate()); + ourView = v; + ourView.fZ = playerView.fZ; + ourView.Normalize(); + } + hsScalar dot = playerView * ourView; + if (dot >= fTolerance) + { + return true; + } + else + { + if (!IsToggle()) + { + fLogicMod->GetNotify()->AddFacingEvent( pPlayer->GetKey(), fLogicMod->GetTarget()->GetKey(), dot, false); + fLogicMod->RequestUnTrigger(); + return false; + } + } + } + } + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plFacingConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plFacingConditionalObject.h new file mode 100644 index 00000000..34fa0838 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plFacingConditionalObject.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plFacingConditionalObject_inc +#define plFacingConditionalObject_inc + +#include "../../NucleusLib/pnModifier/plConditionalObject.h" + +class plSceneObject; + +class plFacingConditionalObject : public plConditionalObject +{ +protected: + hsScalar fTolerance; + hsBool fDirectional; + +public: + + plFacingConditionalObject(); + ~plFacingConditionalObject(){;} + + CLASSNAME_REGISTER( plFacingConditionalObject ); + GETINTERFACE_ANY( plFacingConditionalObject, plConditionalObject ); + + hsBool MsgReceive(plMessage* msg); + + void SetTolerance(hsScalar d) { fTolerance = d; } + void SetDirectional(hsBool d) { fDirectional = d; } + + virtual hsBool Verify(plMessage* msg); + + void Evaluate(){;} + void Reset() { SetSatisfied(true); } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + +}; + + +#endif // plFacingConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plKeyPressConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plKeyPressConditionalObject.cpp new file mode 100644 index 00000000..151dfe9f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plKeyPressConditionalObject.cpp @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plKeyPressConditionalObject.h" +#include "../plPhysical/plDetectorModifier.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" +#include "../plMessage/plInputEventMsg.h" + +plKeyPressConditionalObject::plKeyPressConditionalObject() +{ + SetFlag(kLocalElement); // since it relies on user input +} + +hsBool plKeyPressConditionalObject::MsgReceive(plMessage* msg) +{ + plKeyEventMsg* pKeyMsg = plKeyEventMsg::ConvertNoRef(msg); + if( pKeyMsg ) + { + if (pKeyMsg && pKeyMsg->GetKeyCode() == fKeyEvent && pKeyMsg->GetKeyDown() && !Satisfied() ) + { + SetSatisfied(true); + // fLogicMod->RequestTrigger(); + } + else + if (pKeyMsg && pKeyMsg->GetKeyCode() == fKeyEvent && !pKeyMsg->GetKeyDown() && Satisfied() ) + { + SetSatisfied(false); + } + return true; + } + return plConditionalObject::MsgReceive(msg); +} + +void plKeyPressConditionalObject::Read(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Read(stream, mgr); + fKeyEvent = (plKeyDef)stream->ReadSwap32(); +} +void plKeyPressConditionalObject::Write(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Write(stream, mgr); + stream->WriteSwap32((UInt32)fKeyEvent); + +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plKeyPressConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plKeyPressConditionalObject.h new file mode 100644 index 00000000..5b85a313 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plKeyPressConditionalObject.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plKeyPressConditionalObject_inc +#define plKeyPressConditionalObject_inc + +#include "../../NucleusLib/pnModifier/plConditionalObject.h" +#include "../../NucleusLib/pnInputCore/plKeyDef.h" + + +class plKeyPressConditionalObject : public plConditionalObject +{ +protected: + + plKeyDef fKeyEvent; + +public: + + plKeyPressConditionalObject(); + ~plKeyPressConditionalObject(){;} + + CLASSNAME_REGISTER( plKeyPressConditionalObject ); + GETINTERFACE_ANY( plKeyPressConditionalObject, plConditionalObject ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + hsBool MsgReceive(plMessage* msg); + + void Evaluate(){;} + void Reset() { SetSatisfied(false); } + void SetKeyEvent(const plKeyDef k ) { fKeyEvent = k; } + +}; + +#endif // plKeyPressConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerInBoxConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerInBoxConditionalObject.cpp new file mode 100644 index 00000000..61b4b1c2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerInBoxConditionalObject.cpp @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plLocalPlayerInBoxConditionalObject.h" +#include "../../PubUtilLib/plPhysical/plDetectorModifier.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" + +plLocalPlayerInBoxConditionalObject::plLocalPlayerInBoxConditionalObject() +{ + // find the player's key here... + SetFlag(kLocalElement); // since it relies on the local player +} + +hsBool plLocalPlayerInBoxConditionalObject::MsgReceive(plMessage* msg) +{ + /* + + if our target is within the bounds of the object our logic modifier is attached to... + + { + SetSatisfied(true); + fLogicMod->RequestTrigger(); + return true; + } + */ + return plConditionalObject::MsgReceive(msg); +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerInBoxConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerInBoxConditionalObject.h new file mode 100644 index 00000000..e394178b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerInBoxConditionalObject.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLocalPlayerInBoxConditionalObject_inc +#define plLocalPlayerInBoxConditionalObject_inc + +#include "../../NucleusLib/pnModifier/plConditionalObject.h" + +class plKey; + +class plLocalPlayerInBoxConditionalObject : public plConditionalObject +{ +protected: + plKey fPlayer; + plKey fBox; + +public: + + plLocalPlayerInBoxConditionalObject(); + ~plLocalPlayerInBoxConditionalObject(){;} + + CLASSNAME_REGISTER( plLocalPlayerInBoxConditionalObject ); + GETINTERFACE_ANY( plLocalPlayerInBoxConditionalObject, plConditionalObject ); + + hsBool MsgReceive(plMessage* msg); + + void SetBox(plKey pKey) { fBox = pKey; } + + void Evaluate(){;} + void Reset() { SetSatisfied(false); } + +}; + +#endif // plLocalPlayerInBoxConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerIntersectPlaneConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerIntersectPlaneConditionalObject.cpp new file mode 100644 index 00000000..872c77ad --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerIntersectPlaneConditionalObject.cpp @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plLocalPlayerIntersectPlaneConditionalObject.h" +#include "../../PubUtilLib/plPhysical/plDetectorModifier.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" + +plLocalPlayerIntersectPlaneConditionalObject::plLocalPlayerIntersectPlaneConditionalObject() +{ + SetFlag(kLocalElement); // since it relies on the local player +} + +hsBool plLocalPlayerIntersectPlaneConditionalObject::MsgReceive(plMessage* msg) +{ + /* + + if our target is within the bounds of the object our logic modifier is attached to... + + { + SetSatisfied(true); + fLogicMod->RequestTrigger(); + return true; + } + */ + return plConditionalObject::MsgReceive(msg); +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerIntersectPlaneConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerIntersectPlaneConditionalObject.h new file mode 100644 index 00000000..4845c685 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plLocalPlayerIntersectPlaneConditionalObject.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLocalPlayerIntersectPlaneConditionalObject_inc +#define plLocalPlayerIntersectPlaneConditionalObject_inc + +#include "../../NucleusLib/pnModifier/plConditionalObject.h" + +class plKey; + +class plLocalPlayerIntersectPlaneConditionalObject : public plConditionalObject +{ +protected: + plKey fTarget; + plKey fPlane; + +public: + + plLocalPlayerIntersectPlaneConditionalObject(); + ~plLocalPlayerIntersectPlaneConditionalObject(){;} + + CLASSNAME_REGISTER( plLocalPlayerIntersectPlaneConditionalObject ); + GETINTERFACE_ANY( plLocalPlayerIntersectPlaneConditionalObject, plConditionalObject ); + + hsBool MsgReceive(plMessage* msg); + + void SetTarget(plKey pKey) { fTarget = pKey; } + void SetPlane(plKey pKey) { fPlane = pKey; } + + void Evaluate(){;} + void Reset() { SetSatisfied(false); } + +}; + +#endif // plLocalPlayerIntersectPlaneConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plORConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plORConditionalObject.cpp new file mode 100644 index 00000000..249f7ba8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plORConditionalObject.cpp @@ -0,0 +1,112 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plORConditionalObject.h" +#include "../plPhysical/plDetectorModifier.h" +#include "hsResMgr.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" + +#include "../plMessage/plCondRefMsg.h" + + +plORConditionalObject::plORConditionalObject() +{ + +} + +plORConditionalObject::~plORConditionalObject() +{ + fChildren.SetCountAndZero(0); +} + +hsBool plORConditionalObject::MsgReceive(plMessage* msg) +{ + plCondRefMsg* pCondMsg = plCondRefMsg::ConvertNoRef(msg); + if (pCondMsg) + { + fChildren[pCondMsg->fWhich] = plConditionalObject::ConvertNoRef( pCondMsg->GetRef() ); + fChildren[pCondMsg->fWhich]->SetLogicMod(fLogicMod); + return true; + } + + return plConditionalObject::MsgReceive(msg); +} + +void plORConditionalObject::SetLogicMod(plLogicModBase* pMod) +{ + fLogicMod = pMod; + for (int i = 0; i < fChildren.Count(); i++) + { + if( fChildren[i] ) + fChildren[i]->SetLogicMod(pMod); + } +} + +hsBool plORConditionalObject::Satisfied() +{ + for (int i = 0; i < fChildren.Count(); i++) + { + if (fChildren[i]->Satisfied()) + return true; + } + return false; +} + +void plORConditionalObject::AddChild(plConditionalObject* pObj) +{ + fChildren.Append(pObj); + pObj->SetLogicMod(fLogicMod); +} + +void plORConditionalObject::Reset() +{ + for (int i = 0; i < fChildren.Count(); i++) + fChildren[i]->Reset(); +} + +void plORConditionalObject::Read(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Read(stream, mgr); + + plCondRefMsg* refMsg; + int n = stream->ReadSwap32(); + fChildren.SetCountAndZero(n); + for(int i = 0; i < n; i++ ) + { + refMsg = TRACKED_NEW plCondRefMsg(GetKey(), i); + mgr->ReadKeyNotifyMe(stream,refMsg, plRefFlags::kActiveRef); + } +} + +void plORConditionalObject::Write(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Write(stream, mgr); + + stream->WriteSwap32(fChildren.GetCount()); + for( int i = 0; i < fChildren.GetCount(); i++ ) + mgr->WriteKey(stream, fChildren[i]); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plORConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plORConditionalObject.h new file mode 100644 index 00000000..38c4e1a9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plORConditionalObject.h @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plORConditionalObject_inc +#define plORConditionalObject_inc + +#include "../../NucleusLib/pnModifier/plConditionalObject.h" +#include "hsTemplates.h" + +class plORConditionalObject : public plConditionalObject +{ +protected: + hsTArray fChildren; + +public: + + plORConditionalObject(); + ~plORConditionalObject(); + + CLASSNAME_REGISTER( plORConditionalObject ); + GETINTERFACE_ANY( plORConditionalObject, plConditionalObject ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool Satisfied(); + + virtual hsBool MsgReceive(plMessage* msg); + void Evaluate(){;} + void Reset(); + + virtual void SetLogicMod(plLogicModBase* pMod); + void AddChild(plConditionalObject* pObj); + +}; + +#endif // plORConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp new file mode 100644 index 00000000..3db2a101 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.cpp @@ -0,0 +1,432 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plObjectInBoxConditionalObject.h" +#include "../../PubUtilLib/plPhysical/plDetectorModifier.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" +#include "../../PubUtilLib/plMessage/plActivatorMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnMessage/plFakeOutMsg.h" +#include "../pnNetCommon/plNetApp.h" +#include "../plAvatar/plArmatureMod.h" +#include "../pnSceneObject/plSceneObject.h" + +bool plVolumeSensorConditionalObject::makeBriceHappyVar = true; + +plObjectInBoxConditionalObject::plObjectInBoxConditionalObject() : +fCurrentTrigger(nil) +{ + SetSatisfied(true); +} + +hsBool plObjectInBoxConditionalObject::MsgReceive(plMessage* msg) +{ + plActivatorMsg* pActivateMsg = plActivatorMsg::ConvertNoRef(msg); + if (pActivateMsg) + { + if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeEnter) + { + fInside.Append(pActivateMsg->fHitterObj); + return true; + } + else + if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeExit) + { + for (int i = 0; i < fInside.Count(); i++) + { + if (fInside[i] == pActivateMsg->fHitterObj) + { + fInside.Remove(i); + if (pActivateMsg->fHitterObj == fCurrentTrigger && fCurrentTrigger && fLogicMod->HasFlag(plLogicModBase::kTriggered) && !IsToggle()) + { + fCurrentTrigger = nil; + fLogicMod->GetNotify()->AddContainerEvent( pActivateMsg->fHiteeObj, pActivateMsg->fHitterObj, false ); + fLogicMod->RequestUnTrigger(); + } + } + } + return true; + } + + return false; + } + return plConditionalObject::MsgReceive(msg); +} + +hsBool plObjectInBoxConditionalObject::Verify(plMessage* msg) +{ + plActivatorMsg* pActivateMsg = plActivatorMsg::ConvertNoRef(msg); + if (pActivateMsg) + { + for (int i = 0; i < fInside.Count(); i++) + { + if (pActivateMsg->fHitterObj == fInside[i]) + { + fLogicMod->GetNotify()->AddContainerEvent( pActivateMsg->fHiteeObj, pActivateMsg->fHitterObj, true ); + fCurrentTrigger = pActivateMsg->fHiteeObj; + return true; + } + } + } + + plFakeOutMsg* pFakeMsg = plFakeOutMsg::ConvertNoRef(msg); + if (pFakeMsg && plNetClientApp::GetInstance()->GetLocalPlayerKey()) + { + for (int i = 0; i < fInside.Count(); i++) + { + if (plNetClientApp::GetInstance()->GetLocalPlayerKey() == fInside[i]) + return true; + } + } + return false; +} + + +// +// volume sensor conditional +// +plVolumeSensorConditionalObject::plVolumeSensorConditionalObject() : +fTrigNum(-1), +fType(0), +fFirst(false), +fTriggered(false), +fIgnoreExtraEnters(true) +{ + SetSatisfied(true); +} + + +hsBool plVolumeSensorConditionalObject::MsgReceive(plMessage* msg) +{ + plActivatorMsg* pActivateMsg = plActivatorMsg::ConvertNoRef(msg); + if (pActivateMsg) + { + // single player hack + if (!fLogicMod->HasFlag(plLogicModBase::kRequestingTrigger)) + fLogicMod->GetNotify()->ClearEvents(); + + if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeEnter) + { + int i; + for (i = 0; i < fInside.Count(); i++) + { + if (fInside[i] == pActivateMsg->fHitterObj) + { + if (fIgnoreExtraEnters) + return false; // this is the "correct" way to handle this situation + break; // this is for those special situations where, due to some physics oddity, we need to allow the avatar to enter without exiting + } + } + if (i == fInside.Count()) + fInside.Append(pActivateMsg->fHitterObj); + if (makeBriceHappyVar) + { + plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() ); + if( pObj ) + { + //need to check for human vs quabish type things in here + int i; + for( i = 0; i < pObj->GetNumModifiers(); i++ ) + { + if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i))) + { + if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj) + { + plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i))); + if((am->IsLocalAI())==nil) + { + return false; + } + } + } + } + plSynchedObject* syncObj = (plSynchedObject*)pObj; + if (syncObj->IsLocallyOwned() != plSynchedObject::kYes) + { + return false; + } + } + } + + if (fType == kTypeEnter) + { + fLogicMod->GetNotify()->AddCollisionEvent(true, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); + fLogicMod->RequestTrigger(false); + } + else + { + fLogicMod->GetNotify()->AddCollisionEvent(false, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); + fLogicMod->RequestUnTrigger(); + } + return false; + } + else + if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeExit) + { + for (int i = 0; i < fInside.Count(); i++) + { + if (fInside[i] == pActivateMsg->fHitterObj) + { + fInside.Remove(i); + if (makeBriceHappyVar) + { + //need to check for human vs quabish type things in here + plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() ); + if( pObj ) + { + int i; + for( i = 0; i < pObj->GetNumModifiers(); i++ ) + { + if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i))) + { + if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj) + { + plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i))); + if((am->IsLocalAI())==nil) + { + return false; + } + } + } + } + plSynchedObject* syncObj = (plSynchedObject*)pObj; + if (syncObj->IsLocallyOwned() != plSynchedObject::kYes) + { + return false; + } + } + } + if (fType == kTypeExit) + { + fLogicMod->GetNotify()->AddCollisionEvent(false, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); + fLogicMod->RequestTrigger(false); + + } + else + { + fLogicMod->GetNotify()->AddCollisionEvent(true, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); + fLogicMod->RequestUnTrigger(); + } + return false; + } + } + } + + return false; + } + return plConditionalObject::MsgReceive(msg); +} + +hsBool plVolumeSensorConditionalObject::Satisfied() +{ + if (fType == kTypeExit && fFirst && !fTriggered) + { + if (fInside.Count()) + fTriggered = true; + return true; + } + if (fTriggered) + { + if (fInside.Count() == 0) + fTriggered = false; + return false; + } + + if (fTrigNum == -1) + return true; + if (fInside.Count() == fTrigNum) + return true; + else + return false; +} + +void plVolumeSensorConditionalObject::Read(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Read(stream, mgr); + fTrigNum = stream->ReadSwap32(); + fType = stream->ReadSwap32(); + fFirst = stream->ReadBool(); +} +void plVolumeSensorConditionalObject::Write(hsStream* stream, hsResMgr* mgr) +{ + plConditionalObject::Write(stream, mgr); + stream->WriteSwap32(fTrigNum); + stream->WriteSwap32(fType); + stream->WriteBool(fFirst); +} +#include "../pnMessage/plPlayerPageMsg.h" +#include "../../NucleusLib/inc/plgDispatch.h" +hsBool plVolumeSensorConditionalObjectNoArbitration::MsgReceive(plMessage* msg) +{ + plActivatorMsg* pActivateMsg = plActivatorMsg::ConvertNoRef(msg); + if (pActivateMsg) + { + // single player hack + if (!fLogicMod->HasFlag(plLogicModBase::kRequestingTrigger)) + fLogicMod->GetNotify()->ClearEvents(); + fHittee= pActivateMsg->fHiteeObj; + if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeEnter) + { + int i; + for (i = 0; i < fInside.Count(); i++) + { + if (fInside[i] == pActivateMsg->fHitterObj) + { + if (fIgnoreExtraEnters) + return false; // this is the "correct" way to handle this situation + break; // this is for those special situations where, due to some physics oddity, we need to allow the avatar to enter without exiting + } + } + if (i == fInside.Count()) + fInside.Append(pActivateMsg->fHitterObj); + if (makeBriceHappyVar) + { + plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() ); + if( pObj ) + { + //need to check for human vs quabish type things in here + int i; + for( i = 0; i < pObj->GetNumModifiers(); i++ ) + { + if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i))) + { + if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj) + { + plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i))); + if((am->IsLocalAI())==nil) + { + return false; + } + } + } + } + plSynchedObject* syncObj = (plSynchedObject*)pObj; + if (syncObj->IsLocallyOwned() != plSynchedObject::kYes) + { + return false; + } + } + } + + if (fType == kTypeEnter) + { + fLogicMod->GetNotify()->AddCollisionEvent(true, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); + //fLogicMod->RequestTrigger(false); + + if (!Satisfied()) + return false; + + fLogicMod->Trigger(false); + } + + return false; + } + else + if (pActivateMsg->fTriggerType == plActivatorMsg::kVolumeExit) + { + for (int i = 0; i < fInside.Count(); i++) + { + if (fInside[i] == pActivateMsg->fHitterObj) + { + fInside.Remove(i); + if (makeBriceHappyVar) + { + //need to check for human vs quabish type things in here + plSceneObject *pObj = plSceneObject::ConvertNoRef( pActivateMsg->fHitterObj->ObjectIsLoaded() ); + if( pObj ) + { + int i; + for( i = 0; i < pObj->GetNumModifiers(); i++ ) + { + if (plArmatureMod::ConvertNoRef( pObj->GetModifier(i))) + { + if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pActivateMsg->fHitterObj) + { + plArmatureMod *am=const_cast( plArmatureMod::ConvertNoRef(pObj->GetModifier(i))); + if((am->IsLocalAI())==nil) + { + return false; + } + } + } + } + plSynchedObject* syncObj = (plSynchedObject*)pObj; + if (syncObj->IsLocallyOwned() != plSynchedObject::kYes) + { + return false; + } + } + } + if (fType == kTypeExit) + { + fLogicMod->GetNotify()->AddCollisionEvent(false, pActivateMsg->fHitterObj, pActivateMsg->fHiteeObj, false); + //fLogicMod->RequestTrigger(false); + if (!Satisfied()) + return false; + + fLogicMod->Trigger(false); + } + return false; + } + } + } + + return false; + } + + plPlayerPageMsg* page = plPlayerPageMsg::ConvertNoRef(msg); + if(page && page->fUnload) + { + for(int j= 0; j< fInside.Count(); j++) + { + if(fInside[j] == page->fPlayer) + {//this is the one inside + if(fHittee) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fHittee->ObjectIsLoaded()); + if(so && so->IsLocallyOwned()) + { + if (fType == kTypeExit) + { + fLogicMod->GetNotify()->AddCollisionEvent(false, page->fPlayer, fHittee, false); + //fLogicMod->RequestTrigger(false); + if (!Satisfied()) + return false; + fLogicMod->Trigger(false); + } + } + } + fInside.Remove(j); + } + } + } + return plConditionalObject::MsgReceive(msg); +} +void plVolumeSensorConditionalObjectNoArbitration::Read(hsStream* stream, hsResMgr* mgr) +{ + plVolumeSensorConditionalObject::Read(stream, mgr); + plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h new file mode 100644 index 00000000..e7f1cde4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectInBoxConditionalObject.h @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plObjectInBoxConditionalObject_inc +#define plObjectInBoxConditionalObject_inc + +#include "../../NucleusLib/pnModifier/plConditionalObject.h" +#include "hsTemplates.h" + +class plKey; + +class plObjectInBoxConditionalObject : public plConditionalObject +{ +protected: + + hsTArray fInside; + plKey fCurrentTrigger; + +public: + + plObjectInBoxConditionalObject(); + ~plObjectInBoxConditionalObject(){;} + + CLASSNAME_REGISTER( plObjectInBoxConditionalObject ); + GETINTERFACE_ANY( plObjectInBoxConditionalObject, plConditionalObject ); + + hsBool MsgReceive(plMessage* msg); + + void Evaluate(){;} + void Reset() { SetSatisfied(false); } + virtual hsBool Satisfied() { return true; } + virtual hsBool Verify(plMessage* msg); + +}; + +class plVolumeSensorConditionalObject : public plConditionalObject +{ + +protected: + + hsTArray fInside; + int fTrigNum; + int fType; + hsBool fFirst; + hsBool fTriggered; + hsBool fIgnoreExtraEnters; +public: + + static bool makeBriceHappyVar; + + + enum + { + kTypeEnter = 1, + kTypeExit, + }; + plVolumeSensorConditionalObject(); + ~plVolumeSensorConditionalObject(){;} + + CLASSNAME_REGISTER( plVolumeSensorConditionalObject ); + GETINTERFACE_ANY( plVolumeSensorConditionalObject, plConditionalObject ); + + virtual hsBool MsgReceive(plMessage* msg); + + void Evaluate(){;} + void Reset() { SetSatisfied(false); } + virtual hsBool Satisfied(); + void SetType(int i) { fType = i; } + + void SetTrigNum(int i) { fTrigNum = i; } + void SetFirst(hsBool b) { fFirst = b; } + + void IgnoreExtraEnters(hsBool ignore = true) {fIgnoreExtraEnters = ignore;} + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + +}; +class plVolumeSensorConditionalObjectNoArbitration : public plVolumeSensorConditionalObject +{ +public: + plVolumeSensorConditionalObjectNoArbitration ():plVolumeSensorConditionalObject(){;} + ~plVolumeSensorConditionalObjectNoArbitration (){;} + CLASSNAME_REGISTER( plVolumeSensorConditionalObjectNoArbitration ); + GETINTERFACE_ANY( plVolumeSensorConditionalObjectNoArbitration, plConditionalObject ); + virtual hsBool MsgReceive(plMessage* msg); + virtual void Read(hsStream* stream, hsResMgr* mgr); +protected: + plKey fHittee; +}; + + +#endif // plObjectInBoxConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectIntersectPlaneConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectIntersectPlaneConditionalObject.cpp new file mode 100644 index 00000000..838fb08f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectIntersectPlaneConditionalObject.cpp @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plObjectIntersectPlaneConditionalObject.h" +#include "../../PubUtilLib/plPhysical/plDetectorModifier.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" + +plObjectIntersectPlaneConditionalObject::plObjectIntersectPlaneConditionalObject() +{ + +} + +hsBool plObjectIntersectPlaneConditionalObject::MsgReceive(plMessage* msg) +{ + /* + + if our target is within the bounds of the object our logic modifier is attached to... + + { + bSatisfied = true; + fLogicMod->RequestTrigger(); + return true; + } + */ + return plConditionalObject::MsgReceive(msg); +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectIntersectPlaneConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectIntersectPlaneConditionalObject.h new file mode 100644 index 00000000..c684a7be --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plObjectIntersectPlaneConditionalObject.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plObjectIntersectPlaneConditionalObject_inc +#define plObjectIntersectPlaneConditionalObject_inc + +#include "../../NucleusLib/pnModifier/plConditionalObject.h" + +class plKey; + +class plObjectIntersectPlaneConditionalObject : public plConditionalObject +{ +protected: + plKey fTarget; + plKey fPlane; + +public: + + plObjectIntersectPlaneConditionalObject(); + ~plObjectIntersectPlaneConditionalObject(){;} + + CLASSNAME_REGISTER( plObjectIntersectPlaneConditionalObject ); + GETINTERFACE_ANY( plObjectIntersectPlaneConditionalObject, plConditionalObject ); + + hsBool MsgReceive(plMessage* msg); + + void SetTarget(plKey pKey) { fTarget = pKey; } + void SetPlane(plKey pKey) { fPlane = pKey; } + + void Evaluate(){;} + void Reset() { SetSatisfied(false); } + +}; + +#endif // plObjectIntersectPlaneConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plPickedConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plPickedConditionalObject.cpp new file mode 100644 index 00000000..dce2cbaf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plPickedConditionalObject.cpp @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plPickedConditionalObject.h" +#include "../../PubUtilLib/plPhysical/plDetectorModifier.h" +#include "../../NucleusLib/pnModifier/plLogicModBase.h" + +#include "../plMessage/plActivatorMsg.h" + +plPickedConditionalObject::plPickedConditionalObject() +{ + SetFlag(kLocalElement); // since it relies on user input +} + +hsBool plPickedConditionalObject::MsgReceive(plMessage* msg) +{ + plActivatorMsg* pDetectorMsg = plActivatorMsg::ConvertNoRef(msg); + if (pDetectorMsg && pDetectorMsg->TriggerType() == plActivatorMsg::kPickedTrigger ) + { + SetSatisfied(true); +// fLogicMod->RequestTrigger(); + return true; + } + return plConditionalObject::MsgReceive(msg); +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plPickedConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plPickedConditionalObject.h new file mode 100644 index 00000000..239c41be --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConditional/plPickedConditionalObject.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPickedConditionalObject_inc +#define plPickedConditionalObject_inc + +#include "../../NucleusLib/pnModifier/plConditionalObject.h" + +class plKey; + +class plPickedConditionalObject : public plConditionalObject +{ +protected: + + +public: + + plPickedConditionalObject(); + ~plPickedConditionalObject(){;} + + CLASSNAME_REGISTER( plPickedConditionalObject ); + GETINTERFACE_ANY( plPickedConditionalObject, plConditionalObject ); + + hsBool MsgReceive(plMessage* msg); + + void Evaluate(){;} + void Reset() { SetSatisfied(false); } + +}; + +#endif // plPickedConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfAvatarConsoleCommands.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfAvatarConsoleCommands.cpp new file mode 100644 index 00000000..aab4083f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfAvatarConsoleCommands.cpp @@ -0,0 +1,924 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// Avatar Console Commands and Groups // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifdef PLASMA_EXTERNAL_RELEASE +#define LIMIT_CONSOLE_COMMANDS 1 +#endif + +#include "pfConsoleCmd.h" +#include "plgDispatch.h" +#include "pfConsole.h" +#include "hsResMgr.h" + +#include "../pfMessage/plArmatureEffectMsg.h" + +#include "../plMessage/plOneShotMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plSimStateMsg.h" +#include "../plMessage/plCCRMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../pnMessage/plNotifyMsg.h" + +#include "../plModifier/plSpawnModifier.h" + +#include "../plResMgr/plKeyFinder.h" + +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvBrainCritter.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plAvatar/plAvBrainSwim.h" +#include "../plAvatar/plAvBrainGeneric.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plSeekPointMod.h" +#include "../plAvatar/plOneShotMod.h" +#include "../plAvatar/plAGAnim.h" +#include "../plAvatar/plAvBrainUser.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plAvatar/plNPCSpawnMod.h" +#include "../plAvatar/plAGAnimInstance.h" +#include "../plAvatar/plArmatureEffects.h" +#include "../plAvatar/plAvTaskSeek.h" + +//#include "../plHavok1/plSimulationMgr.h" +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plDrawable/plInstanceDrawInterface.h" +#include "../plDrawable/plDrawableSpans.h" + +#define PF_SANITY_CHECK( cond, msg ) { if( !( cond ) ) { PrintString( msg ); return; } } + +//// This is here so Microsoft VC won't decide to "optimize" this file out +void pfConsoleCmdGroup::DummyAvatar( void ) +{ +} + +///////////////////////////////////////////////////////////////// +// +// Please see pfConsoleCommands.cpp for detailed instructions on +// how to add console commands. +// +///////////////////////////////////////////////////////////////// + + + +///////////////////////////////////////////////////////////////// +// +// UTILITIES - LOCAL AND OTHERWISE +// +///////////////////////////////////////////////////////////////// + +plKey FindSceneObjectByName(const char* name, const char* ageName, char* statusStr, bool subString=false); +plKey FindObjectByName(const char* name, int type, const char* ageName, char* statusStr, bool subString=false); +plKey FindObjectByNameAndType(const char* name, const char* typeName, const char* ageName, + char* statusStr, bool subString=false); +void PrintStringF(void pfun(const char *),const char * fmt, ...); + +PF_CONSOLE_GROUP( Avatar ) + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_SUBGROUP( Avatar, Spawn ) +PF_CONSOLE_SUBGROUP( Avatar, Multistage ) +PF_CONSOLE_SUBGROUP( Avatar, X ) // experimental stuff +PF_CONSOLE_SUBGROUP( Avatar, Climb ) +PF_CONSOLE_SUBGROUP( Avatar, Turn ) // Turning config +PF_CONSOLE_SUBGROUP( Avatar, Physics ) +PF_CONSOLE_SUBGROUP( Avatar, Warp ) +PF_CONSOLE_SUBGROUP( Avatar, Anim ) // anim commands +PF_CONSOLE_SUBGROUP( Avatar, AG ) // animation graph stuff +PF_CONSOLE_SUBGROUP( Avatar, LOD ) + +#endif // LIMIT_CONSOLE_COMMANDS + +#include "../plAvatar/plAnimStage.h" + +plAvBrainHuman * GetMainAvatarBrain() +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if(avatar) + { + plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(avatar->GetBrain(0)); + if(brain) + return brain; + } + return nil; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// THE COMMANDS +// +///////////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////// +// +// SPAWNING +// +///////////////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Avatar_Spawn, Show, "", "Print a list of spawn points.") +{ + plAvatarMgr *mgr = plAvatarMgr::GetInstance(); + int n = mgr->NumSpawnPoints(); + + for (int i = 0; i < n; i++) + { + const plSpawnModifier * spawn = mgr->GetSpawnPoint(i); + if(spawn) + { + PrintStringF(PrintString, "%d. %s", i, spawn->GetKey()->GetName()); + } + } +} + +PF_CONSOLE_CMD( Avatar_Spawn, Go, "int which", "Go a spawn point indicated by number.") +{ + plAvatarMgr *mgr = plAvatarMgr::GetInstance(); + int n = params[0]; + int max = mgr->NumSpawnPoints(); + + if(n < max) + { + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avatar) + { + double fakeTime = 0.0f; + avatar->SpawnAt(n, fakeTime); + } + } +} + +static int whichSpawn = 0; +PF_CONSOLE_CMD( Avatar_Spawn, next, "", "Go to the next spawn point in sequence.") +{ + plAvatarMgr *mgr = plAvatarMgr::GetInstance(); + int max = mgr->NumSpawnPoints(); + + whichSpawn = ++whichSpawn < max ? whichSpawn : 0; + + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if(avatar) + { + PrintStringF(PrintString, "Spawning at point %d", whichSpawn); + double fakeTime = 0.0f; + avatar->SpawnAt(whichSpawn, fakeTime); + } +} + +PF_CONSOLE_CMD( Avatar_Spawn, prev, "", "Go to the prev spawn point in sequence.") +{ + plAvatarMgr *mgr = plAvatarMgr::GetInstance(); + int max = mgr->NumSpawnPoints(); + + whichSpawn= --whichSpawn >= 0 ? whichSpawn: max-1; + + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if(avatar) + { + PrintStringF(PrintString, "Spawning at point %d", whichSpawn); + double fakeTime = 0.0f; + avatar->SpawnAt(whichSpawn, fakeTime); + } +} + +PF_CONSOLE_CMD( Avatar_Spawn, Respawn,"", "Moves the avatar back to the start point.") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avatar) + { + avatar->Spawn(0); + } +} + +PF_CONSOLE_CMD( Avatar_Spawn, SetSpawnOverride, "string spawnPointName", "Overrides the normal spawn point choice to be the object specified.") +{ + plArmatureMod::SetSpawnPointOverride( (const char *)params[ 0 ] ); + + char str1[ 512 ]; + sprintf( str1, "Spawn point override set to object %s", (const char *)params[ 0 ] ); + PrintString( str1 ); +} + +PF_CONSOLE_CMD( Avatar_Spawn, DontPanic,"", "Toggles the Don't panic link flag.") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avatar) + { + bool state = avatar->ToggleDontPanicLinkFlag(); + char str1[256]; + sprintf(str1, "DontPanic set to %s", state?"true":"false"); + PrintString( str1 ); + } +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// TURN TUNING +// +///////////////////////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_CMD( Avatar_Turn, GetMaxTurn, "int walk", "Show the maximum turn speed in radians per second.") +{ + plAvBrainHuman *brain = GetMainAvatarBrain(); + + float maxTurn = brain->GetMaxTurnSpeed((int)params[0] != 0); + + PrintStringF(PrintString, "Avatar max turn speed is %f radians per second.", maxTurn); +} + +PF_CONSOLE_CMD( Avatar_Turn, SetMaxTurn, "float maxTurn, int walk", "Set the maximum turn speed in radians per second.") +{ + plAvBrainHuman *brain = GetMainAvatarBrain(); + + float newMaxTurn = params[0]; + + brain->SetMaxTurnSpeed(newMaxTurn, (int)params[1] != 0); + + PrintStringF(PrintString, "Set the avatar max turn speed to %f radians per second.", newMaxTurn); +} + +// TURN TIME + +PF_CONSOLE_CMD( Avatar_Turn, GetTurnTime, "int walk", "Show the amount of time required to reach max turn speed.") +{ + plAvBrainHuman *brain = GetMainAvatarBrain(); + + float turnTime = brain->GetTimeToMaxTurn((int)params[0] != 0); + + PrintStringF(PrintString, "The amount of time required to reach max avatar turn speed is %f seconds.", turnTime); +} + +PF_CONSOLE_CMD( Avatar_Turn, SetTurnTime, "float turnTime, int walk", "Set the amount of time required to reach max turn speed.") +{ + plAvBrainHuman *brain = GetMainAvatarBrain(); + + float newTurnTime = params[0]; + + brain->SetTimeToMaxTurn(newTurnTime, (int)params[1] != 0); + + PrintStringF(PrintString, "Set the amount of time required to reach max avatar turn speed to %f seconds.", newTurnTime); +} + +// TURN TYPE + +PF_CONSOLE_CMD( Avatar_Turn, GetTurnType, "int walk", "Show the amount of time required to reach max turn speed.") +{ + plAvBrainHuman *brain = GetMainAvatarBrain(); + + int turnType = brain->GetTurnCurve((int)params[0] != 0); + + PrintStringF(PrintString, "The avatar turn curve type is %d.", turnType); +} + +PF_CONSOLE_CMD( Avatar_Turn, SetTurnType, "int turnType, int walk", "Set the turn acceleration curve type [0..2].") +{ + plAvBrainHuman *brain = GetMainAvatarBrain(); + + int newCurveType = params[0]; + + brain->SetTurnCurve(plAvBrainHuman::TurnCurve(newCurveType), (int)params[1] != 0); + + PrintStringF(PrintString, "Set turn curve to %d.", newCurveType); +} + + +PF_CONSOLE_CMD( Avatar_Turn, SetMouseTurnSensitivity, "float sensitivity", "Set how strong the mouse affects turning.") +{ + plArmatureMod::SetMouseTurnSensitivity(params[0]); + + PrintStringF(PrintString, "Set mouse sensitivity to %f", (float)params[0]); +} + + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// MULTISTAGE +// +///////////////////////////////////////////////////////////////////////////////////////// + + +// MULTISTAGE.TRIGGER +PF_CONSOLE_CMD( Avatar_Multistage, Trigger, "string multiComp", "Triggers the named Multistage Animation component") +{ + char str[256]; + plKey key = FindObjectByNameAndType((const char*)params[0], "plMultistageBehMod", nil, str, true); + PrintString(str); + + if (key) + { + plNotifyMsg *msg = TRACKED_NEW plNotifyMsg; + + msg->fType = plNotifyMsg::kActivator; + msg->fState = 1; // Triggered + + // Setup the event data in case this is a OneShot responder that needs it + plKey playerKey = plAvatarMgr::GetInstance()->GetLocalAvatar()->GetKey(); + proPickedEventData *ed = TRACKED_NEW proPickedEventData; + ed->fPicker = playerKey; + ed->fPicked = key; // ??? + msg->AddEvent(ed); + + // Send it to the responder modifier + msg->AddReceiver(key); + plgDispatch::MsgSend(msg); + } +} + +// MULTISTAGE.ADVANCE +PF_CONSOLE_CMD( Avatar_Multistage, Advance, "", "Advances the avatar's current multistage to the next stage.") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avatar) + { + plKey avKey = avatar->GetKey(); + + plAvBrainGenericMsg *msg = TRACKED_NEW plAvBrainGenericMsg(nil, avKey, plAvBrainGenericMsg::kNextStage, 0, true, 0.5f); + msg->Send(); + } +} + +// MULTISTAGE.REGRESS +PF_CONSOLE_CMD( Avatar_Multistage, Regress, "", "Regresses the avatar's current multistage to the previous stage.") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avatar) + { + plKey avKey = avatar->GetKey(); + + plAvBrainGenericMsg *msg = TRACKED_NEW plAvBrainGenericMsg(nil, avKey, plAvBrainGenericMsg::kPrevStage, 0, true, 0.5f); + msg->Send(); + } +} + +PF_CONSOLE_CMD( Avatar_Multistage, Mode, "string stage1, string stage2, string stage3", "make a simple multistage") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + const char *one = params[0]; + const char *two = params[1]; + const char *three = params[2]; + + PushSimpleMultiStage(avatar, one, two, three, true, true, plAGAnim::kBodyFull); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// MISCKELANYOUS +// +///////////////////////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_CMD( Avatar, Debug, "", "Toggle the avatar debug display.") +{ + static int toggle = 0; + + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if(avatar) + { + toggle = toggle ? 0 : 1; + avatar->SetDebugState(toggle); + } +} + +PF_CONSOLE_CMD( Avatar, DebugByID, "int PlayerID", "Show debug display for a specific avatar.") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->FindAvatarByPlayerID((int)params[0]); + if (avatar) + { + avatar->SetDebugState(!avatar->GetDebugState()); + } +} + +PF_CONSOLE_CMD( Avatar, LogSmartSeek, "int enabled", "Enable/Disable smart seek logging (avatar.log)") +{ + plAvTaskSeek::fLogProcess = ((int)params[0] == 1); +} + +PF_CONSOLE_CMD( Avatar, PopBrain, "", "Remove the topmost brain from the avatar. Careful there, sport.") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if(avatar) + { + avatar->PopBrain(); + } +} + +#include "../plScene/plRelevanceMgr.h" + +PF_CONSOLE_CMD( Avatar, + MarkRelevanceRegion, + "string regionA, string regionB, int wantsUpdate", + "Mark whether avatars in regionA want updates on those on regionB" ) +{ + plRelevanceMgr *mgr = plRelevanceMgr::Instance(); + char *regA = params[0]; + char *regB = params[1]; + mgr->MarkRegion(mgr->GetIndex(regA), mgr->GetIndex(regB), params[2]); +} + +PF_CONSOLE_CMD( Avatar, + ToggleRelevanceRegions, + "", + "Enable/Disable all relevance regions" ) +{ + plRelevanceMgr *mgr = plRelevanceMgr::Instance(); + mgr->SetEnabled(!mgr->GetEnabled()); + + char buff[256]; + sprintf(buff, "All relevance regions are now %s", (mgr->GetEnabled() ? "ENABLED" : "DISABLED")); + PrintString(buff); +} + +PF_CONSOLE_CMD( Avatar, SeekPoint, "string seekpoint", "Move to the given seekpoint.") +{ + char *spName = params[0]; + + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if(avatar) + { + char buff[256]; + plKey seekKey = FindSceneObjectByName(spName, nil, buff); + plSeekPointMod *mod = plAvatarMgr::GetInstance()->FindSeekPoint(spName); + + if(mod) + { + plKey seekKey = mod->GetKey(); + plSeekPointMod *seekMod = (plSeekPointMod *)seekKey->GetObjectPtr(); + plSceneObject *seekTarget = seekMod->GetTarget(0); + plKey targetKey = seekTarget->GetKey(); + + plKey avKey = avatar->GetKey(); + hsScalar unused = 0.0f; + plAvSeekMsg *msg = TRACKED_NEW plAvSeekMsg(nil, avKey, targetKey, unused, false); + + plgDispatch::MsgSend(msg); + } + } +} + +PF_CONSOLE_CMD( Avatar, ShowLocations, "", "Show player positions/orientations" ) +{ + hsBool curVal = plNetClientApp::GetInstance()->GetFlagsBit(plNetClientApp::kShowAvatars); + plNetClientApp::GetInstance()->SetFlagsBit(plNetClientApp::kShowAvatars, !curVal); +} + +PF_CONSOLE_CMD( Avatar, + SetFootEffect, + "int group", + "Force the avatar to use certain footstep effects" ) +{ + const plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avMod) + { + plArmatureEffectStateMsg *msg = TRACKED_NEW plArmatureEffectStateMsg(); + msg->AddReceiver(avMod->GetArmatureEffects()->GetKey()); + msg->fSurface = (int)params[0]; + plgDispatch::MsgSend(msg); + } +} + +PF_CONSOLE_CMD( Avatar, SetStealthMode, "int mode", "Set the stealth mode of your avatar.") +{ + const plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + { + int mode=params[0]; + plKey avKey=avMod->GetTarget(0)->GetKey(); + int level = mode==plAvatarStealthModeMsg::kStealthVisible ? 0 : 1; + + // send msg to make myself invisible locally + plAvatarStealthModeMsg *msg = TRACKED_NEW plAvatarStealthModeMsg(); + msg->SetSender(avKey); + msg->fMode = mode; + msg->fLevel = level; + plgDispatch::MsgSend(msg); + + // send net msg to other players to synch them up + // the msg will go to their NetClientMgr who will decide whether they see + // our avatar as total or semi-invisible based on the invis level. + plCCRInvisibleMsg *invisMsg = TRACKED_NEW plCCRInvisibleMsg; // ctor sets flags and receiver + invisMsg->fInvisLevel=level; + invisMsg->fAvKey=avKey; + invisMsg->Send(); + } +} + +PF_CONSOLE_CMD( Avatar, SortFaces, "", "Toggle sorting of polys on the avatar" ) +{ + const plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + const plSceneObject *so = avMod->GetClothingSO(0); + + plInstanceDrawInterface *idi = plInstanceDrawInterface::ConvertNoRef(const_cast(so->GetDrawInterface())); + plDrawableSpans *drawable = idi->GetInstanceDrawable(); + drawable->SetNativeProperty(plDrawable::kPropSortFaces, !drawable->GetNativeProperty(plDrawable::kPropSortFaces)); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Avatar, SetMouseTurnSensitivity, "float sensitivity", "Set how strong the mouse affects turning.") +{ + plArmatureMod::SetMouseTurnSensitivity(params[0]); + + PrintStringF(PrintString, "Set mouse sensitivity to %f", (float)params[0]); +} + + +PF_CONSOLE_CMD( Avatar, ClickToTurn, "bool b", "Set click-to-turn functionality.") +{ + bool b = params[0]; + plArmatureMod::fClickToTurn = b; +} + +PF_CONSOLE_CMD( Avatar, FakeLinkToObj, "string objName", "Pseudo-Link the avatar to the specified object's location") +{ + char *spName = params[0]; + char buff[256]; + plKey seekKey = FindSceneObjectByName(spName, nil, buff); + if (!seekKey) + { + PrintString("Can't find object with that name, fake link failed."); + return; + } + plPseudoLinkEffectMsg* msg = TRACKED_NEW plPseudoLinkEffectMsg; + msg->fAvatarKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + msg->fLinkObjKey = seekKey; + plgDispatch::MsgSend(msg); + +} + + +#ifndef LIMIT_CONSOLE_COMMANDS + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// PHYSICS +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_CMD( Avatar_Physics, TogglePhysical, "", "Disable/enable physics on the avatar.") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if(avatar) + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlActivated(true); + pMsg->SetControlCode(B_CONTROL_TOGGLE_PHYSICAL); + + avatar->MsgReceive(pMsg); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Animation +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_CMD( Avatar_Anim, BlendAnim, "string Animation, float blendFactor", "Blend the given animation with the current animation.") +{ + char *animationName = params[0]; + float blendFactor = params[1]; + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avatar && animationName) + { + plAGAnim * anim = plAGAnim::FindAnim(animationName); + if(anim) + { + plAGAnimInstance * inst = avatar->AttachAnimationBlended(animationName, blendFactor); + inst->SetLoop(true); + } else + PrintString("BlendAnim: Couldn't find animation."); + } +} + +PF_CONSOLE_CMD( Avatar_Anim, BlendAnimPri, "string Animation, float blendFactor, int priority", "Blend animation using priority.") +{ + char *animationName = params[0]; + float blendFactor = params[1]; + int priority = params[2]; + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avatar && animationName) + { + plAGAnim * anim = plAGAnim::FindAnim(animationName); + if(anim) + { + plAGAnimInstance * inst = avatar->AttachAnimationBlended(animationName, blendFactor, priority); + inst->SetLoop(true); + } else + PrintString("BlendAnim: Couldn't find animation."); + } +} + +PF_CONSOLE_CMD( Avatar_Anim, PlaySimpleAnim, "string AvatarName, string Animation", "Play a simple (root not animated) one time animation on the avatar") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->FindAvatarByModelName(params[0]); + if (avatar) + avatar->PlaySimpleAnim(params[1]); +} + +PF_CONSOLE_CMD( Avatar_Anim, DetachAnim, "string Animation", "Remove the given animation from the avatar.") +{ + char *animationName = params[0]; + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avatar && animationName) + { + plAGAnimInstance * instance = avatar->FindAnimInstance(animationName); + if(instance) + avatar->DetachAnimation(instance); + else + PrintString("DetachAnim: Couldn't find animation on avatar."); + } +} + +PF_CONSOLE_CMD( Avatar_Anim, SetBlend, "string Animation, float blend", "Set the blend of the given animation.") +{ + char *animationName = params[0]; + float blend = params[1]; + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avatar && animationName) + { + plAGAnimInstance *anim = avatar->FindAnimInstance(animationName); + if(anim) + anim->SetBlend(blend); + else + PrintString("SetBlend: Couldn't find animation."); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// LOD +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_CMD( Avatar_LOD, SetLOD, "int lod", "Show only the selected LOD.") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if(avatar) + { + plArmatureLODMod *lodder = plArmatureLODMod::ConvertNoRef(avatar); + + if(lodder) + { + int lod = params[0]; + + lodder->SetLOD(lod); + } else { + PrintString("SetLOD: found avatar, but it doesn't support LOD."); + } + } else { + PrintString("SetLOD: couldn't find avatar with that name."); + } +} + + +PF_CONSOLE_CMD( Avatar_LOD, LimitLOD, "int newLOD", "Zero is (always) highest detail; 2 is (currently) lowest." ) +{ + int newLOD = params[0]; + + if(newLOD >= 0 && newLOD <= 2) + plArmatureLODMod::fMinLOD = newLOD; +} + +PF_CONSOLE_CMD( Avatar_LOD, SetLODDistance, "float newDist", "Set Distance for switching Avatar LOD" ) +{ + plArmatureLODMod::fLODDistance = float(params[0]); +} + +PF_CONSOLE_CMD( Avatar_LOD, GetLODDistance, "", "Get Distance for switching Avatar LOD" ) +{ + char buffer[256]; + + sprintf(buffer, "Lod Distance = %f", plArmatureLODMod::fLODDistance); + PrintString(buffer); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// CLIMBING +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + + + +#include "../plAvatar/plAvBrainClimb.h" + +PF_CONSOLE_CMD( Avatar_Climb, Start, "string direction", "Specify initial mount direction: up, down, left, right") +{ + plArmatureMod *avMod = const_cast(plAvatarMgr::GetInstance()->GetLocalAvatar()); + if(avMod) + { + const char *dirStr = params[0]; + plAvBrainClimb::Mode mode; + if(stricmp(dirStr, "up") == 0) + mode = plAvBrainClimb::kMountingUp; + else if(stricmp(dirStr, "down") == 0) + mode = plAvBrainClimb::kMountingDown; + else if(stricmp(dirStr, "left") == 0) + mode = plAvBrainClimb::kMountingLeft; + else if(stricmp(dirStr, "right") == 0) + mode = plAvBrainClimb::kMountingRight; + plAvBrainClimb *brain = TRACKED_NEW plAvBrainClimb(mode); + avMod->PushBrain(brain); + } +} + +PF_CONSOLE_CMD( Avatar_Climb, EnableDismount, "string direction", "Let the avatar dismount in the specified direction.") +{ + plArmatureMod *avMod = const_cast(plAvatarMgr::GetInstance()->GetLocalAvatar()); + if(avMod) + { + plKey mgr = plAvatarMgr::GetInstance()->GetKey(); + plKey avKey = avMod->GetKey(); + const char *dirStr = params[0]; + plClimbMsg::Direction dir; + if(stricmp(dirStr, "up") == 0) + dir = plClimbMsg::kUp; + else if(stricmp(dirStr, "down") == 0) + dir = plClimbMsg::kDown; + else if(stricmp(dirStr, "left") == 0) + dir = plClimbMsg::kLeft; + else if(stricmp(dirStr, "right") == 0) + dir = plClimbMsg::kRight; + plClimbMsg *msg = TRACKED_NEW plClimbMsg(mgr, avKey, plClimbMsg::kEnableDismount, dir, true); + msg->Send(); + } +} + +PF_CONSOLE_CMD( Avatar_Climb, EnableClimb, "string direction, int onOff", "Allow or forbid climbing in the given direction.") +{ + plArmatureMod *avMod = const_cast(plAvatarMgr::GetInstance()->GetLocalAvatar()); + if(avMod) + { + plKey mgr = plAvatarMgr::GetInstance()->GetKey(); + plKey avKey = avMod->GetKey(); + const char *dirStr = params[0]; + plClimbMsg::Direction dir; + if(stricmp(dirStr, "up") == 0) + dir = plClimbMsg::kUp; + else if(stricmp(dirStr, "down") == 0) + dir = plClimbMsg::kDown; + else if(stricmp(dirStr, "left") == 0) + dir = plClimbMsg::kLeft; + else if(stricmp(dirStr, "right") == 0) + dir = plClimbMsg::kRight; + hsBool enable = static_cast(params[1]) ? true : false; + plClimbMsg *msg = TRACKED_NEW plClimbMsg(mgr, avKey, plClimbMsg::kEnableClimb, dir, enable); + msg->Send(); + } +} + +PF_CONSOLE_CMD( Avatar_Climb, Release, "", "") +{ + plArmatureMod *avMod = const_cast(plAvatarMgr::GetInstance()->GetLocalAvatar()); + if(avMod) + { + plKey mgr = plAvatarMgr::GetInstance()->GetKey(); + plKey avKey = avMod->GetKey(); + plClimbMsg *msg = TRACKED_NEW plClimbMsg(mgr, avKey, plClimbMsg::kRelease); + msg->Send(); + } +} + +PF_CONSOLE_CMD( Avatar_Climb, FallOff, "", "") +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if(avMod) + { + plKey mgr = plAvatarMgr::GetInstance()->GetKey(); + plKey avKey = avMod->GetKey(); + plClimbMsg *msg = TRACKED_NEW plClimbMsg(mgr, avKey, plClimbMsg::kFallOff); + msg->Send(); + } +} + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// SWIMMING +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +PF_CONSOLE_SUBGROUP( Avatar, Swim ) + +PF_CONSOLE_CMD( Avatar_Swim, Start, "", "") +{ + plArmatureMod *avMod = const_cast(plAvatarMgr::GetInstance()->GetLocalAvatar()); + if(avMod) + { + plAvBrainSwim * brayne = TRACKED_NEW plAvBrainSwim(); + avMod->PushBrain(brayne); + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// WARP +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// void WarpPlayerToAnother(hsBool iMove, UInt32 remoteID) +PF_CONSOLE_CMD( Avatar_Warp, WarpToPlayer, "int PlayerID", "Warp our player to the same position as another player.") +{ + plAvatarMgr::WarpPlayerToAnother(true, (int)params[0]); +} + +PF_CONSOLE_CMD( Avatar_Warp, WarpPlayerHere, "int PlayerID", "Warp another player to the same position as us.") +{ + plAvatarMgr::WarpPlayerToAnother(false, (int)params[0]); +} + +PF_CONSOLE_CMD( Avatar_Warp, WarpToXYZ, "float x, float y, float z", "Warp our avatar to the given location.") +{ + plAvatarMgr::WarpPlayerToXYZ((float)params[0], (float)params[1], (float)params[2]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// AG (Animation Graph) +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "hsTimer.h" + +PF_CONSOLE_CMD( Avatar_AG, DumpFull, "", "print out the animation graph for the avatar") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + double time = hsTimer::GetSysSeconds(); + + avatar->DumpAniGraph(nil, false, time); +} + +PF_CONSOLE_CMD( Avatar_AG, DumpFullOptimized, "", "print out the optimized animation graph for the avatar") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + double time = hsTimer::GetSysSeconds(); + avatar->DumpAniGraph(nil, true, time); +} + +PF_CONSOLE_CMD( Avatar_AG, DumpSingle, "string boneName", "print out the animation graph for the given (avatar) bone") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + double time = hsTimer::GetSysSeconds(); + const char *bone = params[0]; + avatar->DumpAniGraph(bone, false, time); +} + +PF_CONSOLE_CMD( Avatar_AG, DumpSingleOptimized, "string boneName", "print out the optimized animatoin graph for the given (avatar) bone") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + double time = hsTimer::GetSysSeconds(); + const char *bone = params[0]; + avatar->DumpAniGraph(bone, true, time); +} + +#endif // LIMIT_CONSOLE_COMMANDS diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfCCRConsoleCommands.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfCCRConsoleCommands.cpp new file mode 100644 index 00000000..3d46e39f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfCCRConsoleCommands.cpp @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +////////////////////////////////////////////////////////////////////////////// +// // +// CCR Console Commands and Groups // +// These console commands are meant for use by customer care reps. // +// Eventually the functionality defined here will be accessed through a GUI// +// // +////////////////////////////////////////////////////////////////////////////// + +// +// Only calls to the CCRMgr interface are allowed here +// See me if you need to include any other files... +// +#include "pfConsoleCmd.h" +#include "pfConsole.h" +#include "../pfCCR/plCCRMgr.h" +#include "../plNetClient/plNetClientMgr.h" + +//// This is here so Microsoft VC won't decide to "optimize" this file out +// YOU ALSO NEED TO CALL THIS FXN +void pfConsoleCmdGroup::DummyCCR( void ) +{ +} + +void PrintStringF(void pfun(const char *),const char * fmt, ...); + + +///////////////////////////////////////////////////////////////// +// +// Please see pfConsoleCommands.cpp for detailed instructions on +// how to add console commands. +// +///////////////////////////////////////////////////////////////// + +#define PF_SANITY_CHECK( cond, msg ) { if( !( cond ) ) { PrintString( msg ); return; } } diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsole.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsole.cpp new file mode 100644 index 00000000..1f146a40 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsole.cpp @@ -0,0 +1,1214 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfConsole Functions // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "pfConsole.h" +#include "pfConsoleEngine.h" +#include "../plPipeline/plDebugText.h" +#include "../plInputCore/plInputDevice.h" +#include "../plInputCore/plInputInterface.h" +#include "../plInputCore/plInputInterfaceMgr.h" +#include "../pnInputCore/plKeyMap.h" +#include "../pnInputCore/plKeyDef.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plConsoleMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "hsTimer.h" +#include "plgDispatch.h" +#include "plPipeline.h" +#include "hsConfig.h" + +#include "../pfPython/cyPythonInterface.h" +#include "../plNetClient/plNetClientMgr.h" + +#ifndef PLASMA_EXTERNAL_RELEASE +#include "../pfGameMgr/pfGameMgr.h" +#endif // PLASMA_EXTERNAL_RELEASE + + +//// Static Class Stuff ////////////////////////////////////////////////////// + +pfConsole *pfConsole::fTheConsole = nil; +UInt32 pfConsole::fConsoleTextColor = 0xff00ff00; +plPipeline *pfConsole::fPipeline = nil; + + +////////////////////////////////////////////////////////////////////////////// +//// pfConsoleInputInterface - Input interface layer for the console ///////// +////////////////////////////////////////////////////////////////////////////// + +class pfConsoleInputInterface : public plInputInterface +{ + protected: + + pfConsole *fConsole; + + + virtual hsBool IHandleCtrlCmd( plCtrlCmd *cmd ) + { + if( cmd->fControlCode == B_SET_CONSOLE_MODE ) + { + if( cmd->fControlActivated ) + { + // Activate/deactivate + switch( fConsole->fMode ) + { + case pfConsole::kModeHidden: + fConsole->ISetMode( pfConsole::kModeSingleLine ); + break; + case pfConsole::kModeSingleLine: + fConsole->ISetMode( pfConsole::kModeFull ); + break; + case pfConsole::kModeFull: + fConsole->ISetMode( pfConsole::kModeHidden ); + break; + } + } + return true; + } + return false; + } + + public: + + pfConsoleInputInterface( pfConsole *console ) + { + fConsole = console; + SetEnabled( true ); // Always enabled + + // Add our control codes to our control map. Do NOT add the key bindings yet. + // Note: HERE is where you specify the actions for each command, i.e. net propagate and so forth. + // This part basically declares us master of the bindings for these commands. + + // IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO + // RestoreDefaultKeyMappings()!!!! + +#ifndef PLASMA_EXTERNAL_RELEASE + fControlMap->AddCode( B_SET_CONSOLE_MODE, kControlFlagNormal | kControlFlagNoRepeat ); +#endif + + // IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO + // RestoreDefaultKeyMappings()!!!! + } + + virtual UInt32 GetPriorityLevel( void ) const { return kConsolePriority; } + virtual UInt32 GetCurrentCursorID( void ) const { return kCursorHidden; } + virtual hsBool HasInterestingCursorID( void ) const { return false; } + + virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg ) + { + plKeyEventMsg *keyMsg = plKeyEventMsg::ConvertNoRef( pMsg ); + + // HACK for now to let runlock work always (until we can think of a more generic and good way of doing this) + if( keyMsg != nil && keyMsg->GetKeyCode() != KEY_CAPSLOCK ) + { + if( fConsole->fMode ) + { + fConsole->IHandleKey( keyMsg ); + return true; + } + } + + return false; + } + + virtual void RefreshKeyMap( void ) + { + } + + virtual void RestoreDefaultKeyMappings( void ) + { + if( fControlMap != nil ) + { + fControlMap->UnmapAllBindings(); +#ifndef PLASMA_EXTERNAL_RELEASE + fControlMap->BindKey( KEY_TILDE, B_SET_CONSOLE_MODE ); +#endif + } + } +}; + +//// Constructor & Destructor //////////////////////////////////////////////// + +pfConsole::pfConsole() +{ + fNumDisplayLines = 32; + fDisplayBuffer = nil; + fTheConsole = this; + fFXEnabled = true; +} + +pfConsole::~pfConsole() +{ + if( fInputInterface != nil ) + { + plInputIfaceMgrMsg *msg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kRemoveInterface ); + msg->SetIFace( fInputInterface ); + plgDispatch::MsgSend( msg ); + + hsRefCnt_SafeUnRef( fInputInterface ); + fInputInterface = nil; + } + + if( fDisplayBuffer != nil ) + { + delete [] fDisplayBuffer; + fDisplayBuffer = nil; + } + + fTheConsole = nil; + + plgDispatch::Dispatch()->UnRegisterForExactType( plConsoleMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->UnRegisterForExactType( plControlEventMsg::Index(), GetKey() ); + +#ifndef PLASMA_EXTERNAL_RELEASE + pfGameMgr::GetInstance()->RemoveReceiver(GetKey()); +#endif // PLASMA_EXTERNAL_RELEASE +} + +pfConsole * pfConsole::GetInstance () { + + return fTheConsole; +} + +//// Init //////////////////////////////////////////////////////////////////// + +void pfConsole::Init( pfConsoleEngine *engine ) +{ + fDisplayBuffer = TRACKED_NEW char[ fNumDisplayLines * kMaxCharsWide ]; + memset( fDisplayBuffer, 0, fNumDisplayLines * kMaxCharsWide ); + + memset( fWorkingLine, 0, sizeof( fWorkingLine ) ); + fWorkingCursor = 0; + + memset( fHistory, 0, sizeof( fHistory ) ); + fHistoryCursor = fHistoryRecallCursor = 0; + + fEffectCounter = 0; + fMode = 0; + fMsgTimeoutTimer = 0; + fHelpMode = false; + fPythonMode = false; + fPythonFirstTime = true; + fPythonMultiLines = 0; + fHelpTimer = 0; + fCursorTicks = 0; + memset( fLastHelpMsg, 0, sizeof( fLastHelpMsg ) ); + fEngine = engine; + + fInputInterface = TRACKED_NEW pfConsoleInputInterface( this ); + plInputIfaceMgrMsg *msg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kAddInterface ); + msg->SetIFace( fInputInterface ); + plgDispatch::MsgSend( msg ); + + // Register for keyboard event messages + plgDispatch::Dispatch()->RegisterForExactType( plConsoleMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->RegisterForExactType( plControlEventMsg::Index(), GetKey() ); + +#ifndef PLASMA_EXTERNAL_RELEASE + pfGameMgr::GetInstance()->AddReceiver(GetKey()); +#endif // PLASMA_EXTERNAL_RELEASE +} + +//// ISetMode //////////////////////////////////////////////////////////////// + +void pfConsole::ISetMode( UInt8 mode ) +{ + fMode = mode; + fEffectCounter = ( fFXEnabled ? kEffectDivisions : 0 ); + fMsgTimeoutTimer = 0; + fInputInterface->RefreshKeyMap(); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfConsole::MsgReceive( plMessage *msg ) +{ + plControlEventMsg *ctrlMsg = plControlEventMsg::ConvertNoRef( msg ); + if( ctrlMsg != nil ) + { + if( ctrlMsg->ControlActivated() && ctrlMsg->GetControlCode() == B_CONTROL_CONSOLE_COMMAND && plNetClientMgr::GetInstance()->GetFlagsBit(plNetClientMgr::kPlayingGame)) + { + fEngine->RunCommand( ctrlMsg->GetCmdString(), IAddLineCallback ); + return true; + } + return false; + } + + plConsoleMsg *cmd = plConsoleMsg::ConvertNoRef( msg ); + if( cmd != nil && cmd->GetString() != nil ) + { + if( cmd->GetCmd() == plConsoleMsg::kExecuteFile ) + { + if( !fEngine->ExecuteFile( (char *)cmd->GetString() ) ) + { + // Change the following line once we have a better way of reporting + // errors in the parsing + static char str[ 256 ]; + static char msg[ 1024 ]; + + sprintf( str, "Error parsing %s", cmd->GetString() ); + sprintf( msg, "%s:\n\nCommand: '%s'\n%s", fEngine->GetErrorMsg(), fEngine->GetLastErrorLine(), +#ifdef HS_DEBUGGING + "" ); + + hsAssert( false, msg ); +#else + "\nPress OK to continue parsing files." ); + + hsMessageBox( msg, str, hsMessageBoxNormal ); +#endif + } + } + else if( cmd->GetCmd() == plConsoleMsg::kAddLine ) + IAddParagraph( (char *)cmd->GetString() ); + else if( cmd->GetCmd() == plConsoleMsg::kExecuteLine ) + { + if( !fEngine->RunCommand( (char *)cmd->GetString(), IAddLineCallback ) ) + { + // Change the following line once we have a better way of reporting + // errors in the parsing + static char msg[ 1024 ]; + + sprintf( msg, "%s:\n\nCommand: '%s'\n", fEngine->GetErrorMsg(), fEngine->GetLastErrorLine() ); + IAddLineCallback( msg ); + } + } + + return true; + } + + + //======================================================================== + // pfGameMgrMsg +#ifndef PLASMA_EXTERNAL_RELEASE + if (pfGameMgrMsg * gameMgrMsg = pfGameMgrMsg::ConvertNoRef(msg)) { + + switch (gameMgrMsg->netMsg->messageId) { + + //================================================================ + // InviteReceived + case kSrv2Cli_GameMgr_InviteReceived: { + const Srv2Cli_GameMgr_InviteReceived & gmMsg = *(const Srv2Cli_GameMgr_InviteReceived *)gameMgrMsg->netMsg; + const char * inviterName = plNetClientMgr::GetInstance()->GetPlayerNameById(gmMsg.inviterId); + AddLineF("[GameMgr] Invite received: %S, %u. Inviter: %s", pfGameMgr::GetInstance()->GetGameNameByTypeId(gmMsg.gameTypeId), gmMsg.newGameId, inviterName ? inviterName : ""); + } + return true; + + //================================================================ + // InviteRevoked + case kSrv2Cli_GameMgr_InviteRevoked: { + const Srv2Cli_GameMgr_InviteRevoked & gmMsg = *(const Srv2Cli_GameMgr_InviteRevoked *)gameMgrMsg->netMsg; + const char * inviterName = plNetClientMgr::GetInstance()->GetPlayerNameById(gmMsg.inviterId); + AddLineF("[GameMgr] Invite revoked: %S, %u. Inviter: %s", pfGameMgr::GetInstance()->GetGameNameByTypeId(gmMsg.gameTypeId), gmMsg.newGameId, inviterName ? inviterName : ""); + } + return true; + + DEFAULT_FATAL(gameMgrMsg->netMsg->messageId); + }; + } +#endif // PLASMA_EXTERNAL_RELEASE + //======================================================================== + + //======================================================================== + // pfGameCliMsg +#ifndef PLASMA_EXTERNAL_RELEASE + if (pfGameCliMsg * gameCliMsg = pfGameCliMsg::ConvertNoRef(msg)) { + + pfGameCli * cli = gameCliMsg->gameCli; + + //==================================================================== + // Handle pfGameCli msgs + switch (gameCliMsg->netMsg->messageId) { + //================================================================ + // PlayerJoined + case kSrv2Cli_Game_PlayerJoined: { + const Srv2Cli_Game_PlayerJoined & netMsg = *(const Srv2Cli_Game_PlayerJoined *)gameCliMsg->netMsg; + AddLineF( + "[Game %s:%u] Player joined: %s", + cli->GetName(), + cli->GetGameId(), + netMsg.playerId + ? plNetClientMgr::GetInstance()->GetPlayerNameById(netMsg.playerId) + : "Computer" + ); + } + return true; + + //================================================================ + // PlayerLeft + case kSrv2Cli_Game_PlayerLeft: { + const Srv2Cli_Game_PlayerLeft & netMsg = *(const Srv2Cli_Game_PlayerLeft *)gameCliMsg->netMsg; + AddLineF( + "[Game %s:%u] Player left: %s", + cli->GetName(), + cli->GetGameId(), + netMsg.playerId + ? plNetClientMgr::GetInstance()->GetPlayerNameById(netMsg.playerId) + : "Computer" + ); + } + return true; + + //================================================================ + // InviteFailed + case kSrv2Cli_Game_InviteFailed: { + const Srv2Cli_Game_InviteFailed & netMsg = *(const Srv2Cli_Game_InviteFailed *)gameCliMsg->netMsg; + AddLineF( + "[Game %s:%u] Invite failed for playerId %u, error %u", + cli->GetName(), + cli->GetGameId(), + netMsg.inviteeId, + netMsg.error + ); + } + return true; + + //================================================================ + // OwnerChange + case kSrv2Cli_Game_OwnerChange: { + const Srv2Cli_Game_OwnerChange & netMsg = *(const Srv2Cli_Game_OwnerChange *)gameCliMsg->netMsg; + AddLineF( + "[Game %s:%u] Owner changed to playerId %u", + cli->GetName(), + cli->GetGameId(), + netMsg.ownerId + ); + } + return true; + } + + //==================================================================== + // Handle Tic-Tac-Toe msgs + if (gameCliMsg->gameCli->GetGameTypeId() == kGameTypeId_TicTacToe) { + + pfGmTicTacToe * ttt = pfGmTicTacToe::ConvertNoRef(cli); + ASSERT(ttt); + + switch (gameCliMsg->netMsg->messageId) { + //============================================================ + // GameStarted + case kSrv2Cli_TTT_GameStarted: { + const Srv2Cli_TTT_GameStarted & netMsg = *(const Srv2Cli_TTT_GameStarted *)gameCliMsg->netMsg; + if (netMsg.yourTurn) + AddLineF( + "[Game %s:%u] Game started. You are X's. You go first.", + cli->GetName(), + cli->GetGameId() + ); + else + AddLineF( + "[Game %s:%u] Game started. You are O's. Other player goes first.", + cli->GetName(), + cli->GetGameId() + ); + ttt->ShowBoard(); + } + return true; + + //============================================================ + // GameOver + case kSrv2Cli_TTT_GameOver: { + const Srv2Cli_TTT_GameOver & netMsg = *(const Srv2Cli_TTT_GameOver *)gameCliMsg->netMsg; + switch (netMsg.result) { + case kTTTGameResultWinner: + if (netMsg.winnerId == NetCommGetPlayer()->playerInt) + AddLineF( + "[Game %s:%u] Game over. You won!", + cli->GetName(), + cli->GetGameId() + ); + else + AddLineF( + "[Game %s:%u] Game over. You lost.", + cli->GetName(), + cli->GetGameId() + ); + break; + + case kTTTGameResultTied: + AddLineF( + "[Game %s:%u] Game over. You tied.", + cli->GetName(), + cli->GetGameId() + ); + break; + + case kTTTGameResultGave: + AddLineF( + "[Game %s:%u] Game over. You win by default.", + cli->GetName(), + cli->GetGameId() + ); + break; + + default: + AddLineF( + "[Game %s:%u] Game over. Server-side error %u.", + cli->GetName(), + cli->GetGameId(), + netMsg.result + ); + break; + } + } + return true; + + //============================================================ + // MoveMade + case kSrv2Cli_TTT_MoveMade: { + const Srv2Cli_TTT_MoveMade & netMsg = *(const Srv2Cli_TTT_MoveMade *)gameCliMsg->netMsg; + const char * playerName + = netMsg.playerId + ? plNetClientMgr::GetInstance()->GetPlayerNameById(netMsg.playerId) + : "Computer"; + AddLineF( + "[Game %s:%u] %s moved:", + cli->GetName(), + cli->GetGameId(), + playerName + ); + ttt->ShowBoard(); + } + return true; + + DEFAULT_FATAL(gameCliMsg->netMsg->messageId); + } + } + else { + FATAL("Unknown game type"); + } + + return true; + } +#endif // PLASMA_EXTERNAL_RELEASE + //======================================================================== + + return hsKeyedObject::MsgReceive(msg); +} + +//// IHandleKey ////////////////////////////////////////////////////////////// + +void pfConsole::IHandleKey( plKeyEventMsg *msg ) +{ + char *c, key; + int i,eol; + static hsBool findAgain = false; + static UInt32 findCounter = 0; + + + if( !msg->GetKeyDown() ) + return; + + if( msg->GetKeyCode() == KEY_ESCAPE ) + { + fWorkingLine[ fWorkingCursor = 0 ] = 0; + findAgain = false; + findCounter = 0; + fHelpMode = false; + fPythonMode = false; + fPythonMultiLines = 0; + IUpdateTooltip(); + } + else if( msg->GetKeyCode() == KEY_TAB ) + { + if ( fPythonMode ) + { + // if we are in Python mode, then just add two spaces, tab over, sorta + if ( strlen(fWorkingLine) < kWorkingLineSize+2 ) + { + strcat(&fWorkingLine[fWorkingCursor], " "); + fWorkingCursor += 2; + } + } + else + { + static char lastSearch[ kMaxCharsWide ]; + char search[ kMaxCharsWide ]; + + if( !findAgain && findCounter == 0 ) + strcpy( lastSearch, fWorkingLine ); + strcpy( search, lastSearch ); + + if( findCounter > 0 ) + { + // Not found the normal way; try using an unrestricted search + if( fEngine->FindNestedPartialCmd( search, findCounter, true ) ) + { + strcpy( fWorkingLine, search ); + findCounter++; + } + else + { + /// Try starting over...? + findCounter = 0; + if( fEngine->FindNestedPartialCmd( search, findCounter, true ) ) + { + strcpy( fWorkingLine, search ); + findCounter++; + } + } + } + else if( fEngine->FindPartialCmd( search, findAgain, true ) ) + { + strcpy( fWorkingLine, search ); + findAgain = true; + } + else if( findAgain ) + { + /// Try starting over + strcpy( search, lastSearch ); + if( fEngine->FindPartialCmd( search, findAgain = false, true ) ) + { + strcpy( fWorkingLine, search ); + findAgain = true; + } + } + + else + { + // Not found the normal way; start an unrestricted search + if( fEngine->FindNestedPartialCmd( search, findCounter, true ) ) + { + strcpy( fWorkingLine, search ); + findCounter++; + } + } + + fWorkingCursor = strlen( fWorkingLine ); + IUpdateTooltip(); + } + } + else if( msg->GetKeyCode() == KEY_UP ) + { + i = ( fHistoryRecallCursor > 0 ) ? fHistoryRecallCursor - 1 : kNumHistoryItems - 1; + if( fHistory[ i ][ 0 ] != 0 ) + { + fHistoryRecallCursor = i; + strcpy( fWorkingLine, fHistory[ fHistoryRecallCursor ] ); + findAgain = false; + findCounter = 0; + fWorkingCursor = strlen( fWorkingLine ); + IUpdateTooltip(); + } + } + else if( msg->GetKeyCode() == KEY_DOWN ) + { + if( fHistoryRecallCursor != fHistoryCursor ) + { + i = ( fHistoryRecallCursor < kNumHistoryItems - 1 ) ? fHistoryRecallCursor + 1 : 0; + if( i != fHistoryCursor ) + { + fHistoryRecallCursor = i; + strcpy( fWorkingLine, fHistory[ fHistoryRecallCursor ] ); + } + else + { + memset( fWorkingLine, 0, sizeof( fWorkingLine ) ); + fHistoryRecallCursor = fHistoryCursor; + } + findAgain = false; + findCounter = 0; + fWorkingCursor = strlen( fWorkingLine ); + IUpdateTooltip(); + } + } + else if( msg->GetKeyCode() == KEY_LEFT ) + { + if( fWorkingCursor > 0 ) + fWorkingCursor--; + } + else if( msg->GetKeyCode() == KEY_RIGHT ) + { + if( fWorkingCursor < strlen( fWorkingLine ) ) + fWorkingCursor++; + } + else if( msg->GetKeyCode() == KEY_BACKSPACE ) + { + if( fWorkingCursor > 0 ) + { + fWorkingCursor--; + + c = &fWorkingLine[ fWorkingCursor ]; + do + { + *c = *( c + 1 ); + c++; + } while( *c != 0 ); + + findAgain = false; + findCounter = 0; + } + else if( fHelpMode ) + fHelpMode = false; + + IUpdateTooltip(); + } + else if( msg->GetKeyCode() == KEY_DELETE ) + { + c = &fWorkingLine[ fWorkingCursor ]; + do + { + *c = *( c + 1 ); + c++; + } while( *c != 0 ); + + findAgain = false; + findCounter = 0; + IUpdateTooltip(); + } + else if( msg->GetKeyCode() == KEY_ENTER ) + { + // leave leading space for Python multi lines (need the indents!) + if ( fPythonMultiLines == 0 ) + { + // Clean up working line by removing any leading whitespace + for( c = fWorkingLine; *c == ' ' || *c == '\t'; c++ ); + for( i = 0; *c != 0; i++, c++ ) + fWorkingLine[ i ] = *c; + fWorkingLine[ i ] = 0; + eol = i; + } + + if( fWorkingLine[ 0 ] == 0 && !fHelpMode && !fPythonMode ) + { + // Blank line--just print a blank line to the console and skip + IAddLine( "" ); + return; + } + + // only save history line if there is something there + if( fWorkingLine[ 0 ] != 0 ) + { + // Save to history + strcpy( fHistory[ fHistoryCursor ], fWorkingLine ); + fHistoryCursor = ( fHistoryCursor < kNumHistoryItems - 1 ) ? fHistoryCursor + 1 : 0; + fHistoryRecallCursor = fHistoryCursor; + } + + // EXECUTE!!! (warning: DESTROYS fWorkingLine) + if( fHelpMode ) + { + if( fWorkingLine[ 0 ] == 0 ) + IPrintSomeHelp(); + else if( stricmp( fWorkingLine, "commands" ) == 0 ) + fEngine->PrintCmdHelp( "", IAddLineCallback ); + else if( !fEngine->PrintCmdHelp( fWorkingLine, IAddLineCallback ) ) + { + c = (char *)fEngine->GetErrorMsg(); + AddLine( c ); + } + + fHelpMode = false; + } + // are we in Python mode? + else if ( fPythonMode ) + { + // are we in Python multi-line mode? + if ( fPythonMultiLines > 0 ) + { + // if there was a line then bump num lines + if ( fWorkingLine[0] != 0 ) + { + char displine[300]; + sprintf(displine,"... %s",fWorkingLine); + AddLine( displine ); + fPythonMultiLines++; + } + + // is it time to evaluate all the multi lines that are saved? + if ( fWorkingLine[0] == 0 || fPythonMultiLines >= kNumHistoryItems ) + { + if ( fPythonMultiLines >= kNumHistoryItems ) + AddLine("Python Multi-line buffer full!"); + // get the lines and stuff them in our buffer + char biglines[kNumHistoryItems*(kMaxCharsWide+1)]; + biglines[0] = 0; + for ( i=fPythonMultiLines; i>0 ; i--) + { + // reach back in the history and find this line and paste it in here + int recall = fHistoryCursor - i; + if ( recall < 0 ) + recall += kNumHistoryItems; + strcat(biglines,fHistory[ recall ]); + strcat(biglines,"\n"); + } + // now evaluate this mess they made + PyObject* mymod = PythonInterface::FindModule("__main__"); + PythonInterface::RunStringInteractive(biglines,mymod); + std::string output; + // get the messages + PythonInterface::getOutputAndReset(&output); + AddLine( output.c_str() ); + // all done doing multi lines... + fPythonMultiLines = 0; + } + } + // else we are just doing single lines + else + { + // was there actually anything in the input buffer? + if ( fWorkingLine[0] != 0 ) + { + char displine[300]; + sprintf(displine,">>> %s",fWorkingLine); + AddLine( displine ); + // check to see if this is going to be a multi line mode ( a ':' at the end) + if ( fWorkingLine[eol-1] == ':' ) + { + fPythonMultiLines = 1; + } + else + // else if not the start of a multi-line then execute it + { + PyObject* mymod = PythonInterface::FindModule("__main__"); + PythonInterface::RunStringInteractive(fWorkingLine,mymod); + std::string output; + // get the messages + PythonInterface::getOutputAndReset(&output); + AddLine( output.c_str() ); + } + } + else + AddLine( ">>> " ); + } + // find the end of the line + for( c = fWorkingLine, eol = 0; *c != 0; eol++, c++ ); + } + else + { + if( !fEngine->RunCommand( fWorkingLine, IAddLineCallback ) ) + { + c = (char *)fEngine->GetErrorMsg(); + if( c[ 0 ] != 0 ) + AddLine( c ); + } + } + + // Clear + fWorkingLine[ fWorkingCursor = 0 ] = 0; + findAgain = false; + findCounter = 0; + IUpdateTooltip(); + } + else if( msg->GetKeyCode() == KEY_END ) + { + fWorkingCursor = strlen( fWorkingLine ); + } + else if( msg->GetKeyCode() == KEY_HOME ) + { + fWorkingCursor = 0; + } + else + { + key = plKeyboardDevice::KeyEventToChar( msg ); + // do they want to go into help mode? + if( !fPythonMode && key == '?' && fWorkingCursor == 0 ) + { + /// Go into help mode + fHelpMode = true; + } + // do they want to go into Python mode? + else if( !fHelpMode && key == '\\' && fWorkingCursor == 0 ) + { + // toggle Python mode + fPythonMode = fPythonMode ? false:true; + if ( fPythonMode ) + { + if ( fPythonFirstTime ) + { + IAddLine( "" ); // add a blank line + PyObject* mymod = PythonInterface::FindModule("__main__"); + PythonInterface::RunStringInteractive("import sys;print 'Python',sys.version",mymod); + std::string output; + // get the messages + PythonInterface::getOutputAndReset(&output); + AddLine( output.c_str() ); + fPythonFirstTime = false; // do this only once! + } + } + } + // or are they just typing in a working line + else if( fWorkingCursor < kMaxCharsWide - 2 && key != 0 ) + { + for( i = strlen( fWorkingLine ) + 1; i > fWorkingCursor; i-- ) + fWorkingLine[ i ] = fWorkingLine[ i - 1 ]; + + fWorkingLine[ fWorkingCursor++ ] = key; + + findAgain = false; + findCounter = 0; + IUpdateTooltip(); + } + } +} + +//// IAddLineCallback //////////////////////////////////////////////////////// + +void pfConsole::IAddLineCallback( const char *string ) +{ + fTheConsole->IAddParagraph( string, 0 ); +} + +//// IAddLine //////////////////////////////////////////////////////////////// + +void pfConsole::IAddLine( const char *string, short leftMargin ) +{ + int i; + char *ptr; + + + /// Advance upward + for( i = 0, ptr = fDisplayBuffer; i < fNumDisplayLines - 1; i++ ) + { + memcpy( ptr, ptr + kMaxCharsWide, kMaxCharsWide ); + ptr += kMaxCharsWide; + } + + if( string[ 0 ] == '\t' ) + { + leftMargin += 4; + string++; + } + + memset( ptr, 0, kMaxCharsWide ); + memset( ptr, ' ', leftMargin ); + strncpy( ptr + leftMargin, string, kMaxCharsWide - leftMargin - 1 ); + if( fMode == 0 ) + { + /// Console is invisible, so show this line for a bit of time + fMsgTimeoutTimer = kMsgHintTimeout; + } +} + +//// IAddParagraph /////////////////////////////////////////////////////////// + +void pfConsole::IAddParagraph( const char *s, short margin ) +{ + char *ptr, *ptr2, *ptr3, *string=(char*)s; + + + // Special character: if \i is in front of the string, indent it + while( strncmp( string, "\\i", 2 ) == 0 ) + { + margin += 3; + string += 2; + } + + for( ptr = string; ptr != nil && *ptr != 0; ) + { + // Go as far as possible + if( strlen( ptr ) < kMaxCharsWide - margin - margin - 1 ) + ptr2 = ptr + strlen( ptr ); + else + { + // Back up until we hit a sep + ptr2 = ptr + kMaxCharsWide - margin - margin - 1; + for( ; ptr2 > string && *ptr2 != ' ' && *ptr2 != '\t' && *ptr2 != '\n'; ptr2-- ); + } + + // Check for carriage return + ptr3 = strchr( ptr, '\n' ); + if( ptr3 == ptr ) + { + IAddLine( "", margin ); + ptr++; + continue; + } + if( ptr3 != nil && ptr3 < ptr2 ) + ptr2 = ptr3; + + // Add this part + if( ptr2 == ptr || *ptr2 == 0 ) + { + IAddLine( ptr, margin ); + break; + } + + *ptr2 = 0; + IAddLine( ptr, margin ); + ptr = ptr2 + 1; + } +} + +//// IClear ////////////////////////////////////////////////////////////////// + +void pfConsole::IClear( void ) +{ + memset( fDisplayBuffer, 0, kMaxCharsWide * fNumDisplayLines ); +} + +//// Draw //////////////////////////////////////////////////////////////////// + +void pfConsole::Draw( plPipeline *p ) +{ + int i, yOff, y, x, eOffset, height; + char *line; + char tmp[ kMaxCharsWide ]; + hsBool showTooltip = false; + float thisTime; // For making the console FX speed konstant regardless of framerate + const float kEffectDuration = 0.5f; + + + plDebugText& drawText = plDebugText::Instance(); + + thisTime = (float)hsTimer::PrecTicksToSecs( hsTimer::GetPrecTickCount() ); + + if( fMode == kModeHidden && fEffectCounter == 0 ) + { + if( fMsgTimeoutTimer > 0 ) + { + /// Message hint--draw the last line of the console for a bit + drawText.DrawString( 10, 4, fDisplayBuffer + kMaxCharsWide * ( fNumDisplayLines - 1 ), fConsoleTextColor ); + fMsgTimeoutTimer--; + } + fLastTime = thisTime; + return; + } + + drawText.SetDrawOnTopMode( true ); + + yOff = drawText.GetFontHeight() + 2; + if( fMode == kModeSingleLine ) + height = yOff * 3 + 14; + else + height = yOff * ( fNumDisplayLines + 2 ) + 14; + + if( fHelpTimer == 0 && !fHelpMode && fLastHelpMsg[ 0 ] != 0 ) + showTooltip = true; + + if( fEffectCounter > 0 ) + { + int numElapsed = (int)( (float)kEffectDivisions * ( ( thisTime - fLastTime ) / (float)kEffectDuration ) ); + if( numElapsed > fEffectCounter ) + numElapsed = fEffectCounter; + else if( numElapsed < 0 ) + numElapsed = 0; + + if( fMode == kModeSingleLine ) + eOffset = fEffectCounter * height / kEffectDivisions; + else if( fMode == kModeFull ) + eOffset = fEffectCounter * ( height - yOff * 3 - 14 ) / kEffectDivisions; + else + eOffset = ( kEffectDivisions - fEffectCounter ) * ( height - yOff * 3 - 14 ) / kEffectDivisions; + fEffectCounter -= numElapsed; + } + else + eOffset = 0; + fLastTime = thisTime; + + if( fMode == kModeSingleLine ) + { + // Bgnd (TEMP ONLY) + x = kMaxCharsWide * drawText.CalcStringWidth( "W" ) + 4; + y = height - eOffset; + drawText.DrawRect( 4, 0, x, y, /*color*/0, 0, 0, 127 ); + + /// Actual text + if( fEffectCounter == 0 ) + drawText.DrawString( 10, 4, "Plasma 2.0 Console", 255, 255, 255, 255 ); + + if( !showTooltip ) + drawText.DrawString( 10, 4 + yOff - eOffset, fDisplayBuffer + kMaxCharsWide * ( fNumDisplayLines - 1 ), fConsoleTextColor ); + + y = 4 + yOff + yOff - eOffset; + } + else + { + // Bgnd (TEMP ONLY) + x = kMaxCharsWide * drawText.CalcStringWidth( "W" ) + 4; + y = yOff * ( fNumDisplayLines + 2 ) + 14 - eOffset; + drawText.DrawRect( 4, 0, x, y, /*color*/0, 0, 0, 127 ); + + /// Actual text + drawText.DrawString( 10, 4, "Plasma 2.0 Console", 255, 255, 255, 255 ); + + static int countDown = 3000; + if( fHelpTimer > 0 || fEffectCounter > 0 || fMode != kModeFull ) + countDown = 3000; + else if( countDown > -720 ) + countDown--; + + // Resource data is encrypted so testers can't peer in to the EXE, plz don't decrypt + static bool rezLoaded = false; + static char tmpSrc[ kMaxCharsWide ]; + if( !rezLoaded ) + { + memset( tmp, 0, sizeof( tmp ) ); + memset( tmpSrc, 0, sizeof( tmpSrc ) ); + // Our concession to windows +#ifdef HS_BUILD_FOR_WIN32 + #include "../../Apps/plClient/res/resource.h" + HRSRC rsrc = FindResource( nil, MAKEINTRESOURCE( IDR_CNSL1 ), "CNSL" ); + if( rsrc != nil ) + { + HGLOBAL hdl = LoadResource( nil, rsrc ); + if( hdl != nil ) + { + UInt8 *ptr = (UInt8 *)LockResource( hdl ); + if( ptr != nil ) + { + for( i = 0; i < SizeofResource( nil, rsrc ); i++ ) + tmpSrc[ i ] = ptr[ i ] + 26; + UnlockResource( hdl ); + } + } + } + rezLoaded = true; +#else + // Need to define for other platforms? +#endif + } + memcpy( tmp, tmpSrc, sizeof( tmp ) ); + + if( countDown <= 0 ) + { + y = 4 + yOff - eOffset; + if( countDown <= -480 ) + { + tmp[ ( (-countDown - 480)>> 4 ) + 1 ] = 0; + drawText.DrawString( 10, y, tmp, fConsoleTextColor ); + } + y += yOff * ( fNumDisplayLines - ( showTooltip ? 1 : 0 ) ); + } + else + { + for( i = 0, y = 4 + yOff - eOffset, line = fDisplayBuffer; i < fNumDisplayLines - ( showTooltip ? 1 : 0 ); i++ ) + { + drawText.DrawString( 10, y, line, fConsoleTextColor ); + y += yOff; + line += kMaxCharsWide; + } + } + + if( showTooltip ) + y += yOff; + } + +// strcpy( tmp, fHelpMode ? "Get Help On:" : "]" ); + if ( fHelpMode ) + strcpy( tmp, "Get Help On:"); + else if (fPythonMode ) + if ( fPythonMultiLines == 0 ) + strcpy( tmp, ">>>"); + else + strcpy( tmp, "..."); + else + strcpy( tmp, "]" ); + + drawText.DrawString( 10, y, tmp, 255, 255, 255, 255 ); + i = 10 + drawText.CalcStringWidth( tmp ) + 4; + drawText.DrawString( i, y, fWorkingLine, fConsoleTextColor ); + + if( fCursorTicks >= 0 ) + { + strcpy( tmp, fWorkingLine ); + tmp[ fWorkingCursor ] = 0; + x = drawText.CalcStringWidth( tmp ); + drawText.DrawString( i + x, y + 2, "_", 255, 255, 255 ); + } + fCursorTicks--; + if( fCursorTicks < -kCursorBlinkRate ) + fCursorTicks = kCursorBlinkRate; + + if( showTooltip ) + drawText.DrawString( i, y - yOff, fLastHelpMsg, 255, 255, 0 ); + else + fHelpTimer--; + + drawText.SetDrawOnTopMode( false ); +} + +//// IUpdateTooltip ////////////////////////////////////////////////////////// + +void pfConsole::IUpdateTooltip( void ) +{ + char tmpStr[ kWorkingLineSize ]; + char *c; + + + strcpy( tmpStr, fWorkingLine ); + c = (char *)fEngine->GetCmdSignature( tmpStr ); + if( c == nil || strcmp( c, fLastHelpMsg ) != 0 ) + { + /// Different--update timer to wait + fHelpTimer = kHelpDelay; + strncpy( fLastHelpMsg, c ? c : "", kMaxCharsWide - 2 ); + } +} + +//// IPrintSomeHelp ////////////////////////////////////////////////////////// + +void pfConsole::IPrintSomeHelp( void ) +{ + char msg1[] = "The console contains commands arranged under groups and subgroups. \ +To use a command, you type the group name plus the command, such as 'Console.Clear' or \ +'Console Clear'."; + + char msg2[] = "To get help on a command or group, type '?' followed by the command or \ +group name. Typing '?' and just hitting enter will bring up this message. Typing '?' and \ +then 'commands' will bring up a list of all base groups and commands."; + + char msg3[] = "You can also have the console auto-complete a command by pressing tab. \ +This will search for a group or command that starts with what you have typed. If there is more \ +than one match, pressing tab repeatedly will cycle through all the matches."; + + + AddLine( "" ); + AddLine( "How to use the console:" ); + IAddParagraph( msg1, 2 ); + AddLine( "" ); + IAddParagraph( msg2, 2 ); + AddLine( "" ); + IAddParagraph( msg3, 2 ); + AddLine( "" ); +} + + +//============================================================================ +void pfConsole::AddLineF(const char * fmt, ...) { + char str[1024]; + va_list args; + va_start(args, fmt); + _vsnprintf(str, arrsize(str), fmt, args); + va_end(args); + AddLine(str); +} + +//============================================================================ +void pfConsole::RunCommandAsync (const char cmd[]) { + + plConsoleMsg * consoleMsg = NEWZERO(plConsoleMsg); + consoleMsg->SetCmd(plConsoleMsg::kExecuteLine); + consoleMsg->SetString(cmd); +// consoleMsg->SetBreakBeforeDispatch(true); + consoleMsg->Send(nil, true); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsole.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsole.h new file mode 100644 index 00000000..9582576b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsole.h @@ -0,0 +1,149 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfConsole Header // +// // +// 9.21.2001 mcn - Added pfLogDisplays. These are classes that represent // +// a scrolling buffer, much like the console, for on- // +// screen display of error logs and the such. Currently // +// managed by the console, but hopefully once we have // +// a general manager for dialogs/menus (debug or // +// otherwise), that manager will take the displays over. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfConsole_h +#define _pfConsole_h + +#include "hsTypes.h" +#include "../pnKeyedObject/hsKeyedObject.h" + + +class plPipeline; + +//// Class Definition //////////////////////////////////////////////////////// + +class pfConsoleEngine; +class plKeyEventMsg; +class pfConsoleInputInterface; + +class pfConsole : public hsKeyedObject +{ + friend class pfConsoleInputInterface; + + protected: + + enum Konstants + { + kNumHistoryItems = 16, + kModeHidden = 0, + kModeSingleLine = 1, + kModeFull = 2, + kEffectDivisions = 1000, + kMaxCharsWide = 80, + kHelpDelay = 32, + kCursorBlinkRate = 16, + kMsgHintTimeout = 64, + kWorkingLineSize = 256 + }; + + + UInt32 fNumDisplayLines; + + Int32 fEffectCounter; + float fLastTime; + UInt32 fHelpTimer; + char fLastHelpMsg[ kWorkingLineSize ]; + UInt8 fMode; // 0 - invisible, 1 - single line, 2 - full + hsBool fInited, fHelpMode, fPythonMode, fPythonFirstTime, fFXEnabled; + UInt32 fPythonMultiLines; + short fCursorTicks; + UInt32 fMsgTimeoutTimer; + + char fHistory[ kNumHistoryItems ][ kMaxCharsWide ]; + UInt32 fHistoryCursor, fHistoryRecallCursor; + char *fDisplayBuffer; + char fWorkingLine[ kWorkingLineSize ]; + UInt32 fWorkingCursor; + + pfConsoleInputInterface *fInputInterface; + + pfConsoleEngine *fEngine; + + void IHandleKey( plKeyEventMsg *msg ); + char IKeyEventToChar( plKeyEventMsg *msg ); + + static UInt32 fConsoleTextColor; + static pfConsole *fTheConsole; + static void _cdecl IAddLineCallback( const char *string ); + + static plPipeline *fPipeline; + + void IAddLine( const char *string, short leftMargin = 0 ); + void IAddParagraph( const char *string, short margin = 0 ); + void IClear( void ); + + void ISetMode( UInt8 mode ); + void IEnableFX( hsBool e ) { fFXEnabled = e; } + hsBool IFXEnabled( void ) { return fFXEnabled; } + + void IPrintSomeHelp( void ); + void IUpdateTooltip( void ); + + public: + + pfConsole(); + ~pfConsole(); + + CLASSNAME_REGISTER( pfConsole ); + GETINTERFACE_ANY( pfConsole, plReceiver ); + + static pfConsole * GetInstance (); + + virtual hsBool MsgReceive( plMessage *msg ); + + void Init( pfConsoleEngine *engine ); + void Draw( plPipeline *p ); + + static void AddLine( const char *string ) { fTheConsole->IAddParagraph( string ); } + static void AddLineF(const char * fmt, ...); + static void Clear( void ) { fTheConsole->IClear(); } + static void Hide( void ) { fTheConsole->ISetMode(kModeHidden); } + + static void EnableEffects( hsBool enable ) { fTheConsole->IEnableFX( enable ); } + static hsBool AreEffectsEnabled( void ) { return fTheConsole->IFXEnabled(); } + static void SetTextColor( UInt32 color ) { fConsoleTextColor = color; } + static UInt32 GetTextColor() { return fConsoleTextColor; } + + static void SetPipeline( plPipeline *pipe ) { fPipeline = pipe; } + static plPipeline *GetPipeline( void ) { return fPipeline; } + + static void RunCommandAsync (const char cmd[]); +}; + +#endif //_pfConsole_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCmd.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCmd.cpp new file mode 100644 index 00000000..1e07cacb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCmd.cpp @@ -0,0 +1,657 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfConsoleCmd Functions // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "pfConsoleCmd.h" +#include "hsUtils.h" + + +////////////////////////////////////////////////////////////////////////////// +//// pfConsoleCmdGroup Stuff ///////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +pfConsoleCmdGroup *pfConsoleCmdGroup::fBaseCmdGroup = nil; +UInt32 pfConsoleCmdGroup::fBaseCmdGroupRef = 0; + + +//// Constructor & Destructor //////////////////////////////////////////////// + +pfConsoleCmdGroup::pfConsoleCmdGroup( char *name, char *parent ) +{ + Dummy(); + DummyJunior(); + DummyNet(); + DummyAvatar(); + DummyCCR(); + + fNext = nil; + fPrevPtr = nil; + fCommands = nil; + fSubGroups = nil; + + if( name == nil ) + { + /// Create base + hsStrncpy( fName, "base", sizeof( fName ) ); + fParentGroup = nil; + } + else + { + pfConsoleCmdGroup *group = GetBaseGroup(); + + if( parent != nil && parent[ 0 ] != 0 ) + { + group = group->FindSubGroupRecurse( parent ); + hsAssert( group != nil, "Trying to register group under nonexistant group!" ); + } + + hsStrncpy( fName, name, sizeof( fName ) ); + group->AddSubGroup( this ); + fParentGroup = group; + } + +} + +pfConsoleCmdGroup::~pfConsoleCmdGroup() +{ + if( this != fBaseCmdGroup ) + { + Unlink(); + + DecBaseCmdGroupRef(); + } +} + +//// GetBaseGroup //////////////////////////////////////////////////////////// + +pfConsoleCmdGroup *pfConsoleCmdGroup::GetBaseGroup( void ) +{ + if( fBaseCmdGroup == nil ) + { + /// Initialize base group + fBaseCmdGroup = TRACKED_NEW pfConsoleCmdGroup( nil, nil ); + } + + return fBaseCmdGroup; +} + +//// DecBaseCmdGroupRef ////////////////////////////////////////////////////// + +void pfConsoleCmdGroup::DecBaseCmdGroupRef( void ) +{ + fBaseCmdGroupRef--; + if( fBaseCmdGroupRef == 0 ) + { + delete fBaseCmdGroup; + fBaseCmdGroup = nil; + } +} + +//// Add Functions /////////////////////////////////////////////////////////// + +void pfConsoleCmdGroup::AddCommand( pfConsoleCmd *cmd ) +{ + cmd->Link( &fCommands ); + fBaseCmdGroupRef++; +} + +void pfConsoleCmdGroup::AddSubGroup( pfConsoleCmdGroup *group ) +{ + group->Link( &fSubGroups ); + fBaseCmdGroupRef++; +} + +//// FindCommand ///////////////////////////////////////////////////////////// +// No longer recursive. + +pfConsoleCmd *pfConsoleCmdGroup::FindCommand( char *name ) +{ + pfConsoleCmd *cmd; + + + hsAssert( name != nil, "nil name passed to FindCommand()" ); + + /// Only search locally + for( cmd = fCommands; cmd != nil; cmd = cmd->GetNext() ) + { + if( strcmp( cmd->GetName(), name ) == 0 ) + return cmd; + } + + return nil; +} + +//// FindNestedPartialCommand //////////////////////////////////////////////// +// Okay. YAFF. This one searches through the group and its children looking +// for a partial command based on the string given. The counter determines +// how many matches it skips before returning a match. (That way you can +// cycle through matches by sending 1 + the last counter every time). + +pfConsoleCmd *pfConsoleCmdGroup::FindNestedPartialCommand( char *name, UInt32 *counter ) +{ + pfConsoleCmd *cmd; + pfConsoleCmdGroup *group; + + + hsAssert( name != nil, "nil name passed to FindNestedPartialCommand()" ); + hsAssert( counter != nil, "nil counter passed to FindNestedPartialCommand()" ); + + // Try us + for( cmd = fCommands; cmd != nil; cmd = cmd->GetNext() ) + { + if( _strnicmp( cmd->GetName(), name, strlen( name ) ) == 0 ) + { + if( *counter == 0 ) + return cmd; + + (*counter)--; + } + } + + // Try children + for( group = fSubGroups; group != nil; group = group->GetNext() ) + { + cmd = group->FindNestedPartialCommand( name, counter ); + if( cmd != nil ) + return cmd; + } + + return nil; +} + +//// FindSubGroup //////////////////////////////////////////////////////////// + +pfConsoleCmdGroup *pfConsoleCmdGroup::FindSubGroup( char *name ) +{ + pfConsoleCmdGroup *group; + + + hsAssert( name != nil, "nil name passed to FindSubGroup()" ); + + /// Only search locally + for( group = fSubGroups; group != nil; group = group->GetNext() ) + { + if( strcmp( group->GetName(), name ) == 0 ) + return group; + } + + return nil; +} + +//// FindSubGroupRecurse ///////////////////////////////////////////////////// +// Resurces through a string, finding the final subgroup that the string +// represents. Parses with spaces, _ or . as the separators. Copies string. + +pfConsoleCmdGroup *pfConsoleCmdGroup::FindSubGroupRecurse( const char *name ) +{ + char *ptr, *string; + pfConsoleCmdGroup *group; + static char seps[] = " ._"; + + + string = TRACKED_NEW char[ strlen( name ) + 1 ]; + hsAssert( string != nil, "Cannot allocate string in FindSubGroupRecurse()" ); + strcpy( string, name ); + + /// Scan for subgroups + group = pfConsoleCmdGroup::GetBaseGroup(); + ptr = strtok( string, seps ); + while( ptr != nil ) + { + // Take this token and check to see if it's a group + group = group->FindSubGroup( ptr ); + hsAssert( group != nil, "Invalid group name to FindSubGroupRecurse()" ); + + ptr = strtok( nil, seps ); + } + + delete [] string; + return group; +} + +//// FindCommandNoCase /////////////////////////////////////////////////////// +// Case-insensitive version of FindCommand. + +pfConsoleCmd *pfConsoleCmdGroup::FindCommandNoCase( char *name, UInt8 flags, pfConsoleCmd *start ) +{ + pfConsoleCmd *cmd; + + + hsAssert( name != nil, "nil name passed to FindCommandNoCase()" ); + + /// Only search locally + if( start == nil ) + start = fCommands; + else + start = start->GetNext(); + + if( flags & kFindPartial ) + { + for( cmd = start; cmd != nil; cmd = cmd->GetNext() ) + { + if( _strnicmp( cmd->GetName(), name, strlen( name ) ) == 0 ) + return cmd; + } + } + else + { + for( cmd = start; cmd != nil; cmd = cmd->GetNext() ) + { + if( stricmp( cmd->GetName(), name ) == 0 ) + return cmd; + } + } + + return nil; +} + +//// FindSubGroupNoCase ////////////////////////////////////////////////////// + +pfConsoleCmdGroup *pfConsoleCmdGroup::FindSubGroupNoCase( char *name, UInt8 flags, pfConsoleCmdGroup *start ) +{ + pfConsoleCmdGroup *group; + + + hsAssert( name != nil, "nil name passed to FindSubGroupNoCase()" ); + + /// Only search locally + if( start == nil ) + start = fSubGroups; + else + start = start->GetNext(); + + if( flags & kFindPartial ) + { + for( group = start; group != nil; group = group->GetNext() ) + { + if( _strnicmp( group->GetName(), name, strlen( name ) ) == 0 ) + return group; + } + } + else + { + for( group = start; group != nil; group = group->GetNext() ) + { + if( stricmp( group->GetName(), name ) == 0 ) + return group; + } + } + + return nil; +} + +//// Link & Unlink /////////////////////////////////////////////////////////// + +void pfConsoleCmdGroup::Link( pfConsoleCmdGroup **prevPtr ) +{ + hsAssert( fNext == nil && fPrevPtr == nil, "Trying to link console group that's already linked!" ); + + fNext = *prevPtr; + if( *prevPtr ) + (*prevPtr)->fPrevPtr = &fNext; + fPrevPtr = prevPtr; + *fPrevPtr = this; +} + +void pfConsoleCmdGroup::Unlink( void ) +{ + hsAssert( fNext != nil || fPrevPtr != nil, "Trying to unlink console group that isn't linked!" ); + + if( fNext ) + fNext->fPrevPtr = fPrevPtr; + *fPrevPtr = fNext; +} + + +int pfConsoleCmdGroup::IterateCommands(pfConsoleCmdIterator* t, int depth) +{ + pfConsoleCmd *cmd; + + cmd = this->GetFirstCommand(); + while(cmd) + { + t->ProcessCmd(cmd,depth); + cmd = cmd->GetNext(); + } + + pfConsoleCmdGroup *grp; + + grp = this->GetFirstSubGroup(); + while(grp) + { + if(t->ProcessGroup(grp, depth)) + grp->IterateCommands(t, depth+1); + grp = grp->GetNext(); + } + return 0; +} + + + +////////////////////////////////////////////////////////////////////////////// +//// pfConsoleCmd Functions ////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +char pfConsoleCmd::fSigTypes[ kNumTypes ][ 8 ] = { "int", "float", "bool", "string", "char", "void", "..." }; + +pfConsoleCmd::pfConsoleCmd( char *group, char *name, char *paramList, char *help, + pfConsoleCmdPtr func, hsBool localOnly ) +{ + fNext = nil; + fPrevPtr = nil; + + fFunction = func; + fLocalOnly = localOnly; + + hsStrncpy( fName, name, sizeof( fName ) ); + fHelpString = help; + + ICreateSignature( paramList ); + Register( group, name ); +} + +pfConsoleCmd::~pfConsoleCmd() +{ + int i; + + + for( i = 0; i < fSigLabels.GetCount(); i++ ) + { + if( fSigLabels[ i ] != nil ) + delete [] fSigLabels[ i ]; + } + Unregister(); + + fSignature.Reset(); + fSigLabels.Reset(); +} + +//// ICreateSignature //////////////////////////////////////////////////////// +// Creates the signature and sig labels based on the given string. + +void pfConsoleCmd::ICreateSignature( char *paramList ) +{ + static char seps[] = " :-"; + + char params[ 256 ]; + char *ptr, *nextPtr, *tok, *tok2; + int i; + + + /// Simple check + if( paramList == nil ) + { + fSignature.Push( kAny ); + fSigLabels.Push( (char *)nil ); + return; + } + + /// So we can do stuff to it + hsAssert( strlen( paramList ) < sizeof( params ), "Make the (#*$& params string larger!" ); + hsStrcpy( params, paramList ); + + fSignature.Empty(); + fSigLabels.Empty(); + + /// Loop through all the types given in the list + ptr = params; + do + { + /// Find break + nextPtr = strchr( ptr, ',' ); + if( nextPtr != nil ) + { + *nextPtr = 0; + nextPtr++; + } + + /// Do this param + tok = strtok( ptr, seps ); + if( tok == nil && ptr == params ) + break; + + hsAssert( tok != nil, "Bad parameter list for console command!" ); + tok2 = strtok( nil, seps ); + + if( tok2 != nil ) + { + // Type and label: assume label second + fSigLabels.Push( hsStrcpy( tok2 ) ); + } + else + fSigLabels.Push( (char *)nil ); + + // Find type + for( i = 0; i < kNumTypes; i++ ) + { + if( strcmp( fSigTypes[ i ], tok ) == 0 ) + { + fSignature.Push( (UInt8)i ); + break; + } + } + + hsAssert( i < kNumTypes, "Bad parameter type in console command parameter list!" ); + + } while( ( ptr = nextPtr ) != nil ); +} + +//// Register //////////////////////////////////////////////////////////////// +// Finds the group this command should be in and registers it with that +// group. + +void pfConsoleCmd::Register( char *group, char *name ) +{ + pfConsoleCmdGroup *g; + + + if( group == nil || group[ 0 ] == 0 ) + { + g = pfConsoleCmdGroup::GetBaseGroup(); + g->AddCommand( this ); + } + else + { + g = pfConsoleCmdGroup::FindSubGroupRecurse( group ); + hsAssert( g != nil, "Trying to register command under nonexistant group!" ); + g->AddCommand( this ); + } + + fParentGroup = g; +} + +//// Unregister ////////////////////////////////////////////////////////////// + +void pfConsoleCmd::Unregister( void ) +{ + Unlink(); + pfConsoleCmdGroup::DecBaseCmdGroupRef(); +} + +//// Execute ///////////////////////////////////////////////////////////////// +// Run da thing! + +void pfConsoleCmd::Execute( Int32 numParams, pfConsoleCmdParam *params, void (*PrintFn)( const char * ) ) +{ + fFunction( numParams, params, PrintFn ); +} + +//// Link & Unlink /////////////////////////////////////////////////////////// + +void pfConsoleCmd::Link( pfConsoleCmd **prevPtr ) +{ + hsAssert( fNext == nil && fPrevPtr == nil, "Trying to link console command that's already linked!" ); + + fNext = *prevPtr; + if( *prevPtr ) + (*prevPtr)->fPrevPtr = &fNext; + fPrevPtr = prevPtr; + *fPrevPtr = this; +} + +void pfConsoleCmd::Unlink( void ) +{ + hsAssert( fNext != nil || fPrevPtr != nil, "Trying to unlink console command that isn't linked!" ); + + if( fNext ) + fNext->fPrevPtr = fPrevPtr; + *fPrevPtr = fNext; +} + +//// GetSigEntry ///////////////////////////////////////////////////////////// + +UInt8 pfConsoleCmd::GetSigEntry( UInt8 i ) +{ + if( fSignature.GetCount() == 0 ) + return kNone; + + if( i < fSignature.GetCount() ) + { + if( fSignature[ i ] == kEtc ) + return kAny; + + return fSignature[ i ]; + } + + if( fSignature[ fSignature.GetCount() - 1 ] == kEtc ) + return kAny; + + return kNone; +} + +//// GetSignature //////////////////////////////////////////////////////////// +// Gets the signature of the command as a string. Format is: +// name [ type param [, type param ... ] ] +// WARNING: uses a static buffer, so don't rely on the contents if you call +// it more than once! (You shouldn't need to, though) + +const char *pfConsoleCmd::GetSignature( void ) +{ + static char string[ 256 ]; + + int i; + char pStr[ 128 ]; + + + strcpy( string, fName ); + for( i = 0; i < fSignature.GetCount(); i++ ) + { + if( fSigLabels[ i ] == nil ) + sprintf( pStr, "%s", fSigTypes[ fSignature[ i ] ] ); + else + sprintf( pStr, "%s %s", fSigTypes[ fSignature[ i ] ], fSigLabels[ i ] ); + + hsAssert( strlen( string ) + strlen( pStr ) + 2 < sizeof( string ), "Not enough room for signature string" ); + strcat( string, ( i > 0 ) ? ", " : " " ); + + strcat( string, pStr ); + } + + return string; +} + + +////////////////////////////////////////////////////////////////////////////// +//// pfConsoleCmdParam Functions ///////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Conversion Functions //////////////////////////////////////////////////// + +const int & pfConsoleCmdParam::IToInt( void ) const +{ + hsAssert( fType == kInt || fType == kAny, "Trying to use a non-int parameter as an int!" ); + + static int i; + if( fType == kAny ) + { + hsAssert( fValue.s != nil, "Weird parameter during conversion" ); + i = atoi( fValue.s ); + return i; + } + + return fValue.i; +} + +const float & pfConsoleCmdParam::IToFloat( void ) const +{ + hsAssert( fType == kFloat || fType == kAny, "Trying to use a non-float parameter as a float!" ); + + static float f; + if( fType == kAny ) + { + hsAssert( fValue.s != nil, "Weird parameter during conversion" ); + f = (float)atof( fValue.s ); + return f; + } + + return fValue.f; +} + +const bool & pfConsoleCmdParam::IToBool( void ) const +{ + hsAssert( fType == kBool || fType == kAny, "Trying to use a non-bool parameter as a bool!" ); + + static bool b; + if( fType == kAny ) + { + hsAssert( fValue.s != nil, "Weird parameter during conversion" ); + if( atoi( fValue.s ) > 0 || stricmp( fValue.s, "true" ) == 0 ) + b = true; + else + b = false; + + return b; + } + + return fValue.b; +} + +const pfConsoleCmdParam::CharPtr & pfConsoleCmdParam::IToString( void ) const +{ + hsAssert( fType == kString || fType == kAny, "Trying to use a non-string parameter as a string!" ); + + return fValue.s; +} + +const char & pfConsoleCmdParam::IToChar( void ) const +{ + hsAssert( fType == kChar || fType == kAny, "Trying to use a non-char parameter as a char!" ); + + static char c; + if( fType == kAny ) + { + hsAssert( fValue.s != nil, "Weird parameter during conversion" ); + c = fValue.s[ 0 ]; + return c; + } + + return fValue.c; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCmd.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCmd.h new file mode 100644 index 00000000..e50d141f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCmd.h @@ -0,0 +1,272 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfConsoleCmd Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfConsoleCmd_h +#define _pfConsoleCmd_h + +#include "hsTypes.h" +#include "hsBiExpander.h" + + +//// pfConsoleCmdGroup Class Definition ////////////////////////////////////// + +class pfConsoleCmd; +class pfConsoleCmdIterator; + + +class pfConsoleCmdGroup +{ + protected: + + static pfConsoleCmdGroup *fBaseCmdGroup; + static UInt32 fBaseCmdGroupRef; + + char fName[ 128 ]; + + pfConsoleCmdGroup *fNext; + pfConsoleCmdGroup **fPrevPtr; + + pfConsoleCmdGroup *fSubGroups; + pfConsoleCmd *fCommands; + + pfConsoleCmdGroup *fParentGroup; + + public: + + enum FindFlags { + kFindPartial = 0x01 + }; + + pfConsoleCmdGroup( char *name, char *parent ); + ~pfConsoleCmdGroup(); + + void AddCommand( pfConsoleCmd *cmd ); + void AddSubGroup( pfConsoleCmdGroup *group ); + + void Link( pfConsoleCmdGroup **prevPtr ); + void Unlink( void ); + + pfConsoleCmdGroup *GetNext( void ) { return fNext; } + char *GetName( void ) { return fName; } + pfConsoleCmdGroup *GetParent( void ) { return fParentGroup; } + + static pfConsoleCmdGroup *GetBaseGroup( void ); + + pfConsoleCmd *FindCommand( char *name ); + pfConsoleCmd *FindCommandNoCase( char *name, UInt8 flags = 0, pfConsoleCmd *start = nil ); + pfConsoleCmd *FindNestedPartialCommand( char *name, UInt32 *counter ); + + pfConsoleCmdGroup *FindSubGroup( char *name ); + pfConsoleCmdGroup *FindSubGroupNoCase( char *name, UInt8 flags = 0, pfConsoleCmdGroup *start = nil ); + + pfConsoleCmd *GetFirstCommand( void ) { return fCommands; } + pfConsoleCmdGroup *GetFirstSubGroup( void ) { return fSubGroups; } + + int IterateCommands(pfConsoleCmdIterator*, int depth=0); + + static pfConsoleCmdGroup *FindSubGroupRecurse( const char *name ); + static void DecBaseCmdGroupRef( void ); + + static void Dummy( void ); + static void DummyJunior( void ); + static void DummyNet( void ); + static void DummyAvatar( void ); + static void DummyCCR( void ); +}; + +//// pfConsoleCmdParam Class Definition ////////////////////////////////////// + +class pfConsoleCmdParam +{ + protected: + + UInt8 fType; + + typedef char *CharPtr; + + union + { + int i; + float f; + bool b; + CharPtr s; + char c; + } fValue; + + const int &IToInt( void ) const; + const float &IToFloat( void ) const; + const bool &IToBool( void ) const; + const CharPtr &IToString( void ) const; + const char &IToChar( void ) const; + + public: + + enum Types + { + kInt = 0, + kFloat, + kBool, + kString, + kChar, + kAny, + kNone = 0xff + }; + + operator int() const { return IToInt(); } + operator float() const { return IToFloat(); } + operator bool() const { return IToBool(); } + operator const CharPtr() const { return IToString(); } + operator char() const { return IToChar(); } + + UInt8 GetType( void ) { return fType; } + + void SetInt( int i ) { fValue.i = i; fType = kInt; } + void SetFloat( float f ) { fValue.f = f; fType = kFloat; } + void SetBool( bool b ) { fValue.b = b; fType = kBool; } + void SetString( CharPtr s ) { fValue.s = s; fType = kString; } + void SetChar( char c ) { fValue.c = c; fType = kChar; } + void SetAny( CharPtr s ) { fValue.s = s; fType = kAny; } + void SetNone( void ) { fType = kNone; } +}; + +//// pfConsoleCmd Class Definition /////////////////////////////////////////// + +typedef void (*pfConsoleCmdPtr)( Int32 numParams, pfConsoleCmdParam *params, void (*PrintString)( const char * ) ); + +class pfConsoleCmd +{ + protected: + char fName[ 128 ]; + char *fHelpString; + + pfConsoleCmdPtr fFunction; + hsBool fLocalOnly; + + pfConsoleCmd *fNext; + pfConsoleCmd **fPrevPtr; + + pfConsoleCmdGroup *fParentGroup; + + hsExpander fSignature; + hsExpander fSigLabels; + + void ICreateSignature( char *paramList ); + + public: + + enum ParamTypes + { + kInt = 0, + kFloat, + kBool, + kString, + kChar, + kAny, + kEtc, + kNumTypes, + kNone = 0xff + }; + + static char fSigTypes[ kNumTypes ][ 8 ]; + + + pfConsoleCmd( char *group, char *name, char *paramList, char *help, pfConsoleCmdPtr func, hsBool localOnly = false ); + ~pfConsoleCmd(); + + void Register( char *group, char *name ); + void Unregister(); + void Execute( Int32 numParams, pfConsoleCmdParam *params, void (*PrintFn)( const char * ) = nil ); + + void Link( pfConsoleCmd **prevPtr ); + void Unlink( void ); + + pfConsoleCmd *GetNext( void ) { return fNext; } + char *GetName( void ) { return fName; } + char *GetHelp( void ) { return fHelpString; } + const char *GetSignature( void ); + + pfConsoleCmdGroup *GetParent( void ) { return fParentGroup; } + + UInt8 GetSigEntry( UInt8 i ); +}; + + + +class pfConsoleCmdIterator +{ +public: + virtual void ProcessCmd(pfConsoleCmd*, int ) {} + virtual bool ProcessGroup(pfConsoleCmdGroup *, int) {return true;} +}; + + +//// pfConsoleCmd Creation Macro ///////////////////////////////////////////// +// +// This expands into 3 things: +// - A prototype for the function. +// - A declaration of a pfConsoleCmd object, which takes in that function +// as a parameter +// - The start of the function itself, so that the {} after the macro +// define the body of that function. +// +// PF_LOCAL_CONSOLE_CMD is identical, only it passes true for the localOnly flag. +// This isn't used currently and is here only for legacy. + +// PF_CONSOLE_BASE_CMD doesn't belong to a group; it creates a global console function. + + +#define PF_CONSOLE_BASE_CMD( name, p, help ) \ + void pfConsoleCmd_##name##_proc( Int32 numParams, pfConsoleCmdParam *params, void (*PrintString)( const char * ) ); \ + pfConsoleCmd conCmd_##name( nil, #name, p, help, pfConsoleCmd_##name##_proc ); \ + void pfConsoleCmd_##name##_proc( Int32 numParams, pfConsoleCmdParam *params, void (*PrintString)( const char * ) ) + +#define PF_CONSOLE_CMD( grp, name, p, help ) \ + void pfConsoleCmd_##grp##_##name##_proc( Int32 numParams, pfConsoleCmdParam *params, void (*PrintString)( const char * ) ); \ + pfConsoleCmd conCmd_##grp##_##name( #grp, #name, p, help, pfConsoleCmd_##grp##_##name##_proc ); \ + void pfConsoleCmd_##grp##_##name##_proc( Int32 numParams, pfConsoleCmdParam *params, void (*PrintString)( const char * ) ) + +#define PF_LOCAL_CONSOLE_CMD( grp, name, p, help ) \ + void pfConsoleCmd_##grp##_##name##_proc( Int32 numParams, pfConsoleCmdParam *params, void (*PrintString)( const char * ) ); \ + pfConsoleCmd conCmd_##grp##_##name( #grp, #name, p, help, pfConsoleCmd_##grp##_##name##_proc, true ); \ + void pfConsoleCmd_##grp##_##name##_proc( Int32 numParams, pfConsoleCmdParam *params, void (*PrintString)( const char * ) ) + +//// pfConsoleCmdGroup Creation Macro //////////////////////////////////////// + +#define PF_CONSOLE_GROUP( name ) \ + pfConsoleCmdGroup conGroup_##name( #name, nil ); + +#define PF_CONSOLE_SUBGROUP( parent, name ) \ + pfConsoleCmdGroup conGroup_##parent##_##name( #name, #parent ); + + + +#endif //_pfConsoleCmd_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp new file mode 100644 index 00000000..63607d7c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp @@ -0,0 +1,7165 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// Actual Console Commands and Groups // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifdef PLASMA_EXTERNAL_RELEASE +#define LIMIT_CONSOLE_COMMANDS 1 +#endif + + +#include "pfConsoleCmd.h" +#include "plgDispatch.h" + +#include "../plAgeLoader/plAgeLoader.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plPipeline/plDebugText.h" +#include "../plPipeline/plPipeDebugFlags.h" +#include "../plMessage/plMovieMsg.h" +#include "../plDrawable/plDrawableSpans.h" +#include "plPipeline.h" +#include "../pfCamera/plCameraModifier.h" +#include "../pfCamera/plVirtualCamNeu.h" +#include "../pfCamera/plCameraBrain.h" +#include "../plResMgr/plResManager.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plKeyImp.h" +#include "../pnModifier/plLogicModBase.h" +#include "../plModifier/plSDLModifier.h" +#include "../plSDL/plSDL.h" +#include "../pfCharacter/plPlayerModifier.h" +#include "../plSurface/plLayerDepth.h" +#include "../plSurface/plLayerOr.h" +#include "../plSurface/plLayerOr.h" +#include "../plAudio/plAudioSystem.h" +#include "../plAudio/plVoiceChat.h" +#include "../plAudio/plWinMicLevel.h" +#include "../plPipeline/plFogEnvironment.h" +#include "../plPipeline/plPlates.h" +#include "../plPipeline/plDynamicEnvMap.h" +#include "../../NucleusLib/inc/hsTimer.h" +#include "../pnMessage/plClientMsg.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plAudioSysMsg.h" +#include "../plMessage/plListenerMsg.h" +#include "../pfAudio/plListener.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plOneShotMsg.h" +#include "../plVault/plVault.h" +#include "../../Apps/plClient/plClient.h" +#include "pfConsole.h" +#include "pfConsoleContext.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plModifier/plSimpleModifier.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plAvatarTasks.h" +#include "../plAvatar/plAvBrainGeneric.h" +//#include "../plHavok1/plSimulationMgr.h" + +#include "../plMessage/plConsoleMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pnMessage/plSoundMsg.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pfAnimation/plAnimDebugList.h" + +#include "../pnMessage/plNodeChangeMsg.h" +#include "../pnMessage/plProxyDrawMsg.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plAttachMsg.h" +#include "../plMessage/plSimInfluenceMsg.h" +#include "../plMessage/plSimStateMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../pfMessage/pfKIMsg.h" + +#include "../plInputCore/plInputInterfaceMgr.h" +#include "../plInputCore/plInputManager.h" +#include "../plInputCore/plInputDevice.h" +#include "../plInputCore/plAvatarInputInterface.h" +#include "../plMessage/plInputEventMsg.h" +#include "../pnInputCore/plKeyMap.h" + +#include "../plParticleSystem/plParticleSystem.h" +#include "../plParticleSystem/plConvexVolume.h" +#include "../plParticleSystem/plParticleEffect.h" +#include "../plParticleSystem/plParticleGenerator.h" +#include "../plSurface/hsGMaterial.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plScene/plSceneNode.h" +#include "../plScene/plPageTreeMgr.h" +#include "../plScene/plPostEffectMod.h" +#include "../pnMessage/plNodeRefMsg.h" +//#include "../pnMessage/plWarpMsg.h" +#include "hsResMgr.h" + +#include "../plParticleSystem/plParticleSystem.h" +#include "../plMessage/plParticleUpdateMsg.h" + +#include "../plDrawable/plDynaBulletMgr.h" + +#include "../plGImage/plMipmap.h" +#include "../plGImage/plTGAWriter.h" + +#include "../plGLight/plShadowCaster.h" +#include "../plGLight/plShadowMaster.h" + +// begin for agedefn test +#include "hsStream.h" +#include "../plAgeDescription/plAgeDescription.h" +#include "../plUnifiedTime/plUnifiedTime.h" +//end for agedefn test + +#include "../../PubUtilLib/plFile/hsFiles.h" +#include "../../NucleusLib/pnSceneObject/plAudioInterface.h" + +#include "../plStatusLog/plStatusLog.h" +#include "../pnTimer/pnBuildDates.h" + +#include "hsStlUtils.h" +#include "hsTemplates.h" +#include "../Corelib/hsUtils.h" + +#include "../pfPython/cyPythonInterface.h" +#include "../pfPython/plPythonSDLModifier.h" + +#include "../plResMgr/plResManagerHelper.h" +#include "../plResMgr/plResMgrSettings.h" +#include "../plResMgr/plLocalization.h" + + +#define PF_SANITY_CHECK( cond, msg ) { if( !( cond ) ) { PrintString( msg ); return; } } + +//// DO NOT REMOVE!!!! +//// This is here so Microsoft VC won't decide to "optimize" this file out +//// DO NOT REMOVE!!!! +void pfConsoleCmdGroup::Dummy( void ) +{ +} +//// DO NOT REMOVE!!!! + +//// Defining Console Commands /////////////////////////////////////////////// +// +// You define console commands by using the PF_CONSOLE_CMD macro. The format +// of the macro is: +// +// PF_CONSOLE_CMD( groupName, functionName, "paramList", "Help string" ) +// +// Where: +// - groupName is a string representing what command group the command +// is in. Subgroups are specified by an underscore, i.e. to put a command +// in the Draw subgroup of the Graphics group, you would specify +// "Graphics_Draw". Specifying "" means to put the command in the base +// command group--i.e. it has no group. +// - functionName is required; it specifies the function name (really?!?!). +// Function names must be globally unique, so you can't have a Draw +// function in both the Graphics and SceneAPI groups. Sorry. :( +// - paramList specifies the parameters and types to the function. +// The smallest list you can have is "", which means "no parameters". +// If you have parameters, it must be in a comma-delimited string. +// You can either specify types or labels and types, so you can say +// "int, float" or "int x, float value". Currently, the labels are only +// used when printing out usage strings, but they will be used later +// for auto-labeling GUI elements, so please put them in where viable. +// +// White space does not matter. Valid types are int, float, char, string +// bool (auto-conversion of "true"/"false" strings to 1 or 0) and "...". +// "..." is a special type that means the same as the C equivalent: +// "there can be zero or more parameters here and I don't care what the +// type is" is the gist of it. +// - helpString is a short description of the function, which currently +// isn't used, but will be used in the future when implementing help +// (could you have guessed it? :) Please fill it in when the function +// name isn't obvious (i.e. SetFogColor doesn't really need one) +// +// The actual C code prototype looks like: +// void pfConsoleCmd_groupName_functionName( UInt32 numParams, pfConsoleCmdParam *params, +// void (*PrintString)( char * ) ); +// +// numParams is exactly what it sounds like. params is an array of console +// parameter objects, each of which are rather nifty in that they can be cast +// immediately to whatever type you asked for in your parameter list +// ("paramList" above). So if your paramList was "int", then params[ 0 ] +// can be cast to an int immediately, such as int x = params[ 0 ]; +// If you attempt to cast a parameter to a type other than the one specified +// in the paramList, you get an hsAssert saying so, so don't do it! Any +// parameters that fall under "..." are automagically strings, but they can +// be cast to any valid type without an assert. So basically, if you want +// to still do your own conversion, just specify "..." as the entire paramList +// and treat the params array as if it were an array of strings. +// +// Thus, the net result of the paramList is that it lets the console engine +// do the parameter parsing for you. If the paramters given to the function +// do not match the list (this includes too many or too few parameters), a +// usage string is printed out and the function is not called. Thus, the +// ONLY parameter error you can possibly have is casting a parameter object +// to a type other than you asked for. So don't do it! +// +// (Note: this makes numParams almost obsolete; the only reason it still has +// a use is for "...", which of course allows variable number of parameters.) +// +// PrintString is a function that lets you print output to the on-screen +// console. It is guaranteed to be non-null. Worst case is that it points +// to a dummy function that does nothing, but it will *always* be valid. +// +// To define console command groups, you use the macro: +// +// PF_CONSOLE_GROUP( group ) +// +// where "group" is the name without quotes of the group you want to create. +// To create a subgroup inside a group, use: +// +// PF_CONSOLE_SUBGROUP( parent, group ) +// +// where "parent" is the parent group for the subgroup. "parent" can have +// underscores in it just like the group of a CONSOLE_CMD, so you can say +// +// PF_CONSOLE_SUBGROUP( Graphics_Render, Drawing ) +// +// to create the Graphics_Render_Drawing subgroup. All groups must be +// defined before any commands that are in that group. Note that although +// the +// +////////////////////////////////////////////////////////////////////////////// + +// +// utility functions +// +////////////////////////////////////////////////////////////////////////////// +plKey FindSceneObjectByName(const char* name, const char* ageName, char* statusStr, bool subString=false); +plKey FindObjectByName(const char* name, int type, const char* ageName, char* statusStr, bool subString=false); +plKey FindObjectByNameAndType(const char* name, const char* typeName, const char* ageName, + char* statusStr, bool subString=false); + +// +// Find an object from name, type (int), and optionally age. +// Name can be an alias specified by saying $foo +// +plKey FindObjectByName(const char* name, int type, const char* ageName, char* statusStr, bool subString) +{ + if (!name) + { + if (statusStr) + sprintf(statusStr, "Object name is nil"); + return nil; + } + + if (type<0 || type>=plFactory::GetNumClasses()) + { + if (statusStr) + sprintf(statusStr, "Illegal class type val"); + return nil; + } + + plKey key=nil; + // Try restricted to our age first, if we're not given an age name. This works + // around most of the problems associated with unused keys in pages causing the pages to be marked + // as not loaded and thus screwing up our searches + if( ageName == nil && plNetClientMgr::GetInstance() != nil ) + { + const char *thisAge = plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeName(); + if (thisAge != nil) + { + key = plKeyFinder::Instance().StupidSearch(thisAge, nil, type, name, subString); + if (key != nil) + { + if (statusStr) + sprintf(statusStr, "Found Object"); + return key; + } + } + } + // Fallback + key = plKeyFinder::Instance().StupidSearch(ageName,nil,type, name, subString); + + if (!key) + { + if (statusStr) + sprintf(statusStr, "Can't find object"); + return nil; + } + + if (!key->ObjectIsLoaded()) + { + if (statusStr) + sprintf(statusStr, "Object is not loaded"); + } + + if (statusStr) + sprintf(statusStr, "Found Object"); + + return key; +} + +// +// Find a SCENEOBJECT from name, and optionally age. +// Name can be an alias specified by saying $foo. +// Will load the object if necessary. +// +plKey FindSceneObjectByName(const char* name, const char* ageName, char* statusStr, bool subString) +{ + plKey key=FindObjectByName(name, plSceneObject::Index(), ageName, statusStr, subString); + + if (!plSceneObject::ConvertNoRef(key ? key->ObjectIsLoaded() : nil)) + { + if (statusStr) + sprintf(statusStr, "Can't find SceneObject"); + return nil; + } + + return key; +} + +// +// Find an object from name, type (string) and optionally age. +// Name can be an alias specified by saying $foo +// +plKey FindObjectByNameAndType(const char* name, const char* typeName, const char* ageName, + char* statusStr, bool subString) +{ + if (!typeName) + { + if (statusStr) + sprintf(statusStr, "TypeName is nil"); + return nil; + } + + return FindObjectByName(name, plFactory::FindClassIndex(typeName), ageName, statusStr, subString); +} + +void PrintStringF(void pfun(const char *),const char * fmt, ...) +{ + va_list args; + + char buffy[512]; + va_start(args, fmt); + vsprintf(buffy, fmt, args); + va_end(args); + pfun(buffy); +} + +//// plDoesFileExist ////////////////////////////////////////////////////////// +// Utility function to determine whether the given file exists + +static hsBool plDoesFileExist( const char *path ) +{ + hsUNIXStream stream; + + + if( !stream.Open( path, "rb" ) ) + return false; + + stream.Close(); + return true; +} + + +////////////////////////////////////////////////////////////////////////////// +//// Base Commands /////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_BASE_CMD( SampleCmd1, "", "Sample command #1" ) +{ + // No parameters, enforced (i.e. this function won't get called unless + // the calling line has no parameters) +} + +PF_CONSOLE_BASE_CMD( SampleCmd2, "int myValue", "Sample command #2" ) +{ + // One parameter, which is an int. Note that because of the console + // engine, we no longer have to test for the number of parameters. + + // Since we said "int" as our type, this is perfectly valid + int myInt = (int)params[ 0 ] * 5; + + // This will assert on run-time, since it's not an int + float myFloat = params[ 0 ]; + + // This is also BAD, since we only specified one parameter. It'll assert + int test = params[ 1 ]; +} + +PF_CONSOLE_BASE_CMD( SampleCmd3, "int, ...", "Sample command #3" ) +{ + // One parameter, which is an int, plus zero or more paramters. + + // Still perfectly valid + int myInt = (int)params[ 0 ] * 5; + + // Note: we have to test numParams here because ... allows no extra + // params, so we have to make sure we have one. Note: numParams + // INCLUDES all parameters, in this case our int. + if( numParams > 1 ) + { + // This is okay--any parameters via ... are strings + char *str = params[ 1 ]; + + // This is also okay, since ... allows the params to be cast + // to any valid type. Note that if the parameter isn't actually + // an int, it'll behave just like atoi()--i.e. return 0 + int value = params[ 1 ]; + } +} + +#endif // LIMIT_CONSOLE_COMMANDS + + +#ifndef LIMIT_CONSOLE_COMMANDS + +#include "../plMessage/plTransitionMsg.h" + +PF_CONSOLE_BASE_CMD( FadeIn, "float len, bool hold", "Sample command #1" ) +{ + plTransitionMsg *msg = TRACKED_NEW plTransitionMsg( plTransitionMsg::kFadeIn, (float)params[ 0 ], (bool)params[ 1 ] ); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_BASE_CMD( FadeOut, "float len, bool hold", "Sample command #1" ) +{ + plTransitionMsg *msg = TRACKED_NEW plTransitionMsg( plTransitionMsg::kFadeOut, (float)params[ 0 ], (bool)params[ 1 ] ); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_BASE_CMD( NextStatusLog, "", "Cycles through the status logs" ) +{ + plStatusLogMgr::GetInstance().NextStatusLog(); +} + +PF_CONSOLE_BASE_CMD( PrevStatusLog, "", "Cycles backwards through the status logs" ) +{ + plStatusLogMgr::GetInstance().PrevStatusLog(); +} + + +PF_CONSOLE_BASE_CMD( ShowStatusLog, "string logName", "Advances the status log display to the given log" ) +{ + plStatusLogMgr::GetInstance().SetCurrStatusLog( params[ 0 ] ); +} + +#endif // LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_BASE_CMD( DisableLogging, "", "Turns off logging" ) +{ + plStatusLog::fLoggingOff = true; +} + + +PF_CONSOLE_BASE_CMD( EnableLogging, "", "Turns on logging" ) +{ + plStatusLog::fLoggingOff = false; +} + +PF_CONSOLE_BASE_CMD( DumpLogs, "string folderName", "Dumps all current logs to the folder specified, relative to the log folder" ) +{ + plStatusLogMgr::GetInstance().DumpLogs( params[ 0 ] ); +} + + +////////////////////////////////////////////////////////////////////////////// +//// Stat Gather Commands //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_GROUP( Stats ) + +#include "../plStatGather/plProfileManagerFull.h" + +PF_CONSOLE_CMD( Stats, Show, // Group name, Function name + "...", // Params + "Shows or hides a given category of statistics.\n" + "List the valid categories using Stats.ListGroups") +{ + const char* group = nil; + if (numParams > 0) + group = params[0]; + + if(numParams > 1) + plProfileManagerFull::Instance().ShowLaps(params[0], params[1]); + else + plProfileManagerFull::Instance().ShowGroup(group); +} + +PF_CONSOLE_CMD(Stats, ResetMax, // Group name, Function name + "", // Params + "Resets the max value for all stats") +{ + plProfileManagerFull::Instance().ResetMax(); +} + +PF_CONSOLE_CMD(Stats, ShowNext, "", "Shows the next group of stats") +{ + plProfileManagerFull::Instance().ShowNextGroup(); +} + +PF_CONSOLE_CMD(Stats, ShowLaps, + "string group, string stat", + "") +{ + plProfileManagerFull::Instance().ShowLaps(params[0], params[1]); +} + +PF_CONSOLE_CMD(Stats, ListGroups, "", "Prints the names of all the stat groups to the console") +{ + plProfileManagerFull::GroupSet groups; + plProfileManagerFull::Instance().GetGroups(groups); + + plProfileManagerFull::GroupSet::iterator it; + for (it = groups.begin(); it != groups.end(); it++) + PrintString((char*)*it); +} + +PF_CONSOLE_CMD(Stats, ListLaps, "", "Prints the names of all the stats with laps to the console") +{ + plProfileManagerFull::LapNames laps; + plProfileManagerFull::Instance().GetLaps(laps); + + for (int i = 0; i < laps.size(); i++) + { + char buf[256]; + sprintf(buf, "%s - %s", laps[i].group, laps[i].varName); + PrintString(buf); + } +} + +PF_CONSOLE_CMD(Stats, SetLapMin, "int min", "Sets the minimum index of which lap will display") +{ + plProfileManagerFull::Instance().SetMinLap((int)params[0]); +} + + +PF_CONSOLE_CMD(Stats, PageDownLaps, "", "Show the next page of laps") +{ + plProfileManagerFull::Instance().PageDownLaps(); +} + + +PF_CONSOLE_CMD(Stats, PageUpLaps, "", "Show the previous page of laps") +{ + plProfileManagerFull::Instance().PageUpLaps(); +} + +PF_CONSOLE_CMD(Stats, SetAvgTime, "int ms", "Sets the amount of time stats are averaged over") +{ + plProfileManager::Instance().SetAvgTime((int)params[0]); +} + +PF_CONSOLE_CMD(Stats, Graph, "string stat, int min, int max", "Graphs the specified stat") +{ + plProfileManagerFull::Instance().CreateGraph(params[0], (int)params[1], (int)params[2]); +} + +PF_CONSOLE_CMD(Stats, ShowDetail, "", "Shows the detail stat graph") +{ + plProfileManagerFull::Instance().ShowDetailGraph(); +} + +PF_CONSOLE_CMD(Stats, HideDetail, "", "Hides the detail stat graph") +{ + plProfileManagerFull::Instance().HideDetailGraph(); +} + +PF_CONSOLE_CMD(Stats, ResetDetailDefaults, "", "Resets the detail graph's defaults") +{ + plProfileManagerFull::Instance().ResetDefaultDetailVars(); +} + +PF_CONSOLE_CMD(Stats, AddDetailVar, "string stat", "Adds the specified var to the detail graph with the default range of 0->100") +{ + plProfileManagerFull::Instance().AddDetailVar(params[0], 0, 100); +} + +PF_CONSOLE_CMD(Stats, AddDetailVarWithOffset, "string stat, int offset", "Adds the specified var to the detail graph with a offset and default range\n" + "of 0->(100-offset)") +{ + int offset = (int)params[1]; + plProfileManagerFull::Instance().AddDetailVar(params[0], -offset, 100-offset); +} + +PF_CONSOLE_CMD(Stats, AddDetailVarWithRange, "string stat, int min, int max", "Adds the specified var to the detail graph") +{ + plProfileManagerFull::Instance().AddDetailVar(params[0], (int)params[1], (int)params[2]); +} + +PF_CONSOLE_CMD(Stats, AddDetailVarWithOffsetAndRange, "string stat, int offset, int min, int max", "Adds the specified var to the detail graph with an\n" + "offset and a range of min->(max-offset)") +{ + int offset = (int)params[1]; + plProfileManagerFull::Instance().AddDetailVar(params[0], (int)params[2]-offset, (int)params[3]-offset); +} + +PF_CONSOLE_CMD(Stats, RemoveDetailVar, "string stat", "Removes the specified var from the detail graph") +{ + plProfileManagerFull::Instance().RemoveDetailVar(params[0]); +} + +#include "../plStatGather/plAutoProfile.h" + +PF_CONSOLE_CMD(Stats, AutoProfile, "...", "Performs an automated profile in all the ages. Optional: Specify an age name to do just that age") +{ + const char* ageName = nil; + if (numParams > 0) + ageName = params[0]; + + plAutoProfile::Instance()->StartProfile(ageName); +} + +PF_CONSOLE_CMD(Stats, ProfileAllAgeLoads, "", "Turns on Registry.LogReadTimes and links to each age, then exits") +{ + ((plResManager*)hsgResMgr::ResMgr())->LogReadTimes(true); + plAutoProfile::Instance()->LinkToAllAges(); +} + +#endif // LIMIT_CONSOLE_COMMANDS + + +////////////////////////////////////////////////////////////////////////////// +//// Memory Commands //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_GROUP( Memory ) + +#ifdef HS_FIND_MEM_LEAKS + +PF_CONSOLE_CMD( Memory, DumpAllocReport, // Group name, Function name + "", // Params + "Dump heap allocations to file." ) // Help string +{ + MemDumpAllocReport(); +} + + +PF_CONSOLE_CMD(Memory, + ValidateNow, + "", + "Validate all heap allocations") +{ + MemValidateNow(); +} + + +PF_CONSOLE_CMD(Memory, + SetValidation, + "bool on", + "Validate all heap allocations each time memory is alloced or freed.") +{ + MemSetValidation((bool)params[0]); +} + +PF_CONSOLE_CMD(Memory, + DumpUsage, + "", + "Dump heap usage to file") +{ + MemDumpUsageReport(); +} + + +#endif + +#endif // LIMIT_CONSOLE_COMMANDS + +////////////////////////////////////////////////////////////////////////////// +//// Console Group Commands ////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_GROUP( Console ) + +PF_CONSOLE_CMD( Console, Clear, "", "Clears the console" ) +{ + pfConsole::Clear(); +} + +PF_CONSOLE_CMD( Console, EnableFX, "bool enable", "Enables flashy console effects" ) +{ + pfConsole::EnableEffects( (hsBool)(bool)params[ 0 ] ); + if( pfConsole::AreEffectsEnabled() ) + PrintString( "Console effects enabled" ); + else + PrintString( "Console effects disabled" ); +} + +PF_CONSOLE_CMD( Console, SetTextColor, "int r, int g, int b", + "Sets the color of normal console text" ) +{ + UInt32 color = 0xff000000 | ( (int)params[ 0 ] << 16 ) | ( (int)params[ 1 ] << 8 ) | ( (int)params[ 2 ] ); + + pfConsole::SetTextColor( color ); +} + +class DocGenIterator : public pfConsoleCmdIterator +{ + FILE *fFile; + +public: + DocGenIterator(FILE *f) { fFile = f; } + virtual void ProcessCmd(pfConsoleCmd* c, int depth) + { + + if(strncmp("SampleCmd",c->GetName(), 9) != 0) + { + fprintf(fFile, "

    %s
    %s

    \n",c->GetSignature(), + c->GetHelp()); + } + } + virtual bool ProcessGroup(pfConsoleCmdGroup *g, int depth) + { + // if(g->GetFirstCommand() != nil) + { + fprintf(fFile, "

    Command %sGroup %s

    \n", + (depth > 0) ? "3" : "2", + (depth > 0) ? "Sub" :"" , + g->GetName()); + } + return true; + } +}; + +class BriefDocGenIterator : public pfConsoleCmdIterator +{ + FILE *fFile; + char fGrpName[200]; + +public: + BriefDocGenIterator(FILE *f) { fFile = f; strcpy(fGrpName,"");} + virtual void ProcessCmd(pfConsoleCmd* c, int depth) + { + + if(strncmp("SampleCmd",c->GetName(), 9) != 0) + { + fprintf(fFile, "%s.%s - %s
    \n",fGrpName,c->GetSignature(), + c->GetHelp()); + } + } + virtual bool ProcessGroup(pfConsoleCmdGroup *g, int depth) + { + // if(g->GetFirstCommand() != nil) + { + fprintf(fFile, "
    \n"); + if(depth <1) + strcpy(fGrpName, g->GetName()); + else + { + strcat(fGrpName,"."); + strcat(fGrpName,g->GetName()); + } + + } + return true; + } +}; + +PF_CONSOLE_CMD( Console, CreateDocumentation, "string fileName", + "Writes HTML documentation for the current console commands" ) +{ + + PrintString((char*)params[0]); + + + pfConsoleCmdGroup *group; + FILE *f; + + + f = fopen(params[0],"wt"); + if(f == nil) + { + PrintString("Couldn't Open File"); + return; + } + + + fprintf(f, "

    Console Commands for Plasma 2.0 Client

    Built %s on %s.

    ", + pnBuildDates::fBuildTime, pnBuildDates::fBuildDate ); + + DocGenIterator iter(f); + group = pfConsoleCmdGroup::GetBaseGroup(); + group->IterateCommands(&iter); + + fclose(f); + +} + + +PF_CONSOLE_CMD( Console, CreateBriefDocumentation, "string fileName", + "Writes brief HTML documentation for the current console commands" ) +{ + + PrintString((char*)params[0]); + + + pfConsoleCmdGroup *group; + FILE *f; + + + f = fopen(params[0],"wt"); + if(f == nil) + { + PrintString("Couldn't Open File"); + return; + } + + fprintf(f, "

    Console Commands for Plasma 2.0 Client

    Built %s on %s.

    ", + pnBuildDates::fBuildTime, pnBuildDates::fBuildDate ); + BriefDocGenIterator iter(f); + group = pfConsoleCmdGroup::GetBaseGroup(); + group->IterateCommands(&iter); + + fclose(f); + +} + +PF_CONSOLE_CMD( Console, SetVar, "string name, string value", + "Sets the value of a given global console variable" ) +{ + pfConsoleContext &ctx = pfConsoleContext::GetRootContext(); + + + hsBool oldF = ctx.GetAddWhenNotFound(); + ctx.SetAddWhenNotFound( true ); + ctx.SetVar( params[ 0 ], params[ 1 ] ); + ctx.SetAddWhenNotFound( oldF ); +} + +PF_CONSOLE_CMD( Console, PrintVar, "string name", "Prints the value of a given global console variable" ) +{ + pfConsoleContext &ctx = pfConsoleContext::GetRootContext(); + + Int32 idx = ctx.FindVar( params[ 0 ] ); + if( idx == -1 ) + PrintString( "Variable not found" ); + else + { + PrintStringF( PrintString, "The value of %s is %s", (const char *)params[ 0 ], (const char *)ctx.GetVarValue( idx ) ); + } +} + +PF_CONSOLE_CMD( Console, PrintAllVars, "", "Prints the values of all global console variables" ) +{ + pfConsoleContext &ctx = pfConsoleContext::GetRootContext(); + + UInt32 i; + + PrintString( "Global console variables:" ); + for( i = 0; i < ctx.GetNumVars(); i++ ) + { + PrintStringF( PrintString, " %s: %s", (const char *)ctx.GetVarName( i ), (const char *)ctx.GetVarValue( i ) ); + } +} + +PF_CONSOLE_CMD( Console, ClearAllVars, "", "Wipes the global console variable context" ) +{ + pfConsoleContext &ctx = pfConsoleContext::GetRootContext(); + ctx.Clear(); + PrintString( "Global context wiped" ); +} + +PF_CONSOLE_CMD( Console, ExecuteFile, "string filename", "Runs the given file as if it were an .ini or .fni file" ) +{ + plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kExecuteFile, params[ 0 ] ); + cMsg->Send(); +} + +PF_CONSOLE_CMD( Console, ExecuteFileDelayed, "string filename, float timeInSecs", "Runs the given file as if it were an .ini or .fni file, but at some given point in the future" ) +{ + plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kExecuteFile, params[ 0 ] ); + cMsg->SetTimeStamp( hsTimer::GetSysSeconds() + (float)params[ 1 ] ); + cMsg->Send(); +} + +#endif // LIMIT_CONSOLE_COMMANDS + + +////////////////////////////////////////////////////////////////////////////// +//// Graphics Group Commands ///////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_GROUP( Graphics ) // Defines a main command group + +#ifndef LIMIT_CONSOLE_COMMANDS + +// NOTE ON THESE DEBUG FLAGS: +// Any unique prefix will work for a given flag (although the +// TAB won't fill out the rest of the command). So "noli" works +// as well as "noLightMaps". So try to make the first 4 or 5 letters of +// the string flag both unique AND meaningful. E.g. if the flag will +// disable XYZ, then "noXYZ..." is a good start for the command (as opposed +// to "disableXYZ..."). Note also, that no checking for uniqueness happens +// (because I don't feel like it), so "SetDebugFlag no" will grab the first +// of many things that start with no (currently noMultiTexture). Since verbs +// tend to get reused more than subjects, start commands with the noun instead +// of the verb. E.g. "showBufferData" and "showNormals" can be more easily distinguished +// as bufferDataShow and normalsShow. +PF_CONSOLE_CMD( Graphics, // Group name + SetDebugFlag, // Function name + "string flag, ...", // Params + "Sets or toggles a pipeline debug flag.\nValid flags are:\n\ +\tbufferDataShow - Shows vertex/index buffer stats\n\ +\tnoMultiTexture - Disables multitexturing\n\ +\tnoLightMaps - Disables lightmaps\n\ +\tnoRTLights - Disables runtime lighting\n\ +\tnoAlphaBlending - Disables alpha blending\n\ +\tnoDecals - Disables drawing of decals\n\ +\tnoFaceSort - Disables alpha-blending face sorting\n\ +\tnormalShow - Shows normals for all vertices\n\ +\tnoShadows - Toggles shadow generation and display\n\ +\tnoUpper - Toggles render of upper layers\n\ +\tnoBump - Toggles bump mapping\n\ +\tnoRender - Toggles all rendering\n\ +\tnoLODPop - Toggles ignoring of LOD Pops\n\ +\tnoPlates - Toggles rendering of plates\n\ +\tmipmapColorize - Color-codes mipmap levels\n\ +\tnoAnisotropy - Disables anisotropic filtering\n\ +\tallBright - Overrides D3D lighting equations (forces everything emissive)\n\ +\tnoProjLights - Turns off runtime projected lights\n\ +\toneMaterial - Toggles using one material for the entire scene\n\ +\treShaders - reload all shaders\n\ +\treTex - reload all textures from sysmem\n\ +\tonlyProjLights - Turns off runtime non-projected lights\n\ +\tnoFog - Disable all fog" ) // Help string +{ + UInt32 flag; + bool on; + char string[ 128 ], name[ 64 ]; + int i; + + struct + { + char name[ 64 ]; UInt32 flag; + } flags[] = { { "reloadTextures", plPipeDbg::kFlagReload }, + { "noPreShade", plPipeDbg::kFlagNoPreShade}, + { "noMultitexture", plPipeDbg::kFlagNoMultitexture }, + { "noLightMaps", plPipeDbg::kFlagNoLightmaps }, + { "noRTLights", plPipeDbg::kFlagNoRuntimeLights }, + { "noAlphaBlending", plPipeDbg::kFlagNoAlphaBlending }, + { "noDecals", plPipeDbg::kFlagNoDecals }, + { "noFaceSort", plPipeDbg::kFlagDontSortFaces }, + { "noSpecular", plPipeDbg::kFlagDisableSpecular }, + { "normalShow", plPipeDbg::kFlagShowNormals }, + { "mipmapColorize", plPipeDbg::kFlagColorizeMipmaps }, + { "noShadows", plPipeDbg::kFlagNoShadows }, + { "noUpper", plPipeDbg::kFlagNoUpperLayers }, + { "noRender", plPipeDbg::kFlagNoRender }, + { "noLODPop", plPipeDbg::kFlagSkipVisDist }, + { "noPlates", plPipeDbg::kFlagNoPlates }, + { "noBump", plPipeDbg::kFlagNoBump }, + { "noAnisotropy", plPipeDbg::kFlagNoAnisotropy }, + { "allBright", plPipeDbg::kFlagAllBright }, + { "noProjLights", plPipeDbg::kFlagNoApplyProjLights }, + { "oneMaterial", plPipeDbg::kFlagSingleMat}, + { "onlyProjLights", plPipeDbg::kFlagOnlyApplyProjLights }, + { "noFog", plPipeDbg::kFlagNoFog } + }; + int numDebugFlags = sizeof( flags ) / sizeof( flags[ 0 ] ); + + + if( numParams > 2 ) + { + PrintString( "Invalid parameters. Use 'SetDebugFlag flag [, true|flase]'." ); + return; + } + + for( i = 0; i < numDebugFlags; i++ ) + { + if( strnicmp( params[ 0 ], flags[ i ].name, strlen(params[0]) ) == 0 ) + { + flag = flags[ i ].flag; + strcpy( name, flags[ i ].name ); + break; + } + } + if( i == numDebugFlags ) + { + flag = atoi( params[ 0 ] ); +#ifndef HS_DEBUGGING + if( flag < 1 || flag > flags[ numDebugFlags ].flag ) + { + PrintString( "Invalid Flag. Check help for valid flags." ); + return; + } +#endif + sprintf( name, "Flag %d", flag ); + } + + if( numParams == 1 ) + on = !pfConsole::GetPipeline()->IsDebugFlagSet( flag ); + else + on = params[ 1 ]; + + pfConsole::GetPipeline()->SetDebugFlag( flag, on ); + + sprintf( string, "%s is now %s", name, on ? "enabled" : "disabled" ); + PrintString( string ); +} + + + +PF_CONSOLE_SUBGROUP( Graphics, VisSet ) // Creates a sub-group under a given group + +PF_CONSOLE_CMD( Graphics_VisSet, Toggle, "", "Toggle using VisSets" ) +{ + hsBool turnOn = !plPageTreeMgr::VisMgrEnabled(); + plPageTreeMgr::EnableVisMgr(turnOn); + + PrintStringF( PrintString, "Visibility Sets %s", turnOn ? "Enabled" : "Disabled" ); +} + +PF_CONSOLE_CMD( Graphics, BumpNormal, "", "Set bump mapping method to default for your hardware." ) +{ + PF_SANITY_CHECK( pfConsole::GetPipeline(), "This command MUST be used in an .fni file (after pipeline initialization)" ); + + if( pfConsole::GetPipeline()->GetMaxLayersAtOnce() > 3 ) + { + pfConsole::GetPipeline()->SetDebugFlag(plPipeDbg::kFlagBumpUV, false); + pfConsole::GetPipeline()->SetDebugFlag(plPipeDbg::kFlagBumpW, false); + } + else + { + pfConsole::GetPipeline()->SetDebugFlag(plPipeDbg::kFlagBumpUV, true); + pfConsole::GetPipeline()->SetDebugFlag(plPipeDbg::kFlagBumpW, false); + } +} + +PF_CONSOLE_CMD( Graphics, BumpUV, "", "Set bump mapping method to GeForce2 compatible." ) +{ + PF_SANITY_CHECK( pfConsole::GetPipeline(), "This command MUST be used in an .fni file (after pipeline initialization)" ); + + pfConsole::GetPipeline()->SetDebugFlag(plPipeDbg::kFlagBumpUV, true); + pfConsole::GetPipeline()->SetDebugFlag(plPipeDbg::kFlagBumpW, false); +} + +PF_CONSOLE_CMD( Graphics, BumpW, "", "Set bump mapping method to cheapest available." ) +{ + PF_SANITY_CHECK( pfConsole::GetPipeline(), "This command MUST be used in an .fni file (after pipeline initialization)" ); + + pfConsole::GetPipeline()->SetDebugFlag(plPipeDbg::kFlagBumpUV, false); + pfConsole::GetPipeline()->SetDebugFlag(plPipeDbg::kFlagBumpW, true); +} + + +PF_CONSOLE_CMD( Graphics, AllowWBuffering, "", "Enables the use of w-buffering\n(w-buffering is disabled by default)." ) +{ + PF_SANITY_CHECK( pfConsole::GetPipeline() == nil, "This command MUST be used in an .ini file (before pipeline initialization)" ); + + extern UInt32 fDbgSetupInitFlags; + + + fDbgSetupInitFlags |= 0x00000001; + PrintString( "W-buffering enabled." ); +} + +PF_CONSOLE_CMD( Graphics, ForceGeForce2Quality, "", "Forces higher-level hardware down to roughly the capabilities of a GeForce 2." ) +{ + PF_SANITY_CHECK( pfConsole::GetPipeline() == nil, "This command MUST be used in an .ini file (before pipeline initialization)" ); + + extern UInt32 fDbgSetupInitFlags; + + + fDbgSetupInitFlags |= 0x00000004; + PrintString( "Hardware caps forced down to GeForce 2 level." ); +} + +#endif // LIMIT_CONSOLE_COMMANDS + + + +//// Graphics.Shadow SubGroup ///////////////////////////////////////////// +PF_CONSOLE_SUBGROUP( Graphics, Shadow ) // Creates a sub-group under a given group + +PF_CONSOLE_CMD( Graphics_Shadow, + Enable, + "bool enable", + "Enable shadows." ) +{ + bool enable = (bool)params[0]; + + if (enable) + { + plShadowCaster::EnableShadowCast(); + PrintString("Shadows Enabled"); + } + else + { + plShadowCaster::DisableShadowCast(); + PrintString("Shadows Disabled"); + } +} + +PF_CONSOLE_CMD( Graphics_Shadow, + Disable, + "", + "Disable shadows." ) +{ + plShadowCaster::DisableShadowCast(); + PrintString("Shadows Disabled"); +} + +PF_CONSOLE_CMD( Graphics_Shadow, + Toggle, + "", + "Toggle shadows." ) +{ + plShadowCaster::ToggleShadowCast(); + PrintString(plShadowCaster::ShadowCastDisabled() ? "Shadows Disabled" : "Shadows Enabled"); +} + +PF_CONSOLE_CMD( Graphics_Shadow, + Show, + "", + "Show shadows." ) +{ + hsBool on = !pfConsole::GetPipeline()->IsDebugFlagSet(plPipeDbg::kFlagShowShadowBounds); + pfConsole::GetPipeline()->SetDebugFlag( plPipeDbg::kFlagShowShadowBounds, on ); + + char str[ 256 ]; + sprintf( str, "Shadow bounds now %s", on ? "visible" : "invisible" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Shadow, + Apply, + "", + "Toggles applying shadows (they are still computed)." ) +{ + hsBool on = !pfConsole::GetPipeline()->IsDebugFlagSet(plPipeDbg::kFlagNoShadowApply); + pfConsole::GetPipeline()->SetDebugFlag( plPipeDbg::kFlagNoShadowApply, on ); + + char str[ 256 ]; + sprintf( str, "Shadow apply now %s", on ? "disabled" : "enabled" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Shadow, + MaxSize, + "...", + "Max shadowmap size." ) +{ + int size; + if( numParams > 0 ) + { + size = atoi( params[ 0 ] ); + + plShadowMaster::SetGlobalMaxSize(size); + } + else + { + size = plShadowMaster::GetGlobalMaxSize(); + } + char str[256]; + sprintf(str, "Max shadowmap size %d", size); + PrintString(str); +} + +PF_CONSOLE_CMD( Graphics_Shadow, + MaxDist, + "...", + "Max shadowmap vis distance." ) +{ + float dist; + if( numParams > 0 ) + { + dist = (float)atof( params[ 0 ] ); + + plShadowMaster::SetGlobalMaxDist(dist); + } + else + { + dist = plShadowMaster::GetGlobalMaxDist(); + } + char str[256]; + sprintf(str, "Max shadowmap vis dist %f", dist); + PrintString(str); +} + +PF_CONSOLE_CMD( Graphics_Shadow, + VisibleDistance, + "...", + "Shadow quality (0 to 1)." ) +{ + float parm; + if( numParams > 0 ) + { + parm = (float)atof( params[ 0 ] ); + + plShadowMaster::SetGlobalShadowQuality(parm); + } + else + { + parm = plShadowMaster::GetGlobalShadowQuality(); + } + char str[256]; + sprintf(str, "Shadow quality %f", parm); + PrintString(str); +} + +PF_CONSOLE_CMD( Graphics_Shadow, + Blur, + "...", + "Max shadowmap blur size." ) +{ + extern hsScalar blurScale; + if( numParams > 0 ) + { + blurScale = (hsScalar)atof( params[ 0 ] ); + } + else + { + } + char str[256]; + sprintf(str, "Max shadowmap Blur %f", blurScale); + PrintString(str); +} + +//// Graphics.DebugText SubGroup ///////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_SUBGROUP( Graphics, DebugText ) // Creates a sub-group under a given group + +PF_CONSOLE_CMD( Graphics_DebugText, // Group name + SetFont, // Function name + "string face, int size", // Params + "Sets the font face and size used for drawing debug text" ) // Help string +{ + plDebugText::Instance().SetFont( params[ 0 ], (UInt16)(int)params[ 1 ] ); +} + +PF_CONSOLE_CMD( Graphics_DebugText, // Group name + Enable, // Function name + "", // Params + "Enables debug text drawing" ) // Help string +{ + plDebugText::Instance().SetEnable( true ); +} + +PF_CONSOLE_CMD( Graphics_DebugText, // Group name + Disable, // Function name + "", // Params + "Disables debug text drawing" ) // Help string +{ + plDebugText::Instance().SetEnable( false ); +} + +#endif // LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_SUBGROUP( Graphics, Renderer ) // Creates a sub-group under a given group + +PF_CONSOLE_CMD( Graphics_Renderer, SetClearColor, "float r, float g, float b", "Sets the global clear color" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + hsColorRGBA c; + + c.Set( params[ 0 ], params[ 1 ], params[ 2 ], 1.0f ); + plClient::GetInstance()->SetClearColor( c ); +} + + +#ifndef LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_CMD( Graphics_Renderer, mfTest, "int mfDbgTest", "Reserved for internal testing" ) +{ + extern int mfCurrentTest; + + mfCurrentTest = (int) params[0]; + + char str[ 256 ]; + sprintf( str, "Current test %d.", mfCurrentTest ); + PrintString( str ); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Graphics_Renderer, Gamma, "float g, ...", "Set gamma value (g or (r,g,b))" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + hsScalar g = params[0]; + + if( numParams == 1 ) + { + pfConsole::GetPipeline()->SetGamma(g); + +// char str[ 256 ]; +// sprintf(str, "Gamma set to %g.", g); +// PrintString(str); + } + else + { + hsScalar eR = g; + hsScalar eG = g; + hsScalar eB = g; + + if( numParams > 2 ) + eB = params[2]; + eG = params[1]; + + pfConsole::GetPipeline()->SetGamma(eR, eG, eB); + +// char str[ 256 ]; +// sprintf(str, "Gamma set to (%g, %g, %g).", eR, eG, eB); +// PrintString(str); + } +} + +PF_CONSOLE_CMD( Graphics_Renderer, Gamma2, "float g", "Set gamma value (alternative ramp)" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + hsTArray ramp; + ramp.SetCount(256); + + hsScalar g = params[0]; + + int i; + for( i = 0; i < 256; i++ ) + { + hsScalar t = hsScalar(i) / 255.f; + hsScalar sinT = sin(t * hsScalarPI / 2.f); + + hsScalar remap = t + (sinT - t) * g; + if( remap < 0 ) + remap = 0; + else if( remap > 1.f ) + remap = 1.f; + + ramp[i] = UInt16(remap * hsScalar(UInt16(-1)) + 0.5f); + } + + pfConsole::GetPipeline()->SetGamma(ramp.AcquireArray()); + +// char str[ 256 ]; +// sprintf(str, "Gamma set to %g.", g); +// PrintString(str); +} + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Graphics_Renderer, MaxCullNodes, "...", "Limit occluder processing" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + int maxCullNodes; + if( numParams > 0 ) + { + maxCullNodes = (int) params[0]; + pfConsole::GetPipeline()->SetMaxCullNodes(maxCullNodes); + } + else + { + maxCullNodes = pfConsole::GetPipeline()->GetMaxCullNodes(); + } + + char str[ 256 ]; + sprintf( str, "Max Cull Nodes now %d.", maxCullNodes ); + PrintString( str ); +} + + +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Graphics_Renderer, SetYon, "float yon, ...", "Sets the view yon" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + hsScalar hither, yon; + + + pfConsole::GetPipeline()->GetDepth( hither, yon ); + + pfConsole::GetPipeline()->SetDepth( hither, (float)params[ 0 ] ); + pfConsole::GetPipeline()->RefreshMatrices(); + + char str[ 256 ]; + sprintf( str, "Yon set to %4.1f.", (float)params[ 0 ] ); + PrintString( str ); +} + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Graphics_Renderer, TweakZBiasScale, "float deltaScale", "Adjusts the device-dependent scale value for upper-layer z biasing" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + hsScalar scale; + + scale = pfConsole::GetPipeline()->GetZBiasScale(); + scale += (float)params[ 0 ]; + pfConsole::GetPipeline()->SetZBiasScale( scale ); + + char str[ 256 ]; + sprintf( str, "Z bias scale now set to %4.2f.", (float)scale ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Renderer, SetZBiasScale, "float scale", "Sets the device-dependent scale value for upper-layer z biasing" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + pfConsole::GetPipeline()->SetZBiasScale( (float)params[ 0 ] ); + + char str[ 256 ]; + sprintf( str, "Z bias scale now set to %4.2f.", (float)params[ 0 ] ); + PrintString( str ); +} + + + +PF_CONSOLE_CMD( Graphics_Renderer, Overwire, "...", "Turn on (off) overlay wire rendering" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + hsBool on = false; + UInt32 flag = plPipeDbg::kFlagOverlayWire; + if( !numParams ) + on = !pfConsole::GetPipeline()->IsDebugFlagSet( flag ); + else + on = (bool)params[ 0 ]; + + pfConsole::GetPipeline()->SetDebugFlag( flag, on ); + + char str[256]; + sprintf( str, "OverlayWire is now %s", on ? "enabled" : "disabled" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Renderer, Overdraw, "bool on", "Turn on (off) overdraw rendering" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + static plLayerDepth ld; + static bool ldOn = false; + if( (bool)params[0] ) + { + if( !ldOn ) + { + pfConsole::GetPipeline()->AppendLayerInterface( &ld, true ); + pfConsole::GetPipeline()->SetDebugFlag(plPipeDbg::kFlagNoLightmaps, true); + ldOn = true; + } + } + else + { + if( ldOn ) + { + pfConsole::GetPipeline()->RemoveLayerInterface( &ld, true ); + pfConsole::GetPipeline()->SetDebugFlag(plPipeDbg::kFlagNoLightmaps, false ); + ldOn = false; + } + } +} + +PF_CONSOLE_CMD( Graphics_Renderer, Wireframe, "...", "Toggle or set wireframe view mode." ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + static bool wireOn = false; + static plLayerOr wireLayer; + + + if( numParams > 1 ) + { + PrintString( "Invalid parameters. Use 'Wireframe [true|false]'." ); + return; + } + + wireLayer.SetMiscFlags( hsGMatState::kMiscWireFrame ); + + if( numParams == 0 ) + wireOn = !wireOn; + else if( wireOn == (bool)params[ 0 ] ) + return; + else + wireOn = (bool)params[ 0 ]; + + if( wireOn ) + pfConsole::GetPipeline()->AppendLayerInterface( &wireLayer ); + else + pfConsole::GetPipeline()->RemoveLayerInterface( &wireLayer ); + + char str[ 256 ]; + sprintf( str, "Wireframe view mode is now %s.", wireOn ? "enabled" : "disabled" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Renderer, TwoSided, "...", "Toggle or set force two-sided." ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + static bool twoSideOn = false; + static plLayerOr twoSideLayer; + + + if( numParams > 1 ) + { + PrintString( "Invalid parameters. Use 'TwoSided [true|false]'." ); + return; + } + + twoSideLayer.SetMiscFlags( hsGMatState::kMiscTwoSided ); + + if( numParams == 0 ) + twoSideOn = !twoSideOn; + else if( twoSideOn == (bool)params[ 0 ] ) + return; + else + twoSideOn = (bool)params[ 0 ]; + + if( twoSideOn ) + pfConsole::GetPipeline()->AppendLayerInterface( &twoSideLayer ); + else + pfConsole::GetPipeline()->RemoveLayerInterface( &twoSideLayer ); + + char str[ 256 ]; + sprintf( str, "Two-sided mode is now %s.", twoSideOn ? "enabled" : "disabled" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Renderer, ResetDevice, + "int width, int height, int colordepth, bool Windowed, int numAASamples, int MaxAnisotropicSamples", + "Reset Display Device") +{ + plClient::GetInstance()->ResetDisplayDevice((int)params[0], (int)params[1], (int)params[2], (bool)params[3], (int)params[4], (int)params[5]); +} +#endif // LIMIT_CONSOLE_COMMANDS + + +static bool MakeUniqueFileName(const char* prefix, const char* ext, char* fileName) +{ + for (UInt32 uniqueNumber = 1; uniqueNumber < 1000; uniqueNumber++) + { + sprintf(fileName, "%s%03d.%s", prefix, uniqueNumber, ext); + + if (!plDoesFileExist(fileName)) + return true; + } + + return false; +} + + +#ifndef LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_CMD( Graphics_Renderer, TakeScreenshot, "...", "Takes a shot of the current frame and saves it to the given file" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + plMipmap myMipmap; + char fileName[ 512 ]; + + + if( numParams > 1 ) + { + PrintString( "Too many parameters to TakeScreenshot" ); + return; + } + else if( numParams == 1 ) + strcpy( fileName, (char *)params[ 0 ] ); + else + { + // Think up a filename + if (!MakeUniqueFileName("screen", "tga", fileName)) + { + PrintString( "Out of filenames for TakeScreenshot" ); + return; + } + } + + if( !pfConsole::GetPipeline()->CaptureScreen( &myMipmap ) ) + PrintString( "Error capturing screenshot" ); + else + { + char str[ 512 ]; + + plTGAWriter::Instance().WriteMipmap( fileName, &myMipmap ); + sprintf( str, "Screenshot written to '%s'.", fileName ); + PrintString( str ); + } +} + +#include "../pfSurface/plGrabCubeMap.h" + +PF_CONSOLE_CMD( Graphics_Renderer, GrabCubeMap, + "string sceneObject, string prefix", + "Take cubemap from sceneObject's position and name it prefix_XX.jpg") +{ + char str[512]; + const char* objName = params[0]; + plKey key = FindSceneObjectByName(objName, nil, str); + PrintString( str ); + if( !key ) + return; + plSceneObject *obj = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if( !obj ) + return; + + hsColorRGBA clearColor = plClient::GetInstance()->GetClearColor(); + const char* pref = params[1]; + plGrabCubeMap grabCube; + grabCube.GrabCube(pfConsole::GetPipeline(), obj, pref, clearColor); +} + +PF_CONSOLE_CMD( Graphics_Renderer, GrabCubeCam, + "string prefix", + "Take cubemap from camera's position and name it prefix_XX.jpg") +{ + hsPoint3 pos = pfConsole::GetPipeline()->GetViewPositionWorld(); + + hsColorRGBA clearColor = plClient::GetInstance()->GetClearColor(); + const char* pref = params[1]; + plGrabCubeMap grabCube; + grabCube.GrabCube(pfConsole::GetPipeline(), pos, pref, clearColor); +} + +#include "../plJPEG/plJPEG.h" + +PF_CONSOLE_CMD( Graphics_Renderer, TakeJPEGScreenshot, "...", "Takes a shot of the current frame and saves it to the given file" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + plMipmap myMipmap; + char fileName[ 512 ]; + + + if( numParams > 2 ) + { + PrintString( "Too many parameters to TakeScreenshot" ); + return; + } + else if( numParams > 0 ) + strcpy( fileName, (char *)params[ 0 ] ); + else + { + // Think up a filename + if (!MakeUniqueFileName("screen", "jpg", fileName)) + { + PrintString( "Out of filenames for TakeScreenshot" ); + return; + } + } + + if( !pfConsole::GetPipeline()->CaptureScreen( &myMipmap ) ) + PrintString( "Error capturing screenshot" ); + else + { + char str[ 512 ]; + UInt8 quality = 75; + + + if( numParams == 2 ) + quality = (int)params[ 1 ]; + + plJPEG::Instance().SetWriteQuality( quality ); + + if( !plJPEG::Instance().WriteToFile( fileName, &myMipmap ) ) + { + sprintf( str, "JPEG write failed (%s).", plJPEG::Instance().GetLastError() ); + } + else + sprintf( str, "Screenshot written to '%s', quality of %d%%.", fileName, quality ); + PrintString( str ); + } +} + +#include "../plGImage/plAVIWriter.h" + +PF_CONSOLE_CMD( Graphics_Renderer, AVIWrite, "...", "Saves each frame to an AVI file" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + char fileName[ 512 ]; + + if( numParams > 2 ) + { + PrintString( "Too many parameters to AVIWrite" ); + return; + } + else if( numParams > 0 ) + strcpy( fileName, (char *)params[ 0 ] ); + else + { + // Think up a filename + if (!MakeUniqueFileName("movie", "avi", fileName)) + { + PrintString( "Out of filenames for AVIWrite" ); + return; + } + } + + if (!plAVIWriter::Instance().Open(fileName, pfConsole::GetPipeline())) + PrintString( "AVI file create failed"); +} + +PF_CONSOLE_CMD(Graphics_Renderer, AVIClose, "", "Stops the current AVI recording") +{ + plAVIWriter::Instance().Close(); + PrintString("Recording stopped"); +} + +/* +PF_CONSOLE_CMD( Graphics_Renderer, GenerateReflectMaps, "string baseObject, string baseFileName, int size", "Generates the six cubic faces of a reflection map centered on the given object" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + // First, create the renderTarget for the renderRequests + plRenderTarget *target = TRACKED_NEW plRenderTarget( plRenderTarget::kIsProjected | plRenderTarget::kIsTexture, + params[ 2 ], params[ 2 ], 32, 24, 0 ); + +// plMipmap *newMip = TRACKED_NEW plMipmap( size, size, plMipmap::kARGB32Config, 1 ); + + + c.Set( params[ 0 ], params[ 1 ], params[ 2 ], 1.0f ); + plClient::GetInstance()->SetClearColor( c ); +} +*/ + +PF_CONSOLE_CMD( Graphics_Renderer, ToggleScene, "", "Toggles main scene drawing" ) +{ + if( plClient::GetInstance() == nil ) + { + PrintString( "Command invalid before client init" ); + return; + } + + if( plClient::GetInstance()->HasFlag( plClient::kFlagDBGDisableRender ) ) + { + plClient::GetInstance()->SetFlag( plClient::kFlagDBGDisableRender, false ); + PrintString( "Scene rendering enabled" ); + } + else + { + plClient::GetInstance()->SetFlag( plClient::kFlagDBGDisableRender, true ); + PrintString( "Scene rendering disabled" ); + } +} + +PF_CONSOLE_CMD( Graphics_Renderer, ToggleRenderRequests, "", "Toggles processing of pre- and post-render requests" ) +{ + if( plClient::GetInstance() == nil ) + { + PrintString( "Command invalid before client init" ); + return; + } + + if( plClient::GetInstance()->HasFlag( plClient::kFlagDBGDisableRRequests ) ) + { + plClient::GetInstance()->SetFlag( plClient::kFlagDBGDisableRRequests, false ); + PrintString( "Render requests enabled" ); + } + else + { + plClient::GetInstance()->SetFlag( plClient::kFlagDBGDisableRRequests, true ); + PrintString( "Render requests disabled" ); + } +} + +#endif // LIMIT_CONSOLE_COMMANDS + +//// Graphics.Renderer.Fog Subgroup ////////////////////////////////////////// + +PF_CONSOLE_SUBGROUP( Graphics_Renderer, Fog ) + +PF_CONSOLE_CMD( Graphics_Renderer_Fog, SetDefColor, "float r, float g, float b", "Sets the default fog color" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + + plFogEnvironment env; + hsColorRGBA color; + + color.Set( params[ 0 ], params[ 1 ], params[ 2 ], 1.0f ); + env = pfConsole::GetPipeline()->GetDefaultFogEnviron(); + env.SetColor( color ); + pfConsole::GetPipeline()->SetDefaultFogEnviron( &env ); +} + +PF_CONSOLE_CMD( Graphics_Renderer_Fog, SetDefLinear, "float start, float end, float density", "Sets the default fog to linear" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + + plFogEnvironment env; + + env = pfConsole::GetPipeline()->GetDefaultFogEnviron(); + env.Set( params[ 0 ], params[ 1 ], params[ 2 ] ); + pfConsole::GetPipeline()->SetDefaultFogEnviron( &env ); +} + +PF_CONSOLE_CMD( Graphics_Renderer_Fog, SetDefExp, "float end, float density", "Sets the default fog to linear" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + + plFogEnvironment env; + + env = pfConsole::GetPipeline()->GetDefaultFogEnviron(); + env.SetExp( plFogEnvironment::kExpFog, params[ 0 ], params[ 1 ] ); + pfConsole::GetPipeline()->SetDefaultFogEnviron( &env ); +} + +PF_CONSOLE_CMD( Graphics_Renderer_Fog, SetDefExp2, "float end, float density", "Sets the default fog to exp^2" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + + plFogEnvironment env; + + env = pfConsole::GetPipeline()->GetDefaultFogEnviron(); + env.SetExp( plFogEnvironment::kExp2Fog, params[ 0 ], params[ 1 ] ); + pfConsole::GetPipeline()->SetDefaultFogEnviron( &env ); +} + +//// Graphics.Show Subgroups ////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_SUBGROUP( Graphics, Show ); + +PF_CONSOLE_CMD( Graphics_Show, Bounds, "", "Toggle object bounds display") +{ + hsBool on = !pfConsole::GetPipeline()->IsDebugFlagSet( plPipeDbg::kFlagShowAllBounds ); + pfConsole::GetPipeline()->SetDebugFlag( plPipeDbg::kFlagShowAllBounds, on ); + + char str[ 256 ]; + sprintf( str, "Bounds now %s", on ? "visible" : "invisible" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, Sound, "", "Toggle sound fields visible") +{ + static hsBool on = false; + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kAudible | ((on = !on) ? plProxyDrawMsg::kCreate : plProxyDrawMsg::kDestroy)); + plgDispatch::MsgSend(msg); + if( on ) + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() | plDrawableSpans::kAudibleProxy); + else + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() & ~plDrawableSpans::kAudibleProxy); + + char str[ 256 ]; + sprintf( str, "Sounds now %s", on ? "visible" : "invisible" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, SingleSound, + "string sceneObject", "Toggles the proxy fields for a single sound object" ) +{ + char str[ 512 ]; + + const char *ageName = plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeName(); + + plKey key = FindSceneObjectByName( params[ 0 ], ageName, str, true ); + plSceneObject *obj = ( key != nil ) ? plSceneObject::ConvertNoRef( key->GetObjectPtr() ) : nil; + if( !obj ) + { + sprintf( str, "Cannot find sceneObject %s", (char *)params[ 0 ] ); + PrintString( str ); + return; + } + + const plAudioInterface *ai = obj->GetAudioInterface(); + if( ai == nil ) + { + sprintf( str, "sceneObject %s has no audio interface", (char *)params[ 0 ] ); + PrintString( str ); + return; + } + plKey aiKey = ai->GetKey(); + + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg( plProxyDrawMsg::kAudible | plProxyDrawMsg::kToggle ); + msg->SetBCastFlag( plMessage::kBCastByExactType, false ); + msg->AddReceiver( aiKey ); + plgDispatch::MsgSend( msg ); + + // Just in case we need to show them. Since we're toggling, we don't even know if it's being hidden and + // thus we should turn this off. Since this is purely for debugging, the slight performance hit on this + // is, imho, acceptable. + pfConsole::GetPipeline()->SetDrawableTypeMask( pfConsole::GetPipeline()->GetDrawableTypeMask() | plDrawableSpans::kAudibleProxy ); + + char str2[ 256 ]; + sprintf( str2, "Toggling proxies on sceneObject %s", (char *)params[ 0 ] ); + PrintString( str2 ); +} + +PF_CONSOLE_CMD( Graphics_Show, SoundOnly, "", "Toggle only sound fields visible") +{ + static hsBool on = false; + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kAudible | ((on = !on) ? plProxyDrawMsg::kCreate : plProxyDrawMsg::kDestroy)); + plgDispatch::MsgSend(msg); + static UInt32 oldMask = plDrawableSpans::kNormal; + if( on ) + { + oldMask = pfConsole::GetPipeline()->GetDrawableTypeMask(); + pfConsole::GetPipeline()->SetDrawableTypeMask(plDrawableSpans::kAudibleProxy); + } + else + { + pfConsole::GetPipeline()->SetDrawableTypeMask(oldMask); + } + char str[ 256 ]; + sprintf( str, on ? "Now showing only sound proxies" : "Restoring previous render state" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, OccSnap, "", "Take snapshot of current occlusion and render (or toggle)") +{ + UInt32 flag = plPipeDbg::kFlagOcclusionSnap; + hsBool on = !pfConsole::GetPipeline()->IsDebugFlagSet(flag); + + pfConsole::GetPipeline()->SetDebugFlag( flag, on ); + if( on ) + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() | plDrawableSpans::kOccSnapProxy); + else + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() & ~plDrawableSpans::kOccSnapProxy); + + char str[ 256 ]; + sprintf( str, "Active Occluders now %s", on ? "visible" : "invisible" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, OccSnapOnly, "", "Take snapshot of current occlusion and render (or toggle)") +{ + UInt32 flag = plPipeDbg::kFlagOcclusionSnap; + hsBool on = !pfConsole::GetPipeline()->IsDebugFlagSet(flag); + + static UInt32 oldMask = pfConsole::GetPipeline()->GetDrawableTypeMask(); + + pfConsole::GetPipeline()->SetDebugFlag( flag, on ); + if( on ) + pfConsole::GetPipeline()->SetDrawableTypeMask(plDrawableSpans::kOccSnapProxy); + else + pfConsole::GetPipeline()->SetDrawableTypeMask(oldMask); + + char str[ 256 ]; + sprintf( str, "Active Occluders now %s", on ? "visible" : "invisible" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, Occluders, "", "Toggle occluder geometry visible") +{ + static hsBool on = false; + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kOccluder | ((on = !on) ? plProxyDrawMsg::kCreate : plProxyDrawMsg::kDestroy)); + plgDispatch::MsgSend(msg); + + if( on ) + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() | plDrawableSpans::kOccluderProxy); + else + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() & ~plDrawableSpans::kOccluderProxy); + + char str[ 256 ]; + sprintf( str, "Occluders now %s", on ? "visible" : "invisible" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, OccludersOnly, "", "Toggle only occluder geometry visible") +{ + static hsBool on = false; + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kOccluder | ((on = !on) ? plProxyDrawMsg::kCreate : plProxyDrawMsg::kDestroy)); + plgDispatch::MsgSend(msg); + static UInt32 oldMask = plDrawableSpans::kNormal; + if( on ) + { + oldMask = pfConsole::GetPipeline()->GetDrawableTypeMask(); + pfConsole::GetPipeline()->SetDrawableTypeMask(plDrawableSpans::kOccluderProxy); + } + else + { + pfConsole::GetPipeline()->SetDrawableTypeMask(oldMask); + } + char str[ 256 ]; + sprintf( str, on ? "Now showing only occluder proxies" : "Restoring previous render state" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, Physicals, "", "Toggle Physical geometry visible") +{ + static hsBool on = false; + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kPhysical | ((on = !on) ? plProxyDrawMsg::kCreate : plProxyDrawMsg::kDestroy)); + plgDispatch::MsgSend(msg); + if( on ) + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() | plDrawableSpans::kPhysicalProxy); + else + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() & ~plDrawableSpans::kPhysicalProxy); + + char str[ 256 ]; + sprintf( str, "Physicals now %s", on ? "visible" : "invisible" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, PhysicalsOnly, "", "Toggle only Physical geometry visible") +{ + static hsBool on = false; + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kPhysical | ((on = !on) ? plProxyDrawMsg::kCreate : plProxyDrawMsg::kDestroy)); + plgDispatch::MsgSend(msg); + static UInt32 oldMask = plDrawableSpans::kNormal; + if( on ) + { + oldMask = pfConsole::GetPipeline()->GetDrawableTypeMask(); + pfConsole::GetPipeline()->SetDrawableTypeMask(plDrawableSpans::kPhysicalProxy); + } + else + { + pfConsole::GetPipeline()->SetDrawableTypeMask(oldMask); + } + char str[ 256 ]; + sprintf( str, on ? "Now showing only physics proxies" : "Restoring previous render state" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, Normal, "", "Toggle normal geometry visible") +{ + static hsBool on = true; + if( on = !on ) + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() | plDrawableSpans::kNormal); + else + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() & ~plDrawableSpans::kNormal); + + char str[ 256 ]; + sprintf( str, "Normal geometry now %s", on ? "visible" : "invisible" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, NormalOnly, "", "Toggle only normal geometry visible") +{ + static hsBool on = false; + static UInt32 oldMask = plDrawableSpans::kNormal; + if( on = !on ) + { + oldMask = pfConsole::GetPipeline()->GetDrawableTypeMask(); + pfConsole::GetPipeline()->SetDrawableTypeMask(plDrawableSpans::kNormal); + } + else + { + pfConsole::GetPipeline()->SetDrawableTypeMask(oldMask); + } + char str[ 256 ]; + sprintf( str, on ? "Now showing only normal geometry" : "Restoring previous render state" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, Lights, "", "Toggle visible proxies for lights") +{ + static hsBool on = false; + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kLight | ((on = !on) ? plProxyDrawMsg::kCreate : plProxyDrawMsg::kDestroy)); + plgDispatch::MsgSend(msg); + if( on ) + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() | plDrawableSpans::kLightProxy); + else + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() & ~plDrawableSpans::kLightProxy); + + char str[ 256 ]; + sprintf( str, "Lights now %s", on ? "visible" : "invisible" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, LightsOnly, "", "Toggle visible proxies for lights and everything else invisible") +{ + static hsBool on = false; + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kLight | ((on = !on) ? plProxyDrawMsg::kCreate : plProxyDrawMsg::kDestroy)); + plgDispatch::MsgSend(msg); + static UInt32 oldMask = plDrawableSpans::kNormal; + if( on ) + { + oldMask = pfConsole::GetPipeline()->GetDrawableTypeMask(); + pfConsole::GetPipeline()->SetDrawableTypeMask(plDrawableSpans::kLightProxy); + } + else + { + pfConsole::GetPipeline()->SetDrawableTypeMask(oldMask); + } + char str[ 256 ]; + sprintf( str, on ? "Now showing only light proxies" : "Restoring previous render state" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics_Show, Clicks, "", "Toggle visible proxies for clicks") +{ + static hsBool on = false; + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kCamera | ((on = !on) ? plProxyDrawMsg::kCreate : plProxyDrawMsg::kDestroy)); + plgDispatch::MsgSend(msg); + if( on ) + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() | plDrawableSpans::kCameraProxy); + else + pfConsole::GetPipeline()->SetDrawableTypeMask(pfConsole::GetPipeline()->GetDrawableTypeMask() & ~plDrawableSpans::kCameraProxy); + + char str[ 256 ]; + sprintf( str, "Clicks now %s", on ? "visible" : "invisible" ); + PrintString( str ); +} + + +PF_CONSOLE_CMD( Graphics_Show, ClickOnly, "", "Toggle visible proxies for click points") +{ + static hsBool on = false; + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kCamera | ((on = !on) ? plProxyDrawMsg::kCreate : plProxyDrawMsg::kDestroy)); + plgDispatch::MsgSend(msg); + static UInt32 oldMask = plDrawableSpans::kNormal; + if( on ) + { + oldMask = pfConsole::GetPipeline()->GetDrawableTypeMask(); + pfConsole::GetPipeline()->SetDrawableTypeMask(plDrawableSpans::kCameraProxy); + } + else + { + pfConsole::GetPipeline()->SetDrawableTypeMask(oldMask); + } + char str[ 256 ]; + sprintf( str, on ? "Now showing only camera proxies" : "Restoring previous render state" ); + PrintString( str ); +} + +PF_CONSOLE_CMD( Graphics, ForceSecondMonitor, "bool v", "Run the game on the second monitor" ) +{ + plPipeline::fInitialPipeParams.ForceSecondMonitor = (bool) params[0]; +} + +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Graphics, Width, "int w", "Initializes width" ) +{ + plPipeline::fInitialPipeParams.Width = (int) params[0]; +} + +PF_CONSOLE_CMD( Graphics, Height, "int h", "Initializes height" ) +{ + plPipeline::fInitialPipeParams.Height = (int) params[0]; +} +PF_CONSOLE_CMD(Graphics, ColorDepth, "int colordepth", "Initializes color depth") +{ + plPipeline::fInitialPipeParams.ColorDepth = (int) params[0]; +} + +PF_CONSOLE_CMD( Graphics, Windowed, "bool w", "Initialize Windowed Mode") +{ + plPipeline::fInitialPipeParams.Windowed = (bool) params[0]; +} + +PF_CONSOLE_CMD( Graphics, TextureQuality, "int quality", "Initialize texture quality") +{ + int texqual = (int)params[0]; + if (texqual < 0) + texqual = 0; + else if (texqual > 2) + texqual = 2; + + plPipeline::fInitialPipeParams.TextureQuality = texqual; +} + +PF_CONSOLE_CMD( Graphics, AntiAliasAmount, "int aa", "Init AA Level") +{ + plPipeline::fInitialPipeParams.AntiAliasingAmount = (int) params[0]; +} + +PF_CONSOLE_CMD( Graphics, AnisotropicLevel, "int l", "Init Aniso Level" ) +{ + plPipeline::fInitialPipeParams.AnisotropicLevel = (int) params[0]; +} + +PF_CONSOLE_CMD( Graphics, EnableVSync, "bool b", "Init VerticalSync" ) +{ + plPipeline::fInitialPipeParams.VSync = (bool) params[0]; +} + +PF_CONSOLE_CMD( Graphics, EnablePlanarReflections, "bool", "Enable the draw and update of planar reflections" ) +{ + bool enable = (bool)params[0]; + plDynamicCamMap::SetEnabled(enable); +} + +////////////////////////////////////////////////////////////////////////////// +//// App Group Commands ////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +PF_CONSOLE_GROUP( App ) // Defines a main command group + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( App, + Event, + "string obj, string evType, float s, int reps", + "string obj, string evType, float s, int reps" ) +{ + + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + plSceneObject* obj = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if( !obj ) + { + strcat(str, " - Not Found!"); + PrintString(str); + return; + } + + plKey receiver = nil; + PrintString(str); + + int i; + for( i = 0; i < obj->GetNumModifiers(); i++ ) + { + if( plSimpleModifier::ConvertNoRef(obj->GetModifier(i)) ) + { + receiver = obj->GetModifier(i)->GetKey(); + break; + } + } + if( !receiver ) + { + strcat(str, " - Modifier Not Found!"); + PrintString(str); + return; + } + + plAnimCmdMsg* cmd = TRACKED_NEW plAnimCmdMsg; + cmd->SetSender(plClient::GetInstance()->GetKey()); + cmd->SetCmd(plAnimCmdMsg::kAddCallbacks); +#if 1 + cmd->AddReceiver(receiver); +#else + cmd->AddReceiver(key); + cmd->SetBCastFlag(plMessage::kPropagateToModifiers, true); +#endif + + float secs = 0; + int reps = 1; + + char* eventStr = params[1]; + CallbackEvent event; + if( !_stricmp(eventStr, "Start") ) + { + event = kStart; + } + else + if( !_stricmp(eventStr, "Stop") ) + { + event = kStop; + } + else + if( !_stricmp(eventStr, "Time") ) + { + event = kTime; + secs = params[2]; + } + if( numParams > 3 ) + { + reps = params[3]; + } + reps--; + + plEventCallbackMsg* callback = TRACKED_NEW plEventCallbackMsg(plClient::GetInstance()->GetKey(), event, 0, secs, reps); + cmd->AddCallback(callback); + hsRefCnt_SafeUnRef(callback); + plgDispatch::MsgSend(cmd); +} + +PF_CONSOLE_CMD( App, + Sound, + "string obj, string evType, float s, int reps", + "string obj, string evType, float s, int reps" ) +{ + + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + plSceneObject* obj = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if( !obj ) + { + strcat(str, " - Not Found!"); + PrintString(str); + return; + } + + PrintString(str); + + plSoundMsg* cmd = TRACKED_NEW plSoundMsg; + cmd->SetSender(plClient::GetInstance()->GetKey()); + cmd->SetCmd(plSoundMsg::kAddCallbacks); + cmd->AddReceiver(key); + + float secs = 0; + int reps = -1; + + char* eventStr = params[1]; + CallbackEvent event; + if( !_stricmp(eventStr, "Start") ) + { + event = kStart; + } + else + if( !_stricmp(eventStr, "Stop") ) + { + event = kStop; + } + else + if( !_stricmp(eventStr, "Time") ) + { + event = kTime; + secs = params[2]; + } + if( numParams > 3 ) + { + reps = params[3]; + } + reps--; + + plEventCallbackMsg* callback = TRACKED_NEW plEventCallbackMsg(plClient::GetInstance()->GetKey(), event, 0, secs, reps); + cmd->AddCallback(callback); + hsRefCnt_SafeUnRef(callback); + plgDispatch::MsgSend(cmd); +} + +PF_CONSOLE_CMD( App, + Overlay, + "string name, ...", // paramList + "Enable/Disable/Toggle display of named CamView object" ) +{ + char str[256]; + char* name = params[0]; + plKey key = FindSceneObjectByName(name, nil, str); + if( !key ) + { + sprintf(str, "%s - Not Found!", name); + PrintString(str); + return; + } + plSceneObject* obj = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if( !obj ) + { + sprintf(str, "%s - Not Found!", name); + PrintString(str); + return; + } + + int i; + for( i = 0; i < obj->GetNumModifiers(); i++ ) + { + if( plPostEffectMod::ConvertNoRef(obj->GetModifier(i)) ) + break; + } + if( i >= obj->GetNumModifiers() ) + { + sprintf(str, "%s - No CamView Modifier found!", name); + PrintString(str); + return; + } + strcpy(str, name); + + plAnimCmdMsg* cmd = TRACKED_NEW plAnimCmdMsg(nil, obj->GetModifier(i)->GetKey(), nil); + + if( numParams > 1 ) + { + bool on = bool(params[1]); + if( on ) + { + cmd->SetCmd(plAnimCmdMsg::kContinue); + strcat(str, " - Enabled"); + } + else + { + cmd->SetCmd(plAnimCmdMsg::kStop); + strcat(str, " - Disabled"); + } + } + else + { + cmd->SetCmd(plAnimCmdMsg::kToggleState); + strcat(str, " - Toggled"); + } + plgDispatch::MsgSend(cmd); + PrintString(str); +} + +PF_CONSOLE_CMD( App, // groupName + TimeClamp, // fxnName + "float maxSecsPerFrame", // paramList + "Clamp elapsed game time per frame (b4 scale)" ) // helpString +{ + float s = params[0]; + hsTimer::SetTimeClamp( s ); + + char str[256]; + sprintf(str, "Time clamped to %f secs", s); + PrintString( str ); +} + +PF_CONSOLE_CMD( App, // groupName + TimeSmoothingClamp, // fxnName + "float maxSecsPerFrame", // paramList + "Clamp max elapsed time that we'll smooth frame deltas" ) // helpString +{ + float s = params[0]; + hsTimer::SetTimeSmoothingClamp( s ); + + char str[256]; + sprintf(str, "Time smoothing clamped to %f secs", s); + PrintString( str ); +} + +PF_CONSOLE_CMD( App, // groupName + FrameInc, // fxnName + "float msPerFrame", // paramList + "Advance exactly msPerFrame milliseconds each frame" ) // helpString +{ + float s = params[0]; + s *= 1.e-3f; + hsTimer::SetFrameTimeInc( s ); + + char str[256]; + sprintf(str, "Frame advancing %f per frame (in frame time)", s * 1.e3f ); + PrintString( str ); +} + +PF_CONSOLE_CMD( App, // groupName + RealTime, // fxnName + "", // paramList + "Run in realtime" ) // helpString +{ + hsTimer::SetRealTime( true ); + + char str[256]; + sprintf(str, "Now running real time"); + PrintString( str ); +} + +PF_CONSOLE_CMD( App, // groupName + FrameTime, // fxnName + "", // paramList + "Run in frametime" ) // helpString +{ + hsTimer::SetRealTime( false ); + + char str[256]; + sprintf(str, "Now running frame time"); + PrintString( str ); +} + +PF_CONSOLE_CMD( App, // groupName + ScaleTime, // fxnName + "float s", // paramList + "Scale factor for time (e.g. ScaleTime 2 doubles speed of game)" ) // helpString +{ + float s = params[0]; + hsTimer::SetTimeScale( s ); + + char str[256]; + sprintf(str, "Time scaled to %4.4f percent", s * 100.f ); + PrintString( str ); +} + +#include "../plInputCore/plSceneInputInterface.h" + +PF_CONSOLE_CMD( App, // groupName + ShowLOS, // fxnName + "", // paramList + "Show object LOS hits" ) // helpString +{ + char str[256]; + if (plSceneInputInterface::fShowLOS) + { + plSceneInputInterface::fShowLOS = false; + sprintf(str, "Stop displaying LOS hits"); + } + else + { + plSceneInputInterface::fShowLOS = true; + sprintf(str, "Start showing LOS hits"); + } + PrintString( str ); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( App, // groupName + Quit, // fxnName + "", // paramList + "Quit the client app" ) // helpString +{ + if( plClient::GetInstance() ) + PostMessage(plClient::GetInstance()->GetWindowHandle(), + WM_SYSCOMMAND, + SC_CLOSE, + 0); +} + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD(App, + AuxInitDir, + "string pathName", + "Set an auxiliary init directory to read") +{ + if( plClient::GetInstance() ) + plClient::GetInstance()->SetAuxInitDir(params[0]); +} + + +PF_CONSOLE_CMD( App, // groupName + GetBuildDate, // fxnName + "", // paramList + "Prints the date and time this build was created" ) // helpString +{ + char str[256]; + sprintf(str, "This Plasma 2.0 client built at %s on %s.", pnBuildDates::fBuildTime, pnBuildDates::fBuildDate ); + PrintString( str ); +} + +PF_CONSOLE_CMD( App, // groupName + GetBranchDate, // fxnName + "", // paramList + "Prints the date of the branch this code was produced from, or \"Pre-release\" if it is from the main code" ) // helpString +{ + char str[256]; + sprintf(str, "The branch date for this Plasma 2.0 client is: %s.", pnBuildDates::fBranchDate ); + PrintString( str ); +} + +PF_CONSOLE_CMD(App, + LowPriority, + "", + "Set low priority for this process") +{ + SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS ); + PrintString( "Set process priority to lowest setting" ); +} + + +PF_CONSOLE_CMD(App, + VerifyUnloaded, + "string age", + "Verify the given age is really unloaded into logfile logs/.log") +{ + hsAssert(0, "Fixme"); + char* age = params[0]; + char str[256]; + sprintf(str, "%s.log", age); +// hsgResMgr::ResMgr()->VerifyAgeUnloaded(str, age); + + sprintf(str, "Verification of age %s complete", age); + PrintString(str); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +#ifdef LIMIT_CONSOLE_COMMANDS // for now, disable languages in external clients +PF_CONSOLE_CMD(App, + SetLanguage, + "string language", + "Set the language (English, French, German, Spanish, Italian, or Japanese)") +{ + if (pfConsole::GetPipeline()) + { + PrintString("This command must be used in an .ini file"); + return; + } + + if (stricmp(params[0], "english") == 0) + plLocalization::SetLanguage(plLocalization::kEnglish); + else if (stricmp(params[0], "french") == 0) + plLocalization::SetLanguage(plLocalization::kFrench); + else if (stricmp(params[0], "german") == 0) + plLocalization::SetLanguage(plLocalization::kGerman); + else if (stricmp(params[0], "spanish") == 0) + plLocalization::SetLanguage(plLocalization::kSpanish); + else if (stricmp(params[0], "italian") == 0) + plLocalization::SetLanguage(plLocalization::kItalian); + else if (stricmp(params[0], "japanese") == 0) + plLocalization::SetLanguage(plLocalization::kJapanese); + +} +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD(App, + DemoMode, + "", + "Set the app to demo mode") +{ + if (pfConsole::GetPipeline()) + { + PrintString("This command must be used in an .ini file"); + return; + } + + plNetClientApp::GetInstance()->SetFlagsBit(plNetClientApp::kDemoMode); +} + +PF_CONSOLE_CMD(App, + BounceLogs, + "", + "Clear all log files.") +{ + plStatusLogMgr::GetInstance().BounceLogs(); +} + +////////////////////////////////////////////////////////////////////////////// +//// Dispatch Group Commands ///////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +#include "pfDispatchLog.h" + +PF_CONSOLE_GROUP( Dispatch ) // Defines a main command group +PF_CONSOLE_SUBGROUP( Dispatch, Log ) // Creates a sub-group under a given group + +PF_CONSOLE_CMD( Dispatch_Log, // groupName + LongReceives, // fxnName + "", // paramList + "Log long msg receives (over 50 ms)" ) // helpString +{ + plDispatchLog::InitInstance(); + plDispatchLog::GetInstance()->SetFlags(plDispatchLog::GetInstance()->GetFlags() | plDispatchLog::kLogLongReceives); +} + +PF_CONSOLE_CMD( Dispatch_Log, // groupName + AddFilterType, // fxnName + "string className", // paramList + "Adds a type filter to the Dispatch Logger" ) // helpString +{ + plDispatchLog::InitInstance(); + plDispatchLog::GetInstance()->AddFilterType(plFactory::FindClassIndex(params[0])); +} + +PF_CONSOLE_CMD( Dispatch_Log, // groupName + AddFilterExactType, // fxnName + "string className", // paramList + "Adds an exact type filter to the Dispatch Logger" ) // helpString +{ + plDispatchLog::InitInstance(); + plDispatchLog::GetInstance()->AddFilterExactType(plFactory::FindClassIndex(params[0])); +} + +PF_CONSOLE_CMD( Dispatch_Log, // groupName + RemoveFilterType, // fxnName + "string className", // paramList + "Removes a type filter to the Dispatch Logger" ) // helpString +{ + plDispatchLog::InitInstance(); + plDispatchLog::GetInstance()->RemoveFilterType(plFactory::FindClassIndex(params[0])); +} + +PF_CONSOLE_CMD( Dispatch_Log, // groupName + RemoveFilterExactType, // fxnName + "string className", // paramList + "Removes an exact type filter to the Dispatch Logger" ) // helpString +{ + plDispatchLog::InitInstance(); + plDispatchLog::GetInstance()->RemoveFilterExactType(plFactory::FindClassIndex(params[0])); +} + +PF_CONSOLE_CMD( Dispatch_Log, // groupName + Include, // fxnName + "", // paramList + "Sets Dispatch Log filters to be treated as an include list" ) // helpString +{ + plDispatchLog::InitInstance(); + plDispatchLog::GetInstance()->SetFlags(plDispatchLog::GetInstance()->GetFlags() | plDispatchLog::kInclude); +} + +PF_CONSOLE_CMD( Dispatch_Log, // groupName + Exclude, // fxnName + "", // paramList + "Sets Dispatch Log filters to be treated as an exclude list" ) // helpString +{ + plDispatchLog::InitInstance(); + plDispatchLog::GetInstance()->SetFlags(plDispatchLog::GetInstance()->GetFlags() & ~plDispatchLog::kInclude); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +////////////////////////////////////////////////////////////////////////////// +//// ResManager/Registry Commands //////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_GROUP( Registry ) // Defines a main command group + +PF_CONSOLE_CMD( Registry, ToggleDebugStats, "", "Toggles the debug statistics screen for the registry" ) +{ + plResManagerHelper *helper = plResManagerHelper::GetInstance(); + if( helper == nil ) + { + PrintString( "ERROR: ResManager helper object not initialized." ); + return; + } + + static bool on = false; + if( on ) + { + helper->EnableDebugScreen( false ); + PrintString( "ResManager debug stats disabled" ); + } + else + { + helper->EnableDebugScreen( true ); + plStatusLogMgr::GetInstance().SetCurrStatusLog( "ResManager Status" ); + PrintString( "ResManager debug stats enabled" ); + } +} + +PF_CONSOLE_CMD( Registry, SetLoggingLevel, "int level", "Sets the logging level for the registry. 0 is no logging, 3 is max detail." ) +{ + int newLevel = params[ 0 ]; + + if( newLevel < 0 || newLevel > 4 ) + { + PrintString( "ERROR: Invalid level specified. Valid levels are 0-3." ); + return; + } + + plResMgrSettings::Get().SetLoggingLevel( (UInt8)newLevel ); + { + char msg[ 128 ]; + sprintf( msg, "Registry logging set to %s", ( newLevel == 0 ) ? "none" : ( newLevel == 1 ) ? "basic" : + ( newLevel == 2 ) ? "detailed" : ( newLevel == 3 ) ? "object-level" + : "object-read-level" ); + PrintString( msg ); + } +} + +class plActiveRefPeekerKey : public plKeyImp +{ + public: + UInt16 PeekNumNotifies() { return GetNumNotifyCreated(); } + plRefMsg* PeekNotifyCreated(int i) { return GetNotifyCreated(i); } + hsBool PeekIsActiveRef(int i) const { return IsActiveRef(i); } +}; + +// Not static so others can call it - making it even handier +void MyHandyPrintFunction( const plKey &obj, void (*PrintString)( const char * ) ) +{ + plActiveRefPeekerKey *peeker = (plActiveRefPeekerKey *)(plKeyImp *)obj; + + if( peeker->GetUoid().IsClone() ) + PrintStringF( PrintString, "%d refs on %s, clone %d:%d: loaded=%d", + peeker->PeekNumNotifies(), obj->GetUoid().GetObjectName(), + peeker->GetUoid().GetCloneID(), peeker->GetUoid().GetClonePlayerID(), + obj->ObjectIsLoaded() ? 1 : 0); + else + PrintStringF( PrintString, "%d refs on %s: loaded=%d", + peeker->PeekNumNotifies(), obj->GetUoid().GetObjectName(), obj->ObjectIsLoaded() ? 1 : 0 ); + + if( peeker->PeekNumNotifies() == 0 ) + return; + + UInt32 a, i, j, limit = 30, count = 0; + for( a = 0; a < 2; a++ ) + { + PrintString( ( a == 0 ) ? " Active:" : " Passive:" ); + + for( i = 0; i < peeker->PeekNumNotifies(); i++ ) + { + if( ( a == 0 && peeker->PeekIsActiveRef( i ) ) || ( a == 1 && !peeker->PeekIsActiveRef( i ) ) ) + { + plRefMsg *msg = peeker->PeekNotifyCreated( i ); + if( msg != nil ) + { + for( j = 0; j < msg->GetNumReceivers(); j++ ) + { + if( limit == 0 ) + count++; + else + { + limit--; + + const plKey rcvr = msg->GetReceiver( j ); + PrintStringF( PrintString, " %s:%s", plFactory::GetNameOfClass( rcvr->GetUoid().GetClassType() ), rcvr->GetUoid().GetObjectName() ); + } + } + } + } + } + } + + if( count > 0 ) + PrintStringF( PrintString, "...and %d others", count ); +} + +PF_CONSOLE_CMD( Registry, ListRefs, "string keyType, string keyName", "For the given key (referenced by type and name), lists all of " + "the objects who currently have active refs on it." ) +{ + char result[ 256 ]; + plKey obj = FindObjectByNameAndType( params[ 1 ], params[ 0 ], nil, result); + if( obj == nil ) + { + PrintString( result ); + return; + } + + MyHandyPrintFunction( obj, PrintString ); + + plActiveRefPeekerKey *peeker = (plActiveRefPeekerKey *)(plKeyImp *)obj; + if( peeker->GetNumClones() > 0 ) + { + UInt32 i; + for( i = 0; i < peeker->GetNumClones(); i++ ) + { + MyHandyPrintFunction( peeker->GetCloneByIdx( i ), PrintString ); + } + } +} + +PF_CONSOLE_CMD(Registry, LogReadTimes, "", "Dumps the time for each object read to a file") +{ + ((plResManager*)hsgResMgr::ResMgr())->LogReadTimes(true); +} + +#endif // LIMIT_CONSOLE_COMMANDS + + +////////////////////////////////////////////////////////////////////////////// +//// Camera Group Commands /////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +PF_CONSOLE_GROUP( Camera ) // Defines a main command group + +#ifndef LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_CMD( Camera, AvatarVisible1stPerson, "bool b", "turn avatar visibility in 1st person on or off") +{ + bool b = params[0]; + plCameraBrain1_FirstPerson::fDontFade = b; +} + +PF_CONSOLE_CMD( Camera, FallTimerDelay, "float b", "fall timer delay") +{ + hsScalar f = params[0]; + plVirtualCam1::fFallTimerDelay = f; +} + + +PF_CONSOLE_CMD( Camera, Force3rdPersonOneshots, "bool b", "force camera to 3rd person for oneshots on or off") +{ + bool b = params[0]; + plAvOneShotTask::fForce3rdPerson = b; +} + +PF_CONSOLE_CMD( Camera, Force3rdPersonMultistage, "bool b", "force camera to 3rd person for multistage on or off") +{ + bool b = params[0]; + plAvBrainGeneric::fForce3rdPerson = b; +} + + +PF_CONSOLE_CMD( Camera, // groupName + Next, // fxnName + "", // paramList + "Set the virtual camera to go to the next camera in the scene" ) // helpString +{ + plUoid pU1( kVirtualCamera1_KEY ); + plKey fLOS1 = hsgResMgr::ResMgr()->FindKey( pU1 ); + if (fLOS1) + { + plVirtualCam1::ConvertNoRef(fLOS1->GetObjectPtr())->Next(); + return; + } + +} + +PF_CONSOLE_CMD( Camera, // groupName + IgnoreRegions, // fxnName + "bool b", // paramList + "Switch on / off camera regions" ) // helpString +{ + bool b = params[0]; + + plUoid pU1( kVirtualCamera1_KEY ); + plKey fLOS1 = hsgResMgr::ResMgr()->FindKey( pU1 ); + if (fLOS1) + { + plVirtualCam1::ConvertNoRef(fLOS1->GetObjectPtr())->CameraRegions(b); + return; + } + +} + +PF_CONSOLE_CMD( Camera, // groupName + LogFOV, // fxnName + "bool b", // paramList + "Switch on / off verbose camera FOV change logging" ) // helpString +{ + bool b = params[0]; + + plUoid pU1( kVirtualCamera1_KEY ); + plKey fLOS1 = hsgResMgr::ResMgr()->FindKey( pU1 ); + if (fLOS1) + { + plVirtualCam1::ConvertNoRef(fLOS1->GetObjectPtr())->LogFOV(b); + return; + } + +} + +PF_CONSOLE_CMD( Camera, // groupName + Prev, // fxnName + "", // paramList + "Set the virtual camera to go to the prev camera in the scene" ) // helpString +{ + plUoid pU1( kVirtualCamera1_KEY ); + plKey fLOS1 = hsgResMgr::ResMgr()->FindKey( pU1 ); + if (fLOS1) + { + plVirtualCam1::ConvertNoRef(fLOS1->GetObjectPtr())->Prev(); + return; + } + +} + +PF_CONSOLE_CMD( Camera, // groupName + SetFOV, // fxnName + "float x, float y", // paramList + "Set the field of view for all cameras" ) // helpString +{ + float x = params[0]; + float y = params[1]; + plUoid pU1( kVirtualCamera1_KEY ); + plKey fLOS1 = hsgResMgr::ResMgr()->FindKey( pU1 ); + if (fLOS1) + { + plVirtualCam1::SetFOV(x,y); + return; + } +} + +PF_CONSOLE_CMD( Camera, // groupName + Drive, // fxnName + "", // paramList + "Toggle drive mode" ) // helpString +{ + plVirtualCam1::Instance()->Drive(); +} + + + +PF_CONSOLE_CMD( Camera, // groupName + IncreaseDriveTurnRate, // fxnName + "", // paramList + "increase drive turn rate" ) // helpString +{ + plCameraBrain1_Drive::fTurnRate += 20.0f; + +} + +PF_CONSOLE_CMD( Camera, // groupName + DecreaseDriveTurnRate, // fxnName + "", // paramList + "decrease drive turn rate" ) // helpString +{ + plCameraBrain1_Drive::fTurnRate -= 20.0f; + if (plCameraBrain1_Drive::fTurnRate < 0.0) + plCameraBrain1_Drive::fTurnRate = 20.0f; + +} + + +PF_CONSOLE_CMD( Camera, SwitchTo, "string cameraName", "Switch to the named camera") +{ + char str[256]; + char foo[256]; + sprintf(foo, "%s_", (const char*)params[0]); + plKey key = FindObjectByNameAndType(foo, "plCameraModifier1", nil, str, true); + PrintString(str); + + if (key) + { + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetCmd(plCameraMsg::kResponderTrigger); + pMsg->SetCmd(plCameraMsg::kRegionPushCamera); + pMsg->SetNewCam(key); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + plgDispatch::MsgSend(pMsg); + } +} + +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Camera, // groupName + SetFallSpeeds, // fxnName + "float accel, float vel, float decel", // paramList + "Set camera fall speeds" ) // helpString +{ + float a = params[0]; + float v = params[1]; + float d = params[2]; + plCameraBrain1::fFallAccel = a; + plCameraBrain1::fFallVelocity = v; + plCameraBrain1::fFallDecel = d; +} + +PF_CONSOLE_CMD( Camera, // groupName + SetFallPOASpeeds, // fxnName + "float accel, float vel, float decel", // paramList + "Set camera fall speeds" ) // helpString +{ + float a = params[0]; + float v = params[1]; + float d = params[2]; + plCameraBrain1::fFallPOAAccel = a; + plCameraBrain1::fFallPOAVelocity = v; + plCameraBrain1::fFallPOADecel = d; +} + + +PF_CONSOLE_CMD( Camera, // groupName + SetGlobalAccel, // fxnName + "float x", // paramList + "Set global camera acceleration - must set Camera.UseSpeedOverrides to TRUE to see effect" ) // helpString +{ + float f = params[0]; + plVirtualCam1::Instance()->fAccel = f; +} + +PF_CONSOLE_CMD( Camera, // groupName + SetGlobalDecel, // fxnName + "float x", // paramList + "Set global camera deceleration - must set Camera.UseSpeedOverrides to TRUE to see effect" ) // helpString +{ + float f = params[0]; + plVirtualCam1::Instance()->fDecel = f; +} + +PF_CONSOLE_CMD( Camera, // groupName + SetGlobalVelocity, // fxnName + "float x", // paramList + "Set global camera velocity - must set Camera.UseSpeedOverrides to TRUE to see effect" ) // helpString +{ + float f = params[0]; + plVirtualCam1::Instance()->fVel = f; +} + +PF_CONSOLE_CMD( Camera, // groupName + UseSpeedOverrides, // fxnName + "bool b", // paramList + "Use console overrides for accel / decel" ) // helpString +{ + bool b = params[0]; + plVirtualCam1::Instance()->fUseAccelOverride = b; +} + +PF_CONSOLE_CMD( Camera, VerticalPanAlways, "bool b", "turn vertical panning on always when walking") +{ + bool b = params[0]; + plVirtualCam1::WalkPan3rdPerson = b; +} + +PF_CONSOLE_CMD( Camera, FirstPersonAlways, "bool b", "always in first person") +{ + bool b = params[0]; + plVirtualCam1::StayInFirstPersonForever = b; +} + + +#ifndef LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_CMD( Camera, // groupName + Freeze, // fxnName + "bool b", // paramList + "freeze the camera system" ) // helpString +{ + bool b = params[0]; + plVirtualCam1::Instance()->freeze = b; +} + +PF_CONSOLE_CMD( Camera, // groupName + AlwaysCut, // fxnName + "bool b", // paramList + "Forces camera transitions to always cut" ) // helpString +{ + bool b = params[0]; + plVirtualCam1::Instance()->alwaysCutForColin = b; +} + +#endif // LIMIT_CONSOLE_COMMANDS + +//////////////////////////////////////////////////////////////////////// +//// Logic Mod Group Commands /////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_GROUP( Logic ) + +static plLogicModBase *FindLogicMod(const char *name) +{ + char str[256]; + plKey key = FindObjectByNameAndType(name, "plLogicModifier", nil, str, true); + pfConsole::AddLine(str); + + if (key) + return plLogicModBase::ConvertNoRef(key->GetObjectPtr()); + + return nil; +} + +PF_CONSOLE_CMD( Logic, TriggerDetectorNum, "int detectorNum", "Triggers the detector with this number (from ListDetectors)") +{ + std::vector activatorNames; + plKeyFinder::Instance().GetActivatorNames(activatorNames); + + int activatorNum = params[0]; + if (activatorNum < 1 || activatorNum > activatorNames.size()) + { + PrintString("Detector number out of range"); + return; + } + + plLogicModBase *mod = FindLogicMod(activatorNames[activatorNum-1].c_str()); + if (mod) + mod->ConsoleTrigger(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); +} + +PF_CONSOLE_CMD( Logic, TriggerDetector, "string detectorComp", "Triggers the named detector component") +{ + plLogicModBase *mod = FindLogicMod((const char*)params[0]); + if (mod) + mod->ConsoleTrigger(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); +} + +PF_CONSOLE_CMD(Logic, EnableDetector, "string detectorComp, bool enable", "Enables/disables the named detector component") +{ + plLogicModBase *mod = FindLogicMod((const char*)params[0]); + if (mod) + { + plEnableMsg* enableMsg = TRACKED_NEW plEnableMsg; + enableMsg->SetCmd(params[1] ? plEnableMsg::kEnable : plEnableMsg::kDisable); + enableMsg->SetCmd(plEnableMsg::kAll); + enableMsg->Send(mod->GetKey()); + } +} + +static void ResponderSendTrigger(plKey responderKey, int responderState, bool fastForward = false) +{ + plNotifyMsg *msg = TRACKED_NEW plNotifyMsg; + + if (fastForward) + { + msg->fType = plNotifyMsg::kResponderFF; + } + else + { + msg->fType = plNotifyMsg::kActivator; + } + + msg->fState = 1; // Triggered + + // Setup the event data in case this is a OneShot responder that needs it + plKey playerKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + msg->AddPickEvent(playerKey, nil, true, hsPoint3(0,0,0) ); + + if (responderState != -1) + msg->AddResponderStateEvent(responderState); + + // Send it to the responder modifier + msg->AddReceiver(responderKey); + plgDispatch::MsgSend(msg); +} + +PF_CONSOLE_CMD( Logic, TriggerResponderNum, "int responderNum, ...", "Triggers the responder with this number (from ListResponders). (Optional: number of the state to switch to)") +{ + if (numParams > 2) + { + PrintString("Too many parameters"); + return; + } + + std::vector responderNames; + plKeyFinder::Instance().GetResponderNames(responderNames); + + int responderNum = params[0]; + if (responderNum < 1 || responderNum > responderNames.size()) + { + PrintString("Responder number out of range"); + return; + } + + int responderState = -1; + if (numParams == 2) + { + responderState = params[1]; + } + + char str[256]; + plKey key = FindObjectByNameAndType(responderNames[responderNum-1].c_str(), "plResponderModifier", nil, str, true); + PrintString(str); + + if (key) + ResponderSendTrigger(key, responderState); +} + +PF_CONSOLE_CMD( Logic, TriggerResponder, "string responderComp, ...", "Triggers the named responder component. (Optional: number of the state to switch to)") +{ + if (numParams > 2) + { + PrintString("Too many parameters"); + return; + } + + char str[256]; + plKey key = FindObjectByNameAndType(params[0], "plResponderModifier", nil, str, true); + PrintString(str); + + int responderState = -1; + if (numParams == 2) + { + responderState = params[1]; + } + + if (key) + ResponderSendTrigger(key, responderState); +} + +PF_CONSOLE_CMD( Logic, FastForwardResponder, "string responderComp, ...", "Fastforwards the named responder component. (Optional: number of the state to switch to)") +{ + if (numParams > 2) + { + PrintString("Too many parameters"); + return; + } + + char str[256]; + plKey key = FindObjectByNameAndType(params[0], "plResponderModifier", nil, str, true); + PrintString(str); + + int responderState = -1; + if (numParams == 2) + { + responderState = params[1]; + } + + if (key) + ResponderSendTrigger(key, responderState, true); +} + +PF_CONSOLE_CMD(Logic, ListDetectors, "", "Prints the names of the loaded detectors to the console") +{ + std::vector activatorNames; + plKeyFinder::Instance().GetActivatorNames(activatorNames); + + for (int i = 0; i < activatorNames.size(); i++) + { + char buf[256]; + sprintf(buf, "%d. %s", i+1, activatorNames[i].c_str()); + PrintString(buf); + } +} + +PF_CONSOLE_CMD(Logic, ListResponders, "", "Prints the names of the loaded responders to the console") +{ + std::vector responderNames; + plKeyFinder::Instance().GetResponderNames(responderNames); + + for (int i = 0; i < responderNames.size(); i++) + { + char buf[256]; + sprintf(buf, "%d. %s", i+1, responderNames[i].c_str()); + PrintString(buf); + } +} + +#include "../plModifier/plResponderModifier.h" + +PF_CONSOLE_CMD(Logic, ResponderAnimCue, "", "Toggle box being drawn on screen when a responder starts an anim") +{ + if (plResponderModifier::ToggleDebugAnimBox()) + PrintString("Responder Anim Cue On"); + else + PrintString("Responder Anim Cue Off"); +} + +PF_CONSOLE_CMD(Logic, ResponderNoLog, "string prefix", "Don't log responders that begin with the specified string") +{ + plResponderModifier::NoLogString(params[0]); +} + +#include "../plModifier/plDetectorLog.h" +PF_CONSOLE_CMD(Logic, WriteDetectorLog, "", "Write detector log to logfile") +{ + DetectorDoLogfile(); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +//////////////////////////////////////////////////////////////////////// +//// Audio System Group Commands /////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +PF_CONSOLE_GROUP( Audio ) + +PF_CONSOLE_CMD( Audio, Enable, "bool on", "Switch DirectX Audio on or off at runtime") +{ + bool on = params[0]; + plgAudioSys::Activate( on ); +} + +PF_CONSOLE_CMD( Audio, UseHardware, "bool on", "Enable audio hardware acceleration") +{ + bool on = params[0]; + plgAudioSys::SetUseHardware( on ); +} + +PF_CONSOLE_CMD( Audio, UseEAX, "bool on", "Enable EAX sound acceleration (requires hardware acceleration)") +{ + bool on = params[0]; + plgAudioSys::EnableEAX( on ); +} + +PF_CONSOLE_CMD( Audio, Initialize, "bool on", "Set to false to completely disable audio playback in plasma") +{ + bool on = params[0]; + plgAudioSys::SetActive(on); +} + +PF_CONSOLE_CMD( Audio, Restart, "", "Restarts the audio system" ) +{ + plgAudioSys::Restart(); +} + +PF_CONSOLE_CMD( Audio, MuteAll, "bool on", "Mute or unmute all sounds") +{ + plgAudioSys::SetMuted( (bool)params[ 0 ] ); +} + +PF_CONSOLE_CMD( Audio, SetDistanceModel, "int type", "Sets the distance model for all 3d sounds") +{ + if(plgAudioSys::Sys()) + { + plgAudioSys::Sys()->SetDistanceModel((int) params[0]); + } +} + +PF_CONSOLE_CMD( Audio, LogStreamingUpdates, "bool on", "Logs every buffer fill for streaming sounds") +{ + plgAudioSys::SetLogStreamingUpdates((bool) params[0]); +} + +PF_CONSOLE_CMD( Audio, SetAllChannelVolumes, "float soundFX, float music, float ambience, float voice, float gui", "Sets the master volume of all the given audio channels.") +{ + plgAudioSys::ASChannel chans[ 6 ] = { plgAudioSys::kSoundFX, plgAudioSys::kBgndMusic, plgAudioSys::kAmbience, plgAudioSys::kVoice, plgAudioSys::kGUI, plgAudioSys::kNPCVoice }; + + + int i; + + for( i = 0; i < 5; i++ ) + { + hsScalar vol = (hsScalar)(float)params[ i ]; + if( vol > 1.f ) + vol = 1.f; + else if( vol < 0.f ) + vol = 0.f; + + plgAudioSys::SetChannelVolume( chans[ i ], vol ); + } +} + +PF_CONSOLE_CMD( Audio, SetChannelVolume, "string channel, float percentage", "Sets the master volume of a given audio channel\n\ +Valid channels are: SoundFX, BgndMusic, Voice, GUI, NPCVoice and Ambience.") +{ + plgAudioSys::ASChannel chan; + + + if( stricmp( params[ 0 ], "SoundFX" ) == 0 ) + chan = plgAudioSys::kSoundFX; + else if( stricmp( params[ 0 ], "BgndMusic" ) == 0 ) + chan = plgAudioSys::kBgndMusic; + else if( stricmp( params[ 0 ], "Voice" ) == 0 ) + chan = plgAudioSys::kVoice; + else if( stricmp( params[ 0 ], "Ambience" ) == 0 ) + chan = plgAudioSys::kAmbience; + else if( stricmp( params[ 0 ], "GUI" ) == 0 ) + chan = plgAudioSys::kGUI; + else if( stricmp( params[ 0 ], "NPCVoice" ) == 0 ) + chan = plgAudioSys::kNPCVoice; + else + { + PrintString( "Invalid channel specified. Use SoundFX, BgndMusic, Voice, Ambience or GUI." ); + return; + } + + hsScalar vol = (hsScalar)(float)params[ 1 ]; + if( vol > 1.f ) + vol = 1.f; + else if( vol < 0.f ) + vol = 0.f; + + plgAudioSys::SetChannelVolume( chan, vol ); + + char msg[ 128 ]; + switch( chan ) + { + case plgAudioSys::kSoundFX: sprintf( msg, "Setting SoundFX master volume to %4.2f", vol ); break; + case plgAudioSys::kBgndMusic: sprintf( msg, "Setting BgndMusic master volume to %4.2f", vol ); break; + case plgAudioSys::kVoice: sprintf( msg, "Setting Voice master volume to %4.2f", vol ); break; + case plgAudioSys::kAmbience: sprintf( msg, "Setting Ambience master volume to %4.2f", vol ); break; + case plgAudioSys::kGUI: sprintf( msg, "Setting GUI master volume to %4.2f", vol ); break; + case plgAudioSys::kNPCVoice: sprintf( msg, "Setting NPC Voice master volume to %4.2f", vol ); break; + } + PrintString( msg ); +} + +PF_CONSOLE_CMD( Audio, Set2D3DBias, "float bias", "Sets the 2D/3D bias when not using hardware acceleration.") +{ + + hsScalar bias = (hsScalar)(float)params[ 0 ]; + plgAudioSys::Set2D3DBias( bias ); + +} + +PF_CONSOLE_CMD( Audio, ShowNumActiveBuffers, "bool b", "Shows the number of Direct sounds buffers in use") +{ + plgAudioSys::ShowNumBuffers((bool)params[0]); +} + +PF_CONSOLE_CMD( Audio, SetDeviceName, "string deviceName", "Meant for plClient init only") +{ + plgAudioSys::SetDeviceName(params[0]); // this will set the name of the audio system device without actually reseting it +} + +PF_CONSOLE_CMD( Audio, // groupName + EnableVoiceCompression, // fxnName + "bool b", // paramList + "turn voice compression on and off" ) // helpString +{ + bool b = params[0]; + plVoiceRecorder::EnableCompression(b); + +} + +PF_CONSOLE_CMD( Audio, // groupName + ShowIcons, // fxnName + "bool b", // paramList + "turn voice recording icons on and off" ) // helpString +{ + bool b = params[0]; + plVoiceRecorder::EnableIcons(b); + +} + +PF_CONSOLE_CMD( Audio, // groupName + SquelchLevel, // fxnName + "float f", // paramList + "Set the squelch level" ) // helpString +{ + float f = params[0]; + plVoiceRecorder::SetSquelch(f); + +} + + +PF_CONSOLE_CMD( Audio, // groupName + PushToTalk, // fxnName + "bool b", // paramList + "turn push-to-talk on or off" ) // helpString +{ + bool b = params[0]; + plVoiceRecorder::EnablePushToTalk(b); + +} + +PF_CONSOLE_CMD( Audio, // groupName + EnableVoiceNetBroadcast, // fxnName + "bool b", // paramList + "turn voice-over-net on and off" ) // helpString +{ + bool b = params[0]; + plVoiceRecorder::EnableNetVoice(b); + +} + +PF_CONSOLE_CMD( Audio, // groupName + SetVoiceQuality, // fxnName + "int q", // paramList + "Set quality of voice encoding" ) // helpString +{ + int q = params[0]; + plVoiceRecorder::SetQuality(q); +} + + +PF_CONSOLE_CMD( Audio, // groupName + SetVBR, // fxnName + "bool q", // paramList + "Toggle variable bit rate" ) // helpString +{ + bool q = params[0]; + plVoiceRecorder::SetVBR(q); +} + +PF_CONSOLE_CMD( Audio, // groupName + EnableVoiceRecording, // fxnName + "bool b", // paramList + "turn voice recording on or off" ) // helpString +{ + bool b = params[0]; + plVoiceRecorder::EnableRecording(b); + +} + +PF_CONSOLE_CMD( Audio, + EnableVoiceChat, + "bool b", + "Enable Voice chat" ) +{ + plVoicePlayer::Enable((bool) params[0]); + plVoiceRecorder::EnableRecording((bool) params[0]); +} + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Audio, NextDebugPlate, "", "Cycles through the volume displays for all registered sounds" ) +{ + plgAudioSys::NextDebugSound(); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Audio, SetLoadOnDemand, "bool on", "Enable or disable load-on-demand for sounds") +{ + plSound::SetLoadOnDemand( (bool)params[ 0 ] ); +} + +PF_CONSOLE_CMD( Audio, SetTwoStageLOD, "bool on", "Enables or disables two-stage LOD, where sounds can be loaded into RAM but not into sound buffers. Less of a performance hit, harder on memory.") +{ + // For two-stage LOD, we want to disable LoadFromDiskOnDemand, so that we'll load into RAM at startup but not + // into sound buffers until demanded to do so. Enabling LoadFromDiskOnDemand basically conserves as much memory + // as possible + plSound::SetLoadFromDiskOnDemand( !(bool)params[ 0 ] ); +} + +PF_CONSOLE_CMD( Audio, SetVolume, + "string obj, float vol", "Sets the volume on a given object. 1 is max volume, 0 is silence" ) +{ + char str[ 256 ]; + plKey key = FindSceneObjectByName(params[ 0 ], nil, str); + if( key == nil ) + return; + + plSceneObject* obj = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if( !obj ) + return; + + const plAudioInterface *ai = obj->GetAudioInterface(); + plKey aiKey = ai->GetKey(); + + plSoundMsg* cmd = TRACKED_NEW plSoundMsg; + cmd->SetSender( plClient::GetInstance()->GetKey() ); + cmd->SetCmd( plSoundMsg::kSetVolume ); + cmd->fVolume = params[ 1 ]; + cmd->AddReceiver( key ); + plgDispatch::MsgSend(cmd); +} + + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Audio, IsolateSound, + "string soundComponentName", "Mutes all sounds except the given sound. Use Audio.MuteAll false to remove the isolation." ) +{ + char str[ 512 ]; + plKey key; + plAudioSysMsg *asMsg; + + key = FindSceneObjectByName( params[ 0 ], nil, str ); + if( key == nil ) + { + sprintf( str, "Cannot find sound %s", (char *)params[ 0 ] ); + PrintString( str ); + return; + } + + plSceneObject *obj = plSceneObject::ConvertNoRef( key->GetObjectPtr() ); + if( !obj ) + { + sprintf( str, "Cannot get sceneObject %s", (char *)params[ 0 ] ); + PrintString( str ); + return; + } + + const plAudioInterface *ai = obj->GetAudioInterface(); + if( ai == nil ) + { + sprintf( str, "sceneObject %s has no audio interface", (char *)params[ 0 ] ); + PrintString( str ); + return; + } + + asMsg = TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kMuteAll ); + plgDispatch::MsgSend( asMsg ); + + asMsg = TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kUnmuteAll ); + asMsg->AddReceiver( ai->GetKey() ); + asMsg->SetBCastFlag( plMessage::kBCastByExactType, false ); + plgDispatch::MsgSend( asMsg ); + + + char str2[ 256 ]; + sprintf( str2, "Sounds on sceneObject %s isolated.", (char *)params[ 0 ] ); + PrintString( str2 ); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Audio, SetMicVolume, "float volume", "Sets the microphone volume, in the range of 0 to 1" ) +{ + if( !plWinMicLevel::CanSetLevel() ) + PrintString( "Unable to set microphone level" ); + else + { + plWinMicLevel::SetLevel( (float)params[ 0 ] ); + } +} + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Audio, MCNTest, "int which", "" ) +{ + if( (int)params[ 0 ] == 0 ) + plgAudioSys::ClearDebugFlags(); + else if( (int)params[ 0 ] == 1 ) + plgAudioSys::SetDebugFlag( plgAudioSys::kDisableRightSelect ); + else if( (int)params[ 0 ] == 2 ) + plgAudioSys::SetDebugFlag( plgAudioSys::kDisableLeftSelect ); +} + +PF_CONSOLE_CMD( Audio, Mark, "", "" ) +{ + static int markNum = 0; + plStatusLog::AddLineS( "threadfun.log", "******* Mark #%d *******", markNum++ ); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Audio, SetStreamingBufferSize, "float sizeInSecs", "Sets the size of the streaming buffer for each streaming sound." ) +{ + plgAudioSys::SetStreamingBufferSize( (float)params[ 0 ] ); + PrintString( "Changes won't take effect until you restart the audio system." ); +} + +PF_CONSOLE_CMD( Audio, SetStreamFromRAMCutoff, "float cutoffInSecs", "Sets the cutoff between streaming from RAM and streaming directly from disk." ) +{ + plgAudioSys::SetStreamFromRAMCutoff( (float)params[ 0 ] ); + PrintString( "Changes won't take effect until you restart the audio system." ); +} + +PF_CONSOLE_CMD( Audio, SetPriorityCutoff, "int cutoff", "Stops sounds from loading whose priority is greater than this cutoff." ) +{ + plgAudioSys::SetPriorityCutoff( (int)params[ 0 ] ); +} + +PF_CONSOLE_CMD( Audio, EnableExtendedLogs, "bool enable", "Enables or disables the extended audio logs." ) +{ + plgAudioSys::EnableExtendedLogs( (bool)params[ 0 ] ); +} + +PF_CONSOLE_GROUP( Listener ) + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Listener, ShowDebugInfo, "bool show", "Shows or hides debugging info") +{ + plListener::ShowDebugInfo( (bool)params[ 0 ] ); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Listener, UseCameraOrientation, "", "Use the camera's orientation to orient the listener") +{ + plSetListenerMsg *set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVCam | plSetListenerMsg::kFacing, nil, true ); + set->Send(); +} + +PF_CONSOLE_CMD( Listener, UseCameraPosition, "", "Use the canera's position to position the listener") +{ + plSetListenerMsg *set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVCam | plSetListenerMsg::kPosition, nil, true ); + set->Send(); +} + +PF_CONSOLE_CMD( Listener, UseCameraVelocity, "", "Use the camera's velocity to set the listener velocity") +{ + plSetListenerMsg *set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVCam | plSetListenerMsg::kVelocity, nil, true ); + set->Send(); +} + +PF_CONSOLE_CMD( Listener, UsePlayerOrientation, "", "Use the player's orientation to orient the listener") +{ + plKey pKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + if( pKey ) + { + plSetListenerMsg *set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kFacing, pKey, true ); + set->Send(); + } +} +PF_CONSOLE_CMD( Listener, UsePlayerPosition, "", "Use the player's position to position the listener") +{ + plKey pKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + if (pKey) + { + plSetListenerMsg *set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kPosition, pKey, true ); + set->Send(); + } +} + +PF_CONSOLE_CMD( Listener, UsePlayerVelocity, "", "Use the player's velocity to set the listener velocity") +{ + plKey pKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + if (pKey) + { + plSetListenerMsg *set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVelocity, pKey, true ); + set->Send(); + } +} + +PF_CONSOLE_CMD( Listener, XMode, "bool b", "Sets velocity and position to avatar, and orientation to camera") +{ + static UInt32 oldPosType = 0, oldFacingType = 0, oldVelType = 0; + + plSetListenerMsg *set = nil; + plKey pKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + plListener* pListener; + + if( (bool)params[ 0 ] ) + { + // Get the listener object + plUoid lu(kListenerMod_KEY); + plKey pLKey = hsgResMgr::ResMgr()->FindKey(lu); + if (pLKey) + { + pListener = plListener::ConvertNoRef(pLKey->GetObjectPtr()); + } + + if(pListener) + { + // Save old types + oldPosType = pListener->GetAttachedPosType(); + oldFacingType = pListener->GetAttachedFacingType(); + oldVelType = pListener->GetAttachedVelType(); + } + + plStatusLog::AddLineS("audio.log", "XMode on"); + + plSetListenerMsg *set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVCam | plSetListenerMsg::kFacing, nil, true ); + set->Send(); + if (pKey) + { + set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVelocity, pKey, true ); + set->Send(); + set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kPosition, pKey, true ); + set->Send(); + } + } + else + { + if(oldPosType == plListener::kCamera) + { + plSetListenerMsg *set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVCam | plSetListenerMsg::kPosition, nil, true ); + set->Send(); + } + else + { + set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kPosition, pKey, true ); + set->Send(); + } + if(oldFacingType == plListener::kCamera) + { + set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVCam | plSetListenerMsg::kFacing, nil, true ); + set->Send(); + } + else + { + set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kFacing, pKey, true ); + set->Send(); + } + if(oldVelType == plListener::kCamera) + { + set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVCam | plSetListenerMsg::kVelocity, nil, true ); + set->Send(); + } + else + { + set = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVelocity, pKey, true ); + set->Send(); + } + plStatusLog::AddLineS("audio.log", "%s, %d, %d, %d", "XMode off", oldPosType, oldFacingType, oldVelType); + } +} + + + +//////////////////////////////////////////////////////////////////////// +//// Input System Group Commands /////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_GROUP( DInput ) + +PF_CONSOLE_CMD( DInput, UseDInput, "bool on", "Turns off DirectInput") +{ + bool on = params[0]; + plInputManager::UseDInput(on); +} + + +PF_CONSOLE_CMD( DInput, Config, "", "Launch DInput configuration screen") +{ + plgAudioSys::Activate(false); + plInputEventMsg* pMsg = TRACKED_NEW plInputEventMsg; + pMsg->fEvent = plInputEventMsg::kConfigure; + pMsg->AddReceiver( plInputManager::GetInstance()->GetKey() ); + pMsg->SetBCastFlag(plMessage::kBCastByType, false); + plgDispatch::MsgSend(pMsg); +} + +#endif // LIMIT_CONSOLE_COMMANDS + + +////////////////////////////////////////////////////////////////////////////// +//// Keyboard Remapping Group Commands /////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_GROUP( Keyboard ) // Defines a main command group + +PF_CONSOLE_CMD( Keyboard, ResetBindings, "", "Resets the keyboard bindings to their defaults" ) +{ + if( plInputInterfaceMgr::GetInstance() != nil ) + plInputInterfaceMgr::GetInstance()->InitDefaultKeyMap(); + + PrintString( "Keyboard bindings reset" ); +} + + +PF_CONSOLE_CMD( Keyboard, ClearBindings, "", "Resets the keyboard bindings to empty" ) +{ + if( plInputInterfaceMgr::GetInstance() != nil ) + plInputInterfaceMgr::GetInstance()->ClearAllKeyMaps(); + + PrintString( "Keyboard bindings destroyed" ); +} + +static plKeyCombo IBindKeyToVKey( const char *string ) +{ + char str[ 16 ]; + int i; + + plKeyCombo combo; + + + strcpy( str, string ); + + // Find modifiers to set flags with + combo.fFlags = 0; + if( strstr( str, "_S" ) || strstr( str, "_s" ) ) + combo.fFlags |= plKeyCombo::kShift; + if( strstr( str, "_C" ) || strstr( str, "_c" ) ) + combo.fFlags |= plKeyCombo::kCtrl; + + // Get rid of modififers + for( i = 0; str[ i ] != 0 && str[ i ] != '_'; i++ ); + str[ i ] = 0; + + // Convert raw key + combo.fKey = plKeyMap::ConvertCharToVKey( str ); + if( combo.fKey == KEY_UNMAPPED ) + combo = plKeyCombo::kUnmapped; + + // And return! + return combo; + +} + +static ControlEventCode IBindStringToCmdCode( const char *string ) +{ + return plKeyMap::ConvertCharToControlCode( string ); +} + +PF_CONSOLE_CMD( Keyboard, // groupName + BindKey, // fxnName + "string key1, string action", // paramList + "Binds the given single key combo to the given action" ) // helpString +{ + ControlEventCode code = IBindStringToCmdCode( params[ 1 ] ); + if( code == END_CONTROLS ) + { + PrintString( "ERROR: Invalid command name" ); + return; + } + + if( plInputInterfaceMgr::GetInstance() != nil ) + { + plKeyCombo key1 = IBindKeyToVKey( params[ 0 ] ); + plInputInterfaceMgr::GetInstance()->BindAction( key1, code ); + } +} + +PF_CONSOLE_CMD( Keyboard, // groupName + BindAction, // fxnName + "string key1, string key2, string action", // paramList + "Binds the given two keys to the given action (you can specify 'UNDEFINED' for either key if you wish)" ) // helpString +{ + ControlEventCode code = IBindStringToCmdCode( params[ 2 ] ); + if( code == END_CONTROLS ) + { + PrintString( "ERROR: Invalid command name" ); + return; + } + + if( plInputInterfaceMgr::GetInstance() != nil ) + { + plKeyCombo key1 = IBindKeyToVKey( params[ 0 ] ); + plKeyCombo key2 = IBindKeyToVKey( params[ 1 ] ); + plInputInterfaceMgr::GetInstance()->BindAction( key1, key2, code ); + } +} + +PF_CONSOLE_CMD( Keyboard, // groupName + BindConsoleCmd, // fxnName + "string key, string command", // paramList + "Bind console command to key" ) // helpString +{ + plKeyCombo key = IBindKeyToVKey( params[ 0 ] ); + + if( plInputInterfaceMgr::GetInstance() != nil ) + plInputInterfaceMgr::GetInstance()->BindConsoleCmd( key, params[ 1 ], plKeyMap::kFirstAlways ); +} + + +////////////////////////////////////////////////////////////////////////////// +//// Stat Gather Commands //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_GROUP( Nav ) + +PF_CONSOLE_CMD( Nav, PageInNode, // Group name, Function name + "string roomName", // Params + "Pages in a scene node." ) // Help string +{ + plSynchEnabler ps(false); // disable dirty tracking while paging in + plClientMsg* pMsg1 = TRACKED_NEW plClientMsg(plClientMsg::kLoadRoom); + pMsg1->AddReceiver( plClient::GetInstance()->GetKey() ); + pMsg1->AddRoomLoc(plKeyFinder::Instance().FindLocation(nil, params[0])); + plgDispatch::MsgSend(pMsg1); +} + +PF_CONSOLE_CMD( Nav, PageInNodeList, // Group name, Function name + "string roomNameBase", // Params + "Pages in all scene nodes that start with name." ) // Help string +{ + plSynchEnabler ps(false); // disable dirty tracking while paging in + + std::string pageInNodesStr; + pageInNodesStr += "dat\\"; + pageInNodesStr += (char*)params[0]; + pageInNodesStr += "*.prx"; + hsFolderIterator pageInNodesIter(pageInNodesStr.data(), true); + + plClientMsg* pMsg1 = TRACKED_NEW plClientMsg(plClientMsg::kLoadRoom); + while (pageInNodesIter.NextFile()) + { + char nodeName[255]; + _splitpath(pageInNodesIter.GetFileName(), NULL, NULL, nodeName, NULL); + pMsg1->AddRoomLoc(plKeyFinder::Instance().FindLocation(nil, nodeName)); + } + pMsg1->AddReceiver( plClient::GetInstance()->GetKey() ); + plgDispatch::MsgSend(pMsg1); +} + +#ifndef LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_CMD( Nav, PageOutNode, // Group name, Function name + "string roomName", // Params + "pages out a scene node." ) // Help string +{ + plSynchEnabler ps(false); // disable dirty tracking while paging out + plClientMsg* pMsg1 = TRACKED_NEW plClientMsg(plClientMsg::kUnloadRoom); + pMsg1->AddReceiver( plClient::GetInstance()->GetKey() ); + pMsg1->AddRoomLoc(plKeyFinder::Instance().FindLocation(nil, params[0])); + plgDispatch::MsgSend(pMsg1); +} + +PF_CONSOLE_CMD( Nav, UnloadPlayer, // Group name, Function name + "string objName", // Params + "unloads a named player" ) // Help string +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + PrintString("UnloadPlayer (console version) is currently broken. Hassle Matt."); + // plNetClientMgr::UnloadPlayer(key); +} + +PF_CONSOLE_CMD( Nav, UnloadSceneObject, // Group name, Function name + "string objName", // Params + "unloads a named scene object" ) // Help string +{ + + PrintString("OBSOLETE"); +} + +PF_CONSOLE_CMD( Nav, MovePlayer, // Group name, Function name + "string playerName, string destPage", // Params + "moves a player from one paging unit to another" ) // Help string +{ + char str[256]; + plKey playerKey = FindSceneObjectByName(params[0], nil, str); + PrintString(str); + if( !playerKey ) + return; + + plKey nodeKey = FindObjectByName(params[1], plSceneNode::Index(), nil, str); + PrintString(str); + if( !nodeKey ) + return; + + plNodeChangeMsg* msg = TRACKED_NEW plNodeChangeMsg(nil, playerKey, nodeKey); + plgDispatch::MsgSend(msg); + sprintf(str, "%s moved to %s", (char*)params[0], (char*)params[1]); + PrintString(str); +} + +PF_CONSOLE_CMD( Nav, ExcludePage, "string pageName", "Excludes the given page from ever being loaded. Useful for debugging." ) +{ + if( plNetClientMgr::GetInstance() == nil ) + PrintString( "Unable to exclude page--NetClientMgr not loaded" ); + else + { + char str[ 256 ]; + sprintf( str, "Page %s excluded from load", (char *)params[ 0 ] ); + plAgeLoader::GetInstance()->AddExcludedPage( params[ 0 ] ); + PrintString( str ); + } +} + +PF_CONSOLE_CMD( Nav, ClearExcludeList, "", "Clears the list of pages to exclude from loading." ) +{ + if( plAgeLoader::GetInstance() != nil ) + plAgeLoader::GetInstance()->ClearPageExcludeList(); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_GROUP( Movie ) // Defines a main command group + +PF_CONSOLE_CMD( Movie, + Start, + "string filename", + "Start of movie with this filename" ) +{ + char* filename = params[0]; + plMovieMsg* mov = TRACKED_NEW plMovieMsg(filename, plMovieMsg::kStart); + +//#define MF_TEST_MOVIECALLBACKS +#ifdef MF_TEST_MOVIECALLBACKS + plMovieMsg* cb = TRACKED_NEW plMovieMsg("avi/intro0.bik", plMovieMsg::kStart); + mov->AddCallback(cb); + mov->SetCmd(mov->GetCmd() | plMovieMsg::kAddCallbacks); +#endif // MF_TEST_MOVIECALLBACKS + + mov->Send(); + + PrintStringF(PrintString, "%s now playing", filename); +} + +PF_CONSOLE_CMD( Movie, + Stop, + "string filename", + "Stop movie with this filename" ) +{ + char* filename = params[0]; + plMovieMsg* mov = TRACKED_NEW plMovieMsg(filename, plMovieMsg::kStop); + mov->Send(); + + PrintStringF(PrintString, "%s now stopping", filename); +} + +PF_CONSOLE_CMD( Movie, + Pause, + "string filename", + "Pause movie with this filename" ) +{ + char* filename = params[0]; + plMovieMsg* mov = TRACKED_NEW plMovieMsg(filename, plMovieMsg::kPause); + mov->Send(); + + PrintStringF(PrintString, "%s now pausing", filename); +} + +PF_CONSOLE_CMD( Movie, + Resume, + "string filename", + "Resume movie with this filename" ) +{ + char* filename = params[0]; + plMovieMsg* mov = TRACKED_NEW plMovieMsg(filename, plMovieMsg::kResume); + mov->Send(); + + PrintStringF(PrintString, "%s now resuming", filename); +} + +PF_CONSOLE_CMD( Movie, + Move, + "string filename, float x, float y", + "Move center of movie with this filename to x,y" ) +{ + char* filename = params[0]; + plMovieMsg* mov = TRACKED_NEW plMovieMsg(filename, plMovieMsg::kMove); + float x = params[1]; + float y = params[2]; + mov->SetCenter(x, y); + mov->Send(); + + PrintStringF(PrintString, "%s now at %g,%g", filename, x, y); +} + +PF_CONSOLE_CMD( Movie, + Scale, + "string filename, float x, float y", + "Scale movie with this filename by x,y" ) +{ + char* filename = params[0]; + plMovieMsg* mov = TRACKED_NEW plMovieMsg(filename, plMovieMsg::kScale); + float x = params[1]; + float y = params[2]; + mov->SetScale(x, y); + mov->Send(); + + PrintStringF(PrintString, "%s now scaled to %g,%g", filename, x, y); +} + +PF_CONSOLE_CMD( Movie, + Alpha, + "string filename, float a", + "Set opacity of movie with this filename to a" ) +{ + char* filename = params[0]; + plMovieMsg* mov = TRACKED_NEW plMovieMsg(filename, plMovieMsg::kOpacity); + float a = params[1]; + mov->SetOpacity(a); + mov->Send(); + + PrintStringF(PrintString, "%s opacity now at %g", filename, a); +} + +PF_CONSOLE_CMD( Movie, + Color, + "string filename, float r, float g, float b", + "Color movie with this filename as r,g,b" ) +{ + char* filename = params[0]; + plMovieMsg* mov = TRACKED_NEW plMovieMsg(filename, plMovieMsg::kColor); + float r = params[1]; + float g = params[2]; + float b = params[3]; + mov->SetColor(r, g, b, 1.f); + mov->Send(); + + PrintStringF(PrintString, "%s now tinted to %g,%g,%g", filename, r, g, b); +} + +PF_CONSOLE_CMD( Movie, + Volume, + "string filename, float v", + "Set volume of movie with this filename to v" ) +{ + char* filename = params[0]; + plMovieMsg* mov = TRACKED_NEW plMovieMsg(filename, plMovieMsg::kVolume); + float v = params[1]; + mov->SetVolume(v); + mov->Send(); + + PrintStringF(PrintString, "%s volume now at %g", filename, v); +} + +PF_CONSOLE_CMD( Movie, + FadeIn, + "string filename, float secs, float r, float g, float b, float a", + "Fade in movie with this filename from r,g,b,a over secs seconds" ) +{ + char* filename = params[0]; + plMovieMsg* mov = TRACKED_NEW plMovieMsg(filename, plMovieMsg::kFadeIn); + float secs = params[1]; + float r = params[2]; + float g = params[3]; + float b = params[4]; + float a = params[5]; + mov->SetFadeInSecs(secs); + mov->SetFadeInColor(r, g, b, a); + mov->Send(); + + PrintStringF(PrintString, "%s now fading from %g,%g,%g,%g over %g secs", filename, r, g, b, a, secs); +} + +PF_CONSOLE_CMD( Movie, + FadeOut, + "string filename, float secs, float r, float g, float b, float a", + "Fade out movie with this filename to r,g,b,a over secs seconds" ) +{ + char* filename = params[0]; + plMovieMsg* mov = TRACKED_NEW plMovieMsg(filename, plMovieMsg::kFadeOut); + float secs = params[1]; + float r = params[2]; + float g = params[3]; + float b = params[4]; + float a = params[5]; + mov->SetFadeOutSecs(secs); + mov->SetFadeOutColor(r, g, b, a); + mov->Send(); + + PrintStringF(PrintString, "%s now fading to %g,%g,%g,%g over %g secs", filename, r, g, b, a, secs); +} + + + +////////////////////////////////////////////////////////////////////////////// +// Test hacks for setting overall quality and clamping board capability +////////////////////////////////////////////////////////////////////////////// +PF_CONSOLE_GROUP( Quality ) + +PF_CONSOLE_CMD( Quality, + Level, + "int quality", + "Fake quality slider from .ini file" ) +{ + int q = params[0]; + if( q < 0 ) + q = 0; + else if (q > 3) + q = 3; + plClient::GetInstance()->SetQuality(q); + + char str[256]; + sprintf(str, "Quality slider to %d", q); + PrintString(str); +} + +#include "../plSurface/plShaderTable.h" + +PF_CONSOLE_CMD( Quality, + Cap, + "int cap", + "Limit graphics capability from .ini file" ) +{ + int c = params[0]; + if( c < 0 ) + c = 0; + if( c == 666 ) + plShaderTable::SetLoadFromFile(true); + else + plClient::GetInstance()->SetClampCap(c); + + char str[256]; + sprintf(str, "Graphics capability clamped to %d", c); + PrintString(str); +} + + +////////////////////////////////////////////////////////////////////////////// +//// Geometry Object Access Console Commands (strictly for testing) //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +#include "../plDrawable/plSharedMesh.h" +#include "../plDrawable/plAccessGeometry.h" +#include "../plDrawable/plMorphSequence.h" +#include "../plAvatar/plAvatarClothing.h" + +PF_CONSOLE_GROUP( Access ) + + +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +static plMorphSequence* LocalMorphSequence() +{ + plKey playerKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + if( !playerKey ) + return nil; + plSceneObject* playerObj = plSceneObject::ConvertNoRef(playerKey->ObjectIsLoaded()); + if( !playerObj ) + return nil; + + const plCoordinateInterface* pci = playerObj->GetCoordinateInterface(); + const plModifier* constSeq = nil; + int i; + for( i = 0; i < pci->GetNumChildren(); i++ ) + { + const plSceneObject* child = pci->GetChild(i)->GetOwner(); + if( child ) + { + int j; + for( j = 0; j < child->GetNumModifiers(); j++ ) + { + constSeq = child->GetModifier(j); + if( constSeq && plMorphSequence::ConvertNoRef(constSeq) ) + { + return (plMorphSequence*)constSeq; // safe cast, we've already checked type (plus we're const_cast'ing). + } + } + } + } + return nil; +} + +PF_CONSOLE_CMD( Access, + Morph, + "string morphMod, int iLay, int iDel, float wgt", + "Set the weight for a morphMod" ) +{ + char str[256]; + char name[256]; + char* preFix = params[0]; + sprintf(name, "%s_plMorphSequence_0", preFix); + plKey key = FindObjectByName(name, plMorphSequence::Index(), nil, str); + PrintString(str); + if (!key) + return; + + plMorphSequence* seq = plMorphSequence::ConvertNoRef(key->GetObjectPtr()); + + int iLay = params[1]; + int iDel = params[2]; + float wgt = params[3]; + + seq->SetWeight(iLay, iDel, wgt); + + sprintf(str, "Layer[%d][%d] = %g\n", iLay, iDel, wgt); + PrintString(str); +} + +PF_CONSOLE_CMD( Access, + MoAct, + "string morphMod", + "Activate a morphMod" ) +{ + char str[256]; + char name[256]; + char* preFix = params[0]; + sprintf(name, "%s_plMorphSequence_2", preFix); + plKey key = FindObjectByName(name, plMorphSequence::Index(), nil, str); + PrintString(str); + if (!key) + return; + + plMorphSequence* seq = plMorphSequence::ConvertNoRef(key->GetObjectPtr()); + + seq->Activate(); + + sprintf(str, "%s Active\n", name); + PrintString(str); +} + +PF_CONSOLE_CMD( Access, + MoDeAct, + "string morphMod", + "Activate a morphMod" ) +{ + char str[256]; + char name[256]; + char* preFix = params[0]; + sprintf(name, "%s_plMorphSequence_2", preFix); + plKey key = FindObjectByName(name, plMorphSequence::Index(), nil, str); + PrintString(str); + if (!key) + return; + + plMorphSequence* seq = plMorphSequence::ConvertNoRef(key->GetObjectPtr()); + + seq->DeActivate(); + + sprintf(str, "%s Unactive\n", name); + PrintString(str); +} +////////////////// +PF_CONSOLE_CMD( Access, + Weight, + "int iLay, int iDel, float wgt", + "Set the weight for a morphMod" ) +{ + plMorphSequence* seq = LocalMorphSequence(); + if( !seq ) + { + PrintString("Sequence not found\n"); + return; + } + + int iLay = params[0]; + int iDel = params[1]; + float wgt = params[2]; + + seq->SetWeight(iLay, iDel, wgt); + + char str[256]; + sprintf(str, "Layer[%d][%d] = %g\n", iLay, iDel, wgt); + PrintString(str); +} + + +PF_CONSOLE_CMD( Access, + ZeroBrian, + "int BeginLay, int EndLay", + "Zero the morph layers from begin to end" ) +{ + plMorphSequence* seq = LocalMorphSequence(); + if( !seq ) + { + PrintString("Sequence not found\n"); + return; + } + + int i; + int s = params[0]; + int e = params[1]; + + for(i = s; i <= e; i++) + { + seq->SetWeight(i, 0, 0.0); + seq->SetWeight(i, 1, 0.0); + } + PrintString("Zeroed"); + +} + +char *gCurrMorph = nil; + +PF_CONSOLE_CMD( Access, + SetMorphItem, + "string itemName", + "Set which clothing item we want to morph" ) +{ + delete [] gCurrMorph; + gCurrMorph = hsStrcpy(nil, params[0]); +} + +PF_CONSOLE_CMD( Access, + IncBrian, + "int iLay, float inc", + "Inc the weight for a morphMod pair" ) +{ + plMorphSequence* seq = LocalMorphSequence(); + if( !seq ) + { + PrintString("Sequence not found\n"); + return; + } + + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName(gCurrMorph); + if( !item ) + { + PrintString("Item not found"); + return; + } + + plKey meshKey = item->fMeshes[0]->GetKey(); + int iLay = params[0]; + float inc = params[1]; + + if (iLay >= seq->GetNumLayers(meshKey)) + { + PrintString("Layer index too high"); + return; + } + + float wgtPlus; + float wgtMinus; + + wgtPlus = seq->GetWeight(iLay,0,meshKey); + wgtMinus = seq->GetWeight(iLay,1,meshKey); + + float val = wgtPlus - wgtMinus; + + val += inc; + + if(val > 1.0) val = 1.0; + if(val < -1.0) val = -1.0; + + if(val > 0) + { + wgtPlus = val; + wgtMinus = 0; + } + else + { + wgtMinus = -val; + wgtPlus = 0; + } + + seq->SetWeight(iLay, 0, wgtPlus, meshKey); + seq->SetWeight(iLay, 1, wgtMinus, meshKey); + + char str[256]; + sprintf(str, "Layer[%d][%d] = %g\n", iLay, 0, wgtPlus); + PrintString(str); + + sprintf(str, "Layer[%d][%d] = %g\n", iLay, 1, wgtMinus); + PrintString(str); +} + + + + +PF_CONSOLE_CMD( Access, + FaAct, + "", + "Activate face morphMod" ) +{ + plMorphSequence* seq = LocalMorphSequence(); + if( !seq ) + { + PrintString("Sequence not found\n"); + return; + } + + seq->Activate(); + + char str[256]; + sprintf(str, "%s Active\n", seq->GetKey()->GetName()); + PrintString(str); +} + +PF_CONSOLE_CMD( Access, + FaDeAct, + "", + "Deactivate a morphMod" ) +{ + plMorphSequence* seq = LocalMorphSequence(); + if( !seq ) + { + PrintString("Sequence not found\n"); + return; + } + + seq->DeActivate(); + + char str[256]; + sprintf(str, "%s Unactive\n", seq->GetKey()->GetName()); + PrintString(str); +} + +PF_CONSOLE_CMD( Access, + Face, + "string clothItem", + "Set face morphMod to affect a clothing item" ) +{ + char str[256]; + plMorphSequence* seq = LocalMorphSequence(); + if( !seq ) + { + PrintString("Sequence not found\n"); + return; + } + + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName(params[0]); + if( !item ) + return; + + seq->SetUseSharedMesh(true); + seq->AddSharedMesh(item->fMeshes[plClothingItem::kLODHigh]); + + sprintf(str, "%s on item %s\n", seq->GetKey()->GetName(), (char *)params[0]); + PrintString(str); +} + +#include "../pfSurface/plFadeOpacityMod.h" + +PF_CONSOLE_CMD( Access, + Fade, + "", + "Test fading on visibility" ) +{ + hsBool disabled = !plFadeOpacityMod::GetLOSCheckDisabled(); + + plFadeOpacityMod::SetLOSCheckDisabled(disabled); + + char str[256]; + sprintf(str, "LOS check now %s", disabled ? "disabled" : "enabled"); + PrintString(str); +} + +PF_CONSOLE_CMD( Access, + ObjFade, + "string obj, float fadeUp, float fadeDown", + "Test fading on visibility" ) +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + PrintString(str); + if( !key ) + return; + + plSceneObject* obj = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if( !obj ) + return; + + float fadeUp = params[1]; + float fadeDown = params[2]; + + plFadeOpacityMod* mod = TRACKED_NEW plFadeOpacityMod; + mod->SetFadeUp(fadeUp); + mod->SetFadeDown(fadeDown); + + hsgResMgr::ResMgr()->NewKey(obj->GetKey()->GetName(), mod, obj->GetKey()->GetUoid().GetLocation()); + + hsgResMgr::ResMgr()->AddViaNotify(mod->GetKey(), TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + +} + +#include "../plDrawable/plVisLOSMgr.h" + +static plSceneObject* losObj = nil; + +PF_CONSOLE_CMD( Access, + Obj, + "string losObj", + "Set the los test marker" ) +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + PrintString(str); + if( !key ) + return; + + losObj = plSceneObject::ConvertNoRef(key->GetObjectPtr()); +} + +PF_CONSOLE_CMD( Access, + HackLOS, + "string marker", + "Set the Los hack marker" ) +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + PrintString(str); + + plSceneObject* so = nil; + if( key ) + { + so = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + } + + extern void VisLOSHackBegin(plPipeline* p, plSceneObject* m); + + VisLOSHackBegin(pfConsole::GetPipeline(), so); +} + +PF_CONSOLE_CMD( Access, + HackEnd, + "", + "stop the hackage" ) +{ + extern void VisLOSHackBegin(plPipeline* p, plSceneObject* m); + + VisLOSHackBegin(nil, nil); +} + + +PF_CONSOLE_CMD( Access, + LOS, + "...", + "Fire LOS test check" ) +{ + static float dist = 1.e5f; + if( numParams > 0 ) + dist = params[0]; + + hsPoint3 from = pfConsole::GetPipeline()->GetViewPositionWorld(); + + Int32 sx = pfConsole::GetPipeline()->Width() / 2; + Int32 sy = pfConsole::GetPipeline()->Height() / 2; + hsPoint3 targ; + pfConsole::GetPipeline()->ScreenToWorldPoint(1, 0, &sx, &sy, dist, 0, &targ); + + plVisHit hit; + if( plVisLOSMgr::Instance()->Check(from, targ, hit) ) + { + char buff[256]; + sprintf(buff, "(%g, %g, %g)", hit.fPos.fX, hit.fPos.fY, hit.fPos.fZ); + PrintString(buff); + + if( losObj ) + { + hsMatrix44 l2w = losObj->GetLocalToWorld(); + l2w.fMap[0][3] = hit.fPos.fX; + l2w.fMap[1][3] = hit.fPos.fY; + l2w.fMap[2][3] = hit.fPos.fZ; + l2w.NotIdentity(); + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + losObj->SetTransform(l2w, w2l); + } + } + +} + +#include "../plMessage/plBulletMsg.h" + +plSceneObject* gunObj = nil; +hsScalar gunRadius = 1.f; +hsScalar gunRange = 5000.f; + +PF_CONSOLE_CMD( Access, + Gun, + "string gun, float radius, ...", + "Fire shot along gun's z-axis, creating decal of radius , with optional max-range (def 1000)" ) +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + PrintString(str); + if( !key ) + return; + + plSceneObject* so = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if( !so ) + return; + + gunObj = so; + + gunRadius = params[1]; + + if( numParams > 2 ) + gunRange = params[2]; +} + +PF_CONSOLE_CMD( Access, + Shot, + "", + "Fire shot along gun's z-axis" ) +{ + plSceneObject* so = gunObj; + if( !so ) + { + PrintString("Set gun object first"); + return; + } + + hsMatrix44 l2w = so->GetLocalToWorld(); + hsVector3 dir(l2w.fMap[0][2], l2w.fMap[1][2], l2w.fMap[2][2]); + dir.Normalize(); + hsPoint3 pos = l2w.GetTranslate(); + + hsScalar radius = gunRadius; + + hsScalar range = gunRange; + + plBulletMsg* bull = TRACKED_NEW plBulletMsg(nil, nil, nil); + bull->FireShot(pos, dir, radius, range); + + bull->Send(); + + PrintString("Shot fired!"); +} + +PF_CONSOLE_CMD( Access, + XShot, + "", + "Fire shot along gun's neg x-axis" ) +{ + plSceneObject* so = gunObj; + if( !so ) + { + PrintString("Set gun object first"); + return; + } + + hsMatrix44 l2w = so->GetLocalToWorld(); + hsVector3 dir(-l2w.fMap[0][0], -l2w.fMap[1][0], -l2w.fMap[2][0]); + dir.Normalize(); + hsPoint3 pos = l2w.GetTranslate(); + + hsScalar radius = gunRadius; + + hsScalar range = gunRange; + + plBulletMsg* bull = TRACKED_NEW plBulletMsg(nil, nil, nil); + bull->FireShot(pos, dir, radius, range); + + bull->Send(); + + PrintString("Shot fired!"); +} + +PF_CONSOLE_CMD( Access, + Party, + "string bull, string psys", + "Add particle system to bulletMgr ") +{ + char str[256]; + plKey bullKey = FindObjectByName(params[0], plDynaBulletMgr::Index(), nil, str, false); + PrintString(str); + if( !(bullKey && bullKey->GetObjectPtr()) ) + { + PrintString("bullet not found"); + return; + } + + plKey sysKey = FindSceneObjectByName(params[1], nil, str); + if( !(sysKey && sysKey->GetObjectPtr()) ) + { + PrintString("Psys not found"); + return; + } + hsgResMgr::ResMgr()->AddViaNotify(sysKey, TRACKED_NEW plGenRefMsg(bullKey, plRefMsg::kOnCreate, 0, plDynaBulletMgr::kRefPartyObject), plRefFlags::kPassiveRef); + + PrintString("sys added"); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +////////////////////////////////////////////////////////////////////////////// +//// WaveSet Console Commands //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +#include "../plDrawable/plWaveSet7.h" +#include "../plDrawable/plFixedWaterState7.h" +PF_CONSOLE_GROUP( Wave ) + +PF_CONSOLE_SUBGROUP( Wave, Set) // Creates a sub-group under a given group + +namespace plWaveCmd { + enum Cmd + { + kWindDir, + + kGeoLen, + kGeoChop, + kGeoAmp, + kGeoAngle, + + kTexLen, + kTexChop, + kTexAmp, + kTexAngle, + + kNoise, + + kSpecAtten, + + kWaterTint, + kWaterOpacity, + kSpecularTint, + kSpecularMute, + kRippleScale, + kWaterHeight, + kWaterOffsetOpac, + kWaterOffsetRefl, + kWaterOffsetWave, + kDepthFalloffOpac, + kDepthFalloffRefl, + kDepthFalloffWave, + kMaxAtten, + kMinAtten, + kEnvCenter, + kEnvRadius, + }; +}; + +typedef void PrintFunk(const char* str); + +static inline hsScalar FracToPercent(hsScalar f) { return (hsScalar)(1.e2 * f); } +static inline hsScalar PercentToFrac(hsScalar f) { return (hsScalar)(1.e-2 * f); } + +static inline hsScalar RadToDeg(hsScalar r) { return r * 180.f / hsScalarPI; } +static inline hsScalar DegToRad(hsScalar d) { return d * hsScalarPI / 180.f; } + +static void IDisplayWaveVal(PrintFunk PrintString, plWaveSet7* wave, plWaveCmd::Cmd cmd) +{ + if( !wave ) + return; + + using namespace plWaveCmd; + + char buff[256]; + + hsPoint3 pos; + hsVector3 vec; + hsColorRGBA col; + + plFixedWaterState7 state = wave->State(); + switch( cmd ) + { + case kGeoLen: + sprintf(buff, "Min/Max Geo Wavelengths = %f/%f", wave->GetGeoMinLength(), wave->GetGeoMaxLength()); + break; + case kGeoChop: + sprintf(buff, "Geo Choppiness = %f", FracToPercent(wave->GetGeoChop())); + break; + case kGeoAmp: + sprintf(buff, "Geo Wave Amplitude to Length Ratio (%%) = %f", FracToPercent(wave->GetGeoAmpOverLen())); + break; + case kGeoAngle: + sprintf(buff, "Geo Spread of waves about Wind Dir = %f degrees", RadToDeg(wave->GetGeoAngleDev())); + break; + + case kTexLen: + sprintf(buff, "Min/Max Tex Wavelengths = %f/%f", wave->GetTexMinLength(), wave->GetTexMaxLength()); + break; + case kTexChop: + sprintf(buff, "Tex Choppiness = %f", FracToPercent(wave->GetTexChop())); + break; + case kTexAmp: + sprintf(buff, "Tex Wave Amplitude to Length Ratio = %f%%", FracToPercent(wave->GetTexAmpOverLen())); + break; + case kTexAngle: + sprintf(buff, "Tex Spread of waves about Wind Dir = %f degrees", RadToDeg(wave->GetTexAngleDev())); + break; + + case kNoise: + sprintf(buff, "Noising texture ripples at %f %%", FracToPercent(wave->GetSpecularNoise())); + break; + + case kSpecAtten: + sprintf(buff, "Ripples fade out from %f to %f feet", wave->GetSpecularStart(), wave->GetSpecularEnd()); + break; + + case kWaterOpacity: + sprintf(buff, "WaterOpacity = %f", FracToPercent(wave->GetWaterTint().a)); + break; + case kRippleScale: + sprintf(buff, "RippleScale = %f", wave->GetRippleScale()); + break; + case kWaterHeight: + sprintf(buff, "WaterHeight = %f", wave->GetWaterHeight()); + break; + case kEnvRadius: + sprintf(buff, "EnvRadius = %f", wave->GetEnvRadius()); + break; + case kWaterOffsetOpac: + sprintf(buff, "OpacStart = %f", -wave->GetOpacOffset()); + break; + case kWaterOffsetRefl: + sprintf(buff, "ReflStart = %f", -wave->GetReflOffset()); + break; + case kWaterOffsetWave: + sprintf(buff, "WaveStart = %f", -wave->GetWaveOffset()); + break; + + case kDepthFalloffOpac: + sprintf(buff, "OpacEnd = %f", wave->GetOpacFalloff()); + break; + case kDepthFalloffRefl: + sprintf(buff, "ReflEnd = %f", wave->GetReflFalloff()); + break; + case kDepthFalloffWave: + sprintf(buff, "WaveEnd = %f", wave->GetWaveFalloff()); + break; + + case kWindDir: + vec = wave->GetWindDir(); + sprintf(buff, "WindDir (%f, %f)", vec.fX, vec.fY); + break; + case kMinAtten: + vec = wave->GetMinAtten(); + sprintf(buff, "MinAtten (%f, %f, %f)", vec.fX, vec.fY, vec.fZ); + break; + case kMaxAtten: + vec = wave->GetMaxAtten(); + sprintf(buff, "MaxAtten (%f, %f, %f)", vec.fX, vec.fY, vec.fZ); + break; + + case kEnvCenter: + pos = wave->GetEnvCenter(); + sprintf(buff, "EnvCenter (%f, %f, %f)", pos.fX, pos.fY, pos.fZ); + break; + + case kWaterTint: + col = wave->GetWaterTint(); + sprintf(buff, "Water tint (%d, %d, %d)", + int(col.r * 255.9f), + int(col.g * 255.9f), + int(col.b * 255.9f)); + break; + case kSpecularTint: + col = wave->GetSpecularTint(); + sprintf(buff, "Specular tint (%d, %d, %d)", + int(col.r * 255.9f), + int(col.g * 255.9f), + int(col.b * 255.9f)); + break; + case kSpecularMute: + col = wave->GetSpecularTint(); + sprintf(buff, "Specular mute %f", FracToPercent(col.a)); + break; + + default: + sprintf(buff, "Unknown parameter"); + break; + } + PrintString(buff); +} + +static plWaveSet7* IGetWaveSet(PrintFunk PrintString, const char* name) +{ + char str[256]; + plKey waveKey = FindObjectByName(name, plWaveSet7::Index(), nil, str, false); + PrintString(str); + if (!waveKey) + return nil; + + plWaveSet7* waveSet = plWaveSet7::ConvertNoRef(waveKey->ObjectIsLoaded()); + if( !waveSet ) + { + PrintString("Found object, but it's not a Water component. Ignoring"); + } + return waveSet; +} + +static plWaveSet7* ICheckWaveParams(PrintFunk PrintString, const char* name, int numParams, int n, plWaveCmd::Cmd cmd) +{ + if( !numParams ) + { + PrintString("Missing name of water component"); + return nil; + } + plWaveSet7* waveSet = IGetWaveSet(PrintString, name); + if( waveSet && (numParams < n) ) + { + IDisplayWaveVal(PrintString, waveSet, cmd); + return nil; + } + return waveSet; +} + +static hsScalar LimitVal(hsScalar val, hsScalar lo, hsScalar hi, PrintFunk PrintString) +{ + if( val < lo ) + { + char buff[256]; + sprintf(buff, "%f too low, clamped to %f", val, lo); + PrintString(buff); + val = lo; + } + else if( val > hi ) + { + char buff[256]; + sprintf(buff, "%f too high, clamped to %f", val, hi); + PrintString(buff); + val = hi; + } + return val; +} + +static bool ISendWaveCmd1f(PrintFunk PrintString, pfConsoleCmdParam* params, int numParams, plWaveCmd::Cmd cmd) +{ + plWaveSet7* wave = ICheckWaveParams(PrintString, params[0], numParams, 2, cmd); + if( !wave ) + return false; + + using namespace plWaveCmd; + + float val = params[1]; + + hsScalar secs = ( numParams > 2 ) ? params[2] : 0.f; + + switch( cmd ) + { + case kGeoChop: + wave->SetGeoChop(PercentToFrac(val), secs); + break; + case kGeoAmp: + wave->SetGeoAmpOverLen(PercentToFrac(val), secs); + break; + case kGeoAngle: + wave->SetGeoAngleDev(DegToRad(val), secs); + break; + + case kTexChop: + wave->SetTexChop(PercentToFrac(val), secs); + break; + case kTexAmp: + wave->SetTexAmpOverLen(PercentToFrac(val), secs); + break; + case kTexAngle: + wave->SetTexAngleDev(DegToRad(val), secs); + break; + + case kNoise: + wave->SetSpecularNoise(PercentToFrac(val), secs); + break; + + case kWaterOpacity: + wave->SetWaterOpacity(PercentToFrac(val), secs); + break; + case kSpecularMute: + wave->SetSpecularMute(PercentToFrac(val), secs); + break; + case kRippleScale: + wave->SetRippleScale(val, secs); + break; + case kWaterHeight: + wave->SetWaterHeight(val, secs); + break; + case kEnvRadius: + wave->SetEnvRadius(val, secs); + break; + case kWaterOffsetOpac: + wave->SetOpacOffset(-val, secs); + break; + case kWaterOffsetRefl: + wave->SetReflOffset(-val, secs); + break; + case kWaterOffsetWave: + wave->SetWaveOffset(-val, secs); + break; + + case kDepthFalloffOpac: + wave->SetOpacFalloff(val, secs); + break; + case kDepthFalloffRefl: + wave->SetReflFalloff(val, secs); + break; + case kDepthFalloffWave: + wave->SetWaveFalloff(val, secs); + break; + + default: + return false; + } + return true; +} + +static bool ISendWaveCmd2f(PrintFunk PrintString, pfConsoleCmdParam* params, int numParams, plWaveCmd::Cmd cmd) +{ + plWaveSet7* wave = ICheckWaveParams(PrintString, params[0], numParams, 3, cmd); + if( !wave ) + return false; + + using namespace plWaveCmd; + + hsScalar secs = ( numParams > 3 ) ? params[3] : 0.f; + + hsVector3 vec; + plFixedWaterState7 state = wave->State(); + switch( cmd ) + { + case kWindDir: + vec = wave->GetWindDir(); + vec.fX = params[1]; + vec.fY = params[2]; + wave->SetWindDir(vec, secs); + break; + + case kGeoLen: + wave->SetGeoMinLength(params[1], secs); + wave->SetGeoMaxLength(params[2], secs); + break; + + case kTexLen: + wave->SetTexMinLength(params[1], secs); + wave->SetTexMaxLength(params[2], secs); + break; + + case kSpecAtten: + wave->SetSpecularStart(params[1], secs); + wave->SetSpecularEnd(params[2], secs); + break; + + + default: + return false; + } + return true; +} + +static bool ISendWaveCmd3f(PrintFunk PrintString, pfConsoleCmdParam* params, int numParams, plWaveCmd::Cmd cmd) +{ + plWaveSet7* wave = ICheckWaveParams(PrintString, params[0], numParams, 4, cmd); + if( !wave ) + return false; + + using namespace plWaveCmd; + + float x = params[1]; + float y = params[2]; + float z = params[3]; + hsVector3 vec(x, y, z); + hsPoint3 pos(x, y, z); + + hsScalar secs = ( numParams > 4 ) ? params[4] : 0.f; + + switch( cmd ) + { + case kWindDir: + wave->SetWindDir(vec, secs); + break; + case kMinAtten: + wave->SetMinAtten(vec, secs); + break; + case kMaxAtten: + wave->SetMaxAtten(vec, secs); + break; + + case kEnvCenter: + wave->SetEnvCenter(pos, secs); + break; + + default: + return false; + } + return true; +} + +static bool ISendWaveCmd4c(PrintFunk PrintString, pfConsoleCmdParam* params, int numParams, plWaveCmd::Cmd cmd) +{ + plWaveSet7* wave = ICheckWaveParams(PrintString, params[0], numParams, 4, cmd); + if( !wave ) + return false; + + using namespace plWaveCmd; + + float r = params[1]; + float g = params[2]; + float b = params[3]; + + hsScalar secs = ( numParams > 4 ) ? params[4] : 0.f; + + hsColorRGBA col; + col.Set(r / 255.f, g / 255.f, b / 255.f, 1.f); + + switch( cmd ) + { + case kWaterTint: + col.a = wave->GetWaterOpacity(); + wave->SetWaterTint(col, secs); + break; + case kSpecularTint: + col.a = wave->GetSpecularMute(); + wave->SetSpecularTint(col, secs); + break; + + default: + return false; + } + return true; +} + +PF_CONSOLE_CMD( Wave, Log, // Group name, Function name + "string waveSet", // Params none + "Toggle logging for waves" ) // Help string +{ + const char* name = params[0]; + plWaveSet7* waveSet = IGetWaveSet(PrintString, name); + if( waveSet ) + { + hsBool logging = !waveSet->Logging(); + if( logging ) + waveSet->StartLog(); + else + waveSet->StopLog(); + + char buff[256]; + sprintf(buff, "Logging for %s now %s", name, logging ? "on" : "off"); + PrintString(buff); + } +} + +PF_CONSOLE_CMD( Wave, Graph, // Group name, Function name + "string waveSet", // Params none + "Toggle graphing lens for waves" ) // Help string +{ + const char* name = params[0]; + plWaveSet7* waveSet = IGetWaveSet(PrintString, name); + if( waveSet ) + { + hsBool graphing = !waveSet->Graphing(); + if( graphing ) + waveSet->StartGraph(); + else + waveSet->StopGraph(); + + char buff[256]; + sprintf(buff, "Graphing for %s now %s", name, graphing ? "on" : "off"); + PrintString(buff); + } +} + +// Geometric wave param block +PF_CONSOLE_CMD( Wave_Set, GeoLen, // Group name, Function name + "string waveSet, ...", // Params none + "Set and geometric wavelengths in feet" ) // Help string +{ + ISendWaveCmd2f(PrintString, params, numParams, plWaveCmd::kGeoLen); +} + +PF_CONSOLE_CMD( Wave_Set, GeoAmp, // Group name, Function name + "string waveSet, ...", // Params none + "Set geometric wave ratio of Amplitude to Wavelengths (as percentage)" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kGeoAmp); +} + +PF_CONSOLE_CMD( Wave_Set, GeoChop, // Group name, Function name + "string waveSet, ...", // Params none + "Set current geometric wave choppiness" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kGeoChop); +} + +PF_CONSOLE_CMD( Wave_Set, GeoAngle, // Group name, Function name + "string waveSet, ...", // Params none + "Set geometric wave angular Spread about wind direction (in degrees)" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kGeoAngle); +} + +PF_CONSOLE_CMD( Wave_Set, ReflTint, // Group name, Function name + "string waveSet, ...", // Params none + "Set current reflection tint (r g b)" ) // Help string +{ + ISendWaveCmd4c(PrintString, params, numParams, plWaveCmd::kSpecularTint); +} + +PF_CONSOLE_CMD( Wave_Set, ReflMute, // Group name, Function name + "string waveSet, ...", // Params none + "Set current reflection muting f (100 % no muting, 0% all gone)" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kSpecularMute); +} + +// Texture wave param block + +PF_CONSOLE_CMD( Wave_Set, TexLen, // Group name, Function name + "string waveSet, ...", // Params none + "Set and texture wavelengths in feet" ) // Help string +{ + ISendWaveCmd2f(PrintString, params, numParams, plWaveCmd::kTexLen); +} + +PF_CONSOLE_CMD( Wave_Set, TexAmp, // Group name, Function name + "string waveSet, ...", // Params none + "Set texture wave ratio of Amplitude to Wavelengths (as percentage)" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kTexAmp); +} + +PF_CONSOLE_CMD( Wave_Set, TexChop, // Group name, Function name + "string waveSet, ...", // Params none + "Set current texture wave choppiness" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kTexChop); +} + +PF_CONSOLE_CMD( Wave_Set, Noise, // Group name, Function name + "string waveSet, ...", // Params none + "Set current noising of texture waves (as percentage)" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kNoise); +} + +PF_CONSOLE_CMD( Wave_Set, Scale, // Group name, Function name + "string waveSet, ...", // Params none + "Set current water ripple scale f" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kRippleScale); +} + +PF_CONSOLE_CMD( Wave_Set, TexAngle, // Group name, Function name + "string waveSet, ...", // Params none + "Set texture wave spread about wind dir (in degrees)" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kTexAngle); +} + +PF_CONSOLE_CMD( Wave_Set, SpecAtten, // Group name, Function name + "string waveSet, ...", // Params none + "Set falloff of ripples from to in feet" ) // Help string +{ + ISendWaveCmd2f(PrintString, params, numParams, plWaveCmd::kSpecAtten); +} + +// Minor water param block + +PF_CONSOLE_CMD( Wave_Set, OpacStart, // Group name, Function name + "string waveSet, ...", // Params none + "Set current water opacity start f" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kWaterOffsetOpac); +} + +PF_CONSOLE_CMD( Wave_Set, OpacEnd, // Group name, Function name + "string waveSet, ...", // Params none + "Set current water opacity end f" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kDepthFalloffOpac); +} + +PF_CONSOLE_CMD( Wave_Set, ReflStart, // Group name, Function name + "string waveSet, ...", // Params none + "Set current water reflection start f" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kWaterOffsetRefl); +} + +PF_CONSOLE_CMD( Wave_Set, ReflEnd, // Group name, Function name + "string waveSet, ...", // Params none + "Set current water refleciton end f" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kDepthFalloffRefl); +} + +PF_CONSOLE_CMD( Wave_Set, WaveStart, // Group name, Function name + "string waveSet, ...", // Params none + "Set current water wave start f" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kWaterOffsetWave); +} + +PF_CONSOLE_CMD( Wave_Set, WaveEnd, // Group name, Function name + "string waveSet, ...", // Params none + "Set current water wave end f" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kDepthFalloffWave); +} + +// Reflection param block + +PF_CONSOLE_CMD( Wave_Set, EnvCenter, // Group name, Function name + "string waveSet, ...", // Params none + "Set current EnvMap Center (x y z)" ) // Help string +{ + ISendWaveCmd3f(PrintString, params, numParams, plWaveCmd::kEnvCenter); +} + +PF_CONSOLE_CMD( Wave_Set, Radius, // Group name, Function name + "string waveSet, ...", // Params none + "Set current envmap radius f" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kEnvRadius); +} + +// Misc. These are normally implicit by associated data (ref object or material). +PF_CONSOLE_CMD( Wave_Set, Direction, // Group name, Function name + "string waveSet, ...", // Params none + "Set current wind direction (x y)" ) // Help string +{ + ISendWaveCmd2f(PrintString, params, numParams, plWaveCmd::kWindDir); +} + + +PF_CONSOLE_CMD( Wave_Set, WaterTint, // Group name, Function name + "string waveSet, ...", // Params none + "Set current water tint (r g b)" ) // Help string +{ + ISendWaveCmd4c(PrintString, params, numParams, plWaveCmd::kWaterTint); +} + + +PF_CONSOLE_CMD( Wave_Set, Opacity, // Group name, Function name + "string waveSet, ...", // Params none + "Set current water max opacity f" ) // Help string +{ + ISendWaveCmd1f(PrintString, params, numParams, plWaveCmd::kWaterOpacity); +} + +#endif // LIMIT_CONSOLE_COMMANDS + + + +////////////////////////////////////////////////////////////////////////////// +//// Object Console Commands //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + + +PF_CONSOLE_GROUP( SceneObject ) + +PF_CONSOLE_SUBGROUP( SceneObject, SetEnable) // Creates a sub-group under a given group + + +PF_CONSOLE_CMD( SceneObject_SetEnable, Drawable, // Group name, Function name + "string objName, bool on", // Params none + "Enable or disable drawing of a sceneobject" ) // Help string +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + PrintString(str); + if (!key) + return; + + bool enable = params[1]; + + plEnableMsg* pEMsg = TRACKED_NEW plEnableMsg; + pEMsg->SetCmd( enable ? plEnableMsg::kEnable : plEnableMsg::kDisable ); + pEMsg->SetCmd( plEnableMsg::kDrawable ); + pEMsg->AddReceiver( key ); + plgDispatch::MsgSend( pEMsg ); +} + +PF_CONSOLE_CMD( SceneObject_SetEnable, Physical, // Group name, Function name + "string objName, bool on", // Params none + "Enable or disable the physical of a sceneobject" ) // Help string +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + PrintString(str); + if (!key) + return; + + bool enable = params[1]; + + plEnableMsg* pEMsg = TRACKED_NEW plEnableMsg; + pEMsg->SetCmd( enable ? plEnableMsg::kEnable : plEnableMsg::kDisable ); + pEMsg->SetCmd( plEnableMsg::kPhysical ); + pEMsg->AddReceiver( key ); + plgDispatch::MsgSend( pEMsg ); +} +/* +PF_CONSOLE_CMD( SceneObject_SetEnable, PhysicalT, // Group name, Function name + "string objName, bool on", // Params none + "Enable or disable the physical of a sceneobject" ) // Help string +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + PrintString(str); + if (!key) + return; + + bool enable = params[1]; + + plEventGroupEnableMsg* pMsg = TRACKED_NEW plEventGroupEnableMsg; + if( enable ) + pMsg->SetFlags(plEventGroupEnableMsg::kCollideOn | plEventGroupEnableMsg::kReportOn); + else + pMsg->SetFlags(plEventGroupEnableMsg::kCollideOff | plEventGroupEnableMsg::kReportOff); + + pMsg->AddReceiver(key); + pMsg->Send(); + +} +*/ +PF_CONSOLE_CMD( SceneObject_SetEnable, Audible, // Group name, Function name + "string objName, bool on", // Params none + "Enable or disable the audible of a sceneobject" ) // Help string +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + PrintString(str); + if (!key) + return; + + bool enable = params[1]; + + plEnableMsg* pEMsg = TRACKED_NEW plEnableMsg; + pEMsg->SetCmd( enable ? plEnableMsg::kEnable : plEnableMsg::kDisable ); + pEMsg->SetCmd( plEnableMsg::kAudible ); + pEMsg->AddReceiver( key ); + plgDispatch::MsgSend( pEMsg ); +} + +PF_CONSOLE_CMD( SceneObject_SetEnable, All, // Group name, Function name + "string objName, bool on", // Params none + "Enable or disable all fxns of a sceneobject" ) // Help string +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + PrintString(str); + if (!key) + return; + + bool enable = params[1]; + + plEnableMsg* pEMsg = TRACKED_NEW plEnableMsg; + pEMsg->SetCmd( enable ? plEnableMsg::kEnable : plEnableMsg::kDisable ); + pEMsg->SetCmd( plEnableMsg::kAll ); + pEMsg->AddReceiver( key ); + plgDispatch::MsgSend( pEMsg ); +} + +PF_CONSOLE_CMD( SceneObject, Attach, // Group name, Function name + "string childName, string parentName", // Params none + "Attach child to parent" ) // Help string +{ + char str[256]; + + const char* childName = params[0]; + const char* parentName = params[1]; + + plKey childKey = FindSceneObjectByName(childName, nil, str); + if( !childKey ) + { + PrintString(str); + return; + } + plSceneObject* child = plSceneObject::ConvertNoRef(childKey->GetObjectPtr()); + if( !child ) + { + sprintf( str, "Child SceneObject not found"); + PrintString(str); + return; + } + + plKey parentKey = FindSceneObjectByName(parentName, nil, str); + if( !parentKey ) + { + PrintString(str); + return; + } + + plAttachMsg* attMsg = TRACKED_NEW plAttachMsg(parentKey, child, plRefMsg::kOnRequest, nil); + plgDispatch::MsgSend(attMsg); + + sprintf(str, "%s now child of %s", childName, parentName); + PrintString(str); + +} + +PF_CONSOLE_CMD( SceneObject, Detach, // Group name, Function name + "string childName", // Params none + "Detach a child from parent (if any)" ) // Help string +{ + char str[256]; + + const char* childName = params[0]; + + plKey childKey = FindSceneObjectByName(childName, nil, str); + if( !childKey ) + { + PrintString(str); + return; + } + plSceneObject* child = plSceneObject::ConvertNoRef(childKey->GetObjectPtr()); + if( !child ) + { + sprintf( str, "Child SceneObject not found"); + PrintString(str); + return; + } + + if( child + && child->GetCoordinateInterface() + && child->GetCoordinateInterface()->GetParent() + && child->GetCoordinateInterface()->GetParent()->GetOwner() ) + { + plKey parentKey = child->GetCoordinateInterface()->GetParent()->GetOwner()->GetKey(); + plAttachMsg* attMsg = TRACKED_NEW plAttachMsg(parentKey, child, plRefMsg::kOnRemove, nil); + plgDispatch::MsgSend(attMsg); + + sprintf(str, "%s detached from %s", childName, parentKey->GetName()); + PrintString(str); + return; + } + else + { + sprintf(str, "%s not attached to anything", childName); + PrintString(str); + return; + } +} + +#endif // LIMIT_CONSOLE_COMMANDS + +////////////////////////////////////////////////////////////// +// PHYSICS (The Havok Flavour) +////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +#include "../plPhysX/plPXPhysicalControllerCore.h" + +PF_CONSOLE_GROUP( Physics ) + +PF_CONSOLE_CMD( Physics, Rebuild, "", "Rebuilds the avatars collision cache") +{ + plPXPhysicalControllerCore::RebuildCache(); +} + +PF_CONSOLE_CMD(Physics, MaxPhysicalAvatars, "int max", "Set the maximum number of avatar physicals allowed. Default = 0 (meaning no limit)") +{ + int max = params[0]; + plPXPhysicalControllerCore::SetMaxNumberOfControllers(max); +} + +/* +PF_CONSOLE_CMD( Physics, SetStepsPerSecond, "int steps", "Sets the number of physics substeps per second, regardless of rendering framerate.") +{ + int newSteps = params[0]; + plSimulationMgr::GetInstance()->SetStepsPerSecond(newSteps); +} + +PF_CONSOLE_CMD( Physics, GetStepsPerSecond, "", "Prints the number of physics substeps per second.") +{ + int steps = plSimulationMgr::GetInstance()->GetStepsPerSecond(); + + char buffy[256]; + sprintf(buffy, "Current physics resolution is %d frames per second.", steps); + PrintString(buffy); +} + +PF_CONSOLE_CMD(Physics, SetMaxDelta, "float maxDelta", "Sets the largest frame-to-frame delta that physics will try to resolve before giving up and freezing.") +{ + float newMaxDelta = params[0]; + plSimulationMgr::GetInstance()->SetMaxDelta(newMaxDelta); +} + +PF_CONSOLE_CMD(Physics, GetMaxDelta, "", "Prints the largest frame-to-frame delta that physics will try to resolve before giving up and freezing.") +{ + float oldMaxDelta = plSimulationMgr::GetInstance()->GetMaxDelta(); + + char buffy[256]; + sprintf(buffy, "When (delta > %f), physics is suspended for that frame.", oldMaxDelta); + PrintString(buffy); +} + +PF_CONSOLE_CMD(Physics, SetDeactivateFreq, "float freq", "") +{ + float freq = params[0]; + plSimulationMgr::GetInstance()->SetDeactivateFreq(freq); +} + +PF_CONSOLE_CMD(Physics, SetCollisionTolerance, "float tol", "Minimum distance objects must be from each other to collide. Set from an ini file.") +{ + float tol = params[0]; + plHKPhysicsContext::SetCollisionTolerance(tol); +} + +PF_CONSOLE_CMD( Physics, Suspend, "", "Toggle suspend/resume physics.") +{ + if(plSimulationMgr::GetInstance()->IsSuspended()) + plSimulationMgr::GetInstance()->Resume(); + else + plSimulationMgr::GetInstance()->Suspend(); +} + +PF_CONSOLE_CMD( Physics, ShowExternal, "", "Display a snapshot of the world as Havok sees it. Requires separate debug app." ) +{ + plSimulationMgr::GetInstance()->ExternalDebugDisplay(); +} + +//PF_CONSOLE_CMD( Physics, SetGravity, "float fpsps", "Set gravity in feet per second per second.") +//{ +// plSimulationMgr::GetInstance()->SetGravity(0,0,params[0]); +//} + +PF_CONSOLE_CMD( Physics, ApplyForce, "string Object, float x, float y, float z", "Apply a force to a scene object at its center of mass.") +{ + plKey key = FindSceneObjectByName(params[0], nil, nil); + + if(key) + { + hsVector3 force(params[1], params[2], params[3]); + plForceMsg *m = TRACKED_NEW plForceMsg(nil, key, force); + plgDispatch::MsgSend(m); + } +} + +PF_CONSOLE_CMD( Physics, ApplyForceAtPoint, "string Object, float forceX, float forceY, float forceZ, float pointX, float pointY, float pointZ", "Apply a force to a scene object at a particular point in world space.") +{ + plKey key = FindSceneObjectByName(params[0], nil, nil, nil); + + if(key) + { + hsVector3 force(params[1], params[2], params[3]); + hsPoint3 point(params[4], params[5], params[6]); + plOffsetForceMsg *m = TRACKED_NEW plOffsetForceMsg(nil, key, force, point); + plgDispatch::MsgSend(m); + } +} + +PF_CONSOLE_CMD( Physics, ApplyTorque, "string Object, float axisX, float axisY, float axisZ", "Apply a torque to a scene object about given axis. Magnitude is size of force.") +{ + plKey key = FindSceneObjectByName(params[0], nil, nil); + + if(key) + { + hsVector3 torque(params[1], params[2], params[3]); + plTorqueMsg *m = TRACKED_NEW plTorqueMsg(nil, key, torque); + plgDispatch::MsgSend(m); + } +} + +PF_CONSOLE_CMD( Physics, ApplyImpulse, "string Object, float x, float y, float z", "Apply an impulse to a scene object.") +{ + plKey key = FindSceneObjectByName(params[0], nil, nil); + + if(key) + { + hsVector3 impulse(params[1], params[2], params[3]); + plImpulseMsg *m = TRACKED_NEW plImpulseMsg(nil, key, impulse); + plgDispatch::MsgSend(m); + } +} + +PF_CONSOLE_CMD( Physics, ApplyImpulseAtPoint, "string Object, float impulseX, float impulseY, float impulseZ, float pointX, float pointY, float pointZ", "Apply an impulse to a scene object at a particular point in world space.") +{ + plKey key = FindSceneObjectByName(params[0], nil, nil); + + if(key) + { + hsVector3 impulse(params[1], params[2], params[3]); + hsPoint3 point(params[4], params[5], params[6]); + plOffsetImpulseMsg *m = TRACKED_NEW plOffsetImpulseMsg(nil, key, impulse, point); + plgDispatch::MsgSend(m); + } +} + +PF_CONSOLE_CMD( Physics, ApplyAngularImpulse, "string Object, float x, float y, float z", "Apply a rotational impulse about the given axis a scene object. Magnitude is strength.") +{ + plKey key = FindSceneObjectByName(params[0], nil, nil); + + if(key) + { + hsVector3 impulse(params[1], params[2], params[3]); + plAngularImpulseMsg *m = TRACKED_NEW plAngularImpulseMsg(nil, key, impulse); + plgDispatch::MsgSend(m); + } +} + +PF_CONSOLE_CMD( Physics, ApplyDamping, "string Object, float dampFactor", "Reduce the velocity and spin of the object to the given percentage.") +{ + plKey key = FindSceneObjectByName(params[0], nil, nil); + + if(key) + { + float dampFactor = params[1]; + plDampMsg *m = TRACKED_NEW plDampMsg(nil, key, dampFactor); + plgDispatch::MsgSend(m); + } +} + +PF_CONSOLE_CMD( Physics, ShiftMass, "string Object, float x, float y, float z", "Shift the object's center of mass.") +{ + plKey key = FindSceneObjectByName(params[0], nil, nil); + + if(key) + { + hsVector3 offset(params[1], params[2], params[3]); + plShiftMassMsg *m = TRACKED_NEW plShiftMassMsg(nil, key, offset); + plgDispatch::MsgSend(m); + } +} + +PF_CONSOLE_CMD( Physics, Suppress, "string Object, int doSuppress", "Remove(true) or re-add the named physical from/to the simulation.") +{ + plKey key = FindSceneObjectByName(params[0], nil, nil); + if(key) + { + int iDoSuppress = params[1]; + + bool doSuppress = iDoSuppress ? true : false; + plSimSuppressMsg *msg = TRACKED_NEW plSimSuppressMsg(nil, key, doSuppress); + msg->Send(); + } +} + +PF_CONSOLE_CMD( Physics, SetEventGroup, "string Object, int group, int status, int clearOthers", "Add to or remove from physics event group.") +{ + plKey key = FindSceneObjectByName(params[0], nil, nil); + + if(key) + { + int group = params[1], status = params[2], clearOthers = params[3]; + plEventGroupMsg *m = TRACKED_NEW plEventGroupMsg(nil, key, group, status, clearOthers); + plgDispatch::MsgSend(m); + } +} + +PF_CONSOLE_CMD( Physics, Freeze, "string Object, int status", "Immobilize the given simulated object.") +{ + plKey key = FindSceneObjectByName(params[0], nil, nil); + + if(key) + { + int status = params[1]; + + plFreezeMsg *m = TRACKED_NEW plFreezeMsg(nil, key, nil, status); + + plgDispatch::MsgSend(m); + } +} + +#include "../plHavok1/plHKCollision.h" + +PF_CONSOLE_CMD( Physics, ToggleShowImpacts, "", "Shows the names of impacting physicals on screen.") +{ + plHKCollision::ToggleDisplayImpacts(); +} + +PF_CONSOLE_CMD( Physics, DumpRejectedBroadphase, "", "") +{ + plSimulationMgr::GetInstance()->DumpRejectedBroadPhase(true); +} + +extern int gPhysicsAnimatedOptimize; +PF_CONSOLE_CMD( Physics, OptimizeAnimatedPhysicals, "int enable", "if true then dont eval non moving physical animations") +{ + gPhysicsAnimatedOptimize = params[0]; +} + +PF_CONSOLE_CMD( Physics, ClearLog, "", "Clear the physics log.") +{ + plSimulationMgr::ClearLog(); +} +*/ +#include "../plPhysical/plPhysicalSDLModifier.h" + +PF_CONSOLE_CMD(Physics, LogSDL, "int level", "Turn logging of physics SDL state on or off. 0=off 1=send/receive only 2=any attempt") +{ + int level = params[0]; + plPhysicalSDLModifier::SetLogLevel(level); +} + +#include "../plPhysX/plSimulationMgr.h" +PF_CONSOLE_CMD(Physics, ExtraProfile, "", "Toggle extra simulation profiling") +{ + char str[256]; + if (plSimulationMgr::fExtraProfile) + { + plSimulationMgr::fExtraProfile = false; + sprintf(str, "Stop extra profiling"); + } + else + { + plSimulationMgr::fExtraProfile = true; + sprintf(str, "Start extra profiling"); + } + PrintString( str ); +} +PF_CONSOLE_CMD(Physics, SubworldOptimization, "", "Toggle subworld optimization") +{ + char str[256]; + if (plSimulationMgr::fSubworldOptimization) + { + plSimulationMgr::fSubworldOptimization = false; + sprintf(str, "Stop subworld optimization"); + } + else + { + plSimulationMgr::fSubworldOptimization = true; + sprintf(str, "Start subworld optimization"); + } + PrintString( str ); +} +PF_CONSOLE_CMD(Physics, ClampingOnStep, "", "Toggle whether to clamp the step size on advance") +{ + char str[256]; + if (plSimulationMgr::fDoClampingOnStep) + { + plSimulationMgr::fDoClampingOnStep = false; + sprintf(str, "Stop clamping the step size"); + } + else + { + plSimulationMgr::fDoClampingOnStep = true; + sprintf(str, "Start clamping the step size"); + } + PrintString( str ); +} + +PF_CONSOLE_CMD(Physics, + ShowControllerDebugDisplay, + "", + "Toggle the physics controller debug display") +{ + plPXPhysicalControllerCore::fDebugDisplay = !plPXPhysicalControllerCore::fDebugDisplay; +} +PF_CONSOLE_CMD(Physics, + ListAwakeActors, + "", + "Toggles displaying the list of awake actors") +{ + plSimulationMgr::fDisplayAwakeActors= !plSimulationMgr::fDisplayAwakeActors; +} + +/* +PF_CONSOLE_CMD( Physics, PlayPhysicsSounds, "bool b", "Turn physics sounds on/off.") +{ + bool b = params[0]; + plHKCollision::TogglePhysicsSounds(b); +} +*/ +#endif // LIMIT_CONSOLE_COMMANDS + + +////////////////////////////////////////////////////////////// +// Mouse controls +////////////////////////////////////////////////////////////// + + +PF_CONSOLE_GROUP( Mouse ) + +PF_CONSOLE_CMD( Mouse, Invert, nil, "invert the mouse") +{ + plMouseDevice::SetInverted(true); +} + +PF_CONSOLE_CMD( Mouse, UnInvert, nil, "un-invert the mouse") +{ + plMouseDevice::SetInverted(false); +} + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Mouse, SetDeadZone, "float zone", "Sets the dead zone for the mouse - range is from 0.0 to 1.0") +{ + hsScalar f = params[0]; +} + +PF_CONSOLE_CMD( Mouse, Enable, nil, "Enable mouse input") +{ +// plCommandInterfaceModifier::GetInstance()->EnableMouseInput(); +} +PF_CONSOLE_CMD( Mouse, Disable, nil, "Disable mouse input") +{ +// plCommandInterfaceModifier::GetInstance()->DisableMouseInput(); +} + +PF_CONSOLE_CMD( Mouse, SetFadeDelay, "float delayInSecs", "Set how long the cursor has to not move before it fades out" ) +{ + if( plAvatarInputInterface::GetInstance() != nil ) + plAvatarInputInterface::GetInstance()->SetCursorFadeDelay( params[ 0 ] ); +} + +PF_CONSOLE_CMD( Mouse, Hide, nil, "hide mouse cursor") +{ + plMouseDevice::HideCursor(true); +} + +PF_CONSOLE_CMD( Mouse, Show, nil, "hide mouse cursor") +{ + plMouseDevice::ShowCursor(true); +} + +/*PF_CONSOLE_CMD( Mouse, SetScale, "float scale", "Sets the mouse scaling factor (sensitivity)" ) +{ + plInputManager::GetInstance()->SetMouseScale( params[ 0 ] ); + PrintStringF( PrintString, "Mouse scale factor set to %4.2f", params[ 0 ] ); +} +*/ + +PF_CONSOLE_CMD( Mouse, ForceHide, "bool force", "Forces the mouse to be hidden (or doesn't)" ) +{ + plInputInterfaceMgr::GetInstance()->ForceCursorHidden( (bool)params[ 0 ] ); + PrintStringF( PrintString, "Mouse cursor %s", params[ 0 ] ? "forced to be hidden" : "back to normal" ); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +////////////////////////////////////////////////////////////////////////////// +//// Age Group Commands ////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_GROUP( Age ) + +PF_CONSOLE_CMD(Age, ShowSDL, "", "Prints the age SDL values") +{ + plStateDataRecord * rec = NEWZERO(plStateDataRecord); + if (!VaultAgeGetAgeSDL(rec)) { + PrintString("Age SDL not found"); + DEL(rec); + return; + } + + char line[2048]; + + plStatusLog::AddLineS("ShowSDL.log", "-----------------------------------"); + for (unsigned i = 0; i < rec->GetNumVars(); ++i) { + plStateVariable * var = rec->GetVar(i); + if (plSimpleStateVariable * simple = var->GetAsSimpleStateVar()) { + const char * name = var->GetName(); + StrPrintf(line, arrsize(line), "%s=", name); + for (unsigned j = 0; j < simple->GetCount(); ++j) { + char * str = simple->GetAsString(j); + StrPack(line, str, arrsize(line)); + StrPack(line, ",", arrsize(line)); + FREE(str); + } + PrintString(line); + plStatusLog::AddLineS("ShowSDL.log", "%s", line); + } + } + + DEL(rec); +} + +PF_CONSOLE_CMD( Age, GetElapsedDays, "string agedefnfile", "Gets the elapsed days and fractions" ) +{ + hsUNIXStream s; + if (!s.Open(params[0])) + { + PrintString("Couldn't open age defn file!"); + return; + } + + plAgeDescription age; + age.Read(&s); + + char str[256]; + plUnifiedTime current; + current.SetToUTC(); + sprintf(str,"ElapsedTime: %f Days",age.GetAgeElapsedDays(current)); + + PrintString(str); + s.Close(); +} + +PF_CONSOLE_CMD( Age, GetTimeOfDay, "string agedefnfile", "Gets the elapsed days and fractions" ) +{ + hsUNIXStream s; + if (!s.Open(params[0])) + { + PrintString("Couldn't open age defn file!"); + return; + } + + plAgeDescription age; + age.Read(&s); + + char str[256]; + plUnifiedTime current; + current.SetToUTC(); + sprintf(str,"TimeOfDay: %f percent",age.GetAgeTimeOfDayPercent(current)); + + PrintString(str); + s.Close(); +} + +PF_CONSOLE_CMD( Age, SetSDLFloat, "string varName, float value, int index", "Set the value of an age global variable" ) +{ + int index = (int)params[2]; + + extern const plPythonSDLModifier *ExternFindAgePySDL(); + const plPythonSDLModifier *sdlMod = ExternFindAgePySDL(); + if (!sdlMod) + return; + + plSimpleStateVariable *var = sdlMod->GetStateCache()->FindVar(params[0]); + if (!var) + return; + + float v; + var->Get(&v, index); + var->Set((float)params[1], index); + // set the variable in the pythonSDL also + ((plPythonSDLModifier*)sdlMod)->SetItemFromSDLVar(var); + // set it back to original so that its different + plSynchedObject* p = plSynchedObject::ConvertNoRef(((plSDLModifier*)sdlMod)->GetStateOwnerKey()->GetObjectPtr()); + if (p) + p->DirtySynchState(sdlMod->GetSDLName(),plSynchedObject::kSendImmediately|plSynchedObject::kSkipLocalOwnershipCheck|plSynchedObject::kForceFullSend); +} + +PF_CONSOLE_CMD( Age, SetSDLInt, "string varName, int value, int index", "Set the value of an age global variable" ) +{ + int index = (int)params[2]; + + extern const plPythonSDLModifier *ExternFindAgePySDL(); + const plPythonSDLModifier *sdlMod = ExternFindAgePySDL(); + if (!sdlMod) + return; + + plSimpleStateVariable *var = sdlMod->GetStateCache()->FindVar(params[0]); + if (!var) + return; + + int v; + var->Get(&v, index); + var->Set((int)params[1], index); + // set the variable in the pythonSDL also + ((plPythonSDLModifier*)sdlMod)->SetItemFromSDLVar(var); + plSynchedObject* p = plSynchedObject::ConvertNoRef(((plSDLModifier*)sdlMod)->GetStateOwnerKey()->GetObjectPtr()); + if (p) + p->DirtySynchState(sdlMod->GetSDLName(),plSynchedObject::kSendImmediately|plSynchedObject::kSkipLocalOwnershipCheck|plSynchedObject::kForceFullSend); +} + +PF_CONSOLE_CMD( Age, SetSDLBool, "string varName, bool value, int index", "Set the value of an age global variable" ) +{ + int index = (int)params[2]; + + extern const plPythonSDLModifier *ExternFindAgePySDL(); + const plPythonSDLModifier *sdlMod = ExternFindAgePySDL(); + if (!sdlMod) + return; + + plSimpleStateVariable *var = sdlMod->GetStateCache()->FindVar(params[0]); + if (!var) + return; + + bool v; + var->Get(&v, index); + var->Set((bool)params[1], index); + // set the variable in the pythonSDL also + ((plPythonSDLModifier*)sdlMod)->SetItemFromSDLVar(var); + plSynchedObject* p = plSynchedObject::ConvertNoRef(((plSDLModifier*)sdlMod)->GetStateOwnerKey()->GetObjectPtr()); + if (p) + p->DirtySynchState(sdlMod->GetSDLName(),plSynchedObject::kSendImmediately|plSynchedObject::kSkipLocalOwnershipCheck|plSynchedObject::kForceFullSend); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +////////////////////////////////////////////////////////////////////////////// +//// Particle System Group Commands ///////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_GROUP( ParticleSystem ) // Defines a main command group + +void UpdateParticleParam(char *objName, Int32 paramID, hsScalar value, void (*PrintString)(const char *)) +{ + char str[256]; + plKey key = FindSceneObjectByName(objName, nil, str); + PrintString(str); + if (key == nil) return; + + plSceneObject* so = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if (so == nil) return; + + int i; + for (i = so->GetNumModifiers() - 1; i >= 0; i--) + { + const plParticleSystem *sys = plParticleSystem::ConvertNoRef(so->GetModifier(i)); + if (sys != nil) + { + plgDispatch::MsgSend(TRACKED_NEW plParticleUpdateMsg(nil, sys->GetKey(), nil, paramID, value)); + PrintString("Particle system successfully updated."); + return; + } + } + PrintString("The scene object specified has no particle system."); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetPPS, // Function name + "string objName, float value", // Params + "Set the particles-per-second generated" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamParticlesPerSecond, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetInitialPitchRange, // Function name + "string objName, float value", // Params + "Set the initial range of pitch of generated particles" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamInitPitchRange, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetInitialYawRange, // Function name + "string objName, float value", // Params + "Set the initial range of yaw of generated particles" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamInitYawRange, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetInitialVelocityMin, // Function name + "string objName, float value", // Params + "Set the minimum initial velocity of generated particles" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamVelMin, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetInitialVelocityMax, // Function name + "string objName, float value", // Params + "Set the maximum initial velocity of generated particles" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamVelMax, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetWidth, // Function name + "string objName, float value", // Params + "Set the width of generated particles" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamXSize, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetHeight, // Function name + "string objName, float value", // Params + "Set the height of generated particles" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamYSize, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetScaleMin, // Function name + "string objName, float value", // Params + "Set the minimum width/height scaling of generated particles" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamScaleMin, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetScaleMax, // Function name + "string objName, float value", // Params + "Set the maximum width/height scaling of generated particles" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamScaleMax, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetGeneratorLife, // Function name + "string objName, float value", // Params + "Set the remaining life of the particle generator" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamGenLife, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetParticleLifeMin, // Function name + "string objName, float value", // Params + "Set the minimum lifespan of generated particles (negative values make them immortal)" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamPartLifeMin, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, // Group name + SetParticleLifeMax, // Function name + "string objName, float value", // Params + "Set the max lifespan of generated particles" ) // Help string +{ + UpdateParticleParam(params[0], plParticleUpdateMsg::kParamPartLifeMax, params[1], PrintString); +} + +PF_CONSOLE_CMD( ParticleSystem, + TransferParticlesToAvatar, + "string objName, int numParticles", + "Creates a system (if necessary) on the avatar, and transfers particles" ) +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + if (key == nil) + return; + + plSceneObject* so = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if (so == nil) + return; + + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + (TRACKED_NEW plParticleTransferMsg(nil, avMod->GetKey(), 0, so->GetKey(), (int)params[1]))->Send(); +} + +PF_CONSOLE_CMD( ParticleSystem, + KillParticles, + "string objName, float timeLeft, float num, bool useAsPercentage", + "Flag some particles for death." ) +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + if (key == nil) + return; + + plSceneObject* so = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if (so == nil) + return; + + const plParticleSystem *sys = plParticleSystem::ConvertNoRef(so->GetModifierByType(plParticleSystem::Index())); + if (sys != nil) + { + UInt8 flags = (params[3] ? plParticleKillMsg::kParticleKillPercentage : 0); + (TRACKED_NEW plParticleKillMsg(nil, sys->GetKey(), 0, params[2], params[1], flags))->Send(); + } +} + + +PF_CONSOLE_SUBGROUP( ParticleSystem, Flock ) + +static plParticleFlockEffect *FindFlock(char *objName) +{ + char str[256]; + plKey key = FindSceneObjectByName(objName, nil, str); + + if (key == nil) + return nil; + + plSceneObject *so = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if (so == nil) + return nil; + + const plParticleSystem *sys = plParticleSystem::ConvertNoRef(so->GetModifierByType(plParticleSystem::Index())); + if (sys == nil) + return nil; + + plParticleFlockEffect *flock = plParticleFlockEffect::ConvertNoRef(sys->GetEffect(plParticleFlockEffect::Index())); + if (flock == nil) + return nil; + + return flock; +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetFlockOffset, + "string objName, float x, float y, float z", + "Set the flock's goal to be an offset from its sceneObject") +{ + plParticleEffect *flock = FindFlock(params[0]); + if (flock) + { + (TRACKED_NEW plParticleFlockMsg(nil, flock->GetKey(), 0, plParticleFlockMsg::kFlockCmdSetOffset, params[1], params[2], params[3]))->Send(); + } +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetDissentTarget, + "string objName, float x, float y, float z", + "Set the goal for particles that leave the flock") +{ + plParticleEffect *flock = FindFlock(params[0]); + if (flock) + { + (TRACKED_NEW plParticleFlockMsg(nil, flock->GetKey(), 0, plParticleFlockMsg::kFlockCmdSetDissentPoint, params[1], params[2], params[3]))->Send(); + } +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetConformDistance, + "string objName, float value", + "") +{ + plParticleFlockEffect *flock = FindFlock(params[0]); + if (flock) + flock->SetInfluenceAvgRadius(params[1]); + else + PrintString("Can't find flock effect"); +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetRepelDistance, + "string objName, float value", + "") +{ + plParticleFlockEffect *flock = FindFlock(params[0]); + if (flock) + flock->SetInfluenceRepelRadius(params[1]); + else + PrintString("Can't find flock effect"); +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetGoalDistance, + "string objName, float value", + "") +{ + plParticleFlockEffect *flock = FindFlock(params[0]); + if (flock) + flock->SetGoalRadius(params[1]); + else + PrintString("Can't find flock effect"); +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetFullChaseDistance, + "string objName, float value", + "") +{ + plParticleFlockEffect *flock = FindFlock(params[0]); + if (flock) + flock->SetFullChaseRadius(params[1]); + else + PrintString("Can't find flock effect"); +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetConformStr, + "string objName, float value", + "") +{ + plParticleFlockEffect *flock = FindFlock(params[0]); + if (flock) + flock->SetConformStr(params[1]); + else + PrintString("Can't find flock effect"); +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetRepelStr, + "string objName, float value", + "") +{ + plParticleFlockEffect *flock = FindFlock(params[0]); + if (flock) + flock->SetRepelStr(params[1]); + else + PrintString("Can't find flock effect"); +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetGoalOrbitStr, + "string objName, float value", + "") +{ + plParticleFlockEffect *flock = FindFlock(params[0]); + if (flock) + flock->SetGoalOrbitStr(params[1]); + else + PrintString("Can't find flock effect"); +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetGoalChaseStr, + "string objName, float value", + "") +{ + plParticleFlockEffect *flock = FindFlock(params[0]); + if (flock) + flock->SetGoalChaseStr(params[1]); + else + PrintString("Can't find flock effect"); +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetMaxOrbitSpeed, + "string objName, float value", + "") +{ + plParticleFlockEffect *flock = FindFlock(params[0]); + if (flock) + flock->SetMaxOrbitSpeed(params[1]); + else + PrintString("Can't find flock effect"); +} + +PF_CONSOLE_CMD( ParticleSystem_Flock, + SetMaxChaseSpeed, + "string objName, float value", + "") +{ + plParticleFlockEffect *flock = FindFlock(params[0]); + if (flock) + flock->SetMaxChaseSpeed(params[1]); + else + PrintString("Can't find flock effect"); +} + +#endif // LIMIT_CONSOLE_COMMANDS + + +////////////////////////////////////////////////////////////////////////////// +//// Animation Commands ////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_GROUP( Animation ) // Defines a main command group + +void SendAnimCmdMsg(char *objName, plMessage *msg) +{ + char str[256]; + plKey key = FindSceneObjectByName(objName, nil, str); + if (key != nil) + { + msg->AddReceiver(key); + plgDispatch::MsgSend(msg); + } + else // message wasn't sent + delete msg; +} + + +PF_CONSOLE_CMD( Animation, // Group name + Start, // Function name + "string objName, string animName", // Params + "Start the animation" ) // Help string +{ + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + msg->SetCmd(plAnimCmdMsg::kContinue); + msg->SetAnimName(nil); + msg->SetBCastFlag(plMessage::kPropagateToModifiers); + SendAnimCmdMsg(params[0], msg); +} + +PF_CONSOLE_CMD( Animation, // Group name + Stop, // Function name + "string objName, string animName", // Params + "Stop the animation" ) // Help string +{ + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + msg->SetCmd(plAnimCmdMsg::kStop); + msg->SetAnimName(nil); + msg->SetBCastFlag(plMessage::kPropagateToModifiers); + SendAnimCmdMsg(params[0], msg); +} + +PF_CONSOLE_CMD( Animation, // Group name + SetBlend, // Function name + "string objName, string animName, float blend, float rate", // Params + "Set the animation's blend value and rate to change" ) // Help string +{ + plAGCmdMsg *msg = TRACKED_NEW plAGCmdMsg(); + msg->SetCmd(plAGCmdMsg::kSetBlend); + msg->fBlend = params[2]; + msg->fBlendRate = params[3]; + msg->SetAnimName(params[1]); + msg->SetBCastFlag(plMessage::kPropagateToModifiers); + SendAnimCmdMsg(params[0], msg); +} + +PF_CONSOLE_CMD( Animation, // Group name + SetAmp, // Function name + "string objName, string animName, float amp, float rate", // Params + "Set the amplitude of this animation and rate to change" ) // Help string +{ + plAGCmdMsg *msg = TRACKED_NEW plAGCmdMsg(); + msg->SetCmd(plAGCmdMsg::kSetAmp); + msg->fAmp = params[2]; + msg->fAmpRate = params[3]; + msg->SetAnimName(params[1]); + msg->SetBCastFlag(plMessage::kPropagateToModifiers); + SendAnimCmdMsg(params[0], msg); +} + +PF_CONSOLE_CMD( Animation, // Group name + SetSpeed, // Function name + "string objName, string animName, float speed, float rate", // Params + "Set the speed of this animation and rate to change" ) // Help string +{ + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + msg->SetCmd(plAnimCmdMsg::kSetSpeed); + msg->fSpeed = params[2]; + msg->fSpeedChangeRate = params[3]; + msg->SetAnimName(params[1]); + msg->SetBCastFlag(plMessage::kPropagateToModifiers); + SendAnimCmdMsg(params[0], msg); +} + +PF_CONSOLE_CMD( Animation, + AddDebugItems, + "string name", + "Add keys with the given name (substrings ok) to our report list" ) +{ + plAnimDebugList *adl = plClient::GetInstance()->fAnimDebugList; + if (adl) + adl->AddObjects(params[0]); +} + +PF_CONSOLE_CMD( Animation, + RemoveDebugItems, + "string name", + "Remove keys with the given name (substrings ok) to our report list" ) +{ + plAnimDebugList *adl = plClient::GetInstance()->fAnimDebugList; + if (adl) + adl->RemoveObjects(params[0]); +} + +PF_CONSOLE_CMD( Animation, + ShowDebugTimes, + "", + "Toggle the view of our debug list." ) +{ + plAnimDebugList *adl = plClient::GetInstance()->fAnimDebugList; + if (adl) + adl->fEnabled = !adl->fEnabled; +} + +PF_CONSOLE_CMD( Animation, + ToggleDelayedTransforms, + "", + "Toggle the possibility of delayed transform evaluation." ) +{ + hsBool enabled = !plCoordinateInterface::GetDelayedTransformsEnabled(); + plCoordinateInterface::SetDelayedTransformsEnabled(enabled); + + char buff[256]; + sprintf(buff, "Potential delay of transform eval is now %s", (enabled ? "ENABLED" : "DISABLED")); + PrintString(buff); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +//////////////////////////////////////////////////////////////////////// +// Clothing Commands +//////////////////////////////////////////////////////////////////////// +#ifndef LIMIT_CONSOLE_COMMANDS + +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvatarClothing.h" +#include "../plAvatar/plClothingLayout.h" +#include "../pfMessage/plClothingMsg.h" + +PF_CONSOLE_GROUP( Clothing ) // Defines a main command group + +PF_CONSOLE_CMD( Clothing, // Group name + AddItemToCloset, // Function name + "string itemName, float r, float g, float b, float r2, float g2, float b2", // Params + "Add a clothing item to your closet" ) // Help string +{ + hsTArray items; + items.SetCount(1); + items[0].fItem = plClothingMgr::GetClothingMgr()->FindItemByName(params[0]); + items[0].fOptions.fTint1.Set(params[1], params[2], params[3], 1.f); + items[0].fOptions.fTint2.Set(params[4], params[5], params[6], 1.f); + + plClothingMgr::GetClothingMgr()->AddItemsToCloset(items); +} + +PF_CONSOLE_CMD( Clothing, // Group name + WearItem, // Function name + "string itemName", // Params + "Has your avatar wear the item of clothing specified" ) // Help string +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName(params[0]); + + if (avMod && item) + { + avMod->GetClothingOutfit()->AddItem(item); + } +} + +PF_CONSOLE_CMD( Clothing, // Group name + RemoveItem, // Function name + "string itemName", // Params + "Has your avatar remove the item of clothing specified" ) // Help string +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName(params[0]); + + if (avMod && item) + { + avMod->GetClothingOutfit()->RemoveItem(item); + } +} + +PF_CONSOLE_CMD( Clothing, // Group name + TintItem, // Function name + "string itemName, float red, float green, float blue, int layer", // Params + "Change the color of an item of clothing you're wearing" ) // Help string +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName(params[0]); + UInt8 layer; + if ((int)params[4] == 2) + layer = plClothingElement::kLayerTint2; + else + layer = plClothingElement::kLayerTint1; + + if (avMod && item) + { + avMod->GetClothingOutfit()->TintItem(item, params[1], params[2], params[3], true, true, true, true, layer); + } +} + +PF_CONSOLE_CMD( Clothing, // Group name + TintSkin, // Function name + "float red, float green, float blue", // Params + "Change your avatar's skin color" ) // Help string +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + { + avMod->GetClothingOutfit()->TintSkin(params[0], params[1], params[2]); + } +} + +PF_CONSOLE_CMD( Clothing, // Group name + AgeSkin, // Function name + "float age", // Params + "Blend (0 to 1) between young and old skin." ) // Help string +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + { + avMod->GetClothingOutfit()->SetAge(params[0]); + } +} + +PF_CONSOLE_CMD( Clothing, // Group name + BlendSkin, // Function name + "float blend, int layer", // Params + "Set the blend (0 to 1) for a specific skin layer (1 to 6)." ) // Help string +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + { + avMod->GetClothingOutfit()->SetSkinBlend(params[0], (int)params[1] + plClothingElement::kLayerSkinBlend1 - 1); + } +} + +PF_CONSOLE_CMD( Clothing, + ChangeAvatar, + "string name", + "Switch your avatar to a different gender ('Male' / 'Female')" ) +{ + plClothingMgr::ChangeAvatar(params[0]); +} + +PF_CONSOLE_CMD( Clothing, // Group name + SaveCustomizations, // Function name + "", // Params + "Save your customizations to the vault." ) // Help string +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + { + avMod->GetClothingOutfit()->WriteToVault(); + } +} + +static plPlate *avatarTargetTexturePlate = nil; + +PF_CONSOLE_CMD( Clothing, + ShowTargetTexture, + "...", + "Show/hide the texture we use for the local avatar on a square (for debugging)." ) +{ + if (avatarTargetTexturePlate == nil) + { + plArmatureMod *avMod = nil; + if (numParams > 0) + avMod = plAvatarMgr::GetInstance()->FindAvatarByPlayerID((int)params[0]); + else + avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + { + plPlateManager::Instance().CreatePlate( &avatarTargetTexturePlate ); + avatarTargetTexturePlate->SetMaterial(avMod->GetClothingOutfit()->fMaterial); + avatarTargetTexturePlate->SetPosition(0,0); + avatarTargetTexturePlate->SetSize(1.9, 1.9); + avatarTargetTexturePlate->SetVisible(true); + } + } + else + { + plPlateManager::Instance().DestroyPlate(avatarTargetTexturePlate); + } +} + +PF_CONSOLE_CMD( Clothing, + WearMaintainerOutfit, + "", + "Wear the Maintainer outfit" ) +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + { + avMod->GetClothingOutfit()->WearMaintainerOutfit(); + } +} + +PF_CONSOLE_CMD( Clothing, + RemoveMaintainerOutfit, + "", + "Return to your normal outfit" ) +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + { + avMod->GetClothingOutfit()->RemoveMaintainerOutfit(); + } +} + +#endif // LIMIT_CONSOLE_COMMANDS + +////////////////////////////////////////////////////////////////////////////// +//// KI Commands ///////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_GROUP( KI ) // Defines a main command group + + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( KI, // Group name + UpgradeLevel, // Function name + "int level", // Params + "Upgrade KI to new level." ) // Help string +{ + // create the mesage to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kUpgradeKILevel ); + + msg->SetIntValue((int) params[0]); + + // send it off + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( KI, // Group name + DowngradeLevel, // Function name + "int level", // Params + "Downgrade KI level to next lower level." ) // Help string +{ + // create the mesage to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kDowngradeKILevel ); + + msg->SetIntValue((int) params[0]); + + // send it off + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( KI, // Group name + AllOfThePower, // Function name + "", // Params + "All the phased functionality turned on." ) // Help string +{ + // create the mesage to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kKIPhasedAllOn ); + + // send it off + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( KI, // Group name + NoneOfThePower, // Function name + "", // Params + "All the phased functionality turned off." ) // Help string +{ + // create the mesage to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kKIPhasedAllOff ); + + // send it off + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( KI, // Group name + AddJournal, // Function name + "", // Params + "Add the journal to the Blackbar." ) // Help string +{ + // create the message to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kAddJournalBook ); + + // send if off + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( KI, // Group name + RemoveJournal, // Function name + "", // Params + "Removes the journal from the Blackbar." ) // Help string +{ + // create the message to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kRemoveJournalBook ); + + // send if off + plgDispatch::MsgSend( msg ); +} + +#endif // LIMIT_CONSOLE_COMMANDS + +//////////////////////////////////////////////////////////////////////// +// Execute a Python file command +//////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_GROUP( Python ) // Defines a main command group + +PF_CONSOLE_CMD( Python, // Group name + RunFile, // Function name + "string filename", // Params + "Run the specified Python file program" ) // Help string +{ + // now evaluate this mess they made + PyObject* mymod = PythonInterface::FindModule("__main__"); + // make sure the filename doesn't have the .py extension (import doesn't need it) + char importname[200]; + int i; + for (i=0; i<199; i++ ) + { + char ch = ((const char*)params[0])[i]; + // if we are at the end of the string or at a dot, truncate here + if ( ch == '.' || ch == 0 ) + break; + else + importname[i] = ((const char*)params[0])[i]; + } + importname[i] = 0; + + // create the line to execute the file + char runline[256]; + sprintf(runline,"import %s", importname); + PythonInterface::RunString(runline,mymod); + std::string output; + // get the messages + PythonInterface::getOutputAndReset(&output); + PrintString(output.c_str()); +} + + +#include "../pfPython/cyMisc.h" + +PF_CONSOLE_CMD( Python, // Group name + SetLoggingLevel, // Function name + "int level", // Params + "Set the python logging print level (1-4)" ) // Help string +{ + cyMisc::SetPythonLoggingLevel((int) params[0]); +} + +#ifndef LIMIT_CONSOLE_COMMANDS +PF_CONSOLE_CMD( Python, + UsePythonDebugger, + "", + "Enables the python debugger (only works in an .ini file)" ) +{ + PythonInterface::UsePythonDebugger(true); +} + + +#include "../pfMessage/pfBackdoorMsg.h" +PF_CONSOLE_CMD( Python, + Backdoor, + "string target, ...", // Params + "Send a debug trigger to a python file modifier" ) +{ + const char* extraParms = ""; + if (numParams > 1) + extraParms = params[1]; + pfBackdoorMsg *msg = TRACKED_NEW pfBackdoorMsg( params[0],extraParms ); + // send it off + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Python, + Cheat, + "string functions, ...", // Params + "Run a cheat command" ) +{ + const char* extraParms = ""; + if (numParams > 1) + extraParms = params[1]; + // now evaluate this mess they made + PyObject* mymod = PythonInterface::FindModule("__main__"); + + // create the line to execute the file + char runline[256]; + sprintf(runline,"import xCheat;xCheat.%s('%s')", (const char*)params[0],extraParms); + PythonInterface::RunString(runline,mymod); + std::string output; + // get the messages + PythonInterface::getOutputAndReset(&output); + PrintString(output.c_str()); +} + +PF_CONSOLE_CMD( Python, + ListCheats, + "", // Params - None + "Show a list of python commands" ) +{ + // now evaluate this mess they made + PyObject* mymod = PythonInterface::FindModule("__main__"); + + PythonInterface::RunString("import xCheat;xc=[x for x in dir(xCheat) if not x.startswith('_')]\nfor i in range((len(xc)/4)+1): print xc[i*4:(i*4)+4]\n",mymod); + std::string output; + // get the messages + PythonInterface::getOutputAndReset(&output); + PrintString(output.c_str()); +} + +#endif // LIMIT_CONSOLE_COMMANDS + + + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_GROUP(Demo) + +PF_CONSOLE_CMD(Demo, RecordNet, "string recType, string recName", "Records a network demo (must be set in an ini file)") +{ + if (plNetClientMgr::GetInstance()->RecordMsgs(params[0],params[1])) + PrintString("Recording Started"); + else + PrintString("Recording Failed"); +} + +PF_CONSOLE_CMD(Demo, PlayNet, "string recName", "Plays back a network demo") +{ + if (plNetClientMgr::GetInstance()->PlaybackMsgs(params[0])) + PrintString("Playback Started"); + else + PrintString("Playback Failed"); +} +/* +#include "../plHavok1/plVehicleModifier.h" + +PF_CONSOLE_GROUP(Vehicle) + +PF_CONSOLE_CMD(Vehicle, ShowStats, "", "") +{ + plVehicleModifier::ShowStats(); +} + +PF_CONSOLE_CMD(Vehicle, SetSuspensionStrength, "float val", "10-100") +{ + float val = params[0]; + plVehicleModifier::SetSuspensionStrength(val); +} + +PF_CONSOLE_CMD(Vehicle, SetSuspensionDamping, "float val", "1-8") +{ + float val = params[0]; + plVehicleModifier::SetSuspensionDamping(val); +} + +PF_CONSOLE_CMD(Vehicle, SetMass, "float val", "") +{ + float val = params[0]; + plVehicleModifier::SetMass(val); +} + +PF_CONSOLE_CMD(Vehicle, LoadSettings, "", "") +{ + plVehicleModifier::ReadKeyValues(); + PrintString("Loaded vehicle settings"); +} + +PF_CONSOLE_CMD(Vehicle, SaveSettings, "", "") +{ + plVehicleModifier::WriteKeyValues(); + PrintString("Saved vehicle settings"); +} +*/ +#endif // LIMIT_CONSOLE_COMMANDS + + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_GROUP(Vault) + +PF_CONSOLE_CMD(Vault, Dump, "", "Prints the vault structure of current player and age to the nearest log file") +{ + VaultDump(L"Player", NetCommGetPlayer()->playerInt); + VaultDump(L"Age", NetCommGetAge()->ageVaultId); +} + +#endif + +////////////////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommandsNet.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommandsNet.cpp new file mode 100644 index 00000000..2ea11fbd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommandsNet.cpp @@ -0,0 +1,1057 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// Actual NET Console Commands and Groups // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifdef PLASMA_EXTERNAL_RELEASE +#define LIMIT_CONSOLE_COMMANDS 1 +#endif + + +#include "pfConsoleCmd.h" +#include "plgDispatch.h" + +#include "../plAgeLoader/plAgeLoader.h" +#include "../plNetClient/plNetObjectDebugger.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetClient/plNetLinkingMgr.h" +#include "../plAgeLoader/plResPatcher.h" +#include "../../NucleusLib/inc/hsResMgr.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plKeyImp.h" +#include "../pnModifier/plLogicModBase.h" +#include "../pfCharacter/plPlayerModifier.h" +#include "../../NucleusLib/inc/hsTimer.h" +#include "../pnMessage/plClientMsg.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plAudioSysMsg.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plOneShotMsg.h" +#include "../plMessage/plConsoleMsg.h" +#include "../../Apps/plClient/plClient.h" +#include "pfConsole.h" +#include "../plResMgr/plKeyFinder.h" +#include "hsResMgr.h" + +// begin for agedefn test +#include "hsStream.h" +#include "../plAgeDescription/plAgeDescription.h" +#include "../plUnifiedTime/plUnifiedTime.h" +//end for agedefn test + +#include "../../PubUtilLib/plFile/hsFiles.h" + +#include "../plStatusLog/plStatusLog.h" + +#include "hsStlUtils.h" +#include "hsTemplates.h" + +#include "../plVault/plVault.h" + +#include "../plNetCommon/plSpawnPointInfo.h" + +#include "../plSDL/plSDL.h" + +#include "../plNetGameLib/plNetGameLib.h" + +#include "../pfGameMgr/pfGameMgr.h" + + +#define PF_SANITY_CHECK( cond, msg ) { if( !( cond ) ) { PrintString( msg ); return; } } + +//// DO NOT REMOVE!!!! +//// This is here so Microsoft VC won't decide to "optimize" this file out +//// DO NOT REMOVE!!!! +void pfConsoleCmdGroup::DummyNet( void ) +{ +} +//// DO NOT REMOVE!!!! + +//// Defining Console Commands /////////////////////////////////////////////// +// +// You define console commands by using the PF_CONSOLE_CMD macro. The format +// of the macro is: +// +// PF_CONSOLE_CMD( groupName, functionName, "paramList", "Help string" ) +// +// Where: +// - groupName is a string representing what command group the command +// is in. Subgroups are specified by an underscore, i.e. to put a command +// in the Draw subgroup of the Graphics group, you would specify +// "Graphics_Draw". Specifying "" means to put the command in the base +// command group--i.e. it has no group. +// - functionName is required; it specifies the function name (really?!?!). +// Function names must be globally unique, so you can't have a Draw +// function in both the Graphics and SceneAPI groups. Sorry. :( +// - paramList specifies the parameters and types to the function. +// The smallest list you can have is "", which means "no parameters". +// If you have parameters, it must be in a comma-delimited string. +// You can either specify types or labels and types, so you can say +// "int, float" or "int x, float value". Currently, the labels are only +// used when printing out usage strings, but they will be used later +// for auto-labeling GUI elements, so please put them in where viable. +// +// White space does not matter. Valid types are int, float, char, string +// bool (auto-conversion of "true"/"false" strings to 1 or 0) and "...". +// "..." is a special type that means the same as the C equivalent: +// "there can be zero or more parameters here and I don't care what the +// type is" is the gist of it. +// - helpString is a short description of the function, which currently +// isn't used, but will be used in the future when implementing help +// (could you have guessed it? :) Please fill it in when the function +// name isn't obvious (i.e. SetFogColor doesn't really need one) +// +// The actual C code prototype looks like: +// void pfConsoleCmd_groupName_functionName( UInt32 numParams, pfConsoleCmdParam *params, +// void (*PrintString)( char * ) ); +// +// numParams is exactly what it sounds like. params is an array of console +// parameter objects, each of which are rather nifty in that they can be cast +// immediately to whatever type you asked for in your parameter list +// ("paramList" above). So if your paramList was "int", then params[ 0 ] +// can be cast to an int immediately, such as int x = params[ 0 ]; +// If you attempt to cast a parameter to a type other than the one specified +// in the paramList, you get an hsAssert saying so, so don't do it! Any +// parameters that fall under "..." are automagically strings, but they can +// be cast to any valid type without an assert. So basically, if you want +// to still do your own conversion, just specify "..." as the entire paramList +// and treat the params array as if it were an array of strings. +// +// Thus, the net result of the paramList is that it lets the console engine +// do the parameter parsing for you. If the paramters given to the function +// do not match the list (this includes too many or too few parameters), a +// usage string is printed out and the function is not called. Thus, the +// ONLY parameter error you can possibly have is casting a parameter object +// to a type other than you asked for. So don't do it! +// +// (Note: this makes numParams almost obsolete; the only reason it still has +// a use is for "...", which of course allows variable number of parameters.) +// +// PrintString is a function that lets you print output to the on-screen +// console. It is guaranteed to be non-null. Worst case is that it points +// to a dummy function that does nothing, but it will *always* be valid. +// +// To define console command groups, you use the macro: +// +// PF_CONSOLE_GROUP( group ) +// +// where "group" is the name without quotes of the group you want to create. +// To create a subgroup inside a group, use: +// +// PF_CONSOLE_SUBGROUP( parent, group ) +// +// where "parent" is the parent group for the subgroup. "parent" can have +// underscores in it just like the group of a CONSOLE_CMD, so you can say +// +// PF_CONSOLE_SUBGROUP( Graphics_Render, Drawing ) +// +// to create the Graphics_Render_Drawing subgroup. All groups must be +// defined before any commands that are in that group. Note that although +// the +// +////////////////////////////////////////////////////////////////////////////// + +// +// utility functions +// +////////////////////////////////////////////////////////////////////////////// +plKey FindSceneObjectByName(const char* name, const char* ageName, char* statusStr, bool subString=false); +plKey FindObjectByName(const char* name, int type, const char* ageName, char* statusStr, bool subString=false); +plKey FindObjectByNameAndType(const char* name, const char* typeName, const char* ageName, + char* statusStr, bool subString=false); +void PrintStringF(void pfun(const char *),const char * fmt, ...); + +////////////////////////////////////////////////////////////////////////////// +//// Network Group Commands ////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_GROUP( Net ) // Defines a main command group + + +#ifndef LIMIT_CONSOLE_COMMANDS +// Net.Ping +PF_CONSOLE_CMD( Net, + Ping, + "bool enable", + "Enable/disable server ping" ) +{ + bool enable = params[0]; + NetClientPingEnable(enable); +} + +// +// Temp until we get real text chat +// +PF_CONSOLE_CMD( Net, // groupName + Chat, // fxnName + "...", // string params + "broadcast chat msg" ) // helpString +{ + // send chat text + std::string text=plNetClientMgr::GetInstance()->GetPlayerName(); + text += ":"; + int i; + for(i=0;iSetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce); + cMsg->SetBCastFlag(plMessage::kLocalPropagate, 0); + plgDispatch::MsgSend( cMsg ); + + PrintString(const_cast(text.c_str())); // show locally too +} + + +PF_CONSOLE_CMD( Net, // groupName + ShowRelevanceRegions, // fxnName + "bool on", // paramList + "Show player relevance regions" ) // helpString +{ + bool on = params[0]; + plNetClientMgr::GetInstance()->SetFlagsBit(plNetClientMgr::kShowRelevanceRegions, on); + if (on) + { + plNetClientMgr::GetInstance()->SetFlagsBit(plNetClientMgr::kShowLists, false); + plNetClientMgr::GetInstance()->SetFlagsBit(plNetClientMgr::kShowRooms, false); + } + +} + +PF_CONSOLE_CMD( Net, // groupName + ShowLists, // fxnName + "bool on", // paramList + "Show debug player lists" ) // helpString +{ + bool on = params[0]; + plNetClientMgr::GetInstance()->SetFlagsBit(plNetClientMgr::kShowLists, on); + if (on) + { + plNetClientMgr::GetInstance()->SetFlagsBit(plNetClientMgr::kShowRooms, false); + plNetClientMgr::GetInstance()->SetFlagsBit(plNetClientMgr::kShowRelevanceRegions, false); + } +} + +PF_CONSOLE_CMD( Net, // groupName + ShowRooms, // fxnName + "bool on", // paramList + "Show debug room lists" ) // helpString +{ + bool on = params[0]; + plNetClientMgr::GetInstance()->SetFlagsBit(plNetClientMgr::kShowRooms, on); + if (on) + { + plNetClientMgr::GetInstance()->SetFlagsBit(plNetClientMgr::kShowLists, false); + plNetClientMgr::GetInstance()->SetFlagsBit(plNetClientMgr::kShowRelevanceRegions, false); + } +} + +#endif + + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Net, // groupName + LocalTriggers, // fxnName + "", // paramList + "Make triggers localOnly" ) // helpString +{ + plNetClientMgr::GetInstance()->SetFlagsBit(plNetClientMgr::kLocalTriggers); +} + +PF_CONSOLE_CMD( Net, // groupName + SetConsoleOutput, // fxnName + "bool onoff", // paramList + "send log output to the debug window" ) // helpString +{ + plNetClientMgr::GetInstance()->SetConsoleOutput( params[0] ); +} + + + +///////////// + +// ENABLE/DISABLE LINKING +PF_CONSOLE_CMD( Net, // groupName + EnableLinking, // fxnName + "bool enable", // paramList + "Enable/disable linking." ) // helpString +{ + if ( (int)params[0] ) + { + plNetLinkingMgr::GetInstance()->SetEnabled( true ); + PrintString("Linking enabled."); + } + else + { + plNetLinkingMgr::GetInstance()->SetEnabled( false ); + PrintString("Linking disabled."); + } +} +// GENERIC LINK. PLS WILL LOAD-BALANCE YOU TO A PUBLIC INSTANCE. +PF_CONSOLE_CMD( Net, // groupName + LinkToAge, // fxnName + "string ageFilename", // paramList + "Link to an age." ) // helpString +{ + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename( params[0] ); + link.SetLinkingRules( plNetCommon::LinkingRules::kBasicLink ); + plNetLinkingMgr::GetInstance()->LinkToAge( &link ); + PrintString("Linking to age..."); +} + + +// LINK TO A SPECIFIC AGE INSTANCE BY GUID. +PF_CONSOLE_CMD( Net, // groupName + LinkToAgeInstance, // fxnName + "string ageFilename, string ageGuid", // paramList + "Link to a specific age by guid." ) // helpString +{ + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename( params[0] ); + //link.GetAgeInfo()->SetAgeInstanceName( params[0] ); + //link.GetAgeInfo()->SetAgeUserDefinedName( params[0] ); + link.GetAgeInfo()->SetAgeInstanceGuid( &plUUID( params[1] ) ); + link.SetLinkingRules( plNetCommon::LinkingRules::kBasicLink ); + plNetLinkingMgr::GetInstance()->LinkToAge( &link ); + PrintString("Linking to age..."); +} +// LINK TO MY PREVIOUS AGE +PF_CONSOLE_CMD( Net, // groupName + LinkToPrevAge, // fxnName + "", // paramList + "Link to my last age." ) // helpString +{ + plNetLinkingMgr::GetInstance()->LinkToPrevAge( ); + PrintString("Linking to previous age..."); +} +// LINK WITH ORIGINAL LINKING BOOK +PF_CONSOLE_CMD( Net, + LinkWithOriginalBook, + "string ageFilename, string spawnPt", + "Link to specified age using Original Age Linking Book rules" ) +{ + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename( params[0] ); + link.SpawnPoint() = plSpawnPointInfo( params[1], params[1] ); + link.SetLinkingRules( plNetCommon::LinkingRules::kOriginalBook ); + plNetLinkingMgr::GetInstance()->LinkToAge( &link ); + PrintString("Linking to age with original book..."); +} +// LINK TO AN AGE I OWN +PF_CONSOLE_CMD( Net, + LinkWithOwnedBook, + "string ageFilename", + "Link to specified age using Personal Age Linking Book rules" ) +{ + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename( params[0] ); + link.SetLinkingRules( plNetCommon::LinkingRules::kOwnedBook ); + plNetLinkingMgr::GetInstance()->LinkToAge( &link ); + PrintString("Linking to age I own..."); +} +// LINK TO AN AGE I CAN VISIT +PF_CONSOLE_CMD( Net, + LinkWithVisitBook, + "string ageFilename", + "Link to specified age using Personal Age Linking Book rules" ) +{ + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename( params[0] ); + link.SetLinkingRules( plNetCommon::LinkingRules::kVisitBook ); + plNetLinkingMgr::GetInstance()->LinkToAge( &link ); + PrintString("Linking to age I can visit..."); +} +// LINK TO A SUB AGE +PF_CONSOLE_CMD( Net, + LinkWithSubAgeBook, + "string ageFilename", + "Link to a sub-age of the current age" ) +{ + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename( params[0] ); + link.SetLinkingRules( plNetCommon::LinkingRules::kSubAgeBook ); + plNetLinkingMgr::GetInstance()->LinkToAge( &link ); + PrintString("Linking to a sub-age..."); +} + +// LINK TO A PLAYER'S CURRENT AGE +PF_CONSOLE_CMD( Net, + LinkToPlayersAge, + "int playerID", + "Link to specified player's current age" ) +{ + plNetLinkingMgr::GetInstance()->LinkToPlayersAge( (int)params[0] ); + PrintString("Linking to a player's current age..."); +} +// LINK A PLAYER HERE +PF_CONSOLE_CMD( Net, + LinkPlayerHere, + "int playerID", + "Link specified player to our current age." ) +{ + plNetLinkingMgr::GetInstance()->LinkPlayerHere( (int)params[0] ); + PrintString("Linking player to our current age..."); +} +// LINK A PLAYER BACK TO HIS LAST AGE +PF_CONSOLE_CMD( Net, + LinkPlayerToPrevAge, + "int playerID", + "Link specified player back to his last age." ) +{ + plNetLinkingMgr::GetInstance()->LinkPlayerToPrevAge( (int)params[0] ); + PrintString("Linking player back to his last age..."); +} + +// LINK TO MY NEIGHBORHOOD AGE +PF_CONSOLE_CMD( Net, + LinkToMyNeighborhoodAge, + "", + "Link to my neighborhood" ) +{ + plNetLinkingMgr::GetInstance()->LinkToMyNeighborhoodAge(); + PrintString("Linking to my neighborhood..."); +} + +// LINK TO MY PERSONAL AGE +PF_CONSOLE_CMD( Net, + LinkToMyPersonalAge, + "", + "Link to my personal age" ) +{ + plNetLinkingMgr::GetInstance()->LinkToMyPersonalAge(); + PrintString("Linking to my personal age..."); +} + +#endif + + +#ifndef LIMIT_CONSOLE_COMMANDS + +// OFFER LINK TO PLAYER +PF_CONSOLE_CMD( Net, + OfferLinkToPlayer, + "string ageFilename, int playerID", + "Offer a link to a player" ) +{ + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + plNetLinkingMgr * lm = plNetLinkingMgr::GetInstance(); + + plAgeInfoStruct info; + info.SetAgeFilename( params[0] ); + + plAgeLinkStruct link; + if (!VaultGetOwnedAgeLink(&info, &link)) { + PrintString( "You don't own an age by that name. Not offering link to player." ); + return; + } + + + link.SetLinkingRules(plNetCommon::LinkingRules::kVisitBook); + + plNetLinkingMgr::GetInstance()->OfferLinkToPlayer(&link, (int)params[1] ); + PrintString("Offered age link to player."); +} + + + +///////////// + + +PF_CONSOLE_CMD( Net, // groupName + SetObjUpdateFreq, // fxnName + "string objName, float freqInSecs", // paramList + "Instructs the server to only send me updates about this object periodically" ) // helpString +{ + char str[256]; + plKey key = FindSceneObjectByName(params[0], nil, str); + PrintString(str); + if (!key) + return; + + float freq = params[1]; + + // send net msg + plNetMsgObjectUpdateFilter netMsg; + netMsg.SetNetProtocol(kNetProtocolCli2Game); + netMsg.SetMaxUpdateFreq(freq); + netMsg.ObjectListInfo()->AddObject(key); + plNetClientMgr::GetInstance()->SendMsg(&netMsg); +} + +PF_CONSOLE_CMD( Net, // groupName + EnableClientDelay, // fxnName + "", // paramList + "Switches on delay in client update loop" ) // helpString +{ + plClient::EnableClientDelay(); +} + +PF_CONSOLE_CMD( Net, // groupName + GetServerTime, // fxnName + "", // paramList + "returns the current server clock" ) // helpString +{ + PrintStringF(PrintString, "Current server time = %s", + plNetClientMgr::GetInstance()->GetServerTime().Print()); +} + +PF_CONSOLE_CMD( Net, // groupName + GetAgeElapsedTime, // fxnName + "", // paramList + "returns the age of the age" ) // helpString +{ + PrintStringF(PrintString, "Current age is %s, elapsed time since birth = %f secs", + NetCommGetAge()->ageDatasetName, plNetClientMgr::GetInstance()->GetCurrentAgeElapsedSeconds()); +} + +PF_CONSOLE_CMD( Net, DownloadViaManifest, + "string remoteManifestFileName", // paramList + "Downloads the given file from the dataserver, then updates all the ages listed in the file" ) // helpString +{ +// if( hsgResMgr::ResMgr() != nil ) +// { +// plResPatcher::PatchFromManifest( params[ 0 ] ); +// } +// else + PrintString( "DownloadViaManifest failed: resManager not initialized. This command must be used in an .fni file or later." ); +} + +#endif + + +// must be in 'INI' file +PF_CONSOLE_CMD( Net, SetProxyAddr, + "string proxyHostAddr", // paramList + "Sets the address of a proxy host to send all messages to" ) // helpString +{ + // plNetClientMgr::GetInstance()->SetProxyAddr((const char*)params[0]); + PrintString("OBSOLETE"); +} + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Net, GetCCRAwayStatus, + "", // paramList + "Find out if CCR's are offline" ) // helpString +{ + PrintStringF(PrintString,"The CCR dept is %s", VaultGetCCRStatus() ? "online" : "away"); +} + +PF_CONSOLE_CMD( Net, // groupName + ForceSetAgeTimeOfDay, // fxnName + "float timePercent", // paramList + "Force the age time of day to a specific value from 0.0 to 1.0. Set to -1 to unset." ) // helpString +{ + plNetClientMgr::GetInstance()->SetOverrideAgeTimeOfDayPercent(params[0]); +} + +PF_CONSOLE_CMD( Net, // groupName + ScreenMessages, // fxnName + "bool on", // paramList + "Screen out illegal net msgs." ) // helpString +{ + bool on = params[0]; + plNetApp::GetInstance()->SetFlagsBit(plNetApp::kScreenMessages, on); +} + +#endif + +/////////////////////////////////////// +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_SUBGROUP( Net, DebugObject ) // Creates a DebugFocus sub-group under the net group + +PF_CONSOLE_CMD( Net_DebugObject, // groupName + AddObject, // fxnName + "string objName, ...", // paramList + "Create a debug log about the specified object. AddObject objName [pageName], wildcards allowed" ) // helpString +{ + char* objName = params[0]; + char* pageName = numParams>1 ? (char*)params[1] : nil; + if (plNetObjectDebugger::GetInstance()) + plNetObjectDebugger::GetInstance()->AddDebugObject(objName, pageName); +} + +PF_CONSOLE_CMD( Net_DebugObject, // groupName + RemoveObject, // fxnName + "string objName, ...", // paramList + "Stop focused debugging about the specified object. RemoveObject objName [pageName], wildcards allowed" ) // helpString +{ + char* objName = params[0]; + char* pageName = numParams>1 ? (char*)params[1] : nil; + if (plNetObjectDebugger::GetInstance()) + plNetObjectDebugger::GetInstance()->RemoveDebugObject(objName, pageName); +} + +PF_CONSOLE_CMD( Net_DebugObject, // groupName + ClearAllObjects, // fxnName + "", // paramList + "Stop focused debugging about all objects" ) // helpString +{ + if (plNetObjectDebugger::GetInstance()) + plNetObjectDebugger::GetInstance()->ClearAllDebugObjects(); +} + +#include "../pfPython/plPythonSDLModifier.h" +PF_CONSOLE_CMD( Net_DebugObject, // groupName + DumpAgeSDLHook, // fxnName + "bool dirtyOnly", // paramList + "Dump the age SDL hook to the object debugger" ) // helpString +{ + const plPythonSDLModifier * mod = plPythonSDLModifier::FindAgeSDL(); + mod->GetStateCache()->DumpToObjectDebugger("AgeSDLHook", params[0] ); +} + +#endif + +/////////////////////////////////////// +PF_CONSOLE_SUBGROUP( Net, Voice ) // Creates a VOICE sub-group under a given group + +PF_CONSOLE_CMD( Net_Voice, // groupName + Echo, // fxnName + "bool on", // paramList + "Turn on/off echoing of voice packets" ) // helpString +{ + bool on = params[0]; + plNetClientMgr::GetInstance()->SetFlagsBit(plNetClientMgr::kEchoVoice,on); +} + +PF_CONSOLE_CMD( Net_Voice, // groupName + SetMaxListeningRadius, // fxnName + "float dist", // paramList + "Set the max distance that I hear another player's voice" ) // helpString +{ + float dist = params[0]; + plNetClientMgr::GetInstance()->GetListenList()->kMaxListenDistSq = dist*dist; +} + +PF_CONSOLE_CMD( Net_Voice, // groupName + SetMaxNumListeningTo, // fxnName + "int num", // paramList + "Set the max number of other players that I can listen to at once" ) // helpString +{ + int max = params[0]; + plNetClientMgr::GetInstance()->GetListenList()->kMaxListenListSize=max; +} + +/////////////////////////////////////// +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_SUBGROUP( Net, Log ) // Creates a LOG sub-group under a given group + +PF_CONSOLE_CMD( Net_Log, // groupName + Create, // fxnName + "string fileName", // paramList + "obsolete" ) // helpString +{ +} + +#endif + +/////////////////////////////////////// +// Account Authentication +PF_CONSOLE_SUBGROUP( Net, Auth ) // Creates an AUTH sub-group under a given group + +PF_CONSOLE_CMD( Net_Auth, // groupName + SetAccountName, // fxnName + "string name", // paramList + "Sets account name." ) // helpString +{ + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + nc->SetIniAccountName(params[0]); +} + +PF_CONSOLE_CMD( Net_Auth, // groupName + SetAccountPassword, // fxnName + "string password", // paramList + "Sets account password." ) // helpString +{ + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + nc->SetIniAccountPass(params[0]); +} + +#ifndef LIMIT_CONSOLE_COMMANDS + +PF_CONSOLE_CMD( Net_Auth, // groupName + Disconnect, // fxnName + "", // paramList + "Cause an auth server disconnect" ) // helpString +{ + NetCliAuthUnexpectedDisconnect(); +} + +#endif + + +/////////////////////////////////////// + +#ifndef LIMIT_CONSOLE_COMMANDS +PF_CONSOLE_SUBGROUP( Net, Vault ) // Creates sub-group Net.Vault + +PF_CONSOLE_CMD( Net_Vault, // groupName + Dump, // fxnName + "", // paramList + "Dump vault to log" ) // helpString +{ + hsAssert(false, "eric, implement me"); +// VaultDump(); +} + +/////////////////////////////////////// + +PF_CONSOLE_CMD( Net_Vault, // groupName + SetSeen, // fxnName + "int nodeId, int seen", // paramList + "Set or clear node seen flag" ) // helpString +{ + VaultSetNodeSeen((int)params[0], (bool)params[1]); +} + +/////////////////////////////////////// + +PF_CONSOLE_CMD( Net_Vault, // groupName + InMyPersonalAge, // fxnName + "", // paramList + "" ) // helpString +{ + bool in = VaultAmInMyPersonalAge(); + PrintStringF( PrintString,"You are %sin your personal age", in?"":"not " ); +} +PF_CONSOLE_CMD( Net_Vault, // groupName + InMyNeighborhoodAge, // fxnName + "", // paramList + "" ) // helpString +{ + bool in = VaultAmInMyNeighborhoodAge(); + PrintStringF( PrintString,"You are %sin your neighborhood age", in?"":"not " ); +} +PF_CONSOLE_CMD( Net_Vault, // groupName + AmOwnerOfCurrentAge, // fxnName + "", // paramList + "" ) // helpString +{ + bool in = VaultAmOwnerOfCurrentAge(); + PrintStringF( PrintString,"You are %san owner of the current age", in?"":"not " ); +} +PF_CONSOLE_CMD( Net_Vault, // groupName + AmCzarOfCurrentAge, // fxnName + "", // paramList + "" ) // helpString +{ + bool in = VaultAmCzarOfCurrentAge(); + PrintStringF( PrintString,"You are %sczar of the current age", in?"":"not " ); +} +// REGISTER MT STATION +PF_CONSOLE_CMD( Net_Vault, + RegisterMTStation, + "string stationName, string mtSpawnPt", + "Register an MT Station with your Nexus" ) +{ + wchar wName[MAX_PATH]; + wchar wObj[MAX_PATH]; + StrToUnicode(wName, params[0], arrsize(wName)); + StrToUnicode(wObj, params[1], arrsize(wObj)); + VaultRegisterMTStationAndWait ( wName, wObj ); + PrintString("Registered MT Station."); +} + + +// REGISTER OWNED AGE +PF_CONSOLE_CMD( Net_Vault, + RegisterOwnedAge, + "string ageName", + "Add an instance of the specified age to your bookshelf" ) +{ + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename( params[0] ); + link.GetAgeInfo()->SetAgeInstanceName( params[0] ); + link.GetAgeInfo()->SetAgeInstanceGuid( &plUUID(GuidGenerate())); + link.SetSpawnPoint( kDefaultSpawnPoint ); + bool success = VaultRegisterOwnedAgeAndWait(&link); + PrintStringF(PrintString, "Operation %s.", success ? "Successful" : "Failed"); +} + +// UNREGISTER OWNED AGE +PF_CONSOLE_CMD( Net_Vault, + UnregisterOwnedAge, + "string ageName", + "Remove the specified age from your bookshelf" ) +{ + plAgeInfoStruct info; + info.SetAgeFilename( params[0] ); + bool success = VaultUnregisterOwnedAgeAndWait(&info); + PrintStringF(PrintString, "Operation %s.", success ? "Successful" : "Failed"); +} + +// REGISTER VISIT AGE +PF_CONSOLE_CMD( Net_Vault, + RegisterVisitAge, + "string ageName", + "Add an instance of the specified age to your private links" ) +{ + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename( params[0] ); + link.GetAgeInfo()->SetAgeInstanceName( params[0] ); + link.GetAgeInfo()->SetAgeInstanceGuid( &plUUID(GuidGenerate())); + link.SetSpawnPoint( kDefaultSpawnPoint ); + bool success = VaultRegisterOwnedAgeAndWait(&link); + PrintStringF(PrintString, "Operation %s.", success ? "Successful" : "Failed"); +} + +// UNREGISTER VISIT AGE +PF_CONSOLE_CMD( Net_Vault, + UnregisterVisitAge, + "string ageName", + "Remove all instances of the specified age from your private links" ) +{ + plAgeInfoStruct info; + info.SetAgeFilename( params[0] ); + + unsigned count = 0; + while (VaultUnregisterVisitAgeAndWait(&info)) + ++count; + + PrintStringF(PrintString, "Operation %s.", count > 0 ? "Successful" : "Failed"); +} + +#endif + +/***************************************************************************** +* +* GameMgr +* +***/ + +#ifndef LIMIT_CONSOLE_COMMANDS + +//============================================================================ +// GameMgr group +PF_CONSOLE_GROUP(GameMgr) + +//============================================================================ +PF_CONSOLE_CMD( + GameMgr, + ListGames, + "", + "List all games we're currently playing" +) { + ARRAY(unsigned) arr; + pfGameMgr::GetInstance()->GetGameIds(&arr); + PrintStringF(PrintString, "You are playing %u games", arr.Count()); + for (unsigned i = 0; i < arr.Count(); ++i) { + pfGameCli * game = pfGameMgr::GetInstance()->GetGameCli(arr[i]); + PrintStringF(PrintString, "%u: Game %u, %s", i, arr[i], game->ClassName()); + } +} + +//============================================================================ +PF_CONSOLE_CMD( + GameMgr, + JoinGame, + "int gameId", + "Join the specified game of tic-tac-toe." +) { + unsigned gameId = (int)params[0]; + + pfGameMgr::GetInstance()->JoinGame( + pfConsole::GetInstance()->GetKey(), + gameId + ); +} + + +//============================================================================ +// GameMgr.TicTacToe group +PF_CONSOLE_SUBGROUP(GameMgr, TicTacToe) + +//============================================================================ +PF_CONSOLE_CMD( + GameMgr_TicTacToe, + CreateGame, + "int numPlayers", + "Create a new game of tic-tac-toe. numPlayers in [1..2]" +) { + TTT_CreateParam init; + init.playerCount = (int)params[0]; + + unsigned createOptions = 0; + + pfGameMgr::GetInstance()->CreateGame( + pfConsole::GetInstance()->GetKey(), + kGameTypeId_TicTacToe, + createOptions, + sizeof(init), + &init + ); +} + +//============================================================================ +PF_CONSOLE_CMD( + GameMgr_TicTacToe, + JoinCommonGame, + "", + "Join the common game of tic-tac-toe." +) { + TTT_CreateParam init; + init.playerCount = 2; + + unsigned gameNumber = 1; + + pfGameMgr::GetInstance()->JoinCommonGame( + pfConsole::GetInstance()->GetKey(), + kGameTypeId_TicTacToe, + gameNumber, + sizeof(init), + &init + ); +} + +//============================================================================ +PF_CONSOLE_CMD( + GameMgr_TicTacToe, + MakeMove, + "int gameId, int row, int col", + "Make your move. row and col in [0..2]" +) { + pfGameCli * game = pfGameMgr::GetInstance()->GetGameCli((int)params[0]); + if (!game) { + PrintStringF(PrintString, "Game %u not found", (int)params[0]); + return; + } + pfGmTicTacToe * ttt = pfGmTicTacToe::ConvertNoRef(game); + if (!ttt) { + PrintStringF(PrintString, "Game %u is not a game of tie-tac-toe", (int)params[0]); + return; + } + + unsigned row = (int)params[1]; + unsigned col = (int)params[2]; + + ttt->MakeMove(row, col); +} + +//============================================================================ +PF_CONSOLE_CMD( + GameMgr_TicTacToe, + ShowBoard, + "int gameId", + "Show the game board" +) { + pfGameCli * game = pfGameMgr::GetInstance()->GetGameCli((int)params[0]); + if (!game) { + PrintStringF(PrintString, "Game %u not found", (int)params[0]); + return; + } + pfGmTicTacToe * ttt = pfGmTicTacToe::ConvertNoRef(game); + if (!ttt) { + PrintStringF(PrintString, "Game %u is not a game of tie-tac-toe", (int)params[0]); + return; + } + + ttt->ShowBoard(); +} + +//============================================================================ +PF_CONSOLE_CMD( + GameMgr_TicTacToe, + InvitePlayer, + "int gameId, string playerName", + "Invite a player to play tic-tac-toe with you" +) { + pfGameCli * game = pfGameMgr::GetInstance()->GetGameCli((int)params[0]); + if (!game) { + PrintStringF(PrintString, "Game %u not found", (int)params[0]); + return; + } + pfGmTicTacToe * ttt = pfGmTicTacToe::ConvertNoRef(game); + if (!ttt) { + PrintStringF(PrintString, "Game %u is not a game of tie-tac-toe", (int)params[0]); + return; + } + + if (unsigned playerId = plNetClientMgr::GetInstance()->GetPlayerIdByName((const char *)params[1])) { + ttt->InvitePlayer(playerId); + PrintStringF(PrintString, "Sent invite to playerId %u", playerId); + } + else { + PrintStringF(PrintString, "Player %s not found in this age", (const char *)params[1]); + } +} + +//============================================================================ +PF_CONSOLE_CMD( + GameMgr_TicTacToe, + Uninvite, + "int gameId, int playerId", + "Revoke an invitation to your game of tic-tac-toe" +) { + pfGameCli * game = pfGameMgr::GetInstance()->GetGameCli((int)params[0]); + if (!game) { + PrintStringF(PrintString, "Game %u not found", (int)params[0]); + return; + } + pfGmTicTacToe * ttt = pfGmTicTacToe::ConvertNoRef(game); + if (!ttt) { + PrintStringF(PrintString, "Game %u is not a game of tie-tac-toe", (int)params[0]); + return; + } + + ttt->UninvitePlayer((int)params[1]); +} + +//============================================================================ +PF_CONSOLE_CMD( + GameMgr_TicTacToe, + LeaveGame, + "int gameId", + "Abandon the game of tic-tac-toe" +) { + pfGameCli * game = pfGameMgr::GetInstance()->GetGameCli((int)params[0]); + if (!game) { + PrintStringF(PrintString, "Game %u not found", (int)params[0]); + return; + } + pfGmTicTacToe * ttt = pfGmTicTacToe::ConvertNoRef(game); + if (!ttt) { + PrintStringF(PrintString, "Game %u is not a game of tie-tac-toe", (int)params[0]); + return; + } + + ttt->LeaveGame(); +} + +#endif + +/////////////////////////////////////// diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleContext.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleContext.cpp new file mode 100644 index 00000000..dd8bad6b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleContext.cpp @@ -0,0 +1,276 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfConsoleContext // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfConsoleContext.h" + + +//// Static Root Context ///////////////////////////////////////////////////// + +pfConsoleContext pfConsoleContext::fRootContext( "global" ); + +pfConsoleContext &pfConsoleContext::GetRootContext( void ) +{ + return fRootContext; +} + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfConsoleContext::pfConsoleContext( const char *name ) +{ + fName = ( name != nil ) ? hsStrcpy( name ) : nil; + fAddWhenNotFound = true; +} + +pfConsoleContext::~pfConsoleContext() +{ + Clear(); + delete [] fName; +} + +//// Clear /////////////////////////////////////////////////////////////////// + +void pfConsoleContext::Clear( void ) +{ + Int32 idx; + + + for( idx = fVarValues.GetCount() - 1; idx >= 0; idx-- ) + RemoveVar( idx ); +} + +//// Getters ///////////////////////////////////////////////////////////////// + +UInt32 pfConsoleContext::GetNumVars( void ) const +{ + hsAssert( fVarValues.GetCount() == fVarNames.GetCount(), "Mismatch in console var context arrays" ); + return fVarValues.GetCount(); +} + +const char *pfConsoleContext::GetVarName( UInt32 idx ) const +{ + hsAssert( fVarValues.GetCount() == fVarNames.GetCount(), "Mismatch in console var context arrays" ); + + if( idx >= fVarNames.GetCount() ) + { + hsAssert( false, "GetVarName() index out of range for console context" ); + return nil; + } + + return fVarNames[ idx ]; +} + +pfConsoleCmdParam &pfConsoleContext::GetVarValue( UInt32 idx ) const +{ + hsAssert( fVarValues.GetCount() == fVarNames.GetCount(), "Mismatch in console var context arrays" ); + hsAssert( idx < fVarValues.GetCount(), "GetVarValue() index out of range for console context" ); + + return fVarValues[ idx ]; +} + + +//// FindVar ///////////////////////////////////////////////////////////////// + +Int32 pfConsoleContext::FindVar( const char *name ) const +{ + UInt32 idx; + + + hsAssert( fVarValues.GetCount() == fVarNames.GetCount(), "Mismatch in console var context arrays" ); + + for( idx = 0; idx < fVarNames.GetCount(); idx++ ) + { + if( stricmp( name, fVarNames[ idx ] ) == 0 ) + { + return (Int32)idx; + } + } + + return -1; +} + +//// RemoveVar /////////////////////////////////////////////////////////////// + +void pfConsoleContext::RemoveVar( UInt32 idx ) +{ + hsAssert( fVarValues.GetCount() == fVarNames.GetCount(), "Mismatch in console var context arrays" ); + + if( idx >= fVarValues.GetCount() ) + { + hsAssert( false, "RemoveVar() index out of range for console context" ); + return; + } + + delete [] fVarNames[ idx ]; + if( fVarValues[ idx ].GetType() == pfConsoleCmdParam::kString ) + // Necessary because the params won't delete the data themselves + delete [] ( (char *)fVarValues[ idx ] ); + + fVarNames.Remove( idx ); + fVarValues.Remove( idx ); +} + +//// AddVar Variants ///////////////////////////////////////////////////////// + +void pfConsoleContext::IAddVar( const char *name, const pfConsoleCmdParam &value ) +{ + fVarNames.Append( hsStrcpy( name ) ); + fVarValues.Append( value ); + + // Remember, params won't know any better, since by default they don't own a copy of their string + UInt32 idx = fVarValues.GetCount() - 1; + if( fVarValues[ idx ].GetType() == pfConsoleCmdParam::kString ) + fVarValues[ idx ].SetString( hsStrcpy( fVarValues[ idx ] ) ); +} + +void pfConsoleContext::AddVar( const char *name, const pfConsoleCmdParam &value ) +{ + Int32 idx = FindVar( name ); + if( idx != -1 ) + { + hsAssert( false, "AddVar() failed because variable already in console context" ); + return; + } + + IAddVar( name, value ); +} + +void pfConsoleContext::AddVar( const char *name, int value ) +{ + pfConsoleCmdParam param; + param.SetInt( value ); + AddVar( name, param ); +} + +void pfConsoleContext::AddVar( const char *name, float value ) +{ + pfConsoleCmdParam param; + param.SetFloat( value ); + AddVar( name, param ); +} + +void pfConsoleContext::AddVar( const char *name, const char *value ) +{ + pfConsoleCmdParam param; + param.SetString( (char *)value ); // It's ok, we'll be copying it soon 'nuf + AddVar( name, param ); +} + +void pfConsoleContext::AddVar( const char *name, char value ) +{ + pfConsoleCmdParam param; + param.SetChar( value ); + AddVar( name, param ); +} + +void pfConsoleContext::AddVar( const char *name, bool value ) +{ + pfConsoleCmdParam param; + param.SetBool( value ); + AddVar( name, param ); +} + +//// SetVar Variants ///////////////////////////////////////////////////////// + +hsBool pfConsoleContext::SetVar( UInt32 idx, const pfConsoleCmdParam &value ) +{ + hsAssert( fVarValues.GetCount() == fVarNames.GetCount(), "Mismatch in console var context arrays" ); + if( idx >= fVarValues.GetCount() ) + { + hsAssert( false, "SetVar() index out of range for console context" ); + return false; + } + + if( fVarValues[ idx ].GetType() == pfConsoleCmdParam::kString ) + { + // Remember, params won't know any better, since by default they don't own a copy of their string + delete [] ( (char *)fVarValues[ idx ] ); + } + + fVarValues[ idx ] = value; + if( fVarValues[ idx ].GetType() == pfConsoleCmdParam::kString ) + fVarValues[ idx ].SetString( hsStrcpy( fVarValues[ idx ] ) ); + + return true; +} + +hsBool pfConsoleContext::SetVar( const char *name, const pfConsoleCmdParam &value ) +{ + Int32 idx = FindVar( name ); + if( idx == -1 ) + { + if( fAddWhenNotFound ) + { + // Don't panic, just add + IAddVar( name, value ); + return true; + } + return false; + } + + return SetVar( idx, value ); +} + +hsBool pfConsoleContext::SetVar( const char *name, int value ) +{ + pfConsoleCmdParam param; + param.SetInt( value ); + return SetVar( name, param ); +} + +hsBool pfConsoleContext::SetVar( const char *name, float value ) +{ + pfConsoleCmdParam param; + param.SetFloat( value ); + return SetVar( name, param ); +} + +hsBool pfConsoleContext::SetVar( const char *name, const char *value ) +{ + pfConsoleCmdParam param; + param.SetString( (char *)value ); // Don't worry, we'll be copying it soon 'nuf + return SetVar( name, param ); +} + +hsBool pfConsoleContext::SetVar( const char *name, char value ) +{ + pfConsoleCmdParam param; + param.SetChar( value ); + return SetVar( name, param ); +} + +hsBool pfConsoleContext::SetVar( const char *name, bool value ) +{ + pfConsoleCmdParam param; + param.SetBool( value ); + return SetVar( name, param ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleContext.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleContext.h new file mode 100644 index 00000000..810498d8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleContext.h @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfConsoleContext Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfConsoleContext_h +#define _pfConsoleContext_h + +#include "hsTypes.h" +#include "pfConsoleCmd.h" + + +//// Class Definition //////////////////////////////////////////////////////// + +class pfConsoleContext +{ + protected: + + hsBool fAddWhenNotFound; // Controls whether we add variables on Set() calls if they're not found + + char *fName; + + hsTArray fVarNames; + hsTArray fVarValues; + + void IAddVar( const char *name, const pfConsoleCmdParam &value ); + + static pfConsoleContext fRootContext; + + public: + + pfConsoleContext( const char *name ); + virtual ~pfConsoleContext(); + + void Clear( void ); + + UInt32 GetNumVars( void ) const; + const char *GetVarName( UInt32 idx ) const; + pfConsoleCmdParam &GetVarValue( UInt32 idx ) const; + + Int32 FindVar( const char *name ) const; + void RemoveVar( UInt32 idx ); + + void AddVar( const char *name, const pfConsoleCmdParam &value ); + void AddVar( const char *name, int value ); + void AddVar( const char *name, float value ); + void AddVar( const char *name, const char *value ); + void AddVar( const char *name, char value ); + void AddVar( const char *name, bool value ); + + hsBool SetVar( UInt32 idx, const pfConsoleCmdParam &value ); + + hsBool SetVar( const char *name, const pfConsoleCmdParam &value ); + hsBool SetVar( const char *name, int value ); + hsBool SetVar( const char *name, float value ); + hsBool SetVar( const char *name, const char *value ); + hsBool SetVar( const char *name, char value ); + hsBool SetVar( const char *name, bool value ); + + // Decide whether Sets() on nonexistant variables will fail or add a new variable + void SetAddWhenNotFound( hsBool f ) { fAddWhenNotFound = f; } + hsBool GetAddWhenNotFound( void ) const { return fAddWhenNotFound; } + + static pfConsoleContext &GetRootContext( void ); +}; + + +#endif //_pfConsoleContext_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCreatable.h new file mode 100644 index 00000000..72ce4cc7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCreatable.h @@ -0,0 +1,36 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pfConsoleCreatable_inc +#define pfConsoleCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "pfConsole.h" + +REGISTER_CREATABLE( pfConsole ); + +#endif // pfConsoleCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleDirSrc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleDirSrc.cpp new file mode 100644 index 00000000..3f88bf57 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleDirSrc.cpp @@ -0,0 +1,149 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfConsoleDirSrc Functions // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "pfConsoleDirSrc.h" + +#include "hsExceptions.h" + +#ifdef HS_BUILD_FOR_WIN32 + +#define WIN32_EXTRA_LEAN +#define WIN32_LEAN_AND_MEAN +#ifndef _WINDOWS_H_ // redundant include guard to minimize compile times +#define _WINDOWS_H_ +#include +#endif // _WINDOWS_H_ + +#include + +#include + + +//// ParseDirectory ////////////////////////////////////////////////////////// + +hsBool pfConsoleDirSrc::ParseDirectory(const std::string& path, const std::string& mask /* = "*.*" */) +{ + wchar* wPath = hsStringToWString(path.c_str()); + wchar* wMask = hsStringToWString(mask.c_str()); + hsBool ret = ParseDirectory(wPath, wMask); + delete [] wPath; + delete [] wMask; + return ret; +} + +hsBool pfConsoleDirSrc::ParseDirectory(const std::wstring& path, const std::wstring& mask /* = L"*.*" */) +{ + std::wstringstream search; + std::wstring file; + WIN32_FIND_DATAW findInfo; + HANDLE handle; + + hsAssert( fEngine != nil, "Cannot do a dir execute without an engine!" ); + + search << path << L"\\" << mask; + handle = FindFirstFileW(search.str().c_str(), &findInfo); + if (handle == INVALID_HANDLE_VALUE) + return false; + + do + { + if (!( findInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + std::wstringstream fileAndPath; + fileAndPath << path << L"\\" << findInfo.cFileName; + if (AlreadyProcessedFile(path, findInfo.cFileName)) + continue; + AddProcessedFile(path, findInfo.cFileName); + if (!fEngine->ExecuteFile(fileAndPath.str().c_str())) + { + // Change the following line once we have a better way of reporting + // errors in the parsing + std::wstringstream error; + std::wstringstream caption; + wchar* errorMsg = hsStringToWString(fEngine->GetErrorMsg()); + wchar* errorLine = hsStringToWString(fEngine->GetLastErrorLine()); + + caption << L"Error parsing " << findInfo.cFileName; + error << errorMsg << L":\n\nCommand: '" << errorLine << L"'\n\nPress OK to continue parsing files."; + + hsMessageBox(error.str().c_str(), caption.str().c_str(), hsMessageBoxNormal); + + delete [] errorMsg; + delete [] errorLine; + + FindClose(handle); + SetCheckProcessedFiles(true); + return false; + } + } + } while (FindNextFileW(handle, &findInfo) != 0); + + FindClose(handle); + SetCheckProcessedFiles(true); + return true; +} + +#else + +#error This needs to be implemented for this platform!!!! + +#endif + +void pfConsoleDirSrc::ResetProcessedFiles() +{ + int i; + for(i=0;ifFile && path == fProcessedFiles[i]->fPath) + return true; + } + } + return false; +} + +void pfConsoleDirSrc::AddProcessedFile(const std::wstring& path, const std::wstring& file) +{ + fProcessedFiles.push_back(TRACKED_NEW FileName(path, file)); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleDirSrc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleDirSrc.h new file mode 100644 index 00000000..3413f41e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleDirSrc.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfConsoleDirSrc Header // +// // +//// Description ///////////////////////////////////////////////////////////// +// // +// Simple wrapper for parsing an entire directory of files and executing // +// each one through the pfConsoleEngine object given. // +// I.E. the source for the console commmands is a directory of files, // +// hence it's a Console Directory Source, or ConsoleDirSrc. :) // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfConsoleDirSrc_h +#define _pfConsoleDirSrc_h + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "pfConsoleEngine.h" + +//// pfConsoleDirSrc Class Definition //////////////////////////////////////// + +class pfConsoleDirSrc +{ + protected: + pfConsoleEngine *fEngine; + struct FileName + { + std::wstring fPath; + std::wstring fFile; + FileName() : fPath(L""), fFile(L"") {} + FileName(const std::wstring& p, const std::wstring& f) : fPath(p), fFile(f) {} + }; + std::vector fProcessedFiles; // list of init files we've already executed + hsBool fCheckProcessedFiles; // set to check and skip files init files we've already executed + public: + pfConsoleDirSrc(pfConsoleEngine *engine) : fCheckProcessedFiles(false) { fEngine = engine; } + pfConsoleDirSrc(pfConsoleEngine *engine, const std::string& path, const std::string& mask = "*.ini") : + fCheckProcessedFiles(false) + { + fEngine = engine; + ParseDirectory(path, mask); + } + pfConsoleDirSrc(pfConsoleEngine *engine, const std::wstring& path, const std::wstring& mask = L"*.ini") : + fCheckProcessedFiles(false) + { + fEngine = engine; + ParseDirectory(path, mask); + } + + ~pfConsoleDirSrc() { ResetProcessedFiles(); } + + // Steps through the given directory and executes all files with the console engine + hsBool ParseDirectory(const std::string& path, const std::string& mask = "*.*"); + hsBool ParseDirectory(const std::wstring& path, const std::wstring& mask = L"*.*"); + + void ResetProcessedFiles(); + hsBool AlreadyProcessedFile(const std::wstring& path, const std::wstring& file); + void AddProcessedFile(const std::wstring& path, const std::wstring& file); + void SetCheckProcessedFiles(hsBool c) { fCheckProcessedFiles=c; } +}; + + +#endif //_pfConsoleDirSrc_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleEngine.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleEngine.cpp new file mode 100644 index 00000000..d2d84cdc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleEngine.cpp @@ -0,0 +1,579 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfConsoleEngine Functions // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "pfConsoleEngine.h" +#include "pfConsoleCmd.h" +#include "pfConsoleContext.h" + +#include "../plFile/plEncryptedStream.h" + + +const Int32 pfConsoleEngine::fMaxNumParams = 16; +const char pfConsoleEngine::fTokenSeparators[] = " =\r\n\t,"; +const char pfConsoleEngine::fTokenGrpSeps[] = " =\r\n._\t,"; + + + +//// Constructor & Destructor //////////////////////////////////////////////// + +pfConsoleEngine::pfConsoleEngine() +{ +} + +pfConsoleEngine::~pfConsoleEngine() +{ +} + +//// PrintCmdHelp //////////////////////////////////////////////////////////// + +hsBool pfConsoleEngine::PrintCmdHelp( char *name, void (*PrintFn)( const char * ) ) +{ + pfConsoleCmd *cmd; + pfConsoleCmdGroup *group, *subGrp; + char *ptr; + static char string[ 512 ]; + static char tempString[ 512 ]; + UInt32 i; + + + /// Scan for subgroups. This can be an empty loop + group = pfConsoleCmdGroup::GetBaseGroup(); + ptr = strtok( name, fTokenGrpSeps ); + while( ptr != nil ) + { + // Take this token and check to see if it's a group + if( ( subGrp = group->FindSubGroupNoCase( ptr ) ) != nil ) + group = subGrp; + else + break; + + ptr = strtok( nil, fTokenGrpSeps ); + } + + if( ptr == nil ) + { + if( group == nil ) + { + ISetErrorMsg( "Invalid command syntax" ); + return false; + } + + // Print help for this group + if( group == pfConsoleCmdGroup::GetBaseGroup() ) + strcpy( string, "Base commands and groups:" ); + else + sprintf( string, "Group %s:", group->GetName() ); + PrintFn( string ); + PrintFn( " Subgroups:" ); + for( subGrp = group->GetFirstSubGroup(); subGrp != nil; subGrp = subGrp->GetNext() ) + { + sprintf( string, " %s", subGrp->GetName() ); + PrintFn( string ); + } + PrintFn( " Commands:" ); + for( cmd = group->GetFirstCommand(); cmd != nil; cmd = cmd->GetNext() ) + { + for( ptr = cmd->GetHelp(), i = 0; ptr[ i ] != 0 && ptr[ i ] != '\n'; i++ ) + tempString[ i ] = ptr[ i ]; + tempString[ i ] = 0; + + sprintf( string, " %s: %s", cmd->GetName(), tempString ); + PrintFn( string ); + } + + return true; + } + + /// OK, so what we found wasn't a group. Which means we need a command... + cmd = group->FindCommandNoCase( ptr ); + if( cmd == nil ) + { + ISetErrorMsg( "Invalid syntax: command not found" ); + return false; + } + + /// That's it! + sprintf( string, "\nHelp for the command %s:", cmd->GetName() ); + PrintFn( string ); + sprintf( string, "\\i%s", cmd->GetHelp() ); + PrintFn( string ); + sprintf( string, "\\iUsage: %s", cmd->GetSignature() ); + PrintFn( string ); + + return true; +} + +//// GetCmdSignature ///////////////////////////////////////////////////////// + +const char *pfConsoleEngine::GetCmdSignature( char *name ) +{ + pfConsoleCmd *cmd; + pfConsoleCmdGroup *group, *subGrp; + char *ptr; + static char string[ 512 ]; + + + /// Scan for subgroups. This can be an empty loop + group = pfConsoleCmdGroup::GetBaseGroup(); + ptr = strtok( name, fTokenGrpSeps ); + while( ptr != nil ) + { + // Take this token and check to see if it's a group + if( ( subGrp = group->FindSubGroupNoCase( ptr ) ) != nil ) + group = subGrp; + else + break; + + ptr = strtok( nil, fTokenGrpSeps ); + } + + if( ptr == nil ) + { + ISetErrorMsg( "Invalid command syntax" ); + return nil; + } + + /// OK, so what we found wasn't a group. Which means we need a command... + cmd = group->FindCommandNoCase( ptr ); + if( cmd == nil ) + { + ISetErrorMsg( "Invalid syntax: command not found" ); + return nil; + } + + /// That's it! + return (char *)cmd->GetSignature(); +} + +//// Dummy Local Function //////////////////////////////////////////////////// + +void DummyPrintFn( const char *line ) +{ +} + +//// ExecuteFile ///////////////////////////////////////////////////////////// + +hsBool pfConsoleEngine::ExecuteFile( const char *fileName ) +{ + wchar* wFilename = hsStringToWString(fileName); + hsBool ret = ExecuteFile(wFilename); + delete [] wFilename; + return ret; +} + +hsBool pfConsoleEngine::ExecuteFile( const wchar *fileName ) +{ + char string[ 512 ]; + int line; + + hsStream* stream = plEncryptedStream::OpenEncryptedFile(fileName); + + if( !stream ) + { + ISetErrorMsg( "Cannot open given file" ); +// return false; + /// THIS IS BAD: because of the asserts we throw after this if we return false, a missing + /// file will throw an assert. This is all well and good except for the age-specific .fni files, + /// which aren't required to be there and rely on this functionality to test whether the file is + /// present. Once age-specific .fni's are gone, reinstate the return false here. -mcn + return true; + } + + for( line = 1; stream->ReadLn( string, sizeof( string ) ); line++ ) + { + strncpy( fLastErrorLine, string, sizeof( fLastErrorLine ) ); + + if( !RunCommand( string, DummyPrintFn ) ) + { + sprintf( string, "Error in console file %s, command line %d: %s", fileName, line, fErrorMsg ); + ISetErrorMsg( string ); + stream->Close(); + delete stream; + return false; + } + } + stream->Close(); + delete stream; + fLastErrorLine[ 0 ] = 0; + + return true; +} + +//// RunCommand ////////////////////////////////////////////////////////////// +// Updated 2.14.2001 mcn to support spaces, _ or . as group separators. This +// requires tokenizing the entire line and searching the tokens one by one, +// parsing them first as groups, then commands and then params. + +hsBool pfConsoleEngine::RunCommand( char *line, void (*PrintFn)( const char * ) ) +{ + pfConsoleCmd *cmd; + pfConsoleCmdGroup *group, *subGrp; + Int32 numParams, i, numQuotedParams = 0; + pfConsoleCmdParam paramArray[ fMaxNumParams + 1 ]; + char *ptr; + hsBool valid = true; + + + hsAssert( line != nil, "Bad parameter to RunCommand()" ); + + /// Loop #1: Scan for subgroups. This can be an empty loop + group = pfConsoleCmdGroup::GetBaseGroup(); + ptr = strtok( line, fTokenGrpSeps ); + while( ptr != nil ) + { + // Take this token and check to see if it's a group + if( ( subGrp = group->FindSubGroupNoCase( ptr ) ) != nil ) + group = subGrp; + else + break; + + ptr = strtok( nil, fTokenGrpSeps ); + } + + if( ptr == nil ) + { + ISetErrorMsg( "Invalid command syntax" ); + return false; + } + + /// OK, so what we found wasn't a group. Which means we need a command next + cmd = group->FindCommandNoCase( ptr ); + if( cmd == nil ) + { + ISetErrorMsg( "Invalid syntax: command not found" ); + return false; + } + + /// We have the group, we have the command from that group. So continue + /// tokenizing (with the new separators now, mind you) and turn them into + /// params + + for( numParams = numQuotedParams = 0; numParams < fMaxNumParams + && ( ptr = strtok( nil, fTokenSeparators ) ) != nil + && valid; numParams++ ) + { + if( ptr[ 0 ] == '\'' || ptr[ 0 ] == '"' ) + { + // String parameter--keep getting tokens until we hit the other end + + // Note: since params take pointers to strings, we have to have unique temp strings + // for each quoted param we parse. So we have a static array here to a) do so, b) + // avoid having to delete them afterwards, and thus c) reduce overhead. + static char tempStrings[ fMaxNumParams ][ 512 ]; + + char *tempStr = tempStrings[ numQuotedParams++ ], toSearch[ 2 ] = "'"; + + toSearch[ 0 ] = ptr[ 0 ]; + + if( strlen( ptr ) >= sizeof( tempStrings[ 0 ] ) ) // They're all the same, after all... + { + ISetErrorMsg( "Invalid syntax: quoted parameter too long" ); + return false; + } + + if( strlen( ptr ) > 1 && ptr[ strlen( ptr ) - 1 ] == toSearch[ 0 ] ) + { + // Single word string + strcpy( tempStr, ptr + 1 ); + tempStr[ strlen( tempStr ) - 1 ] = 0; + } + else + { + // Multiple word string + sprintf( tempStr, "%s ", ptr + 1 ); // Not perfect, but close + ptr = strtok( nil, toSearch ); + if( ptr == nil ) + { + ISetErrorMsg( "Invalid syntax: unterminated quoted parameter" ); + return false; + } + + if( strlen( ptr ) + strlen( tempStr ) >= sizeof( tempStrings[ 0 ] ) ) // They're all the same, after all... + { + ISetErrorMsg( "Invalid syntax: quoted parameter too long" ); + return false; + } + strcat( tempStr, ptr ); + } + + valid = IConvertToParam( cmd->GetSigEntry( (UInt8)numParams ), tempStr, ¶mArray[ numParams ] ); + } + else + { + // Normal parameter + + // Special case for context variables--if we're specifying one, we want to just grab + // the value of it and return that instead + valid = false; + if( ptr[ 0 ] == '$' ) + { + pfConsoleContext &context = pfConsoleContext::GetRootContext(); + + // Potential variable, see if we can find it + Int32 idx = context.FindVar( ptr + 1 ); + if( idx == -1 ) + { + ISetErrorMsg( "Invalid console variable name" ); + } + else + { + // Just copy. Note that this will copy string pointers, but so long as the variable in + // question doesn't change, we'll be OK... + paramArray[ numParams ] = context.GetVarValue( idx ); + valid = true; + } + } + + if( !valid ) + valid = IConvertToParam( cmd->GetSigEntry( (UInt8)numParams ), ptr, ¶mArray[ numParams ] ); + } + } + for( i = numParams; i < fMaxNumParams + 1; i++ ) + paramArray[ i ].SetNone(); + + if( !valid || ( cmd->GetSigEntry( (UInt8)numParams ) != pfConsoleCmd::kAny && + cmd->GetSigEntry( (UInt8)numParams ) != pfConsoleCmd::kNone ) ) + { + // Print help string and return + static char string[ 512 ]; + + ISetErrorMsg( "" ); // Printed on next line + PrintFn( "Invalid parameters to command" ); + sprintf( string, "Usage: %s", cmd->GetSignature() ); + PrintFn( string ); + return false; + } + + /// Execute it and return + cmd->Execute( numParams, paramArray, PrintFn ); + return true; +} + +//// IConvertToParam ///////////////////////////////////////////////////////// +// Converts a null-terminated string representing a parameter to a +// pfConsoleCmdParam argument. + +hsBool pfConsoleEngine::IConvertToParam( UInt8 type, char *string, pfConsoleCmdParam *param ) +{ + char *c, expChars[] = "dDeE+-."; + hsBool hasDecimal = false, hasLetters = false; + + + if( type == pfConsoleCmd::kNone ) + return false; + + for( c = string; *c != 0; c++ ) + { + if( !isdigit( *c ) ) + { + if( c == string && ( *c == '-' || *c == '+' ) ) + { + // Do nothing--perfectly legal to have these at the beginning of an int + } + else if( strchr( expChars, *c ) != nil ) + hasDecimal = true; + else + hasLetters = true; + } + } + + if( type == pfConsoleCmd::kAny ) + { + /// Want "any" + param->SetAny( string ); + } + else if( type == pfConsoleCmd::kString ) + { + /// Want just a string + param->SetString( string ); + } + else if( type == pfConsoleCmd::kFloat ) + { + if( hasLetters ) + return false; + + param->SetFloat( (float)atof( string ) ); + } + else if( type == pfConsoleCmd::kInt ) + { + if( hasLetters || hasDecimal ) + return false; + + param->SetInt( atoi( string ) ); + } + else if( type == pfConsoleCmd::kBool ) + { + if( stricmp( string, "true" ) == 0 || stricmp( string, "t" ) == 0 ) + param->SetBool( true ); + else if( stricmp( string, "false" ) == 0 || stricmp( string, "f" ) == 0 ) + param->SetBool( false ); + else if( atoi( string ) == 0 ) + param->SetBool( false ); + else + param->SetBool( true ); + } + + return true; +} + +//// FindPartialCmd ////////////////////////////////////////////////////////// +// Given a string which is the beginning of a console command, modifies the +// string to represent the best match of command (or group) for that string. +// WARNING: modifies the string passed to it. + +hsBool pfConsoleEngine::FindPartialCmd( char *line, hsBool findAgain, hsBool preserveParams ) +{ + pfConsoleCmd *cmd = nil; + pfConsoleCmdGroup *group, *subGrp; + hsBool foundMore = false; + + static char *ptr = nil, *insertLoc = nil; + static pfConsoleCmd *lastCmd = nil; + static pfConsoleCmdGroup *lastGroup = nil, *lastParentGroup = nil; + static char newStr[ 256 ]; + + + /// Repeat search + if( !findAgain ) + { + lastCmd = nil; + lastGroup = nil; + } + + /// New search + if( strlen( line ) > sizeof( newStr ) ) + return false; + + newStr[ 0 ] = 0; + insertLoc = newStr; + + /// Loop #1: Scan for subgroups. This can be an empty loop + lastParentGroup = group = pfConsoleCmdGroup::GetBaseGroup(); + ptr = strtok( line, fTokenGrpSeps ); + while( ptr != nil ) + { + // Take this token and check to see if it's a group + if( ( subGrp = group->FindSubGroupNoCase( ptr, 0/*pfConsoleCmdGroup::kFindPartial*/ + , /*lastGroup*/nil ) ) != nil ) + { + lastParentGroup = group; + group = subGrp; + strcat( newStr, group->GetName() ); + insertLoc += strlen( group->GetName() ); + } + else + break; + + ptr = strtok( nil, fTokenGrpSeps ); + strcat( newStr, "." ); + insertLoc++; + } + + if( ptr != nil ) + { + // Still got at least one token left. Try to match to either + // a partial group or a partial command + if( ( subGrp = group->FindSubGroupNoCase( ptr, pfConsoleCmdGroup::kFindPartial, lastGroup ) ) != nil ) + { + lastParentGroup = group; + lastGroup = group = subGrp; + strcat( newStr, group->GetName() ); + strcat( newStr, "." ); + } + else + { + cmd = group->FindCommandNoCase( ptr, pfConsoleCmdGroup::kFindPartial, lastCmd ); + if( cmd == nil ) + return false; + + strcat( newStr, cmd->GetName() ); + strcat( newStr, " " ); + lastCmd = cmd; + } + } + + if( preserveParams ) + { + /// Preserve the rest of the string after the matched command + ptr = strtok( nil, "\0" ); + if( ptr != nil ) + strcat( newStr, ptr ); + } + + // Copy back! + strcpy( line, newStr ); + + return true; +} + +//// FindNestedPartialCmd //////////////////////////////////////////////////// +// Same as FindPartialCmd, only starts from the global group and searches +// everything. The string passed should only be a partial command sans +// groups. numToSkip specifies how many matches to skip before returning one +// (so if numToSkip = 1, then this will return the second match found). + +hsBool pfConsoleEngine::FindNestedPartialCmd( char *line, UInt32 numToSkip, hsBool preserveParams ) +{ + pfConsoleCmd *cmd; + + + /// Somewhat easier than FindPartialCmd... + cmd = pfConsoleCmdGroup::GetBaseGroup()->FindNestedPartialCommand( line, &numToSkip ); + if( cmd == nil ) + return false; + + /// Recurse back up and get the group hierarchy + line[ 0 ] = 0; + IBuildCmdNameRecurse( cmd->GetParent(), line ); + strcat( line, cmd->GetName() ); + strcat( line, " " ); + + if( preserveParams ) + { + /// Preserve the rest of the string after the matched command + } + + return true; +} + +//// IBuildCmdNameRecurse //////////////////////////////////////////////////// + +void pfConsoleEngine::IBuildCmdNameRecurse( pfConsoleCmdGroup *group, char *string ) +{ + if( group == nil || group == pfConsoleCmdGroup::GetBaseGroup() ) + return; + + + IBuildCmdNameRecurse( group->GetParent(), string ); + + strcat( string, group->GetName() ); + strcat( string, "." ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleEngine.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleEngine.h new file mode 100644 index 00000000..ee62b5ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfConsoleEngine.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfConsoleEngine Header // +// // +//// Description ///////////////////////////////////////////////////////////// +// // +// The engine is where parsing and execution of console commands takes // +// place. It takes place independently of the interface, so that the // +// execution can happen from an INI file, from a screen-based console or // +// even a GUI interface. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfConsoleEngine_h +#define _pfConsoleEngine_h + +#include "hsTypes.h" +#include "hsUtils.h" + + +//// pfConsoleEngine Class Definition //////////////////////////////////////// + +class pfConsoleCmdParam; +class pfConsoleCmdGroup; +class pfConsoleEngine +{ + private: + + static const Int32 fMaxNumParams; + static const char fTokenSeparators[]; + static const char fTokenGrpSeps[]; + + hsBool IConvertToParam( UInt8 type, char *string, pfConsoleCmdParam *param ); + + char fErrorMsg[ 128 ]; + char fLastErrorLine[ 512 ]; + + void ISetErrorMsg( char *msg ) { hsStrncpy( fErrorMsg, msg, sizeof( fErrorMsg ) ); } + + // Recursive function to build a string of the groups a command is in + void IBuildCmdNameRecurse( pfConsoleCmdGroup *group, char *string ); + + public: + + pfConsoleEngine(); + ~pfConsoleEngine(); + + // Gets the signature for the command given (NO groups!) + const char *GetCmdSignature( char *name ); + + // Prints out the help for a given command (or group) + hsBool PrintCmdHelp( char *name, void (*PrintFn)( const char * ) ); + + // Breaks the given line into a command and parameters and runs the command + hsBool RunCommand( char *line, void (*PrintFn)( const char * ) ); + + // Executes the given file as a sequence of console commands + hsBool ExecuteFile( const char *fileName ); + hsBool ExecuteFile( const wchar *fileName ); + + // Get the last reported error + const char *GetErrorMsg( void ) { return fErrorMsg; } + + // Get the line for which the last reported error was for + const char *GetLastErrorLine( void ) { return fLastErrorLine; } + + // Does command completion on a partially-complete console line + hsBool FindPartialCmd( char *line, hsBool findAgain = false, hsBool perserveParams = false ); + + // Does command completion without restrictions to any group, skipping the number of matches given + hsBool FindNestedPartialCmd( char *line, UInt32 numToSkip, hsBool perserveParams = false ); +}; + + +#endif //_pfConsoleEngine_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.cpp new file mode 100644 index 00000000..7018b09b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.cpp @@ -0,0 +1,313 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTimer.h" +#include "pfDispatchLog.h" +#include "../plStatusLog/plStatusLog.h" +#include "../pnMessage/plMessage.h" +#include "../pnKeyedObject/plKey.h" +#include "hsWindows.h" + +bool DumpSpecificMsgInfo(plMessage* msg, std::string& info); + +plDispatchLog::plDispatchLog() : + fLog(nil), + fStartTicks(hsTimer::GetFullTickCount()) +{ + fLog = plStatusLogMgr::GetInstance().CreateStatusLog(20, "Dispatch.log", plStatusLog::kAlignToTop | plStatusLog::kFilledBackground | plStatusLog::kRawTimeStamp); + fIncludeTypes.SetSize(plFactory::GetNumClasses()); +} + +plDispatchLog::~plDispatchLog() +{ + delete fLog; +} + +void plDispatchLog::InitInstance() +{ + static plDispatchLog dispatchLog; + fInstance = &dispatchLog; +} + +void plDispatchLog::LogStatusBarChange(const char* name, const char* action) +{ + fLog->AddLineF("----- Status bar '%s' %s -----", name, action); + +#ifdef HS_BUILD_FOR_WIN32 + MEMORYSTATUS ms; + GlobalMemoryStatus(&ms); + + MEMORY_BASIC_INFORMATION mbi; + memset(&mbi, 0, sizeof(MEMORY_BASIC_INFORMATION)); + + // Note: this will return shared mem too on Win9x. There's a way to catch that, but it's too slow -Colin + UInt32 processMemUsed = 0; + void* curAddress = 0; + while (VirtualQuery(curAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION)) == sizeof(MEMORY_BASIC_INFORMATION)) + { + if (mbi.State == MEM_COMMIT && mbi.Type == MEM_PRIVATE) + processMemUsed += mbi.RegionSize; + curAddress = ((BYTE*)mbi.BaseAddress) + mbi.RegionSize; + } + + #define ToMB(mem) float(mem) / (1024.f*1024.f) + fLog->AddLineF("# Mem stats"); + fLog->AddLineF("# Physical: %.1f MB used %.1f MB free", ToMB(ms.dwTotalPhys-ms.dwAvailPhys), ToMB(ms.dwAvailPhys)); + fLog->AddLineF("# Virtual: %.1f MB used %.1f MB free", ToMB(ms.dwTotalVirtual-ms.dwAvailVirtual), ToMB(ms.dwAvailVirtual)); + fLog->AddLineF("# Pagefile: %.1f MB used %.1f MB free", ToMB(ms.dwTotalPageFile-ms.dwAvailPageFile), ToMB(ms.dwAvailPageFile)); + fLog->AddLineF("# Process: %.1f MB used", ToMB(processMemUsed)); +#endif // HS_BUILD_FOR_WIN32 +} + +void plDispatchLog::LogLongReceive(const char* keyname, const char* className, UInt32 clonePlayerID, plMessage* msg, float ms) +{ + std::string info; + if (DumpSpecificMsgInfo(msg, info)) + fLog->AddLineF("%-30s[%7u](%-20s) took %6.1f ms to receive %s[%s]\n", keyname, clonePlayerID, className, ms, msg->ClassName(), info.c_str()); + else + fLog->AddLineF("%-30s[%7u](%-20s) took %6.1f ms to receive %s\n", keyname, clonePlayerID, className, ms, msg->ClassName()); +} + +void plDispatchLog::DumpMsg(plMessage* msg, int numReceivers, int sendTimeMs, Int32 indent) +{ + if (!msg) + return; + + hsBool found=fIncludeTypes.IsBitSet(msg->ClassIndex()); + if (found && !hsCheckBits(fFlags, plDispatchLogBase::kInclude)) + // it's an exclude list and we found it + return; + if (!found && hsCheckBits(fFlags, plDispatchLogBase::kInclude)) + // it's an include list and we didn't find it + return; + + static hsScalar lastTime=0; + hsScalar curTime = (hsScalar)hsTimer::GetSysSeconds(); + + if (lastTime!=curTime) + { + // add linebreak for new frame + fLog->AddLine("\n"); + } + + float sendTime = hsTimer::FullTicksToMs(hsTimer::GetFullTickCount() - fStartTicks); + + char indentStr[50]; + indent = hsMinimum(indent, sizeof(indentStr)-1); + memset(indentStr, ' ', indent); + indentStr[indent] = '\0'; + + fLog->AddLineF("%sDispatched (%d) %d ms: time=%d CName=%s, sndr=%s, rcvr(%d)=%s, flags=0x%lx, tstamp=%f\n", + indentStr, numReceivers, sendTimeMs, + int(sendTime), msg->ClassName(), msg->fSender?msg->fSender->GetName():"nil", + msg->GetNumReceivers(), msg->GetNumReceivers() && msg->GetReceiver(0) + ? msg->GetReceiver(0)->GetName():"nil", + msg->fBCastFlags, msg->fTimeStamp); + + lastTime=curTime; +} + +void plDispatchLog::AddFilterType(UInt16 hClass) +{ + if (hClass>=plFactory::GetNumClasses()) + return; + + int i; + for( i = 0; i < plFactory::GetNumClasses(); i++ ) + { + if( plFactory::DerivesFrom(hClass, i) ) + AddFilterExactType(i); + } +} + +void plDispatchLog::AddFilterExactType(UInt16 type) +{ + if (type=plFactory::GetNumClasses()) + return; + + int i; + for( i = 0; i < plFactory::GetNumClasses(); i++ ) + { + if( plFactory::DerivesFrom(hClass, i) ) + RemoveFilterExactType(i); + } +} + +void plDispatchLog::RemoveFilterExactType(UInt16 type) +{ + if (typeGetCommand() == pfKIMsg::##type) typeName = #type; + PrintKIType(kHACKChatMsg); // send chat message via pfKIMsg + PrintKIType(kEnterChatMode); // toggle chat mode + PrintKIType(kSetChatFadeDelay); // set the chat delay + PrintKIType(kSetTextChatAdminMode); // set the chat admin mode... not used (see CCR) + PrintKIType(kDisableKIandBB); // disable KI and blackbar (for things like AvaCusta) + PrintKIType(kEnableKIandBB); // re-enable the KI and blackbar + PrintKIType(kYesNoDialog); // display a Yes/No dialog + PrintKIType(kAddPlayerDevice); // add a device player list); such as imager + PrintKIType(kRemovePlayerDevice); // remove a device from player list + PrintKIType(kUpgradeKILevel); // upgrade the KI to higher level + PrintKIType(kDowngradeKILevel); // downgrade KI to next lower level + PrintKIType(kRateIt); // display the "RateIt"(tm) dialog + PrintKIType(kSetPrivateChatChannel); // set the private chat channel (for private rooms) + PrintKIType(kUnsetPrivateChatChannel); // unset private chat channel + PrintKIType(kStartBookAlert); // blink the book image on the blackbar + PrintKIType(kMiniBigKIToggle); // shortcut to toggling the miniKI/bigKI + PrintKIType(kKIPutAway); // shortcut to hiding all of the KI + PrintKIType(kChatAreaPageUp); // shortcut to paging up the chat area + PrintKIType(kChatAreaPageDown); // shortcut to paging down the chat area + PrintKIType(kChatAreaGoToBegin); // shortcut to going to the beginning of the chat area + PrintKIType(kChatAreaGoToEnd); // shortcut to going to the end of the chat area + PrintKIType(kKITakePicture); // shortcut to taking a picture in the KI + PrintKIType(kKICreateJournalNote); // shortcut to creating a journal note in the KI + PrintKIType(kKIToggleFade); // shortcut to toggle fade mode + PrintKIType(kKIToggleFadeEnable); // shortcut to toggling fade enabled + PrintKIType(kKIChatStatusMsg); // display status message in chat window + PrintKIType(kKILocalChatStatusMsg); // display status message in chat window + PrintKIType(kKIUpSizeFont); // bump up the size of the chat area font + PrintKIType(kKIDownSizeFont); // down size the font of the chat area + PrintKIType(kKIOpenYeehsaBook); // open the playerbook if not already open + PrintKIType(kKIOpenKI); // open up in degrees the KI + PrintKIType(kKIShowCCRHelp); // show the CCR help dialog + PrintKIType(kKICreateMarker); // create a marker + PrintKIType(kKICreateMarkerFolder); // create a marker folder in the current Age's journal folder + PrintKIType(kKILocalChatErrorMsg); // display error message in chat window + PrintKIType(kKIPhasedAllOn); // turn on all the phased KI functionality + PrintKIType(kKIPhasedAllOff); // turn off all the phased KI functionality + PrintKIType(kKIOKDialog); // display an OK dialog box (localized) + PrintKIType(kDisableYeeshaBook); // don't allow linking with the Yeesha book (gameplay) + PrintKIType(kEnableYeeshaBook); // re-allow linking with the Yeesha book + PrintKIType(kQuitDialog); // put up quit dialog + PrintKIType(kTempDisableKIandBB); // temp disable KI and blackbar (done by av system) + PrintKIType(kTempEnableKIandBB); // temp re-enable the KI and blackbar (done by av system) + PrintKIType(kDisableEntireYeeshaBook); // disable the entire Yeeshabook); not for gameplay); but prevent linking + PrintKIType(kEnableEntireYeeshaBook); + PrintKIType(kKIOKDialogNoQuit); // display OK dialog in the KI without quiting afterwards + PrintKIType(kGZUpdated); // the GZ game was updated + PrintKIType(kGZInRange); // a GZ marker is in range + PrintKIType(kGZOutRange); // GZ markers are out of range + PrintKIType(kUpgradeKIMarkerLevel); // upgrade the KI Marker level (current 0 and 1) + PrintKIType(kKIShowMiniKI); // force the miniKI up + PrintKIType(kGZFlashUpdate); // flash an update without saving (for animation of GZFill in) + PrintKIType(kNoCommand); + + info = xtl::format("Type: %s Str: %s User: %s(%d) Delay: %f Int: %d", + typeName, + kiMsg->GetString() != "" ? kiMsg->GetString().c_str() : "(nil)", + kiMsg->GetUser() ? kiMsg->GetUser() : "(nil)", + kiMsg->GetPlayerID(), + kiMsg->GetDelay(), + kiMsg->GetIntValue()); + + return true; + } + + plClientMsg* clientMsg = plClientMsg::ConvertNoRef(msg); + if (clientMsg) + { + #define PrintType(type) if (clientMsg->GetClientMsgFlag() == plClientMsg::##type) info = #type; + PrintType(kLoadRoom); + PrintType(kLoadRoomHold); + PrintType(kUnloadRoom); + PrintType(kLoadNextRoom); + PrintType(kInitComplete); + PrintType(kDisableRenderScene); + PrintType(kEnableRenderScene); + PrintType(kQuit); + PrintType(kLoadAgeKeys); + PrintType(kReleaseAgeKeys); + + switch (clientMsg->GetClientMsgFlag()) + { + case plClientMsg::kLoadRoom: + case plClientMsg::kLoadRoomHold: + case plClientMsg::kUnloadRoom: + { + info += " - Pages: "; + + const std::vector& locs = clientMsg->GetRoomLocs(); + for (int i = 0; i < locs.size(); i++) + { + const plLocation& loc = locs[i]; + const plPageInfo* pageInfo = plKeyFinder::Instance().GetLocationInfo(loc); + + if (pageInfo) + info += xtl::format("%s-%s ", pageInfo->GetAge(), pageInfo->GetPage()); + } + } + break; + + case plClientMsg::kLoadAgeKeys: + case plClientMsg::kReleaseAgeKeys: + info += xtl::format(" - Age: %s", clientMsg->GetAgeName()); + break; + } + return true; + } + + plRefMsg* refMsg = plRefMsg::ConvertNoRef(msg); + if (refMsg) + { + const char* typeName = nil; + #define GetType(type) if (refMsg->GetContext() == plRefMsg::##type) typeName = #type; + GetType(kOnCreate); + GetType(kOnDestroy); + GetType(kOnRequest); + GetType(kOnRemove); + GetType(kOnReplace); + xtl::format(info, "Obj: %s RefType: %s", refMsg->GetRef()->GetKeyName(), typeName); + + return true; + } +#endif // PLASMA_EXTERNAL_RELEASE + + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.h new file mode 100644 index 00000000..d54505ad --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfDispatchLog.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pfDispatchLog_inc +#define pfDispatchLog_inc + +#include "../pnDispatch/plDispatchLogBase.h" +#include "hsBitVector.h" + +class plStatusLog; + +class plDispatchLog : public plDispatchLogBase +{ +private: + hsBitVector fIncludeTypes; // include/exclude list + UInt64 fStartTicks; + plStatusLog* fLog; + +public: + plDispatchLog(); + ~plDispatchLog(); + + static void InitInstance(); + + void AddFilterType(UInt16 type); + void AddFilterExactType(UInt16 type); + + void RemoveFilterType(UInt16 type); + void RemoveFilterExactType(UInt16 type); + + void LogStatusBarChange(const char* name, const char* action); + void LogLongReceive(const char* keyname, const char* className, UInt32 clonePlayerID, plMessage* msg, float ms); + + void DumpMsg(plMessage* msg, int numReceivers, int sendTimeMs, Int32 indent); +}; + +#endif // pfDispatchLog_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp new file mode 100644 index 00000000..21a7ce05 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfConsole/pfGameConsoleCommands.cpp @@ -0,0 +1,451 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGameConsoleCommands // +// // +// Set of console commands that are actually meant for implementing // +// gameplay. Simpler than Python, easier to implement for programmers, and // +// far lower level, but should be used with extreme caution. // +// // +// Why? Because unlike normal console commands, which will eventually be // +// disabled in the shipping product (thus no danger of any fan cracking // +// them), these WILL be in the finished product. Debugging hacks and temp // +// workarounds to new features are NOT to be implemented here. // +// // +// Because of their nature, all Game. console commands should be approved // +// by Brice before checkin. // +// // +// Preferred method of adding Game. commands: // +// 1. Implement in normal console command groups (Debug. if nothing // +// else) // +// 2. Get the command approved // +// 3. Once command is tested and approved and deemed both worthy and // +// bug-free, cut-and-paste the command into the Game. group // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifdef PLASMA_EXTERNAL_RELEASE +#define LIMIT_CONSOLE_COMMANDS 1 +#endif + + +#include "pfConsoleCmd.h" +#include "pfConsole.h" + +#include "plPipeline.h" +#include "plgDispatch.h" +#include "../plGImage/plMipmap.h" +#include "../plGImage/plTGAWriter.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../../NucleusLib/inc/hsResMgr.h" +#include "../pfGameGUIMgr/pfGUICtrlGenerator.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plAnimStage.h" +#include "../plAvatar/plAvBrainGeneric.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plMessage/plAvatarMsg.h" +#include "../pnKeyedObject/plFixedKey.h" + + +#define PF_SANITY_CHECK( cond, msg ) { if( !( cond ) ) { PrintString( msg ); return; } } + +//// DO NOT REMOVE!!!! +//// This is here so Microsoft VC won't decide to "optimize" this file out +//// DO NOT REMOVE!!!! +void pfConsoleCmdGroup::DummyJunior( void ) +{ +} +//// DO NOT REMOVE!!!! + + +//// plDoesFileExist ////////////////////////////////////////////////////////// +// Utility function to determine whether the given file exists + +static hsBool plDoesFileExist( const char *path ) +{ + hsUNIXStream stream; + + + if( !stream.Open( path, "rb" ) ) + return false; + + stream.Close(); + return true; +} + +////////////////////////////////////////////////////////////////////////////// +//// Game Group Commands ///////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +PF_CONSOLE_GROUP( Game ) + +#ifndef LIMIT_CONSOLE_COMMANDS +PF_CONSOLE_CMD( Game, TakeScreenshot, "...", "Takes a shot of the current frame and saves it to the given file" ) +{ + hsAssert( pfConsole::GetPipeline() != nil, "Cannot use this command before pipeline initialization" ); + + plMipmap myMipmap; + char fileName[ 512 ]; + UInt32 uniqueNumber; + + + if( numParams > 1 ) + { + PrintString( "Too many parameters to TakeScreenshot" ); + return; + } + else if( numParams == 1 ) + strcpy( fileName, (char *)params[ 0 ] ); + else + { + // Think up a filename + for( uniqueNumber = 1; uniqueNumber < 1000; uniqueNumber++ ) + { + sprintf( fileName, "screen%03d.tga", uniqueNumber ); + if( !plDoesFileExist( fileName ) ) + break; + } + if( uniqueNumber == 1000 ) + { + PrintString( "Out of filenames for TakeScreenshot" ); + return; + } + } + + if( !pfConsole::GetPipeline()->CaptureScreen( &myMipmap ) ) + PrintString( "Error capturing screenshot" ); + else + { + char str[ 512 ]; + + + plTGAWriter::Instance().WriteMipmap( fileName, &myMipmap ); + sprintf( str, "Screenshot written to '%s'.", fileName ); + PrintString( str ); + } +} + +PF_CONSOLE_CMD( Game, LoadDialog, "string dlgName", "Loads the given GUI dialog into memory" ) +{ + plUoid lu( kGameGUIMgr_KEY ); + plKey mgrKey = hsgResMgr::ResMgr()->FindKey( lu ); + if( mgrKey ) + { + pfGameGUIMsg *msg = TRACKED_NEW pfGameGUIMsg( mgrKey, pfGameGUIMsg::kLoadDialog ); + msg->SetString( params[ 0 ] ); + plgDispatch::MsgSend( msg ); + } +} + +PF_CONSOLE_CMD( Game, LoadLocalDialog, "string ageName, string dlgName", "Loads the given GUI dialog into memory" ) +{ + plUoid lu( kGameGUIMgr_KEY ); + plKey mgrKey = hsgResMgr::ResMgr()->FindKey( lu ); + if( mgrKey ) + { + pfGameGUIMsg *msg = TRACKED_NEW pfGameGUIMsg( mgrKey, pfGameGUIMsg::kLoadDialog ); + msg->SetString( params[ 1 ] ); + msg->SetAge( params[ 0 ] ); + plgDispatch::MsgSend( msg ); + } +} + +PF_CONSOLE_CMD( Game, ShowDialog, "string dlgName", "Shows the given GUI dialog" ) +{ + plUoid lu( kGameGUIMgr_KEY ); + plKey mgrKey = hsgResMgr::ResMgr()->FindKey( lu ); + if( mgrKey ) + { + pfGameGUIMsg *msg = TRACKED_NEW pfGameGUIMsg( mgrKey, pfGameGUIMsg::kShowDialog ); + msg->SetString( params[ 0 ] ); + plgDispatch::MsgSend( msg ); + } +} + +PF_CONSOLE_CMD( Game, HideDialog, "string dlgName", "Hides the given GUI dialog" ) +{ + plUoid lu( kGameGUIMgr_KEY ); + plKey mgrKey = hsgResMgr::ResMgr()->FindKey( lu ); + if( mgrKey ) + { + pfGameGUIMsg *msg = TRACKED_NEW pfGameGUIMsg( mgrKey, pfGameGUIMsg::kHideDialog ); + msg->SetString( params[ 0 ] ); + plgDispatch::MsgSend( msg ); + } +} + + + +PF_CONSOLE_CMD( Game, SwitchDialog, "string olddlgName, string newdlgName", "Hide olddlg and show newdlg" ) +{ + plUoid lu( kGameGUIMgr_KEY ); + plKey mgrKey = hsgResMgr::ResMgr()->FindKey( lu ); + if( mgrKey ) + { + pfGameGUIMsg *msg = TRACKED_NEW pfGameGUIMsg( mgrKey, pfGameGUIMsg::kHideDialog ); + msg->SetString( params[ 0 ] ); + plgDispatch::MsgSend( msg ); + + pfGameGUIMsg *msg2 = TRACKED_NEW pfGameGUIMsg( mgrKey, pfGameGUIMsg::kShowDialog ); + msg2->SetString( params[ 1 ] ); + plgDispatch::MsgSend( msg2 ); + } +} + +PF_CONSOLE_SUBGROUP( Game, GUI ) + +#include "../pfGameGUIMgr/pfGUICtrlGenerator.h" + +static hsColorRGBA sDynCtrlColor = hsColorRGBA().Set( 1, 1, 1, 1 ), sDynCtrlTextColor = hsColorRGBA().Set( 0, 0, 0, 1 ); + +PF_CONSOLE_CMD( Game_GUI, SetDynamicCtrlColor, "float bgRed, float bgGreen, float bgBlue, float textRed, float textGreen, float textBlue", "" ) +{ + sDynCtrlColor.Set( params[ 0 ], params[ 1 ], params[ 2 ], 1.f ); + sDynCtrlTextColor.Set( params[ 3 ], params[ 4 ], params[ 5 ], 1.f ); +} + + +PF_CONSOLE_CMD( Game_GUI, CreateRectButton, "string title, float x, float y, float width, float height, string command", "" ) +{ + pfGUICtrlGenerator::Instance().GenerateRectButton( params[ 0 ], params[ 1 ], params[ 2 ], + params[ 3 ], params[ 4 ], + params[ 5 ], + sDynCtrlColor, sDynCtrlTextColor ); +} + +PF_CONSOLE_CMD( Game_GUI, CreateRoundButton, "float x, float y, float radius, string command", "" ) +{ + pfGUICtrlGenerator::Instance().GenerateSphereButton( params[ 0 ], params[ 1 ], params[ 2 ], + params[ 3 ], + sDynCtrlColor ); +} + +PF_CONSOLE_CMD( Game_GUI, CreateDragBar, "float x, float y, float width, float height", "") +{ + pfGUICtrlGenerator::Instance().GenerateDragBar( params[ 0 ], params[ 1 ], params[ 2 ], params[ 3 ], sDynCtrlColor ); +} + +PF_CONSOLE_CMD( Game_GUI, CreateDialog, "string name", "" ) +{ + pfGUICtrlGenerator::Instance().GenerateDialog( params[ 0 ] ); +} + + +#endif + + +//#include "../pfKI/pfKI.h" +#include "../pfMessage/pfKIMsg.h" + +PF_CONSOLE_CMD( Game, EnterChatMode, "", "Enters in-game chat mode" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kEnterChatMode); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIToggleMini, "", "Toggle between mini and big KI" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kMiniBigKIToggle); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIPutAway, "", "Put KI completely away" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKIPutAway); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIChatPageUp, "", "Scroll chat display one page up" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kChatAreaPageUp); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIChatPageDown, "", "Scroll chat display one page down" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kChatAreaPageDown); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIChatToStart, "", "Scroll chat display to top of buffer" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kChatAreaGoToBegin); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIChatToEnd, "", "Scroll chat display to bottom of buffer" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kChatAreaGoToEnd); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KITakePicture, "", "Take picture with KI" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKITakePicture); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KICreateJournal, "", "Create journal note entry" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKICreateJournalNote); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIChatToggleFaded, "", "Toggle fade of chat display" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKIToggleFade); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIChatToggleFadeEnable, "", "Toggle enable of chat fade" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKIToggleFadeEnable); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIUpSizeFont, "", "Up size the KI font (chatarea)" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKIUpSizeFont); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIDownSizeFont, "", "Down size the KI font (chatarea)" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKIDownSizeFont); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIOpenYeeshaBook, "", "Open the player's Yeesha book" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKIOpenYeehsaBook); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIOpenKI, "", "Open the KI a little at a time" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKIOpenKI); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KIHelp, "", "Open the CCR help dialog" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKIShowCCRHelp); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KICreateMarker, "", "Create marker in the working marker folder" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKICreateMarker); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, KICreateMarkerFolder, "", "Create marker folder in current Age's journal folder" ) +{ + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKICreateMarkerFolder); + plgDispatch::MsgSend( msg ); +} + +PF_CONSOLE_CMD( Game, SetChatFadeDelay, "float delayInSecs", "Sets the time in seconds before the chat text disappears" ) +{ +// pfKI::GetInstance()->SetChatFadeDelay( params[ 0 ] ); + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kSetChatFadeDelay); + msg->SetDelay( params[0] ); + plgDispatch::MsgSend( msg ); +} + +#include "../plAvatar/plArmatureMod.h" + +PF_CONSOLE_CMD( Game, LimitAvatarLOD, "int newLOD", "Zero is (always) highest detail; 2 is (currently) lowest." ) +{ + int newLOD = params[0]; + + if(newLOD >= 0 && newLOD <= 2) + plArmatureLODMod::fMinLOD = newLOD; +} + + +PF_CONSOLE_SUBGROUP( Game, Emote) // Game.Emote.Shakefist + +void Emote(const char *emotion, float fadeIn = 2.0, float fadeOut = 2.0) +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + AvatarEmote(avatar, emotion); +} + +PF_CONSOLE_CMD( Game_Emote, Wave, "", "") +{ + Emote("Wave", 4.0, 1.0); +} + +PF_CONSOLE_CMD( Game_Emote, Sneeze, "", "") +{ + Emote("Sneeze"); +} + +PF_CONSOLE_CMD( Game_Emote, Dance, "", "") +{ + Emote("Dance"); +} + +PF_CONSOLE_CMD( Game_Emote, Laugh, "", "") +{ + Emote("Laugh"); +} + +PF_CONSOLE_CMD( Game_Emote, Clap, "", "") +{ + Emote("Clap", 4.0, 3.0); +} + +PF_CONSOLE_CMD( Game_Emote, Talk, "", "") +{ + Emote("Talk"); +} + +PF_CONSOLE_CMD( Game_Emote, Sit, "", "") +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + PushSimpleMultiStage(avatar, "SitDownGround", "SitIdleGround", "SitStandGround", true, true, plAGAnim::kBodyLower, plAvBrainGeneric::kSitOnGround); +} + +#ifndef PLASMA_EXTERNAL_RELEASE +PF_CONSOLE_CMD( Game, SetLocalClientAsAdmin, "bool enable", "Makes chat messages from this client appear as admin messages" ) +{ +// pfKI::GetInstance()->SetTextChatAdminMode( (bool)params[ 0 ] ); + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kSetTextChatAdminMode); + msg->SetFlags( params[0] ? pfKIMsg::kAdminMsg : 0 ); + plgDispatch::MsgSend( msg ); +} +#endif + +#include "../pfConditional/plObjectInBoxConditionalObject.h" + +PF_CONSOLE_CMD( Game, BreakVolumeSensors, "bool break", "reverts to old broken volume sensor logic" ) +{ + bool b = params[ 0 ]; + plVolumeSensorConditionalObject::makeBriceHappyVar = !b; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/Pch.h new file mode 100644 index 00000000..9fffdc0e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/Pch.h @@ -0,0 +1,42 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFCSRSRV_PCH_H +#error "Header $/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFCSRSRV_PCH_H + + +#include "pfCsrSrv.h" + +#pragma warning(push, 0) +#include "pfConsole/pfConsole.h" +#pragma warning(pop) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.cpp new file mode 100644 index 00000000..4c728531 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.cpp @@ -0,0 +1,145 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + +/***************************************************************************** +* +* Local data +* +***/ + +static bool s_running; + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static bool Recv_ExecConsoleCmd ( + SimpleNetConn * , + CsrNet_ExecConsoleCmd * msg +) { + LogMsg(kLogPerf, L"pfCsrSrv: ExecConsoleCmd: %S", msg->cmd); + + pfConsole::RunCommandAsync(msg->cmd); + + return true; +} + +//============================================================================ +static bool OnMsg ( + SimpleNetConn * conn, + SimpleNet_MsgHeader * msg +) { + bool result; + + #define DISPATCH(a) case kCsrNet_##a: result = Recv_##a(conn, (CsrNet_##a *) msg); break + switch (msg->messageId) { + DISPATCH(ExecConsoleCmd); + default: + result = false; + } + #undef DISPATCH + + return result; +} + +//============================================================================ +static void OnError ( + SimpleNetConn * , + ENetError error +) { + LogMsg(kLogPerf, L"pfCsrSrv NetError: %s", NetErrorToString(error)); +} + +//============================================================================ +static bool QueryAccept ( + void * , + unsigned channel, + SimpleNetConn * , + const NetAddress & addr +) { + wchar str[64]; + NetAddressToString(addr, str, arrsize(str), kNetAddressFormatAll); + LogMsg(kLogPerf, L"pfCsrSrv: Accepted connection from %s", str); + return channel == kSimpleNetChannelCsr; +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void CsrSrvInitialize () { + +#ifdef PLASMA_ENABLE_CSR_EXTERNAL + + LogMsg(kLogPerf, L"pfCsrSrv: Initializing"); + + s_running = true; + + SimpleNetInitialize(); + SimpleNetCreateChannel(kSimpleNetChannelCsr, OnMsg, OnError); + SimpleNetStartListening(QueryAccept, nil); + +#endif +} + +//============================================================================ +void CsrSrvShutdown () { + +#ifdef PLASMA_ENABLE_CSR_EXTERNAL + + LogMsg(kLogPerf, L"pfCsrSrv: Shutting down"); + + s_running = false; + + SimpleNetStopListening(); + SimpleNetDestroyChannel(kSimpleNetChannelCsr); + SimpleNetShutdown(); + +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.h new file mode 100644 index 00000000..207e469d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfCsrSrv/pfCsrSrv.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFCSRSRV_PFCSRSRV_H +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFCSRSRV_PFCSRSRV_H + + +#include "pnCsrNet/pnCsrNet.h" + + +/***************************************************************************** +* +* pfCsrSrv +* - Executes remote CSR commands received through pnCsrNet +* +***/ + +void CsrSrvInitialize (); +void CsrSrvShutdown (); + + +#endif // PLASMA20_SOURCES_PLASMA_FEATURELIB_PFCSRSRV_PFCSRSRV_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIButtonMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIButtonMod.cpp new file mode 100644 index 00000000..4689166d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIButtonMod.cpp @@ -0,0 +1,392 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIButtonMod Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIButtonMod.h" +#include "pfGUIDraggableMod.h" +#include "pfGameGUIMgr.h" +#include "pfGUIControlHandlers.h" +#include "pfGUIDialogMod.h" + +#include "../plInputCore/plInputInterface.h" +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plAvatar/plAGModifier.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Control Proc For Managing the Draggable ///////////////////////////////// + +class pfGUIButtonDragProc : public pfGUICtrlProcObject +{ + protected: + + pfGUICtrlProcObject *fOrigProc; + + pfGUIButtonMod *fParent; + pfGUIDraggableMod *fDraggable; + hsBool fReportDrag; + + public: + + pfGUIButtonDragProc( pfGUIButtonMod *parent, pfGUIDraggableMod *draggable, pfGUICtrlProcObject *origProc, hsBool reportDrag ) + { + fParent = parent; + fDraggable = draggable; + fOrigProc = origProc; + fReportDrag = reportDrag; + } + + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + // The draggable was let up, so now we stop dragging, disable the draggable again, and pass + // on the event to our original proc + if( fOrigProc != nil && fParent->IsTriggering() ) + fOrigProc->DoSomething( ctrl ); + if (!fParent->IsButtonDown()) + fParent->StopDragging( false ); + } + + virtual void HandleExtendedEvent( pfGUIControlMod *ctrl, UInt32 event ) + { + if( event == pfGUIDraggableMod::kDragging ) + { + // First test if we're inside our button (if so, we stop dragging) + if( fParent->PointInBounds( fDraggable->GetLastMousePt() ) ) + { + // Cancel the drag + fParent->StopDragging( true ); + return; + } + + if( !fReportDrag ) + return; + } + + if( fOrigProc != nil ) + fOrigProc->HandleExtendedEvent( ctrl, event ); + } + + virtual void UserCallback( UInt32 userValue ) + { + if( fOrigProc != nil ) + fOrigProc->UserCallback( userValue ); + } +}; + + +void pfGUIButtonMod::StopDragging( hsBool cancel ) +{ + fDraggable->StopDragging( cancel ); + fDraggable->SetVisible( false ); + fDraggable->SetHandler( fOrigHandler ); + fOrigHandler = nil; + + if( !fOrigReportedDrag ) + fDraggable->ClearFlag( pfGUIDraggableMod::kReportDragging ); + + // Steal interest back + fDialog->SetControlOfInterest( this ); +} + +void pfGUIButtonMod::StartDragging( void ) +{ + fOrigReportedDrag = fDraggable->HasFlag( pfGUIDraggableMod::kReportDragging ); + fDraggable->SetFlag( pfGUIDraggableMod::kReportDragging ); + + fOrigHandler = fDraggable->GetHandler(); + fDraggable->SetVisible( true ); + fDraggable->SetHandler( TRACKED_NEW pfGUIButtonDragProc( this, fDraggable, fOrigHandler, fOrigReportedDrag ) ); + fDraggable->HandleMouseDown( fOrigMouseDownPt, 0 ); +} + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIButtonMod::pfGUIButtonMod() +{ + fAnimName = nil; + fMouseOverAnimName = nil; + fDraggable = nil; + fOrigHandler = nil; + + fClicking = false; + fTriggering = false; + fNotifyType = kNotifyOnUp; + SetFlag( kWantsInterest ); +} + +pfGUIButtonMod::~pfGUIButtonMod() +{ + delete [] fAnimName; + delete [] fMouseOverAnimName; +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIButtonMod::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIControlMod::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIButtonMod::MsgReceive( plMessage *msg ) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); + if( refMsg != nil && refMsg->fType == kRefDraggable ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fDraggable = pfGUIDraggableMod::ConvertNoRef( refMsg->GetRef() ); + fDraggable->SetVisible( false ); // Disable until we're dragging + } + else + fDraggable = nil; + return true; + } + + return pfGUIControlMod::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIButtonMod::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Read(s, mgr); + + fAnimationKeys.Reset(); + UInt32 i, count = s->ReadSwap32(); + for( i = 0; i < count; i++ ) + fAnimationKeys.Append( mgr->ReadKey( s ) ); + fAnimName = s->ReadSafeString(); + + fMouseOverAnimKeys.Reset(); + count = s->ReadSwap32(); + for( i = 0; i < count; i++ ) + fMouseOverAnimKeys.Append( mgr->ReadKey( s ) ); + fMouseOverAnimName = s->ReadSafeString(); + + fNotifyType = s->ReadSwap32(); + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefDraggable ), plRefFlags::kActiveRef ); +} + +void pfGUIButtonMod::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Write( s, mgr ); + + UInt32 i, count = fAnimationKeys.GetCount(); + s->WriteSwap32( count ); + for( i = 0; i < count; i++ ) + mgr->WriteKey( s, fAnimationKeys[ i ] ); + s->WriteSafeString( fAnimName ); + + count = fMouseOverAnimKeys.GetCount(); + s->WriteSwap32( count ); + for( i = 0; i < count; i++ ) + mgr->WriteKey( s, fMouseOverAnimKeys[ i ] ); + s->WriteSafeString( fMouseOverAnimName ); + + s->WriteSwap32( fNotifyType ); + + mgr->WriteKey( s, fDraggable != nil ? fDraggable->GetKey() : nil ); + +} + +//// UpdateBounds //////////////////////////////////////////////////////////// + +void pfGUIButtonMod::UpdateBounds( hsMatrix44 *invXformMatrix, hsBool force ) +{ + pfGUIControlMod::UpdateBounds( invXformMatrix, force ); + if( fAnimationKeys.GetCount() > 0 || fMouseOverAnimKeys.GetCount() > 0 ) + fBoundsValid = false; +} + +//// HandleMouseDown/Up ////////////////////////////////////////////////////// + +void pfGUIButtonMod::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) +{ + fClicking = true; + if( fAnimationKeys.GetCount() > 0 ) + { + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + msg->SetCmd( plAnimCmdMsg::kContinue ); + msg->SetCmd( plAnimCmdMsg::kSetForewards ); + msg->SetCmd( plAnimCmdMsg::kGoToBegin ); + msg->SetAnimName( fAnimName ); + msg->AddReceivers( fAnimationKeys ); + plgDispatch::MsgSend( msg ); + } + + IPlaySound( kMouseDown ); + + fOrigMouseDownPt = mousePt; + if ( fNotifyType == kNotifyOnDown || fNotifyType == kNotifyOnUpAndDown) + { + fTriggering = true; + DoSomething(); + fTriggering = false; + } +} + +void pfGUIButtonMod::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) +{ + + // make sure that we got the down click first + if ( !fClicking ) + return; + + fClicking = false; + if( fAnimationKeys.GetCount() > 0 ) + { + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + msg->SetCmd( plAnimCmdMsg::kContinue ); + msg->SetCmd( plAnimCmdMsg::kSetBackwards ); + msg->SetCmd( plAnimCmdMsg::kGoToEnd ); + msg->SetAnimName( fAnimName ); + msg->AddReceivers( fAnimationKeys ); + plgDispatch::MsgSend( msg ); + } + + IPlaySound( kMouseUp ); + + // Don't run the command if the mouse is outside our bounds + if( !fBounds.IsInside( &mousePt ) && fNotifyType != kNotifyOnUpAndDown ) + return; + + if ( fNotifyType == kNotifyOnUp || fNotifyType == kNotifyOnUpAndDown) + fTriggering = true; + DoSomething(); + fTriggering = false; +} + +void pfGUIButtonMod::HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ) +{ + if( !fClicking ) + return; + + if( fDraggable == nil ) + return; + + if( !fDraggable->IsVisible() ) + { + // Are we outside ourselves? + if( !PointInBounds( mousePt ) ) + { + // Yes, start dragging + StartDragging(); + + // Hand off our interest to the draggable + fDialog->SetControlOfInterest( fDraggable ); + } + } +} + +void pfGUIButtonMod::SetNotifyType(Int32 kind) +{ + fNotifyType = kind; +} + +Int32 pfGUIButtonMod::GetNotifyType() +{ + return fNotifyType; +} + +hsBool pfGUIButtonMod::IsButtonDown() +{ + return fClicking; +} + +//// SetInteresting ////////////////////////////////////////////////////////// +// Overridden to play mouse over animation when we're interesting + +void pfGUIButtonMod::SetInteresting( hsBool i ) +{ + pfGUIControlMod::SetInteresting( i ); + + if( fMouseOverAnimKeys.GetCount() ) + { + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + msg->SetCmd( plAnimCmdMsg::kContinue ); + msg->SetCmd( fInteresting ? plAnimCmdMsg::kSetForewards : plAnimCmdMsg::kSetBackwards ); + msg->SetAnimName( fMouseOverAnimName ); + msg->AddReceivers( fMouseOverAnimKeys ); + plgDispatch::MsgSend( msg ); + } + + if( i ) + IPlaySound( kMouseOver ); + else + IPlaySound( kMouseOff ); +} + + +void pfGUIButtonMod::SetAnimationKeys( hsTArray &keys, const char *name ) +{ + fAnimationKeys = keys; + delete [] fAnimName; + if( name != nil ) + { + fAnimName = TRACKED_NEW char[ strlen( name ) + 1 ]; + strcpy( fAnimName, name ); + } + else + fAnimName = nil; +} + +void pfGUIButtonMod::SetMouseOverAnimKeys( hsTArray &keys, const char *name ) +{ + fMouseOverAnimKeys = keys; + delete [] fMouseOverAnimName; + if( name != nil ) + { + fMouseOverAnimName = TRACKED_NEW char[ strlen( name ) + 1 ]; + strcpy( fMouseOverAnimName, name ); + } + else + fMouseOverAnimName = nil; +} + + +//// IGetDesiredCursor /////////////////////////////////////////////////////// + +UInt32 pfGUIButtonMod::IGetDesiredCursor( void ) const +{ + if( fHandler == nil ) + return 0; + + if( fClicking ) + return plInputInterface::kCursorClicked; + + return plInputInterface::kCursorPoised; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIButtonMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIButtonMod.h new file mode 100644 index 00000000..e0831787 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIButtonMod.h @@ -0,0 +1,121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIButtonMod Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIButtonMod_h +#define _pfGUIButtonMod_h + +#include "pfGUIControlMod.h" + +class plMessage; +class plPostEffectMod; +class plAGMasterMod; +class pfGUIDraggableMod; + +class pfGUIButtonMod : public pfGUIControlMod +{ + protected: + + hsTArray fAnimationKeys; + char *fAnimName; + + hsTArray fMouseOverAnimKeys; + char *fMouseOverAnimName; + + hsBool fClicking; + hsBool fTriggering; + + hsPoint3 fOrigMouseDownPt; + pfGUIDraggableMod *fDraggable; + pfGUICtrlProcObject *fOrigHandler; + hsBool fOrigReportedDrag; + + + Int32 fNotifyType; + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + virtual UInt32 IGetDesiredCursor( void ) const; // As specified in plInputInterface.h + + public: + + pfGUIButtonMod(); + virtual ~pfGUIButtonMod(); + + CLASSNAME_REGISTER( pfGUIButtonMod ); + GETINTERFACE_ANY( pfGUIButtonMod, pfGUIControlMod ); + + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void SetInteresting( hsBool i ); + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ); + + virtual void UpdateBounds( hsMatrix44 *invXformMatrix = nil, hsBool force = false ); + + virtual void SetNotifyType(Int32 kind); + virtual Int32 GetNotifyType(); + virtual hsBool IsButtonDown(); + virtual hsBool IsTriggering() { return fTriggering; } + enum SoundEvents + { + kMouseDown, + kMouseUp, + kMouseOver, + kMouseOff + }; + + enum + { + kRefDraggable = kRefDerivedStart + }; + + enum NotifyType + { + kNotifyOnUp = 0, + kNotifyOnDown, + kNotifyOnUpAndDown + }; + + void StartDragging( void ); + void StopDragging( hsBool cancel ); + + // Export only + void SetAnimationKeys( hsTArray &keys, const char *name ); + void SetMouseOverAnimKeys( hsTArray &keys, const char *name ); +}; + +#endif // _pfGUIButtonMod_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICheckBoxCtrl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICheckBoxCtrl.cpp new file mode 100644 index 00000000..92abaabc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICheckBoxCtrl.cpp @@ -0,0 +1,206 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUICheckBoxCtrl Definition // +// // +// Almost like buttons, only they keep their stated (pressed/unpressed) // +// when you click them, instead of reverting on mouse up. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUICheckBoxCtrl.h" +#include "pfGameGUIMgr.h" + +#include "../plInputCore/plInputInterface.h" + #include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plAvatar/plAGModifier.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUICheckBoxCtrl::pfGUICheckBoxCtrl() +{ + fAnimName = nil; + SetFlag( kWantsInterest ); + fChecked = false; + fClicking = false; + fPlaySound = true; +} + +pfGUICheckBoxCtrl::~pfGUICheckBoxCtrl() +{ + delete [] fAnimName; +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUICheckBoxCtrl::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIControlMod::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUICheckBoxCtrl::MsgReceive( plMessage *msg ) +{ + return pfGUIControlMod::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUICheckBoxCtrl::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Read(s, mgr); + + fAnimationKeys.Reset(); + UInt32 i, count = s->ReadSwap32(); + for( i = 0; i < count; i++ ) + fAnimationKeys.Append( mgr->ReadKey( s ) ); + + fAnimName = s->ReadSafeString(); + fChecked = s->ReadBool(); +} + +void pfGUICheckBoxCtrl::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Write( s, mgr ); + + UInt32 i, count = fAnimationKeys.GetCount(); + s->WriteSwap32( count ); + for( i = 0; i < count; i++ ) + mgr->WriteKey( s, fAnimationKeys[ i ] ); + + s->WriteSafeString( fAnimName ); + s->WriteBool( fChecked ); +} + +//// UpdateBounds //////////////////////////////////////////////////////////// + +void pfGUICheckBoxCtrl::UpdateBounds( hsMatrix44 *invXformMatrix, hsBool force ) +{ + pfGUIControlMod::UpdateBounds( invXformMatrix, force ); + if( fAnimationKeys.GetCount() > 0 ) + fBoundsValid = false; +} + +//// HandleMouseDown/Up ////////////////////////////////////////////////////// + +void pfGUICheckBoxCtrl::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) +{ + fClicking = true; + if(fPlaySound) + IPlaySound( kMouseDown ); +} + +void pfGUICheckBoxCtrl::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) +{ + if( fClicking ) + { + fClicking = false; + + if(fPlaySound) + IPlaySound( kMouseUp ); + + // Don't run the command if the mouse is outside our bounds + if( fBounds.IsInside( &mousePt ) ) + { + SetChecked( !fChecked ); + DoSomething(); + } + } +} + +//// SetChecked ////////////////////////////////////////////////////////////// + +void pfGUICheckBoxCtrl::SetChecked( hsBool checked, hsBool immediate /*= false*/ ) +{ + fChecked = checked; + if( fAnimationKeys.GetCount() > 0 ) + { + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + if( fChecked ) + { + // Moving to true + if( immediate ) + { + msg->SetCmd( plAnimCmdMsg::kGoToEnd ); + } + else + { + msg->SetCmd( plAnimCmdMsg::kContinue ); + msg->SetCmd( plAnimCmdMsg::kSetForewards ); + msg->SetCmd( plAnimCmdMsg::kGoToBegin ); + } + } + else + { + // Moving to false + if( immediate ) + { + msg->SetCmd( plAnimCmdMsg::kGoToBegin ); + } + else + { + msg->SetCmd( plAnimCmdMsg::kContinue ); + msg->SetCmd( plAnimCmdMsg::kSetBackwards ); + msg->SetCmd( plAnimCmdMsg::kGoToEnd ); + } + } + msg->SetAnimName( fAnimName ); + msg->AddReceivers( fAnimationKeys ); + plgDispatch::MsgSend( msg ); + } +} + +void pfGUICheckBoxCtrl::SetAnimationKeys( hsTArray &keys, const char *name ) +{ + fAnimationKeys = keys; + delete [] fAnimName; + if( name != nil ) + { + fAnimName = TRACKED_NEW char[ strlen( name ) + 1 ]; + strcpy( fAnimName, name ); + } + else + fAnimName = nil; +} + +//// IGetDesiredCursor /////////////////////////////////////////////////////// + +UInt32 pfGUICheckBoxCtrl::IGetDesiredCursor( void ) const +{ + if( fClicking ) + return plInputInterface::kCursorClicked; + + return plInputInterface::kCursorPoised; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICheckBoxCtrl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICheckBoxCtrl.h new file mode 100644 index 00000000..5452265c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICheckBoxCtrl.h @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUICheckBoxCtrl Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUICheckBoxCtrl_h +#define _pfGUICheckBoxCtrl_h + +#include "pfGUIControlMod.h" + +class plMessage; +class plPostEffectMod; +class plAGMasterMod; + +class pfGUICheckBoxCtrl : public pfGUIControlMod +{ + protected: + + hsTArray fAnimationKeys; + char *fAnimName; + hsBool fClicking; + + hsBool fChecked; + hsBool fPlaySound; + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + virtual UInt32 IGetDesiredCursor( void ) const; // As specified in plInputInterface.h + + public: + + pfGUICheckBoxCtrl(); + virtual ~pfGUICheckBoxCtrl(); + + CLASSNAME_REGISTER( pfGUICheckBoxCtrl ); + GETINTERFACE_ANY( pfGUICheckBoxCtrl, pfGUIControlMod ); + + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ); + + virtual void UpdateBounds( hsMatrix44 *invXformMatrix = nil, hsBool force = false ); + + void SetChecked( hsBool checked, hsBool immediate = false ); + hsBool IsChecked( void ) { return fChecked; } + + void DontPlaySounds() { fPlaySound = false; } // should the checkbox play sounds? + + const hsTArray &GetAnimationKeys( void ) const { return fAnimationKeys; } + const char *GetAnimationName( void ) const { return fAnimName; } + + enum SoundEvents + { + kMouseDown, + kMouseUp, + kMouseOver, + kMouseOff + }; + + // Export only + void SetAnimationKeys( hsTArray &keys, const char *name ); +}; + +#endif // _pfGUICheckBoxCtrl_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIClickMapCtrl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIClickMapCtrl.cpp new file mode 100644 index 00000000..98b9c4eb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIClickMapCtrl.cpp @@ -0,0 +1,130 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIClickMapCtrl Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIClickMapCtrl.h" +#include "pfGameGUIMgr.h" +#include "pfGUIDialogMod.h" + +#include "../plInputCore/plInputInterface.h" +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIClickMapCtrl::pfGUIClickMapCtrl() +{ + fTracking = false; + fCustomCursor = -1; +} + +pfGUIClickMapCtrl::~pfGUIClickMapCtrl() +{ +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIClickMapCtrl::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIControlMod::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIClickMapCtrl::MsgReceive( plMessage *msg ) +{ + return pfGUIControlMod::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIClickMapCtrl::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Read(s, mgr); +} + +void pfGUIClickMapCtrl::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Write( s, mgr ); +} + +void pfGUIClickMapCtrl::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) +{ + IScreenToLocalPt( mousePt ); + fLastMousePt = fLastMouseDragPt = mousePt; + fTracking = true; +} + +void pfGUIClickMapCtrl::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) +{ + if( fTracking ) + { + IScreenToLocalPt( mousePt ); + fLastMousePt = fLastMouseUpPt = fLastMouseDragPt = mousePt; + DoSomething(); + fTracking = false; + } +} + +void pfGUIClickMapCtrl::HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ) +{ + if( fTracking ) + { + IScreenToLocalPt( mousePt ); + fLastMousePt = fLastMouseDragPt = mousePt; + if( HasFlag( kReportDragging ) ) + HandleExtendedEvent( kMouseDragged ); + } +} + +void pfGUIClickMapCtrl::HandleMouseHover( hsPoint3 &mousePt, UInt8 modifiers ) +{ + IScreenToLocalPt( mousePt ); + fLastMousePt = mousePt; + + if( HasFlag( kReportHovering ) ) + HandleExtendedEvent( kMouseHovered ); +} + +//// IGetDesiredCursor /////////////////////////////////////////////////////// + +UInt32 pfGUIClickMapCtrl::IGetDesiredCursor( void ) const +{ + if( fCustomCursor != -1 ) + return (UInt32)fCustomCursor; + + return plInputInterface::kCursorPoised; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIClickMapCtrl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIClickMapCtrl.h new file mode 100644 index 00000000..820ee553 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIClickMapCtrl.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIClickMapCtrl Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIClickMapCtrl_h +#define _pfGUIClickMapCtrl_h + +#include "pfGUIControlMod.h" + +class plMessage; + +class pfGUIClickMapCtrl : public pfGUIControlMod +{ + protected: + + hsPoint3 fLastMousePt, fLastMouseUpPt, fLastMouseDragPt; + hsBool fTracking; + Int32 fCustomCursor; + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + virtual UInt32 IGetDesiredCursor( void ) const; // As specified in plInputInterface.h + + public: + + pfGUIClickMapCtrl(); + virtual ~pfGUIClickMapCtrl(); + + CLASSNAME_REGISTER( pfGUIClickMapCtrl ); + GETINTERFACE_ANY( pfGUIClickMapCtrl, pfGUIControlMod ); + + enum OurFlags + { + kReportDragging = kDerivedFlagsStart, + kReportHovering + }; + + // Extended event types + enum ExtendedEvents + { + kMouseDragged, + kMouseHovered + }; + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseHover( hsPoint3 &mousePt, UInt8 modifiers ); + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + const hsPoint3 &GetLastMousePt( void ) const { return fLastMousePt; } + const hsPoint3 &GetLastMouseUpPt( void ) const { return fLastMouseUpPt; } + const hsPoint3 &GetLastMouseDragPt( void ) const { return fLastMouseDragPt; } + + void SetCustomCursor( Int32 cursor = -1 ) { fCustomCursor = cursor; } +}; + +#endif // _pfGUIClickMapCtrl_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlHandlers.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlHandlers.cpp new file mode 100644 index 00000000..755c027d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlHandlers.cpp @@ -0,0 +1,186 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIControl Handler Definitions // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIControlHandlers.h" +#include "pfGUIControlMod.h" +#include "pfGUIDialogMod.h" + +#include "../plMessage/plConsoleMsg.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Writeable Stuff ///////////////////////////////////////////////////////// + +void pfGUICtrlProcWriteableObject::Write( pfGUICtrlProcWriteableObject *obj, hsStream *s ) +{ + if( obj != nil ) + { + s->WriteSwap32( obj->fType ); + obj->IWrite( s ); + } + else + s->WriteSwap32( kNull ); +} + +pfGUICtrlProcWriteableObject *pfGUICtrlProcWriteableObject::Read( hsStream *s ) +{ + pfGUICtrlProcWriteableObject *obj; + + UInt32 type = s->ReadSwap32(); + + switch( type ) + { + case kConsoleCmd: + obj = TRACKED_NEW pfGUIConsoleCmdProc; + break; + + case kPythonScript: + obj = TRACKED_NEW pfGUIPythonScriptProc; + break; + + case kCloseDlg: + obj = TRACKED_NEW pfGUICloseDlgProc; + break; + + case kNull: + return nil; + + default: + hsAssert( false, "Invalid proc type in Read()" ); + return nil; + } + + obj->IRead( s ); + return obj; +} + +////////////////////////////////////////////////////////////////////////////// +//// Predefined Exportables ////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// pfGUIConsoleCmdProc ///////////////////////////////////////////////////// + +pfGUIConsoleCmdProc::pfGUIConsoleCmdProc() : pfGUICtrlProcWriteableObject( kConsoleCmd ) +{ + fCommand = nil; +} + +pfGUIConsoleCmdProc::pfGUIConsoleCmdProc( const char *cmd ) + : pfGUICtrlProcWriteableObject( kConsoleCmd ) +{ + fCommand = nil; + SetCommand( cmd ); +} + +pfGUIConsoleCmdProc::~pfGUIConsoleCmdProc() +{ + delete [] fCommand; +} + +void pfGUIConsoleCmdProc::IRead( hsStream *s ) +{ + int i = s->ReadSwap32(); + if( i > 0 ) + { + fCommand = TRACKED_NEW char[ i + 1 ]; + memset( fCommand, 0, i + 1 ); + s->Read( i, fCommand ); + } + else + fCommand = nil; +} + +void pfGUIConsoleCmdProc::IWrite( hsStream *s ) +{ + if( fCommand != nil ) + { + s->WriteSwap32( strlen( fCommand ) ); + s->Write( strlen( fCommand ), fCommand ); + } + else + s->WriteSwap32( 0 ); +} + +void pfGUIConsoleCmdProc::DoSomething( pfGUIControlMod *ctrl ) +{ + if( fCommand != nil ) + { + plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kExecuteLine, fCommand ); + plgDispatch::MsgSend( cMsg ); + } +} + +void pfGUIConsoleCmdProc::SetCommand( const char *cmd ) +{ + delete [] fCommand; + + if( cmd == nil ) + fCommand = nil; + else + { + fCommand = TRACKED_NEW char[ strlen( cmd ) + 1 ]; + memset( fCommand, 0, strlen( cmd ) + 1 ); + strcpy( fCommand, cmd ); + } +} + +//// pfGUIPythonScriptProc /////////////////////////////////////////////////// + +pfGUIPythonScriptProc::pfGUIPythonScriptProc() : pfGUICtrlProcWriteableObject( kPythonScript ) +{ +} + +pfGUIPythonScriptProc::~pfGUIPythonScriptProc() +{ +} + +void pfGUIPythonScriptProc::IRead( hsStream *s ) +{ +} + +void pfGUIPythonScriptProc::IWrite( hsStream *s ) +{ +} + +void pfGUIPythonScriptProc::DoSomething( pfGUIControlMod *ctrl ) +{ +} + +////////////////////////////////////////////////////////////////////////////// +//// Simple Runtime Ones ///////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +void pfGUICloseDlgProc::DoSomething( pfGUIControlMod *ctrl ) +{ + ctrl->GetOwnerDlg()->Hide(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlHandlers.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlHandlers.h new file mode 100644 index 00000000..3eaa16ec --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlHandlers.h @@ -0,0 +1,180 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIControlHandlers Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIControlHandlers_h +#define _pfGUIControlHandlers_h + +#include "hsStream.h" + +//// pfGUICtrlProcObject Definition ////////////////////////////////////////// +// Any control which "does something" (buttons, edit boxes on Enter/Return, +// etc) uses this in some form. Basically, each control will have an (optional?) +// pointer to an object derived from this class type. The class has a single +// standard, virtual function that gets called on the "do something" event. +// Derive from this class, override the virtual and set the control's handler +// to your object and you're all set. Kinda like windowProcs wrapped in a +// C++ object. +// Note: there are some predefined objects for simple, common events. See +// below. +// Note the second: DoSomething() takes a parameter--namely, a pointer to +// the control that called it. Thus, you can make one object handle +// several controls by just switch()ing on that parameter and save yourself +// some object creation. +// +// UserCallback() is an additional function for use in communicating between +// procs. Basically, if you want another proc to do something (another dialog), +// and want you to get called once it's done, set the callback on the other +// proc/whatever to you and override UserCallback(). +// +// HandleExtendedEvent() is similar to DoSomething(), but is called for extended +// event types, such as value changing (for an edit control while typing) or +// list scrolled. The event parameter is control-type-specific. +// +// Dialogs will use a similar functionality, but with more functions available. + +class pfGUIControlMod; +class pfGUICtrlProcObject +{ + protected: + + UInt32 fRefCnt; + + public: + + pfGUICtrlProcObject() { fRefCnt = 0; } + virtual ~pfGUICtrlProcObject() { ; } + + virtual void DoSomething( pfGUIControlMod *ctrl ) = 0; + + virtual void HandleExtendedEvent( pfGUIControlMod *ctrl, UInt32 event ) { ; } + + virtual void UserCallback( UInt32 userValue ) { ; } + + // ONLY THE GUI SYSTEM SHOULD CALL THESE + void IncRef( void ) { fRefCnt++; } + hsBool DecRef( void ) { fRefCnt--; return ( fRefCnt > 0 ) ? false : true; } +}; + +//// pfGUICtrlProcWriteableObject //////////////////////////////////////////// +// This one is a read/writeable version of the above. Basically, you can just +// call Read/Write() and it'll do all the mini-functionality of the factory +// stuff so you don't have to worry about the derived type at runtime. Do +// NOT derive from this class for your own handlers; this is just for the +// handfull that will get added on export. + +class pfGUICtrlProcWriteableObject : public pfGUICtrlProcObject +{ + protected: + + UInt32 fType; + + virtual void IRead( hsStream *s ) = 0; + virtual void IWrite( hsStream *s ) = 0; + + public: + + enum Types + { + kNull, + kConsoleCmd, + kPythonScript, + kCloseDlg + }; + + pfGUICtrlProcWriteableObject() { fType = kNull; } + pfGUICtrlProcWriteableObject( UInt32 type ) : fType( type ) { ; } + virtual ~pfGUICtrlProcWriteableObject() { ; } + + virtual void DoSomething( pfGUIControlMod *ctrl ) = 0; + + static void Write( pfGUICtrlProcWriteableObject *obj, hsStream *s ); + + static pfGUICtrlProcWriteableObject *Read( hsStream *s ); +}; + +//// pfGUIConsoleCmdProc ///////////////////////////////////////////////////// +// Simply runs the console command specified. Exportable. + +class pfGUIConsoleCmdProc : public pfGUICtrlProcWriteableObject +{ + protected: + + char *fCommand; + + virtual void IRead( hsStream *s ); + virtual void IWrite( hsStream *s ); + + public: + + pfGUIConsoleCmdProc(); + pfGUIConsoleCmdProc( const char *cmd ); + virtual ~pfGUIConsoleCmdProc(); + + virtual void DoSomething( pfGUIControlMod *ctrl ); + + void SetCommand( const char *cmd ); +}; + +//// pfGUIPythonScriptProc /////////////////////////////////////////////////// + +class pfGUIPythonScriptProc : public pfGUICtrlProcWriteableObject +{ + protected: + + virtual void IRead( hsStream *s ); + virtual void IWrite( hsStream *s ); + + public: + + pfGUIPythonScriptProc(); + virtual ~pfGUIPythonScriptProc(); + + virtual void DoSomething( pfGUIControlMod *ctrl ); +}; + +//// Simple Runtime Ones ///////////////////////////////////////////////////// + +class pfGUICloseDlgProc : public pfGUICtrlProcWriteableObject +{ + protected: + + virtual void IRead( hsStream *s ) {} + virtual void IWrite( hsStream *s ) {} + + public: + + pfGUICloseDlgProc() : pfGUICtrlProcWriteableObject( kCloseDlg ) {} + virtual ~pfGUICloseDlgProc() {} + + virtual void DoSomething( pfGUIControlMod *ctrl ); +}; + +#endif // _pfGUIControlHandlers_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlMod.cpp new file mode 100644 index 00000000..0e5dc78d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlMod.cpp @@ -0,0 +1,996 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIControlMod Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIControlMod.h" +#include "pfGameGUIMgr.h" +#include "pfGUIDialogMod.h" +#include "pfGUIControlHandlers.h" +#include "pfGUIDialogHandlers.h" +#include "pfGUIListElement.h" // Includes dropTargetProc + +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plAudioInterface.h" + +#include "../plGImage/plDynamicTextMap.h" +#include "../plSurface/plLayer.h" +#include "../plMessage/plRenderMsg.h" +#include "../pnMessage/plSoundMsg.h" +#include "plPipeline.h" + +#include "../plDrawable/plAccessGeometry.h" +#include "../plDrawable/plAccessSpan.h" +#include "../plDrawable/plAccessVtxSpan.h" + +#include "pfGUIPopUpMenu.h" // For skin, can we move that please? Thank you + +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// pfGUIColorScheme Functions ////////////////////////////////////////////// + +void pfGUIColorScheme::IReset( void ) +{ + fForeColor.Set( 1, 1, 1, 1 ); + fBackColor.Set( 0, 0, 0, 1 ); + fSelForeColor.Set( 1, 1, 1, 1 ); + fSelBackColor.Set( 0, 0, 1, 1 ); + fTransparent = false; + fFontFace = hsStrcpy( "Times New Roman" ); + fFontSize = 10; + fFontFlags = 0; +} + +pfGUIColorScheme::pfGUIColorScheme() +{ + IReset(); +} + +pfGUIColorScheme::~pfGUIColorScheme() +{ + delete [] fFontFace; +} + +pfGUIColorScheme::pfGUIColorScheme( hsColorRGBA &foreColor, hsColorRGBA &backColor ) +{ + IReset(); + fForeColor = foreColor; + fBackColor = backColor; +} + +pfGUIColorScheme::pfGUIColorScheme( const char *face, UInt8 size, UInt8 fontFlags ) +{ + IReset(); + fFontFace = hsStrcpy( face ); + fFontSize = size; + fFontFlags = fontFlags; +} + +void pfGUIColorScheme::SetFontFace( const char *face ) +{ + delete [] fFontFace; + fFontFace = hsStrcpy( face ); +} + +void pfGUIColorScheme::Read( hsStream *s ) +{ + fForeColor.Read( s ); + fBackColor.Read( s ); + fSelForeColor.Read( s ); + fSelBackColor.Read( s ); + s->ReadSwap( &fTransparent ); + + delete [] fFontFace; + fFontFace = s->ReadSafeString(); + s->ReadSwap( &fFontSize ); + s->ReadSwap( &fFontFlags ); +} + +void pfGUIColorScheme::Write( hsStream *s ) +{ + fForeColor.Write( s ); + fBackColor.Write( s ); + fSelForeColor.Write( s ); + fSelBackColor.Write( s ); + s->WriteSwap( fTransparent ); + + s->WriteSafeString( fFontFace ); + s->WriteSwap( fFontSize ); + s->WriteSwap( fFontFlags ); +} + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIControlMod::pfGUIControlMod() +{ + fEnabled = true; + fDialog = nil; + fBoundsValid = false; + fCenterValid = false; + fFocused = false; + fInteresting = false; + fVisible = true; + fHandler = nil; + fTagID = 0; + fDropTargetHdlr = nil; + fDynTextMap = nil; + fProxy = nil; + + fColorScheme = nil; + fSkin = nil; + + fNotifyOnInteresting = false; +} + +pfGUIControlMod::~pfGUIControlMod() +{ + ISetHandler( nil ); + SetDropTargetHdlr( nil ); + SetColorScheme( nil ); +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIControlMod::IEval( double secs, hsScalar del, UInt32 dirty ) +{ +// UpdateBounds(); + return false; +} + +//// GetBounds /////////////////////////////////////////////////////////////// + +const hsBounds3 &pfGUIControlMod::GetBounds( void ) +{ + UpdateBounds(); + return fBounds; +} + +//// SetTransform //////////////////////////////////////////////////////////// +// Override from plModifier so we can update our bounds + +void pfGUIControlMod::SetTransform( const hsMatrix44 &l2w, const hsMatrix44 &w2l ) +{ + fBoundsValid = false; +} + + +//// GetVectorAngle ////////////////////////////////////////////////////////// + +static hsScalar GetVectorAngle( const hsPoint3 &basePt, const hsPoint3 &pointA, const hsPoint3 &pointB ) +{ + hsVector3 vectorA( &pointA, &basePt ), vectorB( &pointB, &basePt ); + + hsScalar dot = vectorA * vectorB; + hsVector3 cross = vectorA % vectorB; + hsScalar crossLen = cross.fZ; + + return atan2( crossLen, dot ); +} + +//// CreateConvexHull //////////////////////////////////////////////////////// +// Algorithm is Graham's scan algorithm: +// R.L. Graham, "An efficient algorithm for determining the convex hull of a finite +// planar set", Info. Proc. Lett. 1, 132-133 (1972). +// Note: THIS WILL DESTROY YOUR INPOINTS ARRAY. + +static hsBool CreateConvexHull( hsPoint3 *inPoints, int &numPoints ) +{ + int i, j, pointA, pointB, pointC; + hsScalar *angles; + + if( numPoints < 3 ) + return false; + + // Step 1: Find a point interior to our hull. Easiest is average of all our input points... + // (plus: set the Zs of all the points to the Z of the first point, since we want to be + // working in 2D) + hsPoint3 avgPoint = inPoints[ 0 ]; + for( i = 1; i < numPoints; i++ ) + { + avgPoint += inPoints[ i ]; + inPoints[ i ].fZ = inPoints[ 0 ].fZ; + } + avgPoint.fX /= numPoints; + avgPoint.fY /= numPoints; + avgPoint.fZ /= numPoints; + + // Step 2: Sort all the in points by the angle to the X axis (vector <1,0>). + // Step A: Calculate all the angles + + angles = TRACKED_NEW hsScalar[ numPoints ]; + hsPoint3 xAxisPoint( avgPoint.fX + 1, avgPoint.fY, avgPoint.fZ ); + for( i = 0; i < numPoints; i++ ) + angles[ i ] = GetVectorAngle( avgPoint, inPoints[ i ], xAxisPoint ); + + // Step B: Bubble sort by the angles + for( i = 0; i < numPoints - 1; i++ ) + { + for( j = i + 1; j < numPoints; j++ ) + { + if( angles[ j ] < angles[ i ] ) + { + hsScalar tempAngle = angles[ j ]; + angles[ j ] = angles[ i ]; + angles[ i ] = tempAngle; + + hsPoint3 tempPt = inPoints[ j ]; + inPoints[ j ] = inPoints[ i ]; + inPoints[ i ] = tempPt; + } + } + } + + // Step 3: Eliminate non-convex points to form the hull + for( pointA = 0, pointB = 1, pointC = 2; pointA < numPoints && numPoints > 3; ) + { + // Two cases of wrap-around... + if( pointC >= numPoints ) + pointC -= numPoints; + else if( pointC < 0 ) + pointC += numPoints; + if( pointB >= numPoints ) + pointB -= numPoints; + else if( pointB < 0 ) + pointB += numPoints; + + // For points A, B, and C, find the interior angle between them + hsScalar angle = GetVectorAngle( inPoints[ pointB ], inPoints[ pointA ], inPoints[ pointC ] ); + + // If the angle is < 180, then it's a good angle and we can advance all our points by 1... + // Note: we have a tolerance so that we don't get points that form edges that are pretty darned close... + const hsScalar tolerance = hsScalarPI / 90.f; + if( angle > tolerance && angle < hsScalarPI - tolerance ) + { + pointA++; + pointB++; + pointC++; + } + else + { + // Angle is > 180 degrees, this is bad. This means our middle point doesn't belong, + // so we need to remove it + for( i = pointB; i < numPoints - 1; i++ ) + inPoints[ i ] = inPoints[ i + 1 ]; + numPoints--; + if( pointC > pointB ) + pointC--; + // There's one case where point B and C could've wrapped around and so deleting that point + // actually moves point A down by 1... + if( pointA > pointB ) + pointA--; + + // Back up the points by 1 if possible (so we can keep checking to make sure we're still convex). + // If not, just increment C up + if( pointA > 0 ) + { + pointA--; + pointB--; + } + else + pointC++; + } + } + + delete [] angles; + + return true; +} + +//// GetObjectPoints ///////////////////////////////////////////////////////// +// Retrieves ALL of the points of a sceneObject's meshes. And I mean ALL of +// 'em... + +static void GetObjectPoints( plSceneObject *so, hsTArray &outPoints ) +{ + const plDrawInterface* di = so->GetDrawInterface(); + if( !di ) + return; + + // The following uses mf's spiffy plAccessGeometry/Spans stuff, which, in + // one word, kicksAss. + hsTArray spans; + plAccessGeometry::Instance()->OpenRO( di, spans ); + + int i; + outPoints.Reset(); + for( i = 0; i < spans.GetCount(); i++ ) + { + plAccessVtxSpan& vtxSrc = spans[ i ].AccessVtx(); + plAccPositionIterator iterSrc( &vtxSrc ); + + for( iterSrc.Begin(); iterSrc.More(); iterSrc.Advance() ) + outPoints.Append( *iterSrc.Position() ); + } + + if (plAccessGeometry::Instance()) + plAccessGeometry::Instance()->Close( spans ); +} + +//// PointsOnSameSide //////////////////////////////////////////////////////// +// Given two ends of a line segment and two points, tells you whether the +// two points are on the same side of the line. Used in PointInTriangle(). + +static hsBool PointsOnSameSide( const hsPoint3 &line1, const hsPoint3 &line2, const hsPoint3 &pointA, const hsPoint3 &pointB ) +{ + hsVector3 baseVec( &line2, &line1 ); + hsVector3 cp1 = hsVector3( &pointA, &line1 ) % baseVec; + hsVector3 cp2 = hsVector3( &pointB, &line1 ) % baseVec; + return ( cp1.fZ * cp2.fZ > 0 ) ? true : false; +} + +//// PointInTriangle ///////////////////////////////////////////////////////// +// Given three points that define a triangle and a fourth point, tells you +// whether the fourth point is inside the triangle. + +static hsBool PointInTriangle( hsPoint3 tri1, hsPoint3 tri2, hsPoint3 tri3, const hsPoint3 &testPoint ) +{ + tri1.fZ = tri2.fZ = tri3.fZ = testPoint.fZ; + if( PointsOnSameSide( tri1, tri2, testPoint, tri3 ) && + PointsOnSameSide( tri2, tri3, testPoint, tri1 ) && + PointsOnSameSide( tri3, tri1, testPoint, tri2 ) ) + return true; + return false; +} + +//// PointInBounds /////////////////////////////////////////////////////////// +// Tells you whether said point is in the control's bounds. + +hsBool pfGUIControlMod::PointInBounds( const hsPoint3 &point ) +{ + UpdateBounds(); + + if( fBounds.GetType() != kBoundsEmpty && fBounds.GetType() != kBoundsUninitialized && fBounds.IsInside( &point ) ) + { + if( fBoundsPoints.GetCount() > 0 ) + { + // We have a more-accurate bounds set, so use it + int i; + + + for( i = 1; i < fBoundsPoints.GetCount() - 1; i++ ) + { + // Test the triangle (0,i,i+1) + if( PointInTriangle( fBoundsPoints[ 0 ], fBoundsPoints[ i ], fBoundsPoints[ i + 1 ], point ) ) + return true; + } + return false; + } + else + return true; + } + return false; +} + +//// CalcInitialBounds /////////////////////////////////////////////////////// +// Called by the dialog once as soon as the dialog adds the control, so that +// initial bounds for the control can be calced. This is used for initing +// any dynmaic text maps, since we want to use the initial bounds to do so +// instead of any currently animated state of the bounds. + +void pfGUIControlMod::CalcInitialBounds( void ) +{ + UpdateBounds( nil, true ); + fInitialBounds = fBounds; +} + +//// UpdateBounds //////////////////////////////////////////////////////////// + +void pfGUIControlMod::UpdateBounds( hsMatrix44 *invXformMatrix, hsBool force ) +{ + hsMatrix44 xformMatrix, projMatrix; + hsPoint3 corners[ 8 ]; + int i; + + + if( ( !fBoundsValid || force ) && fDialog && GetTarget() ) + { + plDrawInterface *DI = IGetTargetDrawInterface( 0 ); + if( DI == nil ) + return; + + if( HasFlag( kBetterHitTesting ) ) + { + hsTArray scrnPoints; + + // Create a list of points to make a 2D convex hull from + GetObjectPoints( GetTarget(), scrnPoints ); + hsMatrix44 l2w = GetTarget()->GetLocalToWorld(); + for( i = 0; i < scrnPoints.GetCount(); i++ ) + { + scrnPoints[ i ] = l2w * scrnPoints[ i ]; + scrnPoints[ i ] = fDialog->WorldToScreenPoint( scrnPoints[ i ] ); + } + + // Now create a convex hull from them, assuming the Zs are all the same + int numPoints = scrnPoints.GetCount(); + if( !CreateConvexHull( scrnPoints.AcquireArray(), numPoints ) ) + return; + + // Copy & store. Also recalc our bounding box just for fun + fBounds.MakeEmpty(); + fBoundsPoints.SetCount( numPoints ); + for( i = 0; i < numPoints; i++ ) + { + fBoundsPoints[ i ] = scrnPoints[ i ]; + fBounds.Union( &fBoundsPoints[ i ] ); + } + } + else + { + fBounds.MakeEmpty(); + + hsBounds3Ext worldBounds = DI->GetLocalBounds(); + hsMatrix44 l2w = GetTarget()->GetLocalToWorld(); + worldBounds.Transform( &l2w ); + + worldBounds.GetCorners( corners ); + for( i = 0; i < 8; i++ ) + { + hsPoint3 scrnPt = fDialog->WorldToScreenPoint( corners[ i ] ); + fBounds.Union( &scrnPt ); + } + } + + // Calc center Z +// if( !fCenterValid ) + { +#if 0 + corners[ 1 ] = GetTarget()->GetLocalToWorld().GetTranslate(); + float w = corners[ 1 ].fX * fXformMatrix.fMap[3][0] + + corners[ 1 ].fY * fXformMatrix.fMap[3][1] + + corners[ 1 ].fZ * fXformMatrix.fMap[3][2] + + 1.f * fXformMatrix.fMap[3][3]; + corners[ 1 ] = fXformMatrix * corners[ 1 ]; + + corners[ 1 ].fX = ( ( corners[ 1 ].fX / corners[ 1 ].fZ ) + 1.f ) / 2.f; + corners[ 1 ].fY = ( ( corners[ 1 ].fY / corners[ 1 ].fZ ) + 1.f ) / 2.f; + fScreenCenter = corners[ 1 ]; + +// fScreenCenter.fZ = w; + + + corners[ 1 ] = GetTarget()->GetLocalToWorld().GetTranslate(); + fDialog->WorldToScreenPoint( corners[ 1 ].fX, corners[ 1 ].fY, corners[ 1 ].fZ, fScreenCenter ); + fCenterValid = true; +#else + corners[ 1 ] = GetTarget()->GetLocalToWorld().GetTranslate(); + fScreenCenter = fDialog->WorldToScreenPoint( corners[ 1 ] ); + corners[ 1 ] = fScreenCenter; + fCenterValid = true; +#endif + } + + fScreenMinZ = fBounds.GetMins().fZ; + + // Manually change the bounds so we know the z ranges from at least -1 to 1, suitable for us testing against for clicks + corners[ 0 ] = fBounds.GetCenter(); + corners[ 0 ].fZ = -1.f; + fBounds.Union( &corners[ 0 ] ); + corners[ 0 ].fZ = 1.f; + fBounds.Union( &corners[ 0 ] ); + + fBoundsValid = true; + } +} + +//// SetObjectCenter ///////////////////////////////////////////////////////// +// Given the x/y coordinates in 0..1 space, recalcs the sceneObject position +// and moves the object to match, retaining the stored fCenterZ coordinate + +void pfGUIControlMod::SetObjectCenter( hsScalar x, hsScalar y ) +{ + hsMatrix44 xformMatrix, l2p, p2l; + hsPoint3 center, corners[ 8 ]; + + + if( x > 1.f ) + x = 1.f; + else if( x < 0.f ) + x = 0.f; + if( y > 1.f ) + y = 1.f; + else if( y < 0.f ) + y = 0.f; + + if( fDialog && GetTarget() ) + { + plCoordinateInterface *CI = IGetTargetCoordinateInterface( 0 ); + if( CI == nil ) + return; + +// if( !fInvXformValid ) +// UpdateBounds(); + + l2p = GetTarget()->GetLocalToWorld(); +hsPoint3 oldPt = l2p.GetTranslate(); + +hsPoint3 oldScrnPt = fDialog->WorldToScreenPoint( oldPt ); +hsPoint3 oldPtRedux; +fDialog->ScreenToWorldPoint( oldScrnPt.fX, oldScrnPt.fY, oldScrnPt.fZ, oldPtRedux ); + + fDialog->ScreenToWorldPoint( x, y, fScreenCenter.fZ, center ); + + l2p.SetTranslate( ¢er ); + l2p.GetInverse( &p2l ); + + GetTarget()->SetTransform( l2p, p2l ); + + fScreenCenter.fX = x; + fScreenCenter.fY = y; + } +} + +void pfGUIControlMod::SetTarget( plSceneObject *object ) +{ + plSingleModifier::SetTarget( object ); + + UpdateBounds(); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +#include "plProfile.h" +plProfile_CreateTimer("Gui", "RenderSetup", GUITime); + +hsBool pfGUIControlMod::MsgReceive( plMessage *msg ) +{ + plRenderMsg* rend = plRenderMsg::ConvertNoRef( msg ); + + if( rend ) + { + plProfile_BeginLap(GUITime, this->GetKey()->GetUoid().GetObjectName()); + // Only need it once + if( ISetUpDynTextMap( rend->Pipeline() ) ) + plgDispatch::Dispatch()->UnRegisterForExactType( plRenderMsg::Index(), GetKey() ); + plProfile_EndLap(GUITime, this->GetKey()->GetUoid().GetObjectName()); + return true; + } + + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); + if( refMsg != nil ) + { + if( refMsg->fType == kRefDynTextMap ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fDynTextMap = plDynamicTextMap::ConvertNoRef( refMsg->GetRef() ); + // Register for a render msg so we can leech the material when we finally + // have a pipeline to work with + plgDispatch::Dispatch()->RegisterForExactType( plRenderMsg::Index(), GetKey() ); + } + else + fDynTextMap = nil; + return true; + } + else if( refMsg->fType == kRefDynTextLayer ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fDynTextLayer = plLayerInterface::ConvertNoRef( refMsg->GetRef() ); + else + fDynTextLayer = nil; + return true; + } + else if( refMsg->fType == kRefProxy ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fProxy = plSceneObject::ConvertNoRef( refMsg->GetRef() ); + else + fProxy = nil; + return true; + } + else if( refMsg->fType == kRefSkin ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fSkin = pfGUISkin::ConvertNoRef( refMsg->GetRef() ); + else + fSkin = nil; + + return true; + } + } + + return plSingleModifier::MsgReceive( msg ); +} + +//// ISetUpDynTextMap //////////////////////////////////////////////////////// +// Given a pointer to a dynamic text map, regurgitates it so it matches our +// screen res and fun stuff like that. Also sets the layer transform to give +// us a 1:1 textel-pixel ratio, which we like. + +hsBool pfGUIControlMod::ISetUpDynTextMap( plPipeline *pipe ) +{ + if( fDynTextMap == nil ) + { + hsAssert( false, "Trying to set up a nil dynamicTextMap in a GUI control" ); + return true; + } + if( fDynTextLayer == nil || fInitialBounds.GetType() == kBoundsUninitialized )//|| fDialog == nil ) + return false; + + UInt32 scrnWidth, scrnHeight; + if( !HasFlag( kScaleTextWithResolution ) ) + { + // Scale so that there is a 1:1 pixel:textel ratio + scrnWidth = pipe->Width(); + scrnHeight = pipe->Height(); + } + else + { + // Scale with the resolution so that we take up the same % of screen space no matter what resolution + // Assume a base "resolution" of 1024xX, where X is such that the ratio "1024/X = scrnWidth/scrnHt" holds + const int kBaseScaleRes = 1024; + const int kBaseScaleHeightRes = 768; + scrnWidth = kBaseScaleRes; + scrnHeight = kBaseScaleHeightRes; + // we are going to just force things to be in 4 by 3 ratio... + // ...cause it seems to work better. +/////// scrnHeight = ( pipe->Height() * kBaseScaleRes ) / pipe->Width(); + } + + const hsBounds3 &bounds = fInitialBounds;//GetBounds(); + UInt16 width = (UInt16)(( bounds.GetMaxs().fX - bounds.GetMins().fX ) * scrnWidth); + UInt16 height = (UInt16)(( bounds.GetMaxs().fY - bounds.GetMins().fY ) * scrnHeight); + + // Allow derived controls to allocate some extra scratch space if desired + // (Do it this way so we can pass in our current calculated dimensions for them to play with) + UInt16 extraW = width, extraH = height; + IGrowDTMDimsToDesiredSize( extraW, extraH ); + extraW -= width; + extraH -= height; + + fDynTextMap->Reset(); + fDynTextMap->Create( width, height, HasFlag( kXparentBgnd ), extraW, extraH ); + + fDynTextMap->SetFont( GetColorScheme()->fFontFace, GetColorScheme()->fFontSize, GetColorScheme()->fFontFlags, + HasFlag( kXparentBgnd ) ? false : true ); + fDynTextMap->SetTextColor( GetColorScheme()->fForeColor, + ( HasFlag( kXparentBgnd ) && GetColorScheme()->fBackColor.a == 0.f ) ? true : false ); + + // Now we gotta set the texture transform on the layer so our texture comes + // out with 1:1 mapping from textel to pixel + plLayer *layer = (plLayer *)fDynTextLayer; + layer->SetTransform( fDynTextMap->GetLayerTransform() ); + + // Let the derived classes do their things + IPostSetUpDynTextMap(); + + // Do our first update + IUpdate(); + + return true; +} + +//// Get/SetColorScheme ////////////////////////////////////////////////////// + +pfGUIColorScheme *pfGUIControlMod::GetColorScheme( void ) const +{ + if( fColorScheme == nil ) + return fDialog->GetColorScheme(); + + return fColorScheme; +} + +void pfGUIControlMod::SetColorScheme( pfGUIColorScheme *newScheme ) +{ + if( fColorScheme != nil ) + { + hsRefCnt_SafeUnRef( fColorScheme ); + fColorScheme = nil; + } + + fColorScheme = newScheme; + if( fColorScheme != nil ) + hsRefCnt_SafeRef( fColorScheme ); +} + +//// SetDynTextMap /////////////////////////////////////////////////////////// +// EXPORT ONLY + +void pfGUIControlMod::SetDynTextMap( plLayerInterface *layer, plDynamicTextMap *dynText ) +{ + hsgResMgr::ResMgr()->AddViaNotify( layer->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, pfGUIControlMod::kRefDynTextLayer ), plRefFlags::kActiveRef ); + hsgResMgr::ResMgr()->AddViaNotify( dynText->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, pfGUIControlMod::kRefDynTextMap ), plRefFlags::kActiveRef ); +} + +//// SetEnabled ////////////////////////////////////////////////////////////// + +void pfGUIControlMod::SetEnabled( hsBool e ) +{ + if( e == fEnabled ) + return; + + fEnabled = e; + IUpdate(); +} + +//// SetFocused ////////////////////////////////////////////////////////////// + +void pfGUIControlMod::SetFocused( hsBool e ) +{ + if( e == fFocused ) + return; + + fFocused = e; + IUpdate(); +} + +//// SetInteresting ////////////////////////////////////////////////////////// + +void pfGUIControlMod::SetInteresting( hsBool i ) +{ + if( i == fInteresting ) + return; + + fInteresting = i; + IUpdate(); + + if ( fNotifyOnInteresting && fDialog && fDialog->GetHandler() ) + fDialog->GetHandler()->OnInterestingEvent(this); + +} + +//// SetVisible ////////////////////////////////////////////////////////////// + +void pfGUIControlMod::SetVisible( hsBool vis ) +{ + if( vis == fVisible ) + return; + + fVisible = vis; + if (fTarget) + { + plEnableMsg *msg = TRACKED_NEW plEnableMsg(); + msg->SetCmd( fVisible ? plEnableMsg::kEnable : plEnableMsg::kDisable ); + msg->SetCmd( plEnableMsg::kDrawable ); + msg->AddReceiver( fTarget->GetKey() ); + plgDispatch::MsgSend( msg ); + } + + if( !fVisible && fFocused ) + fDialog->SetFocus( nil ); +} + +void pfGUIControlMod::Refresh( void ) +{ + IUpdate(); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIControlMod::Read( hsStream *s, hsResMgr *mgr ) +{ + plSingleModifier::Read(s, mgr); + s->ReadSwap( &fTagID ); + fVisible = s->ReadBool(); + + // Read the handler in + ISetHandler( pfGUICtrlProcWriteableObject::Read( s ) ); + + // Read in the dynTextMap if there is one + if( s->ReadBool() ) + { + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefDynTextLayer ), plRefFlags::kActiveRef ); + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefDynTextMap ), plRefFlags::kActiveRef ); + } + else + { + fDynTextLayer = nil; + fDynTextMap = nil; + } + + if( s->ReadBool() ) + { + SetColorScheme( nil ); + fColorScheme = TRACKED_NEW pfGUIColorScheme(); + fColorScheme->Read( s ); + } + + // Read in our sound indices + UInt8 i, count = s->ReadByte(); + if( count == 0 ) + fSoundIndices.Reset(); + else + { + fSoundIndices.SetCountAndZero( count ); + for( i = 0; i < count; i++ ) + fSoundIndices[ i ] = (int)s->ReadSwap32(); + } + + if( HasFlag( kHasProxy ) ) + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefProxy ), plRefFlags::kActiveRef ); + + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefSkin ), plRefFlags::kActiveRef ); +} + +void pfGUIControlMod::Write( hsStream *s, hsResMgr *mgr ) +{ + if( HasFlag( kHasProxy ) && !fProxy ) + ClearFlag( kHasProxy ); + + plSingleModifier::Write( s, mgr ); + s->WriteSwap( fTagID ); + s->WriteBool( fVisible ); + + // Write the handler out (if it's not a writeable, damn you) + pfGUICtrlProcWriteableObject::Write( (pfGUICtrlProcWriteableObject *)fHandler, s ); + + // Write out the dynTextMap + if( fDynTextMap != nil ) + { + s->WriteBool( true ); + mgr->WriteKey( s, fDynTextLayer->GetKey() ); + mgr->WriteKey( s, fDynTextMap->GetKey() ); + } + else + s->WriteBool( false ); + + if( fColorScheme != nil ) + { + s->WriteBool( true ); + fColorScheme->Write( s ); + } + else + s->WriteBool( false ); + + // Write out our sound indices + s->WriteByte( fSoundIndices.GetCount() ); + UInt8 i; + for( i = 0; i < fSoundIndices.GetCount(); i++ ) + s->WriteSwap32( fSoundIndices[ i ] ); + + if( HasFlag( kHasProxy ) ) + mgr->WriteKey( s, fProxy->GetKey() ); + + mgr->WriteKey( s, fSkin ); +} + +//// HandleKeyPress/Event //////////////////////////////////////////////////// + +hsBool pfGUIControlMod::HandleKeyPress( char key, UInt8 modifiers ) +{ + return false; +} + +hsBool pfGUIControlMod::HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers ) +{ + return false; +} + +//// IScreenToLocalPt //////////////////////////////////////////////////////// + +void pfGUIControlMod::IScreenToLocalPt( hsPoint3 &pt ) +{ + const hsBounds3 &bnds = GetBounds(); + + pt.fX -= bnds.GetMins().fX; + pt.fY -= bnds.GetMins().fY; + pt.fX /= bnds.GetMaxs().fX - bnds.GetMins().fX; + pt.fY /= bnds.GetMaxs().fY - bnds.GetMins().fY; +} + +//// ISetHandler ///////////////////////////////////////////////////////////// + +void pfGUIControlMod::ISetHandler( pfGUICtrlProcObject *h, hsBool clearInheritFlag ) +{ + if( fHandler && fHandler->DecRef() ) + delete fHandler; + + fHandler = h; + if( fHandler ) + fHandler->IncRef(); + + if( clearInheritFlag ) + ClearFlag( kInheritProcFromDlg ); +} + +//// DoSomething ///////////////////////////////////////////////////////////// + +void pfGUIControlMod::DoSomething( void ) +{ + if( fEnabled && fHandler != nil ) + fHandler->DoSomething( this ); +} + +//// HandleExtendedEvent ///////////////////////////////////////////////////// + +void pfGUIControlMod::HandleExtendedEvent( UInt32 event ) +{ + if( fEnabled && fHandler != nil ) + fHandler->HandleExtendedEvent( this, event ); +} + +//// SetDropTargetHdlr /////////////////////////////////////////////////////// + +void pfGUIControlMod::SetDropTargetHdlr( pfGUIDropTargetProc *h ) +{ + if( fDropTargetHdlr && fDropTargetHdlr->DecRef() ) + delete fDropTargetHdlr; + + fDropTargetHdlr = h; + if( fDropTargetHdlr ) + fDropTargetHdlr->IncRef(); +} + +//// SetSoundIndex /////////////////////////////////////////////////////////// +// Associates the given GUI event with an index of a sound on the target SO's +// audioInterface. The guiCtrlEvent is specific to each type of control. + +void pfGUIControlMod::SetSoundIndex( UInt8 guiCtrlEvent, int soundIndex ) +{ + if( fSoundIndices.GetCount() < guiCtrlEvent + 1 ) + fSoundIndices.ExpandAndZero( guiCtrlEvent + 1 ); + + fSoundIndices[ guiCtrlEvent ] = soundIndex + 1; // We +1, since 0 means no sound +} + +//// IPlaySound ////////////////////////////////////////////////////////////// +// Sends a sound play message with the soundIndex associated with the given +// event. + +void pfGUIControlMod::IPlaySound( UInt8 guiCtrlEvent, hsBool loop /* = false */ ) +{ + if( guiCtrlEvent >= fSoundIndices.GetCount() || fSoundIndices[ guiCtrlEvent ] == 0 ) + return; + + if( GetTarget() == nil || GetTarget()->GetAudioInterface() == nil ) + return; + + plSoundMsg *msg = TRACKED_NEW plSoundMsg; + msg->fIndex = fSoundIndices[ guiCtrlEvent ] - 1; + msg->SetCmd( plSoundMsg::kGoToTime ); + msg->fTime = 0.f; + msg->SetCmd( plSoundMsg::kPlay ); + if (loop) + { + msg->fLoop = true; + msg->SetCmd( plSoundMsg::kSetLooping ); + } + msg->Send( GetTarget()->GetAudioInterface()->GetKey() ); +} + +void pfGUIControlMod::IStopSound(UInt8 guiCtrlEvent) +{ + if (guiCtrlEvent >= fSoundIndices.GetCount() || fSoundIndices[guiCtrlEvent] == 0) + return; + + if (GetTarget() == nil || GetTarget()->GetAudioInterface() == nil ) + return; + + plSoundMsg *msg = TRACKED_NEW plSoundMsg; + msg->fIndex = fSoundIndices[guiCtrlEvent] - 1; + msg->SetCmd(plSoundMsg::kStop); + msg->Send(GetTarget()->GetAudioInterface()->GetKey()); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlMod.h new file mode 100644 index 00000000..eb8b581d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIControlMod.h @@ -0,0 +1,250 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIControlMod Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIControlMod_h +#define _pfGUIControlMod_h + + +#include "../pnModifier/plSingleModifier.h" +#include "hsBounds.h" +#include "../plMessage/plInputEventMsg.h" +#include "pfGameGUIMgr.h" +#include "hsColorRGBA.h" +#include "hsRefCnt.h" + +class plMessage; +class plPostEffectMod; +class pfGUIDialogMod; +class pfGUICtrlProcObject; +class pfGUIDropTargetProc; +class plDynamicTextMap; +class plLayerInterface; + +//// pfGUIColorScheme //////////////////////////////////////////////////////// +// Tiny helper wrapper for a set of colors used to draw various controls + +class pfGUIColorScheme : public hsRefCnt +{ + public: + hsColorRGBA fForeColor, fBackColor; + hsColorRGBA fSelForeColor, fSelBackColor; + hsBool fTransparent; + + char *fFontFace; + UInt8 fFontSize; + UInt8 fFontFlags; + + enum FontFlags + { + kFontBold = 0x01, + kFontItalic = 0x02, + kFontShadowed = 0x04 + }; + + pfGUIColorScheme(); + ~pfGUIColorScheme(); + pfGUIColorScheme( hsColorRGBA &foreColor, hsColorRGBA &backColor ); + pfGUIColorScheme( const char *face, UInt8 size, UInt8 fontFlags ); + + void SetFontFace( const char *face ); + + void Read( hsStream *s ); + void Write( hsStream *s ); + + hsBool IsBold( void ) { return ( fFontFlags & kFontBold ) ? true : false; } + hsBool IsItalic( void ) { return ( fFontFlags & kFontItalic ) ? true : false; } + hsBool IsShadowed( void ) { return ( fFontFlags & kFontShadowed ) ? true : false; } + + protected: + + void IReset( void ); +}; + +//// Class Def /////////////////////////////////////////////////////////////// + +class pfGUISkin; +class pfGUIControlMod : public plSingleModifier +{ + friend class pfGUIDialogMod; + + protected: + + UInt32 fTagID; + hsBool fEnabled, fFocused, fVisible, fInteresting; + hsBool fNotifyOnInteresting; + pfGUIDialogMod *fDialog; + + hsBounds3 fBounds, fInitialBounds; // Z component is 0-1 + hsScalar fScreenMinZ; // Closest Z coordinate in screen space + hsPoint3 fScreenCenter; + hsBool fBoundsValid, fCenterValid; + hsMatrix44 fXformMatrix; // Only used for doing drag work, etc. + + pfGUICtrlProcObject *fHandler; + pfGUIDropTargetProc *fDropTargetHdlr; + + plDynamicTextMap *fDynTextMap; // Some controls use this; for others, it'll be nil + plLayerInterface *fDynTextLayer; // Juse so we can reset the transform. Sheesh! + + pfGUIColorScheme *fColorScheme; + plSceneObject *fProxy; + + hsTArray fBoundsPoints; // For more accurate bounds tests + + hsTArray fSoundIndices; // Indices of sounds to trigger on the target SO's audible interface + + pfGUISkin *fSkin; + + + hsBool ISetUpDynTextMap( plPipeline *pipe ); + virtual void IPostSetUpDynTextMap( void ) {} + virtual void IGrowDTMDimsToDesiredSize( UInt16 &width, UInt16 &height ) { } + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + void ISetDialog( pfGUIDialogMod *mod ) { fDialog = mod; } + void IScreenToLocalPt( hsPoint3 &pt ); + + virtual void IUpdate( void ) {;} + void ISetHandler( pfGUICtrlProcObject *h, hsBool clearInheritFlag = false ); + + void IPlaySound( UInt8 guiCtrlEvent, hsBool loop = false ); + void IStopSound( UInt8 guiCtrlEvent ); + + virtual UInt32 IGetDesiredCursor( void ) const { return 0; } // As specified in plInputInterface.h + + public: + + pfGUIControlMod(); + virtual ~pfGUIControlMod(); + + CLASSNAME_REGISTER( pfGUIControlMod ); + GETINTERFACE_ANY( pfGUIControlMod, plSingleModifier ); + + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + UInt32 GetTagID( void ) { return fTagID; } + + virtual void SetEnabled( hsBool e ); + virtual hsBool IsEnabled( void ) { return fEnabled; } + virtual void SetFocused( hsBool e ); + virtual hsBool IsFocused( void ) { return fFocused; } + virtual void SetVisible( hsBool vis ); + virtual hsBool IsVisible( void ) { return fVisible; } + + virtual void SetInteresting( hsBool i ); + hsBool IsInteresting( void ) { return fInteresting; } + + virtual void SetNotifyOnInteresting( hsBool state ) { fNotifyOnInteresting = state; } + + pfGUIDialogMod *GetOwnerDlg( void ) { return fDialog; } + + virtual void Refresh( void ); + + virtual void UpdateBounds( hsMatrix44 *invXformMatrix = nil, hsBool force = false ); + void SetObjectCenter( hsScalar x, hsScalar y ); + virtual hsPoint3 GetObjectCenter() { return fScreenCenter; } + hsScalar GetScreenMinZ( void ) { return fScreenMinZ; } + void CalcInitialBounds( void ); + + const hsBounds3 &GetBounds( void ); + hsBool PointInBounds( const hsPoint3 &point ); + + virtual void SetTarget( plSceneObject *object ); + + // Return false if you actually DON'T want the mouse clicked at this point (should only be used for non-rectangular region rejection) + virtual hsBool FilterMousePosition( hsPoint3 &mousePt ) { return true; } + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) {;} + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) {;} + virtual void HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ) {;} + virtual void HandleMouseHover( hsPoint3 &mousePt, UInt8 modifiers ) {;} + virtual void HandleMouseDblClick( hsPoint3 &mousePt, UInt8 modifiers ) {;} + + virtual hsBool HandleKeyPress( char key, UInt8 modifiers ); + virtual hsBool HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers ); + + void SetHandler( pfGUICtrlProcObject *h ) { ISetHandler( h, true ); } + void DoSomething( void ); // Will call the handler + void HandleExtendedEvent( UInt32 event ); // Will call the handler + + pfGUICtrlProcObject *GetHandler( void ) const { return fHandler; } + + void SetDropTargetHdlr( pfGUIDropTargetProc *drop ); + pfGUIDropTargetProc *GetDropTargetHdlr( void ) { return fDropTargetHdlr; } + + enum + { + kRefDynTextMap, + kRefDynTextLayer, + kRefProxy, + kRefSkin, + kRefDerivedStart = 32 + }; + + enum Flags // plSingleModifier already has SetFlag()/HasFlag() + { + kWantsInterest, + kInheritProcFromDlg, + kIntangible, // I.E. it doesn't exists on the screen/can't be clicked on. + // Used for group objects like the up/down pair + kXparentBgnd, + kScaleTextWithResolution, // I.E. take up the same space on screen no matter the resolution + kTakesSpecialKeys, // I.E. disable bindings for keys like backspace because we want them + kHasProxy, + kBetterHitTesting, + kDerivedFlagsStart = 32 + }; + + virtual void SetColorScheme( pfGUIColorScheme *newScheme ); + pfGUIColorScheme *GetColorScheme( void ) const; + + // should be override by specific GUIcontrol + virtual void PurgeDynaTextMapImage() {;} + + // Override from plModifier so we can update our bounds + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + // Forces an immediate play of the given GUI control event sound + void PlaySound( UInt8 guiCtrlEvent, hsBool loop = false ) { IPlaySound( guiCtrlEvent, loop ); } + void StopSound( UInt8 guiCtrlEvent ) { IStopSound( guiCtrlEvent ); } + + // Export only + void SetTagID( UInt32 id ) { fTagID = id; } + void SetDynTextMap( plLayerInterface *layer, plDynamicTextMap *dynText ); + void SetSoundIndex( UInt8 guiCtrlEvent, int soundIndex ); +}; + +#endif // _pfGUIControlMod_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICtrlGenerator.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICtrlGenerator.cpp new file mode 100644 index 00000000..b48bfb4b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICtrlGenerator.cpp @@ -0,0 +1,508 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUICtrlGenerator Definitions // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUICtrlGenerator.h" +#include "pfGameGUIMgr.h" +#include "pfGUIControlMod.h" +#include "pfGUIDialogMod.h" +#include "pfGUIButtonMod.h" +#include "pfGUIDragBarCtrl.h" +#include "pfGUIControlHandlers.h" +#include "pfGUIMenuItem.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayer.h" +#include "../plGImage/plMipmap.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../plPipeline/plTextGenerator.h" +#include "../plScene/plPostEffectMod.h" +#include "../plScene/plSceneNode.h" +#include "../pnMessage/plClientMsg.h" +#include "../plMessage/plLayRefMsg.h" +#include "../pnMessage/plAttachMsg.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUICtrlGenerator::pfGUICtrlGenerator() +{ + strcpy( fFontFace, "Arial" ); + fFontSize = 18; +} + +pfGUICtrlGenerator::~pfGUICtrlGenerator() +{ + Shutdown(); +} + +void pfGUICtrlGenerator::Shutdown( void ) +{ + int i; + + + // Destroy our scene nodes and dialogs + for( i = 0; i < fDynDlgNodes.GetCount(); i++ ) + { + pfGameGUIMgr::GetInstance()->UnloadDialog( fDynDialogs[ i ] ); + fDynDlgNodes[ i ]->GetKey()->UnRefObject(); + } + fDynDlgNodes.Reset(); + fDynDialogs.Reset(); + + for( i = 0; i < fTextGens.GetCount(); i++ ) + delete fTextGens[ i ]; + fTextGens.Reset(); + +} + +//// Instance //////////////////////////////////////////////////////////////// + +pfGUICtrlGenerator &pfGUICtrlGenerator::Instance( void ) +{ + static pfGUICtrlGenerator myInstance; + + return myInstance; +} + +//// IGetNextKeyName ///////////////////////////////////////////////////////// + +void pfGUICtrlGenerator::IGetNextKeyName( char *name, const char *prefix ) +{ + static UInt32 keyCount = 0; + + + sprintf( name, "%s%d", prefix, keyCount++ ); +} + +//// IAddKey ///////////////////////////////////////////////////////////////// + +plKey pfGUICtrlGenerator::IAddKey( hsKeyedObject *ko, const char *prefix ) +{ + char keyName[ 128 ]; + + + IGetNextKeyName( keyName, prefix ); + return hsgResMgr::ResMgr()->NewKey( keyName, ko, plLocation::kGlobalFixedLoc ); +} + +//// SetFont ///////////////////////////////////////////////////////////////// + +void pfGUICtrlGenerator::SetFont( const char *face, UInt16 size ) +{ + strcpy( fFontFace, face ); + fFontSize = size; +} + +//// ICreateSolidMaterial //////////////////////////////////////////////////// +// Creates a material with no texture, just color. + +hsGMaterial *pfGUICtrlGenerator::ICreateSolidMaterial( hsColorRGBA &color ) +{ + hsColorRGBA black; + + + // Create a material with a simple blank layer, fully ambient + hsGMaterial *material = TRACKED_NEW hsGMaterial; + IAddKey( material, "GUIMaterial" ); + + plLayer *lay = material->MakeBaseLayer(); + black.Set( 0.f,0.f,0.f,1.f ); + + lay->SetRuntimeColor( black ); + lay->SetPreshadeColor( black ); + lay->SetAmbientColor( color ); + + return material; +} + +//// ICreateTextMaterial ///////////////////////////////////////////////////// +// Creates a material with a texture that has a string centered on it. + +hsGMaterial *pfGUICtrlGenerator::ICreateTextMaterial( const char *text, hsColorRGBA &bgColor, + hsColorRGBA &textColor, float objWidth, float objHeight ) +{ + UInt16 pixWidth, pixHeight, strWidth, strHeight; + hsColorRGBA black, white; + + + // Guess at some pixel width and heights we want. We're guessing b/c we want it to look reasonably + // good on the screen, but we don't know exactly how big is big, so we guess + pixWidth = (UInt16)(objWidth * 64.f); + pixHeight = (UInt16)(objHeight * 64.f); + + // Create blank mipmap + plMipmap *bitmap = TRACKED_NEW plMipmap( 1, 1, plMipmap::kRGB32Config, 1 ); + IAddKey( bitmap, "GUIMipmap" ); + + // Create textGen to write string with + plTextGenerator *textGen = TRACKED_NEW plTextGenerator( bitmap, pixWidth, pixHeight ); + textGen->SetFont( fFontFace, (UInt16)fFontSize ); + textGen->ClearToColor( bgColor ); + textGen->SetTextColor( textColor ); + strWidth = textGen->CalcStringWidth( text, &strHeight ); + textGen->DrawString( ( pixWidth - strWidth ) >> 1, ( pixHeight - strHeight ) >> 1, text ); + textGen->FlushToHost(); + fTextGens.Append( textGen ); + + // Create a material with a simple blank layer, fully ambient + hsGMaterial *material = TRACKED_NEW hsGMaterial; + IAddKey( material, "GUIMaterial" ); + + plLayer *lay = material->MakeBaseLayer(); + white.Set( 1.f,1.f,1.f,1.f ); + black.Set( 0.f,0.f,0.f,1.f ); + + lay->SetRuntimeColor( black ); + lay->SetPreshadeColor( black ); + lay->SetAmbientColor( white ); + + hsgResMgr::ResMgr()->AddViaNotify( bitmap->GetKey(), TRACKED_NEW plLayRefMsg( lay->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture ), plRefFlags::kActiveRef ); +// lay->SetTexture( bitmap ); + lay->SetTransform( textGen->GetLayerTransform() ); + + return material; +} + +//// GenerateDialog ////////////////////////////////////////////////////////// + +void pfGUICtrlGenerator::GenerateDialog( const char *name ) +{ + IGenerateDialog( name, 20.f, false ); +} + +//// IGenSceneObject ///////////////////////////////////////////////////////// + +plSceneObject *pfGUICtrlGenerator::IGenSceneObject( pfGUIDialogMod *dlg, plDrawable *myDraw, plSceneObject *parent, + hsMatrix44 *l2w, hsMatrix44 *w2l ) +{ + plKey snKey = ( dlg != nil ) ? ( dlg->GetTarget() != nil ? dlg->GetTarget()->GetSceneNode() : nil ) : nil; + if( snKey == nil ) + snKey = fDynDlgNodes.Peek()->GetKey(); + + hsgResMgr::ResMgr()->SendRef( myDraw->GetKey(), TRACKED_NEW plNodeRefMsg( snKey, plRefMsg::kOnCreate, 0, plNodeRefMsg::kDrawable ), plRefFlags::kActiveRef ); + + plDrawInterface *newDI = TRACKED_NEW plDrawInterface; + IAddKey( newDI, "GUIDrawIFace" ); + + plSceneObject *newObj = TRACKED_NEW plSceneObject; + IAddKey( newObj, "GUISceneObject" ); + + plCoordinateInterface *newCI = TRACKED_NEW plCoordinateInterface; + IAddKey( newCI, "GUICoordIFace" ); + + hsgResMgr::ResMgr()->SendRef( newCI->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface ), plRefFlags::kActiveRef ); + hsgResMgr::ResMgr()->SendRef( newDI->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface ), plRefFlags::kActiveRef ); + hsgResMgr::ResMgr()->SendRef( myDraw->GetKey(), TRACKED_NEW plIntRefMsg( newDI->GetKey(), plRefMsg::kOnCreate, 0, plIntRefMsg::kDrawable ), plRefFlags::kActiveRef ); + + if( parent == nil ) + { + parent = ( fDynDragBars.GetCount() > 0 ) ? fDynDragBars.Peek() : nil; + if( parent == nil ) + parent = dlg->GetTarget(); + } + + if( parent != nil ) +// hsgResMgr::ResMgr()->SendRef( newCI->GetKey(), TRACKED_NEW plIntRefMsg( parent->GetKey(), plRefMsg::kOnCreate, 0, plIntRefMsg::kChild ), plRefFlags::kActiveRef ); + hsgResMgr::ResMgr()->SendRef( newCI->GetKey(), TRACKED_NEW plAttachMsg( parent->GetKey(), nil, plRefMsg::kOnRequest ), plRefFlags::kActiveRef ); + + newObj->SetSceneNode( snKey ); + + if( l2w != nil ) + { + newObj->SetTransform( *l2w, *w2l ); +// newCI->SetLocalToParent( *l2w, *w2l ); +// myDraw->SetTransform( -1, *l2w, *w2l ); + } + + return newObj; +} + +//// GenerateRectButton ////////////////////////////////////////////////////// + +pfGUIButtonMod *pfGUICtrlGenerator::GenerateRectButton( const char *title, float x, float y, float width, float height, + const char *consoleCmd, hsColorRGBA &color, hsColorRGBA &textColor ) +{ + hsGMaterial *material; + hsMatrix44 l2w, w2l; + hsVector3 vec; + pfGUIDialogMod *dlgToAddTo = IGetDialog(); + + + // Get us a material + material = ICreateTextMaterial( title, color, textColor, width * 20.f, height * 20.f ); + + pfGUIButtonMod *but = CreateRectButton( dlgToAddTo, title, x, y, width, height, material ); + if( but != nil ) + but->SetHandler( TRACKED_NEW pfGUIConsoleCmdProc( consoleCmd ) ); + + return but; +} + +//// CreateRectButton //////////////////////////////////////////////////////// + +pfGUIButtonMod *pfGUICtrlGenerator::CreateRectButton( pfGUIDialogMod *parent, const char *title, float x, float y, float width, float height, + hsGMaterial *material, hsBool asMenuItem ) +{ + wchar_t *wTitle = hsStringToWString(title); + pfGUIButtonMod *retVal = CreateRectButton(parent,wTitle,x,y,width,height,material,asMenuItem); + delete [] wTitle; + return retVal; +} + +pfGUIButtonMod *pfGUICtrlGenerator::CreateRectButton( pfGUIDialogMod *parent, const wchar_t *title, float x, float y, float width, float height, + hsGMaterial *material, hsBool asMenuItem ) +{ + plDrawableSpans *myDraw; + hsMatrix44 l2w, w2l; + hsVector3 vec; + + + // Translate x and y from (0:1) to (-10:10) + x = ( x - 0.5f ) * 20.f; + y = ( y - 0.5f ) * 20.f; + // Translate width and height from (0:1) to (-10:10) + width *= 20.f; + height *= 20.f; + + // Create drawable that is rectangular + l2w.Reset(); + hsPoint3 corner( x, -y, -100 ); + hsVector3 xVec( width, 0, 0 ), yVec( 0, height, 0 ), zVec( 0, 0, 0.1f ); + + myDraw = plDrawableGenerator::GeneratePlanarDrawable( corner, xVec, yVec, material, l2w ); + + plSceneObject *newObj = IGenSceneObject( parent, myDraw ); + + pfGUIButtonMod *newBtn = asMenuItem ? TRACKED_NEW pfGUIMenuItem : TRACKED_NEW pfGUIButtonMod; + IAddKey( newBtn, "GUIButton" ); + hsgResMgr::ResMgr()->SendRef( newBtn->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); + parent->AddControl( newBtn ); + hsgResMgr::ResMgr()->AddViaNotify( newBtn->GetKey(), TRACKED_NEW plGenRefMsg( parent->GetKey(), plRefMsg::kOnCreate, parent->GetNumControls() - 1, pfGUIDialogMod::kControlRef ), plRefFlags::kActiveRef ); + + return newBtn; +} + +//// GenerateSphereButton //////////////////////////////////////////////////// + +pfGUIButtonMod *pfGUICtrlGenerator::GenerateSphereButton( float x, float y, float radius, + const char *consoleCmd, hsColorRGBA &color ) +{ + hsGMaterial *material; + plDrawableSpans *myDraw; + hsMatrix44 l2w, w2l; + hsVector3 vec; + hsPoint3 pt( x, -y, -100.f ); + pfGUIDialogMod *dlgToAddTo = IGetDialog(); + + + // Translate x and y from (0:1) to (-10:10) + x = ( x - 0.5f ) * 20.f; + y = ( y - 0.5f ) * 20.f; + // Translate width and height from (0:1) to (-10:10) + radius *= 20.f; + + // Get us a material + material = ICreateSolidMaterial( color ); + + // Create drawable that is rectangular + l2w.Reset(); + // We bump up the quality since we're actually far closer to these things then the normal + // world camera would put us + myDraw = plDrawableGenerator::GenerateSphericalDrawable( pt, radius, material, l2w, + false, nil, nil, nil, 100.f ); + + vec.Set( x, -y, 0 ); + l2w.MakeTranslateMat( &vec ); + l2w.GetInverse( &w2l ); + + plSceneObject *newObj = IGenSceneObject( dlgToAddTo, myDraw );//, nil, &l2w, &w2l ); + + pfGUIButtonMod *newBtn = TRACKED_NEW pfGUIButtonMod; + IAddKey( newBtn, "GUIButton" ); + newBtn->SetHandler( TRACKED_NEW pfGUIConsoleCmdProc( consoleCmd ) ); + hsgResMgr::ResMgr()->AddViaNotify( newBtn->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); + dlgToAddTo->AddControl( newBtn ); + + return newBtn; +} + +//// GenerateDragBar ////////////////////////////////////////////////////// + +pfGUIDragBarCtrl *pfGUICtrlGenerator::GenerateDragBar( float x, float y, float width, float height, hsColorRGBA &color ) +{ + hsGMaterial *material; + plDrawableSpans *myDraw; + hsMatrix44 l2w, w2l; + hsVector3 vec; + pfGUIDialogMod *dlgToAddTo = IGetDialog(); + + + // Translate x and y from (0:1) to (-10:10) + x = ( x - 0.5f ) * 20.f; + y = ( y - 0.5f ) * 20.f; + // Translate width and height from (0:1) to (-10:10) + width *= 20.f; + height *= 20.f; + + // Get us a material + material = ICreateSolidMaterial( color ); + + // Create drawable that is rectangular + l2w.Reset(); + + hsPoint3 corner( x, -y, -100 );//x - width / 2.f, -y - height / 2.f, -100 ); + hsVector3 xVec( width, 0, 0 ), yVec( 0, height, 0 ), zVec( 0, 0, 0.1f ); + + myDraw = plDrawableGenerator::GenerateBoxDrawable( corner, xVec, yVec, zVec,/*width, height, 0.01f, */material, l2w ); + + // Drag bars are special--everything else gets attached to them and they get attached to the dialog + vec.Set( x, -y, -100 ); + l2w.MakeTranslateMat( &vec ); + l2w.GetInverse( &w2l ); + + plSceneObject *newObj = IGenSceneObject( dlgToAddTo, myDraw, dlgToAddTo->GetTarget(), &l2w, &w2l ); + + fDynDragBars[ fDynDragBars.GetCount() - 1 ] = newObj; + + pfGUIDragBarCtrl *newBtn = TRACKED_NEW pfGUIDragBarCtrl; + IAddKey( newBtn, "GUIDragBar" ); + hsgResMgr::ResMgr()->AddViaNotify( newBtn->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); + dlgToAddTo->AddControl( newBtn ); + +/* vec.Set( -x, y, 100 ); + l2w.MakeTranslateMat( &vec ); + l2w.GetInverse( &w2l ); + + plCoordinateInterface *ci = (plCoordinateInterface *)dlgToAddTo->GetTarget()->GetCoordinateInterface(); + ci->SetLocalToParent( l2w, w2l ); +*/ + return newBtn; +} + +//// IGetDialog ////////////////////////////////////////////////////////////// + +pfGUIDialogMod *pfGUICtrlGenerator::IGetDialog( void ) +{ + if( fDynDialogs.GetCount() == 0 ) + IGenerateDialog( "GUIBaseDynamicDlg", 20.f ); + + hsAssert( fDynDialogs.GetCount() > 0, "Unable to get a dynamic dialog to add buttons to" ); + return fDynDialogs.Peek(); +} + +//// IGenerateDialog ///////////////////////////////////////////////////////// + +pfGUIDialogMod *pfGUICtrlGenerator::IGenerateDialog( const char *name, float scrnWidth, hsBool show ) +{ + float fovX, fovY; + plSceneNode *node; + pfGUIDialogMod *dialog; + + + // Create the rendermod + plPostEffectMod *renderMod = TRACKED_NEW plPostEffectMod; + IAddKey( renderMod, "GUIRenderMod" ); + + renderMod->SetHither( 0.5f ); + renderMod->SetYon( 200.f ); + + // fovX should be such that scrnWidth is the projected width at z=100 + fovX = atan( scrnWidth / ( 2.f * 100.f ) ) * 2.f; + fovY = fovX;// * 3.f / 4.f; + + renderMod->SetFovX( fovX * 180.f / hsScalarPI ); + renderMod->SetFovY( fovY * 180.f / hsScalarPI ); + + // Create the sceneNode to go with it + node = TRACKED_NEW plSceneNode; + IAddKey( node, "GUISceneNode" ); + node->GetKey()->RefObject(); + fDynDlgNodes.Append( node ); + fDynDragBars.Append( nil ); + + hsgResMgr::ResMgr()->AddViaNotify( node->GetKey(), TRACKED_NEW plGenRefMsg( renderMod->GetKey(), plRefMsg::kOnCreate, 0, plPostEffectMod::kNodeRef ), plRefFlags::kPassiveRef ); + + // Create the dialog + dialog = TRACKED_NEW pfGUIDialogMod; + IAddKey( dialog, "GUIDialog" ); + + dialog->SetRenderMod( renderMod ); + dialog->SetName( name ); + + // Create the dummy scene object to hold the dialog + plSceneObject *newObj = TRACKED_NEW plSceneObject; + IAddKey( newObj, "GUISceneObject" ); + + // *#&$(*@&#$ need a coordIface... + plCoordinateInterface *newCI = TRACKED_NEW plCoordinateInterface; + IAddKey( newCI, "GUICoordIFace" ); + + hsMatrix44 l2w, w2l; + l2w.Reset(); +// l2w.NotIdentity(); + + l2w.GetInverse( &w2l ); + + // Using SendRef here because AddViaNotify will queue the messages up, which doesn't do us any good + // if we need these refs right away + hsgResMgr::ResMgr()->SendRef( dialog->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); + + hsgResMgr::ResMgr()->AddViaNotify( newCI->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface ), plRefFlags::kActiveRef ); + hsgResMgr::ResMgr()->AddViaNotify( renderMod->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); + + // Add the dialog to the GUI mgr + plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( pfGameGUIMgr::GetInstance()->GetKey(), + plRefMsg::kOnCreate, 0, pfGameGUIMgr::kDlgModRef ); + hsgResMgr::ResMgr()->AddViaNotify( dialog->GetKey(), refMsg, plRefFlags::kActiveRef ); + + newObj->SetSceneNode( node->GetKey() ); + + newObj->SetTransform( l2w, w2l ); +// newCI->SetLocalToParent( l2w, w2l ); + + if( show ) + pfGameGUIMgr::GetInstance()->ShowDialog( dialog ); + + fDynDialogs.Append( dialog ); + return dialog; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICtrlGenerator.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICtrlGenerator.h new file mode 100644 index 00000000..c946367a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUICtrlGenerator.h @@ -0,0 +1,113 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUICtrlGenerator Header // +// Generates really primitive GUI controls (and dialogs) at runtime. // +// Useful for, well, generating really primitive GUI controls and dialogs // +// at runtime... +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUICtrlGenerator_h +#define _pfGUICtrlGenerator_h + +#include "hsStream.h" +#include "hsTemplates.h" + + +//// pfGUICtrlGenerator Definition /////////////////////////////////////////// + +class pfGUIDialogMod; +class pfGUIButtonMod; +class pfGUIDragBarCtrl; +class hsGMaterial; +struct hsColorRGBA; +class plSceneNode; +class hsKeyedObject; +class plKey; +class plTextGenerator; +class plSceneObject; +class plDrawable; +struct hsMatrix44; + +class pfGUICtrlGenerator +{ + protected: + + char fFontFace[ 256 ]; + UInt32 fFontSize; + + hsTArray fTextGens; + + hsTArray fDynDlgNodes; + hsTArray fDynDialogs; + hsTArray fDynDragBars; + + + plKey IAddKey( hsKeyedObject *ko, const char *prefix ); + void IGetNextKeyName( char *name, const char *prefix ); + + hsGMaterial *ICreateSolidMaterial( hsColorRGBA &color ); + + hsGMaterial *ICreateTextMaterial( const char *text, hsColorRGBA &bgColor, + hsColorRGBA &textColor, float objWidth, float objHeight ); + + pfGUIDialogMod *IGetDialog( void ); + pfGUIDialogMod *IGenerateDialog( const char *name, float scrnWidth, hsBool show = true ); + + plSceneObject *IGenSceneObject( pfGUIDialogMod *dlg, plDrawable *myDraw, plSceneObject *parent = nil, hsMatrix44 *l2w = nil, hsMatrix44 *w2l = nil ); + + public: + + pfGUICtrlGenerator(); + ~pfGUICtrlGenerator(); + + void Shutdown( void ); + + void SetFont( const char *face, UInt16 size ); + + + pfGUIButtonMod *GenerateRectButton( const char *title, float x, float y, float width, float height, + const char *consoleCmd, hsColorRGBA &color, hsColorRGBA &textColor ); + + pfGUIButtonMod *GenerateSphereButton( float x, float y, float radius, + const char *consoleCmd, hsColorRGBA &color ); + + pfGUIDragBarCtrl *GenerateDragBar( float x, float y, float width, float height, hsColorRGBA &color ); + + void GenerateDialog( const char *name ); + + + pfGUIButtonMod *CreateRectButton( pfGUIDialogMod *parent, const char *title, float x, float y, + float width, float height, hsGMaterial *material, hsBool asMenuItem = false ); + pfGUIButtonMod *CreateRectButton( pfGUIDialogMod *parent, const wchar_t *title, float x, float y, + float width, float height, hsGMaterial *material, hsBool asMenuItem = false ); + + static pfGUICtrlGenerator &Instance( void ); +}; + +#endif // _pfGUICtrlGenerator_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogHandlers.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogHandlers.h new file mode 100644 index 00000000..e151f6b5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogHandlers.h @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIDialogHandlers Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIDialogHandlers_h +#define _pfGUIDialogHandlers_h + +#include "hsStream.h" +#include "pfGUIControlHandlers.h" + +//// pfGUIDialogProc Definition ////////////////////////////////////////////// +// This works very much like the control proc objects. The idea is, if you +// want to do some custom work on a dialog (and who doesn't?), you create an +// object derived from this type, override the functions, and do as you +// please. The class type also derives from the control proc type, meaning +// that you can implement DoSomething() as well and use the same object for +// both your dialog and your control procs. (DoSomething() is overloaded here +// so that it's no longer pure virtual, so you can use it for only handling +// dialogs if you prefer). + +class pfGUIDialogMod; +class pfGUIDialogProc : public pfGUICtrlProcObject +{ + protected: + + pfGUIDialogMod *fDialog; + + public: + + pfGUIDialogProc() { } + virtual ~pfGUIDialogProc() { ; } + + // Called by the mgr--don't call yourself! + void SetDialog( pfGUIDialogMod *dlg ) { fDialog = dlg; } + + // Enums for OnControlEvent + enum ControlEvt + { + kExitMode + }; + + //////// FUNCTIONS TO OVERLOAD //////// + + // Overloaded here so you don't have to unless you want to. Overload + // it if you want to use this for a control handler as well. + virtual void DoSomething( pfGUIControlMod *ctrl ) {;} + + // Called on dialog init (i.e. first showing, before OnShow() is called), only ever called once + virtual void OnInit( void ) { ; } + + // Called before the dialog is shown, always after OnInit() + virtual void OnShow( void ) { ; } + + // Called before the dialog is hidden + virtual void OnHide( void ) { ; } + + // Called on the dialog's destructor, before it's unregistered with the game GUI manager + virtual void OnDestroy( void ) { ; } + + // Called when the dialog's focused control changes + virtual void OnCtrlFocusChange( pfGUIControlMod *oldCtrl, pfGUIControlMod *newCtrl ) { ; } + + // Called when the key bound to a GUI event is pressed. Only called on the top modal dialog + virtual void OnControlEvent( ControlEvt event ) { ; } + + // Called when the GUI changes interesting state + virtual void OnInterestingEvent( pfGUIControlMod *ctrl ) { ; } +}; + +#endif // _pfGUIDialogHandlers_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogMod.cpp new file mode 100644 index 00000000..b9aedb91 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogMod.cpp @@ -0,0 +1,832 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIDialogMod Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGameGUIMgr.h" +#include "pfGUIDialogMod.h" +#include "pfGUIControlMod.h" +#include "pfGUIDialogHandlers.h" +#include "pfGUIDialogNotifyProc.h" +#include "pfGUIListElement.h" +#include "../plScene/plPostEffectMod.h" + +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plScene/plSceneNode.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +#include "../plStatusLog/plStatusLog.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "plViewTransform.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIDialogMod::pfGUIDialogMod() : fRenderMod( nil ), fNext( nil ), fPrevPtr( nil ) +{ + memset( fName, 0, sizeof( fName ) ); + fEnabled = false; + fControlOfInterest = nil; + fFocusCtrl = nil; + fMousedCtrl = nil; + fTagID = 0; + fHandler = nil; + fVersion = 0; + + fDragMode = false; + fDragReceptive = false; + fDragTarget = nil; + fProcReceiver = nil; + + fColorScheme = TRACKED_NEW pfGUIColorScheme(); +} + +pfGUIDialogMod::~pfGUIDialogMod() +{ + // Call the handler's destroy if there is one + if( fHandler ) + fHandler->OnDestroy(); + + // Unregister us with the Game GUI manager + plUoid lu( kGameGUIMgr_KEY ); + plKey mgrKey = hsgResMgr::ResMgr()->FindKey( lu ); + if( mgrKey ) + { + plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( mgrKey, plRefMsg::kOnRemove, 0, pfGameGUIMgr::kDlgModRef ); + refMsg->SetRef( this ); + plgDispatch::MsgSend( refMsg ); + } + + SetHandler( nil ); + + hsRefCnt_SafeUnRef( fColorScheme ); + fColorScheme = nil; +} + +//// ScreenToWorldPoint ////////////////////////////////////////////////////// +// Sometimes it just sucks not having access to the pipeline at just the +// right time. + +void pfGUIDialogMod::ScreenToWorldPoint( hsScalar x, hsScalar y, hsScalar z, hsPoint3 &outPt ) +{ + plViewTransform view = fRenderMod->GetViewTransform(); + view.SetScreenSize( 1, 1 ); + + outPt = view.ScreenToWorld( hsPoint3( x, y, z ) ); +} + +//// WorldToScreenPoint ////////////////////////////////////////////////////// +// Given a point in world-space, translates it into screen coordinates +// (with range 0-1, origin top-left). + +hsPoint3 pfGUIDialogMod::WorldToScreenPoint( const hsPoint3 &inPt ) +{ + plViewTransform view = fRenderMod->GetViewTransform(); + view.SetScreenSize( 1, 1 ); + + hsPoint3 tempPt = view.WorldToScreen( inPt ); + tempPt.fZ = view.WorldToCamera( inPt ).fZ; + return tempPt; +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIDialogMod::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return false; +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIDialogMod::MsgReceive( plMessage *msg ) +{ + plGenRefMsg *ref = plGenRefMsg::ConvertNoRef( msg ); + if( ref ) + { + switch( ref->fType ) + { + case kRenderModRef: + if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fRenderMod = plPostEffectMod::ConvertNoRef( ref->GetRef() ); + fRenderMod->EnableLightsOnRenderRequest(); + + if( fEnabled ) + { + plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg( GetKey(), fRenderMod->GetKey(), nil ); + animMsg->SetCmd( plAnimCmdMsg::kContinue ); + plgDispatch::MsgSend( animMsg ); + } + } + else if( ref->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) ) + { + plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg( GetKey(), fRenderMod->GetKey(), nil ); + animMsg->SetCmd( plAnimCmdMsg::kStop ); + plgDispatch::MsgSend( animMsg ); + + fRenderMod = nil; + } + break; + + case kControlRef: + if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + if( ref->fWhich >= fControls.GetCount() ) + { + hsAssert( false, "Bad index in reffing a control on a GUI dialog" ); + } + else + { + pfGUIControlMod *oldCtrl = fControls[ ref->fWhich ]; + + fControls[ ref->fWhich ] = pfGUIControlMod::ConvertNoRef( ref->GetRef() ); + fControls[ ref->fWhich ]->ISetDialog( this ); + if( oldCtrl != fControls[ ref->fWhich ] ) + // They're equal on export time, when we DON'T want to be updating the bounds + fControls[ ref->fWhich ]->CalcInitialBounds(); + + if( fControls[ ref->fWhich ]->HasFlag( pfGUIControlMod::kInheritProcFromDlg ) ) + fControls[ ref->fWhich ]->ISetHandler( fHandler ); + } + } + + else if( ref->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) ) + { + if( ref->fWhich >= fControls.GetCount() ) + { + hsAssert( false, "Bad index in unreffing a control on a GUI dialog." ); + } + else + { + if( fControls[ ref->fWhich ] != nil ) + fControls[ ref->fWhich ]->ISetDialog( nil ); + fControls[ ref->fWhich ] = nil; + } + } + break; + } + return true; + } + + return plSingleModifier::MsgReceive( msg ); +} + +//// AddControl ////////////////////////////////////////////////////////////// + +void pfGUIDialogMod::AddControl( pfGUIControlMod *ctrl ) +{ + fControls.Append( ctrl ); + ctrl->ISetDialog( this ); + ctrl->CalcInitialBounds(); +} + +//// AddControlOnExport ////////////////////////////////////////////////////// + +void pfGUIDialogMod::AddControlOnExport( pfGUIControlMod *ctrl ) +{ + fControls.Append( ctrl ); + hsgResMgr::ResMgr()->AddViaNotify( ctrl->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, fControls.GetCount() - 1, pfGUIDialogMod::kControlRef ), plRefFlags::kActiveRef ); +} + +//// SetEnabled ////////////////////////////////////////////////////////////// + +void pfGUIDialogMod::SetEnabled( hsBool e ) +{ + if( e == fEnabled ) + return; + + fEnabled = e; + + if( fHandler != nil ) + { + if( fEnabled ) + fHandler->OnShow(); + else + fHandler->OnHide(); + } + + if ( !fEnabled ) + { + // if we are being hidden then there should be no controls that have interest + fControlOfInterest = nil; + // also we can purge the dynaText images on the controls + int i; + for( i = 0; i < fControls.GetCount(); i++ ) + { + if( fControls[ i ] == nil ) + continue; + fControls[ i ]->PurgeDynaTextMapImage(); + } + } + + if( fRenderMod != nil ) + { + plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg( GetKey(), fRenderMod->GetKey(), nil ); + if( fEnabled ) + { + animMsg->SetCmd( plAnimCmdMsg::kContinue ); + + // Update the bounds on all controls that we own + UpdateAllBounds(); + } + else + animMsg->SetCmd( plAnimCmdMsg::kStop ); + plgDispatch::MsgSend( animMsg ); + } + +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIDialogMod::Read( hsStream *s, hsResMgr *mgr ) +{ + plSingleModifier::Read(s, mgr); + + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRenderModRef ), plRefFlags::kActiveRef ); + + s->Read( sizeof( fName ), fName ); + + UInt32 i, count = s->ReadSwap32(); + fControls.SetCountAndZero( count ); + for( i = 0; i < count; i++ ) + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kControlRef ), plRefFlags::kActiveRef ); + + // Register us with the Game GUI manager + plUoid lu( kGameGUIMgr_KEY ); + plKey mgrKey = hsgResMgr::ResMgr()->FindKey( lu ); + if( mgrKey ) + { + plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( mgrKey, plRefMsg::kOnCreate, 0, pfGameGUIMgr::kDlgModRef ); + hsgResMgr::ResMgr()->AddViaNotify( GetKey(), refMsg, plRefFlags::kPassiveRef ); + } + + s->ReadSwap( &fTagID ); + + fProcReceiver = mgr->ReadKey( s ); + if( fProcReceiver != nil ) + SetHandler( TRACKED_NEW pfGUIDialogNotifyProc( fProcReceiver ) ); + + s->ReadSwap( &fVersion ); + + fColorScheme->Read( s ); + + fSceneNodeKey = mgr->ReadKey( s ); +} + +void pfGUIDialogMod::Write( hsStream *s, hsResMgr *mgr ) +{ + UInt32 i; + + + plSingleModifier::Write( s, mgr ); + + mgr->WriteKey( s, fRenderMod->GetKey() ); + s->Write( sizeof( fName ), fName ); + + s->WriteSwap32( fControls.GetCount() ); + for( i = 0; i < fControls.GetCount(); i++ ) + mgr->WriteKey( s, fControls[ i ]->GetKey() ); + + s->WriteSwap( fTagID ); + + mgr->WriteKey( s, fProcReceiver ); + + s->WriteSwap( fVersion ); + + fColorScheme->Write( s ); + + mgr->WriteKey( s, fSceneNodeKey ); +} + +plKey pfGUIDialogMod::GetSceneNodeKey( void ) +{ + if( fSceneNodeKey != nil ) + return fSceneNodeKey; + + // Attempt to grab it + if( GetTarget() != nil && GetTarget()->GetSceneNode() != nil ) + return ( fSceneNodeKey = GetTarget()->GetSceneNode() ); + + return nil; +} + +//// UpdateInterestingThings ///////////////////////////////////////////////// +// Really. We go through and make sure every control marked as interesting +// still has the mouse inside it and vice versa. + +void pfGUIDialogMod::UpdateInterestingThings( hsScalar mouseX, hsScalar mouseY, UInt8 modifiers, hsBool modalPreset ) +{ + int i; + hsPoint3 mousePoint; + + + mousePoint.Set( mouseX, mouseY, 0.f ); + + for( i = 0; i < fControls.GetCount(); i++ ) + { + if( fControls[ i ] == nil ) + continue; + + // if there was a modal present and we are not modal, then everything is unInteresting! + if ( modalPreset && !HasFlag(pfGUIDialogMod::kModal) ) + { + if( fControls[ i ]->IsInteresting() ) + fControls[ i ]->SetInteresting( false ); + } + else + { + if( !fControls[ i ]->HasFlag( pfGUIControlMod::kIntangible ) && fControls[ i ]->PointInBounds( mousePoint ) || fControls[ i ] == fControlOfInterest ) + { + if( !fControls[ i ]->IsInteresting() ) + fControls[ i ]->SetInteresting( true ); + } + else + { + if( fControls[ i ]->IsInteresting() ) + fControls[ i ]->SetInteresting( false ); + } + } + } +} + +//// HandleMouseEvent //////////////////////////////////////////////////////// + +#ifdef HS_DEBUGGING // Debugging bounds rects +#include "../plPipeline/plDebugText.h" +#endif + +hsBool pfGUIDialogMod::HandleMouseEvent( pfGameGUIMgr::EventType event, hsScalar mouseX, hsScalar mouseY, + UInt8 modifiers ) +{ + hsPoint3 mousePoint; + UInt32 i; + + pfGUIControlMod *oldInterestingCtrl = nil; + hsScalar smallestZ; + +#ifdef HS_DEBUGGING // Debugging bounds rects +static bool showBounds = false; + + if( showBounds ) + { + UInt32 sW, sH; + plDebugText::Instance().GetScreenSize(&sW,&sH); + for( i = 0; i < fControls.GetCount(); i++ ) + { + if( fControls[ i ] == nil ) + continue; + if( fControls[ i ]->HasFlag( pfGUIControlMod::kIntangible ) ) + continue; + + if( fControls[ i ]->fBoundsPoints.GetCount() > 0 ) + { + const hsBounds3 &bnds = fControls[ i ]->GetBounds(); + plDebugText::Instance().Draw3DBorder( (UInt16)(sW * bnds.GetMins().fX), + (UInt16)(sH * bnds.GetMins().fY), + (UInt16)(sW * bnds.GetMaxs().fX), + (UInt16)(sH * bnds.GetMaxs().fY), 0x3000ffff, 0x3000ffff ); + + UInt32 color = 0xffff0000; + for( int j = 0; j < fControls[ i ]->fBoundsPoints.GetCount(); j++ ) + { +// color = 0xff000000 | ( ( j * 16 ) << 16 ); + float x = sW * fControls[ i ]->fBoundsPoints[ j ].fX; + float y = sH * fControls[ i ]->fBoundsPoints[ j ].fY; + plDebugText::Instance().DrawRect( (UInt16)(x - 2), (UInt16)(y - 2), (UInt16)(x + 2), (UInt16)(y + 2), color ); + char str[ 16 ]; + itoa( j, str, 10 ); + plDebugText::Instance().DrawString( (UInt16)(x + 8), (UInt16)(y - 8), str, color ); + } + } + else + { + const hsBounds3 &bnds = fControls[ i ]->GetBounds(); + plDebugText::Instance().Draw3DBorder( (UInt16)(sW * bnds.GetMins().fX), + (UInt16)(sH * bnds.GetMins().fY), + (UInt16)(sW * bnds.GetMaxs().fX), + (UInt16)(sH * bnds.GetMaxs().fY), 0x300000ff, 0x300000ff ); + } + } + } +#endif + + mousePoint.Set( mouseX, mouseY, 0.f ); + + if( fDragMode ) + { + IHandleDrag( mousePoint, event, modifiers ); + return true; // We ALWAYS handle events if we're in drag mode + } + + oldInterestingCtrl = fMousedCtrl; + if( fControlOfInterest != nil ) + { + // A particular control already has interest--pass messages directly to it no matter what + fMousedCtrl = fControlOfInterest; + } + else + { + for( i = 0, fMousedCtrl = nil, smallestZ = 1.e30f; i < fControls.GetCount(); i++ ) + { + if( fControls[ i ] != nil && !fControls[ i ]->HasFlag( pfGUIControlMod::kIntangible ) && fControls[ i ]->PointInBounds( mousePoint ) && fControls[ i ]->IsVisible() && fControls[ i ]->IsEnabled() ) + { + if( fControls[ i ]->GetScreenMinZ() < smallestZ ) + { + if( fControls[ i ]->FilterMousePosition( mousePoint ) ) + { + fMousedCtrl = fControls[ i ]; + smallestZ = fControls[ i ]->GetScreenMinZ(); + } + } + } + } + } + + if( fMousedCtrl != nil ) + { +#ifdef HS_DEBUGGING // Debugging bounds rects +if( showBounds ) +{ + const hsBounds3 &bnds = fMousedCtrl->GetBounds(); + plDebugText::Instance().DrawString( (UInt16)(bnds.GetMins().fX), (UInt16)(bnds.GetMins().fY), fMousedCtrl->GetKeyName(), (UInt32)0xffffff00 ); +} +#endif + + if( event == pfGameGUIMgr::kMouseDown ) + { + if( fMousedCtrl->HasFlag( pfGUIControlMod::kWantsInterest ) ) + fControlOfInterest = fMousedCtrl; + + fMousedCtrl->HandleMouseDown( mousePoint, modifiers ); + + // Clicking on a control (mouse down) also sets focus to that control. Unlike + // control-of-interest, this does NOT get reset until a new control is clicked on + if( fFocusCtrl != fMousedCtrl ) + { + if( fHandler != nil ) + fHandler->OnCtrlFocusChange( fFocusCtrl, fMousedCtrl ); + + if( fFocusCtrl != nil ) + fFocusCtrl->SetFocused( false ); + fFocusCtrl = fMousedCtrl; + fFocusCtrl->SetFocused( true ); + } + } + else if( event == pfGameGUIMgr::kMouseUp ) + { + fMousedCtrl->HandleMouseUp( mousePoint, modifiers ); + + // Controls lose interest on mouse up + fControlOfInterest = nil; + } + else if( event == pfGameGUIMgr::kMouseMove ) + fMousedCtrl->HandleMouseHover( mousePoint, modifiers ); + else if( event == pfGameGUIMgr::kMouseDrag ) + fMousedCtrl->HandleMouseDrag( mousePoint, modifiers ); + else if( event == pfGameGUIMgr::kMouseDblClick ) + fMousedCtrl->HandleMouseDblClick( mousePoint, modifiers ); + + return true; + } + // Clicked on nobody, make sure we lose focus on any controls + if( fFocusCtrl != nil && event == pfGameGUIMgr::kMouseDown ) + { + if( fHandler != nil ) + fHandler->OnCtrlFocusChange( fFocusCtrl, nil ); + + if( fFocusCtrl != nil ) // The handler call could've changed it + fFocusCtrl->SetFocused( false ); + fFocusCtrl = nil; + } + + return false; +} + +//// HandleKeyEvent ////////////////////////////////////////////////////////// + +hsBool pfGUIDialogMod::HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers ) +{ + // Only process if a control has focus... + if( fFocusCtrl != nil ) + { + // And guess what, it's up to that control to process it! Gee, how easy... + return fFocusCtrl->HandleKeyEvent( event, key, modifiers ); + } + return false; +} + +//// HandleKeyPress ////////////////////////////////////////////////////////// + +hsBool pfGUIDialogMod::HandleKeyPress( char key, UInt8 modifiers ) +{ + // Same deal as HandleKeyPress. Only problem is, we needed the msg to translate + // to a char, so it had to be done up at the mgr level (sadly) + // Only process if a control has focus... + + if( fFocusCtrl != nil ) + { + return fFocusCtrl->HandleKeyPress( key, modifiers ); + } + + return false; +} + +//// SetFocus //////////////////////////////////////////////////////////////// + +void pfGUIDialogMod::SetFocus( pfGUIControlMod *ctrl ) +{ + if( ctrl != nil && ctrl->fDialog != this ) + { + if( fHandler != nil ) + fHandler->OnCtrlFocusChange( fFocusCtrl, nil ); + + if( fFocusCtrl != nil ) + fFocusCtrl->SetFocused( false ); + fFocusCtrl = nil; + + ctrl->fDialog->SetFocus( ctrl ); + } + else if( ctrl != fFocusCtrl ) + { + if( fFocusCtrl != nil ) + fFocusCtrl->SetFocused( false ); + + if( fHandler != nil ) + fHandler->OnCtrlFocusChange( fFocusCtrl, ctrl ); + + fFocusCtrl = ctrl; + if( fFocusCtrl != nil ) + fFocusCtrl->SetFocused( true ); + } +} + +//// Show/Hide /////////////////////////////////////////////////////////////// + +void pfGUIDialogMod::Show( void ) +{ + pfGameGUIMgr::GetInstance()->ShowDialog( this ); +} + +void pfGUIDialogMod::ShowNoReset( void ) +{ + pfGameGUIMgr::GetInstance()->ShowDialog( this, false ); +} + +void pfGUIDialogMod::Hide( void ) +{ + pfGameGUIMgr::GetInstance()->HideDialog( this ); +} + +//// GetControlFromTag /////////////////////////////////////////////////////// + +pfGUIControlMod *pfGUIDialogMod::GetControlFromTag( UInt32 tagID ) +{ + int i; + + int ctrlCount = fControls.GetCount(); + + for( i = 0; i < ctrlCount; i++ ) + { + pfGUIControlMod *ctrl = fControls[i]; + if( ctrl && ctrl->GetTagID() == tagID ) + return fControls[ i ]; + } + + return nil; +} + +//// SetControlOfInterest //////////////////////////////////////////////////// + +void pfGUIDialogMod::SetControlOfInterest( pfGUIControlMod *c ) +{ + fControlOfInterest = c; +} + +//// SetHandler ////////////////////////////////////////////////////////////// + +void pfGUIDialogMod::SetHandler( pfGUIDialogProc *hdlr ) +{ + int i; + + + if( fHandler && fHandler->DecRef() ) + delete fHandler; + + fHandler = hdlr; + if( fHandler != nil ) + { + fHandler->IncRef(); + fHandler->SetDialog( this ); + } + + // We also set the handler for any controls that are flagged to inherit + // from the parent dialog. Note that SetHandlerForAll() can thus be + // seen as a function that forces this flag (temporarily) on all controls + for( i = 0; i < fControls.GetCount(); i++ ) + { + // Test for nil controls since we get this also on destruct + if( fControls[ i ] != nil && fControls[ i ]->HasFlag( pfGUIControlMod::kInheritProcFromDlg ) ) + fControls[ i ]->ISetHandler( hdlr ); + } +} + +//// SetHandlerForAll //////////////////////////////////////////////////////// +// Does SetHandler() for the dialog and all of its controls. Handy if you +// have one of those all-encompasing dialog procs. :) + +void pfGUIDialogMod::SetHandlerForAll( pfGUIDialogProc *hdlr ) +{ + int i; + + + SetHandler( hdlr ); + for( i = 0; i < fControls.GetCount(); i++ ) + fControls[ i ]->ISetHandler( hdlr ); +} + +//// SetControlHandler /////////////////////////////////////////////////////// + +void pfGUIDialogMod::SetControlHandler( UInt32 tagID, pfGUIDialogProc *hdlr ) +{ + int i; + for( i = 0; i < fControls.GetCount(); i++ ) + { + if( fControls[ i ]->GetTagID() == tagID ) + { + fControls[ i ]->SetHandler( hdlr ); + break; + } + } +} + + +//// UpdateAspectRatio /////////////////////////////////////////////////////// + +void pfGUIDialogMod::UpdateAspectRatio( void ) +{ + if (fRenderMod) + { + // Set width fov respecting height fov + fRenderMod->SetFovX(pfGameGUIMgr::GetInstance()->GetAspectRatio() * fRenderMod->GetFovY()); + } + UpdateAllBounds(); +} + + +//// UpdateAllBounds ///////////////////////////////////////////////////////// + +void pfGUIDialogMod::UpdateAllBounds( void ) +{ + int i; + for( i = 0; i < fControls.GetCount(); i++ ) + { + if( fControls[ i ] != nil ) + fControls[ i ]->UpdateBounds( nil, true ); + } +} + +//// RefreshAllControls ////////////////////////////////////////////////////// + +void pfGUIDialogMod::RefreshAllControls( void ) +{ + int i; + for( i = 0; i < fControls.GetCount(); i++ ) + fControls[ i ]->IUpdate(); +} + +////////////////////////////////////////////////////////////////////////////// +//// ListElement Drag Functions ////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// ClearDragList /////////////////////////////////////////////////////////// + +void pfGUIDialogMod::ClearDragList( void ) +{ + fDragElements.Reset(); +} + +//// AddToDragList /////////////////////////////////////////////////////////// + +void pfGUIDialogMod::AddToDragList( pfGUIListElement *e ) +{ + fDragElements.Append( e ); +} + +//// EnterDragMode /////////////////////////////////////////////////////////// + +void pfGUIDialogMod::EnterDragMode( pfGUIControlMod *source ) +{ + if( fDragElements.GetCount() > 0 ) + { + fDragMode = true; + fDragReceptive = false; + fDragTarget = nil; + + fDragSource = source; + } +} + +//// IHandleDrag ///////////////////////////////////////////////////////////// +// Oooh, we're in dragging-list-elements-around mode! So completely ignore +// the normal way we do things; what we need to do is wait until the mouse +// button is up, all the while testing to see if the control we're on top of +// is capable of receiving the elements we have. Once the mouse button is let +// up, if the control is indeed receptive, we call its drag handler for each +// of our elements, and either way, exit drag mode. + +void pfGUIDialogMod::IHandleDrag( hsPoint3 &mousePoint, pfGameGUIMgr::EventType event, UInt8 modifiers ) +{ + int i; + hsScalar smallestZ; + + + // First, see if our target control has changed + for( i = 0, fMousedCtrl = nil, smallestZ = 1.e30f; i < fControls.GetCount(); i++ ) + { + if( fControls[ i ]->PointInBounds( mousePoint ) && fControls[ i ]->GetBounds().GetMaxs().fZ < smallestZ ) + fMousedCtrl = fControls[ i ]; + } + + if( fMousedCtrl != fDragTarget ) + { + // Target has changed, update our receptive flag + fDragTarget = fMousedCtrl; + if( fDragTarget == nil ) + fDragReceptive = false; + else + { + pfGUIDropTargetProc *dropProc = fDragTarget->GetDropTargetHdlr(); + if( dropProc == nil ) + fDragReceptive = false; + else + { + fDragReceptive = true; + for( i = 0; i < fDragElements.GetCount(); i++ ) + { + if( !dropProc->CanEat( fDragElements[ i ], fDragSource ) ) + { + fDragReceptive = false; + break; + } + } + } + } + } + + if( event == pfGameGUIMgr::kMouseUp ) + { + /// Mouse got let up--we're exiting drag mode, but can we process the drop? + fDragMode = false; + if( fDragReceptive ) + { + pfGUIDropTargetProc *dropProc = fDragTarget->GetDropTargetHdlr(); + for( i = 0; i < fDragElements.GetCount(); i++ ) + dropProc->Eat( fDragElements[ i ], fDragSource, fDragTarget ); + } + } +} + +//// GetDesiredCursor //////////////////////////////////////////////////////// + +UInt32 pfGUIDialogMod::GetDesiredCursor( void ) const +{ + if( fMousedCtrl != nil ) + return fMousedCtrl->IGetDesiredCursor(); + + return 0; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogMod.h new file mode 100644 index 00000000..bfaddd58 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogMod.h @@ -0,0 +1,198 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIDialogMod Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIDialogMod_h +#define _pfGUIDialogMod_h + + +#include "../pnModifier/plSingleModifier.h" +#include "pfGameGUIMgr.h" +#include "hsMatrix44.h" + +class plMessage; +class plPostEffectMod; +class pfGUIControlMod; +class pfGUIDialogProc; +class pfGUIListElement; +class pfGUIColorScheme; + +class pfGUIDialogMod : public plSingleModifier +{ + private: + pfGUIDialogMod *fNext, **fPrevPtr; + + protected: + + UInt32 fTagID; // 0 if none + + UInt32 fVersion; // Nice for syncing to C++ code + + plPostEffectMod *fRenderMod; + hsBool fEnabled; + char fName[ 128 ]; + hsTArray fControls; + pfGUIControlMod *fControlOfInterest; + pfGUIControlMod *fFocusCtrl; + pfGUIControlMod *fMousedCtrl; // Which one is the mouse over? + pfGUIColorScheme *fColorScheme; + + pfGUIDialogProc *fHandler; + plKey fProcReceiver; // Non-nil means we handle everything by creating notify messages and sending them to this key + + hsTArray fDragElements; + hsBool fDragMode, fDragReceptive; + pfGUIControlMod *fDragTarget; + pfGUIControlMod *fDragSource; + + plKey fSceneNodeKey; + + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + void IHandleDrag( hsPoint3 &mousePt, pfGameGUIMgr::EventType event, UInt8 modifiers ); + + public: + + enum + { + kRenderModRef = 0, + kControlRef, + kRefDerviedStart + }; + + enum Flags + { + kModal, + kDerivedFlagsStart + }; + + pfGUIDialogMod(); + virtual ~pfGUIDialogMod(); + + CLASSNAME_REGISTER( pfGUIDialogMod ); + GETINTERFACE_ANY( pfGUIDialogMod, plSingleModifier ); + + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + void SetSceneNodeKey( plKey &key ) { fSceneNodeKey = key; } + plKey GetSceneNodeKey( void ); + + virtual void SetEnabled( hsBool e ); + hsBool IsEnabled( void ) { return fEnabled; } + + const char *GetName( void ) { return fName; } + + void ScreenToWorldPoint( hsScalar x, hsScalar y, hsScalar z, hsPoint3 &outPt ); + hsPoint3 WorldToScreenPoint( const hsPoint3 &inPt ); + + virtual hsBool HandleMouseEvent( pfGameGUIMgr::EventType event, hsScalar mouseX, hsScalar mouseY, UInt8 modifiers ); + hsBool HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers ); + hsBool HandleKeyPress( char key, UInt8 modifiers ); + void UpdateInterestingThings( hsScalar mouseX, hsScalar mouseY, UInt8 modifiers, hsBool modalPreset ); + + void SetControlOfInterest( pfGUIControlMod *c ); + pfGUIControlMod *GetControlOfInterest( void ) const { return fControlOfInterest; } + UInt32 GetDesiredCursor( void ) const; + + void UpdateAspectRatio( void ); + void UpdateAllBounds( void ); + void RefreshAllControls( void ); + + void AddControl( pfGUIControlMod *ctrl ); + UInt32 GetNumControls( void ) { return fControls.GetCount(); } + pfGUIControlMod *GetControl( UInt32 idx ) { return fControls[ idx ]; } + + pfGUIColorScheme *GetColorScheme( void ) const { return fColorScheme; } + + void LinkToList( pfGUIDialogMod **prevPtr ) + { + fNext = *prevPtr; + if( *prevPtr ) + (*prevPtr)->fPrevPtr = &fNext; + fPrevPtr = prevPtr; + *prevPtr = this; + } + + void Unlink( void ) + { + if( fNext ) + fNext->fPrevPtr = fPrevPtr; + *fPrevPtr = fNext; + + fPrevPtr = nil; + fNext = nil; + } + + void SetFocus( pfGUIControlMod *ctrl ); + void Show( void ); + void ShowNoReset( void ); + void Hide( void ); + hsBool IsVisible( void ) { return IsEnabled(); } + + pfGUIControlMod *GetFocus( void ) { return fFocusCtrl; } + + pfGUIDialogMod *GetNext( void ) { return fNext; } + UInt32 GetTagID( void ) { return fTagID; } + pfGUIControlMod *GetControlFromTag( UInt32 tagID ); + + void SetHandler( pfGUIDialogProc *hdlr ); + pfGUIDialogProc *GetHandler( void ) const { return fHandler; } + + plPostEffectMod *GetRenderMod( void ) const { return fRenderMod; } + + // This sets the handler for the dialog and ALL of its controls + void SetHandlerForAll( pfGUIDialogProc *hdlr ); + + // Just a little macro-type thing here + void SetControlHandler( UInt32 tagID, pfGUIDialogProc *hdlr ); + + /// Methods for doing drag & drop of listElements + + void ClearDragList( void ); + void AddToDragList( pfGUIListElement *e ); + void EnterDragMode( pfGUIControlMod *source ); + + UInt32 GetVersion( void ) const { return fVersion; } + + // Export only + void SetRenderMod( plPostEffectMod *mod ) { fRenderMod = mod; } + void SetName( const char *name ) { hsStrncpy( fName, name, sizeof( fName ) - 1 ); } + void AddControlOnExport( pfGUIControlMod *ctrl ); + void SetTagID( UInt32 id ) { fTagID = id; } + void SetProcReceiver( plKey key ) { fProcReceiver = key; } + void SetVersion( UInt32 version ) { fVersion = version; } +}; + +#endif // _pfGUIDialogMod_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogNotifyProc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogNotifyProc.cpp new file mode 100644 index 00000000..051f0ccf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogNotifyProc.cpp @@ -0,0 +1,131 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIDialogNotifyProc // +// // +// Helper dialog proc that takes all control events and turns them into // +// notify messages that get sent out. // +////////////////////////////////////////////////////////////////////////////// + + +#include "pfGUIDialogNotifyProc.h" + +#include "hsTypes.h" +#include "pfGameGUIMgr.h" +#include "pfGUIDialogMod.h" +#include "pfGUIControlMod.h" +#include "pfGUIDialogHandlers.h" +#include "pfGUIListElement.h" +#include "pfGUIButtonMod.h" // Next three are for notify stuff +#include "pfGUIListBoxMod.h" +#include "pfGUIEditBoxMod.h" + +#include "../pfMessage/pfGUINotifyMsg.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" + + +pfGUIDialogNotifyProc::pfGUIDialogNotifyProc( plKey &r ) +{ + fReceiver = r; +} + + +void pfGUIDialogNotifyProc::ISendNotify( plKey ctrlKey, UInt32 event ) +{ + pfGUINotifyMsg *notify = TRACKED_NEW pfGUINotifyMsg( fDialog->GetKey(), fReceiver, nil ); + notify->SetEvent( ctrlKey, event ); + plgDispatch::MsgSend( notify ); +} + + +void pfGUIDialogNotifyProc::DoSomething( pfGUIControlMod *ctrl ) +{ + if( pfGUIButtonMod::ConvertNoRef( ctrl ) != nil || + pfGUIListBoxMod::ConvertNoRef( ctrl ) != nil || + pfGUIEditBoxMod::ConvertNoRef( ctrl ) != nil ) + { + // only fire the button if it is triggering + // ... all other types just fire + pfGUIButtonMod* btn = pfGUIButtonMod::ConvertNoRef( ctrl ); + if ( !btn || btn->IsTriggering() ) + ISendNotify( ctrl->GetKey(), pfGUINotifyMsg::kAction ); + } + else + ISendNotify( ctrl->GetKey(), pfGUINotifyMsg::kValueChanged ); +} + +void pfGUIDialogNotifyProc::OnInit( void ) +{ + if ( fDialog ) + ISendNotify( fDialog->GetKey(), pfGUINotifyMsg::kDialogLoaded ); + else + ISendNotify( nil, pfGUINotifyMsg::kDialogLoaded ); +} + +void pfGUIDialogNotifyProc::OnShow( void ) +{ + if ( fDialog ) + ISendNotify( fDialog->GetKey(), pfGUINotifyMsg::kShowHide ); + else + ISendNotify( nil, pfGUINotifyMsg::kShowHide ); +} + +void pfGUIDialogNotifyProc::OnHide( void ) +{ + if ( fDialog ) + ISendNotify( fDialog->GetKey(), pfGUINotifyMsg::kShowHide ); + else + ISendNotify( nil, pfGUINotifyMsg::kShowHide ); +} + +void pfGUIDialogNotifyProc::OnDestroy( void ) +{ +} + +void pfGUIDialogNotifyProc::OnControlEvent( ControlEvt event ) +{ + if( event == kExitMode ) + ISendNotify( ( fDialog != nil ) ? fDialog->GetKey() : nil, pfGUINotifyMsg::kExitMode ); +} + +// Called when the dialog's focused control changes +void pfGUIDialogNotifyProc::OnCtrlFocusChange( pfGUIControlMod *oldCtrl, pfGUIControlMod *newCtrl ) +{ + if ( newCtrl ) + ISendNotify( newCtrl->GetKey(), pfGUINotifyMsg::kFocusChange); + else + ISendNotify( nil, pfGUINotifyMsg::kFocusChange); + +} + +void pfGUIDialogNotifyProc::OnInterestingEvent( pfGUIControlMod *ctrl ) +{ + ISendNotify( ( ctrl != nil ) ? ctrl->GetKey() : nil, pfGUINotifyMsg::kInterestingEvent ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogNotifyProc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogNotifyProc.h new file mode 100644 index 00000000..8ec1941b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDialogNotifyProc.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIDialogNotifyProc Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIDialogNotifyProc_h +#define _pfGUIDialogNotifyProc_h + +#include "pfGUIDialogHandlers.h" +#include "../pnKeyedObject/plKey.h" + +class plGUIControlMod; + +//// pfGUIDialogNotifyProc Definition //////////////////////////////////////// +// Helper dialog proc that takes all control events and turns them into +// notify messages that get sent out. + +class pfGUIDialogNotifyProc : public pfGUIDialogProc +{ + protected: + + plKey fReceiver; + + void ISendNotify( plKey ctrlKey, UInt32 event ); + + public: + + pfGUIDialogNotifyProc( plKey &r ); + + virtual void DoSomething( pfGUIControlMod *ctrl ); + virtual void OnInit( void ); + virtual void OnShow( void ); + virtual void OnHide( void ); + virtual void OnDestroy( void ); + virtual void OnCtrlFocusChange( pfGUIControlMod *oldCtrl, pfGUIControlMod *newCtrl ); + virtual void OnControlEvent( ControlEvt event ); + virtual void OnInterestingEvent( pfGUIControlMod *ctrl ); +}; + +#endif // _pfGUIDialogNotifyProc_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDragBarCtrl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDragBarCtrl.cpp new file mode 100644 index 00000000..37b91889 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDragBarCtrl.cpp @@ -0,0 +1,149 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIDragBarCtrl Definition // +// // +// DragBars are draggable controls that take their dialogs along with // +// them. Because they're essentially part of the dialog directly (the part // +// that can be dragged), they're processed after the normal hit testing. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIDragBarCtrl.h" +#include "pfGameGUIMgr.h" +#include "pfGUIDialogMod.h" + +#include "../plInputCore/plInputInterface.h" +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plAvatar/plAGModifier.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIDragBarCtrl::pfGUIDragBarCtrl() +{ + SetFlag( kWantsInterest ); + fDragging = false; + fAnchored = false; +} + +pfGUIDragBarCtrl::~pfGUIDragBarCtrl() +{ +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIDragBarCtrl::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIControlMod::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIDragBarCtrl::MsgReceive( plMessage *msg ) +{ + return pfGUIControlMod::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIDragBarCtrl::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Read(s, mgr); +} + +void pfGUIDragBarCtrl::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Write( s, mgr ); +} + +//// UpdateBounds //////////////////////////////////////////////////////////// + +void pfGUIDragBarCtrl::UpdateBounds( hsMatrix44 *invXformMatrix, hsBool force ) +{ + pfGUIControlMod::UpdateBounds( invXformMatrix, force ); + fBoundsValid = false; +} + +//// HandleMouseDown/Up ////////////////////////////////////////////////////// + +void pfGUIDragBarCtrl::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) +{ + // if we are anchored then don't let it be moved + if ( fAnchored ) + return; + + fDragging = true; + fDragOffset = fScreenCenter - mousePt; + + SetObjectCenter( mousePt.fX + fDragOffset.fX, mousePt.fY + fDragOffset.fY ); + + // We know that the entire dialog is going to move, so we better make + // sure to update the bounds on all the controls + fDialog->UpdateAllBounds(); +} + +void pfGUIDragBarCtrl::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) +{ + // if we are anchored then don't let it be moved + if ( fAnchored ) + return; + + fDragging = false; + SetObjectCenter( mousePt.fX + fDragOffset.fX, mousePt.fY + fDragOffset.fY ); + fDialog->UpdateAllBounds(); +} + +void pfGUIDragBarCtrl::HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ) +{ + // if we are anchored then don't let it be moved + if ( fAnchored ) + return; + + SetObjectCenter( mousePt.fX + fDragOffset.fX, mousePt.fY + fDragOffset.fY ); + fDialog->UpdateAllBounds(); +} + +//// IGetDesiredCursor /////////////////////////////////////////////////////// + +UInt32 pfGUIDragBarCtrl::IGetDesiredCursor( void ) const +{ + // if we are anchored, then no cursors that say we can move + if ( fAnchored ) + return 0; + + if( fDragging ) + return plInputInterface::kCursor4WayDragging; + + return plInputInterface::kCursor4WayDraggable; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDragBarCtrl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDragBarCtrl.h new file mode 100644 index 00000000..cd29aad1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDragBarCtrl.h @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIDragBarCtrl Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIDragBarCtrl_h +#define _pfGUIDragBarCtrl_h + +#include "pfGUIControlMod.h" + +class plMessage; + +class pfGUIDragBarCtrl : public pfGUIControlMod +{ + protected: + + hsPoint3 fDragOffset; + hsBool fDragging; + hsBool fAnchored; + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + virtual UInt32 IGetDesiredCursor( void ) const; // As specified in plInputInterface.h + + public: + + pfGUIDragBarCtrl(); + virtual ~pfGUIDragBarCtrl(); + + CLASSNAME_REGISTER( pfGUIDragBarCtrl ); + GETINTERFACE_ANY( pfGUIDragBarCtrl, pfGUIControlMod ); + + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ); + + virtual void SetAnchored( hsBool anchored ) { fAnchored = anchored; } + virtual hsBool IsAnchored(void) { return fAnchored; } + + virtual void UpdateBounds( hsMatrix44 *invXformMatrix = nil, hsBool force = false ); + + // Export only +}; + +#endif // _pfGUIDragBarCtrl_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDraggableMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDraggableMod.cpp new file mode 100644 index 00000000..7a0a998e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDraggableMod.cpp @@ -0,0 +1,165 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIDraggableMod Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIDraggableMod.h" +#include "pfGameGUIMgr.h" + +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plAvatar/plAGModifier.h" +#include "../plInputCore/plInputInterface.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIDraggableMod::pfGUIDraggableMod() +{ + SetFlag( kWantsInterest ); + fDragging = false; +} + +pfGUIDraggableMod::~pfGUIDraggableMod() +{ +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIDraggableMod::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIControlMod::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIDraggableMod::MsgReceive( plMessage *msg ) +{ + return pfGUIControlMod::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIDraggableMod::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Read(s, mgr); +} + +void pfGUIDraggableMod::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Write( s, mgr ); +} + +//// UpdateBounds //////////////////////////////////////////////////////////// + +void pfGUIDraggableMod::UpdateBounds( hsMatrix44 *invXformMatrix, hsBool force ) +{ + pfGUIControlMod::UpdateBounds( invXformMatrix, force ); + fBoundsValid = false; +} + +//// HandleMouseDown/Up ////////////////////////////////////////////////////// + +void pfGUIDraggableMod::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) +{ + if( !fDragging ) + { + fLastMousePt = mousePt; + fOrigCenter = fScreenCenter; + + fDragging = true; + fDragOffset = fScreenCenter - mousePt; + + SetObjectCenter( mousePt.fX + fDragOffset.fX, mousePt.fY + fDragOffset.fY ); + + HandleExtendedEvent( kStartingDrag ); + } +} + +void pfGUIDraggableMod::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) +{ + if( fDragging ) + { + fLastMousePt = mousePt; + fDragging = false; + SetObjectCenter( mousePt.fX + fDragOffset.fX, mousePt.fY + fDragOffset.fY ); + + DoSomething(); + + if( HasFlag( kAlwaysSnapBackToStart ) ) + SetObjectCenter( fOrigCenter.fX, fOrigCenter.fY ); + } +} + +void pfGUIDraggableMod::HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ) +{ + if( fDragging ) + { + fLastMousePt = mousePt; + + SetObjectCenter( mousePt.fX + fDragOffset.fX, mousePt.fY + fDragOffset.fY ); + + if( HasFlag( kReportDragging ) ) + HandleExtendedEvent( kDragging ); + } +} + +//// IGetDesiredCursor /////////////////////////////////////////////////////// + +UInt32 pfGUIDraggableMod::IGetDesiredCursor( void ) const +{ + // if we are anchored, then no cursors that say we can move + if( fDragging ) + { + if( HasFlag( kHideCursorWhileDragging ) ) + return plInputInterface::kCursorHidden; + + return plInputInterface::kCursor4WayDragging; + } + + return plInputInterface::kCursor4WayDraggable; +} + +void pfGUIDraggableMod::StopDragging( hsBool cancel ) +{ + if( fDragging ) + { + fDragging = false; + if( cancel ) + HandleExtendedEvent( kCancelled ); + + if( HasFlag( kAlwaysSnapBackToStart ) ) + SetObjectCenter( fOrigCenter.fX, fOrigCenter.fY ); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDraggableMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDraggableMod.h new file mode 100644 index 00000000..178cfe22 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDraggableMod.h @@ -0,0 +1,90 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIDraggableMod Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIDraggableMod_h +#define _pfGUIDraggableMod_h + +#include "pfGUIControlMod.h" + +class plMessage; + +class pfGUIDraggableMod : public pfGUIControlMod +{ + protected: + + hsPoint3 fDragOffset, fLastMousePt; + hsPoint3 fOrigCenter; + hsBool fDragging; + + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + virtual UInt32 IGetDesiredCursor( void ) const; // As specified in plInputInterface.h + + public: + + pfGUIDraggableMod(); + virtual ~pfGUIDraggableMod(); + + CLASSNAME_REGISTER( pfGUIDraggableMod ); + GETINTERFACE_ANY( pfGUIDraggableMod, pfGUIControlMod ); + + enum OurFlags + { + kReportDragging = kDerivedFlagsStart, + kHideCursorWhileDragging, + kAlwaysSnapBackToStart + }; + + // Extended event types (endDrag is the default event) + enum ExtendedEvents + { + kDragging, + kCancelled, + kStartingDrag + }; + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ); + + virtual void UpdateBounds( hsMatrix44 *invXformMatrix = nil, hsBool force = false ); + + void StopDragging( hsBool cancel ); + const hsPoint3 &GetLastMousePt( void ) const { return fLastMousePt; } +}; + +#endif // _pfGUIDraggableMod_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDynDisplayCtrl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDynDisplayCtrl.cpp new file mode 100644 index 00000000..be950fa7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDynDisplayCtrl.cpp @@ -0,0 +1,169 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIDynDisplayCtrl Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIDynDisplayCtrl.h" +#include "pfGameGUIMgr.h" + +#include "../pnMessage/plRefMsg.h" +#include "../plGImage/plDynamicTextMap.h" +#include "../plSurface/plLayerInterface.h" +#include "../plSurface/hsGMaterial.h" +#include "../plPipeline/plTextGenerator.h" +#include "plPipeline.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIDynDisplayCtrl::pfGUIDynDisplayCtrl() +{ + SetFlag( kIntangible ); +} + +pfGUIDynDisplayCtrl::~pfGUIDynDisplayCtrl() +{ +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIDynDisplayCtrl::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIControlMod::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIDynDisplayCtrl::MsgReceive( plMessage *msg ) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); + if( refMsg != nil ) + { + if( refMsg->fType == kRefTextMap ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fTextMaps[ refMsg->fWhich ] = plDynamicTextMap::ConvertNoRef( refMsg->GetRef() ); + else + fTextMaps[ refMsg->fWhich ] = nil; + return true; + } + else if( refMsg->fType == kRefLayer ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fLayers[ refMsg->fWhich ] = plLayerInterface::ConvertNoRef( refMsg->GetRef() ); + else + fLayers[ refMsg->fWhich ] = nil; + return true; + } + else if( refMsg->fType == kRefMaterial ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fMaterials[ refMsg->fWhich ] = hsGMaterial::ConvertNoRef( refMsg->GetRef() ); + else + fMaterials[ refMsg->fWhich ] = nil; + } + } + + return pfGUIControlMod::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIDynDisplayCtrl::Read( hsStream *s, hsResMgr *mgr ) +{ + UInt32 count, i; + + + pfGUIControlMod::Read(s, mgr); + + count = s->ReadSwap32(); + fTextMaps.SetCountAndZero( count ); + for( i = 0; i < count; i++ ) + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kRefTextMap ), plRefFlags::kActiveRef ); + + count = s->ReadSwap32(); + fLayers.SetCountAndZero( count ); + for( i = 0; i < count; i++ ) + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kRefLayer ), plRefFlags::kActiveRef ); + + count = s->ReadSwap32(); + fMaterials.SetCountAndZero( count ); + for( i = 0; i < count; i++ ) + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kRefMaterial ), plRefFlags::kActiveRef ); +} + +void pfGUIDynDisplayCtrl::Write( hsStream *s, hsResMgr *mgr ) +{ + UInt32 i; + + + pfGUIControlMod::Write( s, mgr ); + + s->WriteSwap32( fTextMaps.GetCount() ); + for( i = 0; i < fTextMaps.GetCount(); i++ ) + mgr->WriteKey( s, fTextMaps[ i ]->GetKey() ); + + s->WriteSwap32( fLayers.GetCount() ); + for( i = 0; i < fLayers.GetCount(); i++ ) + mgr->WriteKey( s, fLayers[ i ]->GetKey() ); + + s->WriteSwap32( fMaterials.GetCount() ); + for( i = 0; i < fMaterials.GetCount(); i++ ) + mgr->WriteKey( s, fMaterials[ i ]->GetKey() ); +} + +//// AddMap ////////////////////////////////////////////////////////////////// +// Export only + +void pfGUIDynDisplayCtrl::AddMap( plDynamicTextMap *map ) +{ + fTextMaps.Append( map ); + hsgResMgr::ResMgr()->AddViaNotify( map->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, fTextMaps.GetCount() - 1, kRefTextMap ), plRefFlags::kActiveRef ); +} + +//// AddLayer //////////////////////////////////////////////////////////////// +// Export only + +void pfGUIDynDisplayCtrl::AddLayer( plLayerInterface *layer ) +{ + fLayers.Append( layer ); + hsgResMgr::ResMgr()->AddViaNotify( layer->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, fLayers.GetCount() - 1, kRefLayer ), plRefFlags::kActiveRef ); +} + +//// AddMaterial ///////////////////////////////////////////////////////////// +// Export only + +void pfGUIDynDisplayCtrl::AddMaterial( hsGMaterial *material ) +{ + fMaterials.Append( material ); + hsgResMgr::ResMgr()->AddViaNotify( material->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, fMaterials.GetCount() - 1, kRefMaterial ), plRefFlags::kActiveRef ); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDynDisplayCtrl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDynDisplayCtrl.h new file mode 100644 index 00000000..5d61ac25 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIDynDisplayCtrl.h @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIDynDisplayCtrl Header // +// // +// Fun little helper control that just stores a pointer to a single // +// plDynamicTextMap, chosen in MAX. Note that we could also just search // +// for the right key name, but that requires a StupidSearch(tm), while // +// this way just requires an extra dummy control that automatically reads // +// in the right ref (and searching for controls by TagID is a lot faster // +// than searching for keys). // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIDynDisplayCtrl_h +#define _pfGUIDynDisplayCtrl_h + +#include "pfGUIControlMod.h" +#include "hsTemplates.h" + +class plMessage; +class plDynamicTextMap; +class plLayerInterface; +class hsGMaterial; + +class pfGUIDynDisplayCtrl : public pfGUIControlMod +{ + protected: + + enum + { + kRefTextMap = kRefDerivedStart, + kRefLayer, + kRefMaterial + }; + + hsTArray fTextMaps; + hsTArray fLayers; + + hsTArray fMaterials; + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + public: + + pfGUIDynDisplayCtrl(); + virtual ~pfGUIDynDisplayCtrl(); + + CLASSNAME_REGISTER( pfGUIDynDisplayCtrl ); + GETINTERFACE_ANY( pfGUIDynDisplayCtrl, pfGUIControlMod ); + + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + UInt32 GetNumMaps( void ) const { return fTextMaps.GetCount(); } + plDynamicTextMap *GetMap( UInt32 i ) const { return fTextMaps[ i ]; } + + UInt32 GetNumLayers( void ) const { return fLayers.GetCount(); } + plLayerInterface *GetLayer( UInt32 i ) const { return fLayers[ i ]; } + + UInt32 GetNumMaterials( void ) const { return fMaterials.GetCount(); } + hsGMaterial *GetMaterial( UInt32 i ) const { return fMaterials[ i ]; } + + // Export only + void AddMap( plDynamicTextMap *map ); + void AddLayer( plLayerInterface *layer ); + void AddMaterial( hsGMaterial *material ); +}; + +#endif // _pfGUIDynDisplayCtrl_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIEditBoxMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIEditBoxMod.cpp new file mode 100644 index 00000000..2072ebd3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIEditBoxMod.cpp @@ -0,0 +1,650 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIEditBoxMod Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifdef PLASMA_EXTERNAL_RELEASE +//#define LIMIT_VOICE_CHAT 1 +#endif + +#include "hsTypes.h" +#include "pfGUIEditBoxMod.h" +#include "pfGameGUIMgr.h" + +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plAvatar/plAGModifier.h" +#include "../plGImage/plDynamicTextMap.h" +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "../pnInputCore/plKeyMap.h" + +#include + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIEditBoxMod::pfGUIEditBoxMod() +{ + SetFlag( kWantsInterest ); + SetFlag( kTakesSpecialKeys ); + fIgnoreNextKey = false; + fEscapedFlag = false; + fFirstHalfExitKeyPushed = false; + fSpecialCaptureKeyEventMode = false; + fBuffer = 0; + fLastDeadKey = 0; + SetBufferSize( 128 ); + + SetupDeadKeyConverter(); +} + +pfGUIEditBoxMod::~pfGUIEditBoxMod() +{ + delete [] fBuffer; +} + +void pfGUIEditBoxMod::SetupDeadKeyConverter() +{ + int i,j; + for (i=0; i<255; i++) + for (j=0; j<255; j++) + fDeadKeyConverter[i][j] = 0L; + + // we are adding 100 to the indexes because some of these chars have a negative index for some reason + fDeadKeyConverter['^'+100]['a'] = L'â'; + fDeadKeyConverter['^'+100]['e'] = L'ê'; + fDeadKeyConverter['^'+100]['i'] = L'î'; + fDeadKeyConverter['^'+100]['o'] = L'ô'; + fDeadKeyConverter['^'+100]['u'] = L'û'; + fDeadKeyConverter['^'+100]['A'] = L'Â'; + fDeadKeyConverter['^'+100]['E'] = L'Ê'; + fDeadKeyConverter['^'+100]['I'] = L'Î'; + fDeadKeyConverter['^'+100]['O'] = L'Ô'; + fDeadKeyConverter['^'+100]['U'] = L'Û'; + + fDeadKeyConverter['¨'+100]['a'] = L'ä'; + fDeadKeyConverter['¨'+100]['e'] = L'ë'; + fDeadKeyConverter['¨'+100]['i'] = L'ï'; + fDeadKeyConverter['¨'+100]['o'] = L'ö'; + fDeadKeyConverter['¨'+100]['u'] = L'ü'; + fDeadKeyConverter['¨'+100]['A'] = L'Ä'; + fDeadKeyConverter['¨'+100]['E'] = L'Ë'; + fDeadKeyConverter['¨'+100]['I'] = L'Ï'; + fDeadKeyConverter['¨'+100]['O'] = L'Ö'; + fDeadKeyConverter['¨'+100]['U'] = L'Ü'; + + fDeadKeyConverter['´'+100]['a'] = L'á'; + fDeadKeyConverter['´'+100]['e'] = L'é'; + fDeadKeyConverter['´'+100]['i'] = L'í'; + fDeadKeyConverter['´'+100]['o'] = L'ó'; + fDeadKeyConverter['´'+100]['u'] = L'ú'; + fDeadKeyConverter['´'+100]['y'] = L'ý'; + fDeadKeyConverter['´'+100]['A'] = L'Á'; + fDeadKeyConverter['´'+100]['E'] = L'É'; + fDeadKeyConverter['´'+100]['I'] = L'Í'; + fDeadKeyConverter['´'+100]['O'] = L'Ó'; + fDeadKeyConverter['´'+100]['U'] = L'Ú'; + fDeadKeyConverter['´'+100]['Y'] = L'Ý'; + + fDeadKeyConverter['`'+100]['a'] = L'à'; + fDeadKeyConverter['`'+100]['e'] = L'è'; + fDeadKeyConverter['`'+100]['i'] = L'ì'; + fDeadKeyConverter['`'+100]['o'] = L'ò'; + fDeadKeyConverter['`'+100]['u'] = L'ù'; + fDeadKeyConverter['`'+100]['A'] = L'À'; + fDeadKeyConverter['`'+100]['E'] = L'È'; + fDeadKeyConverter['`'+100]['I'] = L'Ì'; + fDeadKeyConverter['`'+100]['O'] = L'Ò'; + fDeadKeyConverter['`'+100]['U'] = L'Ù'; +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIEditBoxMod::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIControlMod::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIEditBoxMod::MsgReceive( plMessage *msg ) +{ + return pfGUIControlMod::MsgReceive( msg ); +} + +//// IPostSetUpDynTextMap //////////////////////////////////////////////////// + +void pfGUIEditBoxMod::IPostSetUpDynTextMap( void ) +{ + pfGUIColorScheme *scheme = GetColorScheme(); + fDynTextMap->SetFont( scheme->fFontFace, scheme->fFontSize, scheme->fFontFlags, + HasFlag( kXparentBgnd ) ? false : true ); +} + +//// IUpdate ///////////////////////////////////////////////////////////////// + +void pfGUIEditBoxMod::IUpdate( void ) +{ + hsColorRGBA c; + + + if( fDynTextMap == nil || !fDynTextMap->IsValid() ) + return; + + c.Set( 0.f, 0.f, 0.f, 1.f ); + if ( fFocused && fSpecialCaptureKeyEventMode ) + fDynTextMap->ClearToColor( GetColorScheme()->fSelBackColor ); + else + fDynTextMap->ClearToColor( GetColorScheme()->fBackColor ); + + if( fBuffer != nil ) + { + // First, calc the cursor position, so we can adjust the scrollPos as necessary + Int16 cursorPos, oldCursorPos; + if( fFocused && !fSpecialCaptureKeyEventMode ) + { + // Really cheap hack here to figure out where to draw the cursor + wchar_t backup = fBuffer[ fCursorPos ]; + fBuffer[ fCursorPos ] = 0; + cursorPos = fDynTextMap->CalcStringWidth( fBuffer ); + fBuffer[ fCursorPos ] = backup; + + oldCursorPos = cursorPos; + cursorPos -= (Int16)fScrollPos; + + if( 4 + cursorPos > fDynTextMap->GetVisibleWidth() - 18 ) + { + fScrollPos += ( 4 + cursorPos ) - ( fDynTextMap->GetVisibleWidth() - 18 ); + } + else if( 4 + cursorPos < 4 ) + { + fScrollPos -= 4 - ( 4 + cursorPos ); + if( fScrollPos < 0 ) + fScrollPos = 0; + } + + cursorPos = (Int16)(oldCursorPos - fScrollPos); + } + + if ( fFocused && fSpecialCaptureKeyEventMode ) + // if special and has focus then use select + fDynTextMap->SetTextColor( GetColorScheme()->fSelForeColor, GetColorScheme()->fTransparent && + GetColorScheme()->fSelBackColor.a == 0.f ); + else + fDynTextMap->SetTextColor( GetColorScheme()->fForeColor, GetColorScheme()->fTransparent && + GetColorScheme()->fBackColor.a == 0.f ); + fDynTextMap->DrawClippedString( (Int16)(4 - fScrollPos), 4, fBuffer, + 4, 4, fDynTextMap->GetVisibleWidth() - 8, fDynTextMap->GetVisibleHeight() - 8 ); + + if( fFocused && !fSpecialCaptureKeyEventMode ) + { + fDynTextMap->FrameRect( 4 + cursorPos, 4, 2, fDynTextMap->GetVisibleHeight() - 8, GetColorScheme()->fSelForeColor ); + } + } + fDynTextMap->FlushToHost(); +} + +void pfGUIEditBoxMod::PurgeDynaTextMapImage() +{ + if ( fDynTextMap != nil ) + fDynTextMap->PurgeImage(); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIEditBoxMod::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Read(s, mgr); +} + +void pfGUIEditBoxMod::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Write( s, mgr ); +} + +//// HandleMouseDown ///////////////////////////////////////////////////////// +// What we do: normal click deselects all and selects the item clicked on +// (if any). Shift-click and ctrl-click avoids the deselect and toggles +// the item clicked on. + +void pfGUIEditBoxMod::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) +{ + wchar_t backup; + UInt16 width; + + + if( fBuffer != nil && fDynTextMap != nil ) + { + if( !fBounds.IsInside( &mousePt ) ) + return; + + IScreenToLocalPt( mousePt ); + + mousePt.fX *= fDynTextMap->GetVisibleWidth(); + mousePt.fX += fScrollPos - 4; + for( fCursorPos = 0; fCursorPos < wcslen( fBuffer ); fCursorPos++ ) + { + backup = fBuffer[ fCursorPos + 1 ]; + fBuffer[ fCursorPos + 1 ] = 0; + width = fDynTextMap->CalcStringWidth( fBuffer ); + fBuffer[ fCursorPos + 1 ] = backup; + + if( width > mousePt.fX ) + break; + } + + IUpdate(); + } +} + +//// HandleMouseUp /////////////////////////////////////////////////////////// + +void pfGUIEditBoxMod::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) +{ +} + +//// HandleMouseDrag ///////////////////////////////////////////////////////// + +void pfGUIEditBoxMod::HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ) +{ +} + +hsBool pfGUIEditBoxMod::HandleKeyPress( char inKey, UInt8 modifiers ) +{ + wchar_t key = (wchar_t)inKey; + + if( fBuffer == nil ) + return false; + + if( fIgnoreNextKey ) + { + // So we don't process keys that already got handled by HandleKeyEvent() + fIgnoreNextKey = false; + return true; + } + + if (plKeyboardDevice::KeyIsDeadKey()) + { + if (fLastDeadKey != 0) + { + wchar_t temp = key; // we have two dead keys in a row, print out the old one and store the new one + key = fLastDeadKey; + fLastDeadKey = temp; + } + else + { + fLastDeadKey = key; // store the dead key and don't print it until we get the next char + return true; + } + } + + int i = wcslen( fBuffer ); + if (fLastDeadKey != 0) // we have a dead key that needs to be added in + { + wchar_t translatedKey = fDeadKeyConverter[(char)fLastDeadKey+100][(char)key]; + if (translatedKey == 0) // no translation possible? + { + // so we need to print the dead key, followed by the typed key + // unless key is a space, then we just type the dead key + if (key == L' ') + { + if (i 0 ) + fCursorPos--; + } + else if( key == KEY_RIGHT && fBuffer != nil ) + { + if( fCursorPos < wcslen( fBuffer ) ) + fCursorPos++; + } + else if( key == KEY_BACKSPACE && fBuffer != nil ) + { + if( fCursorPos > 0 ) + { + fCursorPos--; + memmove( fBuffer + fCursorPos, fBuffer + fCursorPos + 1, (wcslen( fBuffer + fCursorPos + 1 ) + 1) * sizeof(wchar_t) ); + } + } + else if( key == KEY_DELETE && fBuffer != nil ) + { + if( fCursorPos < wcslen( fBuffer ) ) + memmove( fBuffer + fCursorPos, fBuffer + fCursorPos + 1, (wcslen( fBuffer + fCursorPos + 1 ) + 1) * sizeof(wchar_t) ); + } + else if( key == KEY_ENTER ) + { + // do nothing here... wait for the keyup event + fFirstHalfExitKeyPushed = true; + } + else if( key == KEY_ESCAPE ) + { +// // do nothing here... wait for the keyup event +// fFirstHalfExitKeyPushed = true; + fEscapedFlag = true; + DoSomething(); // Query WasEscaped() to see if it was escape vs enter + return true; + } + else + { + fIgnoreNextKey = false; + return true; + } + + fIgnoreNextKey = true; + IUpdate(); + return true; + } + // wait until the Key up for enter and escape to make sure we capture the whole key + // ...before we give on focus control + else if( event == pfGameGUIMgr::kKeyUp ) + { + if( key == KEY_ENTER ) + { + if (fFirstHalfExitKeyPushed) + { + // Do jack, just here to filter out it being added to the buffer + // Well, ok, actually do *something*. *cough*. + DoSomething(); + fFirstHalfExitKeyPushed = false; + return true; + } + } + else if( key == KEY_ESCAPE ) + { + if (fFirstHalfExitKeyPushed) + { +// fEscapedFlag = true; +// DoSomething(); // Query WasEscaped() to see if it was escape vs enter + fFirstHalfExitKeyPushed = false; + return true; + } + } + fFirstHalfExitKeyPushed = false; + return true; + } + else + { + // We don't process them, but we don't want anybody else processing them either + return true; + } + } +} + +std::string pfGUIEditBoxMod::GetBuffer( void ) +{ + char* temp = hsWStringToString(fBuffer); + std::string retVal = temp; + delete [] temp; + return retVal; +} + +void pfGUIEditBoxMod::ClearBuffer( void ) +{ + if( fBuffer != nil ) + { + memset( fBuffer, 0, (fBufferSize + 1) * sizeof(wchar_t) ); + fCursorPos = 0; + fScrollPos = 0; + IUpdate(); + } +} + +void pfGUIEditBoxMod::SetText( const char *str ) +{ + wchar_t* temp = hsStringToWString(str); + SetText(temp); + delete [] temp; +} + +void pfGUIEditBoxMod::SetText( const wchar_t *str ) +{ + if( fBuffer != nil ) + { + wcsncpy( fBuffer, str, fBufferSize - 1 ); + fCursorPos = 0; + fScrollPos = 0; + IUpdate(); + } +} + +void pfGUIEditBoxMod::SetBufferSize( UInt32 size ) +{ + delete [] fBuffer; + + fBufferSize = size; + if( size > 0 ) + { + fBuffer = TRACKED_NEW wchar_t[ size + 1 ]; + memset( fBuffer, 0, (size + 1) * sizeof(wchar_t) ); + } + else + fBuffer = nil; + + fCursorPos = 0; + fScrollPos = 0; +} + + +void pfGUIEditBoxMod::SetCursorToHome( void ) +{ + fCursorPos = 0; +} + +void pfGUIEditBoxMod::SetCursorToEnd( void ) +{ + if( fBuffer != nil ) + fCursorPos = wcslen( fBuffer ); +} + +void pfGUIEditBoxMod::SetLastKeyCapture(UInt32 key, UInt8 modifiers) +{ + // capture the key + fSavedKey = (plKeyDef)key; + fSavedModifiers = modifiers; + + // turn key event into string + char keyStr[30]; + if (plKeyMap::ConvertVKeyToChar( key )) + strcpy(keyStr, plKeyMap::ConvertVKeyToChar( key )); + else + memset(keyStr, 0, sizeof(keyStr)); + + static char shortKey[ 2 ]; + if( strlen(keyStr) == 0 ) + { + if( isalnum( key ) ) + { + shortKey[ 0 ] = (char)key; + shortKey[ 1 ] = 0; + strcpy(keyStr, shortKey); + } + else + strcpy(keyStr, plKeyMap::GetStringUnmapped()); + } + else + { + // check to see the buffer has ForewardSlash and change it to ForwardSlash + if ( strcmp(keyStr,"ForewardSlash") == 0) + { + strcpy(keyStr,"ForwardSlash"); + } + } + + static char newKey[ 16 ]; + newKey[0] = 0; + if( modifiers & kShift ) + strcat( newKey, plKeyMap::GetStringShift() ); + if( modifiers & kCtrl ) + strcat( newKey, plKeyMap::GetStringCtrl() ); + strcat( newKey, keyStr ); + + // set something in the buffer to be displayed + wchar_t* temp = hsStringToWString(newKey); + wcsncpy( fBuffer, temp , fBufferSize - 1 ); + delete [] temp; + + fCursorPos = 0; + SetCursorToEnd(); + IUpdate(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIEditBoxMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIEditBoxMod.h new file mode 100644 index 00000000..a313403b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIEditBoxMod.h @@ -0,0 +1,125 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIEditBoxMod Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIEditBoxMod_h +#define _pfGUIEditBoxMod_h + +#include "hsStlUtils.h" +#include "pfGUIControlMod.h" +#include "../pnInputCore/plKeyDef.h" + +#include "../plInputCore/plInputDevice.h" + +class plMessage; +class hsGMaterial; +class plTextGenerator; + + +class pfGUIEditBoxMod : public pfGUIControlMod +{ + protected: + + wchar_t *fBuffer; + UInt32 fBufferSize, fCursorPos; + Int32 fScrollPos; + hsBool fIgnoreNextKey, fEscapedFlag; + hsBool fFirstHalfExitKeyPushed; + + hsBool fSpecialCaptureKeyEventMode; + + plKeyDef fSavedKey; + UInt8 fSavedModifiers; + + wchar_t fLastDeadKey; // if the previous key was a dead key, its value goes here + wchar_t fDeadKeyConverter[256][256]; // first index is the dead key, second index is the char to combine it with + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + virtual void IPostSetUpDynTextMap( void ); + virtual void IUpdate( void ); + + void SetupDeadKeyConverter(); + + public: + enum + { + kShift = 0x01, + kCtrl = 0x02 + }; + + pfGUIEditBoxMod(); + virtual ~pfGUIEditBoxMod(); + + CLASSNAME_REGISTER( pfGUIEditBoxMod ); + GETINTERFACE_ANY( pfGUIEditBoxMod, pfGUIControlMod ); + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ); + + virtual hsBool HandleKeyPress( char key, UInt8 modifiers ); + virtual hsBool HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers ); + + virtual void PurgeDynaTextMapImage(); + + void SetBufferSize( UInt32 size ); + + std::string GetBuffer( void ); + std::wstring GetBufferW( void ) { return fBuffer; } + void ClearBuffer( void ); + void SetText( const char *str ); + void SetText( const wchar_t *str ); + + void SetCursorToHome( void ); + void SetCursorToEnd( void ); + + hsBool WasEscaped( void ) { hsBool e = fEscapedFlag; fEscapedFlag = false; return e; } + + void SetSpecialCaptureKeyMode(hsBool state) { fSpecialCaptureKeyEventMode = state; } + UInt32 GetLastKeyCaptured() { return (UInt32)fSavedKey; } + UInt8 GetLastModifiersCaptured() { return fSavedModifiers; } + void SetLastKeyCapture(UInt32 key, UInt8 modifiers); + + void SetChatMode(hsBool state) { plKeyboardDevice::IgnoreCapsLock(state); } + + // Extended event types + enum ExtendedEvents + { + kValueChanging + }; +}; + +#endif // _pfGUIEditBoxMod_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIKnobCtrl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIKnobCtrl.cpp new file mode 100644 index 00000000..df02410c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIKnobCtrl.cpp @@ -0,0 +1,368 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIKnobCtrl Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIKnobCtrl.h" +#include "pfGameGUIMgr.h" +#include "pfGUIDialogMod.h" + +#include "../plInputCore/plInputInterface.h" +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +// #include "../plAvatar/plAGModifier.h" +#include "../plAvatar/plAGMasterMod.h" +#include "../plAvatar/plAGAnimInstance.h" +#include "../plSurface/plLayerAnimation.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIKnobCtrl::pfGUIKnobCtrl() : + fAnimName(nil), + fDragStart(0.f, 0.f, 0.f), + fDragging(false), + fAnimStartPos(0.f, 0.f, 0.f), + fAnimEndPos(0.f, 0.f, 0.f), + fDragRangeMin(0.f), + fDragRangeMax(0.f), + fAnimBegin(0.f), + fAnimEnd(0.f), + fAnimTimesCalced(false) +{ + SetFlag( kWantsInterest ); +} + +pfGUIKnobCtrl::~pfGUIKnobCtrl() +{ + delete [] fAnimName; +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIKnobCtrl::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIValueCtrl::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIKnobCtrl::MsgReceive( plMessage *msg ) +{ + return pfGUIValueCtrl::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIKnobCtrl::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIValueCtrl::Read(s, mgr); + + fAnimationKeys.Reset(); + UInt32 i, count = s->ReadSwap32(); + for( i = 0; i < count; i++ ) + fAnimationKeys.Append( mgr->ReadKey( s ) ); + fAnimName = s->ReadSafeString(); + + fAnimTimesCalced = false; + + fAnimStartPos.Read( s ); + fAnimEndPos.Read( s ); +} + +void pfGUIKnobCtrl::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIValueCtrl::Write( s, mgr ); + + UInt32 i, count = fAnimationKeys.GetCount(); + s->WriteSwap32( count ); + for( i = 0; i < count; i++ ) + mgr->WriteKey( s, fAnimationKeys[ i ] ); + s->WriteSafeString( fAnimName ); + + fAnimStartPos.Write( s ); + fAnimEndPos.Write( s ); +} + +//// UpdateBounds //////////////////////////////////////////////////////////// + +void pfGUIKnobCtrl::UpdateBounds( hsMatrix44 *invXformMatrix, hsBool force ) +{ + pfGUIValueCtrl::UpdateBounds( invXformMatrix, force ); + if( fAnimationKeys.GetCount() > 0 ) + fBoundsValid = false; +} + +//// HandleMouseDown/Up ////////////////////////////////////////////////////// + +void pfGUIKnobCtrl::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) +{ + fDragStart = mousePt; + fDragValue = fValue; + fDragging = true; + + if( HasFlag( kMapToAnimationRange ) ) + { + hsPoint3 scrnStart, scrnEnd; + + // At mouse-down, we take our local-space start and end points and + // translate them by our parent object's local-to-world to get the + // right points in world-space. We do this now because our parent + // might be animated, which could complicate matters a tad. + scrnStart = fAnimStartPos; + scrnEnd = fAnimEndPos; + + plSceneObject *target = GetTarget(); + if( target != nil ) + { + const plCoordinateInterface *ci = target->GetCoordinateInterface(); + if( ci != nil ) + { + const plCoordinateInterface *parentCI = ci->GetParent(); + if( parentCI != nil ) + { + const hsMatrix44 &parentLocalToWorld = parentCI->GetLocalToWorld(); + + scrnStart = parentLocalToWorld * scrnStart; + scrnEnd = parentLocalToWorld * scrnEnd; + } + } + } + + scrnStart = fDialog->WorldToScreenPoint( scrnStart ); + scrnEnd = fDialog->WorldToScreenPoint( scrnEnd ); + + if( HasFlag( kLeftRightOrientation ) ) + { + fDragRangeMin = scrnStart.fX; + fDragRangeMax = scrnEnd.fX; + } + else + { + fDragRangeMin = scrnStart.fY; + fDragRangeMax = scrnEnd.fY; + } + } + else if( HasFlag( kMapToScreenRange ) ) + { + fDragRangeMin = 0.f; + fDragRangeMax = 1.f; + } + else + fDragRangeMin = -1; +} + +void pfGUIKnobCtrl::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) +{ + fDragging = false; + HandleMouseDrag( mousePt, modifiers ); +} + +void pfGUIKnobCtrl::HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ) +{ + hsScalar oldValue = fValue, newValue = fDragValue; + + if( fDragRangeMin != -1 ) + { + if( HasFlag( kLeftRightOrientation ) ) + { + if( mousePt.fX < fDragRangeMin ) + newValue = fMin; + else if( mousePt.fX > fDragRangeMax ) + newValue = fMax; + else + newValue = ( ( mousePt.fX - fDragRangeMin ) / ( fDragRangeMax - fDragRangeMin ) ) * + ( fMax - fMin ) + fMin; + } + else + { + if( mousePt.fY > fDragRangeMin ) + newValue = fMin; + else if( mousePt.fY < fDragRangeMax ) + newValue = fMax; + else + newValue = ( ( fDragRangeMin - mousePt.fY) / ( fDragRangeMin - fDragRangeMax ) ) * + ( fMax - fMin ) + fMin; + } + + if( HasFlag( kReverseValues ) ) + SetCurrValue( fMax - ( newValue - fMin ) ); + else + SetCurrValue( newValue ); + } + else + { + hsScalar diff; + if( HasFlag( kLeftRightOrientation ) ) + diff = ( mousePt.fX - fDragStart.fX ) * 20.f; + else + diff = ( fDragStart.fY - mousePt.fY ) * 20.f; + + if( HasFlag( kReverseValues ) ) + SetCurrValue( fDragValue - diff ); + else + SetCurrValue( fDragValue + diff ); + } + + // !fDragging = We're mousing-up, so if we're still dragging, we need to not have the only- + // on-mouse-up flag set. Just FYI + if( !fDragging || !HasFlag( kTriggerOnlyOnMouseUp ) ) + DoSomething(); +} + +//// SetAnimationKeys //////////////////////////////////////////////////////// + +void pfGUIKnobCtrl::SetAnimationKeys( hsTArray &keys, const char *name ) +{ + fAnimationKeys = keys; + delete [] fAnimName; + if( name != nil ) + { + fAnimName = TRACKED_NEW char[ strlen( name ) + 1 ]; + strcpy( fAnimName, name ); + } + else + fAnimName = nil; +} + +//// ICalcAnimTimes ////////////////////////////////////////////////////////// +// Loops through and computes the max begin and end for our animations. If +// none of them are loaded and we're not already calced, returns false. + +hsBool pfGUIKnobCtrl::ICalcAnimTimes( void ) +{ + if( fAnimTimesCalced ) + return true; + + hsScalar tBegin = 1e30, tEnd = -1e30; + bool foundOne = false; + + for( int i = 0; i < fAnimationKeys.GetCount(); i++ ) + { + // Handle AGMasterMods + plAGMasterMod *mod = plAGMasterMod::ConvertNoRef( fAnimationKeys[ i ]->ObjectIsLoaded() ); + if( mod != nil ) + { + for( int j = 0; j < mod->GetNumAnimations(); j++ ) + { + hsScalar begin = mod->GetAnimInstance( j )->GetTimeConvert()->GetBegin(); + hsScalar end = mod->GetAnimInstance( j )->GetTimeConvert()->GetEnd(); + if( begin < tBegin ) + tBegin = begin; + if( end > tEnd ) + tEnd = end; + } + foundOne = true; + } + // Handle layer animations + plLayerAnimation *layer = plLayerAnimation::ConvertNoRef( fAnimationKeys[ i ]->ObjectIsLoaded() ); + if( layer != nil ) + { + hsScalar begin = layer->GetTimeConvert().GetBegin(); + hsScalar end = layer->GetTimeConvert().GetEnd(); + if( begin < tBegin ) + tBegin = begin; + if( end > tEnd ) + tEnd = end; + foundOne = true; + } + } + + if( foundOne ) + { + fAnimBegin = tBegin; + fAnimEnd = tEnd; + + fAnimTimesCalced = true; + } + + return fAnimTimesCalced; +} + +//// SetCurrValue //////////////////////////////////////////////////////////// + +void pfGUIKnobCtrl::SetCurrValue( hsScalar v ) +{ + int old = (int)fValue; + pfGUIValueCtrl::SetCurrValue( v ); + +// if( old == (int)fValue ) +// return; + + if( fAnimationKeys.GetCount() > 0 ) + { + ICalcAnimTimes(); + + hsScalar tLength = fAnimEnd - fAnimBegin; + hsScalar newTime = fMin; + + if (fMin != fMax) // Protect against div by zero + { + if( HasFlag( kReverseValues ) ) + newTime = ( ( fMax - fValue ) / ( fMax - fMin ) ) * tLength + fAnimBegin; + else + newTime = ( ( fValue - fMin ) / ( fMax - fMin ) ) * tLength + fAnimBegin; + } + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + msg->SetCmd( plAnimCmdMsg::kGoToTime ); + msg->SetAnimName( fAnimName ); + msg->fTime = newTime; + msg->AddReceivers( fAnimationKeys ); + plgDispatch::MsgSend( msg ); + } +} + +//// IGetDesiredCursor /////////////////////////////////////////////////////// + +UInt32 pfGUIKnobCtrl::IGetDesiredCursor( void ) const +{ + if( HasFlag( kLeftRightOrientation ) ) + { + if( fDragging ) + return plInputInterface::kCursorLeftRightDragging; + + return plInputInterface::kCursorLeftRightDraggable; + } + else + { + if( fDragging ) + return plInputInterface::kCursorUpDownDragging; + + return plInputInterface::kCursorUpDownDraggable; + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIKnobCtrl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIKnobCtrl.h new file mode 100644 index 00000000..309d3510 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIKnobCtrl.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIKnobCtrl Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIKnobCtrl_h +#define _pfGUIKnobCtrl_h + +#include "pfGUIValueCtrl.h" + +class plMessage; +class plAGMasterMod; + +class pfGUIKnobCtrl : public pfGUIValueCtrl +{ + protected: + + hsTArray fAnimationKeys; + char *fAnimName; + + hsPoint3 fDragStart; + hsScalar fDragValue; + hsBool fDragging; + + hsPoint3 fAnimStartPos, fAnimEndPos; // Calculated at export time for kMapToScreenRange + hsScalar fDragRangeMin, fDragRangeMax; + + // Computed once, once an anim is loaded that we can compute this with + hsScalar fAnimBegin, fAnimEnd; + hsBool fAnimTimesCalced; + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + virtual UInt32 IGetDesiredCursor( void ) const; // As specified in plInputInterface.h + + hsBool ICalcAnimTimes( void ); + + public: + + pfGUIKnobCtrl(); + virtual ~pfGUIKnobCtrl(); + + CLASSNAME_REGISTER( pfGUIKnobCtrl ); + GETINTERFACE_ANY( pfGUIKnobCtrl, pfGUIValueCtrl ); + + + enum OurFlags + { + kReverseValues = kDerivedFlagsStart, + kLeftRightOrientation, + kMapToScreenRange, + kTriggerOnlyOnMouseUp, + kMapToAnimationRange + }; + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ); + + virtual void UpdateBounds( hsMatrix44 *invXformMatrix = nil, hsBool force = false ); + + virtual void SetCurrValue( hsScalar v ); + + // Export only + void SetAnimationKeys( hsTArray &keys, const char *name ); + void SetScreenRange( const hsPoint3 &startPos, const hsPoint3 &endPos ) { fAnimStartPos = startPos; fAnimEndPos = endPos; } +}; + +#endif // _pfGUIKnobCtrl_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListBoxMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListBoxMod.cpp new file mode 100644 index 00000000..41f5c688 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListBoxMod.cpp @@ -0,0 +1,1196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIListBoxMod Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIListBoxMod.h" +#include "pfGUIListElement.h" +#include "pfGameGUIMgr.h" +#include "pfGUIUpDownPairMod.h" +#include "pfGUIControlHandlers.h" +#include "pfGUIDialogMod.h" + +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plAvatar/plAGModifier.h" +#include "../plGImage/plDynamicTextMap.h" +#include "../plInputCore/plInputInterface.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + + +#define kIndentAmount 16 + + +//// plSmallRect Stuff /////////////////////////////////////////////////////// + +void pfGUIListBoxMod::plSmallRect::Set( Int16 l, Int16 t, Int16 r, Int16 b ) +{ + fLeft = l; + fTop = t; + fRight = r; + fBottom = b; +} + +hsBool pfGUIListBoxMod::plSmallRect::Contains( Int16 x, Int16 y ) +{ + if( x < fLeft || x > fRight || y < fTop || y > fBottom ) + return false; + + return true; +} + +//// Wee Little Control Proc for scrolling /////////////////////////////////// + +class pfScrollProc : public pfGUICtrlProcObject +{ + protected: + + pfGUIListBoxMod *fParent; + + public: + + pfScrollProc( pfGUIListBoxMod *parent ) : fParent( parent ) {} + + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + // Do a check here to make sure we actually changed scroll + // positions--if not, we don't want to update, since that'll be + // slow as hell + int newScrollPos = (int)fParent->fScrollControl->GetMax() - (int)fParent->fScrollControl->GetCurrValue(); + if( newScrollPos != fParent->fScrollPos ) + { + fParent->IUpdate(); + fParent->HandleExtendedEvent( pfGUIListBoxMod::kScrollPosChanged ); + } + } +}; + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIListBoxMod::pfGUIListBoxMod() +{ + SetFlag( kWantsInterest ); + fCurrClick = -1; + fCurrHover = -1; + fModsAtDragTime = 0; + fScrollControl = nil; + fCheckScroll = false; + fSingleSelElement = -1; + fScrollRangeUpdateDeferred = false; + fScrollPos = 0; + fLocked = false; + fReadyToRoll = false; + fClicking = false; + + fScrollProc = nil; +} + +pfGUIListBoxMod::~pfGUIListBoxMod() +{ + ClearAllElements(); + + if( fScrollProc && fScrollProc->DecRef() ) + delete fScrollProc; +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIListBoxMod::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIControlMod::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIListBoxMod::MsgReceive( plMessage *msg ) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); + if( refMsg != nil ) + { + if( refMsg->fType == kRefScrollCtrl ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fScrollControl = pfGUIValueCtrl::ConvertNoRef( refMsg->GetRef() ); + fScrollControl->SetHandler( fScrollProc ); + fScrollControl->SetStep( 1.f ); + ICalcScrollRange(); + } + else + fScrollControl = nil; + return true; + } + else if( refMsg->fType == kRefDynTextMap ) + { + // Capture this so we can reset our flag, but do NOT just return from it! + if( !( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) ) + fReadyToRoll = false; + } + } + + return pfGUIControlMod::MsgReceive( msg ); +} + +//// IPostSetUpDynTextMap //////////////////////////////////////////////////// + +void pfGUIListBoxMod::IPostSetUpDynTextMap( void ) +{ + ICalcWrapStarts(); + ICalcScrollRange(); + fReadyToRoll = true; +} + +//// IUpdate ///////////////////////////////////////////////////////////////// + +void pfGUIListBoxMod::IUpdate( void ) +{ + int i, j; + UInt16 x, y, width, height, maxHeight; + + + if( !fReadyToRoll ) + return; + if( fScrollRangeUpdateDeferred ) + { + fScrollRangeUpdateDeferred = false; + ICalcScrollRange(); + } + + fDynTextMap->ClearToColor( GetColorScheme()->fBackColor ); + if( fElements.GetCount() == 0 ) + { + fDynTextMap->FlushToHost(); + return; + } + + if( fLocked ) + { + hsStatusMessage( "WARNING: Forcing unlock on GUI list box due to call to IUpdate()\n" ); + UnlockList(); + } + + if( fScrollControl != nil ) + { + // We reverse the value, since we want the "up" button to scroll "up", which actually + // *decreases* the scrollPos + fScrollPos = (int)fScrollControl->GetMax() - (int)fScrollControl->GetCurrValue(); + + if( fCheckScroll ) + { + // If this is set, we just did something to change the selection to a single item. + // Since it makes sense to try to ensure this item is on the screen, we thus do so here. + // We don't want to do this every time, because we could be simply clicking on the + // scroll bars, in which case we DON'T want to do this check, since it would be + // fighting against the user, which is a big UI design no-no. + // To do this check, we search on either side of our scroll range (we can only + // search past after we draw, unfortunately). If there's a selected item out + // of our range, then we best move the scrollPos to move it into view + for( j = 0; j < fScrollPos; j++ ) + { + int end = ( j < fWrapStartIdxs.GetCount() - 1 ) ? fWrapStartIdxs[ j + 1 ] : fElements.GetCount(); + + hsBool anySelected = false; + for( i = fWrapStartIdxs[ j ]; i < end; i++ ) + anySelected |= fElements[ i ]->IsSelected(); + + if( anySelected ) + { + // Shit. Move the scrollPos up to this item at the very least + fScrollPos = j; + fScrollControl->SetCurrValue( (hsScalar)( (int)fScrollControl->GetMax() - fScrollPos ) ); + fCheckScroll = false; + break; + } + } + } + } + else + fScrollPos = 0; + + fElementBounds.SetCountAndZero( fElements.GetCount() ); + + if( !HasFlag( kScrollLeftToRight ) ) + { + for( j = fScrollPos, y = 4; j < fWrapStartIdxs.GetCount() && y < fDynTextMap->GetVisibleHeight() - 8; j++ ) + { + int end = ( j < fWrapStartIdxs.GetCount() - 1 ) ? fWrapStartIdxs[ j + 1 ] : fElements.GetCount(); + + for( maxHeight = 0, i = fWrapStartIdxs[ j ], x = 0; i < end; i++ ) + { + if( HasFlag( kGrowLeavesAndProcessOxygen ) && fElements[ i ]->IsCollapsed() ) + continue; // Skip collapsed items + + fElements[ i ]->GetSize( fDynTextMap, &width, &height ); + + if( !HasFlag( kAllowMultipleElementsPerRow ) ) + width = fDynTextMap->GetVisibleWidth(); + + if( y + height >= fDynTextMap->GetVisibleHeight() - 8 ) + height = fDynTextMap->GetVisibleHeight() - 8 - y; + + if( HasFlag( kGrowLeavesAndProcessOxygen ) ) + { + x += fElements[ i ]->GetIndentLevel() * kIndentAmount; + if( x + width > fDynTextMap->GetVisibleWidth() ) + width = fDynTextMap->GetVisibleWidth() - x; + } + + if( !fElements[ i ]->Draw( fDynTextMap, x, y, width, height ) ) + { + // Couldn't draw, so force it to be at the end of the scroll range + y = fDynTextMap->GetVisibleHeight() - 8; + j--; + break; + } + + maxHeight = ( height > maxHeight ) ? height : maxHeight; + fElementBounds[ i ].Set( x, y, x + width - 1, y + height - 1 ); + x += width; + } + + y += maxHeight; + } + } + else + { + for( j = fScrollPos, x = 4; j < fWrapStartIdxs.GetCount() && x < fDynTextMap->GetVisibleWidth() - 8; j++ ) + { + int end = ( j < fWrapStartIdxs.GetCount() - 1 ) ? fWrapStartIdxs[ j + 1 ] : fElements.GetCount(); + + for( maxHeight = 0, i = fWrapStartIdxs[ j ], y = 0; i < end; i++ ) + { + fElements[ i ]->GetSize( fDynTextMap, &width, &height ); + + if( !HasFlag( kAllowMultipleElementsPerRow ) ) + height = fDynTextMap->GetVisibleHeight(); + + if( x + width >= fDynTextMap->GetVisibleWidth() - 8 ) + width = fDynTextMap->GetVisibleWidth() - 8 - x; + + if( !fElements[ i ]->Draw( fDynTextMap, x, y, width, height ) ) + { + // Couldn't draw, so force it to be at the end of the scroll range + x = fDynTextMap->GetVisibleWidth() - 8; + j--; + break; + } + + maxHeight = ( width > maxHeight ) ? width : maxHeight; + fElementBounds[ i ].Set( x, y, x + width - 1, y + height - 1 ); + y += height; + } + + x += maxHeight; + } + } + + // Second part of our scroll check--if we got here, then there was nothing selected + // before the viewing area. So check the rest + if( fCheckScroll && fScrollControl != nil ) + { + fCheckScroll = false; + for( ; j < fWrapStartIdxs.GetCount(); j++ ) + { + int end = ( j < fWrapStartIdxs.GetCount() - 1 ) ? fWrapStartIdxs[ j + 1 ] : fElements.GetCount(); + + hsBool anySelected = false; + for( i = fWrapStartIdxs[ j ]; i < end; i++ ) + anySelected |= fElements[ i ]->IsSelected(); + + if( anySelected ) + { + fScrollPos = j; + fScrollControl->SetCurrValue( (hsScalar)( (int)fScrollControl->GetMax() - fScrollPos ) ); + IUpdate(); // Gotta update again, since we just changed the scrollPos after the fact + return; + } + } + } + + fDynTextMap->FlushToHost(); +} + +//// ICalcWrapStarts ///////////////////////////////////////////////////////// +// Calculates the starting indexes for each row of items. Call whenever you +// update the element list. + +void pfGUIListBoxMod::ICalcWrapStarts( void ) +{ + UInt16 i, x, y, maxHeight, width, height; + + + fWrapStartIdxs.Reset(); + + if( HasFlag( kAllowMultipleElementsPerRow ) ) + { + if( !HasFlag( kScrollLeftToRight ) ) + { + for( i = 0, x = (UInt16)-1, y = 4, maxHeight = 0; i < fElements.GetCount(); i++ ) + { + fElements[ i ]->GetSize( fDynTextMap, &width, &height ); + + if( x + width >= fDynTextMap->GetVisibleWidth() ) + { + x = 0; + y += maxHeight; + maxHeight = 0; + fWrapStartIdxs.Append( i ); + } + x += width; + maxHeight = ( height > maxHeight ) ? height : maxHeight; + } + } + else + { + for( i = 0, y = (UInt16)-1, x = 4, maxHeight = 0; i < fElements.GetCount(); i++ ) + { + fElements[ i ]->GetSize( fDynTextMap, &width, &height ); + if( y + height >= fDynTextMap->GetVisibleHeight() ) + { + y = 0; + x += maxHeight; + maxHeight = 0; + fWrapStartIdxs.Append( i ); + } + y += height; + maxHeight = ( width > maxHeight ) ? width : maxHeight; + } + } + } + else + { + for( i = 0; i < fElements.GetCount(); i++ ) + { + if( HasFlag( kGrowLeavesAndProcessOxygen ) && fElements[ i ]->IsCollapsed() ) + continue; + + fWrapStartIdxs.Append( i ); + } + } +} + +//// ICalcScrollRange //////////////////////////////////////////////////////// +// Call whenever you update the element list + +void pfGUIListBoxMod::ICalcScrollRange( void ) +{ + UInt16 ctrlHt, ctrlWd, height, width, maxHeight; + int i, j, end; + + + if( !fReadyToRoll ) + { + fScrollRangeUpdateDeferred = true; + return; + } + + // Basically, the scroll range is 0 to (numElements-1), but since if we scroll + // that far, we won't see anything left except the last element, we have to calculate + // how many elements away from the end we can get before we see blank space. Which means + // counting up from the end until we have passed the height of our control. + + end = fElements.GetCount(); + if( !HasFlag( kScrollLeftToRight ) ) + { + if( HasFlag( kGrowLeavesAndProcessOxygen ) ) + { + // OK, that really isn't the end, the end will the count of non-collapsed elements. Haha. + for( i = 0, end = 0; i < fElements.GetCount(); i++ ) + { + if( !fElements[ i ]->IsCollapsed() ) + end++; + } + } + + for( i = fWrapStartIdxs.GetCount() - 1, height = 0; i >= 0; i-- ) + { + // Get the max height of this row + maxHeight = 0; + for( j = fWrapStartIdxs[ i ]; j < end; j++ ) + { + fElements[ j ]->GetSize( fDynTextMap, &width, &ctrlHt ); + maxHeight = ( ctrlHt > maxHeight ) ? ctrlHt : maxHeight; + } + end = fWrapStartIdxs[ i ]; + height += maxHeight; + + if( height > fDynTextMap->GetVisibleHeight() - 8 ) + break; + } + } + else + { + for( i = fWrapStartIdxs.GetCount() - 1, width = 0; i >= 0; i-- ) + { + // Get the max width of this column + maxHeight = 0; + for( j = fWrapStartIdxs[ i ]; j < end; j++ ) + { + fElements[ j ]->GetSize( fDynTextMap, &ctrlWd, &ctrlHt ); + maxHeight = ( ctrlWd > maxHeight ) ? ctrlWd : maxHeight; + } + end = fWrapStartIdxs[ i ]; + width += maxHeight; + + if( width > fDynTextMap->GetVisibleWidth() - 8 ) + break; + } + } + + // Note: i went one past, so our scroll range is really 0 to i+1 + if( fScrollControl ) + { + // Because we reverse the position on draw, we have to do some special trick here + // to make sure the reversed position stays the same + fScrollPos = (int)fScrollControl->GetMax() - (int)fScrollControl->GetCurrValue(); + if( fScrollPos >= fWrapStartIdxs.GetCount() ) + fScrollPos = fWrapStartIdxs.GetCount() - 1; + + if( i < 0 ) + // Smaller than the viewing area, so we don't scroll at all + fScrollControl->SetRange( 0.f, 0.f ); + else + fScrollControl->SetRange( 0.f, (hsScalar)( i + 1 ) ); + + fScrollControl->SetCurrValue( (hsScalar)( (int)fScrollControl->GetMax() - fScrollPos ) ); + } +} + +void pfGUIListBoxMod::PurgeDynaTextMapImage() +{ + if ( fDynTextMap != nil ) + fDynTextMap->PurgeImage(); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIListBoxMod::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Read(s, mgr); + + fScrollControl = nil; + if( s->ReadBool() ) + { + fScrollProc = TRACKED_NEW pfScrollProc( this ); + fScrollProc->IncRef(); + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefScrollCtrl ), plRefFlags::kActiveRef ); + } + + if( HasFlag( kDisableSelection ) ) + ClearFlag( kWantsInterest ); + +} + +void pfGUIListBoxMod::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Write( s, mgr ); + + if( fScrollControl != nil ) + { + s->WriteBool( true ); + mgr->WriteKey( s, fScrollControl->GetKey() ); + } + else + s->WriteBool( false ); +} + +//// FilterMousePosition ///////////////////////////////////////////////////// +// Tests the mouse point and decides whether we still want to accept input +// based on the position. This allows us to act etheral (i.e. pass mouse +// messages through) when the mouse is over an empty portion of the list. + +hsBool pfGUIListBoxMod::FilterMousePosition( hsPoint3 &mousePt ) +{ + if( !HasFlag( kAllowMousePassThrough ) ) + return true; + + Int32 hover = fCurrClick = IGetItemFromPoint( mousePt ); + if( hover == -1 ) + return false; + + return true; +} + +//// HandleMouseDown ///////////////////////////////////////////////////////// +// What we do: normal click deselects all and selects the item clicked on +// (if any). Shift-click and ctrl-click avoids the deselect and toggles +// the item clicked on. + +void pfGUIListBoxMod::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) +{ + Int32 i; + + int lastSelection = -1; + if (HasFlag(kForbidNoSelection)) + { + // grab the last item we had selected just in case they clicked outside the list of selectable objects + for (i=0; iIsSelected()) + { + lastSelection = i; + break; + } + } + + if( HasFlag(kSingleSelect) || ( !( modifiers & ( pfGameGUIMgr::kShiftDown | pfGameGUIMgr::kCtrlDown ) ) && !HasFlag( kHandsOffMultiSelect ) ) ) + { + // Deselect everyone! + for( i = 0; i < fElements.GetCount(); i++ ) + fElements[ i ]->SetSelected( false ); + fSingleSelElement = -1; + } + else if( modifiers & pfGameGUIMgr::kShiftDown ) + { + IFindSelectionRange( &fMinSel, &fMaxSel ); + } + + fCurrHover = fCurrClick = IGetItemFromPoint( mousePt ); + if( fCurrClick != -1 ) + { + if( ( ( modifiers & pfGameGUIMgr::kShiftDown ) && !HasFlag( kSingleSelect ) ) ) + { + // Select our new range, deselect everything outside + if( fCurrClick <= fMaxSel ) + { + ISelectRange( 0, (Int8)(fCurrClick - 1), false ); + ISelectRange( (Int8)fCurrClick, (Int8)fMaxSel, true ); + ISelectRange( (Int8)(fMaxSel + 1), -1, false ); + } + else if( fCurrClick >= fMinSel ) + { + ISelectRange( 0, (Int8)(fMinSel - 1), false ); + ISelectRange( (Int8)fMinSel, (Int8)fCurrClick, true ); + ISelectRange( (Int8)(fCurrClick + 1), -1, false ); + } + fElements[ fCurrClick ]->SetSelected( true ); + } + else + { + fElements[ fCurrClick ]->SetSelected( true ); + fSingleSelElement = fCurrClick; + } + } + else + { + if (HasFlag(kForbidNoSelection) && (lastSelection != -1)) + { + fElements[ lastSelection ]->SetSelected(true); + fSingleSelElement = lastSelection; + } + } + + // We want drag/up events to be processed with the modifiers we had on + // mouse down, not the current ones. So store 'em for reference + fModsAtDragTime = modifiers; + fClicking = true; + IUpdate(); +} + +//// HandleMouseUp /////////////////////////////////////////////////////////// + +void pfGUIListBoxMod::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) +{ + fClicking = false; + if( !( fModsAtDragTime & ( pfGameGUIMgr::kShiftDown | pfGameGUIMgr::kCtrlDown ) ) && !HasFlag( kHandsOffMultiSelect ) ) + { + // No modifiers--simply select whatever item we're on + Int32 item = IGetItemFromPoint( mousePt ); + if( item != fCurrClick ) + { + if( fCurrClick >= 0 && fCurrClick < fElements.GetCount() ) + fElements[ fCurrClick ]->SetSelected( false ); + fCurrHover = fCurrClick = item; + if( fCurrClick >= 0 && fCurrClick < fElements.GetCount() ) + fElements[ fCurrClick ]->SetSelected( true ); + else if (HasFlag(kForbidNoSelection) && fCurrClick == -1) + { + fElements[fSingleSelElement]->SetSelected(true); + fCurrClick = fSingleSelElement; + } + fCheckScroll = true; + fSingleSelElement = fCurrClick; + IUpdate(); + } + else + { + // We didn't change selection, so go ahead and pass the click on to + // the item, in case it wants to do something + if( fCurrClick >= 0 && fCurrClick < fElements.GetCount() ) + { + hsPoint3 localPt = mousePt; + IScreenToLocalPt( localPt ); + + UInt16 lX = (UInt16)(( localPt.fX * ( fDynTextMap->GetVisibleWidth() - 1 ) ) - fElementBounds[ fCurrClick ].fLeft); + UInt16 lY = (UInt16)(( localPt.fY * ( fDynTextMap->GetVisibleHeight() - 1 ) )- fElementBounds[ fCurrClick ].fTop); + + if( fElements[ fCurrClick ]->MouseClicked( lX, lY ) ) + { + ICalcWrapStarts(); + ICalcScrollRange(); + IUpdate(); + } + } + } + } + + DoSomething(); // Change in selection triggers our DoSomething() call +} + +//// HandleMouseHover //////////////////////////////////////////////////////// +// Just updates our currHover item so we can update the mouse appropriately + +void pfGUIListBoxMod::HandleMouseHover( hsPoint3 &mousePt, UInt8 modifiers ) +{ + fCurrHover = IGetItemFromPoint( mousePt ); +} + +//// HandleMouseDrag ///////////////////////////////////////////////////////// + +void pfGUIListBoxMod::HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ) +{ + int i; + + + if( ( fModsAtDragTime & pfGameGUIMgr::kShiftDown && !HasFlag( kSingleSelect ) ) ) + { + // Select our new range, deselect everything outside + if( fCurrClick <= fMaxSel ) + { + ISelectRange( 0, (Int8)(fCurrClick - 1), false ); + ISelectRange( (Int8)fCurrClick, (Int8)fMaxSel, true ); + ISelectRange( (Int8)(fMaxSel + 1), -1, false ); + } + else if( fCurrClick >= fMinSel ) + { + ISelectRange( 0, (Int8)(fMinSel - 1), false ); + ISelectRange( (Int8)fMinSel, (Int8)fCurrClick, true ); + ISelectRange( (Int8)(fCurrClick + 1), -1, false ); + } + IUpdate(); + } + else if( !( fModsAtDragTime & ( pfGameGUIMgr::kShiftDown | pfGameGUIMgr::kCtrlDown ) ) ) + { + // No modifiers--simply select whatever item we're on + if( HasFlag( kDragAndDropCapable ) ) + { + // If we're drag and drop capable, then a mouse drag function results in + // the dialog entering Drag Mode(tm). Basically, we tell our parent dialog + // which items to drag and it'll run with the rest + fDialog->ClearDragList(); + for( i = 0; i < fElements.GetCount(); i++ ) + { + if( fElements[ i ]->IsSelected() ) + fDialog->AddToDragList( fElements[ i ] ); + } + fDialog->EnterDragMode( this ); + } + else + { + Int32 item = IGetItemFromPoint( mousePt ); + if( item != fCurrClick ) + { + if( fCurrClick >= 0 && fCurrClick < fElements.GetCount() ) + fElements[ fCurrClick ]->SetSelected( false ); + fCurrHover = fCurrClick = item; + if( fCurrClick >= 0 && fCurrClick < fElements.GetCount() ) + fElements[ fCurrClick ]->SetSelected( true ); + fCheckScroll = true; + fSingleSelElement = fCurrClick; + IUpdate(); + } + } + } +} + +//// HandleMouseDblClick ///////////////////////////////////////////////////// + +void pfGUIListBoxMod::HandleMouseDblClick( hsPoint3 &mousePt, UInt8 modifiers ) +{ + if( !HasFlag( kGrowLeavesAndProcessOxygen ) ) + return; // We only care about double clicks if we make oxygen + + Int32 item = IGetItemFromPoint( mousePt ); + if( item != -1 ) + { + if( fElements[ item ]->GetType() == pfGUIListElement::kTreeRoot ) + { + pfGUIListTreeRoot *root = (pfGUIListTreeRoot *)fElements[ item ]; + + root->ShowChildren( !root->IsShowingChildren() ); + if( !fLocked ) + { + ICalcWrapStarts(); + ICalcScrollRange(); + IUpdate(); + } + } + } +} + +//// IGetItemFromPoint /////////////////////////////////////////////////////// + +Int32 pfGUIListBoxMod::IGetItemFromPoint( hsPoint3 &mousePt ) +{ + if( !fBounds.IsInside( &mousePt ) ) + return -1; + + hsPoint3 localPt = mousePt; // despite getting a ref to the point (why?) we do NOT want to modify it + IScreenToLocalPt( localPt ); + UInt32 i; + Int16 clickItem, clickY = (Int16)( localPt.fY * ( fDynTextMap->GetVisibleHeight() - 1 ) ); + Int16 clickX = (Int16)( localPt.fX * ( fDynTextMap->GetVisibleWidth() - 1 ) ); + + // We have a nice array that has the starting (top) Y's of each visible element. So we just + // check against that. + UInt32 startAt = 0; + // make sure that we have a valid fScrollPos + if ( fScrollPos != -1 ) + { + // make sure that there is an Idx at fScrollPos + if ( fWrapStartIdxs.GetCount() > fScrollPos ) + { + startAt = fWrapStartIdxs[ fScrollPos ]; + clickItem = -1; + } + } + for( i = startAt; i < fElements.GetCount(); i++ ) + { + if( i fElements.GetCount() - 1 || clickItem < 0 ) + clickItem = -1; + + return clickItem; +} + +//// IFindSelectionRange ///////////////////////////////////////////////////// +// Find the min and max item that's selected. Returns -1 for both if nobody +// is selected + +void pfGUIListBoxMod::IFindSelectionRange( Int32 *min, Int32 *max ) +{ + Int32 i; + + + *min = *max = -1; + + for( i = 0; i < fElements.GetCount(); i++ ) + { + if( fElements[ i ]->IsSelected() ) + { + *min = i; + break; + } + } + + for( i = fElements.GetCount() - 1; i >= 0; i-- ) + { + if( fElements[ i ]->IsSelected() ) + { + *max = i; + break; + } + } +} + +//// ISelectRange //////////////////////////////////////////////////////////// + +void pfGUIListBoxMod::ISelectRange( Int8 min, Int8 max, hsBool select ) +{ + Int16 i; + + + if( max == -1 ) + max = fElements.GetCount() - 1; + + for( i = min; i <= max; i++ ) + fElements[ i ]->SetSelected( select ); +} + +//// SetSelection //////////////////////////////////////////////////////////// + +void pfGUIListBoxMod::SetSelection( Int32 item ) +{ + if( HasFlag( kSingleSelect ) ) + { + // Easy, only one item selected + if( fSingleSelElement != -1 ) + fElements[ fSingleSelElement ]->SetSelected( false ); + + fSingleSelElement = item; + } + else + ISelectRange( 0, -1, false ); + + if( item != -1 && item < fElements.GetCount() ) + fElements[ item ]->SetSelected( true ); + + fCheckScroll = true; + IUpdate(); +} + +void pfGUIListBoxMod::RemoveSelection( Int32 item ) +{ + // make sure the item is valid + if ( item != -1 && item < fElements.GetCount() ) + { + // if single select and its what is selected + if ( HasFlag( kSingleSelect) && fSingleSelElement == item ) + { + // then remove the selection and make nothing selected + fElements[ fSingleSelElement ]->SetSelected( false ); + fSingleSelElement = item; + } + // else if multiple selection + else + // just remove the selection + fElements[ item ]->SetSelected( false ); + } + fCheckScroll = true; + IUpdate(); +} + +void pfGUIListBoxMod::AddSelection( Int32 item ) +{ + // make sure the item is valid (can't add a non-selection!) + if ( item != -1 && item < fElements.GetCount() ) + { + // if single select then just like SetSelection + if ( HasFlag( kSingleSelect) ) + { + SetSelection(item); + } + // else if multiple selection + else + // just set its selection + fElements[ item ]->SetSelected( true ); + } + fCheckScroll = true; + IUpdate(); +} + +//// HandleKeyPress ////////////////////////////////////////////////////////// + +hsBool pfGUIListBoxMod::HandleKeyPress( char key, UInt8 modifiers ) +{ + return false; +} + +hsBool pfGUIListBoxMod::HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers ) +{ + if( key == KEY_CAPSLOCK ) + return false; + + if( HasFlag( kDisableKeyActions ) ) + return false; + + if( event == pfGameGUIMgr::kKeyDown || event == pfGameGUIMgr::kKeyRepeat ) + { + // Use arrow keys to do our dirty work + if( key == KEY_UP ) + { + if( fCurrClick == -1 && fElements.GetCount() > 0 ) + fCurrClick = 0; + else while( fCurrClick > 0 ) + { + fCurrClick--; + if( !HasFlag( kGrowLeavesAndProcessOxygen ) || !fElements[ fCurrClick ]->IsCollapsed() ) + break; + } + } + else if( key == KEY_DOWN ) + { + if( fCurrClick == -1 && fElements.GetCount() > 0 ) + fCurrClick = fElements.GetCount() - 1; + else while( fCurrClick < fElements.GetCount() - 1 ) + { + fCurrClick++; + if( !HasFlag( kGrowLeavesAndProcessOxygen ) || !fElements[ fCurrClick ]->IsCollapsed() ) + break; + } + } + else if( key == KEY_HOME ) + { + if( fElements.GetCount() > 0 ) + fCurrClick = 0; + } + else if( key == KEY_END ) + { + if( fElements.GetCount() > 0 ) + fCurrClick = fElements.GetCount() - 1; + } + else if( key == KEY_ENTER && HasFlag( kGrowLeavesAndProcessOxygen ) ) + { + if( fCurrClick != -1 && fElements[ fCurrClick ]->GetType() == pfGUIListElement::kTreeRoot ) + { + pfGUIListTreeRoot *root = (pfGUIListTreeRoot *)fElements[ fCurrClick ]; + root->ShowChildren( !root->IsShowingChildren() ); + + if( !fLocked ) + { + ICalcWrapStarts(); + ICalcScrollRange(); + } + } + } + else + { + return false; + } + + ISelectRange( 0, -1, false ); + if( fCurrClick != -1 ) + fElements[ fCurrClick ]->SetSelected( true ); + fSingleSelElement = fCurrClick; + + DoSomething(); // Change in selection triggers our DoSomething() call + + fCheckScroll = true; + IUpdate(); + return true; + } + + return false; +} + +//// ScrollToEnd ///////////////////////////////////////////////////////////// + +void pfGUIListBoxMod::ScrollToEnd( void ) +{ + if( fScrollControl != nil ) + { + fScrollPos = (int)fScrollControl->GetMin(); + fScrollControl->SetCurrValue( fScrollControl->GetMax() ); + } + IUpdate(); +} + +//// ScrollToBegin /////////////////////////////////////////////////////////// + +void pfGUIListBoxMod::ScrollToBegin( void ) +{ + if( fScrollControl != nil ) + { + fScrollPos = (int)fScrollControl->GetMax(); + fScrollControl->SetCurrValue( fScrollControl->GetMin() ); + } + IUpdate(); +} + +void pfGUIListBoxMod::SetColorScheme( pfGUIColorScheme *newScheme ) +{ + UInt16 i; + + pfGUIControlMod::SetColorScheme( newScheme ); + + for( i = 0; i < fElements.GetCount(); i++ ) + { + fElements[ i ]->SetColorScheme( newScheme ); + fElements[ i ]->SetSkin( fSkin ); + } +} + +void pfGUIListBoxMod::SetScrollPos( Int32 pos ) +{ + if ( pos >= (int)fScrollControl->GetMin() && pos <= (int)fScrollControl->GetMax() ) + { + fScrollControl->SetCurrValue( (hsScalar)pos ); + fScrollPos = (int)fScrollControl->GetMax() - pos; + } + IUpdate(); +} + +Int32 pfGUIListBoxMod::GetScrollPos( void ) +{ + return (int)fScrollControl->GetCurrValue(); +} + +Int32 pfGUIListBoxMod::GetScrollRange( void ) +{ + return (int)fScrollControl->GetMax() - (int)fScrollControl->GetMin(); +} + + +////////////////////////////////////////////////////////////////////////////// +//// Element Manipulation //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +UInt16 pfGUIListBoxMod::AddElement( pfGUIListElement *el ) +{ + UInt16 idx = fElements.GetCount(); + fElements.Append( el ); + el->SetColorScheme( GetColorScheme() ); + el->SetSkin( fSkin ); + if( !fLocked ) + { + ICalcWrapStarts(); + ICalcScrollRange(); + IUpdate(); + HandleExtendedEvent( pfGUIListBoxMod::kItemAdded ); + } + return idx; +} + +void pfGUIListBoxMod::RemoveElement( UInt16 index ) +{ + // Make sure no other elements care about this one + UInt16 i, j; + for( i = 0; i < fElements.GetCount(); i++ ) + { + if( fElements[ i ]->GetType() == pfGUIListElement::kTreeRoot ) + { + pfGUIListTreeRoot *root = (pfGUIListTreeRoot *)fElements[ i ]; + for( j = 0; j < root->GetNumChildren(); ) + { + if( root->GetChild( j ) == fElements[ index ] ) + root->RemoveChild( j ); + else + j++; + } + } + } + + delete fElements[ index ]; + fElements.Remove( index ); + + if( index == fSingleSelElement ) + fSingleSelElement = -1; + else if( index < fSingleSelElement ) + fSingleSelElement--; + fCurrHover = fCurrClick = -1; + + if( !fLocked ) + { + ICalcWrapStarts(); + ICalcScrollRange(); + IUpdate(); + HandleExtendedEvent( pfGUIListBoxMod::kItemRemoved ); + } +} + +Int16 pfGUIListBoxMod::FindElement( pfGUIListElement *toCompareTo ) +{ + int i; + + + for( i = 0; i < fElements.GetCount(); i++ ) + { + if( fElements[ i ]->CompareTo( toCompareTo ) == 0 ) + return i; + } + + return (Int16)-1; +} + +void pfGUIListBoxMod::ClearAllElements( void ) +{ + int i; + + + for( i = 0; i < fElements.GetCount(); i++ ) + delete fElements[ i ]; + fElements.Reset(); + fSingleSelElement = -1; + + if( !fLocked ) + { + ICalcWrapStarts(); + ICalcScrollRange(); + IUpdate(); + } + + HandleExtendedEvent( pfGUIListBoxMod::kListCleared ); +} + +UInt16 pfGUIListBoxMod::AddString( const char *string ) +{ + return AddElement( TRACKED_NEW pfGUIListText( string ) ); +} + +UInt16 pfGUIListBoxMod::AddString( const wchar_t *string ) +{ + return AddElement( TRACKED_NEW pfGUIListText( string ) ); +} + +Int16 pfGUIListBoxMod::FindString( const char *toCompareTo ) +{ + pfGUIListText text( toCompareTo ); + return FindElement( &text ); +} + +Int16 pfGUIListBoxMod::FindString( const wchar_t *toCompareTo ) +{ + pfGUIListText text( toCompareTo ); + return FindElement( &text ); +} + +UInt16 pfGUIListBoxMod::GetNumElements( void ) +{ + return fElements.GetCount(); +} + +pfGUIListElement *pfGUIListBoxMod::GetElement( UInt16 idx ) +{ + return fElements[ idx ]; +} + +void pfGUIListBoxMod::LockList( void ) +{ + fLocked = true; +} + +void pfGUIListBoxMod::UnlockList( void ) +{ + fLocked = false; + ICalcWrapStarts(); + ICalcScrollRange(); + IUpdate(); +} + +//// IGetDesiredCursor /////////////////////////////////////////////////////// + +UInt32 pfGUIListBoxMod::IGetDesiredCursor( void ) const +{ + if( fCurrHover == -1 ) + return plInputInterface::kNullCursor; + + if( fClicking ) + return plInputInterface::kCursorClicked; + + return plInputInterface::kCursorPoised; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListBoxMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListBoxMod.h new file mode 100644 index 00000000..68e1d471 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListBoxMod.h @@ -0,0 +1,183 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIListBoxMod Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIListBoxMod_h +#define _pfGUIListBoxMod_h + +#include "pfGUIControlMod.h" + +class plMessage; +class hsGMaterial; +class plTextGenerator; +class pfGUIListElement; +class pfScrollProc; +class pfGUIValueCtrl; + +class pfGUIListBoxMod : public pfGUIControlMod +{ + friend class pfScrollProc; + + protected: + + struct plSmallRect + { + Int16 fLeft, fTop, fRight, fBottom; + + void Set( Int16 l, Int16 t, Int16 r, Int16 b ); + hsBool Contains( Int16 x, Int16 y ); + + plSmallRect& operator=(const int zero) { fLeft = fTop = fRight = fBottom = 0; return *this; } + }; + + pfGUIValueCtrl *fScrollControl; + + pfScrollProc *fScrollProc; + + hsTArray fElements; + Int32 fCurrClick, fScrollPos, fCurrHover; + UInt8 fModsAtDragTime; + Int32 fMinSel, fMaxSel; + hsBool fCheckScroll, fClicking; + Int32 fSingleSelElement; + hsBool fScrollRangeUpdateDeferred; + hsBool fLocked, fReadyToRoll; + hsTArray fElementBounds; + hsTArray fWrapStartIdxs; + + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + void ICalcScrollRange( void ); + void ICalcWrapStarts( void ); + + virtual void IUpdate( void ); + virtual void IPostSetUpDynTextMap( void ); + virtual UInt32 IGetDesiredCursor( void ) const; + + Int32 IGetItemFromPoint( hsPoint3 &mousePt ); + void IFindSelectionRange( Int32 *min, Int32 *max ); + void ISelectRange( Int8 min, Int8 max, hsBool select ); + + public: + + pfGUIListBoxMod(); + virtual ~pfGUIListBoxMod(); + + CLASSNAME_REGISTER( pfGUIListBoxMod ); + GETINTERFACE_ANY( pfGUIListBoxMod, pfGUIControlMod ); + + enum OurFlags + { + kSingleSelect = kDerivedFlagsStart, + kDragAndDropCapable, + kDisableSelection, + kDisableKeyActions, + kAllowMultipleElementsPerRow, + kScrollLeftToRight, + kAllowMousePassThrough, + kGrowLeavesAndProcessOxygen, + kHandsOffMultiSelect, // Do multiselect w/o needing ctrl or shift + kForbidNoSelection + }; + + // Extended event types + enum ExtendedEvents + { + kScrollPosChanged, + kItemAdded, + kItemRemoved, + kListCleared + }; + + enum + { + kRefScrollCtrl = kRefDerivedStart + }; + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseHover( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseDblClick( hsPoint3 &mousePt, UInt8 modifiers ); + + virtual hsBool HandleKeyPress( char key, UInt8 modifiers ); + virtual hsBool HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers ); + + virtual hsBool FilterMousePosition( hsPoint3 &mousePt ); + + virtual void PurgeDynaTextMapImage(); + + // Returns selected element. Only valid for kSingleSelect list boxes + Int32 GetSelection( void ) { return fSingleSelElement; } + void SetSelection( Int32 item ); + void RemoveSelection( Int32 item ); + void AddSelection( Int32 item ); + + virtual void ScrollToBegin( void ); + virtual void ScrollToEnd( void ); + virtual void SetScrollPos( Int32 pos ); + virtual Int32 GetScrollPos( void ); + virtual Int32 GetScrollRange( void ); + + + void Refresh( void ) { IUpdate(); } + + virtual void SetColorScheme( pfGUIColorScheme *newScheme ); + + // Element manipulation + + UInt16 AddElement( pfGUIListElement *el ); + void RemoveElement( UInt16 index ); + Int16 FindElement( pfGUIListElement *toCompareTo ); + void ClearAllElements( void ); + + void LockList( void ); + void UnlockList( void ); + + UInt16 GetNumElements( void ); + pfGUIListElement *GetElement( UInt16 idx ); + + UInt16 AddString( const char *string ); + UInt16 AddString( const wchar_t *string ); + Int16 FindString( const char *toCompareTo ); + Int16 FindString( const wchar_t *toCompareTo ); + + // Export only + void SetScrollCtrl( pfGUIValueCtrl *ctrl ) { fScrollControl = ctrl; } + void SetSingleSelect( hsBool yes ) { if( yes ) SetFlag( kSingleSelect ); else ClearFlag( kSingleSelect ); } +}; + +#endif // _pfGUIListBoxMod_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListElement.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListElement.cpp new file mode 100644 index 00000000..399128d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListElement.cpp @@ -0,0 +1,456 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIListElement Class Definitions // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIListElement.h" +#include "pfGameGUIMgr.h" + +#include "pfGUIPopUpMenu.h" // For skins + +#include "../plGImage/plDynamicTextMap.h" +#include "../plGImage/hsCodecManager.h" +#include "../plPipeline/plDebugText.h" // To quickly and hackily get the screen size in pixels +#include "hsResMgr.h" + + +////////////////////////////////////////////////////////////////////////////// +//// Base Stuff ////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +void pfGUIListElement::Read( hsStream *s, hsResMgr *mgr ) +{ + fSelected = s->ReadBool(); +} + +void pfGUIListElement::Write( hsStream *s, hsResMgr *mgr ) +{ + s->WriteBool( fSelected ); +} + +////////////////////////////////////////////////////////////////////////////// +//// pfGUIListText /////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIListText::pfGUIListText() : pfGUIListElement( kText ) +{ + fText = nil; + fJustify = kLeftJustify; +} + +pfGUIListText::pfGUIListText( const char *text ) : pfGUIListElement( kText ) +{ + fText = hsStringToWString(text); + fJustify = kLeftJustify; +} + +pfGUIListText::pfGUIListText( const wchar_t *text ) : pfGUIListElement( kText ) +{ + fText = TRACKED_NEW wchar_t[ wcslen( text ) + 1 ]; + wcscpy( fText, text ); + fJustify = kLeftJustify; +} + +pfGUIListText::~pfGUIListText() +{ + delete [] fText; +} + +//// Virtuals //////////////////////////////////////////////////////////////// + +void pfGUIListText::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIListElement::Read( s, mgr ); + + char *text = s->ReadSafeString(); + fText = hsStringToWString(text); + delete [] text; +} + +void pfGUIListText::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIListElement::Write( s, mgr ); + + char *text = hsWStringToString(fText); + s->WriteSafeString(text); + delete [] text; +} + +hsBool pfGUIListText::Draw( plDynamicTextMap *textGen, UInt16 x, UInt16 y, UInt16 maxWidth, UInt16 maxHeight ) +{ + textGen->SetJustify( (plDynamicTextMap::Justify)fJustify ); + if( fSelected ) + { + textGen->FillRect( x, y, maxWidth, maxHeight, fColors->fSelBackColor ); + textGen->SetTextColor( fColors->fSelForeColor, fColors->fTransparent && fColors->fSelBackColor.a == 0.f ); + } + else + { + // Normal back color will be cleared for us + textGen->SetTextColor( fColors->fForeColor, fColors->fTransparent && fColors->fBackColor.a == 0.f ); + } + + textGen->DrawClippedString( x + 4, y, GetText(), maxWidth - 8, maxHeight ); + return true; +} + +void pfGUIListText::GetSize( plDynamicTextMap *textGen, UInt16 *width, UInt16 *height ) +{ + *width = textGen->CalcStringWidth( GetText(), height ); + if( height != nil ) + { + if( *height == 0 ) + *height = 10; // Never allow zero height elements + else + *height += 0; // Add one pixel on each side for padding (or not, 3.21.02 mcn) + } +} + +int pfGUIListText::CompareTo( pfGUIListElement *rightSide ) +{ + pfGUIListText *text = (pfGUIListText *)rightSide; + + if( text->fType != kText ) + return -2; + + return wcscmp( GetText(), text->GetText() ); +} + +void pfGUIListText::SetText( const char *text ) +{ + wchar_t *wText = hsStringToWString(text); + SetText(wText); + delete [] wText; +} + +void pfGUIListText::SetText( const wchar_t *text ) +{ + delete [] fText; + if( text != nil ) + { + fText = TRACKED_NEW wchar_t[ wcslen( text ) + 1 ]; + wcscpy( fText, text ); + } + else + fText = nil; +} + +void pfGUIListText::SetJustify( JustifyTypes justify ) +{ + switch( justify ) + { + case kRightJustify: + fJustify = plDynamicTextMap::kRightJustify; + break; + case kCenter: + fJustify = plDynamicTextMap::kCenter; + break; + case kLeftJustify: + default: + fJustify = plDynamicTextMap::kLeftJustify; + break; + } +} + + +////////////////////////////////////////////////////////////////////////////// +//// pfGUIListPicture //////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIListPicture::pfGUIListPicture() : pfGUIListElement( kPicture ) +{ + fBorderSize = 2; + fMipmapKey = nil; +} + +pfGUIListPicture::pfGUIListPicture( plKey mipKey, hsBool respectAlpha ) : pfGUIListElement( kPicture ) +{ + fBorderSize = 2; + fMipmapKey = mipKey; + fRespectAlpha = respectAlpha; + + plMipmap *mip = plMipmap::ConvertNoRef( fMipmapKey->ObjectIsLoaded() ); + if( mip != nil && mip->IsCompressed() ) + { + // Gotta make and grab an uncompressed one + plMipmap *uncompBuffer = hsCodecManager::Instance().CreateUncompressedMipmap( mip, hsCodecManager::k32BitDepth ); + char str[ 512 ]; + sprintf( str, "%s_uncomp", mip->GetKeyName() ); + fMipmapKey = hsgResMgr::ResMgr()->NewKey( str, uncompBuffer, fMipmapKey->GetUoid().GetLocation() ); + fMipmapKey->RefObject(); + } +} + +pfGUIListPicture::~pfGUIListPicture() +{ + fMipmapKey->UnRefObject(); + fMipmapKey = nil; +} + +//// Virtuals //////////////////////////////////////////////////////////////// + +void pfGUIListPicture::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIListElement::Read( s, mgr ); + +} + +void pfGUIListPicture::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIListElement::Write( s, mgr ); + +} + +hsBool pfGUIListPicture::Draw( plDynamicTextMap *textGen, UInt16 x, UInt16 y, UInt16 maxWidth, UInt16 maxHeight ) +{ + if( fSelected ) + textGen->FillRect( x, y, maxWidth, maxHeight, fColors->fSelBackColor ); + + plMipmap *mip = plMipmap::ConvertNoRef( fMipmapKey->ObjectIsLoaded() ); + if( mip != nil ) + { + if( mip->GetWidth() + fBorderSize + fBorderSize > maxWidth || mip->GetHeight() + fBorderSize + fBorderSize > maxHeight ) + return false; + + textGen->DrawImage( x + fBorderSize, y + fBorderSize, mip, fRespectAlpha ? plDynamicTextMap::kImgBlend : plDynamicTextMap::kImgNoAlpha ); + } + + return true; +} + +void pfGUIListPicture::GetSize( plDynamicTextMap *textGen, UInt16 *width, UInt16 *height ) +{ + plMipmap *mip = plMipmap::ConvertNoRef( fMipmapKey->ObjectIsLoaded() ); + if( mip == nil ) + { + *width = 16; + if( height != nil ) + *height = 16; + } + + *width = (UInt16)(mip->GetWidth() + fBorderSize + fBorderSize); + if( height != nil ) + *height = (UInt16)(mip->GetHeight() + fBorderSize + fBorderSize); +} + +int pfGUIListPicture::CompareTo( pfGUIListElement *rightSide ) +{ + pfGUIListPicture *text = (pfGUIListPicture *)rightSide; + + if( text->fType != kPicture ) + return -2; + + return -2; +} + +////////////////////////////////////////////////////////////////////////////// +//// pfGUIListTreeRoot /////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIListTreeRoot::pfGUIListTreeRoot() : pfGUIListElement( kTreeRoot ) +{ + fText = nil; + fShowChildren = true; +} + +pfGUIListTreeRoot::pfGUIListTreeRoot( const char *text ) : pfGUIListElement( kTreeRoot ) +{ + fText = hsStringToWString(text); +} + +pfGUIListTreeRoot::pfGUIListTreeRoot( const wchar_t *text ) : pfGUIListElement( kTreeRoot ) +{ + fText = TRACKED_NEW wchar_t[ wcslen( text ) + 1 ]; + wcscpy( fText, text ); +} + +pfGUIListTreeRoot::~pfGUIListTreeRoot() +{ + delete [] fText; +} + +//// Virtuals //////////////////////////////////////////////////////////////// + +void pfGUIListTreeRoot::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIListElement::Read( s, mgr ); + + char *temp = s->ReadSafeString(); + fText = hsStringToWString(temp); + delete [] temp; +} + +void pfGUIListTreeRoot::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIListElement::Write( s, mgr ); + + char *temp = hsWStringToString(fText); + s->WriteSafeString( temp ); + delete [] temp; +} + +hsBool pfGUIListTreeRoot::Draw( plDynamicTextMap *textGen, UInt16 x, UInt16 y, UInt16 maxWidth, UInt16 maxHeight ) +{ + textGen->SetJustify( plDynamicTextMap::kLeftJustify ); + if( fSelected ) + { + textGen->FillRect( x, y, maxWidth, maxHeight, fColors->fSelBackColor ); + textGen->SetTextColor( fColors->fSelForeColor, fColors->fTransparent && fColors->fSelBackColor.a == 0.f ); + } + else + { + // Normal back color will be cleared for us + textGen->SetTextColor( fColors->fForeColor, fColors->fTransparent && fColors->fBackColor.a == 0.f ); + } + + if( fSkin != nil ) + { + const pfGUISkin::pfSRect &r = fSkin->GetElement( fShowChildren ? pfGUISkin::kTreeButtonOpen : pfGUISkin::kTreeButtonClosed ); + + Int16 e = ( maxHeight - r.fHeight ); + if( e < 0 ) + e = 0; + e >>= 1; + + textGen->DrawClippedImage( x + 2, y + e, fSkin->GetTexture(), r.fX, r.fY, r.fWidth, r.fHeight, plDynamicTextMap::kImgSprite ); + x += r.fWidth + 4; + } + + textGen->DrawClippedString( x + 4, y, GetTitle(), maxWidth - 8, maxHeight ); + return true; +} + +hsBool pfGUIListTreeRoot::MouseClicked( UInt16 localX, UInt16 localY ) +{ + if( fSkin != nil ) + { + const pfGUISkin::pfSRect &r = fSkin->GetElement( fShowChildren ? pfGUISkin::kTreeButtonOpen : pfGUISkin::kTreeButtonClosed ); + + // For now, I can't think of a clean way of getting the current visible height to this function, + // but just testing the X value for tree controls is good enough for now. If we need Y testing for + // other elements, I'll figure out something. + if( localX >= 2 && localX <= 2 + r.fWidth ) + { + ShowChildren( !fShowChildren ); + return true; + } + } + + return false; +} + +void pfGUIListTreeRoot::GetSize( plDynamicTextMap *textGen, UInt16 *width, UInt16 *height ) +{ + *width = textGen->CalcStringWidth( GetTitle(), height ); + if( height != nil ) + { + if( *height == 0 ) + *height = 10; // Never allow zero height elements + else + *height += 0; // Add one pixel on each side for padding (or not, 3.21.02 mcn) + + if( fSkin != nil ) + { + UInt16 h = fSkin->GetElement( pfGUISkin::kTreeButtonClosed ).fHeight; + if( *height < h ) + *height = h; + } + } + + if( fSkin != nil ) + *width += fSkin->GetElement( pfGUISkin::kTreeButtonClosed ).fWidth; +} + +int pfGUIListTreeRoot::CompareTo( pfGUIListElement *rightSide ) +{ + pfGUIListTreeRoot *text = (pfGUIListTreeRoot *)rightSide; + + if( text->fType != kTreeRoot ) + return -2; + + return wcscmp( GetTitle(), text->GetTitle() ); +} + +void pfGUIListTreeRoot::SetTitle( const char *text ) +{ + wchar_t *wText = hsStringToWString(text); + SetTitle(wText); + delete [] wText; +} + +void pfGUIListTreeRoot::SetTitle( const wchar_t *text ) +{ + delete [] fText; + if( text != nil ) + { + fText = TRACKED_NEW wchar_t[ wcslen( text ) + 1 ]; + wcscpy( fText, text ); + } + else + fText = nil; +} + +void pfGUIListTreeRoot::AddChild( pfGUIListElement *el ) +{ + fChildren.Append( el ); + el->SetIndentLevel( GetIndentLevel() + 1 ); + el->SetCollapsed( !fShowChildren ); +} + +void pfGUIListTreeRoot::RemoveChild( UInt32 idx ) +{ + fChildren.Remove( idx ); +} + +void pfGUIListTreeRoot::ShowChildren( hsBool s ) +{ + UInt32 i; + + + fShowChildren = s; + for( i = 0; i < fChildren.GetCount(); i++ ) + fChildren[ i ]->SetCollapsed( !s ); +} + +void pfGUIListTreeRoot::SetCollapsed( hsBool c ) +{ + UInt32 i; + + + pfGUIListElement::SetCollapsed( c ); + for( i = 0; i < fChildren.GetCount(); i++ ) + fChildren[ i ]->SetCollapsed( c ? true : !fShowChildren ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListElement.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListElement.h new file mode 100644 index 00000000..0c3c7269 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIListElement.h @@ -0,0 +1,228 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIListElement Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIListElement_h +#define _pfGUIListElement_h + +#include "pfGUIControlMod.h" + +class plDynamicTextMap; + +class pfGUISkin; +class pfGUIListElement +{ + protected: + + hsBool fSelected; + const UInt8 fType; + + hsBool fCollapsed; // For tree view support + UInt8 fIndentLevel; // Ditto + + pfGUIColorScheme *fColors; + pfGUISkin *fSkin; + + public: + + enum Types + { + kText, + kPicture, + kTreeRoot + }; + + pfGUIListElement( UInt8 type ) : fType( type ), fSelected( false ), fCollapsed( false ), fIndentLevel( 0 ) {} + virtual ~pfGUIListElement() {} + + virtual void Read( hsStream *s, hsResMgr *mgr ); + virtual void Write( hsStream *s, hsResMgr *mgr ); + + virtual hsBool Draw( plDynamicTextMap *textGen, UInt16 x, UInt16 y, UInt16 maxWidth, UInt16 maxHeight ) = 0; + virtual void GetSize( plDynamicTextMap *textGen, UInt16 *width, UInt16 *height ) = 0; + virtual int CompareTo( pfGUIListElement *rightSide ) = 0; + + virtual void SetSelected( hsBool sel ) { fSelected = sel; } + virtual hsBool IsSelected( void ) { return fSelected; } + + virtual hsBool CanBeDragged( void ) { return false; } + + // Return true here if you need the list refreshed + virtual hsBool MouseClicked( UInt16 localX, UInt16 localY ) { return false; } + + UInt8 GetType( void ) { return fType; } + + void SetColorScheme( pfGUIColorScheme *scheme ) { fColors = scheme; } + void SetSkin( pfGUISkin *skin ) { fSkin = skin; } + + hsBool IsCollapsed( void ) const { return fCollapsed; } + virtual void SetCollapsed( hsBool c ) { fCollapsed = c; } + + UInt8 GetIndentLevel( void ) const { return fIndentLevel; } + void SetIndentLevel( UInt8 i ) { fIndentLevel = i; } +}; + +class pfGUIListText : public pfGUIListElement +{ + public: + // these enums should at least agree with the plDynamicTextMap's version of the Justify types + enum JustifyTypes + { + kLeftJustify = 0, + kCenter, + kRightJustify + }; + + protected: + + wchar_t *fText; + UInt8 fJustify; // This is not our JustifyTypes, but from plDynamicTextMap + + public: + + pfGUIListText(); + pfGUIListText( const char *text ); + pfGUIListText( const wchar_t *text ); + virtual ~pfGUIListText(); + + virtual void Read( hsStream *s, hsResMgr *mgr ); + virtual void Write( hsStream *s, hsResMgr *mgr ); + + virtual hsBool Draw( plDynamicTextMap *textGen, UInt16 x, UInt16 y, UInt16 maxWidth, UInt16 maxHeight ); + virtual void GetSize( plDynamicTextMap *textGen, UInt16 *width, UInt16 *height ); + virtual int CompareTo( pfGUIListElement *rightSide ); + + virtual hsBool CanBeDragged( void ) { return true; } + virtual void SetJustify( JustifyTypes justify ); + + // These two are virtual so we can derive and override them + virtual const wchar_t *GetText( void ) { return fText; } + virtual void SetText( const char *text ); + virtual void SetText( const wchar_t *text ); +}; + +class pfGUIListPicture : public pfGUIListElement +{ + protected: + + plKey fMipmapKey; + UInt8 fBorderSize; // Defaults to 2 + hsBool fRespectAlpha; + + public: + + pfGUIListPicture(); + pfGUIListPicture( plKey mipKey, hsBool respectAlpha ); + virtual ~pfGUIListPicture(); + + virtual void Read( hsStream *s, hsResMgr *mgr ); + virtual void Write( hsStream *s, hsResMgr *mgr ); + + virtual hsBool Draw( plDynamicTextMap *textGen, UInt16 x, UInt16 y, UInt16 maxWidth, UInt16 maxHeight ); + virtual void GetSize( plDynamicTextMap *textGen, UInt16 *width, UInt16 *height ); + virtual int CompareTo( pfGUIListElement *rightSide ); + + virtual hsBool CanBeDragged( void ) { return false; } + + void SetBorderSize( UInt32 size ) { fBorderSize = (UInt8)size; } + void SetRespectAlpha( hsBool r ) { fRespectAlpha = r; } + +}; + +class pfGUIListTreeRoot : public pfGUIListElement +{ + protected: + + wchar_t *fText; + hsBool fShowChildren; + + hsTArray fChildren; + + public: + + pfGUIListTreeRoot(); + pfGUIListTreeRoot( const char *text ); + pfGUIListTreeRoot( const wchar_t *text ); + virtual ~pfGUIListTreeRoot(); + + virtual void Read( hsStream *s, hsResMgr *mgr ); + virtual void Write( hsStream *s, hsResMgr *mgr ); + + virtual hsBool Draw( plDynamicTextMap *textGen, UInt16 x, UInt16 y, UInt16 maxWidth, UInt16 maxHeight ); + virtual void GetSize( plDynamicTextMap *textGen, UInt16 *width, UInt16 *height ); + virtual int CompareTo( pfGUIListElement *rightSide ); + + virtual hsBool MouseClicked( UInt16 localX, UInt16 localY ); + + const wchar_t *GetTitle( void ) { return fText; } + void SetTitle( const char *text ); + void SetTitle( const wchar_t *text ); + + UInt32 GetNumChildren( void ) const { return fChildren.GetCount(); } + pfGUIListElement *GetChild( UInt32 i ) const { return fChildren[ i ]; } + + void AddChild( pfGUIListElement *el ); + void RemoveChild( UInt32 idx ); + + virtual void SetCollapsed( hsBool c ); + + void ShowChildren( hsBool s ); + hsBool IsShowingChildren( void ) const { return fShowChildren; } +}; + +//// pfGUIDropTargetProc ///////////////////////////////////////////////////// +// A little proc object you create if you want a control to be a potential +// target for drag & drop operations. It has two functions: one takes a +// listElement and returns whether it can accept that type, and the other +// actually gets called when a listElement is "dropped" onto the associated +// control. Any control can be a dropTarget; just attach the right proc +// to it! +// If you are dragging multiple elements, both CanEat() and Eat() will get +// called for each element that is being dragged. + +class pfGUIDropTargetProc +{ + protected: + + UInt32 fRefCnt; + + public: + + pfGUIDropTargetProc() { fRefCnt = 0; } + + virtual hsBool CanEat( pfGUIListElement *element, pfGUIControlMod *source ) = 0; + virtual void Eat( pfGUIListElement *element, pfGUIControlMod *source, pfGUIControlMod *parent ) = 0; + + // ONLY THE GUI SYSTEM SHOULD CALL THESE + void IncRef( void ) { fRefCnt++; } + hsBool DecRef( void ) { fRefCnt--; return ( fRefCnt > 0 ) ? false : true; } +}; + +#endif // _pfGUIListElement_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMenuItem.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMenuItem.cpp new file mode 100644 index 00000000..8b55b957 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMenuItem.cpp @@ -0,0 +1,463 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIMenuItem Definition // +// // +// The type of button that knows how to party. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIMenuItem.h" +#include "pfGameGUIMgr.h" +#include "pfGUIControlHandlers.h" +#include "pfGUIDialogMod.h" +#include "pfGUIPopUpMenu.h" + +#include "../plInputCore/plInputInterface.h" +#include "../pnMessage/plRefMsg.h" +#include "../plGImage/plDynamicTextMap.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIMenuItem::pfGUIMenuItem() +{ + fName = nil; + fSkin = nil; + fReportingHover = false; + fSkinBuffersUpdated = true; +} + +pfGUIMenuItem::~pfGUIMenuItem() +{ + SetSkin( nil, kTop ); + delete [] fName; +} + +void pfGUIMenuItem::SetName( const char *name ) +{ + wchar_t *wName = hsStringToWString(name); + SetName(wName); + delete [] wName; +} + +void pfGUIMenuItem::SetName( const wchar_t *name ) +{ + delete [] fName; + if (name != nil) + { + fName = TRACKED_NEW wchar_t[wcslen(name)+1]; + wcscpy(fName,name); + } + else + fName = nil; + + IUpdate(); +} + +//// SetSkin ///////////////////////////////////////////////////////////////// + +void pfGUIMenuItem::SetSkin( pfGUISkin *skin, HowToSkin s ) +{ + // Just a function wrapper for SendRef + if( fSkin != nil ) + GetKey()->Release( fSkin->GetKey() ); + + if( skin != nil ) + hsgResMgr::ResMgr()->SendRef( skin->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefSkin ), plRefFlags::kActiveRef ); + + fHowToSkin = s; + + fSkinBuffersUpdated = false; +} + +//// IPostSetUpDynTextMap //////////////////////////////////////////////////// +// Draw our initial image on the dynTextMap + +void pfGUIMenuItem::IPostSetUpDynTextMap( void ) +{ +} + + +//// IGetDesiredExtraDTMRoom ///////////////////////////////////////////////// +// Overridden so we can enlarge our DTMap by 3 vertically, to use the extra +// space as basically a double buffer for our skinning + +void pfGUIMenuItem::IGrowDTMDimsToDesiredSize( UInt16 &width, UInt16 &height ) +{ + height *= 3; +} + +//// IUpdateSkinBuffers ////////////////////////////////////////////////////// +// Redraws the double buffers for the two skin images we keep hidden in the +// DTMap, so we don't have to re-composite them every time we draw the +// control. + +void pfGUIMenuItem::IUpdateSkinBuffers( void ) +{ + if( fSkinBuffersUpdated ) + return; + if( fSkin == nil ) + return; + if( fDynTextMap == nil ) + return; + if( fSkin->GetTexture() == nil ) + return; + + UInt16 y = fDynTextMap->GetVisibleHeight(); + + IUpdateSingleSkinBuffer( y, false ); + IUpdateSingleSkinBuffer( y << 1, true ); + + fSkinBuffersUpdated = true; +} + +//// IUpdateSingleSkinBuffer ///////////////////////////////////////////////// +// Broken down functionality for the above function + +void pfGUIMenuItem::IUpdateSingleSkinBuffer( UInt16 y, hsBool sel ) +{ + hsAssert( fSkin != nil && fDynTextMap != nil, "Invalid pointers in IUpdateSingleSkinBuffer()" ); + + + // Note: add 1 to the visible height so we get enough overlap to take care of mipmapping issues + UInt16 x = 0, totWidth = fDynTextMap->GetVisibleWidth(); + UInt16 totHeight = y + fDynTextMap->GetVisibleHeight(); + pfGUISkin::pfSRect element; + + + totWidth -= fSkin->GetElement( pfGUISkin::kRightSpan ).fWidth; + if( fHowToSkin == kTop ) + { + // Draw up-left corner + element = fSkin->GetElement( pfGUISkin::kUpLeftCorner ); + fDynTextMap->DrawClippedImage( x, y, fSkin->GetTexture(), element.fX, element.fY, element.fWidth, element.fHeight, plDynamicTextMap::kImgSprite ); + x += element.fWidth; + + element = fSkin->GetElement( pfGUISkin::kTopSpan ); + for( ; x < totWidth; ) + { + UInt16 wid = element.fWidth; + if( x + wid > totWidth ) + wid = totWidth - x; + fDynTextMap->DrawClippedImage( x, y, fSkin->GetTexture(), element.fX, element.fY, wid, element.fHeight, plDynamicTextMap::kImgSprite ); + x += wid; + } + + element = fSkin->GetElement( pfGUISkin::kUpRightCorner ); + fDynTextMap->DrawClippedImage( x, y, fSkin->GetTexture(), element.fX, element.fY, element.fWidth, element.fHeight, plDynamicTextMap::kImgSprite ); + + y += element.fHeight; + } + else if( fHowToSkin == kBottom ) + { + // Clip some space for now + totHeight -= fSkin->GetElement( pfGUISkin::kLowerLeftCorner ).fHeight; + } + + // Group drawing by skin elements for caching performance + UInt16 startY = y; + x = 0; + element = fSkin->GetElement( pfGUISkin::kLeftSpan ); + for( ; y < totHeight; ) + { + UInt16 ht = element.fHeight; + if( y + ht > totHeight ) + ht = totHeight - y; + fDynTextMap->DrawClippedImage( x, y, fSkin->GetTexture(), element.fX, element.fY, element.fWidth, ht, plDynamicTextMap::kImgSprite ); + y += ht; + } + + x += element.fWidth; + if( sel ) + element = fSkin->GetElement( pfGUISkin::kSelectedFill ); + else + element = fSkin->GetElement( pfGUISkin::kMiddleFill ); + for( ; x < totWidth; ) + { + UInt16 wid = element.fWidth; + if( x + wid > totWidth ) + wid = totWidth - x; + + for( y = startY; y < totHeight; ) + { + UInt16 ht = element.fHeight; + if( y + ht > totHeight ) + ht = totHeight - y; + fDynTextMap->DrawClippedImage( x, y, fSkin->GetTexture(), element.fX, element.fY, wid, ht, plDynamicTextMap::kImgSprite ); + y += ht; + } + + x += wid; + } + + element = fSkin->GetElement( pfGUISkin::kRightSpan ); + for( y = startY; y < totHeight; ) + { + UInt16 ht = element.fHeight; + if( y + ht > totHeight ) + ht = totHeight - y; + fDynTextMap->DrawClippedImage( x, y, fSkin->GetTexture(), element.fX, element.fY, element.fWidth, ht, plDynamicTextMap::kImgSprite ); + y += ht; + } + + if( fHowToSkin == kBottom ) + { + x = 0; + + // Draw lower-left corner + element = fSkin->GetElement( pfGUISkin::kLowerLeftCorner ); + fDynTextMap->DrawClippedImage( x, y, fSkin->GetTexture(), element.fX, element.fY, element.fWidth, element.fHeight, plDynamicTextMap::kImgSprite ); + x += element.fWidth; + + element = fSkin->GetElement( pfGUISkin::kBottomSpan ); + for( ; x < totWidth; ) + { + UInt16 wid = element.fWidth; + if( x + wid > totWidth ) + wid = totWidth - x; + fDynTextMap->DrawClippedImage( x, y, fSkin->GetTexture(), element.fX, element.fY, wid, element.fHeight, plDynamicTextMap::kImgSprite ); + x += wid; + } + + element = fSkin->GetElement( pfGUISkin::kLowerRightCorner ); + fDynTextMap->DrawClippedImage( x, y, fSkin->GetTexture(), element.fX, element.fY, element.fWidth, element.fHeight, plDynamicTextMap::kImgSprite ); + + y += element.fHeight; + } +} + +//// IUpdate ///////////////////////////////////////////////////////////////// + +void pfGUIMenuItem::IUpdate( void ) +{ + if( fDynTextMap == nil ) + return; + + if( fSkin != nil ) + { + IUpdateSkinBuffers(); + + if( !fSkinBuffersUpdated ) + return; + + // Copy now from our skin buffer, plus set our text color + UInt16 y = fDynTextMap->GetVisibleHeight(); + + if( IsInteresting() ) + { + fDynTextMap->DrawClippedImage( 0, 0, fDynTextMap, 0, y << 1, fDynTextMap->GetVisibleWidth(), y, plDynamicTextMap::kImgSprite ); + fDynTextMap->SetTextColor( GetColorScheme()->fSelForeColor ); + } + else + { + fDynTextMap->DrawClippedImage( 0, 0, fDynTextMap, 0, y, fDynTextMap->GetVisibleWidth(), y, plDynamicTextMap::kImgSprite ); + fDynTextMap->SetTextColor( GetColorScheme()->fForeColor ); + } + } + else + { + if( IsInteresting() ) + { + fDynTextMap->ClearToColor( GetColorScheme()->fSelBackColor ); + fDynTextMap->SetTextColor( GetColorScheme()->fSelForeColor ); + } + else + { + fDynTextMap->ClearToColor( GetColorScheme()->fBackColor ); + fDynTextMap->SetTextColor( GetColorScheme()->fForeColor ); + } + } + + fDynTextMap->SetJustify( plDynamicTextMap::kLeftJustify ); + + if( fName != nil ) + { + UInt16 ht; + fDynTextMap->CalcStringWidth( fName, &ht ); + + Int16 x = 0, y = ( fDynTextMap->GetVisibleHeight() - ht ) >> 1; + if( fHowToSkin == kTop && fSkin != nil ) + y += fSkin->GetElement( pfGUISkin::kTopSpan ).fHeight >> 1; + else if( fHowToSkin == kBottom && fSkin != nil ) + y -= fSkin->GetElement( pfGUISkin::kTopSpan ).fHeight >> 1; + + if( fSkin != nil ) + x += fSkin->GetBorderMargin(); + + if( fClicking ) + { + x += 2; + y += 2; + } + + fDynTextMap->DrawClippedString( x, y, fName, fDynTextMap->GetVisibleWidth(), fDynTextMap->GetVisibleHeight() ); + + if( HasFlag( kDrawSubMenuArrow ) ) + { + if( fSkin != nil ) + { + pfGUISkin::pfSRect element; + + if( IsInteresting() ) + element = fSkin->GetElement( pfGUISkin::kSelectedSubMenuArrow ); + else + element = fSkin->GetElement( pfGUISkin::kSubMenuArrow ); + + y += ( ht >> 1 ) - ( element.fHeight >> 1 ); + if( y < 0 || y + element.fHeight >= fDynTextMap->GetHeight() ) + y = 0; + + fDynTextMap->DrawClippedImage( x + fDynTextMap->GetVisibleWidth() - 2 - element.fWidth + - fSkin->GetElement( pfGUISkin::kRightSpan ).fWidth, + y, + fSkin->GetTexture(), element.fX, element.fY, element.fWidth, element.fHeight, plDynamicTextMap::kImgBlend ); + } + else + { + fDynTextMap->SetJustify( plDynamicTextMap::kRightJustify ); + fDynTextMap->DrawString( x + fDynTextMap->GetVisibleWidth() - 2, y, ">>" ); + } + } + } + + fDynTextMap->FlushToHost(); +} + +void pfGUIMenuItem::PurgeDynaTextMapImage() +{ + if ( fDynTextMap != nil ) + fDynTextMap->PurgeImage(); +} + +//// GetTextExtents ////////////////////////////////////////////////////////// +// Calculate the size of the drawn text. + +void pfGUIMenuItem::GetTextExtents( UInt16 &width, UInt16 &height ) +{ + if( fName == nil ) + width = height = 0; + else + width = fDynTextMap->CalcStringWidth( fName, &height ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIMenuItem::MsgReceive( plMessage *msg ) +{ + return pfGUIButtonMod::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIMenuItem::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIButtonMod::Read( s, mgr ); +} + +void pfGUIMenuItem::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIButtonMod::Write( s, mgr ); +} + +//// HandleMouseDown/Up ////////////////////////////////////////////////////// + +void pfGUIMenuItem::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) +{ + pfGUIButtonMod::HandleMouseDown( mousePt, modifiers ); + IUpdate(); +} + +void pfGUIMenuItem::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) +{ + pfGUIButtonMod::HandleMouseUp( mousePt, modifiers ); + IUpdate(); +} + +void pfGUIMenuItem::HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ) +{ +/* if( !fClicking ) + return; + + if( fDraggable == nil ) + return; + + if( !fDraggable->IsVisible() ) + { + // Are we outside ourselves? + if( !PointInBounds( mousePt ) ) + { + // Yes, start dragging + StartDragging(); + + // Hand off our interest to the draggable + fDialog->SetControlOfInterest( fDraggable ); + } + } +*/ + pfGUIButtonMod::HandleMouseDrag( mousePt, modifiers ); +} + +void pfGUIMenuItem::HandleMouseHover( hsPoint3 &mousePt, UInt8 modifiers ) +{ + pfGUIButtonMod::HandleMouseHover( mousePt, modifiers ); + if( HasFlag( kReportHovers ) ) + { + if( PointInBounds( mousePt ) ) + { + if( !fReportingHover && ( fDialog->GetControlOfInterest() == nil || + fDialog->GetControlOfInterest() == this ) ) + { + fReportingHover = true; + HandleExtendedEvent( kMouseHover ); + fDialog->SetControlOfInterest( this ); + } + } + else if( fReportingHover ) + { + fReportingHover = false; + HandleExtendedEvent( kMouseExit ); + fDialog->SetControlOfInterest( nil ); + } + } +} + +//// SetInteresting ////////////////////////////////////////////////////////// +// Overridden to play mouse over animation when we're interesting + +void pfGUIMenuItem::SetInteresting( hsBool i ) +{ + pfGUIButtonMod::SetInteresting( i ); + IUpdate(); + + // Make sure we're not still thinking we're reporting hovers when we're not + if( !i ) + fReportingHover = false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMenuItem.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMenuItem.h new file mode 100644 index 00000000..f67bdfc4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMenuItem.h @@ -0,0 +1,112 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIMenuItem Header // +// // +// The type of button that knows how to party. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIMenuItem_h +#define _pfGUIMenuItem_h + +#include "pfGUIButtonMod.h" + +class plMessage; + +class pfGUISkin; +class pfGUIMenuItem : public pfGUIButtonMod +{ + public: + enum HowToSkin + { + kTop, + kMiddle, + kBottom + }; + + protected: + + wchar_t *fName; + hsBool fReportingHover; + + HowToSkin fHowToSkin; + hsBool fSkinBuffersUpdated; + + virtual void IGrowDTMDimsToDesiredSize( UInt16 &width, UInt16 &height ); + virtual void IPostSetUpDynTextMap( void ); + virtual void IUpdate( void ); + + void IUpdateSkinBuffers( void ); + void IUpdateSingleSkinBuffer( UInt16 y, hsBool sel ); + + public: + + pfGUIMenuItem(); + virtual ~pfGUIMenuItem(); + + CLASSNAME_REGISTER( pfGUIMenuItem ); + GETINTERFACE_ANY( pfGUIMenuItem, pfGUIButtonMod ); + + enum ItemFlags + { + kDrawSubMenuArrow = kDerivedFlagsStart, + kReportHovers + }; + + // Extended event types + enum ExtendedEvents + { + kMouseHover, + kMouseExit + }; + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void SetInteresting( hsBool i ); + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseHover( hsPoint3 &mousePt, UInt8 modifiers ); + + virtual void PurgeDynaTextMapImage(); + + + void SetName( const char *name ); + void SetName( const wchar_t *name ); + const wchar_t *GetName( void ) const { return fName; } + + void GetTextExtents( UInt16 &width, UInt16 &height ); + + void SetSkin( pfGUISkin *skin, HowToSkin s ); +}; + +#endif // _pfGUIMenuItem_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMultiLineEditCtrl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMultiLineEditCtrl.cpp new file mode 100644 index 00000000..db2f6353 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMultiLineEditCtrl.cpp @@ -0,0 +1,2037 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIMultiLineEditCtrl Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIMultiLineEditCtrl.h" +#include "pfGameGUIMgr.h" +#include "pfGUIUpDownPairMod.h" +#include "pfGUIControlHandlers.h" +#include "pfGUIDialogMod.h" +#include "pfGUIDialogHandlers.h" + +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plAvatar/plAGModifier.h" +#include "../plGImage/plDynamicTextMap.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Tiny Helper Class /////////////////////////////////////////////////////// + +class plStringSlicer +{ + wchar_t *fString; + wchar_t fTempChar; + UInt32 fStart, fEnd; + + typedef wchar_t *CharPtr; + + public: + plStringSlicer( wchar_t *string, UInt32 start, UInt32 end ) + { + fString = string; + fTempChar = string[ end ]; + string[ end ] = 0L; + fStart = start; + fEnd = end; + } + + plStringSlicer( hsTArray &string, UInt32 start, UInt32 end ) + { + fString = string.AcquireArray(); + fStart = start; + if( end < string.GetCount() ) + fEnd = end; + else + fEnd = fStart; + + if( fEnd > fStart ) + { + fTempChar = fString[ end ]; + fString[ end ] = 0L; + } + } + + ~plStringSlicer() + { + if( fEnd > fStart ) + fString[ fEnd ] = fTempChar; + } + + operator const CharPtr() const + { + return &fString[ fStart ]; + } +}; + +//// Wee Little Control Proc for scrolling /////////////////////////////////// + +class pfMLScrollProc : public pfGUICtrlProcObject +{ + protected: + + pfGUIMultiLineEditCtrl *fParent; + + public: + + pfMLScrollProc( pfGUIMultiLineEditCtrl *parent ) : fParent( parent ) {} + + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + // Do a check here to make sure we actually changed scroll + // positions--if not, we don't want to update, since that'll be + // slow as hell + int newScrollPos = (int)fParent->fScrollControl->GetMax() - (int)fParent->fScrollControl->GetCurrValue(); + fParent->SetScrollPosition( newScrollPos ); + } +}; + + +//// Statics ///////////////////////////////////////////////////////////////// + +wchar_t pfGUIMultiLineEditCtrl::fColorCodeChar = (wchar_t)1; +wchar_t pfGUIMultiLineEditCtrl::fStyleCodeChar = (wchar_t)2; +UInt32 pfGUIMultiLineEditCtrl::fColorCodeSize = (wchar_t)5; +UInt32 pfGUIMultiLineEditCtrl::fStyleCodeSize = (wchar_t)3; + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIMultiLineEditCtrl::pfGUIMultiLineEditCtrl() +{ + SetFlag( kWantsInterest ); + SetFlag( kTakesSpecialKeys ); + fCursorPos = 0; + fLastCursorLine = 0; + fBuffer.Append( 0L ); + fBufferLimit = -1; + fIgnoreNextKey = false; + fScrollControl = nil; + fScrollProc = nil; + fScrollPos = 0; + fReadyToRender = false; + + + fLastKeyModifiers = 0; + fLastKeyPressed = 0; + fLockCount = 0; + + fLastDeadKey = 0; + SetupDeadKeyConverter(); + + fNextCtrl = nil; + fPrevCtrl = nil; + + fEventProc = nil; + + fTopMargin = fLeftMargin = 0; + fBottomMargin = fRightMargin = 0; + + fFontFace = ""; + fFontColor.FromARGB32(0xFF000000); + fFontSize = 0; + fFontStyle = 0; + fFontFlagsSet = 0; +} + +pfGUIMultiLineEditCtrl::~pfGUIMultiLineEditCtrl() +{ + ClearNext(); // make sure that no one is referencing us + ClearPrev(); + + if( fScrollProc && fScrollProc->DecRef() ) + delete fScrollProc; + if (fEventProc) + delete fEventProc; +} + +void pfGUIMultiLineEditCtrl::SetupDeadKeyConverter() +{ + int i,j; + for (i=0; i<255; i++) + for (j=0; j<255; j++) + fDeadKeyConverter[i][j] = 0L; + + // we are adding 100 to the indexes because some of these chars have a negative index for some reason + fDeadKeyConverter['^'+100]['a'] = L'â'; + fDeadKeyConverter['^'+100]['e'] = L'ê'; + fDeadKeyConverter['^'+100]['i'] = L'î'; + fDeadKeyConverter['^'+100]['o'] = L'ô'; + fDeadKeyConverter['^'+100]['u'] = L'û'; + fDeadKeyConverter['^'+100]['A'] = L'Â'; + fDeadKeyConverter['^'+100]['E'] = L'Ê'; + fDeadKeyConverter['^'+100]['I'] = L'Î'; + fDeadKeyConverter['^'+100]['O'] = L'Ô'; + fDeadKeyConverter['^'+100]['U'] = L'Û'; + + fDeadKeyConverter['¨'+100]['a'] = L'ä'; + fDeadKeyConverter['¨'+100]['e'] = L'ë'; + fDeadKeyConverter['¨'+100]['i'] = L'ï'; + fDeadKeyConverter['¨'+100]['o'] = L'ö'; + fDeadKeyConverter['¨'+100]['u'] = L'ü'; + fDeadKeyConverter['¨'+100]['A'] = L'Ä'; + fDeadKeyConverter['¨'+100]['E'] = L'Ë'; + fDeadKeyConverter['¨'+100]['I'] = L'Ï'; + fDeadKeyConverter['¨'+100]['O'] = L'Ö'; + fDeadKeyConverter['¨'+100]['U'] = L'Ü'; + + fDeadKeyConverter['´'+100]['a'] = L'á'; + fDeadKeyConverter['´'+100]['e'] = L'é'; + fDeadKeyConverter['´'+100]['i'] = L'í'; + fDeadKeyConverter['´'+100]['o'] = L'ó'; + fDeadKeyConverter['´'+100]['u'] = L'ú'; + fDeadKeyConverter['´'+100]['y'] = L'ý'; + fDeadKeyConverter['´'+100]['A'] = L'Á'; + fDeadKeyConverter['´'+100]['E'] = L'É'; + fDeadKeyConverter['´'+100]['I'] = L'Í'; + fDeadKeyConverter['´'+100]['O'] = L'Ó'; + fDeadKeyConverter['´'+100]['U'] = L'Ú'; + fDeadKeyConverter['´'+100]['Y'] = L'Ý'; + + fDeadKeyConverter['`'+100]['a'] = L'à'; + fDeadKeyConverter['`'+100]['e'] = L'è'; + fDeadKeyConverter['`'+100]['i'] = L'ì'; + fDeadKeyConverter['`'+100]['o'] = L'ò'; + fDeadKeyConverter['`'+100]['u'] = L'ù'; + fDeadKeyConverter['`'+100]['A'] = L'À'; + fDeadKeyConverter['`'+100]['E'] = L'È'; + fDeadKeyConverter['`'+100]['I'] = L'Ì'; + fDeadKeyConverter['`'+100]['O'] = L'Ò'; + fDeadKeyConverter['`'+100]['U'] = L'Ù'; +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIMultiLineEditCtrl::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIControlMod::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIMultiLineEditCtrl::MsgReceive( plMessage *msg ) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); + if( refMsg != nil ) + { + if( refMsg->fType == kRefScrollCtrl ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fScrollControl = pfGUIValueCtrl::ConvertNoRef( refMsg->GetRef() ); + fScrollControl->SetHandler( fScrollProc ); + fScrollControl->SetStep( 1.f ); + IUpdateScrollRange(); + } + else + fScrollControl = nil; + return true; + } + } + + return pfGUIControlMod::MsgReceive( msg ); +} + +//// SetScrollPosition /////////////////////////////////////////////////////// + +void pfGUIMultiLineEditCtrl::SetScrollPosition( Int32 topLine ) +{ + if( topLine < 0 ) + topLine = 0; + else if( topLine > fLineStarts.GetCount() - ICalcNumVisibleLines() + 1 ) + topLine = fLineStarts.GetCount() - ICalcNumVisibleLines() + 1; + + if( fScrollPos == topLine ) + return; + + if (topLine >= 0) + fScrollPos = topLine; + else + fScrollPos = 0; + + IUpdate(); + + if( fScrollControl != nil ) + // Scroll control values are reversed + fScrollControl->SetCurrValue( fScrollControl->GetMax() - (hsScalar)fScrollPos ); + + HandleExtendedEvent( pfGUIMultiLineEditCtrl::kScrollPosChanged ); + + // notify thru the dialog something has changed + if (fDialog && fDialog->GetHandler()) + fDialog->GetHandler()->DoSomething(this); +} + +//// MoveCursor - by direction command//////////////////////////////////////////////// +void pfGUIMultiLineEditCtrl::MoveCursor( Direction dir ) +{ + IMoveCursor(dir); +} + + +//// IUpdateScrollRange ////////////////////////////////////////////////////// + +void pfGUIMultiLineEditCtrl::IUpdateScrollRange( void ) +{ + if( fScrollControl == nil ) + return; + + if( fLineStarts.GetCount() > ICalcNumVisibleLines() - 1 ) + { + // +1 here because the last visible line is only a partial, but we want to be able to view + // full lines all the way to the end. + hsScalar newMax = (hsScalar)( fLineStarts.GetCount() - ICalcNumVisibleLines() + 1 ); + + if( newMax != fScrollControl->GetMax() ) + { + fScrollControl->SetRange( 0, (hsScalar)(fLineStarts.GetCount() - ICalcNumVisibleLines() + 1) ); + fScrollControl->SetEnabled( true ); + if( fScrollPos > fLineStarts.GetCount() - ICalcNumVisibleLines() + 1 ) + { + fScrollPos = fLineStarts.GetCount() - ICalcNumVisibleLines() + 1; + fScrollControl->SetCurrValue( fScrollControl->GetMax() - (hsScalar)fScrollPos ); + } + + // All bets are off on scrolling, so refresh the whole area + IUpdate(); + } + } + else + { + if( fScrollControl->GetMax() > 0 ) + { + fScrollControl->SetRange( 0, 0 ); + fScrollControl->SetEnabled( false ); + fScrollPos = 0; + IUpdate(); + } + } +} + +void pfGUIMultiLineEditCtrl::SetScrollEnable( hsBool state ) +{ + if (fScrollControl == nil ) + return; + + if ( state ) + { + IUpdateScrollRange(); + } + else + { + fScrollControl->SetRange( 0, 0 ); + fScrollControl->SetEnabled( false ); + fScrollPos = 0; + IUpdate(); + } +} + +//// IPostSetUpDynTextMap //////////////////////////////////////////////////// + +void pfGUIMultiLineEditCtrl::IPostSetUpDynTextMap( void ) +{ + pfGUIColorScheme *scheme = GetColorScheme(); + + // fill in any blanks + if (!(fFontFlagsSet & kFontFaceSet)) + { + fFontFace = scheme->fFontFace; + fFontFlagsSet |= kFontFaceSet; + } + if (!(fFontFlagsSet & kFontColorSet)) + { + fFontColor = scheme->fForeColor; + fFontFlagsSet |= kFontColorSet; + } + if (!(fFontFlagsSet & kFontSizeSet)) + { + fFontSize = scheme->fFontSize; + fFontFlagsSet |= kFontSizeSet; + } + if (!(fFontFlagsSet & kFontStyleSet)) + { + fFontStyle = scheme->fFontFlags; + fFontFlagsSet |= kFontStyleSet; + } + + fDynTextMap->SetFont( fFontFace.c_str(), fFontSize, fFontStyle, + HasFlag( kXparentBgnd ) ? false : true ); + + // Calculate a height for each line + fDynTextMap->CalcStringWidth( L"The quick brown fox jumped over the lazy dog.", &fLineHeight ); + fCalcedFontSize = fFontSize; + + fReadyToRender = true; + IRecalcLineStarts( 0, true ); +} + +//// ICalcNumVisibleLines //////////////////////////////////////////////////// + +Int32 pfGUIMultiLineEditCtrl::ICalcNumVisibleLines( void ) const +{ + if (fDynTextMap == nil || fLineHeight == 0) + return 0; + Int32 numLines = 0; + numLines = (fDynTextMap->GetVisibleHeight() + fLineHeight - (fTopMargin+fBottomMargin+1))/fLineHeight; + return numLines; +} + +//// IUpdate ///////////////////////////////////////////////////////////////// +// Ranged version + +void pfGUIMultiLineEditCtrl::IUpdate( Int32 startLine, Int32 endLine ) +{ + hsColorRGBA c; + static int testingFlip = 0; + bool clearEachLine = true; + UInt32 line, x, y = 0; + Int32 numVisibleLines, lastVisibleLine; + + + if( !fReadyToRender ) + return; + + // Detect whether we need to recalc all of our dimensions entirely + if( fFontFlagsSet & (kFontFaceSet & kFontColorSet & kFontSizeSet & kFontStyleSet) ) + IPostSetUpDynTextMap(); + + if( fLineStarts.GetCount() == 0 ) + { + // Just clear and go away + fDynTextMap->ClearToColor( GetColorScheme()->fBackColor ); + if( IsFocused() ) + fDynTextMap->FrameRect( fLeftMargin, fTopMargin, 2, fLineHeight, GetColorScheme()->fSelForeColor ); + fDynTextMap->FlushToHost(); + return; + } + + // Recalculate the visible range due to our visible area and the scroll range. + // adjust the scroll position if we are linked + if (fPrevCtrl) // the first control (which has a nil fPrevCtrl) shouldn't adjust it's scroll pos because the app may want it to start there + { + IUpdateBuffer(); // make sure we are rendering the correct text + fScrollPos = GetFirstVisibleLine(); + } + numVisibleLines = ICalcNumVisibleLines(); + if (fNextCtrl || fPrevCtrl) + numVisibleLines--; // we don't want "partially visible" lines + lastVisibleLine = fScrollPos + numVisibleLines - 1; + if( lastVisibleLine > fLineStarts.GetCount() - 1 ) + lastVisibleLine = fLineStarts.GetCount() - 1; + + if( startLine < fScrollPos ) + startLine = fScrollPos; + if( endLine > lastVisibleLine ) + endLine = lastVisibleLine; + + if( startLine == fScrollPos && endLine == lastVisibleLine ) + { + c.Set( 0.f, 0.f, 0.f, 1.f ); + fDynTextMap->ClearToColor( GetColorScheme()->fBackColor ); + clearEachLine = false; + } + + // Start at our line + y = ( startLine - fScrollPos ) * fLineHeight + fTopMargin; + // And loop! + + for( line = startLine; line <= endLine; line++ ) + { + // Clear this line + if( clearEachLine ) + { + fDynTextMap->FillRect( 0, (UInt16)y, fDynTextMap->GetVisibleWidth(), fLineHeight, + GetColorScheme()->fBackColor ); + } + + UInt32 start = fLineStarts[ line ], end; + if( line == fLineStarts.GetCount() - 1 ) + end = fBuffer.GetCount(); + else + end = fLineStarts[ line + 1 ]; + + // Render the actual text + IRenderLine( fLeftMargin, (UInt16)y, start, end ); + + // Render the cursor + if( fCursorPos >= start && fCursorPos < end && IsFocused() ) + { + if( fCursorPos > start ) + x = IRenderLine( fLeftMargin, (UInt16)y, start, fCursorPos, true ); + else + x = fLeftMargin; + + fDynTextMap->FrameRect( (UInt16)x, (UInt16)y, 2, fLineHeight, GetColorScheme()->fSelForeColor ); + + // Store the cursor X,Y pair. Go figure, the ONLY time we actually need this is + // to move up or down one line, and even then it's only because we want to keep + // the same approximate horizontal position (versus same character offset) + fCurrCursorX = (UInt16)x; + fCurrCursorY = (UInt16)y; + } + y += fLineHeight; + } + if( clearEachLine && line >= fLineStarts.GetCount() && y < fDynTextMap->GetVisibleHeight()-fBottomMargin ) + { + // No lines left, so clear the rest of the visible area + fDynTextMap->FillRect( 0, (UInt16)y, fDynTextMap->GetVisibleWidth(), (UInt16)(fDynTextMap->GetVisibleHeight() - y), + GetColorScheme()->fBackColor ); + } + fDynTextMap->FlushToHost(); +} + +//// IReadColorCode ////////////////////////////////////////////////////////// +// Reads a color code from the given position and advances the given position +// appropriately + +void pfGUIMultiLineEditCtrl::IReadColorCode( Int32 &pos, hsColorRGBA &color ) const +{ + UInt16 *buffer = (UInt16 *)fBuffer.AcquireArray() + pos; + UInt8 r, g, b; + + + hsAssert( buffer[ 0 ] == fColorCodeChar, "Invalid position in IReadColorCode()" ); + buffer++; + r = (UInt8)buffer[ 0 ]; + g = (UInt8)buffer[ 1 ]; + b = (UInt8)buffer[ 2 ]; + pos += fColorCodeSize; // We have a duplicate code at the end of this block, for searching backwards + color.Set( r / 255.f, g / 255.f, b / 255.f, fFontColor.a ); +} + +//// IReadStyleCode ////////////////////////////////////////////////////////// +// Reads a style code from the given position and advances the given position +// appropriately + +void pfGUIMultiLineEditCtrl::IReadStyleCode( Int32 &pos, UInt8 &fontFlags ) const +{ + UInt16 *buffer = (UInt16 *)fBuffer.AcquireArray() + pos; + + + hsAssert( buffer[ 0 ] == fStyleCodeChar, "Invalid position in IReadStyleCode()" ); + fontFlags = (UInt8)buffer[ 1 ]; + pos += fStyleCodeSize; // We have a duplicate code at the end of this block, for searching backwards +} + +inline bool pfGUIMultiLineEditCtrl::IIsRenderable( const wchar_t c ) +{ + return ( !IIsCodeChar( c ) && c != L'\n' && c != L'\t' ); +} + +inline bool pfGUIMultiLineEditCtrl::IIsCodeChar( const wchar_t c ) +{ + return ( c == fColorCodeChar || c == fStyleCodeChar ); +} + +//// IFindLastCode Functions ///////////////////////////////////////////////// +// Given a position, these functions start at that position and work +// backward until they find their respective code type, then return that code +// type. If none is found, they set the given parameter to the default value +// and return false. + +hsBool pfGUIMultiLineEditCtrl::IFindLastColorCode( Int32 pos, hsColorRGBA &color, hsBool ignoreFirstCharacter ) const +{ + for( ; pos >= 0; pos -= IOffsetToNextCharFromPos( pos - 1 ) ) + { + if( fBuffer[ pos ] == fColorCodeChar && !ignoreFirstCharacter ) + { + IReadColorCode( pos, color ); + return true; + } + ignoreFirstCharacter = false; + } + color = fFontColor; // use our default color + return false; +} + +hsBool pfGUIMultiLineEditCtrl::IFindLastStyleCode( Int32 pos, UInt8 &style, hsBool ignoreFirstCharacter ) const +{ + for( ; pos >= 0; pos -= IOffsetToNextCharFromPos( pos - 1 ) ) + { + if( fBuffer[ pos ] == fStyleCodeChar && !ignoreFirstCharacter ) + { + IReadStyleCode( pos, style ); + return true; + } + ignoreFirstCharacter = false; + } + + style = fFontStyle; // use our default style + return false; +} + +//// IRenderLine ///////////////////////////////////////////////////////////// +// Renders a null-terminated string to the dynamic text map at the location +// given. Takes into account style codes and special characters (like returns +// and tabs). Returns the final X value after rendering. + +UInt32 pfGUIMultiLineEditCtrl::IRenderLine( UInt16 x, UInt16 y, Int32 start, Int32 end, hsBool dontRender ) +{ + Int32 pos; + hsColorRGBA currColor = fFontColor; + UInt8 currStyle; + const wchar_t *buffer = fBuffer.AcquireArray(); + + // First, gotta go back from our starting position and find a color and style code to use + IFindLastColorCode( start, currColor ); + IFindLastStyleCode( start, currStyle ); + + fDynTextMap->SetTextColor( currColor, HasFlag( kXparentBgnd ) ? true : false ); + fDynTextMap->SetFont( fFontFace.c_str(), fFontSize, GetColorScheme()->fFontFlags | currStyle, + HasFlag( kXparentBgnd ) ? false : true ); + + // Now, start from our start and go to the end and keep eating up as many chunks + // that aren't made up of control codes or carriage returns or tabs (since we render + // those separately) + for( pos = start; pos < end; ) + { + start = pos; + if( IIsRenderable( buffer[ pos ] ) ) + { + // State 1: Render a renderable chunk + while( pos < end && IIsRenderable( buffer[ pos ] ) ) + pos++; + + { + plStringSlicer slicer( fBuffer, start, pos ); + if( !dontRender ) + fDynTextMap->DrawString( x, y, slicer ); + x += fDynTextMap->CalcStringWidth( slicer ); + } + } + else + { + // State 2: Process non-renderable characters + if( buffer[ pos ] == fColorCodeChar ) + { + // Read color and switch to that one + IReadColorCode( pos, currColor ); + if( !dontRender ) + fDynTextMap->SetTextColor( currColor, HasFlag( kXparentBgnd ) ? true : false ); + } + else if( buffer[ pos ] == fStyleCodeChar ) + { + // Read style and switch to that one + IReadStyleCode( pos, currStyle ); + if( !dontRender ) + fDynTextMap->SetFont( fFontFace.c_str(), fFontSize , GetColorScheme()->fFontFlags | currStyle, + HasFlag( kXparentBgnd ) ? false : true ); + } + else if( buffer[ pos ] == L'\n' ) + { + // We stop at carriage returns + break; + } + else if( buffer[ pos ] == L'\t' ) + { + // Increment by tab amount + x += 16; + pos++; + } + else + { + hsAssert( false, "Unknown unrenderable character, ignoring" ); + pos++; + } + } + } + + return x; +} + +void pfGUIMultiLineEditCtrl::PurgeDynaTextMapImage() +{ + if ( fDynTextMap != nil ) + fDynTextMap->PurgeImage(); +} + +//// IUpdate ///////////////////////////////////////////////////////////////// + +void pfGUIMultiLineEditCtrl::IUpdate( void ) +{ + // Just call the ranged one with a full range + IUpdate( 0, fLineStarts.GetCount() - 1 ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIMultiLineEditCtrl::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Read(s, mgr); + + fScrollControl = nil; + if( s->ReadBool() ) + { + fScrollProc = TRACKED_NEW pfMLScrollProc( this ); + fScrollProc->IncRef(); + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefScrollCtrl ), plRefFlags::kActiveRef ); + } +} + +void pfGUIMultiLineEditCtrl::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Write( s, mgr ); + + if( fScrollControl != nil ) + { + s->WriteBool( true ); + mgr->WriteKey( s, fScrollControl->GetKey() ); + } + else + s->WriteBool( false ); +} + +//// IPointToPosition //////////////////////////////////////////////////////// +// Translates a 2D point on the visible texture surface to a cursor position. + +Int32 pfGUIMultiLineEditCtrl::IPointToPosition( Int16 ptX, Int16 ptY, hsBool searchOutsideBounds ) +{ + // Find our line + Int32 line, start, pos, end, lastVisibleLine; + Int16 x, y; + + if (fPrevCtrl) + fScrollPos = GetFirstVisibleLine(); // update the scroll position if we are linked + + if( searchOutsideBounds ) + lastVisibleLine = fLineStarts.GetCount() - 1; + else + { + lastVisibleLine = fScrollPos + ICalcNumVisibleLines() - 1; + if( lastVisibleLine > fLineStarts.GetCount() - 1 ) + lastVisibleLine = fLineStarts.GetCount() - 1; + } + + line = searchOutsideBounds ? 0 : fScrollPos; + y = (Int16)(-( fScrollPos - line ) * fLineHeight); + y += fTopMargin; + for( ; line < lastVisibleLine; line++, y += fLineHeight ) + { + if( ptY >= y && ptY < y + fLineHeight ) + { + if (line < 0) + break; // abort, and yes, this IS possible with this crappy code + + // Found the line, figure out what character + start = fLineStarts[ line ]; + end = ( line == fLineStarts.GetCount() - 1 ) ? fBuffer.GetCount() - 1 : fLineStarts[ line + 1 ]; + + for( pos = start; pos < end; pos++ ) + { + x = (Int16)IRenderLine( fLeftMargin, 0, start, pos, true ); + if( x > ptX ) + break; + } + + // Go figure, our test puts it 1 past no matter what :) + return pos - 1; + } + } + + // Just put us at the end of the last line + return fBuffer.GetCount() - 1; +} + +//// IIsWordBreaker ////////////////////////////////////////////////////////// +// Returns whether the given character is one that can break a line + +inline bool IIsWordBreaker( const wchar_t c ) +{ + return ( wcschr( L" \t,.;\n", c ) != nil ) ? true : false; +} + +//// IOffsetToNextChar /////////////////////////////////////////////////////// + +inline Int32 pfGUIMultiLineEditCtrl::IOffsetToNextChar( wchar_t stringChar ) +{ + if( stringChar == fColorCodeChar ) + return fColorCodeSize; + else if( stringChar == fStyleCodeChar ) + return fStyleCodeSize; + else + return 1; +} + +inline Int32 pfGUIMultiLineEditCtrl::IOffsetToNextCharFromPos( Int32 position ) const +{ + if( position >= 0 ) + return IOffsetToNextChar( fBuffer[ position ] ); + else + return 1; +} + +//// IRecalcLineStarts /////////////////////////////////////////////////////// +// Recalculates all the word wrapping/line start values, starting at the +// given line. If not forced, recalc will stop once a calculated line start +// matches one already stored (this implying that everything after will be +// the same as well, assuming contents are the same). If this assumption can't +// be made, force recalc of all the lines. +// Returns the last line recalced. +// The starting line is basically the first line whose start might have +// changed, so we assume as a hint that every line before the starting line +// is still valid. If startingLine = 0, we recalc 'em all. + +Int32 pfGUIMultiLineEditCtrl::IRecalcLineStarts( Int32 startingLine, hsBool force, hsBool dontUpdate ) +{ + UInt16 wrapWidth, widthCounter; + UInt32 charPos = 0, nextPos, startPos, lastStartPos; + Int32 currLine, realStartingLine; + bool firstLine; + wchar_t *buffer; + const wchar_t wordBreaks[] = L" \t,."; + const wchar_t wordSeparators[] = L" \t,.\n"; + + if( fPrevCtrl ) + IUpdateBuffer(); // make sure our buffer is correct if we are linked + + if( fDynTextMap == nil ) + { + // Can't calculate anything. Just return invalid + fLineStarts.Reset(); + IUpdateScrollRange(); + return -1; + } + + // Figure out our starting character + if( startingLine > 0 ) + { + if( startingLine >= fLineStarts.GetCount() ) + { + // Must be a problem, force full recalc + hsStatusMessage( "Invalid starting line in IRecalcLineStarts(), forcing full recalc" ); + currLine = 0; + force = true; + } + else + { + // Start 1 line before, since we assume that line start is still valid + currLine = startingLine - 1; + charPos = fLineStarts[ currLine ]; + } + } + else + currLine = 0; + + realStartingLine = currLine; // For the IUpdate call later + + // Precalculate some helper values + wrapWidth = fDynTextMap->GetVisibleWidth() - fRightMargin; + buffer = fBuffer.AcquireArray(); + firstLine = true; + lastStartPos = (UInt32)-1; + + for( ; charPos < fBuffer.GetCount(); currLine++ ) + { + //// Store this line start + startPos = charPos; + if( IStoreLineStart( currLine, startPos ) ) + { + if( currLine > startingLine ) + { + // We just stored a line that didn't change its starting position (first line doesn't count) + if( !force ) + break; + } + else if( currLine > realStartingLine ) + { + // Line start didn't change, but we're at the start. See, we always go one line back in case + // something changed that would cause the line before to update. But if we're here, both the + // line before and this line have the same starting position, which means that the line before + // didn't change, so we can increment realStartingLine and skip re-drawing that line + realStartingLine++; + } + } + + firstLine = false; + + //// We do a walk where we find the start of the next word (i.e. the end of this word plus + //// any "white space"), and then see if we can fit everything up to that point. If we can, + //// keep walking, if not, stop at whatever we had as a starting point. + nextPos = charPos; + for( widthCounter = 0; widthCounter < wrapWidth; ) + { + charPos = nextPos; + + // Are we on a line break? + if( nextPos >= fBuffer.GetCount() || buffer[ nextPos ] == L'\n' || buffer[ nextPos ] == 0L ) + { + charPos++; + break; // Yup, so do so + } + + // Find the end of this word + while( nextPos < fBuffer.GetCount() && !IIsWordBreaker( buffer[ nextPos ] ) ) + nextPos += IOffsetToNextChar( buffer[ nextPos ] ); + + // Now we're at some white space, keep going until we hit the next word + while( nextPos < fBuffer.GetCount() && IIsWordBreaker( buffer[ nextPos ] ) && buffer[ nextPos ] != L'\n' ) + nextPos += IOffsetToNextChar( buffer[ nextPos ] ); + + // Now see how much width this is + widthCounter = (UInt16)IRenderLine( fLeftMargin, 0, startPos, nextPos, true ); + + // Now we loop. If wrapWidth is too much, we'll break the loop with charPos pointing to the + // end of our line. If not, charPos will advance to start the search again + } + + // Check to see if the line was just too long to wrap at all + if( startPos == charPos ) + { + // OK, so just try again and break as soon as necessary. nextPos should be + // already advanced too far, so start from there and go back + while( widthCounter >= wrapWidth && nextPos > startPos ) + { + nextPos -= IOffsetToNextChar( buffer[ nextPos - 1 ] ); + widthCounter = (UInt16)IRenderLine( fLeftMargin, 0, startPos, nextPos, true ); + } + + charPos = nextPos; + } + + // Continue on! + } + + if( charPos >= fBuffer.GetCount() ) + { + // Make sure there are no lines stored after this one + fLineStarts.SetCount( currLine ); + } + + IUpdateScrollRange(); + + if( !dontUpdate ) + // We just changed some of the line starts, so time to redraw + IUpdate( realStartingLine, currLine - 1 ); + + // we just updated the line starts, so set all following controls to the same set + if (fNextCtrl) + fNextCtrl->ISetLineStarts(fLineStarts); + + return currLine; +} + +//// IStoreLineStart ///////////////////////////////////////////////////////// +// Stores a single line start, expanding the array if necessary. + +hsBool pfGUIMultiLineEditCtrl::IStoreLineStart( UInt32 line, Int32 start ) +{ + if( fLineStarts.GetCount() <= line ) + { + hsAssert( line == fLineStarts.GetCount(), "Trying to store a line way past the end of line starts!" ); + fLineStarts.Expand( line + 1 ); + fLineStarts.SetCount( line + 1 ); + fLineStarts[ line ] = -1; + } + + hsBool same = ( fLineStarts[ line ] == start ) ? true : false; + fLineStarts[ line ] = start; + return same; +} + +//// IFindCursorLine ///////////////////////////////////////////////////////// +// Calculates the line the cursor is sitting on + +Int32 pfGUIMultiLineEditCtrl::IFindCursorLine( Int32 cursorPos ) const +{ + Int32 line; + + + if( cursorPos == -1 ) + cursorPos = fCursorPos; + + for( line = 0; line < fLineStarts.GetCount() - 1; line++ ) + { + if( fLineStarts[ line + 1 ] > cursorPos ) + break; + } + return line; +} + +//// IRecalcFromCursor /////////////////////////////////////////////////////// +// Recalcs starting with the line that the cursor is sitting on + +void pfGUIMultiLineEditCtrl::IRecalcFromCursor( hsBool force ) +{ + IRecalcLineStarts( IFindCursorLine(), force ); +} + +//// IOffsetLineStarts /////////////////////////////////////////////////////// +// Given the position and offset, offsets all line starts >= that position +// by the given amount. Used to insert a character and you know that all the +// line offsets after that character should just bump up one, for example. +// Also offsets the end of the current selection if desired and in range. +// Why only the end of the selection? Because in the only cases where we're +// doing this, we are inserting into (and offseting inside) a selection, +// so we don't want the start moving around. + +void pfGUIMultiLineEditCtrl::IOffsetLineStarts( UInt32 position, Int32 offset, hsBool offsetSelectionEnd ) +{ + Int32 line; + + // Check our first line and make sure offsetting it won't make it invalid. + // If it will, we need to recalc the line starts entirely (which is fine, + // since this function is just called to try to optimize out doing so, but + // when you gotta, you gotta...) + line = IFindCursorLine( position ); + if( line < fLineStarts.GetCount() - 1 ) + { + if( fLineStarts[ line + 1 ] + offset <= fLineStarts[ line ] ) + { + // We want to force the line starts to recalc. Otherwise, IRecalc() will attempt + // to stop once it detects a line start that hasn't changed. Which doesn't work + // because our line starts just got offsetted, hence why this function was called. + // Even then, it isn't necessarily a bad thing, since that line will be OK, but + // the assumption IRecalc() makes is that all the lines *after* that must be OK too + // b/c it just found the same line start, which in this case it didn't. + // Just trust me. + IRecalcLineStarts( line, true ); + return; + } + } + + // Offset all lines past our given position + for( line = 0; line < fLineStarts.GetCount(); line++ ) + { + if( fLineStarts[ line ] > position ) + fLineStarts[ line ] += offset; + } + + // now update all following controls + if (fNextCtrl) + fNextCtrl->ISetLineStarts(fLineStarts); +} + +//// HandleMouseDown ///////////////////////////////////////////////////////// + +void pfGUIMultiLineEditCtrl::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) +{ + if( fDynTextMap == nil || !fBounds.IsInside( &mousePt ) ) + return; + + IScreenToLocalPt( mousePt ); + mousePt.fX *= fDynTextMap->GetVisibleWidth(); + mousePt.fY *= fDynTextMap->GetVisibleHeight(); + + IMoveCursorTo( IPointToPosition( (Int16)(mousePt.fX), (Int16)(mousePt.fY) ) ); +} + +//// HandleMouseUp /////////////////////////////////////////////////////////// + +void pfGUIMultiLineEditCtrl::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) +{ + if( fDynTextMap == nil || !fBounds.IsInside( &mousePt ) ) + return; + + IScreenToLocalPt( mousePt ); + mousePt.fX *= fDynTextMap->GetVisibleWidth(); + mousePt.fY *= fDynTextMap->GetVisibleHeight(); + + IMoveCursorTo( IPointToPosition( (Int16)(mousePt.fX), (Int16)(mousePt.fY) ) ); +} + +//// HandleMouseDrag ///////////////////////////////////////////////////////// + +void pfGUIMultiLineEditCtrl::HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ) +{ + if( fDynTextMap == nil || !fBounds.IsInside( &mousePt ) ) + return; + + IScreenToLocalPt( mousePt ); + mousePt.fX *= fDynTextMap->GetVisibleWidth(); + mousePt.fY *= fDynTextMap->GetVisibleHeight(); + + IMoveCursorTo( IPointToPosition( (Int16)(mousePt.fX), (Int16)(mousePt.fY) ) ); +} + +hsBool pfGUIMultiLineEditCtrl::HandleKeyPress( char keyIn, UInt8 modifiers ) +{ + wchar_t key = (wchar_t)keyIn; + + if ((fPrevCtrl || fNextCtrl) && (fLineStarts.GetCount() <= GetFirstVisibleLine())) + return true; // we're ignoring if we can't actually edit our visible frame (and we're linked) + + if (modifiers & pfGameGUIMgr::kCtrlDown) + return true; // we're ignoring ctrl key events + + if( fIgnoreNextKey ) + { + // So we don't process keys that already got handled by HandleKeyEvent() + fIgnoreNextKey = false; + return true; + } + + // Store info for the event we're about to send out + fLastKeyModifiers = modifiers; + fLastKeyPressed = key; + + // Send out a key pressed event. + HandleExtendedEvent( kKeyPressedEvent ); + + // We discard keys when locked only after we give our handler the key + if( IsLocked() ) + return true; + + if (plKeyboardDevice::KeyIsDeadKey()) + { + if (fLastDeadKey != 0) + { + wchar_t temp = key; // we have two dead keys in a row, print out the old one and store the new one + key = fLastDeadKey; + fLastDeadKey = temp; + } + else + { + fLastDeadKey = key; // store the dead key and don't print it until we get the next char + return true; + } + } + + if (fLastDeadKey != 0) // we have a dead key that needs to be added in + { + wchar_t translatedKey = fDeadKeyConverter[(char)fLastDeadKey+100][(char)key]; + if (translatedKey == 0) // no translation possible? + { + // so we need to print the dead key, followed by the typed key + // unless key is a space, then we just type the dead key + if (key == ' ') + { + // Insert character at the current cursor position, then inc the cursor by one + // Note: we always want selection mode off when we're typing + InsertChar( fLastDeadKey ); + fLastDeadKey = 0; + return true; + } + // Insert characters at the current cursor position, then inc the cursor by one + // Note: we always want selection mode off when we're typing + InsertChar( fLastDeadKey ); + InsertChar( key ); + fLastDeadKey = 0; + return true; + } + // ok, so we have a translated key now, so assign it to our key and print it normally + key = translatedKey; + fLastDeadKey = 0; + } + + // Insert character at the current cursor position, then inc the cursor by one + // Note: we always want selection mode off when we're typing + InsertChar( key ); + return true; +} + +hsBool pfGUIMultiLineEditCtrl::HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers ) +{ + if( key == KEY_CAPSLOCK ) + return false; + + if ((fPrevCtrl || fNextCtrl) && (fLineStarts.GetCount() <= GetFirstVisibleLine())) + return true; // we're ignoring if we can't actually edit our visible frame (and we're linked) + + if (modifiers & pfGameGUIMgr::kCtrlDown) + return true; // we're ignoring ctrl key events + + if( event == pfGameGUIMgr::kKeyDown || event == pfGameGUIMgr::kKeyRepeat ) + { + // Use arrow keys to do our dirty work + if( key == KEY_UP ) + IMoveCursor( kOneLineUp ); + else if( key == KEY_DOWN ) + IMoveCursor( kOneLineDown ); + else if( key == KEY_HOME ) + IMoveCursor( ( modifiers & pfGameGUIMgr::kCtrlDown ) ? kBufferStart : kLineStart ); + else if( key == KEY_END ) + IMoveCursor( ( modifiers & pfGameGUIMgr::kCtrlDown ) ? kBufferEnd : kLineEnd ); + else if( key == KEY_PAGEUP ) + IMoveCursor( kPageUp ); + else if( key == KEY_PAGEDOWN ) + IMoveCursor( kPageDown ); + + else if( key == KEY_LEFT ) + IMoveCursor( ( modifiers & pfGameGUIMgr::kCtrlDown ) ? kOneWordBack : kOneBack ); + else if( key == KEY_RIGHT ) + IMoveCursor( ( modifiers & pfGameGUIMgr::kCtrlDown ) ? kOneWordForward : kOneForward ); + + else if( key == KEY_BACKSPACE ) + { + if( IsLocked() ) + return true; + + if( fCursorPos > 0 ) + { + IMoveCursor(kOneBack); + DeleteChar(); + } + } + else if( key == KEY_DELETE ) + { + if( IsLocked() ) + return true; + + DeleteChar(); + } + else if( key == KEY_ESCAPE ) + { +// fEscapedFlag = true; + DoSomething(); // Query WasEscaped() to see if it was escape vs enter + } + else + { + fIgnoreNextKey = false; + return true; + } + + fIgnoreNextKey = true; + return true; + } + else + // We don't process them, but we don't want anybody else processing them either + return true; +} + +//// ISetCursor ////////////////////////////////////////////////////////////// +// Only moves the cursor and redraws, doesn't bother with selections. You +// should probably call IMoveCursorTo() unless you really know what you're +// doing and don't want the current selection updated. + +void pfGUIMultiLineEditCtrl::ISetCursor( Int32 newPosition ) +{ + fCursorPos = newPosition; + + Int32 newLine = IFindCursorLine(); + + // Rescroll if necessary + if( fLastCursorLine != newLine ) + { + if( newLine < fScrollPos ) + { + if (fPrevCtrl) // we are linked + { + fPrevCtrl->ISetCursor(newPosition); // so tell the prev control to set its cursor + fPrevCtrl->GetOwnerDlg()->SetFocus(fPrevCtrl); // give control to the prev control (since we just moved our cursor there) + } + else if (!fPrevCtrl && fNextCtrl) // we are linked, but there isn't anyone before us + IHitBeginningOfControlList(newPosition); // send an event to the person that wanted it + else + SetScrollPosition( newLine ); + } + else + { + // -2 here for a reason: 1 because we want the last fully visible line, not partially visible, + // and 1 because we want the actual last visible line index, which is of course start + len - 1 + Int32 delta = newLine - ( fScrollPos + ICalcNumVisibleLines() - 2 ); + if( delta > 0 ) + { + if (fNextCtrl) // we are linked + { + fNextCtrl->ISetCursor(newPosition); // so tell the next control to set its cursor + fNextCtrl->GetOwnerDlg()->SetFocus(fNextCtrl); // give control to the next control (since we just moved our cursor there) + } + else if (!fNextCtrl && fPrevCtrl) // we are linked, but there isn't anyone after us + IHitEndOfControlList(newPosition); // send an event to the person that wanted it + else + SetScrollPosition( fScrollPos + delta ); + } + } + } + + if( fLastCursorLine < newLine ) + IUpdate( fLastCursorLine, newLine ); + else + IUpdate( newLine, fLastCursorLine ); + + fLastCursorLine = newLine; +} + +//// IMoveCursor ///////////////////////////////////////////////////////////// +// Moves the cursor in a relative direction. + +void pfGUIMultiLineEditCtrl::IMoveCursor( pfGUIMultiLineEditCtrl::Direction dir ) +{ + Int32 cursor = fCursorPos, line, offset, end; + + + switch( dir ) + { + case kLineStart: + while( cursor > 0 && fBuffer[ cursor - 1 ] != L'\n' ) + cursor--; + break; + + case kLineEnd: + while( cursor < fBuffer.GetCount() - 1 && fBuffer[ cursor ] != L'\n' ) + cursor++; + break; + + case kBufferStart: + cursor = 0; + break; + + case kBufferEnd: + cursor = fBuffer.GetCount() - 1; + break; + + case kOneBack: + if( cursor > 0 ) + { + cursor--; + while( cursor > 0 && ( fBuffer[ cursor ] == fColorCodeChar || fBuffer[ cursor ] == fStyleCodeChar ) ) + cursor -= IOffsetToNextChar( fBuffer[ cursor ] ); + } + break; + + case kOneForward: + if( cursor < fBuffer.GetCount() - 1 ) + { + cursor++; + while( cursor < fBuffer.GetCount() - 1 && ( fBuffer[ cursor ] == fColorCodeChar || fBuffer[ cursor ] == fStyleCodeChar ) ) + cursor += IOffsetToNextChar( fBuffer[ cursor ] ); + } + break; + + case kOneWordBack: + if( cursor > 0 ) + { + for( ; cursor > 0 && IIsWordBreaker( fBuffer[ cursor - 1 ] ); cursor-- ); + for( ; cursor > 0 && !IIsWordBreaker( fBuffer[ cursor - 1 ] ); cursor-- ); + } + break; + + case kOneWordForward: + if( cursor < fBuffer.GetCount() - 1 ) + { + for( ; cursor < fBuffer.GetCount() - 1 && !IIsWordBreaker( fBuffer[ cursor ] ); cursor++ ); + for( ; cursor < fBuffer.GetCount() - 1 && IIsWordBreaker( fBuffer[ cursor ] ); cursor++ ); + } + break; + + case kOneLineUp: + // The wonderful thing is, since we keep going on the position of the cursor (which magically + // is the left side of the character we're on), we end up drifting left as we keep going up + // or down. So to compensate, we put a little fudge factor in that lets us attempt to stay + // "on course", as it were. + if( IFindCursorLine( cursor ) > 0 ) + cursor = IPointToPosition( fCurrCursorX + ( fLineHeight >> 2 ), fCurrCursorY - fLineHeight, true ); + break; + + case kOneLineDown: + if( IFindCursorLine( cursor ) < fLineStarts.GetCount() - 1 ) + cursor = IPointToPosition( fCurrCursorX + ( fLineHeight >> 2 ), fCurrCursorY + fLineHeight, true ); + break; + + case kPageUp: + line = IFindCursorLine( cursor ); + offset = cursor - fLineStarts[ line ]; + + line -= ( ICalcNumVisibleLines() - 1 ); + if( line < 0 ) + line = 0; + + end = ( line < fLineStarts.GetCount() - 1 ) ? fLineStarts[ line + 1 ] : fBuffer.GetCount() - 1; + if( fLineStarts[ line ] + offset > end ) + offset = end - fLineStarts[ line ] - 1; + + cursor = fLineStarts[ line ] + offset; + break; + + case kPageDown: + line = IFindCursorLine( cursor ); + offset = cursor - fLineStarts[ line ]; + + line += ( ICalcNumVisibleLines() - 1 ); + if( line > fLineStarts.GetCount() - 1 ) + line = fLineStarts.GetCount() - 1; + + end = ( line < fLineStarts.GetCount() - 1 ) ? fLineStarts[ line + 1 ] : fBuffer.GetCount() - 1; + if( fLineStarts[ line ] + offset > end ) + offset = end - fLineStarts[ line ] - 1; + + cursor = fLineStarts[ line ] + offset; + break; + } + + IMoveCursorTo( cursor ); +} + +//// IMoveCursorTo /////////////////////////////////////////////////////////// +// Moves the cursor to the given absolute position and updates the selection +// area accordingly and if necessary. + +void pfGUIMultiLineEditCtrl::IMoveCursorTo( Int32 position ) +{ + ISetCursor( position ); +} + +////////////////////////////////////////////////////////////////////////////// +//// Buffer Modification ///////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// InsertChar ////////////////////////////////////////////////////////////// +// Inserts a character at the cursor, or if there is a selection, replaces +// the selection with the given character. Either way, at the end the cursor +// is after the inserted character. + +void pfGUIMultiLineEditCtrl::InsertChar( char c ) +{ + InsertChar((wchar_t)c); // a simple cast should be fine +} + +void pfGUIMultiLineEditCtrl::InsertChar( wchar_t c ) +{ + // Error checking--make sure our actual character isn't in the range of our + // code characters, or chaos could erupt + //if( c < 3 ) + //return; + if (c == 0) + return; + + if ( fBufferLimit == -1 || fBuffer.GetCount()+1 < fBufferLimit-1) + { + fBuffer.Insert( fCursorPos, c ); + + ISetGlobalBuffer(); // update the global buffer + + IOffsetLineStarts( fCursorPos, 1 ); + IMoveCursor( kOneForward ); + IRecalcFromCursor(); + } +} + +//// InsertString //////////////////////////////////////////////////////////// +// Same as InsertChar, only with a null-terminated string of characters +// instead of just one. + +void pfGUIMultiLineEditCtrl::InsertString( const char *string ) +{ + wchar_t* temp = hsStringToWString(string); + InsertString(temp); + delete [] temp; +} + +void pfGUIMultiLineEditCtrl::InsertString( const wchar_t *string ) +{ + int numChars = wcslen( string ); + + if ( fBufferLimit == -1 || fBuffer.GetCount() + numChars < fBufferLimit-1) + { + // Don't freak out, the cast is OK here. Insert() wants an array of chars. + // It should really take a const array, since it's just copying them, + // but since it doesn't and we know it's just copying them, we cast and + // be happy for now. + fBuffer.Insert( fCursorPos, numChars, (wchar_t *)string ); + + ISetGlobalBuffer(); // update the global buffer + + IOffsetLineStarts( fCursorPos, numChars ); + IMoveCursorTo( fCursorPos + numChars ); + IRecalcFromCursor(); + } +} + +//// InsertColor ///////////////////////////////////////////////////////////// +// Inserts a color control code into the buffer, or if there is a selection, +// places TWO color codes in: one before the selection with the given color +// and the other after the given selection with the previous color. +// There is one catch to all of this, though: if we have a selection AND +// we're inserting a code that conflicts with any code contained in the +// selection, we remove the conflicting codes. + +void pfGUIMultiLineEditCtrl::InsertColor( hsColorRGBA &color ) +{ + IActuallyInsertColor( fCursorPos, color ); + IOffsetLineStarts( fCursorPos, fColorCodeSize ); + fCursorPos += fColorCodeSize; + IRecalcFromCursor( true ); // Force update of all following lines, since + // insertion of this code changes appearance of following characters +} + +void pfGUIMultiLineEditCtrl::IActuallyInsertColor( Int32 pos, hsColorRGBA &color ) +{ + if ( fBufferLimit == -1 || fBuffer.GetCount()+4 < fBufferLimit-1 ) + { + fBuffer.Insert( pos, fColorCodeChar ); + fBuffer.Insert( pos + 1, (UInt8)( color.r * 255.f ) ); + fBuffer.Insert( pos + 2, (UInt8)( color.g * 255.f ) ); + fBuffer.Insert( pos + 3, (UInt8)( color.b * 255.f ) ); + fBuffer.Insert( pos + 4, fColorCodeChar ); + } +} + +//// InsertStyle ///////////////////////////////////////////////////////////// +// Same thing as InsertColor(), only with a style code (or two). + +void pfGUIMultiLineEditCtrl::InsertStyle( UInt8 fontStyle ) +{ + IActuallyInsertStyle( fCursorPos, fontStyle ); + + IOffsetLineStarts( fCursorPos, fStyleCodeSize ); + fCursorPos += fStyleCodeSize; + IRecalcFromCursor( true ); // Force update of all following lines, since + // insertion of this code changes appearance of following characters +} + +void pfGUIMultiLineEditCtrl::IActuallyInsertStyle( Int32 pos, UInt8 style ) +{ + if ( fBufferLimit == -1 || fBuffer.GetCount() + 3 < fBufferLimit-1 ) + { + fBuffer.Insert( pos, fStyleCodeChar ); + fBuffer.Insert( pos + 1, (wchar_t)style ); + fBuffer.Insert( pos + 2, fStyleCodeChar ); + } +} + +//// DeleteChar ////////////////////////////////////////////////////////////// +// If there is no selection, deletes the single character that the cursor +// is in front of. Otherwise, deletes the current selection and places the +// cursor where the selection used to be. + +void pfGUIMultiLineEditCtrl::DeleteChar( void ) +{ + if( fCursorPos < fBuffer.GetCount() - 1 ) + { + Int32 offset = IOffsetToNextChar( fBuffer[ fCursorPos ] ); + bool forceUpdate = IIsCodeChar( fBuffer[ fCursorPos ] ); + fBuffer.Remove( fCursorPos, offset ); + + ISetGlobalBuffer(); // update the global buffer + + IOffsetLineStarts( fCursorPos, -offset ); + IRecalcFromCursor( forceUpdate ); + } +} + +//// ICopyRange ////////////////////////////////////////////////////////////// +// Generic coded-to-non-coded conversion. Returns a copy of the string that +// the caller must free. + +wchar_t *pfGUIMultiLineEditCtrl::ICopyRange( Int32 start, Int32 end ) const +{ + Int32 stringSize, pos; + wchar_t *string; + + + // First loop, just count how much space we need + for( stringSize = 0, pos = start; pos < end; pos = pos + IOffsetToNextChar( fBuffer[ pos ] ) ) + { + if( !IIsCodeChar( fBuffer[ pos ] ) ) + stringSize++; + } + + // Our string... + string = TRACKED_NEW wchar_t[ stringSize + 1 ]; + + // Now actually copy the characters + for( stringSize = 0, pos = start; pos < end; pos = pos + IOffsetToNextChar( fBuffer[ pos ] ) ) + { + if( !IIsCodeChar( fBuffer[ pos ] ) ) + string[ stringSize++ ] = fBuffer[ pos ]; + } + string[ stringSize++ ] = 0; + + // All done! + return string; +} + +//// ClearBuffer ///////////////////////////////////////////////////////////// +// Clears everything, including the undo list. + +void pfGUIMultiLineEditCtrl::ClearBuffer( void ) +{ + fBuffer.Reset(); + fBuffer.Append( 0 ); + fCursorPos = 0; + fLastCursorLine = 0; + fScrollPos = 0; + IRecalcLineStarts( 0, true ); +} + +//// SetBuffer /////////////////////////////////////////////////////////////// +// Replaces the entire contents of the buffer with the given text. Also +// clears the undo list. + +void pfGUIMultiLineEditCtrl::SetBuffer( const char *asciiText ) +{ + SetBuffer( (const UInt8 *)asciiText, (UInt32)strlen( asciiText ) ); +} + +void pfGUIMultiLineEditCtrl::SetBuffer( const wchar_t *asciiText ) +{ + SetBuffer( (const UInt16 *)asciiText, (UInt32)wcslen( asciiText ) ); +} + +//// SetBuffer /////////////////////////////////////////////////////////////// +// The non-0-terminated-string version that can handle buffers with style +// codes in them. + +void pfGUIMultiLineEditCtrl::SetBuffer( const UInt8 *codedText, UInt32 length ) +{ + // convert to UInt16 and set + UInt16 *convertedText = TRACKED_NEW UInt16[ length ]; + for( Int32 curChar = 0; curChar < length; curChar++ ) + convertedText[ curChar ] = (UInt16)codedText[ curChar ]; + SetBuffer(convertedText,length); + delete [] convertedText; +} + +void pfGUIMultiLineEditCtrl::SetBuffer( const UInt16 *codedText, UInt32 length ) +{ + // recursively call back to the first control and set it + if (fPrevCtrl) + { + fPrevCtrl->SetBuffer(codedText,length); + IUpdateBuffer(); + } + else // we are the first control, so set our buffer + { + fBuffer.Reset(); + // Why o why doesn't Insert() take a const array.... + fBuffer.Insert( 0, length, (wchar_t *)codedText ); + fBuffer.Append( 0 ); + IRecalcLineStarts( 0, true ); // only the first control will recalc, this function recurses down to handle all following controls + } + + fScrollPos = GetFirstVisibleLine(); + fCursorPos = 0; + fLastCursorLine = 0; +} + +//// GetNonCodedBuffer /////////////////////////////////////////////////////// +// Copies the entire buffer into an ASCII string (i.e. strips formatting) +// and returns it. The caller is responsible for freeing it. +// To avoid code duplication, we'll just cheat and use CopySelection()... + +char *pfGUIMultiLineEditCtrl::GetNonCodedBuffer( void ) const +{ + // recursively search back to the first control in the linked list and grab its buffer + if (fPrevCtrl) + return fPrevCtrl->GetNonCodedBuffer(); + else + { + // -1 for the null terminator + wchar_t *buffer = ICopyRange( 0, fBuffer.GetCount() - 1 ); + char *retVal = hsWStringToString(buffer); + delete [] buffer; + + return retVal; + } +} + +wchar_t *pfGUIMultiLineEditCtrl::GetNonCodedBufferW( void ) const +{ + // recursively search back to the first control in the linked list and grab its buffer + if (fPrevCtrl) + return fPrevCtrl->GetNonCodedBufferW(); + else + { + // -1 for the null terminator + return ICopyRange( 0, fBuffer.GetCount() - 1 ); + } +} + +//// GetCodedBuffer ////////////////////////////////////////////////////////// +// Basically does a blanket copy of the entire buffer and returns it and +// the length. The caller is responsible for freeing the buffer. + +UInt8 *pfGUIMultiLineEditCtrl::GetCodedBuffer( UInt32 &length ) const +{ + // recursively search back to the first control in the linked list and grab its buffer + if (fPrevCtrl) + return fPrevCtrl->GetCodedBuffer(length); + else + { + length = fBuffer.GetCount() - 1; + + // convert to UInt8 and return + UInt8 *buffer = TRACKED_NEW UInt8[ length ]; + + for (Int32 curChar = 0; curChar < length; curChar++) + { + if (fBuffer[ curChar ] > (wchar_t)0xFF) + { + // char doesn't fit, fake it with a space + buffer[ curChar ] = (UInt8)(L' '); + } + else + buffer[ curChar ] = (UInt8)fBuffer[ curChar ]; + } + return buffer; + } +} + +UInt16 *pfGUIMultiLineEditCtrl::GetCodedBufferW( UInt32 &length ) const +{ + // recursively search back to the first control in the linked list and grab its buffer + if (fPrevCtrl) + return fPrevCtrl->GetCodedBufferW(length); + else + { + length = fBuffer.GetCount() - 1; + + UInt16 *buffer = TRACKED_NEW UInt16[ length ]; + + // AcquireArray() isn't const... + memcpy( buffer, &fBuffer[ 0 ], length * sizeof(UInt16) ); + + return buffer; + } +} + +UInt32 pfGUIMultiLineEditCtrl::GetBufferSize() +{ + return fBuffer.GetCount() - 1; +} + +//// ICharPosToBufferPos ///////////////////////////////////////////////////// +// Given a character position (i.e. a buffer position if we didn't have +// control codes), returns the actual buffer pos. + +Int32 pfGUIMultiLineEditCtrl::ICharPosToBufferPos( Int32 charPos ) const +{ + Int32 pos; + + + for( pos = 0; charPos > 0 && pos < fBuffer.GetCount() - 1; pos += IOffsetToNextCharFromPos( pos ), charPos-- ); + + return pos; +} + +//// Locking ///////////////////////////////////////////////////////////////// + +void pfGUIMultiLineEditCtrl::Lock( void ) +{ + fLockCount++; + IUpdate(); +} + +void pfGUIMultiLineEditCtrl::Unlock( void ) +{ + fLockCount--; + //hsAssert( fLockCount >= 0, "Too many unlocks for pfGUIMultiLineEditCtrl" ); + if (fLockCount < 0) + fLockCount = 0; // instead of asserting, hande it nicely + IUpdate(); +} + +// linking functions, for linking multiple multi-line edit controls together + +void pfGUIMultiLineEditCtrl::SetNext( pfGUIMultiLineEditCtrl *newNext ) +{ + ClearNext(); // clear the existing link (if there is one) + if (newNext) + { + newNext->ClearPrev(); // clear any existing prev link + fNextCtrl = newNext; + fNextCtrl->fPrevCtrl = this; + fNextCtrl->IUpdateBuffer(); + fNextCtrl->fScrollPos = fNextCtrl->GetFirstVisibleLine(); + } +} + +void pfGUIMultiLineEditCtrl::ClearNext() +{ + if (fNextCtrl) + fNextCtrl->fPrevCtrl = nil; // unlink ourselves from the next control + fNextCtrl = nil; +} + +void pfGUIMultiLineEditCtrl::SetPrev( pfGUIMultiLineEditCtrl *newPrev ) +{ + ClearPrev(); // clear existing link + if (newPrev) + { + newPrev->ClearNext(); // clear any existing next link + fPrevCtrl = newPrev; + fPrevCtrl->fNextCtrl = this; + IUpdateBuffer(); + fScrollPos = GetFirstVisibleLine(); + } +} + +void pfGUIMultiLineEditCtrl::ClearPrev() +{ + if (fPrevCtrl) + fPrevCtrl->fNextCtrl = nil; // unlink ourselves from the prev control + fPrevCtrl = nil; +} + +void pfGUIMultiLineEditCtrl::SetEventProc(pfGUIMultiLineEditProc *eventProc) +{ + if (fPrevCtrl) + fPrevCtrl->SetEventProc(eventProc); + else + { + if (fEventProc) + delete fEventProc; + fEventProc = eventProc; + } +} + +void pfGUIMultiLineEditCtrl::ClearEventProc() +{ + if (fPrevCtrl) + fPrevCtrl->ClearEventProc(); + else + { + if (fEventProc) + delete fEventProc; + fEventProc = nil; + } +} + +Int32 pfGUIMultiLineEditCtrl::GetFirstVisibleLine() +{ + // recursively search back to the first control and work our way forwards to where we are supposed to be + if (fPrevCtrl) + return fPrevCtrl->GetLastVisibleLine(); + return fScrollPos; // we're the first control, so we show the first part of the buffer +} + +Int32 pfGUIMultiLineEditCtrl::GetLastVisibleLine() +{ + // simply add our number of lines to our first visible line + return GetFirstVisibleLine()+ICalcNumVisibleLines()-1; +} + +void pfGUIMultiLineEditCtrl::SetGlobalStartLine(Int32 line) +{ + // recursively call back to the first control and set it + if (fPrevCtrl) + fPrevCtrl->SetGlobalStartLine(line); + else + { + fScrollPos = line; + IRecalcLineStarts(fScrollPos,true); + } +} + +void pfGUIMultiLineEditCtrl::IUpdateLineStarts() +{ + // make sure our line starts array matches the gloabl one + if (fPrevCtrl) + { + fPrevCtrl->IUpdateLineStarts(); + fLineStarts = fPrevCtrl->fLineStarts; + } +} + +void pfGUIMultiLineEditCtrl::IUpdateBuffer() +{ + // make sure our local buffer matches the global one + if (fPrevCtrl) + { + // copy the buffer from our global one + UInt32 length; + UInt16 *codedText = GetCodedBufferW(length); + fBuffer.Reset(); + fBuffer.Insert( 0, length, (wchar_t *)codedText ); + fBuffer.Append( 0 ); + delete [] codedText; + } +} + +void pfGUIMultiLineEditCtrl::ISetGlobalBuffer() +{ + if (fPrevCtrl) + { + fPrevCtrl->fBuffer.Reset(); + int i; + for (i=0; ifBuffer.Append(fBuffer[i]); + fPrevCtrl->ISetGlobalBuffer(); // pass the update backwards + } +} + +void pfGUIMultiLineEditCtrl::ISetLineStarts(hsTArray lineStarts) +{ + if (fNextCtrl) + fNextCtrl->ISetLineStarts(lineStarts); // pass it on down + + IUpdateBuffer(); // make sure the buffer is correct + fLineStarts = lineStarts; + + IUpdate(); // make sure everything looks right +} + +void pfGUIMultiLineEditCtrl::SetMargins(int top, int left, int bottom, int right) +{ + fTopMargin = top; + fLeftMargin = left; + fBottomMargin = bottom; + fRightMargin = right; + IRecalcLineStarts(0,false); +} + +void pfGUIMultiLineEditCtrl::IHitEndOfControlList(Int32 cursorPos) +{ + if (fPrevCtrl) + fPrevCtrl->IHitEndOfControlList(cursorPos); + else + { + if (fEventProc) + fEventProc->OnEndOfControlList(cursorPos); + } +} + +void pfGUIMultiLineEditCtrl::IHitBeginningOfControlList(Int32 cursorPos) +{ + if (fPrevCtrl) + fPrevCtrl->IHitBeginningOfControlList(cursorPos); + else + { + if (fEventProc) + fEventProc->OnBeginningOfControlList(cursorPos); + } +} + +void pfGUIMultiLineEditCtrl::SetFontFace(std::string fontFace) +{ + fFontFace = fontFace; + fFontFlagsSet |= kFontFaceSet; + fDynTextMap->SetFont( fFontFace.c_str(), fFontSize, fFontStyle, + HasFlag( kXparentBgnd ) ? false : true ); + fDynTextMap->CalcStringWidth( "The quick brown fox jumped over the lazy dog.", &fLineHeight ); +} + +void pfGUIMultiLineEditCtrl::SetFontSize(UInt8 fontSize) +{ + fFontSize = fontSize; + fFontFlagsSet |= kFontSizeSet; + fCalcedFontSize = fontSize; + fDynTextMap->SetFont( fFontFace.c_str(), fFontSize, fFontStyle, + HasFlag( kXparentBgnd ) ? false : true ); + fDynTextMap->CalcStringWidth( "The quick brown fox jumped over the lazy dog.", &fLineHeight ); +} + +// are we showing the beginning of the buffer? (controls before us are too far up) +hsBool pfGUIMultiLineEditCtrl::ShowingBeginningOfBuffer() +{ + if (fScrollPos == 0) + return true; + return false; +} + +// are we showing the end of the buffer? (controls after us are too far down) +hsBool pfGUIMultiLineEditCtrl::ShowingEndOfBuffer() +{ + //IRecalcLineStarts(0,true); // This function gets called a lot from the journal book, so IRecalcLineStarts() REALLY slows things + // down if we're looking at a large amount of text, hopefully we can mess with the existing line starts for now without issue + if (GetLastVisibleLine() >= fLineStarts.GetCount()) + return true; + return false; +} + +void pfGUIMultiLineEditCtrl::DeleteLinesFromTop(int numLines) +{ + if (fPrevCtrl || fNextCtrl) + return; // don't do anything + + UInt32 bufferLen = 0; + UInt16* buffer = GetCodedBufferW(bufferLen); + + if (bufferLen == 0) + { + delete [] buffer; + return; + } + + for (int curLine = 0; curLine < numLines; ++curLine) + { + bool hitEnd = true; // did we hit the end of the buffer before we hit a newline? + + // search for the first newline and nuke it and everything before it + bool skippingColor = false, skippingStyle = false; + int curColorPos = 0, curStylePos = 0; + for (UInt32 curChar = 0; curChar < bufferLen - 1; ++curChar) + { + // we need to skip the crappy color and style "tags" so non-character values inside them + // don't trigger our newline check + if (!skippingColor && !skippingStyle) + { + if (buffer[curChar] == fColorCodeChar) + { + curColorPos = 0; + skippingColor = true; + continue; + } + else if (buffer[curChar] == fStyleCodeChar) + { + curStylePos = 0; + skippingStyle = true; + continue; + } + } + + if (skippingColor) + { + ++curColorPos; + if (curColorPos == fColorCodeSize) + skippingColor = false; + else + continue; + } + + if (skippingStyle) + { + ++curStylePos; + if (curStylePos == fStyleCodeSize) + skippingStyle = false; + else + continue; + } + + // if it's a newline, erase it and everything before it + if ((buffer[curChar] == L'\n') || (buffer[curChar] == L'\r')) + { + hitEnd = false; + UInt32 newBufferStart = curChar + 1; // +1 so we eat the newline as well + UInt32 newBufferLen = bufferLen - newBufferStart; + MemCopy(buffer, buffer + newBufferStart, newBufferLen * sizeof(UInt16)); // copy all bytes after the newline to the beginning + MemSet(buffer + newBufferLen, 0, (bufferLen - newBufferLen) * sizeof(UInt16)); // fill out the rest of the buffer with null chars + bufferLen = newBufferLen; + break; + } + } + + if (hitEnd) + { + delete [] buffer; + SetBuffer(L""); // we are removing too many (or all) lines, just clear it + return; + } + + // still got lines to go, start looking for the next line + } + + // we got here, so buffer is now our new buffer + SetBuffer(buffer, bufferLen); + delete [] buffer; + return; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMultiLineEditCtrl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMultiLineEditCtrl.h new file mode 100644 index 00000000..7193b250 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIMultiLineEditCtrl.h @@ -0,0 +1,273 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIMultiLineEditCtrl Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIMultiLineEditCtrl_h +#define _pfGUIMultiLineEditCtrl_h + +#include "pfGUIControlMod.h" +#include "hsTemplates.h" + +#include "../plInputCore/plInputDevice.h" + +class plMessage; +class hsGMaterial; +class plTextGenerator; +class pfMLScrollProc; +class pfGUIValueCtrl; + +struct plUndoAction; + +class pfGUIMultiLineEditProc +{ +public: + pfGUIMultiLineEditProc() {} + virtual ~pfGUIMultiLineEditProc() {} + + // we've hit the end of the control list (by moving the cursor) + virtual OnEndOfControlList(Int32 cursorPos) {} + + // we've hit the beginning of the control ist (by moving the cursor) + virtual OnBeginningOfControlList(Int32 cursorPos) {} +}; + +class pfGUIMultiLineEditCtrl : public pfGUIControlMod +{ + public: + enum Direction + { + kLineStart = 1, + kLineEnd, + kBufferStart, + kBufferEnd, + kOneBack, + kOneForward, + kOneWordBack, + kOneWordForward, + kOneLineUp, + kOneLineDown, + kPageUp, + kPageDown + }; + + protected: + + mutable hsTArray fBuffer; // Because AcquireArray() isn't const + + hsTArray fLineStarts; + UInt16 fLineHeight, fCurrCursorX, fCurrCursorY; + Int32 fCursorPos, fLastCursorLine; + hsBool fIgnoreNextKey, fReadyToRender; + hsBounds3Ext fLastP2PArea; + Int8 fLockCount; + UInt8 fCalcedFontSize; // The font size that we calced our line height at + + UInt8 fLastKeyModifiers; + wchar_t fLastKeyPressed; + + static wchar_t fColorCodeChar, fStyleCodeChar; + static UInt32 fColorCodeSize, fStyleCodeSize; + + wchar_t fLastDeadKey; // if the previous key was a dead key, its value goes here + wchar_t fDeadKeyConverter[256][256]; // first index is the dead key, second index is the char to combine it with + + void SetupDeadKeyConverter(); + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + virtual void IPostSetUpDynTextMap( void ); + virtual void IUpdate( void ); + void IUpdate( Int32 startLine, Int32 endLine ); + + friend class pfMLScrollProc; + + pfGUIValueCtrl *fScrollControl; + pfMLScrollProc *fScrollProc; + Int32 fScrollPos; + Int32 fBufferLimit; + + pfGUIMultiLineEditCtrl *fNextCtrl; // used for linking multiple controls together to share a buffer + pfGUIMultiLineEditCtrl *fPrevCtrl; + + pfGUIMultiLineEditProc *fEventProc; // where we send events to + + std::string fFontFace; + hsColorRGBA fFontColor; + UInt8 fFontSize; + UInt8 fFontStyle; + enum flagsSet + { + kFontFaceSet = 1, + kFontColorSet = 2, + kFontSizeSet = 4, + kFontStyleSet = 8 + }; + UInt8 fFontFlagsSet; + + int fTopMargin,fLeftMargin,fBottomMargin,fRightMargin; + + void IMoveCursor( Direction dir ); + void IMoveCursorTo( Int32 position ); // Updates selection + void ISetCursor( Int32 newPosition ); // Doesn't update selection + + Int32 IRecalcLineStarts( Int32 startingLine, hsBool force, hsBool dontUpdate = false ); + void IRecalcFromCursor( hsBool forceUpdate = false ); + Int32 IFindCursorLine( Int32 cursorPos = -1 ) const; + hsBool IStoreLineStart( UInt32 line, Int32 start ); + void IOffsetLineStarts( UInt32 position, Int32 offset, hsBool offsetSelectionEnd = false ); + Int32 IPointToPosition( Int16 x, Int16 y, hsBool searchOutsideBounds = false ); + Int32 ICalcNumVisibleLines( void ) const; + + void IReadColorCode( Int32 &pos, hsColorRGBA &color ) const; + void IReadStyleCode( Int32 &pos, UInt8 &fontStyle ) const; + UInt32 IRenderLine( UInt16 x, UInt16 y, Int32 start, Int32 end, hsBool dontRender = false ); + hsBool IFindLastColorCode( Int32 pos, hsColorRGBA &color, hsBool ignoreFirstCharacter = false ) const; + hsBool IFindLastStyleCode( Int32 pos, UInt8 &style, hsBool ignoreFirstCharacter = false ) const; + + inline static bool IIsCodeChar( const wchar_t c ); + inline static bool IIsRenderable( const wchar_t c ); + inline static Int32 IOffsetToNextChar( wchar_t stringChar ); + inline Int32 IOffsetToNextCharFromPos( Int32 pos ) const; + + void IActuallyInsertColor( Int32 pos, hsColorRGBA &color ); + void IActuallyInsertStyle( Int32 pos, UInt8 style ); + + void IUpdateScrollRange( void ); + + wchar_t *ICopyRange( Int32 start, Int32 end ) const; + + Int32 ICharPosToBufferPos( Int32 charPos ) const; + + void IUpdateBuffer(); + void IUpdateLineStarts(); + void ISetGlobalBuffer(); + void ISetLineStarts(hsTArray lineStarts); + + void IHitEndOfControlList(Int32 cursorPos); + void IHitBeginningOfControlList(Int32 cursorPos); + + public: + + enum + { + kRefScrollCtrl = kRefDerivedStart + }; + + pfGUIMultiLineEditCtrl(); + virtual ~pfGUIMultiLineEditCtrl(); + + CLASSNAME_REGISTER( pfGUIMultiLineEditCtrl ); + GETINTERFACE_ANY( pfGUIMultiLineEditCtrl, pfGUIControlMod ); + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ); + + virtual hsBool HandleKeyPress( char key, UInt8 modifiers ); + virtual hsBool HandleKeyEvent( pfGameGUIMgr::EventType event, plKeyDef key, UInt8 modifiers ); + + virtual void PurgeDynaTextMapImage(); + + // Extended event types + enum ExtendedEvents + { + kValueChanging, + kScrollPosChanged, + kKeyPressedEvent + }; + + void SetScrollPosition( Int32 topLine ); + void MoveCursor( Direction dir ); + + void InsertChar( char c ); + void InsertChar( wchar_t c); + void InsertString( const char *string ); + void InsertString( const wchar_t *string ); + void InsertColor( hsColorRGBA &color ); + void InsertStyle( UInt8 fontStyle ); + void DeleteChar( void ); + void ClearBuffer( void ); + void SetBuffer( const char *asciiText ); + void SetBuffer( const wchar_t *asciiText ); + void SetBuffer( const UInt8 *codedText, UInt32 length ); + void SetBuffer( const UInt16 *codedText, UInt32 length ); + char *GetNonCodedBuffer( void ) const; + wchar_t *GetNonCodedBufferW( void ) const; + UInt8 *GetCodedBuffer( UInt32 &length ) const; + UInt16 *GetCodedBufferW( UInt32 &length ) const; + UInt32 GetBufferSize(); + + void SetBufferLimit(Int32 limit) { fBufferLimit = limit; } + Int32 GetBufferLimit() { return fBufferLimit; } + + void GetThisKeyPressed( char &key, UInt8 &modifiers ) const { key = (char)fLastKeyPressed; modifiers = fLastKeyModifiers; } + + void Lock( void ); + void Unlock( void ); + hsBool IsLocked( void ) const { return ( fLockCount > 0 ) ? true : false; } + + void SetScrollEnable( hsBool state ); + + void ForceUpdate() {/*IRecalcLineStarts(0,true);*/IUpdateLineStarts(); IUpdate();} + + void SetNext( pfGUIMultiLineEditCtrl *newNext ); + void ClearNext(); + void SetPrev( pfGUIMultiLineEditCtrl *newPrev ); + void ClearPrev(); + void SetEventProc( pfGUIMultiLineEditProc *eventProc ); + void ClearEventProc(); + Int32 GetFirstVisibleLine(); + Int32 GetLastVisibleLine(); + Int32 GetNumVisibleLines() {return ICalcNumVisibleLines();} + void SetGlobalStartLine(Int32 line); + + void SetCursorToLoc(Int32 loc) {ISetCursor(loc);} + + void SetMargins(int top, int left, int bottom, int right); + + UInt8 GetFontSize() {return fFontSize;} // because we're too cool to use the color scheme crap + + void SetFontFace(std::string fontFace); + void SetFontColor(hsColorRGBA fontColor) {fFontColor = fontColor; fFontFlagsSet |= kFontColorSet;} + void SetFontSize(UInt8 fontSize); + void SetFontStyle(UInt8 fontStyle) {fFontStyle = fontStyle; fFontFlagsSet |= kFontStyleSet;} + + hsBool ShowingBeginningOfBuffer(); + hsBool ShowingEndOfBuffer(); + + void DeleteLinesFromTop(int numLines); // cursor and scroll position might be off after this call, not valid on connected controls +}; + +#endif // _pfGUIMultiLineEditCtrl_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIPopUpMenu.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIPopUpMenu.cpp new file mode 100644 index 00000000..dc0dc9f3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIPopUpMenu.cpp @@ -0,0 +1,940 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIPopUpMenu Header // +// // +// Pop-up menus are really just dialogs that know how to create themselves // +// and create buttons on themselves to simulate a menu (after all, that's // +// all a menu really is anyway). // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGameGUIMgr.h" +#include "pfGUIPopUpMenu.h" +#include "pfGUIMenuItem.h" +#include "pfGUIButtonMod.h" +#include "pfGUIDialogHandlers.h" +#include "pfGUIDialogNotifyProc.h" +#include "pfGUIControlHandlers.h" +#include "pfGUICtrlGenerator.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayer.h" +#include "../plGImage/plDynamicTextMap.h" +#include "../plMessage/plLayRefMsg.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" + +#include "../plScene/plPostEffectMod.h" +#include "../plScene/plSceneNode.h" +#include "../pnMessage/plClientMsg.h" + +#include "plViewTransform.h" +#include "../plPipeline/plDebugText.h" + + +class pfPopUpKeyGenerator +{ + public: + char fPrefix[ 128 ]; + UInt32 fKeyCount; + plLocation fLoc; + + pfPopUpKeyGenerator( const char *p, const plLocation &loc ) + { + strcpy( fPrefix, p ); + fLoc = loc; + } + + plKey CreateKey( hsKeyedObject *ko ) + { + char name[ 256 ]; + sprintf( name, "%s-%d", fPrefix, fKeyCount++ ); + + return hsgResMgr::ResMgr()->NewKey( name, ko, fLoc ); + } +}; + +//// Router Proc So The Parent Can Handle Click Events /////////////////////// + +class pfGUIMenuItemProc : public pfGUICtrlProcObject +{ + protected: + + pfGUIPopUpMenu *fParent; + UInt32 fIndex; + + public: + + pfGUIMenuItemProc( pfGUIPopUpMenu *parent, UInt32 idx ) + { + fParent = parent; + fIndex = idx; + } + + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + fParent->IHandleMenuSomething( fIndex, ctrl ); + } + + virtual void HandleExtendedEvent( pfGUIControlMod *ctrl, UInt32 event ) + { + fParent->IHandleMenuSomething( fIndex, ctrl, (Int32)event ); + } +}; + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIPopUpMenu::pfGUIPopUpMenu() +{ + fNeedsRebuilding = false; + fParent = nil; + fKeyGen = nil; + fSubMenuOpen = -1; + SetFlag( kModalOutsideMenus ); + fMargin = 4; + fSkin = nil; + fWaitingForSkin = false; + + fParentNode = nil; + fOriginX = fOriginY = 0.f; + fOriginAnchor = nil; + fOriginContext = nil; + + fAlignment = kAlignDownRight; +} + +pfGUIPopUpMenu::~pfGUIPopUpMenu() +{ + SetSkin( nil ); + +// if( fParentNode != nil ) +// fParentNode->GetKey()->UnRefObject(); + + ITearDownMenu(); + ClearItems(); + + delete fKeyGen; +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIPopUpMenu::MsgReceive( plMessage *msg ) +{ + plGenRefMsg *ref = plGenRefMsg::ConvertNoRef( msg ); + if( ref != nil ) + { + if( ref->fType == kRefSkin ) + { + if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fSkin = pfGUISkin::ConvertNoRef( ref->GetRef() ); + fWaitingForSkin = false; + } + else + fSkin = nil; + + fNeedsRebuilding = true; + if( IsVisible() ) + { + // Rebuild NOW + ITearDownMenu(); + IBuildMenu(); + } + return true; + } + else if( ref->fType == kRefSubMenu ) + { + if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fMenuItems[ ref->fWhich ].fSubMenu = pfGUIPopUpMenu::ConvertNoRef( ref->GetRef() ); + else + fMenuItems[ ref->fWhich ].fSubMenu = nil; + return true; + } + else if( ref->fType == kRefOriginAnchor ) + { + if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fOriginAnchor = plSceneObject::ConvertNoRef( ref->GetRef() ); + else + fOriginAnchor = nil; + return true; + } + else if( ref->fType == kRefOriginContext ) + { + if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fOriginContext = pfGUIDialogMod::ConvertNoRef( ref->GetRef() ); + else + fOriginContext = nil; + return true; + } + else if( ref->fType == kRefParentNode ) + { + if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fParentNode = plSceneNode::ConvertNoRef( ref->GetRef() ); + else + fParentNode = nil; + return true; + } + } + + return pfGUIDialogMod::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIPopUpMenu::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIDialogMod::Read( s, mgr ); + + // In case we need it... + fKeyGen = TRACKED_NEW pfPopUpKeyGenerator( GetName(), GetKey()->GetUoid().GetLocation() ); + + fOriginX = fOriginY = -1.f; + + fMargin = s->ReadSwap16(); + + UInt32 i, count = s->ReadSwap32(); + fMenuItems.SetCountAndZero( count ); + for( i = 0; i < count; i++ ) + { + char readTemp[ 256 ]; + s->Read( sizeof( readTemp ), readTemp ); + wchar_t *wReadTemp = hsStringToWString( readTemp ); + fMenuItems[ i ].fName = wReadTemp; + delete [] wReadTemp; + + fMenuItems[ i ].fHandler = pfGUICtrlProcWriteableObject::Read( s ); + + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kRefSubMenu ), plRefFlags::kActiveRef ); + } + + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefSkin ), plRefFlags::kActiveRef ); + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefOriginAnchor ), plRefFlags::kPassiveRef ); + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefOriginContext ), plRefFlags::kPassiveRef ); + + fAlignment = (Alignment)s->ReadByte(); + + fNeedsRebuilding = true; +} + +void pfGUIPopUpMenu::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIDialogMod::Write( s, mgr ); + + + s->WriteSwap16( fMargin ); + + s->WriteSwap32( fMenuItems.GetCount() ); + UInt32 i; + for( i = 0; i < fMenuItems.GetCount(); i++ ) + { + char writeTemp[ 256 ]; + char *sName = hsWStringToString( fMenuItems[ i ].fName.c_str() ); + strncpy( writeTemp, sName, sizeof( writeTemp ) ); + delete [] sName; + s->Write( sizeof( writeTemp ), writeTemp ); + + // Write the handler out (if it's not a writeable, damn you) + pfGUICtrlProcWriteableObject::Write( (pfGUICtrlProcWriteableObject *)fMenuItems[ i ].fHandler, s ); + + mgr->WriteKey( s, fMenuItems[ i ].fSubMenu ); + } + + // Note: we force parentNode to nil here because we only use it when we dynamically + // create nodes at runtime and need to unref and destroy them later. Since we're + // reading from disk, we'll already have a sceneNode somewhere, so we don't need + // this. + fParentNode = nil; + + mgr->WriteKey( s, fSkin ); + mgr->WriteKey( s, fOriginAnchor ); + mgr->WriteKey( s, fOriginContext ); + + s->WriteByte( (UInt8)fAlignment ); +} + +void pfGUIPopUpMenu::SetOriginAnchor( plSceneObject *anchor, pfGUIDialogMod *context ) +{ + fOriginAnchor = anchor; + fOriginContext = context; + hsgResMgr::ResMgr()->AddViaNotify( fOriginAnchor->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefOriginAnchor ), plRefFlags::kPassiveRef ); + hsgResMgr::ResMgr()->AddViaNotify( fOriginContext->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefOriginContext ), plRefFlags::kPassiveRef ); +} + +//// SetEnabled ////////////////////////////////////////////////////////////// + +void pfGUIPopUpMenu::SetEnabled( hsBool e ) +{ + if( e && fNeedsRebuilding ) + { + // Make sure our menu is rebuilt before enabling + ITearDownMenu(); + IBuildMenu(); + } + else if( !e ) + { + if( fParent != nil ) + pfGUIPopUpMenu::ConvertNoRef( fParent )->fSubMenuOpen = -1; + + // Hide our submenus if we have any open + if( fSubMenuOpen != -1 ) + { + fMenuItems[ fSubMenuOpen ].fSubMenu->Hide(); + fSubMenuOpen = -1; + } + } + + pfGUIDialogMod::SetEnabled( e ); +} + +void pfGUIPopUpMenu::Show( hsScalar x, hsScalar y ) +{ + fOriginX = x; + fOriginY = y; + pfGUIDialogMod::Show(); // C++ is kinda stupid if it can't find this naturally + ISeekToOrigin(); +} + +void pfGUIPopUpMenu::ISeekToOrigin( void ) +{ +#if 0 + UInt32 i; + float x = 0.5f/*fOriginX*/, y = fOriginY; + + for( i = 0; i < fControls.GetCount(); i++ ) + { + if( fControls[ i ] != nil ) + { + fControls[ i ]->SetObjectCenter( x, y ); + +// const hsBounds3 &bnds = fControls[ i ]->GetBounds(); + y += fMenuItems[ i ].fYOffsetToNext;//bnds.GetMaxs().fY - bnds.GetMins().fY; + +/* hsMatrix44 p2l, l2p = GetTarget()->GetLocalToWorld(); + + hsPoint3 center, origin; + ScreenToWorldPoint( fOriginX, fOriginY, 100.f, center ); + ScreenToWorldPoint( 0.f, 0.f, 100.f, origin ); + + center = origin - center; + + center.fZ = 0.f; + l2p.SetTranslate( ¢er ); + l2p.GetInverse( &p2l ); + + GetTarget()->SetTransform( l2p, p2l ); +*/ } + } +#endif +} + +//// IHandleMenuSomething //////////////////////////////////////////////////// +// Handles a normal event from one of the item controls. + +void pfGUIPopUpMenu::IHandleMenuSomething( UInt32 idx, pfGUIControlMod *ctrl, Int32 extended ) +{ + if( extended != -1 ) + { + if( fSubMenuOpen != -1 && fSubMenuOpen != idx ) + { + // Better close the submenu(s) + fMenuItems[ fSubMenuOpen ].fSubMenu->Hide(); + fSubMenuOpen = -1; + } + + if( extended == pfGUIMenuItem::kMouseHover && fMenuItems[ idx ].fSubMenu != nil ) + { + // Open new submenu + const hsBounds3 &bnds = ctrl->GetBounds(); + fMenuItems[ idx ].fSubMenu->Show( bnds.GetMaxs().fX, bnds.GetMins().fY ); + fSubMenuOpen = idx; + } + } + else + { + if( fMenuItems[ idx ].fHandler != nil ) + fMenuItems[ idx ].fHandler->DoSomething( ctrl ); + + // If item isn't a sub-menu, close this menu. Else add to list of menus to close + // once the smallest submenu goes away + if( fMenuItems[ idx ].fSubMenu == nil ) + { + // Basically, we want to hide ourselves and as many up in the chain of command as + // can be hidden + pfGUIPopUpMenu *menu = this; + while( menu != nil && !menu->HasFlag( kStayOpenAfterClick ) ) + { + menu->Hide(); + menu = pfGUIPopUpMenu::ConvertNoRef( menu->fParent ); + } + } + else + { + // Show relative to the corner of our current item + const hsBounds3 &bnds = ctrl->GetBounds(); + fMenuItems[ idx ].fSubMenu->Show( bnds.GetMaxs().fX, bnds.GetMins().fY ); + fSubMenuOpen = idx; + } + } +} + +//// IBuildMenu ////////////////////////////////////////////////////////////// +// Given the list of menu items, builds our set of dynamic buttons + +hsBool pfGUIPopUpMenu::IBuildMenu( void ) +{ + int i; + + + if( fWaitingForSkin && fSkin == nil ) + return false; // Still waiting to get our skin before building + + pfGUIColorScheme *scheme = TRACKED_NEW pfGUIColorScheme(); + scheme->fForeColor.Set( 0, 0, 0, 1 ); + scheme->fBackColor.Set( 1, 1, 1, 1 ); + + // If we don't have origin points, get them from translating our anchor + if( fOriginX == -1 || fOriginY == -1 && fOriginAnchor != nil ) + { + hsPoint3 scrnPt; + const plDrawInterface *di = fOriginAnchor->GetDrawInterface(); + if( di != nil ) + { + scrnPt = di->GetLocalBounds().GetCenter(); + scrnPt = fOriginAnchor->GetLocalToWorld() * scrnPt; + } + else + scrnPt = fOriginAnchor->GetLocalToWorld().GetTranslate(); + if( fOriginContext != nil ) + scrnPt = fOriginContext->WorldToScreenPoint( scrnPt ); + else + scrnPt = WorldToScreenPoint( scrnPt ); + + if( fOriginX == -1 ) + fOriginX = scrnPt.fX; + if( fOriginY == -1 ) + fOriginY = scrnPt.fY; + } + + float x = fOriginX, y = fOriginY; + float width = 0.f, height = 0.f; + float topMargin = ( fSkin != nil ) ? fSkin->GetBorderMargin() : 0.f; + + // First step: loop through and calculate the size of our menu + // The PROBLEM is that we can't do that unless we have a friggin surface on + // which to calculate the text extents! So sadly, we're going to have to create + // a whole new DTMap and use it to calculate some stuff + plDynamicTextMap *scratch = TRACKED_NEW plDynamicTextMap( 8, 8, false ); + scratch->SetFont( scheme->fFontFace, scheme->fFontSize, scheme->fFontFlags, true ); + for( i = 0; i < fMenuItems.GetCount(); i++ ) + { + UInt16 thisW, thisH; + thisW = scratch->CalcStringWidth( fMenuItems[ i ].fName.c_str(), &thisH ); + if( fMenuItems[ i ].fSubMenu != nil ) + { + if( fSkin != nil ) + thisW += 4 + ( fSkin->GetElement( pfGUISkin::kSubMenuArrow ).fWidth << 1 ); + else + thisW += scratch->CalcStringWidth( " >>", nil ); + } + thisH += 2; // Give us at least one pixel on each side + + int margin = fMargin; + if( fSkin != nil ) + margin = fSkin->GetBorderMargin() << 1; + + if( width < thisW + margin ) + width = (float)(thisW + margin); + + if( fSkin != nil ) + margin = fSkin->GetItemMargin() << 1; + + if( height < thisH + margin ) + height = (float)(thisH + margin); + } + delete scratch; + + width += 4; // give us a little space, just in case + + UInt32 scrnWidth, scrnHeight; + // A cheat here, I know, but I'm lazy + plDebugText::Instance().GetScreenSize( &scrnWidth, &scrnHeight ); + + // Use the same base res calc that dtMaps use + if( !HasFlag( kScaleWithResolution ) ) + { + // Just use what we were passed in + } + else + { + // Scale with the resolution so that we take up the same % of screen space no matter what resolution + // Assume a base "resolution" of 1024xX, where X is such that the ratio "1024/X = scrnWidth/scrnHt" holds + const int kBaseScaleRes = 1024; + scrnHeight = ( scrnHeight * kBaseScaleRes ) / scrnWidth; + scrnWidth = kBaseScaleRes; + } + + width /= (float)scrnWidth; + height /= (float)scrnHeight; + topMargin /= (float)scrnHeight; + + switch( fAlignment ) + { + case kAlignUpLeft: x -= width; y -= height * fMenuItems.GetCount(); break; + case kAlignUpRight: y -= height * fMenuItems.GetCount(); break; + case kAlignDownLeft: x -= width; break; + case kAlignDownRight: break; + } + + if( y + height * fMenuItems.GetCount() > 1.f ) + { + // Make sure we don't go off the bottom + y = 1.f - height * fMenuItems.GetCount(); + } + // And the top (takes precedence) + if( y < 0.f ) + y = 0.f; + + // Control positions are in the lower left corner, so increment Y by 1 control height first + y += height;// + topMargin; + + hsTArray buildList; + + for( i = 0; i < fMenuItems.GetCount(); i++ ) + { + hsGMaterial *mat = ICreateDynMaterial(); + + float thisMargin = ( i == 0 || i == fMenuItems.GetCount() - 1 ) ? topMargin : 0.f; + float thisOffset = ( i == fMenuItems.GetCount() - 1 ) ? topMargin : 0.f; + + pfGUIMenuItem *button = pfGUIMenuItem::ConvertNoRef( pfGUICtrlGenerator::Instance().CreateRectButton( this, fMenuItems[ i ].fName.c_str(), x, y + thisOffset, width, height + thisMargin, mat, true ) ); + if( button != nil ) + { + button->SetColorScheme( scheme ); + button->SetName( fMenuItems[ i ].fName.c_str() ); + button->SetHandler( TRACKED_NEW pfGUIMenuItemProc( this, i ) ); + // make the tag ID the position in the menu list + button->SetTagID(i); + button->SetDynTextMap( mat->GetLayer( 0 ), plDynamicTextMap::ConvertNoRef( mat->GetLayer( 0 )->GetTexture() ) ); + button->SetFlag( pfGUIMenuItem::kReportHovers ); + button->SetSkin( fSkin, ( i == 0 ) ? pfGUIMenuItem::kTop : ( i == fMenuItems.GetCount() - 1 ) ? pfGUIMenuItem::kBottom : pfGUIMenuItem::kMiddle ); + if( fMenuItems[ i ].fSubMenu != nil ) + { + button->SetFlag( pfGUIMenuItem::kDrawSubMenuArrow ); + buildList.Append( pfGUIPopUpMenu::ConvertNoRef( fMenuItems[ i ].fSubMenu ) ); + } + } + + // Tiny bit of overlap to prevent gaps + fMenuItems[ i ].fYOffsetToNext = height + thisOffset; + y += height + thisOffset;// - ( 1.f / kBaseScaleResY ); + } + + fNeedsRebuilding = false; + +#if 0 + // Finally, go down our list of submenus and rebuild them, since they'll need to be rebuilt soon anyway, + // and at least this way it's all in one pass + + // Also, we need to bump the tag ID used, such as adding parent menuItem TagID * 100.. or something + + // Disabled because right now we can't move menus, which is required for this to work + for( i = 0; i < buildList.GetCount(); i++ ) + buildList[ i ]->IBuildMenu(); +#endif + + return true; +} + +//// ITearDownMenu /////////////////////////////////////////////////////////// +// Destroys all of our dynamic controls representing the menu + +void pfGUIPopUpMenu::ITearDownMenu( void ) +{ + int i; + + + for( i = 0; i < fControls.GetCount(); i++ ) + { + if( fControls[ i ] != nil ) + { + // It's not enough to release the key, we have to have the sceneNode release the key, too. + // Easy enough to do by just setting it's sn to nil + if( fControls[ i ]->GetTarget() != nil ) + fControls[ i ]->GetTarget()->SetSceneNode( nil ); + + // Now release it from us + GetKey()->Release( fControls[ i ]->GetKey() ); + } + } + + fNeedsRebuilding = true; +} + +//// HandleMouseEvent //////////////////////////////////////////////////////// + +hsBool pfGUIPopUpMenu::HandleMouseEvent( pfGameGUIMgr::EventType event, hsScalar mouseX, hsScalar mouseY, + UInt8 modifiers ) +{ + hsBool r = pfGUIDialogMod::HandleMouseEvent( event, mouseX, mouseY, modifiers ); + if( r == false && event == pfGameGUIMgr::kMouseUp ) + { + // We don't want to be active anymore! + if( !HasFlag( kStayOpenAfterClick ) ) + { + Hide(); + + // Now we pass the click to our parent. Why? Because it's possible that someone above us + // will either a) also want to hide (cancel the entire menu selection) or b) select + // another option + if( fParent != nil ) + return fParent->HandleMouseEvent( event, mouseX, mouseY, modifiers ); + } + } + + return ( fParent != nil ) ? r : ( HasFlag( kModalOutsideMenus ) || ( fSubMenuOpen != -1 ) ); +} + +//// ClearItems ////////////////////////////////////////////////////////////// +// Clears the list of template items + +void pfGUIPopUpMenu::ClearItems( void ) +{ + int i; + + + for( i = 0; i < fMenuItems.GetCount(); i++ ) + { + if( fMenuItems[ i ].fHandler != nil ) + { + if( fMenuItems[ i ].fHandler->DecRef() ) + delete fMenuItems[ i ].fHandler; + } + } + + fMenuItems.Reset(); + + fNeedsRebuilding = true; +} + +//// AddItem ///////////////////////////////////////////////////////////////// +// Append a new item to the list of things to build the menu from + +void pfGUIPopUpMenu::AddItem( const char *name, pfGUICtrlProcObject *handler, pfGUIPopUpMenu *subMenu ) +{ + wchar_t *wName = hsStringToWString(name); + AddItem(wName,handler,subMenu); + delete [] wName; +} + +void pfGUIPopUpMenu::AddItem( const wchar_t *name, pfGUICtrlProcObject *handler, pfGUIPopUpMenu *subMenu ) +{ + pfMenuItem newItem; + + + newItem.fName = name; + newItem.fHandler = handler; + if( newItem.fHandler != nil ) + newItem.fHandler->IncRef(); + newItem.fSubMenu = subMenu; + + if( subMenu != nil ) + subMenu->fParent = this; + + fMenuItems.Append( newItem ); + + fNeedsRebuilding = true; +} + +//// ICreateDynMaterial ////////////////////////////////////////////////////// +// Creates the hsGMaterial tree for a single layer with a plDynamicTextMap. + +hsGMaterial *pfGUIPopUpMenu::ICreateDynMaterial( void ) +{ + hsColorRGBA black, white; + + + // Create the new dynTextMap + plDynamicTextMap *textMap = TRACKED_NEW plDynamicTextMap(); + fKeyGen->CreateKey( textMap ); + + // Create the material + hsGMaterial *material = TRACKED_NEW hsGMaterial; + fKeyGen->CreateKey( material ); + + // Create the layer and attach + plLayer *lay = material->MakeBaseLayer(); + white.Set( 1.f,1.f,1.f,1.f ); + black.Set( 0.f,0.f,0.f,1.f ); + + lay->SetRuntimeColor( black ); + lay->SetPreshadeColor( black ); + lay->SetAmbientColor( white ); + lay->SetClampFlags( hsGMatState::kClampTexture ); + + // Do sendRef here, since we're going to need it set pretty darned quick + hsgResMgr::ResMgr()->SendRef( textMap->GetKey(), TRACKED_NEW plLayRefMsg( lay->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture ), plRefFlags::kActiveRef ); + + return material; + +} + +//// Build /////////////////////////////////////////////////////////////////// +// Constructs a shiny new pop-up menu at runtime, complete with trimmings + +#include "../plJPEG/plJPEG.h" + +pfGUIPopUpMenu *pfGUIPopUpMenu::Build( const char *name, pfGUIDialogMod *parent, hsScalar x, hsScalar y, const plLocation &destLoc ) +{ + float fovX, fovY; + + + // Create the menu and give it a key gen + pfGUIPopUpMenu *menu = TRACKED_NEW pfGUIPopUpMenu(); + menu->fKeyGen = TRACKED_NEW pfPopUpKeyGenerator( name, destLoc ); + menu->fKeyGen->CreateKey( menu ); + + menu->fOriginX = x; + menu->fOriginY = y; + + // By default, share the same skin as the parent + if( parent != nil && ( (pfGUIPopUpMenu *)parent )->fSkin != nil ) + { + menu->fWaitingForSkin = true; + hsgResMgr::ResMgr()->SendRef( ( (pfGUIPopUpMenu *)parent )->fSkin->GetKey(), TRACKED_NEW plGenRefMsg( menu->GetKey(), plRefMsg::kOnCreate, -1, pfGUIPopUpMenu::kRefSkin ), plRefFlags::kActiveRef ); + } + + // HACK for now: create us a temp skin to use +/* static pfGUISkin *skin = nil; + if( skin == nil ) + { + plLocation loc; + loc.Set( 0x1425 ); + plKey skinKey = hsgResMgr::ResMgr()->FindKey( plUoid( loc, pfGUISkin::Index(), "GUISkin01_GUISkin" ) ); + menu->fWaitingForSkin = true; + hsgResMgr::ResMgr()->AddViaNotify( skinKey, TRACKED_NEW plGenRefMsg( menu->GetKey(), plRefMsg::kOnCreate, -1, pfGUIPopUpMenu::kRefSkin ), plRefFlags::kActiveRef ); + } +*/ + + // Create the rendermod + plPostEffectMod *renderMod = TRACKED_NEW plPostEffectMod; + menu->fKeyGen->CreateKey( renderMod ); + + renderMod->SetHither( 0.5f ); + renderMod->SetYon( 200.f ); + + float scrnWidth = 20.f; + + // fovX should be such that scrnWidth is the projected width at z=100 + fovX = atan( scrnWidth / ( 2.f * 100.f ) ) * 2.f; + fovY = fovX;// * 3.f / 4.f; + + renderMod->SetFovX( fovX * 180.f / hsScalarPI ); + renderMod->SetFovY( fovY * 180.f / hsScalarPI ); + + // Create the sceneNode to go with it + menu->fParentNode= TRACKED_NEW plSceneNode; + menu->fKeyGen->CreateKey( menu->fParentNode ); +// menu->fParentNode->GetKey()->RefObject(); + hsgResMgr::ResMgr()->SendRef( menu->fParentNode->GetKey(), TRACKED_NEW plGenRefMsg( menu->GetKey(), plRefMsg::kOnCreate, 0, kRefParentNode ), plRefFlags::kActiveRef ); + + hsgResMgr::ResMgr()->AddViaNotify( menu->fParentNode->GetKey(), TRACKED_NEW plGenRefMsg( renderMod->GetKey(), plRefMsg::kOnCreate, 0, plPostEffectMod::kNodeRef ), plRefFlags::kPassiveRef ); + + menu->SetRenderMod( renderMod ); + menu->SetName( name ); + + // Create the dummy scene object to hold the menu + plSceneObject *newObj = TRACKED_NEW plSceneObject; + menu->fKeyGen->CreateKey( newObj ); + + // *#&$(*@&#$ need a coordIface... + plCoordinateInterface *newCI = TRACKED_NEW plCoordinateInterface; + menu->fKeyGen->CreateKey( newCI ); + + hsMatrix44 l2w, w2l; + l2w.Reset(); + l2w.GetInverse( &w2l ); + + // Using SendRef here because AddViaNotify will queue the messages up, which doesn't do us any good + // if we need these refs right away + hsgResMgr::ResMgr()->SendRef( newCI->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface ), plRefFlags::kActiveRef ); + hsgResMgr::ResMgr()->SendRef( renderMod->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); + newObj->SetSceneNode( menu->fParentNode->GetKey() ); + newObj->SetTransform( l2w, w2l ); + + hsgResMgr::ResMgr()->SendRef( menu->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); + + // Add the menu to the GUI mgr + plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( pfGameGUIMgr::GetInstance()->GetKey(), + plRefMsg::kOnCreate, 0, pfGameGUIMgr::kDlgModRef ); + hsgResMgr::ResMgr()->AddViaNotify( menu->GetKey(), refMsg, plRefFlags::kActiveRef ); + + menu->ISeekToOrigin(); + + return menu; +} + +//// SetSkin ///////////////////////////////////////////////////////////////// + +void pfGUIPopUpMenu::SetSkin( pfGUISkin *skin ) +{ + // Just a function wrapper for SendRef + if( fSkin != nil ) + GetKey()->Release( fSkin->GetKey() ); + + if( skin != nil ) + { + hsgResMgr::ResMgr()->SendRef( skin->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefSkin ), plRefFlags::kActiveRef ); + fWaitingForSkin = true; + } + else + fWaitingForSkin = false; +} + + +////////////////////////////////////////////////////////////////////////////// +//// pfGUISkin Implementation //////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +pfGUISkin::pfGUISkin() +{ + fTexture = nil; + memset( fElements, 0, sizeof( pfSRect ) * kNumElements ); +} + +pfGUISkin::pfGUISkin( plMipmap *texture ) +{ + fTexture = texture; + if( fTexture != nil ) + { + hsAssert( fTexture->GetKey() != nil, "Creating a GUI skin via a mipmap with no key!" ); + fTexture->GetKey()->RefObject(); + } + memset( fElements, 0, sizeof( pfSRect ) * kNumElements ); +} + +pfGUISkin::~pfGUISkin() +{ + SetTexture( nil ); +} + +void pfGUISkin::SetTexture( plMipmap *tex ) +{ + if( fTexture != nil && fTexture->GetKey() != nil ) + fTexture->GetKey()->UnRefObject(); + + fTexture = tex; + if( fTexture != nil ) + { + hsAssert( fTexture->GetKey() != nil, "Creating a GUI skin via a mipmap with no key!" ); + fTexture->GetKey()->RefObject(); + } +} + +void pfGUISkin::SetElement( UInt32 idx, UInt16 x, UInt16 y, UInt16 w, UInt16 h ) +{ + fElements[ idx ].fX = x; + fElements[ idx ].fY = y; + fElements[ idx ].fWidth = w; + fElements[ idx ].fHeight = h; +} + +void pfGUISkin::Read( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Read( s, mgr ); + + s->ReadSwap( &fItemMargin ); + s->ReadSwap( &fBorderMargin ); + + UInt32 i, count; + s->ReadSwap( &count ); + + for( i = 0; i < count; i++ ) + fElements[ i ].Read( s ); + + for( ; i < kNumElements; i++ ) + fElements[ i ].Empty(); + + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefMipmap ), plRefFlags::kActiveRef ); +} + +void pfGUISkin::Write( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Write( s, mgr ); + + s->WriteSwap( fItemMargin ); + s->WriteSwap( fBorderMargin ); + + UInt32 i = kNumElements; + s->WriteSwap( i ); + + for( i = 0; i < kNumElements; i++ ) + fElements[ i ].Write( s ); + + mgr->WriteKey( s, fTexture ); +} + +hsBool pfGUISkin::MsgReceive( plMessage *msg ) +{ + plGenRefMsg *ref = plGenRefMsg::ConvertNoRef( msg ); + if( ref != nil ) + { + if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fTexture = plMipmap::ConvertNoRef( ref->GetRef() ); + else + fTexture = nil; + + return true; + } + + return hsKeyedObject::MsgReceive( msg ); +} + +void pfGUISkin::pfSRect::Read( hsStream *s ) +{ + s->ReadSwap( &fX ); + s->ReadSwap( &fY ); + s->ReadSwap( &fWidth ); + s->ReadSwap( &fHeight ); +} + +void pfGUISkin::pfSRect::Write( hsStream *s ) +{ + s->WriteSwap( fX ); + s->WriteSwap( fY ); + s->WriteSwap( fWidth ); + s->WriteSwap( fHeight ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIPopUpMenu.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIPopUpMenu.h new file mode 100644 index 00000000..15d76760 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIPopUpMenu.h @@ -0,0 +1,225 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIPopUpMenu Header // +// // +// Pop-up menus are really just dialogs that know how to create themselves // +// and create buttons on themselves to simulate a menu (after all, that's // +// all a menu really is anyway). // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIPopUpMenu_h +#define _pfGUIPopUpMenu_h + + +#include "pfGUIDialogMod.h" +#include "hsBounds.h" + +class plMessage; +class pfGUIButtonMod; +class pfPopUpKeyGenerator; +class pfGUICtrlProcObject; +class hsGMaterial; +class plSceneNode; +class pfGUIMenuItemProc; +class pfGUISkin; + +class pfGUIPopUpMenu : public pfGUIDialogMod +{ + public: + + enum Alignment + { + kAlignUpLeft, + kAlignUpRight, + kAlignDownLeft, + kAlignDownRight // Default + }; + + protected: + + friend class pfGUIMenuItemProc; + + pfGUIDialogMod *fParent; // Pop-up menus also have a sense of who owns them + plSceneNode *fParentNode; + + pfPopUpKeyGenerator *fKeyGen; // Generates keys for our dynamic objects + + class pfMenuItem + { + // Simple wrapper class that tells us how to build our menu + public: + std::wstring fName; + pfGUICtrlProcObject *fHandler; + pfGUIPopUpMenu *fSubMenu; + float fYOffsetToNext; // Filled in by IBuildMenu() + + pfMenuItem& operator=(const int zero) { fName = L""; fHandler = nil; fSubMenu = nil; fYOffsetToNext = 0; return *this; } + }; + + // Array of info to rebuild our menu from. Note that this is ONLY used when rebuilding + hsBool fNeedsRebuilding, fWaitingForSkin; + hsScalar fOriginX, fOriginY; + UInt16 fMargin; + hsTArray fMenuItems; + Int32 fSubMenuOpen; + + pfGUISkin *fSkin; + + plSceneObject *fOriginAnchor; + pfGUIDialogMod *fOriginContext; + + Alignment fAlignment; + + + hsBool IBuildMenu( void ); + void ITearDownMenu( void ); + + hsGMaterial *ICreateDynMaterial( void ); + + void IHandleMenuSomething( UInt32 idx, pfGUIControlMod *ctrl, Int32 extended = -1 ); + + void ISeekToOrigin( void ); + + public: + + pfGUIPopUpMenu(); + virtual ~pfGUIPopUpMenu(); + + CLASSNAME_REGISTER( pfGUIPopUpMenu ); + GETINTERFACE_ANY( pfGUIPopUpMenu, pfGUIDialogMod ); + + enum MenuFlags + { + kStayOpenAfterClick = kDerivedFlagsStart, + kModalOutsideMenus, + kOpenSubMenusOnHover, + kScaleWithResolution + }; + + enum Refs + { + kRefSkin = kRefDerviedStart, + kRefSubMenu, + kRefOriginAnchor, + kRefOriginContext, + kRefParentNode + }; + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void SetEnabled( hsBool e ); + virtual hsBool HandleMouseEvent( pfGameGUIMgr::EventType event, hsScalar mouseX, hsScalar mouseY, UInt8 modifiers ); + + void Show( hsScalar x, hsScalar y ); + + void SetOriginAnchor( plSceneObject *anchor, pfGUIDialogMod *context ); + void SetAlignment( Alignment a ) { fAlignment = a; } + void ClearItems( void ); + void AddItem( const char *name, pfGUICtrlProcObject *handler, pfGUIPopUpMenu *subMenu = nil ); + void AddItem( const wchar_t *name, pfGUICtrlProcObject *handler, pfGUIPopUpMenu *subMenu = nil ); + void SetSkin( pfGUISkin *skin ); + + static pfGUIPopUpMenu *Build( const char *name, pfGUIDialogMod *parent, hsScalar x, hsScalar y, const plLocation &destLoc = plLocation::kGlobalFixedLoc ); + +}; + +// Skin definition. Here for now 'cause only the menus use it, but might move it later +class plMipmap; +class pfGUISkin : public hsKeyedObject +{ + public: + enum Elements + { + kUpLeftCorner = 0, + kTopSpan, + kUpRightCorner, + kRightSpan, + kLowerRightCorner, + kBottomSpan, + kLowerLeftCorner, + kLeftSpan, + kMiddleFill, + kSelectedFill, + kSubMenuArrow, + kSelectedSubMenuArrow, + kTreeButtonClosed, + kTreeButtonOpen, + kNumElements + }; + + class pfSRect + { + public: + UInt16 fX, fY, fWidth, fHeight; + + void Empty( void ) { fX = fY = fWidth = fHeight = 0; } + void Read( hsStream *s ); + void Write( hsStream *s ); + }; + + protected: + + plMipmap *fTexture; + pfSRect fElements[ kNumElements ]; + UInt16 fItemMargin, fBorderMargin; + + public: + + pfGUISkin(); + pfGUISkin( plMipmap *texture ); + virtual ~pfGUISkin(); + + CLASSNAME_REGISTER( pfGUISkin ); + GETINTERFACE_ANY( pfGUISkin, hsKeyedObject ); + + enum Refs + { + kRefMipmap + }; + + virtual void Read( hsStream *s, hsResMgr *mgr ); + virtual void Write( hsStream *s, hsResMgr *mgr ); + virtual hsBool MsgReceive( plMessage *msg ); + + plMipmap *GetTexture( void ) const { return fTexture; } + void SetTexture( plMipmap *tex ); + + const pfSRect &GetElement( UInt32 idx ) const { return fElements[ idx ]; } + hsBool IsElementSet( UInt32 idx ) const { return ( fElements[ idx ].fWidth > 0 && fElements[ idx ].fHeight > 0 ); } + void SetElement( UInt32 idx, UInt16 x, UInt16 y, UInt16 w, UInt16 h ); + + void SetMargins( UInt16 item, UInt16 border ) { fItemMargin = item; fBorderMargin = border; } + UInt16 GetItemMargin( void ) const { return fItemMargin; } + UInt16 GetBorderMargin( void ) const { return fBorderMargin; } +}; + +#endif // _pfGUIPopUpMenu_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIProgressCtrl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIProgressCtrl.cpp new file mode 100644 index 00000000..e2f470b7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIProgressCtrl.cpp @@ -0,0 +1,255 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIProgressCtrl Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIProgressCtrl.h" +#include "pfGameGUIMgr.h" +#include "pfGUIDialogMod.h" + +#include "../plInputCore/plInputInterface.h" +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plTimerCallbackMsg.h" +// #include "../plAvatar/plAGModifier.h" +#include "../plAvatar/plAGMasterMod.h" +#include "../plAvatar/plAGAnimInstance.h" +#include "../plSurface/plLayerAnimation.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnTimer/plTimerCallbackManager.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIProgressCtrl::pfGUIProgressCtrl() : fStopSoundTimer(99) +{ + fAnimTimesCalced = false; + fAnimName = nil; + fPlaySound = true; +} + +pfGUIProgressCtrl::~pfGUIProgressCtrl() +{ + delete [] fAnimName; +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIProgressCtrl::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIValueCtrl::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIProgressCtrl::MsgReceive( plMessage *msg ) +{ + plTimerCallbackMsg *timerMsg = plTimerCallbackMsg::ConvertNoRef(msg); + if (timerMsg) + { + if (timerMsg->fID == fStopSoundTimer) + { + // we've finished animating, stop the sound that's playing + StopSound(kAnimateSound); + } + } + return pfGUIValueCtrl::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIProgressCtrl::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIValueCtrl::Read(s, mgr); + + fAnimationKeys.Reset(); + UInt32 i, count = s->ReadSwap32(); + for( i = 0; i < count; i++ ) + fAnimationKeys.Append( mgr->ReadKey( s ) ); + fAnimName = s->ReadSafeString(); + + fAnimTimesCalced = false; +} + +void pfGUIProgressCtrl::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIValueCtrl::Write( s, mgr ); + + UInt32 i, count = fAnimationKeys.GetCount(); + s->WriteSwap32( count ); + for( i = 0; i < count; i++ ) + mgr->WriteKey( s, fAnimationKeys[ i ] ); + s->WriteSafeString( fAnimName ); +} + +//// UpdateBounds //////////////////////////////////////////////////////////// + +void pfGUIProgressCtrl::UpdateBounds( hsMatrix44 *invXformMatrix, hsBool force ) +{ + pfGUIValueCtrl::UpdateBounds( invXformMatrix, force ); + if( fAnimationKeys.GetCount() > 0 ) + fBoundsValid = false; +} + +//// SetAnimationKeys //////////////////////////////////////////////////////// + +void pfGUIProgressCtrl::SetAnimationKeys( hsTArray &keys, const char *name ) +{ + fAnimationKeys = keys; + delete [] fAnimName; + if( name != nil ) + { + fAnimName = TRACKED_NEW char[ strlen( name ) + 1 ]; + strcpy( fAnimName, name ); + } + else + fAnimName = nil; +} + +//// ICalcAnimTimes ////////////////////////////////////////////////////////// +// Loops through and computes the max begin and end for our animations. If +// none of them are loaded and we're not already calced, returns false. + +hsBool pfGUIProgressCtrl::ICalcAnimTimes( void ) +{ + if( fAnimTimesCalced ) + return true; + + hsScalar tBegin = 1e30, tEnd = -1e30; + bool foundOne = false; + + for( int i = 0; i < fAnimationKeys.GetCount(); i++ ) + { + // Handle AGMasterMods + plAGMasterMod *mod = plAGMasterMod::ConvertNoRef( fAnimationKeys[ i ]->ObjectIsLoaded() ); + if( mod != nil ) + { + for( int j = 0; j < mod->GetNumAnimations(); j++ ) + { + hsScalar begin = mod->GetAnimInstance( j )->GetTimeConvert()->GetBegin(); + hsScalar end = mod->GetAnimInstance( j )->GetTimeConvert()->GetEnd(); + if( begin < tBegin ) + tBegin = begin; + if( end > tEnd ) + tEnd = end; + } + foundOne = true; + } + // Handle layer animations + plLayerAnimation *layer = plLayerAnimation::ConvertNoRef( fAnimationKeys[ i ]->ObjectIsLoaded() ); + if( layer != nil ) + { + hsScalar begin = layer->GetTimeConvert().GetBegin(); + hsScalar end = layer->GetTimeConvert().GetEnd(); + if( begin < tBegin ) + tBegin = begin; + if( end > tEnd ) + tEnd = end; + foundOne = true; + } + } + + if( foundOne ) + { + fAnimBegin = tBegin; + fAnimEnd = tEnd; + + fAnimTimesCalced = true; + } + + return fAnimTimesCalced; +} + +//// SetCurrValue //////////////////////////////////////////////////////////// + +void pfGUIProgressCtrl::SetCurrValue( hsScalar v ) +{ + int old = (int)fValue; + + pfGUIValueCtrl::SetCurrValue( v ); + +// if( old == (int)fValue ) +// return; + + if( fAnimationKeys.GetCount() > 0 ) + { + ICalcAnimTimes(); + + hsScalar tLength = fAnimEnd - fAnimBegin; + hsScalar newTime; + + if( HasFlag( kReverseValues ) ) + newTime = ( ( fMax - fValue ) / ( fMax - fMin ) ) * tLength + fAnimBegin; + else + newTime = ( ( fValue - fMin ) / ( fMax - fMin ) ) * tLength + fAnimBegin; + + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + msg->SetCmd( plAnimCmdMsg::kGoToTime ); + msg->SetAnimName( fAnimName ); + msg->fTime = newTime; + msg->AddReceivers( fAnimationKeys ); + plgDispatch::MsgSend( msg ); + } +} + +void pfGUIProgressCtrl::AnimateToPercentage( hsScalar percent ) +{ + // percent should be a value in range 0.0 to 1.0 + if (percent >= 0.0f && percent <= 1.0f) + { + pfGUIValueCtrl::SetCurrValue( (fMax - fMin) * percent + fMin ); + + if( fAnimationKeys.GetCount() > 0 ) + { + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + msg->SetCmd( plAnimCmdMsg::kPlayToPercentage ); + msg->SetAnimName( fAnimName ); + msg->fTime = percent; + msg->AddReceivers( fAnimationKeys ); + plgDispatch::MsgSend( msg ); + + if (fPlaySound) + { + // play the sound, looping + PlaySound(kAnimateSound, true); + + // setup a timer to call back when we finish animating + hsScalar elapsedTime = (fAnimEnd - fAnimBegin) * percent; + plTimerCallbackMsg *timerMsg = TRACKED_NEW plTimerCallbackMsg(GetKey(), fStopSoundTimer); + plgTimerCallbackMgr::NewTimer(elapsedTime, timerMsg); + } + } + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIProgressCtrl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIProgressCtrl.h new file mode 100644 index 00000000..79e80682 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIProgressCtrl.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIProgressCtrl Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIProgressCtrl_h +#define _pfGUIProgressCtrl_h + +#include "pfGUIValueCtrl.h" + +class plMessage; +class plAGMasterMod; + +class pfGUIProgressCtrl : public pfGUIValueCtrl +{ + protected: + + hsTArray fAnimationKeys; + char *fAnimName; + + // Computed once, once an anim is loaded that we can compute this with + hsScalar fAnimBegin, fAnimEnd; + hsBool fAnimTimesCalced; + hsBool fPlaySound; + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + hsBool ICalcAnimTimes( void ); + + const UInt32 fStopSoundTimer; + + public: + + pfGUIProgressCtrl(); + virtual ~pfGUIProgressCtrl(); + + CLASSNAME_REGISTER( pfGUIProgressCtrl ); + GETINTERFACE_ANY( pfGUIProgressCtrl, pfGUIValueCtrl ); + + + enum OurFlags + { + kReverseValues = kDerivedFlagsStart + }; + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void UpdateBounds( hsMatrix44 *invXformMatrix = nil, hsBool force = false ); + + virtual void SetCurrValue( hsScalar v ); + virtual void AnimateToPercentage( hsScalar percent ); + + enum SoundEvents + { + kAnimateSound + }; + + void DontPlaySounds() { fPlaySound = false; } + + // Export only + void SetAnimationKeys( hsTArray &keys, const char *name ); +}; + +#endif // _pfGUIProgressCtrl_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIRadioGroupCtrl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIRadioGroupCtrl.cpp new file mode 100644 index 00000000..de660f2a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIRadioGroupCtrl.cpp @@ -0,0 +1,257 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIRadioGroupCtrl Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIRadioGroupCtrl.h" +#include "pfGameGUIMgr.h" +#include "pfGUICheckBoxCtrl.h" +#include "pfGUIControlHandlers.h" + +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + +//// Wee Little Control Proc for our buttons ///////////////////////////////// + +class pfGroupProc : public pfGUICtrlProcObject +{ + protected: + + pfGUIRadioGroupCtrl *fParent; + + public: + + pfGroupProc( pfGUIRadioGroupCtrl *parent ) + { + fParent = parent; + } + + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + Int32 newIdx; + + + // So one of our controls got clicked. That means that we change our value + // to the proper index + + pfGUICheckBoxCtrl *check = pfGUICheckBoxCtrl::ConvertNoRef( ctrl ); + + // Are we unselecting? And do we allow this? + if( !check->IsChecked() && !fParent->HasFlag( pfGUIRadioGroupCtrl::kAllowNoSelection ) ) + { + // Boo on you. Re-check + check->SetChecked( true ); + return; + } + + for( newIdx = 0; newIdx < fParent->fControls.GetCount(); newIdx++ ) + { + if( fParent->fControls[ newIdx ] == check ) + break; + } + + if( newIdx == fParent->fControls.GetCount() ) + newIdx = -1; + + if( newIdx != fParent->fValue ) + { + if( fParent->fValue != -1 ) + fParent->fControls[ fParent->fValue ]->SetChecked( false ); + + fParent->fValue = newIdx; + if( newIdx != -1 ) + fParent->fControls[ newIdx ]->SetChecked( true ); + } + else + { + if( !check->IsChecked() && fParent->HasFlag( pfGUIRadioGroupCtrl::kAllowNoSelection ) ) + { + // nobody is checked! + fParent->fValue = -1; + } + } + + fParent->DoSomething(); + } +}; + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIRadioGroupCtrl::pfGUIRadioGroupCtrl() +{ + fButtonProc = TRACKED_NEW pfGroupProc( this ); + fButtonProc->IncRef(); + SetFlag( kIntangible ); +} + +pfGUIRadioGroupCtrl::~pfGUIRadioGroupCtrl() +{ + if( fButtonProc->DecRef() ) + delete fButtonProc; +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIRadioGroupCtrl::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIControlMod::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIRadioGroupCtrl::MsgReceive( plMessage *msg ) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); + if( refMsg != nil ) + { + if( refMsg->fType == kRefControl ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fControls[ refMsg->fWhich ] = pfGUICheckBoxCtrl::ConvertNoRef( refMsg->GetRef() ); + fControls[ refMsg->fWhich ]->SetHandler( fButtonProc ); + if( fValue == refMsg->fWhich ) + fControls[ refMsg->fWhich ]->SetChecked( true ); + } + else + { + fControls[ refMsg->fWhich ] = nil; + } + return true; + } + } + + return pfGUIControlMod::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIRadioGroupCtrl::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Read(s, mgr); + + UInt32 i, count = s->ReadSwap32(); + fControls.SetCountAndZero( count ); + + for( i = 0; i < count; i++ ) + { + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kRefControl ), plRefFlags::kActiveRef ); + } + + fValue = fDefaultValue = s->ReadSwap16(); + if( fValue != -1 && fControls[ fValue ] != nil ) + fControls[ fValue ]->SetChecked( true ); +} + +void pfGUIRadioGroupCtrl::Write( hsStream *s, hsResMgr *mgr ) +{ + UInt32 i; + + + pfGUIControlMod::Write( s, mgr ); + + s->WriteSwap32( fControls.GetCount() ); + for( i = 0; i < fControls.GetCount(); i++ ) + mgr->WriteKey( s, fControls[ i ]->GetKey() ); + + s->WriteSwap16( (UInt16)fDefaultValue ); +} + +//// SetValue //////////////////////////////////////////////////////////////// + +void pfGUIRadioGroupCtrl::SetValue( Int32 value ) +{ + if( value != fValue && ( value != -1 || HasFlag( kAllowNoSelection ) ) ) + { + if( fValue != -1 ) + fControls[ fValue ]->SetChecked( false ); + + fValue = value; + if( value != -1 ) + fControls[ value ]->SetChecked( true ); + + DoSomething(); + } +} + +///// Setting to be trickled down to the underlings + +void pfGUIRadioGroupCtrl::SetEnabled( hsBool e ) +{ + int i; + for( i = 0; i < fControls.GetCount(); i++ ) + fControls[ i ]->SetEnabled(e); +} + +void pfGUIRadioGroupCtrl::SetInteresting( hsBool e ) +{ + int i; + for( i = 0; i < fControls.GetCount(); i++ ) + fControls[ i ]->SetInteresting(e); +} + +void pfGUIRadioGroupCtrl::SetVisible( hsBool vis ) +{ + int i; + for( i = 0; i < fControls.GetCount(); i++ ) + fControls[ i ]->SetVisible(vis); +} + +void pfGUIRadioGroupCtrl::SetControlsFlag( int flag ) +{ + int i; + for( i = 0; i < fControls.GetCount(); i++ ) + fControls[ i ]->SetFlag(flag); +} + + +void pfGUIRadioGroupCtrl::ClearControlsFlag( int flag ) +{ + int i; + for( i = 0; i < fControls.GetCount(); i++ ) + fControls[ i ]->ClearFlag(flag); +} + + +//// Export Functions //////////////////////////////////////////////////////// + +void pfGUIRadioGroupCtrl::ClearControlList( void ) +{ + fControls.Reset(); + fValue = -1; +} + +void pfGUIRadioGroupCtrl::AddControl( pfGUICheckBoxCtrl *ctrl ) +{ + fControls.Append( ctrl ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIRadioGroupCtrl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIRadioGroupCtrl.h new file mode 100644 index 00000000..4c4f3bf6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIRadioGroupCtrl.h @@ -0,0 +1,94 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIRadioGroupCtrl Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIRadioGroupCtrl_h +#define _pfGUIRadioGroupCtrl_h + +#include "pfGUIValueCtrl.h" +#include "hsTemplates.h" + +class plMessage; +class pfGUICheckBoxCtrl; +class pfGroupProc; + + +class pfGUIRadioGroupCtrl : public pfGUIControlMod +{ + friend class pfGroupProc; + + protected: + + enum + { + kRefControl = kRefDerivedStart + }; + + hsTArray fControls; + pfGroupProc *fButtonProc; + + Int32 fValue, fDefaultValue; + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + public: + + pfGUIRadioGroupCtrl(); + virtual ~pfGUIRadioGroupCtrl(); + + CLASSNAME_REGISTER( pfGUIRadioGroupCtrl ); + GETINTERFACE_ANY( pfGUIRadioGroupCtrl, pfGUIControlMod ); + + enum OurFlags + { + kAllowNoSelection = kDerivedFlagsStart + }; + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + Int32 GetValue( void ) { return fValue; } + void SetValue( Int32 value ); + + virtual void SetEnabled( hsBool e ); + virtual void SetInteresting( hsBool e ); + virtual void SetVisible( hsBool vis ); + virtual void SetControlsFlag( int flag ); + virtual void ClearControlsFlag( int flag ); + + /// Export ONLY + void ClearControlList( void ); + void AddControl( pfGUICheckBoxCtrl *ctrl ); + void SetDefaultValue( Int32 value ) { fDefaultValue = value; } +}; + +#endif // _pfGUIRadioGroupCtrl_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITagDefs.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITagDefs.cpp new file mode 100644 index 00000000..99260015 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITagDefs.cpp @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUITagDefs.cpp // +// List of Tag IDs for the GameGUIMgr // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "pfGameGUIMgr.h" +#include "pfGUITagDefs.h" + +//// Tag List //////////////////////////////////////////////////////////////// +// Here's the actual list of tags. It's basically a list of konstants, but +// they get translated into two things: +// 1. An enum, to send as a UInt32 to the GetDialogFromTag() and +// GetControlFromTag() functions. +// 2. A string, which gets put in a dropdown box in the appropriate +// MAX component, which sets the given control's tag ID to the +// right konstant. + +// Step 1: add your konstant to the end of the .h file list + +// Step 2: Add the string here + +pfGUITag gGUITags[] = { + { kKIMainDialog, "KI Main Dialog" }, + { kKITestEditBox, "KI Test Control" }, + { kKIEntryDlg, "KI Entry Dlg" }, + { kKICloseButton, "KI Close Dlg Button" }, + { kKITestControl2, "KI Test Control 2" }, + { kKIAddButton, "KI Add Button" }, + { kKIEditButton, "KI Edit Button" }, + { kKIRemoveButton, "KI Remove Button" }, + { kKIYesNoDlg, "KI Yes/No Dialog" }, + { kKIYesBtn, "KI Yes Button" }, + { kKINoBtn, "KI No Button" }, + { kKIStaticText, "KI Static Text" }, + { kKITestControl3, "KI Test Control 3" }, + { kKIMiniDialog, "KI Mini Dialog" }, + { kPlayerBook, "PB Dialog" }, + { kPBLinkToBtn, "PB Link To Button" }, + { kPBSaveLinkBtn, "PB Save Link Button" }, + { kPBSaveSlotRadio, "PB Save Slot Radio" }, + { kPBSaveSlotPrev1, "PB Save Slot Preview 1" }, + { kPBSaveSlotPrev2, "PB Save Slot Preview 2" }, + { kPBSaveSlotPrev3, "PB Save Slot Preview 3" }, + { kPBSaveSlotPrev4, "PB Save Slot Preview 4" }, + { kPBSaveSlotPrev5, "PB Save Slot Preview 5" }, + { kPBSaveSlotPrev6, "PB Save Slot Preview 6" }, + { kKICurrPlayerText, "KI Current Player Label" }, + { kKIPlayerList, "KI Mini Friends List" }, + { kKIChatModeBtn, "KI Toggle Chat Mode Btn" }, + { kBlackBarDlg, "Black Bar Dialog" }, + { kBlackBarKIButtons, "Black Bar KI Radio Group" }, + { kKILogoutButton, "KI Logout Button" }, + + { 0, "" } // Ending tag, MUST ALWAYS BE HERE +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITagDefs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITagDefs.h new file mode 100644 index 00000000..f095995c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITagDefs.h @@ -0,0 +1,88 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUITagDefs.cpp // +// List of Tag IDs for the GameGUIMgr // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUITagDefs_h +#define _pfGUITagDefs_h + +#include "pfGameGUIMgr.h" + +//// Tag List //////////////////////////////////////////////////////////////// +// Here's the actual list of tags. It's basically a list of konstants, but +// they get translated into two things: +// 1. An enum, to send as a UInt32 to the GetDialogFromTag() and +// GetControlFromTag() functions. +// 2. A string, which gets put in a dropdown box in the appropriate +// MAX component, which sets the given control's tag ID to the +// right konstant. + + +// Step 1: Add your konstant to the end of this list + +enum +{ + kKIMainDialog = 1, + kKITestEditBox, + kKIEntryDlg, + kKICloseButton, + kKITestControl2, + kKIAddButton, + kKIEditButton, + kKIRemoveButton, + kKIYesNoDlg, + kKIYesBtn, + kKINoBtn, + kKIStaticText, + kKITestControl3, + kKIMiniDialog, + kPlayerBook, + kPBLinkToBtn, + kPBSaveLinkBtn, + kPBSaveSlotRadio, + kPBSaveSlotPrev1, + kPBSaveSlotPrev2, + kPBSaveSlotPrev3, + kPBSaveSlotPrev4, + kPBSaveSlotPrev5, + kPBSaveSlotPrev6, + + kKICurrPlayerText = 30, + kKIPlayerList = 31, + kKIChatModeBtn = 32, + + kBlackBarDlg = 33, + kBlackBarKIButtons = 34, + kKILogoutButton = 35, +}; + +// Step 2: Add the string to the .cpp file + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITextBoxMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITextBoxMod.cpp new file mode 100644 index 00000000..7b60dddb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITextBoxMod.cpp @@ -0,0 +1,246 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUITextBoxMod Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "pfGUITextBoxMod.h" +#include "pfGameGUIMgr.h" + +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plAvatar/plAGModifier.h" +#include "../plGImage/plDynamicTextMap.h" +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "../plResMgr/plLocalization.h" + +#include "../pfLocalizationMgr/pfLocalizationMgr.h" + + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUITextBoxMod::pfGUITextBoxMod() +{ +// SetFlag( kWantsInterest ); + SetFlag( kIntangible ); + fText = nil; + fUseLocalizationPath = false; +} + +pfGUITextBoxMod::~pfGUITextBoxMod() +{ + delete [] fText; +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUITextBoxMod::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIControlMod::IEval( secs, del, dirty ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUITextBoxMod::MsgReceive( plMessage *msg ) +{ + return pfGUIControlMod::MsgReceive( msg ); +} + +//// IPostSetUpDynTextMap //////////////////////////////////////////////////// + +void pfGUITextBoxMod::IPostSetUpDynTextMap( void ) +{ + pfGUIColorScheme *scheme = GetColorScheme(); + + fDynTextMap->SetFont( scheme->fFontFace, scheme->fFontSize, scheme->fFontFlags, + HasFlag( kXparentBgnd ) ? false : true ); + fDynTextMap->SetTextColor( scheme->fForeColor, + ( HasFlag( kXparentBgnd ) && scheme->fBackColor.a == 0.f ) ? true : false ); +} + +//// IUpdate ///////////////////////////////////////////////////////////////// + +void pfGUITextBoxMod::IUpdate( void ) +{ + if( fDynTextMap == nil || !fDynTextMap->IsValid() ) + return; + + if( HasFlag( kCenterJustify ) ) + fDynTextMap->SetJustify( plDynamicTextMap::kCenter ); + else if( HasFlag( kRightJustify ) ) + fDynTextMap->SetJustify( plDynamicTextMap::kRightJustify ); + else + fDynTextMap->SetJustify( plDynamicTextMap::kLeftJustify ); + + fDynTextMap->ClearToColor( GetColorScheme()->fBackColor ); + + std::wstring drawStr; + if (fUseLocalizationPath && !fLocalizationPath.empty() && pfLocalizationMgr::InstanceValid()) + drawStr = pfLocalizationMgr::Instance().GetString(fLocalizationPath.c_str()); + else + { + if( fText != nil ) + { + int lang = plLocalization::GetLanguage(); + std::vector translations = plLocalization::StringToLocal(fText); + if (translations[lang] == L"") // if the translations doesn't exist, draw English + drawStr = translations[0].c_str(); + else + drawStr = translations[lang].c_str(); + } + } + + if (!drawStr.empty()) + fDynTextMap->DrawWrappedString( 4, 4, drawStr.c_str(), fDynTextMap->GetVisibleWidth() - 8, fDynTextMap->GetVisibleHeight() - 8 ); + + fDynTextMap->FlushToHost(); +} + +void pfGUITextBoxMod::PurgeDynaTextMapImage() +{ + if ( fDynTextMap != nil ) + fDynTextMap->PurgeImage(); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUITextBoxMod::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Read(s, mgr); + + UInt32 len = s->ReadSwap32(); + if( len > 0 ) + { + char *text = TRACKED_NEW char[ len + 1 ]; + s->Read( len, text ); + text[ len ] = 0; + + fText = hsStringToWString(text); + delete [] text; + } + else + fText = nil; + + fUseLocalizationPath = (s->ReadBool() != 0); + if (fUseLocalizationPath) + { + wchar_t* temp = s->ReadSafeWString(); + fLocalizationPath = temp; + delete [] temp; + } +} + +void pfGUITextBoxMod::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Write( s, mgr ); + + if( fText == nil ) + s->WriteSwap32( 0 ); + else + { + char *text = hsWStringToString(fText); + s->WriteSwap32( strlen( text ) ); + s->Write( strlen( text ), text ); + delete [] text; + } + + // Make sure we only write out to use localization path if the box is checked + // and the path isn't empty + bool useLoc = fUseLocalizationPath && !fLocalizationPath.empty(); + + s->WriteBool(useLoc); + if (useLoc) + s->WriteSafeWString(fLocalizationPath.c_str()); +} + +//// HandleMouseDown/Up ////////////////////////////////////////////////////// + +void pfGUITextBoxMod::HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ) +{ +} + +void pfGUITextBoxMod::HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ) +{ +} + +void pfGUITextBoxMod::HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ) +{ +} + +//// SetText ///////////////////////////////////////////////////////////////// + +void pfGUITextBoxMod::SetText( const char *text ) +{ + delete [] fText; + if (text) + { + fText = hsStringToWString(text); + } + else + fText = nil; + IUpdate(); +} + +void pfGUITextBoxMod::SetText( const wchar_t *text ) +{ + delete [] fText; + if (text) + { + fText = TRACKED_NEW wchar_t[wcslen(text)+1]; + wcscpy(fText,text); + } + else + fText = nil; + IUpdate(); +} + +void pfGUITextBoxMod::SetLocalizationPath(const wchar_t* path) +{ + if (path) + fLocalizationPath = path; +} + +void pfGUITextBoxMod::SetLocalizationPath(const char* path) +{ + if (path) + { + wchar_t* wPath = hsStringToWString(path); + fLocalizationPath = wPath; + delete [] wPath; + } +} + +void pfGUITextBoxMod::SetUseLocalizationPath(bool use) +{ + fUseLocalizationPath = use; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITextBoxMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITextBoxMod.h new file mode 100644 index 00000000..56aa1df7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUITextBoxMod.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUITextBoxMod Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUITextBoxMod_h +#define _pfGUITextBoxMod_h + +#include "pfGUIControlMod.h" + +class plMessage; +class hsGMaterial; +class plTextGenerator; + +class pfGUITextBoxMod : public pfGUIControlMod +{ + protected: + + wchar_t *fText; + std::wstring fLocalizationPath; + bool fUseLocalizationPath; + + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + virtual void IUpdate( void ); + virtual void IPostSetUpDynTextMap( void ); + + public: + + pfGUITextBoxMod(); + virtual ~pfGUITextBoxMod(); + + CLASSNAME_REGISTER( pfGUITextBoxMod ); + GETINTERFACE_ANY( pfGUITextBoxMod, pfGUIControlMod ); + + enum OurFlags + { + kCenterJustify = kDerivedFlagsStart, + kRightJustify + }; + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void HandleMouseDown( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseUp( hsPoint3 &mousePt, UInt8 modifiers ); + virtual void HandleMouseDrag( hsPoint3 &mousePt, UInt8 modifiers ); + + virtual void PurgeDynaTextMapImage(); + + virtual const wchar_t* GetText() { return fText; } + + // Export only + void SetText( const char *text ); + void SetText( const wchar_t *text ); + + void SetLocalizationPath(const wchar_t* path); + void SetLocalizationPath(const char* path); + void SetUseLocalizationPath(bool use); + + virtual void UpdateColorScheme() { IPostSetUpDynTextMap(); IUpdate(); } +}; + +#endif // _pfGUITextBoxMod_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIUpDownPairMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIUpDownPairMod.cpp new file mode 100644 index 00000000..3d08f391 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIUpDownPairMod.cpp @@ -0,0 +1,225 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIUpDownPairMod Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIUpDownPairMod.h" +#include "pfGameGUIMgr.h" +#include "pfGUIButtonMod.h" +#include "pfGUIControlHandlers.h" + +#include "../pnMessage/plRefMsg.h" +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plAvatar/plAGModifier.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + +//// Wee Little Control Proc for our buttons ///////////////////////////////// + +class pfUpDownBtnProc : public pfGUICtrlProcObject +{ + protected: + + pfGUIButtonMod *fUp, *fDown; + pfGUIUpDownPairMod *fParent; + + public: + + pfUpDownBtnProc( pfGUIButtonMod *up, pfGUIButtonMod *down, pfGUIUpDownPairMod *parent ) + { + fUp = up; + fDown = down; + fParent = parent; + } + + void SetUp( pfGUIButtonMod *up ) { fUp = up; } + void SetDown( pfGUIButtonMod *down ) { fDown = down; } + + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + if( (pfGUIButtonMod *)ctrl == fUp ) + { + fParent->fValue += fParent->fStep; + if( fParent->fValue > fParent->fMax ) + fParent->fValue = fParent->fMax; + } + else + { + fParent->fValue -= fParent->fStep; + if( fParent->fValue < fParent->fMin ) + fParent->fValue = fParent->fMin; + } + fParent->Update(); + fParent->DoSomething(); + } +}; + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIUpDownPairMod::pfGUIUpDownPairMod() +{ + fUpControl = nil; + fDownControl = nil; + fValue = fMin = fMax = fStep = 0.f; + + fButtonProc = TRACKED_NEW pfUpDownBtnProc( nil, nil, this ); + fButtonProc->IncRef(); + SetFlag( kIntangible ); +} + +pfGUIUpDownPairMod::~pfGUIUpDownPairMod() +{ + if( fButtonProc->DecRef() ) + delete fButtonProc; +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfGUIUpDownPairMod::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return pfGUIValueCtrl::IEval( secs, del, dirty ); +} + +void pfGUIUpDownPairMod::IUpdate( void ) +{ + if (fEnabled) + { + if (fUpControl) + { + if ( fValue >= fMax) + fUpControl->SetVisible(false); + else + fUpControl->SetVisible(true); + } + if (fDownControl) + { + if ( fValue <= fMin ) + fDownControl->SetVisible(false); + else + fDownControl->SetVisible(true); + } + } + else + { + fUpControl->SetVisible(false); + fDownControl->SetVisible(false); + } +} + +void pfGUIUpDownPairMod::Update( void ) +{ + IUpdate(); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGUIUpDownPairMod::MsgReceive( plMessage *msg ) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); + if( refMsg != nil ) + { + if( refMsg->fType == kRefUpControl ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fUpControl = pfGUIButtonMod::ConvertNoRef( refMsg->GetRef() ); + fUpControl->SetHandler( fButtonProc ); + fButtonProc->SetUp( fUpControl ); + } + else + { + fUpControl = nil; + fButtonProc->SetUp( nil ); + } + return true; + } + else if( refMsg->fType == kRefDownControl ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fDownControl = pfGUIButtonMod::ConvertNoRef( refMsg->GetRef() ); + fDownControl->SetHandler( fButtonProc ); + fButtonProc->SetDown( fDownControl ); + } + else + { + fDownControl = nil; + fButtonProc->SetDown( nil ); + } + return true; + } + } + + return pfGUIValueCtrl::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIUpDownPairMod::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIValueCtrl::Read(s, mgr); + + fUpControl = nil; + fDownControl = nil; + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefUpControl ), plRefFlags::kActiveRef ); + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefDownControl ), plRefFlags::kActiveRef ); + + s->ReadSwap( &fMin ); + s->ReadSwap( &fMax ); + s->ReadSwap( &fStep ); + + fValue = fMin; +} + +void pfGUIUpDownPairMod::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIValueCtrl::Write( s, mgr ); + + mgr->WriteKey( s, fUpControl->GetKey() ); + mgr->WriteKey( s, fDownControl->GetKey() ); + + s->WriteSwap( fMin ); + s->WriteSwap( fMax ); + s->WriteSwap( fStep ); +} + + +void pfGUIUpDownPairMod::SetRange( hsScalar min, hsScalar max ) +{ + pfGUIValueCtrl::SetRange( min, max ); + IUpdate(); +} + +void pfGUIUpDownPairMod::SetCurrValue( hsScalar v ) +{ + pfGUIValueCtrl::SetCurrValue( v ); + IUpdate(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIUpDownPairMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIUpDownPairMod.h new file mode 100644 index 00000000..a94a394f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIUpDownPairMod.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIUpDownPairMod Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIUpDownPairMod_h +#define _pfGUIUpDownPairMod_h + +#include "pfGUIValueCtrl.h" + +class plMessage; +class pfGUIButtonMod; +class pfUpDownBtnProc; + +class pfGUIUpDownPairMod : public pfGUIValueCtrl +{ + friend class pfUpDownBtnProc; + + protected: + + enum + { + kRefUpControl = kRefDerivedStart, + kRefDownControl + }; + + pfGUIButtonMod *fUpControl, *fDownControl; + pfUpDownBtnProc *fButtonProc; + + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + virtual void IUpdate( void ); + + public: + + pfGUIUpDownPairMod(); + virtual ~pfGUIUpDownPairMod(); + + CLASSNAME_REGISTER( pfGUIUpDownPairMod ); + GETINTERFACE_ANY( pfGUIUpDownPairMod, pfGUIValueCtrl ); + + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Update( void ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual void SetRange( hsScalar min, hsScalar max ); + virtual void SetCurrValue( hsScalar v ); + + /// Export ONLY + + void SetControls( pfGUIButtonMod *up, pfGUIButtonMod *down ) { fUpControl = up; fDownControl = down; } +}; + +#endif // _pfGUIUpDownPairMod_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIValueCtrl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIValueCtrl.cpp new file mode 100644 index 00000000..cadcd868 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIValueCtrl.cpp @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIValueCtrl Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfGUIValueCtrl.h" +#include "pfGameGUIMgr.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfGUIValueCtrl::pfGUIValueCtrl() +{ + fValue = fMin = fMax = fStep = 0.f; +} + +pfGUIValueCtrl::~pfGUIValueCtrl() +{ +} + +//// SetCurrValue //////////////////////////////////////////////////////////// + +void pfGUIValueCtrl::SetCurrValue( hsScalar v ) +{ + fValue = v; + if( fValue < fMin ) + fValue = fMin; + else if( fValue > fMax ) + fValue = fMax; +} + +//// SetRange //////////////////////////////////////////////////////////////// + +void pfGUIValueCtrl::SetRange( hsScalar min, hsScalar max ) +{ + fMin = min; + fMax = max; + if( fValue < fMin ) + SetCurrValue( fMin ); + else if( fValue > fMax ) + SetCurrValue( fMax ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfGUIValueCtrl::Read( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Read(s, mgr); + + s->ReadSwap( &fMin ); + s->ReadSwap( &fMax ); + s->ReadSwap( &fStep ); + + fValue = fMin; +} + +void pfGUIValueCtrl::Write( hsStream *s, hsResMgr *mgr ) +{ + pfGUIControlMod::Write( s, mgr ); + + s->WriteSwap( fMin ); + s->WriteSwap( fMax ); + s->WriteSwap( fStep ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIValueCtrl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIValueCtrl.h new file mode 100644 index 00000000..283043e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGUIValueCtrl.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGUIValueCtrl Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGUIValueCtrl_h +#define _pfGUIValueCtrl_h + +#include "pfGUIControlMod.h" + + +class pfGUIValueCtrl : public pfGUIControlMod +{ + protected: + + hsScalar fValue, fMin, fMax, fStep; + + + public: + + pfGUIValueCtrl(); + virtual ~pfGUIValueCtrl(); + + CLASSNAME_REGISTER( pfGUIValueCtrl ); + GETINTERFACE_ANY( pfGUIValueCtrl, pfGUIControlMod ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + virtual hsScalar GetCurrValue( void ) { return fValue; } + virtual void SetCurrValue( hsScalar v ); + + virtual hsScalar GetMin( void ) { return fMin; } + virtual hsScalar GetMax( void ) { return fMax; } + virtual hsScalar GetStep( void ) { return fStep; } + + virtual void SetRange( hsScalar min, hsScalar max ); + virtual void SetStep( hsScalar step ) { fStep = step; } + +}; + +#endif // _pfGUIValueCtrl_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.cpp new file mode 100644 index 00000000..7cfe6950 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.cpp @@ -0,0 +1,943 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGameGUIMgr // +// // +//// History ///////////////////////////////////////////////////////////////// +// // +// 11.13.2001 mcn - Created // +// // +////////////////////////////////////////////////////////////////////////////// + +#include +#include "hsTimer.h" +#include "hsTypes.h" +#include "pfGameGUIMgr.h" +#include "pfGUIDialogMod.h" +#include "pfGUIDialogHandlers.h" +#include "pfGUIDialogNotifyProc.h" +#include "pfGUIControlMod.h" +#include "pfGUIPopUpMenu.h" + +#include "../pfMessage/pfGameGUIMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../pnMessage/plClientMsg.h" +#include "../pnNetCommon/plSynchedObject.h" +#include "../plInputCore/plInputInterface.h" +#include "../plInputCore/plInputDevice.h" +#include "../plInputCore/plInputInterfaceMgr.h" +#include "../pnInputCore/plKeyMap.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnSceneObject/plSceneObject.h" // So we can get the target sceneNode of a dialog +#include "../plMessage/plConsoleMsg.h" +#include "plgDispatch.h" + +#include "../plResMgr/plKeyFinder.h" + +#include "pfGUITagDefs.h" + + +////////////////////////////////////////////////////////////////////////////// +//// pfGameUIInputInterface Definition /////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +class pfGameUIInputInterface : public plInputInterface +{ + protected: + pfGameGUIMgr * const fGUIManager; + + UInt8 fModifiers; + UInt8 fButtonState; + hsBool fHaveInterestingCursor; + UInt32 fCurrentCursor; + + virtual hsBool IHandleCtrlCmd( plCtrlCmd *cmd ); + virtual hsBool IControlCodeEnabled( ControlEventCode code ); + + public: + + pfGameUIInputInterface( pfGameGUIMgr * const mgr ); + + virtual UInt32 GetPriorityLevel( void ) const { return kGUISystemPriority; } + virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg ); + virtual UInt32 GetCurrentCursorID( void ) const; + virtual hsScalar GetCurrentCursorOpacity( void ) const; + virtual hsBool HasInterestingCursorID( void ) const { return fHaveInterestingCursor; } + virtual hsBool SwitchInterpretOrder( void ) const { return true; } + + virtual void RestoreDefaultKeyMappings( void ) + { + if( fControlMap != nil ) + { + fControlMap->UnmapAllBindings(); + fControlMap->BindKey( KEY_BACKSPACE, B_CONTROL_EXIT_GUI_MODE, plKeyMap::kFirstAlways ); + fControlMap->BindKey( KEY_ESCAPE, B_CONTROL_EXIT_GUI_MODE, plKeyMap::kSecondAlways ); + } + } +}; + +////////////////////////////////////////////////////////////////////////////// +//// pfGameGUIMgr Functions ////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +pfGameGUIMgr *pfGameGUIMgr::fInstance = nil; + + +//// Constructor & Destructor //////////////////////////////////////////////// + +pfGameGUIMgr::pfGameGUIMgr() +{ + fActivated = false; + fInputCtlIndex = 0; + fActiveDialogs = nil; + + fInputConfig = nil; + + fInstance = this; + + fDefaultCursor = plInputInterface::kCursorUp; + fCursorOpacity = 1.f; + fAspectRatio = 0; +} + +pfGameGUIMgr::~pfGameGUIMgr() +{ + int i; + + // the GUIMgr is dead! + fInstance = nil; + + for( i = 0; i < fDialogs.GetCount(); i++ ) + UnloadDialog( fDialogs[ i ] ); + + for( i = 0; i < fDialogToSetKeyOf.GetCount(); i++ ) + delete fDialogToSetKeyOf[i]; + + if( fActivated ) + IActivateGUI( false ); + + delete fInputConfig; +} + + +//// Init //////////////////////////////////////////////////////////////////// + +hsBool pfGameGUIMgr::Init( void ) +{ + return true; +} + +//// Draw //////////////////////////////////////////////////////////////////// + +void pfGameGUIMgr::Draw( plPipeline *p ) +{ +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfGameGUIMgr::MsgReceive( plMessage* pMsg ) +{ + pfGameGUIMsg *guiMsg = pfGameGUIMsg::ConvertNoRef( pMsg ); + if( guiMsg != nil ) + { + if( guiMsg->GetCommand() == pfGameGUIMsg::kLoadDialog ) + LoadDialog( guiMsg->GetString(), nil, guiMsg->GetAge() ); + else if( guiMsg->GetCommand() == pfGameGUIMsg::kShowDialog ) + IShowDialog( guiMsg->GetString() ); + else if( guiMsg->GetCommand() == pfGameGUIMsg::kHideDialog ) + IHideDialog( guiMsg->GetString() ); + + return true; + } + + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( pMsg ); + if( refMsg != nil ) + { + if( refMsg->fType == kDlgModRef ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest ) ) + { + IAddDlgToList( refMsg->GetRef() ); + } + else if( refMsg->GetContext() & plRefMsg::kOnReplace ) + { + IRemoveDlgFromList( refMsg->GetOldRef() ); + IAddDlgToList( refMsg->GetRef() ); + } + else if( refMsg->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) ) + { + IRemoveDlgFromList( refMsg->GetRef() ); + } + } + return true; + } + + return hsKeyedObject::MsgReceive( pMsg ); +} + +//// IAddDlgToList /////////////////////////////////////////////////////////// + +void pfGameGUIMgr::IAddDlgToList( hsKeyedObject *obj ) +{ + int i; + + + if( fDialogs.Find( (pfGUIDialogMod *)obj ) == fDialogs.kMissingIndex ) + { + pfGUIDialogMod *mod = pfGUIDialogMod::ConvertNoRef( obj ); + if( mod != nil ) + { + mod->UpdateAspectRatio(); // adding a new dialog, make sure the correct aspect ratio is set + fDialogs.Append( mod ); + + + // check to see if it is the dialog we are waiting for to be loaded + for ( i=0 ; iGetName(), mod->GetName()) ) + { + SetDialogToNotify(mod,fDialogToSetKeyOf[i]->GetKey()); + // now remove this entry... we did it + delete fDialogToSetKeyOf[i]; + fDialogToSetKeyOf.Remove(i); + // that's all the damage we can do for now... + break; + } + } + } + } + +/* // It's now officially "loaded"; take it off the pending list + for( i = 0; i < fDlgsPendingLoad.GetCount(); i++ ) + { + if( stricmp( fDlgsPendingLoad[ i ]->GetName(), ( (pfGUIDialogMod *)obj )->GetName() ) == 0 ) + { + // Here it is + delete fDlgsPendingLoad[ i ]; + fDlgsPendingLoad.Remove( i ); + break; + } + } +*/ +} + +//// IRemoveDlgFromList ////////////////////////////////////////////////////// + +void pfGameGUIMgr::IRemoveDlgFromList( hsKeyedObject *obj ) +{ + int idx = fDialogs.Find( (pfGUIDialogMod *)obj ); + if( idx != fDialogs.kMissingIndex ) + { + pfGUIDialogMod *mod = pfGUIDialogMod::ConvertNoRef( obj ); + hsAssert( mod != nil, "Non-dialog sent to gameGUIMgr::IRemoveDlg()" ); + + if( mod != nil ) + { + if( mod->IsEnabled() ) + { + mod->SetEnabled( false ); + + mod->Unlink(); + if( fActiveDialogs == nil ) + IActivateGUI( false ); + } + + // Needed? +// GetKey()->Release( mod->GetKey() ); + fDialogs.Remove( idx ); + } + } + + // It's now officially "unloaded"; take it off the pending list +/* int i; + for( i = 0; i < fDlgsPendingUnload.GetCount(); i++ ) + { + if( stricmp( fDlgsPendingUnload[ i ]->GetName(), ( (pfGUIDialogMod *)obj )->GetName() ) == 0 ) + { + // Here it is + delete fDlgsPendingUnload[ i ]; + fDlgsPendingUnload.Remove( i ); + break; + } + } +*/ +} + +//// LoadDialog ////////////////////////////////////////////////////////////// + +void pfGameGUIMgr::LoadDialog( const char *name, plKey recvrKey, const char *ageName ) +{ + // see if they want to set the receiver key once the dialog is loaded + if ( recvrKey != nil ) + { + // first see if we are loading a dialog that is already being loaded + bool alreadyLoaded = false; + int i; + for ( i=0 ; iGetName(), name) ) + { + alreadyLoaded = true; + break; + } + } + if (!alreadyLoaded) + { + pfDialogNameSetKey* pDNSK = TRACKED_NEW pfDialogNameSetKey(name,recvrKey); + fDialogToSetKeyOf.Append(pDNSK); + } + } + + hsStatusMessageF("\nLoading Dialog %s %s ... %f\n", name, ( ageName != nil ) ? ageName : "GUI", hsTimer::GetSeconds() ); + + plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY ); + + plClientMsg *msg = TRACKED_NEW plClientMsg( plClientMsg::kLoadRoomHold ); + msg->AddReceiver( clientKey ); + msg->AddRoomLoc(plKeyFinder::Instance().FindLocation(ageName ? ageName : "GUI", name)); + msg->Send(); + + // Now add this dialog to a list of pending loads (will remove it once it's fully loaded) +// fDlgsPendingLoad.Append( TRACKED_NEW pfDialogNameSetKey( name, nil ) ); +} + +//// IShowDialog ///////////////////////////////////////////////////////////// + +void pfGameGUIMgr::IShowDialog( const char *name ) +{ + int i; + + + for( i = 0; i < fDialogs.GetCount(); i++ ) + { + if( stricmp( fDialogs[ i ]->GetName(), name ) == 0 ) + { + ShowDialog( fDialogs[ i ] ); + fDialogs[i]->RefreshAllControls(); + break; + } + } +} + +//// IHideDialog ///////////////////////////////////////////////////////////// + +void pfGameGUIMgr::IHideDialog( const char *name ) +{ + int i; + + + for( i = 0; i < fDialogs.GetCount(); i++ ) + { + if( stricmp( fDialogs[ i ]->GetName(), name ) == 0 ) + { + HideDialog( fDialogs[ i ] ); + break; + } + } +} + +//// ShowDialog ////////////////////////////////////////////////////////////// + +void pfGameGUIMgr::ShowDialog( pfGUIDialogMod *dlg, bool resetClickables /* = true */ ) +{ + if ( resetClickables ) + plInputInterfaceMgr::GetInstance()->ResetClickableState(); + if( !dlg->IsEnabled() ) + { + dlg->SetEnabled( true ); + + // Add to active list + if( fActiveDialogs == nil ) + IActivateGUI( true ); + + dlg->LinkToList( &fActiveDialogs ); + + return; + } +} + +//// HideDialog ////////////////////////////////////////////////////////////// + +void pfGameGUIMgr::HideDialog( pfGUIDialogMod *dlg ) +{ + if( dlg->IsEnabled() ) + { + dlg->SetEnabled( false ); + + dlg->Unlink(); + if( fActiveDialogs == nil ) + IActivateGUI( false ); + } +} + +//// UnloadDialog //////////////////////////////////////////////////////////// +// Destroy the dialog and all the things associated with it. Fun fun. + +void pfGameGUIMgr::UnloadDialog( const char *name ) +{ + int i; + + + for( i = 0; i < fDialogs.GetCount(); i++ ) + { + if( stricmp( fDialogs[ i ]->GetName(), name ) == 0 ) + { + UnloadDialog( fDialogs[ i ] ); + break; + } + } +} + +void pfGameGUIMgr::UnloadDialog( pfGUIDialogMod *dlg ) +{ +// IRemoveDlgFromList( dlg ); + + // Add the name to our list of dialogs pending unload +// fDlgsPendingUnload.Append( TRACKED_NEW pfDialogNameSetKey( dlg->GetName(), nil ) ); + + plKey sceneNodeKey = dlg->GetSceneNodeKey(); + if( sceneNodeKey == nil ) + { + hsStatusMessageF( "Warning: Unable to grab sceneNodeKey to unload dialog %s; searching for it...", dlg->GetName() ); + sceneNodeKey = plKeyFinder::Instance().FindSceneNodeKey( dlg->GetKey()->GetUoid().GetLocation() ); + } + +// if( dlg->GetTarget() ) + if( sceneNodeKey != nil ) + { + plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY ); + + plClientMsg *msg = TRACKED_NEW plClientMsg( plClientMsg::kUnloadRoom ); + msg->AddReceiver( clientKey ); +// msg->SetProgressBarSuppression( true ); + msg->AddRoomLoc(sceneNodeKey->GetUoid().GetLocation()); + msg->Send(); + } +// GetKey()->Release( dlg->GetKey() ); +} + +//// IsDialogLoaded ////// see if the dialog is in our list of loaded dialogs + +hsBool pfGameGUIMgr::IsDialogLoaded( const char *name ) +{ + // search through all the dialogs we've loaded + int i; + for( i = 0; i < fDialogs.GetCount(); i++ ) + { + if( stricmp( fDialogs[ i ]->GetName(), name ) == 0 ) + { + // found 'em + return true; + } + } + // nota + return false; +} + +pfGUIPopUpMenu *pfGameGUIMgr::FindPopUpMenu( const char *name ) +{ + int i; + + + for( i = 0; i < fDialogs.GetCount(); i++ ) + { + pfGUIPopUpMenu *menu = pfGUIPopUpMenu::ConvertNoRef( fDialogs[ i ] ); + if( menu != nil && stricmp( menu->GetName(), name ) == 0 ) + return menu; + } + + return nil; +} + +std::vector pfGameGUIMgr::GetDlgRenderMods( void ) const +{ + std::vector retVal; + pfGUIDialogMod* curDialog = fActiveDialogs; + while (curDialog) + { + retVal.push_back(curDialog->GetRenderMod()); + curDialog = curDialog->GetNext(); + } + return retVal; +} + +///// SetDialogToNotify - based on name +// This will Set the handler to a Notify Handler that will send a GUINotifyMsg to the receiver +// +void pfGameGUIMgr::SetDialogToNotify(const char *name, plKey recvrKey) +{ + int i; + for( i = 0; i < fDialogs.GetCount(); i++ ) + { + if( stricmp( fDialogs[ i ]->GetName(), name ) == 0 ) + { + SetDialogToNotify( fDialogs[ i ], recvrKey ); + break; + } + } +} + +///// SetDialogToNotify - pfGUIDialogMod* +// This will Set the handler to a Notify Handler that will send a GUINotifyMsg to the receiver +// +void pfGameGUIMgr::SetDialogToNotify(pfGUIDialogMod *dlg, plKey recvrKey) +{ + pfGUIDialogNotifyProc* handler = TRACKED_NEW pfGUIDialogNotifyProc(recvrKey); + dlg->SetHandler(handler); + handler->OnInit(); +} + + +//// IActivateGUI //////////////////////////////////////////////////////////// +// "Activates" the GUI manager. This means enabling the drawing of GUI +// elements on the screen as well as rerouting input to us. + +void pfGameGUIMgr::IActivateGUI( hsBool activate ) +{ + if( fActivated == activate ) + return; + + if( activate ) + { + fInputConfig = TRACKED_NEW pfGameUIInputInterface( this ); + plInputIfaceMgrMsg *msg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kAddInterface ); + msg->SetIFace( fInputConfig ); + plgDispatch::MsgSend( msg ); + } + else + { + plInputIfaceMgrMsg *msg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kRemoveInterface ); + msg->SetIFace( fInputConfig ); + plgDispatch::MsgSend( msg ); + + hsRefCnt_SafeUnRef( fInputConfig ); + fInputConfig = nil; + } + + fActivated = activate; +} + +//// IHandleMouse //////////////////////////////////////////////////////////// +// Distributes mouse events to the dialogs currently active + +hsBool pfGameGUIMgr::IHandleMouse( EventType event, hsScalar mouseX, hsScalar mouseY, UInt8 modifiers, UInt32 *desiredCursor ) +{ + pfGUIDialogMod *dlg; + + + // Update interesting things first, no matter what, for ALL dialogs + hsBool modalPresent = false; + for( dlg = fActiveDialogs; dlg != nil; dlg = dlg->GetNext() ) + { + dlg->UpdateInterestingThings( mouseX, mouseY, modifiers, modalPresent ); + if (dlg->HasFlag( pfGUIDialogMod::kModal )) + modalPresent = true; + } + + for( dlg = fActiveDialogs; dlg != nil; dlg = dlg->GetNext() ) + { + if( dlg->HandleMouseEvent( event, mouseX, mouseY, modifiers ) || + ( dlg->HasFlag( pfGUIDialogMod::kModal ) && event != pfGameGUIMgr::kMouseUp ) ) + { + // If this dialog handled it, also get the cursor it wants + *desiredCursor = dlg->GetDesiredCursor(); + return true; + } + } + + return false; +} + +//// IHandleKeyEvt /////////////////////////////////////////////////////////// +// Distributes mouse events to the dialogs currently active + +hsBool pfGameGUIMgr::IHandleKeyEvt( EventType event, plKeyDef key, UInt8 modifiers ) +{ + pfGUIDialogMod *dlg; + + + for( dlg = fActiveDialogs; dlg != nil; dlg = dlg->GetNext() ) + { + if( dlg->HandleKeyEvent( event, key, modifiers ) ) + return true; + } + + return false; +} + +//// IHandleKeyPress ///////////////////////////////////////////////////////// +// Like IHandleKeyPress, but takes in a char for distributing actual +// characters typed. + +hsBool pfGameGUIMgr::IHandleKeyPress( char key, UInt8 modifiers ) +{ + pfGUIDialogMod *dlg; + + + for( dlg = fActiveDialogs; dlg != nil; dlg = dlg->GetNext() ) + { + if( dlg->HandleKeyPress( key, modifiers ) ) + return true; + } + + return false; +} + +//// IModalBlocking ////////////////////////////////////////////////////////// +// Looks at the chain of active dialogs and determines if there's any modals +// blocking input. Returns true if so. + +hsBool pfGameGUIMgr::IModalBlocking( void ) +{ + return ( IGetTopModal() != nil ) ? true : false; +} + +//// IGetTopModal //////////////////////////////////////////////////////////// +// Returns the topmost (visible) modal dialog, nil if none. + +pfGUIDialogMod *pfGameGUIMgr::IGetTopModal( void ) const +{ + pfGUIDialogMod *dlg; + + + for( dlg = fActiveDialogs; dlg != nil; dlg = dlg->GetNext() ) + { + if( dlg->HasFlag( pfGUIDialogMod::kModal ) ) + return dlg; + } + + return nil; +} + + +////////////////////////////////////////////////////////////////////////////// +//// Control Config Class //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +pfGameUIInputInterface::pfGameUIInputInterface( pfGameGUIMgr * const mgr ) : plInputInterface(), fGUIManager( mgr ) +{ + fModifiers = pfGameGUIMgr::kNoModifiers; + fButtonState = 0; + fHaveInterestingCursor = false; + SetEnabled( true ); // Always enabled + fCurrentCursor = kCursorUp; + + // Add our control codes to our control map. Do NOT add the key bindings yet. + // Note: HERE is where you specify the actions for each command, i.e. net propagate and so forth. + // This part basically declares us master of the bindings for these commands. + + // IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO + // RestoreDefaultKeyMappings()!!!! + + fControlMap->AddCode( B_CONTROL_EXIT_GUI_MODE, kControlFlagNormal | kControlFlagNoRepeat ); + + // IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO + // RestoreDefaultKeyMappings()!!!! +} + +hsBool pfGameUIInputInterface::IControlCodeEnabled( ControlEventCode code ) +{ + if( code == B_CONTROL_EXIT_GUI_MODE ) + { + // Disable the exitGUIMode key binding if we don't have a modal dialog up or if + // the cursor is inside an edit or multiline edit control + if( !fGUIManager->IModalBlocking() ) + return false; + + pfGUIDialogMod *dlg = fGUIManager->IGetTopModal(); + if( dlg != nil ) + { + pfGUIControlMod *ctrl = dlg->GetFocus(); + if( ctrl != nil && ctrl->HasFlag( pfGUIControlMod::kTakesSpecialKeys ) ) + return false; + } + } + return true; // Enable all other codes +} + +hsBool pfGameUIInputInterface::IHandleCtrlCmd( plCtrlCmd *cmd ) +{ + if( cmd->fControlCode == B_CONTROL_EXIT_GUI_MODE ) + { + if( cmd->fControlActivated ) + { + pfGUIDialogMod *dlg = fGUIManager->IGetTopModal(); + if( dlg != nil && dlg->GetHandler() != nil ) + dlg->GetHandler()->OnControlEvent( pfGUIDialogProc::kExitMode ); + } + + return true; + } + return false; +} + +hsBool pfGameUIInputInterface::InterpretInputEvent( plInputEventMsg *pMsg ) +{ + hsBool handled = false; + + + /// The in-game UI has to do far more complicated control handling, so we just overload this entirely + plKeyEventMsg *pKeyMsg = plKeyEventMsg::ConvertNoRef( pMsg ); + if( pKeyMsg ) + { + // By default, we don't want the modifier keys treated as "handled", 'cause + // we want the other interfaces to get them as well (unless we have a modal + // as the top dialog). + if( pKeyMsg->GetKeyCode() == KEY_SHIFT ) + { + if( pKeyMsg->GetKeyDown() ) + fModifiers |= pfGameGUIMgr::kShiftDown; + else + fModifiers &= ~pfGameGUIMgr::kShiftDown; + } + else if( pKeyMsg->GetKeyCode() == KEY_CTRL ) + { + if( pKeyMsg->GetKeyDown() ) + fModifiers |= pfGameGUIMgr::kCtrlDown; + else + fModifiers &= ~pfGameGUIMgr::kCtrlDown; + } + else if( pKeyMsg->GetKeyCode() == KEY_CAPSLOCK ) + { + if( pKeyMsg->GetKeyDown() ) + fModifiers |= pfGameGUIMgr::kCapsDown; + else + fModifiers &= ~pfGameGUIMgr::kCapsDown; + } + else + { + // Sometimes I can't explain why Mathew does some of the things he does. + // I going to replace his modifier flags (which I don't know why he thought he had to have his own) + // with the ones that are in the keymsg since they seem to be more accurate! + fModifiers = 0; + if ( pKeyMsg->GetShiftKeyDown() ) + fModifiers |= pfGameGUIMgr::kShiftDown; + if ( pKeyMsg->GetCtrlKeyDown() ) + fModifiers |= pfGameGUIMgr::kCtrlDown; + if ( pKeyMsg->GetCapsLockKeyDown() ) + fModifiers |= pfGameGUIMgr::kCapsDown; + if( pKeyMsg->GetKeyDown() ) + { + if( !pKeyMsg->GetRepeat() ) + handled = fGUIManager->IHandleKeyEvt( pfGameGUIMgr::kKeyDown, pKeyMsg->GetKeyCode(), fModifiers ); + else + handled = fGUIManager->IHandleKeyEvt( pfGameGUIMgr::kKeyRepeat, pKeyMsg->GetKeyCode(), fModifiers ); + + handled |= fGUIManager->IHandleKeyPress( plKeyboardDevice::KeyEventToChar( pKeyMsg ), fModifiers ); + } + else + handled = fGUIManager->IHandleKeyEvt( pfGameGUIMgr::kKeyUp, pKeyMsg->GetKeyCode(), fModifiers ); + } + + // We need to do early interception of a screenshot request, since they want + // us to be able to take screen shots while in a modal GUI... whee + // Also, this should only be run if the dialog didn't handle the command in + // the first place (taking screenshots while the user is typing would be + // awkward) and we must do it on key down because the key binding routines + // also trigger on key-down and we don't want to be taking screen shots when + // the user re-binds the screenshot command. + // HACK HACK HACK + if ((!handled) && (pKeyMsg->GetKeyDown())) + { + const plKeyBinding* keymap = plInputInterfaceMgr::GetInstance()->FindBindingByConsoleCmd("Game.KITakePicture"); + if (keymap) + { + unsigned keyFlags = 0; + if (pKeyMsg->GetCtrlKeyDown()) + keyFlags |= plKeyCombo::kCtrl; + if (pKeyMsg->GetShiftKeyDown()) + keyFlags |= plKeyCombo::kShift; + plKeyCombo combo(pKeyMsg->GetKeyCode(), keyFlags); + if ((keymap->GetKey1().IsSatisfiedBy(combo)) || (keymap->GetKey2().IsSatisfiedBy(combo))) + { + // tell the KI to take the shot + plConsoleMsg * consoleMsg = NEWZERO(plConsoleMsg); + consoleMsg->SetCmd(plConsoleMsg::kExecuteLine); + consoleMsg->SetString("Game.KITakePicture"); + consoleMsg->Send(nil, true); + } + } + } + + bool modal = fGUIManager->IModalBlocking(); + return handled || modal; // we "handle" it if we are modal, even if it didn't do anything + } + + plMouseEventMsg *pMouseMsg = plMouseEventMsg::ConvertNoRef( pMsg ); + if( pMouseMsg && fManager->IsClickEnabled() ) + { + if( pMouseMsg->GetButton() == kLeftButtonDown ) + { + handled = fGUIManager->IHandleMouse( pfGameGUIMgr::kMouseDown, pMouseMsg->GetXPos(), pMouseMsg->GetYPos(), fModifiers, &fCurrentCursor ); + if (handled) + fButtonState |= kLeftButtonDown; + } + else if( pMouseMsg->GetButton() == kLeftButtonUp ) + { + handled = fGUIManager->IHandleMouse( pfGameGUIMgr::kMouseUp, pMouseMsg->GetXPos(), pMouseMsg->GetYPos(), fModifiers, &fCurrentCursor ); + if ((handled) || (fButtonState & kLeftButtonDown)) // even if we didn't handle the mouse up, if we think the button is still down, we should clear our flag + fButtonState &= ~kLeftButtonDown; + } + else if( pMouseMsg->GetButton() == kLeftButtonDblClk ) + handled = fGUIManager->IHandleMouse( pfGameGUIMgr::kMouseDblClick, pMouseMsg->GetXPos(), pMouseMsg->GetYPos(), fModifiers, &fCurrentCursor ); + else if( fButtonState & kLeftButtonDown ) + handled = fGUIManager->IHandleMouse( pfGameGUIMgr::kMouseDrag, pMouseMsg->GetXPos(), pMouseMsg->GetYPos(), fModifiers, &fCurrentCursor ); + else + handled = fGUIManager->IHandleMouse( pfGameGUIMgr::kMouseMove, pMouseMsg->GetXPos(), pMouseMsg->GetYPos(), fModifiers, &fCurrentCursor ); + + fHaveInterestingCursor = handled; + return handled; + } + + return false; +} + +UInt32 pfGameUIInputInterface::GetCurrentCursorID( void ) const +{ + if( fCurrentCursor == 0 ) + { + if ( pfGameGUIMgr::GetInstance() ) + return pfGameGUIMgr::GetInstance()->GetDefaultCursor(); + else + return kCursorUp; + } + + return fCurrentCursor; +} + +hsScalar pfGameUIInputInterface::GetCurrentCursorOpacity( void ) const +{ + if ( pfGameGUIMgr::GetInstance() ) + return pfGameGUIMgr::GetInstance()->GetCursorOpacity(); + else + return 1.f; +} + +////////////////////////////////////////////////////////////////////////////// +//// Tag Stuff /////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +extern pfGUITag gGUITags[]; // From pfGUITagDefs.cpp + +//// GetDialogFromTag //////////////////////////////////////////////////////// + +pfGUIDialogMod *pfGameGUIMgr::GetDialogFromTag( UInt32 tagID ) +{ + int i; + + + for( i = 0; i < fDialogs.GetCount(); i++ ) + { + if( fDialogs[ i ]->GetTagID() == tagID ) + return fDialogs[ i ]; + } + + return nil; +} + +//// GetDialogFromString //////////////////////////////////////////////////////// + +pfGUIDialogMod *pfGameGUIMgr::GetDialogFromString( const char *name ) +{ + int i; + + + for( i = 0; i < fDialogs.GetCount(); i++ ) + { + if( stricmp( fDialogs[ i ]->GetName(), name ) == 0 ) + return fDialogs[ i ]; + } + + return nil; +} + +//// GetControlFromTag /////////////////////////////////////////////////////// + +pfGUIControlMod *pfGameGUIMgr::GetControlFromTag( pfGUIDialogMod *dlg, UInt32 tagID ) +{ + return dlg->GetControlFromTag( tagID ); +} + +//// GetNumTags ////////////////////////////////////////////////////////////// + +UInt32 pfGameGUIMgr::GetNumTags( void ) +{ + UInt32 count; + + + for( count = 0; gGUITags[ count ].fID != 0; count++ ); + return count; +} + +//// GetTag ////////////////////////////////////////////////////////////////// + +pfGUITag *pfGameGUIMgr::GetTag( UInt32 tagIndex ) +{ + UInt32 count; + + + for( count = 0; gGUITags[ count ].fID != 0; count++ ); + hsAssert( tagIndex < count, "Bad index to GetTag()" ); + + return &gGUITags[ tagIndex ]; +} + +UInt32 pfGameGUIMgr::GetHighestTag( void ) +{ + UInt32 i, id = 1; + + + for( i = 0; gGUITags[ i ].fID != 0; i++ ) + { + if( id < gGUITags[ i ].fID ) + id = gGUITags[ i ].fID; + } + + return id; +} + + +void pfGameGUIMgr::SetAspectRatio(hsScalar aspectratio) +{ + hsScalar oldAspectRatio = fAspectRatio; + + // don't allow the aspectratio below 4:3 + hsScalar fourThree = 4.0f/3.0f; + fAspectRatio = aspectratio < fourThree ? fourThree : aspectratio; + + if (fAspectRatio != oldAspectRatio) + { + // need to tell dialogs to update + int i; + for (i = 0; i < fDialogs.GetCount(); i++) + { + if (fDialogs[i]) + fDialogs[i]->UpdateAspectRatio(); + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.h new file mode 100644 index 00000000..a98f8e32 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgr.h @@ -0,0 +1,221 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGameGUIMgr Header // +// A.K.A. "Ooh, we get a GUI!" // +// // +//// Description ///////////////////////////////////////////////////////////// +// // +// The in-game GUI manager. Handles reading, creation, and input for // +// dialog boxes at runtime. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGameGUIMgr_h +#define _pfGameGUIMgr_h + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "../pnInputCore/plKeyDef.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include + +class plPipeline; +class plMessage; +class pfGUIDialogMod; +class pfGUIControlMod; +class pfGameUIInputInterface; +class plPostEffectMod; + +//// Tag Definitions ///////////////////////////////////////////////////////// +// Each dialog/control gets an optional tag ID number. This is the link +// between MAX and C++. You attach a Tag component to a control or dialog +// in MAX and assign it an ID (supplied by a list of konstants that are +// hard-coded). Then, in code, you ask the gameGUIMgr for the dialog (or +// control) with that ID, and pop, you get it back. Then you run with it. +// +// Easy, huh? + +class pfGUITag +{ + public: + UInt32 fID; + char fName[ 128 ]; +}; + + +// +// This class just holds a name and the key to set the receiver to +// after the dialog gets loaded. +class pfDialogNameSetKey +{ +private: + char *fName; + plKey fKey; +public: + pfDialogNameSetKey(const char *name, plKey key) { fName = hsStrcpy(name); fKey=key; } + ~pfDialogNameSetKey() { delete [] fName; } + const char *GetName() { return fName; } + plKey GetKey() { return fKey; } +}; + +//// Manager Class Definition //////////////////////////////////////////////// + +class pfGUIPopUpMenu; +class pfGameGUIMgr : public hsKeyedObject +{ + friend class pfGameUIInputInterface; + + public: + + enum EventType + { + kMouseDown, + kMouseUp, + kMouseMove, + kMouseDrag, + kKeyDown, + kKeyUp, + kKeyRepeat, + kMouseDblClick + }; + + enum + { + kNoModifiers = 0, + kShiftDown = 0x01, + kCtrlDown = 0x02, + kCapsDown = 0x04 + }; + + private: + + static pfGameGUIMgr *fInstance; + + protected: + + hsTArray fDialogs; + pfGUIDialogMod *fActiveDialogs; + + // These two lists help us manage when dialogs get told to load or unload versus when they actually *do* + hsTArray fDlgsPendingLoad; + hsTArray fDlgsPendingUnload; + + hsBool fActivated; + UInt32 fActiveDlgCount; + + pfGameUIInputInterface *fInputConfig; + UInt32 fInputCtlIndex; + + UInt32 fDefaultCursor; + hsScalar fCursorOpacity; + hsScalar fAspectRatio; + + // This is an array of the dialogs (by name) that need their + // receiver key set once they are loaded. + // This array shouldn't get more than one entry... but + // it could be more.... + // LoadDialog adds an entry and MsgReceive removes it + hsTArray fDialogToSetKeyOf; + + void ILoadDialog( const char *name ); + void IShowDialog( const char *name ); + void IHideDialog( const char *name ); + + void IAddDlgToList( hsKeyedObject *obj ); + void IRemoveDlgFromList( hsKeyedObject *obj ); + + void IActivateGUI( hsBool activate ); + + hsBool IHandleMouse( EventType event, hsScalar mouseX, hsScalar mouseY, UInt8 modifiers, UInt32 *desiredCursor ); + hsBool IHandleKeyEvt( EventType event, plKeyDef key, UInt8 modifiers ); + hsBool IHandleKeyPress( char key, UInt8 modifiers ); + + hsBool IModalBlocking( void ); + + pfGUIDialogMod *IGetTopModal( void ) const; + + public: + + enum + { + kDlgModRef = 0 + }; + + + pfGameGUIMgr(); + ~pfGameGUIMgr(); + + CLASSNAME_REGISTER( pfGameGUIMgr ); + GETINTERFACE_ANY( pfGameGUIMgr, hsKeyedObject ); + + void Draw( plPipeline *p ); + + hsBool Init( void ); + + virtual hsBool MsgReceive( plMessage* pMsg ); + + void LoadDialog( const char *name, plKey recvrKey=nil, const char *ageName = nil ); // AgeName = nil defaults to "GUI" + void ShowDialog( const char *name ) { IShowDialog(name); } + void HideDialog( const char *name ) { IHideDialog(name); } + void UnloadDialog( const char *name ); + void UnloadDialog( pfGUIDialogMod *dlg ); + + void ShowDialog( pfGUIDialogMod *dlg, bool resetClickables=true ); + void HideDialog( pfGUIDialogMod *dlg ); + + hsBool IsDialogLoaded( const char *name ); + pfGUIDialogMod *GetDialogFromString( const char *name ); + + void SetDialogToNotify(const char *name, plKey recvrKey); + void SetDialogToNotify(pfGUIDialogMod *dlg, plKey recvrKey); + + void SetDefaultCursor(UInt32 defaultCursor) { fDefaultCursor = defaultCursor; } + UInt32 GetDefaultCursor() { return fDefaultCursor; } + void SetCursorOpacity(hsScalar opacity) { fCursorOpacity = opacity; } + hsScalar GetCursorOpacity() { return fCursorOpacity; } + + pfGUIPopUpMenu *FindPopUpMenu( const char *name ); + + std::vector GetDlgRenderMods( void ) const; + hsBool IsModalBlocking( void ) {return IModalBlocking();} + + // Tag ID stuff + pfGUIDialogMod *GetDialogFromTag( UInt32 tagID ); + pfGUIControlMod *GetControlFromTag( pfGUIDialogMod *dlg, UInt32 tagID ); + + static UInt32 GetNumTags( void ); + static pfGUITag *GetTag( UInt32 tagIndex ); + static UInt32 GetHighestTag( void ); + void SetAspectRatio(hsScalar aspectratio); + hsScalar GetAspectRatio() { return fAspectRatio; } + + static pfGameGUIMgr *GetInstance( void ) { return fInstance; } +}; + +#endif //_pfGameGUIMgr_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgrCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgrCreatable.h new file mode 100644 index 00000000..e77e5706 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameGUIMgr/pfGameGUIMgrCreatable.h @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef _pfGameGUIMgrCreatable_inc +#define _pfGameGUIMgrCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "pfGameGUIMgr.h" + +REGISTER_CREATABLE( pfGameGUIMgr ); + +#include "pfGUIDialogMod.h" +#include "pfGUIControlMod.h" +#include "pfGUIButtonMod.h" +#include "pfGUIDraggableMod.h" +#include "pfGUIListBoxMod.h" +#include "pfGUITextBoxMod.h" +#include "pfGUIEditBoxMod.h" +#include "pfGUIUpDownPairMod.h" +#include "pfGUIValueCtrl.h" +#include "pfGUIKnobCtrl.h" +#include "pfGUIDragBarCtrl.h" +#include "pfGUICheckBoxCtrl.h" +#include "pfGUIRadioGroupCtrl.h" +#include "pfGUIDynDisplayCtrl.h" +#include "pfGUIMultiLineEditCtrl.h" +#include "pfGUIProgressCtrl.h" +#include "pfGUIClickMapCtrl.h" +#include "pfGUIPopUpMenu.h" +#include "pfGUIMenuItem.h" + +REGISTER_CREATABLE( pfGUIDialogMod ); +REGISTER_NONCREATABLE( pfGUIControlMod ); +REGISTER_CREATABLE( pfGUIButtonMod ); +REGISTER_CREATABLE( pfGUIDraggableMod ); +REGISTER_CREATABLE( pfGUIListBoxMod ); +REGISTER_CREATABLE( pfGUITextBoxMod ); +REGISTER_CREATABLE( pfGUIEditBoxMod ); +REGISTER_NONCREATABLE( pfGUIValueCtrl ); +REGISTER_CREATABLE( pfGUIUpDownPairMod ); +REGISTER_CREATABLE( pfGUIKnobCtrl ); +REGISTER_CREATABLE( pfGUIDragBarCtrl ); +REGISTER_CREATABLE( pfGUICheckBoxCtrl ); +REGISTER_CREATABLE( pfGUIRadioGroupCtrl ); +REGISTER_CREATABLE( pfGUIDynDisplayCtrl ); +REGISTER_CREATABLE( pfGUIMultiLineEditCtrl ); +REGISTER_CREATABLE( pfGUIProgressCtrl ); +REGISTER_CREATABLE( pfGUIClickMapCtrl ); +REGISTER_CREATABLE( pfGUIPopUpMenu ); +REGISTER_CREATABLE( pfGUIMenuItem ); +REGISTER_CREATABLE( pfGUISkin ); + +#endif // _pfGameGUIMgrCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/BlueSpiral/pfGmBlueSpiral.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/BlueSpiral/pfGmBlueSpiral.cpp new file mode 100644 index 00000000..ab06759f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/BlueSpiral/pfGmBlueSpiral.cpp @@ -0,0 +1,269 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/BlueSpiral/pfGmBlueSpiral.cpp +* +***/ + +#define USES_GAME_BLUESPIRAL +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + +struct IBlueSpiral { + pfGmBlueSpiral * gameCli; + + IBlueSpiral (pfGmBlueSpiral * gameCli); + + // pfGameCli event notification handlers + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + + // BlueSpiral network message handlers + void RecvClothOrder (const Srv2Cli_BlueSpiral_ClothOrder & msg, void * param); + void RecvSuccessfulHit (const Srv2Cli_BlueSpiral_SuccessfulHit & msg, void * param); + void RecvGameWon (const Srv2Cli_BlueSpiral_GameWon & msg, void * param); + void RecvGameOver (const Srv2Cli_BlueSpiral_GameOver & msg, void * param); + void RecvGameStarted (const Srv2Cli_BlueSpiral_GameStarted & msg, void * param); +}; + + +/***************************************************************************** +* +* Factory functions +* +***/ + +//============================================================================ +static pfGameCli * BlueSpiralFactory ( + unsigned gameId, + plKey receiver +) { + return NEWZERO(pfGmBlueSpiral)(gameId, receiver); +} + +//============================================================================ +AUTO_INIT_FUNC(RegisterBlueSpiralFactory) { + + static GameTypeReg reg = { + BlueSpiralFactory, + kGameTypeId_BlueSpiral, + L"BlueSpiral" + }; + + GameMgrRegisterGameType(reg); +} + + +/***************************************************************************** +* +* IBlueSpiral +* +***/ + +//============================================================================ +IBlueSpiral::IBlueSpiral (pfGmBlueSpiral * gameCli) +: gameCli(gameCli) +{ +} + +//============================================================================ +void IBlueSpiral::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IBlueSpiral::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IBlueSpiral::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IBlueSpiral::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IBlueSpiral::RecvClothOrder (const Srv2Cli_BlueSpiral_ClothOrder & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IBlueSpiral::RecvSuccessfulHit (const Srv2Cli_BlueSpiral_SuccessfulHit & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IBlueSpiral::RecvGameWon (const Srv2Cli_BlueSpiral_GameWon & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IBlueSpiral::RecvGameOver (const Srv2Cli_BlueSpiral_GameOver & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IBlueSpiral::RecvGameStarted (const Srv2Cli_BlueSpiral_GameStarted & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + + +/***************************************************************************** +* +* pfGmBlueSpiral +* +***/ + +//============================================================================ +pfGmBlueSpiral::pfGmBlueSpiral ( + unsigned gameId, + plKey receiver +) +: pfGameCli(gameId, receiver) +{ + internal = NEWZERO(IBlueSpiral)(this); +} + +//============================================================================ +pfGmBlueSpiral::~pfGmBlueSpiral () { + + DEL(internal); +} + +//============================================================================ +void pfGmBlueSpiral::Recv (GameMsgHeader * msg, void * param) { + + #define DISPATCH(a) case kSrv2Cli_BlueSpiral_##a: { \ + const Srv2Cli_BlueSpiral_##a & m = *(const Srv2Cli_BlueSpiral_##a *)msg; \ + internal->Recv##a(m, param); \ + } \ + break; // + switch (msg->messageId) { + DISPATCH(ClothOrder); + DISPATCH(SuccessfulHit); + DISPATCH(GameWon); + DISPATCH(GameOver); + DISPATCH(GameStarted); + DEFAULT_FATAL(msg->messageId); + } + #undef DISPATCH +} + +//============================================================================ +void pfGmBlueSpiral::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + + internal->OnPlayerJoined(msg); +} + +//============================================================================ +void pfGmBlueSpiral::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + + internal->OnPlayerLeft(msg); +} + +//============================================================================ +void pfGmBlueSpiral::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + + internal->OnInviteFailed(msg); +} + +//============================================================================ +void pfGmBlueSpiral::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + + internal->OnOwnerChange(msg); +} + +//============================================================================ +void pfGmBlueSpiral::StartGame () { + + Cli2Srv_BlueSpiral_StartGame msg; + msg.messageId = kCli2Srv_BlueSpiral_StartGame; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmBlueSpiral::HitCloth (int clothNum) { + + Cli2Srv_BlueSpiral_HitCloth msg; + msg.messageId = kCli2Srv_BlueSpiral_HitCloth; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.clothNum = clothNum; + + GameMgrSend(&msg); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/BlueSpiral/pfGmBlueSpiral.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/BlueSpiral/pfGmBlueSpiral.h new file mode 100644 index 00000000..7c7b2664 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/BlueSpiral/pfGmBlueSpiral.h @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_BLUESPIRAL_PFGMBLUESPIRAL_H +#error "Header $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/BlueSpiral/pfGmBlueSpiral.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_BLUESPIRAL_PFGMBLUESPIRAL_H + + +/***************************************************************************** +* +* pfGmBlueSpiral interface +* +***/ +class pfGmBlueSpiral : public pfGameCli { + + // Encapsulate all implementation details such as member fields + // in an opaque friend class, in this case that's IBlueSpiral. + friend struct IBlueSpiral; + struct IBlueSpiral * internal; + + //======================================================================== + // Required subclass methods + //-------------------------- + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + //======================================================================== + +public: + #pragma warning(push, 0) + // These macros produce warnings on W4 + CLASSNAME_REGISTER(pfGmBlueSpiral); + GETINTERFACE_ANY(pfGmBlueSpiral, pfGameCli); + #pragma warning(pop) + + pfGmBlueSpiral (unsigned gameId, plKey receiver); + ~pfGmBlueSpiral (); + + //======================================================================== + // Game methods + //------------- + void StartGame (); + void HitCloth (int clothNum); + //======================================================================== +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/ClimbingWall/pfGmClimbingWall.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/ClimbingWall/pfGmClimbingWall.cpp new file mode 100644 index 00000000..fa36a161 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/ClimbingWall/pfGmClimbingWall.cpp @@ -0,0 +1,346 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/ClimbingWall/pfGmClimbingWall.cpp +* +***/ + +#define USES_GAME_CLIMBINGWALL +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + +struct IClimbingWall { + pfGmClimbingWall * gameCli; + + IClimbingWall (pfGmClimbingWall * gameCli); + + // pfGameCli event notification handlers + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + + // ClimbingWall network message handlers + void RecvNumBlockersChanged (const Srv2Cli_ClimbingWall_NumBlockersChanged & msg, void * param); + void RecvReady (const Srv2Cli_ClimbingWall_Ready & msg, void * param); + void RecvBlockersChanged (const Srv2Cli_ClimbingWall_BlockersChanged & msg, void * param); + void RecvPlayerEntered (const Srv2Cli_ClimbingWall_PlayerEntered & msg, void * param); + void RecvSuitMachineLocked (const Srv2Cli_ClimbingWall_SuitMachineLocked & msg, void * param); + void RecvGameOver (const Srv2Cli_ClimbingWall_GameOver & msg, void * param); +}; + + +/***************************************************************************** +* +* Factory functions +* +***/ + +//============================================================================ +static pfGameCli * ClimbingWallFactory ( + unsigned gameId, + plKey receiver +) { + return NEWZERO(pfGmClimbingWall)(gameId, receiver); +} + +//============================================================================ +AUTO_INIT_FUNC(RegisterClimbingWallFactory) { + + static GameTypeReg reg = { + ClimbingWallFactory, + kGameTypeId_ClimbingWall, + L"ClimbingWall" + }; + + GameMgrRegisterGameType(reg); +} + + +/***************************************************************************** +* +* IClimbingWall +* +***/ + +//============================================================================ +IClimbingWall::IClimbingWall (pfGmClimbingWall * gameCli) +: gameCli(gameCli) +{ +} + +//============================================================================ +void IClimbingWall::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IClimbingWall::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IClimbingWall::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IClimbingWall::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IClimbingWall::RecvNumBlockersChanged (const Srv2Cli_ClimbingWall_NumBlockersChanged & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IClimbingWall::RecvReady (const Srv2Cli_ClimbingWall_Ready & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IClimbingWall::RecvBlockersChanged (const Srv2Cli_ClimbingWall_BlockersChanged & msg, void * param) { + ref (param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IClimbingWall::RecvPlayerEntered (const Srv2Cli_ClimbingWall_PlayerEntered & msg, void * param) { + ref (param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IClimbingWall::RecvSuitMachineLocked (const Srv2Cli_ClimbingWall_SuitMachineLocked & msg, void * param) { + ref (param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IClimbingWall::RecvGameOver (const Srv2Cli_ClimbingWall_GameOver & msg, void * param) { + ref (param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + + +/***************************************************************************** +* +* pfGmClimbingWall +* +***/ + +//============================================================================ +pfGmClimbingWall::pfGmClimbingWall ( + unsigned gameId, + plKey receiver +) +: pfGameCli(gameId, receiver) +{ + internal = NEWZERO(IClimbingWall)(this); +} + +//============================================================================ +pfGmClimbingWall::~pfGmClimbingWall () { + + DEL(internal); +} + +//============================================================================ +void pfGmClimbingWall::Recv (GameMsgHeader * msg, void * param) { + + #define DISPATCH(a) case kSrv2Cli_ClimbingWall_##a: { \ + const Srv2Cli_ClimbingWall_##a & m = *(const Srv2Cli_ClimbingWall_##a *)msg; \ + internal->Recv##a(m, param); \ + } \ + break; // + switch (msg->messageId) { + DISPATCH(NumBlockersChanged); + DISPATCH(Ready); + DISPATCH(BlockersChanged); + DISPATCH(PlayerEntered); + DISPATCH(SuitMachineLocked); + DISPATCH(GameOver); + DEFAULT_FATAL(msg->messageId); + } + #undef DISPATCH +} + +//============================================================================ +void pfGmClimbingWall::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + + internal->OnPlayerJoined(msg); +} + +//============================================================================ +void pfGmClimbingWall::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + + internal->OnPlayerLeft(msg); +} + +//============================================================================ +void pfGmClimbingWall::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + + internal->OnInviteFailed(msg); +} + +//============================================================================ +void pfGmClimbingWall::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + + internal->OnOwnerChange(msg); +} + +//============================================================================ +void pfGmClimbingWall::ChangeNumBlockers (int amountToAdjust) { + + Cli2Srv_ClimbingWall_ChangeNumBlockers msg; + msg.messageId = kCli2Srv_ClimbingWall_ChangeNumBlockers; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.amountToAdjust = amountToAdjust; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmClimbingWall::Ready (unsigned readyType, unsigned teamNumber) { + + Cli2Srv_ClimbingWall_Ready msg; + msg.messageId = kCli2Srv_ClimbingWall_Ready; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.readyType = (byte)readyType; + msg.teamNumber = (byte)teamNumber; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmClimbingWall::ChangeBlocker (unsigned teamNumber, unsigned blockerNumber, bool added) { + + Cli2Srv_ClimbingWall_BlockerChanged msg; + msg.messageId = kCli2Srv_ClimbingWall_BlockerChanged; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.teamNumber = (byte)teamNumber; + msg.blockerNumber = (byte)blockerNumber; + msg.added = added; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmClimbingWall::Reset () { + + Cli2Srv_ClimbingWall_Reset msg; + msg.messageId = kCli2Srv_ClimbingWall_Reset; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmClimbingWall::PlayerEntered (unsigned teamNumber) { + + Cli2Srv_ClimbingWall_PlayerEntered msg; + msg.messageId = kCli2Srv_ClimbingWall_PlayerEntered; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.teamNumber = (byte)teamNumber; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmClimbingWall::FinishedGame () { + + Cli2Srv_ClimbingWall_FinishedGame msg; + msg.messageId = kCli2Srv_ClimbingWall_FinishedGame; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmClimbingWall::Panic () { + + Cli2Srv_ClimbingWall_Panic msg; + msg.messageId = kCli2Srv_ClimbingWall_Panic; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + GameMgrSend(&msg); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/ClimbingWall/pfGmClimbingWall.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/ClimbingWall/pfGmClimbingWall.h new file mode 100644 index 00000000..56278431 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/ClimbingWall/pfGmClimbingWall.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/ClimbingWall/pfGmClimbingWall.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_CLIMBINGWALL_PFGMCLIMBINGWALL_H +#error "Header $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/ClimbingWall/pfGmClimbingWall.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_CLIMBINGWALL_PFGMCLIMBINGWALL_H + + +/***************************************************************************** +* +* pfGmClimbingWall interface +* +***/ +class pfGmClimbingWall : public pfGameCli { + + // Encapsulate all implementation details such as member fields + // in an opaque friend class, in this case that's IClimbingWall. + friend struct IClimbingWall; + struct IClimbingWall * internal; + + //======================================================================== + // Required subclass methods + //-------------------------- + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + //======================================================================== + +public: + #pragma warning(push, 0) + // These macros produce warnings on W4 + CLASSNAME_REGISTER(pfGmClimbingWall); + GETINTERFACE_ANY(pfGmClimbingWall, pfGameCli); + #pragma warning(pop) + + pfGmClimbingWall (unsigned gameId, plKey receiver); + ~pfGmClimbingWall (); + + //======================================================================== + // Game methods + //------------- + void ChangeNumBlockers (int amountToAdjust); + void Ready (unsigned readyType, unsigned teamNumber); + void ChangeBlocker (unsigned teamNumber, unsigned blockerNumber, bool added); + void Reset (); + void PlayerEntered (unsigned teamNumber); + void FinishedGame (); + void Panic (); + //======================================================================== +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Heek/pfGmHeek.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Heek/pfGmHeek.cpp new file mode 100644 index 00000000..dfaa8026 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Heek/pfGmHeek.cpp @@ -0,0 +1,341 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.cpp +* +***/ + +#define USES_GAME_HEEK +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + +struct IHeek { + pfGmHeek * gameCli; + + IHeek (pfGmHeek * gameCli); + + // pfGameCli event notification handlers + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + + // Heek network message handlers + void RecvPlayGame (const Srv2Cli_Heek_PlayGame & msg, void * param); + void RecvGoodbye (const Srv2Cli_Heek_Goodbye & msg, void * param); + void RecvWelcome (const Srv2Cli_Heek_Welcome & msg, void * param); + void RecvDrop (const Srv2Cli_Heek_Drop & msg, void * param); + void RecvSetup (const Srv2Cli_Heek_Setup & msg, void * param); + void RecvLightState (const Srv2Cli_Heek_LightState & msg, void * param); + void RecvInterfaceState (const Srv2Cli_Heek_InterfaceState & msg, void * param); + void RecvCountdownState (const Srv2Cli_Heek_CountdownState & msg, void * param); + void RecvWinLose (const Srv2Cli_Heek_WinLose & msg, void * param); + void RecvGameWin (const Srv2Cli_Heek_GameWin & msg, void * param); + void RecvPointUpdate (const Srv2Cli_Heek_PointUpdate & msg, void * param); +}; + + +/***************************************************************************** +* +* Factory functions +* +***/ + +//============================================================================ +static pfGameCli * HeekFactory ( +unsigned gameId, +plKey receiver +) { + return NEWZERO(pfGmHeek)(gameId, receiver); +} + +//============================================================================ +AUTO_INIT_FUNC(RegisterHeek) { + static GameTypeReg reg = { + HeekFactory, + kGameTypeId_Heek, + L"Heek" + }; + + GameMgrRegisterGameType(reg); +} + + +/***************************************************************************** +* +* IHeek +* +***/ + +//============================================================================ +IHeek::IHeek (pfGmHeek * gameCli) +: gameCli(gameCli) +{ +} + +//============================================================================ +void IHeek::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::RecvPlayGame (const Srv2Cli_Heek_PlayGame & msg, void * param) { + ref(param); + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::RecvGoodbye (const Srv2Cli_Heek_Goodbye & msg, void * param) { + ref(param); + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::RecvWelcome (const Srv2Cli_Heek_Welcome & msg, void * param) { + ref(param); + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::RecvDrop (const Srv2Cli_Heek_Drop & msg, void * param) { + ref(param); + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::RecvSetup (const Srv2Cli_Heek_Setup & msg, void * param) { + ref(param); + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::RecvLightState (const Srv2Cli_Heek_LightState & msg, void * param) { + ref(param); + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::RecvInterfaceState (const Srv2Cli_Heek_InterfaceState & msg, void * param) { + ref(param); + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::RecvCountdownState (const Srv2Cli_Heek_CountdownState & msg, void * param) { + ref(param); + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::RecvWinLose (const Srv2Cli_Heek_WinLose & msg, void * param) { + ref(param); + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::RecvGameWin (const Srv2Cli_Heek_GameWin & msg, void * param) { + ref(param); + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IHeek::RecvPointUpdate (const Srv2Cli_Heek_PointUpdate & msg, void * param) { + ref(param); + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + + +/***************************************************************************** +* +* pfGmHeek +* +***/ + +//============================================================================ +pfGmHeek::pfGmHeek ( + unsigned gameId, + plKey receiver +) +: pfGameCli(gameId, receiver) +{ + internal = NEWZERO(IHeek)(this); +} + +//============================================================================ +pfGmHeek::~pfGmHeek () { + DEL(internal); +} + +//============================================================================ +void pfGmHeek::Recv (GameMsgHeader * msg, void * param) { +#define DISPATCH(a) case kSrv2Cli_Heek_##a: { \ + const Srv2Cli_Heek_##a & m = *(const Srv2Cli_Heek_##a *)msg; \ + internal->Recv##a(m, param); \ + } \ + break; + + switch (msg->messageId) { + DISPATCH(PlayGame); + DISPATCH(Goodbye); + DISPATCH(Welcome); + DISPATCH(Drop); + DISPATCH(Setup); + DISPATCH(LightState); + DISPATCH(InterfaceState); + DISPATCH(CountdownState); + DISPATCH(WinLose); + DISPATCH(GameWin); + DISPATCH(PointUpdate); + DEFAULT_FATAL(msg->messageId); + } +#undef DISPATCH +} + +//============================================================================ +void pfGmHeek::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + internal->OnPlayerJoined(msg); +} + +//============================================================================ +void pfGmHeek::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + internal->OnPlayerLeft(msg); +} + +//============================================================================ +void pfGmHeek::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + internal->OnInviteFailed(msg); +} + +//============================================================================ +void pfGmHeek::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + internal->OnOwnerChange(msg); +} + +//============================================================================ +void pfGmHeek::PlayGame (unsigned position, dword points, const wchar name[]) { + Cli2Srv_Heek_PlayGame msg; + msg.messageId = kCli2Srv_Heek_PlayGame; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + msg.position = (byte)position; + msg.points = points; + StrCopy(msg.name, name, arrsize(msg.name)); + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmHeek::LeaveGame () { + Cli2Srv_Heek_LeaveGame msg; + msg.messageId = kCli2Srv_Heek_LeaveGame; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmHeek::Choose (EHeekChoice choice) { + Cli2Srv_Heek_Choose msg; + msg.messageId = kCli2Srv_Heek_Choose; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + msg.choice = (byte)choice; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmHeek::SequenceFinished (EHeekSeqFinished seq) { + Cli2Srv_Heek_SeqFinished msg; + msg.messageId = kCli2Srv_Heek_SeqFinished; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + msg.seqFinished = (byte)seq; + + GameMgrSend(&msg); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Heek/pfGmHeek.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Heek/pfGmHeek.h new file mode 100644 index 00000000..5adaca27 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Heek/pfGmHeek.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Heek/pfGmHeek.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_HEEK_PFGMHEEK_H +#error "Header $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Heek/pfGmHeek.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_HEEK_PFGMHEEK_H + + +/***************************************************************************** +* +* pfGmHeek interface +* +***/ +class pfGmHeek : public pfGameCli { + + // Encapsulate all implementation details such as member fields + // in an opaque friend class, in this case that's IHeek. + friend struct IHeek; + struct IHeek * internal; + + //======================================================================== + // Required subclass methods + //-------------------------- + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + //======================================================================== + +public: +#pragma warning(push, 0) + // These macros produce warnings on W4 + CLASSNAME_REGISTER(pfGmHeek); + GETINTERFACE_ANY(pfGmHeek, pfGameCli); +#pragma warning(pop) + + pfGmHeek (unsigned gameId, plKey receiver); + ~pfGmHeek (); + + //======================================================================== + // Game methods + //------------- + void PlayGame (unsigned position, dword points, const wchar name[]); + void LeaveGame (); + void Choose (EHeekChoice choice); + void SequenceFinished (EHeekSeqFinished seq); + //======================================================================== +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Intern.h new file mode 100644 index 00000000..84a8855d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Intern.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_INTERN_H + + + +/***************************************************************************** +* +* GameMgr +* +***/ + +typedef pfGameCli * (*FGameCliFactory)( + unsigned gameSrvId, + plKey receiver +); + +struct GameTypeReg { + FGameCliFactory create; + Uuid typeId; + const wchar * name; +}; + +void GameMgrRegisterGameType (const GameTypeReg & reg); +void GameMgrSend (GameMsgHeader * msg, void * param = nil); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Marker/pfGmMarker.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Marker/pfGmMarker.cpp new file mode 100644 index 00000000..cd691bd5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Marker/pfGmMarker.cpp @@ -0,0 +1,475 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.cpp +* +***/ + +#define USES_GAME_MARKER +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + +struct IMarker { + pfGmMarker * gameCli; + + IMarker (pfGmMarker * gameCli); + + // pfGameCli event notification handlers + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + + // Marker network message handlers + void RecvTemplateCreated (const Srv2Cli_Marker_TemplateCreated & msg, void * param); + void RecvTeamAssigned (const Srv2Cli_Marker_TeamAssigned & msg, void * param); + void RecvGameType (const Srv2Cli_Marker_GameType & msg, void * param); + void RecvGameStarted (const Srv2Cli_Marker_GameStarted & msg, void * param); + void RecvGamePaused (const Srv2Cli_Marker_GamePaused & msg, void * param); + void RecvGameReset (const Srv2Cli_Marker_GameReset & msg, void * param); + void RecvGameOver (const Srv2Cli_Marker_GameOver & msg, void * param); + void RecvGameNameChanged (const Srv2Cli_Marker_GameNameChanged & msg, void * param); + void RecvTimeLimitChanged (const Srv2Cli_Marker_TimeLimitChanged & msg, void * param); + void RecvGameDeleted (const Srv2Cli_Marker_GameDeleted & msg, void * param); + void RecvMarkerAdded (const Srv2Cli_Marker_MarkerAdded & msg, void * param); + void RecvMarkerDeleted (const Srv2Cli_Marker_MarkerDeleted & msg, void * param); + void RecvMarkerNameChanged (const Srv2Cli_Marker_MarkerNameChanged & msg, void * param); + void RecvMarkerCaptured (const Srv2Cli_Marker_MarkerCaptured & msg, void * param); +}; + + +/***************************************************************************** +* +* Factory functions +* +***/ + +//============================================================================ +static pfGameCli * MarkerFactory ( + unsigned gameId, + plKey receiver +) { + return NEWZERO(pfGmMarker)(gameId, receiver); +} + +//============================================================================ +AUTO_INIT_FUNC(RegisterMarkerFactory) { + + static GameTypeReg reg = { + MarkerFactory, + kGameTypeId_Marker, + L"Marker" + }; + + GameMgrRegisterGameType(reg); +} + + +/***************************************************************************** +* +* IMarker +* +***/ + +//============================================================================ +IMarker::IMarker (pfGmMarker * gameCli) +: gameCli(gameCli) +{ +} + +//============================================================================ +void IMarker::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvTemplateCreated (const Srv2Cli_Marker_TemplateCreated & msg, void * param) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvTeamAssigned (const Srv2Cli_Marker_TeamAssigned & msg, void * param) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvGameType (const Srv2Cli_Marker_GameType & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvGameStarted (const Srv2Cli_Marker_GameStarted & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvGamePaused (const Srv2Cli_Marker_GamePaused & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvGameReset (const Srv2Cli_Marker_GameReset & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvGameOver (const Srv2Cli_Marker_GameOver & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvGameNameChanged (const Srv2Cli_Marker_GameNameChanged & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvTimeLimitChanged (const Srv2Cli_Marker_TimeLimitChanged & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvGameDeleted (const Srv2Cli_Marker_GameDeleted & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); + + if (!msg.failed) + DEL(gameCli); // we're done +} + +//============================================================================ +void IMarker::RecvMarkerAdded (const Srv2Cli_Marker_MarkerAdded & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvMarkerDeleted (const Srv2Cli_Marker_MarkerDeleted & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvMarkerNameChanged (const Srv2Cli_Marker_MarkerNameChanged & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IMarker::RecvMarkerCaptured (const Srv2Cli_Marker_MarkerCaptured & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + + +/***************************************************************************** +* +* pfGmMarker +* +***/ + +//============================================================================ +pfGmMarker::pfGmMarker ( + unsigned gameId, + plKey receiver +) +: pfGameCli(gameId, receiver) +{ + internal = NEWZERO(IMarker)(this); +} + +//============================================================================ +pfGmMarker::~pfGmMarker () { + + DEL(internal); +} + +//============================================================================ +void pfGmMarker::Recv (GameMsgHeader * msg, void * param) { + + #define DISPATCH(a) case kSrv2Cli_Marker_##a: { \ + const Srv2Cli_Marker_##a & m = *(const Srv2Cli_Marker_##a *)msg; \ + internal->Recv##a(m, param); \ + } \ + break; // + switch (msg->messageId) { + DISPATCH(TemplateCreated); + DISPATCH(TeamAssigned); + DISPATCH(GameType); + DISPATCH(GameStarted); + DISPATCH(GamePaused); + DISPATCH(GameReset); + DISPATCH(GameOver); + DISPATCH(GameNameChanged); + DISPATCH(TimeLimitChanged); + DISPATCH(GameDeleted); + DISPATCH(MarkerAdded); + DISPATCH(MarkerDeleted); + DISPATCH(MarkerNameChanged); + DISPATCH(MarkerCaptured); + DEFAULT_FATAL(msg->messageId); + } + #undef DISPATCH +} + +//============================================================================ +void pfGmMarker::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + + internal->OnPlayerJoined(msg); +} + +//============================================================================ +void pfGmMarker::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + + internal->OnPlayerLeft(msg); +} + +//============================================================================ +void pfGmMarker::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + + internal->OnInviteFailed(msg); +} + +//============================================================================ +void pfGmMarker::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + + internal->OnOwnerChange(msg); +} + +//============================================================================ +void pfGmMarker::StartGame () { + + Cli2Srv_Marker_StartGame msg; + msg.messageId = kCli2Srv_Marker_StartGame; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmMarker::PauseGame () { + + Cli2Srv_Marker_StartGame msg; + msg.messageId = kCli2Srv_Marker_PauseGame; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmMarker::ResetGame () { + + Cli2Srv_Marker_StartGame msg; + msg.messageId = kCli2Srv_Marker_ResetGame; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmMarker::ChangeGameName (const wchar name[]) { + + Cli2Srv_Marker_ChangeGameName msg; + msg.messageId = kCli2Srv_Marker_ChangeGameName; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + StrCopy(msg.gameName, name, arrsize(msg.gameName)); + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmMarker::ChangeTimeLimit (unsigned long timeLimit) { + + Cli2Srv_Marker_ChangeTimeLimit msg; + msg.messageId = kCli2Srv_Marker_ChangeTimeLimit; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.timeLimit = timeLimit; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmMarker::DeleteGame () { + + Cli2Srv_Marker_DeleteGame msg; + msg.messageId = kCli2Srv_Marker_DeleteGame; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmMarker::AddMarker (double x, double y, double z, const wchar name[], const wchar age[]) { + + Cli2Srv_Marker_AddMarker msg; + msg.messageId = kCli2Srv_Marker_AddMarker; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.x = x; + msg.y = y; + msg.z = z; + StrCopy(msg.name, name, arrsize(msg.name)); + StrCopy(msg.age, age, arrsize(msg.age)); + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmMarker::DeleteMarker (unsigned long markerID) { + + Cli2Srv_Marker_DeleteMarker msg; + msg.messageId = kCli2Srv_Marker_DeleteMarker; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.markerID = markerID; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmMarker::ChangeMarkerName (unsigned long markerID, const wchar name[]) { + + Cli2Srv_Marker_ChangeMarkerName msg; + msg.messageId = kCli2Srv_Marker_ChangeMarkerName; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.markerID = markerID; + StrCopy(msg.markerName, name, arrsize(msg.markerName)); + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmMarker::CaptureMarker (unsigned long markerID) { + + Cli2Srv_Marker_CaptureMarker msg; + msg.messageId = kCli2Srv_Marker_CaptureMarker; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.markerID = markerID; + + GameMgrSend(&msg); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Marker/pfGmMarker.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Marker/pfGmMarker.h new file mode 100644 index 00000000..914557c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Marker/pfGmMarker.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Marker/pfGmMarker.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_MARKER_PFGMMARKER_H +#error "Header $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Marker/pfGmMarker.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_MARKER_PFGMMARKER_H + + +/***************************************************************************** +* +* pfGmMarker interface +* +***/ +class pfGmMarker : public pfGameCli { + + // Encapsulate all implementation details such as member fields + // in an opaque friend class, in this case that's IMarker. + friend struct IMarker; + struct IMarker * internal; + + //======================================================================== + // Required subclass methods + //-------------------------- + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + //======================================================================== + +public: + #pragma warning(push, 0) + // These macros produce warnings on W4 + CLASSNAME_REGISTER(pfGmMarker); + GETINTERFACE_ANY(pfGmMarker, pfGameCli); + #pragma warning(pop) + + pfGmMarker (unsigned gameId, plKey receiver); + ~pfGmMarker (); + + //======================================================================== + // Game methods + //------------- + void StartGame (); + void PauseGame (); + void ResetGame (); + void ChangeGameName (const wchar name[]); + void ChangeTimeLimit (unsigned long timeLimit); + void DeleteGame (); + void AddMarker (double x, double y, double z, const wchar name[], const wchar age[]); + void DeleteMarker (unsigned long markerID); + void ChangeMarkerName (unsigned long markerID, const wchar name[]); + void CaptureMarker (unsigned long markerID); + //======================================================================== +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Pch.h new file mode 100644 index 00000000..d850c2eb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Pch.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_PCH_H +#error "Header $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_PCH_H + + +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" +#include "../pnAsyncCore/pnAsyncCore.h" +#include "../pnNetCli/pnNetCli.h" +#include "../pnProduct/pnProduct.h" +#include "../pnGameMgr/pnGameMgr.h" +#include "../plNetGameLib/plNetGameLib.h" + +#pragma warning(push, 0) +// These includes produce lots of warnings on W4 +#include "../pnMessage/plMessage.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../pfConsole/pfConsole.h" +#pragma warning(pop) + +#include "pfGameMgr.h" +#include "Intern.h" + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.cpp new file mode 100644 index 00000000..83b14326 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.cpp @@ -0,0 +1,277 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.cpp +* +***/ + +#define USES_GAME_TICTACTOE +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + +struct ITicTacToe { + pfGmTicTacToe * gameCli; + char board[3][3]; + char myself; + char other; + + ITicTacToe (pfGmTicTacToe * gameCli); + + // pfGameCli event notification handlers + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + + // TicTacToe network message handlers + void RecvGameStarted (const Srv2Cli_TTT_GameStarted & msg, void * param); + void RecvGameOver (const Srv2Cli_TTT_GameOver & msg, void * param); + void RecvMoveMade (const Srv2Cli_TTT_MoveMade & msg, void * param); +}; + + +/***************************************************************************** +* +* Factory functions +* +***/ + +//============================================================================ +static pfGameCli * TicTacToeFactory ( + unsigned gameId, + plKey receiver +) { + return NEWZERO(pfGmTicTacToe)(gameId, receiver); +} + +//============================================================================ +AUTO_INIT_FUNC(RegisterTicTacToeFactory) { + + static GameTypeReg reg = { + TicTacToeFactory, + kGameTypeId_TicTacToe, + L"Tic-Tac-Toe" + }; + + GameMgrRegisterGameType(reg); +} + + +/***************************************************************************** +* +* ITicTacToe +* +***/ + +//============================================================================ +ITicTacToe::ITicTacToe (pfGmTicTacToe * gameCli) +: gameCli(gameCli) +{ + // Fill the board with space chars + MemSet(board, ' ', sizeof(board)); +} + +//============================================================================ +void ITicTacToe::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void ITicTacToe::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void ITicTacToe::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void ITicTacToe::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void ITicTacToe::RecvGameStarted (const Srv2Cli_TTT_GameStarted & msg, void * param) { + ref(param); + + // player that goes first is shown as X's. + if (msg.yourTurn) { + myself = 'X'; + other = 'O'; + } + else { + myself = 'O'; + other = 'X'; + } + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void ITicTacToe::RecvGameOver (const Srv2Cli_TTT_GameOver & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); + + DEL(gameCli); // we're done +} + +//============================================================================ +void ITicTacToe::RecvMoveMade (const Srv2Cli_TTT_MoveMade & msg, void * param) { + ref(param); + + // Update the board with the appropriate piece + if (msg.playerId == NetCommGetPlayer()->playerInt) + board[msg.row][msg.col] = myself; + else + board[msg.row][msg.col] = other; + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + + +/***************************************************************************** +* +* pfGmTicTacToe +* +***/ + +//============================================================================ +pfGmTicTacToe::pfGmTicTacToe ( + unsigned gameId, + plKey receiver +) +: pfGameCli(gameId, receiver) +{ + internal = NEWZERO(ITicTacToe)(this); +} + +//============================================================================ +pfGmTicTacToe::~pfGmTicTacToe () { + + DEL(internal); +} + +//============================================================================ +void pfGmTicTacToe::Recv (GameMsgHeader * msg, void * param) { + + #define DISPATCH(a) case kSrv2Cli_TTT_##a: { \ + const Srv2Cli_TTT_##a & m = *(const Srv2Cli_TTT_##a *)msg; \ + internal->Recv##a(m, param); \ + } \ + break; // + switch (msg->messageId) { + DISPATCH(GameStarted); + DISPATCH(GameOver); + DISPATCH(MoveMade); + DEFAULT_FATAL(msg->messageId); + } + #undef DISPATCH +} + +//============================================================================ +void pfGmTicTacToe::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + + internal->OnPlayerJoined(msg); +} + +//============================================================================ +void pfGmTicTacToe::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + + internal->OnPlayerLeft(msg); +} + +//============================================================================ +void pfGmTicTacToe::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + + internal->OnInviteFailed(msg); +} + +//============================================================================ +void pfGmTicTacToe::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + + internal->OnOwnerChange(msg); +} + +//============================================================================ +void pfGmTicTacToe::MakeMove (unsigned row, unsigned col) { + + Cli2Srv_TTT_MakeMove msg; + msg.messageId = kCli2Srv_TTT_MakeMove; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.row = (byte)row; + msg.col = (byte)col; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmTicTacToe::ShowBoard () { + + // Technically, we should stuff the board into a plMessage and + // have our receiver handle how the board is shown, but heck, + // this is just a little demo and not quite worth the effort. + +#if 0 // Max doesn't have the console, and can't link with it anyway, so I'm removing this code since "this is just a little demo and not quite worth the effort" + pfConsole::AddLine ("\n"); + pfConsole::AddLineF("\\i %c | %c | %c", internal->board[0][0], internal->board[0][1], internal->board[0][2]); + pfConsole::AddLine ("\\i---+---+---"); + pfConsole::AddLineF("\\i %c | %c | %c", internal->board[1][0], internal->board[1][1], internal->board[1][2]); + pfConsole::AddLine ("\\i---+---+---"); + pfConsole::AddLineF("\\i %c | %c | %c", internal->board[2][0], internal->board[2][1], internal->board[2][2]); + pfConsole::AddLine ("\n"); +#endif // 0 +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.h new file mode 100644 index 00000000..f0fc4762 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.h @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_TICTACTOE_PFGMTICTACTOE_H +#error "Header $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/TicTacToe/pfGmTicTacToe.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_TICTACTOE_PFGMTICTACTOE_H + + +/***************************************************************************** +* +* pfGmTicTacToe interface +* +***/ +class pfGmTicTacToe : public pfGameCli { + + // Encapsulate all implementation details such as member fields + // in an opaque friend class, in this case that's ITicTacToe. + friend struct ITicTacToe; + struct ITicTacToe * internal; + + //======================================================================== + // Required subclass methods + //-------------------------- + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + //======================================================================== + +public: + #pragma warning(push, 0) + // These macros produce warnings on W4 + CLASSNAME_REGISTER(pfGmTicTacToe); + GETINTERFACE_ANY(pfGmTicTacToe, pfGameCli); + #pragma warning(pop) + + pfGmTicTacToe (unsigned gameId, plKey receiver); + ~pfGmTicTacToe (); + + //======================================================================== + // Game methods + //------------- + void MakeMove (unsigned row, unsigned col); + void ShowBoard (); + //======================================================================== +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/VarSync/pfGmVarSync.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/VarSync/pfGmVarSync.cpp new file mode 100644 index 00000000..9d45499b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/VarSync/pfGmVarSync.cpp @@ -0,0 +1,311 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/VarSync/pfGmVarSync.cpp +* +***/ + +#define USES_GAME_VARSYNC +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + +struct IVarSync { + pfGmVarSync * gameCli; + + IVarSync (pfGmVarSync * gameCli); + + // pfGameCli event notification handlers + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + + // VarSync network message handlers + void RecvStringVarChanged (const Srv2Cli_VarSync_StringVarChanged & msg, void * param); + void RecvNumericVarChanged (const Srv2Cli_VarSync_NumericVarChanged & msg, void * param); + void RecvAllVarsSent (const Srv2Cli_VarSync_AllVarsSent & msg, void * param); + void RecvStringVarCreated (const Srv2Cli_VarSync_StringVarCreated & msg, void * param); + void RecvNumericVarCreated (const Srv2Cli_VarSync_NumericVarCreated & msg, void * param); +}; + + +/***************************************************************************** +* +* Factory functions +* +***/ + +//============================================================================ +static pfGameCli * VarSyncFactory ( + unsigned gameId, + plKey receiver +) { + return NEWZERO(pfGmVarSync)(gameId, receiver); +} + +//============================================================================ +AUTO_INIT_FUNC(RegisterVarSyncFactory) { + + static GameTypeReg reg = { + VarSyncFactory, + kGameTypeId_VarSync, + L"VarSync" + }; + + GameMgrRegisterGameType(reg); +} + + +/***************************************************************************** +* +* IVarSync +* +***/ + +//============================================================================ +IVarSync::IVarSync (pfGmVarSync * gameCli) +: gameCli(gameCli) +{ +} + +//============================================================================ +void IVarSync::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IVarSync::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IVarSync::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IVarSync::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IVarSync::RecvStringVarChanged (const Srv2Cli_VarSync_StringVarChanged & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IVarSync::RecvNumericVarChanged (const Srv2Cli_VarSync_NumericVarChanged & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IVarSync::RecvAllVarsSent (const Srv2Cli_VarSync_AllVarsSent & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IVarSync::RecvStringVarCreated (const Srv2Cli_VarSync_StringVarCreated & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +//============================================================================ +void IVarSync::RecvNumericVarCreated (const Srv2Cli_VarSync_NumericVarCreated & msg, void * param) { + ref(param); + + pfGameCliMsg * gameCliMsg = NEWZERO(pfGameCliMsg); + gameCliMsg->Set(gameCli, msg); + gameCliMsg->Send(gameCli->GetReceiver()); +} + +/***************************************************************************** +* +* pfGmVarSync +* +***/ + +//============================================================================ +pfGmVarSync::pfGmVarSync ( + unsigned gameId, + plKey receiver +) +: pfGameCli(gameId, receiver) +{ + internal = NEWZERO(IVarSync)(this); +} + +//============================================================================ +pfGmVarSync::~pfGmVarSync () { + + DEL(internal); +} + +//============================================================================ +void pfGmVarSync::Recv (GameMsgHeader * msg, void * param) { + + #define DISPATCH(a) case kSrv2Cli_VarSync_##a: { \ + const Srv2Cli_VarSync_##a & m = *(const Srv2Cli_VarSync_##a *)msg; \ + internal->Recv##a(m, param); \ + } \ + break; // + switch (msg->messageId) { + DISPATCH(StringVarChanged); + DISPATCH(NumericVarChanged); + DISPATCH(AllVarsSent); + DISPATCH(StringVarCreated); + DISPATCH(NumericVarCreated); + DEFAULT_FATAL(msg->messageId); + } + #undef DISPATCH +} + +//============================================================================ +void pfGmVarSync::OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) { + + internal->OnPlayerJoined(msg); +} + +//============================================================================ +void pfGmVarSync::OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) { + + internal->OnPlayerLeft(msg); +} + +//============================================================================ +void pfGmVarSync::OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) { + + internal->OnInviteFailed(msg); +} + +//============================================================================ +void pfGmVarSync::OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) { + + internal->OnOwnerChange(msg); +} + +//============================================================================ +void pfGmVarSync::SetStringVar (unsigned long id, const wchar* val) { + + Cli2Srv_VarSync_SetStringVar msg; + msg.messageId = kCli2Srv_VarSync_SetStringVar; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.varID = id; + StrCopy(msg.varValue, val, arrsize(msg.varValue)); + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmVarSync::SetNumericVar (unsigned long id, double val) { + + Cli2Srv_VarSync_SetNumericVar msg; + msg.messageId = kCli2Srv_VarSync_SetNumericVar; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + msg.varID = id; + msg.varValue = val; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmVarSync::RequestAllVars () { + + Cli2Srv_VarSync_RequestAllVars msg; + msg.messageId = kCli2Srv_VarSync_RequestAllVars; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmVarSync::CreateStringVar (const wchar* name, const wchar* val) { + + Cli2Srv_VarSync_CreateStringVar msg; + msg.messageId = kCli2Srv_VarSync_CreateStringVar; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + StrCopy(msg.varName, name, arrsize(msg.varName)); + StrCopy(msg.varValue, val, arrsize(msg.varValue)); + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGmVarSync::CreateNumericVar (const wchar* name, double val) { + + Cli2Srv_VarSync_CreateNumericVar msg; + msg.messageId = kCli2Srv_VarSync_CreateNumericVar; + msg.messageBytes = sizeof(msg); + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.transId = 0; + StrCopy(msg.varName, name, arrsize(msg.varName)); + msg.varValue = val; + + GameMgrSend(&msg); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/VarSync/pfGmVarSync.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/VarSync/pfGmVarSync.h new file mode 100644 index 00000000..c79ad50f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/VarSync/pfGmVarSync.h @@ -0,0 +1,80 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/VarSync/pfGmVarSync.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_VARSYNC_PFGMVARSYNC_H +#error "Header $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/VarSync/pfGmVarSync.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_VARSYNC_PFGMVARSYNC_H + + +/***************************************************************************** +* +* pfGmVarSync interface +* +***/ +class pfGmVarSync : public pfGameCli { + + // Encapsulate all implementation details such as member fields + // in an opaque friend class, in this case that's IVarSync. + friend struct IVarSync; + struct IVarSync * internal; + + //======================================================================== + // Required subclass methods + //-------------------------- + void Recv (GameMsgHeader * msg, void * param); + void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg); + void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg); + void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg); + void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg); + //======================================================================== + +public: + #pragma warning(push, 0) + // These macros produce warnings on W4 + CLASSNAME_REGISTER(pfGmVarSync); + GETINTERFACE_ANY(pfGmVarSync, pfGameCli); + #pragma warning(pop) + + pfGmVarSync (unsigned gameId, plKey receiver); + ~pfGmVarSync (); + + //======================================================================== + // Game methods + //------------- + void SetStringVar (unsigned long id, const wchar* val); + void SetNumericVar (unsigned long id, double val); + void RequestAllVars (); + void CreateStringVar (const wchar* name, const wchar* val); + void CreateNumericVar (const wchar* name, double val); + //======================================================================== +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgr.cpp new file mode 100644 index 00000000..0cc6101c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgr.cpp @@ -0,0 +1,657 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgr.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + +//============================================================================ +// pfGameCli factory +//============================================================================ +struct Factory : THashKeyVal { + + HASHLINK(Factory) link; + const GameTypeReg & reg; + + Factory (const GameTypeReg & reg); + operator= (const Factory &); // not impl +}; + +//============================================================================ +// pfGameCli internal state +//============================================================================ +struct IGameCli : THashKeyVal { + + HASHLINK(IGameCli) link; + pfGameCli * gameCli; + Factory * factory; + plKey receiver; + unsigned playerCount; + + IGameCli ( + pfGameCli * gameCli, + unsigned gameId, + plKey receiver + ); + + void Recv (GameMsgHeader * msg, void * param); + void RecvPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg, void * param); + void RecvPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg, void * param); + void RecvInviteFailed (const Srv2Cli_Game_InviteFailed & msg, void * param); + void RecvOwnerChange (const Srv2Cli_Game_OwnerChange & msg, void * param); +}; + +//============================================================================ +// Transaction states +//============================================================================ +struct TransState : THashKeyVal { + + HASHLINK(TransState) link; + void * param; + TransState (unsigned transId, void * param); +}; +struct JoinTransState { + + plKey receiver; + JoinTransState (plKey receiver) + : receiver(receiver) + { } +}; + +//============================================================================ +// IGameMgr +//============================================================================ +struct IGameMgr { + + pfGameCli * CreateGameCli (const Uuid & gameTypeId, unsigned gameId, plKey receiver); + + void Recv (GameMsgHeader * msg); + void RecvGameInstance (const Srv2Cli_GameMgr_GameInstance & msg, void * param); + void RecvInviteReceived (const Srv2Cli_GameMgr_InviteReceived & msg, void * param); + void RecvInviteRevoked (const Srv2Cli_GameMgr_InviteRevoked & msg, void * param); + + static void StaticRecv (GameMsgHeader * msg); +}; + + +/***************************************************************************** +* +* Local data +* +***/ + +static HASHTABLEDECL(TransState, THashKeyVal, link) s_trans; +static HASHTABLEDECL(IGameCli, THashKeyVal, link) s_games; +static HASHTABLEDECL(Factory, THashKeyVal, link) s_factories; + +static long s_transId; +static ARRAYOBJ(plKey) s_receivers; + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static void ShutdownFactories () { + + while (Factory * factory = s_factories.Head()) + DEL(factory); +} + +//============================================================================ +AUTO_INIT_FUNC (SetGameMgrMsgHandler) { + + NetCliGameSetRecvGameMgrMsgHandler(IGameMgr::StaticRecv); + atexit(ShutdownFactories); +} + +//============================================================================ +static inline unsigned INextTransId () { + + unsigned transId = AtomicAdd(&s_transId, 1); + while (!transId) + transId = AtomicAdd(&s_transId, 1); + return transId; +} + + +/***************************************************************************** +* +* IGameMgr +* +***/ + +//============================================================================ +pfGameCli * IGameMgr::CreateGameCli (const Uuid & gameTypeId, unsigned gameId, plKey receiver) { + + if (Factory * factory = s_factories.Find(gameTypeId)) { + pfGameCli * gameCli = factory->reg.create(gameId, receiver); + gameCli->internal->factory = factory; + return gameCli; + } + + return nil; +} + +//============================================================================ +void IGameMgr::RecvGameInstance (const Srv2Cli_GameMgr_GameInstance & msg, void * param) { + + JoinTransState * state = (JoinTransState *)param; + + pfGameCli * cli = nil; + IGameCli * internal = nil; + if (msg.result == kGameJoinSuccess) { + if (nil == (internal = s_games.Find(msg.newGameId))) + cli = CreateGameCli(msg.gameTypeId, msg.newGameId, state->receiver); + else + cli = internal->gameCli; + } + + DEL(state); +} + +//============================================================================ +void IGameMgr::RecvInviteReceived (const Srv2Cli_GameMgr_InviteReceived & msg, void * param) { + ref(param); + + pfGameMgrMsg * gameMgrMsg = NEWZERO(pfGameMgrMsg); + gameMgrMsg->Set(msg); + for (unsigned i = 0; i < s_receivers.Count(); ++i) + gameMgrMsg->AddReceiver(s_receivers[i]); + gameMgrMsg->Send(); +} + +//============================================================================ +void IGameMgr::RecvInviteRevoked (const Srv2Cli_GameMgr_InviteRevoked & msg, void * param) { + ref(param); + + pfGameMgrMsg * gameMgrMsg = NEWZERO(pfGameMgrMsg); + gameMgrMsg->Set(msg); + for (unsigned i = 0; i < s_receivers.Count(); ++i) + gameMgrMsg->AddReceiver(s_receivers[i]); + gameMgrMsg->Send(); +} + +//============================================================================ +void IGameMgr::Recv (GameMsgHeader * msg) { + + // Look for transaction state associated with this message + void * param; + if (TransState * trans = s_trans.Find(msg->transId)) { + param = trans->param; + DEL(trans); + } + else { + param = nil; + } + + // If the message has a receiver gameId specified, then + // hand it off to that game client for dispatch. + if (unsigned gameId = msg->recvGameId) { + if (IGameCli * node = s_games.Find(gameId)) { + node->Recv(msg, param); + } + return; + } + + // The message was meant for the game manager (that's us); dispatch it. + #define DISPATCH(a) case kSrv2Cli_GameMgr_##a: { \ + const Srv2Cli_GameMgr_##a & m = *(const Srv2Cli_GameMgr_##a *)msg; \ + Recv##a(m, param); \ + } \ + break; // + switch (msg->messageId) { + DISPATCH(GameInstance); + DISPATCH(InviteReceived); + DISPATCH(InviteRevoked); + DEFAULT_FATAL(msg->messageId); + } + #undef DISPATCH +} + +//============================================================================ +void IGameMgr::StaticRecv (GameMsgHeader * msg) { + + pfGameMgr::GetInstance()->internal->Recv(msg); +} + + +/***************************************************************************** +* +* pfGameMgrMsg +* +***/ + +//============================================================================ +pfGameMgrMsg::pfGameMgrMsg () { + + netMsg = nil; +} + +//============================================================================ +pfGameMgrMsg::~pfGameMgrMsg () { + + FREE(netMsg); +} + +//============================================================================ +void pfGameMgrMsg::Set (const GameMsgHeader & msg) { + + netMsg = (GameMsgHeader *)ALLOC(msg.messageBytes); + MemCopy(netMsg, &msg, msg.messageBytes); +} + + +/***************************************************************************** +* +* pfGameCliMsg +* +***/ + +//============================================================================ +pfGameCliMsg::pfGameCliMsg () { + + gameCli = nil; + netMsg = nil; +} + +//============================================================================ +pfGameCliMsg::~pfGameCliMsg () { + + FREE(netMsg); +} + +//============================================================================ +void pfGameCliMsg::Set (pfGameCli * cli, const GameMsgHeader & msg) { + + netMsg = (GameMsgHeader *)ALLOC(msg.messageBytes); + MemCopy(netMsg, &msg, msg.messageBytes); + gameCli = cli; +} + +/***************************************************************************** +* +* pfGameMgr +* +***/ + +//============================================================================ +pfGameMgr::pfGameMgr () { +} + +//============================================================================ +pfGameMgr * pfGameMgr::GetInstance () { + + static pfGameMgr s_instance; + return &s_instance; +} + +//============================================================================ +void pfGameMgr::GetGameIds (ARRAY(unsigned) * arr) const { + + for (IGameCli * node = s_games.Head(); node; node = s_games.Next(node)) + arr->Add(node->GetValue()); +} + +//============================================================================ +pfGameCli * pfGameMgr::GetGameCli (unsigned gameId) const { + + if (IGameCli * node = s_games.Find(gameId)) + return node->gameCli; + return nil; +} + +//============================================================================ +const wchar * pfGameMgr::GetGameNameByTypeId (const Uuid & gameTypeId) const { + + if (Factory * factory = s_factories.Find(gameTypeId)) + return factory->reg.name; + return nil; +} + +//============================================================================ +void pfGameMgr::RemoveReceiver (plKey receiver) { + + for (unsigned i = 0; i < s_receivers.Count(); ++i) { + if (s_receivers[i] == receiver) { + s_receivers.DeleteUnordered(i); + break; + } + } +} + +//============================================================================ +void pfGameMgr::AddReceiver (plKey receiver) { + + RemoveReceiver(receiver); + s_receivers.Add(receiver); +} + +//============================================================================ +void pfGameMgr::JoinGame ( + plKey receiver, + unsigned gameId +) { + Cli2Srv_GameMgr_JoinGame msg; + ZERO(msg); + + msg.messageId = kCli2Srv_GameMgr_JoinGame; + msg.recvGameId = 0; // send to GameMgr on server + msg.newGameId = gameId; // the GameSrv we wish to join + + // Don't send "common game" message fields + unsigned msgBytes + = sizeof(msg) + - sizeof(msg.gameTypeId) + - sizeof(msg.createDataBytes) + - sizeof(msg.createData); + + msg.messageBytes = msgBytes; + + GameMgrSend(&msg, NEWZERO(JoinTransState)(receiver)); +} + +//============================================================================ +void pfGameMgr::CreateGame ( + plKey receiver, + const Uuid & gameTypeId, + unsigned createOptions, + unsigned initBytes, + const void * initData +) { + Cli2Srv_GameMgr_CreateGame * msg; + + unsigned msgBytes + = sizeof(*msg) + - sizeof(msg->createData) + + initBytes; + + msg = (Cli2Srv_GameMgr_CreateGame *)_alloca(msgBytes); + + msg->messageId = kCli2Srv_GameMgr_CreateGame; + msg->recvGameId = 0; // send to GameMgr on server + msg->gameTypeId = gameTypeId; // The type of game we wish to create + msg->createOptions = createOptions; + msg->messageBytes = msgBytes; + msg->createDataBytes = initBytes; + MemCopy(msg->createData, initData, initBytes); + + GameMgrSend(msg, NEWZERO(JoinTransState)(receiver)); +} + +//============================================================================ +void pfGameMgr::JoinCommonGame ( + plKey receiver, + const Uuid & gameTypeId, + unsigned gameNumber, + unsigned initBytes, + const void * initData +) { + Cli2Srv_GameMgr_JoinGame * msg; + + unsigned msgBytes + = sizeof(*msg) + - sizeof(msg->createData) + + initBytes; + + msg = (Cli2Srv_GameMgr_JoinGame *)_alloca(msgBytes); + + msg->messageId = kCli2Srv_GameMgr_JoinGame; + msg->recvGameId = 0; // send to GameMgr on server + msg->gameTypeId = gameTypeId; // the type of common game we with to join + msg->newGameId = gameNumber; // the "table number" of th common game we wish to join + msg->createOptions = kGameJoinCommon; + msg->messageBytes = msgBytes; + msg->createDataBytes = initBytes; + MemCopy(msg->createData, initData, initBytes); + + GameMgrSend(msg, NEWZERO(JoinTransState)(receiver)); +} + + +/***************************************************************************** +* +* pfGameCli +* +***/ + +//============================================================================ +pfGameCli::pfGameCli ( + unsigned gameId, + plKey receiver +) { + internal = NEWZERO(IGameCli)(this, gameId, receiver); +} + +//============================================================================ +pfGameCli::~pfGameCli () { + + DEL(internal); +} + +//============================================================================ +unsigned pfGameCli::GetGameId () const { + + return internal->GetValue(); +} + +//============================================================================ +const Uuid & pfGameCli::GetGameTypeId () const { + + return internal->factory->GetValue(); +} + +//============================================================================ +const wchar * pfGameCli::GetName () const { + + return internal->factory->reg.name; +} + +//============================================================================ +plKey pfGameCli::GetReceiver () const { + + return internal->receiver; +} + +//============================================================================ +unsigned pfGameCli::GetPlayerCount () const { + + return internal->playerCount; +} + +//============================================================================ +void pfGameCli::InvitePlayer (unsigned playerId) { + + Cli2Srv_Game_Invite msg; + msg.messageId = kCli2Srv_Game_Invite; + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.playerId = playerId; + msg.messageBytes = sizeof(msg); + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGameCli::UninvitePlayer (unsigned playerId) { + + Cli2Srv_Game_Uninvite msg; + msg.messageId = kCli2Srv_Game_Uninvite; + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.playerId = playerId; + msg.messageBytes = sizeof(msg); + + GameMgrSend(&msg); +} + +//============================================================================ +void pfGameCli::LeaveGame () { + + Cli2Srv_Game_LeaveGame msg; + msg.messageId = kCli2Srv_Game_LeaveGame; + msg.recvGameId = GetGameId(); // send to GameSrv on server + msg.messageBytes = sizeof(msg); + + GameMgrSend(&msg); +} + + +/***************************************************************************** +* +* IGameCli +* +***/ + +//============================================================================ +IGameCli::IGameCli ( + pfGameCli * gameCli, + unsigned gameId, + plKey receiver +) : THashKeyVal(gameId) +, gameCli(gameCli) +, receiver(receiver) +{ + s_games.Add(this); +} + +//============================================================================ +void IGameCli::Recv (GameMsgHeader * msg, void * param) { + + #define DISPATCH(a) case kSrv2Cli_Game_##a: { \ + const Srv2Cli_Game_##a & m = *(const Srv2Cli_Game_##a *)msg; \ + Recv##a(m, param); \ + } \ + break; + switch (msg->messageId) { + DISPATCH(PlayerJoined); + DISPATCH(PlayerLeft); + DISPATCH(InviteFailed); + DISPATCH(OwnerChange); + default: + gameCli->Recv(msg, param); + } + #undef DISPATCH +} + +//============================================================================ +void IGameCli::RecvPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg, void * param) { + ref(param); + + ++playerCount; + gameCli->OnPlayerJoined(msg); +} + +//============================================================================ +void IGameCli::RecvPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg, void * param) { + ref(param); + + --playerCount; + gameCli->OnPlayerLeft(msg); +} + +//============================================================================ +void IGameCli::RecvInviteFailed (const Srv2Cli_Game_InviteFailed & msg, void * param) { + ref(param); + + gameCli->OnInviteFailed(msg); +} + +//============================================================================ +void IGameCli::RecvOwnerChange (const Srv2Cli_Game_OwnerChange & msg, void * param) { + ref(param); + + gameCli->OnOwnerChange(msg); +} + + +/***************************************************************************** +* +* Factory +* +***/ + +//============================================================================ +Factory::Factory (const GameTypeReg & reg) +: reg(reg) +, THashKeyVal(reg.typeId) +{ + s_factories.Add(this); +} + + +/***************************************************************************** +* +* TransState +* +***/ + +//============================================================================ +TransState::TransState (unsigned transId, void * param) +: THashKeyVal(transId) +, param(param) +{ + s_trans.Add(this); +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void GameMgrRegisterGameType (const GameTypeReg & reg) { + + (void)NEWZERO(Factory)(reg); +} + +//============================================================================ +void GameMgrSend (GameMsgHeader * msg, void * param) { + + if (param) { + msg->transId = INextTransId(); + (void)NEW(TransState)(msg->transId, param); + } + else { + msg->transId = 0; + } + + NetCliGameSendGameMgrMsg(msg); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgr.h new file mode 100644 index 00000000..62ba72a7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgr.h @@ -0,0 +1,256 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgr.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_PFGAMEMGR_H +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_PFGAMEMGR_H + + +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" +#include "../pnAsyncCore/pnAsyncCore.h" +#include "../pnNetCli/pnNetCli.h" +#include "../pnProduct/pnProduct.h" +#include "../plNetGameLib/plNetGameLib.h" + +#pragma warning(push, 0) +// These includes produce lots of warnings on W4 +#include "../pnMessage/plMessage.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#pragma warning(pop) + + +//============================================================================ +// Forward declarations +//============================================================================ +class pfGameCli; + + +//============================================================================ +// pfGameMgrMsg +//------------- +// Notifications received from SrvGameMgr are sent to pfGameMgr's receivers in +// the form of a pfGameMgrMsg. +//============================================================================ +class pfGameMgrMsg : public plMessage { +public: + #pragma warning(push, 0) + // These macros produce warnings on W4 + CLASSNAME_REGISTER(pfGameMgrMsg); + GETINTERFACE_ANY(pfGameMgrMsg, plMessage); + #pragma warning(pop) + + void Read(hsStream *, hsResMgr *) { FATAL("not impl"); } + void Write(hsStream *, hsResMgr *) { FATAL("not impl"); } + + pfGameMgrMsg (); + ~pfGameMgrMsg (); + + void Set (const GameMsgHeader & msg); + + GameMsgHeader * netMsg; +}; + + +//============================================================================ +// pfGameCliMsg +//------------- +// Notifications received by a pfGameCli are sent to its receiver in the form +// of a pfGameCliMsg. +//============================================================================ +class pfGameCliMsg : public plMessage { +public: + #pragma warning(push, 0) + // These macros produce warnings on W4 + CLASSNAME_REGISTER(pfGameCliMsg); + GETINTERFACE_ANY(pfGameCliMsg, plMessage); + #pragma warning(pop) + + pfGameCliMsg (); + ~pfGameCliMsg (); + + void Read(hsStream *, hsResMgr *) { FATAL("not impl"); } + void Write(hsStream *, hsResMgr *) { FATAL("not impl"); } + + void Set (pfGameCli * cli, const GameMsgHeader & msg); + + pfGameCli * gameCli; + GameMsgHeader * netMsg; +}; + + +//============================================================================ +// pfGameMgr singleton interface +//------------------------------ +// pfGameMgr is the client-side proxy of the age server's SrvGameMgr. It sends +// client requests to the SrvGameMgr for processing, and executes SrvGameMgr +// commands locally to create pfGameCli objects, notify player of received +// game invites, etc. +//============================================================================ +class pfGameMgr { + friend struct IGameMgr; + struct IGameMgr * internal; + + pfGameMgr (); + +public: + static pfGameMgr * GetInstance (); + + //======================================================================== + // Receiver list + //-------------- + // When notificatons are received from SrvGameMgr, they are dispatched + // as pfGameMgrMsgs to the receiver list maintained by these functions. + void AddReceiver (plKey receiver); + void RemoveReceiver (plKey receiver); + //======================================================================== + + //======================================================================== + // GameMgr properties + //------------------- + // Get a list of ids of games to which player is joined + void GetGameIds (ARRAY(unsigned) * arr) const; + // Return interface to the specified game + pfGameCli * GetGameCli (unsigned gameId) const; + // Get the name of a game by its typeid + const wchar * GetGameNameByTypeId (const Uuid & gameTypeId) const; + //======================================================================== + + //======================================================================== + // pfGameCli creation + //------------------- + // Join an existing game + void JoinGame ( + plKey receiver, // Receiver of pfGameCliMsgs for this game + unsigned gameId // id of the game to join + ); + // Create a new game + void CreateGame ( + plKey receiver, // Receiver of pfGameCliMsgs for this game + const Uuid & gameTypeId, // typeid of game to create + unsigned createOptions, // Game create options from pnGameMgr.h + unsigned initBytes, // Game-specific initialization data + const void * initData + ); + // Join or create the specified common game + void JoinCommonGame ( + plKey receiver, // Receiver of pfGameCliMsgs for this game + const Uuid & gameTypeId, // typeid of common game to create/join + unsigned gameNumber, // "table number" of common game to create/join + // In case the common game needs to + // be created on the server, these + // are its creation parameters: + unsigned initBytes, // Game-specific initialization data + const void * initData + ); + //======================================================================== + + //======================================================================== + // @@@: FUTURE WORK + //----------------- + // Fetch the list of games registered with SrvGameMgr's matchmaking service. + // void RequestPublishedGameList (); + //======================================================================== +}; + +//============================================================================ +// pfGameCli interface +//============================================================================ +class pfGameCli : public plCreatable { + friend struct IGameMgr; + friend struct IGameCli; + struct IGameCli * internal; + + //======================================================================== + // sub-classes must implement these + virtual void Recv (GameMsgHeader * msg, void * param) = 0; + virtual void OnPlayerJoined (const Srv2Cli_Game_PlayerJoined & msg) = 0; + virtual void OnPlayerLeft (const Srv2Cli_Game_PlayerLeft & msg) = 0; + virtual void OnInviteFailed (const Srv2Cli_Game_InviteFailed & msg) = 0; + virtual void OnOwnerChange (const Srv2Cli_Game_OwnerChange & msg) = 0; + //======================================================================== + +public: + #pragma warning(push, 0) + // These macros produce warnings on W4 + CLASSNAME_REGISTER(pfGameCli); + GETINTERFACE_ANY(pfGameCli, plCreatable); + #pragma warning(pop) + + pfGameCli (unsigned gameId, plKey receiver); + ~pfGameCli (); + + //======================================================================== + // Game client properties + //----------------------- + unsigned GetGameId () const; + const Uuid & GetGameTypeId () const; + const wchar * GetName () const; + plKey GetReceiver () const; + unsigned GetPlayerCount () const; + //======================================================================== + + //======================================================================== + // Player invitation management + //----------------------------- + void InvitePlayer (unsigned playerId); + void UninvitePlayer (unsigned playerId); + //======================================================================== + + //======================================================================== + // Game methods + //------------- + void LeaveGame (); + //======================================================================== + + //======================================================================== + // @@@: FUTURE WORK + //----------------- + // "Publish" this game, adding it to the age's the matchmaking service. + // void PublishGame (const wchar desc[]); + // void UnpublishGame (); + //======================================================================== +}; + + +/***************************************************************************** +* +* Games +* +***/ + +#include "TicTacToe/pfGmTicTacToe.h" +#include "Heek/pfGmHeek.h" +#include "Marker/pfGmMarker.h" +#include "BlueSpiral/pfGmBlueSpiral.h" +#include "ClimbingWall/pfGmClimbingWall.h" +#include "VarSync/pfGmVarSync.h" + +#endif // PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_PFGAMEMGR_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgrCreatables.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgrCreatables.h new file mode 100644 index 00000000..aec6d619 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgrCreatables.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameMgr/pfGameMgrCreatables.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_PFGAMEMGRCREATABLES_H +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_PFGAMEMGRCREATABLES_H + + +#include "pfGameMgr.h" + +REGISTER_CREATABLE(pfGameMgrMsg); +REGISTER_CREATABLE(pfGameCliMsg); + +REGISTER_NONCREATABLE(pfGameCli); +REGISTER_NONCREATABLE(pfGmTicTacToe); +REGISTER_NONCREATABLE(pfGmHeek); +REGISTER_NONCREATABLE(pfGmMarker); +REGISTER_NONCREATABLE(pfGmBlueSpiral); +REGISTER_NONCREATABLE(pfGmClimbingWall); +REGISTER_NONCREATABLE(pfGmVarSync); + +#endif // PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMEMGR_PFGAMEMGRCREATABLES_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.cpp new file mode 100644 index 00000000..171e949c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.cpp @@ -0,0 +1,458 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pfGameScoreMgr.h" + +#include "../pnUtils/pnUtils.h" +#include "../plNetGameLib/plNetGameLib.h" +#include "../pnNetProtocol/pnNetProtocol.h" + +//============================================================================ +pfGameScore::pfGameScore() +{ +} + +pfGameScore::~pfGameScore() +{ + pfGameScoreMgr::GetInstance()->RemoveCachedScore(scoreId); +} + +void pfGameScore::Init( + unsigned sid, + unsigned oid, + UInt32 createTime, + const char gname[], + unsigned gType, + int val +) { + scoreId = sid; + ownerId = oid; + createdTime = createTime; + gameType = gType; + value = val; + + StrCopy(gameName, gname, arrsize(gameName)); + pfGameScoreMgr::GetInstance()->AddCachedScore(this); +} + +void pfGameScore::CopyFrom( + const pfGameScore* score +) { + scoreId = score->scoreId; + ownerId = score->ownerId; + createdTime = score->createdTime; + gameType = score->gameType; + value = score->value; + + StrCopy(gameName, score->gameName, arrsize(gameName)); +} + +//============================================================================ +pfGameScoreMgr::pfGameScoreMgr() +{ +} + +pfGameScoreMgr* pfGameScoreMgr::GetInstance() +{ + static pfGameScoreMgr s_instance; + return &s_instance; +} + +void pfGameScoreMgr::AddCachedScore(pfGameScore * score) +{ + GameScoreLink * scoreLink = fScores.Find(score->scoreId); + if (scoreLink == nil) + { + GameScoreLink * link = TRACKED_NEW GameScoreLink(score); + fScores.Add(link); + } + else + scoreLink->score->CopyFrom(score); +} + +void pfGameScoreMgr::RemoveCachedScore(unsigned scoreId) +{ + if (GameScoreLink * link = fScores.Find(scoreId)) + { + DEL(link); + } +} + +//============================================================================ +struct NetWaitOp +{ + ENetError result; + bool complete; +}; + +static void WaitOpCallback( + ENetError result, + void * param +) { + NetWaitOp * op = (NetWaitOp *)param; + + op->result = result; + op->complete = true; +} + +//============================================================================ +// CreateScore +//============================================================================ +struct CreateScoreOp : NetWaitOp +{ + pfGameScore * score; +}; + +static void CreateScoreCallback( + ENetError result, + void * param, + unsigned scoreId, + UInt32 createdTime, + unsigned ownerId, + const char* gameName, + unsigned gameType, + int value +) { + CreateScoreOp * op = (CreateScoreOp*)param; + op->result = result; + + if (IS_NET_SUCCESS(result)) { + op->score->Init( + scoreId, + ownerId, + createdTime, + gameName, + gameType, + value + ); + } + else + op->score->scoreId = 0; + + op->complete = true; +} + +ENetError pfGameScoreMgr::CreateScore( + unsigned ownerId, + const char* gameName, + unsigned gameType, + int value, + pfGameScore& score +) { + CreateScoreOp param; + MemZero(¶m, sizeof(CreateScoreOp)); + param.score = &score; + + NetCliAuthScoreCreate( + ownerId, + gameName, + gameType, + value, + CreateScoreCallback, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + AsyncSleep(10); + } + + return param.result; +} + +//============================================================================ +// DeleteScore +//============================================================================ +ENetError pfGameScoreMgr::DeleteScore( + unsigned scoreId +) { + NetWaitOp param; + MemZero(¶m, sizeof(NetWaitOp)); + + NetCliAuthScoreDelete( + scoreId, + WaitOpCallback, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + AsyncSleep(10); + } + + return param.result; +} + +//============================================================================ +// GetScores +//============================================================================ +struct GetScoresOp : NetWaitOp +{ + pfGameScore*** scores; + int* scoreCount; +}; + +static void GetScoresCallback( + ENetError result, + void * param, + const NetGameScore scores[], + unsigned scoreCount +) { + GetScoresOp * op = (GetScoresOp*)param; + op->result = result; + + if (IS_NET_SUCCESS(result)) { + *(op->scores) = TRACKED_NEW pfGameScore*[scoreCount]; + *(op->scoreCount) = scoreCount; + + for (int i = 0; i < scoreCount; ++i) { + pfGameScore* score = TRACKED_NEW pfGameScore(); + score->IncRef(); + + char tempGameName[kMaxGameScoreNameLength]; + StrToAnsi(tempGameName, scores[i].gameName, arrsize(tempGameName)); + + score->Init( + scores[i].scoreId, + scores[i].ownerId, + scores[i].createdTime, + tempGameName, + scores[i].gameType, + scores[i].value + ); + + (*op->scores)[i] = score; + } + } + else { + *(op->scores) = nil; + op->scoreCount = 0; + } + + op->complete = true; +} + +ENetError pfGameScoreMgr::GetScoresIncRef( + unsigned ownerId, + const char* gameName, + pfGameScore**& outScoreList, + int& outScoreListCount +) { + GetScoresOp param; + MemZero(¶m, sizeof(GetScoresOp)); + param.scores = &outScoreList; + param.scoreCount = &outScoreListCount; + + NetCliAuthScoreGetScores( + ownerId, + gameName, + GetScoresCallback, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + AsyncSleep(10); + } + + return param.result; +} + +//============================================================================ +// AddPoints +//============================================================================ +ENetError pfGameScoreMgr::AddPoints( + unsigned scoreId, + int numPoints +) { + NetWaitOp param; + MemZero(¶m, sizeof(NetWaitOp)); + + NetCliAuthScoreAddPoints( + scoreId, + numPoints, + WaitOpCallback, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + AsyncSleep(10); + } + + if (IS_NET_SUCCESS(param.result)) { + if (GameScoreLink * link = fScores.Find(scoreId)) { + link->score->value += numPoints; + } + } + + return param.result; +} + +//============================================================================ +// TransferPoints +//============================================================================ +ENetError pfGameScoreMgr::TransferPoints( + unsigned srcScoreId, + unsigned destScoreId, + int numPoints +) { + NetWaitOp param; + MemZero(¶m, sizeof(NetWaitOp)); + + NetCliAuthScoreTransferPoints( + srcScoreId, + destScoreId, + numPoints, + WaitOpCallback, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + AsyncSleep(10); + } + + if (IS_NET_SUCCESS(param.result)) { + if (GameScoreLink * link = fScores.Find(srcScoreId)) { + link->score->value -= numPoints; + } + if (GameScoreLink * link = fScores.Find(destScoreId)) { + link->score->value += numPoints; + } + } + + return param.result; +} + +//============================================================================ +// SetPoints +//============================================================================ +ENetError pfGameScoreMgr::SetPoints( + unsigned scoreId, + int numPoints +) { + NetWaitOp param; + MemZero(¶m, sizeof(NetWaitOp)); + + NetCliAuthScoreSetPoints( + scoreId, + numPoints, + WaitOpCallback, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + AsyncSleep(10); + } + + if (IS_NET_SUCCESS(param.result)) { + if (GameScoreLink * link = fScores.Find(scoreId)) { + link->score->value = numPoints; + } + } + + return param.result; +} + +//============================================================================ +// GetRankList +//============================================================================ + +struct GetRanksOp : NetWaitOp +{ + NetGameRank*** ranks; + int* rankCount; +}; + +static void GetRanksCallback( + ENetError result, + void * param, + const NetGameRank ranks[], + unsigned rankCount +) { + GetRanksOp * op = (GetRanksOp*)param; + op->result = result; + + if (IS_NET_SUCCESS(result)) { + *(op->ranks) = TRACKED_NEW NetGameRank*[rankCount]; + *(op->rankCount) = rankCount; + + for (int i = 0; i < rankCount; ++i) { + NetGameRank * rank = TRACKED_NEW NetGameRank; + + rank->rank = ranks[i].rank; + rank->score = ranks[i].score; + StrCopy(rank->name, ranks[i].name, arrsize(rank->name)); + + (*op->ranks)[i] = rank; + } + } + else { + *(op->ranks) = nil; + op->rankCount = 0; + } + + op->complete = true; +} + +ENetError pfGameScoreMgr::GetRankList( + unsigned ownerId, + unsigned scoreGroup, + unsigned parentFolderId, + const char * gameName, + unsigned timePeriod, + unsigned numResults, + unsigned pageNumber, + bool sortDesc, + NetGameRank**& outRankList, + int& outRankListCount +) { + GetRanksOp param; + MemZero(¶m, sizeof(GetRanksOp)); + param.ranks = &outRankList; + param.rankCount = &outRankListCount; + + NetCliAuthScoreGetRankList( + ownerId, + scoreGroup, + parentFolderId, + gameName, + timePeriod, + numResults, + pageNumber, + sortDesc, + GetRanksCallback, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + AsyncSleep(10); + } + + return param.result; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.h new file mode 100644 index 00000000..b5a219bf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.h @@ -0,0 +1,137 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfGameScoreMgr/pfGameScoreMgr.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMESCOREMGR_PFGAMESCOREMGR_H +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMESCOREMGR_PFGAMESCOREMGR_H + +#include "hsTypes.h" +#include "../pnNetBase/pnNetBase.h" +#include "../pnUtils/pnUtils.h" + +struct NetGameRank; + +struct pfGameScore : AtomicRef +{ + unsigned scoreId; + unsigned ownerId; + UInt32 createdTime; + char gameName[kMaxGameScoreNameLength]; + unsigned gameType; + int value; + + pfGameScore(); + ~pfGameScore(); + + void Init( + unsigned sid, + unsigned oid, + UInt32 createTime, + const char gname[], + unsigned gType, + int val + ); + + void CopyFrom(const pfGameScore* score); +}; + +class pfGameScoreMgr +{ +private: + pfGameScoreMgr(); + + struct GameScoreLink : THashKeyVal + { + HASHLINK(GameScoreLink) link; + pfGameScore * score; + + GameScoreLink(pfGameScore * gscore) + : THashKeyVal(gscore->scoreId) + , score(gscore) + { + } + }; + + HASHTABLEDECL( + GameScoreLink, + THashKeyVal, + link + ) fScores; + +public: + static pfGameScoreMgr* GetInstance(); + + void AddCachedScore(pfGameScore * score); + void RemoveCachedScore(unsigned scoreId); + + ENetError CreateScore( + unsigned ownerId, + const char* gameName, + unsigned gameType, + int value, + pfGameScore& score + ); + ENetError DeleteScore( + unsigned scoreId + ); + ENetError AddPoints( + unsigned scoreId, + int numPoints + ); + ENetError TransferPoints( + unsigned srcScoreId, + unsigned destScoreId, + int numPoints + ); + ENetError SetPoints( + unsigned scoreId, + int numPoints + ); + ENetError GetScoresIncRef( + unsigned ownerId, + const char* gameName, + pfGameScore**& outScoreList, + int& outScoreListCount + ); + ENetError GetRankList( + unsigned ownerId, + unsigned scoreGroup, + unsigned parentFolderId, + const char * gameName, + unsigned timePeriod, + unsigned numResults, + unsigned pageNumber, + bool sortDesc, + NetGameRank**& outRankList, + int& outRankListCount + ); +}; + +#endif // PLASMA20_SOURCES_PLASMA_FEATURELIB_PFGAMESCOREMGR_PFGAMESCOREMGR_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBook.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBook.cpp new file mode 100644 index 00000000..363fa9ce --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBook.cpp @@ -0,0 +1,3569 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfJournalBook Class // +// A generic, high-level, abstract method of creating various Myst-like // +// books within the game with very little effort, while ensuring that they // +// all remain consistent in appearance and operability. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "pfJournalBook.h" +#include "hsUtils.h" +#include "hsStlUtils.h" +#include "hsResMgr.h" +#include "pcSmallRect.h" +#include "plgDispatch.h" +#include "../pfGameGUIMgr/pfGUIDialogMod.h" +#include "../pfGameGUIMgr/pfGUIControlMod.h" +#include "../pfGameGUIMgr/pfGUICheckBoxCtrl.h" +#include "../pfGameGUIMgr/pfGUIDialogHandlers.h" +#include "../pfGameGUIMgr/pfGUIDynDisplayCtrl.h" +#include "../pfGameGUIMgr/pfGUIClickMapCtrl.h" +#include "../pfGameGUIMgr/pfGUIButtonMod.h" +#include "../pfGameGUIMgr/pfGUIProgressCtrl.h" +#include "../pfGameGUIMgr/pfGUIMultiLineEditCtrl.h" + +#include "../pfMessage/pfGUINotifyMsg.h" +#include "../plGImage/plMipmap.h" +#include "../plGImage/plDynamicTextMap.h" +#include "../plPipeline/hsGDeviceRef.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../plMessage/plLayRefMsg.h" +#include "../plMessage/plMatRefMsg.h" +#include "../plSurface/plLayerInterface.h" +#include "../plSurface/plLayer.h" +#include "../plSurface/hsGMaterial.h" +#include "../plAgeLoader/plAgeLoader.h" +#include "../pfSurface/plLayerBink.h" + +// So we can do image searches in our local age +#include "../plNetClient/plNetClientMgr.h" +#include "../plResMgr/plKeyFinder.h" + +// For notify sends +#include "../pnMessage/plNotifyMsg.h" +#include "../pnTimer/plTimerCallbackManager.h" +#include "../plMessage/plTimerCallbackMsg.h" + +// For custom cursors +#include "../plInputCore/plInputInterface.h" + +// For measuring text +#include "../plGImage/plFont.h" + +// For SFX +#include "hsTimer.h" + + + +////////////////////////////////////////////////////////////////////////////// +//// pfEsHTMLChunk Class ///////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +class pfEsHTMLChunk +{ + public: + + std::wstring fText; // Paragraph text, or face name + plKey fImageKey; // Key of image + UInt8 fFontSize; + UInt32 fFlags; + UInt8 fType; + UInt32 fEventID; + + pcSmallRect fLinkRect; // Used only for image chunks, and only when stored in the fVisibleLinks array + + hsColorRGBA fColor; + + UInt16 fAbsoluteX, fAbsoluteY; + + hsScalar fCurrOpacity; // For SFX images + hsScalar fSFXTime; // For SFX images + hsScalar fMinOpacity, fMaxOpacity; + + hsColorRGBA fCurrColor; + hsColorRGBA fOffColor, fOnColor; + + bool fNoResizeImg; + Int16 fLineSpacing; + bool fTintDecal; + bool fLoopMovie; + bool fOnCover; // if true, the movie is on the cover + UInt8 fMovieIndex; // the index of the movie in the source code, used for identification + + enum Flags + { + kFontBold = 0x00000001, + kFontItalic = 0x00000002, + kFontRegular= 0x00000004, // 'cause 0 means "style not defined" + kFontMask = kFontBold | kFontItalic | kFontRegular, + + kFontColor = 0x00000008, + kFontSpacing= 0x00000010, + + kCenter = 0x00000001, + kLeft = 0x00000002, + kRight = 0x00000003, + kAlignMask = 0x00000003, + + kBlendAlpha = 0x00000004, + kCanLink = 0x00000008, + kFloating = 0x00000010, + kGlowing = 0x00000020, + kActAsCB = 0x00000040, // Cause the image to act in a checkbox-like fashion. + // Min opacity turns into "off opacity" and max opacity + // is "on opacity" + kChecked = 0x00000080, // Only for kActAsCB, set if it's currently "checked" + kTranslucent= 0x00000100 // is the image translucent? if so, use fCurrOpacity + }; + + enum Types + { + kEmpty = 0, + kParagraph, + kImage, + kPageBreak, + kFontChange, + kMargin, + kCover, // Just a placeholder, never actually used after compile time + kBook, // another placeholder + kDecal, + kMovie, + kEditable // placeholder, ver 3.0 + }; + + // Paragraph constructor + pfEsHTMLChunk( const wchar_t *text ) + { + fType = kParagraph; + if (text) + fText = text; + else + fText = L""; + fFlags = kLeft; + fFontSize = 0; + fImageKey = nil; + fEventID = 0; + fColor.Set( 0.f, 0.f, 0.f, 1.f ); + fAbsoluteX = fAbsoluteY = 0; + fCurrOpacity = 1.f; + fMinOpacity = 0.f; + fMaxOpacity = 1.f; + fNoResizeImg = false; + fLineSpacing = 0; + fTintDecal = false; + fLoopMovie = true; + fOnCover = false; + fMovieIndex = -1; + } + + // Image constructor (used for decals and movies too) + pfEsHTMLChunk( plKey imageKey, UInt32 alignFlags ) + { + fType = kImage; + fText = L""; + fFlags = alignFlags; + fFontSize = 0; + fImageKey = imageKey; + fEventID = 0; + fColor.Set( 0.f, 0.f, 0.f, 1.f ); + fAbsoluteX = fAbsoluteY = 0; + fCurrOpacity = 1.f; + fMinOpacity = 0.f; + fMaxOpacity = 1.f; + fNoResizeImg = false; + fLineSpacing = 0; + fTintDecal = false; + fLoopMovie = true; + fOnCover = false; + fMovieIndex = -1; + } + + // Page break constructor + pfEsHTMLChunk() + { + fType = kPageBreak; + fText = L""; + fImageKey = nil; + fFontSize = 0; + fFlags = 0; + fEventID = 0; + fColor.Set( 0.f, 0.f, 0.f, 1.f ); + fAbsoluteX = fAbsoluteY = 0; + fCurrOpacity = 1.f; + fMinOpacity = 0.f; + fMaxOpacity = 1.f; + fNoResizeImg = false; + fLineSpacing = 0; + fTintDecal = false; + fLoopMovie = true; + fOnCover = false; + fMovieIndex = -1; + } + + // Font change constructor + pfEsHTMLChunk( const wchar_t *face, UInt8 size, UInt32 fontFlags ) + { + fType = kFontChange; + if (face) + fText = face; + else + fText = L""; + fFontSize = size; + fFlags = fontFlags; + fImageKey = nil; + fEventID = 0; + fColor.Set( 0.f, 0.f, 0.f, 1.f ); + fAbsoluteX = fAbsoluteY = 0; + fCurrOpacity = 1.f; + fMinOpacity = 0.f; + fMaxOpacity = 1.f; + fNoResizeImg = false; + fLineSpacing = 0; + fTintDecal = false; + fLoopMovie = true; + fOnCover = false; + fMovieIndex = -1; + } + + ~pfEsHTMLChunk() {} +}; + +////////////////////////////////////////////////////////////////////////////// +//// Our Template Dialog Handler ///////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +class pfJournalDlgProc : public pfGUIDialogProc +{ + protected: + + pfBookData *fBook; + + public: + + enum TagIDs + { + kTagBookCover = 100, + kTagLeftDTMap = 101, + kTagRightDTMap = 102, + kTagTurnFrontDTMap = 103, + kTagTurnBackDTMap = 104, + kTagTurnPageCtrl = 105, + kTagLeftPageBtn = 106, + kTagRightPageBtn = 107, + kTagOutsideBookBtn = 108, + kTagCoverLayer = 109, + kTagLeftCornerBtn = 110, + kTagRightCornerBtn = 111, + kTagWidthCBDummy = 112, + kTagHeightCBDummy = 113, + kTagLeftEditCtrl = 120, + kTagRightEditCtrl = 121, + kTagTurnFrontEditCtrl = 122, + kTagTurnBackEditCtrl = 123 + }; + + pfJournalDlgProc( pfBookData *book ) : fBook( book ) + { + } + + virtual ~pfJournalDlgProc() + { + } + + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + if ( fBook && fBook->fCurrBook ) + { + if( ctrl->GetTagID() == kTagBookCover ) + { + fBook->fCurrBook->Open(); + } + else if( ctrl->GetTagID() == kTagLeftPageBtn ) + { + // only turn pages if the book is actually open + if( fBook->fCurrentlyOpen ) + fBook->fCurrBook->IHandleLeftSideClick(); + } + else if( ctrl->GetTagID() == kTagRightPageBtn ) + { + // only turn pages if the book is actually open + if( fBook->fCurrentlyOpen ) + fBook->fCurrBook->IHandleRightSideClick(); + } + else if( ctrl->GetTagID() == kTagOutsideBookBtn ) + { + if( fBook->fCurrentlyOpen ) + fBook->fCurrBook->CloseAndHide(); + else + fBook->fCurrBook->Hide(); + } + } + } + + // Called on dialog init (i.e. first showing, before OnShow() is called), only ever called once + virtual void OnInit( void ) + { + } + + // Called before the dialog is shown, always after OnInit() + virtual void OnShow( void ) + { + } + + // Called before the dialog is hidden + virtual void OnHide( void ) + { + } + + // Called on the dialog's destructor, before it's unregistered with the game GUI manager + virtual void OnDestroy( void ) + { + } + + // Called when the dialog's focused control changes + virtual void OnCtrlFocusChange( pfGUIControlMod *oldCtrl, pfGUIControlMod *newCtrl ) + { + } + + // Called when the key bound to a GUI event is pressed. Only called on the top modal dialog + virtual void OnControlEvent( ControlEvt event ) + { + if( event == kExitMode ) + { + if( fBook->fCurrentlyOpen ) + fBook->fCurrBook->CloseAndHide(); + else + fBook->fCurrBook->Hide(); + } + } + + virtual void HandleExtendedEvent( pfGUIControlMod *ctrl, UInt32 event ) + { + if (fBook) + { + if( ctrl == fBook->fLeftPageMap ) + { + if( event == pfGUIClickMapCtrl::kMouseHovered ) + { + if (fBook->fCurrBook) + { + // Update our custom cursor on the map + Int32 idx = fBook->fCurrBook->IFindCurrVisibleLink( false, true ); + if( idx != -1 ) + fBook->fLeftPageMap->SetCustomCursor( plInputInterface::kCursorPoised/*Hand*/ ); + else if(( fBook->fCurrBook->fCurrentPage > 1 )&&( fBook->fCurrBook->fAllowTurning )) + fBook->fLeftPageMap->SetCustomCursor( plInputInterface::kCursorLeft ); + else if ((fBook->fCurrBook->fAreEditing) && !(fBook->fLeftEditCtrl->ShowingBeginningOfBuffer())) // if we have more buffer to show + fBook->fLeftPageMap->SetCustomCursor( plInputInterface::kCursorLeft ); + else + fBook->fLeftPageMap->SetCustomCursor( plInputInterface::kCursorUp ); + } + } + } + else if( ctrl == fBook->fRightPageMap ) + { + if( event == pfGUIClickMapCtrl::kMouseHovered ) + { + if (fBook->fCurrBook) + { + // Update our custom cursor on the map + Int32 idx = fBook->fCurrBook->IFindCurrVisibleLink( true, true ); + if( idx != -1 ) + fBook->fRightPageMap->SetCustomCursor( plInputInterface::kCursorPoised/*Hand*/ ); + else if((fBook->fCurrBook->fAreWeShowing) && ( fBook->fCurrBook->fCurrentPage + 2 <= fBook->fCurrBook->fLastPage )&&( fBook->fCurrBook->fAllowTurning )) + fBook->fRightPageMap->SetCustomCursor( plInputInterface::kCursorRight ); + else if((fBook->fCurrBook->fAreEditing) && !(fBook->fRightEditCtrl->ShowingEndOfBuffer())) // if we have more buffer to show + fBook->fRightPageMap->SetCustomCursor( plInputInterface::kCursorRight ); + else + fBook->fRightPageMap->SetCustomCursor( plInputInterface::kCursorUp ); + } + } + } + } + } +}; + +//// Multiline edit handler class //////////////////////////////////////////// + +class pfBookMultiLineEditProc : public pfGUIMultiLineEditProc +{ +private: + pfBookData *bookData; +public: + pfBookMultiLineEditProc(pfBookData *owner) { bookData = owner; } + virtual ~pfBookMultiLineEditProc() {} + + virtual OnEndOfControlList(Int32 cursorPos) { bookData->HitEndOfControlList(cursorPos); } + virtual OnBeginningOfControlList(Int32 cursorPos) { bookData->HitBeginningOfControlList(cursorPos); } +}; + +//// Book data class ///////////////////////////////////////////////////////// + +pfBookData::pfBookData(const char *guiName /* = nil */) +{ + fCurrBook = nil; + fDialog = nil; + fCoverButton = fTurnPageButton = nil; + fLeftPageMap = fRightPageMap = nil; + fCoverLayer = nil; + fCoverMaterial = nil; + UInt16 i; + for (i=0; i<4; i++) + fPageMaterials[i] = nil; + fLeftCorner = fRightCorner = nil; + fWidthCtrl = fHeightCtrl = nil; + fCurrSFXPages = kNoSides; + fBaseSFXTime = 0.f; + fResetSFXFlag = false; + fSFXUpdateFlip = false; + fCurrentlyTurning = false; + + fRightEditCtrl = fLeftEditCtrl = nil; + fTurnFrontEditCtrl = fTurnBackEditCtrl = nil; + fEditable = false; + fAdjustCursorTo = -1; + + if (guiName) + fGUIName = guiName; + else + fGUIName = "BkBook"; +} + +pfBookData::~pfBookData() +{ + RegisterForSFX( kNoSides ); +} + +void pfBookData::LoadGUI() +{ + // has the dialog been loaded yet? + if (!pfGameGUIMgr::GetInstance()->IsDialogLoaded(fGUIName.c_str())) + // no then load and set handler + pfGameGUIMgr::GetInstance()->LoadDialog(fGUIName.c_str(), GetKey(), "GUI"); + else + // yes then just set the handler + pfGameGUIMgr::GetInstance()->SetDialogToNotify(fGUIName.c_str(), GetKey()); +} + +hsBool pfBookData::MsgReceive(plMessage *pMsg) +{ + plGenRefMsg *ref = plGenRefMsg::ConvertNoRef(pMsg); + if(ref != nil) + { + if(ref->fType == kRefDialog) + { + if(ref->GetContext() & (plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace)) + { + pfGUIDialogMod *temp = pfGUIDialogMod::ConvertNoRef(ref->GetRef()); + if (temp != nil) // sanity check + fDialog = temp; + } + /*else + { + fDialog = nil; + fCoverButton = nil; + }*/ + return true; + } + else if(ref->fType == kRefDefaultCover) + { + if(ref->GetContext() & (plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace)) + fDefaultCover = plMipmap::ConvertNoRef(ref->GetRef()); + else + fDefaultCover = nil; + return true; + } + } + + plEventCallbackMsg *callback = plEventCallbackMsg::ConvertNoRef( pMsg ); + if( callback != nil ) + { + // Our callback message to tell us the page is done flipping + if( callback->fUser & 0x08 ) + { + // make sure that we still have a current book + if (fCurrBook) + { + // Or actually maybe it's that we're done closing and should hide now, + // produced from a CloseAndHide() + if( callback->fEvent == kStop ) + fCurrBook->Hide(); + else + fCurrBook->IFinishShow( ( callback->fUser & 0x01 ) ? true : false ); + } + } + else if( fCurrentlyTurning ) + { + if( callback->fUser & 0x04 ) + IFillUncoveringPage( (hsBool)( callback->fUser & 0x01 ) ? true : false ); + else if( callback->fUser & 0x02 ) + StartTriggeredFlip( (hsBool)( callback->fUser & 0x01 ) ? true : false ); + else + IFinishTriggeredFlip( (hsBool)( callback->fUser & 0x01 ) ? true : false ); + } + return true; + } + + pfGUINotifyMsg *notify = pfGUINotifyMsg::ConvertNoRef(pMsg); + if(notify != nil) + { + // The only time we should get this is when the dialog loads; after that, we hijack + // the dialog proc with our own + IInitTemplate(pfGUIDialogMod::ConvertNoRef(notify->GetSender()->ObjectIsLoaded())); + return true; + } + + plTimeMsg *time = plTimeMsg::ConvertNoRef( pMsg ); + if( time != nil && fCurrSFXPages != kNoSides && !fCurrentlyTurning && fCurrentlyOpen ) + { + IHandleSFX( (hsScalar)time->DSeconds() ); + return true; + } + + plTimerCallbackMsg* timerMsg = plTimerCallbackMsg::ConvertNoRef(pMsg); + if (timerMsg) + { + if (timerMsg->fID == 99) // the flip animation is about to end, hide the page to prevent flickering + { + // the right side was uncovered, so the left side needs to be hidden + fLeftEditCtrl->SetVisible(false); + } + else if (timerMsg->fID == 98) + { + // the left side was uncovered, so the right side needs to be hidden + fRightEditCtrl->SetVisible(false); + } + } + + return hsKeyedObject::MsgReceive(pMsg); +} + +void pfBookData::IInitTemplate(pfGUIDialogMod *templateDlg) +{ + hsAssert(templateDlg != nil, "Nil template in pfBookData::IInitTemplate()!"); + + // Init and ref our fDialog pointer + hsgResMgr::ResMgr()->SendRef(templateDlg->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefDialog), plRefFlags::kPassiveRef); + + // Hijack the dialog proc with our own + templateDlg->SetHandlerForAll(TRACKED_NEW pfJournalDlgProc(this)); + + // Find our animation keys + + // And other interesting pointers + fCoverButton = pfGUICheckBoxCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagBookCover)); + fTurnPageButton = pfGUICheckBoxCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagTurnPageCtrl)); + fLeftPageMap = pfGUIClickMapCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagLeftPageBtn)); + fRightPageMap = pfGUIClickMapCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagRightPageBtn)); + fLeftCorner = pfGUIButtonMod::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagLeftCornerBtn)); + fRightCorner = pfGUIButtonMod::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagRightCornerBtn)); + fWidthCtrl = pfGUIProgressCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagWidthCBDummy)); + fHeightCtrl = pfGUIProgressCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagHeightCBDummy)); + + fTurnPageButton->SetEnabled(false); + fCoverButton->DontPlaySounds(); // dont let checkbox play sounds, journal will take care of that. + + fCoverLayer = pfGUIDynDisplayCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagCoverLayer))->GetLayer(0); + fCoverMaterial = pfGUIDynDisplayCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagCoverLayer))->GetMaterial(0); + + fPageMaterials[kLeftPage] = pfGUIDynDisplayCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagLeftDTMap))->GetMaterial(0); + fPageMaterials[kRightPage] = pfGUIDynDisplayCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagRightDTMap))->GetMaterial(0); + fPageMaterials[kTurnFrontPage] = pfGUIDynDisplayCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagTurnFrontDTMap))->GetMaterial(0); + fPageMaterials[kTurnBackPage] = pfGUIDynDisplayCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagTurnBackDTMap))->GetMaterial(0); + + // Grab and ref the default cover mipmap + plLayer *lay = plLayer::ConvertNoRef(fCoverLayer); + if((lay != nil)&&(lay->GetTexture() != nil)) + hsgResMgr::ResMgr()->AddViaNotify(lay->GetTexture()->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefDefaultCover), plRefFlags::kPassiveRef); + + fLeftPageMap->SetFlag(pfGUIClickMapCtrl::kReportHovering); + fRightPageMap->SetFlag(pfGUIClickMapCtrl::kReportHovering); + + fLeftEditCtrl = pfGUIMultiLineEditCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagLeftEditCtrl)); + if (fLeftEditCtrl) + { + fLeftEditCtrl->SetEnabled(false); // disable the edit controls initially, we can turn them on later + fLeftEditCtrl->SetVisible(false); + } + fRightEditCtrl = pfGUIMultiLineEditCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagRightEditCtrl)); + if (fRightEditCtrl) + { + fRightEditCtrl->SetEnabled(false); + fRightEditCtrl->SetVisible(false); + } + + fTurnFrontEditCtrl = pfGUIMultiLineEditCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagTurnFrontEditCtrl)); + if (fTurnFrontEditCtrl) + { + fTurnFrontEditCtrl->SetEnabled(false); + fTurnFrontEditCtrl->SetVisible(false); + } + + fTurnBackEditCtrl = pfGUIMultiLineEditCtrl::ConvertNoRef(templateDlg->GetControlFromTag(pfJournalDlgProc::kTagTurnBackEditCtrl)); + if (fTurnBackEditCtrl) + { + fTurnBackEditCtrl->SetEnabled(false); + fTurnBackEditCtrl->SetVisible(false); + } + // if all the edit controls are here, we are editable, so set up the initial link + if (fLeftEditCtrl && fRightEditCtrl && fTurnFrontEditCtrl && fTurnBackEditCtrl) + { + fEditable = true; + fLeftEditCtrl->SetNext(fRightEditCtrl); + fLeftEditCtrl->SetEventProc(TRACKED_NEW pfBookMultiLineEditProc(this)); + fTurnFrontEditCtrl->SetNext(fTurnBackEditCtrl); // these are always back to back + } +} + +//// IGetDTMap /////////////////////////////////////////////////////////////// +// Just a quick helper + +plDynamicTextMap *pfBookData::GetDTMap(UInt32 which) +{ + pfGUIDynDisplayCtrl *display = pfGUIDynDisplayCtrl::ConvertNoRef(fDialog->GetControlFromTag(which)); + return display->GetMap(0); +} + +//// GetEditCtrl ///////////////////////////////////////////////////////////// + +pfGUIMultiLineEditCtrl *pfBookData::GetEditCtrl(UInt32 which) +{ + switch (which) + { + case pfJournalDlgProc::kTagLeftEditCtrl: + return fLeftEditCtrl; + case pfJournalDlgProc::kTagRightEditCtrl: + return fRightEditCtrl; + case pfJournalDlgProc::kTagTurnFrontEditCtrl: + return fTurnFrontEditCtrl; + case pfJournalDlgProc::kTagTurnBackEditCtrl: + return fTurnBackEditCtrl; + default: + return nil; + } +} + +//// IRegisterForSFX ///////////////////////////////////////////////////////// +// Registers (or unregisters) for time messages so we can process special FX +// if we need to + +void pfBookData::RegisterForSFX(WhichSide whichPages) +{ + if( whichPages == fCurrSFXPages) + return; + + if(whichPages != kNoSides) + { + plgDispatch::Dispatch()->RegisterForExactType(plTimeMsg::Index(), GetKey()); + } + else + plgDispatch::Dispatch()->UnRegisterForExactType(plTimeMsg::Index(), GetKey()); + + fCurrSFXPages = whichPages; +} + +//// IHandleSFX ////////////////////////////////////////////////////////////// +// Process SFX for this frame. Note: if ANYTHING is wrong (page starts not +// calced, pointers bad, etc) just bail, since the SFX are just for visual +// flair and not really needed. + +void pfBookData::IHandleSFX(hsScalar currTime, WhichSide whichSide /*= kNoSides*/) +{ + if(fCurrBook == nil) + return; + if(whichSide == kNoSides) + { + if(fResetSFXFlag) + { + fBaseSFXTime=currTime; + fResetSFXFlag=false; + } + + fSFXUpdateFlip = !fSFXUpdateFlip; + + // Slightly recursive here to help us out a bit + if(fSFXUpdateFlip&&(fCurrSFXPages & kLeftSide)) + IHandleSFX(currTime, kLeftSide); + else if(!fSFXUpdateFlip &&(fCurrSFXPages & kRightSide)) + IHandleSFX(currTime, kRightSide); + return; + } + + // Update all SFX images for this page first + hsScalar deltaT = currTime - fBaseSFXTime; + + UInt32 idx, inc = (whichSide == kLeftSide) ? 0 : 1; + if(fCurrBook->fPageStarts.GetCount() <= fCurrBook->fCurrentPage + inc + 1) + return; + + bool stillWant = false; + for(idx = fCurrBook->fPageStarts[fCurrBook->fCurrentPage + inc]; idx < fCurrBook->fPageStarts[fCurrBook->fCurrentPage + inc + 1]; idx++) + { + pfEsHTMLChunk *chunk = fCurrBook->fHTMLSource[idx]; + + if(chunk->fFlags & pfEsHTMLChunk::kGlowing) + { + // Glow SFX: animate opacity based on time offset + UInt8 isOdd = 0; + hsScalar newDelta = deltaT; + while(newDelta > chunk->fSFXTime) + { + isOdd = ~isOdd; + newDelta -= chunk->fSFXTime; + } + + // If we're not odd, then we're decreasing in opacity, else we're increasing + if(isOdd) + newDelta = chunk->fSFXTime - newDelta; + + chunk->fCurrOpacity = chunk->fMaxOpacity - ((chunk->fMaxOpacity - chunk->fMinOpacity)*(newDelta / chunk->fSFXTime)); + stillWant = true; + } + else if(chunk->fFlags & pfEsHTMLChunk::kActAsCB) + { + // If our opacity doesn't match our checked state, slowly fade to it + hsColorRGBA inc; + inc.Set(0.1f, 0.1f, 0.1f, 0.1f); + + hsColorRGBA &want = (chunk->fFlags & pfEsHTMLChunk::kChecked) ? chunk->fOnColor : chunk->fOffColor; + if(want != chunk->fCurrColor) + { +#define COMPARE_ME( wnt, curr ) \ + if( wnt > curr + 0.1f ) \ + { \ + curr += 0.1f; stillWant = true; \ + } \ + else if( wnt < curr - 0.1f ) \ + { \ + curr -= 0.1f; stillWant = true; \ + } \ + else \ + curr = wnt; + + COMPARE_ME( want.r, chunk->fCurrColor.r ) + COMPARE_ME( want.g, chunk->fCurrColor.g ) + COMPARE_ME( want.b, chunk->fCurrColor.b ) + COMPARE_ME( want.a, chunk->fCurrColor.a ) + } + } + } + + // All done updating that page. Now render it! + fCurrBook->IRenderPage( fCurrBook->fCurrentPage + inc, ( whichSide == kLeftSide ) ? pfJournalDlgProc::kTagLeftDTMap : pfJournalDlgProc::kTagRightDTMap ); + + if( !stillWant ) + { + // Done with FX for this page, so unregister for FX on this page now + RegisterForSFX((WhichSide)(fCurrSFXPages & ~whichSide)); + } +} + +//// IFillUncoveringPage ///////////////////////////////////////////////////// +// Yet another step in the page flip, to make SURE we're already showing the +// turning page before we fill in the page behind it + +void pfBookData::IFillUncoveringPage(hsBool rightSide) +{ + // only show the turning page if the book is open + if ( CurrentlyOpen() ) + fTurnPageButton->SetVisible(true); + // make sure there is a current book + if (fCurrBook) + { + if (fCurrBook->fAreEditing) + { + int id; + UpdatePageCorners(rightSide ? kRightSide : kLeftSide); + if (rightSide) + { + fTurnBackEditCtrl->ForceUpdate(); // force everything that is changing to update + fTurnBackEditCtrl->SetVisible(true); // and make sure everything is showing + fTurnFrontEditCtrl->ForceUpdate(); + fTurnFrontEditCtrl->SetVisible(true); + fRightEditCtrl->ForceUpdate(); + fRightEditCtrl->SetVisible(true); + // The left edit ctrl doesn't update until the page flip animation is done + id = 99; + } + else + { + fTurnFrontEditCtrl->ForceUpdate(); + fTurnFrontEditCtrl->SetVisible(true); + fTurnBackEditCtrl->ForceUpdate(); + fTurnBackEditCtrl->SetVisible(true); + fLeftEditCtrl->ForceUpdate(); + fLeftEditCtrl->SetVisible(true); + // The right edit ctrl doesn't update until the page flip animation is done + id = 98; + } + + // create a timer so we can hide the old left or right turn page right before the animation finishes to prevent flicker + plTimerCallbackMsg* pTimerMsg = TRACKED_NEW plTimerCallbackMsg(GetKey(),id); + plgTimerCallbackMgr::NewTimer( .5, pTimerMsg ); // .5 found by trial and error + return; // the gui controls render everything for us, so ignoring this request + } + if(rightSide) + fCurrBook->IRenderPage(fCurrBook->fCurrentPage + 1, pfJournalDlgProc::kTagRightDTMap); + else + fCurrBook->IRenderPage(fCurrBook->fCurrentPage, pfJournalDlgProc::kTagLeftDTMap); + } + + // Update the page corner we're flipping away from + UpdatePageCorners(rightSide ? kRightSide : kLeftSide); +} + +//// ITriggerPageFlip //////////////////////////////////////////////////////// +// Triggers the start of the page-flipping animation, as well as sets up the callback for when it's finished + +void pfBookData::ITriggerPageFlip(hsBool flipBackwards, hsBool immediate) +{ + // Hack here: since we don't have an official interface to select these directly + // in MAX, we just use a GUI check box to grab them for us, even though we never + // actually use the functionality of the checkbox itself + const hsTArray &keys = fTurnPageButton->GetAnimationKeys(); + const char *animName = fTurnPageButton->GetAnimationName(); + + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + if (immediate) + { + msg->SetCmd(plAnimCmdMsg::kGoToEnd); + } + else + { + msg->SetCmd(plAnimCmdMsg::kContinue); + msg->SetCmd(plAnimCmdMsg::kSetForewards); + } + msg->SetAnimName(flipBackwards ? "backward" : "forward"); + msg->AddReceivers(keys); + + // Here's the whole reason why we're not just checking the checkbox: so we can attach a callback + // so we know when the animation completes. Pretty sad, huh? Poor checkbox. + plEventCallbackMsg *eventMsg = TRACKED_NEW plEventCallbackMsg; + eventMsg->AddReceiver(GetKey()); + eventMsg->fRepeats = 0; + if (immediate) + { + eventMsg->fUser = ((!flipBackwards) ? 0x01 : 0x00) | 0x02; + eventMsg->fEvent = kSingleFrameAdjust; + } + else + { + eventMsg->fUser = (flipBackwards ? 0x01 : 0x00); + eventMsg->fEvent = kStop; + } + msg->SetCmd(plAnimCmdMsg::kAddCallbacks); + msg->AddCallback(eventMsg); + hsRefCnt_SafeUnRef(eventMsg); + if (!immediate) + { + // We want a second callback to tell us when, indeed, the page has started turning + // and is thus visible and thus we can actually, really, honestly, safely fill in the + // page behind it + eventMsg = TRACKED_NEW plEventCallbackMsg; + eventMsg->AddReceiver(GetKey()); + eventMsg->fRepeats = 0; + eventMsg->fUser = !flipBackwards ? (0x04 | 0x01) : 0x04; + eventMsg->fEvent = kBegin; // Should cause it to be triggered once it seeks at the start of the command + msg->AddCallback(eventMsg); + hsRefCnt_SafeUnRef(eventMsg); + } + fCurrentlyTurning = true; + + msg->Send(); +} + +//// StartTriggeredFlip ///////////////////////////////////////////////////// +// Finishes the start of the triggered page flip (once we're sure the +// animation is at the new frame) + +void pfBookData::StartTriggeredFlip(hsBool flipBackwards) +{ + if(flipBackwards) + { + ITriggerPageFlip(true, false); + } + else + { + ITriggerPageFlip(false, false); + } +} + +//// Kill the page flipping cause, we're closing the book + +void pfBookData::KillPageFlip() +{ + if ( fCurrentlyTurning ) + { + //ITriggerPageFlip(false, true); + fTurnPageButton->SetVisible(false); + } +} + +//// IFinishTriggeredFlip //////////////////////////////////////////////////// +// Finishes the triggered page flip, on callback + +void pfBookData::IFinishTriggeredFlip(hsBool wasBackwards) +{ + if (fCurrBook && fCurrBook->fAreEditing) // this is handled differently when we are editing + { + fLeftEditCtrl->SetNext(fRightEditCtrl); // relink the original path + if (!wasBackwards) + { + // adjust the starting point of the control (not needed if we weren't backwards since that was done when we started turning + Int32 newStart = fRightEditCtrl->GetLastVisibleLine(); + fLeftEditCtrl->SetGlobalStartLine(newStart); + } + if (fAdjustCursorTo >= 0) + { + if (wasBackwards) + { + fRightEditCtrl->SetCursorToLoc(fAdjustCursorTo); + fRightEditCtrl->GetOwnerDlg()->SetFocus(fRightEditCtrl); + } + else + { + fLeftEditCtrl->SetCursorToLoc(fAdjustCursorTo); + fLeftEditCtrl->GetOwnerDlg()->SetFocus(fLeftEditCtrl); + } + fAdjustCursorTo = -1; + } + + fTurnFrontEditCtrl->SetVisible(false); // hide the controls + fTurnBackEditCtrl->SetVisible(false); + + fLeftEditCtrl->SetVisible(true); + fRightEditCtrl->SetVisible(true); + + fLeftEditCtrl->ForceUpdate(); + fRightEditCtrl->ForceUpdate(); + } + else if(wasBackwards) + { + // Grab the DTMaps for the front of the flip page and the right page, so we can + // copy the front into the right page + plDynamicTextMap *turnFront = GetDTMap(pfJournalDlgProc::kTagTurnFrontDTMap); + plDynamicTextMap *right = GetDTMap(pfJournalDlgProc::kTagRightDTMap); +// right->Swap( turnFront ); + if ( turnFront->IsValid() && right->IsValid() ) + { + memcpy(right->GetImage(), turnFront->GetImage(), right->GetLevelSize(0)); + if(right->GetDeviceRef() != nil) + right->GetDeviceRef()->SetDirty(true); + } + // we are going to attempt to re-render the left-hand page + // sometimes, the book stutters on a page flip and screws up the book + if (fCurrBook) + { + fCurrBook->IRenderPage(fCurrBook->fCurrentPage, pfJournalDlgProc::kTagLeftDTMap); + // move the videos over + fCurrBook->IMoveMovies(PageMaterial(kTurnFrontPage),PageMaterial(kRightPage)); + } + } + else + { + // Grab the DTMaps for the back of the flip page and the left page, so we can + // copy the back into the left page + plDynamicTextMap *turnBack = GetDTMap(pfJournalDlgProc::kTagTurnBackDTMap); + plDynamicTextMap *left = GetDTMap(pfJournalDlgProc::kTagLeftDTMap); + // right->Swap( turnFront ); + if ( turnBack->IsValid() && left->IsValid() ) + { + memcpy(left->GetImage(), turnBack->GetImage(), left->GetLevelSize(0)); + if(left->GetDeviceRef() != nil) + left->GetDeviceRef()->SetDirty(true); + } + // we are going to attempt to re-render the right-hand page + // sometimes, the book stutters on a page flip and screws up the book + if (fCurrBook) + { + fCurrBook->IRenderPage(fCurrBook->fCurrentPage + 1, pfJournalDlgProc::kTagRightDTMap); + // move the videos over + fCurrBook->IMoveMovies(PageMaterial(kTurnBackPage),PageMaterial(kLeftPage)); + } + } + + // Hide our page flipping button/checkbox/whatever + fTurnPageButton->SetVisible(false); + + // Update page corners + UpdatePageCorners(kBothSides); + + fCurrentlyTurning = false; + + // Start our FX once the page is done turning + fResetSFXFlag = true; +} + +//// UpdatePageCorners ////////////////////////////////////////////////////// +// Enables or disables the left and right page corners, to indicate current turnage state + +void pfBookData::UpdatePageCorners(WhichSide which) +{ + // make sure there is a book to update! + if (fCurrBook) + { + if (!fCurrBook->fAllowTurning || !fCurrBook->fAreWeShowing) + { + fLeftCorner->SetVisible(false); + fLeftCorner->SetEnabled(false); + fRightCorner->SetVisible(false); + fRightCorner->SetEnabled(false); + return; + } + if((which == kLeftSide)||(which == kBothSides)) + { + if (fCurrBook->fAreEditing) + fLeftCorner->SetVisible(!fLeftEditCtrl->ShowingBeginningOfBuffer()); // only show if the control is not viewing the beginning of the buffer + else + fLeftCorner->SetVisible((fCurrBook->fCurrentPage >= 2) ? true : false); + // Note: always disabled (we just go on the page click itself) + fLeftCorner->SetEnabled(false); + } + if((which == kRightSide)||(which == kBothSides)) + { + if (fCurrBook->fAreEditing) + fRightCorner->SetVisible(!fRightEditCtrl->ShowingEndOfBuffer()); // only show if the control is not viewing the end of the buffer + else + fRightCorner->SetVisible((fCurrBook->fCurrentPage + 2 <= fCurrBook->fLastPage) ? true : false); + fRightCorner->SetEnabled(false); + } + } +} + +//// SetCurrSize //////////////////////////////////////////////////////////// +// Seeks the width and height animations to set the desired book size. Sizes are in % across the animation + +void pfBookData::SetCurrSize(hsScalar w, hsScalar h) +{ + fWidthCtrl->SetCurrValue(w); + fHeightCtrl->SetCurrValue(h); +} + +//// PlayBookCloseAnim ////////////////////////////////////////////////////// +// Triggers our animation for closing or opening the book. + +void pfBookData::PlayBookCloseAnim(hsBool closeIt /*= true*/, hsBool immediate /*= false*/) +{ + // Disable the book cover button if we're opening, enable otherwise + fCoverButton->SetEnabled(closeIt); + + // Tell our cover button to check or uncheck + fCoverButton->SetChecked(!closeIt, immediate); + + // Trigger the open (or close) sound + if(!immediate) + fCoverButton->PlaySound(closeIt ? pfGUICheckBoxCtrl::kMouseUp : pfGUICheckBoxCtrl::kMouseDown); + + fCurrentlyOpen = !closeIt; +} + +//// Event routines from a linked multi-line edit control //////////////////// + +void pfBookData::HitEndOfControlList(Int32 cursorPos) +{ + fAdjustCursorTo = cursorPos; + if (fCurrBook) + fCurrBook->NextPage(); +} + +void pfBookData::HitBeginningOfControlList(Int32 cursorPos) +{ + fAdjustCursorTo = cursorPos; + if (fCurrBook) + fCurrBook->PreviousPage(); +} + +void pfBookData::EnableEditGUI(hsBool enable/* =true */) +{ + if (fEditable) + { + fLeftEditCtrl->SetEnabled(enable); + fLeftEditCtrl->SetVisible(enable); + fRightEditCtrl->SetEnabled(enable); + fRightEditCtrl->SetVisible(enable); + // we don't make these editable because they are temps used for the turning page + fTurnFrontEditCtrl->SetVisible(false); // we don't want these visible initially either + fTurnBackEditCtrl->SetVisible(false); + } +} + +//// Our Singleton Stuff ///////////////////////////////////////////////////// + +//pfJournalBook *pfJournalBook::fInstance = nil; +std::map pfJournalBook::fBookGUIs; + +void pfJournalBook::SingletonInit( void ) +{ + fBookGUIs["BkBook"] = TRACKED_NEW pfBookData(); // load the default book data object + hsgResMgr::ResMgr()->NewKey("BkBook",fBookGUIs["BkBook"],pfGameGUIMgr::GetInstance()->GetKey()->GetUoid().GetLocation()); + fBookGUIs["BkBook"]->LoadGUI(); +} + +void pfJournalBook::SingletonShutdown( void ) +{ + std::map::iterator i = fBookGUIs.begin(); + while (i != fBookGUIs.end()) + { + pfBookData *bookData = i->second; + bookData->GetKey()->UnRefObject(); + i->second = nil; + i++; + } + fBookGUIs.clear(); +} + +void pfJournalBook::LoadGUI( const char *guiName ) +{ + if (fBookGUIs.find(guiName) == fBookGUIs.end()) // is it already loaded? + { // nope, load it + fBookGUIs[guiName] = TRACKED_NEW pfBookData(guiName); + hsgResMgr::ResMgr()->NewKey(guiName,fBookGUIs[guiName],pfGameGUIMgr::GetInstance()->GetKey()->GetUoid().GetLocation()); + fBookGUIs[guiName]->LoadGUI(); + } +} + +void pfJournalBook::UnloadGUI( const char *guiName ) +{ + if (strcmp(guiName,"BkBook")==0) + return; // do not allow people to unload the default book gui + std::map::iterator loc = fBookGUIs.find(guiName); + if (loc != fBookGUIs.end()) // make sure it's loaded + { + fBookGUIs[guiName]->GetKey()->UnRefObject(); + fBookGUIs[guiName] = nil; + fBookGUIs.erase(loc); + } +} + +void pfJournalBook::UnloadAllGUIs() +{ + std::map::iterator i = fBookGUIs.begin(); + std::vector names; + while (i != fBookGUIs.end()) + { + std::string name = i->first; + names.push_back(name); // store a list of keys + i++; + } + int idx; + for (idx = 0; idx < names.size(); idx++) + UnloadGUI(names[idx].c_str()); // UnloadGUI won't unload BkBook +} + +//// Constructor ///////////////////////////////////////////////////////////// +// The constructor takes in the esHTML source for the journal, along with +// the name of the mipmap to use as the cover of the book. The callback +// key is the keyed object to send event messages to (see tag). + +pfJournalBook::pfJournalBook( const char *esHTMLSource, plKey coverImageKey, plKey callbackKey /*= nil*/, + const plLocation &hintLoc /* = plLocation::kGlobalFixedLoc */, const char *guiName /* = nil */ ) +{ + if (guiName && (strcmp(guiName,"") != 0)) + fCurBookGUI = guiName; + else + fCurBookGUI = "BkBook"; + if (fBookGUIs.find(fCurBookGUI) == fBookGUIs.end()) + { + fBookGUIs[fCurBookGUI] = TRACKED_NEW pfBookData(fCurBookGUI.c_str()); + hsgResMgr::ResMgr()->NewKey(fCurBookGUI.c_str(),fBookGUIs[fCurBookGUI],pfGameGUIMgr::GetInstance()->GetKey()->GetUoid().GetLocation()); + fBookGUIs[fCurBookGUI]->LoadGUI(); + } + + fCurrentPage = 0; + fLastPage = -1; + fCoverMipKey = coverImageKey; + fCoverFromHTML = false; + fCallbackKey = callbackKey; + fWidthScale = fHeightScale = 0.f; + fPageTMargin = fPageLMargin = fPageBMargin = fPageRMargin = 16; + fAllowTurning = true; + fAreWeShowing = false; + fCoverTint.Set( 0.f, 0.f, 0.f, 1.f ); + fTintFirst = true; + fTintCover = false; + fAreEditing = false; + fWantEditing = false; + fDefLoc = hintLoc; + + wchar_t *wESHTMLSource = hsStringToWString(esHTMLSource); + fUncompiledSource = wESHTMLSource; + ICompileSource( wESHTMLSource, hintLoc ); + delete [] wESHTMLSource; +} + +pfJournalBook::pfJournalBook( const wchar_t *esHTMLSource, plKey coverImageKey, plKey callbackKey /*= nil*/, + const plLocation &hintLoc /* = plLocation::kGlobalFixedLoc */, const char *guiName /* = nil */ ) +{ + if (guiName && (strcmp(guiName,"") != 0)) + fCurBookGUI = guiName; + else + fCurBookGUI = "BkBook"; + if (fBookGUIs.find(fCurBookGUI) == fBookGUIs.end()) + { + fBookGUIs[fCurBookGUI] = TRACKED_NEW pfBookData(fCurBookGUI.c_str()); + hsgResMgr::ResMgr()->NewKey(fCurBookGUI.c_str(),fBookGUIs[fCurBookGUI],pfGameGUIMgr::GetInstance()->GetKey()->GetUoid().GetLocation()); + fBookGUIs[fCurBookGUI]->LoadGUI(); + } + + fCurrentPage = 0; + fLastPage = -1; + fCoverMipKey = coverImageKey; + fCoverFromHTML = false; + fCallbackKey = callbackKey; + fWidthScale = fHeightScale = 0.f; + fPageTMargin = fPageLMargin = fPageBMargin = fPageRMargin = 16; + fAllowTurning = true; + fAreWeShowing = false; + fCoverTint.Set( 1.f, 1.f, 1.f, 1.f ); + fTintFirst = true; + fTintCover = false; + fAreEditing = false; + fWantEditing = false; + fDefLoc = hintLoc; + fUncompiledSource = esHTMLSource; + + ICompileSource( esHTMLSource, hintLoc ); +} + +pfJournalBook::~pfJournalBook() +{ + if (fBookGUIs.find(fCurBookGUI) != fBookGUIs.end()) // it might have been deleted before we got here + if( fBookGUIs[fCurBookGUI] && fBookGUIs[fCurBookGUI]->CurBook() == this ) + Hide(); + + IFreeSource(); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfJournalBook::MsgReceive( plMessage *pMsg ) +{ + return hsKeyedObject::MsgReceive( pMsg ); +} + +void pfJournalBook::SetGUI( const char *guiName ) +{ + if (guiName && (strcmp(guiName,"") != 0)) + fCurBookGUI = guiName; + if (fBookGUIs.find(fCurBookGUI) == fBookGUIs.end()) + fCurBookGUI = "BkBook"; // requested GUI isn't loaded, so use default GUI + SetEditable(fWantEditing); // make sure that if we want editing, to set it + ICompileSource(fUncompiledSource.c_str(), fDefLoc); // recompile the source to be safe +} + +//// Show //////////////////////////////////////////////////////////////////// +// Shows the book, optionally starting open or closed + +void pfJournalBook::Show( hsBool startOpened /*= false */) +{ + fBookGUIs[fCurBookGUI]->StartedOpen(startOpened); + fBookGUIs[fCurBookGUI]->CurBook(this); + fBookGUIs[fCurBookGUI]->SetCurrSize(fWidthScale, fHeightScale); + ILoadAllImages( false ); + + hsGMaterial *cover = fBookGUIs[fCurBookGUI]->CoverMaterial(); + if( cover != nil ) + { + hsTArray layers; + plMipmap *mip = fCoverMipKey ? plMipmap::ConvertNoRef( fCoverMipKey->ObjectIsLoaded() ) : nil; + if( mip != nil ) + { + layers.Append(IMakeBaseLayer(mip)); + + int i; + for (i=0; ifType == pfEsHTMLChunk::kDecal) + { + plMipmap *decal = plMipmap::ConvertNoRef( fCoverDecals[i]->fImageKey != nil ? fCoverDecals[i]->fImageKey->ObjectIsLoaded() : nil ); + if (decal != nil) + layers.Append(IMakeDecalLayer(fCoverDecals[i],decal,mip)); + } + else + { + // it's a cover movie, not a decal, so we make a layer, thinking it's at 0,0 and a left map (which gives us the results we want) + plLayerBink *movieLayer = IMakeMovieLayer(fCoverDecals[i],0,0,mip,pfJournalDlgProc::kTagLeftDTMap,false); + loadedMovie *movie = TRACKED_NEW loadedMovie; + movie->movieLayer = movieLayer; + movie->movieChunk = fCoverDecals[i]; + fLoadedMovies.Append(movie); + layers.Append(plLayerInterface::ConvertNoRef(movieLayer)); + fVisibleLinks.Reset(); // remove any links that the make movie layer might have added, since a cover movie can't link + } + } + ISetDecalLayers(cover,layers); + } + else + { + layers.Append(IMakeBaseLayer(fBookGUIs[fCurBookGUI]->DefaultCover())); + ISetDecalLayers(cover,layers); + } + // release our ref on the cover layers since the material will take care of them now + int i; + for (i=0; iRelease(layers[i]->GetKey()); + } + +// fInstance->IPlayBookCloseAnim( !startOpened, true ); + fBookGUIs[fCurBookGUI]->TurnPageButton()->SetVisible( false ); + ITriggerCloseWithNotify( !startOpened, true ); +} + +//// IFinishShow ///////////////////////////////////////////////////////////// +// Finish showing the book, due to the animation being done seeking + +void pfJournalBook::IFinishShow( hsBool startOpened ) +{ + fBookGUIs[fCurBookGUI]->Dialog()->Show(); + + fAreWeShowing = true; + + if( startOpened ) + { + // Render initial pages + fCurrentPage = 0; + fVisibleLinks.Reset(); + IRenderPage( 0, pfJournalDlgProc::kTagLeftDTMap ); + IRenderPage( 1, pfJournalDlgProc::kTagRightDTMap ); + + fBookGUIs[fCurBookGUI]->UpdatePageCorners( pfBookData::kBothSides ); + } + + ISendNotify( kNotifyShow ); +} + +//// Hide //////////////////////////////////////////////////////////////////// + +void pfJournalBook::Hide( void ) +{ + if (fBookGUIs[fCurBookGUI]) + { + // we can only hide our own dialog + if (fBookGUIs[fCurBookGUI]->CurBook() == this ) + { + if (fBookGUIs[fCurBookGUI]->Dialog()) + fBookGUIs[fCurBookGUI]->Dialog()->Hide(); + fBookGUIs[fCurBookGUI]->CurBook(nil); + ISendNotify( kNotifyHide ); + ILoadAllImages( true ); + // purge the dynaTextMaps, we're done with them for now + IPurgeDynaTextMaps(); + // nuke the movies so they don't stay in memory (they're big!) + int i; + for( i = 0; i < fLoadedMovies.GetCount(); i++ ) + { + plLayerBink *movie = fLoadedMovies[ i ]->movieLayer; + movie->GetKey()->UnRefObject(); + delete fLoadedMovies[ i ]; + } + fLoadedMovies.Reset(); + } + } + +} + +//// Open //////////////////////////////////////////////////////////////////// +// Opens the book, optionally to the given page + +void pfJournalBook::Open( UInt32 startingPage /*= 0 */) +{ + if( !fBookGUIs[fCurBookGUI]->CurrentlyOpen() ) + { + fBookGUIs[fCurBookGUI]->PlayBookCloseAnim( false ); + + // Render initial pages + fCurrentPage = startingPage; + fVisibleLinks.Reset(); + IRenderPage( startingPage, pfJournalDlgProc::kTagLeftDTMap ); + IRenderPage( startingPage + 1, pfJournalDlgProc::kTagRightDTMap ); + + fBookGUIs[fCurBookGUI]->UpdatePageCorners( pfBookData::kBothSides ); + } +} + +//// Close /////////////////////////////////////////////////////////////////// +// Closes the book. + +void pfJournalBook::Close( void ) +{ + // don't allow them to close the book if the book started open + if( !fBookGUIs[fCurBookGUI]->StartedOpen() && fBookGUIs[fCurBookGUI]->CurrentlyOpen() ) + { + ISendNotify( kNotifyClose ); + fBookGUIs[fCurBookGUI]->PlayBookCloseAnim( true ); + } +} + +//// CloseAndHide //////////////////////////////////////////////////////////// +// Closes the book, then calls Hide() once it's done closing + +void pfJournalBook::CloseAndHide( void ) +{ + // if they start with the book open, then don't allow them to close it + if( !fBookGUIs[fCurBookGUI]->StartedOpen() && fBookGUIs[fCurBookGUI]->CurrentlyOpen() ) + { + // if we are flipping a book then kill the page flipping animation + if ( fBookGUIs[fCurBookGUI]->CurrentlyTurning() ) + fBookGUIs[fCurBookGUI]->KillPageFlip(); + ISendNotify( kNotifyClose ); + + ITriggerCloseWithNotify( true, false ); + + // Don't hide until we get the callback! + } + else + // Already closed, just hide + Hide(); +} + +//// ITriggerCloseWithNotify ///////////////////////////////////////////////// +// Close with a notify + +void pfJournalBook::ITriggerCloseWithNotify( hsBool closeNotOpen, hsBool immediate ) +{ + // Disable the book cover button if we're opening, enable otherwise + fBookGUIs[fCurBookGUI]->CoverButton()->SetEnabled( closeNotOpen ); + + // Do the animation manually so we can get a callback + fBookGUIs[fCurBookGUI]->CurrentlyOpen(!closeNotOpen); + + const hsTArray &keys = fBookGUIs[fCurBookGUI]->CoverButton()->GetAnimationKeys(); + const char *animName = fBookGUIs[fCurBookGUI]->CoverButton()->GetAnimationName(); + + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg(); + if( !immediate ) + { + msg->SetCmd( plAnimCmdMsg::kContinue ); + msg->SetCmd( closeNotOpen ? plAnimCmdMsg::kSetBackwards : plAnimCmdMsg::kSetForewards ); + } + else + { + msg->SetCmd( plAnimCmdMsg::kStop ); + msg->SetCmd( closeNotOpen ? plAnimCmdMsg::kGoToBegin : plAnimCmdMsg::kGoToEnd ); + } + msg->SetAnimName( animName ); + msg->AddReceivers( keys ); + + plEventCallbackMsg *eventMsg = TRACKED_NEW plEventCallbackMsg; + eventMsg->AddReceiver( fBookGUIs[fCurBookGUI]->GetKey() ); + eventMsg->fRepeats = 0; + eventMsg->fUser = 0x08 | ( closeNotOpen ? 0x00 : 0x01 ); // So we know which this is for + eventMsg->fEvent = immediate ? ( kSingleFrameEval ) : kStop; + msg->SetCmd( plAnimCmdMsg::kAddCallbacks ); + msg->AddCallback( eventMsg ); + hsRefCnt_SafeUnRef( eventMsg ); + + msg->Send(); + + // Trigger the open (or close) sound + if( !immediate ) + fBookGUIs[fCurBookGUI]->CoverButton()->PlaySound(closeNotOpen ? pfGUICheckBoxCtrl::kMouseUp : pfGUICheckBoxCtrl::kMouseDown); +} + +//// NextPage //////////////////////////////////////////////////////////////// +// Advances forward one page + +void pfJournalBook::NextPage( void ) +{ + if( (fBookGUIs[fCurBookGUI]->CurrentlyTurning()) || (!fAllowTurning) || (!fAreWeShowing) ) + return; + + if ((fAreEditing)&&!(fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagRightEditCtrl)->ShowingEndOfBuffer())) // we're editing the book, so page turning is different here + { + fCurrentPage += 2; // we just go to the next page, an editable book has "infinite" pages anyway + + pfGUIMultiLineEditCtrl *right = fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagRightEditCtrl); + pfGUIMultiLineEditCtrl *left = fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagLeftEditCtrl); + pfGUIMultiLineEditCtrl *turnFront = fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnFrontEditCtrl); + pfGUIMultiLineEditCtrl *turnBack = fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnBackEditCtrl); + // re-link the controls with the turn page in the middle + left->SetNext(turnFront); + turnBack->SetNext(right); + + // At this point, only the left and right pages are visible, we don't want to actually update anything until the animation + // starts so that nothing flashes or overdraws. + + fBookGUIs[fCurBookGUI]->StartTriggeredFlip( false ); + fBookGUIs[fCurBookGUI]->TurnPageButton()->PlaySound( pfGUICheckBoxCtrl::kMouseUp ); + ISendNotify(kNotifyNextPage); + } + else if( fCurrentPage + 2 <= fLastPage ) + { + fCurrentPage += 2; + fVisibleLinks.Reset(); + + // Swap the right DT map into the turn page front DTMap, then render + // the new current page into turn page back and currPage+1 into + // the right DTMap + plDynamicTextMap *turnFront = fBookGUIs[fCurBookGUI]->GetDTMap( pfJournalDlgProc::kTagTurnFrontDTMap ); + plDynamicTextMap *right = fBookGUIs[fCurBookGUI]->GetDTMap( pfJournalDlgProc::kTagRightDTMap ); + if ( turnFront->IsValid() && right->IsValid() ) + { + memcpy( turnFront->GetImage(), right->GetImage(), right->GetLevelSize( 0 ) ); + if( turnFront->GetDeviceRef() != nil ) + turnFront->GetDeviceRef()->SetDirty( true ); + } + // copy the videos over + IMoveMovies( fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kRightPage), fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kTurnFrontPage) ); + IRenderPage( fCurrentPage, pfJournalDlgProc::kTagTurnBackDTMap ); + + // This will fire a callback when it's done that'll let us continue the setup + fBookGUIs[fCurBookGUI]->StartTriggeredFlip( false ); + + // Play us a sound too, if defined on our button + fBookGUIs[fCurBookGUI]->TurnPageButton()->PlaySound( pfGUICheckBoxCtrl::kMouseUp ); + + ISendNotify( kNotifyNextPage ); + } +} + +//// PreviousPage //////////////////////////////////////////////////////////// +// Same, only back + +void pfJournalBook::PreviousPage( void ) +{ + if(( fBookGUIs[fCurBookGUI]->CurrentlyTurning() )||( !fAllowTurning )) + return; + + if (fAreEditing) // we're editing the book, so page turning is different here + { + if (!(fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagLeftEditCtrl)->ShowingBeginningOfBuffer())) // make sure we don't flip past the beginning + { + if (fCurrentPage >= 2) // this variable can get out of whack if we open the book to a page in the middle + fCurrentPage -= 2; // just making sure that this doesn't go below zero (and therefore wrap around) + + pfGUIMultiLineEditCtrl *right = fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagRightEditCtrl); + pfGUIMultiLineEditCtrl *left = fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagLeftEditCtrl); + pfGUIMultiLineEditCtrl *turnFront = fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnFrontEditCtrl); + pfGUIMultiLineEditCtrl *turnBack = fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnBackEditCtrl); + // adjust the starting position of the left control to the new start + Int32 newStartLine = left->GetFirstVisibleLine() - ((left->GetNumVisibleLines()-1)*2); + left->SetGlobalStartLine(newStartLine); + // re-link the controls with the turn page in the middle + left->SetNext(turnFront); + turnBack->SetNext(right); + + // At this point, only the left and right pages are visible, we don't want to actually update anything until the animation + // starts so that nothing flashes or overdraws. + + fBookGUIs[fCurBookGUI]->StartTriggeredFlip( true ); + fBookGUIs[fCurBookGUI]->TurnPageButton()->PlaySound( pfGUICheckBoxCtrl::kMouseUp ); + ISendNotify(kNotifyPreviousPage); + } + else + { + Close(); + } + } + else if( fCurrentPage > 1 ) + { + fCurrentPage -= 2; + fVisibleLinks.Reset(); + + // Swap the left DT map into the turn page back DTMap, then render + // the new current page into the left and currPage+1 into + // the turn page front DTMap + plDynamicTextMap *turnBack = fBookGUIs[fCurBookGUI]->GetDTMap( pfJournalDlgProc::kTagTurnBackDTMap ); + plDynamicTextMap *left = fBookGUIs[fCurBookGUI]->GetDTMap( pfJournalDlgProc::kTagLeftDTMap ); + if ( turnBack->IsValid() && left->IsValid() ) + { + memcpy( turnBack->GetImage(), left->GetImage(), left->GetLevelSize( 0 ) ); + if( turnBack->GetDeviceRef() != nil ) + turnBack->GetDeviceRef()->SetDirty( true ); + } + // copy the videos over + IMoveMovies( fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kLeftPage), fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kTurnBackPage) ); + IRenderPage( fCurrentPage + 1, pfJournalDlgProc::kTagTurnFrontDTMap ); + + // This will fire a callback when it's done that'll let us continue the setup + fBookGUIs[fCurBookGUI]->StartTriggeredFlip( true ); + + // Play us a sound too, if defined on our button + fBookGUIs[fCurBookGUI]->TurnPageButton()->PlaySound( pfGUICheckBoxCtrl::kMouseUp ); + + ISendNotify( kNotifyPreviousPage ); + } + else + { + Close(); + } +} + +//// IFindCurrVisibleLink //////////////////////////////////////////////////// +// Find the current moused link, if any + +Int32 pfJournalBook::IFindCurrVisibleLink( hsBool rightNotLeft, hsBool hoverNotUp ) +{ + pfGUIClickMapCtrl *ctrl = ( rightNotLeft ) ? fBookGUIs[fCurBookGUI]->RightPageMap() : fBookGUIs[fCurBookGUI]->LeftPageMap(); + + hsPoint3 pt = hoverNotUp ? ctrl->GetLastMousePt() : ctrl->GetLastMouseUpPt(); + + // This should be 0-1 in the context of the control, so scale to our DTMap size + plDynamicTextMap *dtMap = fBookGUIs[fCurBookGUI]->GetDTMap( rightNotLeft ? pfJournalDlgProc::kTagRightDTMap : pfJournalDlgProc::kTagLeftDTMap ); + pt.fX *= (hsScalar)dtMap->GetWidth(); + pt.fY *= (hsScalar)dtMap->GetHeight(); + if( rightNotLeft ) + { + // Clicks on the right side are offsetted in x by the left side's width + dtMap = fBookGUIs[fCurBookGUI]->GetDTMap( pfJournalDlgProc::kTagLeftDTMap ); + pt.fX += dtMap->GetWidth(); + } + + // Search through the list of visible hotspots + UInt32 i; + for( i = 0; i < fVisibleLinks.GetCount(); i++ ) + { + if( fVisibleLinks[ i ]->fLinkRect.Contains( (Int16)pt.fX, (Int16)pt.fY ) ) + { + // Found a visible link + return (Int32)i; + } + } + + return -1; +} + +//// IHandleLeftSideClick //////////////////////////////////////////////////// + +void pfJournalBook::IHandleLeftSideClick( void ) +{ + if( fBookGUIs[fCurBookGUI]->CurrentlyTurning() ) + return; + + Int32 idx = IFindCurrVisibleLink( false, false ); + if( idx != -1 ) + { + if( fVisibleLinks[ idx ]->fFlags & pfEsHTMLChunk::kActAsCB ) + IHandleCheckClick( idx, pfBookData::kLeftSide ); + else + ISendNotify( kNotifyImageLink, fVisibleLinks[ idx ]->fEventID ); + return; + } + + // No link found that we're inside of, so just do the default behavior of turning the page + PreviousPage(); +} + +void pfJournalBook::IHandleRightSideClick( void ) +{ + if( fBookGUIs[fCurBookGUI]->CurrentlyTurning() ) + return; + + Int32 idx = IFindCurrVisibleLink( true, false ); + if( idx != -1 ) + { + if( fVisibleLinks[ idx ]->fFlags & pfEsHTMLChunk::kActAsCB ) + IHandleCheckClick( idx, pfBookData::kRightSide ); + else + ISendNotify( kNotifyImageLink, fVisibleLinks[ idx ]->fEventID ); + return; + } + + // No link found that we're inside of, so just do the default behavior of turning the page + NextPage(); +} + +//// IHandleCheckClick /////////////////////////////////////////////////////// +// Process a click on the given "check box" image + +void pfJournalBook::IHandleCheckClick( UInt32 idx, pfBookData::WhichSide which ) +{ + // Special processing for checkboxes--toggle our state, switch our opacity + // and then send a notify about our new state + hsBool check = ( fVisibleLinks[ idx ]->fFlags & pfEsHTMLChunk::kChecked ) ? false : true; + if( check ) + { + fVisibleLinks[ idx ]->fFlags |= pfEsHTMLChunk::kChecked; +// fVisibleLinks[ idx ]->fCurrOpacity = fVisibleLinks[ idx ]->fMaxOpacity; + } + else + { + fVisibleLinks[ idx ]->fFlags &= ~pfEsHTMLChunk::kChecked; +// fVisibleLinks[ idx ]->fCurrOpacity = fVisibleLinks[ idx ]->fMinOpacity; + } + + // Re-render the page we're on, to show the change in state + IRenderPage( fCurrentPage + ( ( which == pfBookData::kLeftSide ) ? 0 : 1 ), ( which == pfBookData::kLeftSide ) ? pfJournalDlgProc::kTagLeftDTMap: pfJournalDlgProc::kTagRightDTMap ); + + ISendNotify( check ? kNotifyImageLink : kNotifyCheckUnchecked, fVisibleLinks[ idx ]->fEventID ); + + // Register for FX processing, so we can fade the checkbox in + fBookGUIs[fCurBookGUI]->RegisterForSFX( (pfBookData::WhichSide)( fBookGUIs[fCurBookGUI]->CurSFXPages() | which ) ); +} + +//// GoToPage //////////////////////////////////////////////////////////////// +// For completeness... + +void pfJournalBook::GoToPage( UInt32 pageNumber ) +{ + // Put us here, but only on an even page (odd pages go on the right, y'know) + if (pageNumber < fPageStarts.Count()) + fCurrentPage = pageNumber & ~0x00000001; + else + fCurrentPage = 0; + fVisibleLinks.Reset(); + IRenderPage( fCurrentPage, pfJournalDlgProc::kTagLeftDTMap ); + IRenderPage( fCurrentPage + 1, pfJournalDlgProc::kTagRightDTMap ); + fBookGUIs[fCurBookGUI]->UpdatePageCorners( pfBookData::kBothSides ); +} + +//// SetEditable ///////////////////////////////////////////////////////////// + +void pfJournalBook::SetEditable(hsBool editable) +{ + if (fBookGUIs[fCurBookGUI]->IsEditable()) // make sure this GUI supports editing + { + fBookGUIs[fCurBookGUI]->EnableEditGUI(editable); + fAreEditing = editable; // we may be editing the book, so change rendering/page flipping methods + if (editable) + fLastPage = 0; // setting this to 0 since editable books don't know what the last page is (it always changes) + } + else + fWantEditing = editable; // we want to edit, but the gui doesn't support it, check again if the GUI changes +}; + +//// ForceCacheCalculations ////////////////////////////////////////////////// +// Just forces a full calc of the cached info + +void pfJournalBook::ForceCacheCalculations( void ) +{ + // Make sure our page starts are up-to-snuff, at least to this point + IRecalcPageStarts( -1 ); +} + +// Tiny helper to convert hex values the *right* way +static UInt32 IConvertHex( const wchar_t *str ) +{ + UInt32 value = 0; + while( *str != 0 ) + { + value <<= 4; + switch( *str ) + { + case L'0': value |= 0x0; break; + case L'1': value |= 0x1; break; + case L'2': value |= 0x2; break; + case L'3': value |= 0x3; break; + case L'4': value |= 0x4; break; + case L'5': value |= 0x5; break; + case L'6': value |= 0x6; break; + case L'7': value |= 0x7; break; + case L'8': value |= 0x8; break; + case L'9': value |= 0x9; break; + case L'a': case L'A': value |= 0xa; break; + case L'b': case L'B': value |= 0xb; break; + case L'c': case L'C': value |= 0xc; break; + case L'd': case L'D': value |= 0xd; break; + case L'e': case L'E': value |= 0xe; break; + case L'f': case L'F': value |= 0xf; break; + } + str++; + } + + return value; +} + +//// ICompileSource ////////////////////////////////////////////////////////// +// Compiles the given string of esHTML source into our compiled chunk list + +hsBool pfJournalBook::ICompileSource( const wchar_t *source, const plLocation &hintLoc ) +{ + IFreeSource(); + + + pfEsHTMLChunk *chunk, *lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil ); + const wchar_t *c, *start; + wchar_t name[ 128 ], option[ 256 ]; + float bookWidth=1.0, bookHeight=1.0; + UInt8 movieIndex = 0; // the index of a movie in the source (used for id purposes) + + plKey anotherKey; + + + // Parse our source! + for( start = c = source; *c != 0; ) + { + // Are we on a tag? + UInt8 type = IGetTagType( c ); + if( type != pfEsHTMLChunk::kEmpty ) + { + // First, end the current paragraph chunk, which is a special case 'cause its + // text is defined outside the tag + if( start == c ) + { + // No actual text, just delete + delete lastParChunk; + lastParChunk = nil; + } + else if( lastParChunk != nil ) + { + UInt32 count = ((UInt32)c - (UInt32)start)/2; // wchar_t is 2 bytes + + wchar_t *temp = TRACKED_NEW wchar_t[ count + 1 ]; + wcsncpy( temp, start, count ); + temp[count] = L'\0'; + lastParChunk->fText = temp; + delete [] temp; + + // Special case to remove any last trailing carriage return +// if( count > 1 && lastParChunk->fText[ count - 1 ] == '\n' ) +// lastParChunk->fText[ count - 1 ] = 0; + + fHTMLSource.Append( lastParChunk ); + } + + // What chunk are we making now? + switch( type ) + { + case pfEsHTMLChunk::kParagraph: + c += 2; + chunk = TRACKED_NEW pfEsHTMLChunk( nil ); + chunk->fFlags = IFindLastAlignment(); + while( IGetNextOption( c, name, option ) ) + { + if( wcsicmp( name, L"align" ) == 0 ) + { + if( wcsicmp( option, L"left" ) == 0 ) + chunk->fFlags = pfEsHTMLChunk::kLeft; + else if( wcsicmp( option, L"center" ) == 0 ) + chunk->fFlags = pfEsHTMLChunk::kCenter; + else if( wcsicmp( option, L"right" ) == 0 ) + chunk->fFlags = pfEsHTMLChunk::kRight; + } + } + // Append text to this one (don't add to source just yet) + lastParChunk = chunk; + break; + + case pfEsHTMLChunk::kImage: + c += 4; + chunk = TRACKED_NEW pfEsHTMLChunk( nil, 0 ); + while( IGetNextOption( c, name, option ) ) + { + if( wcsicmp( name, L"align" ) == 0 ) + { + chunk->fFlags &= ~pfEsHTMLChunk::kAlignMask; + if( wcsicmp( option, L"left" ) == 0 ) + chunk->fFlags |= pfEsHTMLChunk::kLeft; + else if( wcsicmp( option, L"center" ) == 0 ) + chunk->fFlags |= pfEsHTMLChunk::kCenter; + else if( wcsicmp( option, L"right" ) == 0 ) + chunk->fFlags |= pfEsHTMLChunk::kRight; + } + else if( wcsicmp( name, L"src" ) == 0 ) + { + // Name of mipmap source + chunk->fImageKey = IGetMipmapKey( option, hintLoc ); + } + else if( wcsicmp( name, L"link" ) == 0 ) + { + chunk->fEventID = _wtoi( option ); + chunk->fFlags |= pfEsHTMLChunk::kCanLink; + } + else if( wcsicmp( name, L"blend" ) == 0 ) + { + if( wcsicmp( option, L"alpha" ) == 0 ) + chunk->fFlags |= pfEsHTMLChunk::kBlendAlpha; + } + else if( wcsicmp( name, L"pos" ) == 0 ) + { + chunk->fFlags |= pfEsHTMLChunk::kFloating; + + wchar_t *comma = wcschr( option, L',' ); + if( comma != nil ) + { + + chunk->fAbsoluteY = _wtoi( comma + 1 ); + *comma = 0; + } + chunk->fAbsoluteX = _wtoi( option ); + } + else if( wcsicmp( name, L"glow" ) == 0 ) + { + chunk->fFlags |= pfEsHTMLChunk::kGlowing; + chunk->fFlags &= ~pfEsHTMLChunk::kActAsCB; + + char *cOption = hsWStringToString(option); + char *comma = strchr( cOption, ',' ); + if( comma != nil ) + { + char *comma2 = strchr( comma + 1, ',' ); + if( comma2 != nil ) + { + chunk->fMaxOpacity = (hsScalar)atof( comma2 + 1 ); + *comma2 = 0; + } + chunk->fMinOpacity = (hsScalar)atof( comma + 1 ); + *comma = 0; + } + chunk->fSFXTime = (hsScalar)atof( cOption ); + delete [] cOption; + } + else if( wcsicmp( name, L"opacity" ) == 0 ) + { + chunk->fFlags |= pfEsHTMLChunk::kTranslucent; + char *cOption = hsWStringToString(option); + chunk->fCurrOpacity = (hsScalar)atof( cOption ); + delete [] cOption; + } + else if( wcsicmp( name, L"check" ) == 0 ) + { + chunk->fFlags |= pfEsHTMLChunk::kActAsCB; + chunk->fFlags &= ~pfEsHTMLChunk::kGlowing; + + wchar_t *comma = wcschr( option, L',' ); + if( comma != nil ) + { + wchar_t *comma2 = wcschr( comma + 1, L',' ); + if( comma2 != nil ) + { + if( _wtoi( comma2 + 1 ) != 0 ) + chunk->fFlags |= pfEsHTMLChunk::kChecked; + *comma2 = 0; + } + UInt32 c = IConvertHex( comma + 1 ); + if( wcslen( comma + 1 ) <= 6 ) + c |= 0xff000000; // Add in full alpha if none specified + chunk->fOffColor.FromARGB32( c ); + *comma = 0; + } + UInt32 c = IConvertHex( option ); + if( wcslen( option ) <= 6 ) + c |= 0xff000000; // Add in full alpha if none specified + chunk->fOnColor.FromARGB32( c ); + + if( chunk->fFlags & pfEsHTMLChunk::kChecked ) + chunk->fCurrColor = chunk->fOnColor; + else + chunk->fCurrColor = chunk->fOffColor; + } + else if (wcsicmp(name,L"resize")==0) + { + if (wcsicmp(option,L"no")==0) + chunk->fNoResizeImg = true; + } + } + if( chunk->fImageKey != nil ) + fHTMLSource.Append( chunk ); + else + delete chunk; + // Start new paragraph chunk after this one + lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil ); + lastParChunk->fFlags = IFindLastAlignment(); + break; + + case pfEsHTMLChunk::kCover: + // Don't create an actual chunk for this one, just use the "src" and + // grab the mipmap key for our cover + c += 6; + while( IGetNextOption( c, name, option ) ) + { + if( wcsicmp( name, L"src" ) == 0 ) + { + // Name of mipmap source + anotherKey = IGetMipmapKey( option, hintLoc ); + if( anotherKey != nil ) + { + fCoverMipKey = anotherKey; + fCoverFromHTML = true; + } + } + if( wcsicmp( name, L"tint" ) == 0 ) + { + fTintCover = true; + fCoverTint.FromARGB32( wcstol( option, nil, 16 ) | 0xff000000 ); + } + if( wcsicmp( name, L"tintfirst" ) == 0 ) + { + if (wcsicmp(option,L"no")==0) + fTintFirst = false; + } + } + // Still gotta create a new par chunk + lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil ); + lastParChunk->fFlags = IFindLastAlignment(); + break; + + case pfEsHTMLChunk::kPageBreak: + c += 3; + chunk = TRACKED_NEW pfEsHTMLChunk(); + while( IGetNextOption( c, name, option ) ) + { + } + fHTMLSource.Append( chunk ); + // Start new paragraph chunk after this one + lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil ); + lastParChunk->fFlags = IFindLastAlignment(); + break; + + case pfEsHTMLChunk::kFontChange: + c += 5; + chunk = TRACKED_NEW pfEsHTMLChunk( nil, 0, 0 ); + while( IGetNextOption( c, name, option ) ) + { + if( wcsicmp( name, L"style" ) == 0 ) + { + UInt8 guiFlags = 0; + if( wcsicmp( option, L"b" ) == 0 ) + { + chunk->fFlags = pfEsHTMLChunk::kFontBold; + guiFlags = plDynamicTextMap::kFontBold; + } + else if( wcsicmp( option, L"i" ) == 0 ) + { + chunk->fFlags = pfEsHTMLChunk::kFontItalic; + guiFlags = plDynamicTextMap::kFontItalic; + } + else if( wcsicmp( option, L"bi" ) == 0 ) + { + chunk->fFlags = pfEsHTMLChunk::kFontBold | pfEsHTMLChunk::kFontItalic; + guiFlags = plDynamicTextMap::kFontBold | plDynamicTextMap::kFontItalic; + } + else + chunk->fFlags = pfEsHTMLChunk::kFontRegular; + if (fBookGUIs[fCurBookGUI]->IsEditable()) + { + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagRightEditCtrl)->SetFontStyle(guiFlags); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagLeftEditCtrl)->SetFontStyle(guiFlags); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnFrontEditCtrl)->SetFontStyle(guiFlags); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnBackEditCtrl)->SetFontStyle(guiFlags); + } + } + else if( wcsicmp( name, L"face" ) == 0 ) + { + // Name of mipmap source + chunk->fText = option; + if (fBookGUIs[fCurBookGUI]->IsEditable()) + { + char *fontFace = hsWStringToString(option); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagRightEditCtrl)->SetFontFace(fontFace); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagLeftEditCtrl)->SetFontFace(fontFace); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnFrontEditCtrl)->SetFontFace(fontFace); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnBackEditCtrl)->SetFontFace(fontFace); + delete [] fontFace; + } + } + else if( wcsicmp( name, L"size" ) == 0 ) + { + chunk->fFontSize = _wtoi( option ); + if (fBookGUIs[fCurBookGUI]->IsEditable()) + { + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagRightEditCtrl)->SetFontSize(chunk->fFontSize); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagLeftEditCtrl)->SetFontSize(chunk->fFontSize); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnFrontEditCtrl)->SetFontSize(chunk->fFontSize); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnBackEditCtrl)->SetFontSize(chunk->fFontSize); + } + } + else if( wcsicmp( name, L"color" ) == 0 ) + { + chunk->fColor.FromARGB32( wcstol( option, nil, 16 ) | 0xff000000 ); + chunk->fFlags |= pfEsHTMLChunk::kFontColor; + if (fBookGUIs[fCurBookGUI]->IsEditable()) + { + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagRightEditCtrl)->SetFontColor(chunk->fColor); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagLeftEditCtrl)->SetFontColor(chunk->fColor); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnFrontEditCtrl)->SetFontColor(chunk->fColor); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnBackEditCtrl)->SetFontColor(chunk->fColor); + } + } + else if( wcsicmp( name, L"spacing" ) == 0 ) + { + chunk->fLineSpacing = _wtoi(option); + chunk->fFlags |= pfEsHTMLChunk::kFontSpacing; + } + } + fHTMLSource.Append( chunk ); + // Start new paragraph chunk after this one + lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil ); + lastParChunk->fFlags = IFindLastAlignment(); + break; + + case pfEsHTMLChunk::kMargin: + c += 7; + while(IGetNextOption(c,name,option)) + { + if (wcsicmp(name,L"top") == 0) + fPageTMargin = _wtoi(option); + else if (wcsicmp(name,L"left") == 0) + fPageLMargin = _wtoi(option); + else if (wcsicmp(name,L"bottom") == 0) + fPageBMargin = _wtoi(option); + else if (wcsicmp(name,L"right") == 0) + fPageRMargin = _wtoi(option); + } + // set the edit controls to the margins we just set + if (fBookGUIs[fCurBookGUI]->IsEditable()) + { + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagRightEditCtrl)->SetMargins(fPageTMargin,fPageLMargin,fPageBMargin,fPageRMargin); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagLeftEditCtrl)->SetMargins(fPageTMargin,fPageLMargin,fPageBMargin,fPageRMargin); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnFrontEditCtrl)->SetMargins(fPageTMargin,fPageLMargin,fPageBMargin,fPageRMargin); + fBookGUIs[fCurBookGUI]->GetEditCtrl(pfJournalDlgProc::kTagTurnBackEditCtrl)->SetMargins(fPageTMargin,fPageLMargin,fPageBMargin,fPageRMargin); + } + // Start a new paragraph chunk after this one + lastParChunk = TRACKED_NEW pfEsHTMLChunk(nil); + lastParChunk->fFlags = IFindLastAlignment(); + break; + + case pfEsHTMLChunk::kBook: + c += 5; + // don't actually create a chunk, just set the book size + while (IGetNextOption(c,name,option)) + { + if (wcsicmp(name,L"height") == 0) + { + char *temp = hsWStringToString(option); + bookHeight = (float)atof(temp); + delete [] temp; + } + else if (wcsicmp(name,L"width") == 0) + { + char *temp = hsWStringToString(option); + bookWidth = (float)atof(temp); + delete [] temp; + } + } + fHeightScale = 1.f - bookHeight; + fWidthScale = 1.f - bookWidth; + + // Still gotta create a new par chunk + lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil ); + lastParChunk->fFlags = IFindLastAlignment(); + break; + + case pfEsHTMLChunk::kDecal: + c += 6; + chunk = TRACKED_NEW pfEsHTMLChunk( nil, 0 ); + chunk->fType = pfEsHTMLChunk::kDecal; + while( IGetNextOption( c, name, option ) ) + { + if( wcsicmp( name, L"align" ) == 0 ) + { + chunk->fFlags &= ~pfEsHTMLChunk::kAlignMask; + if( wcsicmp( option, L"left" ) == 0 ) + chunk->fFlags |= pfEsHTMLChunk::kLeft; + else if( wcsicmp( option, L"center" ) == 0 ) + chunk->fFlags |= pfEsHTMLChunk::kCenter; + else if( wcsicmp( option, L"right" ) == 0 ) + chunk->fFlags |= pfEsHTMLChunk::kRight; + } + else if( wcsicmp( name, L"src" ) == 0 ) + { + // Name of mipmap source + chunk->fImageKey = IGetMipmapKey( option, hintLoc ); + } + else if( wcsicmp( name, L"pos" ) == 0 ) + { + chunk->fFlags |= pfEsHTMLChunk::kFloating; + + wchar_t *comma = wcschr( option, L',' ); + if( comma != nil ) + { + + chunk->fAbsoluteY = _wtoi( comma + 1 ); + *comma = 0; + } + chunk->fAbsoluteX = _wtoi( option ); + } + else if (wcsicmp(name,L"resize")==0) + { + if (wcsicmp(option,L"no")==0) + chunk->fNoResizeImg = true; + } + else if (wcsicmp(name,L"tint")==0) + { + if (wcsicmp(option,L"yes")==0) + chunk->fTintDecal = true; + } + } + // add it to our cover decals list (this is tag is essentially thrown away as far as the parser cares) + if( chunk->fImageKey != nil ) + fCoverDecals.Append( chunk ); + else + delete chunk; + // Start new paragraph chunk after this one + lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil ); + lastParChunk->fFlags = IFindLastAlignment(); + break; + + case pfEsHTMLChunk::kMovie: + c += 6; + chunk = TRACKED_NEW pfEsHTMLChunk( nil, 0 ); + chunk->fType = pfEsHTMLChunk::kMovie; + while( IGetNextOption( c, name, option ) ) + { + if( wcsicmp( name, L"align" ) == 0 ) + { + chunk->fFlags &= ~pfEsHTMLChunk::kAlignMask; + if( wcsicmp( option, L"left" ) == 0 ) + chunk->fFlags |= pfEsHTMLChunk::kLeft; + else if( wcsicmp( option, L"center" ) == 0 ) + chunk->fFlags |= pfEsHTMLChunk::kCenter; + else if( wcsicmp( option, L"right" ) == 0 ) + chunk->fFlags |= pfEsHTMLChunk::kRight; + } + else if( wcsicmp( name, L"src" ) == 0 ) + { + chunk->fText = option; + } + else if( wcsicmp( name, L"link" ) == 0 ) + { + chunk->fEventID = _wtoi( option ); + chunk->fFlags |= pfEsHTMLChunk::kCanLink; + } + else if( wcsicmp( name, L"pos" ) == 0 ) + { + chunk->fFlags |= pfEsHTMLChunk::kFloating; + + wchar_t *comma = wcschr( option, L',' ); + if( comma != nil ) + { + + chunk->fAbsoluteY = _wtoi( comma + 1 ); + *comma = 0; + } + chunk->fAbsoluteX = _wtoi( option ); + } + else if (wcsicmp(name,L"resize")==0) + { + if (wcsicmp(option,L"no")==0) + chunk->fNoResizeImg = true; + } + else if (wcsicmp(name,L"oncover")==0) + { + if (wcsicmp(option,L"yes")==0) + chunk->fOnCover = true; + } + else if (wcsicmp(name,L"loop")==0) + { + if (wcsicmp(option,L"no")==0) + chunk->fLoopMovie = false; + } + } + chunk->fMovieIndex = movieIndex; + movieIndex++; + if (chunk->fOnCover) + { + if( chunk->fText != L"" ) + fCoverDecals.Append( chunk ); + else + delete chunk; + } + else + { + if( chunk->fText != L"" ) + fHTMLSource.Append( chunk ); + else + delete chunk; + } + // Start new paragraph chunk after this one + lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil ); + lastParChunk->fFlags = IFindLastAlignment(); + break; + + case pfEsHTMLChunk::kEditable: + c += 9; + SetEditable(true); + chunk = TRACKED_NEW pfEsHTMLChunk(); + while( IGetNextOption( c, name, option ) ) + { + } + fHTMLSource.Append( chunk ); + // Start new paragraph chunk after this one + lastParChunk = TRACKED_NEW pfEsHTMLChunk( nil ); + lastParChunk->fFlags = IFindLastAlignment(); + break; + } + + start = c; + } + else + { + // Keep looking + c++; + } + } + + // Final bit goes into the last paragraph chunk we had + if( start == c ) + { + // No actual text, just delete + delete lastParChunk; + lastParChunk = nil; + } + else if( lastParChunk != nil ) + { + UInt32 count = (UInt32)c - (UInt32)start; + + wchar_t *temp = TRACKED_NEW wchar_t[ count + 1 ]; + wcsncpy( temp, start, count + 1 ); + lastParChunk->fText = temp; + delete [] temp; + + // Special case to remove any last trailing carriage return +// if( count > 1 && lastParChunk->fText[ count - 1 ] == '\n' ) +// lastParChunk->fText[ count - 1 ] = 0; + + fHTMLSource.Append( lastParChunk ); + } + + // Reset a few + fPageStarts.Reset(); + fPageStarts.Append( 0 ); + if (fAreEditing) + fLastPage = 0; + else + fLastPage = -1; + + return true; +} + +UInt8 pfJournalBook::IGetTagType( const wchar_t *string ) +{ + if( string[ 0 ] != '<' ) + return pfEsHTMLChunk::kEmpty; + + struct TagRec + { + const wchar_t *fTag; + UInt8 fType; + } tags[] = { { L"p", pfEsHTMLChunk::kParagraph }, + { L"img", pfEsHTMLChunk::kImage }, + { L"pb", pfEsHTMLChunk::kPageBreak }, + { L"font", pfEsHTMLChunk::kFontChange }, + { L"margin", pfEsHTMLChunk::kMargin }, + { L"cover", pfEsHTMLChunk::kCover }, + { L"book", pfEsHTMLChunk::kBook }, + { L"decal", pfEsHTMLChunk::kDecal }, + { L"movie", pfEsHTMLChunk::kMovie }, + { L"editable", pfEsHTMLChunk::kEditable }, + { nil, pfEsHTMLChunk::kEmpty } }; + + + UInt32 i; + for( i = 0; tags[ i ].fTag != nil; i++ ) + { + if( wcsnicmp( string + 1, tags[ i ].fTag, wcslen( tags[ i ].fTag ) ) == 0 ) + { + // Found tag--but only space or end tag marker allowed afterwards + char end = (char)string[ wcslen( tags[ i ].fTag ) + 1 ]; + if( end == '>' || end == ' ' ) + return tags[ i ].fType; + } + } + + return pfEsHTMLChunk::kEmpty; +} + +hsBool pfJournalBook::IGetNextOption( const wchar_t *&string, wchar_t *name, wchar_t *option ) +{ + const wchar_t *c; + + + // Advance past any white space + while( *string == L' ' ) + string++; + + if( *string == L'>' ) + { + string++; + return false; + } + + // Advance to = + c = string; + while( *string != L'>' && *string != L' ' && *string != L'=' && *string != L'\0' ) + string++; + + if( *string != L'=' ) + return false; + + // Copy name + UInt32 len = ((UInt32)string - (UInt32)c)/2; // divide length by 2 because each character is two bytes + wcsncpy( name, c, len ); + name[len] = L'\0'; + + // Find start of option value + string++; + while( *string == L' ' ) + string++; + + if( *string == L'\0' || *string == L'>' ) + return false; + + if( *string == L'\"' ) + { + // Search for other quote + string++; + c = string; + while( *string != L'>' && *string != L'\"' && *string != L'\0' ) + string++; + + len = ((UInt32)string - (UInt32)c)/2; // divide length by 2 because each character is two bytes + wcsncpy( option, c, len ); + option[len] = L'\0'; + + if( *string == L'\"' ) + string++; + + return true; + } + + // Non-quoted token + c = string; + while( *string != L' ' && *string != L'>' && *string != L'\0' ) + string++; + + len = ((UInt32)string - (UInt32)c)/2; // divide length by 2 because each character is two bytes + wcsncpy( option, c, len ); + option[len] = L'\0'; + + return true; +} + +void pfJournalBook::IFreeSource( void ) +{ + UInt32 i; + + for( i = 0; i < fHTMLSource.GetCount(); i++ ) + delete fHTMLSource[ i ]; + fHTMLSource.Reset(); + + for( i = 0; i < fCoverDecals.GetCount(); i++ ) + delete fCoverDecals[ i ]; + fCoverDecals.Reset(); + + for( i = 0; i < fLoadedMovies.GetCount(); i++ ) + { + plLayerBink *movie = fLoadedMovies[ i ]->movieLayer; + movie->GetKey()->UnRefObject(); + delete fLoadedMovies[ i ]; + } + fLoadedMovies.Reset(); +} + +//// IGetMipmapKey /////////////////////////////////////////////////////////// +// Looks up the key for a mipmap given the image name. Note that the given +// location is treated as a hint; if the image isn't found in that location, +// the code will attempt to look in the currently loaded age for a matching +// image name. + +#ifndef PLASMA_EXTERNAL_RELEASE +#include "../plJPEG/plJPEG.h" +#endif + +plKey pfJournalBook::IGetMipmapKey( const wchar_t *name, const plLocation &loc ) +{ + char *cName = hsWStringToString(name); +#ifndef PLASMA_EXTERNAL_RELEASE + if( strchr( cName, '/' ) != nil || strchr( cName, '\\' ) != nil ) + { + // For internal use only--we allow local path names of JPEG images, to + // facilitate fast prototyping + plMipmap *mip = plJPEG::Instance().ReadFromFile( cName ); + hsgResMgr::ResMgr()->NewKey( cName, mip, loc ); + delete [] cName; + return mip->GetKey(); + } +#endif + + // Try first to find in the given location + plUoid myUoid( loc, plMipmap::Index(), cName ); + plKey key = hsgResMgr::ResMgr()->FindKey( myUoid ); + if( key != nil ) + { + delete [] cName; + return key; + } + + + // Next, try our "global" pre-defined age + const plLocation &globLoc = plKeyFinder::Instance().FindLocation( "GUI", "BkBookImages" ); + myUoid = plUoid( globLoc, plMipmap::Index(), cName ); + key = hsgResMgr::ResMgr()->FindKey( myUoid ); + if( key != nil ) + { + delete [] cName; + return key; + } + + // Do a search through our current age with just the name given + if( plNetClientMgr::GetInstance() != nil ) + { + const char *thisAge = plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeName(); + if( thisAge != nil ) + { + key = plKeyFinder::Instance().StupidSearch( thisAge, nil, plMipmap::Index(), cName, true ); + if( key != nil ) + { + delete [] cName; + return key; + } + } + } + + delete [] cName; + return nil; +} + +//// IRenderPage ///////////////////////////////////////////////////////////// +// Takes the given page out of the source and renders it into the specified +// DTMap (by GUI tag ID). If no page is cached after this one, also updates +// the various cached info about page endings, etc. + +void pfJournalBook::IRenderPage( UInt32 page, UInt32 whichDTMap, hsBool suppressRendering /*= false*/ ) +{ + if (fAreEditing) + return; // we don't render if we are editing the book + + // Grab the DTMap via the GUI system + plDynamicTextMap *dtMap = fBookGUIs[fCurBookGUI]->GetDTMap( whichDTMap ); + hsAssert( dtMap != nil, "Invalid DT map in IRenderPage()" ); + + loadedMovie *movie = nil; + bool movieAlreadyLoaded = false; + + // Make sure our page starts are up-to-snuff, at least to this point + IRecalcPageStarts( page ); + + // Render! + hsColorRGBA color; + color.Set( 0, 0, 0, 0 ); + if( !suppressRendering ) + dtMap->ClearToColor( color ); + + hsGMaterial *material = nil; + if (whichDTMap == pfJournalDlgProc::kTagLeftDTMap) + material = fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kLeftPage); + else if (whichDTMap == pfJournalDlgProc::kTagRightDTMap) + material = fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kRightPage); + else if (whichDTMap == pfJournalDlgProc::kTagTurnFrontDTMap) + material = fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kTurnFrontPage); + else if (whichDTMap == pfJournalDlgProc::kTagTurnBackDTMap) + material = fBookGUIs[fCurBookGUI]->PageMaterial(pfBookData::kTurnBackPage); + + if (material) + { + // clear any exiting layers (movies) from the material + int i; + for( i = 0; i < material->GetNumLayers(); i++ ) // remove all plLayerBink layers + { + plLayerInterface *matLayer = material->GetLayer(i); + plLayerBink *bink = plLayerBink::ConvertNoRef(matLayer); + if (bink) // if it was a bink layer + { + plMatRefMsg* refMsg = TRACKED_NEW plMatRefMsg(material->GetKey(), plRefMsg::kOnRemove, i, plMatRefMsg::kLayer); // remove it + hsgResMgr::ResMgr()->SendRef(material->GetLayer(i)->GetKey(), refMsg, plRefFlags::kActiveRef); + } + } + } + + hsAssert(page < fPageStarts.GetCount(), "UnInitialized page start!"); + if( page <= fLastPage + && page < fPageStarts.GetCount()) // Added this as a crash-prevention bandaid - MT + { + UInt32 idx; + UInt16 width, height, y, x, ascent, lastX, lastY; + + UInt8 fontFlags, fontSize; + const wchar_t *fontFace; + hsColorRGBA fontColor; + Int16 fontSpacing; + hsBool needSFX = false; + + // Find and set initial font properties + IFindFontProps( fPageStarts[ page ], fontFace, fontSize, fontFlags, fontColor, fontSpacing ); + dtMap->SetFont( fontFace, fontSize, fontFlags, false ); + dtMap->SetTextColor( fontColor, true ); + dtMap->SetLineSpacing(fontSpacing); + + for( idx = fPageStarts[ page ], x = (UInt16)fPageLMargin, y = (UInt16)fPageTMargin; + y < (UInt16)(512 - fPageTMargin - fPageBMargin) && idx < fHTMLSource.GetCount(); idx++ ) + { + if( fPageStarts.GetCount() > page + 1 && idx == fPageStarts[ page + 1 ] ) + break; // Just go ahead and break at the start of the next page, since we already found it + + pfEsHTMLChunk *chunk = fHTMLSource[ idx ]; + + switch( chunk->fType ) + { + case pfEsHTMLChunk::kParagraph: + if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kLeft ) + { + dtMap->SetJustify( plDynamicTextMap::kLeftJustify ); + x = (UInt16)fPageLMargin; // reset X if our justification changes + } + else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kRight ) + { + dtMap->SetJustify( plDynamicTextMap::kRightJustify ); + x = (UInt16)fPageLMargin; // reset X if our justification changes + } + else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kCenter ) + { + dtMap->SetJustify( plDynamicTextMap::kCenter ); + x = (UInt16)fPageLMargin; // reset X if our justification changes + } + + dtMap->SetFirstLineIndent( (Int16)(x - fPageLMargin) ); + width = (UInt16)(512 - fPageLMargin - fPageRMargin); + height = (UInt16)(512 - fPageBMargin - y); + UInt32 lastChar; + dtMap->CalcWrappedStringSize( chunk->fText.c_str(), &width, &height, &lastChar, &ascent, &lastX, &lastY ); + width = (UInt16)(512 - fPageLMargin - fPageRMargin); + if( !suppressRendering ) + dtMap->DrawWrappedString( (UInt16)fPageLMargin, y, chunk->fText.c_str(), width, (UInt16)(512 - fPageBMargin - y), &lastX, &lastY ); + + if( lastChar == 0 ) + { + // This paragraph didn't fit on this page at *all*, so just bump it to the next + // one artificially (the -- is to account for the for loop; see image handling below) + y += 512; + if( idx > fPageStarts[ page ] ) + idx--; + break; + } + if( chunk->fText[ lastChar ] != 0 ) + { + // Didn't get to render the whole paragraph in this go, so we're going to cheat + // and split the paragraph up into two so that we can handle it properly. Note: + // this changes the chunk array beyond this point, so we need to invalidate the + // cache, but that's ok 'cause if we're doing this, it's probably invalid (or empty) + // anyway + int fTextLen = chunk->fText.length(); + wchar_t *s = TRACKED_NEW wchar_t[fTextLen+1]; + wcscpy(s,chunk->fText.c_str()); + s[fTextLen] = L'\0'; + + // Note: Makes a copy of the string + pfEsHTMLChunk *c2 = TRACKED_NEW pfEsHTMLChunk( &s[ lastChar ] ); + c2->fFlags = chunk->fFlags; + fHTMLSource.Insert( idx + 1, c2 ); + + // Clip and reallocate so we don't have two copies laying around + s[ lastChar ] = L'\0'; + chunk->fText = s; + delete [] s; + + // Invalidate our cache starting with the next page + if( fPageStarts.GetCount() > page + 1 ) + fPageStarts.SetCount( page + 1 ); + + y += 512; + break; + } + + x = lastX; + y = (UInt16)(lastY - dtMap->GetCurrFont()->GetAscent()); // Since our text is top-justified + + if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) != pfEsHTMLChunk::kLeft ) + { + // Ending X is not guaranteed to be anything useful if we're not left justified + x = (UInt16)fPageLMargin; + } + + break; + + case pfEsHTMLChunk::kImage: + { + plMipmap *mip = plMipmap::ConvertNoRef( chunk->fImageKey != nil ? chunk->fImageKey->ObjectIsLoaded() : nil ); + if( mip != nil ) + { + // First, determine if we need to be processing FX messages + if( chunk->fFlags & pfEsHTMLChunk::kGlowing ) + needSFX = true; + else if( chunk->fFlags & pfEsHTMLChunk::kActAsCB ) + { + // If our color doesn't match our checked state, we want to be fading it in + hsColorRGBA &want = ( chunk->fFlags & pfEsHTMLChunk::kChecked ) ? chunk->fOnColor : chunk->fOffColor; + if( want != chunk->fCurrColor ) + needSFX = true; + } + + if( chunk->fFlags & pfEsHTMLChunk::kFloating ) + { + // Floating image, ignore the text flow completely and just splat the image on! + IDrawMipmap( chunk, chunk->fAbsoluteX, chunk->fAbsoluteY, mip, dtMap, whichDTMap, suppressRendering ); + } + else + { + if( y + mip->GetHeight() >= 512 - fPageBMargin ) + { + // Mipmap overlaps the bottom of this page, so forcibly break so we'll + // end up marking the page break here (note that, unlike paragraphs, we + // can't really break the mipmap into two...well, OK, we could, but it + // wouldn't make much sense :) + y += (UInt16)(mip->GetHeight()); + + // Wonderful, the break breaks us from the switch(), which means the for() + // loops runs once more and increments idx. So this is to counter that. + // (We better check tho, just to make sure nobody feeds us an extra-large + // image and sends us on an infinite loop) + if( idx > fPageStarts[ page ] ) + idx--; + break; + } + if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kLeft ) + x = (UInt16)fPageLMargin; + else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kRight ) + x = (UInt16)(512 - fPageRMargin - mip->GetWidth()); + else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kCenter ) + x = (UInt16)(256 - ( mip->GetWidth() >> 1 )); + + IDrawMipmap( chunk, x, y, mip, dtMap, whichDTMap, suppressRendering ); + + y += (UInt16)(mip->GetHeight()); + x = (UInt16)fPageLMargin; + } + } + } + break; + + case pfEsHTMLChunk::kPageBreak: + // Time for some creative guesswork. See, if the user put a in at the end of a page, + // but coincidentally we already broke to a new page, then the explicit is redundant, + // so we don't want it. However, if we're at a new page *because* of a , then a new + // is intentional, so we DO want to process it. + if( idx == fPageStarts[ page ] ) + { + // Did we get here b/c the last chunk was a pb? + if( idx > 0 && fHTMLSource[ idx - 1 ]->fType != pfEsHTMLChunk::kPageBreak ) + { + // Nope, so we DO want to ignore it! + continue; + } + } + y = (UInt16)(512 - fPageTMargin - fPageBMargin); + x = (UInt16)fPageLMargin; + break; + + case pfEsHTMLChunk::kFontChange: + IFindFontProps( idx, fontFace, fontSize, fontFlags, fontColor, fontSpacing ); + dtMap->SetFont( fontFace, fontSize, fontFlags, false ); + dtMap->SetTextColor( fontColor, true ); + dtMap->SetLineSpacing(fontSpacing); + break; + + case pfEsHTMLChunk::kMovie: + movieAlreadyLoaded = (IMovieAlreadyLoaded(chunk) != nil); // have we already cached it? + plLayerBink *movieLayer = IMakeMovieLayer(chunk, x, y, (plMipmap*)dtMap, whichDTMap, suppressRendering); + if (movieLayer) + { + // adjust the starting height of the movie if we are keeping it inline with the text + UInt32 movieHeight = 0, movieWidth = 0; + movieHeight = movieLayer->GetHeight(); + movieWidth = movieLayer->GetWidth(); + if(!(chunk->fFlags & pfEsHTMLChunk::kFloating )) + { + if( y + movieHeight >= 512 - fPageBMargin ) + { + // Movie overlaps the bottom of this page, so forcibly break so we'll + // end up marking the page break here (note that, unlike paragraphs, we + // can't really break the Movie into two) + y += (UInt16)movieHeight; + + // Wonderful, the break breaks us from the switch(), which means the for() + // loops runs once more and increments idx. So this is to counter that. + // (We better check tho, just to make sure nobody feeds us an extra-large + // image and sends us on an infinite loop) + if( idx > fPageStarts[ page ] ) + idx--; + break; + } + y += (UInt16)movieHeight; + x = (UInt16)fPageLMargin; + } + if (!movieAlreadyLoaded) // if the movie wasn't already cached, cache it + { + movie = TRACKED_NEW loadedMovie; + movie->movieLayer = movieLayer; // save the layer and chunk data + movie->movieChunk = chunk; + fLoadedMovies.Append(movie); + movie = nil; + movieAlreadyLoaded = false; + } + if (material && !suppressRendering) + material->AddLayerViaNotify(movieLayer); + } + break; + } + } + + if( fPageStarts.GetCount() <= page + 1 ) + fPageStarts.ExpandAndZero( page + 2 ); + fPageStarts[ page + 1 ] = idx; + + if( idx == fHTMLSource.GetCount() ) + fLastPage = page; + + pfBookData::WhichSide thisWhich = ( whichDTMap == pfJournalDlgProc::kTagRightDTMap ) ? pfBookData::kRightSide : ( whichDTMap == pfJournalDlgProc::kTagLeftDTMap ) ? pfBookData::kLeftSide : pfBookData::kNoSides; + if( needSFX ) + fBookGUIs[fCurBookGUI]->RegisterForSFX( (pfBookData::WhichSide)( fBookGUIs[fCurBookGUI]->CurSFXPages() | thisWhich ) ); + else + fBookGUIs[fCurBookGUI]->RegisterForSFX( (pfBookData::WhichSide)( fBookGUIs[fCurBookGUI]->CurSFXPages() & ~thisWhich ) ); + } + + if( !suppressRendering ) + dtMap->FlushToHost(); +} + +//// IMoveMovies ///////////////////////////////////////////////////////////// + +void pfJournalBook::IMoveMovies( hsGMaterial *source, hsGMaterial *dest ) +{ + hsTArray moviesOnPage; + if (source && dest) + { + // clear any exiting layers (movies) from the material and save them to our local array + int i; + for( i = 0; i < source->GetNumLayers(); i++ ) // remove all plLayerBink layers + { + plLayerInterface *matLayer = source->GetLayer(i); + plLayerBink *bink = plLayerBink::ConvertNoRef(matLayer); + if (bink) // if it was a bink layer + { + plMatRefMsg* refMsg = TRACKED_NEW plMatRefMsg(source->GetKey(), plRefMsg::kOnRemove, i, plMatRefMsg::kLayer); // remove it + hsgResMgr::ResMgr()->SendRef(source->GetLayer(i)->GetKey(), refMsg, plRefFlags::kActiveRef); + moviesOnPage.Append(bink); + } + } + // clear the destination's movies (if it has any) + for( i = 0; i < dest->GetNumLayers(); i++ ) // remove all plLayerBink layers + { + plLayerInterface *matLayer = dest->GetLayer(i); + plLayerBink *bink = plLayerBink::ConvertNoRef(matLayer); + if (bink) // if it was a bink layer + { + plMatRefMsg* refMsg = TRACKED_NEW plMatRefMsg(dest->GetKey(), plRefMsg::kOnRemove, i, plMatRefMsg::kLayer); // remove it + hsgResMgr::ResMgr()->SendRef(dest->GetLayer(i)->GetKey(), refMsg, plRefFlags::kActiveRef); + } + } + // put the movies we ripped off the old page onto the new one + for( i = 0; i < moviesOnPage.GetCount(); i++ ) + { + dest->AddLayerViaNotify(moviesOnPage[i]); + } + } +} + +//// IDrawMipmap ///////////////////////////////////////////////////////////// + +void pfJournalBook::IDrawMipmap( pfEsHTMLChunk *chunk, UInt16 x, UInt16 y, plMipmap *mip, plDynamicTextMap *dtMap, UInt32 whichDTMap, hsBool dontRender ) +{ + plMipmap *copy = TRACKED_NEW plMipmap(); + copy->CopyFrom(mip); + if (chunk->fNoResizeImg) + { + // book is NOT square, there is a h/w ratio of 1/0.7 + // calc new size based on how the book has been skewed + float xScale = (fWidthScale == 0) ? 1 : 1/(1-fWidthScale); + float yScale = (fHeightScale == 0) ? 1 : 1/(1-fHeightScale); + yScale *= 0.7; // adjust because the book isn't square + UInt32 width = (UInt32)(mip->GetWidth()*xScale); + UInt32 height = (UInt32)(mip->GetHeight()*yScale); + UInt16 xShift; + UInt16 yShift; + + if (dtMap->GetWidth() < width) width = dtMap->GetWidth(); + if (dtMap->GetHeight() < height) height = dtMap->GetHeight(); + + if (height < mip->GetHeight()) + { + yShift = (UInt16)((mip->GetHeight()-height)/2); + if (y+yShift+height > dtMap->GetHeight()) + y = (UInt16)(dtMap->GetHeight()-height); + else + y += yShift; + } + else + { + yShift = (UInt16)((height-mip->GetHeight())/2); + if (yShift > y) + y = 0; + else + y -= yShift; + } + + if (width < mip->GetWidth()) + { + xShift = (UInt16)((mip->GetWidth()-width)/2); + if (x+xShift+width > dtMap->GetWidth()) + x = (UInt16)(dtMap->GetWidth()-width); + else + x += xShift; + } + else + { + xShift = (UInt16)((width-mip->GetWidth())/2); + if (xShift > x) + x = 0; + else + x -= xShift; + } + + copy->SetCurrLevel(0); // resize the image so it will look unchanged when rendered on the altered book + copy->ResizeNicely((UInt16)width,(UInt16)height,plMipmap::kDefaultFilter); + } + if( !dontRender ) + { + plMipmap::CompositeOptions opts; + if( chunk->fFlags & pfEsHTMLChunk::kActAsCB ) + { + opts.fFlags = ( chunk->fFlags & pfEsHTMLChunk::kBlendAlpha ) ? 0 : plMipmap::kBlendWriteAlpha; + opts.fRedTint = chunk->fCurrColor.r; + opts.fGreenTint = chunk->fCurrColor.g; + opts.fBlueTint = chunk->fCurrColor.b; + opts.fOpacity = (UInt8)(chunk->fCurrColor.a * 255.f); + } + else + { + if( chunk->fFlags & pfEsHTMLChunk::kGlowing ) + opts.fFlags = ( chunk->fFlags & pfEsHTMLChunk::kBlendAlpha ) ? 0 : plMipmap::kMaskSrcAlpha; + else if (chunk->fFlags & pfEsHTMLChunk::kTranslucent) + opts.fFlags = plMipmap::kMaskSrcAlpha; + else + opts.fFlags = ( chunk->fFlags & pfEsHTMLChunk::kBlendAlpha ) ? plMipmap::kCopySrcAlpha : plMipmap::kForceOpaque; + opts.fOpacity = (UInt8)(chunk->fCurrOpacity * 255.f); + } + dtMap->Composite( copy, x, y, &opts ); + } + + if( chunk->fFlags & pfEsHTMLChunk::kCanLink ) + { + if( whichDTMap == pfJournalDlgProc::kTagRightDTMap || whichDTMap == pfJournalDlgProc::kTagTurnFrontDTMap ) + x += (UInt16)(dtMap->GetWidth()); // Right page rects are offsetted to differentiate + + if (dontRender) // if we aren't rendering then this link isn't visible, but the index still needs to be valid, so give it a rect of 0,0,0,0 + chunk->fLinkRect.Set(0,0,0,0); + else + chunk->fLinkRect.Set( x, y, (Int16)(copy->GetWidth()), (Int16)(copy->GetHeight()) ); + fVisibleLinks.Append( chunk ); + } + delete copy; +} + +pfJournalBook::loadedMovie *pfJournalBook::IMovieAlreadyLoaded(pfEsHTMLChunk *chunk) +{ + int i; + for (i=0; ifText == fLoadedMovies[i]->movieChunk->fText)&&(chunk->fMovieIndex == fLoadedMovies[i]->movieChunk->fMovieIndex)) + return fLoadedMovies[i]; + } + return nil; +} + +plKey pfJournalBook::GetMovie(UInt8 index) +{ + loadedMovie *movie = IGetMovieByIndex(index); + if (movie) + return movie->movieLayer->GetKey(); + return plKey(nil); +} + +pfJournalBook::loadedMovie *pfJournalBook::IGetMovieByIndex(UInt8 index) +{ + int i; + for (i=0; imovieChunk->fMovieIndex == index) + return fLoadedMovies[i]; + } + return nil; +} + +plLayerBink *pfJournalBook::IMakeMovieLayer(pfEsHTMLChunk *chunk, UInt16 x, UInt16 y, plMipmap *baseMipmap, UInt32 whichDTMap, hsBool dontRender) +{ + // see if it's already loaded + loadedMovie *movie = IMovieAlreadyLoaded(chunk); + plLayer* layer = nil; + plLayerBink* movieLayer = nil; + UInt16 movieWidth=0,movieHeight=0; + if (movie) + { + movieLayer = movie->movieLayer; + layer = plLayer::ConvertNoRef(movieLayer->BottomOfStack()); + movieWidth = movieLayer->GetWidth(); + movieHeight = movieLayer->GetHeight(); + } + else + { + // Create the layer and register it. + + // We'll need a unique name. This is a hack, but an effective hack. + static int uniqueSuffix = 0; + char buff[256]; + + sprintf(buff, "%s_%d_ml", GetKey()->GetName(), uniqueSuffix); + layer = TRACKED_NEW plLayer; + hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation()); + + sprintf(buff, "%s_%d_m", GetKey()->GetName(), uniqueSuffix++); + movieLayer = TRACKED_NEW plLayerBink; + hsgResMgr::ResMgr()->NewKey(buff, movieLayer, GetKey()->GetUoid().GetLocation()); + movieLayer->GetKey()->RefObject(); // we want to own a ref so we can nuke it at will + + movieLayer->AttachViaNotify(layer); + + // Initialize it. + char *name = hsWStringToString(chunk->fText.c_str()); + movieLayer->SetMovieName(name); + delete [] name; + movieLayer->Eval(0,0,0); // set up the movie + + movieWidth = movieLayer->GetWidth(); + movieHeight = movieLayer->GetHeight(); + + if (movieHeight == 0 || movieWidth == 0) // problem loading the file + { + movieLayer->GetKey()->UnRefObject(); + return nil; + } + } + + if (layer) + { + layer->InitToDefault(); + + layer->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.0f)); + layer->SetOpacity(1.0f); + + // Blend flags, movies are opaque, but they don't take the whole page, so alphamapping them + layer->SetBlendFlags(hsGMatState::kBlendAlpha); + + // Movie shouldn't have to ZWrite + layer->SetZFlags(hsGMatState::kZNoZWrite); + + // No special shading. + layer->SetShadeFlags(0); + + // Clamp all textures. + layer->SetClampFlags(hsGMatState::kClampTexture); + + // Draw passes individually. + layer->SetMiscFlags(hsGMatState::kMiscRestartPassHere); + + // Shared UV coordinates. + layer->SetUVWSrc(0); + + if( chunk->fFlags & pfEsHTMLChunk::kFloating ) + { + // Floating movie, ignore the text flow completely and just splat the movie on! + x = chunk->fAbsoluteX; + y = chunk->fAbsoluteY; + } + + if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kLeft ) + x = (UInt16)fPageLMargin; + else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kRight ) + x = (UInt16)(baseMipmap->GetWidth() - fPageRMargin - movieWidth); + else if( ( chunk->fFlags & pfEsHTMLChunk::kAlignMask ) == pfEsHTMLChunk::kCenter ) + x = (UInt16)((baseMipmap->GetWidth() >> 1) - (movieWidth >> 1)); + + // x and y are in pixels, need to convert to a range of 0 to 1 + float xRel = (float)x/(float)baseMipmap->GetWidth(); + float yRel = (float)y/(float)baseMipmap->GetHeight(); + + // Need to convert the scaling to texture space + float xScale = (float)baseMipmap->GetWidth()/(float)movieWidth; + float yScale = (float)baseMipmap->GetHeight()/(float)movieHeight; + + if (chunk->fNoResizeImg) + { + // book is NOT square, there is a h/w ratio of 1/0.7 + // calc new size based on how the book has been skewed + xScale *= (fWidthScale == 0) ? 1 : (1-fWidthScale); + yScale *= (fHeightScale == 0) ? 1 : (1-fHeightScale); + yScale *= 1/0.7; // adjust because the book isn't square + } + + hsVector3 scaleVec(xScale,yScale,1), translateVec(-(xRel*xScale),-(yRel*yScale),0); + hsMatrix44 scaleMat, translateMat; + scaleMat.MakeScaleMat(&scaleVec); + translateMat.MakeTranslateMat(&translateVec); + + hsMatrix44 flipMat; + if (chunk->fOnCover) // cover movies need to be y flipped + { + hsVector3 yTransVec(0,-1,0), invertYVec(1,-1,1); + hsMatrix44 invertY, transY; + invertY.MakeScaleMat(&invertYVec); + transY.MakeTranslateMat(&yTransVec); + flipMat = invertY * transY; + } + else // left page movies need to be x flipped + { + if ((whichDTMap == pfJournalDlgProc::kTagLeftDTMap) || (whichDTMap == pfJournalDlgProc::kTagTurnBackDTMap)) + { + hsVector3 xTransVec(-1,0,0), invertXVec(-1,1,1); + hsMatrix44 invertX, transX; + invertX.MakeScaleMat(&invertXVec); + transX.MakeTranslateMat(&xTransVec); + flipMat = invertX * transX; + } + else + flipMat = hsMatrix44::IdentityMatrix(); + } + + hsMatrix44 xfm; + xfm = translateMat * scaleMat * flipMat; + + layer->SetTransform(xfm); + } + if( chunk->fFlags & pfEsHTMLChunk::kCanLink ) + { + if( whichDTMap == pfJournalDlgProc::kTagRightDTMap || whichDTMap == pfJournalDlgProc::kTagTurnFrontDTMap ) + x += (UInt16)(baseMipmap->GetWidth()); // Right page rects are offsetted to differentiate + + if (dontRender) // if we aren't rendering then this link isn't visible, but the index still needs to be valid, so give it a rect of 0,0,0,0 + chunk->fLinkRect.Set(0,0,0,0); + else + chunk->fLinkRect.Set( x, y, movieWidth, movieHeight ); + fVisibleLinks.Append( chunk ); + } + + plAnimTimeConvert &timeConvert = movieLayer->GetTimeConvert(); + timeConvert.SetBegin(0); + timeConvert.SetEnd(movieLayer->GetLength()); + if (chunk->fLoopMovie) + { + timeConvert.SetLoopPoints(0,movieLayer->GetLength()); + timeConvert.Loop(); + } + timeConvert.Start(); // start the show! + + return movieLayer; +} + +plLayerInterface *pfJournalBook::IMakeBaseLayer(plMipmap *image) +{ + // Create the layer and register it. + + // We'll need a unique name. This is a hack, but an effective hack. + static int uniqueSuffix = 0; + char buff[256]; + sprintf(buff, "%s_%d", GetKey()->GetName(), uniqueSuffix++); + + plLayer* layer = TRACKED_NEW plLayer; + hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation()); + + // Initialize it. + layer->InitToDefault(); + + layer->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + if (fTintCover) + layer->SetRuntimeColor(hsColorRGBA().Set(fCoverTint.r, fCoverTint.g, fCoverTint.b, 1.f)); + layer->SetOpacity(1.f); + + // Blend flags, opaque for the bottom layer. + layer->SetBlendFlags(0); + + // Only the bottom layer writes it's Z value. + layer->SetZFlags(0); + + // No special shading. + layer->SetShadeFlags(hsGMatState::kShadeReallyNoFog); + + // Clamp all textures. + layer->SetClampFlags(hsGMatState::kClampTexture); + + // Draw passes individually. + layer->SetMiscFlags(hsGMatState::kMiscRestartPassHere); + + // Shared UV coordinates. + layer->SetUVWSrc(0); + + // Set up the transform. + hsVector3 yTransVec(0,-1,0), invertYVec(1,-1,1); + hsMatrix44 xfm, invertY, transY, flipY; + invertY.MakeScaleMat(&invertYVec); + transY.MakeTranslateMat(&yTransVec); + flipY = invertY * transY; + xfm = flipY; + + layer->SetTransform(xfm); + + // Set the texture (assumes mipmap is non-nil). + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture); + hsgResMgr::ResMgr()->SendRef(image->GetKey(), refMsg, plRefFlags::kActiveRef); + + return plLayerInterface::ConvertNoRef(layer); +} + +plLayerInterface *pfJournalBook::IMakeDecalLayer(pfEsHTMLChunk *decalChunk, plMipmap *decal, plMipmap *baseMipmap) +{ + // Create the layer and register it. + + // We'll need a unique name. This is a hack, but an effective hack. + static int uniqueSuffix = 0; + char buff[256]; + sprintf(buff, "%s_%d_d", GetKey()->GetName(), uniqueSuffix++); + + plLayer* layer = TRACKED_NEW plLayer; + hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation()); + + // Initialize it. + layer->InitToDefault(); + + // tint the layer only if the decal wants to be tinted + layer->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + if (decalChunk->fTintDecal) + layer->SetRuntimeColor(hsColorRGBA().Set(fCoverTint.r, fCoverTint.g, fCoverTint.b, 1.f)); + layer->SetOpacity(1.f); + + // Blend flags, decals are alpha blended. + layer->SetBlendFlags(hsGMatState::kBlendAlpha); + + // Only the bottom layer writes it's Z value. + layer->SetZFlags(hsGMatState::kZNoZWrite); + + // No special shading. + layer->SetShadeFlags(hsGMatState::kShadeReallyNoFog); + + // Clamp all textures. + layer->SetClampFlags(hsGMatState::kClampTexture); + + // Draw passes individually. + layer->SetMiscFlags(hsGMatState::kMiscRestartPassHere); + + // Shared UV coordinates. + layer->SetUVWSrc(0); + + // Set up the transform. + UInt16 x,y; + x = decalChunk->fAbsoluteX; + y = decalChunk->fAbsoluteY; + + if((decalChunk->fFlags & pfEsHTMLChunk::kAlignMask) == pfEsHTMLChunk::kLeft) + x = (UInt16)fPageLMargin; + else if((decalChunk->fFlags & pfEsHTMLChunk::kAlignMask) == pfEsHTMLChunk::kRight) + x = (UInt16)(baseMipmap->GetWidth() - decal->GetWidth()); + else if((decalChunk->fFlags & pfEsHTMLChunk::kAlignMask) == pfEsHTMLChunk::kCenter) + x = (UInt16)((baseMipmap->GetWidth() >> 1) - (decal->GetWidth() >> 1)); + + // x and y are in pixels, need to convert to a range of 0 to 1 + float xRel = (float)x/(float)baseMipmap->GetWidth(); + float yRel = (float)y/(float)baseMipmap->GetHeight(); + + // Need to convert the scaling to texture space + float xScale = (float)baseMipmap->GetWidth()/(float)decal->GetWidth(); + float yScale = (float)baseMipmap->GetHeight()/(float)decal->GetHeight(); + + if (decalChunk->fNoResizeImg) + { + // book is NOT square, there is a h/w ratio of 1/0.7 + // calc new size based on how the book has been skewed + xScale *= (fWidthScale == 0) ? 1 : (1-fWidthScale); + yScale *= (fHeightScale == 0) ? 1 : (1-fHeightScale); + yScale *= 1/0.7; // adjust because the book isn't square + } + + hsVector3 scaleVec(xScale,yScale,1), translateVec(-(xRel*xScale),-(yRel*yScale),0), yTransVec(0,-1,0), invertYVec(1,-1,1); + hsMatrix44 scaleMat, translateMat, invertY, transY; + scaleMat.MakeScaleMat(&scaleVec); + translateMat.MakeTranslateMat(&translateVec); + invertY.MakeScaleMat(&invertYVec); + transY.MakeTranslateMat(&yTransVec); + + hsMatrix44 xfm, flipY; + flipY = invertY * transY; + xfm = translateMat * scaleMat * flipY; + + layer->SetTransform(xfm); + + // Set the texture (assumes mipmap is non-nil). + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture); + hsgResMgr::ResMgr()->SendRef(decal->GetKey(), refMsg, plRefFlags::kActiveRef); + + return plLayerInterface::ConvertNoRef(layer); +} + +void pfJournalBook::ISetDecalLayers(hsGMaterial *material,hsTArray layers) +{ + // First, clear out the existing layers. + int i; + for( i = material->GetNumLayers()-1; i >= 0; i-- ) + { + plMatRefMsg* refMsg = TRACKED_NEW plMatRefMsg(material->GetKey(), plRefMsg::kOnRemove, i, plMatRefMsg::kLayer); + hsgResMgr::ResMgr()->SendRef(material->GetLayer(i)->GetKey(), refMsg, plRefFlags::kActiveRef); + } + + // Now append our new layers in order. + for( i = 0; i < layers.GetCount(); i++ ) + { + material->AddLayerViaNotify(layers[i]); + } +} + +//// IFindFontProps ////////////////////////////////////////////////////////// +// Starting at the given chunk, works backwards to determine the full set of current +// font properties at that point, or assigns defaults if none were specified + +void pfJournalBook::IFindFontProps( UInt32 chunkIdx, const wchar_t *&face, UInt8 &size, UInt8 &flags, hsColorRGBA &color, Int16 &spacing ) +{ + enum Which + { + kFace = 0x01, + kSize = 0x02, + kFlags = 0x04, + kColor = 0x08, + kSpacing= 0x10, + + kAllFound = kFace | kSize | kFlags | kColor | kSpacing + }; + + // Start empty + UInt8 found = 0; + + // Work backwards and fill in our properties + chunkIdx++; + do + { + chunkIdx--; + if (fHTMLSource.Count() <= chunkIdx) + break; // apparently it's sometimes possible for fHTMLSource to be empty (parse errors?) + pfEsHTMLChunk *chunk = fHTMLSource[ chunkIdx ]; + + if( chunk->fType == pfEsHTMLChunk::kFontChange ) + { + // What do we (still) need? + if( !( found & kFace ) && chunk->fText != L"" ) + { + face = chunk->fText.c_str(); + found |= kFace; + } + if( !( found & kSize ) && chunk->fFontSize > 0 ) + { + size = chunk->fFontSize; + found |= kSize; + } + if( !( found & kFlags ) && ( chunk->fFlags & pfEsHTMLChunk::kFontMask ) != 0 ) + { + flags = 0; + if( chunk->fFlags & pfEsHTMLChunk::kFontBold ) + flags |= plDynamicTextMap::kFontBold; + if( chunk->fFlags & pfEsHTMLChunk::kFontItalic ) + flags |= plDynamicTextMap::kFontItalic; + + found |= kFlags; + } + if( !( found & kColor ) && ( chunk->fFlags & pfEsHTMLChunk::kFontColor ) ) + { + color = chunk->fColor; + found |= kColor; + } + if( !( found & kSpacing ) && ( chunk->fFlags & pfEsHTMLChunk::kFontSpacing ) ) + { + spacing = chunk->fLineSpacing; + found |= kSpacing; + } + } + + } while( chunkIdx != 0 && found != kAllFound ); + + // Set any un-found defaults + if( !( found & kFace ) ) + face = L"Arial"; + if( !( found & kSize ) ) + size = 24; + if( !( found & kFlags ) ) + flags = 0; + if( !( found & kColor ) ) + color.Set( 0.f, 0.f, 0.f, 1.f ); + if( !( found & kSpacing ) ) + spacing = 0; +} + +//// IFindLastAlignment ////////////////////////////////////////////////////// +// Find the last paragraph chunk and thus the last par alignment settings + +UInt8 pfJournalBook::IFindLastAlignment( void ) const +{ + Int32 idx; + + + for( idx = fHTMLSource.GetCount() - 1; idx >= 0; idx-- ) + { + if( fHTMLSource[ idx ]->fType == pfEsHTMLChunk::kParagraph && fHTMLSource[ idx ]->fFlags != 0 ) + return (UInt8)(fHTMLSource[ idx ]->fFlags); + } + + return pfEsHTMLChunk::kLeft; +} + +//// IRecalcPageStarts /////////////////////////////////////////////////////// +// Ensures that all the page starts are calced up to the given page (but not including it) + +void pfJournalBook::IRecalcPageStarts( UInt32 upToPage ) +{ + UInt32 page; + + + // Well, sadly, we can't really calc the page starts without at least a DTMap + // we can change things on...so we just pick one and render. Note: this WILL + // trash the font settings on the given DTMap! + + // We assume that the stored page starts we already have are accurate, so + // just start from there and calc onward + + for( page = fPageStarts.GetCount()-1; page < upToPage && page <= fLastPage; page++ ) + { + // normally we would surpress rendering the pages, but that seems to have a bug in it + // that causes lost text that the rendering doesn't have. Since it isn't very costly to + // actually draw them all (even in large journals), we're just going to do it + IRenderPage( page, pfJournalDlgProc::kTagTurnBackDTMap, false ); + // Reset any "visible" links since they aren't really visible + UInt16 i; + for (i=0; ifLinkRect.Set(0,0,0,0); + } + } +} + +//// ISendNotify ///////////////////////////////////////////////////////////// +// Just sends out a notify to our currently set receiver key + +void pfJournalBook::ISendNotify( UInt32 type, UInt32 linkID ) +{ + if( fCallbackKey != nil ) + { + plNotifyMsg *pMsg = TRACKED_NEW plNotifyMsg; + pMsg->AddBookEvent( type, linkID ); + pMsg->SetBCastFlag( plMessage::kNetPropagate, false ); // don't deliver networked! + pMsg->Send( fCallbackKey ); + } +} + +//// SetBookSize ///////////////////////////////////////////////////////////// +// Sets the book size scaling. 1,1 would be full size, 0,0 is the smallest size possible +// Note: internally we store these as the seek positions on our animations, +// so the incoming parameters are actually inverse of what we finally want + +void pfJournalBook::SetBookSize( hsScalar width, hsScalar height ) +{ + fWidthScale = 1.f - width; + fHeightScale = 1.f - height; + + if( fBookGUIs[fCurBookGUI]->CurBook() == this ) + fBookGUIs[fCurBookGUI]->SetCurrSize( fWidthScale, fHeightScale ); +} + +//// ILoadAllImages ////////////////////////////////////////////////////////// +// Load (or unload) all the images for the book + +void pfJournalBook::ILoadAllImages( hsBool unload ) +{ + UInt32 i; + + // load the cover + if( fCoverFromHTML && fCoverMipKey != nil ) + { + if( unload ) + fBookGUIs[fCurBookGUI]->GetKey()->Release( fCoverMipKey ); + else + { + plGenRefMsg *ref = TRACKED_NEW plGenRefMsg( fBookGUIs[fCurBookGUI]->GetKey(), plRefMsg::kOnCreate, -1, kRefImage ); + hsgResMgr::ResMgr()->AddViaNotify( fCoverMipKey, ref, plRefFlags::kActiveRef ); + } + } + + for( i = 0; i < fHTMLSource.GetCount(); i++ ) + { + if( fHTMLSource[ i ]->fType == pfEsHTMLChunk::kImage && fHTMLSource[ i ]->fImageKey != nil ) + { + if( unload ) + fBookGUIs[fCurBookGUI]->GetKey()->Release( fHTMLSource[ i ]->fImageKey ); + else + { + plGenRefMsg *ref = TRACKED_NEW plGenRefMsg( fBookGUIs[fCurBookGUI]->GetKey(), plRefMsg::kOnCreate, -1, kRefImage ); + hsgResMgr::ResMgr()->AddViaNotify( fHTMLSource[ i ]->fImageKey, ref, plRefFlags::kActiveRef ); + } + } + } +} + +void pfJournalBook::IPurgeDynaTextMaps( ) +{ + plDynamicTextMap* turnFront = fBookGUIs[fCurBookGUI]->GetDTMap( pfJournalDlgProc::kTagTurnFrontDTMap ); + if (turnFront) + turnFront->PurgeImage(); + + plDynamicTextMap* right = fBookGUIs[fCurBookGUI]->GetDTMap( pfJournalDlgProc::kTagRightDTMap ); + if (right) + right->PurgeImage(); + + plDynamicTextMap* turnBack = fBookGUIs[fCurBookGUI]->GetDTMap( pfJournalDlgProc::kTagTurnBackDTMap ); + if (turnBack) + turnBack->PurgeImage(); + + plDynamicTextMap* left = fBookGUIs[fCurBookGUI]->GetDTMap( pfJournalDlgProc::kTagLeftDTMap ); + if (left) + left->PurgeImage(); + + pfGUIMultiLineEditCtrl *leftEdit = fBookGUIs[fCurBookGUI]->GetEditCtrl( pfJournalDlgProc::kTagLeftEditCtrl ); + if (leftEdit) + leftEdit->PurgeDynaTextMapImage(); + + pfGUIMultiLineEditCtrl *rightEdit = fBookGUIs[fCurBookGUI]->GetEditCtrl( pfJournalDlgProc::kTagRightEditCtrl ); + if (rightEdit) + rightEdit->PurgeDynaTextMapImage(); + + pfGUIMultiLineEditCtrl *turnFrontEdit = fBookGUIs[fCurBookGUI]->GetEditCtrl( pfJournalDlgProc::kTagTurnFrontEditCtrl ); + if (turnFrontEdit) + turnFrontEdit->PurgeDynaTextMapImage(); + + pfGUIMultiLineEditCtrl *turnBackEdit = fBookGUIs[fCurBookGUI]->GetEditCtrl( pfJournalDlgProc::kTagTurnBackEditCtrl ); + if (turnBackEdit) + turnBackEdit->PurgeDynaTextMapImage(); +} + +std::string pfJournalBook::GetEditableText() +{ + pfGUIMultiLineEditCtrl *left = fBookGUIs[fCurBookGUI]->GetEditCtrl( pfJournalDlgProc::kTagLeftEditCtrl ); + if (left) + { + char *temp = left->GetNonCodedBuffer(); + std::string retVal = temp; + delete [] temp; + return retVal; + } + return ""; +} + +void pfJournalBook::SetEditableText(std::string text) +{ + pfGUIMultiLineEditCtrl *left = fBookGUIs[fCurBookGUI]->GetEditCtrl( pfJournalDlgProc::kTagLeftEditCtrl ); + if (left) + { + left->SetBuffer(text.c_str()); + left->ForceUpdate(); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBook.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBook.h new file mode 100644 index 00000000..230ba4d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBook.h @@ -0,0 +1,568 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pfJournalBook_h +#define _pfJournalBook_h + +////////////////////////////////////////////////////////////////////////////// +// // +// pfJournalBook Class // +// A generic, high-level, abstract method of creating various Myst-like // +// books within the game with very little effort, while ensuring that they // +// all remain consistent in appearance and operability. // +// // +////////////////////////////////////////////////////////////////////////////// +// // +// Journal books are created via a journal book template. The template // +// takes the form of an extremely simplified version of HTML. The esHTML // +// has the following tags defined: // +//

    - Start of a new paragraph.

    isn't used. // +// - Places an image in-line with the text. Options are: // +// align=left/right/center // +// src= // +// link= - Defines the image as clickable. When the user // +// clicks the image, it will generate an event // +// with the given event ID and send it to the // +// calling Python handler. // +// blend=none/alpha - Controls the blending method of drawing // +// the image // +// pos=, - Positions the image absolutely on the page // +// using the upper-left pixel given. Absolute- // +// placed images have no effect on text flow. // +// glow= // +// - Defines a glow special effect, which amounts // +// to the image oscillating in opacity across // +// the time interval specified. Optionally, you // +// can also specify the min and max opacity, // +// from 0 to 1. // +// check= // +// - Makes the image act as a checkbox, flipping // +// between on and off states (as defined by the // +// blending colors) on clicks. Default is either // +// 1 for on or 0 for off. Link notifys are sent // +// by checkboxes, with the event type // +// "kNotifyCheckUnchecked" negative if the // +// state is switching to off. Cannot be used // +// with "glow". // +// resize=yes/no - Defines whether the image will be resized // +// with the book or not, defaults to yes // +// opacity=0 to 1 - Defines the opacity of the image, 0 is // +// completely transparent, 1 is opaque // +// - Page break // +// - Set the font for the following text. Options are: // +// face= // +// size= // +// style=r/b/i/bi // +// color=rrggbb - Hex color // +// spacing= - line spacing in pixels // +// - Optionally sets the cover mipmap for the book. // +// src= - Selects the mipmap to be used, using the // +// same search methods as . Unlike // +// though, has no restriction on // +// mipmap format. // +// tint=rrggbb - Hex color for tinting the cover image // +// tintfirst=yes/no - Tint the cover before applying decals, // +// defaults to yes. A no option overrides the // +// individual tint options on the tags // +// - Optionally sets the margin size in pixels, default is 16 // +// top= // +// left= // +// bottom= // +// right= // +// - Optionally sets the size of the book in percent (0-1), // +// can be overridden by the SetBookSize funtion. // +// height= // +// width= // +// - Optionally specifies a decal to be drawn on the cover in // +// the specified position. Options are: // +// src= - Selects the mipmap to be used, using the // +// same search methods as . Unlike // +// though, has no restrictions on // +// mipmap format. // +// pos=, - Specifies the position of the decal on the // +// cover using the upper-left pixel // +// align=left/right/center // +// - Aligns the decal horizontally, overriding // +// any x coord in the pos option, if no y has // +// been specified, then it places it at the // +// top of the cover // +// resize=yes/no - Defines whether the image will be resized // +// with the book or not, defaults to yes // +// tint=yes/no - Defines whether or not this decal is tinted // +// with the cover. Overridden by the tintfirst // +// option on the tag. Defaults to no // +// - Places a movie (.bik file) inline with the text. Options: // +// src= - Selects the movie to be used. (nead search // +// methods here eventually) // +// align=left/right/center // +// - Aligns the movie horizontally, overriding // +// any x coord in the pos option // +// link= - Defines the movie as clickable. When the // +// User clicks the movie, it will generate an // +// event with the given event ID and send it // +// to the calling python handler // +// pos=, - Specifies the position of the movie on the // +// page using the upper left pixel given, does // +// not influence text flow // +// resize=yes/no - Defines whether the movie will be resized // +// with the book or not, defaults to yes // +// oncover=yes/no - Defines whether the movie will be placed on // +// the cover or not, defaults to no. NOTE: // +// setting this to yes causes the link option // +// to be ignored since cover movies can't link // +// loop=yes/no - Defines whether the movie will loop or not // +// defaults to yes // +// - Marks this book as editable (if the GUI supports it) // +// // +// The pages don't render until displayed. As a result, jumping to a given // +// page requires each page from the current position to the destination // +// to be rendered. Normally, this won't be a problem, because by default // +// books open to the first page, at which point each page renders one // +// at a time as the user flips through. // +// // +// The system assumes that no more than one book will ever actually be // +// shown at any time. As a result, the internal geometry for displaying // +// each book can be shared, reducing overhead and potential for errors. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsTemplates.h" +#include "hsColorRGBA.h" + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plUoid.h" + + + +class pfEsHTMLChunk; +class pfGUIDialogMod; +class plLocation; +class pfGUICheckBoxCtrl; +class pfGUIButtonMod; +class pfJournalDlgProc; +class plDynamicTextMap; +class pfGUIClickMapCtrl; +class plLayerInterface; +class plMipmap; +class pfGUIProgressCtrl; +class hsGMaterial; +class plLayerBink; +class pfGUIMultiLineEditCtrl; + +class pfJournalBook; +class pfBookMultiLineEditProc; + +class pfBookData : public hsKeyedObject +{ +public: + enum WhichSide + { + kLeftSide = 0x01, + kRightSide = 0x02, + kBothSides = 0x03, + kNoSides = 0 + }; + + enum DynDisplayIndex + { + kLeftPage = 0, + kRightPage, + kTurnFrontPage, + kTurnBackPage + }; + + pfBookData(const char *guiName = nil); + virtual ~pfBookData(); + + void LoadGUI(); // need this seperate because the plKey isn't setup until the constructor is done + + CLASSNAME_REGISTER(pfBookData); + GETINTERFACE_ANY(pfBookData, hsKeyedObject); + + virtual hsBool MsgReceive(plMessage *pMsg); + + pfGUIDialogMod *Dialog() const {return fDialog;} + pfGUICheckBoxCtrl *CoverButton() const {return fCoverButton;} + pfGUICheckBoxCtrl *TurnPageButton() const {return fTurnPageButton;} + pfGUIClickMapCtrl *LeftPageMap() const {return fLeftPageMap;} + pfGUIClickMapCtrl *RightPageMap() const {return fRightPageMap;} + plLayerInterface *CoverLayer() const {return fCoverLayer;} + hsGMaterial *CoverMaterial() const {return fCoverMaterial;} + hsGMaterial *PageMaterial(int index) const {if ((index<0)||(index>3)) return nil; else return fPageMaterials[index];} + pfGUIButtonMod *LeftCorner() const {return fLeftCorner;} + pfGUIButtonMod *RightCorner() const {return fRightCorner;} + pfGUIProgressCtrl *WidthCtrl() const {return fWidthCtrl;} + pfGUIProgressCtrl *HeightCtrl() const {return fHeightCtrl;} + plMipmap *DefaultCover() const {return fDefaultCover;} + pfJournalBook *CurBook() const {return fCurrBook;} + + hsBool StartedOpen() {return fStartedOpen;} + hsBool CurrentlyOpen() {return fCurrentlyOpen;} + hsBool CurrentlyTurning() {return fCurrentlyTurning;} + hsBool IsEditable() {return fEditable;} + WhichSide CurSFXPages() {return fCurrSFXPages;} + + void StartedOpen(hsBool startedOpen) {fStartedOpen = startedOpen;} + void CurrentlyOpen(hsBool currentlyOpen) {fCurrentlyOpen = currentlyOpen;} + void CurrentlyTurning(hsBool currentlyTurning) {fCurrentlyTurning = currentlyTurning;} + void CurBook(pfJournalBook *curBook) {fCurrBook = curBook;} + + // Quick helper + plDynamicTextMap *GetDTMap(UInt32 which); + pfGUIMultiLineEditCtrl *GetEditCtrl(UInt32 which); + + // Seeks the width and height animations to set the desired book size. Sizes are in % across the animation + void SetCurrSize(hsScalar w, hsScalar h); + + // Enables or disables the left and right page corners, to indicate current turnage state + void UpdatePageCorners(WhichSide which); + + // Plays our book close animation + void PlayBookCloseAnim(hsBool closeIt = true, hsBool immediate = false); + + // Finishes the start of the triggered page flip (once we're sure the animation is at the new frame) + void StartTriggeredFlip(hsBool flipBackwards); + + // kill the flipping of a page... we are probably closing the book + void KillPageFlip(); + + // Registers (or unregisters) for time messages so we can process special FX if we need to + void RegisterForSFX(WhichSide whichSides); + + void HitEndOfControlList(Int32 cursorPos); + void HitBeginningOfControlList(Int32 cursorPos); + + void EnableEditGUI(hsBool enable=true); + void DisableEditGUI() {EnableEditGUI(false);} + +protected: + friend class pfJournalDlgProc; + + enum Refs + { + kRefDialog = 0, + kRefDefaultCover + }; + + std::string fGUIName; + + // The pointer to our dialog + pfGUIDialogMod *fDialog; + + // And other interesting pointers + pfGUICheckBoxCtrl *fCoverButton; + pfGUICheckBoxCtrl *fTurnPageButton; + pfGUIClickMapCtrl *fLeftPageMap; + pfGUIClickMapCtrl *fRightPageMap; + plLayerInterface *fCoverLayer; + hsGMaterial *fCoverMaterial; + hsGMaterial *fPageMaterials[4]; + pfGUIButtonMod *fLeftCorner; + pfGUIButtonMod *fRightCorner; + pfGUIProgressCtrl *fWidthCtrl; + pfGUIProgressCtrl *fHeightCtrl; + + pfGUIMultiLineEditCtrl *fLeftEditCtrl; + pfGUIMultiLineEditCtrl *fRightEditCtrl; + pfGUIMultiLineEditCtrl *fTurnFrontEditCtrl; + pfGUIMultiLineEditCtrl *fTurnBackEditCtrl; + + // Pointer to our default (base) cover mipmap + plMipmap *fDefaultCover; + + // The current book using our data + pfJournalBook *fCurrBook; + + // Which side(s) we're currently doing SFX for + WhichSide fCurrSFXPages; + + // Base time to calc SFX anim positions from + hsScalar fBaseSFXTime; + hsBool fResetSFXFlag; + hsBool fSFXUpdateFlip; // So we only update alternating pages every frame, to save processor time + + // What it says + hsBool fCurrentlyTurning, fCurrentlyOpen, fStartedOpen; + + hsBool fEditable; + Int32 fAdjustCursorTo; + + // Inits our dialog template + void IInitTemplate(pfGUIDialogMod *templateDlg); + + // Process SFX for this frame + void IHandleSFX(hsScalar currTime, WhichSide whichSide = kNoSides); + + // Yet another step in the page flip, to make SURE we're already showing the turning page before we fill in the page behind it + void IFillUncoveringPage(hsBool rightSide); + + // Triggers the start of the page-flipping animation, as well as sets up the callback for when it's finished + void ITriggerPageFlip(hsBool flipBackwards, hsBool immediate); + + // Finishes the triggered page flip, on callback + void IFinishTriggeredFlip(hsBool wasBackwards); +}; + +class pfJournalBook : public hsKeyedObject +{ + public: + + // Enums of event types for the Book plNotifyMsg type + enum NotifyTypes + { + kNotifyImageLink = 0, + kNotifyShow, + kNotifyHide, + kNotifyNextPage, + kNotifyPreviousPage, + kNotifyCheckUnchecked, + kNotifyClose, + }; + + // The constructor takes in the esHTML source for the journal, along with + // the name of the mipmap to use as the cover of the book. The callback + // key is the keyed object to send event messages to (see tag). + pfJournalBook( const char *esHTMLSource, plKey coverImageKey = nil, plKey callbackKey = nil, const plLocation &hintLoc = plLocation::kGlobalFixedLoc, const char *guiName = nil ); + pfJournalBook( const wchar_t *esHTMLSource, plKey coverImageKey = nil, plKey callbackKey = nil, const plLocation &hintLoc = plLocation::kGlobalFixedLoc, const char *guiName = nil ); + + virtual ~pfJournalBook(); + + CLASSNAME_REGISTER( pfJournalBook ); + GETINTERFACE_ANY( pfJournalBook, hsKeyedObject ); + + // Our required virtual + virtual hsBool MsgReceive( plMessage *pMsg ); + + // Init the singleton, for client startup + static void SingletonInit( void ); + + // Shutdown the singleton + static void SingletonShutdown( void ); + + // loads a gui + static void LoadGUI( const char *guiName ); + + // unloads a gui if we don't need it any more and want to free up memory + static void UnloadGUI( const char *guiName ); + + // unloads all GUIs except for the default + static void UnloadAllGUIs(); + + void SetGUI( const char *guiName ); + + // Shows the book, optionally starting open or closed + void Show( hsBool startOpened = false ); + + + /// NOTE: The following functions expose functionality that is normally + /// handled by the book logic itself. So you should only need to use these + /// in unusual circumstances. + + + // Book handles hiding itself once someone clicks away. + void Hide( void ); + + // Opens the book, optionally to the given page + void Open( UInt32 startingPage = 0 ); + + // Closes the book. + void Close( void ); + + // Advances forward one page + void NextPage( void ); + + // Same, only back + void PreviousPage( void ); + + // For completeness... + void GoToPage( UInt32 pageNumber ); + + // See below. Just forces a full calc of the cached info + void ForceCacheCalculations( void ); + + // Closes the book, then calls Hide() once it's done closing + void CloseAndHide( void ); + + // Sets the book size scaling. 1,1 would be full size, 0,0 is the smallest size possible + void SetBookSize( hsScalar width, hsScalar height ); + + // What page are we on? + UInt32 GetCurrentPage( void ) const { return fCurrentPage; } + + // Set the margin (defaults to 16 pixels) + void SetPageMargin( UInt32 margin ) { fPageTMargin = fPageLMargin = fPageBMargin = fPageRMargin = margin; } + + // Turns on or off page turning + void AllowPageTurning( hsBool allow ) { fAllowTurning = allow; } + + // grabs a certain movie based on it's index in the source file + plKey GetMovie( UInt8 index ); + + // turns on and off editing of the book + void SetEditable( hsBool editable=true ); + + // returns the text contained by the edit controls + std::string GetEditableText(); + + void SetEditableText(std::string text); + + protected: + + struct loadedMovie + { + pfEsHTMLChunk *movieChunk; + plLayerBink *movieLayer; + }; + + friend class pfJournalDlgProc; + friend class pfBookData; + + // Our compiled esHTML source + std::wstring fUncompiledSource; + plLocation fDefLoc; + hsTArray fHTMLSource; + hsTArray fCoverDecals; // stored in a separate location so we can draw them all immediately + + hsTArray fLoadedMovies; + + // The key of the mipmap to use as the cover image + plKey fCoverMipKey; + bool fTintCover; + hsColorRGBA fCoverTint; + bool fTintFirst; // tint before applying decals? + + // Receiver key to send notifys to, if any + plKey fCallbackKey; + bool fCoverFromHTML; + // Cached array of page starts in the esHTML source. Generated as we flip through + // the book, so that going backwards can be done efficiently. + hsTArray fPageStarts; + + // is the book done showing and ready for more page calculations + hsBool fAreWeShowing; + + // Our current page + UInt32 fCurrentPage; + + // are we editing this book? (adjusts how we draw and flip pages) + hsBool fAreEditing; + hsBool fWantEditing; // the code specifies that we want to edit, but the gui doesn't support it, we will check again if the gui changes + + hsBool fAllowTurning; // do we allow the user to turn pages? + + // The ending page. -1 until calculated by flipping to it + UInt32 fLastPage; + + // Per book size + hsScalar fWidthScale, fHeightScale; + + // Per book margin around the edge (defaults to 16 pixels) + UInt32 fPageTMargin, fPageLMargin, fPageBMargin, fPageRMargin; + + // Some animation keys we use + plKey fPageTurnAnimKey; + + // Current list of linkable image chunks we have visible on the screen, for quick hit testing + hsTArray fVisibleLinks; + + static std::map fBookGUIs; + std::string fCurBookGUI; + + enum Refs + { + kRefImage = 0 + }; + + // Compiles the given string of esHTML source into our compiled chunk list + hsBool ICompileSource( const wchar_t *source, const plLocation &hintLoc ); + + // Frees our source array + void IFreeSource( void ); + + // Compile helpers + UInt8 IGetTagType( const wchar_t *string ); + hsBool IGetNextOption( const wchar_t *&string, wchar_t *name, wchar_t *option ); + + plKey IGetMipmapKey( const wchar_t *name, const plLocation &loc ); + + // Renders one (1) page into the given DTMap + void IRenderPage( UInt32 page, UInt32 whichDTMap, hsBool suppressRendering = false ); + + // moves the movie layers from one material onto another + void IMoveMovies( hsGMaterial *source, hsGMaterial *dest); + + // Starting at the given chunk, works backwards to determine the full set of current + // font properties at that point, or assigns defaults if none were specified + void IFindFontProps( UInt32 chunkIdx, const wchar_t *&face, UInt8 &size, UInt8 &flags, hsColorRGBA &color, Int16 &spacing ); + + // Find the last paragraph chunk and thus the last par alignment settings + UInt8 IFindLastAlignment( void ) const; + + // Handle clicks on either side of the book + void IHandleLeftSideClick( void ); + void IHandleRightSideClick( void ); + + // Just sends out a notify to our currently set receiver key + void ISendNotify( UInt32 type, UInt32 linkID = 0 ); + + // Close with a notify + void ITriggerCloseWithNotify( hsBool closeNotOpen, hsBool immediate ); + + // Finish showing the book, due to the animation being done seeking + void IFinishShow( hsBool startOpened ); + + // Find the current moused link, if any + Int32 IFindCurrVisibleLink( hsBool rightNotLeft, hsBool hoverNotUp ); + + // Ensures that all the page starts are calced up to the given page (but not including it) + void IRecalcPageStarts( UInt32 upToPage ); + + // Load (or unload) all the images for the book + void ILoadAllImages( hsBool unload ); + + // Purge the DynaTextMaps + void IPurgeDynaTextMaps( ); + + // Process a click on the given "check box" image + void IHandleCheckClick( UInt32 idx, pfBookData::WhichSide which ); + + // Draw me an image! + void IDrawMipmap( pfEsHTMLChunk *chunk, UInt16 x, UInt16 y, plMipmap *mip, plDynamicTextMap *dtMap, UInt32 whichDTMap, hsBool dontRender ); + + // Movie functions + loadedMovie *IMovieAlreadyLoaded(pfEsHTMLChunk *chunk); + loadedMovie *IGetMovieByIndex(UInt8 index); + plLayerBink *IMakeMovieLayer(pfEsHTMLChunk *chunk, UInt16 x, UInt16 y, plMipmap *baseMipmap, UInt32 whichDTMap, hsBool dontRender); + + // Cover functions + plLayerInterface *IMakeBaseLayer(plMipmap *image); + plLayerInterface *IMakeDecalLayer(pfEsHTMLChunk *decalChunk, plMipmap *decal, plMipmap *baseMipmap); + void ISetDecalLayers(hsGMaterial *material,hsTArray layers); +}; + + +#endif //_pfJournalBook_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBookCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBookCreatable.h new file mode 100644 index 00000000..4100f3bf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfJournalBook/pfJournalBookCreatable.h @@ -0,0 +1,38 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef _pfJournalBookCreatable_h +#define _pfJournalBookCreatable_h + +#include "../pnFactory/plCreator.h" + +#include "pfJournalBook.h" + +REGISTER_CREATABLE( pfBookData ); +REGISTER_NONCREATABLE( pfJournalBook ); + + +#endif // _pfJournalBookCreatable_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKI.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKI.cpp new file mode 100644 index 00000000..65e876f0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKI.cpp @@ -0,0 +1,1362 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfKI Functions // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "pfKI.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + +#include "../pfGameGUIMgr/pfGameGUIMgr.h" +#include "../pfGameGUIMgr/pfGUITagDefs.h" +#include "../pfGameGUIMgr/pfGUIDialogMod.h" +#include "../pfGameGUIMgr/pfGUIControlHandlers.h" +#include "../pfGameGUIMgr/pfGUIDialogHandlers.h" +#include "../pfGameGUIMgr/pfGUIEditBoxMod.h" +#include "../pfGameGUIMgr/pfGUIListBoxMod.h" +#include "../pfGameGUIMgr/pfGUIButtonMod.h" +#include "../pfGameGUIMgr/pfGUIListElement.h" +#include "../pfGameGUIMgr/pfGUITextBoxMod.h" +#include "../pfGameGUIMgr/pfGUIRadioGroupCtrl.h" + +#include "../plGImage/plDynamicTextMap.h" + +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetClient/plNetKI.h" +#include "../pnNetCommon/plNetMsg.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../pfMessage/pfKIMsg.h" +#include "../plMessage/plMemberUpdateMsg.h" +#include "../pnMessage/plTimeMsg.h" + + +#include "../pnMessage/plRemoteAvatarInfoMsg.h" + +#define kKITempID_ListOfLists 25 +#define kKITempID_MsgDestRadio 26 +#define kKITempID_SendBtn 27 + +#define kKITempID_CurrPlayerText 30 +#define kKITempID_PlayerList 31 +#define kKITempID_ChatModeBtn 32 + +#define kKITempID_BlackBarDlg 33 +#define kKITempID_BarKIButtons 34 + +#define kDesiredKIVersion 0 + +#define kMaxNumChatItems 42 + + +//// Static Class Stuff ////////////////////////////////////////////////////// + +pfKI *pfKI::fInstance = nil; + + +//// Dialog Proc Definitions ///////////////////////////////////////////////// + +class plKIYesNoBox : public pfGUIDialogProc +{ + protected: + + pfGUICtrlProcObject *fCBProc; + UInt32 fNoCBValue, fYesCBValue; + + public: + + plKIYesNoBox() { fCBProc = nil; fNoCBValue = 0; fYesCBValue = 0; } + + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + UInt32 cbValue = 0; + + if( ctrl->GetTagID() == kKIYesBtn ) + { + cbValue = fYesCBValue; + } + else if( ctrl->GetTagID() == kKINoBtn ) + { + cbValue = fNoCBValue; + } + fDialog->Hide(); + + // Call da callback + if( fCBProc != nil ) + fCBProc->UserCallback( cbValue ); + } + + void SetMessage( const char *msg ) + { + pfGUITextBoxMod *ctrl = pfGUITextBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKIStaticText ) ); + ctrl->SetText( msg ); + } + + void Ask( const char *msg, pfGUICtrlProcObject *callbackProc, UInt32 noCBValue, UInt32 yesCBValue ) + { + SetMessage( msg ); + fCBProc = callbackProc; + fNoCBValue = noCBValue; + fYesCBValue = yesCBValue; + fDialog->Show(); + } +}; + +//// BlackBar Proc /////////////////////////////////////////////////////////// + +class plBlackBarProc : public pfGUIDialogProc +{ + protected: + + pfGUIDialogMod *fKIMiniDlg, *fKIMainDlg; + pfGUIDialogProc *fOrigProc; + + static bool fExpected; + static pfGUIRadioGroupCtrl *fKIButtons; + + public: + + plBlackBarProc( pfGUIDialogMod *miniKI, pfGUIDialogMod *mainKI, pfGUIDialogProc *origProc ) + { + fOrigProc = origProc; + if( fOrigProc != nil ) + fOrigProc->IncRef(); + + fKIMiniDlg = miniKI; + fKIMainDlg = mainKI; + fExpected = false; + } + + virtual ~plBlackBarProc() + { + if( fOrigProc != nil && fOrigProc->DecRef() ) + delete fOrigProc; + } + + virtual void OnShow( void ) + { + fKIButtons = pfGUIRadioGroupCtrl::ConvertNoRef( fDialog->GetControlFromTag( kKITempID_BarKIButtons ) ); + if( fKIButtons != nil ) + { + fKIButtons->SetValue( -1 ); + fKIMiniDlg->Hide(); + fKIMainDlg->Hide(); + } + if( fOrigProc != nil ) fOrigProc->OnShow(); + } + + virtual void OnHide( void ) { if( fOrigProc != nil ) fOrigProc->OnHide(); } + virtual void OnInit( void ) { if( fOrigProc != nil ) fOrigProc->OnInit(); } + + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + if( ctrl->GetTagID() == kKITempID_BarKIButtons ) + { + fExpected = true; + if( fKIButtons->GetValue() == 0 ) fKIMiniDlg->Show(); else fKIMiniDlg->Hide(); + if( fKIButtons->GetValue() == 1 ) fKIMainDlg->Show(); else fKIMainDlg->Hide(); + fExpected = false; + } + else if( fOrigProc != nil ) + fOrigProc->DoSomething( ctrl ); + } + + static void ClearKIButtons( void ) + { + if( fKIButtons != nil && !fExpected ) + fKIButtons->SetValue( -1 ); + } +}; + +bool plBlackBarProc::fExpected = false; +pfGUIRadioGroupCtrl *plBlackBarProc::fKIButtons = nil; + + +//// KIMain Proc ///////////////////////////////////////////////////////////// + +class plKIMainProc : public pfGUIDialogProc +{ + protected: + + pfGUIListBoxMod *fOther; + plKIYesNoBox *fYesNoDlg; + pfGUIDialogMod *fMiniDlg; + + plKIFolder *fKIFolder; + + public: + plKIAddEditBox *fAddRemoveHandler; + + plKIMainProc( pfGUIControlMod *other, plKIYesNoBox *yesNo, pfGUIDialogMod *miniDlg ); + + virtual void DoSomething( pfGUIControlMod *ctrl ); + virtual void OnHide( void ); + virtual void UserCallback( UInt32 userValue ); + virtual void OnInit( void ); + virtual void OnShow( void ); + + void GrabVaultFolder( void ); + void UpdateTextList( void ); + void UpdateTextPreview( void ); + + void AddNewItem( const char *title, const char *text ); + void EditCurrItem( const char *newText ); + + void SetCurrentAvatar( plKey avKey ); +}; + +class plKIAddEditBox : public pfGUIDialogProc +{ + protected: + pfGUIListBoxMod *fList; + pfGUIDialogMod *fParentDlg; + plKIMainProc *fMainProc; + + public: + hsBool fEditing; + + plKIAddEditBox( pfGUIListBoxMod *list, pfGUIDialogMod *p, plKIMainProc *mainProc ) + { + fList = list; + fParentDlg = p; + fEditing = false; + fMainProc = mainProc; + } + + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + if( ctrl->GetTagID() != kKINoBtn ) + { + // Get the string and add it to the list + pfGUIEditBoxMod *edit = pfGUIEditBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITestEditBox ) ); + if( ctrl != (pfGUIControlMod *)edit || !edit->WasEscaped() ) + { + if( fEditing ) + fMainProc->EditCurrItem( edit->GetBuffer() ); + else + fMainProc->AddNewItem( "TestTitle", edit->GetBuffer() ); + } + } + fEditing = false; + + // Both controls should close the dialog + fDialog->Hide(); + } + + virtual void OnShow( void ) + { + pfGUIEditBoxMod *edit = pfGUIEditBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITestEditBox ) ); + if( fEditing ) + { + if( fList->GetSelection() != -1 ) + { + pfGUIListElement *el = fList->GetElement( fList->GetSelection() ); + if( el->GetType() == pfGUIListElement::kText ) + edit->SetText( ( (pfGUIListText *)el )->GetText() ); + } + } + else + edit->ClearBuffer(); + fDialog->SetFocus( edit ); + } +}; + +class pfKITextItemElement : public pfGUIListText +{ + protected: + + plKITextNoteElement *fDataItem; + + public: + pfKITextItemElement( plKITextNoteElement *source ) : pfGUIListText() + { + fDataItem = source; + } + + virtual const char *GetText( void ) { return fDataItem->GetText(); } + virtual void SetText( const char *text ) { fDataItem->SetText( text ); } + + plKITextNoteElement *GetSource( void ) { return fDataItem; } +}; + +class pfKIListItemElement : public pfGUIListText +{ + protected: + + plKIFolder *fFolder; + + public: + pfKIListItemElement( plKIFolder *folder) : pfGUIListText(), fFolder( folder ) {} + + virtual const char *GetText( void ) { return fFolder->GetName(); } + virtual void SetText( const char *text ) { } + + plKIFolder *GetFolder( void ) { return fFolder; } +}; + +plKIMainProc::plKIMainProc( pfGUIControlMod *other, plKIYesNoBox *yesNo, pfGUIDialogMod *miniDlg ) +{ + fOther = (pfGUIListBoxMod *)other; + fYesNoDlg = yesNo; + fMiniDlg = miniDlg; + fKIFolder = nil; +} + +void plKIMainProc::DoSomething( pfGUIControlMod *ctrl ) +{ + if( ctrl->GetTagID() == kKIAddButton ) + { + pfGUIDialogMod *dlg = pfGameGUIMgr::GetInstance()->GetDialogFromTag( kKIEntryDlg ); + dlg->Show(); + } + else if( ctrl->GetTagID() == kKIEditButton ) + { + // Edit btn + if( fOther->GetSelection() != -1 ) + { + pfGUIDialogMod *dlg = pfGameGUIMgr::GetInstance()->GetDialogFromTag( kKIEntryDlg ); + fAddRemoveHandler->fEditing = true; + dlg->Show(); + } + } + else if( ctrl->GetTagID() == kKIRemoveButton ) + { + // Remove btn - remove all selected items + if( fOther->GetSelection() != -1 ) + fYesNoDlg->Ask( "Are you sure you wish to remove this item?", this, 0, 1 ); + } + else if( ctrl->GetTagID() == kKITestEditBox ) // Yeah, yeah, i know + { + // List box. Gets sent/called when selection changes. + UpdateTextPreview(); + } + else if( ctrl->GetTagID() == kKITempID_ListOfLists ) + { + // Change lists + pfGUIListBoxMod *list = pfGUIListBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITempID_ListOfLists ) ); // Temp tag ID + if( list->GetSelection() != -1 ) + { + pfKIListItemElement *whichList = (pfKIListItemElement *)list->GetElement( list->GetSelection() ); + fKIFolder = whichList->GetFolder(); + } + else + fKIFolder = nil; + UpdateTextList(); + } +} + +void plKIMainProc::UpdateTextPreview( void ) +{ + pfGUIControlMod *editBtn = fDialog->GetControlFromTag( kKIEditButton ); + pfGUIControlMod *removeBtn = fDialog->GetControlFromTag( kKIRemoveButton ); + pfGUIControlMod *sendBtn = fDialog->GetControlFromTag( kKITempID_SendBtn ); + + pfGUITextBoxMod *text = pfGUITextBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITestControl2 ) ); + if( fOther->GetSelection() != -1 && fOther->GetElement( fOther->GetSelection() )->GetType() == pfGUIListElement::kText ) + { + pfGUIListText *element = (pfGUIListText *)fOther->GetElement( fOther->GetSelection() ); + text->SetText( element->GetText() ); + + sendBtn->SetVisible( true ); + editBtn->SetVisible( true ); + removeBtn->SetVisible( true ); + } + else + { + text->SetText( "" ); + sendBtn->SetVisible( false ); + editBtn->SetVisible( false ); + removeBtn->SetVisible( false ); + } +} + +void plKIMainProc::AddNewItem( const char *title, const char *text ) +{ + if( fKIFolder != nil ) + { + plKITextNoteElement *item = new plKITextNoteElement(); + item->SetTitle( title ); + item->SetText( text ); + fKIFolder->AddElement(item); + } +} + +void plKIMainProc::EditCurrItem( const char *newText ) +{ + pfKITextItemElement *listEl = (pfKITextItemElement *)fOther->GetElement( fOther->GetSelection() ); + listEl->GetSource()->SetText( newText ); +} + +void plKIMainProc::OnHide( void ) +{ + plBlackBarProc::ClearKIButtons(); +} + +void plKIMainProc::UserCallback( UInt32 userValue ) +{ + if( userValue == 1 ) + { + // Yes/no callback for removing an element + if( fOther->GetSelection() != -1 ) + { + pfKITextItemElement *listEl = (pfKITextItemElement *)fOther->GetElement( fOther->GetSelection() ); + + // This will result in a callback to update our list + fKIFolder->RemoveElement( listEl->GetSource() ); + } + } +} + +void plKIMainProc::OnShow( void ) +{ + GrabVaultFolder(); + UpdateTextList(); +} + +void plKIMainProc::GrabVaultFolder( void ) +{ + Int16 sel = -1; + + + plKI *kiVault = plNetClientMgr::GetInstance()->GetPlayerKI(); + fKIFolder = kiVault->FindFolder( plKIFolder::MatchesName( "INBOX" ) ); + + // Populate list-of-lists + pfGUIListBoxMod *listsList = pfGUIListBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITempID_ListOfLists ) ); // Temp tag ID + listsList->LockList(); + listsList->ClearAllElements(); + + plKIFolderVec * folders = kiVault->GetFolders(); + for( plKIFolderVec::const_iterator folderIter = folders->begin(); folderIter != folders->end(); ++folderIter ) + { + UInt16 id = listsList->AddElement( new pfKIListItemElement( *folderIter ) ); + if( *folderIter == fKIFolder ) + sel = id; + } + listsList->SetSelection( sel ); + listsList->UnlockList(); +} + +void plKIMainProc::UpdateTextList( void ) +{ + pfGUIControlMod *addBtn = fDialog->GetControlFromTag( kKIAddButton ); + int i; + + + if( fKIFolder == nil ) + { + fOther->ClearAllElements(); + UpdateTextPreview(); + addBtn->SetVisible( false ); + return; + } + + addBtn->SetVisible( true ); + plKIElementVec *items = fKIFolder->GetElements(); + + fOther->LockList(); + fOther->ClearAllElements(); + + for( i = 0; i < items->size(); i++ ) + { + plKITextNoteElement *note = plKITextNoteElement::ConvertNoRef( (*items)[ i ] ); + hsAssert( note != nil, "What the *#($& is a non-text item doing in the text item list??" ); + fOther->AddElement( new pfKITextItemElement( note ) ); + } + fOther->UnlockList(); + + UpdateTextPreview(); +} + +void plKIMainProc::OnInit( void ) +{ +} + +void plKIMainProc::SetCurrentAvatar( plKey avKey ) +{ + static char str[ 512 ]; + + + pfGUITextBoxMod *text = pfGUITextBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITempID_CurrPlayerText ) ); + const char *avName = plNetClientMgr::GetInstance()->GetPlayerName( avKey ); + if( text != nil ) + { + if( avName == nil ) + text->SetText( "Selected:" ); + else + { + sprintf( str, "%s", avName ); + text->SetText( str ); + } + } +} + +//// MiniKI Proc ///////////////////////////////////////////////////////////// + +#define kHackFlagLocalMsg 0x800 +class pfKIChatElement : public pfGUIListElement +{ + protected: + + plKITextNoteElement *fDataItem; + hsColorRGBA fTextColor; + char *fString; + UInt32 fFlags; + + public: + + pfKIChatElement( plKITextNoteElement *source ) : pfGUIListElement( 0 ) + { + fDataItem = source; + fFlags = 0; + if( strcmp( source->GetTitle(), plNetClientMgr::GetInstance()->GetPlayerName() ) == 0 ) + fFlags |= kHackFlagLocalMsg; + + fString = new char[ strlen( source->GetTitle() ) + strlen( source->GetText() ) + 3 ]; + sprintf( fString, "%s: %s", source->GetTitle(), source->GetText() ); + } + + pfKIChatElement( const char *user, const char *msg, UInt32 flags ) : pfGUIListElement( 0 ) + { + fDataItem = nil; + fFlags = flags; + + if( strcmp( user, plNetClientMgr::GetInstance()->GetPlayerName() ) == 0 ) + fFlags |= kHackFlagLocalMsg; + + if( fFlags & pfKIMsg::kAdminMsg ) + fTextColor.Set( 0, 0, 1, 1 ); + else if( fFlags & pfKIMsg::kPrivateMsg ) + fTextColor.Set( 1, 0, 0, 1 ); + else + fTextColor.Set( 1, 1, 1, 1 ); + + if( user == nil ) + user = " "; + if( msg == nil ) + msg = " "; + fString = new char[ strlen( user ) + strlen( msg ) + 3 ]; + sprintf( fString, "%s: %s", user, msg ); + } + + virtual ~pfKIChatElement() { delete [] fString; } + + virtual void Draw( plDynamicTextMap *textGen, UInt16 x, UInt16 y, UInt16 maxWidth, UInt16 maxHeight ) + { + if( fFlags & kHackFlagLocalMsg ) + fTextColor.a = fColors->fSelForeColor.a; + else + fTextColor.a = fColors->fForeColor.a; + + textGen->SetTextColor( fTextColor, fColors->fTransparent && fColors->fBackColor.a == 0.f ); + + textGen->DrawWrappedString( x + 2, y, fString, maxWidth - 4, maxHeight ); + } + + virtual void GetSize( plDynamicTextMap *textGen, UInt16 *width, UInt16 *height ) + { + *width = textGen->GetVisibleWidth() - 4; + textGen->CalcWrappedStringSize( fString, width, height ); + if( height != nil ) + *height += 0; + *width += 4; + } + + virtual int CompareTo( pfGUIListElement *rightSide ) + { + return -2; + } + + plKITextNoteElement *GetSource( void ) { return fDataItem; } +}; + +class pfKIListPlayerItem : public pfGUIListText +{ + protected: + + plKey fPlayerKey; + + public: + pfKIListPlayerItem( plKey key, hsBool inRange = false ) : pfGUIListText(), fPlayerKey( key ) + { + static char str[ 256 ]; + + + if( key == nil ) + SetText( "" ); + else + { + const char *name = plNetClientMgr::GetInstance()->GetPlayerName( key ); + if( inRange ) + { + sprintf( str, ">%s<", name != nil ? name : key->GetName() ); + SetText( str ); + } + else + SetText( name != nil ? name : key->GetName() ); + } + ISetJustify( true ); + } + plKey GetPlayerKey( void ) { return fPlayerKey; } +}; + +class plKIMiniProc : public pfGUIDialogProc +{ + protected: + + pfGUIDialogMod *fMainDlg; + pfGUIListBoxMod *fChatList; + plKIFolder *fChatVaultFolder; + hsBool fChatting, fInited; + float fFadeOutTimer, fFadeOutDelay; + float fForeAlpha, fSelForeAlpha; + + hsBool fLocalClientIsAdmin; + + public: + + plKIMiniProc( pfGUIDialogMod *main ) + { + fMainDlg = main; + fChatting = false; + fFadeOutDelay = 20.f; + fFadeOutTimer = 0.f; + fInited = false; + fLocalClientIsAdmin = false; + } + + void SetLocalClientAsAdmin( hsBool yes ) { fLocalClientIsAdmin = yes; } + + virtual void OnInit( void ) + { + fForeAlpha = fDialog->GetColorScheme()->fForeColor.a; + fSelForeAlpha = fDialog->GetColorScheme()->fSelForeColor.a; + fInited = true; + } + + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + if( ctrl->GetTagID() == kKIYesBtn ) + { + fDialog->Hide(); + fMainDlg->Show(); + } + else if( ctrl->GetTagID() == kKITestControl2 ) // I.E. chat box + { + pfGUIEditBoxMod *edit = pfGUIEditBoxMod::ConvertNoRef( ctrl ); + pfGUIControlMod *label = fDialog->GetControlFromTag( kKIStaticText ); + + if( !edit->WasEscaped() ) + SendChatItem( edit->GetBuffer() ); + + EnterChatMode( false ); + } + else if( ctrl->GetTagID() == kKITempID_PlayerList ) + { + pfGUIListBoxMod *list = pfGUIListBoxMod::ConvertNoRef( ctrl ); + if( list != nil ) // it BETTER be + { + if( list->GetSelection() == -1 && list->GetNumElements() > 0 ) + list->SetSelection( 0 ); + } + } + } + + virtual void OnShow( void ) + { + // Get our chat list + fChatList = pfGUIListBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITestEditBox ) ); + + EnterChatMode( false ); + + // Start with the chat not showing + fFadeOutTimer = 0.f; + IncFadeOutTimer( fFadeOutDelay + 2.f ); + + GrabChatList(); + UpdateChatList(); + RefreshUserList(); + } + + virtual void OnHide( void ) + { + plBlackBarProc::ClearKIButtons(); + } + + virtual void OnCtrlFocusChange( pfGUIControlMod *oldCtrl, pfGUIControlMod *newCtrl ) + { + if( oldCtrl != nil && oldCtrl->GetTagID() == kKITestControl2 && + ( newCtrl == nil || newCtrl->GetTagID() != kKITempID_ChatModeBtn ) ) + { + // We were chatting and lost focus, so hide the chatting controls + EnterChatMode( false ); + } + } + + void SetFadeOutDelay( hsScalar secs ) { fFadeOutDelay = secs; } + + void EnterChatMode( hsBool enteringNotLeaving ) + { + pfGUIEditBoxMod *edit = pfGUIEditBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITestControl2 ) ); + pfGUIControlMod *label = fDialog->GetControlFromTag( kKIStaticText ); + + if( enteringNotLeaving ) + { + if( !fDialog->IsVisible() ) + fDialog->Show(); + + edit->ClearBuffer(); + edit->SetVisible( true ); + label->SetVisible( true ); + fDialog->SetFocus( edit ); + IncFadeOutTimer( -fFadeOutTimer ); + } + else + { + edit->SetVisible( false ); + label->SetVisible( false ); + } + fChatting = enteringNotLeaving; + } + + hsBool IsChatting( void ) const { return fChatting; } + + void GrabChatList( void ) + { +/* plKI *kiVault = plNetKI::GetInstance(); + if( kiVault != nil ) + fChatVaultList = kiVault->FindList( &plKIList::MatchesListDefID( plKITextChatMsgsIRcvdList::Index() ) ); + else + fChatVaultList = nil; +*/ } + + plKIFolder *GetChatFolder( void ) const { return fChatVaultFolder; } + + void RefreshUserList( void ) + { + int i; + + + if( !fDialog->IsVisible() ) + return; + + pfGUIListBoxMod *userList = pfGUIListBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITempID_PlayerList ) ); + if( userList != nil ) + { + plKey currKey = nil; + + if( userList->GetNumElements() > 0 && userList->GetSelection() != -1 ) + currKey = ( (pfKIListPlayerItem *)userList->GetElement( userList->GetSelection() ) )->GetPlayerKey(); + + userList->LockList(); + userList->ClearAllElements(); + + if( plNetClientMgr::GetInstance() != nil ) + { + userList->AddElement( new pfKIListPlayerItem( nil ) ); + + plNetTransportMember **members = nil; + plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members ); + if( members != nil ) + { + for( i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ ) + { + plNetTransportMember *mbr = members[ i ]; + + if( mbr != nil && mbr->GetAvatarKey() != nil ) + { + hsBool inRange = ( i < plNetListenList::kMaxListenListSize || plNetListenList::kMaxListenListSize==-1) && + ( mbr->GetDistSq() < plNetListenList::kMaxListenDistSq ); + + userList->AddElement( new pfKIListPlayerItem( mbr->GetAvatarKey(), inRange ) ); + } + } + + delete [] members; + } + } + + if( currKey == nil ) + { + if( userList->GetNumElements() > 0 ) + userList->SetSelection( 0 ); + } + else + { + for( i = 0; i < userList->GetNumElements(); i++ ) + { + if( ( (pfKIListPlayerItem *)userList->GetElement( i ) )->GetPlayerKey() == currKey ) + { + userList->SetSelection( i ); + break; + } + } + if( i == userList->GetNumElements() && userList->GetNumElements() > 0 ) + userList->SetSelection( 0 ); + } + + userList->UnlockList(); + } + } + + virtual void HandleExtendedEvent( pfGUIControlMod *ctrl, UInt32 event ) + { + // The only controls that will trigger a HandleExtendedEvent() are the ones that we want + // to have force the text to show + if( pfGUIListBoxMod::ConvertNoRef( ctrl ) == nil || event != pfGUIListBoxMod::kListCleared ) + IncFadeOutTimer( -fFadeOutTimer ); + } + + void IncFadeOutTimer( float delSeconds ) + { + if( !fInited ) + return; + + if( fDialog->GetFocus() && fDialog->GetFocus()->GetTagID() == kKITestControl2 ) + delSeconds = -fFadeOutTimer; + + bool didntChange = ( fFadeOutTimer <= fFadeOutDelay && fFadeOutTimer + delSeconds <= fFadeOutDelay ) + || ( fFadeOutTimer > fFadeOutDelay + 1.f && delSeconds >= 0.f ); + + pfGUIColorScheme *colors = fDialog->GetColorScheme(); + + fFadeOutTimer += delSeconds; + if( fFadeOutTimer > fFadeOutDelay ) + { + if( fFadeOutTimer > fFadeOutDelay + 1.f ) + { + colors->fForeColor.a = 0.f; + colors->fSelForeColor.a = 0.f; + } + else + { + colors->fForeColor.a = fForeAlpha * ( fFadeOutDelay + 1.f - fFadeOutTimer ); + colors->fSelForeColor.a = fSelForeAlpha * ( fFadeOutDelay + 1.f - fFadeOutTimer ); + } + } + else + { + colors->fForeColor.a = fForeAlpha; + colors->fSelForeColor.a = fSelForeAlpha; + } + + if( !didntChange ) + fDialog->RefreshAllControls(); + } + + void UpdateChatList( void ) + { + if( !fDialog->IsVisible() ) + return; + + fChatList->LockList(); // Makes updates faster +// fChatList->ClearAllElements(); + +/* if( fChatVaultList != nil ) + { + int i; + plKIElementVec *items = fChatVaultList->GetElements(); + + for( i = 0; i < items->size(); i++ ) + { + plKITextNoteElement *note = plKITextNoteElement::ConvertNoRef( (*items)[ i ] ); + hsAssert( note != nil, "What the *#($& is a non-text item doing in the text item list??" ); + fChatList->AddElement( new pfKIChatElement( note ) ); + } + } + +*/ + fChatList->UnlockList(); + fChatList->ScrollToBegin(); + } + +/* void ReceivedChatItem( plKIElement *element ) + { + if( fChatList == nil ) + return; + + plKITextNoteElement *note = plKITextNoteElement::ConvertNoRef( element ); + if( note != nil ) + fChatList->AddElement( new pfKIChatElement( note ) ); + fChatList->ScrollToBegin(); + } +*/ + void ReceivedChatItem( const char *user, const char *msg, UInt32 flags ) + { + if( fChatList == nil ) + return; + + if( !fDialog->IsVisible() ) + fDialog->Show(); + + fChatList->AddElement( new pfKIChatElement( user, msg, flags ) ); + if( fChatList->GetNumElements() > kMaxNumChatItems ) + fChatList->RemoveElement( 0 ); + + fChatList->ScrollToBegin(); + IncFadeOutTimer( -fFadeOutTimer ); + } + + void SendChatItem( const char *text ) + { +// if( fChatVaultList != nil ) + { + const char *userName = plNetClientMgr::GetInstance()->GetPlayerName(); +/* plKITextNoteElement *item = new plKITextNoteElement(); + item->SetTitle( plNetClientMgr::GetInstance()->GetPlayerName() ); + item->SetText( text ); + fChatVaultList->AddElement( item ); +*/ + + pfGUIListBoxMod *userList = pfGUIListBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITempID_PlayerList ) ); + pfKIListPlayerItem *currPlayer = nil; + int mbrIndex = -1; + UInt32 msgFlags; + + if( userList != nil && userList->GetNumElements() > 0 && userList->GetSelection() != -1 ) + currPlayer = (pfKIListPlayerItem *)userList->GetElement( userList->GetSelection() ); + + if( currPlayer != nil && currPlayer->GetPlayerKey() != nil ) + mbrIndex = plNetClientMgr::GetInstance()->TransportMgr().FindMember( currPlayer->GetPlayerKey() ); + + pfKIMsg *msg = new pfKIMsg( pfKIMsg::kHACKChatMsg ); + msg->SetString( text ); + msg->SetUser( userName ); + if( fLocalClientIsAdmin ) + msg->SetFlags( pfKIMsg::kAdminMsg ); + + if( mbrIndex != -1 ) + { + // Send to one player + msg->SetFlags( msg->GetFlags() | pfKIMsg::kPrivateMsg ); + + msg->SetTimeStamp( 0 ); // Remove timestamp + + // write message (and label) to ram stream + hsRAMStream stream; + hsgResMgr::ResMgr()->WriteCreatable( &stream, msg ); + + // put stream in net msg wrapper + plNetMsgGameMessageDirected netMsgWrap; + netMsgWrap.StreamInfo()->CopyStream( &stream ); + netMsgWrap.StreamInfo()->SetStreamType( msg->ClassIndex() ); // type of game msg + netMsgWrap.SetTimeOffset( 0 ); + netMsgWrap.SetClassName( msg->ClassName() ); + netMsgWrap.SetSenderPlayerID( plNetClientMgr::GetInstance()->GetPlayerID() ); + + // set directed client receiver + netMsgWrap.Receivers()->AddReceiverPlayerID( plNetClientMgr::GetInstance()->TransportMgr().GetMember( mbrIndex )->GetPlayerID() ); + + // send + msgFlags = msg->GetFlags(); + plNetClientMgr::GetInstance()->SendMsg( &netMsgWrap, 0 ); + } + else + { + // Broadcast to all + msg->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce); + msg->SetBCastFlag(plMessage::kLocalPropagate, 0); + msgFlags = msg->GetFlags(); + plgDispatch::MsgSend( msg ); + } + + fChatList->AddElement( new pfKIChatElement( userName, text, msgFlags ) ); + fChatList->ScrollToBegin(); + IncFadeOutTimer( -fFadeOutTimer ); + if( fChatList->GetNumElements() > kMaxNumChatItems ) + fChatList->RemoveElement( 0 ); + } + } + + void SetCurrentAvatar( plKey avKey ) + { + static char str[ 512 ]; + int i; + + + pfGUITextBoxMod *text = pfGUITextBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITempID_CurrPlayerText ) ); + const char *avName = plNetClientMgr::GetInstance()->GetPlayerName( avKey ); + if( text != nil ) + { + if( avKey == nil || avName == nil ) + text->SetText( "" ); + else + { + sprintf( str, "%s", avName ); + text->SetText( str ); + } + IncFadeOutTimer( -fFadeOutTimer ); + } + + pfGUIListBoxMod *userList = pfGUIListBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKITempID_PlayerList ) ); + if( userList != nil && userList->GetNumElements() > 0 ) + { + if( avKey == nil ) + userList->SetSelection( 0 ); + else + { + for( i = 0; i < userList->GetNumElements(); i++ ) + { + pfKIListPlayerItem *el = (pfKIListPlayerItem *)userList->GetElement( i ); + if( el != nil && el->GetPlayerKey() == avKey ) + { + userList->SetSelection( i ); + break; + } + } + } + } + } +}; + +//// Callback From the KI Vault ////////////////////////////////////////////// + +class pfKITextVaultCallback : public plKICallback +{ + protected: + + plKIMainProc *fTextDlgProc; + plKIMiniProc *fMiniProc; + + public: + + pfKITextVaultCallback( plKIMainProc *proc, plKIMiniProc *miniProc ) + { + fTextDlgProc = proc; + fMiniProc = miniProc; + } + + void KIFolderAdded( plKIFolder *folder ) + { + } + + void KIFolderRemoved() + { + } + + void KIElementAddedToFolder( plKIElement *elem, plKIFolder *folder ) + { + fTextDlgProc->UpdateTextList(); +// if( folder == fMiniProc->GetChatFolder() ) +// fMiniProc->ReceivedChatItem( elem ); + } + + void KIElementChanged( plKIElement *elem ) + { + fTextDlgProc->UpdateTextList(); + } + + void KIElementSeen( plKIElement *elem ) + { + } + + void KIElementRemovedFromFolder( plKIFolder *folder ) + { + fTextDlgProc->UpdateTextList(); +// if( folder == fMiniProc->GetChatFolder() ) +// fMiniProc->UpdateChatList(); + } + + void KIAllChanged( void ) + { + // Gotta re-grab the list + fTextDlgProc->GrabVaultFolder(); + fTextDlgProc->UpdateTextList(); + fMiniProc->GrabChatList(); + fMiniProc->UpdateChatList(); + } + + void KISelectedBuddyChanged( plKIPlayerElement *elem ) + { + } + + void KISendModeChanged( int newMode ) + { + } +}; + +//// Constructor & Destructor //////////////////////////////////////////////// + +pfKI::pfKI() +{ +// fKIVaultCallback = nil; +// fInstance = this; +} + +pfKI::~pfKI() +{ + pfGameGUIMgr::GetInstance()->UnloadDialog( "KIMain" ); + pfGameGUIMgr::GetInstance()->UnloadDialog( "KIEntry" ); + pfGameGUIMgr::GetInstance()->UnloadDialog( "KIYesNo" ); + pfGameGUIMgr::GetInstance()->UnloadDialog( "KIMini" ); + pfGameGUIMgr::GetInstance()->UnloadDialog( "KIBlackBar" ); + +// delete fKIVaultCallback; +// fInstance = nil; +} + +void pfKI::Init( void ) +{ +#ifdef USE_INTERNAL_PLAYERBOOK + IInitPlayerBook(); +#endif // USE_INTERNAL_PLAYERBOOK + + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + + + // Load KI dat files + mgr->LoadDialog( "KIBlackBar" ); +#ifdef USE_INTERNAL_KI + mgr->LoadDialog( "KIMini" ); + mgr->LoadDialog( "KIMain" ); + mgr->LoadDialog( "KIEntry" ); + mgr->LoadDialog( "KIYesNo" ); + + + pfGUIDialogMod *yesNoDlg = mgr->GetDialogFromTag( kKIYesNoDlg ); + pfGUIDialogMod *mainDlg = mgr->GetDialogFromTag( kKIMainDialog ); + pfGUIDialogMod *arDlg = mgr->GetDialogFromTag( kKIEntryDlg ); + pfGUIDialogMod *miniDlg = mgr->GetDialogFromTag( kKIMiniDialog ); + pfGUIDialogMod *blackBarDlg = mgr->GetDialogFromTag( kKITempID_BlackBarDlg ); + + if( yesNoDlg == nil || mainDlg == nil || arDlg == nil || miniDlg == nil ) + { + hsStatusMessage( "==== WARNING: KI Interface not inited (GUI data missing) ====" ); + return; + } + if( mainDlg->GetVersion() != kDesiredKIVersion ) + { + char str[ 512 ]; + sprintf( str, "Incorrect KI dataset version. KI will not be loaded. (Looking for version %d, found version %d)", + kDesiredKIVersion, mainDlg->GetVersion() ); + hsMessageBox( str, "Error", hsMessageBoxNormal ); + return; + } + + // Init our yes/no dialog + fYesNoProc = new plKIYesNoBox; + yesNoDlg->SetHandler( fYesNoProc ); + + // Init our main dialog + pfGUIListBoxMod *listOfLists = pfGUIListBoxMod::ConvertNoRef( mainDlg->GetControlFromTag( kKITempID_ListOfLists ) ); + pfGUIListBoxMod *list = pfGUIListBoxMod::ConvertNoRef( mainDlg->GetControlFromTag( kKITestEditBox ) ); + pfGUIControlMod *editBtn = mainDlg->GetControlFromTag( kKIEditButton ); + pfGUIControlMod *removeBtn = mainDlg->GetControlFromTag( kKIRemoveButton ); + pfGUIRadioGroupCtrl *destRadio = pfGUIRadioGroupCtrl::ConvertNoRef( mainDlg->GetControlFromTag( kKITempID_MsgDestRadio ) ); + + fMainProc = new plKIMainProc( list, fYesNoProc, miniDlg ); + mainDlg->SetHandler( fMainProc ); + destRadio->SetValue( 0 ); + + + // Init our add/remove text item dialog + fAddEditProc = new plKIAddEditBox( pfGUIListBoxMod::ConvertNoRef( list ), mainDlg, fMainProc ); + arDlg->SetHandler( fAddEditProc ); + + fMainProc->fAddRemoveHandler = fAddEditProc; + + // Make us a proc for our mini dialog's maximize button + fMiniProc = new plKIMiniProc( mainDlg ); + miniDlg->SetHandler( fMiniProc ); + + // Set the callback for the ki vault thingy + plKI *kiVault = plNetClientMgr::GetInstance()->GetPlayerKI(); + fKIVaultCallback = new pfKITextVaultCallback( fMainProc, fMiniProc ); + kiVault->AddCallback( fKIVaultCallback ); + + // Finally, show our KI main dialog + if( blackBarDlg != nil ) + { + blackBarDlg->SetHandler( new plBlackBarProc( miniDlg, mainDlg, blackBarDlg->GetHandler() ) ); + blackBarDlg->Show(); + } + else + miniDlg->Show(); + + // Register for KI messages + plgDispatch::Dispatch()->RegisterForExactType( pfKIMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->RegisterForExactType( plRemoteAvatarInfoMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->RegisterForExactType( plMemberUpdateMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->RegisterForExactType( plTimeMsg::Index(), GetKey() ); +#endif // USE_INTERNAL_KI + +} + +hsBool pfKI::MsgReceive( plMessage *msg ) +{ + pfKIMsg *kiMsg = pfKIMsg::ConvertNoRef( msg ); + if( kiMsg != nil ) + { + if ( fMiniProc != nil ) + { + switch ( kiMsg->GetCommand() ) + { + case pfKIMsg::kHACKChatMsg: + fMiniProc->ReceivedChatItem( kiMsg->GetUser(), kiMsg->GetString(), kiMsg->GetFlags() ); + break; + + case pfKIMsg::kEnterChatMode: + fMiniProc->EnterChatMode( !fMiniProc->IsChatting() ); + break; + + case pfKIMsg::kSetChatFadeDelay: + fMiniProc->SetFadeOutDelay( kiMsg->GetDelay() ); + break; + + case pfKIMsg::kSetTextChatAdminMode: + fMiniProc->SetLocalClientAsAdmin( kiMsg->GetFlags() & pfKIMsg::kAdminMsg ? true : false ); + break; + } + } + + return true; + } + + plRemoteAvatarInfoMsg *avInfo = plRemoteAvatarInfoMsg::ConvertNoRef( msg ); + if( avInfo != nil ) + { + if( fMainProc != nil ) + fMainProc->SetCurrentAvatar( avInfo->GetAvatarKey() ); + if( fMiniProc != nil ) + fMiniProc->SetCurrentAvatar( avInfo->GetAvatarKey() ); + return true; + } + + plMemberUpdateMsg *userUpdateMsg = plMemberUpdateMsg::ConvertNoRef( msg ); + if( userUpdateMsg != nil ) + { + if( fMiniProc != nil ) + fMiniProc->RefreshUserList(); + return true; + } + + plTimeMsg *time = plTimeMsg::ConvertNoRef( msg ); + if( time != nil ) + { + if( fMiniProc != nil ) + fMiniProc->IncFadeOutTimer( time->DelSeconds() ); + return true; + } + + return hsKeyedObject::MsgReceive( msg ); +} + + +////////////////////////////////////////////////////////////////////////////// +//// Player Book Stuff /////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#include "../plNetCommon/plNetAvatarVault.h" +#include "../pfGameGUIMgr/pfGUICheckBoxCtrl.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../pfGameGUIMgr/pfGUIDynDisplayCtrl.h" +#include "../plGImage/plDynamicTextMap.h" +#include "../plJPEG/plJPEG.h" + +//// plPlayerBookProc //////////////////////////////////////////////////////// + +class plPlayerBookProc : public pfGUIDialogProc +{ + protected: + + pfGUIRadioGroupCtrl *fRadio; + pfGUITextBoxMod *fDescTextBox; + + public: + virtual void DoSomething( pfGUIControlMod *ctrl ) + { + if( ctrl->GetTagID() == kPBSaveSlotRadio ) + { + if( plNetClientMgr::GetInstance()->IsEnabled() ) + { + plPlayerLinkPoint &linkPt = plNetClientMgr::GetInstance()->GetLinkPoint( fRadio->GetValue() ); + fDescTextBox->SetText( linkPt.GetDescription() ); + } + } + else if( ctrl->GetTagID() == kPBLinkToBtn ) + { + if( plNetClientMgr::GetInstance()->IsEnabled() ) + { + plLinkBackToAgeMsg *msg = new plLinkBackToAgeMsg; + msg->AddReceiver( plNetClientMgr::GetInstance()->GetKey() ); + msg->fSavedLinkPointNum = fRadio->GetValue(); + plgDispatch::MsgSend( msg ); + } + else + { + } + } + else if( ctrl->GetTagID() == kPBSaveLinkBtn ) + { + if( plNetClientMgr::GetInstance()->IsEnabled() ) + { + plNetClientMgr::GetInstance()->SaveLinkPoint( fRadio->GetValue(), "Description" ); + } + else + { + } + } + } + + virtual void OnShow( void ) + { + fDescTextBox = pfGUITextBoxMod::ConvertNoRef( fDialog->GetControlFromTag( kKIStaticText ) ); + fDescTextBox->SetText( "" ); + + fRadio = pfGUIRadioGroupCtrl::ConvertNoRef( fDialog->GetControlFromTag( kPBSaveSlotRadio ) ); + fRadio->SetValue( 0 ); + + pfGUIDynDisplayCtrl *disp = pfGUIDynDisplayCtrl::ConvertNoRef( fDialog->GetControlFromTag( kPBSaveSlotPrev1 ) ); + plDynamicTextMap *map = disp->GetMap( 0 ); + map->ClearToColor( hsColorRGBA().Set( 1, 0, 0, 1 ) ); + + plMipmap *img = plJPEG::Instance().ReadFromFile( "e:\\plasma20\\data\\localdata\\testfile.jpg" ); + if( img == nil ) + return; + + map->DrawImage( 4, 4, img ); + } +}; + +//// IInitPlayerBook ///////////////////////////////////////////////////////// + +void pfKI::IInitPlayerBook( void ) +{ + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + + mgr->LoadDialog( "PBDialog" ); + + pfGUIDialogMod *pbDialog = mgr->GetDialogFromTag( kPlayerBook ); + if( pbDialog == nil ) + return; + + fPBProc = new plPlayerBookProc(); + pbDialog->SetHandler( fPBProc ); + pbDialog->Show(); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKI.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKI.h new file mode 100644 index 00000000..abb9378e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKI.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfKI Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfKI_h +#define _pfKI_h + +#include "hsTypes.h" +#include "../pnKeyedObject/hsKeyedObject.h" + + +//// Class Definition //////////////////////////////////////////////////////// + +class plKIYesNoBox; +class plKIAddEditBox; +class plKIMainProc; +class plKIMiniProc; +class plPlayerBookProc; +class pfKITextVaultCallback; + +class pfKI : public hsKeyedObject +{ + protected: + + plKIYesNoBox *fYesNoProc; + plKIAddEditBox *fAddEditProc; + plKIMainProc *fMainProc; + plKIMiniProc *fMiniProc; + + plPlayerBookProc *fPBProc; + + pfKITextVaultCallback *fKIVaultCallback; + + static pfKI *fInstance; + + void IInitPlayerBook( void ); + + public: + + pfKI(); + ~pfKI(); + + CLASSNAME_REGISTER( pfKI ); + GETINTERFACE_ANY( pfKI, plReceiver ); + + virtual hsBool MsgReceive( plMessage *msg ); + + void Init( void ); + + static pfKI *GetInstance( void ) { return fInstance; } +}; + + +#endif //_pfKI_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKICreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKICreatable.h new file mode 100644 index 00000000..4d53cbb8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfKICreatable.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pfKICreatable_inc +#define pfKICreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "pfKI.h" + +REGISTER_CREATABLE( pfKI ); + +#include "pfPlayerBookMod.h" + +REGISTER_CREATABLE( pfPlayerBookMod ); + + +#endif // pfKICreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfPlayerBookMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfPlayerBookMod.cpp new file mode 100644 index 00000000..d4592a04 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfPlayerBookMod.cpp @@ -0,0 +1,142 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfPlayerBookMod Definition // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "pfPlayerBookMod.h" +#include "../pfGameGUIMgr/pfGameGUIMgr.h" +#include "../pfGameGUIMgr/pfGUIButtonMod.h" +#include "../pfGameGUIMgr/pfGUICheckBoxCtrl.h" + +#include "../pnMessage/plRefMsg.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +pfPlayerBookMod::pfPlayerBookMod() +{ + int i; + + + for( i = 0; i < 6; i++ ) + { + fCheckBoxes[ i ] = nil; + fDynLayerKeys[ i ] = nil; + } + + fLoadButton = nil; + fSaveButton = nil; + + fPBProc = nil; +} + +pfPlayerBookMod::~pfPlayerBookMod() +{ +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool pfPlayerBookMod::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return false; +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool pfPlayerBookMod::MsgReceive( plMessage *msg ) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); + if( refMsg != nil ) + { + if( refMsg->fType == kRefCheckBox ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fCheckBoxes[ refMsg->fWhich ] = pfGUICheckBoxCtrl::ConvertNoRef( refMsg->GetRef() ); + else + fCheckBoxes[ refMsg->fWhich ] = nil; + } + else if( refMsg->fType == kRefLoadButton ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fLoadButton = pfGUIButtonMod::ConvertNoRef( refMsg->GetRef() ); + else + fLoadButton = nil; + } + else if( refMsg->fType == kRefSaveButton ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fSaveButton = pfGUIButtonMod::ConvertNoRef( refMsg->GetRef() ); + else + fSaveButton = nil; + } + return true; + } + + return plSingleModifier::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void pfPlayerBookMod::Read( hsStream *s, hsResMgr *mgr ) +{ + int i; + + + plSingleModifier::Read(s, mgr); + + for( i = 0; i < 6; i++ ) + mgr->ReadKeyNotifyMe( s, new plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kRefCheckBox ), plRefFlags::kActiveRef ); + + for( i = 0; i < 6; i++ ) + fDynLayerKeys[ i ] = mgr->ReadKey( s ); + + mgr->ReadKeyNotifyMe( s, new plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefLoadButton ), plRefFlags::kActiveRef ); + mgr->ReadKeyNotifyMe( s, new plGenRefMsg( GetKey(), plRefMsg::kOnCreate, -1, kRefSaveButton ), plRefFlags::kActiveRef ); +} + +void pfPlayerBookMod::Write( hsStream *s, hsResMgr *mgr ) +{ + int i; + + + plSingleModifier::Write( s, mgr ); + + for( i = 0; i < 6; i++ ) + mgr->WriteKey( s, fCheckBoxes[ i ]->GetKey() ); + + for( i = 0; i < 6; i++ ) + mgr->WriteKey( s, fDynLayerKeys[ i ] ); + + mgr->WriteKey( s, fLoadButton->GetKey() ); + mgr->WriteKey( s, fSaveButton->GetKey() ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfPlayerBookMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfPlayerBookMod.h new file mode 100644 index 00000000..167f1a0d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfKI/pfPlayerBookMod.h @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfPlayerBookMod Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfPlayerBookMod_h +#define _pfPlayerBookMod_h + +#include "../pnModifier/plSingleModifier.h" + +class plMessage; +class pfGUICheckBoxCtrl; +class pfGUIButtonMod; +class pfPlayerBookProc; + +class pfPlayerBookMod : public plSingleModifier +{ + protected: + + // We have six preview panes, each with a GUI check box control. + // We have to have pointers to all six checkboxes so we can attach + // procs to them, as well as all six dynamic layers that are the + // preview panes. + + pfGUICheckBoxCtrl *fCheckBoxes[ 6 ]; + plKey fDynLayerKeys[ 6 ]; + + // Also got a load and save button somewhere + pfGUIButtonMod *fLoadButton, *fSaveButton; + + pfPlayerBookProc *fPBProc; + + enum + { + kRefCheckBox, + kRefLoadButton, + kRefSaveButton + }; + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() + + public: + + pfPlayerBookMod(); + virtual ~pfPlayerBookMod(); + + CLASSNAME_REGISTER( pfPlayerBookMod ); + GETINTERFACE_ANY( pfPlayerBookMod, plSingleModifier ); + + virtual hsBool MsgReceive( plMessage* pMsg ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + +}; + +#endif // _pfPlayerBookMod_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationDataMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationDataMgr.cpp new file mode 100644 index 00000000..705287b6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationDataMgr.cpp @@ -0,0 +1,1408 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pfLocalizationDataMgr - singleton class for managing the +// localization XML data tree +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsUtils.h" +#include "../plResMgr/plLocalization.h" + +#include "../plFile/hsFiles.h" +#include "../plFile/plEncryptedStream.h" +#include "../plStatusLog/plStatusLog.h" + +#include "pfLocalizedString.h" +#include "pfLocalizationMgr.h" +#include "pfLocalizationDataMgr.h" + +#include "expat.h" + +#include + +#if HS_BUILD_FOR_MAC +#include +#endif + +// Add ..\..\..\..\..\StaticSDKs\XPlatform\expat-1.95.7\StaticLibs\Win32 to your +// lib include path if you include this file. +#pragma comment(lib, "libexpatwMT.lib") + + +////////////////////////////////////////////////////////////////////// +// +// LocalizationXMLFile - a basic class for storing all the +// localization data grabbed from a single XML +// file +// +////////////////////////////////////////////////////////////////////// + +class LocalizationXMLFile +{ +public: + //replace friend by static because of conflits with subtitleXMLFile + static void XMLCALL StartTag(void *userData, const XML_Char *element, const XML_Char **attributes); + static void XMLCALL EndTag(void *userData, const XML_Char *element); + static void XMLCALL HandleData(void *userData, const XML_Char *data, int stringLength); + friend class LocalizationDatabase; + + // first wstring is language, second is data + typedef std::map element; + + // the wstring is the element name + typedef std::map set; + + // the wstring is the set name + typedef std::map age; + + // the wstring is the age name + typedef std::map ageMap; + +protected: + std::wstring fLastError; + std::string fFilename; + XML_Parser fParser; + + struct tagInfo + { + std::wstring fTag; + std::map fAttributes; + }; + std::stack fTagStack; + + int fSkipDepth; // if we need to skip a block, this is the depth we need to skip to + + bool fIgnoreContents; // are we ignoring the contents between tags? + std::wstring fCurrentAge, fCurrentSet, fCurrentElement, fCurrentTranslation; + + ageMap fData; + + void IHandleLocalizationsTag(const tagInfo & parentTag, const tagInfo & thisTag); + + void IHandleAgeTag(const tagInfo & parentTag, const tagInfo & thisTag); + void IHandleSetTag(const tagInfo & parentTag, const tagInfo & thisTag); + void IHandleElementTag(const tagInfo & parentTag, const tagInfo & thisTag); + + void IHandleTranslationTag(const tagInfo & parentTag, const tagInfo & thisTag); + +public: + LocalizationXMLFile() : fLastError(L""), fFilename("") {} + + bool Parse(const std::string & fileName); // returns false on failure + void AddError(const std::wstring & errorText); + + std::wstring GetLastError() {return fLastError;} +}; + +// A few small helper structs +// I am setting these up so the header file can use this data without having to put +// the LocalizationXMLFile class into the header file +struct LocElementInfo +{ + LocalizationXMLFile::element fElement; +}; + +struct LocSetInfo +{ + LocalizationXMLFile::set fSet; +}; + +struct LocAgeInfo +{ + LocalizationXMLFile::age fAge; +}; + +////////////////////////////////////////////////////////////////////// +// Memory functions +////////////////////////////////////////////////////////////////////// + +static void * XMLCALL XmlMalloc (size_t size) { + return ALLOC(size); +} + +static void * XMLCALL XmlRealloc (void * ptr, size_t size) { + return REALLOC(ptr, size); +} + +static void XMLCALL XmlFree (void * ptr) { + FREE(ptr); +} + +XML_Memory_Handling_Suite gHeapAllocator = { + XmlMalloc, + XmlRealloc, + XmlFree +}; + + +////////////////////////////////////////////////////////////////////// +//// XML Parsing functions /////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +//metmet remove static +void XMLCALL LocalizationXMLFile::StartTag(void *userData, const XML_Char *element, const XML_Char **attributes) +{ +#if !HS_BUILD_FOR_MAC + std::wstring wElement = element; +#else + // jfim + wchar_t buf[2048], buf2[2048]; + BX_Char16ToWchar(element, buf); + std::wstring wElement = buf; // jfim: element; +#endif + LocalizationXMLFile *file = (LocalizationXMLFile*)userData; + std::map wAttributes; + + for (int i = 0; attributes[i]; i += 2) +#if !HS_BUILD_FOR_MAC + wAttributes[attributes[i]] = attributes[i+1]; +#else + { + // jfim + BX_Char16ToWchar(attributes[i], buf); + BX_Char16ToWchar(attributes[i+1], buf2); + wAttributes[buf] = buf2; + } +#endif + + LocalizationXMLFile::tagInfo parentTag; + if (!file->fTagStack.empty()) + parentTag = file->fTagStack.top(); + + LocalizationXMLFile::tagInfo newTag; + newTag.fTag = wElement; + newTag.fAttributes = wAttributes; + + file->fTagStack.push(newTag); + + if (file->fSkipDepth != -1) // we're currently skipping + return; + + // now we handle this tag + if (wElement == L"localizations") + file->IHandleLocalizationsTag(parentTag, newTag); + else if (wElement == L"age") + file->IHandleAgeTag(parentTag, newTag); + else if (wElement == L"set") + file->IHandleSetTag(parentTag, newTag); + else if (wElement == L"element") + file->IHandleElementTag(parentTag, newTag); + else if (wElement == L"translation") + file->IHandleTranslationTag(parentTag, newTag); + else + file->AddError(L"Unknown tag " + wElement + L" found"); +} +//metmet remove static and include the function inside LocalizationXMLFile +void XMLCALL LocalizationXMLFile::EndTag(void *userData, const XML_Char *element) +{ +#if !HS_BUILD_FOR_MAC + std::wstring wElement = element; +#else + // jfim + wchar_t buf[2048], buf2[2048]; + BX_Char16ToWchar(element, buf); + std::wstring wElement = buf; // jfim: element; +#endif + LocalizationXMLFile *file = (LocalizationXMLFile*)userData; + + if (file->fSkipDepth != -1) // we're currently skipping + { + // check to see if we are done with the block we wanted skipped + if (file->fTagStack.size() == file->fSkipDepth) + file->fSkipDepth = -1; // we're done skipping + } + + if (wElement == L"age") // we left the age block + file->fCurrentAge = L""; + else if (wElement == L"set") // we left the set block + file->fCurrentSet = L""; + else if (wElement == L"element") // we left the element block + file->fCurrentElement = L""; + else if (wElement == L"translation") // we left the translation block + { + file->fIgnoreContents = true; + file->fCurrentTranslation = L""; + } + + file->fTagStack.pop(); +} +//metmet remove static and include the function inside LocalizationXMLFile +void XMLCALL LocalizationXMLFile::HandleData(void *userData, const XML_Char *data, int stringLength) +{ + LocalizationXMLFile *file = (LocalizationXMLFile*)userData; + if (file->fIgnoreContents) + return; // we're ignoring data, so just return + if (file->fSkipDepth != -1) // we're currently skipping + return; + + // This gets all data between tags, including indentation and newlines + // so we'll have to ignore data when we aren't expecting it (not in a translation tag) + std::wstring wData = L""; + + for (int i = 0; i < stringLength; i++) + wData += data[i]; + + // we must be in a translation tag since that's the only tag that doesn't ignore the contents + file->fData[file->fCurrentAge][file->fCurrentSet][file->fCurrentElement][file->fCurrentTranslation] += wData; +} + +////////////////////////////////////////////////////////////////////// +//// LocalizationXMLFile Functions /////////////////////////////////// +////////////////////////////////////////////////////////////////////// + +#define FILEBUFFERSIZE 8192 + +//// IHandleSubtitlesTag() /////////////////////////////////////////// + +void LocalizationXMLFile::IHandleLocalizationsTag(const LocalizationXMLFile::tagInfo & parentTag, const LocalizationXMLFile::tagInfo & thisTag) +{ + if (parentTag.fTag != L"") // we only allow tags at root level + { + AddError(L"localizations tag only allowed at root level"); + return; + } +} + +//// IHandleAgeTag() ///////////////////////////////////////////////// + +void LocalizationXMLFile::IHandleAgeTag(const LocalizationXMLFile::tagInfo & parentTag, const LocalizationXMLFile::tagInfo & thisTag) +{ + // it has to be inside the subtitles tag + if (parentTag.fTag != L"localizations") + { + AddError(L"age tag can only be directly inside a localizations tag"); + return; + } + + // we have to have a name attribute + if (thisTag.fAttributes.find(L"name") == thisTag.fAttributes.end()) + { + AddError(L"age tag is missing the name attribute"); + return; + } + + fCurrentAge = thisTag.fAttributes.find(L"name")->second; +} + +//// IHandleSetTag() ///////////////////////////////////////////////// + +void LocalizationXMLFile::IHandleSetTag(const LocalizationXMLFile::tagInfo & parentTag, const LocalizationXMLFile::tagInfo & thisTag) +{ + // it has to be inside the age tag + if (parentTag.fTag != L"age") + { + AddError(L"set tag can only be directly inside a age tag"); + return; + } + + // we have to have a name attribute + if (thisTag.fAttributes.find(L"name") == thisTag.fAttributes.end()) + { + AddError(L"set tag is missing the name attribute"); + return; + } + + fCurrentSet = thisTag.fAttributes.find(L"name")->second; +} + +//// IHandleElementTag() ///////////////////////////////////////////// + +void LocalizationXMLFile::IHandleElementTag(const LocalizationXMLFile::tagInfo & parentTag, const LocalizationXMLFile::tagInfo & thisTag) +{ + // it has to be inside the element tag + if (parentTag.fTag != L"set") + { + AddError(L"element tag can only be directly inside a set tag"); + return; + } + + // we have to have a name attribute + if (thisTag.fAttributes.find(L"name") == thisTag.fAttributes.end()) + { + AddError(L"element tag is missing the name attribute"); + return; + } + + fCurrentElement = thisTag.fAttributes.find(L"name")->second; +} + +//// IHandleTranslationTag() ///////////////////////////////////////// + +void LocalizationXMLFile::IHandleTranslationTag(const LocalizationXMLFile::tagInfo & parentTag, const LocalizationXMLFile::tagInfo & thisTag) +{ + // it has to be inside the element tag + if (parentTag.fTag != L"element") + { + AddError(L"translation tag can only be directly inside a element tag"); + return; + } + + // we have to have a language attribute + if (thisTag.fAttributes.find(L"language") == thisTag.fAttributes.end()) + { + AddError(L"translation tag is missing the language attribute"); + return; + } + + fIgnoreContents = false; // we now want contents between tags + fCurrentTranslation = thisTag.fAttributes.find(L"language")->second; +} + +//// Parse() ///////////////////////////////////////////////////////// + +bool LocalizationXMLFile::Parse(const std::string & fileName) +{ + fFilename = fileName; + + fLastError = L""; + + while (!fTagStack.empty()) + fTagStack.pop(); + + fCurrentAge = L""; + fCurrentSet = L""; + fCurrentElement = L""; + fCurrentTranslation = L""; + + fIgnoreContents = true; + fSkipDepth = -1; + + char Buff[FILEBUFFERSIZE]; + + fParser = XML_ParserCreate_MM(NULL, &gHeapAllocator, NULL); + if (!fParser) + { + fLastError = L"ERROR: Couldn't allocate memory for parser"; + return false; + } + + XML_SetElementHandler(fParser, StartTag, EndTag); + XML_SetCharacterDataHandler(fParser, HandleData); + XML_SetUserData(fParser, (void*)this); + + hsStream *xmlStream = plEncryptedStream::OpenEncryptedFile(fileName.c_str(), false); + if (!xmlStream) + { + wchar_t *wFilename = hsStringToWString(fileName.c_str()); + fLastError += L"ERROR: Can't open file stream for "; + fLastError += wFilename; + fLastError += L"\n"; + delete [] wFilename; + return false; + } + + for (;;) + { + int done; + size_t len; + + len = xmlStream->Read(FILEBUFFERSIZE, Buff); + done = xmlStream->AtEnd(); + + if (XML_Parse(fParser, Buff, (int)len, done) == XML_STATUS_ERROR) + { + wchar_t lineNumber[256]; + _itow(XML_GetCurrentLineNumber(fParser), lineNumber, 10); + fLastError += L"ERROR: Parse error at line "; + fLastError += lineNumber; + fLastError += L": "; +#if !HS_BUILD_FOR_MAC + fLastError += XML_ErrorString(XML_GetErrorCode(fParser)); +#else + // jfim + wchar_t buf[2048]; + BX_Utf8ToWchar(XML_ErrorString(XML_GetErrorCode(fParser)), buf); + fLastError += buf; +#endif + fLastError += L"\n"; + XML_ParserFree(fParser); + fParser = nil; + xmlStream->Close(); + delete xmlStream; + return false; + } + + if (fLastError != L"") // some error occurred in the parser + { + XML_ParserFree(fParser); + fParser = nil; + xmlStream->Close(); + delete xmlStream; + return false; + } + + if (done) + break; + } + XML_ParserFree(fParser); + fParser = nil; + xmlStream->Close(); + delete xmlStream; + return true; +} + +//// AddError() ////////////////////////////////////////////////////// + +void LocalizationXMLFile::AddError(const std::wstring & errorText) +{ + wchar_t lineNumber[256]; + _itow(XML_GetCurrentLineNumber(fParser), lineNumber, 10); + fLastError += L"ERROR (line "; + fLastError += lineNumber; + fLastError += L"): " + errorText + L"\n"; + fSkipDepth = fTagStack.size(); // skip this block + return; +} + +////////////////////////////////////////////////////////////////////// +// +// LocalizationDatabase - a basic class for storing all the subtitle +// data grabbed from a XML directory (handles +// the merging and final validation of the data) +// +////////////////////////////////////////////////////////////////////// + +class LocalizationDatabase +{ +protected: + std::string fDirectory; // the directory we're supposed to parse + std::wstring fErrorString; // total sum of all errors encountered (also has warnings and status messages) + + std::vector fFiles; // the various XML files in that directory + + LocalizationXMLFile::ageMap fData; + + LocalizationXMLFile::element IMergeElementData(LocalizationXMLFile::element firstElement, LocalizationXMLFile::element secondElement, const std::wstring & fileName, const std::wstring & path); + LocalizationXMLFile::set IMergeSetData(LocalizationXMLFile::set firstSet, LocalizationXMLFile::set secondSet, const std::wstring & fileName, const std::wstring & path); + LocalizationXMLFile::age IMergeAgeData(LocalizationXMLFile::age firstAge, LocalizationXMLFile::age secondAge, const std::wstring & fileName, const std::wstring & path); + void IMergeData(); // merge all localization data in the files + + void IVerifyElement(const std::wstring &ageName, const std::wstring &setName, LocalizationXMLFile::set::iterator& curElement); + void IVerifySet(const std::wstring &ageName, const std::wstring &setName); + void IVerifyAge(const std::wstring &ageName); + void IVerifyData(); // verify the localization data once it has been merged in + +public: + LocalizationDatabase() {} + + void Parse(const std::string & directory); + void ResetOutput() {fErrorString = L"";} + std::wstring GetOutput() {return fErrorString;} + + LocalizationXMLFile::ageMap GetData() {return fData;} +}; + +////////////////////////////////////////////////////////////////////// +//// LocalizationDatabase Functions ////////////////////////////////// +////////////////////////////////////////////////////////////////////// + +//// IMergeElementData /////////////////////////////////////////////// + +LocalizationXMLFile::element LocalizationDatabase::IMergeElementData(LocalizationXMLFile::element firstElement, LocalizationXMLFile::element secondElement, const std::wstring & fileName, const std::wstring & path) +{ + // copy the data over, alerting the user to any duplicate translations + LocalizationXMLFile::element::iterator curTranslation; + for (curTranslation = secondElement.begin(); curTranslation != secondElement.end(); curTranslation++) + { + if (firstElement.find(curTranslation->first) != firstElement.end()) + fErrorString += L"Duplicate " + curTranslation->first + L" translation for " + path + L" found in file " + fileName + L" Ignoring second translation\n"; + else + firstElement[curTranslation->first] = curTranslation->second; + } + + return firstElement; +} + +//// IMergeSetData /////////////////////////////////////////////////// + +LocalizationXMLFile::set LocalizationDatabase::IMergeSetData(LocalizationXMLFile::set firstSet, LocalizationXMLFile::set secondSet, const std::wstring & fileName, const std::wstring & path) +{ + // Merge all the elements + LocalizationXMLFile::set::iterator curElement; + for (curElement = secondSet.begin(); curElement != secondSet.end(); curElement++) + { + // if the element doesn't exist in the current set, add it + if (firstSet.find(curElement->first) == firstSet.end()) + firstSet[curElement->first] = curElement->second; + else // merge the element in + firstSet[curElement->first] = IMergeElementData(firstSet[curElement->first], curElement->second, fileName, path + L"." + curElement->first); + } + + return firstSet; +} + +//// IMergeAgeData /////////////////////////////////////////////////// + +LocalizationXMLFile::age LocalizationDatabase::IMergeAgeData(LocalizationXMLFile::age firstAge, LocalizationXMLFile::age secondAge, const std::wstring & fileName, const std::wstring & path) +{ + // Merge all the sets + LocalizationXMLFile::age::iterator curSet; + for (curSet = secondAge.begin(); curSet != secondAge.end(); curSet++) + { + // if the set doesn't exist in the current age, just add it + if (firstAge.find(curSet->first) == firstAge.end()) + firstAge[curSet->first] = curSet->second; + else // merge the data in + firstAge[curSet->first] = IMergeSetData(firstAge[curSet->first], curSet->second, fileName, path + L"." + curSet->first); + } + + return firstAge; +} + +//// IMergeData() //////////////////////////////////////////////////// + +void LocalizationDatabase::IMergeData() +{ + for (int i = 0; i < fFiles.size(); i++) + { + std::wstring wFilename; + wchar_t *buff = hsStringToWString(fFiles[i].fFilename.c_str()); + wFilename = buff; + delete [] buff; + + LocalizationXMLFile::ageMap fileData = fFiles[i].fData; + LocalizationXMLFile::ageMap::iterator curAge; + for (curAge = fileData.begin(); curAge != fileData.end(); curAge++) + { + // if the age doesn't exist in the current merged database, just add it with no more checking + if (fData.find(curAge->first) == fData.end()) + fData[curAge->first] = curAge->second; + else // otherwise, merge the data in + fData[curAge->first] = IMergeAgeData(fData[curAge->first], curAge->second, wFilename, curAge->first); + } + } +} + +//// IVerifyElement() //////////////////////////////////////////////// + +void LocalizationDatabase::IVerifyElement(const std::wstring &ageName, const std::wstring &setName, LocalizationXMLFile::set::iterator& curElement) +{ + std::vector languageNames; + std::wstring defaultLanguage; + + int numLocales = plLocalization::GetNumLocales(); + for (int curLocale = 0; curLocale <= numLocales; curLocale++) + { + char *name = plLocalization::GetLanguageName((plLocalization::Language)curLocale); + wchar_t *wName = hsStringToWString(name); + languageNames.push_back(wName); + delete [] wName; + } + defaultLanguage = languageNames[0]; + + std::wstring elementName = curElement->first; + LocalizationXMLFile::element& theElement = curElement->second; + LocalizationXMLFile::element::iterator curTranslation; + for (curTranslation = theElement.begin(); curTranslation != theElement.end(); curTranslation++) + { + // Make sure this language exists! + bool languageExists = false; + for (int i = 0; i < languageNames.size(); i++) + { + if (languageNames[i] == curTranslation->first) + { + languageExists = true; + break; + } + } + if (!languageExists) + { + fErrorString += L"ERROR: The language " + curTranslation->first + L" used by " + ageName + L"." + setName + L"."; + fErrorString += elementName + L" is not supported, discarding translation\n"; +#if !HS_BUILD_FOR_MAC + curTranslation = theElement.erase(curTranslation); +#else + // jfim: I thought std::map::erase returned void? + theElement.erase(curTranslation); +#endif + curTranslation--; // because this will be incremented on the next run through the loop + continue; + } + } + + LocalizationXMLFile::set& theSet = fData[ageName][setName]; + if (theElement.find(defaultLanguage) == theElement.end()) + { + fErrorString += L"ERROR: Default language " + defaultLanguage + L" is missing from the translations in element "; + fErrorString += ageName + L"." + setName + L"." + elementName + L", deleting element\n"; +#if !HS_BUILD_FOR_MAC + curElement = theSet.erase(curElement); +#else + // jfim: I thought std::map::erase returned void? + theSet.erase(curElement); +#endif + curElement--; + return; + } + for (int i = 1; i < languageNames.size(); i++) + { + if (theElement.find(languageNames[i]) == theElement.end()) + { + fErrorString += L"WARNING: Language " + languageNames[i] + L" is missing from the translations in element "; + fErrorString += ageName + L"." + setName + L"." + elementName + L", you'll want to get translations for that!\n"; + } + } +} + +//// IVerifySet() //////////////////////////////////////////////////// + +void LocalizationDatabase::IVerifySet(const std::wstring &ageName, const std::wstring &setName) +{ + LocalizationXMLFile::set& theSet = fData[ageName][setName]; + LocalizationXMLFile::set::iterator curElement; + for (curElement = theSet.begin(); curElement != theSet.end(); curElement++) + IVerifyElement(ageName, setName, curElement); +} + +//// IVerifyAge() //////////////////////////////////////////////////// + +void LocalizationDatabase::IVerifyAge(const std::wstring &ageName) +{ + LocalizationXMLFile::age& theAge = fData[ageName]; + LocalizationXMLFile::age::iterator curSet; + for (curSet = theAge.begin(); curSet != theAge.end(); curSet++) + IVerifySet(ageName, curSet->first); +} + +//// IVerifyData() /////////////////////////////////////////////////// + +void LocalizationDatabase::IVerifyData() +{ + LocalizationXMLFile::ageMap::iterator curAge; + for (curAge = fData.begin(); curAge != fData.end(); curAge++) + IVerifyAge(curAge->first); +} + +//// Parse() ///////////////////////////////////////////////////////// + +void LocalizationDatabase::Parse(const std::string & directory) +{ + fDirectory = directory; + fFiles.clear(); + fErrorString = L""; + + char filename[255]; + hsFolderIterator xmlFolder((directory+PATH_SEPARATOR_STR).c_str()); + while(xmlFolder.NextFileSuffix(".loc")) + { + xmlFolder.GetPathAndName(filename); + + wchar_t *buff = hsStringToWString(filename); + std::wstring wFilename = buff; + delete [] buff; + + LocalizationXMLFile newFile; + bool retVal = newFile.Parse(filename); + if (!retVal) + fErrorString += L"Errors in file " + wFilename + L":\n" + newFile.GetLastError() + L"\n"; + + fFiles.push_back(newFile); + + fErrorString += L"File " + wFilename + L" parsed and added to database\n"; + } + + IMergeData(); + IVerifyData(); + + return; +} + +////////////////////////////////////////////////////////////////////// +//// pf3PartMap Functions //////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + +//// ISplitString() ////////////////////////////////////////////////// + +template +void pfLocalizationDataMgr::pf3PartMap::ISplitString(std::wstring key, std::wstring &age, std::wstring &set, std::wstring &name) +{ + std::wstring::size_type periodLoc = key.find(L"."); + age = key.substr(0, periodLoc); + if (periodLoc >= key.length()) + return; // don't get set or name if there isn't any period + + key = key.substr(periodLoc + 1, key.length()); + periodLoc = key.find(L"."); + set = key.substr(0, periodLoc); + if (periodLoc >= key.length()) + return; // don't get name if there isn't another period + + name = key.substr(periodLoc + 1, key.length()); +} + +//// exists() //////////////////////////////////////////////////////// + +template +bool pfLocalizationDataMgr::pf3PartMap::exists(const std::wstring & key) +{ + std::wstring age, set, name; + ISplitString(key, age, set, name); + if (age == L"" || set == L"" || name == L"") // if any are missing, it's invalid, so we don't have it + return false; + + // now check individually + if (fData.find(age) == fData.end()) // age doesn't exist + return false; + if (fData[age].find(set) == fData[age].end()) // set doesn't exist + return false; + if (fData[age][set].find(name) == fData[age][set].end()) // name doesn't exist + return false; + + // we passed all the tests, return true! + return true; +} + +//// setExists() ///////////////////////////////////////////////////// + +template +bool pfLocalizationDataMgr::pf3PartMap::setExists(const std::wstring & key) +{ + std::wstring age, set, name; + ISplitString(key, age, set, name); + if (age == L"" || set == L"") // if any are missing, it's invalid, so we don't have it (ignoring name) + return false; + + // now check individually + if (fData.find(age) == fData.end()) // age doesn't exist + return false; + if (fData[age].find(set) == fData[age].end()) // set doesn't exist + return false; + + // we passed all the tests, return true! + return true; +} + +//// erase() ///////////////////////////////////////////////////////// + +template +void pfLocalizationDataMgr::pf3PartMap::erase(const std::wstring & key) +{ + std::wstring age, set, name; + ISplitString(key, age, set, name); + if (age == L"" || set == L"" || name == L"") // if any are missing, it's invalid, so we don't delete it + return; + + // now check individually + if (fData.find(age) == fData.end()) // age doesn't exist + return; + if (fData[age].find(set) == fData[age].end()) // set doesn't exist + return; + if (fData[age][set].find(name) == fData[age][set].end()) // name doesn't exist + return; + + // ok, so now we want to nuke it! + fData[age][set].erase(name); + if (fData[age][set].size() == 0) // is the set now empty? + fData[age].erase(set); // nuke it! + if (fData[age].size() == 0) // is the age now empty? + fData.erase(age); // nuke it! +} + +//// operator[]() //////////////////////////////////////////////////// + +template +mapT &pfLocalizationDataMgr::pf3PartMap::operator[](const std::wstring &key) +{ + std::wstring age, set, name; + ISplitString(key, age, set, name); + return fData[age][set][name]; +} + +//// getAgeList() //////////////////////////////////////////////////// + +template +std::vector pfLocalizationDataMgr::pf3PartMap::getAgeList() +{ + std::vector retVal; + ThreePartMap::iterator curAge; + + for (curAge = fData.begin(); curAge != fData.end(); curAge++) + retVal.push_back(curAge->first); + + return retVal; +} + +//// getSetList() //////////////////////////////////////////////////// + +template +std::vector pfLocalizationDataMgr::pf3PartMap::getSetList(const std::wstring & age) +{ + std::vector retVal; + std::map >::iterator curSet; + + if (fData.find(age) == fData.end()) + return retVal; // return an empty list, the age doesn't exist + + for (curSet = fData[age].begin(); curSet != fData[age].end(); curSet++) + retVal.push_back(curSet->first); + + return retVal; +} + +//// getNameList() /////////////////////////////////////////////////// + +template +std::vector pfLocalizationDataMgr::pf3PartMap::getNameList(const std::wstring & age, const std::wstring & set) +{ + std::vector retVal; + std::map::iterator curName; + + if (fData.find(age) == fData.end()) + return retVal; // return an empty list, the age doesn't exist + + if (fData[age].find(set) == fData[age].end()) + return retVal; // return an empty list, the set doesn't exist + + for (curName = fData[age][set].begin(); curName != fData[age][set].end(); curName++) + retVal.push_back(curName->first); + + return retVal; +} + +////////////////////////////////////////////////////////////////////// +//// pfLocalizationDataMgr Functions ///////////////////////////////// +////////////////////////////////////////////////////////////////////// + +pfLocalizationDataMgr *pfLocalizationDataMgr::fInstance = nil; +plStatusLog *pfLocalizationDataMgr::fLog = nil; // output logfile + +//// Constructor/Destructor ////////////////////////////////////////// + +pfLocalizationDataMgr::pfLocalizationDataMgr(const std::string & path) +{ + hsAssert(!fInstance, "Tried to create the localization data manager more than once!"); + fInstance = this; + + fDataPath = path; + + fDatabase = nil; +} + +pfLocalizationDataMgr::~pfLocalizationDataMgr() +{ + fInstance = nil; + + if (fDatabase) + { + delete fDatabase; + fDatabase = nil; + } +} + +//// ICreateLocalizedElement ///////////////////////////////////////// + +pfLocalizationDataMgr::localizedElement pfLocalizationDataMgr::ICreateLocalizedElement() +{ + int numLocales = plLocalization::GetNumLocales(); + pfLocalizationDataMgr::localizedElement retVal; + + for (int curLocale = 0; curLocale <= numLocales; curLocale++) + { + char *name = plLocalization::GetLanguageName((plLocalization::Language)curLocale); + wchar_t *wName = hsStringToWString(name); + retVal[wName] = L""; + delete [] wName; + } + + return retVal; +} + +//// IGetCurrentLanguageName ///////////////////////////////////////// + +std::wstring pfLocalizationDataMgr::IGetCurrentLanguageName() +{ + std::wstring retVal; + char *name = plLocalization::GetLanguageName(plLocalization::GetLanguage()); + wchar_t *wName = hsStringToWString(name); + retVal = wName; + delete [] wName; + return retVal; +} + +//// IGetAllLanguageNames //////////////////////////////////////////// + +std::vector pfLocalizationDataMgr::IGetAllLanguageNames() +{ + int numLocales = plLocalization::GetNumLocales(); + std::vector retVal; + + for (int curLocale = 0; curLocale <= numLocales; curLocale++) + { + char *name = plLocalization::GetLanguageName((plLocalization::Language)curLocale); + wchar_t *wName = hsStringToWString(name); + retVal.push_back(wName); + delete [] wName; + } + + return retVal; +} + +//// IConvertSubtitle //////////////////////////////////////////////// + +void pfLocalizationDataMgr::IConvertElement(LocElementInfo *elementInfo, const std::wstring & curPath) +{ + pfLocalizationDataMgr::localizedElement newElement; + Int16 numArgs = -1; + + LocalizationXMLFile::element::iterator curTranslation; + for (curTranslation = elementInfo->fElement.begin(); curTranslation != elementInfo->fElement.end(); curTranslation++) + { + newElement[curTranslation->first].FromXML(curTranslation->second); + UInt16 argCount = newElement[curTranslation->first].GetArgumentCount(); + if (numArgs == -1) // just started + numArgs = argCount; + else if (argCount != numArgs) + { + std::wstring errorStr = L"WARNING: Argument number mismatch in element " + curPath; + char* cErrorStr = hsWStringToString(errorStr.c_str()); + fLog->AddLine(cErrorStr); + delete [] cErrorStr; + } + } + + fLocalizedElements[curPath] = newElement; +} + +//// IConvertSet ///////////////////////////////////////////////////// + +void pfLocalizationDataMgr::IConvertSet(LocSetInfo *setInfo, const std::wstring & curPath) +{ + LocalizationXMLFile::set::iterator curElement; + for (curElement = setInfo->fSet.begin(); curElement != setInfo->fSet.end(); curElement++) + { + LocElementInfo elementInfo; + elementInfo.fElement = curElement->second; + + IConvertElement(&elementInfo, curPath + L"." + curElement->first); + } +} + +//// IConvertAge ///////////////////////////////////////////////////// + +void pfLocalizationDataMgr::IConvertAge(LocAgeInfo *ageInfo, const std::wstring & curPath) +{ + LocalizationXMLFile::age::iterator curSet; + for (curSet = ageInfo->fAge.begin(); curSet != ageInfo->fAge.end(); curSet++) + { + LocSetInfo setInfo; + setInfo.fSet = curSet->second; + + IConvertSet(&setInfo, curPath + L"." + curSet->first); + } +} + +//// IConvertToByteStream //////////////////////////////////////////// + +char *pfLocalizationDataMgr::IConvertToByteStream(const std::wstring & data, UInt32 &len) +{ + len = data.length() * 2 + 2; // each wchar_t is two chars and add two bytes for the header + char *retVal = TRACKED_NEW char[len]; // we don't add an extra byte for the 0 because the parser doesn't need it + char lowbyte = 0, highbyte = 0; + retVal[0] = (char)0xFF; // insert FFFE for little-endian UTF-16 (big-endian would be FEFF) + retVal[1] = (char)0xFE; + int curByteStreamPos = 2; + for (int curLoc = 0; curLoc < data.length(); curLoc++) + { + wchar_t curChar = data[curLoc]; + lowbyte = (char)(curChar & 0x00FF); + highbyte = (char)((curChar & 0xFF00) >> 8); + + // since the data is AABBCCDD, we need to put in in our byte stream as BBAADDCC + // (so it kinda looks backward because we're storing this as little-endian) + retVal[curByteStreamPos + 1] = highbyte; + retVal[curByteStreamPos] = lowbyte; + curByteStreamPos += 2; + } + return retVal; +} + +//// IWriteText ////////////////////////////////////////////////////// + +void pfLocalizationDataMgr::IWriteText(const std::string & filename, const std::wstring & ageName, const std::wstring & languageName) +{ + bool weWroteData = false; // did we actually write any data of consequence? + bool setEmpty = true; + + // we will try to pretty print it all so it's easy to read for the devs + std::wstring fileData = L"\n"; // stores the xml we are going to write to the file (UTF-16 format) + fileData += L"\n"; + fileData += L"\t\n"; + + std::vector setNames = GetSetList(ageName); + for (int curSet = 0; curSet < setNames.size(); curSet++) + { + setEmpty = true; // so far, this set is empty + std::wstring setCode = L""; + setCode += L"\t\t\n"; + + std::vector elementNames = GetElementList(ageName, setNames[curSet]); + for (int curElement = 0; curElement < elementNames.size(); curElement++) + { + setCode += L"\t\t\t\n"; + std::wstring key = ageName + L"." + setNames[curSet] + L"." + elementNames[curElement]; + + if (fLocalizedElements[key].find(languageName) != fLocalizedElements[key].end()) + { + std::wstring key = ageName + L"." + setNames[curSet] + L"." + elementNames[curElement]; + weWroteData = true; + setEmpty = false; + setCode += L"\t\t\t\t"; + setCode += fLocalizedElements[key][languageName].ToXML(); + setCode += L"\n"; + } + + setCode += L"\t\t\t\n"; + } + + setCode += L"\t\t\n"; + + if (!setEmpty) + fileData += setCode; + } + + fileData += L"\t\n"; + fileData += L"\n"; + + if (weWroteData) + { + // now spit the results out to the file + UInt32 numBytes; + char *byteStream = IConvertToByteStream(fileData, numBytes); + hsStream *xmlStream = plEncryptedStream::OpenEncryptedFileWrite(filename.c_str()); + xmlStream->Write(numBytes, byteStream); + xmlStream->Close(); + delete xmlStream; + delete [] byteStream; + } +} + +//// Initialize ////////////////////////////////////////////////////// + +void pfLocalizationDataMgr::Initialize(const std::string & path) +{ + if (fInstance) + return; + + fInstance = TRACKED_NEW pfLocalizationDataMgr(path); + fLog = plStatusLogMgr::GetInstance().CreateStatusLog(30, "LocalizationDataMgr.log", + plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kTimestamp); + fInstance->SetupData(); +} + +//// Shutdown //////////////////////////////////////////////////////// + +void pfLocalizationDataMgr::Shutdown() +{ + if ( fLog != nil ) + { + delete fLog; + fLog = nil; + } + + if (fInstance) + { + delete fInstance; + fInstance = nil; + } +} + +//// SetupData /////////////////////////////////////////////////////// + +void pfLocalizationDataMgr::SetupData() +{ + if (fDatabase) + delete fDatabase; + + fDatabase = TRACKED_NEW LocalizationDatabase(); + fDatabase->Parse(fDataPath); + + char *temp = hsWStringToString(fDatabase->GetOutput().c_str()); + fLog->AddLine(temp); + delete [] temp; + + fLog->AddLine("File reading complete, converting to native data format"); + + // and now we read all the data out of the database and convert it to our native formats + + // transfer subtitle data + LocalizationXMLFile::ageMap data = fDatabase->GetData(); + LocalizationXMLFile::ageMap::iterator curAge; + for (curAge = data.begin(); curAge != data.end(); curAge++) + { + LocAgeInfo ageInfo; + ageInfo.fAge = curAge->second; + + IConvertAge(&ageInfo, curAge->first); + } + + OutputTreeToLog(); +} + +//// GetElement ////////////////////////////////////////////////////// + +pfLocalizedString pfLocalizationDataMgr::GetElement(const std::wstring & name) +{ + pfLocalizedString retVal; // if this returns before we initialize it, it will be empty, indicating failure + + if (!fLocalizedElements.exists(name)) // does the requested element exist? + return retVal; // nope, so return failure + + std::wstring languageName = IGetCurrentLanguageName(); + if (fLocalizedElements[name].find(languageName) == fLocalizedElements[name].end()) // current language isn't specified + { + languageName = L"English"; // force to english + if (fLocalizedElements[name].find(languageName) == fLocalizedElements[name].end()) // make sure english exists + return retVal; // language doesn't exist + } + retVal = fLocalizedElements[name][languageName]; + return retVal; +} + +//// GetSpecificElement ////////////////////////////////////////////// + +pfLocalizedString pfLocalizationDataMgr::GetSpecificElement(const std::wstring & name, const std::wstring & language) +{ + pfLocalizedString retVal; // if this returns before we initialize it, it will have an ID of 0, indicating failure + + if (!fLocalizedElements.exists(name)) // does the requested subtitle exist? + return retVal; // nope, so return failure + + if (fLocalizedElements[name].find(language) == fLocalizedElements[name].end()) + return retVal; // language doesn't exist + + retVal = fLocalizedElements[name][language]; + return retVal; +} + +//// GetAgeList ////////////////////////////////////////////////////// + +std::vector pfLocalizationDataMgr::GetAgeList() +{ + return fLocalizedElements.getAgeList(); +} + +//// GetSetList ////////////////////////////////////////////////////// + +std::vector pfLocalizationDataMgr::GetSetList(const std::wstring & ageName) +{ + return fLocalizedElements.getSetList(ageName); +} + +//// GetElementList ////////////////////////////////////////////////// + +std::vector pfLocalizationDataMgr::GetElementList(const std::wstring & ageName, const std::wstring & setName) +{ + return fLocalizedElements.getNameList(ageName, setName); +} + +//// GetLanguages //////////////////////////////////////////////////// + +std::vector pfLocalizationDataMgr::GetLanguages(const std::wstring & ageName, const std::wstring & setName, const std::wstring & elementName) +{ + std::vector retVal; + std::wstring key = ageName + L"." + setName + L"." + elementName; + if (fLocalizedElements.exists(key)) + { + // age, set, and element exists + localizedElement elem = fLocalizedElements[key]; + localizedElement::iterator curLanguage; + for (curLanguage = elem.begin(); curLanguage != elem.end(); curLanguage++) + { + std::wstring language = curLanguage->first; + if (language != L"") // somehow blank language names sneak in... so don't return them + retVal.push_back(curLanguage->first); + } + } + return retVal; +} + +//// GetElementXMLData /////////////////////////////////////////////// + +std::wstring pfLocalizationDataMgr::GetElementXMLData(const std::wstring & name, const std::wstring & languageName) +{ + std::wstring retVal = L""; + if (fLocalizedElements.exists(name)) + { + if (fLocalizedElements[name].find(languageName) != fLocalizedElements[name].end()) + retVal = fLocalizedElements[name][languageName].ToXML(); + } + return retVal; +} + +//// GetElementPlainTextData ///////////////////////////////////////// + +std::wstring pfLocalizationDataMgr::GetElementPlainTextData(const std::wstring & name, const std::wstring & languageName) +{ + std::wstring retVal = L""; + if (fLocalizedElements.exists(name)) + { + if (fLocalizedElements[name].find(languageName) != fLocalizedElements[name].end()) + retVal = (std::wstring)fLocalizedElements[name][languageName]; + } + return retVal; +} + +//// SetElementXMLData /////////////////////////////////////////////// + +bool pfLocalizationDataMgr::SetElementXMLData(const std::wstring & name, const std::wstring & languageName, const std::wstring & xmlData) +{ + if (!fLocalizedElements.exists(name)) + return false; // doesn't exist + + fLocalizedElements[name][languageName].FromXML(xmlData); + return true; +} + +//// SetElementPlainTextData ///////////////////////////////////////// + +bool pfLocalizationDataMgr::SetElementPlainTextData(const std::wstring & name, const std::wstring & languageName, const std::wstring & plainText) +{ + if (!fLocalizedElements.exists(name)) + return false; // doesn't exist + + fLocalizedElements[name][languageName] = plainText; + return true; +} + +//// AddLocalization ///////////////////////////////////////////////// + +bool pfLocalizationDataMgr::AddLocalization(const std::wstring & name, const std::wstring & newLanguage) +{ + if (!fLocalizedElements.exists(name)) + return false; // doesn't exist + + // copy the english over so it can be localized + fLocalizedElements[name][newLanguage] = fLocalizedElements[name][L"English"]; + return true; +} + +//// AddElement ////////////////////////////////////////////////////// + +bool pfLocalizationDataMgr::AddElement(const std::wstring & name) +{ + if (fLocalizedElements.exists(name)) + return false; // already exists + + pfLocalizedString newElement; + fLocalizedElements[name][L"English"] = newElement; + return true; +} + +//// DeleteLocalization ////////////////////////////////////////////// + +bool pfLocalizationDataMgr::DeleteLocalization(const std::wstring & name, const std::wstring & languageName) +{ + if (!fLocalizedElements.exists(name)) + return false; // doesn't exist + + if (fLocalizedElements[name].find(languageName) == fLocalizedElements[name].end()) + return false; // doesn't exist + + fLocalizedElements[name].erase(languageName); + return true; +} + +//// DeleteElement /////////////////////////////////////////////////// + +bool pfLocalizationDataMgr::DeleteElement(const std::wstring & name) +{ + if (!fLocalizedElements.exists(name)) + return false; // doesn't exist + + // delete it! + fLocalizedElements.erase(name); + return true; +} + +//// WriteDatabaseToDisk ///////////////////////////////////////////// + +void pfLocalizationDataMgr::WriteDatabaseToDisk(const std::string & path) +{ + // first, write the styles and panel settings to styles.sub + std::vector ageNames = GetAgeList(); + std::vector languageNames = IGetAllLanguageNames(); + for (int curAge = 0; curAge < ageNames.size(); curAge++) + { + for (int curLanguage = 0; curLanguage < languageNames.size(); curLanguage++) + { + std::string cAgeName, cLanguageName; + char *temp = hsWStringToString(ageNames[curAge].c_str()); + cAgeName = temp; + delete [] temp; + temp = hsWStringToString(languageNames[curLanguage].c_str()); + cLanguageName = temp; + delete [] temp; + + IWriteText(path + "/" + cAgeName + cLanguageName + ".loc", ageNames[curAge], languageNames[curLanguage]); + } + } +} + +//// OutputTreeToLog ///////////////////////////////////////////////// + +void pfLocalizationDataMgr::OutputTreeToLog() +{ + std::vector ages = GetAgeList(); + + fLog->AddLine("\n"); + fLog->AddLine("Localization tree:\n"); + + for (int i = 0; i < ages.size(); i++) + { + char *ageName = hsWStringToString(ages[i].c_str()); + std::string temp = ageName; + delete [] ageName; + + temp = "\t" + temp + "\n"; + fLog->AddLine(temp.c_str()); + + std::vector sets = GetSetList(ages[i]); + + for (int j = 0; j < sets.size(); j++) + { + char *setName = hsWStringToString(sets[j].c_str()); + std::string temp = setName; + delete [] setName; + + temp = "\t\t" + temp + "\n"; + fLog->AddLine(temp.c_str()); + + std::vector names = GetElementList(ages[i], sets[j]); + + for (int k = 0; k < names.size(); k++) + { + char *elemName = hsWStringToString(names[k].c_str()); + std::string temp = elemName; + delete [] elemName; + + temp = "\t\t\t" + temp + "\n"; + fLog->AddLine(temp.c_str()); + } + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationDataMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationDataMgr.h new file mode 100644 index 00000000..02c31e79 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationDataMgr.h @@ -0,0 +1,141 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pfLocalizationDataMgr - singleton class for managing the +// localization XML data tree +// +////////////////////////////////////////////////////////////////////// + +#ifndef _pfLocalizationDataMgr_h +#define _pfLocalizationDataMgr_h + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include "pfLocalizedString.h" + +class plStatusLog; + +// Helper classes/structs that are only used in this main class +class LocalizationDatabase; +struct LocElementInfo; +struct LocSetInfo; +struct LocAgeInfo; + +class pfLocalizationDataMgr +{ +private: + static pfLocalizationDataMgr* fInstance; + static plStatusLog* fLog; + +protected: + // This is a special case map class that will deconstruct the "Age.Set.Name" key into component parts + // and store them so that a list of each part given it's parent part is easy to grab. I.e. I can grab + // a list of all age names, a list of all set names (given age name) and a set of all names (given + // age and set names) + template + class pf3PartMap + { + protected: + // Outer map is Age, then Set, finally Name + typedef std::map > > ThreePartMap; + ThreePartMap fData; + + void ISplitString(std::wstring key, std::wstring &age, std::wstring &set, std::wstring &name); + public: + // We will just have very basic functionality + bool exists(const std::wstring & key); // returns true if the key exists + bool setExists(const std::wstring & key); // returns true if the age.set exists (ignores name if passed in) + void erase(const std::wstring & key); // erases the key from the map + + mapT &operator[](const std::wstring &key); // returns the item referenced by the key (and creates if necessary) + + std::vector getAgeList(); // returns a list of all ages in this map + std::vector getSetList(const std::wstring & age); // returns a list of all sets in the specified age + std::vector getNameList(const std::wstring & age, const std::wstring & set); + }; + + LocalizationDatabase *fDatabase; + + typedef std::map localizedElement; + + // Contains all localized strings, the key is the Age.Set.Name specified by XML, in localizedElement, the key is the language string + pf3PartMap fLocalizedElements; + + std::string fDataPath; + + localizedElement ICreateLocalizedElement(); // ease of use function that creates a basic localized element object + + std::wstring IGetCurrentLanguageName(); // get the name of the current language + std::vector IGetAllLanguageNames(); + + void IConvertElement(LocElementInfo *elementInfo, const std::wstring & curPath); + void IConvertSet(LocSetInfo *setInfo, const std::wstring & curPath); + void IConvertAge(LocAgeInfo *ageInfo, const std::wstring & curPath); + + char *IConvertToByteStream(const std::wstring & data, UInt32 &len); // converts the wstring data to a string of bytes for file writing + void IWriteText(const std::string & filename, const std::wstring & ageName, const std::wstring & languageName); // Write localization text to the specified file + + pfLocalizationDataMgr(const std::string & path); +public: + virtual ~pfLocalizationDataMgr(); + + static void Initialize(const std::string & path); + static void Shutdown(); + static pfLocalizationDataMgr &Instance(void) {return *fInstance;} + static bool InstanceValid(void) {return fInstance != nil;} + + void SetupData(); + + pfLocalizedString GetElement(const std::wstring & name); + pfLocalizedString GetSpecificElement(const std::wstring & name, const std::wstring & languageName); + + std::vector GetAgeList(); + std::vector GetSetList(const std::wstring & ageName); + std::vector GetElementList(const std::wstring & ageName, const std::wstring & setName); + std::vector GetLanguages(const std::wstring & ageName, const std::wstring & setName, const std::wstring & elementName); + + std::wstring GetElementXMLData(const std::wstring & name, const std::wstring & languageName); + std::wstring GetElementPlainTextData(const std::wstring & name, const std::wstring & languageName); + + // These convert the XML data to the actual subtitle and return true if successful (editor only) + bool SetElementXMLData(const std::wstring & name, const std::wstring & languageName, const std::wstring & xmlData); + bool SetElementPlainTextData(const std::wstring & name, const std::wstring & languageName, const std::wstring & plainText); + + // Addition and deletion functions, return true if successful (editor only) + bool AddLocalization(const std::wstring & name, const std::wstring & newLanguage); + bool AddElement(const std::wstring & name); + bool DeleteLocalization(const std::wstring & name, const std::wstring & languageName); + bool DeleteElement(const std::wstring & name); + + // Writes the current database to the disk (editor only). It will create all the files and put them into path + void WriteDatabaseToDisk(const std::string & path); + + void OutputTreeToLog(); // prints the localization tree to the log file +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.cpp new file mode 100644 index 00000000..94e4de0b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.cpp @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pfLocalizationMgr - singleton class for managing localization of +// strings (so python doesn't have to do it) +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" + +#include "pfLocalizedString.h" +#include "pfLocalizationDataMgr.h" +#include "pfLocalizationMgr.h" + +////////////////////////////////////////////////////////////////////// +//// pfLocalizationMgr Functions ///////////////////////////////////// +////////////////////////////////////////////////////////////////////// + +pfLocalizationMgr *pfLocalizationMgr::fInstance = nil; + +//// Constructor/Destructor ////////////////////////////////////////// + +pfLocalizationMgr::pfLocalizationMgr() +{ + hsAssert(!fInstance, "Tried to create the localization manager more than once!"); + fInstance = this; +} + +pfLocalizationMgr::~pfLocalizationMgr() +{ + fInstance = nil; +} + +//// Initialize ////////////////////////////////////////////////////// + +void pfLocalizationMgr::Initialize(const std::string & dataPath) +{ + if (fInstance) + return; + + fInstance = TRACKED_NEW pfLocalizationMgr(); + pfLocalizationDataMgr::Initialize(dataPath); // set up the data manager +} + +//// Shutdown //////////////////////////////////////////////////////// + +void pfLocalizationMgr::Shutdown() +{ + if (fInstance) + { + pfLocalizationDataMgr::Shutdown(); // make sure the subtitle data manager is shut down + delete fInstance; + } +} + +//// GetString /////////////////////////////////////////////////////// + +std::wstring pfLocalizationMgr::GetString(const std::wstring & path, const std::vector & args) +{ + return pfLocalizationDataMgr::Instance().GetElement(path) % args; +} + +std::wstring pfLocalizationMgr::GetString(const std::wstring & path) +{ + std::vector args; // blank args so that % signs are still handled correctly + return pfLocalizationDataMgr::Instance().GetElement(path) % args; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.h new file mode 100644 index 00000000..72ed1f5d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizationMgr.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pfLocalizationMgr - singleton class for managing localization of +// strings (so python doesn't have to do it) +// +////////////////////////////////////////////////////////////////////// + +#ifndef _pfLocalizationMgr_h +#define _pfLocalizationMgr_h + +#include "hsTypes.h" +#include "hsStlUtils.h" + +class pfLocalizationMgr +{ +private: + static pfLocalizationMgr *fInstance; +protected: + pfLocalizationMgr(); +public: + virtual ~pfLocalizationMgr(); + + static void Initialize(const std::string & dataPath); + static void Shutdown(); + static pfLocalizationMgr &Instance(void) {return *fInstance;} + static bool InstanceValid(void) {return fInstance != nil;} + + // Returns the final localized string, designated by path, and with the arguments properly substituted + // if you want to use the default argument order, just use %s like you would with printf, BUT, if you + // want the arguments in a different order (like you had to switch things around for a specific language) + // then you use %1s, %2s, %3s and so on to specify arguments, these two cannot be mixed and you won't get + // the results you expect if you do mix them. Path is specified by Age.Set.Name + std::wstring GetString(const std::wstring & path, const std::vector & args); + std::wstring GetString(const std::wstring & path); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizedString.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizedString.cpp new file mode 100644 index 00000000..9ba90870 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizedString.cpp @@ -0,0 +1,368 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pfLocalizedString - a small class to handle localized strings and +// which can take parameters (like %s or %1s) and +// also can be easily translated to XML format +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsUtils.h" + +#include "pfLocalizedString.h" + +#if HS_BUILD_FOR_MAC +#include +#endif + +////////////////////////////////////////////////////////////////////// +//// pfLocalizedString functions ///////////////////////////////////// +////////////////////////////////////////////////////////////////////// + +//// Constructors //////////////////////////////////////////////////// + +pfLocalizedString::pfLocalizedString(const wchar_t *plainText) +{ + fNumArguments = 0; + IConvertFromPlainText(plainText); +} + +pfLocalizedString::pfLocalizedString(const std::wstring & plainText) +{ + fNumArguments = 0; + IConvertFromPlainText(plainText); +} + +//// IConvertFromPlainText /////////////////////////////////////////// + +void pfLocalizedString::IConvertFromPlainText(const std::wstring & plainText) +{ + textBlock curTextBlock; + fText.clear(); + fPlainTextRep = plainText; + fNumArguments = 0; // reset the argument count + int curParameter = 0; + + for (std::wstring::size_type curIndex = 0; curIndex < plainText.size(); curIndex++) + { + wchar_t curChar = plainText[curIndex]; + bool isLastChar = (curIndex == (plainText.length() - 1)); + switch (curChar) + { + case L'\\': + if (!isLastChar) + { + // we need to see the next character + curIndex++; + wchar_t nextChar = plainText[curIndex]; + if ((nextChar == L'%')||(nextChar == L'\\')) + { + // we recognize it as an escaped character, so add it to the text + curTextBlock.fText += nextChar; + } + // otherwise we don't recognize it and it will be skipped + } + // if it's the last char, just drop it + break; + case L'%': + if (!isLastChar) + { + // we need to grab the trailing s character + std::wstring::size_type endArgPos = plainText.find(L"s", curIndex); + if (endArgPos != std::wstring::npos) // make sure the s exists + { + if (endArgPos == (curIndex + 1)) // no number specifier + { + fText.push_back(curTextBlock); + curTextBlock.fIsParam = true; + curTextBlock.fParamIndex = curParameter; + curParameter++; + curTextBlock.fText = L""; + fText.push_back(curTextBlock); + curTextBlock.fIsParam = false; + curTextBlock.fParamIndex = 0; + } + else // number specified + { + fText.push_back(curTextBlock); + curTextBlock.fIsParam = true; + curTextBlock.fText = L""; + + std::wstring number = plainText.substr(curIndex + 1, (endArgPos - (curIndex + 1))); + curTextBlock.fParamIndex = _wtoi(number.c_str()) - 1; // args are 1-based, vectors are 0-based + fText.push_back(curTextBlock); + + curTextBlock.fIsParam = false; + curTextBlock.fParamIndex = 0; + } + fNumArguments++; // increment our argument count + curIndex = endArgPos; // update our position + } + // if s didn't exist, we just skip this % sign + } + // if it was the last char, we just skip this % sign + break; + default: + curTextBlock.fText += curChar; + break; + } + } + fText.push_back(curTextBlock); + + IUpdateXML(); +} + +//// IUpdatePlainText //////////////////////////////////////////////// + +void pfLocalizedString::IUpdatePlainText() +{ + fPlainTextRep = L""; + for (std::vector::size_type curIndex = 0; curIndex < fText.size(); curIndex++) + { + textBlock curTextBlock = fText[curIndex]; + + if (curTextBlock.fIsParam) + { + std::wstring paramStr = L"%"; + wchar_t buff[256]; + _itow(curTextBlock.fParamIndex + 1, buff, 10); + paramStr += buff; + paramStr += L"s"; + fPlainTextRep += paramStr; + } + else + { + // otherwise, we need to copy all the text over, making sure that % and \ are properly escaped + for (std::wstring::size_type curChar = 0; curChar < curTextBlock.fText.size(); curChar++) + { + if ((curTextBlock.fText[curChar] == L'\\')||(curTextBlock.fText[curChar] == L'%')) + fPlainTextRep += L"\\"; + fPlainTextRep += curTextBlock.fText[curChar]; + } + } + } +} + +//// IConvertFromXML ///////////////////////////////////////////////// + +void pfLocalizedString::IConvertFromXML(const std::wstring & xml) +{ + textBlock curTextBlock; + fText.clear(); + fNumArguments = 0; // reset the argument counter + int curParameter = 0; + + for (std::wstring::size_type curIndex = 0; curIndex < xml.length(); curIndex++) + { + wchar_t curChar = xml[curIndex]; + bool isLastChar = (curIndex == (xml.length() - 1)); + switch (curChar) + { // expat handles the > < and so on stuff for us + case L'\\': // but we want to be able to escape the % sign and the \ character + if (!isLastChar) + { + // we need to see the next character + curIndex++; + wchar_t nextChar = xml[curIndex]; + if ((nextChar == L'%')||(nextChar == L'\\')) + { + // we recognize it as an escaped character, so add it to the text + curTextBlock.fText += nextChar; + } + // otherwise we don't recognize it and it will be skipped + } + // if it's the last char, just drop it + break; + case L'%': + if (!isLastChar) + { + // we need to grab the trailing s character + std::wstring::size_type endArgPos = xml.find(L"s", curIndex); + if (endArgPos != std::wstring::npos) // make sure the s exists + { + if (endArgPos == (curIndex + 1)) // no number specifier + { + fText.push_back(curTextBlock); + curTextBlock.fIsParam = true; + curTextBlock.fParamIndex = curParameter; + curParameter++; + curTextBlock.fText = L""; + fText.push_back(curTextBlock); + curTextBlock.fIsParam = false; + curTextBlock.fParamIndex = 0; + } + else // number specified + { + fText.push_back(curTextBlock); + curTextBlock.fIsParam = true; + curTextBlock.fText = L""; + + std::wstring number = xml.substr(curIndex + 1, (endArgPos - (curIndex + 1))); + curTextBlock.fParamIndex = _wtoi(number.c_str()) - 1; // args are 1-based, vectors are 0-based + fText.push_back(curTextBlock); + + curTextBlock.fIsParam = false; + curTextBlock.fParamIndex = 0; + } + fNumArguments++; // increment the number of arguments + curIndex = endArgPos; // update our position + } + // if s didn't exist, we just skip this % sign + } + // if it was the last char, we just skip this % sign + break; + default: + curTextBlock.fText += curChar; + break; + } + } + fText.push_back(curTextBlock); + + IUpdatePlainText(); + IUpdateXML(); // we don't really get pure xml from the parser (since it auto translates all the &x; stuff) +} + +//// IUpdateXML ////////////////////////////////////////////////////// + +void pfLocalizedString::IUpdateXML() +{ + fXMLRep = L""; + for (std::vector::size_type curIndex = 0; curIndex < fText.size(); curIndex++) + { + textBlock curTextBlock = fText[curIndex]; + + if (curTextBlock.fIsParam) + { + std::wstring paramStr = L"%"; + wchar_t buff[256]; + _itow(curTextBlock.fParamIndex + 1, buff, 10); + paramStr += buff; + paramStr += L"s"; + fXMLRep += paramStr; + } + else + { + // otherwise, we need to copy all the text over, making sure that %, &, <, and > are properly converted + for (std::wstring::size_type curChar = 0; curChar < curTextBlock.fText.size(); curChar++) + { + if (curTextBlock.fText[curChar] == L'%') + fXMLRep += L"\\%"; + else if (curTextBlock.fText[curChar] == L'&') + fXMLRep += L"&"; + else if (curTextBlock.fText[curChar] == L'<') + fXMLRep += L"<"; + else if (curTextBlock.fText[curChar] == L'>') + fXMLRep += L">"; + else + fXMLRep += curTextBlock.fText[curChar]; + } + } + } +} + +//// FromXML ///////////////////////////////////////////////////////// + +void pfLocalizedString::FromXML(const std::wstring & xml) +{ + IConvertFromXML(xml); +} + +//// Operators /////////////////////////////////////////////////////// + +bool pfLocalizedString::operator<(pfLocalizedString &obj) +{ + return (fPlainTextRep < obj.fPlainTextRep); +} + +bool pfLocalizedString::operator>(pfLocalizedString &obj) +{ + return (fPlainTextRep > obj.fPlainTextRep); +} + +bool pfLocalizedString::operator==(pfLocalizedString &obj) +{ + return (fPlainTextRep == obj.fPlainTextRep); +} + +bool pfLocalizedString::operator<=(pfLocalizedString &obj) +{ + return (fPlainTextRep <= obj.fPlainTextRep); +} + +bool pfLocalizedString::operator>=(pfLocalizedString &obj) +{ + return (fPlainTextRep >= obj.fPlainTextRep); +} + +bool pfLocalizedString::operator!=(pfLocalizedString &obj) +{ + return (fPlainTextRep != obj.fPlainTextRep); +} + +pfLocalizedString pfLocalizedString::operator+(pfLocalizedString &obj) +{ + fPlainTextRep += obj.fPlainTextRep; + IConvertFromPlainText(fPlainTextRep); + return *this; +} + +pfLocalizedString &pfLocalizedString::operator+=(pfLocalizedString &obj) +{ + fPlainTextRep += obj.fPlainTextRep; + IConvertFromPlainText(fPlainTextRep); + return *this; +} + +pfLocalizedString &pfLocalizedString::operator=(const std::wstring & plainText) +{ + IConvertFromPlainText(plainText); + return *this; +} + +pfLocalizedString &pfLocalizedString::operator=(const wchar_t *plainText) +{ + IConvertFromPlainText(plainText); + return *this; +} + +std::wstring pfLocalizedString::operator%(const std::vector & arguments) +{ + std::wstring retVal = L""; + for (std::vector::size_type curIndex = 0; curIndex < fText.size(); curIndex++) + { + if (fText[curIndex].fIsParam) + { + int curParam = fText[curIndex].fParamIndex; + if (curParam < arguments.size()) + retVal += arguments[curParam]; + } + else + retVal += fText[curIndex].fText; + } + return retVal; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizedString.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizedString.h new file mode 100644 index 00000000..cce64792 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLocalizationMgr/pfLocalizedString.h @@ -0,0 +1,102 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pfLocalizedString - a small class to handle localized strings and +// which can take parameters (like %s or %1s) and +// also can be easily translated to XML format +// +////////////////////////////////////////////////////////////////////// + +#ifndef _pfLocalizedString_h +#define _pfLocalizedString_h + +#include "hsTypes.h" +#include "hsStlUtils.h" + + +//// pfLocalizedString Class Definition ////////////////////////////// +// a small class to handle localized strings and which can take +// parameters (like %s or %1s) and also can be easily translated to +// XML format + +class pfLocalizedString +{ +protected: + // stores all basic text and param information in a nice, easy-to-use block of data + struct textBlock + { + bool fIsParam; // if true, then this is a parameter, not a string + std::wstring fText; + UInt8 fParamIndex; + + textBlock() : fIsParam(false), fParamIndex(0) {} + }; + + std::vector fText; // the individual text elements that make up this string + std::wstring fXMLRep; // the XML representation of this string + std::wstring fPlainTextRep; // the plain text representation of this string + UInt16 fNumArguments; // number of arguments this string has + + void IConvertFromPlainText(const std::wstring & plainText); + void IUpdatePlainText(); // from the internal representation + void IConvertFromXML(const std::wstring & xml); + void IUpdateXML(); // from the internal representation +public: + pfLocalizedString() : fNumArguments(0) {} + pfLocalizedString(const wchar_t *plainText); + pfLocalizedString(const std::wstring & plainText); + virtual ~pfLocalizedString() {} + + // To translate to and from xml format (where <, > and other signs can't be used) + void FromXML(const std::wstring & xml); + std::wstring ToXML() {return fXMLRep;} + + UInt16 GetArgumentCount() {return fNumArguments;} + + // Various operators, they all work pretty much the same as the standard string or wstring operators + // but note that the all work on the plain text representation (not the XML representation) + bool operator<(pfLocalizedString &obj); + bool operator>(pfLocalizedString &obj); + bool operator==(pfLocalizedString &obj); + bool operator<=(pfLocalizedString &obj); + bool operator>=(pfLocalizedString &obj); + bool operator!=(pfLocalizedString &obj); + + operator const wchar_t *() {return fPlainTextRep.c_str();} + operator std::wstring() {return fPlainTextRep;} + + pfLocalizedString operator+(pfLocalizedString &obj); + pfLocalizedString &operator+=(pfLocalizedString &obj); + + pfLocalizedString &operator=(const std::wstring & plainText); + pfLocalizedString &operator=(const wchar_t *plainText); + + // Specialized operator for replacing text with arguments + std::wstring operator%(const std::vector & arguments); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/pfLoginDialog.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/pfLoginDialog.rc new file mode 100644 index 00000000..f131969e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/pfLoginDialog.rc @@ -0,0 +1,110 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DIALOG_LOGIN DIALOGEX 0, 0, 186, 114 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Login" +FONT 8, "Tahoma" +BEGIN + EDITTEXT IDC_LOGIN_USERNAME,55,7,98,12,ES_AUTOHSCROLL + EDITTEXT IDC_LOGIN_PASSWORD,55,23,98,12,ES_PASSWORD | + ES_AUTOHSCROLL + COMBOBOX IDC_LOGIN_LOBBYLIST,55,39,98,76,CBS_DROPDOWN | + CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "Login",IDC_LOGIN_LOGIN,41,58,50,14 + PUSHBUTTON "Cancel",IDC_LOGIN_CANCEL,94,58,50,14 + LTEXT "Account:",IDC_STATIC,21,9,30,8,0,WS_EX_RIGHT + LTEXT "Password:",IDC_STATIC,17,25,34,8,0,WS_EX_RIGHT + LTEXT "Server:",IDC_LOGIN_STATIC_SERVER,27,41,24,8,0, + WS_EX_RIGHT + PUSHBUTTON "?",IDC_SERVER_QUERY_BTN,157,39,22,13,NOT WS_VISIBLE + CONTROL "Remember Password",IDC_REMEMBER_PASSWORD,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,18,79,119,12 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_DIALOG_LOGIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 107 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/plLoginDialog.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/plLoginDialog.cpp new file mode 100644 index 00000000..d9f5dc5c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/plLoginDialog.cpp @@ -0,0 +1,657 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plLoginDialog.h" +#include "resource.h" +#include "../plNetCommon/plNetCommonConstants.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plHttpServer/plHttpResponse.h" +#include "../plSDL/plSDL.h" +#include "../plFile/hsFiles.h" +#include "../plNetMessage/plNetCommonMessage.h" + +// 'this' : used in base member initializer list +#pragma warning(disable:4355) + +#define kAuthTimedOut WM_USER+2 + +plLoginDialog::plLoginDialog( HWND parentWnd ) +: plDialog(IDD_DIALOG_LOGIN) +, fParentWnd( parentWnd ) +, fLoginBtn(this, IDC_LOGIN_LOGIN, plDelegate(this,(TDelegate)ILogin)) +, fCancelBtn(this, IDC_LOGIN_CANCEL, plDelegate(this,(TDelegate)IExit)) +, fAccountName(this,IDC_LOGIN_USERNAME) +, fPassword(this,IDC_LOGIN_PASSWORD) +, fLobbyList(this,IDC_LOGIN_LOBBYLIST) +, fLobbyText(this, IDC_LOGIN_STATIC_SERVER) +, fRememberPassword(this, IDC_REMEMBER_PASSWORD, plDelegate(this, (TDelegate)IOnRememberPwdChanged)) +, fCancelled(false) +, fAutoLogin(false) +#ifndef PLASMA_EXTERNAL_RELEASE +, fServerQueryBtn(this, IDC_SERVER_QUERY_BTN) +#endif +{ + fLobbyList.fSelectionEndOkDelegate = plDelegate(this,(TDelegate)SelectedLobbyChanged); + fLobbyList.fEditUpdateDelegate = plDelegate(this,(TDelegate)SelectedLobbyTextEdited); + fLobbyList.fKillFocusDelegate = plDelegate(this,(TDelegate)OnLobbyListLostFocus); +#ifndef PLASMA_EXTERNAL_RELEASE + fServerQueryBtn.fClickDelegate = plDelegate(this,(TDelegate)ServerQueryBtnClicked); +#endif +} + +std::string plLoginDialog::MakeSafeLobbyServerName(const std::string & value) +{ + //return plIDataServer::MakeSafeMachineName(value,"parablegame.cyanworlds.com"); + return ""; +} + + +void plLoginDialog::SelectedLobbyChanged() +{ + fLobbyVal.SetValue(fLobbyList.GetValue().c_str()); + fLobbyList.SetEdited(false); +} + +void plLoginDialog::SelectedLobbyTextEdited() +{ + fLobbyVal.SetValue(fLobbyList.GetValue().c_str()); + fLobbyList.SetEdited(true); +} + +// Ugh +#ifdef PLASMA_EXTERNAL_RELEASE +#include +#endif + +bool plLoginDialog::RefreshLobbyList() +{ + fStatusBar.SetText(L"Refreshing lobby server list..."); + + plStringList lobbies; + std::vector wLobbies; + GetLobbyList(lobbies); + + // Strip off the shard name and just leave the address + for (int i = 0; i < lobbies.size(); i++) + { + std::string& str = lobbies[i]; + + std::string::size_type endofname = str.find('\t'); + if (endofname != std::string::npos) + str.erase(str.begin() + endofname, str.end()); + + wchar_t *wLobby = hsStringToWString(str.c_str()); + wLobbies.push_back(wLobby); + delete [] wLobby; + } + + fLobbyList.Empty(); + +#ifdef PLASMA_EXTERNAL_RELEASE + // In release mode, put the user in a random lobby for rudimentary load balancing + int numLobbies = lobbies.size(); + if (numLobbies > 0) + { + srand(time(NULL)); + int rnum = rand(); + int whichLobby = rnum % numLobbies; + + fLobbyList.AddString(wLobbies[whichLobby].c_str()); + } + + fLobbyList.SetCurrent(0); +#else + if (AllowSinglePlayerLobby()) + fLobbyList.AddString(L"Single Player"); + fLobbyList.AddStrings(wLobbies); + + wchar_t *wLobby = hsStringToWString(fLobbyVal.GetValue().c_str()); + int index = fLobbyList.FindStringExact(wLobby); + if (index==LB_ERR && fLobbyVal.GetValue().length()>0) + { + fLobbyList.AddString(wLobby); + index = fLobbyList.FindStringExact(wLobby); + } + delete [] wLobby; + + fLobbyList.SetCurrent((index!=LB_ERR)?index:0); +#endif // PLASMA_EXTERNAL_RELEASE + + SelectedLobbyChanged(); + + fStatusBar.SetText(L""); + return true; +} + +void plLoginDialog::OnLobbyListLostFocus() +{ + std::string value = fLobbyList.GetValue(); + if (value.length()==0) + { + fLobbyList.SetCurrent(0); + fLobbyList.SetValue(MakeSafeLobbyServerName(fLobbyList.GetValue()).c_str()); + SelectedLobbyChanged(); + } +} + +#if 0 +void plLoginDialog::UpdateCtrls() +{ + bool networkEnabled = IsNetworkPlayEnabled(); + bool loggedIn = GetLoggedIn(); + bool loggingIn = GetLoggingIn(); + bool loggedOut = GetLoggedOut(); + if (!networkEnabled && (loggedIn || loggingIn)) + { + Logout(); + + // these don't do anything. need to set the vars in fMainDIalog? + loggedIn = false; + loggingIn = false; + loggedOut = true; + } +} +#endif + +bool plLoginDialog::IsNetworkPlayDisabled() +{ +#ifdef PLASMA_EXTERNAL_RELEASE + return false; +#else + xtl::istring tmp = fLobbyVal.GetValue().c_str(); + return (tmp.compare("single player")==0); +#endif +} + +bool plLoginDialog::IsNetworkPlayEnabled() +{ + return !IsNetworkPlayDisabled(); +} + +void plLoginDialog::OnInitDialog() +{ + plDialog::OnInitDialog(); + + if ( fParentWnd ) + SetParent( Handle(), fParentWnd ); + + fStatusBar.OpenWindow(this,true); + +#ifdef PLASMA_EXTERNAL_RELEASE + fLobbyList.Show(false); + fLobbyText.Show(false); +#endif + +#ifndef PLASMA_EXTERNAL_RELEASE + fServerQueryBtn.Show(true); +#endif + + bool rememberPwd = (fRememberPasswordVal.GetValue()=="true"); + fAccountName.SetValue(fAccountNameVal.GetValue().c_str()); + if (rememberPwd) + { + int len = atoi(fPasswordLen.GetValue().c_str()); + std::string fakePwd(len, '*'); + fPassword.SetValue(fakePwd.c_str()); + } + + fRememberPassword.Check(rememberPwd); + + RefreshLobbyList(); + + if ( fAutoLogin ) + fLoginBtn.Click(); + +// SetForegroundWindow(*this); +} + +bool plLoginDialog::Login() +{ + int ret = DoModal(); + if (ret<0) + { + hsAssert(false, xtl::format("plLoginDialog failed to initialize, err code %d, GetLastError %d", + ret, GetLastError()).c_str()); + } + + return (ret != 0); +} + +void plLoginDialog::ILogin() +{ + OnLoginClicked(); + + fAccountNameVal.SetValue(fAccountName.GetValue().c_str()); + + std::string pwd = fPassword.GetValue(); + int pwdSize = pwd.size(); + + std::string fakePwd = "*" + std::string(pwdSize-1, '*'); + if (pwd != fakePwd) // user has entered a real pwd + { + fPasswordLen.SetValue(xtl::format("%d",pwd.size()).c_str()); + // MD5 HASH the pwd + std::string hex; + plChallengeResponse::HashPassword(pwd.c_str(), hex); + fPasswordVal.SetValue(hex.c_str()); + } + + SetDataServerUserName(true, fAccountNameVal.GetValue().c_str()); + SetDataServerPassword(true, fPasswordVal.GetValue().c_str()); + SetDataServerUserName(false, fAccountNameVal.GetValue().c_str()); + SetDataServerPassword(false, fPasswordVal.GetValue().c_str()); + + if (IsNetworkPlayEnabled()) + StartLogin(); + else + CompleteLogin(); +} + +void plLoginDialog::IOnRememberPwdChanged() +{ + fRememberPasswordVal.SetValue(fRememberPassword.IsChecked() ? "true" : "false"); +} + +void plLoginDialog::IExit() +{ + fAccountNameVal.SetValue(fAccountName.GetValue().c_str()); + fPasswordVal.SetValue(fPassword.GetValue().c_str()); + fLobbyVal.SetValue(fLobbyList.GetValue().c_str()); + SetDataServerUserName(true, fAccountNameVal.GetValue().c_str()); + SetDataServerPassword(true, fPasswordVal.GetValue().c_str()); + SetDataServerUserName(false, fAccountNameVal.GetValue().c_str()); + SetDataServerPassword(false, fPasswordVal.GetValue().c_str()); + SHORT state = GetKeyState(VK_SHIFT); + if (state&0x8000) + EndDialogTrue(); + else + EndDialogFalse(); + fCancelled=true; +} + +int plLoginDialog::ICheckNetVersion(plNetMsgAuthenticateChallenge * msg) +{ + if (msg) + { + if (msg->GetVersionMajor() != plNetMessage::kVerMajor || + msg->GetVersionMinor() != plNetMessage::kVerMinor) + { + std::string str = xtl::format("Login Failed, client/server version mismatch, client %d.%d, server %d.%d", + plNetMessage::kVerMajor, plNetMessage::kVerMinor, + msg->GetVersionMajor(), + msg->GetVersionMinor()); + FailLogin(str.c_str()); + return hsFail; + } + return hsOK; + } + return hsFail; +} + +void plLoginDialog::HandleAuthChallenge(plNetMsgAuthenticateChallenge * msg) +{ + int cnt = msg->PeekBuffer(msg->GetNetCoreMsg()->GetData(),msg->GetNetCoreMsg()->GetLen()); + + // check protocol version first, in case msg contents are hosed + if (ICheckNetVersion(msg) == hsFail) + return; // version err + + if (msg->IsContinuing()) + { + // Respond to the Challenge + std::string hex = plChallengeResponse::GetBufferAsHexStr(msg->GetChallenge().data(), msg->GetChallenge().size(), true); + fChallengeResponse.SetChallenge(hex); + fChallengeResponse.GenerateResponse(fAccountNameVal.GetValue().c_str(),fPasswordVal.GetValue().c_str()); + KillTimer(*this,kAuthTimedOut); + SendAuthenticateResponse(); + } + else + { + FailLogin(msg->GetHelloResult()); + } +} + +void plLoginDialog::HandleAccountAuthenticated(plNetMsgAccountAuthenticated * msg) +{ + int cnt = msg->PeekBuffer(msg->GetNetCoreMsg()->GetData(),msg->GetNetCoreMsg()->GetLen()); + if (msg->IsAuthenticated()) + { + CompleteLogin(); + } + else + { + FailLogin(msg->GetAuthResult()); + } +} + + +void plLoginDialog::StartLogin() +{ + fLoginBtn.SetEnabled(false); + std::string value = fLobbyList.GetValue(); + if (value.length()==0) + { + fLobbyList.SetCurrent(0); + fLobbyList.SetValue(MakeSafeLobbyServerName(fLobbyList.GetValue()).c_str()); + SelectedLobbyChanged(); + } + fStatusBar.SetText(L"Authenticating..."); +// fMainDialog->InitNetCore(); +// fMainDialog->fLoginState = kLoggingIn; +// fAccountTab.UpdateCtrls(); +// fPlayerTab.SetPlayerVault(nil); + SendAuthenticateHello(); +} + +void plLoginDialog::CompleteLogin() +{ + if ( Handle() ) + fLoginBtn.SetEnabled(true); + + KillTimer(*this,kAuthTimedOut); + + fStatusBar.SetText(L""); + if (IsNetworkPlayEnabled()) + NotifyConnected(); + else + NotifyDisconnected(); + + EndDialogTrue(); + + GetClientManifests(); + UpdateAllCtrls(); +} + +void plLoginDialog::FailLogin(const char* str) +{ + fLoginBtn.SetEnabled(true); + KillTimer(*this, kAuthTimedOut); + fStatusBar.SetText(L""); + hsMessageBoxWithOwner((void*)*this,str,"Error",hsMessageBoxNormal); + Logout(); +} + +void plLoginDialog::FailLogin(int reasonCode) +{ + std::string str = xtl::format("Failed to login to lobby server %s: %s", + fLobbyVal.GetValue().c_str(), plNetMsgAccountAuthenticated::GetAuthResultString(reasonCode)); + FailLogin(str.c_str()); +} + +void plLoginDialog::TimeoutLogin() +{ + fLoginBtn.SetEnabled(true); + + wchar_t *wStr = hsStringToWString(xtl::format("Timed out logging into lobby server %s.", fLobbyVal.GetValue().c_str()).c_str()); + fStatusBar.SetText(wStr); + delete [] wStr; + + KillTimer(*this, kAuthTimedOut); + Logout(); +} + +void plLoginDialog::Logout() +{ + KillTimer(*this, kAuthTimedOut); + SendLobbyLeave(); + //fMainDialog->ShutdownNetCore(); + NotifyDisconnected(); +} + +void plLoginDialog::SendLobbyLeave() +{ + plNetMsgLeave msg; + msg.SetReason( plPlayerUpdateConstants::kPlayerQuitting ); + SendMsg(&msg,plNetAddress(fLobbyVal.GetValue().c_str(),plNetLobbyServerConstants::GetPort())); + RemoveLobbyPeer(); +} + +#define MSG_TIMEOUT 8000 +#include "../pnNetCommon/plNetAddress.h" + +void plLoginDialog::SendAuthenticateHello() +{ + SetTimer(*this,kAuthTimedOut,MSG_TIMEOUT,nil); + plNetMsgAuthenticateHello msg; + msg.SetAccountName(fAccountNameVal.GetValue().c_str()); + msg.SetMaxPacketSize(GetPacketSize()); + SendMsg(&msg,plNetAddress(fLobbyVal.GetValue().c_str(),plNetLobbyServerConstants::GetPort())); +} + +void plLoginDialog::SendAuthenticateResponse() +{ + SetTimer(*this,kAuthTimedOut,MSG_TIMEOUT,nil); + plNetMsgAuthenticateResponse msg; + msg.SetResponse(fChallengeResponse.GetResponse()); + SendMsg(&msg,plNetAddress(fLobbyVal.GetValue().c_str(),plNetLobbyServerConstants::GetPort())); +} + +int plLoginDialog::CallDefaultProc( unsigned int message, unsigned int wParam, LONG lParam ) +{ + switch (message) + { + case WM_TIMER: + switch (wParam) + { + case kAuthTimedOut: + TimeoutLogin(); + break; + } + } + + return 0; +} + + + + +///////////////////////////////////////////////////////////////////////////// +#ifndef PLASMA_EXTERNAL_RELEASE +#define kServerInfoFilename "server_info.html" + +static void StringToLines(std::string str, plStringList & lines, bool includeBlankLines=true) +{ + xtl::trim(str); + if (str.length()==0) + return; + str.append("\n"); + int pos; + while ((pos=str.find("\n"))!=std::string::npos) + { + std::string line = xtl::trim(str.substr(0,pos).c_str()); + str.erase(0,pos+1); + if (includeBlankLines || (!includeBlankLines && line.length()>0)) + lines.push_back(line); + } +} + +static void GetPathElements(std::string filename, plStringList & lst) +{ + int pos; + while ((pos=filename.find_first_of("\\/"))!=std::string::npos) + { + std::string element = filename.substr(0,pos); + filename.erase(0,pos+1); + if (element.length()) + lst.push_back(element); + } +} + +struct SDLInfoParser +{ + std::string fFilename; + std::string fDescriptorName; + int fVersion; + void ParseString( const char * s ) + { + std::string str = s; + int p = str.find(","); + fFilename = str.substr(0,p); + str.erase(0,p+1); + p = str.find(","); + fDescriptorName = str.substr(0,p); + str.erase(0,p+1); + fVersion = atoi(str.c_str()); + + p = fFilename.find_last_of("\\"); + if( p!=std::string::npos ) + fFilename.erase(0,p+1); + p = fFilename.find_last_of("/"); + if( p!=std::string::npos ) + fFilename.erase(0,p+1); + } +}; + + +struct DescriptorReport +{ + int fServerVersion; // 0 means the descriptor is missing + int fClientVersion; + DescriptorReport(): fServerVersion(0),fClientVersion(0){} +}; + +#define kStyleSheet \ + "" + +void plLoginDialog::ServerQueryBtnClicked() +{ + hsUNIXStream file; + file.Open( kServerInfoFilename, "wt" ); + file.WriteString(""kStyleSheet"\n"); + + try + { + typedef std::map< std::string, DescriptorReport > DescriptorReports; + typedef std::map< std::string, DescriptorReports > FileReports; + + FileReports fileReports; + + + /*plURL url; + plHttpRequest request; + plHttpResponse response; + + // read server build date etc. + url.SetHost( fLobbyList.GetValue().c_str() ); + url.SetPort( 7676 ); + url.SetFile( "VersionInfo" ); + request.SetUrl( url ); + request.SetType( plHttpRequest::kGet ); + if ( !request.MakeRequest( response ) ) + throw 0; + file.WriteString("

    Server Info

    \n" ); + file.WriteString( "
    \n" );
    +		file.WriteString( response.c_str() );
    +		file.WriteString( "
    \n" ); + + // get server's SDL info + url.SetFile( "SDLInfo" ); + request.SetUrl( url ); + if ( !request.MakeRequest( response ) ) + throw 0; + plStringList lines; + StringToLines( response, lines, false ); + SDLInfoParser parser; + {for ( plStringList::iterator ii=lines.begin(); ii!=lines.end(); ++ii ) + { + parser.ParseString( (*ii).c_str() ); + fileReports[ parser.fFilename ][ parser.fDescriptorName ].fServerVersion = parser.fVersion; + }} + + // get client's SDL info + plSDLMgr::GetInstance()->DeInit(); + plSDLMgr::GetInstance()->SetSDLDir( "SDL" ); + plSDLMgr::GetInstance()->Init(); + const plSDL::DescriptorList * cds = plSDLMgr::GetInstance()->GetDescriptors(); + {for ( plSDL::DescriptorList::const_iterator ii=cds->begin(); ii!=cds->end(); ++ii ) + { + plStateDescriptor * descriptor = *ii; + std::string filename = descriptor->GetFilename(); + int p = filename.find_last_of(PATH_SEPARATOR_STR); + if( p!=std::string::npos ) + filename.erase(0,p+1); + fileReports[ filename ][ descriptor->GetName() ].fClientVersion = descriptor->GetVersion(); + }} + + // write SDL comparison report + file.WriteString("

    SDL File Comparison

    \n" ); + file.WriteString("Version=0 means descriptor doesn't exist.

    \n" ); + file.WriteString( "\n" ); + + + { for ( FileReports::iterator ii=fileReports.begin(); ii!=fileReports.end(); ++ii ) + { + std::string sdlFilename = ii->first; + DescriptorReports & descrReports = ii->second; + file.WriteFmt( "\n", sdlFilename.c_str() ); + { for ( DescriptorReports::iterator jj=descrReports.begin(); jj!=descrReports.end(); ++jj ) + { +#define kSDLBad "Bad" +#define kSDLOk "Ok" + std::string descrName = jj->first; + DescriptorReport & descrReport = jj->second; + file.WriteFmt( "\n", + descrName.c_str(), descrReport.fServerVersion, descrReport.fClientVersion, + ( descrReport.fServerVersion==descrReport.fClientVersion ) ? kSDLOk:kSDLBad ); + }} + }} + + file.WriteString("
    FileServer VersionClient VersionStatus
    %s
       %s%d%d%s
    \n");*/ + } + catch (...) + { + file.WriteString("

    An error occurred while querying the server.\n"); + } + + file.WriteString("\n"); + file.Close(); + + ShellExecute( nil, nil, kServerInfoFilename, nil, nil, SW_SHOWNORMAL ); +} +#endif +///////////////////////////////////////////////////////////////////////////// + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/plLoginDialog.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/plLoginDialog.h new file mode 100644 index 00000000..db1c3760 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/plLoginDialog.h @@ -0,0 +1,122 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plLoginDialog_h_inc +#define plLoginDialog_h_inc + +#include "../plWndCtrls/plWndCtrls.h" +#include "../plEncryption/plChallengeResponse.h" +#include "../pnNetCommon/plNetAddress.h" + +class plMainDialog; +class plNetMsgAuthenticateChallenge; +class plNetMsgAccountAuthenticated; +class plNetMessage; + +class plLoginDialog : public plDialog +{ + HWND fParentWnd; +public: + DECLARE_WINDOWCLASS(plLoginDialog, plDialog); + + plLoginDialog( HWND parentWnd=nil ); + + bool Login(); + + void OnInitDialog(); + + void HandleAuthChallenge(plNetMsgAuthenticateChallenge * msg); + void HandleAccountAuthenticated(plNetMsgAccountAuthenticated * msg); + void StartLogin(); + void CompleteLogin(); + void FailLogin(int reasonCode); + void FailLogin(const char* str); + void TimeoutLogin(); + void Logout(); + void SendAuthenticateHello(); + void SendAuthenticateResponse(); + + bool IsNetworkPlayDisabled(); + bool IsNetworkPlayEnabled(); +// void UpdateCtrls(); + void SelectedLobbyChanged(); + void SelectedLobbyTextEdited(); + void OnLobbyListLostFocus(); + void SendLobbyLeave(); + bool RefreshLobbyList(); + bool GetCancelled() const { return fCancelled; } + bool GetRememberPassword() const { return fRememberPassword.IsChecked(); } + + // callbacks + virtual void GetLobbyList(plStringList& lobbies) = 0; + virtual bool AllowSinglePlayerLobby() { return true; } // checked in non external-release build + virtual void SetDataServerUserName(bool local, const char* s) {} + virtual void SetDataServerPassword(bool local, const char* s) {} + virtual void RemoveLobbyPeer() {} + virtual void NotifyConnected() {} + virtual void NotifyDisconnected() {} + virtual void GetClientManifests() {} + virtual void UpdateAllCtrls() {} + virtual unsigned int GetPacketSize() = 0; + virtual bool SendMsg(plNetMessage * msg, plNetAddress & addr) = 0; + virtual void OnLoginClicked() {} + + std::string MakeSafeLobbyServerName(const std::string & value); + + int CallDefaultProc( unsigned int message, unsigned int wParam, LONG lParam ); + + plEdit fAccountName; + plEdit fPassword; + plChallengeResponse fChallengeResponse; + plComboBox fLobbyList; + plLabel fLobbyText; + plCheckBox fRememberPassword; + +#ifndef PLASMA_EXTERNAL_RELEASE + plButton fServerQueryBtn; + void ServerQueryBtnClicked(); +#endif + + plConfigValue fAccountNameVal; + plConfigValue fPasswordVal; // the pwd as a MD5 hash + plConfigValue fRememberPasswordVal; // the checkbox state + plConfigValue fPasswordLen; // the length of the original pwd + plConfigValue fLobbyVal; + bool fAutoLogin; + + plStatusBar fStatusBar; + +protected: + int ICheckNetVersion(plNetMsgAuthenticateChallenge * msg); + void ILogin(); + void IExit(); + void IOnRememberPwdChanged(); + + bool fCancelled; + plButton fLoginBtn; + plButton fCancelBtn; +}; + +#endif // plLoginDialog_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/resource.h new file mode 100644 index 00000000..fd46b400 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfLoginDialog/resource.h @@ -0,0 +1,24 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by pfLoginDialog.rc +// +#define IDD_DIALOG_LOGIN 326 +#define IDC_LOGIN_USERNAME 3029 +#define IDC_LOGIN_PASSWORD 3031 +#define IDC_LOGIN_LOBBYLIST 3033 +#define IDC_LOGIN_STATIC_SERVER 3127 +#define IDC_LOGIN_LOGIN 3130 +#define IDC_LOGIN_CANCEL 3131 +#define IDC_SERVER_QUERY_BTN 3132 +#define IDC_REMEMBER_PASSWORD 3133 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 327 +#define _APS_NEXT_COMMAND_VALUE 42001 +#define _APS_NEXT_CONTROL_VALUE 3134 +#define _APS_NEXT_SYMED_VALUE 327 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfBackdoorMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfBackdoorMsg.h new file mode 100644 index 00000000..5dcc39e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfBackdoorMsg.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfBackdoorMsg Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfBackdoorMsg_h +#define _pfBackdoorMsg_h + +#include "hsTypes.h" +#include "hsStream.h" +#include "../pnMessage/plMessage.h" + +class pfBackdoorMsg : public plMessage +{ + protected: + char *fTarget; + char *fString; + + public: + pfBackdoorMsg() : plMessage( nil, nil, nil ),fTarget(nil),fString(nil) {} + pfBackdoorMsg( const char* target, const char* string) : plMessage( nil, nil, nil ) + { + // across the net and just to those listening + SetBCastFlag( plMessage::kNetPropagate ); + SetBCastFlag( plMessage::kBCastByExactType ); + fTarget = hsStrcpy( target ); + fString = hsStrcpy( string ); + } + + ~pfBackdoorMsg() + { + delete [] fTarget; + delete [] fString; + } + + CLASSNAME_REGISTER( pfBackdoorMsg ); + GETINTERFACE_ANY( pfBackdoorMsg, plMessage ); + + virtual void Read(hsStream* s, hsResMgr* mgr) + { + plMessage::IMsgRead( s, mgr ); + fTarget = s->ReadSafeString(); + fString = s->ReadSafeString(); + } + + virtual void Write(hsStream* s, hsResMgr* mgr) + { + plMessage::IMsgWrite( s, mgr ); + s->WriteSafeString( fTarget ); + s->WriteSafeString( fString ); + } + + const char *GetTarget( void ) { return fTarget; } + const char *GetString( void ) { return fString; } + +}; + +#endif // _pfBackdoorMsg_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfGUINotifyMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfGUINotifyMsg.h new file mode 100644 index 00000000..60bc7b09 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfGUINotifyMsg.h @@ -0,0 +1,126 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pfGUINotifyMsg_h_ +#define _pfGUINotifyMsg_h_ + +#include "../pnMessage/plMessage.h" +#include "hsResMgr.h" +#include "../pnModifier/plSingleModifier.h" +#include "hsUtils.h" + + + +///////////////////////////////////////////////////////////////////////////// +// +// MESSAGE : plGUINotifyMsg +// PARAMETERS : none +// +// PURPOSE : This is the message that notifies someone (either a responder or activator) +// : that some event or transition of state has happened on a GUI control +// +// +class pfGUINotifyMsg : public plMessage +{ +protected: + plKey fControlKey; // who start this mess + UInt32 fEvent; // what was the event that happened + +public: + pfGUINotifyMsg() : plMessage() {} + pfGUINotifyMsg(const plKey &s, + const plKey &r, + const double* t) : plMessage(s, r, t) {} + ~pfGUINotifyMsg() {} + + CLASSNAME_REGISTER( pfGUINotifyMsg ); + GETINTERFACE_ANY( pfGUINotifyMsg, plMessage ); + + enum GUIEventType + { + kShowHide = 1, // show or hide change + kAction, // button clicked, ListBox click on item, EditBox hit enter + kValueChanged, // value changed in control + kDialogLoaded, // the dialog is now loaded and ready for action + kFocusChange, // when one of its controls loses focus to another + kExitMode, // GUI Exit Mode key was pressed + kInterestingEvent, // GUI interesting-ness has changed + kEndEventList + }; + +///////////////// +// Currently, the following is the only event types that get produced: +// +// kDialog +// kShowHide - when the dialog is shown or hidden +// kButton +// kAction - when the button clicked (should be on mouse button up) +// kListBox +// kAction - single click on item(s) +// kEditBox +// kAction - enter key hit +// kUpDownPair +// kValueChanged - the value of the pair has been changed +// kKnob +// kValueChanged - the value of the knob has been changed +// kCheckBox +// kValueChanged - the checkbox state has been changed +// kRadioGroup +// kValueChanged - the radio group button has been changed +// +// Control types that don't create events +// +// kDraggable +// kTextBox +// kDragBar + + void SetEvent( plKey &key, UInt32 event) + { + fControlKey = key; + fEvent = event; + } + + plKey GetControlKey() { return fControlKey; } + UInt32 GetEvent() { return fEvent; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fControlKey = mgr->ReadKey(stream); + fEvent = stream->ReadSwap32(); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + mgr->WriteKey(stream, fControlKey); + stream->WriteSwap32(fEvent); + } +}; + + + +#endif // _pfGUINotifyMsg_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfGameGUIMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfGameGUIMsg.h new file mode 100644 index 00000000..7ea09691 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfGameGUIMsg.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfGameGUIMsg Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfGameGUIMsg_h +#define _pfGameGUIMsg_h + +#include "hsTypes.h" +#include "hsStream.h" +#include "../pnMessage/plMessage.h" + +class pfGameGUIMsg : public plMessage +{ + protected: + + UInt8 fCommand; + char fString[ 128 ]; + char *fAge; + + public: + enum + { + kShowDialog, + kHideDialog, + kLoadDialog + }; + + pfGameGUIMsg() : plMessage( nil, nil, nil ) { SetBCastFlag( kBCastByExactType ); fAge = nil; } + pfGameGUIMsg( plKey &receiver, UInt8 command ) : plMessage( nil, nil, nil ) { AddReceiver( receiver ); fCommand = command; fAge = nil; } + ~pfGameGUIMsg() { delete [] fAge; } + + CLASSNAME_REGISTER( pfGameGUIMsg ); + GETINTERFACE_ANY( pfGameGUIMsg, plMessage ); + + virtual void Read(hsStream* s, hsResMgr* mgr) + { + plMessage::IMsgRead( s, mgr ); + s->ReadSwap( &fCommand ); + s->Read( sizeof( fString ), fString ); + fAge = s->ReadSafeString(); + } + + virtual void Write(hsStream* s, hsResMgr* mgr) + { + plMessage::IMsgWrite( s, mgr ); + s->WriteSwap( fCommand ); + s->Write( sizeof( fString ), fString ); + s->WriteSafeString( fAge ); + } + + UInt8 GetCommand( void ) { return fCommand; } + + void SetString( const char *str ) { hsStrncpy( fString, str, sizeof( fString ) - 1 ); } + const char *GetString( void ) { return fString; } + + void SetAge( const char *str ) { delete [] fAge; if( str == nil ) fAge = nil; else fAge = hsStrcpy( str ); } + const char *GetAge( void ) { return fAge; } +}; + +#endif // _pfGameGUIMsg_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfKIMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfKIMsg.cpp new file mode 100644 index 00000000..058fe609 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfKIMsg.cpp @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfKIMsg Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "../pfMessage/pfKIMsg.h" + + +const char* pfKIMsg::kChronicleKILevel = "PlayerKILevel"; + +void pfKIMsg::SetString( const char *str ) +{ + wchar_t *temp = hsStringToWString( str ); + fString = temp; + delete [] temp; +} + +std::string pfKIMsg::GetString( void ) +{ + char *temp = hsWStringToString( fString.c_str() ); + std::string retVal = temp; + delete [] temp; + return retVal; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfKIMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfKIMsg.h new file mode 100644 index 00000000..2cc5ba20 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfKIMsg.h @@ -0,0 +1,227 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pfKIMsg Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pfKIMsg_h +#define _pfKIMsg_h + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsStream.h" +#include "../pnMessage/plMessage.h" + + + + +class pfKIMsg : public plMessage +{ +#ifndef KI_CONSTANTS_ONLY + protected: + + UInt8 fCommand; + UInt32 fFlags; + + // for the hack chat message thingy + char *fUser; + UInt32 fPlayerID; + std::wstring fString; + + // for the SetChatFadeDelay + hsScalar fDelay; + + // other values + Int32 fValue; + + void IInit() + { + fCommand = kNoCommand; + fString = L""; + fUser = nil; + fPlayerID = 0; + fFlags = 0; + fDelay = 0.0; + fValue = 0; + } + +#endif // def KI_CONSTANTS_ONLY + + public: + enum + { + kHACKChatMsg, // send chat message via pfKIMsg + kEnterChatMode, // toggle chat mode + kSetChatFadeDelay, // set the chat delay + kSetTextChatAdminMode, // set the chat admin mode... not used (see CCR) + kDisableKIandBB, // disable KI and blackbar (for things like AvaCusta) + kEnableKIandBB, // re-enable the KI and blackbar + kYesNoDialog, // display a Yes/No dialog + kAddPlayerDevice, // add a device player list, such as imager + kRemovePlayerDevice, // remove a device from player list + kUpgradeKILevel, // upgrade the KI to higher level + kDowngradeKILevel, // downgrade KI to next lower level + kRateIt, // display the "RateIt"(tm) dialog + kSetPrivateChatChannel, // set the private chat channel (for private rooms) + kUnsetPrivateChatChannel, // unset private chat channel + kStartBookAlert, // blink the book image on the blackbar + kMiniBigKIToggle, // shortcut to toggling the miniKI/bigKI + kKIPutAway, // shortcut to hiding all of the KI + kChatAreaPageUp, // shortcut to paging up the chat area + kChatAreaPageDown, // shortcut to paging down the chat area + kChatAreaGoToBegin, // shortcut to going to the beginning of the chat area + kChatAreaGoToEnd, // shortcut to going to the end of the chat area + kKITakePicture, // shortcut to taking a picture in the KI + kKICreateJournalNote, // shortcut to creating a journal note in the KI + kKIToggleFade, // shortcut to toggle fade mode + kKIToggleFadeEnable, // shortcut to toggling fade enabled + kKIChatStatusMsg, // display status message in chat window + kKILocalChatStatusMsg, // display status message in chat window + kKIUpSizeFont, // bump up the size of the chat area font + kKIDownSizeFont, // down size the font of the chat area + kKIOpenYeehsaBook, // open the playerbook if not already open + kKIOpenKI, // open up in degrees the KI + kKIShowCCRHelp, // show the CCR help dialog + kKICreateMarker, // create a marker + kKICreateMarkerFolder, // create a marker folder in the current Age's journal folder + kKILocalChatErrorMsg, // display error message in chat window + kKIPhasedAllOn, // turn on all the phased KI functionality + kKIPhasedAllOff, // turn off all the phased KI functionality + kKIOKDialog, // display an OK dialog box (localized) + kDisableYeeshaBook, // don't allow linking with the Yeesha book (gameplay) + kEnableYeeshaBook, // re-allow linking with the Yeesha book + kQuitDialog, // put up quit dialog + kTempDisableKIandBB, // temp disable KI and blackbar (done by av system) + kTempEnableKIandBB, // temp re-enable the KI and blackbar (done by av system) + kDisableEntireYeeshaBook, // disable the entire Yeeshabook, not for gameplay, but prevent linking + kEnableEntireYeeshaBook, + kKIOKDialogNoQuit, // display OK dialog in the KI without quiting afterwards + kGZUpdated, // the GZ game was updated + kGZInRange, // a GZ marker is in range + kGZOutRange, // GZ markers are out of range + kUpgradeKIMarkerLevel, // upgrade the KI Marker level (current 0 and 1) + kKIShowMiniKI, // force the miniKI up + kGZFlashUpdate, // flash an update without saving (for animation of GZFill in) + kStartJournalAlert, // blink the journal image on the blackbar + kAddJournalBook, // add the journal to the blackbar + kRemoveJournalBook, // remove the journal from the blackbar + kKIOpenJournalBook, // open the journal book + kMGStartCGZGame, // Start CGZ Marker Game + kMGStopCGZGame, // Stop CGZ Marker Game + kKICreateMarkerNode, // Creates the marker game vault Node + kStartKIAlert, // start the KI alert + kUpdatePelletScore, // Updates the pellet score + kFriendInviteSent, // Friend invite was attempted and result received + kRegisterImager, // Register imager with the KI + kNoCommand + }; + + enum Flags + { + kPrivateMsg = 0x00000001, + kAdminMsg = 0x00000002, + kDead = 0x00000004, + kUNUSED1 = 0x00000008, + kStatusMsg = 0x00000010, + kNeighborMsg = 0x00000020, // sending to all the neighbors + kChannelMask = 0x0000ff00 + }; + + static const char* kChronicleKILevel; + enum KILevels + { + kNanoKI = 0, + kMicroKI = 1, + kNormalKI = 2 + }; + +#ifndef KI_CONSTANTS_ONLY + + pfKIMsg() : plMessage( nil, nil, nil ) { SetBCastFlag( kBCastByExactType ); IInit(); } + pfKIMsg( UInt8 command ) : plMessage( nil, nil, nil ) { SetBCastFlag( kBCastByExactType ); IInit(); fCommand = command; } + pfKIMsg( plKey &receiver, UInt8 command ) : plMessage( nil, nil, nil ) { AddReceiver( receiver ); IInit(); fCommand = command; } + ~pfKIMsg() { delete [] fUser; } + + CLASSNAME_REGISTER( pfKIMsg ); + GETINTERFACE_ANY( pfKIMsg, plMessage ); + + virtual void Read(hsStream* s, hsResMgr* mgr) + { + plMessage::IMsgRead( s, mgr ); + s->ReadSwap( &fCommand ); + fUser = s->ReadSafeString(); + fPlayerID = s->ReadSwap32(); + + wchar_t *temp = s->ReadSafeWString(); + if (temp) // apparently ReadSafeWString can return null, which std::wstring doesn't like being assigned + fString = temp; + else + fString = L""; + delete [] temp; + + fFlags = s->ReadSwap32(); + fDelay = s->ReadSwapScalar(); + fValue = s->ReadSwap32(); + } + + virtual void Write(hsStream* s, hsResMgr* mgr) + { + plMessage::IMsgWrite( s, mgr ); + s->WriteSwap( fCommand ); + s->WriteSafeString( fUser ); + s->WriteSwap32( fPlayerID ); + s->WriteSafeWString( fString.c_str() ); + s->WriteSwap32( fFlags ); + s->WriteSwapScalar(fDelay); + s->WriteSwap32( fValue ); + } + + UInt8 GetCommand( void ) const { return fCommand; } + + void SetString( const char *str ); + void SetString( const wchar_t *str ) { fString = str; } + std::string GetString( void ); + std::wstring GetStringU( void ) { return fString; } + + void SetUser( const char *str, UInt32 pid=0 ) { fUser = hsStrcpy( str ); fPlayerID = pid; } + const char *GetUser( void ) { return fUser; } + UInt32 GetPlayerID( void ) { return fPlayerID; } + + void SetFlags( UInt32 flags ) { fFlags = flags; } + UInt32 GetFlags( void ) const { return fFlags; } + + void SetDelay( hsScalar delay ) { fDelay = delay; } + hsScalar GetDelay( void ) { return fDelay; } + + void SetIntValue( Int32 value ) { fValue = value; } + Int32 GetIntValue( void ) { return fValue; } + +#endif // def KI_CONSTANTS_ONLY +}; + +#endif // _pfKIMsg_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMarkerMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMarkerMsg.cpp new file mode 100644 index 00000000..c3994fc6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMarkerMsg.cpp @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pfMarkerMsg.h" +#include "hsStream.h" +#include "hsGeometry3.h" + +pfMarkerMsg::pfMarkerMsg() : + fType(kMarkerCaptured), + fMarkerID(0) +{ + SetBCastFlag(plMessage::kBCastByExactType); + SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce, false); +} + +pfMarkerMsg::~pfMarkerMsg() +{ +} + +void pfMarkerMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + fType = (Type)stream->ReadByte(); + + if (fType == kMarkerCaptured) + fMarkerID = stream->ReadSwap32(); +} + +void pfMarkerMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + stream->WriteByte(fType); + + if (fType == kMarkerCaptured) + stream->WriteSwap32(fMarkerID); +} + +void pfMarkerMsg::PrintDebug(char* buf) +{ + switch (fType) + { + case pfMarkerMsg::kMarkerCaptured: + sprintf(buf, "Marker Captured - ID %d", fMarkerID); + break; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMarkerMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMarkerMsg.h new file mode 100644 index 00000000..677e571f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMarkerMsg.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pfMarkerMsg_h_inc +#define pfMarkerMsg_h_inc + +#include "../pnMessage/plMessage.h" +struct hsPoint3; + +class pfMarkerMsg : public plMessage +{ +public: + enum Type + { + // Sent by yourself when you hit a marker + // - fMarkerID is the id number of the marker we hit + kMarkerCaptured, + }; + Type fType; + + UInt32 fMarkerID; + + pfMarkerMsg(); + virtual ~pfMarkerMsg(); + + void PrintDebug(char* buf); + + CLASSNAME_REGISTER(pfMarkerMsg); + GETINTERFACE_ANY(pfMarkerMsg, plMessage); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // pfMarkerMsg_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMessageCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMessageCreatable.h new file mode 100644 index 00000000..b20a2e32 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMessageCreatable.h @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pfMessageCreatable_inc +#define pfMessageCreatable_inc + +#include "../pnFactory/plCreator.h" + +#if 0 +#include "plTriggerMsg.h" + +REGISTER_CREATABLE( plTriggerMsg ); + +#include "plPlayerMsg.h" + +REGISTER_CREATABLE( plPlayerMsg ); + +#include "plAvatarMsg.h" + +REGISTER_CREATABLE( plAvatarMsg ); +REGISTER_NONCREATABLE( plAvTaskMsg ); +REGISTER_CREATABLE( plAvSeekMsg ); +REGISTER_CREATABLE( plAvOneShotMsg ); +REGISTER_CREATABLE( plAvEnableMsg ); +#endif + + +#include "pfGameGUIMsg.h" + +REGISTER_CREATABLE( pfGameGUIMsg ); + +#include "pfGUINotifyMsg.h" + +REGISTER_CREATABLE( pfGUINotifyMsg ); + +#include "plClothingMsg.h" + +REGISTER_CREATABLE( plClothingMsg ); +REGISTER_CREATABLE( plElementRefMsg ); +REGISTER_CREATABLE( plClothingUpdateBCMsg ); + +#include "plArmatureEffectMsg.h" + +REGISTER_CREATABLE( plArmatureEffectMsg ); +REGISTER_CREATABLE( plArmatureEffectStateMsg ); + +#include "pfKIMsg.h" + +REGISTER_CREATABLE( pfKIMsg ); + +#include "pfMarkerMsg.h" + +REGISTER_CREATABLE(pfMarkerMsg); + +#include "pfBackdoorMsg.h" + +REGISTER_CREATABLE(pfBackdoorMsg); + +#include "pfMovieEventMsg.h" + +REGISTER_CREATABLE(pfMovieEventMsg); + +#endif pfMessageCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMovieEventMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMovieEventMsg.cpp new file mode 100644 index 00000000..08a02b15 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMovieEventMsg.cpp @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pfMovieEventMsg.h" +#include "hsStream.h" + + +pfMovieEventMsg::~pfMovieEventMsg() +{ + delete [] fMovieName; +} + + +void pfMovieEventMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + fReason = (Reason)stream->ReadByte(); + + fMovieName = stream->ReadSafeString(); +} + +void pfMovieEventMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + stream->WriteByte(fReason); + + stream->WriteSafeString(fMovieName); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMovieEventMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMovieEventMsg.h new file mode 100644 index 00000000..5f7b8ae6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/pfMovieEventMsg.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pfMovieEventMsg_h_inc +#define pfMovieEventMsg_h_inc + +#include "../pnMessage/plMessage.h" + +class pfMovieEventMsg : public plMessage +{ +public: + enum Reason + { + kMovieDone, + }; + Reason fReason; + + + char* fMovieName; + + pfMovieEventMsg(const char* movieName, Reason reason=kMovieDone) : plMessage(nil, nil, nil) + { + fReason = reason; + if (movieName) + fMovieName = hsStrcpy(movieName); + else + fMovieName = nil; + } + + pfMovieEventMsg() : plMessage(nil, nil, nil), fMovieName(nil), fReason(kMovieDone) + { + } + + virtual ~pfMovieEventMsg(); + + + CLASSNAME_REGISTER(pfMovieEventMsg); + GETINTERFACE_ANY(pfMovieEventMsg, plMessage); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // pfMovieEventMsg_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plArmatureEffectMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plArmatureEffectMsg.cpp new file mode 100644 index 00000000..0464b807 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plArmatureEffectMsg.cpp @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "plArmatureEffectMsg.h" +#include "../plAvatar/plArmatureEffects.h" + +plArmatureEffectStateMsg::plArmatureEffectStateMsg() : + fSurface(plArmatureEffectsMgr::kFootNoSurface), fAddSurface(false) {} + +plArmatureEffectStateMsg::~plArmatureEffectStateMsg() {} + +void plArmatureEffectStateMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + fSurface = stream->ReadByte(); + fAddSurface = stream->ReadBool(); +} + +void plArmatureEffectStateMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + stream->WriteByte(fSurface); + stream->WriteBool(fAddSurface); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plArmatureEffectMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plArmatureEffectMsg.h new file mode 100644 index 00000000..db5e0930 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plArmatureEffectMsg.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plArmatureEffectMsg_inc +#define plArmatureEffectMsg_inc + +#include "../pnMessage/plEventCallbackMsg.h" + +class plArmatureEffectMsg : public plEventCallbackMsg +{ +public: + plArmatureEffectMsg() : plEventCallbackMsg(), fTriggerIdx(-1) {} + plArmatureEffectMsg(const plKey &receiver, CallbackEvent e, int idx=0, hsScalar t=0, Int16 repeats=-1, UInt16 user=0) : + plEventCallbackMsg(receiver, e, idx, t, repeats, user), fTriggerIdx(-1) {} + + CLASSNAME_REGISTER( plArmatureEffectMsg ); + GETINTERFACE_ANY( plArmatureEffectMsg, plEventCallbackMsg ); + + // These aren't meant to go across the net, so no IO necessary. + void Read(hsStream* stream, hsResMgr* mgr) {} + void Write(hsStream* stream, hsResMgr* mgr) {} + + Int8 fTriggerIdx; +}; + +class plArmatureEffectStateMsg : public plMessage +{ +public: + plArmatureEffectStateMsg(); + ~plArmatureEffectStateMsg(); + + CLASSNAME_REGISTER( plArmatureEffectStateMsg ); + GETINTERFACE_ANY( plArmatureEffectStateMsg, plMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + Int8 fSurface; + hsBool fAddSurface; +}; + + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plClothingMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plClothingMsg.cpp new file mode 100644 index 00000000..9e1ccd00 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plClothingMsg.cpp @@ -0,0 +1,114 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plClothingMsg.h" +#include "hsResMgr.h" +#include "hsBitVector.h" + +void plClothingMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + fCommands = stream->ReadSwap32(); + if (stream->ReadBool()) + fItemKey = mgr->ReadKey(stream); + fColor.Read(stream); + fLayer = stream->ReadByte(); + fDelta = stream->ReadByte(); + fWeight = stream->ReadSwapScalar(); +} + +void plClothingMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + stream->WriteSwap32(fCommands); + stream->WriteBool(fItemKey != nil); + if (fItemKey) + mgr->WriteKey(stream, fItemKey); + fColor.Write(stream); + stream->WriteByte(fLayer); + stream->WriteByte(fDelta); + stream->WriteSwapScalar(fWeight); +} + +enum ClothingFlags +{ + kClothingCommands, + kClothingItemKey, + kClothingColor, +}; + +void plClothingMsg::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kClothingCommands)) + fCommands = s->ReadSwap32(); + if (contentFlags.IsBitSet(kClothingItemKey)) + { + if (s->ReadBool()) + fItemKey = mgr->ReadKey(s); + } + if (contentFlags.IsBitSet(kClothingColor)) + fColor.Read(s); +} + +void plClothingMsg::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kClothingCommands); + contentFlags.SetBit(kClothingItemKey); + contentFlags.SetBit(kClothingColor); + contentFlags.Write(s); + + // kClothingCommands + s->WriteSwap32(fCommands); + // kClothingItemKey + s->WriteBool(fItemKey != nil); + if (fItemKey) + mgr->WriteKey(s, fItemKey); + // kClothingColor + fColor.Write(s); +} + +///////////////////////////////////////////////////////////////////////////////// + +plClothingUpdateBCMsg::plClothingUpdateBCMsg() { SetBCastFlag(plMessage::kBCastByExactType); } + +void plClothingUpdateBCMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); +} + +void plClothingUpdateBCMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plClothingMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plClothingMsg.h new file mode 100644 index 00000000..85e46e37 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfMessage/plClothingMsg.h @@ -0,0 +1,111 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plClothingMsg_inc +#define plClothingMsg_inc + +#include "../pnMessage/plRefMsg.h" +#include "hsStream.h" +#include "../CoreLib/hsColorRGBA.h" + +class hsResMgr; + +class plClothingMsg : public plMessage +{ +protected: + UInt32 fCommands; + +public: + plKey fItemKey; + hsColorRGBA fColor; + UInt8 fLayer; + UInt8 fDelta; + hsScalar fWeight; + + plClothingMsg() : fCommands(0), fItemKey(nil), fLayer(0), fDelta(0), fWeight(0) { fColor.Set(1.f, 1.f, 1.f, 1.f); } + ~plClothingMsg() {} + + CLASSNAME_REGISTER( plClothingMsg ); + GETINTERFACE_ANY( plClothingMsg, plMessage ); + + enum commands + { + kAddItem = 0x0001, + kRemoveItem = 0x0002, + kUpdateTexture = 0x0004, + kTintItem = 0x0008, + kRetry = 0x0010, + kTintSkin = 0x0020, + kBlendSkin = 0x0040, + kMorphItem = 0x0080, + kSaveCustomizations = 0x0100, + }; + + hsBool GetCommand(UInt32 command) { return fCommands & command; } + void AddCommand(UInt32 command) { fCommands |= command; } + hsBool ResendUpdate() { return fCommands != kUpdateTexture; } + + // IO + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + // WriteVersion writes the current version of this creatable and ReadVersion will read in + // any previous version. + virtual void ReadVersion(hsStream* s, hsResMgr* mgr); + virtual void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +class plElementRefMsg : public plGenRefMsg +{ +public: + char *fElementName; + UInt32 fLayer; + + plElementRefMsg() : plGenRefMsg(), fElementName(nil), fLayer(1) {} + plElementRefMsg(const plKey &r, UInt8 c, int which, int type, char *name, UInt8 layer) : plGenRefMsg(r, c, which, type) + { + fLayer = layer; + fElementName = hsStrcpy(name); + } + ~plElementRefMsg() { delete [] fElementName; } + + CLASSNAME_REGISTER( plElementRefMsg ); + GETINTERFACE_ANY( plElementRefMsg, plGenRefMsg ); +}; + +class plClothingUpdateBCMsg : public plMessage +{ +public: + plClothingUpdateBCMsg(); + ~plClothingUpdateBCMsg() {} + + CLASSNAME_REGISTER( plClothingUpdateBCMsg ); + GETINTERFACE_ANY( plClothingUpdateBCMsg, plMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plClothingMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGame.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGame.cpp new file mode 100644 index 00000000..eae3c608 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGame.cpp @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyBlueSpiralGame.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base BlueSpiral game client class +// + +pyBlueSpiralGame::pyBlueSpiralGame(): pyGameCli() {} + +pyBlueSpiralGame::pyBlueSpiralGame(pfGameCli* client): pyGameCli(client) +{ + if (client && (client->GetGameTypeId() != kGameTypeId_BlueSpiral)) + gameClient = nil; // wrong type, just clear it out +} + +bool pyBlueSpiralGame::IsBlueSpiralGame(std::wstring guid) +{ + Uuid gameUuid(guid.c_str()); + return gameUuid == kGameTypeId_BlueSpiral; +} + +void pyBlueSpiralGame::JoinCommonBlueSpiralGame(pyKey& callbackKey, unsigned gameID) +{ + BlueSpiral_CreateParam init; + pfGameMgr::GetInstance()->JoinCommonGame(callbackKey.getKey(), kGameTypeId_BlueSpiral, gameID, sizeof(init), &init); +} + +void pyBlueSpiralGame::StartGame() +{ + if (gameClient) + { + pfGmBlueSpiral* blueSpiral = pfGmBlueSpiral::ConvertNoRef(gameClient); + blueSpiral->StartGame(); + } +} + +void pyBlueSpiralGame::HitCloth(int clothNum) +{ + if (gameClient) + { + pfGmBlueSpiral* blueSpiral = pfGmBlueSpiral::ConvertNoRef(gameClient); + blueSpiral->HitCloth(clothNum); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGame.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGame.h new file mode 100644 index 00000000..a0773677 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGame.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyBlueSpiralGame_h +#define pyBlueSpiralGame_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyBlueSpiralGame +// +// PURPOSE: Class wrapper for the BlueSpiral game client +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCli.h" +#include "../../pyKey.h" + +class pyBlueSpiralGame : public pyGameCli +{ +protected: + pyBlueSpiralGame(); + pyBlueSpiralGame(pfGameCli* client); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptBlueSpiralGame); + static PyObject* New(pfGameCli* client); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyBlueSpiralGame object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyBlueSpiralGame); // converts a PyObject to a pyBlueSpiralGame (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaMethods(std::vector& methods); + + static bool IsBlueSpiralGame(std::wstring guid); + static void JoinCommonBlueSpiralGame(pyKey& callbackKey, unsigned gameID); + + void StartGame(); + void HitCloth(int clothNum); +}; + +#endif // pyBlueSpiralGame_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGameGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGameGlue.cpp new file mode 100644 index 00000000..0a8180a5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralGameGlue.cpp @@ -0,0 +1,142 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyBlueSpiralGame.h" + +#include +#include "../../pyEnum.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base BlueSpiral game client class +// + +PYTHON_CLASS_DEFINITION(ptBlueSpiralGame, pyBlueSpiralGame); + +PYTHON_DEFAULT_NEW_DEFINITION(ptBlueSpiralGame, pyBlueSpiralGame) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptBlueSpiralGame) + +PYTHON_NO_INIT_DEFINITION(ptBlueSpiralGame) + +PYTHON_GLOBAL_METHOD_DEFINITION(PtIsBlueSpiralGame, args, "Params: typeID\nReturns true if the specifed typeID (guid as a string) is a BlueSpiral game") +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "PtIsBlueSpiralGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + bool retVal = pyBlueSpiralGame::IsBlueSpiralGame(text); + delete [] text; + PYTHON_RETURN_BOOL(retVal); + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(textObj); + wchar_t* wText = hsStringToWString(text); + bool retVal = pyBlueSpiralGame::IsBlueSpiralGame(wText); + delete [] wText; + PYTHON_RETURN_BOOL(retVal); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtIsBlueSpiralGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtJoinCommonBlueSpiralGame, args, "Params: callbackKey, gameID\nJoins a common BlueSpiral game with the specified ID. If one doesn't exist, it creates it") +{ + PyObject* callbackObj = NULL; + int gameID = 0; + if (!PyArg_ParseTuple(args, "Oi", &callbackObj, &gameID)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinCommonBlueSpiralGame expects a ptKey and an integer"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(callbackObj)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinCommonBlueSpiralGame expects a ptKey and an integer"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(callbackObj); + pyBlueSpiralGame::JoinCommonBlueSpiralGame(*key, gameID); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptBlueSpiralGame, startGame, StartGame) + +PYTHON_METHOD_DEFINITION(ptBlueSpiralGame, hitCloth, args) +{ + int clothNum = 0; + if (!PyArg_ParseTuple(args, "i", &clothNum)) + { + PyErr_SetString(PyExc_TypeError, "hitCloth expects one integer"); + PYTHON_RETURN_ERROR; + } + self->fThis->HitCloth(clothNum); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptBlueSpiralGame) + PYTHON_BASIC_METHOD(ptBlueSpiralGame, startGame, "Starts a new game"), + PYTHON_METHOD(ptBlueSpiralGame, hitCloth, "Params: clothNum\nTells the server you hit the specified cloth"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptBlueSpiralGame, pyGameCli, "Game client for the BlueSpiral game"); + +// required functions for PyObject interoperability +PyObject* pyBlueSpiralGame::New(pfGameCli* client) +{ + ptBlueSpiralGame *newObj = (ptBlueSpiralGame*)ptBlueSpiralGame_type.tp_new(&ptBlueSpiralGame_type, NULL, NULL); + if (client && (client->GetGameTypeId() == kGameTypeId_BlueSpiral)) + newObj->fThis->gameClient = client; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptBlueSpiralGame, pyBlueSpiralGame) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptBlueSpiralGame, pyBlueSpiralGame) + +// Module and method definitions +void pyBlueSpiralGame::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptBlueSpiralGame); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyBlueSpiralGame::AddPlasmaMethods(std::vector& methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtIsBlueSpiralGame); + PYTHON_GLOBAL_METHOD(methods, PtJoinCommonBlueSpiralGame); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsg.cpp new file mode 100644 index 00000000..5996cea3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsg.cpp @@ -0,0 +1,139 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyBlueSpiralMsg.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base BlueSpiral msg class +// + +pyBlueSpiralMsg::pyBlueSpiralMsg(): pyGameCliMsg() {} + +pyBlueSpiralMsg::pyBlueSpiralMsg(pfGameCliMsg* msg): pyGameCliMsg(msg) +{ + if (message && (message->gameCli->GetGameTypeId() != kGameTypeId_BlueSpiral)) + message = nil; // wrong type, just clear it out +} + +int pyBlueSpiralMsg::GetBlueSpiralMsgType() const +{ + if (message) + return message->netMsg->messageId; + return -1; +} + +PyObject* pyBlueSpiralMsg::UpcastToFinalBlueSpiralMsg() const +{ + if (!message) + PYTHON_RETURN_NONE; + switch (message->netMsg->messageId) + { + case kSrv2Cli_BlueSpiral_ClothOrder: + return pyBlueSpiralClothOrderMsg::New(message); + case kSrv2Cli_BlueSpiral_SuccessfulHit: + return pyBlueSpiralSuccessfulHitMsg::New(message); + case kSrv2Cli_BlueSpiral_GameWon: + return pyBlueSpiralGameWonMsg::New(message); + case kSrv2Cli_BlueSpiral_GameOver: + return pyBlueSpiralGameOverMsg::New(message); + case kSrv2Cli_BlueSpiral_GameStarted: + return pyBlueSpiralGameStartedMsg::New(message); + default: + PYTHON_RETURN_NONE; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// The different messages we can receive +// + +pyBlueSpiralClothOrderMsg::pyBlueSpiralClothOrderMsg(): pyBlueSpiralMsg() {} + +pyBlueSpiralClothOrderMsg::pyBlueSpiralClothOrderMsg(pfGameCliMsg* msg): pyBlueSpiralMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_BlueSpiral_ClothOrder)) + message = nil; // wrong type, just clear it out +} + +std::vector pyBlueSpiralClothOrderMsg::Order() +{ + std::vector retVal; + if (message) + { + const Srv2Cli_BlueSpiral_ClothOrder* gmMsg = (const Srv2Cli_BlueSpiral_ClothOrder*)message->netMsg; + for (int i = 0; i < arrsize(gmMsg->order); ++i) + retVal.push_back(gmMsg->order[i]); + } + return retVal; +} + +/////////////////////////////////////////////////////////////////////////////// +pyBlueSpiralSuccessfulHitMsg::pyBlueSpiralSuccessfulHitMsg(): pyBlueSpiralMsg() {} + +pyBlueSpiralSuccessfulHitMsg::pyBlueSpiralSuccessfulHitMsg(pfGameCliMsg* msg): pyBlueSpiralMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_BlueSpiral_SuccessfulHit)) + message = nil; // wrong type, just clear it out +} + +/////////////////////////////////////////////////////////////////////////////// +pyBlueSpiralGameWonMsg::pyBlueSpiralGameWonMsg(): pyBlueSpiralMsg() {} + +pyBlueSpiralGameWonMsg::pyBlueSpiralGameWonMsg(pfGameCliMsg* msg): pyBlueSpiralMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_BlueSpiral_GameWon)) + message = nil; // wrong type, just clear it out +} + +/////////////////////////////////////////////////////////////////////////////// +pyBlueSpiralGameOverMsg::pyBlueSpiralGameOverMsg(): pyBlueSpiralMsg() {} + +pyBlueSpiralGameOverMsg::pyBlueSpiralGameOverMsg(pfGameCliMsg* msg): pyBlueSpiralMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_BlueSpiral_GameOver)) + message = nil; // wrong type, just clear it out +} + +/////////////////////////////////////////////////////////////////////////////// +pyBlueSpiralGameStartedMsg::pyBlueSpiralGameStartedMsg(): pyBlueSpiralMsg() {} + +pyBlueSpiralGameStartedMsg::pyBlueSpiralGameStartedMsg(pfGameCliMsg* msg): pyBlueSpiralMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_BlueSpiral_GameStarted)) + message = nil; // wrong type, just clear it out +} + +bool pyBlueSpiralGameStartedMsg::StartSpin() +{ + if (message) + { + const Srv2Cli_BlueSpiral_GameStarted* gmMsg = (const Srv2Cli_BlueSpiral_GameStarted*)message->netMsg; + return gmMsg->startSpin; + } + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsg.h new file mode 100644 index 00000000..5180a57c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsg.h @@ -0,0 +1,153 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyBlueSpiralMsg_h +#define pyBlueSpiralMsg_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyBlueSpiralMsg +// +// PURPOSE: Class wrapper for BlueSpiral game messages +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCliMsg.h" + +class pyBlueSpiralMsg : public pyGameCliMsg +{ +protected: + pyBlueSpiralMsg(); + pyBlueSpiralMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptBlueSpiralMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyBlueSpiralMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyBlueSpiralMsg); // converts a PyObject to a pyBlueSpiralMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + + int GetBlueSpiralMsgType() const; + + PyObject* UpcastToFinalBlueSpiralMsg() const; // returns this message as the blue spiral message it is +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyBlueSpiralClothOrderMsg : public pyBlueSpiralMsg +{ +protected: + pyBlueSpiralClothOrderMsg(); + pyBlueSpiralClothOrderMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptBlueSpiralClothOrderMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyBlueSpiralClothOrderMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyBlueSpiralClothOrderMsg); // converts a PyObject to a pyBlueSpiralClothOrderMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + std::vector Order(); +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyBlueSpiralSuccessfulHitMsg : public pyBlueSpiralMsg +{ +protected: + pyBlueSpiralSuccessfulHitMsg(); + pyBlueSpiralSuccessfulHitMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptBlueSpiralSuccessfulHitMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyBlueSpiralSuccessfulHitMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyBlueSpiralSuccessfulHitMsg); // converts a PyObject to a pyBlueSpiralSuccessfulHitMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyBlueSpiralGameWonMsg : public pyBlueSpiralMsg +{ +protected: + pyBlueSpiralGameWonMsg(); + pyBlueSpiralGameWonMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptBlueSpiralGameWonMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyBlueSpiralGameWonMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyBlueSpiralGameWonMsg); // converts a PyObject to a pyBlueSpiralGameWonMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyBlueSpiralGameOverMsg : public pyBlueSpiralMsg +{ +protected: + pyBlueSpiralGameOverMsg(); + pyBlueSpiralGameOverMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptBlueSpiralGameOverMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyBlueSpiralGameOverMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyBlueSpiralGameOverMsg); // converts a PyObject to a pyBlueSpiralGameOverMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyBlueSpiralGameStartedMsg : public pyBlueSpiralMsg +{ +protected: + pyBlueSpiralGameStartedMsg(); + pyBlueSpiralGameStartedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptBlueSpiralGameStartedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyBlueSpiralGameStartedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyBlueSpiralGameStartedMsg); // converts a PyObject to a pyBlueSpiralGameStartedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + bool StartSpin(); +}; + +#endif // pyBlueSpiralMsg_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsgGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsgGlue.cpp new file mode 100644 index 00000000..081739ab --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/BlueSpiral/pyBlueSpiralMsgGlue.cpp @@ -0,0 +1,281 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyBlueSpiralMsg.h" +#include "../../pyEnum.h" + +#include + +/////////////////////////////////////////////////////////////////////////////// +// +// Base TTT msg class +// + +PYTHON_CLASS_DEFINITION(ptBlueSpiralMsg, pyBlueSpiralMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptBlueSpiralMsg, pyBlueSpiralMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptBlueSpiralMsg) + +PYTHON_NO_INIT_DEFINITION(ptBlueSpiralMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptBlueSpiralMsg, getBlueSpiralMsgType) +{ + return PyInt_FromLong(self->fThis->GetBlueSpiralMsgType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptBlueSpiralMsg, upcastToFinalBlueSpiralMsg) +{ + return self->fThis->UpcastToFinalBlueSpiralMsg(); +} + +PYTHON_START_METHODS_TABLE(ptBlueSpiralMsg) + PYTHON_METHOD_NOARGS(ptBlueSpiralMsg, getBlueSpiralMsgType, "Returns the type of the BlueSpiral message (see PtBlueSpiralMsgTypes)"), + PYTHON_METHOD_NOARGS(ptBlueSpiralMsg, upcastToFinalBlueSpiralMsg, "Returns this message as the BlueSpiral message it really is"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptBlueSpiralMsg, pyGameCliMsg, "Base class for BlueSpiral game messages"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptBlueSpiralMsg, pyBlueSpiralMsg); + +// required functions for PyObject interoperability +PyObject* pyBlueSpiralMsg::New(pfGameCliMsg* msg) +{ + ptBlueSpiralMsg *newObj = (ptBlueSpiralMsg*)ptBlueSpiralMsg_type.tp_new(&ptBlueSpiralMsg_type, NULL, NULL); + if (msg && (msg->gameCli->GetGameTypeId() == kGameTypeId_BlueSpiral)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptBlueSpiralMsg, pyBlueSpiralMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptBlueSpiralMsg, pyBlueSpiralMsg) + +// Module and method definitions +void pyBlueSpiralMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptBlueSpiralMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyBlueSpiralMsg::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtBlueSpiralMsgTypes); + PYTHON_ENUM_ELEMENT(PtBlueSpiralMsgTypes, kBlueSpiralClothOrder, kSrv2Cli_BlueSpiral_ClothOrder); + PYTHON_ENUM_ELEMENT(PtBlueSpiralMsgTypes, kBlueSpiralSuccessfulHit, kSrv2Cli_BlueSpiral_SuccessfulHit); + PYTHON_ENUM_ELEMENT(PtBlueSpiralMsgTypes, kBlueSpiralGameWon, kSrv2Cli_BlueSpiral_GameWon); + PYTHON_ENUM_ELEMENT(PtBlueSpiralMsgTypes, kBlueSpiralGameOver, kSrv2Cli_BlueSpiral_GameOver); + PYTHON_ENUM_ELEMENT(PtBlueSpiralMsgTypes, kBlueSpiralGameStarted, kSrv2Cli_BlueSpiral_GameStarted); + PYTHON_ENUM_END(m, PtBlueSpiralMsgTypes); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Game client message subclasses +// + +PYTHON_CLASS_DEFINITION(ptBlueSpiralClothOrderMsg, pyBlueSpiralClothOrderMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptBlueSpiralClothOrderMsg, pyBlueSpiralClothOrderMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptBlueSpiralClothOrderMsg) + +PYTHON_NO_INIT_DEFINITION(ptBlueSpiralClothOrderMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptBlueSpiralClothOrderMsg, order) +{ + std::vector order = self->fThis->Order(); + PyObject* retVal = PyList_New(order.size()); + for (unsigned i = 0; i < order.size(); ++i) + PyList_SetItem(retVal, i, PyInt_FromLong(order[i])); + return retVal; +} + +PYTHON_START_METHODS_TABLE(ptBlueSpiralClothOrderMsg) + PYTHON_METHOD_NOARGS(ptBlueSpiralClothOrderMsg, order, "Returns a list of numbers indicating the correct order to hit the clothes in"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptBlueSpiralClothOrderMsg, pyBlueSpiralMsg, "BlueSpiral message received when the game is started and the cloth order is set"); + +// required functions for PyObject interoperability +PyObject* pyBlueSpiralClothOrderMsg::New(pfGameCliMsg* msg) +{ + ptBlueSpiralClothOrderMsg *newObj = (ptBlueSpiralClothOrderMsg*)ptBlueSpiralClothOrderMsg_type.tp_new(&ptBlueSpiralClothOrderMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_BlueSpiral_ClothOrder)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptBlueSpiralClothOrderMsg, pyBlueSpiralClothOrderMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptBlueSpiralClothOrderMsg, pyBlueSpiralClothOrderMsg) + +// Module and method definitions +void pyBlueSpiralClothOrderMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptBlueSpiralClothOrderMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptBlueSpiralSuccessfulHitMsg, pyBlueSpiralSuccessfulHitMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptBlueSpiralSuccessfulHitMsg, pyBlueSpiralSuccessfulHitMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptBlueSpiralSuccessfulHitMsg) + +PYTHON_NO_INIT_DEFINITION(ptBlueSpiralSuccessfulHitMsg) + +PYTHON_START_METHODS_TABLE(ptBlueSpiralSuccessfulHitMsg) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptBlueSpiralSuccessfulHitMsg, pyBlueSpiralMsg, "BlueSpiral message received when a cloth is hit in the correct order"); + +// required functions for PyObject interoperability +PyObject* pyBlueSpiralSuccessfulHitMsg::New(pfGameCliMsg* msg) +{ + ptBlueSpiralSuccessfulHitMsg *newObj = (ptBlueSpiralSuccessfulHitMsg*)ptBlueSpiralSuccessfulHitMsg_type.tp_new(&ptBlueSpiralSuccessfulHitMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_BlueSpiral_SuccessfulHit)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptBlueSpiralSuccessfulHitMsg, pyBlueSpiralSuccessfulHitMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptBlueSpiralSuccessfulHitMsg, pyBlueSpiralSuccessfulHitMsg) + +// Module and method definitions +void pyBlueSpiralSuccessfulHitMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptBlueSpiralSuccessfulHitMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptBlueSpiralGameWonMsg, pyBlueSpiralGameWonMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptBlueSpiralGameWonMsg, pyBlueSpiralGameWonMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptBlueSpiralGameWonMsg) + +PYTHON_NO_INIT_DEFINITION(ptBlueSpiralGameWonMsg) + +PYTHON_START_METHODS_TABLE(ptBlueSpiralGameWonMsg) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptBlueSpiralGameWonMsg, pyBlueSpiralMsg, "BlueSpiral message received when the last cloth is successfully hit"); + +// required functions for PyObject interoperability +PyObject* pyBlueSpiralGameWonMsg::New(pfGameCliMsg* msg) +{ + ptBlueSpiralGameWonMsg *newObj = (ptBlueSpiralGameWonMsg*)ptBlueSpiralGameWonMsg_type.tp_new(&ptBlueSpiralGameWonMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_BlueSpiral_GameWon)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptBlueSpiralGameWonMsg, pyBlueSpiralGameWonMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptBlueSpiralGameWonMsg, pyBlueSpiralGameWonMsg) + +// Module and method definitions +void pyBlueSpiralGameWonMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptBlueSpiralGameWonMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptBlueSpiralGameOverMsg, pyBlueSpiralGameOverMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptBlueSpiralGameOverMsg, pyBlueSpiralGameOverMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptBlueSpiralGameOverMsg) + +PYTHON_NO_INIT_DEFINITION(ptBlueSpiralGameOverMsg) + +PYTHON_START_METHODS_TABLE(ptBlueSpiralGameOverMsg) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptBlueSpiralGameOverMsg, pyBlueSpiralMsg, "BlueSpiral message received when the timer runs out, someone hits the wrong cloth, or the game is restarted (before a game start msg in that last case)"); + +// required functions for PyObject interoperability +PyObject* pyBlueSpiralGameOverMsg::New(pfGameCliMsg* msg) +{ + ptBlueSpiralGameOverMsg *newObj = (ptBlueSpiralGameOverMsg*)ptBlueSpiralGameOverMsg_type.tp_new(&ptBlueSpiralGameOverMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_BlueSpiral_GameOver)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptBlueSpiralGameOverMsg, pyBlueSpiralGameOverMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptBlueSpiralGameOverMsg, pyBlueSpiralGameOverMsg) + +// Module and method definitions +void pyBlueSpiralGameOverMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptBlueSpiralGameOverMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptBlueSpiralGameStartedMsg, pyBlueSpiralGameStartedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptBlueSpiralGameStartedMsg, pyBlueSpiralGameStartedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptBlueSpiralGameStartedMsg) + +PYTHON_NO_INIT_DEFINITION(ptBlueSpiralGameStartedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptBlueSpiralGameStartedMsg, startSpin) +{ + PYTHON_RETURN_BOOL(self->fThis->StartSpin()); +} + +PYTHON_START_METHODS_TABLE(ptBlueSpiralGameStartedMsg) + PYTHON_METHOD_NOARGS(ptBlueSpiralGameStartedMsg, startSpin, "Returns true if you are supposed to start spinning the door thingy"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptBlueSpiralGameStartedMsg, pyBlueSpiralMsg, "BlueSpiral message received when someone starts the game (or when you join a game that is running)"); + +// required functions for PyObject interoperability +PyObject* pyBlueSpiralGameStartedMsg::New(pfGameCliMsg* msg) +{ + ptBlueSpiralGameStartedMsg *newObj = (ptBlueSpiralGameStartedMsg*)ptBlueSpiralGameStartedMsg_type.tp_new(&ptBlueSpiralGameStartedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_BlueSpiral_GameStarted)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptBlueSpiralGameStartedMsg, pyBlueSpiralGameStartedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptBlueSpiralGameStartedMsg, pyBlueSpiralGameStartedMsg) + +// Module and method definitions +void pyBlueSpiralGameStartedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptBlueSpiralGameStartedMsg); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGame.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGame.cpp new file mode 100644 index 00000000..dc3d429d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGame.cpp @@ -0,0 +1,114 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyClimbingWallGame.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base climbing wall game client class +// + +pyClimbingWallGame::pyClimbingWallGame(): pyGameCli() {} + +pyClimbingWallGame::pyClimbingWallGame(pfGameCli* client): pyGameCli(client) +{ + if (client && (client->GetGameTypeId() != kGameTypeId_ClimbingWall)) + gameClient = nil; // wrong type, just clear it out +} + +bool pyClimbingWallGame::IsClimbingWallGame(std::wstring guid) +{ + Uuid gameUuid(guid.c_str()); + return gameUuid == kGameTypeId_ClimbingWall; +} + +void pyClimbingWallGame::JoinCommonClimbingWallGame(pyKey& callbackKey, unsigned gameID) +{ + ClimbingWall_CreateParam init; + pfGameMgr::GetInstance()->JoinCommonGame(callbackKey.getKey(), kGameTypeId_ClimbingWall, gameID, sizeof(init), &init); +} + +void pyClimbingWallGame::ChangeNumBlockers(int amountToAdjust) +{ + if (gameClient) + { + pfGmClimbingWall* climbingWall = pfGmClimbingWall::ConvertNoRef(gameClient); + climbingWall->ChangeNumBlockers(amountToAdjust); + } +} + +void pyClimbingWallGame::Ready(unsigned readyType, unsigned teamNumber) +{ + if (gameClient) + { + pfGmClimbingWall* climbingWall = pfGmClimbingWall::ConvertNoRef(gameClient); + climbingWall->Ready(readyType, teamNumber); + } +} + +void pyClimbingWallGame::ChangeBlocker(unsigned teamNumber, unsigned blockerNumber, bool added) +{ + if (gameClient) + { + pfGmClimbingWall* climbingWall = pfGmClimbingWall::ConvertNoRef(gameClient); + climbingWall->ChangeBlocker(teamNumber, blockerNumber, added); + } +} + +void pyClimbingWallGame::Reset() +{ + if (gameClient) + { + pfGmClimbingWall* climbingWall = pfGmClimbingWall::ConvertNoRef(gameClient); + climbingWall->Reset(); + } +} + +void pyClimbingWallGame::PlayerEntered(unsigned teamNumber) +{ + if (gameClient) + { + pfGmClimbingWall* climbingWall = pfGmClimbingWall::ConvertNoRef(gameClient); + climbingWall->PlayerEntered(teamNumber); + } +} + +void pyClimbingWallGame::FinishedGame() +{ + if (gameClient) + { + pfGmClimbingWall* climbingWall = pfGmClimbingWall::ConvertNoRef(gameClient); + climbingWall->FinishedGame(); + } +} + +void pyClimbingWallGame::Panic() +{ + if (gameClient) + { + pfGmClimbingWall* climbingWall = pfGmClimbingWall::ConvertNoRef(gameClient); + climbingWall->Panic(); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGame.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGame.h new file mode 100644 index 00000000..cba987d7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGame.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyClimbingWallGame_h +#define pyClimbingWallGame_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyClimbingWallGame +// +// PURPOSE: Class wrapper for the climbing wall game client +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCli.h" +#include "../../pyKey.h" + +class pyClimbingWallGame : public pyGameCli +{ +protected: + pyClimbingWallGame(); + pyClimbingWallGame(pfGameCli* client); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptClimbingWallGame); + static PyObject* New(pfGameCli* client); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyClimbingWallGame object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyClimbingWallGame); // converts a PyObject to a pyClimbingWallGame (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaMethods(std::vector& methods); + static void AddPlasmaConstantsClasses(PyObject* m); + + static bool IsClimbingWallGame(std::wstring guid); + static void JoinCommonClimbingWallGame(pyKey& callbackKey, unsigned gameID); + + void ChangeNumBlockers(int amountToAdjust); + void Ready(unsigned readyType, unsigned teamNumber); + void ChangeBlocker(unsigned teamNumber, unsigned blockerNumber, bool added); + void Reset(); + void PlayerEntered(unsigned teamNumber); + void FinishedGame(); + void Panic(); +}; + +#endif // pyClimbingWallGame_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGameGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGameGlue.cpp new file mode 100644 index 00000000..558b6c65 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallGameGlue.cpp @@ -0,0 +1,196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyClimbingWallGame.h" + +#include +#include "../../pyEnum.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base climbing wall game client class +// + +PYTHON_CLASS_DEFINITION(ptClimbingWallGame, pyClimbingWallGame); + +PYTHON_DEFAULT_NEW_DEFINITION(ptClimbingWallGame, pyClimbingWallGame) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptClimbingWallGame) + +PYTHON_NO_INIT_DEFINITION(ptClimbingWallGame) + +PYTHON_GLOBAL_METHOD_DEFINITION(PtIsClimbingWallGame, args, "Params: typeID\nReturns true if the specifed typeID (guid as a string) is a ClimbingWall game") +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "PtIsClimbingWallGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + bool retVal = pyClimbingWallGame::IsClimbingWallGame(text); + delete [] text; + PYTHON_RETURN_BOOL(retVal); + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(textObj); + wchar_t* wText = hsStringToWString(text); + bool retVal = pyClimbingWallGame::IsClimbingWallGame(wText); + delete [] wText; + PYTHON_RETURN_BOOL(retVal); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtIsClimbingWallGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtJoinCommonClimbingWallGame, args, "Params: callbackKey, gameID\nJoins a common ClimbingWall game with the specified ID. If one doesn't exist, it creates it") +{ + PyObject* callbackObj = NULL; + int gameID = 0; + if (!PyArg_ParseTuple(args, "Oii", &callbackObj, &gameID)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinCommonClimbingWallGame expects a ptKey and an integer"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(callbackObj)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinCommonClimbingWallGame expects a ptKey and an integer"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(callbackObj); + pyClimbingWallGame::JoinCommonClimbingWallGame(*key, gameID); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptClimbingWallGame, changeNumBlockers, args) +{ + int amountToAdjust; + if (!PyArg_ParseTuple(args, "i", &amountToAdjust)) + { + PyErr_SetString(PyExc_TypeError, "changeNumBlockers expects an integer"); + PYTHON_RETURN_ERROR; + } + self->fThis->ChangeNumBlockers(amountToAdjust); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptClimbingWallGame, ready, args) +{ + int readyType, teamNumber; + if (!PyArg_ParseTuple(args, "ii", &readyType, &teamNumber)) + { + PyErr_SetString(PyExc_TypeError, "ready expects two integers"); + PYTHON_RETURN_ERROR; + } + self->fThis->Ready((unsigned)readyType, (unsigned)teamNumber); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptClimbingWallGame, changeBlocker, args) +{ + int teamNumber, blockerNumber; + char added; + if (!PyArg_ParseTuple(args, "iib", &teamNumber, &blockerNumber, &added)) + { + PyErr_SetString(PyExc_TypeError, "changeBlocker expects two integers and a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->ChangeBlocker(teamNumber, blockerNumber, added != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptClimbingWallGame, reset, Reset) + +PYTHON_METHOD_DEFINITION(ptClimbingWallGame, playerEntered, args) +{ + int teamNumber; + if (!PyArg_ParseTuple(args, "i", &teamNumber)) + { + PyErr_SetString(PyExc_TypeError, "playerEntered expects an integer"); + PYTHON_RETURN_ERROR; + } + self->fThis->PlayerEntered(teamNumber); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptClimbingWallGame, finishedGame, FinishedGame) + +PYTHON_BASIC_METHOD_DEFINITION(ptClimbingWallGame, panic, Panic) + +PYTHON_START_METHODS_TABLE(ptClimbingWallGame) + PYTHON_METHOD(ptClimbingWallGame, changeNumBlockers, "Params: amountToAdjust\nAdjusts the number of blockers we are playing with"), + PYTHON_METHOD(ptClimbingWallGame, ready, "Params: readyType, teamNumber\nMarks the specified team as ready for the specified type (See PtClimbingWallReadyTypes)"), + PYTHON_METHOD(ptClimbingWallGame, changeBlocker, "Params: teamNumber, blockerNumber, added\nChanges the specified marker's state for the specified team"), + PYTHON_BASIC_METHOD(ptClimbingWallGame, reset, "Attempts to reset the game's control panel"), + PYTHON_METHOD(ptClimbingWallGame, playerEntered, "Params: teamNumber\nTells the server that you are trying to play the game for the specified team"), + PYTHON_BASIC_METHOD(ptClimbingWallGame, finishedGame, "Tells the server you reached the top of the wall"), + PYTHON_BASIC_METHOD(ptClimbingWallGame, panic, "Tells the server you are panicking and want your blockers reset"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptClimbingWallGame, pyGameCli, "Game client for the ClimbingWall game"); + +// required functions for PyObject interoperability +PyObject* pyClimbingWallGame::New(pfGameCli* client) +{ + ptClimbingWallGame *newObj = (ptClimbingWallGame*)ptClimbingWallGame_type.tp_new(&ptClimbingWallGame_type, NULL, NULL); + if (client && (client->GetGameTypeId() == kGameTypeId_ClimbingWall)) + newObj->fThis->gameClient = client; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptClimbingWallGame, pyClimbingWallGame) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptClimbingWallGame, pyClimbingWallGame) + +// Module and method definitions +void pyClimbingWallGame::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptClimbingWallGame); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyClimbingWallGame::AddPlasmaMethods(std::vector& methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtIsClimbingWallGame); + PYTHON_GLOBAL_METHOD(methods, PtJoinCommonClimbingWallGame); +} + +void pyClimbingWallGame::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtClimbingWallReadyTypes); + PYTHON_ENUM_ELEMENT(PtClimbingWallReadyTypes, kClimbingWallReadyNumBlockers, kClimbingWallReadyNumBlockers); + PYTHON_ENUM_ELEMENT(PtClimbingWallReadyTypes, kClimbingWallReadyBlockers, kClimbingWallReadyBlockers); + PYTHON_ENUM_END(m, PtClimbingWallReadyTypes); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsg.cpp new file mode 100644 index 00000000..c6287a36 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsg.cpp @@ -0,0 +1,308 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyClimbingWallMsg.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base climbing wall msg class +// + +pyClimbingWallMsg::pyClimbingWallMsg(): pyGameCliMsg() {} + +pyClimbingWallMsg::pyClimbingWallMsg(pfGameCliMsg* msg): pyGameCliMsg(msg) +{ + if (message && (message->gameCli->GetGameTypeId() != kGameTypeId_ClimbingWall)) + message = nil; // wrong type, just clear it out +} + +int pyClimbingWallMsg::GetClimbingWallMsgType() const +{ + if (message) + return message->netMsg->messageId; + return -1; +} + +PyObject* pyClimbingWallMsg::UpcastToFinalClimbingWallMsg() const +{ + if (!message) + PYTHON_RETURN_NONE; + switch (message->netMsg->messageId) + { + case kSrv2Cli_ClimbingWall_NumBlockersChanged: + return pyClimbingWallNumBlockersChangedMsg::New(message); + case kSrv2Cli_ClimbingWall_Ready: + return pyClimbingWallReadyMsg::New(message); + case kSrv2Cli_ClimbingWall_BlockersChanged: + return pyClimbingWallBlockersChangedMsg::New(message); + case kSrv2Cli_ClimbingWall_PlayerEntered: + return pyClimbingWallPlayerEnteredMsg::New(message); + case kSrv2Cli_ClimbingWall_SuitMachineLocked: + return pyClimbingWallSuitMachineLockedMsg::New(message); + case kSrv2Cli_ClimbingWall_GameOver: + return pyClimbingWallGameOverMsg::New(message); + default: + PYTHON_RETURN_NONE; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// The different messages we can receive +// + +pyClimbingWallNumBlockersChangedMsg::pyClimbingWallNumBlockersChangedMsg(): pyClimbingWallMsg() {} + +pyClimbingWallNumBlockersChangedMsg::pyClimbingWallNumBlockersChangedMsg(pfGameCliMsg* msg): pyClimbingWallMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_ClimbingWall_NumBlockersChanged)) + message = nil; // wrong type, just clear it out +} + +int pyClimbingWallNumBlockersChangedMsg::NewBlockerCount() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_NumBlockersChanged* gmMsg = (const Srv2Cli_ClimbingWall_NumBlockersChanged*)message->netMsg; + return gmMsg->newBlockerCount; + } + return 0; +} + +bool pyClimbingWallNumBlockersChangedMsg::LocalOnly() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_Ready* gmMsg = (const Srv2Cli_ClimbingWall_Ready*)message->netMsg; + return gmMsg->localOnly; + } + return true; // safe-guard so we don't screw up other's state if the python does something stupid +} + +/////////////////////////////////////////////////////////////////////////////// + +pyClimbingWallReadyMsg::pyClimbingWallReadyMsg(): pyClimbingWallMsg() {} + +pyClimbingWallReadyMsg::pyClimbingWallReadyMsg(pfGameCliMsg* msg): pyClimbingWallMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_ClimbingWall_Ready)) + message = nil; // wrong type, just clear it out +} + +int pyClimbingWallReadyMsg::ReadyType() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_Ready* gmMsg = (const Srv2Cli_ClimbingWall_Ready*)message->netMsg; + return gmMsg->readyType; + } + return 0; +} + +bool pyClimbingWallReadyMsg::Team1Ready() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_Ready* gmMsg = (const Srv2Cli_ClimbingWall_Ready*)message->netMsg; + return gmMsg->team1Ready; + } + return false; +} + +bool pyClimbingWallReadyMsg::Team2Ready() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_Ready* gmMsg = (const Srv2Cli_ClimbingWall_Ready*)message->netMsg; + return gmMsg->team2Ready; + } + return false; +} + +bool pyClimbingWallReadyMsg::LocalOnly() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_Ready* gmMsg = (const Srv2Cli_ClimbingWall_Ready*)message->netMsg; + return gmMsg->localOnly; + } + return true; // safe-guard so we don't screw up other's state if the python does something stupid +} + +/////////////////////////////////////////////////////////////////////////////// + +pyClimbingWallBlockersChangedMsg::pyClimbingWallBlockersChangedMsg(): pyClimbingWallMsg() {} + +pyClimbingWallBlockersChangedMsg::pyClimbingWallBlockersChangedMsg(pfGameCliMsg* msg): pyClimbingWallMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_ClimbingWall_BlockersChanged)) + message = nil; // wrong type, just clear it out +} + +int pyClimbingWallBlockersChangedMsg::TeamNumber() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_BlockersChanged* gmMsg = (const Srv2Cli_ClimbingWall_BlockersChanged*)message->netMsg; + return gmMsg->teamNumber; + } + return 0; +} + +std::vector pyClimbingWallBlockersChangedMsg::BlockersSet() const +{ + std::vector retVal; + if (message) + { + const Srv2Cli_ClimbingWall_BlockersChanged* gmMsg = (const Srv2Cli_ClimbingWall_BlockersChanged*)message->netMsg; + for (unsigned i = 0; i < kClimbingWallMaxBlockers; ++i) + { + if (gmMsg->blockersSet[i] != kClimbingWallNoBlocker) + retVal.push_back(gmMsg->blockersSet[i]); + } + } + return retVal; +} + +bool pyClimbingWallBlockersChangedMsg::LocalOnly() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_BlockersChanged* gmMsg = (const Srv2Cli_ClimbingWall_BlockersChanged*)message->netMsg; + return gmMsg->localOnly; + } + return true; // safe-guard so we don't screw up other's state if the python does something stupid +} + +/////////////////////////////////////////////////////////////////////////////// + +pyClimbingWallPlayerEnteredMsg::pyClimbingWallPlayerEnteredMsg(): pyClimbingWallMsg() {} + +pyClimbingWallPlayerEnteredMsg::pyClimbingWallPlayerEnteredMsg(pfGameCliMsg* msg): pyClimbingWallMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_ClimbingWall_PlayerEntered)) + message = nil; // wrong type, just clear it out +} + +/////////////////////////////////////////////////////////////////////////////// + +pyClimbingWallSuitMachineLockedMsg::pyClimbingWallSuitMachineLockedMsg(): pyClimbingWallMsg() {} + +pyClimbingWallSuitMachineLockedMsg::pyClimbingWallSuitMachineLockedMsg(pfGameCliMsg* msg): pyClimbingWallMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_ClimbingWall_SuitMachineLocked)) + message = nil; // wrong type, just clear it out +} + +bool pyClimbingWallSuitMachineLockedMsg::Team1MachineLocked() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_SuitMachineLocked* gmMsg = (const Srv2Cli_ClimbingWall_SuitMachineLocked*)message->netMsg; + return gmMsg->team1MachineLocked; + } + return true; // err on the side of caution +} + +bool pyClimbingWallSuitMachineLockedMsg::Team2MachineLocked() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_SuitMachineLocked* gmMsg = (const Srv2Cli_ClimbingWall_SuitMachineLocked*)message->netMsg; + return gmMsg->team2MachineLocked; + } + return true; // err on the side of caution +} + +bool pyClimbingWallSuitMachineLockedMsg::LocalOnly() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_SuitMachineLocked* gmMsg = (const Srv2Cli_ClimbingWall_SuitMachineLocked*)message->netMsg; + return gmMsg->localOnly; + } + return true; // safe-guard so we don't screw up other's state if the python does something stupid +} + +/////////////////////////////////////////////////////////////////////////////// + +pyClimbingWallGameOverMsg::pyClimbingWallGameOverMsg(): pyClimbingWallMsg() {} + +pyClimbingWallGameOverMsg::pyClimbingWallGameOverMsg(pfGameCliMsg* msg): pyClimbingWallMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_ClimbingWall_GameOver)) + message = nil; // wrong type, just clear it out +} + +int pyClimbingWallGameOverMsg::TeamWon() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_GameOver* gmMsg = (const Srv2Cli_ClimbingWall_GameOver*)message->netMsg; + return gmMsg->teamWon; + } + return 0; +} + +std::vector pyClimbingWallGameOverMsg::Team1Blockers() const +{ + std::vector retVal; + if (message) + { + const Srv2Cli_ClimbingWall_GameOver* gmMsg = (const Srv2Cli_ClimbingWall_GameOver*)message->netMsg; + for (unsigned i = 0; i < kClimbingWallMaxBlockers; ++i) + { + if (gmMsg->team1Blockers[i] != kClimbingWallNoBlocker) + retVal.push_back(gmMsg->team1Blockers[i]); + } + } + return retVal; +} + +std::vector pyClimbingWallGameOverMsg::Team2Blockers() const +{ + std::vector retVal; + if (message) + { + const Srv2Cli_ClimbingWall_GameOver* gmMsg = (const Srv2Cli_ClimbingWall_GameOver*)message->netMsg; + for (unsigned i = 0; i < kClimbingWallMaxBlockers; ++i) + { + if (gmMsg->team2Blockers[i] != kClimbingWallNoBlocker) + retVal.push_back(gmMsg->team2Blockers[i]); + } + } + return retVal; +} + +bool pyClimbingWallGameOverMsg::LocalOnly() const +{ + if (message) + { + const Srv2Cli_ClimbingWall_GameOver* gmMsg = (const Srv2Cli_ClimbingWall_GameOver*)message->netMsg; + return gmMsg->localOnly; + } + return true; // safe-guard so we don't screw up other's state if the python does something stupid +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsg.h new file mode 100644 index 00000000..6d1ebe87 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsg.h @@ -0,0 +1,187 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyClimbingWallMsg_h +#define pyClimbingWallMsg_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyClimbingWallMsg +// +// PURPOSE: Class wrapper for ClimbingWall game messages +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCliMsg.h" + +class pyClimbingWallMsg : public pyGameCliMsg +{ +protected: + pyClimbingWallMsg(); + pyClimbingWallMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptClimbingWallMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyClimbingWallMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyClimbingWallMsg); // converts a PyObject to a pyClimbingWallMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + + int GetClimbingWallMsgType() const; + + PyObject* UpcastToFinalClimbingWallMsg() const; // returns the climbing wall message that this really is +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyClimbingWallNumBlockersChangedMsg : public pyClimbingWallMsg +{ +protected: + pyClimbingWallNumBlockersChangedMsg(); + pyClimbingWallNumBlockersChangedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptClimbingWallNumBlockersChangedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyClimbingWallNumBlockersChangedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyClimbingWallNumBlockersChangedMsg); // converts a PyObject to a pyClimbingWallNumBlockersChangedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + int NewBlockerCount() const; + bool LocalOnly() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyClimbingWallReadyMsg : public pyClimbingWallMsg +{ +protected: + pyClimbingWallReadyMsg(); + pyClimbingWallReadyMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptClimbingWallReadyMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyClimbingWallReadyMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyClimbingWallReadyMsg); // converts a PyObject to a pyClimbingWallReadyMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + int ReadyType() const; + bool Team1Ready() const; + bool Team2Ready() const; + bool LocalOnly() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyClimbingWallBlockersChangedMsg : public pyClimbingWallMsg +{ +protected: + pyClimbingWallBlockersChangedMsg(); + pyClimbingWallBlockersChangedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptClimbingWallBlockersChangedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyClimbingWallBlockersChangedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyClimbingWallBlockersChangedMsg); // converts a PyObject to a pyClimbingWallBlockersChangedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + int TeamNumber() const; + std::vector BlockersSet() const; + bool LocalOnly() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyClimbingWallPlayerEnteredMsg : public pyClimbingWallMsg +{ +protected: + pyClimbingWallPlayerEnteredMsg(); + pyClimbingWallPlayerEnteredMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptClimbingWallPlayerEnteredMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyClimbingWallPlayerEnteredMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyClimbingWallPlayerEnteredMsg); // converts a PyObject to a pyClimbingWallPlayerEnteredMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyClimbingWallSuitMachineLockedMsg : public pyClimbingWallMsg +{ +protected: + pyClimbingWallSuitMachineLockedMsg(); + pyClimbingWallSuitMachineLockedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptClimbingWallSuitMachineLockedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyClimbingWallSuitMachineLockedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyClimbingWallSuitMachineLockedMsg); // converts a PyObject to a pyClimbingWallSuitMachineLockedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + bool Team1MachineLocked() const; + bool Team2MachineLocked() const; + bool LocalOnly() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyClimbingWallGameOverMsg : public pyClimbingWallMsg +{ +protected: + pyClimbingWallGameOverMsg(); + pyClimbingWallGameOverMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptClimbingWallGameOverMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyClimbingWallGameOverMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyClimbingWallGameOverMsg); // converts a PyObject to a pyClimbingWallGameOverMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + int TeamWon() const; + std::vector Team1Blockers() const; + std::vector Team2Blockers() const; + bool LocalOnly() const; +}; + +#endif // pyClimbingWallMsg_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsgGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsgGlue.cpp new file mode 100644 index 00000000..a4744707 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/ClimbingWall/pyClimbingWallMsgGlue.cpp @@ -0,0 +1,413 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyClimbingWallMsg.h" +#include "../../pyEnum.h" + +#include + +/////////////////////////////////////////////////////////////////////////////// +// +// Base climbing wall msg class +// + +PYTHON_CLASS_DEFINITION(ptClimbingWallMsg, pyClimbingWallMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptClimbingWallMsg, pyClimbingWallMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptClimbingWallMsg) + +PYTHON_NO_INIT_DEFINITION(ptClimbingWallMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallMsg, getClimbingWallMsgType) +{ + return PyInt_FromLong(self->fThis->GetClimbingWallMsgType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallMsg, upcastToFinalClimbingWallMsg) +{ + return self->fThis->UpcastToFinalClimbingWallMsg(); +} + +PYTHON_START_METHODS_TABLE(ptClimbingWallMsg) + PYTHON_METHOD_NOARGS(ptClimbingWallMsg, getClimbingWallMsgType, "Returns the type of the ClimbingWall message (see PtClimbingWallMsgTypes)"), + PYTHON_METHOD_NOARGS(ptClimbingWallMsg, upcastToFinalClimbingWallMsg, "Returns this message as the ClimbingWall msg it is"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptClimbingWallMsg, pyGameCliMsg, "Base class for ClimbingWall game messages"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptClimbingWallMsg, pyClimbingWallMsg); + +// required functions for PyObject interoperability +PyObject* pyClimbingWallMsg::New(pfGameCliMsg* msg) +{ + ptClimbingWallMsg *newObj = (ptClimbingWallMsg*)ptClimbingWallMsg_type.tp_new(&ptClimbingWallMsg_type, NULL, NULL); + if (msg && (msg->gameCli->GetGameTypeId() == kGameTypeId_ClimbingWall)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptClimbingWallMsg, pyClimbingWallMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptClimbingWallMsg, pyClimbingWallMsg) + +// Module and method definitions +void pyClimbingWallMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptClimbingWallMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyClimbingWallMsg::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtClimbingWallMsgTypes); + PYTHON_ENUM_ELEMENT(PtClimbingWallMsgTypes, kClimbingWallNumBlockersChanged, kSrv2Cli_ClimbingWall_NumBlockersChanged); + PYTHON_ENUM_ELEMENT(PtClimbingWallMsgTypes, kClimbingWallReadyMsg, kSrv2Cli_ClimbingWall_Ready); + PYTHON_ENUM_ELEMENT(PtClimbingWallMsgTypes, kClimbingWallBlockersChanged, kSrv2Cli_ClimbingWall_BlockersChanged); + PYTHON_ENUM_ELEMENT(PtClimbingWallMsgTypes, kClimbingWallPlayerEntered, kSrv2Cli_ClimbingWall_PlayerEntered); + PYTHON_ENUM_ELEMENT(PtClimbingWallMsgTypes, kClimbingWallSuitMachineLocked, kSrv2Cli_ClimbingWall_SuitMachineLocked); + PYTHON_ENUM_ELEMENT(PtClimbingWallMsgTypes, kClimbingWallGameOver, kSrv2Cli_ClimbingWall_GameOver); + PYTHON_ENUM_END(m, PtClimbingWallMsgTypes); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Game client message subclasses +// + +PYTHON_CLASS_DEFINITION(ptClimbingWallNumBlockersChangedMsg, pyClimbingWallNumBlockersChangedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptClimbingWallNumBlockersChangedMsg, pyClimbingWallNumBlockersChangedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptClimbingWallNumBlockersChangedMsg) + +PYTHON_NO_INIT_DEFINITION(ptClimbingWallNumBlockersChangedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallNumBlockersChangedMsg, newBlockerCount) +{ + return PyInt_FromLong((long)self->fThis->NewBlockerCount()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallNumBlockersChangedMsg, localOnly) +{ + PYTHON_RETURN_BOOL(self->fThis->LocalOnly()); +} + +PYTHON_START_METHODS_TABLE(ptClimbingWallNumBlockersChangedMsg) + PYTHON_METHOD_NOARGS(ptClimbingWallNumBlockersChangedMsg, newBlockerCount, "Returns the number of blockers this game is current running with"), + PYTHON_METHOD_NOARGS(ptClimbingWallNumBlockersChangedMsg, localOnly, "Returns true if we are only supposed to adjust our stuff locally, and not net-prop it"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptClimbingWallNumBlockersChangedMsg, pyClimbingWallMsg, "ClimbingWall message received when the number of blockers is changed"); + +// required functions for PyObject interoperability +PyObject* pyClimbingWallNumBlockersChangedMsg::New(pfGameCliMsg* msg) +{ + ptClimbingWallNumBlockersChangedMsg *newObj = (ptClimbingWallNumBlockersChangedMsg*)ptClimbingWallNumBlockersChangedMsg_type.tp_new(&ptClimbingWallNumBlockersChangedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_ClimbingWall_NumBlockersChanged)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptClimbingWallNumBlockersChangedMsg, pyClimbingWallNumBlockersChangedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptClimbingWallNumBlockersChangedMsg, pyClimbingWallNumBlockersChangedMsg) + +// Module and method definitions +void pyClimbingWallNumBlockersChangedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptClimbingWallNumBlockersChangedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptClimbingWallReadyMsg, pyClimbingWallReadyMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptClimbingWallReadyMsg, pyClimbingWallReadyMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptClimbingWallReadyMsg) + +PYTHON_NO_INIT_DEFINITION(ptClimbingWallReadyMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallReadyMsg, readyType) +{ + return PyInt_FromLong((long)self->fThis->ReadyType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallReadyMsg, team1Ready) +{ + PYTHON_RETURN_BOOL(self->fThis->Team1Ready()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallReadyMsg, team2Ready) +{ + PYTHON_RETURN_BOOL(self->fThis->Team2Ready()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallReadyMsg, localOnly) +{ + PYTHON_RETURN_BOOL(self->fThis->LocalOnly()); +} + +PYTHON_START_METHODS_TABLE(ptClimbingWallReadyMsg) + PYTHON_METHOD_NOARGS(ptClimbingWallReadyMsg, readyType, "The type of ready message this represents (see PtClimbingWallReadyTypes)"), + PYTHON_METHOD_NOARGS(ptClimbingWallReadyMsg, team1Ready, "Whether team 1 is ready or not"), + PYTHON_METHOD_NOARGS(ptClimbingWallReadyMsg, team2Ready, "Whether team 2 is ready or not"), + PYTHON_METHOD_NOARGS(ptClimbingWallReadyMsg, localOnly, "Returns true if we are only supposed to adjust our stuff locally, and not net-prop it"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptClimbingWallReadyMsg, pyClimbingWallMsg, "ClimbingWall message received when the ready state of the teams is changed"); + +// required functions for PyObject interoperability +PyObject* pyClimbingWallReadyMsg::New(pfGameCliMsg* msg) +{ + ptClimbingWallReadyMsg *newObj = (ptClimbingWallReadyMsg*)ptClimbingWallReadyMsg_type.tp_new(&ptClimbingWallReadyMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_ClimbingWall_Ready)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptClimbingWallReadyMsg, pyClimbingWallReadyMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptClimbingWallReadyMsg, pyClimbingWallReadyMsg) + +// Module and method definitions +void pyClimbingWallReadyMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptClimbingWallReadyMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptClimbingWallBlockersChangedMsg, pyClimbingWallBlockersChangedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptClimbingWallBlockersChangedMsg, pyClimbingWallBlockersChangedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptClimbingWallBlockersChangedMsg) + +PYTHON_NO_INIT_DEFINITION(ptClimbingWallBlockersChangedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallBlockersChangedMsg, teamNumber) +{ + return PyInt_FromLong((long)self->fThis->TeamNumber()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallBlockersChangedMsg, blockersSet) +{ + std::vector blockers = self->fThis->BlockersSet(); + PyObject* retVal = PyList_New(blockers.size()); + for (unsigned i = 0; i < blockers.size(); ++i) + PyList_SetItem(retVal, i, PyInt_FromLong(blockers[i])); // steals the ref + return retVal; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallBlockersChangedMsg, localOnly) +{ + PYTHON_RETURN_BOOL(self->fThis->LocalOnly()); +} + +PYTHON_START_METHODS_TABLE(ptClimbingWallBlockersChangedMsg) + PYTHON_METHOD_NOARGS(ptClimbingWallBlockersChangedMsg, teamNumber, "The team that this message is for"), + PYTHON_METHOD_NOARGS(ptClimbingWallBlockersChangedMsg, blockersSet, "Returns an array of blocker indicies denoting which blockers are set"), + PYTHON_METHOD_NOARGS(ptClimbingWallBlockersChangedMsg, localOnly, "Returns true if we are only supposed to adjust our stuff locally, and not net-prop it"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptClimbingWallBlockersChangedMsg, pyClimbingWallMsg, "ClimbingWall message received when the blocker state changes"); + +// required functions for PyObject interoperability +PyObject* pyClimbingWallBlockersChangedMsg::New(pfGameCliMsg* msg) +{ + ptClimbingWallBlockersChangedMsg *newObj = (ptClimbingWallBlockersChangedMsg*)ptClimbingWallBlockersChangedMsg_type.tp_new(&ptClimbingWallBlockersChangedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_ClimbingWall_BlockersChanged)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptClimbingWallBlockersChangedMsg, pyClimbingWallBlockersChangedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptClimbingWallBlockersChangedMsg, pyClimbingWallBlockersChangedMsg) + +// Module and method definitions +void pyClimbingWallBlockersChangedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptClimbingWallBlockersChangedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptClimbingWallPlayerEnteredMsg, pyClimbingWallPlayerEnteredMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptClimbingWallPlayerEnteredMsg, pyClimbingWallPlayerEnteredMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptClimbingWallPlayerEnteredMsg) + +PYTHON_NO_INIT_DEFINITION(ptClimbingWallPlayerEnteredMsg) + +PYTHON_START_METHODS_TABLE(ptClimbingWallPlayerEnteredMsg) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptClimbingWallPlayerEnteredMsg, pyClimbingWallMsg, "ClimbingWall message received when you successfully enter the suit machine"); + +// required functions for PyObject interoperability +PyObject* pyClimbingWallPlayerEnteredMsg::New(pfGameCliMsg* msg) +{ + ptClimbingWallPlayerEnteredMsg *newObj = (ptClimbingWallPlayerEnteredMsg*)ptClimbingWallPlayerEnteredMsg_type.tp_new(&ptClimbingWallPlayerEnteredMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_ClimbingWall_PlayerEntered)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptClimbingWallPlayerEnteredMsg, pyClimbingWallPlayerEnteredMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptClimbingWallPlayerEnteredMsg, pyClimbingWallPlayerEnteredMsg) + +// Module and method definitions +void pyClimbingWallPlayerEnteredMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptClimbingWallPlayerEnteredMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptClimbingWallSuitMachineLockedMsg, pyClimbingWallSuitMachineLockedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptClimbingWallSuitMachineLockedMsg, pyClimbingWallSuitMachineLockedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptClimbingWallSuitMachineLockedMsg) + +PYTHON_NO_INIT_DEFINITION(ptClimbingWallSuitMachineLockedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallSuitMachineLockedMsg, team1MachineLocked) +{ + PYTHON_RETURN_BOOL(self->fThis->Team1MachineLocked()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallSuitMachineLockedMsg, team2MachineLocked) +{ + PYTHON_RETURN_BOOL(self->fThis->Team2MachineLocked()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallSuitMachineLockedMsg, localOnly) +{ + PYTHON_RETURN_BOOL(self->fThis->LocalOnly()); +} + +PYTHON_START_METHODS_TABLE(ptClimbingWallSuitMachineLockedMsg) + PYTHON_METHOD_NOARGS(ptClimbingWallSuitMachineLockedMsg, team1MachineLocked, "Whether team 1's suit machine is locked or not"), + PYTHON_METHOD_NOARGS(ptClimbingWallSuitMachineLockedMsg, team2MachineLocked, "Whether team 2's suit machine is locked or not"), + PYTHON_METHOD_NOARGS(ptClimbingWallSuitMachineLockedMsg, localOnly, "Returns true if we are only supposed to adjust our stuff locally, and not net-prop it"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptClimbingWallSuitMachineLockedMsg, pyClimbingWallMsg, "ClimbingWall message received when the locked state of the suit machines is changed"); + +// required functions for PyObject interoperability +PyObject* pyClimbingWallSuitMachineLockedMsg::New(pfGameCliMsg* msg) +{ + ptClimbingWallSuitMachineLockedMsg *newObj = (ptClimbingWallSuitMachineLockedMsg*)ptClimbingWallSuitMachineLockedMsg_type.tp_new(&ptClimbingWallSuitMachineLockedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_ClimbingWall_SuitMachineLocked)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptClimbingWallSuitMachineLockedMsg, pyClimbingWallSuitMachineLockedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptClimbingWallSuitMachineLockedMsg, pyClimbingWallSuitMachineLockedMsg) + +// Module and method definitions +void pyClimbingWallSuitMachineLockedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptClimbingWallSuitMachineLockedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptClimbingWallGameOverMsg, pyClimbingWallGameOverMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptClimbingWallGameOverMsg, pyClimbingWallGameOverMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptClimbingWallGameOverMsg) + +PYTHON_NO_INIT_DEFINITION(ptClimbingWallGameOverMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallGameOverMsg, teamWon) +{ + return PyInt_FromLong((long)self->fThis->TeamWon()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallGameOverMsg, team1Blockers) +{ + std::vector blockers = self->fThis->Team1Blockers(); + PyObject* retVal = PyList_New(blockers.size()); + for (unsigned i = 0; i < blockers.size(); ++i) + PyList_SetItem(retVal, i, PyInt_FromLong(blockers[i])); // steals the ref + return retVal; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallGameOverMsg, team2Blockers) +{ + std::vector blockers = self->fThis->Team2Blockers(); + PyObject* retVal = PyList_New(blockers.size()); + for (unsigned i = 0; i < blockers.size(); ++i) + PyList_SetItem(retVal, i, PyInt_FromLong(blockers[i])); // steals the ref + return retVal; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptClimbingWallGameOverMsg, localOnly) +{ + PYTHON_RETURN_BOOL(self->fThis->LocalOnly()); +} + +PYTHON_START_METHODS_TABLE(ptClimbingWallGameOverMsg) + PYTHON_METHOD_NOARGS(ptClimbingWallGameOverMsg, teamWon, "The team that won the game"), + PYTHON_METHOD_NOARGS(ptClimbingWallGameOverMsg, team1Blockers, "Returns an array of blocker indicies denoting which blockers team 1 set"), + PYTHON_METHOD_NOARGS(ptClimbingWallGameOverMsg, team2Blockers, "Returns an array of blocker indicies denoting which blockers team 2 set"), + PYTHON_METHOD_NOARGS(ptClimbingWallGameOverMsg, localOnly, "Returns true if we are only supposed to adjust our stuff locally, and not net-prop it"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptClimbingWallGameOverMsg, pyClimbingWallMsg, "ClimbingWall message received when the game is over"); + +// required functions for PyObject interoperability +PyObject* pyClimbingWallGameOverMsg::New(pfGameCliMsg* msg) +{ + ptClimbingWallGameOverMsg *newObj = (ptClimbingWallGameOverMsg*)ptClimbingWallGameOverMsg_type.tp_new(&ptClimbingWallGameOverMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_ClimbingWall_GameOver)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptClimbingWallGameOverMsg, pyClimbingWallGameOverMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptClimbingWallGameOverMsg, pyClimbingWallGameOverMsg) + +// Module and method definitions +void pyClimbingWallGameOverMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptClimbingWallGameOverMsg); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGame.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGame.cpp new file mode 100644 index 00000000..e3e6b7cc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGame.cpp @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyHeekGame.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base Heek game client class +// + +pyHeekGame::pyHeekGame(): pyGameCli() {} + +pyHeekGame::pyHeekGame(pfGameCli* client): pyGameCli(client) +{ + if (client && (client->GetGameTypeId() != kGameTypeId_Heek)) + gameClient = nil; // wrong type, just clear it out +} + +bool pyHeekGame::IsHeekGame(std::wstring guid) +{ + Uuid gameUuid(guid.c_str()); + return gameUuid == kGameTypeId_Heek; +} + +void pyHeekGame::JoinCommonHeekGame(pyKey& callbackKey, unsigned gameID) +{ + pfGameMgr::GetInstance()->JoinCommonGame(callbackKey.getKey(), kGameTypeId_Heek, gameID, 0, NULL); +} + +void pyHeekGame::PlayGame(int position, UInt32 points, std::wstring name) +{ + if (gameClient) + { + pfGmHeek* heek = pfGmHeek::ConvertNoRef(gameClient); + heek->PlayGame((unsigned)position, (dword)points, name.c_str()); + } +} + +void pyHeekGame::LeaveGame() +{ + if (gameClient) + { + pfGmHeek* heek = pfGmHeek::ConvertNoRef(gameClient); + heek->LeaveGame(); + } +} + +void pyHeekGame::Choose(int choice) +{ + if (gameClient) + { + pfGmHeek* heek = pfGmHeek::ConvertNoRef(gameClient); + heek->Choose((EHeekChoice)choice); + } +} + +void pyHeekGame::SequenceFinished(int seq) +{ + if (gameClient) + { + pfGmHeek* heek = pfGmHeek::ConvertNoRef(gameClient); + heek->SequenceFinished((EHeekSeqFinished)seq); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGame.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGame.h new file mode 100644 index 00000000..8cdd54f8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGame.h @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyHeekGame_h +#define pyHeekGame_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyHeekGame +// +// PURPOSE: Class wrapper for the Heek game client +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCli.h" +#include "../../pyKey.h" + +class pyHeekGame : public pyGameCli +{ +protected: + pyHeekGame(); + pyHeekGame(pfGameCli* client); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekGame); + static PyObject* New(pfGameCli* client); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekGame object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekGame); // converts a PyObject to a pyHeekGame (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + static void AddPlasmaMethods(std::vector& methods); + + static bool IsHeekGame(std::wstring guid); + static void JoinCommonHeekGame(pyKey& callbackKey, unsigned gameID); + + void PlayGame(int position, UInt32 points, std::wstring name); + void LeaveGame(); + void Choose(int choice); + void SequenceFinished(int seq); +}; + +#endif // pyHeekGame_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGameGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGameGlue.cpp new file mode 100644 index 00000000..ee815cfb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekGameGlue.cpp @@ -0,0 +1,207 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyHeekGame.h" + +#include +#include "../../pyEnum.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base Heek game client class +// + +PYTHON_CLASS_DEFINITION(ptHeekGame, pyHeekGame); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekGame, pyHeekGame) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekGame) + +PYTHON_NO_INIT_DEFINITION(ptHeekGame) + +PYTHON_GLOBAL_METHOD_DEFINITION(PtIsHeekGame, args, "Params: typeID\nReturns true if the specifed typeID (guid as a string) is a Heek game") +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "PtIsHeekGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + bool retVal = pyHeekGame::IsHeekGame(text); + delete [] text; + PYTHON_RETURN_BOOL(retVal); + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(textObj); + wchar_t* wText = hsStringToWString(text); + bool retVal = pyHeekGame::IsHeekGame(wText); + delete [] wText; + PYTHON_RETURN_BOOL(retVal); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtIsHeekGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtJoinCommonHeekGame, args, "Params: callbackKey, gameID\nJoins a common Heek game with the specified ID. If one doesn't exist, it creates it") +{ + PyObject* callbackObj = NULL; + int gameID = 0; + if (!PyArg_ParseTuple(args, "Oi", &callbackObj, &gameID)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinCommonHeekGame expects a ptKey and an integer"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(callbackObj)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinCommonHeekGame expects a ptKey and an integer"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(callbackObj); + pyHeekGame::JoinCommonHeekGame(*key, gameID); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptHeekGame, playGame, args) +{ + int position = 0; + long points = 0; + PyObject* textObj = NULL; + if (!PyArg_ParseTuple(args,"ilO", &position, &points, &textObj)) + { + PyErr_SetString(PyExc_TypeError, "playGame expects an int, a long, and a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* temp = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, temp, strLen); + temp[strLen] = L'\0'; + self->fThis->PlayGame(position, points, temp); + delete [] temp; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* temp = PyString_AsString(textObj); + wchar_t* wTemp = hsStringToWString(temp); + self->fThis->PlayGame(position, points, wTemp); + delete [] wTemp; + PYTHON_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_TypeError, "playGame expects an int, a long, and a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_BASIC_METHOD_DEFINITION(ptHeekGame, leaveGame, LeaveGame) + +PYTHON_METHOD_DEFINITION(ptHeekGame, choose, args) +{ + int choice = 0; + if (!PyArg_ParseTuple(args, "i", &choice)) + { + PyErr_SetString(PyExc_TypeError, "choose expects an int"); + PYTHON_RETURN_ERROR; + } + self->fThis->Choose(choice); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptHeekGame, sequenceFinished, args) +{ + int seq = 0; + if (!PyArg_ParseTuple(args, "i", &seq)) + { + PyErr_SetString(PyExc_TypeError, "sequenceFinished expects an int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SequenceFinished(seq); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptHeekGame) + PYTHON_METHOD(ptHeekGame, playGame, "Params: position, points, name\nRequests to start playing the game in the specified position"), + PYTHON_BASIC_METHOD(ptHeekGame, leaveGame, "Leaves this game (puts us into \"observer\" mode"), + PYTHON_METHOD(ptHeekGame, choose, "Params: choice\nMakes the specified move (see PtHeekGameChoice)"), + PYTHON_METHOD(ptHeekGame, sequenceFinished, "Params: sequence\nTells the server that the specified animation finished (see PtHeekGameSeq)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekGame, pyGameCli, "Game client for the Heek game"); + +// required functions for PyObject interoperability +PyObject* pyHeekGame::New(pfGameCli* client) +{ + ptHeekGame *newObj = (ptHeekGame*)ptHeekGame_type.tp_new(&ptHeekGame_type, NULL, NULL); + if (client && (client->GetGameTypeId() == kGameTypeId_Heek)) + newObj->fThis->gameClient = client; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekGame, pyHeekGame) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekGame, pyHeekGame) + +// Module and method definitions +void pyHeekGame::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekGame); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyHeekGame::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtHeekGameChoice); + PYTHON_ENUM_ELEMENT(PtHeekGameChoice, kHeekGameChoiceRock, kHeekRock); + PYTHON_ENUM_ELEMENT(PtHeekGameChoice, kHeekGameChoicePaper, kHeekPaper); + PYTHON_ENUM_ELEMENT(PtHeekGameChoice, kHeekGameChoiceScissors, kHeekScissors); + PYTHON_ENUM_END(m, PtHeekGameChoice); + + PYTHON_ENUM_START(PtHeekGameSeq); + PYTHON_ENUM_ELEMENT(PtHeekGameSeq, kHeekGameSeqCountdown, kHeekCountdownSeq); + PYTHON_ENUM_ELEMENT(PtHeekGameSeq, kHeekGameSeqChoiceAnim, kHeekChoiceAnimSeq); + PYTHON_ENUM_ELEMENT(PtHeekGameSeq, kHeekGameSeqGameWinAnim, kHeekGameWinAnimSeq); + PYTHON_ENUM_END(m, PtHeekGameSeq); +} + +void pyHeekGame::AddPlasmaMethods(std::vector& methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtIsHeekGame); + PYTHON_GLOBAL_METHOD(methods, PtJoinCommonHeekGame); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsg.cpp new file mode 100644 index 00000000..3ab3e6b5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsg.cpp @@ -0,0 +1,386 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyHeekMsg.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base Heek msg class +// + +pyHeekMsg::pyHeekMsg(): pyGameCliMsg() {} + +pyHeekMsg::pyHeekMsg(pfGameCliMsg* msg): pyGameCliMsg(msg) +{ + if (message && (message->gameCli->GetGameTypeId() != kGameTypeId_Heek)) + message = nil; // wrong type, just clear it out +} + +int pyHeekMsg::GetHeekMsgType() const +{ + if (message) + return message->netMsg->messageId; + return -1; +} + +PyObject* pyHeekMsg::UpcastToFinalHeekMsg() const +{ + if (!message) + PYTHON_RETURN_NONE; + switch (message->netMsg->messageId) + { + case kSrv2Cli_Heek_PlayGame: + return pyHeekPlayGameMsg::New(message); + case kSrv2Cli_Heek_Goodbye: + return pyHeekGoodbyeMsg::New(message); + case kSrv2Cli_Heek_Welcome: + return pyHeekWelcomeMsg::New(message); + case kSrv2Cli_Heek_Drop: + return pyHeekDropMsg::New(message); + case kSrv2Cli_Heek_Setup: + return pyHeekSetupMsg::New(message); + case kSrv2Cli_Heek_LightState: + return pyHeekLightStateMsg::New(message); + case kSrv2Cli_Heek_InterfaceState: + return pyHeekInterfaceStateMsg::New(message); + case kSrv2Cli_Heek_CountdownState: + return pyHeekCountdownStateMsg::New(message); + case kSrv2Cli_Heek_WinLose: + return pyHeekWinLoseMsg::New(message); + case kSrv2Cli_Heek_GameWin: + return pyHeekGameWinMsg::New(message); + case kSrv2Cli_Heek_PointUpdate: + return pyHeekPointUpdateMsg::New(message); + default: + PYTHON_RETURN_NONE; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// The different messages we can receive +// + +pyHeekPlayGameMsg::pyHeekPlayGameMsg(): pyHeekMsg() {} + +pyHeekPlayGameMsg::pyHeekPlayGameMsg(pfGameCliMsg* msg): pyHeekMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Heek_PlayGame)) + message = nil; // wrong type, just clear it out +} + +bool pyHeekPlayGameMsg::IsPlaying() const +{ + if (message) + { + const Srv2Cli_Heek_PlayGame* gmMsg = (const Srv2Cli_Heek_PlayGame*)message->netMsg; + return gmMsg->isPlaying; + } + return false; +} + +bool pyHeekPlayGameMsg::IsSinglePlayer() const +{ + if (message) + { + const Srv2Cli_Heek_PlayGame* gmMsg = (const Srv2Cli_Heek_PlayGame*)message->netMsg; + return gmMsg->isSinglePlayer; + } + return false; +} + +bool pyHeekPlayGameMsg::EnableButtons() const +{ + if (message) + { + const Srv2Cli_Heek_PlayGame* gmMsg = (const Srv2Cli_Heek_PlayGame*)message->netMsg; + return gmMsg->enableButtons; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +pyHeekGoodbyeMsg::pyHeekGoodbyeMsg(): pyHeekMsg() {} + +pyHeekGoodbyeMsg::pyHeekGoodbyeMsg(pfGameCliMsg* msg): pyHeekMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Heek_Goodbye)) + message = nil; // wrong type, just clear it out +} + +/////////////////////////////////////////////////////////////////////////////// +pyHeekWelcomeMsg::pyHeekWelcomeMsg(): pyHeekMsg() {} + +pyHeekWelcomeMsg::pyHeekWelcomeMsg(pfGameCliMsg* msg): pyHeekMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Heek_Welcome)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyHeekWelcomeMsg::Points() const +{ + if (message) + { + const Srv2Cli_Heek_Welcome* gmMsg = (const Srv2Cli_Heek_Welcome*)message->netMsg; + return gmMsg->points; + } + return 0; +} + +unsigned long pyHeekWelcomeMsg::Rank() const +{ + if (message) + { + const Srv2Cli_Heek_Welcome* gmMsg = (const Srv2Cli_Heek_Welcome*)message->netMsg; + return gmMsg->rank; + } + return 0; +} + +std::wstring pyHeekWelcomeMsg::Name() const +{ + if (message) + { + const Srv2Cli_Heek_Welcome* gmMsg = (const Srv2Cli_Heek_Welcome*)message->netMsg; + return gmMsg->name; + } + return L""; +} + +/////////////////////////////////////////////////////////////////////////////// +pyHeekDropMsg::pyHeekDropMsg(): pyHeekMsg() {} + +pyHeekDropMsg::pyHeekDropMsg(pfGameCliMsg* msg): pyHeekMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Heek_Drop)) + message = nil; // wrong type, just clear it out +} + +int pyHeekDropMsg::Position() const +{ + if (message) + { + const Srv2Cli_Heek_Drop* gmMsg = (const Srv2Cli_Heek_Drop*)message->netMsg; + return gmMsg->position; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +pyHeekSetupMsg::pyHeekSetupMsg(): pyHeekMsg() {} + +pyHeekSetupMsg::pyHeekSetupMsg(pfGameCliMsg* msg): pyHeekMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Heek_Welcome)) + message = nil; // wrong type, just clear it out +} + +int pyHeekSetupMsg::Position() const +{ + if (message) + { + const Srv2Cli_Heek_Setup* gmMsg = (const Srv2Cli_Heek_Setup*)message->netMsg; + return gmMsg->position; + } + return -1; +} + +bool pyHeekSetupMsg::ButtonState() const +{ + if (message) + { + const Srv2Cli_Heek_Setup* gmMsg = (const Srv2Cli_Heek_Setup*)message->netMsg; + return gmMsg->buttonState; + } + return false; +} + +std::vector pyHeekSetupMsg::LightOn() const +{ + std::vector retVal; + if (message) + { + const Srv2Cli_Heek_Setup* gmMsg = (const Srv2Cli_Heek_Setup*)message->netMsg; + int numLights = arrsize(gmMsg->lightOn); + for (int i = 0; i < numLights; ++i) + retVal.push_back(gmMsg->lightOn[i]); + return retVal; + } + return retVal; +} + +/////////////////////////////////////////////////////////////////////////////// +pyHeekLightStateMsg::pyHeekLightStateMsg(): pyHeekMsg() {} + +pyHeekLightStateMsg::pyHeekLightStateMsg(pfGameCliMsg* msg): pyHeekMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Heek_LightState)) + message = nil; // wrong type, just clear it out +} + +int pyHeekLightStateMsg::LightNum() const +{ + if (message) + { + const Srv2Cli_Heek_LightState* gmMsg = (const Srv2Cli_Heek_LightState*)message->netMsg; + return gmMsg->lightNum; + } + return -1; +} + +int pyHeekLightStateMsg::State() const +{ + if (message) + { + const Srv2Cli_Heek_LightState* gmMsg = (const Srv2Cli_Heek_LightState*)message->netMsg; + return gmMsg->state; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +pyHeekInterfaceStateMsg::pyHeekInterfaceStateMsg(): pyHeekMsg() {} + +pyHeekInterfaceStateMsg::pyHeekInterfaceStateMsg(pfGameCliMsg* msg): pyHeekMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Heek_InterfaceState)) + message = nil; // wrong type, just clear it out +} + +bool pyHeekInterfaceStateMsg::ButtonsEnabled() const +{ + if (message) + { + const Srv2Cli_Heek_InterfaceState* gmMsg = (const Srv2Cli_Heek_InterfaceState*)message->netMsg; + return gmMsg->buttonsEnabled; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +pyHeekCountdownStateMsg::pyHeekCountdownStateMsg(): pyHeekMsg() {} + +pyHeekCountdownStateMsg::pyHeekCountdownStateMsg(pfGameCliMsg* msg): pyHeekMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Heek_CountdownState)) + message = nil; // wrong type, just clear it out +} + +int pyHeekCountdownStateMsg::State() const +{ + if (message) + { + const Srv2Cli_Heek_CountdownState* gmMsg = (const Srv2Cli_Heek_CountdownState*)message->netMsg; + return gmMsg->state; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +pyHeekWinLoseMsg::pyHeekWinLoseMsg(): pyHeekMsg() {} + +pyHeekWinLoseMsg::pyHeekWinLoseMsg(pfGameCliMsg* msg): pyHeekMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Heek_WinLose)) + message = nil; // wrong type, just clear it out +} + +bool pyHeekWinLoseMsg::Win() const +{ + if (message) + { + const Srv2Cli_Heek_WinLose* gmMsg = (const Srv2Cli_Heek_WinLose*)message->netMsg; + return gmMsg->win; + } + return false; +} + +int pyHeekWinLoseMsg::Choice() const +{ + if (message) + { + const Srv2Cli_Heek_WinLose* gmMsg = (const Srv2Cli_Heek_WinLose*)message->netMsg; + return gmMsg->choice; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +pyHeekGameWinMsg::pyHeekGameWinMsg(): pyHeekMsg() {} + +pyHeekGameWinMsg::pyHeekGameWinMsg(pfGameCliMsg* msg): pyHeekMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Heek_GameWin)) + message = nil; // wrong type, just clear it out +} + +int pyHeekGameWinMsg::Choice() const +{ + if (message) + { + const Srv2Cli_Heek_GameWin* gmMsg = (const Srv2Cli_Heek_GameWin*)message->netMsg; + return gmMsg->choice; + } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +pyHeekPointUpdateMsg::pyHeekPointUpdateMsg(): pyHeekMsg() {} + +pyHeekPointUpdateMsg::pyHeekPointUpdateMsg(pfGameCliMsg* msg): pyHeekMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Heek_PointUpdate)) + message = nil; // wrong type, just clear it out +} + +bool pyHeekPointUpdateMsg::DisplayUpdate() const +{ + if (message) + { + const Srv2Cli_Heek_PointUpdate* gmMsg = (const Srv2Cli_Heek_PointUpdate*)message->netMsg; + return gmMsg->displayUpdate; + } + return false; +} + +unsigned long pyHeekPointUpdateMsg::Points() const +{ + if (message) + { + const Srv2Cli_Heek_PointUpdate* gmMsg = (const Srv2Cli_Heek_PointUpdate*)message->netMsg; + return gmMsg->points; + } + return 0; +} + +unsigned long pyHeekPointUpdateMsg::Rank() const +{ + if (message) + { + const Srv2Cli_Heek_PointUpdate* gmMsg = (const Srv2Cli_Heek_PointUpdate*)message->netMsg; + return gmMsg->rank; + } + return 0; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsg.h new file mode 100644 index 00000000..6fd8c229 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsg.h @@ -0,0 +1,283 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyHeekMsg_h +#define pyHeekMsg_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyHeekMsg +// +// PURPOSE: Class wrapper for Heek game messages +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCliMsg.h" + +class pyHeekMsg : public pyGameCliMsg +{ +protected: + pyHeekMsg(); + pyHeekMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptHeekMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekMsg); // converts a PyObject to a pyHeekMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + + int GetHeekMsgType() const; + + PyObject* UpcastToFinalHeekMsg() const; // returns the heek message this really is +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyHeekPlayGameMsg : public pyHeekMsg +{ +protected: + pyHeekPlayGameMsg(); + pyHeekPlayGameMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekPlayGameMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekPlayGameMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekPlayGameMsg); // converts a PyObject to a pyHeekPlayGameMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + bool IsPlaying() const; + bool IsSinglePlayer() const; + bool EnableButtons() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyHeekGoodbyeMsg : public pyHeekMsg +{ +protected: + pyHeekGoodbyeMsg(); + pyHeekGoodbyeMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekGoodbyeMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekGoodbyeMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekGoodbyeMsg); // converts a PyObject to a pyHeekGoodbyeMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyHeekWelcomeMsg : public pyHeekMsg +{ +protected: + pyHeekWelcomeMsg(); + pyHeekWelcomeMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekWelcomeMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekWelcomeMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekWelcomeMsg); // converts a PyObject to a pyHeekWelcomeMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long Points() const; + unsigned long Rank() const; + std::wstring Name() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyHeekDropMsg : public pyHeekMsg +{ +protected: + pyHeekDropMsg(); + pyHeekDropMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekDropMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekDropMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekDropMsg); // converts a PyObject to a pyHeekDropMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + int Position() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyHeekSetupMsg : public pyHeekMsg +{ +protected: + pyHeekSetupMsg(); + pyHeekSetupMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekSetupMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekSetupMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekSetupMsg); // converts a PyObject to a pyHeekSetupMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + int Position() const; + bool ButtonState() const; + std::vector LightOn() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyHeekLightStateMsg : public pyHeekMsg +{ +protected: + pyHeekLightStateMsg(); + pyHeekLightStateMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekLightStateMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekLightStateMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekLightStateMsg); // converts a PyObject to a pyHeekLightStateMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + + int LightNum() const; + int State() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyHeekInterfaceStateMsg : public pyHeekMsg +{ +protected: + pyHeekInterfaceStateMsg(); + pyHeekInterfaceStateMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekInterfaceStateMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekInterfaceStateMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekInterfaceStateMsg); // converts a PyObject to a pyHeekInterfaceStateMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + bool ButtonsEnabled() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyHeekCountdownStateMsg : public pyHeekMsg +{ +protected: + pyHeekCountdownStateMsg(); + pyHeekCountdownStateMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekCountdownStateMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekCountdownStateMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekCountdownStateMsg); // converts a PyObject to a pyHeekCountdownStateMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + + int State() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyHeekWinLoseMsg : public pyHeekMsg +{ +protected: + pyHeekWinLoseMsg(); + pyHeekWinLoseMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekWinLoseMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekWinLoseMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekWinLoseMsg); // converts a PyObject to a pyHeekWinLoseMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + bool Win() const; + int Choice() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyHeekGameWinMsg : public pyHeekMsg +{ +protected: + pyHeekGameWinMsg(); + pyHeekGameWinMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekGameWinMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekGameWinMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekGameWinMsg); // converts a PyObject to a pyHeekGameWinMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + int Choice() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyHeekPointUpdateMsg : public pyHeekMsg +{ +protected: + pyHeekPointUpdateMsg(); + pyHeekPointUpdateMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptHeekPointUpdateMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyHeekPointUpdateMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyHeekPointUpdateMsg); // converts a PyObject to a pyHeekPointUpdateMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + bool DisplayUpdate() const; + unsigned long Points() const; + unsigned long Rank() const; +}; + +#endif // pyHeekMsg_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsgGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsgGlue.cpp new file mode 100644 index 00000000..ae6235f5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Heek/pyHeekMsgGlue.cpp @@ -0,0 +1,618 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyHeekMsg.h" +#include "../../pyEnum.h" + +#include + +/////////////////////////////////////////////////////////////////////////////// +// +// Base Heek msg class +// + +PYTHON_CLASS_DEFINITION(ptHeekMsg, pyHeekMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekMsg, pyHeekMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekMsg, getHeekMsgType) +{ + return PyInt_FromLong(self->fThis->GetHeekMsgType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekMsg, upcastToFinalHeekMsg) +{ + return self->fThis->UpcastToFinalHeekMsg(); +} + +PYTHON_START_METHODS_TABLE(ptHeekMsg) + PYTHON_METHOD_NOARGS(ptHeekMsg, getHeekMsgType, "Returns the type of the Heek message (see PtHeekMsgTypes)"), + PYTHON_METHOD_NOARGS(ptHeekMsg, upcastToFinalHeekMsg, "Returns this message as the Heek message it is"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekMsg, pyGameCliMsg, "Base class for Heek game messages"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptHeekMsg, pyHeekMsg); + +// required functions for PyObject interoperability +PyObject* pyHeekMsg::New(pfGameCliMsg* msg) +{ + ptHeekMsg *newObj = (ptHeekMsg*)ptHeekMsg_type.tp_new(&ptHeekMsg_type, NULL, NULL); + if (msg && (msg->gameCli->GetGameTypeId() == kGameTypeId_Heek)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekMsg, pyHeekMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekMsg, pyHeekMsg) + +// Module and method definitions +void pyHeekMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyHeekMsg::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtHeekMsgTypes); + PYTHON_ENUM_ELEMENT(PtHeekMsgTypes, kHeekPlayGame, kSrv2Cli_Heek_PlayGame); + PYTHON_ENUM_ELEMENT(PtHeekMsgTypes, kHeekGoodbye, kSrv2Cli_Heek_Goodbye); + PYTHON_ENUM_ELEMENT(PtHeekMsgTypes, kHeekWelcome, kSrv2Cli_Heek_Welcome); + PYTHON_ENUM_ELEMENT(PtHeekMsgTypes, kHeekDrop, kSrv2Cli_Heek_Drop); + PYTHON_ENUM_ELEMENT(PtHeekMsgTypes, kHeekSetup, kSrv2Cli_Heek_Setup); + PYTHON_ENUM_ELEMENT(PtHeekMsgTypes, kHeekLightState, kSrv2Cli_Heek_LightState); + PYTHON_ENUM_ELEMENT(PtHeekMsgTypes, kHeekInterfaceState, kSrv2Cli_Heek_InterfaceState); + PYTHON_ENUM_ELEMENT(PtHeekMsgTypes, kHeekCountdownState, kSrv2Cli_Heek_CountdownState); + PYTHON_ENUM_ELEMENT(PtHeekMsgTypes, kHeekWinLose, kSrv2Cli_Heek_WinLose); + PYTHON_ENUM_ELEMENT(PtHeekMsgTypes, kHeekGameWin, kSrv2Cli_Heek_GameWin); + PYTHON_ENUM_ELEMENT(PtHeekMsgTypes, kHeekPointUpdate, kSrv2Cli_Heek_PointUpdate); + PYTHON_ENUM_END(m, PtHeekMsgTypes); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Game client message subclasses +// + +PYTHON_CLASS_DEFINITION(ptHeekPlayGameMsg, pyHeekPlayGameMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekPlayGameMsg, pyHeekPlayGameMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekPlayGameMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekPlayGameMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekPlayGameMsg, isPlaying) +{ + PYTHON_RETURN_BOOL(self->fThis->IsPlaying()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekPlayGameMsg, isSinglePlayer) +{ + PYTHON_RETURN_BOOL(self->fThis->IsSinglePlayer()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekPlayGameMsg, enableButtons) +{ + PYTHON_RETURN_BOOL(self->fThis->EnableButtons()) +} + +PYTHON_START_METHODS_TABLE(ptHeekPlayGameMsg) + PYTHON_METHOD_NOARGS(ptHeekPlayGameMsg, isPlaying, "Returns true if the server accepted the play game request"), + PYTHON_METHOD_NOARGS(ptHeekPlayGameMsg, isSinglePlayer, "Returns true if you are the only player at the table"), + PYTHON_METHOD_NOARGS(ptHeekPlayGameMsg, enableButtons, "Returns true if we should enable the buttons at the place we sat down"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekPlayGameMsg, pyHeekMsg, "Heek message received when the server processes your play game request"); + +// required functions for PyObject interoperability +PyObject* pyHeekPlayGameMsg::New(pfGameCliMsg* msg) +{ + ptHeekPlayGameMsg *newObj = (ptHeekPlayGameMsg*)ptHeekPlayGameMsg_type.tp_new(&ptHeekPlayGameMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Heek_PlayGame)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekPlayGameMsg, pyHeekPlayGameMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekPlayGameMsg, pyHeekPlayGameMsg) + +// Module and method definitions +void pyHeekPlayGameMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekPlayGameMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptHeekGoodbyeMsg, pyHeekGoodbyeMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekGoodbyeMsg, pyHeekGoodbyeMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekGoodbyeMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekGoodbyeMsg) + +PYTHON_START_METHODS_TABLE(ptHeekGoodbyeMsg) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekGoodbyeMsg, pyHeekMsg, "Heek message received when the server processes leave request"); + +// required functions for PyObject interoperability +PyObject* pyHeekGoodbyeMsg::New(pfGameCliMsg* msg) +{ + ptHeekGoodbyeMsg *newObj = (ptHeekGoodbyeMsg*)ptHeekGoodbyeMsg_type.tp_new(&ptHeekGoodbyeMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Heek_Goodbye)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekGoodbyeMsg, pyHeekGoodbyeMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekGoodbyeMsg, pyHeekGoodbyeMsg) + +// Module and method definitions +void pyHeekGoodbyeMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekGoodbyeMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptHeekWelcomeMsg, pyHeekWelcomeMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekWelcomeMsg, pyHeekWelcomeMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekWelcomeMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekWelcomeMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekWelcomeMsg, points) +{ + return PyLong_FromUnsignedLong(self->fThis->Points()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekWelcomeMsg, rank) +{ + return PyLong_FromUnsignedLong(self->fThis->Rank()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekWelcomeMsg, name) +{ + std::wstring retVal = self->fThis->Name(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_START_METHODS_TABLE(ptHeekWelcomeMsg) + PYTHON_METHOD_NOARGS(ptHeekWelcomeMsg, points, "Returns the new player's points"), + PYTHON_METHOD_NOARGS(ptHeekWelcomeMsg, rank, "Returns the new player's rank"), + PYTHON_METHOD_NOARGS(ptHeekWelcomeMsg, name, "Returns the new player's name"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekWelcomeMsg, pyHeekMsg, "Heek message received when a new player sits down"); + +// required functions for PyObject interoperability +PyObject* pyHeekWelcomeMsg::New(pfGameCliMsg* msg) +{ + ptHeekWelcomeMsg *newObj = (ptHeekWelcomeMsg*)ptHeekWelcomeMsg_type.tp_new(&ptHeekWelcomeMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Heek_Welcome)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekWelcomeMsg, pyHeekWelcomeMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekWelcomeMsg, pyHeekWelcomeMsg) + +// Module and method definitions +void pyHeekWelcomeMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekWelcomeMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptHeekDropMsg, pyHeekDropMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekDropMsg, pyHeekDropMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekDropMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekDropMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekDropMsg, position) +{ + return PyLong_FromUnsignedLong(self->fThis->Position()); +} + +PYTHON_START_METHODS_TABLE(ptHeekDropMsg) + PYTHON_METHOD_NOARGS(ptHeekDropMsg, position, "Returns player position to cleanup and dump"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekDropMsg, pyHeekMsg, "Heek message received when another player's position needs to be reset/modified"); + +// required functions for PyObject interoperability +PyObject* pyHeekDropMsg::New(pfGameCliMsg* msg) +{ + ptHeekDropMsg *newObj = (ptHeekDropMsg*)ptHeekDropMsg_type.tp_new(&ptHeekDropMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Heek_Drop)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekDropMsg, pyHeekDropMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekDropMsg, pyHeekDropMsg) + +// Module and method definitions +void pyHeekDropMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekDropMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptHeekSetupMsg, pyHeekSetupMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekSetupMsg, pyHeekSetupMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekSetupMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekSetupMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekSetupMsg, position) +{ + return PyInt_FromLong(self->fThis->Position()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekSetupMsg, buttonState) +{ + PYTHON_RETURN_BOOL(self->fThis->ButtonState()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekSetupMsg, lightOn) +{ + std::vector lights = self->fThis->LightOn(); + PyObject* retVal = PyList_New(lights.size()); + for (unsigned i = 0; i < lights.size(); ++i) + PyList_SetItem(retVal, i, PyInt_FromLong(lights[i] ? 1 : 0)); + return retVal; +} + +PYTHON_START_METHODS_TABLE(ptHeekSetupMsg) + PYTHON_METHOD_NOARGS(ptHeekSetupMsg, position, "Returns the position this message is for"), + PYTHON_METHOD_NOARGS(ptHeekSetupMsg, buttonState, "Returns whether the buttons are enabled or not"), + PYTHON_METHOD_NOARGS(ptHeekSetupMsg, lightOn, "Returns a list of bools representing lights on or off"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekSetupMsg, pyHeekMsg, "Heek message for setting up each position's state"); + +// required functions for PyObject interoperability +PyObject* pyHeekSetupMsg::New(pfGameCliMsg* msg) +{ + ptHeekSetupMsg *newObj = (ptHeekSetupMsg*)ptHeekSetupMsg_type.tp_new(&ptHeekSetupMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Heek_Setup)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekSetupMsg, pyHeekSetupMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekSetupMsg, pyHeekSetupMsg) + +// Module and method definitions +void pyHeekSetupMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekSetupMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptHeekLightStateMsg, pyHeekLightStateMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekLightStateMsg, pyHeekLightStateMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekLightStateMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekLightStateMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekLightStateMsg, lightNum) +{ + return PyInt_FromLong(self->fThis->LightNum()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekLightStateMsg, state) +{ + return PyInt_FromLong(self->fThis->State()); +} + +PYTHON_START_METHODS_TABLE(ptHeekLightStateMsg) + PYTHON_METHOD_NOARGS(ptHeekLightStateMsg, lightNum, "Returns the index of the light this refers to"), + PYTHON_METHOD_NOARGS(ptHeekLightStateMsg, state, "Returns state the light should be switched to (see PtHeekLightStates)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekLightStateMsg, pyHeekMsg, "Heek message received when one of your local lights needs to change state"); + +// required functions for PyObject interoperability +PyObject* pyHeekLightStateMsg::New(pfGameCliMsg* msg) +{ + ptHeekLightStateMsg *newObj = (ptHeekLightStateMsg*)ptHeekLightStateMsg_type.tp_new(&ptHeekLightStateMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Heek_LightState)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekLightStateMsg, pyHeekLightStateMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekLightStateMsg, pyHeekLightStateMsg) + +// Module and method definitions +void pyHeekLightStateMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekLightStateMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyHeekLightStateMsg::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtHeekLightStates); + PYTHON_ENUM_ELEMENT(PtHeekLightStates, kHeekLightOn, kHeekLightOn); + PYTHON_ENUM_ELEMENT(PtHeekLightStates, kHeekLightOff, kHeekLightOff); + PYTHON_ENUM_ELEMENT(PtHeekLightStates, kHeekLightFlash, kHeekLightFlash); + PYTHON_ENUM_END(m, PtHeekLightStates); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptHeekInterfaceStateMsg, pyHeekInterfaceStateMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekInterfaceStateMsg, pyHeekInterfaceStateMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekInterfaceStateMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekInterfaceStateMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekInterfaceStateMsg, buttonsEnabled) +{ + PYTHON_RETURN_BOOL(self->fThis->ButtonsEnabled()); +} + +PYTHON_START_METHODS_TABLE(ptHeekInterfaceStateMsg) + PYTHON_METHOD_NOARGS(ptHeekInterfaceStateMsg, buttonsEnabled, "Returns whether your buttons should be enabled"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekInterfaceStateMsg, pyHeekMsg, "Heek message received when your interface buttons need to enable or disable"); + +// required functions for PyObject interoperability +PyObject* pyHeekInterfaceStateMsg::New(pfGameCliMsg* msg) +{ + ptHeekInterfaceStateMsg *newObj = (ptHeekInterfaceStateMsg*)ptHeekInterfaceStateMsg_type.tp_new(&ptHeekInterfaceStateMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Heek_InterfaceState)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekInterfaceStateMsg, pyHeekInterfaceStateMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekInterfaceStateMsg, pyHeekInterfaceStateMsg) + +// Module and method definitions +void pyHeekInterfaceStateMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekInterfaceStateMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptHeekCountdownStateMsg, pyHeekCountdownStateMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekCountdownStateMsg, pyHeekCountdownStateMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekCountdownStateMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekCountdownStateMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekCountdownStateMsg, state) +{ + return PyInt_FromLong(self->fThis->State()); +} + +PYTHON_START_METHODS_TABLE(ptHeekCountdownStateMsg) + PYTHON_METHOD_NOARGS(ptHeekCountdownStateMsg, state, "Returns state the countdown should be switched to (see PtHeekCountdownStates)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekCountdownStateMsg, pyHeekMsg, "Heek message received by game admin when the countdown state needs to change"); + +// required functions for PyObject interoperability +PyObject* pyHeekCountdownStateMsg::New(pfGameCliMsg* msg) +{ + ptHeekCountdownStateMsg *newObj = (ptHeekCountdownStateMsg*)ptHeekCountdownStateMsg_type.tp_new(&ptHeekCountdownStateMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Heek_CountdownState)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekCountdownStateMsg, pyHeekCountdownStateMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekCountdownStateMsg, pyHeekCountdownStateMsg) + +// Module and method definitions +void pyHeekCountdownStateMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekCountdownStateMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyHeekCountdownStateMsg::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtHeekCountdownStates); + PYTHON_ENUM_ELEMENT(PtHeekCountdownStates, kHeekCountdownStart, kHeekCountdownStart); + PYTHON_ENUM_ELEMENT(PtHeekCountdownStates, kHeekCountdownStop, kHeekCountdownStop); + PYTHON_ENUM_ELEMENT(PtHeekCountdownStates, kHeekCountdownIdle, kHeekCountdownIdle); + PYTHON_ENUM_END(m, PtHeekCountdownStates); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptHeekWinLoseMsg, pyHeekWinLoseMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekWinLoseMsg, pyHeekWinLoseMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekWinLoseMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekWinLoseMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekWinLoseMsg, win) +{ + PYTHON_RETURN_BOOL(self->fThis->Win()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekWinLoseMsg, choice) +{ + return PyInt_FromLong(self->fThis->Choice()); +} + +PYTHON_START_METHODS_TABLE(ptHeekWinLoseMsg) + PYTHON_METHOD_NOARGS(ptHeekWinLoseMsg, win, "Returns true if you won"), + PYTHON_METHOD_NOARGS(ptHeekWinLoseMsg, choice, "Returns the choice that won or lost (see PtHeekGameChoice)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekWinLoseMsg, pyHeekMsg, "Heek message received when the round is over and you won or lost"); + +// required functions for PyObject interoperability +PyObject* pyHeekWinLoseMsg::New(pfGameCliMsg* msg) +{ + ptHeekWinLoseMsg *newObj = (ptHeekWinLoseMsg*)ptHeekWinLoseMsg_type.tp_new(&ptHeekWinLoseMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Heek_WinLose)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekWinLoseMsg, pyHeekWinLoseMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekWinLoseMsg, pyHeekWinLoseMsg) + +// Module and method definitions +void pyHeekWinLoseMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekWinLoseMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptHeekGameWinMsg, pyHeekGameWinMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekGameWinMsg, pyHeekGameWinMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekGameWinMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekGameWinMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekGameWinMsg, choice) +{ + return PyInt_FromLong(self->fThis->Choice()); +} + +PYTHON_START_METHODS_TABLE(ptHeekGameWinMsg) + PYTHON_METHOD_NOARGS(ptHeekGameWinMsg, choice, "Returns the choice that won (see PtHeekGameChoice)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekGameWinMsg, pyHeekMsg, "Heek message received by game admin when a game is won"); + +// required functions for PyObject interoperability +PyObject* pyHeekGameWinMsg::New(pfGameCliMsg* msg) +{ + ptHeekGameWinMsg *newObj = (ptHeekGameWinMsg*)ptHeekGameWinMsg_type.tp_new(&ptHeekGameWinMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Heek_GameWin)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekGameWinMsg, pyHeekGameWinMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekGameWinMsg, pyHeekGameWinMsg) + +// Module and method definitions +void pyHeekGameWinMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekGameWinMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptHeekPointUpdateMsg, pyHeekPointUpdateMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptHeekPointUpdateMsg, pyHeekPointUpdateMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptHeekPointUpdateMsg) + +PYTHON_NO_INIT_DEFINITION(ptHeekPointUpdateMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekPointUpdateMsg, displayUpdate) +{ + PYTHON_RETURN_BOOL(self->fThis->DisplayUpdate()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekPointUpdateMsg, points) +{ + return PyLong_FromUnsignedLong(self->fThis->Points()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptHeekPointUpdateMsg, rank) +{ + return PyLong_FromUnsignedLong(self->fThis->Rank()); +} + +PYTHON_START_METHODS_TABLE(ptHeekPointUpdateMsg) + PYTHON_METHOD_NOARGS(ptHeekPointUpdateMsg, displayUpdate, "Returns whether you should display a message to the user"), + PYTHON_METHOD_NOARGS(ptHeekPointUpdateMsg, points, "Returns your new amount of points"), + PYTHON_METHOD_NOARGS(ptHeekPointUpdateMsg, rank, "Returns your new rank"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptHeekPointUpdateMsg, pyHeekMsg, "Heek message received when the number of points you have needs to be changed"); + +// required functions for PyObject interoperability +PyObject* pyHeekPointUpdateMsg::New(pfGameCliMsg* msg) +{ + ptHeekPointUpdateMsg *newObj = (ptHeekPointUpdateMsg*)ptHeekPointUpdateMsg_type.tp_new(&ptHeekPointUpdateMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Heek_PointUpdate)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptHeekPointUpdateMsg, pyHeekPointUpdateMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptHeekPointUpdateMsg, pyHeekPointUpdateMsg) + +// Module and method definitions +void pyHeekPointUpdateMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptHeekPointUpdateMsg); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGame.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGame.cpp new file mode 100644 index 00000000..04528b05 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGame.cpp @@ -0,0 +1,145 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyMarkerGame.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base Marker game client class +// + +pyMarkerGame::pyMarkerGame(): pyGameCli() {} + +pyMarkerGame::pyMarkerGame(pfGameCli* client): pyGameCli(client) +{ + if (client && (client->GetGameTypeId() != kGameTypeId_Marker)) + gameClient = nil; // wrong type, just clear it out +} + +bool pyMarkerGame::IsMarkerGame(std::wstring guid) +{ + Uuid gameUuid(guid.c_str()); + return gameUuid == kGameTypeId_Marker; +} + +void pyMarkerGame::CreateMarkerGame(pyKey& callbackKey, unsigned gameType, std::wstring gameName, unsigned long timeLimit, std::wstring templateId) +{ + Marker_CreateParam init; + init.gameType = gameType; + StrCopy(init.gameName, gameName.c_str(), arrsize(init.gameName)); + init.timeLimit = timeLimit; + StrCopy(init.templateID, templateId.c_str(), arrsize(init.templateID)); + pfGameMgr::GetInstance()->CreateGame(callbackKey.getKey(), kGameTypeId_Marker, 0, sizeof(init), &init); +} + +void pyMarkerGame::StartGame() +{ + if (gameClient) + { + pfGmMarker* marker = pfGmMarker::ConvertNoRef(gameClient); + marker->StartGame(); + } +} + +void pyMarkerGame::PauseGame() +{ + if (gameClient) + { + pfGmMarker* marker = pfGmMarker::ConvertNoRef(gameClient); + marker->PauseGame(); + } +} + +void pyMarkerGame::ResetGame() +{ + if (gameClient) + { + pfGmMarker* marker = pfGmMarker::ConvertNoRef(gameClient); + marker->ResetGame(); + } +} + +void pyMarkerGame::ChangeGameName(std::wstring newName) +{ + if (gameClient) + { + pfGmMarker* marker = pfGmMarker::ConvertNoRef(gameClient); + marker->ChangeGameName(newName.c_str()); + } +} + +void pyMarkerGame::ChangeTimeLimit(unsigned long timeLimit) +{ + if (gameClient) + { + pfGmMarker* marker = pfGmMarker::ConvertNoRef(gameClient); + marker->ChangeTimeLimit(timeLimit); + } +} + +void pyMarkerGame::DeleteGame() +{ + if (gameClient) + { + pfGmMarker* marker = pfGmMarker::ConvertNoRef(gameClient); + marker->DeleteGame(); + } +} + +void pyMarkerGame::AddMarker(double x, double y, double z, std::wstring name, std::wstring age) +{ + if (gameClient) + { + pfGmMarker* marker = pfGmMarker::ConvertNoRef(gameClient); + marker->AddMarker(x, y, z, name.c_str(), age.c_str()); + } +} + +void pyMarkerGame::DeleteMarker(unsigned long markerId) +{ + if (gameClient) + { + pfGmMarker* marker = pfGmMarker::ConvertNoRef(gameClient); + marker->DeleteMarker(markerId); + } +} + +void pyMarkerGame::ChangeMarkerName(unsigned long markerId, std::wstring newName) +{ + if (gameClient) + { + pfGmMarker* marker = pfGmMarker::ConvertNoRef(gameClient); + marker->ChangeMarkerName(markerId, newName.c_str()); + } +} + +void pyMarkerGame::CaptureMarker(unsigned long markerId) +{ + if (gameClient) + { + pfGmMarker* marker = pfGmMarker::ConvertNoRef(gameClient); + marker->CaptureMarker(markerId); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGame.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGame.h new file mode 100644 index 00000000..f9127b61 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGame.h @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyMarkerGame_h +#define pyMarkerGame_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyMarkerGame +// +// PURPOSE: Class wrapper for the Marker game client +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCli.h" +#include "../../pyKey.h" + +class pyMarkerGame : public pyGameCli +{ +protected: + pyMarkerGame(); + pyMarkerGame(pfGameCli* client); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerGame); + static PyObject* New(pfGameCli* client); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a ptMarkerGame object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerGame); // converts a PyObject to a pyMarkerGame (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + static void AddPlasmaMethods(std::vector& methods); + + static bool IsMarkerGame(std::wstring guid); + static void CreateMarkerGame(pyKey& callbackKey, unsigned gameType, std::wstring gameName, unsigned long timeLimit, std::wstring templateId); + + void StartGame(); + void PauseGame(); + void ResetGame(); + void ChangeGameName(std::wstring newName); + void ChangeTimeLimit(unsigned long timeLimit); + void DeleteGame(); + void AddMarker(double x, double y, double z, std::wstring name, std::wstring age); + void DeleteMarker(unsigned long markerId); + void ChangeMarkerName(unsigned long markerId, std::wstring newName); + void CaptureMarker(unsigned long markerId); +}; + +#endif // pyMarkerGame_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGameGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGameGlue.cpp new file mode 100644 index 00000000..42f8bf47 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerGameGlue.cpp @@ -0,0 +1,381 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyMarkerGame.h" + +#include +#include "../../pyEnum.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base TTT game client class +// + +PYTHON_CLASS_DEFINITION(ptMarkerGame, pyMarkerGame); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerGame, pyMarkerGame) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerGame) + +PYTHON_NO_INIT_DEFINITION(ptMarkerGame) + +PYTHON_GLOBAL_METHOD_DEFINITION(PtIsMarkerGame, args, "Params: typeID\nReturns true if the specifed typeID (guid as a string) is a Marker game") +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "PtIsMarkerGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + bool retVal = pyMarkerGame::IsMarkerGame(text); + delete [] text; + PYTHON_RETURN_BOOL(retVal); + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(textObj); + wchar_t* wText = hsStringToWString(text); + bool retVal = pyMarkerGame::IsMarkerGame(wText); + delete [] wText; + PYTHON_RETURN_BOOL(retVal); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtIsMarkerGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(PtCreateMarkerGame, args, keywords, "Params: callbackKey, gameType, gameName = \"\", timeLimit = 0, templateId = \"\"\n" + "Creates a new Marker game with the specified callback key, game type (from PtMarkerGameTypes), time limit (in ms), and template id (guid string)") +{ + char *kwlist[] = {"callbackKey", "gameType", "gameName", "timeLimit", "templateId", NULL}; + PyObject* callbackObj = NULL; + unsigned int gameType = 0; + PyObject* gameNameObj = NULL; + unsigned long timeLimit = 0; + PyObject* templateIdObj = NULL; + if (!PyArg_ParseTupleAndKeywords(args, keywords, "OI|OkO", kwlist, &callbackObj, &gameType, &gameNameObj, &timeLimit, &templateIdObj)) + { + PyErr_SetString(PyExc_TypeError, "PtCreateMarkerGame expects a ptKey, unsigned int, and optionally a string, an unsigned long, and another string"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(callbackObj)) + { + PyErr_SetString(PyExc_TypeError, "PtCreateMarkerGame expects a ptKey, unsigned int, and optionally a string, an unsigned long, and another string"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(callbackObj); + std::wstring name = L""; + std::wstring templateId = L""; + if (gameNameObj != NULL) + { + if (PyUnicode_Check(gameNameObj)) + { + int strLen = PyUnicode_GetSize(gameNameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)gameNameObj, text, strLen); + text[strLen] = L'\0'; + name = text; + delete [] text; + } + else if (PyString_Check(gameNameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(gameNameObj); + wchar_t* wText = hsStringToWString(text); + name = wText; + delete [] wText; + } + else + { + PyErr_SetString(PyExc_TypeError, "PtCreateMarkerGame expects a ptKey, unsigned int, and optionally a string, an unsigned long, and another string"); + PYTHON_RETURN_ERROR; + } + } + if (templateIdObj != NULL) + { + if (PyUnicode_Check(templateIdObj)) + { + int strLen = PyUnicode_GetSize(templateIdObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)templateIdObj, text, strLen); + text[strLen] = L'\0'; + templateId = text; + delete [] text; + } + else if (PyString_Check(templateIdObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(templateIdObj); + wchar_t* wText = hsStringToWString(text); + templateId = wText; + delete [] wText; + } + else + { + PyErr_SetString(PyExc_TypeError, "PtCreateMarkerGame expects a ptKey, unsigned int, and optionally a string, an unsigned long, and another string"); + PYTHON_RETURN_ERROR; + } + } + pyMarkerGame::CreateMarkerGame(*key, gameType, name, timeLimit, templateId); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptMarkerGame, startGame, StartGame) +PYTHON_BASIC_METHOD_DEFINITION(ptMarkerGame, pauseGame, PauseGame) +PYTHON_BASIC_METHOD_DEFINITION(ptMarkerGame, resetGame, ResetGame) + +PYTHON_METHOD_DEFINITION(ptMarkerGame, changeGameName, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "changeGameName expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + self->fThis->ChangeGameName(text); + delete [] text; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(textObj); + wchar_t* wText = hsStringToWString(text); + self->fThis->ChangeGameName(wText); + delete [] wText; + PYTHON_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_TypeError, "changeGameName expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_METHOD_DEFINITION(ptMarkerGame, changeTimeLimit, args) +{ + unsigned long timeLimit; + if (!PyArg_ParseTuple(args, "k", &timeLimit)) + { + PyErr_SetString(PyExc_TypeError, "changeTimeLimit expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->ChangeTimeLimit(timeLimit); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptMarkerGame, deleteGame, DeleteGame) + +PYTHON_METHOD_DEFINITION_WKEY(ptMarkerGame, addMarker, args, keywords) +{ + char *kwlist[] = {"x", "y", "z", "name", "age", NULL}; + double x, y, z; + PyObject* nameObj = NULL; + PyObject* ageObj = NULL; + if (!PyArg_ParseTupleAndKeywords(args, keywords, "ddd|OO", kwlist, &x, &y, &z, &nameObj, &ageObj)) + { + PyErr_SetString(PyExc_TypeError, "addMarker expects three doubles, and optionally two strings"); + PYTHON_RETURN_ERROR; + } + std::wstring name = L""; + std::wstring age = L""; + if (nameObj != NULL) + { + if (PyUnicode_Check(nameObj)) + { + int strLen = PyUnicode_GetSize(nameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)nameObj, text, strLen); + text[strLen] = L'\0'; + name = text; + delete [] text; + } + else if (PyString_Check(nameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(nameObj); + wchar_t* wText = hsStringToWString(text); + name = wText; + delete [] wText; + } + else + { + PyErr_SetString(PyExc_TypeError, "addMarker expects three doubles, and optionally two strings"); + PYTHON_RETURN_ERROR; + } + } + if (ageObj != NULL) + { + if (PyUnicode_Check(ageObj)) + { + int strLen = PyUnicode_GetSize(ageObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)ageObj, text, strLen); + text[strLen] = L'\0'; + age = text; + delete [] text; + } + else if (PyString_Check(ageObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(ageObj); + wchar_t* wText = hsStringToWString(text); + age = wText; + delete [] wText; + } + else + { + PyErr_SetString(PyExc_TypeError, "addMarker expects three doubles, and optionally two strings"); + PYTHON_RETURN_ERROR; + } + } + self->fThis->AddMarker(x, y, z, name, age); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMarkerGame, deleteMarker, args) +{ + unsigned long markerId; + if (!PyArg_ParseTuple(args, "k", &markerId)) + { + PyErr_SetString(PyExc_TypeError, "deleteMarker expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->DeleteMarker(markerId); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMarkerGame, changeMarkerName, args) +{ + unsigned long markerId; + PyObject* nameObj = NULL; + if (!PyArg_ParseTuple(args, "kO", &markerId, &nameObj)) + { + PyErr_SetString(PyExc_TypeError, "changeMarkerName expects an unsigned long and a string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(nameObj)) + { + int strLen = PyUnicode_GetSize(nameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)nameObj, text, strLen); + text[strLen] = L'\0'; + self->fThis->ChangeMarkerName(markerId, text); + delete [] text; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(nameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(nameObj); + wchar_t* wText = hsStringToWString(text); + self->fThis->ChangeMarkerName(markerId, wText); + delete [] wText; + PYTHON_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_TypeError, "changeMarkerName expects an unsigned long and a string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_METHOD_DEFINITION(ptMarkerGame, captureMarker, args) +{ + unsigned long markerId; + if (!PyArg_ParseTuple(args, "k", &markerId)) + { + PyErr_SetString(PyExc_TypeError, "captureMarker expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->CaptureMarker(markerId); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptMarkerGame) + PYTHON_BASIC_METHOD(ptMarkerGame, startGame, "Starts the game. Won't work on MP games if you're not the owner/creator"), + PYTHON_BASIC_METHOD(ptMarkerGame, pauseGame, "Pauses the game. Won't work on MP games if you're not the owner/creator"), + PYTHON_BASIC_METHOD(ptMarkerGame, resetGame, "Resets the game. Won't work on MP games if you're not the owner/creator"), + PYTHON_METHOD(ptMarkerGame, changeGameName, "Params: newName\nChanges the name of the game. Won't work if you're not the game owner/creator"), + PYTHON_METHOD(ptMarkerGame, changeTimeLimit, "Params: newTimeLimit\nChanges the time limit on the game (in ms). Won't work if you're not the game owner/creator, or if it's a quest game"), + PYTHON_BASIC_METHOD(ptMarkerGame, deleteGame, "Tells the server to delete the game. Won't work if you're not the game owner/creator"), + PYTHON_METHOD_WKEY(ptMarkerGame, addMarker, "Params: x, y, z, name = \"\", age = \"\"\nAdds a marker to the game. Age is ignored in a non-quest game. Won't work if you're not the owner/creator"), + PYTHON_METHOD(ptMarkerGame, deleteMarker, "Params: markerId\nDeletes the specified marker from the game. Won't work if you're not the game owner/creator"), + PYTHON_METHOD(ptMarkerGame, changeMarkerName, "Params: markerId, newName\nChanges the name of the specified marker. Won't work if you're not the game owner/creator"), + PYTHON_METHOD(ptMarkerGame, captureMarker, "Params: markerId\nCaptures the specified marker"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerGame, pyGameCli, "Game client for the Marker game"); + +// required functions for PyObject interoperability +PyObject* pyMarkerGame::New(pfGameCli* client) +{ + ptMarkerGame *newObj = (ptMarkerGame*)ptMarkerGame_type.tp_new(&ptMarkerGame_type, NULL, NULL); + if (client && (client->GetGameTypeId() == kGameTypeId_Marker)) + newObj->fThis->gameClient = client; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerGame, pyMarkerGame) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerGame, pyMarkerGame) + +// Module and method definitions +void pyMarkerGame::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerGame); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyMarkerGame::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtMarkerGameTypes); + PYTHON_ENUM_ELEMENT(PtMarkerGameTypes, kMarkerGameQuest, kMarkerGameQuest); + PYTHON_ENUM_ELEMENT(PtMarkerGameTypes, kMarkerGameCGZ, kMarkerGameCGZ); + PYTHON_ENUM_ELEMENT(PtMarkerGameTypes, kMarkerGameCapture, kMarkerGameCapture); + PYTHON_ENUM_ELEMENT(PtMarkerGameTypes, kMarkerGameCaptureAndHold, kMarkerGameCaptureAndHold); + PYTHON_ENUM_END(m, PtMarkerGameTypes); +} + +void pyMarkerGame::AddPlasmaMethods(std::vector& methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtIsMarkerGame); + PYTHON_GLOBAL_METHOD_WKEY(methods, PtCreateMarkerGame); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsg.cpp new file mode 100644 index 00000000..f51c5e5b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsg.cpp @@ -0,0 +1,408 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyMarkerMsg.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base Marker msg class +// + +pyMarkerMsg::pyMarkerMsg(): pyGameCliMsg() {} + +pyMarkerMsg::pyMarkerMsg(pfGameCliMsg* msg): pyGameCliMsg(msg) +{ + if (message && (message->gameCli->GetGameTypeId() != kGameTypeId_Marker)) + message = nil; // wrong type, just clear it out +} + +int pyMarkerMsg::GetMarkerMsgType() const +{ + if (message) + return message->netMsg->messageId; + return -1; +} + +PyObject* pyMarkerMsg::UpcastToFinalMarkerMsg() const +{ + if (!message) + PYTHON_RETURN_NONE; + switch (message->netMsg->messageId) + { + case kSrv2Cli_Marker_TemplateCreated: + return pyMarkerTemplateCreatedMsg::New(message); + case kSrv2Cli_Marker_TeamAssigned: + return pyMarkerTeamAssignedMsg::New(message); + case kSrv2Cli_Marker_GameType: + return pyMarkerGameTypeMsg::New(message); + case kSrv2Cli_Marker_GameStarted: + return pyMarkerGameStartedMsg::New(message); + case kSrv2Cli_Marker_GamePaused: + return pyMarkerGamePausedMsg::New(message); + case kSrv2Cli_Marker_GameReset: + return pyMarkerGameResetMsg::New(message); + case kSrv2Cli_Marker_GameOver: + return pyMarkerGameOverMsg::New(message); + case kSrv2Cli_Marker_GameNameChanged: + return pyMarkerGameNameChangedMsg::New(message); + case kSrv2Cli_Marker_TimeLimitChanged: + return pyMarkerTimeLimitChangedMsg::New(message); + case kSrv2Cli_Marker_GameDeleted: + return pyMarkerGameDeletedMsg::New(message); + case kSrv2Cli_Marker_MarkerAdded: + return pyMarkerMarkerAddedMsg::New(message); + case kSrv2Cli_Marker_MarkerDeleted: + return pyMarkerMarkerDeletedMsg::New(message); + case kSrv2Cli_Marker_MarkerNameChanged: + return pyMarkerMarkerNameChangedMsg::New(message); + case kSrv2Cli_Marker_MarkerCaptured: + return pyMarkerMarkerCapturedMsg::New(message); + default: + PYTHON_RETURN_NONE; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// The different messages we can receive +// + +pyMarkerTemplateCreatedMsg::pyMarkerTemplateCreatedMsg(): pyMarkerMsg() {} + +pyMarkerTemplateCreatedMsg::pyMarkerTemplateCreatedMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_TemplateCreated)) + message = nil; // wrong type, just clear it out +} + +std::wstring pyMarkerTemplateCreatedMsg::TemplateID() const +{ + if (message) + { + const Srv2Cli_Marker_TemplateCreated* gmMsg = (const Srv2Cli_Marker_TemplateCreated*)message->netMsg; + return gmMsg->templateID; + } + return L""; +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerTeamAssignedMsg::pyMarkerTeamAssignedMsg(): pyMarkerMsg() {} + +pyMarkerTeamAssignedMsg::pyMarkerTeamAssignedMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_TeamAssigned)) + message = nil; // wrong type, just clear it out +} + +int pyMarkerTeamAssignedMsg::TeamNumber() const +{ + if (message) + { + const Srv2Cli_Marker_TeamAssigned* gmMsg = (const Srv2Cli_Marker_TeamAssigned*)message->netMsg; + return gmMsg->teamNumber; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerGameTypeMsg::pyMarkerGameTypeMsg(): pyMarkerMsg() {} + +pyMarkerGameTypeMsg::pyMarkerGameTypeMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_GameType)) + message = nil; // wrong type, just clear it out +} + +int pyMarkerGameTypeMsg::GameType() const +{ + if (message) + { + const Srv2Cli_Marker_GameType* gmMsg = (const Srv2Cli_Marker_GameType*)message->netMsg; + return gmMsg->gameType; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerGameStartedMsg::pyMarkerGameStartedMsg(): pyMarkerMsg() {} + +pyMarkerGameStartedMsg::pyMarkerGameStartedMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_GameStarted)) + message = nil; // wrong type, just clear it out +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerGamePausedMsg::pyMarkerGamePausedMsg(): pyMarkerMsg() {} + +pyMarkerGamePausedMsg::pyMarkerGamePausedMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_GamePaused)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyMarkerGamePausedMsg::TimeLeft() const +{ + if (message) + { + const Srv2Cli_Marker_GamePaused* gmMsg = (const Srv2Cli_Marker_GamePaused*)message->netMsg; + return gmMsg->timeLeft; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerGameResetMsg::pyMarkerGameResetMsg(): pyMarkerMsg() {} + +pyMarkerGameResetMsg::pyMarkerGameResetMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_GameReset)) + message = nil; // wrong type, just clear it out +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerGameOverMsg::pyMarkerGameOverMsg(): pyMarkerMsg() {} + +pyMarkerGameOverMsg::pyMarkerGameOverMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_GameOver)) + message = nil; // wrong type, just clear it out +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerGameNameChangedMsg::pyMarkerGameNameChangedMsg(): pyMarkerMsg() {} + +pyMarkerGameNameChangedMsg::pyMarkerGameNameChangedMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_GameNameChanged)) + message = nil; // wrong type, just clear it out +} + +std::wstring pyMarkerGameNameChangedMsg::Name() const +{ + if (message) + { + const Srv2Cli_Marker_GameNameChanged* gmMsg = (const Srv2Cli_Marker_GameNameChanged*)message->netMsg; + return gmMsg->newName; + } + return L""; +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerTimeLimitChangedMsg::pyMarkerTimeLimitChangedMsg(): pyMarkerMsg() {} + +pyMarkerTimeLimitChangedMsg::pyMarkerTimeLimitChangedMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_TimeLimitChanged)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyMarkerTimeLimitChangedMsg::TimeLimit() const +{ + if (message) + { + const Srv2Cli_Marker_TimeLimitChanged* gmMsg = (const Srv2Cli_Marker_TimeLimitChanged*)message->netMsg; + return gmMsg->newTimeLimit; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerGameDeletedMsg::pyMarkerGameDeletedMsg(): pyMarkerMsg() {} + +pyMarkerGameDeletedMsg::pyMarkerGameDeletedMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_GameDeleted)) + message = nil; // wrong type, just clear it out +} + +bool pyMarkerGameDeletedMsg::Failed() const +{ + if (message) + { + const Srv2Cli_Marker_GameDeleted* gmMsg = (const Srv2Cli_Marker_GameDeleted*)message->netMsg; + return gmMsg->failed; + } + return true; // assume it failed if we have a problem +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerMarkerAddedMsg::pyMarkerMarkerAddedMsg(): pyMarkerMsg() {} + +pyMarkerMarkerAddedMsg::pyMarkerMarkerAddedMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_MarkerAdded)) + message = nil; // wrong type, just clear it out +} + +double pyMarkerMarkerAddedMsg::X() const +{ + if (message) + { + const Srv2Cli_Marker_MarkerAdded* gmMsg = (const Srv2Cli_Marker_MarkerAdded*)message->netMsg; + return gmMsg->x; + } + return 0; +} + +double pyMarkerMarkerAddedMsg::Y() const +{ + if (message) + { + const Srv2Cli_Marker_MarkerAdded* gmMsg = (const Srv2Cli_Marker_MarkerAdded*)message->netMsg; + return gmMsg->y; + } + return 0; +} + +double pyMarkerMarkerAddedMsg::Z() const +{ + if (message) + { + const Srv2Cli_Marker_MarkerAdded* gmMsg = (const Srv2Cli_Marker_MarkerAdded*)message->netMsg; + return gmMsg->z; + } + return 0; +} + +unsigned long pyMarkerMarkerAddedMsg::MarkerId() const +{ + if (message) + { + const Srv2Cli_Marker_MarkerAdded* gmMsg = (const Srv2Cli_Marker_MarkerAdded*)message->netMsg; + return gmMsg->markerID; + } + return 0; +} + +std::wstring pyMarkerMarkerAddedMsg::Name() const +{ + if (message) + { + const Srv2Cli_Marker_MarkerAdded* gmMsg = (const Srv2Cli_Marker_MarkerAdded*)message->netMsg; + return gmMsg->name; + } + return L""; +} + +std::wstring pyMarkerMarkerAddedMsg::Age() const +{ + if (message) + { + const Srv2Cli_Marker_MarkerAdded* gmMsg = (const Srv2Cli_Marker_MarkerAdded*)message->netMsg; + return gmMsg->age; + } + return L""; +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerMarkerDeletedMsg::pyMarkerMarkerDeletedMsg(): pyMarkerMsg() {} + +pyMarkerMarkerDeletedMsg::pyMarkerMarkerDeletedMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_MarkerDeleted)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyMarkerMarkerDeletedMsg::MarkerId() const +{ + if (message) + { + const Srv2Cli_Marker_MarkerDeleted* gmMsg = (const Srv2Cli_Marker_MarkerDeleted*)message->netMsg; + return gmMsg->markerID; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerMarkerNameChangedMsg::pyMarkerMarkerNameChangedMsg(): pyMarkerMsg() {} + +pyMarkerMarkerNameChangedMsg::pyMarkerMarkerNameChangedMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_MarkerNameChanged)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyMarkerMarkerNameChangedMsg::MarkerId() const +{ + if (message) + { + const Srv2Cli_Marker_MarkerNameChanged* gmMsg = (const Srv2Cli_Marker_MarkerNameChanged*)message->netMsg; + return gmMsg->markerID; + } + return 0; +} + +std::wstring pyMarkerMarkerNameChangedMsg::Name() const +{ + if (message) + { + const Srv2Cli_Marker_MarkerNameChanged* gmMsg = (const Srv2Cli_Marker_MarkerNameChanged*)message->netMsg; + return gmMsg->newName; + } + return L""; +} + +/////////////////////////////////////////////////////////////////////////////// + +pyMarkerMarkerCapturedMsg::pyMarkerMarkerCapturedMsg(): pyMarkerMsg() {} + +pyMarkerMarkerCapturedMsg::pyMarkerMarkerCapturedMsg(pfGameCliMsg* msg): pyMarkerMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Marker_MarkerCaptured)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyMarkerMarkerCapturedMsg::MarkerId() const +{ + if (message) + { + const Srv2Cli_Marker_MarkerCaptured* gmMsg = (const Srv2Cli_Marker_MarkerCaptured*)message->netMsg; + return gmMsg->markerID; + } + return 0; +} + +unsigned int pyMarkerMarkerCapturedMsg::Team() const +{ + if (message) + { + const Srv2Cli_Marker_MarkerCaptured* gmMsg = (const Srv2Cli_Marker_MarkerCaptured*)message->netMsg; + return gmMsg->team; + } + return 0; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsg.h new file mode 100644 index 00000000..d0a2f661 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsg.h @@ -0,0 +1,331 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyMarkerMsg_h +#define pyMarkerMsg_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyMarkerMsg +// +// PURPOSE: Class wrapper for Marker game messages +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCliMsg.h" + +class pyMarkerMsg : public pyGameCliMsg +{ +protected: + pyMarkerMsg(); + pyMarkerMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptMarkerMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerMsg); // converts a PyObject to a pyMarkerMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + + int GetMarkerMsgType() const; + + PyObject* UpcastToFinalMarkerMsg() const; // returns this message as the marker message it really is +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerTemplateCreatedMsg : public pyMarkerMsg +{ +protected: + pyMarkerTemplateCreatedMsg(); + pyMarkerTemplateCreatedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerTemplateCreatedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerTemplateCreatedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerTemplateCreatedMsg); // converts a PyObject to a pyMarkerTemplateCreatedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + std::wstring TemplateID() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerTeamAssignedMsg : public pyMarkerMsg +{ +protected: + pyMarkerTeamAssignedMsg(); + pyMarkerTeamAssignedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerTeamAssignedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerTeamAssignedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerTeamAssignedMsg); // converts a PyObject to a pyMarkerTeamAssignedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + int TeamNumber() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerGameTypeMsg : public pyMarkerMsg +{ +protected: + pyMarkerGameTypeMsg(); + pyMarkerGameTypeMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerGameTypeMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerGameTypeMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerGameTypeMsg); // converts a PyObject to a pyMarkerGameTypeMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + int GameType() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerGameStartedMsg : public pyMarkerMsg +{ +protected: + pyMarkerGameStartedMsg(); + pyMarkerGameStartedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerGameStartedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerGameStartedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerGameStartedMsg); // converts a PyObject to a pyMarkerGameStartedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerGamePausedMsg : public pyMarkerMsg +{ +protected: + pyMarkerGamePausedMsg(); + pyMarkerGamePausedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerGamePausedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerGamePausedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerGamePausedMsg); // converts a PyObject to a pyMarkerGamePausedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long TimeLeft() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerGameResetMsg : public pyMarkerMsg +{ +protected: + pyMarkerGameResetMsg(); + pyMarkerGameResetMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerGameResetMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerGameResetMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerGameResetMsg); // converts a PyObject to a pyMarkerGameResetMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerGameOverMsg : public pyMarkerMsg +{ +protected: + pyMarkerGameOverMsg(); + pyMarkerGameOverMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerGameOverMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerGameOverMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerGameOverMsg); // converts a PyObject to a pyMarkerGameOverMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerGameNameChangedMsg : public pyMarkerMsg +{ +protected: + pyMarkerGameNameChangedMsg(); + pyMarkerGameNameChangedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerGameNameChangedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerGameNameChangedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerGameNameChangedMsg); // converts a PyObject to a pyMarkerGameNameChangedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + std::wstring Name() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerTimeLimitChangedMsg : public pyMarkerMsg +{ +protected: + pyMarkerTimeLimitChangedMsg(); + pyMarkerTimeLimitChangedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerTimeLimitChangedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerTimeLimitChangedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerTimeLimitChangedMsg); // converts a PyObject to a pyMarkerTimeLimitChangedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long TimeLimit() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerGameDeletedMsg : public pyMarkerMsg +{ +protected: + pyMarkerGameDeletedMsg(); + pyMarkerGameDeletedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerGameDeletedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerGameDeletedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerGameDeletedMsg); // converts a PyObject to a pyMarkerGameDeletedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + bool Failed() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerMarkerAddedMsg : public pyMarkerMsg +{ +protected: + pyMarkerMarkerAddedMsg(); + pyMarkerMarkerAddedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerMarkerAddedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerMarkerAddedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerMarkerAddedMsg); // converts a PyObject to a pyMarkerMarkerAddedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + double X() const; + double Y() const; + double Z() const; + unsigned long MarkerId() const; + std::wstring Name() const; + std::wstring Age() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerMarkerDeletedMsg : public pyMarkerMsg +{ +protected: + pyMarkerMarkerDeletedMsg(); + pyMarkerMarkerDeletedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerMarkerDeletedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerMarkerDeletedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerMarkerDeletedMsg); // converts a PyObject to a pyMarkerMarkerDeletedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long MarkerId() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerMarkerNameChangedMsg : public pyMarkerMsg +{ +protected: + pyMarkerMarkerNameChangedMsg(); + pyMarkerMarkerNameChangedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerMarkerNameChangedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerMarkerNameChangedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerMarkerNameChangedMsg); // converts a PyObject to a pyMarkerMarkerNameChangedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long MarkerId() const; + std::wstring Name() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyMarkerMarkerCapturedMsg : public pyMarkerMsg +{ +protected: + pyMarkerMarkerCapturedMsg(); + pyMarkerMarkerCapturedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerMarkerCapturedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerMarkerCapturedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerMarkerCapturedMsg); // converts a PyObject to a pyMarkerMarkerCapturedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long MarkerId() const; + unsigned int Team() const; +}; + +#endif // pyMarkerMsg_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsgGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsgGlue.cpp new file mode 100644 index 00000000..e04c2ed3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/Marker/pyMarkerMsgGlue.cpp @@ -0,0 +1,706 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyMarkerMsg.h" +#include "../../pyEnum.h" + +#include + +/////////////////////////////////////////////////////////////////////////////// +// +// Base Marker msg class +// + +PYTHON_CLASS_DEFINITION(ptMarkerMsg, pyMarkerMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerMsg, pyMarkerMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMsg, getMarkerMsgType) +{ + return PyInt_FromLong(self->fThis->GetMarkerMsgType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMsg, upcastToFinalMarkerMsg) +{ + return self->fThis->UpcastToFinalMarkerMsg(); +} + +PYTHON_START_METHODS_TABLE(ptMarkerMsg) + PYTHON_METHOD_NOARGS(ptMarkerMsg, getMarkerMsgType, "Returns the type of the Marker message (see PtMarkerMsgTypes)"), + PYTHON_METHOD_NOARGS(ptMarkerMsg, upcastToFinalMarkerMsg, "Returns this message as the Marker message it is"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerMsg, pyGameCliMsg, "Base class for Marker game messages"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptMarkerMsg, pyMarkerMsg); + +// required functions for PyObject interoperability +PyObject* pyMarkerMsg::New(pfGameCliMsg* msg) +{ + ptMarkerMsg *newObj = (ptMarkerMsg*)ptMarkerMsg_type.tp_new(&ptMarkerMsg_type, NULL, NULL); + if (msg && (msg->gameCli->GetGameTypeId() == kGameTypeId_Marker)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerMsg, pyMarkerMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerMsg, pyMarkerMsg) + +// Module and method definitions +void pyMarkerMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyMarkerMsg::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtMarkerMsgTypes); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerTemplateCreated, kSrv2Cli_Marker_TemplateCreated); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerTeamAssigned, kSrv2Cli_Marker_TeamAssigned); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerGameType, kSrv2Cli_Marker_GameType); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerGameStarted, kSrv2Cli_Marker_GameStarted); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerGamePaused, kSrv2Cli_Marker_GamePaused); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerGameReset, kSrv2Cli_Marker_GameReset); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerGameOver, kSrv2Cli_Marker_GameOver); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerGameNameChanged, kSrv2Cli_Marker_GameNameChanged); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerTimeLimitChanged, kSrv2Cli_Marker_TimeLimitChanged); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerGameDeleted, kSrv2Cli_Marker_GameDeleted); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerMarkerAdded, kSrv2Cli_Marker_MarkerAdded); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerMarkerDeleted, kSrv2Cli_Marker_MarkerDeleted); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerMarkerNameChanged, kSrv2Cli_Marker_MarkerNameChanged); + PYTHON_ENUM_ELEMENT(PtMarkerMsgTypes, kMarkerMarkerCaptured, kSrv2Cli_Marker_MarkerCaptured); + PYTHON_ENUM_END(m, PtMarkerMsgTypes); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Game client message subclasses +// + +PYTHON_CLASS_DEFINITION(ptMarkerTemplateCreatedMsg, pyMarkerTemplateCreatedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerTemplateCreatedMsg, pyMarkerTemplateCreatedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerTemplateCreatedMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerTemplateCreatedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerTemplateCreatedMsg, templateID) +{ + std::wstring retVal = self->fThis->TemplateID(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerTemplateCreatedMsg) + PYTHON_METHOD_NOARGS(ptMarkerTemplateCreatedMsg, templateID, "Returns the ID number of the template that was created"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerTemplateCreatedMsg, pyMarkerMsg, "Marker message received when a quest game template is created"); + +// required functions for PyObject interoperability +PyObject* pyMarkerTemplateCreatedMsg::New(pfGameCliMsg* msg) +{ + ptMarkerTemplateCreatedMsg *newObj = (ptMarkerTemplateCreatedMsg*)ptMarkerTemplateCreatedMsg_type.tp_new(&ptMarkerTemplateCreatedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_TemplateCreated)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerTemplateCreatedMsg, pyMarkerTemplateCreatedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerTemplateCreatedMsg, pyMarkerTemplateCreatedMsg) + +// Module and method definitions +void pyMarkerTemplateCreatedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerTemplateCreatedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerTeamAssignedMsg, pyMarkerTeamAssignedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerTeamAssignedMsg, pyMarkerTeamAssignedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerTeamAssignedMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerTeamAssignedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerTeamAssignedMsg, teamNumber) +{ + return PyInt_FromLong(self->fThis->TeamNumber()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerTeamAssignedMsg) + PYTHON_METHOD_NOARGS(ptMarkerTeamAssignedMsg, teamNumber, "Returns the number of the team you were assigned to"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerTeamAssignedMsg, pyMarkerMsg, "Marker message received when you are assigned a team number"); + +// required functions for PyObject interoperability +PyObject* pyMarkerTeamAssignedMsg::New(pfGameCliMsg* msg) +{ + ptMarkerTeamAssignedMsg *newObj = (ptMarkerTeamAssignedMsg*)ptMarkerTeamAssignedMsg_type.tp_new(&ptMarkerTeamAssignedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_TeamAssigned)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerTeamAssignedMsg, pyMarkerTeamAssignedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerTeamAssignedMsg, pyMarkerTeamAssignedMsg) + +// Module and method definitions +void pyMarkerTeamAssignedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerTeamAssignedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerGameTypeMsg, pyMarkerGameTypeMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerGameTypeMsg, pyMarkerGameTypeMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerGameTypeMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerGameTypeMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerGameTypeMsg, gameType) +{ + return PyInt_FromLong(self->fThis->GameType()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerGameTypeMsg) + PYTHON_METHOD_NOARGS(ptMarkerGameTypeMsg, gameType, "Returns the type of the game you just joined"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerGameTypeMsg, pyMarkerMsg, "Marker message received when you are assigned a team number"); + +// required functions for PyObject interoperability +PyObject* pyMarkerGameTypeMsg::New(pfGameCliMsg* msg) +{ + ptMarkerGameTypeMsg *newObj = (ptMarkerGameTypeMsg*)ptMarkerGameTypeMsg_type.tp_new(&ptMarkerGameTypeMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_GameType)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerGameTypeMsg, pyMarkerGameTypeMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerGameTypeMsg, pyMarkerGameTypeMsg) + +// Module and method definitions +void pyMarkerGameTypeMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerGameTypeMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerGameStartedMsg, pyMarkerGameStartedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerGameStartedMsg, pyMarkerGameStartedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerGameStartedMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerGameStartedMsg) + +PYTHON_START_METHODS_TABLE(ptMarkerGameStartedMsg) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerGameStartedMsg, pyMarkerMsg, "Marker message received when the game is started by the owner"); + +// required functions for PyObject interoperability +PyObject* pyMarkerGameStartedMsg::New(pfGameCliMsg* msg) +{ + ptMarkerGameStartedMsg *newObj = (ptMarkerGameStartedMsg*)ptMarkerGameStartedMsg_type.tp_new(&ptMarkerGameStartedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_GameStarted)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerGameStartedMsg, pyMarkerGameStartedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerGameStartedMsg, pyMarkerGameStartedMsg) + +// Module and method definitions +void pyMarkerGameStartedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerGameStartedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerGamePausedMsg, pyMarkerGamePausedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerGamePausedMsg, pyMarkerGamePausedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerGamePausedMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerGamePausedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerGamePausedMsg, timeLeft) +{ + return PyLong_FromUnsignedLong(self->fThis->TimeLeft()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerGamePausedMsg) + PYTHON_METHOD_NOARGS(ptMarkerGamePausedMsg, timeLeft, "Returns the amount of time left on the server clock"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerGamePausedMsg, pyMarkerMsg, "Marker message received when the game is paused by the owner"); + +// required functions for PyObject interoperability +PyObject* pyMarkerGamePausedMsg::New(pfGameCliMsg* msg) +{ + ptMarkerGamePausedMsg *newObj = (ptMarkerGamePausedMsg*)ptMarkerGamePausedMsg_type.tp_new(&ptMarkerGamePausedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_GamePaused)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerGamePausedMsg, pyMarkerGamePausedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerGamePausedMsg, pyMarkerGamePausedMsg) + +// Module and method definitions +void pyMarkerGamePausedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerGamePausedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerGameResetMsg, pyMarkerGameResetMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerGameResetMsg, pyMarkerGameResetMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerGameResetMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerGameResetMsg) + +PYTHON_START_METHODS_TABLE(ptMarkerGameResetMsg) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerGameResetMsg, pyMarkerMsg, "Marker message received when the game is reset by the owner"); + +// required functions for PyObject interoperability +PyObject* pyMarkerGameResetMsg::New(pfGameCliMsg* msg) +{ + ptMarkerGameResetMsg *newObj = (ptMarkerGameResetMsg*)ptMarkerGameResetMsg_type.tp_new(&ptMarkerGameResetMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_GameReset)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerGameResetMsg, pyMarkerGameResetMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerGameResetMsg, pyMarkerGameResetMsg) + +// Module and method definitions +void pyMarkerGameResetMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerGameResetMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerGameOverMsg, pyMarkerGameOverMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerGameOverMsg, pyMarkerGameOverMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerGameOverMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerGameOverMsg) + +PYTHON_START_METHODS_TABLE(ptMarkerGameOverMsg) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerGameOverMsg, pyMarkerMsg, "Marker message received when the server determines the game is over (usually via timeout)"); + +// required functions for PyObject interoperability +PyObject* pyMarkerGameOverMsg::New(pfGameCliMsg* msg) +{ + ptMarkerGameOverMsg *newObj = (ptMarkerGameOverMsg*)ptMarkerGameOverMsg_type.tp_new(&ptMarkerGameOverMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_GameOver)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerGameOverMsg, pyMarkerGameOverMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerGameOverMsg, pyMarkerGameOverMsg) + +// Module and method definitions +void pyMarkerGameOverMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerGameOverMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerGameNameChangedMsg, pyMarkerGameNameChangedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerGameNameChangedMsg, pyMarkerGameNameChangedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerGameNameChangedMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerGameNameChangedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerGameNameChangedMsg, name) +{ + std::wstring retVal = self->fThis->Name(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerGameNameChangedMsg) + PYTHON_METHOD_NOARGS(ptMarkerGameNameChangedMsg, name, "Returns the new game name"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerGameNameChangedMsg, pyMarkerMsg, "Marker message received when the game name is changed"); + +// required functions for PyObject interoperability +PyObject* pyMarkerGameNameChangedMsg::New(pfGameCliMsg* msg) +{ + ptMarkerGameNameChangedMsg *newObj = (ptMarkerGameNameChangedMsg*)ptMarkerGameNameChangedMsg_type.tp_new(&ptMarkerGameNameChangedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_GameNameChanged)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerGameNameChangedMsg, pyMarkerGameNameChangedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerGameNameChangedMsg, pyMarkerGameNameChangedMsg) + +// Module and method definitions +void pyMarkerGameNameChangedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerGameNameChangedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerTimeLimitChangedMsg, pyMarkerTimeLimitChangedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerTimeLimitChangedMsg, pyMarkerTimeLimitChangedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerTimeLimitChangedMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerTimeLimitChangedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerTimeLimitChangedMsg, timeLimit) +{ + return PyLong_FromUnsignedLong(self->fThis->TimeLimit()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerTimeLimitChangedMsg) + PYTHON_METHOD_NOARGS(ptMarkerTimeLimitChangedMsg, timeLimit, "Returns the new time limit (in ms)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerTimeLimitChangedMsg, pyMarkerMsg, "Marker message received when the game name is changed"); + +// required functions for PyObject interoperability +PyObject* pyMarkerTimeLimitChangedMsg::New(pfGameCliMsg* msg) +{ + ptMarkerTimeLimitChangedMsg *newObj = (ptMarkerTimeLimitChangedMsg*)ptMarkerTimeLimitChangedMsg_type.tp_new(&ptMarkerTimeLimitChangedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_TimeLimitChanged)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerTimeLimitChangedMsg, pyMarkerTimeLimitChangedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerTimeLimitChangedMsg, pyMarkerTimeLimitChangedMsg) + +// Module and method definitions +void pyMarkerTimeLimitChangedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerTimeLimitChangedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerGameDeletedMsg, pyMarkerGameDeletedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerGameDeletedMsg, pyMarkerGameDeletedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerGameDeletedMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerGameDeletedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerGameDeletedMsg, failed) +{ + PYTHON_RETURN_BOOL(self->fThis->Failed()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerGameDeletedMsg) + PYTHON_METHOD_NOARGS(ptMarkerGameDeletedMsg, failed, "Returns whether the delete succeeded or not"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerGameDeletedMsg, pyMarkerMsg, "Marker message received when the game is deleted"); + +// required functions for PyObject interoperability +PyObject* pyMarkerGameDeletedMsg::New(pfGameCliMsg* msg) +{ + ptMarkerGameDeletedMsg *newObj = (ptMarkerGameDeletedMsg*)ptMarkerGameDeletedMsg_type.tp_new(&ptMarkerGameDeletedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_GameDeleted)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerGameDeletedMsg, pyMarkerGameDeletedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerGameDeletedMsg, pyMarkerGameDeletedMsg) + +// Module and method definitions +void pyMarkerGameDeletedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerGameDeletedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerMarkerAddedMsg, pyMarkerMarkerAddedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerMarkerAddedMsg, pyMarkerMarkerAddedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerMarkerAddedMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerMarkerAddedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMarkerAddedMsg, x) +{ + return PyFloat_FromDouble(self->fThis->X()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMarkerAddedMsg, y) +{ + return PyFloat_FromDouble(self->fThis->Y()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMarkerAddedMsg, z) +{ + return PyFloat_FromDouble(self->fThis->Z()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMarkerAddedMsg, markerId) +{ + return PyLong_FromUnsignedLong(self->fThis->MarkerId()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMarkerAddedMsg, name) +{ + std::wstring retVal = self->fThis->Name(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMarkerAddedMsg, age) +{ + std::wstring retVal = self->fThis->Age(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerMarkerAddedMsg) + PYTHON_METHOD_NOARGS(ptMarkerMarkerAddedMsg, x, "Returns x coord of the marker"), + PYTHON_METHOD_NOARGS(ptMarkerMarkerAddedMsg, y, "Returns y coord of the marker"), + PYTHON_METHOD_NOARGS(ptMarkerMarkerAddedMsg, z, "Returns z coord of the marker"), + PYTHON_METHOD_NOARGS(ptMarkerMarkerAddedMsg, markerId, "Returns the id number of the marker"), + PYTHON_METHOD_NOARGS(ptMarkerMarkerAddedMsg, name, "Returns the name of the marker"), + PYTHON_METHOD_NOARGS(ptMarkerMarkerAddedMsg, age, "Returns the age the marker was created in"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerMarkerAddedMsg, pyMarkerMsg, "Marker message received when a marker is added to the game"); + +// required functions for PyObject interoperability +PyObject* pyMarkerMarkerAddedMsg::New(pfGameCliMsg* msg) +{ + ptMarkerMarkerAddedMsg *newObj = (ptMarkerMarkerAddedMsg*)ptMarkerMarkerAddedMsg_type.tp_new(&ptMarkerMarkerAddedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_MarkerAdded)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerMarkerAddedMsg, pyMarkerMarkerAddedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerMarkerAddedMsg, pyMarkerMarkerAddedMsg) + +// Module and method definitions +void pyMarkerMarkerAddedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerMarkerAddedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerMarkerDeletedMsg, pyMarkerMarkerDeletedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerMarkerDeletedMsg, pyMarkerMarkerDeletedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerMarkerDeletedMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerMarkerDeletedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMarkerDeletedMsg, markerId) +{ + return PyLong_FromUnsignedLong(self->fThis->MarkerId()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerMarkerDeletedMsg) + PYTHON_METHOD_NOARGS(ptMarkerMarkerDeletedMsg, markerId, "Returns id of the marker that was deleted"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerMarkerDeletedMsg, pyMarkerMsg, "Marker message received when a marker is deleted"); + +// required functions for PyObject interoperability +PyObject* pyMarkerMarkerDeletedMsg::New(pfGameCliMsg* msg) +{ + ptMarkerMarkerDeletedMsg *newObj = (ptMarkerMarkerDeletedMsg*)ptMarkerMarkerDeletedMsg_type.tp_new(&ptMarkerMarkerDeletedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_MarkerDeleted)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerMarkerDeletedMsg, pyMarkerMarkerDeletedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerMarkerDeletedMsg, pyMarkerMarkerDeletedMsg) + +// Module and method definitions +void pyMarkerMarkerDeletedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerMarkerDeletedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerMarkerNameChangedMsg, pyMarkerMarkerNameChangedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerMarkerNameChangedMsg, pyMarkerMarkerNameChangedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerMarkerNameChangedMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerMarkerNameChangedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMarkerNameChangedMsg, markerId) +{ + return PyLong_FromUnsignedLong(self->fThis->MarkerId()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMarkerNameChangedMsg, name) +{ + std::wstring retVal = self->fThis->Name(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerMarkerNameChangedMsg) + PYTHON_METHOD_NOARGS(ptMarkerMarkerNameChangedMsg, markerId, "Returns id of the marker who's name was changed"), + PYTHON_METHOD_NOARGS(ptMarkerMarkerNameChangedMsg, name, "Returns the new name"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerMarkerNameChangedMsg, pyMarkerMsg, "Marker message received when the name of a marker is changed"); + +// required functions for PyObject interoperability +PyObject* pyMarkerMarkerNameChangedMsg::New(pfGameCliMsg* msg) +{ + ptMarkerMarkerNameChangedMsg *newObj = (ptMarkerMarkerNameChangedMsg*)ptMarkerMarkerNameChangedMsg_type.tp_new(&ptMarkerMarkerNameChangedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_MarkerNameChanged)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerMarkerNameChangedMsg, pyMarkerMarkerNameChangedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerMarkerNameChangedMsg, pyMarkerMarkerNameChangedMsg) + +// Module and method definitions +void pyMarkerMarkerNameChangedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerMarkerNameChangedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +PYTHON_CLASS_DEFINITION(ptMarkerMarkerCapturedMsg, pyMarkerMarkerCapturedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerMarkerCapturedMsg, pyMarkerMarkerCapturedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerMarkerCapturedMsg) + +PYTHON_NO_INIT_DEFINITION(ptMarkerMarkerCapturedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMarkerCapturedMsg, markerId) +{ + return PyLong_FromUnsignedLong(self->fThis->MarkerId()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMarkerCapturedMsg, team) +{ + return PyInt_FromLong((long)self->fThis->Team()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerMarkerCapturedMsg) + PYTHON_METHOD_NOARGS(ptMarkerMarkerCapturedMsg, markerId, "Returns id of the marker which was captured"), + PYTHON_METHOD_NOARGS(ptMarkerMarkerCapturedMsg, team, "Returns the team number of the team that captured it (0 for no team, or a quest game)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptMarkerMarkerCapturedMsg, pyMarkerMsg, "Marker message received when a marker is captured"); + +// required functions for PyObject interoperability +PyObject* pyMarkerMarkerCapturedMsg::New(pfGameCliMsg* msg) +{ + ptMarkerMarkerCapturedMsg *newObj = (ptMarkerMarkerCapturedMsg*)ptMarkerMarkerCapturedMsg_type.tp_new(&ptMarkerMarkerNameChangedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Marker_MarkerCaptured)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMarkerMarkerCapturedMsg, pyMarkerMarkerCapturedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerMarkerCapturedMsg, pyMarkerMarkerCapturedMsg) + +// Module and method definitions +void pyMarkerMarkerCapturedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerMarkerCapturedMsg); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGame.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGame.cpp new file mode 100644 index 00000000..566c3426 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGame.cpp @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyTTTGame.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base TTT game client class +// + +pyTTTGame::pyTTTGame(): pyGameCli() {} + +pyTTTGame::pyTTTGame(pfGameCli* client): pyGameCli(client) +{ + if (client && (client->GetGameTypeId() != kGameTypeId_TicTacToe)) + gameClient = nil; // wrong type, just clear it out +} + +bool pyTTTGame::IsTTTGame(std::wstring guid) +{ + Uuid gameUuid(guid.c_str()); + return gameUuid == kGameTypeId_TicTacToe; +} + +void pyTTTGame::CreateTTTGame(pyKey& callbackKey, unsigned numPlayers) +{ + TTT_CreateParam init; + init.playerCount = numPlayers; + pfGameMgr::GetInstance()->CreateGame(callbackKey.getKey(), kGameTypeId_TicTacToe, 0, sizeof(init), &init); +} + +void pyTTTGame::JoinCommonTTTGame(pyKey& callbackKey, unsigned gameID, unsigned numPlayers) +{ + TTT_CreateParam init; + init.playerCount = numPlayers; + pfGameMgr::GetInstance()->JoinCommonGame(callbackKey.getKey(), kGameTypeId_TicTacToe, gameID, sizeof(init), &init); +} + +void pyTTTGame::MakeMove(unsigned row, unsigned col) +{ + if (gameClient) + { + pfGmTicTacToe* ttt = pfGmTicTacToe::ConvertNoRef(gameClient); + ttt->MakeMove(row, col); + } +} + +void pyTTTGame::ShowBoard() +{ + if (gameClient) + { + pfGmTicTacToe* ttt = pfGmTicTacToe::ConvertNoRef(gameClient); + ttt->ShowBoard(); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGame.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGame.h new file mode 100644 index 00000000..327382fe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGame.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyTTTGame_h +#define pyTTTGame_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyTTTGame +// +// PURPOSE: Class wrapper for the TTT game client +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCli.h" +#include "../../pyKey.h" + +class pyTTTGame : public pyGameCli +{ +protected: + pyTTTGame(); + pyTTTGame(pfGameCli* client); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptTTTGame); + static PyObject* New(pfGameCli* client); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyTTTGame object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyTTTGame); // converts a PyObject to a pyTTTGame (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + static void AddPlasmaMethods(std::vector& methods); + + static bool IsTTTGame(std::wstring guid); + static void CreateTTTGame(pyKey& callbackKey, unsigned numPlayers); + static void JoinCommonTTTGame(pyKey& callbackKey, unsigned gameID, unsigned numPlayers); + + void MakeMove(unsigned row, unsigned col); + void ShowBoard(); +}; + +#endif // pyTTTGame_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGameGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGameGlue.cpp new file mode 100644 index 00000000..8bf92f76 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTGameGlue.cpp @@ -0,0 +1,172 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyTTTGame.h" + +#include +#include "../../pyEnum.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base TTT game client class +// + +PYTHON_CLASS_DEFINITION(ptTTTGame, pyTTTGame); + +PYTHON_DEFAULT_NEW_DEFINITION(ptTTTGame, pyTTTGame) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptTTTGame) + +PYTHON_NO_INIT_DEFINITION(ptTTTGame) + +PYTHON_GLOBAL_METHOD_DEFINITION(PtIsTTTGame, args, "Params: typeID\nReturns true if the specifed typeID (guid as a string) is a TicTacToe game") +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "PtIsTTTGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + bool retVal = pyTTTGame::IsTTTGame(text); + delete [] text; + PYTHON_RETURN_BOOL(retVal); + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(textObj); + wchar_t* wText = hsStringToWString(text); + bool retVal = pyTTTGame::IsTTTGame(wText); + delete [] wText; + PYTHON_RETURN_BOOL(retVal); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtIsTTTGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtCreateTTTGame, args, "Params: callbackKey, numPlayers\nCreates a new TicTacToe game with the specified callback key and number of players (1 or 2)") +{ + PyObject* callbackObj = NULL; + int numPlayers = 0; + if (!PyArg_ParseTuple(args, "Oi", &callbackObj, &numPlayers)) + { + PyErr_SetString(PyExc_TypeError, "PtCreateTTTGame expects a ptKey and an integer"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(callbackObj)) + { + PyErr_SetString(PyExc_TypeError, "PtCreateTTTGame expects a ptKey and an integer"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(callbackObj); + pyTTTGame::CreateTTTGame(*key, numPlayers); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtJoinCommonTTTGame, args, "Params: callbackKey, gameID, numPlayers\nJoins a common TicTacToe game with the specified ID. If one doesn't exist, it creates it with the specified number of players") +{ + PyObject* callbackObj = NULL; + int gameID = 0, numPlayers = 0; + if (!PyArg_ParseTuple(args, "Oii", &callbackObj, &gameID, &numPlayers)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinCommonTTTGame expects a ptKey and two integers"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(callbackObj)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinCommonTTTGame expects a ptKey and two integers"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(callbackObj); + pyTTTGame::JoinCommonTTTGame(*key, gameID, numPlayers); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptTTTGame, makeMove, args) +{ + int row = 0, col = 0; + if (!PyArg_ParseTuple(args, "ii", &row, &col)) + { + PyErr_SetString(PyExc_TypeError, "makeMove expects two integers"); + PYTHON_RETURN_ERROR; + } + self->fThis->MakeMove(row, col); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptTTTGame, showBoard, ShowBoard) + +PYTHON_START_METHODS_TABLE(ptTTTGame) + PYTHON_METHOD(ptTTTGame, makeMove, "Params: row, col\nMakes a move in the specified spot"), + PYTHON_BASIC_METHOD(ptTTTGame, showBoard, "Prints the current board layout to the console"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptTTTGame, pyGameCli, "Game client for the TicTacToe game"); + +// required functions for PyObject interoperability +PyObject* pyTTTGame::New(pfGameCli* client) +{ + ptTTTGame *newObj = (ptTTTGame*)ptTTTGame_type.tp_new(&ptTTTGame_type, NULL, NULL); + if (client && (client->GetGameTypeId() == kGameTypeId_TicTacToe)) + newObj->fThis->gameClient = client; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptTTTGame, pyTTTGame) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptTTTGame, pyTTTGame) + +// Module and method definitions +void pyTTTGame::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptTTTGame); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyTTTGame::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtTTTGameResult); + PYTHON_ENUM_ELEMENT(PtTTTGameResult, kTTTGameResultWinner, kTTTGameResultWinner); + PYTHON_ENUM_ELEMENT(PtTTTGameResult, kTTTGameResultTied, kTTTGameResultTied); + PYTHON_ENUM_ELEMENT(PtTTTGameResult, kTTTGameResultGave, kTTTGameResultGave); + PYTHON_ENUM_ELEMENT(PtTTTGameResult, kTTTGameResultError, kTTTGameResultError); + PYTHON_ENUM_END(m, PtTTTGameResult); +} + +void pyTTTGame::AddPlasmaMethods(std::vector& methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtIsTTTGame); + PYTHON_GLOBAL_METHOD(methods, PtCreateTTTGame); + PYTHON_GLOBAL_METHOD(methods, PtJoinCommonTTTGame); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsg.cpp new file mode 100644 index 00000000..1adce42a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsg.cpp @@ -0,0 +1,155 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyTTTMsg.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base TTT msg class +// + +pyTTTMsg::pyTTTMsg(): pyGameCliMsg() {} + +pyTTTMsg::pyTTTMsg(pfGameCliMsg* msg): pyGameCliMsg(msg) +{ + if (message && (message->gameCli->GetGameTypeId() != kGameTypeId_TicTacToe)) + message = nil; // wrong type, just clear it out +} + +int pyTTTMsg::GetTTTMsgType() const +{ + if (message) + return message->netMsg->messageId; + return -1; +} + +PyObject* pyTTTMsg::UpcastToFinalTTTMsg() const +{ + if (!message) + PYTHON_RETURN_NONE; + switch (message->netMsg->messageId) + { + case kSrv2Cli_TTT_GameStarted: + return pyTTTGameStartedMsg::New(message); + case kSrv2Cli_TTT_GameOver: + return pyTTTGameOverMsg::New(message); + case kSrv2Cli_TTT_MoveMade: + return pyTTTMoveMadeMsg::New(message); + default: + PYTHON_RETURN_NONE; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// The different messages we can receive +// + +pyTTTGameStartedMsg::pyTTTGameStartedMsg(): pyTTTMsg() {} + +pyTTTGameStartedMsg::pyTTTGameStartedMsg(pfGameCliMsg* msg): pyTTTMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_TTT_GameStarted)) + message = nil; // wrong type, just clear it out +} + +bool pyTTTGameStartedMsg::YourTurn() const +{ + if (message) + { + const Srv2Cli_TTT_GameStarted* gmMsg = (const Srv2Cli_TTT_GameStarted*)message->netMsg; + return gmMsg->yourTurn; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +pyTTTGameOverMsg::pyTTTGameOverMsg(): pyTTTMsg() {} + +pyTTTGameOverMsg::pyTTTGameOverMsg(pfGameCliMsg* msg): pyTTTMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_TTT_GameOver)) + message = nil; // wrong type, just clear it out +} + +int pyTTTGameOverMsg::Result() const +{ + if (message) + { + const Srv2Cli_TTT_GameOver* gmMsg = (const Srv2Cli_TTT_GameOver*)message->netMsg; + return gmMsg->result; + } + return false; +} + +unsigned long pyTTTGameOverMsg::WinnerID() const +{ + if (message) + { + const Srv2Cli_TTT_GameOver* gmMsg = (const Srv2Cli_TTT_GameOver*)message->netMsg; + return gmMsg->winnerId; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +pyTTTMoveMadeMsg::pyTTTMoveMadeMsg(): pyTTTMsg() {} + +pyTTTMoveMadeMsg::pyTTTMoveMadeMsg(pfGameCliMsg* msg): pyTTTMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_TTT_MoveMade)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyTTTMoveMadeMsg::PlayerID() const +{ + if (message) + { + const Srv2Cli_TTT_MoveMade* gmMsg = (const Srv2Cli_TTT_MoveMade*)message->netMsg; + return gmMsg->playerId; + } + return false; +} + +int pyTTTMoveMadeMsg::Row() const +{ + if (message) + { + const Srv2Cli_TTT_MoveMade* gmMsg = (const Srv2Cli_TTT_MoveMade*)message->netMsg; + return gmMsg->row; + } + return false; +} + +int pyTTTMoveMadeMsg::Col() const +{ + if (message) + { + const Srv2Cli_TTT_MoveMade* gmMsg = (const Srv2Cli_TTT_MoveMade*)message->netMsg; + return gmMsg->col; + } + return false; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsg.h new file mode 100644 index 00000000..77b4e053 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsg.h @@ -0,0 +1,124 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyTTTMsg_h +#define pyTTTMsg_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyTTTMsg +// +// PURPOSE: Class wrapper for TTT game messages +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCliMsg.h" + +class pyTTTMsg : public pyGameCliMsg +{ +protected: + pyTTTMsg(); + pyTTTMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptTTTMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyTTTMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyTTTMsg); // converts a PyObject to a pyTTTMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + + int GetTTTMsgType() const; + + PyObject* UpcastToFinalTTTMsg() const; // returns the ttt message that this really is +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyTTTGameStartedMsg : public pyTTTMsg +{ +protected: + pyTTTGameStartedMsg(); + pyTTTGameStartedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptTTTGameStartedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyTTTGameStartedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyTTTGameStartedMsg); // converts a PyObject to a pyTTTGameStartedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + bool YourTurn() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyTTTGameOverMsg : public pyTTTMsg +{ +protected: + pyTTTGameOverMsg(); + pyTTTGameOverMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptTTTGameOverMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyTTTGameOverMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyTTTGameOverMsg); // converts a PyObject to a pyTTTGameOverMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + int Result() const; + unsigned long WinnerID() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyTTTMoveMadeMsg : public pyTTTMsg +{ +protected: + pyTTTMoveMadeMsg(); + pyTTTMoveMadeMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptTTTMoveMadeMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyTTTMoveMadeMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyTTTMoveMadeMsg); // converts a PyObject to a pyTTTMoveMadeMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long PlayerID() const; + int Row() const; + int Col() const; +}; + +#endif // pyTTTMsg_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsgGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsgGlue.cpp new file mode 100644 index 00000000..a9cf9ef0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/TicTacToe/pyTTTMsgGlue.cpp @@ -0,0 +1,231 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyTTTMsg.h" +#include "../../pyEnum.h" + +#include + +/////////////////////////////////////////////////////////////////////////////// +// +// Base TTT msg class +// + +PYTHON_CLASS_DEFINITION(ptTTTMsg, pyTTTMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptTTTMsg, pyTTTMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptTTTMsg) + +PYTHON_NO_INIT_DEFINITION(ptTTTMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptTTTMsg, getTTTMsgType) +{ + return PyInt_FromLong(self->fThis->GetTTTMsgType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptTTTMsg, upcastToFinalTTTMsg) +{ + return self->fThis->UpcastToFinalTTTMsg(); +} + +PYTHON_START_METHODS_TABLE(ptTTTMsg) + PYTHON_METHOD_NOARGS(ptTTTMsg, getTTTMsgType, "Returns the type of the TTT message (see PtTTTMsgTypes)"), + PYTHON_METHOD_NOARGS(ptTTTMsg, upcastToFinalTTTMsg, "Returns this message as the TTT msg it is"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptTTTMsg, pyGameCliMsg, "Base class for TicTacToe game messages"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptTTTMsg, pyTTTMsg); + +// required functions for PyObject interoperability +PyObject* pyTTTMsg::New(pfGameCliMsg* msg) +{ + ptTTTMsg *newObj = (ptTTTMsg*)ptTTTMsg_type.tp_new(&ptTTTMsg_type, NULL, NULL); + if (msg && (msg->gameCli->GetGameTypeId() == kGameTypeId_TicTacToe)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptTTTMsg, pyTTTMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptTTTMsg, pyTTTMsg) + +// Module and method definitions +void pyTTTMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptTTTMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyTTTMsg::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtTTTMsgTypes); + PYTHON_ENUM_ELEMENT(PtTTTMsgTypes, kTTTGameStarted, kSrv2Cli_TTT_GameStarted); + PYTHON_ENUM_ELEMENT(PtTTTMsgTypes, kTTTGameOver, kSrv2Cli_TTT_GameOver); + PYTHON_ENUM_ELEMENT(PtTTTMsgTypes, kTTTMoveMade, kSrv2Cli_TTT_MoveMade); + PYTHON_ENUM_END(m, PtTTTMsgTypes); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Game client message subclasses +// + +PYTHON_CLASS_DEFINITION(ptTTTGameStartedMsg, pyTTTGameStartedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptTTTGameStartedMsg, pyTTTGameStartedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptTTTGameStartedMsg) + +PYTHON_NO_INIT_DEFINITION(ptTTTGameStartedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptTTTGameStartedMsg, yourTurn) +{ + PYTHON_RETURN_BOOL(self->fThis->YourTurn()); +} + +PYTHON_START_METHODS_TABLE(ptTTTGameStartedMsg) + PYTHON_METHOD_NOARGS(ptTTTGameStartedMsg, yourTurn, "Returns true if you are the first player (and therefore it's your turn)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptTTTGameStartedMsg, pyTTTMsg, "TicTacToe message received when the game is started"); + +// required functions for PyObject interoperability +PyObject* pyTTTGameStartedMsg::New(pfGameCliMsg* msg) +{ + ptTTTGameStartedMsg *newObj = (ptTTTGameStartedMsg*)ptTTTGameStartedMsg_type.tp_new(&ptTTTGameStartedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_TTT_GameStarted)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptTTTGameStartedMsg, pyTTTGameStartedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptTTTGameStartedMsg, pyTTTGameStartedMsg) + +// Module and method definitions +void pyTTTGameStartedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptTTTGameStartedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptTTTGameOverMsg, pyTTTGameOverMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptTTTGameOverMsg, pyTTTGameOverMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptTTTGameOverMsg) + +PYTHON_NO_INIT_DEFINITION(ptTTTGameOverMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptTTTGameOverMsg, result) +{ + return PyInt_FromLong(self->fThis->Result()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptTTTGameOverMsg, winnerID) +{ + return PyLong_FromUnsignedLong(self->fThis->WinnerID()); +} + +PYTHON_START_METHODS_TABLE(ptTTTGameOverMsg) + PYTHON_METHOD_NOARGS(ptTTTGameOverMsg, result, "Returns the result of the game (see PtTTTGameResult)"), + PYTHON_METHOD_NOARGS(ptTTTGameOverMsg, winnerID, "Returns the winner's ID"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptTTTGameOverMsg, pyTTTMsg, "TicTacToe message received when the game is over"); + +// required functions for PyObject interoperability +PyObject* pyTTTGameOverMsg::New(pfGameCliMsg* msg) +{ + ptTTTGameOverMsg *newObj = (ptTTTGameOverMsg*)ptTTTGameOverMsg_type.tp_new(&ptTTTGameOverMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_TTT_GameOver)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptTTTGameOverMsg, pyTTTGameOverMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptTTTGameOverMsg, pyTTTGameOverMsg) + +// Module and method definitions +void pyTTTGameOverMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptTTTGameOverMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptTTTMoveMadeMsg, pyTTTMoveMadeMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptTTTMoveMadeMsg, pyTTTMoveMadeMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptTTTMoveMadeMsg) + +PYTHON_NO_INIT_DEFINITION(ptTTTMoveMadeMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptTTTMoveMadeMsg, playerID) +{ + return PyLong_FromUnsignedLong(self->fThis->PlayerID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptTTTMoveMadeMsg, row) +{ + return PyInt_FromLong(self->fThis->Row()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptTTTMoveMadeMsg, col) +{ + return PyInt_FromLong(self->fThis->Col()); +} + +PYTHON_START_METHODS_TABLE(ptTTTMoveMadeMsg) + PYTHON_METHOD_NOARGS(ptTTTMoveMadeMsg, playerID, "Returns the the ID of the player that just moved"), + PYTHON_METHOD_NOARGS(ptTTTMoveMadeMsg, row, "Returns the row index of the move (1..3)"), + PYTHON_METHOD_NOARGS(ptTTTMoveMadeMsg, col, "Returns the col index of the move (1..3)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptTTTMoveMadeMsg, pyTTTMsg, "TicTacToe message received when someone makes a move"); + +// required functions for PyObject interoperability +PyObject* pyTTTMoveMadeMsg::New(pfGameCliMsg* msg) +{ + ptTTTMoveMadeMsg *newObj = (ptTTTMoveMadeMsg*)ptTTTMoveMadeMsg_type.tp_new(&ptTTTMoveMadeMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_TTT_MoveMade)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptTTTMoveMadeMsg, pyTTTMoveMadeMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptTTTMoveMadeMsg, pyTTTMoveMadeMsg) + +// Module and method definitions +void pyTTTMoveMadeMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptTTTMoveMadeMsg); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGame.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGame.cpp new file mode 100644 index 00000000..23344efd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGame.cpp @@ -0,0 +1,99 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVarSyncGame.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base VarSync game client class +// + +const unsigned kGameId = 255; // random number that should be high enough to avoid collisions with other global games + +pyVarSyncGame::pyVarSyncGame(): pyGameCli() {} + +pyVarSyncGame::pyVarSyncGame(pfGameCli* client): pyGameCli(client) +{ + if (client && (client->GetGameTypeId() != kGameTypeId_VarSync)) + gameClient = nil; // wrong type, just clear it out +} + +bool pyVarSyncGame::IsVarSyncGame(std::wstring guid) +{ + Uuid gameUuid(guid.c_str()); + return gameUuid == kGameTypeId_VarSync; +} + +void pyVarSyncGame::JoinCommonVarSyncGame(pyKey& callbackKey) +{ + // NOTE: We don't let the player specify the game ID, because there should only be one of these in an age, ever + VarSync_CreateParam init; + pfGameMgr::GetInstance()->JoinCommonGame(callbackKey.getKey(), kGameTypeId_VarSync, kGameId, sizeof(init), &init); +} + +void pyVarSyncGame::SetStringVar(unsigned long id, std::wstring val) +{ + if (gameClient) + { + pfGmVarSync* vsync = pfGmVarSync::ConvertNoRef(gameClient); + vsync->SetStringVar(id, val.c_str()); + } +} + +void pyVarSyncGame::SetNumericVar(unsigned long id, double val) +{ + if (gameClient) + { + pfGmVarSync* vsync = pfGmVarSync::ConvertNoRef(gameClient); + vsync->SetNumericVar(id, val); + } +} + +void pyVarSyncGame::RequestAllVars() +{ + if (gameClient) + { + pfGmVarSync* vsync = pfGmVarSync::ConvertNoRef(gameClient); + vsync->RequestAllVars(); + } +} + +void pyVarSyncGame::CreateStringVar(std::wstring name, std::wstring val) +{ + if (gameClient) + { + pfGmVarSync* vsync = pfGmVarSync::ConvertNoRef(gameClient); + vsync->CreateStringVar(name.c_str(), val.c_str()); + } +} + +void pyVarSyncGame::CreateNumericVar(std::wstring name, double val) +{ + if (gameClient) + { + pfGmVarSync* vsync = pfGmVarSync::ConvertNoRef(gameClient); + vsync->CreateNumericVar(name.c_str(), val); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGame.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGame.h new file mode 100644 index 00000000..ccd744b3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGame.h @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyVarSyncGame_h +#define pyVarSyncGame_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyVarSyncGame +// +// PURPOSE: Class wrapper for the VarSync game client +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCli.h" +#include "../../pyKey.h" + +class pyVarSyncGame : public pyGameCli +{ +protected: + pyVarSyncGame(); + pyVarSyncGame(pfGameCli* client); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVarSyncGame); + static PyObject* New(pfGameCli* client); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVarSyncGame object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVarSyncGame); // converts a PyObject to a pyVarSyncGame (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaMethods(std::vector& methods); + + static bool IsVarSyncGame(std::wstring guid); + static void JoinCommonVarSyncGame(pyKey& callbackKey); + + void SetStringVar(unsigned long id, std::wstring val); + void SetNumericVar(unsigned long id, double val); + void RequestAllVars(); + void CreateStringVar(std::wstring name, std::wstring val); + void CreateNumericVar(std::wstring name, double val); +}; + +#endif // pyVarSyncGame_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGameGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGameGlue.cpp new file mode 100644 index 00000000..fcc2aec0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncGameGlue.cpp @@ -0,0 +1,308 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVarSyncGame.h" + +#include +#include "../../pyEnum.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base VarSync game client class +// + +PYTHON_CLASS_DEFINITION(ptVarSyncGame, pyVarSyncGame); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVarSyncGame, pyVarSyncGame) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVarSyncGame) + +PYTHON_NO_INIT_DEFINITION(ptVarSyncGame) + +PYTHON_GLOBAL_METHOD_DEFINITION(PtIsVarSyncGame, args, "Params: typeID\nReturns true if the specifed typeID (guid as a string) is a VarSync game") +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "PtIsVarSyncGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + bool retVal = pyVarSyncGame::IsVarSyncGame(text); + delete [] text; + PYTHON_RETURN_BOOL(retVal); + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(textObj); + wchar_t* wText = hsStringToWString(text); + bool retVal = pyVarSyncGame::IsVarSyncGame(wText); + delete [] wText; + PYTHON_RETURN_BOOL(retVal); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtIsVarSyncGame expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtJoinCommonVarSyncGame, args, "Params: callbackKey\nJoins the common VarSync game. If one doesn't exist, it creates it") +{ + PyObject* callbackObj = NULL; + if (!PyArg_ParseTuple(args, "O", &callbackObj)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinCommonVarSyncGame expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(callbackObj)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinCommonVarSyncGame expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(callbackObj); + pyVarSyncGame::JoinCommonVarSyncGame(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVarSyncGame, setStringVar, args) +{ + unsigned long id; + PyObject* valueObj = NULL; + if (!PyArg_ParseTuple(args, "kO", &id, &valueObj)) + { + PyErr_SetString(PyExc_TypeError, "setStringVar expects an unsigned long and a string"); + PYTHON_RETURN_ERROR; + } + + if (PyUnicode_Check(valueObj)) + { + int strLen = PyUnicode_GetSize(valueObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)valueObj, text, strLen); + text[strLen] = L'\0'; + self->fThis->SetStringVar(id, text); + delete [] text; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(valueObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(valueObj); + wchar_t* wText = hsStringToWString(text); + self->fThis->SetStringVar(id, wText); + delete [] wText; + PYTHON_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_TypeError, "setStringVar expects an unsigned long and a string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_METHOD_DEFINITION(ptVarSyncGame, setNumericVar, args) +{ + unsigned long id; + PyObject* valueObj = NULL; + if (!PyArg_ParseTuple(args, "kO", &id, &valueObj)) + { + PyErr_SetString(PyExc_TypeError, "setNumericVar expects an unsigned long and a number"); + PYTHON_RETURN_ERROR; + } + + double val = 0; + if (PyFloat_Check(valueObj)) + val = PyFloat_AsDouble(valueObj); + else if (PyInt_Check(valueObj)) + val = (double)PyInt_AsLong(valueObj); + else if (PyLong_Check(valueObj)) + val = PyLong_AsDouble(valueObj); + else + { + PyErr_SetString(PyExc_TypeError, "setNumericVar expects an unsigned long and a number"); + PYTHON_RETURN_ERROR; + } + + self->fThis->SetNumericVar(id, val); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptVarSyncGame, requestAllVars, RequestAllVars) + +PYTHON_METHOD_DEFINITION(ptVarSyncGame, createStringVar, args) +{ + PyObject* varNameObj = NULL; + PyObject* valueObj = NULL; + if (!PyArg_ParseTuple(args, "OO", &varNameObj, &valueObj)) + { + PyErr_SetString(PyExc_TypeError, "createStringVar expects two strings"); + PYTHON_RETURN_ERROR; + } + + std::wstring varName = L""; + if (PyUnicode_Check(varNameObj)) + { + int strLen = PyUnicode_GetSize(varNameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)varNameObj, text, strLen); + text[strLen] = L'\0'; + varName = text; + delete [] text; + } + else if (PyString_Check(varNameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(varNameObj); + wchar_t* wText = hsStringToWString(text); + varName = wText; + delete [] wText; + } + else + { + PyErr_SetString(PyExc_TypeError, "createStringVar expects two strings"); + PYTHON_RETURN_ERROR; + } + + std::wstring val = L""; + if (PyUnicode_Check(valueObj)) + { + int strLen = PyUnicode_GetSize(valueObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)valueObj, text, strLen); + text[strLen] = L'\0'; + val = text; + delete [] text; + } + else if (PyString_Check(valueObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(valueObj); + wchar_t* wText = hsStringToWString(text); + val = wText; + delete [] wText; + } + else + { + PyErr_SetString(PyExc_TypeError, "createStringVar expects two strings"); + PYTHON_RETURN_ERROR; + } + + self->fThis->CreateStringVar(varName, val); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVarSyncGame, createNumericVar, args) +{ + PyObject* varNameObj = NULL; + PyObject* valueObj = NULL; + if (!PyArg_ParseTuple(args, "OO", &varNameObj, &valueObj)) + { + PyErr_SetString(PyExc_TypeError, "createNumericVar expects a string and a number"); + PYTHON_RETURN_ERROR; + } + + std::wstring varName = L""; + if (PyUnicode_Check(varNameObj)) + { + int strLen = PyUnicode_GetSize(varNameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)varNameObj, text, strLen); + text[strLen] = L'\0'; + varName = text; + delete [] text; + } + else if (PyString_Check(varNameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(varNameObj); + wchar_t* wText = hsStringToWString(text); + varName = wText; + delete [] wText; + } + else + { + PyErr_SetString(PyExc_TypeError, "createNumericVar expects a string and a number"); + PYTHON_RETURN_ERROR; + } + + double val = 0; + if (PyFloat_Check(valueObj)) + val = PyFloat_AsDouble(valueObj); + else if (PyInt_Check(valueObj)) + val = (double)PyInt_AsLong(valueObj); + else if (PyLong_Check(valueObj)) + val = PyLong_AsDouble(valueObj); + else + { + PyErr_SetString(PyExc_TypeError, "createNumericVar expects a string and a number"); + PYTHON_RETURN_ERROR; + } + + self->fThis->CreateNumericVar(varName, val); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptVarSyncGame) + PYTHON_METHOD(ptVarSyncGame, setStringVar, "Params: varID, value\nAttempts to set a string variable to the specified string (clipped to 255 chars)"), + PYTHON_METHOD(ptVarSyncGame, setNumericVar, "Params: varID, value\nAttempts to set a numeric variable to the specified number (clipped to double)"), + PYTHON_BASIC_METHOD(ptVarSyncGame, requestAllVars, "Requests all the vars the server knows about"), + PYTHON_METHOD(ptVarSyncGame, createStringVar, "Params: varName, value\nAttempts to create a new string variable and set it to the specified string (clipped to 255 chars)"), + PYTHON_METHOD(ptVarSyncGame, createNumericVar, "Params: varName, value\nAttempts to create a new numeric variable and set it to the specified number (clipped to double)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVarSyncGame, pyGameCli, "Game client for the VarSync game"); + +// required functions for PyObject interoperability +PyObject* pyVarSyncGame::New(pfGameCli* client) +{ + ptVarSyncGame *newObj = (ptVarSyncGame*)ptVarSyncGame_type.tp_new(&ptVarSyncGame_type, NULL, NULL); + if (client && (client->GetGameTypeId() == kGameTypeId_VarSync)) + newObj->fThis->gameClient = client; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVarSyncGame, pyVarSyncGame) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVarSyncGame, pyVarSyncGame) + +// Module and method definitions +void pyVarSyncGame::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVarSyncGame); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyVarSyncGame::AddPlasmaMethods(std::vector& methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtIsVarSyncGame); + PYTHON_GLOBAL_METHOD(methods, PtJoinCommonVarSyncGame); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsg.cpp new file mode 100644 index 00000000..dc12d74b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsg.cpp @@ -0,0 +1,217 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVarSyncMsg.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base VarSync msg class +// + +pyVarSyncMsg::pyVarSyncMsg(): pyGameCliMsg() {} + +pyVarSyncMsg::pyVarSyncMsg(pfGameCliMsg* msg): pyGameCliMsg(msg) +{ + if (message && (message->gameCli->GetGameTypeId() != kGameTypeId_VarSync)) + message = nil; // wrong type, just clear it out +} + +int pyVarSyncMsg::GetVarSyncMsgType() const +{ + if (message) + return message->netMsg->messageId; + return -1; +} + +PyObject* pyVarSyncMsg::UpcastToFinalVarSyncMsg() const +{ + if (!message) + PYTHON_RETURN_NONE; + switch (message->netMsg->messageId) + { + case kSrv2Cli_VarSync_StringVarChanged: + return pyVarSyncStringVarChangedMsg::New(message); + case kSrv2Cli_VarSync_NumericVarChanged: + return pyVarSyncNumericVarChangedMsg::New(message); + case kSrv2Cli_VarSync_AllVarsSent: + return pyVarSyncAllVarsSentMsg::New(message); + case kSrv2Cli_VarSync_StringVarCreated: + return pyVarSyncStringVarCreatedMsg::New(message); + case kSrv2Cli_VarSync_NumericVarCreated: + return pyVarSyncNumericVarCreatedMsg::New(message); + default: + PYTHON_RETURN_NONE; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// The different messages we can receive +// + +pyVarSyncStringVarChangedMsg::pyVarSyncStringVarChangedMsg(): pyVarSyncMsg() {} + +pyVarSyncStringVarChangedMsg::pyVarSyncStringVarChangedMsg(pfGameCliMsg* msg): pyVarSyncMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_VarSync_StringVarChanged)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyVarSyncStringVarChangedMsg::ID() const +{ + if (message) + { + const Srv2Cli_VarSync_StringVarChanged* gmMsg = (const Srv2Cli_VarSync_StringVarChanged*)message->netMsg; + return gmMsg->varID; + } + return 0; +} + +std::wstring pyVarSyncStringVarChangedMsg::Value() const +{ + if (message) + { + const Srv2Cli_VarSync_StringVarChanged* gmMsg = (const Srv2Cli_VarSync_StringVarChanged*)message->netMsg; + return gmMsg->varValue; + } + return L""; +} + +/////////////////////////////////////////////////////////////////////////////// +pyVarSyncNumericVarChangedMsg::pyVarSyncNumericVarChangedMsg(): pyVarSyncMsg() {} + +pyVarSyncNumericVarChangedMsg::pyVarSyncNumericVarChangedMsg(pfGameCliMsg* msg): pyVarSyncMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_VarSync_NumericVarChanged)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyVarSyncNumericVarChangedMsg::ID() const +{ + if (message) + { + const Srv2Cli_VarSync_NumericVarChanged* gmMsg = (const Srv2Cli_VarSync_NumericVarChanged*)message->netMsg; + return gmMsg->varID; + } + return 0; +} + +double pyVarSyncNumericVarChangedMsg::Value() const +{ + if (message) + { + const Srv2Cli_VarSync_NumericVarChanged* gmMsg = (const Srv2Cli_VarSync_NumericVarChanged*)message->netMsg; + return gmMsg->varValue; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +pyVarSyncAllVarsSentMsg::pyVarSyncAllVarsSentMsg(): pyVarSyncMsg() {} + +pyVarSyncAllVarsSentMsg::pyVarSyncAllVarsSentMsg(pfGameCliMsg* msg): pyVarSyncMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_VarSync_AllVarsSent)) + message = nil; // wrong type, just clear it out +} + +/////////////////////////////////////////////////////////////////////////////// +pyVarSyncStringVarCreatedMsg::pyVarSyncStringVarCreatedMsg(): pyVarSyncMsg() {} + +pyVarSyncStringVarCreatedMsg::pyVarSyncStringVarCreatedMsg(pfGameCliMsg* msg): pyVarSyncMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_VarSync_StringVarCreated)) + message = nil; // wrong type, just clear it out +} + +std::wstring pyVarSyncStringVarCreatedMsg::Name() const +{ + if (message) + { + const Srv2Cli_VarSync_StringVarCreated* gmMsg = (const Srv2Cli_VarSync_StringVarCreated*)message->netMsg; + return gmMsg->varName; + } + return L""; +} + +unsigned long pyVarSyncStringVarCreatedMsg::ID() const +{ + if (message) + { + const Srv2Cli_VarSync_StringVarCreated* gmMsg = (const Srv2Cli_VarSync_StringVarCreated*)message->netMsg; + return gmMsg->varID; + } + return 0; +} + +std::wstring pyVarSyncStringVarCreatedMsg::Value() const +{ + if (message) + { + const Srv2Cli_VarSync_StringVarCreated* gmMsg = (const Srv2Cli_VarSync_StringVarCreated*)message->netMsg; + return gmMsg->varValue; + } + return L""; +} + +/////////////////////////////////////////////////////////////////////////////// +pyVarSyncNumericVarCreatedMsg::pyVarSyncNumericVarCreatedMsg(): pyVarSyncMsg() {} + +pyVarSyncNumericVarCreatedMsg::pyVarSyncNumericVarCreatedMsg(pfGameCliMsg* msg): pyVarSyncMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_VarSync_NumericVarCreated)) + message = nil; // wrong type, just clear it out +} + +std::wstring pyVarSyncNumericVarCreatedMsg::Name() const +{ + if (message) + { + const Srv2Cli_VarSync_NumericVarCreated* gmMsg = (const Srv2Cli_VarSync_NumericVarCreated*)message->netMsg; + return gmMsg->varName; + } + return L""; +} + +unsigned long pyVarSyncNumericVarCreatedMsg::ID() const +{ + if (message) + { + const Srv2Cli_VarSync_NumericVarCreated* gmMsg = (const Srv2Cli_VarSync_NumericVarCreated*)message->netMsg; + return gmMsg->varID; + } + return 0; +} + +double pyVarSyncNumericVarCreatedMsg::Value() const +{ + if (message) + { + const Srv2Cli_VarSync_NumericVarCreated* gmMsg = (const Srv2Cli_VarSync_NumericVarCreated*)message->netMsg; + return gmMsg->varValue; + } + return 0; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsg.h new file mode 100644 index 00000000..bb3fcca1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsg.h @@ -0,0 +1,163 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyVarSyncMsg_h +#define pyVarSyncMsg_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyVarSyncMsg +// +// PURPOSE: Class wrapper for VarSync game messages +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../../pyGlueHelpers.h" +#include "../pyGameCliMsg.h" + +class pyVarSyncMsg : public pyGameCliMsg +{ +protected: + pyVarSyncMsg(); + pyVarSyncMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptVarSyncMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVarSyncMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVarSyncMsg); // converts a PyObject to a pyVarSyncMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + + int GetVarSyncMsgType() const; + + PyObject* UpcastToFinalVarSyncMsg() const; // returns the VarSync message that this really is +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyVarSyncStringVarChangedMsg : public pyVarSyncMsg +{ +protected: + pyVarSyncStringVarChangedMsg(); + pyVarSyncStringVarChangedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVarSyncStringVarChangedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVarSyncStringVarChangedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVarSyncStringVarChangedMsg); // converts a PyObject to a pyVarSyncStringVarChangedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long ID() const; + std::wstring Value() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyVarSyncNumericVarChangedMsg : public pyVarSyncMsg +{ +protected: + pyVarSyncNumericVarChangedMsg(); + pyVarSyncNumericVarChangedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVarSyncNumericVarChangedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVarSyncNumericVarChangedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVarSyncNumericVarChangedMsg); // converts a PyObject to a pyVarSyncNumericVarChangedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long ID() const; + double Value() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyVarSyncAllVarsSentMsg : public pyVarSyncMsg +{ +protected: + pyVarSyncAllVarsSentMsg(); + pyVarSyncAllVarsSentMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVarSyncAllVarsSentMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVarSyncAllVarsSentMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVarSyncAllVarsSentMsg); // converts a PyObject to a pyVarSyncAllVarsSentMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyVarSyncStringVarCreatedMsg : public pyVarSyncMsg +{ +protected: + pyVarSyncStringVarCreatedMsg(); + pyVarSyncStringVarCreatedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVarSyncStringVarCreatedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVarSyncStringVarCreatedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVarSyncStringVarCreatedMsg); // converts a PyObject to a pyVarSyncStringVarCreatedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + std::wstring Name() const; + unsigned long ID() const; + std::wstring Value() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyVarSyncNumericVarCreatedMsg : public pyVarSyncMsg +{ +protected: + pyVarSyncNumericVarCreatedMsg(); + pyVarSyncNumericVarCreatedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVarSyncNumericVarCreatedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVarSyncNumericVarCreatedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVarSyncNumericVarCreatedMsg); // converts a PyObject to a pyVarSyncNumericVarCreatedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + std::wstring Name() const; + unsigned long ID() const; + double Value() const; +}; + +#endif // pyVarSyncMsg_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsgGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsgGlue.cpp new file mode 100644 index 00000000..8527c182 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/VarSync/pyVarSyncMsgGlue.cpp @@ -0,0 +1,329 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVarSyncMsg.h" +#include "../../pyEnum.h" + +#include + +/////////////////////////////////////////////////////////////////////////////// +// +// Base VarSync msg class +// + +PYTHON_CLASS_DEFINITION(ptVarSyncMsg, pyVarSyncMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVarSyncMsg, pyVarSyncMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVarSyncMsg) + +PYTHON_NO_INIT_DEFINITION(ptVarSyncMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncMsg, getVarSyncMsgType) +{ + return PyInt_FromLong(self->fThis->GetVarSyncMsgType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncMsg, upcastToFinalVarSyncMsg) +{ + return self->fThis->UpcastToFinalVarSyncMsg(); +} + +PYTHON_START_METHODS_TABLE(ptVarSyncMsg) + PYTHON_METHOD_NOARGS(ptVarSyncMsg, getVarSyncMsgType, "Returns the type of the VarSync message (see PtVarSyncMsgTypes)"), + PYTHON_METHOD_NOARGS(ptVarSyncMsg, upcastToFinalVarSyncMsg, "Returns this message as the VarSync msg it is"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVarSyncMsg, pyGameCliMsg, "Base class for VarSync game messages"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptVarSyncMsg, pyVarSyncMsg); + +// required functions for PyObject interoperability +PyObject* pyVarSyncMsg::New(pfGameCliMsg* msg) +{ + ptVarSyncMsg *newObj = (ptVarSyncMsg*)ptVarSyncMsg_type.tp_new(&ptVarSyncMsg_type, NULL, NULL); + if (msg && (msg->gameCli->GetGameTypeId() == kGameTypeId_VarSync)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVarSyncMsg, pyVarSyncMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVarSyncMsg, pyVarSyncMsg) + +// Module and method definitions +void pyVarSyncMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVarSyncMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyVarSyncMsg::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtVarSyncMsgTypes); + PYTHON_ENUM_ELEMENT(PtVarSyncMsgTypes, kVarSyncStringVarChanged, kSrv2Cli_VarSync_StringVarChanged); + PYTHON_ENUM_ELEMENT(PtVarSyncMsgTypes, kVarSyncNumericVarChanged, kSrv2Cli_VarSync_NumericVarChanged); + PYTHON_ENUM_ELEMENT(PtVarSyncMsgTypes, kVarSyncAllVarsSent, kSrv2Cli_VarSync_AllVarsSent); + PYTHON_ENUM_ELEMENT(PtVarSyncMsgTypes, kVarSyncStringVarCreated, kSrv2Cli_VarSync_StringVarCreated); + PYTHON_ENUM_ELEMENT(PtVarSyncMsgTypes, kVarSyncNumericVarCreated, kSrv2Cli_VarSync_NumericVarCreated); + PYTHON_ENUM_END(m, PtVarSyncMsgTypes); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Game client message subclasses +// + +PYTHON_CLASS_DEFINITION(ptVarSyncStringVarChangedMsg, pyVarSyncStringVarChangedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVarSyncStringVarChangedMsg, pyVarSyncStringVarChangedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVarSyncStringVarChangedMsg) + +PYTHON_NO_INIT_DEFINITION(ptVarSyncStringVarChangedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncStringVarChangedMsg, id) +{ + return PyLong_FromUnsignedLong(self->fThis->ID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncStringVarChangedMsg, value) +{ + std::wstring retVal = self->fThis->Value(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.size()); +} + +PYTHON_START_METHODS_TABLE(ptVarSyncStringVarChangedMsg) + PYTHON_METHOD_NOARGS(ptVarSyncStringVarChangedMsg, id, "Returns the id of the var that changed"), + PYTHON_METHOD_NOARGS(ptVarSyncStringVarChangedMsg, value, "Returns the variable's new value"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVarSyncStringVarChangedMsg, pyVarSyncMsg, "VarSync message received when a string variable's value changes"); + +// required functions for PyObject interoperability +PyObject* pyVarSyncStringVarChangedMsg::New(pfGameCliMsg* msg) +{ + ptVarSyncStringVarChangedMsg *newObj = (ptVarSyncStringVarChangedMsg*)ptVarSyncStringVarChangedMsg_type.tp_new(&ptVarSyncStringVarChangedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_VarSync_StringVarChanged)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVarSyncStringVarChangedMsg, pyVarSyncStringVarChangedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVarSyncStringVarChangedMsg, pyVarSyncStringVarChangedMsg) + +// Module and method definitions +void pyVarSyncStringVarChangedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVarSyncStringVarChangedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptVarSyncNumericVarChangedMsg, pyVarSyncNumericVarChangedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVarSyncNumericVarChangedMsg, pyVarSyncNumericVarChangedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVarSyncNumericVarChangedMsg) + +PYTHON_NO_INIT_DEFINITION(ptVarSyncNumericVarChangedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncNumericVarChangedMsg, id) +{ + return PyLong_FromUnsignedLong(self->fThis->ID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncNumericVarChangedMsg, value) +{ + return PyLong_FromDouble(self->fThis->Value()); +} + +PYTHON_START_METHODS_TABLE(ptVarSyncNumericVarChangedMsg) + PYTHON_METHOD_NOARGS(ptVarSyncNumericVarChangedMsg, id, "Returns the id of the var that changed"), + PYTHON_METHOD_NOARGS(ptVarSyncNumericVarChangedMsg, value, "Returns the variable's new value"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVarSyncNumericVarChangedMsg, pyVarSyncMsg, "VarSync message received when a numeric variable's value changes"); + +// required functions for PyObject interoperability +PyObject* pyVarSyncNumericVarChangedMsg::New(pfGameCliMsg* msg) +{ + ptVarSyncNumericVarChangedMsg *newObj = (ptVarSyncNumericVarChangedMsg*)ptVarSyncNumericVarChangedMsg_type.tp_new(&ptVarSyncNumericVarChangedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_VarSync_NumericVarChanged)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVarSyncNumericVarChangedMsg, pyVarSyncNumericVarChangedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVarSyncNumericVarChangedMsg, pyVarSyncNumericVarChangedMsg) + +// Module and method definitions +void pyVarSyncNumericVarChangedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVarSyncNumericVarChangedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptVarSyncAllVarsSentMsg, pyVarSyncAllVarsSentMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVarSyncAllVarsSentMsg, pyVarSyncAllVarsSentMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVarSyncAllVarsSentMsg) + +PYTHON_NO_INIT_DEFINITION(ptVarSyncAllVarsSentMsg) + +PYTHON_START_METHODS_TABLE(ptVarSyncAllVarsSentMsg) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVarSyncAllVarsSentMsg, pyVarSyncMsg, "VarSync message received after the last var is sent to you when you join the game, or request a list of vars"); + +// required functions for PyObject interoperability +PyObject* pyVarSyncAllVarsSentMsg::New(pfGameCliMsg* msg) +{ + ptVarSyncAllVarsSentMsg *newObj = (ptVarSyncAllVarsSentMsg*)ptVarSyncAllVarsSentMsg_type.tp_new(&ptVarSyncAllVarsSentMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_VarSync_AllVarsSent)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVarSyncAllVarsSentMsg, pyVarSyncAllVarsSentMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVarSyncAllVarsSentMsg, pyVarSyncAllVarsSentMsg) + +// Module and method definitions +void pyVarSyncAllVarsSentMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVarSyncAllVarsSentMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptVarSyncStringVarCreatedMsg, pyVarSyncStringVarCreatedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVarSyncStringVarCreatedMsg, pyVarSyncStringVarCreatedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVarSyncStringVarCreatedMsg) + +PYTHON_NO_INIT_DEFINITION(ptVarSyncStringVarCreatedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncStringVarCreatedMsg, name) +{ + std::wstring retVal = self->fThis->Name(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.size()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncStringVarCreatedMsg, id) +{ + return PyLong_FromUnsignedLong(self->fThis->ID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncStringVarCreatedMsg, value) +{ + std::wstring retVal = self->fThis->Value(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.size()); +} + +PYTHON_START_METHODS_TABLE(ptVarSyncStringVarCreatedMsg) + PYTHON_METHOD_NOARGS(ptVarSyncStringVarCreatedMsg, name, "Returns the name of the var that was created"), + PYTHON_METHOD_NOARGS(ptVarSyncStringVarCreatedMsg, id, "Returns the id that was assigned to this variable"), + PYTHON_METHOD_NOARGS(ptVarSyncStringVarCreatedMsg, value, "Returns the variable's new value"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVarSyncStringVarCreatedMsg, pyVarSyncMsg, "VarSync message received when a string variable is created and assigned an id"); + +// required functions for PyObject interoperability +PyObject* pyVarSyncStringVarCreatedMsg::New(pfGameCliMsg* msg) +{ + ptVarSyncStringVarCreatedMsg *newObj = (ptVarSyncStringVarCreatedMsg*)ptVarSyncStringVarCreatedMsg_type.tp_new(&ptVarSyncStringVarCreatedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_VarSync_StringVarCreated)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVarSyncStringVarCreatedMsg, pyVarSyncStringVarCreatedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVarSyncStringVarCreatedMsg, pyVarSyncStringVarCreatedMsg) + +// Module and method definitions +void pyVarSyncStringVarCreatedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVarSyncStringVarCreatedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptVarSyncNumericVarCreatedMsg, pyVarSyncNumericVarCreatedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVarSyncNumericVarCreatedMsg, pyVarSyncNumericVarCreatedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVarSyncNumericVarCreatedMsg) + +PYTHON_NO_INIT_DEFINITION(ptVarSyncNumericVarCreatedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncNumericVarCreatedMsg, name) +{ + std::wstring retVal = self->fThis->Name(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.size()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncNumericVarCreatedMsg, id) +{ + return PyLong_FromUnsignedLong(self->fThis->ID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVarSyncNumericVarCreatedMsg, value) +{ + return PyLong_FromDouble(self->fThis->Value()); +} + +PYTHON_START_METHODS_TABLE(ptVarSyncNumericVarCreatedMsg) + PYTHON_METHOD_NOARGS(ptVarSyncNumericVarCreatedMsg, name, "Returns the name of the var that was created"), + PYTHON_METHOD_NOARGS(ptVarSyncNumericVarCreatedMsg, id, "Returns the id assigned to this variable"), + PYTHON_METHOD_NOARGS(ptVarSyncNumericVarCreatedMsg, value, "Returns the variable's new value"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVarSyncNumericVarCreatedMsg, pyVarSyncMsg, "VarSync message received when a numeric variable is created and assigned an id"); + +// required functions for PyObject interoperability +PyObject* pyVarSyncNumericVarCreatedMsg::New(pfGameCliMsg* msg) +{ + ptVarSyncNumericVarCreatedMsg *newObj = (ptVarSyncNumericVarCreatedMsg*)ptVarSyncNumericVarCreatedMsg_type.tp_new(&ptVarSyncNumericVarCreatedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_VarSync_NumericVarCreated)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVarSyncNumericVarCreatedMsg, pyVarSyncNumericVarCreatedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVarSyncNumericVarCreatedMsg, pyVarSyncNumericVarCreatedMsg) + +// Module and method definitions +void pyVarSyncNumericVarCreatedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVarSyncNumericVarCreatedMsg); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCli.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCli.cpp new file mode 100644 index 00000000..0c21d4b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCli.cpp @@ -0,0 +1,163 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGameCli.h" + +#include "TicTacToe/pyTTTGame.h" +#include "Heek/pyHeekGame.h" +#include "Marker/pyMarkerGame.h" +#include "BlueSpiral/pyBlueSpiralGame.h" +#include "ClimbingWall/pyClimbingWallGame.h" +#include "VarSync/pyVarSyncGame.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base game client class +// + +pyGameCli::pyGameCli(): gameClient(nil) {} + +pyGameCli::pyGameCli(pfGameCli* client): gameClient(client) {} + +std::vector pyGameCli::GetGameIDs() +{ + ARRAY(unsigned) gameIDs; + std::vector retVal; + pfGameMgr::GetInstance()->GetGameIds(&gameIDs); + for (unsigned i = 0; i < gameIDs.Count(); ++i) + retVal.push_back(gameIDs[i]); + return retVal; +} + +PyObject* pyGameCli::GetGameCli(unsigned gameID) +{ + pfGameCli* client = pfGameMgr::GetInstance()->GetGameCli(gameID); + if (client) + return pyGameCli::New(client); + PYTHON_RETURN_NONE; +} + +std::wstring pyGameCli::GetGameNameByTypeID(std::wstring typeID) +{ + Uuid gameUuid(typeID.c_str()); + return pfGameMgr::GetInstance()->GetGameNameByTypeId(gameUuid); +} + +void pyGameCli::JoinGame(pyKey& callbackKey, unsigned gameID) +{ + pfGameMgr::GetInstance()->JoinGame(callbackKey.getKey(), gameID); +} + +unsigned pyGameCli::GameID() const +{ + if (gameClient) + return gameClient->GetGameId(); + return 0; +} + +std::wstring pyGameCli::GameTypeID() const +{ + if (gameClient) + { + wchar_t guidStr[256]; + GuidToString(gameClient->GetGameTypeId(), guidStr, arrsize(guidStr)); + return guidStr; + } + return L""; +} + +std::wstring pyGameCli::Name() const +{ + if (gameClient) + return gameClient->GetName(); + return L""; +} + +unsigned pyGameCli::PlayerCount() const +{ + if (gameClient) + return gameClient->GetPlayerCount(); + return 0; +} + +void pyGameCli::InvitePlayer(unsigned playerID) +{ + if (gameClient) + gameClient->InvitePlayer(playerID); +} + +void pyGameCli::UninvitePlayer(unsigned playerID) +{ + if (gameClient) + gameClient->UninvitePlayer(playerID); +} + +void pyGameCli::LeaveGame() +{ + if (gameClient) + gameClient->LeaveGame(); +} + +PyObject* pyGameCli::UpcastToTTTGame() +{ + if (gameClient && (gameClient->GetGameTypeId() == kGameTypeId_TicTacToe)) + return pyTTTGame::New(gameClient); + PYTHON_RETURN_NONE; +} + +PyObject* pyGameCli::UpcastToHeekGame() +{ + if (gameClient && (gameClient->GetGameTypeId() == kGameTypeId_Heek)) + return pyHeekGame::New(gameClient); + PYTHON_RETURN_NONE; +} + +PyObject* pyGameCli::UpcastToMarkerGame() +{ + if (gameClient && (gameClient->GetGameTypeId() == kGameTypeId_Marker)) + return pyMarkerGame::New(gameClient); + PYTHON_RETURN_NONE; +} + +PyObject* pyGameCli::UpcastToBlueSpiralGame() +{ + if (gameClient && (gameClient->GetGameTypeId() == kGameTypeId_BlueSpiral)) + return pyBlueSpiralGame::New(gameClient); + PYTHON_RETURN_NONE; +} + +PyObject* pyGameCli::UpcastToClimbingWallGame() +{ + if (gameClient && (gameClient->GetGameTypeId() == kGameTypeId_ClimbingWall)) + return pyClimbingWallGame::New(gameClient); + PYTHON_RETURN_NONE; +} + +PyObject* pyGameCli::UpcastToVarSyncGame() +{ + if (gameClient && (gameClient->GetGameTypeId() == kGameTypeId_VarSync)) + return pyVarSyncGame::New(gameClient); + PYTHON_RETURN_NONE; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCli.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCli.h new file mode 100644 index 00000000..2b74d767 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCli.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyGameCli_h +#define pyGameCli_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyGameCli +// +// PURPOSE: Class wrapper for the game client base class +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../pyGlueHelpers.h" +#include "../pyKey.h" + +class pyGameCli +{ +protected: + pfGameCli* gameClient; + + pyGameCli(); + pyGameCli(pfGameCli* client); + +public: + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptGameCli); + static PyObject* New(pfGameCli* client); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGameCli object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGameCli); // converts a PyObject to a pyGameCli (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaMethods(std::vector& methods); + + static std::vector GetGameIDs(); + static PyObject* GetGameCli(unsigned gameID); // returns a ptGameCli + static std::wstring GetGameNameByTypeID(std::wstring typeID); + static void JoinGame(pyKey& callbackKey, unsigned gameID); + + unsigned GameID() const; + std::wstring GameTypeID() const; + std::wstring Name() const; + unsigned PlayerCount() const; + + void InvitePlayer(unsigned playerID); + void UninvitePlayer(unsigned playerID); + + void LeaveGame(); + + PyObject* UpcastToTTTGame(); // returns ptTTTGame + PyObject* UpcastToHeekGame(); // returns ptHeekGame + PyObject* UpcastToMarkerGame(); // returns ptMarkerGame + PyObject* UpcastToBlueSpiralGame(); // returns ptBlueSpiralGame + PyObject* UpcastToClimbingWallGame(); // returns ptClimbingWallGame + PyObject* UpcastToVarSyncGame(); // returns ptVarSyncGame +}; + +#endif // pyGameCli_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliGlue.cpp new file mode 100644 index 00000000..d1725a41 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliGlue.cpp @@ -0,0 +1,239 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGameCli.h" +#include "../pyEnum.h" + +#include + +/////////////////////////////////////////////////////////////////////////////// +// +// Base game client class +// + +PYTHON_CLASS_DEFINITION(ptGameCli, pyGameCli); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGameCli, pyGameCli) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGameCli) + +PYTHON_NO_INIT_DEFINITION(ptGameCli) + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetGameIDs, "Returns a list of game IDs that the player is currently joined to") +{ + std::vector ids = pyGameCli::GetGameIDs(); + PyObject* retVal = PyList_New(ids.size()); + for (unsigned i = 0; i < ids.size(); ++i) + PyList_SetItem(retVal, i, PyInt_FromLong(ids[i])); + return retVal; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetGameCli, args, "Params: gameID\nReturns a ptGameCli associated with the specified id") +{ + int gameID = 0; + if (!PyArg_ParseTuple(args, "i", &gameID)) + { + PyErr_SetString(PyExc_TypeError, "PtGetGameCli expects an integer"); + PYTHON_RETURN_ERROR; + } + return pyGameCli::GetGameCli(gameID); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetGameNameByTypeID, args, "Params: guid\nReturns the name of the game represented by guid passed in as a string") +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "PtGetGameNameByTypeID expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + std::wstring retVal = pyGameCli::GetGameNameByTypeID(text); + delete [] text; + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(textObj); + wchar_t* wText = hsStringToWString(text); + std::wstring retVal = pyGameCli::GetGameNameByTypeID(wText); + delete [] wText; + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtGetGameNameByTypeID expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtJoinGame, args, "Params: callbackKey, gameID\nSends a join request to the specified game. Messages are sent to the callback key") +{ + PyObject* callbackObj = NULL; + int gameID = 0; + if (!PyArg_ParseTuple(args, "Oi", &callbackObj, &gameID)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinGame expects a ptKey and an integer"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(callbackObj)) + { + PyErr_SetString(PyExc_TypeError, "PtJoinGame expects a ptKey and an integer"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(callbackObj); + pyGameCli::JoinGame(*key, gameID); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCli, gameID) +{ + return PyInt_FromLong(self->fThis->GameID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCli, gameTypeID) +{ + std::wstring retVal = self->fThis->GameTypeID(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCli, name) +{ + std::wstring retVal = self->fThis->Name(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCli, playerCount) +{ + return PyInt_FromLong(self->fThis->PlayerCount()); +} + +PYTHON_METHOD_DEFINITION(ptGameCli, invitePlayer, args) +{ + int playerID = 0; + if (!PyArg_ParseTuple(args, "i", &playerID)) + { + PyErr_SetString(PyExc_TypeError, "invitePlayer expects an unsigned int"); + PYTHON_RETURN_ERROR; + } + self->fThis->InvitePlayer(playerID); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGameCli, uninvitePlayer, args) +{ + int playerID = 0; + if (!PyArg_ParseTuple(args, "i", &playerID)) + { + PyErr_SetString(PyExc_TypeError, "uninvitePlayer expects an unsigned int"); + PYTHON_RETURN_ERROR; + } + self->fThis->UninvitePlayer(playerID); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGameCli, leaveGame, LeaveGame) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCli, upcastToTTTGame) +{ + return self->fThis->UpcastToTTTGame(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCli, upcastToHeekGame) +{ + return self->fThis->UpcastToHeekGame(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCli, upcastToMarkerGame) +{ + return self->fThis->UpcastToMarkerGame(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCli, upcastToBlueSpiralGame) +{ + return self->fThis->UpcastToBlueSpiralGame(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCli, upcastToClimbingWallGame) +{ + return self->fThis->UpcastToClimbingWallGame(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCli, upcastToVarSyncGame) +{ + return self->fThis->UpcastToVarSyncGame(); +} + +PYTHON_START_METHODS_TABLE(ptGameCli) + PYTHON_METHOD_NOARGS(ptGameCli, gameID, "Returns the ID number for this game"), + PYTHON_METHOD_NOARGS(ptGameCli, gameTypeID, "Returns the game type ID for this game (as a guid string)"), + PYTHON_METHOD_NOARGS(ptGameCli, name, "Returns the name of the game"), + PYTHON_METHOD_NOARGS(ptGameCli, playerCount, "Returns the current number of players"), + PYTHON_METHOD(ptGameCli, invitePlayer, "Params: playerID\nInvites the specified player to join the game"), + PYTHON_METHOD(ptGameCli, uninvitePlayer, "Params: playerID\nRevokes the invitation for the specified player"), + PYTHON_BASIC_METHOD(ptGameCli, leaveGame, "Leaves this game"), + PYTHON_METHOD_NOARGS(ptGameCli, upcastToTTTGame, "Returns this game client as a ptTTTGame"), + PYTHON_METHOD_NOARGS(ptGameCli, upcastToHeekGame, "Returns this game client as a ptHeekGame"), + PYTHON_METHOD_NOARGS(ptGameCli, upcastToMarkerGame, "Returns this game client as a ptMarkerGame"), + PYTHON_METHOD_NOARGS(ptGameCli, upcastToBlueSpiralGame, "Returns this game client as a ptBlueSpiralGame"), + PYTHON_METHOD_NOARGS(ptGameCli, upcastToClimbingWallGame, "Returns this game client as a ptClimbingWallGame"), + PYTHON_METHOD_NOARGS(ptGameCli, upcastToVarSyncGame, "Returns this game client as a ptVarSyncGame"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptGameCli, "Base class for all game client interfaces"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptGameCli, pyGameCli); + +// required functions for PyObject interoperability +PyObject* pyGameCli::New(pfGameCli* client) +{ + ptGameCli *newObj = (ptGameCli*)ptGameCli_type.tp_new(&ptGameCli_type, NULL, NULL); + newObj->fThis->gameClient = client; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGameCli, pyGameCli) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGameCli, pyGameCli) + +// Module and method definitions +void pyGameCli::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGameCli); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyGameCli::AddPlasmaMethods(std::vector& methods) +{ + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetGameIDs); + PYTHON_GLOBAL_METHOD(methods, PtGetGameCli); + PYTHON_GLOBAL_METHOD(methods, PtGetGameNameByTypeID); + PYTHON_GLOBAL_METHOD(methods, PtJoinGame); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsg.cpp new file mode 100644 index 00000000..11e37722 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsg.cpp @@ -0,0 +1,226 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGameCliMsg.h" +#include "pyGameCli.h" + +#include "TicTacToe\pyTTTMsg.h" +#include "Heek\pyHeekMsg.h" +#include "Marker\pyMarkerMsg.h" +#include "BlueSpiral\pyBlueSpiralMsg.h" +#include "ClimbingWall\pyClimbingWallMsg.h" +#include "VarSync/pyVarSyncMsg.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base game cli msg class +// + +pyGameCliMsg::pyGameCliMsg(): message(nil) {} + +pyGameCliMsg::pyGameCliMsg(pfGameCliMsg* msg): message(msg) {} + +int pyGameCliMsg::GetType() const +{ + if (message) + { + switch (message->netMsg->messageId) + { + case kSrv2Cli_Game_PlayerJoined: + case kSrv2Cli_Game_PlayerLeft: + case kSrv2Cli_Game_InviteFailed: + case kSrv2Cli_Game_OwnerChange: + return message->netMsg->messageId; // just return the type straight up + } + + // if we get here, it's probably a game message, check the game guid + if (message->gameCli->GetGameTypeId() == kGameTypeId_TicTacToe) + return kPyGameCliTTTMsg; + + if (message->gameCli->GetGameTypeId() == kGameTypeId_Heek) + return kPyGameCliHeekMsg; + + if (message->gameCli->GetGameTypeId() == kGameTypeId_Marker) + return kPyGameCliMarkerMsg; + + if (message->gameCli->GetGameTypeId() == kGameTypeId_BlueSpiral) + return kPyGameCliBlueSpiralMsg; + + if (message->gameCli->GetGameTypeId() == kGameTypeId_ClimbingWall) + return kPyGameCliClimbingWallMsg; + + if (message->gameCli->GetGameTypeId() == kGameTypeId_VarSync) + return kPyGameCliVarSyncMsg; + } + return -1; +} + +PyObject* pyGameCliMsg::GetGameCli() const +{ + if (message && (message->gameCli)) + return pyGameCli::New(message->gameCli); + PYTHON_RETURN_NONE; +} + +PyObject* pyGameCliMsg::UpcastToFinalGameCliMsg() const +{ + if (!message) + PYTHON_RETURN_NONE; + switch (message->netMsg->messageId) + { + case kSrv2Cli_Game_PlayerJoined: + return pyGameCliPlayerJoinedMsg::New(message); + case kSrv2Cli_Game_PlayerLeft: + return pyGameCliPlayerLeftMsg::New(message); + case kSrv2Cli_Game_InviteFailed: + return pyGameCliInviteFailedMsg::New(message); + case kSrv2Cli_Game_OwnerChange: + return pyGameCliOwnerChangeMsg::New(message); + default: + PYTHON_RETURN_NONE; + } +} + +PyObject* pyGameCliMsg::UpcastToGameMsg() const +{ + if (!message) + PYTHON_RETURN_NONE; + + const Uuid& gameTypeId = message->gameCli->GetGameTypeId(); + if (gameTypeId == kGameTypeId_TicTacToe) + return pyTTTMsg::New(message); + else if (gameTypeId == kGameTypeId_Heek) + return pyHeekMsg::New(message); + else if (gameTypeId == kGameTypeId_Marker) + return pyMarkerMsg::New(message); + else if (gameTypeId == kGameTypeId_BlueSpiral) + return pyBlueSpiralMsg::New(message); + else if (gameTypeId == kGameTypeId_ClimbingWall) + return pyClimbingWallMsg::New(message); + else if (gameTypeId == kGameTypeId_VarSync) + return pyVarSyncMsg::New(message); + else + PYTHON_RETURN_NONE; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// The different messages we can receive +// + +pyGameCliPlayerJoinedMsg::pyGameCliPlayerJoinedMsg(): pyGameCliMsg() {} + +pyGameCliPlayerJoinedMsg::pyGameCliPlayerJoinedMsg(pfGameCliMsg* msg): pyGameCliMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Game_PlayerJoined)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyGameCliPlayerJoinedMsg::PlayerID() const +{ + if (message) + { + const Srv2Cli_Game_PlayerJoined* gmMsg = (const Srv2Cli_Game_PlayerJoined*)message->netMsg; + return gmMsg->playerId; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +pyGameCliPlayerLeftMsg::pyGameCliPlayerLeftMsg(): pyGameCliMsg() {} + +pyGameCliPlayerLeftMsg::pyGameCliPlayerLeftMsg(pfGameCliMsg* msg): pyGameCliMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Game_PlayerLeft)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyGameCliPlayerLeftMsg::PlayerID() const +{ + if (message) + { + const Srv2Cli_Game_PlayerLeft* gmMsg = (const Srv2Cli_Game_PlayerLeft*)message->netMsg; + return gmMsg->playerId; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +pyGameCliInviteFailedMsg::pyGameCliInviteFailedMsg(): pyGameCliMsg() {} + +pyGameCliInviteFailedMsg::pyGameCliInviteFailedMsg(pfGameCliMsg* msg): pyGameCliMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Game_InviteFailed)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyGameCliInviteFailedMsg::InviteeID() const +{ + if (message) + { + const Srv2Cli_Game_InviteFailed* gmMsg = (const Srv2Cli_Game_InviteFailed*)message->netMsg; + return gmMsg->inviteeId; + } + return 0; +} + +unsigned long pyGameCliInviteFailedMsg::OperationID() const +{ + if (message) + { + const Srv2Cli_Game_InviteFailed* gmMsg = (const Srv2Cli_Game_InviteFailed*)message->netMsg; + return gmMsg->operationId; + } + return 0; +} + +int pyGameCliInviteFailedMsg::Error() const +{ + if (message) + { + const Srv2Cli_Game_InviteFailed* gmMsg = (const Srv2Cli_Game_InviteFailed*)message->netMsg; + return gmMsg->error; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +pyGameCliOwnerChangeMsg::pyGameCliOwnerChangeMsg(): pyGameCliMsg() {} + +pyGameCliOwnerChangeMsg::pyGameCliOwnerChangeMsg(pfGameCliMsg* msg): pyGameCliMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_Game_OwnerChange)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyGameCliOwnerChangeMsg::OwnerID() const +{ + if (message) + { + const Srv2Cli_Game_OwnerChange* gmMsg = (const Srv2Cli_Game_OwnerChange*)message->netMsg; + return gmMsg->ownerId; + } + return 0; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsg.h new file mode 100644 index 00000000..79e384af --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsg.h @@ -0,0 +1,161 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyGameCliMsg_h +#define pyGameCliMsg_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyGameCliMsg +// +// PURPOSE: Class wrapper for game client messages +// + +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../pyGlueHelpers.h" + +class pyGameCliMsg +{ +protected: + pfGameCliMsg* message; + + pyGameCliMsg(); + pyGameCliMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptGameCliMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGameCliMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGameCliMsg); // converts a PyObject to a pyGameCliMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + + int GetType() const; + PyObject* GetGameCli() const; // returns ptGameCli + + PyObject* UpcastToFinalGameCliMsg() const; // returns it upcasted to player joined/left/invite/owner change message + PyObject* UpcastToGameMsg() const; // returns it upcasted to a game's message base class + + // for convenience, we define our own message types, one message type for each game, so that we can have + // a "base class" for each game's messages. Anything under kCli2Srv_NumGameMsgIds keeps their normal type + // (and is exposed to python here), but anything above it has the message code look at the game type and + // return the "message type" indicating what game the message is from + enum pyGameCliMsgType + { + kPyGameCliMsgTypeStart = kCli2Srv_NumGameMsgIds, + kPyGameCliTTTMsg, // Tick Tack Toe game messages + kPyGameCliHeekMsg, // Heek game messages + kPyGameCliMarkerMsg, // Marker game messages + kPyGameCliBlueSpiralMsg, // Blue Spiral game messages + kPyGameCliClimbingWallMsg, // Climbing Wall game messages + kPyGameCliVarSyncMsg, // Var Sync game messages + }; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyGameCliPlayerJoinedMsg : public pyGameCliMsg +{ +protected: + pyGameCliPlayerJoinedMsg(); + pyGameCliPlayerJoinedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGameCliPlayerJoinedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGameCliPlayerJoinedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGameCliPlayerJoinedMsg); // converts a PyObject to a pyGameCliPlayerJoinedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long PlayerID() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyGameCliPlayerLeftMsg : public pyGameCliMsg +{ +protected: + pyGameCliPlayerLeftMsg(); + pyGameCliPlayerLeftMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGameCliPlayerLeftMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGameCliPlayerLeftMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGameCliPlayerLeftMsg); // converts a PyObject to a pyGameCliPlayerLeftMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long PlayerID() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyGameCliInviteFailedMsg : public pyGameCliMsg +{ +protected: + pyGameCliInviteFailedMsg(); + pyGameCliInviteFailedMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGameCliInviteFailedMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGameCliInviteFailedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGameCliInviteFailedMsg); // converts a PyObject to a pyGameCliInviteFailedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + + unsigned long InviteeID() const; + unsigned long OperationID() const; + int Error() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyGameCliOwnerChangeMsg : public pyGameCliMsg +{ +protected: + pyGameCliOwnerChangeMsg(); + pyGameCliOwnerChangeMsg(pfGameCliMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGameCliOwnerChangeMsg); + static PyObject* New(pfGameCliMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGameCliOwnerChangeMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGameCliOwnerChangeMsg); // converts a PyObject to a pyGameCliOwnerChangeMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long OwnerID() const; +}; + +#endif // pyGameCliMsg_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsgGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsgGlue.cpp new file mode 100644 index 00000000..cd92680c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameCliMsgGlue.cpp @@ -0,0 +1,297 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGameCliMsg.h" +#include "../pyEnum.h" + +#include + +/////////////////////////////////////////////////////////////////////////////// +// +// Base game client msg class +// + +PYTHON_CLASS_DEFINITION(ptGameCliMsg, pyGameCliMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGameCliMsg, pyGameCliMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGameCliMsg) + +PYTHON_NO_INIT_DEFINITION(ptGameCliMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCliMsg, getType) +{ + return PyInt_FromLong(self->fThis->GetType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCliMsg, getGameCli) +{ + return self->fThis->GetGameCli(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCliMsg, upcastToFinalGameCliMsg) +{ + return self->fThis->UpcastToFinalGameCliMsg(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCliMsg, upcastToGameMsg) +{ + return self->fThis->UpcastToGameMsg(); +} + +PYTHON_START_METHODS_TABLE(ptGameCliMsg) + PYTHON_METHOD_NOARGS(ptGameCliMsg, getType, "Returns the type of the message (see PtGameCliMsgTypes)"), + PYTHON_METHOD_NOARGS(ptGameCliMsg, getGameCli, "Returns the game client associated with this message"), + PYTHON_METHOD_NOARGS(ptGameCliMsg, upcastToFinalGameCliMsg, "Returns this message as the game client message it is (player joined, player left, invite failed, or owner change)"), + PYTHON_METHOD_NOARGS(ptGameCliMsg, upcastToGameMsg, "Returns this message as the base class of message for the game it is associated with (ttt, heek, marker, etc)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptGameCliMsg, "Message from the game server from a game"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptGameCliMsg, pyGameCliMsg); + +// required functions for PyObject interoperability +PyObject* pyGameCliMsg::New(pfGameCliMsg* msg) +{ + ptGameCliMsg *newObj = (ptGameCliMsg*)ptGameCliMsg_type.tp_new(&ptGameCliMsg_type, NULL, NULL); + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGameCliMsg, pyGameCliMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGameCliMsg, pyGameCliMsg) + +// Module and method definitions +void pyGameCliMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGameCliMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyGameCliMsg::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtGameCliMsgTypes); + PYTHON_ENUM_ELEMENT(PtGameCliMsgTypes, kGameCliPlayerJoinedMsg, kSrv2Cli_Game_PlayerJoined); + PYTHON_ENUM_ELEMENT(PtGameCliMsgTypes, kGameCliPlayerLeftMsg, kSrv2Cli_Game_PlayerLeft); + PYTHON_ENUM_ELEMENT(PtGameCliMsgTypes, kGameCliInviteFailedMsg, kSrv2Cli_Game_InviteFailed); + PYTHON_ENUM_ELEMENT(PtGameCliMsgTypes, kGameCliOwnerChangeMsg, kSrv2Cli_Game_OwnerChange); + PYTHON_ENUM_ELEMENT(PtGameCliMsgTypes, kGameCliTTTMsg, kPyGameCliTTTMsg); + PYTHON_ENUM_ELEMENT(PtGameCliMsgTypes, kGameCliHeekMsg, kPyGameCliHeekMsg); + PYTHON_ENUM_ELEMENT(PtGameCliMsgTypes, kGameCliMarkerMsg, kPyGameCliMarkerMsg); + PYTHON_ENUM_ELEMENT(PtGameCliMsgTypes, kGameCliBlueSpiralMsg, kPyGameCliBlueSpiralMsg); + PYTHON_ENUM_ELEMENT(PtGameCliMsgTypes, kGameCliClimbingWallMsg, kPyGameCliClimbingWallMsg); + PYTHON_ENUM_ELEMENT(PtGameCliMsgTypes, kGameCliVarSyncMsg, kPyGameCliVarSyncMsg); + PYTHON_ENUM_END(m, PtGameCliMsgTypes); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Game client message subclasses +// + +PYTHON_CLASS_DEFINITION(ptGameCliPlayerJoinedMsg, pyGameCliPlayerJoinedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGameCliPlayerJoinedMsg, pyGameCliPlayerJoinedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGameCliPlayerJoinedMsg) + +PYTHON_NO_INIT_DEFINITION(ptGameCliPlayerJoinedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCliPlayerJoinedMsg, playerID) +{ + return PyLong_FromUnsignedLong(self->fThis->PlayerID()); +} + +PYTHON_START_METHODS_TABLE(ptGameCliPlayerJoinedMsg) + PYTHON_METHOD_NOARGS(ptGameCliPlayerJoinedMsg, playerID, "Returns the player's ID number"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGameCliPlayerJoinedMsg, pyGameCliMsg, "Game client message when a player joined message is received"); + +// required functions for PyObject interoperability +PyObject* pyGameCliPlayerJoinedMsg::New(pfGameCliMsg* msg) +{ + ptGameCliPlayerJoinedMsg *newObj = (ptGameCliPlayerJoinedMsg*)ptGameCliPlayerJoinedMsg_type.tp_new(&ptGameCliPlayerJoinedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Game_PlayerJoined)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGameCliPlayerJoinedMsg, pyGameCliPlayerJoinedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGameCliPlayerJoinedMsg, pyGameCliPlayerJoinedMsg) + +// Module and method definitions +void pyGameCliPlayerJoinedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGameCliPlayerJoinedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptGameCliPlayerLeftMsg, pyGameCliPlayerLeftMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGameCliPlayerLeftMsg, pyGameCliPlayerLeftMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGameCliPlayerLeftMsg) + +PYTHON_NO_INIT_DEFINITION(ptGameCliPlayerLeftMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCliPlayerLeftMsg, playerID) +{ + return PyLong_FromUnsignedLong(self->fThis->PlayerID()); +} + +PYTHON_START_METHODS_TABLE(ptGameCliPlayerLeftMsg) + PYTHON_METHOD_NOARGS(ptGameCliPlayerLeftMsg, playerID, "Returns the player's ID number"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGameCliPlayerLeftMsg, pyGameCliMsg, "Game client message when a player left message is received"); + +// required functions for PyObject interoperability +PyObject* pyGameCliPlayerLeftMsg::New(pfGameCliMsg* msg) +{ + ptGameCliPlayerLeftMsg *newObj = (ptGameCliPlayerLeftMsg*)ptGameCliPlayerLeftMsg_type.tp_new(&ptGameCliPlayerLeftMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Game_PlayerLeft)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGameCliPlayerLeftMsg, pyGameCliPlayerLeftMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGameCliPlayerLeftMsg, pyGameCliPlayerLeftMsg) + +// Module and method definitions +void pyGameCliPlayerLeftMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGameCliPlayerLeftMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptGameCliInviteFailedMsg, pyGameCliInviteFailedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGameCliInviteFailedMsg, pyGameCliInviteFailedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGameCliInviteFailedMsg) + +PYTHON_NO_INIT_DEFINITION(ptGameCliInviteFailedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCliInviteFailedMsg, inviteeID) +{ + return PyLong_FromUnsignedLong(self->fThis->InviteeID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCliInviteFailedMsg, operationID) +{ + return PyLong_FromUnsignedLong(self->fThis->OperationID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCliInviteFailedMsg, error) +{ + return PyLong_FromLong(self->fThis->Error()); +} + +PYTHON_START_METHODS_TABLE(ptGameCliInviteFailedMsg) + PYTHON_METHOD_NOARGS(ptGameCliInviteFailedMsg, inviteeID, "Returns the invitee's ID number"), + PYTHON_METHOD_NOARGS(ptGameCliInviteFailedMsg, operationID, "Returns the operation's ID number"), + PYTHON_METHOD_NOARGS(ptGameCliInviteFailedMsg, error, "Returns the error value (See PtGameCliInviteErrors)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGameCliInviteFailedMsg, pyGameCliMsg, "Game client message when an invite failed message is received"); + +// required functions for PyObject interoperability +PyObject* pyGameCliInviteFailedMsg::New(pfGameCliMsg* msg) +{ + ptGameCliInviteFailedMsg *newObj = (ptGameCliInviteFailedMsg*)ptGameCliInviteFailedMsg_type.tp_new(&ptGameCliInviteFailedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Game_InviteFailed)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGameCliInviteFailedMsg, pyGameCliInviteFailedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGameCliInviteFailedMsg, pyGameCliInviteFailedMsg) + +// Module and method definitions +void pyGameCliInviteFailedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGameCliInviteFailedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyGameCliInviteFailedMsg::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtGameCliInviteErrors); + PYTHON_ENUM_ELEMENT(PtGameCliInviteErrors, kGameInviteSuccess, kGameInviteSuccess); + PYTHON_ENUM_ELEMENT(PtGameCliInviteErrors, kGameInviteErrNotOwner, kGameInviteErrNotOwner); + PYTHON_ENUM_ELEMENT(PtGameCliInviteErrors, kGameInviteErrAlreadyInvited, kGameInviteErrAlreadyInvited); + PYTHON_ENUM_ELEMENT(PtGameCliInviteErrors, kGameInviteErrAlreadyJoined, kGameInviteErrAlreadyJoined); + PYTHON_ENUM_ELEMENT(PtGameCliInviteErrors, kGameInviteErrGameStarted, kGameInviteErrGameStarted); + PYTHON_ENUM_ELEMENT(PtGameCliInviteErrors, kGameInviteErrGameOver, kGameInviteErrGameOver); + PYTHON_ENUM_ELEMENT(PtGameCliInviteErrors, kGameInviteErrGameFull, kGameInviteErrGameFull); + PYTHON_ENUM_ELEMENT(PtGameCliInviteErrors, kGameInviteErrNoJoin, kGameInviteErrNoJoin); + PYTHON_ENUM_END(m, PtGameCliInviteErrors); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptGameCliOwnerChangeMsg, pyGameCliOwnerChangeMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGameCliOwnerChangeMsg, pyGameCliOwnerChangeMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGameCliOwnerChangeMsg) + +PYTHON_NO_INIT_DEFINITION(ptGameCliOwnerChangeMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameCliOwnerChangeMsg, ownerID) +{ + return PyLong_FromUnsignedLong(self->fThis->OwnerID()); +} + +PYTHON_START_METHODS_TABLE(ptGameCliOwnerChangeMsg) + PYTHON_METHOD_NOARGS(ptGameCliOwnerChangeMsg, ownerID, "Returns the owner's ID number"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGameCliOwnerChangeMsg, pyGameCliMsg, "Game client message when a owner change message is received"); + +// required functions for PyObject interoperability +PyObject* pyGameCliOwnerChangeMsg::New(pfGameCliMsg* msg) +{ + ptGameCliOwnerChangeMsg *newObj = (ptGameCliOwnerChangeMsg*)ptGameCliOwnerChangeMsg_type.tp_new(&ptGameCliOwnerChangeMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_Game_OwnerChange)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGameCliOwnerChangeMsg, pyGameCliOwnerChangeMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGameCliOwnerChangeMsg, pyGameCliOwnerChangeMsg) + +// Module and method definitions +void pyGameCliOwnerChangeMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGameCliOwnerChangeMsg); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsg.cpp new file mode 100644 index 00000000..8f9e6f0c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsg.cpp @@ -0,0 +1,146 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGameMgrMsg.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// Base game mgr msg class +// + +pyGameMgrMsg::pyGameMgrMsg(): message(nil) {} + +pyGameMgrMsg::pyGameMgrMsg(pfGameMgrMsg* msg): message(msg) {} + +int pyGameMgrMsg::GetType() const +{ + if (message) + return message->netMsg->messageId; + return -1; +} + +PyObject* pyGameMgrMsg::UpcastToInviteReceivedMsg() const +{ + if (!message) + PYTHON_RETURN_NONE; + if (message->netMsg->messageId != kSrv2Cli_GameMgr_InviteReceived) + PYTHON_RETURN_NONE; + return pyGameMgrInviteReceivedMsg::New(message); +} + +PyObject* pyGameMgrMsg::UpcastToInviteRevokedMsg() const +{ + if (!message) + PYTHON_RETURN_NONE; + if (message->netMsg->messageId != kSrv2Cli_GameMgr_InviteRevoked) + PYTHON_RETURN_NONE; + return pyGameMgrInviteRevokedMsg::New(message); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// The different messages we can receive +// + +pyGameMgrInviteReceivedMsg::pyGameMgrInviteReceivedMsg(): pyGameMgrMsg() {} + +pyGameMgrInviteReceivedMsg::pyGameMgrInviteReceivedMsg(pfGameMgrMsg* msg): pyGameMgrMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_GameMgr_InviteReceived)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyGameMgrInviteReceivedMsg::InviterID() const +{ + if (message) + { + const Srv2Cli_GameMgr_InviteReceived* gmMsg = (const Srv2Cli_GameMgr_InviteReceived*)message->netMsg; + return gmMsg->inviterId; + } + return 0; +} + +std::wstring pyGameMgrInviteReceivedMsg::GameTypeID() const +{ + if (message) + { + const Srv2Cli_GameMgr_InviteReceived* gmMsg = (const Srv2Cli_GameMgr_InviteReceived*)message->netMsg; + wchar_t buffer[256]; + GuidToString(gmMsg->gameTypeId, buffer, arrsize(buffer)); + return buffer; + } + return L""; +} + +unsigned long pyGameMgrInviteReceivedMsg::NewGameID() const +{ + if (message) + { + const Srv2Cli_GameMgr_InviteReceived* gmMsg = (const Srv2Cli_GameMgr_InviteReceived*)message->netMsg; + return gmMsg->newGameId; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +pyGameMgrInviteRevokedMsg::pyGameMgrInviteRevokedMsg(): pyGameMgrMsg() {} + +pyGameMgrInviteRevokedMsg::pyGameMgrInviteRevokedMsg(pfGameMgrMsg* msg): pyGameMgrMsg(msg) +{ + if (message && (message->netMsg->messageId != kSrv2Cli_GameMgr_InviteRevoked)) + message = nil; // wrong type, just clear it out +} + +unsigned long pyGameMgrInviteRevokedMsg::InviterID() const +{ + if (message) + { + const Srv2Cli_GameMgr_InviteRevoked* gmMsg = (const Srv2Cli_GameMgr_InviteRevoked*)message->netMsg; + return gmMsg->inviterId; + } + return 0; +} + +std::wstring pyGameMgrInviteRevokedMsg::GameTypeID() const +{ + if (message) + { + const Srv2Cli_GameMgr_InviteRevoked* gmMsg = (const Srv2Cli_GameMgr_InviteRevoked*)message->netMsg; + wchar_t buffer[256]; + GuidToString(gmMsg->gameTypeId, buffer, arrsize(buffer)); + return buffer; + } + return L""; +} + +unsigned long pyGameMgrInviteRevokedMsg::NewGameID() const +{ + if (message) + { + const Srv2Cli_GameMgr_InviteRevoked* gmMsg = (const Srv2Cli_GameMgr_InviteRevoked*)message->netMsg; + return gmMsg->newGameId; + } + return 0; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsg.h new file mode 100644 index 00000000..48f8f3a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsg.h @@ -0,0 +1,109 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyGameMgrMsg_h +#define pyGameMgrMsg_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyGameMgrMsg +// +// PURPOSE: Class wrapper for game manager messages +// + +#include "hsStlUtils.h" +#include "../pfGameMgr/pfGameMgr.h" + +#include +#include "../pyGlueHelpers.h" + +class pyGameMgrMsg +{ +protected: + pfGameMgrMsg* message; + + pyGameMgrMsg(); + pyGameMgrMsg(pfGameMgrMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptGameMgrMsg); + static PyObject* New(pfGameMgrMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGameMgrMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGameMgrMsg); // converts a PyObject to a pyGameMgrMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + static void AddPlasmaConstantsClasses(PyObject* m); + + int GetType() const; + + PyObject* UpcastToInviteReceivedMsg() const; // returns ptGameMgrInviteReceivedMsg + PyObject* UpcastToInviteRevokedMsg() const; // returns ptGameMgrInviteRevokedMsg +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyGameMgrInviteReceivedMsg : public pyGameMgrMsg +{ +protected: + pyGameMgrInviteReceivedMsg(); + pyGameMgrInviteReceivedMsg(pfGameMgrMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGameMgrInviteReceivedMsg); + static PyObject* New(pfGameMgrMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGameMgrInviteReceivedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGameMgrInviteReceivedMsg); // converts a PyObject to a pyGameMgrInviteReceivedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long InviterID() const; + std::wstring GameTypeID() const; + unsigned long NewGameID() const; +}; + +/////////////////////////////////////////////////////////////////////////////// +class pyGameMgrInviteRevokedMsg : public pyGameMgrMsg +{ +protected: + pyGameMgrInviteRevokedMsg(); + pyGameMgrInviteRevokedMsg(pfGameMgrMsg* msg); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGameMgrInviteRevokedMsg); + static PyObject* New(pfGameMgrMsg* msg); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGameMgrInviteRevokedMsg object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGameMgrInviteRevokedMsg); // converts a PyObject to a pyGameMgrInviteRevokedMsg (throws error if not correct type) + + static void AddPlasmaClasses(PyObject* m); + + unsigned long InviterID() const; + std::wstring GameTypeID() const; + unsigned long NewGameID() const; +}; + +#endif // pyGameMgrMsg_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsgGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsgGlue.cpp new file mode 100644 index 00000000..8bfadf89 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/Games/pyGameMgrMsgGlue.cpp @@ -0,0 +1,203 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGameMgrMsg.h" +#include "../pyEnum.h" + +#include + +/////////////////////////////////////////////////////////////////////////////// +// +// Base game manager msg class +// + +PYTHON_CLASS_DEFINITION(ptGameMgrMsg, pyGameMgrMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGameMgrMsg, pyGameMgrMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGameMgrMsg) + +PYTHON_NO_INIT_DEFINITION(ptGameMgrMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameMgrMsg, getType) +{ + return PyInt_FromLong(self->fThis->GetType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameMgrMsg, upcastToInviteReceivedMsg) +{ + return self->fThis->UpcastToInviteReceivedMsg(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameMgrMsg, upcastToInviteRevokedMsg) +{ + return self->fThis->UpcastToInviteRevokedMsg(); +} + +PYTHON_START_METHODS_TABLE(ptGameMgrMsg) + PYTHON_METHOD_NOARGS(ptGameMgrMsg, getType, "Returns the type of the message (see PtGameMgrMsgTypes)"), + PYTHON_METHOD_NOARGS(ptGameMgrMsg, upcastToInviteReceivedMsg, "Returns this message as a ptGameMgrInviteReceivedMsg"), + PYTHON_METHOD_NOARGS(ptGameMgrMsg, upcastToInviteRevokedMsg, "Returns this message as a ptGameMgrInviteRevokedMsg"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptGameMgrMsg, "Message from the game manager"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptGameMgrMsg, pyGameMgrMsg); + +// required functions for PyObject interoperability +PyObject* pyGameMgrMsg::New(pfGameMgrMsg* msg) +{ + ptGameMgrMsg *newObj = (ptGameMgrMsg*)ptGameMgrMsg_type.tp_new(&ptGameMgrMsg_type, NULL, NULL); + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGameMgrMsg, pyGameMgrMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGameMgrMsg, pyGameMgrMsg) + +// Module and method definitions +void pyGameMgrMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGameMgrMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyGameMgrMsg::AddPlasmaConstantsClasses(PyObject* m) +{ + PYTHON_ENUM_START(PtGameMgrMsgTypes); + PYTHON_ENUM_ELEMENT(PtGameMgrMsgTypes, kGameMgrInviteReceivedMsg, kSrv2Cli_GameMgr_InviteReceived); + PYTHON_ENUM_ELEMENT(PtGameMgrMsgTypes, kGameMgrInviteRevokedMsg, kSrv2Cli_GameMgr_InviteRevoked); + PYTHON_ENUM_END(m, PtGameMgrMsgTypes); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Game manager message subclasses +// + +PYTHON_CLASS_DEFINITION(ptGameMgrInviteReceivedMsg, pyGameMgrInviteReceivedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGameMgrInviteReceivedMsg, pyGameMgrInviteReceivedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGameMgrInviteReceivedMsg) + +PYTHON_NO_INIT_DEFINITION(ptGameMgrInviteReceivedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameMgrInviteReceivedMsg, inviterID) +{ + return PyLong_FromUnsignedLong(self->fThis->InviterID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameMgrInviteReceivedMsg, gameTypeID) +{ + std::wstring retVal = self->fThis->GameTypeID(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameMgrInviteReceivedMsg, newGameID) +{ + return PyLong_FromUnsignedLong(self->fThis->NewGameID()); +} + +PYTHON_START_METHODS_TABLE(ptGameMgrInviteReceivedMsg) + PYTHON_METHOD_NOARGS(ptGameMgrInviteReceivedMsg, inviterID, "Returns the inviter's ID number"), + PYTHON_METHOD_NOARGS(ptGameMgrInviteReceivedMsg, gameTypeID, "Returns the game type ID (as a guid string)"), + PYTHON_METHOD_NOARGS(ptGameMgrInviteReceivedMsg, newGameID, "Returns the new game's ID number"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGameMgrInviteReceivedMsg, pyGameMgrMsg, "Game manager message when an invite is received"); + +// required functions for PyObject interoperability +PyObject* pyGameMgrInviteReceivedMsg::New(pfGameMgrMsg* msg) +{ + ptGameMgrInviteReceivedMsg *newObj = (ptGameMgrInviteReceivedMsg*)ptGameMgrInviteReceivedMsg_type.tp_new(&ptGameMgrInviteReceivedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_GameMgr_InviteReceived)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGameMgrInviteReceivedMsg, pyGameMgrInviteReceivedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGameMgrInviteReceivedMsg, pyGameMgrInviteReceivedMsg) + +// Module and method definitions +void pyGameMgrInviteReceivedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGameMgrInviteReceivedMsg); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// +PYTHON_CLASS_DEFINITION(ptGameMgrInviteRevokedMsg, pyGameMgrInviteRevokedMsg); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGameMgrInviteRevokedMsg, pyGameMgrInviteRevokedMsg) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGameMgrInviteRevokedMsg) + +PYTHON_NO_INIT_DEFINITION(ptGameMgrInviteRevokedMsg) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameMgrInviteRevokedMsg, inviterID) +{ + return PyLong_FromUnsignedLong(self->fThis->InviterID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameMgrInviteRevokedMsg, gameTypeID) +{ + std::wstring retVal = self->fThis->GameTypeID(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameMgrInviteRevokedMsg, newGameID) +{ + return PyLong_FromUnsignedLong(self->fThis->NewGameID()); +} + +PYTHON_START_METHODS_TABLE(ptGameMgrInviteRevokedMsg) + PYTHON_METHOD_NOARGS(ptGameMgrInviteRevokedMsg, inviterID, "Returns the inviter's ID number"), + PYTHON_METHOD_NOARGS(ptGameMgrInviteRevokedMsg, gameTypeID, "Returns the game type ID (as a guid string)"), + PYTHON_METHOD_NOARGS(ptGameMgrInviteRevokedMsg, newGameID, "Returns the new game's ID number"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGameMgrInviteRevokedMsg, pyGameMgrMsg, "Game manager message when an invite is received"); + +// required functions for PyObject interoperability +PyObject* pyGameMgrInviteRevokedMsg::New(pfGameMgrMsg* msg) +{ + ptGameMgrInviteRevokedMsg *newObj = (ptGameMgrInviteRevokedMsg*)ptGameMgrInviteRevokedMsg_type.tp_new(&ptGameMgrInviteRevokedMsg_type, NULL, NULL); + if (msg && (msg->netMsg->messageId == kSrv2Cli_GameMgr_InviteRevoked)) + newObj->fThis->message = msg; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGameMgrInviteRevokedMsg, pyGameMgrInviteRevokedMsg) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGameMgrInviteRevokedMsg, pyGameMgrInviteRevokedMsg) + +// Module and method definitions +void pyGameMgrInviteRevokedMsg::AddPlasmaClasses(PyObject* m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGameMgrInviteRevokedMsg); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagement.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagement.cpp new file mode 100644 index 00000000..2a62ae66 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagement.cpp @@ -0,0 +1,125 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +///////////////////////////////////////////////////////////////////////////// +// +// NAME: cyAccountManagement +// +// PURPOSE: Python wrapper for account management functions +// + +#include "cyAccountManagement.h" + +#include "../plNetClientComm/plNetClientComm.h" + +bool cyAccountManagement::IsSubscriptionActive() +{ + const NetCommAccount* account = NetCommGetAccount(); + return (account->billingType & kBillingTypePaidSubscriber); +} + +PyObject* cyAccountManagement::GetPlayerList() +{ + const ARRAY(NetCommPlayer)& playerList = NetCommGetPlayerList(); + int numPlayers = NetCommGetPlayerCount(); + PyObject* pList = PyList_New(0); + + PyObject* visitor = nil; + + for (int i = 0; i < numPlayers; ++i) + { + PyObject* playerTuple = PyTuple_New(3); + PyObject* playerName = PyUnicode_FromUnicode(playerList[i].playerName, wcslen(playerList[i].playerName)); + PyObject* playerId = PyInt_FromLong(playerList[i].playerInt); + PyObject* avatarShape = PyString_FromString(playerList[i].avatarDatasetName); + + PyTuple_SetItem(playerTuple, 0, playerName); + PyTuple_SetItem(playerTuple, 1, playerId); + PyTuple_SetItem(playerTuple, 2, avatarShape); + + if (visitor || playerList[i].explorer) + PyList_Append(pList, playerTuple); + else + visitor = playerTuple; + } + + if (visitor) + { + PyList_Insert(pList, 0, visitor); + } + else + { + Py_INCREF(Py_None); + PyList_Insert(pList, 0, Py_None); + } + + return pList; +} + +std::wstring cyAccountManagement::GetAccountName() +{ + const NetCommAccount* acct = NetCommGetAccount(); + if (acct) + return acct->accountName; + else + return L""; +} + +void cyAccountManagement::CreatePlayer(const char* playerName, const char* avatar, const char* invitationCode) +{ + NetCommCreatePlayer(playerName, avatar, invitationCode, 0, nil); +} + +void cyAccountManagement::CreatePlayerW(const wchar_t* playerName, const wchar_t* avatar, const wchar_t* invitationCode) +{ + NetCommCreatePlayer(playerName, avatar, invitationCode, 0, nil); +} + +void cyAccountManagement::DeletePlayer(unsigned playerId) +{ + NetCommDeletePlayer(playerId, nil); +} + +void cyAccountManagement::SetActivePlayer(unsigned playerId) +{ + NetCommSetActivePlayer(playerId, nil); +} + +bool cyAccountManagement::IsActivePlayerSet() +{ + return NetCommGetPlayer()->playerInt != 0; +} + +void cyAccountManagement::UpgradeVisitorToExplorer(unsigned playerId) +{ + NetCommUpgradeVisitorToExplorer(playerId, nil); +} + +void cyAccountManagement::ChangePassword(const char* password) +{ + wchar* wpassword = StrDupToUnicode(password); + NetCommChangeMyPassword(wpassword); + FREE(wpassword); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagement.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagement.h new file mode 100644 index 00000000..c640ae77 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagement.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef cyAccountManagement_h +#define cyAccountManagement_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: cyAccountManagement +// +// PURPOSE: Python wrapper for account management functions +// + +#include +#include "hsTypes.h" +#include "hsStlUtils.h" + +class cyAccountManagement +{ +public: + static void AddPlasmaMethods(std::vector &methods); + static void AddPlasmaConstantsClasses(PyObject *m); + + static bool IsSubscriptionActive(); + static PyObject* GetPlayerList(); + static std::wstring GetAccountName(); + static void CreatePlayer(const char* playerName, const char* avatar, const char* invitationCode); + static void CreatePlayerW(const wchar_t* playerName, const wchar_t* avatar, const wchar_t* invitationCode); + static void DeletePlayer(unsigned playerId); + static void SetActivePlayer(unsigned playerId); + static bool IsActivePlayerSet(); + static void UpgradeVisitorToExplorer(unsigned playerId); + static void ChangePassword(const char* password); +}; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagementGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagementGlue.cpp new file mode 100644 index 00000000..9313ddad --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAccountManagementGlue.cpp @@ -0,0 +1,230 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyAccountManagement.h" + +#include "pyGlueHelpers.h" +#include "pyEnum.h" + +#include "../plMessage/plAccountUpdateMsg.h" + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsSubscriptionActive, "Returns true if the current player is a paying subscriber") +{ + PYTHON_RETURN_BOOL(cyAccountManagement::IsSubscriptionActive()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetAccountPlayerList, "Returns list of players associated with the current account") +{ + return cyAccountManagement::GetPlayerList(); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetAccountName, "Returns the account name for the current account") +{ + std::wstring name = cyAccountManagement::GetAccountName(); + return PyUnicode_FromWideChar(name.c_str(), name.length()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtCreatePlayer, args, "Params: playerName, avatarShape, invitation\nCreates a new player") +{ + char* playerName; + char* avatarShape; + char* invitation; + if (!PyArg_ParseTuple(args, "ssz", &playerName, &avatarShape, &invitation)) + { + PyErr_SetString(PyExc_TypeError, "PtCreatePlayer expects three strings"); + PYTHON_RETURN_ERROR; + } + + cyAccountManagement::CreatePlayer(playerName, avatarShape, invitation); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtCreatePlayerW, args, "Params: playerName, avatarShape, invitation\nUnicode version of PtCreatePlayer") +{ + PyObject* playerNameObj; + PyObject* avatarShapeObj; + PyObject* invitationObj; + if (!PyArg_ParseTuple(args, "OOO", &playerNameObj, &avatarShapeObj, &invitationObj)) + { + PyErr_SetString(PyExc_TypeError, "PtCreatePlayerW expects three unicode strings"); + PYTHON_RETURN_ERROR; + } + + std::wstring playerName, avatarShape, invitation; + + if (PyUnicode_Check(playerNameObj)) + { + int strLen = PyUnicode_GetSize(playerNameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)playerNameObj, text, strLen); + text[strLen] = L'\0'; + playerName = text; + delete [] text; + } + else if (PyString_Check(playerNameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(playerNameObj); + wchar_t* temp = hsStringToWString(text); + playerName = temp; + delete [] temp; + } + else + { + PyErr_SetString(PyExc_TypeError, "PtCreatePlayerW expects three unicode strings"); + PYTHON_RETURN_ERROR; + } + + if (PyUnicode_Check(avatarShapeObj)) + { + int strLen = PyUnicode_GetSize(avatarShapeObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)avatarShapeObj, text, strLen); + text[strLen] = L'\0'; + avatarShape = text; + delete [] text; + } + else if (PyString_Check(avatarShapeObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(avatarShapeObj); + wchar_t* temp = hsStringToWString(text); + avatarShape = temp; + delete [] temp; + } + else + { + PyErr_SetString(PyExc_TypeError, "PtCreatePlayerW expects three unicode strings"); + PYTHON_RETURN_ERROR; + } + + if (PyUnicode_Check(invitationObj)) + { + int strLen = PyUnicode_GetSize(invitationObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)invitationObj, text, strLen); + text[strLen] = L'\0'; + invitation = text; + delete [] text; + } + else if (PyString_Check(invitationObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(invitationObj); + wchar_t* temp = hsStringToWString(text); + invitation = temp; + delete [] temp; + } + else + { + PyErr_SetString(PyExc_TypeError, "PtCreatePlayerW expects three unicode strings"); + PYTHON_RETURN_ERROR; + } + + cyAccountManagement::CreatePlayerW(playerName.c_str(), avatarShape.c_str(), invitation.c_str()); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtDeletePlayer, args, "Params: playerInt\nDeletes a player associated with the current account") +{ + unsigned playerInt = 0; + if (!PyArg_ParseTuple(args, "I", &playerInt)) + { + PyErr_SetString(PyExc_TypeError, "PtDeletePlayer expects a unsigned int"); + PYTHON_RETURN_ERROR; + } + + cyAccountManagement::DeletePlayer(playerInt); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetActivePlayer, args, "Params: playerInt\nSets the active player associated with the current account") +{ + unsigned playerInt = 0; + if (!PyArg_ParseTuple(args, "I", &playerInt)) + { + PyErr_SetString(PyExc_TypeError, "PtSetActivePlayer expects a unsigned int"); + PYTHON_RETURN_ERROR; + } + + cyAccountManagement::SetActivePlayer(playerInt); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsActivePlayerSet, "Returns whether or not an active player is set") +{ + PYTHON_RETURN_BOOL(cyAccountManagement::IsActivePlayerSet()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtUpgradeVisitorToExplorer, args, "Params: playerInt\nUpgrades the player to explorer status") +{ + unsigned playerInt = 0; + if (!PyArg_ParseTuple(args, "I", &playerInt)) + { + PyErr_SetString(PyExc_TypeError, "PtUpgradeVisitorToExplorer expects a unsigned int"); + PYTHON_RETURN_ERROR; + } + + cyAccountManagement::UpgradeVisitorToExplorer(playerInt); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtChangePassword, args, "Params: password\nChanges the current account's password") +{ + char* password = nil; + if (!PyArg_ParseTuple(args, "s", &password)) + { + PyErr_SetString(PyExc_TypeError, "PtChangePassword expects a string"); + PYTHON_RETURN_ERROR; + } + + cyAccountManagement::ChangePassword(password); + PYTHON_RETURN_NONE; +} + +void cyAccountManagement::AddPlasmaMethods(std::vector &methods) +{ + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsSubscriptionActive); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetAccountPlayerList); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetAccountName); + PYTHON_GLOBAL_METHOD(methods, PtCreatePlayer); + PYTHON_GLOBAL_METHOD(methods, PtCreatePlayerW); + PYTHON_GLOBAL_METHOD(methods, PtDeletePlayer); + PYTHON_GLOBAL_METHOD(methods, PtSetActivePlayer); + PYTHON_GLOBAL_METHOD(methods, PtIsActivePlayerSet); + PYTHON_GLOBAL_METHOD(methods, PtUpgradeVisitorToExplorer); + PYTHON_GLOBAL_METHOD(methods, PtChangePassword); +} + +void cyAccountManagement::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtAccountUpdateType); + PYTHON_ENUM_ELEMENT(PtAccountUpdateType, kCreatePlayer, plAccountUpdateMsg::kCreatePlayer); + PYTHON_ENUM_ELEMENT(PtAccountUpdateType, kDeletePlayer, plAccountUpdateMsg::kDeletePlayer); + PYTHON_ENUM_ELEMENT(PtAccountUpdateType, kUpgradePlayer, plAccountUpdateMsg::kUpgradePlayer); + PYTHON_ENUM_ELEMENT(PtAccountUpdateType, kActivePlayer, plAccountUpdateMsg::kActivePlayer); + PYTHON_ENUM_ELEMENT(PtAccountUpdateType, kChangePassword, plAccountUpdateMsg::kChangePassword); + PYTHON_ENUM_END(m, PtAccountUpdateType); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimation.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimation.cpp new file mode 100644 index 00000000..80e05ca8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimation.cpp @@ -0,0 +1,614 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +///////////////////////////////////////////////////////////////////////////// +// +// NAME: cyAnimation +// +// PURPOSE: Class wrapper to map animation functions to plasma2 message +// + +#include "plgDispatch.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../pnMessage/plEventCallbackMsg.h" + +#include "cyAnimation.h" + +cyAnimation::cyAnimation() +{ + fSender = nil; + fAnimName = nil; + fNetForce = false; +} + +cyAnimation::cyAnimation(pyKey& sender) +{ + SetSender(sender); + fAnimName = nil; + fNetForce = false; +} + +// copy constructor +cyAnimation::cyAnimation(const cyAnimation& anim) +{ + fSender = anim.fSender; + fRecvr = anim.fRecvr; + // here is why we needed the copy constructor + fAnimName = hsStrcpy(anim.fAnimName); // make our own copy of this string + fNetForce = anim.fNetForce; +} + +// clean up on the way out +cyAnimation::~cyAnimation() +{ + if (fAnimName != nil ) + { + delete [] fAnimName; + fAnimName = nil; + } +} + + +// setters +void cyAnimation::SetSender(pyKey& sender) +{ + fSender = sender.getKey(); +} + +void cyAnimation::AddRecvr(pyKey& recvr) +{ + fRecvr.Append(recvr.getKey()); +} + +PyObject* cyAnimation::GetFirstRecvr() +{ + if ( fRecvr.Count() > 0 ) + return pyKey::New(fRecvr[0]); + return nil; +} + +void cyAnimation::SetAnimName(const char* name) +{ + if ( fAnimName != nil ) + delete [] fAnimName; + fAnimName = hsStrcpy(name); +} + +void cyAnimation::SetNetForce(hsBool state) +{ + // set our flag + fNetForce = state; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Play +// PARAMETERS : +// +// PURPOSE : Play animation from start to end (whatever is already set) +// +void cyAnimation::Play() +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + + // NOTE: The animation modifier will set the animation back to the starting point automatically + + // then continue from there + pMsg->SetCmd(plAnimCmdMsg::kGoToBegin); + pMsg->SetCmd(plAnimCmdMsg::kContinue); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Stop +// PARAMETERS : +// +// PURPOSE : Stop an animation +// +void cyAnimation::Stop() +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + pMsg->SetCmd(plAnimCmdMsg::kStop); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Resume +// PARAMETERS : +// +// PURPOSE : Continue playing animation from wherever it last stopped +// +void cyAnimation::Resume() +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + pMsg->SetCmd(plAnimCmdMsg::kContinue); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : PlayRange +// PARAMETERS : start - start time for the range +// : end - end time of the range +// +// PURPOSE : Play an animation only from specific time start to end +// +void cyAnimation::PlayRange(hsScalar start, hsScalar end) +{ + SkipToTime(start); + PlayToTime(end); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : PlayToTime +// PARAMETERS : time - where to stop playing animation +// +// PURPOSE : Play (continue) an animation until the specified time is reached +// +void cyAnimation::PlayToTime(hsScalar time) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + pMsg->SetCmd(plAnimCmdMsg::kPlayToTime); + pMsg->fTime = time; + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : PlayToPercentage +// PARAMETERS : zeroToOne - How far (scale of 0 to 1) to play into the anim +// +// PURPOSE : Play (continue) an animation until the specified point is reached +// +void cyAnimation::PlayToPercentage(hsScalar zeroToOne) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + pMsg->SetCmd(plAnimCmdMsg::kPlayToPercentage); + pMsg->fTime = zeroToOne; + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SkipToTime +// PARAMETERS : +// +// PURPOSE : Jump the animation to the specified time +// : Doesn't start or stop playing of animation +// +void cyAnimation::SkipToTime(hsScalar time) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + pMsg->SetCmd(plAnimCmdMsg::kGoToTime); + pMsg->fTime = time; + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Looped +// PARAMETERS : looped - state to change to +// +// PURPOSE : Set whether the animation is to be looped or not +// +void cyAnimation::Looped(hsBool looped) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + if ( looped ) + pMsg->SetCmd(plAnimCmdMsg::kSetLooping); + else + pMsg->SetCmd(plAnimCmdMsg::kUnSetLooping); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Backwards +// PARAMETERS : backwards - state of the backwards flag +// +// PURPOSE : Sets the backwards state for the animation +// +void cyAnimation::Backwards(hsBool backwards) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + if ( backwards ) + pMsg->SetCmd(plAnimCmdMsg::kSetBackwards); + else + pMsg->SetCmd(plAnimCmdMsg::kSetForewards); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SetLoopStart and SetLoopEnd +// PARAMETERS : value - sets the start or the end of the animation +// +void cyAnimation::SetLoopStart(hsScalar start) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + pMsg->SetCmd(plAnimCmdMsg::kSetLoopBegin); + pMsg->fLoopBegin = start; + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void cyAnimation::SetLoopEnd(hsScalar end) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + pMsg->SetCmd(plAnimCmdMsg::kSetLoopEnd); + pMsg->fLoopEnd = end; + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Speed +// PARAMETERS : speed - speed to set the animation to +// +// PURPOSE : Sets the speed of the animation +// : Doesn't start or stop playing animation +// +void cyAnimation::Speed(hsScalar speed) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + pMsg->SetCmd(plAnimCmdMsg::kSetSpeed); + pMsg->fSpeed = speed; + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + + +void cyAnimation::IRunOneCmd(int cmd) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the notetrack name (if there is one) + if ( fAnimName != nil ) + pMsg->SetAnimName(fAnimName); + pMsg->SetCmd(cmd); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void cyAnimation::SkipToBegin() +{ + IRunOneCmd(plAnimCmdMsg::kGoToBegin); +} + +void cyAnimation::SkipToEnd() +{ + IRunOneCmd(plAnimCmdMsg::kGoToEnd); +} + +void cyAnimation::SkipToLoopBegin() +{ + IRunOneCmd(plAnimCmdMsg::kGoToLoopBegin); +} + +void cyAnimation::SkipToLoopEnd() +{ + IRunOneCmd(plAnimCmdMsg::kGoToLoopEnd); +} + +// Bump the animation ahead one frame (whatever deltime is) +// +void cyAnimation::IncrementForward() +{ + IRunOneCmd(plAnimCmdMsg::kIncrementForward); +} + +// Bump the animation back one frame (whatever deltime is) +// +void cyAnimation::IncrementBackward() +{ + IRunOneCmd(plAnimCmdMsg::kIncrementBackward); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimation.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimation.h new file mode 100644 index 00000000..f053cbfe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimation.h @@ -0,0 +1,149 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef cyAnimation_h +#define cyAnimation_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: cyAnimation +// +// PURPOSE: Class wrapper to map animation functions to plasma2 message +// + +#include "pyKey.h" +#include "hsTemplates.h" + +#include +#include "pyGlueHelpers.h" + +class cyAnimation +{ + + plKey fSender; + hsTArray fRecvr; + char* fAnimName; + hsBool fNetForce; + + virtual void IRunOneCmd(int cmd); + +protected: + cyAnimation(); + cyAnimation(pyKey& sender); + + // copy constructor + cyAnimation(const cyAnimation& anim); +public: + + // clean up on the way out + ~cyAnimation(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptAnimation); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(PyObject *sender); + static PyObject *New(cyAnimation &obj); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a cyAnimation object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(cyAnimation); // converts a PyObject to a cyAnimation (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // setters + virtual void SetSender(pyKey& sender); + virtual void AddRecvr(pyKey& recvr); + virtual void SetAnimName(const char* name); + + virtual PyObject* GetFirstRecvr(); + + virtual void SetNetForce(hsBool state); + + // Play animation from start to end (whatever is already set) + // + virtual void Play(); + + // Stop an animation + // + virtual void Stop(); + + // Continue playing animation from wherever it last stopped + // + virtual void Resume(); + + // Play an animation only from specific time start to end + // + virtual void PlayRange(hsScalar start, hsScalar end); + + // Play (continue) an animation until the specified time is reached + // + virtual void PlayToTime(hsScalar time); + + // Play (continue) an animation until the specified point is reached + // + virtual void PlayToPercentage(hsScalar zeroToOne); + + // Jump the animation to the specified time + // Doesn't start or stop playing of animation + // + virtual void SkipToTime(hsScalar time); + + // Set whether the animation is to be looped or not + // + virtual void Looped(hsBool looped); + + // Sets the backwards state for the animation + // + virtual void Backwards(hsBool backwards); + + + // Sets the start and end of the looping points in the animation + // + virtual void SetLoopStart(hsScalar start); + virtual void SetLoopEnd(hsScalar end); + + // Sets the speed of the animation + // Doesn't start or stop playing animation + // + virtual void Speed(hsScalar speed); + + + // Jump the animation to the specified time + // Doesn't start or stop playing of animation + // + virtual void SkipToBegin(); + virtual void SkipToEnd(); + virtual void SkipToLoopBegin(); + virtual void SkipToLoopEnd(); + + // Bump the animation ahead one frame (whatever deltime is) + // + virtual void IncrementForward(); + + // Bump the animation back one frame (whatever deltime is) + // + virtual void IncrementBackward(); +}; + + +#endif // cyAnimation_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimationGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimationGlue.cpp new file mode 100644 index 00000000..ba712e94 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAnimationGlue.cpp @@ -0,0 +1,318 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyAnimation.h" +#include "hsUtils.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptAnimation, cyAnimation); + +PYTHON_DEFAULT_NEW_DEFINITION(ptAnimation, cyAnimation) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptAnimation) + +PYTHON_INIT_DEFINITION(ptAnimation, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "|O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (keyObject == NULL) // no parameter was passed + PYTHON_RETURN_INIT_OK; // nothing to init + + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->SetSender(*key); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, sender, args) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "sender requires a ptKey argument"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "sender requires a ptKey argument"); + PYTHON_RETURN_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->SetSender(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, addKey, args) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "addKey requires a ptKey argument"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "addKey requires a ptKey argument"); + PYTHON_RETURN_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->AddRecvr(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, netForce, args) +{ + char forceFlag; + if (!PyArg_ParseTuple(args, "b", &forceFlag)) + { + PyErr_SetString(PyExc_TypeError, "netForce requires a boolean argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNetForce(forceFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, setAnimName, args) +{ + char *name = NULL; + if (!PyArg_ParseTuple(args, "s", &name)) // name points at the internal buffer SO DON'T DELETE IT + { + PyErr_SetString(PyExc_TypeError, "setAnimName requires a string argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAnimName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptAnimation, play, Play) +PYTHON_BASIC_METHOD_DEFINITION(ptAnimation, stop, Stop) +PYTHON_BASIC_METHOD_DEFINITION(ptAnimation, resume, Resume) + +PYTHON_METHOD_DEFINITION(ptAnimation, playRange, args) +{ + float start, end; + if (!PyArg_ParseTuple(args, "ff", &start, &end)) + { + PyErr_SetString(PyExc_TypeError, "playRange requires two floating-point arguments"); + PYTHON_RETURN_ERROR; + } + self->fThis->PlayRange(start, end); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, playToTime, args) +{ + float time; + if (!PyArg_ParseTuple(args, "f", &time)) + { + PyErr_SetString(PyExc_TypeError, "playToTime requires one floating-point argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->PlayToTime(time); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, playToPercentage, args) +{ + float percent; + if (!PyArg_ParseTuple(args, "f", &percent)) + { + PyErr_SetString(PyExc_TypeError, "playToPercentage requires one floating-point argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->PlayToPercentage(percent); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, skipToTime, args) +{ + float time; + if (!PyArg_ParseTuple(args, "f", &time)) + { + PyErr_SetString(PyExc_TypeError, "skipToTime requires one floating-point argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->SkipToTime(time); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, looped, args) +{ + char looped; + if (!PyArg_ParseTuple(args, "b", &looped)) + { + PyErr_SetString(PyExc_TypeError, "looped requires a boolean argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->Looped(looped != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, backwards, args) +{ + char backwards; + if (!PyArg_ParseTuple(args, "b", &backwards)) + { + PyErr_SetString(PyExc_TypeError, "backwards requires a boolean argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->Backwards(backwards != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, setLoopStart, args) +{ + float time; + if (!PyArg_ParseTuple(args, "f", &time)) + { + PyErr_SetString(PyExc_TypeError, "setLoopStart requires one floating-point argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetLoopStart(time); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, setLoopEnd, args) +{ + float time; + if (!PyArg_ParseTuple(args, "f", &time)) + { + PyErr_SetString(PyExc_TypeError, "setLoopEnd requires one floating-point argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetLoopEnd(time); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAnimation, speed, args) +{ + float speed; + if (!PyArg_ParseTuple(args, "f", &speed)) + { + PyErr_SetString(PyExc_TypeError, "speed requires one floating-point argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->Speed(speed); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptAnimation, skipToBegin, SkipToBegin) +PYTHON_BASIC_METHOD_DEFINITION(ptAnimation, skipToEnd, SkipToEnd) +PYTHON_BASIC_METHOD_DEFINITION(ptAnimation, skipToLoopBegin, SkipToLoopBegin) +PYTHON_BASIC_METHOD_DEFINITION(ptAnimation, skipToLoopEnd, SkipToLoopEnd) +PYTHON_BASIC_METHOD_DEFINITION(ptAnimation, incrementForward, IncrementForward) +PYTHON_BASIC_METHOD_DEFINITION(ptAnimation, incrementBackward, IncrementBackward) + +PYTHON_METHOD_DEFINITION_NOARGS(ptAnimation, getFirstKey) +{ + PyObject *key = self->fThis->GetFirstRecvr(); + if (key == NULL) + PYTHON_RETURN_NONE; + return key; +} + +PYTHON_START_METHODS_TABLE(ptAnimation) + PYTHON_METHOD(ptAnimation, sender, "Params: selfKey\nSets the sender of the messages being sent to the animation modifier"), + PYTHON_METHOD(ptAnimation, addKey, "Params: key\nAdds an animation modifier to the list of receiver keys"), + PYTHON_METHOD(ptAnimation, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n" + "- This is to be used if your Python program is running on only one client\n" + "Such as a game master, only running on the client that owns a particular object"), + PYTHON_METHOD(ptAnimation, setAnimName, "Params: name\nSets the animation notetrack name (or (Entire Animation))"), + PYTHON_BASIC_METHOD(ptAnimation, play, "Plays the animation"), + PYTHON_BASIC_METHOD(ptAnimation, stop, "Stops the animation"), + PYTHON_BASIC_METHOD(ptAnimation, resume, "Resumes the animation from where it was stopped last"), + PYTHON_METHOD(ptAnimation, playRange, "Params: start,end\nPlay the animation from start to end"), + PYTHON_METHOD(ptAnimation, playToTime, "Params: time\nPlay the animation to the specified time"), + PYTHON_METHOD(ptAnimation, playToPercentage, "Params: zeroToOne\nPlay the animation to the specified percentage (0 to 1)"), + PYTHON_METHOD(ptAnimation, skipToTime, "Params: time\nSkip the animation to time (don't play)"), + PYTHON_METHOD(ptAnimation, looped, "Params: loopedFlag\nTurn on and off looping of the animation"), + PYTHON_METHOD(ptAnimation, backwards, "Params: backwardsFlag\nTurn on and off playing the animation backwards"), + PYTHON_METHOD(ptAnimation, setLoopStart, "Params: loopStart\nSets the loop starting position\n" + "- 'loopStart' is the number of seconds from the absolute beginning of the animation"), + PYTHON_METHOD(ptAnimation, setLoopEnd, "Params: loopEnd\nSets the loop ending position\n" + "- 'loopEnd' is the number of seconds from the absolute beginning of the animation"), + PYTHON_METHOD(ptAnimation, speed, "Params: speed\nSets the animation playback speed"), + PYTHON_BASIC_METHOD(ptAnimation, skipToBegin, "Skip to the beginning of the animation (don't play)"), + PYTHON_BASIC_METHOD(ptAnimation, skipToEnd, "Skip to the end of the animation (don't play)"), + PYTHON_BASIC_METHOD(ptAnimation, skipToLoopBegin, "Skip to the beginning of the animation loop (don't play)"), + PYTHON_BASIC_METHOD(ptAnimation, skipToLoopEnd, "Skip to the end of the animation loop (don't play)"), + PYTHON_BASIC_METHOD(ptAnimation, incrementForward, "Step the animation forward a frame"), + PYTHON_BASIC_METHOD(ptAnimation, incrementBackward, "Step the animation backward a frame"), + PYTHON_METHOD_NOARGS(ptAnimation, getFirstKey, "This will return a ptKey object that is the first receiver (target)\n" + "However, if the parent is not a modifier or not loaded, then None is returned."), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptAnimation, "Params: key=None\nPlasma animation class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptAnimation, cyAnimation) + +PyObject *cyAnimation::New(PyObject *sender) +{ + ptAnimation *newObj = (ptAnimation*)ptAnimation_type.tp_new(&ptAnimation_type, NULL, NULL); + pyKey *key = pyKey::ConvertFrom(sender); + newObj->fThis->SetSender(*key); + newObj->fThis->fAnimName = nil; + newObj->fThis->fNetForce = false; + return (PyObject*)newObj; +} + +PyObject *cyAnimation::New(cyAnimation &obj) +{ + ptAnimation *newObj = (ptAnimation*)ptAnimation_type.tp_new(&ptAnimation_type, NULL, NULL); + newObj->fThis->fSender = obj.fSender; + newObj->fThis->fRecvr = obj.fRecvr; + newObj->fThis->fAnimName = hsStrcpy(obj.fAnimName); + newObj->fThis->fNetForce = obj.fNetForce; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptAnimation, cyAnimation) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptAnimation, cyAnimation) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void cyAnimation::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptAnimation); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp new file mode 100644 index 00000000..e2d62b1d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp @@ -0,0 +1,1964 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyAvatar.h" + + +#include "plgDispatch.h" +#include "../plAvatar/plAvatarMgr.h" + +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../plMessage/plOneShotCallbacks.h" +#include "../plMessage/plOneShotMsg.h" +#include "../plMessage/plMultistageMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../plGImage/plMipmap.h" +#include "pyKey.h" +#include "pySceneObject.h" +#include "pyColor.h" +#include "pyImage.h" +#include "cyPythonInterface.h" +#include "cyMisc.h" + +#include "../plAvatar/plOneShotMod.h" +#include "../plAvatar/plMultistageBehMod.h" +#include "../plAvatar/plAvatarClothing.h" +#include "../plAvatar/plClothingLayout.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvBrainHuman.h" // needed to call the emote +#include "../plAvatar/plAGAnim.h" // to get the BodyUsage enum +#include "../plInputCore/plAvatarInputInterface.h" +#include "plPhysical.h" +#include "../plMessage/plSimStateMsg.h" + +#include "../pnNetCommon/plNetApp.h" +#include "../plVault/plVault.h" + +#include "../plDrawable/plSharedMesh.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plDrawable/plMorphSequence.h" + + +/////////////////////////////////////////////////////////////////////////// +// +// LOCAL FORWARD DECLs +// +/////////////////////////////////////////////////////////////////////////// +bool IEnterGenericMode(const char *enterAnim, const char *idleAnim, const char *exitAnim, bool autoExit, plAGAnim::BodyUsage bodyUsage, + plAvBrainGeneric::BrainType = plAvBrainGeneric::kGeneric); +bool IExitTopmostGenericMode(); + + +cyAvatar::cyAvatar(plKey sender, plKey recvr) +{ + SetSender(sender); + AddRecvr(recvr); + fNetForce = false; +} + +// setters +void cyAvatar::SetSender(plKey &sender) +{ + fSender = sender; +} + +void cyAvatar::AddRecvr(plKey &recvr) +{ + if ( recvr != nil ) + fRecvr.Append(recvr); +} + +void cyAvatar::SetNetForce(hsBool state) +{ + // set our flag + fNetForce = state; +} + +void cyAvatar::SetSenderKey(pyKey& pKey) +{ + SetSender(pKey.getKey()); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IFindArmatureModKey +// PARAMETERS : avObj - avatar sceneobject +// +// PURPOSE : find the armature mod for this sceneoabject (if its an avatar) +// +const plArmatureMod* cyAvatar::IFindArmatureMod(plKey avKey) +{ + plSceneObject* avObj = plSceneObject::ConvertNoRef(avKey->ObjectIsLoaded()); + if ( avObj ) + { + // search through its modifiers to see if one of them is an avatar modifier + int i; + for ( i=0; iGetNumModifiers(); i++ ) + { + const plModifier* mod = avObj->GetModifier(i); + // see if it is an avatar mod base class + const plArmatureMod* avmod = plArmatureMod::ConvertNoRef(mod); + if ( avmod ) + return avmod; + } + } + // otherwise we didn't find anything + return nil; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IFindArmatureModKey +// PARAMETERS : avObj - avatar sceneobject +// +// PURPOSE : find the armature mod for this sceneoabject (if its an avatar) +// +plKey cyAvatar::IFindArmatureModKey(plKey avKey) +{ + const plArmatureMod* avatar = IFindArmatureMod(avKey); + if ( avatar ) + return avatar->GetKey(); + // otherwise we didn't find anything + return nil; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : oneShot +// PARAMETERS : +// +// PURPOSE : oneShot Avatar (must already be there) +// +void cyAvatar::OneShot(pyKey &seekKey, float duration, hsBool usePhysics, + const char *animName, hsBool drivable, hsBool reversible) +{ + if ( fRecvr.Count() > 0 ) + { + // create message + plAvOneShotMsg* pMsg = TRACKED_NEW plAvOneShotMsg( + (plKey )fSender, + nil, + seekKey.getKey(), // Mark D told me to do it ...paulg + duration, + usePhysics, + animName, // Constructor will do a copy. -mf- hsStrcpy(animName), + drivable, + reversible); + + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + // must have a receiver! + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : RunBehavior +// PARAMETERS : +// +// PURPOSE : Run Behavior, could be single or multi-stage shot +// +void cyAvatar::RunBehavior(pyKey &behKey, hsBool netForce, hsBool netProp) +{ + // first there is someone to send to and make sure that we an avatar to send this to + if ( behKey.getKey() != nil && fRecvr.Count() > 0) + { + // must determine if the behKey is pointing to Single or Multi Shot behavior + if ( plOneShotMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil ) + { + // create a message OneShotMessage + plOneShotMsg* pMsg = TRACKED_NEW plOneShotMsg; + // check if this needs to be network forced to all clients + if (netProp) + { + pMsg->SetBCastFlag(plMessage::kNetPropagate); + } + else + { + pMsg->SetBCastFlag(plMessage::kNetPropagate, false); + } + + if (netForce) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + else + { + pMsg->SetBCastFlag(plMessage::kNetForce, false); + } + + pMsg->SetSender(fSender); + pMsg->AddReceiver(behKey.getKey()); + int i; + for ( i=0; ifPlayerKey = (plKey)fRecvr[i]; + plgDispatch::MsgSend( pMsg ); // send off command for each valid avatar we find + // ... really, should only be one... though + } + } + } + // else if it is a Multistage guy + else if ( plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil ) + { + // its a multistage thingy... need to send it a plNotifyMsg + // create new notify message to do the actual send with + plNotifyMsg* pNMsg = TRACKED_NEW plNotifyMsg; + + // set whether this should be forced over the network (ignoring net-cascading) + if (netProp) + { + pNMsg->SetBCastFlag(plMessage::kNetPropagate); + } + else + { + pNMsg->SetBCastFlag(plMessage::kNetPropagate, false); + } + + if ( netForce ) + { + pNMsg->SetBCastFlag(plMessage::kNetPropagate); + pNMsg->SetBCastFlag(plMessage::kNetForce); + } + else + { + pNMsg->SetBCastFlag(plMessage::kNetForce, false); + } + + // copy data and event records to new NotifyMsg + pNMsg->fState = 1.0; + // need to recreate all the events in the new message by Adding them + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + pNMsg->AddPickEvent( (plKey)fRecvr[0], nil, true, hsPoint3(0,0,0) ); + } + + // add receivers + // loop though adding the ones that want to be notified of the change + pNMsg->AddReceiver(behKey.getKey()); + pNMsg->SetSender(fSender); + plgDispatch::MsgSend( pNMsg ); + } + + } +} +///////////////////////////////////////////////////////////////////////////// +// +// Function : RunBehaviorAndReply +// PARAMETERS : +// +// PURPOSE : Run Behavior, multistage only, reply to specified key'd object +// +void cyAvatar::RunBehaviorAndReply(pyKey& behKey, pyKey& replyKey, hsBool netForce, hsBool netProp) +{ + plMultistageBehMod* pMod = plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()); + if ( pMod ) + { + // its a multistage thingy... need to send it a plNotifyMsg + // create new notify message to do the actual send with + plNotifyMsg* pNMsg = TRACKED_NEW plNotifyMsg; + + // set whether this should be forced over the network (ignoring net-cascading) + if (netProp) + { + pNMsg->SetBCastFlag(plMessage::kNetPropagate); + } + else + { + pNMsg->SetBCastFlag(plMessage::kNetPropagate, false); + } + + if (netForce) + { + // set the network propagate flag to make sure it gets to the other clients + pNMsg->SetBCastFlag(plMessage::kNetPropagate); + pNMsg->SetBCastFlag(plMessage::kNetForce); + } + else + { + pNMsg->SetBCastFlag(plMessage::kNetForce, false); + } + + // copy data and event records to new NotifyMsg + pNMsg->fState = 1.0; + // need to recreate all the events in the new message by Adding them + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + pNMsg->AddPickEvent( (plKey)fRecvr[0], nil, true, hsPoint3(0,0,0) ); + } + + // add receivers + // loop though adding the ones that want to be notified of the change + pNMsg->AddReceiver(behKey.getKey()); + pNMsg->SetSender(replyKey.getKey()); + plgDispatch::MsgSend( pNMsg ); + } + +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : NextStage +// PARAMETERS : behKey - behavior pyKey +// : transTime - the transition time to the next stage +// (optional): rewind - whether to rewind to the front of the next stage +// +// PURPOSE : Go to the next stage in a multi-stage behavior +// +// NOTE: only works with multi-stage behaviors +// +void cyAvatar::NextStage(pyKey &behKey, hsScalar transTime, hsBool setTime, hsScalar newTime, + hsBool setDirection, bool isForward, hsBool netForce) +{ + // first there is someone to send to and make sure that we an avatar to send this to + if ( behKey.getKey() != nil && fRecvr.Count() > 0) + { + // if it is a Multistage guy + if ( plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil ) + { + plKey avKey = IFindArmatureModKey( (plKey)fRecvr[0] ); + if ( avKey ) + { + // create the message + plAvBrainGenericMsg* pMsg = TRACKED_NEW plAvBrainGenericMsg((plKey)fSender, avKey, + plAvBrainGenericMsg::kNextStage, 0, setTime, newTime, + setDirection, (bool)isForward, transTime); + + if ( netForce ) + pMsg->SetBCastFlag(plMessage::kNetForce | plMessage::kNetPropagate); + + plgDispatch::MsgSend( pMsg ); + } + } + + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : PreviousStage +// PARAMETERS : behKey - behavior pyKey +// : transTime - the transition time to the next stage +// (optional): rewind - whether to rewind to the front of the next stage +// +// PURPOSE : Go to the previous stage in a multi-stage behavior +// +// NOTE: only works with multi-stage behaviors +// +void cyAvatar::PreviousStage(pyKey &behKey, hsScalar transTime, hsBool setTime, hsScalar newTime, + hsBool setDirection, bool isForward, hsBool netForce) +{ + // first there is someone to send to and make sure that we an avatar to send this to + if ( behKey.getKey() != nil && fRecvr.Count() > 0) + { + // if it is a Multistage guy + if ( plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil ) + { + plKey avKey = IFindArmatureModKey( (plKey)fRecvr[0] ); + if ( avKey ) + { + // create the message + plAvBrainGenericMsg* pMsg = TRACKED_NEW plAvBrainGenericMsg((plKey)fSender, avKey, + plAvBrainGenericMsg::kPrevStage, 0, setTime, newTime, + setDirection, (bool)isForward, transTime); + + if ( netForce ) + pMsg->SetBCastFlag(plMessage::kNetForce | plMessage::kNetPropagate); + + plgDispatch::MsgSend( pMsg ); + } + } + + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GotoStage +// PARAMETERS : behKey - behavior pyKey +// : stage - stage number to go to +// : transTime - the transition time to the next stage +// (optional): rewind - whether to rewind to the front of the next stage +// +// PURPOSE : Go to a particular stage in a multi-stage behavior +// +// NOTE: only works with multi-stage behaviors +// +void cyAvatar::GoToStage(pyKey &behKey, Int32 stage, hsScalar transTime, hsBool setTime, hsScalar newTime, + hsBool setDirection, bool isForward, hsBool netForce) +{ + // first there is someone to send to and make sure that we an avatar to send this to + if ( behKey.getKey() != nil && fRecvr.Count() > 0) + { + // if it is a Multistage guy + if ( plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil ) + { + plKey avKey = IFindArmatureModKey( (plKey)fRecvr[0] ); + if ( avKey ) + { + // create the message + plAvBrainGenericMsg* pMsg = TRACKED_NEW plAvBrainGenericMsg((plKey)fSender, avKey, + plAvBrainGenericMsg::kGotoStage, stage, setTime, newTime, + setDirection, isForward, transTime); + + if ( netForce ) + pMsg->SetBCastFlag(plMessage::kNetForce | plMessage::kNetPropagate); + + plgDispatch::MsgSend( pMsg ); + } + } + + } +} + + +void cyAvatar::SetLoopCount(pyKey &behKey, Int32 stage, Int32 loopCount, hsBool netForce) +{ + // if it is a Multistage guy + if ( plMultistageBehMod::ConvertNoRef(behKey.getKey()->GetObjectPtr()) != nil ) + { + plMultistageModMsg* pMsg = TRACKED_NEW plMultistageModMsg((plKey)nil, behKey.getKey()); + pMsg->SetCommand(plMultistageModMsg::kSetLoopCount); + pMsg->fStageNum = (UInt8)stage; + pMsg->fNumLoops = (UInt8)loopCount; + + if ( netForce ) + pMsg->SetBCastFlag(plMessage::kNetForce | plMessage::kNetPropagate); + + plgDispatch::MsgSend( pMsg ); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : seek +// PARAMETERS : +// +// PURPOSE : seek Avatar (must already be there) +// + +/* Unsupported. Ask Bob if you want it back. + +void cyAvatar::Seek(pyKey &seekKey, float duration, hsBool usePhysics) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plAvSeekMsg* pMsg = TRACKED_NEW plAvSeekMsg( + (plKey)fSender,nil, seekKey.getKey(),duration,usePhysics); + + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} +*/ + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetAvatarClothingGroup +// PARAMETERS : +// +// PURPOSE : Return what clothing group the avatar is in +// +Int32 cyAvatar::GetAvatarClothingGroup() +{ + // find the avatar's armature modifier + const plArmatureMod *avMod = nil; + + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + if ( avMod ) + { + return avMod->GetClothingOutfit()->fGroup; + } + } + } + return -1; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetClosetClothingList +// PARAMETERS : +// +// PURPOSE : Return a list of the wearable items for this avatar of that clothing_type +// +std::vector cyAvatar::GetEntireClothingList(Int32 clothing_type) +{ + // Currently, just all the clothing available will be returned + hsTArray clothingList = plClothingMgr::GetClothingMgr()->GetItemList(); + int numItems = clothingList.GetCount(); + + // create the string list to send to python... + std::vector retVal; + for (int i = 0; i < numItems; i++) + retVal.push_back(clothingList[i]->GetName()); + + return retVal; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetClosetClothingList +// PARAMETERS : +// +// PURPOSE : Return a list of the wearable items for this avatar of that clothing_type +// +std::vector cyAvatar::GetClosetClothingList(Int32 clothing_type) +{ + std::vector retVal; + + // find the avatar's armature modifier + const plArmatureMod *avMod = nil; + + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + if ( avMod ) + { + // Get all the clothes that we can wear + hsTArray clothingList; + plClothingMgr::GetClothingMgr()->GetItemsByGroup(avMod->GetClothingOutfit()->fGroup, clothingList); + int numItems = clothingList.GetCount(); + // create the string list to send to python... as a python object + int i; + for ( i=0; ifType == clothing_type ) + { + // add this event record to the main event list (lists within a list) + // create list + PyObject* clothingItem = PyList_New(5); + + // [0] = clothing name + PyList_SetItem(clothingItem, 0, PyString_FromString(item->GetName())); + + // [1] = clothing type + PyList_SetItem(clothingItem, 1, PyInt_FromLong(item->fType)); + + // [2] = description + const char* description = ""; // assume an empty string + if ( item->fDescription != nil ) + description = item->fDescription; + PyList_SetItem(clothingItem, 2, PyString_FromString(description)); + + // [3] = ptImage of icon + if ( item->fThumbnail != nil ) + // create a ptImage + PyList_SetItem(clothingItem, 3, pyImage::New(item->fThumbnail->GetKey())); + else + PyList_SetItem(clothingItem, 3, PyInt_FromLong(0)); + + // [4] = fCustomText + const char* custom = ""; // assume an empty string + if ( item->fCustomText != nil ) + custom = item->fCustomText; + PyList_SetItem(clothingItem, 4, PyString_FromString(custom)); + + retVal.push_back(clothingItem); + } + } + } + } + } + return retVal; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetAvatarClothingList +// PARAMETERS : +// +// PURPOSE : Return a list of the wearable items for this avatar of that clothing_type +// +std::vector cyAvatar::GetAvatarClothingList() +{ + std::vector retVal; + // find the avatar's armature modifier + const plArmatureMod *avMod = nil; + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + if ( avMod ) + { + // Currently, just all the clothing available will be returned + hsTArray clothingList = avMod->GetClothingOutfit()->GetItemList(); + int numItems = clothingList.GetCount(); + // create the string list to send to python... as a python object + int i; + for ( i=0; iGetName())); + + // [1] = clothing type + PyList_SetItem(clothingItem, 1, PyInt_FromLong(item->fType)); + + // [2] = description + const char* description = ""; // assume an empty string + if ( item->fDescription != nil ) + description = item->fDescription; + PyList_SetItem(clothingItem, 2, PyString_FromString(description)); + + // [3] = ptImage of icon + if ( item->fThumbnail != nil ) + // create a ptImage + PyList_SetItem(clothingItem, 3, pyImage::New(item->fThumbnail->GetKey())); + else + PyList_SetItem(clothingItem, 3, PyInt_FromLong(0)); + + // [4] = fCustomText + const char* custom = ""; // assume an empty string + if ( item->fCustomText != nil ) + custom = item->fCustomText; + PyList_SetItem(clothingItem, 4, PyString_FromString(custom)); + + retVal.push_back(clothingItem); + } + } + } + } + return retVal; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetWardrobeClothingList +// PARAMETERS : +// +// PURPOSE : Return a list of items that are in the avatars closet +// +std::vector cyAvatar::GetWardrobeClothingList() +{ + std::vector retVal; + hsTArray closetList; + plClothingMgr::GetClothingMgr()->GetClosetItems(closetList); + int numItems = closetList.GetCount(); + // create the string list to send to python... as a python object + int i; + for ( i=0; iGetName())); + + // [1] = clothing type + PyList_SetItem(closetItem, 1, PyInt_FromLong(closetList[i].fItem->fType)); + + // [2] = description + const char* description = ""; // assume an empty string + if ( closetList[i].fItem->fDescription != nil ) + description = closetList[i].fItem->fDescription; + PyList_SetItem(closetItem, 2, PyString_FromString(description)); + + // [3] = ptImage of icon + if ( closetList[i].fItem->fThumbnail != nil ) + // create a ptImage + PyList_SetItem(closetItem, 3, pyImage::New(closetList[i].fItem->fThumbnail->GetKey())); + else + PyList_SetItem(closetItem, 3, PyInt_FromLong(0)); + + // [4] = fCustomText + const char* custom = ""; // assume an empty string + if ( closetList[i].fItem->fCustomText != nil ) + custom = closetList[i].fItem->fCustomText; + PyList_SetItem(closetItem, 4, PyString_FromString(custom)); + + // [5] = fTint1 + PyList_SetItem(closetItem, 5, pyColor::New(closetList[i].fOptions.fTint1)); + + // [6] = fTint2 + PyList_SetItem(closetItem, 6, pyColor::New(closetList[i].fOptions.fTint2)); + + retVal.push_back(closetItem); + } + return retVal; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddWardrobeClothingItem +// PARAMETERS : clothing_name - the name of the clothing item to add to your wardrobe +// : tint1 - layer one color +// : tint2 - layer two color +// +// PURPOSE : To add a clothing item to the avatar's wardrobe (closet) +// +void cyAvatar::AddWardrobeClothingItem(const char* clothing_name,pyColor& tint1,pyColor& tint2) +{ + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name); + if ( item ) + { + hsTArray items; + items.SetCount(1); + items[0].fItem = item; + items[0].fOptions.fTint1.Set(tint1.getRed(), tint1.getGreen(), tint1.getBlue(), 1.f); + items[0].fOptions.fTint2.Set(tint2.getRed(), tint2.getGreen(), tint2.getBlue(), 1.f); + + plClothingMgr::GetClothingMgr()->AddItemsToCloset(items); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetUniqueMeshList +// PARAMETERS : clothing_type - the type of clothing to get +// +// PURPOSE : Return a list of unique clothing items (each has a different mesh) +// : that belong to the specific type +// +std::vector cyAvatar::GetUniqueMeshList(Int32 clothing_type) +{ + std::vector retVal; + + // find the avatar's armature modifier + const plArmatureMod *avMod = nil; + + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + if ( avMod ) + { + // Get all the clothes that we can wear + hsTArray clothingList; + plClothingMgr::GetClothingMgr()->GetItemsByGroup(avMod->GetClothingOutfit()->fGroup, clothingList); + plClothingMgr::GetClothingMgr()->FilterUniqueMeshes(clothingList); // filter all redundant meshes + int numItems = clothingList.GetCount(); + // create the string list to send to python... as a python object + int i; + for ( i=0; ifType == clothing_type ) + { + // add this event record to the main event list (lists within a list) + // create list + PyObject* clothingItem = PyList_New(5); + + // [0] = clothing name + PyList_SetItem(clothingItem, 0, PyString_FromString(item->GetName())); + + // [1] = clothing type + PyList_SetItem(clothingItem, 1, PyInt_FromLong(item->fType)); + + // [2] = description + const char* description = ""; // assume an empty string + if ( item->fDescription != nil ) + description = item->fDescription; + PyList_SetItem(clothingItem, 2, PyString_FromString(description)); + + // [3] = ptImage of icon + if ( item->fThumbnail != nil ) + // create a ptImage + PyList_SetItem(clothingItem, 3, pyImage::New(item->fThumbnail->GetKey())); + else + PyList_SetItem(clothingItem, 3, PyInt_FromLong(0)); + + // [4] = fCustomText + const char* custom = ""; // assume an empty string + if ( item->fCustomText != nil ) + custom = item->fCustomText; + PyList_SetItem(clothingItem, 4, PyString_FromString(custom)); + + retVal.push_back(clothingItem); + } + } + } + } + } + return retVal; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetAllWithSameMesh +// PARAMETERS : clothing_name - the name of the mesh to get the textures of +// +// PURPOSE : Return a list of clothing items that have the same mesh as +// : the item passed in +// +std::vector cyAvatar::GetAllWithSameMesh(const char* clothing_name) +{ + std::vector retVal; + + // find the avatar's armature modifier + const plArmatureMod *avMod = nil; + + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + if ( avMod ) + { + // Get all clothes with the same mesh as the one passed in + hsTArray clothingList; + plClothingMgr::GetClothingMgr()->GetAllWithSameMesh(plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name), clothingList); + int numItems = clothingList.GetCount(); + // create the string list to send to python... as a python object + int i; + for ( i=0; iGetName())); + + // [1] = clothing type + PyList_SetItem(clothingItem, 1, PyInt_FromLong(item->fType)); + + // [2] = description + const char* description = ""; // assume an empty string + if ( item->fDescription != nil ) + description = item->fDescription; + PyList_SetItem(clothingItem, 2, PyString_FromString(description)); + + // [3] = ptImage of icon + if ( item->fThumbnail != nil ) + // create a ptImage + PyList_SetItem(clothingItem, 3, pyImage::New(item->fThumbnail->GetKey())); + else + PyList_SetItem(clothingItem, 3, PyInt_FromLong(0)); + + // [4] = fCustomText + const char* custom = ""; // assume an empty string + if ( item->fCustomText != nil ) + custom = item->fCustomText; + PyList_SetItem(clothingItem, 4, PyString_FromString(custom)); + + retVal.push_back(clothingItem); + } + } + } + } + return retVal; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetMatchingClothingItem +// PARAMETERS : +// +// PURPOSE : Return the clothing item that matches this one +// : If no match then returns the number 0 +// +PyObject* cyAvatar::GetMatchingClothingItem(const char* clothing_name) +{ + // Get all the clothes that we can wear + hsTArray clothingList; + plClothingItem* match = plClothingMgr::GetClothingMgr()->GetLRMatch(plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name)); + if ( match ) + { + // create list + PyObject* clothingItem = PyList_New(5); + + // [0] = clothing name + PyList_SetItem(clothingItem, 0, PyString_FromString(match->GetName())); + + // [1] = clothing type + PyList_SetItem(clothingItem, 1, PyInt_FromLong(match->fType)); + + // [2] = description + const char* description = ""; // assume an empty string + if ( match->fDescription != nil ) + description = match->fDescription; + PyList_SetItem(clothingItem, 2, PyString_FromString(description)); + + // [3] = ptImage of icon + if ( match->fThumbnail != nil ) + // create a ptImage + PyList_SetItem(clothingItem, 3, pyImage::New(match->fThumbnail->GetKey())); + else + PyList_SetItem(clothingItem, 3, PyInt_FromLong(0)); + + // [4] = fCustomText + const char* custom = ""; // assume an empty string + if ( match->fCustomText != nil ) + custom = match->fCustomText; + PyList_SetItem(clothingItem, 4, PyString_FromString(custom)); + + return clothingItem; + } + else + return PyInt_FromLong(0); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : WearClothingItem +// PARAMETERS : +// +// PURPOSE : Wear a particular piece of clothing based on name of clothing item +// : returns 0, if clothing item was not found +// +hsBool cyAvatar::WearClothingItem(const char* clothing_name) +{ + return WearClothingItemU(clothing_name,true); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : RemoveClothingItem +// PARAMETERS : +// +// PURPOSE : Wear a particular piece of clothing based on name of clothing item +// : returns false, if clothing item was not found +// +hsBool cyAvatar::RemoveClothingItem(const char* clothing_name) +{ + return RemoveClothingItemU(clothing_name,true); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : TintClothingItem +// PARAMETERS : +// +// PURPOSE : Tint a clothing item, i.e. change the color of it +// +hsBool cyAvatar::TintClothingItem(const char* clothing_name, pyColor& tint) +{ + return TintClothingItemU(clothing_name,tint,true); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : TintClothingItemLayer +// PARAMETERS : clothing_name - name of the clothing item to change the color of +// : tint - what color to change it to +// : layer - which layer to change (1 or 2) +// +// PURPOSE : Tint a clothing item, i.e. change the color of it +// +hsBool cyAvatar::TintClothingItemLayer(const char* clothing_name, pyColor& tint, UInt8 layer) +{ + return TintClothingItemLayerU(clothing_name,tint,layer,true); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : WearClothingItem +// PARAMETERS : --- with update flag +// +// PURPOSE : Wear a particular piece of clothing based on name of clothing item +// : returns 0, if clothing item was not found +// +hsBool cyAvatar::WearClothingItemU(const char* clothing_name, hsBool update) +{ + const plArmatureMod *avMod = nil; + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name); + + if (avMod && item) + { + if ( fNetForce ) + avMod->GetClothingOutfit()->AddItem(item, update, true, true); + else + avMod->GetClothingOutfit()->AddItem(item, update); + return true; + } + } + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : RemoveClothingItemU +// PARAMETERS : --- with update flag +// +// PURPOSE : Wear a particular piece of clothing based on name of clothing item +// : returns false, if clothing item was not found +// +hsBool cyAvatar::RemoveClothingItemU(const char* clothing_name, hsBool update) +{ + const plArmatureMod *avMod = nil; + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name); + + if (avMod && item) + { + if ( fNetForce ) + avMod->GetClothingOutfit()->RemoveItem(item,update,true); + else + avMod->GetClothingOutfit()->RemoveItem(item,update); + return true; + } + } + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : TintClothingItemU +// PARAMETERS : --- with update flag +// +// PURPOSE : Tint a clothing item, i.e. change the color of it +// +hsBool cyAvatar::TintClothingItemU(const char* clothing_name, pyColor& tint, hsBool update) +{ + const plArmatureMod *avMod = nil; + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name); + + if (avMod && item) + { + avMod->GetClothingOutfit()->TintItem(item, tint.getRed(),tint.getGreen(),tint.getBlue(),update,true,fNetForce,true,plClothingElement::kLayerTint1); + return true; + } + } + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : TintClothingItemLayer +// PARAMETERS : clothing_name - name of the clothing item to change the color of +// : tint - what color to change it to +// : layer - which layer to change (1 or 2) +// +// PURPOSE : Tint a clothing item, i.e. change the color of it +// +hsBool cyAvatar::TintClothingItemLayerU(const char* clothing_name, pyColor& tint, UInt8 layer, hsBool update) +{ + const plArmatureMod *avMod = nil; + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name); + + if (avMod && item) + { + // Convert Python layer number to clothing code... + if (layer == 2) + layer = plClothingElement::kLayerTint2; + else + layer = plClothingElement::kLayerTint1; + avMod->GetClothingOutfit()->TintItem(item, tint.getRed(),tint.getGreen(),tint.getBlue(),update,true,fNetForce,true,layer); + return true; + } + } + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetClothingItemParameterString +// PARAMETERS : +// +// PURPOSE : Get the custom parameter string for a clothing item +// +const char* cyAvatar::GetClothingItemParameterString(const char* clothing_name) +{ + const plArmatureMod *avMod = nil; + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name); + + if (avMod && item) + { + if ( item->fCustomText != nil ) + return item->fCustomText; + else + return ""; + } + } + } + + return ""; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetTintClothingItem +// PARAMETERS : +// +// PURPOSE : Get the tint a clothing item, i.e. change the color of it +// +PyObject* cyAvatar::GetTintClothingItem(const char* clothing_name) +{ + return GetTintClothingItemL(clothing_name,1); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetTintClothingItem +// PARAMETERS : +// +// PURPOSE : Get the tint a clothing item, i.e. change the color of it +// +PyObject* cyAvatar::GetTintClothingItemL(const char* clothing_name, UInt8 layer) +{ + const plArmatureMod *avMod = nil; + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName((char*)clothing_name); + + if (avMod && item) + { + // Convert Python layer number to clothing code... + if (layer == 2) + layer = plClothingElement::kLayerTint2; + else + layer = plClothingElement::kLayerTint1; + hsColorRGBA tint = avMod->GetClothingOutfit()->GetItemTint(item,layer); + return pyColor::New(tint); + } + } + } + + char errmsg[256]; + sprintf(errmsg,"Cannot find clothing item %d to find out what tint it is",clothing_name); + PyErr_SetString(PyExc_KeyError, errmsg); + // returning nil means an error occurred + return nil; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : TintSkin +// PARAMETERS : +// +// PURPOSE : Tint the skin of the player's avatar +// +void cyAvatar::TintSkin(pyColor& tint) +{ + TintSkinU(tint,true); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : TintSkinU +// PARAMETERS : +// +// PURPOSE : Tint the skin of the player's avatar with optional update flag +// +void cyAvatar::TintSkinU(pyColor& tint, hsBool update) +{ + const plArmatureMod *avMod = nil; + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + avMod->GetClothingOutfit()->TintSkin(tint.getRed(),tint.getGreen(),tint.getBlue(),update,true); + } + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetTintSkin +// PARAMETERS : +// +// PURPOSE : Get the tint of the skin of the player's avatar +// +PyObject* cyAvatar::GetTintSkin() +{ + const plArmatureMod *avMod = nil; + // we can really only talk to one avatar, so just get the first one (which is probably the only one) + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + hsColorRGBA tint = avMod->GetClothingOutfit()->fSkinTint; + // now create the ptColor Python object + return pyColor::New(tint); + } + } + + char errmsg[256]; + sprintf(errmsg,"Cannot find the skin of the player. Whatever that means!"); + PyErr_SetString(PyExc_KeyError, errmsg); + // returning nil means an error occurred + return nil; +} + +plMorphSequence* cyAvatar::LocalMorphSequence() +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (!avMod) + return nil; + + + const plSceneObject *so = avMod->GetClothingSO(0); // grabbing the high LOD node + if (!so) + return nil; + + const plModifier* constSeq = nil; + int i; + for (i = 0; i < so->GetNumModifiers(); i++) + { + constSeq = so->GetModifier(i); + if (constSeq && plMorphSequence::ConvertNoRef(constSeq)) + { + return (plMorphSequence*)constSeq; // safe cast, we've already checked type (plus we're const_cast'ing). + } + } + + return nil; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SetMorph +// PARAMETERS : clothing_name - the name of the clothing to morph +// : layer - the layer to affect +// : value - what the new value should be (clipped between -1 and 1) +// +// PURPOSE : Set the morph value of a specific layer of clothing +// +void cyAvatar::SetMorph(const char* clothing_name, UInt8 layer, float value) +{ + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName(clothing_name); + if( !item ) + { + PyErr_SetString(PyExc_KeyError, "Item not found"); + return; + } + + float wgtPlus; + float wgtMinus; + + if(value > 1.0) value = 1.0; + if(value < -1.0) value = -1.0; + + if (value > 0) + { + wgtPlus = value; + wgtMinus = 0; + } + else + { + wgtMinus = -value; + wgtPlus = 0; + } + + if ( fRecvr.Count() > 0 && fRecvr[0] != nil ) + { + plSceneObject *so = plSceneObject::ConvertNoRef(fRecvr[0]->GetObjectPtr()); + if (so != nil) + { + const plArmatureMod *avMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + if (avMod && avMod->GetClothingOutfit()) + { + avMod->GetClothingOutfit()->MorphItem(item, layer, 0, wgtPlus, true); + avMod->GetClothingOutfit()->MorphItem(item, layer, 1, wgtMinus, true); + } + } + } + + +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetMorph +// PARAMETERS : clothing_name - the name of the clothing to get the value from +// : layer - the layer to get the value from +// +// PURPOSE : Returns the current morph value of the specific layer of clothing +// +float cyAvatar::GetMorph(const char* clothing_name, UInt8 layer) +{ + plMorphSequence* seq = LocalMorphSequence(); + if( !seq ) + { + PyErr_SetString(PyExc_KeyError, "Sequence not found"); + return 0; + } + + plClothingItem *item = plClothingMgr::GetClothingMgr()->FindItemByName(clothing_name); + if( !item ) + { + PyErr_SetString(PyExc_KeyError, "Item not found"); + return 0; + } + + plKey meshKey = item->fMeshes[0]->GetKey(); + + if (layer >= seq->GetNumLayers(meshKey)) + { + PyErr_SetString(PyExc_KeyError, "Layer index too high"); + return 0; + } + + float wgtPlus; + float wgtMinus; + + wgtPlus = seq->GetWeight(layer,0,meshKey); + wgtMinus = seq->GetWeight(layer,1,meshKey); + + if (wgtPlus > 0) + return wgtPlus; + else + return -wgtMinus; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SetSkinBlend +// PARAMETERS : layer - the layer to affect +// : value - what the new value should be (clipped between 0 and 1) +// +// PURPOSE : Set the skin blend for the specified layer +// +void cyAvatar::SetSkinBlend(UInt8 layer, float value) +{ + if (value < 0.0) value = 0.0; + if (value > 1.0) value = 1.0; + + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avMod) + { + avMod->GetClothingOutfit()->SetSkinBlend(value, (int)layer + plClothingElement::kLayerSkinBlend1 - 1); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetSkinBlend +// PARAMETERS : layer - the layer to get the blend for +// +// PURPOSE : Returns the current layer's skin blend +// +float cyAvatar::GetSkinBlend(UInt8 layer) +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avMod) + { + return avMod->GetClothingOutfit()->GetSkinBlend((int)layer + plClothingElement::kLayerSkinBlend1 - 1); + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SaveClothing +// PARAMETERS : +// +// PURPOSE : Saves the current clothing to the vault (including morphs) +// +void cyAvatar::SaveClothing() +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avMod) + avMod->GetClothingOutfit()->SaveCustomizations(); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : EnterSubWorld +// PARAMETERS : object - a sceneobject that is in the subworld +// +// PURPOSE : Place the Avatar into the subworld of the sceneobject specified +// +void cyAvatar::EnterSubWorld(pySceneObject& object) +{ + // make sure that there is atleast one avatar scene object attached (should be) + if ( fRecvr.Count() > 0) + { + // find the armature modifier + plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]); + if(avatar) + { + // get the sceneobject that we will use to find the subworld + plKey SOkey = object.getObjKey(); + if ( SOkey ) + { + plSceneObject *SO = plSceneObject::ConvertNoRef(SOkey->ObjectIsLoaded()); + if(SO) + { + plKey subWorldKey = SOkey; + plKey physKey = avatar->GetKey(); + plKey nilKey; // sorry + plSubWorldMsg *swMsg = TRACKED_NEW plSubWorldMsg(nilKey, physKey, subWorldKey); + swMsg->Send(); + } + } + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ExitSubWorld +// PARAMETERS : (none) +// +// PURPOSE : Exit the avatar from the subworld, back into the ... world +// +void cyAvatar::ExitSubWorld() +{ + // make sure that there is atleast one avatar scene object attached (should be) + if ( fRecvr.Count() > 0) + { + // find the armature modifier + plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]); + if(avatar) + { + plKey subWorldKey; // we're going to the nil subworld + plKey physKey = avatar->GetKey(); + plKey nilKey; // sorry + plSubWorldMsg *swMsg = TRACKED_NEW plSubWorldMsg(nilKey, physKey, subWorldKey); + swMsg->Send(); + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : PlaySimpleAnimation +// PARAMETERS : object - a sceneobject that is in the subworld +// +// PURPOSE : Place the Avatar into the subworld of the sceneobject specified +// +void cyAvatar::PlaySimpleAnimation(const char* animName) +{ + // make sure that there is atleast one avatar scene object attached (should be) + if ( fRecvr.Count() > 0) + { + // find the armature modifier + plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]); + if(avatar) + { + avatar->PlaySimpleAnim(animName); + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ChangeAvatar +// PARAMETERS : gender name - is a string of the name of the gender to go to +// +// PURPOSE : Change the local avatar's gender. +// +// Valid genders: +// Male +// Female +// +void cyAvatar::ChangeAvatar(const char* genderName) +{ +#ifndef PLASMA_EXTERNAL_RELEASE + plClothingMgr::ChangeAvatar((char*)genderName); + + wchar wStr[MAX_PATH]; + StrToUnicode(wStr, genderName, arrsize(wStr)); + + RelVaultNode * rvnPlr = VaultGetPlayerNodeIncRef(); + if (rvnPlr) { + VaultPlayerNode plr(rvnPlr); + plr.SetAvatarShapeName(wStr); + rvnPlr->DecRef(); + } +#endif +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ChangePlayerName +// PARAMETERS : name - is a string of the new name for the player +// +// PURPOSE : Change the local player's avatar name +// +void cyAvatar::ChangePlayerName(const char* playerName) +{ + wchar wStr[MAX_PATH]; + StrToUnicode(wStr, playerName, arrsize(wStr)); + + RelVaultNode * rvnPlr = VaultGetPlayerNodeIncRef(); + if (rvnPlr) { + VaultPlayerNode plr(rvnPlr); + plr.SetPlayerName(wStr); + rvnPlr->DecRef(); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Emote +// PARAMETERS : emoteName - name of the emote to play on the avatar +// +// PURPOSE : plays an emote on a the local avatar (net propagated) +// +bool cyAvatar::Emote(const char* emoteName) +{ + // can we find an emote of this name? + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + return AvatarEmote(avatar, emoteName); +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Sit +// PARAMETERS : none +// +// PURPOSE : Makes the avatar sit down on the ground where they are. +// The avatar will automatically stand when the user tries to move. +// +bool cyAvatar::Sit() +{ + return IEnterGenericMode("SitDownGround", "SitIdleGround", "SitStandGround", true, plAGAnim::kBodyLower, plAvBrainGeneric::kSitOnGround); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : EnterKiMode +// PARAMETERS : none +// +// PURPOSE : Makes the avatar appear to be using the ki. +// +bool cyAvatar::EnterKiMode() +{ + return IEnterGenericMode("KiBegin", "KiUse", "KiEnd", false, plAGAnim::kBodyFull); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ExitKiMode +// PARAMETERS : none +// +// PURPOSE : Makes the avatar stop appearing to use the ki. +// May cause problems if EnterKiMode() was not called earlier. +// +bool cyAvatar::ExitKiMode() +{ + return IExitTopmostGenericMode(); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : EnterAFKMode +// PARAMETERS : none +// +// PURPOSE : Tell the avatar to enter the AFK mode (sitting, head down) +// +bool cyAvatar::EnterAFKMode() +{ + return IEnterGenericMode("AFKEnter", "AFKIdle", "AFKExit", true, plAGAnim::kBodyFull, plAvBrainGeneric::kAFK); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ExitAFKMode +// PARAMETERS : none +// +// PURPOSE : Tell the avatar to exit the AFK mode +// May cause problems if EnterKiMode() was not called earlier. +// +bool cyAvatar::ExitAFKMode() +{ + return IExitTopmostGenericMode(); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : EnterPBMode +// PARAMETERS : none +// +// PURPOSE : Enter the personal book mode...stay until further notice. +// +bool cyAvatar::EnterPBMode() +{ + return IEnterGenericMode("PersonalBookEnter", "PersonalBookIdle", "PersonalBookExit", false, plAGAnim::kBodyFull); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ExitPBMode +// PARAMETERS : none +// +// PURPOSE : Leave the personal book mode. Currently leaves any mode; will become +// : more specific in future version +// +bool cyAvatar::ExitPBMode() +{ + return IExitTopmostGenericMode(); +} + + +int cyAvatar::GetCurrentMode() +{ + // make sure that there is atleast one avatar scene object attached (should be) + if ( fRecvr.Count() > 0) + { + // find the armature modifier + plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]); + if(avatar) + { + return avatar->GetCurrentGenericType(); + } + } + return plAvBrainGeneric::kNonGeneric; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : disable movement controls +// PARAMETERS : +// +// PURPOSE : something tells me python shouldn't do this this way +// + + +void cyAvatar::DisableMovementControls() +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avatar) + { + if (!avatar->IsInputSuspended()) + avatar->SuspendInput(); + } +} + +void cyAvatar::EnableMovementControls() +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avatar) + { + if (avatar->IsInputSuspended()) + avatar->ResumeInput(); + } +} + +void cyAvatar::DisableMouseMovement() +{ + plAvatarInputInterface::GetInstance()->SuspendMouseMovement(); +} + +void cyAvatar::EnableMouseMovement() +{ + plAvatarInputInterface::GetInstance()->EnableMouseMovement(); +} + +void cyAvatar::EnableAvatarJump() +{ + plAvatarInputInterface::GetInstance()->EnableJump(true); +} + +void cyAvatar::DisableAvatarJump() +{ + plAvatarInputInterface::GetInstance()->EnableJump(false); +} + +void cyAvatar::EnableForwardMovement() +{ + plAvatarInputInterface::GetInstance()->EnableForwardMovement(true); +} + +void cyAvatar::DisableForwardMovement() +{ + plAvatarInputInterface::GetInstance()->EnableForwardMovement(false); +} + +bool cyAvatar::LocalAvatarRunKeyDown() +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + return avMod->FastKeyDown(); + return false; +} + +bool cyAvatar::LocalAvatarIsMoving() +{ + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avMod) + return avMod->ForwardKeyDown() || avMod->BackwardKeyDown() || avMod->StrafeRightKeyDown() || + avMod->StrafeLeftKeyDown() || avMod->TurnRightKeyDown() || avMod->TurnLeftKeyDown() || + avMod->JumpKeyDown(); + return false; +} + +void cyAvatar::SetMouseTurnSensitivity(hsScalar val) +{ + plArmatureMod::SetMouseTurnSensitivity(val); +} + +hsScalar cyAvatar::GetMouseTurnSensitivity() +{ + return plArmatureMod::GetMouseTurnSensitivity(); +} + +void cyAvatar::SpawnNext() +{ + static int whichSpawn = 0; + plAvatarMgr *mgr = plAvatarMgr::GetInstance(); + int max = mgr->NumSpawnPoints(); + + whichSpawn = ++whichSpawn < max ? whichSpawn : 0; + + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if(avatar) + { + double fakeTime = 0.0f; + avatar->SpawnAt(whichSpawn, fakeTime); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : RegisterForBehaviorNotify() +// PARAMETERS : none +// +// PURPOSE : To register for notifies from the avatar for any kind of behavior notify +// +void cyAvatar::RegisterForBehaviorNotify(pyKey &selfKey) +{ + // make sure that there is atleast one avatar scene object attached (should be) + if ( fRecvr.Count() > 0) + { + // find the armature modifier + plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]); + if(avatar) + { + avatar->RegisterForBehaviorNotify(selfKey.getKey()); + } + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : UnRegisterForBehaviorNotify() +// PARAMETERS : none +// +// PURPOSE : To remove the registeration for notifies from the avatar +// +void cyAvatar::UnRegisterForBehaviorNotify(pyKey &selfKey) +{ + // make sure that there is atleast one avatar scene object attached (should be) + if ( fRecvr.Count() > 0) + { + // find the armature modifier + plArmatureMod* avatar = (plArmatureMod*)IFindArmatureMod((plKey)fRecvr[0]); + if(avatar) + { + avatar->UnRegisterForBehaviorNotify(selfKey.getKey()); + } + } +} + + + + + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IEnterGenericMode +// PARAMETERS : none +// +// PURPOSE : Three-stage multistage animations (sit down, sit, get up) are really common. +// : This does the basic setup. +// +bool IEnterGenericMode(const char *enterAnim, const char *idleAnim, const char *exitAnim, bool autoExit, plAGAnim::BodyUsage bodyUsage, + plAvBrainGeneric::BrainType type /* = kGeneric */) +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + return PushSimpleMultiStage(avatar, enterAnim, idleAnim, exitAnim, true, autoExit, bodyUsage, type); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IExitTopmostGenericMode +// PARAMETERS : none +// +// PURPOSE : Exits whatever multistage animation you're in. We currently don't discriminate; +// : that will be added later. +// +bool IExitTopmostGenericMode() +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + plAvBrainGenericMsg* pMsg = TRACKED_NEW plAvBrainGenericMsg(nil, avatar->GetKey(), + plAvBrainGenericMsg::kGotoStage, 2, false, 0.0, + false, false, 0.0); + + pMsg->SetBCastFlag(plMessage::kNetForce | plMessage::kNetPropagate); + + plgDispatch::MsgSend( pMsg ); + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IsCurrentBrainHuman +// PARAMETERS : none +// +// PURPOSE : Returns whether the top most brain is a human brain +// +hsBool cyAvatar::IsCurrentBrainHuman() +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avatar) + { + plArmatureBrain *brain = avatar->GetCurrentBrain(); + plAvBrainHuman *human = plAvBrainHuman::ConvertNoRef(brain); + if (human) + return true; + } + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h new file mode 100644 index 00000000..3df20f03 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h @@ -0,0 +1,555 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef cyAvatar_h +#define cyAvatar_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: cyAvatar +// +// PURPOSE: Class wrapper to map animation functions to plasma2 message +// +#include "hsStlUtils.h" +#include "hsTemplates.h" +#include "hsBitVector.h" +#include "../pnKeyedObject/plKey.h" + +#include +#include "pyGlueHelpers.h" + + +class plKey; +class pyKey; +class pySceneObject; +class pyColor; +class plMipmap; +class plClothingItem; +class plArmatureMod; +class plMorphSequence; + +class cyAvatar +{ +protected: + plKey fSender; + hsTArray fRecvr; + hsBool fNetForce; + + virtual const plArmatureMod* IFindArmatureMod(plKey avObj); + virtual plKey IFindArmatureModKey(plKey avObj); + +// XX static bool IEnterGenericMode(const char *enterAnim, const char *idleAnim, const char *exitAnim, bool autoExit); +// XX static bool IExitTopmostGenericMode(); + +protected: + cyAvatar() {} + cyAvatar(plKey sender,plKey recvr=nil); + +public: + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptAvatar); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject* New(PyObject* sender, PyObject* recvr = nil); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a cyAvatar object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(cyAvatar); // converts a PyObject to a cyAvatar (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaMethods(std::vector &methods); + static void AddPlasmaConstantsClasses(PyObject *m); + + // setters + void SetSender(plKey &sender); + void AddRecvr(plKey &recvr); + virtual void SetNetForce(hsBool state); + + // oneShot Avatar (must already be there) + virtual void OneShot(pyKey &seekKey, float duration, hsBool usePhysics, + const char *animName, hsBool drivable, hsBool reversible); + + // oneShot Avatar + virtual void RunBehavior(pyKey &behKey, hsBool netForce, hsBool netProp); + virtual void RunBehaviorAndReply(pyKey& behKey, pyKey& replyKey, hsBool netForce, hsBool netProp); + + // for the multistage behaviors + virtual void NextStage(pyKey &behKey, hsScalar transTime, hsBool setTime, hsScalar newTime, + hsBool setDirection, bool isForward, hsBool netForce); + virtual void PreviousStage(pyKey &behKey, hsScalar transTime, hsBool setTime, hsScalar newTime, + hsBool setDirection, bool isForward, hsBool netForce); + virtual void GoToStage(pyKey &behKey, Int32 stage, hsScalar transTime, hsBool setTime, hsScalar newTime, + hsBool setDirection, bool isForward, hsBool netForce); + + // static behavior functions: + static void SetLoopCount(pyKey &behKey, Int32 stage, Int32 loopCount, hsBool netForce); + + virtual void SetSenderKey(pyKey &pKey); + + // seek Avatar (must already be there) + //virtual void Seek(pyKey &seekKey, float duration, hsBool usePhysics); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetAvatarClothingGroup + // PARAMETERS : + // + // PURPOSE : Return what clothing group the avatar is in + // + virtual Int32 GetAvatarClothingGroup(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetEntireClothingList + // PARAMETERS : + // + // PURPOSE : Return a list of the wearable items for this avatar of that clothing_type + // + virtual std::vector GetEntireClothingList(Int32 clothing_type); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetClosetClothingList + // PARAMETERS : + // + // PURPOSE : Return a list of the wearable items for this avatar of that clothing_type + // + virtual std::vector GetClosetClothingList(Int32 clothing_type); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetAvatarClothingList + // PARAMETERS : + // + // PURPOSE : Return a list of items being worn by this avatar + // + virtual std::vector GetAvatarClothingList(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetWardrobeClothingList + // PARAMETERS : + // + // PURPOSE : Return a list of items that are in the avatars closet + // + virtual std::vector GetWardrobeClothingList(); + ///////////////////////////////////////////////////////////////////////////// + // + // Function : AddWardrobeClothingItem + // PARAMETERS : clothing_name - the name of the clothing item to add to your wardrobe + // : tint1 - layer one color + // : tint2 - layer two color + // + // PURPOSE : To add a clothing item to the avatar's wardrobe (closet) + // + virtual void AddWardrobeClothingItem(const char* clothing_name,pyColor& tint1,pyColor& tint2); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetUniqueMeshList + // PARAMETERS : clothing_type - the type of clothing to get + // + // PURPOSE : Return a list of unique clothing items (each has a different mesh) + // : that belong to the specific type + // + virtual std::vector GetUniqueMeshList(Int32 clothing_type); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetAllWithSameMesh + // PARAMETERS : clothing_name - the name of the mesh to get the textures of + // + // PURPOSE : Return a list of clothing items that have the same mesh as + // : the item passed in + // + virtual std::vector GetAllWithSameMesh(const char* clothing_name); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetMatchingClothingItem + // PARAMETERS : + // + // PURPOSE : Return the clothing item that matches this one + // + virtual PyObject* GetMatchingClothingItem(const char* clothing_name); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : WearClothingItem + // PARAMETERS : + // + // PURPOSE : Wear a particular piece of clothing based on name of clothing item + // : returns 0, if clothing item was not found + // + virtual hsBool WearClothingItem(const char* clothing_name); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : RemoveClothingItem + // PARAMETERS : + // + // PURPOSE : Remove (take off) a particular piece of clothing based on name of clothing item + // : returns 0, if clothing item was not found + // + virtual hsBool RemoveClothingItem(const char* clothing_name); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : TintClothingItem + // PARAMETERS : + // + // PURPOSE : Tint a clothing item, i.e. change the color of it + // + virtual hsBool TintClothingItem(const char* clothing_name, pyColor& tint); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : TintClothingItemLayer + // PARAMETERS : clothing_name - name of the clothing item to change the color of + // : tint - what color to change it to + // : layer - which layer to change (1 or 2) + // + // PURPOSE : Tint a clothing item, i.e. change the color of it + // + virtual hsBool TintClothingItemLayer(const char* clothing_name, pyColor& tint, UInt8 layer); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : WearClothingItem + // PARAMETERS : + // + // PURPOSE : Wear a particular piece of clothing based on name of clothing item + // : returns 0, if clothing item was not found + // + virtual hsBool WearClothingItemU(const char* clothing_name, hsBool update); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : RemoveClothingItem + // PARAMETERS : + // + // PURPOSE : Remove (take off) a particular piece of clothing based on name of clothing item + // : returns 0, if clothing item was not found + // + virtual hsBool RemoveClothingItemU(const char* clothing_name, hsBool update); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : TintClothingItem + // PARAMETERS : + // + // PURPOSE : Tint a clothing item, i.e. change the color of it + // + virtual hsBool TintClothingItemU(const char* clothing_name, pyColor& tint, hsBool update); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : TintClothingItemLayer + // PARAMETERS : clothing_name - name of the clothing item to change the color of + // : tint - what color to change it to + // : whatpart - which layer to change (1 or 2) + // + // PURPOSE : Tint a clothing item, i.e. change the color of it + // + virtual hsBool TintClothingItemLayerU(const char* clothing_name, pyColor& tint, UInt8 layer, hsBool update); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetClothingItemParameterString + // PARAMETERS : + // + // PURPOSE : Get the custom parameter string for a clothing item + // + virtual const char* GetClothingItemParameterString(const char* clothing_name); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetTintClothingItem + // PARAMETERS : + // + // PURPOSE : Get the tint a clothing item, i.e. change the color of it + // + virtual PyObject* GetTintClothingItem(const char* clothing_name); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetTintClothingItem + // PARAMETERS : + // + // PURPOSE : Get the tint a clothing item, i.e. change the color of it + // + virtual PyObject* GetTintClothingItemL(const char* clothing_name, UInt8 layer); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : TintSkin + // PARAMETERS : + // + // PURPOSE : Tint the skin of the player's avatar + // + virtual void TintSkin(pyColor& tint); + ///////////////////////////////////////////////////////////////////////////// + // + // Function : TintSkinU + // PARAMETERS : + // + // PURPOSE : Tint the skin of the player's avatar with optional update flag + // + virtual void TintSkinU(pyColor& tint, hsBool update); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetTintSkin + // PARAMETERS : + // + // PURPOSE : Get the tint of the skin of the player's avatar + // + virtual PyObject* GetTintSkin(); + + virtual plMorphSequence* LocalMorphSequence(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SetMorph + // PARAMETERS : clothing_name - the name of the clothing to morph + // : layer - the layer to affect + // : value - what the new value should be (clipped between -1 and 1) + // + // PURPOSE : Set the morph value of a specific layer of clothing + // + virtual void SetMorph(const char* clothing_name, UInt8 layer, float value); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetMorph + // PARAMETERS : clothing_name - the name of the clothing to get the vaule from + // : layer - the layer to get the value from + // + // PURPOSE : Returns the current morph value of the specific layer of clothing + // + virtual float GetMorph(const char* clothing_name, UInt8 layer); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SetSkinBlend + // PARAMETERS : layer - the layer to affect + // : value - what the new value should be (clipped between 0 and 1) + // + // PURPOSE : Set the skin blend for the specified layer + // + virtual void SetSkinBlend(UInt8 layer, float value); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetSkinBlend + // PARAMETERS : layer - the layer to get the blend for + // + // PURPOSE : Returns the current layer's skin blend + // + virtual float GetSkinBlend(UInt8 layer); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SaveClothing + // PARAMETERS : + // + // PURPOSE : Saves the current clothing to the vault (including morphs) + // + virtual void SaveClothing(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : EnterSubWorld + // PARAMETERS : object - a sceneobject that is in the subworld + // + // PURPOSE : Place the Avatar into the subworld of the sceneobject specified + // + virtual void EnterSubWorld(pySceneObject& object); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : ExitSubWorld + // PARAMETERS : (none) + // + // PURPOSE : Exit the avatar from the subworld, back into the ... world + // + virtual void ExitSubWorld(); + + virtual void PlaySimpleAnimation(const char* animName); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : ChangeAvatar + // PARAMETERS : gender name - is a string of the name of the gender to go to + // + // PURPOSE : Change the local avatar's gender. + // + // Valid genders: + // Male + // Female + // + static void ChangeAvatar(const char* genderName); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : ChangePlayerName + // PARAMETERS : name - is a string of the new name for the player + // + // PURPOSE : Change the local player's avatar name + // + static void ChangePlayerName(const char* playerName); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : Emote + // PARAMETERS : emoteName - name of the emote to play on the avatar + // + // PURPOSE : plays an emote on the local avatar (net propagated) + // + static bool Emote(const char* emoteName); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : Sit + // PARAMETERS : none + // + // PURPOSE : Makes the avatar sit down on the ground where they are. + // The avatar will automatically stand when the user tries to move. + // + static bool Sit(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : EnterKiMode + // PARAMETERS : none + // + // PURPOSE : Makes the avatar appear to be using the ki. + // + static bool EnterKiMode(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : ExitKiMode + // PARAMETERS : none + // + // PURPOSE : Makes the avatar stop appearing to use the ki. + // May cause problems if EnterKiMode() was not called earlier. + // + static bool ExitKiMode(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : EnterAFKMode + // PARAMETERS : none + // + // PURPOSE : Tell the avatar to enter the AFK mode (sitting, head down) + // + static bool EnterAFKMode(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : ExitKiMode + // PARAMETERS : none + // + // PURPOSE : Tell the avatar to exit the AFK mode + // May cause problems if EnterKiMode() was not called earlier. + // + static bool ExitAFKMode(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : EnterPBMode + // PARAMETERS : none + // + // PURPOSE : Enter the personal book mode...stay until further notice. + // + static bool cyAvatar::EnterPBMode(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : ExitPBMode + // PARAMETERS : none + // + // PURPOSE : Leave the personal book mode. Currently leaves any mode; will become + // : more specific in future version + // + static bool cyAvatar::ExitPBMode(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetCurrentMode() + // PARAMETERS : none + // + // PURPOSE : Leave the personal book mode. Currently leaves any mode; will become + // : more specific in future version + // + int GetCurrentMode(); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : (En/Dis)ableMovementControls() + // PARAMETERS : none + // + // PURPOSE : Suspend input on the local avatar + // + static void EnableMovementControls(); + static void DisableMovementControls(); + static void EnableMouseMovement(); + static void DisableMouseMovement(); + static void EnableAvatarJump(); + static void DisableAvatarJump(); + static void EnableForwardMovement(); + static void DisableForwardMovement(); + + static bool LocalAvatarRunKeyDown(); + static bool LocalAvatarIsMoving(); + + static void SetMouseTurnSensitivity(hsScalar val); + static hsScalar GetMouseTurnSensitivity(); + + static void SpawnNext(); + ///////////////////////////////////////////////////////////////////////////// + // + // Function : RegisterForBehaviorNotify() + // PARAMETERS : none + // + // PURPOSE : To register for notifies from the avatar for any kind of behavior notify + // + void RegisterForBehaviorNotify(pyKey &selfKey); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : UnRegisterForBehaviorNotify() + // PARAMETERS : none + // + // PURPOSE : To remove the registeration for notifies from the avatar + // + void UnRegisterForBehaviorNotify(pyKey &selfKey); + + static hsBool IsCurrentBrainHuman(); + +}; + +#endif // cyAvatar_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp new file mode 100644 index 00000000..3423add3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp @@ -0,0 +1,886 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyAvatar.h" +#include "pyKey.h" +#include "pyEnum.h" +#include "pyColor.h" +#include "pySceneObject.h" + +#include "../plAvatar/plAvBrainHuman.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptAvatar, cyAvatar); + +PYTHON_DEFAULT_NEW_DEFINITION(ptAvatar, cyAvatar); +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptAvatar); + +PYTHON_NO_INIT_DEFINITION(ptAvatar); + +PYTHON_METHOD_DEFINITION(ptAvatar, netForce, args) +{ + char forceFlag; + if (!PyArg_ParseTuple(args, "b", &forceFlag)) + { + PyErr_SetString(PyExc_TypeError, "netForce expects a boolean"); + PYTHON_RETURN_ERROR; + } + + self->fThis->SetNetForce(forceFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, oneShot, args) +{ + PyObject* keyObj = NULL; + float duration; + char usePhysics; + char* animName = NULL; + char drivable, reversable; + if (!PyArg_ParseTuple(args, "Ofbsbb", &keyObj, &duration, &usePhysics, &animName, &drivable, &reversable)) + { + PyErr_SetString(PyExc_TypeError, "oneShot expects a ptKey, float, boolean, string, and two booleans"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "oneShot expects a ptKey, float, boolean, string, and two booleans"); + PYTHON_RETURN_ERROR; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + std::string animNameStr = animName; // convert to string (for safety) + self->fThis->OneShot(*key, duration, usePhysics != 0, animNameStr.c_str(), drivable != 0, reversable != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, runBehavior, args) +{ + PyObject* keyObj = NULL; + char netForce; + char netProp = 1; + if (!PyArg_ParseTuple(args, "Ob|b", &keyObj, &netForce, &netProp)) + { + PyErr_SetString(PyExc_TypeError, "runBehavior expects a ptKey and a boolean and an optional boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "runBehavior expects a ptKey and a boolean and an optional boolean"); + PYTHON_RETURN_ERROR; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->RunBehavior(*key, netForce != 0, netProp != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, runBehaviorSetNotify, args) +{ + PyObject* behKeyObj = NULL; + PyObject* replyKeyObj = NULL; + char netForce; + char netProp = 1; + if (!PyArg_ParseTuple(args, "OOb|b", &behKeyObj, &replyKeyObj, &netForce, &netProp)) + { + PyErr_SetString(PyExc_TypeError, "runBehaviorSetNotify expects two ptKeys and a boolean and an optional boolean"); + PYTHON_RETURN_ERROR; + } + if ((!pyKey::Check(behKeyObj)) || (!pyKey::Check(replyKeyObj))) + { + PyErr_SetString(PyExc_TypeError, "runBehaviorSetNotify expects two ptKeys and a boolean and an optional boolean"); + PYTHON_RETURN_ERROR; + } + + pyKey* behKey = pyKey::ConvertFrom(behKeyObj); + pyKey* replyKey = pyKey::ConvertFrom(replyKeyObj); + self->fThis->RunBehaviorAndReply(*behKey, *replyKey, netForce != 0, netProp != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, nextStage, args) +{ + PyObject* keyObj = NULL; + float transTime; + char setTime; + float newTime; + char setDirection, isForward, netForce; + if (!PyArg_ParseTuple(args, "Ofbfbbb", &keyObj, &transTime, &setTime, &newTime, &setDirection, &isForward, &netForce)) + { + PyErr_SetString(PyExc_TypeError, "nextStage expects a ptkey, float, bool, float, and three booleans"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "nextStage expects a ptkey, float, bool, float, and three booleans"); + PYTHON_RETURN_ERROR; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->NextStage(*key, transTime, setTime != 0, newTime, setDirection != 0, isForward != 0, netForce != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, previousStage, args) +{ + PyObject* keyObj = NULL; + float transTime; + char setTime; + float newTime; + char setDirection, isForward, netForce; + if (!PyArg_ParseTuple(args, "Ofbfbbb", &keyObj, &transTime, &setTime, &newTime, &setDirection, &isForward, &netForce)) + { + PyErr_SetString(PyExc_TypeError, "previousStage expects a ptkey, float, bool, float, and three booleans"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "previousStage expects a ptkey, float, bool, float, and three booleans"); + PYTHON_RETURN_ERROR; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->PreviousStage(*key, transTime, setTime != 0, newTime, setDirection != 0, isForward != 0, netForce != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, gotoStage, args) +{ + PyObject* keyObj = NULL; + long stage; + float transTime; + char setTime; + float newTime; + char setDirection, isForward, netForce; + if (!PyArg_ParseTuple(args, "Olfbfbbb", &keyObj, &stage, &transTime, &setTime, &newTime, &setDirection, &isForward, &netForce)) + { + PyErr_SetString(PyExc_TypeError, "previousStage expects a ptkey, long, float, bool, float, and three booleans"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "previousStage expects a ptkey, long, float, bool, float, and three booleans"); + PYTHON_RETURN_ERROR; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->GoToStage(*key, stage, transTime, setTime != 0, newTime, setDirection != 0, isForward != 0, netForce != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAvatar, getAvatarClothingGroup) +{ + return PyLong_FromLong(self->fThis->GetAvatarClothingGroup()); +} + +PYTHON_METHOD_DEFINITION(ptAvatar, getEntireClothingList, args) +{ + long clothingType; + if (!PyArg_ParseTuple(args, "l", &clothingType)) + { + PyErr_SetString(PyExc_TypeError, "getEntireClothingList expects a long"); + PYTHON_RETURN_ERROR; + } + + std::vector clothingList = self->fThis->GetEntireClothingList(clothingType); + PyObject* retVal = PyList_New(clothingList.size()); + for (int i = 0; i < clothingList.size(); i++) + PyList_SetItem(retVal, i, PyString_FromString(clothingList[i].c_str())); + return retVal; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, getClosetClothingList, args) +{ + long clothingType; + if (!PyArg_ParseTuple(args, "l", &clothingType)) + { + PyErr_SetString(PyExc_TypeError, "getClosetCothingList expects a long"); + PYTHON_RETURN_ERROR; + } + + std::vector clothingList = self->fThis->GetClosetClothingList(clothingType); + PyObject* retVal = PyList_New(clothingList.size()); + for (int i = 0; i < clothingList.size(); i++) + PyList_SetItem(retVal, i, clothingList[i]); // steals the ref, so no need to decref + return retVal; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAvatar, getAvatarClothingList) +{ + std::vector clothingList = self->fThis->GetAvatarClothingList(); + PyObject* retVal = PyList_New(clothingList.size()); + for (int i = 0; i < clothingList.size(); i++) + PyList_SetItem(retVal, i, clothingList[i]); // steals the ref, so no need to decref + return retVal; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, getMatchingClothingItem, args) +{ + char* clothingName = NULL; + if (!PyArg_ParseTuple(args, "s", &clothingName)) + { + PyErr_SetString(PyExc_TypeError, "getMatchingClothingItem expects a string"); + PYTHON_RETURN_ERROR; + } + + std::string clothingNameStr = clothingName; // convert to string (for safety) + return self->fThis->GetMatchingClothingItem(clothingNameStr.c_str()); +} + +PYTHON_METHOD_DEFINITION(ptAvatar, wearClothingItem, args) +{ + char* clothingName = NULL; + char update = 1; + if (!PyArg_ParseTuple(args, "s|b", &clothingName, &update)) + { + PyErr_SetString(PyExc_TypeError, "wearClothingItem expects a string and an optional boolean"); + PYTHON_RETURN_ERROR; + } + + std::string clothingNameStr = clothingName; // convert to string (for safety) + PYTHON_RETURN_BOOL(self->fThis->WearClothingItemU(clothingNameStr.c_str(), update != 0)); +} + +PYTHON_METHOD_DEFINITION(ptAvatar, removeClothingItem, args) +{ + char* clothingName = NULL; + char update = 1; + if (!PyArg_ParseTuple(args, "s|b", &clothingName, &update)) + { + PyErr_SetString(PyExc_TypeError, "removeClothingItem expects a string and an optional boolean"); + PYTHON_RETURN_ERROR; + } + + std::string clothingNameStr = clothingName; // convert to string (for safety) + PYTHON_RETURN_BOOL(self->fThis->RemoveClothingItemU(clothingNameStr.c_str(), update != 0)); +} + +PYTHON_METHOD_DEFINITION(ptAvatar, tintClothingItem, args) +{ + char* clothingName = NULL; + PyObject* tintObj = NULL; + char update = 1; + if (!PyArg_ParseTuple(args, "sO|b", &clothingName, &tintObj, &update)) + { + PyErr_SetString(PyExc_TypeError, "tintClothingItem expects a string, a ptColor, and an optional boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(tintObj)) + { + PyErr_SetString(PyExc_TypeError, "tintClothingItem expects a string, a ptColor, and an optional boolean"); + PYTHON_RETURN_ERROR; + } + + std::string clothingNameStr = clothingName; // convert to string (for safety) + pyColor* tint = pyColor::ConvertFrom(tintObj); + PYTHON_RETURN_BOOL(self->fThis->TintClothingItemU(clothingNameStr.c_str(), *tint, update != 0)); +} + +PYTHON_METHOD_DEFINITION(ptAvatar, tintClothingItemLayer, args) +{ + char* clothingName = NULL; + PyObject* tintObj = NULL; + unsigned char layer; + char update = 1; + if (!PyArg_ParseTuple(args, "sOB|b", &clothingName, &tintObj, &layer, &update)) + { + PyErr_SetString(PyExc_TypeError, "tintClothingItemLayer expects a string, a ptColor, an unsigned 8-bit int, and an optional boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(tintObj)) + { + PyErr_SetString(PyExc_TypeError, "tintClothingItemLayer expects a string, a ptColor, an unsigned 8-bit int, and an optional boolean"); + PYTHON_RETURN_ERROR; + } + + std::string clothingNameStr = clothingName; // convert to string (for safety) + pyColor* tint = pyColor::ConvertFrom(tintObj); + PYTHON_RETURN_BOOL(self->fThis->TintClothingItemLayerU(clothingNameStr.c_str(), *tint, layer, update != 0)); +} + +PYTHON_METHOD_DEFINITION(ptAvatar, getTintClothingItem, args) +{ + char* clothingName = NULL; + unsigned char layer = 1; + if (!PyArg_ParseTuple(args, "s|B", &clothingName, &layer)) + { + PyErr_SetString(PyExc_TypeError, "getTintClothingItem expects a string and an optional unsigned 8-bit int"); + PYTHON_RETURN_NONE; + } + + std::string clothingNameStr = clothingName; // convert to string (for safety) + return self->fThis->GetTintClothingItemL(clothingNameStr.c_str(), layer); +} + +PYTHON_METHOD_DEFINITION(ptAvatar, tintSkin, args) +{ + PyObject* tintObj = NULL; + char update = 1; + if (!PyArg_ParseTuple(args, "O|b", &tintObj, &update)) + { + PyErr_SetString(PyExc_TypeError, "tintSkin expects a ptColor and an optional boolean"); + PYTHON_RETURN_NONE; + } + if (!pyColor::Check(tintObj)) + { + PyErr_SetString(PyExc_TypeError, "tintSkin expects a ptColor and an optional boolean"); + PYTHON_RETURN_NONE; + } + + pyColor* tint = pyColor::ConvertFrom(tintObj); + self->fThis->TintSkinU(*tint, update != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAvatar, getTintSkin) +{ + return self->fThis->GetTintSkin(); +} + +PYTHON_METHOD_DEFINITION(ptAvatar, enterSubWorld, args) +{ + PyObject* sceneObj = NULL; + if (!PyArg_ParseTuple(args, "O", &sceneObj)) + { + PyErr_SetString(PyExc_TypeError, "enterSubWorld expects a ptSceneObject"); + PYTHON_RETURN_ERROR; + } + if (!pySceneObject::Check(sceneObj)) + { + PyErr_SetString(PyExc_TypeError, "enterSubWorld expects a ptSceneObject"); + PYTHON_RETURN_ERROR; + } + + pySceneObject* target = pySceneObject::ConvertFrom(sceneObj); + self->fThis->EnterSubWorld(*target); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptAvatar, exitSubWorld, ExitSubWorld) + +PYTHON_METHOD_DEFINITION(ptAvatar, setMorph, args) +{ + char* clothingName = NULL; + unsigned char layer; + float value; + if (!PyArg_ParseTuple(args, "sBf", &clothingName, &layer, &value)) + { + PyErr_SetString(PyExc_TypeError, "setMorph expects a string, unsigned 8-bit int, and a float"); + PYTHON_RETURN_ERROR; + } + + std::string clothingNameStr = clothingName; // convert to string (for safety) + self->fThis->SetMorph(clothingNameStr.c_str(), layer, value); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, getMorph, args) +{ + char* clothingName = NULL; + unsigned char layer; + if (!PyArg_ParseTuple(args, "sB", &clothingName, &layer)) + { + PyErr_SetString(PyExc_TypeError, "getMorph expects a string, and an unsignd 8-bit int"); + PYTHON_RETURN_ERROR; + } + + std::string clothingNameStr = clothingName; // convert to string (for safety) + return PyFloat_FromDouble(self->fThis->GetMorph(clothingNameStr.c_str(), layer)); +} + +PYTHON_METHOD_DEFINITION(ptAvatar, setSkinBlend, args) +{ + unsigned char layer; + float value; + if (!PyArg_ParseTuple(args, "Bf", &layer, &value)) + { + PyErr_SetString(PyExc_TypeError, "setSkinBlend expects an unsigned 8-bit int and a float"); + PYTHON_RETURN_ERROR; + } + + self->fThis->SetSkinBlend(layer, value); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, getSkinBlend, args) +{ + unsigned char layer; + if (!PyArg_ParseTuple(args, "B", &layer)) + { + PyErr_SetString(PyExc_TypeError, "getSkinBlend expects an unsigned 8-bit int"); + PYTHON_RETURN_ERROR; + } + + return PyFloat_FromDouble(self->fThis->GetSkinBlend(layer)); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptAvatar, saveClothing, SaveClothing) + +PYTHON_METHOD_DEFINITION(ptAvatar, getUniqueMeshList, args) +{ + long clothingType; + if (!PyArg_ParseTuple(args, "l", &clothingType)) + { + PyErr_SetString(PyExc_TypeError, "getUniqueMeshList expects a long"); + PYTHON_RETURN_ERROR; + } + + std::vector clothingList = self->fThis->GetUniqueMeshList(clothingType); + PyObject* retVal = PyList_New(clothingList.size()); + for (int i = 0; i < clothingList.size(); i++) + PyList_SetItem(retVal, i, clothingList[i]); // steals the ref, so no need to decref + return retVal; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, getAllWithSameMesh, args) +{ + char* clothingName = NULL; + if (!PyArg_ParseTuple(args, "s", &clothingName)) + { + PyErr_SetString(PyExc_TypeError, "getAllWithSameMesh expects a string"); + PYTHON_RETURN_ERROR; + } + + std::string clothingNameStr = clothingName; // convert to string (for safety) + std::vector clothingList = self->fThis->GetAllWithSameMesh(clothingNameStr.c_str()); + PyObject* retVal = PyList_New(clothingList.size()); + for (int i = 0; i < clothingList.size(); i++) + PyList_SetItem(retVal, i, clothingList[i]); // steals the ref, so no need to decref + return retVal; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAvatar, getWardrobeClothingList) +{ + std::vector clothingList = self->fThis->GetWardrobeClothingList(); + PyObject* retVal = PyList_New(clothingList.size()); + for (int i = 0; i < clothingList.size(); i++) + PyList_SetItem(retVal, i, clothingList[i]); // steals the ref, so no need to decref + return retVal; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, addWardrobeClothingItem, args) +{ + char* clothingName = NULL; + PyObject* tint1Obj = NULL; + PyObject* tint2Obj = NULL; + if (!PyArg_ParseTuple(args, "sOO", &clothingName, &tint1Obj, &tint2Obj)) + { + PyErr_SetString(PyExc_TypeError, "addWardrobeClothingItem expects a string and two ptColor objects"); + PYTHON_RETURN_ERROR; + } + if ((!pyColor::Check(tint1Obj)) || (!pyColor::Check(tint2Obj))) + { + PyErr_SetString(PyExc_TypeError, "addWardrobeClothingItem expects a string and two ptColor objects"); + PYTHON_RETURN_ERROR; + } + + std::string clothingNameStr = clothingName; // convert to string (for safety) + pyColor* tint1 = pyColor::ConvertFrom(tint1Obj); + pyColor* tint2 = pyColor::ConvertFrom(tint2Obj); + self->fThis->AddWardrobeClothingItem(clothingNameStr.c_str(), *tint1, *tint2); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, setReplyKey, args) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "setReplyKey expects a ptKey object"); + PYTHON_RETURN_NONE; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "setReplyKey expects a ptKey object"); + PYTHON_RETURN_NONE; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->SetSenderKey(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAvatar, getCurrentMode) +{ + return PyInt_FromLong(self->fThis->GetCurrentMode()); +} + +PYTHON_METHOD_DEFINITION(ptAvatar, registerForBehaviorNotify, args) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "registerForBehaviorNotify expects a ptKey object"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "registerForBehaviorNotify expects a ptKey object"); + PYTHON_RETURN_ERROR; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->RegisterForBehaviorNotify(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, unRegisterForBehaviorNotify, args) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "unRegisterForBehaviorNotify expects a ptKey object"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "unRegisterForBehaviorNotify expects a ptKey object"); + PYTHON_RETURN_ERROR; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->UnRegisterForBehaviorNotify(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAvatar, playSimpleAnimation, args) +{ + char* animName = NULL; + if (!PyArg_ParseTuple(args, "s", &animName)) + { + PyErr_SetString(PyExc_TypeError, "playSimpleAnimation expects a string object"); + PYTHON_RETURN_ERROR; + } + + std::string animNameStr = animName; // convert to a string (for safety) + self->fThis->PlaySimpleAnimation(animNameStr.c_str()); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptAvatar) + PYTHON_METHOD(ptAvatar, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n" + "- This is to be used if your Python program is running on only one client\n" + "Such as a game master, only running on the client that owns a particular object"), + + PYTHON_METHOD(ptAvatar, oneShot, "Params: seekKey,duration,usePhysicsFlag,animationName,drivableFlag,reversibleFlag\nPlays a one-shot animation on the avatar"), + PYTHON_METHOD(ptAvatar, runBehavior, "Params: behaviorKey,netForceFlag\nRuns a behavior on the avatar. Can be a single or multi-stage behavior."), + PYTHON_METHOD(ptAvatar, runBehaviorSetNotify, "Params: behaviorKey,replyKey,netForceFlag\nSame as runBehavior, except send notifications to specified keyed object"), + PYTHON_METHOD(ptAvatar, nextStage, "Params: behaviorKey,transitionTime,setTimeFlag,newTime,SetDirectionFlag,isForward,netForce\nTells a multistage behavior to go to the next stage (Why does Matt like so many parameters?)"), + PYTHON_METHOD(ptAvatar, previousStage, "Params: behaviorKey,transitionTime,setTimeFlag,newTime,SetDirectionFlag,isForward,netForce\nTells a multistage behavior to go to the previous stage"), + PYTHON_METHOD(ptAvatar, gotoStage, "Params: behaviorKey,stage,transitionTime,setTimeFlag,newTime,SetDirectionFlag,isForward,netForce\nTells a multistage behavior to go to a particular stage"), + + PYTHON_METHOD_NOARGS(ptAvatar, getAvatarClothingGroup, "Returns what clothing group the avatar belongs to.\n" + "It is also a means to determine if avatar is male or female"), + PYTHON_METHOD(ptAvatar, getEntireClothingList, "Params: clothing_type\nGets the entire list of clothing available. 'clothing_type' not used\n" + "NOTE: should use getClosetClothingList"), + PYTHON_METHOD(ptAvatar, getClosetClothingList, "Params: clothing_type\nReturns a list of clothes for the avatar that are in specified clothing group."), + PYTHON_METHOD_NOARGS(ptAvatar, getAvatarClothingList, "Returns a list of clothes that the avatar is currently wearing."), + PYTHON_METHOD(ptAvatar, getMatchingClothingItem, "Params: clothingName\nFinds the matching clothing item that goes with 'clothingName'\n" + "Used to find matching left and right gloves and shoes."), + PYTHON_METHOD(ptAvatar, wearClothingItem, "Params: clothing_name,update=1\nTells the avatar to wear a particular item of clothing.\n" + "And optionally hold update until later (for applying tinting before wearing)."), + PYTHON_METHOD(ptAvatar, removeClothingItem, "Params: clothing_name,update=1\nTells the avatar to remove a particular item of clothing."), + PYTHON_METHOD(ptAvatar, tintClothingItem, "Params: clothing_name,tint,update=1\nTells the avatar to tint(color) a particular item of clothing that they are already wearing.\n" + "'tint' is a ptColor object"), + PYTHON_METHOD(ptAvatar, tintClothingItemLayer, "Params: clothing_name,tint,layer,update=1\nTells the avatar to tint(color) a particular layer of a particular item of clothing."), + PYTHON_METHOD(ptAvatar, getTintClothingItem, "Params: clothing_name,layer=1\nReturns a ptColor of a particular item of clothing that the avatar is wearing.\n" + "The color will be a ptColor object."), + PYTHON_METHOD(ptAvatar, tintSkin, "Params: tint,update=1\nTints all of the skin on the avatar, with the ptColor tint"), + PYTHON_METHOD_NOARGS(ptAvatar, getTintSkin, "Returns a ptColor of the current skin tint for the avatar"), + + PYTHON_METHOD(ptAvatar, enterSubWorld, "Params: sceneobject\nPlaces the avatar into the subworld of the ptSceneObject specified"), + PYTHON_BASIC_METHOD(ptAvatar, exitSubWorld, "Exits the avatar from the subWorld where it was"), + + PYTHON_METHOD(ptAvatar, setMorph, "Params: clothing_name,layer,value\nSet the morph value (clipped between -1 and 1)"), + PYTHON_METHOD(ptAvatar, getMorph, "Params: clothing_name,layer\nGet the current morph value"), + PYTHON_METHOD(ptAvatar, setSkinBlend, "Params: layer,value\nSet the skin blend (value between 0 and 1)"), + PYTHON_METHOD(ptAvatar, getSkinBlend, "Params: layer\nGet the current skin blend value"), + + PYTHON_BASIC_METHOD(ptAvatar, saveClothing, "Saves the current clothing options (including morphs) to the vault"), + + PYTHON_METHOD(ptAvatar, getUniqueMeshList, "Params: clothing_type\nReturns a list of unique clothing items of the desired type (different meshes)"), + PYTHON_METHOD(ptAvatar, getAllWithSameMesh, "Params: clothing_name\nReturns a lilst of all clothing items that use the same mesh as the specified one"), + PYTHON_METHOD_NOARGS(ptAvatar, getWardrobeClothingList, "Return a list of items that are in the avatars closet"), + PYTHON_METHOD(ptAvatar, addWardrobeClothingItem, "Params: clothing_name,tint1,tint2\nTo add a clothing item to the avatar's wardrobe (closet)"), + + PYTHON_METHOD(ptAvatar, setReplyKey, "Params: key\nSets the sender's key"), + + PYTHON_METHOD_NOARGS(ptAvatar, getCurrentMode, "Returns current brain mode for avatar"), + + PYTHON_METHOD(ptAvatar, registerForBehaviorNotify, "Params: selfKey\nThis will register for behavior notifies from the avatar"), + PYTHON_METHOD(ptAvatar, unRegisterForBehaviorNotify, "Params: selfKey\nThis will unregister behavior notifications"), + + PYTHON_METHOD(ptAvatar, playSimpleAnimation, "Params: animName\nPlay simple animation on avatar"), +PYTHON_END_METHODS_TABLE; + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetBehaviorLoopCount, args, "Params: behaviorKey,stage,loopCount,netForce\nThis will set the loop count for a particular stage in a multistage behavior") +{ + PyObject* keyObj = NULL; + long stage, loopCount; + char netForce; + if (!PyArg_ParseTuple(args, "Ollb", &keyObj, &stage, &loopCount, &netForce)) + { + PyErr_SetString(PyExc_TypeError, "PtSetBehaviorLoopCount expects a ptKey, two longs, and a boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSetBehaviorLoopCount expects a ptKey, two longs, and a boolean"); + PYTHON_RETURN_ERROR; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + cyAvatar::SetLoopCount(*key, stage, loopCount, netForce != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtChangeAvatar, args, "Params: gender\nChange the local avatar's gender (or clothing type)") +{ + char* gender = NULL; + if (!PyArg_ParseTuple(args, "s", &gender)) + { + PyErr_SetString(PyExc_TypeError, "PtChangeAvatar expects a string"); + PYTHON_RETURN_ERROR; + } + + std::string genderStr = gender; // convert to string (for safety) + cyAvatar::ChangeAvatar(genderStr.c_str()); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtChangePlayerName, args, "Params: name\nChange the local avatar's name") +{ + char* name = NULL; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "PtChangePlayerName expects a string"); + PYTHON_RETURN_ERROR; + } + + std::string nameStr = name; // convert to string (for safety) + cyAvatar::ChangePlayerName(nameStr.c_str()); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtEmoteAvatar, args, "Params: emote\nPlay an emote on the local avatar (netpropagated)") +{ + char* emote = NULL; + if (!PyArg_ParseTuple(args, "s", &emote)) + { + PyErr_SetString(PyExc_TypeError, "PtEmoteAvatar expects a string"); + PYTHON_RETURN_ERROR; + } + + std::string emoteStr = emote; // convert to string (for safety) + PYTHON_RETURN_BOOL(cyAvatar::Emote(emoteStr.c_str())); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtAvatarSitOnGround, "Tells the local avatar to sit on ground and enter sit idle loop (netpropagated)") +{ + PYTHON_RETURN_BOOL(cyAvatar::Sit()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtAvatarEnterLookingAtKI, "Tells the local avatar to enter looking at KI idle loop (netpropagated)") +{ + PYTHON_RETURN_BOOL(cyAvatar::EnterKiMode()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtAvatarExitLookingAtKI, "Tells the local avatar to exit looking at KI idle loop (netpropagated)") +{ + PYTHON_RETURN_BOOL(cyAvatar::ExitKiMode()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtAvatarEnterUsePersBook, "Tells the local avatar to enter using their personal book idle loop (netpropagated)") +{ + PYTHON_RETURN_BOOL(cyAvatar::EnterPBMode()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtAvatarExitUsePersBook, "Tells the local avatar to exit using their personal book idle loop (netpropagated)") +{ + PYTHON_RETURN_BOOL(cyAvatar::ExitPBMode()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtAvatarEnterAFK, "Tells the local avatar to enter AwayFromKeyboard idle loop (netpropagated)") +{ + PYTHON_RETURN_BOOL(cyAvatar::EnterAFKMode()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtAvatarExitAFK, "Tells the local avatar to exit AwayFromKeyboard idle loop (netpropagated)") +{ + PYTHON_RETURN_BOOL(cyAvatar::ExitAFKMode()); +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtDisableMovementKeys, cyAvatar::DisableMovementControls, "Disable avatar movement input") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtEnableMovementKeys, cyAvatar::EnableMovementControls, "Enable avatar movement input") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtDisableMouseMovement, cyAvatar::DisableMouseMovement, "Disable avatar mouse movement input") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtEnableMouseMovement, cyAvatar::EnableMouseMovement, "Enable avatar mouse movement input") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtDisableAvatarJump, cyAvatar::DisableAvatarJump, "Disable the ability of the avatar to jump") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtEnableAvatarJump, cyAvatar::EnableAvatarJump, "Enable the ability of the avatar to jump") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtDisableForwardMovement, cyAvatar::DisableForwardMovement, "Disable the ability of the avatar to move forward") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtEnableForwardMovement, cyAvatar::EnableForwardMovement, "Enable the ability of the avatar to move forward") + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtLocalAvatarRunKeyDown, "Returns true if the run key is being held down for the local avatar") +{ + PYTHON_RETURN_BOOL(cyAvatar::LocalAvatarRunKeyDown()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtLocalAvatarIsMoving, "Returns true if the local avatar is moving (a movement key is held down)") +{ + PYTHON_RETURN_BOOL(cyAvatar::LocalAvatarIsMoving()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetMouseTurnSensitivity, args, "Params: sensitivity\nSet the mouse sensitivity") +{ + float sensitivity; + if (!PyArg_ParseTuple(args, "f", &sensitivity)) + { + PyErr_SetString(PyExc_TypeError, "PtSetMouseTurnSensitivity expects a floating point value"); + PYTHON_RETURN_ERROR; + } + + cyAvatar::SetMouseTurnSensitivity(sensitivity); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetMouseTurnSensitivity, "Returns the sensitivity") +{ + return PyFloat_FromDouble(cyAvatar::GetMouseTurnSensitivity()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsCurrentBrainHuman, "Returns whether the local avatar current brain is the human brain") +{ + PYTHON_RETURN_BOOL(cyAvatar::IsCurrentBrainHuman()); +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtAvatarSpawnNext, cyAvatar::SpawnNext, "Send the avatar to the next spawn point") + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptAvatar, "Plasma avatar class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptAvatar, cyAvatar) + +static PyObject* New(PyObject* sender, PyObject* recvr = nil) +{ + ptAvatar* newObj = (ptAvatar*)ptAvatar_type.tp_new(&ptAvatar_type, NULL, NULL); + pyKey* senderKey = pyKey::ConvertFrom(sender); + pyKey* recvrKey = pyKey::ConvertFrom(recvr); + newObj->fThis->SetSender(senderKey->getKey()); + newObj->fThis->AddRecvr(recvrKey->getKey()); + newObj->fThis->SetNetForce(false); + return (PyObject*) newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptAvatar, cyAvatar) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptAvatar, cyAvatar) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void cyAvatar::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptAvatar); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaMethods - the python method definitions +// +void cyAvatar::AddPlasmaMethods(std::vector &methods) +{ + // static/global functions (to the local avatar) + PYTHON_GLOBAL_METHOD(methods, PtSetBehaviorLoopCount); + PYTHON_GLOBAL_METHOD(methods, PtChangeAvatar); + PYTHON_GLOBAL_METHOD(methods, PtChangePlayerName); + PYTHON_GLOBAL_METHOD(methods, PtEmoteAvatar); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtAvatarSitOnGround); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtAvatarEnterLookingAtKI); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtAvatarExitLookingAtKI); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtAvatarEnterUsePersBook); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtAvatarExitUsePersBook); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtAvatarEnterAFK); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtAvatarExitAFK); + + // Suspend avatar input + PYTHON_BASIC_GLOBAL_METHOD(methods, PtDisableMovementKeys); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtEnableMovementKeys); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtDisableMouseMovement); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtEnableMouseMovement); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtDisableAvatarJump); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtEnableAvatarJump); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtDisableForwardMovement); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtEnableForwardMovement); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtLocalAvatarRunKeyDown); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtLocalAvatarIsMoving); + PYTHON_GLOBAL_METHOD(methods, PtSetMouseTurnSensitivity); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetMouseTurnSensitivity); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsCurrentBrainHuman); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtAvatarSpawnNext); +} + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaConstantsClasses - the python constants definitions +// +void cyAvatar::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtBrainModes); + PYTHON_ENUM_ELEMENT(PtBrainModes, kGeneric, plAvBrainGeneric::kGeneric); + PYTHON_ENUM_ELEMENT(PtBrainModes, kLadder, plAvBrainGeneric::kLadder); + PYTHON_ENUM_ELEMENT(PtBrainModes, kSit, plAvBrainGeneric::kSit); + PYTHON_ENUM_ELEMENT(PtBrainModes, kSitOnGround, plAvBrainGeneric::kSitOnGround); + PYTHON_ENUM_ELEMENT(PtBrainModes, kEmote, plAvBrainGeneric::kEmote); + PYTHON_ENUM_ELEMENT(PtBrainModes, kAFK, plAvBrainGeneric::kAFK); + PYTHON_ENUM_ELEMENT(PtBrainModes, kNonGeneric, plAvBrainGeneric::kNonGeneric); + PYTHON_ENUM_END(m, PtBrainModes); + + PYTHON_ENUM_START(PtBehaviorTypes); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeStandingJump, plHBehavior::kBehaviorTypeStandingJump); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeWalkingJump, plHBehavior::kBehaviorTypeWalkingJump); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeRunningJump, plHBehavior::kBehaviorTypeRunningJump); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeAnyJump, plHBehavior::kBehaviorTypeAnyJump); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeRunningImpact, plHBehavior::kBehaviorTypeRunningImpact); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeGroundImpact, plHBehavior::kBehaviorTypeGroundImpact); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeAnyImpact, plHBehavior::kBehaviorTypeAnyImpact); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeIdle, plHBehavior::kBehaviorTypeIdle); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeWalk, plHBehavior::kBehaviorTypeWalk); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeRun, plHBehavior::kBehaviorTypeRun); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeWalkBack, plHBehavior::kBehaviorTypeWalkBack); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeTurnLeft, plHBehavior::kBehaviorTypeTurnLeft); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeTurnRight, plHBehavior::kBehaviorTypeTurnRight); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeSidestepLeft, plHBehavior::kBehaviorTypeSidestepLeft); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeSidestepRight, plHBehavior::kBehaviorTypeSidestepRight); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeFall, plHBehavior::kBehaviorTypeFall); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeMovingTurnLeft, plHBehavior::kBehaviorTypeMovingTurnLeft); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeMovingTurnRight, plHBehavior::kBehaviorTypeMovingTurnRight); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeLinkIn, plHBehavior::kBehaviorTypeLinkIn); + PYTHON_ENUM_ELEMENT(PtBehaviorTypes, kBehaviorTypeLinkOut, plHBehavior::kBehaviorTypeLinkOut); + PYTHON_ENUM_END(m, PtBehaviorTypes); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCamera.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCamera.cpp new file mode 100644 index 00000000..54cc6eaf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCamera.cpp @@ -0,0 +1,370 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyCamera.h" + +#include "../pnMessage/plCameraMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnKeyedObject/plUoid.h" +#include "hsResMgr.h" +#include "pyKey.h" +#include "plgDispatch.h" + +#include "../pfCamera/plVirtualCamNeu.h" +#include "../pfCamera/plCameraModifier.h" +#include "../pfCamera/plCameraBrain.h" + +cyCamera::cyCamera() +{ + // get _the_ virtual camera + plUoid pU( kVirtualCamera1_KEY ); + hsResMgr* hrm = hsgResMgr::ResMgr(); + if ( hrm) + fTheCam = hrm->FindKey( pU ); + else + fTheCam = nil; +} + +// setters +void cyCamera::SetSender(plKey &sender) +{ + fSender = sender; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Push +// PARAMETERS : +// +// PURPOSE : Save the current state of the virtual camera +// +// NOTE: doesn't work by itself at the moment +// +void cyCamera::Push(pyKey& newCamKey) +{ + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + if ( fSender ) + pMsg->SetSender(fSender); + + // if we're sending to the virtual camera + if ( fTheCam ) + pMsg->AddReceiver(fTheCam); + else + // otherwise, broadcast by type + pMsg->SetBCastFlag(plMessage::kBCastByType); + + // set command to do the transition + pMsg->SetCmd(plCameraMsg::kResponderTrigger); + pMsg->SetCmd(plCameraMsg::kRegionPushCamera); + // set the new camera + pMsg->SetNewCam(newCamKey.getKey()); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Pop +// PARAMETERS : +// +// PURPOSE : Restore the state of the virtual camera with a previously saved setting +// +void cyCamera::Pop(pyKey& oldCamKey) +{ + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + if ( fSender ) + pMsg->SetSender(fSender); + + // if we're sending to the virtual camera + if ( fTheCam ) + pMsg->AddReceiver(fTheCam); + else + // otherwise, broadcast by type + pMsg->SetBCastFlag(plMessage::kBCastByType); + + // set command to undo the camera... somehow not saying ResponderTrigger but Push means Pop...whatever + pMsg->SetCmd(plCameraMsg::kRegionPushCamera); + // set the new camera + pMsg->SetNewCam(oldCamKey.getKey()); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ControlKey +// PARAMETERS : controlKey - what command key to simulate being hit +// : activate - whether its being pressed or released (activated or deactivated) +// +// PURPOSE : Send controlKey commands to the virtual camera (should be like a pass thru) +// +void cyCamera::ControlKey(Int32 controlKey, hsBool activated) +{ + // make sure that we have a virtual camera to send this to + if ( fTheCam ) + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + // set sender if there is one + if ( fSender ) + pMsg->SetSender(fSender); + + // if we're sending to the virtual camera + pMsg->AddReceiver(fTheCam); + + // set the control key and activateFlag + pMsg->SetControlCode((ControlEventCode)controlKey); + pMsg->SetControlActivated(activated); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : TransitionTo +// PARAMETERS : newCamKey - what to switch the camera to +// : time - how long it takes to transition to new camera +// +// PURPOSE : Transition to a new camera (position and settings) +// +void cyCamera::TransitionTo(pyKey& newCamKey, double time, hsBool save) +{ + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + if ( fSender ) + pMsg->SetSender(fSender); + // must have a receiver! + if ( fTheCam ) + { + pMsg->AddReceiver(fTheCam); + // set command to do the transition + pMsg->SetCmd(plCameraMsg::kTransitionTo); + // set the new camera + pMsg->SetNewCam(newCamKey.getKey()); + // set the transition time + pMsg->SetTransTime(time); + // test to see if they want to save + if ( save ) + pMsg->SetCmd(plCameraMsg::kPush); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void cyCamera::SetEnableFirstPersonOverride(hsBool state) +{ + // must have a receiver! + if ( fTheCam ) + { + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + if ( fSender ) + pMsg->SetSender(fSender); + + pMsg->AddReceiver(fTheCam); + // set command to do the transition + pMsg->SetCmd(plCameraMsg::kPythonSetFirstPersonOverrideEnable); + // set the state + pMsg->SetActivated(state); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + + +void cyCamera::UndoFirstPerson() +{ + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + if ( fSender ) + pMsg->SetSender(fSender); + // must have a receiver! + if ( fTheCam ) + { + pMsg->AddReceiver(fTheCam); + // set command to do the transition + pMsg->SetCmd(plCameraMsg::kPythonUndoFirstPerson); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + + +hsScalar cyCamera::GetFOV() +{ + if ( fTheCam ) + { + plVirtualCam1* virtCam = plVirtualCam1::ConvertNoRef( fTheCam->ObjectIsLoaded() ); + if ( virtCam ) + { + plCameraModifier1* curCam = virtCam->GetCurrentCamera(); + if ( curCam ) + { + return curCam->GetFOVh(); + } + } + } + return 0.0; +} + +void cyCamera::SetFOV(hsScalar fov, double t) +{ + if ( fTheCam ) + { + plVirtualCam1* virtCam = plVirtualCam1::ConvertNoRef( fTheCam->ObjectIsLoaded() ); + if ( virtCam ) + { + plCameraModifier1* curCam = virtCam->GetCurrentCamera(); + if ( curCam ) + { + plCameraBrain1* camBrain = curCam->GetBrain(); + if (camBrain) + { + camBrain->SetFOVGoal(fov,t); + } + } + } + } +} + + +void cyCamera::SetSmootherCam(hsBool state) +{ + if ( fTheCam ) + { + plVirtualCam1* virtCam = plVirtualCam1::ConvertNoRef( fTheCam->ObjectIsLoaded() ); + if ( virtCam ) + { + if (state) + { + virtCam->fUseAccelOverride = false; + } + else + { + virtCam->fAccel = 50.0; + virtCam->fDecel = 50.0; + virtCam->fVel = 100.0; + virtCam->fUseAccelOverride = true; + } + + } + } +} + +hsBool cyCamera::IsSmootherCam() +{ + if ( fTheCam ) + { + plVirtualCam1* virtCam = plVirtualCam1::ConvertNoRef( fTheCam->ObjectIsLoaded() ); + if ( virtCam ) + { + if ( virtCam->fUseAccelOverride ) + return false; + else + return true; + } + + } + return false; +} + +void cyCamera::SetWalkAndVerticalPan(hsBool state) +{ + if ( fTheCam ) + { + plVirtualCam1* virtCam = plVirtualCam1::ConvertNoRef( fTheCam->ObjectIsLoaded() ); + if ( virtCam ) + { + if (state) + virtCam->WalkPan3rdPerson = true; + else + virtCam->WalkPan3rdPerson = false; + + } + } +} + + +hsBool cyCamera::IsWalkAndVerticalPan() +{ + if ( fTheCam ) + { + plVirtualCam1* virtCam = plVirtualCam1::ConvertNoRef( fTheCam->ObjectIsLoaded() ); + if ( virtCam ) + { + return virtCam->WalkPan3rdPerson; + } + } + return false; +} + + +void cyCamera::SetStayInFirstPerson(hsBool state) +{ + if ( fTheCam ) + { + plVirtualCam1* virtCam = plVirtualCam1::ConvertNoRef( fTheCam->ObjectIsLoaded() ); + if ( virtCam ) + { + if (state) + virtCam->StayInFirstPersonForever = true; + else + virtCam->StayInFirstPersonForever = false; + } + } +} + +hsBool cyCamera::IsStayInFirstPerson() +{ + if ( fTheCam ) + { + plVirtualCam1* virtCam = plVirtualCam1::ConvertNoRef( fTheCam->ObjectIsLoaded() ); + if ( virtCam ) + { + return virtCam->StayInFirstPersonForever; + } + } + return false; +} + +void cyCamera::SetAspectRatio(float aspectratio) +{ + plVirtualCam1::SetAspectRatio(aspectratio); +} + +float cyCamera::GetAspectRatio() +{ + return plVirtualCam1::GetAspectRatio(); +} + +void cyCamera::RefreshFOV() +{ + plVirtualCam1::SetFOV(plVirtualCam1::GetFOVw(), plVirtualCam1::GetFOVh()); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCamera.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCamera.h new file mode 100644 index 00000000..7aef4a56 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCamera.h @@ -0,0 +1,107 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef cyCamera_h +#define cyCamera_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: cyCamera +// +// PURPOSE: Class wrapper to map camera functions to plasma2 message +// +#include "hsTypes.h" + +#include "../pnKeyedObject/plKey.h" +class pyKey; + +#include +#include "pyGlueHelpers.h" + +class cyCamera +{ +protected: + plKey fSender; + plKey fTheCam; + + cyCamera(); +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptCamera); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a cyCamera object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(cyCamera); // converts a PyObject to a cyCamera (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // setters + void SetSender(plKey &sender); + + // Save the current state of the virtual camera + // NOTE: doesn't work by itself at the moment + virtual void Push(pyKey& newCamKey); + + // Restore the state of the virtual camera with a previously saved setting + virtual void Pop(pyKey& oldCamKey); + + // Send controlKey commands to the virtual camera (should be like a pass thru) + virtual void ControlKey(Int32 controlKey, hsBool activated); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : TransitionTo + // PARAMETERS : newCamKey - what to switch the camera to + // : time - how long it takes to transition to new camera + // + // PURPOSE : Transition to a new camera (position and settings) + // + virtual void TransitionTo(pyKey& newCamKey, double time, hsBool save); + + virtual void SetEnableFirstPersonOverride(hsBool state); + virtual void EnableFirstPersonOverride() { SetEnableFirstPersonOverride(true); } + virtual void DisableFirstPersonOverride() { SetEnableFirstPersonOverride(false); } + + virtual void UndoFirstPerson(); + + virtual hsScalar GetFOV(); + virtual void SetFOV(hsScalar fov, double t); + + virtual void SetSmootherCam(hsBool state); + virtual hsBool IsSmootherCam(); + + virtual void SetWalkAndVerticalPan(hsBool state); + virtual hsBool IsWalkAndVerticalPan(); + + virtual void SetStayInFirstPerson(hsBool state); + virtual hsBool IsStayInFirstPerson(); + + virtual void SetAspectRatio(float aspectratio); + virtual float GetAspectRatio(); + virtual void RefreshFOV(); +}; + + +#endif // cyCamera_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCameraGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCameraGlue.cpp new file mode 100644 index 00000000..186f5169 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyCameraGlue.cpp @@ -0,0 +1,261 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyCamera.h" +#include "pyKey.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptCamera, cyCamera); + +PYTHON_DEFAULT_NEW_DEFINITION(ptCamera, cyCamera) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptCamera) + +PYTHON_INIT_DEFINITION(ptCamera, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptCamera, save, args) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "save expects a ptKey object"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "save expects a ptKey object"); + PYTHON_RETURN_ERROR; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->Push(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptCamera, restore, args) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "restore expects a ptKey object"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "restore expects a ptKey object"); + PYTHON_RETURN_ERROR; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->Pop(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptCamera, controlKey, args) +{ + long controlKey; + char activateFlag; + if (!PyArg_ParseTuple(args, "lb", &controlKey, &activateFlag)) + { + PyErr_SetString(PyExc_TypeError, "controlKey expects a long and a boolean"); + PYTHON_RETURN_ERROR; + } + + self->fThis->ControlKey(controlKey, activateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptCamera, set, args) +{ + PyObject* keyObj = NULL; + double time; + char save; + if (!PyArg_ParseTuple(args, "Odb", &keyObj, &time, &save)) + { + PyErr_SetString(PyExc_TypeError, "set expects a ptKey, double, and a boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "set expects a ptKey, double, and a boolean"); + PYTHON_RETURN_ERROR; + } + + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->TransitionTo(*key, time, save != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptCamera, enableFirstPersonOverride, EnableFirstPersonOverride) +PYTHON_BASIC_METHOD_DEFINITION(ptCamera, disableFirstPersonOverride, DisableFirstPersonOverride) +PYTHON_BASIC_METHOD_DEFINITION(ptCamera, undoFirstPerson, UndoFirstPerson) + +PYTHON_METHOD_DEFINITION_NOARGS(ptCamera, getFOV) +{ + return PyFloat_FromDouble(self->fThis->GetFOV()); +} + +PYTHON_METHOD_DEFINITION(ptCamera, setFOV, args) +{ + float fov; + double time; + if (!PyArg_ParseTuple(args, "fd", &fov, &time)) + { + PyErr_SetString(PyExc_TypeError, "setFOV expects a float and a double"); + PYTHON_RETURN_ERROR; + } + + self->fThis->SetFOV(fov, time); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptCamera, setSmootherCam, args) +{ + char state; + if (!PyArg_ParseTuple(args, "b", &state)) + { + PyErr_SetString(PyExc_TypeError, "setSmootherCam expects a boolean"); + PYTHON_RETURN_ERROR; + } + + self->fThis->SetSmootherCam(state != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCamera, isSmootherCam) +{ + PYTHON_RETURN_BOOL(self->fThis->IsSmootherCam()); +} + +PYTHON_METHOD_DEFINITION(ptCamera, setWalkAndVerticalPan, args) +{ + char state; + if (!PyArg_ParseTuple(args, "b", &state)) + { + PyErr_SetString(PyExc_TypeError, "setWalkAndVerticalPan expects a boolean"); + PYTHON_RETURN_ERROR; + } + + self->fThis->SetWalkAndVerticalPan(state != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCamera, isWalkAndVerticalPan) +{ + PYTHON_RETURN_BOOL(self->fThis->IsWalkAndVerticalPan()); +} + +PYTHON_METHOD_DEFINITION(ptCamera, setStayInFirstPerson, args) +{ + char state; + if (!PyArg_ParseTuple(args, "b", &state)) + { + PyErr_SetString(PyExc_TypeError, "setStayInFirstPerson expects a boolean"); + PYTHON_RETURN_ERROR; + } + + self->fThis->SetStayInFirstPerson(state != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCamera, isStayInFirstPerson) +{ + PYTHON_RETURN_BOOL(self->fThis->IsStayInFirstPerson()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCamera, getAspectRatio) +{ + return PyFloat_FromDouble(self->fThis->GetAspectRatio()); +} + +PYTHON_METHOD_DEFINITION(ptCamera, setAspectRatio, args) +{ + float aspect; + if (!PyArg_ParseTuple(args, "f", &aspect)) + { + PyErr_SetString(PyExc_TypeError, "setAspectRatio expects a float"); + PYTHON_RETURN_ERROR; + } + + self->fThis->SetAspectRatio(aspect); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCamera, refreshFOV) +{ + self->fThis->RefreshFOV(); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptCamera) + PYTHON_METHOD(ptCamera, save, "Params: cameraKey\nSaves the current camera and sets the camera to cameraKey"), + PYTHON_METHOD(ptCamera, restore, "Params: cameraKey\nRestores camera to saved one"), + + PYTHON_METHOD(ptCamera, controlKey, "Params: controlKey,activateFlag\nSend a control key to the camera as if it was hit by the user.\n" + "This is for sending things like pan-up, pan-down, zoom-in, etc."), + + PYTHON_METHOD(ptCamera, set, "Params: cameraKey,time,save\nDO NOT USE"), + + PYTHON_BASIC_METHOD(ptCamera, enableFirstPersonOverride, "Allows the user to override the camera and go to a first person camera."), + PYTHON_BASIC_METHOD(ptCamera, disableFirstPersonOverride, "Does _not_ allow the user to override the camera to go to first person camera."), + PYTHON_BASIC_METHOD(ptCamera, undoFirstPerson, "If the user has overridden the camera to be in first person, this will take them out of first person.\n" + "If the user didn't override the camera, then this will do nothing."), + + PYTHON_METHOD_NOARGS(ptCamera, getFOV, "Returns the current camera's FOV(h)"), + PYTHON_METHOD(ptCamera, setFOV, "Params: fov, time\nSets the current cameras FOV (based on h)"), + + PYTHON_METHOD(ptCamera, setSmootherCam, "Params: state\nSet the faster cams thing"), + PYTHON_METHOD_NOARGS(ptCamera, isSmootherCam, "Returns true if we are using the faster cams thing"), + PYTHON_METHOD(ptCamera, setWalkAndVerticalPan, "Params: state\nSet Walk and chew gum"), + PYTHON_METHOD_NOARGS(ptCamera, isWalkAndVerticalPan, "Returns true if we are walking and chewing gum"), + PYTHON_METHOD(ptCamera, setStayInFirstPerson, "Params: state\nSet Stay In First Person Always"), + PYTHON_METHOD_NOARGS(ptCamera, isStayInFirstPerson, "Are we staying in first person?"), + PYTHON_METHOD_NOARGS(ptCamera, getAspectRatio, "Get the global aspect ratio"), + PYTHON_METHOD(ptCamera, setAspectRatio, "Params: aspect\nSet the global aspect ratio"), + PYTHON_METHOD_NOARGS(ptCamera, refreshFOV, "Refreshes the FOV"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptCamera, "Plasma camera class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptCamera, cyCamera) +PYTHON_CLASS_CHECK_IMPL(ptCamera, cyCamera) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptCamera, cyCamera) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void cyCamera::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptCamera); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDraw.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDraw.cpp new file mode 100644 index 00000000..30d0b20b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDraw.cpp @@ -0,0 +1,105 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyDraw.h" + +#include "plgDispatch.h" +#include "hsBitVector.h" +#include "../pnMessage/plEnableMsg.h" + +cyDraw::cyDraw(plKey sender, plKey recvr) +{ + SetSender(sender); + AddRecvr(recvr); + fNetForce = false; +} + +// setters +void cyDraw::SetSender(plKey &sender) +{ + fSender = sender; +} + +void cyDraw::AddRecvr(plKey &recvr) +{ + if ( recvr != nil ) + fRecvr.Append(recvr); +} + + +void cyDraw::SetNetForce(hsBool state) +{ + // set our flag + fNetForce = state; +} + + +void cyDraw::EnableT(hsBool state) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plEnableMsg* pMsg = TRACKED_NEW plEnableMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // set the interface to the draw + pMsg->SetCmd(plEnableMsg::kDrawable); + pMsg->AddType(plEnableMsg::kDrawable); + + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + + // which way are we doin' it? + if ( state ) + pMsg->SetCmd(plEnableMsg::kEnable); + else + pMsg->SetCmd(plEnableMsg::kDisable); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void cyDraw::Enable() +{ + EnableT(true); +} + +void cyDraw::Disable() +{ + EnableT(false); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDraw.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDraw.h new file mode 100644 index 00000000..204898ad --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDraw.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef cyDraw_h +#define cyDraw_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: cyDraw +// +// PURPOSE: Class wrapper to map draw functions to plasma2 message +// + +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" + +#include +#include "pyGlueHelpers.h" + +class cyDraw +{ +protected: + plKey fSender; + hsTArray fRecvr; + hsBool fNetForce; + + cyDraw(plKey sender=nil,const plKey recvr=nil); +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptDraw); + static PyObject *New(PyObject *sender = NULL, PyObject* recvr = NULL); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a cyDraw object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(cyDraw); // converts a PyObject to a cyDraw (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // setters + void SetSender(plKey &sender); + void AddRecvr(plKey &recvr); + virtual void SetNetForce(hsBool state); + + // Enable draw + virtual void EnableT(hsBool state); + virtual void Enable(); + virtual void Disable(); +}; + +#endif // cyDraw_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDrawGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDrawGlue.cpp new file mode 100644 index 00000000..da722698 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyDrawGlue.cpp @@ -0,0 +1,109 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyDraw.h" +#include "pyKey.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptDraw, cyDraw); + +PYTHON_DEFAULT_NEW_DEFINITION(ptDraw, cyDraw) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptDraw) + +PYTHON_NO_INIT_DEFINITION(ptDraw) + +PYTHON_METHOD_DEFINITION(ptDraw, netForce, args) +{ + char forceFlag; + if (!PyArg_ParseTuple(args, "b", &forceFlag)) + { + PyErr_SetString(PyExc_TypeError, "netForce requires a boolean argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNetForce(forceFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDraw, enable, args) +{ + char state = 1; + if (!PyArg_ParseTuple(args, "|b", &state)) + { + PyErr_SetString(PyExc_TypeError, "enable expects an optional boolean argument"); + PYTHON_RETURN_ERROR; + } + + self->fThis->EnableT(state != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptDraw, disable, Disable) + +PYTHON_START_METHODS_TABLE(ptDraw) + PYTHON_METHOD(ptDraw, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n" + "- This is to be used if your Python program is running on only one client\n" + "Such as a game master, only running on the client that owns a particular object"), + PYTHON_METHOD(ptDraw, enable, "Params: state=1\nSets the draw enable for the sceneobject attached"), + PYTHON_BASIC_METHOD(ptDraw, disable, "Disables the draw on the sceneobject attached\n" + "In other words, makes it invisible"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptDraw, "Plasma Draw class"); + +// required functions for PyObject interoperability +PyObject *cyDraw::New(PyObject *sender, PyObject *recvr) +{ + ptDraw *newObj = (ptDraw*)ptDraw_type.tp_new(&ptDraw_type, NULL, NULL); + if (sender != NULL) + { + pyKey *senderKey = pyKey::ConvertFrom(sender); + newObj->fThis->SetSender(senderKey->getKey()); + } + if (recvr != NULL) + { + pyKey *recvrKey = pyKey::ConvertFrom(recvr); + newObj->fThis->AddRecvr(recvrKey->getKey()); + } + newObj->fThis->fNetForce = false; + + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptDraw, cyDraw) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptDraw, cyDraw) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void cyDraw::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptDraw); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterface.cpp new file mode 100644 index 00000000..d052f7df --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterface.cpp @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyInputInterface.h" + +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../plInputCore/plTelescopeInputInterface.h" +#include "plgDispatch.h" + +cyInputInterface::cyInputInterface() : +fTelescopeInterface(nil) +{ +} + +cyInputInterface::~cyInputInterface() +{ + if (fTelescopeInterface) + hsRefCnt_SafeUnRef( fTelescopeInterface ); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Push interface functions +// PARAMETERS : +// +// PURPOSE : create and push a specialized input interface onto the control stack. +// : for now we only have the telescope... +// +// + +void cyInputInterface::PushTelescopeInterface() +{ + if (!fTelescopeInterface) + { + fTelescopeInterface = TRACKED_NEW plTelescopeInputInterface; + plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kAddInterface); + pMsg->SetIFace(fTelescopeInterface); + pMsg->Send(); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Pop +// PARAMETERS : +// +// PURPOSE : Remove and delete the interface we have on the stack +// +void cyInputInterface::PopTelescope() +{ + if (fTelescopeInterface) + { + plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kRemoveInterface); + pMsg->SetIFace(fTelescopeInterface); + pMsg->Send(); + hsRefCnt_SafeUnRef( fTelescopeInterface ); + fTelescopeInterface = nil; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterface.h new file mode 100644 index 00000000..b25c8b96 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterface.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef cyInputInterface_h +#define cyInputInterface_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: cyInputInterface +// +// PURPOSE: Class wrapper to map InputInterface functions to plasma2 message +// + +#include +#include "pyGlueHelpers.h" + +class plInputInterface; + +class cyInputInterface +{ +protected: + plInputInterface* fTelescopeInterface; + + cyInputInterface(); +public: + ~cyInputInterface(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptInputInterface); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a cyInputInterface object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(cyInputInterface); // converts a PyObject to a cyInputInterface (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // setters + + // we should add a ::Push_X_Interface function for any special type + // of interface we might want to set... for now there's just the telescope... + void PushTelescopeInterface(); + void PopTelescope(); + + + ///////////////////////////////////////////////////////////////////////////// +}; + + +#endif // cyInputInterface_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterfaceGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterfaceGlue.cpp new file mode 100644 index 00000000..b4f82b4a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyInputInterfaceGlue.cpp @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "cyInputInterface.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptInputInterface, cyInputInterface); + +PYTHON_DEFAULT_NEW_DEFINITION(ptInputInterface, cyInputInterface) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptInputInterface) + +PYTHON_INIT_DEFINITION(ptInputInterface, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptInputInterface, pushTelescope, PushTelescopeInterface) +PYTHON_BASIC_METHOD_DEFINITION(ptInputInterface, popTelescope, PopTelescope) + +PYTHON_START_METHODS_TABLE(ptInputInterface) + PYTHON_BASIC_METHOD(ptInputInterface, pushTelescope, "pushes on the telescope interface"), + PYTHON_BASIC_METHOD(ptInputInterface, popTelescope, "pops off the telescope interface and gos back to previous interface"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptInputInterface, "Plasma input interface class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptInputInterface, cyInputInterface) +PYTHON_CLASS_CHECK_IMPL(ptInputInterface, cyInputInterface) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptInputInterface, cyInputInterface) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void cyInputInterface::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptInputInterface); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp new file mode 100644 index 00000000..3a3ad971 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMisc.cpp @@ -0,0 +1,2839 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyMisc.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "../plResMgr/plKeyFinder.h" + +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../plMessage/plConsoleMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plExcludeRegionMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../pnMessage/plCmdIfaceModMsg.h" +#include "../pnMessage/plAttachMsg.h" +#include "../plMessage/plTimerCallbackMsg.h" +#include "../plMessage/plNetVoiceListMsg.h" +#include "../pnMessage/plClientMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pnTimer/plTimerCallbackManager.h" +#include "../plVault/plVault.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetClient/plNetLinkingMgr.h" +#include "../plNetTransport/plNetTransport.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plMultistageBehMod.h" +#include "../plAvatar/plAvBrainCritter.h" +#include "pyCritterBrain.h" +#include "cyPythonInterface.h" +#include "pyKey.h" +#include "pySceneObject.h" +#include "pyPlayer.h" +#include "pyImage.h" +#include "pyDniCoordinates.h" +#include "pyDniInfoSource.h" +#include "pyColor.h" +#include "pyNetLinkingMgr.h" +#include "pyAgeInfoStruct.h" +#include "pyAgeLinkStruct.h" +#include "pyAlarm.h" +#include "../pfMessage/pfKIMsg.h" +#include "../plNetMessage/plNetMessage.h" +#include "../pfCamera/plVirtualCamNeu.h" +#include "../plPipeline/plDynamicEnvMap.h" + +#include "../pfGameGUIMgr/pfGameGUIMgr.h" +#include "../pfGameGUIMgr/pfGUIDialogMod.h" +#include "pyGUIDialog.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +#include "../plMessage/plCCRMsg.h" +#include "../plAgeLoader/plAgeLoader.h" + +#include "../plResMgr/plLocalization.h" +#include "../plGLight/plLightInfo.h" + +#include "../plInputCore/plAvatarInputInterface.h" +#include "../plInputCore/plInputDevice.h" + +#include "../plVault/plAgeInfoSource.h" + +#include "../pfLocalizationMgr/pfLocalizationMgr.h" + +//// Static Class Stuff ////////////////////////////////////////////////////// +plPipeline* cyMisc::fPipeline = nil; +UInt32 cyMisc::fUniqueNumber = 0; + +#ifdef PLASMA_EXTERNAL_RELEASE +UInt32 cyMisc::fPythonLoggingLevel = cyMisc::kErrorLevel; +#else +UInt32 cyMisc::fPythonLoggingLevel = cyMisc::kWarningLevel; +#endif + +///////////////////////////////////////////////////////////////////////////// +// static +void cyMisc::Update( double secs ) +{ + // only update once per 1/2 sec + static double lastUpdateTime = 0.0; + if ( secs-lastUpdateTime>=0.5 ) + { + lastUpdateTime = secs; + pyAlarmMgr::GetInstance()->Update( secs ); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Get/SetDebugPrintLevel +// +// PURPOSE : gets and sets the python debug print level +// +UInt32 cyMisc::GetPythonLoggingLevel() +{ + return fPythonLoggingLevel; +} +void cyMisc::SetPythonLoggingLevel(UInt32 new_level) +{ + fPythonLoggingLevel = new_level; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Console +// PARAMETERS : command - string of console commmand to execute +// +// PURPOSE : Execute a console command from a python script +// +void cyMisc::Console(const char* command) +{ + if ( command != nil ) + { + // create message to send to the console + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetBCastFlag(plMessage::kBCastByType); + pMsg->SetControlCode(B_CONTROL_CONSOLE_COMMAND); + pMsg->SetControlActivated(true); + pMsg->SetCmdString(command); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void cyMisc::ConsoleNet(const char* command, hsBool netForce) +{ + if ( command != nil ) + { + // create message to send to the console + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetBCastFlag(plMessage::kBCastByType); + pMsg->SetBCastFlag(plMessage::kNetPropagate); + if ( netForce ) + pMsg->SetBCastFlag(plMessage::kNetForce); + pMsg->SetControlCode(B_CONTROL_CONSOLE_COMMAND); + pMsg->SetControlActivated(true); + pMsg->SetCmdString(command); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : FindSceneObject +// PARAMETERS : name - string of name of the sceneobject +// : ageName - string of the name of the age to look in +// +// PURPOSE : Execute a console command from a python script, +// optionally propagate over the net +// +PyObject* cyMisc::FindSceneObject(const char* name, const char* ageName) +{ + // assume that we won't find the sceneobject (key is equal to nil) + plKey key=nil; + + if ( name || name[0] != 0) + { + const char* theAge = ageName; + if ( ageName[0] == 0 ) + theAge = nil; + key=plKeyFinder::Instance().StupidSearch(theAge,nil,plSceneObject::Index(), name, false); + } + + if ( key == nil ) + { + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s not found",name); + PyErr_SetString(PyExc_NameError, errmsg); + return nil; // return nil cause we errored + } + return pySceneObject::New(key); +} + +PyObject* cyMisc::FindActivator(const char* name) +{ + plKey key = nil; + if (name && strlen(name) > 0) + { + std::vector keylist; + plKeyFinder::Instance().ReallyStupidActivatorSearch(name, keylist); + + if (keylist.size() == 1) + key = keylist[0]; + } + + if (key == nil) + { + PYTHON_RETURN_NONE; + } + else + return pyKey::New(key); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : PopUpConsole +// PARAMETERS : command - string of console commmand to execute +// +// PURPOSE : Execute a console command from a python script +// +void cyMisc::PopUpConsole(const char* command) +{ + if ( command != nil ) + { + // create message to send to the console + plControlEventMsg* pMsg1 = TRACKED_NEW plControlEventMsg; + pMsg1->SetBCastFlag(plMessage::kBCastByType); + pMsg1->SetControlCode(B_SET_CONSOLE_MODE); + pMsg1->SetControlActivated(true); + plgDispatch::MsgSend( pMsg1 ); // whoosh... off it goes + // create message to send to the console + plControlEventMsg* pMsg2 = TRACKED_NEW plControlEventMsg; + pMsg2->SetBCastFlag(plMessage::kBCastByType); + pMsg2->SetControlCode(B_CONTROL_CONSOLE_COMMAND); + pMsg2->SetControlActivated(true); + pMsg2->SetCmdString(command); + plgDispatch::MsgSend( pMsg2 ); // whoosh... off it goes + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : TimerCallback +// PARAMETERS : command - string of console commmand to execute +// +// PURPOSE : Execute a console command from a python script +// +void cyMisc::TimerCallback(pyKey& selfkey, hsScalar time, UInt32 id) +{ + // setup the message to come back to whoever the pyKey is pointing to + plTimerCallbackMsg* pTimerMsg = TRACKED_NEW plTimerCallbackMsg(selfkey.getKey(),id); + plgTimerCallbackMgr::NewTimer( time, pTimerMsg ); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ClearTimerCallbacks +// PARAMETERS : key of object to clear callbacks to +// +// PURPOSE : clear timer callbacks to a certain key +// +void cyMisc::ClearTimerCallbacks(pyKey& selfkey) +{ + plgTimerCallbackMgr::CancelCallbacksToKey(selfkey.getKey()); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AttachObject +// PARAMETERS : child object +// : to be attached to parent object +// +// PURPOSE : Attach an object to another object, knowing only their pyKeys +// +void cyMisc::AttachObject(pyKey& ckey, pyKey& pkey) +{ + plKey childKey = ckey.getKey(); + plKey parentKey = pkey.getKey(); + + // make sure that there was a child ket + if ( childKey ) + { + // create the attach message to attach the child + plAttachMsg* pMsg = TRACKED_NEW plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRequest); + plgDispatch::MsgSend( pMsg ); + } +} +void cyMisc::AttachObjectSO(pySceneObject& cobj, pySceneObject& pobj) +{ + plKey childKey = cobj.getObjKey(); + plKey parentKey = pobj.getObjKey(); + + // make sure that there was a child ket + if ( childKey ) + { + // create the attach message to attach the child + plAttachMsg* pMsg = TRACKED_NEW plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRequest); + plgDispatch::MsgSend( pMsg ); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : DetachObject +// PARAMETERS : child object +// : to be attached to parent object +// +// PURPOSE : Attach an object to another object, knowing only their pyKeys +// +void cyMisc::DetachObject(pyKey& ckey, pyKey& pkey) +{ + plKey childKey = ckey.getKey(); + plKey parentKey = pkey.getKey(); + + // make sure that there was a child ket + if ( childKey ) + { + // create the attach message to detach the child + plAttachMsg* pMsg = TRACKED_NEW plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRemove); + plgDispatch::MsgSend( pMsg ); + } +} +void cyMisc::DetachObjectSO(pySceneObject& cobj, pySceneObject& pobj) +{ + plKey childKey = cobj.getObjKey(); + plKey parentKey = pobj.getObjKey(); + + // make sure that there was a child ket + if ( childKey ) + { + // create the attach message to detach the child + plAttachMsg* pMsg = TRACKED_NEW plAttachMsg(parentKey, childKey->GetObjectPtr(), plRefMsg::kOnRemove); + plgDispatch::MsgSend( pMsg ); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : LinkToAge +// PARAMETERS : +// +// PURPOSE : LinkToAge +// +// STATUS : Depreciated. Use plNetLinkingMgr or pyNetLinkingMgr instead. +// + +//void cyMisc::LinkToAge(pyKey &selfkey, const char *AgeName,const char *SpawnPointName) +//{ +// // find the Modifier that called us +// hsStatusMessage("PY: LinkToAge\n"); +// // Ask the Modifier if it was Local or Network +// if (selfkey.WasLocalNotify()) +// { +// hsStatusMessage("PY:LOCAL NOTIFY\n"); +// plNetLinkingMgr::GetInstance()->LinkToPublicAge( AgeName, SpawnPointName ); +// } +// else +// { +// hsStatusMessage("PY:REMOTE NOTIFY\n"); +// } +//} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SetDirtySyncStateServer +// PARAMETERS : +// +// PURPOSE : set the Python modifier to be dirty and asked to be saved out +// +void cyMisc::SetDirtySyncState(pyKey &selfkey, const char* SDLStateName, UInt32 sendFlags) +{ + selfkey.DirtySynchState(SDLStateName, sendFlags); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SetDirtySyncStateClients +// PARAMETERS : +// +// PURPOSE : set the Python modifier to be dirty and asked to be saved out +// +void cyMisc::SetDirtySyncStateWithClients(pyKey &selfkey, const char* SDLStateName, UInt32 sendFlags) +{ + selfkey.DirtySynchState(SDLStateName, sendFlags|plSynchedObject::kBCastToClients); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : EnableControlKeyEvents & DisableControlKeyEvents +// PARAMETERS : none +// +// PURPOSE : register and unregister for control key events +// +void cyMisc::EnableControlKeyEvents(pyKey &selfkey) +{ + selfkey.EnableControlKeyEvents(); +} + +void cyMisc::DisableControlKeyEvents(pyKey &selfkey) +{ + selfkey.DisableControlKeyEvents(); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetClientName +// PARAMETERS : avatar key +// +// PURPOSE : Return the net client (account) name of the player whose avatar +// key is provided. +// +hsBool cyMisc::WasLocallyNotified(pyKey &selfkey) +{ + return selfkey.WasLocalNotify(); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetClientName +// PARAMETERS : avatar key +// +// PURPOSE : Return the net client (account) name of the player whose avatar +// key is provided. +// +const char* cyMisc::GetClientName(pyKey &avKey) +{ + const char* ret=plNetClientMgr::GetInstance()->GetPlayerName(avKey.getKey()); + return (ret==nil) ? "" : ret; +} + +PyObject* cyMisc::GetAvatarKeyFromClientID(int clientID) +{ + PyObject* keyObj = NULL; + + if (clientID == plNetClientMgr::GetInstance()->GetPlayerID()) + { + keyObj = pyKey::New(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + } + else + { + plNetTransportMember **members = nil; + plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members ); + + if( members != nil) + { + for(int i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ ) + { + plNetTransportMember *mbr = members[ i ]; + if( mbr != nil && mbr->GetAvatarKey() != nil && mbr->GetPlayerID() == clientID) + { + keyObj = pyKey::New(mbr->GetAvatarKey()); + break; + } + } + } + + delete [] members; + } + + if (keyObj) + return keyObj; + else + PYTHON_RETURN_NONE; +} + + +int cyMisc::GetClientIDFromAvatarKey(pyKey& avatar) +{ + int ret = -1; + + if (plNetClientMgr::GetInstance()->GetLocalPlayerKey() == avatar.getKey()) + { + return (plNetClientMgr::GetInstance()->GetPlayerID()); + } + plNetTransportMember **members = nil; + plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members ); + if( members != nil) + { + for(int i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ ) + { + plNetTransportMember *mbr = members[ i ]; + + if( mbr != nil && mbr->GetAvatarKey() == avatar.getKey()) + { + ret = mbr->GetPlayerID(); + break; + } + } + } + + delete [] members; + return ret; +} + +int cyMisc::GetLocalClientID() +{ + return (plNetClientMgr::GetInstance()->GetPlayerID()); +} + +hsBool cyMisc::ValidateKey(pyKey& key) +{ + plKey pKey = key.getKey(); + + if (pKey && pKey->ObjectIsLoaded()) + return true; + + return false; +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetClientName +// PARAMETERS : +// +// PURPOSE : Return the local net client (account) name +// +const char* cyMisc::GetLocalClientName() +{ + return plNetClientMgr::GetInstance()->GetPlayerName(); +} + + +// +// Get Current age information - DEPRECIATED. Use ptDniInfoSource() object instead +// +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetAgeName +// Function : GetAgeTime +// Function : GetAgeGuid +// PARAMETERS : +// +// PURPOSE : Return the age name of the current age the local player is in +// : Return the current coordinates of the player within this age +// : Return the current time with the current age the player is in +// : Return the current guid of the instance of the age the player is in +// + +const char * cyMisc::GetAgeName() +{ + return NetCommGetAge()->ageDatasetName; +} + +PyObject* cyMisc::GetAgeInfo() +{ + plNetLinkingMgr* nmgr = plNetLinkingMgr::GetInstance(); + if (nmgr) + { + plAgeLinkStruct* als = nmgr->GetAgeLink(); + if (als) + return pyAgeInfoStruct::New(als->GetAgeInfo()); + } + PYTHON_RETURN_NONE; // return none, not nil (cause it isn't really an error... or is it?) +} + + +const char* cyMisc::GetPrevAgeName() +{ + plNetLinkingMgr* nmgr = plNetLinkingMgr::GetInstance(); + if (nmgr) + { + plAgeLinkStruct* als = nmgr->GetPrevAgeLink(); + if (als) + return als->GetAgeInfo()->GetAgeFilename(); + } + return nil; +} + +PyObject* cyMisc::GetPrevAgeInfo() +{ + plNetLinkingMgr* nmgr = plNetLinkingMgr::GetInstance(); + if (nmgr) + { + plAgeLinkStruct* als = nmgr->GetPrevAgeLink(); + if (als) + return pyAgeInfoStruct::New(als->GetAgeInfo()); + } + PYTHON_RETURN_NONE; // return none, not nil (cause it isn't really an error... or is it?) +} + +// current time in current age +UInt32 cyMisc::GetAgeTime( void ) +{ + return VaultAgeGetAgeTime(); +} + + + +UInt32 cyMisc::GetDniTime(void) +{ + const plUnifiedTime utime = plNetClientMgr::GetInstance()->GetServerTime(); + if ( utime.GetSecs() != 0) + return ConvertGMTtoDni(utime.GetSecs()); + else + return 0; +} + +UInt32 cyMisc::GetServerTime(void) +{ + const plUnifiedTime utime = plNetClientMgr::GetInstance()->GetServerTime(); + return utime.GetSecs(); +} + +float cyMisc::GetAgeTimeOfDayPercent(void) +{ + return plNetClientMgr::GetInstance()->GetCurrentAgeTimeOfDayPercent(); +} + +#define kMST (UInt32)25200 +#define kOneHour (UInt32)3600 +#define kOneDay (UInt32)86400 + +UInt32 cyMisc::ConvertGMTtoDni(UInt32 gtime) +{ + // convert to mountain time + UInt32 dtime = gtime - kMST; + plUnifiedTime utime = plUnifiedTime(); + utime.SetSecs(dtime); + // check for daylight savings time in New Mexico and adjust + if ( utime.GetMonth() >= 4 && utime.GetMonth() < 11 ) + { + plUnifiedTime dstStart = plUnifiedTime(); + dstStart.SetGMTime(utime.GetYear(),4,1,2,0,0); + // find first Sunday after 4/1 (first sunday of April) + UInt32 days_to_go = 7 - dstStart.GetDayOfWeek(); + if (days_to_go == 7) + days_to_go = 0; + UInt32 dstStartSecs = dstStart.GetSecs() + days_to_go * kOneDay; + + plUnifiedTime dstEnd = plUnifiedTime(); + dstEnd.SetGMTime(utime.GetYear(),10,25,1,0,0); + // find first sunday after 10/25 (last sunday of Oct.) + days_to_go = 7 - dstEnd.GetDayOfWeek(); + if (days_to_go == 7) + days_to_go = 0; + UInt32 dstEndSecs = dstEnd.GetSecs() + days_to_go * kOneDay; + + if ( dtime > dstStartSecs && dtime < dstEndSecs ) + // add hour for daylight savings time + dtime += kOneHour; + } + + return dtime; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ExcludeRegionSet +// PARAMETERS : key - of the exclude region, ie. where to send the message +// state - what state of to set at: +// 0 = release +// 1 = clear +// +// PURPOSE : Sets the state of an exclude region +// +void cyMisc::ExcludeRegionSet(pyKey& sender, pyKey& exKey, UInt16 state) +{ + plExcludeRegionMsg *msg = TRACKED_NEW plExcludeRegionMsg; + + switch (state) + { + case kExRegClear: + msg->SetCmd(plExcludeRegionMsg::kClear); + break; + case kExRegRelease: + msg->SetCmd(plExcludeRegionMsg::kRelease); + break; + } + msg->SetSender(sender.getKey()); + msg->AddReceiver(exKey.getKey()); + plgDispatch::MsgSend( msg ); // whoosh... off it goes +} + +void cyMisc::ExcludeRegionSetNow(pyKey& sender, pyKey& exKey, UInt16 state) +{ + plExcludeRegionMsg *msg = TRACKED_NEW plExcludeRegionMsg; + + switch (state) + { + case kExRegClear: + msg->SetCmd(plExcludeRegionMsg::kClear); + break; + case kExRegRelease: + msg->SetCmd(plExcludeRegionMsg::kRelease); + break; + } + msg->SetSender(sender.getKey()); + msg->AddReceiver(exKey.getKey()); + msg->fSynchFlags = plSynchedObject::kSendImmediately; + plgDispatch::MsgSend( msg ); // whoosh... off it goes +} + +#include "hsTimer.h" +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetSeconds +// PARAMETERS : +// +// PURPOSE : Return the nunber of seconds elapsed +// +double cyMisc::GetSeconds() +{ + return hsTimer::GetSeconds(); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetSysSeconds +// PARAMETERS : +// +// PURPOSE : Return the number of system seconds elapsed +// +double cyMisc::GetSysSeconds() +{ + return hsTimer::GetSysSeconds(); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetDelSysSeconds +// PARAMETERS : +// +// PURPOSE : Return the frame delta seconds +// +hsScalar cyMisc::GetDelSysSeconds() +{ + return hsTimer::GetDelSysSeconds(); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : LoadDialog +// PARAMETERS : +// +// PURPOSE : Return the frame delta seconds +// +void cyMisc::LoadDialog(const char* name) +{ + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + if ( mgr ) + { + if ( !mgr->IsDialogLoaded(name) ) + mgr->LoadDialog( name ); + } +} + +// Load dialog and set the GUINotifyMsg receiver key +void cyMisc::LoadDialogK(const char* name, pyKey& rKey) +{ + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + if ( mgr ) + { + // has the dialog been loaded yet? + if ( !mgr->IsDialogLoaded(name) ) + // no then load and set handler + mgr->LoadDialog( name, rKey.getKey() ); + else + // yes then just set the handler + mgr->SetDialogToNotify(name,rKey.getKey()); + } +} + +// Load dialog and set the GUINotifyMsg receiver key +void cyMisc::LoadDialogKA(const char* name, pyKey& rKey, const char* ageName) +{ + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + if ( mgr ) + { + // has the dialog been loaded yet? + if ( !mgr->IsDialogLoaded(name) ) + // no then load and set handler + mgr->LoadDialog( name, rKey.getKey(), ageName ); + else + // yes then just set the handler + mgr->SetDialogToNotify(name,rKey.getKey()); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : UnLoadDialog +// PARAMETERS : +// +// PURPOSE : UnLoads the dialog by name +// : optionally sets the receiver key for the GUINotifyMsg +// +void cyMisc::UnloadDialog(const char* name) +{ + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + if ( mgr ) + { + if ( mgr->IsDialogLoaded(name) ) + mgr->UnloadDialog( name ); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IsDialogLoaded +// PARAMETERS : +// +// PURPOSE : Test to see if a dialog is loaded (according to the dialog manager) +// +hsBool cyMisc::IsDialogLoaded(const char* name) +{ + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + if ( mgr ) + return mgr->IsDialogLoaded(name); + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ShowDialog +// Function : HideDialog +// PARAMETERS : +// +// PURPOSE : Show or Hide a dialog by name +// +void cyMisc::ShowDialog(const char* name) +{ + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + if ( mgr ) + mgr->ShowDialog(name); +} +void cyMisc::HideDialog(const char* name) +{ + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + if ( mgr ) + mgr->HideDialog(name); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetDialogFromTagID +// PARAMETERS : +// +// PURPOSE : Return the frame delta seconds +// +PyObject* cyMisc::GetDialogFromTagID(UInt32 tag) +{ + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + if ( mgr ) + { + // get the owner dialog modifier pointer + pfGUIDialogMod* pdialog = mgr->GetDialogFromTag(tag); + if ( pdialog ) + return pyGUIDialog::New(pdialog->GetKey()); + } + + char errmsg[256]; + sprintf(errmsg,"GUIDialog TagID %d not found",tag); + PyErr_SetString(PyExc_KeyError, errmsg); + return nil; // return nil, cause we threw an error +} + +PyObject* cyMisc::GetDialogFromString(const char* name) +{ + pfGameGUIMgr *mgr = pfGameGUIMgr::GetInstance(); + if ( mgr ) + { + // get the owner dialog modifier pointer + pfGUIDialogMod* pdialog = mgr->GetDialogFromString(name); + if ( pdialog ) + return pyGUIDialog::New(pdialog->GetKey()); + } + + char errmsg[256]; + sprintf(errmsg,"GUIDialog %s not found",name); + PyErr_SetString(PyExc_KeyError, errmsg); + return nil; // return nil, cause we threw an error +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IsGUIModal +// PARAMETERS : +// +// PURPOSE : Returns true if the GUI is currently modal (and therefore blocking input) +// +bool cyMisc::IsGUIModal() +{ + pfGameGUIMgr* mgr = pfGameGUIMgr::GetInstance(); + if (mgr) + return mgr->IsModalBlocking(); + return false; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetLocalAvatar +// Function : GetLocalPlayer +// PARAMETERS : +// +// PURPOSE : Return a pySceneobject of the local Avatar +// : Player - returns ptPlayer object +// +PyObject* cyMisc::GetLocalAvatar() +{ + plSceneObject *so = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); + if ( so ) + return pySceneObject::New(so->GetKey()); + + char errmsg[256]; + sprintf(errmsg,"Local avatar not found"); + PyErr_SetString(PyExc_NameError, errmsg); + return nil; // returns nil, cause we threw an error +} + +PyObject* cyMisc::GetLocalPlayer() +{ + return pyPlayer::New(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), + plNetClientMgr::GetInstance()->GetPlayerName(), + plNetClientMgr::GetInstance()->GetPlayerID(), + 0.0 ); +} + + + +#if 1 +#include "../plStatusLog/plStatusLog.h" +// +// TEMP SCREEN PRINT CODE FOR NON-DBG TEXT DISPLAY +// +void cyMisc::PrintToScreen(const char* msg) +{ + static plStatusLog* gStatusLog=nil; + if (gStatusLog==nil) + { + gStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog( 32, "", + plStatusLog::kDontWriteFile | plStatusLog::kDeleteForMe | plStatusLog::kFilledBackground ); + plStatusLogMgr::GetInstance().ToggleStatusLog(gStatusLog); + } + gStatusLog->AddLine(msg, plStatusLog::kBlue); +} +#endif + +#include "plPipeline.h" +#include "../plGImage/plMipmap.h" + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetPlayerList +// Function : GetPlayerListDistanceSorted +// PARAMETERS : +// +// PURPOSE : Get a list of players (other than self) that are playing the game +// : optionally get it sorted by distance +// +// RETURNS : Python object list of pyPlayer s +// +std::vector cyMisc::GetPlayerList() +{ + std::vector pyPL; + plNetClientMgr* nc=plNetClientMgr::GetInstance(); + + if (!nc) // only ever really happens if they try to call us in max... I hope + return pyPL; + + int i; + for( i = 0; i < nc->TransportMgr().GetNumMembers(); i++ ) + { + plNetTransportMember *mbr = nc->TransportMgr().GetMember(i); + plKey avkey = mbr->GetAvatarKey(); + if (avkey) + { + // only non-ignored people in list and not in ignore list + if ( !VaultAmIgnoringPlayer ( mbr->GetPlayerID()) ) + { + PyObject* playerObj = pyPlayer::New(avkey, mbr->GetPlayerName(), mbr->GetPlayerID(), mbr->GetDistSq()); + pyPlayer* player = pyPlayer::ConvertFrom(playerObj); // accesses internal pyPlayer object + + // modifies playerObj + if ( mbr->IsCCR() ) + player->SetCCRFlag(true); + if ( mbr->IsServer() ) + player->SetServerFlag(true); + + pyPL.push_back(playerObj); + } + } + } + return pyPL; +} + +std::vector cyMisc::GetPlayerListDistanceSorted() +{ + std::vector pyPL; + + // get the sorted member list from the Net transport manager + plNetTransportMember **members = nil; + plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members ); + if( members != nil ) + { + int i; + for( i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ ) + { + plNetTransportMember *mbr = members[ i ]; + plKey avkey = mbr->GetAvatarKey(); + if (avkey) + { + // only non-ignored people in list and not in ignore list + if ( !VaultAmIgnoringPlayer ( mbr->GetPlayerID()) ) + { + PyObject* playerObj = pyPlayer::New(avkey, mbr->GetPlayerName(), mbr->GetPlayerID(), mbr->GetDistSq()); + pyPlayer* player = pyPlayer::ConvertFrom(playerObj); // accesses internal pyPlayer object + + // modifies playerObj + if ( mbr->IsCCR() ) + player->SetCCRFlag(true); + if ( mbr->IsServer() ) + player->SetServerFlag(true); + + pyPL.push_back(playerObj); + } + } + } + delete [] members; + } + return pyPL; +} + +UInt32 cyMisc::GetMaxListenListSize() +{ + return plNetListenList::kMaxListenListSize; +} + +hsScalar cyMisc::GetMaxListenDistSq() +{ + return plNetListenList::kMaxListenDistSq; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SendRTChat +// PARAMETERS : from - is a pyPlayer of the person who is sending this +// : tolist - is a python list object, if empty then broadcast message +// : message - text string to send to others +// : flags - the flags of destiny... whatever that means +// +// PURPOSE : To send a real time chat message to a particualr user, a list of users +// : or broadcast it to everyone (within hearing distance?) +// +// RETURNS : the flags that were sent with the message (may be modified) +// +UInt32 cyMisc::SendRTChat(pyPlayer& from, const std::vector & tolist, const char* message, UInt32 flags) +{ + // create the messge that will contain the chat message + pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kHACKChatMsg ); + msg->SetString( message ); + msg->SetUser( from.GetPlayerName(), from.GetPlayerID() ); + msg->SetFlags( flags ); + msg->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce); + msg->SetBCastFlag(plMessage::kLocalPropagate, 0); + + if (tolist.size() > 0) + { + if (flags & 8/* kRTChatInterAge in PlasmaKITypes.py */) + { + // allow inter-age routing of this msg + msg->SetBCastFlag( plMessage::kNetAllowInterAge ); + } + // add net rcvrs to msg + int i; + for ( i=0 ; iGetPlayerID() ) ) + msg->AddNetReceiver(tolist[i]->GetPlayerID()); + } + } + + UInt32 msgFlags = msg->GetFlags(); + + if (tolist.size() == 0 || (msg->GetNetReceivers() && msg->GetNetReceivers()->size() > 0)) + msg->Send(); + + return msgFlags; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SendKIMessage +// PARAMETERS : command - the command type +// : value - extra value +// +// PURPOSE : Send message to the KI, to tell it things to do +// +// RETURNS : nothing +// +void cyMisc::SendKIMessage(UInt32 command, hsScalar value) +{ + // create the mesage to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( (UInt8)command ); + + // check to see if the value makes any sense + if ( command == pfKIMsg::kSetChatFadeDelay ) + { + msg->SetDelay(value); + } + else if ( command == pfKIMsg::kSetTextChatAdminMode ) + { + msg->SetFlags( value==1.0f ? pfKIMsg::kAdminMsg : 0 ); + } + // send it off + plgDispatch::MsgSend( msg ); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SendKIMessageS +// PARAMETERS : command - the command type +// : value - extra value as a string +// +// PURPOSE : Send message to the KI, to tell it things to do +// +// RETURNS : nothing +// +void cyMisc::SendKIMessageS(UInt32 command, const wchar_t* value) +{ + // create the mesage to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( (UInt8)command ); + + msg->SetString( value ); + + // send it off + plgDispatch::MsgSend( msg ); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SendKIMessageI +// PARAMETERS : command - the command type +// : value - extra value as an Int32 +// +// PURPOSE : Send message to the KI, to tell it things to do +// +// RETURNS : nothing +// +void cyMisc::SendKIMessageI(UInt32 command, Int32 value) +{ + // create the mesage to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( (UInt8)command ); + + msg->SetIntValue(value); + + // send it off + plgDispatch::MsgSend( msg ); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SendKIMessageIReply +// PARAMETERS : command - the command type +// : value - extra value as an Int32 +// +// PURPOSE : Send message to the KI, to tell it things to do +// +// RETURNS : nothing +// +void cyMisc::SendKIGZMarkerMsg(Int32 markerNumber, pyKey& sender) +{ + // create the mesage to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kGZInRange ); + + msg->SetIntValue(markerNumber); + msg->SetSender(sender.getKey()); + + // send it off + plgDispatch::MsgSend( msg ); +} + +void cyMisc::SendKIRegisterImagerMsg(const char* imagerName, pyKey& sender) +{ + pfKIMsg *msg = TRACKED_NEW pfKIMsg(pfKIMsg::kRegisterImager); + + msg->SetString(imagerName); + msg->SetSender(sender.getKey()); + + plgDispatch::MsgSend( msg ); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : YesNoDialog +// PARAMETERS : sender - who set this and will get the notify +// : message - message to put up in YesNo dialog +// +// PURPOSE : Put up Yes/No dialog +// +// RETURNS : nothing +// + +void cyMisc::YesNoDialog(pyKey& sender, const char* thestring) +{ + // create the mesage to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kYesNoDialog ); + + msg->SetSender(sender.getKey()); + msg->SetString(thestring); + // send it off + plgDispatch::MsgSend( msg ); +} + +void cyMisc::YesNoDialog(pyKey& sender, std::wstring thestring) +{ + char *temp = hsWStringToString(thestring.c_str()); + YesNoDialog(sender,temp); + delete [] temp; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : RateIt +// PARAMETERS : chonicleName - where to store the rating +// : thestring - the message in the RateIt dialog +// : onceFlag - flag to tell whether this is a onetime thing or ask everytime +// +// PURPOSE : Send message to the KI to tell it to ask the user to Rate something +// +// RETURNS : nothing +// +void cyMisc::RateIt(const char* chronicleName, const char* thestring, hsBool onceFlag) +{ + // create the mesage to send + pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kRateIt ); + + msg->SetUser(chronicleName,0); + msg->SetString(thestring); + msg->SetIntValue(onceFlag); + // send it off + plgDispatch::MsgSend( msg ); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SetPrivateChatList +// PARAMETERS : key - who's joining +// +// PURPOSE : Lock the local avatar into private vox messaging, and / or add new memebers to his chat list +// +// RETURNS : nothing +// + +void cyMisc::SetPrivateChatList(const std::vector & tolist) +{ + if (tolist.size() > 0) + { + plNetVoiceListMsg* pMsg = TRACKED_NEW plNetVoiceListMsg(plNetVoiceListMsg::kForcedListenerMode); + for (int i=0 ; iGetClientList()->Append(tolist[i]->GetPlayerID()); + + pMsg->SetRemovedKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + pMsg->Send(); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ClearPrivateChatList +// PARAMETERS : key - who's leaving +// +// PURPOSE : Remove the local avatar from private vox messaging, and / or clear memebers from his chat list +// +// RETURNS : nothing +// +void cyMisc::ClearPrivateChatList(pyKey& member) +{ + plNetVoiceListMsg* pMsg = TRACKED_NEW plNetVoiceListMsg(plNetVoiceListMsg::kDistanceMode); + pMsg->SetRemovedKey( member.getKey() ); + pMsg->Send(); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ClearCameraStack +// PURPOSE : clear the camera stack +// +// RETURNS : nothing +// + +void cyMisc::ClearCameraStack() +{ + plVirtualCam1::Instance()->ClearStack(); +} + +bool cyMisc::IsFirstPerson() +{ + return (plVirtualCam1::Instance()->Is1stPersonCamera()!=0); +} +///////////////////////////////////////////////////////////////////////////// +// +// Function : SendPetitionToCCR +// PARAMETERS : message - message to send to the CCR ("please help me!") +// +// PURPOSE : Send a petition to the CCR for help or questions +// +void cyMisc::SendPetitionToCCR(const char* message) +{ + SendPetitionToCCRI(message,plNetCommon::PetitionTypes::kGeneralHelp,nil); +} +void cyMisc::SendPetitionToCCRI(const char* message, UInt8 reason,const char* title) +{ + // create the mesage to send + plCCRPetitionMsg *msg = TRACKED_NEW plCCRPetitionMsg(); + msg->SetNote(message); + msg->SetType(reason); + if (title) + msg->SetTitle(title); + // send it off + plgDispatch::MsgSend( msg ); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SendChatToCCR +// PARAMETERS : message - message to send to the CCR ("please help me!") +// +// PURPOSE : Send a chat message to the CCR for help or questions +// +void cyMisc::SendChatToCCR(const char* message,Int32 CCRPlayerID) +{ + // create the mesage to send + plCCRCommunicationMsg *msg = TRACKED_NEW plCCRCommunicationMsg(); + msg->SetMessage(message); + msg->SetType(plCCRCommunicationMsg::kReturnChatMsg); + msg->SetBCastFlag(plMessage::kNetAllowInterAge); + msg->SetBCastFlag(plMessage::kNetPropagate); + msg->SetBCastFlag(plMessage::kNetForce); + msg->SetBCastFlag(plMessage::kLocalPropagate,0); + msg->AddNetReceiver( CCRPlayerID ); + msg->Send(); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetNumRemotePlayers +// +// PURPOSE : return the number of remote players connected +// +int cyMisc::GetNumRemotePlayers() +{ + return plNetClientMgr::GetInstance()->RemotePlayerKeys().size(); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Paging functions +// PARAMETERS : nodeName - name of the page to load +// +// PURPOSE : page in, hold or out a particular node +// + +void cyMisc::PageInNodes(const std::vector & nodeNames, const char* age) +{ + if (hsgResMgr::ResMgr()) + { + plSynchEnabler ps(false); // disable dirty tracking while paging in + plClientMsg* msg = TRACKED_NEW plClientMsg(plClientMsg::kLoadRoom); + plKey clientKey = hsgResMgr::ResMgr()->FindKey(kClient_KEY); + msg->AddReceiver(clientKey); + + int numNames = nodeNames.size(); + for (int i = 0; i < numNames; i++) + msg->AddRoomLoc(plKeyFinder::Instance().FindLocation(age ? age : NetCommGetAge()->ageDatasetName, nodeNames[i].c_str())); + + msg->Send(); + } +} + +void cyMisc::PageOutNode(const char* nodeName) +{ + if ( hsgResMgr::ResMgr() ) + { + plSynchEnabler ps(false); // disable dirty tracking while paging out + plClientMsg* pMsg1 = TRACKED_NEW plClientMsg(plClientMsg::kUnloadRoom); + plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY ); + pMsg1->AddReceiver( clientKey ); + pMsg1->AddRoomLoc(plKeyFinder::Instance().FindLocation(nil, nodeName)); + plgDispatch::MsgSend(pMsg1); + } +} + + +#include "../plAvatar/plArmatureMod.h" +///////////////////////////////////////////////////////////////////////////// +// +// Function : LimitAvatarLOD +// PARAMETERS : LODlimit - number of to limit the LOD to +// +// PURPOSE : sets the avatar LOD limit +// +void cyMisc::LimitAvatarLOD(int LODlimit) +{ + if(LODlimit >= 0 && LODlimit <= 2) + plArmatureLODMod::fMinLOD = LODlimit; +} + + + +#include "../plPipeline/plFogEnvironment.h" + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Set fog default functions +// PARAMETERS : floats - the parameters +// +// PURPOSE : sets the fog defaults +// +void cyMisc::FogSetDefColor(pyColor& color) +{ + if ( fPipeline ) + { + hsColorRGBA hcolor = color.getColor(); + hcolor.a = 1.0f; // make sure that alpha is 1 + plFogEnvironment env = fPipeline->GetDefaultFogEnviron(); + env.SetColor( hcolor ); + fPipeline->SetDefaultFogEnviron( &env ); + } +} + +void cyMisc::FogSetDefLinear(float start, float end, float density) +{ + if ( fPipeline ) + { + plFogEnvironment env = fPipeline->GetDefaultFogEnviron(); + env.Set( start, end, density ); + fPipeline->SetDefaultFogEnviron( &env ); + } +} + +void cyMisc::FogSetDefExp(float end, float density) +{ + if ( fPipeline ) + { + plFogEnvironment env = fPipeline->GetDefaultFogEnviron(); + env.SetExp( plFogEnvironment::kExpFog, end, density ); + fPipeline->SetDefaultFogEnviron( &env ); + } +} + +void cyMisc::FogSetDefExp2(float end, float density) +{ + if ( fPipeline ) + { + plFogEnvironment env = fPipeline->GetDefaultFogEnviron(); + env.SetExp( plFogEnvironment::kExp2Fog, end, density ); + fPipeline->SetDefaultFogEnviron( &env ); + } +} + + +void cyMisc::SetClearColor(float red, float green, float blue) +{ + // do this command via the console to keep the maxplugins from barfing + char command[256]; + sprintf(command,"Graphics.Renderer.SetClearColor %f %f %f",red,green,blue); + // create message to send to the console + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetBCastFlag(plMessage::kBCastByType); + pMsg->SetControlCode(B_CONTROL_CONSOLE_COMMAND); + pMsg->SetControlActivated(true); + pMsg->SetCmdString(command); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Enable / disable cursor fade for avatar +// PARAMETERS : +// +// PURPOSE : turns avatar fade out on / off +// + +void cyMisc::EnableAvatarCursorFade() +{ + plNetClientMgr* nmgr = plNetClientMgr::GetInstance(); + if (nmgr) + { + plIfaceFadeAvatarMsg* iMsg = TRACKED_NEW plIfaceFadeAvatarMsg; + iMsg->SetSubjectKey(nmgr->GetLocalPlayerKey()); + iMsg->SetBCastFlag(plMessage::kBCastByExactType); + iMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE); + iMsg->Enable(); + iMsg->Send(); + } +} + +void cyMisc::DisableAvatarCursorFade() +{ + plNetClientMgr* nmgr = plNetClientMgr::GetInstance(); + if (nmgr) + { + plIfaceFadeAvatarMsg* iMsg = TRACKED_NEW plIfaceFadeAvatarMsg; + iMsg->SetSubjectKey(nmgr->GetLocalPlayerKey()); + iMsg->SetBCastFlag(plMessage::kBCastByExactType); + iMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE); + iMsg->Disable(); + iMsg->Send(); + } +} + +void cyMisc::FadeLocalPlayer(hsBool fade) +{ + plNetClientMgr* nmgr = plNetClientMgr::GetInstance(); + if (nmgr) + { + plCameraTargetFadeMsg* pMsg = TRACKED_NEW plCameraTargetFadeMsg; + pMsg->SetFadeOut(fade); + pMsg->SetSubjectKey(nmgr->GetLocalPlayerKey()); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE); + pMsg->AddReceiver(nmgr->GetLocalPlayerKey()); + plgDispatch::MsgSend(pMsg); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : offer linking book functions +// PARAMETERS : +// +// PURPOSE : manage offering public (pedestal) books +// + +void cyMisc::EnableOfferBookMode(pyKey& selfkey, const char* ageFilename, const char* ageInstanceName) +{ + plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kSetOfferBookMode); + pMsg->SetSender(selfkey.getKey()); + pMsg->SetAgeFileName(ageFilename); + pMsg->SetAgeName(ageInstanceName); + pMsg->Send(); +} + +void cyMisc::DisableOfferBookMode() +{ + plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kClearOfferBookMode); + pMsg->Send(); +} + +void cyMisc::NotifyOffererPublicLinkCompleted(UInt32 offerer) +{ + plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kNotifyOfferCompleted, plNetClientMgr::GetInstance()->GetPlayerID()); + pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + if (offerer != plNetClientMgr::GetInstance()->GetPlayerID()) + { + pMsg->SetBCastFlag( plMessage::kNetPropagate ); + pMsg->SetBCastFlag( plMessage::kNetForce ); + pMsg->SetBCastFlag( plMessage::kLocalPropagate, 0 ); + pMsg->AddNetReceiver( offerer ); + } + + pMsg->Send(); +} + +void cyMisc::NotifyOffererPublicLinkRejected(UInt32 offerer) +{ + plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kNotifyOfferRejected); + pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + if (offerer != plNetClientMgr::GetInstance()->GetPlayerID()) + { + pMsg->SetBCastFlag( plMessage::kNetPropagate ); + pMsg->SetBCastFlag( plMessage::kNetForce ); + pMsg->SetBCastFlag( plMessage::kLocalPropagate, 0 ); + pMsg->AddNetReceiver( offerer ); + } + + pMsg->Send(); + ToggleAvatarClickability(true); +} + +void cyMisc::NotifyOffererPublicLinkAccepted(UInt32 offerer) +{ + plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kNotifyOfferAccepted); + pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + if (offerer != plNetClientMgr::GetInstance()->GetPlayerID()) + { + pMsg->SetBCastFlag( plMessage::kNetPropagate ); + pMsg->SetBCastFlag( plMessage::kNetForce ); + pMsg->SetBCastFlag( plMessage::kLocalPropagate, 0 ); + pMsg->AddNetReceiver( offerer ); + } + + pMsg->Send(); +} + +void cyMisc::ToggleAvatarClickability(hsBool on) +{ + plInputIfaceMgrMsg* pMsg = 0; + if (on) + pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kGUIEnableAvatarClickable); + else + pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kGUIDisableAvatarClickable); + pMsg->SetAvKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + pMsg->Send(); + +} + +void cyMisc::SetShareSpawnPoint(const char* spawnPoint) +{ + plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kSetShareSpawnPoint); + pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + pMsg->SetSpawnPoint(spawnPoint); + pMsg->Send(); +} + +void cyMisc::SetShareAgeInstanceGuid(const Uuid& guid) +{ + plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kSetShareAgeInstanceGuid); + pMsg->SetSender(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + pMsg->SetAgeInstanceGuid(guid); + pMsg->Send(); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : GetCCRAwayStatus +// PARAMETERS : +// +// PURPOSE : Returns current status of CCR dept +// +hsBool cyMisc::IsCCRAwayStatus() +{ + return !VaultGetCCRStatus(); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : AmCCR +// PARAMETERS : +// +// PURPOSE : Returns true if local player is a CCR +// +hsBool cyMisc::AmCCR() +{ + if ( plNetClientApp::GetInstance() ) + return plNetClientApp::GetInstance()->AmCCR(); + return false; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// Function : AcceptInviteInGame +// PARAMETERS : Friend's Name, Invite Key +// +// PURPOSE : Send's a VaultTask to the server to perform the invite +// +void cyMisc::AcceptInviteInGame(const char* friendName, const char* inviteKey) +{ + hsAssert(false, "eric, implement me"); +#if 0 + plNetClientMgr* nc = plNetClientMgr::GetInstance(); + if (nc) + { + plNetMsgVaultTask msg; + msg.SetNetProtocol(kNetProtocolCli2Auth); + msg.SetTopic(plNetCommon::VaultTasks::kFriendInvite); + msg.GetArgs()->AddString(plNetCommon::VaultTaskArgs::kFriendName,friendName); + msg.GetArgs()->AddString(plNetCommon::VaultTaskArgs::kInviteKey,inviteKey); + nc->SendMsg(&msg); + } +#endif +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : GetLanguage +// PARAMETERS : +// +// PURPOSE : Returns the current language the game is in +// +int cyMisc::GetLanguage() +{ + return int(plLocalization::GetLanguage()); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : UsingUnicode +// PARAMETERS : +// +// PURPOSE : Returns true if the current language uses Unicode (like Japanese) +// +bool cyMisc::UsingUnicode() +{ + return plLocalization::UsingUnicode(); +} + +////////////////////////////////////////////////////////////////////////////// +// +// +// particle system management +// +// +// +#include "../plMessage/plParticleUpdateMsg.h" +#include "../plParticleSystem/plParticleSystem.h" +#include "../plParticleSystem/plParticleEffect.h" +void cyMisc::TransferParticlesToKey(pyKey& fromKey, pyKey& toKey, int numParticles) +{ + plKey frKey = fromKey.getKey(); + plSceneObject* so = plSceneObject::ConvertNoRef(toKey.getKey()->ObjectIsLoaded()); + if (so == nil) + return; + + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + plParticleTransferMsg* pMsg = TRACKED_NEW plParticleTransferMsg(nil, avMod->GetKey(), 0, frKey, numParticles); + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + pMsg->Send(); +} + +void cyMisc::SetParticleDissentPoint(float x, float y, float z, pyKey& particles) +{ + plKey frKey = particles.getKey(); + plSceneObject* pObj = plSceneObject::ConvertNoRef(particles.getKey()->ObjectIsLoaded()); + if (!pObj) + return; + const plParticleSystem *sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index())); + if (sys == nil) + { + const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index()); + if (pArm) + { + // it's the avatar + const plArmatureMod* pCArm = (const plArmatureMod*)pArm; + plArmatureMod* pNonConstArm = const_cast(pCArm); + pObj = pNonConstArm->GetFollowerParticleSystemSO(); + if (!pObj) + return; + else + sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index())); + + } + if (sys == nil) + return; + + } + plParticleEffect *flock = sys->GetEffect(plParticleFlockEffect::Index()); + if (flock) + { + (TRACKED_NEW plParticleFlockMsg(nil, flock->GetKey(), 0, plParticleFlockMsg::kFlockCmdSetDissentPoint, x, y, z))->Send(); + } +} + +void cyMisc::SetParticleOffset(float x, float y, float z, pyKey& particles) +{ + plKey frKey = particles.getKey(); + plSceneObject* pObj = plSceneObject::ConvertNoRef(particles.getKey()->ObjectIsLoaded()); + if (!pObj) + return; + const plParticleSystem *sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index())); + if (sys == nil) + { + const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index()); + if (pArm) + { + // it's the avatar + const plArmatureMod* pCArm = (const plArmatureMod*)pArm; + plArmatureMod* pNonConstArm = const_cast(pCArm); + pObj = pNonConstArm->GetFollowerParticleSystemSO(); + if (!pObj) + return; + else + sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index())); + + } + if (sys == nil) + return; + + } + + plParticleEffect *flock = sys->GetEffect(plParticleFlockEffect::Index()); + if (flock) + { + (TRACKED_NEW plParticleFlockMsg(nil, flock->GetKey(), 0, plParticleFlockMsg::kFlockCmdSetOffset, x, y, z))->Send(); + } +} + +void cyMisc::KillParticles(float time, float pct, pyKey& particles) +{ + plKey frKey = particles.getKey(); + plSceneObject* pObj = plSceneObject::ConvertNoRef(particles.getKey()->ObjectIsLoaded()); + if (!pObj) + return; + const plParticleSystem *sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index())); + if (sys == nil) + { + const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index()); + if (pArm) + { + // it's the avatar + const plArmatureMod* pCArm = (const plArmatureMod*)pArm; + plArmatureMod* pNonConstArm = const_cast(pCArm); + pObj = pNonConstArm->GetFollowerParticleSystemSO(); + if (!pObj) + return; + else + sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index())); + + } + if (sys == nil) + return; + + } + plParticleEffect *flock = sys->GetEffect(plParticleFlockEffect::Index()); + if (flock) + { + plParticleKillMsg* pMsg = TRACKED_NEW plParticleKillMsg(nil, frKey, 0, pct, time, plParticleKillMsg::kParticleKillPercentage | plParticleKillMsg::kParticleKillImmortalOnly); + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + pMsg->SetBCastFlag(plMessage::kPropagateToChildren); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + pMsg->Send(); + } +} + +int cyMisc::GetNumParticles(pyKey& host) +{ + plSceneObject* pObj = plSceneObject::ConvertNoRef(host.getKey()->ObjectIsLoaded()); + if (!pObj) + return 0; + const plModifier* pArm = pObj->GetModifierByType(plArmatureMod::Index()); + if (pArm) + { + // it's the avatar + const plArmatureMod* pCArm = (const plArmatureMod*)pArm; + plArmatureMod* pNonConstArm = const_cast(pCArm); + pObj = pNonConstArm->GetFollowerParticleSystemSO(); + if (!pObj) + return 0; + } + const plModifier* pPart = pObj->GetModifierByType(plParticleSystem::Index()); + if (!pPart) + return 0; + return ((const plParticleSystem*)pPart)->GetNumValidParticles(true); +} + + +void cyMisc::SetLightColorValue(pyKey& light, std::string lightName, hsScalar r, hsScalar g, hsScalar b, hsScalar a) +{ + // lightName is the name of the light object attached to the light that we want to talk to + // for the bug lights, this would be "RTOmni-BugLightTest" + plSceneObject* pObj = plSceneObject::ConvertNoRef(light.getKey()->ObjectIsLoaded()); + if (!pObj) + return; + + plObjInterface* pIface = pObj->GetGenericInterface(plLightInfo::Index()); + if (pIface) + { + // we are a light ourselves... are we the one they are looking for? + if (lightName != pObj->GetKeyName()) + pIface = nil; // not the one they want, check our children + } + + if (!pIface) + { + // recurse through our children... + for (int i = 0; i < pObj->GetCoordinateInterface()->GetNumChildren(); i++) + { + const plSceneObject* child = pObj->GetCoordinateInterface()->GetChild(i)->GetOwner(); + if (child) + { + pIface = child->GetGenericInterface(plLightInfo::Index()); + if (pIface) + { + // found a child... is it the one we want? + if (lightName != child->GetKeyName()) + pIface = nil; // not the child we want, keep looking + } + } + if (pIface) + break; + } + } + if (pIface) + { + plLightInfo* pLight = (plLightInfo*)pIface; + hsColorRGBA c; + c.Set(r,g,b,a); + pLight->SetDiffuse(c); + pLight->SetDiffuse(c); + pLight->SetSpecular(c); + } +} + +#include "../pnMessage/plEnableMsg.h" +void cyMisc::SetLightAnimationOn(pyKey& light, std::string lightName, hsBool start) +{ + // lightName is the name of the light object attached to the light that we want to talk to + // for the bug lights, this would be "RTOmni-BugLightTest" + plSceneObject* pObj = plSceneObject::ConvertNoRef(light.getKey()->ObjectIsLoaded()); + if (!pObj) + return; + + plObjInterface* pIface = pObj->GetGenericInterface(plLightInfo::Index()); + if (pIface) + { + // we are a light ourselves... are we the one they are looking for? + if (lightName != pObj->GetKeyName()) + pIface = nil; // not the one they want, check our children + } + + if (!pIface) + { + // recurse through our children... + for (int i = 0; i < pObj->GetCoordinateInterface()->GetNumChildren(); i++) + { + const plSceneObject* child = pObj->GetCoordinateInterface()->GetChild(i)->GetOwner(); + if (child) + { + pIface = child->GetGenericInterface(plLightInfo::Index()); + if (pIface) + { + // found a child... is it the one we want? + if (lightName != child->GetKeyName()) + pIface = nil; // not the child we want, keep looking + } + } + if (pIface) + break; + } + } + if (pIface) + { + plEnableMsg* enableMsg = TRACKED_NEW plEnableMsg; + enableMsg->AddReceiver(pIface->GetKey()); + enableMsg->SetBCastFlag(plMessage::kNetPropagate); + enableMsg->SetBCastFlag(plMessage::kNetForce); + + plAnimCmdMsg* animMsg = TRACKED_NEW plAnimCmdMsg; + animMsg->AddReceiver(pIface->GetOwnerKey()); + animMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + animMsg->SetBCastFlag(plMessage::kNetPropagate); + animMsg->SetBCastFlag(plMessage::kNetForce); + + if (start) + { + enableMsg->SetCmd(plEnableMsg::kEnable); + animMsg->SetCmd(plAnimCmdMsg::kContinue); + } + else + { + enableMsg->SetCmd(plEnableMsg::kDisable); + animMsg->SetCmd(plAnimCmdMsg::kStop); + animMsg->SetCmd(plAnimCmdMsg::kGoToBegin); + } + + enableMsg->Send(); + animMsg->Send(); + } +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : RegisterForControlEventMessages +// PARAMETERS : switch on or off, registrant +// +// PURPOSE : let you get control event messages at will (for pseudo-GUI's like the psnl bookshelf or clft imager) + +void cyMisc::RegisterForControlEventMessages(hsBool on, pyKey& k) +{ + plCmdIfaceModMsg* pMsg = TRACKED_NEW plCmdIfaceModMsg; + pMsg->SetSender(k.getKey()); + if (on) + pMsg->SetCmd(plCmdIfaceModMsg::kAdd); + else + pMsg->SetCmd(plCmdIfaceModMsg::kRemove); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->Send(); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : RequestLOSScreen +// PARAMETERS : lots... +// +// PURPOSE : To request an LOS from a point on the screen +// +#include "../plMessage/plLOSRequestMsg.h" +bool cyMisc::RequestLOSScreen(pyKey &selfkey, Int32 ID, hsScalar xPos, hsScalar yPos, hsScalar distance, int what, int reportType) +{ + plPipeline* pipe = selfkey.GetPipeline(); + if (pipe) + { + Int32 x=(Int32) ( xPos * pipe->Width() ); + Int32 y=(Int32) ( yPos * pipe->Height() ); + + hsPoint3 endPos, startPos; + + pipe->ScreenToWorldPoint( 1,0, &x, &y, distance, 0, &endPos ); + startPos = pipe->GetViewPositionWorld(); + + // move the start pos out a little to avoid backing up against physical objects... + hsVector3 view(endPos - startPos); + view.Normalize(); + startPos = startPos + (view * 1.5f); + + plLOSRequestMsg* pMsg = nil; + switch (what) + { + case kClickables: + pMsg = TRACKED_NEW plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBUIItems, plLOSRequestMsg::kTestClosest ); + pMsg->SetCullDB(plSimDefs::kLOSDBUIBlockers); + break; + case kCameraBlockers: + pMsg = TRACKED_NEW plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBCameraBlockers, plLOSRequestMsg::kTestClosest ); + break; + case kCustom: + pMsg = TRACKED_NEW plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBCustom, plLOSRequestMsg::kTestClosest ); + break; + case kShootable: + pMsg = TRACKED_NEW plLOSRequestMsg( selfkey.getKey(), startPos, endPos, plSimDefs::kLOSDBShootableItems, plLOSRequestMsg::kTestClosest ); + break; + } + + if ( pMsg ) + { + pMsg->SetReportType( (plLOSRequestMsg::ReportType)reportType ); + + pMsg->SetRequestID( ID ); + + plgDispatch::MsgSend( pMsg ); + return true; + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : CheckVisLOS,CheckVisLOSFromCursor +// PARAMETERS : StartPoint, EndPoint +// +// PURPOSE : Check is there is something visible in the path from StartPoint to EndPoint +// +#include "../plDrawable/plVisLOSMgr.h" +PyObject* cyMisc::CheckVisLOS(pyPoint3 startPoint, pyPoint3 endPoint) +{ + if (plVisLOSMgr::Instance()) + { + plVisHit hit; + if( plVisLOSMgr::Instance()->Check(startPoint.fPoint,endPoint.fPoint,hit) ) + { + return pyPoint3::New(hit.fPos); + } + } + PYTHON_RETURN_NONE; +} + +PyObject* cyMisc::CheckVisLOSFromCursor() +{ + if (plVisLOSMgr::Instance()) + { + plVisHit hit; + if( plVisLOSMgr::Instance()->CursorCheck(hit) ) + { + return pyPoint3::New(hit.fPos); + } + } + PYTHON_RETURN_NONE; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : IsSinglePlayerMode +// PARAMETERS : +// +// PURPOSE : Returns whether the game is in Single Player mode +// +bool cyMisc::IsSinglePlayerMode() +{ + return false; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : IsDemoMode +// PARAMETERS : +// +// PURPOSE : Returns whether the game is in Single Player mode +// +bool cyMisc::IsDemoMode() +{ + plNetClientApp* nc = plNetClientApp::GetInstance(); + if (nc) + return nc->InDemoMode(); + // if we couldn't find the net client app, maybe it was because we are single player mode + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : IsInternalRelease +// PARAMETERS : +// +// PURPOSE : Returns true if we are running an internal build +// +bool cyMisc::IsInternalRelease() +{ +#ifdef PLASMA_EXTERNAL_RELEASE + return false; +#else + return true; +#endif +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : IsEnterChatModeKeyBound +// PARAMETERS : +// +// PURPOSE : Returns whether the EnterChatMode is bound to a key +// +bool cyMisc::IsEnterChatModeKeyBound() +{ + plAvatarInputInterface* aii = plAvatarInputInterface::GetInstance(); + if (aii) + return aii->IsEnterChatModeBound(); + // if we couldn't find the net client app, maybe it was because we are single player mode + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : ShootBulletFromScreen +// PARAMETERS : lots... +// +// PURPOSE : Shoots from screen coordinates, a bullet and makes a mark on objects that know about bullet holes +// +#include "../plMessage/plBulletMsg.h" +void cyMisc::ShootBulletFromScreen(pyKey &selfkey, hsScalar xPos, hsScalar yPos, hsScalar radius, hsScalar range) +{ + plPipeline* pipe = selfkey.GetPipeline(); + if (pipe) + { + Int32 x=(Int32) ( xPos * pipe->Width() ); + Int32 y=(Int32) ( yPos * pipe->Height() ); + + hsPoint3 endPos, startPos; + + pipe->ScreenToWorldPoint( 1,0, &x, &y, range, 0, &endPos ); + startPos = pipe->GetViewPositionWorld(); + + // move the start pos out a little to avoid backing up against physical objects... + hsVector3 view(endPos - startPos); + view.Normalize(); + startPos = startPos + (view * 1.5f); + + plBulletMsg* bull = TRACKED_NEW plBulletMsg( selfkey.getKey(), nil, nil ); + bull->FireShot(startPos, view, radius, range); + bull->Send(); + } +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : ShootBulletFromObject +// PARAMETERS : lots... +// +// PURPOSE : Shoots from an object, a bullet and makes a mark on objects that know about bullet holes +// +void cyMisc::ShootBulletFromObject(pyKey &selfkey, pySceneObject* sobj, hsScalar radius, hsScalar range) +{ + plSceneObject* so = plSceneObject::ConvertNoRef(sobj->getObjKey()->ObjectIsLoaded()); + if( so ) + { + // find the direction + + hsMatrix44 l2w = so->GetLocalToWorld(); + hsVector3 dir(-l2w.fMap[0][0], -l2w.fMap[1][0], -l2w.fMap[2][0]); + dir.Normalize(); + hsPoint3 pos = l2w.GetTranslate(); + + plBulletMsg* bull = TRACKED_NEW plBulletMsg(selfkey.getKey(), nil, nil); + bull->FireShot(pos, dir, radius, range); + + bull->Send(); + } +} + + +////////////////////////////////////////////////////////////////////////////// +class NetClientCommCallback : public plNetClientComm::Callback +{ +public: + enum Contexts + { + kInvalid, + kGetPublicAgeList, + kCreatePublicAge, + kRemovePublicAge, + }; + + PyObject * fPyObject; + NetClientCommCallback( PyObject * pyObject ) + : fPyObject( pyObject ) + { + Py_XINCREF( fPyObject ); + } + ~NetClientCommCallback() + { + Py_XDECREF( fPyObject ); + } + void OperationStarted( UInt32 context ) + {} + void OperationComplete( UInt32 context, int resultCode ) + { + if ( !fPyObject ) + return; + + + + PyObject* func = nil; + + switch ( context ) + { + case kGetPublicAgeList: + // Call the callback. + func = PyObject_GetAttrString( fPyObject, "gotPublicAgeList" ); + if ( func ) + { + if ( PyCallable_Check(func)>0 ) + { + plCreatableStream * ageInfoStream = plCreatableStream::ConvertNoRef( fCbArgs.GetItem( 0 ) ); + plCreatableStream * nPlayersStream = plCreatableStream::ConvertNoRef( fCbArgs.GetItem( 1 ) ); + + if ( ageInfoStream && nPlayersStream ) + { + UInt16 nAgeInfoEntries; + ageInfoStream->GetStream()->ReadSwap( &nAgeInfoEntries ); + + UInt16 nPlayerCountEntries; + nPlayersStream->GetStream()->ReadSwap( &nPlayerCountEntries ); + + hsAssert( nAgeInfoEntries==nPlayerCountEntries, "huh?" ); + + // convert callback args to a list of tuple(ageInfo,nPlayers) + PyObject* pyEL = PyList_New(nAgeInfoEntries); + + for ( int i=0; iGetStream(), nil ); + nPlayersStream->GetStream()->ReadSwap( &nPlayers ); + PyObject* t = PyTuple_New(2); + PyTuple_SetItem(t, 0, pyAgeInfoStruct::New(&ageInfo)); + PyTuple_SetItem(t, 1, PyLong_FromUnsignedLong(nPlayers)); + PyList_SetItem(pyEL, i, t); // steals the ref + } + PyObject* retVal = PyObject_CallMethod(fPyObject, "gotPublicAgeList", "O", pyEL); + Py_XDECREF(retVal); + } + } + } + break; + case kCreatePublicAge: + // Call the callback. + func = PyObject_GetAttrString( fPyObject, "publicAgeCreated" ); + if ( func ) + { + if ( PyCallable_Check(func)>0 ) + { + plAgeInfoStruct * ageInfo = plAgeInfoStruct::ConvertNoRef( fCbArgs.GetItem( 0 ) ); + + if ( ageInfo ) + { + PyObject* ageInfoObj = pyAgeInfoStruct::New(ageInfo); + PyObject* retVal = PyObject_CallMethod(fPyObject, "publicAgeCreated", "O", ageInfoObj); + Py_XDECREF(retVal); + Py_DECREF(ageInfoObj); + } + } + } + break; + case kRemovePublicAge: + // Call the callback. + func = PyObject_GetAttrString( fPyObject, "publicAgeRemoved" ); + if ( func ) + { + if ( PyCallable_Check(func)>0 ) + { + plCreatableUuid * guid = plCreatableUuid::ConvertNoRef( fCbArgs.GetItem( 0 ) ); + + if ( guid ) + { + PyObject* retVal = PyObject_CallMethod(fPyObject, "publicAgeRemoved", "s", guid->AsString()); + Py_XDECREF(retVal); + } + } + } + break; + } + + delete this; + } +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// Function : GetPublicAgeList +// PARAMETERS : ageName, callback object +// +// PURPOSE : Get the list of public ages for the given age name. +// +void cyMisc::GetPublicAgeList( const char * ageName, PyObject * cbObject ) +{ + if (cbObject) + Py_XINCREF(cbObject); + NetCommGetPublicAgeList( + ageName, + cbObject, + plNetCommReplyMsg::kParamTypePython + ); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : CreatePublicAge +// PARAMETERS : ageInfo, callback object +// +// PURPOSE : Add a public age to the list of available ones. +// +void cyMisc::CreatePublicAge( pyAgeInfoStruct * ageInfo, PyObject * cbObject ) +{ + VaultSetOwnedAgePublicAndWait(ageInfo->GetAgeInfo(), true); + // TODO: make the callback here +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : RemovePublicAge +// PARAMETERS : ageInstanceGuid, callback object +// +// PURPOSE : Remove a public age from the list of available ones. +// +void cyMisc::RemovePublicAge( const char * ageInstanceGuid, PyObject * cbObject/*=nil */) +{ + plAgeInfoStruct info; + plUUID uuid(ageInstanceGuid); + info.SetAgeInstanceGuid(&uuid); + VaultSetOwnedAgePublicAndWait(&info, false); + // TODO: make the callback here +} + +int cyMisc::GetKILevel() +{ + int result = pfKIMsg::kNanoKI; + + wchar wStr[MAX_PATH]; + StrToUnicode(wStr, pfKIMsg::kChronicleKILevel, arrsize(wStr)); + if (RelVaultNode * rvn = VaultFindChronicleEntryIncRef(wStr)) { + VaultChronicleNode chron(rvn); + result = _wtoi(chron.entryValue); + rvn->DecRef(); + } + + return result; +} + +////////////////////////////////////////////////////////////////////////////// +// +// the following are for recording and rebuilding the camera stack + +#include "../pfCamera/plCameraModifier.h" + +int cyMisc::GetNumCameras() +{ + return (plVirtualCam1::Instance()->GetNumCameras()); +} + +const char* cyMisc::GetCameraNumber(int number) +{ + plCameraModifier1* pCam = plVirtualCam1::Instance()->GetCameraNumber(number-1); + if (pCam->GetTarget()) + { + const char* ret = pCam->GetTarget()->GetKeyName(); + (ret==nil) ? "empty" : ret; + char str[256]; + sprintf(str, "saving camera named %s to chronicle\n",ret); + plVirtualCam1::Instance()->AddMsgToLog(str); + return ret; + } + plVirtualCam1::Instance()->AddMsgToLog("sending empty to camera chronicle\n"); + return "empty"; +} + +void cyMisc::RebuildCameraStack(const char* name, const char* ageName) +{ + plKey key=nil; + char str[256]; + sprintf(str, "attempting to restore camera named %s from chronicle\n",name); + plVirtualCam1::Instance()->AddMsgToLog(str); + + if (strcmp(name, "empty") == 0) + return; + + if ( name || name[0] != 0) + { + key=plKeyFinder::Instance().StupidSearch(nil,nil,plSceneObject::Index(), name, false); + } + if ( key == nil ) + { + // try and use this new hack method to find it + if (!plVirtualCam1::Instance()->RestoreFromName(name)) + { + // give up and force built in 3rd person + plVirtualCam1::Instance()->PushThirdPerson(); + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s not found",name); + PyErr_SetString(PyExc_NameError, errmsg); + } + } + else + { + // now we have the scene object, look for it's camera modifier + const plCameraModifier1* pMod = nil; + plSceneObject* pObj = plSceneObject::ConvertNoRef(key->ObjectIsLoaded()); + if (pObj) + { + for (int i = 1; i < pObj->GetNumModifiers(); i++) + { + pMod = plCameraModifier1::ConvertNoRef(pObj->GetModifier(i)); + if (pMod) + break; + } + if (pMod) + { + plVirtualCam1::Instance()->RebuildStack(pMod->GetKey()); + return; + } + } + plVirtualCam1::Instance()->PushThirdPerson(); + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s has no camera modifier",name); + PyErr_SetString(PyExc_NameError, errmsg); + } + +} + +void cyMisc::PyClearCameraStack() +{ + plVirtualCam1::Instance()->ClearStack(); +} + +void cyMisc::RecenterCamera() +{ + plCameraMsg* pCam = TRACKED_NEW plCameraMsg; + pCam->SetBCastFlag(plMessage::kBCastByExactType); + pCam->SetCmd(plCameraMsg::kResetPanning); + pCam->Send(); +} + +#include "../plMessage/plTransitionMsg.h" + +void cyMisc::FadeIn(float lenTime, bool holdFlag, bool noSound) +{ + plTransitionMsg *msg = TRACKED_NEW plTransitionMsg( noSound ? plTransitionMsg::kFadeInNoSound : plTransitionMsg::kFadeIn, lenTime, holdFlag ); + plgDispatch::MsgSend( msg ); +} + +void cyMisc::FadeOut(float lenTime, bool holdFlag, bool noSound) +{ + plTransitionMsg *msg = TRACKED_NEW plTransitionMsg( noSound ? plTransitionMsg::kFadeOutNoSound : plTransitionMsg::kFadeOut, lenTime, holdFlag ); + plgDispatch::MsgSend( msg ); +} + +void cyMisc::SetClickability(hsBool b) +{ + plInputIfaceMgrMsg* msg = TRACKED_NEW plInputIfaceMgrMsg(b ? plInputIfaceMgrMsg::kEnableClickables : plInputIfaceMgrMsg::kDisableClickables ); + plgDispatch::MsgSend(msg); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : Debug build only: Assert if condition is false. +// +// PURPOSE : debugging +// +void cyMisc::DebugAssert( bool cond, const char * msg ) +{ + hsAssert( cond, msg ); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : Set a python object to be called back after a certain amount of time. +// +// PURPOSE : script can trigger itself over time w/o having to specify it in the dataset. +// +void cyMisc::SetAlarm( float secs, PyObject * cb, UInt32 cbContext ) +{ + pyAlarmMgr::GetInstance()->SetAlarm( secs, cb, cbContext ); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : Save Screen Shot +// +// PURPOSE : captures the screen and saves it as a jpeg +// +#include "../plJPEG/plJPEG.h" +void cyMisc::SaveScreenShot(const char* fileName, int x, int y, int quality) +{ + if ( cyMisc::GetPipeline() ) + { + if (quality <= 0 || quality > 100) + quality = 75; + + plMipmap mipmap; + cyMisc::GetPipeline()->CaptureScreen( &mipmap, false, x, y ); + + plJPEG::Instance().SetWriteQuality( quality ); + plJPEG::Instance().WriteToFile( fileName, &mipmap ); + } +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : Start a screen capture +// +// PURPOSE : This starts a screen capture in motion. It will be capture on the next +// update and a plCaptureRenderMsg when its ready +// +#include "../plPipeline/plCaptureRender.h" +void cyMisc::StartScreenCapture(pyKey& selfkey) +{ + cyMisc::StartScreenCaptureWH(selfkey, 800, 600); +} + +void cyMisc::StartScreenCaptureWH(pyKey& selfkey, UInt16 width, UInt16 height) +{ + plCaptureRender::Capture(selfkey.getKey(), width, height); +} + + +#include "../plAvatar/plAvatarClothing.h" +void cyMisc::WearMaintainerSuit(pyKey& key, hsBool wear) +{ + // run on all machines, but only affects us if we call it on our local guy (who props it to others himself) + if (key.getKey() != plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + return; + + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avMod) + { + if (wear) + avMod->GetClothingOutfit()->WearMaintainerOutfit(); + else + avMod->GetClothingOutfit()->RemoveMaintainerOutfit(); + } + +} + +void cyMisc::WearDefaultClothing(pyKey& key) +{ + if (key.getKey() != plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + return; + + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avMod) + { + avMod->GetClothingOutfit()->WearDefaultClothing(); + } +} + +void cyMisc::WearDefaultClothingType(pyKey& key, UInt32 type) +{ + if (key.getKey() != plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + return; + + plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avMod) + { + avMod->GetClothingOutfit()->WearDefaultClothingType(type); + } +} + +////////////////////////////////////////////////////////////////////////////// +// +// Function : Fake link to object +// +// PURPOSE : takes an avatar key and an object key and fake-links the avatar +// to that object's position. appears to be a link to other players +// + +void cyMisc::FakeLinkToObject(pyKey& avatar, pyKey& object) +{ + plPseudoLinkEffectMsg* msg = TRACKED_NEW plPseudoLinkEffectMsg; + msg->fAvatarKey = avatar.getKey(); + msg->fLinkObjKey = object.getKey(); + plgDispatch::MsgSend(msg); +} + +void cyMisc::FakeLinkToObjectNamed(const char* name) +{ + plKey key = nil; + if ( name || name[0] != 0) + { + key = plKeyFinder::Instance().StupidSearch(nil,nil,plSceneObject::Index(), name, false); + } + + if (!key) + return; + plPseudoLinkEffectMsg* msg = TRACKED_NEW plPseudoLinkEffectMsg; + msg->fAvatarKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + msg->fLinkObjKey = key; + plgDispatch::MsgSend(msg); +} + +PyObject* cyMisc::LoadAvatarModel(const char* modelName, pyKey& spawnPoint, const char* userStr) +{ + plKey SpawnedKey = plAvatarMgr::GetInstance()->LoadAvatar(modelName, "", false, spawnPoint.getKey(), nil, userStr); + return pyKey::New(SpawnedKey); +} + +void cyMisc::UnLoadAvatarModel(pyKey& avatar) +{ + plAvatarMgr::GetInstance()->UnLoadAvatar(avatar.getKey(), false); +} + +void cyMisc::ForceCursorHidden() +{ + plMouseDevice::HideCursor(true); +} + +void cyMisc::ForceCursorShown() +{ + plMouseDevice::ShowCursor(true); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Function : GetLocalizedString +// +// PURPOSE : Returns the specified localized string with the parameters +// properly replaced (the list is a list of unicode strings) Name +// is in "Age.Set.Name" format +// +std::wstring cyMisc::GetLocalizedString(std::wstring name, const std::vector & arguments) +{ + if (pfLocalizationMgr::InstanceValid()) + return pfLocalizationMgr::Instance().GetString(name, arguments); + return L""; +} + +void cyMisc::EnablePlanarReflections(bool enable) +{ + plDynamicCamMap::SetEnabled(enable); +} + +void cyMisc::GetSupportedDisplayModes(std::vector *res) +{ + fPipeline->GetSupportedDisplayModes(res); +} + +int cyMisc::GetDesktopWidth() +{ + return fPipeline->GetDesktopWidth(); +} + +int cyMisc::GetDesktopHeight() +{ + return fPipeline->GetDesktopHeight(); +} + +int cyMisc::GetDesktopColorDepth() +{ + return fPipeline->GetDesktopColorDepth(); +} + +PipelineParams *cyMisc::GetDefaultDisplayParams() +{ + return fPipeline->GetDefaultParams(); +} + +void cyMisc::SetGraphicsOptions(int Width, int Height, int ColorDepth, hsBool Windowed, int NumAASamples, int MaxAnisotropicSamples, hsBool VSync) +{ + // This has to send a message to plClient because python is loaded in the max plugins + + plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY ); + plClientMsg* clientMsg = TRACKED_NEW plClientMsg(plClientMsg::kResetGraphicsDevice); + clientMsg->AddReceiver(clientKey); + //clientMsg->SetBCastFlag(plMessage::kBCastByType); + clientMsg->fGraphicsSettings.fWidth = Width; + clientMsg->fGraphicsSettings.fHeight = Height; + clientMsg->fGraphicsSettings.fColorDepth = ColorDepth; + clientMsg->fGraphicsSettings.fWindowed = Windowed; + clientMsg->fGraphicsSettings.fNumAASamples = NumAASamples; + clientMsg->fGraphicsSettings.fMaxAnisoSamples = MaxAnisotropicSamples; + clientMsg->fGraphicsSettings.fVSync = VSync; + clientMsg->Send(); + + //plClient::GetInstance()->ResetDisplayDevice(Width, Height, ColorDepth, Windowed, NumAASamples, MaxAnisotropicSamples); +} + +bool cyMisc::DumpLogs(const std::wstring & folder) +{ + char* temp = hsWStringToString(folder.c_str()); + bool retVal = plStatusLogMgr::GetInstance().DumpLogs(temp); + delete [] temp; + return retVal; +} + +bool cyMisc::FileExists(const std::wstring & filename) +{ + return PathDoesFileExist(filename.c_str()); +} + +bool cyMisc::CreateDir(const std::wstring & directory) +{ + return PathCreateDirectory(directory.c_str(), kPathCreateDirFlagEntireTree) == kPathCreateDirSuccess; +} + +std::wstring cyMisc::GetUserPath() +{ + wchar_t path[MAX_PATH]; + PathGetUserDirectory(path, arrsize(path)); + return path; +} + +std::wstring cyMisc::GetInitPath() +{ + wchar_t path[MAX_PATH]; + PathGetInitDirectory(path, arrsize(path)); + return path; +} + +void cyMisc::SetBehaviorNetFlags(pyKey & behKey, hsBool netForce, hsBool netProp) +{ + if (plMultistageBehMod * behMod = plMultistageBehMod::ConvertNoRef(behKey.getKey()->ObjectIsLoaded())) + { + behMod->SetNetForce(netForce); + behMod->SetNetProp(netProp); + } +} + +void cyMisc::SendFriendInvite(const wchar email[], const wchar toName[]) +{ + if (RelVaultNode* pNode = VaultGetPlayerNodeIncRef()) + { + VaultPlayerNode player(pNode); + Uuid inviteUuid = player.inviteUuid; + + // If we don't have an invite UUID set then make a new one + if (GuidIsNil(inviteUuid)) + { + inviteUuid = GuidGenerate(); + player.SetInviteUuid(inviteUuid); + } + + NetCommSendFriendInvite(email, toName, inviteUuid); + pNode->DecRef(); + } +} + +PyObject* cyMisc::PyGuidGenerate() +{ + char guidStr[64]; + Uuid newGuid = GuidGenerate(); + GuidToString(newGuid, guidStr, arrsize(guidStr)); + + return PyString_FromString(guidStr); +} + +PyObject* cyMisc::GetAIAvatarsByModelName(const char* name) +{ + plAvatarMgr::plArmatureModPtrVec armVec; + plAvatarMgr::GetInstance()->FindAllAvatarsByModelName(name, armVec); + + PyObject* avList = PyList_New(0); + + for (plAvatarMgr::plArmatureModPtrVec::iterator it = armVec.begin(); it != armVec.end(); ++it) + { + plArmatureMod* armMod = (*it); + plAvBrainCritter* critterBrain = plAvBrainCritter::ConvertNoRef(armMod->FindBrainByClass(plAvBrainCritter::Index())); + if (critterBrain) + { + PyObject* tuple = PyTuple_New(2); + PyTuple_SetItem(tuple, 0, pyCritterBrain::New(critterBrain)); + PyTuple_SetItem(tuple, 1, PyString_FromString(armMod->GetUserStr())); + + PyList_Append(avList, tuple); + Py_DECREF(tuple); + } + } + + if (PyList_Size(avList) > 0) + return avList; + else + { + Py_DECREF(avList); + PYTHON_RETURN_NONE; + } +} + +void cyMisc::ForceVaultNodeUpdate(unsigned nodeId) +{ + VaultFetchNodesAndWait(&nodeId, 1, true); +} + +void cyMisc::VaultDownload(unsigned nodeId) +{ + VaultDownloadAndWait( + L"PyVaultDownload", + nodeId, + nil, + nil + ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMisc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMisc.h new file mode 100644 index 00000000..4f4e4bd8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMisc.h @@ -0,0 +1,920 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef cyMisc_h +#define cyMisc_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: cyMisc +// +// PURPOSE: Class wrapper to map misc functions, such as the console +// +class pyKey; +class pySceneObject; +class pyPlayer; +class pyImage; +class pyDniCoordinates; +class pyColor; +class pyAgeInfoStruct; +class pyPoint3; + +#include +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "../pnUtils/pnUtils.h" + +class pyGUIDialog; +class plPipeline; +class plDisplayMode; +struct PipelineParams; + +class cyMisc +{ + + // game playing to get the pipeline from the client when we can't really + // this is only for the C++ side + // The pipeline is set in the plClient + static plPipeline* fPipeline; + static UInt32 fUniqueNumber; + static UInt32 fPythonLoggingLevel; + +public: + // periodically do things + static void Update( double secs ); + + // the python definitions + static void AddPlasmaMethods(std::vector &methods); + static void AddPlasmaMethods2(std::vector &methods); + static void AddPlasmaMethods3(std::vector &methods); + static void AddPlasmaMethods4(std::vector &methods); + + static void AddPlasmaConstantsClasses(PyObject *m); + + + static void SetPipeline( plPipeline *pipe ) { fPipeline = pipe; } + static plPipeline *GetPipeline( void ) { return fPipeline; } + + +#if 1 + // + // TEMP SCREEN PRINT CODE FOR NON-DBG TEXT DISPLAY + // +public: + static void PrintToScreen(const char* msg); +#endif + + enum PythonDebugPrintLevels + { + kNoLevel = 0, + kDebugDump = 1, + kWarningLevel = 2, + kErrorLevel = 3, + kAssertLevel = 4 + }; + + enum LOSObjectType + { + kClickables, + kCameraBlockers, + kCustom, + kShootable + }; + + static UInt32 GetPythonLoggingLevel(); + static void SetPythonLoggingLevel(UInt32 new_level); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : Console + // PARAMETERS : command - string of console commmand to execute + // + // PURPOSE : Execute a console command from a python script, + // optionally propagate over the net + // + static void Console(const char* command); + static void ConsoleNet(const char* command, hsBool netForce); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : FindSceneObject + // PARAMETERS : name - string of name of the sceneobject + // : ageName - string of the name of the age to look in + // + // PURPOSE : Execute a console command from a python script, + // optionally propagate over the net + // + static PyObject* FindSceneObject(const char* name, const char* ageName); // returns pySceneObject + static PyObject* FindActivator(const char* name); // returns pyKey + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : PopUpConsole + // PARAMETERS : command - string of console commmand to execute + // + // PURPOSE : Execute a console command from a python script + // + static void PopUpConsole(const char* command); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : TimerCallback + // PARAMETERS : command - string of console commmand to execute + // + // PURPOSE : Execute a console command from a python script + // + static void TimerCallback(pyKey& selfkey, hsScalar time, UInt32 id); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : ClearTimerCallbacks + // PARAMETERS : key and (optional) timer id + // + // PURPOSE : clears timer callbacks + // + static void ClearTimerCallbacks(pyKey& selfkey); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : AttachObject + // PARAMETERS : child object + // : to be attached to parent object + // + // PURPOSE : Attach an object to another object, knowing only their pyKeys + // + static void AttachObject(pyKey& ckey, pyKey& pkey); + static void AttachObjectSO(pySceneObject& cobj, pySceneObject& pobj); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : DetachObject + // PARAMETERS : child object + // : to be attached to parent object + // + // PURPOSE : Attach an object to another object, knowing only their pyKeys + // + static void DetachObject(pyKey& ckey, pyKey& pkey); + static void DetachObjectSO(pySceneObject& cobj, pySceneObject& pobj); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : LinkToAge + // PARAMETERS : + // + // PURPOSE : LinkToAge + // + // STATUS : Depreciated. Use plNetLinkingMgr or pyNetLinkingMgr instead. + // +/// static void LinkToAge(pyKey &selfkey, const char *AgeName,const char *SpawnPointName); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SetDirtySyncStateServer + // PARAMETERS : + // + // PURPOSE : set the Python modifier to be dirty and asked to be saved out + // + static void SetDirtySyncState(pyKey &selfkey, const char* SDLStateName, UInt32 sendFlags); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SetDirtySyncStateClients + // PARAMETERS : + // + // PURPOSE : set the Python modifier to be dirty and asked to be saved out + // specifies that state should be sent to other clients as well as server + // + static void SetDirtySyncStateWithClients(pyKey &selfkey, const char* SDLStateName, UInt32 sendFlags); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : EnableControlKeyEvents & DisableControlKeyEvents + // PARAMETERS : none + // + // PURPOSE : register and unregister for control key events + // + static void EnableControlKeyEvents(pyKey &selfkey); + static void DisableControlKeyEvents(pyKey &selfkey); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetClientName + // PARAMETERS : avatar key + // + // PURPOSE : Return the net client (account) name of the player whose avatar + // key is provided. + // + static hsBool WasLocallyNotified(pyKey &selfkey); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetClientName + // PARAMETERS : avatar key + // + // PURPOSE : Return the net client (account) name of the player whose avatar + // key is provided. + // + static const char* GetClientName(pyKey &avKey); + + static PyObject* cyMisc::GetAvatarKeyFromClientID(int clientID); // returns pyKey + static int cyMisc::GetLocalClientID(); + static int cyMisc::GetClientIDFromAvatarKey(pyKey& avatar); + static hsBool cyMisc::ValidateKey(pyKey& key); + + + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetClientName + // PARAMETERS : + // + // PURPOSE : Return the local net client (account) name + // + static const char* GetLocalClientName(); + + + // + // Get Current age information - DEPRECIATED. Use ptDniInfoSource() object instead. + // + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetAgeName + // Function : GetAgeTime + // PARAMETERS : + // + // PURPOSE : Return the age name of the current age the local player is in + // : Return the current coordinates of the player within this age + // : Return the current time with the current age the player is in + // + static const char* GetAgeName(); + static PyObject* GetAgeInfo(); // returns pyAgeInfoStruct + static const char* GetPrevAgeName(); + static PyObject* GetPrevAgeInfo(); + // current time in current age + static UInt32 GetAgeTime( void ); + static UInt32 GetDniTime(void); + static UInt32 ConvertGMTtoDni(UInt32 time); + static UInt32 GetServerTime( void ); // returns the current server time in GMT + static float GetAgeTimeOfDayPercent(void); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : ExcludeRegionSet + // PARAMETERS : key - of the exclude region, ie. where to send the message + // state - what state of to set at: + // + // PURPOSE : Sets the state of an exclude region + // + enum + { + kExRegRelease = 0, + kExRegClear = 1, + }; + static void ExcludeRegionSet(pyKey& sender, pyKey& exKey, UInt16 state); + static void ExcludeRegionSetNow(pyKey& sender, pyKey& exKey, UInt16 state); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetSeconds + // PARAMETERS : + // + // PURPOSE : Return the nunber of seconds elapsed + // + static double GetSeconds(); + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetSysSeconds + // PARAMETERS : + // + // PURPOSE : Return the number of system seconds elapsed + // + static double GetSysSeconds(); + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetDelSysSeconds + // PARAMETERS : + // + // PURPOSE : Return the frame delta seconds + // + static hsScalar GetDelSysSeconds(); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : LoadDialog + // PARAMETERS : + // + // PURPOSE : Loads the dialog by name + // : optionally sets the receiver key for the GUINotifyMsg + // + static void LoadDialog(const char* name); + static void LoadDialogK(const char* name, pyKey& modKey); + static void LoadDialogKA(const char* name, pyKey& rKey, const char* ageName); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : UnLoadDialog + // PARAMETERS : + // + // PURPOSE : UnLoads the dialog by name + // : optionally sets the receiver key for the GUINotifyMsg + // + static void UnloadDialog(const char* name); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : IsDialogLoaded + // PARAMETERS : + // + // PURPOSE : Test to see if a dialog is loaded (according to the dialog manager) + // + static hsBool IsDialogLoaded(const char* name); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : ShowDialog + // Function : HideDialog + // PARAMETERS : + // + // PURPOSE : Show or Hide a dialog by name + // + static void ShowDialog(const char* name); + static void HideDialog(const char* name); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetDialogFromTagID + // PARAMETERS : + // + // PURPOSE : Return the frame delta seconds + // + static PyObject* GetDialogFromTagID(UInt32 tag); // returns pyGUIDialog + static PyObject* GetDialogFromString(const char* name); // returns pyGUIDialog + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : IsGUIModal + // PARAMETERS : + // + // PURPOSE : Returns true if the GUI is currently modal (and therefore blocking input) + // + static bool IsGUIModal(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetLocalAvatar + // PARAMETERS : + // + // PURPOSE : Return a pySceneobject of the local Avatar + // + static PyObject* GetLocalAvatar(); // returns pySceneObject + static PyObject* GetLocalPlayer(); // returns pyPlayer + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetPlayerList + // Function : GetPlayerListDistanceSorted + // PARAMETERS : + // + // PURPOSE : Get a list of players (other than self) that are playing the game + // : optionally get it sorted by distance + // + // RETURNS : Python object list of pyPlayer s + // + static std::vector GetPlayerList(); // list of pyPlayer + static std::vector GetPlayerListDistanceSorted(); // list of pyPlayer + + static UInt32 GetMaxListenListSize(); + static hsScalar GetMaxListenDistSq(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SendRTChat + // PARAMETERS : from - is a pyPlayer of the person who is sending this + // : tolist - is a python list object, if empty then broadcast message + // : message - text string to send to others + // : flags - the flags of destiny... whatever that means + // + // PURPOSE : To send a real time chat message to a particualr user, a list of users + // : or broadcast it to everyone (within hearing distance?) + // + // RETURNS : the flags that were sent with the message (may be modified) + // + static UInt32 SendRTChat(pyPlayer& from, const std::vector & tolist, const char* message, UInt32 flags); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SendKIMessage + // PARAMETERS : command - the command type + // : value - extra value + // + // PURPOSE : Send message to the KI, to tell it things to do + // + // RETURNS : nothing + // + static void SendKIMessage(UInt32 command, hsScalar value); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SendKIMessageS + // PARAMETERS : command - the command type + // : value - extra value as a string + // + // PURPOSE : Send message to the KI, to tell it things to do + // + // RETURNS : nothing + // + static void SendKIMessageS(UInt32 command, const wchar_t* value); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SendKIMessageI + // PARAMETERS : command - the command type + // : value - extra value as an Int32 + // + // PURPOSE : Send message to the KI, to tell it things to do + // + // RETURNS : nothing + // + static void SendKIMessageI(UInt32 command, Int32 value); + static void SendKIGZMarkerMsg(Int32 markerNumber, pyKey& sender); + static void SendKIRegisterImagerMsg(const char* imagerName, pyKey& sender); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : YesNoDialog + // PARAMETERS : sender - sender's key (to get the reply) + // : value - extra value + // + // PURPOSE : Send message to the KI, to tell it things to do + // + // RETURNS : nothing + // + static void YesNoDialog(pyKey& sender, const char* thestring); + static void YesNoDialog(pyKey& sender, std::wstring thestring); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : RateIt + // PARAMETERS : chonicleName - where to store the rating + // : thestring - the message in the RateIt dialog + // : onceFlag - flag to tell whether this is a onetime thing or ask everytime + // + // PURPOSE : Send message to the KI to tell it to ask the user to Rate something + // + // RETURNS : nothing + // + static void RateIt(const char* chronicleName, const char* thestring, hsBool onceFlag); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SetPrivateChatList + // PARAMETERS : key - who's joining + // + // PURPOSE : Lock the local avatar into private vox messaging, and / or add new memebers to his chat list + // + // RETURNS : nothing + // + static void SetPrivateChatList(const std::vector & tolist); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : RemovePrivateChatMember + // PARAMETERS : key - who's joining + // + // PURPOSE : Remove the local avatar from private vox messaging, and / or clear memebers from his chat list + // + // RETURNS : nothing + // + static void ClearPrivateChatList(pyKey& member); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : ClearCameraStack + // PARAMETERS : + // + // PURPOSE : knocks all the cameras off the current stack + // + static void cyMisc::ClearCameraStack(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SendPetitionToCCR + // PARAMETERS : message - message to send to the CCR ("please help me!") + // + // PURPOSE : Send a petition to the CCR for help or questions + // + static void SendPetitionToCCR(const char* message); + static void SendPetitionToCCRI(const char* message, UInt8 reason,const char* title); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : SendChatToCCR + // PARAMETERS : message - message to send to the CCR ("please help me!") + // + // PURPOSE : Send a petition to the CCR for help or questions + // + static void SendChatToCCR(const char* message,Int32 CCRPlayerID); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetNumRemotePlayers + // + // PURPOSE : return the number of remote players connected + // + static int GetNumRemotePlayers(); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : Paging functions + // PARAMETERS : nodeName - name of the page to load + // + // PURPOSE : page in, or out a paritcular node + // + static void PageInNodes(const std::vector & nodeNames, const char* age); + static void PageOutNode(const char* nodeName); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : LimitAvatarLOD + // PARAMETERS : LODlimit - number of to limit the LOD to + // + // PURPOSE : sets the avatar LOD limit + // + static void LimitAvatarLOD(int LODlimit); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : Set fog default functions + // PARAMETERS : floats - the parameters + // + // PURPOSE : sets the fog defaults + // + static void FogSetDefColor(pyColor& color); + static void FogSetDefLinear(float start, float end, float density); + static void FogSetDefExp(float end, float density); + static void FogSetDefExp2(float end, float density); + static void SetClearColor(float red, float green, float blue); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : Enable / disable cursor fade for avatar + // PARAMETERS : + // + // PURPOSE : turns avatar fade out on / off + // + static void EnableAvatarCursorFade(); + static void DisableAvatarCursorFade(); + static void FadeLocalPlayer(hsBool fade); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : Put the interface into 'offer book mode' + // PARAMETERS : + // + // PURPOSE : + // + static void EnableOfferBookMode(pyKey& selfkey, const char* ageFileName, const char* ageInstanceName); + static void DisableOfferBookMode(); + static void NotifyOffererPublicLinkAccepted(UInt32 offerer); + static void NotifyOffererPublicLinkRejected(UInt32 offerer); + static void NotifyOffererPublicLinkCompleted(UInt32 offerer); + static void ToggleAvatarClickability(hsBool on); + static void SetShareSpawnPoint(const char* spawnPoint); + static void SetShareAgeInstanceGuid(const Uuid& guid); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : IsCCRAwayStatus + // PARAMETERS : + // + // PURPOSE : Returns current status of CCR dept + // + static hsBool IsCCRAwayStatus(); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : AmCCR + // PARAMETERS : + // + // PURPOSE : Returns true if local player is a CCR + // + static hsBool AmCCR(); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : AcceptInviteInGame + // PARAMETERS : Friend's Name, Invite Key + // + // PURPOSE : Send's a VaultTask to the server to perform the invite + // + static void AcceptInviteInGame(const char* friendName, const char* inviteKey); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : GetLanguage + // PARAMETERS : + // + // PURPOSE : Returns the current language the game is in + // + static int GetLanguage(); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : UsingUnicode + // PARAMETERS : + // + // PURPOSE : Returns true if the current language uses Unicode (like Japanese) + // + static bool UsingUnicode(); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : RequestLOSScreen + // PARAMETERS : lots... + // + // PURPOSE : To request an LOS from a point on the screen + // + static bool RequestLOSScreen(pyKey &selfkey, Int32 ID, hsScalar xPos, hsScalar yPos, hsScalar distance, int what, int reportType); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : CheckVisLOS + // PARAMETERS : StartPoint, EndPoint + // + // PURPOSE : Check is there is something visible in the path from StartPoint to EndPoint + // + static PyObject* CheckVisLOS(pyPoint3 startPoint, pyPoint3 endPoint); + static PyObject* CheckVisLOSFromCursor(); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : particle system management + // + static void TransferParticlesToKey(pyKey& fromKey, pyKey& toKey, int numParticles); + static void SetParticleDissentPoint(float x, float y, float z, pyKey& particles); + static void SetParticleOffset(float x, float y, float z, pyKey& particles); + static void KillParticles(float time, float pct, pyKey& particles); + static int GetNumParticles(pyKey& host); + static void SetLightColorValue(pyKey& light, std::string lightName, hsScalar r, hsScalar g, hsScalar b, hsScalar a); + static void SetLightAnimationOn(pyKey& light, std::string lightName, hsBool start); + ////////////////////////////////////////////////////////////////////////////// + // + // Function : RegisterForControlEventMessages + // PARAMETERS : switch on or off, registrant + // + // PURPOSE : let you get control event messages at will (for pseudo-GUI's like the psnl bookshelf or clft imager) + + static void RegisterForControlEventMessages(hsBool on, pyKey& k); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : IsSinglePlayerMode + // And : IsDemoMode + // PARAMETERS : + // + // PURPOSE : Returns whether the game is in Single Player mode + // And : returns whether the game in in Demo mode + // + static bool IsSinglePlayerMode(); + static bool IsDemoMode(); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : IsInternalRelease + // PARAMETERS : + // + // PURPOSE : Returns true if we are running an internal build + // + static bool IsInternalRelease(); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : IsEnterChatModeKeyBound + // PARAMETERS : + // + // PURPOSE : Returns whether the EnterChatMode is bound to a key + // + static bool IsEnterChatModeKeyBound(); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : ShootBulletFromScreen + // PARAMETERS : lots... + // + // PURPOSE : Shoots from screen coordinates, a bullet and makes a mark on objects that know about bullet holes + // + static void ShootBulletFromScreen(pyKey &selfkey, hsScalar xPos, hsScalar yPos, hsScalar radius, hsScalar range); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : ShootBulletFromObject + // PARAMETERS : lots... + // + // PURPOSE : Shoots from an object, a bullet and makes a mark on objects that know about bullet holes + // + static void ShootBulletFromObject(pyKey &selfkey, pySceneObject* sobj, hsScalar radius, hsScalar range); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : GetPublicAgeList + // PARAMETERS : ageName, callback object + // + // PURPOSE : Get the list of public ages for the given age name. + // + static void GetPublicAgeList( const char * ageName, PyObject * cbObject=nil ); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : CreatePublicAge + // PARAMETERS : ageInfo, callback object + // + // PURPOSE : Add a public age to the list of available ones. + // + static void CreatePublicAge( pyAgeInfoStruct * ageInfo, PyObject * cbObject=nil ); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : RemovePublicAge + // PARAMETERS : ageInstanceGuid, callback object + // + // PURPOSE : Remove a public age from the list of available ones. + // + static void RemovePublicAge( const char * ageInstanceGuid, PyObject * cbObject=nil ); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : GetKILevel + // + // PURPOSE : returns the ki level of the local player + // + static int GetKILevel(); // return local player's ki level + + ////////////////////////////////////////////////////////////////////////////// + // + // + // PURPOSE : these functions are for saving / restoring the camera stack + // + + static int GetNumCameras(); + static const char* GetCameraNumber(int number); + static void RebuildCameraStack(const char* name, const char* ageName); + static void PyClearCameraStack(); + static void RecenterCamera(); + static bool IsFirstPerson(); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : FadeIn and FadeOut + // + // PURPOSE : fade screen in and out + // + static void FadeIn(float lenTime, bool holdFlag, bool noSound = 0); + static void FadeOut(float lenTime, bool holdFlag, bool noSound = 0); + + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : Disable / Enable global clickability + // + // PURPOSE : globally enable or disable ALL clickables on this local client + // + static void SetClickability(hsBool b); + + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : Debug build only: Assert if condition is false. + // + // PURPOSE : debugging + // + static void DebugAssert( bool cond, const char * msg ); + + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : Set a python object to be called back after a certain amount of time. + // Python object should have this method: def onAlarm(self,context) + // + // PURPOSE : script can trigger itself over time w/o having to specify it in the dataset. + // + static void SetAlarm( float secs, PyObject * cb, UInt32 cbContext ); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : Save Screen Shot + // + // PURPOSE : captures the screen and saves it as a jpeg + // + static void SaveScreenShot(const char* fileName, int x = 640, int y = 480, int quality = 75); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : Start a screen capture + // + // PURPOSE : This starts a screen capture in motion. It will be capture on the next + // update and a plCaptureRenderMsg when its ready + // + static void StartScreenCapture(pyKey& selfkey); + static void StartScreenCaptureWH(pyKey& selfkey, UInt16 width, UInt16 height); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : wear maintainer suit + // + // PURPOSE : puts the maintainer suit on or off the avatar + // + static void WearMaintainerSuit(pyKey& key, hsBool wear); + + static void WearDefaultClothing(pyKey& key); + static void WearDefaultClothingType(pyKey& key, UInt32 type); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : Fake link to object + // + // PURPOSE : takes an avatar key and an object key and fake-links the avatar + // to that object's position. appears to be a link to other players + // + static void FakeLinkToObject(pyKey& avatar, pyKey& object); + static void FakeLinkToObjectNamed(const char* name); + + ////////////////////////////////////////////////////////////////////////////// + // + // Function : Spawn an avatar + // + // PURPOSE : takes the name of an avatar model and a sceneobject key and + // spawns the avatar at that point + // + static PyObject* LoadAvatarModel(const char* modelName, pyKey& object, const char* userStr); // returns pyKey + static void UnLoadAvatarModel(pyKey& avatar); + + /////////////////////////////////////////////////////////////////////////////// + // + // Function : Hide/show the mouse cursor + // + // PURPOSE : forces the mouse cursor to hide/show, overrides pretty much + // everything and should only be used if "normal" methods don't + // work (like ptGUIMouseOff) + static void ForceCursorHidden(); + static void ForceCursorShown(); + + /////////////////////////////////////////////////////////////////////////////// + // + // Function : GetLocalizedString + // + // PURPOSE : Returns the specified localized string with the parameters + // properly replaced (the list is a list of unicode strings) Name + // is in "Age.Set.Name" format + // + static std::wstring GetLocalizedString(std::wstring name, const std::vector & arguments); + + static void EnablePlanarReflections(bool enable = true); + static void SetGraphicsOptions(int Width, int Height, int ColorDepth, hsBool Windowed, int NumAASamples, int MaxAnisotropicSamples, hsBool VSync); + static void GetSupportedDisplayModes(std::vector *res); + static int GetDesktopWidth(); + static int GetDesktopHeight(); + static int GetDesktopColorDepth(); + static PipelineParams *GetDefaultDisplayParams(); + + static bool DumpLogs(const std::wstring & folder); + + static bool FileExists(const std::wstring & filename); + static bool CreateDir(const std::wstring & directory); + + static std::wstring GetUserPath(); + static std::wstring GetInitPath(); + + static void SetBehaviorNetFlags(pyKey & behKey, hsBool netForce, hsBool netProp); + static void SendFriendInvite(const wchar email[], const wchar toName[]); + static PyObject* PyGuidGenerate(); + static PyObject* GetAIAvatarsByModelName(const char* name); + static void ForceVaultNodeUpdate(unsigned nodeId); + static void VaultDownload(unsigned nodeId); +}; + +#endif // cyMisc_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp new file mode 100644 index 00000000..fd77b81c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue.cpp @@ -0,0 +1,522 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyMisc.h" +#include "pyGlueHelpers.h" +#include "pyKey.h" +#include "pyPlayer.h" + +#include "hsUtils.h" + +#include + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetAgeName, "DEPRECIATED - use ptDniInfoSource instead") +{ + return PyString_FromString(cyMisc::GetAgeName()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetAgeInfo, "Returns ptAgeInfoStruct of the current Age") +{ + return cyMisc::GetAgeInfo(); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetAgeTime, "DEPRECIATED - use ptDniInfoSource instead") +{ + return PyLong_FromUnsignedLong(cyMisc::GetAgeTime()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetPrevAgeName, "Returns filename of previous age visited") +{ + return PyString_FromString(cyMisc::GetPrevAgeName()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetPrevAgeInfo, "Returns ptAgeInfoStruct of previous age visited") +{ + return cyMisc::GetPrevAgeInfo(); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetDniTime, "Returns current D'Ni time") +{ + return PyLong_FromUnsignedLong(cyMisc::GetDniTime()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetServerTime, "Returns the current time on the server (which is GMT)") +{ + return PyLong_FromUnsignedLong(cyMisc::GetServerTime()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGMTtoDniTime, args, "Params: gtime\nConverts GMT time (passed in) to D'Ni time") +{ + unsigned long gtime; + if (!PyArg_ParseTuple(args, "l", >ime)) + { + PyErr_SetString(PyExc_TypeError, "PtGMTtoDniTime expects a long"); + PYTHON_RETURN_ERROR; + } + return PyLong_FromUnsignedLong(cyMisc::ConvertGMTtoDni(gtime)); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetClientName, args, "Params: avatarKey=None\nThis will return the name of the client that is owned by the avatar\n" + "- avatarKey is the ptKey of the avatar to get the client name of.\n" + "If avatarKey is omitted then the local avatar is used") +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "|O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtGetClientName expects an optional ptKey"); + PYTHON_RETURN_ERROR; + } + if (keyObj != NULL) + { + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtGetClientName expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + return PyString_FromString(cyMisc::GetClientName(*key)); + } + else + return PyString_FromString(cyMisc::GetLocalClientName()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetLocalAvatar, "This will return a ptSceneobject of the local avatar\n" + "- if there is no local avatar a NameError exception will happen.") +{ + return cyMisc::GetLocalAvatar(); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetLocalPlayer, "Returns a ptPlayer object of the local player") +{ + return cyMisc::GetLocalPlayer(); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetPlayerList, "Returns a list of ptPlayer objects of all the remote players") +{ + std::vector playerList = cyMisc::GetPlayerList(); + PyObject* retVal = PyList_New(playerList.size()); + for (int i = 0; i < playerList.size(); i++) + PyList_SetItem(retVal, i, playerList[i]); // steals the ref + return retVal; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetPlayerListDistanceSorted, "Returns a list of ptPlayers, sorted by distance") +{ + std::vector playerList = cyMisc::GetPlayerListDistanceSorted(); + PyObject* retVal = PyList_New(playerList.size()); + for (int i = 0; i < playerList.size(); i++) + PyList_SetItem(retVal, i, playerList[i]); // steals the ref + return retVal; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtMaxListenListSize, "Returns the maximum listen number of players") +{ + return PyLong_FromUnsignedLong(cyMisc::GetMaxListenListSize()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtMaxListenDistSq, "Returns the maximum distance (squared) of the listen range") +{ + return PyFloat_FromDouble(cyMisc::GetMaxListenDistSq()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetAvatarKeyFromClientID, args, "Params: clientID\nFrom an integer that is the clientID, find the avatar and return its ptKey") +{ + int clientID; + if (!PyArg_ParseTuple(args, "i", &clientID)) + { + PyErr_SetString(PyExc_TypeError, "PtGetAvatarKeyFromClientID expects an integer"); + PYTHON_RETURN_ERROR; + } + return cyMisc::GetAvatarKeyFromClientID(clientID); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetClientIDFromAvatarKey, args, "Params: avatarKey\nFrom a ptKey that points at an avatar, return the players clientID (integer)") +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtGetClientIDFromAvatarKey expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtGetClientIDFromAvatarKey expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey *key = pyKey::ConvertFrom(keyObj); + return PyInt_FromLong(cyMisc::GetClientIDFromAvatarKey(*key)); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetNumRemotePlayers, "Returns the number of remote players in this Age with you.") +{ + return PyInt_FromLong(cyMisc::GetNumRemotePlayers()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtValidateKey, args, "Params: key\nReturns true(1) if 'key' is valid and loaded,\n" + "otherwise returns false(0)") +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtValidateKey expects an object"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PYTHON_RETURN_BOOL(false); + } + pyKey* key = pyKey::ConvertFrom(keyObj); + PYTHON_RETURN_BOOL(cyMisc::ValidateKey(*key)); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSendRTChat, args, "Params: fromPlayer,toPlayerList,message,flags\nSends a realtime chat message to the list of ptPlayers\n" + "If toPlayerList is an empty list, it is a broadcast message") +{ + PyObject* fromPlayerObj = NULL; + PyObject* toPlayerListObj = NULL; + char* message = NULL; + unsigned long msgFlags; + if (!PyArg_ParseTuple(args, "OOsl", &fromPlayerObj, &toPlayerListObj, &message, &msgFlags)) + { + PyErr_SetString(PyExc_TypeError, "PtSendRTChat expects a ptPlayer, a list of ptPlayers, a string, and a long"); + PYTHON_RETURN_ERROR; + } + if (!pyPlayer::Check(fromPlayerObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSendRTChat expects a ptPlayer, a list of ptPlayers, a string, and a long"); + PYTHON_RETURN_ERROR; + } + + pyPlayer* fromPlayer = pyPlayer::ConvertFrom(fromPlayerObj); + + std::vector toPlayerList; + if (PyList_Check(toPlayerListObj)) + { + int listSize = PyList_Size(toPlayerListObj); + for (int i = 0; i < listSize; i++) + { + PyObject* listItem = PyList_GetItem(toPlayerListObj, i); + if (!pyPlayer::Check(listItem)) + { + PyErr_SetString(PyExc_TypeError, "PtSendRTChat expects a ptPlayer, a list of ptPlayers, a string, and a long"); + PYTHON_RETURN_ERROR; + } + toPlayerList.push_back(pyPlayer::ConvertFrom(listItem)); + } + } + else + { + PyErr_SetString(PyExc_TypeError, "PtSendRTChat expects a ptPlayer, a list of ptPlayers, a string, and a long"); + PYTHON_RETURN_ERROR; + } + + return PyLong_FromUnsignedLong(cyMisc::SendRTChat(*fromPlayer, toPlayerList, message, msgFlags)); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSendKIMessage, args, "Params: command,value\nSends a command message to the KI frontend.\n" + "See PlasmaKITypes.py for list of commands") +{ + unsigned long command; + PyObject* val; + if (!PyArg_ParseTuple(args, "lO", &command, &val)) + { + PyErr_SetString(PyExc_TypeError, "PtSendKIMessage expects a long and either a float or a string"); + PYTHON_RETURN_ERROR; + } + if (PyString_Check(val)) + { + char* strValue = PyString_AsString(val); + wchar_t* temp = hsStringToWString(strValue); + cyMisc::SendKIMessageS(command, temp); + delete [] temp; + } + else if (PyUnicode_Check(val)) + { + int len = PyUnicode_GetSize(val); + wchar_t* buffer = TRACKED_NEW wchar_t[len + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)val, buffer, len); + buffer[len] = L'\0'; + cyMisc::SendKIMessageS(command, buffer); + delete [] buffer; + } + else if (PyFloat_Check(val)) + { + float floatValue = (float)PyFloat_AsDouble(val); + cyMisc::SendKIMessage(command, floatValue); + } + else if (PyInt_Check(val)) + { + // accepting an int if people get lazy + float floatValue = (float)PyInt_AsLong(val); + cyMisc::SendKIMessage(command, floatValue); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtSendKIMessage expects a long and either a float or a string"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSendKIMessageInt, args, "Params: command,value\nSame as PtSendKIMessage except the value is guaranteed to be a UInt32\n" + "(for things like player IDs)") +{ + unsigned long command; + long val; + if (!PyArg_ParseTuple(args, "ll", &command, &val)) + { + PyErr_SetString(PyExc_TypeError, "PtSendKIMessageInt expects two longs"); + PYTHON_RETURN_ERROR; + } + cyMisc::SendKIMessageI(command, val); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtLoadAvatarModel, args, "Params: modelName, spawnPoint, userStr = \"\"\nLoads an avatar model at the given spawn point. Assigns the user specified string to it.") +{ + char* modelName; + PyObject* keyObj = NULL; + PyObject* userStrObj = NULL; + if (!PyArg_ParseTuple(args, "sO|O", &modelName, &keyObj, &userStrObj)) + { + PyErr_SetString(PyExc_TypeError, "PtLoadAvatarModel expects a string, a ptKey, and an optional string"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtLoadAvatarModel expects a string, a ptKey, and an optional string"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + + std::string userStr = ""; + // convert name from a string or unicode string + if (userStrObj) + { + if (PyUnicode_Check(userStrObj)) + { + int len = PyUnicode_GetSize(userStrObj); + wchar_t* buffer = TRACKED_NEW wchar_t[len + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)userStrObj, buffer, len); + buffer[len] = L'\0'; + char* temp = hsWStringToString(buffer); + delete [] buffer; + userStr = temp; + delete [] temp; + } + else if (PyString_Check(userStrObj)) + userStr = PyString_AsString(userStrObj); + else + { + PyErr_SetString(PyExc_TypeError, "PtLoadAvatarModel expects a string, a ptKey, and an optional string"); + PYTHON_RETURN_ERROR; + } + } + + return cyMisc::LoadAvatarModel(modelName, *key, userStr.c_str()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtUnLoadAvatarModel, args, "Params: avatarKey\nUnloads the specified avatar model") +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtUnLoadAvatarModel expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtUnLoadAvatarModel expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::UnLoadAvatarModel(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtForceCursorHidden, cyMisc::ForceCursorHidden, "Forces the cursor to hide, overriding everything.\n" + "Only call if other methods won't work. The only way to show the cursor after this call is PtForceMouseShown()") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtForceCursorShown, cyMisc::ForceCursorShown, "Forces the cursor to show, overriding everything.\n" + "Only call if other methods won't work. This is the only way to show the cursor after a call to PtForceMouseHidden()") + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetLocalizedString, args, "Params: name, arguments=None\nReturns the localized string specified by name " + "(format is Age.Set.Name) and substitutes the arguments in the list of strings passed in as arguments.") +{ + PyObject* nameObj = NULL; + PyObject* argObj = NULL; + if (!PyArg_ParseTuple(args, "O|O", &nameObj, &argObj)) + { + PyErr_SetString(PyExc_TypeError, "PtGetLocalizedString expects a unicode string and a list of unicode strings"); + PYTHON_RETURN_ERROR; + } + std::wstring name; + std::vector argList; + + // convert name from a string or unicode string + if (PyUnicode_Check(nameObj)) + { + int len = PyUnicode_GetSize(nameObj); + wchar_t* buffer = TRACKED_NEW wchar_t[len + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)nameObj, buffer, len); + buffer[len] = L'\0'; + name = buffer; + delete [] buffer; + } + else if (PyString_Check(nameObj)) + { + char* temp = PyString_AsString(nameObj); + wchar_t* wTemp = hsStringToWString(temp); + name = wTemp; + delete [] wTemp; + } + else + { + PyErr_SetString(PyExc_TypeError, "PtGetLocalizedString expects a unicode string and a list of unicode strings"); + PYTHON_RETURN_ERROR; + } + + if (argObj != NULL) // NULL is valid... but won't fill the args vector + { + // convert args from a list of strings or unicode strings + if (!PyList_Check(argObj)) + { + PyErr_SetString(PyExc_TypeError, "PtGetLocalizedString expects a unicode string and a list of unicode strings"); + PYTHON_RETURN_ERROR; + } + + int len = PyList_Size(argObj); + for (int curItem = 0; curItem < len; curItem++) + { + PyObject* item = PyList_GetItem(argObj, curItem); + std::wstring arg = L"INVALID ARG"; + if (item == Py_None) // none is allowed, but treated as a blank string + arg = L""; + else if (PyUnicode_Check(item)) + { + int strLen = PyUnicode_GetSize(item); + wchar_t* buffer = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)item, buffer, strLen); + buffer[strLen] = L'\0'; + arg = buffer; + delete [] buffer; + } + else if (PyString_Check(item)) + { + char* temp = PyString_AsString(item); + wchar_t* wTemp = hsStringToWString(temp); + arg = wTemp; + delete [] wTemp; + } + // everything else won't throw an error, but will show up as INVALID ARG in the string + argList.push_back(arg); + } + } + + std::wstring retVal = cyMisc::GetLocalizedString(name, argList); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtDumpLogs, args, "Params: folder\nDumps all current log files to the specified folder (a sub-folder to the log folder)") +{ + PyObject* folderObj = NULL; + if (!PyArg_ParseTuple(args, "O|O", &folderObj)) + { + PyErr_SetString(PyExc_TypeError, "PtDumpLogs expects a unicode or standard string"); + PYTHON_RETURN_ERROR; + } + + // convert folder from a string or unicode string + if (PyUnicode_Check(folderObj)) + { + int len = PyUnicode_GetSize(folderObj); + wchar_t* buffer = TRACKED_NEW wchar_t[len + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)folderObj, buffer, len); + buffer[len] = L'\0'; + bool retVal = cyMisc::DumpLogs(buffer); + delete [] buffer; + PYTHON_RETURN_BOOL(retVal); + } + else if (PyString_Check(folderObj)) + { + char* temp = PyString_AsString(folderObj); + wchar_t* wTemp = hsStringToWString(temp); + bool retVal = cyMisc::DumpLogs(wTemp); + delete [] wTemp; + PYTHON_RETURN_BOOL(retVal); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtDumpLogs expects a unicode or standard string"); + PYTHON_RETURN_ERROR; + } +} + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaMethods - the python method definitions +// + +void cyMisc::AddPlasmaMethods(std::vector &methods) +{ + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetAgeName); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetAgeInfo); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetAgeTime); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetPrevAgeName); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetPrevAgeInfo); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetDniTime); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetServerTime); + PYTHON_GLOBAL_METHOD(methods, PtGMTtoDniTime); + + PYTHON_GLOBAL_METHOD(methods, PtGetClientName); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetLocalAvatar); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetLocalPlayer); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetPlayerList); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetPlayerListDistanceSorted); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtMaxListenListSize); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtMaxListenDistSq); + PYTHON_GLOBAL_METHOD(methods, PtGetAvatarKeyFromClientID); + PYTHON_GLOBAL_METHOD(methods, PtGetClientIDFromAvatarKey); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetNumRemotePlayers); + + PYTHON_GLOBAL_METHOD(methods, PtValidateKey); + + PYTHON_GLOBAL_METHOD(methods, PtSendRTChat); + PYTHON_GLOBAL_METHOD(methods, PtSendKIMessage); + PYTHON_GLOBAL_METHOD(methods, PtSendKIMessageInt); + + PYTHON_GLOBAL_METHOD(methods, PtLoadAvatarModel); + PYTHON_GLOBAL_METHOD(methods, PtUnLoadAvatarModel); + + PYTHON_BASIC_GLOBAL_METHOD(methods, PtForceCursorHidden); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtForceCursorShown); + + PYTHON_GLOBAL_METHOD(methods, PtGetLocalizedString); + + PYTHON_GLOBAL_METHOD(methods, PtDumpLogs); + + AddPlasmaMethods2(methods); + AddPlasmaMethods3(methods); + AddPlasmaMethods4(methods); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp new file mode 100644 index 00000000..ba9de33c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue2.cpp @@ -0,0 +1,514 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyMisc.h" +#include "pyGlueHelpers.h" +#include "pyKey.h" +#include "pyColor.h" +#include "pyPlayer.h" +#include "pyEnum.h" + +// for enums +#include "..\plNetCommon\plNetCommon.h" +#include "..\plResMgr\plLocalization.h" +#include "..\plMessage\plLOSRequestMsg.h" + +#include + +PYTHON_GLOBAL_METHOD_DEFINITION(PtYesNoDialog, args, "Params: selfkey,dialogMessage\nThis will display a Yes/No dialog to the user with the text dialogMessage\n" + "This dialog _has_ to be answered by the user.\n" + "And their answer will be returned in a Notify message.") +{ + PyObject* keyObj = NULL; + PyObject* dialogMsgObj = NULL; + if (!PyArg_ParseTuple(args, "OO", &keyObj, &dialogMsgObj)) + { + PyErr_SetString(PyExc_TypeError, "PtYesNoDialog expects a ptKey and a string or unicode string"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtYesNoDialog expects a ptKey and a string or unicode string"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + if (PyUnicode_Check(dialogMsgObj)) + { + int len = PyUnicode_GetSize(dialogMsgObj); + wchar_t* text = TRACKED_NEW wchar_t[len + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)dialogMsgObj, text, len); + text[len] = L'\0'; + cyMisc::YesNoDialog(*key, text); + delete [] text; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(dialogMsgObj)) + { + char* text = PyString_AsString(dialogMsgObj); + cyMisc::YesNoDialog(*key, text); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "PtYesNoDialog expects a ptKey and a string or unicode string"); + PYTHON_RETURN_ERROR; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtRateIt, args, "Params: chronicleName,dialogPrompt,onceFlag\nShows a dialog with dialogPrompt and stores user input rating into chronicleName") +{ + char* chronicleName; + char* dialogPrompt; + char onceFlag; + if (!PyArg_ParseTuple(args, "ssb", &chronicleName, &dialogPrompt, &onceFlag)) + { + PyErr_SetString(PyExc_TypeError, "PtRateIt expects two strings and a boolean"); + PYTHON_RETURN_ERROR; + } + cyMisc::RateIt(chronicleName, dialogPrompt, onceFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtExcludeRegionSet, args, "Params: senderKey,regionKey,state\nThis will set the state of an exclude region\n" + "- 'senderKey' is a ptKey of the PythonFile component\n" + "- 'regionKey' is a ptKey of the exclude region\n" + "- 'state' is either kExRegRelease or kExRegClear") +{ + PyObject* senderObj = NULL; + PyObject* regionObj = NULL; + unsigned short stateVal; + if (!PyArg_ParseTuple(args, "OOh", &senderObj, ®ionObj, &stateVal)) + { + PyErr_SetString(PyExc_TypeError, "PtExcludeRegionSet expects two ptKeys and a short"); + PYTHON_RETURN_ERROR; + } + if ((!pyKey::Check(senderObj)) || (!pyKey::Check(regionObj))) + { + PyErr_SetString(PyExc_TypeError, "PtExcludeRegionSet expects two ptKeys and a short"); + PYTHON_RETURN_ERROR; + } + pyKey* sender = pyKey::ConvertFrom(senderObj); + pyKey* region = pyKey::ConvertFrom(regionObj); + cyMisc::ExcludeRegionSet(*sender, *region, stateVal); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtExcludeRegionSetNow, args, "Params: senderKey,regionKey,state\nThis will set the state of an exclude region immediately on the server\n" + "- 'senderKey' is a ptKey of the PythonFile component\n" + "- 'regionKey' is a ptKey of the exclude region\n" + "- 'state' is either kExRegRelease or kExRegClear") +{ + PyObject* senderObj = NULL; + PyObject* regionObj = NULL; + unsigned short stateVal; + if (!PyArg_ParseTuple(args, "OOh", &senderObj, ®ionObj, &stateVal)) + { + PyErr_SetString(PyExc_TypeError, "PtExcludeRegionSetNow expects two ptKeys and a short"); + PYTHON_RETURN_ERROR; + } + if ((!pyKey::Check(senderObj)) || (!pyKey::Check(regionObj))) + { + PyErr_SetString(PyExc_TypeError, "PtExcludeRegionSetNow expects two ptKeys and a short"); + PYTHON_RETURN_ERROR; + } + pyKey* sender = pyKey::ConvertFrom(senderObj); + pyKey* region = pyKey::ConvertFrom(regionObj); + cyMisc::ExcludeRegionSetNow(*sender, *region, stateVal); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtAcceptInviteInGame, args, "Params: friendName,inviteKey\nSends a VaultTask to the server to perform the invite") +{ + char* friendName; + char* inviteKey; + if (!PyArg_ParseTuple(args, "ss", &friendName, &inviteKey)) + { + PyErr_SetString(PyExc_TypeError, "PtAcceptInviteInGame expects two strings"); + PYTHON_RETURN_ERROR; + } + cyMisc::AcceptInviteInGame(friendName, inviteKey); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetTime, "Returns the number of seconds since the game was started.") +{ + return PyFloat_FromDouble(cyMisc::GetSeconds()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetGameTime, "Returns the system game time (frame based) in seconds.") +{ + return PyFloat_FromDouble(cyMisc::GetSysSeconds()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetFrameDeltaTime, "Returns the amount of time that has elapsed since last frame.") +{ + return PyFloat_FromDouble(cyMisc::GetDelSysSeconds()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtPageInNode, args, "Params: nodeName, ageName=\"\"\nPages in node, or a list of nodes") +{ + PyObject* nodeNameObj = NULL; + char* ageName = NULL; + if (!PyArg_ParseTuple(args, "O|s", &nodeNameObj, &ageName)) + { + PyErr_SetString(PyExc_TypeError, "PtPageInNode expects a string or list of strings, and optionally a string"); + PYTHON_RETURN_ERROR; + } + std::vector nodeNames; + if (PyString_Check(nodeNameObj)) + { + nodeNames.push_back(PyString_AsString(nodeNameObj)); + } + else if (PyList_Check(nodeNameObj)) + { + int num = PyList_Size(nodeNameObj); + for (int i = 0; i < num; i++) + { + PyObject* listItem = PyList_GetItem(nodeNameObj, i); + if (!PyString_Check(listItem)) + { + PyErr_SetString(PyExc_TypeError, "PtPageInNode expects a string or list of strings, and optionally a string"); + PYTHON_RETURN_ERROR; + } + nodeNames.push_back(PyString_AsString(listItem)); + } + } + else + { + PyErr_SetString(PyExc_TypeError, "PtPageInNode expects a string or list of strings, and optionally a string"); + PYTHON_RETURN_ERROR; + } + + cyMisc::PageInNodes(nodeNames, ageName); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtPageOutNode, args, "Params: nodeName\nPages out a node") +{ + char* nodeName; + if (!PyArg_ParseTuple(args, "s", &nodeName)) + { + PyErr_SetString(PyExc_TypeError, "PtPageOutNode expects a string"); + PYTHON_RETURN_ERROR; + } + cyMisc::PageOutNode(nodeName); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtLimitAvatarLOD, args, "Params: LODlimit\nSets avatar's LOD limit") +{ + int lodLimit; + if (!PyArg_ParseTuple(args, "i", &lodLimit)) + { + PyErr_SetString(PyExc_TypeError, "PtLimitAvatarLOD expects an integer"); + PYTHON_RETURN_ERROR; + } + cyMisc::LimitAvatarLOD(lodLimit); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtFogSetDefColor, args, "Params: color\nSets default fog color") +{ + PyObject* colorObj = NULL; + if (!PyArg_ParseTuple(args, "O", &colorObj)) + { + PyErr_SetString(PyExc_TypeError, "PtFogSetDefColor expects a ptColor object"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "PtFogSetDefColor expects a ptColor object"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + cyMisc::FogSetDefColor(*color); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtFogSetDefLinear, args, "Params: start,end,density\nSet linear fog values") +{ + float start, end, density; + if (!PyArg_ParseTuple(args, "fff", &start, &end, &density)) + { + PyErr_SetString(PyExc_TypeError, "PtFogSetDefLinear expects three floats"); + PYTHON_RETURN_ERROR; + } + cyMisc::FogSetDefLinear(start, end, density); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtFogSetDefExp, args, "Params: end,density\nSet exp fog values") +{ + float end, density; + if (!PyArg_ParseTuple(args, "ff", &end, &density)) + { + PyErr_SetString(PyExc_TypeError, "PtFogSetDefExp expects three floats"); + PYTHON_RETURN_ERROR; + } + cyMisc::FogSetDefExp(end, density); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtFogSetDefExp2, args, "Params: end,density\nSet exp2 fog values") +{ + float end, density; + if (!PyArg_ParseTuple(args, "ff", &end, &density)) + { + PyErr_SetString(PyExc_TypeError, "PtFogSetDefExp2 expects three floats"); + PYTHON_RETURN_ERROR; + } + cyMisc::FogSetDefExp2(end, density); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtLoadDialog, args, "Params: dialogName,selfKey=None,ageName=\"\"\nLoads a GUI dialog by name and optionally set the Notify proc key\n" + "If the dialog is already loaded then it won't load it again") +{ + char* dialogName; + PyObject* keyObj = NULL; + char* ageName = NULL; + if (!PyArg_ParseTuple(args, "s|Os", &dialogName, &keyObj, &ageName)) + { + PyErr_SetString(PyExc_TypeError, "PtLoadDialog expects a string, and optionally a ptKey and second string"); + PYTHON_RETURN_ERROR; + } + if (keyObj) + { + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtLoadDialog expects a string, and optionally a ptKey and second string"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + if (ageName) + cyMisc::LoadDialogKA(dialogName, *key, ageName); + else + cyMisc::LoadDialogK(dialogName, *key); + } + else + cyMisc::LoadDialog(dialogName); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtUnloadDialog, args, "Params: dialogName\nThis will unload the GUI dialog by name. If not loaded then nothing will happen") +{ + char* dialogName; + if (!PyArg_ParseTuple(args, "s", &dialogName)) + { + PyErr_SetString(PyExc_TypeError, "PtUnloadDialog expects a string"); + PYTHON_RETURN_ERROR; + } + cyMisc::UnloadDialog(dialogName); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtIsDialogLoaded, args, "Params: dialogName\nTest to see if a GUI dialog is loaded, by name") +{ + char* dialogName; + if (!PyArg_ParseTuple(args, "s", &dialogName)) + { + PyErr_SetString(PyExc_TypeError, "PtIsDialogLoaded expects a string"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(cyMisc::IsDialogLoaded(dialogName)); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtShowDialog, args, "Params: dialogName\nShow a GUI dialog by name (does not load dialog)") +{ + char* dialogName; + if (!PyArg_ParseTuple(args, "s", &dialogName)) + { + PyErr_SetString(PyExc_TypeError, "PtShowDialog expects a string"); + PYTHON_RETURN_ERROR; + } + cyMisc::ShowDialog(dialogName); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtHideDialog, args, "Params: dialogName\nHide a GUI dialog by name (does not unload dialog)") +{ + char* dialogName; + if (!PyArg_ParseTuple(args, "s", &dialogName)) + { + PyErr_SetString(PyExc_TypeError, "PtHideDialog expects a string"); + PYTHON_RETURN_ERROR; + } + cyMisc::HideDialog(dialogName); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetDialogFromTagID, args, "Params: tagID\nReturns the dialog associated with the tagID") +{ + unsigned long tagID; + if (!PyArg_ParseTuple(args, "l", &tagID)) + { + PyErr_SetString(PyExc_TypeError, "PtGetDialogFromTagID expects a long"); + PYTHON_RETURN_ERROR; + } + return cyMisc::GetDialogFromTagID(tagID); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetDialogFromString, args, "Params: dialogName\nGet a ptGUIDialog from its name") +{ + char* dialogName; + if (!PyArg_ParseTuple(args, "s", &dialogName)) + { + PyErr_SetString(PyExc_TypeError, "PtHideDialog expects a string"); + PYTHON_RETURN_ERROR; + } + return cyMisc::GetDialogFromString(dialogName); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsGUIModal, "Returns true if the GUI is displaying a modal dialog and blocking input") +{ + PYTHON_RETURN_BOOL(cyMisc::IsGUIModal()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSendPrivateChatList, args, "Params: chatList\nLock the local avatar into private vox messaging, and / or add new members to his chat list") +{ + PyObject* chatListObj = NULL; + if (!PyArg_ParseTuple(args, "O", &chatListObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSendPrivateChatList expects a list of ptPlayers"); + PYTHON_RETURN_ERROR; + } + + std::vector chatList; + if (PyList_Check(chatListObj)) + { + int listSize = PyList_Size(chatListObj); + for (int i = 0; i < listSize; i++) + { + PyObject* listItem = PyList_GetItem(chatListObj, i); + if (!pyPlayer::Check(listItem)) + { + PyErr_SetString(PyExc_TypeError, "PtSendPrivateChatList expects a list of ptPlayers"); + PYTHON_RETURN_ERROR; + } + chatList.push_back(pyPlayer::ConvertFrom(listItem)); + } + } + else + { + PyErr_SetString(PyExc_TypeError, "PtSendPrivateChatList expects a list of ptPlayers"); + PYTHON_RETURN_ERROR; + } + + cyMisc::SetPrivateChatList(chatList); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtClearPrivateChatList, args, "Params: memberKey\nRemove the local avatar from private vox messaging, and / or clear members from his chat list") +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtClearPrivateChatList expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtClearPrivateChatList expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::ClearPrivateChatList(*key); + PYTHON_RETURN_NONE; +} + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaMethods - the python method definitions +// + +void cyMisc::AddPlasmaMethods2(std::vector &methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtYesNoDialog); + PYTHON_GLOBAL_METHOD(methods, PtRateIt); + + PYTHON_GLOBAL_METHOD(methods, PtExcludeRegionSet); + PYTHON_GLOBAL_METHOD(methods, PtExcludeRegionSetNow); + + PYTHON_GLOBAL_METHOD(methods, PtAcceptInviteInGame); + + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetTime); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetGameTime); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetFrameDeltaTime); + + PYTHON_GLOBAL_METHOD(methods, PtPageInNode); + PYTHON_GLOBAL_METHOD(methods, PtPageOutNode); + + PYTHON_GLOBAL_METHOD(methods, PtLimitAvatarLOD); + + PYTHON_GLOBAL_METHOD(methods, PtFogSetDefColor); + PYTHON_GLOBAL_METHOD(methods, PtFogSetDefLinear); + PYTHON_GLOBAL_METHOD(methods, PtFogSetDefExp); + PYTHON_GLOBAL_METHOD(methods, PtFogSetDefExp2); + + PYTHON_GLOBAL_METHOD(methods, PtLoadDialog); + PYTHON_GLOBAL_METHOD(methods, PtUnloadDialog); + PYTHON_GLOBAL_METHOD(methods, PtIsDialogLoaded); + PYTHON_GLOBAL_METHOD(methods, PtShowDialog); + PYTHON_GLOBAL_METHOD(methods, PtHideDialog); + PYTHON_GLOBAL_METHOD(methods, PtGetDialogFromTagID); + PYTHON_GLOBAL_METHOD(methods, PtGetDialogFromString); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsGUIModal); + + PYTHON_GLOBAL_METHOD(methods, PtSendPrivateChatList); + PYTHON_GLOBAL_METHOD(methods, PtClearPrivateChatList); +} + +void cyMisc::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtCCRPetitionType); + PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kGeneralHelp,plNetCommon::PetitionTypes::kGeneralHelp); + PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kBug, plNetCommon::PetitionTypes::kBug); + PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kFeedback, plNetCommon::PetitionTypes::kFeedback); + PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kExploit, plNetCommon::PetitionTypes::kExploit); + PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kHarass, plNetCommon::PetitionTypes::kHarass); + PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kStuck, plNetCommon::PetitionTypes::kStuck); + PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kTechnical, plNetCommon::PetitionTypes::kTechnical); + PYTHON_ENUM_END(m, PtCCRPetitionType); + + PYTHON_ENUM_START(PtLanguage); + PYTHON_ENUM_ELEMENT(PtLanguage, kEnglish, plLocalization::kEnglish); + PYTHON_ENUM_ELEMENT(PtLanguage, kFrench, plLocalization::kFrench); + PYTHON_ENUM_ELEMENT(PtLanguage, kGerman, plLocalization::kGerman); + PYTHON_ENUM_ELEMENT(PtLanguage, kSpanish, plLocalization::kSpanish); + PYTHON_ENUM_ELEMENT(PtLanguage, kItalian, plLocalization::kItalian); + PYTHON_ENUM_ELEMENT(PtLanguage, kJapanese, plLocalization::kJapanese); + PYTHON_ENUM_ELEMENT(PtLanguage, kNumLanguages, plLocalization::kNumLanguages); + PYTHON_ENUM_END(m, PtLanguage); + + PYTHON_ENUM_START(PtLOSReportType); + PYTHON_ENUM_ELEMENT(PtLOSReportType, kReportHit, plLOSRequestMsg::kReportHit); + PYTHON_ENUM_ELEMENT(PtLOSReportType, kReportMiss, plLOSRequestMsg::kReportMiss); + PYTHON_ENUM_ELEMENT(PtLOSReportType, kReportHitOrMiss, plLOSRequestMsg::kReportHitOrMiss); + PYTHON_ENUM_END(m, PtLOSReportType); + + PYTHON_ENUM_START(PtLOSObjectType); + PYTHON_ENUM_ELEMENT(PtLOSObjectType, kClickables, kClickables); + PYTHON_ENUM_ELEMENT(PtLOSObjectType, kCameraBlockers, kCameraBlockers); + PYTHON_ENUM_ELEMENT(PtLOSObjectType, kCustom, kCustom); + PYTHON_ENUM_ELEMENT(PtLOSObjectType, kShootable, kShootable); + PYTHON_ENUM_END(m, PtLOSObjectType); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp new file mode 100644 index 00000000..2b0987d6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue3.cpp @@ -0,0 +1,762 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyMisc.h" +#include "pyGlueHelpers.h" +#include "pyKey.h" +#include "pySceneObject.h" + +#include + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSendPetitionToCCR, args, "Params: message,reason=0,title=\"\"\nSends a petition with a message to the CCR group") +{ + char* message; + unsigned char reason = 0; + char* title = nil; + if (!PyArg_ParseTuple(args, "s|bs", &message, &reason, &title)) + { + PyErr_SetString(PyExc_TypeError, "PtSendPetitionToCCR expects a string, and an optional unsigned 8-bit int and optional string"); + PYTHON_RETURN_ERROR; + } + cyMisc::SendPetitionToCCRI(message, reason, title); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSendChatToCCR, args, "Params: message,CCRPlayerID\nSends a chat message to a CCR that has contacted this player") +{ + char* message; + long CCRPlayerID; + if (!PyArg_ParseTuple(args, "sl", &message, &CCRPlayerID)) + { + PyErr_SetString(PyExc_TypeError, "PtSendChatToCCR expects a string and a long"); + PYTHON_RETURN_ERROR; + } + cyMisc::SendChatToCCR(message, CCRPlayerID); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetPythonLoggingLevel, "Returns the current level of python logging") +{ + return PyLong_FromUnsignedLong(cyMisc::GetPythonLoggingLevel()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetPythonLoggingLevel, args, "Params: level\nSets the current level of python logging") +{ + unsigned long level; + if (!PyArg_ParseTuple(args, "l", &level)) + { + PyErr_SetString(PyExc_TypeError, "PtSetPythonLoggingLevel expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + cyMisc::SetPythonLoggingLevel(level); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtConsole, args, "Params: command\nThis will execute 'command' as if it were typed into the Plasma console.") +{ + char* command; + if (!PyArg_ParseTuple(args, "s", &command)) + { + PyErr_SetString(PyExc_TypeError, "PtConsole expects a string"); + PYTHON_RETURN_ERROR; + } + cyMisc::Console(command); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtConsoleNet, args, "Params: command,netForce\nThis will execute 'command' on the console, over the network, on all clients.\n" + "If 'netForce' is true then force command to be sent over the network.") +{ + char* command; + char netForce; + if (!PyArg_ParseTuple(args, "sb", &command, &netForce)) + { + PyErr_SetString(PyExc_TypeError, "PtConsoleNet expects a string and a boolean"); + PYTHON_RETURN_ERROR; + } + cyMisc::ConsoleNet(command, netForce != 0); + PYTHON_RETURN_NONE; +} + +#if 1 +// TEMP +PYTHON_GLOBAL_METHOD_DEFINITION(PtPrintToScreen, args, "Params: message\nPrints 'message' to the status log, for debug only.") +{ + char* message; + if (!PyArg_ParseTuple(args, "s", &message)) + { + PyErr_SetString(PyExc_TypeError, "PtPrintToScreen expects a string"); + PYTHON_RETURN_ERROR; + } + cyMisc::PrintToScreen(message); + PYTHON_RETURN_NONE; +} +#endif + +PYTHON_GLOBAL_METHOD_DEFINITION(PtAtTimeCallback, args, "Params: selfkey,time,id\nThis will create a timer callback that will call OnTimer when complete\n" + "- 'selfkey' is the ptKey of the PythonFile component\n" + "- 'time' is how much time from now (in seconds) to call back\n" + "- 'id' is an integer id that will be returned in the OnTimer call") +{ + PyObject* keyObj = NULL; + float time; + unsigned long id; + if (!PyArg_ParseTuple(args, "Ofl", &keyObj, &time, &id)) + { + PyErr_SetString(PyExc_TypeError, "PtAtTimeCallback expects a ptKey, a float, and an unsigned long"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtAtTimeCallback expects a ptKey, a float, and an unsigned long"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::TimerCallback(*key, time, id); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtClearTimerCallbacks, args, "Params: key\nThis will remove timer callbacks to the specified key") +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtClearTimerCallbacks expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtClearTimerCallbacks expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::ClearTimerCallbacks(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtFindSceneobject, args, "Params: name,ageName\nThis will try to find a sceneobject based on its name and what age its in\n" + "- it will return a ptSceneObject if found" + "- if not found then a NameError exception will happen") +{ + char* name; + char* ageName; + if (!PyArg_ParseTuple(args, "ss", &name, &ageName)) + { + PyErr_SetString(PyExc_TypeError, "PtFindSceneobject expects two strings"); + PYTHON_RETURN_ERROR; + } + return cyMisc::FindSceneObject(name, ageName); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtFindActivator, args, "Params: name\nThis will try to find an activator based on its name\n" + "- it will return a ptKey if found" + "- it will return None if not found") +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "PtFindActivator expects a string"); + PYTHON_RETURN_ERROR; + } + + return cyMisc::FindActivator(name); +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtClearCameraStack, cyMisc::ClearCameraStack, "Clears the camera stack") + +PYTHON_GLOBAL_METHOD_DEFINITION(PtWasLocallyNotified, args, "Params: selfKey\nReturns 1 if the last notify was local or 0 if the notify originated on the network") +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtWasLocallyNotified expects a ptKey"); + PYTHON_RETURN_NONE; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtWasLocallyNotified expects a ptKey"); + PYTHON_RETURN_NONE; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + PYTHON_RETURN_BOOL(cyMisc::WasLocallyNotified(*key)); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtAttachObject, args, "Params: child,parent\nAttach child to parent based on ptKey or ptSceneobject\n" + "- childKey is the ptKey or ptSceneobject of the one being attached\n" + "- parentKey is the ptKey or ptSceneobject of the one being attached to\n" + "(both arguments must be ptKeys or ptSceneobjects, you cannot mix types)") +{ + PyObject* childObj = NULL; + PyObject* parentObj = NULL; + if (!PyArg_ParseTuple(args, "OO", &childObj, &parentObj)) + { + PyErr_SetString(PyExc_TypeError, "PtAttachObject expects either two ptKeys or two ptSceneobjects"); + PYTHON_RETURN_ERROR; + } + if ((pyKey::Check(childObj)) && (pyKey::Check(parentObj))) + { + pyKey* child = pyKey::ConvertFrom(childObj); + pyKey* parent = pyKey::ConvertFrom(parentObj); + cyMisc::AttachObject(*child, *parent); + } + else if ((pySceneObject::Check(childObj)) && (pySceneObject::Check(parentObj))) + { + pySceneObject* child = pySceneObject::ConvertFrom(childObj); + pySceneObject* parent = pySceneObject::ConvertFrom(parentObj); + cyMisc::AttachObjectSO(*child, *parent); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtAttachObject expects either two ptKeys or two ptSceneobjects"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtDetachObject, args, "Params: child,parent\nDetach child from parent based on ptKey or ptSceneobject\n" + "- child is the ptKey or ptSceneobject of the one being detached\n" + "- parent is the ptKey or ptSceneobject of the one being detached from\n" + "(both arguments must be ptKeys or ptSceneobjects, you cannot mix types)") +{ + PyObject* childObj = NULL; + PyObject* parentObj = NULL; + if (!PyArg_ParseTuple(args, "OO", &childObj, &parentObj)) + { + PyErr_SetString(PyExc_TypeError, "PtDetachObject expects either two ptKeys or two ptSceneobjects"); + PYTHON_RETURN_ERROR; + } + if ((pyKey::Check(childObj)) && (pyKey::Check(parentObj))) + { + pyKey* child = pyKey::ConvertFrom(childObj); + pyKey* parent = pyKey::ConvertFrom(parentObj); + cyMisc::DetachObject(*child, *parent); + } + else if ((pySceneObject::Check(childObj)) && (pySceneObject::Check(parentObj))) + { + pySceneObject* child = pySceneObject::ConvertFrom(childObj); + pySceneObject* parent = pySceneObject::ConvertFrom(parentObj); + cyMisc::DetachObjectSO(*child, *parent); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtDetachObject expects either two ptKeys or two ptSceneobjects"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_NONE; +} + +/*PYTHON_GLOBAL_METHOD_DEFINITION(PtLinkToAge, args, "Params: selfKey,ageName,spawnPointName\nDEPRECIATED: Links you to the specified age and spawnpoint") +{ + PyObject* keyObj = NULL; + char* ageName; + char* spawnPointName; + if (!PyArg_ParseTuple(args, "Oss", &keyObj, &ageName, &spawnPointName)) + { + PyErr_SetString(PyExc_TypeError, "PtLinkToAge expects a ptKey, and two strings"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtLinkToAge expects a ptKey, and two strings"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::LinkToAge(*key, ageName, spawnPointName); + PYTHON_RETURN_NONE; +}*/ + +PYTHON_GLOBAL_METHOD_DEFINITION(PtDirtySynchState, args, "Params: selfKey,SDLStateName,flags\nDO NOT USE - handled by ptSDL") +{ + PyObject* keyObj = NULL; + char* SDLStateName; + unsigned long flags; + if (!PyArg_ParseTuple(args, "Osl", &keyObj, &SDLStateName, &flags)) + { + PyErr_SetString(PyExc_TypeError, "PtDirtySynchState expects a ptKey, a string, and an unsigned long"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtDirtySynchState expects a ptKey, a string, and an unsigned long"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::SetDirtySyncState(*key, SDLStateName, flags); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtDirtySynchClients, args, "Params: selfKey,SDLStateName,flags\nDO NOT USE - handled by ptSDL") +{ + PyObject* keyObj = NULL; + char* SDLStateName; + unsigned long flags; + if (!PyArg_ParseTuple(args, "Osl", &keyObj, &SDLStateName, &flags)) + { + PyErr_SetString(PyExc_TypeError, "PtDirtySynchClients expects a ptKey, a string, and an unsigned long"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtDirtySynchClients expects a ptKey, a string, and an unsigned long"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::SetDirtySyncStateWithClients(*key, SDLStateName, flags); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtEnableControlKeyEvents, args, "Params: selfKey\nEnable control key events to call OnControlKeyEvent(controlKey,activateFlag)") +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtEnableControlKeyEvents expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtEnableControlKeyEvents expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::EnableControlKeyEvents(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtDisableControlKeyEvents, args, "Params: selfKey\nDisable the control key events from calling OnControlKeyEvent") +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtDisableControlKeyEvents expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtDisableControlKeyEvents expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::DisableControlKeyEvents(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtEnableAvatarCursorFade, cyMisc::EnableAvatarCursorFade, "Enable the avatar cursor fade") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtDisableAvatarCursorFade, cyMisc::DisableAvatarCursorFade, "Disable the avatar cursor fade") + +PYTHON_GLOBAL_METHOD_DEFINITION(PtFadeLocalAvatar, args, "Params: fade\nFade (or unfade) the local avatar") +{ + char fade; + if (!PyArg_ParseTuple(args, "b", &fade)) + { + PyErr_SetString(PyExc_TypeError, "PtFadeLocalAvatar expects a boolean"); + PYTHON_RETURN_ERROR; + } + cyMisc::FadeLocalPlayer(fade != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetOfferBookMode, args, "Params: selfkey,ageFilename,ageInstanceName\nPut us into the offer book interface") +{ + PyObject* keyObj = NULL; + char* ageFilename; + char* ageInstanceName; + if (!PyArg_ParseTuple(args, "Oss", &keyObj, &ageFilename, &ageInstanceName)) + { + PyErr_SetString(PyExc_TypeError, "PtSetOfferBookMode expects a ptKey, and two strings"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSetOfferBookMode expects a ptKey, and two strings"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::EnableOfferBookMode(*key, ageFilename, ageInstanceName); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetShareSpawnPoint, args, "Params: spawnPoint\nThis sets the desired spawn point for the receiver to link to") +{ + char* spawnPoint; + if (!PyArg_ParseTuple(args, "s", &spawnPoint)) + { + PyErr_SetString(PyExc_TypeError, "PtSetShareSpawnPoint expects a string"); + PYTHON_RETURN_ERROR; + } + cyMisc::SetShareSpawnPoint(spawnPoint); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetShareAgeInstanceGuid, args, "Params: instanceGuid\nThis sets the desired age instance guid for the receiver to link to") +{ + char* guidStr; + if (!PyArg_ParseTuple(args, "s", &guidStr)) + { + PyErr_SetString(PyExc_TypeError, "PtSetShareAgeInstanceGuid expects a string"); + PYTHON_RETURN_ERROR; + } + Uuid guid; + if (!GuidFromString(guidStr, &guid)) + { + PyErr_SetString(PyExc_TypeError, "PtSetShareAgeInstanceGuid string parameter is not a guid string"); + PYTHON_RETURN_ERROR; + } + cyMisc::SetShareAgeInstanceGuid(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtNotifyOffererLinkAccepted, args, "Params: offerer\nTell the offerer that we accepted the link offer") +{ + unsigned long offerer; + if (!PyArg_ParseTuple(args, "l", &offerer)) + { + PyErr_SetString(PyExc_TypeError, "PtNotifyOffererLinkAccepted expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + cyMisc::NotifyOffererPublicLinkAccepted(offerer); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtNotifyOffererLinkRejected, args, "Params: offerer\nTell the offerer that we rejected the link offer") +{ + unsigned long offerer; + if (!PyArg_ParseTuple(args, "l", &offerer)) + { + PyErr_SetString(PyExc_TypeError, "PtNotifyOffererLinkRejected expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + cyMisc::NotifyOffererPublicLinkRejected(offerer); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtNotifyOffererLinkCompleted, args, "Params: offerer\nTell the offerer that we completed the link") +{ + unsigned long offerer; + if (!PyArg_ParseTuple(args, "l", &offerer)) + { + PyErr_SetString(PyExc_TypeError, "PtNotifyOffererLinkCompleted expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + cyMisc::NotifyOffererPublicLinkCompleted(offerer); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtClearOfferBookMode, cyMisc::DisableOfferBookMode, "Cancel the offer book interface") + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetLocalClientID, "Returns our local client ID number") +{ + return PyInt_FromLong(cyMisc::GetLocalClientID()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsCCRAway, "Returns current status of CCR dept") +{ + PYTHON_RETURN_BOOL(cyMisc::IsCCRAwayStatus()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtAmCCR, "Returns true if local player is a CCR") +{ + PYTHON_RETURN_BOOL(cyMisc::AmCCR()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtToggleAvatarClickability, args, "Params: on\nTurns on and off our avatar's clickability") +{ + char on; + if (!PyArg_ParseTuple(args, "b", &on)) + { + PyErr_SetString(PyExc_TypeError, "PtToggleAvatarClickability expects a boolean"); + PYTHON_RETURN_ERROR; + } + cyMisc::ToggleAvatarClickability(on != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtTransferParticlesToObject, args, "Params: objFrom, objTo, num\nTransfers num particles from objFrom to objTo") +{ + PyObject* objFrom = NULL; + PyObject* objTo = NULL; + int num; + if (!PyArg_ParseTuple(args, "OOi", &objFrom, &objTo, &num)) + { + PyErr_SetString(PyExc_TypeError, "PtTransferParticlesToObject expects two ptKeys and an int"); + PYTHON_RETURN_ERROR; + } + if ((!pyKey::Check(objFrom)) || (!pyKey::Check(objTo))) + { + PyErr_SetString(PyExc_TypeError, "PtTransferParticlesToObject expects two ptKeys and an int"); + PYTHON_RETURN_ERROR; + } + pyKey* from = pyKey::ConvertFrom(objFrom); + pyKey* to = pyKey::ConvertFrom(objTo); + cyMisc::TransferParticlesToKey(*from, *to, num); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetParticleDissentPoint, args, "Params: x, y, z, particlesys\nSets the dissent point of the particlesys to x,y,z") +{ + float x,y,z; + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "fffO", &x, &y, &z, &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSetParticleDissentPoint expects three floats and a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSetParticleDissentPoint expects three floats and a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::SetParticleDissentPoint(x, y, z, *key); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetControlEvents, args, "Params: on, key\nRegisters or unregisters for control event messages") +{ + char on; + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "bO", &on, &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtGetControlEvents expects a boolean and a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtGetControlEvents expects a boolean and a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::RegisterForControlEventMessages(on != 0, *key); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetLanguage, "Returns the current language as a PtLanguage enum") +{ + return PyInt_FromLong(cyMisc::GetLanguage()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtUsingUnicode, "Returns true if the current language is a unicode language (like Japanese)") +{ + PYTHON_RETURN_BOOL(cyMisc::UsingUnicode()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtFakeLinkAvatarToObject, args, "Params: avatar,object\nPseudo-links avatar to object within the same age\n") +{ + PyObject* avatarObj = NULL; + PyObject* objectObj = NULL; + if (!PyArg_ParseTuple(args, "OO", &avatarObj, &objectObj)) + { + PyErr_SetString(PyExc_TypeError, "PtFakeLinkAvatarToObject expects two ptKeys"); + PYTHON_RETURN_ERROR; + } + if ((!pyKey::Check(avatarObj)) || (!pyKey::Check(objectObj))) + { + PyErr_SetString(PyExc_TypeError, "PtFakeLinkAvatarToObject expects two ptKeys"); + PYTHON_RETURN_ERROR; + } + pyKey* avatar = pyKey::ConvertFrom(avatarObj); + pyKey* object = pyKey::ConvertFrom(objectObj); + cyMisc::FakeLinkToObject(*avatar, *object); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtWearDefaultClothingType, args, "Params: key,type\nForces the avatar to wear the default clothing of the specified type") +{ + PyObject* keyObj = NULL; + unsigned long type; + if (!PyArg_ParseTuple(args, "Ol", &keyObj, &type)) + { + PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey and an unsigned long"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey and an unsigned long"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::WearDefaultClothingType(*key, type); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtFileExists, args, "Params: filename\nReturns true if the specified file exists") +{ + PyObject* filenameObj; + if (!PyArg_ParseTuple(args, "O", &filenameObj)) + { + PyErr_SetString(PyExc_TypeError, "PtFileExists expects a string"); + PYTHON_RETURN_ERROR; + } + + if (PyUnicode_Check(filenameObj)) + { + int strLen = PyUnicode_GetSize(filenameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)filenameObj, text, strLen); + text[strLen] = L'\0'; + bool retVal = cyMisc::FileExists(text); + delete [] text; + PYTHON_RETURN_BOOL(retVal); + } + else if (PyString_Check(filenameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(filenameObj); + wchar_t* wText = hsStringToWString(text); + bool retVal = cyMisc::FileExists(wText); + delete [] wText; + PYTHON_RETURN_BOOL(retVal); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtFileExists expects a string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtCreateDir, args, "Params: directory\nCreates the directory and all parent folders. Returns false on failure") +{ + PyObject* directoryObj; + if (!PyArg_ParseTuple(args, "O", &directoryObj)) + { + PyErr_SetString(PyExc_TypeError, "PtCreateDir expects a string"); + PYTHON_RETURN_ERROR; + } + + if (PyUnicode_Check(directoryObj)) + { + int strLen = PyUnicode_GetSize(directoryObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)directoryObj, text, strLen); + text[strLen] = L'\0'; + bool retVal = cyMisc::CreateDir(text); + delete [] text; + PYTHON_RETURN_BOOL(retVal); + } + else if (PyString_Check(directoryObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(directoryObj); + wchar_t* wText = hsStringToWString(text); + bool retVal = cyMisc::CreateDir(wText); + delete [] wText; + PYTHON_RETURN_BOOL(retVal); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtCreateDir expects a string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetUserPath, "Returns the unicode path to the client's root user directory. Do NOT convert to a standard string.") +{ + std::wstring val = cyMisc::GetUserPath(); + return PyUnicode_FromWideChar(val.c_str(), val.length()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetInitPath, "Returns the unicode path to the client's init directory. Do NOT convert to a standard string.") +{ + std::wstring val = cyMisc::GetInitPath(); + return PyUnicode_FromWideChar(val.c_str(), val.length()); +} + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaMethods - the python method definitions +// + +void cyMisc::AddPlasmaMethods3(std::vector &methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtSendPetitionToCCR); + PYTHON_GLOBAL_METHOD(methods, PtSendChatToCCR); + + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetPythonLoggingLevel); + PYTHON_GLOBAL_METHOD(methods, PtSetPythonLoggingLevel); + + PYTHON_GLOBAL_METHOD(methods, PtConsole); + PYTHON_GLOBAL_METHOD(methods, PtConsoleNet); + +#if 1 + // TEMP + PYTHON_GLOBAL_METHOD(methods, PtPrintToScreen); +#endif + + PYTHON_GLOBAL_METHOD(methods, PtAtTimeCallback); + PYTHON_GLOBAL_METHOD(methods, PtClearTimerCallbacks); + + PYTHON_GLOBAL_METHOD(methods, PtFindSceneobject); + PYTHON_GLOBAL_METHOD(methods, PtFindActivator); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtClearCameraStack); + PYTHON_GLOBAL_METHOD(methods, PtWasLocallyNotified); + + PYTHON_GLOBAL_METHOD(methods, PtAttachObject); + PYTHON_GLOBAL_METHOD(methods, PtDetachObject); + + //PYTHON_GLOBAL_METHOD(methods, PtLinkToAge); + + PYTHON_GLOBAL_METHOD(methods, PtDirtySynchState); + PYTHON_GLOBAL_METHOD(methods, PtDirtySynchClients); + + PYTHON_GLOBAL_METHOD(methods, PtEnableControlKeyEvents); + PYTHON_GLOBAL_METHOD(methods, PtDisableControlKeyEvents); + + PYTHON_BASIC_GLOBAL_METHOD(methods, PtEnableAvatarCursorFade); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtDisableAvatarCursorFade); + PYTHON_GLOBAL_METHOD(methods, PtFadeLocalAvatar); + + PYTHON_GLOBAL_METHOD(methods, PtSetOfferBookMode); + PYTHON_GLOBAL_METHOD(methods, PtSetShareSpawnPoint); + PYTHON_GLOBAL_METHOD(methods, PtSetShareAgeInstanceGuid); + PYTHON_GLOBAL_METHOD(methods, PtNotifyOffererLinkAccepted); + PYTHON_GLOBAL_METHOD(methods, PtNotifyOffererLinkRejected); + PYTHON_GLOBAL_METHOD(methods, PtNotifyOffererLinkCompleted); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtClearOfferBookMode); + + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetLocalClientID); + + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsCCRAway); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtAmCCR); + + PYTHON_GLOBAL_METHOD(methods, PtToggleAvatarClickability); + + PYTHON_GLOBAL_METHOD(methods, PtTransferParticlesToObject); + PYTHON_GLOBAL_METHOD(methods, PtSetParticleDissentPoint); + + PYTHON_GLOBAL_METHOD(methods, PtGetControlEvents); + + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetLanguage); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtUsingUnicode); + + PYTHON_GLOBAL_METHOD(methods, PtFakeLinkAvatarToObject); + + PYTHON_GLOBAL_METHOD(methods, PtWearDefaultClothingType); + + PYTHON_GLOBAL_METHOD(methods, PtFileExists); + PYTHON_GLOBAL_METHOD(methods, PtCreateDir); + + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetUserPath); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetInitPath); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp new file mode 100644 index 00000000..69709fba --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyMiscGlue4.cpp @@ -0,0 +1,838 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyMisc.h" +#include "pyGlueHelpers.h" +#include "pyKey.h" +#include "pySceneObject.h" +#include "pyAgeInfoStruct.h" +#include "pyGeometry3.h" +#include "../NucleusLib/inc/plPipeline.h" +#include "../pnNetBase/pnNetBase.h" + +#include + +PYTHON_GLOBAL_METHOD_DEFINITION(PtRequestLOSScreen, args, "Params: selfKey,ID,xPos,yPos,distance,what,reportType\nRequest a LOS check from a point on the screen") +{ + PyObject* keyObj = NULL; + long id; + float xPos, yPos, distance; + int what, reportType; + if (!PyArg_ParseTuple(args, "Olfffii", &keyObj, &id, &xPos, &yPos, &distance, &what, &reportType)) + { + PyErr_SetString(PyExc_TypeError, "PtRequestLOSScreen expects a ptKey, a long, three floats, and two ints"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtRequestLOSScreen expects a ptKey, a long, three floats, and two ints"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + PYTHON_RETURN_BOOL(cyMisc::RequestLOSScreen(*key, id, xPos, yPos, distance, what, reportType)); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtKillParticles, args, "Params: timeRemaining,pctToKill,particleSystem\nTells particleSystem to kill pctToKill percent of its particles") +{ + float timeRemaining, pctToKill; + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "ffO", &timeRemaining, &pctToKill, &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtKillParticles expects two floats and a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtKillParticles expects two floats and a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::KillParticles(timeRemaining, pctToKill, *key); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetNumParticles, args, "Params: key\nKey is the key of scene object host to particle system") +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtGetNumParticles expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtGetNumParticles expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + return PyInt_FromLong(cyMisc::GetNumParticles(*key)); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetParticleOffset, args, "Params: x,y,z,particlesys\nSets the particlesys particle system's offset") +{ + float x,y,z; + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "fffO", &x, &y, &z, &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSetParticleOffset expects three floats and a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSetParticleOffset expects three floats and a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::SetParticleOffset(x, y, z, *key); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetLightValue, args, "Params: key,name,r,g,b,a\n Key is the key of scene object host to light. Name is the name of the light to manipulate") +{ + PyObject* keyObj = NULL; + PyObject* nameObj = NULL; + float r,g,b,a; + if (!PyArg_ParseTuple(args, "OOffff", &keyObj, &nameObj, &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "PtSetLightValue expects a ptKey, a string, and four floats"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSetLightValue expects a ptKey, a string, and four floats"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + std::string name = ""; + if (PyUnicode_Check(nameObj)) + { + int strLen = PyUnicode_GetSize(nameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)nameObj, text, strLen); + text[strLen] = L'\0'; + char* cText = hsWStringToString(text); + name = cText; + delete [] cText; + delete [] text; + } + else if (PyString_Check(nameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(nameObj); + name = text; + } + else + { + PyErr_SetString(PyExc_TypeError, "PtSetLightValue expects a ptKey, a string, and four floats"); + PYTHON_RETURN_ERROR; + } + cyMisc::SetLightColorValue(*key, name, r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetLightAnimStart, args, "Params: key,name,start\n Key is the key of scene object host to light, start is a bool. Name is the name of the light to manipulate") +{ + PyObject* keyObj = NULL; + PyObject* nameObj = NULL; + char start; + if (!PyArg_ParseTuple(args, "OOb", &keyObj, &nameObj, &start)) + { + PyErr_SetString(PyExc_TypeError, "PtSetLightAnimStart expects a ptKey, a string, and a boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSetLightAnimStart expects a ptKey, a string, and a boolean"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + std::string name = ""; + if (PyUnicode_Check(nameObj)) + { + int strLen = PyUnicode_GetSize(nameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)nameObj, text, strLen); + text[strLen] = L'\0'; + char* cText = hsWStringToString(text); + name = cText; + delete [] cText; + delete [] text; + } + else if (PyString_Check(nameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(nameObj); + name = text; + } + else + { + PyErr_SetString(PyExc_TypeError, "PtSetLightAnimStart expects a ptKey, a string, and a boolean"); + PYTHON_RETURN_ERROR; + } + cyMisc::SetLightAnimationOn(*key, name, start != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsSinglePlayerMode, "Returns whether the game is in single player mode or not") +{ + PYTHON_RETURN_BOOL(cyMisc::IsSinglePlayerMode()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsDemoMode, "Returns whether the game is in Demo mode or not") +{ + PYTHON_RETURN_BOOL(cyMisc::IsDemoMode()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsInternalRelease, "Returns whether the client is an internal build or not") +{ + PYTHON_RETURN_BOOL(cyMisc::IsInternalRelease()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsEnterChatModeKeyBound, "Returns whether the EnterChatMode is bound to a key") +{ + PYTHON_RETURN_BOOL(cyMisc::IsEnterChatModeKeyBound()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtShootBulletFromScreen, args, "Params: selfkey, xPos, yPos, radius, range\nShoots a bullet from a position on the screen") +{ + PyObject* keyObj = NULL; + float xPos, yPos, radius, range; + if (!PyArg_ParseTuple(args, "Offff", &keyObj, &xPos, &yPos, &radius, &range)) + { + PyErr_SetString(PyExc_TypeError, "PtShootBulletFromScreen expects a ptKey and four floats"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtShootBulletFromScreen expects a ptKey and four floats"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::ShootBulletFromScreen(*key, xPos, yPos, radius, range); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtShootBulletFromObject, args, "Params: selfkey, gunObj, radius, range\nShoots a bullet from an object") +{ + PyObject* selfKeyObj = NULL; + PyObject* gunSceneObj = NULL; + float radius, range; + if (!PyArg_ParseTuple(args, "OOff", &selfKeyObj, &gunSceneObj, &radius, &range)) + { + PyErr_SetString(PyExc_TypeError, "PtShootBulletFromObject expects a ptKey, a ptSceneobject, and two floats"); + PYTHON_RETURN_ERROR; + } + if ((!pyKey::Check(selfKeyObj)) || (!pySceneObject::Check(gunSceneObj))) + { + PyErr_SetString(PyExc_TypeError, "PtShootBulletFromObject expects a ptKey, a ptSceneobject, and two floats"); + PYTHON_RETURN_ERROR; + } + pyKey* selfKey = pyKey::ConvertFrom(selfKeyObj); + pySceneObject* gunObj = pySceneObject::ConvertFrom(gunSceneObj); + cyMisc::ShootBulletFromObject(*selfKey, gunObj, radius, range); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetPublicAgeList, args, "Params: ageName, cbObject=None\nGet list of public ages for the given age name.\n" + "cbObject, if supplied should have a method called gotPublicAgeList(self,ageList). ageList is a list of tuple(ptAgeInfoStruct,nPlayersInAge)") +{ + char* ageName; + PyObject* cbObject = NULL; + if (!PyArg_ParseTuple(args, "s|O", &ageName, &cbObject)) + { + PyErr_SetString(PyExc_TypeError, "PtGetPublicAgeList expects a string and an optional object with a gotPublicAgeList() method"); + PYTHON_RETURN_ERROR; + } + cyMisc::GetPublicAgeList(ageName, cbObject); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtCreatePublicAge, args, "Params: ageInfo, cbObject=None\nCreate a public instance of the given age.\n" + "cbObject, if supplied should have a member called publicAgeCreated(self,ageInfo)") +{ + PyObject* ageInfoObj = NULL; + PyObject* cbObject = NULL; + if (!PyArg_ParseTuple(args, "O|O", &ageInfoObj, &cbObject)) + { + PyErr_SetString(PyExc_TypeError, "PtCreatePublicAge expects a ptAgeInfoStruct object and an optional object with a publicAgeCreated() method"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeInfoStruct::Check(ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "PtCreatePublicAge expects a ptAgeInfoStruct object and an optional object with a publicAgeCreated() method"); + PYTHON_RETURN_ERROR; + } + pyAgeInfoStruct* ageInfo = pyAgeInfoStruct::ConvertFrom(ageInfoObj); + cyMisc::CreatePublicAge(ageInfo, cbObject); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtRemovePublicAge, args, "Params: ageInstanceGuid, cbObject=None\nRemove a public instance of the given age.\n" + "cbObject, if supplied should have a member called publicAgeRemoved(self,ageInstanceGuid)") +{ + char* ageInstanceGUID; + PyObject* cbObject = NULL; + if (!PyArg_ParseTuple(args, "s|O", &ageInstanceGUID, &cbObject)) + { + PyErr_SetString(PyExc_TypeError, "PtRemovePublicAge expects a string and an optional object with a publicAgeRemoved() method"); + PYTHON_RETURN_ERROR; + } + cyMisc::RemovePublicAge(ageInstanceGUID, cbObject); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetClearColor, args, "Params: red,green,blue\nSet the clear color") +{ + float red, green, blue; + if (!PyArg_ParseTuple(args, "fff", &red, &green, &blue)) + { + PyErr_SetString(PyExc_TypeError, "PtSetClearColor expects three floats"); + PYTHON_RETURN_ERROR; + } + cyMisc::SetClearColor(red, green, blue); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetLocalKILevel, "returns local player's ki level") +{ + return PyInt_FromLong(cyMisc::GetKILevel()); +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtClearCameraStack, cyMisc::ClearCameraStack, "clears all cameras") + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetCameraNumber, args, "Params: x\nReturns camera x's name from stack") +{ + int x; + if (!PyArg_ParseTuple(args, "i", &x)) + { + PyErr_SetString(PyExc_TypeError, "PtGetCameraNumber expects an int"); + PYTHON_RETURN_ERROR; + } + return PyString_FromString(cyMisc::GetCameraNumber(x)); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetNumCameras, "returns camera stack size") +{ + return PyInt_FromLong(cyMisc::GetNumCameras()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtRebuildCameraStack, args, "Params: name,ageName\nPush camera with this name on the stack") +{ + char* name; + char* ageName; + if (!PyArg_ParseTuple(args, "ss", &name, &ageName)) + { + PyErr_SetString(PyExc_TypeError, "PtRebuildCameraStack expects two strings"); + PYTHON_RETURN_ERROR; + } + cyMisc::RebuildCameraStack(name, ageName); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtRecenterCamera, cyMisc::RecenterCamera, "re-centers the camera") + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtFirstPerson, "is the local avatar in first person mode") +{ + PYTHON_RETURN_BOOL(cyMisc::IsFirstPerson()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtFadeIn, args, "Params: lenTime, holdFlag, noSound=0\nFades screen in for lenTime seconds") +{ + float lenTime; + char holdFlag, noSound = 0; + if (!PyArg_ParseTuple(args, "fb|b", &lenTime, &holdFlag, &noSound)) + { + PyErr_SetString(PyExc_TypeError, "PtFadeIn expects a float, a boolean, and an optional boolean"); + PYTHON_RETURN_ERROR; + } + cyMisc::FadeIn(lenTime, holdFlag != 0, noSound != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtFadeOut, args, "Params: lenTime, holdFlag, noSound=0\nFades screen out for lenTime seconds") +{ + float lenTime; + char holdFlag, noSound = 0; + if (!PyArg_ParseTuple(args, "fb|b", &lenTime, &holdFlag, &noSound)) + { + PyErr_SetString(PyExc_TypeError, "PtFadeOut expects a float, a boolean, and an optional boolean"); + PYTHON_RETURN_ERROR; + } + cyMisc::FadeOut(lenTime, holdFlag != 0, noSound != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetGlobalClickability, args, "Params: enable\nEnable or disable all clickables on the local client") +{ + char enable; + if (!PyArg_ParseTuple(args, "b", &enable)) + { + PyErr_SetString(PyExc_TypeError, "PtSetGlobalClickability expects a boolean"); + PYTHON_RETURN_ERROR; + } + cyMisc::SetClickability(enable != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtDebugAssert, args, "Params: cond, msg\nDebug only: Assert if condition is false.") +{ + char cond; + char* msg; + if (!PyArg_ParseTuple(args, "bs", &cond, &msg)) + { + PyErr_SetString(PyExc_TypeError, "PtDebugAssert expects a boolean and a string"); + PYTHON_RETURN_ERROR; + } + cyMisc::DebugAssert(cond != 0, msg); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetAlarm, args, "Params: secs, cbObject, cbContext\nsecs is the amount of time before your alarm goes off.\n" + "cbObject is a python object with the method onAlarm(int context)\ncbContext is an integer.") +{ + float secs; + PyObject* cbObject = NULL; + unsigned long cbContext; + if (!PyArg_ParseTuple(args, "fOl", &secs, &cbObject, &cbContext)) + { + PyErr_SetString(PyExc_TypeError, "PtSetAlarm expects a float, a object with a onAlarm() method, and an int"); + PYTHON_RETURN_ERROR; + } + cyMisc::SetAlarm(secs, cbObject, cbContext); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSaveScreenShot, args, "Params: fileName,width=640,height=480,quality=75\nTakes a screenshot with the specified filename, size, and quality") +{ + char* fileName; + int width = 640, height = 480, quality = 75; + if (!PyArg_ParseTuple(args, "s|iii", &fileName, &width, &height, &quality)) + { + PyErr_SetString(PyExc_TypeError, "PtSaveScreenShot expects a string, and three optional integers"); + PYTHON_RETURN_ERROR; + } + cyMisc::SaveScreenShot(fileName, width, height, quality); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtStartScreenCapture, args, "Params: selfKey,width=800,height=600\nStarts a capture of the screen") +{ + PyObject* keyObj = NULL; + unsigned short width = 800, height = 600; + if (!PyArg_ParseTuple(args, "O|hh", &keyObj, &width, &height)) + { + PyErr_SetString(PyExc_TypeError, "PtStartScreenCapture expects a ptKey, and two optional unsigned 16-bit ints"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtStartScreenCapture expects a ptKey, and two optional unsigned 16-bit ints"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::StartScreenCaptureWH(*key, width, height); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSendKIGZMarkerMsg, args, "Params: markerNumber,sender\nSame as PtSendKIMessageInt except 'sender' could get a notify message back\n") +{ + long markerNumber; + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "lO", &markerNumber, &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSendKIGZMarkerMsg expects a long and a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSendKIGZMarkerMsg expects a long and a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::SendKIGZMarkerMsg(markerNumber, *key); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSendKIRegisterImagerMsg, args, "Params: imagerName, sender\nSends a message to the KI to register the specified imager") +{ + char* name; + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "sO", &name, &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSendKIRegisterImagerMsg expects a string and a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSendKIRegisterImagerMsg expects a string and a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::SendKIRegisterImagerMsg(name, *key); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtWearMaintainerSuit, args, "Params: key,wearOrNot\nWears or removes the maintainer suit of clothes") +{ + PyObject* keyObj = NULL; + char wearOrNot; + if (!PyArg_ParseTuple(args, "Ob", &keyObj, &wearOrNot)) + { + PyErr_SetString(PyExc_TypeError, "PtWearMaintainerSuit expects a ptKey and a boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtWearMaintainerSuit expects a ptKey and a boolean"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::WearMaintainerSuit(*key, wearOrNot != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtWearDefaultClothing, args, "Params: key\nForces the avatar to wear the default clothing set") +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothing expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothing expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::WearDefaultClothing(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetAgeTimeOfDayPercent, "Returns the current age time of day as a percent (0 to 1)") +{ + return PyFloat_FromDouble(cyMisc::GetAgeTimeOfDayPercent()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtCheckVisLOS, args, "Params: startPoint,endPoint\nDoes LOS check from start to end") +{ + PyObject* startPointObj = NULL; + PyObject* endPointObj = NULL; + if (!PyArg_ParseTuple(args, "OO", &startPointObj, &endPointObj)) + { + PyErr_SetString(PyExc_TypeError, "PtCheckVisLOS expects two ptPoint3 objects"); + PYTHON_RETURN_ERROR; + } + if ((!pyPoint3::Check(startPointObj)) || (!pyPoint3::Check(endPointObj))) + { + PyErr_SetString(PyExc_TypeError, "PtCheckVisLOS expects two ptPoint3 objects"); + PYTHON_RETURN_ERROR; + } + pyPoint3* startPoint = pyPoint3::ConvertFrom(startPointObj); + pyPoint3* endPoint = pyPoint3::ConvertFrom(endPointObj); + return cyMisc::CheckVisLOS(*startPoint, *endPoint); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtCheckVisLOSFromCursor, "Does LOS check from where the mouse cursor is, into the screen") +{ + return cyMisc::CheckVisLOSFromCursor(); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtEnablePlanarReflections, args, "Params: on\nEnables/disables planar reflections") +{ + char on; + if (!PyArg_ParseTuple(args, "b", &on)) + { + PyErr_SetString(PyExc_TypeError, "PtEnablePlanarReflections expects a boolean"); + PYTHON_RETURN_ERROR; + } + cyMisc::EnablePlanarReflections(on != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetSupportedDisplayModes, "Returns a list of supported resolutions") +{ + std::vector res; + cyMisc::GetSupportedDisplayModes(&res); + PyObject *retVal = PyList_New(0); + for (std::vector::iterator curArg = res.begin(); curArg != res.end(); ++curArg) + { + PyObject* tup = PyTuple_New(2); + PyTuple_SetItem(tup, 0, PyInt_FromLong((long)(*curArg).Width)); + PyTuple_SetItem(tup, 1, PyInt_FromLong((long)(*curArg).Height)); + + PyList_Append(retVal, tup); + } + return retVal; +} +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetDesktopWidth, "Returns desktop width") +{ + return PyInt_FromLong((long)cyMisc::GetDesktopWidth()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetDesktopHeight, "Returns desktop height") +{ + return PyInt_FromLong((long)cyMisc::GetDesktopHeight()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetDesktopColorDepth, "Returns desktop ColorDepth") +{ + return PyInt_FromLong((long)cyMisc::GetDesktopColorDepth()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetDefaultDisplayParams, "Returns the default resolution and display settings") +{ + PipelineParams *pp = cyMisc::GetDefaultDisplayParams(); + PyObject* tup = PyTuple_New(10); + PyTuple_SetItem(tup, 0, PyInt_FromLong((long)pp->Width)); + PyTuple_SetItem(tup, 1, PyInt_FromLong((long)pp->Height)); + PyTuple_SetItem(tup, 2, PyInt_FromLong((long)pp->Windowed)); + PyTuple_SetItem(tup, 3, PyInt_FromLong((long)pp->ColorDepth)); + PyTuple_SetItem(tup, 4, PyInt_FromLong((long)pp->AntiAliasingAmount)); + PyTuple_SetItem(tup, 5, PyInt_FromLong((long)pp->AnisotropicLevel)); + PyTuple_SetItem(tup, 6, PyInt_FromLong((long)pp->TextureQuality)); + PyTuple_SetItem(tup, 7, PyInt_FromLong((long)pp->VideoQuality)); + PyTuple_SetItem(tup, 8, PyInt_FromLong((long)pp->Shadows)); + PyTuple_SetItem(tup, 9, PyInt_FromLong((long)pp->PlanarReflections)); + return tup; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetGraphicsOptions, args, "Params: width, height, colordepth, windowed, numAAsamples, numAnisoSamples, VSync\nSet the graphics options") +{ + int width = 800, height = 600, colordepth = 32, windowed = 0, numAAsamples = 0, numAnisoSamples = 0, vsync = 0; + if (!PyArg_ParseTuple(args, "iiiiiii", &width, &height, &colordepth, &windowed, &numAAsamples, &numAnisoSamples, &vsync)) + { + PyErr_SetString(PyExc_TypeError, "PtSetGraphicsOptions expects a ints for width, height, colordepth, windowed, numAAsamples, numAnisoSamples"); + PYTHON_RETURN_ERROR; + } + + cyMisc::SetGraphicsOptions(width, height, colordepth, windowed != 0, numAAsamples, numAnisoSamples, vsync != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetBehaviorNetFlags, args, "Params: behKey, netForce, netProp\nSets net flags on the associated behavior") +{ + PyObject* keyObj = NULL; + char netForce; + char netProp; + if (!PyArg_ParseTuple(args, "Obb", &keyObj, &netForce, &netProp)) + { + PyErr_SetString(PyExc_TypeError, "PtSetBehaviorNetFlags expects a ptKey, a boolean and a boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSetBehaviorNetFlags expects a ptKey, a boolean and a boolean"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + cyMisc::SetBehaviorNetFlags(*key, netForce != 0, netProp != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSendFriendInvite, args, "Params: emailAddress, toName = \"Friend\"\nSends an email with invite code") +{ + PyObject* emailObj; + PyObject* toNameObj = nil; + if (!PyArg_ParseTuple(args, "O|O", &emailObj, &toNameObj)) + { + PyErr_SetString(PyExc_TypeError, "PtSendFriendInvite expects a string and optionally another string"); + PYTHON_RETURN_ERROR; + } + + wchar emailAddr[kMaxEmailAddressLength]; + MemSet(emailAddr, 0, sizeof(emailAddr)); + + wchar toName[kMaxPlayerNameLength]; + MemSet(toName, 0, sizeof(toName)); + + // Check and see if the email address is ok + int origStrLen = 0; + if (PyUnicode_Check(emailObj)) + { + origStrLen = PyUnicode_GET_SIZE(emailObj); + PyUnicode_AsWideChar((PyUnicodeObject*)emailObj, emailAddr, arrsize(emailAddr) - 1); + } + else if (PyString_Check(emailObj)) + { + char* cAddr = PyString_AsString(emailObj); + origStrLen = StrLen(cAddr); + StrToUnicode(emailAddr, cAddr, arrsize(emailAddr)); + } + else + { + PyErr_SetString(PyExc_TypeError, "PtSendFriendInvite expects a string and optionally another string"); + PYTHON_RETURN_ERROR; + } + + if (origStrLen >= kMaxEmailAddressLength) + { + PyErr_SetString(PyExc_TypeError, "PtSendFriendInvite: Email address too long"); + PYTHON_RETURN_ERROR; + } + + // Check if the "to name" field is ok + if (toNameObj) + { + if (PyUnicode_Check(toNameObj)) + { + origStrLen = PyUnicode_GET_SIZE(toNameObj); + PyUnicode_AsWideChar((PyUnicodeObject*)toNameObj, toName, arrsize(toName) - 1); + } + else if (PyString_Check(toNameObj)) + { + char* cName = PyString_AsString(toNameObj); + origStrLen = StrLen(cName); + StrToUnicode(toName, cName, arrsize(toName)); + } + else + StrCopy(toName, L"Friend", arrsize(toName)); + } + else + StrCopy(toName, L"Friend", arrsize(toName)); + + cyMisc::SendFriendInvite(emailAddr, toName); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGuidGenerate, "Returns string representation for a new guid") +{ + return cyMisc::PyGuidGenerate(); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtGetAIAvatarsByModelName, args, "Params: modelName\nReturns a list of tuples representing the matching ai avatars") +{ + char* modelName; + if (!PyArg_ParseTuple(args, "s", &modelName)) + { + PyErr_SetString(PyExc_TypeError, "PtGetAIAvatarsByModelName expects a string"); + PYTHON_RETURN_ERROR; + } + + return cyMisc::GetAIAvatarsByModelName(modelName); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtForceVaultNodeUpdate, args, "Params: nodeId\nForces a vault node to update") +{ + unsigned nodeId; + if (!PyArg_ParseTuple(args, "I", &nodeId)) + { + PyErr_SetString(PyExc_TypeError, "PtForceVaultNodeUpdate expects an unsigned int"); + PYTHON_RETURN_ERROR; + } + + cyMisc::ForceVaultNodeUpdate(nodeId); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtVaultDownload, args, "Params: nodeId\nDownloads the vault tree of the given nodeid") +{ + unsigned nodeId; + if (!PyArg_ParseTuple(args, "I", &nodeId)) + { + PyErr_SetString(PyExc_TypeError, "PtVaultDownload expects an unsigned int"); + PYTHON_RETURN_ERROR; + } + + cyMisc::VaultDownload(nodeId); + PYTHON_RETURN_NONE; +} + + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaMethods - the python method definitions +// + +void cyMisc::AddPlasmaMethods4(std::vector &methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtRequestLOSScreen); + + PYTHON_GLOBAL_METHOD(methods, PtKillParticles); + PYTHON_GLOBAL_METHOD(methods, PtGetNumParticles); + PYTHON_GLOBAL_METHOD(methods, PtSetParticleOffset); + + PYTHON_GLOBAL_METHOD(methods, PtSetLightValue); + PYTHON_GLOBAL_METHOD(methods, PtSetLightAnimStart); + + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsSinglePlayerMode); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsDemoMode); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsInternalRelease); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsEnterChatModeKeyBound); + + PYTHON_GLOBAL_METHOD(methods, PtShootBulletFromScreen); + PYTHON_GLOBAL_METHOD(methods, PtShootBulletFromObject); + + PYTHON_GLOBAL_METHOD(methods, PtGetPublicAgeList); + PYTHON_GLOBAL_METHOD(methods, PtCreatePublicAge); + PYTHON_GLOBAL_METHOD(methods, PtRemovePublicAge); + + PYTHON_GLOBAL_METHOD(methods, PtSetClearColor); + + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetLocalKILevel); + + PYTHON_BASIC_GLOBAL_METHOD(methods, PtClearCameraStack); + PYTHON_GLOBAL_METHOD(methods, PtGetCameraNumber); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetNumCameras); + PYTHON_GLOBAL_METHOD(methods, PtRebuildCameraStack); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtRecenterCamera); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtFirstPerson); + + PYTHON_GLOBAL_METHOD(methods, PtFadeIn); + PYTHON_GLOBAL_METHOD(methods, PtFadeOut); + + PYTHON_GLOBAL_METHOD(methods, PtSetGlobalClickability); + PYTHON_GLOBAL_METHOD(methods, PtDebugAssert); + PYTHON_GLOBAL_METHOD(methods, PtSetAlarm); + + PYTHON_GLOBAL_METHOD(methods, PtSaveScreenShot); + PYTHON_GLOBAL_METHOD(methods, PtStartScreenCapture); + + PYTHON_GLOBAL_METHOD(methods, PtSendKIGZMarkerMsg); + PYTHON_GLOBAL_METHOD(methods, PtSendKIRegisterImagerMsg); + + PYTHON_GLOBAL_METHOD(methods, PtWearMaintainerSuit); + PYTHON_GLOBAL_METHOD(methods, PtWearDefaultClothing); + + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetAgeTimeOfDayPercent); + + PYTHON_GLOBAL_METHOD(methods, PtCheckVisLOS); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtCheckVisLOSFromCursor); + + PYTHON_GLOBAL_METHOD(methods, PtEnablePlanarReflections); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetSupportedDisplayModes); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetDesktopWidth); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetDesktopHeight); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetDesktopColorDepth); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetDefaultDisplayParams); + PYTHON_GLOBAL_METHOD(methods, PtSetGraphicsOptions); + + PYTHON_GLOBAL_METHOD(methods, PtSetBehaviorNetFlags); + PYTHON_GLOBAL_METHOD(methods, PtSendFriendInvite); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGuidGenerate); + PYTHON_GLOBAL_METHOD(methods, PtGetAIAvatarsByModelName); + PYTHON_GLOBAL_METHOD(methods, PtForceVaultNodeUpdate); + PYTHON_GLOBAL_METHOD(methods, PtVaultDownload); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSys.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSys.cpp new file mode 100644 index 00000000..e3c6126e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSys.cpp @@ -0,0 +1,148 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyParticleSys.h" + +#include "hsStream.h" +#include "../pnMessage/plMessage.h" +#include "hsResMgr.h" +#include "plgDispatch.h" +#include "../plMessage/plParticleUpdateMsg.h" + +cyParticleSys::cyParticleSys(plKey sender, plKey recvr) +{ + SetSender(sender); + AddRecvr(recvr); + fNetForce = false; +} + +// setters +void cyParticleSys::SetSender(plKey &sender) +{ + fSender = sender; +} + +void cyParticleSys::AddRecvr(plKey &recvr) +{ + if ( recvr != nil ) + fRecvr.Append(recvr); +} + +void cyParticleSys::SetNetForce(hsBool state) +{ + // set our flag + fNetForce = state; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ISendParticleSysMsg +// PARAMETERS : +// +// PURPOSE : send the message to the Particle System +// +void cyParticleSys::ISendParticleSysMsg(UInt32 param, hsScalar value) +{ + plParticleUpdateMsg* pMsg = TRACKED_NEW plParticleUpdateMsg(fSender, nil, nil, param, value); + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + plgDispatch::MsgSend(pMsg); +} + +///////////////////////////////////////////////////////////////////////////// +// +// All these methods just call the IsendParticleSysMsg to do the real work +// +void cyParticleSys::SetParticlesPerSecond(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamParticlesPerSecond,value); +} + +void cyParticleSys::SetInitPitchRange(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamInitPitchRange,value); +} + +void cyParticleSys::SetInitYawRange(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamInitYawRange,value); +} + +void cyParticleSys::SetVelMin(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamVelMin,value); +} + +void cyParticleSys::SetVelMax(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamVelMax,value); +} + +void cyParticleSys::SetXSize(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamXSize,value); +} + +void cyParticleSys::SetYSize(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamYSize,value); +} + +void cyParticleSys::SetScaleMin(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamScaleMin,value); +} + +void cyParticleSys::SetScaleMax(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamScaleMax,value); +} + +void cyParticleSys::SetGenLife(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamGenLife,value); +} + +void cyParticleSys::SetPartLifeMin(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamPartLifeMin,value); +} + +void cyParticleSys::SetPartLifeMax(hsScalar value) +{ + ISendParticleSysMsg(plParticleUpdateMsg::kParamPartLifeMax,value); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSys.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSys.h new file mode 100644 index 00000000..7f320acf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSys.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef cyParticleSys_h +#define cyParticleSys_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: cyParticleSys +// +// PURPOSE: Class wrapper to for Particle System +// +#include "hsTemplates.h" + +#include "../pnKeyedObject/plKey.h" +class pyKey; + +#include +#include "pyGlueHelpers.h" + +class cyParticleSys +{ +protected: + plKey fSender; + hsTArray fRecvr; + hsBool fNetForce; + + virtual void ISendParticleSysMsg(UInt32 param, hsScalar value); + + cyParticleSys(const plKey sender=nil,const plKey recvr=nil); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptParticle); + static PyObject *New(PyObject *sender = nil, PyObject *recvr = nil); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a cyParticleSys object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(cyParticleSys); // converts a PyObject to a cyParticleSys (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // setters + virtual void SetSender(plKey &sender); + virtual void AddRecvr(plKey &recvr); + virtual void SetNetForce(hsBool state); + + virtual void SetParticlesPerSecond(hsScalar value); + virtual void SetInitPitchRange(hsScalar value); + virtual void SetInitYawRange(hsScalar value); + virtual void SetVelMin(hsScalar value); + virtual void SetVelMax(hsScalar value); + virtual void SetXSize(hsScalar value); + virtual void SetYSize(hsScalar value); + virtual void SetScaleMin(hsScalar value); + virtual void SetScaleMax(hsScalar value); + virtual void SetGenLife(hsScalar value); + virtual void SetPartLifeMin(hsScalar value); + virtual void SetPartLifeMax(hsScalar value); + +}; + + +#endif // cyParticleSys_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSysGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSysGlue.cpp new file mode 100644 index 00000000..fa816c8f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyParticleSysGlue.cpp @@ -0,0 +1,131 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyParticleSys.h" +#include "pyKey.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptParticle, cyParticleSys); + +PYTHON_DEFAULT_NEW_DEFINITION(ptParticle, cyParticleSys) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptParticle) + +PYTHON_NO_INIT_DEFINITION(ptParticle) + +PYTHON_METHOD_DEFINITION(ptParticle, netForce, args) +{ + char forceFlag; + if (!PyArg_ParseTuple(args, "b", &forceFlag)) + { + PyErr_SetString(PyExc_TypeError, "netForce requires a boolean argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNetForce(forceFlag != 0); + PYTHON_RETURN_NONE; +} + +// define a nice little marco to do the grunt work, since all the functions are identical +#define PARTICLE_FUNC(funcName, classFunc) \ + PYTHON_METHOD_DEFINITION(ptParticle, funcName, args) \ + { \ + float val; \ + if (!PyArg_ParseTuple(args, "f", &val)) \ + { \ + PyErr_SetString(PyExc_TypeError, #funcName " expects a float"); \ + PYTHON_RETURN_ERROR; \ + } \ + self->fThis->classFunc(val); \ + PYTHON_RETURN_NONE; \ + } + +// now make all the functions using the above macro +PARTICLE_FUNC(setParticlesPerSecond, SetParticlesPerSecond) +PARTICLE_FUNC(setInitPitchRange, SetInitPitchRange) +PARTICLE_FUNC(setInitYawRange, SetInitYawRange) +PARTICLE_FUNC(setVelocityMinimum, SetVelMin) +PARTICLE_FUNC(setVelocityMaximum, SetVelMax) +PARTICLE_FUNC(setWidthSize, SetXSize) +PARTICLE_FUNC(setHeightSize, SetYSize) +PARTICLE_FUNC(setScaleMinimum, SetScaleMin) +PARTICLE_FUNC(setScaleMaximum, SetScaleMax) +PARTICLE_FUNC(setGeneratorLife, SetGenLife) +PARTICLE_FUNC(setParticleLifeMinimum, SetPartLifeMin) +PARTICLE_FUNC(setParticleLifeMaximum, SetPartLifeMax) + +PYTHON_START_METHODS_TABLE(ptParticle) + PYTHON_METHOD(ptParticle, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n" + "- This is to be used if your Python program is running on only one client\n" + "Such as a game master, only running on the client that owns a particular object"), + + PYTHON_METHOD(ptParticle, setParticlesPerSecond, "Params: value\nNEEDS DOCSTRING"), + PYTHON_METHOD(ptParticle, setInitPitchRange, "Params: value\nNEEDS DOCSTRING"), + PYTHON_METHOD(ptParticle, setInitYawRange, "Params: value\nNEEDS DOCSTRING"), + PYTHON_METHOD(ptParticle, setVelocityMinimum, "Params: value\nNEEDS DOCSTRING"), + PYTHON_METHOD(ptParticle, setVelocityMaximum, "Params: value\nNEEDS DOCSTRING"), + PYTHON_METHOD(ptParticle, setWidthSize, "Params: value\nNEEDS DOCSTRING"), + PYTHON_METHOD(ptParticle, setHeightSize, "Params: value\nNEEDS DOCSTRING"), + PYTHON_METHOD(ptParticle, setScaleMinimum, "Params: value\nNEEDS DOCSTRING"), + PYTHON_METHOD(ptParticle, setScaleMaximum, "Params: value\nNEEDS DOCSTRING"), + PYTHON_METHOD(ptParticle, setGeneratorLife, "Params: value\nNEEDS DOCSTRING"), + PYTHON_METHOD(ptParticle, setParticleLifeMinimum, "Params: value\nNEEDS DOCSTRING"), + PYTHON_METHOD(ptParticle, setParticleLifeMaximum, "Params: value\nNEEDS DOCSTRING"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptParticle, "Plasma particle system class"); + +// required functions for PyObject interoperability +PyObject *cyParticleSys::New(PyObject *sender, PyObject *recvr) +{ + ptParticle *newObj = (ptParticle*)ptParticle_type.tp_new(&ptParticle_type, NULL, NULL); + if (sender != NULL) + { + pyKey *senderKey = pyKey::ConvertFrom(sender); + newObj->fThis->SetSender(senderKey->getKey()); + } + if (recvr != NULL) + { + pyKey *recvrKey = pyKey::ConvertFrom(recvr); + newObj->fThis->AddRecvr(recvrKey->getKey()); + } + newObj->fThis->SetNetForce(false); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptParticle, cyParticleSys) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptParticle, cyParticleSys) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void cyParticleSys::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptParticle); + PYTHON_CLASS_IMPORT_END(m); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysics.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysics.cpp new file mode 100644 index 00000000..8472f7e0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysics.cpp @@ -0,0 +1,787 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyPhysics.h" + +#include "plgDispatch.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plWarpMsg.h" +#include "../plMessage/plSimInfluenceMsg.h" +#include "../plMessage/plSimStateMsg.h" +#include "../plMessage/plLinearVelocityMsg.h" +#include "../plMessage/plAngularVelocityMsg.h" + +#include "pyGeometry3.h" +#include "pyMatrix44.h" +#include "pyKey.h" +#include "hsQuat.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnKeyedObject/plKey.h" + +cyPhysics::cyPhysics(plKey sender, plKey recvr) +{ + SetSender(sender); + AddRecvr(recvr); + fNetForce = false; +} + +// setters +void cyPhysics::SetSender(plKey &sender) +{ + fSender = sender; +} + +void cyPhysics::AddRecvr(plKey &recvr) +{ + if ( recvr != nil ) + fRecvr.Append(recvr); +} + +void cyPhysics::SetNetForce(hsBool state) +{ + // set our flag + fNetForce = state; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Enable +// PARAMETERS : +// +// PURPOSE : Enable physics (must already be there) +// +void cyPhysics::EnableT(hsBool state) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plEnableMsg* pMsg = TRACKED_NEW plEnableMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + // jump back to frame 0 + pMsg->SetCmd(plEnableMsg::kPhysical); + // which way are we doin' it? + if ( state ) + pMsg->SetCmd(plEnableMsg::kEnable); + else + pMsg->SetCmd(plEnableMsg::kDisable); + // make sure to propagate this to the modifiers to tell things like the clickables to disable. + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} +void cyPhysics::Enable() +{ + EnableT(true); +} + +void cyPhysics::Disable() +{ + EnableT(false); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Enable / Disable Collision +// +// PURPOSE : Enable / Disable collision for terrain and proxy terrain objects +// because using cyPhysics::Enable() does not work for these physical types +// +// + +void cyPhysics::EnableCollision() +{ + hsAssert(0, "Who uses this?"); + /* + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + plEventGroupEnableMsg* pMsg = TRACKED_NEW plEventGroupEnableMsg; + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + pMsg->SetFlags(plEventGroupEnableMsg::kCollideOn); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + */ +} + +void cyPhysics::DisableCollision() +{ + hsAssert(0, "Who uses this?"); + /* + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + plEventGroupEnableMsg* pMsg = TRACKED_NEW plEventGroupEnableMsg; + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + pMsg->SetFlags(plEventGroupEnableMsg::kCollideOff); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + */ +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Warp +// PARAMETERS : pos - the point to translate to +// +// PURPOSE : Suggest to physics engine where you want to place something +// +// +void cyPhysics::Warp(pyPoint3& pos) +{ + // create message + PyObject* matObj = pyMatrix44::New(); + pyMatrix44* mat = pyMatrix44::ConvertFrom(matObj); + mat->fMatrix.IdentityMatrix(); + mat->fMatrix.SetTranslate(&pos.fPoint); + WarpMat(*mat); + Py_DECREF(matObj); +} + +// warp v2 - for warping to the matching transform of an object (like a reference point) +void cyPhysics::WarpObj(pyKey& obj) +{ + plKey obKey = obj.getKey(); + plSceneObject* pObj = plSceneObject::ConvertNoRef(obKey->GetObjectPtr()); + if (pObj && pObj->GetCoordinateInterface()) + { + // create message + PyObject* matObj = pyMatrix44::New(); + pyMatrix44* mat = pyMatrix44::ConvertFrom(matObj); + mat->fMatrix = pObj->GetCoordinateInterface()->GetLocalToWorld(); + WarpMat(*mat); + Py_DECREF(matObj); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : WarpMat +// PARAMETERS : mat - the matrix to translate to +// +// PURPOSE : Suggest to physics engine where you want to place something +// +// +void cyPhysics::WarpMat(pyMatrix44& mat) +{ + // must have a receiver! + if ( fRecvr.Count() > 0 ) + { + // create message + plWarpMsg* pMsg = TRACKED_NEW plWarpMsg(mat.fMatrix); + pMsg->SetWarpFlags(plWarpMsg::kFlushTransform); + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Move +// PARAMETERS : direction - vector of direction to move towards +// : distance - how far to move in that direction +// +// PURPOSE : Move the object in a direction and distance +// : if the object is physical then warp it +// : otherwise just use the coordinate interface and set the transform +// +void cyPhysics::Move(pyVector3& direction, hsScalar distance) +{ + //move each receiver (object) separately + int i; + for ( i=0; iGetObjectPtr()); + if ( obj ) + { + const plCoordinateInterface* ci = obj->GetCoordinateInterface(); + if ( ci ) + { + hsVector3 offset = direction.fVector * distance; + hsMatrix44 trans; + trans.MakeTranslateMat(&offset); + + hsMatrix44 target_matrix = ci->GetWorldToLocal(); + target_matrix = target_matrix * trans; + + // see if this has a physical interface, if so, then its physical, therefore use warp + const plSimulationInterface* si = obj->GetSimulationInterface(); + if ( si ) + { + // create message for each receiver + plWarpMsg* pMsg = TRACKED_NEW plWarpMsg(target_matrix); + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + // must have a receiver! + pMsg->AddReceiver(fRecvr[i]); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + else + { + // else just use the coordinate interface + hsMatrix44 w2l; + target_matrix.GetInverse(&w2l); + obj->SetTransform(target_matrix,w2l); + } + } + else + { + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s does not have a coordinate interface.",obj->GetKeyName()); + PyErr_SetString(PyExc_RuntimeError, errmsg); + } + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Rotate +// PARAMETERS : rad - radians to rotate +// : axis - axis to rotate around +// +// PURPOSE : Rotate the object +// : if the object is physical then warp it +// : otherwise just use the coordinate interface and set the transform +// +void cyPhysics::Rotate(hsScalar rad, pyVector3& axis) +{ + // rotate each receiver (object) separately + int i; + for ( i=0; iGetObjectPtr()); + if ( obj ) + { + const plCoordinateInterface* ci = obj->GetCoordinateInterface(); + if ( ci ) + { + hsQuat q(rad, &axis.fVector); + q.Normalize(); + hsMatrix44 rot; + q.MakeMatrix(&rot); + + hsMatrix44 target_matrix = ci->GetWorldToLocal(); + target_matrix = target_matrix * rot; + + // see if this has a physical interface, then its physical, therefore use warp + const plSimulationInterface* si = obj->GetSimulationInterface(); + if ( si ) + { + // create message for each receiver + plWarpMsg* pMsg = TRACKED_NEW plWarpMsg(target_matrix); + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + // must have a receiver! + pMsg->AddReceiver(fRecvr[i]); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + else + { + // else just use the coordinate interface + hsMatrix44 w2l; + target_matrix.GetInverse(&w2l); + obj->SetTransform(target_matrix,w2l); + } + } + else + { + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s does not have a coordinate interface.",obj->GetKeyName()); + PyErr_SetString(PyExc_RuntimeError, errmsg); + } + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Force +// PARAMETERS : +// +// PURPOSE : apply a force to the center of mass of the receiver +// +// +void cyPhysics::Force(pyVector3& force) +{ + hsAssert(0, "Who uses this?"); + // must have a receiver! +/* if ( fRecvr.Count() > 0 ) + { + // create message + plForceMsg* pMsg = TRACKED_NEW plForceMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + pMsg->SetForce(force.fVector); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +*/ +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ForceWithOffset +// PARAMETERS : +// +// PURPOSE : apply a force to the receiver as though it were being impacted at the +// : given point in global space +// +// +void cyPhysics::ForceWithOffset(pyVector3& force, pyPoint3& offset) +{ + hsAssert(0, "Who uses this?"); + // must have a receiver! +/* if ( fRecvr.Count() > 0 ) + { + // create message + plOffsetForceMsg* pMsg = TRACKED_NEW plOffsetForceMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + pMsg->SetForce(force.fVector); + pMsg->SetPoint(offset.fPoint); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +*/ +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Torque +// PARAMETERS : +// +// PURPOSE : Apply the given torque force to the body +// : The vector indicates the axes, and the magnitude indicates the strength +// +// +void cyPhysics::Torque(pyVector3& torque) +{ + hsAssert(0, "Who uses this?"); + // must have a receiver! +/* if ( fRecvr.Count() > 0 ) + { + // create message + plTorqueMsg* pMsg = TRACKED_NEW plTorqueMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + pMsg->SetTorque(torque.fVector); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +*/ +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Impulse +// PARAMETERS : +// +// PURPOSE : Add the given vector to the objects velocity +// +// +void cyPhysics::Impulse(pyVector3& impulse) +{ + hsAssert(0, "Who uses this?"); + // must have a receiver! +/* if ( fRecvr.Count() > 0 ) + { + // create message + plImpulseMsg* pMsg = TRACKED_NEW plImpulseMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + pMsg->SetImpulse(impulse.fVector); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +*/ +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ImpulseWithOffset +// PARAMETERS : +// +// PURPOSE : Apply the given impulse to the object at the given point in global space +// : Will impart torque if not applied to center of mass +// +// +void cyPhysics::ImpulseWithOffset(pyVector3& impulse, pyPoint3& offset) +{ + hsAssert(0, "Who uses this?"); + // must have a receiver! +/* if ( fRecvr.Count() > 0 ) + { + // create message + plOffsetImpulseMsg* pMsg = TRACKED_NEW plOffsetImpulseMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + pMsg->SetImpulse(impulse.fVector); + pMsg->SetPoint(offset.fPoint); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +*/ +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AngularImpulse +// PARAMETERS : +// +// PURPOSE : Add the given vector (representing a rotation axis and magnitude) +// +// +void cyPhysics::AngularImpulse(pyVector3& impulse) +{ + hsAssert(0, "Who uses this?"); + // must have a receiver! +/* if ( fRecvr.Count() > 0 ) + { + // create message + plAngularImpulseMsg* pMsg = TRACKED_NEW plAngularImpulseMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + pMsg->SetImpulse(impulse.fVector); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +*/ +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Damp +// PARAMETERS : +// +// PURPOSE : Decrease all velocities on the given object. +// : A damp factor of 0 nulls them all entirely; +// : A damp factor of 1 leaves them alone. +// +// +void cyPhysics::Damp(hsScalar damp) +{ + hsAssert(0, "Who uses this?"); + // must have a receiver! +/* if ( fRecvr.Count() > 0 ) + { + // create message + plDampMsg* pMsg = TRACKED_NEW plDampMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + pMsg->SetDamp(damp); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +*/ +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ShiftMass +// PARAMETERS : +// +// PURPOSE : Shift the center of mass of the given object by the given +// : amount in the given direction. +// +// +void cyPhysics::ShiftMass(pyVector3& offset) +{ + hsAssert(0, "Who uses this?"); + // must have a receiver! +/* if ( fRecvr.Count() > 0 ) + { + // create message + plShiftMassMsg* pMsg = TRACKED_NEW plShiftMassMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + pMsg->SetOffset(offset.fVector); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +*/ +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Suppress +// PARAMETERS : doSuppress: if true, remove the physical (see below) +// if false, add it back +// +// PURPOSE : Completely remove the physical, but keep it around so it +// can be added back later. +// +// +void cyPhysics::Suppress(bool doSuppress) +{ + EnableT(!doSuppress); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SetLinearVelocity +// PARAMETERS : velocity +// +// PURPOSE : Change the objects linear velocity to this +// +// +void cyPhysics::SetLinearVelocity(pyVector3& velocity) +{ + if ( fRecvr.Count() > 0 ) + { + // create message + plLinearVelocityMsg* pMsg = TRACKED_NEW plLinearVelocityMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + + pMsg->Velocity(velocity.fVector); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} +void cyPhysics::SetAngularVelocity(pyVector3& angVel) +{ + if ( fRecvr.Count() > 0 ) + { + // create message + plAngularVelocityMsg* pMsg = TRACKED_NEW plAngularVelocityMsg; + // check if this needs to be network forced to all clients + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + if ( fSender ) + pMsg->SetSender(fSender); + + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fRecvr[i]); + } + pMsg->AngularVelocity(angVel.fVector); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysics.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysics.h new file mode 100644 index 00000000..c14073be --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysics.h @@ -0,0 +1,133 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef cyPhysics_h +#define cyPhysics_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: cyPhysics +// +// PURPOSE: Class wrapper to map animation functions to plasma2 message +// + +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" + +#include +#include "pyGlueHelpers.h" + +class pyPoint3; +class pyVector3; +class pyMatrix44; +class pyKey; + +class cyPhysics +{ +protected: + plKey fSender; + hsTArray fRecvr; + hsBool fNetForce; + + cyPhysics(plKey sender=nil,plKey recvr=nil); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptPhysics); + static PyObject *New(PyObject *sender = nil, PyObject *recvr = nil); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a cyPhysics object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(cyPhysics); // converts a PyObject to a cyPhysics (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // setters + void SetSender(plKey &sender); + void AddRecvr(plKey &recvr); + + virtual void SetNetForce(hsBool state); + + // Enable physics (must already be there) + virtual void EnableT(hsBool state); + virtual void Enable(); + virtual void Disable(); + + virtual void EnableCollision(); + virtual void DisableCollision(); + + // Suggest to physics engine where you want to place something + virtual void Warp(pyPoint3& pos); + virtual void WarpObj(pyKey& obj); + + // Suggest to physics engine where you want to place something + virtual void WarpMat(pyMatrix44& mat); + + // Move the object in a direction and distance + // if the object is physical then warp it + // otherwise just use the coordinate interface and set the transform + virtual void Move(pyVector3& direction, hsScalar distance); + + // Rotate the object + // if the object is physical then warp it + // otherwise just use the coordinate interface and set the transform + virtual void Rotate(hsScalar rad, pyVector3& axis); + + // apply a force to the center of mass of the receiver + virtual void Force(pyVector3& force); + + // apply a force to the receiver as though it were being impacted at the + // given point in global space + virtual void ForceWithOffset(pyVector3& force, pyPoint3& offset); + + // Apply the given torque force to the body + // The vector indicates the axes, and the magnitude indicates the strength + virtual void Torque(pyVector3& torque); + + // Add the given vector to the objects velocity + virtual void Impulse(pyVector3& impulse); + + // Apply the given impulse to the object at the given point in global space + // Will impart torque if not applied to center of mass + virtual void ImpulseWithOffset(pyVector3& impulse, pyPoint3& offset); + + // Add the given vector (representing a rotation axis and magnitude) + virtual void AngularImpulse(pyVector3& impulse); + + // Decrease all velocities on the given object. + // A damp factor of 0 nulls them all entirely; + // A damp factor of 1 leaves them alone. + virtual void Damp(hsScalar damp); + + // Shift the center of mass of the given object by the given + // amount in the given direction. + virtual void ShiftMass(pyVector3& offset); + + virtual void Suppress(bool doSuppress); + + //Set the Linear Velocity of the Object + virtual void SetLinearVelocity(pyVector3& velocity); + virtual void SetAngularVelocity(pyVector3& angVel); +}; + +#endif // cyPhysics_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysicsGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysicsGlue.cpp new file mode 100644 index 00000000..15f76634 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPhysicsGlue.cpp @@ -0,0 +1,407 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "cyPhysics.h" +#include "pyKey.h" +#include "pyGeometry3.h" +#include "pyMatrix44.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptPhysics, cyPhysics); + +PYTHON_DEFAULT_NEW_DEFINITION(ptPhysics, cyPhysics) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptPhysics) + +PYTHON_INIT_DEFINITION(ptPhysics, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, netForce, args) +{ + char forceFlag; + if (!PyArg_ParseTuple(args, "b", &forceFlag)) + { + PyErr_SetString(PyExc_TypeError, "netForce requires a boolean argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNetForce(forceFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, enable, args) +{ + char stateFlag = 1; + if (!PyArg_ParseTuple(args, "|b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "enable expects an optional boolean argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->EnableT(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptPhysics, disable, Disable) + +PYTHON_BASIC_METHOD_DEFINITION(ptPhysics, disableCollision, DisableCollision) +PYTHON_BASIC_METHOD_DEFINITION(ptPhysics, enableCollision, EnableCollision) + +PYTHON_METHOD_DEFINITION(ptPhysics, warp, args) +{ + PyObject *positionObject = NULL; + if (!PyArg_ParseTuple(args, "O", &positionObject)) + { + PyErr_SetString(PyExc_TypeError, "warp expects a ptPoint3 or ptMatrix44 object"); + PYTHON_RETURN_ERROR; + } + if (pyPoint3::Check(positionObject)) + { + pyPoint3 *pos = pyPoint3::ConvertFrom(positionObject); + self->fThis->Warp(*pos); + PYTHON_RETURN_NONE; + } + else if (pyMatrix44::Check(positionObject)) + { + pyMatrix44 *mat = pyMatrix44::ConvertFrom(positionObject); + self->fThis->WarpMat(*mat); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "warp expects a ptPoint3 or ptMatrix44 object"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, warpObj, args) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "warpObj expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "warpObj expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->WarpObj(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, move, args) +{ + PyObject *directionObject = NULL; + float distance; + if (!PyArg_ParseTuple(args, "Of", &directionObject, &distance)) + { + PyErr_SetString(PyExc_TypeError, "move expects a ptVector3 and float"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(directionObject)) + { + PyErr_SetString(PyExc_TypeError, "move expects a ptVector3 and float"); + PYTHON_RETURN_ERROR; + } + pyVector3 *direction = pyVector3::ConvertFrom(directionObject); + self->fThis->Move(*direction, distance); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, rotate, args) +{ + float radians; + PyObject *axisObject = NULL; + if (!PyArg_ParseTuple(args, "fO", &radians, &axisObject)) + { + PyErr_SetString(PyExc_TypeError, "rotate expects a float and ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(axisObject)) + { + PyErr_SetString(PyExc_TypeError, "rotate expects a float and ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *axis = pyVector3::ConvertFrom(axisObject); + self->fThis->Rotate(radians, *axis); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, force, args) +{ + PyObject *forceObject = NULL; + if (!PyArg_ParseTuple(args, "O", &forceObject)) + { + PyErr_SetString(PyExc_TypeError, "force expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(forceObject)) + { + PyErr_SetString(PyExc_TypeError, "force expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *force = pyVector3::ConvertFrom(forceObject); + self->fThis->Force(*force); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, forceWithOffset, args) +{ + PyObject *forceObject = NULL; + PyObject *offsetObject = NULL; + if (!PyArg_ParseTuple(args, "OO", &forceObject, &offsetObject)) + { + PyErr_SetString(PyExc_TypeError, "forceWithOffset expects a ptVector3 and a ptPoint3"); + PYTHON_RETURN_ERROR; + } + if ((!pyVector3::Check(forceObject)) || (!pyPoint3::Check(offsetObject))) + { + PyErr_SetString(PyExc_TypeError, "forceWithOffset expects a ptVector3 and a ptPoint3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *force = pyVector3::ConvertFrom(forceObject); + pyPoint3 *offset = pyPoint3::ConvertFrom(offsetObject); + self->fThis->ForceWithOffset(*force, *offset); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, torque, args) +{ + PyObject *torqueObject = NULL; + if (!PyArg_ParseTuple(args, "O", &torqueObject)) + { + PyErr_SetString(PyExc_TypeError, "torque expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(torqueObject)) + { + PyErr_SetString(PyExc_TypeError, "torque expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *torque = pyVector3::ConvertFrom(torqueObject); + self->fThis->Torque(*torque); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, impulse, args) +{ + PyObject *forceObject = NULL; + if (!PyArg_ParseTuple(args, "O", &forceObject)) + { + PyErr_SetString(PyExc_TypeError, "impulse expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(forceObject)) + { + PyErr_SetString(PyExc_TypeError, "impulse expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *force = pyVector3::ConvertFrom(forceObject); + self->fThis->Impulse(*force); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, impulseWithOffset, args) +{ + PyObject *forceObject = NULL; + PyObject *offsetObject = NULL; + if (!PyArg_ParseTuple(args, "OO", &forceObject, &offsetObject)) + { + PyErr_SetString(PyExc_TypeError, "impulseWithOffset expects a ptVector3 and a ptPoint3"); + PYTHON_RETURN_ERROR; + } + if ((!pyVector3::Check(forceObject)) || (!pyPoint3::Check(offsetObject))) + { + PyErr_SetString(PyExc_TypeError, "impulseWithOffset expects a ptVector3 and a ptPoint3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *force = pyVector3::ConvertFrom(forceObject); + pyPoint3 *offset = pyPoint3::ConvertFrom(offsetObject); + self->fThis->ImpulseWithOffset(*force, *offset); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, angularImpulse, args) +{ + PyObject *forceObject = NULL; + if (!PyArg_ParseTuple(args, "O", &forceObject)) + { + PyErr_SetString(PyExc_TypeError, "angularImpulse expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(forceObject)) + { + PyErr_SetString(PyExc_TypeError, "angularImpulse expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *force = pyVector3::ConvertFrom(forceObject); + self->fThis->AngularImpulse(*force); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, damp, args) +{ + float damp; + if (!PyArg_ParseTuple(args, "f", &damp)) + { + PyErr_SetString(PyExc_TypeError, "damp expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->Damp(damp); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, shiftMass, args) +{ + PyObject *offestObject = NULL; + if (!PyArg_ParseTuple(args, "O", &offestObject)) + { + PyErr_SetString(PyExc_TypeError, "shiftMass expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(offestObject)) + { + PyErr_SetString(PyExc_TypeError, "shiftMass expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *offset = pyVector3::ConvertFrom(offestObject); + self->fThis->ShiftMass(*offset); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, suppress, args) +{ + char doSuppress; + if (!PyArg_ParseTuple(args, "b", &doSuppress)) + { + PyErr_SetString(PyExc_TypeError, "suppress expects a boolean"); + PYTHON_RETURN_NONE; + } + self->fThis->Suppress(doSuppress != 0); + PYTHON_RETURN_NONE; +} +PYTHON_METHOD_DEFINITION(ptPhysics, setLinearVelocity, args) +{ + PyObject *velocity = NULL; + if (!PyArg_ParseTuple(args, "O", &velocity)) + { + PyErr_SetString(PyExc_TypeError, "setVelocity expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(velocity)) + { + PyErr_SetString(PyExc_TypeError, "setVelocity expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *velocityVec = pyVector3::ConvertFrom(velocity); + self->fThis->SetLinearVelocity(*velocityVec); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPhysics, setAngularVelocity, args) +{ + PyObject *velocity = NULL; + if (!PyArg_ParseTuple(args, "O", &velocity)) + { + PyErr_SetString(PyExc_TypeError, "setAngularVelocity expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(velocity)) + { + PyErr_SetString(PyExc_TypeError, "setAngularVelocity expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *velocityVec = pyVector3::ConvertFrom(velocity); + self->fThis->SetAngularVelocity(*velocityVec); + PYTHON_RETURN_NONE; +} +PYTHON_START_METHODS_TABLE(ptPhysics) + PYTHON_METHOD(ptPhysics, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n" + "- This is to be used if your Python program is running on only one client\n" + "Such as a game master, only running on the client that owns a particular object"), + + PYTHON_METHOD(ptPhysics, enable, "Params: state=1\nSets the physics enable state for the sceneobject attached"), + PYTHON_BASIC_METHOD(ptPhysics, disable, "Disables physics on the sceneobject attached"), + + PYTHON_BASIC_METHOD(ptPhysics, disableCollision, "Disables collision detection on the attached sceneobject"), + PYTHON_BASIC_METHOD(ptPhysics, enableCollision, "Enables collision detection on the attached sceneobject"), + + PYTHON_METHOD(ptPhysics, warp, "Params: position\nWarps the sceneobject to a specified location.\n" + "'position' can be a ptPoint3 or a ptMatrix44"), + PYTHON_METHOD(ptPhysics, warpObj, "Params: objkey\nWarps the sceneobject to match the location and orientation of the specified object"), + + PYTHON_METHOD(ptPhysics, move, "Params: direction,distance\nMoves the attached sceneobject the specified distance in the specified direction"), + PYTHON_METHOD(ptPhysics, rotate, "Params: radians,axis\nRotates the attached sceneobject the specified radians around the specified axis"), + PYTHON_METHOD(ptPhysics, force, "Params: forceVector\nApplies the specified force to the attached sceneobject"), + PYTHON_METHOD(ptPhysics, forceWithOffset, "Params: forceVector,offsetPt\nApplies the specified offsetted force to the attached sceneobject"), + PYTHON_METHOD(ptPhysics, torque, "Params: torqueVector\nApplies the specified torque to the attached sceneobject"), + PYTHON_METHOD(ptPhysics, impulse, "Params: impulseVector\nAdds the given vector to the attached sceneobject's velocity"), + PYTHON_METHOD(ptPhysics, impulseWithOffset, "Params: impulseVector,offsetPt\nAdds the given vector to the attached sceneobject's velocity\n" + "with the specified offset"), + PYTHON_METHOD(ptPhysics, angularImpulse, "Params: impulseVector\nAdd the given vector (representing a rotation axis and magnitude) to\n" + "the attached sceneobject's velocity"), + PYTHON_METHOD(ptPhysics, damp, "Params: damp\nReduce all velocities on the object (0 = all stop, 1 = no effect)"), + PYTHON_METHOD(ptPhysics, shiftMass, "Params: offsetVector\nShifts the attached sceneobject's center to mass in the specified direction and distance"), + PYTHON_METHOD(ptPhysics, suppress, "Params: doSuppress\nCompletely remove the physical, but keep it around so it\n" + "can be added back later."), + PYTHON_METHOD(ptPhysics, setLinearVelocity, "Params: velocityVector\nSets the objects LinearVelocity to the specified vector"), + PYTHON_METHOD(ptPhysics, setAngularVelocity, "Params: velocityVector\nSets the objects AngularVelocity to the specified vector"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptPhysics, "Plasma physics class"); + +// required functions for PyObject interoperability +PyObject *cyPhysics::New(PyObject *sender, PyObject *recvr) +{ + ptPhysics *newObj = (ptPhysics*)ptPhysics_type.tp_new(&ptPhysics_type, NULL, NULL); + if (sender != NULL) + { + pyKey *senderKey = pyKey::ConvertFrom(sender); + newObj->fThis->SetSender(senderKey->getKey()); + } + if (recvr != NULL) + { + pyKey *recvrKey = pyKey::ConvertFrom(recvr); + newObj->fThis->AddRecvr(recvrKey->getKey()); + } + newObj->fThis->SetNetForce(false); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptPhysics, cyPhysics) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptPhysics, cyPhysics) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void cyPhysics::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptPhysics); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp new file mode 100644 index 00000000..878c179a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.cpp @@ -0,0 +1,2060 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// PythonInterface - The Python interface to the Python dll +// +// NOTE: Eventually, this will be made into a separate dll, because there should +// only be one instance of this interface. +// +#include "cyPythonInterface.h" + +#include "compile.h" +#include "marshal.h" +#include "eval.h" + +#include "pyEnum.h" + +#include "pyKey.h" +#include "cyDraw.h" +#include "cyPhysics.h" +#include "pySceneObject.h" +#include "cyMisc.h" +#include "cyCamera.h" +#include "pyNotify.h" +#include "cyAvatar.h" +#include "pyGeometry3.h" +#include "pyMatrix44.h" +#include "pyColor.h" +#include "pyDynamicText.h" +#include "cyAnimation.h" +#include "pyPlayer.h" +#include "pyImage.h" +#include "pyDniCoordinates.h" +#include "cyInputInterface.h" +#include "pySDL.h" +#include "cyAccountManagement.h" + +// GUIDialog and its controls +#include "pyGUIDialog.h" +#include "pyGUIControlButton.h" +#include "pyGUIControlDragBar.h" +#include "pyGUIControlCheckBox.h" +#include "pyGUIControlListBox.h" +#include "pyGUIControlEditBox.h" +#include "pyGUIControlMultiLineEdit.h" +#include "pyGUIControlRadioGroup.h" +#include "pyGUIControlTextBox.h" +#include "pyGUIControlValue.h" +#include "pyGUIControlDynamicText.h" +#include "pyGUIControlClickMap.h" +#include "pyGUIControlDraggable.h" +#include "pyGUIPopUpMenu.h" +#include "pyGUISkin.h" + +#include "plPythonSDLModifier.h" + +// For printing to the log +#include "../plStatusLog/plStatusLog.h" +#include "../plNetGameLib/plNetGameLib.h" + +// vault +#include "pyVaultNode.h" +#include "pyVaultFolderNode.h" +#include "pyVaultPlayerInfoListNode.h" +#include "pyVaultImageNode.h" +#include "pyVaultTextNoteNode.h" +#include "pyVaultAgeLinkNode.h" +#include "pyVaultChronicleNode.h" +#include "pyVaultPlayerInfoNode.h" +#include "pyVaultAgeInfoNode.h" +#include "pyVaultAgeInfoListNode.h" +#include "pyVaultSDLNode.h" +#include "pyVaultNodeRef.h" +#include "pyVaultMarkerGameNode.h" +#include "pyVaultSystemNode.h" + +// player vault +#include "pyVault.h" +// age vault +#include "pyAgeVault.h" + +// net linking mgr +#include "pyNetLinkingMgr.h" +#include "pyAgeInfoStruct.h" +#include "pyAgeLinkStruct.h" + +// dni info source +#include "pyDniInfoSource.h" + +// audio setting stuff +#include "pyAudioControl.h" + +//CCR stufff +#include "pyCCRMgr.h" + +// spawn point def +#include "pySpawnPointInfo.h" + +#include "pyMarkerMgr.h" +#include "pyStatusLog.h" + +// Guess what this is for :P +#include "pyJournalBook.h" + +#include "pyKeyMap.h" +#include "pyStream.h" + +#include "pyMoviePlayer.h" +#include "pyDrawControl.h" + +#include "pyWaveSet.h" +#include "pySwimCurrentInterface.h" + +#include "pyCluster.h" +#include "pyGrassShader.h" + +#include "pyScoreMgr.h" +#include "pyGameScore.h" + +#include "pyCritterBrain.h" + +// Game manager stuff +#include "Games/pyGameMgrMsg.h" +#include "Games/pyGameCliMsg.h" +#include "Games/pyGameCli.h" +#include "Games/TicTacToe/pyTTTMsg.h" +#include "Games/TicTacToe/pyTTTGame.h" +#include "Games/Heek/pyHeekMsg.h" +#include "Games/Heek/pyHeekGame.h" +#include "Games/Marker/pyMarkerMsg.h" +#include "Games/Marker/pyMarkerGame.h" +#include "Games/BlueSpiral/pyBlueSpiralMsg.h" +#include "Games/BlueSpiral/pyBlueSpiralGame.h" +#include "Games/ClimbingWall/pyClimbingWallMsg.h" +#include "Games/ClimbingWall/pyClimbingWallGame.h" +#include "Games/VarSync/pyVarSyncMsg.h" +#include "Games/VarSync/pyVarSyncGame.h" + +Int32 PythonInterface::initialized = 0; // only need to initialize all of Python once +hsBool PythonInterface::FirstTimeInit = true; // start with "this is the first time" +hsBool PythonInterface::IsInShutdown = false; // whether we are _really_ in shutdown mode + +PyMethodDef* PythonInterface::plasmaMethods = nil; // the Plasma module's methods +PyObject* PythonInterface::plasmaMod = nil; // pointer to the Plasma module +PyObject* PythonInterface::plasmaConstantsMod = nil; // pointer to the PlasmaConstants module +PyObject* PythonInterface::plasmaNetConstantsMod = nil; // pointer to the PlasmaNetConstants module +PyObject* PythonInterface::plasmaVaultConstantsMod = nil; // pointer to the PlasmaVaultConstants module +PyMethodDef* PythonInterface::plasmaGameMethods = nil; // the PlasmaGame module's methods +PyObject* PythonInterface::plasmaGameMod = nil; // python object that holds the PlasmaGame module +PyObject* PythonInterface::plasmaGameConstantsMod = nil; // python object that holds the PlasmaGameConstants module +PyObject* PythonInterface::stdOut = nil; // python object of the stdout file +PyObject* PythonInterface::stdErr = nil; // python object of the err file + +hsBool PythonInterface::debug_initialized = false; // has the debug been initialized yet? +PyObject* PythonInterface::dbgMod = nil; // display module for stdout and stderr +PyObject* PythonInterface::dbgOut = nil; +PyObject* PythonInterface::dbgSlice = nil; // time slice function for the debug window +plStatusLog* PythonInterface::dbgLog = nil; // output logfile + +#ifndef PLASMA_EXTERNAL_RELEASE +bool PythonInterface::usePythonDebugger = false; +plCyDebServer PythonInterface::debugServer; +bool PythonInterface::requestedExit = false; +#endif + +// stupid Windows.h and who started including that! +#undef DrawText + +#ifndef PLASMA_EXTERNAL_RELEASE +// Special includes for debugging +#include + +///////////////////////////////////////////////////////////////////////////// +// Our debugger callback class +class DebuggerCallback: public plCyDebServer::IDebServerCallback +{ +private: + plCyDebServer& fServer; + + PyFrameObject* fFrame; + PyObject* fExceptionInfo; + + std::string IParseCurrentException(); // returns the current exception as a string representation, and clears it + +public: + DebuggerCallback(plCyDebServer& server): fServer(server) {} + + virtual bool MsgReceive(const plCyDebMessage& msg); + virtual std::string AdjustFilename(const std::string& filename); + virtual bool CheckBreakpointCondition(const std::string& condition, std::string& error); + + virtual void InitializeBreak(); + virtual std::vector GenerateCallstack(); + virtual std::vector > GenerateGlobalsList(); + virtual std::vector > GenerateLocalsList(); + virtual std::string EvaluateVariable(const std::string& varName); + virtual void SetVariableValue(const std::string& varName, const std::string& newValue); + + void SetFrame(PyFrameObject* frame) {fFrame = frame;} + void SetExceptionInfo(PyObject* exceptionInfo) {fExceptionInfo = exceptionInfo;} +}; + +std::string DebuggerCallback::IParseCurrentException() +{ + std::string error = ""; + + if (PyErr_Occurred() == NULL) + return error; // no error occurred + + PyObject* errType = NULL; + PyObject* errVal = NULL; + PyObject* errTraceback = NULL; + PyErr_Fetch(&errType, &errVal, &errTraceback); // clears the error flag + PyErr_NormalizeException(&errType, &errVal, &errTraceback); + + if (PyErr_GivenExceptionMatches(errType, PyExc_SyntaxError)) + { + // we know how to parse out information from syntax errors + PyObject* message; + char* filename = NULL; + int lineNumber = 0; + int offset = 0; + char* text = NULL; + + if (PyTuple_Check(errVal)) + { + // nested tuple, parse out the error information + PyArg_Parse(errVal, "(O(ziiz))", &message, &filename, &lineNumber, &offset, &text); + error += PyString_AsString(message); + if (text) + error += text; + } + else + { + // probably just the error class, retrieve the message and text directly + PyObject* v; + if ((v = PyObject_GetAttrString(errVal, "msg"))) + { + error += PyString_AsString(v); + Py_DECREF(v); + } + if ((v == PyObject_GetAttrString(errVal, "text"))) + { + if (v != Py_None) + error += PyString_AsString(v); + Py_DECREF(v); + } + } + } + else if (PyClass_Check(errType)) + { + // otherwise, just return the type of error that occurred + PyClassObject* exc = (PyClassObject*)errType; + PyObject* className = exc->cl_name; + if (className) + error += PyString_AsString(className); + } + else + error = "Unknown Error"; + + // cleanup + Py_XDECREF(errType); + Py_XDECREF(errVal); + Py_XDECREF(errTraceback); + + return error; +} + +bool DebuggerCallback::MsgReceive(const plCyDebMessage& msg) +{ + switch (msg.GetMsgType()) + { + case plCyDebMessage::kMsgExit: + PythonInterface::DebuggerRequestedExit(true); + break; + } + return false; // let default handling take over +} + +std::string DebuggerCallback::AdjustFilename(const std::string& filename) +{ + // python doesn't deal with paths, so we strip out all path information + std::string retVal = filename; + std::string::size_type slashPos = filename.rfind('\\'); + if (slashPos != std::string::npos) + retVal = filename.substr(slashPos + 1); + else // no back-slashes, look for forward ones + { + slashPos = filename.rfind('/'); + if (slashPos != std::string::npos) + retVal = filename.substr(slashPos + 1); + } + return retVal; +} + +bool DebuggerCallback::CheckBreakpointCondition(const std::string& condition, std::string& error) +{ + if (!fFrame) + return true; // just break, we have no current frame? + + if (condition == "") + return true; // empty condition, break (python doesn't like empty strings) + + // initialize locals, since InitializeBreak isn't called til we break + PyFrame_FastToLocals(fFrame); + + // run the string in the current context + PyObject* result = PyRun_String(const_cast(condition.c_str()), Py_eval_input, fFrame->f_globals, fFrame->f_locals); + if (result) + { + // is the result true? + int retVal = PyObject_IsTrue(result); + Py_DECREF(result); + + return (retVal == 1); + } + + // error occurred, translate it and return + error = IParseCurrentException(); + return true; +} + +void DebuggerCallback::InitializeBreak() +{ + // do a little initialization of our frame (ensuring we get all local data) + PyFrame_FastToLocals(fFrame); +} + +std::vector DebuggerCallback::GenerateCallstack() +{ + std::vector retVal; + + // we use the frame stored for us by the trace function + PyFrameObject* curFrame = fFrame; + while (curFrame) + { + std::string filename = PyString_AsString(curFrame->f_code->co_filename); + int lineNumber = PyCode_Addr2Line(curFrame->f_code, curFrame->f_lasti); // python uses base-1 numbering, we use base-0, but for display we want base-1 + std::string functionName = PyString_AsString(curFrame->f_code->co_name); + + functionName += "("; + if (curFrame->f_code->co_argcount) + { + // we have arguments! + int argCount = __min(PyTuple_Size(curFrame->f_code->co_varnames), curFrame->f_code->co_argcount); + + for (int curArg = 0; curArg < argCount; ++curArg) + { + PyObject* argName = PyTuple_GetItem(curFrame->f_code->co_varnames, curArg); + if (argName) + { + std::string arg = PyString_AsString(argName); + if (arg == "self") + continue; // skip self, for readability + + functionName += arg; + + if (curFrame->f_locals) + { + // grab value, if our locals dictionary exists + PyObject* val = PyDict_GetItemString(curFrame->f_locals, arg.c_str()); + if (val) + { + functionName += "="; + functionName += PyString_AsString(PyObject_Str(val)); + } + } + } + + if (curArg < argCount - 1) + functionName += ", "; + } + } + functionName += ")"; + + // add it to the callstack + retVal.push_back(fServer.ConstructCallstackLine(filename, lineNumber, functionName)); + + // and step back one frame + curFrame = curFrame->f_back; + } + + return retVal; +} + +std::vector > DebuggerCallback::GenerateGlobalsList() +{ + std::vector > retVal; + if (fFrame && fFrame->f_globals) + { + int pos = 0; + PyObject* key; + PyObject* value; + while (PyDict_Next(fFrame->f_globals, &pos, &key, &value)) + { + // leave modules out of the globals display + if (key && value && !PyModule_Check(value)) + { + // leave out glue functions + if (PyObject_Compare((PyObject*)&PyCFunction_Type, PyObject_Type(value)) == 0) + continue; + + std::string keyStr = PyString_AsString(PyObject_Str(key)); + if (keyStr == "__builtins__") + continue; // skip builtins + + bool addQuotes = (PyString_Check(value) || PyUnicode_Check(value)); + + std::string valueStr = ""; + if (addQuotes) + valueStr += "\""; + valueStr += PyString_AsString(PyObject_Str(value)); + if (addQuotes) + valueStr += "\""; + + // add it to the list of pairs + retVal.push_back(std::pair(keyStr, valueStr)); + } + } + } + return retVal; +} + +std::vector > DebuggerCallback::GenerateLocalsList() +{ + std::vector > retVal; + if (fFrame && fFrame->f_locals) + { + int pos = 0; + PyObject* key; + PyObject* value; + while (PyDict_Next(fFrame->f_locals, &pos, &key, &value)) + { + // leave modules and instances out of the globals display + if (key && value && !PyModule_Check(value) && !PyInstance_Check(value)) + { + // leave out functions, classes, and types + if (PyObject_Compare((PyObject*)&PyFunction_Type, PyObject_Type(value)) == 0) + continue; + if (PyObject_Compare((PyObject*)&PyClass_Type, PyObject_Type(value)) == 0) + continue; + if (PyObject_Compare((PyObject*)&PyType_Type, PyObject_Type(value)) == 0) + continue; + + std::string keyStr = PyString_AsString(PyObject_Str(key)); + if (keyStr == "__builtins__") + continue; // skip builtins + + bool addQuotes = (PyString_Check(value) || PyUnicode_Check(value)); + + std::string valueStr = ""; + if (addQuotes) + valueStr += "\""; + valueStr += PyString_AsString(PyObject_Str(value)); + if (addQuotes) + valueStr += "\""; + + // add it to the list of pairs + retVal.push_back(std::pair(keyStr, valueStr)); + } + } + } + return retVal; +} + +std::string DebuggerCallback::EvaluateVariable(const std::string& varName) +{ + if (fFrame) + { + PyObject* evalResult = PyRun_String(const_cast(varName.c_str()), Py_eval_input, fFrame->f_globals, fFrame->f_locals); + std::string retVal = ""; + if (evalResult) + { + // convert the result to something readable + PyObject* reprObj = PyObject_Repr(evalResult); + if (reprObj) + retVal = PyString_AsString(reprObj); + else + retVal = ""; + Py_XDECREF(reprObj); + } + else + retVal = IParseCurrentException(); + Py_XDECREF(evalResult); + return retVal; + } + else + return ""; +} + +void DebuggerCallback::SetVariableValue(const std::string& varName, const std::string& newValue) +{ + std::string expression = varName + "=" + newValue; + if (fFrame) + { + PyObject* evalResult = PyRun_String(const_cast(expression.c_str()), Py_single_input, fFrame->f_globals, fFrame->f_locals); + if (evalResult) + PyFrame_LocalsToFast(fFrame, 0); // convert the locals that changed (if any) back to "fast" locals + else + PyErr_Print(); + Py_XDECREF(evalResult); + } +} + +DebuggerCallback debServerCallback(*PythonInterface::PythonDebugger()); + +// python trace function, handles most of the work required for debugging +static int PythonTraceCallback(PyObject*, PyFrameObject* frame, int what, PyObject* arg) +{ + // obj (first parameter) is always NULL for is (it's the parameter passed by the set trace function) + + // update the callback class' stored values + debServerCallback.SetFrame(frame); + debServerCallback.SetExceptionInfo(NULL); + + // translate the python what value to the debugger what value + plCyDebServer::TraceWhat debuggerWhat; + switch (what) + { + case PyTrace_LINE: + debuggerWhat = plCyDebServer::kTraceLine; + break; + + case PyTrace_CALL: + debuggerWhat = plCyDebServer::kTraceCall; + break; + + case PyTrace_RETURN: + debuggerWhat = plCyDebServer::kTraceReturn; + break; + + case PyTrace_EXCEPTION: + debuggerWhat = plCyDebServer::kTraceException; + debServerCallback.SetExceptionInfo(arg); // save off the exception information + break; + + default: + assert(!"Invalid what for python trace function"); + return 0; // pretty much ignore if they pass us a bad value + } + + std::string filename = PyString_AsString(frame->f_code->co_filename); + int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti) - 1; // python uses base-1 numbering, we use base-0 + + // now handle the trace call + PythonInterface::PythonDebugger()->Trace(debuggerWhat, filename, line, frame->f_tstate->recursion_depth); + + return 0; +} +#endif // PLASMA_EXTERNAL_RELEASE + +///////////////////////////////////////////////////////////////////////////// +// A small class that is bound to python so we can redirect stdout + +class pyOutputRedirector +{ +private: + std::string fData; + static bool fTypeCreated; + +protected: + pyOutputRedirector() {} + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptOutputRedirector); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyOutputRedirector object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyOutputRedirector); // converts a PyObject to a pyOutputRedirector (throws error if not correct type) + + void Write(std::string data) {fData += data;} + void Write(std::wstring data) + { + char* strData = hsWStringToString(data.c_str()); + Write(strData); + delete [] strData; + } + + // accessor functions for the PyObject* + + // returns the current data stored + static std::string GetData(PyObject *redirector) + { + if (!pyOutputRedirector::Check(redirector)) + return ""; // it's not a redirector object + pyOutputRedirector *obj = pyOutputRedirector::ConvertFrom(redirector); + return obj->fData; + } + + // clears the internal buffer out + static void ClearData(PyObject *redirector) + { + if (!pyOutputRedirector::Check(redirector)) + return; // it's not a redirector object + pyOutputRedirector *obj = pyOutputRedirector::ConvertFrom(redirector); + obj->fData = ""; + } +}; + +bool pyOutputRedirector::fTypeCreated = false; + +// Now for the glue for the redirector +PYTHON_CLASS_DEFINITION(ptOutputRedirector, pyOutputRedirector); + +PYTHON_DEFAULT_NEW_DEFINITION(ptOutputRedirector, pyOutputRedirector) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptOutputRedirector) + +PYTHON_INIT_DEFINITION(ptOutputRedirector, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptOutputRedirector, write, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "write expects a string or unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + self->fThis->Write(text); + delete [] text; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + char* text = PyString_AsString(textObj); + self->fThis->Write(text); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "write expects a string or unicode string"); + PYTHON_RETURN_ERROR; +} + +PYTHON_START_METHODS_TABLE(ptOutputRedirector) + PYTHON_METHOD(ptOutputRedirector, write, "Adds text to the output object"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptOutputRedirector, "A class that is used to redirect stdout and stderr"); + +// required functions for PyObject interoperability +PyObject *pyOutputRedirector::New() +{ + if (!fTypeCreated) + { + if (PyType_Ready(&ptOutputRedirector_type) < 0) + return NULL; + fTypeCreated = true; + } + ptOutputRedirector *newObj = (ptOutputRedirector*)ptOutputRedirector_type.tp_new(&ptOutputRedirector_type, NULL, NULL); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptOutputRedirector, pyOutputRedirector) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptOutputRedirector, pyOutputRedirector) + + +///////////////////////////////////////////////////////////////////////////// +// A small class that is bound to python so we can redirect stderr + +class pyErrorRedirector +{ +private: + static bool fTypeCreated; + + std::string fData; + bool fLog; + +protected: + pyErrorRedirector() : fLog(true) {} + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptErrorRedirector); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyOutputRedirector object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyErrorRedirector); // converts a PyObject to a pyOutputRedirector (throws error if not correct type) + + void SetLogging(bool log) + { + fLog = log; + } + + void Write(std::string data) + { + PyObject* stdOut = PythonInterface::GetStdOut(); + + if (stdOut && pyOutputRedirector::Check(stdOut)) + { + pyOutputRedirector *obj = pyOutputRedirector::ConvertFrom(stdOut); + obj->Write(data); + } + + if (fLog) + fData += data; + } + + void Write(std::wstring data) + { + char* strData = hsWStringToString(data.c_str()); + Write(strData); + delete [] strData; + } + + void ExceptHook(PyObject* except, PyObject* val, PyObject* tb) + { + PyErr_Display(except, val, tb); + + // Send to the log server + wchar* wdata = hsStringToWString(fData.c_str()); + NetCliAuthLogPythonTraceback(wdata); + delete [] wdata; + + if (fLog) + fData.clear(); + } +}; + +bool pyErrorRedirector::fTypeCreated = false; + +// Now for the glue for the redirector +PYTHON_CLASS_DEFINITION(ptErrorRedirector, pyErrorRedirector); + +PYTHON_DEFAULT_NEW_DEFINITION(ptErrorRedirector, pyErrorRedirector) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptErrorRedirector) + +PYTHON_INIT_DEFINITION(ptErrorRedirector, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptErrorRedirector, write, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "write expects a string or unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + self->fThis->Write(text); + delete [] text; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + char* text = PyString_AsString(textObj); + self->fThis->Write(text); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "write expects a string or unicode string"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION(ptErrorRedirector, excepthook, args) +{ + PyObject *exc, *value, *tb; + if (!PyArg_ParseTuple(args, "OOO", &exc, &value, &tb)) + PYTHON_RETURN_ERROR; + + self->fThis->ExceptHook(exc, value, tb); + + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptErrorRedirector) + PYTHON_METHOD(ptErrorRedirector, write, "Adds text to the output object"), + PYTHON_METHOD(ptErrorRedirector, excepthook, "Handles exceptions"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptErrorRedirector, "A class that is used to redirect stdout and stderr"); + +// required functions for PyObject interoperability +PyObject *pyErrorRedirector::New() +{ + if (!fTypeCreated) + { + if (PyType_Ready(&ptErrorRedirector_type) < 0) + return NULL; + fTypeCreated = true; + } + ptErrorRedirector *newObj = (ptErrorRedirector*)ptErrorRedirector_type.tp_new(&ptErrorRedirector_type, NULL, NULL); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptErrorRedirector, pyErrorRedirector) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptErrorRedirector, pyErrorRedirector) + +///////////////////////////////////////////////////////////////////////////// +// +// Function : initPython +// PARAMETERS : none +// +// PURPOSE : Initialize the Python dll +// +void PythonInterface::initPython() +{ + // if haven't been initialized then do it + if ( FirstTimeInit && Py_IsInitialized() == 0 ) + { + FirstTimeInit = false; + // initialize the Python stuff + // let Python do some initialization... + Py_SetProgramName("plasma"); + Py_Initialize(); + +#ifndef PLASMA_EXTERNAL_RELEASE + if (usePythonDebugger) + { + debugServer.SetCallbackClass(&debServerCallback); + debugServer.Init(); + PyEval_SetTrace((Py_tracefunc)PythonTraceCallback, NULL); + } +#endif + + if (!dbgLog) + { + dbgLog = plStatusLogMgr::GetInstance().CreateStatusLog( 30, "Python.log", + plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kTimestamp ); + } + + // create the output redirector for the stdout and stderr file + stdOut = pyOutputRedirector::New(); + stdErr = pyErrorRedirector::New(); + + // if we need the builtins then find the builtin module + PyObject* sysmod = PyImport_ImportModule("sys"); + // then add the builtin dictionary to our module's dictionary + // get the sys's dictionary to find the stdout and stderr + PyObject* sys_dict = PyModule_GetDict(sysmod); + Py_INCREF(sys_dict); + if (stdOut != nil) + { + if (PyDict_SetItemString(sys_dict,"stdout", stdOut)) + dbgLog->AddLine("Could not redirect stdout, Python output may not appear in the log\n"); + } + else + dbgLog->AddLine("Could not create python redirector, Python output will not appear in the log\n"); + + if (stdErr != nil) + { + if (!PyDict_SetItemString(sys_dict,"stderr", stdErr)) + { + bool dontLog = false; + + // Find the excepthook + PyObject* stdErrExceptHook = PyObject_GetAttrString(stdErr, "excepthook"); + if (stdErrExceptHook) + { + if (!PyCallable_Check(stdErrExceptHook) || PyDict_SetItemString(sys_dict,"excepthook", stdErrExceptHook)) + { + dbgLog->AddLine("Could not redirect excepthook, Python error output will not get to the log server\n"); + dontLog = true; + } + Py_DECREF(stdErrExceptHook); + } + else + { + dbgLog->AddLine("Could not find stdErr excepthook, Python error output will not get to the log server\n"); + dontLog = true; + } + + if (dontLog) + { + if (pyErrorRedirector::Check(stdErr)) + { + pyErrorRedirector* redir = pyErrorRedirector::ConvertFrom(stdErr); + redir->SetLogging(false); + } + } + } + else + { + dbgLog->AddLine("Could not redirect stderr, Python error output may not appear in the log or on the log server\n"); + } + } + else + { + dbgLog->AddLine("Could not create python redirector, Python error output will not appear in the log\n"); + } + + // NOTE: we will reset the path to not include paths + // that Python may have found in the registry + PyObject* path_list = PyList_New(3); + if (PyList_SetItem(path_list, 0, PyString_FromString(".\\python"))) + { + Py_DECREF(sys_dict); + Py_DECREF(path_list); + dbgLog->AddLine("Error while creating python path:\n"); + getOutputAndReset(); + return; + } + // make sure that our plasma libraries are gotten before the system ones + if (PyList_SetItem(path_list, 1, PyString_FromString(".\\python\\plasma"))) + { + Py_DECREF(sys_dict); + Py_DECREF(path_list); + dbgLog->AddLine("Error while creating python path:\n"); + getOutputAndReset(); + return; + } + if (PyList_SetItem(path_list, 2, PyString_FromString(".\\python\\system"))) + { + Py_DECREF(sys_dict); + Py_DECREF(path_list); + dbgLog->AddLine("Error while creating python path:\n"); + getOutputAndReset(); + return; + } + + // set the path to be this one + if (PyDict_SetItemString(sys_dict,"path",path_list)) + { + Py_DECREF(sys_dict); + Py_DECREF(path_list); + dbgLog->AddLine("Error while setting python path:\n"); + getOutputAndReset(); + return; + } + + Py_DECREF(sys_dict); + Py_DECREF(path_list); + + std::vector methods; // this is temporary, for easy addition of new methods + AddPlasmaMethods(methods); + + // now copy the data to our real method definition structure + plasmaMethods = TRACKED_NEW PyMethodDef[methods.size() + 1]; + for (int curMethod = 0; curMethod < methods.size(); curMethod++) + plasmaMethods[curMethod] = methods[curMethod]; + PyMethodDef terminator = {NULL}; + plasmaMethods[methods.size()] = terminator; // add the terminator + + // now set up the module with the method data + plasmaMod = Py_InitModule("Plasma", plasmaMethods); + if (plasmaMod == NULL) + { + dbgLog->AddLine("Could not setup the Plasma module\n"); + return; + } + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while setting up Plasma:\n"); + getOutputAndReset(); + } + Py_INCREF(plasmaMod); // make sure python doesn't get rid of it + + AddPlasmaClasses(); // now add the classes to the module + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while adding classes to Plasma:\n"); + std::string error; + getOutputAndReset(&error); + } + + // initialize the PlasmaConstants module + PyMethodDef noMethods = {NULL}; + plasmaConstantsMod = Py_InitModule("PlasmaConstants", &noMethods); // it has no methods, just values + if (plasmaConstantsMod == NULL) + { + dbgLog->AddLine("Could not setup the PlasmaConstants module\n"); + return; + } + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while setting up PlasmaConstants:\n"); + std::string error; + getOutputAndReset(&error); + } + Py_INCREF(plasmaConstantsMod); + + AddPlasmaConstantsClasses(); + + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while adding classes to PlasmaConstants:\n"); + std::string error; + getOutputAndReset(&error); + } + + // initialize the PlasmaNetConstants module + plasmaNetConstantsMod = Py_InitModule("PlasmaNetConstants", &noMethods); // it has no methods, just values + if (plasmaNetConstantsMod == NULL) + { + dbgLog->AddLine("Could not setup the PlasmaNetConstants module\n"); + return; + } + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while setting up PlasmaNetConstants:\n"); + std::string error; + getOutputAndReset(&error); + } + Py_INCREF(plasmaNetConstantsMod); + + AddPlasmaNetConstantsClasses(); + + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while adding classes to PlasmaNetConstants:\n"); + std::string error; + getOutputAndReset(&error); + } + + // initialize the PlasmaVaultConstants module + plasmaVaultConstantsMod = Py_InitModule("PlasmaVaultConstants", &noMethods); // it has no methods, just values + if (plasmaVaultConstantsMod == NULL) + { + dbgLog->AddLine("Could not setup the PlasmaVaultConstants module\n"); + return; + } + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while setting up PlasmaVaultConstants:\n"); + std::string error; + getOutputAndReset(&error); + } + Py_INCREF(plasmaVaultConstantsMod); + + AddPlasmaVaultConstantsClasses(); + + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while adding classes to PlasmaVaultConstants:\n"); + std::string error; + getOutputAndReset(&error); + } + + // setup the global methods for the PlasmaGame module + methods.clear(); + AddPlasmaGameMethods(methods); + + // now copy the data to our real method definition structure + plasmaGameMethods = TRACKED_NEW PyMethodDef[methods.size() + 1]; + for (int curMethod = 0; curMethod < methods.size(); curMethod++) + plasmaGameMethods[curMethod] = methods[curMethod]; + plasmaGameMethods[methods.size()] = terminator; // add the terminator + + // initialize the PlasmaGame module + plasmaGameMod = Py_InitModule("PlasmaGame", plasmaGameMethods); + if (plasmaGameMod == NULL) + { + dbgLog->AddLine("Could not setup the PlasmaGame module\n"); + return; + } + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while setting up PlasmaGame:\n"); + std::string error; + getOutputAndReset(&error); + } + Py_INCREF(plasmaGameMod); + + AddPlasmaGameClasses(); + + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while adding classes to PlasmaGame:\n"); + std::string error; + getOutputAndReset(&error); + } + + // initialize the PlasmaGameConstants module + plasmaGameConstantsMod = Py_InitModule("PlasmaGameConstants", &noMethods); // it has no methods, just values + if (plasmaGameConstantsMod == NULL) + { + dbgLog->AddLine("Could not setup the PlasmaGameConstants module\n"); + return; + } + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while setting up PlasmaGameConstants:\n"); + std::string error; + getOutputAndReset(&error); + } + Py_INCREF(plasmaGameConstantsMod); + + AddPlasmaGameConstantsClasses(); + + if (PyErr_Occurred()) + { + dbgLog->AddLine("Python error while adding classes to PlasmaGameConstants:\n"); + std::string error; + getOutputAndReset(&error); + } + } + initialized++; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : initDebugInterface +// PARAMETERS : none +// +// PURPOSE : Initialize the Python to Plasma +// +void PythonInterface::initDebugInterface() +{ + if ( !debug_initialized ) + { + // bring up the debug window + dbgMod = PyImport_ImportModule("cydebug"); + // was there a debug module? + if ( dbgMod != nil ) + { + PyObject *dict; + // get the dictionary for this module + dict = PyModule_GetDict(dbgMod); + dbgOut = PyDict_GetItemString(dict, "writeout"); + dbgSlice = PyDict_GetItemString(dict, "timeslice"); + } + } + debug_initialized = true; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddPlasmaMethods +// PARAMETERS : none +// +// PURPOSE : Add global methods to the Plasma module +// +void PythonInterface::AddPlasmaMethods(std::vector &methods) +{ + cyMisc::AddPlasmaMethods(methods); + cyAvatar::AddPlasmaMethods(methods); + cyAccountManagement::AddPlasmaMethods(methods); + + pyDrawControl::AddPlasmaMethods(methods); + pyGUIDialog::AddPlasmaMethods(methods); + pyImage::AddPlasmaMethods(methods); + pyJournalBook::AddPlasmaMethods(methods); + pySDLModifier::AddPlasmaMethods(methods); + pySpawnPointInfo::AddPlasmaMethods(methods); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddPlasmaClasses +// PARAMETERS : none +// +// PURPOSE : Add classes to the Plasma module +// +void PythonInterface::AddPlasmaClasses() +{ + pyKey::AddPlasmaClasses(plasmaMod); + pySceneObject::AddPlasmaClasses(plasmaMod); + + pyAgeInfoStruct::AddPlasmaClasses(plasmaMod); + pyAgeInfoStructRef::AddPlasmaClasses(plasmaMod); + pyAgeLinkStruct::AddPlasmaClasses(plasmaMod); + pyAgeLinkStructRef::AddPlasmaClasses(plasmaMod); + pySpawnPointInfo::AddPlasmaClasses(plasmaMod); + pySpawnPointInfoRef::AddPlasmaClasses(plasmaMod); + + pyColor::AddPlasmaClasses(plasmaMod); + pyMatrix44::AddPlasmaClasses(plasmaMod); + pyPoint3::AddPlasmaClasses(plasmaMod); + pyVector3::AddPlasmaClasses(plasmaMod); + + cyAnimation::AddPlasmaClasses(plasmaMod); + cyAvatar::AddPlasmaClasses(plasmaMod); + cyCamera::AddPlasmaClasses(plasmaMod); + cyDraw::AddPlasmaClasses(plasmaMod); + cyInputInterface::AddPlasmaClasses(plasmaMod); + cyParticleSys::AddPlasmaClasses(plasmaMod); + cyPhysics::AddPlasmaClasses(plasmaMod); + + pyAudioControl::AddPlasmaClasses(plasmaMod); + pyCluster::AddPlasmaClasses(plasmaMod); + pyDniCoordinates::AddPlasmaClasses(plasmaMod); + pyDniInfoSource::AddPlasmaClasses(plasmaMod); + pyDynamicText::AddPlasmaClasses(plasmaMod); + pyImage::AddPlasmaClasses(plasmaMod); + pyJournalBook::AddPlasmaClasses(plasmaMod); + pyKeyMap::AddPlasmaClasses(plasmaMod); + pyMarkerMgr::AddPlasmaClasses(plasmaMod); + pyMoviePlayer::AddPlasmaClasses(plasmaMod); + pyNetLinkingMgr::AddPlasmaClasses(plasmaMod); + pyNotify::AddPlasmaClasses(plasmaMod); + pyPlayer::AddPlasmaClasses(plasmaMod); + pyStatusLog::AddPlasmaClasses(plasmaMod); + pyStream::AddPlasmaClasses(plasmaMod); + pySwimCurrentInterface::AddPlasmaClasses(plasmaMod); + pyWaveSet::AddPlasmaClasses(plasmaMod); + + // SDL + pySDLModifier::AddPlasmaClasses(plasmaMod); + pySDLStateDataRecord::AddPlasmaClasses(plasmaMod); + pySimpleStateVariable::AddPlasmaClasses(plasmaMod); + + // GUI objects + pyGUIDialog::AddPlasmaClasses(plasmaMod); + pyGUISkin::AddPlasmaClasses(plasmaMod); + pyGUIPopUpMenu::AddPlasmaClasses(plasmaMod); + // GUI base classes + pyGUIControl::AddPlasmaClasses(plasmaMod); + pyGUIControlValue::AddPlasmaClasses(plasmaMod); + // GUI derived classes + pyGUIControlButton::AddPlasmaClasses(plasmaMod); + pyGUIControlCheckBox::AddPlasmaClasses(plasmaMod); + pyGUIControlClickMap::AddPlasmaClasses(plasmaMod); + pyGUIControlDragBar::AddPlasmaClasses(plasmaMod); + pyGUIControlDraggable::AddPlasmaClasses(plasmaMod); + pyGUIControlDynamicText::AddPlasmaClasses(plasmaMod); + pyGUIControlEditBox::AddPlasmaClasses(plasmaMod); + pyGUIControlKnob::AddPlasmaClasses(plasmaMod); + pyGUIControlListBox::AddPlasmaClasses(plasmaMod); + pyGUIControlMultiLineEdit::AddPlasmaClasses(plasmaMod); + pyGUIControlProgress::AddPlasmaClasses(plasmaMod); + pyGUIControlRadioGroup::AddPlasmaClasses(plasmaMod); + pyGUIControlTextBox::AddPlasmaClasses(plasmaMod); + pyGUIControlUpDownPair::AddPlasmaClasses(plasmaMod); + + // Vault objects + pyAgeVault::AddPlasmaClasses(plasmaMod); + pyVault::AddPlasmaClasses(plasmaMod); + // Vault node base classes + pyVaultNode::AddPlasmaClasses(plasmaMod); + pyVaultNodeRef::AddPlasmaClasses(plasmaMod); + pyVaultFolderNode::AddPlasmaClasses(plasmaMod); + // Vault node derived classes + pyVaultAgeInfoListNode::AddPlasmaClasses(plasmaMod); + pyVaultAgeInfoNode::AddPlasmaClasses(plasmaMod); + pyVaultAgeLinkNode::AddPlasmaClasses(plasmaMod); + pyVaultChronicleNode::AddPlasmaClasses(plasmaMod); + pyVaultImageNode::AddPlasmaClasses(plasmaMod); + pyVaultMarkerGameNode::AddPlasmaClasses(plasmaMod); + pyVaultPlayerInfoListNode::AddPlasmaClasses(plasmaMod); + pyVaultPlayerInfoNode::AddPlasmaClasses(plasmaMod); + pyVaultSDLNode::AddPlasmaClasses(plasmaMod); + pyVaultSystemNode::AddPlasmaClasses(plasmaMod); + pyVaultTextNoteNode::AddPlasmaClasses(plasmaMod); + + // Shaders + pyGrassShader::AddPlasmaClasses(plasmaMod); + + // Game Scores + pyScoreMgr::AddPlasmaClasses(plasmaMod); + pyGameScore::AddPlasmaClasses(plasmaMod); + + // AI + pyCritterBrain::AddPlasmaClasses(plasmaMod); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddPlasmaConstantsClasses +// PARAMETERS : none +// +// PURPOSE : Initialize the PlasmaConstants module +// +void PythonInterface::AddPlasmaConstantsClasses() +{ + pyEnum::AddPlasmaConstantsClasses(plasmaConstantsMod); + + cyAvatar::AddPlasmaConstantsClasses(plasmaConstantsMod); + cyMisc::AddPlasmaConstantsClasses(plasmaConstantsMod); + cyAccountManagement::AddPlasmaConstantsClasses(plasmaConstantsMod); + + //pyDrawControl::AddPlasmaConstantsClasses(plasmaConstantsMod); + pyDynamicText::AddPlasmaConstantsClasses(plasmaConstantsMod); + pyGUIControlButton::AddPlasmaConstantsClasses(plasmaConstantsMod); + pyGUIControlMultiLineEdit::AddPlasmaConstantsClasses(plasmaConstantsMod); + pyJournalBook::AddPlasmaConstantsClasses(plasmaConstantsMod); + pyMarkerMgr::AddPlasmaConstantsClasses(plasmaConstantsMod); + pyMoviePlayer::AddPlasmaConstantsClasses(plasmaConstantsMod); + pyNotify::AddPlasmaConstantsClasses(plasmaConstantsMod); + pySDL::AddPlasmaConstantsClasses(plasmaConstantsMod); + pyStatusLog::AddPlasmaConstantsClasses(plasmaConstantsMod); + pyScoreMgr::AddPlasmaConstantsClasses(plasmaConstantsMod); + + pyAIMsg::AddPlasmaConstantsClasses(plasmaConstantsMod); + + // TODO: put these constants here. remove them from below. + //pyNetLinkingMgr::AddPlasmaConstantsClasses(plasmaConstantsMod); + //pyVault::AddPlasmaConstantsClasses(plasmaConstantsMod); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddPlasmaNetConstantsClasses +// PARAMETERS : none +// +// PURPOSE : Initialize the PlasmaNetConstants module +// +void PythonInterface::AddPlasmaNetConstantsClasses() +{ + pyNetLinkingMgr::AddPlasmaConstantsClasses(plasmaNetConstantsMod); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddPlasmaVaultConstantsClasses +// PARAMETERS : none +// +// PURPOSE : Initialize the PlasmaVaultConstants module +// +void PythonInterface::AddPlasmaVaultConstantsClasses() +{ + pyVault::AddPlasmaConstantsClasses(plasmaVaultConstantsMod); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddPlasmaGameMethods +// PARAMETERS : none +// +// PURPOSE : Add global methods to the PlasmaGame module +// +void PythonInterface::AddPlasmaGameMethods(std::vector &methods) +{ + // General + pyGameCli::AddPlasmaMethods(methods); + + // TicTacToe game + pyTTTGame::AddPlasmaMethods(methods); + + // Heek game + pyHeekGame::AddPlasmaMethods(methods); + + // Marker game + pyMarkerGame::AddPlasmaMethods(methods); + + // Blue Spiral game + pyBlueSpiralGame::AddPlasmaMethods(methods); + + // Climbing Wall game + pyClimbingWallGame::AddPlasmaMethods(methods); + + // Variable Sync game + pyVarSyncGame::AddPlasmaMethods(methods); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddPlasmaGameClasses +// PARAMETERS : none +// +// PURPOSE : Initialize the PlasmaGame module +// +void PythonInterface::AddPlasmaGameClasses() +{ + // General + pyGameMgrMsg::AddPlasmaClasses(plasmaGameMod); + pyGameMgrInviteReceivedMsg::AddPlasmaClasses(plasmaGameMod); + pyGameMgrInviteRevokedMsg::AddPlasmaClasses(plasmaGameMod); + pyGameCliMsg::AddPlasmaClasses(plasmaGameMod); + pyGameCliPlayerJoinedMsg::AddPlasmaClasses(plasmaGameMod); + pyGameCliPlayerLeftMsg::AddPlasmaClasses(plasmaGameMod); + pyGameCliInviteFailedMsg::AddPlasmaClasses(plasmaGameMod); + pyGameCliOwnerChangeMsg::AddPlasmaClasses(plasmaGameMod); + pyGameCli::AddPlasmaClasses(plasmaGameMod); + + // TicTacToe game + pyTTTMsg::AddPlasmaClasses(plasmaGameMod); + pyTTTGameStartedMsg::AddPlasmaClasses(plasmaGameMod); + pyTTTGameOverMsg::AddPlasmaClasses(plasmaGameMod); + pyTTTMoveMadeMsg::AddPlasmaClasses(plasmaGameMod); + pyTTTGame::AddPlasmaClasses(plasmaGameMod); + + // Heek game + pyHeekMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekPlayGameMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekGoodbyeMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekWelcomeMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekDropMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekSetupMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekLightStateMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekInterfaceStateMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekCountdownStateMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekWinLoseMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekGameWinMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekPointUpdateMsg::AddPlasmaClasses(plasmaGameMod); + pyHeekGame::AddPlasmaClasses(plasmaGameMod); + + // Marker game + pyMarkerMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerTemplateCreatedMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerTeamAssignedMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerGameTypeMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerGameStartedMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerGamePausedMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerGameResetMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerGameOverMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerGameNameChangedMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerTimeLimitChangedMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerGameDeletedMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerMarkerAddedMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerMarkerDeletedMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerMarkerNameChangedMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerMarkerCapturedMsg::AddPlasmaClasses(plasmaGameMod); + pyMarkerGame::AddPlasmaClasses(plasmaGameMod); + + // Blue Spiral game + pyBlueSpiralMsg::AddPlasmaClasses(plasmaGameMod); + pyBlueSpiralClothOrderMsg::AddPlasmaClasses(plasmaGameMod); + pyBlueSpiralSuccessfulHitMsg::AddPlasmaClasses(plasmaGameMod); + pyBlueSpiralGameWonMsg::AddPlasmaClasses(plasmaGameMod); + pyBlueSpiralGameOverMsg::AddPlasmaClasses(plasmaGameMod); + pyBlueSpiralGameStartedMsg::AddPlasmaClasses(plasmaGameMod); + pyBlueSpiralGame::AddPlasmaClasses(plasmaGameMod); + + // Climbing Wall game + pyClimbingWallMsg::AddPlasmaClasses(plasmaGameMod); + pyClimbingWallNumBlockersChangedMsg::AddPlasmaClasses(plasmaGameMod); + pyClimbingWallReadyMsg::AddPlasmaClasses(plasmaGameMod); + pyClimbingWallBlockersChangedMsg::AddPlasmaClasses(plasmaGameMod); + pyClimbingWallPlayerEnteredMsg::AddPlasmaClasses(plasmaGameMod); + pyClimbingWallSuitMachineLockedMsg::AddPlasmaClasses(plasmaGameMod); + pyClimbingWallGameOverMsg::AddPlasmaClasses(plasmaGameMod); + pyClimbingWallGame::AddPlasmaClasses(plasmaGameMod); + + // Variable Sync game + pyVarSyncMsg::AddPlasmaClasses(plasmaGameMod); + pyVarSyncStringVarChangedMsg::AddPlasmaClasses(plasmaGameMod); + pyVarSyncNumericVarChangedMsg::AddPlasmaClasses(plasmaGameMod); + pyVarSyncAllVarsSentMsg::AddPlasmaClasses(plasmaGameMod); + pyVarSyncStringVarCreatedMsg::AddPlasmaClasses(plasmaGameMod); + pyVarSyncNumericVarCreatedMsg::AddPlasmaClasses(plasmaGameMod); + pyVarSyncGame::AddPlasmaClasses(plasmaGameMod); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddPlasmaGameConstantsClasses +// PARAMETERS : none +// +// PURPOSE : Initialize the PlasmaGameConstants module +// +void PythonInterface::AddPlasmaGameConstantsClasses() +{ + // General + pyGameMgrMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + pyGameCliMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + pyGameCliInviteFailedMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + + // TicTacToe game + pyTTTMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + pyTTTGame::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + + // Heek game + pyHeekMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + pyHeekLightStateMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + pyHeekCountdownStateMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + pyHeekGame::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + + // Marker game + pyMarkerMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + pyMarkerGame::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + + // Blue Spiral game + pyBlueSpiralMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + + // Climbing Wall game + pyClimbingWallMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + pyClimbingWallGame::AddPlasmaConstantsClasses(plasmaGameConstantsMod); + + // Variable Sync game + pyVarSyncMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : finiPython +// PARAMETERS : none +// +// PURPOSE : Finalize the Python dll, ie. get ready to shut down +// +void PythonInterface::finiPython() +{ + // decrement the number of initializations, on last one do the finalization + initialized--; + if ( initialized < 1 && Py_IsInitialized() != 0 && IsInShutdown ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + if (usePythonDebugger) + debugServer.Disconnect(); +#endif + // remove debug module if used + if ( dbgMod ) + { + Py_DECREF(dbgMod); + dbgMod = nil; + } + + if ( stdOut ) + { + Py_DECREF(stdOut); + stdOut = nil; + } + + if ( stdErr ) + { + Py_DECREF(stdErr); + stdErr = nil; + } + + if ( plasmaMod ) + { + Py_DECREF(plasmaMod); // get rid of our reference + plasmaMod = nil; + } + + if ( plasmaConstantsMod ) + { + Py_DECREF(plasmaConstantsMod); + plasmaConstantsMod = nil; + } + + if ( plasmaNetConstantsMod ) + { + Py_DECREF(plasmaNetConstantsMod); + plasmaNetConstantsMod = nil; + } + + if ( plasmaVaultConstantsMod ) + { + Py_DECREF(plasmaVaultConstantsMod); + plasmaVaultConstantsMod = nil; + } + + if ( plasmaGameMod ) + { + Py_DECREF(plasmaGameMod); + plasmaGameMod = nil; + } + + if ( plasmaGameConstantsMod ) + { + Py_DECREF(plasmaGameConstantsMod); + plasmaGameConstantsMod = nil; + } + + // let Python clean up after itself + Py_Finalize(); + + if (plasmaMethods) + { + delete [] plasmaMethods; + plasmaMethods = nil; + } + + if (plasmaGameMethods) + { + delete [] plasmaGameMethods; + plasmaGameMethods = nil; + } + + // close done the log file, if we created one + if ( dbgLog != nil ) + { + delete dbgLog; + dbgLog = nil; + } + + initialized = 0; + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : debugTimeSlice +// PARAMETERS : none +// +// PURPOSE : give the debug window a time slice +// +void PythonInterface::debugTimeSlice() +{ + // check to see if the debug python module is loaded + if ( dbgSlice != nil ) + { + // then send it the new text + PyObject* retVal = PyObject_CallFunction(dbgSlice,nil); + if ( retVal == nil ) + { + // for some reason this function didn't, remember that and not call it again + dbgSlice = nil; + // if there was an error make sure that the stderr gets flushed so it can be seen + PyErr_Print(); // make sure the error is printed + PyErr_Clear(); // clear the error + } + Py_XDECREF(retVal); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetStdOut +// PARAMETERS : none +// +// PURPOSE : get the stdOut python object +// +PyObject* PythonInterface::GetStdOut() +{ + return stdOut; +} + +PyObject* PythonInterface::GetStdErr() +{ + return stdErr; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : getOutputAndReset +// PARAMETERS : none +// +// PURPOSE : get the Output to the error file to be displayed +// +int PythonInterface::getOutputAndReset(std::string *output) +{ + if (stdOut != nil) + { + std::string strVal = pyOutputRedirector::GetData(stdOut); + int size = strVal.length(); + dbgLog->AddLine(strVal.c_str()); + + // reset the file back to zero + pyOutputRedirector::ClearData(stdOut); + + // tell python debugger +#ifndef PLASMA_EXTERNAL_RELEASE + if (UsePythonDebugger()) + PythonInterface::PythonDebugger()->StdOut(strVal); +#endif + + // check to see if the debug python module is loaded + if ( dbgOut != nil ) + { + // then send it the new text + PyObject* retVal = PyObject_CallFunction(dbgOut,"s",strVal.c_str()); + if ( retVal == nil ) + { + // for some reason this function didn't, remember that and not call it again + dbgOut = nil; + // if there was an error make sure that the stderr gets flushed so it can be seen + PyErr_Print(); // make sure the error is printed + PyErr_Clear(); // clear the error + } + Py_XDECREF(retVal); + } + + if (output) + (*output) = strVal; + return size; + } + return 0; +} + +void PythonInterface::WriteToLog(const char* text) +{ + dbgLog->AddLine(text); +} + +void PythonInterface::WriteToStdErr(const char* text) +{ + PyObject* stdErr = PythonInterface::GetStdErr(); + if (stdErr && pyErrorRedirector::Check(stdErr)) + { + pyErrorRedirector *obj = pyErrorRedirector::ConvertFrom(stdErr); + obj->Write(text); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : FindModule +// PARAMETERS : module - module name to find +// +// PURPOSE : Find module. If it doesn't exist then don't create, return nil. +// +PyObject* PythonInterface::FindModule(char* module) +{ + PyObject *m; + // first we must get rid of any old modules of the same name, we'll replace it + PyObject *modules = PyImport_GetModuleDict(); + if ((m = PyDict_GetItemString(modules, module)) != NULL && PyModule_Check(m)) + // just return what we found + return m; + + // couldn't find the module, return None (sorta) + return nil; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IsModuleNameUnique +// PARAMETERS : module - module name to create +// +// PURPOSE : Test to see if the module name is unique +// +// Returns : True if unique , otherwise returns False +// +hsBool PythonInterface::IsModuleNameUnique(char* module) +{ + PyObject *m; + // first we must get rid of any old modules of the same name, we'll replace it + PyObject *modules = PyImport_GetModuleDict(); + if ((m = PyDict_GetItemString(modules, module)) != NULL && PyModule_Check(m)) + { + return false; + } + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : CreateModule +// PARAMETERS : module - module name to create +// +// PURPOSE : create a new module with built-ins +// +PyObject* PythonInterface::CreateModule(char* module) +{ + PyObject *m, *d; + // first we must get rid of any old modules of the same name, we'll replace it + PyObject *modules = PyImport_GetModuleDict(); + if ((m = PyDict_GetItemString(modules, module)) != NULL && PyModule_Check(m)) + { + // clear it + char message[256]; + sprintf(message,"ERROR! Creating a python module of the same name - %s",module); + hsAssert(false,message); + _PyModule_Clear(m); + } + + // create the module + m = PyImport_AddModule(module); + if (m == NULL) + return nil; + d = PyModule_GetDict(m); + // add in the built-ins + // first make sure that we don't already have the builtins + if (PyDict_GetItemString(d, "__builtins__") == NULL) + { + // if we need the builtins then find the builtin module + PyObject *bimod = PyImport_ImportModule("__builtin__"); + // then add the builtin dicitionary to our module's dictionary + if (bimod == NULL || PyDict_SetItemString(d, "__builtins__", bimod) != 0) + return nil; + Py_DECREF(bimod); + } + return m; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetPlasmaItem +// PARAMETERS : item - what item in the plasma module to get +// +// PURPOSE : get an item (probably a function) from the Plasma module +// +PyObject* PythonInterface::GetPlasmaItem(char* item) +{ + if ( plasmaMod ) + { + PyObject* d = PyModule_GetDict(plasmaMod); + return PyDict_GetItemString(d, item); + } + return nil; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetModuleItem +// PARAMETERS : item - what item in the plasma module to get +// +// PURPOSE : get an item (probably a function) from a specific module +// +PyObject* PythonInterface::GetModuleItem(char* item, PyObject* module) +{ + if ( module ) + { + PyObject* d = PyModule_GetDict(module); + return PyDict_GetItemString(d, item); + } + + return nil; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : CheckModuleForFunctions +// PARAMETERS : module - module to check for +// +// PURPOSE : checks to see if a specific function is defined in this module +// +void PythonInterface::CheckModuleForFunctions(PyObject* module, char** funcNames, PyObject** funcTable) +{ + PyObject *dict; + // get the dictionary for this module + dict = PyModule_GetDict(module); + // start looking for the functions + int i=0; + while ( funcNames[i] != nil ) + { + PyObject* func = PyDict_GetItemString(dict, funcNames[i]); + if ( func != NULL && PyCallable_Check(func)>0 ) + { + // if it is defined then mark the funcTable + funcTable[i] = func; + } + else // else we couldn't find the funtion + { + funcTable[i] = nil; + } + i++; + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : CheckInstanceForFunctions +// PARAMETERS : instance - instance of a class to check +// +// PURPOSE : checks to see if a specific function is defined in this instance of a class +// : and will fill out the funcTable with object instances of where the funciton is +// +void PythonInterface::CheckInstanceForFunctions(PyObject* instance, char** funcNames, PyObject** funcTable) +{ + // start looking for the functions + int i=0; + while ( funcNames[i] != nil ) + { + PyObject* func = PyObject_GetAttrString(instance, funcNames[i]); + if ( func != NULL ) + { + if ( PyCallable_Check(func)>0 ) + { + // if it is defined then mark the funcTable + funcTable[i] = instance; + } + Py_DECREF(func); + } + i++; + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : CompileString +// PARAMETERS : command - string of commands to execute in the... +// : filename - filename to say where to code came from +// +// PURPOSE : run a python string in a specific module name +// +PyObject* PythonInterface::CompileString(char *command, char* filename) +{ + PyObject* pycode = Py_CompileString(command, filename, Py_file_input); + return pycode; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : DumpObject +// PARAMETERS : pyobject - string of commands to execute in the... +// +// PURPOSE : marshals an object into a char string +// +hsBool PythonInterface::DumpObject(PyObject* pyobj, char** pickle, Int32* size) +{ + PyObject *s; // the python string object where the marsalled object wil go + // convert object to a marshalled string python object + s = PyMarshal_WriteObjectToString(pyobj); + // did it actually do it? + if ( s != NULL ) + { + // yes, then get the size and the string address + *size = PyString_Size(s); + *pickle = PyString_AsString(s); + return true; + } + else // otherwise, there was an error + { + // Yikes! errors! + PyErr_Print(); // FUTURE: we may have to get the string to display in max...later + return false; + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : LoadObject +// PARAMETERS : pickle - the pickled object in char string form +// : size - size of the guts to load into an object +// +// PURPOSE : Load a python object from a pickled object +// +PyObject* PythonInterface::LoadObject(char* pickle, Int32 size) +{ + return PyMarshal_ReadObjectFromString(pickle, size); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : RunStringInteractive +// PARAMETERS : command - string of commands to execute in the... +// : module - module name to run 'command' in +// +// PURPOSE : run a python string in a specific module name +// +// RETURNS : pointer to PyObject that is the result of the command +// +hsBool PythonInterface::RunStringInteractive(char *command, PyObject* module) +{ + PyObject *d, *v; + // make sure that we're given a good module... or at least one with an address + if ( !module ) + { + // if no module was given then use just use the main module + module = PyImport_AddModule("__main__"); + if (module == NULL) + return false; + } + // get the dictionaries for this module + d = PyModule_GetDict(module); + // run the string + v = PyRun_String(command, Py_single_input, d, d); + // check for errors and print them + if (v == NULL) + { + // Yikes! errors! + PyErr_Print(); + return false; + } + Py_DECREF(v); + if (Py_FlushLine()) + PyErr_Clear(); + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : RunString +// PARAMETERS : command - string of commands to execute in the... +// : module - module name to run 'command' in +// +// PURPOSE : run a python string in a specific module name +// +hsBool PythonInterface::RunString(char *command, PyObject* module) +{ + PyObject *d, *v; + // make sure that we're given a good module... or at least one with an address + if ( !module ) + { + // if no module was given then use just use the main module + module = PyImport_AddModule("__main__"); + if (module == NULL) + return false; + } + // get the dictionaries for this module + d = PyModule_GetDict(module); + // run the string + v = PyRun_String(command, Py_file_input, d, d); + // check for errors and print them + if (v == NULL) + { + // Yikes! errors! + PyErr_Print(); + return false; + } + Py_DECREF(v); + if (Py_FlushLine()) + PyErr_Clear(); + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : RunPYC +// PARAMETERS : code - compiled code +// : module - module name to run the code in +// +// PURPOSE : run a compiled python code in a specific module name +// +hsBool PythonInterface::RunPYC(PyObject* code, PyObject* module) +{ + PyObject *d, *v; + // make sure that we're given a good module... or at least one with an address + if ( !module ) + { + // if no module was given then use just use the main module + module = PyImport_AddModule("__main__"); + if (module == NULL) + return false; + } + // get the dictionaries for this module + d = PyModule_GetDict(module); + // run the string + v = PyEval_EvalCode((PyCodeObject*)code, d, d); + // check for errors and print them + if (v == NULL) + { + // Yikes! errors! + PyErr_Print(); + return false; + } + Py_DECREF(v); + if (Py_FlushLine()) + PyErr_Clear(); + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : GetpyKeyFromPython +// PARAMETERS : pkey - python object that is a pyKey (ptKey) class +// +// PURPOSE : turn a PyObject* into a pyKey* +// +pyKey* PythonInterface::GetpyKeyFromPython(PyObject* pkey) +{ + if (!pyKey::Check(pkey)) + return nil; + return pyKey::ConvertFrom(pkey); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.h new file mode 100644 index 00000000..28751ad6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/cyPythonInterface.h @@ -0,0 +1,223 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// PythonInterface - The Python interface to the Python dll +// +// NOTE: Eventually, this will be made into a separate dll, because there should +// only be one instance of this interface. +// +#include "hsTypes.h" +#include "hsStlUtils.h" +#include + +#ifndef PLASMA_EXTERNAL_RELEASE +#include "../../Apps/CyPythonIDE/plCyDebug/plCyDebServer.h" +#endif + +class plStatusLog; +class pyKey; + +class PythonInterface +{ +private: + static Int32 initialized; // count how many times we initialize + // and make sure that many finalize on the way out + static hsBool FirstTimeInit; + static hsBool IsInShutdown; // whether we are _really_ in shutdown mode + + static PyMethodDef* plasmaMethods; + static PyObject* plasmaMod; // python object that holds the Plasma module + static PyObject* plasmaConstantsMod; // python object that holds the PlasmaConstants module + static PyObject* plasmaNetConstantsMod; // python object that holds the PlasmaNetConstants module + static PyObject* plasmaVaultConstantsMod; // python object that holds the PlasmaVaultConstants module + static PyMethodDef* plasmaGameMethods; + static PyObject* plasmaGameMod; // python object that holds the PlasmaGame module + static PyObject* plasmaGameConstantsMod; // python object that holds the PlasmaGameConstants module + static PyObject* stdOut; // python object of the stdout file + static PyObject* stdErr; // python object of the err file + + static hsBool debug_initialized; // has the debug been initialized yet? + static PyObject* dbgMod; // display module for stdout and stderr + static PyObject* dbgOut; + static PyObject* dbgSlice; // time slice function for the debug window + static plStatusLog* dbgLog; + +#ifndef PLASMA_EXTERNAL_RELEASE + static bool usePythonDebugger; + static bool requestedExit; + static plCyDebServer debugServer; +#endif + +public: + + // set that we are truly shutting down + static void WeAreInShutdown() { IsInShutdown = true; } + + // Initialize the Python dll + static void initPython(); + + // Initialize the Plasma module + static void AddPlasmaMethods(std::vector &methods); + static void AddPlasmaClasses(); + + // Initialize the PlasmaConstants module + static void AddPlasmaConstantsClasses(); + + // Initialize the PlasmaNetConstants module; + static void AddPlasmaNetConstantsClasses(); + + // Initialize the PlasmaVaultConstants module; + static void AddPlasmaVaultConstantsClasses(); + + // Initialize the PlasmaGame module + static void AddPlasmaGameMethods(std::vector &methods); + static void AddPlasmaGameClasses(); + + // Initialize the PlasmaGameConstants module + static void AddPlasmaGameConstantsClasses(); + + // Initialize the Python to Plasma + static void initDebugInterface(); + + // Finalize the Python dll, ie. get ready to shut down + static void finiPython(); + + // give the debug window a time slice + static void debugTimeSlice(); + + // get the stdout PyObject + static PyObject* GetStdOut(); + static PyObject* GetStdErr(); + + // get the Output to the error file to be displayed + static int getOutputAndReset(std::string* output = nil); + + // Writes 'text' to the Python log + static void WriteToLog(const char* text); + + // Writes 'text' to stderr specified in the python interface + static void WriteToStdErr(const char* text); + + // Find module. If it doesn't exist then don't create, return nil. + static PyObject* FindModule(char* module); + + // create a new module with built-ins + static PyObject* CreateModule(char* module); + + // checks to see if a specific function is defined in this module + // get an item (probably a function) from the Plasma module + static PyObject* GetPlasmaItem(char* item); + + // Determine if the module name is unique + static hsBool IsModuleNameUnique(char* module); + // get an item (probably a function) from a specific module + static PyObject* GetModuleItem(char* item, PyObject* module); + + // check a specific module for the define funcitons + static void CheckModuleForFunctions(PyObject* module, char** funcNames, PyObject** funcTable); + + // checks to see if a specific function is defined in this instance of a class + // and will fill out the funcTable with object instances of where the funciton is + // + static void CheckInstanceForFunctions(PyObject* instance, char** funcNames, PyObject** funcTable); + + // run a python string in a specific module name + // PARAMETERS : command - string of commands to execute in the... + // : filename - filename to say where to code came from + static PyObject* CompileString(char *command, char* filename); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : DumpObject + // PARAMETERS : pyobject - string of commands to execute in the... + // + // PURPOSE : marshals an object into a char string + // + static hsBool DumpObject(PyObject* pyobj, char** pickle, Int32* size); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : LoadObject + // PARAMETERS : pickle - the pickled object in char string form + // : size - size of the guts to load into an object + // + // PURPOSE : Load a python object from a pickled object + // + static PyObject* LoadObject(char* pickle, Int32 size); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : RunStringInteractive + // PARAMETERS : command - string of commands to execute in the... + // : module - module name to run 'command' in + // + // PURPOSE : run a python string in a specific module name + // : Interactive mode (displays results) + // + static hsBool RunStringInteractive(char *command, PyObject* module); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : RunString + // PARAMETERS : command - string of commands to execute in the... + // : module - module name to run 'command' in + // + // PURPOSE : run a python string in a specific module name + // + static hsBool RunString(char *command, PyObject* module); + + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : RunPYC + // PARAMETERS : code - compiled code + // : module - module name to run the code in + // + // PURPOSE : run a compiled python code in a specific module name + // + static hsBool RunPYC(PyObject* code, PyObject* module); + + ///////////////////////////////////////////////////////////////////////////// + // + // Function : GetpyKeyFromPython + // PARAMETERS : pkey - python object that is a pyKey (ptKey) class + // + // PURPOSE : turn a PyObject* into a pyKey* + // + static pyKey* GetpyKeyFromPython(PyObject* pkey); + +#ifndef PLASMA_EXTERNAL_RELEASE + static bool UsePythonDebugger() { return usePythonDebugger; } + static void UsePythonDebugger(bool use) { usePythonDebugger = use; } + + static plCyDebServer* PythonDebugger() {return &debugServer;} + static bool DebuggerRequestedExit() {return requestedExit;} + static void DebuggerRequestedExit(bool reqExit) {requestedExit = reqExit;} +#endif +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pfPythonCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pfPythonCreatable.h new file mode 100644 index 00000000..094308af --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pfPythonCreatable.h @@ -0,0 +1,37 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPythonCreatable_inc +#define plPythonCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plPythonFileMod.h" +REGISTER_CREATABLE( plPythonFileMod ); + +#include "plPythonSDLModifier.h" +REGISTER_CREATABLE( plPythonSDLModifier ); + +#endif // plPythonCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp new file mode 100644 index 00000000..45eaf29d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp @@ -0,0 +1,2919 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// plPythonFileMod - the 'special' Python File modifier. +// +// This modifier will handle the interface to python code that has been file-ized. +// +////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStream.h" +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "../plResMgr/plKeyFinder.h" +#include "../pnKeyedObject/plKeyImp.h" +#include "../pnKeyedObject/plUoid.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plCmdIfaceModMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plModifier/plLogicModifier.h" +#include "../pfMessage/pfGUINotifyMsg.h" +#include "../plMessage/plRoomLoadNotifyMsg.h" +#include "../pfMessage/plClothingMsg.h" +#include "../pfMessage/pfKIMsg.h" +#include "../plMessage/plMemberUpdateMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../pnMessage/plRemoteAvatarInfoMsg.h" +#include "../pnMessage/plPlayerPageMsg.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../pnMessage/plSDLNotificationMsg.h" +#include "../plMessage/plNetOwnershipMsg.h" +#include "../plSDL/plSDL.h" +#include "../plVault/plVault.h" +#include "../plMessage/plCCRMsg.h" +#include "../plMessage/plVaultNotifyMsg.h" +#include "../plInputCore/plInputInterfaceMgr.h" +#include "../plInputCore/plInputDevice.h" +#include "../pfMessage/pfMarkerMsg.h" +#include "../pfMessage/pfBackdoorMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plLOSHitMsg.h" +#include "../plMessage/plRenderMsg.h" +#include "../pfMessage/pfMovieEventMsg.h" +#include "../plMessage/plClimbEventMsg.h" +#include "../plMessage/plCaptureRenderMsg.h" +#include "../plGImage/plMipmap.h" +#include "../plMessage/plAccountUpdateMsg.h" +#include "../plAgeLoader/plAgeLoader.h" +#include "../pfGameMgr/pfGameMgr.h" +#include "../plMessage/plAIMsg.h" +#include "../plAvatar/plAvBrainCritter.h" + +#include "plProfile.h" + +#include "plPythonFileMod.h" +#include "cyPythonInterface.h" +#include "pyKey.h" +#include "cyDraw.h" +#include "cyPhysics.h" +#include "pySceneObject.h" +#include "cyMisc.h" +#include "cyCamera.h" +#include "pyNotify.h" +#include "cyAvatar.h" +#include "pyGeometry3.h" +#include "pyVault.h" +#include "pyVaultNode.h" +#include "pyVaultNodeRef.h" +#include "pyVaultAgeLinkNode.h" +#include "pyPlayer.h" +#include "pyNetLinkingMgr.h" +#include "pyAgeInfoStruct.h" +#include "pyAgeLinkStruct.h" +#include "pyImage.h" +#include "pyCritterBrain.h" + +// GUI Control: +#include "pyGUIDialog.h" +#include "pyGUIControlButton.h" +#include "pyGUIControlCheckBox.h" +#include "pyGUIControlEditBox.h" +#include "pyGUIControlListBox.h" +#include "pyGUIControlRadioGroup.h" +#include "pyGUIControlTextBox.h" +#include "pyGUIControlValue.h" +#include "pyGUIControlDynamicText.h" +#include "pyGUIControlMultiLineEdit.h" +#include "pyGUIPopUpMenu.h" +#include "pyGUIControlClickMap.h" + +// Game manager +#include "Games/pyGameMgrMsg.h" +#include "Games/pyGameCliMsg.h" + +#include + +#include "plPythonSDLModifier.h" + +#include "../plMessage/plTimerCallbackMsg.h" + +plProfile_CreateTimer("Update", "Python", PythonUpdate); + +///////////////////////////////////////////////////////////////////////////// +// +// fFunctionNames - the actual names of the functions for On[event] types +// +char* plPythonFileMod::fFunctionNames[] = +{ + { "OnFirstUpdate" }, // kfunc_FirstUpdate + { "OnUpdate" }, // kfunc_Update + { "OnNotify" }, // kfunc_Notify + { "OnTimer" }, // kfunc_AtTimer + { "OnControlKeyEvent" }, // kfunc_OnKeyEvent + { "Load" }, // kfunc_Load + { "Save" }, // kfunc_Save + { "OnGUINotify" }, // kfunc_GUINotify + { "OnPageLoad" }, // kfunc_PageLoad + { "OnClothingUpdate" }, // kfunc_ClothingUpdate + { "OnKIMsg" }, // kfunc_KIMsg, + { "OnMemberUpdate" }, // kfunc_MemberUpdate, + { "OnRemoteAvatarInfo" }, // kfunc_RemoteAvatarInfo, + { "OnRTChat" }, // kfunc_RTChat, + { "OnVaultEvent" }, // kfunc_VaultEvent, + { "AvatarPage" }, // kfunc_AvatarPage, + { "OnSDLNotify" }, // kfunc_SDLNotify + { "OnOwnershipChanged" }, // kfunc_OwnershipNotify + { "OnAgeVaultEvent" }, // kfunc_AgeVaultEvent + { "OnInit" }, // kfunc_Init, + { "OnCCRMsg" }, // kfunc_OnCCRMsg, + { "OnServerInitComplete" }, // kfunc_OnServerInitComplete + { "OnVaultNotify" }, // kfunc_OnVaultNotify + { "OnDefaultKeyCaught" }, // kfunc_OnDefaultKeyCaught + { "OnMarkerMsg" }, // kfunc_OnMarkerMsg, + { "OnBackdoorMsg" }, // kfunc_OnBackdoorMsg, + { "OnBehaviorNotify" }, // kfunc_OnBehaviorNotify, + { "OnLOSNotify" }, // kfunc_OnLOSNotify, + { "BeginAgeUnLoad" }, // kfunc_OnBeginAgeLoad, + { "OnMovieEvent" }, // kfunc_OnMovieEvent, + { "OnScreenCaptureDone" }, // kfunc_OnScreenCaptureDone, + { "OnClimbingBlockerEvent"},// kFunc_OnClimbingBlockerEvent, + { "OnAvatarSpawn"}, // kFunc_OnAvatarSpawn + { "OnAccountUpdate"}, // kFunc_OnAccountUpdate + { "gotPublicAgeList"}, // kfunc_gotPublicAgeList + { "OnGameMgrMsg" }, // kfunc_OnGameMgrMsg + { "OnGameCliMsg" }, // kfunc_OnGameCliMsg + { "OnAIMsg" }, // kfunc_OnAIMsg + { nil } +}; + +//// Callback From the Vault Events ////////////////////////////////////////////// +class PythonVaultCallback : public VaultCallback +{ +protected: + plPythonFileMod* fPyFileMod; + int fFunctionIdx; + +public: + PythonVaultCallback( plPythonFileMod *pymod, int fidx ) + { + fPyFileMod = pymod; + fFunctionIdx = fidx; + } + + void AddedChildNode ( RelVaultNode * parentNode, RelVaultNode * childNode ) + { + // is there an 'OnVaultEvent' defined? + if ( fPyFileMod && fPyFileMod->fPyFunctionInstances[fFunctionIdx] != nil ) + { + PyObject* ptuple = PyTuple_New(1); + PyTuple_SetItem(ptuple, 0, pyVaultNodeRef::New(parentNode, childNode)); + // call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFileMod->fPyFunctionInstances[fFunctionIdx], + fPyFileMod->fFunctionNames[fFunctionIdx], + "lO",pyVault::kVaultNodeRefAdded,ptuple); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFileMod->fPyFunctionInstances[fFunctionIdx] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + fPyFileMod->ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(ptuple); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + fPyFileMod->DisplayPythonOutput(); + } + } + + void RemovingChildNode ( RelVaultNode * parentNode, RelVaultNode * childNode ) + { + // is there an 'OnVaultEvent' defined? + if ( fPyFileMod && fPyFileMod->fPyFunctionInstances[fFunctionIdx] != nil ) + { + PyObject* ptuple = PyTuple_New(1); + PyTuple_SetItem(ptuple, 0, pyVaultNodeRef::New(parentNode, childNode)); + // call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFileMod->fPyFunctionInstances[fFunctionIdx], + fPyFileMod->fFunctionNames[fFunctionIdx], + "lO",pyVault::kVaultRemovingNodeRef,ptuple); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFileMod->fPyFunctionInstances[fFunctionIdx] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + fPyFileMod->ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(ptuple); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + fPyFileMod->DisplayPythonOutput(); + } + } + + void ChangedNode ( RelVaultNode * changedNode ) + { + // is there an 'OnVaultEvent' defined? + if ( fPyFileMod && fPyFileMod->fPyFunctionInstances[fFunctionIdx] != nil ) + { + PyObject* ptuple = PyTuple_New(1); + PyTuple_SetItem(ptuple, 0, pyVaultNode::New(changedNode)); + // call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFileMod->fPyFunctionInstances[fFunctionIdx], + fPyFileMod->fFunctionNames[fFunctionIdx], + "lO",pyVault::kVaultNodeSaved,ptuple); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFileMod->fPyFunctionInstances[fFunctionIdx] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + fPyFileMod->ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(ptuple); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + fPyFileMod->DisplayPythonOutput(); + } + } +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Class : pfPythonKeyCatcher +// PARAMETERS : none +// +// PURPOSE : Small wrapper class to catch discarded key events and pass +// them to a plPythonFileMod +// + +class pfPythonKeyCatcher : public plDefaultKeyCatcher +{ + plPythonFileMod *fMod; + + public: + pfPythonKeyCatcher( plPythonFileMod *mod ) : fMod( mod ) {} + + virtual void HandleKeyEvent( plKeyEventMsg *event ) + { + fMod->HandleDiscardedKey( event ); + } +}; + +hsBool plPythonFileMod::fAtConvertTime = false; + +///////////////////////////////////////////////////////////////////////////// +// +// Function : plPythonFileMod and ~plPythonFileMod +// PARAMETERS : none +// +// PURPOSE : Constructor and destructor +// +plPythonFileMod::plPythonFileMod() +{ + fPythonFile = nil; + fModuleName = nil; + fModule = nil; + fLocalNotify= true; + fIsFirstTimeEval = true; + fVaultCallback = nil; + fSDLMod = nil; + fSelfKey = nil; + fInstance = nil; + fKeyCatcher = nil; + fPipe = nil; + fAmIAttachedToClone = false; + + // assume that all the functions are not available + // ...if the functions are defined in the module, then we'll call 'em + int i; + for (i=0 ; iob_refcnt > 1) + Py_DECREF(fInstance); + // then have the glue delete the instance of class + PyObject* delInst = PythonInterface::GetModuleItem("glue_delInst",fModule); + if ( delInst!=nil && PyCallable_Check(delInst) ) + { + PyObject* retVal = PyObject_CallFunction(delInst,nil); + if ( retVal == nil ) + { + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + // display any output + DisplayPythonOutput(); + } + } + fInstance = nil; + } + + // If we have a key catcher, get rid of it + delete fKeyCatcher; + fKeyCatcher = nil; + + // if we created a Vault callback, undo it and get rid of it + if (fVaultCallback) + { + // Set the callback for the vault thingy + VaultUnregisterCallback(fVaultCallback); + delete fVaultCallback; + fVaultCallback = nil; + } + + if (fSelfKey) + { + Py_DECREF(fSelfKey); + fSelfKey = nil; + } + + // get rid of the python code + if ( fPythonFile ) + { + delete [] fPythonFile; + fPythonFile = nil; + } + // then get rid of this module + // NOTE: fModule shouldn't be made in the plugin, only at runtime + if ( fModuleName && fModule ) + { + //_PyModule_Clear(fModule); + PyObject *m; + PyObject *modules = PyImport_GetModuleDict(); + if( modules && (m = PyDict_GetItemString(modules, fModuleName)) && PyModule_Check(m)) + { + hsStatusMessageF("Module %s removed from python dictionary",fModuleName); + PyDict_DelItemString(modules, fModuleName); + } + else + { + hsStatusMessageF("Module %s not found in python dictionary. Already removed?",fModuleName); + } + // the above code should have unloaded the module from python, so it will delete itself, therefore + // we need to set our pointer to nil to make sure we don't try to use it + fModule = nil; + } + delete [] fModuleName; + fModuleName = nil; +} + +#include "plPythonPack.h" + +bool plPythonFileMod::ILoadPythonCode() +{ + +#ifndef PLASMA_EXTERNAL_RELEASE + // get code from file and execute in module + // see if the file exists first before trying to import it + char pathandfile[256]; + sprintf(pathandfile, ".\\python\\%s.py",fPythonFile); + wchar *wPathandfile = hsStringToWString(pathandfile); + hsBool exists = PathDoesFileExist(wPathandfile); + delete [] wPathandfile; + if (exists) + { + char fromLoad[256]; + //sprintf(fromLoad,"from %s import *", fPythonFile); + // ok... we can't really use import because Python remembers too much where global variables came from + // ...and using execfile make it sure that globals are defined in this module and not in the imported module + sprintf(fromLoad,"execfile('.\\\\python\\\\%s.py')", fPythonFile); + if ( PythonInterface::RunString( fromLoad, fModule) ) + { + // we've loaded the code into our module + // now attach the glue python code to the end + if ( !PythonInterface::RunString("execfile('.\\\\python\\\\plasma\\\\glue.py')", fModule) ) + { + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + return false; + } + else + return true; + } + DisplayPythonOutput(); + char errMsg[256]; + sprintf(errMsg,"Python file %s.py had errors!!! Could not load.",fPythonFile); + PythonInterface::WriteToLog(errMsg); + hsAssert(0,errMsg); + return false; + } +#endif //PLASMA_EXTERNAL_RELEASE + + // Finally, try and find the file in the Python packfile + // ... for the external users .pak file is only used + PyObject* pythonCode = PythonPack::OpenPythonPacked(fPythonFile); + if (pythonCode && PythonInterface::RunPYC(pythonCode, fModule)) + return true; + + DisplayPythonOutput(); + char errMsg[256]; + sprintf(errMsg,"Python file %s.py was not found.",fPythonFile); + PythonInterface::WriteToLog(errMsg); + hsAssert(0,errMsg); + return false; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddTarget +// PARAMETERS : sobj - object to add as our target +// +// PURPOSE : Get the Key of our target +// +// NOTE: This modifier wasn't intended to have multiple targets +// +void plPythonFileMod::AddTarget(plSceneObject* sobj) +{ + plMultiModifier::AddTarget(sobj); + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAgeBeginLoadingMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + // initialize the python stuff + if ( !fAtConvertTime ) // if this is just an Add that's during a convert, then don't do anymore + { + // was there a python file module with this? + if ( fPythonFile ) + { + // has the module not been initialized yet + if ( !fModule ) + { + plKey pkey = sobj->GetKey(); + // nope, must be the first object. Then use it as the basis for the module + char modulename[256]; + IMakeModuleName(modulename,sobj); + delete [] fModuleName; + fModuleName = StrDup(modulename); + fModule = PythonInterface::CreateModule(modulename); + + // if we can't create the instance then there is nothing to do here + if (!ILoadPythonCode()) + { + // things are getting off on a bad foot... just say there wasn't a module... + fModule = nil; + return; + } + + // set the name of the file (in the global dictionary of the module) + PyObject* dict = PyModule_GetDict(fModule); + PyObject* pfilename = PyString_FromString(fPythonFile); + PyDict_SetItemString(dict, "glue_name", pfilename); + // next we need to: + // - create instance of class + PyObject* getInst = PythonInterface::GetModuleItem("glue_getInst",fModule); + fInstance = nil; + if ( getInst!=nil && PyCallable_Check(getInst) ) + { + fInstance = PyObject_CallFunction(getInst,nil); + if ( fInstance == nil ) + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + // display any output + DisplayPythonOutput(); + if ( fInstance == nil ) // then there was an error + { + // display any output (NOTE: this would be disabled in production) + char errMsg[256]; + sprintf(errMsg,"Python file %s.py, instance not found.",fPythonFile); + PythonInterface::WriteToLog(errMsg); + hsAssert(0, errMsg); + return; // if we can't create the instance then there is nothing to do here + } + + // Add the SDL modifier + if (plPythonSDLModifier::HasSDL(fPythonFile)) + { + plSceneObject* sceneObj = plSceneObject::ConvertNoRef(GetTarget(0)->GetKey()->ObjectIsLoaded()); + if (sceneObj) + { + hsAssert(!fSDLMod, "Python SDL modifier already created"); + fSDLMod = TRACKED_NEW plPythonSDLModifier(this); + sceneObj->AddModifier(fSDLMod); + } + } + + // - set the self.key and self.sceneobject in the instance of the class + // create the pyKey for this modifier + fSelfKey = pyKey::New(GetKey(),this); + // set the selfKey as an attribute to their instance + PyObject_SetAttrString(fInstance, "key", fSelfKey); + // create the sceneobject + PyObject* pSobj = pySceneObject::New(pkey, fSelfKey); + // set the sceneobject as an attribute to their instance + PyObject_SetAttrString(fInstance, "sceneobject", pSobj); + Py_DECREF(pSobj); + // set the isInitialStateLoaded to not loaded... yet + PyObject* pInitialState = PyInt_FromLong(0); + PyObject_SetAttrString(fInstance, "isInitialStateLoaded", pInitialState); + Py_DECREF(pInitialState); + // Give the SDL mod to Python + if (fSDLMod) + { + PyObject* pSDL = pySDLModifier::New(fSDLMod); + PyObject_SetAttrString(fInstance, "SDL", pSDL); + Py_DECREF(pSDL); + } + + // - set the parameters + PyObject* setParams = PythonInterface::GetModuleItem("glue_setParam",fModule); + PyObject* check_isNamed = PythonInterface::GetModuleItem("glue_isNamedAttribute",fModule); + if ( setParams!=nil && PyCallable_Check(setParams) ) + { + // loop throught the parameters and set them by id + // (will need to create the appropiate Python object for each type) + int nparam; + for ( nparam=0; nparamIsLoadingAge()) + { + NamedComponent comp; + comp.isActivator = (isNamedAttr == 1); + comp.id = parameter.fID; + comp.name = TRACKED_NEW char[strlen(parameter.datarecord.fString) + 1]; + strcpy(comp.name, parameter.datarecord.fString); + + fNamedCompQueue.Append(comp); + } + else + { + if (isNamedAttr == 1) + IFindActivatorAndAdd(parameter.datarecord.fString, parameter.fID); + else + IFindResponderAndAdd(parameter.datarecord.fString, parameter.fID); + } + } + } + // if it wasn't a named string then must be normal string type + if ( isNamedAttr == 0 ) + if ( parameter.datarecord.fString != nil ) + value = PyString_FromString(parameter.datarecord.fString); + break; + case plPythonParameter::kSceneObject: + case plPythonParameter::kSceneObjectList: + if ( parameter.fObjectKey != nil ) + { + // create the sceneobject + value = pySceneObject::New(parameter.fObjectKey, fSelfKey); + } + break; + case plPythonParameter::kActivatorList: + case plPythonParameter::kResponderList: + case plPythonParameter::kDynamicText: + case plPythonParameter::kGUIDialog: + case plPythonParameter::kExcludeRegion: + case plPythonParameter::kAnimation: + case plPythonParameter::kBehavior: + case plPythonParameter::kMaterial: + case plPythonParameter::kGUIPopUpMenu: + case plPythonParameter::kGUISkin: + case plPythonParameter::kWaterComponent: + case plPythonParameter::kSwimCurrentInterface: + case plPythonParameter::kClusterComponentList: + case plPythonParameter::kMaterialAnimation: + case plPythonParameter::kGrassShaderComponent: + if ( parameter.fObjectKey != nil ) + { + // create pyKey for the object + value = pyKey::New(parameter.fObjectKey); + } + break; + } + // if there is a value that was converted then tell the Python code + if ( value != nil ) + { + PyObject* retVal = PyObject_CallFunction(setParams,"lO", parameter.fID, value); + if ( retVal == nil ) + { + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(value); + } + } + } + + // check if we need to register named activators or responders + if (fNamedCompQueue.Count() > 0) + { + plgDispatch::Dispatch()->RegisterForExactType( plAgeLoadedMsg::Index(), GetKey() ); + } + + // - find functions in class they've defined. + PythonInterface::CheckInstanceForFunctions(fInstance,fFunctionNames,fPyFunctionInstances); + // clear any errors created by checking for methods in a class + PyErr_Clear(); // clear the error + // register for messages that they have functions defined for + // register for PageLoaded message if needed + if ( fPyFunctionInstances[kfunc_PageLoad] != nil ) + { + // register for plRoomLoadNotifyMsg + plgDispatch::Dispatch()->RegisterForExactType(plRoomLoadNotifyMsg::Index(), GetKey()); + } + + // register for ClothingUpdate message if needed + if ( fPyFunctionInstances[kfunc_ClothingUpdate] != nil ) + { + // register for plRoomLoadNotifyMsg + plgDispatch::Dispatch()->RegisterForExactType(plClothingUpdateBCMsg::Index(), GetKey()); + } + + // register for pfKIMsg message if needed + if ( fPyFunctionInstances[kfunc_KIMsg] != nil ) + { + // register for pfKIMsg + plgDispatch::Dispatch()->RegisterForExactType(pfKIMsg::Index(), GetKey()); + } + + // register for Member update message if needed + if ( fPyFunctionInstances[kfunc_MemberUpdate] != nil ) + { + // register for plMemberUpdateMsg + plgDispatch::Dispatch()->RegisterForExactType(plMemberUpdateMsg::Index(), GetKey()); + } + + // register for Remote Avatar Info message if needed + if ( fPyFunctionInstances[kfunc_RemoteAvatarInfo] != nil ) + { + // register for plRemoteAvatarInfoMsg + plgDispatch::Dispatch()->RegisterForExactType(plRemoteAvatarInfoMsg::Index(), GetKey()); + } + + // register for CCR message if needed + if ( fPyFunctionInstances[kfunc_OnCCRMsg] != nil ) + { + // register for plCCRCommunicationMsg + plgDispatch::Dispatch()->RegisterForExactType(plCCRCommunicationMsg::Index(), GetKey()); + } + + // register for VaultNotify message if needed + if ( fPyFunctionInstances[kfunc_OnVaultNotify] != nil ) + { + // register for plVaultNotifyMsg + plgDispatch::Dispatch()->RegisterForExactType(plVaultNotifyMsg::Index(), GetKey()); + } + + // register for Owndership change notification message if needed + if ( fPyFunctionInstances[kfunc_OwnershipNotify] != nil ) + { + // register for plNetOwnershipMsg + plgDispatch::Dispatch()->RegisterForExactType(plNetOwnershipMsg::Index(), GetKey()); + } + +#ifndef PLASMA_EXTERNAL_RELEASE + // register for Backdoor message if needed + if ( fPyFunctionInstances[kfunc_OnBackdoorMsg] != nil ) + { + // register for pfDebugTriggerMsg + plgDispatch::Dispatch()->RegisterForExactType(pfBackdoorMsg::Index(), GetKey()); + } +#endif //PLASMA_EXTERNAL_RELEASE + + // register for VaultCallback events if needed + if ( fPyFunctionInstances[kfunc_VaultEvent] != nil ) + { + // create the callback object + // Set the callback for the vault thingy + fVaultCallback = TRACKED_NEW PythonVaultCallback( this, kfunc_VaultEvent ); + VaultRegisterCallback(fVaultCallback); + } + + // register ourselves to be the default key catcher if necessary + if ( fPyFunctionInstances[kfunc_OnDefaultKeyCaught] != nil ) + { + // Make us a key catcher + fKeyCatcher = TRACKED_NEW pfPythonKeyCatcher( this ); + + // Tell the input interface manager to use our catcher + plInputInterfaceMgr::GetInstance()->SetDefaultKeyCatcher( fKeyCatcher ); + } + + // register for Marker messages if needed + if ( fPyFunctionInstances[kfunc_OnMarkerMsg] != nil ) + { + plgDispatch::Dispatch()->RegisterForExactType(pfMarkerMsg::Index(), GetKey()); + } + + // if they are going to get LOS hit messages then we need to get the Pipeline pointer + if ( fPyFunctionInstances[kfunc_OnLOSNotify] != nil ) + { + plgDispatch::Dispatch()->RegisterForExactType( plRenderMsg::Index(), GetKey() ); + } + + // if this is a climbing-wall function, we need to register for climbing wall messages + if ( fPyFunctionInstances[kfunc_OnClimbBlockerEvent] != nil) + { + plgDispatch::Dispatch()->RegisterForExactType( plClimbEventMsg::Index(), GetKey() ); + } + if ( fPyFunctionInstances[kfunc_OnAvatarSpawn] != nil) + { + plgDispatch::Dispatch()->RegisterForExactType( plAvatarSpawnNotifyMsg::Index(), GetKey() ); + } + if ( fPyFunctionInstances[kfunc_OnAccountUpdate] != nil) + { + plgDispatch::Dispatch()->RegisterForExactType( plAccountUpdateMsg::Index(), GetKey() ); + } + if ( fPyFunctionInstances[kfunc_gotPublicAgeList] != nil) + { + plgDispatch::Dispatch()->RegisterForExactType(plNetCommPublicAgeListMsg::Index(), GetKey()); + } + if ( fPyFunctionInstances[kfunc_OnGameMgrMsg] != nil) + { + pfGameMgr::GetInstance()->AddReceiver(GetKey()); + } + if ( fPyFunctionInstances[kfunc_OnAIMsg] != nil) + { + // the message that is spammed to anyone who will listen + plgDispatch::Dispatch()->RegisterForExactType(plAIBrainCreatedMsg::Index(), GetKey()); + } + + // As the last thing... call the OnInit function if they have one + if ( fPyFunctionInstances[kfunc_Init] != nil ) + { + plProfile_BeginTiming(PythonUpdate); + // call it + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_Init],fFunctionNames[kfunc_Init],nil); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_Init] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + } + + // display python output + DisplayPythonOutput(); + } + else + { + // else if module is already created... Then we are just adding an addition object to the already existing SceneObject + if ( fInstance ) // make sure that we have an instance already also + { + PyObject* dict = PyModule_GetDict(fModule); + // create pyKey for the object + PyObject* pkeyObj = pyKey::New(sobj->GetKey()); + // need to get the instance, that holds the sceneobject that we are attached to + PyObject* getInst = PythonInterface::GetModuleItem("glue_getInst",fModule); + // get the sceneObject that should already be created + PyObject* pSceneObject = PyObject_GetAttrString(fInstance,"sceneobject"); + // add our new object to the list of objects that are in the _selfObject + PyObject* retVal = PyObject_CallMethod(pSceneObject,"addKey","O",pkeyObj ); + Py_XDECREF(retVal); + // GetAttrString put a ref on pSceneObject, but we're done with it now. + Py_XDECREF(pSceneObject); + Py_DECREF(pkeyObj); + } + } + } + } +} + +void plPythonFileMod::RemoveTarget(plSceneObject* so) +{ + // remove sdl modifier + if (fSDLMod) + { + if (GetNumTargets() > 0) + { + plSceneObject* sceneObj = plSceneObject::ConvertNoRef(GetTarget(0)->GetKey()->ObjectIsLoaded()); + if (sceneObj && fSDLMod) + sceneObj->RemoveModifier(fSDLMod); + } + delete fSDLMod; + fSDLMod = nil; + } + + plMultiModifier::RemoveTarget(so); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : HandleDiscardedKey +// PARAMETERS : msg - the key event message that was discarded +// +// PURPOSE : API for processing discarded keys as the deafult key catcher +// + +void plPythonFileMod::HandleDiscardedKey( plKeyEventMsg *msg ) +{ + // So OnDefaultKeyCaught takes two parameters: the key character pressed and a boolean saying up or down + char keyChar = plKeyboardDevice::KeyEventToChar( msg ); + + // if the caps lock is down then reverse upper and lowercase + if ( msg->GetCapsLockKeyDown() ) + { + if ( std::islower(keyChar,std::locale()) ) + keyChar = std::toupper(keyChar,std::locale()); + else + keyChar = std::tolower(keyChar,std::locale()); + } + + if (!fPyFunctionInstances[kfunc_OnDefaultKeyCaught]) + return; + + plProfile_BeginTiming( PythonUpdate ); + + PyObject* retVal = PyObject_CallMethod( fPyFunctionInstances[ kfunc_OnDefaultKeyCaught ], + fFunctionNames[ kfunc_OnDefaultKeyCaught ], + "ciiiii", + keyChar, + (int)msg->GetKeyDown(), + (int)msg->GetRepeat(), + (int)msg->GetShiftKeyDown(), + (int)msg->GetCtrlKeyDown(), + (int)msg->GetKeyCode() ); + if( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[ kfunc_OnDefaultKeyCaught ] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + + plProfile_EndTiming( PythonUpdate ); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IMakeModuleName +// PARAMETERS : sobj - object to add as our target +// +// PURPOSE : Get the Key of our target +// +// NOTE: This modifier wasn't intended to have multiple targets +// +void plPythonFileMod::IMakeModuleName(char* modulename,plSceneObject* sobj) +{ + // Forgive my general crapulance... + // This strips underscores out of module names + // so python won't truncate them... -S + + plKey pKey = GetKey(); + plKey sKey = sobj->GetKey(); + + const char* pKeyName = pKey->GetName(); + const char* pSobjName = sKey->GetName(); + + UInt16 len = hsStrlen(pKeyName); + UInt16 slen = hsStrlen(pSobjName); + + hsAssert(len+slen < 256, "Warning: String length exceeds 256 characters."); + + int i, k = 0; + for(i = 0; i < slen; i++) + { + if(pSobjName[i] == '_') continue; + + modulename[k++] = pSobjName[i]; + } + for(i = 0; i < len; i++) + { + if(pKeyName[i] == '_') continue; + + modulename[k++] = pKeyName[i]; + } + + modulename[k] = '\0'; + + // check to see if we are attaching to a clone? + plKeyImp* pKeyImp = (plKeyImp*)(sKey); + if (pKeyImp->GetCloneOwner()) + { + // we have an owner... so we must be a clone. + // add the cloneID to the end of the module name + // and set the fIAmAClone flag + UInt32 cloneID = pKeyImp->GetUoid().GetCloneID(); + sprintf(modulename,"%s%d",modulename,cloneID); + fAmIAttachedToClone = true; + } + + // make sure that the actual modulue will be uniqie + if ( !PythonInterface::IsModuleNameUnique(modulename) ) + { + // if not unique then add the sequence number to the end of the modulename + UInt32 seqID = pKeyImp->GetUoid().GetLocation().GetSequenceNumber(); + sprintf(modulename,"%s%d",modulename,seqID); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ISetKeyValue +// PARAMETERS : key to responder, parameter id +// +// PURPOSE : set the param in the python file +// : so named stuff works +// +void plPythonFileMod::ISetKeyValue(const plKey& key, Int32 id) +{ + PyObject* setParams = PythonInterface::GetModuleItem("glue_setParam",fModule); + + if ( setParams != nil && PyCallable_Check(setParams) ) + { + if ( key != nil ) + { + // create pyKey for the object + PyObject* value = pyKey::New(key); + + if ( value != nil ) + { + PyObject* retVal = PyObject_CallFunction(setParams,"lO", id, value); + if ( retVal == nil ) + { + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(value); + } + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IFindResponderAndAdd +// PARAMETERS : ResponderName - name of the responder to find +// +// PURPOSE : find a responder by name in all age and page locations +// : and add to the Parameter list +// +void plPythonFileMod::IFindResponderAndAdd(const char *responderName, Int32 id) +{ + if ( responderName != nil ) + { + std::vector keylist; + const plLocation &loc = GetKey()->GetUoid().GetLocation(); + plKeyFinder::Instance().ReallyStupidResponderSearch(responderName,keylist,loc); // use the really stupid search to find the responder + // the keylist will be filled with all the keys that correspond to that responder + int list_size = keylist.size(); + int i; + for ( i=0 ; i keylist; + const plLocation &loc = GetKey()->GetUoid().GetLocation(); + plKeyFinder::Instance().ReallyStupidActivatorSearch(activatorName,keylist, loc); // use the really stupid search to find the responder + // the keylist will be filled with all the keys that correspond to that responder + int list_size = keylist.size(); + // create the Python object that is the list, starts as empty + int i; + for ( i=0 ; iObjectIsLoaded()); + if (logic) + { + logic->AddNotifyReceiver(this->GetKey()); + } + else // else might be a python file key + { + // next check to see if it is another PythonFileMod, and add to their notify list + plPythonFileMod *pymod = plPythonFileMod::ConvertNoRef(keylist[i]->ObjectIsLoaded()); + if (pymod) + { + pymod->AddToNotifyList(this->GetKey()); + } + else // else maybe its just not loaded yet + { + // setup a ref notify when it does get loaded + hsgResMgr::ResMgr()->AddViaNotify(keylist[i], + TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, kAddNotify, 0), + plRefFlags::kPassiveRef); + } + } + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IEval +// PARAMETERS : secs +// del +// dirty +// +// PURPOSE : This is where the main update work is done +// Tasks: +// - Call the Python code's Update function (if there) +// +hsBool plPythonFileMod::IEval(double secs, hsScalar del, UInt32 dirty) +{ + if ( fModule ) + { + // if this is the first time at the Eval, then run Python init + if ( fIsFirstTimeEval ) + { + fIsFirstTimeEval = false; // no longer the first time + // now run the __init__ function if there is one. + // is the Update function defined and working (as far as we know)? + if ( fPyFunctionInstances[kfunc_FirstUpdate] != nil ) + { + plProfile_BeginTiming(PythonUpdate); + // call it + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_FirstUpdate],fFunctionNames[kfunc_FirstUpdate],nil); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_FirstUpdate] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + } + } + + // is the Update function defined and working (as far as we know)? + if ( fPyFunctionInstances[kfunc_Update] != nil ) + { + plProfile_BeginTiming(PythonUpdate); + // call it + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_Update],fFunctionNames[kfunc_Update],"df",secs,del); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_Update] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + } + } + return true; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : MsgReceive +// PARAMETERS : msg - the message that came to us. +// +// PURPOSE : Handle all the different types of messages that we recv +// +hsBool plPythonFileMod::MsgReceive(plMessage* msg) +{ + // is it a ref message + plGenRefMsg* genRefMsg = plGenRefMsg::ConvertNoRef(msg); + if (genRefMsg) + { + // is it a ref for a named activator that we need to add to notify? + if ((genRefMsg->GetContext() & plRefMsg::kOnCreate) && genRefMsg->fWhich == kAddNotify) + { + // which kind of activator is this + plLogicModifier *logic = plLogicModifier::ConvertNoRef(genRefMsg->GetRef()); + if (logic) + { + logic->AddNotifyReceiver(this->GetKey()); + } + else // else might be a python file key + { + // next check to see if it is another PythonFileMod, and add to their notify list + plPythonFileMod *pymod = plPythonFileMod::ConvertNoRef(genRefMsg->GetRef()); + if (pymod) + { + pymod->AddToNotifyList(this->GetKey()); + } + } + } + } + + plAgeLoadedMsg* ageLoadedMsg = plAgeLoadedMsg::ConvertNoRef(msg); + if (ageLoadedMsg && ageLoadedMsg->fLoaded) + { + for (int i = 0; i < fNamedCompQueue.Count(); ++i) + { + NamedComponent comp = fNamedCompQueue[i]; + if (comp.isActivator) + IFindActivatorAndAdd(comp.name, comp.id); + else + IFindResponderAndAdd(comp.name, comp.id); + + delete [] comp.name; + } + + fNamedCompQueue.Reset(); + + plgDispatch::Dispatch()->UnRegisterForExactType( plAgeLoadedMsg::Index(), GetKey() ); + } + + // if this is a render message, then we are just trying to get a pointer to the Pipeline + plRenderMsg *rMsg = plRenderMsg::ConvertNoRef( msg ); + if( rMsg != nil ) + { + fPipe = rMsg->Pipeline(); + plgDispatch::Dispatch()->UnRegisterForExactType( plRenderMsg::Index(), GetKey() ); + return true; + } + + // are they looking for an Notify message? should be coming from a proActivator + if (fPyFunctionInstances[kfunc_Notify] != nil) + { + // yes, so was there actually a plActivateMsg? + plNotifyMsg* pNtfyMsg = plNotifyMsg::ConvertNoRef(msg); + if (pNtfyMsg) + { + // remember if this was a Local Broad cast or not + fLocalNotify = (pNtfyMsg->HasBCastFlag(plMessage ::plBCastFlags::kNetNonLocal)) ? false : true; + + // create a list for the event records + PyObject* levents = PyList_New(0); // start with a list of no elements + // loop thought the event records to get the data and transform into python objects + Int32 num_records = pNtfyMsg->GetEventCount(); + int j; + for ( j=0; jGetEventRecord(j); + switch ( pED->fEventType ) + { + + case proEventData::kCollision: + { + proCollisionEventData *eventData = (proCollisionEventData *)pED; + // get data from the collision + // create list + PyObject* event = PyList_New(4); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kCollision)); + PyList_SetItem(event, 1, PyInt_FromLong(eventData->fEnter ? 1 : 0)); + PyList_SetItem(event, 2, pySceneObject::New(eventData->fHitter, fSelfKey)); + PyList_SetItem(event, 3, pySceneObject::New(eventData->fHittee, fSelfKey)); + // add this event record to the main event list (lists within a list) + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + + case proEventData::kSpawned: + { + proSpawnedEventData *eventData = (proSpawnedEventData *)pED; + PyObject* event = PyList_New(3); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kSpawned)); + PyList_SetItem(event, 1, pySceneObject::New(eventData->fSpawner, fSelfKey)); + PyList_SetItem(event, 2, pySceneObject::New(eventData->fSpawnee, fSelfKey)); + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + + case proEventData::kPicked: + { + // get data from the picked event + proPickedEventData *eventData = (proPickedEventData *)pED; + // create list + PyObject* event = PyList_New(6); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kPicked)); + PyList_SetItem(event, 1, PyInt_FromLong(eventData->fEnabled ? 1 : 0)); + PyList_SetItem(event, 2, pySceneObject::New(eventData->fPicker, fSelfKey)); + PyList_SetItem(event, 3, pySceneObject::New(eventData->fPicked, fSelfKey)); + PyList_SetItem(event, 4, pyPoint3::New(eventData->fHitPoint)); + + // make it in the local space + hsPoint3 tolocal(0,0,0); + if(eventData->fPicked) + { + plSceneObject* obj = plSceneObject::ConvertNoRef(eventData->fPicked->ObjectIsLoaded()); + if ( obj ) + { + const plCoordinateInterface* ci = obj->GetCoordinateInterface(); + if ( ci ) + tolocal = (hsMatrix44)ci->GetWorldToLocal() * eventData->fHitPoint; + } + } + PyList_SetItem(event, 5, pyPoint3::New(tolocal)); + + // add this event record to the main event list (lists within a list) + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + + case proEventData::kControlKey: + { + proControlKeyEventData *eventData = (proControlKeyEventData *)pED; + // create event list + PyObject* event = PyList_New(3); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kControlKey)); + PyList_SetItem(event, 1, PyLong_FromLong(eventData->fControlKey)); + PyList_SetItem(event, 2, PyInt_FromLong(eventData->fDown ? 1 : 0)); + // add this event record to the main event list (lists within a list) + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + + case proEventData::kVariable: + { + proVariableEventData *eventData = (proVariableEventData *)pED; + // create event list + PyObject* event = PyList_New(4); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kVariable)); + PyList_SetItem(event, 1, PyString_FromString(eventData->fName)); + PyList_SetItem(event, 2, PyLong_FromLong(eventData->fDataType)); + + // depending on the data type create the data + switch ( eventData->fDataType ) + { + case proEventData::kNumber: + PyList_SetItem(event, 3, PyFloat_FromDouble(eventData->fNumber)); + break; + case proEventData::kKey: + PyList_SetItem(event, 3, pyKey::New(eventData->fKey)); + break; + } + // add this event record to the main event list (lists within a list) + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + + case proEventData::kFacing: + { + proFacingEventData *eventData = (proFacingEventData *)pED; + // create event list + PyObject* event = PyList_New(5); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kFacing)); + PyList_SetItem(event, 1, PyInt_FromLong(eventData->enabled ? 1 : 0)); + PyList_SetItem(event, 2, pySceneObject::New(eventData->fFacer, fSelfKey)); + PyList_SetItem(event, 3, pySceneObject::New(eventData->fFacee, fSelfKey)); + PyList_SetItem(event, 4, PyFloat_FromDouble(eventData->dot)); + // add this event record to the main event list (lists within a list) + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + + case proEventData::kContained: + { + proContainedEventData *eventData = (proContainedEventData *)pED; + // create event list + PyObject* event = PyList_New(4); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kContained)); + PyList_SetItem(event, 1, PyInt_FromLong(eventData->fEntering ? 1 : 0)); + PyList_SetItem(event, 2, pySceneObject::New(eventData->fContained, fSelfKey)); + PyList_SetItem(event, 3, pySceneObject::New(eventData->fContainer, fSelfKey)); + // add this event record to the main event list (lists within a list) + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + + case proEventData::kActivate: + { + proActivateEventData *eventData = (proActivateEventData *)pED; + // create event list + PyObject* event = PyList_New(3); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kActivate)); + PyList_SetItem(event, 1, PyInt_FromLong(eventData->fActive ? 1 : 0)); + PyList_SetItem(event, 2, PyInt_FromLong(eventData->fActivate ? 1 : 0)); + // add this event record to the main event list (lists within a list) + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + + case proEventData::kCallback: + { + proCallbackEventData *eventData = (proCallbackEventData *)pED; + // create event list + PyObject* event = PyList_New(2); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kCallback)); + PyList_SetItem(event, 1, PyLong_FromLong(eventData->fEventType)); + // add this event record to the main event list (lists within a list) + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + + case proEventData::kResponderState: + { + proResponderStateEventData *eventData = (proResponderStateEventData *)pED; + // create event list + PyObject* event = PyList_New(2); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kResponderState)); + PyList_SetItem(event, 1, PyLong_FromLong(eventData->fState)); + // add this event record to the main event list (lists within a list) + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + + case proEventData::kMultiStage: + { + proMultiStageEventData *eventData = (proMultiStageEventData *)pED; + // create event list + PyObject* event = PyList_New(4); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kMultiStage)); + PyList_SetItem(event, 1, PyLong_FromLong(eventData->fStage)); + PyList_SetItem(event, 2, PyLong_FromLong(eventData->fEvent)); + PyList_SetItem(event, 3, pySceneObject::New(eventData->fAvatar, fSelfKey)); + // add this event record to the main event list (lists within a list) + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + case proEventData::kOfferLinkingBook: + { + proOfferLinkingBookEventData* eventData = (proOfferLinkingBookEventData*)pED; + // create event list + PyObject* event = PyList_New(4); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kOfferLinkingBook)); + PyList_SetItem(event, 1, pySceneObject::New(eventData->offerer, fSelfKey)); + PyList_SetItem(event, 2, PyInt_FromLong(eventData->targetAge)); + PyList_SetItem(event, 3, PyInt_FromLong(eventData->offeree)); + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + case proEventData::kBook: + { + proBookEventData* eventData = (proBookEventData*)pED; + // create event list + PyObject* event = PyList_New(3); + PyList_SetItem(event, 0, PyLong_FromLong((long)proEventData::kBook)); + PyList_SetItem(event, 1, PyLong_FromUnsignedLong(eventData->fEvent)); + PyList_SetItem(event, 2, PyLong_FromUnsignedLong(eventData->fLinkID)); + PyList_Append(levents, event); + Py_DECREF(event); + } + break; + } + } + + // Need to determine which of the Activators sent this plNotifyMsg + // and set the ID appropriately + Int32 id = -1; // assume that none was found + if ( pNtfyMsg->GetSender() != nil ) + { + // loop throught the parameters and set them by id + // (will need to create the appropiate Python object for each type) + int npm; + for ( npm=0; npmGetSender()->GetUoid() == parameter.fObjectKey->GetUoid() ) + { + // match! Then return that as the ID + id = parameter.fID; + } + } + } + } + } + + + + // call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_Notify],fFunctionNames[kfunc_Notify], + "flO",pNtfyMsg->fState,id,levents); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_Notify] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(levents); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + // are they looking for a key event message? + if (fPyFunctionInstances[kfunc_OnKeyEvent] != nil) + { + // we are looking for collision messages, is it one? + plControlEventMsg* pEMsg = plControlEventMsg::ConvertNoRef(msg); + if (pEMsg) + { + // call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnKeyEvent],fFunctionNames[kfunc_OnKeyEvent], + "ll",pEMsg->GetControlCode(),pEMsg->ControlActivated()); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnKeyEvent] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + + } + + // are they looking for an Timer message? + if (fPyFunctionInstances[kfunc_AtTimer]) + { + // yes, so was there actually a plActivateMsg? + plTimerCallbackMsg* pTimerMsg = plTimerCallbackMsg::ConvertNoRef(msg); + if (pTimerMsg) + { + // yes... + // call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_AtTimer],fFunctionNames[kfunc_AtTimer], + "l",pTimerMsg->fID); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_AtTimer] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + // are they looking for an GUINotify message? + if (fPyFunctionInstances[kfunc_GUINotify]) + { + // yes, so was there actually a plActivateMsg? + pfGUINotifyMsg* pGUIMsg = pfGUINotifyMsg::ConvertNoRef(msg); + if (pGUIMsg) + { + // yes... + // call it ... but first create the control that started this mess + // create the key + PyObject* pyControl = nil; + if ( pGUIMsg->GetControlKey() ) // make sure there is a control key + { + // now create the control... but first we need to find out what it is + PyObject* pyCtrlKey = pyKey::New(pGUIMsg->GetControlKey()); + UInt32 control_type = pyGUIDialog::WhatControlType(*(pyKey::ConvertFrom(pyCtrlKey))); + Py_DECREF(pyCtrlKey); + + switch (control_type) + { + case pyGUIDialog::kDialog: + pyControl = pyGUIDialog::New(pGUIMsg->GetControlKey()); + break; + + case pyGUIDialog::kButton: + pyControl = pyGUIControlButton::New(pGUIMsg->GetControlKey()); + break; + + case pyGUIDialog::kListBox: + pyControl = pyGUIControlListBox::New(pGUIMsg->GetControlKey()); + break; + + case pyGUIDialog::kTextBox: + pyControl = pyGUIControlTextBox::New(pGUIMsg->GetControlKey()); + break; + + case pyGUIDialog::kEditBox: + pyControl = pyGUIControlEditBox::New(pGUIMsg->GetControlKey()); + break; + + case pyGUIDialog::kUpDownPair: + case pyGUIDialog::kKnob: + pyControl = pyGUIControlValue::New(pGUIMsg->GetControlKey()); + break; + + case pyGUIDialog::kCheckBox: + pyControl = pyGUIControlCheckBox::New(pGUIMsg->GetControlKey()); + break; + + case pyGUIDialog::kRadioGroup: + pyControl = pyGUIControlRadioGroup::New(pGUIMsg->GetControlKey()); + break; + + case pyGUIDialog::kDynamicText: + pyControl = pyGUIControlDynamicText::New(pGUIMsg->GetControlKey()); + break; + + case pyGUIDialog::kMultiLineEdit: + pyControl = pyGUIControlMultiLineEdit::New(pGUIMsg->GetControlKey()); + break; + + case pyGUIDialog::kPopUpMenu: + pyControl = pyGUIPopUpMenu::New(pGUIMsg->GetControlKey()); + break; + + case pyGUIDialog::kClickMap: + pyControl = pyGUIControlClickMap::New(pGUIMsg->GetControlKey()); + break; + + default: + // we don't know what it is... just send 'em the pyKey + pyControl = pyKey::New(pGUIMsg->GetControlKey()); + break; + + } + } + // Need to determine which of the GUIDialogs sent this plGUINotifyMsg + // and set the ID appropriately + Int32 id = -1; // assume that none was found + if ( pGUIMsg->GetSender() != nil ) + { + // loop throught the parameters and set them by id + // (will need to create the appropiate Python object for each type) + int npm; + for ( npm=0; npmGetSender()->GetUoid() == parameter.fObjectKey->GetUoid() ) + { + // match! then set the ID to what the parameter is, so the python programmer can find it + id = parameter.fID; + } + } + } + } + } + + // make sure that we found a control to go with this + if ( pyControl == nil ) + { + // if none then return a Python None object + Py_INCREF(Py_None); + pyControl = Py_None; + } + + // call their OnGUINotify method + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_GUINotify],fFunctionNames[kfunc_GUINotify], + "lOl",id,pyControl,pGUIMsg->GetEvent() ); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_GUINotify] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(pyControl); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + // are they looking for an RoomLoadNotify message? + if (fPyFunctionInstances[kfunc_PageLoad]) + { + // yes, so was there actually a plRoomLoadNotifyMsg? + plRoomLoadNotifyMsg* pRLNMsg = plRoomLoadNotifyMsg::ConvertNoRef(msg); + if (pRLNMsg) + { + // yes... + // call it + char* roomname = ""; + if ( pRLNMsg->GetRoom() != nil ) + roomname = (char*)pRLNMsg->GetRoom()->GetName(); + + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_PageLoad],fFunctionNames[kfunc_PageLoad], + "ls",pRLNMsg->GetWhatHappen(),roomname); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_PageLoad] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + + // are they looking for an ClothingUpdate message? + if (fPyFunctionInstances[kfunc_ClothingUpdate]) + { + // yes, so was there actually a plClothingUpdateBCMsg? + plClothingUpdateBCMsg* pCUMsg = plClothingUpdateBCMsg::ConvertNoRef(msg); + if (pCUMsg) + { + // yes... + // call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_ClothingUpdate],fFunctionNames[kfunc_ClothingUpdate],nil); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_ClothingUpdate] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + // are they looking for an KIMsg message? + if (fPyFunctionInstances[kfunc_KIMsg]) + { + // yes, so was there actually a pfKIMsg? + pfKIMsg* pkimsg = pfKIMsg::ConvertNoRef(msg); + if (pkimsg && pkimsg->GetCommand() != pfKIMsg::kHACKChatMsg) + { + // yes... + // find the value that would go with a command + PyObject* value; + std::wstring str; + switch (pkimsg->GetCommand()) + { + case pfKIMsg::kSetChatFadeDelay: + value = PyFloat_FromDouble(pkimsg->GetDelay()); + break; + case pfKIMsg::kSetTextChatAdminMode: + value = PyLong_FromLong(pkimsg->GetFlags()&pfKIMsg::kAdminMsg ? 1 : 0 ); + break; + case pfKIMsg::kYesNoDialog: + value = PyTuple_New(2); + str = pkimsg->GetStringU(); + PyTuple_SetItem(value, 0, PyUnicode_FromWideChar(str.c_str(), str.length())); + PyTuple_SetItem(value, 1, pyKey::New(pkimsg->GetSender())); + break; + case pfKIMsg::kGZInRange: + value = PyTuple_New(2); + PyTuple_SetItem(value, 0, PyLong_FromLong(pkimsg->GetIntValue())); + PyTuple_SetItem(value, 1, pyKey::New(pkimsg->GetSender())); + break; + case pfKIMsg::kRateIt: + value = PyTuple_New(3); + str = pkimsg->GetStringU(); + PyTuple_SetItem(value,0,PyString_FromString(pkimsg->GetUser())); + PyTuple_SetItem(value,1,PyUnicode_FromWideChar(str.c_str(), str.length())); + PyTuple_SetItem(value,2,PyLong_FromLong(pkimsg->GetIntValue())); + break; + case pfKIMsg::kRegisterImager: + value = PyTuple_New(2); + str = pkimsg->GetStringU(); + PyTuple_SetItem(value, 0, PyUnicode_FromWideChar(str.c_str(), str.length())); + PyTuple_SetItem(value, 1, pyKey::New(pkimsg->GetSender())); + break; + case pfKIMsg::kAddPlayerDevice: + case pfKIMsg::kRemovePlayerDevice: + { + str = pkimsg->GetStringU(); + if ( str.length() > 0 ) + value = PyUnicode_FromWideChar(str.c_str(), str.length()); + else + { + Py_INCREF(Py_None); + value = Py_None; + } + } + break; + case pfKIMsg::kKIChatStatusMsg: + case pfKIMsg::kKILocalChatStatusMsg: + case pfKIMsg::kKILocalChatErrorMsg: + case pfKIMsg::kKIOKDialog: + case pfKIMsg::kKIOKDialogNoQuit: + case pfKIMsg::kGZFlashUpdate: + case pfKIMsg::kKICreateMarkerNode: + str = pkimsg->GetStringU(); + value = PyUnicode_FromWideChar(str.c_str(), str.length()); + break; + case pfKIMsg::kMGStartCGZGame: + case pfKIMsg::kMGStopCGZGame: + case pfKIMsg::kFriendInviteSent: + default: + value = PyLong_FromLong(pkimsg->GetIntValue()); + break; + } + + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_KIMsg],fFunctionNames[kfunc_KIMsg], + "lO",pkimsg->GetCommand(),value); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_KIMsg] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(value); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + // are they looking for an MemberUpdate message? + if (fPyFunctionInstances[kfunc_MemberUpdate]) + { + // yes, so was there actually a plMemberUpdateMsg? + plMemberUpdateMsg* pmumsg = plMemberUpdateMsg::ConvertNoRef(msg); + if (pmumsg) + { + // yes... then call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_MemberUpdate],fFunctionNames[kfunc_MemberUpdate],nil); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_MemberUpdate] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + // are they looking for a RemoteAvatar Info message? + if (fPyFunctionInstances[kfunc_RemoteAvatarInfo]) + { + // yes, so was there actually a plActivateMsg? + plRemoteAvatarInfoMsg* pramsg = plRemoteAvatarInfoMsg::ConvertNoRef(msg); + if (pramsg) + { + // yes... + PyObject* player; + // if there was no avatar key in the message + if ( pramsg->GetAvatarKey() == nil ) + { + // then just return a None... same thing as nil.. which I guess means a non-avatar is selected + player = PyInt_FromLong(0); + } + else + { + // try to create the pyPlayer for where this message came from + int mbrIndex = plNetClientMgr::GetInstance()->TransportMgr().FindMember(pramsg->GetAvatarKey()); + if ( mbrIndex != -1 ) + { + plNetTransportMember *mbr = plNetClientMgr::GetInstance()->TransportMgr().GetMember( mbrIndex ); + player = pyPlayer::New(mbr->GetAvatarKey(), mbr->GetPlayerName(), mbr->GetPlayerID(), mbr->GetDistSq()); + } + else + { + // else if we could not find the player in our list, then no avatar selected + player = PyInt_FromLong(0); + } + } + + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_RemoteAvatarInfo],fFunctionNames[kfunc_RemoteAvatarInfo], + "O",player); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_RemoteAvatarInfo] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(player); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + + // are they looking for a CCR communication message? + if (fPyFunctionInstances[kfunc_OnCCRMsg]) + { + // yes, so was there actually a plActivateMsg? + plCCRCommunicationMsg* ccrmsg = plCCRCommunicationMsg::ConvertNoRef(msg); + if (ccrmsg) + { + const char* textmessage = ccrmsg->GetMessage(); + if ( textmessage == nil) + textmessage = ""; + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnCCRMsg],fFunctionNames[kfunc_OnCCRMsg], + "lsl",ccrmsg->GetType(),textmessage,ccrmsg->GetCCRPlayerID()); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnCCRMsg] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + + // are they looking for a VaultNotify message? + if (fPyFunctionInstances[kfunc_OnVaultNotify]) + { + // yes, so was there actually a plVaultNotifyMsg? + if (plVaultNotifyMsg * vaultNotifyMsg = plVaultNotifyMsg::ConvertNoRef(msg)) + { + if ( hsSucceeded( vaultNotifyMsg->GetResultCode() ) ) + { + // Create a tuple for second argument according to msg type. + // Default to an empty tuple. + PyObject* ptuple = PyTuple_New(0); + switch ( vaultNotifyMsg->GetType() ) + { + case plVaultNotifyMsg::kRegisteredOwnedAge: + case plVaultNotifyMsg::kRegisteredVisitAge: + case plVaultNotifyMsg::kUnRegisteredOwnedAge: + case plVaultNotifyMsg::kUnRegisteredVisitAge: { + if (RelVaultNode * rvn = VaultGetNodeIncRef(vaultNotifyMsg->GetArgs()->GetInt(plNetCommon::VaultTaskArgs::kAgeLinkNode))) { + Py_DECREF(ptuple); + ptuple = PyTuple_New(1); + PyTuple_SetItem(ptuple, 0, pyVaultAgeLinkNode::New(rvn)); + rvn->DecRef(); + } + } + break; + + case plVaultNotifyMsg::kPublicAgeCreated: + case plVaultNotifyMsg::kPublicAgeRemoved: { + if (const char * ageName = vaultNotifyMsg->GetArgs()->GetString(plNetCommon::VaultTaskArgs::kAgeFilename)) { + Py_DECREF(ptuple); + ptuple = PyTuple_New(1); + PyTuple_SetItem(ptuple, 0, PyString_FromString(ageName)); + } + } + break; + } + + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnVaultNotify],fFunctionNames[kfunc_OnVaultNotify], + "lO",vaultNotifyMsg->GetType(),ptuple); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnVaultNotify] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(ptuple); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + } + return true; + } + } + + + // are they looking for a RealTimeChat message? + if (fPyFunctionInstances[kfunc_RTChat]) + { + // yes, so was there actually a pfKIMsg? + pfKIMsg* pkimsg = pfKIMsg::ConvertNoRef(msg); + if (pkimsg && pkimsg->GetCommand() == pfKIMsg::kHACKChatMsg) + { + // yes... + // filter ignored player + if ( !VaultAmIgnoringPlayer( pkimsg->GetPlayerID() ) ) + { + // create the pyPlayer for where this message came from + PyObject* player; + PyObject* ptPlayerClass = PythonInterface::GetPlasmaItem("ptPlayer"); + hsAssert(ptPlayerClass,"Could not create a ptPlayer"); + int mbrIndex = plNetClientMgr::GetInstance()->TransportMgr().FindMember(pkimsg->GetPlayerID()); + if ( mbrIndex != -1 ) + { + plNetTransportMember *mbr = plNetClientMgr::GetInstance()->TransportMgr().GetMember( mbrIndex ); + player = pyPlayer::New(mbr->GetAvatarKey(), pkimsg->GetUser(), mbr->GetPlayerID(), mbr->GetDistSq()); + } + else + { + // else if we could not find the player in our list, then just return a string of the user's name + const char * fromName = pkimsg->GetUser(); + if (!fromName) + fromName = "Anonymous Coward"; + player = pyPlayer::New(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), fromName, pkimsg->GetPlayerID(), 0.0); + } + + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_RTChat],fFunctionNames[kfunc_RTChat], + "Osl",player,pkimsg->GetString().c_str(),pkimsg->GetFlags()); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_RTChat] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(player); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + } + if (plPlayerPageMsg::ConvertNoRef(msg)) + { + if (fPyFunctionInstances[kfunc_AvatarPage]) + { + // yes, so was there actually a player page msg + plPlayerPageMsg* ppMsg = plPlayerPageMsg::ConvertNoRef(msg); + if (ppMsg) + { + PyObject* pSobj = pySceneObject::New(ppMsg->fPlayer, fSelfKey); + plProfile_BeginTiming(PythonUpdate); + plSynchEnabler ps(true); // enable dirty state tracking during shutdown + + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_AvatarPage],fFunctionNames[kfunc_AvatarPage], + "Oli",pSobj,!ppMsg->fUnload,ppMsg->fLastOut); + if ( retVal == nil ) + { + #ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_AvatarPage] = nil; + #endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(pSobj); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + } + if (plAgeBeginLoadingMsg::ConvertNoRef(msg)) + { + if (fPyFunctionInstances[kfunc_OnBeginAgeLoad]) + { + // yes, so was there actually a player page msg + plAgeBeginLoadingMsg* ppMsg = plAgeBeginLoadingMsg::ConvertNoRef(msg); + if (ppMsg) + { + PyObject* pSobj = pySceneObject::New(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), fSelfKey); + plProfile_BeginTiming(PythonUpdate); + plSynchEnabler ps(true); // enable dirty state tracking during shutdown + + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnBeginAgeLoad],fFunctionNames[kfunc_OnBeginAgeLoad], + "O",pSobj); + if ( retVal == nil ) + { + #ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnBeginAgeLoad] = nil; + #endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(pSobj); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + } + if (plInitialAgeStateLoadedMsg::ConvertNoRef(msg))// initial server update complete message + { + // make sure there is a valid python instance + if ( fInstance ) + { + // set the isInitialStateLoaded to that it is loaded + PyObject* pInitialState = PyInt_FromLong(1); + PyObject_SetAttrString(fInstance, "isInitialStateLoaded", pInitialState); + Py_DECREF(pInitialState); + } + if (fPyFunctionInstances[kfunc_OnServerInitComplete]) + { + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnServerInitComplete],fFunctionNames[kfunc_OnServerInitComplete],nil); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnServerInitComplete] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + // are they looking for an plSDLNotificationMsg message? + if (fPyFunctionInstances[kfunc_SDLNotify]) + { + // yes, so was there actually a plSDLNotificationMsg? + plSDLNotificationMsg* sn = plSDLNotificationMsg::ConvertNoRef(msg); + if (sn) + { + const char* tag = sn->fHintString.c_str(); + if (tag == nil) + tag = ""; + // yes... then call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_SDLNotify],fFunctionNames[kfunc_SDLNotify], + "ssls",sn->fVar->GetName(),sn->fSDLName.c_str(),sn->fPlayerID,tag); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_SDLNotify] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + // are they looking for an plNetOwnershipMsg message? + if (fPyFunctionInstances[kfunc_OwnershipNotify]) + { + // yes, so was there actually a plNetOwnershipMsg? + plNetOwnershipMsg* nom = plNetOwnershipMsg::ConvertNoRef(msg); + if (nom) + { + // yes... then call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OwnershipNotify],fFunctionNames[kfunc_OwnershipNotify], + nil); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OwnershipNotify] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + // are they looking for an pfMarkerMsg message? + if (fPyFunctionInstances[kfunc_OnMarkerMsg]) + { + pfMarkerMsg* markermsg = pfMarkerMsg::ConvertNoRef(msg); + if (markermsg) + { + // yes... then call it + plProfile_BeginTiming(PythonUpdate); + // Default to an empty tuple. + PyObject* ptuple = PyTuple_New(0); + switch ( markermsg->fType ) + { + case pfMarkerMsg::kMarkerCaptured: + // Sent when we collide with a marker + Py_DECREF(ptuple); + ptuple = PyTuple_New(1); + PyTuple_SetItem(ptuple, 0, PyLong_FromUnsignedLong((long)markermsg->fMarkerID)); + break; + } + + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnMarkerMsg], fFunctionNames[kfunc_OnMarkerMsg], + "lO", (UInt32)markermsg->fType, ptuple); + if (retVal == nil) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnMarkerMsg] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(ptuple); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + +#ifndef PLASMA_EXTERNAL_RELEASE + // are they looking for an pfDebugTriggerMsg message? + if (fPyFunctionInstances[kfunc_OnBackdoorMsg]) + { + // yes, so was there actually a plNetOwnershipMsg? + pfBackdoorMsg* dt = pfBackdoorMsg::ConvertNoRef(msg); + if (dt) + { + // yes... then call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnBackdoorMsg], + fFunctionNames[kfunc_OnBackdoorMsg], + "ss",dt->GetTarget(),dt->GetString()); + if ( retVal == nil ) + { + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } +#endif //PLASMA_EXTERNAL_RELEASE + + // are they looking for a plLOSHitMsg message? + if (fPyFunctionInstances[kfunc_OnLOSNotify]) + { + // yes, so was there actually a plLOSHitMsg? + plLOSHitMsg *pLOSMsg = plLOSHitMsg::ConvertNoRef( msg ); + if (pLOSMsg) + { + // yes... then call it (self,ID,noHitFlag,sceneobject,hitPoint,distance) + plProfile_BeginTiming(PythonUpdate); + PyObject* scobj; + PyObject* hitpoint; + if ( pLOSMsg->fObj && plSceneObject::ConvertNoRef( pLOSMsg->fObj->ObjectIsLoaded()) ) + { + scobj = pySceneObject::New(pLOSMsg->fObj); + hitpoint = pyPoint3::New(pLOSMsg->fHitPoint); + } + else + { + // otherwise return a None object for the avatarKey + Py_INCREF(Py_None); + scobj = Py_None; + Py_INCREF(Py_None); + hitpoint = Py_None; + } + + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnLOSNotify], + fFunctionNames[kfunc_OnLOSNotify], + "llOOf",pLOSMsg->fRequestID,pLOSMsg->fNoHit, + scobj, hitpoint, pLOSMsg->fDistance); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnLOSNotify] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(scobj); + Py_DECREF(hitpoint); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + // are they looking for a plAvatarBehaviorNotifyMsg message? + if (fPyFunctionInstances[kfunc_OnBehaviorNotify]) + { + // yes, so was there actually a plAvatarBehaviorNotifyMsg? + plAvatarBehaviorNotifyMsg *behNotifymsg = plAvatarBehaviorNotifyMsg::ConvertNoRef(msg); + if (behNotifymsg) + { + // yes... then call it + plProfile_BeginTiming(PythonUpdate); + // the parent of the sender should be the avatar that did the behavior + PyObject* pSobj; + + plModifier* avmod = plModifier::ConvertNoRef(behNotifymsg->GetSender()->ObjectIsLoaded()); + if ( avmod && avmod->GetNumTargets() > 0 ) + { + pSobj = pySceneObject::New(avmod->GetTarget(0)->GetKey(), fSelfKey); + } + else + { + // otherwise return a None object for the avatarKey + Py_INCREF(Py_None); + pSobj = Py_None; + } + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnBehaviorNotify], + fFunctionNames[kfunc_OnBehaviorNotify], + "lOl",behNotifymsg->fType,pSobj,behNotifymsg->state); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnBehaviorNotify] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(pSobj); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + // are they looking for a pfMovieEventMsg message? + if (fPyFunctionInstances[kfunc_OnMovieEvent]) + { + // yes, so was there actually a pfMovieEventMsg? + pfMovieEventMsg *moviemsg = pfMovieEventMsg::ConvertNoRef(msg); + if (moviemsg) + { + // yes... then call it + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnMovieEvent], + fFunctionNames[kfunc_OnMovieEvent], + "si",moviemsg->fMovieName,(UInt32)moviemsg->fReason); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnMovieEvent] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + // are they looking for a plCaptureRenderMsg message? + if (fPyFunctionInstances[kfunc_OnScreenCaptureDone]) + { + // yes, so was there actually a pfMovieEventMsg? + plCaptureRenderMsg *capturemsg = plCaptureRenderMsg::ConvertNoRef(msg); + if (capturemsg) + { + // yes... then call it + plProfile_BeginTiming(PythonUpdate); + PyObject* pSobj; + + if ( capturemsg->GetMipmap() ) + { + pSobj = pyImage::New(capturemsg->GetMipmap()); + } + else + { + // otherwise return a None object for the avatarKey + Py_INCREF(Py_None); + pSobj = Py_None; + } + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnScreenCaptureDone], + fFunctionNames[kfunc_OnScreenCaptureDone], + "O",pSobj); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnScreenCaptureDone] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(pSobj); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + // we handled this message (I think) + return true; + } + } + + if (fPyFunctionInstances[kfunc_OnClimbBlockerEvent]) + { + plClimbEventMsg* pEvent = plClimbEventMsg::ConvertNoRef(msg); + if (pEvent) + { + PyObject* pSobj = pySceneObject::New(pEvent->GetSender(), fSelfKey); + + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnClimbBlockerEvent], + fFunctionNames[kfunc_OnClimbBlockerEvent], + "O",pSobj); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnClimbBlockerEvent] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + Py_DECREF(pSobj); + return true; + } + } + if (fPyFunctionInstances[kfunc_OnAvatarSpawn]) + { + plAvatarSpawnNotifyMsg* pSpawn = plAvatarSpawnNotifyMsg::ConvertNoRef(msg); + if (pSpawn) + { + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnAvatarSpawn], + fFunctionNames[kfunc_OnAvatarSpawn], + "l",1); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnAvatarSpawn] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + return true; + } + } + + if (fPyFunctionInstances[kfunc_OnAccountUpdate]) + { + plAccountUpdateMsg* pUpdateMsg = plAccountUpdateMsg::ConvertNoRef(msg); + if (pUpdateMsg) + { + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod(fPyFunctionInstances[kfunc_OnAccountUpdate], fFunctionNames[kfunc_OnAccountUpdate], + "iii", (int)pUpdateMsg->GetUpdateType(), (int)pUpdateMsg->GetResult(), (int)pUpdateMsg->GetPlayerInt() + ); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnAccountUpdate] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + + return true; + } + } + + if (fPyFunctionInstances[kfunc_gotPublicAgeList]) + { + plNetCommPublicAgeListMsg * pPubAgeMsg = plNetCommPublicAgeListMsg::ConvertNoRef(msg); + if (pPubAgeMsg) + { + plProfile_BeginTiming(PythonUpdate); + PyObject* pyEL = PyList_New(pPubAgeMsg->ages.Count()); + for (unsigned i = 0; iages.Count(); ++i) { + plAgeInfoStruct ageInfo; + ageInfo.CopyFrom(pPubAgeMsg->ages[i]); + unsigned nPlayers = pPubAgeMsg->ages[i].currentPopulation; + unsigned nOwners = pPubAgeMsg->ages[i].population; + + PyObject* t = PyTuple_New(3); + PyTuple_SetItem(t, 0, pyAgeInfoStruct::New(&ageInfo)); + PyTuple_SetItem(t, 1, PyLong_FromUnsignedLong(nPlayers)); + PyTuple_SetItem(t, 2, PyLong_FromUnsignedLong(nOwners)); + PyList_SetItem(pyEL, i, t); // steals the ref + } + + PyObject* retVal = PyObject_CallMethod( + fPyFunctionInstances[kfunc_gotPublicAgeList], + fFunctionNames[kfunc_gotPublicAgeList], + "O", + pyEL + ); + if ( retVal == nil ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_gotPublicAgeList] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + + return true; + } + } + + if (fPyFunctionInstances[kfunc_OnGameMgrMsg]) + { + pfGameMgrMsg* gameMgrMsg = pfGameMgrMsg::ConvertNoRef(msg); + if (gameMgrMsg) + { + plProfile_BeginTiming(PythonUpdate); + PyObject* pythonMsg = pyGameMgrMsg::New(gameMgrMsg); + PyObject* retVal = PyObject_CallMethod( + fPyFunctionInstances[kfunc_OnGameMgrMsg], + fFunctionNames[kfunc_OnGameMgrMsg], + "O", + pythonMsg + ); + Py_DECREF(pythonMsg); + if (retVal == nil) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnGameMgrMsg] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output + DisplayPythonOutput(); + + return true; + } + } + + if (fPyFunctionInstances[kfunc_OnGameCliMsg]) + { + pfGameCliMsg* gameMgrMsg = pfGameCliMsg::ConvertNoRef(msg); + if (gameMgrMsg) + { + plProfile_BeginTiming(PythonUpdate); + PyObject* pythonMsg = pyGameCliMsg::New(gameMgrMsg); + PyObject* retVal = PyObject_CallMethod( + fPyFunctionInstances[kfunc_OnGameCliMsg], + fFunctionNames[kfunc_OnGameCliMsg], + "O", + pythonMsg + ); + Py_DECREF(pythonMsg); + if (retVal == nil) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnGameCliMsg] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + // display any output + DisplayPythonOutput(); + + return true; + } + } + + if (fPyFunctionInstances[kfunc_OnAIMsg]) + { + plAIMsg* aiMsg = plAIMsg::ConvertNoRef(msg); + if (aiMsg) + { + plProfile_BeginTiming(PythonUpdate); + + // grab the sender (the armature mod that has our brain) + plArmatureMod* armMod = plArmatureMod::ConvertNoRef(aiMsg->GetSender()->ObjectIsLoaded()); + PyObject* brainObj = NULL; + if (armMod) + { + plArmatureBrain* brain = armMod->FindBrainByClass(plAvBrainCritter::Index()); + plAvBrainCritter* critterBrain = plAvBrainCritter::ConvertNoRef(brain); + if (critterBrain) + brainObj = pyCritterBrain::New(critterBrain); + } + if (!brainObj) + { + Py_INCREF(Py_None); + brainObj = Py_None; + } + + // set up the msg type and any args, based on the message we got + int msgType = plAIMsg::kAIMsg_Unknown; + PyObject* args = NULL; + plAIBrainCreatedMsg* brainCreatedMsg = plAIBrainCreatedMsg::ConvertNoRef(aiMsg); + if (brainCreatedMsg) + msgType = plAIMsg::kAIMsg_BrainCreated; + + plAIArrivedAtGoalMsg* arrivedMsg = plAIArrivedAtGoalMsg::ConvertNoRef(aiMsg); + if (arrivedMsg) + { + msgType = plAIMsg::kAIMsg_ArrivedAtGoal; + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pyPoint3::New(arrivedMsg->Goal())); + } + + // if no args were set, simply set to none + if (!args) + { + Py_INCREF(Py_None); + args = Py_None; + } + + // call the function with the above arguments + PyObject* retVal = PyObject_CallMethod( + fPyFunctionInstances[kfunc_OnAIMsg], + fFunctionNames[kfunc_OnAIMsg], + "OisO", + brainObj, msgType, aiMsg->BrainUserString().c_str(), args + ); + Py_DECREF(brainObj); + Py_DECREF(args); + if (retVal == nil) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnAIMsg] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + Py_XDECREF(retVal); + + plProfile_EndTiming(PythonUpdate); + // display any output + DisplayPythonOutput(); + + return true; + } + } + + return plModifier::MsgReceive(msg); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ReportError +// PARAMETERS : +// +// PURPOSE : Report error to somewhere +// +void plPythonFileMod::ReportError() +{ + char objectName[128]; + StrCopy(objectName, this->GetKeyName(), arrsize(objectName)); + StrPack(objectName, " - ", arrsize(objectName)); + + PythonInterface::WriteToStdErr(objectName); + + PyErr_Print(); // make sure the error is printed + PyErr_Clear(); // clear the error +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : DisplayPythonOutput +// PARAMETERS : +// +// PURPOSE : display any Python stdout or stderr to file and to screen(later) +// +void plPythonFileMod::DisplayPythonOutput() +{ + // get the messages + PythonInterface::getOutputAndReset(); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : SetSourceFile +// PARAMETERS : code - text source code +// : filename - where the source code came from (just say the object name) +// +// PURPOSE : Sets the source code for this modifier. +// : Compile it into a Python code object +// : (This is usually called by the component) +// +void plPythonFileMod::SetSourceFile(const char* filename) +{ + delete [] fPythonFile; + fPythonFile = hsStrcpy(filename); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : getPythonOutput +// PARAMETERS : none +// +// PURPOSE : get the Output to the error file to be displayed +// +int plPythonFileMod::getPythonOutput(std::string* line) +{ + return PythonInterface::getOutputAndReset(line); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : EnableControlKeys +// PARAMETERS : none +// +// PURPOSE : get the Output to the error file to be displayed +// +void plPythonFileMod::EnableControlKeyEvents() +{ + // register for keyboard events if needed + if ( fPyFunctionInstances[kfunc_OnKeyEvent] != nil ) + { + // register for key events + plCmdIfaceModMsg* pModMsg = TRACKED_NEW plCmdIfaceModMsg; + pModMsg->SetBCastFlag(plMessage::kBCastByExactType); + pModMsg->SetSender(GetKey()); + pModMsg->SetCmd(plCmdIfaceModMsg::kAdd); + plgDispatch::MsgSend(pModMsg); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : DisableControlKeys +// PARAMETERS : none +// +// PURPOSE : get the Output to the error file to be displayed +// +void plPythonFileMod::DisableControlKeyEvents() +{ + // unregister for key events + plCmdIfaceModMsg* pModMsg = TRACKED_NEW plCmdIfaceModMsg; + pModMsg->SetBCastFlag(plMessage::kBCastByExactType); + pModMsg->SetSender(GetKey()); + pModMsg->SetCmd(plCmdIfaceModMsg::kRemove); + plgDispatch::MsgSend(pModMsg); +} + +void plPythonFileMod::Read(hsStream* stream, hsResMgr* mgr) +{ + plMultiModifier::Read(stream, mgr); + + // read in the compile python code (pyc) + if ( fPythonFile ) + { + // if we already have some code, get rid of it! + delete [] fPythonFile; + fPythonFile = nil; + } + fPythonFile = stream->ReadSafeString(); + + // then read in the list of receivers that want to be notified + int nRcvs = stream->ReadSwap32(); + fReceivers.Reset(); + int m; + for( m=0; mReadKey(stream)); + } + + // then read in the list of parameters + int nParms = stream->ReadSwap32(); + fParameters.SetCountAndZero(nParms); + int i; + for( i=0; iWriteSafeString(fPythonFile); + + // then write out the list of receivers that want to be notified + stream->WriteSwap32(fReceivers.GetCount()); + int m; + for( m=0; mWriteKey(stream, fReceivers[m]); + + // then write out the list of parameters + stream->WriteSwap32(fParameters.GetCount()); + int i; + for( i=0; i. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plPythonFileMod_h_ +#define _plPythonFileMod_h_ + +////////////////////////////////////////////////////////////////////// +// +// plPythonFileMod - the 'special' Python File modifier +// +// This modifier will handle the interface to python code that has been file-ized. +// +////////////////////////////////////////////////////////////////////// + +#include "../pnModifier/plMultiModifier.h" +#include "hsGeometry3.h" +#include "hsResMgr.h" + +#include + +#include "plPythonParameter.h" + +class PythonVaultCallback; +class plPythonSDLModifier; +class pyKey; +class pfPythonKeyCatcher; +class plKeyEventMsg; +class plPipeline; + +class plPythonFileMod : public plMultiModifier +{ +protected: + friend class plPythonSDLModifier; + + plPythonSDLModifier* fSDLMod; + + hsBool IEval(double secs, hsScalar del, UInt32 dirty); + + void IMakeModuleName(char* modulename,plSceneObject* sobj); + + char* fPythonFile; + char* fModuleName; + + // the list of receivers that want to be notified + hsTArray fReceivers; + + PyObject* fSelfKey; + plPipeline *fPipe; + + // the list of parameters (attributes) + hsTArray fParameters; + + // internal data + PyObject* fModule; // python module object + PyObject* fInstance; // python object that the instance of the class to run + static hsBool fAtConvertTime; // flag for when in convert time within Max, don't run code + hsBool fLocalNotify; // True when This Mod was Notified by a local plNotify + hsBool fIsFirstTimeEval; // flag to determine when the first time at the eval, + // so the Python coders can hava a chance to run initialization + // code after the system is up, but before things are displayed + hsBool fAmIAttachedToClone; // is this python file mod attached to a cloned object + + // callback class for the KI + PythonVaultCallback *fVaultCallback; + pfPythonKeyCatcher *fKeyCatcher; + + struct NamedComponent + { + char* name; + Int32 id; + bool isActivator; + }; + + hsTArray fNamedCompQueue; + + virtual void IFindResponderAndAdd(const char *responderName, Int32 id); + virtual void IFindActivatorAndAdd(const char *activatorName, Int32 id); + void ISetKeyValue(const plKey& key, Int32 id); + + bool ILoadPythonCode(); + + enum genref_whats + { + kNotSure = 0, + kAddNotify + }; + +public: + + plPythonFileMod(); + ~plPythonFileMod(); + + CLASSNAME_REGISTER( plPythonFileMod ); + GETINTERFACE_ANY( plPythonFileMod, plMultiModifier ); + + plPythonSDLModifier* GetSDLMod() { return fSDLMod; } + hsBool WasLocalNotify() { return fLocalNotify; } + plPipeline* GetPipeline() { return fPipe; } + virtual void SetSourceFile(const char* filename); + virtual int getPythonOutput(std::string* line); + virtual void ReportError(); + virtual void DisplayPythonOutput(); + static void SetAtConvertTime() { fAtConvertTime=true; } + virtual hsBool AmIAttachedToClone() { return fAmIAttachedToClone; } + + virtual void AddToNotifyList(plKey pKey) { fReceivers.Append(pKey); } + virtual Int32 NotifyListCount() { return fReceivers.Count(); } + virtual plKey GetNotifyListItem(Int32 i) { return fReceivers[i]; } + + virtual void AddParameter(plPythonParameter param) { fParameters.Append(param); } + virtual Int32 GetParameterListCount() { return fParameters.Count(); } + virtual plPythonParameter GetParameterItem(Int32 i) { return fParameters[i]; } + + virtual void AddTarget(plSceneObject* sobj); + virtual void RemoveTarget(plSceneObject* so); + + virtual void EnableControlKeyEvents(); + virtual void DisableControlKeyEvents(); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + // this is to keep track of what python functions are available and working, + // so there is no need to keep trying and banging our head until its bloody + enum func_num + { + kfunc_FirstUpdate = 0, // these enums _have_ to match the static names in fEventFunctionNames + kfunc_Update, + kfunc_Notify, // OnNotify + kfunc_AtTimer, + kfunc_OnKeyEvent, + kfunc_Load, + kfunc_Save, + kfunc_GUINotify, + kfunc_PageLoad, + kfunc_ClothingUpdate, + kfunc_KIMsg, + kfunc_MemberUpdate, + kfunc_RemoteAvatarInfo, + kfunc_RTChat, + kfunc_VaultEvent, + kfunc_AvatarPage, + kfunc_SDLNotify, + kfunc_OwnershipNotify, + kfunc_AgeVaultEvent, + kfunc_Init, + kfunc_OnCCRMsg, + kfunc_OnServerInitComplete, + kfunc_OnVaultNotify, + kfunc_OnDefaultKeyCaught, + kfunc_OnMarkerMsg, + kfunc_OnBackdoorMsg, + kfunc_OnBehaviorNotify, + kfunc_OnLOSNotify, + kfunc_OnBeginAgeLoad, + kfunc_OnMovieEvent, + kfunc_OnScreenCaptureDone, + kfunc_OnClimbBlockerEvent, + kfunc_OnAvatarSpawn, + kfunc_OnAccountUpdate, + kfunc_gotPublicAgeList, + kfunc_OnGameMgrMsg, + kfunc_OnGameCliMsg, + kfunc_OnAIMsg, + kfunc_lastone + }; + // array of matching Python instance where the functions are, if defined + PyObject* fPyFunctionInstances[kfunc_lastone]; + // array of the names of the standard functions that can be called + static char* fFunctionNames[]; + + // The konstant hard-coded name to be used for all global pythonFileMods + static char kGlobalNameKonstant[]; + + // API for processing discarded keys as the deafult key catcher + void HandleDiscardedKey( plKeyEventMsg *msg ); +}; + +#endif // _plPythonFileMod_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonHelpers.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonHelpers.h new file mode 100644 index 00000000..85431b99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonHelpers.h @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plPythonHelpers_h_ +#define _plPythonHelpers_h_ + +////////////////////////////////////////////////////////////////////// +// +// plPythonHelpers - helper definitions for plPythonFileMod +// +////////////////////////////////////////////////////////////////////// + +// notetrack data structure +typedef struct +{ + plKey objKey; // plKey to the object + const char* note_name; // notetrack name + plKey modKey; // the animation modifier plKey that is associated with the notetrack name +} PY_NOTETRACK; + + +// material animation record structure +typedef struct +{ + const char* material_name; // material name + const char* note_name; // notetrack name + plKey modKey; // the animation modifier plKey that is associated with this material animation +} PY_MATERIAL_ANIM; + +// material animation record structure +typedef struct +{ + const char* sound_name; // the sound name + int sound_index; // the sound index that goes with the sound +} PY_SOUND_IDX; + + +#endif // _plPythonHelpers_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonPack.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonPack.cpp new file mode 100644 index 00000000..14996e77 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonPack.cpp @@ -0,0 +1,229 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "plPythonPack.h" +#include "hsStream.h" +#include "../plFile/hsFiles.h" +#include "../plFile/plSecureStream.h" +#include "../plFile/plStreamSource.h" +#include "hsStlSortUtils.h" +#include "marshal.h" + +static const char* kPackFilePath = ".\\Python\\"; + +struct plPackOffsetInfo +{ + UInt32 fOffset; + UInt32 fStreamIndex; // index of the stream object in the plPythonPack object that the file resides in +}; + +class plPythonPack +{ +protected: + std::vector fPackStreams; + bool fPackNotFound; // No pack file, don't keep trying + + typedef std::map FileOffset; + FileOffset fFileOffsets; + + plPythonPack(); + +public: + ~plPythonPack(); + + static plPythonPack& Instance(); + + bool Open(); + void Close(); + + PyObject* OpenPacked(const char *fileName); + hsBool IsPackedFile(const char* fileName); +}; + +PyObject* PythonPack::OpenPythonPacked(const char* fileName) +{ + return plPythonPack::Instance().OpenPacked(fileName); +} + +hsBool PythonPack::IsItPythonPacked(const char* fileName) +{ + return plPythonPack::Instance().IsPackedFile(fileName); +} + +plPythonPack::plPythonPack() : fPackNotFound(false) +{ +} + +plPythonPack::~plPythonPack() +{ + Close(); +} + +plPythonPack& plPythonPack::Instance() +{ + static plPythonPack theInstance; + return theInstance; +} + +#include +#include +#include + +bool plPythonPack::Open() +{ + if (fPackStreams.size() > 0) + return true; + + // We already tried and it wasn't there + if (fPackNotFound) + return false; + + fPackNotFound = true; + + // Get the names of all the pak files + std::vector files = plStreamSource::GetInstance()->GetListOfNames(L"python", L".pak"); + + std::vector modTimes; // the modification time for each of the streams (to resolve duplicate file issues) + + // grab all the .pak files in the folder + for (int curName = 0; curName < files.size(); curName++) + { + // obtain the stream + hsStream *fPackStream = plStreamSource::GetInstance()->GetFile(files[curName]); + if (fPackStream) + { + fPackStream->Rewind(); // make sure we're at the beginning of the file + fPackNotFound = false; + + char* tempFilename = hsWStringToString(files[curName].c_str()); + struct stat buf; + time_t curModTime = 0; + if (stat(tempFilename,&buf)==0) + curModTime = buf.st_mtime; + modTimes.push_back(curModTime); + delete [] tempFilename; + + // read the index data + int numFiles = fPackStream->ReadSwap32(); + UInt32 streamIndex = (UInt32)(fPackStreams.size()); + for (int i = 0; i < numFiles; i++) + { + // and pack the index into our own data structure + char* buf = fPackStream->ReadSafeString(); + std::string pythonName = buf; // reading a "string" from a hsStream directly into a stl string causes memory loss + delete [] buf; + UInt32 offset = fPackStream->ReadSwap32(); + + plPackOffsetInfo offsetInfo; + offsetInfo.fOffset = offset; + offsetInfo.fStreamIndex = streamIndex; + + if (fFileOffsets.find(pythonName) != fFileOffsets.end()) + { + UInt32 index = fFileOffsets[pythonName].fStreamIndex; + if (modTimes[index] < curModTime) // is the existing file older then the new one? + fFileOffsets[pythonName] = offsetInfo; // yup, so replace it with the new info + } + else + fFileOffsets[pythonName] = offsetInfo; // no conflicts, add the info + } + fPackStreams.push_back(fPackStream); + } + } + + return !fPackNotFound; +} + +void plPythonPack::Close() +{ + if (fPackStreams.size() == 0) + return; + + int i; + for (i=0; iSetPosition(offsetInfo.fOffset); + + Int32 size = fPackStream->ReadSwap32(); + if (size > 0) + { + char *buf = TRACKED_NEW char[size]; + UInt32 readSize = fPackStream->Read(size, buf); + hsAssert(readSize <= size, xtl::format("Python PackFile %s: Incorrect amount of data, read %d instead of %d", + fileName, readSize, size).c_str()); + + // let the python marshal make it back into a code object + PyObject *pythonCode = PyMarshal_ReadObjectFromString(buf, size); + + delete [] buf; + + return pythonCode; + } + } + + return nil; +} + +hsBool plPythonPack::IsPackedFile(const char* fileName) +{ + if (!Open()) + return nil; + + std::string pythonName = fileName; + pythonName += ".py"; + + FileOffset:: iterator it = fFileOffsets.find(pythonName); + if (it != fFileOffsets.end()) + return true; + + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonPack.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonPack.h new file mode 100644 index 00000000..14a40dfd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonPack.h @@ -0,0 +1,38 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPythonPack_h_inc +#define plPythonPack_h_inc + +#include "Python.h" +#include "hsTypes.h" + +namespace PythonPack +{ + PyObject* OpenPythonPacked(const char* fileName); + hsBool IsItPythonPacked(const char* fileName); +} + +#endif // plPythonPack_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonParameter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonParameter.h new file mode 100644 index 00000000..ca927d71 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonParameter.h @@ -0,0 +1,450 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPythonParameter_h_inc +#define plPythonParameter_h_inc + +#include "../pnKeyedObject/plKey.h" + +// +// This is the data for the parameters (or attributes) for the PythonFile components +// +// What is in the records is the id of the attribute and its value +// +// NOTE: Lists of values for parameters will have a specific type that means list +// however, the list of values will not be in one record but many records, with +// the fID being the same. +// + +typedef struct plPythonParameter +{ +public: + // this is a unique (within one Python mod) id for this parameter + Int32 fID; + + // then comes the value, which is a type followed by the data + + enum valueType + { + kInt=1, + kFloat, + kBoolean, + kString, + kSceneObject, + kSceneObjectList, + kActivatorList, + kResponderList, + kDynamicText, + kGUIDialog, + kExcludeRegion, + kAnimation, + kAnimationName, + kBehavior, + kMaterial, + kGUIPopUpMenu, + kGUISkin, + kWaterComponent, + kSwimCurrentInterface, + kClusterComponentList, + kMaterialAnimation, + kGrassShaderComponent, + kNone + }; + + Int32 fValueType; // what type of value (dataType enum) + + // the data of the value + union + { + Int32 fIntNumber; + + hsScalar fFloatNumber; + + hsBool fBool; + + char* fString; + + } datarecord; + + plKey fObjectKey; // the plKey of the scene object (should be part of the union, but unions don't allow complex types) + + + plPythonParameter() + { + fID = 0; + fValueType = kNone; + } + + plPythonParameter(Int32 id) + { + fID = id; + fValueType = kNone; + } + + plPythonParameter& operator=(const plPythonParameter& other) + { + return Copy(other); + } + // copy constructor + plPythonParameter(const plPythonParameter& other) + { + fID = 0; + fValueType = kNone; + + Copy(other); + } + + plPythonParameter& Copy(const plPythonParameter& other) + { + fID = other.fID; + switch ( other.fValueType ) + { + case kInt: + SetToInt(other.datarecord.fIntNumber); + break; + case kFloat: + SetToFloat(other.datarecord.fFloatNumber); + break; + case kBoolean: + SetToBoolean(other.datarecord.fBool); + break; + case kString: + SetToString(other.datarecord.fString); + break; + case kSceneObject: + SetToSceneObject(other.fObjectKey); + break; + case kSceneObjectList: + SetToSceneObject(other.fObjectKey,true); + break; + case kActivatorList: + SetToActivator(other.fObjectKey); + break; + case kResponderList: + SetToResponder(other.fObjectKey); + break; + case kDynamicText: + SetToDynamicText(other.fObjectKey); + break; + case kGUIDialog: + SetToGUIDialog(other.fObjectKey); + break; + case kExcludeRegion: + SetToExcludeRegion(other.fObjectKey); + break; + case kAnimation: + SetToAnimation(other.fObjectKey); + break; + case kAnimationName: + SetToAnimationName(other.datarecord.fString); + break; + case kBehavior: + SetToBehavior(other.fObjectKey); + break; + case kMaterial: + SetToMaterial(other.fObjectKey); + break; + case kGUIPopUpMenu: + SetToGUIPopUpMenu(other.fObjectKey); + break; + case kGUISkin: + SetToGUISkin(other.fObjectKey); + break; + case kWaterComponent: + SetToWaterComponent(other.fObjectKey); + break; + case kSwimCurrentInterface: + SetToSwimCurrentInterface(other.fObjectKey); + break; + case kClusterComponentList: + SetToClusterComponent(other.fObjectKey); + break; + case kMaterialAnimation: + SetToMaterialAnimation(other.fObjectKey); + break; + case kGrassShaderComponent: + SetToGrassShaderComponent(other.fObjectKey); + break; + } + return *this; + } + + ~plPythonParameter() + { + SetToNone(); + } + + void SetToNone() + { + // remove the string if one was created + if ( fValueType == kString || fValueType == kAnimationName ) + delete [] datarecord.fString; + + fValueType = kNone; + } + + void SetToInt(Int32 number) + { + SetToNone(); + fValueType = kInt; + datarecord.fIntNumber = number; + } + void SetToFloat(hsScalar number) + { + SetToNone(); + fValueType = kFloat; + datarecord.fFloatNumber = number; + } + void SetToBoolean(hsBool state) + { + SetToNone(); + fValueType = kBoolean; + datarecord.fBool = state; + } + void SetToString(const char* string) + { + SetToNone(); + fValueType = kString; + datarecord.fString = hsStrcpy(string); + } + void SetToSceneObject(plKey key, hsBool list=false) + { + SetToNone(); + if (list) + fValueType = kSceneObjectList; + else + fValueType = kSceneObject; + fObjectKey = key; + } + void SetToActivator(plKey key) + { + SetToNone(); + fValueType = kActivatorList; + fObjectKey = key; + } + void SetToResponder(plKey key) + { + SetToNone(); + fValueType = kResponderList; + fObjectKey = key; + } + void SetToDynamicText(plKey key) + { + SetToNone(); + fValueType = kDynamicText; + fObjectKey = key; + } + void SetToGUIDialog(plKey key) + { + SetToNone(); + fValueType = kGUIDialog; + fObjectKey = key; + } + void SetToExcludeRegion(plKey key) + { + SetToNone(); + fValueType = kExcludeRegion; + fObjectKey = key; + } + void SetToWaterComponent(plKey key) + { + SetToNone(); + fValueType = kWaterComponent; + fObjectKey = key; + } + void SetToSwimCurrentInterface(plKey key) + { + SetToNone(); + fValueType = kSwimCurrentInterface; + fObjectKey = key; + } + void SetToAnimation(plKey key) + { + SetToNone(); + fValueType = kAnimation; + fObjectKey = key; + } + void SetToAnimationName(const char* string) + { + SetToNone(); + fValueType = kAnimationName; + datarecord.fString = hsStrcpy(string); + } + void SetToBehavior(plKey key) + { + SetToNone(); + fValueType = kBehavior; + fObjectKey = key; + } + void SetToMaterial(plKey key) + { + SetToNone(); + fValueType = kMaterial; + fObjectKey = key; + } + void SetToGUIPopUpMenu(plKey key) + { + SetToNone(); + fValueType = kGUIPopUpMenu; + fObjectKey = key; + } + void SetToGUISkin(plKey key) + { + SetToNone(); + fValueType = kGUISkin; + fObjectKey = key; + } + void SetToClusterComponent(plKey key) + { + SetToNone(); + fValueType = kClusterComponentList; + fObjectKey = key; + } + void SetToMaterialAnimation(plKey key) + { + SetToNone(); + fValueType = kMaterialAnimation; + fObjectKey = key; + } + void SetToGrassShaderComponent(plKey key) + { + SetToNone(); + fValueType = kGrassShaderComponent; + fObjectKey = key; + } + + // read and write routines for export and reading in at runtime + void Read(hsStream *stream, hsResMgr* mgr) + { + SetToNone(); + + fID = stream->ReadSwap32(); + fValueType = stream->ReadSwap32(); + + // read the different types of data + int count; + switch ( fValueType ) + { + case kInt: + datarecord.fIntNumber = stream->ReadSwap32(); + break; + + case kFloat: + stream->ReadSwap(&datarecord.fFloatNumber); + break; + + case kBoolean: + datarecord.fBool = stream->ReadSwap32(); + break; + + case kString: + case kAnimationName: + count = stream->ReadSwap32(); + if ( count != 0 ) + { + datarecord.fString = TRACKED_NEW char[count+1]; + stream->ReadSwap(count,datarecord.fString); + } + else + datarecord.fString = nil; + break; + + case kSceneObject: + case kSceneObjectList: + case kActivatorList: + case kResponderList: + case kDynamicText: + case kGUIDialog: + case kExcludeRegion: + case kAnimation: + case kBehavior: + case kMaterial: + case kGUIPopUpMenu: + case kGUISkin: + case kWaterComponent: + case kSwimCurrentInterface: + case kClusterComponentList: + case kMaterialAnimation: + case kGrassShaderComponent: + fObjectKey = mgr->ReadKey(stream); + break; + } + } + + void Write(hsStream * stream, hsResMgr* mgr) + { + int count; + stream->WriteSwap32(fID); + stream->WriteSwap32(fValueType); + switch ( fValueType ) + { + case kInt: + stream->WriteSwap32(datarecord.fIntNumber); + break; + + case kFloat: + stream->WriteSwap(datarecord.fFloatNumber); + break; + + case kBoolean: + stream->WriteSwap32(datarecord.fBool); + break; + + case kString: + case kAnimationName: + if ( datarecord.fString != nil ) + count = hsStrlen(datarecord.fString)+1; + else + count = 0; + stream->WriteSwap(count); + if ( count != 0 ) + stream->WriteSwap(count,datarecord.fString); + break; + + case kSceneObject: + case kSceneObjectList: + case kActivatorList: + case kResponderList: + case kDynamicText: + case kGUIDialog: + case kExcludeRegion: + case kAnimation: + case kBehavior: + case kMaterial: + case kGUIPopUpMenu: + case kGUISkin: + case kWaterComponent: + case kSwimCurrentInterface: + case kClusterComponentList: + case kMaterialAnimation: + case kGrassShaderComponent: + mgr->WriteKey(stream, fObjectKey); + break; + + } + } +} plPythonParameter; + +#endif // plPythonParameter_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifier.cpp new file mode 100644 index 00000000..9107ee55 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifier.cpp @@ -0,0 +1,703 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" +#include "plPythonSDLModifier.h" +#include "cyPythonInterface.h" + +#include "plPythonFileMod.h" +#include "pyKey.h" +#include "cyMisc.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plAgeDescription/plAgeDescription.h" + +#include "../plSDL/plSDL.h" +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClient/plNetClientMgr.h" + +plStateDataRecord * GetAgeSDL() +{ + const plPythonSDLModifier * mod = plPythonSDLModifier::FindAgeSDL(); + if ( mod ) + return mod->GetStateCache(); + return nil; +} + + +#define PyLoggedAssert(cond, text) \ + if (!cond) PythonInterface::WriteToLog(text); \ + hsAssert(cond, text); + +plPythonSDLModifier::plPythonSDLModifier(plPythonFileMod* owner) : fOwner(owner) +{ + plStateDescriptor* desc = plSDLMgr::GetInstance()->FindDescriptor(fOwner->fPythonFile, plSDL::kLatestVersion); + if (desc) + { + // Create a Python SDL record with no values set + int numVars = desc->GetNumVars(); + for (int i = 0; i < numVars; i++) + { + plVarDescriptor* var = desc->GetVar(i); + + const char* name = var->GetName(); + int count = var->GetCount(); + + fMap[name] = SDLObj(nil, count, false); + } + } +} + +plPythonSDLModifier::~plPythonSDLModifier() +{ + SDLMap::iterator it; + for (it = fMap.begin(); it != fMap.end(); it++) + { + PyObject* obj = it->second.obj; + Py_XDECREF(obj); + } +} + +PyObject* plPythonSDLModifier::GetItem(const char* key) +{ + SDLMap::iterator it = fMap.find(key); + + if (it == fMap.end()) + { + char errmsg[256]; + sprintf(errmsg,"SDL key %s not found",key); + PyErr_SetString(PyExc_KeyError, errmsg); + PYTHON_RETURN_ERROR; + } + + PyObject* val = it->second.obj; + if (!val) + val = PyTuple_New(0); + + Py_INCREF(val); + return val; +} + +void plPythonSDLModifier::ISetItem(const char* key, PyObject* value) +{ + if (!value || !PyTuple_Check(value)) + { + PyLoggedAssert(0, "[SDL] Trying to set a tuple value to something that isn't a tuple"); + return; + } + + SDLMap::iterator it = fMap.find(key); + + if (it == fMap.end()) + { + PyLoggedAssert(0, "[SDL] Tried to set a nonexistent SDL value"); + return; + } + + SDLObj& oldObj = it->second; + + if (oldObj.size != 0 && PyTuple_Size(value) != oldObj.size) + { + PyLoggedAssert(0, "[SDL] Wrong size for a fixed size SDL value"); + return; + } + + Py_XDECREF(oldObj.obj); + + Py_XINCREF(value); + oldObj.obj = value; +} + +void plPythonSDLModifier::SendToClients(const char* key) +{ + SDLMap::iterator it = fMap.find(key); + if (it != fMap.end()) + it->second.sendToClients = true; +} + +void plPythonSDLModifier::SetNotify(pyKey& selfkey, const char* key, float tolerance) +{ + AddNotifyForVar(selfkey.getKey(), key, tolerance); +} + +void plPythonSDLModifier::SetItem(const char* key, PyObject* value) +{ + ISetItem(key, value); + IDirtySynchState(key); +} + +void plPythonSDLModifier::SetItemFromSDLVar(plSimpleStateVariable* var) +{ + const char* name = var->GetName(); + + // Get the SDL value in Python format + PyObject* pyVar = ISDLVarToPython(var); + + ISetItem(name, pyVar); + Py_XDECREF(pyVar); + // let the sender do the dirty sync state stuff +} + + +void plPythonSDLModifier::SetDefault(const char* key, PyObject* value) +{ + ISetItem(key, value); +} + +void plPythonSDLModifier::SetItemIdx(const char* key, int idx, PyObject* value, hsBool sendImmediate) +{ + if (!value) + { + PyLoggedAssert(0, "[SDL] Trying to set a value to nil"); + return; + } + + SDLMap::iterator it = fMap.find(key); + + if (it == fMap.end()) + { + PyLoggedAssert(0, "[SDL] Tried to set a nonexistent SDL value"); + return; + } + + PyObject* pyTuple = it->second.obj; + int size = it->second.size; + + if (size != 0 && idx >= size) + { + PyLoggedAssert(0, "[SDL] Trying to resize a SDL value that can't be"); + return; + } + + if (pyTuple) + { + if (PyTuple_Size(pyTuple) <= idx) + { + int oldsize = PyTuple_Size(pyTuple); + _PyTuple_Resize(&pyTuple, idx+1); + // initialize the tuple elements to None, because Python don't like NULLs + int j; + for ( j=oldsize; jsecond.obj = pyTuple; + } + + Py_XINCREF(value); // PyTuple_SetItem doesn't increment the ref count + PyTuple_SetItem(pyTuple, idx, value); + + IDirtySynchState(key, sendImmediate); +} + + +const char* plPythonSDLModifier::GetSDLName() const +{ + return fOwner->fPythonFile; +} + +void plPythonSDLModifier::SetFlags(const char* name, bool sendImmediate, bool skipOwnershipCheck) +{ + SDLMap::iterator it = fMap.find(name); + if (it != fMap.end()) + { + it->second.sendImmediate = sendImmediate; + it->second.skipLocalCheck = skipOwnershipCheck; + } +} + +void plPythonSDLModifier::SetTagString(const char* name, const char* tag) +{ + SDLMap::iterator it = fMap.find(name); + if (it != fMap.end()) + { + it->second.hintString = tag; + } +} + +void plPythonSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + plStateDataRecord::SimpleVarsList vars; + int num = srcState->GetUsedVars(&vars); + + for (int i = 0; i < num; i++) + { + plSimpleStateVariable* var = vars[i]; + + const char* name = var->GetName(); + + // Get the SDL value in Python format + PyObject* pyVar = ISDLVarToPython(var); + + SetItem(name, pyVar); + Py_XDECREF(pyVar); + } + + // Notify the Python code that we updated the SDL record + if (fOwner->fPyFunctionInstances[plPythonFileMod::kfunc_Load] != nil) + { + PyObject* retVal = PyObject_CallMethod(fOwner->fPyFunctionInstances[plPythonFileMod::kfunc_Load], + fOwner->fFunctionNames[plPythonFileMod::kfunc_Load], nil); + if (retVal == nil) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fOwner->fPyFunctionInstances[plPythonFileMod::kfunc_Load] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + fOwner->ReportError(); + } + Py_XDECREF(retVal); + } + + // display any output + fOwner->DisplayPythonOutput(); +} + +void plPythonSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + SDLMap::iterator it = fMap.begin(); + for (; it != fMap.end(); it++) + { + IPythonVarToSDL(dstState, it->first.c_str()); + } +} + +void plPythonSDLModifier::IDirtySynchState(const char* name, hsBool sendImmediate) +{ + SDLMap::iterator it = fMap.find(name); + if (it != fMap.end()) + { + UInt32 flags = 0; + if (it->second.sendToClients) + flags |= kBCastToClients; + if (it->second.sendImmediate) + flags |= kSendImmediately; + if (it->second.skipLocalCheck) + flags |= kSkipLocalOwnershipCheck; + if (sendImmediate) + flags |= kSendImmediately; + + GetTarget()->DirtySynchState(fOwner->fPythonFile, flags); + } +} + +bool plPythonSDLModifier::IPythonVarIdxToSDL(plSimpleStateVariable* var, int varIdx, int type, PyObject* pyVar, + const char* hintstring) +{ + switch (type) + { + case plVarDescriptor::kShort: + case plVarDescriptor::kByte: + case plVarDescriptor::kBool: + case plVarDescriptor::kInt: + if (PyInt_Check(pyVar)) + { + int v = PyInt_AsLong(pyVar); + var->Set(v, varIdx); + if (hintstring) + var->GetNotificationInfo().SetHintString(hintstring); + return true; + } + else if (PyLong_Check(pyVar)) + { + int v = (int)PyLong_AsLong(pyVar); + var->Set(v, varIdx); + if (hintstring) + var->GetNotificationInfo().SetHintString(hintstring); + return true; + } + + else if (PyFloat_Check(pyVar)) + { + int v = (int)PyFloat_AsDouble(pyVar); + var->Set(v, varIdx); + if (hintstring) + var->GetNotificationInfo().SetHintString(hintstring); + return true; + } + break; + + case plVarDescriptor::kFloat: + if (PyFloat_Check(pyVar)) + { + float v = (float)PyFloat_AsDouble(pyVar); + var->Set(v, varIdx); + if (hintstring) + var->GetNotificationInfo().SetHintString(hintstring); + return true; + } + // does python think that its an integer? too bad, we'll make it a float anyhow! + else if (PyInt_Check(pyVar)) + { + float v = (float)PyInt_AsLong(pyVar); + var->Set(v, varIdx); + if (hintstring) + var->GetNotificationInfo().SetHintString(hintstring); + return true; + } + break; + + case plVarDescriptor::kString32: + if (PyString_Check(pyVar)) + { + char* v = PyString_AsString(pyVar); + var->Set(v, varIdx); + if (hintstring) + var->GetNotificationInfo().SetHintString(hintstring); + } + break; + + case plVarDescriptor::kKey: + { + pyKey* key = PythonInterface::GetpyKeyFromPython(pyVar); + if ( key ) + var->Set(key->getKey(),varIdx); + if (hintstring) + var->GetNotificationInfo().SetHintString(hintstring); + } + break; + + case plVarDescriptor::kDouble: + if (PyFloat_Check(pyVar)) + { + double v = PyFloat_AsDouble(pyVar); + var->Set(v, varIdx); + if (hintstring) + var->GetNotificationInfo().SetHintString(hintstring); + return true; + } + else if (PyInt_Check(pyVar)) + { + double v = (double)PyInt_AsLong(pyVar); + var->Set(v, varIdx); + if (hintstring) + var->GetNotificationInfo().SetHintString(hintstring); + return true; + } + break; + + case plVarDescriptor::kAgeTimeOfDay: + break; + + default: + hsAssert(0, "Not supported yet"); + } + + return false; +} + +void plPythonSDLModifier::IPythonVarToSDL(plStateDataRecord* state, const char* name) +{ + plSimpleStateVariable* var = state->FindVar(name); + PyObject* pyVar = nil; + SDLMap::iterator it = fMap.find(name); + if (it != fMap.end()) + pyVar = it->second.obj; + + if (!var || !pyVar) + return; + + if (PyTuple_Check(pyVar)) + { + int count = PyTuple_Size(pyVar); + plSimpleVarDescriptor::Type type = var->GetSimpleVarDescriptor()->GetType(); + + for (int i = 0; i < count; i++) + { + PyObject* pyVarItem = PyTuple_GetItem(pyVar, i); + if (pyVarItem) + IPythonVarIdxToSDL(var, i, type, pyVarItem,it->second.hintString.c_str()); + } + } +} + +PyObject* plPythonSDLModifier::ISDLVarIdxToPython(plSimpleStateVariable* var, int type, int idx) +{ + switch (type) + { + case plVarDescriptor::kShort: + case plVarDescriptor::kByte: + case plVarDescriptor::kInt: + { + int v; + var->Get(&v, idx); + return PyInt_FromLong(v); + } + + case plVarDescriptor::kFloat: + case plVarDescriptor::kAgeTimeOfDay: + { + float v; + var->Get(&v, idx); + return PyFloat_FromDouble(v); + } + + case plVarDescriptor::kBool: + { + bool v; + var->Get(&v, idx); + return PyLong_FromLong(v); + } + + case plVarDescriptor::kString32: + { + char v[256]; + var->Get(v, idx); + return PyString_FromString(v); + } + + case plVarDescriptor::kKey: + { + plKey v; + var->Get(&v, idx); + PyObject* keyObj = pyKey::New(v); + return keyObj; + } + + case plVarDescriptor::kDouble: + { + double v; + var->Get(&v, idx); + return PyFloat_FromDouble(v); + } + +// case plVarDescriptor::kStateDescriptor: +// case plVarDescriptor::kCreatable: +// case plVarDescriptor::kVector3: +// case plVarDescriptor::kPoint3: +// case plVarDescriptor::kRGB: +// case plVarDescriptor::kRGBA: +// case plVarDescriptor::kQuaternion: + default: + hsAssert(0, "Not supported yet"); + } + + PYTHON_RETURN_NONE; +} + +PyObject* plPythonSDLModifier::ISDLVarToPython(plSimpleStateVariable* var) +{ + plSimpleVarDescriptor::Type type = var->GetSimpleVarDescriptor()->GetType(); + int count = var->GetCount(); + + PyObject* pyTuple = PyTuple_New(count); + + for (int i = 0; i < count; i++) + { + PyObject* varVal = ISDLVarIdxToPython(var, type, i); + PyTuple_SetItem(pyTuple, i, varVal); + } + + return pyTuple; +} + +bool plPythonSDLModifier::HasSDL(const char* pythonFile) +{ + return (plSDLMgr::GetInstance()->FindDescriptor(pythonFile, plSDL::kLatestVersion) != nil); +} + +const plSDLModifier *ExternFindAgeSDL() +{ + return plPythonSDLModifier::FindAgeSDL(); +} + +const plPythonSDLModifier *ExternFindAgePySDL() +{ + return plPythonSDLModifier::FindAgeSDL(); +} + +const plPythonSDLModifier* plPythonSDLModifier::FindAgeSDL() +{ + const char* ageName = cyMisc::GetAgeName(); + + if (strcmp(ageName, "") == 0) + return nil; // don't have an age, probably because we're running in max? + + // find the Age Global object + plLocation loc = plKeyFinder::Instance().FindLocation(ageName,plAgeDescription::GetCommonPage(plAgeDescription::kGlobal)); + if ( loc.IsValid() ) + { + plUoid oid(loc,plPythonFileMod::Index(), plPythonFileMod::kGlobalNameKonstant); + if ( oid.IsValid() ) + { + plKey key = hsgResMgr::ResMgr()->FindKey(oid); + + plPythonFileMod *pfmod = plPythonFileMod::ConvertNoRef(key ? key->ObjectIsLoaded() : nil); + if ( pfmod ) + { + plPythonSDLModifier * sdlMod = pfmod->GetSDLMod(); + if(sdlMod) + // we found it! + return sdlMod; + + plNetClientApp::StaticErrorMsg("pfmod %s has a nil python SDL modifier for age sdl %s", + pfmod->GetKeyName() ? pfmod->GetKeyName() : "?", ageName); + } + else + { + char str[256]; + if (!key) + plNetClientApp::StaticErrorMsg("nil key %s for age sdl %s", ageName, oid.StringIze(str)); + else + if (!key->ObjectIsLoaded()) + plNetClientApp::StaticErrorMsg("key %s not loaded for age sdl %s", + key->GetName() ? key->GetName() : "?", ageName); + else + if (!plPythonFileMod::ConvertNoRef(key->ObjectIsLoaded())) + plNetClientApp::StaticErrorMsg("key %s is not a python file mod for age sdl %s", + key->GetName() ? key->GetName() : "?", ageName); + } + } + else + plNetClientApp::StaticErrorMsg("Invalid plUoid for age sdl %s", ageName); + } + else + plNetClientApp::StaticErrorMsg("Invalid plLocation for age sdl %s", ageName); + + // couldn't find one (maybe because we didn't look) + return nil; +} + +plKey ExternFindAgeSDLTarget() +{ + return plPythonSDLModifier::FindAgeSDLTarget(); +} + +plKey plPythonSDLModifier::FindAgeSDLTarget() +{ + + // find the Age Global object + plLocation loc = plKeyFinder::Instance().FindLocation(cyMisc::GetAgeName(),plAgeDescription::GetCommonPage(plAgeDescription::kGlobal)); + if ( loc.IsValid() ) + { + plUoid oid(loc,plPythonFileMod::Index(), plPythonFileMod::kGlobalNameKonstant); + if ( oid.IsValid() ) + { + plKey key = hsgResMgr::ResMgr()->FindKey(oid); + + plPythonFileMod *pfmod = plPythonFileMod::ConvertNoRef(key ? key->GetObjectPtr() : nil); + if ( pfmod ) + { + if (pfmod->GetTarget(0)) + return pfmod->GetTarget(0)->GetKey(); + } + } + } + + // couldn't find one (maybe because we didn't look) + return nil; +} + + +///////////////////////////////////////////// + +pySDLModifier::pySDLModifier(plPythonSDLModifier* sdlMod) +{ + fRecord = sdlMod; +} + + +PyObject* pySDLModifier::GetAgeSDL() +{ + const char* ageName = cyMisc::GetAgeName(); + if (strcmp(ageName, "") == 0) + PYTHON_RETURN_NONE; // just return none if the age is blank (running in max?) + + const plPythonSDLModifier* ageSDL = plPythonSDLModifier::FindAgeSDL(); + if ( ageSDL ) + { + return pySDLModifier::New((plPythonSDLModifier*)ageSDL); + } + + // didn't find one, throw an exception for the python programmer to chew on + char errmsg[256]; + sprintf(errmsg,"Age Global SDL for %s does not exist!",ageName); + plNetClientApp::StaticErrorMsg(errmsg); + PyErr_SetString(PyExc_KeyError, errmsg); + PYTHON_RETURN_ERROR; +} + + +void pySDLModifier::SetDefault(pySDLModifier& self, std::string key, PyObject* value) +{ + self.fRecord->SetDefault(key.c_str(), value); +} + +void pySDLModifier::SendToClients(pySDLModifier& self, std::string key) +{ + self.fRecord->SendToClients(key.c_str()); +} + +void pySDLModifier::SetNotify(pySDLModifier& self, pyKey& selfkey, std::string key, float tolerance) +{ + self.fRecord->SetNotify(selfkey, key.c_str(), tolerance); +} + +PyObject* pySDLModifier::GetItem(pySDLModifier& self, std::string key) +{ + return self.fRecord->GetItem(key.c_str()); +} + +void pySDLModifier::SetItem(pySDLModifier& self, std::string key, PyObject* value) +{ + self.fRecord->SetItem(key.c_str(), value); +} + + +void pySDLModifier::SetItemIdx(pySDLModifier& self, std::string key, int idx, PyObject* value) +{ + self.fRecord->SetItemIdx(key.c_str(), idx, value); +} + +void pySDLModifier::SetItemIdxImmediate(pySDLModifier& self, std::string key, int idx, PyObject* value) +{ + self.fRecord->SetItemIdx(key.c_str(), idx, value, true); +} + +void pySDLModifier::SetFlags(pySDLModifier& self, const char* name, bool sendImmediate, bool skipOwnershipCheck) +{ + self.fRecord->SetFlags(name,sendImmediate,skipOwnershipCheck); +} + +void pySDLModifier::SetTagString(pySDLModifier& self, const char* name, const char* tag) +{ + self.fRecord->SetTagString(name,tag); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifier.h new file mode 100644 index 00000000..7d0570bf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifier.h @@ -0,0 +1,141 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPythonSDLModifier_h_inc +#define plPythonSDLModifier_h_inc + +class plPythonFileMod; +class plStateDataRecord; +class plSimpleStateVariable; + +#include "hsStlUtils.h" +#include "../plModifier/plSDLModifier.h" + +#include +#include "pyGlueHelpers.h" + + +// hack for plNetClientVNodeMgr single-player mode SDLHook stuff. +plStateDataRecord * GetAgeSDL(); + +class pyKey; +// +// The fields of a SDL record in Python format. +// If the Python code changes a value an update is sent automatically +// +class plPythonSDLModifier : public plSDLModifier +{ +protected: + class SDLObj + { + public: + PyObject* obj; + int size; // 0 for resizable + bool sendToClients; + bool skipLocalCheck; + bool sendImmediate; + std::string hintString; + SDLObj() : obj(nil), size(-1), sendToClients(false) {} + SDLObj(PyObject* obj, int size, bool sendToClients) : obj(obj), size(size), sendToClients(sendToClients) {} + }; + typedef std::map SDLMap; + SDLMap fMap; + plPythonFileMod* fOwner; + + plPythonSDLModifier() {} + + PyObject* ISDLVarToPython(plSimpleStateVariable* var); + PyObject* ISDLVarIdxToPython(plSimpleStateVariable* var, int type, int idx); + + void IPythonVarToSDL(plStateDataRecord* state, const char* name); + bool IPythonVarIdxToSDL(plSimpleStateVariable* var, int varIdx, int type, PyObject* pyVar, const char* hintstring); + + void ISetItem(const char* key, PyObject* value); + void IDirtySynchState(const char* name, hsBool sendImmediate = false); + + void IPutCurrentStateIn(plStateDataRecord* dstState); + void ISetCurrentStateFrom(const plStateDataRecord* srcState); +public: + plPythonSDLModifier(plPythonFileMod* owner); + ~plPythonSDLModifier(); + + CLASSNAME_REGISTER(plPythonSDLModifier); + GETINTERFACE_ANY(plPythonSDLModifier, plSDLModifier); + + virtual const char* GetSDLName() const; + virtual void SetItemFromSDLVar(plSimpleStateVariable* var); + + static bool HasSDL(const char* pythonFile); + // find the Age global SDL guy... if there is one + static const plPythonSDLModifier* FindAgeSDL(); + static plKey FindAgeSDLTarget(); + + void SetDefault(const char* key, PyObject* value); + void SendToClients(const char* key); + void SetNotify(pyKey& selfkey, const char* key, float tolerance); + + PyObject* GetItem(const char* key); + void SetItem(const char* key, PyObject* value); + void SetItemIdx(const char* key, int idx, PyObject* value, hsBool sendImmediate = false); + void SetFlags(const char* name, bool sendImmediate, bool skipOwnershipCheck); + void SetTagString(const char* name, const char* tag); +}; + +// A wrapper for plPythonSDLModifier that Python uses +class pySDLModifier +{ +protected: + plPythonSDLModifier* fRecord; + + pySDLModifier(plPythonSDLModifier* sdlMod); + pySDLModifier() {} +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptSDL); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(plPythonSDLModifier *sdlMod); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pySDLModifier object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pySDLModifier); // converts a PyObject to a pySDLModifier (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaMethods(std::vector &methods); + + // global function to get the GrandMaster Age SDL object + static PyObject* GetAgeSDL(); + + static void SetDefault(pySDLModifier& self, std::string key, PyObject* value); + static void SendToClients(pySDLModifier& self, std::string key); + static void SetNotify(pySDLModifier& self, pyKey& selfkey, std::string key, float tolerance); + + static PyObject* GetItem(pySDLModifier& self, std::string key); + static void SetItem(pySDLModifier& self, std::string key, PyObject* value); + static void SetItemIdx(pySDLModifier& self, std::string key, int idx, PyObject* value); + static void SetItemIdxImmediate(pySDLModifier& self, std::string key, int idx, PyObject* value); + static void SetFlags(pySDLModifier& self, const char* name, bool sendImmediate, bool skipOwnershipCheck); + static void SetTagString(pySDLModifier& self, const char* name, const char* tag); + +}; + +#endif // plPythonSDLModifier_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifierGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifierGlue.cpp new file mode 100644 index 00000000..e16cddc5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/plPythonSDLModifierGlue.cpp @@ -0,0 +1,243 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPythonSDLModifier.h" +#include "pyKey.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptSDL, pySDLModifier); + +PYTHON_DEFAULT_NEW_DEFINITION(ptSDL, pySDLModifier) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptSDL) + +PYTHON_INIT_DEFINITION(ptSDL, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptSDL, setIndex, args) +{ + char* key; + int idx; + PyObject* value = NULL; + if (!PyArg_ParseTuple(args, "siO", &key, &idx, &value)) + { + PyErr_SetString(PyExc_TypeError, "setIndex expects a string, int, and an object"); + PYTHON_RETURN_ERROR; + } + pySDLModifier::SetItemIdx(*(self->fThis), key, idx, value); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSDL, setIndexNow, args) +{ + char* key; + int idx; + PyObject* value = NULL; + if (!PyArg_ParseTuple(args, "siO", &key, &idx, &value)) + { + PyErr_SetString(PyExc_TypeError, "setIndexNow expects a string, int, and an object"); + PYTHON_RETURN_ERROR; + } + pySDLModifier::SetItemIdxImmediate(*(self->fThis), key, idx, value); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSDL, setDefault, args) +{ + char* key; + PyObject* value = NULL; + if (!PyArg_ParseTuple(args, "sO", &key, &value)) + { + PyErr_SetString(PyExc_TypeError, "setDefault expects a string and a tuple"); + PYTHON_RETURN_ERROR; + } + if (!PyTuple_Check(value)) + { + PyErr_SetString(PyExc_TypeError, "setDefault expects a string and a tuple"); + PYTHON_RETURN_ERROR; + } + pySDLModifier::SetDefault(*(self->fThis), key, value); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSDL, sendToClients, args) +{ + char* key; + if (!PyArg_ParseTuple(args, "s", &key)) + { + PyErr_SetString(PyExc_TypeError, "sendToClients expects a string"); + PYTHON_RETURN_ERROR; + } + pySDLModifier::SendToClients(*(self->fThis), key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSDL, setNotify, args) +{ + PyObject* selfKeyObj; + char* key; + float tolerance; + if (!PyArg_ParseTuple(args, "Osf", &selfKeyObj, &key, &tolerance)) + { + PyErr_SetString(PyExc_TypeError, "setNotify expects a ptKey, string, and float"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(selfKeyObj)) + { + PyErr_SetString(PyExc_TypeError, "setNotify expects a ptKey, string, and float"); + PYTHON_RETURN_ERROR; + } + pyKey* selfKey = pyKey::ConvertFrom(selfKeyObj); + pySDLModifier::SetNotify(*(self->fThis), *selfKey, key, tolerance); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSDL, setFlags, args) +{ + char* key; + char sendImmediate, skipOwnershipCheck; + if (!PyArg_ParseTuple(args, "sbb", &key, &sendImmediate, &skipOwnershipCheck)) + { + PyErr_SetString(PyExc_TypeError, "setFlags expects a string and two booleans"); + PYTHON_RETURN_ERROR; + } + pySDLModifier::SetFlags(*(self->fThis), key, sendImmediate != 0, skipOwnershipCheck != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSDL, setTagString, args) +{ + char* key; + char* tag; + if (!PyArg_ParseTuple(args, "ss", &key, &tag)) + { + PyErr_SetString(PyExc_TypeError, "setTagString expects two strings"); + PYTHON_RETURN_ERROR; + } + pySDLModifier::SetTagString(*(self->fThis), key, tag); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptSDL) + PYTHON_METHOD(ptSDL, setIndex, "Params: key,idx,value\nSets the value at a specific index in the tuple,\n" + "so you don't have to pass the whole thing in"), + PYTHON_METHOD(ptSDL, setIndexNow, "Params: key,idx,value\nSame as setIndex but sends immediately"), + PYTHON_METHOD(ptSDL, setDefault, "Params: key,value\nLike setitem, but doesn't broadcast over the net.\n" + "Only use for setting defaults that everyone will\n" + "already know (from reading it off disk)"), + PYTHON_METHOD(ptSDL, sendToClients, "Params: key\nSets it so changes to this key are sent to the\n" + "server AND the clients. (Normally it just goes\n" + "to the server.)"), + PYTHON_METHOD(ptSDL, setNotify, "Params: selfkey,key,tolerance\nSets the OnSDLNotify to be called when 'key'\n" + "SDL variable changes by 'tolerance' (if number)"), + PYTHON_METHOD(ptSDL, setFlags, "Params: name,sendImmediate,skipOwnershipCheck\nSets the flags for a variable in this SDL"), + PYTHON_METHOD(ptSDL, setTagString, "Params: name,tag\nSets the tag string for a variable"), +PYTHON_END_METHODS_TABLE; + +PyObject* ptSDL_subscript(ptSDL* self, PyObject* key) +{ + if (!PyString_Check(key)) + { + PyErr_SetString(PyExc_TypeError, "SDL indexes must be strings"); + PYTHON_RETURN_ERROR; + } + char *keyStr = PyString_AsString(key); + return pySDLModifier::GetItem(*(self->fThis), keyStr); +} + +int ptSDL_ass_subscript(ptSDL* self, PyObject* key, PyObject* value) +{ + if (value == NULL) // remove, which isn't supported + { + PyErr_SetString(PyExc_RuntimeError, "Cannot remove sdl records"); + return -1; // error return + } + if (!PyString_Check(key)) + { + PyErr_SetString(PyExc_TypeError, "SDL indexes must be strings"); + return -1; // error return + } + if (!PyTuple_Check(value)) + { + PyErr_SetString(PyExc_TypeError, "SDL values must be tuples"); + return -1; // error return + } + char* keyStr = PyString_AsString(key); + pySDLModifier::SetItem(*(self->fThis), keyStr, value); + return 0; // success return +} + +PYTHON_START_AS_MAPPING_TABLE(ptSDL) + 0, /* mp_length */ + (binaryfunc)ptSDL_subscript, /* mp_subscript */ + (objobjargproc)ptSDL_ass_subscript, /* mp_ass_subscript */ +PYTHON_END_AS_MAPPING_TABLE; + +#define ptSDL_COMPARE PYTHON_NO_COMPARE +#define ptSDL_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptSDL_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptSDL_AS_MAPPING PYTHON_DEFAULT_AS_MAPPING(ptSDL) +#define ptSDL_STR PYTHON_NO_STR +#define ptSDL_RICH_COMPARE PYTHON_NO_RICH_COMPARE +#define ptSDL_GETSET PYTHON_NO_GETSET +#define ptSDL_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptSDL, "SDL accessor"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptSDL, pySDLModifier) + +PyObject *pySDLModifier::New(plPythonSDLModifier *sdlMod) +{ + ptSDL *newObj = (ptSDL*)ptSDL_type.tp_new(&ptSDL_type, NULL, NULL); + newObj->fThis->fRecord = sdlMod; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptSDL, pySDLModifier) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptSDL, pySDLModifier) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pySDLModifier::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptSDL); + PYTHON_CLASS_IMPORT_END(m); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetAgeSDL, "Returns the global ptSDL for the current Age") +{ + return pySDLModifier::GetAgeSDL(); +} + +void pySDLModifier::AddPlasmaMethods(std::vector &methods) +{ + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetAgeSDL); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStruct.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStruct.cpp new file mode 100644 index 00000000..5ab68328 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStruct.cpp @@ -0,0 +1,217 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStlUtils.h" + +#include "pyAgeInfoStruct.h" + +/////////////////////////////////////////////////////////////////////////// + +pyAgeInfoStruct::pyAgeInfoStruct() +{ +} + +pyAgeInfoStruct::pyAgeInfoStruct(plAgeInfoStruct * info) +{ + fAgeInfo.CopyFrom( info ); +} + +pyAgeInfoStruct::~pyAgeInfoStruct() +{ +} + +bool pyAgeInfoStruct::operator==(const pyAgeInfoStruct &other) const +{ + return fAgeInfo.IsEqualTo( other.GetAgeInfo() ); +} + +///////////////////////////////////////////////////////////////////// + +void pyAgeInfoStruct::CopyFrom( const pyAgeInfoStruct & other ) +{ + fAgeInfo.CopyFrom( other.GetAgeInfo() ); +} + +void pyAgeInfoStruct::CopyFromRef( const pyAgeInfoStructRef & other ) +{ + fAgeInfo.CopyFrom( other.GetAgeInfo() ); +} + +const char * pyAgeInfoStruct::GetAgeFilename() const +{ + return fAgeInfo.GetAgeFilename(); +} + +void pyAgeInfoStruct::SetAgeFilename( const char * v ) +{ + fAgeInfo.SetAgeFilename( v ); +} + +const char * pyAgeInfoStruct::GetAgeInstanceName() const +{ + return fAgeInfo.GetAgeInstanceName(); +} + +void pyAgeInfoStruct::SetAgeInstanceName( const char * v ) +{ + fAgeInfo.SetAgeInstanceName( v ); +} + +const char * pyAgeInfoStruct::GetAgeUserDefinedName() const +{ + return fAgeInfo.GetAgeUserDefinedName(); +} + +void pyAgeInfoStruct::SetAgeUserDefinedName( const char * v ) +{ + fAgeInfo.SetAgeUserDefinedName( v ); +} + +const char * pyAgeInfoStruct::GetAgeDescription() const +{ + return fAgeInfo.GetAgeDescription(); +} + +void pyAgeInfoStruct::SetAgeDescription( const char * v ) +{ + fAgeInfo.SetAgeDescription( v ); +} + +const char * pyAgeInfoStruct::GetAgeInstanceGuid() const +{ + fAgeInstanceGuidStr = fAgeInfo.GetAgeInstanceGuid()->AsStdString(); + return fAgeInstanceGuidStr.c_str(); +} + +void pyAgeInfoStruct::SetAgeInstanceGuid( const char * guid ) +{ + fAgeInfo.SetAgeInstanceGuid( &plUUID( guid ) ); +} + +Int32 pyAgeInfoStruct::GetAgeSequenceNumber() const +{ + return fAgeInfo.GetAgeSequenceNumber(); +} + +void pyAgeInfoStruct::SetAgeSequenceNumber( Int32 v ) +{ + fAgeInfo.SetAgeSequenceNumber( v ); +} + +Int32 pyAgeInfoStruct::GetAgeLanguage() const +{ + return fAgeInfo.GetAgeLanguage(); +} + +void pyAgeInfoStruct::SetAgeLanguage( Int32 v ) +{ + fAgeInfo.SetAgeLanguage( v ); +} + +const char * pyAgeInfoStruct::GetDisplayName() const +{ + Int32 seq = GetAgeSequenceNumber(); + if ( seq>0 ) + xtl::format( fDisplayName, "%s (%d) %s", GetAgeUserDefinedName(), seq, GetAgeInstanceName() ); + else + xtl::format( fDisplayName, "%s %s", GetAgeUserDefinedName(), GetAgeInstanceName() ); + return fDisplayName.c_str(); +} + + +///////////////////////////////////////////////////////////////////// + +plAgeInfoStruct pyAgeInfoStructRef::fDefaultAgeInfo; // created so a default constructor could be made for python. Do NOT use + +void pyAgeInfoStructRef::CopyFrom( const pyAgeInfoStruct & other ) +{ + fAgeInfo.CopyFrom( other.GetAgeInfo() ); +} + +void pyAgeInfoStructRef::CopyFromRef( const pyAgeInfoStructRef & other ) +{ + fAgeInfo.CopyFrom( other.GetAgeInfo() ); +} + +const char * pyAgeInfoStructRef::GetAgeFilename() const +{ + return fAgeInfo.GetAgeFilename(); +} + +void pyAgeInfoStructRef::SetAgeFilename( const char * v ) +{ + fAgeInfo.SetAgeFilename( v ); +} + +const char * pyAgeInfoStructRef::GetAgeInstanceName() const +{ + return fAgeInfo.GetAgeInstanceName(); +} + +void pyAgeInfoStructRef::SetAgeInstanceName( const char * v ) +{ + fAgeInfo.SetAgeInstanceName( v ); +} + +const char * pyAgeInfoStructRef::GetAgeUserDefinedName() const +{ + return fAgeInfo.GetAgeUserDefinedName(); +} + +void pyAgeInfoStructRef::SetAgeUserDefinedName( const char * v ) +{ + fAgeInfo.SetAgeUserDefinedName( v ); +} + +const char * pyAgeInfoStructRef::GetAgeInstanceGuid() const +{ + fAgeInstanceGuidStr = fAgeInfo.GetAgeInstanceGuid()->AsStdString(); + return fAgeInstanceGuidStr.c_str(); +} + +void pyAgeInfoStructRef::SetAgeInstanceGuid( const char * guid ) +{ + fAgeInfo.SetAgeInstanceGuid( &plUUID( guid ) ); +} + +Int32 pyAgeInfoStructRef::GetAgeSequenceNumber() const +{ + return fAgeInfo.GetAgeSequenceNumber(); +} + +void pyAgeInfoStructRef::SetAgeSequenceNumber( Int32 v ) +{ + fAgeInfo.SetAgeSequenceNumber( v ); +} + +const char * pyAgeInfoStructRef::GetDisplayName() const +{ + Int32 seq = GetAgeSequenceNumber(); + if ( seq>0 ) + xtl::format( fDisplayName, "%s (%d) %s", GetAgeUserDefinedName(), seq, GetAgeInstanceName() ); + else + xtl::format( fDisplayName, "%s %s", GetAgeUserDefinedName(), GetAgeInstanceName() ); + return fDisplayName.c_str(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStruct.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStruct.h new file mode 100644 index 00000000..5ef1c6a8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStruct.h @@ -0,0 +1,134 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyAgeInfoStruct_h_inc +#define pyAgeInfoStruct_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "../plNetCommon/plNetServerSessionInfo.h" + +#include +#include "pyGlueHelpers.h" + +////////////////////////////////////////////////////////////////////// +// +// pyAgeInfoStruct - a wrapper class to provide interface to the plAgeInfoStruct +// +////////////////////////////////////////////////////////////////////// + +class pyVaultAgeInfoNode; +class pyAgeInfoStructRef; + + +class pyAgeInfoStruct +{ +private: + plAgeInfoStruct fAgeInfo; + mutable std::string fAgeInstanceGuidStr; // for getting Age Instance GUID + mutable std::string fDisplayName; // used by GetDisplayName() + +protected: + pyAgeInfoStruct(); + pyAgeInfoStruct(plAgeInfoStruct * info); + +public: + ~pyAgeInfoStruct(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptAgeInfoStruct); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(plAgeInfoStruct *info); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyAgeInfoStruct object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyAgeInfoStruct); // converts a PyObject to a pyAgeInfoStruct (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + bool operator==(const pyAgeInfoStruct &other) const; + bool operator!=(const pyAgeInfoStruct &other) const { return !(other==*this); } + plAgeInfoStruct * GetAgeInfo() { return &fAgeInfo; } + const plAgeInfoStruct * GetAgeInfo() const { return &fAgeInfo; } + static void PythonModDef(); + void CopyFrom( const pyAgeInfoStruct & other ); + void CopyFromRef( const pyAgeInfoStructRef & other ); + const char * GetAgeFilename() const; + void SetAgeFilename( const char * v ); + const char * GetAgeInstanceName() const; + void SetAgeInstanceName( const char * v ); + const char * GetAgeUserDefinedName() const; + void SetAgeUserDefinedName( const char * v ); + const char * GetAgeDescription() const; + void SetAgeDescription( const char * v ); + const char * GetAgeInstanceGuid() const; + void SetAgeInstanceGuid( const char * guid ); + Int32 GetAgeSequenceNumber() const; + void SetAgeSequenceNumber( Int32 v ); + Int32 GetAgeLanguage() const; + void SetAgeLanguage( Int32 v ); + const char * GetDisplayName() const; +}; + +class pyAgeInfoStructRef +{ +private: + static plAgeInfoStruct fDefaultAgeInfo; // created so a default constructor could be made for python. Do NOT use + + plAgeInfoStruct & fAgeInfo; + mutable std::string fAgeInstanceGuidStr; // for getting Age Instance GUID + mutable std::string fDisplayName; // used by GetDisplayName() + +protected: + pyAgeInfoStructRef(): fAgeInfo( fDefaultAgeInfo ) {} // only here for the python glue... do NOT call directly + pyAgeInfoStructRef(plAgeInfoStruct & info): fAgeInfo( info ){} + +public: + ~pyAgeInfoStructRef() {} + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptAgeInfoStructRef); + static PyObject *New(plAgeInfoStruct &info); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyAgeInfoStructRef object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyAgeInfoStructRef); // converts a PyObject to a pyAgeInfoStructRef (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + plAgeInfoStruct * GetAgeInfo() { return &fAgeInfo; } + const plAgeInfoStruct * GetAgeInfo() const { return &fAgeInfo; } + void CopyFrom( const pyAgeInfoStruct & other ); + void CopyFromRef( const pyAgeInfoStructRef & other ); + const char * GetAgeFilename() const; + void SetAgeFilename( const char * v ); + const char * GetAgeInstanceName() const; + void SetAgeInstanceName( const char * v ); + const char * GetAgeUserDefinedName() const; + void SetAgeUserDefinedName( const char * v ); + const char * GetAgeInstanceGuid() const; + void SetAgeInstanceGuid( const char * guid ); + Int32 GetAgeSequenceNumber() const; + void SetAgeSequenceNumber( Int32 v ); + const char * GetDisplayName() const; +}; + +#endif // pyAgeInfoStruct_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStructGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStructGlue.cpp new file mode 100644 index 00000000..52697624 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeInfoStructGlue.cpp @@ -0,0 +1,436 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyAgeInfoStruct.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptAgeInfoStruct, pyAgeInfoStruct); + +PYTHON_DEFAULT_NEW_DEFINITION(ptAgeInfoStruct, pyAgeInfoStruct) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptAgeInfoStruct) + +PYTHON_INIT_DEFINITION(ptAgeInfoStruct, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_RICH_COMPARE_DEFINITION(ptAgeInfoStruct, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pyAgeInfoStruct::Check(obj1) || !pyAgeInfoStruct::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptAgeInfoStruct object"); + PYTHON_RCOMPARE_ERROR; + } + } + pyAgeInfoStruct *struct1 = pyAgeInfoStruct::ConvertFrom(obj1); + pyAgeInfoStruct *struct2 = pyAgeInfoStruct::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*struct1) == (*struct2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*struct1) != (*struct2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptAgeInfoStruct object"); + PYTHON_RCOMPARE_ERROR; +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStruct, copyFrom, args) +{ + PyObject* infoStructObj = NULL; + if (!PyArg_ParseTuple(args, "O", &infoStructObj)) + { + PyErr_SetString(PyExc_TypeError, "copyFrom expects a ptAgeInfoStruct or ptAgeInfoStructRef"); + PYTHON_RETURN_ERROR; + } + if (pyAgeInfoStruct::Check(infoStructObj)) + { + pyAgeInfoStruct* infoStruct = pyAgeInfoStruct::ConvertFrom(infoStructObj); + self->fThis->CopyFrom(*infoStruct); + PYTHON_RETURN_NONE; + } + else if (pyAgeInfoStructRef::Check(infoStructObj)) + { + pyAgeInfoStructRef* infoStruct = pyAgeInfoStructRef::ConvertFrom(infoStructObj); + self->fThis->CopyFromRef(*infoStruct); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "copyFrom expects a ptAgeInfoStruct or ptAgeInfoStructRef"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStruct, getAgeFilename) +{ + return PyString_FromString(self->fThis->GetAgeFilename()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStruct, setAgeFilename, args) +{ + char* filename; + if (!PyArg_ParseTuple(args, "s", &filename)) + { + PyErr_SetString(PyExc_TypeError, "setAgeFilename expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeFilename(filename); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStruct, getAgeInstanceName) +{ + return PyString_FromString(self->fThis->GetAgeInstanceName()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStruct, setAgeInstanceName, args) +{ + char* instanceName; + if (!PyArg_ParseTuple(args, "s", &instanceName)) + { + PyErr_SetString(PyExc_TypeError, "setAgeInstanceName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeInstanceName(instanceName); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStruct, getAgeUserDefinedName) +{ + return PyString_FromString(self->fThis->GetAgeUserDefinedName()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStruct, setAgeUserDefinedName, args) +{ + char* userName; + if (!PyArg_ParseTuple(args, "s", &userName)) + { + PyErr_SetString(PyExc_TypeError, "setAgeUserDefinedName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeUserDefinedName(userName); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStruct, getAgeDescription) +{ + return PyString_FromString(self->fThis->GetAgeDescription()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStruct, setAgeDescription, args) +{ + char* desc; + if (!PyArg_ParseTuple(args, "s", &desc)) + { + PyErr_SetString(PyExc_TypeError, "setAgeDescription expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeDescription(desc); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStruct, getAgeInstanceGuid) +{ + return PyString_FromString(self->fThis->GetAgeInstanceGuid()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStruct, setAgeInstanceGuid, args) +{ + char* guid; + if (!PyArg_ParseTuple(args, "s", &guid)) + { + PyErr_SetString(PyExc_TypeError, "setAgeInstanceGuid expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeInstanceGuid(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStruct, getAgeSequenceNumber) +{ + return PyInt_FromLong(self->fThis->GetAgeSequenceNumber()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStruct, setAgeSequenceNumber, args) +{ + long sequenceNum; + if (!PyArg_ParseTuple(args, "l", &sequenceNum)) + { + PyErr_SetString(PyExc_TypeError, "setAgeSequenceNumber expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeSequenceNumber(sequenceNum); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStruct, getAgeLanguage) +{ + return PyInt_FromLong(self->fThis->GetAgeLanguage()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStruct, setAgeLanguage, args) +{ + long lang; + if (!PyArg_ParseTuple(args, "l", &lang)) + { + PyErr_SetString(PyExc_TypeError, "setAgeLanguage expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeLanguage(lang); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStruct, getDisplayName) +{ + return PyString_FromString(self->fThis->GetDisplayName()); +} + +PYTHON_START_METHODS_TABLE(ptAgeInfoStruct) + PYTHON_METHOD(ptAgeInfoStruct, copyFrom, "Params: other\nCopies data from one ptAgeInfoStruct or ptAgeInfoStructRef to this one"), + PYTHON_METHOD_NOARGS(ptAgeInfoStruct, getAgeFilename, "Gets the Age's filename"), + PYTHON_METHOD(ptAgeInfoStruct, setAgeFilename, "Params: filename\nSets the filename of the Age"), + PYTHON_METHOD_NOARGS(ptAgeInfoStruct, getAgeInstanceName, "Get the instance name of the Age"), + PYTHON_METHOD(ptAgeInfoStruct, setAgeInstanceName, "Params: instanceName\nSets the instance name of the Age"), + PYTHON_METHOD_NOARGS(ptAgeInfoStruct, getAgeUserDefinedName, "Gets the user defined part of the Age name"), + PYTHON_METHOD(ptAgeInfoStruct, setAgeUserDefinedName, "Params: udName\nSets the user defined part of the Age"), + PYTHON_METHOD_NOARGS(ptAgeInfoStruct, getAgeDescription, "Gets the description part of the Age name"), + PYTHON_METHOD(ptAgeInfoStruct, setAgeDescription, "Params: udName\nSets the description part of the Age"), + PYTHON_METHOD_NOARGS(ptAgeInfoStruct, getAgeInstanceGuid, "Get the Age's instance GUID"), + PYTHON_METHOD(ptAgeInfoStruct, setAgeInstanceGuid, "Params: guid\nSets the Age instance's GUID"), + PYTHON_METHOD_NOARGS(ptAgeInfoStruct, getAgeSequenceNumber, "Gets the unique sequence number"), + PYTHON_METHOD(ptAgeInfoStruct, setAgeSequenceNumber, "Params: seqNumber\nSets the unique sequence number"), + PYTHON_METHOD_NOARGS(ptAgeInfoStruct, getAgeLanguage, "Gets the age's language (integer)"), + PYTHON_METHOD(ptAgeInfoStruct, setAgeLanguage, "Params: lang\nSets the age's language (integer)"), + PYTHON_METHOD_NOARGS(ptAgeInfoStruct, getDisplayName, "Returns a string that is the displayable name of the age instance"), +PYTHON_END_METHODS_TABLE; + +// type structure definition +#define ptAgeInfoStruct_COMPARE PYTHON_NO_COMPARE +#define ptAgeInfoStruct_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptAgeInfoStruct_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptAgeInfoStruct_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptAgeInfoStruct_STR PYTHON_NO_STR +#define ptAgeInfoStruct_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptAgeInfoStruct) +#define ptAgeInfoStruct_GETSET PYTHON_NO_GETSET +#define ptAgeInfoStruct_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptAgeInfoStruct, "Class to hold AgeInfo struct data"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptAgeInfoStruct, pyAgeInfoStruct) + +PyObject *pyAgeInfoStruct::New(plAgeInfoStruct *info) +{ + ptAgeInfoStruct *newObj = (ptAgeInfoStruct*)ptAgeInfoStruct_type.tp_new(&ptAgeInfoStruct_type, NULL, NULL); + newObj->fThis->fAgeInfo.CopyFrom(info); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptAgeInfoStruct, pyAgeInfoStruct) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptAgeInfoStruct, pyAgeInfoStruct) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyAgeInfoStruct::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptAgeInfoStruct); + PYTHON_CLASS_IMPORT_END(m); +} + +// glue functions +PYTHON_CLASS_DEFINITION(ptAgeInfoStructRef, pyAgeInfoStructRef); + +PYTHON_DEFAULT_NEW_DEFINITION(ptAgeInfoStructRef, pyAgeInfoStructRef) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptAgeInfoStructRef) + +PYTHON_NO_INIT_DEFINITION(ptAgeInfoStructRef) + +PYTHON_METHOD_DEFINITION(ptAgeInfoStructRef, copyFrom, args) +{ + PyObject* infoStructObj = NULL; + if (!PyArg_ParseTuple(args, "O", &infoStructObj)) + { + PyErr_SetString(PyExc_TypeError, "copyFrom expects a ptAgeInfoStruct or ptAgeInfoStructRef"); + PYTHON_RETURN_ERROR; + } + if (pyAgeInfoStruct::Check(infoStructObj)) + { + pyAgeInfoStruct* infoStruct = pyAgeInfoStruct::ConvertFrom(infoStructObj); + self->fThis->CopyFrom(*infoStruct); + PYTHON_RETURN_NONE; + } + else if (pyAgeInfoStructRef::Check(infoStructObj)) + { + pyAgeInfoStructRef* infoStruct = pyAgeInfoStructRef::ConvertFrom(infoStructObj); + self->fThis->CopyFromRef(*infoStruct); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "copyFrom expects a ptAgeInfoStruct or ptAgeInfoStructRef"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStructRef, getAgeFilename) +{ + return PyString_FromString(self->fThis->GetAgeFilename()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStructRef, setAgeFilename, args) +{ + char* filename; + if (!PyArg_ParseTuple(args, "s", &filename)) + { + PyErr_SetString(PyExc_TypeError, "setAgeFilename expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeFilename(filename); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStructRef, getAgeInstanceName) +{ + return PyString_FromString(self->fThis->GetAgeInstanceName()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStructRef, setAgeInstanceName, args) +{ + char* instanceName; + if (!PyArg_ParseTuple(args, "s", &instanceName)) + { + PyErr_SetString(PyExc_TypeError, "setAgeInstanceName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeInstanceName(instanceName); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStructRef, getAgeUserDefinedName) +{ + return PyString_FromString(self->fThis->GetAgeUserDefinedName()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStructRef, setAgeUserDefinedName, args) +{ + char* userName; + if (!PyArg_ParseTuple(args, "s", &userName)) + { + PyErr_SetString(PyExc_TypeError, "setAgeUserDefinedName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeUserDefinedName(userName); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStructRef, getAgeInstanceGuid) +{ + return PyString_FromString(self->fThis->GetAgeInstanceGuid()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStructRef, setAgeInstanceGuid, args) +{ + char* guid; + if (!PyArg_ParseTuple(args, "s", &guid)) + { + PyErr_SetString(PyExc_TypeError, "setAgeInstanceGuid expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeInstanceGuid(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStructRef, getAgeSequenceNumber) +{ + return PyInt_FromLong(self->fThis->GetAgeSequenceNumber()); +} + +PYTHON_METHOD_DEFINITION(ptAgeInfoStructRef, setAgeSequenceNumber, args) +{ + long sequenceNum; + if (!PyArg_ParseTuple(args, "l", &sequenceNum)) + { + PyErr_SetString(PyExc_TypeError, "setAgeSequenceNumber expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeSequenceNumber(sequenceNum); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeInfoStructRef, getDisplayName) +{ + return PyString_FromString(self->fThis->GetDisplayName()); +} + +PYTHON_START_METHODS_TABLE(ptAgeInfoStructRef) + PYTHON_METHOD(ptAgeInfoStructRef, copyFrom, "Params: other\nCopies data from one ptAgeInfoStruct or ptAgeInfoStructRef to this one"), + PYTHON_METHOD_NOARGS(ptAgeInfoStructRef, getAgeFilename, "Gets the Age's filename"), + PYTHON_METHOD(ptAgeInfoStructRef, setAgeFilename, "Params: filename\nSets the filename of the Age"), + PYTHON_METHOD_NOARGS(ptAgeInfoStructRef, getAgeInstanceName, "Get the instance name of the Age"), + PYTHON_METHOD(ptAgeInfoStructRef, setAgeInstanceName, "Params: instanceName\nSets the instance name of the Age"), + PYTHON_METHOD_NOARGS(ptAgeInfoStructRef, getAgeUserDefinedName, "Gets the user defined part of the Age name"), + PYTHON_METHOD(ptAgeInfoStructRef, setAgeUserDefinedName, "Params: udName\nSets the user defined part of the Age"), + PYTHON_METHOD_NOARGS(ptAgeInfoStructRef, getAgeInstanceGuid, "Get the Age's instance GUID"), + PYTHON_METHOD(ptAgeInfoStructRef, setAgeInstanceGuid, "Params: guid\nSets the Age instance's GUID"), + PYTHON_METHOD_NOARGS(ptAgeInfoStructRef, getAgeSequenceNumber, "Gets the unique sequence number"), + PYTHON_METHOD(ptAgeInfoStructRef, setAgeSequenceNumber, "Params: seqNumber\nSets the unique sequence number"), + PYTHON_METHOD_NOARGS(ptAgeInfoStructRef, getDisplayName, "Returns a string that is the displayable name of the age instance"), +PYTHON_END_METHODS_TABLE; + +// type structure definition +PLASMA_DEFAULT_TYPE(ptAgeInfoStructRef, "Class to hold AgeInfo struct data"); + +// required functions for PyObject interoperability +PyObject *pyAgeInfoStructRef::New(plAgeInfoStruct &info) +{ + ptAgeInfoStructRef *newObj = (ptAgeInfoStructRef*)ptAgeInfoStructRef_type.tp_new(&ptAgeInfoStructRef_type, NULL, NULL); + newObj->fThis->fAgeInfo = info; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptAgeInfoStructRef, pyAgeInfoStructRef) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptAgeInfoStructRef, pyAgeInfoStructRef) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyAgeInfoStructRef::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptAgeInfoStructRef); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStruct.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStruct.cpp new file mode 100644 index 00000000..791abd5c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStruct.cpp @@ -0,0 +1,159 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStlUtils.h" + +#include "pyAgeLinkStruct.h" +#include "pySpawnPointInfo.h" + +/////////////////////////////////////////////////////////////////////////// + + +pyAgeLinkStruct::pyAgeLinkStruct() +{ +} + +pyAgeLinkStruct::pyAgeLinkStruct( plAgeLinkStruct * link ) +{ + fAgeLink.CopyFrom( link ); +} + +pyAgeLinkStruct::~pyAgeLinkStruct() +{ +} + +bool pyAgeLinkStruct::operator==(const pyAgeLinkStruct &other) const +{ + return fAgeLink.IsEqualTo( other.GetAgeLink() ); +} + +///////////////////////////////////////////////////////////////////// + +PyObject * pyAgeLinkStruct::GetAgeInfo() +{ + return pyAgeInfoStructRef::New( *fAgeLink.GetAgeInfo() ); +} + +void pyAgeLinkStruct::SetAgeInfo( pyAgeInfoStruct & info ) +{ + fAgeLink.GetAgeInfo()->CopyFrom( info.GetAgeInfo() ); +} + +const char* pyAgeLinkStruct::GetParentAgeFilename() +{ + return fAgeLink.GetParentAgeFilename(); +} + +void pyAgeLinkStruct::SetParentAgeFilename( const char* parentname ) +{ + fAgeLink.SetParentAgeFilename(parentname); +} + +void pyAgeLinkStruct::CopyFrom( const pyAgeLinkStruct & other ) +{ + fAgeLink.CopyFrom( other.GetAgeLink() ); +} + +void pyAgeLinkStruct::CopyFromRef( const pyAgeLinkStructRef & other ) +{ + fAgeLink.CopyFrom( other.GetAgeLink() ); +} + +void pyAgeLinkStruct::SetLinkingRules( int v ) +{ + fAgeLink.SetLinkingRules( v ); +} + +int pyAgeLinkStruct::GetLinkingRules() const +{ + return fAgeLink.GetLinkingRules(); +} + +void pyAgeLinkStruct::SetSpawnPoint( pySpawnPointInfo & v ) +{ + fAgeLink.SpawnPoint() = v.SpawnPoint(); +} + +void pyAgeLinkStruct::SetSpawnPointRef( pySpawnPointInfoRef & v ) +{ + fAgeLink.SpawnPoint() = v.SpawnPoint(); +} + +PyObject * pyAgeLinkStruct::GetSpawnPoint() +{ + return pySpawnPointInfoRef::New( fAgeLink.SpawnPoint() ); +} + + +//////////////////////////////////////////////////////////////////////// + +plAgeLinkStruct pyAgeLinkStructRef::fDefaultLinkStruct; // created so a default constructor could be made for python. Do NOT use + +PyObject * pyAgeLinkStructRef::GetAgeInfo() +{ + return pyAgeInfoStructRef::New( *fAgeLink.GetAgeInfo() ); +} + +void pyAgeLinkStructRef::SetAgeInfo( pyAgeInfoStruct & info ) +{ + fAgeLink.GetAgeInfo()->CopyFrom( info.GetAgeInfo() ); +} + +void pyAgeLinkStructRef::CopyFrom( const pyAgeLinkStruct & other ) +{ + fAgeLink.CopyFrom( other.GetAgeLink() ); +} + +void pyAgeLinkStructRef::CopyFromRef( const pyAgeLinkStructRef & other ) +{ + fAgeLink.CopyFrom( other.GetAgeLink() ); +} + +void pyAgeLinkStructRef::SetLinkingRules( int v ) +{ + fAgeLink.SetLinkingRules( v ); +} + +int pyAgeLinkStructRef::GetLinkingRules() const +{ + return fAgeLink.GetLinkingRules(); +} + +void pyAgeLinkStructRef::SetSpawnPoint( pySpawnPointInfo & v ) +{ + fAgeLink.SpawnPoint() = v.SpawnPoint(); +} + +void pyAgeLinkStructRef::SetSpawnPointRef( pySpawnPointInfoRef & v ) +{ + fAgeLink.SpawnPoint() = v.SpawnPoint(); +} + +PyObject * pyAgeLinkStructRef::GetSpawnPoint() +{ + return pySpawnPointInfoRef::New( fAgeLink.SpawnPoint() ); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStruct.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStruct.h new file mode 100644 index 00000000..7c801526 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStruct.h @@ -0,0 +1,122 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyAgeLinkStruct_h_inc +#define pyAgeLinkStruct_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "../plNetCommon/plNetServerSessionInfo.h" +#include "pyAgeInfoStruct.h" + +#include +#include "pyGlueHelpers.h" + +////////////////////////////////////////////////////////////////////// +// +// pyAgeLinkStruct - a wrapper class to provide interface to the plAgeLinkStruct +// +////////////////////////////////////////////////////////////////////// + +class pyVaultAgeLinkNode; +class pySpawnPointInfo; +class pySpawnPointInfoRef; +class pyAgeLinkStructRef; + +class pyAgeLinkStruct +{ +private: + plAgeLinkStruct fAgeLink; + +protected: + pyAgeLinkStruct(); + pyAgeLinkStruct( plAgeLinkStruct * link ); + +public: + ~pyAgeLinkStruct(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptAgeLinkStruct); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(plAgeLinkStruct* link); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyAgeLinkStruct object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyAgeLinkStruct); // converts a PyObject to a pyAgeLinkStruct (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + bool operator==(const pyAgeLinkStruct &other) const; + bool operator!=(const pyAgeLinkStruct &other) const { return !(other==*this); } + plAgeLinkStruct * GetAgeLink() { return &fAgeLink; } + const plAgeLinkStruct * GetAgeLink() const { return &fAgeLink; } + PyObject * GetAgeInfo(); // returns pyAgeInfoStructRef + void SetAgeInfo( pyAgeInfoStruct & info ); + const char* GetParentAgeFilename(); + void SetParentAgeFilename( const char* parentname ); + void CopyFrom( const pyAgeLinkStruct & other ); + void CopyFromRef( const pyAgeLinkStructRef & other ); + void SetLinkingRules( int v ); + int GetLinkingRules() const; + void SetSpawnPoint( pySpawnPointInfo & v ); + void SetSpawnPointRef( pySpawnPointInfoRef & v ); + PyObject * GetSpawnPoint(); // returns pySpawnPointInfoRef +}; + + +class pyAgeLinkStructRef +{ +private: + static plAgeLinkStruct fDefaultLinkStruct; // created so a default constructor could be made for python, do NOT use + plAgeLinkStruct & fAgeLink; + +protected: + pyAgeLinkStructRef(): fAgeLink(fDefaultLinkStruct) {} // only used by python glue, do NOT call directly + pyAgeLinkStructRef( plAgeLinkStruct & link ):fAgeLink(link) {} + +public: + ~pyAgeLinkStructRef(){} + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptAgeLinkStructRef); + static PyObject *New(plAgeLinkStruct& link); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyAgeLinkStructRef object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyAgeLinkStructRef); // converts a PyObject to a pyAgeLinkStructRef (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + plAgeLinkStruct * GetAgeLink() { return &fAgeLink; } + const plAgeLinkStruct * GetAgeLink() const { return &fAgeLink; } + PyObject * GetAgeInfo(); // returns pyAgeInfoStructRef + void SetAgeInfo( pyAgeInfoStruct & info ); + void CopyFrom( const pyAgeLinkStruct & other ); + void CopyFromRef( const pyAgeLinkStructRef & other ); + void SetLinkingRules( int v ); + int GetLinkingRules() const; + void SetSpawnPoint( pySpawnPointInfo & v ); + void SetSpawnPointRef( pySpawnPointInfoRef & v ); + PyObject * GetSpawnPoint(); // returns pySpawnPointInfoRef +}; + + +#endif // pyAgeLinkStruct_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStructGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStructGlue.cpp new file mode 100644 index 00000000..5606dc64 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeLinkStructGlue.cpp @@ -0,0 +1,366 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyAgeLinkStruct.h" +#include "pySpawnPointInfo.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptAgeLinkStruct, pyAgeLinkStruct); + +PYTHON_DEFAULT_NEW_DEFINITION(ptAgeLinkStruct, pyAgeLinkStruct) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptAgeLinkStruct) + +PYTHON_INIT_DEFINITION(ptAgeLinkStruct, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_RICH_COMPARE_DEFINITION(ptAgeLinkStruct, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pyAgeLinkStruct::Check(obj1) || !pyAgeLinkStruct::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptAgeLinkStruct object"); + PYTHON_RCOMPARE_ERROR; + } + } + pyAgeLinkStruct *struct1 = pyAgeLinkStruct::ConvertFrom(obj1); + pyAgeLinkStruct *struct2 = pyAgeLinkStruct::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*struct1) == (*struct2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*struct1) != (*struct2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptAgeLinkStruct object"); + PYTHON_RCOMPARE_ERROR; +} + +PYTHON_METHOD_DEFINITION(ptAgeLinkStruct, copyFrom, args) +{ + PyObject* linkStructObj = NULL; + if (!PyArg_ParseTuple(args, "O", &linkStructObj)) + { + PyErr_SetString(PyExc_TypeError, "copyFrom expects a ptAgeLinkStruct or ptAgeLinkStructRef"); + PYTHON_RETURN_ERROR; + } + if (pyAgeLinkStruct::Check(linkStructObj)) + { + pyAgeLinkStruct* linkStruct = pyAgeLinkStruct::ConvertFrom(linkStructObj); + self->fThis->CopyFrom(*linkStruct); + PYTHON_RETURN_NONE; + } + else if (pyAgeLinkStructRef::Check(linkStructObj)) + { + pyAgeLinkStructRef* linkStruct = pyAgeLinkStructRef::ConvertFrom(linkStructObj); + self->fThis->CopyFromRef(*linkStruct); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "copyFrom expects a ptAgeLinkStruct or ptAgeLinkStructRef"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeLinkStruct, getAgeInfo) +{ + return self->fThis->GetAgeInfo(); +} + +PYTHON_METHOD_DEFINITION(ptAgeLinkStruct, setAgeInfo, args) +{ + PyObject* ageInfoObj = NULL; + if (!PyArg_ParseTuple(args, "O", &ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "setAgeInfo expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeInfoStruct::Check(ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "setAgeInfo expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + pyAgeInfoStruct* ageInfo = pyAgeInfoStruct::ConvertFrom(ageInfoObj); + self->fThis->SetAgeInfo(*ageInfo); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeLinkStruct, getParentAgeFilename) +{ + return PyString_FromString(self->fThis->GetParentAgeFilename()); +} + +PYTHON_METHOD_DEFINITION(ptAgeLinkStruct, setParentAgeFilename, args) +{ + char* filename; + if (!PyArg_ParseTuple(args, "s", &filename)) + { + PyErr_SetString(PyExc_TypeError, "setParentAgeFilename expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetParentAgeFilename(filename); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeLinkStruct, getLinkingRules) +{ + return PyInt_FromLong(self->fThis->GetLinkingRules()); +} + +PYTHON_METHOD_DEFINITION(ptAgeLinkStruct, setLinkingRules, args) +{ + int rules; + if (!PyArg_ParseTuple(args, "i", &rules)) + { + PyErr_SetString(PyExc_TypeError, "setLinkingRules expects an int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetLinkingRules(rules); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeLinkStruct, getSpawnPoint) +{ + return self->fThis->GetSpawnPoint(); +} + +PYTHON_METHOD_DEFINITION(ptAgeLinkStruct, setSpawnPoint, args) +{ + PyObject* spawnPtInfoObj = NULL; + if (!PyArg_ParseTuple(args, "O", &spawnPtInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "setSpawnPoint expects a ptSpawnPointInfo or a ptSpawnPointInfoRef"); + PYTHON_RETURN_ERROR; + } + if (pySpawnPointInfo::Check(spawnPtInfoObj)) + { + pySpawnPointInfo* spawnPt = pySpawnPointInfo::ConvertFrom(spawnPtInfoObj); + self->fThis->SetSpawnPoint(*spawnPt); + PYTHON_RETURN_NONE; + } + else if (pySpawnPointInfoRef::Check(spawnPtInfoObj)) + { + pySpawnPointInfoRef* spawnPt = pySpawnPointInfoRef::ConvertFrom(spawnPtInfoObj); + self->fThis->SetSpawnPointRef(*spawnPt); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "setSpawnPoint expects a ptSpawnPointInfo or a ptSpawnPointInfoRef"); + PYTHON_RETURN_ERROR; +} + +PYTHON_START_METHODS_TABLE(ptAgeLinkStruct) + PYTHON_METHOD(ptAgeLinkStruct, copyFrom, "Params: other\nCopies data from one ptAgeLinkStruct or ptAgeLinkStructRef to this one"), + PYTHON_METHOD_NOARGS(ptAgeLinkStruct, getAgeInfo, "Returns a ptAgeInfoStructRef of the AgeInfo for this link"), + PYTHON_METHOD(ptAgeLinkStruct, setAgeInfo, "Params: ageInfo\nSets the AgeInfoStruct from the data in ageInfo (a ptAgeInfoStruct)"), + PYTHON_METHOD_NOARGS(ptAgeLinkStruct, getParentAgeFilename, "Returns a string of the parent age filename"), + PYTHON_METHOD(ptAgeLinkStruct, setParentAgeFilename, "Params: filename\nSets the parent age filename for child age links"), + PYTHON_METHOD_NOARGS(ptAgeLinkStruct, getLinkingRules, "Returns the linking rules of this link"), + PYTHON_METHOD(ptAgeLinkStruct, setLinkingRules, "Params: rule\nSets the linking rules for this link"), + PYTHON_METHOD_NOARGS(ptAgeLinkStruct, getSpawnPoint, "Gets the spawn point ptSpawnPointInfoRef of this link"), + PYTHON_METHOD(ptAgeLinkStruct, setSpawnPoint, "Params: spawnPtInfo\nSets the spawn point of this link (a ptSpawnPointInfo or ptSpawnPointInfoRef)"), +PYTHON_END_METHODS_TABLE; + +// type structure definition +#define ptAgeLinkStruct_COMPARE PYTHON_NO_COMPARE +#define ptAgeLinkStruct_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptAgeLinkStruct_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptAgeLinkStruct_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptAgeLinkStruct_STR PYTHON_NO_STR +#define ptAgeLinkStruct_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptAgeLinkStruct) +#define ptAgeLinkStruct_GETSET PYTHON_NO_GETSET +#define ptAgeLinkStruct_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptAgeLinkStruct, "Class to hold the data of the AgeLink structure"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptAgeLinkStruct, pyAgeLinkStruct) + +PyObject *pyAgeLinkStruct::New(plAgeLinkStruct *link) +{ + ptAgeLinkStruct *newObj = (ptAgeLinkStruct*)ptAgeLinkStruct_type.tp_new(&ptAgeLinkStruct_type, NULL, NULL); + newObj->fThis->fAgeLink.CopyFrom(link); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptAgeLinkStruct, pyAgeLinkStruct) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptAgeLinkStruct, pyAgeLinkStruct) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyAgeLinkStruct::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptAgeLinkStruct); + PYTHON_CLASS_IMPORT_END(m); +} + +// glue functions +PYTHON_CLASS_DEFINITION(ptAgeLinkStructRef, pyAgeLinkStructRef); + +PYTHON_DEFAULT_NEW_DEFINITION(ptAgeLinkStructRef, pyAgeLinkStructRef) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptAgeLinkStructRef) + +PYTHON_NO_INIT_DEFINITION(ptAgeLinkStructRef) + +PYTHON_METHOD_DEFINITION(ptAgeLinkStructRef, copyFrom, args) +{ + PyObject* linkStructObj = NULL; + if (!PyArg_ParseTuple(args, "O", &linkStructObj)) + { + PyErr_SetString(PyExc_TypeError, "copyFrom expects a ptAgeLinkStruct or ptAgeLinkStructRef"); + PYTHON_RETURN_ERROR; + } + if (pyAgeLinkStruct::Check(linkStructObj)) + { + pyAgeLinkStruct* linkStruct = pyAgeLinkStruct::ConvertFrom(linkStructObj); + self->fThis->CopyFrom(*linkStruct); + PYTHON_RETURN_NONE; + } + else if (pyAgeLinkStructRef::Check(linkStructObj)) + { + pyAgeLinkStructRef* linkStruct = pyAgeLinkStructRef::ConvertFrom(linkStructObj); + self->fThis->CopyFromRef(*linkStruct); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "copyFrom expects a ptAgeLinkStruct or ptAgeLinkStructRef"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeLinkStructRef, getAgeInfo) +{ + return self->fThis->GetAgeInfo(); +} + +PYTHON_METHOD_DEFINITION(ptAgeLinkStructRef, setAgeInfo, args) +{ + PyObject* ageInfoObj = NULL; + if (!PyArg_ParseTuple(args, "O", &ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "setAgeInfo expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeInfoStruct::Check(ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "setAgeInfo expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + pyAgeInfoStruct* ageInfo = pyAgeInfoStruct::ConvertFrom(ageInfoObj); + self->fThis->SetAgeInfo(*ageInfo); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeLinkStructRef, getLinkingRules) +{ + return PyInt_FromLong(self->fThis->GetLinkingRules()); +} + +PYTHON_METHOD_DEFINITION(ptAgeLinkStructRef, setLinkingRules, args) +{ + int rules; + if (!PyArg_ParseTuple(args, "i", &rules)) + { + PyErr_SetString(PyExc_TypeError, "setLinkingRules expects an int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetLinkingRules(rules); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeLinkStructRef, getSpawnPoint) +{ + return self->fThis->GetSpawnPoint(); +} + +PYTHON_METHOD_DEFINITION(ptAgeLinkStructRef, setSpawnPoint, args) +{ + PyObject* spawnPtInfoObj = NULL; + if (!PyArg_ParseTuple(args, "O", &spawnPtInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "setSpawnPoint expects a ptSpawnPointInfo or a ptSpawnPointInfoRef"); + PYTHON_RETURN_ERROR; + } + if (pySpawnPointInfo::Check(spawnPtInfoObj)) + { + pySpawnPointInfo* spawnPt = pySpawnPointInfo::ConvertFrom(spawnPtInfoObj); + self->fThis->SetSpawnPoint(*spawnPt); + PYTHON_RETURN_NONE; + } + else if (pySpawnPointInfoRef::Check(spawnPtInfoObj)) + { + pySpawnPointInfoRef* spawnPt = pySpawnPointInfoRef::ConvertFrom(spawnPtInfoObj); + self->fThis->SetSpawnPointRef(*spawnPt); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "setSpawnPoint expects a ptSpawnPointInfo or a ptSpawnPointInfoRef"); + PYTHON_RETURN_ERROR; +} + +PYTHON_START_METHODS_TABLE(ptAgeLinkStructRef) + PYTHON_METHOD(ptAgeLinkStructRef, copyFrom, "Params: other\nCopies data from one ptAgeLinkStruct or ptAgeLinkStructRef to this one"), + PYTHON_METHOD_NOARGS(ptAgeLinkStructRef, getAgeInfo, "Returns a ptAgeInfoStructRef of the AgeInfo for this link"), + PYTHON_METHOD(ptAgeLinkStructRef, setAgeInfo, "Params: ageInfo\nSets the AgeInfoStruct from the data in ageInfo (a ptAgeInfoStruct)"), + PYTHON_METHOD_NOARGS(ptAgeLinkStructRef, getLinkingRules, "Returns the linking rules of this link"), + PYTHON_METHOD(ptAgeLinkStructRef, setLinkingRules, "Params: rule\nSets the linking rules for this link"), + PYTHON_METHOD_NOARGS(ptAgeLinkStructRef, getSpawnPoint, "Gets the spawn point ptSpawnPointInfoRef of this link"), + PYTHON_METHOD(ptAgeLinkStructRef, setSpawnPoint, "Params: spawnPtInfo\nSets the spawn point of this link (a ptSpawnPointInfo or ptSpawnPointInfoRef)"), +PYTHON_END_METHODS_TABLE; + +// type structure definition +PLASMA_DEFAULT_TYPE(ptAgeLinkStructRef, "Class to hold the data of the AgeLink structure"); + +// required functions for PyObject interoperability +PyObject *pyAgeLinkStructRef::New(plAgeLinkStruct &link) +{ + ptAgeLinkStructRef *newObj = (ptAgeLinkStructRef*)ptAgeLinkStructRef_type.tp_new(&ptAgeLinkStructRef_type, NULL, NULL); + newObj->fThis->fAgeLink = link; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptAgeLinkStructRef, pyAgeLinkStructRef) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptAgeLinkStructRef, pyAgeLinkStructRef) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyAgeLinkStructRef::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptAgeLinkStructRef); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVault.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVault.cpp new file mode 100644 index 00000000..2e0b3466 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVault.cpp @@ -0,0 +1,329 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyAgeVault - a wrapper class to provide interface to the plVaultAgeNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyAgeVault.h" +#include "pyVault.h" +#include "pyVaultNodeRef.h" +#include "pyVaultFolderNode.h" +#include "pyVaultPlayerInfoListNode.h" +#include "pyVaultPlayerInfoNode.h" +#include "pyVaultAgeInfoNode.h" +#include "pyVaultAgeLinkNode.h" +#include "pyVaultChronicleNode.h" +#include "pyVaultTextNoteNode.h" +#include "pyNetLinkingMgr.h" +#include "pyAgeInfoStruct.h" +#include "pySDL.h" + + +#include "../plVault/plVault.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetClient/plNetLinkingMgr.h" +#include "../plNetTransport/plNetTransport.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../plSDL/plSDL.h" +#include "../pnNetCommon/plNetApp.h" + +pyAgeVault::pyAgeVault() { +} + +pyAgeVault::~pyAgeVault() { +} + +////////////////////////////////////////////////// + +PyObject* pyAgeVault::GetAgeInfo() +{ + RelVaultNode * rvn = VaultGetAgeInfoNodeIncRef(); + if (rvn) { + PyObject * result = pyVaultAgeInfoNode::New(rvn); + rvn->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyAgeVault::GetAgeDevicesFolder( void ) +{ + RelVaultNode * rvn = VaultGetAgeDevicesFolderIncRef(); + if (rvn) { + PyObject * result = pyVaultFolderNode::New(rvn); + rvn->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyAgeVault::GetSubAgesFolder( void ) +{ + RelVaultNode * rvn = VaultGetAgeSubAgesFolderIncRef(); + if (rvn) { + PyObject * result = pyVaultFolderNode::New(rvn); + rvn->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyAgeVault::GetChronicleFolder( void ) +{ + RelVaultNode * rvn = VaultGetAgeChronicleFolderIncRef(); + if (rvn) { + PyObject * result = pyVaultFolderNode::New(rvn); + rvn->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyAgeVault::GetBookshelfFolder ( void ) +{ + RelVaultNode * rvn = VaultAgeGetBookshelfFolderIncRef(); + if (rvn) { + PyObject * result = pyVaultFolderNode::New(rvn); + rvn->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyAgeVault::GetPeopleIKnowAboutFolder( void ) +{ + RelVaultNode * rvn = VaultGetAgePeopleIKnowAboutFolderIncRef(); + if (rvn) { + PyObject * result = pyVaultFolderNode::New(rvn); + rvn->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + + +PyObject* pyAgeVault::GetPublicAgesFolder(void) +{ + RelVaultNode * rvn = VaultGetAgePublicAgesFolderIncRef(); + if (rvn) { + PyObject * result = pyVaultFolderNode::New(rvn); + rvn->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyAgeVault::GetSubAgeLink( const pyAgeInfoStruct & info ) +{ + RelVaultNode * rvn = VaultFindAgeSubAgeLinkIncRef(info.GetAgeInfo()); + if (rvn) { + PyObject * result = pyVaultAgeLinkNode::New(rvn); + rvn->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + +const char* pyAgeVault::GetAgeGuid( void ) +{ + RelVaultNode * rvn = VaultGetAgeInfoNodeIncRef(); + if (rvn) { + VaultAgeInfoNode ageInfo(rvn); + GuidToString(ageInfo.ageInstUuid, fAgeGuid, arrsize(fAgeGuid)); + rvn->DecRef(); + } + else { + fAgeGuid[0] = 0; + } + return fAgeGuid; +} + + +/////////////// +// Chronicle +PyObject* pyAgeVault::FindChronicleEntry( const char * entryName ) +{ + wchar wEntryName[kMaxVaultNodeStringLength]; + StrToUnicode(wEntryName, entryName, arrsize(wEntryName)); + + if (RelVaultNode * rvn = VaultFindAgeChronicleEntryIncRef(wEntryName)) { + PyObject * result = pyVaultChronicleNode::New(rvn); + rvn->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + +void pyAgeVault::AddChronicleEntry( const char * name, UInt32 type, const char * value ) +{ + wchar * wEntryName = StrDupToUnicode(name); + wchar * wEntryValue = StrDupToUnicode(value); + + VaultAddAgeChronicleEntry(wEntryName, type, wEntryValue); + + FREE(wEntryName); + FREE(wEntryValue); +} + +// AGE DEVICES. AKA IMAGERS, WHATEVER. +// Add a new device. +void pyAgeVault::AddDevice( const char * deviceName, PyObject * cbObject, UInt32 cbContext ) +{ + pyVaultNode::pyVaultNodeOperationCallback * cb = NEWZERO(pyVaultNode::pyVaultNodeOperationCallback)( cbObject ); + cb->VaultOperationStarted( cbContext ); + + wchar wStr[MAX_PATH]; + StrToUnicode(wStr, deviceName, arrsize(wStr)); + + if (RelVaultNode * rvn = VaultAgeAddDeviceAndWaitIncRef(wStr)) { + cb->SetNode(rvn); + rvn->DecRef(); + } + + cb->VaultOperationComplete( cbContext, cb->GetNode() ? hsOK : hsFail); // cbHolder deletes itself here. +} + +// Remove a device. +void pyAgeVault::RemoveDevice( const char * deviceName ) +{ + wchar wStr[MAX_PATH]; + StrToUnicode(wStr, deviceName, arrsize(wStr)); + + VaultAgeRemoveDevice(wStr); +} + +// True if device exists in age. +bool pyAgeVault::HasDevice( const char * deviceName ) +{ + wchar wStr[MAX_PATH]; + StrToUnicode(wStr, deviceName, arrsize(wStr)); + + return VaultAgeHasDevice(wStr); +} + +PyObject * pyAgeVault::GetDevice( const char * deviceName ) +{ + wchar wStr[MAX_PATH]; + StrToUnicode(wStr, deviceName, arrsize(wStr)); + + if (RelVaultNode * rvn = VaultAgeGetDeviceIncRef(wStr)) { + PyObject * result = pyVaultTextNoteNode::New(rvn); + rvn->DecRef(); + return result; + } + + PYTHON_RETURN_NONE; +} + +// Sets the inbox associated with a device. +void pyAgeVault::SetDeviceInbox( const char * deviceName, const char * inboxName, PyObject * cbObject, UInt32 cbContext ) +{ + pyVaultNode::pyVaultNodeOperationCallback * cb = NEWZERO(pyVaultNode::pyVaultNodeOperationCallback)( cbObject ); + cb->VaultOperationStarted( cbContext ); + + wchar wDev[MAX_PATH]; + StrToUnicode(wDev, deviceName, arrsize(wDev)); + wchar wInb[MAX_PATH]; + StrToUnicode(wInb, inboxName, arrsize(wInb)); + + if (RelVaultNode * rvn = VaultAgeSetDeviceInboxAndWaitIncRef(wDev, wInb)) { + cb->SetNode(rvn); + rvn->DecRef(); + } + + cb->VaultOperationComplete( cbContext, cb->GetNode() ? hsOK : hsFail ); // cbHolder deletes itself here. +} + +PyObject * pyAgeVault::GetDeviceInbox( const char * deviceName ) +{ + wchar wStr[MAX_PATH]; + StrToUnicode(wStr, deviceName, arrsize(wStr)); + + if (RelVaultNode * rvn = VaultAgeGetDeviceInboxIncRef(wStr)) { + PyObject * result = pyVaultTextNoteNode::New(rvn); + rvn->DecRef(); + return result; + } + + PYTHON_RETURN_NONE; +} + +PyObject * pyAgeVault::GetAgeSDL() const +{ + plStateDataRecord * rec = NEWZERO(plStateDataRecord); + if (!VaultAgeGetAgeSDL(rec)) { + DEL(rec); + PYTHON_RETURN_NONE; + } + else { + return pySDLStateDataRecord::New( rec ); + } +} + +void pyAgeVault::UpdateAgeSDL( pySDLStateDataRecord & pyrec ) +{ + plStateDataRecord * rec = pyrec.GetRec(); + if ( !rec ) + return; + + VaultAgeUpdateAgeSDL(rec); +} + +PyObject* pyAgeVault::FindNode( pyVaultNode* templateNode ) const +{ + if (RelVaultNode * rvn = VaultGetAgeNodeIncRef()) { + RelVaultNode * find = rvn->GetChildNodeIncRef(templateNode->fNode, 1); + rvn->DecRef(); + if (find) { + PyObject * result = pyVaultNode::New(find); + find->DecRef(); + return result; + } + } + + PYTHON_RETURN_NONE; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVault.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVault.h new file mode 100644 index 00000000..83d531a6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVault.h @@ -0,0 +1,110 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyAgeVault_h_ +#define _pyAgeVault_h_ + +#ifdef BUILDING_PYPLASMA +# error "pyAgeVault is not compatible with pyPlasma.pyd. Use BUILDING_PYPLASMA macro to ifdef out unwanted headers." +#endif + +////////////////////////////////////////////////////////////////////// +// +// pyAgeVault - a wrapper class to provide interface to the plVaultAgeNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +class pyVaultNode; +class pyVaultFolderNode; +class pyVaultPlayerInfoListNode; +class pyVaultAgeLinkNode; +class pyVaultAgeInfoNode; +class pyAgeInfoStruct; +class pyVaultChronicleNode; +class pySDLStateDataRecord; +class pyVaultTextNoteNode; + +class pyAgeVault +{ +private: + mutable char fAgeGuid[MAX_PATH]; // for getting Age GUID + +protected: + pyAgeVault(); + +public: + ~pyAgeVault(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptAgeVault); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyAgeVault object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyAgeVault); // converts a PyObject to a pyAgeVault (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + const char* GetAgeGuid( void ); + + PyObject * GetAgeSDL() const; // returns pySDLStateDataRecord + void UpdateAgeSDL( pySDLStateDataRecord & pyrec ); + + PyObject* GetAgeInfo(); // returns pyVaultAgeInfoNode + PyObject* GetAgeDevicesFolder( void ); // returns pyVaultFolderNode + PyObject* GetSubAgesFolder( void ); // returns pyVaultFolderNode + PyObject* GetChronicleFolder( void ); // returns pyVaultFolderNode + // Age chronicle (not the player chronicle!) + PyObject* FindChronicleEntry( const char * entryName ); // returns pyVaultChronicleNode + void AddChronicleEntry( const char * name, UInt32 type, const char * value ); + // Players who have published to devices in this age + PyObject* GetPeopleIKnowAboutFolder( void ); // returns pyVaultPlayerInfoListNode + // PERSONAL AGE SPECIFIC + PyObject* GetBookshelfFolder ( void ); // returns pyVaultFolderNode + // NEXUS SPECIFIC + PyObject* GetPublicAgesFolder( void ); // returns pyVaultFolderNode + PyObject* GetSubAgeLink( const pyAgeInfoStruct & info ); // returns pyVaultAgeLinkNode + // AGE DEVICES. AKA IMAGERS, WHATEVER. + // Add a new device. + void AddDevice( const char * deviceName, PyObject * cb=nil, UInt32 cbContext=0 ); + // Remove a device. + void RemoveDevice( const char * deviceName ); + // True if device exists in age. + bool HasDevice( const char * deviceName ); + // Get the device node by name. + PyObject * GetDevice( const char * deviceName ); // returns pyVaultTextNoteNode + // Sets the inbox associated with a device. + void SetDeviceInbox( const char * deviceName, const char * inboxName, PyObject * cb=nil, UInt32 cbContext=0 ); + // Get the inbox associated with a device. + PyObject * GetDeviceInbox( const char * deviceName ); // returns pyVaultFolderNode + // find matching node + PyObject* FindNode( pyVaultNode* templateNode ) const; // returns pyVaultNode +}; + +#endif // _pyAgeVault_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVaultGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVaultGlue.cpp new file mode 100644 index 00000000..ea104a47 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAgeVaultGlue.cpp @@ -0,0 +1,268 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyAgeVault.h" +#include "pyAgeInfoStruct.h" +#include "pySDL.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptAgeVault, pyAgeVault); + +PYTHON_DEFAULT_NEW_DEFINITION(ptAgeVault, pyAgeVault) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptAgeVault) + +PYTHON_INIT_DEFINITION(ptAgeVault, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeVault, getAgeInfo) +{ + return self->fThis->GetAgeInfo(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeVault, getAgeDevicesFolder) +{ + return self->fThis->GetAgeDevicesFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeVault, getSubAgesFolder) +{ + return self->fThis->GetSubAgesFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeVault, getChronicleFolder) +{ + return self->fThis->GetChronicleFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeVault, getAgesIOwnFolder) +{ + return self->fThis->GetBookshelfFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeVault, getBookshelfFolder) +{ + return self->fThis->GetBookshelfFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeVault, getPeopleIKnowAboutFolder) +{ + return self->fThis->GetPeopleIKnowAboutFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeVault, getPublicAgesFolder) +{ + return self->fThis->GetPublicAgesFolder(); +} + +PYTHON_METHOD_DEFINITION(ptAgeVault, getSubAgeLink, args) +{ + PyObject* ageInfoObj = NULL; + if (!PyArg_ParseTuple(args, "O", &ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "getSubAgeLink expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeInfoStruct::Check(ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "getSubAgeLink expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + pyAgeInfoStruct* ageInfo = pyAgeInfoStruct::ConvertFrom(ageInfoObj); + return self->fThis->GetSubAgeLink(*ageInfo); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeVault, getAgeGuid) +{ + return PyString_FromString(self->fThis->GetAgeGuid()); +} + +PYTHON_METHOD_DEFINITION(ptAgeVault, addDevice, args) +{ + char* name; + PyObject* cbObj = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "s|Ol", &name, &cbObj, &context)) + { + PyErr_SetString(PyExc_TypeError, "addDevice expects a string, an optional object, and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddDevice(name, cbObj, context); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAgeVault, removeDevice, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "removeDevice expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->RemoveDevice(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAgeVault, hasDevice, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "hasDevice expects a string"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->HasDevice(name)); +} + +PYTHON_METHOD_DEFINITION(ptAgeVault, getDevice, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "getDevice expects a string"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetDevice(name); +} + +PYTHON_METHOD_DEFINITION(ptAgeVault, setDeviceInbox, args) +{ + char* name; + char* inboxName; + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "ss|Ol", &name, &inboxName, &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "setDeviceInbox expects two strings, an optional object, and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetDeviceInbox(name, inboxName, cb, context); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAgeVault, getDeviceInbox, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "getDeviceInbox expects a string"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetDeviceInbox(name); +} + +PYTHON_METHOD_DEFINITION(ptAgeVault, findChronicleEntry, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "findChronicleEntry expects a string"); + PYTHON_RETURN_ERROR; + } + return self->fThis->FindChronicleEntry(name); +} + +PYTHON_METHOD_DEFINITION(ptAgeVault, addChronicleEntry, args) +{ + char* name; + unsigned long entryType; + char* val; + if (!PyArg_ParseTuple(args, "sls", &name, &entryType, &val)) + { + PyErr_SetString(PyExc_TypeError, "addChronicleEntry expects a string, an unsigned long, and a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddChronicleEntry(name, entryType, val); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAgeVault, getAgeSDL) +{ + return self->fThis->GetAgeSDL(); +} + +PYTHON_METHOD_DEFINITION(ptAgeVault, updateAgeSDL, args) +{ + PyObject* recordObj = NULL; + if (!PyArg_ParseTuple(args, "O", &recordObj)) + { + PyErr_SetString(PyExc_TypeError, "updateAgeSDL expects a ptSDLStateDataRecord"); + PYTHON_RETURN_NONE; + } + if (!pySDLStateDataRecord::Check(recordObj)) + { + PyErr_SetString(PyExc_TypeError, "updateAgeSDL expects a ptSDLStateDataRecord"); + PYTHON_RETURN_NONE; + } + pySDLStateDataRecord* record = pySDLStateDataRecord::ConvertFrom(recordObj); + self->fThis->UpdateAgeSDL(*record); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptAgeVault) + PYTHON_METHOD_NOARGS(ptAgeVault, getAgeInfo, "Returns a ptVaultAgeInfoNode of the this Age"), + PYTHON_METHOD_NOARGS(ptAgeVault, getAgeDevicesFolder, "Returns a ptVaultFolderNode of the inboxes for the devices in this Age."), + PYTHON_METHOD_NOARGS(ptAgeVault, getSubAgesFolder, "Returns a ptVaultFolderNode of sub Age's folder."), + PYTHON_METHOD_NOARGS(ptAgeVault, getChronicleFolder, "Returns a ptVaultFolderNode"), + PYTHON_METHOD_NOARGS(ptAgeVault, getAgesIOwnFolder, "(depreciated, use getBookshelfFolder) Returns a ptVaultFolderNode that contain the Ages I own"), + PYTHON_METHOD_NOARGS(ptAgeVault, getBookshelfFolder, "Personal age only: Returns a ptVaultFolderNode that contains the owning player's AgesIOwn age list"), + PYTHON_METHOD_NOARGS(ptAgeVault, getPeopleIKnowAboutFolder, "Returns a ptVaultPlayerInfoListNode of the players the Age knows about(?)."), + PYTHON_METHOD_NOARGS(ptAgeVault, getPublicAgesFolder, "Returns a ptVaultFolderNode that contains all the public Ages"), + PYTHON_METHOD(ptAgeVault, getSubAgeLink, "Params: ageInfo\nReturns a ptVaultAgeLinkNode to 'ageInfo' (a ptAgeInfoStruct) for this Age."), + PYTHON_METHOD_NOARGS(ptAgeVault, getAgeGuid, "Returns the current Age's guid as a string."), + PYTHON_METHOD(ptAgeVault, addDevice, "Params: deviceName,cb=None,cbContext=0\nAdds a device to the age"), + PYTHON_METHOD(ptAgeVault, removeDevice, "Params: deviceName\nRemoves a device from the age"), + PYTHON_METHOD(ptAgeVault, hasDevice, "Params: deviceName\nDoes a device with this name exist?"), + PYTHON_METHOD(ptAgeVault, getDevice, "Params: deviceName\nReturns the specified device (ptVaultTextNoteNode)"), + PYTHON_METHOD(ptAgeVault, setDeviceInbox, "Params: deviceName,inboxName,cb=None,cbContext=0\nSet's the device's inbox"), + PYTHON_METHOD(ptAgeVault, getDeviceInbox, "Params: deviceName\nReturns a ptVaultFolderNode of the inbox for the named device in this age."), + PYTHON_METHOD(ptAgeVault, findChronicleEntry, "Params: entryName\nReturns the named ptVaultChronicleNode"), + PYTHON_METHOD(ptAgeVault, addChronicleEntry, "Params: name,type,value\nAdds a chronicle entry with the specified type and value"), + PYTHON_METHOD_NOARGS(ptAgeVault, getAgeSDL, "Returns the age's SDL (ptSDLStateDataRecord)"), + PYTHON_METHOD(ptAgeVault, updateAgeSDL, "Params: pyrec\nUpdates the age's SDL"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptAgeVault, "Accessor class to the Age's vault"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptAgeVault, pyAgeVault) + +PYTHON_CLASS_CHECK_IMPL(ptAgeVault, pyAgeVault) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptAgeVault, pyAgeVault) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyAgeVault::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptAgeVault); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAlarm.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAlarm.cpp new file mode 100644 index 00000000..3ee49e4b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAlarm.cpp @@ -0,0 +1,127 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyAlarm.h" +#include "hsTimer.h" +#include "hsStlUtils.h" + +//////////////////////////////////////////////////////////////////// + +struct pyAlarm +{ + double fStart; + float fSecs; + PyObject * fCb; + UInt32 fCbContext; + pyAlarm( double start, float secs, PyObject * cb, UInt32 cbContext ) + : fStart( start ) + , fSecs( secs ) + , fCb( cb ) + , fCbContext( cbContext ) + { + Py_XINCREF( fCb ); + } + ~pyAlarm() + { + Py_XDECREF( fCb ); + } + bool MaybeFire( double secs ) + { + if ( secs-fStart>fSecs ) + { + Fire(); + return true; + } + return false; + } + void Fire() + { + if ( fCb ) + { + PyObject* func = nil; + + // Call the callback. + func = PyObject_GetAttrString( fCb, "onAlarm" ); + if ( func ) + { + if ( PyCallable_Check(func)>0 ) + { + PyObject *retVal = PyObject_CallMethod(fCb, "onAlarm", "l", fCbContext); + Py_XDECREF(retVal); + } + } + Py_XDECREF(func); + } + } +}; + + +//////////////////////////////////////////////////////////////////// + +//static +pyAlarmMgr * pyAlarmMgr::GetInstance() +{ + static pyAlarmMgr inst; + return &inst; +} + +pyAlarmMgr::~pyAlarmMgr() +{ +// Clear(); +} + +void pyAlarmMgr::Update( double secs ) +{ + Alarms::iterator it = fAlarms.begin(); + while ( it!=fAlarms.end() ) + { + pyAlarm * alarm = (*it); + if ( alarm->MaybeFire( secs ) ) + { + Alarms::iterator jt = it++; + fAlarms.erase( jt ); + delete alarm; + } + else + { + it++; + } + } +} + +void pyAlarmMgr::SetAlarm( float secs, PyObject * cb, UInt32 cbContext ) +{ + double start = hsTimer::GetSysSeconds(); + fAlarms.push_back( TRACKED_NEW pyAlarm( start, secs, cb, cbContext ) ); +} + +void pyAlarmMgr::Clear() +{ + for (Alarms::iterator i = fAlarms.begin(); i != fAlarms.end(); i++) + delete *i; + fAlarms.clear(); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAlarm.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAlarm.h new file mode 100644 index 00000000..76e05ec2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAlarm.h @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyAlarm_h_inc +#define pyAlarm_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include + +struct pyAlarm; +class pyAlarmMgr +{ + typedef std::list Alarms; + Alarms fAlarms; +public: + ~pyAlarmMgr(); + static pyAlarmMgr * GetInstance(); + void Update( double secs ); + void SetAlarm( float secs, PyObject * cb, UInt32 cbContext ); + void Clear(); +}; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp new file mode 100644 index 00000000..f98e7411 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp @@ -0,0 +1,433 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyAudioControl - a wrapper class all the audio control functions +// +////////////////////////////////////////////////////////////////////// + +#include "pyAudioControl.h" + +#include "../plAudio/plAudioSystem.h" +#include "../plAudio/plVoiceChat.h" +#include "../plAudio/plWinMicLevel.h" + +#include "../plAudio/plAudioCaps.h" + +// Sets the master volume of a given audio channel +void pyAudioControl::SetSoundFXVolume( hsScalar volume ) +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kSoundFX; + + // make sure the volume is within range + if( volume > 1.f ) + volume = 1.f; + else if( volume < 0.f ) + volume = 0.f; + + plgAudioSys::SetChannelVolume( chan, volume ); +} + +void pyAudioControl::SetMusicVolume( hsScalar volume ) +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kBgndMusic; + + // make sure the volume is within range + if( volume > 1.f ) + volume = 1.f; + else if( volume < 0.f ) + volume = 0.f; + + plgAudioSys::SetChannelVolume( chan, volume ); +} + +void pyAudioControl::SetVoiceVolume( hsScalar volume ) +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kVoice; + + // make sure the volume is within range + if( volume > 1.f ) + volume = 1.f; + else if( volume < 0.f ) + volume = 0.f; + + plgAudioSys::SetChannelVolume( chan, volume ); +} + +void pyAudioControl::SetAmbienceVolume( hsScalar volume ) +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kAmbience; + + // make sure the volume is within range + if( volume > 1.f ) + volume = 1.f; + else if( volume < 0.f ) + volume = 0.f; + + plgAudioSys::SetChannelVolume( chan, volume ); +} + +void pyAudioControl::SetGUIVolume( hsScalar volume ) +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kGUI; + + // make sure the volume is within range + if( volume > 1.f ) + volume = 1.f; + else if( volume < 0.f ) + volume = 0.f; + + plgAudioSys::SetChannelVolume( chan, volume ); +} + +void pyAudioControl::SetNPCVoiceVolume( hsScalar volume ) +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kNPCVoice; + + // make sure the volume is within range + if( volume > 1.f ) + volume = 1.f; + else if( volume < 0.f ) + volume = 0.f; + + plgAudioSys::SetChannelVolume( chan, volume ); +} + +hsScalar pyAudioControl::GetSoundFXVolume() +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kSoundFX; + return plgAudioSys::GetChannelVolume(chan); +} + +hsScalar pyAudioControl::GetMusicVolume() +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kBgndMusic; + return plgAudioSys::GetChannelVolume(chan); +} + +hsScalar pyAudioControl::GetVoiceVolume() +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kVoice; + return plgAudioSys::GetChannelVolume(chan); +} + +hsScalar pyAudioControl::GetAmbienceVolume() +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kAmbience; + return plgAudioSys::GetChannelVolume(chan); +} + +hsScalar pyAudioControl::GetGUIVolume() +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kGUI; + return plgAudioSys::GetChannelVolume(chan); +} + +hsScalar pyAudioControl::GetNPCVoiceVolume() +{ + plgAudioSys::ASChannel chan; + chan = plgAudioSys::kNPCVoice; + return plgAudioSys::GetChannelVolume(chan); +} + + +// Switch DirectX Audio on or off at runtime +void pyAudioControl::Enable() +{ + plgAudioSys::Activate(true); +} + +void pyAudioControl::Disable() +{ + plgAudioSys::Activate(false); +} + +hsBool pyAudioControl::IsEnabled() +{ + return plgAudioSys::Active(); +} + + +// Enable or disable load-on-demand for sounds +void pyAudioControl::SetLoadOnDemand( hsBool state ) +{ + plSound::SetLoadOnDemand(state); +} + + +// Enables or disables two-stage LOD, where sounds can be loaded into RAM but not into sound buffers. +// ...Less of a performance hit, harder on memory. +void pyAudioControl::SetTwoStageLOD( hsBool state ) +{ + // For two-stage LOD, we want to disable LoadFromDiskOnDemand, so that we'll load into RAM at startup but not + // into sound buffers until demanded to do so. Enabling LoadFromDiskOnDemand basically conserves as much memory + // as possible + plSound::SetLoadFromDiskOnDemand( !state ); +} + + +// Enable audio hardware acceleration +void pyAudioControl::UseHardwareAcceleration( hsBool state ) +{ + plgAudioSys::SetUseHardware(state); +} + +hsBool pyAudioControl::IsHardwareAccelerated() +{ + return plgAudioSys::Hardware(); +} + + +// Enable EAX sound acceleration (requires hardware acceleration) +void pyAudioControl::UseEAXAcceleration( hsBool state ) +{ + plgAudioSys::EnableEAX(state); +} + +hsBool pyAudioControl::IsUsingEAXAcceleration() +{ + return plgAudioSys::UsingEAX(); +} + + +// Mute or unmute all sounds +void pyAudioControl::MuteAll() +{ + plgAudioSys::SetMuted(true); +} + +void pyAudioControl::UnmuteAll() +{ + plgAudioSys::SetMuted(false); +} + +hsBool pyAudioControl::IsMuted() +{ + return plgAudioSys::IsMuted(); +} + +hsBool pyAudioControl::SupportEAX(const char *deviceName) +{ + return plgAudioSys::SupportsEAX(deviceName); +} + + +//------------------------ +// Voice Settings + +// Sets the microphone volume, in the range of 0 to 1 +hsBool pyAudioControl::CanSetMicLevel() +{ + return plWinMicLevel::CanSetLevel(); +} + +void pyAudioControl::SetMicLevel( hsScalar level ) +{ + // make sure the volume is within range + if( level > 1.f ) + level = 1.f; + else if( level < 0.f ) + level = 0.f; + if( CanSetMicLevel() ) + plWinMicLevel::SetLevel( level ); +} + +hsScalar pyAudioControl::GetMicLevel() +{ + return plWinMicLevel::GetLevel(); +} + + +// turn voice recording on or off +void pyAudioControl::EnableVoiceRecording( hsBool state ) +{ + plVoiceRecorder::EnableRecording(state); +} + +hsBool pyAudioControl::IsVoiceRecordingEnabled() +{ + return plVoiceRecorder::RecordingEnabled(); +} + + +// turn voice compression on and off +void pyAudioControl::EnableVoiceCompression( hsBool state ) +{ +} + +hsBool pyAudioControl::IsVoiceCompressionEnabled() +{ + return true; +} + + +// turn voice-over-net on and off +void pyAudioControl::EnableVoiceNetBroadcast( hsBool state ) +{ + //plWinRecorder::EnableNetVoice(state); +} + +hsBool pyAudioControl::IsVoiceNetBroadcastEnabled() +{ + + return true; +} + +void pyAudioControl::EnableVoiceChat(hsBool enable) +{ + plVoicePlayer::Enable(enable); +} + + +// turn voice recording icons on and off +void pyAudioControl::ShowIcons() +{ + plVoiceRecorder::EnableIcons(true); +} + +void pyAudioControl::HideIcons() +{ + plVoiceRecorder::EnableIcons(false); +} + + +// turn push-to-talk on or off +void pyAudioControl::PushToTalk( hsBool state ) +{ + plVoiceRecorder::EnablePushToTalk(state); +} + +// Set the squelch level +void pyAudioControl::SquelchLevel( hsScalar level ) +{ + plVoiceRecorder::SetSquelch(level); +} + + +// Adjust voice packet frame size +void pyAudioControl::RecordFrame( Int32 size ) +{ +} + + +// Set the sample rate for recording +void pyAudioControl::RecordSampleRate( Int32 sample_rate ) +{ +} + +UInt8 pyAudioControl::GetPriorityCutoff( void ) +{ + return plgAudioSys::GetPriorityCutoff(); +} + +void pyAudioControl::SetPriorityCutoff( UInt8 cut ) +{ + plgAudioSys::SetPriorityCutoff( cut ); +} + +void pyAudioControl::SetAudioSystemMode(int mode) +{ + switch (mode) + { + case plgAudioSys::kDisabled: + plgAudioSys::SetAudioMode(plgAudioSys::kDisabled); + break; + case plgAudioSys::kSoftware: + plgAudioSys::SetAudioMode(plgAudioSys::kSoftware); + break; + case plgAudioSys::kHardware: + plgAudioSys::SetAudioMode(plgAudioSys::kHardware); + break; + case plgAudioSys::kHardwarePlusEAX: + plgAudioSys::SetAudioMode(plgAudioSys::kHardwarePlusEAX); + break; + default: + break; + } +} + +int pyAudioControl::GetAudioSystemMode() +{ + return plgAudioSys::GetAudioMode(); +} + +int pyAudioControl::GetHighestAudioMode() +{ + int highestMode = plgAudioSys::kDisabled; + plAudioCaps caps = plAudioCapsDetector::Detect(); + + if ( caps.IsEAXAvailable() ) + { + highestMode = plgAudioSys::kHardwarePlusEAX; + } + else + { + if ( 1 ) // This is taken care of in the audio system + { + highestMode = plgAudioSys::kHardware; + } + else + { + if ( caps.IsAvailable() ) + { + highestMode = plgAudioSys::kSoftware; + } + } + } + + return highestMode; +} + +int pyAudioControl::GetNumAudioDevices() +{ + return plgAudioSys::GetNumAudioDevices(); +} + +const char *pyAudioControl::GetAudioDeviceName(int index) +{ + return plgAudioSys::GetAudioDeviceName(index); +} + +void pyAudioControl::SetDeviceName(const char *device, bool restart) +{ + plgAudioSys::SetDeviceName(device, restart); +} + +const char * pyAudioControl::GetDeviceName() +{ + return plgAudioSys::GetDeviceName(); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h new file mode 100644 index 00000000..c2c4c24f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h @@ -0,0 +1,152 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyAutoControl_h_ +#define _pyAutoControl_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyAudioControl - a wrapper class all the audio control functions +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" + +#include +#include "pyGlueHelpers.h" + +class pyAudioControl +{ +protected: + pyAudioControl() {}; + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptAudioControl); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyAudioControl object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyAudioControl); // converts a PyObject to a pyAudioControl (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + //----------------------- + // Audio settings + + // Sets the master volume of a given audio channel + virtual void SetSoundFXVolume( hsScalar volume ); + virtual void SetMusicVolume( hsScalar volume ); + virtual void SetVoiceVolume( hsScalar volume ); + virtual void SetAmbienceVolume( hsScalar volume ); + virtual void SetGUIVolume( hsScalar volume ); + virtual void SetNPCVoiceVolume( hsScalar volume ); + virtual hsScalar GetSoundFXVolume(); + virtual hsScalar GetMusicVolume(); + virtual hsScalar GetVoiceVolume(); + virtual hsScalar GetAmbienceVolume(); + virtual hsScalar GetGUIVolume(); + virtual hsScalar GetNPCVoiceVolume(); + + // Switch DirectX Audio on or off at runtime + virtual void Enable(); + virtual void Disable(); + virtual hsBool IsEnabled(); + + // Enable or disable load-on-demand for sounds + virtual void SetLoadOnDemand( hsBool state ); + + // Enables or disables two-stage LOD, where sounds can be loaded into RAM but not into sound buffers. + // ...Less of a performance hit, harder on memory. + virtual void SetTwoStageLOD( hsBool state ); + + // Enable audio hardware acceleration + virtual void UseHardwareAcceleration( hsBool state ); + virtual hsBool IsHardwareAccelerated(); + + // Enable EAX sound acceleration (requires hardware acceleration) + virtual void UseEAXAcceleration( hsBool state ); + virtual hsBool IsUsingEAXAcceleration(); + + // Mute or unmute all sounds + virtual void MuteAll(); + virtual void UnmuteAll(); + virtual hsBool IsMuted(); + + virtual void SetAudioSystemMode(int mode); // sets the current mode + virtual int GetAudioSystemMode(); // returns the current mode + virtual int GetHighestAudioMode(); // returns the highest mode the card is capable of handling + virtual int GetNumAudioDevices(); + virtual const char *GetAudioDeviceName(int index); + virtual void SetDeviceName(const char *device, bool restart); + virtual const char *GetDeviceName(); + + + + //------------------------ + // Voice Settings + + // Sets the microphone volume, in the range of 0 to 1 + virtual hsBool CanSetMicLevel(); + virtual void SetMicLevel( hsScalar level ); + virtual hsScalar GetMicLevel(); + + // turn voice recording on or off + virtual void EnableVoiceRecording( hsBool state ); + virtual hsBool IsVoiceRecordingEnabled(); + + // turn voice compression on and off + virtual void EnableVoiceCompression( hsBool state ); + virtual hsBool IsVoiceCompressionEnabled(); + + // turn voice-over-net on and off + virtual void EnableVoiceNetBroadcast( hsBool state ); + virtual hsBool IsVoiceNetBroadcastEnabled(); + + void EnableVoiceChat(hsBool enable); + + // turn voice recording icons on and off + virtual void ShowIcons(); + virtual void HideIcons(); + + // turn push-to-talk on or off + virtual void PushToTalk( hsBool state ); + + // Set the squelch level + virtual void SquelchLevel( hsScalar level ); + + // Adjust voice packet frame size + virtual void RecordFrame( Int32 size ); + + // Set the sample rate for recording + virtual void RecordSampleRate( Int32 sample_rate ); + + virtual UInt8 GetPriorityCutoff( void ); + virtual void SetPriorityCutoff( UInt8 cut ); + + // does the device specified support EAX + virtual hsBool SupportEAX(const char *deviceName); + +}; + +#endif // _pyAudioControl_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp new file mode 100644 index 00000000..711366cf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp @@ -0,0 +1,518 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyAudioControl.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptAudioControl, pyAudioControl); + +PYTHON_DEFAULT_NEW_DEFINITION(ptAudioControl, pyAudioControl) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptAudioControl) + +PYTHON_INIT_DEFINITION(ptAudioControl, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setSoundFXVolume, args) +{ + float volume; + if (!PyArg_ParseTuple(args, "f", &volume)) + { + PyErr_SetString(PyExc_TypeError, "setSoundFXVolume expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSoundFXVolume(volume); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setMusicVolume, args) +{ + float volume; + if (!PyArg_ParseTuple(args, "f", &volume)) + { + PyErr_SetString(PyExc_TypeError, "setMusicVolume expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetMusicVolume(volume); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setVoiceVolume, args) +{ + float volume; + if (!PyArg_ParseTuple(args, "f", &volume)) + { + PyErr_SetString(PyExc_TypeError, "setVoiceVolume expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetVoiceVolume(volume); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setAmbienceVolume, args) +{ + float volume; + if (!PyArg_ParseTuple(args, "f", &volume)) + { + PyErr_SetString(PyExc_TypeError, "setAmbienceVolume expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAmbienceVolume(volume); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setGUIVolume, args) +{ + float volume; + if (!PyArg_ParseTuple(args, "f", &volume)) + { + PyErr_SetString(PyExc_TypeError, "setGUIVolume expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetGUIVolume(volume); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setNPCVoiceVolume, args) +{ + float volume; + if (!PyArg_ParseTuple(args, "f", &volume)) + { + PyErr_SetString(PyExc_TypeError, "setNPCVoiceVolume expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNPCVoiceVolume(volume); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getSoundFXVolume) +{ + return PyFloat_FromDouble(self->fThis->GetSoundFXVolume()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getMusicVolume) +{ + return PyFloat_FromDouble(self->fThis->GetMusicVolume()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getVoiceVolume) +{ + return PyFloat_FromDouble(self->fThis->GetVoiceVolume()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getAmbienceVolume) +{ + return PyFloat_FromDouble(self->fThis->GetAmbienceVolume()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getGUIVolume) +{ + return PyFloat_FromDouble(self->fThis->GetGUIVolume()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getNPCVoiceVolume) +{ + return PyFloat_FromDouble(self->fThis->GetNPCVoiceVolume()); +} + + +PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, enable, Enable) +PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, disable, Disable) + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, isEnabled) +{ + PYTHON_RETURN_BOOL(self->fThis->IsEnabled()); +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setLoadOnDemand, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "setLoadOnDemand expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetLoadOnDemand(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setTwoStageLOD, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "setTwoStageLOD expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetTwoStageLOD(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, useHardwareAcceleration, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "useHardwareAcceleration expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->UseHardwareAcceleration(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, isHardwareAccelerated) +{ + PYTHON_RETURN_BOOL(self->fThis->IsHardwareAccelerated()); +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, useEAXAcceleration, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "useEAXAcceleration expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->UseEAXAcceleration(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, isUsingEAXAcceleration) +{ + PYTHON_RETURN_BOOL(self->fThis->IsUsingEAXAcceleration()); +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, supportsEAX, args) +{ + char *deviceName; + if (!PyArg_ParseTuple(args, "s", &deviceName)) + { + PyErr_SetString(PyExc_TypeError, "supportsEAX expects a string"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->SupportEAX(deviceName)); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, muteAll, MuteAll) +PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, unmuteAll, UnmuteAll) + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, isMuted) +{ + PYTHON_RETURN_BOOL(self->fThis->IsMuted()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, canSetMicLevel) +{ + PYTHON_RETURN_BOOL(self->fThis->CanSetMicLevel()); +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setMicLevel, args) +{ + float volume; + if (!PyArg_ParseTuple(args, "f", &volume)) + { + PyErr_SetString(PyExc_TypeError, "setMicLevel expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetMicLevel(volume); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getMicLevel) +{ + return PyFloat_FromDouble(self->fThis->GetMicLevel()); +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, enableVoiceRecording, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "enableVoiceRecording expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->EnableVoiceRecording(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, enableVoiceChat, args) +{ + char enable; + if (!PyArg_ParseTuple(args, "b", &enable)) + { + PyErr_SetString(PyExc_TypeError, "enableVoiceChat expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->EnableVoiceChat(enable != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, isVoiceRecordingEnabled) +{ + PYTHON_RETURN_BOOL(self->fThis->IsVoiceRecordingEnabled()); +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, enableVoiceCompression, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "enableVoiceCompression expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->EnableVoiceCompression(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, isVoiceCompressionEnabled) +{ + PYTHON_RETURN_BOOL(self->fThis->IsVoiceCompressionEnabled()); +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, enableVoiceNetBroadcast, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "enableVoiceNetBroadcast expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->EnableVoiceNetBroadcast(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, isVoiceNetBroadcastEnabled) +{ + PYTHON_RETURN_BOOL(self->fThis->IsVoiceNetBroadcastEnabled()); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, showIcons, ShowIcons) +PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, hideIcons, HideIcons) + +PYTHON_METHOD_DEFINITION(ptAudioControl, pushToTalk, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "pushToTalk expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->PushToTalk(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, squelchLevel, args) +{ + float volume; + if (!PyArg_ParseTuple(args, "f", &volume)) + { + PyErr_SetString(PyExc_TypeError, "squelchLevel expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SquelchLevel(volume); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, recordFrame, args) +{ + long frameSize; + if (!PyArg_ParseTuple(args, "l", &frameSize)) + { + PyErr_SetString(PyExc_TypeError, "recordFrame expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->RecordFrame(frameSize); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, recordSampleRate, args) +{ + long sampleRate; + if (!PyArg_ParseTuple(args, "l", &sampleRate)) + { + PyErr_SetString(PyExc_TypeError, "recordSampleRate expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->RecordSampleRate(sampleRate); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getPriorityCutoff) +{ + return PyInt_FromLong(self->fThis->GetPriorityCutoff()); +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setPriorityCutoff, args) +{ + unsigned char cutoff; + if (!PyArg_ParseTuple(args, "b", &cutoff)) + { + PyErr_SetString(PyExc_TypeError, "setPriorityCutoff expects an unsigned 8-bit int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetPriorityCutoff(cutoff); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setMode, args) +{ + int mode; + if (!PyArg_ParseTuple(args, "i", &mode)) + { + PyErr_SetString(PyExc_TypeError, "setMode expects an integer"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAudioSystemMode(mode); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getMode) +{ + return PyInt_FromLong((long)self->fThis->GetAudioSystemMode()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getHighestMode) +{ + return PyInt_FromLong((long)self->fThis->GetHighestAudioMode()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getNumAudioDevices) +{ + return PyInt_FromLong(self->fThis->GetNumAudioDevices()); +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, getAudioDeviceName, args) +{ + int index; + if (!PyArg_ParseTuple(args, "i", &index)) + { + PyErr_SetString(PyExc_TypeError, "getAudioDeviceName expects an int"); + PYTHON_RETURN_ERROR; + } + return PyString_FromString(self->fThis->GetAudioDeviceName(index)); +} + +PYTHON_METHOD_DEFINITION(ptAudioControl, setDeviceName, args) +{ + char *devicename = NULL; + int restart; + if (!PyArg_ParseTuple(args, "si", &devicename, &restart)) + { + PyErr_SetString(PyExc_TypeError, "setDeviceName expects a string and a bool"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetDeviceName(devicename, restart != 0); + PYTHON_RETURN_NONE; +} + + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, getDeviceName) +{ + return PyString_FromString(self->fThis->GetDeviceName()); +} + +PYTHON_START_METHODS_TABLE(ptAudioControl) + PYTHON_METHOD(ptAudioControl, setSoundFXVolume, "Params: volume\nSets the SoundFX volume (0.0 to 1.0) for the game.\n" + "This only sets the volume for this game session."), + PYTHON_METHOD(ptAudioControl, setMusicVolume, "Params: volume\nSets the Music volume (0.0 to 1.0) for the game.\n" + "This only sets the volume for this game session."), + PYTHON_METHOD(ptAudioControl, setVoiceVolume, "Params: volume\nSets the Voice volume (0.0 to 1.0) for the game.\n" + "This only sets the volume for this game session."), + PYTHON_METHOD(ptAudioControl, setAmbienceVolume, "Params: volume\nSets the Ambience volume (0.0 to 1.0) for the game.\n" + "This only sets the volume for this game session."), + PYTHON_METHOD(ptAudioControl, setGUIVolume, "Params: volume\nSets the GUI dialog volume (0.0 to 1.0) for the game.\n" + "This only sets the volume for this game session."), + PYTHON_METHOD(ptAudioControl, setNPCVoiceVolume, "Params: volume\nSets the NPC's voice volume (0.0 to 1.0) for the game.\n" + "This only sets the volume for this game session."), + PYTHON_METHOD_NOARGS(ptAudioControl, getSoundFXVolume, "Returns the volume (0.0 to 1.0) for the Sound FX."), + PYTHON_METHOD_NOARGS(ptAudioControl, getMusicVolume, "Returns the volume (0.0 to 1.0) for the Music."), + PYTHON_METHOD_NOARGS(ptAudioControl, getVoiceVolume, "Returns the volume (0.0 to 1.0) for the Voices."), + PYTHON_METHOD_NOARGS(ptAudioControl, getAmbienceVolume, "Returns the volume (0.0 to 1.0) for the Ambiance."), + PYTHON_METHOD_NOARGS(ptAudioControl, getGUIVolume, "Returns the volume (0.0 to 1.0) for the GUI dialogs."), + PYTHON_METHOD_NOARGS(ptAudioControl, getNPCVoiceVolume, "Returns the volume (0.0 to 1.0) for the NPC's voice."), + PYTHON_BASIC_METHOD(ptAudioControl, enable, "Enables audio"), + PYTHON_BASIC_METHOD(ptAudioControl, disable, "Disabled audio"), + PYTHON_METHOD_NOARGS(ptAudioControl, isEnabled, "Is the audio enabled? Returns 1 if true otherwise returns 0."), + PYTHON_METHOD(ptAudioControl, setLoadOnDemand, "Params: state\nEnables or disables the load on demand for sounds."), + PYTHON_METHOD(ptAudioControl, setTwoStageLOD, "Params: state\nEnables or disables two-stage LOD, where sounds can be loaded into RAM but not into sound buffers.\n" + "...Less of a performance hit, harder on memory."), + PYTHON_METHOD(ptAudioControl, useHardwareAcceleration, "Params: state\nEnables or disables audio hardware acceleration."), + PYTHON_METHOD_NOARGS(ptAudioControl, isHardwareAccelerated, "Is audio hardware acceleration enabled? Returns 1 if true otherwise returns 0."), + PYTHON_METHOD(ptAudioControl, useEAXAcceleration, "Params: state\nEnables or disables EAX sound acceleration (requires hardware acceleration)."), + PYTHON_METHOD_NOARGS(ptAudioControl, isUsingEAXAcceleration, "Is EAX sound acceleration enabled? Returns 1 if true otherwise returns 0."), + PYTHON_BASIC_METHOD(ptAudioControl, muteAll, "Mutes all sounds."), + PYTHON_BASIC_METHOD(ptAudioControl, unmuteAll, "Unmutes all sounds."), + PYTHON_METHOD_NOARGS(ptAudioControl, isMuted, "Are all sounds muted? Returns 1 if true otherwise returns 0."), + PYTHON_METHOD_NOARGS(ptAudioControl, canSetMicLevel, "Can the microphone level be set? Returns 1 if true otherwise returns 0."), + PYTHON_METHOD(ptAudioControl, setMicLevel, "Params: level\nSets the microphone recording level (0.0 to 1.0)."), + PYTHON_METHOD_NOARGS(ptAudioControl, getMicLevel, "Returns the microphone recording level (0.0 to 1.0)."), + PYTHON_METHOD(ptAudioControl, enableVoiceRecording, "Params: state\nEnables or disables voice recording."), + PYTHON_METHOD_NOARGS(ptAudioControl, isVoiceRecordingEnabled, "Is voice recording enabled? Returns 1 if true otherwise returns 0."), + PYTHON_METHOD(ptAudioControl, enableVoiceCompression, "Params: state\nEnables or disables voice compression."), + PYTHON_METHOD_NOARGS(ptAudioControl, isVoiceCompressionEnabled, "Is voice compression enabled? Returns 1 if true otherwise returns 0."), + PYTHON_METHOD(ptAudioControl, enableVoiceNetBroadcast, "Params: state\nEnables or disables voice over network broadcast."), + PYTHON_METHOD_NOARGS(ptAudioControl, isVoiceNetBroadcastEnabled, "Is voice over net enabled? Returns 1 if true otherwise returns 0."), + PYTHON_BASIC_METHOD(ptAudioControl, showIcons, "Shows (enables) the voice recording icons."), + PYTHON_BASIC_METHOD(ptAudioControl, hideIcons, "Hides (disables) the voice recording icons."), + PYTHON_METHOD(ptAudioControl, pushToTalk, "Params: state\nEnables or disables 'push-to-talk'."), + PYTHON_METHOD(ptAudioControl, squelchLevel, "Params: level\nSets the squelch level."), + PYTHON_METHOD(ptAudioControl, recordFrame, "Params: size\nSets the voice packet frame size."), + PYTHON_METHOD(ptAudioControl, recordSampleRate, "Params: sampleRate\nSets the recording sample rate."), + PYTHON_METHOD_NOARGS(ptAudioControl, getPriorityCutoff, "Returns current sound priority"), + PYTHON_METHOD(ptAudioControl, setPriorityCutoff, "Params: priority\nSets the sound priority"), + PYTHON_METHOD(ptAudioControl, setMode, "Params: mode\nSets the audio system mode"), + PYTHON_METHOD_NOARGS(ptAudioControl, getMode, "Gets the audio system mode"), + PYTHON_METHOD_NOARGS(ptAudioControl, getHighestMode, "Gets the highest possible audio system mode"), + PYTHON_METHOD_NOARGS(ptAudioControl, getNumAudioDevices, "Returns the number of available audio devices."), + PYTHON_METHOD(ptAudioControl, getAudioDeviceName, "Params: index\nGets the name of audio device for the given index"), + PYTHON_METHOD(ptAudioControl, setDeviceName, "Params: devicename,restart\nSets the device name for the audio system, and optionally restarts it"), + PYTHON_METHOD(ptAudioControl, getDeviceName, "Gets the name for the device being used by the audio system"), + PYTHON_METHOD(ptAudioControl, supportsEAX, "Returns true or false based on whether or not a the device specified supports EAX"), + PYTHON_METHOD(ptAudioControl, enableVoiceChat, "Params: state\nEnables or disables voice chat."), + +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptAudioControl, "Accessor class to the Audio controls"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptAudioControl, pyAudioControl) + +PYTHON_CLASS_CHECK_IMPL(ptAudioControl, pyAudioControl) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptAudioControl, pyAudioControl) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyAudioControl::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptAudioControl); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgr.cpp new file mode 100644 index 00000000..84087252 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgr.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyCCRMgr - a wrapper class to provide interface to the pfCCR stuff +// +////////////////////////////////////////////////////////////////////// + + +#include "../pfCCR/plCCRMgr.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgr.h new file mode 100644 index 00000000..f04b3c0f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgr.h @@ -0,0 +1,40 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyCCRMgr_h_inc +#define pyCCRMgr_h_inc + +////////////////////////////////////////////////////////////////////// +// +// pyCCRMgr - a wrapper class to provide interface to the pfCCR stuff +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "../pfCCR/plCCRMgr.h" + + +#endif // pyCCRMgr_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgrGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgrGlue.cpp new file mode 100644 index 00000000..94f0c700 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgrGlue.cpp @@ -0,0 +1,29 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyCCRMgr.h" +#include "pyAgeLinkStruct.h" + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgrGlue2.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgrGlue2.cpp new file mode 100644 index 00000000..c6b70a7a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCCRMgrGlue2.cpp @@ -0,0 +1,28 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyCCRMgr.h" + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCluster.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCluster.cpp new file mode 100644 index 00000000..e6a6d08d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCluster.cpp @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyCluster.h" +#include "../plDrawable/plClusterGroup.h" + +pyCluster::pyCluster(plKey key) +{ + fClusterKey = key; +} + +pyCluster::pyCluster(pyKey& key) +{ + fClusterKey = key.getKey(); +} + +void pyCluster::SetVisible(bool visible) +{ + if (fClusterKey) + { + plClusterGroup* clusterGroup = plClusterGroup::ConvertNoRef(fClusterKey->ObjectIsLoaded()); + if (clusterGroup) + clusterGroup->SetVisible(visible); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCluster.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCluster.h new file mode 100644 index 00000000..29f8a35b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCluster.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyCluster_h +#define pyCluster_h + +#include "pyKey.h" + +#include +#include "pyGlueHelpers.h" + +////////////////////////////////////////////////////////////////////// +// +// pyCluster - a wrapper class to provide interface to cluster objects +// +////////////////////////////////////////////////////////////////////// + +class pyCluster +{ +private: + plKey fClusterKey; + +protected: + pyCluster(): fClusterKey(nil) {} // for python glue only, do NOT call + pyCluster(plKey key); + pyCluster(pyKey& key); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptCluster); + static PyObject *New(plKey key); + static PyObject *New(pyKey& key); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyCluster object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyCluster); // converts a PyObject to a pyCluster (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + void SetKey(pyKey& key) {fClusterKey = key.getKey();} // for python glue only, do NOT call + + void SetVisible(bool visible); +}; + + +#endif // pyCluster_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyClusterGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyClusterGlue.cpp new file mode 100644 index 00000000..26858566 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyClusterGlue.cpp @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyCluster.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptCluster, pyCluster); + +PYTHON_DEFAULT_NEW_DEFINITION(ptCluster, pyCluster) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptCluster) + +PYTHON_INIT_DEFINITION(ptCluster, args, keywords) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->SetKey(*key); + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptCluster, setVisible, args) +{ + char visibleFlag; + if (!PyArg_ParseTuple(args, "b", &visibleFlag)) + { + PyErr_SetString(PyExc_TypeError, "setVisible expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetVisible(visibleFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptCluster) + PYTHON_METHOD(ptCluster, setVisible, "Params:visible\nShows or hides the cluster object"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptCluster, "Params:key\nCreates a new ptCluster"); + +// required functions for PyObject interoperability +PyObject *pyCluster::New(plKey key) +{ + ptCluster *newObj = (ptCluster*)ptCluster_type.tp_new(&ptCluster_type, NULL, NULL); + newObj->fThis->fClusterKey = key; + return (PyObject*)newObj; +} + +PyObject *pyCluster::New(pyKey& key) +{ + ptCluster *newObj = (ptCluster*)ptCluster_type.tp_new(&ptCluster_type, NULL, NULL); + newObj->fThis->fClusterKey = key.getKey(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptCluster, pyCluster) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptCluster, pyCluster) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyCluster::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptCluster); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColor.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColor.cpp new file mode 100644 index 00000000..61e4a59c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColor.cpp @@ -0,0 +1,36 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyColor.h" + +pyColor::pyColor(hsScalar r, hsScalar g, hsScalar b, hsScalar a) +{ + fColor.Set(r, g, b ,a); +} + +pyColor::pyColor(hsColorRGBA color) +{ + fColor = color; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColor.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColor.h new file mode 100644 index 00000000..1567c59f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColor.h @@ -0,0 +1,109 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyColor_h_ +#define _pyColor_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyColor - the wrapper class for hsColorRGBA structure +// +////////////////////////////////////////////////////////////////////// + +#include "hsColorRGBA.h" + +#include +#include "pyGlueHelpers.h" + +class pyColor +{ +private: + hsColorRGBA fColor; + +protected: + pyColor(hsScalar r=0.0f, hsScalar g=0.0f, hsScalar b=0.0f, hsScalar a=0.0f); + pyColor(hsColorRGBA color); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptColor); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(hsScalar red, hsScalar green, hsScalar blue, hsScalar alpha = 0.0f); + static PyObject *New(const hsColorRGBA & color); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyColor object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyColor); // converts a PyObject to a pyColor (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // non-python interface + hsColorRGBA getColor() { return fColor; } + void setColor(hsColorRGBA color) { fColor = color; } + + // python get attributes helpers + hsScalar getRed() const { return fColor.r; } + hsScalar getGreen() const { return fColor.g; } + hsScalar getBlue() const { return fColor.b; } + hsScalar getAlpha() const { return fColor.a; } + + // python set attributes helpers + void setRed(hsScalar red) { fColor.r = red; } + void setGreen(hsScalar green) { fColor.g = green; } + void setBlue(hsScalar blue) { fColor.b = blue; } + void setAlpha(hsScalar alpha) { fColor.a = alpha; } + + // override the equals to operator + hsBool operator==(const pyColor &color) const + { + return ((pyColor*)this)->getColor() == ((pyColor&)color).getColor(); + } + hsBool operator!=(const pyColor &color) const { return !(color == *this); } + + // helper colors settings + void White() { fColor.Set(1.0,1.0,1.0,1.0); } + void Black() { fColor.Set(0.0,0.0,0.0,1.0); } + void Red() { fColor.Set(1.0,0.0,0.0,1.0); } + void Green() { fColor.Set(0.0,1.0,0.0,1.0); } + void Blue() { fColor.Set(0.0,0.0,1.0,1.0); } + void Magenta() { fColor.Set(1.0,0.0,1.0,1.0); } + void Cyan() { fColor.Set(0.0,1.0,1.0,1.0); } + void Yellow() { fColor.Set(1.0,1.0,0.0,1.0); } + + void Brown() { fColor.Set(0.65, 0.165,0.165,1.0); } + void Gray() { fColor.Set(0.75, 0.75, 0.75, 1.0); } + void Orange() { fColor.Set(1.0, 0.5, 0.0, 1.0); } + void Pink() { fColor.Set(0.73, 0.56, 0.56, 1.0); } + void DarkBrown() { fColor.Set(0.36, 0.25, 0.20, 1.0); } + void DarkGreen() { fColor.Set(0.18, 0.31, 0.18, 1.0); } + void DarkPurple() { fColor.Set(0.53, 0.12, 0.47, 1.0); } + void NavyBlue() { fColor.Set(0.137,0.137,0.557,1.0); } + void Maroon() { fColor.Set(0.557,0.137,0.42, 1.0); } + void Tan() { fColor.Set(0.858,0.576,0.439,1.0); } + void SlateBlue() { fColor.Set(0.0, 0.495,1.0, 1.0); } + void SteelBlue() { fColor.Set(0.137,0.42, 0.557,1.0); } + +}; + + +#endif // _pyColor_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColorGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColorGlue.cpp new file mode 100644 index 00000000..a813772c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyColorGlue.cpp @@ -0,0 +1,296 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyColor.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptColor, pyColor); + +PYTHON_DEFAULT_NEW_DEFINITION(ptColor, pyColor) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptColor) + +PYTHON_INIT_DEFINITION(ptColor, args, keywords) +{ + char *kwlist[] = {"red", "green", "blue", "alpha", NULL}; + PyObject* redObj = NULL; + PyObject* greenObj = NULL; + PyObject* blueObj = NULL; + PyObject* alphaObj = NULL; + float red = 0.0f, green = 0.0f, blue = 0.0f, alpha = 0.0f; + if (!PyArg_ParseTupleAndKeywords(args, keywords, "|OOOO", kwlist, &redObj, &greenObj, &blueObj, &alphaObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects four optional floats"); + PYTHON_RETURN_INIT_ERROR; + } + // extra code to allow ints if people are lazy + if (redObj) + { + if (PyFloat_Check(redObj)) + red = (float)PyFloat_AsDouble(redObj); + else if (PyInt_Check(redObj)) + red = (float)PyInt_AsLong(redObj); + else + { + PyErr_SetString(PyExc_TypeError, "__init__ expects four optional floats"); + PYTHON_RETURN_INIT_ERROR; + } + } + if (greenObj) + { + if (PyFloat_Check(greenObj)) + green = (float)PyFloat_AsDouble(greenObj); + else if (PyInt_Check(greenObj)) + green = (float)PyInt_AsLong(greenObj); + else + { + PyErr_SetString(PyExc_TypeError, "__init__ expects four optional floats"); + PYTHON_RETURN_INIT_ERROR; + } + } + if (blueObj) + { + if (PyFloat_Check(blueObj)) + blue = (float)PyFloat_AsDouble(blueObj); + else if (PyInt_Check(blueObj)) + blue = (float)PyInt_AsLong(blueObj); + else + { + PyErr_SetString(PyExc_TypeError, "__init__ expects four optional floats"); + PYTHON_RETURN_INIT_ERROR; + } + } + if (alphaObj) + { + if (PyFloat_Check(alphaObj)) + alpha = (float)PyFloat_AsDouble(alphaObj); + else if (PyInt_Check(alphaObj)) + alpha = (float)PyInt_AsLong(alphaObj); + else + { + PyErr_SetString(PyExc_TypeError, "__init__ expects four optional floats"); + PYTHON_RETURN_INIT_ERROR; + } + } + + self->fThis->setRed(red); + self->fThis->setGreen(green); + self->fThis->setBlue(blue); + self->fThis->setAlpha(alpha); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_RICH_COMPARE_DEFINITION(ptColor, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pyColor::Check(obj1) || !pyColor::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptColor object"); + PYTHON_RCOMPARE_ERROR; + } + } + pyColor *color1 = pyColor::ConvertFrom(obj1); + pyColor *color2 = pyColor::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*color1) == (*color2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*color1) != (*color2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptColor object"); + PYTHON_RCOMPARE_ERROR; +} +// quick and easy macro for making element get functions +#define GET_ELEMENT_FUNC(funcName) \ + PYTHON_METHOD_DEFINITION_NOARGS(ptColor, funcName) \ +{ \ + return PyFloat_FromDouble((double)self->fThis->funcName()); \ +} + +GET_ELEMENT_FUNC(getRed) +GET_ELEMENT_FUNC(getGreen) +GET_ELEMENT_FUNC(getBlue) +GET_ELEMENT_FUNC(getAlpha) + +// quick and easy macro for making element set functions +#define SET_ELEMENT_FUNC(funcName) \ + PYTHON_METHOD_DEFINITION(ptColor, funcName, args) \ +{ \ + float value; \ + if (!PyArg_ParseTuple(args, "f", &value)) \ +{ \ + PyErr_SetString(PyExc_TypeError, #funcName " expects a float"); \ + PYTHON_RETURN_ERROR; \ +} \ + self->fThis->funcName(value); \ + PYTHON_RETURN_NONE; \ +} + +SET_ELEMENT_FUNC(setRed) +SET_ELEMENT_FUNC(setGreen) +SET_ELEMENT_FUNC(setBlue) +SET_ELEMENT_FUNC(setAlpha) + +// quick and easy macro for the color functions +#define COLOR_FUNC(funcName, classFunc) \ + PYTHON_METHOD_DEFINITION_NOARGS(ptColor, funcName) \ +{ \ + self->fThis->classFunc(); /* set the internal color */ \ + Py_INCREF((PyObject*)self); /* incref it because we have to return a new reference */ \ + return (PyObject*)self; /* and return ourself */ \ +} + +COLOR_FUNC(white, White) +COLOR_FUNC(black, Black) +COLOR_FUNC(red, Red) +COLOR_FUNC(green, Green) +COLOR_FUNC(blue, Blue) +COLOR_FUNC(magenta, Magenta) +COLOR_FUNC(cyan, Cyan) +COLOR_FUNC(yellow, Yellow) +COLOR_FUNC(brown, Brown) +COLOR_FUNC(gray, Gray) +COLOR_FUNC(orange, Orange) +COLOR_FUNC(pink, Pink) +COLOR_FUNC(darkbrown, DarkBrown) +COLOR_FUNC(darkgreen, DarkGreen) +COLOR_FUNC(darkpurple, DarkPurple) +COLOR_FUNC(navyblue, NavyBlue) +COLOR_FUNC(maroon, Maroon) +COLOR_FUNC(tan, Tan) +COLOR_FUNC(slateblue, SlateBlue) +COLOR_FUNC(steelblue, SteelBlue) + +PYTHON_START_METHODS_TABLE(ptColor) + PYTHON_METHOD_NOARGS(ptColor, getRed, "Get the red component of the color"), + PYTHON_METHOD_NOARGS(ptColor, getGreen, "Get the green component of the color"), + PYTHON_METHOD_NOARGS(ptColor, getBlue, "Get the blue component of the color"), + PYTHON_METHOD_NOARGS(ptColor, getAlpha, "Get the alpha blend component of the color"), + + PYTHON_METHOD(ptColor, setRed, "Params: red\nSet the red component of the color. 0.0 to 1.0"), + PYTHON_METHOD(ptColor, setGreen, "Params: green\nSet the green component of the color. 0.0 to 1.0"), + PYTHON_METHOD(ptColor, setBlue, "Params: blue\nSet the blue component of the color. 0.0 to 1.0"), + PYTHON_METHOD(ptColor, setAlpha, "Params: alpha\nSet the alpha blend component of the color. 0.0 to 1.0"), + + PYTHON_METHOD_NOARGS(ptColor, white, "Sets the color to be white\n" + "Example: white = ptColor().white()"), + PYTHON_METHOD_NOARGS(ptColor, black, "Sets the color to be black\n" + "Example: black = ptColor().black()"), + PYTHON_METHOD_NOARGS(ptColor, red, "Sets the color to be red\n" + "Example: red = ptColor().red()"), + PYTHON_METHOD_NOARGS(ptColor, green, "Sets the color to be green\n" + "Example: green = ptColor().green()"), + PYTHON_METHOD_NOARGS(ptColor, blue, "Sets the color to be blue\n" + "Example: blue = ptColor().blue()"), + PYTHON_METHOD_NOARGS(ptColor, magenta, "Sets the color to be magenta\n" + "Example: magenta = ptColor().magenta()"), + PYTHON_METHOD_NOARGS(ptColor, cyan, "Sets the color to be cyan\n" + "Example: cyan = ptColor.cyan()"), + PYTHON_METHOD_NOARGS(ptColor, yellow, "Sets the color to be yellow\n" + "Example: yellow = ptColor().yellow()"), + PYTHON_METHOD_NOARGS(ptColor, brown, "Sets the color to be brown\n" + "Example: brown = ptColor().brown()"), + PYTHON_METHOD_NOARGS(ptColor, gray, "Sets the color to be gray\n" + "Example: gray = ptColor().gray()"), + PYTHON_METHOD_NOARGS(ptColor, orange, "Sets the color to be orange\n" + "Example: orange = ptColor().orange()"), + PYTHON_METHOD_NOARGS(ptColor, pink, "Sets the color to be pink\n" + "Example: pink = ptColor().pink()"), + PYTHON_METHOD_NOARGS(ptColor, darkbrown, "Sets the color to be darkbrown\n" + "Example: darkbrown = ptColor().darkbrown()"), + PYTHON_METHOD_NOARGS(ptColor, darkgreen, "Sets the color to be darkgreen\n" + "Example: darkgreen = ptColor().darkgreen()"), + PYTHON_METHOD_NOARGS(ptColor, darkpurple, "Sets the color to be darkpurple\n" + "Example: darkpurple = ptColor().darkpurple()"), + PYTHON_METHOD_NOARGS(ptColor, navyblue, "Sets the color to be navyblue\n" + "Example: navyblue = ptColor().navyblue()"), + PYTHON_METHOD_NOARGS(ptColor, maroon, "Sets the color to be maroon\n" + "Example: maroon = ptColor().maroon()"), + PYTHON_METHOD_NOARGS(ptColor, tan, "Sets the color to be tan\n" + "Example: tan = ptColor().tan()"), + PYTHON_METHOD_NOARGS(ptColor, slateblue, "Sets the color to be slateblue\n" + "Example: slateblue = ptColor().slateblue()"), + PYTHON_METHOD_NOARGS(ptColor, steelblue, "Sets the color to be steelblue\n" + "Example: steelblue = ptColor().steelblue()"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +#define ptColor_COMPARE PYTHON_NO_COMPARE +#define ptColor_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptColor_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptColor_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptColor_STR PYTHON_NO_STR +#define ptColor_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptColor) +#define ptColor_GETSET PYTHON_NO_GETSET +#define ptColor_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptColor, "Params: red=0, green=0, blue=0, alpha=0\nPlasma color class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptColor, pyColor) + +PyObject *pyColor::New(hsScalar red, hsScalar green, hsScalar blue, hsScalar alpha) +{ + ptColor *newObj = (ptColor*)ptColor_type.tp_new(&ptColor_type, NULL, NULL); + newObj->fThis->setRed(red); + newObj->fThis->setGreen(green); + newObj->fThis->setBlue(blue); + newObj->fThis->setAlpha(alpha); + return (PyObject*)newObj; +} + +PyObject *pyColor::New(const hsColorRGBA & color) +{ + ptColor *newObj = (ptColor*)ptColor_type.tp_new(&ptColor_type, NULL, NULL); + newObj->fThis->setColor(color); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptColor, pyColor) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptColor, pyColor) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyColor::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptColor); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.cpp new file mode 100644 index 00000000..96093a23 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.cpp @@ -0,0 +1,256 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyCritterBrain.h" +#include "pyGeometry3.h" + +#include "..\plAvatar\plAvBrainCritter.h" + +pyCritterBrain::pyCritterBrain(): fBrain(nil) {} + +hsBool pyCritterBrain::operator==(const pyCritterBrain& other) const +{ + // pointer compare + return fBrain == other.fBrain; +} + +void pyCritterBrain::AddReceiver(pyKey& newReceiver) +{ + if (!fBrain) + return; + fBrain->AddReceiver(newReceiver.getKey()); +} + +void pyCritterBrain::RemoveReceiver(pyKey& oldReceiver) +{ + if (!fBrain) + return; + fBrain->RemoveReceiver(oldReceiver.getKey()); +} + +void pyCritterBrain::LocallyControlled(bool local) +{ + if (!fBrain) + return; + fBrain->LocallyControlled(local); +} + +bool pyCritterBrain::LocallyControlled() const +{ + if (!fBrain) + return false; + return fBrain->LocallyControlled(); +} + +void pyCritterBrain::AddBehavior(const std::string& animationName, const std::string& behaviorName, bool loop /* = true */, + bool randomStartPos /* = true */, float fadeInLen /* = 2.f */, float fadeOutLen /* = 2.f */) +{ + if (!fBrain) + return; + fBrain->AddBehavior(animationName, behaviorName, loop, randomStartPos, fadeInLen, fadeOutLen); +} + +void pyCritterBrain::StartBehavior(const std::string& behaviorName, bool fade /* = true */) +{ + if (!fBrain) + return; + fBrain->StartBehavior(behaviorName, fade); +} + +bool pyCritterBrain::RunningBehavior(const std::string& behaviorName) const +{ + if (!fBrain) + return false; + return fBrain->RunningBehavior(behaviorName); +} + +std::string pyCritterBrain::BehaviorName(int behavior) const +{ + if (!fBrain) + return false; + return fBrain->BehaviorName(behavior); +} + +std::string pyCritterBrain::AnimationName(int behavior) const +{ + if (!fBrain) + return false; + return fBrain->AnimationName(behavior); +} + +int pyCritterBrain::CurBehavior() const +{ + if (!fBrain) + return false; + return fBrain->CurBehavior(); +} + +int pyCritterBrain::NextBehavior() const +{ + if (!fBrain) + return false; + return fBrain->NextBehavior(); +} + +std::string pyCritterBrain::IdleBehaviorName() const +{ + if (!fBrain) + return false; + return fBrain->IdleBehaviorName(); +} + +std::string pyCritterBrain::RunBehaviorName() const +{ + if (!fBrain) + return false; + return fBrain->RunBehaviorName(); +} + +void pyCritterBrain::GoToGoal(hsPoint3 newGoal, bool avoidingAvatars /* = false */) +{ + if (!fBrain) + return; + fBrain->GoToGoal(newGoal, avoidingAvatars); +} + +PyObject* pyCritterBrain::CurrentGoal() const +{ + if (!fBrain) + PYTHON_RETURN_NONE; + return pyPoint3::New(fBrain->CurrentGoal()); +} + +bool pyCritterBrain::AvoidingAvatars() const +{ + if (!fBrain) + return false; + return fBrain->AvoidingAvatars(); +} + +bool pyCritterBrain::AtGoal() const +{ + if (!fBrain) + return false; + return fBrain->AtGoal(); +} + +void pyCritterBrain::StopDistance(hsScalar stopDistance) +{ + if (!fBrain) + return; + fBrain->StopDistance(stopDistance); +} + +hsScalar pyCritterBrain::StopDistance() const +{ + if (!fBrain) + return 0; + return fBrain->StopDistance(); +} + +void pyCritterBrain::SightCone(hsScalar coneRad) +{ + if (!fBrain) + return; + fBrain->SightCone(coneRad); +} + +hsScalar pyCritterBrain::SightCone() const +{ + if (!fBrain) + return 0; + return fBrain->SightCone(); +} + +void pyCritterBrain::SightDistance(hsScalar sightDis) +{ + if (!fBrain) + return; + fBrain->SightDistance(sightDis); +} + +hsScalar pyCritterBrain::SightDistance() const +{ + if (!fBrain) + return 0; + return fBrain->SightDistance(); +} + +void pyCritterBrain::HearingDistance(hsScalar hearDis) +{ + if (!fBrain) + return; + fBrain->HearingDistance(hearDis); +} + +hsScalar pyCritterBrain::HearingDistance() const +{ + if (!fBrain) + return 0; + return fBrain->HearingDistance(); +} + +bool pyCritterBrain::CanSeeAvatar(unsigned long id) const +{ + if (!fBrain) + return false; + return fBrain->CanSeeAvatar(id); +} + +bool pyCritterBrain::CanHearAvatar(unsigned long id) const +{ + if (!fBrain) + return false; + return fBrain->CanHearAvatar(id); +} + +PyObject* pyCritterBrain::PlayersICanSee() const +{ + if (!fBrain) + PYTHON_RETURN_NONE; + std::vector players = fBrain->PlayersICanSee(); + PyObject* retVal = PyList_New(players.size()); + for (unsigned i = 0; i < players.size(); ++i) + PyList_SetItem(retVal, i, PyLong_FromUnsignedLong(players[i])); + return retVal; +} + +PyObject* pyCritterBrain::PlayersICanHear() const +{ + if (!fBrain) + PYTHON_RETURN_NONE; + std::vector players = fBrain->PlayersICanHear(); + PyObject* retVal = PyList_New(players.size()); + for (unsigned i = 0; i < players.size(); ++i) + PyList_SetItem(retVal, i, PyLong_FromUnsignedLong(players[i])); + return retVal; +} + +PyObject* pyCritterBrain::VectorToPlayer(unsigned long id) const +{ + if (!fBrain) + PYTHON_RETURN_NONE; + return pyVector3::New(fBrain->VectorToPlayer(id)); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.h new file mode 100644 index 00000000..4c137d57 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrain.h @@ -0,0 +1,110 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyCritterBrain_h_ +#define _pyCritterBrain_h_ + +#include +#include "pyGlueHelpers.h" + +#include "pyKey.h" + +#include +#include "hsGeometry3.h" + +class plAvBrainCritter; + +// simply here so we can pass our message types on to python +class pyAIMsg +{ +public: + static void AddPlasmaConstantsClasses(PyObject *m); +}; + +// python glue class for the critter brain +class pyCritterBrain +{ +private: + plAvBrainCritter* fBrain; + +protected: + pyCritterBrain(); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptCritterBrain); + static PyObject* New(plAvBrainCritter* brain); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyCritterBrain object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyCritterBrain); // converts a PyObject to a pyCritterBrain (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + hsBool operator==(const pyCritterBrain& other) const; + hsBool operator!=(const pyCritterBrain& other) const {return !(*this == other);} + + void AddReceiver(pyKey& newReceiver); + void RemoveReceiver(pyKey& oldReceiver); + + void LocallyControlled(bool local); + bool LocallyControlled() const; + + void AddBehavior(const std::string& animationName, const std::string& behaviorName, bool loop = true, bool randomStartPos = true, + float fadeInLen = 2.f, float fadeOutLen = 2.f); + void StartBehavior(const std::string& behaviorName, bool fade = true); + bool RunningBehavior(const std::string& behaviorName) const; + + std::string BehaviorName(int behavior) const; + std::string AnimationName(int behavior) const; + int CurBehavior() const; + int NextBehavior() const; + + std::string IdleBehaviorName() const; + std::string RunBehaviorName() const; + + void GoToGoal(hsPoint3 newGoal, bool avoidingAvatars = false); + PyObject* CurrentGoal() const; // returns ptPoint3 + bool AvoidingAvatars() const; + bool AtGoal() const; + + void StopDistance(hsScalar stopDistance); + hsScalar StopDistance() const; + + void SightCone(hsScalar coneRad); + hsScalar SightCone() const; + void SightDistance(hsScalar sightDis); + hsScalar SightDistance() const; + void HearingDistance(hsScalar hearDis); + hsScalar HearingDistance() const; + + bool CanSeeAvatar(unsigned long id) const; + bool CanHearAvatar(unsigned long id) const; + + PyObject* PlayersICanSee() const; // returns list of unsigned longs + PyObject* PlayersICanHear() const; // returns list of unsigned longs + + PyObject* VectorToPlayer(unsigned long id) const; // returns ptVector3 +}; + +#endif // _pyCritterBrain_h_ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrainGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrainGlue.cpp new file mode 100644 index 00000000..0ef069ef --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyCritterBrainGlue.cpp @@ -0,0 +1,527 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyCritterBrain.h" +#include "pyEnum.h" +#include "pyGeometry3.h" + +#include "..\plMessage\plAIMsg.h" + +#include + +/////////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaConstantsClasses +// +void pyAIMsg::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtAIMsgType); + PYTHON_ENUM_ELEMENT(PtAIMsgType, kUnknown, plAIMsg::kAIMsg_Unknown); + PYTHON_ENUM_ELEMENT(PtAIMsgType, kBrainCreated, plAIMsg::kAIMsg_BrainCreated); + PYTHON_ENUM_ELEMENT(PtAIMsgType, kArrivedAtGoal, plAIMsg::kAIMsg_ArrivedAtGoal); + PYTHON_ENUM_END(m, PtAIMsgType); +} + +/////////////////////////////////////////////////////////////////////////////// + +// glue functions +PYTHON_CLASS_DEFINITION(ptCritterBrain, pyCritterBrain); + +PYTHON_DEFAULT_NEW_DEFINITION(ptCritterBrain, pyCritterBrain) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptCritterBrain) + +PYTHON_NO_INIT_DEFINITION(ptCritterBrain) + +PYTHON_RICH_COMPARE_DEFINITION(ptCritterBrain, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pyCritterBrain::Check(obj1) || !pyCritterBrain::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptCritterBrain object"); + PYTHON_RCOMPARE_ERROR; + } + } + pyCritterBrain* brain1 = pyCritterBrain::ConvertFrom(obj1); + pyCritterBrain* brain2 = pyCritterBrain::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*brain1) == (*brain2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*brain1) != (*brain2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptCritterBrain object"); + PYTHON_RCOMPARE_ERROR; +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, addReceiver, args) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "addReceiver expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "addReceiver expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->AddReceiver(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, removeReceiver, args) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "removeReceiver expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "removeReceiver expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->RemoveReceiver(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, setLocallyControlled, args) +{ + char local; + if (!PyArg_ParseTuple(args, "b", &local)) + { + PyErr_SetString(PyExc_TypeError, "setLocallyControlled expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->LocallyControlled(local != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, getLocallyControlled) +{ + PYTHON_RETURN_BOOL(self->fThis->LocallyControlled()); +} + +PYTHON_METHOD_DEFINITION_WKEY(ptCritterBrain, addBehavior, args, keywords) +{ + char* kwlist[] = {"animationName", "behaviorName", "loop", "randomStartPos", "fadeInLen", "fadeOutLen", NULL}; + PyObject* animNameObj = NULL; + PyObject* behNameObj = NULL; + char loop = 1, randomStartPos = 1; + float fadeInLen = 2.f, fadeOutLen = 2.f; + if (!PyArg_ParseTupleAndKeywords(args, keywords, "OO|bbff", kwlist, &animNameObj, &behNameObj, &loop, &randomStartPos, &fadeInLen, &fadeOutLen)) + { + PyErr_SetString(PyExc_TypeError, "addBehavior expects two strings, and optionally two booleans and two floats"); + PYTHON_RETURN_ERROR; + } + + std::string animName = ""; + if (PyUnicode_Check(animNameObj)) + { + int strLen = PyUnicode_GetSize(animNameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)animNameObj, text, strLen); + text[strLen] = L'\0'; + char* cText = hsWStringToString(text); + animName = cText; + delete [] cText; + delete [] text; + } + else if (PyString_Check(animNameObj)) + animName = PyString_AsString(animNameObj); + else + { + PyErr_SetString(PyExc_TypeError, "addBehavior expects two strings, and optionally two booleans and two floats"); + PYTHON_RETURN_ERROR; + } + + std::string behName = ""; + if (PyUnicode_Check(behNameObj)) + { + int strLen = PyUnicode_GetSize(behNameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)behNameObj, text, strLen); + text[strLen] = L'\0'; + char* cText = hsWStringToString(text); + behName = cText; + delete [] cText; + delete [] text; + } + else if (PyString_Check(behNameObj)) + behName = PyString_AsString(behNameObj); + else + { + PyErr_SetString(PyExc_TypeError, "addBehavior expects two strings, and optionally two booleans and two floats"); + PYTHON_RETURN_ERROR; + } + + self->fThis->AddBehavior(animName, behName, loop != 0, randomStartPos != 0, fadeInLen, fadeOutLen); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_WKEY(ptCritterBrain, startBehavior, args, keywords) +{ + char* kwlist[] = {"behaviorName", "fade", NULL}; + PyObject* behNameObj = NULL; + char fade = 1; + if (!PyArg_ParseTupleAndKeywords(args, keywords, "O|b", kwlist, &behNameObj, &fade)) + { + PyErr_SetString(PyExc_TypeError, "startBehavior expects a string, and an optional boolean"); + PYTHON_RETURN_NONE; + } + + std::string behName = ""; + if (PyUnicode_Check(behNameObj)) + { + int strLen = PyUnicode_GetSize(behNameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)behNameObj, text, strLen); + text[strLen] = L'\0'; + char* cText = hsWStringToString(text); + behName = cText; + delete [] cText; + delete [] text; + } + else if (PyString_Check(behNameObj)) + behName = PyString_AsString(behNameObj); + else + { + PyErr_SetString(PyExc_TypeError, "startBehavior expects a string, and an optional boolean"); + PYTHON_RETURN_ERROR; + } + + self->fThis->StartBehavior(behName, fade != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, runningBehavior, args) +{ + PyObject* behNameObj = NULL; + if (!PyArg_ParseTuple(args, "O", &behNameObj)) + { + PyErr_SetString(PyExc_TypeError, "runningBehavior expects a string"); + PYTHON_RETURN_ERROR; + } + + if (PyUnicode_Check(behNameObj)) + { + int strLen = PyUnicode_GetSize(behNameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)behNameObj, text, strLen); + text[strLen] = L'\0'; + char* cText = hsWStringToString(text); + bool retVal = self->fThis->RunningBehavior(cText); + delete [] cText; + delete [] text; + PYTHON_RETURN_BOOL(retVal); + } + else if (PyString_Check(behNameObj)) + { + bool retVal = self->fThis->RunningBehavior(PyString_AsString(behNameObj)); + PYTHON_RETURN_BOOL(retVal); + } + else + { + PyErr_SetString(PyExc_TypeError, "runningBehavior expects a string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, behaviorName, args) +{ + int behavior; + if (!PyArg_ParseTuple(args, "i", &behavior)) + { + PyErr_SetString(PyExc_TypeError, "behaviorName expects an integer"); + PYTHON_RETURN_ERROR; + } + return PyString_FromString(self->fThis->BehaviorName(behavior).c_str()); +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, animationName, args) +{ + int behavior; + if (!PyArg_ParseTuple(args, "i", &behavior)) + { + PyErr_SetString(PyExc_TypeError, "animationName expects an integer"); + PYTHON_RETURN_ERROR; + } + return PyString_FromString(self->fThis->AnimationName(behavior).c_str()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, curBehavior) +{ + return PyInt_FromLong(self->fThis->CurBehavior()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, nextBehavior) +{ + return PyInt_FromLong(self->fThis->NextBehavior()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, idleBehaviorName) +{ + return PyString_FromString(self->fThis->IdleBehaviorName().c_str()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, runBehaviorName) +{ + return PyString_FromString(self->fThis->RunBehaviorName().c_str()); +} + +PYTHON_METHOD_DEFINITION_WKEY(ptCritterBrain, goToGoal, args, keywords) +{ + char* kwlist[] = {"newGoal", "avoidingAvatars", NULL}; + PyObject* goalObj = NULL; + char avoidingAvatars = 0; + if (!PyArg_ParseTupleAndKeywords(args, keywords, "O|b", kwlist, &goalObj, &avoidingAvatars)) + { + PyErr_SetString(PyExc_TypeError, "goToGoal expects a ptPoint and an optional boolean."); + PYTHON_RETURN_ERROR; + } + + if (!pyPoint3::Check(goalObj)) + { + PyErr_SetString(PyExc_TypeError, "goToGoal expects a ptPoint and an optional boolean."); + PYTHON_RETURN_ERROR; + } + self->fThis->GoToGoal(pyPoint3::ConvertFrom(goalObj)->fPoint, avoidingAvatars != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, currentGoal) +{ + return self->fThis->CurrentGoal(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, avoidingAvatars) +{ + PYTHON_RETURN_BOOL(self->fThis->AvoidingAvatars()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, atGoal) +{ + PYTHON_RETURN_BOOL(self->fThis->AtGoal()); +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, setStopDistance, args) +{ + float dist; + if (!PyArg_ParseTuple(args, "f", &dist)) + { + PyErr_SetString(PyExc_TypeError, "setStopDistance expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->StopDistance(dist); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, getStopDistance) +{ + return PyFloat_FromDouble(self->fThis->StopDistance()); +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, setSightCone, args) +{ + float dist; + if (!PyArg_ParseTuple(args, "f", &dist)) + { + PyErr_SetString(PyExc_TypeError, "setSightCone expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SightCone(dist); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, getSightCone) +{ + return PyFloat_FromDouble(self->fThis->SightCone()); +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, setSightDistance, args) +{ + float dist; + if (!PyArg_ParseTuple(args, "f", &dist)) + { + PyErr_SetString(PyExc_TypeError, "setSightDistance expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SightDistance(dist); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, getSightDistance) +{ + return PyFloat_FromDouble(self->fThis->SightDistance()); +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, setHearingDistance, args) +{ + float dist; + if (!PyArg_ParseTuple(args, "f", &dist)) + { + PyErr_SetString(PyExc_TypeError, "setHearingDistance expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->HearingDistance(dist); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, getHearingDistance) +{ + return PyFloat_FromDouble(self->fThis->HearingDistance()); +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, canSeeAvatar, args) +{ + unsigned long id; + if (!PyArg_ParseTuple(args, "k", &id)) + { + PyErr_SetString(PyExc_TypeError, "canSeeAvatar expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->CanSeeAvatar(id)); +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, canHearAvatar, args) +{ + unsigned long id; + if (!PyArg_ParseTuple(args, "k", &id)) + { + PyErr_SetString(PyExc_TypeError, "canHearAvatar expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->CanHearAvatar(id)); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, playersICanSee) +{ + return self->fThis->PlayersICanSee(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptCritterBrain, playersICanHear) +{ + return self->fThis->PlayersICanHear(); +} + +PYTHON_METHOD_DEFINITION(ptCritterBrain, vectorToPlayer, args) +{ + unsigned long id; + if (!PyArg_ParseTuple(args, "k", &id)) + { + PyErr_SetString(PyExc_TypeError, "vectorToPlayer expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return self->fThis->VectorToPlayer(id); +} + +PYTHON_START_METHODS_TABLE(ptCritterBrain) + PYTHON_METHOD(ptCritterBrain, addReceiver, "Params: key\nTells the brain that the specified key wants AI messages"), + PYTHON_METHOD(ptCritterBrain, removeReceiver, "Params: key\nTells the brain that the specified key no longer wants AI messages"), + PYTHON_METHOD(ptCritterBrain, setLocallyControlled, "Params: local\nTells the brain that we are the ones making all the AI decisions, and to prop location " + "and other information to the server."), + PYTHON_METHOD_NOARGS(ptCritterBrain, getLocallyControlled, "Are we the one making AI decisions? NOTE: Not set automatically, some python script needs to " + "tell the brain this using setLocallyControlled()."), + PYTHON_METHOD_WKEY(ptCritterBrain, addBehavior, "Params: animName, behaviorName, loop = 1, randomStartPos = 1, fadeInLen = 2.0, fadeOutLen = 2.0\n" + "Adds a new animation to the brain as a behavior with the specified name and parameters. If multiple animations are assigned to the same behavior, " + "they will be randomly picked from when started."), + PYTHON_METHOD_WKEY(ptCritterBrain, startBehavior, "Params: behaviorName, fade = 1\nStarts playing the named behavior. If fade is true, it will fade out " + "the previous behavior and fade in the new one. If false, they will immediately switch."), + PYTHON_METHOD(ptCritterBrain, runningBehavior, "Params: behaviorName\nReturns true if the named behavior is running."), + PYTHON_METHOD(ptCritterBrain, behaviorName, "Params: behavior\nReturns the behavior name associated with the specified integral behavior."), + PYTHON_METHOD(ptCritterBrain, animationName, "Params: behavior\nReturns the animation name associated with the specified integral behavior."), + PYTHON_METHOD_NOARGS(ptCritterBrain, curBehavior, "Returns the current integral behavior the brain is running."), + PYTHON_METHOD_NOARGS(ptCritterBrain, nextBehavior, "Returns the behavior the brain will be switching to next frame. (-1 if no change)"), + PYTHON_METHOD_NOARGS(ptCritterBrain, idleBehaviorName, "Returns the name of the brain's idle behavior."), + PYTHON_METHOD_NOARGS(ptCritterBrain, runBehaviorName, "Returns the name of the brain's run behavior."), + PYTHON_METHOD_WKEY(ptCritterBrain, goToGoal, "Params: newGoal, avoidingAvatars = 0\nTells the brain to start running towards the specified location, " + "avoiding avatars it can see or hear if told to."), + PYTHON_METHOD_NOARGS(ptCritterBrain, currentGoal, "Returns the current ptPoint that the brain is running towards."), + PYTHON_METHOD_NOARGS(ptCritterBrain, avoidingAvatars, "Are we currently avoiding avatars while pathfinding?"), + PYTHON_METHOD_NOARGS(ptCritterBrain, atGoal, "Are we currently are our final destination?"), + PYTHON_METHOD(ptCritterBrain, setStopDistance, "Params: dist\nSet how far away from the goal we should be when we are considered there and stop running."), + PYTHON_METHOD_NOARGS(ptCritterBrain, getStopDistance, "Returns how far away from the goal we could be and still be considered there."), + PYTHON_METHOD(ptCritterBrain, setSightCone, "Params: radians\nSet how wide the brain's field of view is in radians. Note that it is the total angle of the " + "cone, half on one side of the brain's line of sight, half on the other."), + PYTHON_METHOD_NOARGS(ptCritterBrain, getSightCone, "Returns the width of the brain's field of view in radians."), + PYTHON_METHOD(ptCritterBrain, setSightDistance, "Params: dist\nSet how far away the brain can see."), + PYTHON_METHOD_NOARGS(ptCritterBrain, getSightDistance, "Returns how far the brain can see."), + PYTHON_METHOD(ptCritterBrain, setHearingDistance, "Params: dist\nSet how far away the brain can hear (360 degree field of hearing)."), + PYTHON_METHOD_NOARGS(ptCritterBrain, getHearingDistance, "Returns how far away the brain can hear."), + PYTHON_METHOD(ptCritterBrain, canSeeAvatar, "Params: avatarID\nReturns whether this brain can see the avatar with the specified id."), + PYTHON_METHOD(ptCritterBrain, canHearAvatar, "Params: avatarID\nReturns whether this brain can hear the avatar with the specified id."), + PYTHON_METHOD_NOARGS(ptCritterBrain, playersICanSee, "Returns a list of player ids which this brain can see."), + PYTHON_METHOD_NOARGS(ptCritterBrain, playersICanHear, "Returns a list of player ids which this brain can hear."), + PYTHON_METHOD(ptCritterBrain, vectorToPlayer, "Params: avatarID\nReturns the vector between us and the specified player."), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +#define ptCritterBrain_COMPARE PYTHON_NO_COMPARE +#define ptCritterBrain_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptCritterBrain_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptCritterBrain_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptCritterBrain_STR PYTHON_NO_STR +#define ptCritterBrain_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptCritterBrain) +#define ptCritterBrain_GETSET PYTHON_NO_GETSET +#define ptCritterBrain_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptCritterBrain, "Object to manipulate critter brains"); + +// required functions for PyObject interoperability +PyObject* pyCritterBrain::New(plAvBrainCritter* brain) +{ + ptCritterBrain *newObj = (ptCritterBrain*)ptCritterBrain_type.tp_new(&ptCritterBrain_type, NULL, NULL); + newObj->fThis->fBrain = brain; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptCritterBrain, pyCritterBrain) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptCritterBrain, pyCritterBrain) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyCritterBrain::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptCritterBrain); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinates.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinates.cpp new file mode 100644 index 00000000..fed6f7c2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinates.cpp @@ -0,0 +1,112 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyDniCoordinates.h" +#include "../plVault/plDniCoordinateInfo.h" +#ifndef BUILDING_PYPLASMA +#include "../plAvatar/plAvatarMgr.h" +#include "pyVault.h" +#endif + +pyDniCoordinates::pyDniCoordinates(plDniCoordinateInfo* coord) +{ + fCoords = TRACKED_NEW plDniCoordinateInfo; + if (coord) { + // copy their coords into our copy + fCoords->SetTorans(coord->GetTorans()); + fCoords->SetHSpans(coord->GetHSpans()); + fCoords->SetVSpans(coord->GetVSpans()); + } +} + + +pyDniCoordinates::pyDniCoordinates() +{ + fCoords = TRACKED_NEW plDniCoordinateInfo; + fCoords->SetTorans(0); + fCoords->SetHSpans(0); + fCoords->SetVSpans(0); +} + +pyDniCoordinates::~pyDniCoordinates() +{ + if (fCoords) + delete(fCoords); +} + +int pyDniCoordinates::GetHSpans() const +{ + if ( fCoords ) + { + return fCoords->GetHSpans(); + } + return 0; +} + +int pyDniCoordinates::GetVSpans() const +{ + if ( fCoords ) + { + return fCoords->GetVSpans(); + } + return 0; +} + +int pyDniCoordinates::GetTorans() const +{ + if ( fCoords ) + { + return fCoords->GetTorans(); + } + return 0; +} + +void pyDniCoordinates::UpdateCoordinates() +{ +#ifndef BUILDING_PYPLASMA + if (fCoords) + { + // reset GPS to 0,0,0 in case there is no maintainers marker in this age + fCoords->SetTorans(0); + fCoords->SetHSpans(0); + fCoords->SetVSpans(0); + plAvatarMgr::GetInstance()->GetDniCoordinate(fCoords); + } +#endif +} + +void pyDniCoordinates::FromPoint(const hsPoint3& pt) +{ +#ifndef BUILDING_PYPLASMA + if (fCoords) + { + // reset GPS to 0,0,0 in case there is no maintainers marker in this age + fCoords->SetTorans(0); + fCoords->SetHSpans(0); + fCoords->SetVSpans(0); + plAvatarMgr::GetInstance()->PointToDniCoordinate(pt, fCoords); + } +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinates.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinates.h new file mode 100644 index 00000000..25b6120a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinates.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyDniCoordinates_h_ +#define _pyDniCoordinates_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyDniCoordinates - the wrapper class for D'ni coordinates system +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsGeometry3.h" + +#include +#include "pyGlueHelpers.h" + +class plDniCoordinateInfo; + +class pyDniCoordinates +{ +private: + plDniCoordinateInfo* fCoords; + +protected: + pyDniCoordinates(); + pyDniCoordinates(plDniCoordinateInfo* coord); + +public: + ~pyDniCoordinates(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptDniCoordinates); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(plDniCoordinateInfo* coord); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyDniCoordinates object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyDniCoordinates); // converts a PyObject to a pyDniCoordinates (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // python get attributes helpers + int GetHSpans() const; + int GetVSpans() const; + int GetTorans() const; + void UpdateCoordinates(); + void FromPoint(const hsPoint3& pt); +}; + + +#endif // _pyDniCoordinates_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinatesGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinatesGlue.cpp new file mode 100644 index 00000000..149a2790 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniCoordinatesGlue.cpp @@ -0,0 +1,115 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyDniCoordinates.h" +#include "pyGeometry3.h" +#include "../plVault/plDniCoordinateInfo.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptDniCoordinates, pyDniCoordinates); + +PYTHON_DEFAULT_NEW_DEFINITION(ptDniCoordinates, pyDniCoordinates) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptDniCoordinates) + +PYTHON_INIT_DEFINITION(ptDniCoordinates, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptDniCoordinates, getHSpans) +{ + return PyInt_FromLong(self->fThis->GetHSpans()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptDniCoordinates, getVSpans) +{ + return PyInt_FromLong(self->fThis->GetVSpans()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptDniCoordinates, getTorans) +{ + return PyInt_FromLong(self->fThis->GetTorans()); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptDniCoordinates, update, UpdateCoordinates) + +PYTHON_METHOD_DEFINITION(ptDniCoordinates, fromPoint, args) +{ + PyObject* pointObj = NULL; + if (!PyArg_ParseTuple(args, "O", &pointObj)) + { + PyErr_SetString(PyExc_TypeError, "fromPoint expects a ptPoint3"); + PYTHON_RETURN_ERROR; + } + if (pyPoint3::Check(pointObj)) + { + pyPoint3* pos = pyPoint3::ConvertFrom(pointObj); + self->fThis->FromPoint(pos->fPoint); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "fromPoint expects a ptPoint3"); + PYTHON_RETURN_ERROR; +} + +PYTHON_START_METHODS_TABLE(ptDniCoordinates) + PYTHON_METHOD_NOARGS(ptDniCoordinates, getHSpans, "Returns the HSpans component of the coordinate"), + PYTHON_METHOD_NOARGS(ptDniCoordinates, getVSpans, "Returns the VSpans component of the coordinate"), + PYTHON_METHOD_NOARGS(ptDniCoordinates, getTorans, "Returns the Torans component of the coordinate"), + PYTHON_BASIC_METHOD(ptDniCoordinates, update, "Update these coordinates with the players current position"), + PYTHON_METHOD(ptDniCoordinates, fromPoint, "Params: pt\nUpdate these coordinates with the specified ptPoint3"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptDniCoordinates, "Constructor for a D'Ni coordinate"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptDniCoordinates, pyDniCoordinates) + +PyObject *pyDniCoordinates::New(plDniCoordinateInfo* coord) +{ + ptDniCoordinates *newObj = (ptDniCoordinates*)ptDniCoordinates_type.tp_new(&ptDniCoordinates_type, NULL, NULL); + if (coord) { + newObj->fThis->fCoords->SetTorans(coord->GetTorans()); + newObj->fThis->fCoords->SetHSpans(coord->GetHSpans()); + newObj->fThis->fCoords->SetVSpans(coord->GetVSpans()); + } + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptDniCoordinates, pyDniCoordinates) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptDniCoordinates, pyDniCoordinates) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyDniCoordinates::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptDniCoordinates); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSource.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSource.cpp new file mode 100644 index 00000000..a4d378d5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSource.cpp @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../pnUtils/pnUtils.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "../plVault/plAgeInfoSource.h" +#include "../plVault/plVault.h" +#include "pyDniInfoSource.h" +#include "pyDniCoordinates.h" + +pyDniInfoSource::pyDniInfoSource() +: fAgeName(nil) +{} + +pyDniInfoSource::~pyDniInfoSource() { + FREE(fAgeName); +} + +PyObject* pyDniInfoSource::GetAgeCoords( void ) +{ +#if 0 // this may get retooled for another purpose someday... + const plDniCoordinateInfo * coords = plNetPlayerVNodeMgr::GetInstance()->GetAgeInfo()->GetAgeCoords(); + if (coords) + return pyDniCoordinates::New((plDniCoordinateInfo*)coords); +#endif + // just return a None object + PYTHON_RETURN_NONE; +} + +UInt32 pyDniInfoSource::GetAgeTime( void ) const +{ + RelVaultNode * node = VaultGetAgeInfoNodeIncRef(); + if (!node) + return 0; + + unsigned result; + VaultAgeInfoNode ageInfo(node); + if (const plUnifiedTime * utime = ageInfo.GetAgeTime()) + result = utime->GetSecs(); + else + result = 0; + node->DecRef(); + + return result; +} + +const char * pyDniInfoSource::GetAgeName( void ) const +{ + RelVaultNode * node = VaultGetAgeInfoNodeIncRef(); + if (!node) + return ""; + + VaultAgeInfoNode ageInfo(node); + + fAgeName = StrDupToAnsi(ageInfo.ageInstName); + node->DecRef(); + + return fAgeName; +} + +const char * pyDniInfoSource::GetAgeGuid( void ) const +{ + RelVaultNode * node = VaultGetAgeInfoNodeIncRef(); + if (!node) + return ""; + + VaultAgeInfoNode ageInfo(node); + + GuidToString(ageInfo.ageInstUuid, fAgeGuid, arrsize(fAgeGuid)); + node->DecRef(); + + return fAgeGuid; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSource.h new file mode 100644 index 00000000..19cfd86b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSource.h @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyDniInfoSource_h_inc +#define pyDniInfoSource_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" + + +#include +#include "pyGlueHelpers.h" + +class pyDniCoordinates; + +class pyDniInfoSource +{ +private: + mutable char * fAgeName; + mutable char fAgeGuid[MAX_PATH]; + +protected: + pyDniInfoSource(); + +public: + ~pyDniInfoSource(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptDniInfoSource); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyDniInfoSource object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyDniInfoSource); // converts a PyObject to a pyDniInfoSource (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // current coords of the player in current age as a pyDniCoordinates + PyObject* GetAgeCoords( void ); // returns pyDniCoordinates + // current time in current age (tbd) + UInt32 GetAgeTime( void ) const; + // name of current age + const char * GetAgeName( void ) const; + // unique identifier for this age instance + const char * GetAgeGuid( void ) const; +}; + + +#endif // pyDniInfoSource_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSourceGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSourceGlue.cpp new file mode 100644 index 00000000..a9de675c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDniInfoSourceGlue.cpp @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyDniInfoSource.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptDniInfoSource, pyDniInfoSource); + +PYTHON_DEFAULT_NEW_DEFINITION(ptDniInfoSource, pyDniInfoSource) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptDniInfoSource) + +PYTHON_INIT_DEFINITION(ptDniInfoSource, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptDniInfoSource, getAgeCoords) +{ + return self->fThis->GetAgeCoords(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptDniInfoSource, getAgeTime) +{ + return PyLong_FromUnsignedLong(self->fThis->GetAgeTime()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptDniInfoSource, getAgeName) +{ + return PyString_FromString(self->fThis->GetAgeName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptDniInfoSource, getAgeGuid) +{ + return PyString_FromString(self->fThis->GetAgeGuid()); +} + +PYTHON_START_METHODS_TABLE(ptDniInfoSource) + PYTHON_METHOD_NOARGS(ptDniInfoSource, getAgeCoords, "Current coords of the player in current age as a ptDniCoordinates"), + PYTHON_METHOD_NOARGS(ptDniInfoSource, getAgeTime, "Current time in current age (tbd)"), + PYTHON_METHOD_NOARGS(ptDniInfoSource, getAgeName, "Name of current age"), + PYTHON_METHOD_NOARGS(ptDniInfoSource, getAgeGuid, "Unique identifier for this age instance"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptDniInfoSource, "DO NOT USE"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptDniInfoSource, pyDniInfoSource) + +PYTHON_CLASS_CHECK_IMPL(ptDniInfoSource, pyDniInfoSource) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptDniInfoSource, pyDniInfoSource) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyDniInfoSource::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptDniInfoSource); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControl.cpp new file mode 100644 index 00000000..d69ee339 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControl.cpp @@ -0,0 +1,171 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyDrawControl - a wrapper class all the draw/pipeline control functions +// +////////////////////////////////////////////////////////////////////// + +#include "pyDrawControl.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" + + +#ifndef BUILDING_PYPLASMA +#include "../plMessage/plInputEventMsg.h" +#include "../pnMessage/plClientMsg.h" +#include "../plInputCore/plInputDevice.h" +#include "../plAvatar/plArmatureMod.h" +#endif + +void pyDrawControl::SetGamma2(hsScalar gamma) +{ +#ifndef BUILDING_PYPLASMA + char command[256]; + sprintf(command,"Graphics.Renderer.Gamma2 %f",gamma); + // create message to send to the console + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetBCastFlag(plMessage::kBCastByType); + pMsg->SetControlCode(B_CONTROL_CONSOLE_COMMAND); + pMsg->SetControlActivated(true); + pMsg->SetCmdString(command); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes +#endif +} + +#ifndef BUILDING_PYPLASMA +#include "../plGLight/plShadowMaster.h" +#endif + +void pyDrawControl::SetShadowVisDistance(hsScalar distance) +{ +#ifndef BUILDING_PYPLASMA + plShadowMaster::SetGlobalShadowQuality(distance); +#endif +} + +hsScalar pyDrawControl::GetShadowVisDistance() +{ +#ifndef BUILDING_PYPLASMA + return plShadowMaster::GetGlobalShadowQuality(); +#else + return 1.0; +#endif +} + +#ifndef BUILDING_PYPLASMA +#include "../plGLight/plShadowCaster.h" +#endif + +void pyDrawControl::EnableShadows() +{ +#ifndef BUILDING_PYPLASMA + plShadowCaster::EnableShadowCast(); +#endif +} +void pyDrawControl::DisableShadows() +{ +#ifndef BUILDING_PYPLASMA + plShadowCaster::DisableShadowCast(); +#endif +} +hsBool pyDrawControl::IsShadowsEnabled() +{ +#ifndef BUILDING_PYPLASMA + return !plShadowCaster::ShadowCastDisabled(); +#else + return true; +#endif +} + +hsBool pyDrawControl::CanShadowCast() +{ +#ifndef BUILDING_PYPLASMA + return plShadowCaster::CanShadowCast(); +#else + return false; +#endif +} + +void pyDrawControl::DisableRenderScene() +{ +#ifndef BUILDING_PYPLASMA + plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY ); + plClientMsg* msg = TRACKED_NEW plClientMsg(plClientMsg::kDisableRenderScene); + msg->AddReceiver( clientKey ); + msg->Send(); +#endif +} + +void pyDrawControl::EnableRenderScene() +{ +#ifndef BUILDING_PYPLASMA + plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY ); + plClientMsg* msg = TRACKED_NEW plClientMsg(plClientMsg::kEnableRenderScene); + msg->AddReceiver( clientKey ); + msg->Send(); +#endif +} + +void pyDrawControl::SetMouseInverted() +{ +#ifndef BUILDING_PYPLASMA + plMouseDevice::SetInverted(true); +#endif +} + +void pyDrawControl::SetMouseUninverted() +{ +#ifndef BUILDING_PYPLASMA + plMouseDevice::SetInverted(false); +#endif +} + +hsBool pyDrawControl::IsMouseInverted() +{ +#ifndef BUILDING_PYPLASMA + return plMouseDevice::GetInverted(); +#else + return false; +#endif +} + +void pyDrawControl::SetClickToTurn(hsBool state) +{ +#ifndef BUILDING_PYPLASMA + plArmatureMod::fClickToTurn = state; +#endif +} + +hsBool pyDrawControl::IsClickToTurn() +{ +#ifndef BUILDING_PYPLASMA + if (plArmatureMod::fClickToTurn) + return true; +#endif + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControl.h new file mode 100644 index 00000000..ce951d10 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControl.h @@ -0,0 +1,71 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyDrawControl_h_ +#define _pyDrawControl_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyDrawControl - a wrapper class all the draw/pipeline control functions +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + + +#include +#include "pyGlueHelpers.h" + +class pyDrawControl +{ +protected: + pyDrawControl() {}; + +public: + static void AddPlasmaMethods(std::vector &methods); + //static void AddPlasmaConstantsClasses(PyObject* m); + + // static python functions + static void SetGamma2(hsScalar gamma); + static void SetShadowVisDistance(hsScalar distance); + static hsScalar GetShadowVisDistance(); + static void EnableShadows(); + static void DisableShadows(); + static hsBool IsShadowsEnabled(); + static hsBool CanShadowCast(); + + static void DisableRenderScene(); + static void EnableRenderScene(); + + static void SetMouseInverted(); + static void SetMouseUninverted(); + static hsBool IsMouseInverted(); + + static void SetClickToTurn(hsBool state); + static hsBool IsClickToTurn(); +}; + +#endif // _pyDrawControl_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControlGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControlGlue.cpp new file mode 100644 index 00000000..9fa99079 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDrawControlGlue.cpp @@ -0,0 +1,130 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyDrawControl.h" +#include "pyEnum.h" + +#include + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetGamma2, args, "Params: gamma\nSet the gamma with gamma2 rules") +{ + float gamma; + if (!PyArg_ParseTuple(args, "f", &gamma)) + { + PyErr_SetString(PyExc_TypeError, "PtSetGamma2 expects a float"); + PYTHON_RETURN_ERROR; + } + pyDrawControl::SetGamma2(gamma); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetShadowVisDistance, args, "Params: distance\nSet the maximum shadow visibility distance") +{ + float distance; + if (!PyArg_ParseTuple(args, "f", &distance)) + { + PyErr_SetString(PyExc_TypeError, "PtSetShadowVisDistance expects a float"); + PYTHON_RETURN_ERROR; + } + pyDrawControl::SetShadowVisDistance(distance); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetShadowVisDistance, "Returns the maximum shadow visibility distance") +{ + return PyFloat_FromDouble(pyDrawControl::GetShadowVisDistance()); +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtEnableShadows, pyDrawControl::EnableShadows, "Turns shadows on") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtDisableShadows, pyDrawControl::DisableShadows, "Turns shadows off") + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsShadowsEnabled, "Returns whether shadows are currently turned on") +{ + PYTHON_RETURN_BOOL(pyDrawControl::IsShadowsEnabled()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtCanShadowCast, "Can we cast shadows?") +{ + PYTHON_RETURN_BOOL(pyDrawControl::CanShadowCast()); +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtDisableRenderScene, pyDrawControl::DisableRenderScene, "UNKNOWN") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtEnableRenderScene, pyDrawControl::EnableRenderScene, "UNKNOWN") + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtSetMouseInverted, pyDrawControl::SetMouseInverted, "Inverts the mouse") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtSetMouseUninverted, pyDrawControl::SetMouseUninverted, "Uninverts the mouse") + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsMouseInverted, "Is the mouse currently inverted?") +{ + PYTHON_RETURN_BOOL(pyDrawControl::IsMouseInverted()); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtSetClickToTurn, args, "Params: state\nTurns on click-to-turn") +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "PtSetClickToTurn expects a boolean"); + PYTHON_RETURN_ERROR; + } + pyDrawControl::SetClickToTurn(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsClickToTurn, "Is click-to-turn on?") +{ + PYTHON_RETURN_BOOL(pyDrawControl::IsClickToTurn()); +} + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaMethods - the python method definitions +// + +void pyDrawControl::AddPlasmaMethods(std::vector &methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtSetGamma2); + PYTHON_GLOBAL_METHOD(methods, PtSetShadowVisDistance); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetShadowVisDistance); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtEnableShadows); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtDisableShadows); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsShadowsEnabled); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtCanShadowCast); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtDisableRenderScene); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtEnableRenderScene); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtSetMouseInverted); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtSetMouseUninverted); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsMouseInverted); + PYTHON_GLOBAL_METHOD(methods, PtSetClickToTurn); + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsClickToTurn); +} + +/*void pyDrawControl::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtMovieType); + PYTHON_ENUM_ELEMENT(PtMovieType, kUnknownTypeMovie, pyMoviePlayer::kUnknownTypeMovie); + PYTHON_ENUM_ELEMENT(PtMovieType, kBinkMovie, pyMoviePlayer::kBinkMovie); + PYTHON_ENUM_END; +}*/ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicText.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicText.cpp new file mode 100644 index 00000000..56bbcac5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicText.cpp @@ -0,0 +1,390 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDynamicTextMsg Header // +// Message wrapper for commands to plDynamicTextMap. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "plgDispatch.h" +#include "../plMessage/plDynamicTextMsg.h" +#include "pyKey.h" +#include "plPythonFileMod.h" +#include "pyColor.h" +#include "pyImage.h" +#include "../plGImage/plDynamicTextMap.h" + +#include "pyDynamicText.h" + +pyDynamicText::pyDynamicText() +{ + IInit(); +} + +pyDynamicText::pyDynamicText(pyKey& key) +{ + IInit(); + fReceivers.Append(key.getKey()); +} + +pyDynamicText::pyDynamicText(plKey key) +{ + IInit(); + fReceivers.Append(key); +} + + +void pyDynamicText::IInit() +{ + fSenderKey = nil; + fNetPropagate = false; + fNetForce = false; + fWrap = false; + fClip = false; +} + + +// methods that will be exposed to Python +void pyDynamicText::SetSender(pyKey& selfKey) +{ + fSenderKey = selfKey.getKey(); +} + +void pyDynamicText::ClearReceivers() +{ + fReceivers.Reset(); +} + +void pyDynamicText::AddReceiver(pyKey& key) +{ + fReceivers.Append(key.getKey()); +} + + +void pyDynamicText::SetNetPropagate(hsBool propagate) +{ + fNetPropagate = propagate; +} + +void pyDynamicText::SetNetForce(hsBool force) +{ + fNetForce = force; +} + +////////////////////////////////////////////// +// internal methods +////////////////////////////////////////////// + +plDynamicTextMsg* pyDynamicText::ICreateDTMsg() +{ + // must have a receiver! + if ( fReceivers.Count() > 0 ) + { + plDynamicTextMsg* pMsg = TRACKED_NEW plDynamicTextMsg; + if ( fSenderKey ) + pMsg->SetSender(fSenderKey); + if ( fNetPropagate ) + pMsg->SetBCastFlag(plMessage::kNetPropagate); + if ( fNetForce ) + pMsg->SetBCastFlag(plMessage::kNetForce); + // add all our receivers to the message receiver list + int i; + for ( i=0; iAddReceiver(fReceivers[i]); + } + + return pMsg; + } + else + return nil; +} + +////////////////////////////////////////////// +// dynamicText commands +////////////////////////////////////////////// + +// +// Need to determine whether to send over the network (netpropagate) or not +// + +void pyDynamicText::ClearToColor( pyColor& color ) +{ + // create message + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if ( pMsg ) + { + pMsg->ClearToColor(color.getColor()); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void pyDynamicText::Flush( void ) +{ + // create message + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if ( pMsg ) + { + pMsg->Flush(); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void pyDynamicText::PurgeImage( void ) +{ + // create message + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if ( pMsg ) + { + pMsg->PurgeImage(); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void pyDynamicText::SetTextColor( pyColor& color ) +{ + SetTextColor2(color,false); +} + +void pyDynamicText::SetTextColor2( pyColor& color, bool blockRGB ) +{ + // create message + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if ( pMsg ) + { + pMsg->SetTextColor(color.getColor(),blockRGB); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void pyDynamicText::SetFont( const char *facename, Int16 size ) +{ + // create message + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if ( pMsg ) + { + pMsg->SetFont(facename,size); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void pyDynamicText::FillRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, pyColor& color ) +{ + // create message + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if ( pMsg ) + { + pMsg->FillRect(left,top,right,bottom,color.getColor()); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void pyDynamicText::FrameRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, pyColor& color ) +{ + // create message + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if ( pMsg ) + { + pMsg->FrameRect(left,top,right,bottom,color.getColor()); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void pyDynamicText::SetClipping( UInt16 clipLeft, UInt16 clipTop, UInt16 clipRight, UInt16 clipBottom) +{ + fClip = true; + fClipLeft = clipLeft; + fClipTop = clipTop; + fClipRight = clipRight; + fClipBottom = clipBottom; +} + +void pyDynamicText::UnsetClipping() +{ + fClip = false; +} + +void pyDynamicText::SetWrapping( UInt16 wrapWidth, UInt16 wrapHeight ) +{ + fWrap = true; + fWrapWidth = wrapWidth; + fWrapHeight = wrapHeight; +} + +void pyDynamicText::UnsetWrapping() +{ + fWrap = false; +} + +// +// Draw text paying attention to Clipping and Wrapping if user wanted it +// +void pyDynamicText::DrawText( Int16 x, Int16 y, const char *text ) +{ + // create message + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if ( pMsg ) + { + // The priority is: + // 1) wrap (if you wrap you probably don't need to clip) + // 2) clip + // 3) just draw + if ( fWrap ) + pMsg->DrawWrappedString(x,y,fWrapWidth,fWrapHeight,text); + else if ( fClip ) + pMsg->DrawClippedString(x,y,fClipLeft,fClipTop,fClipRight,fClipBottom,text); + else + pMsg->DrawString(x,y,text); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void pyDynamicText::DrawTextW( Int16 x, Int16 y, std::wstring text ) +{ + // create message + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if ( pMsg ) + { + // The priority is: + // 1) wrap (if you wrap you probably don't need to clip) + // 2) clip + // 3) just draw + if ( fWrap ) + pMsg->DrawWrappedString(x,y,fWrapWidth,fWrapHeight,text.c_str()); + else if ( fClip ) + pMsg->DrawClippedString(x,y,fClipLeft,fClipTop,fClipRight,fClipBottom,text.c_str()); + else + pMsg->DrawString(x,y,text.c_str()); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +// +// Draw an image on the DynamicMap +// +void pyDynamicText::DrawImage( UInt16 x, UInt16 y, pyImage& image, hsBool respectAlpha ) +{ + // create message + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if ( pMsg ) + { + pMsg->DrawImage( x, y, image.GetKey(), respectAlpha); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +// +// Draw an image on the DynamicMap +// +void pyDynamicText::DrawImageClipped( UInt16 x, UInt16 y, pyImage& image, UInt16 cx, UInt16 cy, UInt16 cw, UInt16 ch, + hsBool respectAlpha ) +{ + // create message + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if ( pMsg ) + { + pMsg->DrawClippedImage( x, y, image.GetKey(), cx, cy, cw, ch, respectAlpha); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + + +UInt16 pyDynamicText::GetWidth( void ) +{ + // We better just pick our first key. Note that the ONLY time we should be getting multiple receivers + // is if the export process ends up creating multiple copies of the material. Now, WHY you'd be wanting + // to draw all of them is a question for wiser men, but they should all be the same size regardless + if( fReceivers.GetCount() == 0 ) + return 0; + + plDynamicTextMap *dtMap = plDynamicTextMap::ConvertNoRef( fReceivers[ 0 ]->ObjectIsLoaded() ); + if( dtMap == nil ) + return 0; + + return (UInt16)dtMap->GetWidth(); +} + +UInt16 pyDynamicText::GetHeight( void ) +{ + // We better just pick our first key. Note that the ONLY time we should be getting multiple receivers + // is if the export process ends up creating multiple copies of the material. Now, WHY you'd be wanting + // to draw all of them is a question for wiser men, but they should all be the same size regardless + if( fReceivers.GetCount() == 0 ) + return 0; + + plDynamicTextMap *dtMap = plDynamicTextMap::ConvertNoRef( fReceivers[ 0 ]->ObjectIsLoaded() ); + if( dtMap == nil ) + return 0; + + return (UInt16)dtMap->GetHeight(); +} + +void pyDynamicText::CalcTextExtents( std::wstring text, unsigned &width, unsigned &height ) +{ + width = 0; + height = 0; + + if (fReceivers.GetCount() == 0) + return; + + plDynamicTextMap* dtMap = plDynamicTextMap::ConvertNoRef(fReceivers[0]->ObjectIsLoaded()); + if (!dtMap) + return; + + width = dtMap->CalcStringWidth(text.c_str(), (UInt16*)&height); +} + +void pyDynamicText::SetJustify(UInt8 justify) +{ + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if (pMsg) + { + pMsg->SetJustify(justify); + plgDispatch::MsgSend(pMsg); + } +} + +void pyDynamicText::SetLineSpacing(Int16 spacing) +{ + plDynamicTextMsg* pMsg = ICreateDTMsg(); + if (pMsg) + { + pMsg->SetLineSpacing(spacing); + plgDispatch::MsgSend(pMsg); + } +} + +plKey pyDynamicText::GetImage() +{ + if (fReceivers.GetCount() > 0) + return fReceivers[0]; + else + return nil; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicText.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicText.h new file mode 100644 index 00000000..27975a8b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicText.h @@ -0,0 +1,127 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyDynamicText_h_ +#define _pyDynamicText_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyDynamicText - a wrapper class interface to plDynamicTextMsg +// +////////////////////////////////////////////////////////////////////// + +class plDynamicTextMsg; +class pyKey; +class pyColor; +class pyImage; + +#include "hsTemplates.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + + +class pyDynamicText +{ +private: + plKey fSenderKey; // the holder of the who (the modifier) we are + // the list of receivers that want to be notified + hsTArray fReceivers; + + hsBool fNetPropagate; + hsBool fNetForce; + + // clipping + hsBool fClip; + UInt16 fClipLeft; + UInt16 fClipTop; + UInt16 fClipRight; + UInt16 fClipBottom; + + // wrapping + hsBool fWrap; + UInt16 fWrapWidth; + UInt16 fWrapHeight; + + virtual plDynamicTextMsg* ICreateDTMsg(); + virtual void IInit(); + +protected: + pyDynamicText(); + pyDynamicText(pyKey& key); + pyDynamicText(plKey key); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptDynamicMap); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(pyKey& key); + static PyObject *New(plKey key); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyDynamicText object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyDynamicText); // converts a PyObject to a pyDynamicText (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaConstantsClasses(PyObject *m); + +// methods that will be exposed to Python + // message stuff + virtual void SetSender(pyKey& selfKey); + virtual void ClearReceivers(); + virtual void AddReceiver(pyKey& key); + virtual void SetNetPropagate(hsBool propagate); + virtual void SetNetForce(hsBool force); + + // dynamicText commands + virtual void ClearToColor( pyColor& color ); + virtual void Flush( void ); + virtual void SetTextColor( pyColor& color ); + virtual void SetTextColor2( pyColor& color, bool blockRGB ); + virtual void SetFont( const char *facename, Int16 size ); + virtual void FillRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, pyColor& color ); + virtual void FrameRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, pyColor& color ); + virtual void SetClipping( UInt16 clipLeft, UInt16 clipTop, UInt16 clipRight, UInt16 clipBottom); + virtual void UnsetClipping(); + virtual void SetWrapping( UInt16 wrapWidth, UInt16 wrapHeight ); + virtual void UnsetWrapping(); + virtual void DrawText( Int16 x, Int16 y, const char *text ); + virtual void DrawTextW( Int16 x, Int16 y, std::wstring text ); + virtual void DrawImage( UInt16 x, UInt16 y, pyImage& image, hsBool respectAlpha ); + virtual void DrawImageClipped( UInt16 x, UInt16 y, pyImage& image, UInt16 cx, UInt16 cy, UInt16 cw, UInt16 ch, + hsBool respectAlpha ); + virtual void PurgeImage( void ); + + // Actually return the visible width and height, since that's what you want to be drawing to + virtual UInt16 GetWidth( void ); + virtual UInt16 GetHeight( void ); + virtual void CalcTextExtents( std::wstring text, unsigned &width, unsigned &height ); + + virtual void SetJustify(UInt8 justify); + virtual void SetLineSpacing(Int16 spacing); + + virtual plKey GetImage(); +}; + +#endif // _pyDynamicText_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicTextGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicTextGlue.cpp new file mode 100644 index 00000000..a73124ff --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyDynamicTextGlue.cpp @@ -0,0 +1,478 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyKey.h" +#include "pyDynamicText.h" +#include "pyEnum.h" +#include "pyColor.h" +#include "pyImage.h" +#include "../plGImage/plDynamicTextMap.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptDynamicMap, pyDynamicText); + +PYTHON_DEFAULT_NEW_DEFINITION(ptDynamicMap, pyDynamicText) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptDynamicMap) + +PYTHON_INIT_DEFINITION(ptDynamicMap, args, keywords) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "|O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!keyObj) + PYTHON_RETURN_INIT_OK; // nothing to do + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->AddReceiver(*key); + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, sender, args) +{ + PyObject* senderObj = NULL; + if (!PyArg_ParseTuple(args, "O", &senderObj)) + { + PyErr_SetString(PyExc_TypeError, "sender expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(senderObj)) + { + PyErr_SetString(PyExc_TypeError, "sender expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* sender = pyKey::ConvertFrom(senderObj); + self->fThis->SetSender(*sender); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptDynamicMap, clearKeys, ClearReceivers) + +PYTHON_METHOD_DEFINITION(ptDynamicMap, addKey, args) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "addKey expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "addKey expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->AddReceiver(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, netPropagate, args) +{ + char propagateFlag; + if (!PyArg_ParseTuple(args, "b", &propagateFlag)) + { + PyErr_SetString(PyExc_TypeError, "netPropagate expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNetPropagate(propagateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, netForce, args) +{ + char forceFlag; + if (!PyArg_ParseTuple(args, "b", &forceFlag)) + { + PyErr_SetString(PyExc_TypeError, "netForce expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNetForce(forceFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, clearToColor, args) +{ + PyObject* colorObj = NULL; + if (!PyArg_ParseTuple(args, "O", &colorObj)) + { + PyErr_SetString(PyExc_TypeError, "clearToColor expects a ptColor"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "clearToColor expects a ptColor"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + self->fThis->ClearToColor(*color); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptDynamicMap, flush, Flush) +PYTHON_BASIC_METHOD_DEFINITION(ptDynamicMap, purgeImage, PurgeImage) + +PYTHON_METHOD_DEFINITION(ptDynamicMap, setTextColor, args) +{ + PyObject* colorObj = NULL; + char blockRGB = 0; + if (!PyArg_ParseTuple(args, "O|b", &colorObj, &blockRGB)) + { + PyErr_SetString(PyExc_TypeError, "setTextColor expects a ptColor and an optional boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "setTextColor expects a ptColor and an optional boolean"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + self->fThis->SetTextColor2(*color, blockRGB != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, setFont, args) +{ + char* faceName; + short fontSize; + if (!PyArg_ParseTuple(args, "sh", &faceName, &fontSize)) + { + PyErr_SetString(PyExc_TypeError, "setFont expects a string and a short int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetFont(faceName, fontSize); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, fillRect, args) +{ + unsigned short left, top, right, bottom; + PyObject* colorObj = NULL; + if (!PyArg_ParseTuple(args, "hhhhO", &left, &top, &right, &bottom, &colorObj)) + { + PyErr_SetString(PyExc_TypeError, "fillRect expects four unsigned short ints and a ptColor"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "fillRect expects four unsigned short ints and a ptColor"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + self->fThis->FillRect(left, top, right, bottom, *color); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, frameRect, args) +{ + unsigned short left, top, right, bottom; + PyObject* colorObj = NULL; + if (!PyArg_ParseTuple(args, "hhhhO", &left, &top, &right, &bottom, &colorObj)) + { + PyErr_SetString(PyExc_TypeError, "frameRect expects four unsigned short ints and a ptColor"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "frameRect expects four unsigned short ints and a ptColor"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + self->fThis->FrameRect(left, top, right, bottom, *color); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, setClipping, args) +{ + unsigned short left, top, right, bottom; + if (!PyArg_ParseTuple(args, "hhhh", &left, &top, &right, &bottom)) + { + PyErr_SetString(PyExc_TypeError, "setClipping expects four unsigned short ints"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetClipping(left, top, right, bottom); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptDynamicMap, unsetClipping, UnsetClipping) + +PYTHON_METHOD_DEFINITION(ptDynamicMap, setWrapping, args) +{ + unsigned short wrapWidth, wrapHeight; + if (!PyArg_ParseTuple(args, "hh", &wrapWidth, &wrapHeight)) + { + PyErr_SetString(PyExc_TypeError, "setWrapping expects two unsigned short ints"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetWrapping(wrapWidth, wrapHeight); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptDynamicMap, unsetWrapping, UnsetWrapping) + +PYTHON_METHOD_DEFINITION(ptDynamicMap, drawText, args) +{ + short x, y; + char* text; + if (!PyArg_ParseTuple(args, "hhs", &x, &y, &text)) + { + PyErr_SetString(PyExc_TypeError, "drawText expects two short ints and a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->DrawText(x, y, text); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, drawTextW, args) +{ + short x, y; + wchar_t* text; + if (!PyArg_ParseTuple(args, "hhu", &x, &y, &text)) + { + PyErr_SetString(PyExc_TypeError, "drawTextW expects two short ints and a unicode string"); + PYTHON_RETURN_ERROR; + } + self->fThis->DrawTextW(x, y, text); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, drawImage, args) +{ + short x, y; + PyObject* imageObj = NULL; + char respectAlpha; + if (!PyArg_ParseTuple(args, "hhOb", &x, &y, &imageObj, &respectAlpha)) + { + PyErr_SetString(PyExc_TypeError, "drawImage expects two shorts, a ptImage, and a boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyImage::Check(imageObj)) + { + PyErr_SetString(PyExc_TypeError, "drawImage expects two shorts, a ptImage, and a boolean"); + PYTHON_RETURN_ERROR; + } + pyImage* image = pyImage::ConvertFrom(imageObj); + self->fThis->DrawImage(x, y, *image, respectAlpha != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, drawImageClipped, args) +{ + unsigned short x, y; + PyObject* imageObj = NULL; + unsigned short cx, cy, cw, ch; + char respectAlpha; + if (!PyArg_ParseTuple(args, "hhOhhhhb", &x, &y, &imageObj, &cx, &cy, &cw, &ch, &respectAlpha)) + { + PyErr_SetString(PyExc_TypeError, "drawImageClipped expects two shorts, a ptImage, four shorts, and a boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyImage::Check(imageObj)) + { + PyErr_SetString(PyExc_TypeError, "drawImageClipped expects two shorts, a ptImage, four shorts, and a boolean"); + PYTHON_RETURN_ERROR; + } + pyImage* image = pyImage::ConvertFrom(imageObj); + self->fThis->DrawImageClipped(x, y, *image, cx, cy, cw, ch, respectAlpha != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptDynamicMap, getWidth) +{ + return PyInt_FromLong(self->fThis->GetWidth()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptDynamicMap, getHeight) +{ + return PyInt_FromLong(self->fThis->GetHeight()); +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, calcTextExtents, args) +{ + PyObject* textObj = NULL; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "calcTextExtents expects a string"); + PYTHON_RETURN_ERROR; + } + + std::wstring wText; + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + wText = text; + delete [] text; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(textObj); + wchar_t* temp = hsStringToWString(text); + wText = temp; + delete [] temp; + } + else + { + PyErr_SetString(PyExc_TypeError, "calcTextExtents expects a string"); + PYTHON_RETURN_ERROR; + } + + unsigned height, width; + self->fThis->CalcTextExtents(wText, width, height); + PyObject* retVal = PyTuple_New(2); + PyTuple_SetItem(retVal, 0, PyInt_FromLong(width)); + PyTuple_SetItem(retVal, 1, PyInt_FromLong(height)); + return retVal; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, setJustify, args) +{ + unsigned char justify; + if (!PyArg_ParseTuple(args, "b", &justify)) + { + PyErr_SetString(PyExc_TypeError, "setJustify expects a unsigned 8-bit int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetJustify(justify); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptDynamicMap, setLineSpacing, args) +{ + short spacing; + if (!PyArg_ParseTuple(args, "h", &spacing)) + { + PyErr_SetString(PyExc_TypeError, "setLineSpacing expects a short int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetLineSpacing(spacing); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptDynamicMap, getImage) +{ + return pyImage::New(self->fThis->GetImage()); +} + +PYTHON_START_METHODS_TABLE(ptDynamicMap) + PYTHON_METHOD(ptDynamicMap, sender, "Params: sender\nSet the sender of the message being sent to the DynamicMap"), + PYTHON_BASIC_METHOD(ptDynamicMap, clearKeys, "Clears the receiver list"), + PYTHON_METHOD(ptDynamicMap, addKey, "Params: key\nAdd a receiver... in other words a DynamicMap"), + PYTHON_METHOD(ptDynamicMap, netPropagate, "Params: propagateFlag\nSpecify whether this object needs to use messages that are sent on the network\n" + "- The default is for this to be false."), + PYTHON_METHOD(ptDynamicMap, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n" + "- This is to be used if your Python program is running on only one client\n" + "Such as a game master, only running on the client that owns a particular object\n" + "This only applies when NetPropagate is set to true"), + PYTHON_METHOD(ptDynamicMap, clearToColor, "Params: color\nClear the DynamicMap to the specified color\n" + "- 'color' is a ptColor object"), + PYTHON_BASIC_METHOD(ptDynamicMap, flush, "Flush all the commands that were issued since the last flush()"), + PYTHON_BASIC_METHOD(ptDynamicMap, purgeImage, "Purge the DynamicTextMap images"), + PYTHON_METHOD(ptDynamicMap, setTextColor, "Params: color, blockRGB=0\nSet the color of the text to be written\n" + "- 'color' is a ptColor object\n" + "- 'blockRGB' must be true if you're trying to render onto a transparent or semi-transparent color"), + PYTHON_METHOD(ptDynamicMap, setFont, "Params: facename,size\nSet the font of the text to be written\n" + "- 'facename' is a string with the name of the font\n" + "- 'size' is the point size of the font to use"), + PYTHON_METHOD(ptDynamicMap, fillRect, "Params: left,top,right,bottom,color\nFill in the specified rectangle with a color\n" + "- left,top,right,bottom define the rectangle\n" + "- 'color' is a ptColor object"), + PYTHON_METHOD(ptDynamicMap, frameRect, "Params: left,top,right,bottom,color\nFrame a rectangle with a specified color\n" + "- left,top,right,bottom define the rectangle\n" + "- 'color' is a ptColor object"), + PYTHON_METHOD(ptDynamicMap, setClipping, "Params: clipLeft,clipTop,clipRight,clipBottom\nSets the clipping rectangle\n" + "- All drawtext will be clipped to this until the\n" + "unsetClipping() is called"), + PYTHON_BASIC_METHOD(ptDynamicMap, unsetClipping, "Stop the clipping of text"), + PYTHON_METHOD(ptDynamicMap, setWrapping, "Params: wrapWidth,wrapHeight\nSet where text will be wrapped horizontally and vertically\n" + "- All drawtext commands will be wrapped until the\n" + "unsetWrapping() is called"), + PYTHON_BASIC_METHOD(ptDynamicMap, unsetWrapping, "Stop text wrapping"), + PYTHON_METHOD(ptDynamicMap, drawText, "Params: x,y,text\nDraw text at a specified location\n" + "- x,y is the point to start drawing the text\n" + "- 'text' is a string of the text to be drawn"), + PYTHON_METHOD(ptDynamicMap, drawTextW, "Params: x,y,text\nUnicode version of drawText"), + PYTHON_METHOD(ptDynamicMap, drawImage, "Params: x,y,image,respectAlphaFlag\nDraws a ptImage object on the dynamicTextmap starting at the location x,y"), + PYTHON_METHOD(ptDynamicMap, drawImageClipped, "Params: x,y,image,cx,cy,cw,ch,respectAlphaFlag\nDraws a ptImage object clipped to cx,cy with cw(width),ch(height)"), + PYTHON_METHOD_NOARGS(ptDynamicMap, getWidth, "Returns the width of the dynamicTextmap"), + PYTHON_METHOD_NOARGS(ptDynamicMap, getHeight, "Returns the height of the dynamicTextmap"), + PYTHON_METHOD(ptDynamicMap, calcTextExtents, "Params: text\nCalculates the extent of the specified text, returns it as a (width, height) tuple"), + PYTHON_METHOD(ptDynamicMap, setJustify, "Params: justify\nSets the justification of the text. (justify is a PtJustify)"), + PYTHON_METHOD(ptDynamicMap, setLineSpacing, "Params: spacing\nSets the line spacing (in pixels)"), + PYTHON_METHOD_NOARGS(ptDynamicMap, getImage, "Returns a pyImage associated with the dynamicTextmap"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptDynamicMap, "Params: key=None\nCreates a ptDynamicMap object"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptDynamicMap, pyDynamicText) + +PyObject *pyDynamicText::New(pyKey& key) +{ + ptDynamicMap *newObj = (ptDynamicMap*)ptDynamicMap_type.tp_new(&ptDynamicMap_type, NULL, NULL); + newObj->fThis->fReceivers.Append(key.getKey()); + return (PyObject*)newObj; +} + +PyObject *pyDynamicText::New(plKey key) +{ + ptDynamicMap *newObj = (ptDynamicMap*)ptDynamicMap_type.tp_new(&ptDynamicMap_type, NULL, NULL); + newObj->fThis->fReceivers.Append(key); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptDynamicMap, pyDynamicText) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptDynamicMap, pyDynamicText) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyDynamicText::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptDynamicMap); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyDynamicText::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtJustify); + PYTHON_ENUM_ELEMENT(PtJustify, kCenter, plDynamicTextMap::Justify::kCenter); + PYTHON_ENUM_ELEMENT(PtJustify, kLeftJustify, plDynamicTextMap::Justify::kLeftJustify); + PYTHON_ENUM_ELEMENT(PtJustify, kRightJustify, plDynamicTextMap::Justify::kRightJustify); + PYTHON_ENUM_END(m, PtJustify); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyEnum.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyEnum.cpp new file mode 100644 index 00000000..38706c21 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyEnum.cpp @@ -0,0 +1,719 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyEnum +// +// PURPOSE: Base class stuff for enumeration support (you don't instance this +// class +// + +#include "pyEnum.h" + +#include +#include "structmember.h" +#include "pyGlueHelpers.h" + +#if HS_BUILD_FOR_MAC +#include +#include +#endif + +struct EnumValue { + PyObject_HEAD + long value; + char* name; +}; + +PyObject *NewEnumValue(char const* name, long value); + +static PyObject *EnumValue_new(PyTypeObject *type, PyObject *args, PyObject *) +{ + EnumValue *self = (EnumValue*)type->tp_alloc(type, 0); + if (self != NULL) + { + char *nameTemp = NULL; + long value; + if (!PyArg_ParseTuple(args, "l|s", &value, &nameTemp)) + { + Py_DECREF(self); + return NULL; + } + + if (nameTemp) // copy the value if it was passed + { + self->name = TRACKED_NEW char[strlen(nameTemp) + 1]; + strcpy(self->name, nameTemp); + self->name[strlen(nameTemp)] = '\0'; + } + else + self->name = NULL; + self->value = value; + } + + return (PyObject*)self; +} + +static void EnumValue_dealloc(EnumValue *self) +{ + if (self->name) + delete [] self->name; + self->ob_type->tp_free((PyObject*)self); +} + +static int EnumValue_print(PyObject *self, FILE *fp, int flags) +{ + // convert this object to a string + PyObject *strObject = (flags & Py_PRINT_RAW) ? self->ob_type->tp_str(self) : self->ob_type->tp_repr(self); + if (strObject == NULL) + return -1; // failure + + const char* text = PyString_AsString(strObject); + if (text == NULL) + return -1; + + fprintf(fp, text); // and print it to the file + return 0; +} + +static PyObject *EnumValue_repr(PyObject *self) +{ + EnumValue *obj = (EnumValue*)self; + if (obj->name == NULL) + { + // no name, so just output our value + return PyString_FromFormat("%s(%ld)", obj->ob_type->tp_name, obj->value); + } + else + { + // we have a name, so output it + return PyString_FromString(obj->name); + } +} + +static PyObject* EnumValue_str(PyObject* self) +{ + EnumValue *obj = (EnumValue*)self; + if (obj->name == NULL) + { + // no name, so return our value + return PyString_FromFormat("%s(%ld)", obj->ob_type->tp_name, obj->value); + } + else + { + // just return the name + return PyString_FromString(obj->name); + } +} + +// forward def because the type object isn't defined yet +bool IsEnumValue(PyObject *obj); + +long EnumValue_hash(PyObject *v) +{ + // stolen from python's int class + if (!IsEnumValue(v)) + { + PyErr_SetString(PyExc_TypeError, "hash was passed a non enum value (this would be weird)"); + return 0; + } + long x = ((EnumValue*)v)->value; + if (x == -1) + x = -2; + return x; +} + +int EnumValue_nonzero(EnumValue *v) +{ + return v->value != 0; +} + +PyObject *EnumValue_and(PyObject *v, PyObject *w) +{ + EnumValue *obj = NULL; + long other = 0; + if (IsEnumValue(v)) + { + obj = (EnumValue*)v; + if (!PyInt_Check(w)) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + other = PyInt_AsLong(w); + } + else if (IsEnumValue(w)) + { + obj = (EnumValue*)w; + if (!PyInt_Check(v)) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + other = PyInt_AsLong(v); + } + else + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return PyInt_FromLong(obj->value & other); +} + +PyObject *EnumValue_xor(PyObject *v, PyObject *w) +{ + EnumValue *obj = NULL; + long other = 0; + if (IsEnumValue(v)) + { + obj = (EnumValue*)v; + if (!PyInt_Check(w)) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + other = PyInt_AsLong(w); + } + else if (IsEnumValue(w)) + { + obj = (EnumValue*)w; + if (!PyInt_Check(v)) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + other = PyInt_AsLong(v); + } + else + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return PyInt_FromLong(obj->value ^ other); +} + +PyObject *EnumValue_or(PyObject *v, PyObject *w) +{ + EnumValue *obj = NULL; + long other = 0; + if (IsEnumValue(v)) + { + obj = (EnumValue*)v; + if (!PyInt_Check(w)) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + other = PyInt_AsLong(w); + } + else if (IsEnumValue(w)) + { + obj = (EnumValue*)w; + if (!PyInt_Check(v)) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + other = PyInt_AsLong(v); + } + else + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + return PyInt_FromLong(obj->value | other); +} + +PyObject *EnumValue_int(EnumValue *v) +{ + return PyInt_FromLong(v->value); +} + +PyObject *EnumValue_long(EnumValue *v) +{ + return PyLong_FromLong((v->value)); +} + +PyObject *EnumValue_float(EnumValue *v) +{ + return PyFloat_FromDouble((double)(v->value)); +} + +PyObject *EnumValue_oct(EnumValue *v) +{ + char buf[100]; + long x = v->value; + if (x < 0) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + if (x == 0) + strcpy(buf, "0"); + else + _snprintf(buf, sizeof(buf), "0%lo", x); + return PyString_FromString(buf); +} + +PyObject *EnumValue_hex(EnumValue *v) +{ + char buf[100]; + long x = v->value; + if (x < 0) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + _snprintf(buf, sizeof(buf), "0x%lx", x); + return PyString_FromString(buf); +} + +int EnumValue_coerce(PyObject **pv, PyObject **pw) +{ + if (PyInt_Check(*pw)) + { + long x = PyInt_AsLong(*pw); + *pw = NewEnumValue(NULL, x); + Py_INCREF(*pv); + return 0; + } + else if (PyLong_Check(*pw)) + { + double x = PyLong_AsDouble(*pw); + if (x == -1.0 && PyErr_Occurred()) + return -1; + *pw = NewEnumValue(NULL, (long)x); + Py_INCREF(*pv); + return 0; + } + else if (PyFloat_Check(*pw)) + { + double x = PyFloat_AsDouble(*pw); + *pw = NewEnumValue(NULL, (long)x); + Py_INCREF(*pv); + return 0; + } + else if (IsEnumValue(*pw)) + { + Py_INCREF(*pv); + Py_INCREF(*pw); + return 0; + } + return 1; // Can't do it +} + +// we support some of the number methods +PYTHON_START_AS_NUMBER_TABLE(EnumValue) + 0, /*nb_add*/ + 0, /*nb_subtract*/ + 0, /*nb_multiply*/ + 0, /*nb_divide*/ + 0, /*nb_remainder*/ + 0, /*nb_divmod*/ + 0, /*nb_power*/ + 0, /*nb_negative*/ + 0, /*nb_positive*/ + 0, /*nb_absolute*/ + (inquiry)EnumValue_nonzero, /*nb_nonzero*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + (binaryfunc)EnumValue_and, /*nb_and*/ + (binaryfunc)EnumValue_xor, /*nb_xor*/ + (binaryfunc)EnumValue_or, /*nb_or*/ + (coercion)EnumValue_coerce, /*nb_coerce*/ + (unaryfunc)EnumValue_int, /*nb_int*/ + (unaryfunc)EnumValue_long, /*nb_long*/ + (unaryfunc)EnumValue_float, /*nb_float*/ + (unaryfunc)EnumValue_oct, /*nb_oct*/ + (unaryfunc)EnumValue_hex, /*nb_hex*/ + 0, /*nb_inplace_add*/ + 0, /*nb_inplace_subtract*/ + 0, /*nb_inplace_multiply*/ + 0, /*nb_inplace_divide*/ + 0, /*nb_inplace_remainder*/ + 0, /*nb_inplace_power*/ + 0, /*nb_inplace_lshift*/ + 0, /*nb_inplace_rshift*/ + 0, /*nb_inplace_and*/ + 0, /*nb_inplace_xor*/ + 0, /*nb_inplace_or*/ + 0, /* nb_floor_divide */ + 0, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ +PYTHON_END_AS_NUMBER_TABLE; + +PYTHON_COMPARE_DEFINITION(EnumValue, v, w) +{ + long i = 0; + long j = 0; + if (IsEnumValue(v)) + { + i = ((EnumValue*)v)->value; + if (PyInt_Check(w)) + j = PyInt_AsLong(w); + else if (PyFloat_Check(w)) + j = (long)PyFloat_AsDouble(w); + else if (PyLong_Check(w)) + j = PyLong_AsLong(w); + else if (IsEnumValue(w)) + j = ((EnumValue*)w)->value; + else + { + PyErr_SetString(PyExc_TypeError, "cannot compare EnumValue to a non number"); + return 0; + } + + } + else if (IsEnumValue(w)) + { + j = ((EnumValue*)w)->value; + if (PyInt_Check(v)) + i = PyInt_AsLong(v); + else if (PyFloat_Check(v)) + i = (long)PyFloat_AsDouble(v); + else if (PyLong_Check(v)) + i = PyLong_AsLong(v); + else if (IsEnumValue(v)) + i = ((EnumValue*)v)->value; + else + { + PyErr_SetString(PyExc_TypeError, "cannot compare EnumValue to a non number"); + return 0; + } + } + else + { + PyErr_SetString(PyExc_TypeError, "cannot compare EnumValue to a non number"); + return 0; + } + + if (i < j) + PYTHON_COMPARE_LESS_THAN; + else if (i > j) + PYTHON_COMPARE_GREATER_THAN; + PYTHON_COMPARE_EQUAL; +} + +PYTHON_NO_INIT_DEFINITION(EnumValue) + +PYTHON_START_METHODS_TABLE(EnumValue) +PYTHON_END_METHODS_TABLE; + +PYTHON_TYPE_START(EnumValue) + 0, + "PlasmaConstants.EnumValue", + sizeof(EnumValue), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)EnumValue_dealloc, /* tp_dealloc */ + EnumValue_print, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + PYTHON_DEFAULT_COMPARE(EnumValue), /* tp_compare */ + EnumValue_repr, /* tp_repr */ + PYTHON_DEFAULT_AS_NUMBER(EnumValue),/* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + EnumValue_hash, /* tp_hash */ + 0, /* tp_call */ + EnumValue_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ + "A basic enumeration value", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PYTHON_DEFAULT_METHODS_TABLE(EnumValue), /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + PYTHON_DEFAULT_INIT(EnumValue), /* tp_init */ + 0, /* tp_alloc */ + EnumValue_new /* tp_new */ +PYTHON_TYPE_END; + +bool IsEnumValue(PyObject *obj) +{ + return PyObject_TypeCheck(obj, &EnumValue_type); +} + +PyObject *NewEnumValue(char const* name, long value) +{ + PyObject *tempArgs = NULL; + if (name) + tempArgs = Py_BuildValue("(ls)", value, name); // args are value, name + else + tempArgs = Py_BuildValue("(l)", value); // args are value only + EnumValue *newObj = (EnumValue*)EnumValue_type.tp_new(&EnumValue_type, tempArgs, NULL); + Py_DECREF(tempArgs); // clean up the temps + return (PyObject*)newObj; +} + +// Now for the Enum base class + +struct Enum +{ + PyObject_HEAD + PyObject *values; // the values list + PyObject *lookup; // the enum values, key is enum, value is enum value + PyObject *reverseLookup; // the enum values, key is enum value, value is enum +}; + +static PyObject *Enum_new(PyTypeObject *type, PyObject *, PyObject *) +{ + Enum *self = (Enum*)type->tp_alloc(type, 0); + if (self != NULL) + { + self->values = PyDict_New(); + if (self->values == NULL) + { + Py_DECREF(self); + return NULL; + } + self->lookup = PyDict_New(); + if (self->lookup == NULL) + { + Py_DECREF(self); + return NULL; + } + self->reverseLookup = PyDict_New(); + if (self->reverseLookup == NULL) + { + Py_DECREF(self); + return NULL; + } + } + + return (PyObject*)self; +} + +static int Enum_traverse(Enum *self, visitproc visit, void *arg) +{ + if (self->values && visit(self->values, arg) < 0) + return -1; + if (self->lookup && visit(self->lookup, arg) < 0) + return -1; + if (self->reverseLookup && visit(self->reverseLookup, arg) < 0) + return -1; + + return 0; +} + +static int Enum_clear(Enum *self) +{ + Py_XDECREF(self->values); + self->values = NULL; + Py_XDECREF(self->lookup); + self->lookup = NULL; + Py_XDECREF(self->reverseLookup); + self->reverseLookup = NULL; + + return 0; +} + +static void Enum_dealloc(Enum *self) +{ + Enum_clear(self); + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject *Enum_getattro(PyObject *self, PyObject *attr_name) +{ + if (!PyString_Check(attr_name)) + { + PyErr_SetString(PyExc_TypeError, "getattro expects a string argument"); + PYTHON_RETURN_ERROR; + } + Enum *theEnum = (Enum*)self; + PyObject *item = PyDict_GetItem(theEnum->lookup, attr_name); + if (!item) + { + PyErr_Clear(); // wasn't in the dictionary, check to see if they want the values or lookup dict + char *name = PyString_AsString(attr_name); + if (strcmp(name, "values") == 0) + { + Py_INCREF(theEnum->values); + return theEnum->values; + } + else if (strcmp(name, "lookup") == 0) + { + Py_INCREF(theEnum->lookup); + return theEnum->lookup; + } + else if (strcmp(name, "reverseLookup") == 0) + { + Py_INCREF(theEnum->reverseLookup); + return theEnum->reverseLookup; + } + return PyObject_GenericGetAttr(self, attr_name); // let the default method handle it + } + Py_INCREF(item); + return item; +} + +static int Enum_setattro(PyObject *self, PyObject *attr_name, PyObject *value) +{ + if (!PyString_Check(attr_name)) + { + PyErr_SetString(PyExc_TypeError, "setattro expects a string argument"); + return -1;; + } + Enum *theEnum = (Enum*)self; + PyObject *item = PyDict_GetItem(theEnum->lookup, attr_name); + if (!item) + { + PyErr_Clear(); // wasn't in the dictionary, check to see if they want the values or lookup dict + char *name = PyString_AsString(attr_name); + if (strcmp(name, "values") == 0) + { + PyErr_SetString(PyExc_RuntimeError, "Cannot set the value attribute"); + return -1; + } + else if (strcmp(name, "lookup") == 0) + { + PyErr_SetString(PyExc_RuntimeError, "Cannot set the lookup attribute"); + return -1; + } + else if (strcmp(name, "reverseLookup") == 0) + { + PyErr_SetString(PyExc_RuntimeError, "Cannot set the reverseLookup attribute"); + return -1; + } + // they aren't trying to set an enum value or any of our special values, so let them + return PyObject_GenericSetAttr(self, attr_name, value); // let the default method handle it + } + PyErr_SetString(PyExc_RuntimeError, "Cannot set any enum value"); + return -1; +} + +PYTHON_NO_INIT_DEFINITION(Enum) + +PYTHON_START_METHODS_TABLE(Enum) +PYTHON_END_METHODS_TABLE; + +PYTHON_TYPE_START(Enum) + 0, + "PlasmaConstants.Enum", + sizeof(Enum), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Enum_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + Enum_getattro, /* tp_getattro */ + Enum_setattro, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + "Enum base class", /* tp_doc */ + (traverseproc)Enum_traverse, /* tp_traverse */ + (inquiry)Enum_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PYTHON_DEFAULT_METHODS_TABLE(Enum), /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + PYTHON_DEFAULT_INIT(Enum), /* tp_init */ + 0, /* tp_alloc */ + Enum_new /* tp_new */ +PYTHON_TYPE_END; + +// creates and sets up the enum base class +void pyEnum::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, EnumValue); + PYTHON_CLASS_IMPORT(m, Enum); + PYTHON_CLASS_IMPORT_END(m); +} + +// makes an enum object using the specified name and values +void pyEnum::MakeEnum(PyObject *m, const char* name, std::map values) +{ + if (m == NULL) + return; + + Enum *newEnum = (Enum*)Enum_type.tp_new(&Enum_type, NULL, NULL); + if (newEnum == NULL) + { + std::string errorStr = "Could not create enum named "; + errorStr += name; + PyErr_SetString(PyExc_RuntimeError, errorStr.c_str()); + return; + } + + PyObject *valuesDict = newEnum->values; + PyObject *lookupDict = newEnum->lookup; + PyObject *reverseLookupDict = newEnum->reverseLookup; + for (std::map::iterator curValue = values.begin(); curValue != values.end(); curValue++) + { + std::string key = curValue->first; + int value = curValue->second; + + std::string enumValueName = name; + enumValueName += "." + key; + + PyObject *newValue = NewEnumValue(enumValueName.c_str(), value); + PyObject *valueObj = PyInt_FromLong((long)value); + PyObject *keyObj = PyString_FromString(key.c_str()); + PyDict_SetItem(valuesDict, valueObj, newValue); + PyDict_SetItem(lookupDict, keyObj, newValue); + PyDict_SetItem(reverseLookupDict, newValue, keyObj); + Py_DECREF(keyObj); + Py_DECREF(valueObj); + Py_DECREF(newValue); + } + + PyModule_AddObject(m, (char*)name, (PyObject*)newEnum); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyEnum.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyEnum.h new file mode 100644 index 00000000..b7ea8e82 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyEnum.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyEnum_h +#define pyEnum_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyEnum +// +// PURPOSE: Base class stuff for enumeration support (you don't instance this +// class +// + +#include +#include "hsConfig.h" +#include "hsStlUtils.h" +#include "pyGlueHelpers.h" + + +class pyEnum +{ +public: + static void AddPlasmaConstantsClasses(PyObject *m); + static void RemovePlasmaConstantsClasses(PyObject *m); + static void MakeEnum(PyObject *m, const char* name, std::map values); +}; + +#endif // pyEnum_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControl.cpp new file mode 100644 index 00000000..dff83680 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControl.cpp @@ -0,0 +1,430 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "../pfGameGUIMgr/pfGUIControlMod.h" +#include "../pfGameGUIMgr/pfGUIDialogMod.h" + +#include "pyKey.h" +#include "pyGUIControl.h" +#include "pyGUIDialog.h" +#include "pyColor.h" + +#include "pyGUIControlCheckBox.h" + +pyGUIControl::pyGUIControl(pyKey& gckey) +{ + fGCkey = gckey.getKey(); +} + +pyGUIControl::pyGUIControl(plKey objkey) +{ + fGCkey = objkey; +} + +pyGUIControl& pyGUIControl::operator=(const pyGUIControl& other) +{ + return Copy(other); +} +// copy constructor +pyGUIControl::pyGUIControl(const pyGUIControl& other) +{ + Copy(other); +} + +pyGUIControl& pyGUIControl::Copy(const pyGUIControl& other) +{ + fGCkey = other.fGCkey; + return *this; +} + +// override the equals to operator +hsBool pyGUIControl::operator==(const pyGUIControl &gcobj) const +{ + plKey theirs = ((pyGUIControl&)gcobj).getObjKey(); + if ( fGCkey == nil && theirs == nil ) + return true; + else if ( fGCkey != nil && theirs != nil ) + return (fGCkey->GetUoid()==theirs->GetUoid()); + else + return false; +} + + +// getter and setters +plKey pyGUIControl::getObjKey() +{ + return fGCkey; +} + + +PyObject* pyGUIControl::getObjPyKey() +{ + // create the pyKey that Python will manage + return pyKey::New(fGCkey); +} + + +// interface functions +UInt32 pyGUIControl::GetTagID() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + return pbmod->GetTagID(); + } + return 0; +} + + +void pyGUIControl::SetEnabled( hsBool e ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->SetEnabled(e); + if ( e ) + pbmod->ClearFlag(pfGUIControlMod::kIntangible); + else + pbmod->SetFlag(pfGUIControlMod::kIntangible); + } + } +} + +hsBool pyGUIControl::IsEnabled( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + return pbmod->IsEnabled(); + } + return false; +} + +void pyGUIControl::SetFocused( hsBool e ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->SetFocused(e); + } +} + +hsBool pyGUIControl::IsFocused( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + return pbmod->IsFocused(); + } + return false; +} + +void pyGUIControl::SetVisible( hsBool vis ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->SetVisible(vis); + } +} + +hsBool pyGUIControl::IsVisible( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + return pbmod->IsVisible(); + } + return false; +} + +hsBool pyGUIControl::IsInteresting( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + return pbmod->IsInteresting(); + } + return false; +} + +void pyGUIControl::SetNotifyOnInteresting( hsBool state ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->SetNotifyOnInteresting(state); + } +} + +void pyGUIControl::Refresh( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->Refresh(); + } +} + +void pyGUIControl::SetObjectCenter( pyPoint3& pt) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->SetObjectCenter( pt.fPoint.fX, pt.fPoint.fY ); + } +} + + +PyObject* pyGUIControl::GetObjectCenter() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + return pyPoint3::New(pbmod->GetObjectCenter()); + } + return pyPoint3::New(hsPoint3(0,0,0)); +} + + + +PyObject* pyGUIControl::GetOwnerDlg( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIControlMod* pbmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + // get the owner dialog modifier pointer + pfGUIDialogMod* pdialog = pbmod->GetOwnerDlg(); + if ( pdialog ) + { + // create a pythonized Dialog class object + return pyGUIDialog::New(pdialog->GetKey()); + } + } + } + PyErr_SetString(PyExc_NameError, "No owner GUIDialog could be found for this control...?"); + PYTHON_RETURN_ERROR; +} + + // get color schemes +PyObject* pyGUIControl::GetForeColor() +{ + if ( fGCkey ) + { + pfGUIControlMod* pdmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + return pyColor::New(color->fForeColor.r,color->fForeColor.g,color->fForeColor.b,color->fForeColor.a); + } + } + PYTHON_RETURN_NONE; +} + +PyObject* pyGUIControl::GetSelColor() +{ + if ( fGCkey ) + { + pfGUIControlMod* pdmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + return pyColor::New(color->fSelForeColor.r,color->fSelForeColor.g,color->fSelForeColor.b,color->fSelForeColor.a); + } + } + PYTHON_RETURN_NONE; +} + +PyObject* pyGUIControl::GetBackColor() +{ + if ( fGCkey ) + { + pfGUIControlMod* pdmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + return pyColor::New(color->fBackColor.r,color->fBackColor.g,color->fBackColor.b,color->fBackColor.a); + } + } + PYTHON_RETURN_NONE; +} + +PyObject* pyGUIControl::GetBackSelColor() +{ + if ( fGCkey ) + { + pfGUIControlMod* pdmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + return pyColor::New(color->fSelBackColor.r,color->fSelBackColor.g,color->fSelBackColor.b,color->fSelBackColor.a); + } + } + PYTHON_RETURN_NONE; +} + +UInt32 pyGUIControl::GetFontSize() +{ + if ( fGCkey ) + { + pfGUIControlMod* pdmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + return color->fFontSize; + } + } + // create a pyColor that will be managed by Python + return 0; +} + + + // set color scheme +void pyGUIControl::SetForeColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + if ( fGCkey ) + { + pfGUIControlMod* pdmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fForeColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fForeColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fForeColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fForeColor.a = a; + } + } +} + +void pyGUIControl::SetSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + if ( fGCkey ) + { + pfGUIControlMod* pdmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fSelForeColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fSelForeColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fSelForeColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fSelForeColor.a = a; + } + } +} + +void pyGUIControl::SetBackColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + if ( fGCkey ) + { + pfGUIControlMod* pdmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fBackColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fBackColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fBackColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fBackColor.a = a; + } + } +} + +void pyGUIControl::SetBackSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + if ( fGCkey ) + { + pfGUIControlMod* pdmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fSelBackColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fSelBackColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fSelBackColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fSelBackColor.a = a; + } + } +} + + +void pyGUIControl::SetFontSize(UInt32 fontsize) +{ + if ( fGCkey ) + { + pfGUIControlMod* pdmod = pfGUIControlMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + color->fFontSize = (UInt8)fontsize; + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControl.h new file mode 100644 index 00000000..54c7def3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControl.h @@ -0,0 +1,117 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControl_h_ +#define _pyGUIControl_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControl - a base wrapper class to provide interface to modifier +// attached to a GUIControl (base class for the GUI controls) +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" +#include "pyGeometry3.h" + +#include +#include "pyGlueHelpers.h" + +class pyGUIDialog; +class pyColor; + +class pyGUIControl +{ +protected: + plKey fGCkey; + + pyGUIControl(): fGCkey(nil) {} // only used by python glue, do NOT call + pyGUIControl(pyKey& gckey); + pyGUIControl(plKey objkey); + // copy constructor + pyGUIControl(const pyGUIControl& other); + +public: + pyGUIControl& operator=(const pyGUIControl& other); + pyGUIControl& Copy(const pyGUIControl& other); + + void setKey(plKey key) {fGCkey = key;} // only used by python glue, do NOT call + + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptGUIControl); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + static PyObject *New(const pyGUIControl& other); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControl object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControl); // converts a PyObject to a pyGUIControl (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // override the equals to operator + hsBool operator==(const pyGUIControl &gcobj) const; + hsBool operator!=(const pyGUIControl &gcobj) const { return !(gcobj == *this); } + + // getter and setters + virtual plKey getObjKey(); + virtual PyObject* getObjPyKey(); // returns pyKey + + // interface functions + virtual UInt32 GetTagID(); + virtual void SetEnabled( hsBool e ); + virtual void Enable() { SetEnabled(true); } + virtual void Disable() { SetEnabled(false); } + virtual hsBool IsEnabled( void ); + virtual void SetFocused( hsBool e ); + virtual void Focus() { SetFocused(true); } + virtual void UnFocus() { SetFocused(false); } + virtual hsBool IsFocused( void ); + virtual void SetVisible( hsBool vis ); + virtual void Show() { SetVisible(true); } + virtual void Hide() { SetVisible(false); } + virtual hsBool IsVisible( void ); + virtual hsBool IsInteresting( void ); + virtual void SetNotifyOnInteresting( hsBool state ); + virtual void Refresh( void ); + virtual void SetObjectCenter( pyPoint3& pt); + virtual PyObject* GetObjectCenter(); // returns pyPoint3 + virtual PyObject* GetOwnerDlg( void ); // returns pyGUIDialog + + // get color schemes + virtual PyObject* GetForeColor(); // returns pyColor + virtual PyObject* GetSelColor(); // returns pyColor + virtual PyObject* GetBackColor(); // returns pyColor + virtual PyObject* GetBackSelColor(); // returns pyColor + virtual UInt32 GetFontSize(); + // set color scheme + virtual void SetForeColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + virtual void SetSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + virtual void SetBackColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + virtual void SetBackSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + virtual void SetFontSize(UInt32 fontsize); + +}; + +#endif // _pyGUIControl_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButton.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButton.cpp new file mode 100644 index 00000000..acb9947d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButton.cpp @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "pyGUIControlButton.h" +#include "../pfGameGUIMgr/pfGUIButtonMod.h" + +pyGUIControlButton::pyGUIControlButton(pyKey& gckey) : pyGUIControl(gckey) +{ +} + +pyGUIControlButton::pyGUIControlButton(plKey objkey) : pyGUIControl(objkey) +{ +} + +hsBool pyGUIControlButton::IsGUIControlButton(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIButtonMod::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + +void pyGUIControlButton::SetNotifyType(Int32 kind) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIButtonMod* butnmod = pfGUIButtonMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( butnmod ) + butnmod->SetNotifyType(kind); + } +} + +Int32 pyGUIControlButton::GetNotifyType() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIButtonMod* butnmod = pfGUIButtonMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( butnmod ) + return butnmod->GetNotifyType(); + } + return false; +} + +hsBool pyGUIControlButton::IsButtonDown() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIButtonMod* butnmod = pfGUIButtonMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( butnmod ) + return butnmod->IsButtonDown(); + } + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButton.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButton.h new file mode 100644 index 00000000..53250617 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButton.h @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlButton_h_ +#define _pyGUIControlButton_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlButton - a wrapper class to provide interface to modifier +// attached to a GUIControlButton +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" +#include "pyGeometry3.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyGUIControl.h" + +class pyGUIControlButton : public pyGUIControl +{ +protected: + pyGUIControlButton(): pyGUIControl() {} // used by python glue, do NOT call + pyGUIControlButton(pyKey& gckey); + pyGUIControlButton(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlButton); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlButton object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlButton); // converts a PyObject to a pyGUIControlButton (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaConstantsClasses(PyObject *m); + + static hsBool IsGUIControlButton(pyKey& gckey); + + virtual void SetNotifyType(Int32 kind); + virtual Int32 GetNotifyType(); + virtual hsBool IsButtonDown(); +}; + +#endif // _pyGUIControlButton_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButtonGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButtonGlue.cpp new file mode 100644 index 00000000..1d122727 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlButtonGlue.cpp @@ -0,0 +1,125 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlButton.h" +#include "pyEnum.h" + +#include "../pfGameGUIMgr/pfGUIButtonMod.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlButton, pyGUIControlButton); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlButton, pyGUIControlButton) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlButton) + +PYTHON_INIT_DEFINITION(ptGUIControlButton, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlButton, setNotifyType, args) +{ + long kind; + if (!PyArg_ParseTuple(args, "l", &kind)) + { + PyErr_SetString(PyExc_TypeError, "setNotifyType expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNotifyType(kind); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlButton, getNotifyType) +{ + return PyLong_FromLong(self->fThis->GetNotifyType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlButton, isButtonDown) +{ + PYTHON_RETURN_BOOL(self->fThis->IsButtonDown()); +} + +PYTHON_START_METHODS_TABLE(ptGUIControlButton) + PYTHON_METHOD(ptGUIControlButton, setNotifyType, "Params: kind\nSets this button's notify type. See PtButtonNotifyTypes"), + PYTHON_METHOD_NOARGS(ptGUIControlButton, getNotifyType, "Returns this button's notify type. See PtButtonNotifyTypes"), + PYTHON_METHOD_NOARGS(ptGUIControlButton, isButtonDown, "Is the button down? Returns 1 for true otherwise returns 0"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlButton, pyGUIControl, "Params: ctrlKey\nPlasma GUI Control Button class"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlButton::New(pyKey& gckey) +{ + ptGUIControlButton *newObj = (ptGUIControlButton*)ptGUIControlButton_type.tp_new(&ptGUIControlButton_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlButton::New(plKey objkey) +{ + ptGUIControlButton *newObj = (ptGUIControlButton*)ptGUIControlButton_type.tp_new(&ptGUIControlButton_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlButton, pyGUIControlButton) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlButton, pyGUIControlButton) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlButton::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlButton); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyGUIControlButton::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtButtonNotifyTypes); + PYTHON_ENUM_ELEMENT(PtButtonNotifyTypes, kNotifyOnUp, pfGUIButtonMod::kNotifyOnUp); + PYTHON_ENUM_ELEMENT(PtButtonNotifyTypes, kNotifyOnDown, pfGUIButtonMod::kNotifyOnDown); + PYTHON_ENUM_ELEMENT(PtButtonNotifyTypes, kNotifyOnUpAndDown, pfGUIButtonMod::kNotifyOnUpAndDown); + PYTHON_ENUM_END(m, PtButtonNotifyTypes); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBox.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBox.cpp new file mode 100644 index 00000000..c46d71e7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBox.cpp @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "../pfGameGUIMgr/pfGUICheckBoxCtrl.h" +#include "../pfGameGUIMgr/pfGUIDialogMod.h" + +#include "pyGUIControlCheckBox.h" +#include "pyGUIDialog.h" + +pyGUIControlCheckBox::pyGUIControlCheckBox(pyKey& gckey) : pyGUIControl(gckey) +{ +} + +pyGUIControlCheckBox::pyGUIControlCheckBox(plKey objkey) : pyGUIControl(objkey) +{ +} + + +hsBool pyGUIControlCheckBox::IsGUIControlCheckBox(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUICheckBoxCtrl::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + + +void pyGUIControlCheckBox::SetChecked( hsBool checked ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUICheckBoxCtrl* pcbmod = pfGUICheckBoxCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pcbmod ) + pcbmod->SetChecked(checked); + } +} + + +hsBool pyGUIControlCheckBox::IsChecked( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUICheckBoxCtrl* pcbmod = pfGUICheckBoxCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pcbmod ) + return pcbmod->IsChecked(); + } + return false; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBox.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBox.h new file mode 100644 index 00000000..ff2d45d7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBox.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlCheckBox_h_ +#define _pyGUIControlCheckBox_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlCheckBox - a wrapper class to provide interface to modifier +// attached to a GUIControlCheckBox +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyGUIControl.h" + + +class pyGUIControlCheckBox : public pyGUIControl +{ +protected: + pyGUIControlCheckBox(): pyGUIControl() {} // only used by python glue, do NOT call + pyGUIControlCheckBox(pyKey& gckey); + pyGUIControlCheckBox(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlCheckBox); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlCheckBox object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlCheckBox); // converts a PyObject to a pyGUIControlCheckBox (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlCheckBox(pyKey& gckey); + + virtual void SetChecked( hsBool checked ); + virtual hsBool IsChecked( void ); + +}; + +#endif // _pyGUIControlCheckBox_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBoxGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBoxGlue.cpp new file mode 100644 index 00000000..2e21f6ac --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlCheckBoxGlue.cpp @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlCheckBox.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlCheckBox, pyGUIControlCheckBox); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlCheckBox, pyGUIControlCheckBox) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlCheckBox) + +PYTHON_INIT_DEFINITION(ptGUIControlCheckBox, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlCheckBox, setChecked, args) +{ + char checkedState; + if (!PyArg_ParseTuple(args, "b", &checkedState)) + { + PyErr_SetString(PyExc_TypeError, "setChecked expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetChecked(checkedState != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlCheckBox, isChecked) +{ + PYTHON_RETURN_BOOL(self->fThis->IsChecked()); +} + +PYTHON_START_METHODS_TABLE(ptGUIControlCheckBox) + PYTHON_METHOD(ptGUIControlCheckBox, setChecked, "Params: checkedState\nSets this checkbox to the 'checkedState'"), + PYTHON_METHOD_NOARGS(ptGUIControlCheckBox, isChecked, "Is this checkbox checked? Returns 1 for true otherwise returns 0"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlCheckBox, pyGUIControl, "Params: ctrlKey\nPlasma GUI Control Checkbox class"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlCheckBox::New(pyKey& gckey) +{ + ptGUIControlCheckBox *newObj = (ptGUIControlCheckBox*)ptGUIControlCheckBox_type.tp_new(&ptGUIControlCheckBox_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlCheckBox::New(plKey objkey) +{ + ptGUIControlCheckBox *newObj = (ptGUIControlCheckBox*)ptGUIControlCheckBox_type.tp_new(&ptGUIControlCheckBox_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlCheckBox, pyGUIControlCheckBox) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlCheckBox, pyGUIControlCheckBox) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlCheckBox::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlCheckBox); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMap.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMap.cpp new file mode 100644 index 00000000..bdb9e5eb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMap.cpp @@ -0,0 +1,90 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "../pfGameGUIMgr/pfGUIClickMapCtrl.h" +#include "../pfGameGUIMgr/pfGUIDialogMod.h" + +#include "pyGUIControlClickMap.h" +#include "pyGUIDialog.h" + +pyGUIControlClickMap::pyGUIControlClickMap(pyKey& gckey) : pyGUIControl(gckey) +{ +} + +pyGUIControlClickMap::pyGUIControlClickMap(plKey objkey) : pyGUIControl(objkey) +{ +} + + +hsBool pyGUIControlClickMap::IsGUIControlClickMap(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIClickMapCtrl::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + + +PyObject* pyGUIControlClickMap::GetLastMousePt( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIClickMapCtrl* pcmmod = pfGUIClickMapCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pcmmod ) + return pyPoint3::New(pcmmod->GetLastMousePt()); + } + PYTHON_RETURN_NONE; +} + +PyObject* pyGUIControlClickMap::GetLastMouseUpPt( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIClickMapCtrl* pcmmod = pfGUIClickMapCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pcmmod ) + return pyPoint3::New(pcmmod->GetLastMouseUpPt()); + } + PYTHON_RETURN_NONE; +} + +PyObject* pyGUIControlClickMap::GetLastMouseDragPt( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIClickMapCtrl* pcmmod = pfGUIClickMapCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pcmmod ) + return pyPoint3::New(pcmmod->GetLastMouseDragPt()); + } + PYTHON_RETURN_NONE; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMap.h new file mode 100644 index 00000000..2a0a34e4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMap.h @@ -0,0 +1,71 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlClickMap_h_ +#define _pyGUIControlClickMap_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlClickMap - a wrapper class to provide interface to modifier +// attached to a GUIControlClickMap +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyGUIControl.h" + +class pyPoint3; + + +class pyGUIControlClickMap : public pyGUIControl +{ +protected: + pyGUIControlClickMap(): pyGUIControl() {} // for python glue only, do NOT call + pyGUIControlClickMap(pyKey& gckey); + pyGUIControlClickMap(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlClickMap); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlClickMap object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlClickMap); // converts a PyObject to a pyGUIControlClickMap (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlClickMap(pyKey& gckey); + + PyObject* GetLastMousePt( void ); // returns pyPoint3 + PyObject* GetLastMouseUpPt( void ); // returns pyPoint3 + PyObject* GetLastMouseDragPt( void ); // returns pyPoint3 + +}; + +#endif // _pyGUIControlClickMap_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMapGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMapGlue.cpp new file mode 100644 index 00000000..dc1c3fcc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlClickMapGlue.cpp @@ -0,0 +1,107 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlClickMap.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlClickMap, pyGUIControlClickMap); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlClickMap, pyGUIControlClickMap) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlClickMap) + +PYTHON_INIT_DEFINITION(ptGUIControlClickMap, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlClickMap, getLastMousePoint) +{ + return self->fThis->GetLastMousePt(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlClickMap, getLastMouseUpPoint) +{ + return self->fThis->GetLastMouseUpPt(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlClickMap, getLastMouseDragPoint) +{ + return self->fThis->GetLastMouseDragPt(); +} + +PYTHON_START_METHODS_TABLE(ptGUIControlClickMap) + PYTHON_METHOD_NOARGS(ptGUIControlClickMap, getLastMousePoint, "Returns the last point the mouse was at"), + PYTHON_METHOD_NOARGS(ptGUIControlClickMap, getLastMouseUpPoint, "Returns the last point the mouse was released at"), + PYTHON_METHOD_NOARGS(ptGUIControlClickMap, getLastMouseDragPoint, "Returns the last point the mouse was dragged to"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlClickMap, pyGUIControl, "Params: ctrlKey\nPlasma GUI control Click Map"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlClickMap::New(pyKey& gckey) +{ + ptGUIControlClickMap *newObj = (ptGUIControlClickMap*)ptGUIControlClickMap_type.tp_new(&ptGUIControlClickMap_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlClickMap::New(plKey objkey) +{ + ptGUIControlClickMap *newObj = (ptGUIControlClickMap*)ptGUIControlClickMap_type.tp_new(&ptGUIControlClickMap_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlClickMap, pyGUIControlClickMap) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlClickMap, pyGUIControlClickMap) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlClickMap::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlClickMap); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBar.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBar.cpp new file mode 100644 index 00000000..fdc0e8cd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBar.cpp @@ -0,0 +1,90 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "../pfGameGUIMgr/pfGUIDragBarCtrl.h" + +#include "pyGUIControlDragBar.h" + +pyGUIControlDragBar::pyGUIControlDragBar(pyKey& gckey) : pyGUIControl(gckey) +{ +} + +pyGUIControlDragBar::pyGUIControlDragBar(plKey objkey) : pyGUIControl(objkey) +{ +} + +pyGUIControlDragBar::~pyGUIControlDragBar() +{ +} + + +hsBool pyGUIControlDragBar::IsGUIControlDragBar(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIDragBarCtrl::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + + +void pyGUIControlDragBar::Anchor( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIDragBarCtrl* pbmod = pfGUIDragBarCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->SetAnchored(true); + } +} + +void pyGUIControlDragBar::Unanchor( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIDragBarCtrl* pbmod = pfGUIDragBarCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->SetAnchored(false); + } +} + +hsBool pyGUIControlDragBar::IsAnchored() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIDragBarCtrl* pbmod = pfGUIDragBarCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + return pbmod->IsAnchored(); + } + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBar.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBar.h new file mode 100644 index 00000000..d846ddaa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBar.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlDragBar_h_ +#define _pyGUIControlDragBar_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlDragBar - a wrapper class to provide interface to modifier +// attached to a GUIControlDragBar +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyGUIControl.h" + +class pyGUIControlDragBar : public pyGUIControl +{ +protected: + pyGUIControlDragBar(): pyGUIControl() {} // for python glue only, do NOT call + pyGUIControlDragBar(pyKey& gckey); + pyGUIControlDragBar(plKey objkey); + +public: + ~pyGUIControlDragBar(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlDragBar); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlDragBar object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlDragBar); // converts a PyObject to a pyGUIControlDragBar (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlDragBar(pyKey& gckey); + + virtual void Anchor( void ); + virtual void Unanchor( void ); + virtual hsBool IsAnchored(); + +}; + +#endif // _pyGUIControlDragBar_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBarGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBarGlue.cpp new file mode 100644 index 00000000..7e1be406 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDragBarGlue.cpp @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlDragBar.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlDragBar, pyGUIControlDragBar); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlDragBar, pyGUIControlDragBar) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlDragBar) + +PYTHON_INIT_DEFINITION(ptGUIControlDragBar, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlDragBar, anchor, Anchor) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlDragBar, unanchor, Unanchor) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlDragBar, isAnchored) +{ + PYTHON_RETURN_BOOL(self->fThis->IsAnchored()); +} + +PYTHON_START_METHODS_TABLE(ptGUIControlDragBar) + PYTHON_BASIC_METHOD(ptGUIControlDragBar, anchor, "Don't allow this dragbar object to be moved by the user.\nDrop anchor!"), + PYTHON_BASIC_METHOD(ptGUIControlDragBar, unanchor, "Allow the user to drag this control around the screen.\nRaise anchor."), + PYTHON_METHOD_NOARGS(ptGUIControlDragBar, isAnchored, "Is this dragbar control anchored? Returns 1 if true otherwise returns 0"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlDragBar, pyGUIControl, "Params: ctrlKey\nPlasma GUI Control DragBar class"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlDragBar::New(pyKey& gckey) +{ + ptGUIControlDragBar *newObj = (ptGUIControlDragBar*)ptGUIControlDragBar_type.tp_new(&ptGUIControlDragBar_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlDragBar::New(plKey objkey) +{ + ptGUIControlDragBar *newObj = (ptGUIControlDragBar*)ptGUIControlDragBar_type.tp_new(&ptGUIControlDragBar_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlDragBar, pyGUIControlDragBar) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlDragBar, pyGUIControlDragBar) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlDragBar::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlDragBar); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggable.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggable.cpp new file mode 100644 index 00000000..e99a07ee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggable.cpp @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "../pfGameGUIMgr/pfGUIDraggableMod.h" +#include "../pfGameGUIMgr/pfGUIDialogMod.h" + +#include "pyGUIControlDraggable.h" +#include "pyGUIDialog.h" + +pyGUIControlDraggable::pyGUIControlDraggable(pyKey& gckey) : pyGUIControl(gckey) +{ +} + +pyGUIControlDraggable::pyGUIControlDraggable(plKey objkey) : pyGUIControl(objkey) +{ +} + + +hsBool pyGUIControlDraggable::IsGUIControlDraggable(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIDraggableMod::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + +void pyGUIControlDraggable::StopDragging( hsBool cancel ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIDraggableMod* pcmmod = pfGUIDraggableMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pcmmod ) + pcmmod->StopDragging(cancel); + } +} + +PyObject* pyGUIControlDraggable::GetLastMousePt( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIDraggableMod* pcmmod = pfGUIDraggableMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pcmmod ) + return pyPoint3::New(pcmmod->GetLastMousePt()); + } + PYTHON_RETURN_NONE; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggable.h new file mode 100644 index 00000000..7c199800 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggable.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlDraggable_h_ +#define _pyGUIControlDraggable_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlDraggable - a wrapper class to provide interface to modifier +// attached to a GUIControlDraggable +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyGUIControl.h" + +class pyPoint3; + + +class pyGUIControlDraggable : public pyGUIControl +{ +protected: + pyGUIControlDraggable(): pyGUIControl() {} // for python glue only, do NOT call + pyGUIControlDraggable(pyKey& gckey); + pyGUIControlDraggable(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlDraggable); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlDraggable object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlDraggable); // converts a PyObject to a pyGUIControlDraggable (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlDraggable(pyKey& gckey); + + void StopDragging( hsBool cancel ); + PyObject* GetLastMousePt( void ); // returns pyPoint3 + +}; + +#endif // _pyGUIControlDraggable_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggableGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggableGlue.cpp new file mode 100644 index 00000000..37c5dd12 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDraggableGlue.cpp @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlDraggable.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlDraggable, pyGUIControlDraggable); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlDraggable, pyGUIControlDraggable) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlDraggable) + +PYTHON_INIT_DEFINITION(ptGUIControlDraggable, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlDraggable, stopDragging, args) +{ + char cancelFlag; + if (!PyArg_ParseTuple(args, "b", &cancelFlag)) + { + PyErr_SetString(PyExc_TypeError, "stopDragging expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->StopDragging(cancelFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlDraggable, getLastMousePoint) +{ + return self->fThis->GetLastMousePt(); +} + +PYTHON_START_METHODS_TABLE(ptGUIControlDraggable) + PYTHON_METHOD(ptGUIControlDraggable, stopDragging, "Params: cancelFlag\nUNKNOWN"), + PYTHON_METHOD_NOARGS(ptGUIControlDraggable, getLastMousePoint, "Returns the last point we were dragged to"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlDraggable, pyGUIControl, "Params: ctrlKey\nPlasma GUI control for something draggable"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlDraggable::New(pyKey& gckey) +{ + ptGUIControlDraggable *newObj = (ptGUIControlDraggable*)ptGUIControlDraggable_type.tp_new(&ptGUIControlDraggable_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlDraggable::New(plKey objkey) +{ + ptGUIControlDraggable *newObj = (ptGUIControlDraggable*)ptGUIControlDraggable_type.tp_new(&ptGUIControlDraggable_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlDraggable, pyGUIControlDraggable) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlDraggable, pyGUIControlDraggable) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlDraggable::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlDraggable); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicText.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicText.cpp new file mode 100644 index 00000000..c1f45027 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicText.cpp @@ -0,0 +1,96 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "../pfGameGUIMgr/pfGUIDynDisplayCtrl.h" +#include "../plGImage/plDynamicTextMap.h" +#include "pyDynamicText.h" + +#include "pyGUIControlDynamicText.h" + +pyGUIControlDynamicText::pyGUIControlDynamicText(pyKey& gckey) : pyGUIControl(gckey) +{ +} + +pyGUIControlDynamicText::pyGUIControlDynamicText(plKey objkey) : pyGUIControl(objkey) +{ +} + + +hsBool pyGUIControlDynamicText::IsGUIControlDynamicText(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIDynDisplayCtrl::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + + + //specific interface functions +UInt32 pyGUIControlDynamicText::GetNumMaps() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIDynDisplayCtrl* pdtmod = pfGUIDynDisplayCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdtmod ) + return pdtmod->GetNumMaps(); + } + return 0; +} + + +PyObject* pyGUIControlDynamicText::GetMap(UInt32 i) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIDynDisplayCtrl* pdtmod = pfGUIDynDisplayCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdtmod ) + { + // get the owner dialog modifier pointer + plDynamicTextMap* pdyntm = pdtmod->GetMap(i); + if ( pdyntm ) + { + // create a pythonized Dialog class object (Python will manage it) + PyObject* dynTextObj = pyDynamicText::New(pdyntm->GetKey()); + pyDynamicText* pDynText = pyDynamicText::ConvertFrom(dynTextObj); + // since these are for GUI dialog items, don't net propagate these! + pDynText->SetNetPropagate(false); + pDynText->SetNetForce(false); + return dynTextObj; + } + } + } + char errmsg[256]; + sprintf(errmsg,"DynamicDisplay map number %d could be found on this control...?",i); + PyErr_SetString(PyExc_KeyError, errmsg); + PYTHON_RETURN_ERROR; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicText.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicText.h new file mode 100644 index 00000000..db0440f1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicText.h @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlDynamicText_h_ +#define _pyGUIControlDynamicText_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlDynamicText - a wrapper class to provide interface to modifier +// attached to a GUIControlDynamicText +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" +#include "pyGUIControl.h" + +#include +#include "pyGlueHelpers.h" + +class pyDynamicText; + + +class pyGUIControlDynamicText : public pyGUIControl +{ +protected: + pyGUIControlDynamicText(): pyGUIControl() {} // for python glue only, do NOT call + pyGUIControlDynamicText(pyKey& gckey); + pyGUIControlDynamicText(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlDynamicText); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlDynamicText object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlDynamicText); // converts a PyObject to a pyGUIControlDynamicText (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlDynamicText(pyKey& gckey); + + //specific interface functions + virtual UInt32 GetNumMaps(); + virtual PyObject* GetMap(UInt32 i); // returns pyDynamicText +}; + +#endif // _pyGUIControlButton_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicTextGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicTextGlue.cpp new file mode 100644 index 00000000..bc6a3462 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlDynamicTextGlue.cpp @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlDynamicText.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlDynamicText, pyGUIControlDynamicText); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlDynamicText, pyGUIControlDynamicText) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlDynamicText) + +PYTHON_INIT_DEFINITION(ptGUIControlDynamicText, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlDynamicText, getNumMaps) +{ + return PyLong_FromUnsignedLong(self->fThis->GetNumMaps()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlDynamicText, getMap, args) +{ + unsigned long i; + if (!PyArg_ParseTuple(args, "l", &i)) + { + PyErr_SetString(PyExc_KeyError, "getMap expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetMap(i); +} + +PYTHON_START_METHODS_TABLE(ptGUIControlDynamicText) + PYTHON_METHOD_NOARGS(ptGUIControlDynamicText, getNumMaps, "Returns the number of ptDynamicText maps attached"), + PYTHON_METHOD(ptGUIControlDynamicText, getMap, "Params: index\nReturns a specific ptDynamicText attached to this contol\n" + "If there is no map at 'index' then a KeyError exception will be raised"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlDynamicText, pyGUIControl, "Params: ctrlKey\nPlasma GUI Control DynamicText class"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlDynamicText::New(pyKey& gckey) +{ + ptGUIControlDynamicText *newObj = (ptGUIControlDynamicText*)ptGUIControlDynamicText_type.tp_new(&ptGUIControlDynamicText_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlDynamicText::New(plKey objkey) +{ + ptGUIControlDynamicText *newObj = (ptGUIControlDynamicText*)ptGUIControlDynamicText_type.tp_new(&ptGUIControlDynamicText_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlDynamicText, pyGUIControlDynamicText) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlDynamicText, pyGUIControlDynamicText) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlDynamicText::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlDynamicText); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBox.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBox.cpp new file mode 100644 index 00000000..e1b99369 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBox.cpp @@ -0,0 +1,242 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "../pfGameGUIMgr/pfGUIEditBoxMod.h" + +#include "pyGUIControlEditBox.h" +#include "pyColor.h" + +pyGUIControlEditBox::pyGUIControlEditBox(pyKey& gckey) : pyGUIControl(gckey) +{ +} + +pyGUIControlEditBox::pyGUIControlEditBox(plKey objkey) : pyGUIControl(objkey) +{ +} + + +hsBool pyGUIControlEditBox::IsGUIControlEditBox(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIEditBoxMod::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + +void pyGUIControlEditBox::SetBufferSize( UInt32 size ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + pebmod->SetBufferSize(size); + } +} + + +std::string pyGUIControlEditBox::GetBuffer( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + return pebmod->GetBuffer(); + } + return ""; +} + +std::wstring pyGUIControlEditBox::GetBufferW( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + return pebmod->GetBufferW(); + } + return L""; +} + +void pyGUIControlEditBox::ClearBuffer( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + pebmod->ClearBuffer(); + } +} + +void pyGUIControlEditBox::SetText( const char *str ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + pebmod->SetText(str); + } +} + +void pyGUIControlEditBox::SetTextW( const wchar_t *str ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + pebmod->SetText(str); + } +} + +void pyGUIControlEditBox::SetCursorToHome(void) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + pebmod->SetCursorToHome(); + } +} + +void pyGUIControlEditBox::SetCursorToEnd(void) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + pebmod->SetCursorToEnd(); + } +} + + +void pyGUIControlEditBox::SetColor(pyColor& forecolor, pyColor& backcolor) +{ +/* if ( fGCkey ) + { + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + { + pebmod->GetColorScheme().fForeColor = forecolor.getColor(); + pebmod->GetColorScheme().fBackColor = backcolor.getColor(); + } + } +*/ +} + + +void pyGUIControlEditBox::SetSelColor(pyColor& forecolor, pyColor& backcolor) +{ +/* if ( fGCkey ) + { + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + { + pebmod->GetColorScheme().fSelForeColor = forecolor.getColor(); + pebmod->GetColorScheme().fSelBackColor = backcolor.getColor(); + } + } +*/ +} + +hsBool pyGUIControlEditBox::WasEscaped() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + return pebmod->WasEscaped(); + } + return false; +} + +void pyGUIControlEditBox::SetSpecialCaptureKeyMode(hsBool state) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + pebmod->SetSpecialCaptureKeyMode(state); + } +} + +UInt32 pyGUIControlEditBox::GetLastKeyCaptured() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + return pebmod->GetLastKeyCaptured(); + } + return 0; +} + +UInt32 pyGUIControlEditBox::GetLastModifiersCaptured() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + return (UInt32)(pebmod->GetLastModifiersCaptured()); + } + return 0; +} + +void pyGUIControlEditBox::SetLastKeyCapture(UInt32 key, UInt32 modifiers) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + pebmod->SetLastKeyCapture(key,(UInt8)modifiers); + } +} + +void pyGUIControlEditBox::SetChatMode(hsBool state) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIEditBoxMod* pebmod = pfGUIEditBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pebmod ) + pebmod->SetChatMode(state); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBox.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBox.h new file mode 100644 index 00000000..df0b7493 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBox.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlEditBox_h_ +#define _pyGUIControlEditBox_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlEditBox - a wrapper class to provide interface to modifier +// attached to a GUIControlEditBox +// +////////////////////////////////////////////////////////////////////// + +#include "hsStlUtils.h" +#include "pyKey.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyGUIControl.h" + +class pyColor; + +class pyGUIControlEditBox : public pyGUIControl +{ +protected: + pyGUIControlEditBox(): pyGUIControl() {} // for python glue only, do NOT call + pyGUIControlEditBox(pyKey& gckey); + pyGUIControlEditBox(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlEditBox); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlEditBox object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlEditBox); // converts a PyObject to a pyGUIControlEditBox (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlEditBox(pyKey& gckey); + + virtual void SetBufferSize( UInt32 size ); + virtual std::string GetBuffer( void ); + virtual std::wstring GetBufferW( void ); + virtual void ClearBuffer( void ); + virtual void SetText( const char *str ); + virtual void SetTextW( const wchar_t *str ); + virtual void SetCursorToHome(void); + virtual void SetCursorToEnd(void); + virtual void SetColor(pyColor& forecolor, pyColor& backcolor); + virtual void SetSelColor(pyColor& forecolor, pyColor& backcolor); + + virtual hsBool WasEscaped(); + + virtual void SetSpecialCaptureKeyMode(hsBool state); + virtual UInt32 GetLastKeyCaptured(); + virtual UInt32 GetLastModifiersCaptured(); + virtual void SetLastKeyCapture(UInt32 key, UInt32 modifiers); + + virtual void SetChatMode(hsBool state); + +}; + +#endif // _pyGUIControlEditBox_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBoxGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBoxGlue.cpp new file mode 100644 index 00000000..b9a59cff --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlEditBoxGlue.cpp @@ -0,0 +1,270 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlEditBox.h" +#include "pyColor.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlEditBox, pyGUIControlEditBox); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlEditBox, pyGUIControlEditBox) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlEditBox) + +PYTHON_INIT_DEFINITION(ptGUIControlEditBox, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlEditBox, setStringSize, args) +{ + unsigned long strLen; + if (!PyArg_ParseTuple(args, "l", &strLen)) + { + PyErr_SetString(PyExc_TypeError, "setStringSize expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetBufferSize(strLen); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlEditBox, getString) +{ + return PyString_FromString(self->fThis->GetBuffer().c_str()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlEditBox, getStringW) +{ + std::wstring val = self->fThis->GetBufferW(); + return PyUnicode_FromWideChar(val.c_str(), val.length()); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlEditBox, clearString, ClearBuffer) + +PYTHON_METHOD_DEFINITION(ptGUIControlEditBox, setString, args) +{ + char* text; + if (!PyArg_ParseTuple(args, "s", &text)) + { + PyErr_SetString(PyExc_TypeError, "setString expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetText(text); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlEditBox, setStringW, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "setStringW expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + self->fThis->SetTextW(text); + delete [] text; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(textObj); + self->fThis->SetText(text); + PYTHON_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_TypeError, "setStringW expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlEditBox, home, SetCursorToHome) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlEditBox, end, SetCursorToEnd) + +PYTHON_METHOD_DEFINITION(ptGUIControlEditBox, setColor, args) +{ + PyObject* foreColorObj = NULL; + PyObject* backColorObj = NULL; + if (!PyArg_ParseTuple(args, "OO", &foreColorObj, &backColorObj)) + { + PyErr_SetString(PyExc_TypeError, "setColor expects two ptColor objects"); + PYTHON_RETURN_ERROR; + } + if ((!pyColor::Check(foreColorObj)) || (!pyColor::Check(backColorObj))) + { + PyErr_SetString(PyExc_TypeError, "setColor expects two ptColor objects"); + PYTHON_RETURN_ERROR; + } + pyColor* foreColor = pyColor::ConvertFrom(foreColorObj); + pyColor* backColor = pyColor::ConvertFrom(backColorObj); + self->fThis->SetColor(*foreColor, *backColor); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlEditBox, setSelectionColor, args) +{ + PyObject* foreColorObj = NULL; + PyObject* backColorObj = NULL; + if (!PyArg_ParseTuple(args, "OO", &foreColorObj, &backColorObj)) + { + PyErr_SetString(PyExc_TypeError, "setSelectionColor expects two ptColor objects"); + PYTHON_RETURN_ERROR; + } + if ((!pyColor::Check(foreColorObj)) || (!pyColor::Check(backColorObj))) + { + PyErr_SetString(PyExc_TypeError, "setSelectionColor expects two ptColor objects"); + PYTHON_RETURN_ERROR; + } + pyColor* foreColor = pyColor::ConvertFrom(foreColorObj); + pyColor* backColor = pyColor::ConvertFrom(backColorObj); + self->fThis->SetSelColor(*foreColor, *backColor); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlEditBox, wasEscaped) +{ + PYTHON_RETURN_BOOL(self->fThis->WasEscaped()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlEditBox, setSpecialCaptureKeyMode, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "setSpecialCaptureKeyMode expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSpecialCaptureKeyMode(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlEditBox, getLastKeyCaptured) +{ + return PyInt_FromLong(self->fThis->GetLastKeyCaptured()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlEditBox, getLastModifiersCaptured) +{ + return PyInt_FromLong(self->fThis->GetLastModifiersCaptured()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlEditBox, setLastKeyCapture, args) +{ + unsigned long key; + unsigned long modifiers; + if (!PyArg_ParseTuple(args, "ll", &key, &modifiers)) + { + PyErr_SetString(PyExc_TypeError, "setLastKeyCapture expects two unsigned longs"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetLastKeyCapture(key, modifiers); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlEditBox, setChatMode, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "setChatMode expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetChatMode(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptGUIControlEditBox) + PYTHON_METHOD(ptGUIControlEditBox, setStringSize, "Params: size\nSets the maximum size of the string that can be inputted by the user."), + PYTHON_METHOD_NOARGS(ptGUIControlEditBox, getString, "Returns the sting that the user typed in."), + PYTHON_METHOD_NOARGS(ptGUIControlEditBox, getStringW, "Unicode version of getString."), + PYTHON_BASIC_METHOD(ptGUIControlEditBox, clearString, "Clears the editbox."), + PYTHON_METHOD(ptGUIControlEditBox, setString, "Params: text\nPre-sets the editbox to a atring."), + PYTHON_METHOD(ptGUIControlEditBox, setStringW, "Params: text\nUnicode version of setString."), + PYTHON_BASIC_METHOD(ptGUIControlEditBox, home, "Sets the cursor in the editbox to before the first character."), + PYTHON_BASIC_METHOD(ptGUIControlEditBox, end, "Sets the cursor in the editbox to the after the last character."), + PYTHON_METHOD(ptGUIControlEditBox, setColor, "Params: foreColor,backColor\nSets the fore and back color of the editbox."), + PYTHON_METHOD(ptGUIControlEditBox, setSelectionColor, "Params: foreColor,backColor\nSets the selection color of the editbox."), + PYTHON_METHOD_NOARGS(ptGUIControlEditBox, wasEscaped, "If the editbox was escaped then return 1 else return 0"), + PYTHON_METHOD(ptGUIControlEditBox, setSpecialCaptureKeyMode, "Params: state\nSet the Capture mode on this control"), + PYTHON_METHOD_NOARGS(ptGUIControlEditBox, getLastKeyCaptured, "Gets the last capture key"), + PYTHON_METHOD_NOARGS(ptGUIControlEditBox, getLastModifiersCaptured, "Gets the last modifiers flags captured"), + PYTHON_METHOD(ptGUIControlEditBox, setLastKeyCapture, "Params: key, modifiers\nSet last key captured"), + PYTHON_METHOD(ptGUIControlEditBox, setChatMode, "Params: state\nSet the Chat mode on this control"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlEditBox, pyGUIControl, "Params: ctrlKey\nPlasma GUI Control Editbox class"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlEditBox::New(pyKey& gckey) +{ + ptGUIControlEditBox *newObj = (ptGUIControlEditBox*)ptGUIControlEditBox_type.tp_new(&ptGUIControlEditBox_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlEditBox::New(plKey objkey) +{ + ptGUIControlEditBox *newObj = (ptGUIControlEditBox*)ptGUIControlEditBox_type.tp_new(&ptGUIControlEditBox_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlEditBox, pyGUIControlEditBox) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlEditBox, pyGUIControlEditBox) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlEditBox::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlEditBox); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlGlue.cpp new file mode 100644 index 00000000..819abe1c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlGlue.cpp @@ -0,0 +1,369 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControl.h" +#include "pyKey.h" +#include "pyGeometry3.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControl, pyGUIControl); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControl, pyGUIControl) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControl) + +PYTHON_INIT_DEFINITION(ptGUIControl, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + PYTHON_RETURN_INIT_OK; +} + +PYTHON_RICH_COMPARE_DEFINITION(ptGUIControl, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pyGUIControl::Check(obj1) || !pyGUIControl::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptGUIControl object"); + PYTHON_RCOMPARE_ERROR; + } + } + pyGUIControl *ctrl1 = pyGUIControl::ConvertFrom(obj1); + pyGUIControl *ctrl2 = pyGUIControl::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*ctrl1) == (*ctrl2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*ctrl1) != (*ctrl2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptGUIControl object"); + PYTHON_RCOMPARE_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, getKey) +{ + return self->fThis->getObjPyKey(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, getTagID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetTagID()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControl, enable, args) +{ + char enableFlag = 1; + if (!PyArg_ParseTuple(args, "|b", &enableFlag)) + { + PyErr_SetString(PyExc_TypeError, "enable expects an optional boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetEnabled(enableFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControl, disable, Disable) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, isEnabled) +{ + PYTHON_RETURN_BOOL(self->fThis->IsEnabled()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControl, setFocus, args) +{ + char focusFlag; + if (!PyArg_ParseTuple(args, "b", &focusFlag)) + { + PyErr_SetString(PyExc_TypeError, "setFocus expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetFocused(focusFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControl, focus, Focus) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControl, unFocus, UnFocus) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, isFocused) +{ + PYTHON_RETURN_BOOL(self->fThis->IsFocused()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControl, setVisible, args) +{ + char visible; + if (!PyArg_ParseTuple(args, "b", &visible)) + { + PyErr_SetString(PyExc_TypeError, "setVisible expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetVisible(visible != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControl, show, Show) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControl, hide, Hide) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, isVisible) +{ + PYTHON_RETURN_BOOL(self->fThis->IsVisible()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, isInteresting) +{ + PYTHON_RETURN_BOOL(self->fThis->IsInteresting()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControl, setNotifyOnInteresting, args) +{ + char notify; + if (!PyArg_ParseTuple(args, "b", ¬ify)) + { + PyErr_SetString(PyExc_TypeError, "setNotifyOnInteresting expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNotifyOnInteresting(notify != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControl, refresh, Refresh) + +PYTHON_METHOD_DEFINITION(ptGUIControl, setObjectCenter, args) +{ + PyObject* pointObj = NULL; + if (!PyArg_ParseTuple(args, "O", &pointObj)) + { + PyErr_SetString(PyExc_TypeError, "setObjectCenter expects a ptPoint3"); + PYTHON_RETURN_ERROR; + } + if (!pyPoint3::Check(pointObj)) + { + PyErr_SetString(PyExc_TypeError, "setObjectCenter expects a ptPoint3"); + PYTHON_RETURN_ERROR; + } + pyPoint3* point = pyPoint3::ConvertFrom(pointObj); + self->fThis->SetObjectCenter(*point); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, getObjectCenter) +{ + return self->fThis->GetObjectCenter(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, getOwnerDialog) +{ + return self->fThis->GetOwnerDlg(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, getForeColor) +{ + return self->fThis->GetForeColor(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, getSelectColor) +{ + return self->fThis->GetSelColor(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, getBackColor) +{ + return self->fThis->GetBackColor(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, getBackSelectColor) +{ + return self->fThis->GetBackSelColor(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControl, getFontSize) +{ + return PyLong_FromUnsignedLong(self->fThis->GetFontSize()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControl, setForeColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setForeColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetForeColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControl, setSelectColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setSelectColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSelColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControl, setBackColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setBackColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetBackColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControl, setBackSelectColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setBackSelectColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetBackSelColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControl, setFontSize, args) +{ + unsigned long fontSize; + if (!PyArg_ParseTuple(args, "l", &fontSize)) + { + PyErr_SetString(PyExc_TypeError, "setFontSize expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetFontSize(fontSize); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptGUIControl) + PYTHON_METHOD_NOARGS(ptGUIControl, getKey, "Returns the ptKey for this GUI control"), + PYTHON_METHOD_NOARGS(ptGUIControl, getTagID, "Returns the Tag ID for this GUI control"), + PYTHON_METHOD(ptGUIControl, enable, "Params: flag=1\nEnables this GUI control"), + PYTHON_BASIC_METHOD(ptGUIControl, disable, "Disables this GUI control"), + PYTHON_METHOD_NOARGS(ptGUIControl, isEnabled, "Returns whether this GUI control is enabled"), + PYTHON_METHOD(ptGUIControl, setFocus, "Params: state\nSets the state of the focus of this GUI control"), + PYTHON_BASIC_METHOD(ptGUIControl, focus, "Gets focus for this GUI control"), + PYTHON_BASIC_METHOD(ptGUIControl, unFocus, "Releases focus for this GUI control"), + PYTHON_METHOD_NOARGS(ptGUIControl, isFocused, "Returns whether this GUI control has focus"), + PYTHON_METHOD(ptGUIControl, setVisible, "Params: state\nSets the state of visibility of this GUI control"), + PYTHON_BASIC_METHOD(ptGUIControl, show, "Shows this GUI control"), + PYTHON_BASIC_METHOD(ptGUIControl, hide, "Hides this GUI control"), + PYTHON_METHOD(ptGUIControl, isVisible, "Returns whether this GUI control is visible"), + PYTHON_METHOD_NOARGS(ptGUIControl, isInteresting, "Returns whether this GUI control is interesting at the moment"), + PYTHON_METHOD(ptGUIControl, setNotifyOnInteresting, "Params: state\nSets whether this control should send interesting events or not"), + PYTHON_BASIC_METHOD(ptGUIControl, refresh, "UNKNOWN"), + PYTHON_METHOD(ptGUIControl, setObjectCenter, "Params: point\nSets the GUI controls object center to 'point'"), + PYTHON_METHOD_NOARGS(ptGUIControl, getObjectCenter, "Returns ptPoint3 of the center of the GUI control object"), + PYTHON_METHOD_NOARGS(ptGUIControl, getOwnerDialog, "Returns a ptGUIDialog of the dialog that owns this GUI control"), + PYTHON_METHOD_NOARGS(ptGUIControl, getForeColor, "Returns the foreground color"), + PYTHON_METHOD_NOARGS(ptGUIControl, getSelectColor, "Returns the selection color"), + PYTHON_METHOD_NOARGS(ptGUIControl, getBackColor, "Returns the background color"), + PYTHON_METHOD_NOARGS(ptGUIControl, getBackSelectColor, "Returns the background selection color"), + PYTHON_METHOD_NOARGS(ptGUIControl, getFontSize, "Returns the font size"), + PYTHON_METHOD(ptGUIControl, setForeColor, "Params: r,g,b,a\nSets the foreground color"), + PYTHON_METHOD(ptGUIControl, setSelectColor, "Params: r,g,b,a\nSets the selection color"), + PYTHON_METHOD(ptGUIControl, setBackColor, "Params: r,g,b,a\nSets the background color"), + PYTHON_METHOD(ptGUIControl, setBackSelectColor, "Params: r,g,b,a\nSets the selection background color"), + PYTHON_METHOD(ptGUIControl, setFontSize, "Params: fontSize\nSets the font size"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +#define ptGUIControl_COMPARE PYTHON_NO_COMPARE +#define ptGUIControl_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptGUIControl_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptGUIControl_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptGUIControl_STR PYTHON_NO_STR +#define ptGUIControl_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptGUIControl) +#define ptGUIControl_GETSET PYTHON_NO_GETSET +#define ptGUIControl_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptGUIControl, "Params: controlKey\nBase class for all GUI controls"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptGUIControl, pyGUIControl); + +// required functions for PyObject interoperability +PyObject *pyGUIControl::New(pyKey& gckey) +{ + ptGUIControl *newObj = (ptGUIControl*)ptGUIControl_type.tp_new(&ptGUIControl_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControl::New(plKey objkey) +{ + ptGUIControl *newObj = (ptGUIControl*)ptGUIControl_type.tp_new(&ptGUIControl_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PyObject *pyGUIControl::New(const pyGUIControl& other) +{ + ptGUIControl *newObj = (ptGUIControl*)ptGUIControl_type.tp_new(&ptGUIControl_type, NULL, NULL); + newObj->fThis->fGCkey = other.fGCkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControl, pyGUIControl) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControl, pyGUIControl) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControl::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControl); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBox.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBox.cpp new file mode 100644 index 00000000..aa275cec --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBox.cpp @@ -0,0 +1,1059 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "../pfGameGUIMgr/pfGUIListBoxMod.h" +#include "../pfGameGUIMgr/pfGUIListElement.h" +#include "../pfGameGUIMgr/pfGUIDialogMod.h" +#include "../plGImage/plDynamicTextMap.h" + +#include "pyGUIControlListBox.h" +#include "pyGUIDialog.h" +#include "pyColor.h" +#include "pyImage.h" + +// a special class for different coloured list items +// +class pfColorListElement : public pfGUIListText +{ + protected: + hsColorRGBA fTextColor1; + hsColorRGBA fTextColor2; + wchar_t *fString1; + wchar_t *fString2; + UInt32 fInheritAlpha; + Int32 fOverrideFontSize; // size of font to use (if -1 then just use scheme setting) + + public: + enum InheritTypes + { + kNoInherit = 0, + kInheritFromNormal, + kInheritFromSelect, + kSelectDetermined, + kSelectUseGUIColor, + }; + + pfColorListElement( const char *string1, hsColorRGBA color1, const char *string2, hsColorRGBA color2, UInt32 inheritalpha, Int32 fontsize=-1 ) + { + if ( string1 ) + { + fString1 = hsStringToWString(string1); + fText = nil; + } + else + { + fString1 = nil; + fText = L""; + } + fTextColor1 = color1; + if (string2) + fString2 = hsStringToWString(string2); + else + fString2 = nil; + fTextColor2 = color2; + fInheritAlpha = inheritalpha; + fJustify = kLeftJustify; + fOverrideFontSize = fontsize; + } + + pfColorListElement( const wchar_t *string1, hsColorRGBA color1, const wchar_t *string2, hsColorRGBA color2, UInt32 inheritalpha, Int32 fontsize=-1 ) + { + if ( string1 ) + { + fString1 = TRACKED_NEW wchar_t[wcslen(string1)+1]; + wcscpy(fString1,string1); + fText = nil; + } + else + { + fString1 = nil; + fText = L""; + } + fTextColor1 = color1; + if (string2) + { + fString2 = TRACKED_NEW wchar_t[wcslen(string2)+1]; + wcscpy(fString2,string2); + } + else + fString2 = nil; + fTextColor2 = color2; + fInheritAlpha = inheritalpha; + fJustify = kLeftJustify; + fOverrideFontSize = fontsize; + } + + virtual ~pfColorListElement() + { + if ( fString1 ) + { + delete [] fString1; + fString1 = nil; + fText = nil; + } + if ( fString2 ) + delete [] fString2; + } + + virtual void SetText( const char *text ) + { + if ( fString1 ) + delete [] fString1; + + if( text != nil ) + fString1 = hsStringToWString(text); + else + fString1 = nil; + } + + virtual void SetText( const wchar_t *text ) + { + if ( fString1 ) + delete [] fString1; + + if( text != nil ) + { + fString1 = TRACKED_NEW wchar_t[wcslen(text)+1]; + wcscpy(fString1,text); + } + else + fString1 = nil; + } + + virtual hsBool Draw( plDynamicTextMap *textGen, UInt16 x, UInt16 y, UInt16 maxWidth, UInt16 maxHeight ) + { + hsColorRGBA textColor1; + textColor1 = fTextColor1; + hsColorRGBA textColor2; + textColor2 = fTextColor2; + + textGen->SetJustify( (plDynamicTextMap::Justify)fJustify ); + if( fInheritAlpha == kInheritFromNormal ) + { + textColor1.a = fColors->fForeColor.a; + textColor2.a = fColors->fForeColor.a; + } + else if ( fInheritAlpha == kInheritFromSelect ) + { + textColor1.a = fColors->fSelForeColor.a; + textColor2.a = fColors->fSelForeColor.a; + } + else if ( fInheritAlpha == kSelectDetermined ) + { + if ( fSelected ) + { + textColor1.a = fColors->fSelForeColor.a; + textColor2.a = fColors->fSelForeColor.a; + } + else + { + textColor1.a = fColors->fForeColor.a; + textColor2.a = fColors->fForeColor.a; + } + } + else if ( fInheritAlpha == kSelectUseGUIColor) + { + if ( fSelected ) + { + textColor1.r = fColors->fSelForeColor.r; + textColor2.r = fColors->fSelForeColor.r; + textColor1.g = fColors->fSelForeColor.g; + textColor2.g = fColors->fSelForeColor.g; + textColor1.b = fColors->fSelForeColor.b; + textColor2.b = fColors->fSelForeColor.b; + textColor1.a = fColors->fSelForeColor.a; + textColor2.a = fColors->fSelForeColor.a; + } + else + { + textColor1.a = fColors->fForeColor.a; + textColor2.a = fColors->fForeColor.a; + } + } + + // draw the first string + if (fString1) + { + if ( fOverrideFontSize != -1 ) + textGen->SetFont( fColors->fFontFace, (UInt16)fOverrideFontSize, fColors->fFontFlags ); + textGen->SetTextColor( textColor1, fColors->fTransparent && fColors->fBackColor.a == 0.f ); + textGen->DrawWrappedString( x + 2, y, fString1, maxWidth - 4, maxHeight ); + UInt16 width, height; + textGen->CalcWrappedStringSize(fString1,&width,&height); + x += 2 + width; + if ( fString2 == nil ) + y += height; + if ( fOverrideFontSize != -1 ) + textGen->SetFont( fColors->fFontFace, fColors->fFontSize, fColors->fFontFlags ); + } + + // draw the second string + if ( fString2 ) + { + textGen->SetTextColor( textColor2, fColors->fTransparent && fColors->fBackColor.a == 0.f ); + textGen->DrawWrappedString( x + 2, y, fString2, maxWidth - 4 - x, maxHeight ); + } + + return true; + } + + virtual void GetSize( plDynamicTextMap *textGen, UInt16 *width, UInt16 *height ) + { + hsBool wemade_string = false; + wchar_t* thestring; + if ( fString1 && fString2 ) + { + thestring = TRACKED_NEW wchar_t[ wcslen( fString1 ) + wcslen( fString2 ) + 3 ]; + swprintf( thestring, L"%s %s", fString1, fString2 ); + wemade_string = true; + } + else if (fString1) + thestring = fString1; + else if (fString2) + thestring = fString2; + else + thestring = nil; + *width = textGen->GetVisibleWidth() - 4; + + if ( fOverrideFontSize != -1 ) + textGen->SetFont( fColors->fFontFace, (UInt16)fOverrideFontSize, fColors->fFontFlags ); + textGen->CalcWrappedStringSize( thestring, width, height ); + if ( fOverrideFontSize != -1 ) + textGen->SetFont( fColors->fFontFace, fColors->fFontSize, fColors->fFontFlags ); + + if( height != nil ) + *height += 0; + *width += 4; + // clean up thestring if we made it + if ( wemade_string ) + delete [] thestring; + } + + virtual int CompareTo( pfGUIListElement *rightSide ) + { + return -2; + } + +}; + +class pfListTextInBox : public pfGUIListText +{ + protected: + UInt32 fMinWidth; + UInt32 fMinHeight; + + public: + pfListTextInBox::pfListTextInBox( const char *text, UInt32 min_width=0, UInt32 min_height=0 ) : pfGUIListText( text ) + { + fMinWidth = min_width; + fMinHeight = min_height; + fJustify = pfGUIListText::kCenter; + } + + pfListTextInBox::pfListTextInBox( const wchar_t *text, UInt32 min_width=0, UInt32 min_height=0 ) : pfGUIListText( text ) + { + fMinWidth = min_width; + fMinHeight = min_height; + fJustify = pfGUIListText::kCenter; + } + + virtual void GetSize( plDynamicTextMap *textGen, UInt16 *width, UInt16 *height ) + { + *width = textGen->CalcStringWidth( GetText(), height ); + if ( *width < fMinWidth ) + *width = (UInt16)fMinWidth; + if( height != nil ) + { + if( *height == 0 ) + *height = 10; // Never allow zero height elements + else + *height += 0; // Add one pixel on each side for padding (or not, 3.21.02 mcn) + if ( *height < fMinHeight ) + *height = (UInt16)fMinHeight; + } + } +}; + +class pfListPictureInBox : public pfGUIListPicture +{ + protected: + UInt32 fSrcX; + UInt32 fSrcY; + UInt32 fSrcWidth; + UInt32 fSrcHeight; + + public: + pfListPictureInBox::pfListPictureInBox( plKey mipKey, UInt32 x, UInt32 y, UInt32 width, UInt32 height, hsBool respectAlpha ) : pfGUIListPicture( mipKey,respectAlpha ) + { + fSrcX = x; + fSrcY = y; + fSrcWidth = width; + fSrcHeight = height; + } + + virtual hsBool Draw( plDynamicTextMap *textGen, UInt16 x, UInt16 y, UInt16 maxWidth, UInt16 maxHeight ) + { + plMipmap *mip = plMipmap::ConvertNoRef( fMipmapKey->ObjectIsLoaded() ); + if( mip != nil ) + { + if( fSrcWidth + fBorderSize + fBorderSize > maxWidth || fSrcHeight + fBorderSize + fBorderSize > maxHeight ) + return false; + + if( fSelected ) + textGen->FillRect( x, y, (UInt16)fSrcWidth, (UInt16)fSrcHeight, fColors->fSelForeColor ); +// hack!!!! This is to get the non-selected items to show up.... +// ... they need some kinda background to alpha to + else + { + if ( fRespectAlpha ) + { + hsColorRGBA backcolor = fColors->fBackColor; + backcolor.a = 1.0; + textGen->FillRect( x, y, (UInt16)fSrcWidth, (UInt16)fSrcHeight, backcolor ); + } + } +// end of hack + + textGen->DrawClippedImage( x + fBorderSize, y + fBorderSize, mip, (UInt16)fSrcX, (UInt16)fSrcY, (UInt16)fSrcWidth, (UInt16)fSrcHeight, + fRespectAlpha ? plDynamicTextMap::kImgBlend : plDynamicTextMap::kImgNoAlpha ); + } + + return true; + } + + virtual void GetSize( plDynamicTextMap *textGen, UInt16 *width, UInt16 *height ) + { + plMipmap *mip = plMipmap::ConvertNoRef( fMipmapKey->ObjectIsLoaded() ); + if( mip == nil ) + { + *width = 16; + if( height != nil ) + *height = 16; + } + else + { + *width = (UInt16)(fSrcWidth + fBorderSize + fBorderSize); + if( height != nil ) + *height = (UInt16)(fSrcHeight + fBorderSize + fBorderSize); + } + } +}; + +class pfListPictureInBoxWithSwatches : public pfListPictureInBox +{ + protected: + hsColorRGBA fPColor, fSColor; + + public: + + static UInt16 fSwatchSize, fSwatchOffset; + + pfListPictureInBoxWithSwatches::pfListPictureInBoxWithSwatches( plKey mipKey, UInt32 x, UInt32 y, + UInt32 width, UInt32 height, + hsBool respectAlpha, + const hsColorRGBA &primaryColor, const hsColorRGBA &secondaryColor ) + : pfListPictureInBox( mipKey, x, y, width, height, respectAlpha ) + { + fPColor = primaryColor; + fSColor = secondaryColor; + } + + virtual hsBool Draw( plDynamicTextMap *textGen, UInt16 x, UInt16 y, UInt16 maxWidth, UInt16 maxHeight ) + { + if( !pfListPictureInBox::Draw( textGen, x, y, maxWidth, maxHeight ) ) + return false; + + // Draw two color swatches + if( maxWidth > fSwatchSize * 2 + 1 + fSwatchOffset ) + { + // Secondary on right + x = x + maxWidth - fSwatchOffset - fSwatchSize; + y = y + maxHeight - fSwatchOffset - fSwatchSize; + textGen->FillRect( x, y, fSwatchSize, fSwatchSize, fSColor ); + + // Primary before it + x -= fSwatchSize + 1; + textGen->FillRect( x, y, fSwatchSize, fSwatchSize, fPColor ); + } + return true; + } +}; +UInt16 pfListPictureInBoxWithSwatches::fSwatchSize = 16; +UInt16 pfListPictureInBoxWithSwatches::fSwatchOffset = 5; + + + +pyGUIControlListBox::pyGUIControlListBox(pyKey& gckey) : pyGUIControl(gckey) +{ +} + +pyGUIControlListBox::pyGUIControlListBox(plKey objkey) : pyGUIControl(objkey) +{ +} + +hsBool pyGUIControlListBox::IsGUIControlListBox(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIListBoxMod::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + +Int32 pyGUIControlListBox::GetSelection( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + return plbmod->GetSelection(); + } + return -1; +} + +void pyGUIControlListBox::SetSelection( Int32 item ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->SetSelection(item); + } +} + +void pyGUIControlListBox::Refresh( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->Refresh(); + } +} + +void pyGUIControlListBox::RemoveElement( UInt16 index ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->RemoveElement(index); + } +} + +void pyGUIControlListBox::ClearAllElements( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->ClearAllElements(); + } +} + +UInt16 pyGUIControlListBox::GetNumElements( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + return plbmod->GetNumElements(); + } + return 0; +} + +void pyGUIControlListBox::SetElement( UInt16 idx, const char* text ) +{ + wchar_t *wText = hsStringToWString(text); + SetElementW(idx,wText); + delete [] wText; +} + +void pyGUIControlListBox::SetElementW( UInt16 idx, std::wstring text ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfGUIListElement* le = plbmod->GetElement(idx); + if ( le ) + { + if ( le->GetType() == pfGUIListElement::kText ) + { + // if its a text element type then it should be safe to cast it to a pfGUIListText + pfGUIListText* letext = (pfGUIListText*)le; + letext->SetText(text.c_str()); + } + } + } + } +} + +void pyGUIControlListBox::SetStringJustify( UInt16 idx, UInt32 justify) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfGUIListElement* le = plbmod->GetElement(idx); + if ( le ) + { + if ( le->GetType() == pfGUIListElement::kText ) + { + // if its a text element type then it should be safe to cast it to a pfGUIListText + pfGUIListText* letext = (pfGUIListText*)le; + letext->SetJustify( (pfGUIListText::JustifyTypes)justify ); + } + } + } + } +} + + +std::string pyGUIControlListBox::GetElement( UInt16 idx ) +{ + std::wstring wRetVal = GetElementW(idx); + char *temp = hsWStringToString(wRetVal.c_str()); + std::string retVal = temp; + delete [] temp; + return retVal; +} + +std::wstring pyGUIControlListBox::GetElementW( UInt16 idx ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfGUIListElement* le = plbmod->GetElement(idx); + if ( le ) + { + if ( le->GetType() == pfGUIListElement::kText ) + { + // if its a text element type then it should be safe to cast it to a pfGUIListText + pfGUIListText* letext = (pfGUIListText*)le; + return letext->GetText(); + } + else if ( le->GetType() == pfGUIListElement::kTreeRoot ) + { + pfGUIListTreeRoot* elroot = (pfGUIListTreeRoot*)le; + return elroot->GetTitle(); + } + } + } + } + return L""; +} + +Int16 pyGUIControlListBox::AddString( const char *string ) +{ + wchar_t *wString = hsStringToWString(string); + Int16 retVal = AddStringW(wString); + delete [] wString; + return retVal; +} + +Int16 pyGUIControlListBox::AddStringW( std::wstring string ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfGUIListText *element = TRACKED_NEW pfGUIListText( string.c_str() ); + if( fBuildRoots.GetCount() > 0 ) + fBuildRoots[ fBuildRoots.GetCount() - 1 ]->AddChild( element ); + return plbmod->AddElement( element ); + } + } + return -1; +} + +Int16 pyGUIControlListBox::AddImage( pyImage& image, hsBool respectAlpha ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfGUIListPicture *element = TRACKED_NEW pfGUIListPicture(image.GetKey(),respectAlpha); + if( fBuildRoots.GetCount() > 0 ) + fBuildRoots[ fBuildRoots.GetCount() - 1 ]->AddChild( element ); + return plbmod->AddElement( element ); + } + } + return -1; +} + + +Int16 pyGUIControlListBox::FindString( const char *toCompareTo ) +{ + wchar_t *wToCompareTo = hsStringToWString(toCompareTo); + Int16 retVal = FindStringW(wToCompareTo); + delete [] wToCompareTo; + return retVal; +} + +Int16 pyGUIControlListBox::FindStringW( std::wstring toCompareTo ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + return plbmod->FindString(toCompareTo.c_str()); + } + return -1; +} + +Int16 pyGUIControlListBox::AddTextWColor( const char *str, pyColor& textcolor, UInt32 inheritalpha) +{ + wchar_t *wStr = hsStringToWString(str); + Int16 retVal = AddTextWColorW(wStr,textcolor,inheritalpha); + delete [] wStr; + return retVal; +} + +Int16 pyGUIControlListBox::AddTextWColorW( std::wstring str, pyColor& textcolor, UInt32 inheritalpha) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfColorListElement *element = TRACKED_NEW pfColorListElement( str.c_str(), textcolor.getColor(),nil,hsColorRGBA(),inheritalpha ); + if( fBuildRoots.GetCount() > 0 ) + fBuildRoots[ fBuildRoots.GetCount() - 1 ]->AddChild( element ); + return plbmod->AddElement( element ); + } + } + return -1; +} + +Int16 pyGUIControlListBox::AddTextWColorWSize( const char *str, pyColor& textcolor, UInt32 inheritalpha, Int32 fontsize) +{ + wchar_t *wStr = hsStringToWString(str); + Int16 retVal = AddTextWColorWSizeW(wStr,textcolor,inheritalpha,fontsize); + delete [] wStr; + return retVal; +} + +Int16 pyGUIControlListBox::AddTextWColorWSizeW( std::wstring str, pyColor& textcolor, UInt32 inheritalpha, Int32 fontsize) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfColorListElement *element = TRACKED_NEW pfColorListElement( str.c_str(), textcolor.getColor(),nil,hsColorRGBA(),inheritalpha, fontsize ); + if( fBuildRoots.GetCount() > 0 ) + fBuildRoots[ fBuildRoots.GetCount() - 1 ]->AddChild( element ); + return plbmod->AddElement( element ); + } + } + return -1; +} + +void pyGUIControlListBox::Add2TextWColor( const char *str1, pyColor& textcolor1,const char *str2, pyColor& textcolor2, UInt32 inheritalpha) +{ + wchar_t *wStr1 = hsStringToWString(str1); + wchar_t *wStr2 = hsStringToWString(str2); + Add2TextWColorW(wStr1,textcolor1,wStr2,textcolor2,inheritalpha); + delete [] wStr2; + delete [] wStr1; +} + +void pyGUIControlListBox::Add2TextWColorW( std::wstring str1, pyColor& textcolor1, std::wstring str2, pyColor& textcolor2, UInt32 inheritalpha) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfColorListElement *element = TRACKED_NEW pfColorListElement(str1.c_str(),textcolor1.getColor(),str2.c_str(),textcolor2.getColor(),inheritalpha ); + if( fBuildRoots.GetCount() > 0 ) + fBuildRoots[ fBuildRoots.GetCount() - 1 ]->AddChild( element ); + plbmod->AddElement( element ); + } + } +} + +Int16 pyGUIControlListBox::AddStringInBox( const char *string, UInt32 min_width, UInt32 min_height ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfListTextInBox *element = TRACKED_NEW pfListTextInBox( string, min_width, min_height ); + if( fBuildRoots.GetCount() > 0 ) + fBuildRoots[ fBuildRoots.GetCount() - 1 ]->AddChild( element ); + return plbmod->AddElement( element ); + } + } + return -1; +} + +Int16 pyGUIControlListBox::AddStringInBoxW( std::wstring string, UInt32 min_width, UInt32 min_height ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfListTextInBox *element = TRACKED_NEW pfListTextInBox( string.c_str(), min_width, min_height ); + if( fBuildRoots.GetCount() > 0 ) + fBuildRoots[ fBuildRoots.GetCount() - 1 ]->AddChild( element ); + return plbmod->AddElement( element ); + } + } + return -1; +} + +Int16 pyGUIControlListBox::AddImageInBox( pyImage& image, UInt32 x, UInt32 y, UInt32 width, UInt32 height, hsBool respectAlpha ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfListPictureInBox *element = TRACKED_NEW pfListPictureInBox(image.GetKey(),x,y,width,height,respectAlpha); + if( fBuildRoots.GetCount() > 0 ) + fBuildRoots[ fBuildRoots.GetCount() - 1 ]->AddChild( element ); + return plbmod->AddElement( element ); + } + } + return -1; +} + +Int16 pyGUIControlListBox::AddImageAndSwatchesInBox( pyImage& image, UInt32 x, UInt32 y, UInt32 width, UInt32 height, hsBool respectAlpha, + pyColor &primary, pyColor &secondary ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfListPictureInBoxWithSwatches *element = TRACKED_NEW pfListPictureInBoxWithSwatches( image.GetKey(),x,y, + width,height,respectAlpha, + primary.getColor(), secondary.getColor() ); + if( fBuildRoots.GetCount() > 0 ) + fBuildRoots[ fBuildRoots.GetCount() - 1 ]->AddChild( element ); + return plbmod->AddElement( element ); + } + } + return -1; +} + +void pyGUIControlListBox::SetSwatchSize( UInt32 size ) +{ + pfListPictureInBoxWithSwatches::fSwatchSize = (UInt16)size; +} + +void pyGUIControlListBox::SetSwatchEdgeOffset( UInt32 set ) +{ + pfListPictureInBoxWithSwatches::fSwatchOffset = (UInt16)set; +} + + + +void pyGUIControlListBox::ScrollToBegin( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->ScrollToBegin(); + } +} + + +void pyGUIControlListBox::ScrollToEnd( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->ScrollToEnd(); + } +} + + +void pyGUIControlListBox::SetScrollPos( Int32 pos ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->SetScrollPos(pos); + } +} + + +Int32 pyGUIControlListBox::GetScrollPos( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + return plbmod->GetScrollPos(); + } + return 0; +} + + +Int32 pyGUIControlListBox::GetScrollRange( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + return plbmod->GetScrollRange(); + } + return 0; +} + + +void pyGUIControlListBox::LockList( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->LockList(); + } +} + + +void pyGUIControlListBox::UnlockList( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->UnlockList(); + } +} + +void pyGUIControlListBox::Clickable( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->ClearFlag(pfGUIControlMod::kIntangible); + } +} + +void pyGUIControlListBox::Unclickable( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->SetFlag(pfGUIControlMod::kIntangible); + } +} + +void pyGUIControlListBox::AddBranch( const char *name, hsBool initiallyOpen ) +{ + wchar_t *wName = hsStringToWString(name); + AddBranchW(wName,initiallyOpen); + delete [] wName; +} + +void pyGUIControlListBox::AddBranchW( std::wstring name, hsBool initiallyOpen ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + pfGUIListTreeRoot *root = TRACKED_NEW pfGUIListTreeRoot( name.c_str() ); + root->ShowChildren( initiallyOpen ); + + if( fBuildRoots.GetCount() > 0 ) + fBuildRoots[ fBuildRoots.GetCount() - 1 ]->AddChild( root ); + + fBuildRoots.Append( root ); + plbmod->AddElement( root ); + } + } +} + +void pyGUIControlListBox::CloseBranch( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + if( fBuildRoots.GetCount() > 0 ) + fBuildRoots.Remove( fBuildRoots.GetCount() - 1 ); + } + } +} + +void pyGUIControlListBox::RemoveSelection( Int32 item ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->RemoveSelection(item); + } +} + +void pyGUIControlListBox::AddSelection( Int32 item ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + plbmod->AddSelection(item); + } +} + +PyObject* pyGUIControlListBox::GetSelectionList() +{ + // create the list + PyObject* pySL = PyList_New(0); + + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + int numItems = plbmod->GetNumElements(); + UInt16 i; + for ( i=0; iGetElement(i); + if ( element->IsSelected() ) + { + PyObject* element = PyInt_FromLong(i); + PyList_Append(pySL, element); + Py_XDECREF(element); + } + } + } + } + return pySL; +} + +PyObject* pyGUIControlListBox::GetBranchList() +{ + // create the list + PyObject* pySL = PyList_New(0); + + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( plbmod ) + { + int numItems = plbmod->GetNumElements(); + UInt16 i; + for ( i=0; iGetElement(i); + if ( element ) + { + if ( element->GetType() == pfGUIListElement::kTreeRoot ) + { + pfGUIListTreeRoot* elroot = (pfGUIListTreeRoot*)element; + UInt16 showing = elroot->IsShowingChildren(); + PyObject* element = PyTuple_New(2); + PyTuple_SetItem(element, 0, PyInt_FromLong(i)); + PyTuple_SetItem(element, 1, PyInt_FromLong(showing)); + PyList_Append(pySL, element); + Py_XDECREF(element); + } + } + } + } + } + return pySL; +} + +void pyGUIControlListBox::AllowNoSelect() +{ + if (fGCkey) + { + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if (plbmod) + plbmod->ClearFlag(pfGUIListBoxMod::kForbidNoSelection); + } +} + +void pyGUIControlListBox::DisallowNoSelect() +{ + if (fGCkey) + { + pfGUIListBoxMod* plbmod = pfGUIListBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if (plbmod) + plbmod->SetFlag(pfGUIListBoxMod::kForbidNoSelection); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBox.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBox.h new file mode 100644 index 00000000..f4c70807 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBox.h @@ -0,0 +1,130 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlListBox_h_ +#define _pyGUIControlListBox_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlListBox - a wrapper class to provide interface to modifier +// attached to a GUIControl (such as Button, ListBox, etc.) +// +////////////////////////////////////////////////////////////////////// + +#include "hsTemplates.h" + +#include "hsStlUtils.h" +#include "pyKey.h" +#include "pyGUIControl.h" + +#include +#include "pyGlueHelpers.h" + + +class pyColor; +class pyImage; + +class pfGUIListTreeRoot; +class pyGUIControlListBox : public pyGUIControl +{ +private: + hsTArray fBuildRoots; + +protected: + pyGUIControlListBox(): pyGUIControl() {} // for python glue, do NOT call + pyGUIControlListBox(pyKey& gckey); + pyGUIControlListBox(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlListBox); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlListBox object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlListBox); // converts a PyObject to a pyGUIControlListBox (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlListBox(pyKey& gckey); + + // special case control for the listbox + // ...this allows the listbox to be used without being selectable + virtual void Clickable( void ); + virtual void Unclickable( void ); + virtual Int32 GetSelection( void ); + virtual void SetSelection( Int32 item ); + virtual void Refresh( void ); + virtual void SetElement( UInt16 idx, const char* text ); + virtual void SetElementW( UInt16 idx, std::wstring text ); + virtual void RemoveElement( UInt16 index ); + virtual void ClearAllElements( void ); + virtual UInt16 GetNumElements( void ); + virtual std::string GetElement( UInt16 idx ); + virtual std::wstring GetElementW( UInt16 idx ); + virtual Int16 AddString( const char *string ); + virtual Int16 AddStringW( std::wstring string ); + virtual Int16 AddImage( pyImage& image, hsBool respectAlpha ); + virtual Int16 AddImageInBox( pyImage& image, UInt32 x, UInt32 y, UInt32 width, UInt32 height, hsBool respectAlpha ); + virtual Int16 AddImageAndSwatchesInBox( pyImage& image, UInt32 x, UInt32 y, UInt32 width, UInt32 height, hsBool respectAlpha, + pyColor &primary, pyColor &secondary ); + virtual void SetSwatchSize( UInt32 size ); + virtual void SetSwatchEdgeOffset( UInt32 size ); + virtual void SetStringJustify( UInt16 idx, UInt32 justify); + virtual Int16 FindString( const char *toCompareTo ); + virtual Int16 FindStringW( std::wstring toCompareTo ); + virtual Int16 AddTextWColor( const char *str, pyColor& textcolor, UInt32 inheritalpha); + virtual Int16 AddTextWColorW( std::wstring str, pyColor& textcolor, UInt32 inheritalpha); + virtual Int16 AddTextWColorWSize( const char *str, pyColor& textcolor, UInt32 inheritalpha, Int32 fontsize); + virtual Int16 AddTextWColorWSizeW( std::wstring str, pyColor& textcolor, UInt32 inheritalpha, Int32 fontsize); + virtual void Add2TextWColor( const char *str1, pyColor& textcolor1,const char *str2, pyColor& textcolor2, UInt32 inheritalpha); + virtual void Add2TextWColorW( std::wstring str1, pyColor& textcolor1, std::wstring str2, pyColor& textcolor2, UInt32 inheritalpha); + virtual Int16 AddStringInBox( const char *string, UInt32 min_width, UInt32 min_height ); + virtual Int16 AddStringInBoxW( std::wstring string, UInt32 min_width, UInt32 min_height ); + virtual void ScrollToBegin( void ); + virtual void ScrollToEnd( void ); + virtual void SetScrollPos( Int32 pos ); + virtual Int32 GetScrollPos( void ); + virtual Int32 GetScrollRange( void ); + + virtual void LockList( void ); + virtual void UnlockList( void ); + + // To create tree branches, call AddBranch() with a name, then add elements as usual, including new sub-branches + // via additional AddBranch() calls. Call CloseBranch() to stop writing elements to that branch. + void AddBranch( const char *name, hsBool initiallyOpen ); + void AddBranchW( std::wstring name, hsBool initiallyOpen ); + void CloseBranch( void ); + + void RemoveSelection( Int32 item ); + void AddSelection( Int32 item ); + PyObject* GetSelectionList(); + PyObject* GetBranchList(); + + void AllowNoSelect(); + void DisallowNoSelect(); + +}; + +#endif // _pyGUIControlListBox_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBoxGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBoxGlue.cpp new file mode 100644 index 00000000..bd47c7a1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlListBoxGlue.cpp @@ -0,0 +1,552 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlListBox.h" +#include "pyImage.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlListBox, pyGUIControlListBox); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlListBox, pyGUIControlListBox) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlListBox) + +PYTHON_INIT_DEFINITION(ptGUIControlListBox, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlListBox, clickable, Clickable) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlListBox, unclickable, Unclickable) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlListBox, getSelection) +{ + return PyLong_FromLong(self->fThis->GetSelection()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, setSelection, args) +{ + long selectionIndex; + if (!PyArg_ParseTuple(args, "l", &selectionIndex)) + { + PyErr_SetString(PyExc_TypeError, "setSelection expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSelection(selectionIndex); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlListBox, refresh, Refresh) + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, removeElement, args) +{ + unsigned short index; + if (!PyArg_ParseTuple(args, "h", &index)) + { + PyErr_SetString(PyExc_TypeError, "removeElement expects an unsigned short"); + PYTHON_RETURN_ERROR; + } + self->fThis->RemoveElement(index); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlListBox, clearAllElements, ClearAllElements) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlListBox, getNumElements) +{ + return PyInt_FromLong(self->fThis->GetNumElements()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, setElement, args) +{ + unsigned short index; + char* text; + if (!PyArg_ParseTuple(args, "hs", &index, &text)) + { + PyErr_SetString(PyExc_TypeError, "setElement expects an unsigned short and a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetElement(index, text); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, setElementW, args) +{ + unsigned short index; + wchar_t* text; + if (!PyArg_ParseTuple(args, "hu", &index, &text)) + { + PyErr_SetString(PyExc_TypeError, "setElementW expects an unsigned short and a unicode string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetElementW(index, text); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, getElement, args) +{ + unsigned short index; + if (!PyArg_ParseTuple(args, "h", &index)) + { + PyErr_SetString(PyExc_TypeError, "getElement expects an unsigned short"); + PYTHON_RETURN_ERROR; + } + return PyString_FromString(self->fThis->GetElement(index).c_str()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, getElementW, args) +{ + unsigned short index; + if (!PyArg_ParseTuple(args, "h", &index)) + { + PyErr_SetString(PyExc_TypeError, "getElementW expects an unsigned short"); + PYTHON_RETURN_ERROR; + } + std::wstring retVal = self->fThis->GetElementW(index); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, setStringJustify, args) +{ + unsigned short index; + unsigned long justify; + if (!PyArg_ParseTuple(args, "hl", &index, &justify)) + { + PyErr_SetString(PyExc_TypeError, "setStringJustify expects an unsigned short and an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetStringJustify(index, justify); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, addString, args) +{ + char* text; + if (!PyArg_ParseTuple(args, "s", &text)) + { + PyErr_SetString(PyExc_TypeError, "addString expects a string"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->AddString(text)); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, addStringW, args) +{ + wchar_t* text; + if (!PyArg_ParseTuple(args, "u", &text)) + { + PyErr_SetString(PyExc_TypeError, "addStringW expects a unicode string"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->AddStringW(text)); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, findString, args) +{ + char* text; + if (!PyArg_ParseTuple(args, "s", &text)) + { + PyErr_SetString(PyExc_TypeError, "findString expects a string"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->FindString(text)); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, findStringW, args) +{ + wchar_t* text; + if (!PyArg_ParseTuple(args, "u", &text)) + { + PyErr_SetString(PyExc_TypeError, "findStringW expects a unicode string"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->FindStringW(text)); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, addImage, args) +{ + PyObject* imageObj = NULL; + char respectAlphaFlag; + if (!PyArg_ParseTuple(args, "Ob", &imageObj, &respectAlphaFlag)) + { + PyErr_SetString(PyExc_TypeError, "addImage expects a ptImage and a boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyImage::Check(imageObj)) + { + PyErr_SetString(PyExc_TypeError, "addImage expects a ptImage and a boolean"); + PYTHON_RETURN_ERROR; + } + pyImage* image = pyImage::ConvertFrom(imageObj); + return PyInt_FromLong(self->fThis->AddImage(*image, respectAlphaFlag != 0)); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, addImageInBox, args) +{ + PyObject* imageObj = NULL; + unsigned long x, y, width, height; + char respectAlphaFlag; + if (!PyArg_ParseTuple(args, "Ollllb", &imageObj, &x, &y, &width, &height, &respectAlphaFlag)) + { + PyErr_SetString(PyExc_TypeError, "addImageInBox expects a ptImage, four unsigned longs, and a boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyImage::Check(imageObj)) + { + PyErr_SetString(PyExc_TypeError, "addImageInBox expects a ptImage, four unsigned longs, and a boolean"); + PYTHON_RETURN_ERROR; + } + pyImage* image = pyImage::ConvertFrom(imageObj); + return PyInt_FromLong(self->fThis->AddImageInBox(*image, x, y, width, height, respectAlphaFlag != 0)); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, addStringWithColor, args) +{ + char* text; + PyObject* colorObj = NULL; + unsigned long inheritAlpha; + if (!PyArg_ParseTuple(args, "sOl", &text, &colorObj, &inheritAlpha)) + { + PyErr_SetString(PyExc_TypeError, "addStringWithColor expects a string, ptColor, and an unsigned long"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "addStringWithColor expects a string, ptColor, and an unsigned long"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + return PyInt_FromLong(self->fThis->AddTextWColor(text, *color, inheritAlpha)); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, addStringWithColorWithSize, args) +{ + char* text; + PyObject* colorObj = NULL; + unsigned long inheritAlpha; + long textSize; + if (!PyArg_ParseTuple(args, "sOll", &text, &colorObj, &inheritAlpha, &textSize)) + { + PyErr_SetString(PyExc_TypeError, "addStringWithColorWithSize expects a string, ptColor, an unsigned long, and a long"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "addStringWithColorWithSize expects a string, ptColor, an unsigned long, and a long"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + return PyInt_FromLong(self->fThis->AddTextWColorWSize(text, *color, inheritAlpha, textSize)); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, add2StringsWithColors, args) +{ + char* text1; + PyObject* color1Obj = NULL; + char* text2; + PyObject* color2Obj = NULL; + unsigned long inheritAlpha; + if (!PyArg_ParseTuple(args, "sOsOl", &text1, &color1Obj, &text2, &color2Obj, &inheritAlpha)) + { + PyErr_SetString(PyExc_TypeError, "addStringWithColor expects a string, ptColor, string, ptColor, and an unsigned long"); + PYTHON_RETURN_ERROR; + } + if ((!pyColor::Check(color2Obj)) || (!pyColor::Check(color2Obj))) + { + PyErr_SetString(PyExc_TypeError, "addStringWithColor expects a string, ptColor, string, ptColor, and an unsigned long"); + PYTHON_RETURN_ERROR; + } + pyColor* color1 = pyColor::ConvertFrom(color1Obj); + pyColor* color2 = pyColor::ConvertFrom(color2Obj); + self->fThis->Add2TextWColor(text1, *color1, text2, *color2, inheritAlpha); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, addStringInBox, args) +{ + char* text; + unsigned long minWidth, minHeight; + if (!PyArg_ParseTuple(args, "sll", &text, &minWidth, &minHeight)) + { + PyErr_SetString(PyExc_TypeError, "addStringInBox expects a string and two unsigned longs"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->AddStringInBox(text, minWidth, minHeight)); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlListBox, scrollToBegin, ScrollToBegin) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlListBox, scrollToEnd, ScrollToEnd) + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, setScrollPos, args) +{ + short pos; + if (!PyArg_ParseTuple(args, "h", &pos)) + { + PyErr_SetString(PyExc_TypeError, "setScrollPos expects a short"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetScrollPos(pos); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlListBox, getScrollPos) +{ + return PyLong_FromLong(self->fThis->GetScrollPos()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlListBox, getScrollRange) +{ + return PyLong_FromLong(self->fThis->GetScrollRange()); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlListBox, lock, LockList) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlListBox, unlock, UnlockList) + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, addBranch, args) +{ + char* name; + char initiallyOpen; + if (!PyArg_ParseTuple(args, "sb", &name, &initiallyOpen)) + { + PyErr_SetString(PyExc_TypeError, "addBranch expects a string and a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddBranch(name, initiallyOpen != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, addBranchW, args) +{ + PyObject* textObj; + char initiallyOpen; + if (!PyArg_ParseTuple(args, "Ob", &textObj, &initiallyOpen)) + { + PyErr_SetString(PyExc_TypeError, "addBranchW expects a unicode string and a boolean"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* name = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, name, strLen); + name[strLen] = L'\0'; + self->fThis->AddBranchW(name, initiallyOpen != 0); + delete [] name; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* name = PyString_AsString(textObj); + self->fThis->AddBranch(name, initiallyOpen != 0); + PYTHON_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_TypeError, "addBranchW expects a unicode string and a boolean"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlListBox, closeBranch, CloseBranch) + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, removeSelection, args) +{ + long itemIdx; + if (!PyArg_ParseTuple(args, "l", &itemIdx)) + { + PyErr_SetString(PyExc_TypeError, "removeSelection expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->RemoveSelection(itemIdx); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, addSelection, args) +{ + long itemIdx; + if (!PyArg_ParseTuple(args, "l", &itemIdx)) + { + PyErr_SetString(PyExc_TypeError, "addSelection expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddSelection(itemIdx); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlListBox, getSelectionList) +{ + return self->fThis->GetSelectionList(); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, addImageAndSwatchesInBox, args) +{ + PyObject* imageObj = NULL; + unsigned long x, y, width, height; + char respectAlpha; + PyObject* primaryObj = NULL; + PyObject* secondaryObj = NULL; + if (!PyArg_ParseTuple(args, "OllllbOO", &imageObj, &x, &y, &width, &height, &respectAlpha, &primaryObj, &secondaryObj)) + { + PyErr_SetString(PyExc_TypeError, "addImageAndSwatchesInBox expects a ptImage, four unsigned longs, a boolean, and two ptColor objects"); + PYTHON_RETURN_ERROR; + } + if ((!pyImage::Check(imageObj)) || (!pyColor::Check(primaryObj)) || (!pyColor::Check(secondaryObj))) + { + PyErr_SetString(PyExc_TypeError, "addImageAndSwatchesInBox expects a ptImage, four unsigned longs, a boolean, and two ptColor objects"); + PYTHON_RETURN_ERROR; + } + pyImage* image = pyImage::ConvertFrom(imageObj); + pyColor* primary = pyColor::ConvertFrom(primaryObj); + pyColor* secondary = pyColor::ConvertFrom(secondaryObj); + return PyInt_FromLong(self->fThis->AddImageAndSwatchesInBox(*image, x, y, width, height, respectAlpha != 0, *primary, *secondary)); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, setGlobalSwatchSize, args) +{ + unsigned long swatchSize; + if (!PyArg_ParseTuple(args, "l", &swatchSize)) + { + PyErr_SetString(PyExc_TypeError, "setGlobalSwatchSize expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSwatchSize(swatchSize); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlListBox, setGlobalSwatchEdgeOffset, args) +{ + unsigned long offset; + if (!PyArg_ParseTuple(args, "l", &offset)) + { + PyErr_SetString(PyExc_TypeError, "setGlobalSwatchEdgeOffset expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSwatchEdgeOffset(offset); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlListBox, allowNoSelect, AllowNoSelect) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlListBox, disallowNoSelect, DisallowNoSelect) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlListBox, getBranchList) +{ + return self->fThis->GetBranchList(); +} + +PYTHON_START_METHODS_TABLE(ptGUIControlListBox) + PYTHON_BASIC_METHOD(ptGUIControlListBox, clickable, "Sets this listbox to be clickable by the user."), + PYTHON_BASIC_METHOD(ptGUIControlListBox, unclickable, "Makes this listbox not clickable by the user.\n" + "Useful when just displaying a list that is not really selectable."), + PYTHON_METHOD_NOARGS(ptGUIControlListBox, getSelection, "Returns the currently selected list item in the listbox."), + PYTHON_METHOD(ptGUIControlListBox, setSelection, "Params: selectionIndex\nSets the current selection in the listbox."), + PYTHON_BASIC_METHOD(ptGUIControlListBox, refresh, "Refresh the display of the listbox (after updating contents)."), + PYTHON_METHOD(ptGUIControlListBox, removeElement, "Params: index\nRemoves element at 'index' in the listbox."), + PYTHON_BASIC_METHOD(ptGUIControlListBox, clearAllElements, "Removes all the items from the listbox, making it empty."), + PYTHON_METHOD_NOARGS(ptGUIControlListBox, getNumElements, "Return the number of items in the listbox."), + PYTHON_METHOD(ptGUIControlListBox, setElement, "Params: index,text\nSet a particular item in the listbox to a string."), + PYTHON_METHOD(ptGUIControlListBox, setElementW, "Params: index,text\nUnicode version of setElement."), + PYTHON_METHOD(ptGUIControlListBox, getElement, "Params: index\nGet the string of the item at 'index' in the listbox."), + PYTHON_METHOD(ptGUIControlListBox, getElementW, "Params: index\nUnicode version of getElement."), + PYTHON_METHOD(ptGUIControlListBox, setStringJustify, "Params: index,justify\nSets the text justification"), + PYTHON_METHOD(ptGUIControlListBox, addString, "Params: text\nAppends a list item 'text' to the listbox."), + PYTHON_METHOD(ptGUIControlListBox, addStringW, "Params: text\nUnicode version of addString."), + PYTHON_METHOD(ptGUIControlListBox, findString, "Params: text\nFinds and returns the index of the item that matches 'text' in the listbox."), + PYTHON_METHOD(ptGUIControlListBox, findStringW, "Params: text\nUnicode version of findString."), + PYTHON_METHOD(ptGUIControlListBox, addImage, "Params: image,respectAlphaFlag\nAppends an image item to the listbox"), + PYTHON_METHOD(ptGUIControlListBox, addImageInBox, "Params: image,x,y,width,height,respectAlpha\nAppends an image item to the listbox, centering within the box dimension."), + PYTHON_METHOD(ptGUIControlListBox, addStringWithColor, "Params: text,color,inheritAlpha\nAdds a colored string to the list box"), + PYTHON_METHOD(ptGUIControlListBox, addStringWithColorWithSize, "Params: text,color,inheritAlpha,fontsize\nAdds a text list item with a color and different font size"), + PYTHON_METHOD(ptGUIControlListBox, add2StringsWithColors, "Params: text1,color1,text2,color2,respectAlpha\nDoesn't work right - DONT USE"), + PYTHON_METHOD(ptGUIControlListBox, addStringInBox, "Params: text,min_width,min_height\nAdds a text list item that has a minimum width and height"), + PYTHON_BASIC_METHOD(ptGUIControlListBox, scrollToBegin, "Scrolls the listbox to the beginning of the list"), + PYTHON_BASIC_METHOD(ptGUIControlListBox, scrollToEnd, "Scrolls the listbox to the end of the list"), + PYTHON_METHOD(ptGUIControlListBox, setScrollPos, "Params: pos\nSets the scroll position of the listbox to 'pos'"), + PYTHON_METHOD_NOARGS(ptGUIControlListBox, getScrollPos, "Returns the current scroll position in the listbox."), + PYTHON_METHOD_NOARGS(ptGUIControlListBox, getScrollRange, "Returns the max scroll position"), + PYTHON_BASIC_METHOD(ptGUIControlListBox, lock, "Locks the updates to a listbox, so a number of operations can be performed\n" + "NOTE: an unlock() call must be made before the next lock() can be."), + PYTHON_BASIC_METHOD(ptGUIControlListBox, unlock, "Unlocks updates to a listbox and does any saved up changes"), + PYTHON_METHOD(ptGUIControlListBox, addBranch, "Params: name,initiallyOpen\nUNKNOWN"), + PYTHON_METHOD(ptGUIControlListBox, addBranchW, "Params: name,initiallyOpen\nUnicode version of addBranch"), + PYTHON_BASIC_METHOD(ptGUIControlListBox, closeBranch, "UNKNOWN"), + PYTHON_METHOD(ptGUIControlListBox, removeSelection, "Params: item\nRemoves item from selection list"), + PYTHON_METHOD(ptGUIControlListBox, addSelection, "Params: item\nAdds item to selection list"), + PYTHON_METHOD(ptGUIControlListBox, getSelectionList, "Returns the current selection list"), + PYTHON_METHOD(ptGUIControlListBox, addImageAndSwatchesInBox, "Params: image,x,y,width,height,respectAlpha,primary,secondary\nAdd the image and color swatches to the list"), + PYTHON_METHOD(ptGUIControlListBox, setGlobalSwatchSize, "Params: size\nSets the size of the color swatches"), + PYTHON_METHOD(ptGUIControlListBox, setGlobalSwatchEdgeOffset, "Params: offset\nSets the edge offset of the color swatches"), + PYTHON_BASIC_METHOD(ptGUIControlListBox, allowNoSelect, "Allows the listbox to have no selection"), + PYTHON_BASIC_METHOD(ptGUIControlListBox, disallowNoSelect, "The listbox must always have a selection"), + PYTHON_METHOD_NOARGS(ptGUIControlListBox, getBranchList, "get a list of branches in this list (index,isShowingChildren)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlListBox, pyGUIControl, "Params: ctrlKey\nPlasma GUI Control List Box class"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlListBox::New(pyKey& gckey) +{ + ptGUIControlListBox *newObj = (ptGUIControlListBox*)ptGUIControlListBox_type.tp_new(&ptGUIControlListBox_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlListBox::New(plKey objkey) +{ + ptGUIControlListBox *newObj = (ptGUIControlListBox*)ptGUIControlListBox_type.tp_new(&ptGUIControlListBox_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlListBox, pyGUIControlListBox) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlListBox, pyGUIControlListBox) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlListBox::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlListBox); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEdit.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEdit.cpp new file mode 100644 index 00000000..fdf29eb6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEdit.cpp @@ -0,0 +1,544 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "../pfGameGUIMgr/pfGUIMultiLineEditCtrl.h" + +#include "pyGUIControlMultiLineEdit.h" +#include "pyColor.h" + +pyGUIControlMultiLineEdit::pyGUIControlMultiLineEdit(pyKey& gckey) : pyGUIControl(gckey) +{ +} + +pyGUIControlMultiLineEdit::pyGUIControlMultiLineEdit(plKey objkey) : pyGUIControl(objkey) +{ +} + +hsBool pyGUIControlMultiLineEdit::IsGUIControlMultiLineEdit(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIMultiLineEditCtrl::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + + +void pyGUIControlMultiLineEdit::SetScrollPosition( Int32 topLine ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->SetScrollPosition(topLine); + } + } +} + +void pyGUIControlMultiLineEdit::MoveCursor( Int32 dir) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->MoveCursor((enum pfGUIMultiLineEditCtrl::Direction)dir); + } + } +} + + +void pyGUIControlMultiLineEdit::ClearBuffer( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->ClearBuffer(); + } + } +} + +void pyGUIControlMultiLineEdit::SetText( const char *asciiText ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->SetBuffer(asciiText); + } + } +} + +void pyGUIControlMultiLineEdit::SetTextW( const wchar_t *asciiText ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->SetBuffer(asciiText); + } + } +} + +const char* pyGUIControlMultiLineEdit::GetText( void ) +{ + // up to the caller to free the string... but when? + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + const char* text = pbmod->GetNonCodedBuffer(); + // convert string to a PyObject (which also copies the string) + return text; + } + } + // return None on error + return nil; +} + +const wchar_t* pyGUIControlMultiLineEdit::GetTextW( void ) +{ + // up to the caller to free the string... but when? + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + const wchar_t* text = pbmod->GetNonCodedBufferW(); + // convert string to a PyObject (which also copies the string) + return text; + } + } + // return None on error + return nil; +} + +// +// set the encoded buffer - encoded with style and color +// +void pyGUIControlMultiLineEdit::SetEncodedBuffer( PyObject* buffer_object ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + // something to do here... later + UInt8* daBuffer = nil; + int length; + PyObject_AsReadBuffer( buffer_object, (const void**)&daBuffer, &length); + if ( daBuffer != nil ) + { + // don't alter the user's buffer... but into a copy of our own + UInt8* altBuffer = TRACKED_NEW UInt8[length]; +// =====> temp>> change 0xFEs back into '\0's + int i; + for ( i=0 ; i temp>> change 0xFEs back into '\0's + pbmod->SetBuffer( altBuffer, length ); + delete [] altBuffer; + + pbmod->SetCursorToLoc(0); + pbmod->SetScrollPosition(0); + } + } + } +} + +void pyGUIControlMultiLineEdit::SetEncodedBufferW( PyObject* buffer_object ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + // something to do here... later + UInt16* daBuffer = nil; + int length; + PyObject_AsReadBuffer( buffer_object, (const void**)&daBuffer, &length); + if ( daBuffer != nil ) + { + // don't alter the user's buffer... but into a copy of our own + UInt16* altBuffer = TRACKED_NEW UInt16[length]; + // =====> temp>> change 0xFFFEs back into '\0's + int i; + for ( i=0 ; i temp>> change 0xFFFEs back into '\0's + pbmod->SetBuffer( altBuffer, length ); + delete [] altBuffer; + + pbmod->SetCursorToLoc(0); + pbmod->SetScrollPosition(0); + } + } + } +} + +const char* pyGUIControlMultiLineEdit::GetEncodedBuffer() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + UInt32 length; + UInt8* daBuffer = pbmod->GetCodedBuffer( length ); + if ( daBuffer ) + { + UInt8* altBuffer = TRACKED_NEW UInt8[length+1]; +// =====> temp>> to get rid of '\0's (change into 0xFEs) + int i; + for ( i=0 ; i temp>> to get rid of '\0's (change into 0xFEs) + delete [] daBuffer; + // May have to use a string object instead of a buffer + // (String makes its own copy of the string) + return (const char*)altBuffer; + } + } + } + // return None on error + return nil; +} + +const wchar_t* pyGUIControlMultiLineEdit::GetEncodedBufferW() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + UInt32 length; + UInt16* daBuffer = pbmod->GetCodedBufferW( length ); + if ( daBuffer ) + { + UInt16* altBuffer = TRACKED_NEW UInt16[length+1]; + // =====> temp>> to get rid of '\0's (change into 0xFFFEs) + int i; + for ( i=0 ; i temp>> to get rid of '\0's (change into 0xFEs) + // May have to use a string object instead of a buffer + // (String makes its own copy of the string) + return (const wchar_t*)altBuffer; + } + } + } + // return None on error + return nil; +} + +UInt32 pyGUIControlMultiLineEdit::GetBufferSize() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + return pbmod->GetBufferSize(); + } + } + return 0; +} + + + +void pyGUIControlMultiLineEdit::InsertChar( char c ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->InsertChar(c); + } + } +} + +void pyGUIControlMultiLineEdit::InsertCharW( wchar_t c ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->InsertChar(c); + } + } +} + +void pyGUIControlMultiLineEdit::InsertString( const char *string ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->InsertString(string); + } + } +} + +void pyGUIControlMultiLineEdit::InsertStringW( const wchar_t *string ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->InsertString(string); + } + } +} + +void pyGUIControlMultiLineEdit::InsertColor( pyColor& color ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->InsertColor(color.getColor()); + } + } +} + +void pyGUIControlMultiLineEdit::InsertStyle( UInt8 fontStyle ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->InsertStyle(fontStyle); + } + } +} + +void pyGUIControlMultiLineEdit::DeleteChar( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + { + pbmod->DeleteChar(); + } + } +} + +void pyGUIControlMultiLineEdit::Lock( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->Lock(); + } +} + +void pyGUIControlMultiLineEdit::Unlock( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->Unlock(); + } +} + +hsBool pyGUIControlMultiLineEdit::IsLocked( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + return pbmod->IsLocked(); + } + // don't know... must false then + return false; +} + +void pyGUIControlMultiLineEdit::Clickable( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->ClearFlag(pfGUIControlMod::kIntangible); + } +} + +void pyGUIControlMultiLineEdit::Unclickable( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->SetFlag(pfGUIControlMod::kIntangible); + } +} + +void pyGUIControlMultiLineEdit::SetBufferLimit(Int32 limit) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->SetBufferLimit(limit); + } +} + +Int32 pyGUIControlMultiLineEdit::GetBufferLimit() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + return pbmod->GetBufferLimit(); + } + return 0; +} + +void pyGUIControlMultiLineEdit::EnableScrollControl() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->SetScrollEnable(true); + } +} + +void pyGUIControlMultiLineEdit::DisableScrollControl() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->SetScrollEnable(false); + } +} + +void pyGUIControlMultiLineEdit::DeleteLinesFromTop( int lines ) +{ + if (fGCkey) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->DeleteLinesFromTop(lines); + } +} + +UInt32 pyGUIControlMultiLineEdit::GetFontSize() +{ + if (fGCkey) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + return pbmod->GetFontSize(); + } + return 12; // something relatively sane +} + +void pyGUIControlMultiLineEdit::SetFontSize( UInt32 fontsize ) +{ + if (fGCkey) + { + // get the pointer to the modifier + pfGUIMultiLineEditCtrl* pbmod = pfGUIMultiLineEditCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pbmod ) + pbmod->SetFontSize((UInt8)fontsize); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEdit.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEdit.h new file mode 100644 index 00000000..8fbb9c1d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEdit.h @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlMultiLineEdit_h_ +#define _pyGUIControlMultiLineEdit_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlMultiLineEdit - a wrapper class to provide interface to modifier +// attached to a GUIControlMultiLineEdit +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" +#include "pyGUIControl.h" + +#include +#include "pyGlueHelpers.h" + +class pyColor; + +class pyGUIControlMultiLineEdit : public pyGUIControl +{ +protected: + pyGUIControlMultiLineEdit(): pyGUIControl() {} // used by python glue, do NOT call + pyGUIControlMultiLineEdit(pyKey& gckey); + pyGUIControlMultiLineEdit(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlMultiLineEdit); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlMultiLineEdit object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlMultiLineEdit); // converts a PyObject to a pyGUIControlMultiLineEdit (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaConstantsClasses(PyObject *m); + + static hsBool IsGUIControlMultiLineEdit(pyKey& gckey); + + virtual void Clickable( void ); + virtual void Unclickable( void ); + virtual void SetScrollPosition( Int32 topLine ); + virtual void MoveCursor( Int32 dir ); + virtual void ClearBuffer( void ); + virtual void SetText( const char *asciiText ); + virtual void SetTextW( const wchar_t *asciiText ); + virtual const char* GetText( void ); // returns a python string object + virtual const wchar_t* GetTextW( void ); + virtual void SetEncodedBuffer( PyObject* buffer_object ); + virtual void SetEncodedBufferW( PyObject* buffer_object ); + virtual const char* GetEncodedBuffer(); + virtual const wchar_t* GetEncodedBufferW(); + virtual UInt32 GetBufferSize(); + + virtual void SetBufferLimit(Int32 limit); + virtual Int32 GetBufferLimit(); + + virtual void InsertChar( char c ); + virtual void InsertCharW( wchar_t c ); + virtual void InsertString( const char *string ); + virtual void InsertStringW( const wchar_t *string ); + virtual void InsertColor( pyColor& color ); + virtual void InsertStyle( UInt8 fontStyle ); + virtual void DeleteChar( void ); + + virtual void Lock( void ); + virtual void Unlock( void ); + virtual hsBool IsLocked( void ); + + virtual void EnableScrollControl(); + virtual void DisableScrollControl(); + + virtual void DeleteLinesFromTop( int lines ); + + virtual UInt32 GetFontSize(); + virtual void SetFontSize( UInt32 fontsize ); +}; + +#endif // _pyGUIControlMultiLineEdit_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp new file mode 100644 index 00000000..a2e658fe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlMultiLineEditGlue.cpp @@ -0,0 +1,460 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlMultiLineEdit.h" +#include "pyEnum.h" +#include "pyColor.h" + +#include "../pfGameGUIMgr/pfGUIMultiLineEditCtrl.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlMultiLineEdit, pyGUIControlMultiLineEdit); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlMultiLineEdit, pyGUIControlMultiLineEdit) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlMultiLineEdit) + +PYTHON_INIT_DEFINITION(ptGUIControlMultiLineEdit, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlMultiLineEdit, clickable, Clickable) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlMultiLineEdit, unclickable, Unclickable) + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, setScrollPosition, args) +{ + long topLine; + if (!PyArg_ParseTuple(args, "l", &topLine)) + { + PyErr_SetString(PyExc_TypeError, "setScrollPosition expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetScrollPosition(topLine); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, moveCursor, args) +{ + long dir; + if (!PyArg_ParseTuple(args, "l", &dir)) + { + PyErr_SetString(PyExc_TypeError, "moveCursor expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->MoveCursor(dir); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlMultiLineEdit, clearBuffer, ClearBuffer) + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, setString, args) +{ + char* text; + if (!PyArg_ParseTuple(args, "s", &text)) + { + PyErr_SetString(PyExc_TypeError, "setString expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetText(text); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, setStringW, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "setStringW expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* temp = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, temp, strLen); + temp[strLen] = L'\0'; + self->fThis->SetTextW(temp); + delete [] temp; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* temp = PyString_AsString(textObj); + self->fThis->SetText(temp); + PYTHON_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_TypeError, "setStringW expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlMultiLineEdit, getString) +{ + return PyString_FromString(self->fThis->GetText()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlMultiLineEdit, getStringW) +{ + const wchar_t* text = self->fThis->GetTextW(); + return PyUnicode_FromWideChar(text, wcslen(text)); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, setEncodedBuffer, args) +{ + PyObject* bufferObj = NULL; + if (!PyArg_ParseTuple(args, "O", &bufferObj)) + { + PyErr_SetString(PyExc_TypeError, "setEncodedBuffer expects a python buffer object"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetEncodedBuffer(bufferObj); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, setEncodedBufferW, args) +{ + PyObject* bufferObj = NULL; + if (!PyArg_ParseTuple(args, "O", &bufferObj)) + { + PyErr_SetString(PyExc_TypeError, "setEncodedBufferW expects a python buffer object"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetEncodedBufferW(bufferObj); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlMultiLineEdit, getEncodedBuffer) +{ + const char* buffer = self->fThis->GetEncodedBuffer(); + PyObject* retVal = PyString_FromString(buffer); + delete [] buffer; + return retVal; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlMultiLineEdit, getEncodedBufferW) +{ + const wchar_t* text = self->fThis->GetEncodedBufferW(); + PyObject* retVal = PyUnicode_FromWideChar(text, wcslen(text)); + delete [] text; + return retVal; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlMultiLineEdit, getBufferSize) +{ + return PyLong_FromUnsignedLong(self->fThis->GetBufferSize()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, insertChar, args) +{ + char c; + if (!PyArg_ParseTuple(args, "c", &c)) + { + PyErr_SetString(PyExc_TypeError, "insertChar expects a single character"); + PYTHON_RETURN_ERROR; + } + self->fThis->InsertChar(c); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, insertCharW, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "insertCharW expects a single unicode character"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + if (strLen != 1) + { + PyErr_SetString(PyExc_TypeError, "insertCharW expects a single unicode character"); + PYTHON_RETURN_ERROR; + } + + wchar_t* temp = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, temp, strLen); + temp[strLen] = L'\0'; + self->fThis->InsertCharW(temp[0]); + delete [] temp; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* temp = PyString_AsString(textObj); + if (strlen(temp) != 1) + { + PyErr_SetString(PyExc_TypeError, "insertCharW expects a single unicode character"); + PYTHON_RETURN_ERROR; + } + self->fThis->InsertChar(temp[0]); + PYTHON_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_TypeError, "insertCharW expects a single unicode character"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, insertString, args) +{ + char* str; + if (!PyArg_ParseTuple(args, "s", &str)) + { + PyErr_SetString(PyExc_TypeError, "insertString expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->InsertString(str); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, insertStringW, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "insertStringW expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* temp = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, temp, strLen); + temp[strLen] = L'\0'; + self->fThis->InsertStringW(temp); + delete [] temp; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* temp = PyString_AsString(textObj); + self->fThis->InsertString(temp); + PYTHON_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_TypeError, "insertStringW expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, insertColor, args) +{ + PyObject* colorObj = NULL; + if (!PyArg_ParseTuple(args, "O", &colorObj)) + { + PyErr_SetString(PyExc_TypeError, "insertColor expects a ptColor"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "insertColor expects a ptColor"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + self->fThis->InsertColor(*color); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, insertStyle, args) +{ + char style; + if (!PyArg_ParseTuple(args, "b", &style)) + { + PyErr_SetString(PyExc_TypeError, "insertStyle expects a 8-bit integer"); + PYTHON_RETURN_ERROR; + } + self->fThis->InsertStyle(style); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlMultiLineEdit, deleteChar, DeleteChar) + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlMultiLineEdit, lock, Lock) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlMultiLineEdit, unlock, Unlock) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlMultiLineEdit, isLocked) +{ + PYTHON_RETURN_BOOL(self->fThis->IsLocked()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, setBufferLimit, args) +{ + long limit; + if (!PyArg_ParseTuple(args, "l", &limit)) + { + PyErr_SetString(PyExc_TypeError, "setBufferLimit expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetBufferLimit(limit); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlMultiLineEdit, getBufferLimit) +{ + return PyLong_FromLong(self->fThis->GetBufferLimit()); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlMultiLineEdit, enableScrollControl, EnableScrollControl) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIControlMultiLineEdit, disableScrollControl, DisableScrollControl) + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, deleteLinesFromTop, args) +{ + long lines; + if (!PyArg_ParseTuple(args, "l", &lines)) + { + PyErr_SetString(PyExc_TypeError, "deleteLinesFromTop expects an int"); + PYTHON_RETURN_ERROR; + } + self->fThis->DeleteLinesFromTop(lines); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlMultiLineEdit, getFontSize) +{ + return PyLong_FromUnsignedLong(self->fThis->GetFontSize()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlMultiLineEdit, setFontSize, args) +{ + unsigned long fontSize; + if (!PyArg_ParseTuple(args, "l", &fontSize)) + { + PyErr_SetString(PyExc_TypeError, "setFontSize expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetFontSize(fontSize); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptGUIControlMultiLineEdit) + PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, clickable, "Sets this listbox to be clickable by the user."), + PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, unclickable, "Makes this listbox not clickable by the user.\n" + "Useful when just displaying a list that is not really selectable."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, setScrollPosition, "Params: topLine\nSets the what line is the top line."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, moveCursor, "Params: direction\nMove the cursor in the specified direction (see PtGUIMultiLineDirection)"), + PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, clearBuffer, "Clears all text from the multi-line edit control."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, setString, "Params: asciiText\nSets the multi-line edit control string."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, setStringW, "Params: unicodeText\nUnicode version of setString."), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getString, "Gets the string of the edit control."), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getStringW, "Unicode version of getString."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, setEncodedBuffer, "Params: bufferObject\nSets the edit control to the encoded buffer in the python buffer object. Do NOT use with a result from getEncodedBufferW."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, setEncodedBufferW, "Params: bufferObject\nUnicode version of setEncodedBuffer. Do NOT use with a result from getEncodedBuffer."), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getEncodedBuffer, "Returns the encoded buffer in a python buffer object. Do NOT use result with setEncodedBufferW."), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getEncodedBufferW, "Unicode version of getEncodedBuffer. Do NOT use result with setEncodedBuffer."), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getBufferSize, "Returns the size of the buffer"), + PYTHON_METHOD(ptGUIControlMultiLineEdit, insertChar, "Params: c\nInserts a character at the current cursor position."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, insertCharW, "Params: c\nUnicode version of insertChar."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, insertString, "Params: string\nInserts a string at the current cursor position."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, insertStringW, "Params: string\nUnicode version of insertString"), + PYTHON_METHOD(ptGUIControlMultiLineEdit, insertColor, "Params: color\nInserts an encoded color object at the current cursor position.\n" + "'color' is a ptColor object."), + PYTHON_METHOD(ptGUIControlMultiLineEdit, insertStyle, "Params: style\nInserts an encoded font style at the current cursor position."), + PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, deleteChar, "Deletes a character at the current cursor position."), + PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, lock, "Locks the multi-line edit control so the user cannot make changes."), + PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, unlock, "Unlocks the multi-line edit control so that the user can make changes."), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, isLocked, "Is the multi-line edit control locked? Returns 1 if true otherwise returns 0"), + PYTHON_METHOD(ptGUIControlMultiLineEdit, setBufferLimit, "Params: bufferLimit\nSets the buffer max for the editbox"), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getBufferLimit, "Returns the current buffer limit"), + PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, enableScrollControl, "Enables the scroll control if there is one"), + PYTHON_BASIC_METHOD(ptGUIControlMultiLineEdit, disableScrollControl, "Disables the scroll control if there is one"), + PYTHON_METHOD(ptGUIControlMultiLineEdit, deleteLinesFromTop, "Params: numLines\nDeletes the specified number of lines from the top of the text buffer"), + PYTHON_METHOD_NOARGS(ptGUIControlMultiLineEdit, getFontSize, "Returns the current default font size"), + PYTHON_METHOD(ptGUIControlMultiLineEdit, setFontSize, "Params: fontSize\nSets the default font size for the edit control"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlMultiLineEdit, pyGUIControl, "Params: ctrlKey\nPlasma GUI Control Multi-line edit class"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlMultiLineEdit::New(pyKey& gckey) +{ + ptGUIControlMultiLineEdit *newObj = (ptGUIControlMultiLineEdit*)ptGUIControlMultiLineEdit_type.tp_new(&ptGUIControlMultiLineEdit_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlMultiLineEdit::New(plKey objkey) +{ + ptGUIControlMultiLineEdit *newObj = (ptGUIControlMultiLineEdit*)ptGUIControlMultiLineEdit_type.tp_new(&ptGUIControlMultiLineEdit_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlMultiLineEdit, pyGUIControlMultiLineEdit) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlMultiLineEdit, pyGUIControlMultiLineEdit) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlMultiLineEdit::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlMultiLineEdit); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyGUIControlMultiLineEdit::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtGUIMultiLineDirection); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kLineStart, pfGUIMultiLineEditCtrl::kLineStart); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kLineEnd, pfGUIMultiLineEditCtrl::kLineEnd); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kBufferStart, pfGUIMultiLineEditCtrl::kBufferStart); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kBufferEnd, pfGUIMultiLineEditCtrl::kBufferEnd); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kOneBack, pfGUIMultiLineEditCtrl::kOneBack); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kOneForward, pfGUIMultiLineEditCtrl::kOneForward); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kOneWordBack, pfGUIMultiLineEditCtrl::kOneWordBack); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kOneWordForward, pfGUIMultiLineEditCtrl::kOneWordForward); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kOneLineUp, pfGUIMultiLineEditCtrl::kOneLineUp); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kOneLineDown, pfGUIMultiLineEditCtrl::kOneLineDown); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kPageUp, pfGUIMultiLineEditCtrl::kPageUp); + PYTHON_ENUM_ELEMENT(PtGUIMultiLineDirection, kPageDown, pfGUIMultiLineEditCtrl::kPageDown); + PYTHON_ENUM_END(m, PtGUIMultiLineDirection); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroup.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroup.cpp new file mode 100644 index 00000000..65f73a29 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroup.cpp @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "../pfGameGUIMgr/pfGUIRadioGroupCtrl.h" + +#include "pyGUIControlRadioGroup.h" + +pyGUIControlRadioGroup::pyGUIControlRadioGroup(pyKey& gckey) :pyGUIControl(gckey) +{ +} + +pyGUIControlRadioGroup::pyGUIControlRadioGroup(plKey objkey) : pyGUIControl(objkey) +{ +} + +hsBool pyGUIControlRadioGroup::IsGUIControlRadioGroup(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIRadioGroupCtrl::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + +Int32 pyGUIControlRadioGroup::GetValue( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIRadioGroupCtrl* prgmod = pfGUIRadioGroupCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( prgmod ) + return prgmod->GetValue(); + } + return -1; +} + +void pyGUIControlRadioGroup::SetValue( Int32 value ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIRadioGroupCtrl* prgmod = pfGUIRadioGroupCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( prgmod ) + prgmod->SetValue(value); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroup.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroup.h new file mode 100644 index 00000000..5e5c043e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroup.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlRadioGroup_h_ +#define _pyGUIControlRadioGroup_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlRadioGroup - a wrapper class to provide interface to modifier +// attached to a GUIControlRadioGroup +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" +#include "pyGUIControl.h" + +#include +#include "pyGlueHelpers.h" + +class pyGUIControlRadioGroup :public pyGUIControl +{ +protected: + pyGUIControlRadioGroup(): pyGUIControl() {} // for python glue only, do NOT call + pyGUIControlRadioGroup(pyKey& gckey); + pyGUIControlRadioGroup(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlRadioGroup); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlRadioGroup object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlRadioGroup); // converts a PyObject to a pyGUIControlRadioGroup (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlRadioGroup(pyKey& gckey); + + virtual Int32 GetValue( void ); + virtual void SetValue( Int32 value ); + +}; + +#endif // _pyGUIControlRadioGroup_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroupGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroupGlue.cpp new file mode 100644 index 00000000..40f15f34 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlRadioGroupGlue.cpp @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlRadioGroup.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlRadioGroup, pyGUIControlRadioGroup); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlRadioGroup, pyGUIControlRadioGroup) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlRadioGroup) + +PYTHON_INIT_DEFINITION(ptGUIControlRadioGroup, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlRadioGroup, getValue) +{ + return PyLong_FromLong(self->fThis->GetValue()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlRadioGroup, setValue, args) +{ + long val; + if (!PyArg_ParseTuple(args, "l", &val)) + { + PyErr_SetString(PyExc_TypeError, "setValue expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetValue(val); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptGUIControlRadioGroup) + PYTHON_METHOD_NOARGS(ptGUIControlRadioGroup, getValue, "Returns the current selection of the radio group."), + PYTHON_METHOD(ptGUIControlRadioGroup, setValue, "Params: value\nSets the current selection to 'value'"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlRadioGroup, pyGUIControl, "Params: ctrlKey\nPlasma GUI Control Radio Group class"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlRadioGroup::New(pyKey& gckey) +{ + ptGUIControlRadioGroup *newObj = (ptGUIControlRadioGroup*)ptGUIControlRadioGroup_type.tp_new(&ptGUIControlRadioGroup_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlRadioGroup::New(plKey objkey) +{ + ptGUIControlRadioGroup *newObj = (ptGUIControlRadioGroup*)ptGUIControlRadioGroup_type.tp_new(&ptGUIControlRadioGroup_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlRadioGroup, pyGUIControlRadioGroup) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlRadioGroup, pyGUIControlRadioGroup) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlRadioGroup::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlRadioGroup); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBox.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBox.cpp new file mode 100644 index 00000000..c9676558 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBox.cpp @@ -0,0 +1,200 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "../pfGameGUIMgr/pfGUITextBoxMod.h" +#include "../pfGameGUIMgr/pfGUIListElement.h" + +#include "pyGUIControlTextBox.h" +#include "pyColor.h" + +pyGUIControlTextBox::pyGUIControlTextBox(pyKey& gckey) : pyGUIControl(gckey) +{ + fOriginalColorScheme = nil; +} + +pyGUIControlTextBox::pyGUIControlTextBox(plKey objkey) : pyGUIControl(objkey) +{ + fOriginalColorScheme = nil; +} + + +hsBool pyGUIControlTextBox::IsGUIControlTextBox(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUITextBoxMod::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + + +std::string pyGUIControlTextBox::GetText() +{ + char *temp = hsWStringToString(GetTextW().c_str()); + std::string retVal = temp; + delete [] temp; + return retVal; +} + +std::wstring pyGUIControlTextBox::GetTextW() +{ + if (fGCkey) + { + // get the pointer to the modifier + pfGUITextBoxMod* ptbmod = pfGUITextBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( ptbmod ) + { + if ( ptbmod->GetText() ) + { + std::wstring retVal = ptbmod->GetText(); + return retVal; + } + } + } + // else if there is no string... fake one + return L""; +} + +void pyGUIControlTextBox::SetText( const char *text ) +{ + wchar_t *wText = hsStringToWString(text); + SetTextW(wText); + delete [] wText; +} + +void pyGUIControlTextBox::SetTextW( std::wstring text ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUITextBoxMod* ptbmod = pfGUITextBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( ptbmod ) + ptbmod->SetText(text.c_str()); + } +} + +void pyGUIControlTextBox::SetFontSize( UInt8 size ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUITextBoxMod* ptbmod = pfGUITextBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( ptbmod ) + { + pfGUIColorScheme* colorscheme = ptbmod->GetColorScheme(); + colorscheme->fFontSize = size; + ptbmod->UpdateColorScheme(); + } + } + +} + +void pyGUIControlTextBox::SetForeColor( pyColor& color ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUITextBoxMod* ptbmod = pfGUITextBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( ptbmod ) + { + pfGUIColorScheme* colorscheme = ptbmod->GetColorScheme(); + colorscheme->fForeColor = color.getColor(); + ptbmod->UpdateColorScheme(); + } + } + +} + +PyObject* pyGUIControlTextBox::GetForeColor() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUITextBoxMod* ptbmod = pfGUITextBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( ptbmod ) + { + pfGUIColorScheme* colorscheme = ptbmod->GetColorScheme(); + return pyColor::New(colorscheme->fForeColor); + } + } + PYTHON_RETURN_NONE; +} + +void pyGUIControlTextBox::SetBackColor( pyColor& color ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUITextBoxMod* ptbmod = pfGUITextBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( ptbmod ) + { + pfGUIColorScheme* colorscheme = ptbmod->GetColorScheme(); + colorscheme->fBackColor = color.getColor(); + } + } + +} + +void pyGUIControlTextBox::SetJustify( UInt8 justify ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUITextBoxMod* ptbmod = pfGUITextBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( ptbmod ) + { + // reset all the flags for justification first + ptbmod->ClearFlag(pfGUITextBoxMod::kCenterJustify); + ptbmod->ClearFlag(pfGUITextBoxMod::kRightJustify); + // then set the one they want + if ( justify == pfGUIListText::kCenter) + ptbmod->SetFlag(pfGUITextBoxMod::kCenterJustify); + else if ( justify == pfGUIListText::kRightJustify) + ptbmod->SetFlag(pfGUITextBoxMod::kRightJustify); + } + } +} + +UInt8 pyGUIControlTextBox::GetJustify() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUITextBoxMod* ptbmod = pfGUITextBoxMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( ptbmod ) + { + if ( ptbmod->HasFlag(pfGUITextBoxMod::kCenterJustify) ) + return pfGUIListText::kCenter; + if ( ptbmod->HasFlag(pfGUITextBoxMod::kRightJustify) ) + return pfGUIListText::kRightJustify; + } + } + return pfGUIListText::kLeftJustify; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBox.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBox.h new file mode 100644 index 00000000..6ebbdf5d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBox.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlTextBox_h_ +#define _pyGUIControlTextBox_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlTextBox - a wrapper class to provide interface to modifier +// attached to a GUIControlTextBox +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" +#include "pyGUIControl.h" +#include "../pfGameGUIMgr/pfGUIControlMod.h" + +#include +#include "pyGlueHelpers.h" + +class pyColor; + +class pyGUIControlTextBox : public pyGUIControl +{ +private: + pfGUIColorScheme* fOriginalColorScheme; + +protected: + pyGUIControlTextBox(): pyGUIControl() {} // for python glue only, do NOT call + pyGUIControlTextBox(pyKey& gckey); + pyGUIControlTextBox(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlTextBox); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlTextBox object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlTextBox); // converts a PyObject to a pyGUIControlTextBox (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlTextBox(pyKey& gckey); + + virtual void SetText( const char *text ); + virtual void SetTextW( std::wstring text ); + virtual std::string GetText(); + virtual std::wstring GetTextW(); + virtual void SetFontSize( UInt8 size ); + virtual void SetForeColor( pyColor& color ); + virtual void SetBackColor( pyColor& color ); + virtual void SetJustify( UInt8 justify ); + virtual UInt8 GetJustify(); + virtual PyObject* GetForeColor(); // returns pyColor +}; + +#endif // _pyGUIControlTextBox_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBoxGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBoxGlue.cpp new file mode 100644 index 00000000..f9d3f259 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlTextBoxGlue.cpp @@ -0,0 +1,225 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlTextBox.h" +#include "pyColor.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlTextBox, pyGUIControlTextBox); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlTextBox, pyGUIControlTextBox) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlTextBox) + +PYTHON_INIT_DEFINITION(ptGUIControlTextBox, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlTextBox, setString, args) +{ + char* text; + if (!PyArg_ParseTuple(args, "s", &text)) + { + PyErr_SetString(PyExc_TypeError, "setString expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetText(text); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlTextBox, setStringW, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "setStringW expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* temp = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, temp, strLen); + temp[strLen] = L'\0'; + self->fThis->SetTextW(temp); + delete [] temp; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* temp = PyString_AsString(textObj); + self->fThis->SetText(temp); + PYTHON_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_TypeError, "setStringW expects a unicode string"); + PYTHON_RETURN_ERROR; + } +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlTextBox, getString) +{ + return PyString_FromString(self->fThis->GetText().c_str()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlTextBox, getStringW) +{ + std::wstring retVal = self->fThis->GetTextW(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlTextBox, setFontSize, args) +{ + unsigned char fontSize; + if (!PyArg_ParseTuple(args, "b", &fontSize)) + { + PyErr_SetString(PyExc_TypeError, "setFontSize expects an unsigned 8-bit int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetFontSize(fontSize); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlTextBox, setForeColor, args) +{ + PyObject* colorObj = NULL; + if (!PyArg_ParseTuple(args, "O", &colorObj)) + { + PyErr_SetString(PyExc_TypeError, "setForeColor expects a ptColor"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "setForeColor expects a ptColor"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + self->fThis->SetForeColor(*color); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlTextBox, setBackColor, args) +{ + PyObject* colorObj = NULL; + if (!PyArg_ParseTuple(args, "O", &colorObj)) + { + PyErr_SetString(PyExc_TypeError, "setBackColor expects a ptColor"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "setBackColor expects a ptColor"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + self->fThis->SetBackColor(*color); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlTextBox, setStringJustify, args) +{ + unsigned char justify; + if (!PyArg_ParseTuple(args, "b", &justify)) + { + PyErr_SetString(PyExc_TypeError, "setStringJustify expects an unsigned 8-bit int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetJustify(justify); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlTextBox, getStringJustify) +{ + return PyInt_FromLong(self->fThis->GetJustify()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlTextBox, getForeColor) +{ + return self->fThis->GetForeColor(); +} + +PYTHON_START_METHODS_TABLE(ptGUIControlTextBox) + PYTHON_METHOD(ptGUIControlTextBox, setString, "Params: text\nSets the textbox string to 'text'"), + PYTHON_METHOD(ptGUIControlTextBox, setStringW, "Params: text\nUnicode version of setString"), + PYTHON_METHOD_NOARGS(ptGUIControlTextBox, getString, "Returns the string that the TextBox is set to (in case you forgot)"), + PYTHON_METHOD_NOARGS(ptGUIControlTextBox, getStringW, "Unicode version of getString"), + PYTHON_METHOD(ptGUIControlTextBox, setFontSize, "Params: size\nDon't use"), + PYTHON_METHOD(ptGUIControlTextBox, setForeColor, "Params: color\nSets the text forecolor to 'color', which is a ptColor object."), + PYTHON_METHOD(ptGUIControlTextBox, setBackColor, "Params: color\nSets the text backcolor to 'color', which is a ptColor object."), + PYTHON_METHOD(ptGUIControlTextBox, setStringJustify, "Params: justify\nSets current justify"), + PYTHON_METHOD_NOARGS(ptGUIControlTextBox, getStringJustify, "Returns current justify"), + PYTHON_METHOD_NOARGS(ptGUIControlTextBox, getForeColor, "Returns the current forecolor"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlTextBox, pyGUIControl, "Params: ctrlKey\nPlasma GUI Control Textbox class"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlTextBox::New(pyKey& gckey) +{ + ptGUIControlTextBox *newObj = (ptGUIControlTextBox*)ptGUIControlTextBox_type.tp_new(&ptGUIControlTextBox_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlTextBox::New(plKey objkey) +{ + ptGUIControlTextBox *newObj = (ptGUIControlTextBox*)ptGUIControlTextBox_type.tp_new(&ptGUIControlTextBox_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlTextBox, pyGUIControlTextBox) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlTextBox, pyGUIControlTextBox) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlTextBox::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlTextBox); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValue.cpp new file mode 100644 index 00000000..eac22cb4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValue.cpp @@ -0,0 +1,209 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" + +#include "../pfGameGUIMgr/pfGUIValueCtrl.h" +#include "../pfGameGUIMgr/pfGUIKnobCtrl.h" +#include "../pfGameGUIMgr/pfGUIUpDownPairMod.h" +#include "../pfGameGUIMgr/pfGUIProgressCtrl.h" + +#include "pyGUIControlValue.h" + +pyGUIControlValue::pyGUIControlValue(pyKey& gckey) : pyGUIControl(gckey) +{ +} + +pyGUIControlValue::pyGUIControlValue(plKey objkey) : pyGUIControl(objkey) +{ +} + +hsBool pyGUIControlValue::IsGUIControlValue(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIValueCtrl::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + + +hsScalar pyGUIControlValue::GetValue() +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIValueCtrl* pvcmod = pfGUIValueCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pvcmod ) + return pvcmod->GetCurrValue(); + } + return 0.0; +} + +void pyGUIControlValue::SetValue( hsScalar v ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIValueCtrl* pvcmod = pfGUIValueCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pvcmod ) + pvcmod->SetCurrValue(v); + } +} + +hsScalar pyGUIControlValue::GetMin( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIValueCtrl* pvcmod = pfGUIValueCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pvcmod ) + return pvcmod->GetMin(); + } + return 0.0; +} + +hsScalar pyGUIControlValue::GetMax( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIValueCtrl* pvcmod = pfGUIValueCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pvcmod ) + return pvcmod->GetMax(); + } + return 0.0; +} + +hsScalar pyGUIControlValue::GetStep( void ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIValueCtrl* pvcmod = pfGUIValueCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pvcmod ) + return pvcmod->GetStep(); + } + return 0.0; +} + +void pyGUIControlValue::SetRange( hsScalar min, hsScalar max ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIValueCtrl* pvcmod = pfGUIValueCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pvcmod ) + pvcmod->SetRange(min,max); + } +} + +void pyGUIControlValue::SetStep( hsScalar step ) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIValueCtrl* pvcmod = pfGUIValueCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pvcmod ) + pvcmod->SetStep(step); + } +} + + +/////////////////////////////////////////////////////////////////////////// +// +// Control Knob, which is identical to the ControlValue +// + +pyGUIControlKnob::pyGUIControlKnob(pyKey& gckey) : pyGUIControlValue(gckey) +{ +} + +pyGUIControlKnob::pyGUIControlKnob(plKey objkey) : pyGUIControlValue(objkey) +{ +} + +hsBool pyGUIControlKnob::IsGUIControlKnob(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIKnobCtrl::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// Control Up Down Pair, which is identical to the ControlValue +// + +pyGUIControlUpDownPair::pyGUIControlUpDownPair(pyKey& gckey) : pyGUIControlValue(gckey) +{ +} + +pyGUIControlUpDownPair::pyGUIControlUpDownPair(plKey objkey) : pyGUIControlValue(objkey) +{ +} + +hsBool pyGUIControlUpDownPair::IsGUIControlUpDownPair(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIUpDownPairMod::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + +/////////////////////////////////////////////////////////////////////////// +// +// Control Progress, which is identical to the ControlValue +// + +pyGUIControlProgress::pyGUIControlProgress(pyKey& gckey) : pyGUIControlValue(gckey) +{ +} + +pyGUIControlProgress::pyGUIControlProgress(plKey objkey) : pyGUIControlValue(objkey) +{ +} + +hsBool pyGUIControlProgress::IsGUIControlProgress(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIProgressCtrl::ConvertNoRef(gckey.getKey()->ObjectIsLoaded()) ) + return true; + return false; +} + +void pyGUIControlProgress::AnimateToPercentage(float percent) +{ + if ( fGCkey ) + { + // get the pointer to the modifier + pfGUIProgressCtrl* ppcmod = pfGUIProgressCtrl::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( ppcmod ) + { + ppcmod->AnimateToPercentage(percent); + } + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValue.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValue.h new file mode 100644 index 00000000..b8709bd8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValue.h @@ -0,0 +1,133 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIControlValue_h_ +#define _pyGUIControlValue_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIControlValue - a wrapper class to provide interface to modifier +// attached to a GUIControlValue (such as GUIKnob, GUIUpDownPair) +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" +#include "pyGUIControl.h" + +#include +#include "pyGlueHelpers.h" + +class pyGUIControlValue : public pyGUIControl +{ +protected: + pyGUIControlValue(): pyGUIControl() {} // for python glue only, do NOT call + pyGUIControlValue(pyKey& gckey); + pyGUIControlValue(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; + PYTHON_CLASS_NEW_FRIEND(ptGUIControlValue); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlValue object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlValue); // converts a PyObject to a pyGUIControlValue (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlValue(pyKey& gckey); + + virtual hsScalar GetValue(); + virtual void SetValue( hsScalar v ); + virtual hsScalar GetMin( void ); + virtual hsScalar GetMax( void ); + virtual hsScalar GetStep( void ); + virtual void SetRange( hsScalar min, hsScalar max ); + virtual void SetStep( hsScalar step ); +}; + +class pyGUIControlKnob : public pyGUIControlValue +{ +protected: + pyGUIControlKnob(): pyGUIControlValue() {} // for python glue only, do NOT call + pyGUIControlKnob(pyKey& gckey); + pyGUIControlKnob(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlKnob); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlKnob object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlKnob); // converts a PyObject to a pyGUIControlKnob (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlKnob(pyKey& gckey); +}; + +class pyGUIControlUpDownPair : public pyGUIControlValue +{ +protected: + pyGUIControlUpDownPair(): pyGUIControlValue() {} // for python glue only, do NOT call + pyGUIControlUpDownPair(pyKey& gckey); + pyGUIControlUpDownPair(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlUpDownPair); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlUpDownPair object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlUpDownPair); // converts a PyObject to a pyGUIControlUpDownPair (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlUpDownPair(pyKey& gckey); +}; + +class pyGUIControlProgress : public pyGUIControlValue +{ +protected: + pyGUIControlProgress(): pyGUIControlValue() {} // for python glue only, do NOT call + pyGUIControlProgress(pyKey& gckey); + pyGUIControlProgress(plKey objkey); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIControlProgress); + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIControlProgress object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIControlProgress); // converts a PyObject to a pyGUIControlProgress (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUIControlProgress(pyKey& gckey); + + void AnimateToPercentage(float percent); +}; + +#endif // _pyGUIControlValue_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValueGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValueGlue.cpp new file mode 100644 index 00000000..db99b3de --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIControlValueGlue.cpp @@ -0,0 +1,356 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIControlValue.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlValue, pyGUIControlValue); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlValue, pyGUIControlValue) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlValue) + +PYTHON_INIT_DEFINITION(ptGUIControlValue, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlValue, getValue) +{ + return PyFloat_FromDouble(self->fThis->GetValue()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlValue, setValue, args) +{ + float val; + if (!PyArg_ParseTuple(args, "f", &val)) + { + PyErr_SetString(PyExc_TypeError, "setValue expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetValue(val); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlValue, getMin) +{ + return PyFloat_FromDouble(self->fThis->GetMin()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlValue, getMax) +{ + return PyFloat_FromDouble(self->fThis->GetMax()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIControlValue, getStep) +{ + return PyFloat_FromDouble(self->fThis->GetStep()); +} + +PYTHON_METHOD_DEFINITION(ptGUIControlValue, setRange, args) +{ + float min, max; + if (!PyArg_ParseTuple(args, "ff", &min, &max)) + { + PyErr_SetString(PyExc_TypeError, "setRange expects two floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetRange(min, max); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlValue, setStep, args) +{ + float val; + if (!PyArg_ParseTuple(args, "f", &val)) + { + PyErr_SetString(PyExc_TypeError, "setStep expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetStep(val); + PYTHON_RETURN_NONE; +} + + +PYTHON_START_METHODS_TABLE(ptGUIControlValue) + PYTHON_METHOD_NOARGS(ptGUIControlValue, getValue, "Returns the current value of the control."), + PYTHON_METHOD(ptGUIControlValue, setValue, "Params: value\nSets the current value of the control."), + PYTHON_METHOD_NOARGS(ptGUIControlValue, getMin, "Returns the minimum of the control."), + PYTHON_METHOD_NOARGS(ptGUIControlValue, getMax, "Returns the maximum of the control."), + PYTHON_METHOD_NOARGS(ptGUIControlValue, getStep, "Returns the step increment of the control."), + PYTHON_METHOD(ptGUIControlValue, setRange, "Params: minimum,maximum\nSets the minimum and maximum range of the control."), + PYTHON_METHOD(ptGUIControlValue, setStep, "Params: step\nSets the step increment of the control."), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlValue, pyGUIControl, "Params: ctrlKey\nPlasma GUI Control Value class - knobs, spinners"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptGUIControlValue, pyGUIControlValue); + +// required functions for PyObject interoperability +PyObject *pyGUIControlValue::New(pyKey& gckey) +{ + ptGUIControlValue *newObj = (ptGUIControlValue*)ptGUIControlValue_type.tp_new(&ptGUIControlValue_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlValue::New(plKey objkey) +{ + ptGUIControlValue *newObj = (ptGUIControlValue*)ptGUIControlValue_type.tp_new(&ptGUIControlValue_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlValue, pyGUIControlValue) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlValue, pyGUIControlValue) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlValue::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlValue); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlKnob, pyGUIControlKnob); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlKnob, pyGUIControlKnob) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlKnob) + +PYTHON_INIT_DEFINITION(ptGUIControlKnob, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_START_METHODS_TABLE(ptGUIControlKnob) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlKnob, pyGUIControlValue, "Params: ctrlKey\nPlasma GUI control for knob"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlKnob::New(pyKey& gckey) +{ + ptGUIControlKnob *newObj = (ptGUIControlKnob*)ptGUIControlKnob_type.tp_new(&ptGUIControlKnob_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlKnob::New(plKey objkey) +{ + ptGUIControlKnob *newObj = (ptGUIControlKnob*)ptGUIControlKnob_type.tp_new(&ptGUIControlKnob_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlKnob, pyGUIControlKnob) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlKnob, pyGUIControlKnob) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlKnob::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlKnob); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlUpDownPair, pyGUIControlUpDownPair); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlUpDownPair, pyGUIControlUpDownPair) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlUpDownPair) + +PYTHON_INIT_DEFINITION(ptGUIControlUpDownPair, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_START_METHODS_TABLE(ptGUIControlUpDownPair) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlUpDownPair, pyGUIControlValue, "Params: ctrlKey\nPlasma GUI control for up/down pair"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlUpDownPair::New(pyKey& gckey) +{ + ptGUIControlUpDownPair *newObj = (ptGUIControlUpDownPair*)ptGUIControlUpDownPair_type.tp_new(&ptGUIControlUpDownPair_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlUpDownPair::New(plKey objkey) +{ + ptGUIControlUpDownPair *newObj = (ptGUIControlUpDownPair*)ptGUIControlUpDownPair_type.tp_new(&ptGUIControlUpDownPair_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlUpDownPair, pyGUIControlUpDownPair) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlUpDownPair, pyGUIControlUpDownPair) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlUpDownPair::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlUpDownPair); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIControlProgress, pyGUIControlProgress); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIControlProgress, pyGUIControlProgress) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIControlProgress) + +PYTHON_INIT_DEFINITION(ptGUIControlProgress, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptGUIControlProgress, animateToPercent, args) +{ + float percent; + if (!PyArg_ParseTuple(args, "f", &percent)) + { + PyErr_SetString(PyExc_TypeError, "animateToPercent expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->AnimateToPercentage(percent); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptGUIControlProgress) + PYTHON_METHOD(ptGUIControlProgress, animateToPercent, "Params: percent\nSets the value of the control and animates to that point."), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptGUIControlProgress, pyGUIControlValue, "Params: ctrlKey\nPlasma GUI control for progress bar"); + +// required functions for PyObject interoperability +PyObject *pyGUIControlProgress::New(pyKey& gckey) +{ + ptGUIControlProgress *newObj = (ptGUIControlProgress*)ptGUIControlProgress_type.tp_new(&ptGUIControlProgress_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIControlProgress::New(plKey objkey) +{ + ptGUIControlProgress *newObj = (ptGUIControlProgress*)ptGUIControlProgress_type.tp_new(&ptGUIControlProgress_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIControlProgress, pyGUIControlProgress) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIControlProgress, pyGUIControlProgress) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIControlProgress::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIControlProgress); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialog.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialog.cpp new file mode 100644 index 00000000..e7300b08 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialog.cpp @@ -0,0 +1,521 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" +#include "pyColor.h" +#include "cyPythonInterface.h" + +#include "../pfGameGUIMgr/pfGameGUIMgr.h" +#include "../pfGameGUIMgr/pfGUIDialogMod.h" + +#include "pyGUIDialog.h" + +// the rest of the controls +#include "pyGUIControlButton.h" +#include "pyGUIControlCheckBox.h" +#include "pyGUIControlEditBox.h" +#include "pyGUIControlListBox.h" +#include "pyGUIControlRadioGroup.h" +#include "pyGUIControlTextBox.h" +#include "pyGUIControlValue.h" +#include "pyGUIControlDynamicText.h" +#include "pyGUIControlMultiLineEdit.h" +#include "pyGUIPopUpMenu.h" +#include "pyGUIControlClickMap.h" + +// specific value controls +#include "../pfGameGUIMgr/pfGUIKnobCtrl.h" +#include "../pfGameGUIMgr/pfGUIUpDownPairMod.h" + +#include "../plInputCore/plInputInterface.h" + +pyGUIDialog::pyGUIDialog(pyKey& gckey) +{ + fGCkey = gckey.getKey(); +} + +pyGUIDialog::pyGUIDialog(plKey objkey) +{ + fGCkey = objkey; +} + +pyGUIDialog::pyGUIDialog() +{ + fGCkey = nil; +} + +hsBool pyGUIDialog::IsGUIDialog(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIDialogMod::ConvertNoRef(gckey.getKey()->GetObjectPtr()) ) + return true; + return false; +} + + +UInt32 pyGUIDialog::WhatControlType(pyKey& gckey) +{ + // Do the pop-up menu test first, since it's derived from dialog + if ( pyGUIPopUpMenu::IsGUIPopUpMenu(gckey) ) + return kPopUpMenu; + else if ( pyGUIDialog::IsGUIDialog(gckey) ) + return kDialog; + else if ( pyGUIControlButton::IsGUIControlButton(gckey) ) + return kButton; + else if ( pyGUIControlCheckBox::IsGUIControlCheckBox(gckey) ) + return kCheckBox; + else if ( pyGUIControlEditBox::IsGUIControlEditBox(gckey) ) + return kEditBox; + else if ( pyGUIControlListBox::IsGUIControlListBox(gckey) ) + return kListBox; + else if ( pyGUIControlRadioGroup::IsGUIControlRadioGroup(gckey) ) + return kRadioGroup; + else if ( pyGUIControlTextBox::IsGUIControlTextBox(gckey) ) + return kTextBox; + else if ( pyGUIControlValue::IsGUIControlValue(gckey) ) + { + // then see what kind of value control it is + if ( pfGUIKnobCtrl::ConvertNoRef(gckey.getKey()->GetObjectPtr()) ) + return kKnob; + else if ( pfGUIUpDownPairMod::ConvertNoRef(gckey.getKey()->GetObjectPtr()) ) + return kUpDownPair; + else + return 0; + } + else if ( pyGUIControlDynamicText::IsGUIControlDynamicText( gckey ) ) + return kDynamicText; + else if ( pyGUIControlMultiLineEdit::IsGUIControlMultiLineEdit( gckey ) ) + return kMultiLineEdit; + else if ( pyGUIControlClickMap::IsGUIControlClickMap( gckey ) ) + return kClickMap; + else + return 0; +} + + +// override the equals to operator +hsBool pyGUIDialog::operator==(const pyGUIDialog &gcobj) const +{ + plKey theirs = ((pyGUIDialog&)gcobj).getObjKey(); + if ( fGCkey == nil && theirs == nil ) + return true; + else if ( fGCkey != nil && theirs != nil ) + return (fGCkey->GetUoid()==theirs->GetUoid()); + else + return false; +} + + +// getter and setters +plKey pyGUIDialog::getObjKey() +{ + return fGCkey; +} + + +PyObject* pyGUIDialog::getObjPyKey() +{ + // create a pyKey object that Python will manage + return pyKey::New(fGCkey); +} + + +// interface functions +UInt32 pyGUIDialog::GetTagID() +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + return pdmod->GetTagID(); + } + return 0; +} + + +void pyGUIDialog::SetEnabled( hsBool e ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + pdmod->SetEnabled(e); + } +} + +hsBool pyGUIDialog::IsEnabled( void ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + return pdmod->IsEnabled(); + } + return false; +} + +const char* pyGUIDialog::GetName( void ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + return pdmod->GetName(); + } + return ""; +} + + +UInt32 pyGUIDialog::GetVersion(void) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + return pdmod->GetVersion(); + } + return 0; +} + + +UInt32 pyGUIDialog::GetNumControls( void ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + return pdmod->GetNumControls(); + } + return 0; +} + +PyObject* pyGUIDialog::GetControl( UInt32 idx ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIControlMod* pcontrolmod = pdmod->GetControl(idx); + if ( pcontrolmod ) + return pyKey::New(pcontrolmod->GetKey()); + } + } + + // if we got here then there must have been an error + char errmsg[256]; + sprintf(errmsg,"Index %d not found in GUIDialog %s",idx,GetName()); + PyErr_SetString(PyExc_KeyError, errmsg); + PYTHON_RETURN_ERROR; +} + +void pyGUIDialog::SetFocus( pyKey& gcKey ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIControlMod* pcontrolmod = pfGUIControlMod::ConvertNoRef(gcKey.getKey()->ObjectIsLoaded()); + if ( pcontrolmod ) + pdmod->SetFocus(pcontrolmod); + } + } +} + +void pyGUIDialog::Show( void ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pdmod->Show(); + pdmod->RefreshAllControls(); + } + } +} + +void pyGUIDialog::ShowNoReset( void ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + pdmod->ShowNoReset(); + } +} + +void pyGUIDialog::Hide( void ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + pdmod->Hide(); + } +} + +PyObject* pyGUIDialog::GetControlFromTag( UInt32 tagID ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIControlMod* pcontrolmod = pdmod->GetControlFromTag(tagID); + if ( pcontrolmod ) + return pyKey::New(pcontrolmod->GetKey()); + } + } + + // if we got here then there must have been an error + char errmsg[256]; + sprintf(errmsg,"TagID %d not found in GUIDialog %s",tagID,GetName()); + PyErr_SetString(PyExc_KeyError, errmsg); + PYTHON_RETURN_ERROR; +} + + // get color schemes +PyObject* pyGUIDialog::GetForeColor() +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + return pyColor::New(color->fForeColor.r,color->fForeColor.g,color->fForeColor.b,color->fForeColor.a); + } + } + PYTHON_RETURN_NONE; +} + +PyObject* pyGUIDialog::GetSelColor() +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + return pyColor::New(color->fSelForeColor.r,color->fSelForeColor.g,color->fSelForeColor.b,color->fSelForeColor.a); + } + } + PYTHON_RETURN_NONE; +} + +PyObject* pyGUIDialog::GetBackColor() +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + return pyColor::New(color->fBackColor.r,color->fBackColor.g,color->fBackColor.b,color->fBackColor.a); + } + } + PYTHON_RETURN_NONE; +} + +PyObject* pyGUIDialog::GetBackSelColor() +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + return pyColor::New(color->fSelBackColor.r,color->fSelBackColor.g,color->fSelBackColor.b,color->fSelBackColor.a); + } + } + PYTHON_RETURN_NONE; +} + +UInt32 pyGUIDialog::GetFontSize() +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + return color->fFontSize; + } + } + // create a pyColor that will be managed by Python + return 0; +} + + + // set color scheme +void pyGUIDialog::SetForeColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fForeColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fForeColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fForeColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fForeColor.a = a; + } + } +} + +void pyGUIDialog::SetSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fSelForeColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fSelForeColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fSelForeColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fSelForeColor.a = a; + } + } +} + +void pyGUIDialog::SetBackColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fBackColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fBackColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fBackColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fBackColor.a = a; + } + } +} + +void pyGUIDialog::SetBackSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fSelBackColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fSelBackColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fSelBackColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fSelBackColor.a = a; + } + } +} + + +void pyGUIDialog::SetFontSize(UInt32 fontsize) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pfGUIColorScheme* color = pdmod->GetColorScheme(); + color->fFontSize = (UInt8)fontsize; + } + } +} + +void pyGUIDialog::UpdateAllBounds( void ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + pdmod->UpdateAllBounds(); + } +} + +void pyGUIDialog::RefreshAllControls( void ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + pdmod->RefreshAllControls(); + } +} + +void pyGUIDialog::NoFocus( ) +{ + if ( fGCkey ) + { + pfGUIDialogMod* pdmod = pfGUIDialogMod::ConvertNoRef(fGCkey->ObjectIsLoaded()); + if ( pdmod ) + { + pdmod->SetFocus(nil); + } + } +} + +void pyGUIDialog::GUICursorOff() +{ + if ( pfGameGUIMgr::GetInstance() ) + pfGameGUIMgr::GetInstance()->SetCursorOpacity(0.0f); +} + +void pyGUIDialog::GUICursorOn() +{ + if ( pfGameGUIMgr::GetInstance() ) + pfGameGUIMgr::GetInstance()->SetCursorOpacity(1.0f); +} + +void pyGUIDialog::GUICursorDimmed() +{ + if ( pfGameGUIMgr::GetInstance() ) + pfGameGUIMgr::GetInstance()->SetCursorOpacity(0.4f); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialog.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialog.h new file mode 100644 index 00000000..eb11bae4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialog.h @@ -0,0 +1,139 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIDialog_h_ +#define _pyGUIDialog_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIDialog - a wrapper class to provide interface to modifier +// attached to a GUIDialog +// +////////////////////////////////////////////////////////////////////// + +#include "hsStlUtils.h" +#include "pyKey.h" + +#include +#include "pyGlueHelpers.h" + + +class pyColor; + +class pyGUIDialog +{ +private: + plKey fGCkey; + +protected: + pyGUIDialog(pyKey& gckey); + pyGUIDialog(plKey objkey); + pyGUIDialog(); + +public: + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIDialog); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIDialog object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIDialog); // converts a PyObject to a pyGUIDialog (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaMethods(std::vector &methods); + + static hsBool IsGUIDialog(pyKey& gckey); + + void setKey(plKey key) {fGCkey = key;} // used by python glue, do NOT call + + enum // these enums are used in Python so they have to match PlasmaTypes.py + { + kDialog=1, + kButton=2, + kDraggable=3, + kListBox=4, + kTextBox=5, + kEditBox=6, + kUpDownPair=7, + kKnob=8, + kDragBar=9, + kCheckBox=10, + kRadioGroup=11, + kDynamicText=12, + kMultiLineEdit=13, + kPopUpMenu=14, + kClickMap=15, + }; + static UInt32 WhatControlType(pyKey& gckey); + static void GUICursorOff(); + static void GUICursorOn(); + static void GUICursorDimmed(); + + // override the equals to operator + hsBool operator==(const pyGUIDialog &gdobj) const; + hsBool operator!=(const pyGUIDialog &gdobj) const { return !(gdobj == *this); } + + // getter and setters + virtual plKey getObjKey(); + virtual PyObject* getObjPyKey(); // returns pyKey + + // interface functions + virtual UInt32 GetTagID(); + + virtual void SetEnabled( hsBool e ); + virtual void Enable() { SetEnabled(true); } + virtual void Disable() { SetEnabled(false); } + virtual hsBool IsEnabled( void ); + virtual const char *GetName( void ); + virtual UInt32 GetVersion(void); + + virtual UInt32 GetNumControls( void ); + virtual PyObject* GetControl( UInt32 idx ); // returns pyKey + virtual void SetFocus( pyKey& gcKey ); + virtual void NoFocus( ); + virtual void Show( void ); + virtual void ShowNoReset( void ); + virtual void Hide( void ); + virtual PyObject* GetControlFromTag( UInt32 tagID ); // returns pyKey + + // get color schemes + virtual PyObject* GetForeColor(); // returns pyColor + virtual PyObject* GetSelColor(); // returns pyColor + virtual PyObject* GetBackColor(); // returns pyColor + virtual PyObject* GetBackSelColor(); // returns pyColor + virtual UInt32 GetFontSize(); + // set color scheme + virtual void SetForeColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + virtual void SetSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + virtual void SetBackColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + virtual void SetBackSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + virtual void SetFontSize(UInt32 fontsize); + + virtual void UpdateAllBounds( void ); + virtual void RefreshAllControls( void ); +}; + +#endif // _pyGUIDialog_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialogGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialogGlue.cpp new file mode 100644 index 00000000..e445015d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIDialogGlue.cpp @@ -0,0 +1,365 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIDialog.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIDialog, pyGUIDialog); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIDialog, pyGUIDialog) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIDialog) + +PYTHON_INIT_DEFINITION(ptGUIDialog, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + PYTHON_RETURN_INIT_OK; +} + +PYTHON_RICH_COMPARE_DEFINITION(ptGUIDialog, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pyGUIDialog::Check(obj1) || !pyGUIDialog::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptGUIDialog object"); + PYTHON_RCOMPARE_ERROR; + } + } + pyGUIDialog *dlg1 = pyGUIDialog::ConvertFrom(obj1); + pyGUIDialog *dlg2 = pyGUIDialog::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*dlg1) == (*dlg2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*dlg1) != (*dlg2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptGUIDialog object"); + PYTHON_RCOMPARE_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIDialog, getKey) +{ + return self->fThis->getObjPyKey(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIDialog, getTagID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetTagID()); +} + +PYTHON_METHOD_DEFINITION(ptGUIDialog, enable, args) +{ + char enableFlag = 1; + if (!PyArg_ParseTuple(args, "|b", &enableFlag)) + { + PyErr_SetString(PyExc_TypeError, "enable expects an optional boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetEnabled(enableFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIDialog, disable, Disable) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIDialog, isEnabled) +{ + PYTHON_RETURN_BOOL(self->fThis->IsEnabled()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIDialog, getName) +{ + return PyString_FromString(self->fThis->GetName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIDialog, getVersion) +{ + return PyLong_FromUnsignedLong(self->fThis->GetVersion()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIDialog, getNumControls) +{ + return PyLong_FromUnsignedLong(self->fThis->GetNumControls()); +} + +PYTHON_METHOD_DEFINITION(ptGUIDialog, getControlFromIndex, args) +{ + unsigned long index; + if (!PyArg_ParseTuple(args, "l", &index)) + { + PyErr_SetString(PyExc_TypeError, "getControlFromIndex expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetControl(index); +} + +PYTHON_METHOD_DEFINITION(ptGUIDialog, setFocus, args) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "setFocus expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "setFocus expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->SetFocus(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIDialog, noFocus, NoFocus) + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIDialog, show, Show) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIDialog, showNoReset, ShowNoReset) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIDialog, hide, Hide) + +PYTHON_METHOD_DEFINITION(ptGUIDialog, getControlFromTag, args) +{ + unsigned long tagID; + if (!PyArg_ParseTuple(args, "l", &tagID)) + { + PyErr_SetString(PyExc_TypeError, "getControlFromTag expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetControlFromTag(tagID); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIDialog, getForeColor) +{ + return self->fThis->GetForeColor(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIDialog, getSelectColor) +{ + return self->fThis->GetSelColor(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIDialog, getBackColor) +{ + return self->fThis->GetBackColor(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIDialog, getBackSelectColor) +{ + return self->fThis->GetBackSelColor(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIDialog, getFontSize) +{ + return PyLong_FromUnsignedLong(self->fThis->GetFontSize()); +} + +PYTHON_METHOD_DEFINITION(ptGUIDialog, setForeColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setForeColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetForeColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIDialog, setSelectColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setSelectColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSelColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIDialog, setBackColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setBackColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetBackColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIDialog, setBackSelectColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setBackSelectColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetBackSelColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIDialog, setFontSize, args) +{ + unsigned long fontSize; + if (!PyArg_ParseTuple(args, "l", &fontSize)) + { + PyErr_SetString(PyExc_TypeError, "setFontSize expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetFontSize(fontSize); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIDialog, updateAllBounds, UpdateAllBounds) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIDialog, refreshAllControls, RefreshAllControls) + +PYTHON_START_METHODS_TABLE(ptGUIDialog) + PYTHON_METHOD_NOARGS(ptGUIDialog, getKey, "Returns this dialog's ptKey"), + PYTHON_METHOD_NOARGS(ptGUIDialog, getTagID, "Returns this dialog's tag ID"), + PYTHON_METHOD(ptGUIDialog, enable, "Params: enableFlag=1\nEnable this dialog"), + PYTHON_BASIC_METHOD(ptGUIDialog, disable, "Disables this dialog"), + PYTHON_METHOD_NOARGS(ptGUIDialog, isEnabled, "Is this dialog currently enabled?"), + PYTHON_METHOD_NOARGS(ptGUIDialog, getName, "Returns the dialog's name"), + PYTHON_METHOD_NOARGS(ptGUIDialog, getVersion, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptGUIDialog, getNumControls, "Returns the number of controls in this dialog"), + PYTHON_METHOD(ptGUIDialog, getControlFromIndex, "Params: index\nReturns the ptKey of the control with the specified index (not tag ID!)"), + PYTHON_METHOD(ptGUIDialog, setFocus, "Params: ctrlKey\nSets the control that has input focus"), + PYTHON_BASIC_METHOD(ptGUIDialog, noFocus, "Makes sure no control has input focus"), + PYTHON_BASIC_METHOD(ptGUIDialog, show, "Shows the dialog"), + PYTHON_BASIC_METHOD(ptGUIDialog, showNoReset, "Show dialog without resetting clickables"), + PYTHON_BASIC_METHOD(ptGUIDialog, hide, "Hides the dialog"), + PYTHON_METHOD(ptGUIDialog, getControlFromTag, "Params: tagID\nReturns the ptKey of the control with the specified tag ID"), + PYTHON_METHOD_NOARGS(ptGUIDialog, getForeColor, "Returns the fore color as a ptColor object"), + PYTHON_METHOD_NOARGS(ptGUIDialog, getSelectColor, "Returns the select color as a ptColor object"), + PYTHON_METHOD_NOARGS(ptGUIDialog, getBackColor, "Returns the back color as a ptColor object"), + PYTHON_METHOD_NOARGS(ptGUIDialog, getBackSelectColor, "Returns the select back color as a ptColor object"), + PYTHON_METHOD_NOARGS(ptGUIDialog, getFontSize, "Returns the font size"), + PYTHON_METHOD(ptGUIDialog, setForeColor, "Params: red,green,blue,alpha\nSets the fore color, -1 means don't change"), + PYTHON_METHOD(ptGUIDialog, setSelectColor, "Params: red,green,blue,alpha\nSets the select color, -1 means don't change"), + PYTHON_METHOD(ptGUIDialog, setBackColor, "Params: red,green,blue,alpha\nSets the back color, -1 means don't change"), + PYTHON_METHOD(ptGUIDialog, setBackSelectColor, "Params: red,green,blue,alpha\nSets the select back color, -1 means don't change"), + PYTHON_METHOD(ptGUIDialog, setFontSize, "Params: fontSize\nSets the font size"), + PYTHON_BASIC_METHOD(ptGUIDialog, updateAllBounds, "Tells the dialog to recompute all the bounds for its controls"), + PYTHON_BASIC_METHOD(ptGUIDialog, refreshAllControls, "Tells the dialog to redraw all its controls"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +#define ptGUIDialog_COMPARE PYTHON_NO_COMPARE +#define ptGUIDialog_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptGUIDialog_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptGUIDialog_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptGUIDialog_STR PYTHON_NO_STR +#define ptGUIDialog_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptGUIDialog) +#define ptGUIDialog_GETSET PYTHON_NO_GETSET +#define ptGUIDialog_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptGUIDialog, "Params: dialogKey\nPlasma GUI dialog class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptGUIDialog, pyGUIDialog) + +PyObject *pyGUIDialog::New(pyKey& gckey) +{ + ptGUIDialog *newObj = (ptGUIDialog*)ptGUIDialog_type.tp_new(&ptGUIDialog_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUIDialog::New(plKey objkey) +{ + ptGUIDialog *newObj = (ptGUIDialog*)ptGUIDialog_type.tp_new(&ptGUIDialog_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIDialog, pyGUIDialog) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIDialog, pyGUIDialog) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIDialog::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIDialog); + PYTHON_CLASS_IMPORT_END(m); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtWhatGUIControlType, args, "Params: guiKey\nReturns the control type of the key passed in") +{ + PyObject* guiKeyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &guiKeyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtWhatGUIControlType expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(guiKeyObj)) + { + PyErr_SetString(PyExc_TypeError, "PtWhatGUIControlType expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* guiKey = pyKey::ConvertFrom(guiKeyObj); + return PyLong_FromUnsignedLong(pyGUIDialog::WhatControlType(*guiKey)); +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtGUICursorOff, pyGUIDialog::GUICursorOff, "Turns the GUI cursor off") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtGUICursorOn, pyGUIDialog::GUICursorOn, "Turns the GUI cursor on") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtGUICursorDimmed, pyGUIDialog::GUICursorDimmed, "Dimms the GUI cursor") + +void pyGUIDialog::AddPlasmaMethods(std::vector &methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtWhatGUIControlType); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtGUICursorOff); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtGUICursorOn); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtGUICursorDimmed); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenu.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenu.cpp new file mode 100644 index 00000000..90b540ff --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenu.cpp @@ -0,0 +1,390 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" +#include "pyColor.h" +#include "cyPythonInterface.h" + +#include "../pfGameGUIMgr/pfGameGUIMgr.h" +#include "../pfGameGUIMgr/pfGUIPopUpMenu.h" + +#include "pyGUIPopUpMenu.h" + +// the rest of the controls +#include "pyGUIControlButton.h" +#include "pyGUIControlCheckBox.h" +#include "pyGUIControlEditBox.h" +#include "pyGUIControlListBox.h" +#include "pyGUIControlRadioGroup.h" +#include "pyGUIControlTextBox.h" +#include "pyGUIControlValue.h" +#include "pyGUIControlDynamicText.h" +#include "pyGUIControlMultiLineEdit.h" + +#include "../pfGameGUIMgr/pfGUIControlHandlers.h" + +#define kGetMenuPtr( ret ) if( fGCkey == nil ) return ret; \ + pfGUIPopUpMenu *menu = pfGUIPopUpMenu::ConvertNoRef( fGCkey->ObjectIsLoaded() ); \ + if( menu == nil ) return ret; + + +pyGUIPopUpMenu::pyGUIPopUpMenu(pyKey& gckey) +{ + fGCkey = gckey.getKey(); + fBuiltMenu = nil; +} + +pyGUIPopUpMenu::pyGUIPopUpMenu(plKey objkey) +{ + fGCkey = objkey; + fBuiltMenu = nil; +} + +pyGUIPopUpMenu::pyGUIPopUpMenu() +{ + fGCkey = nil; + fBuiltMenu = nil; +} + +pyGUIPopUpMenu::pyGUIPopUpMenu( const char *name, hsScalar screenOriginX, hsScalar screenOriginY, const plLocation &destLoc/* = plLocation::kGlobalFixedLoc */) +{ + fBuiltMenu = pfGUIPopUpMenu::Build( name, nil, screenOriginX, screenOriginY, destLoc ); + if( fBuiltMenu != nil ) + { + fGCkey = fBuiltMenu->GetKey(); + fGCkey->RefObject(); + } + else + fGCkey = nil; +} + +pyGUIPopUpMenu::pyGUIPopUpMenu( const char *name, pyGUIPopUpMenu &parent, hsScalar screenOriginX, hsScalar screenOriginY ) +{ + pfGUIPopUpMenu *parentMenu = pfGUIPopUpMenu::ConvertNoRef( parent.fGCkey->ObjectIsLoaded() ); + + const plLocation &parentLoc = ( parentMenu != nil ) ? parentMenu->GetKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc; + + fBuiltMenu = pfGUIPopUpMenu::Build( name, parentMenu, screenOriginX, screenOriginY, parentLoc ); + if( fBuiltMenu != nil ) + { + fGCkey = fBuiltMenu->GetKey(); + fGCkey->RefObject(); + } + else + fGCkey = nil; +} + +pyGUIPopUpMenu::~pyGUIPopUpMenu() +{ + // Optimistic, I know, to assume the destructor will be called, but still... + if( fBuiltMenu != nil ) + { + fBuiltMenu->GetKey()->UnRefObject(); + fBuiltMenu = nil; + } +} + +void pyGUIPopUpMenu::setup(plKey objkey) +{ + // kill any previous menu + if( fBuiltMenu != nil ) + { + fBuiltMenu->GetKey()->UnRefObject(); + fBuiltMenu = nil; + } + + // setup the new one + fGCkey = objkey; +} + +void pyGUIPopUpMenu::setup(const char *name, hsScalar screenOriginX, hsScalar screenOriginY, const plLocation &destLoc /* = plLocation::kGlobalFixedLoc */) +{ + // kill any previous menu + if( fBuiltMenu != nil ) + { + fBuiltMenu->GetKey()->UnRefObject(); + fBuiltMenu = nil; + } + + // setup the new one + fBuiltMenu = pfGUIPopUpMenu::Build( name, nil, screenOriginX, screenOriginY, destLoc ); + if( fBuiltMenu != nil ) + { + fGCkey = fBuiltMenu->GetKey(); + fGCkey->RefObject(); + } + else + fGCkey = nil; +} + +void pyGUIPopUpMenu::setup(const char *name, pyGUIPopUpMenu &parent, hsScalar screenOriginX, hsScalar screenOriginY) +{ + // kill any previous menu + if( fBuiltMenu != nil ) + { + fBuiltMenu->GetKey()->UnRefObject(); + fBuiltMenu = nil; + } + + // setup the new one + pfGUIPopUpMenu *parentMenu = pfGUIPopUpMenu::ConvertNoRef( parent.fGCkey->ObjectIsLoaded() ); + + const plLocation &parentLoc = ( parentMenu != nil ) ? parentMenu->GetKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc; + + fBuiltMenu = pfGUIPopUpMenu::Build( name, parentMenu, screenOriginX, screenOriginY, parentLoc ); + if( fBuiltMenu != nil ) + { + fGCkey = fBuiltMenu->GetKey(); + fGCkey->RefObject(); + } + else + fGCkey = nil; +} + +hsBool pyGUIPopUpMenu::IsGUIPopUpMenu(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUIPopUpMenu::ConvertNoRef(gckey.getKey()->GetObjectPtr()) ) + return true; + return false; +} + + +// override the equals to operator +hsBool pyGUIPopUpMenu::operator==(const pyGUIPopUpMenu &gcobj) const +{ + plKey theirs = ((pyGUIPopUpMenu&)gcobj).getObjKey(); + if ( fGCkey == nil && theirs == nil ) + return true; + else if ( fGCkey != nil && theirs != nil ) + return (fGCkey->GetUoid()==theirs->GetUoid()); + else + return false; +} + + +// getter and setters +plKey pyGUIPopUpMenu::getObjKey() +{ + return fGCkey; +} + + +PyObject* pyGUIPopUpMenu::getObjPyKey() +{ + // create a pyKey object that Python will manage + return pyKey::New(fGCkey); +} + + +// interface functions +UInt32 pyGUIPopUpMenu::GetTagID() +{ + kGetMenuPtr( 0 ); + return menu->GetTagID(); +} + + +void pyGUIPopUpMenu::SetEnabled( hsBool e ) +{ + kGetMenuPtr( ; ); + menu->SetEnabled(e); +} + +hsBool pyGUIPopUpMenu::IsEnabled( void ) +{ + kGetMenuPtr( false ); + return menu->IsEnabled(); +} + +const char* pyGUIPopUpMenu::GetName( void ) +{ + kGetMenuPtr( "" ); + return menu->GetName(); +} + + +UInt32 pyGUIPopUpMenu::GetVersion(void) +{ + kGetMenuPtr( 0 ); + return menu->GetVersion(); +} + + +void pyGUIPopUpMenu::Show( void ) +{ + kGetMenuPtr( ; ); + ( (pfGUIDialogMod *)menu )->Show(); +} + +void pyGUIPopUpMenu::Hide( void ) +{ + kGetMenuPtr( ; ); + menu->Hide(); +} + + // get color schemes +PyObject* pyGUIPopUpMenu::GetForeColor() +{ + kGetMenuPtr( nil ); + + pfGUIColorScheme* color = menu->GetColorScheme(); + return pyColor::New(color->fForeColor.r,color->fForeColor.g,color->fForeColor.b,color->fForeColor.a); +} + +PyObject* pyGUIPopUpMenu::GetSelColor() +{ + kGetMenuPtr( nil ); + + pfGUIColorScheme* color = menu->GetColorScheme(); + return pyColor::New(color->fSelForeColor.r,color->fSelForeColor.g,color->fSelForeColor.b,color->fSelForeColor.a); +} + +PyObject* pyGUIPopUpMenu::GetBackColor() +{ + kGetMenuPtr( nil ); + + pfGUIColorScheme* color = menu->GetColorScheme(); + return pyColor::New(color->fBackColor.r,color->fBackColor.g,color->fBackColor.b,color->fBackColor.a); +} + +PyObject* pyGUIPopUpMenu::GetBackSelColor() +{ + kGetMenuPtr( nil ); + + pfGUIColorScheme* color = menu->GetColorScheme(); + return pyColor::New(color->fSelBackColor.r,color->fSelBackColor.g,color->fSelBackColor.b,color->fSelBackColor.a); +} + + // set color scheme +void pyGUIPopUpMenu::SetForeColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + kGetMenuPtr( ; ); + + pfGUIColorScheme* color = menu->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fForeColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fForeColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fForeColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fForeColor.a = a; +} + +void pyGUIPopUpMenu::SetSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + kGetMenuPtr( ; ); + + pfGUIColorScheme* color = menu->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fSelForeColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fSelForeColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fSelForeColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fSelForeColor.a = a; +} + +void pyGUIPopUpMenu::SetBackColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + kGetMenuPtr( ; ); + + pfGUIColorScheme* color = menu->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fBackColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fBackColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fBackColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fBackColor.a = a; +} + +void pyGUIPopUpMenu::SetBackSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ) +{ + kGetMenuPtr( ; ); + + pfGUIColorScheme* color = menu->GetColorScheme(); + if ( r >= 0.0 && r <= 1.0 ) + color->fSelBackColor.r = r; + if ( g >= 0.0 && g <= 1.0 ) + color->fSelBackColor.g = g; + if ( b >= 0.0 && g <= 1.0 ) + color->fSelBackColor.b = b; + if ( a >= 0.0 && g <= 1.0 ) + color->fSelBackColor.a = a; +} + +void pyGUIPopUpMenu::AddConsoleCmdItem( const char *name, const char *consoleCmd ) +{ + wchar_t *wName = hsStringToWString(name); + AddConsoleCmdItemW(wName,consoleCmd); + delete [] wName; +} + +void pyGUIPopUpMenu::AddConsoleCmdItemW( std::wstring name, const char *consoleCmd ) +{ + kGetMenuPtr( ; ); + menu->AddItem( name.c_str(), TRACKED_NEW pfGUIConsoleCmdProc( consoleCmd ), nil ); +} + +void pyGUIPopUpMenu::AddNotifyItem( const char *name ) +{ + wchar_t *wName = hsStringToWString(name); + AddNotifyItemW(wName); + delete [] wName; +} + +void pyGUIPopUpMenu::AddNotifyItemW( std::wstring name ) +{ + kGetMenuPtr( ; ); + menu->AddItem( name.c_str(), (pfGUICtrlProcObject *)( menu->GetHandler() ), nil ); +} + +void pyGUIPopUpMenu::AddSubMenuItem( const char *name, pyGUIPopUpMenu &subMenu ) +{ + wchar_t *wName = hsStringToWString(name); + AddSubMenuItemW(wName,subMenu); + delete [] wName; +} + +void pyGUIPopUpMenu::AddSubMenuItemW( std::wstring name, pyGUIPopUpMenu &subMenu ) +{ + kGetMenuPtr( ; ); + + if( subMenu.fGCkey == nil ) + return; + pfGUIPopUpMenu *subM = pfGUIPopUpMenu::ConvertNoRef( subMenu.fGCkey->ObjectIsLoaded() ); + if( subM == nil ) + return; + + menu->AddItem( name.c_str(), nil, subM ); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenu.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenu.h new file mode 100644 index 00000000..49bc6259 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenu.h @@ -0,0 +1,123 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUIPopUpMenu_h_ +#define _pyGUIPopUpMenu_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUIPopUpMenu - a wrapper class to provide interface to modifier +// attached to a GUI Pop-Up Menu +// +////////////////////////////////////////////////////////////////////// + +#include "../pfGameGUIMgr/pfGUIPopUpMenu.h" + +#include "pyKey.h" + +#include +#include "pyGlueHelpers.h" + +class pyColor; + +class pyGUIPopUpMenu +{ +private: + plKey fGCkey; + + pfGUIPopUpMenu *fBuiltMenu; + +protected: + pyGUIPopUpMenu(pyKey& gckey); + pyGUIPopUpMenu(plKey objkey); + pyGUIPopUpMenu(); + // For creating new menus on the fly + pyGUIPopUpMenu( const char *name, hsScalar screenOriginX, hsScalar screenOriginY, const plLocation &destLoc = plLocation::kGlobalFixedLoc ); + pyGUIPopUpMenu( const char *name, pyGUIPopUpMenu &parent, hsScalar screenOriginX, hsScalar screenOriginY ); + +public: + virtual ~pyGUIPopUpMenu(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUIPopUpMenu); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + static PyObject *New(const char *name, hsScalar screenOriginX, hsScalar screenOriginY, const plLocation &destLoc = plLocation::kGlobalFixedLoc); + static PyObject *New(const char *name, pyGUIPopUpMenu &parent, hsScalar screenOriginX, hsScalar screenOriginY); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUIPopUpMenu object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUIPopUpMenu); // converts a PyObject to a pyGUIPopUpMenu (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // these three are for the python glue only, do NOT call + void setup(plKey objkey); + void setup(const char *name, hsScalar screenOriginX, hsScalar screenOriginY, const plLocation &destLoc = plLocation::kGlobalFixedLoc); + void setup(const char *name, pyGUIPopUpMenu &parent, hsScalar screenOriginX, hsScalar screenOriginY); + + static hsBool IsGUIPopUpMenu(pyKey& gckey); + + // override the equals to operator + hsBool operator==(const pyGUIPopUpMenu &gdobj) const; + hsBool operator!=(const pyGUIPopUpMenu &gdobj) const { return !(gdobj == *this); } + + // getter and setters + virtual plKey getObjKey(); + virtual PyObject* getObjPyKey(); // returns pyKey + + // interface functions + virtual UInt32 GetTagID(); + + virtual void SetEnabled( hsBool e ); + virtual void Enable() { SetEnabled(true); } + virtual void Disable() { SetEnabled(false); } + virtual hsBool IsEnabled( void ); + virtual const char *GetName( void ); + virtual UInt32 GetVersion(void); + + virtual void Show( void ); + virtual void Hide( void ); + + // get color schemes + virtual PyObject* GetForeColor(); // returns pyColor + virtual PyObject* GetSelColor(); // returns pyColor + virtual PyObject* GetBackColor(); // returns pyColor + virtual PyObject* GetBackSelColor(); // returns pyColor + // set color scheme + virtual void SetForeColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + virtual void SetSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + virtual void SetBackColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + virtual void SetBackSelColor( hsScalar r, hsScalar g, hsScalar b, hsScalar a ); + + // Menu item functions + virtual void AddConsoleCmdItem( const char *name, const char *consoleCmd ); + virtual void AddConsoleCmdItemW( std::wstring name, const char *consoleCmd ); + virtual void AddNotifyItem( const char *name ); + virtual void AddNotifyItemW( std::wstring name ); + virtual void AddSubMenuItem( const char *name, pyGUIPopUpMenu &subMenu ); + virtual void AddSubMenuItemW( std::wstring name, pyGUIPopUpMenu &subMenu ); +}; + +#endif // _pyGUIPopUpMenu_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenuGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenuGlue.cpp new file mode 100644 index 00000000..5ed45dc2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUIPopUpMenuGlue.cpp @@ -0,0 +1,430 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUIPopUpMenu.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUIPopUpMenu, pyGUIPopUpMenu); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUIPopUpMenu, pyGUIPopUpMenu) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUIPopUpMenu) + +PYTHON_INIT_DEFINITION(ptGUIPopUpMenu, args, keywords) +{ + PyObject* arg1 = NULL; + PyObject* arg2 = NULL; + PyObject* arg3 = NULL; + PyObject* arg4 = NULL; + if (!PyArg_ParseTuple(args, "O|OOO", &arg1, &arg2, &arg3, &arg4)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects one of three argument lists:\n" + "1 - ptKey\n" + "2 - string, float, float\n" + "3 - string, ptGUIPopUpMenu, float, float"); + PYTHON_RETURN_INIT_ERROR; + } + if (pyKey::Check(arg1)) + { + // arg list 1 + if (arg2 || arg3 || arg4) + { + // too many arguments + PyErr_SetString(PyExc_TypeError, "__init__ expects one of three argument lists:\n" + "1 - ptKey\n" + "2 - string, float, float\n" + "3 - string, ptGUIPopUpMenu, float, float"); + PYTHON_RETURN_INIT_ERROR; + } + pyKey* key = pyKey::ConvertFrom(arg1); + self->fThis->setup(key->getKey()); + PYTHON_RETURN_INIT_OK; + } + else if (PyString_Check(arg1)) + { + // arg list 2 or 3 + char* name = PyString_AsString(arg1); + if (PyFloat_Check(arg2)) + { + // arg list 2 + if ((!PyFloat_Check(arg3)) || arg4) + { + // invalid types or too many arguments + PyErr_SetString(PyExc_TypeError, "__init__ expects one of three argument lists:\n" + "1 - ptKey\n" + "2 - string, float, float\n" + "3 - string, ptGUIPopUpMenu, float, float"); + PYTHON_RETURN_INIT_ERROR; + } + float originX = (float)PyFloat_AsDouble(arg2); + float originY = (float)PyFloat_AsDouble(arg3); + self->fThis->setup(name, originX, originY); + PYTHON_RETURN_INIT_OK; + } + else if (pyGUIPopUpMenu::Check(arg2)) + { + // arg list 3 + if ((!PyFloat_Check(arg3)) || (!PyFloat_Check(arg4))) + { + // invalid types + PyErr_SetString(PyExc_TypeError, "__init__ expects one of three argument lists:\n" + "1 - ptKey\n" + "2 - string, float, float\n" + "3 - string, ptGUIPopUpMenu, float, float"); + PYTHON_RETURN_INIT_ERROR; + } + pyGUIPopUpMenu* parent = pyGUIPopUpMenu::ConvertFrom(arg2); + float originX = (float)PyFloat_AsDouble(arg3); + float originY = (float)PyFloat_AsDouble(arg4); + self->fThis->setup(name, *parent, originX, originY); + PYTHON_RETURN_INIT_OK; + } + } + // invalid type + PyErr_SetString(PyExc_TypeError, "__init__ expects one of three argument lists:\n" + "1 - ptKey\n" + "2 - string, float, float\n" + "3 - string, ptGUIPopUpMenu, float, float"); + PYTHON_RETURN_INIT_ERROR; +} + +PYTHON_RICH_COMPARE_DEFINITION(ptGUIPopUpMenu, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pyGUIPopUpMenu::Check(obj1) || !pyGUIPopUpMenu::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptGUIPopUpMenu object"); + PYTHON_RCOMPARE_ERROR; + } + } + pyGUIPopUpMenu *menu1 = pyGUIPopUpMenu::ConvertFrom(obj1); + pyGUIPopUpMenu *menu2 = pyGUIPopUpMenu::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*menu1) == (*menu2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*menu1) != (*menu2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptGUIPopUpMenu object"); + PYTHON_RCOMPARE_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIPopUpMenu, getKey) +{ + return self->fThis->getObjPyKey(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIPopUpMenu, getTagID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetTagID()); +} + +PYTHON_METHOD_DEFINITION(ptGUIPopUpMenu, enable, args) +{ + char enableFlag = 1; + if (!PyArg_ParseTuple(args, "|b", &enableFlag)) + { + PyErr_SetString(PyExc_TypeError, "enable expects an optional boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetEnabled(enableFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIPopUpMenu, disable, Disable) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIPopUpMenu, isEnabled) +{ + PYTHON_RETURN_BOOL(self->fThis->IsEnabled()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIPopUpMenu, getName) +{ + return PyString_FromString(self->fThis->GetName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIPopUpMenu, getVersion) +{ + return PyLong_FromUnsignedLong(self->fThis->GetVersion()); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGUIPopUpMenu, show, Show) +PYTHON_BASIC_METHOD_DEFINITION(ptGUIPopUpMenu, hide, Hide) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIPopUpMenu, getForeColor) +{ + return self->fThis->GetForeColor(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIPopUpMenu, getSelectColor) +{ + return self->fThis->GetSelColor(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIPopUpMenu, getBackColor) +{ + return self->fThis->GetBackColor(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUIPopUpMenu, getBackSelectColor) +{ + return self->fThis->GetBackSelColor(); +} + +PYTHON_METHOD_DEFINITION(ptGUIPopUpMenu, setForeColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setForeColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetForeColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIPopUpMenu, setSelectColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setSelectColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSelColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIPopUpMenu, setBackColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setBackColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetBackColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIPopUpMenu, setBackSelectColor, args) +{ + float r, g, b, a; + if (!PyArg_ParseTuple(args, "ffff", &r, &g, &b, &a)) + { + PyErr_SetString(PyExc_TypeError, "setBackSelectColor expects four floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetBackSelColor(r, g, b, a); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIPopUpMenu, addConsoleCmdItem, args) +{ + char* name; + char* consoleCmd; + if (!PyArg_ParseTuple(args, "ss", &name, &consoleCmd)) + { + PyErr_SetString(PyExc_TypeError, "addConsoleCmdItem expects two strings"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddConsoleCmdItem(name, consoleCmd); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIPopUpMenu, addConsoleCmdItemW, args) +{ + wchar_t* name; + char* consoleCmd; + if (!PyArg_ParseTuple(args, "us", &name, &consoleCmd)) + { + PyErr_SetString(PyExc_TypeError, "addConsoleCmdItemW expects a unicode string and a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddConsoleCmdItemW(name, consoleCmd); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIPopUpMenu, addNotifyItem, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "addNotifyItem expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddNotifyItem(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIPopUpMenu, addNotifyItemW, args) +{ + wchar_t* name; + if (!PyArg_ParseTuple(args, "u", &name)) + { + PyErr_SetString(PyExc_TypeError, "addNotifyItemW expects a unicode string"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddNotifyItemW(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIPopUpMenu, addSubMenuItem, args) +{ + char* name; + PyObject* subMenuObj = NULL; + if (!PyArg_ParseTuple(args, "sO", &name, &subMenuObj)) + { + PyErr_SetString(PyExc_TypeError, "addSubMenuItem expects a string and a ptGUIPopUpMenu"); + PYTHON_RETURN_ERROR; + } + if (!pyGUIPopUpMenu::Check(subMenuObj)) + { + PyErr_SetString(PyExc_TypeError, "addSubMenuItem expects a string and a ptGUIPopUpMenu"); + PYTHON_RETURN_ERROR; + } + pyGUIPopUpMenu* subMenu = pyGUIPopUpMenu::ConvertFrom(subMenuObj); + self->fThis->AddSubMenuItem(name, *subMenu); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGUIPopUpMenu, addSubMenuItemW, args) +{ + wchar_t* name; + PyObject* subMenuObj = NULL; + if (!PyArg_ParseTuple(args, "uO", &name, &subMenuObj)) + { + PyErr_SetString(PyExc_TypeError, "addSubMenuItemW expects a unicode string and a ptGUIPopUpMenu"); + PYTHON_RETURN_ERROR; + } + if (!pyGUIPopUpMenu::Check(subMenuObj)) + { + PyErr_SetString(PyExc_TypeError, "addSubMenuItemW expects a unicode string and a ptGUIPopUpMenu"); + PYTHON_RETURN_ERROR; + } + pyGUIPopUpMenu* subMenu = pyGUIPopUpMenu::ConvertFrom(subMenuObj); + self->fThis->AddSubMenuItemW(name, *subMenu); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptGUIPopUpMenu) + PYTHON_METHOD_NOARGS(ptGUIPopUpMenu, getKey, "Returns this menu's key"), + PYTHON_METHOD_NOARGS(ptGUIPopUpMenu, getTagID, "Returns this menu's tag id"), + PYTHON_METHOD(ptGUIPopUpMenu, enable, "Params: state=1\nEnables/disables this menu"), + PYTHON_BASIC_METHOD(ptGUIPopUpMenu, disable, "Disables this menu"), + PYTHON_METHOD_NOARGS(ptGUIPopUpMenu, isEnabled, "Returns whether this menu is enabled or not"), + PYTHON_METHOD_NOARGS(ptGUIPopUpMenu, getName, "Returns this menu's name"), + PYTHON_METHOD_NOARGS(ptGUIPopUpMenu, getVersion, "UNKNOWN"), + PYTHON_BASIC_METHOD(ptGUIPopUpMenu, show, "Shows this menu"), + PYTHON_BASIC_METHOD(ptGUIPopUpMenu, hide, "Hides this menu"), + PYTHON_METHOD_NOARGS(ptGUIPopUpMenu, getForeColor, "Returns the foreground color"), + PYTHON_METHOD_NOARGS(ptGUIPopUpMenu, getSelectColor, "Returns the selection color"), + PYTHON_METHOD_NOARGS(ptGUIPopUpMenu, getBackColor, "Returns the background color"), + PYTHON_METHOD_NOARGS(ptGUIPopUpMenu, getBackSelectColor, "Returns the background selection color"), + PYTHON_METHOD(ptGUIPopUpMenu, setForeColor, "Params: r,g,b,a\nSets the foreground color"), + PYTHON_METHOD(ptGUIPopUpMenu, setSelectColor, "Params: r,g,b,a\nSets the selection color"), + PYTHON_METHOD(ptGUIPopUpMenu, setBackColor, "Params: r,g,b,a\nSets the background color"), + PYTHON_METHOD(ptGUIPopUpMenu, setBackSelectColor, "Params: r,g,b,a\nSets the selection background color"), + PYTHON_METHOD(ptGUIPopUpMenu, addConsoleCmdItem, "Params: name,consoleCmd\nAdds a new item to the menu that fires a console command"), + PYTHON_METHOD(ptGUIPopUpMenu, addConsoleCmdItemW, "Params: name,consoleCmd\nUnicode version of addConsoleCmdItem"), + PYTHON_METHOD(ptGUIPopUpMenu, addNotifyItem, "Params: name\nAdds a new item ot the mneu"), + PYTHON_METHOD(ptGUIPopUpMenu, addNotifyItemW, "Params: name\nUnicode version of addNotifyItem"), + PYTHON_METHOD(ptGUIPopUpMenu, addSubMenuItem, "Params: name,subMenu\nAdds a submenu to this menu"), + PYTHON_METHOD(ptGUIPopUpMenu, addSubMenuItemW, "Params: name,subMenu\nUnicode version of addSubMenuItem"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +#define ptGUIPopUpMenu_COMPARE PYTHON_NO_COMPARE +#define ptGUIPopUpMenu_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptGUIPopUpMenu_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptGUIPopUpMenu_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptGUIPopUpMenu_STR PYTHON_NO_STR +#define ptGUIPopUpMenu_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptGUIPopUpMenu) +#define ptGUIPopUpMenu_GETSET PYTHON_NO_GETSET +#define ptGUIPopUpMenu_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptGUIPopUpMenu, "Params: arg1,arg2=None,arg3=None,arg4=None\nTakes three diferent argument lists:\n" + "gckey\n" + "name,screenOriginX,screenOriginY\n" + "name,parent,screenOriginX,screenOriginY"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptGUIPopUpMenu, pyGUIPopUpMenu) + +PyObject *pyGUIPopUpMenu::New(pyKey& gckey) +{ + ptGUIPopUpMenu *newObj = (ptGUIPopUpMenu*)ptGUIPopUpMenu_type.tp_new(&ptGUIPopUpMenu_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + newObj->fThis->fBuiltMenu = nil; + return (PyObject*)newObj; +} + +PyObject *pyGUIPopUpMenu::New(plKey objkey) +{ + ptGUIPopUpMenu *newObj = (ptGUIPopUpMenu*)ptGUIPopUpMenu_type.tp_new(&ptGUIPopUpMenu_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + newObj->fThis->fBuiltMenu = nil; + return (PyObject*)newObj; +} + +PyObject *pyGUIPopUpMenu::New(const char *name, hsScalar screenOriginX, hsScalar screenOriginY, const plLocation &destLoc /* = plLocation::kGlobalFixedLoc */) +{ + ptGUIPopUpMenu *newObj = (ptGUIPopUpMenu*)ptGUIPopUpMenu_type.tp_new(&ptGUIPopUpMenu_type, NULL, NULL); + newObj->fThis->setup(name, screenOriginX, screenOriginY, destLoc); + return (PyObject*)newObj; +} + +PyObject *pyGUIPopUpMenu::New(const char *name, pyGUIPopUpMenu &parent, hsScalar screenOriginX, hsScalar screenOriginY) +{ + ptGUIPopUpMenu *newObj = (ptGUIPopUpMenu*)ptGUIPopUpMenu_type.tp_new(&ptGUIPopUpMenu_type, NULL, NULL); + newObj->fThis->setup(name, parent, screenOriginX, screenOriginY); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUIPopUpMenu, pyGUIPopUpMenu) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUIPopUpMenu, pyGUIPopUpMenu) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUIPopUpMenu::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUIPopUpMenu); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkin.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkin.cpp new file mode 100644 index 00000000..d718b389 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkin.cpp @@ -0,0 +1,88 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// +/////////////////////////////////////////////// + +#include "pyKey.h" +#include "pyColor.h" +#include "cyPythonInterface.h" + +#include "../pfGameGUIMgr/pfGameGUIMgr.h" +#include "../pfGameGUIMgr/pfGUIPopUpMenu.h" + +#include "pyGUISkin.h" + +pyGUISkin::pyGUISkin(pyKey& gckey) +{ + fGCkey = gckey.getKey(); +} + +pyGUISkin::pyGUISkin(plKey objkey) +{ + fGCkey = objkey; +} + +pyGUISkin::pyGUISkin() +{ + fGCkey = nil; +} + +hsBool pyGUISkin::IsGUISkin(pyKey& gckey) +{ + if ( gckey.getKey() && pfGUISkin::ConvertNoRef(gckey.getKey()->GetObjectPtr()) ) + return true; + return false; +} + +// override the equals to operator +hsBool pyGUISkin::operator==(const pyGUISkin &gcobj) const +{ + plKey theirs = ((pyGUISkin&)gcobj).getObjKey(); + if ( fGCkey == nil && theirs == nil ) + return true; + else if ( fGCkey != nil && theirs != nil ) + return (fGCkey->GetUoid()==theirs->GetUoid()); + else + return false; +} + + +// getter and setters +plKey pyGUISkin::getObjKey() +{ + return fGCkey; +} + + +PyObject* pyGUISkin::getObjPyKey() +{ + // create a pyKey object that Python will manage + return pyKey::New(fGCkey); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkin.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkin.h new file mode 100644 index 00000000..5e5265a4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkin.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGUISkin_h_ +#define _pyGUISkin_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGUISkin - a wrapper class to provide interface to a GUI skin +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" +#include +#include "pyGlueHelpers.h" + +class pyColor; + +class pyGUISkin +{ +private: + plKey fGCkey; + +protected: + pyGUISkin(pyKey& gckey); + pyGUISkin(plKey objkey); + pyGUISkin(); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGUISkin); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(pyKey& gckey); + static PyObject *New(plKey objkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGUISkin object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGUISkin); // converts a PyObject to a pyGUISkin (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + static hsBool IsGUISkin(pyKey& gckey); + + void setKey(plKey key) {fGCkey = key;} // used by python glue, do NOT call + + // override the equals to operator + hsBool operator==(const pyGUISkin &gdobj) const; + hsBool operator!=(const pyGUISkin &gdobj) const { return !(gdobj == *this); } + + // getter and setters + virtual plKey getObjKey(); + virtual PyObject* getObjPyKey(); // returns pyKey + +}; + +#endif // _pyGUISkin_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkinGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkinGlue.cpp new file mode 100644 index 00000000..c24c2b13 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGUISkinGlue.cpp @@ -0,0 +1,138 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGUISkin.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGUISkin, pyGUISkin); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGUISkin, pyGUISkin) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGUISkin) + +PYTHON_INIT_DEFINITION(ptGUISkin, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->setKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_RICH_COMPARE_DEFINITION(ptGUISkin, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pyGUISkin::Check(obj1) || !pyGUISkin::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptGUISkin object"); + PYTHON_RCOMPARE_ERROR; + } + } + pyGUISkin *skin1 = pyGUISkin::ConvertFrom(obj1); + pyGUISkin *skin2 = pyGUISkin::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*skin1) == (*skin2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*skin1) != (*skin2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptGUISkin object"); + PYTHON_RCOMPARE_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGUISkin, getKey) +{ + return self->fThis->getObjPyKey(); +} + +PYTHON_START_METHODS_TABLE(ptGUISkin) + PYTHON_METHOD_NOARGS(ptGUISkin, getKey, "Returns this object's ptKey"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +#define ptGUISkin_COMPARE PYTHON_NO_COMPARE +#define ptGUISkin_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptGUISkin_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptGUISkin_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptGUISkin_STR PYTHON_NO_STR +#define ptGUISkin_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptGUISkin) +#define ptGUISkin_GETSET PYTHON_NO_GETSET +#define ptGUISkin_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptGUISkin, "Params: key\nPlasma GUI Skin object"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptGUISkin, pyGUISkin) + +PyObject *pyGUISkin::New(pyKey& gckey) +{ + ptGUISkin *newObj = (ptGUISkin*)ptGUISkin_type.tp_new(&ptGUISkin_type, NULL, NULL); + newObj->fThis->fGCkey = gckey.getKey(); + return (PyObject*)newObj; +} + +PyObject *pyGUISkin::New(plKey objkey) +{ + ptGUISkin *newObj = (ptGUISkin*)ptGUISkin_type.tp_new(&ptGUISkin_type, NULL, NULL); + newObj->fThis->fGCkey = objkey; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGUISkin, pyGUISkin) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGUISkin, pyGUISkin) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGUISkin::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGUISkin); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScore.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScore.cpp new file mode 100644 index 00000000..bdfde627 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScore.cpp @@ -0,0 +1,121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGameScore.h" + +#include "../pfGameScoreMgr/pfGameScoreMgr.h" + +pyGameScore::pyGameScore() : fScore(nil) +{ +} + +pyGameScore::pyGameScore(pfGameScore * score) : fScore(score) +{ + fScore->IncRef(); +} + +pyGameScore::~pyGameScore() +{ + if (fScore) + fScore->DecRef(); +} + +int pyGameScore::GetScoreID() +{ + if (fScore) + return fScore->scoreId; + + return 0; +} + +UInt32 pyGameScore::GetCreatedTime() +{ + if (fScore) + return fScore->createdTime; + + return 0; +} + +int pyGameScore::GetOwnerID() +{ + if (fScore) + return fScore->ownerId; + + return 0; +} + +int pyGameScore::GetGameType() +{ + if (fScore) + return fScore->gameType; + + return 0; +} + +int pyGameScore::GetValue() +{ + if (fScore) + return fScore->value; + + return 0; +} + +const char* pyGameScore::GetGameName() +{ + if (fScore) + return fScore->gameName; + + return ""; +} + +bool pyGameScore::AddPoints(int numPoints) +{ + ENetError result = kNetErrScoreWrongType; + + if (fScore && fScore->gameType != kScoreTypeFixed) + result = pfGameScoreMgr::GetInstance()->AddPoints(fScore->scoreId, numPoints); + + return IS_NET_SUCCESS(result); +} + +bool pyGameScore::TransferPoints(unsigned destination, int numPoints) +{ + ENetError result = kNetErrScoreWrongType; + + if (fScore && fScore->gameType != kScoreTypeFixed) + result = pfGameScoreMgr::GetInstance()->TransferPoints(fScore->scoreId, destination, numPoints); + + return IS_NET_SUCCESS(result); +} + +bool pyGameScore::SetPoints(int numPoints) +{ + ENetError result = kNetErrScoreWrongType; + + if (fScore && fScore->gameType != kScoreTypeFixed) + result = pfGameScoreMgr::GetInstance()->SetPoints(fScore->scoreId, numPoints); + + return IS_NET_SUCCESS(result); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScore.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScore.h new file mode 100644 index 00000000..1b91b1e1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScore.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyGameScore_h +#define pyGameScore_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyGameScore +// +// PURPOSE: a wrapper class to provide access to a game score +// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +struct pfGameScore; + +class pyGameScore +{ +private: + pfGameScore * fScore; + +public: + pyGameScore(); + pyGameScore(pfGameScore * score); + ~pyGameScore(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGameScore); + static PyObject* New(pfGameScore* score); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGameScore object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGameScore); // converts a PyObject to a pyGameScore (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + int GetScoreID(); + UInt32 GetCreatedTime(); + int GetOwnerID(); + int GetGameType(); + int GetValue(); + const char* GetGameName(); + + bool AddPoints(int numPoints); + bool TransferPoints(unsigned destination, int numPoints); + bool SetPoints(int numPoints); +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScoreGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScoreGlue.cpp new file mode 100644 index 00000000..c05016aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGameScoreGlue.cpp @@ -0,0 +1,144 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGameScore.h" + +#include "../pfGameScoreMgr/pfGameScoreMgr.h" + +// glue functions +PYTHON_CLASS_DEFINITION(ptGameScore, pyGameScore); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGameScore, pyGameScore) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGameScore) + +PYTHON_NO_INIT_DEFINITION(ptGameScore) + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameScore, getScoreID) +{ + return PyInt_FromLong(self->fThis->GetScoreID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameScore, getCreatedTime) +{ + return PyLong_FromUnsignedLong(self->fThis->GetCreatedTime()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameScore, getOwnerID) +{ + return PyInt_FromLong(self->fThis->GetOwnerID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameScore, getValue) +{ + return PyInt_FromLong(self->fThis->GetValue()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameScore, getGameType) +{ + return PyInt_FromLong(self->fThis->GetGameType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptGameScore, getGameName) +{ + return PyString_FromString(self->fThis->GetGameName()); +} + +PYTHON_METHOD_DEFINITION(ptGameScore, addPoints, args) +{ + int numPoints = 0; + if (!PyArg_ParseTuple(args, "i", &numPoints)) + { + PyErr_SetString(PyExc_TypeError, "addPoints expects an int"); + PYTHON_RETURN_ERROR; + } + + PYTHON_RETURN_BOOL(self->fThis->AddPoints(numPoints)); +} + +PYTHON_METHOD_DEFINITION(ptGameScore, transferPoints, args) +{ + unsigned destination = 0; + int numPoints = 0; + if (!PyArg_ParseTuple(args, "Ii", &destination, &numPoints)) + { + PyErr_SetString(PyExc_TypeError, "transferPoints expects an unsigned int and an int"); + PYTHON_RETURN_ERROR; + } + + PYTHON_RETURN_BOOL(self->fThis->TransferPoints(destination, numPoints)); +} + +PYTHON_METHOD_DEFINITION(ptGameScore, setPoints, args) +{ + int numPoints = 0; + if (!PyArg_ParseTuple(args, "i", &numPoints)) + { + PyErr_SetString(PyExc_TypeError, "setPoints expects an int"); + PYTHON_RETURN_ERROR; + } + + PYTHON_RETURN_BOOL(self->fThis->SetPoints(numPoints)); +} + +PYTHON_START_METHODS_TABLE(ptGameScore) + PYTHON_METHOD_NOARGS(ptGameScore, getScoreID, "Returns the score id."), + PYTHON_METHOD_NOARGS(ptGameScore, getOwnerID, "Returns a the score owner id."), + PYTHON_METHOD_NOARGS(ptGameScore, getCreatedTime, "Returns a the score creation time."), + PYTHON_METHOD_NOARGS(ptGameScore, getValue, "Returns a the score owner value."), + PYTHON_METHOD_NOARGS(ptGameScore, getGameType, "Returns a the score game type."), + PYTHON_METHOD_NOARGS(ptGameScore, getGameName, "Returns a the score game name."), + PYTHON_METHOD(ptGameScore, addPoints, "Params: numPoints\nAdds points to the score"), + PYTHON_METHOD(ptGameScore, transferPoints, "Params: dest, numPoints\nTransfers points from one score to another"), + PYTHON_METHOD(ptGameScore, setPoints, "Params: numPoints\nSets the number of points in the score\nDon't use to add/remove points, use only to reset values!"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptGameScore, "Game score manager"); + +// required functions for PyObject interoperability +PyObject* pyGameScore::New(pfGameScore* score) +{ + ptGameScore* newObj = (ptGameScore*)ptGameScore_type.tp_new(&ptGameScore_type, NULL, NULL); + if (newObj->fThis->fScore) + newObj->fThis->fScore->DecRef(); + newObj->fThis->fScore = score; + if (newObj->fThis->fScore) + newObj->fThis->fScore->IncRef(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGameScore, pyGameScore) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGameScore, pyGameScore) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGameScore::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGameScore); + PYTHON_CLASS_IMPORT_END(m); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3.cpp new file mode 100644 index 00000000..9c4974c4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3.cpp @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGeometry3.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3.h new file mode 100644 index 00000000..3dd948cb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3.h @@ -0,0 +1,124 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGeometry3_h_ +#define _pyGeometry3_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyGeometry3 - the wrapper class for hsPoint3 and hsVector3 +// +////////////////////////////////////////////////////////////////////// + +#include "hsGeometry3.h" + +#include +#include "pyGlueHelpers.h" + +class pyPoint3 +{ +protected: + pyPoint3() : fPoint(0,0,0) {} + pyPoint3(hsScalar x, hsScalar y, hsScalar z) : fPoint(x,y,z) {} + pyPoint3(hsPoint3 pt) : fPoint(pt.fX,pt.fY,pt.fZ) {} + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptPoint3); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(const hsPoint3 &obj); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyPoint3 object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyPoint3); // converts a PyObject to a pyPoint3 (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + hsPoint3 fPoint; + + // python get attributes helpers + hsScalar getX() { return fPoint.fX; } + hsScalar getY() { return fPoint.fY; } + hsScalar getZ() { return fPoint.fZ; } + + // python set attributes helpers + void setX(hsScalar x) { fPoint.fX = x; } + void setY(hsScalar y) { fPoint.fY = y; } + void setZ(hsScalar z) { fPoint.fZ = z; } + + // methods to manipulate point3's + void Zero() { fPoint.fX=0; fPoint.fY=0; fPoint.fZ=0; } + PyObject* Copy() { return pyPoint3::New(fPoint); } + hsScalar Distance(pyPoint3 other) { return hsVector3(&fPoint,&other.fPoint).Magnitude(); } + hsScalar DistanceSquared(pyPoint3 other) { return hsVector3(&fPoint,&other.fPoint).MagnitudeSquared(); } +}; + + +class pyVector3 +{ +protected: + pyVector3() : fVector(0,0,0) {} + pyVector3(hsScalar x, hsScalar y, hsScalar z) : fVector(x,y,z) {} + pyVector3(hsVector3 v) : fVector(v.fX,v.fY,v.fZ) {} + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVector3); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(const hsVector3 &obj); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVector3 object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVector3); // converts a PyObject to a pyVector3 (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + hsVector3 fVector; + + // python get attributes helpers + hsScalar getX() { return fVector.fX; } + hsScalar getY() { return fVector.fY; } + hsScalar getZ() { return fVector.fZ; } + + // python set attributes helpers + void setX(hsScalar x) { fVector.fX = x; } + void setY(hsScalar y) { fVector.fY = y; } + void setZ(hsScalar z) { fVector.fZ = z; } + + // operator methods + PyObject* operator+(const pyVector3& b) const { return pyVector3::New(fVector + b.fVector); } + PyObject* operator-(const pyVector3& b) const { return pyVector3::New(fVector - b.fVector); } + + // methods to manipulate vectors + void Normalize() { fVector.Normalize(); } + hsScalar Dot(pyVector3 other) { return fVector*other.fVector;} + PyObject* Cross(pyVector3 other) {return pyVector3::New(fVector%other.fVector); } + hsScalar Magnitude() { return fVector.Magnitude(); } + hsScalar MagnitudeSquared() { return fVector.MagnitudeSquared(); } + void Zero() { fVector.fX=0; fVector.fY=0; fVector.fZ=0; } + PyObject* Scale(hsScalar scale) { return pyVector3::New(fVector * scale); } + PyObject* Add(pyVector3& other) { return pyVector3::New(fVector + other.fVector); } + PyObject* Subtract(pyVector3& other) { return pyVector3::New(fVector - other.fVector); } + PyObject* Copy() { return pyVector3::New(fVector); } +}; + + +#endif // _pyGeometry3_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3Glue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3Glue.cpp new file mode 100644 index 00000000..6bf233e9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGeometry3Glue.cpp @@ -0,0 +1,449 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGeometry3.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptPoint3, pyPoint3); + +PYTHON_DEFAULT_NEW_DEFINITION(ptPoint3, pyPoint3) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptPoint3) + +PYTHON_INIT_DEFINITION(ptPoint3, args, keywords) +{ + float x = 0.0f, y = 0.0f, z = 0.0f; + if (!PyArg_ParseTuple(args, "|fff", &x, &y, &z)) + { + PyErr_SetString(PyExc_TypeError, "init optionally expects three floats"); + PYTHON_RETURN_INIT_ERROR; + } + + self->fThis->fPoint.fX = x; + self->fThis->fPoint.fY = y; + self->fThis->fPoint.fZ = z; + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptPoint3, getX) +{ + return PyFloat_FromDouble((double)self->fThis->getX()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptPoint3, getY) +{ + return PyFloat_FromDouble((double)self->fThis->getY()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptPoint3, getZ) +{ + return PyFloat_FromDouble((double)self->fThis->getZ()); +} + +PYTHON_METHOD_DEFINITION(ptPoint3, setX, args) +{ + float x; + if (!PyArg_ParseTuple(args, "f", &x)) + { + PyErr_SetString(PyExc_TypeError, "setX expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->setX(x); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPoint3, setY, args) +{ + float y; + if (!PyArg_ParseTuple(args, "f", &y)) + { + PyErr_SetString(PyExc_TypeError, "setY expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->setY(y); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptPoint3, setZ, args) +{ + float z; + if (!PyArg_ParseTuple(args, "f", &z)) + { + PyErr_SetString(PyExc_TypeError, "setZ expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->setZ(z); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptPoint3, zero, Zero) + +PYTHON_METHOD_DEFINITION_NOARGS(ptPoint3, copy) +{ + return self->fThis->Copy(); +} + +PYTHON_METHOD_DEFINITION(ptPoint3, distance, args) +{ + PyObject *otherObject = NULL; + if (!PyArg_ParseTuple(args, "O", &otherObject)) + { + PyErr_SetString(PyExc_TypeError, "distance expects a ptPoint3"); + PYTHON_RETURN_ERROR; + } + if (!pyPoint3::Check(otherObject)) + { + PyErr_SetString(PyExc_TypeError, "distance expects a ptPoint3"); + PYTHON_RETURN_ERROR; + } + + pyPoint3 *other = pyPoint3::ConvertFrom(otherObject); + return PyFloat_FromDouble((double)self->fThis->Distance(*other)); +} + +PYTHON_METHOD_DEFINITION(ptPoint3, distanceSq, args) +{ + PyObject *otherObject = NULL; + if (!PyArg_ParseTuple(args, "O", &otherObject)) + { + PyErr_SetString(PyExc_TypeError, "distanceSq expects a ptPoint3"); + PYTHON_RETURN_ERROR; + } + if (!pyPoint3::Check(otherObject)) + { + PyErr_SetString(PyExc_TypeError, "distanceSq expects a ptPoint3"); + PYTHON_RETURN_ERROR; + } + + pyPoint3 *other = pyPoint3::ConvertFrom(otherObject); + return PyFloat_FromDouble((double)self->fThis->DistanceSquared(*other)); +} + +PYTHON_START_METHODS_TABLE(ptPoint3) + PYTHON_METHOD_NOARGS(ptPoint3, getX, "Returns the 'x' component of the point"), + PYTHON_METHOD_NOARGS(ptPoint3, getY, "Returns the 'y' component of the point"), + PYTHON_METHOD_NOARGS(ptPoint3, getZ, "Returns the 'z' component of the point"), + PYTHON_METHOD(ptPoint3, setX, "Params: x\nSets the 'x' component of the point"), + PYTHON_METHOD(ptPoint3, setY, "Params: y\nSets the 'y' component of the point"), + PYTHON_METHOD(ptPoint3, setZ, "Params: z\nSets the 'z' component of the point"), + PYTHON_BASIC_METHOD(ptPoint3, zero, "Sets the 'x','y' and the 'z' component to zero"), + PYTHON_METHOD_NOARGS(ptPoint3, copy, "Returns a copy of the point in another ptPoint3 object"), + PYTHON_METHOD(ptPoint3, distance, "Params: other\nComputes the distance from this point to 'other' point"), + PYTHON_METHOD(ptPoint3, distanceSq, "Params: other\nComputes the distance squared from this point to 'other' point\n" + "- this function is faster than distance(other)"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptPoint3, "Params: x=0, y=0, z=0\nPlasma Point class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptPoint3, pyPoint3) + +PyObject *pyPoint3::New(const hsPoint3 &obj) +{ + ptPoint3 *newObj = (ptPoint3*)ptPoint3_type.tp_new(&ptPoint3_type, NULL, NULL); + newObj->fThis->fPoint.Set(&obj); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptPoint3, pyPoint3) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptPoint3, pyPoint3) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyPoint3::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptPoint3); + PYTHON_CLASS_IMPORT_END(m); +} + +// glue functions +PYTHON_CLASS_DEFINITION(ptVector3, pyVector3); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVector3, pyVector3) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVector3) + +PYTHON_INIT_DEFINITION(ptVector3, args, keywords) +{ + float x = 0.0f, y = 0.0f, z = 0.0f; + if (!PyArg_ParseTuple(args, "|fff", &x, &y, &z)) + { + PyErr_SetString(PyExc_TypeError, "init optionally expects three floats"); + PYTHON_RETURN_INIT_ERROR; + } + + self->fThis->fVector.fX = x; + self->fThis->fVector.fY = y; + self->fThis->fVector.fZ = z; + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVector3, getX) +{ + return PyFloat_FromDouble((double)self->fThis->getX()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVector3, getY) +{ + return PyFloat_FromDouble((double)self->fThis->getY()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVector3, getZ) +{ + return PyFloat_FromDouble((double)self->fThis->getZ()); +} + +PYTHON_METHOD_DEFINITION(ptVector3, setX, args) +{ + float x; + if (!PyArg_ParseTuple(args, "f", &x)) + { + PyErr_SetString(PyExc_TypeError, "setX expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->setX(x); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVector3, setY, args) +{ + float y; + if (!PyArg_ParseTuple(args, "f", &y)) + { + PyErr_SetString(PyExc_TypeError, "setY expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->setY(y); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVector3, setZ, args) +{ + float z; + if (!PyArg_ParseTuple(args, "f", &z)) + { + PyErr_SetString(PyExc_TypeError, "setZ expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->setZ(z); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptVector3, zero, Zero) + +PYTHON_METHOD_DEFINITION_NOARGS(ptVector3, copy) +{ + return self->fThis->Copy(); +} + +PYTHON_METHOD_DEFINITION(ptVector3, scale, args) +{ + float scale; + if (!PyArg_ParseTuple(args, "f", &scale)) + { + PyErr_SetString(PyExc_TypeError, "scale expects a float"); + PYTHON_RETURN_ERROR; + } + return self->fThis->Scale(scale); +} + +PYTHON_METHOD_DEFINITION(ptVector3, add, args) +{ + PyObject *otherObject; + if (!PyArg_ParseTuple(args, "O", &otherObject)) + { + PyErr_SetString(PyExc_TypeError, "add expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(otherObject)) + { + PyErr_SetString(PyExc_TypeError, "add expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *other = pyVector3::ConvertFrom(otherObject); + return self->fThis->Add(*other); +} + +PYTHON_METHOD_DEFINITION(ptVector3, subtract, args) +{ + PyObject *otherObject; + if (!PyArg_ParseTuple(args, "O", &otherObject)) + { + PyErr_SetString(PyExc_TypeError, "subtract expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(otherObject)) + { + PyErr_SetString(PyExc_TypeError, "subtract expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *other = pyVector3::ConvertFrom(otherObject); + return self->fThis->Subtract(*other); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptVector3, normalize, Normalize) + +PYTHON_METHOD_DEFINITION(ptVector3, dotProduct, args) +{ + PyObject *otherObject; + if (!PyArg_ParseTuple(args, "O", &otherObject)) + { + PyErr_SetString(PyExc_TypeError, "dotProduct expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(otherObject)) + { + PyErr_SetString(PyExc_TypeError, "dotProduct expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *other = pyVector3::ConvertFrom(otherObject); + return PyFloat_FromDouble((double)self->fThis->Dot(*other)); +} + +PYTHON_METHOD_DEFINITION(ptVector3, crossProduct, args) +{ + PyObject *otherObject; + if (!PyArg_ParseTuple(args, "O", &otherObject)) + { + PyErr_SetString(PyExc_TypeError, "crossProduct expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(otherObject)) + { + PyErr_SetString(PyExc_TypeError, "crossProduct expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + pyVector3 *other = pyVector3::ConvertFrom(otherObject); + return self->fThis->Cross(*other); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVector3, length) +{ + return PyFloat_FromDouble((double)self->fThis->Magnitude()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVector3, lengthSq) +{ + return PyFloat_FromDouble((double)self->fThis->MagnitudeSquared()); +} + +PYTHON_START_METHODS_TABLE(ptVector3) + PYTHON_METHOD_NOARGS(ptVector3, getX, "Returns the 'x' component of the vector"), + PYTHON_METHOD_NOARGS(ptVector3, getY, "Returns the 'y' component of the vector"), + PYTHON_METHOD_NOARGS(ptVector3, getZ, "Returns the 'z' component of the vector"), + PYTHON_METHOD(ptVector3, setX, "Params: x\nSets the 'x' component of the vector"), + PYTHON_METHOD(ptVector3, setY, "Params: y\nSets the 'y' component of the vector"), + PYTHON_METHOD(ptVector3, setZ, "Params: z\nSets the 'z' component of the vector"), + PYTHON_BASIC_METHOD(ptVector3, zero, "Zeros the vector's components"), + PYTHON_METHOD_NOARGS(ptVector3, copy, "Copies the vector into another one (which it returns)"), + PYTHON_METHOD(ptVector3, scale, "Params: scale\nScale the vector by scale"), + PYTHON_METHOD(ptVector3, add, "Params: other\nAdds other to the current vector"), + PYTHON_METHOD(ptVector3, subtract, "Params: other\nSubtracts other from the current vector"), + PYTHON_BASIC_METHOD(ptVector3, normalize, "Normalizes the vector to length 1"), + PYTHON_METHOD(ptVector3, dotProduct, "Params: other\nFinds the dot product between other and this vector"), + PYTHON_METHOD(ptVector3, crossProduct, "Params: other\nFinds the cross product between other and this vector"), + PYTHON_METHOD_NOARGS(ptVector3, length, "Returns the length of the vector"), + PYTHON_METHOD_NOARGS(ptVector3, lengthSq, "Returns the length of the vector, squared\n" + "- this function is faster then length(other)"), +PYTHON_END_METHODS_TABLE; + +PyObject *ptVector3_sub(PyObject *v, PyObject *w) +{ + if (pyVector3::Check(v)) + { + pyVector3 *me = pyVector3::ConvertFrom(v); + if (pyVector3::Check(w)) + { + pyVector3 *other = pyVector3::ConvertFrom(w); + return (*me) - (*other); + } + } + PyErr_SetString(PyExc_NotImplementedError, "can only subtract a ptVector3 from a ptVector3"); + PYTHON_RETURN_NOT_IMPLEMENTED; +} + +PyObject *ptVector3_add(PyObject *v, PyObject *w) +{ + if (pyVector3::Check(v)) + { + pyVector3 *me = pyVector3::ConvertFrom(v); + if (pyVector3::Check(w)) + { + pyVector3 *other = pyVector3::ConvertFrom(w); + return (*me) + (*other); + } + } + PyErr_SetString(PyExc_NotImplementedError, "can only subtract a ptVector3 from a ptVector3"); + PYTHON_RETURN_NOT_IMPLEMENTED; +} + +PYTHON_START_AS_NUMBER_TABLE(ptVector3) + (binaryfunc)ptVector3_add, /*nb_add*/ + (binaryfunc)ptVector3_sub, /*nb_subtract*/ + 0, /*nb_multiply*/ + 0 /*nb_divide*/ + /* the rest can be null */ +PYTHON_END_AS_NUMBER_TABLE; + +// Type structure definition +#define ptVector3_COMPARE PYTHON_NO_COMPARE +#define ptVector3_AS_NUMBER PYTHON_DEFAULT_AS_NUMBER(ptVector3) +#define ptVector3_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptVector3_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptVector3_STR PYTHON_NO_STR +#define ptVector3_RICH_COMPARE PYTHON_NO_RICH_COMPARE +#define ptVector3_GETSET PYTHON_NO_GETSET +#define ptVector3_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptVector3, "Params: x=0, y=0, z=0\nPlasma Point class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptVector3, pyVector3) + +PyObject *pyVector3::New(const hsVector3 &obj) +{ + ptVector3 *newObj = (ptVector3*)ptVector3_type.tp_new(&ptVector3_type, NULL, NULL); + newObj->fThis->fVector.Set(&obj); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVector3, pyVector3) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVector3, pyVector3) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVector3::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVector3); + PYTHON_CLASS_IMPORT_END(m); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGlueHelpers.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGlueHelpers.h new file mode 100644 index 00000000..614988c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGlueHelpers.h @@ -0,0 +1,508 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyGlueHelpers_h_ +#define _pyGlueHelpers_h_ + +// A set of macros to take at least some of the tediousness out of creating straight python glue code + +///////////////////////////////////////////////////////////////////// +// Python class definition macros +///////////////////////////////////////////////////////////////////// + +// This defines the basic PyObject we need for python classes +#define PYTHON_CLASS_DEFINITION(pythonClassName, glueClassName) \ +struct pythonClassName \ +{ \ + PyObject_HEAD \ + glueClassName *fThis; \ +} + +// This makes sure that our python new function can access our constructors +#define PYTHON_CLASS_NEW_FRIEND(pythonClassName) friend PyObject *pythonClassName##_new(PyTypeObject *type, PyObject *args, PyObject *keywords) + +// This defines the basic new function for a class +#define PYTHON_CLASS_NEW_DEFINITION static PyObject *New() + +#define PYTHON_CLASS_NEW_IMPL(pythonClassName, glueClassName) \ +PyObject *glueClassName::New() \ +{ \ + pythonClassName *newObj = (pythonClassName*)pythonClassName##_type.tp_new(&pythonClassName##_type, NULL, NULL); \ + return (PyObject*)newObj; \ +} + +// This defines the basic check function for a class +#define PYTHON_CLASS_CHECK_DEFINITION static bool Check(PyObject *obj) + +#define PYTHON_CLASS_CHECK_IMPL(pythonClassName, glueClassName) \ +bool glueClassName::Check(PyObject *obj) \ +{ \ + return PyObject_TypeCheck(obj, &pythonClassName##_type); \ +} + +// This defines the basic convert from function for a class +#define PYTHON_CLASS_CONVERT_FROM_DEFINITION(glueClassName) static glueClassName *ConvertFrom(PyObject *obj) + +#define PYTHON_CLASS_CONVERT_FROM_IMPL(pythonClassName, glueClassName) \ +glueClassName *glueClassName::ConvertFrom(PyObject *obj) \ +{ \ + if (!Check(obj)) \ + { \ + PyErr_SetString(PyExc_TypeError, "object is not a " #pythonClassName); \ + return NULL; \ + } \ + return ((pythonClassName*)obj)->fThis; \ +} + +///////////////////////////////////////////////////////////////////// +// Python type definition macros +///////////////////////////////////////////////////////////////////// + +// This starts off the type definition (however most of the data still needs to be filled in by hand +#define PYTHON_TYPE_START(pythonClassName) \ +static PyTypeObject pythonClassName##_type = { \ + PyObject_HEAD_INIT(NULL) + +// and easy terminator to make things look pretty +#define PYTHON_TYPE_END } + +// the default new function definition +#define PYTHON_DEFAULT_NEW_DEFINITION(pythonClassName, glueClassName) \ +PyObject *pythonClassName##_new(PyTypeObject *type, PyObject *, PyObject *) \ +{ \ + pythonClassName *self; \ + self = (pythonClassName*)type->tp_alloc(type, 0); \ + if (self != NULL) \ + { \ + self->fThis = TRACKED_NEW glueClassName(); \ + if (self->fThis == NULL) \ + { \ + Py_DECREF(self); \ + return NULL; \ + } \ + } \ +\ + return (PyObject*)self; \ +} + +#define PYTHON_DEFAULT_NEW(pythonClassName) pythonClassName##_new + +// the default dealloc function definition +#define PYTHON_DEFAULT_DEALLOC_DEFINITION(pythonClassName) \ +void pythonClassName##_dealloc(pythonClassName *self) \ +{ \ + delete self->fThis; \ + self->ob_type->tp_free((PyObject*)self); \ +} + +#define PYTHON_DEFAULT_DEALLOC(pythonClassName) (destructor)pythonClassName##_dealloc + +// init function defines +#define PYTHON_RETURN_INIT_ERROR return -1; +#define PYTHON_RETURN_INIT_OK return 0; + +// basic no init function stuff, for when you don't want the class to be created by python +#define PYTHON_NO_INIT_DEFINITION(pythonClassName) \ +int pythonClassName##___init__(pythonClassName *, PyObject *, PyObject *) \ +{ \ + PyErr_SetString(PyExc_RuntimeError, "Cannot create " #pythonClassName " objects from Python"); \ + PYTHON_RETURN_INIT_ERROR; \ +} + +#define PYTHON_INIT_DEFINITION(pythonClassName, argsVar, keywordsVar) \ +int pythonClassName##___init__(pythonClassName *self, PyObject *args, PyObject *keywords) + +#define PYTHON_DEFAULT_INIT(pythonClassName) (initproc)pythonClassName##___init__ + +// message table default name +#define PYTHON_DEFAULT_METHODS_TABLE(pythonClassName) pythonClassName##_methods + +// most glue classes can get away with this default structure +#define PLASMA_DEFAULT_TYPE(pythonClassName, docString) \ +PYTHON_TYPE_START(pythonClassName) \ + 0, /* ob_size */ \ + "Plasma." #pythonClassName, /* tp_name */ \ + sizeof(pythonClassName), /* tp_basicsize */ \ + 0, /* tp_itemsize */ \ + PYTHON_DEFAULT_DEALLOC(pythonClassName), /* tp_dealloc */ \ + 0, /* tp_print */ \ + 0, /* tp_getattr */ \ + 0, /* tp_setattr */ \ + 0, /* tp_compare */ \ + 0, /* tp_repr */ \ + 0, /* tp_as_number */ \ + 0, /* tp_as_sequence */ \ + 0, /* tp_as_mapping */ \ + 0, /* tp_hash */ \ + 0, /* tp_call */ \ + 0, /* tp_str */ \ + 0, /* tp_getattro */ \ + 0, /* tp_setattro */ \ + 0, /* tp_as_buffer */ \ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ \ + docString, /* tp_doc */ \ + 0, /* tp_traverse */ \ + 0, /* tp_clear */ \ + 0, /* tp_richcompare */ \ + 0, /* tp_weaklistoffset */ \ + 0, /* tp_iter */ \ + 0, /* tp_iternext */ \ + PYTHON_DEFAULT_METHODS_TABLE(pythonClassName), /* tp_methods */ \ + 0, /* tp_members */ \ + 0, /* tp_getset */ \ + 0, /* tp_base */ \ + 0, /* tp_dict */ \ + 0, /* tp_descr_get */ \ + 0, /* tp_descr_set */ \ + 0, /* tp_dictoffset */ \ + PYTHON_DEFAULT_INIT(pythonClassName), /* tp_init */ \ + 0, /* tp_alloc */ \ + PYTHON_DEFAULT_NEW(pythonClassName),/* tp_new */ \ +PYTHON_TYPE_END + +// default compare/rich compare function name +#define PYTHON_DEFAULT_COMPARE(pythonClassName) pythonClassName##_compare +#define PYTHON_NO_COMPARE 0 +#define PYTHON_DEFAULT_RICH_COMPARE(pythonClassName) pythonClassName##_richCompare +#define PYTHON_NO_RICH_COMPARE 0 + +// default as_ table names +#define PYTHON_DEFAULT_AS_NUMBER(pythonClassName) &pythonClassName##_as_number +#define PYTHON_NO_AS_NUMBER 0 +#define PYTHON_DEFAULT_AS_SEQUENCE(pythonClassName) &pythonClassName##_as_sequence +#define PYTHON_NO_AS_SEQUENCE 0 +#define PYTHON_DEFAULT_AS_MAPPING(pythonClassName) &pythonClassName##_as_mapping +#define PYTHON_NO_AS_MAPPING 0 + +// str function +#define PYTHON_DEFAULT_STR(pythonClassName) (reprfunc)pythonClassName##_str +#define PYTHON_NO_STR 0 + +// get/set table default name +#define PYTHON_DEFAULT_GETSET(pythonClassName) pythonClassName##_getseters +#define PYTHON_NO_GETSET 0 + +// default base pointer +#define PYTHON_DEFAULT_BASE_TYPE(glueBaseClass) glueBaseClass::type_ptr +#define PYTHON_NO_BASE 0 + +// for glue functions that need custom stuff, you need to define the macros yourself +#define PLASMA_CUSTOM_TYPE(pythonClassName, docString) \ + PYTHON_TYPE_START(pythonClassName) \ + 0, /* ob_size */ \ + "Plasma." #pythonClassName, /* tp_name */ \ + sizeof(pythonClassName), /* tp_basicsize */ \ + 0, /* tp_itemsize */ \ + PYTHON_DEFAULT_DEALLOC(pythonClassName), /* tp_dealloc */ \ + 0, /* tp_print */ \ + 0, /* tp_getattr */ \ + 0, /* tp_setattr */ \ + pythonClassName##_COMPARE, /* tp_compare */ \ + 0, /* tp_repr */ \ + pythonClassName##_AS_NUMBER, /* tp_as_number */ \ + pythonClassName##_AS_SEQUENCE, /* tp_as_sequence */ \ + pythonClassName##_AS_MAPPING, /* tp_as_mapping */ \ + 0, /* tp_hash */ \ + 0, /* tp_call */ \ + pythonClassName##_STR, /* tp_str */ \ + 0, /* tp_getattro */ \ + 0, /* tp_setattro */ \ + 0, /* tp_as_buffer */ \ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ \ + docString, /* tp_doc */ \ + 0, /* tp_traverse */ \ + 0, /* tp_clear */ \ + pythonClassName##_RICH_COMPARE, /* tp_richcompare */ \ + 0, /* tp_weaklistoffset */ \ + 0, /* tp_iter */ \ + 0, /* tp_iternext */ \ + PYTHON_DEFAULT_METHODS_TABLE(pythonClassName), /* tp_methods */ \ + 0, /* tp_members */ \ + pythonClassName##_GETSET, /* tp_getset */ \ + pythonClassName##_BASE, /* tp_base */ \ + 0, /* tp_dict */ \ + 0, /* tp_descr_get */ \ + 0, /* tp_descr_set */ \ + 0, /* tp_dictoffset */ \ + PYTHON_DEFAULT_INIT(pythonClassName), /* tp_init */ \ + 0, /* tp_alloc */ \ + PYTHON_DEFAULT_NEW(pythonClassName),/* tp_new */ \ +PYTHON_TYPE_END + +// for conviencence when we just need a base class +#define PLASMA_DEFAULT_TYPE_WBASE(pythonClassName, glueBaseClass, docString) \ +PYTHON_TYPE_START(pythonClassName) \ + 0, /* ob_size */ \ + "Plasma." #pythonClassName, /* tp_name */ \ + sizeof(pythonClassName), /* tp_basicsize */ \ + 0, /* tp_itemsize */ \ + PYTHON_DEFAULT_DEALLOC(pythonClassName), /* tp_dealloc */ \ + 0, /* tp_print */ \ + 0, /* tp_getattr */ \ + 0, /* tp_setattr */ \ + 0, /* tp_compare */ \ + 0, /* tp_repr */ \ + 0, /* tp_as_number */ \ + 0, /* tp_as_sequence */ \ + 0, /* tp_as_mapping */ \ + 0, /* tp_hash */ \ + 0, /* tp_call */ \ + 0, /* tp_str */ \ + 0, /* tp_getattro */ \ + 0, /* tp_setattro */ \ + 0, /* tp_as_buffer */ \ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ \ + docString, /* tp_doc */ \ + 0, /* tp_traverse */ \ + 0, /* tp_clear */ \ + 0, /* tp_richcompare */ \ + 0, /* tp_weaklistoffset */ \ + 0, /* tp_iter */ \ + 0, /* tp_iternext */ \ + PYTHON_DEFAULT_METHODS_TABLE(pythonClassName), /* tp_methods */ \ + 0, /* tp_members */ \ + 0, /* tp_getset */ \ + glueBaseClass::type_ptr, /* tp_base */ \ + 0, /* tp_dict */ \ + 0, /* tp_descr_get */ \ + 0, /* tp_descr_set */ \ + 0, /* tp_dictoffset */ \ + PYTHON_DEFAULT_INIT(pythonClassName), /* tp_init */ \ + 0, /* tp_alloc */ \ + PYTHON_DEFAULT_NEW(pythonClassName),/* tp_new */ \ +PYTHON_TYPE_END + +// small macros so that the type object can be accessed outside the glue file (for subclassing) +#define PYTHON_EXPOSE_TYPE static PyTypeObject* type_ptr +#define PYTHON_EXPOSE_TYPE_DEFINITION(pythonClass, glueClass) PyTypeObject* glueClass::type_ptr = &pythonClass##_type + +///////////////////////////////////////////////////////////////////// +// Python class import macros +///////////////////////////////////////////////////////////////////// + +// called at the beginning of the class definition function to grab the module +#define PYTHON_CLASS_IMPORT_START(m) \ +if (m == NULL) \ + return; \ +\ +Py_INCREF(m) + +// called at the end of the class definition function to release the module +#define PYTHON_CLASS_IMPORT_END(m) Py_DECREF(m) + +// called for each class you want to add to the module +#define PYTHON_CLASS_IMPORT(m, pythonClassName) \ +if (PyType_Ready(&pythonClassName##_type) < 0) \ + return; \ + \ +Py_INCREF(&pythonClassName##_type); \ +PyModule_AddObject(m, #pythonClassName, (PyObject*)&pythonClassName##_type) + +///////////////////////////////////////////////////////////////////// +// Python method macros +///////////////////////////////////////////////////////////////////// + +// Handles the three types of methods python uses +#define PYTHON_METHOD_DEFINITION(pythonClassName, methodName, argsVar) \ + static PyObject *pythonClassName##_##methodName(pythonClassName *self, PyObject *argsVar) +#define PYTHON_METHOD_DEFINITION_NOARGS(pythonClassName, methodName) \ + static PyObject *pythonClassName##_##methodName(pythonClassName *self) +#define PYTHON_METHOD_DEFINITION_WKEY(pythonClassName, methodName, argsVar, keywordsVar) \ + static PyObject *pythonClassName##_##methodName(pythonClassName *self, PyObject *argsVar, PyObject *keywordsVar) + +// A very basic function, just calls the class method and returns None +#define PYTHON_BASIC_METHOD_DEFINITION(pythonClassName, methodName, classMethodName) \ +static PyObject *pythonClassName##_##methodName(pythonClassName *self) \ +{ \ + self->fThis->classMethodName(); \ + PYTHON_RETURN_NONE; \ +} + +// Different basic return types +#define PYTHON_RETURN_ERROR {return NULL;} +#define PYTHON_RETURN_NONE {Py_INCREF(Py_None); return Py_None;} +#define PYTHON_RETURN_BOOL(testValue) \ +{ \ + if (testValue) \ + return PyInt_FromLong((long)1); \ + else \ + return PyInt_FromLong((long)0); \ +} +#define PYTHON_RETURN_NOT_IMPLEMENTED {Py_INCREF(Py_NotImplemented); return Py_NotImplemented;} + +// method table start +#define PYTHON_START_METHODS_TABLE(pythonClassName) static PyMethodDef pythonClassName##_methods[] = { + +// method table end (automatically adds sentinal value) +#define PYTHON_END_METHODS_TABLE {NULL} } + +// basic method +#define PYTHON_METHOD(pythonClassName, methodName, docString) \ + {#methodName, (PyCFunction)pythonClassName##_##methodName, METH_VARARGS, docString} + +// method with no arguments +#define PYTHON_METHOD_NOARGS(pythonClassName, methodName, docString) \ + {#methodName, (PyCFunction)pythonClassName##_##methodName, METH_NOARGS, docString} + +// method with keywords +#define PYTHON_METHOD_WKEY(pythonClassName, methodName, docString) \ + {#methodName, (PyCFunction)pythonClassName##_##methodName, METH_VARARGS | METH_KEYWORDS, docString} + +// really basic method +#define PYTHON_BASIC_METHOD(pythonClassName, methodName, docString) \ + {#methodName, (PyCFunction)pythonClassName##_##methodName, METH_NOARGS, docString} + +///////////////////////////////////////////////////////////////////// +// Get/set macros +///////////////////////////////////////////////////////////////////// + +// setter defines +#define PYTHON_RETURN_SET_ERROR return -1; +#define PYTHON_RETURN_SET_OK return 0; + +// getter function definition +#define PYTHON_GET_DEFINITION(pythonClassName, attribName) \ + static PyObject *pythonClassName##_get##attribName(pythonClassName *self, void *closure) + +// setter function definition +#define PYTHON_SET_DEFINITION(pythonClassName, attribName, valueVarName) \ + static int pythonClassName##_set##attribName(pythonClassName *self, PyObject *valueVarName, void *closure) + +// read-only setter function +#define PYTHON_SET_DEFINITION_READONLY(pythonClassName, attribName) \ +int pythonClassName##_set##attribName(PyObject *self, PyObject *value, void *closure) \ +{ \ + PyErr_SetString(PyExc_RuntimeError, #attribName " is read-only"); \ + PYTHON_RETURN_SET_ERROR; \ +} + +// starts off the get/set table +#define PYTHON_START_GETSET_TABLE(pythonClassName) static PyGetSetDef pythonClassName##_getseters[] = { + +// and easy terminator to make things look pretty (automatically adds sentinal value) +#define PYTHON_END_GETSET_TABLE {NULL} } + +// the get/set definition +#define PYTHON_GETSET(pythonClassName, attribName, docString) {#attribName, \ + (getter)pythonClassName##_get##attribName, (setter)pythonClassName##_set##attribName, \ + docString, NULL} + +///////////////////////////////////////////////////////////////////// +// as_ table macros +///////////////////////////////////////////////////////////////////// + +// as_number table +#define PYTHON_START_AS_NUMBER_TABLE(pythonClassName) static PyNumberMethods pythonClassName##_as_number = { +#define PYTHON_END_AS_NUMBER_TABLE } + +// as_sequence table +#define PYTHON_START_AS_SEQUENCE_TABLE(pythonClassName) static PySequenceMethods pythonClassName##_as_sequence = { +#define PYTHON_END_AS_SEQUENCE_TABLE } + +// as_mapping table +#define PYTHON_START_AS_MAPPING_TABLE(pythonClassName) static PyMappingMethods pythonClassName##_as_mapping = { +#define PYTHON_END_AS_MAPPING_TABLE } + +///////////////////////////////////////////////////////////////////// +// Compare/Rich compare functions +///////////////////////////////////////////////////////////////////// + +// compare +#define PYTHON_COMPARE_DEFINITION(pythonClassName, obj1, obj2) int pythonClassName##_compare(PyObject *obj1, PyObject *obj2) +#define PYTHON_COMPARE_LESS_THAN return -1 +#define PYTHON_COMPARE_GREATER_THAN return 1 +#define PYTHON_COMPARE_EQUAL return 0 + +// rich compare +#define PYTHON_RICH_COMPARE_DEFINITION(pythonClassName, obj1, obj2, compareType) PyObject *pythonClassName##_richCompare(PyObject *obj1, PyObject *obj2, int compareType) +#define PYTHON_RCOMPARE_TRUE return PyInt_FromLong((long)1) +#define PYTHON_RCOMPARE_FALSE return PyInt_FromLong((long)0) +#define PYTHON_RCOMPARE_ERROR return PyInt_FromLong((long)-1) + +///////////////////////////////////////////////////////////////////// +// str functions +///////////////////////////////////////////////////////////////////// + +// str function +#define PYTHON_STR_DEFINITION(pythonClassName) PyObject *pythonClassName##_str(pythonClassName *self) + +///////////////////////////////////////////////////////////////////// +// Global function macros (for those functions that aren't in a class) +///////////////////////////////////////////////////////////////////// + +// Global method +#define PYTHON_GLOBAL_METHOD_DEFINITION(methodName, argsVar, docString) \ +static PyObject *methodName(PyObject*, PyObject*); /* forward declaration so struct can go here */ \ +static PyMethodDef methodName##_method = {#methodName, methodName, METH_VARARGS, docString}; \ +static PyObject *methodName(PyObject *self, PyObject *argsVar) /* and now for the actual function */ + +// Global method with no arguments +#define PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(methodName, docString) \ +static PyObject *methodName(PyObject*); /* forward declaration so struct can go here */ \ +static PyMethodDef methodName##_method = {#methodName, (PyCFunction)methodName, METH_NOARGS, docString}; \ +static PyObject *methodName(PyObject *self) /* and now for the actual function */ + +// Global method with keywords +#define PYTHON_GLOBAL_METHOD_DEFINITION_WKEY(methodName, argsVar, keywordsVar, docString) \ +static PyObject *methodName(PyObject*, PyObject*, PyObject*); /* forward declaration so struct can go here */ \ +static PyMethodDef methodName##_method = {#methodName, (PyCFunction)methodName, METH_VARARGS | METH_KEYWORDS, docString}; \ +static PyObject *methodName(PyObject *self, PyObject *argsVar, PyObject *keywordsVar) /* and now for the actual function */ + +// Basic global method +#define PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(methodName, classMethodName, docString) \ +static PyObject *methodName(PyObject*); /* forward declaration so struct can go here */ \ +static PyMethodDef methodName##_method = {#methodName, (PyCFunction)methodName, METH_NOARGS, docString}; \ +static PyObject *methodName(PyObject *self) /* and now for the actual function */ \ +{ \ + classMethodName(); \ + PYTHON_RETURN_NONE; \ +} + +// this goes in the definition function +#define PYTHON_GLOBAL_METHOD(vectorVarName, methodName) vectorVarName.push_back(methodName##_method) + +// not necessary, but for continuity with the NOARGS function definition above +#define PYTHON_GLOBAL_METHOD_NOARGS(vectorVarName, methodName) vectorVarName.push_back(methodName##_method); + +// not necessary, but for continuity with the WKEY function definition above +#define PYTHON_GLOBAL_METHOD_WKEY(vectorVarName, methodName) vectorVarName.push_back(methodName##_method) + +// not necessary, but for continuity with the BASIC function definition above +#define PYTHON_BASIC_GLOBAL_METHOD(vectorVarName, methodName) vectorVarName.push_back(methodName##_method) + +///////////////////////////////////////////////////////////////////// +// Enum glue (these should all be inside a function) +///////////////////////////////////////////////////////////////////// + +// the start of an enum block +#define PYTHON_ENUM_START(enumName) std::map enumName##_enumValues + +// for each element of the enum +#define PYTHON_ENUM_ELEMENT(enumName, elementName, elementValue) enumName##_enumValues[#elementName] = elementValue + +// to finish off and define the enum +#define PYTHON_ENUM_END(m, enumName) pyEnum::MakeEnum(m, #enumName, enumName##_enumValues) + +#endif // _pyGlueHelpers_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShader.cpp new file mode 100644 index 00000000..7ac82da6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShader.cpp @@ -0,0 +1,216 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGrassShader.h" +#include "../plSurface/plGrassShaderMod.h" + +pyGrassShader::pyGrassShader() +{ + fShaderKey = nil; +} + +pyGrassShader::pyGrassShader(plKey key) +{ + fShaderKey = key; +} + +pyGrassShader::pyGrassShader(pyKey& key) +{ + fShaderKey = key.getKey(); +} + +void pyGrassShader::SetKey(plKey key) +{ + fShaderKey = key; +} + +////////////////////////////////////////////////////////////////////// +// Setter functions +////////////////////////////////////////////////////////////////////// + +void pyGrassShader::SetWaveDistortion(int waveNum, const std::vector & distortion) +{ + if ((waveNum < 0)||(waveNum >= plGrassShaderMod::kNumWaves)) + { + char errmsg[256]; + sprintf(errmsg,"setWaveDistortion expects the waveNum to be between 0 and %d (inclusive)", plGrassShaderMod::kNumWaves - 1); + PyErr_SetString(PyExc_ValueError, errmsg); + return; + } + if (distortion.size() != 3) + { + char errmsg[256]; + sprintf(errmsg,"setWaveDistortion expects the direction to be a three-element tuple only"); + PyErr_SetString(PyExc_TypeError, errmsg); + return; + } + if (fShaderKey) + { + plGrassShaderMod* shader = plGrassShaderMod::ConvertNoRef(fShaderKey->ObjectIsLoaded()); + if (shader) + { + shader->fWaves[waveNum].fDistX = distortion[0]; + shader->fWaves[waveNum].fDistY = distortion[1]; + shader->fWaves[waveNum].fDistZ = distortion[2]; + shader->RefreshWaves(); + } + } +} + +void pyGrassShader::SetWaveDirection(int waveNum, const std::vector & direction) +{ + if ((waveNum < 0)||(waveNum >= plGrassShaderMod::kNumWaves)) + { + char errmsg[256]; + sprintf(errmsg,"setWaveDirection expects the waveNum to be between 0 and %d (inclusive)", plGrassShaderMod::kNumWaves - 1); + PyErr_SetString(PyExc_ValueError, errmsg); + return; + } + if (direction.size() != 2) + { + char errmsg[256]; + sprintf(errmsg,"setWaveDirection expects the direction to be a two-element tuple only"); + PyErr_SetString(PyExc_TypeError, errmsg); + return; + } + if (fShaderKey) + { + plGrassShaderMod* shader = plGrassShaderMod::ConvertNoRef(fShaderKey->ObjectIsLoaded()); + if (shader) + { + shader->fWaves[waveNum].fDirX = direction[0]; + shader->fWaves[waveNum].fDirY = direction[1]; + shader->RefreshWaves(); + } + } +} + +void pyGrassShader::SetWaveSpeed(int waveNum, hsScalar speed) +{ + if ((waveNum < 0)||(waveNum >= plGrassShaderMod::kNumWaves)) + { + char errmsg[256]; + sprintf(errmsg,"setWaveSpeed expects the waveNum to be between 0 and %d (inclusive)", plGrassShaderMod::kNumWaves - 1); + PyErr_SetString(PyExc_ValueError, errmsg); + return; + } + if (fShaderKey) + { + plGrassShaderMod* shader = plGrassShaderMod::ConvertNoRef(fShaderKey->ObjectIsLoaded()); + if (shader) + { + shader->fWaves[waveNum].fSpeed = speed; + shader->RefreshWaves(); + } + } +} + +////////////////////////////////////////////////////////////////////// +// Getter functions +////////////////////////////////////////////////////////////////////// + +std::vector pyGrassShader::GetWaveDistortion(int waveNum) const +{ + std::vector retVal; + retVal.push_back(-1); + retVal.push_back(-1); + retVal.push_back(-1); + if ((waveNum < 0)||(waveNum >= plGrassShaderMod::kNumWaves)) + { + char errmsg[256]; + sprintf(errmsg,"getWaveDistortion expects the waveNum to be between 0 and %d (inclusive)", plGrassShaderMod::kNumWaves - 1); + PyErr_SetString(PyExc_ValueError, errmsg); + return retVal; + } + if (fShaderKey) + { + plGrassShaderMod* shader = plGrassShaderMod::ConvertNoRef(fShaderKey->ObjectIsLoaded()); + if (shader) + { + retVal[0] = shader->fWaves[waveNum].fDistX; + retVal[1] = shader->fWaves[waveNum].fDistY; + retVal[2] = shader->fWaves[waveNum].fDistZ; + return retVal; + } + } + return retVal; +} + +std::vector pyGrassShader::GetWaveDirection(int waveNum) const +{ + std::vector retVal; + retVal.push_back(-1); + retVal.push_back(-1); + if ((waveNum < 0)||(waveNum >= plGrassShaderMod::kNumWaves)) + { + char errmsg[256]; + sprintf(errmsg,"getWaveDirection expects the waveNum to be between 0 and %d (inclusive)", plGrassShaderMod::kNumWaves - 1); + PyErr_SetString(PyExc_ValueError, errmsg); + return retVal; + } + if (fShaderKey) + { + plGrassShaderMod* shader = plGrassShaderMod::ConvertNoRef(fShaderKey->ObjectIsLoaded()); + if (shader) + { + retVal[0] = shader->fWaves[waveNum].fDirX; + retVal[1] = shader->fWaves[waveNum].fDirY; + return retVal; + } + } + return retVal; +} + +hsScalar pyGrassShader::GetWaveSpeed(int waveNum) const +{ + if ((waveNum < 0)||(waveNum >= plGrassShaderMod::kNumWaves)) + { + char errmsg[256]; + sprintf(errmsg,"getWaveSpeed expects the waveNum to be between 0 and %d (inclusive)", plGrassShaderMod::kNumWaves - 1); + PyErr_SetString(PyExc_ValueError, errmsg); + return -1; + } + if (fShaderKey) + { + plGrassShaderMod* shader = plGrassShaderMod::ConvertNoRef(fShaderKey->ObjectIsLoaded()); + if (shader) + return shader->fWaves[waveNum].fSpeed; + } + return -1; +} + +////////////////////////////////////////////////////////////////////// +// Other functions +////////////////////////////////////////////////////////////////////// + +void pyGrassShader::ResetWaves() +{ + if (fShaderKey) + { + plGrassShaderMod* shader = plGrassShaderMod::ConvertNoRef(fShaderKey->ObjectIsLoaded()); + if (shader) + shader->ResetWaves(); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShader.h new file mode 100644 index 00000000..f8b75a4a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShader.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyGrassShader_h +#define pyGrassShader_h + +#include "hsStlUtils.h" + +#include "pyKey.h" + +#include +#include "pyGlueHelpers.h" + + +////////////////////////////////////////////////////////////////////// +// +// pyGrassShader - a wrapper class to provide interface to the grass +// shader +// +////////////////////////////////////////////////////////////////////// + +class pyGrassShader +{ +private: + plKey fShaderKey; + +protected: + pyGrassShader(); + pyGrassShader(plKey key); + pyGrassShader(pyKey& key); +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptGrassShader); + static PyObject *New(plKey key); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyGrassShader object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyGrassShader); // converts a PyObject to a pyGrassShader (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + void SetKey(plKey key); + + void SetWaveDistortion(int waveNum, const std::vector & distortion); + void SetWaveDirection(int waveNum, const std::vector & direction); + void SetWaveSpeed(int waveNum, hsScalar speed); + + std::vector GetWaveDistortion(int waveNum) const; + std::vector GetWaveDirection(int waveNum) const; + hsScalar GetWaveSpeed(int waveNum) const; + + void ResetWaves(); +}; + + +#endif // pyGrassShader_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShaderGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShaderGlue.cpp new file mode 100644 index 00000000..d9d4a7c7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyGrassShaderGlue.cpp @@ -0,0 +1,213 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyGrassShader.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptGrassShader, pyGrassShader); + +PYTHON_DEFAULT_NEW_DEFINITION(ptGrassShader, pyGrassShader) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptGrassShader) + +PYTHON_INIT_DEFINITION(ptGrassShader, args, keywords) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "init expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "init expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->SetKey(key->getKey()); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptGrassShader, setWaveDistortion, args) +{ + int waveNum; + PyObject *tupleObject = NULL; + if (!PyArg_ParseTuple(args, "iO", &waveNum, &tupleObject)) + { + PyErr_SetString(PyExc_TypeError, "setWaveDistortion expects a integer and tuple of floats"); + PYTHON_RETURN_ERROR; + } + if (!PyTuple_Check(tupleObject)) + { + PyErr_SetString(PyExc_TypeError, "setWaveDistortion expects a integer and tuple of floats"); + PYTHON_RETURN_ERROR; + } + + int len = PyTuple_Size(tupleObject); + std::vector vecArgs; + for (int curArg = 0; curArg < len; curArg++) + { + PyObject *arg = PyTuple_GetItem(tupleObject, curArg); + if (!PyFloat_Check(arg)) + { + PyErr_SetString(PyExc_TypeError, "setWaveDistortion expects a integer and tuple of floats"); + PYTHON_RETURN_ERROR; + } + vecArgs.push_back((hsScalar)PyFloat_AsDouble(arg)); + } + + self->fThis->SetWaveDistortion(waveNum, vecArgs); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGrassShader, setWaveDirection, args) +{ + int waveNum; + PyObject *tupleObject = NULL; + if (!PyArg_ParseTuple(args, "iO", &waveNum, &tupleObject)) + { + PyErr_SetString(PyExc_TypeError, "setWaveDirection expects a integer and tuple of floats"); + PYTHON_RETURN_ERROR; + } + if (!PyTuple_Check(tupleObject)) + { + PyErr_SetString(PyExc_TypeError, "setWaveDirection expects a integer and tuple of floats"); + PYTHON_RETURN_ERROR; + } + + int len = PyTuple_Size(tupleObject); + std::vector vecArgs; + for (int curArg = 0; curArg < len; curArg++) + { + PyObject *arg = PyTuple_GetItem(tupleObject, curArg); + if (!PyFloat_Check(arg)) + { + PyErr_SetString(PyExc_TypeError, "setWaveDirection expects a integer and tuple of floats"); + PYTHON_RETURN_ERROR; + } + vecArgs.push_back((hsScalar)PyFloat_AsDouble(arg)); + } + + self->fThis->SetWaveDirection(waveNum, vecArgs); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGrassShader, setWaveSpeed, args) +{ + int waveNum; + float speed; + if (!PyArg_ParseTuple(args, "if", &waveNum, &speed)) + { + PyErr_SetString(PyExc_TypeError, "setWaveSpeed expects an integer and a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetWaveSpeed(waveNum, speed); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptGrassShader, getWaveDistortion, args) +{ + int waveNum; + if (!PyArg_ParseTuple(args, "i", &waveNum)) + { + PyErr_SetString(PyExc_TypeError, "getWaveDistortion expects an integer"); + PYTHON_RETURN_ERROR; + } + + std::vector vecArgs = self->fThis->GetWaveDistortion(waveNum); + PyObject *retVal = PyTuple_New(vecArgs.size()); + for (int curArg = 0; curArg < vecArgs.size(); curArg++) + PyTuple_SetItem(retVal, curArg, PyFloat_FromDouble((double)vecArgs[curArg])); + return retVal; +} + +PYTHON_METHOD_DEFINITION(ptGrassShader, getWaveDirection, args) +{ + int waveNum; + if (!PyArg_ParseTuple(args, "i", &waveNum)) + { + PyErr_SetString(PyExc_TypeError, "getWaveDirection expects an integer"); + PYTHON_RETURN_ERROR; + } + + std::vector vecArgs = self->fThis->GetWaveDirection(waveNum); + PyObject *retVal = PyTuple_New(vecArgs.size()); + for (int curArg = 0; curArg < vecArgs.size(); curArg++) + PyTuple_SetItem(retVal, curArg, PyFloat_FromDouble((double)vecArgs[curArg])); + return retVal; +} + +PYTHON_METHOD_DEFINITION(ptGrassShader, getWaveSpeed, args) +{ + int waveNum; + if (!PyArg_ParseTuple(args, "i", &waveNum)) + { + PyErr_SetString(PyExc_TypeError, "getWaveDirection expects an integer"); + PYTHON_RETURN_ERROR; + } + return PyFloat_FromDouble((double)self->fThis->GetWaveSpeed(waveNum)); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptGrassShader, resetWaves, ResetWaves) + +PYTHON_START_METHODS_TABLE(ptGrassShader) + PYTHON_METHOD(ptGrassShader, setWaveDistortion, "Params: waveNum, distortion\nSets the wave waveNum's distortion as a tuple of x,y,z. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive"), + PYTHON_METHOD(ptGrassShader, setWaveDirection, "Params: waveNum, direction\nSets the wave waveNum's direction as a tuple of x,y. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive"), + PYTHON_METHOD(ptGrassShader, setWaveSpeed, "Params: waveNum, speed\nSets the wave waveNum's speed as a float. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive"), + + PYTHON_METHOD(ptGrassShader, getWaveDistortion, "Params: waveNum\nGets the wave waveNum's distortion as a tuple of x,y,z. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive"), + PYTHON_METHOD(ptGrassShader, getWaveDirection, "Params: waveNum\nGets the wave waveNum's direction as a tuple of x,y. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive"), + PYTHON_METHOD(ptGrassShader, getWaveSpeed, "Params: waveNum\nGets the wave waveNum's speed as a float. waveNum must be between 0 and plGrassShaderMod::kNumWaves-1 (currently 3) inclusive"), + + PYTHON_BASIC_METHOD(ptGrassShader, resetWaves, "Resets wave data to 0"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptGrassShader, "Params: key\nPlasma Grass Shader class"); + +// required functions for PyObject interoperability +PyObject *pyGrassShader::New(plKey key) +{ + ptGrassShader *newObj = (ptGrassShader*)ptGrassShader_type.tp_new(&ptGrassShader_type, NULL, NULL); + newObj->fThis->SetKey(key); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptGrassShader, pyGrassShader) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptGrassShader, pyGrassShader) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyGrassShader::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptGrassShader); + PYTHON_CLASS_IMPORT_END(m); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImage.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImage.cpp new file mode 100644 index 00000000..bc4d821f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImage.cpp @@ -0,0 +1,210 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyImage.h" + +#include "cyMisc.h" + +#ifndef BUILDING_PYPLASMA +#include "../plGImage/plMipmap.h" +#endif + +#ifndef BUILDING_PYPLASMA +plMipmap* pyImage::GetImage() +{ + if (fMipmap) + return fMipmap; + return ( plMipmap::ConvertNoRef(fMipMapKey->ObjectIsLoaded()) ); +} + +// GetPixelColor +// takes an x and y coord (x and y from 0 to 1) and returns the pixel color at that location +PyObject* pyImage::GetPixelColor(float x, float y) +{ + if (x > 1.0) x = 1.0; + if (x < 0.0) x = 0.0; + if (y > 1.0) y = 1.0; + if (y < 0.0) y = 0.0; + + plMipmap* image; + if (fMipmap) + image = fMipmap; + else + image = plMipmap::ConvertNoRef(fMipMapKey->ObjectIsLoaded()); + if (image) + { + UInt32 height = image->GetHeight(); + UInt32 width = image->GetWidth(); + UInt32 iX = (UInt32)((float)width * x); + UInt32 iY = (UInt32)((float)height * y); + hsColorRGBA pixColor; + image->SetCurrLevel(0); + UInt32 *color = image->GetAddr32(iX,iY); + pixColor.FromARGB32(*color); + return pyColor::New(pixColor); + } + PYTHON_RETURN_NONE; +} + +// GetColorLoc +// takes a color to look for and returns the x and y coord for its location (x and y from 0 to 1), if the +// color exists in more than one location, then the location with the lowest x and y will be returned. +// if the color is not found, it trys to return the closest match +PyObject* pyImage::GetColorLoc(const pyColor &color) +{ + plMipmap* image; + if (fMipmap) + image = fMipmap; + else + image = plMipmap::ConvertNoRef(fMipMapKey->ObjectIsLoaded()); + if (image) + { + UInt32 height = image->GetHeight(); + UInt32 width = image->GetWidth(); + double minSqrDist = 9999999; + hsPoint3 closestMatch; + image->SetCurrLevel(0); + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + hsColorRGBA pixColor; + pixColor.FromARGB32(*(image->GetAddr32(x,y))); + PyObject* imgColorObj = pyColor::New(pixColor); + pyColor* imgColor = pyColor::ConvertFrom(imgColorObj); + if ((*imgColor) == color) + { + Py_DECREF(imgColorObj); + float fX, fY; + fX = (float)x / (float)width; + fY = (float)y / (float)height; + return pyPoint3::New(hsPoint3(fX, fY, 0)); + } + double dist = pow((imgColor->getRed() - color.getRed()),2) + pow((imgColor->getGreen() - color.getGreen()),2) + pow((imgColor->getBlue() - color.getBlue()),2); + if (dist < minSqrDist) + { + minSqrDist = dist; + float fX, fY; + fX = (float)x / (float)width; + fY = (float)y / (float)height; + closestMatch.fX = fX; + closestMatch.fY = fY; + } + Py_DECREF(imgColorObj); + } + } + return pyPoint3::New(closestMatch); + } + PYTHON_RETURN_NONE; +} + +// GetWidth +// returns the width of the image +UInt32 pyImage::GetWidth() +{ + plMipmap* image; + if (fMipmap) + image = fMipmap; + else + image = plMipmap::ConvertNoRef(fMipMapKey->ObjectIsLoaded()); + if (image) + return image->GetWidth(); + return 0; +} + +// GetHeight +// returns the height of the image +UInt32 pyImage::GetHeight() +{ + plMipmap* image; + if (fMipmap) + image = fMipmap; + else + image = plMipmap::ConvertNoRef(fMipMapKey->ObjectIsLoaded()); + if (image) + return image->GetHeight(); + return 0; +} + +#include "../plJPEG/plJPEG.h" +void pyImage::SaveAsJPEG(const wchar* fileName, UInt8 quality) +{ + if (quality <= 0 || quality > 100) + { + quality = 75; + } + + plJPEG::Instance().SetWriteQuality( quality ); + plJPEG::Instance().WriteToFile( fileName, this->GetImage() ); +} + +#include "hsResMgr.h" +#include "../pnKeyedObject/plUoid.h" +PyObject* pyImage::LoadJPEGFromDisk(const wchar* filename, UInt16 width, UInt16 height) +{ + plMipmap* theMipmap = plJPEG::Instance().ReadFromFile(filename); + if (theMipmap) + { + if (width > 0 && height > 0) + { + if (!theMipmap->ResizeNicely(width, height, plMipmap::ScaleFilter::kDefaultFilter)) + { + delete theMipmap; + PYTHON_RETURN_NONE; + } + } + + // let's create a nice name for this thing based on the filename + std::string name = "PtImageFromDisk_"; + const wchar* i = filename; + int charsChecked = 0; + + while (*i != '\\' && *i != '\0' && charsChecked < 1024) + { + i++; + charsChecked++; + } + + if (*i == '\0') + { + i = filename; + } + else + { + i++; + } + + char* cName = hsWStringToString(i); + name = name + cName; + + hsgResMgr::ResMgr()->NewKey(name.c_str(), theMipmap, plLocation::kGlobalFixedLoc); + + return pyImage::New( theMipmap ); + } + else + PYTHON_RETURN_NONE; +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImage.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImage.h new file mode 100644 index 00000000..b3e87aaa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImage.h @@ -0,0 +1,157 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyImage_h +#define pyImage_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyImage +// +// PURPOSE: Class wrapper for Python to a plMipMap image +// + +#include "hsStlUtils.h" + +#include "pyKey.h" +#include "pyColor.h" + +#ifndef BUILDING_PYPLASMA +#include "pyGeometry3.h" +#include "../plGImage/plMipmap.h" +#endif + +#include +#include "pyGlueHelpers.h" + +class plKey; + +class pyImage +{ +protected: + plKey fMipMapKey; +#ifndef BUILDING_PYPLASMA + plMipmap* fMipmap; +#endif + + pyImage() // for python glue only, do NOT call + { + fMipMapKey = nil; +#ifndef BUILDING_PYPLASMA + fMipmap = nil; +#endif + } + + // Constructor from C++ + pyImage(plKey mipmapKey) + { + fMipMapKey = mipmapKey; +#ifndef BUILDING_PYPLASMA + fMipmap = nil; +#endif + } + +#ifndef BUILDING_PYPLASMA + // Constructor from C++ ... use pointer to instead of plKey + pyImage(plMipmap* mipmap) + { + fMipmap = mipmap; + fMipMapKey = fMipmap->GetKey(); + + if (fMipMapKey) + { + fMipMapKey->RefObject(); + } + } +#endif + + // contructor from Python + pyImage(pyKey& mipmapKey) + { + fMipMapKey = mipmapKey.getKey(); +#ifndef BUILDING_PYPLASMA + fMipmap = nil; +#endif + } + +public: +#ifndef BUILDING_PYPLASMA + pyImage::~pyImage() + { + if (fMipmap && fMipMapKey) + fMipMapKey->UnRefObject(); + } +#endif + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptImage); +#ifndef BUILDING_PYPLASMA + static PyObject *New(plMipmap* mipmap); +#endif + static PyObject *New(plKey mipmapKey); + static PyObject *New(pyKey& mipmapKey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyImage object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyImage); // converts a PyObject to a pyImage (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaMethods(std::vector &methods); + + void setKey(pyKey& mipmapKey) // only for python glue, do NOT call + { +#ifndef BUILDING_PYPLASMA + if (fMipmap && fMipMapKey) + fMipMapKey->UnRefObject(); + fMipmap = nil; +#endif + fMipMapKey = mipmapKey.getKey(); + } + + // override the equals to operator + hsBool operator==(const pyImage &image) const + { + // only thing that needs testing is the plKey, which is unique for all + if ( fMipMapKey == ((pyImage&)image).GetKey() ) + return true; + else + return false; + } + hsBool operator!=(const pyImage &image) const { return !(image == *this); } + + // for C++ access + plKey GetKey() { return fMipmap ? fMipmap->GetKey() : fMipMapKey; } +#ifndef BUILDING_PYPLASMA + plMipmap* GetImage(); + + // for python access + PyObject *GetPixelColor(float x, float y); // returns the color at a specific x,y position (x and y from 0 to 1) - returns pyColor + PyObject *GetColorLoc(const pyColor &color); // returns the x,y position of a color (x and y from 0 to 1) - returns pyPoint3 + UInt32 GetWidth(); // returns the width of the image + UInt32 GetHeight(); // returns the height of the image + void SaveAsJPEG(const wchar* fileName, UInt8 quality = 75); + static PyObject* LoadJPEGFromDisk(const wchar* filename, UInt16 width, UInt16 height); // returns pyImage +#endif +}; + +#endif // pyImage_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImageGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImageGlue.cpp new file mode 100644 index 00000000..223beda6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyImageGlue.cpp @@ -0,0 +1,268 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyImage.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptImage, pyImage); + +PYTHON_DEFAULT_NEW_DEFINITION(ptImage, pyImage) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptImage) + +PYTHON_INIT_DEFINITION(ptImage, args, keywords) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->setKey(*key); + PYTHON_RETURN_INIT_OK; +} + +PYTHON_RICH_COMPARE_DEFINITION(ptImage, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pyImage::Check(obj1) || !pyImage::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptImage object"); + PYTHON_RCOMPARE_ERROR; + } + } + pyImage *img1 = pyImage::ConvertFrom(obj1); + pyImage *img2 = pyImage::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*img1) == (*img2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*img1) != (*img2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptImage object"); + PYTHON_RCOMPARE_ERROR; +} + +#ifndef BUILDING_PYPLASMA +PYTHON_METHOD_DEFINITION(ptImage, getPixelColor, args) +{ + float x, y; + if (!PyArg_ParseTuple(args, "ff", &x, &y)) + { + PyErr_SetString(PyExc_TypeError, "getPixelColor expects two floats"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetPixelColor(x, y); +} + +PYTHON_METHOD_DEFINITION(ptImage, getColorLoc, args) +{ + PyObject* colorObj = NULL; + if (!PyArg_ParseTuple(args, "O", &colorObj)) + { + PyErr_SetString(PyExc_TypeError, "getColorLoc expects a ptColor"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "getColorLoc expects a ptColor"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + return self->fThis->GetColorLoc(*color); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptImage, getWidth) +{ + return PyLong_FromUnsignedLong(self->fThis->GetWidth()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptImage, getHeight) +{ + return PyLong_FromUnsignedLong(self->fThis->GetHeight()); +} + +PYTHON_METHOD_DEFINITION(ptImage, saveAsJPEG, args) +{ + PyObject* filenameObj; + unsigned char quality = 75; + if (!PyArg_ParseTuple(args, "O|b", &filenameObj, &quality)) + { + PyErr_SetString(PyExc_TypeError, "saveAsJPEG expects a string and a unsigned 8-bit int"); + PYTHON_RETURN_ERROR; + } + + if (PyUnicode_Check(filenameObj)) + { + int strLen = PyUnicode_GetSize(filenameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)filenameObj, text, strLen); + text[strLen] = L'\0'; + self->fThis->SaveAsJPEG(text, quality); + delete [] text; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(filenameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(filenameObj); + wchar_t* wText = hsStringToWString(text); + self->fThis->SaveAsJPEG(wText, quality); + delete [] wText; + PYTHON_RETURN_NONE; + } + else + { + PyErr_SetString(PyExc_TypeError, "saveAsJPEG expects a string and a unsigned 8-bit int"); + PYTHON_RETURN_ERROR; + } +} +#endif // BUILDING_PYPLASMA + +PYTHON_START_METHODS_TABLE(ptImage) +#ifndef BUILDING_PYPLASMA + PYTHON_METHOD(ptImage, getPixelColor, "Params: x,y\nReturns the ptColor at the specified location (float from 0 to 1)"), + PYTHON_METHOD(ptImage, getColorLoc, "Params: color\nReturns the ptPoint3 where the specified color is located"), + PYTHON_METHOD_NOARGS(ptImage, getWidth, "Returns the width of the image"), + PYTHON_METHOD_NOARGS(ptImage, getHeight, "Returns the height of the image"), + PYTHON_METHOD(ptImage, saveAsJPEG, "Params: filename,quality=75\nSaves this image to disk as a JPEG file"), +#endif +PYTHON_END_METHODS_TABLE; + +// Type structure definition +#define ptImage_COMPARE PYTHON_NO_COMPARE +#define ptImage_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptImage_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptImage_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptImage_STR PYTHON_NO_STR +#define ptImage_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptImage) +#define ptImage_GETSET PYTHON_NO_GETSET +#define ptImage_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptImage, "Params: imgKey\nPlasma image class"); + +// required functions for PyObject interoperability +#ifndef BUILDING_PYPLASMA +PyObject *pyImage::New(plMipmap* mipmap) +{ + ptImage *newObj = (ptImage*)ptImage_type.tp_new(&ptImage_type, NULL, NULL); + newObj->fThis->fMipmap = mipmap; + newObj->fThis->fMipMapKey = mipmap->GetKey(); + if (mipmap->GetKey()) + newObj->fThis->fMipMapKey->RefObject(); + return (PyObject*)newObj; +} +#endif + +PyObject *pyImage::New(plKey mipmapKey) +{ + ptImage *newObj = (ptImage*)ptImage_type.tp_new(&ptImage_type, NULL, NULL); + newObj->fThis->fMipMapKey = mipmapKey; + return (PyObject*)newObj; +} + +PyObject *pyImage::New(pyKey& mipmapKey) +{ + ptImage *newObj = (ptImage*)ptImage_type.tp_new(&ptImage_type, NULL, NULL); + newObj->fThis->fMipMapKey = mipmapKey.getKey(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptImage, pyImage) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptImage, pyImage) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyImage::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptImage); + PYTHON_CLASS_IMPORT_END(m); +} + +#ifndef BUILDING_PYPLASMA +PYTHON_GLOBAL_METHOD_DEFINITION(PtLoadJPEGFromDisk, args, "Params: filename,width,height\nThe image will be resized to fit the width and height arguments. Set to 0 if resizing is not desired.\nReturns a pyImage of the specified file.") +{ + PyObject* filenameObj; + unsigned short width, height; + if (!PyArg_ParseTuple(args, "Ohh", &filenameObj, &width, &height)) + { + PyErr_SetString(PyExc_TypeError, "PtLoadJPEGFromDisk expects a string and two unsigned shorts"); + PYTHON_RETURN_ERROR; + } + + if (PyUnicode_Check(filenameObj)) + { + int strLen = PyUnicode_GetSize(filenameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)filenameObj, text, strLen); + text[strLen] = L'\0'; + PyObject* ret = pyImage::LoadJPEGFromDisk(text, width, height); + delete [] text; + return ret; + } + else if (PyString_Check(filenameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(filenameObj); + wchar_t* wText = hsStringToWString(text); + PyObject* ret = pyImage::LoadJPEGFromDisk(wText, width, height); + delete [] wText; + return ret; + } + else + { + PyErr_SetString(PyExc_TypeError, "saveAsJPEG expects a string and a unsigned 8-bit int"); + PYTHON_RETURN_ERROR; + } +} +#endif + +void pyImage::AddPlasmaMethods(std::vector &methods) +{ +#ifndef BUILDING_PYPLASMA + PYTHON_GLOBAL_METHOD(methods, PtLoadJPEGFromDisk); +#endif +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBook.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBook.cpp new file mode 100644 index 00000000..8d0a8bef --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBook.cpp @@ -0,0 +1,277 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////// +// +// pyJournalBook - Python wrapper for the new journal book API +// +/////////////////////////////////////////////// + +#include "pyJournalBook.h" +#include "../pfJournalBook/pfJournalBook.h" + +#include "cyAnimation.h" +#include "pyColor.h" +#include "pyImage.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plUoid.h" + +UInt32 pyJournalBook::fNextKeyID = 0; + +void pyJournalBook::IMakeNewKey( void ) +{ + char name[ 128 ]; + sprintf( name, "pyJournalBook-%d", fNextKeyID++ ); + hsgResMgr::ResMgr()->NewKey( name, fBook, plLocation::kGlobalFixedLoc ); + + fBook->GetKey()->RefObject(); +} + +pyJournalBook::pyJournalBook() +{ + fBook = nil; +} + +pyJournalBook::pyJournalBook( const char *esHTMLSource ) +{ + fBook = TRACKED_NEW pfJournalBook( esHTMLSource ); + IMakeNewKey(); +} + +pyJournalBook::pyJournalBook( std::wstring esHTMLSource ) +{ + fBook = TRACKED_NEW pfJournalBook( esHTMLSource.c_str() ); + IMakeNewKey(); +} + +pyJournalBook::pyJournalBook( const char *esHTMLSource, pyImage &coverImage, pyKey callbackKey ) +{ + fBook = TRACKED_NEW pfJournalBook( esHTMLSource, coverImage.GetKey(), callbackKey.getKey(), + callbackKey.getKey() != nil ? callbackKey.getKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc ); + IMakeNewKey(); +} + +pyJournalBook::pyJournalBook( std::wstring esHTMLSource, pyImage &coverImage, pyKey callbackKey ) +{ + fBook = TRACKED_NEW pfJournalBook( esHTMLSource.c_str(), coverImage.GetKey(), callbackKey.getKey(), + callbackKey.getKey() != nil ? callbackKey.getKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc ); + IMakeNewKey(); +} + +pyJournalBook::pyJournalBook( const char *esHTMLSource, pyImage &coverImage, pyKey callbackKey, const char *guiName ) +{ + fBook = TRACKED_NEW pfJournalBook( esHTMLSource, coverImage.GetKey(), callbackKey.getKey(), + callbackKey.getKey() != nil ? callbackKey.getKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc, guiName ); + IMakeNewKey(); +} + +pyJournalBook::pyJournalBook( std::wstring esHTMLSource, pyImage &coverImage, pyKey callbackKey, const char *guiName ) +{ + fBook = TRACKED_NEW pfJournalBook( esHTMLSource.c_str(), coverImage.GetKey(), callbackKey.getKey(), + callbackKey.getKey() != nil ? callbackKey.getKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc, guiName ); + IMakeNewKey(); +} + +pyJournalBook::pyJournalBook( const char *esHTMLSource, pyKey callbackKey ) +{ + fBook = TRACKED_NEW pfJournalBook( esHTMLSource, nil, callbackKey.getKey(), + callbackKey.getKey() != nil ? callbackKey.getKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc ); + + IMakeNewKey(); +} + +pyJournalBook::pyJournalBook( std::wstring esHTMLSource, pyKey callbackKey ) +{ + fBook = TRACKED_NEW pfJournalBook( esHTMLSource.c_str(), nil, callbackKey.getKey(), + callbackKey.getKey() != nil ? callbackKey.getKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc ); + + IMakeNewKey(); +} + +pyJournalBook::~pyJournalBook() +{ + if( fBook != nil ) + { + fBook->GetKey()->UnRefObject(); + fBook = nil; + } +} + +void pyJournalBook::MakeBook(std::string esHTMLSource, plKey coverImageKey /* = nil */, plKey callbackKey /* = nil */, std::string guiName /* = "" */) +{ + if (fBook) + fBook->GetKey()->UnRefObject(); + + plLocation loc = plLocation::kGlobalFixedLoc; + if (callbackKey != nil) + loc = callbackKey->GetUoid().GetLocation(); + + fBook = TRACKED_NEW pfJournalBook(esHTMLSource.c_str(), coverImageKey, callbackKey, loc, guiName.c_str()); + IMakeNewKey(); +} + +void pyJournalBook::MakeBook(std::wstring esHTMLSource, plKey coverImageKey /* = nil */, plKey callbackKey /* = nil */, std::string guiName /* = "" */) +{ + if (fBook) + fBook->GetKey()->UnRefObject(); + + plLocation loc = plLocation::kGlobalFixedLoc; + if (callbackKey != nil) + loc = callbackKey->GetUoid().GetLocation(); + + fBook = TRACKED_NEW pfJournalBook(esHTMLSource.c_str(), coverImageKey, callbackKey, loc, guiName.c_str()); + IMakeNewKey(); +} + +void pyJournalBook::Show( hsBool startOpened ) +{ + if( fBook != nil ) + fBook->Show( startOpened ); +} + +void pyJournalBook::Hide( void ) +{ + if( fBook != nil ) + fBook->Hide(); +} + +void pyJournalBook::Open( UInt32 startingPage ) +{ + if( fBook != nil ) + fBook->Open( startingPage ); +} + +void pyJournalBook::Close( void ) +{ + if( fBook != nil ) + fBook->Close(); +} + +void pyJournalBook::CloseAndHide( void ) +{ + if( fBook != nil ) + fBook->CloseAndHide(); +} + +void pyJournalBook::NextPage( void ) +{ + if( fBook != nil ) + fBook->NextPage(); +} + +void pyJournalBook::PreviousPage( void ) +{ + if( fBook != nil ) + fBook->PreviousPage(); +} + +void pyJournalBook::GoToPage( UInt32 page ) +{ + if( fBook != nil ) + fBook->GoToPage( page ); +} + +void pyJournalBook::SetSize( hsScalar width, hsScalar height ) +{ + if( fBook != nil ) + fBook->SetBookSize( width, height ); +} + +UInt32 pyJournalBook::GetCurrentPage( void ) const +{ + if( fBook != nil ) + return fBook->GetCurrentPage(); + + return 0; +} + +void pyJournalBook::SetPageMargin( UInt32 margin ) +{ + if( fBook != nil ) + fBook->SetPageMargin( margin ); +} + +void pyJournalBook::AllowPageTurning( bool allow ) +{ + if( fBook != nil ) + fBook->AllowPageTurning(allow); +} + +void pyJournalBook::SetGUI( const char *guiName ) +{ + if (fBook != nil) + fBook->SetGUI(guiName); +} + +void pyJournalBook::LoadGUI( const char *guiName ) +{ + pfJournalBook::LoadGUI(guiName); +} + +void pyJournalBook::UnloadGUI( const char *guiName ) +{ + pfJournalBook::UnloadGUI(guiName); +} + +void pyJournalBook::UnloadAllGUIs() +{ + pfJournalBook::UnloadAllGUIs(); +} + +PyObject *pyJournalBook::GetMovie(UInt8 index) +{ + if (fBook != nil) + { + plKey movie = fBook->GetMovie(index); + if (movie == plKey(nil)) + PYTHON_RETURN_NONE; + PyObject* key = pyKey::New(movie); + PyObject* animObj = cyAnimation::New(); + cyAnimation* anim = cyAnimation::ConvertFrom(animObj); // points to internal object + anim->AddRecvr(*(pyKey::ConvertFrom(key))); + Py_DECREF(key); + return animObj; + } + PYTHON_RETURN_NONE; +} + +void pyJournalBook::SetEditable( hsBool editable ) +{ + if (fBook != nil) + fBook->SetEditable(editable); +} + +std::string pyJournalBook::GetEditableText( void ) const +{ + if (fBook != nil) + return fBook->GetEditableText(); + return ""; +} + +void pyJournalBook::SetEditableText( std::string text ) +{ + if (fBook != nil) + fBook->SetEditableText(text); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBook.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBook.h new file mode 100644 index 00000000..b4e437b3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBook.h @@ -0,0 +1,117 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyJournalBook_h_ +#define _pyJournalBook_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyJournalBook - Python wrapper for the new journal book API +// +////////////////////////////////////////////////////////////////////// + +#include "hsStlUtils.h" +#include "pyKey.h" +#include "pyGeometry3.h" + + +#include +#include "pyGlueHelpers.h" + +class pyImage; +class pyColor; +class cyAnimation; +class pfJournalBook; + +class pyJournalBook +{ +protected: + + pfJournalBook *fBook; + + static UInt32 fNextKeyID; + + void IMakeNewKey( void ); + + pyJournalBook(); // used by python glue only, do NOT call + pyJournalBook( const char *esHTMLSource ); + pyJournalBook( std::wstring esHTMLSource ); + pyJournalBook( const char *esHTMLSource, pyKey callbackKey ); + pyJournalBook( std::wstring esHTMLSource, pyKey callbackKey ); + pyJournalBook( const char *esHTMLSource, pyImage &coverImage, pyKey callbackKey ); + pyJournalBook( std::wstring esHTMLSource, pyImage &coverImage, pyKey callbackKey ); + pyJournalBook( const char *esHTMLSource, pyImage &coverImage, pyKey callbackKey, const char *guiName ); + pyJournalBook( std::wstring esHTMLSource, pyImage &coverImage, pyKey callbackKey, const char *guiName ); + +public: + virtual ~pyJournalBook(); + + // No copy constructor; don't allow copying + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptBook); + static PyObject *New(std::string htmlSource, plKey coverImageKey = nil, plKey callbackKey = nil, std::string guiName = ""); + static PyObject *New(std::wstring htmlSource, plKey coverImageKey = nil, plKey callbackKey = nil, std::string guiName = ""); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyJournalBook object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyJournalBook); // converts a PyObject to a pyJournalBook (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaMethods(std::vector &methods); + static void AddPlasmaConstantsClasses(PyObject *m); + + // Deletes the existing book and re-creates it, for use by the python glue + void MakeBook(std::string esHTMLSource, plKey coverImageKey = nil, plKey callbackKey = nil, std::string guiName = ""); + void MakeBook(std::wstring esHTMLSource, plKey coverImageKey = nil, plKey callbackKey = nil, std::string guiName = ""); + + // Interface functions per book + virtual void Show( hsBool startOpened ); + virtual void Hide( void ); + virtual void Open( UInt32 startingPage ); + virtual void Close( void ); + virtual void CloseAndHide( void ); + + virtual void NextPage( void ); + virtual void PreviousPage( void ); + virtual void GoToPage( UInt32 page ); + virtual UInt32 GetCurrentPage( void ) const; + virtual void SetPageMargin( UInt32 margin ); + virtual void AllowPageTurning( bool allow ); + + virtual void SetSize( hsScalar width, hsScalar height ); + + virtual void SetGUI( const char *guiName ); + + static void LoadGUI( const char *guiName ); + static void UnloadGUI( const char *guiName ); + static void UnloadAllGUIs(); + + virtual PyObject *GetMovie( UInt8 index ); // returns cyAnimation + + virtual void SetEditable( hsBool editable ); + virtual std::string GetEditableText( void ) const; + virtual void SetEditableText( std::string text ); +}; + +#endif // _pyJournalBook_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBookGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBookGlue.cpp new file mode 100644 index 00000000..328df3f9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyJournalBookGlue.cpp @@ -0,0 +1,352 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyJournalBook.h" +#include "pyEnum.h" +#include "pyKey.h" +#include "pyImage.h" + +#include "../pfJournalBook/pfJournalBook.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptBook, pyJournalBook); + +PYTHON_DEFAULT_NEW_DEFINITION(ptBook, pyJournalBook) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptBook) + +PYTHON_INIT_DEFINITION(ptBook, args, keywords) +{ + char* kwlist[] = {"esHTMLSource", "coverImage", "callbackKey", "guiName", NULL}; + PyObject* sourceObj = NULL; + PyObject* coverObj = NULL; + PyObject* callbackObj = NULL; + char* guiName = NULL; + if (!PyArg_ParseTupleAndKeywords(args, keywords, "O|OOs", kwlist, &sourceObj, &coverObj, &callbackObj, &guiName)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a string or unicode string, and optionally a ptImage, ptKey, and string"); + PYTHON_RETURN_INIT_ERROR; + } + + // convert all the optional arguments + plKey coverKey = nil; + if (coverObj) + { + if (pyKey::Check(coverObj)) + { + // this is really the callback key + if (callbackObj) // callbackObj was already defined, can't have two keys + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a string or unicode string, and optionally a ptImage, ptKey, and string"); + PYTHON_RETURN_INIT_ERROR; + } + callbackObj = coverObj; + coverObj = nil; + } + else if (!pyImage::Check(coverObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a string or unicode string, and optionally a ptImage, ptKey, and string"); + PYTHON_RETURN_INIT_ERROR; + } + else + coverKey = pyImage::ConvertFrom(coverObj)->GetKey(); + } + + plKey callbackKey = nil; + if (callbackObj) + { + if (!pyKey::Check(callbackObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a string or unicode string, and optionally a ptImage, ptKey, and string"); + PYTHON_RETURN_INIT_ERROR; + } + callbackKey = pyKey::ConvertFrom(callbackObj)->getKey(); + } + + std::string guiNameStr = ""; + if (guiName) + guiNameStr = guiName; + + // convert the sourcecode object + if (PyUnicode_Check(sourceObj)) + { + int len = PyUnicode_GetSize(sourceObj); + wchar_t* temp = TRACKED_NEW wchar_t[len + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)sourceObj, temp, len); + temp[len] = L'\0'; + + std::wstring source = temp; + delete [] temp; + + self->fThis->MakeBook(source, coverKey, callbackKey, guiNameStr); + PYTHON_RETURN_INIT_OK; + } + else if (PyString_Check(sourceObj)) + { + std::string source = PyString_AsString(sourceObj); + + self->fThis->MakeBook(source, coverKey, callbackKey, guiNameStr); + PYTHON_RETURN_INIT_OK; + } + + // source wasn't a string or unicode string + PyErr_SetString(PyExc_TypeError, "__init__ expects a string or unicode string, and optionally a ptImage, ptKey, and string"); + PYTHON_RETURN_INIT_ERROR; +} + +PYTHON_METHOD_DEFINITION(ptBook, show, args) +{ + char startOpened; + if (!PyArg_ParseTuple(args, "b", &startOpened)) + { + PyErr_SetString(PyExc_TypeError, "show expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->Show(startOpened != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptBook, hide, Hide) + +PYTHON_METHOD_DEFINITION(ptBook, open, args) +{ + unsigned long startingPage; + if (!PyArg_ParseTuple(args, "l", &startingPage)) + { + PyErr_SetString(PyExc_TypeError, "open expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->Open(startingPage); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptBook, close, Close) +PYTHON_BASIC_METHOD_DEFINITION(ptBook, closeAndHide, CloseAndHide) + +PYTHON_BASIC_METHOD_DEFINITION(ptBook, nextPage, NextPage) +PYTHON_BASIC_METHOD_DEFINITION(ptBook, previousPage, PreviousPage) + +PYTHON_METHOD_DEFINITION(ptBook, goToPage, args) +{ + unsigned long page; + if (!PyArg_ParseTuple(args, "l", &page)) + { + PyErr_SetString(PyExc_TypeError, "goToPage expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->GoToPage(page); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptBook, setSize, args) +{ + float width, height; + if (!PyArg_ParseTuple(args, "ff", &width, &height)) + { + PyErr_SetString(PyExc_TypeError, "setSize expects two floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSize(width, height); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptBook, getCurrentPage) +{ + return PyLong_FromUnsignedLong(self->fThis->GetCurrentPage()); +} + +PYTHON_METHOD_DEFINITION(ptBook, allowPageTurning, args) +{ + char allow; + if (!PyArg_ParseTuple(args, "b", &allow)) + { + PyErr_SetString(PyExc_TypeError, "allowPageTurning expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->AllowPageTurning(allow != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptBook, setPageMargin, args) +{ + unsigned long margin; + if (!PyArg_ParseTuple(args, "l", &margin)) + { + PyErr_SetString(PyExc_TypeError, "setPageMargin expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetPageMargin(margin); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptBook, setGUI, args) +{ + char* guiName; + if (!PyArg_ParseTuple(args, "s", &guiName)) + { + PyErr_SetString(PyExc_TypeError, "setGUI expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetGUI(guiName); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptBook, getMovie, args) +{ + unsigned char index; + if (!PyArg_ParseTuple(args, "b", &index)) + { + PyErr_SetString(PyExc_TypeError, "getMovie expects a unsigned 8-bit int"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetMovie(index); +} + +PYTHON_METHOD_DEFINITION(ptBook, setEditable, args) +{ + char editable; + if (!PyArg_ParseTuple(args, "b", &editable)) + { + PyErr_SetString(PyExc_TypeError, "setEditable expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetEditable(editable != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptBook, getEditableText, args) +{ + return PyString_FromString(self->fThis->GetEditableText().c_str()); +} + +PYTHON_METHOD_DEFINITION(ptBook, setEditableText, args) +{ + char* text; + if (!PyArg_ParseTuple(args, "s", &text)) + { + PyErr_SetString(PyExc_TypeError, "setEditableText expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetEditableText(text); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptBook) + PYTHON_METHOD(ptBook, show, "Params: startOpened\nShows the book closed, or open if the the startOpened flag is true"), + PYTHON_BASIC_METHOD(ptBook, hide, "Hides the book"), + PYTHON_METHOD(ptBook, open, "Params: startingPage\nOpens the book to the specified page"), + PYTHON_BASIC_METHOD(ptBook, close, "Closes the book"), + PYTHON_BASIC_METHOD(ptBook, closeAndHide, "Closes the book and hides it once it finishes animating"), + PYTHON_BASIC_METHOD(ptBook, nextPage, "Flips the book to the next page"), + PYTHON_BASIC_METHOD(ptBook, previousPage, "Flips the book to the previous page"), + PYTHON_METHOD(ptBook, goToPage, "Params: page\nFlips the book to the specified page"), + PYTHON_METHOD(ptBook, setSize, "Params: width,height\nSets the size of the book (width and height are floats from 0 to 1)"), + PYTHON_METHOD_NOARGS(ptBook, getCurrentPage, "Returns the currently shown page"), + PYTHON_METHOD(ptBook, allowPageTurning, "Params: allow\nTurns on and off the ability to flip the pages in a book"), + PYTHON_METHOD(ptBook, setPageMargin, "Params: margin\nSets the text margin for the book"), + PYTHON_METHOD(ptBook, setGUI, "Params: guiName\nSets the gui to be used by the book, if the requested gui is not loaded, it will use the default\nDo not call while the book is open!"), + PYTHON_METHOD(ptBook, getMovie, "Params: index\nGrabs a ptAnimation object representing the movie indexed by index. The index is the index of the movie in the source code"), + PYTHON_METHOD(ptBook, setEditable, "Params: editable\nTurn book editing on or off. If the book GUI does not support editing, nothing will happen"), + PYTHON_METHOD(ptBook, getEditableText, "Returns the editable text currently contained in the book."), + PYTHON_METHOD(ptBook, setEditableText, "Params: text\nSets the book's editable text."), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptBook, "Params: esHTMLSource,coverImage=None,callbackKey=None,guiName=''\nCreates a new book"); + +// required functions for PyObject interoperability +PyObject *pyJournalBook::New(std::string htmlSource, plKey coverImageKey /* = nil */, plKey callbackKey /* = nil */, std::string guiName /* = "" */) +{ + ptBook *newObj = (ptBook*)ptBook_type.tp_new(&ptBook_type, NULL, NULL); + newObj->fThis->MakeBook(htmlSource, coverImageKey, callbackKey, guiName); + return (PyObject*)newObj; +} + +PyObject *pyJournalBook::New(std::wstring htmlSource, plKey coverImageKey /* = nil */, plKey callbackKey /* = nil */, std::string guiName /* = "" */) +{ + ptBook *newObj = (ptBook*)ptBook_type.tp_new(&ptBook_type, NULL, NULL); + newObj->fThis->MakeBook(htmlSource, coverImageKey, callbackKey, guiName); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptBook, pyJournalBook) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptBook, pyJournalBook) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyJournalBook::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptBook); + PYTHON_CLASS_IMPORT_END(m); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtLoadBookGUI, args, "Params: guiName\nLoads the gui specified, a gui must be loaded before it can be used. If the gui is already loaded, doesn't do anything") +{ + char* guiName; + if (!PyArg_ParseTuple(args, "s", &guiName)) + { + PyErr_SetString(PyExc_TypeError, "PtLoadBookGUI expects a string"); + PYTHON_RETURN_ERROR; + } + pyJournalBook::LoadGUI(guiName); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(PtUnloadBookGUI, args, "Params: guiName\nUnloads the gui specified. If the gui isn't loaded, doesn't do anything") +{ + char* guiName; + if (!PyArg_ParseTuple(args, "s", &guiName)) + { + PyErr_SetString(PyExc_TypeError, "PtUnloadBookGUI expects a string"); + PYTHON_RETURN_ERROR; + } + pyJournalBook::UnloadGUI(guiName); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtUnloadAllBookGUIs, pyJournalBook::UnloadAllGUIs, "Unloads all loaded guis except for the default one") + +void pyJournalBook::AddPlasmaMethods(std::vector &methods) +{ + PYTHON_GLOBAL_METHOD(methods, PtLoadBookGUI); + PYTHON_GLOBAL_METHOD(methods, PtUnloadBookGUI); + PYTHON_BASIC_GLOBAL_METHOD(methods, PtUnloadAllBookGUIs); +} + +void pyJournalBook::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtBookEventTypes); + PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyImageLink, pfJournalBook::kNotifyImageLink); + PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyShow, pfJournalBook::kNotifyShow); + PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyHide, pfJournalBook::kNotifyHide); + PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyNextPage, pfJournalBook::kNotifyNextPage); + PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyPreviousPage, pfJournalBook::kNotifyPreviousPage); + PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyCheckUnchecked, pfJournalBook::kNotifyCheckUnchecked); + PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyClose, pfJournalBook::kNotifyClose); + PYTHON_ENUM_END(m, PtBookEventTypes); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKey.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKey.cpp new file mode 100644 index 00000000..d03aa950 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKey.cpp @@ -0,0 +1,231 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyKey - the wrapper class around a plKey so that Python can handle it +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" +#include "../pnKeyedObject/plKey.h" +#include "plgDispatch.h" +#include "plPythonFileMod.h" +#include "../pnMessage/plEnableMsg.h" +#include "hsResMgr.h" +#include "pySceneObject.h" +#include "../pnSceneObject/plSceneObject.h" + +pyKey::pyKey() +{ + fKey=nil; +#ifndef BUILDING_PYPLASMA + fPyFileMod=nil; + fNetForce=false; +#endif +} + +pyKey::pyKey(plKey key) +{ + fKey = key; +#ifndef BUILDING_PYPLASMA + fPyFileMod=nil; + fNetForce=false; +#endif +} + +#ifndef BUILDING_PYPLASMA +pyKey::pyKey(plKey key, plPythonFileMod* pymod) +{ + fKey = key; + fPyFileMod=pymod; + fNetForce=false; +} + +void pyKey::SetNetForce(hsBool state) +{ + // set our flag + fNetForce = state; +} + +// send enable message to the plKey +void pyKey::Enable() +{ + IEnable(true); +} + + +// send disable message to the plKey +void pyKey::Disable() +{ + IEnable(false); +} +#endif + +hsBool pyKey::operator==(const pyKey &key) const +{ + plKey ours = ((pyKey*)this)->getKey(); + plKey theirs = ((pyKey&)key).getKey(); + if ( ours == nil && theirs == nil ) + return true; + else if ( ours != nil && theirs != nil ) + return (ours->GetUoid()==theirs->GetUoid()); + else + return false; +} + +#ifndef BUILDING_PYPLASMA +PyObject* pyKey::GetPySceneObject() +{ + plKey theKey = getKey(); + //if a modifier return the scene objects key + plModifier* mod = plModifier::ConvertNoRef(theKey->ObjectIsLoaded()); + if (mod) + { + if(mod->GetNumTargets()>0) + { + return pySceneObject::New(mod->GetTarget(0)->GetKey()); + } + else return nil; + } + // create pySceneObject that will be managed by Python + return pySceneObject::New(getKey()); +} + +// send disable message to the plKey +void pyKey::IEnable(hsBool state) +{ + // create message + plEnableMsg* pMsg = TRACKED_NEW plEnableMsg; + if (fNetForce ) + { + // set the network propagate flag to make sure it gets to the other clients + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + } + pMsg->AddReceiver(fKey); + // which way are we doin' it? + if ( state ) + pMsg->SetCmd(plEnableMsg::kEnable); + else + pMsg->SetCmd(plEnableMsg::kDisable); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes +} + + +// if this is a modifier then get the (first) object its attached to +PyObject* pyKey::GetParentObject() +{ + if (fKey) + { + // see if this a modifier that is loaded + plModifier* mod = plModifier::ConvertNoRef(fKey->ObjectIsLoaded()); + if (mod) + { + if ( mod->GetNumTargets() > 0 ) + return pyKey::New(mod->GetTarget(0)->GetKey()); + } + } + return nil; +} + + +// special functions when the plKey is a pointer to the PythonModifier +// +// Was the last plNotifyMsg a locally sent? +hsBool pyKey::WasLocalNotify() +{ + // see if we have a PythonFileModifier pointer + if ( fPyFileMod ) + return fPyFileMod->WasLocalNotify(); + // otherwise... just say it is local + return true; +} + +// Is python file mod attached to clone +hsBool pyKey::IsAttachedToClone() +{ + // see if we have a PythonFileModifier pointer + if ( fPyFileMod ) + return fPyFileMod->AmIAttachedToClone(); + // otherwise return nope + return false; +} + +plPipeline* pyKey::GetPipeline() +{ + if ( fPyFileMod ) + return fPyFileMod->GetPipeline(); + return nil; +} + +// get the notify list count +Int32 pyKey::NotifyListCount() +{ + // see if we have a PythonFileModifier pointer + if ( fPyFileMod ) + return fPyFileMod->NotifyListCount(); + // otherwise... just say notify list receivers + return 0; +} + +// get a notify list item +plKey pyKey::GetNotifyListItem(Int32 i) +{ + // see if we have a PythonFileModifier pointer + if ( fPyFileMod ) + return fPyFileMod->GetNotifyListItem(i); + // otherwise... just say it is local + return nil; +} + + +// Set the dirty state on the PythonModifier +void pyKey::DirtySynchState(const char* SDLStateName, UInt32 sendFlags) +{ + // see if we have a PythonFileModifier pointer + if ( fPyFileMod ) + fPyFileMod->DirtySynchState(SDLStateName, sendFlags); +} + + +// register for control key events with the pythonfile modifier +void pyKey::EnableControlKeyEvents() +{ + // see if we have a PythonFileModifier pointer + if ( fPyFileMod ) + // if so, then pass on the command request + fPyFileMod->EnableControlKeyEvents(); +} + +// unregister for control key events with the pythonfile modifier +void pyKey::DisableControlKeyEvents() +{ + // see if we have a PythonFileModifier pointer + if ( fPyFileMod ) + // if so, then pass on the command request + fPyFileMod->DisableControlKeyEvents(); +} +#endif // BUILDING_PYPLASMA \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKey.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKey.h new file mode 100644 index 00000000..97e57fca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKey.h @@ -0,0 +1,126 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyKey_h_ +#define _pyKey_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyKey - the wrapper class around a plKey so that Python can handle it +// +////////////////////////////////////////////////////////////////////// + +#include "../pnKeyedObject/plKey.h" + +#include +#include "pyGlueHelpers.h" + +class plPythonFileMod; +class pySceneObject; +class plPipeline; + +//#include "plPythonHelpers.h" + +class pyKey +{ +private: + plKey fKey; // the plKey that we are holding onto +#ifndef BUILDING_PYPLASMA // pyPlasma (and other plugins) don't need all this extra junk) + plPythonFileMod* fPyFileMod; // pointer to the PythonFileModifier + + hsBool fNetForce; +#endif // BUILDING_PYPLASMA + +protected: + pyKey(); + pyKey(plKey key); +#ifndef BUILDING_PYPLASMA + pyKey(plKey key, plPythonFileMod* pymod); +#endif + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptKey); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(plKey key); + static PyObject *New(pyKey *key); +#ifndef BUILDING_PYPLASMA + static PyObject *New(plKey key, plPythonFileMod *pyMod); + static PyObject *New(pyKey *key, plPythonFileMod *pyMod); +#endif + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyKey object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyKey); // converts a PyObject to a pyKey (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // override the equals to operator + hsBool operator==(const pyKey &key) const; + hsBool operator!=(const pyKey &key) const { return !(key == *this); } + + // getter and setters + virtual plKey getKey() { return fKey; } + virtual void setKey(plKey key) { fKey=key; } + virtual const char* getName() const { return fKey ? fKey->GetName() : "nil"; } +#ifndef BUILDING_PYPLASMA + PyObject* GetPySceneObject(); + + virtual void SetNetForce(hsBool state); + + // methods to be sent to the plKey + // send enable message to the plKey + virtual void Enable(); + // send disable message to the plKey + virtual void Disable(); + // if this is a modifier then get the (first) object its attached to + virtual PyObject* GetParentObject(); + + // special functions when the plKey is a pointer to the PythonModifier + // (Only called from C++, not from Python directly) + // + // Was the last plNotifyMsg a locally sent? + virtual hsBool WasLocalNotify(); + // Is this python file mod attached to a clone? + virtual hsBool IsAttachedToClone(); + // (old style - Used in pyNotify) + // get the notify list count + virtual Int32 NotifyListCount(); + // (old style - Used in pyNotify) + // get a notify list item + virtual plKey GetNotifyListItem(Int32 i); + // Set the dirty state on the PythonModifier + virtual void DirtySynchState(const char* SDLStateName, UInt32 sendFlags); + + // register and unregister for control key envents + virtual void EnableControlKeyEvents(); + virtual void DisableControlKeyEvents(); + + virtual plPipeline* GetPipeline(); +private: + // build and send enable message to the plKey + virtual void IEnable(hsBool state); +#endif // BUILDING_PYPLASMA +}; + +#endif // _pyKey_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyGlue.cpp new file mode 100644 index 00000000..99615366 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyGlue.cpp @@ -0,0 +1,198 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyKey.h" +#ifndef BUILDING_PYPLASMA +#include "pySceneObject.h" +#endif + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptKey, pyKey); + +PYTHON_DEFAULT_NEW_DEFINITION(ptKey, pyKey) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptKey) + +PYTHON_NO_INIT_DEFINITION(ptKey) + +PYTHON_RICH_COMPARE_DEFINITION(ptKey, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pyKey::Check(obj1) || !pyKey::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptKey object"); + PYTHON_RCOMPARE_ERROR; + } + } + pyKey *key1 = pyKey::ConvertFrom(obj1); + pyKey *key2 = pyKey::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*key1) == (*key2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*key1) != (*key2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptKey object"); + PYTHON_RCOMPARE_ERROR; +} + +#ifndef BUILDING_PYPLASMA +PYTHON_METHOD_DEFINITION(ptKey, netForce, args) +{ + char forceFlag; + if (!PyArg_ParseTuple(args, "b", &forceFlag)) + { + PyErr_SetString(PyExc_TypeError, "netForce requires a boolean argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNetForce(forceFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptKey, getName) +{ + return PyString_FromString(self->fThis->getName()); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptKey, enable, Enable) +PYTHON_BASIC_METHOD_DEFINITION(ptKey, disable, Disable) + +PYTHON_METHOD_DEFINITION_NOARGS(ptKey, getSceneObject) +{ + return self->fThis->GetPySceneObject(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptKey, getParentKey) +{ + PyObject* retVal = self->fThis->GetParentObject(); + if (!retVal) + PYTHON_RETURN_NONE; + return retVal; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptKey, isAttachedToClone) +{ + PYTHON_RETURN_BOOL(self->fThis->IsAttachedToClone()); +} + +#endif // BUILDING_PYPLASMA + +PYTHON_START_METHODS_TABLE(ptKey) +#ifndef BUILDING_PYPLASMA + PYTHON_METHOD(ptKey, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n" + "- This is to be used if your Python program is running on only one client\n" + "Such as a game master, only running on the client that owns a particular object"), + PYTHON_METHOD_NOARGS(ptKey, getName, "Get the name of the object that this ptKey is pointing to"), + PYTHON_BASIC_METHOD(ptKey, enable, "Sends an enable message to whatever this ptKey is pointing to"), + PYTHON_BASIC_METHOD(ptKey, disable, "Sends a disable message to whatever this ptKey is pointing to"), + PYTHON_METHOD_NOARGS(ptKey, getSceneObject, "This will return a ptSceneobject object that is associated with this ptKey\n" + "However, if this ptKey is _not_ a sceneobject, then unpredicatable results will ensue"), + PYTHON_METHOD_NOARGS(ptKey, getParentKey, "This will return a ptKey object that is the parent of this modifer\n" + "However, if the parent is not a modifier or not loaded, then None is returned."), + PYTHON_METHOD_NOARGS(ptKey, isAttachedToClone, "Returns whether the python file mod is attached to a clone"), +#endif // BUILDING_PYPLASMA +PYTHON_END_METHODS_TABLE; + +// type structure definition +#define ptKey_COMPARE PYTHON_NO_COMPARE +#define ptKey_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptKey_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptKey_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptKey_STR PYTHON_NO_STR +#define ptKey_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptKey) +#define ptKey_GETSET PYTHON_NO_GETSET +#define ptKey_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptKey, "Plasma Key class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptKey, pyKey) + +PyObject *pyKey::New(plKey key) +{ + ptKey *newObj = (ptKey*)ptKey_type.tp_new(&ptKey_type, NULL, NULL); + newObj->fThis->fKey = key; +#ifndef BUILDING_PYPLASMA + newObj->fThis->fPyFileMod = nil; + newObj->fThis->fNetForce = false; +#endif + return (PyObject*)newObj; +} + +PyObject *pyKey::New(pyKey *key) +{ + ptKey *newObj = (ptKey*)ptKey_type.tp_new(&ptKey_type, NULL, NULL); + newObj->fThis->fKey = key->getKey(); +#ifndef BUILDING_PYPLASMA + newObj->fThis->fPyFileMod = nil; + newObj->fThis->fNetForce = false; +#endif + return (PyObject*)newObj; +} +#ifndef BUILDING_PYPLASMA +PyObject *pyKey::New(plKey key, plPythonFileMod* pymod) +{ + ptKey *newObj = (ptKey*)ptKey_type.tp_new(&ptKey_type, NULL, NULL); + newObj->fThis->fKey = key; + newObj->fThis->fPyFileMod = pymod; + newObj->fThis->fNetForce = false; + return (PyObject*)newObj; +} + +PyObject *pyKey::New(pyKey *key, plPythonFileMod* pymod) +{ + ptKey *newObj = (ptKey*)ptKey_type.tp_new(&ptKey_type, NULL, NULL); + newObj->fThis->fKey = key->getKey(); + newObj->fThis->fPyFileMod = pymod; + newObj->fThis->fNetForce = false; + return (PyObject*)newObj; +} +#endif // BUILDING_PYPLASMA + +PYTHON_CLASS_CHECK_IMPL(ptKey, pyKey) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptKey, pyKey) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyKey::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptKey); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMap.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMap.cpp new file mode 100644 index 00000000..bc05188e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMap.cpp @@ -0,0 +1,268 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyKeyMap - a wrapper class all the key mapping functions +// +////////////////////////////////////////////////////////////////////// + +#include "pyKeyMap.h" + + +#include "../plInputCore/plInputInterfaceMgr.h" +#include "../pnInputCore/plKeyMap.h" + +// conversion functions +const char* pyKeyMap::ConvertVKeyToChar( UInt32 vk, UInt32 flags ) +{ + char *key = plKeyMap::ConvertVKeyToChar( vk ); + static char shortKey[ 2 ]; + if( key == nil ) + { + if( isalnum( vk ) ) + { + shortKey[ 0 ] = (char)vk; + shortKey[ 1 ] = 0; + key = shortKey; + } + else + return "(unmapped)"; + } + + static char newKey[ 16 ]; + strcpy( newKey, key ); + if( flags & kShift ) + strcat( newKey, "_S" ); + if( flags & kCtrl ) + strcat( newKey, "_C" ); + + return newKey; +} + +UInt32 pyKeyMap::ConvertCharToVKey( const char* charVKey ) +{ + char str[ 16 ]; + int i; + + if( strcmp( charVKey, "(unmapped" ) == 0 ) + return KEY_UNMAPPED; + + strcpy( str, charVKey ); + + // Get rid of modififers + for( i = 0; str[ i ] != 0 && str[ i ] != '_'; i++ ); + str[ i ] = 0; + + // Convert raw key and return + return plKeyMap::ConvertCharToVKey( str ); +} + +UInt32 pyKeyMap::ConvertCharToFlags( const char *charVKey ) +{ + char str[ 16 ]; + strcpy( str, charVKey ); + + // Find modifiers to set flags with + UInt32 keyFlags = 0; + if( strstr( str, "_S" ) || strstr( str, "_s" ) ) + keyFlags |= plKeyCombo::kShift; + if( strstr( str, "_C" ) || strstr( str, "_c" ) ) + keyFlags |= plKeyCombo::kCtrl; + + return keyFlags; +} + + +UInt32 pyKeyMap::ConvertCharToControlCode(const char* charCode) +{ + ControlEventCode code = plInputMap::ConvertCharToControlCode(charCode); + return (UInt32)code; +} + +const char* pyKeyMap::ConvertControlCodeToString( UInt32 code ) +{ + return plInputMap::ConvertControlCodeToString((ControlEventCode)code); +} + + +// bind a key to an action +void pyKeyMap::BindKey( const char* keyStr1, const char* keyStr2, const char* act) +{ + + ControlEventCode code = plKeyMap::ConvertCharToControlCode( act ); + if( code == END_CONTROLS ) + { + // error.... traceback? + return; + } + + if( plInputInterfaceMgr::GetInstance() != nil ) + { + plKeyCombo key1 = IBindKeyToVKey( keyStr1 ); + if (keyStr2) + { + plKeyCombo key2 = IBindKeyToVKey( keyStr2 ); + plInputInterfaceMgr::GetInstance()->BindAction( key1, key2, code ); + } + else + plInputInterfaceMgr::GetInstance()->BindAction( key1, code ); + } +} + +// bind a key to an action +void pyKeyMap::BindKeyToConsoleCommand( const char* keyStr1, const char* command) +{ + + if( command && plInputInterfaceMgr::GetInstance() != nil ) + { + plKeyCombo key1; + if (keyStr1) + key1 = IBindKeyToVKey( keyStr1 ); + else + key1 = IBindKeyToVKey( "(unmapped)" ); + plInputInterfaceMgr::GetInstance()->BindConsoleCmd( key1, command, plKeyMap::kFirstAlways); + } +} + +plKeyCombo pyKeyMap::IBindKeyToVKey( const char *keyStr ) +{ + char str[ 16 ]; + int i; + + plKeyCombo combo; + + + strcpy( str, keyStr ); + + // Find modifiers to set flags with + combo.fFlags = 0; + if( strstr( str, "_S" ) || strstr( str, "_s" ) ) + combo.fFlags |= plKeyCombo::kShift; + if( strstr( str, "_C" ) || strstr( str, "_c" ) ) + combo.fFlags |= plKeyCombo::kCtrl; + + // Get rid of modififers + for( i = 0; str[ i ] != 0 && str[ i ] != '_'; i++ ); + str[ i ] = 0; + + // Convert raw key + combo.fKey = plKeyMap::ConvertCharToVKey( str ); + if( combo.fKey == KEY_UNMAPPED ) + combo = plKeyCombo::kUnmapped; + + // And return! + return combo; +} + +UInt32 pyKeyMap::GetBindingKey1(UInt32 code) +{ + if( plInputInterfaceMgr::GetInstance() != nil ) + { + const plKeyBinding* keymap = plInputInterfaceMgr::GetInstance()->FindBinding((ControlEventCode)code); + if ( keymap ) + { + plKeyCombo key = keymap->GetKey1(); + return (UInt32)(key.fKey); + } + } + return 0; +} + +UInt32 pyKeyMap::GetBindingFlags1(UInt32 code) +{ + if( plInputInterfaceMgr::GetInstance() != nil ) + { + const plKeyBinding* keymap = plInputInterfaceMgr::GetInstance()->FindBinding((ControlEventCode)code); + if ( keymap ) + { + plKeyCombo key = keymap->GetKey1(); + return (UInt32)(key.fFlags); + } + } + return 0; +} + +UInt32 pyKeyMap::GetBindingKey2(UInt32 code) +{ + if( plInputInterfaceMgr::GetInstance() != nil ) + { + const plKeyBinding* keymap = plInputInterfaceMgr::GetInstance()->FindBinding((ControlEventCode)code); + if ( keymap ) + { + plKeyCombo key = keymap->GetKey2(); + return (UInt32)(key.fKey); + } + } + return 0; +} + +UInt32 pyKeyMap::GetBindingFlags2(UInt32 code) +{ + if( plInputInterfaceMgr::GetInstance() != nil ) + { + const plKeyBinding* keymap = plInputInterfaceMgr::GetInstance()->FindBinding((ControlEventCode)code); + if ( keymap ) + { + plKeyCombo key = keymap->GetKey2(); + return (UInt32)(key.fFlags); + } + } + return 0; +} + +UInt32 pyKeyMap::GetBindingKeyConsole(const char* command) +{ + if( plInputInterfaceMgr::GetInstance() != nil ) + { + const plKeyBinding* keymap = plInputInterfaceMgr::GetInstance()->FindBindingByConsoleCmd(command); + if ( keymap ) + { + plKeyCombo key = keymap->GetKey1(); + return (UInt32)(key.fKey); + } + } + return 0; +} + +UInt32 pyKeyMap::GetBindingFlagsConsole(const char* command) +{ + if( plInputInterfaceMgr::GetInstance() != nil ) + { + const plKeyBinding* keymap = plInputInterfaceMgr::GetInstance()->FindBindingByConsoleCmd(command); + if ( keymap ) + { + plKeyCombo key = keymap->GetKey1(); + return (UInt32)(key.fFlags); + } + } + return 0; +} + +void pyKeyMap::WriteKeyMap() +{ + if( plInputInterfaceMgr::GetInstance() != nil ) + plInputInterfaceMgr::GetInstance()->WriteKeyMap(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMap.h new file mode 100644 index 00000000..09dc1ea6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMap.h @@ -0,0 +1,90 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyKeyMap_h_ +#define _pyKeyMap_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyKeyMap - a wrapper class all the Key mapping functions +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "../pnInputCore/plKeyMap.h" + +#include +#include "pyGlueHelpers.h" + +class pyKeyMap +{ +protected: + pyKeyMap() {}; + +private: + plKeyCombo IBindKeyToVKey( const char *keyStr ); + + +public: + enum + { + kShift = 0x01, + kCtrl = 0x02 + }; + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptKeyMap); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyKeyMap object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyKeyMap); // converts a PyObject to a pyKeyMap (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // conversion functions + const char* ConvertVKeyToChar( UInt32 vk, UInt32 flags ); + UInt32 ConvertCharToVKey( const char *charVKey ); + UInt32 ConvertCharToFlags( const char *charVKey ); + + UInt32 ConvertCharToControlCode(const char* charCode); + const char* ConvertControlCodeToString( UInt32 code ); + + + // bind a key to an action + void BindKey( const char* keyStr1, const char* keyStr2, const char* act); + void BindKeyToConsoleCommand( const char* keyStr1, const char* command); + + UInt32 GetBindingKey1(UInt32 code); + UInt32 GetBindingFlags1(UInt32 code); + UInt32 GetBindingKey2(UInt32 code); + UInt32 GetBindingFlags2(UInt32 code); + + UInt32 GetBindingKeyConsole(const char* command); + UInt32 GetBindingFlagsConsole(const char* command); + + void WriteKeyMap(); + +}; + +#endif // _pyKeyMap_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMapGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMapGlue.cpp new file mode 100644 index 00000000..791b266a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyKeyMapGlue.cpp @@ -0,0 +1,226 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyKeyMap.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptKeyMap, pyKeyMap); + +PYTHON_DEFAULT_NEW_DEFINITION(ptKeyMap, pyKeyMap) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptKeyMap) + +PYTHON_INIT_DEFINITION(ptKeyMap, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, convertVKeyToChar, args) +{ + unsigned long virtualKey, keyFlags; + if (!PyArg_ParseTuple(args, "ll", &virtualKey, &keyFlags)) + { + PyErr_SetString(PyExc_TypeError, "convertVKeyToChar expects two unsigned longs"); + PYTHON_RETURN_ERROR; + } + return PyString_FromString(self->fThis->ConvertVKeyToChar(virtualKey, keyFlags)); +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, convertCharToVKey, args) +{ + char* charString; + if (!PyArg_ParseTuple(args, "s", &charString)) + { + PyErr_SetString(PyExc_TypeError, "convertCharToVKey expects a string"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->ConvertCharToVKey(charString)); +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, convertCharToFlags, args) +{ + char* charString; + if (!PyArg_ParseTuple(args, "s", &charString)) + { + PyErr_SetString(PyExc_TypeError, "convertCharToFlags expects a string"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->ConvertCharToFlags(charString)); +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, convertCharToControlCode, args) +{ + char* charString; + if (!PyArg_ParseTuple(args, "s", &charString)) + { + PyErr_SetString(PyExc_TypeError, "convertCharToControlCode expects a string"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->ConvertCharToControlCode(charString)); +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, convertControlCodeToString, args) +{ + unsigned long code; + if (!PyArg_ParseTuple(args, "l", &code)) + { + PyErr_SetString(PyExc_TypeError, "convertControlCodeToString expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyString_FromString(self->fThis->ConvertControlCodeToString(code)); +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, bindKey, args) +{ + char* key1; + char* key2; + char* action; + if (!PyArg_ParseTuple(args, "sss", &key1, &key2, &action)) + { + PyErr_SetString(PyExc_TypeError, "bindKey expects three strings"); + PYTHON_RETURN_ERROR; + } + self->fThis->BindKey(key1, key2, action); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, getBindingKey1, args) +{ + unsigned long code; + if (!PyArg_ParseTuple(args, "l", &code)) + { + PyErr_SetString(PyExc_TypeError, "getBindingKey1 expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->GetBindingKey1(code)); +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, getBindingFlags1, args) +{ + unsigned long code; + if (!PyArg_ParseTuple(args, "l", &code)) + { + PyErr_SetString(PyExc_TypeError, "getBindingFlags1 expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->GetBindingFlags1(code)); +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, getBindingKey2, args) +{ + unsigned long code; + if (!PyArg_ParseTuple(args, "l", &code)) + { + PyErr_SetString(PyExc_TypeError, "getBindingKey2 expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->GetBindingKey2(code)); +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, getBindingFlags2, args) +{ + unsigned long code; + if (!PyArg_ParseTuple(args, "l", &code)) + { + PyErr_SetString(PyExc_TypeError, "getBindingFlags2 expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->GetBindingFlags2(code)); +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, bindKeyToConsoleCommand, args) +{ + char* keyStr1; + char* command; + if (!PyArg_ParseTuple(args, "ss", &keyStr1, &command)) + { + PyErr_SetString(PyExc_TypeError, "bindKeyToConsoleCommand expects two strings"); + PYTHON_RETURN_ERROR; + } + self->fThis->BindKeyToConsoleCommand(keyStr1, command); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, getBindingKeyConsole, args) +{ + char* command; + if (!PyArg_ParseTuple(args, "s", &command)) + { + PyErr_SetString(PyExc_TypeError, "getBindingKeyConsole expects a string"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->GetBindingKeyConsole(command)); +} + +PYTHON_METHOD_DEFINITION(ptKeyMap, getBindingFlagsConsole, args) +{ + char* command; + if (!PyArg_ParseTuple(args, "s", &command)) + { + PyErr_SetString(PyExc_TypeError, "getBindingFlagsConsole expects a string"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->GetBindingFlagsConsole(command)); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptKeyMap, writeKeyMap, WriteKeyMap) + +PYTHON_START_METHODS_TABLE(ptKeyMap) + PYTHON_METHOD(ptKeyMap, convertVKeyToChar, "Params: virtualKey,flags\nConvert virtual key and shift flags to string"), + PYTHON_METHOD(ptKeyMap, convertCharToVKey, "Params: charString\nConvert char string to virtual key"), + PYTHON_METHOD(ptKeyMap, convertCharToFlags, "Params: charString\nConvert char string to flags"), + PYTHON_METHOD(ptKeyMap, convertCharToControlCode, "Params: controlCodeString\nConvert string version of control code to number"), + PYTHON_METHOD(ptKeyMap, convertControlCodeToString, "Params controlCode\nConvert control code to character string"), + PYTHON_METHOD(ptKeyMap, bindKey, "Params key1,key2,action\nBind keys to an action"), + PYTHON_METHOD(ptKeyMap, getBindingKey1, "Params controlCode\nReturns key code for controlCode"), + PYTHON_METHOD(ptKeyMap, getBindingFlags1, "Params controlCode\nReturns modifier flags for controlCode"), + PYTHON_METHOD(ptKeyMap, getBindingKey2, "Params controlCode\nReturns key code for controlCode"), + PYTHON_METHOD(ptKeyMap, getBindingFlags2, "Params controlCode\nReturns modifier flags for controlCode"), + PYTHON_METHOD(ptKeyMap, bindKeyToConsoleCommand, "Params: keyStr1, command\nBinds key to console command"), + PYTHON_METHOD(ptKeyMap, getBindingKeyConsole, "Params: command\nReturns key for console command mapping"), + PYTHON_METHOD(ptKeyMap, getBindingFlagsConsole, "Params: command\nReturns modifier flags for the console command mapping"), + PYTHON_BASIC_METHOD(ptKeyMap, writeKeyMap, "Forces write of the keymap file"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptKeyMap, "Accessor class to the Key Mapping functions"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptKeyMap, pyKeyMap) + +PYTHON_CLASS_CHECK_IMPL(ptKeyMap, pyKeyMap) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptKeyMap, pyKeyMap) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyKeyMap::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptKeyMap); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgr.cpp new file mode 100644 index 00000000..c39eb5d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgr.cpp @@ -0,0 +1,126 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyMarkerMgr - a wrapper class to provide interface to the pfMarkerMgr stuff +// +////////////////////////////////////////////////////////////////////// + +#include "pyMarkerMgr.h" + +#include "../pfCharacter/pfMarkerMgr.h" + +void pyMarkerMgr::AddMarker(double x, double y, double z, UInt32 id, bool justCreated) +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + mmi->AddMarker(x, y, z, id, justCreated); +} + +void pyMarkerMgr::RemoveMarker(UInt32 id) +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + mmi->RemoveMarker(id); +} + +void pyMarkerMgr::RemoveAllMarkers() +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + mmi->RemoveAllMarkers(); +} + +void pyMarkerMgr::SetSelectedMarker(UInt32 markerID) +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + mmi->SetSelectedMarker(markerID); +} + +UInt32 pyMarkerMgr::GetSelectedMarker() +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + return mmi->GetSelectedMarker(); + return 0; +} + +void pyMarkerMgr::ClearSelectedMarker() +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + mmi->ClearSelectedMarker(); +} + +void pyMarkerMgr::SetMarkersRespawn(bool respawn) +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + mmi->SetMarkersRespawn(respawn); +} + +bool pyMarkerMgr::GetMarkersRespawn() +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + return mmi->GetMarkersRespawn(); + return false; +} + +void pyMarkerMgr::CaptureQuestMarker(UInt32 id, bool captured) +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + mmi->CaptureMarker(id, captured); +} + +void pyMarkerMgr::CaptureTeamMarker(UInt32 id, int team) +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + mmi->CaptureMarker(id, team); +} + +// Shows your markers locally, so you can see where they are +void pyMarkerMgr::ShowMarkersLocal() +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + mmi->LocalShowMarkers(true); +} + +void pyMarkerMgr::HideMarkersLocal() +{ + pfMarkerMgr* mmi = pfMarkerMgr::Instance(); + if (mmi) + mmi->LocalShowMarkers(false); +} + +bool pyMarkerMgr::AreLocalMarkersShowing() +{ + return pfMarkerMgr::Instance()->AreLocalMarkersShowing(); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgr.h new file mode 100644 index 00000000..a3ed381a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgr.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyMarkerMgr_h_inc +#define pyMarkerMgr_h_inc + +////////////////////////////////////////////////////////////////////// +// +// pyMarkerMgr - a wrapper class to provide interface to the pfMarkerMgr stuff +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" + +#include +#include "pyGlueHelpers.h" + +class pyMarkerMgr +{ +protected: + pyMarkerMgr() {} + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMarkerMgr); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMarkerMgr object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMarkerMgr); // converts a PyObject to a pyMarkerMgr (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaConstantsClasses(PyObject *m); + + void AddMarker(double x, double y, double z, UInt32 id, bool justCreated); + void RemoveMarker(UInt32 id); + void RemoveAllMarkers(); + + void SetSelectedMarker(UInt32 markerID); + UInt32 GetSelectedMarker(); + void ClearSelectedMarker(); + + void SetMarkersRespawn(bool respawn); + bool GetMarkersRespawn(); + + void CaptureQuestMarker(UInt32 id, bool captured); // for QUEST games (no teams) + void CaptureTeamMarker(UInt32 id, int team); // for TEAM games (0 = not captured) + + // Shows your markers locally, so you can see where they are + void ShowMarkersLocal(); + void HideMarkersLocal(); + bool AreLocalMarkersShowing(); +}; + + +#endif // pyMarkerMgr_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgrGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgrGlue.cpp new file mode 100644 index 00000000..6b918878 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMarkerMgrGlue.cpp @@ -0,0 +1,182 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyMarkerMgr.h" +#include "../pfMessage/pfMarkerMsg.h" +#include "pyEnum.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptMarkerMgr, pyMarkerMgr); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMarkerMgr, pyMarkerMgr) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMarkerMgr) + +PYTHON_INIT_DEFINITION(ptMarkerMgr, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptMarkerMgr, addMarker, args) +{ + double x, y, z; + unsigned long id; + byte justCreated; + if (!PyArg_ParseTuple(args, "dddlb", &x, &y, &z, &id, &justCreated)) + { + PyErr_SetString(PyExc_TypeError, "addMarker expects three doubles, an unsigned long, and a bool"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddMarker(x, y, z, id, justCreated != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMarkerMgr, removeMarker, args) +{ + unsigned long id; + if (!PyArg_ParseTuple(args, "l", &id)) + { + PyErr_SetString(PyExc_TypeError, "removeMarker expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->RemoveMarker(id); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptMarkerMgr, removeAllMarkers, RemoveAllMarkers) + +PYTHON_METHOD_DEFINITION(ptMarkerMgr, setSelectedMarker, args) +{ + unsigned long id; + if (!PyArg_ParseTuple(args, "l", &id)) + { + PyErr_SetString(PyExc_TypeError, "setSelectedMarker expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSelectedMarker(id); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMgr, getSelectedMarker) +{ + return PyLong_FromUnsignedLong(self->fThis->GetSelectedMarker()); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptMarkerMgr, clearSelectedMarker, ClearSelectedMarker) + +PYTHON_METHOD_DEFINITION(ptMarkerMgr, setMarkersRespawn, args) +{ + byte respawn; + if (!PyArg_ParseTuple(args, "b", &respawn)) + { + PyErr_SetString(PyExc_TypeError, "setMarkersRespawn expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetMarkersRespawn(respawn != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMgr, getMarkersRespawn) +{ + PYTHON_RETURN_BOOL(self->fThis->GetMarkersRespawn()); +} + +PYTHON_METHOD_DEFINITION(ptMarkerMgr, captureQuestMarker, args) +{ + unsigned long id; + byte captured; + if (!PyArg_ParseTuple(args, "lb", &id, &captured)) + { + PyErr_SetString(PyExc_TypeError, "captureQuestMarker expects an unsigned long and a bool"); + PYTHON_RETURN_ERROR; + } + self->fThis->CaptureQuestMarker(id, captured != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMarkerMgr, captureTeamMarker, args) +{ + unsigned long id; + int team; + if (!PyArg_ParseTuple(args, "li", &id, &team)) + { + PyErr_SetString(PyExc_TypeError, "captureTeamMarker expects an unsigned long and an int"); + PYTHON_RETURN_ERROR; + } + self->fThis->CaptureTeamMarker(id, team); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptMarkerMgr, showMarkersLocal, ShowMarkersLocal) +PYTHON_BASIC_METHOD_DEFINITION(ptMarkerMgr, hideMarkersLocal, HideMarkersLocal) + +PYTHON_METHOD_DEFINITION_NOARGS(ptMarkerMgr, areLocalMarkersShowing) +{ + PYTHON_RETURN_BOOL(self->fThis->AreLocalMarkersShowing()); +} + +PYTHON_START_METHODS_TABLE(ptMarkerMgr) + PYTHON_METHOD(ptMarkerMgr, addMarker, "Params: x, y, z, id, justCreated\nAdd a marker in the specified location with the specified id"), + PYTHON_METHOD(ptMarkerMgr, removeMarker, "Params: id\nRemoves the specified marker from the game"), + PYTHON_BASIC_METHOD(ptMarkerMgr, removeAllMarkers, "Removes all markers"), + PYTHON_METHOD(ptMarkerMgr, setSelectedMarker, "Params: id\nSets the selected marker to the one with the specified id"), + PYTHON_METHOD_NOARGS(ptMarkerMgr, getSelectedMarker, "Returns the id of the selected marker"), + PYTHON_BASIC_METHOD(ptMarkerMgr, clearSelectedMarker, "Unselects the selected marker"), + PYTHON_METHOD(ptMarkerMgr, setMarkersRespawn, "Params: respawn\nSets whether markers respawn after being captured, or not"), + PYTHON_METHOD_NOARGS(ptMarkerMgr, getMarkersRespawn, "Returns whether markers respawn after being captured, or not"), + PYTHON_METHOD(ptMarkerMgr, captureQuestMarker, "Params: id, captured\nSets a marker as captured or not"), + PYTHON_METHOD(ptMarkerMgr, captureTeamMarker, "Params: id, team\nSets a marker as captured by the specified team (0 = not captured)"), + PYTHON_BASIC_METHOD(ptMarkerMgr, showMarkersLocal, "Shows the markers on your machine, so you can see where they are"), + PYTHON_BASIC_METHOD(ptMarkerMgr, hideMarkersLocal, "Hides the markers on your machine, so you can no longer see where they are"), + PYTHON_METHOD_NOARGS(ptMarkerMgr, areLocalMarkersShowing, "Returns true if we are showing the markers on this local machine"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptMarkerMgr, "Marker manager accessor class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptMarkerMgr, pyMarkerMgr) + +PYTHON_CLASS_CHECK_IMPL(ptMarkerMgr, pyMarkerMgr) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMarkerMgr, pyMarkerMgr) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyMarkerMgr::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMarkerMgr); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyMarkerMgr::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtMarkerMsgType); + PYTHON_ENUM_ELEMENT(PtMarkerMsgType, kMarkerCaptured, pfMarkerMsg::kMarkerCaptured); + PYTHON_ENUM_END(m, PtMarkerMsgType); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44.cpp new file mode 100644 index 00000000..d4e46045 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44.cpp @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyMatrix44.h" + +pyMatrix44::pyMatrix44() { fMatrix.Reset(); } +pyMatrix44::pyMatrix44(hsMatrix44 other) +{ // copy the other matrix to this one + int i,j; + for ( i=0;i<4;i++) + for ( j=0;j<4;j++) + fMatrix.fMap[i][j] = other.fMap[i][j]; + fMatrix.fFlags = other.fFlags; +} + +PyObject* pyMatrix44::GetInverse(PyObject* inverse) +{ + pyMatrix44 *obj = pyMatrix44::ConvertFrom(inverse); + fMatrix.GetInverse(&(obj->fMatrix)); + Py_INCREF(inverse); // incref it because we need to return a new ref + return inverse; +} + +PyObject* pyMatrix44::GetTranspose(PyObject* transpose) +{ + pyMatrix44 *obj = pyMatrix44::ConvertFrom(transpose); + fMatrix.GetTranspose(&(obj->fMatrix)); + Py_INCREF(transpose); // incref it because we need to return a new ref + return transpose; +} + +PyObject* pyMatrix44::GetAdjoint(PyObject* adjoint) +{ + pyMatrix44 *obj = pyMatrix44::ConvertFrom(adjoint); + fMatrix.GetAdjoint(&(obj->fMatrix)); + Py_INCREF(adjoint); // incref it because we need to return a new ref + return adjoint; +} + +PyObject* pyMatrix44::GetTranslate(PyObject* pt) +{ + pyVector3 *obj = pyVector3::ConvertFrom(pt); + fMatrix.GetTranslate(&(obj->fVector)); + Py_INCREF(pt); // incref it because we need to return a new ref + return pt; +} + +std::vector< std::vector > pyMatrix44::GetData() +{ + std::vector row0, row1, row2, row3; + row0.push_back(fMatrix.fMap[0][0]); row0.push_back(fMatrix.fMap[0][1]); row0.push_back(fMatrix.fMap[0][2]); row0.push_back(fMatrix.fMap[0][3]); + row1.push_back(fMatrix.fMap[1][0]); row1.push_back(fMatrix.fMap[1][1]); row1.push_back(fMatrix.fMap[1][2]); row1.push_back(fMatrix.fMap[1][3]); + row2.push_back(fMatrix.fMap[2][0]); row2.push_back(fMatrix.fMap[2][1]); row2.push_back(fMatrix.fMap[2][2]); row2.push_back(fMatrix.fMap[2][3]); + row3.push_back(fMatrix.fMap[3][0]); row3.push_back(fMatrix.fMap[3][1]); row3.push_back(fMatrix.fMap[3][2]); row3.push_back(fMatrix.fMap[3][3]); + + std::vector< std::vector > pyMat; + pyMat.push_back(row0); + pyMat.push_back(row1); + pyMat.push_back(row2); + pyMat.push_back(row3); + + return pyMat; +} + +void pyMatrix44::SetData(const std::vector< std::vector > & mat) +{ + // make sure they are passing us the correct size + if ( mat.size() == 4 ) + { + int i,j; + for ( i=0;i<3;i++) + { + std::vector pyrow = mat[i]; + if ( pyrow.size() == 4 ) + { + for ( j=0;j<3;j++) + fMatrix.fMap[i][j] = pyrow[j]; + } + else // not enough ... throw error + { + char errmsg[256]; + sprintf(errmsg, "Wrong number of elements in row of matrix"); + PyErr_SetString(PyExc_TypeError, errmsg); + return; + } + } + } + else + { + char errmsg[256]; + sprintf(errmsg, "Wrong number of rows in the matrix"); + PyErr_SetString(PyExc_TypeError, errmsg); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44.h new file mode 100644 index 00000000..d5e5e4b7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyMatrix44_h_inc +#define pyMatrix44_h_inc + +#include "hsStlUtils.h" + +#include "hsMatrix44.h" +#include "pyGeometry3.h" + +#include +#include "pyGlueHelpers.h" + + +class pyMatrix44 +{ +protected: + pyMatrix44(); + pyMatrix44(hsMatrix44 other); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMatrix44); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(const hsMatrix44 &obj); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMatrix44 object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMatrix44); // converts a PyObject to a pyMatrix44 (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + hsMatrix44 fMatrix; + + // operator methods + PyObject* operator*(const pyMatrix44& b) const { return pyMatrix44::New(fMatrix * b.fMatrix); } // returns pyMatrix44 + PyObject* operator*(const pyVector3& p) const { return pyVector3::New(fMatrix * p.fVector); } // returns pyVector3 + PyObject* operator*(const pyPoint3& p) const { return pyPoint3::New(fMatrix * p.fPoint); } // returns pyPoint3 + + // other methods + PyObject* Copy() { return pyMatrix44::New(fMatrix); } // returns pyMatrix44 + void Translate(pyVector3 v) { fMatrix.Translate(&v.fVector); } + void Scale(pyVector3 v) { fMatrix.Scale(&v.fVector); } + void Rotate(int axis, hsScalar radians) { fMatrix.Rotate(axis,radians); } + void Reset() { fMatrix.Reset(); } + void MakeTranslateMat(pyVector3 trans) { fMatrix.MakeTranslateMat(&trans.fVector); } + void MakeScaleMat(pyVector3 scale) { fMatrix.MakeScaleMat(&scale.fVector); } + void MakeRotateMat(int axis, hsScalar radians) { fMatrix.MakeRotateMat(axis,radians); } + void Make(pyPoint3 from,pyPoint3 at,pyVector3 up) { fMatrix.Make(&from.fPoint,&at.fPoint,&up.fVector); } + void MakeUpPreserving(pyPoint3 from,pyPoint3 at,pyVector3 up) { fMatrix.MakeUpPreserving(&from.fPoint,&at.fPoint,&up.fVector); } + hsBool GetParity() { return fMatrix.GetParity(); } + hsScalar GetDeterminant() { return fMatrix.GetDeterminant(); } + PyObject* GetInverse(PyObject* inverse); // returns (and accepts) pyMatrix44 + PyObject* GetTranspose(PyObject* inverse); // returns (and accepts) pyMatrix44 + PyObject* GetAdjoint(PyObject* adjoint); // returns (and accepts) pyMatrix44 + PyObject* GetTranslate(PyObject* pt); // returns (and accepts) pyVector3 + PyObject* GetViewAxis() { return pyVector3::New(fMatrix.GetAxis(hsMatrix44::kView)); } // returns pyVector3 + PyObject* GetUpAxis() { return pyVector3::New(fMatrix.GetAxis(hsMatrix44::kUp)); } // returns pyVector3 + PyObject* GetRightAxis() { return pyVector3::New(fMatrix.GetAxis(hsMatrix44::kRight)); } // returns pyVector3 + + std::vector< std::vector > GetData(); + void SetData(const std::vector< std::vector > & mat); +}; + + +#endif // pyMatrix44_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44Glue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44Glue.cpp new file mode 100644 index 00000000..e7eab6ca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMatrix44Glue.cpp @@ -0,0 +1,438 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyMatrix44.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptMatrix44, pyMatrix44); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMatrix44, pyMatrix44) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMatrix44) + +PYTHON_INIT_DEFINITION(ptMatrix44, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMatrix44, copy) +{ + return self->fThis->Copy(); +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, translate, args) +{ + PyObject *vectorObj = NULL; + if (!PyArg_ParseTuple(args, "O", &vectorObj)) + { + PyErr_SetString(PyExc_TypeError, "translate expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(vectorObj)) + { + PyErr_SetString(PyExc_TypeError, "translate expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + + pyVector3 *vec = pyVector3::ConvertFrom(vectorObj); + self->fThis->Translate(*vec); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, scale, args) +{ + PyObject *vectorObj = NULL; + if (!PyArg_ParseTuple(args, "O", &vectorObj)) + { + PyErr_SetString(PyExc_TypeError, "scale expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(vectorObj)) + { + PyErr_SetString(PyExc_TypeError, "scale expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + + pyVector3 *vec = pyVector3::ConvertFrom(vectorObj); + self->fThis->Scale(*vec); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, rotate, args) +{ + int axis; + float radians; + if (!PyArg_ParseTuple(args, "if", &axis, &radians)) + { + PyErr_SetString(PyExc_TypeError, "rotate expects an integer and a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->Rotate(axis, radians); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptMatrix44, reset, Reset) + +PYTHON_METHOD_DEFINITION(ptMatrix44, makeTranslateMat, args) +{ + PyObject *vectorObj = NULL; + if (!PyArg_ParseTuple(args, "O", &vectorObj)) + { + PyErr_SetString(PyExc_TypeError, "makeTranslateMat expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(vectorObj)) + { + PyErr_SetString(PyExc_TypeError, "makeTranslateMat expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + + pyVector3 *vec = pyVector3::ConvertFrom(vectorObj); + self->fThis->MakeTranslateMat(*vec); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, makeScaleMat, args) +{ + PyObject *vectorObj = NULL; + if (!PyArg_ParseTuple(args, "O", &vectorObj)) + { + PyErr_SetString(PyExc_TypeError, "makeScaleMat expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(vectorObj)) + { + PyErr_SetString(PyExc_TypeError, "makeScaleMat expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + + pyVector3 *vec = pyVector3::ConvertFrom(vectorObj); + self->fThis->MakeScaleMat(*vec); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, makeRotateMat, args) +{ + int axis; + float radians; + if (!PyArg_ParseTuple(args, "if", &axis, &radians)) + { + PyErr_SetString(PyExc_TypeError, "makeRotateMat expects an integer and a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->MakeRotateMat(axis, radians); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, make, args) +{ + PyObject *fromPtObj = NULL; + PyObject *atPtObj = NULL; + PyObject *upVecObj = NULL; + if (!PyArg_ParseTuple(args, "OOO", &fromPtObj, &atPtObj, &upVecObj)) + { + PyErr_SetString(PyExc_TypeError, "make expects two ptPoint3 objects and a ptVector3"); + PYTHON_RETURN_ERROR; + } + if ((!pyPoint3::Check(fromPtObj))||(!pyPoint3::Check(atPtObj))||(!pyVector3::Check(upVecObj))) + { + PyErr_SetString(PyExc_TypeError, "make expects two ptPoint3 objects and a ptVector3"); + PYTHON_RETURN_ERROR; + } + + pyPoint3 *fromPt = pyPoint3::ConvertFrom(fromPtObj); + pyPoint3 *atPt = pyPoint3::ConvertFrom(atPtObj); + pyVector3 *upVec = pyVector3::ConvertFrom(upVecObj); + self->fThis->Make(*fromPt, *atPt, *upVec); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, makeUpPreserving, args) +{ + PyObject *fromPtObj = NULL; + PyObject *atPtObj = NULL; + PyObject *upVecObj = NULL; + if (!PyArg_ParseTuple(args, "OOO", &fromPtObj, &atPtObj, &upVecObj)) + { + PyErr_SetString(PyExc_TypeError, "makeUpPreserving expects two ptPoint3 objects and a ptVector3"); + PYTHON_RETURN_ERROR; + } + if ((!pyPoint3::Check(fromPtObj))||(!pyPoint3::Check(atPtObj))||(!pyVector3::Check(upVecObj))) + { + PyErr_SetString(PyExc_TypeError, "makeUpPreserving expects two ptPoint3 objects and a ptVector3"); + PYTHON_RETURN_ERROR; + } + + pyPoint3 *fromPt = pyPoint3::ConvertFrom(fromPtObj); + pyPoint3 *atPt = pyPoint3::ConvertFrom(atPtObj); + pyVector3 *upVec = pyVector3::ConvertFrom(upVecObj); + self->fThis->MakeUpPreserving(*fromPt, *atPt, *upVec); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMatrix44, getParity) +{ + PYTHON_RETURN_BOOL(self->fThis->GetParity()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMatrix44, getDeterminant) +{ + return PyFloat_FromDouble((double)self->fThis->GetDeterminant()); +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, getInverse, args) +{ + PyObject *inverseObj = NULL; + if (!PyArg_ParseTuple(args, "O", &inverseObj)) + { + PyErr_SetString(PyExc_TypeError, "getInverse expects a ptMatrix44"); + PYTHON_RETURN_ERROR; + } + if (!pyMatrix44::Check(inverseObj)) + { + PyErr_SetString(PyExc_TypeError, "getInverse expects a ptMatrix44"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetInverse(inverseObj); +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, getTranspose, args) +{ + PyObject *transposeObj = NULL; + if (!PyArg_ParseTuple(args, "O", &transposeObj)) + { + PyErr_SetString(PyExc_TypeError, "getTranspose expects a ptMatrix44"); + PYTHON_RETURN_ERROR; + } + if (!pyMatrix44::Check(transposeObj)) + { + PyErr_SetString(PyExc_TypeError, "getTranspose expects a ptMatrix44"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetTranspose(transposeObj); +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, getAdjoint, args) +{ + PyObject *adjointObj = NULL; + if (!PyArg_ParseTuple(args, "O", &adjointObj)) + { + PyErr_SetString(PyExc_TypeError, "getAdjoint expects a ptMatrix44"); + PYTHON_RETURN_ERROR; + } + if (!pyMatrix44::Check(adjointObj)) + { + PyErr_SetString(PyExc_TypeError, "getAdjoint expects a ptMatrix44"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetAdjoint(adjointObj); +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, getTranslate, args) +{ + PyObject *translateObj = NULL; + if (!PyArg_ParseTuple(args, "O", &translateObj)) + { + PyErr_SetString(PyExc_TypeError, "translateObj expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + if (!pyVector3::Check(translateObj)) + { + PyErr_SetString(PyExc_TypeError, "translateObj expects a ptVector3"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetTranslate(translateObj); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMatrix44, view) +{ + return self->fThis->GetViewAxis(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMatrix44, up) +{ + return self->fThis->GetUpAxis(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMatrix44, right) +{ + return self->fThis->GetRightAxis(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptMatrix44, getData) +{ + std::vector< std::vector > mat = self->fThis->GetData(); + PyObject *retVal = PyTuple_New(4); + for (int curRow = 0; curRow < mat.size(); curRow++) + { + PyObject *row = PyTuple_New(4); + for (int curElement = 0; curElement < mat[curRow].size(); curElement++) + { + PyObject *item = PyInt_FromLong((long)mat[curRow][curElement]); + PyTuple_SetItem(row, curElement, item); // steals the ref + } + PyTuple_SetItem(retVal, curRow, row); // steals the ref + } + return retVal; +} + +PYTHON_METHOD_DEFINITION(ptMatrix44, setData, args) +{ + PyObject *matTuple = NULL; + if (!PyArg_ParseTuple(args, "O", &matTuple)) + { + PyErr_SetString(PyExc_TypeError, "setData expects a 4x4 tuple of floats"); + PYTHON_RETURN_ERROR; + } + if (!PyTuple_Check(matTuple)) + { + PyErr_SetString(PyExc_TypeError, "setData expects a 4x4 tuple of floats"); + PYTHON_RETURN_ERROR; + } + + std::vector< std::vector > mat; + int numRows = PyTuple_Size(matTuple); + for (int curRow = 0; curRow < numRows; curRow++) + { + std::vector vecRow; + PyObject *row = PyTuple_GetItem(matTuple, curRow); // borrowed ref + if (!PyTuple_Check(row)) + { + PyErr_SetString(PyExc_TypeError, "setData expects a 4x4 tuple of floats"); + PYTHON_RETURN_ERROR; + } + int numElements = PyTuple_Size(row); + for (int curElement = 0; curElement < numElements; curElement++) + { + PyObject *item = PyTuple_GetItem(row, curElement); // borrowed ref + if (!PyFloat_Check(item)) + { + PyErr_SetString(PyExc_TypeError, "setData expects a 4x4 tuple of floats"); + PYTHON_RETURN_ERROR; + } + vecRow.push_back((float)PyFloat_AsDouble(item)); + } + mat.push_back(vecRow); + } + + self->fThis->SetData(mat); + if (PyErr_Occurred()) + PYTHON_RETURN_ERROR; + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptMatrix44) + PYTHON_METHOD_NOARGS(ptMatrix44, copy, "Copies the matrix and returns the copy"), + PYTHON_METHOD(ptMatrix44, translate, "Params: vector\nTranslates the matrix by the vector"), + PYTHON_METHOD(ptMatrix44, scale, "Params: scale\nScales the matrix by the vector"), + PYTHON_METHOD(ptMatrix44, rotate, "Params: axis,radians\nRotates the matrix by radians around the axis"), + PYTHON_BASIC_METHOD(ptMatrix44, reset, "Reset the matrix to identity"), + PYTHON_METHOD(ptMatrix44, makeTranslateMat, "Params: trans\nMakes the matrix a translation matrix"), + PYTHON_METHOD(ptMatrix44, makeScaleMat, "Params: scale\nMakes the matrix a scaling matrix"), + PYTHON_METHOD(ptMatrix44, makeRotateMat, "Params: axis,radians\nMakes the matrix a rotation matrix"), + PYTHON_METHOD(ptMatrix44, make, "Params: fromPt, atPt, upVec\nCreates the matrix from from and at points, and the up vector"), + PYTHON_METHOD(ptMatrix44, makeUpPreserving, "Params: fromPt, atPt, upVec\nCreates the matrix from from and at points, and the up vector (perserving the up vector)"), + PYTHON_METHOD_NOARGS(ptMatrix44, getParity, "Get the parity of the matrix"), + PYTHON_METHOD_NOARGS(ptMatrix44, getDeterminant, "Get the matrix's determinant"), + PYTHON_METHOD(ptMatrix44, getInverse, "Params: inverseMat\nReturns the inverse of the matrix"), + PYTHON_METHOD(ptMatrix44, getTranspose, "Params: transposeMat\nReturns the transpose of the matrix"), + PYTHON_METHOD(ptMatrix44, getAdjoint, "Params: adjointMat\nReturns the adjoint of the matrix"), + PYTHON_METHOD(ptMatrix44, getTranslate, "Params: vector\nReturns the translate vector of the matrix (and sets vector to it as well)"), + PYTHON_METHOD_NOARGS(ptMatrix44, view, "Returns the view vector of the matrix"), + PYTHON_METHOD_NOARGS(ptMatrix44, up, "Returns the up vector of the matrix"), + PYTHON_METHOD_NOARGS(ptMatrix44, right, "Returns the right vector of the matrix"), + PYTHON_METHOD_NOARGS(ptMatrix44, getData, "Returns the matrix in tuple form"), + PYTHON_METHOD(ptMatrix44, setData, "Params: mat\nSets the matrix using tuples"), +PYTHON_END_METHODS_TABLE; + +PyObject *ptMatrix44_mul(PyObject *v, PyObject *w) +{ + if (pyMatrix44::Check(v)) + { + pyMatrix44 *us = pyMatrix44::ConvertFrom(v); + if (pyMatrix44::Check(w)) + { + pyMatrix44 *them = pyMatrix44::ConvertFrom(w); + return (*us) * (*them); + } + else if (pyVector3::Check(w)) + { + pyVector3 *them = pyVector3::ConvertFrom(w); + return (*us) * (*them); + } + else if (pyPoint3::Check(w)) + { + pyPoint3 *them = pyPoint3::ConvertFrom(w); + return (*us) * (*them); + } + } + PyErr_SetString(PyExc_NotImplementedError, "can only multiply a ptMatrix44 by a ptVector3, or ptPoint3"); + PYTHON_RETURN_NOT_IMPLEMENTED; +} + +// we support some of the number methods +PYTHON_START_AS_NUMBER_TABLE(ptMatrix44) + 0, /*nb_add*/ + 0, /*nb_subtract*/ + (binaryfunc)ptMatrix44_mul, /*nb_multiply*/ + 0 /*nb_divide*/ + /* the rest can be null */ +PYTHON_END_AS_NUMBER_TABLE; + +// Type structure definition +#define ptMatrix44_COMPARE PYTHON_NO_COMPARE +#define ptMatrix44_AS_NUMBER PYTHON_DEFAULT_AS_NUMBER(ptMatrix44) +#define ptMatrix44_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptMatrix44_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptMatrix44_STR PYTHON_NO_STR +#define ptMatrix44_RICH_COMPARE PYTHON_NO_RICH_COMPARE +#define ptMatrix44_GETSET PYTHON_NO_GETSET +#define ptMatrix44_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptMatrix44, "Plasma Matrix44 class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptMatrix44, pyMatrix44) + +PyObject *pyMatrix44::New(const hsMatrix44 &obj) +{ + ptMatrix44 *newObj = (ptMatrix44*)ptMatrix44_type.tp_new(&ptMatrix44_type, NULL, NULL); + newObj->fThis->fMatrix = obj; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMatrix44, pyMatrix44) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMatrix44, pyMatrix44) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyMatrix44::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMatrix44); + PYTHON_CLASS_IMPORT_END(m); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayer.cpp new file mode 100644 index 00000000..8d19d900 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayer.cpp @@ -0,0 +1,186 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyMoviePlayer - a wrapper class all the Movie functions +// +////////////////////////////////////////////////////////////////////// + +#include "pyMoviePlayer.h" + +#include "../plMessage/plMovieMsg.h" +#include "../pfMessage/pfMovieEventMsg.h" + +pyMoviePlayer::pyMoviePlayer(const char* movieName,pyKey& selfKey) +{ + fMovieName = hsStrcpy(movieName); + fSelfKey = selfKey.getKey(); + // make the movie + if ( fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kMake | plMovieMsg::kAddCallbacks); + mov->SetSender(fSelfKey); + pfMovieEventMsg* movCallback = TRACKED_NEW pfMovieEventMsg(fMovieName); + movCallback->AddReceiver(fSelfKey); + mov->AddCallback(movCallback); + mov->Send(); + hsRefCnt_SafeUnRef(movCallback); + } +} + + +pyMoviePlayer::~pyMoviePlayer() +{ + Stop(); + delete [] fMovieName; +} + +void pyMoviePlayer::MakeMovie(const char* movieName, pyKey& selfKey) +{ + Stop(); + if (fMovieName) + delete [] fMovieName; + fMovieName = hsStrcpy(movieName); + fSelfKey = selfKey.getKey(); + if (fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kMake | plMovieMsg::kAddCallbacks); + mov->SetSender(fSelfKey); + pfMovieEventMsg* movCallback = TRACKED_NEW pfMovieEventMsg(fMovieName); + movCallback->AddReceiver(fSelfKey); + mov->AddCallback(movCallback); + mov->Send(); + hsRefCnt_SafeUnRef(movCallback); + } +} + +void pyMoviePlayer::SetCenter(hsScalar x, hsScalar y) +{ + if ( fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kMove); + mov->SetSender(fSelfKey); + mov->SetCenterX(x); + mov->SetCenterY(y); + mov->Send(); + } +} + +void pyMoviePlayer::SetScale(hsScalar width, hsScalar height) +{ + if ( fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kScale); + mov->SetSender(fSelfKey); + mov->SetScaleX(width); + mov->SetScaleY(height); + mov->Send(); + } +} + +void pyMoviePlayer::SetColor(pyColor color) +{ + if ( fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kColor); + mov->SetSender(fSelfKey); + mov->SetColor(color.getColor()); + mov->Send(); + } +} + +void pyMoviePlayer::SetVolume(hsScalar volume) +{ + if ( fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kVolume); + mov->SetSender(fSelfKey); + mov->SetVolume(volume); + mov->Send(); + } +} + +void pyMoviePlayer::SetOpacity(hsScalar opacity) +{ + if ( fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kOpacity); + mov->SetSender(fSelfKey); + mov->SetOpacity(opacity); + mov->Send(); + } +} + + +void pyMoviePlayer::Play() +{ + if ( fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kStart); + mov->SetSender(fSelfKey); + mov->Send(); + } +} + +void pyMoviePlayer::PlayPaused() +{ + if ( fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kStart | plMovieMsg::kPause); + mov->SetSender(fSelfKey); + mov->Send(); + } +} + +void pyMoviePlayer::Pause() +{ + if ( fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kPause); + mov->SetSender(fSelfKey); + mov->Send(); + } +} + +void pyMoviePlayer::Resume() +{ + if ( fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kResume); + mov->SetSender(fSelfKey); + mov->Send(); + } +} + +void pyMoviePlayer::Stop() +{ + if ( fMovieName) + { + plMovieMsg* mov = TRACKED_NEW plMovieMsg(fMovieName, plMovieMsg::kStop); + mov->SetSender(fSelfKey); + mov->Send(); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayer.h new file mode 100644 index 00000000..5d80fb51 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayer.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyMoviePlayer_h_ +#define _pyMoviePlayer_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyMoviePlayer - a wrapper class all the movie player functions +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" +#include "pyColor.h" + +#include +#include "pyGlueHelpers.h" + +class pyMoviePlayer +{ +protected: + char* fMovieName; + plKey fSelfKey; + + pyMoviePlayer(): fMovieName(nil), fSelfKey(nil) {} // only used by python glue, do NOT call + pyMoviePlayer(const char* movieName,pyKey& selfKey); +public: + ~pyMoviePlayer(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptMoviePlayer); + static PyObject *New(const char* movieName, pyKey& selfKey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyMoviePlayer object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyMoviePlayer); // converts a PyObject to a pyMoviePlayer (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaConstantsClasses(PyObject *m); + + void MakeMovie(const char* movieName, pyKey& selfKey); // only used by python glue, do NOT call + + // getters and setters + virtual void SetCenter(hsScalar x, hsScalar y); + virtual void SetScale(hsScalar width, hsScalar height); + virtual void SetColor(pyColor color); + virtual void SetVolume(hsScalar volume); + virtual void SetOpacity(hsScalar opacity); + + // actions + virtual void Play(); // kStart + virtual void PlayPaused(); // kStart and kPause + virtual void Pause(); // kPause + virtual void Resume(); // kResume + virtual void Stop(); // kStop + +}; + +#endif // _pyMoviePlayer_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayerGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayerGlue.cpp new file mode 100644 index 00000000..e4b1ae49 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyMoviePlayerGlue.cpp @@ -0,0 +1,174 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyMoviePlayer.h" +#include "pyEnum.h" +#include "pyKey.h" +#include "pyColor.h" + +#include "../pfMessage/pfMovieEventMsg.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptMoviePlayer, pyMoviePlayer); + +PYTHON_DEFAULT_NEW_DEFINITION(ptMoviePlayer, pyMoviePlayer) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptMoviePlayer) + +PYTHON_INIT_DEFINITION(ptMoviePlayer, args, keywords) +{ + char* movieName; + PyObject* selfKeyObj = NULL; + if (!PyArg_ParseTuple(args, "sO", &movieName, &selfKeyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a string and ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(selfKeyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a string and ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + pyKey* selfKey = pyKey::ConvertFrom(selfKeyObj); + self->fThis->MakeMovie(movieName, *selfKey); + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptMoviePlayer, setCenter, args) +{ + float x, y; + if (!PyArg_ParseTuple(args, "ff", &x, &y)) + { + PyErr_SetString(PyExc_TypeError, "setCenter expects two floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetCenter(x, y); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMoviePlayer, setScale, args) +{ + float width, height; + if (!PyArg_ParseTuple(args, "ff", &width, &height)) + { + PyErr_SetString(PyExc_TypeError, "setScale expects two floats"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetScale(width, height); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMoviePlayer, setColor, args) +{ + PyObject* colorObj = NULL; + if (!PyArg_ParseTuple(args, "O", &colorObj)) + { + PyErr_SetString(PyExc_TypeError, "setColor expects a ptColor"); + PYTHON_RETURN_ERROR; + } + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "setColor expects a ptColor"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + self->fThis->SetColor(*color); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMoviePlayer, setVolume, args) +{ + float volume; + if (!PyArg_ParseTuple(args, "f", &volume)) + { + PyErr_SetString(PyExc_TypeError, "setVolume expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetVolume(volume); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptMoviePlayer, setOpacity, args) +{ + float opacity; + if (!PyArg_ParseTuple(args, "f", &opacity)) + { + PyErr_SetString(PyExc_TypeError, "setOpacity expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetOpacity(opacity); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptMoviePlayer, play, Play) +PYTHON_BASIC_METHOD_DEFINITION(ptMoviePlayer, playPaused, PlayPaused) +PYTHON_BASIC_METHOD_DEFINITION(ptMoviePlayer, pause, Pause) +PYTHON_BASIC_METHOD_DEFINITION(ptMoviePlayer, resume, Resume) +PYTHON_BASIC_METHOD_DEFINITION(ptMoviePlayer, stop, Stop) + +PYTHON_START_METHODS_TABLE(ptMoviePlayer) + PYTHON_METHOD(ptMoviePlayer, setCenter, "Params: x,y\nSets the center of the movie"), + PYTHON_METHOD(ptMoviePlayer, setScale, "Params: width,height\nSets the width and height scale of the movie"), + PYTHON_METHOD(ptMoviePlayer, setColor, "Params: color\nSets the color of the movie"), + PYTHON_METHOD(ptMoviePlayer, setVolume, "Params: volume\nSet the volume of the movie"), + PYTHON_METHOD(ptMoviePlayer, setOpacity, "Params: opacity\nSets the opacity of the movie"), + PYTHON_BASIC_METHOD(ptMoviePlayer, play, "Plays the movie"), + PYTHON_BASIC_METHOD(ptMoviePlayer, playPaused, "Plays movie, but pauses at first frame"), + PYTHON_BASIC_METHOD(ptMoviePlayer, pause, "Pauses the movie"), + PYTHON_BASIC_METHOD(ptMoviePlayer, resume, "Resumes movie after pausing"), + PYTHON_BASIC_METHOD(ptMoviePlayer, stop, "Stops the movie"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptMoviePlayer, "Params: movieName,selfKey\nAccessor class to play in the MoviePlayer"); + +// required functions for PyObject interoperability +PyObject *pyMoviePlayer::New(const char* movieName, pyKey& selfKey) +{ + ptMoviePlayer *newObj = (ptMoviePlayer*)ptMoviePlayer_type.tp_new(&ptMoviePlayer_type, NULL, NULL); + newObj->fThis->MakeMovie(movieName, selfKey); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptMoviePlayer, pyMoviePlayer) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptMoviePlayer, pyMoviePlayer) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyMoviePlayer::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptMoviePlayer); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyMoviePlayer::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtMovieEventReason); + PYTHON_ENUM_ELEMENT(PtMovieEventReason, kMovieDone, pfMovieEventMsg::kMovieDone); + PYTHON_ENUM_END(m, PtMovieEventReason); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgr.cpp new file mode 100644 index 00000000..c1b14014 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgr.cpp @@ -0,0 +1,96 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifdef BUILDING_PYPLASMA +# error "pyNetLinkingMgr is not compatible with pyPlasma.pyd. Use BUILDING_PYPLASMA macro to ifdef out unwanted headers." +#endif + +#include "hsStlUtils.h" + +#include "pyNetLinkingMgr.h" +#include "../plNetClient/plNetLinkingMgr.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plArmatureMod.h" + +#include "pyAgeInfoStruct.h" +#include "pyAgeLinkStruct.h" + +hsBool pyNetLinkingMgr::IsEnabled( void ) const +{ + return plNetLinkingMgr::GetInstance()->IsEnabled(); +} + +void pyNetLinkingMgr::SetEnabled( hsBool b ) +{ + plNetLinkingMgr::GetInstance()->SetEnabled( b?true:false ); +} + +void pyNetLinkingMgr::LinkToAge( pyAgeLinkStruct & link, const char* linkAnim ) +{ + plNetLinkingMgr::GetInstance()->LinkToAge( link.GetAgeLink(), linkAnim ); +} + +void pyNetLinkingMgr::LinkToMyPersonalAge() +{ + plNetLinkingMgr::GetInstance()->LinkToMyPersonalAge(); +} + +void pyNetLinkingMgr::LinkToMyPersonalAgeWithYeeshaBook() +{ + // use special avatar's open my personal book and link + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + avatar->PersonalLink(); +} + +void pyNetLinkingMgr::LinkToMyNeighborhoodAge() +{ + plNetLinkingMgr::GetInstance()->LinkToMyNeighborhoodAge(); +} + +void pyNetLinkingMgr::LinkPlayerHere( UInt32 playerID ) +{ + plNetLinkingMgr::GetInstance()->LinkPlayerHere( playerID ); +} + +void pyNetLinkingMgr::LinkPlayerToAge( pyAgeLinkStruct & link, UInt32 playerID ) +{ + plNetLinkingMgr::GetInstance()->LinkPlayerToAge( link.GetAgeLink(), playerID ); +} + +void pyNetLinkingMgr::LinkToPlayersAge( UInt32 playerID ) +{ + plNetLinkingMgr::GetInstance()->LinkToPlayersAge( playerID ); +} + +PyObject* pyNetLinkingMgr::GetCurrAgeLink() +{ + return pyAgeLinkStructRef::New( *plNetLinkingMgr::GetInstance()->GetAgeLink() ); +} + +PyObject* pyNetLinkingMgr::GetPrevAgeLink() +{ + return pyAgeLinkStructRef::New( *plNetLinkingMgr::GetInstance()->GetPrevAgeLink() ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgr.h new file mode 100644 index 00000000..94ed8e9f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgr.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyNetLinkingMgr_h_inc +#define pyNetLinkingMgr_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +////////////////////////////////////////////////////////////////////// +// +// pyNetLinkingMgr - a wrapper class to provide interface to the plNetLinkingMgr +// +////////////////////////////////////////////////////////////////////// + +class pyAgeInfoStruct; +class pyAgeLinkStruct; +class pyAgeLinkStructRef; + +class pyNetLinkingMgr +{ +#ifndef BUILDING_PYPLASMA +protected: + pyNetLinkingMgr() {} + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptNetLinkingMgr); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyNetLinkingMgr object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyNetLinkingMgr); // converts a PyObject to a pyNetLinkingMgr (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); +#else +public: +#endif // BUILDING_PYPLASMA + static void AddPlasmaConstantsClasses(PyObject *m); + +#ifndef BUILDING_PYPLASMA + // enable/disable linking + hsBool IsEnabled( void ) const; + void SetEnabled( hsBool b ); + + // Link to a public instance. PLS will load balance. + void LinkToAge( pyAgeLinkStruct & link, const char* linkAnim ); + // Link to my Personal Age + void LinkToMyPersonalAge(); + // link to my personal age with the YeehsaBook + void LinkToMyPersonalAgeWithYeeshaBook(); + // Link to my Neighborhood Age + void LinkToMyNeighborhoodAge(); + // Link player to my current age + void LinkPlayerHere( UInt32 playerID ); + // Link player to specified age + void LinkPlayerToAge( pyAgeLinkStruct & link, UInt32 playerID ); + // Link to player's current age + void LinkToPlayersAge( UInt32 playerID ); + + PyObject* GetCurrAgeLink(); // returns pyAgeLinkStructRef + PyObject* GetPrevAgeLink(); // returns pyAgeLinkStructRef +#endif // BUILDING_PYPLASMA +}; + +#endif // pyNetLinkingMgr_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgrGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgrGlue.cpp new file mode 100644 index 00000000..bd5488b4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetLinkingMgrGlue.cpp @@ -0,0 +1,185 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyNetLinkingMgr.h" +#include "pyEnum.h" +#include "pyAgeLinkStruct.h" + +#include "../plNetCommon/plNetCommon.h" +#include + +#ifndef BUILDING_PYPLASMA + +// glue functions +PYTHON_CLASS_DEFINITION(ptNetLinkingMgr, pyNetLinkingMgr); + +PYTHON_DEFAULT_NEW_DEFINITION(ptNetLinkingMgr, pyNetLinkingMgr) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptNetLinkingMgr) + +PYTHON_INIT_DEFINITION(ptNetLinkingMgr, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetLinkingMgr, isEnabled) +{ + PYTHON_RETURN_BOOL(self->fThis->IsEnabled()); +} + +PYTHON_METHOD_DEFINITION(ptNetLinkingMgr, setEnabled, args) +{ + char enable; + if (!PyArg_ParseTuple(args, "b", &enable)) + { + PyErr_SetString(PyExc_TypeError, "setEnabled expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetEnabled(enable != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetLinkingMgr, linkToAge, args) +{ + PyObject* ageLinkObj = NULL; + char* linkAnim = NULL; + if (!PyArg_ParseTuple(args, "O|s", &ageLinkObj, &linkAnim)) + { + PyErr_SetString(PyExc_TypeError, "linkToAge expects a ptAgeLinkStruct and an optional link anim name"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeLinkStruct::Check(ageLinkObj)) + { + PyErr_SetString(PyExc_TypeError, "linkToAge expects a ptAgeLinkStruct and an optional link anim name"); + PYTHON_RETURN_ERROR; + } + pyAgeLinkStruct* ageLink = pyAgeLinkStruct::ConvertFrom(ageLinkObj); + self->fThis->LinkToAge(*ageLink, linkAnim); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptNetLinkingMgr, linkToMyPersonalAge, LinkToMyPersonalAge) +PYTHON_BASIC_METHOD_DEFINITION(ptNetLinkingMgr, linkToMyPersonalAgeWithYeeshaBook, LinkToMyPersonalAgeWithYeeshaBook) +PYTHON_BASIC_METHOD_DEFINITION(ptNetLinkingMgr, linkToMyNeighborhoodAge, LinkToMyNeighborhoodAge) + +PYTHON_METHOD_DEFINITION(ptNetLinkingMgr, linkPlayerHere, args) +{ + unsigned long pid; + if (!PyArg_ParseTuple(args, "l", &pid)) + { + PyErr_SetString(PyExc_TypeError, "linkPlayerHere expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->LinkPlayerHere(pid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetLinkingMgr, linkPlayerToAge, args) +{ + PyObject* ageLinkObj = NULL; + unsigned long pid; + if (!PyArg_ParseTuple(args, "Ol", &ageLinkObj, &pid)) + { + PyErr_SetString(PyExc_TypeError, "linkPlayerToAge expects a ptAgeLinkStruct and an unsigned long"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeLinkStruct::Check(ageLinkObj)) + { + PyErr_SetString(PyExc_TypeError, "linkPlayerToAge expects a ptAgeLinkStruct and an unsigned long"); + PYTHON_RETURN_ERROR; + } + pyAgeLinkStruct* ageLink = pyAgeLinkStruct::ConvertFrom(ageLinkObj); + self->fThis->LinkPlayerToAge(*ageLink, pid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetLinkingMgr, linkToPlayersAge, args) +{ + unsigned long pid; + if (!PyArg_ParseTuple(args, "l", &pid)) + { + PyErr_SetString(PyExc_TypeError, "linkToPlayersAge expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->LinkToPlayersAge(pid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetLinkingMgr, getCurrAgeLink) +{ + return self->fThis->GetCurrAgeLink(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetLinkingMgr, getPrevAgeLink) +{ + return self->fThis->GetPrevAgeLink(); +} + +PYTHON_START_METHODS_TABLE(ptNetLinkingMgr) + PYTHON_METHOD_NOARGS(ptNetLinkingMgr, isEnabled, "True if linking is enabled."), + PYTHON_METHOD(ptNetLinkingMgr, setEnabled, "Params: enable\nEnable/Disable linking."), + PYTHON_METHOD(ptNetLinkingMgr, linkToAge, "Params: ageLink, linkAnim\nLinks to ageLink (ptAgeLinkStruct, string)"), + PYTHON_BASIC_METHOD(ptNetLinkingMgr, linkToMyPersonalAge, "Link to my Personal Age"), + PYTHON_BASIC_METHOD(ptNetLinkingMgr, linkToMyPersonalAgeWithYeeshaBook, "Link to my Personal Age with the YeeshaBook"), + PYTHON_BASIC_METHOD(ptNetLinkingMgr, linkToMyNeighborhoodAge, "Link to my Neighborhood Age"), + PYTHON_METHOD(ptNetLinkingMgr, linkPlayerHere, "Params: pid\nlink player(pid) to where I am"), + PYTHON_METHOD(ptNetLinkingMgr, linkPlayerToAge, "Params: ageLink,pid\nLink player(pid) to ageLink"), + PYTHON_METHOD(ptNetLinkingMgr, linkToPlayersAge, "Params: pid\nLink me to where player(pid) is"), + PYTHON_METHOD_NOARGS(ptNetLinkingMgr, getCurrAgeLink, "Get the ptAgeLinkStruct for the current age"), + PYTHON_METHOD_NOARGS(ptNetLinkingMgr, getPrevAgeLink, "Get the ptAgeLinkStruct for the previous age"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptNetLinkingMgr, "Constructor to get access to the net link manager"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptNetLinkingMgr, pyNetLinkingMgr) + +PYTHON_CLASS_CHECK_IMPL(ptNetLinkingMgr, pyNetLinkingMgr) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptNetLinkingMgr, pyNetLinkingMgr) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyNetLinkingMgr::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptNetLinkingMgr); + PYTHON_CLASS_IMPORT_END(m); +} + +#endif // BUILDING_PYPLASMA + +void pyNetLinkingMgr::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtLinkingRules); + PYTHON_ENUM_ELEMENT(PtLinkingRules, kBasicLink, plNetCommon::LinkingRules::kBasicLink); + PYTHON_ENUM_ELEMENT(PtLinkingRules, kOriginalBook, plNetCommon::LinkingRules::kOriginalBook); + PYTHON_ENUM_ELEMENT(PtLinkingRules, kSubAgeBook, plNetCommon::LinkingRules::kSubAgeBook); + PYTHON_ENUM_ELEMENT(PtLinkingRules, kOwnedBook, plNetCommon::LinkingRules::kOwnedBook); + PYTHON_ENUM_ELEMENT(PtLinkingRules, kVisitBook, plNetCommon::LinkingRules::kVisitBook); + PYTHON_ENUM_ELEMENT(PtLinkingRules, kChildAgeBook, plNetCommon::LinkingRules::kChildAgeBook); + PYTHON_ENUM_END(m, PtLinkingRules); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfo.cpp new file mode 100644 index 00000000..6b48862f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfo.cpp @@ -0,0 +1,32 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" + +#include "pyNetServerSessionInfo.h" + +//////////////////////////////////////////////////////////////////// + +plNetServerSessionInfo pyNetServerSessionInfoRef::fDefaultServerSessionInfo; // for python glue use only, do NOT use \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfo.h new file mode 100644 index 00000000..f01f159a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfo.h @@ -0,0 +1,123 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyNetServerSessionInfo_h_inc +#define pyNetServerSessionInfo_h_inc + +#include "hsTypes.h" +#include "../plNetCommon/plNetServerSessionInfo.h" +#include "../plUUID/plUUID.h" + +#include +#include "pyGlueHelpers.h" + +////////////////////////////////////////////////////////////////////// +// +// pyNetServerSessionInfo, pyNetServerSessionInfoRef +// - wrapper classes to provide interface to the plNetServerSessionInfo +// +////////////////////////////////////////////////////////////////////// + +class pyNetServerSessionInfo +{ +private: + plNetServerSessionInfo fInfo; + mutable plUUID fServerGuid; // for GetServerGuid() + +protected: + pyNetServerSessionInfo() {} + pyNetServerSessionInfo( const plNetServerSessionInfo & info ): fInfo( info ) {} + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptNetServerSessionInfo); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(const plNetServerSessionInfo &info); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyNetServerSessionInfo object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyNetServerSessionInfo); // converts a PyObject to a pyNetServerSessionInfo (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + plNetServerSessionInfo & ServerInfo() { return fInfo; } + + void SetServerName(const char * val) { fInfo.SetServerName( val ); } + void SetServerType(UInt8 val) { fInfo.SetServerType( val ); } + void SetServerAddr(const char * val) { fInfo.SetServerAddr( val ); } + void SetServerPort(UInt16 val) { fInfo.SetServerPort( val ); } + void SetServerGuid(const char * val) { fServerGuid.FromString( val ); fInfo.SetServerGuid( &fServerGuid ); } + bool HasServerName() const { return fInfo.HasServerName(); } + bool HasServerType() const { return fInfo.HasServerType(); } + bool HasServerAddr() const { return fInfo.HasServerAddr(); } + bool HasServerPort() const { return fInfo.HasServerPort(); } + bool HasServerGuid() const { return fInfo.HasServerGuid(); } + const char * GetServerName() const { return fInfo.GetServerName(); } + UInt8 GetServerType() const { return fInfo.GetServerType(); } + const char * GetServerAddr() const { return fInfo.GetServerAddr(); } + UInt16 GetServerPort() const { return fInfo.GetServerPort(); } + const char * GetServerGuid() const { fServerGuid.CopyFrom( fInfo.GetServerGuid() ); return fServerGuid.AsString(); } +}; + + +class pyNetServerSessionInfoRef +{ +private: + static plNetServerSessionInfo fDefaultServerSessionInfo; // created so a default constructor could be made for python. Do NOT use + + plNetServerSessionInfo& fInfo; + mutable plUUID fServerGuid; // for GetServerGuid() + +protected: + pyNetServerSessionInfoRef(): fInfo(fDefaultServerSessionInfo) {} // only here for the python glue... do NOT call directly + pyNetServerSessionInfoRef( plNetServerSessionInfo& info ): fInfo( info ) {} + plNetServerSessionInfo & ServerInfo() { return fInfo; } + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptNetServerSessionInfoRef); + static PyObject *New(plNetServerSessionInfo &info); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyNetServerSessionInfoRef object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyNetServerSessionInfoRef); // converts a PyObject to a pyNetServerSessionInfoRef (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + void SetServerName(const char * val) { fInfo.SetServerName( val ); } + void SetServerType(UInt8 val) { fInfo.SetServerType( val ); } + void SetServerAddr(const char * val) { fInfo.SetServerAddr( val ); } + void SetServerPort(UInt16 val) { fInfo.SetServerPort( val ); } + void SetServerGuid(const char * val) { fServerGuid.FromString( val ); fInfo.SetServerGuid( &fServerGuid ); } + bool HasServerName() const { return fInfo.HasServerName(); } + bool HasServerType() const { return fInfo.HasServerType(); } + bool HasServerAddr() const { return fInfo.HasServerAddr(); } + bool HasServerPort() const { return fInfo.HasServerPort(); } + bool HasServerGuid() const { return fInfo.HasServerGuid(); } + const char * GetServerName() const { return fInfo.GetServerName(); } + UInt8 GetServerType() const { return fInfo.GetServerType(); } + const char * GetServerAddr() const { return fInfo.GetServerAddr(); } + UInt16 GetServerPort() const { return fInfo.GetServerPort(); } + const char * GetServerGuid() const { fServerGuid.CopyFrom( fInfo.GetServerGuid() ); return fServerGuid.AsString(); } +}; + + +#endif // pyNetServerSessionInfo_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfoGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfoGlue.cpp new file mode 100644 index 00000000..459842f1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNetServerSessionInfoGlue.cpp @@ -0,0 +1,355 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyNetServerSessionInfo.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptNetServerSessionInfo, pyNetServerSessionInfo); + +PYTHON_DEFAULT_NEW_DEFINITION(ptNetServerSessionInfo, pyNetServerSessionInfo) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptNetServerSessionInfo) + +PYTHON_INIT_DEFINITION(ptNetServerSessionInfo, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptNetServerSessionInfo, setServerName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "setServerName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetServerName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetServerSessionInfo, setServerType, args) +{ + unsigned char serverType; + if (!PyArg_ParseTuple(args, "b", &serverType)) + { + PyErr_SetString(PyExc_TypeError, "setServerType expects an unsigned 8-bit int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetServerType(serverType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetServerSessionInfo, setServerAddr, args) +{ + char* addr; + if (!PyArg_ParseTuple(args, "s", &addr)) + { + PyErr_SetString(PyExc_TypeError, "setServerAddr expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetServerAddr(addr); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetServerSessionInfo, setServerPort, args) +{ + unsigned short port; + if (!PyArg_ParseTuple(args, "h", &port)) + { + PyErr_SetString(PyExc_TypeError, "setServerPort expects a unsigned short"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetServerPort(port); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetServerSessionInfo, setServerGuid, args) +{ + char* guid; + if (!PyArg_ParseTuple(args, "s", &guid)) + { + PyErr_SetString(PyExc_TypeError, "setServerGuid expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetServerGuid(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfo, hasServerName) +{ + PYTHON_RETURN_BOOL(self->fThis->HasServerName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfo, hasServerType) +{ + PYTHON_RETURN_BOOL(self->fThis->HasServerType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfo, hasServerAddr) +{ + PYTHON_RETURN_BOOL(self->fThis->HasServerAddr()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfo, hasServerPort) +{ + PYTHON_RETURN_BOOL(self->fThis->HasServerPort()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfo, hasServerGuid) +{ + PYTHON_RETURN_BOOL(self->fThis->HasServerGuid()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfo, getServerName) +{ + return PyString_FromString(self->fThis->GetServerName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfo, getServerType) +{ + return PyInt_FromLong(self->fThis->GetServerType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfo, getServerAddr) +{ + return PyString_FromString(self->fThis->GetServerAddr()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfo, getServerPort) +{ + return PyInt_FromLong(self->fThis->GetServerPort()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfo, getServerGuid) +{ + return PyString_FromString(self->fThis->GetServerGuid()); +} + +PYTHON_START_METHODS_TABLE(ptNetServerSessionInfo) + PYTHON_METHOD(ptNetServerSessionInfo, setServerName, "Params: name\nUNKNOWN"), + PYTHON_METHOD(ptNetServerSessionInfo, setServerType, "Params: type\nUNKNOWN"), + PYTHON_METHOD(ptNetServerSessionInfo, setServerAddr, "Params: addr\nUNKNOWN"), + PYTHON_METHOD(ptNetServerSessionInfo, setServerPort, "Params: port\nUNKNOWN"), + PYTHON_METHOD(ptNetServerSessionInfo, setServerGuid, "Params: guid\nUNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfo, hasServerName, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfo, hasServerType, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfo, hasServerAddr, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfo, hasServerPort, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfo, hasServerGuid, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfo, getServerName, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfo, getServerType, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfo, getServerAddr, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfo, getServerPort, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfo, getServerGuid, "UNKNOWN"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptNetServerSessionInfo, "Basic server session info class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptNetServerSessionInfo, pyNetServerSessionInfo) + +PyObject *pyNetServerSessionInfo::New(const plNetServerSessionInfo &info) +{ + ptNetServerSessionInfo *newObj = (ptNetServerSessionInfo*)ptNetServerSessionInfo_type.tp_new(&ptNetServerSessionInfo_type, NULL, NULL); + newObj->fThis->fInfo = info; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptNetServerSessionInfo, pyNetServerSessionInfo) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptNetServerSessionInfo, pyNetServerSessionInfo) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyNetServerSessionInfo::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptNetServerSessionInfo); + PYTHON_CLASS_IMPORT_END(m); +} + +// glue functions +PYTHON_CLASS_DEFINITION(ptNetServerSessionInfoRef, pyNetServerSessionInfoRef); + +PYTHON_DEFAULT_NEW_DEFINITION(ptNetServerSessionInfoRef, pyNetServerSessionInfoRef) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptNetServerSessionInfoRef) + +PYTHON_NO_INIT_DEFINITION(ptNetServerSessionInfoRef) + +PYTHON_METHOD_DEFINITION(ptNetServerSessionInfoRef, setServerName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "setServerName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetServerName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetServerSessionInfoRef, setServerType, args) +{ + unsigned char serverType; + if (!PyArg_ParseTuple(args, "b", &serverType)) + { + PyErr_SetString(PyExc_TypeError, "setServerType expects an unsigned 8-bit int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetServerType(serverType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetServerSessionInfoRef, setServerAddr, args) +{ + char* addr; + if (!PyArg_ParseTuple(args, "s", &addr)) + { + PyErr_SetString(PyExc_TypeError, "setServerAddr expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetServerAddr(addr); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetServerSessionInfoRef, setServerPort, args) +{ + unsigned short port; + if (!PyArg_ParseTuple(args, "h", &port)) + { + PyErr_SetString(PyExc_TypeError, "setServerPort expects a unsigned short"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetServerPort(port); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetServerSessionInfoRef, setServerGuid, args) +{ + char* guid; + if (!PyArg_ParseTuple(args, "s", &guid)) + { + PyErr_SetString(PyExc_TypeError, "setServerGuid expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetServerGuid(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfoRef, hasServerName) +{ + PYTHON_RETURN_BOOL(self->fThis->HasServerName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfoRef, hasServerType) +{ + PYTHON_RETURN_BOOL(self->fThis->HasServerType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfoRef, hasServerAddr) +{ + PYTHON_RETURN_BOOL(self->fThis->HasServerAddr()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfoRef, hasServerPort) +{ + PYTHON_RETURN_BOOL(self->fThis->HasServerPort()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfoRef, hasServerGuid) +{ + PYTHON_RETURN_BOOL(self->fThis->HasServerGuid()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfoRef, getServerName) +{ + return PyString_FromString(self->fThis->GetServerName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfoRef, getServerType) +{ + return PyInt_FromLong(self->fThis->GetServerType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfoRef, getServerAddr) +{ + return PyString_FromString(self->fThis->GetServerAddr()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfoRef, getServerPort) +{ + return PyInt_FromLong(self->fThis->GetServerPort()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetServerSessionInfoRef, getServerGuid) +{ + return PyString_FromString(self->fThis->GetServerGuid()); +} + +PYTHON_START_METHODS_TABLE(ptNetServerSessionInfoRef) + PYTHON_METHOD(ptNetServerSessionInfoRef, setServerName, "Params: name\nUNKNOWN"), + PYTHON_METHOD(ptNetServerSessionInfoRef, setServerType, "Params: type\nUNKNOWN"), + PYTHON_METHOD(ptNetServerSessionInfoRef, setServerAddr, "Params: addr\nUNKNOWN"), + PYTHON_METHOD(ptNetServerSessionInfoRef, setServerPort, "Params: port\nUNKNOWN"), + PYTHON_METHOD(ptNetServerSessionInfoRef, setServerGuid, "Params: guid\nUNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfoRef, hasServerName, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfoRef, hasServerType, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfoRef, hasServerAddr, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfoRef, hasServerPort, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfoRef, hasServerGuid, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfoRef, getServerName, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfoRef, getServerType, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfoRef, getServerAddr, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfoRef, getServerPort, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetServerSessionInfoRef, getServerGuid, "UNKNOWN"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptNetServerSessionInfoRef, "Basic server session info class"); + +// required functions for PyObject interoperability +PyObject *pyNetServerSessionInfoRef::New(plNetServerSessionInfo &info) +{ + ptNetServerSessionInfoRef *newObj = (ptNetServerSessionInfoRef*)ptNetServerSessionInfoRef_type.tp_new(&ptNetServerSessionInfoRef_type, NULL, NULL); + newObj->fThis->fInfo = info; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptNetServerSessionInfoRef, pyNetServerSessionInfoRef) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptNetServerSessionInfoRef, pyNetServerSessionInfoRef) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyNetServerSessionInfoRef::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptNetServerSessionInfoRef); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotify.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotify.cpp new file mode 100644 index 00000000..324a5648 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotify.cpp @@ -0,0 +1,208 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyNotify - a wrapper class to provide interface to send a NotifyMsg +// +////////////////////////////////////////////////////////////////////// + +#include "plgDispatch.h" +#include "../pnMessage/plNotifyMsg.h" +#include "pyKey.h" +#include "plPythonFileMod.h" +#include "pyGeometry3.h" + +#include "pyNotify.h" + +pyNotify::pyNotify() +{ + fSenderKey = nil; + fNetPropagate = true; + fNetForce = false; + fBuildMsg.fType = plNotifyMsg::kActivator; + fBuildMsg.fState = 0.0f; + fBuildMsg.fID = 0; +} + +pyNotify::pyNotify(pyKey& selfkey) +{ + fSenderKey = selfkey.getKey(); + fNetPropagate = true; + fNetForce = false; + fBuildMsg.fType = plNotifyMsg::kActivator; + fBuildMsg.fState = 0.0f; + fBuildMsg.fID = 0; + // loop though adding the ones that want to be notified of the change + int j; + for ( j=0 ; jgetKey()); +} + +void pyNotify::SetNetPropagate(hsBool propagate) +{ + fNetPropagate = propagate; +} + +void pyNotify::SetNetForce(hsBool state) +{ + fNetForce = state; +} + + +void pyNotify::SetActivateState(hsScalar state) +{ + fBuildMsg.SetState(state); +} + +void pyNotify::SetType(Int32 type) +{ + fBuildMsg.fType = type; +} + + +////////////////////////////////////////////////// +// Add event record helpers +////////////////////////////////////////////////// + +void pyNotify::AddCollisionEvent( hsBool enter, pyKey* other, pyKey* self ) +{ + fBuildMsg.AddCollisionEvent(enter, other ? other->getKey() : plKey(), + self ? self->getKey() : plKey() ); +} + +void pyNotify::AddPickEvent( hsBool enabled, pyKey* other, pyKey* self, pyPoint3 hitPoint) +{ + fBuildMsg.AddPickEvent( other ? other->getKey() : plKey(), + self ? self->getKey() : plKey(), + enabled, + hitPoint.fPoint ); +} + +void pyNotify::AddControlKeyEvent( Int32 key, hsBool down ) +{ + fBuildMsg.AddControlKeyEvent(key,down); +} + +void pyNotify::AddVarNumber(const char* name, hsScalar number) +{ + fBuildMsg.AddVariableEvent(name,number); +} + +void pyNotify::AddVarKey(const char* name, pyKey* key) +{ + fBuildMsg.AddVariableEvent(name, key ? key->getKey() : plKey() ); +} + +void pyNotify::AddFacingEvent( hsBool enabled, pyKey* other, pyKey* self, hsScalar dot) +{ + fBuildMsg.AddFacingEvent( other ? other->getKey() : plKey(), + self ? self->getKey() : plKey(), + dot, enabled); +} + +void pyNotify::AddContainerEvent( hsBool entering, pyKey* contained, pyKey* container) +{ + fBuildMsg.AddContainerEvent( container ? container->getKey() : plKey(), + contained ? contained->getKey() : plKey() , + entering); +} + +void pyNotify::AddActivateEvent( hsBool active, hsBool activate ) +{ + fBuildMsg.AddActivateEvent(activate); +} + +void pyNotify::AddCallbackEvent( Int32 event ) +{ + fBuildMsg.AddCallbackEvent(event); +} + +void pyNotify::AddResponderState(Int32 state) +{ + fBuildMsg.AddResponderStateEvent(state); +} + +void pyNotify::Send() +{ + if (!fReceivers.Count()) // Notify msgs must have receivers, can't be bcast by type + return; + + // create new notify message to do the actual send with + plNotifyMsg* pNMsg = TRACKED_NEW plNotifyMsg; + + if ( fNetPropagate ) + pNMsg->SetBCastFlag(plMessage::kNetPropagate); + else + pNMsg->SetBCastFlag(plMessage::kNetPropagate,false); + // set whether this should be forced over the network (ignoring net-cascading) + if ( fNetForce ) + pNMsg->SetBCastFlag(plMessage::kNetForce); + + // copy data and event records to new NotifyMsg + pNMsg->fType = fBuildMsg.fType; + pNMsg->fState = fBuildMsg.fState; + pNMsg->fID = fBuildMsg.fID; + // need to recreate all the events in the new message by Adding them + int i; + for ( i=0; iAddEvent( pED ); + } + + // add receivers + // loop though adding the ones that want to be notified of the change + int j; + for ( j=0 ; jAddReceiver(fReceivers[j]); + + pNMsg->SetSender(fSenderKey); + plgDispatch::MsgSend( pNMsg ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotify.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotify.h new file mode 100644 index 00000000..52abcccf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotify.h @@ -0,0 +1,98 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyNotify_h_ +#define _pyNotify_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyNotify - a wrapper class to provide interface to send a NotifyMsg +// +////////////////////////////////////////////////////////////////////// + +#include "../pnMessage/plNotifyMsg.h" + +#include "pyKey.h" +#include "pyGeometry3.h" + +#include +#include "pyGlueHelpers.h" + +class pyNotify +{ +private: + plKey fSenderKey; // the holder of the who (the modifier) we are + // the list of receivers that want to be notified + hsTArray fReceivers; + + hsBool fNetPropagate; + hsBool fNetForce; + + // notify message build area + plNotifyMsg fBuildMsg; + +protected: + pyNotify(); // only used by python glue, do NOT call + pyNotify(pyKey& selfkey); + +public: + ~pyNotify(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptNotify); + static PyObject *New(pyKey& selfkey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyNotify object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyNotify); // converts a PyObject to a pyNotify (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaConstantsClasses(PyObject *m); + + void SetSender(pyKey& selfkey); // only used by python glue, do NOT call + + // methods that will be exposed to Python + virtual void ClearReceivers(); + virtual void AddReceiver(pyKey* key); + virtual void SetNetPropagate(hsBool propagate); + virtual void SetNetForce(hsBool state); + virtual void SetActivateState(hsScalar state); + virtual void SetType(Int32 type); + + // add event record helpers + virtual void AddCollisionEvent( hsBool enter, pyKey* other, pyKey* self ); + virtual void AddPickEvent(hsBool enabled, pyKey* other, pyKey* self, pyPoint3 hitPoint); + virtual void AddControlKeyEvent( Int32 key, hsBool down ); + virtual void AddVarNumber(const char* name, hsScalar number); + virtual void AddVarKey(const char* name, pyKey* key); + virtual void AddFacingEvent( hsBool enabled, pyKey* other, pyKey* self, hsScalar dot); + virtual void AddContainerEvent( hsBool entering, pyKey* container, pyKey* contained); + virtual void AddActivateEvent( hsBool active, hsBool activate ); + virtual void AddCallbackEvent( Int32 event ); + virtual void AddResponderState(Int32 state); + + virtual void Send(); + +}; + +#endif // _pyNotify_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotifyGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotifyGlue.cpp new file mode 100644 index 00000000..f4bccb9f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyNotifyGlue.cpp @@ -0,0 +1,399 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyNotify.h" +#include "pyEnum.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptNotify, pyNotify); + +PYTHON_DEFAULT_NEW_DEFINITION(ptNotify, pyNotify) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptNotify) + +PYTHON_INIT_DEFINITION(ptNotify, args, keywords) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->SetSender(*key); + PYTHON_RETURN_INIT_OK; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptNotify, clearReceivers, ClearReceivers) + +PYTHON_METHOD_DEFINITION(ptNotify, addReceiver, args) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "addReceiver expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "addReceiver expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->AddReceiver(key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, netPropagate, args) +{ + char netFlag; + if (!PyArg_ParseTuple(args, "b", &netFlag)) + { + PyErr_SetString(PyExc_TypeError, "netPropagate expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNetPropagate(netFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, netForce, args) +{ + char netFlag; + if (!PyArg_ParseTuple(args, "b", &netFlag)) + { + PyErr_SetString(PyExc_TypeError, "netForce expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNetForce(netFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, setActivate, args) +{ + float actState; + if (!PyArg_ParseTuple(args, "f", &actState)) + { + PyErr_SetString(PyExc_TypeError, "setActivate expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetActivateState(actState); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, setType, args) +{ + long msgType; + if (!PyArg_ParseTuple(args, "l", &msgType)) + { + PyErr_SetString(PyExc_TypeError, "setType expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetType(msgType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, addCollisionEvent, args) +{ + char enterFlag; + PyObject* hitterKey = NULL; + PyObject* hitteeKey = NULL; + if (!PyArg_ParseTuple(args, "bOO", &enterFlag, &hitterKey, &hitteeKey)) + { + PyErr_SetString(PyExc_TypeError, "addCollisionEvent expects a boolean, and two ptKeys"); + PYTHON_RETURN_ERROR; + } + if ((!pyKey::Check(hitterKey)) || (!pyKey::Check(hitteeKey))) + { + PyErr_SetString(PyExc_TypeError, "addCollisionEvent expects a boolean, and two ptKeys"); + PYTHON_RETURN_ERROR; + } + pyKey* hitter = pyKey::ConvertFrom(hitterKey); + pyKey* hittee = pyKey::ConvertFrom(hitteeKey); + self->fThis->AddCollisionEvent(enterFlag != 0, hitter, hittee); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, addPickEvent, args) +{ + char enabledFlag; + PyObject* pickerKey = NULL; + PyObject* pickeeKey = NULL; + PyObject* hitPointObj = NULL; + if (!PyArg_ParseTuple(args, "bOOO", &enabledFlag, &pickerKey, &pickeeKey, &hitPointObj)) + { + PyErr_SetString(PyExc_TypeError, "addPickEvent expects a boolean, two ptKeys and a ptPoint3"); + PYTHON_RETURN_ERROR; + } + if ((!pyKey::Check(pickerKey)) || (!pyKey::Check(pickeeKey)) || (!pyPoint3::Check(hitPointObj))) + { + PyErr_SetString(PyExc_TypeError, "addPickEvent expects a boolean, two ptKeys and a ptPoint3"); + PYTHON_RETURN_ERROR; + } + pyKey* picker = pyKey::ConvertFrom(pickerKey); + pyKey* pickee = pyKey::ConvertFrom(pickeeKey); + pyPoint3* hitPoint = pyPoint3::ConvertFrom(hitPointObj); + self->fThis->AddPickEvent(enabledFlag != 0, picker, pickee, *hitPoint); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, addControlKeyEvent, args) +{ + long key; + char down; + if (!PyArg_ParseTuple(args, "lb", &key, &down)) + { + PyErr_SetString(PyExc_TypeError, "addControlKeyEvent expects a long and a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddControlKeyEvent(key, down != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, addVarNumber, args) +{ + char* name; + float number; + if (!PyArg_ParseTuple(args, "sf", &name, &number)) + { + PyErr_SetString(PyExc_TypeError, "addVarNumber expects a string and a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddVarNumber(name, number); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, addVarKey, args) +{ + char* name; + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "sO", &name, &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "addVarKey expects a string and a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "addVarKey expects a string and a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->AddVarKey(name, key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, addFacingEvent, args) +{ + char enabledFlag; + PyObject* facerKey = NULL; + PyObject* faceeKey = NULL; + float dot; + if (!PyArg_ParseTuple(args, "bOOf", &enabledFlag, &facerKey, &faceeKey, &dot)) + { + PyErr_SetString(PyExc_TypeError, "addFacingEvent expects a boolean, two ptKeys, and a float"); + PYTHON_RETURN_ERROR; + } + if ((!pyKey::Check(facerKey)) || (!pyKey::Check(faceeKey))) + { + PyErr_SetString(PyExc_TypeError, "addFacingEvent expects a boolean, two ptKeys, and a float"); + PYTHON_RETURN_ERROR; + } + pyKey* facer = pyKey::ConvertFrom(facerKey); + pyKey* facee = pyKey::ConvertFrom(faceeKey); + self->fThis->AddFacingEvent(enabledFlag != 0, facer, facee, dot); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, addContainerEvent, args) +{ + char enterFlag; + PyObject* containerKey = NULL; + PyObject* containedKey = NULL; + if (!PyArg_ParseTuple(args, "bOO", &enterFlag, &containerKey, &containedKey)) + { + PyErr_SetString(PyExc_TypeError, "addContainerEvent expects a boolean, and two ptKeys"); + PYTHON_RETURN_ERROR; + } + + pyKey* container = NULL; + pyKey* contained = NULL; + + if (containerKey != Py_None) + { + if (!pyKey::Check(containerKey)) + { + PyErr_SetString(PyExc_TypeError, "addContainerEvent expects a boolean, and two ptKeys"); + PYTHON_RETURN_ERROR; + } + container = pyKey::ConvertFrom(containerKey); + } + + if (containedKey != Py_None) + { + if (!pyKey::Check(containedKey)) + { + PyErr_SetString(PyExc_TypeError, "addContainerEvent expects a boolean, and two ptKeys"); + PYTHON_RETURN_ERROR; + } + contained = pyKey::ConvertFrom(containedKey); + } + + self->fThis->AddContainerEvent(enterFlag != 0, container, contained); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, addActivateEvent, args) +{ + char activeFlag, activateFlag; + if (!PyArg_ParseTuple(args, "bb", &activeFlag, &activateFlag)) + { + PyErr_SetString(PyExc_TypeError, "addActivateEvent expects two booleans"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddActivateEvent(activeFlag != 0, activateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, addCallbackEvent, args) +{ + long eventNumber; + if (!PyArg_ParseTuple(args, "l", &eventNumber)) + { + PyErr_SetString(PyExc_TypeError, "addCallbackEvent expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddCallbackEvent(eventNumber); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNotify, addResponderState, args) +{ + long respState; + if (!PyArg_ParseTuple(args, "l", &respState)) + { + PyErr_SetString(PyExc_TypeError, "addResponderState expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddResponderState(respState); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptNotify, send, Send) + +PYTHON_START_METHODS_TABLE(ptNotify) + PYTHON_BASIC_METHOD(ptNotify, clearReceivers, "Remove all the receivers that this Notify message has\n" + "- receivers are automatically added if from a ptAttribActivator"), + PYTHON_METHOD(ptNotify, addReceiver, "Params: key\nAdd a receivers key to receive this Notify message"), + PYTHON_METHOD(ptNotify, netPropagate, "Params: netFlag\nSets the net propagate flag - default to set"), + PYTHON_METHOD(ptNotify, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n" + "- This is to be used if your Python program is running on only one client\n" + "Such as a game master, only running on the client that owns a particular object"), + PYTHON_METHOD(ptNotify, setActivate, "Params: state\nSet the activate state to true(1.0) or false(0.0)"), + PYTHON_METHOD(ptNotify, setType, "Params: type\nSets the message type"), + PYTHON_METHOD(ptNotify, addCollisionEvent, "Params: enterFlag,hitterKey,hitteeKey\nAdd a collision event record to the Notify message"), + PYTHON_METHOD(ptNotify, addPickEvent, "Params: enabledFlag,pickerKey,pickeeKey,hitPoint\nAdd a pick event record to the Notify message"), + PYTHON_METHOD(ptNotify, addControlKeyEvent, "Params: keynumber,downFlag\nAdd a keyboard event record to the Notify message"), + PYTHON_METHOD(ptNotify, addVarNumber, "Params: name,number\nAdd a number variable event record to the Notify message\n" + "This event record is used to pass a number variable to another python program"), + PYTHON_METHOD(ptNotify, addVarKey, "Params: name,key\nAdd a ptKey variable event record to the Notify message\n" + "This event record is used to pass a ptKey variable to another python program"), + PYTHON_METHOD(ptNotify, addFacingEvent, "Params: enabledFlag,facerKey, faceeKey, dotProduct\nAdd a facing event record to the Notify message"), + PYTHON_METHOD(ptNotify, addContainerEvent, "Params: enteringFlag,containerKey,containedKey\nAdd a container event record to the notify message"), + PYTHON_METHOD(ptNotify, addActivateEvent, "Params: activeFlag,activateFlag\nAdd an activate event record to the notify message"), + PYTHON_METHOD(ptNotify, addCallbackEvent, "Params: eventNumber\nAdd a callback event record to the notify message"), + PYTHON_METHOD(ptNotify, addResponderState, "Params: state\nAdd a responder state event record to the notify message"), + PYTHON_BASIC_METHOD(ptNotify, send, "Send the notify message"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptNotify, "Params: selfKey\nCreates a Notify message\n" + "- selfKey is ptKey of your PythonFile modifier"); + +// required functions for PyObject interoperability +PyObject *pyNotify::New(pyKey& selfkey) +{ + ptNotify *newObj = (ptNotify*)ptNotify_type.tp_new(&ptNotify_type, NULL, NULL); + newObj->fThis->SetSender(selfkey); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptNotify, pyNotify) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptNotify, pyNotify) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyNotify::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptNotify); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyNotify::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtNotificationType); + PYTHON_ENUM_ELEMENT(PtNotificationType, kActivator, plNotifyMsg::kActivator); + PYTHON_ENUM_ELEMENT(PtNotificationType, kVarNotification, plNotifyMsg::kVarNotification); + PYTHON_ENUM_ELEMENT(PtNotificationType, kNotifySelf, plNotifyMsg::kNotifySelf); + PYTHON_ENUM_ELEMENT(PtNotificationType, kResponderFF, plNotifyMsg::kResponderFF); + PYTHON_ENUM_ELEMENT(PtNotificationType, kResponderChangeState, plNotifyMsg::kResponderChangeState); + PYTHON_ENUM_END(m, PtNotificationType); + + PYTHON_ENUM_START(PtEventType); + PYTHON_ENUM_ELEMENT(PtEventType, kCollision, proEventData::kCollision); + PYTHON_ENUM_ELEMENT(PtEventType, kPicked, proEventData::kPicked); + PYTHON_ENUM_ELEMENT(PtEventType, kControlKey, proEventData::kControlKey); + PYTHON_ENUM_ELEMENT(PtEventType, kVariable, proEventData::kVariable); + PYTHON_ENUM_ELEMENT(PtEventType, kFacing, proEventData::kFacing); + PYTHON_ENUM_ELEMENT(PtEventType, kContained, proEventData::kContained); + PYTHON_ENUM_ELEMENT(PtEventType, kActivate, proEventData::kActivate); + PYTHON_ENUM_ELEMENT(PtEventType, kCallback, proEventData::kCallback); + PYTHON_ENUM_ELEMENT(PtEventType, kResponderState, proEventData::kResponderState); + PYTHON_ENUM_ELEMENT(PtEventType, kMultiStage, proEventData::kMultiStage); + PYTHON_ENUM_ELEMENT(PtEventType, kSpawned, proEventData::kSpawned); + PYTHON_ENUM_ELEMENT(PtEventType, kClickDrag, proEventData::kClickDrag); + PYTHON_ENUM_ELEMENT(PtEventType, kOfferLinkingBook, proEventData::kOfferLinkingBook); + PYTHON_ENUM_ELEMENT(PtEventType, kBook, proEventData::kBook); + PYTHON_ENUM_END(m, PtEventType); + + PYTHON_ENUM_START(PtNotifyDataType); + PYTHON_ENUM_ELEMENT(PtNotifyDataType, kNumber, proEventData::kNumber); + PYTHON_ENUM_ELEMENT(PtNotifyDataType, kKey, proEventData::kKey); + PYTHON_ENUM_END(m, PtNotifyDataType); + + PYTHON_ENUM_START(PtMultiStageEventType); + PYTHON_ENUM_ELEMENT(PtMultiStageEventType, kEnterStage, proEventData::kEnterStage); + PYTHON_ENUM_ELEMENT(PtMultiStageEventType, kBeginingOfLoop, proEventData::kBeginingOfLoop); + PYTHON_ENUM_ELEMENT(PtMultiStageEventType, kAdvanceNextStage, proEventData::kAdvanceNextStage); + PYTHON_ENUM_ELEMENT(PtMultiStageEventType, kRegressPrevStage, proEventData::kRegressPrevStage); + PYTHON_ENUM_END(m, PtMultiStageEventType); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayer.cpp new file mode 100644 index 00000000..293a9ca4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayer.cpp @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyPlayer.h" + +pyPlayer::pyPlayer() // only used by python glue, do NOT call +{ + fAvatarKey = nil; + fPlayerName = ""; + fPlayerID = 0; + fDistSq = -1; + fIsCCR = false; + fIsServer = false; +} + +pyPlayer::pyPlayer(pyKey& avKey, const char* pname, UInt32 pid, hsScalar distsq) +{ + fAvatarKey = avKey.getKey(); + fPlayerName = pname; + fPlayerID = pid; + fDistSq = distsq; + fIsCCR = false; + fIsServer = false; +} + +pyPlayer::pyPlayer(plKey avKey, const char* pname, UInt32 pid, hsScalar distsq) +{ + fAvatarKey = avKey; + fPlayerName = pname; + fPlayerID = pid; + fDistSq = distsq; + fIsCCR = false; + fIsServer = false; +} + +// another way to create a player with just a name and number +pyPlayer::pyPlayer(const char* pname, UInt32 pid) +{ + fAvatarKey = nil; + fPlayerName = pname; + fPlayerID = pid; + fDistSq = -1; + fIsCCR = false; + fIsServer = false; +} + +void pyPlayer::Init(plKey avKey, const char* pname, UInt32 pid, hsScalar distsq) // used by python glue, do NOT call +{ + fAvatarKey = avKey; + fPlayerName = pname; + fPlayerID = pid; + fDistSq = distsq; + fIsCCR = false; + fIsServer = false; +} + +void pyPlayer::SetCCRFlag(hsBool state) +{ + fIsCCR = state; +} + +hsBool pyPlayer::IsCCR() +{ + return fIsCCR; +} + +void pyPlayer::SetServerFlag(hsBool state) +{ + fIsServer = state; +} + +hsBool pyPlayer::IsServer() +{ + return fIsServer; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayer.h new file mode 100644 index 00000000..2a337549 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayer.h @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyPlayer_h +#define pyPlayer_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyPlayer +// +// PURPOSE: Class wrapper for Python to the player data +// + +#include "hsStlUtils.h" +#include "pyKey.h" + +#include +#include "pyGlueHelpers.h" + +class plKey; + +class pyPlayer +{ +protected: + plKey fAvatarKey; + std::string fPlayerName; + UInt32 fPlayerID; + hsScalar fDistSq; // from local player, temp + hsBool fIsCCR; + hsBool fIsServer; + + pyPlayer(); // only used by python glue, do NOT call + pyPlayer(pyKey& avKey, const char* pname, UInt32 pid, hsScalar distsq); + pyPlayer(plKey avKey, const char* pname, UInt32 pid, hsScalar distsq); + // another way to create a player with just a name and number + pyPlayer(const char* pname, UInt32 pid); +public: + void Init(plKey avKey, const char* pname, UInt32 pid, hsScalar distsq); // used by python glue, do NOT call + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptPlayer); + static PyObject *New(pyKey& avKey, const char* pname, UInt32 pid, hsScalar distsq); + static PyObject *New(plKey avKey, const char* pname, UInt32 pid, hsScalar distsq); + static PyObject *New(const char* pname, UInt32 pid); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyPlayer object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyPlayer); // converts a PyObject to a pyPlayer (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // override the equals to operator + hsBool operator==(const pyPlayer &player) const + { + // only thing that needs testing is the playerid, which is unique for all + if ( ((pyPlayer*)this)->GetPlayerID() == player.GetPlayerID() ) + return true; + else + return false; + } + hsBool operator!=(const pyPlayer &player) const { return !(player == *this); } + + // for C++ access + plKey GetKey() { return fAvatarKey; } + + // for python access + const char * GetPlayerName() const { return fPlayerName.c_str();} + UInt32 GetPlayerID() const + { + return fPlayerID; + } + + hsScalar GetDistSq() const { return fDistSq; } + + void SetCCRFlag(hsBool state); + hsBool IsCCR(); + + void SetServerFlag(hsBool state); + hsBool IsServer(); + +}; + +#endif // pyPlayer_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayerGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayerGlue.cpp new file mode 100644 index 00000000..b69d34c6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyPlayerGlue.cpp @@ -0,0 +1,213 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyPlayer.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptPlayer, pyPlayer); + +PYTHON_DEFAULT_NEW_DEFINITION(ptPlayer, pyPlayer) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptPlayer) + +PYTHON_INIT_DEFINITION(ptPlayer, args, keywords) +{ + // we have two sets of arguments we can use, hence the generic PyObject* pointers + // argument set 1: pyKey, string, UInt32, hsScalar + // argument set 2: string, UInt32 + PyObject* firstObj = NULL; // can be a pyKey or a string + PyObject* secondObj = NULL; // can be a string or a UInt32 + PyObject* thirdObj = NULL; // UInt32 + PyObject* fourthObj = NULL; // hsScalar + if (!PyArg_ParseTuple(args, "OO|OO", &firstObj, &secondObj, &thirdObj, &fourthObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects one of two argument lists: (ptKey, string, unsigned long, float) or (string, unsigned long)"); + PYTHON_RETURN_INIT_ERROR; + } + if (pyKey::Check(firstObj)) // arg set 1 + { + // make sure the remaining objects fit the arg list + if ((!thirdObj) || (!fourthObj)) + { + // missing arguments + PyErr_SetString(PyExc_TypeError, "__init__ expects one of two argument lists: (ptKey, string, unsigned long, float) or (string, unsigned long)"); + PYTHON_RETURN_INIT_ERROR; + } + if ((!PyString_Check(secondObj)) || (!PyLong_Check(thirdObj)) || (!PyFloat_Check(fourthObj))) + { + // incorrect types + PyErr_SetString(PyExc_TypeError, "__init__ expects one of two argument lists: (ptKey, string, unsigned long, float) or (string, unsigned long)"); + PYTHON_RETURN_INIT_ERROR; + } + // all args are correct, convert and init + pyKey* key = pyKey::ConvertFrom(firstObj); + char* name = PyString_AsString(secondObj); + unsigned long pid = PyLong_AsUnsignedLong(thirdObj); + float distsq = (float)PyFloat_AsDouble(fourthObj); + self->fThis->Init(key->getKey(), name, pid, distsq); + PYTHON_RETURN_INIT_OK; + } + else if (PyString_Check(firstObj)) // arg set 2 + { + // make sure there are only two args + if (thirdObj || fourthObj) + { + // too many arguments + PyErr_SetString(PyExc_TypeError, "__init__ expects one of two argument lists: (ptKey, string, unsigned long, float) or (string, unsigned long)"); + PYTHON_RETURN_INIT_ERROR; + } + char* name = PyString_AsString(firstObj); + unsigned long pid = 0; + if (PyLong_Check(secondObj)) + pid = PyLong_AsUnsignedLong(secondObj); + else if (PyInt_Check(secondObj)) + pid = (unsigned long)PyInt_AsLong(secondObj); + else + { + // incorrect type + PyErr_SetString(PyExc_TypeError, "__init__ expects one of two argument lists: (ptKey, string, unsigned long, float) or (string, unsigned long)"); + PYTHON_RETURN_INIT_ERROR; + } + self->fThis->Init(nil, name, pid, -1); + PYTHON_RETURN_INIT_OK; + } + // some other args came in + PyErr_SetString(PyExc_TypeError, "__init__ expects one of two argument lists: (ptKey, string, unsigned long, float) or (string, unsigned long)"); + PYTHON_RETURN_INIT_ERROR; +} + +PYTHON_RICH_COMPARE_DEFINITION(ptPlayer, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pyPlayer::Check(obj1) || !pyPlayer::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptPlayer object"); + PYTHON_RCOMPARE_ERROR; + } + } + pyPlayer *player1 = pyPlayer::ConvertFrom(obj1); + pyPlayer *player2 = pyPlayer::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*player1) == (*player2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*player1) != (*player2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptPlayer object"); + PYTHON_RCOMPARE_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptPlayer, getPlayerName) +{ + return PyString_FromString(self->fThis->GetPlayerName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptPlayer, getPlayerID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetPlayerID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptPlayer, getDistanceSq) +{ + return PyFloat_FromDouble(self->fThis->GetDistSq()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptPlayer, isCCR) +{ + PYTHON_RETURN_BOOL(self->fThis->IsCCR()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptPlayer, isServer) +{ + PYTHON_RETURN_BOOL(self->fThis->IsServer()); +} + +PYTHON_START_METHODS_TABLE(ptPlayer) + PYTHON_METHOD_NOARGS(ptPlayer, getPlayerName, "Returns the name of the player"), + PYTHON_METHOD_NOARGS(ptPlayer, getPlayerID, "Returns the unique player ID"), + PYTHON_METHOD_NOARGS(ptPlayer, getDistanceSq, "Returns the distance to remote player from local player"), + PYTHON_METHOD_NOARGS(ptPlayer, isCCR, "Is this player a CCR?"), + PYTHON_METHOD_NOARGS(ptPlayer, isServer, "Is this player a server?"), +PYTHON_END_METHODS_TABLE; + +// type structure definition +#define ptPlayer_COMPARE PYTHON_NO_COMPARE +#define ptPlayer_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptPlayer_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptPlayer_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptPlayer_STR PYTHON_NO_STR +#define ptPlayer_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptPlayer) +#define ptPlayer_GETSET PYTHON_NO_GETSET +#define ptPlayer_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptPlayer, "Params: avkey,name,playerID,distanceSq\nAnd optionally __init__(name,playerID)"); + +// required functions for PyObject interoperability +PyObject *pyPlayer::New(pyKey& avKey, const char* pname, UInt32 pid, hsScalar distsq) +{ + ptPlayer *newObj = (ptPlayer*)ptPlayer_type.tp_new(&ptPlayer_type, NULL, NULL); + newObj->fThis->Init(avKey.getKey(), pname, pid, distsq); + return (PyObject*)newObj; +} + +PyObject *pyPlayer::New(plKey avKey, const char* pname, UInt32 pid, hsScalar distsq) +{ + ptPlayer *newObj = (ptPlayer*)ptPlayer_type.tp_new(&ptPlayer_type, NULL, NULL); + newObj->fThis->Init(avKey, pname, pid, distsq); + return (PyObject*)newObj; +} + +PyObject *pyPlayer::New(const char* pname, UInt32 pid) +{ + ptPlayer *newObj = (ptPlayer*)ptPlayer_type.tp_new(&ptPlayer_type, NULL, NULL); + newObj->fThis->Init(nil, pname, pid, -1); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptPlayer, pyPlayer) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptPlayer, pyPlayer) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyPlayer::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptPlayer); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDL.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDL.cpp new file mode 100644 index 00000000..2dea7305 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDL.cpp @@ -0,0 +1,267 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "pySDL.h" +#include "../plSDL/plSDL.h" + +/////////////////////////////////////////////////////////////////////////// + +pySDLStateDataRecord::pySDLStateDataRecord() +: fRec( nil ) +{ +} + +pySDLStateDataRecord::pySDLStateDataRecord( plStateDataRecord * rec ) +: fRec( rec ) +{ +} + +pySDLStateDataRecord::~pySDLStateDataRecord() { + DEL(fRec); +} + +plStateDataRecord * pySDLStateDataRecord::GetRec() const +{ + return fRec; +} + +PyObject * pySDLStateDataRecord::FindVar( const char * name ) const +{ + if ( !fRec ) + PYTHON_RETURN_NONE; + + plSimpleStateVariable * var = fRec->FindVar( name ); + if ( !var ) + PYTHON_RETURN_NONE; + + return pySimpleStateVariable::New( var ); +} + +const char *pySDLStateDataRecord::GetName() const +{ + if (!fRec) + return nil; + const plStateDescriptor *stateDesc = fRec->GetDescriptor(); + return stateDesc->GetName(); +} + +std::vector pySDLStateDataRecord::GetVarList() +{ + std::vector retVal; + if (!fRec) + return retVal; + const plStateDescriptor *stateDesc = fRec->GetDescriptor(); + int numVars = stateDesc->GetNumVars(); + for (int i=0; iGetVar(i); + retVal.push_back(varDesc->GetName()); + } + return retVal; +} + +void pySDLStateDataRecord::SetFromDefaults(bool timeStampNow) +{ + if (fRec) + { + fRec->SetFromDefaults(timeStampNow); + } +} + + +/////////////////////////////////////////////////////////////////////////// + +pySimpleStateVariable::pySimpleStateVariable() +: fVar( nil ) +{ +} + +pySimpleStateVariable::pySimpleStateVariable( plSimpleStateVariable * var ) +: fVar( var ) +{ +} + +plSimpleStateVariable * pySimpleStateVariable::GetVar() const +{ + return fVar; +} + +bool pySimpleStateVariable::SetByte( byte v, int idx ) +{ + if ( !fVar ) + return false; + return fVar->Set( v, idx ); +} + +bool pySimpleStateVariable::SetShort( short v, int idx ) +{ + if ( !fVar ) + return false; + return fVar->Set( v, idx ); +} + +bool pySimpleStateVariable::SetFloat( float v, int idx ) +{ + if ( !fVar ) + return false; + return fVar->Set( v, idx ); +} + +bool pySimpleStateVariable::SetDouble( double v, int idx ) +{ + if ( !fVar ) + return false; + return fVar->Set( v, idx ); +} + +bool pySimpleStateVariable::SetInt( int v, int idx ) +{ + if ( !fVar ) + return false; + return fVar->Set( v, idx ); +} + +bool pySimpleStateVariable::SetString( const char * v, int idx ) +{ + if ( !fVar ) + return false; + return fVar->Set( v, idx ); +} + +bool pySimpleStateVariable::SetBool( bool v, int idx ) +{ + if ( !fVar ) + return false; + return fVar->Set( v, idx ); +} + +byte pySimpleStateVariable::GetByte( int idx ) const +{ + byte v = 0; + if ( fVar ) + fVar->Get( &v, idx ); + return v; +} + +short pySimpleStateVariable::GetShort( int idx ) const +{ + short v = 0; + if ( fVar ) + fVar->Get( &v, idx ); + return v; +} + +int pySimpleStateVariable::GetInt( int idx ) const +{ + int v = 0; + if ( fVar ) + fVar->Get( &v, idx ); + return v; +} + +float pySimpleStateVariable::GetFloat( int idx ) const +{ + float v = 0.f; + if ( fVar ) + fVar->Get( &v, idx ); + return v; +} + +double pySimpleStateVariable::GetDouble( int idx ) const +{ + double v = 0.0; + if ( fVar ) + fVar->Get( &v, idx ); + return v; +} + +bool pySimpleStateVariable::GetBool( int idx ) const +{ + bool v = false; + if ( fVar ) + fVar->Get( &v, idx ); + return v; +} + +const char * pySimpleStateVariable::GetString( int idx ) const +{ + fString = ""; + if ( fVar ) + { + char v[256]; + if ( fVar->Get( v, idx ) ) + fString = v; + } + return fString.c_str(); +} + +plKey pySimpleStateVariable::GetKey( int idx ) const +{ + plKey theKey = nil; + if (fVar) + fVar->Get(&theKey, idx); + return theKey; +} + +int pySimpleStateVariable::GetType() const +{ + if (!fVar) + return plVarDescriptor::kNone; + plVarDescriptor *varDesc = fVar->GetVarDescriptor(); + return varDesc->GetType(); +} + +const char *pySimpleStateVariable::GetDisplayOptions() const +{ + if (!fVar) + return nil; + plVarDescriptor *varDesc = fVar->GetVarDescriptor(); + return varDesc->GetDisplayOptions(); +} + +const char *pySimpleStateVariable::GetDefault() const +{ + if (!fVar) + return nil; + plVarDescriptor *varDesc = fVar->GetVarDescriptor(); + return varDesc->GetDefault(); +} + +bool pySimpleStateVariable::IsInternal() const +{ + if (!fVar) + return nil; + plVarDescriptor *varDesc = fVar->GetVarDescriptor(); + return varDesc->IsInternal(); +} + +bool pySimpleStateVariable::IsAlwaysNew() const +{ + if (!fVar) + return nil; + plVarDescriptor *varDesc = fVar->GetVarDescriptor(); + return varDesc->IsAlwaysNew(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDL.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDL.h new file mode 100644 index 00000000..6c1e018d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDL.h @@ -0,0 +1,135 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pySDL_h_ +#define _pySDL_h_ + +////////////////////////////////////////////////////////////////////// +// +// pySDL - python subset of the plSDL library. +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +class plStateDataRecord; +class plSimpleStateVariable; +class pySimpleStateVariable; +class plKey; + +// pySDL -- this thing really only exists for the constants +class pySDL +{ +public: + static void AddPlasmaConstantsClasses(PyObject *m); +}; + + +// pySDLStateDataRecord +class pySDLStateDataRecord +{ +private: + plStateDataRecord * fRec; + +protected: + pySDLStateDataRecord(); + pySDLStateDataRecord( plStateDataRecord * rec ); + +public: + ~pySDLStateDataRecord(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptSDLStateDataRecord); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(plStateDataRecord* rec); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pySDLStateDataRecord object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pySDLStateDataRecord); // converts a PyObject to a pySDLStateDataRecord (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + plStateDataRecord * GetRec() const; + + ///////////////////// + PyObject * FindVar( const char * name ) const; // returns pySimpleStateVariable + const char *GetName() const; + std::vector GetVarList(); + void SetFromDefaults(bool timeStampNow); +}; + +typedef unsigned char byte; +// pySimpleStateVariable +class pySimpleStateVariable +{ +private: + plSimpleStateVariable * fVar; + mutable std::string fString; // for GetString() + +protected: + pySimpleStateVariable(); + pySimpleStateVariable( plSimpleStateVariable * var ); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptSimpleStateVariable); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(plSimpleStateVariable* var); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pySimpleStateVariable object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pySimpleStateVariable); // converts a PyObject to a pySimpleStateVariable (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + plSimpleStateVariable * GetVar() const; + + ///////////////////// + bool SetByte( byte v, int idx=0 ); + bool SetShort( short v, int idx=0 ); + bool SetFloat( float v, int idx=0 ); + bool SetDouble( double v, int idx=0 ); + bool SetInt( int v, int idx=0 ); + bool SetString( const char * v, int idx=0 ); + bool SetBool(bool v, int idx=0 ); + byte GetByte( int idx=0 ) const; + short GetShort( int idx=0 ) const; + int GetInt( int idx=0 ) const; + float GetFloat( int idx=0 ) const; + double GetDouble( int idx=0 ) const; + bool GetBool( int idx=0 ) const; + const char * GetString( int idx=0 ) const; + plKey GetKey( int idx=0 ) const; + + int GetType() const; + const char *GetDisplayOptions() const; + const char *GetDefault() const; + bool IsAlwaysNew() const; + bool IsInternal() const; + +}; + + +#endif // _pySDL_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDLGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDLGlue.cpp new file mode 100644 index 00000000..a5c94f28 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySDLGlue.cpp @@ -0,0 +1,297 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pySDL.h" +#include "..\plSDL\plSDL.h" +#include "pyEnum.h" +#include "pyKey.h" + +#include + +void pySDL::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtSDLReadWriteOptions); + PYTHON_ENUM_ELEMENT(PtSDLReadWriteOptions, kDirtyOnly, plSDL::kDirtyOnly); + PYTHON_ENUM_ELEMENT(PtSDLReadWriteOptions, kSkipNotificationInfo, plSDL::kSkipNotificationInfo); + PYTHON_ENUM_ELEMENT(PtSDLReadWriteOptions, kBroadcast, plSDL::kBroadcast); + //PYTHON_ENUM_ELEMENT(PtSDLReadWriteOptions, kWriteTimeStamps, plSDL::kWriteTimeStamps); + PYTHON_ENUM_ELEMENT(PtSDLReadWriteOptions, kTimeStampOnRead, plSDL::kTimeStampOnRead); + //PYTHON_ENUM_ELEMENT(PtSDLReadWriteOptions, kTimeStampDirtyOnRead, plSDL::kTimeStampDirtyOnRead); + PYTHON_ENUM_END(m, PtSDLReadWriteOptions); + + PYTHON_ENUM_START(PtSDLVarType); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kNone, plVarDescriptor::kNone); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kInt, plVarDescriptor::kInt); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kFloat, plVarDescriptor::kFloat); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kBool, plVarDescriptor::kBool); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kString32, plVarDescriptor::kString32); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kKey, plVarDescriptor::kKey); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kStateDescriptor, plVarDescriptor::kStateDescriptor); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kCreatable, plVarDescriptor::kCreatable); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kDouble, plVarDescriptor::kDouble); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kTime, plVarDescriptor::kTime); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kVector3, plVarDescriptor::kVector3); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kPoint3, plVarDescriptor::kPoint3); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kRGB, plVarDescriptor::kRGB); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kRGBA, plVarDescriptor::kRGBA); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kQuaternion, plVarDescriptor::kQuaternion); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kByte, plVarDescriptor::kByte); + PYTHON_ENUM_ELEMENT(PtSDLVarType, kShort, plVarDescriptor::kShort); + PYTHON_ENUM_END(m, PtSDLVarType); +} + +// glue functions +PYTHON_CLASS_DEFINITION(ptSDLStateDataRecord, pySDLStateDataRecord); + +PYTHON_DEFAULT_NEW_DEFINITION(ptSDLStateDataRecord, pySDLStateDataRecord) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptSDLStateDataRecord) + +PYTHON_INIT_DEFINITION(ptSDLStateDataRecord, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptSDLStateDataRecord, findVar, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "findVar expects a string"); + PYTHON_RETURN_ERROR; + } + return self->fThis->FindVar(name); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSDLStateDataRecord, getName) +{ + return PyString_FromString(self->fThis->GetName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSDLStateDataRecord, getVarList) +{ + std::vector vars = self->fThis->GetVarList(); + PyObject* varList = PyList_New(vars.size()); + for (int i = 0; i < vars.size(); i++) + PyList_SetItem(varList, i, PyString_FromString(vars[i].c_str())); + return varList; +} + +PYTHON_METHOD_DEFINITION(ptSDLStateDataRecord, setFromDefaults, args) +{ + char timeStampNow; + if (!PyArg_ParseTuple(args, "b", &timeStampNow)) + { + PyErr_SetString(PyExc_TypeError, "setFromDefaults expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetFromDefaults(timeStampNow != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptSDLStateDataRecord) + PYTHON_METHOD(ptSDLStateDataRecord, findVar, "Params: name\nFinds and returns the specified ptSimpleStateVariable"), + PYTHON_METHOD_NOARGS(ptSDLStateDataRecord, getName, "Returns our record's name"), + PYTHON_METHOD_NOARGS(ptSDLStateDataRecord, getVarList, "Returns the names of the vars we hold as a list of strings"), + PYTHON_METHOD(ptSDLStateDataRecord, setFromDefaults, "Params: timeStampNow\nSets all our vars to their defaults"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptSDLStateDataRecord, "Basic SDL state data record class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptSDLStateDataRecord, pySDLStateDataRecord) + +PyObject *pySDLStateDataRecord::New(plStateDataRecord* rec) +{ + ptSDLStateDataRecord* newObj = (ptSDLStateDataRecord*)ptSDLStateDataRecord_type.tp_new(&ptSDLStateDataRecord_type, NULL, NULL); + newObj->fThis->fRec = rec; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptSDLStateDataRecord, pySDLStateDataRecord) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptSDLStateDataRecord, pySDLStateDataRecord) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pySDLStateDataRecord::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptSDLStateDataRecord); + PYTHON_CLASS_IMPORT_END(m); +} + +// glue functions +PYTHON_CLASS_DEFINITION(ptSimpleStateVariable, pySimpleStateVariable); + +PYTHON_DEFAULT_NEW_DEFINITION(ptSimpleStateVariable, pySimpleStateVariable) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptSimpleStateVariable) + +PYTHON_INIT_DEFINITION(ptSimpleStateVariable, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +// two little macros to make things a little easier +#define STATEVAR_SET(funcName, cFuncName, typeName, cTypeName, pythonTypeChar) \ +PYTHON_METHOD_DEFINITION(ptSimpleStateVariable, funcName, args) \ +{ \ + cTypeName val; \ + int idx = 0; \ + if (!PyArg_ParseTuple(args, #pythonTypeChar "|i", &val, &idx)) \ + { \ + PyErr_SetString(PyExc_TypeError, #funcName " expects a " #typeName " and an optional int"); \ + PYTHON_RETURN_ERROR; \ + } \ + PYTHON_RETURN_BOOL(self->fThis->cFuncName(val, idx)); \ +} + +#define STATEVAR_GET(funcName, cFuncName, pythonReturnFunc) \ +PYTHON_METHOD_DEFINITION(ptSimpleStateVariable, funcName, args) \ +{ \ + int idx = 0; \ + if (!PyArg_ParseTuple(args, "|i", &idx)) \ + { \ + PyErr_SetString(PyExc_TypeError, #funcName " expects an optional int"); \ + PYTHON_RETURN_ERROR; \ + } \ + return pythonReturnFunc(self->fThis->cFuncName(idx)); \ +} + +STATEVAR_SET(setByte, SetByte, byte, char, b) +STATEVAR_SET(setShort, SetShort, short, short, h) +STATEVAR_SET(setFloat, SetFloat, float, float, f) +STATEVAR_SET(setDouble, SetDouble, double, double, d) +STATEVAR_SET(setInt, SetInt, int, int, i) +STATEVAR_SET(setString, SetString, string, char*, s) + +// setBool is special cause of the way python represents booleans +PYTHON_METHOD_DEFINITION(ptSimpleStateVariable, setBool, args) +{ + char val; + int idx = 0; + if (!PyArg_ParseTuple(args, "b|i", &val, &idx)) + { + PyErr_SetString(PyExc_TypeError, "setBool expects a boolean and an optional int"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->SetBool(val != 0, idx)); +} + +STATEVAR_GET(getByte, GetByte, PyInt_FromLong) +STATEVAR_GET(getShort, GetShort, PyInt_FromLong) +STATEVAR_GET(getInt, GetInt, PyInt_FromLong) +STATEVAR_GET(getFloat, GetFloat, PyFloat_FromDouble) +STATEVAR_GET(getDouble, GetDouble, PyFloat_FromDouble) +STATEVAR_GET(getString, GetString, PyString_FromString) +STATEVAR_GET(getKey, GetKey, pyKey::New) + +// getBool is special cause of the way python represents booleans +PYTHON_METHOD_DEFINITION(ptSimpleStateVariable, getBool, args) +{ + int idx = 0; + if (!PyArg_ParseTuple(args, "|i", &idx)) + { + PyErr_SetString(PyExc_TypeError, "getBool expects an optional int"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->GetBool(idx)); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSimpleStateVariable, getType) +{ + return PyInt_FromLong(self->fThis->GetType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSimpleStateVariable, getDisplayOptions) +{ + return PyString_FromString(self->fThis->GetDisplayOptions()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSimpleStateVariable, getDefault) +{ + return PyString_FromString(self->fThis->GetDefault()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSimpleStateVariable, isAlwaysNew) +{ + PYTHON_RETURN_BOOL(self->fThis->IsAlwaysNew()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSimpleStateVariable, isInternal) +{ + PYTHON_RETURN_BOOL(self->fThis->IsInternal()); +} + +PYTHON_START_METHODS_TABLE(ptSimpleStateVariable) + PYTHON_METHOD(ptSimpleStateVariable, setByte, "Params: val,idx=0\nSets a byte variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, setShort, "Params: val,idx=0\nSets a short variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, setFloat, "Params: val,idx=0\nSets a float variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, setDouble, "Params: val,idx=0\nSets a double variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, setInt, "Params: val,idx=0\nSets an int variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, setString, "Params: val,idx=0\nSets a string variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, setBool, "Params: val,idx=0\nSets a boolean variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, getByte, "Params: idx=0\nReturns a byte variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, getShort, "Params: idx=0\nReturns a short variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, getInt, "Params: idx=0\nReturns an int variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, getFloat, "Params: idx=0\nReturns a float variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, getDouble, "Params: idx=0\nReturns a double variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, getString, "Params: idx=0\nReturns a string variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, getKey, "Params: idx=0\nReturns a plKey variable's value"), + PYTHON_METHOD(ptSimpleStateVariable, getBool, "Params: idx=0\nReturns a boolean variable's value"), + PYTHON_METHOD_NOARGS(ptSimpleStateVariable, getType, "Returns the variable's type"), + PYTHON_METHOD_NOARGS(ptSimpleStateVariable, getDisplayOptions, "Returns the variable's display options"), + PYTHON_METHOD_NOARGS(ptSimpleStateVariable, getDefault, "Returns the variable's default"), + PYTHON_METHOD_NOARGS(ptSimpleStateVariable, isAlwaysNew, "Is this variable always new?"), + PYTHON_METHOD_NOARGS(ptSimpleStateVariable, isInternal, "Is this an internal variable?"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptSimpleStateVariable, "Basic SDL state data record class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptSimpleStateVariable, pySimpleStateVariable) + +PyObject *pySimpleStateVariable::New(plSimpleStateVariable* var) +{ + ptSimpleStateVariable* newObj = (ptSimpleStateVariable*)ptSimpleStateVariable_type.tp_new(&ptSimpleStateVariable_type, NULL, NULL); + newObj->fThis->fVar = var; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptSimpleStateVariable, pySimpleStateVariable) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptSimpleStateVariable, pySimpleStateVariable) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pySimpleStateVariable::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptSimpleStateVariable); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp new file mode 100644 index 00000000..858258dc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObject.cpp @@ -0,0 +1,979 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" // TEMP, for STL warnings +#include "pySceneObject.h" + +#include "../pnKeyedObject/plKey.h" +#include "cyAvatar.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plResMgr/plResManager.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pfCamera/plCameraModifier.h" +#include "../plAvatar/plArmatureMod.h" +#include "plPhysical.h" +#include "../plModifier/plResponderModifier.h" +#include "../plModifier/plLogicModifier.h" +#include "../pfPython/plPythonFileMod.h" + +#include "pyMatrix44.h" +#include "pyKey.h" +#include "plgDispatch.h" + +void pySceneObject::IAddObjKeyToAll(plKey key) +{ + // set the sender and the receiver to the same thing + cyDraw::ConvertFrom(fDraw)->AddRecvr(key); + cyPhysics::ConvertFrom(fPhysics)->AddRecvr(key); + cyAvatar::ConvertFrom(fAvatar)->AddRecvr(key); + cyParticleSys::ConvertFrom(fParticle)->AddRecvr(key); +} + +void pySceneObject::ISetAllSenderKeys() +{ + // set the sender and the receiver to the same thing + cyDraw::ConvertFrom(fDraw)->SetSender(fSenderKey); + cyPhysics::ConvertFrom(fPhysics)->SetSender(fSenderKey); + cyAvatar::ConvertFrom(fAvatar)->SetSender(fSenderKey); + cyParticleSys::ConvertFrom(fParticle)->SetSender(fSenderKey); +} + +pySceneObject::pySceneObject() +{ + // make sure these are created + fDraw = cyDraw::New(); + fPhysics = cyPhysics::New(); + fAvatar = cyAvatar::New(); + fParticle = cyParticleSys::New(); + fNetForce = false; +} + +pySceneObject::pySceneObject(pyKey& objkey, pyKey& selfkey) +{ + // make sure these are created + fDraw = cyDraw::New(); + fPhysics = cyPhysics::New(); + fAvatar = cyAvatar::New(); + fParticle = cyParticleSys::New(); + + addObjKey(objkey.getKey()); + setSenderKey(selfkey.getKey()); + setPyMod(selfkey); + fNetForce = false; +} + +pySceneObject::pySceneObject(plKey objkey,pyKey& selfkey) +{ + // make sure these are created + fDraw = cyDraw::New(); + fPhysics = cyPhysics::New(); + fAvatar = cyAvatar::New(); + fParticle = cyParticleSys::New(); + + addObjKey(objkey); + setSenderKey(selfkey.getKey()); + setPyMod(selfkey); + fNetForce = false; +} + + +pySceneObject::pySceneObject(plKey objkey) +{ + // make sure these are created + fDraw = cyDraw::New(); + fPhysics = cyPhysics::New(); + fAvatar = cyAvatar::New(); + fParticle = cyParticleSys::New(); + + addObjKey(objkey); + setSenderKey(objkey); + fNetForce = false; +} + + +hsBool pySceneObject::operator==(const pySceneObject &sobj) const +{ + plKey ours = ((pySceneObject*)this)->getObjKey(); + plKey theirs = ((pySceneObject&)sobj).getObjKey(); + if ( ours == nil && theirs == nil ) + return true; + else if ( ours != nil && theirs != nil ) + return (ours->GetUoid()==theirs->GetUoid()); + else + return false; +} + +// getter and setters +void pySceneObject::addObjKey(plKey key) +{ + if ( key != nil ) + { + fSceneObjects.Append(key); + IAddObjKeyToAll(key); + } +} + +void pySceneObject::addObjPyKey(pyKey& objkey) +{ + if ( objkey.getKey() != nil ) + { + fSceneObjects.Append(objkey.getKey()); + IAddObjKeyToAll(objkey.getKey()); + } +} + +plKey pySceneObject::getObjKey() +{ + if ( fSceneObjects.Count() > 0 ) + return fSceneObjects[0]; + else + return nil; +} + +PyObject* pySceneObject::getObjPyKey() +{ + PyObject* pyobj; // Python will manage this... it only knows when everyone is done with it + if ( fSceneObjects.Count() > 0 ) + pyobj = pyKey::New(fSceneObjects[0]); + else + { + Py_INCREF(Py_None); + pyobj = Py_None; + } + return pyobj; +} + +void pySceneObject::setSenderKey(plKey key) +{ + fSenderKey=key; + ISetAllSenderKeys(); +} + +void pySceneObject::setPyMod(pyKey& pymod) +{ + fPyMod = pymod.getKey(); +} + +void pySceneObject::setPyMod(plKey& key) +{ + fPyMod = key; +} + +void pySceneObject::SetNetForce(hsBool state) +{ + // set our flag + fNetForce = state; + // then set the netForce flag in the subs + cyDraw::ConvertFrom(fDraw)->SetNetForce(state); + cyPhysics::ConvertFrom(fPhysics)->SetNetForce(state); + cyAvatar::ConvertFrom(fAvatar)->SetNetForce(state); + cyParticleSys::ConvertFrom(fParticle)->SetNetForce(state); +} + + +const char* pySceneObject::GetName() +{ + if ( fSceneObjects.Count() > 0 ) + return fSceneObjects[0]->GetName(); + return ""; +} + +PyObject* pySceneObject::findObj(const char* name) +{ + PyObject* pSobj = nil; + // search through the plKeys that we have looking for this name + int i; + for ( i=0; iGetName()) ) + { + pSobj = pySceneObject::New(fSceneObjects[i],fPyMod); + break; + } + } + + // did we find one? if not make an object with nil object + if ( pSobj == nil ) + { + // throw a Python error, so the coder knows it didn't work + PyErr_SetString(PyExc_KeyError, name); + } + + return pSobj; +} + + +// +// deteremine if this object (or the first object in the list) +// ...is locally owned +hsBool pySceneObject::IsLocallyOwned() +{ + // make sure that there are sceneobjects + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSynchedObject* obj = plSynchedObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj && obj->IsLocallyOwned() == plSynchedObject::kYes ) + return true; + else + // both No and Maybe answers will be assumed to be "not locally ownded" + return false; + } + else + // if we couldn't find any sceneobject, then there is no way that it could be local... heh + return false; +} + + +// +// get the local to world matrix +PyObject* pySceneObject::GetLocalToWorld() +{ + // make sure that there are sceneobjects + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plCoordinateInterface* ci = obj->GetCoordinateInterface(); + if ( ci ) + return pyMatrix44::New((hsMatrix44)ci->GetLocalToWorld()); + else + { + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s does not have a coordinate interface.",obj->GetKeyName()); + PyErr_SetString(PyExc_RuntimeError, errmsg); + return nil; // return nil to tell python we errored + } + } + } + // if we couldn't find any sceneobject or a coordinate interface + return pyMatrix44::New(); +} + +// +// get the local to world matrix +PyObject* pySceneObject::GetWorldToLocal() +{ + // make sure that there are sceneobjects + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plCoordinateInterface* ci = obj->GetCoordinateInterface(); + if ( ci ) + return pyMatrix44::New((hsMatrix44)ci->GetWorldToLocal()); + else + { + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s does not have a coordinate interface.",obj->GetKeyName()); + PyErr_SetString(PyExc_RuntimeError, errmsg); + return nil; // return nil to tell python we errored + } + } + } + // if we couldn't find any sceneobject or a coordinate interface + return pyMatrix44::New(); +} + +// +// get the local to world matrix +PyObject* pySceneObject::GetLocalToParent() +{ + // make sure that there are sceneobjects + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plCoordinateInterface* ci = obj->GetCoordinateInterface(); + if ( ci ) + return pyMatrix44::New((hsMatrix44)ci->GetLocalToParent()); + else + { + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s does not have a coordinate interface.",obj->GetKeyName()); + PyErr_SetString(PyExc_RuntimeError, errmsg); + return nil; // return nil to tell python we errored + } + } + } + // if we couldn't find any sceneobject or a coordinate interface + return pyMatrix44::New(); +} + +// +// get the local to world matrix +PyObject* pySceneObject::GetParentToLocal() +{ + // make sure that there are sceneobjects + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plCoordinateInterface* ci = obj->GetCoordinateInterface(); + if ( ci ) + return pyMatrix44::New((hsMatrix44)ci->GetParentToLocal()); + else + { + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s does not have a coordinate interface.",obj->GetKeyName()); + PyErr_SetString(PyExc_RuntimeError, errmsg); + return nil; // return nil to tell python we errored + } + } + } + // if we couldn't find any sceneobject or a coordinate interface + return pyMatrix44::New(); +} + +// +// get the local to world matrix +void pySceneObject::SetTransform(pyMatrix44& l2w, pyMatrix44& w2l) +{ + // make sure that there are sceneobjects + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + obj->SetTransform(l2w.fMatrix,w2l.fMatrix); + } +} + +// +// find the position of this object (if there are more than one, just the first one) +PyObject* pySceneObject::GetWorldPosition() +{ + // make sure that there are sceneobjects + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plCoordinateInterface* ci = obj->GetCoordinateInterface(); + if ( ci ) + return pyPoint3::New((hsPoint3)ci->GetWorldPos()); + else + { + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s does not have a coordinate interface.",obj->GetKeyName()); + PyErr_SetString(PyExc_RuntimeError, errmsg); + return nil; // return nil to tell python we errored + } + } + } + // if we couldn't find any sceneobject or a coordinate interface + return pyPoint3::New(hsPoint3(0,0,0)); +} + +// +// find the view vector for this object (if there are more than one, just the first one) +PyObject* pySceneObject::GetViewVector() +{ + // make sure that there are sceneobjects + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plCoordinateInterface* ci = obj->GetCoordinateInterface(); + if ( ci ) + return pyVector3::New(ci->GetLocalToWorld().GetAxis(hsMatrix44::kView)); + else + { + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s does not have a coordinate interface.",obj->GetKeyName()); + PyErr_SetString(PyExc_RuntimeError, errmsg); + return nil; // return nil to tell python we errored + } + } + } + // if we couldn't find any sceneobject or a coordinate interface + return pyVector3::New(hsVector3(0,0,0)); +} + +// +// find the up vector for this object (if there are more than one, just the first one) +PyObject* pySceneObject::GetUpVector() +{ + // make sure that there are sceneobjects + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plCoordinateInterface* ci = obj->GetCoordinateInterface(); + if ( ci ) + return pyVector3::New(ci->GetLocalToWorld().GetAxis(hsMatrix44::kUp)); + else + { + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s does not have a coordinate interface.",obj->GetKeyName()); + PyErr_SetString(PyExc_RuntimeError, errmsg); + return nil; // return nil to tell python we errored + } + } + } + // if we couldn't find any sceneobject or a coordinate interface + return pyVector3::New(hsVector3(0,0,0)); +} + +// +// find the up vector for this object (if there are more than one, just the first one) +PyObject* pySceneObject::GetRightVector() +{ + // make sure that there are sceneobjects + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plCoordinateInterface* ci = obj->GetCoordinateInterface(); + if ( ci ) + return pyVector3::New(ci->GetLocalToWorld().GetAxis(hsMatrix44::kRight)); + else + { + char errmsg[256]; + sprintf(errmsg,"Sceneobject %s does not have a coordinate interface.",obj->GetKeyName()); + PyErr_SetString(PyExc_RuntimeError, errmsg); + return nil; // return nil to tell python we errored + } + } + } + // if we couldn't find any sceneobject or a coordinate interface + return pyVector3::New(hsVector3(0,0,0)); +} + +// +// deteremine if this object (or any of the object attached) +// ...is an avatar, of any type +hsBool pySceneObject::IsAvatar() +{ + // loop through all the sceneobject... looking for avatar modifiers + int j; + for ( j=0 ; jObjectIsLoaded()); + if ( obj ) + { + // search through its modifiers to see if one of them is an avatar modifier + int i; + for ( i=0; iGetNumModifiers(); i++ ) + { + const plModifier* mod = obj->GetModifier(i); + // see if it is an avatar mod base class + const plArmatureMod* avatar = plArmatureMod::ConvertNoRef(mod); + if ( avatar ) + return true; + } + } + } + // if we couldn't find any sceneobject that had an avatar mod then this ain't an avatar + return false; +} + +#include "../plAvatar/plAvCallbackAction.h" + +PyObject* pySceneObject::GetAvatarVelocity() +{ + // loop through all the sceneobject... looking for avatar modifiers + int j; + for ( j=0 ; jObjectIsLoaded()); + if ( obj ) + { + // search through its modifiers to see if one of them is an avatar modifier + int i; + for ( i=0; iGetNumModifiers(); i++ ) + { + const plModifier* mod = obj->GetModifier(i); + // see if it is an avatar mod base class + const plArmatureMod* avatar = plArmatureMod::ConvertNoRef(mod); + if ( avatar && avatar->GetController() ) + { + hsVector3 vel = avatar->GetController()->GetLinearVelocity(); + return pyVector3::New(vel); + } + } + } + } + + // if we couldn't find any sceneobject that had an avatar mod then this ain't an avatar + return pyVector3::New(hsVector3(0,0,0)); +} + + +// +// deteremine if this object (or the first object in the list) +// ...is a human avatar +hsBool pySceneObject::IsHumanAvatar() +{ + // loop through all the sceneobject... looking for avatar modifiers + int j; + for ( j=0 ; jObjectIsLoaded()); + if ( obj ) + { + // search through its modifiers to see if one of them is an avatar modifier + int i; + for ( i=0; iGetNumModifiers(); i++ ) + { + const plModifier* mod = obj->GetModifier(i); + // see if it is an avatar mod base class + plArmatureMod* avatar = (plArmatureMod*)plArmatureMod::ConvertNoRef(mod); + if ( avatar ) + { + plArmatureBrain* brain = avatar->GetCurrentBrain(); + plAvBrainHuman* human = plAvBrainHuman::ConvertNoRef(brain); + if ( human ) + return true; + } + } + } + } + // if we couldn't find any sceneobject that had an avatar mod then this ain't an avatar + return false; +} + +// switch to / from this object (assuming that it is actually a camera) +void pySceneObject::PushCutsceneCamera(hsBool cut, pyKey& avKey) +{ + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + for (int i = 0; i < obj->GetNumModifiers(); i++) + { + const plCameraModifier1* pCam = plCameraModifier1::ConvertNoRef(obj->GetModifier(i)); + if (pCam) + { + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetSender(pCam->GetKey()); + pMsg->SetBCastFlag(plMessage::kBCastByType); + // set command to do the transition + if (cut) + pMsg->SetCmd(plCameraMsg::kPythonOverridePushCut); + else + pMsg->SetCmd(plCameraMsg::kPythonOverridePush); + // set the new camera + pMsg->SetNewCam(pCam->GetKey()); + pMsg->SetTriggerer(avKey.getKey()); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + return; + } + } + } + } +} + +void pySceneObject::PopCutsceneCamera(pyKey& avKey) +{ + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + for (int i = 0; i < obj->GetNumModifiers(); i++) + { + const plCameraModifier1* pCam = plCameraModifier1::ConvertNoRef(obj->GetModifier(i)); + if (pCam) + { + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetSender(pCam->GetKey()); + pMsg->SetBCastFlag(plMessage::kBCastByType); + // set command to do the transition + pMsg->SetCmd(plCameraMsg::kPythonOverridePop); + // set the new camera + pMsg->SetTriggerer(avKey.getKey()); + pMsg->SetNewCam(pCam->GetKey()); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + return; + } + } + } + } + +} + +void pySceneObject::PushCamera(pyKey& avKey) +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + for (int i = 0; i < obj->GetNumModifiers(); i++) + { + const plCameraModifier1* pCam = plCameraModifier1::ConvertNoRef(obj->GetModifier(i)); + if (pCam) + { + + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetSender(pCam->GetKey()); + pMsg->SetBCastFlag(plMessage::kBCastByType); + + // set command to do the transition + pMsg->SetCmd(plCameraMsg::kResponderTrigger); + pMsg->SetCmd(plCameraMsg::kRegionPushCamera); + // set the new camera + pMsg->SetTriggerer(avKey.getKey()); + pMsg->SetNewCam(pCam->GetKey()); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + } + } +} + +void pySceneObject::PushCameraCut(pyKey& avKey) +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + for (int i = 0; i < obj->GetNumModifiers(); i++) + { + const plCameraModifier1* pCam = plCameraModifier1::ConvertNoRef(obj->GetModifier(i)); + if (pCam) + { + + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetSender(pCam->GetKey()); + pMsg->SetBCastFlag(plMessage::kBCastByType); + + // set command to do the transition + pMsg->SetCmd(plCameraMsg::kResponderTrigger); + pMsg->SetCmd(plCameraMsg::kRegionPushCamera); + pMsg->SetCmd(plCameraMsg::kCut); + // set the new camera + pMsg->SetTriggerer(avKey.getKey()); + pMsg->SetNewCam(pCam->GetKey()); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + } + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Pop +// PARAMETERS : +// +// PURPOSE : Restore the state of the virtual camera with a previously saved setting +// +void pySceneObject::PopCamera(pyKey& avKey) +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + for (int i = 0; i < obj->GetNumModifiers(); i++) + { + const plCameraModifier1* pCam = plCameraModifier1::ConvertNoRef(obj->GetModifier(i)); + if (pCam) + { + + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetSender(pCam->GetKey()); + pMsg->SetBCastFlag(plMessage::kBCastByType); + + // set command to do the transition + pMsg->SetCmd(plCameraMsg::kRegionPushCamera); + // set the new camera + pMsg->SetTriggerer(avKey.getKey()); + pMsg->SetNewCam(pCam->GetKey()); + + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + } + } +} + +std::vector pySceneObject::GetResponders() +{ + std::vector pyPL; + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + for (int i = 0; i < obj->GetNumModifiers(); i++) + { + const plResponderModifier* resp = plResponderModifier::ConvertNoRef(obj->GetModifier(i)); + if (resp) + pyPL.push_back(pyKey::New(resp->GetKey())); + } + } + } + return pyPL; +} + +std::vector pySceneObject::GetPythonMods() +{ + std::vector pyPL; + if ( fSceneObjects.Count() > 0 ) + { + // get the object pointer of just the first one in the list + // (We really can't tell which one the user is thinking of if they are + // referring to multiple objects, so the first one in the list will do.) + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + for (int i = 0; i < obj->GetNumModifiers(); i++) + { + const plPythonFileMod* resp = plPythonFileMod::ConvertNoRef(obj->GetModifier(i)); + if (resp) + pyPL.push_back(pyKey::New(resp->GetKey())); + } + } + } + return pyPL; +} + + + +#include "../plMessage/plAnimCmdMsg.h" +#include "../pnMessage/plNotifyMsg.h" + +void pySceneObject::Animate() +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->AddReceiver(obj->GetKey()); + pMsg->SetCmd(plAnimCmdMsg::kGoToBegin); + pMsg->SetCmd(plAnimCmdMsg::kContinue); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +Int8 pySceneObject::GetResponderState() +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + for (int i = 0; i < obj->GetNumModifiers(); i++) + { + const plResponderModifier* resp = plResponderModifier::ConvertNoRef(obj->GetModifier(i)); + if (resp) + return resp->GetState(); + } + } + return -1; +} + +void pySceneObject::RewindAnim(const char* animName) +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->AddReceiver(obj->GetKey()); + pMsg->SetAnimName(animName); + pMsg->SetCmd(plAnimCmdMsg::kGoToBegin); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void pySceneObject::PlayAnim(const char* animName) +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->AddReceiver(obj->GetKey()); + pMsg->SetAnimName(animName); + pMsg->SetCmd(plAnimCmdMsg::kContinue); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void pySceneObject::StopAnim(const char* animName) +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->AddReceiver(obj->GetKey()); + pMsg->SetAnimName(animName); + pMsg->SetCmd(plAnimCmdMsg::kStop); + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } +} + +void pySceneObject::RunResponder(int state) +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plModifier* pMod = obj->GetModifierByType(plResponderModifier::Index()); + if (pMod) + { + plNotifyMsg* pMsg = TRACKED_NEW plNotifyMsg; + pMsg->fType = plNotifyMsg::kResponderChangeState; + pMsg->AddResponderStateEvent(state); + pMsg->AddReceiver(pMod->GetKey()); + pMsg->Send(); + + plNotifyMsg* pMsg0 = TRACKED_NEW plNotifyMsg; + pMsg0->fState = true; + pMsg0->AddReceiver(pMod->GetKey()); + pMsg0->Send(); + } + } +} + + +void pySceneObject::FFResponder(int state) +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plModifier* pMod = obj->GetModifierByType(plResponderModifier::Index()); + if (pMod) + { + plNotifyMsg* pMsg = TRACKED_NEW plNotifyMsg; + pMsg->fType = plNotifyMsg::kResponderFF; + pMsg->AddResponderStateEvent(state); + pMsg->AddReceiver(pMod->GetKey()); + pMsg->Send(); + } + } +} + +#include "../pnSceneObject/plAudioInterface.h" +#include "../NucleusLib/inc/plAudible.h" + +void pySceneObject::SetSoundFilename(int index, const char* filename, bool isCompressed) +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plAudioInterface* ai = obj->GetAudioInterface(); + if (ai) + { + plAudible* au = ai->GetAudible(); + if (au) + { + au->SetFilename(index, filename, isCompressed); + } + } + } +} + +int pySceneObject::GetSoundObjectIndex(const char* sndObj) +{ + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if ( obj ) + { + const plAudioInterface* ai = obj->GetAudioInterface(); + if (ai) + { + plAudible* au = ai->GetAudible(); + if (au) + { + return au->GetSoundIndex(sndObj); + } + } + } + + return -1; +} + +void pySceneObject::VolumeSensorIgnoreExtraEnters(bool ignore) +{ + if (fSceneObjects.Count() > 0) + { + plSceneObject* obj = plSceneObject::ConvertNoRef(fSceneObjects[0]->ObjectIsLoaded()); + if (obj) + { + for (int i = 0; i < obj->GetNumModifiers(); ++i) + { + plLogicModifier* logic = const_cast(plLogicModifier::ConvertNoRef(obj->GetModifier(i))); + if (logic) + logic->VolumeIgnoreExtraEnters(ignore); + } + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h new file mode 100644 index 00000000..8f5c3ea6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObject.h @@ -0,0 +1,189 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pySceneObject_h_ +#define _pySceneObject_h_ + +////////////////////////////////////////////////////////////////////// +// +// pySceneObject - a wrapper class to provide interface to modifier +// attached to a SceneObject +// +////////////////////////////////////////////////////////////////////// + +#include "pyKey.h" +#include "cyDraw.h" +#include "cyPhysics.h" +#include "cyAvatar.h" +#include "cyParticleSys.h" + +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + + +class pySceneObject +{ +private: + hsTArray fSceneObjects; + plKey fSenderKey; // the holder of the who (the modifier) we are + plKey fPyMod; // pyKey that points to modifier + + hsBool fNetForce; + + virtual void IAddObjKeyToAll(plKey key); + virtual void ISetAllSenderKeys(); + +protected: + pySceneObject(); + pySceneObject(pyKey& objkey, pyKey& selfkey); + pySceneObject(plKey objkey,pyKey& selfkey); + pySceneObject(plKey objkey); + +public: + ~pySceneObject() {Py_XDECREF(fDraw); Py_XDECREF(fPhysics); Py_XDECREF(fAvatar); Py_XDECREF(fParticle);} + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptSceneobject); + static PyObject *New(plKey objKey, PyObject *selfKeyObj); + static PyObject *New(plKey objKey, pyKey &selfKey); + static PyObject *New(plKey objKey, plKey selfkey); + static PyObject *New(plKey objKey); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pySceneObject object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pySceneObject); // converts a PyObject to a pySceneObject (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + // override the equals to operator + hsBool operator==(const pySceneObject &sobj) const; + hsBool operator!=(const pySceneObject &sobj) const { return !(sobj == *this); } + + PyObject* fDraw; // cyDraw + PyObject* fPhysics; // cyPhysics + PyObject* fAvatar; // cyAvatar + PyObject* fParticle; // cyParticleSys + + // getter and setters + virtual void addObjKey(plKey key); + virtual void addObjPyKey(pyKey& objkey); + virtual plKey getObjKey(); + virtual PyObject* getObjPyKey(); // pyKey + + virtual void setSenderKey(plKey key); + virtual void setPyMod(pyKey& pymod); + virtual void setPyMod(plKey& key); + + virtual void SetNetForce(hsBool state); + + virtual PyObject* findObj(const char* name); // pySceneObject + + virtual const char* GetName(); + virtual std::vector GetResponders(); // pyKey list + virtual std::vector GetPythonMods(); // pyKey list + // + // deteremine if this object (or the first object in the list) + // ...is locally owned + virtual hsBool IsLocallyOwned(); + + // + // get the local to world matrix + virtual PyObject* GetLocalToWorld(); + + // + // get the local to world matrix + virtual PyObject* GetWorldToLocal(); + + // + // get the local to world matrix + virtual PyObject* GetLocalToParent(); + + // + // get the local to world matrix + virtual PyObject* GetParentToLocal(); + + // + // set the local to world matrix + virtual void SetTransform(pyMatrix44& l2w, pyMatrix44& w2l); + + // + // find the position of this object (if there are more than one, just the first one) + virtual PyObject* GetWorldPosition(); // pyPoint3 + + // + // find the view vector for this object (if there are more than one, just the first one) + virtual PyObject* GetViewVector(); // pyVector3 + + // + // find the up vector for this object (if there are more than one, just the first one) + virtual PyObject* GetUpVector(); // pyVector3 + + // + // find the up vector for this object (if there are more than one, just the first one) + virtual PyObject* GetRightVector(); // pyVector3 + + // + // deteremine if this object (or any of the object attached) + // ...is an avatar, of any type + virtual hsBool IsAvatar(); + + virtual PyObject* GetAvatarVelocity(); // pyVector3 + + // + // deteremine if this object (or the first object in the list) + // ...is a human avatar + virtual hsBool IsHumanAvatar(); + + // + // switch to / from this camera (if it is a camera) + // + void PushCamera(pyKey& avKey); + void PushCameraCut(pyKey& avKey); + void PopCamera(pyKey& avKey); + void PushCutsceneCamera(hsBool cut,pyKey& avKey); + void PopCutsceneCamera(pyKey& avKey); + + void Animate(); + + // return responder state (if responder modifier found) + Int8 GetResponderState(); + + // some animation commands for s.o.'s w/ multiple animations attached + + void RewindAnim(const char* animName); + void PlayAnim(const char* animName); + void StopAnim(const char* animName); + + void RunResponder(int state); + void FFResponder(int state); + + void SetSoundFilename(int index, const char* filename, bool isCompressed); + int GetSoundObjectIndex(const char* sndObj); + + // hack for garrison + void VolumeSensorIgnoreExtraEnters(bool ignore); +}; + +#endif // _pySceneObject_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp new file mode 100644 index 00000000..9b5aaa09 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySceneObjectGlue.cpp @@ -0,0 +1,605 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pySceneObject.h" +#include "pyMatrix44.h" +#include "pyGeometry3.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptSceneobject, pySceneObject); + +PYTHON_DEFAULT_NEW_DEFINITION(ptSceneobject, pySceneObject) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptSceneobject) + +PYTHON_INIT_DEFINITION(ptSceneobject, args, keywords) +{ + PyObject *objKeyObject = NULL; + PyObject *selfKeyObject = NULL; + if (!PyArg_ParseTuple(args, "OO", &objKeyObject, &selfKeyObject)) + { + PyErr_SetString(PyExc_TypeError, "init expects two ptKey objects"); + PYTHON_RETURN_INIT_ERROR; + } + if ((!pyKey::Check(objKeyObject))||(!pyKey::Check(selfKeyObject))) + { + PyErr_SetString(PyExc_TypeError, "init expects two ptKey objects"); + PYTHON_RETURN_INIT_ERROR; + } + + pyKey *objKey = pyKey::ConvertFrom(objKeyObject); + pyKey *selfKey = pyKey::ConvertFrom(selfKeyObject); + self->fThis->addObjKey(objKey->getKey()); + self->fThis->setSenderKey(selfKey->getKey()); + self->fThis->setPyMod(*selfKey); + + PYTHON_RETURN_INIT_OK; +} + +PYTHON_RICH_COMPARE_DEFINITION(ptSceneobject, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None) || !pySceneObject::Check(obj1) || !pySceneObject::Check(obj2)) + { + // if they aren't the same type, they don't match, obviously (we also never equal none) + if (compareType == Py_EQ) + PYTHON_RCOMPARE_FALSE; + else if (compareType == Py_NE) + PYTHON_RCOMPARE_TRUE; + else + { + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptSceneobject object"); + PYTHON_RCOMPARE_ERROR; + } + } + pySceneObject *scObj1 = pySceneObject::ConvertFrom(obj1); + pySceneObject *scObj2 = pySceneObject::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*scObj1) == (*scObj2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*scObj1) != (*scObj2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptSceneobject object"); + PYTHON_RCOMPARE_ERROR; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, addKey, args) +{ + PyObject *keyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObject)) + { + PyErr_SetString(PyExc_TypeError, "addKey expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(keyObject)) + { + PyErr_SetString(PyExc_TypeError, "addKey expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey *key = pyKey::ConvertFrom(keyObject); + self->fThis->addObjPyKey(*key); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, getKey) +{ + return self->fThis->getObjPyKey(); +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, netForce, args) +{ + char forceFlag; + if (!PyArg_ParseTuple(args, "b", &forceFlag)) + { + PyErr_SetString(PyExc_TypeError, "netForce requires a boolean argument"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetNetForce(forceFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, findObject, args) +{ + char *name = NULL; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "findObject expects a string"); + PYTHON_RETURN_ERROR; + } + return self->fThis->findObj(name); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, getName) +{ + return PyString_FromString(self->fThis->GetName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, getResponders) +{ + std::vector vecList = self->fThis->GetResponders(); + PyObject *retVal = PyList_New(vecList.size()); + for (int curKey = 0; curKey < vecList.size(); curKey++) + PyList_SetItem(retVal, curKey, vecList[curKey]); // steals the vecList ref + return retVal; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, getPythonMods) +{ + std::vector vecList = self->fThis->GetPythonMods(); + PyObject *retVal = PyList_New(vecList.size()); + for (int curKey = 0; curKey < vecList.size(); curKey++) + PyList_SetItem(retVal, curKey, vecList[curKey]); // steals the vecList ref + return retVal; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, isLocallyOwned) +{ + PYTHON_RETURN_BOOL(self->fThis->IsLocallyOwned()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, getLocalToWorld) +{ + return self->fThis->GetLocalToWorld(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, getWorldToLocal) +{ + return self->fThis->GetWorldToLocal(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, getLocalToParent) +{ + return self->fThis->GetLocalToParent(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, getParentToLocal) +{ + return self->fThis->GetParentToLocal(); +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, setTransform, args) +{ + PyObject *local2WorldObj = NULL; + PyObject *world2LocalObj = NULL; + if (!PyArg_ParseTuple(args, "OO", &local2WorldObj, &world2LocalObj)) + { + PyErr_SetString(PyExc_TypeError, "setTransform expects two ptMatrix44 objects"); + PYTHON_RETURN_ERROR; + } + if ((!pyMatrix44::Check(local2WorldObj))||(!pyMatrix44::Check(world2LocalObj))) + { + PyErr_SetString(PyExc_TypeError, "setTransform expects two ptMatrix44 objects"); + PYTHON_RETURN_ERROR; + } + pyMatrix44 *local2World = pyMatrix44::ConvertFrom(local2WorldObj); + pyMatrix44 *world2Local = pyMatrix44::ConvertFrom(world2LocalObj); + self->fThis->SetTransform(*local2World, *world2Local); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, position) +{ + return self->fThis->GetWorldPosition(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, view) +{ + return self->fThis->GetViewVector(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, up) +{ + return self->fThis->GetUpVector(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, right) +{ + return self->fThis->GetRightVector(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, isAvatar) +{ + PYTHON_RETURN_BOOL(self->fThis->IsAvatar()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, avatarVelocity) +{ + return self->fThis->GetAvatarVelocity(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, isHuman) +{ + PYTHON_RETURN_BOOL(self->fThis->IsHumanAvatar()); +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, pushCutsceneCamera, args) +{ + char cutFlag; + PyObject *avKeyObject = NULL; + if (!PyArg_ParseTuple(args, "bO", &cutFlag, &avKeyObject)) + { + PyErr_SetString(PyExc_TypeError, "pushCutseneCamera expects a boolean and a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(avKeyObject)) + { + PyErr_SetString(PyExc_TypeError, "pushCutseneCamera expects a boolean and a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey *avKey = pyKey::ConvertFrom(avKeyObject); + self->fThis->PushCutsceneCamera((cutFlag != 0), *avKey); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, popCutsceneCamera, args) +{ + PyObject *avKeyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &avKeyObject)) + { + PyErr_SetString(PyExc_TypeError, "popCutsceneCamera expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(avKeyObject)) + { + PyErr_SetString(PyExc_TypeError, "popCutsceneCamera expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey *avKey = pyKey::ConvertFrom(avKeyObject); + self->fThis->PopCutsceneCamera(*avKey); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, pushCamera, args) +{ + PyObject *avKeyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &avKeyObject)) + { + PyErr_SetString(PyExc_TypeError, "pushCamera expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(avKeyObject)) + { + PyErr_SetString(PyExc_TypeError, "pushCamera expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey *avKey = pyKey::ConvertFrom(avKeyObject); + self->fThis->PushCamera(*avKey); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, pushCameraCut, args) +{ + PyObject *avKeyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &avKeyObject)) + { + PyErr_SetString(PyExc_TypeError, "pushCameraCut expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(avKeyObject)) + { + PyErr_SetString(PyExc_TypeError, "pushCameraCut expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey *avKey = pyKey::ConvertFrom(avKeyObject); + self->fThis->PushCameraCut(*avKey); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, popCamera, args) +{ + PyObject *avKeyObject = NULL; + if (!PyArg_ParseTuple(args, "O", &avKeyObject)) + { + PyErr_SetString(PyExc_TypeError, "popCamera expects a ptKey"); + PYTHON_RETURN_ERROR; + } + if (!pyKey::Check(avKeyObject)) + { + PyErr_SetString(PyExc_TypeError, "popCamera expects a ptKey"); + PYTHON_RETURN_ERROR; + } + pyKey *avKey = pyKey::ConvertFrom(avKeyObject); + self->fThis->PopCamera(*avKey); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSceneobject, getResponderState) +{ + return PyInt_FromLong((long)self->fThis->GetResponderState()); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptSceneobject, animate, Animate) + +PYTHON_METHOD_DEFINITION(ptSceneobject, rewindAnimNamed, args) +{ + char *name = NULL; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "rewindAnimNamed expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->RewindAnim(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, playAnimNamed, args) +{ + char *name = NULL; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "playAnimNamed expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->PlayAnim(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, stopAnimNamed, args) +{ + char *name = NULL; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "stopAnimNamed expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->StopAnim(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, runAttachedResponder, args) +{ + int state; + if (!PyArg_ParseTuple(args, "i", &state)) + { + PyErr_SetString(PyExc_TypeError, "runAttachedResponder expects an integer"); + PYTHON_RETURN_ERROR; + } + self->fThis->RunResponder(state); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, fastForwardAttachedResponder, args) +{ + int state; + if (!PyArg_ParseTuple(args, "i", &state)) + { + PyErr_SetString(PyExc_TypeError, "fastForwardAttachedResponder expects an integer"); + PYTHON_RETURN_ERROR; + } + self->fThis->FFResponder(state); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, setSoundFilename, args) +{ + int index; + char *filename = NULL; + char isCompressed; + if (!PyArg_ParseTuple(args, "isb", &index, &filename, &isCompressed)) + { + PyErr_SetString(PyExc_TypeError, "setSoundFilename expects an integer, string and boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSoundFilename(index, filename, (isCompressed != 0)); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, getSoundIndex, args) +{ + char *sndComponentName = NULL; + if (!PyArg_ParseTuple(args, "s", &sndComponentName)) + { + PyErr_SetString(PyExc_TypeError, "getSoundIndex expects a string"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong((long)self->fThis->GetSoundObjectIndex(sndComponentName)); +} + +PYTHON_METHOD_DEFINITION(ptSceneobject, volumeSensorIgnoreExtraEnters, args) +{ + char ignore; + if (!PyArg_ParseTuple(args, "b", &ignore)) + { + PyErr_SetString(PyExc_TypeError, "volumeSensorIgnoreExtraEnters expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->VolumeSensorIgnoreExtraEnters(ignore != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptSceneobject) + PYTHON_METHOD(ptSceneobject, addKey, "Params: key\nMostly used internally.\n" + "Add another sceneobject ptKey"), + PYTHON_METHOD_NOARGS(ptSceneobject, getKey, "Get the ptKey of this sceneobject\n" + "If there are more then one attached, get the first one"), + + PYTHON_METHOD(ptSceneobject, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n" + "- This is to be used if your Python program is running on only one client\n" + "Such as a game master, only running on the client that owns a particular object\n" + "- Setting the netForce flag on a sceneobject will also set the netForce flag on\n" + "its draw, physics, avatar, particle objects"), + + PYTHON_METHOD(ptSceneobject, findObject, "Params: name\nFind a particular object in just the sceneobjects that are attached"), + PYTHON_METHOD_NOARGS(ptSceneobject, getName, "Returns the name of the sceneobject (Max name)\n" + "- If there are more than one sceneobject attached, return just the first one"), + PYTHON_METHOD_NOARGS(ptSceneobject, getResponders, "Returns list of ptKeys of the responders attached to this sceneobject"), + PYTHON_METHOD_NOARGS(ptSceneobject, getPythonMods, "Returns list of ptKeys of the python modifiers attached to this sceneobject"), + PYTHON_METHOD_NOARGS(ptSceneobject, isLocallyOwned, "Returns true(1) if this object is locally owned by this client\n" + "or returns false(0) if it is not or don't know"), + + PYTHON_METHOD_NOARGS(ptSceneobject, getLocalToWorld, "Returns ptMatrix44 of the local to world transform for this sceneobject\n" + "- If there is more than one sceneobject attached, returns just the first one"), + PYTHON_METHOD_NOARGS(ptSceneobject, getWorldToLocal, "Returns ptMatrix44 of the world to local transform for this sceneobject\n" + "- If there is more than one sceneobject attached, returns just the first one"), + PYTHON_METHOD_NOARGS(ptSceneobject, getLocalToParent, "Returns ptMatrix44 of the local to parent transform for this sceneobject\n" + "- If there is more than one sceneobject attached, returns just the first one"), + PYTHON_METHOD_NOARGS(ptSceneobject, getParentToLocal, "Returns ptMatrix44 of the parent to local transform for this sceneobject\n" + "- If there is more than one sceneobject attached, returns just the first one"), + PYTHON_METHOD(ptSceneobject, setTransform, "Params: local2world,world2local\nSet our current transforms"), + + PYTHON_METHOD_NOARGS(ptSceneobject, position, "Returns the scene object's current position"), + PYTHON_METHOD_NOARGS(ptSceneobject, view, "Returns the scene object's current view vector"), + PYTHON_METHOD_NOARGS(ptSceneobject, up, "Returns the scene object's current up vector"), + PYTHON_METHOD_NOARGS(ptSceneobject, right, "Returns the scene object's current right vector"), + PYTHON_METHOD_NOARGS(ptSceneobject, isAvatar, "Returns true if the scene object is an avatar"), + PYTHON_METHOD_NOARGS(ptSceneobject, avatarVelocity, "Returns the velocity of the first attached avatar scene object"), + PYTHON_METHOD_NOARGS(ptSceneobject, isHuman, "Returns true if the scene object is a human avatar"), + + PYTHON_METHOD(ptSceneobject, pushCutsceneCamera, "Params: cutFlag,avKey\nSwitch to this object (assuming that it is actually a camera)"), + PYTHON_METHOD(ptSceneobject, popCutsceneCamera, "Params: avKey\nPop the camera stack and go back to previous camera."), + PYTHON_METHOD(ptSceneobject, pushCamera, "Params: avKey\nSwitch to this object (if it is a camera)"), + PYTHON_METHOD(ptSceneobject, pushCameraCut, "Params: avKey\nSwitch to this object, cutting the view (if it is a camera)"), + PYTHON_METHOD(ptSceneobject, popCamera, "Params: avKey\nPop the camera stack and go back to the previous camera"), + + PYTHON_METHOD_NOARGS(ptSceneobject, getResponderState, "Return the responder state (if we are a responder)"), + + PYTHON_BASIC_METHOD(ptSceneobject, animate, "If we can animate, start animating"), + PYTHON_METHOD(ptSceneobject, rewindAnimNamed, "Params: animName\nRewind the attached named animation"), + PYTHON_METHOD(ptSceneobject, playAnimNamed, "Params: animName\nPlay the attached named animation"), + PYTHON_METHOD(ptSceneobject, stopAnimNamed, "Params: animName\nStop the attached named animation"), + + PYTHON_METHOD(ptSceneobject, runAttachedResponder, "Params: state\nRun the attached responder to the specified state"), + PYTHON_METHOD(ptSceneobject, fastForwardAttachedResponder, "Params: state\nFast forward the attached responder to the specified state"), + + PYTHON_METHOD(ptSceneobject, setSoundFilename, "Params: index, filename, isCompressed\nSets the sound attached to this sceneobject to use the specified sound file."), + PYTHON_METHOD(ptSceneobject, getSoundIndex, "Params: sndComponentName\nGet the index of the requested sound component"), + + PYTHON_METHOD(ptSceneobject, volumeSensorIgnoreExtraEnters, "Params: ignore\nTells the volume sensor attached to this object to ignore extra enters (default), or not (hack for garrison)."), +PYTHON_END_METHODS_TABLE; + +PYTHON_GET_DEFINITION(ptSceneobject, draw) +{ + Py_INCREF(self->fThis->fDraw); // we need to return a new ref + return self->fThis->fDraw; +} + +PYTHON_SET_DEFINITION_READONLY(ptSceneobject, draw) + +PYTHON_GET_DEFINITION(ptSceneobject, physics) +{ + Py_INCREF(self->fThis->fPhysics); // we need to return a new ref + return self->fThis->fPhysics; +} + +PYTHON_SET_DEFINITION_READONLY(ptSceneobject, physics) + +PYTHON_GET_DEFINITION(ptSceneobject, avatar) +{ + Py_INCREF(self->fThis->fAvatar); // we need to return a new ref + return self->fThis->fAvatar; +} + +PYTHON_SET_DEFINITION_READONLY(ptSceneobject, avatar) + +PYTHON_GET_DEFINITION(ptSceneobject, particle) +{ + Py_INCREF(self->fThis->fParticle); // we need to return a new ref + return self->fThis->fParticle; +} + +PYTHON_SET_DEFINITION_READONLY(ptSceneobject, particle) + +PYTHON_START_GETSET_TABLE(ptSceneobject) + PYTHON_GETSET(ptSceneobject, draw, ""), + PYTHON_GETSET(ptSceneobject, physics, ""), + PYTHON_GETSET(ptSceneobject, avatar, ""), + PYTHON_GETSET(ptSceneobject, particle, ""), +PYTHON_END_GETSET_TABLE; + +// Type structure definition +#define ptSceneobject_COMPARE PYTHON_NO_COMPARE +#define ptSceneobject_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptSceneobject_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptSceneobject_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptSceneobject_STR PYTHON_NO_STR +#define ptSceneobject_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptSceneobject) +#define ptSceneobject_GETSET PYTHON_DEFAULT_GETSET(ptSceneobject) +#define ptSceneobject_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptSceneobject, "Params: objKey, selfKey\nPlasma Sceneobject class"); + +// required functions for PyObject interoperability +PyObject *pySceneObject::New(plKey objKey, PyObject *selfKeyObj) +{ + if (!pyKey::Check(selfKeyObj)) + return NULL; + pyKey *selfKey = pyKey::ConvertFrom(selfKeyObj); + + ptSceneobject *newObj = (ptSceneobject*)ptSceneobject_type.tp_new(&ptSceneobject_type, NULL, NULL); + newObj->fThis->addObjKey(objKey); + newObj->fThis->setSenderKey(selfKey->getKey()); + newObj->fThis->setPyMod(*selfKey); + newObj->fThis->SetNetForce(false); + return (PyObject*)newObj; +} + +PyObject *pySceneObject::New(plKey objKey, pyKey &selfKey) +{ + ptSceneobject *newObj = (ptSceneobject*)ptSceneobject_type.tp_new(&ptSceneobject_type, NULL, NULL); + newObj->fThis->addObjKey(objKey); + newObj->fThis->setSenderKey(selfKey.getKey()); + newObj->fThis->setPyMod(selfKey); + newObj->fThis->SetNetForce(false); + return (PyObject*)newObj; +} + +PyObject *pySceneObject::New(plKey objKey, plKey selfKey) +{ + ptSceneobject *newObj = (ptSceneobject*)ptSceneobject_type.tp_new(&ptSceneobject_type, NULL, NULL); + newObj->fThis->addObjKey(objKey); + newObj->fThis->setSenderKey(selfKey); + newObj->fThis->setPyMod(selfKey); + newObj->fThis->SetNetForce(false); + return (PyObject*)newObj; +} + +PyObject *pySceneObject::New(plKey objKey) +{ + ptSceneobject *newObj = (ptSceneobject*)ptSceneobject_type.tp_new(&ptSceneobject_type, NULL, NULL); + newObj->fThis->addObjKey(objKey); + newObj->fThis->setSenderKey(objKey); + newObj->fThis->SetNetForce(false); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptSceneobject, pySceneObject) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptSceneobject, pySceneObject) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pySceneObject::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptSceneobject); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgr.cpp new file mode 100644 index 00000000..bf1c2f86 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgr.cpp @@ -0,0 +1,338 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyScoreMgr.h" + +#include "../pfGameScoreMgr/pfGameScoreMgr.h" +#include "../plVault/plVault.h" +#include "../plNetCommon/plNetCommon.h" +#include "pyGameScore.h" + +pyScoreMgr::pyScoreMgr() +{ +} + +pyScoreMgr::~pyScoreMgr() +{ +} + +bool pyScoreMgr::DeleteScore(unsigned scoreId) +{ + return IS_NET_SUCCESS(pfGameScoreMgr::GetInstance()->DeleteScore(scoreId)); +} + +PyObject* pyScoreMgr::CreateGlobalScore( + const char * gameName, + unsigned gameType, + int value +) { + pfGameScore * score = NEWZERO(pfGameScore); + score->IncRef(); + + pfGameScoreMgr::GetInstance()->CreateScore(0, gameName, gameType, value, *score); + + if (score) + { + if (score->scoreId > 0) + { + PyObject* pyScore = pyGameScore::New(score); + score->DecRef(); + + return pyScore; + } + + score->DecRef(); + } + + PYTHON_RETURN_NONE; +} + +PyObject* pyScoreMgr::GetGlobalScores(const char* gameName) +{ + pfGameScore** scoreList = nil; + int scoreListCount = 0; + ENetError result = pfGameScoreMgr::GetInstance()->GetScoresIncRef(0, gameName, scoreList, scoreListCount); + + if (IS_NET_SUCCESS(result) && scoreListCount > 0) + { + PyObject* pyScoreList = PyList_New(scoreListCount); + for (int i = 0; i < scoreListCount; ++i) + { + PyObject* pyScore = pyGameScore::New(scoreList[i]); + PyList_SetItem(pyScoreList, i, pyScore); + scoreList[i]->DecRef(); + } + + delete [] scoreList; + + return pyScoreList; + } + + PYTHON_RETURN_NONE; +} + +PyObject* pyScoreMgr::CreatePlayerScore( + const char * gameName, + unsigned gameType, + int value +) { + pfGameScore * score = nil; + + if (RelVaultNode * rvn = VaultGetPlayerInfoNodeIncRef()) { + unsigned ownerId = rvn->nodeId; + rvn->DecRef(); + + score = NEWZERO(pfGameScore); + score->IncRef(); + pfGameScoreMgr::GetInstance()->CreateScore(ownerId, gameName, gameType, value, *score); + } + + if (score) + { + if (score->scoreId > 0) + { + PyObject* pyScore = pyGameScore::New(score); + score->DecRef(); + + return pyScore; + } + + score->DecRef(); + } + + PYTHON_RETURN_NONE; +} + +PyObject* pyScoreMgr::GetPlayerScores(const char* gameName) +{ + if (RelVaultNode * rvn = VaultGetPlayerInfoNodeIncRef()) { + unsigned ownerId = rvn->nodeId; + rvn->DecRef(); + + pfGameScore** scoreList = nil; + int scoreListCount = 0; + ENetError result = pfGameScoreMgr::GetInstance()->GetScoresIncRef(ownerId, gameName, scoreList, scoreListCount); + + if (IS_NET_SUCCESS(result) && scoreListCount > 0) + { + PyObject* pyScoreList = PyList_New(scoreListCount); + for (int i = 0; i < scoreListCount; ++i) + { + PyObject* pyScore = pyGameScore::New(scoreList[i]); + PyList_SetItem(pyScoreList, i, pyScore); + scoreList[i]->DecRef(); + } + + delete [] scoreList; + + return pyScoreList; + } + } + + PYTHON_RETURN_NONE; +} + +PyObject* pyScoreMgr::CreateNeighborhoodScore( + const char * gameName, + unsigned gameType, + int value +) { + pfGameScore * score = nil; + + plAgeInfoStruct info; + info.SetAgeFilename(kNeighborhoodAgeFilename); + + if (RelVaultNode * rvn = VaultGetOwnedAgeInfoIncRef(&info)) { + unsigned ownerId = rvn->nodeId; + rvn->DecRef(); + + score = NEWZERO(pfGameScore); + score->IncRef(); + pfGameScoreMgr::GetInstance()->CreateScore(ownerId, gameName, gameType, value, *score); + } + + if (score) + { + if (score->scoreId > 0) + { + PyObject* pyScore = pyGameScore::New(score); + score->DecRef(); + + return pyScore; + } + + score->DecRef(); + } + + PYTHON_RETURN_NONE; +} + +PyObject* pyScoreMgr::GetNeighborhoodScores(const char* gameName) +{ + plAgeInfoStruct info; + info.SetAgeFilename(kNeighborhoodAgeFilename); + + if (RelVaultNode * rvn = VaultGetOwnedAgeInfoIncRef(&info)) { + unsigned ownerId = rvn->nodeId; + rvn->DecRef(); + + pfGameScore** scoreList = nil; + int scoreListCount = 0; + ENetError result = pfGameScoreMgr::GetInstance()->GetScoresIncRef(ownerId, gameName, scoreList, scoreListCount); + + if (IS_NET_SUCCESS(result) && scoreListCount > 0) + { + PyObject* pyScoreList = PyList_New(scoreListCount); + for (int i = 0; i < scoreListCount; ++i) + { + PyObject* pyScore = pyGameScore::New(scoreList[i]); + PyList_SetItem(pyScoreList, i, pyScore); + scoreList[i]->DecRef(); + } + + delete [] scoreList; + + return pyScoreList; + } + } + + PYTHON_RETURN_NONE; +} + +PyObject* pyScoreMgr::CreateCurrentAgeScore( + const char * gameName, + unsigned gameType, + int value +) { + pfGameScore * score = nil; + + if (RelVaultNode * rvn = VaultGetAgeInfoNodeIncRef()) { + unsigned ownerId = rvn->nodeId; + rvn->DecRef(); + + score = NEWZERO(pfGameScore); + score->IncRef(); + pfGameScoreMgr::GetInstance()->CreateScore(ownerId, gameName, gameType, value, *score); + } + + if (score) + { + if (score->scoreId > 0) + { + PyObject* pyScore = pyGameScore::New(score); + score->DecRef(); + + return pyScore; + } + + score->DecRef(); + } + + PYTHON_RETURN_NONE; +} + +PyObject* pyScoreMgr::GetCurrentAgeScores(const char* gameName) +{ + if (RelVaultNode * rvn = VaultGetAgeInfoNodeIncRef()) { + unsigned ownerId = rvn->nodeId; + rvn->DecRef(); + + pfGameScore** scoreList = nil; + int scoreListCount = 0; + ENetError result = pfGameScoreMgr::GetInstance()->GetScoresIncRef(ownerId, gameName, scoreList, scoreListCount); + + if (IS_NET_SUCCESS(result) && scoreListCount > 0) + { + PyObject* pyScoreList = PyList_New(scoreListCount); + for (int i = 0; i < scoreListCount; ++i) + { + PyObject* pyScore = pyGameScore::New(scoreList[i]); + PyList_SetItem(pyScoreList, i, pyScore); + scoreList[i]->DecRef(); + } + + delete [] scoreList; + + return pyScoreList; + } + } + + PYTHON_RETURN_NONE; +} + +PyObject * pyScoreMgr::GetRankList( + unsigned scoreGroup, + unsigned parentFolderId, + const char * gameName, + unsigned timePeriod, + unsigned numResults, + unsigned pageNumber, + bool sortDesc +) { + if (RelVaultNode * rvn = VaultGetPlayerInfoNodeIncRef()) { + unsigned ownerId = rvn->nodeId; + rvn->DecRef(); + + NetGameRank** rankList = nil; + int rankListCount = 0; + ENetError result = pfGameScoreMgr::GetInstance()->GetRankList( + ownerId, + scoreGroup, + parentFolderId, + gameName, + timePeriod, + numResults, + pageNumber, + sortDesc, + rankList, + rankListCount + ); + + if (IS_NET_SUCCESS(result) && rankListCount > 0) + { + PyObject* pyRankList = PyList_New(rankListCount); + for (int i = 0; i < rankListCount; ++i) + { + char tempStr[kMaxPlayerNameLength]; + StrToAnsi(tempStr, rankList[i]->name, arrsize(tempStr)); + + PyObject* pyRank = PyTuple_New(3); + PyTuple_SetItem(pyRank, 0, PyInt_FromLong(rankList[i]->rank)); + PyTuple_SetItem(pyRank, 1, PyString_FromString(tempStr)); + PyTuple_SetItem(pyRank, 2, PyInt_FromLong(rankList[i]->score)); + + PyList_SetItem(pyRankList, i, pyRank); + + delete rankList[i]; + } + + delete [] rankList; + + return pyRankList; + } + } + + PYTHON_RETURN_NONE; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgr.h new file mode 100644 index 00000000..c7f43614 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgr.h @@ -0,0 +1,99 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyScoreMgr_h +#define pyScoreMgr_h + +///////////////////////////////////////////////////////////////////////////// +// +// NAME: pyScoreMgr +// +// PURPOSE: a wrapper class to provide an interface to the scoring system +// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + + +class pyScoreMgr +{ +public: + pyScoreMgr(); + ~pyScoreMgr(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptScoreMgr); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyScoreMgr object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyScoreMgr); // converts a PyObject to a pyScoreMgr (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaConstantsClasses(PyObject *m); + + bool DeleteScore(unsigned scoreId); + + PyObject* CreateGlobalScore( + const char * gameName, + unsigned gameType, + int value + ); + PyObject* GetGlobalScores(const char* gameName); + + PyObject* CreatePlayerScore( + const char * gameName, + unsigned gameType, + int value + ); + PyObject* GetPlayerScores(const char* gameName); + + PyObject* CreateNeighborhoodScore( + const char * gameName, + unsigned gameType, + int value + ); + PyObject* GetNeighborhoodScores(const char* gameName); + + PyObject* CreateCurrentAgeScore( + const char * gameName, + unsigned gameType, + int value + ); + PyObject* GetCurrentAgeScores(const char* gameName); + + PyObject * GetRankList( + unsigned scoreGroup, + unsigned parentFolderId, + const char * gameName, + unsigned timePeriod, + unsigned numResults, + unsigned pageNumber, + bool sortDesc + ); +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgrGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgrGlue.cpp new file mode 100644 index 00000000..a866b8c7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyScoreMgrGlue.cpp @@ -0,0 +1,219 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyScoreMgr.h" + +#include "pyEnum.h" +#include "../pfGameScoreMgr/pfGameScoreMgr.h" + +// glue functions +PYTHON_CLASS_DEFINITION(ptScoreMgr, pyScoreMgr); + +PYTHON_DEFAULT_NEW_DEFINITION(ptScoreMgr, pyScoreMgr) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptScoreMgr) + +PYTHON_INIT_DEFINITION(ptScoreMgr, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptScoreMgr, deleteScore, args) +{ + int scoreId; + if (!PyArg_ParseTuple(args, "i", &scoreId)) + { + PyErr_SetString(PyExc_TypeError, "deleteScore expects an int"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->DeleteScore(scoreId)); +} + +PYTHON_METHOD_DEFINITION(ptScoreMgr, createGlobalScore, args) +{ + char* gameName; + int gameType; + int value; + if (!PyArg_ParseTuple(args, "sii", &gameName, &gameType, &value)) + { + PyErr_SetString(PyExc_TypeError, "createGlobalScore expects a string, an int, and an int"); + PYTHON_RETURN_ERROR; + } + return self->fThis->CreateGlobalScore(gameName, gameType, value); +} + +PYTHON_METHOD_DEFINITION(ptScoreMgr, getGlobalScores, args) +{ + char* gameName; + if (!PyArg_ParseTuple(args, "s", &gameName)) + { + PyErr_SetString(PyExc_TypeError, "getGlobalScores expects a string"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetGlobalScores(gameName); +} + +PYTHON_METHOD_DEFINITION(ptScoreMgr, createPlayerScore, args) +{ + char* gameName; + int gameType; + int value; + if (!PyArg_ParseTuple(args, "sii", &gameName, &gameType, &value)) + { + PyErr_SetString(PyExc_TypeError, "createPlayerScore expects a string, an int, and an int"); + PYTHON_RETURN_ERROR; + } + return self->fThis->CreatePlayerScore(gameName, gameType, value); +} + +PYTHON_METHOD_DEFINITION(ptScoreMgr, getPlayerScores, args) +{ + char* gameName; + if (!PyArg_ParseTuple(args, "s", &gameName)) + { + PyErr_SetString(PyExc_TypeError, "getPlayerScores expects a string"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetPlayerScores(gameName); +} + +PYTHON_METHOD_DEFINITION(ptScoreMgr, createNeighborhoodScore, args) +{ + char* gameName; + int gameType; + int value; + if (!PyArg_ParseTuple(args, "sii", &gameName, &gameType, &value)) + { + PyErr_SetString(PyExc_TypeError, "createNeighborhoodScore expects a string, an int, and an int"); + PYTHON_RETURN_ERROR; + } + return self->fThis->CreateNeighborhoodScore(gameName, gameType, value); +} + +PYTHON_METHOD_DEFINITION(ptScoreMgr, getNeighborhoodScores, args) +{ + char* gameName; + if (!PyArg_ParseTuple(args, "s", &gameName)) + { + PyErr_SetString(PyExc_TypeError, "getNeighborhoodScores expects a string"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetNeighborhoodScores(gameName); +} + +PYTHON_METHOD_DEFINITION(ptScoreMgr, createCurrentAgeScore, args) +{ + char* gameName; + int gameType; + int value; + if (!PyArg_ParseTuple(args, "sii", &gameName, &gameType, &value)) + { + PyErr_SetString(PyExc_TypeError, "createCurrentAgeScore expects a string, an int, and an int"); + PYTHON_RETURN_ERROR; + } + return self->fThis->CreateCurrentAgeScore(gameName, gameType, value); +} + +PYTHON_METHOD_DEFINITION(ptScoreMgr, getCurrentAgeScores, args) +{ + char* gameName; + if (!PyArg_ParseTuple(args, "s", &gameName)) + { + PyErr_SetString(PyExc_TypeError, "getCurrentAgeScores expects a string"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetCurrentAgeScores(gameName); +} + +PYTHON_METHOD_DEFINITION(ptScoreMgr, getRankList, args) +{ + int scoreGroup; + int parentFolderId; + char* gameName; + int timePeriod; + int numResults; + int pageNumber; + int sortDesc; + if (!PyArg_ParseTuple(args, "iisiiii", &scoreGroup, &parentFolderId, &gameName, &timePeriod, &numResults, &pageNumber, &sortDesc)) + { + PyErr_SetString(PyExc_TypeError, "getRankList expects two ints, a string, and four more ints"); + PYTHON_RETURN_ERROR; + } + + return self->fThis->GetRankList(scoreGroup, parentFolderId, gameName, timePeriod, numResults, pageNumber, sortDesc != 0); +} + +PYTHON_START_METHODS_TABLE(ptScoreMgr) + PYTHON_METHOD(ptScoreMgr, deleteScore, "Params: scoreId\nDeletes the specified score"), + PYTHON_METHOD(ptScoreMgr, createGlobalScore, "Params: gameName, gameType, value\nCreates a score and returns it"), + PYTHON_METHOD(ptScoreMgr, getGlobalScores, "Params: gameName\nReturns a list of scores associated with the specified game."), + PYTHON_METHOD(ptScoreMgr, createPlayerScore, "Params: gameName, gameType, value\nCreates a score and returns it"), + PYTHON_METHOD(ptScoreMgr, getPlayerScores, "Params: gameName\nReturns a list of scores associated with the specified game."), + PYTHON_METHOD(ptScoreMgr, createNeighborhoodScore, "Params: gameName, gameType, value\nCreates a score and returns it"), + PYTHON_METHOD(ptScoreMgr, getNeighborhoodScores, "Params: gameName\nReturns a list of scores associated with the specified game."), + PYTHON_METHOD(ptScoreMgr, createCurrentAgeScore, "Params: gameName, gameType, value\nCreates a score and returns it"), + PYTHON_METHOD(ptScoreMgr, getCurrentAgeScores, "Params: gameName\nReturns a list of scores associated with the specified game."), + PYTHON_METHOD(ptScoreMgr, getRankList, "Params: ownerInfoId, scoreGroup, parentFolderId, gameName, timePeriod, numResults, pageNumber, sortDesc\nReturns a list of scores and rank"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptScoreMgr, "Game score manager"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptScoreMgr, pyScoreMgr) + +PYTHON_CLASS_CHECK_IMPL(ptScoreMgr, pyScoreMgr) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptScoreMgr, pyScoreMgr) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyScoreMgr::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptScoreMgr); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyScoreMgr::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtGameScoreTypes); + PYTHON_ENUM_ELEMENT(PtGameScoreTypes, kFixed, kScoreTypeFixed); + PYTHON_ENUM_ELEMENT(PtGameScoreTypes, kAccumulative, kScoreTypeAccumulative); + PYTHON_ENUM_ELEMENT(PtGameScoreTypes, kAccumAllowNegative, kScoreTypeAccumAllowNegative); + PYTHON_ENUM_END(m, PtGameScoreTypes); + + PYTHON_ENUM_START(PtScoreRankGroups); + PYTHON_ENUM_ELEMENT(PtScoreRankGroups, kIndividual, kScoreRankGroupIndividual); + PYTHON_ENUM_ELEMENT(PtScoreRankGroups, kNeighborhood, kScoreRankGroupNeighborhood); + PYTHON_ENUM_END(m, PtScoreRankGroups); + + PYTHON_ENUM_START(PtScoreTimePeriods); + PYTHON_ENUM_ELEMENT(PtScoreTimePeriods, kOverall, kScoreTimePeriodOverall); + PYTHON_ENUM_ELEMENT(PtScoreTimePeriods, kYear, kScoreTimePeriodYear); + PYTHON_ENUM_ELEMENT(PtScoreTimePeriods, kMonth, kScoreTimePeriodMonth); + PYTHON_ENUM_ELEMENT(PtScoreTimePeriods, kDay, kScoreTimePeriodDay); + PYTHON_ENUM_END(m, PtScoreTimePeriods); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfo.cpp new file mode 100644 index 00000000..8c00b595 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfo.cpp @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" + +#include "pySpawnPointInfo.h" + +/////////////////////////////////////////////////////////////////////////// + +pySpawnPointInfo::pySpawnPointInfo() +{ +} + +pySpawnPointInfo::pySpawnPointInfo( const char * title, const char * spawnPt ) +{ + fInfo.fTitle = title; + fInfo.fSpawnPt = spawnPt; +} + +PyObject *pySpawnPointInfo::GetDefaultSpawnPoint() +{ + return pySpawnPointInfo::New(kDefaultSpawnPoint); // returns a copy +} + +plSpawnPointInfo pySpawnPointInfoRef::fDefaultSPInfo; // created so a default constructor could be made for python, do NOT use diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfo.h new file mode 100644 index 00000000..d4cad9da --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfo.h @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pySpawnPointInfo_h_inc +#define pySpawnPointInfo_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "../plNetCommon/plSpawnPointInfo.h" + +#include +#include "pyGlueHelpers.h" + +////////////////////////////////////////////////////////////////////// +// +// pySpawnPointInfo - a wrapper class to provide interface to the plSpawnPointInfo +// +////////////////////////////////////////////////////////////////////// + +class pySpawnPointInfo +{ +protected: + pySpawnPointInfo(); + pySpawnPointInfo( const plSpawnPointInfo & info ): fInfo( info ) {} + pySpawnPointInfo( const char * title, const char * spawnPt ); + +public: + plSpawnPointInfo fInfo; + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptSpawnPointInfo); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(const plSpawnPointInfo& info); + static PyObject *New(const char* title, const char* spawnPt); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a ptSpawnPointInfo object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pySpawnPointInfo); // converts a PyObject to a pySpawnPointInfo (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaMethods(std::vector &methods); + + plSpawnPointInfo & SpawnPoint() { return fInfo; } + void SetTitle( const char * v ) { fInfo.SetTitle( v ); } + const char * GetTitle() const { return fInfo.GetTitle(); } + void SetName( const char * v ) { fInfo.SetName( v ); } + const char * GetName() const { return fInfo.GetName(); } + void SetCameraStack(const char * v ) { fInfo.SetCameraStack( v ); } + const char * GetCameraStack() const { return fInfo.GetCameraStack(); } + + static PyObject* GetDefaultSpawnPoint(); +}; + +class pySpawnPointInfoRef +{ +private: + static plSpawnPointInfo fDefaultSPInfo; // created so a default constructor could be made for python, do NOT use + +protected: + pySpawnPointInfoRef(): fInfo(fDefaultSPInfo) {} // only used by python glue, do NOT call directly + pySpawnPointInfoRef( plSpawnPointInfo & info ): fInfo( info ) {} + +public: + plSpawnPointInfo & fInfo; + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptSpawnPointInfoRef); + static PyObject *New(plSpawnPointInfo& info); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a ptSpawnPointInfoRef object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pySpawnPointInfoRef); // converts a PyObject to a pySpawnPointInfoRef (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + plSpawnPointInfo & SpawnPoint() { return fInfo; } + void SetTitle( const char * v ) { fInfo.SetTitle( v ); } + const char * GetTitle() const { return fInfo.GetTitle(); } + void SetName( const char * v ) { fInfo.SetName( v ); } + const char * GetName() const { return fInfo.GetName(); } + void SetCameraStack(const char * v ) { fInfo.SetCameraStack( v ); } + const char * GetCameraStack() const { return fInfo.GetCameraStack(); } +}; + +#endif // pySpawnPointInfo_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfoGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfoGlue.cpp new file mode 100644 index 00000000..aadf9ac1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySpawnPointInfoGlue.cpp @@ -0,0 +1,257 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pySpawnPointInfo.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptSpawnPointInfo, pySpawnPointInfo); + +PYTHON_DEFAULT_NEW_DEFINITION(ptSpawnPointInfo, pySpawnPointInfo) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptSpawnPointInfo) + +PYTHON_INIT_DEFINITION(ptSpawnPointInfo, args, keywords) +{ + char* title = NULL; + char* spawnPt = NULL; + if (!PyArg_ParseTuple(args, "|ss", &title, &spawnPt)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects two optional strings, or no parameters"); + PYTHON_RETURN_INIT_ERROR; + } + if (!title && !spawnPt) + { + // default init + PYTHON_RETURN_INIT_OK; + } + else if (title && spawnPt) + { + self->fThis->SetTitle(title); + self->fThis->SetName(spawnPt); + PYTHON_RETURN_INIT_OK; + } + // only one param existed + PyErr_SetString(PyExc_TypeError, "__init__ expects two optional strings, or no parameters"); + PYTHON_RETURN_INIT_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSpawnPointInfo, getTitle) +{ + return PyString_FromString(self->fThis->GetTitle()); +} + +PYTHON_METHOD_DEFINITION(ptSpawnPointInfo, setTitle, args) +{ + char* title; + if (!PyArg_ParseTuple(args, "s", &title)) + { + PyErr_SetString(PyExc_TypeError, "setTitle expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetTitle(title); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSpawnPointInfo, getName) +{ + return PyString_FromString(self->fThis->GetName()); +} + +PYTHON_METHOD_DEFINITION(ptSpawnPointInfo, setName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "setName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSpawnPointInfo, getCameraStack) +{ + return PyString_FromString(self->fThis->GetCameraStack()); +} + +PYTHON_METHOD_DEFINITION(ptSpawnPointInfo, setCameraStack, args) +{ + char* camStack; + if (!PyArg_ParseTuple(args, "s", &camStack)) + { + PyErr_SetString(PyExc_TypeError, "setCameraStack expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetCameraStack(camStack); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptSpawnPointInfo) + PYTHON_METHOD_NOARGS(ptSpawnPointInfo, getTitle, "Returns the spawnpoint's title"), + PYTHON_METHOD(ptSpawnPointInfo, setTitle, "Params: title\nSets the spawnpoint's title"), + PYTHON_METHOD_NOARGS(ptSpawnPointInfo, getName, "Returns the spawnpoint's name"), + PYTHON_METHOD(ptSpawnPointInfo, setName, "Params: name\nSets the spawnpoint's name"), + PYTHON_METHOD_NOARGS(ptSpawnPointInfo, getCameraStack, "Returns the camera stack for this spawnpoint as a string"), + PYTHON_METHOD(ptSpawnPointInfo, setCameraStack, "Params: stack\nSets the spawnpoint's camera stack (as a string)"), +PYTHON_END_METHODS_TABLE; + +// type structure definition +PLASMA_DEFAULT_TYPE(ptSpawnPointInfo, "Params: title=None,spawnPt=None\nClass to hold spawn point data"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptSpawnPointInfo, pySpawnPointInfo) + +PyObject *pySpawnPointInfo::New(const plSpawnPointInfo& info) +{ + ptSpawnPointInfo *newObj = (ptSpawnPointInfo*)ptSpawnPointInfo_type.tp_new(&ptSpawnPointInfo_type, NULL, NULL); + newObj->fThis->fInfo = info; + return (PyObject*)newObj; +} + +PyObject *pySpawnPointInfo::New(const char* title, const char* spawnPt) +{ + ptSpawnPointInfo *newObj = (ptSpawnPointInfo*)ptSpawnPointInfo_type.tp_new(&ptSpawnPointInfo_type, NULL, NULL); + newObj->fThis->fInfo.fTitle = title; + newObj->fThis->fInfo.fSpawnPt = spawnPt; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptSpawnPointInfo, pySpawnPointInfo) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptSpawnPointInfo, pySpawnPointInfo) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pySpawnPointInfo::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptSpawnPointInfo); + PYTHON_CLASS_IMPORT_END(m); +} + +PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetDefaultSpawnPoint, "Returns the default spawnpoint definition (as a ptSpawnPointInfo)") +{ + return pySpawnPointInfo::GetDefaultSpawnPoint(); +} + +void pySpawnPointInfo::AddPlasmaMethods(std::vector &methods) +{ + PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetDefaultSpawnPoint); +} + +// glue functions +PYTHON_CLASS_DEFINITION(ptSpawnPointInfoRef, pySpawnPointInfoRef); + +PYTHON_DEFAULT_NEW_DEFINITION(ptSpawnPointInfoRef, pySpawnPointInfoRef) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptSpawnPointInfoRef) + +PYTHON_NO_INIT_DEFINITION(ptSpawnPointInfoRef) + +PYTHON_METHOD_DEFINITION_NOARGS(ptSpawnPointInfoRef, getTitle) +{ + return PyString_FromString(self->fThis->GetTitle()); +} + +PYTHON_METHOD_DEFINITION(ptSpawnPointInfoRef, setTitle, args) +{ + char* title; + if (!PyArg_ParseTuple(args, "s", &title)) + { + PyErr_SetString(PyExc_TypeError, "setTitle expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetTitle(title); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSpawnPointInfoRef, getName) +{ + return PyString_FromString(self->fThis->GetName()); +} + +PYTHON_METHOD_DEFINITION(ptSpawnPointInfoRef, setName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "setName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptSpawnPointInfoRef, getCameraStack) +{ + return PyString_FromString(self->fThis->GetCameraStack()); +} + +PYTHON_METHOD_DEFINITION(ptSpawnPointInfoRef, setCameraStack, args) +{ + char* camStack; + if (!PyArg_ParseTuple(args, "s", &camStack)) + { + PyErr_SetString(PyExc_TypeError, "setCameraStack expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetCameraStack(camStack); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptSpawnPointInfoRef) + PYTHON_METHOD_NOARGS(ptSpawnPointInfoRef, getTitle, "Returns the spawnpoint's title"), + PYTHON_METHOD(ptSpawnPointInfoRef, setTitle, "Params: title\nSets the spawnpoint's title"), + PYTHON_METHOD_NOARGS(ptSpawnPointInfoRef, getName, "Returns the spawnpoint's name"), + PYTHON_METHOD(ptSpawnPointInfoRef, setName, "Params: name\nSets the spawnpoint's name"), + PYTHON_METHOD_NOARGS(ptSpawnPointInfoRef, getCameraStack, "Returns the camera stack for this spawnpoint as a string"), + PYTHON_METHOD(ptSpawnPointInfoRef, setCameraStack, "Params: stack\nSets the spawnpoint's camera stack (as a string)"), +PYTHON_END_METHODS_TABLE; + +// type structure definition +PLASMA_DEFAULT_TYPE(ptSpawnPointInfoRef, "Class to hold spawn point data"); + +// required functions for PyObject interoperability +PyObject *pySpawnPointInfoRef::New(plSpawnPointInfo& info) +{ + ptSpawnPointInfoRef *newObj = (ptSpawnPointInfoRef*)ptSpawnPointInfoRef_type.tp_new(&ptSpawnPointInfoRef_type, NULL, NULL); + newObj->fThis->fInfo = info; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptSpawnPointInfoRef, pySpawnPointInfoRef) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptSpawnPointInfoRef, pySpawnPointInfoRef) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pySpawnPointInfoRef::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptSpawnPointInfoRef); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLog.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLog.cpp new file mode 100644 index 00000000..8eb05a04 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLog.cpp @@ -0,0 +1,105 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyStatusLog - a wrapper class to provide interface to the plStatusLog stuff +// +// and interface to the ChatLog (ptChatStatusLog) +////////////////////////////////////////////////////////////////////// + +#include "pyStatusLog.h" + +#include "../plStatusLog/plStatusLog.h" + +pyStatusLog::pyStatusLog( plStatusLog* log/*=nil */) +: fLog( log ) +, fICreatedLog( false ) +{ +} + +pyStatusLog::~pyStatusLog() +{ + Close(); +} + + +hsBool pyStatusLog::Open(const char* logName, UInt32 numLines, UInt32 flags) +{ + // make sure its closed first + Close(); + + // create a status log guy for this + fICreatedLog = true; + fLog = plStatusLogMgr::GetInstance().CreateStatusLog( (UInt8)numLines, logName, flags ); + if (fLog) + { + fLog->SetForceLog(true); + return true; + } + return false; +} + +hsBool pyStatusLog::Write(const char* text) +{ + if (fLog) + { + fLog->AddLine(text); + return true; + } + + return false; +} + +hsBool pyStatusLog::WriteColor(const char* text, pyColor& color) +{ + if (fLog) + { + UInt32 st_color = ((UInt32)(color.getAlpha()*255)<<24) + + ((UInt32)(color.getRed()*255)<<16) + + ((UInt32)(color.getGreen()*255)<<8) + + ((UInt32)(color.getBlue()*255)); + fLog->AddLine( text, st_color ); + return true; + } + + return false; +} + +void pyStatusLog::Close() +{ + if (fLog && fICreatedLog) + { + delete fLog; + } + fLog = nil; +} + +hsBool pyStatusLog::IsOpen() +{ + if (fLog) + return true; + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLog.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLog.h new file mode 100644 index 00000000..ca723e9a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLog.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyStatusLog_h_inc +#define pyStatusLog_h_inc + +////////////////////////////////////////////////////////////////////// +// +// pyStatusLog - a wrapper class to provide interface to the plStatusLog stuff +// +// and interface to the ChatLog (ptChatStatusLog) +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyColor.h" +class plStatusLog; + + +class pyStatusLog +{ +private: + plStatusLog* fLog; + bool fICreatedLog; + +protected: + pyStatusLog( plStatusLog* log=nil ); + +public: + ~pyStatusLog(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptStatusLog); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(plStatusLog* log); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyStatusLog object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyStatusLog); // converts a PyObject to a pyStatusLog (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + static void AddPlasmaConstantsClasses(PyObject *m); + + virtual hsBool Open(const char* logName, UInt32 numLines, UInt32 flags); + virtual hsBool Write(const char* text); + virtual hsBool WriteColor(const char* text, pyColor& color); + virtual void Close(); + + virtual hsBool IsOpen(); +}; + + +#endif // pyStatusLog_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLogGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLogGlue.cpp new file mode 100644 index 00000000..79e49241 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStatusLogGlue.cpp @@ -0,0 +1,137 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyStatusLog.h" +#include "pyEnum.h" + +#include "../plStatusLog/plStatusLog.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptStatusLog, pyStatusLog); + +PYTHON_DEFAULT_NEW_DEFINITION(ptStatusLog, pyStatusLog) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptStatusLog) + +PYTHON_INIT_DEFINITION(ptStatusLog, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptStatusLog, open, args) +{ + char* logName; + unsigned long numLines, flags; + if (!PyArg_ParseTuple(args, "sll", &logName, &numLines, &flags)) + { + PyErr_SetString(PyExc_TypeError, "open expects a string and two unsigned longs"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->Open(logName, numLines, flags)); +} + +PYTHON_METHOD_DEFINITION(ptStatusLog, write, args) +{ + char* text; + PyObject* colorObj = NULL; + if (!PyArg_ParseTuple(args, "s|O", &text, &colorObj)) + { + PyErr_SetString(PyExc_TypeError, "write expects a string and an optional ptColor"); + PYTHON_RETURN_ERROR; + } + if (colorObj) + { + if (!pyColor::Check(colorObj)) + { + PyErr_SetString(PyExc_TypeError, "write expects a string and an optional ptColor"); + PYTHON_RETURN_ERROR; + } + pyColor* color = pyColor::ConvertFrom(colorObj); + PYTHON_RETURN_BOOL(self->fThis->WriteColor(text, *color)); + } + PYTHON_RETURN_BOOL(self->fThis->Write(text)); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptStatusLog, close, Close); + +PYTHON_METHOD_DEFINITION_NOARGS(ptStatusLog, isOpen) +{ + PYTHON_RETURN_BOOL(self->fThis->IsOpen()); +} + +PYTHON_START_METHODS_TABLE(ptStatusLog) + PYTHON_METHOD(ptStatusLog, open, "Params: logName,numLines,flags\nOpen a status log for writing to\n" + "'logname' is the name of the log file (example: special.log)\n" + "'numLines' is the number of lines to display on debug screen\n" + "'flags' is a PlasmaConstants.PtStatusLogFlags"), + PYTHON_METHOD(ptStatusLog, write, "Params: text,color=None\nIf the status log is open, write 'text' to log\n" + "'color' is the display color in debug screen"), + PYTHON_BASIC_METHOD(ptStatusLog, close, "Close the status log file"), + PYTHON_METHOD_NOARGS(ptStatusLog, isOpen, "Returns whether the status log is currently opened"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptStatusLog, "A status log class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptStatusLog, pyStatusLog) + +PyObject *pyStatusLog::New(plStatusLog* log) +{ + ptStatusLog *newObj = (ptStatusLog*)ptStatusLog_type.tp_new(&ptStatusLog_type, NULL, NULL); + newObj->fThis->fLog = log; + newObj->fThis->fICreatedLog = false; + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptStatusLog, pyStatusLog) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptStatusLog, pyStatusLog) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyStatusLog::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptStatusLog); + PYTHON_CLASS_IMPORT_END(m); +} + +void pyStatusLog::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtStatusLogFlags); + PYTHON_ENUM_ELEMENT(PtStatusLogFlags, kFilledBackground, plStatusLog::kFilledBackground); + PYTHON_ENUM_ELEMENT(PtStatusLogFlags, kAppendToLast, plStatusLog::kAppendToLast); + PYTHON_ENUM_ELEMENT(PtStatusLogFlags, kDontWriteFile, plStatusLog::kDontWriteFile); + PYTHON_ENUM_ELEMENT(PtStatusLogFlags, kDeleteForMe, plStatusLog::kDeleteForMe); + PYTHON_ENUM_ELEMENT(PtStatusLogFlags, kAlignToTop, plStatusLog::kAlignToTop); + PYTHON_ENUM_ELEMENT(PtStatusLogFlags, kDebugOutput, plStatusLog::kDebugOutput); + PYTHON_ENUM_ELEMENT(PtStatusLogFlags, kTimestamp, plStatusLog::kTimestamp); + PYTHON_ENUM_ELEMENT(PtStatusLogFlags, kStdout, plStatusLog::kStdout); + PYTHON_ENUM_ELEMENT(PtStatusLogFlags, kTimeInSeconds, plStatusLog::kTimeInSeconds); + PYTHON_ENUM_ELEMENT(PtStatusLogFlags, kTimeAsDouble, plStatusLog::kTimeAsDouble); + PYTHON_ENUM_END(m, PtStatusLogFlags); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStream.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStream.cpp new file mode 100644 index 00000000..d3a7731c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStream.cpp @@ -0,0 +1,142 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyStream - a wrapper class to provide interface to the File stream stuff +// +////////////////////////////////////////////////////////////////////// + +#include "pyStream.h" + +#include "../plFile/plEncryptedStream.h" + +pyStream::pyStream() +: fStream( nil ) +{ +} + +pyStream::~pyStream() +{ + Close(); +} + + +hsBool pyStream::Open(const wchar* fileName, const wchar* flags) +{ + // make sure its closed first + Close(); + + if (fileName) + { + if (flags) + { + bool readflag = false; + bool writeflag = false; + bool encryptflag = false; + int i; + for (i=0 ; i < wcslen(flags) ; i++ ) + { + if ( flags[i] == L'r' || flags[i] == L'R' ) + readflag = true; + if ( flags[i] == L'w' || flags[i] == L'W' ) + writeflag = true; + if ( flags[i] == L'e' || flags[i] == L'E' ) + encryptflag = true; + } + // if there is a write flag, it takes priorty over read + if (writeflag) + { + // force encryption? + if (encryptflag) + { + fStream = TRACKED_NEW plEncryptedStream; + fStream->Open(fileName, L"wb"); + } + else + fStream = plEncryptedStream::OpenEncryptedFileWrite(fileName); + } + else + fStream = plEncryptedStream::OpenEncryptedFile(fileName); + return true; + } + } + return false; +} + +std::vector pyStream::ReadLines() +{ + + // read all the lines in the file and put in a python list object + // create the list + std::vector pyPL; + + if (fStream) + { + char buf[4096]; + + while (!fStream->AtEnd()) + { + if (fStream->ReadLn(buf, sizeof(buf), 0, 0)) + pyPL.push_back(buf); + } + } + + return pyPL; +} + +hsBool pyStream::WriteLines(const std::vector & lines) +{ + if (fStream) + { + int i; + for ( i=0 ; iWrite(element.length(),element.c_str()); + } + return true; + } + + return false; +} + + +void pyStream::Close() +{ + if (fStream) + { + fStream->Close(); + delete fStream; + } + fStream = nil; +} + +hsBool pyStream::IsOpen() +{ + if (fStream) + return true; + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStream.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStream.h new file mode 100644 index 00000000..34984b93 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStream.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyStream_h_inc +#define pyStream_h_inc + +////////////////////////////////////////////////////////////////////// +// +// pyStream - a wrapper class to provide interface to the File stream stuff +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + + +class hsStream; + +class pyStream +{ +private: + hsStream* fStream; + +protected: + pyStream(); + +public: + ~pyStream(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptStream); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyStream object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyStream); // converts a PyObject to a pyStream (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + virtual hsBool Open(const wchar* fileName, const wchar* flags); + virtual std::vector ReadLines(); + virtual hsBool WriteLines(const std::vector & lines); + virtual void Close(); + + virtual hsBool IsOpen(); +}; + + +#endif // pyStream_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStreamGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStreamGlue.cpp new file mode 100644 index 00000000..c8b81e1b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyStreamGlue.cpp @@ -0,0 +1,172 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyStream.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptStream, pyStream); + +PYTHON_DEFAULT_NEW_DEFINITION(ptStream, pyStream) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptStream) + +PYTHON_INIT_DEFINITION(ptStream, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptStream, open, args) +{ + PyObject* filenameObj; + PyObject* flagsObj; + if (!PyArg_ParseTuple(args, "OO", &filenameObj, &flagsObj)) + { + PyErr_SetString(PyExc_TypeError, "open expects two strings"); + PYTHON_RETURN_ERROR; + } + + std::wstring filename; + if (PyUnicode_Check(filenameObj)) + { + int strLen = PyUnicode_GetSize(filenameObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)filenameObj, text, strLen); + text[strLen] = L'\0'; + filename = text; + delete [] text; + } + else if (PyString_Check(filenameObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(filenameObj); + wchar_t* wText = hsStringToWString(text); + filename = wText; + delete [] wText; + } + else + { + PyErr_SetString(PyExc_TypeError, "open expects two strings"); + PYTHON_RETURN_ERROR; + } + + std::wstring flags; + if (PyUnicode_Check(flagsObj)) + { + int strLen = PyUnicode_GetSize(flagsObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)flagsObj, text, strLen); + text[strLen] = L'\0'; + flags = text; + delete [] text; + } + else if (PyString_Check(flagsObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(flagsObj); + wchar_t* wText = hsStringToWString(text); + flags = wText; + delete [] wText; + } + else + { + PyErr_SetString(PyExc_TypeError, "open expects two strings"); + PYTHON_RETURN_ERROR; + } + + PYTHON_RETURN_BOOL(self->fThis->Open(filename.c_str(), flags.c_str())); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptStream, readlines) +{ + std::vector lines = self->fThis->ReadLines(); + PyObject* retVal = PyList_New(lines.size()); + for (int i = 0; i < lines.size(); i++) + PyList_SetItem(retVal, i, PyString_FromString(lines[i].c_str())); + return retVal; +} + +PYTHON_METHOD_DEFINITION(ptStream, writelines, args) +{ + PyObject* stringList = NULL; + if (!PyArg_ParseTuple(args, "O", &stringList)) + { + PyErr_SetString(PyExc_TypeError, "writelines expects a list of strings"); + PYTHON_RETURN_ERROR; + } + if (!PyList_Check(stringList)) + { + PyErr_SetString(PyExc_TypeError, "writelines expects a list of strings"); + PYTHON_RETURN_ERROR; + } + std::vector strings; + int len = PyList_Size(stringList); + for (int i = 0; i < len; i++) + { + PyObject* element = PyList_GetItem(stringList, i); + if (!PyString_Check(element)) + { + PyErr_SetString(PyExc_TypeError, "writelines expects a list of strings"); + PYTHON_RETURN_ERROR; + } + strings.push_back(PyString_AsString(element)); + } + PYTHON_RETURN_BOOL(self->fThis->WriteLines(strings)); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptStream, close, Close) + +PYTHON_METHOD_DEFINITION_NOARGS(ptStream, isOpen) +{ + PYTHON_RETURN_BOOL(self->fThis->IsOpen()); +} + +PYTHON_START_METHODS_TABLE(ptStream) + PYTHON_METHOD(ptStream, open, "Params: fileName,flags\nOpen a stream file for reading or writing"), + PYTHON_METHOD_NOARGS(ptStream, readlines, "Reads a list of strings from the file"), + PYTHON_METHOD(ptStream, writelines, "Params: lines\nWrite a list of strings to the file"), + PYTHON_BASIC_METHOD(ptStream, close, "Close the status log file"), + PYTHON_METHOD_NOARGS(ptStream, isOpen, "Returns whether the stream file is currently opened"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptStream, "A basic stream class"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptStream, pyStream) + +PYTHON_CLASS_CHECK_IMPL(ptStream, pyStream) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptStream, pyStream) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyStream::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptStream); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterface.cpp new file mode 100644 index 00000000..8c3586fb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterface.cpp @@ -0,0 +1,209 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pySwimCurrentInterface.h" +#include "../plAvatar/plSwimRegion.h" + +pySwimCurrentInterface::pySwimCurrentInterface(plKey key) +{ + fSwimCurrentKey = key; +} + +pySwimCurrentInterface::pySwimCurrentInterface(pyKey& key) +{ + fSwimCurrentKey = key.getKey(); +} + +hsScalar pySwimCurrentInterface::getNearDist() +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimCircularCurrentRegion* circ = plSwimCircularCurrentRegion::ConvertNoRef(obj)) + { + return circ->fPullNearDistSq; + } + else if (plSwimStraightCurrentRegion* straight = plSwimStraightCurrentRegion::ConvertNoRef(obj)) + { + return straight->fNearDist; + } + else + { + return 0; + } +} + +void pySwimCurrentInterface::setNearDist(hsScalar val) +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimCircularCurrentRegion* circ = plSwimCircularCurrentRegion::ConvertNoRef(obj)) + { + circ->fPullNearDistSq = val; + } + else if (plSwimStraightCurrentRegion* straight = plSwimStraightCurrentRegion::ConvertNoRef(obj)) + { + straight->fNearDist = val; + } +} + +hsScalar pySwimCurrentInterface::getFarDist() +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimCircularCurrentRegion* circ = plSwimCircularCurrentRegion::ConvertNoRef(obj)) + { + return circ->fPullFarDistSq; + } + else if (plSwimStraightCurrentRegion* straight = plSwimStraightCurrentRegion::ConvertNoRef(obj)) + { + return straight->fFarDist; + } + else + { + return 0; + } +} + +void pySwimCurrentInterface::setFarDist(hsScalar val) +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimCircularCurrentRegion* circ = plSwimCircularCurrentRegion::ConvertNoRef(obj)) + { + circ->fPullFarDistSq = val; + } + else if (plSwimStraightCurrentRegion* straight = plSwimStraightCurrentRegion::ConvertNoRef(obj)) + { + straight->fFarDist = val; + } +} + +hsScalar pySwimCurrentInterface::getNearVel() +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimCircularCurrentRegion* circ = plSwimCircularCurrentRegion::ConvertNoRef(obj)) + { + return circ->fPullNearVel; + } + else if (plSwimStraightCurrentRegion* straight = plSwimStraightCurrentRegion::ConvertNoRef(obj)) + { + return straight->fNearVel; + } + else + { + return 0; + } +} + +void pySwimCurrentInterface::setNearVel(hsScalar val) +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimCircularCurrentRegion* circ = plSwimCircularCurrentRegion::ConvertNoRef(obj)) + { + circ->fPullNearVel = val; + } + else if (plSwimStraightCurrentRegion* straight = plSwimStraightCurrentRegion::ConvertNoRef(obj)) + { + straight->fNearVel = val; + } +} + +hsScalar pySwimCurrentInterface::getFarVel() +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimCircularCurrentRegion* circ = plSwimCircularCurrentRegion::ConvertNoRef(obj)) + { + return circ->fPullFarVel; + } + else if (plSwimStraightCurrentRegion* straight = plSwimStraightCurrentRegion::ConvertNoRef(obj)) + { + return straight->fFarVel; + } + else + { + return 0; + } +} + +void pySwimCurrentInterface::setFarVel(hsScalar val) +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimCircularCurrentRegion* circ = plSwimCircularCurrentRegion::ConvertNoRef(obj)) + { + circ->fPullFarVel = val; + } + else if (plSwimStraightCurrentRegion* straight = plSwimStraightCurrentRegion::ConvertNoRef(obj)) + { + straight->fFarVel = val; + } +} + +hsScalar pySwimCurrentInterface::getRotation() +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimCircularCurrentRegion* circ = plSwimCircularCurrentRegion::ConvertNoRef(obj)) + { + return circ->fRotation; + } + else + { + return 0; + } +} + +void pySwimCurrentInterface::setRotation(hsScalar val) +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimCircularCurrentRegion* circ = plSwimCircularCurrentRegion::ConvertNoRef(obj)) + { + circ->fRotation = val; + } +} + +void pySwimCurrentInterface::enable() +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimRegionInterface* regInt = plSwimRegionInterface::ConvertNoRef(obj)) + { + regInt->SetProperty(plSwimRegionInterface::kDisable, 0); + } +} + +void pySwimCurrentInterface::disable() +{ + hsKeyedObject* obj = fSwimCurrentKey->ObjectIsLoaded(); + + if (plSwimRegionInterface* regInt = plSwimRegionInterface::ConvertNoRef(obj)) + { + regInt->SetProperty(plSwimRegionInterface::kDisable, 1); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterface.h new file mode 100644 index 00000000..54949988 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterface.h @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pySwimCurrentInterface_h +#define pySwimCurrentInterface_h + +#include "pyKey.h" + +#include +#include "pyGlueHelpers.h" + +class pySwimCurrentInterface +{ +private: + plKey fSwimCurrentKey; + +protected: + pySwimCurrentInterface(): fSwimCurrentKey(nil) {} // for python glue only, do NOT call + pySwimCurrentInterface(plKey key); + pySwimCurrentInterface(pyKey& key); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptSwimCurrentInterface); + static PyObject *New(plKey key); + static PyObject *New(pyKey& key); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pySwimCurrentInterface object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pySwimCurrentInterface); // converts a PyObject to a pySwimCurrentInterface (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + void setKey(pyKey& key) {fSwimCurrentKey = key.getKey();} // for python glue only, do NOT call + + hsScalar getNearDist(); + void setNearDist(hsScalar val); + + hsScalar getFarDist(); + void setFarDist(hsScalar val); + + hsScalar getNearVel(); + void setNearVel(hsScalar val); + + hsScalar getFarVel(); + void setFarVel(hsScalar val); + + hsScalar getRotation(); + void setRotation(hsScalar val); + + void enable(); + void disable(); +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterfaceGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterfaceGlue.cpp new file mode 100644 index 00000000..d0d4706b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pySwimCurrentInterfaceGlue.cpp @@ -0,0 +1,218 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pySwimCurrentInterface.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptSwimCurrentInterface, pySwimCurrentInterface); + +PYTHON_DEFAULT_NEW_DEFINITION(ptSwimCurrentInterface, pySwimCurrentInterface) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptSwimCurrentInterface) + +PYTHON_INIT_DEFINITION(ptSwimCurrentInterface, args, keywords) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->setKey(*key); + PYTHON_RETURN_INIT_OK; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptSwimCurrentInterface, enable, enable) +PYTHON_BASIC_METHOD_DEFINITION(ptSwimCurrentInterface, disable, disable) + +PYTHON_START_METHODS_TABLE(ptSwimCurrentInterface) + PYTHON_BASIC_METHOD(ptSwimCurrentInterface, enable, "UNKNOWN"), + PYTHON_BASIC_METHOD(ptSwimCurrentInterface, disable, "UNKNOWN"), +PYTHON_END_METHODS_TABLE; + +PYTHON_GET_DEFINITION(ptSwimCurrentInterface, nearDistance) +{ + return PyFloat_FromDouble(self->fThis->getNearDist()); +} + +PYTHON_SET_DEFINITION(ptSwimCurrentInterface, nearDistance, newValue) +{ + if (!newValue) + { + PyErr_SetString(PyExc_TypeError, "nearDistance cannot be deleted"); + PYTHON_RETURN_SET_ERROR; + } + if (!PyFloat_Check(newValue)) + { + PyErr_SetString(PyExc_TypeError, "nearDistance expects a floating point value"); + PYTHON_RETURN_SET_ERROR; + } + float val = (float)PyFloat_AsDouble(newValue); + self->fThis->setNearDist(val); + PYTHON_RETURN_SET_OK; +} + +PYTHON_GET_DEFINITION(ptSwimCurrentInterface, farDistance) +{ + return PyFloat_FromDouble(self->fThis->getFarDist()); +} + +PYTHON_SET_DEFINITION(ptSwimCurrentInterface, farDistance, newValue) +{ + if (!newValue) + { + PyErr_SetString(PyExc_TypeError, "farDistance cannot be deleted"); + PYTHON_RETURN_SET_ERROR; + } + if (!PyFloat_Check(newValue)) + { + PyErr_SetString(PyExc_TypeError, "farDistance expects a floating point value"); + PYTHON_RETURN_SET_ERROR; + } + float val = (float)PyFloat_AsDouble(newValue); + self->fThis->setFarDist(val); + PYTHON_RETURN_SET_OK; +} + +PYTHON_GET_DEFINITION(ptSwimCurrentInterface, nearVelocity) +{ + return PyFloat_FromDouble(self->fThis->getNearVel()); +} + +PYTHON_SET_DEFINITION(ptSwimCurrentInterface, nearVelocity, newValue) +{ + if (!newValue) + { + PyErr_SetString(PyExc_TypeError, "nearVelocity cannot be deleted"); + PYTHON_RETURN_SET_ERROR; + } + if (!PyFloat_Check(newValue)) + { + PyErr_SetString(PyExc_TypeError, "nearVelocity expects a floating point value"); + PYTHON_RETURN_SET_ERROR; + } + float val = (float)PyFloat_AsDouble(newValue); + self->fThis->setNearVel(val); + PYTHON_RETURN_SET_OK; +} + +PYTHON_GET_DEFINITION(ptSwimCurrentInterface, farVelocity) +{ + return PyFloat_FromDouble(self->fThis->getFarVel()); +} + +PYTHON_SET_DEFINITION(ptSwimCurrentInterface, farVelocity, newValue) +{ + if (!newValue) + { + PyErr_SetString(PyExc_TypeError, "farVelocity cannot be deleted"); + PYTHON_RETURN_SET_ERROR; + } + if (!PyFloat_Check(newValue)) + { + PyErr_SetString(PyExc_TypeError, "farVelocity expects a floating point value"); + PYTHON_RETURN_SET_ERROR; + } + float val = (float)PyFloat_AsDouble(newValue); + self->fThis->setFarVel(val); + PYTHON_RETURN_SET_OK; +} + +PYTHON_GET_DEFINITION(ptSwimCurrentInterface, rotation) +{ + return PyFloat_FromDouble(self->fThis->getRotation()); +} + +PYTHON_SET_DEFINITION(ptSwimCurrentInterface, rotation, newValue) +{ + if (!newValue) + { + PyErr_SetString(PyExc_TypeError, "rotation cannot be deleted"); + PYTHON_RETURN_SET_ERROR; + } + if (!PyFloat_Check(newValue)) + { + PyErr_SetString(PyExc_TypeError, "rotation expects a floating point value"); + PYTHON_RETURN_SET_ERROR; + } + float val = (float)PyFloat_AsDouble(newValue); + self->fThis->setRotation(val); + PYTHON_RETURN_SET_OK; +} + +PYTHON_START_GETSET_TABLE(ptSwimCurrentInterface) + PYTHON_GETSET(ptSwimCurrentInterface, nearDistance, "UNKNOWN"), + PYTHON_GETSET(ptSwimCurrentInterface, farDistance, "UNKNOWN"), + PYTHON_GETSET(ptSwimCurrentInterface, nearVelocity, "UNKNOWN"), + PYTHON_GETSET(ptSwimCurrentInterface, farVelocity, "UNKNOWN"), + PYTHON_GETSET(ptSwimCurrentInterface, rotation, "UNKNOWN"), +PYTHON_END_GETSET_TABLE; + +// Type structure definition +#define ptSwimCurrentInterface_COMPARE PYTHON_NO_COMPARE +#define ptSwimCurrentInterface_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptSwimCurrentInterface_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptSwimCurrentInterface_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptSwimCurrentInterface_STR PYTHON_NO_STR +#define ptSwimCurrentInterface_RICH_COMPARE PYTHON_NO_RICH_COMPARE +#define ptSwimCurrentInterface_GETSET PYTHON_DEFAULT_GETSET(ptSwimCurrentInterface) +#define ptSwimCurrentInterface_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptSwimCurrentInterface, "Params: key\nCreates a new ptSwimCurrentInterface"); + +// required functions for PyObject interoperability +PyObject *pySwimCurrentInterface::New(plKey key) +{ + ptSwimCurrentInterface *newObj = (ptSwimCurrentInterface*)ptSwimCurrentInterface_type.tp_new(&ptSwimCurrentInterface_type, NULL, NULL); + newObj->fThis->fSwimCurrentKey = key; + return (PyObject*)newObj; +} + +PyObject *pySwimCurrentInterface::New(pyKey& key) +{ + ptSwimCurrentInterface *newObj = (ptSwimCurrentInterface*)ptSwimCurrentInterface_type.tp_new(&ptSwimCurrentInterface_type, NULL, NULL); + newObj->fThis->fSwimCurrentKey = key.getKey(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptSwimCurrentInterface, pySwimCurrentInterface) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptSwimCurrentInterface, pySwimCurrentInterface) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pySwimCurrentInterface::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptSwimCurrentInterface); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVault.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVault.cpp new file mode 100644 index 00000000..ef7c169c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVault.cpp @@ -0,0 +1,694 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVault - a wrapper class to provide interface to the plVault +// +////////////////////////////////////////////////////////////////////// + +#ifdef BUILDING_PYPLASMA +# error "pyVault is not compatible with pyPlasma.pyd. Use BUILDING_PYPLASMA macro to ifdef out unwanted headers." +#endif + +#include "pyVault.h" +#include "pyVaultNode.h" +#include "pyVaultAgeInfoNode.h" +#include "pyVaultAgeInfoListNode.h" +#include "pyVaultAgeLinkNode.h" +#include "pyVaultFolderNode.h" +#include "pyVaultPlayerInfoListNode.h" +#include "pyVaultPlayerInfoNode.h" +#include "pyVaultChronicleNode.h" +#include "pyVaultTextNoteNode.h" +#include "pyNetLinkingMgr.h" +#include "pyAgeInfoStruct.h" +#include "pyAgeLinkStruct.h" +#include "pySDL.h" + +#include "../pnKeyedObject/plKey.h" +#include "cyPythonInterface.h" + +#include "../plVault/plVault.h" +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetClient/plNetLinkingMgr.h" +#include "../plNetClientComm/plNetClientComm.h" +#include "../plMessage/plVaultNotifyMsg.h" + +#include "../plSDL/plSDL.h" + + +//============================================================================ +static PyObject * GetFolder (unsigned folderType) { + PyObject * result = nil; + if (RelVaultNode * rvnPlr = VaultGetPlayerNodeIncRef()) { + if (RelVaultNode * rvnFldr = rvnPlr->GetChildFolderNodeIncRef(folderType, 1)) { + result = pyVaultFolderNode::New(rvnFldr); + rvnFldr->DecRef(); + } + rvnPlr->DecRef(); + } + + return result; +} + +//============================================================================ +static PyObject * GetPlayerInfoList (unsigned folderType) { + PyObject * result = nil; + if (RelVaultNode * rvnPlr = VaultGetPlayerNodeIncRef()) { + if (RelVaultNode * rvnFldr = rvnPlr->GetChildPlayerInfoListNodeIncRef(folderType, 1)) { + result = pyVaultPlayerInfoListNode::New(rvnFldr); + rvnFldr->DecRef(); + } + rvnPlr->DecRef(); + } + + return result; +} + +//============================================================================ +static PyObject * GetAgeInfoList (unsigned folderType) { + PyObject * result = nil; + if (RelVaultNode * rvnPlr = VaultGetPlayerNodeIncRef()) { + if (RelVaultNode * rvnFldr = rvnPlr->GetChildAgeInfoListNodeIncRef(folderType, 1)) { + result = pyVaultAgeInfoListNode::New(rvnFldr); + rvnFldr->DecRef(); + } + rvnPlr->DecRef(); + } + + return result; +} + +////////////////////////////////////////////////// +PyObject* pyVault::GetPlayerInfo( void ) +{ + PyObject * result = nil; + if (RelVaultNode * rvnPlr = VaultGetPlayerNodeIncRef()) { + if (RelVaultNode * rvnPlrInfo = rvnPlr->GetChildNodeIncRef(plVault::kNodeType_PlayerInfo, 1)) { + result = pyVaultPlayerInfoNode::New(rvnPlrInfo); + rvnPlrInfo->DecRef(); + } + rvnPlr->DecRef(); + } + + // just return an empty node + if (!result) + result = pyVaultPlayerInfoNode::New(nil); + + return result; +} + + +PyObject* pyVault::GetAvatarOutfitFolder( void ) +{ + PyObject * result = GetFolder(plVault::kAvatarOutfitFolder); + + // if good then return py object + if (result) + return result; + + // otherwise return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyVault::GetAvatarClosetFolder( void ) +{ + PyObject * result = GetFolder(plVault::kAvatarClosetFolder); + + // if good then return py object + if (result) + return result; + + // otherwise return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyVault::GetChronicleFolder( void ) +{ + PyObject * result = GetFolder(plVault::kChronicleFolder); + + // if good then return py object + if (result) + return result; + + // otherwise return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyVault::GetInbox( void ) +{ + PyObject * result = GetFolder(plVault::kInboxFolder); + + // if good then return py object + if (result) + return result; + + // otherwise return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyVault::GetAgeJournalsFolder( void ) +{ + PyObject * result = GetFolder(plVault::kAgeJournalsFolder); + + // just return an empty node + if (!result) + result = pyVaultFolderNode::New(nil); + + return result; +} + +// finds the stats for the players vault +// ...such as how many pictures, notes and markers they have +PyObject* pyVault::GetKIUsage(void) +{ + UInt32 pictures = 0; + UInt32 notes = 0; + UInt32 markerGames = 0; + + for (;;) { + RelVaultNode * rvnPlr = VaultGetPlayerNodeIncRef(); + if (!rvnPlr) + break; + + for (;;) { + RelVaultNode * rvnAgeJrnlz = rvnPlr->GetChildFolderNodeIncRef(plVault::kAgeJournalsFolder, 1); + if (!rvnAgeJrnlz) + break; + + // Get child nodes up to two levels deep + ARRAY(RelVaultNode*) nodeArr; + rvnAgeJrnlz->GetChildNodesIncRef(2, &nodeArr); + + RelVaultNode ** cur = nodeArr.Ptr(); + RelVaultNode ** end = nodeArr.Term(); + for (; cur != end; ++cur) { + RelVaultNode * rvn = *cur; + if (rvn->nodeType == plVault::kNodeType_Image) + ++pictures; + else if (rvn->nodeType == plVault::kNodeType_TextNote) + ++notes; + else if (rvn->nodeType == plVault::kNodeType_MarkerGame) + ++markerGames; + rvn->DecRef(); + } + + rvnAgeJrnlz->DecRef(); + break; + } + rvnPlr->DecRef(); + break; + } + + // create the tuple of usage numbers + PyObject* retVal = PyTuple_New(4); + PyTuple_SetItem(retVal, 0, PyLong_FromUnsignedLong(pictures)); + PyTuple_SetItem(retVal, 1, PyLong_FromUnsignedLong(notes)); + PyTuple_SetItem(retVal, 2, PyLong_FromUnsignedLong(markerGames)); + return retVal; +} + + +PyObject* pyVault::GetIgnoreListFolder( void ) +{ + PyObject * result = GetPlayerInfoList(plVault::kIgnoreListFolder); + + // if good then return py object + if (result) + return result; + + // otherwise return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyVault::GetBuddyListFolder( void ) +{ + PyObject * result = GetPlayerInfoList(plVault::kBuddyListFolder); + + // if good then return py object + if (result) + return result; + + // otherwise return a None object + PYTHON_RETURN_NONE; +} + +PyObject* pyVault::GetPeopleIKnowAboutFolder( void ) +{ + PyObject * result = GetPlayerInfoList(plVault::kPeopleIKnowAboutFolder); + + // if good then return py object + if (result) + return result; + + // otherwise return a None object + PYTHON_RETURN_NONE; +} + +///////// + +PyObject* pyVault::GetLinkToMyNeighborhood() const +{ + plAgeInfoStruct info; + info.SetAgeFilename(kNeighborhoodAgeFilename); + + if (RelVaultNode * rvn = VaultGetOwnedAgeLinkIncRef(&info)) { + PyObject * result = pyVaultAgeLinkNode::New(rvn); + rvn->DecRef(); + return result; + } + + PYTHON_RETURN_NONE; +} + +PyObject* pyVault::GetLinkToCity() const +{ + plAgeInfoStruct info; + info.SetAgeFilename(kCityAgeFilename); + + if (RelVaultNode * rvn = VaultGetOwnedAgeLinkIncRef(&info)) { + PyObject * result = pyVaultAgeLinkNode::New(rvn); + rvn->DecRef(); + return result; + } + + PYTHON_RETURN_NONE; +} + + +// Owned ages +PyObject* pyVault::GetOwnedAgeLink( const pyAgeInfoStruct & info ) +{ + if (RelVaultNode * rvnLink = VaultGetOwnedAgeLinkIncRef(info.GetAgeInfo())) { + PyObject * result = pyVaultAgeLinkNode::New(rvnLink); + rvnLink->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + +// Visit ages +PyObject* pyVault::GetVisitAgeLink( const pyAgeInfoStruct & info) +{ + if (RelVaultNode * rvnLink = VaultGetVisitAgeLinkIncRef(info.GetAgeInfo())) { + PyObject * result = pyVaultAgeLinkNode::New(rvnLink); + rvnLink->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + + +/////////////// +// Chronicle +PyObject* pyVault::FindChronicleEntry( const char * entryName ) +{ + wchar wEntryName[kMaxVaultNodeStringLength]; + StrToUnicode(wEntryName, entryName, arrsize(wEntryName)); + + if (RelVaultNode * rvn = VaultFindChronicleEntryIncRef(wEntryName)) { + PyObject * result = pyVaultChronicleNode::New(rvn); + rvn->DecRef(); + return result; + } + + // just return a None object + PYTHON_RETURN_NONE; +} + +void pyVault::AddChronicleEntry( const char * name, UInt32 type, const char * value ) +{ + wchar * wEntryName = StrDupToUnicode(name); + wchar * wEntryValue = StrDupToUnicode(value); + + VaultAddChronicleEntryAndWait(wEntryName, type, wEntryValue); + + FREE(wEntryName); + FREE(wEntryValue); +} + + +void pyVault::SendToDevice( pyVaultNode& node, const char * deviceName ) +{ + if (!node.GetNode()) + return; + + wchar wDevName[256]; + StrToUnicode(wDevName, deviceName, arrsize(wDevName)); + VaultPublishNode(node.GetNode()->nodeId, wDevName); +} + + +PyObject* pyVault::GetAgesICanVisitFolder(void) +{ + PyObject * result = GetAgeInfoList(plVault::kAgesICanVisitFolder); + + // if good then return py object + if (result) + return result; + + // otherwise return a None object + PYTHON_RETURN_NONE; +} + + +PyObject* pyVault::GetAgesIOwnFolder(void) +{ + PyObject * result = GetAgeInfoList(plVault::kAgesIOwnFolder); + + // if good then return py object + if (result) + return result; + + // otherwise return a None object + PYTHON_RETURN_NONE; +} + + +PyObject* pyVault::GetInviteFolder(void) +{ + PyObject * result = GetFolder(plVault::kPlayerInviteFolder); + + // if good then return py object + if (result) + return result; + + // otherwise return a None object + PYTHON_RETURN_NONE; +} + + +PyObject* pyVault::GetPsnlAgeSDL() const +{ + PyObject * result = nil; + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + + if (RelVaultNode * rvnFldr = VaultGetAgesIOwnFolderIncRef()) { + + templateNode->fieldFlags = 0; + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + VaultAgeInfoNode ageInfo(templateNode); + wchar str[MAX_PATH]; + StrToUnicode(str, kPersonalAgeFilename, arrsize(str)); + ageInfo.SetAgeFilename(str); + + if (RelVaultNode * rvnInfo = rvnFldr->GetChildNodeIncRef(templateNode, 2)) { + + templateNode->fieldFlags = 0; + templateNode->SetNodeType(plVault::kNodeType_SDL); + + if (RelVaultNode * rvnSdl = rvnInfo->GetChildNodeIncRef(templateNode, 1)) { + VaultSDLNode sdl(rvnSdl); + plStateDataRecord * rec = NEWZERO(plStateDataRecord); + if (sdl.GetStateDataRecord(rec, plSDL::kKeepDirty)) + result = pySDLStateDataRecord::New(rec); + else + DEL(rec); + rvnSdl->DecRef(); + } + rvnInfo->DecRef(); + } + rvnFldr->DecRef(); + } + + templateNode->DecRef(); + + if (!result) + PYTHON_RETURN_NONE; + + return result; +} + +void pyVault::UpdatePsnlAgeSDL( pySDLStateDataRecord & pyrec ) +{ + plStateDataRecord * rec = pyrec.GetRec(); + if ( !rec ) + return; + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + + if (RelVaultNode * rvnFldr = VaultGetAgesIOwnFolderIncRef()) { + + templateNode->fieldFlags = 0; + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + VaultAgeInfoNode ageInfo(templateNode); + wchar str[MAX_PATH]; + StrToUnicode(str, kPersonalAgeFilename, arrsize(str)); + ageInfo.SetAgeFilename(str); + + if (RelVaultNode * rvnInfo = rvnFldr->GetChildNodeIncRef(templateNode, 2)) { + + templateNode->fieldFlags = 0; + templateNode->SetNodeType(plVault::kNodeType_SDL); + + if (RelVaultNode * rvnSdl = rvnInfo->GetChildNodeIncRef(templateNode, 1)) { + VaultSDLNode sdl(rvnSdl); + sdl.SetStateDataRecord(rec, plSDL::kDirtyOnly | plSDL::kTimeStampOnRead); + rvnSdl->DecRef(); + } + rvnInfo->DecRef(); + } + rvnFldr->DecRef(); + } + + templateNode->DecRef(); +} + +bool pyVault::InMyPersonalAge( void ) const +{ + return VaultAmInMyPersonalAge(); +} + +bool pyVault::InMyNeighborhoodAge( void ) const +{ + return VaultAmInMyNeighborhoodAge(); +} + +bool pyVault::AmOwnerOfCurrentAge() const +{ + return VaultAmOwnerOfCurrentAge(); +} + +bool pyVault::AmCzarOfCurrentAge() const +{ + return VaultAmCzarOfCurrentAge(); +} + +bool pyVault::AmAgeOwner( const pyAgeInfoStruct * ageInfo ) +{ + if (!ageInfo->GetAgeInfo()) + return false; + + Uuid ageInstId = *ageInfo->GetAgeInfo()->GetAgeInstanceGuid(); + return VaultAmOwnerOfAge(ageInstId); +} + +bool pyVault::AmAgeCzar( const pyAgeInfoStruct * ageInfo ) +{ + if (!ageInfo->GetAgeInfo()) + return false; + + Uuid ageInstId = *ageInfo->GetAgeInfo()->GetAgeInstanceGuid(); + return VaultAmCzarOfAge(ageInstId); +} + +void pyVault::RegisterMTStation( const char * stationName, const char * backLinkSpawnPtObjName ) +{ + wchar wStationName[256]; + wchar wSpawnPt[256]; + StrToUnicode(wStationName, stationName, arrsize(wStationName)); + StrToUnicode(wSpawnPt, backLinkSpawnPtObjName, arrsize(wSpawnPt)); + VaultRegisterMTStationAndWait( wStationName, wSpawnPt); +} + +void pyVault::RegisterOwnedAge( const pyAgeLinkStruct & link ) +{ + VaultRegisterOwnedAgeAndWait(link.GetAgeLink()); +} + +void pyVault::UnRegisterOwnedAge( const char * ageFilename ) +{ + plAgeInfoStruct info; + info.SetAgeFilename(ageFilename); + VaultUnregisterOwnedAgeAndWait(&info); +} + +void pyVault::RegisterVisitAge( const pyAgeLinkStruct & link ) +{ + VaultRegisterVisitAgeAndWait(link.GetAgeLink()); +} + +void pyVault::UnRegisterVisitAge( const char * guidstr ) +{ + Uuid uuid; + GuidFromString(guidstr, &uuid); + plAgeInfoStruct info; + info.SetAgeInstanceGuid(&plUUID(uuid)); + VaultUnregisterVisitAgeAndWait(&info); +} + +void pyVault::InvitePlayerToAge( const pyAgeLinkStruct & link, UInt32 playerID ) +{ + ENetError error; + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_TextNote); + VaultTextNoteNode visitAcc(templateNode); + visitAcc.SetNoteType(plVault::kNoteType_Visit); + visitAcc.SetVisitInfo(*link.GetAgeLink()->GetAgeInfo()); + if (RelVaultNode * rvn = VaultCreateNodeAndWaitIncRef(templateNode, &error)) { + VaultSendNode(rvn, playerID); + rvn->DecRef(); + } + templateNode->DecRef(); +} + +void pyVault::UnInvitePlayerToAge( const char * str, UInt32 playerID ) +{ + plAgeInfoStruct info; + info.SetAgeInstanceGuid(&plUUID(str)); + + if (RelVaultNode * rvnLink = VaultGetOwnedAgeLinkIncRef(&info)) { + if (RelVaultNode * rvnInfo = rvnLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + VaultAgeInfoNode ageInfo(rvnInfo); + ageInfo.CopyTo(&info); + rvnInfo->DecRef(); + } + + rvnLink->DecRef(); + } + + ENetError error; + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_TextNote); + VaultTextNoteNode visitAcc(templateNode); + visitAcc.SetNoteType(plVault::kNoteType_UnVisit); + visitAcc.SetVisitInfo(info); + if (RelVaultNode * rvn = VaultCreateNodeAndWaitIncRef(templateNode, &error)) { + VaultSendNode(rvn, playerID); + rvn->DecRef(); + } + templateNode->DecRef(); +} + +void pyVault::OfferLinkToPlayer( const pyAgeLinkStruct & link, UInt32 playerID ) +{ + hsAssert(false, "eric, port me"); +} + +void pyVault::CreateNeighborhood() +{ + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + // Unregister old hood + plAgeInfoStruct info; + info.SetAgeFilename(kNeighborhoodAgeFilename); + VaultUnregisterOwnedAgeAndWait(&info); + + // Register new hood + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename(kNeighborhoodAgeFilename); + link.GetAgeInfo()->SetAgeInstanceName(kNeighborhoodAgeInstanceName); + + std::string title; + std::string desc; + + unsigned nameLen = StrLen(nc->GetPlayerName()); + if (nc->GetPlayerName()[nameLen - 1] == 's' || nc->GetPlayerName()[nameLen - 1] == 'S') + { + xtl::format( title, "%s'", nc->GetPlayerName() ); + xtl::format( desc, "%s' %s", nc->GetPlayerName(), link.GetAgeInfo()->GetAgeInstanceName() ); + } + else + { + xtl::format( title, "%s's", nc->GetPlayerName() ); + xtl::format( desc, "%s's %s", nc->GetPlayerName(), link.GetAgeInfo()->GetAgeInstanceName() ); + } + + link.GetAgeInfo()->SetAgeInstanceGuid(&plUUID(GuidGenerate())); + link.GetAgeInfo()->SetAgeUserDefinedName( title.c_str() ); + link.GetAgeInfo()->SetAgeDescription( desc.c_str() ); + + VaultRegisterOwnedAgeAndWait(&link); +} + +bool pyVault::SetAgePublic( const pyAgeInfoStruct * ageInfo, bool makePublic ) +{ + return VaultSetOwnedAgePublicAndWait(ageInfo->GetAgeInfo(), makePublic); +} + + +PyObject* pyVault::GetGlobalInbox( void ) +{ + PyObject * result = nil; + if (RelVaultNode * rvnGlobalInbox = VaultGetGlobalInboxIncRef()) { + result = pyVaultFolderNode::New(rvnGlobalInbox); + rvnGlobalInbox->DecRef(); + return result; + } + + PYTHON_RETURN_NONE; +} + + +///////////////////////////////////////////////////////////// + +PyObject* pyVault::FindNode( pyVaultNode* templateNode ) const +{ + // See if we already have a matching node locally + if (RelVaultNode * rvn = VaultGetNodeIncRef(templateNode->GetNode())) { + PyObject * result = pyVaultNode::New(rvn); + rvn->DecRef(); + return result; + } + + // See if a matching node exists on the server + ARRAY(unsigned) nodeIds; + VaultFindNodesAndWait(templateNode->GetNode(), &nodeIds); + + if (nodeIds.Count()) { + // Only fetch the first matching node since this function returns a single node + VaultFetchNodesAndWait(&nodeIds[0], 1); + // If we fetched it successfully then it'll be in our local node cache now + if (RelVaultNode * rvn = VaultGetNodeIncRef(nodeIds[0])) { + PyObject * result = pyVaultNode::New(rvn); + rvn->DecRef(); + return result; + } + } + + PYTHON_RETURN_NONE; +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVault.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVault.h new file mode 100644 index 00000000..26f2be6e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVault.h @@ -0,0 +1,175 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVault_h_ +#define _pyVault_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVault - a wrapper class to provide interface to the plVault and the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" + +#include +#include "pyGlueHelpers.h" + +class plKey; +class plDniCoordinateInfo; + +class pyVaultNode; +class pyVaultAgeLinkNode; +class pyVaultFolderNode; +class pyVaultPlayerInfoListNode; +class pyVaultAgeInfoListNode; +class pyVaultAgeInfoNode; +class pyVaultChronicleNode; +class pyVaultTextNoteNode; + +class pyAgeInfoStruct; +class pyAgeLinkStruct; + +class pySDLStateDataRecord; + + +class pyVault +{ +#ifndef BUILDING_PYPLASMA +protected: + pyVault() {}; + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVault); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVault object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVault); // converts a PyObject to a pyVault (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); +#else +public: +#endif // BUILDING_PYPLASMA + static void AddPlasmaConstantsClasses(PyObject *m); + + enum VaultCallbackType + { + kVaultConnected = 1, + kVaultNodeSaved, + kVaultNodeRefAdded, + kVaultRemovingNodeRef, + kVaultNodeRefRemoved, + kVaultNodeInitialized, + kVaultOperationFailed, + kVaultNodeAdded, + kVaultDisconnected, + }; + +#ifndef BUILDING_PYPLASMA + + ////////////////////////////////////////////////// + PyObject* GetPlayerInfo( void ); // returns pyVaultNode + PyObject* GetKIUsage(void); + PyObject* GetAvatarOutfitFolder( void ); // returns pyVaultFolderNode + PyObject* GetAvatarClosetFolder( void ); // returns pyVaultFolderNode + PyObject* GetInbox( void ); // returns pyVaultFolderNode + PyObject* GetChronicleFolder( void ); // returns pyVaultFolderNode + PyObject* GetAgeJournalsFolder( void ); // returns pyVaultFolderNode + PyObject* GetIgnoreListFolder( void ); // returns pyVaultPlayerInfoListNode + PyObject* GetBuddyListFolder( void ); // returns pyVaultPlayerInfoListNode + PyObject* GetPeopleIKnowAboutFolder( void ); // returns pyVaultPlayerInfoListNode + PyObject* GetAgesICanVisitFolder(); // returns pyVaultFolderNode + PyObject* GetAgesIOwnFolder(); // returns pyVaultFolderNode + PyObject* GetInviteFolder(); // returns pyVaultFolderNode + + /////////////// + PyObject* GetLinkToMyNeighborhood() const; // returns pyVaultAgeLinkNode + PyObject* GetLinkToCity() const; // returns pyVaultAgeLinkNode + /////////////// + // Owned ages + PyObject* GetOwnedAgeLink( const pyAgeInfoStruct & info ); // returns pyVaultAgeLinkNode + // Visit ages + PyObject* GetVisitAgeLink( const pyAgeInfoStruct & info ); // returns pyVaultAgeLinkNode + /////////////// + // Chronicle + PyObject* FindChronicleEntry( const char * entryName ); // returns pyVaultChronicleNode + void AddChronicleEntry( const char * name, UInt32 type, const char * value ); + /////////////// + // publishing + void SendToDevice( pyVaultNode& node, const char * deviceName ); + /////////////// + // yeesha pages, etc. + PyObject* GetPsnlAgeSDL() const; // returns pySDLStateDataRecord + void UpdatePsnlAgeSDL( pySDLStateDataRecord & rec ); + + /////////////// + // true if we are joined to our personal age. + bool InMyPersonalAge( void ) const; + // true if we are joined to our neighborhood age. + bool InMyNeighborhoodAge( void ) const; + // true if we own the age we are in + bool AmOwnerOfCurrentAge() const; + // true if we are czar of the age we are in + bool AmCzarOfCurrentAge() const; + // true if we own the given age + bool AmAgeOwner( const pyAgeInfoStruct * ageInfo ); + // true if we are czar of the given age + bool AmAgeCzar( const pyAgeInfoStruct * ageInfo ); + + /////////////// + // Registser the given age as owned by player. + void RegisterOwnedAge( const pyAgeLinkStruct & link ); + void UnRegisterOwnedAge( const char * ageFilename ); + // Register the given age as visitable by player + void RegisterVisitAge( const pyAgeLinkStruct & link ); + void UnRegisterVisitAge( const char * guid ); + // Register a nexus station + void RegisterMTStation( const char * stationName, const char * mtSpawnPt ); + + /////////////// + // Invite player to visit an age. + void InvitePlayerToAge( const pyAgeLinkStruct & link, UInt32 playerID ); + void UnInvitePlayerToAge( const char * guid, UInt32 playerID ); + // Offer link to player + void OfferLinkToPlayer( const pyAgeLinkStruct & link, UInt32 playerID ); + + /////////////// + // Creates neighborhood and joins the player to it as the mayor/czar. + void CreateNeighborhood(); + // set an age's public status. will fail if you aren't czar of age. + bool SetAgePublic( const pyAgeInfoStruct * ageInfo, bool makePublic ); + + PyObject* GetGlobalInbox( void ); // returns pyVaultFolderNode +#ifdef GlobalInboxTestCode + void CreateGlobalInbox( void ); +#endif + + // find matching node + PyObject* FindNode( pyVaultNode* templateNode ) const; // returns pyVaultNode + +#endif // BUILDING_PYPLASMA +}; + +#endif // _pyVault_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNode.cpp new file mode 100644 index 00000000..f695db36 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNode.cpp @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultAgeInfoListNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultAgeInfoListNode.h" +#include "pyVaultFolderNode.h" + +#include "../plVault/plVault.h" + +// should only be created from C++ side +pyVaultAgeInfoListNode::pyVaultAgeInfoListNode(RelVaultNode* nfsNode) +: pyVaultFolderNode(nfsNode) +{ +} + +//create from the Python side +pyVaultAgeInfoListNode::pyVaultAgeInfoListNode(int n) +: pyVaultFolderNode(n) +{ + fNode->SetNodeType(plVault::kNodeType_AgeInfoList); +} + + +//================================================================== +// class RelVaultNode : public plVaultFolderNode +// +hsBool pyVaultAgeInfoListNode::HasAge(UInt32 ageID) +{ + if (!fNode) + return false; + + hsAssert(false, "eric, implement me"); + return false; +} + +hsBool pyVaultAgeInfoListNode::AddAge( UInt32 ageID ) +{ + if (!fNode) + return false; + + hsAssert(false, "eric, implement me"); + return false; +} + +void pyVaultAgeInfoListNode::RemoveAge( UInt32 ageID ) +{ + if (!fNode) + return; + + hsAssert(false, "eric, implement me"); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNode.h new file mode 100644 index 00000000..4b7eea41 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNode.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultAgeInfoListNode_h_ +#define _pyVaultAgeInfoListNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultAgeInfoListNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyVaultFolderNode.h" + +struct RelVaultNode; + + +class pyVaultAgeInfoListNode : public pyVaultFolderNode +{ +protected: + // should only be created from C++ side + pyVaultAgeInfoListNode(RelVaultNode* nfsNode); + + // python-side ctor + pyVaultAgeInfoListNode(int n=0); +public: + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultAgeInfoListNode); + static PyObject *New(RelVaultNode* nfsNode); + static PyObject *New(int n=0); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultAgeInfoListNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultAgeInfoListNode); // converts a PyObject to a pyVaultAgeInfoListNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + +//================================================================== +// class RelVaultNode : public plVaultFolderNode +// + hsBool HasAge( UInt32 ageID ); + hsBool AddAge( UInt32 ageID ); + void RemoveAge( UInt32 ageID ); +}; + +#endif // _pyVaultAgeInfoListNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNodeGlue.cpp new file mode 100644 index 00000000..2d3f30c2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoListNodeGlue.cpp @@ -0,0 +1,123 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultAgeInfoListNode.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultAgeInfoListNode, pyVaultAgeInfoListNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultAgeInfoListNode, pyVaultAgeInfoListNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultAgeInfoListNode) + +PYTHON_INIT_DEFINITION(ptVaultAgeInfoListNode, args, keywords) +{ + int n = 0; + if (!PyArg_ParseTuple(args, "|i", &n)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional int"); + PYTHON_RETURN_INIT_ERROR; + } + // we don't really do anything? Not according to the associated constructor. Odd... + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeInfoListNode, hasAge, args) +{ + unsigned long ageID; + if (!PyArg_ParseTuple(args, "l", &ageID)) + { + PyErr_SetString(PyExc_TypeError, "hasAge expects a unsigned long"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->HasAge(ageID)); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeInfoListNode, addAge, args) +{ + unsigned long ageID; + if (!PyArg_ParseTuple(args, "l", &ageID)) + { + PyErr_SetString(PyExc_TypeError, "addAge expects a unsigned long"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->AddAge(ageID)); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeInfoListNode, removeAge, args) +{ + unsigned long ageID; + if (!PyArg_ParseTuple(args, "l", &ageID)) + { + PyErr_SetString(PyExc_TypeError, "removeAge expects a unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->RemoveAge(ageID); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptVaultAgeInfoListNode) + PYTHON_METHOD(ptVaultAgeInfoListNode, hasAge, "Params: ageID\nReturns whether ageID is in the list of ages"), + PYTHON_METHOD(ptVaultAgeInfoListNode, addAge, "Params: ageID\nAdds ageID to list of ages"), + PYTHON_METHOD(ptVaultAgeInfoListNode, removeAge, "Params: ageID\nRemoves ageID from list of ages"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultAgeInfoListNode, pyVaultFolderNode, "Params: n=0\nPlasma vault age info list node"); + +// required functions for PyObject interoperability +PyObject *pyVaultAgeInfoListNode::New(RelVaultNode* nfsNode) +{ + ptVaultAgeInfoListNode *newObj = (ptVaultAgeInfoListNode*)ptVaultAgeInfoListNode_type.tp_new(&ptVaultAgeInfoListNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PyObject *pyVaultAgeInfoListNode::New(int n /* =0 */) +{ + ptVaultAgeInfoListNode *newObj = (ptVaultAgeInfoListNode*)ptVaultAgeInfoListNode_type.tp_new(&ptVaultAgeInfoListNode_type, NULL, NULL); + // oddly enough, nothing to do here + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultAgeInfoListNode, pyVaultAgeInfoListNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultAgeInfoListNode, pyVaultAgeInfoListNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultAgeInfoListNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultAgeInfoListNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNode.cpp new file mode 100644 index 00000000..a4fc436f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNode.cpp @@ -0,0 +1,315 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultAgeInfoNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsStlUtils.h" + +#include "pyVaultAgeInfoNode.h" +#include "pyVaultAgeInfoListNode.h" +#include "pyVaultPlayerInfoListNode.h" +#include "pyVaultPlayerInfoNode.h" +#include "pyVaultSDLNode.h" +#include "pyVaultAgeLinkNode.h" +#include "pyNetLinkingMgr.h" +#include "pyAgeInfoStruct.h" + +#include "../plVault/plVault.h" + +// should only be created from C++ side +pyVaultAgeInfoNode::pyVaultAgeInfoNode(RelVaultNode* nfsNode) +: pyVaultNode(nfsNode) +{ +} + +//create from the Python side +pyVaultAgeInfoNode::pyVaultAgeInfoNode(int n) +: pyVaultNode(NEWZERO(RelVaultNode)) +{ + fNode->SetNodeType(plVault::kNodeType_AgeInfo); +} + +//============================================================================ +/* +static PyObject * GetChildFolder (RelVaultNode * node, unsigned type) { + PyObject * result = nil; + if (RelVaultNode * rvn = node->GetChildFolderNodeIncRef(type, 1)) { + result = pyVaultFolderNode::New(rvn); + rvn->DecRef(); + } + return result; +} +*/ + +//============================================================================ +static PyObject * GetChildPlayerInfoList (RelVaultNode * node, unsigned type) { + PyObject * result = nil; + if (RelVaultNode * rvn = node->GetChildPlayerInfoListNodeIncRef(type, 1)) { + result = pyVaultPlayerInfoListNode::New(rvn); + rvn->DecRef(); + } + return result; +} + +//============================================================================ +static PyObject * GetChildAgeInfoList (RelVaultNode * node, unsigned type) { + PyObject * result = nil; + if (RelVaultNode * rvn = node->GetChildAgeInfoListNodeIncRef(type, 1)) { + result = pyVaultAgeInfoListNode::New(rvn); + rvn->DecRef(); + } + return result; +} + +//================================================================== +// class RelVaultNode : public plVaultNode +// + +PyObject * pyVaultAgeInfoNode::GetAgeOwnersFolder() const +{ + if (!fNode) + PYTHON_RETURN_NONE; + + if (PyObject * result = GetChildPlayerInfoList(fNode, plVault::kAgeOwnersFolder)) + return result; + + // just return a None object. + PYTHON_RETURN_NONE; +} + +PyObject * pyVaultAgeInfoNode::GetCanVisitFolder() const +{ + if (!fNode) + PYTHON_RETURN_NONE; + + if (PyObject * result = GetChildPlayerInfoList(fNode, plVault::kCanVisitFolder)) + return result; + + // just return a None object. + PYTHON_RETURN_NONE; +} + +PyObject* pyVaultAgeInfoNode::GetChildAgesFolder( void ) +{ + if (!fNode) + PYTHON_RETURN_NONE; + + if (PyObject * result = GetChildAgeInfoList(fNode, plVault::kChildAgesFolder)) + return result; + + // just return a None object. + PYTHON_RETURN_NONE; +} + + +PyObject * pyVaultAgeInfoNode::GetAgeSDL() const +{ + if (!fNode) + PYTHON_RETURN_NONE; + + hsAssert(false, "eric, port me"); + // just return a None object. + PYTHON_RETURN_NONE +} + +PyObject * pyVaultAgeInfoNode::GetCzar() const +{ + if (!fNode) + PYTHON_RETURN_NONE; + + hsAssert(false, "eric, port me"); + + // just return a None object. + PYTHON_RETURN_NONE +} + +PyObject * pyVaultAgeInfoNode::GetParentAgeLink () const +{ + if (!fNode) + PYTHON_RETURN_NONE; + + if (RelVaultNode * rvn = fNode->GetParentAgeLinkIncRef()) { + PyObject * result = pyVaultAgeLinkNode::New(rvn); + rvn->DecRef(); + return result; + } + + // just return a None object. + PYTHON_RETURN_NONE +} + + +const char * pyVaultAgeInfoNode::GetAgeFilename() const +{ + if (fNode) { + char str[MAX_PATH]; + VaultAgeInfoNode access(fNode); + StrToAnsi(str, access.ageFilename, arrsize(str)); + fAgeFilename = str; + } + return fAgeFilename.c_str(); +} + +void pyVaultAgeInfoNode::SetAgeFilename( const char * v ) +{ +} + +const char * pyVaultAgeInfoNode::GetAgeInstanceName() const +{ + if (fNode) { + char str[MAX_PATH]; + VaultAgeInfoNode access(fNode); + StrToAnsi(str, access.ageInstName, arrsize(str)); + fAgeInstName = str; + } + return fAgeInstName.c_str(); +} + +void pyVaultAgeInfoNode::SetAgeInstanceName( const char * v ) +{ +} + +const char * pyVaultAgeInfoNode::GetAgeUserDefinedName() const +{ + if (fNode) { + char str[MAX_PATH]; + VaultAgeInfoNode access(fNode); + StrToAnsi(str, access.ageUserDefinedName, arrsize(str)); + fAgeUserName = str; + } + return fAgeUserName.c_str(); +} + +void pyVaultAgeInfoNode::SetAgeUserDefinedName( const char * v ) +{ +} + +const char * pyVaultAgeInfoNode::GetAgeInstanceGuid() const +{ + fAgeInstGuid[0] = 0; + + if (fNode) { + VaultAgeInfoNode access(fNode); + GuidToString(access.ageInstUuid, fAgeInstGuid, arrsize(fAgeInstGuid)); + } + return fAgeInstGuid; +} + +void pyVaultAgeInfoNode::SetAgeInstanceGuid( const char * sguid ) +{ +} + +const char * pyVaultAgeInfoNode::GetAgeDescription() const +{ + if (fNode) { + char str[MAX_PATH]; + memset(str, 0, sizeof(str)); + VaultAgeInfoNode access(fNode); + StrToAnsi(str, access.ageDescription, arrsize(str)); + fAgeDescription = str; + } + return fAgeDescription.c_str(); +} + +void pyVaultAgeInfoNode::SetAgeDescription( const char * v ) +{ +} + +Int32 pyVaultAgeInfoNode::GetSequenceNumber() const +{ + if (!fNode) + return -1; + + VaultAgeInfoNode access(fNode); + return access.ageSequenceNumber; +} + +void pyVaultAgeInfoNode::SetSequenceNumber( Int32 v ) +{ +} + +Int32 pyVaultAgeInfoNode::GetAgeLanguage() const +{ + if (!fNode) + return -1; + + VaultAgeInfoNode access(fNode); + return access.ageLanguage; +} + +void pyVaultAgeInfoNode::SetAgeLanguage( Int32 v ) +{ +} + +UInt32 pyVaultAgeInfoNode::GetAgeID() const +{ + return 0; +} + +void pyVaultAgeInfoNode::SetAgeID( UInt32 v ) +{ +} + +UInt32 pyVaultAgeInfoNode::GetCzarID() const +{ + hsAssert(false, "eric, port me"); + return 0; +} + + +bool pyVaultAgeInfoNode::IsPublic() const +{ + if (fNode) { + VaultAgeInfoNode access(fNode); + return access.ageIsPublic; + } + return false; +} + +const char * pyVaultAgeInfoNode::GetDisplayName() const +{ + if (fNode) { + char str[MAX_PATH]; + VaultAgeInfoNode access(fNode); + if (access.ageSequenceNumber > 0) + StrPrintf(str, arrsize(str), "%S(%d) %S", access.ageUserDefinedName, access.ageSequenceNumber, access.ageInstName); + else + StrPrintf(str, arrsize(str), "%S %S", access.ageUserDefinedName, access.ageInstName); + fAgeDispName = str; + } + return fAgeDispName.c_str(); +} + +PyObject * pyVaultAgeInfoNode::AsAgeInfoStruct() const +{ + plAgeInfoStruct info; + VaultAgeInfoNode access(fNode); + access.CopyTo(&info); + return pyAgeInfoStruct::New(&info); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNode.h new file mode 100644 index 00000000..657cf634 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNode.h @@ -0,0 +1,119 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultAgeInfoNode_h_ +#define _pyVaultAgeInfoNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultAgeInfoNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyVaultNode.h" + +struct RelVaultNode; +class pyVaultSDLNode; +class pyAgeInfoStruct; + + +class pyVaultAgeInfoNode : public pyVaultNode +{ +private: + mutable char fAgeInstGuid[64]; + mutable std::string fAgeFilename; + mutable std::string fAgeInstName; + mutable std::string fAgeUserName; + mutable std::string fAgeDispName; + mutable std::string fAgeDescription; + +protected: + // should only be created from C++ side + pyVaultAgeInfoNode(RelVaultNode* vaultNode); + + //create from the Python side + pyVaultAgeInfoNode(int n=0); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultAgeInfoNode); + static PyObject *New(RelVaultNode* vaultNode); + static PyObject *New(int n=0); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultAgeInfoNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultAgeInfoNode); // converts a PyObject to a pyVaultAgeInfoNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + +//================================================================== +// class RelVaultNode : public plVaultNode +// + PyObject * GetCanVisitFolder() const; // returns pyVaultPlayerInfoListNode + PyObject * GetAgeOwnersFolder() const; // returns pyVaultPlayerInfoListNode + PyObject* GetChildAgesFolder( void ); // returns pyVaultFolderNode + PyObject * GetAgeSDL() const; // returns pyVaultSDLNode + PyObject * GetCzar() const; // returns pyVaultPlayerInfoNode + + PyObject * GetParentAgeLink () const; // returns pyVaultAgeLinkNode, or None if not a child age. + + const char * GetAgeFilename() const; + void SetAgeFilename( const char * v ); + + const char * GetAgeInstanceName() const; + void SetAgeInstanceName( const char * v ); + + const char * GetAgeUserDefinedName() const; + void SetAgeUserDefinedName( const char * v ); + + const char * GetAgeInstanceGuid() const; + void SetAgeInstanceGuid( const char * guid ); + + const char * GetAgeDescription() const; + void SetAgeDescription( const char * v ); + + Int32 GetSequenceNumber() const; + void SetSequenceNumber( Int32 v ); + + Int32 GetAgeLanguage() const; + void SetAgeLanguage( Int32 v ); + + UInt32 GetAgeID() const; + void SetAgeID( UInt32 v ); + + UInt32 GetCzarID() const; + + bool IsPublic() const; + + const char * GetDisplayName() const; + + PyObject * AsAgeInfoStruct() const; // returns pyAgeInfoStruct +}; + +#endif // _pyVaultAgeInfoNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNodeGlue.cpp new file mode 100644 index 00000000..e529ccf3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeInfoNodeGlue.cpp @@ -0,0 +1,298 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultAgeInfoNode.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultAgeInfoNode, pyVaultAgeInfoNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultAgeInfoNode, pyVaultAgeInfoNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultAgeInfoNode) + +PYTHON_INIT_DEFINITION(ptVaultAgeInfoNode, args, keywords) +{ + int n = 0; + if (!PyArg_ParseTuple(args, "|i", &n)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional int"); + PYTHON_RETURN_INIT_ERROR; + } + // we don't really do anything? Not according to the associated constructor. Odd... + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getCanVisitFolder) +{ + return self->fThis->GetCanVisitFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getAgeOwnersFolder) +{ + return self->fThis->GetAgeOwnersFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getChildAgesFolder) +{ + return self->fThis->GetChildAgesFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getAgeSDL) +{ + return self->fThis->GetAgeSDL(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getCzar) +{ + return self->fThis->GetCzar(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getParentAgeLink) +{ + return self->fThis->GetParentAgeLink(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getAgeFilename) +{ + return PyString_FromString(self->fThis->GetAgeFilename()); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeInfoNode, setAgeFilename, args) +{ + char* filename; + if (!PyArg_ParseTuple(args, "s", &filename)) + { + PyErr_SetString(PyExc_TypeError, "setAgeFilename expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeFilename(filename); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getAgeInstanceName) +{ + return PyString_FromString(self->fThis->GetAgeInstanceName()); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeInfoNode, setAgeInstanceName, args) +{ + char* instanceName; + if (!PyArg_ParseTuple(args, "s", &instanceName)) + { + PyErr_SetString(PyExc_TypeError, "setAgeInstanceName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeInstanceName(instanceName); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getAgeUserDefinedName) +{ + return PyString_FromString(self->fThis->GetAgeUserDefinedName()); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeInfoNode, setAgeUserDefinedName, args) +{ + char* userDefName; + if (!PyArg_ParseTuple(args, "s", &userDefName)) + { + PyErr_SetString(PyExc_TypeError, "setAgeUserDefinedName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeUserDefinedName(userDefName); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getAgeInstanceGuid) +{ + return PyString_FromString(self->fThis->GetAgeInstanceGuid()); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeInfoNode, setAgeInstanceGuid, args) +{ + char* guid; + if (!PyArg_ParseTuple(args, "s", &guid)) + { + PyErr_SetString(PyExc_TypeError, "setAgeInstanceGuid expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeInstanceGuid(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getAgeDescription) +{ + return PyString_FromString(self->fThis->GetAgeDescription()); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeInfoNode, setAgeDescription, args) +{ + char* descr; + if (!PyArg_ParseTuple(args, "s", &descr)) + { + PyErr_SetString(PyExc_TypeError, "setAgeDescription expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeDescription(descr); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getAgeSequenceNumber) +{ + return PyLong_FromLong(self->fThis->GetSequenceNumber()); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeInfoNode, setAgeSequenceNumber, args) +{ + long seqNum; + if (!PyArg_ParseTuple(args, "l", &seqNum)) + { + PyErr_SetString(PyExc_TypeError, "setAgeSequenceNumber expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetSequenceNumber(seqNum); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getAgeLanguage) +{ + return PyLong_FromLong(self->fThis->GetAgeLanguage()); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeInfoNode, setAgeLanguage, args) +{ + long lang; + if (!PyArg_ParseTuple(args, "l", &lang)) + { + PyErr_SetString(PyExc_TypeError, "setAgeLanguage expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeLanguage(lang); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getAgeID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetAgeID()); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeInfoNode, setAgeID, args) +{ + unsigned long ageID; + if (!PyArg_ParseTuple(args, "l", &ageID)) + { + PyErr_SetString(PyExc_TypeError, "setAgeID expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeID(ageID); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getCzarID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetCzarID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, isPublic) +{ + PYTHON_RETURN_BOOL(self->fThis->IsPublic()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, getDisplayName) +{ + return PyString_FromString(self->fThis->GetDisplayName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeInfoNode, asAgeInfoStruct) +{ + return self->fThis->AsAgeInfoStruct(); +} + +PYTHON_START_METHODS_TABLE(ptVaultAgeInfoNode) + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getCanVisitFolder, "Returns a ptVaultPlayerInfoList of the players that can visit this age"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getAgeOwnersFolder, "Returns a ptVaultPlayerInfoList of the players that own this age"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getChildAgesFolder, "Returns a ptVaultFolderNode of the child ages of this age"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getAgeSDL, "Returns a ptVaultSDLNode of the age's SDL"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getCzar, "Returns ptVaultPlayerInfoNode of the player that is the Czar"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getParentAgeLink, "Returns ptVaultAgeLinkNode of the age's parent age, or None if not a child age"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getAgeFilename, "Returns the age filename"), + PYTHON_METHOD(ptVaultAgeInfoNode, setAgeFilename, "Params: fileName\nSets the filename"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getAgeInstanceName, "Returns the instance name of the age"), + PYTHON_METHOD(ptVaultAgeInfoNode, setAgeInstanceName, "Params: instanceName\nSets the instance name"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getAgeUserDefinedName, "Returns the user define part of the age name"), + PYTHON_METHOD(ptVaultAgeInfoNode, setAgeUserDefinedName, "Params: udname\nSets the user defined part of the name"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getAgeInstanceGuid, "Returns the age instance guid"), + PYTHON_METHOD(ptVaultAgeInfoNode, setAgeInstanceGuid, "Params: guid\nSets the age instance GUID"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getAgeDescription, "Returns the description of the age"), + PYTHON_METHOD(ptVaultAgeInfoNode, setAgeDescription, "Params: description\nSets the description of the age"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getAgeSequenceNumber, "Returns the sequence number of this instance of the age"), + PYTHON_METHOD(ptVaultAgeInfoNode, setAgeSequenceNumber, "Params: seqNumber\nSets the sequence number"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getAgeLanguage, "Returns the age's language (integer)"), + PYTHON_METHOD(ptVaultAgeInfoNode, setAgeLanguage, "Params: lang\nSets the age's language (integer)"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getAgeID, "Returns the age ID"), + PYTHON_METHOD(ptVaultAgeInfoNode, setAgeID, "Params: ageID\nSets the age ID"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getCzarID, "Returns the ID of the age's czar"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, isPublic, "Returns whether the age is Public or Not"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, getDisplayName, "Returns the displayable version of the age name"), + PYTHON_METHOD_NOARGS(ptVaultAgeInfoNode, asAgeInfoStruct, "Returns this ptVaultAgeInfoNode as a ptAgeInfoStruct"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultAgeInfoNode, pyVaultNode, "Params: n=0\nPlasma vault age info node"); + +// required functions for PyObject interoperability +PyObject *pyVaultAgeInfoNode::New(RelVaultNode* nfsNode) +{ + ptVaultAgeInfoNode *newObj = (ptVaultAgeInfoNode*)ptVaultAgeInfoNode_type.tp_new(&ptVaultAgeInfoNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PyObject *pyVaultAgeInfoNode::New(int n /* =0 */) +{ + ptVaultAgeInfoNode *newObj = (ptVaultAgeInfoNode*)ptVaultAgeInfoNode_type.tp_new(&ptVaultAgeInfoNode_type, NULL, NULL); + // oddly enough, nothing to do here + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultAgeInfoNode, pyVaultAgeInfoNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultAgeInfoNode, pyVaultAgeInfoNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultAgeInfoNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultAgeInfoNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNode.cpp new file mode 100644 index 00000000..ea823e28 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNode.cpp @@ -0,0 +1,196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultAgeLinkNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultAgeLinkNode.h" +#include "pyVaultAgeInfoNode.h" +#include "pyNetLinkingMgr.h" +#include "pyAgeLinkStruct.h" +#include "pySpawnPointInfo.h" + +#include "../plVault/plVault.h" + +#include "../plNetCommon/plSpawnPointInfo.h" + +// should only be created from C++ side +pyVaultAgeLinkNode::pyVaultAgeLinkNode(RelVaultNode* nfsNode) +: pyVaultNode(nfsNode) +{ +} + +//create from the Python side +pyVaultAgeLinkNode::pyVaultAgeLinkNode(int n) +: pyVaultNode(NEWZERO(RelVaultNode)) +{ + fNode->SetNodeType(plVault::kNodeType_AgeLink); +} + + +//================================================================== +// class RelVaultNode : public plVaultNode +// + +PyObject* pyVaultAgeLinkNode::GetAgeInfo() const +{ + if (!fNode) + PYTHON_RETURN_NONE; + + PyObject * result = nil; + if (RelVaultNode * rvn = fNode->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + result = pyVaultAgeInfoNode::New(rvn); + rvn->DecRef(); + } + + if (result) + return result; + + PYTHON_RETURN_NONE; +} + + +void pyVaultAgeLinkNode::SetLocked( bool v ) +{ + if (!fNode) + return; + + VaultAgeLinkNode access(fNode); + access.SetUnlocked(!v); +} + +bool pyVaultAgeLinkNode::GetLocked() const +{ + if (!fNode) + return false; + + VaultAgeLinkNode access(fNode); + return !access.unlocked; +} + +void pyVaultAgeLinkNode::SetVolatile( bool v ) +{ + if (!fNode) + return; + + VaultAgeLinkNode access(fNode); + access.SetVolatile(v); +} + +bool pyVaultAgeLinkNode::GetVolatile() const +{ + if (!fNode) + return false; + + VaultAgeLinkNode access(fNode); + return access.volat; +} + +void pyVaultAgeLinkNode::AddSpawnPoint( pySpawnPointInfo & point ) +{ + if (!fNode) + return; + + VaultAgeLinkNode access(fNode); + access.AddSpawnPoint(point.fInfo); +} + +void pyVaultAgeLinkNode::AddSpawnPointRef( pySpawnPointInfoRef & point ) +{ + if (!fNode) + return; + + VaultAgeLinkNode access(fNode); + access.AddSpawnPoint(point.fInfo); +} + +void pyVaultAgeLinkNode::RemoveSpawnPoint( pySpawnPointInfo & point ) +{ + if (!fNode) + return; + + VaultAgeLinkNode access(fNode); + access.RemoveSpawnPoint(point.GetName()); +} + +void pyVaultAgeLinkNode::RemoveSpawnPointRef( pySpawnPointInfoRef & point ) +{ + if (!fNode) + return; + + VaultAgeLinkNode access(fNode); + access.RemoveSpawnPoint(point.GetName()); +} + +void pyVaultAgeLinkNode::RemoveSpawnPointByName( const char * spawnPtName ) +{ + if (!fNode) + return; + + VaultAgeLinkNode access(fNode); + access.RemoveSpawnPoint(spawnPtName); +} + +bool pyVaultAgeLinkNode::HasSpawnPoint( const char * spawnPtName ) const +{ + if (!fNode) + return false; + + VaultAgeLinkNode access(fNode); + return access.HasSpawnPoint(spawnPtName); +} + +PyObject * pyVaultAgeLinkNode::GetSpawnPoints() const +{ + PyObject* pyEL = PyList_New(0); + + if (!fNode) + return pyEL; + + plSpawnPointVec points; + VaultAgeLinkNode access(fNode); + access.GetSpawnPoints(&points); + for (unsigned i = 0; i < points.size(); ++i) { + PyObject* elementObj = pySpawnPointInfo::New(points[i]); + PyList_Append(pyEL, elementObj); + Py_DECREF(elementObj); + } + + return pyEL; +} + +PyObject * pyVaultAgeLinkNode::AsAgeLinkStruct() const +{ + if (!fNode) + PYTHON_RETURN_NONE; + + VaultAgeLinkNode access(fNode); + access.CopyTo(&fAgeLinkStruct); + + return pyAgeLinkStruct::New(&fAgeLinkStruct); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNode.h new file mode 100644 index 00000000..66c6869c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNode.h @@ -0,0 +1,98 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultAgeLinkNode_h_ +#define _pyVaultAgeLinkNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultAgeLinkNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyVaultNode.h" +#include "../plNetCommon/plNetServerSessionInfo.h" // for plAgeLinkStruct + +class pyVaultAgeInfoNode; +struct RelVaultNode; +class pyAgeLinkStruct; +class pySpawnPointInfo; +class pySpawnPointInfoRef; + + +class pyVaultAgeLinkNode : public pyVaultNode +{ +private: + mutable std::string fAgeGuidStr; // for getting Age GUID + + mutable plAgeLinkStruct fAgeLinkStruct; // for use with AsAgeLinkStruct() + +protected: + // should only be created from C++ side + pyVaultAgeLinkNode(RelVaultNode* nfsNode); + + //create from the Python side + pyVaultAgeLinkNode(int n=0); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultAgeLinkNode); + static PyObject *New(RelVaultNode* nfsNode); + static PyObject *New(int n=0); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultAgeLinkNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultAgeLinkNode); // converts a PyObject to a pyVaultAgeLinkNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + +//================================================================== +// class RelVaultNode : public plVaultNode +// + PyObject* GetAgeInfo() const; // returns pyVaultAgeInfoNode + // locked on psnl age bookshelf + void SetLocked( bool v ); + bool GetLocked() const; + // volatile on psnl age bookshelf + void SetVolatile( bool v ); + bool GetVolatile() const; + // spawn points + void AddSpawnPoint( pySpawnPointInfo & point ); // will only add if not there already. + void AddSpawnPointRef( pySpawnPointInfoRef & point ); // will only add if not there already. + void RemoveSpawnPoint( pySpawnPointInfo & point ); + void RemoveSpawnPointRef( pySpawnPointInfoRef & point ); + void RemoveSpawnPointByName( const char * spawnPtName ); + bool HasSpawnPoint( const char * spawnPtName ) const; + PyObject * GetSpawnPoints() const; // returns list of pySpawnPointInfo + + PyObject * AsAgeLinkStruct() const; // returns pyAgeLinkStruct + +}; + +#endif // _pyVaultAgeLinkNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNodeGlue.cpp new file mode 100644 index 00000000..0b2d8d98 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultAgeLinkNodeGlue.cpp @@ -0,0 +1,211 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultAgeLinkNode.h" +#include "pySpawnPointInfo.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultAgeLinkNode, pyVaultAgeLinkNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultAgeLinkNode, pyVaultAgeLinkNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultAgeLinkNode) + +PYTHON_INIT_DEFINITION(ptVaultAgeLinkNode, args, keywords) +{ + int n = 0; + if (!PyArg_ParseTuple(args, "|i", &n)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional int"); + PYTHON_RETURN_INIT_ERROR; + } + // we don't really do anything? Not according to the associated constructor. Odd... + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeLinkNode, getAgeInfo) +{ + return self->fThis->GetAgeInfo(); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeLinkNode, setLocked, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "setLocked expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetLocked(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeLinkNode, getLocked) +{ + PYTHON_RETURN_BOOL(self->fThis->GetLocked()); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeLinkNode, setVolatile, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "setVolatile expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetVolatile(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeLinkNode, getVolatile) +{ + PYTHON_RETURN_BOOL(self->fThis->GetVolatile()); +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeLinkNode, addSpawnPoint, args) +{ + PyObject* spawnPtObj = NULL; + if (!PyArg_ParseTuple(args, "O", &spawnPtObj)) + { + PyErr_SetString(PyExc_TypeError, "addSpawnPoint expects a ptSpawnPointInfo or a ptSpawnPointInfoRef"); + PYTHON_RETURN_ERROR; + } + if (pySpawnPointInfo::Check(spawnPtObj)) + { + pySpawnPointInfo* spawnPt = pySpawnPointInfo::ConvertFrom(spawnPtObj); + self->fThis->AddSpawnPoint(*spawnPt); + PYTHON_RETURN_NONE; + } + else if (pySpawnPointInfoRef::Check(spawnPtObj)) + { + pySpawnPointInfoRef* spawnPt = pySpawnPointInfoRef::ConvertFrom(spawnPtObj); + self->fThis->AddSpawnPointRef(*spawnPt); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "addSpawnPoint expects a ptSpawnPointInfo or a ptSpawnPointInfoRef"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeLinkNode, removeSpawnPoint, args) +{ + PyObject* spawnPtObj = NULL; + if (!PyArg_ParseTuple(args, "O", &spawnPtObj)) + { + PyErr_SetString(PyExc_TypeError, "removeSpawnPoint expects a ptSpawnPointInfo, a ptSpawnPointInfoRef, or a string"); + PYTHON_RETURN_ERROR; + } + if (pySpawnPointInfo::Check(spawnPtObj)) + { + pySpawnPointInfo* spawnPt = pySpawnPointInfo::ConvertFrom(spawnPtObj); + self->fThis->RemoveSpawnPoint(*spawnPt); + PYTHON_RETURN_NONE; + } + else if (pySpawnPointInfoRef::Check(spawnPtObj)) + { + pySpawnPointInfoRef* spawnPt = pySpawnPointInfoRef::ConvertFrom(spawnPtObj); + self->fThis->RemoveSpawnPointRef(*spawnPt); + PYTHON_RETURN_NONE; + } + else if (PyString_Check(spawnPtObj)) + { + char* spawnPt = PyString_AsString(spawnPtObj); + self->fThis->RemoveSpawnPointByName(spawnPt); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "removeSpawnPoint expects a ptSpawnPointInfo, a ptSpawnPointInfoRef, or a string"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION(ptVaultAgeLinkNode, hasSpawnPoint, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "hasSpawnPoint expects a string"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->HasSpawnPoint(name)); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeLinkNode, getSpawnPoints) +{ + return self->fThis->GetSpawnPoints(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultAgeLinkNode, asAgeLinkStruct) +{ + return self->fThis->AsAgeLinkStruct(); +} + +PYTHON_START_METHODS_TABLE(ptVaultAgeLinkNode) + PYTHON_METHOD_NOARGS(ptVaultAgeLinkNode, getAgeInfo, "Returns the ageInfo as a ptAgeInfoStruct"), + PYTHON_METHOD(ptVaultAgeLinkNode, setLocked, "Params: state\nSets whether the link is locked or not"), + PYTHON_METHOD_NOARGS(ptVaultAgeLinkNode, getLocked, "Returns whether the link is locked or not"), + PYTHON_METHOD(ptVaultAgeLinkNode, setVolatile, "Params: state\nSets the state of the volitility of the link"), + PYTHON_METHOD_NOARGS(ptVaultAgeLinkNode, getVolatile, "Returns whether the link is volatile or not"), + PYTHON_METHOD(ptVaultAgeLinkNode, addSpawnPoint, "Params: point\nAdds the specified ptSpawnPointInfo or ptSpawnPointInfoRef"), + PYTHON_METHOD(ptVaultAgeLinkNode, removeSpawnPoint, "Params: point\nRemoves the specified spawn point based on a ptSpawnPointInfo, ptSpawnPointInfoRef, or string"), + PYTHON_METHOD(ptVaultAgeLinkNode, hasSpawnPoint, "Params: spawnPtName\nReturns true if this link has the specified spawn point"), + PYTHON_METHOD_NOARGS(ptVaultAgeLinkNode, getSpawnPoints, "Returns a list of ptSpawnPointInfo objects"), + PYTHON_METHOD_NOARGS(ptVaultAgeLinkNode, asAgeLinkStruct, "Returns this ptVaultAgeLinkNode as a ptAgeLinkStruct"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultAgeLinkNode, pyVaultNode, "Params: n=0\nPlasma vault age link node"); + +// required functions for PyObject interoperability +PyObject *pyVaultAgeLinkNode::New(RelVaultNode* nfsNode) +{ + ptVaultAgeLinkNode *newObj = (ptVaultAgeLinkNode*)ptVaultAgeLinkNode_type.tp_new(&ptVaultAgeLinkNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PyObject *pyVaultAgeLinkNode::New(int n /* =0 */) +{ + ptVaultAgeLinkNode *newObj = (ptVaultAgeLinkNode*)ptVaultAgeLinkNode_type.tp_new(&ptVaultAgeLinkNode_type, NULL, NULL); + // oddly enough, nothing to do here + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultAgeLinkNode, pyVaultAgeLinkNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultAgeLinkNode, pyVaultAgeLinkNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultAgeLinkNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultAgeLinkNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNode.cpp new file mode 100644 index 00000000..4fb1f35c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNode.cpp @@ -0,0 +1,132 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultChronicleNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultChronicleNode.h" +#ifndef BUILDING_PYPLASMA +#include "pyVault.h" +#endif + +#include "../plVault/plVault.h" + +// should only be created from C++ side +pyVaultChronicleNode::pyVaultChronicleNode(RelVaultNode* nfsNode) +: pyVaultNode(nfsNode) +, ansiName(nil) +, ansiValue(nil) +{ +} + +//create from the Python side +pyVaultChronicleNode::pyVaultChronicleNode(int n) +: pyVaultNode(NEWZERO(RelVaultNode)) +, ansiName(nil) +, ansiValue(nil) +{ + fNode->SetNodeType(plVault::kNodeType_Chronicle); +} + +pyVaultChronicleNode::~pyVaultChronicleNode () { + FREE(ansiName); + FREE(ansiValue); +} + + +//================================================================== +// class RelVaultNode : public plVaultNode +// +void pyVaultChronicleNode::Chronicle_SetName( const char * text ) +{ + if (!fNode) + return; + + wchar * wStr = StrDupToUnicode(text); + VaultChronicleNode chron(fNode); + chron.SetEntryName(wStr); + FREE(wStr); +} + +const char * pyVaultChronicleNode::Chronicle_GetName( void ) +{ + if (!fNode) + return ""; + + FREE(ansiName); + VaultChronicleNode chron(fNode); + ansiName = StrDupToAnsi(chron.entryName); + + return ansiName; +} + +void pyVaultChronicleNode::Chronicle_SetValue( const char * text ) +{ + if (!fNode) + return; + + wchar * wStr = StrDupToUnicode(text); + VaultChronicleNode chron(fNode); + chron.SetEntryValue(wStr); + FREE(wStr); +} + +const char * pyVaultChronicleNode::Chronicle_GetValue( void ) +{ + if (!fNode) + return ""; + + FREE(ansiValue); + ansiValue = nil; + + VaultChronicleNode chron(fNode); + + if (!chron.entryValue) + return ""; + + ansiValue = StrDupToAnsi(chron.entryValue); + return ansiValue; +} + +void pyVaultChronicleNode::Chronicle_SetType( UInt32 type ) +{ + if (!fNode) + return; + + VaultChronicleNode chron(fNode); + chron.SetEntryType(type); +} + +UInt32 pyVaultChronicleNode::Chronicle_GetType( void ) +{ + if (!fNode) + return 0; + + VaultChronicleNode chron(fNode); + return chron.entryType; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNode.h new file mode 100644 index 00000000..ea9588a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNode.h @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultChronicleNode_h_ +#define _pyVaultChronicleNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultChronicleNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyVaultNode.h" + +struct RelVaultNode; + + +class pyVaultChronicleNode : public pyVaultNode +{ + char * ansiName; + char * ansiValue; + +protected: + // should only be created from C++ side + pyVaultChronicleNode(RelVaultNode* nfsNode); + + //create from the Python side + pyVaultChronicleNode(int n=0); + +public: + ~pyVaultChronicleNode (); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultChronicleNode); + static PyObject *New(RelVaultNode* nfsNode); + static PyObject *New(int n=0); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultChronicleNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultChronicleNode); // converts a PyObject to a pyVaultChronicleNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + +//================================================================== +// class RelVaultNode : public plVaultNode +// + void Chronicle_SetName( const char * text ); + const char * Chronicle_GetName( void ); + void Chronicle_SetValue( const char * text ); + const char * Chronicle_GetValue( void ); + void Chronicle_SetType( UInt32 type ); + UInt32 Chronicle_GetType( void ); +}; + +#endif // _pyVaultChronicleNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNodeGlue.cpp new file mode 100644 index 00000000..a95d3e24 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultChronicleNodeGlue.cpp @@ -0,0 +1,202 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultChronicleNode.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultChronicleNode, pyVaultChronicleNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultChronicleNode, pyVaultChronicleNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultChronicleNode) + +PYTHON_INIT_DEFINITION(ptVaultChronicleNode, args, keywords) +{ + int n = 0; + if (!PyArg_ParseTuple(args, "|i", &n)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional int"); + PYTHON_RETURN_INIT_ERROR; + } + // we don't really do anything? Not according to the associated constructor. Odd... + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptVaultChronicleNode, chronicleSetName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "chronicleSetName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Chronicle_SetName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultChronicleNode, chronicleGetName) +{ + return PyString_FromString(self->fThis->Chronicle_GetName()); +} + +PYTHON_METHOD_DEFINITION(ptVaultChronicleNode, chronicleSetValue, args) +{ + char* val; + if (!PyArg_ParseTuple(args, "s", &val)) + { + PyErr_SetString(PyExc_TypeError, "chronicleSetValue expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Chronicle_SetValue(val); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultChronicleNode, chronicleGetValue) +{ + return PyString_FromString(self->fThis->Chronicle_GetValue()); +} + +PYTHON_METHOD_DEFINITION(ptVaultChronicleNode, chronicleSetType, args) +{ + unsigned long chronType; + if (!PyArg_ParseTuple(args, "l", &chronType)) + { + PyErr_SetString(PyExc_TypeError, "chronicleSetType expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->Chronicle_SetType(chronType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultChronicleNode, chronicleGetType) +{ + return PyLong_FromUnsignedLong(self->fThis->Chronicle_GetType()); +} + +PYTHON_METHOD_DEFINITION(ptVaultChronicleNode, setName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "setName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Chronicle_SetName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultChronicleNode, getName) +{ + return PyString_FromString(self->fThis->Chronicle_GetName()); +} + +PYTHON_METHOD_DEFINITION(ptVaultChronicleNode, setValue, args) +{ + char* val; + if (!PyArg_ParseTuple(args, "s", &val)) + { + PyErr_SetString(PyExc_TypeError, "setValue expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Chronicle_SetValue(val); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultChronicleNode, getValue) +{ + return PyString_FromString(self->fThis->Chronicle_GetValue()); +} + +PYTHON_METHOD_DEFINITION(ptVaultChronicleNode, setEntryType, args) +{ + unsigned long chronType; + if (!PyArg_ParseTuple(args, "l", &chronType)) + { + PyErr_SetString(PyExc_TypeError, "setEntryType expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->Chronicle_SetType(chronType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultChronicleNode, getEntryType) +{ + return PyLong_FromUnsignedLong(self->fThis->Chronicle_GetType()); +} + +PYTHON_START_METHODS_TABLE(ptVaultChronicleNode) + // legacy glue + PYTHON_METHOD(ptVaultChronicleNode, chronicleSetName, "Params: name\nLEGACY: Sets the name of the chronicle node."), + PYTHON_METHOD_NOARGS(ptVaultChronicleNode, chronicleGetName, "LEGACY: Returns the name of the chronicle node."), + PYTHON_METHOD(ptVaultChronicleNode, chronicleSetValue, "Params: value\nLEGACY: Sets the chronicle to a value that is a string"), + PYTHON_METHOD_NOARGS(ptVaultChronicleNode, chronicleGetValue, "LEGACY: Returns the value as a string of this chronicle node."), + PYTHON_METHOD(ptVaultChronicleNode, chronicleSetType, "Params: type\nLEGACY: Sets this chronicle node to a user defined type."), + PYTHON_METHOD_NOARGS(ptVaultChronicleNode, chronicleGetType, "LEGACY: Returns the user defined type of the chronicle node."), + // new glue + PYTHON_METHOD(ptVaultChronicleNode, setName, "Params: name\nSets the name of the chronicle node."), + PYTHON_METHOD_NOARGS(ptVaultChronicleNode, getName, "Returns the name of the chronicle node."), + PYTHON_METHOD(ptVaultChronicleNode, setValue, "Params: value\nSets the chronicle to a value that is a string"), + PYTHON_METHOD_NOARGS(ptVaultChronicleNode, getValue, "Returns the value as a string of this chronicle node."), + PYTHON_METHOD(ptVaultChronicleNode, setEntryType, "Params: type\nSets this chronicle node to a user defined type."), + PYTHON_METHOD_NOARGS(ptVaultChronicleNode, getEntryType, "Returns the user defined type of the chronicle node."), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultChronicleNode, pyVaultNode, "Params: n=0\nPlasma vault chronicle node"); + +// required functions for PyObject interoperability +PyObject *pyVaultChronicleNode::New(RelVaultNode* nfsNode) +{ + ptVaultChronicleNode *newObj = (ptVaultChronicleNode*)ptVaultChronicleNode_type.tp_new(&ptVaultChronicleNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PyObject *pyVaultChronicleNode::New(int n /* =0 */) +{ + ptVaultChronicleNode *newObj = (ptVaultChronicleNode*)ptVaultChronicleNode_type.tp_new(&ptVaultChronicleNode_type, NULL, NULL); + // oddly enough, nothing to do here + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultChronicleNode, pyVaultChronicleNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultChronicleNode, pyVaultChronicleNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultChronicleNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultChronicleNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNode.cpp new file mode 100644 index 00000000..30fea4f0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNode.cpp @@ -0,0 +1,120 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultFolderNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultFolderNode.h" + +#include "hsUtils.h" +#include "../plVault/plVault.h" + +// should only be created from C++ side +pyVaultFolderNode::pyVaultFolderNode( RelVaultNode* nfsNode ) +: pyVaultNode( nfsNode ) +{ +} + +//create from the Python side +pyVaultFolderNode::pyVaultFolderNode(int n) +: pyVaultNode(NEWZERO(RelVaultNode)) +{ + fNode->SetNodeType(plVault::kNodeType_Folder); +} + +pyVaultFolderNode::~pyVaultFolderNode () { +} + +//================================================================== +// class RelVaultNode : public plVaultNode +// +void pyVaultFolderNode::Folder_SetType( int type ) +{ + if (!fNode) + return; + + VaultFolderNode folder(fNode); + folder.SetFolderType(type); +} + +int pyVaultFolderNode::Folder_GetType( void ) +{ + if (!fNode) + return 0; + + VaultFolderNode folder(fNode); + return folder.folderType; +} + +void pyVaultFolderNode::Folder_SetName( std::string name ) +{ + if (!fNode) + return; + + wchar_t* wName = hsStringToWString(name.c_str()); + VaultFolderNode folder(fNode); + folder.SetFolderName(wName); + delete [] wName; +} + +void pyVaultFolderNode::Folder_SetNameW( std::wstring name ) +{ + if (!fNode) + return; + + VaultFolderNode folder(fNode); + folder.SetFolderName(name.c_str()); +} + +std::string pyVaultFolderNode::Folder_GetName( void ) +{ + if (!fNode) + return ""; + + VaultFolderNode folder(fNode); + if (!folder.folderName) + return ""; + + std::string retVal; + char* sName = hsWStringToString(folder.folderName); + retVal = sName; + delete [] sName; + return retVal; +} + +std::wstring pyVaultFolderNode::Folder_GetNameW( void ) +{ + if (!fNode) + return L""; + + VaultFolderNode folder(fNode); + if (!folder.folderName) + return L""; + + return folder.folderName; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNode.h new file mode 100644 index 00000000..22dba2a7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNode.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultFolderNode_h_ +#define _pyVaultFolderNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultFolderNode - a wrapper class to provide interface to the plVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyVaultNode.h" + +struct RelVaultNode; + + +class pyVaultFolderNode : public pyVaultNode +{ +protected: + // should only be created from C++ side + pyVaultFolderNode(RelVaultNode* nfsNode); + + //create from the Python side + pyVaultFolderNode(int n=0); + +public: + ~pyVaultFolderNode(); + + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptVaultFolderNode); + static PyObject *New(RelVaultNode* nfsNode); + static PyObject *New(int n=0); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultFolderNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultFolderNode); // converts a PyObject to a pyVaultFolderNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + virtual void Folder_SetType( int type ); + virtual int Folder_GetType( void ); + void Folder_SetName( std::string name ); + void Folder_SetNameW( std::wstring name ); + std::string Folder_GetName( void ); + std::wstring Folder_GetNameW( void ); + + + +}; + +#endif // _pyVaultFolderNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNodeGlue.cpp new file mode 100644 index 00000000..382761b6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultFolderNodeGlue.cpp @@ -0,0 +1,202 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultFolderNode.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultFolderNode, pyVaultFolderNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultFolderNode, pyVaultFolderNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultFolderNode) + +PYTHON_INIT_DEFINITION(ptVaultFolderNode, args, keywords) +{ + int n = 0; + if (!PyArg_ParseTuple(args, "|i", &n)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional int"); + PYTHON_RETURN_INIT_ERROR; + } + // we don't really do anything? Not according to the associated constructor. Odd... + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptVaultFolderNode, folderSetType, args) +{ + int folderType; + if (!PyArg_ParseTuple(args, "i", &folderType)) + { + PyErr_SetString(PyExc_TypeError, "folderSetType expects an int"); + PYTHON_RETURN_ERROR; + } + self->fThis->Folder_SetType(folderType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultFolderNode, folderGetType) +{ + return PyInt_FromLong(self->fThis->Folder_GetType()); +} + +PYTHON_METHOD_DEFINITION(ptVaultFolderNode, folderSetName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "folderSetName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Folder_SetName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultFolderNode, folderGetName) +{ + return PyString_FromString(self->fThis->Folder_GetName().c_str()); +} + +PYTHON_METHOD_DEFINITION(ptVaultFolderNode, setFolderType, args) +{ + int folderType; + if (!PyArg_ParseTuple(args, "i", &folderType)) + { + PyErr_SetString(PyExc_TypeError, "setFolderType expects an int"); + PYTHON_RETURN_ERROR; + } + self->fThis->Folder_SetType(folderType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultFolderNode, getFolderType) +{ + return PyInt_FromLong(self->fThis->Folder_GetType()); +} + +PYTHON_METHOD_DEFINITION(ptVaultFolderNode, setFolderName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "setFolderName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Folder_SetName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultFolderNode, setFolderNameW, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "setFolderNameW expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* name = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, name, strLen); + name[strLen] = L'\0'; + self->fThis->Folder_SetNameW(name); + delete [] name; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* name = PyString_AsString(textObj); + self->fThis->Folder_SetName(name); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "setFolderNameW expects a unicode string"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultFolderNode, getFolderName) +{ + return PyString_FromString(self->fThis->Folder_GetName().c_str()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultFolderNode, getFolderNameW) +{ + std::wstring name = self->fThis->Folder_GetNameW(); + return PyUnicode_FromWideChar(name.c_str(), name.length()); +} + +PYTHON_START_METHODS_TABLE(ptVaultFolderNode) + // legacy glue + PYTHON_METHOD(ptVaultFolderNode, folderSetType, "Params: type\nLEGACY\nSet the folder type"), + PYTHON_METHOD_NOARGS(ptVaultFolderNode, folderGetType, "LEGACY\nReturns the folder type (of the standard folder types)"), + PYTHON_METHOD(ptVaultFolderNode, folderSetName, "Params: name\nLEGACY\nSet the folder name"), + PYTHON_METHOD_NOARGS(ptVaultFolderNode, folderGetName, "LEGACY\nReturns the folder's name"), + // new glue + PYTHON_METHOD(ptVaultFolderNode, setFolderType, "Params: type\nSet the folder type"), + PYTHON_METHOD_NOARGS(ptVaultFolderNode, getFolderType, "Returns the folder type (of the standard folder types)"), + PYTHON_METHOD(ptVaultFolderNode, setFolderName, "Params: name\nSet the folder name"), + PYTHON_METHOD(ptVaultFolderNode, setFolderNameW, "Params: name\nUnicode version of setFolderName"), + PYTHON_METHOD_NOARGS(ptVaultFolderNode, getFolderName, "Returns the folder's name"), + PYTHON_METHOD_NOARGS(ptVaultFolderNode, getFolderNameW, "Unicode version of getFolerName"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultFolderNode, pyVaultNode, "Params: n=0\nPlasma vault folder node"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptVaultFolderNode, pyVaultFolderNode); + +// required functions for PyObject interoperability +PyObject *pyVaultFolderNode::New(RelVaultNode* nfsNode) +{ + ptVaultFolderNode *newObj = (ptVaultFolderNode*)ptVaultFolderNode_type.tp_new(&ptVaultFolderNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PyObject *pyVaultFolderNode::New(int n /* =0 */) +{ + ptVaultFolderNode *newObj = (ptVaultFolderNode*)ptVaultFolderNode_type.tp_new(&ptVaultFolderNode_type, NULL, NULL); + // oddly enough, nothing to do here + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultFolderNode, pyVaultFolderNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultFolderNode, pyVaultFolderNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultFolderNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultFolderNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultGlue.cpp new file mode 100644 index 00000000..f9c9bce8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultGlue.cpp @@ -0,0 +1,594 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVault.h" +#include "pyEnum.h" +#include "pyAgeInfoStruct.h" +#include "pyVaultNode.h" +#include "pySDL.h" +#include "pyAgeLinkStruct.h" + +#include "../plVault/plVault.h" +#include "../plMessage/plVaultNotifyMsg.h" +#include + +#ifndef BUILDING_PYPLASMA + +// glue functions +PYTHON_CLASS_DEFINITION(ptVault, pyVault); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVault, pyVault) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVault) + +PYTHON_INIT_DEFINITION(ptVault, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getPlayerInfo) +{ + return self->fThis->GetPlayerInfo(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getKIUsage) +{ + return self->fThis->GetKIUsage(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getInbox) +{ + return self->fThis->GetInbox(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getAvatarOutfitFolder) +{ + return self->fThis->GetAvatarOutfitFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getAvatarClosetFolder) +{ + return self->fThis->GetAvatarClosetFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getChronicleFolder) +{ + return self->fThis->GetChronicleFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getAgeJournalsFolder) +{ + return self->fThis->GetAgeJournalsFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getIgnoreListFolder) +{ + return self->fThis->GetIgnoreListFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getBuddyListFolder) +{ + return self->fThis->GetBuddyListFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getPeopleIKnowAboutFolder) +{ + return self->fThis->GetPeopleIKnowAboutFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getAgesICanVisitFolder) +{ + return self->fThis->GetAgesICanVisitFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getAgesIOwnFolder) +{ + return self->fThis->GetAgesIOwnFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getInviteFolder) +{ + return self->fThis->GetInviteFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getLinkToMyNeighborhood) +{ + return self->fThis->GetLinkToMyNeighborhood(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getLinkToCity) +{ + return self->fThis->GetLinkToCity(); +} + +PYTHON_METHOD_DEFINITION(ptVault, getOwnedAgeLink, args) +{ + PyObject* ageInfoObj = NULL; + if (!PyArg_ParseTuple(args, "O", &ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "getOwnedAgeLink expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeInfoStruct::Check(ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "getOwnedAgeLink expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + pyAgeInfoStruct* ageInfo = pyAgeInfoStruct::ConvertFrom(ageInfoObj); + return self->fThis->GetOwnedAgeLink(*ageInfo); +} + +PYTHON_METHOD_DEFINITION(ptVault, getVisitAgeLink, args) +{ + PyObject* ageInfoObj = NULL; + if (!PyArg_ParseTuple(args, "O", &ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "getVisitAgeLink expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeInfoStruct::Check(ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "getVisitAgeLink expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + pyAgeInfoStruct* ageInfo = pyAgeInfoStruct::ConvertFrom(ageInfoObj); + return self->fThis->GetVisitAgeLink(*ageInfo); +} + +PYTHON_METHOD_DEFINITION(ptVault, findChronicleEntry, args) +{ + char* entryName; + if (!PyArg_ParseTuple(args, "s", &entryName)) + { + PyErr_SetString(PyExc_TypeError, "findChronicleEntry expects a string"); + PYTHON_RETURN_ERROR; + } + return self->fThis->FindChronicleEntry(entryName); +} + +PYTHON_METHOD_DEFINITION(ptVault, addChronicleEntry, args) +{ + char* entryName; + unsigned long entryType; + char* entryValue; + if (!PyArg_ParseTuple(args, "sls", &entryName, &entryType, &entryValue)) + { + PyErr_SetString(PyExc_TypeError, "addChronicleEntry expects a string, an unsigned long, and a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddChronicleEntry(entryName, entryType, entryValue); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getGlobalInbox) +{ + return self->fThis->GetGlobalInbox(); +} + +#ifdef GlobalInboxTestCode +PYTHON_BASIC_METHOD_DEFINITION(ptVault, createGlobalInbox, CreateGlobalInbox) +#endif + +PYTHON_METHOD_DEFINITION(ptVault, findNode, args) +{ + PyObject* templateNodeObj = NULL; + if (!PyArg_ParseTuple(args, "O", &templateNodeObj)) + { + PyErr_SetString(PyExc_TypeError, "findNode expects a ptVaultNode"); + PYTHON_RETURN_ERROR; + } + if (!pyVaultNode::Check(templateNodeObj)) + { + PyErr_SetString(PyExc_TypeError, "findNode expects a ptVaultNode"); + PYTHON_RETURN_ERROR; + } + pyVaultNode* templateNode = pyVaultNode::ConvertFrom(templateNodeObj); + return self->fThis->FindNode(templateNode); +} + +PYTHON_METHOD_DEFINITION(ptVault, sendToDevice, args) +{ + PyObject* nodeObj = NULL; + char* deviceName; + if (!PyArg_ParseTuple(args, "Os", &nodeObj, &deviceName)) + { + PyErr_SetString(PyExc_TypeError, "sendToDevice expects a ptVaultNode and a string"); + PYTHON_RETURN_ERROR; + } + if (!pyVaultNode::Check(nodeObj)) + { + PyErr_SetString(PyExc_TypeError, "sendToDevice expects a ptVaultNode and a string"); + PYTHON_RETURN_ERROR; + } + pyVaultNode* node = pyVaultNode::ConvertFrom(nodeObj); + self->fThis->SendToDevice(*node, deviceName); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, getPsnlAgeSDL) +{ + return self->fThis->GetPsnlAgeSDL(); +} + +PYTHON_METHOD_DEFINITION(ptVault, updatePsnlAgeSDL, args) +{ + PyObject* pyrecObj = NULL; + if (!PyArg_ParseTuple(args, "O", &pyrecObj)) + { + PyErr_SetString(PyExc_TypeError, "updatePsnlAgeSDL expects a ptSDLStateDataRecord"); + PYTHON_RETURN_ERROR; + } + if (!pySDLStateDataRecord::Check(pyrecObj)) + { + PyErr_SetString(PyExc_TypeError, "updatePsnlAgeSDL expects a ptSDLStateDataRecord"); + PYTHON_RETURN_ERROR; + } + pySDLStateDataRecord* pyrec = pySDLStateDataRecord::ConvertFrom(pyrecObj); + self->fThis->UpdatePsnlAgeSDL(*pyrec); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, inMyPersonalAge) +{ + PYTHON_RETURN_BOOL(self->fThis->InMyPersonalAge()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, inMyNeighborhoodAge) +{ + PYTHON_RETURN_BOOL(self->fThis->InMyNeighborhoodAge()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, amOwnerOfCurrentAge) +{ + PYTHON_RETURN_BOOL(self->fThis->AmOwnerOfCurrentAge()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVault, amCzarOfCurrentAge) +{ + PYTHON_RETURN_BOOL(self->fThis->AmCzarOfCurrentAge()); +} + +PYTHON_METHOD_DEFINITION(ptVault, amAgeOwner, args) +{ + PyObject* ageInfoObj = NULL; + if (!PyArg_ParseTuple(args, "O", &ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "amAgeOwner expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeInfoStruct::Check(ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "amAgeOwner expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + pyAgeInfoStruct* ageInfo = pyAgeInfoStruct::ConvertFrom(ageInfoObj); + PYTHON_RETURN_BOOL(self->fThis->AmAgeOwner(ageInfo)); +} + +PYTHON_METHOD_DEFINITION(ptVault, amAgeCzar, args) +{ + PyObject* ageInfoObj = NULL; + if (!PyArg_ParseTuple(args, "O", &ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "amAgeCzar expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeInfoStruct::Check(ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "amAgeCzar expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + pyAgeInfoStruct* ageInfo = pyAgeInfoStruct::ConvertFrom(ageInfoObj); + PYTHON_RETURN_BOOL(self->fThis->AmAgeCzar(ageInfo)); +} + +PYTHON_METHOD_DEFINITION(ptVault, registerMTStation, args) +{ + char* stationName; + char* mtSpawnPoint; + if (!PyArg_ParseTuple(args, "ss", &stationName, &mtSpawnPoint)) + { + PyErr_SetString(PyExc_TypeError, "registerMTStation expects two strings"); + PYTHON_RETURN_ERROR; + } + self->fThis->RegisterMTStation(stationName, mtSpawnPoint); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVault, registerOwnedAge, args) +{ + PyObject* ageLinkObj = NULL; + if (!PyArg_ParseTuple(args, "O", &ageLinkObj)) + { + PyErr_SetString(PyExc_TypeError, "registerOwnedAge expects a ptAgeLinkStruct"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeLinkStruct::Check(ageLinkObj)) + { + PyErr_SetString(PyExc_TypeError, "registerOwnedAge expects a ptAgeLinkStruct"); + PYTHON_RETURN_ERROR; + } + pyAgeLinkStruct* ageLink = pyAgeLinkStruct::ConvertFrom(ageLinkObj); + self->fThis->RegisterOwnedAge(*ageLink); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVault, unRegisterOwnedAge, args) +{ + char* ageFilename; + if (!PyArg_ParseTuple(args, "s", &ageFilename)) + { + PyErr_SetString(PyExc_TypeError, "unRegisterOwnedAge expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->UnRegisterOwnedAge(ageFilename); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVault, registerVisitAge, args) +{ + PyObject* ageLinkObj = NULL; + if (!PyArg_ParseTuple(args, "O", &ageLinkObj)) + { + PyErr_SetString(PyExc_TypeError, "registerVisitAge expects a ptAgeLinkStruct"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeLinkStruct::Check(ageLinkObj)) + { + PyErr_SetString(PyExc_TypeError, "registerVisitAge expects a ptAgeLinkStruct"); + PYTHON_RETURN_ERROR; + } + pyAgeLinkStruct* ageLink = pyAgeLinkStruct::ConvertFrom(ageLinkObj); + self->fThis->RegisterVisitAge(*ageLink); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVault, unRegisterVisitAge, args) +{ + char* guid; + if (!PyArg_ParseTuple(args, "s", &guid)) + { + PyErr_SetString(PyExc_TypeError, "unRegisterVisitAge expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->UnRegisterVisitAge(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVault, invitePlayerToAge, args) +{ + PyObject* ageLinkObj = NULL; + unsigned long playerID; + if (!PyArg_ParseTuple(args, "Ol", &ageLinkObj, &playerID)) + { + PyErr_SetString(PyExc_TypeError, "invitePlayerToAge expects a ptAgeLinkStruct and an unsigned long"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeLinkStruct::Check(ageLinkObj)) + { + PyErr_SetString(PyExc_TypeError, "invitePlayerToAge expects a ptAgeLinkStruct and an unsigned long"); + PYTHON_RETURN_ERROR; + } + pyAgeLinkStruct* ageLink = pyAgeLinkStruct::ConvertFrom(ageLinkObj); + self->fThis->InvitePlayerToAge(*ageLink, playerID); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVault, unInvitePlayerToAge, args) +{ + char* guid; + unsigned long playerID; + if (!PyArg_ParseTuple(args, "sl", &guid, &playerID)) + { + PyErr_SetString(PyExc_TypeError, "unInvitePlayerToAge expects a string and an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->UnInvitePlayerToAge(guid, playerID); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVault, offerLinkToPlayer, args) +{ + PyObject* ageLinkObj = NULL; + unsigned long playerID; + if (!PyArg_ParseTuple(args, "Ol", &ageLinkObj, &playerID)) + { + PyErr_SetString(PyExc_TypeError, "offerLinkToPlayer expects a ptAgeLinkStruct and an unsigned long"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeLinkStruct::Check(ageLinkObj)) + { + PyErr_SetString(PyExc_TypeError, "offerLinkToPlayer expects a ptAgeLinkStruct and an unsigned long"); + PYTHON_RETURN_ERROR; + } + pyAgeLinkStruct* ageLink = pyAgeLinkStruct::ConvertFrom(ageLinkObj); + self->fThis->OfferLinkToPlayer(*ageLink, playerID); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptVault, createNeighborhood, CreateNeighborhood) + +PYTHON_METHOD_DEFINITION(ptVault, setAgePublic, args) +{ + PyObject* ageInfoObj = NULL; + char makePublic; + if (!PyArg_ParseTuple(args, "Ob", &ageInfoObj, &makePublic)) + { + PyErr_SetString(PyExc_TypeError, "setAgePublic expects a ptAgeInfoStruct and a boolean"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeInfoStruct::Check(ageInfoObj)) + { + PyErr_SetString(PyExc_TypeError, "setAgePublic expects a ptAgeInfoStruct and a boolean"); + PYTHON_RETURN_ERROR; + } + pyAgeInfoStruct* ageInfo = pyAgeInfoStruct::ConvertFrom(ageInfoObj); + PYTHON_RETURN_BOOL(self->fThis->SetAgePublic(ageInfo, makePublic != 0)); +} + +PYTHON_START_METHODS_TABLE(ptVault) + PYTHON_METHOD_NOARGS(ptVault, getPlayerInfo, "Returns a ptVaultNode of type kNodeTypePlayerInfo of the current player"), + PYTHON_METHOD_NOARGS(ptVault, getKIUsage, "Returns a tuple with usage statistics of the KI (# of pics, # of text notes, # of marker games)"), + PYTHON_METHOD_NOARGS(ptVault, getInbox, "Returns a ptVaultFolderNode of the current player's inbox folder."), + PYTHON_METHOD_NOARGS(ptVault, getAvatarOutfitFolder, "Do not use.\n" + "Returns a ptVaultFolderNode of the avatars outfit."), + PYTHON_METHOD_NOARGS(ptVault, getAvatarClosetFolder, "Do not use.\n" + "Returns a ptVaultFolderNode of the avatars outfit in their closet."), + PYTHON_METHOD_NOARGS(ptVault, getChronicleFolder, "Returns a ptVaultFolderNode of the current player's chronicle folder."), + PYTHON_METHOD_NOARGS(ptVault, getAgeJournalsFolder, "Returns a ptVaultFolderNode of the current player's age journals folder."), + PYTHON_METHOD_NOARGS(ptVault, getIgnoreListFolder, "Returns a ptVaultPlayerInfoListNode of the current player's ignore list folder."), + PYTHON_METHOD_NOARGS(ptVault, getBuddyListFolder, "Returns a ptVaultPlayerInfoListNode of the current player's buddy list folder."), + PYTHON_METHOD_NOARGS(ptVault, getPeopleIKnowAboutFolder, "Returns a ptVaultPlayerInfoListNode of the current player's people I know about (Recent) list folder."), + PYTHON_METHOD_NOARGS(ptVault, getAgesICanVisitFolder, "Returns a ptVaultFolderNode of ages I can visit"), + PYTHON_METHOD_NOARGS(ptVault, getAgesIOwnFolder, "Returns a ptVaultFolderNode of ages that I own"), + PYTHON_METHOD_NOARGS(ptVault, getInviteFolder, "Returns a ptVaultFolderNode of invites"), + PYTHON_METHOD_NOARGS(ptVault, getLinkToMyNeighborhood, "Returns a ptVaultAgeLinkNode that will go to my neighborhood"), + PYTHON_METHOD_NOARGS(ptVault, getLinkToCity, "Returns a ptVaultAgeLinkNode that will go to the city"), + PYTHON_METHOD(ptVault, getOwnedAgeLink, "Params: ageInfo\nReturns a ptVaultAgeLinkNode to my owned age(ageInfo)"), + PYTHON_METHOD(ptVault, getVisitAgeLink, "Params: ageInfo\nReturns a ptVaultAgeLinkNode for a visitor to age(ageInfo)"), + PYTHON_METHOD(ptVault, findChronicleEntry, "Params: entryName\nReturns a ptVaultNode of type kNodeTypeChronicle of the current player's chronicle entry by entryName."), + PYTHON_METHOD(ptVault, addChronicleEntry, "Params: entryName,type,string\nAdds an entry to the player's chronicle with a value of 'string'."), + PYTHON_METHOD_NOARGS(ptVault, getGlobalInbox, "Returns a ptVaultFolderNode of the global inbox folder."), +#ifdef GlobalInboxTestCode + PYTHON_BASIC_METHOD(ptVault, createGlobalInbox, "Creates the global inbox folder."), +#endif + PYTHON_METHOD(ptVault, findNode, "Params: templateNode\nFind the node matching the template"), + PYTHON_METHOD(ptVault, sendToDevice, "Params: node,deviceName\nSends a ptVaultNode object to an Age's device by deviceName."), + PYTHON_METHOD_NOARGS(ptVault, getPsnlAgeSDL, "Returns the personal age SDL"), + PYTHON_METHOD(ptVault, updatePsnlAgeSDL, "Params: pyrec\nUpdates the personal age SDL to the specified data"), + PYTHON_METHOD_NOARGS(ptVault, inMyPersonalAge, "Are we in the player's personal age?"), + PYTHON_METHOD_NOARGS(ptVault, inMyNeighborhoodAge, "Are we in the player's neighborhood age?"), + PYTHON_METHOD_NOARGS(ptVault, amOwnerOfCurrentAge, "Are we the owner of the current age?"), + PYTHON_METHOD_NOARGS(ptVault, amCzarOfCurrentAge, "Are we the czar (WTH is this?) of the current age?"), + PYTHON_METHOD(ptVault, amAgeOwner, "Params: ageInfo\nAre we the owner of the specified age?"), + PYTHON_METHOD(ptVault, amAgeCzar, "Params: ageInfo\nAre we the czar (WTH is this?) of the specified age?"), + PYTHON_METHOD(ptVault, registerMTStation, "Params: stationName,mtSpawnPoint\nRegisters this player at the specified mass-transit point"), + PYTHON_METHOD(ptVault, registerOwnedAge, "Params: link\nRegisters the specified age as owned by the player"), + PYTHON_METHOD(ptVault, unRegisterOwnedAge, "Params: ageFilename\nUnregisters the specified age so it's no longer owned by this player"), + PYTHON_METHOD(ptVault, registerVisitAge, "Params: link\nRegister this age as visitable by this player"), + PYTHON_METHOD(ptVault, unRegisterVisitAge, "Params: guid\nUnregisters the specified age so it can no longer be visited by this player"), + PYTHON_METHOD(ptVault, invitePlayerToAge, "Params: link,playerID\nSends an invitation to visit the age to the specified player"), + PYTHON_METHOD(ptVault, unInvitePlayerToAge, "Params: guid,playerID\nRevokes the invitation to visit the age"), + PYTHON_METHOD(ptVault, offerLinkToPlayer, "Params: link,playerID\nOffer a one-time link to the specified player"), + PYTHON_BASIC_METHOD(ptVault, createNeighborhood, "Creates a new neighborhood"), + PYTHON_METHOD(ptVault, setAgePublic, "Params: ageInfo,makePublic\nMakes the specified age public or private"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptVault, "Accessor class to the player's vault"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptVault, pyVault) + +PYTHON_CLASS_CHECK_IMPL(ptVault, pyVault) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVault, pyVault) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVault::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVault); + PYTHON_CLASS_IMPORT_END(m); +} + +#endif // BUILDING_PYPLASMA + +void pyVault::AddPlasmaConstantsClasses(PyObject *m) +{ + PYTHON_ENUM_START(PtVaultNodeTypes); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kInvalidNode, plVault::kNodeType_Invalid); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kVNodeMgrPlayerNode, plVault::kNodeType_VNodeMgrPlayer); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kVNodeMgrAgeNode, plVault::kNodeType_VNodeMgrAge); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kFolderNode, plVault::kNodeType_Folder); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kPlayerInfoNode, plVault::kNodeType_PlayerInfo); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kImageNode, plVault::kNodeType_Image); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kTextNoteNode, plVault::kNodeType_TextNote); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kSDLNode, plVault::kNodeType_SDL); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kAgeLinkNode, plVault::kNodeType_AgeLink); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kChronicleNode, plVault::kNodeType_Chronicle); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kPlayerInfoListNode, plVault::kNodeType_PlayerInfoList); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kAgeInfoNode, plVault::kNodeType_AgeInfo); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kAgeInfoListNode, plVault::kNodeType_AgeInfoList); + PYTHON_ENUM_ELEMENT(PtVaultNodeTypes, kMarkerGameNode, plVault::kNodeType_MarkerGame); + PYTHON_ENUM_END(m, PtVaultNodeTypes); + + PYTHON_ENUM_START(PtVaultStandardNodes); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kUserDefinedNode, plVault::kUserDefinedNode); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kInboxFolder, plVault::kInboxFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kBuddyListFolder, plVault::kBuddyListFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kIgnoreListFolder, plVault::kIgnoreListFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kPeopleIKnowAboutFolder, plVault::kPeopleIKnowAboutFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kChronicleFolder, plVault::kChronicleFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kAvatarOutfitFolder, plVault::kAvatarOutfitFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kAgeTypeJournalFolder, plVault::kAgeTypeJournalFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kSubAgesFolder, plVault::kSubAgesFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kHoodMembersFolder, plVault::kHoodMembersFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kAllPlayersFolder, plVault::kAllPlayersFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kAllAgeGlobalSDLNodesFolder, plVault::kAllAgeGlobalSDLNodesFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kAgeMembersFolder, plVault::kAgeMembersFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kAgeJournalsFolder, plVault::kAgeJournalsFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kAgeInstanceSDLNode, plVault::kAgeInstanceSDLNode); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kCanVisitFolder, plVault::kCanVisitFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kAgeOwnersFolder, plVault::kAgeOwnersFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kPlayerInfoNode, plVault::kPlayerInfoNode); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kPublicAgesFolder, plVault::kPublicAgesFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kAgesIOwnFolder, plVault::kAgesIOwnFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kAgesICanVisitFolder, plVault::kAgesICanVisitFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kAvatarClosetFolder, plVault::kAvatarClosetFolder); + PYTHON_ENUM_ELEMENT(PtVaultStandardNodes, kGlobalInboxFolder, plVault::kGlobalInboxFolder); + PYTHON_ENUM_END(m, PtVaultStandardNodes); + + PYTHON_ENUM_START(PtVaultTextNoteTypes); + PYTHON_ENUM_ELEMENT(PtVaultTextNoteTypes, kGeneric, plVault::kNoteType_Generic); + PYTHON_ENUM_ELEMENT(PtVaultTextNoteTypes, kCCRPetition, plVault::kNoteType_CCRPetition); + PYTHON_ENUM_END(m, PtVaultTextNoteTypes); + + PYTHON_ENUM_START(PtVaultTextNoteSubTypes); + PYTHON_ENUM_ELEMENT(PtVaultTextNoteSubTypes, kGeneric, plVault::kNoteSubType_Generic); + PYTHON_ENUM_END(m, PtVaultTextNoteSubTypes); + + PYTHON_ENUM_START(PtVaultCallbackTypes); + PYTHON_ENUM_ELEMENT(PtVaultCallbackTypes, kVaultConnected, pyVault::kVaultConnected); + PYTHON_ENUM_ELEMENT(PtVaultCallbackTypes, kVaultNodeSaved, pyVault::kVaultNodeSaved); + PYTHON_ENUM_ELEMENT(PtVaultCallbackTypes, kVaultNodeRefAdded, pyVault::kVaultNodeRefAdded); + PYTHON_ENUM_ELEMENT(PtVaultCallbackTypes, kVaultRemovingNodeRef, pyVault::kVaultRemovingNodeRef); + PYTHON_ENUM_ELEMENT(PtVaultCallbackTypes, kVaultNodeRefRemoved, pyVault::kVaultNodeRefRemoved); + PYTHON_ENUM_ELEMENT(PtVaultCallbackTypes, kVaultNodeInitialized, pyVault::kVaultNodeInitialized); + PYTHON_ENUM_ELEMENT(PtVaultCallbackTypes, kVaultOperationFailed, pyVault::kVaultOperationFailed); + PYTHON_ENUM_ELEMENT(PtVaultCallbackTypes, kVaultNodeAdded, pyVault::kVaultNodeAdded); + PYTHON_ENUM_ELEMENT(PtVaultCallbackTypes, kVaultDisconnected, pyVault::kVaultDisconnected); + PYTHON_ENUM_END(m, PtVaultCallbackTypes); + + PYTHON_ENUM_START(PtVaultNotifyTypes); + PYTHON_ENUM_ELEMENT(PtVaultNotifyTypes, kRegisteredOwnedAge, plVaultNotifyMsg::kRegisteredOwnedAge); + PYTHON_ENUM_ELEMENT(PtVaultNotifyTypes, kRegisteredVisitAge, plVaultNotifyMsg::kRegisteredVisitAge); + PYTHON_ENUM_ELEMENT(PtVaultNotifyTypes, kUnRegisteredOwnedAge, plVaultNotifyMsg::kUnRegisteredOwnedAge); + PYTHON_ENUM_ELEMENT(PtVaultNotifyTypes, kUnRegisteredVisitAge, plVaultNotifyMsg::kUnRegisteredVisitAge); + PYTHON_ENUM_ELEMENT(PtVaultNotifyTypes, kPublicAgeCreated, plVaultNotifyMsg::kPublicAgeCreated); + PYTHON_ENUM_ELEMENT(PtVaultNotifyTypes, kPublicAgeRemoved, plVaultNotifyMsg::kPublicAgeRemoved); + PYTHON_ENUM_END(m, PtVaultNotifyTypes); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNode.cpp new file mode 100644 index 00000000..793606fa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNode.cpp @@ -0,0 +1,236 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultImageNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultImageNode.h" +#ifndef BUILDING_PYPLASMA +#include "pyVault.h" +#endif +#include "pyImage.h" +#include "plPipeline.h" +#include "cyMisc.h" + +#include "hsResMgr.h" +#include "../plGImage/plMipmap.h" +#include "../plVault/plVault.h" +#include "../pnMessage/plRefMsg.h" +#include "../plNetClient/plNetClientMgr.h" + + +static unsigned s_keyseq; + +//============================================================================ +static plKey CreateAndRefImageKey (unsigned nodeId, plMipmap * mipmap) { + char keyName[MAX_PATH]; + StrPrintf(keyName, arrsize(keyName), "VaultImg_%u_%u", nodeId, s_keyseq++); + + plKey key = hsgResMgr::ResMgr()->NewKey(keyName, mipmap, plLocation::kGlobalFixedLoc); + + hsgResMgr::ResMgr()->AddViaNotify( + key, + NEW(plGenRefMsg)( + plNetClientMgr::GetInstance()->GetKey(), + plRefMsg::kOnCreate, + 0, + plNetClientMgr::kVaultImage + ), + plRefFlags::kActiveRef + ); + + return key; +} + +// should only be created from C++ side +pyVaultImageNode::pyVaultImageNode(RelVaultNode* nfsNode) +: pyVaultNode(nfsNode) +, fMipmapKey(nil) +, fMipmap(nil) +{ +} + +//create from the Python side +pyVaultImageNode::pyVaultImageNode(int n) +: pyVaultNode(NEWZERO(RelVaultNode)) +, fMipmapKey(nil) +, fMipmap(nil) +{ + fNode->SetNodeType(plVault::kNodeType_Image); +} + +pyVaultImageNode::~pyVaultImageNode () { + if (fMipmap && fMipmapKey) + fMipmapKey->UnRefObject(); +} + + +//================================================================== +// class RelVaultNode : public plVaultNode +// +void pyVaultImageNode::Image_SetTitle( const char * text ) +{ + if (!fNode) + return; + + wchar * wStr = hsStringToWString(text); + + VaultImageNode image(fNode); + image.SetImageTitle(wStr); + delete [] wStr; +} + +void pyVaultImageNode::Image_SetTitleW( const wchar_t* text ) +{ + if (!fNode) + return; + + VaultImageNode image(fNode); + image.SetImageTitle(text); +} + +std::string pyVaultImageNode::Image_GetTitle( void ) +{ + if (!fNode) + return ""; + + VaultImageNode image(fNode); + + std::string retVal = ""; + if (image.title) + { + char* temp = hsWStringToString(image.title); + retVal = temp; + delete [] temp; + } + + return retVal; +} + +std::wstring pyVaultImageNode::Image_GetTitleW( void ) +{ + if (!fNode) + return L""; + + VaultImageNode image(fNode); + return image.title ? image.title : L""; +} + +PyObject* pyVaultImageNode::Image_GetImage( void ) +{ + if (!fNode) + PYTHON_RETURN_NONE; + + if (!fMipmap) { + VaultImageNode access(fNode); + if (access.ExtractImage(&fMipmap)) { + fMipmapKey = fMipmap->GetKey(); + if (!fMipmapKey) + fMipmapKey = CreateAndRefImageKey(fNode->nodeId, fMipmap); + else + fMipmapKey->RefObject(); + } + } + + return pyImage::New(fMipmap); +} + +void pyVaultImageNode::Image_SetImage(pyImage& image) +{ + if (!fNode) + return; + + if (fMipmapKey) { + fMipmapKey->UnRefObject(); + fMipmapKey = nil; + fMipmap = nil; + } + + if (fMipmap = image.GetImage()) { + VaultImageNode access(fNode); + access.StuffImage(fMipmap); + + fMipmapKey = image.GetKey(); + if (!fMipmapKey) + fMipmapKey = CreateAndRefImageKey(fNode->nodeId, fMipmap); + else + fMipmapKey->RefObject(); + } +} + +void pyVaultImageNode::SetImageFromBuf( PyObject * pybuf ) +{ + if (!fNode) + return; + + if (fMipmapKey) { + fMipmapKey->UnRefObject(); + fMipmapKey = nil; + fMipmap = nil; + } + + byte * buffer = nil; + int bytes; + PyObject_AsReadBuffer(pybuf, (const void **)&buffer, &bytes); + if (buffer) { + VaultImageNode access(fNode); + access.SetImageData(buffer, bytes); + access.SetImageType(VaultImageNode::kJPEG); + } +} + +void pyVaultImageNode::SetImageFromScrShot() +{ + if (!fNode) + return; + + if (fMipmapKey) { + fMipmapKey->UnRefObject(); + fMipmapKey = nil; + fMipmap = nil; + } + + if (cyMisc::GetPipeline()) { + VaultImageNode access(fNode); + fMipmap = NEW(plMipmap); + if (cyMisc::GetPipeline()->CaptureScreen(fMipmap, false, 800, 600)) { + fMipmapKey = fMipmap->GetKey(); + if (!fMipmapKey) + fMipmapKey = CreateAndRefImageKey(fNode->nodeId, fMipmap); + else + fMipmapKey->RefObject(); + access.StuffImage(fMipmap); + } + else { + access.SetImageData(nil, 0); + access.SetImageType(VaultImageNode::kNone); + DEL(fMipmap); + fMipmap = nil; + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNode.h new file mode 100644 index 00000000..dc7bdd26 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNode.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultImageNode_h_ +#define _pyVaultImageNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultImageNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyVaultNode.h" +#include "../pnKeyedObject/plKey.h" + +struct RelVaultNode; + + +class pyVaultImageNode : public pyVaultNode +{ + plKey fMipmapKey; + plMipmap * fMipmap; + +protected: + // should only be created from C++ side + pyVaultImageNode(RelVaultNode* nfsNode); + + //create from the Python side + pyVaultImageNode(int n=0); + + +public: + ~pyVaultImageNode (); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultImageNode); + static PyObject *New(RelVaultNode* nfsNode); + static PyObject *New(int n=0); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultImageNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultImageNode); // converts a PyObject to a pyVaultImageNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + +//================================================================== +// class RelVaultNode : public plVaultNode +// + void Image_SetTitle( const char * text ); + void Image_SetTitleW( const wchar_t * text ); + std::string Image_GetTitle( void ); + std::wstring Image_GetTitleW( void ); + + PyObject* Image_GetImage( void ); // returns pyImage + void Image_SetImage(pyImage& image); + + void SetImageFromBuf( PyObject * buf ); + + void SetImageFromScrShot(); + +}; + +#endif // _pyVaultImageNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNodeGlue.cpp new file mode 100644 index 00000000..214c332b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultImageNodeGlue.cpp @@ -0,0 +1,230 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultImageNode.h" +#include "pyImage.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultImageNode, pyVaultImageNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultImageNode, pyVaultImageNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultImageNode) + +PYTHON_INIT_DEFINITION(ptVaultImageNode, args, keywords) +{ + int n = 0; + if (!PyArg_ParseTuple(args, "|i", &n)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional int"); + PYTHON_RETURN_INIT_ERROR; + } + // we don't really do anything? Not according to the associated constructor. Odd... + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptVaultImageNode, imageSetTitle, args) +{ + char* title; + if (!PyArg_ParseTuple(args, "s", &title)) + { + PyErr_SetString(PyExc_TypeError, "imageSetTitle expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Image_SetTitle(title); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultImageNode, imageGetTitle) +{ + return PyString_FromString(self->fThis->Image_GetTitle().c_str()); +} + +PYTHON_METHOD_DEFINITION(ptVaultImageNode, imageSetImage, args) +{ + PyObject* imageObj = NULL; + if (!PyArg_ParseTuple(args, "O", &imageObj)) + { + PyErr_SetString(PyExc_TypeError, "imageSetImage expects a ptImage"); + PYTHON_RETURN_ERROR; + } + if (!pyImage::Check(imageObj)) + { + PyErr_SetString(PyExc_TypeError, "imageSetImage expects a ptImage"); + PYTHON_RETURN_ERROR; + } + pyImage* image = pyImage::ConvertFrom(imageObj); + self->fThis->Image_SetImage(*image); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultImageNode, imageGetImage) +{ + return self->fThis->Image_GetImage(); +} + +PYTHON_METHOD_DEFINITION(ptVaultImageNode, setTitle, args) +{ + char* title; + if (!PyArg_ParseTuple(args, "s", &title)) + { + PyErr_SetString(PyExc_TypeError, "setTitle expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Image_SetTitle(title); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultImageNode, setTitleW, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "setTitleW expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* title = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, title, strLen); + title[strLen] = L'\0'; + self->fThis->Image_SetTitleW(title); + delete [] title; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* title = PyString_AsString(textObj); + self->fThis->Image_SetTitle(title); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "setTitleW expects a unicode string"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultImageNode, getTitle) +{ + return PyString_FromString(self->fThis->Image_GetTitle().c_str()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultImageNode, getTitleW) +{ + std::wstring retVal = self->fThis->Image_GetTitleW(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_METHOD_DEFINITION(ptVaultImageNode, setImage, args) +{ + PyObject* imageObj = NULL; + if (!PyArg_ParseTuple(args, "O", &imageObj)) + { + PyErr_SetString(PyExc_TypeError, "setImage expects a ptImage"); + PYTHON_RETURN_ERROR; + } + if (!pyImage::Check(imageObj)) + { + PyErr_SetString(PyExc_TypeError, "setImage expects a ptImage"); + PYTHON_RETURN_ERROR; + } + pyImage* image = pyImage::ConvertFrom(imageObj); + self->fThis->Image_SetImage(*image); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultImageNode, getImage) +{ + return self->fThis->Image_GetImage(); +} + +PYTHON_METHOD_DEFINITION(ptVaultImageNode, setImageFromBuf, args) +{ + PyObject* buf = NULL; + if (!PyArg_ParseTuple(args, "O", &buf)) + { + PyErr_SetString(PyExc_TypeError, "setImageFromBuf expects a buffer object"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetImageFromBuf(buf); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptVaultImageNode, setImageFromScrShot, SetImageFromScrShot) + +PYTHON_START_METHODS_TABLE(ptVaultImageNode) + // legacy glue + PYTHON_METHOD(ptVaultImageNode, imageSetTitle, "Params: title\nLEGACY\nSets the title (caption) of this image node"), + PYTHON_METHOD_NOARGS(ptVaultImageNode, imageGetTitle, "LEGACY\nReturns the title (caption) of this image node"), + PYTHON_METHOD(ptVaultImageNode, imageSetImage, "Params: image\nLEGACY\nSets the image(ptImage) of this image node"), + PYTHON_METHOD_NOARGS(ptVaultImageNode, imageGetImage, "LEGACY\nReturns the image(ptImage) of this image node"), + // new glue + PYTHON_METHOD(ptVaultImageNode, setTitle, "Params: title\nSets the title (caption) of this image node"), + PYTHON_METHOD(ptVaultImageNode, setTitleW, "Params: title\nUnicode version of setTitle"), + PYTHON_METHOD_NOARGS(ptVaultImageNode, getTitle, "Returns the title (caption) of this image node"), + PYTHON_METHOD_NOARGS(ptVaultImageNode, getTitleW, "Unicode version of getTitle"), + PYTHON_METHOD(ptVaultImageNode, setImage, "Params: image\nSets the image(ptImage) of this image node"), + PYTHON_METHOD_NOARGS(ptVaultImageNode, getImage, "Returns the image(ptImage) of this image node"), + PYTHON_METHOD(ptVaultImageNode, setImageFromBuf, "Params: buf\nSets our image from a buffer"), + PYTHON_BASIC_METHOD(ptVaultImageNode, setImageFromScrShot, "Grabs a screenshot and stuffs it into this node"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultImageNode, pyVaultNode, "Params: n=0\nPlasma vault image node"); + +// required functions for PyObject interoperability +PyObject *pyVaultImageNode::New(RelVaultNode* nfsNode) +{ + ptVaultImageNode *newObj = (ptVaultImageNode*)ptVaultImageNode_type.tp_new(&ptVaultImageNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PyObject *pyVaultImageNode::New(int n /* =0 */) +{ + ptVaultImageNode *newObj = (ptVaultImageNode*)ptVaultImageNode_type.tp_new(&ptVaultImageNode_type, NULL, NULL); + // oddly enough, nothing to do here + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultImageNode, pyVaultImageNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultImageNode, pyVaultImageNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultImageNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultImageNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNode.cpp new file mode 100644 index 00000000..2313b8a8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNode.cpp @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultMarkerGameNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsStlUtils.h" + +#include "pyVaultMarkerGameNode.h" + +#include "../plVault/plVault.h" + +// should only be created from C++ side +pyVaultMarkerGameNode::pyVaultMarkerGameNode(RelVaultNode* nfsNode) +: pyVaultNode(nfsNode) +{ +} + +//create from the Python side +pyVaultMarkerGameNode::pyVaultMarkerGameNode(int n) +: pyVaultNode(NEWZERO(RelVaultNode)) +{ + fNode->SetNodeType(plVault::kNodeType_MarkerGame); +} + +//================================================================== +// class RelVaultNode : public plVaultNode +// + +const char * pyVaultMarkerGameNode::GetGameName () const +{ + fGameName[0] = 0; + + if (fNode) { + VaultMarkerGameNode access(fNode); + StrToAnsi(fGameName, access.gameName, arrsize(fGameName)); + } + return fGameName; +} + +void pyVaultMarkerGameNode::SetGameName (const char v[]) +{ + if (fNode) { + VaultMarkerGameNode access(fNode); + wchar unicode[kMaxVaultNodeStringLength]; + StrToUnicode(unicode, v, arrsize(unicode)); + access.SetGameName(unicode); + } +} + +const char * pyVaultMarkerGameNode::GetGameGuid () const +{ + fGameGuid[0] = 0; + + if (fNode) { + VaultMarkerGameNode access(fNode); + GuidToString(access.gameGuid, fGameGuid, arrsize(fGameGuid)); + } + return fGameGuid; +} + +void pyVaultMarkerGameNode::SetGameGuid (const char v[]) +{ + if (fNode) { + VaultMarkerGameNode access(fNode); + Uuid uuid; + GuidFromString(v, &uuid); + access.SetGameGuid(uuid); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNode.h new file mode 100644 index 00000000..07b86b41 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNode.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultMarkerGameNode_h_ +#define _pyVaultMarkerGameNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultMarkerGameNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" + +#include +#include "pyGlueHelpers.h" +#include "pyVaultNode.h" + +#include "../pnNetBase/pnNetBase.h" + +struct RelVaultNode; + +class pyVaultMarkerGameNode : public pyVaultNode +{ +private: + mutable char fGameName[kMaxVaultNodeStringLength]; + mutable char fGameGuid[64]; + +protected: + // should only be created from C++ side + pyVaultMarkerGameNode(RelVaultNode* vaultNode); + + //create from the Python side + pyVaultMarkerGameNode(int n=0); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultMarkerGameNode); + static PyObject *New(RelVaultNode* vaultNode); + static PyObject *New(int n=0); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultMarkerGameNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultMarkerGameNode); // converts a PyObject to a pyVaultMarkerGameNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + +//================================================================== +// class RelVaultNode : public plVaultNode +// + const char * GetGameName () const; + void SetGameName (const char v[]); + + const char * GetGameGuid () const; + void SetGameGuid (const char v[]); +}; + +#endif // _pyVaultMarkerGameNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNodeGlue.cpp new file mode 100644 index 00000000..d4078ba9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultMarkerGameNodeGlue.cpp @@ -0,0 +1,125 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultMarkerGameNode.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultMarkerGameNode, pyVaultMarkerGameNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultMarkerGameNode, pyVaultMarkerGameNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultMarkerGameNode) + +PYTHON_INIT_DEFINITION(ptVaultMarkerGameNode, args, keywords) +{ + int n = 0; + if (!PyArg_ParseTuple(args, "|i", &n)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional int"); + PYTHON_RETURN_INIT_ERROR; + } + // we don't really do anything? Not according to the associated constructor. Odd... + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultMarkerGameNode, getGameName) +{ + return PyString_FromString(self->fThis->GetGameName()); +} + +PYTHON_METHOD_DEFINITION(ptVaultMarkerGameNode, setGameName, args) +{ + char * name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "setGameName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetGameName(name); + + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultMarkerGameNode, getGameGuid) +{ + return PyString_FromString(self->fThis->GetGameGuid()); +} + +PYTHON_METHOD_DEFINITION(ptVaultMarkerGameNode, setGameGuid, args) +{ + char * guid; + if (!PyArg_ParseTuple(args, "s", &guid)) + { + PyErr_SetString(PyExc_TypeError, "setGameGuid expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetGameGuid(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptVaultMarkerGameNode) + PYTHON_METHOD_NOARGS(ptVaultMarkerGameNode, getGameName, "Returns the marker game's name"), + PYTHON_METHOD(ptVaultMarkerGameNode, setGameName, "Params: name\nSets marker game's name"), + PYTHON_METHOD_NOARGS(ptVaultMarkerGameNode, getGameGuid, "Returns the marker game's guid"), + PYTHON_METHOD(ptVaultMarkerGameNode, setGameGuid, "Params: guid\nSets the marker game's guid"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultMarkerGameNode, pyVaultNode, "Params: n=0\nPlasma vault age info node"); + +// required functions for PyObject interoperability +PyObject *pyVaultMarkerGameNode::New(RelVaultNode* nfsNode) +{ + ptVaultMarkerGameNode *newObj = (ptVaultMarkerGameNode*)ptVaultMarkerGameNode_type.tp_new(&ptVaultMarkerGameNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PyObject *pyVaultMarkerGameNode::New(int n /* =0 */) +{ + ptVaultMarkerGameNode *newObj = (ptVaultMarkerGameNode*)ptVaultMarkerGameNode_type.tp_new(&ptVaultMarkerGameNode_type, NULL, NULL); + // oddly enough, nothing to do here + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultMarkerGameNode, pyVaultMarkerGameNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultMarkerGameNode, pyVaultMarkerGameNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultMarkerGameNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultMarkerGameNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNode.cpp new file mode 100644 index 00000000..41790f25 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNode.cpp @@ -0,0 +1,829 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultNode.h" +#ifndef BUILDING_PYPLASMA +#include "pyVault.h" +#endif +#include "pyImage.h" +#include "pyDniCoordinates.h" +#include "pyVaultNodeRef.h" +#include "pyVaultFolderNode.h" +#include "pyVaultPlayerInfoListNode.h" +#include "pyVaultImageNode.h" +#include "pyVaultTextNoteNode.h" +#include "pyVaultAgeLinkNode.h" +#include "pyVaultChronicleNode.h" +#include "pyVaultPlayerInfoNode.h" +#include "pyVaultMarkerGameNode.h" +#include "pyVaultAgeInfoNode.h" +#include "pyVaultAgeInfoListNode.h" +#include "pyVaultPlayerNode.h" +#include "pyVaultSDLNode.h" +#ifndef BUILDING_PYPLASMA +#include "pyVaultSystemNode.h" +#endif + +#include "../plGImage/plMipmap.h" + +#include "../plVault/plVault.h" + +#ifndef BUILDING_PYPLASMA +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClientComm/plNetClientComm.h" +#endif + +#include + + +/////////////////////////////////////////////////////////////////////////// + +static void __cdecl LogDumpProc ( + void * , + const wchar fmt[], + ... +) { + va_list args; + va_start(args, fmt); + LogMsgV(kLogDebug, fmt, args); + va_end(args); +} + +pyVaultNode::pyVaultNodeOperationCallback::pyVaultNodeOperationCallback(PyObject * cbObject) +: fCbObject( cbObject ) +, fNode(nil) +, fPyNodeRef(nil) +{ + Py_XINCREF( fCbObject ); +} + +pyVaultNode::pyVaultNodeOperationCallback::~pyVaultNodeOperationCallback() +{ + Py_XDECREF( fCbObject ); +} + +void pyVaultNode::pyVaultNodeOperationCallback::VaultOperationStarted( UInt32 context ) +{ + if ( fCbObject ) + { + // Call the callback. + if (PyObject_HasAttrString( fCbObject, "vaultOperationStarted" )) + { + PyObject* func = nil; + func = PyObject_GetAttrString( fCbObject, "vaultOperationStarted" ); + if ( func ) + { + if ( PyCallable_Check(func)>0 ) + { + PyObject* retVal = PyObject_CallMethod(fCbObject, "vaultOperationStarted", "l", context); + Py_XDECREF(retVal); + } + } + } + } +} + + +void pyVaultNode::pyVaultNodeOperationCallback::VaultOperationComplete( UInt32 context, int resultCode ) +{ + if ( fCbObject ) + { + // Call the callback. + if (PyObject_HasAttrString( fCbObject, "vaultOperationComplete" )) + { + PyObject* func = nil; + func = PyObject_GetAttrString( fCbObject, "vaultOperationComplete" ); + if ( func ) + { + if ( PyCallable_Check(func)>0 ) + { + PyObject * pyNode = pyVaultNode::New(fNode); + PyObject* t = PyTuple_New(2); + PyTuple_SetItem(t, 0, pyNode); + PyTuple_SetItem(t, 1, fPyNodeRef); + PyObject* retVal = PyObject_CallMethod(fCbObject, "vaultOperationComplete", "lOi", context, t, resultCode); + Py_XDECREF(retVal); + Py_DECREF(t); + } + } + } + } + + DEL(this); // commit hara-kiri +} + +void pyVaultNode::pyVaultNodeOperationCallback::SetNode (RelVaultNode * rvn) { + if (rvn) + rvn->IncRef(); + SWAP(rvn, fNode); + if (rvn) + rvn->DecRef(); +} + +RelVaultNode * pyVaultNode::pyVaultNodeOperationCallback::GetNode () { + return fNode; +} + +// only for python glue, do NOT call +pyVaultNode::pyVaultNode() +: fNode(nil) +, fCreateAgeGuid(nil) +, fCreateAgeName(nil) +{ +} + +// should only be created from C++ side +pyVaultNode::pyVaultNode( RelVaultNode* nfsNode ) +: fNode(nfsNode) +, fCreateAgeGuid(nil) +, fCreateAgeName(nil) +{ + if (fNode) + fNode->IncRef("pyVaultNode"); +} + +pyVaultNode::~pyVaultNode() +{ + if (fNode) + fNode->DecRef("pyVaultNode"); + FREE(fCreateAgeGuid); + FREE(fCreateAgeName); +} + + +RelVaultNode* pyVaultNode::GetNode() const +{ + return fNode; +} + + +// override the equals to operator +bool pyVaultNode::operator==(const pyVaultNode &vaultNode) const +{ + RelVaultNode* ours = GetNode(); + RelVaultNode* theirs = vaultNode.GetNode(); + if (ours == nil && theirs == nil) + return true; + if (ours == nil || theirs == nil) + return false; + if (ours->nodeId == theirs->nodeId) + return true; + return ours->Matches(theirs); +} + +// public getters +UInt32 pyVaultNode::GetID( void ) +{ + if (fNode) + return fNode->nodeId; + return 0; +} + +UInt32 pyVaultNode::GetType( void ) +{ + if (fNode) + return fNode->nodeType; + return 0; +} + +UInt32 pyVaultNode::GetOwnerNodeID( void ) +{ + hsAssert(false, "eric, port?"); +// if (fNode) +// return fNode->ownerId; + return 0; +} + +PyObject* pyVaultNode::GetOwnerNode( void ) +{ + hsAssert(false, "eric, port?"); +/* + if (fNode) + { + const plVaultPlayerInfoNode* node = fNode->GetOwnerNode(); + if (node) + return pyVaultPlayerInfoNode::New((plVaultPlayerInfoNode*)node); + } +*/ + // just return a None object + PYTHON_RETURN_NONE; +} + +UInt32 pyVaultNode::GetModifyTime( void ) +{ + if (fNode) + return fNode->modifyTime; + return 0; +} + +UInt32 pyVaultNode::GetCreatorNodeID( void ) +{ + if (fNode) + return fNode->creatorId; + return 0; +} + +PyObject* pyVaultNode::GetCreatorNode( void ) +{ + PyObject * result = nil; + if (fNode) + { + RelVaultNode * templateNode = NEWZERO(RelVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_PlayerInfo); + VaultPlayerInfoNode plrInfo(templateNode); + plrInfo.SetPlayerId(fNode->creatorId); + + if (RelVaultNode * rvn = VaultGetNodeIncRef(templateNode)) { + result = pyVaultPlayerInfoNode::New(rvn); + rvn->DecRef(); + } + templateNode->DecRef(); + } + + if (result) + return result; + + // just return a None object + PYTHON_RETURN_NONE; +} + +UInt32 pyVaultNode::GetCreateTime( void ) +{ + if (fNode) + return fNode->createTime; + return 0; +} + +UInt32 pyVaultNode::GetCreateAgeTime( void ) +{ + hsAssert(false, "eric, port?"); + + // for now, just return the earth-time the node was created + return GetCreateTime(); +} + +const char * pyVaultNode::GetCreateAgeName( void ) +{ + if (!fNode) + return ""; + + if (fCreateAgeName) + return fCreateAgeName; + + if (fNode) { + if (fNode->createAgeName) + fCreateAgeName = StrDupToAnsi(fNode->createAgeName); + else + fCreateAgeName = StrDup(""); + } + + return fCreateAgeName; +} + +const char * pyVaultNode::GetCreateAgeGuid( void ) +{ + if (!fNode) + return ""; + + if (fCreateAgeGuid) + return fCreateAgeGuid; + + if (fNode) { + fCreateAgeGuid = (char*)ALLOC(64); + GuidToString(fNode->createAgeUuid, fCreateAgeGuid, 64); + } + + return fCreateAgeGuid; +} + +PyObject* pyVaultNode::GetCreateAgeCoords () { + if (!fNode) + PYTHON_RETURN_NONE; + + return pyDniCoordinates::New(nil); +} + +void pyVaultNode::SetID( UInt32 v ) +{ + hsAssert(false, "Why are you changing the node id?"); +} + +void pyVaultNode::SetType( int v ) +{ + ASSERT(fNode); + if (fNode->nodeId) { + FATAL("pyVaultNode: You may not change the type of a vault node"); + return; + } + + fNode->SetNodeType(v); +} + +void pyVaultNode::SetOwnerNodeID( UInt32 v ) +{ + hsAssert(false, "eric, implement me."); +} + +void pyVaultNode::SetCreatorNodeID( UInt32 v ) +{ + ASSERT(fNode); + if (fNode->nodeId) { + FATAL("pyVaultNode: You may not change the type of a vault node"); + return; + } + + fNode->SetCreatorId(v); +} + +void pyVaultNode::SetCreateAgeName( const char * v ) +{ + FREE(fCreateAgeName); + fCreateAgeName = nil; + + ASSERT(fNode); + wchar str[MAX_PATH]; + StrToUnicode(str, v, arrsize(str)); + fNode->SetCreateAgeName(str); +} + +void pyVaultNode::SetCreateAgeGuid( const char * v ) +{ + FREE(fCreateAgeGuid); + fCreateAgeGuid = nil; + + ASSERT(fNode); + Uuid uuid; + GuidFromString(v, &uuid); + fNode->SetCreateAgeUuid(uuid); +} + + +///////////////////////////////////////////////// +// Vault Node API + +// Add child node +PyObject* pyVaultNode::AddNode(pyVaultNode* pynode, PyObject* cbObject, UInt32 cbContext) +{ + pyVaultNodeOperationCallback * cb = NEWZERO(pyVaultNodeOperationCallback)(cbObject); + + if ( fNode && pynode && pynode->GetNode() ) + { + // Hack the callbacks until vault notification is in place + cb->VaultOperationStarted(cbContext); + + int hsResult = hsOK; + if ( !pynode->GetID() ) + { + // Block here until node is created and fetched =( + ASSERT(pynode->GetNode()->nodeType); + ENetError result; + RelVaultNode * newNode = VaultCreateNodeAndWaitIncRef( + pynode->GetNode(), + &result + ); + + if (newNode) { + newNode->IncRef(); + pynode->fNode->DecRef(); + pynode->fNode = newNode; + } + else { + hsResult = hsFail; + } + } + + // Block here until we have the child node =( + VaultAddChildNodeAndWait(fNode->nodeId, pynode->fNode->nodeId, NetCommGetPlayer()->playerInt); + + PyObject * nodeRef = cb->fPyNodeRef = pyVaultNodeRef::New(fNode, pynode->fNode); + Py_INCREF(nodeRef); // incref it, because we MUST return a new PyObject, and the callback "steals" the ref from us + cb->SetNode(pynode->fNode); + cb->VaultOperationComplete(cbContext, hsResult); + + return nodeRef; + } + else + { + // manually make the callback + cb->VaultOperationStarted( cbContext ); + cb->VaultOperationComplete( cbContext, hsFail ); + } + + // just return a None object + PYTHON_RETURN_NONE; +} + +// Link a node to this one +void pyVaultNode::LinkToNode(int nodeID, PyObject* cbObject, UInt32 cbContext) +{ + pyVaultNodeOperationCallback * cb = NEWZERO(pyVaultNodeOperationCallback)( cbObject ); + + if (fNode && nodeID) + { + // Hack the callbacks until vault notification is in place + cb->VaultOperationStarted( cbContext ); + + VaultAddChildNodeAndWait(fNode->nodeId, nodeID, NetCommGetPlayer()->playerInt); + if (RelVaultNode * rvn = VaultGetNodeIncRef(nodeID)) { + cb->SetNode(rvn); + cb->fPyNodeRef = pyVaultNodeRef::New(fNode, rvn); + rvn->DecRef(); + } + + cb->VaultOperationComplete( cbContext, hsOK ); + } + else + { + // manually make the callback + cb->VaultOperationStarted( cbContext ); + cb->VaultOperationComplete( cbContext, hsFail ); + } +} + +// Remove child node +hsBool pyVaultNode::RemoveNode( pyVaultNode& pynode, PyObject* cbObject, UInt32 cbContext ) +{ + pyVaultNodeOperationCallback * cb = NEWZERO(pyVaultNodeOperationCallback)( cbObject ); + + if (fNode && pynode.fNode) + { + // Hack the callbacks until vault notification is in place + cb->VaultOperationStarted( cbContext ); + + VaultRemoveChildNode(fNode->nodeId, pynode.fNode->nodeId, nil, nil); + + cb->SetNode(pynode.fNode); + cb->fPyNodeRef = pyVaultNodeRef::New(fNode, pynode.fNode); + cb->VaultOperationComplete( cbContext, hsOK ); + return true; + } + else + { + // manually make the callback + cb->VaultOperationStarted( cbContext ); + cb->VaultOperationComplete( cbContext, hsFail ); + } + + return false; +} + +// Remove all child nodes +void pyVaultNode::RemoveAllNodes( void ) +{ + if (!fNode) + return; + + ARRAY(unsigned) nodeIds; + fNode->GetChildNodeIds(&nodeIds, 1); + for (unsigned i = 0; i < nodeIds.Count(); ++i) + VaultRemoveChildNode(fNode->nodeId, nodeIds[i], nil, nil); +} + +// Add/Save this node to vault +void pyVaultNode::Save(PyObject* cbObject, UInt32 cbContext) +{ + // If the node doesn't have an id, then use it as a template to create the node in the vault, + // otherwise just ignore the save request since vault nodes are now auto-saved. + if (!fNode->nodeId && fNode->nodeType) { + ENetError result; + if (RelVaultNode * node = VaultCreateNodeAndWaitIncRef(fNode, &result)) { + fNode->DecRef(); + fNode = node; + } + } + pyVaultNodeOperationCallback * cb = NEWZERO(pyVaultNodeOperationCallback)( cbObject ); + cb->SetNode(fNode); + cb->VaultOperationStarted( cbContext ); + cb->VaultOperationComplete( cbContext, hsOK ); +} + +// Save this node and all child nodes that need saving. +void pyVaultNode::SaveAll(PyObject* cbObject, UInt32 cbContext) +{ + // Nodes are now auto-saved + pyVaultNodeOperationCallback * cb = NEWZERO(pyVaultNodeOperationCallback)( cbObject ); + cb->VaultOperationStarted( cbContext ); + cb->VaultOperationComplete( cbContext, hsOK ); +} + +void pyVaultNode::ForceSave() +{ + if (!fNode->nodeId && fNode->nodeType) { + ENetError result; + if (RelVaultNode * node = VaultCreateNodeAndWaitIncRef(fNode, &result)) { + fNode->DecRef(); + fNode = node; + } + } + else + VaultForceSaveNodeAndWait(fNode); +} + +// Send this node to the destination client node. will be received in it's inbox folder. +void pyVaultNode::SendTo(UInt32 destClientNodeID, PyObject* cbObject, UInt32 cbContext ) +{ + pyVaultNodeOperationCallback * cb = NEWZERO(pyVaultNodeOperationCallback)( cbObject ); + + if (fNode) + { + // If the node doesn't have an id, then use it as a template to create the node in the vault, + if (!fNode->nodeId && fNode->nodeType) { + ENetError result; + if (RelVaultNode * node = VaultCreateNodeAndWaitIncRef(fNode, &result)) { + fNode->DecRef(); + fNode = node; + } + } + + // Hack the callbacks until vault notification is in place + cb->VaultOperationStarted( cbContext ); + + VaultSendNode(fNode, destClientNodeID); + + cb->VaultOperationComplete( cbContext, hsOK ); + } + else + { + // manually make the callback + cb->VaultOperationStarted( cbContext ); + cb->VaultOperationComplete( cbContext, hsFail ); + } +} + +// Get all child nodes, simulating a NodeRef list for legacy compatibility +PyObject* pyVaultNode::GetChildNodeRefList() +{ + // create the list + PyObject* pyEL = PyList_New(0); + + // fill in the elements list of this folder + if (fNode) + { + ARRAY(RelVaultNode*) nodes; + fNode->GetChildNodesIncRef( + 1, + &nodes + ); + + for (unsigned i = 0; i < nodes.Count(); ++i) { + PyObject* elementObj = pyVaultNodeRef::New(fNode, nodes[i]); + PyList_Append(pyEL, elementObj); + Py_DECREF(elementObj); + nodes[i]->DecRef(); + } + } + + return pyEL; +} + +int pyVaultNode::GetChildNodeCount() +{ + if (!fNode) + return 0; + + ARRAY(unsigned) nodeIds; + fNode->GetChildNodeIds(&nodeIds, 1); + + return nodeIds.Count(); +} + +// Get the client ID from my Vault client. +UInt32 pyVaultNode::GetClientID() +{ + hsAssert(false, "eric, port me"); + return 0; +} + + +bool pyVaultNode::HasNode( UInt32 nodeID ) +{ + if ( fNode ) + return fNode->IsParentOf( nodeID, 1 ); + return false; +} + +PyObject * pyVaultNode::GetNode2( UInt32 nodeID ) const +{ + PyObject * result = nil; + if ( fNode ) + { + RelVaultNode * templateNode = NEWZERO(RelVaultNode); + templateNode->IncRef(); + templateNode->SetNodeId(nodeID); + if (RelVaultNode * rvn = fNode->GetChildNodeIncRef(templateNode, 1)) { + result = pyVaultNodeRef::New(fNode, rvn); + rvn->DecRef(); + } + templateNode->DecRef(); + } + + if (result) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject* pyVaultNode::FindNode( pyVaultNode * templateNode ) +{ + PyObject * result = nil; + if ( fNode && templateNode->fNode ) + { + if (RelVaultNode * rvn = fNode->GetChildNodeIncRef(templateNode->fNode, 1)) { + result = pyVaultNode::New(rvn); + rvn->DecRef(); + } + } + + if (result) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject * pyVaultNode::GetChildNode (unsigned nodeId) { + + if (!fNode) + PYTHON_RETURN_NONE; + + RelVaultNode * templateNode = NEWZERO(RelVaultNode); + templateNode->IncRef(); + templateNode->SetNodeId(nodeId); + RelVaultNode * rvn = fNode->GetChildNodeIncRef(templateNode, 1); + templateNode->DecRef(); + + if (rvn) { + PyObject * result = pyVaultNode::New(rvn); + rvn->DecRef(); + return result; + } + + PYTHON_RETURN_NONE; +} + + +// all the upcasting... + +PyObject* pyVaultNode::UpcastToFolderNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if ( + fNode->nodeType != plVault::kNodeType_Folder && + fNode->nodeType != plVault::kNodeType_AgeInfoList && + fNode->nodeType != plVault::kNodeType_PlayerInfoList + ) + PYTHON_RETURN_NONE; + + return pyVaultFolderNode::New(fNode); +} + +PyObject* pyVaultNode::UpcastToPlayerInfoListNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_PlayerInfoList) + PYTHON_RETURN_NONE; + + return pyVaultPlayerInfoListNode::New(fNode); +} + +PyObject* pyVaultNode::UpcastToImageNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_Image) + PYTHON_RETURN_NONE; + + return pyVaultImageNode::New(fNode); +} + +PyObject* pyVaultNode::UpcastToTextNoteNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_TextNote) + PYTHON_RETURN_NONE; + + return pyVaultTextNoteNode::New(fNode); +} + +PyObject* pyVaultNode::UpcastToAgeLinkNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_AgeLink) + PYTHON_RETURN_NONE; + + return pyVaultAgeLinkNode::New(fNode); +} + +PyObject* pyVaultNode::UpcastToChronicleNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_Chronicle) + PYTHON_RETURN_NONE; + + return pyVaultChronicleNode::New(fNode); +} + +PyObject* pyVaultNode::UpcastToPlayerInfoNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_PlayerInfo) + PYTHON_RETURN_NONE; + + return pyVaultPlayerInfoNode::New(fNode); +} + +PyObject* pyVaultNode::UpcastToMarkerGameNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_MarkerGame) + PYTHON_RETURN_NONE; + + return pyVaultMarkerGameNode::New(fNode); +} + +PyObject* pyVaultNode::UpcastToAgeInfoNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_AgeInfo) + PYTHON_RETURN_NONE; + + return pyVaultAgeInfoNode::New(fNode); +} + +PyObject* pyVaultNode::UpcastToAgeInfoListNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_AgeInfoList) + PYTHON_RETURN_NONE; + + return pyVaultAgeInfoListNode::New(fNode); +} + +PyObject* pyVaultNode::UpcastToSDLNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_SDL) + PYTHON_RETURN_NONE; + + return pyVaultSDLNode::New(fNode); +} + +PyObject* pyVaultNode::UpcastToPlayerNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_VNodeMgrPlayer) + PYTHON_RETURN_NONE; + + return pyVaultPlayerNode::New(fNode); +} + +#ifndef BUILDING_PYPLASMA +PyObject* pyVaultNode::UpcastToSystemNode() +{ + if (!fNode) + PYTHON_RETURN_NONE; + if (fNode->nodeType != plVault::kNodeType_System) + PYTHON_RETURN_NONE; + + return pyVaultSystemNode::New(fNode); +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNode.h new file mode 100644 index 00000000..ba0a6efa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNode.h @@ -0,0 +1,194 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultNode_h_ +#define _pyVaultNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +struct RelVaultNode; +class plMipmap; +class pyImage; + +class pyDniCoordinates; + +class pyVaultNodeRef; +class pyVaultFolderNode; +class pyVaultPlayerInfoListNode; +class pyVaultImageNode; +class pyVaultTextNoteNode; +class pyVaultAgeLinkNode; +class pyVaultChronicleNode; +class pyVaultPlayerInfoNode; +class pyVaultMarkerNode; +class pyVaultAgeInfoNode; +class pyVaultAgeInfoListNode; +class pyVaultSDLNode; +class pyVaultPlayerNode; +class pyVaultMarkerListNode; +#ifndef BUILDING_PYPLASMA +class pyVaultSystemNode; +#endif + +class pyVaultNode +{ +public: + struct pyVaultNodeOperationCallback + { + PyObject * fCbObject; + RelVaultNode * fNode; + PyObject * fPyNodeRef; + + pyVaultNodeOperationCallback(PyObject * cbObject); + ~pyVaultNodeOperationCallback(); + + void VaultOperationStarted(UInt32 context); + void VaultOperationComplete(UInt32 context, int resultCode); + + void SetNode (RelVaultNode * rvn); + RelVaultNode * GetNode (); + }; + + RelVaultNode * fNode; + mutable char * fCreateAgeGuid; + mutable char * fCreateAgeName; + +protected: + // only for python glue, do NOT call + pyVaultNode(); + // should only be created from C++ side + pyVaultNode( RelVaultNode* node ); + +public: + virtual ~pyVaultNode(); + + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptVaultNode); + static PyObject *New(RelVaultNode* node); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultNode); // converts a PyObject to a pyVaultNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + RelVaultNode * GetNode() const; + + // override the equals to operator + bool operator==(const pyVaultNode &vaultNode) const; + bool operator!=(const pyVaultNode &vaultNode) const { return !(vaultNode == *this); } + + // public getters + UInt32 GetID( void ); + virtual UInt32 GetType( void ); + UInt32 GetPermissions( void ); + UInt32 GetOwnerNodeID( void ); + PyObject* GetOwnerNode( void ); // returns pyVaultPlayerInfoNode + UInt32 GetGroupNodeID( void ); + PyObject* GetGroupNode( void ); // returns pyVaultNode + UInt32 GetModifyTime( void ); + UInt32 GetCreatorNodeID( void ); + PyObject* GetCreatorNode( void ); // returns pyVaultPlayerInfoNode + UInt32 GetCreateTime( void ); + UInt32 GetCreateAgeTime( void ); + const char * GetCreateAgeName( void ); + const char * GetCreateAgeGuid( void ); + PyObject* GetCreateAgeCoords (); + + // public setters + void SetID( UInt32 v ); + void SetType( int v ); + void SetOwnerNodeID( UInt32 v ); + void SetCreatorNodeID( UInt32 v ); + void SetCreateAgeName( const char * v ); + void SetCreateAgeGuid( const char * v ); + + + ///////////////////////////////////////////////// + // Vault Node API + + // Add child node + PyObject* AddNode(pyVaultNode* pynode, PyObject* cbObject=nil, UInt32 cbContext=0 ); + // Link node to this one + void LinkToNode(int nodeID, PyObject* cbObject=nil, UInt32 cbContext=0 ); + // Remove child node + hsBool RemoveNode( pyVaultNode& pynode, PyObject* cbObject=nil, UInt32 cbContext=0 ); + // Remove all child nodes + void RemoveAllNodes( void ); + // Add/Save this node to vault + void Save( PyObject* cbObject=nil, UInt32 cbContext=0 ); + // Save this node and all child nodes that need saving. + // NOTE: Currently, the cb object is called back for + // each node saved. + void SaveAll( PyObject* cbObject=nil, UInt32 cbContext=0 ); + // Force a save on this node because currently Save doesn't do anything because dirty + // nodes are periodically saved automatically - call this to force a save immediately + void ForceSave(); + // Send this node to the destination client node. will be received in it's inbox folder. + void SendTo(UInt32 destClientNodeID, PyObject* cbObject=nil, UInt32 cbContext=0 ); + // Returns true if is a child node of ours. + bool HasNode( UInt32 nodeID ); + // Returns a ptVaultNodeRef or nil + PyObject* GetNode2( UInt32 nodeID ) const; // returns pyVaultNodeRef, for legacy compatibility + // Get child node matching template node + PyObject* FindNode( pyVaultNode * templateNode ); // returns pyVaultNode + + PyObject * GetChildNode (unsigned nodeId); // returns pyVaultNode, or None + + // Get all child nodes. + virtual PyObject* GetChildNodeRefList(); // for legacy compatibility + virtual int GetChildNodeCount(); + + // Get the client ID from my Vault client. + UInt32 GetClientID( void ); + + // all the upcasting stuff... + PyObject* UpcastToFolderNode(); // returns pyVaultFolderNode + PyObject* UpcastToPlayerInfoListNode(); // returns pyVaultPlayerInfoListNode + PyObject* UpcastToImageNode(); // returns pyVaultImageNode + PyObject* UpcastToTextNoteNode(); // returns pyVaultTextNoteNode + PyObject* UpcastToAgeLinkNode(); // returns pyVaultAgeLinkNode + PyObject* UpcastToChronicleNode(); // returns pyVaultChronicleNode + PyObject* UpcastToPlayerInfoNode(); // returns pyVaultPlayerInfoNode + PyObject* UpcastToMarkerGameNode(); // returns pyVaultMarkerNode + PyObject* UpcastToAgeInfoNode(); // returns pyVaultAgeInfoNode + PyObject* UpcastToAgeInfoListNode(); // returns pyVaultAgeInfoListNode + PyObject* UpcastToSDLNode(); // returns pyVaultSDLNode + PyObject* UpcastToPlayerNode(); // returns pyVaultPlayerNode +#ifndef BUILDING_PYPLASMA + PyObject* UpcastToSystemNode(); // returns pyVaultSystemNode +#endif + +}; + +#endif // _pyVaultNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeGlue.cpp new file mode 100644 index 00000000..f4233bdb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeGlue.cpp @@ -0,0 +1,545 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultNode.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultNode, pyVaultNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultNode, pyVaultNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultNode) + +PYTHON_NO_INIT_DEFINITION(ptVaultNode) + +PYTHON_RICH_COMPARE_DEFINITION(ptVaultNode, obj1, obj2, compareType) +{ + if ((obj1 == Py_None) || (obj2 == Py_None)) + { + bool isEqual = (obj1 == obj2); + + if (compareType == Py_EQ) + { + if (isEqual) + PYTHON_RCOMPARE_TRUE; + else + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if (isEqual) + PYTHON_RCOMPARE_FALSE; + else + PYTHON_RCOMPARE_TRUE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptVaultNode object"); + PYTHON_RCOMPARE_ERROR; + } + + if (!pyVaultNode::Check(obj1) || !pyVaultNode::Check(obj2)) + { + PyErr_SetString(PyExc_NotImplementedError, "cannot compare ptVaultNode objects to non-ptVaultNode objects"); + PYTHON_RCOMPARE_ERROR; + } + pyVaultNode *vnObj1 = pyVaultNode::ConvertFrom(obj1); + pyVaultNode *vnObj2 = pyVaultNode::ConvertFrom(obj2); + if (compareType == Py_EQ) + { + if ((*vnObj1) == (*vnObj2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + else if (compareType == Py_NE) + { + if ((*vnObj1) != (*vnObj2)) + PYTHON_RCOMPARE_TRUE; + PYTHON_RCOMPARE_FALSE; + } + PyErr_SetString(PyExc_NotImplementedError, "invalid comparison for a ptVaultNode object"); + PYTHON_RCOMPARE_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getType) +{ + return PyLong_FromUnsignedLong(self->fThis->GetType()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getOwnerNodeID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetOwnerNodeID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getOwnerNode) +{ + return self->fThis->GetOwnerNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getModifyTime) +{ + return PyLong_FromUnsignedLong(self->fThis->GetModifyTime()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getCreatorNodeID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetCreatorNodeID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getCreatorNode) +{ + return self->fThis->GetCreatorNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getCreateTime) +{ + return PyLong_FromUnsignedLong(self->fThis->GetCreateTime()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getCreateAgeTime) +{ + return PyLong_FromUnsignedLong(self->fThis->GetCreateAgeTime()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getCreateAgeName) +{ + return PyString_FromString(self->fThis->GetCreateAgeName()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getCreateAgeGuid) +{ + return PyString_FromString(self->fThis->GetCreateAgeGuid()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getCreateAgeCoords) +{ + return self->fThis->GetCreateAgeCoords(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getChildNodeRefList) +{ + return self->fThis->GetChildNodeRefList(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getChildNodeCount) +{ + return PyInt_FromLong(self->fThis->GetChildNodeCount()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, getClientID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetClientID()); +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, setID, args) +{ + unsigned long id; + if (!PyArg_ParseTuple(args, "l", &id)) + { + PyErr_SetString(PyExc_TypeError, "setID expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetID(id); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, setType, args) +{ + int nodeType; + if (!PyArg_ParseTuple(args, "i", &nodeType)) + { + PyErr_SetString(PyExc_TypeError, "setType expects an int"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetType(nodeType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, setOwnerNodeID, args) +{ + unsigned long id; + if (!PyArg_ParseTuple(args, "l", &id)) + { + PyErr_SetString(PyExc_TypeError, "setOwnerNodeID expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetOwnerNodeID(id); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, setCreatorNodeID, args) +{ + unsigned long id; + if (!PyArg_ParseTuple(args, "l", &id)) + { + PyErr_SetString(PyExc_TypeError, "setCreatorNodeID expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetCreatorNodeID(id); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, setCreateAgeName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "setCreateAgeName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetCreateAgeName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, setCreateAgeGuid, args) +{ + char* guid; + if (!PyArg_ParseTuple(args, "s", &guid)) + { + PyErr_SetString(PyExc_TypeError, "setCreateAgeGuid expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetCreateAgeGuid(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_METHOD_DEFINITION(ptVaultNode, removeAllNodes, RemoveAllNodes) + +PYTHON_METHOD_DEFINITION(ptVaultNode, hasNode, args) +{ + unsigned long id; + if (!PyArg_ParseTuple(args, "l", &id)) + { + PyErr_SetString(PyExc_TypeError, "hasNode expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->HasNode(id)); +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, getNode, args) +{ + unsigned long id; + if (!PyArg_ParseTuple(args, "l", &id)) + { + PyErr_SetString(PyExc_TypeError, "getNode expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetNode2(id); +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, getChildNode, args) +{ + unsigned long id; + if (!PyArg_ParseTuple(args, "l", &id)) + { + PyErr_SetString(PyExc_TypeError, "getNode expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetChildNode(id); +} + + +PYTHON_METHOD_DEFINITION(ptVaultNode, findNode, args) +{ + PyObject* nodeObj = NULL; + if (!PyArg_ParseTuple(args, "O", &nodeObj)) + { + PyErr_SetString(PyExc_TypeError, "findNode expects a ptVaultNode"); + PYTHON_RETURN_ERROR; + } + if (!pyVaultNode::Check(nodeObj)) + { + PyErr_SetString(PyExc_TypeError, "findNode expects a ptVaultNode"); + PYTHON_RETURN_ERROR; + } + pyVaultNode* node = pyVaultNode::ConvertFrom(nodeObj); + return self->fThis->FindNode(node); +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, addNode, args) +{ + PyObject* nodeObj = NULL; + PyObject* cb = NULL; + unsigned long cbContext = 0; + if (!PyArg_ParseTuple(args, "O|Ol", &nodeObj, &cb, &cbContext)) + { + PyErr_SetString(PyExc_TypeError, "addNode expects a ptVaultNode, and an optional object and unsigned long"); + PYTHON_RETURN_ERROR; + } + if (!pyVaultNode::Check(nodeObj)) + { + PyErr_SetString(PyExc_TypeError, "addNode expects a ptVaultNode, and an optional object and unsigned long"); + PYTHON_RETURN_ERROR; + } + pyVaultNode* node = pyVaultNode::ConvertFrom(nodeObj); + return self->fThis->AddNode(node, cb, cbContext); +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, linkToNode, args) +{ + int nodeID; + PyObject* cb = NULL; + unsigned long cbContext = 0; + if (!PyArg_ParseTuple(args, "i|Ol", &nodeID, &cb, &cbContext)) + { + PyErr_SetString(PyExc_TypeError, "linkToNode expects an int, and an optional object and unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->LinkToNode(nodeID, cb, cbContext); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, removeNode, args) +{ + PyObject* nodeObj = NULL; + PyObject* cb = NULL; + unsigned long cbContext = 0; + if (!PyArg_ParseTuple(args, "O|Ol", &nodeObj, &cb, &cbContext)) + { + PyErr_SetString(PyExc_TypeError, "removeNode expects a ptVaultNode, and an optional object and unsigned long"); + PYTHON_RETURN_ERROR; + } + if (!pyVaultNode::Check(nodeObj)) + { + PyErr_SetString(PyExc_TypeError, "removeNode expects a ptVaultNode, and an optional object and unsigned long"); + PYTHON_RETURN_ERROR; + } + pyVaultNode* node = pyVaultNode::ConvertFrom(nodeObj); + PYTHON_RETURN_BOOL(self->fThis->RemoveNode(*node, cb, cbContext)); +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, save, args) +{ + PyObject* cb = NULL; + unsigned long cbContext = 0; + if (!PyArg_ParseTuple(args, "|Ol", &cb, &cbContext)) + { + PyErr_SetString(PyExc_TypeError, "save expects an optional object and unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->Save(cb, cbContext); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, saveAll, args) +{ + PyObject* cb = NULL; + unsigned long cbContext = 0; + if (!PyArg_ParseTuple(args, "|Ol", &cb, &cbContext)) + { + PyErr_SetString(PyExc_TypeError, "saveAll expects an optional object and unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SaveAll(cb, cbContext); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, forceSave) +{ + self->fThis->ForceSave(); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultNode, sendTo, args) +{ + unsigned long destNodeID; + PyObject* cb = NULL; + unsigned long cbContext = 0; + if (!PyArg_ParseTuple(args, "l|Ol", &destNodeID, &cb, &cbContext)) + { + PyErr_SetString(PyExc_TypeError, "sendTo expects an unsigned long, and an optional object and unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SendTo(destNodeID, cb, cbContext); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToFolderNode) +{ + return self->fThis->UpcastToFolderNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToPlayerInfoListNode) +{ + return self->fThis->UpcastToPlayerInfoListNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToImageNode) +{ + return self->fThis->UpcastToImageNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToTextNoteNode) +{ + return self->fThis->UpcastToTextNoteNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToAgeLinkNode) +{ + return self->fThis->UpcastToAgeLinkNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToChronicleNode) +{ + return self->fThis->UpcastToChronicleNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToPlayerInfoNode) +{ + return self->fThis->UpcastToPlayerInfoNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToMarkerGameNode) +{ + return self->fThis->UpcastToMarkerGameNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToAgeInfoNode) +{ + return self->fThis->UpcastToAgeInfoNode(); +} + + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToAgeInfoListNode) +{ + return self->fThis->UpcastToAgeInfoListNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToSDLNode) +{ + return self->fThis->UpcastToSDLNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToPlayerNode) +{ + return self->fThis->UpcastToPlayerNode(); +} + +#ifndef BUILDING_PYPLASMA +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNode, upcastToSystemNode) +{ + return self->fThis->UpcastToSystemNode(); +} +#endif + +PYTHON_START_METHODS_TABLE(ptVaultNode) + PYTHON_METHOD_NOARGS(ptVaultNode, getID, "Returns the unique ID of this ptVaultNode."), + PYTHON_METHOD_NOARGS(ptVaultNode, getType, "Returns the type of ptVaultNode this is.\n" + "See PlasmaVaultTypes.py"), + PYTHON_METHOD_NOARGS(ptVaultNode, getOwnerNodeID, "Returns the node ID of the owner of this node"), + PYTHON_METHOD_NOARGS(ptVaultNode, getOwnerNode, "Returns a ptVaultNode of the owner of this node"), + PYTHON_METHOD_NOARGS(ptVaultNode, getModifyTime, "Returns the modified time of this node, that is useable by python's time library."), + PYTHON_METHOD_NOARGS(ptVaultNode, getCreatorNodeID, "Returns the creator's node ID"), + PYTHON_METHOD_NOARGS(ptVaultNode, getCreatorNode, "Returns the creator's node"), + PYTHON_METHOD_NOARGS(ptVaultNode, getCreateTime, "Returns the when this node was created, that is useable by python's time library."), + PYTHON_METHOD_NOARGS(ptVaultNode, getCreateAgeTime, "Returns the time in the Age that the node was created...(?)"), + PYTHON_METHOD_NOARGS(ptVaultNode, getCreateAgeName, "Returns the name of the Age where this node was created."), + PYTHON_METHOD_NOARGS(ptVaultNode, getCreateAgeGuid, "Returns the guid as a string of the Age where this node was created."), + PYTHON_METHOD_NOARGS(ptVaultNode, getCreateAgeCoords, "Returns the location in the Age where this node was created."), + + PYTHON_METHOD_NOARGS(ptVaultNode, getChildNodeRefList, "Returns a list of ptVaultNodeRef that are the children of this node."), + PYTHON_METHOD_NOARGS(ptVaultNode, getChildNodeCount, "Returns how many children this node has."), + + PYTHON_METHOD_NOARGS(ptVaultNode, getClientID, "Returns the client's ID."), + + PYTHON_METHOD(ptVaultNode, setID, "Params: id\nSets ID of this ptVaultNode."), + PYTHON_METHOD(ptVaultNode, setType, "Params: type\nSet the type of ptVaultNode this is."), + PYTHON_METHOD(ptVaultNode, setOwnerNodeID, "Params: id\nSet node ID of the owner of this node"), + PYTHON_METHOD(ptVaultNode, setCreatorNodeID, "Params: id\nSet creator's node ID"), + PYTHON_METHOD(ptVaultNode, setCreateAgeName, "Params: name\nSet name of the Age where this node was created."), + PYTHON_METHOD(ptVaultNode, setCreateAgeGuid, "Params: guid\nSet guid as a string of the Age where this node was created."), + + PYTHON_BASIC_METHOD(ptVaultNode, removeAllNodes, "Removes all the child nodes on this node."), + PYTHON_METHOD(ptVaultNode, hasNode, "Params: id\nReturns true if node if a child node"), + PYTHON_METHOD(ptVaultNode, getNode, "Params: id\nReturns ptVaultNodeRef if is a child node, or None"), + PYTHON_METHOD(ptVaultNode, findNode, "Params: templateNode\nReturns ptVaultNode if child node found matching template, or None"), + + PYTHON_METHOD(ptVaultNode, addNode, "Params: node,cb=None,cbContext=0\nAdds 'node'(ptVaultNode) as a child to this node."), + PYTHON_METHOD(ptVaultNode, linkToNode, "Params: nodeID,cb=None,cbContext=0\nAdds a link to the node designated by nodeID"), + PYTHON_METHOD(ptVaultNode, removeNode, "Params: node,cb=None,cbContext=0\nRemoves the child 'node'(ptVaultNode) from this node."), + PYTHON_METHOD(ptVaultNode, save, "Params: cb=None,cbContext=0\nSave the changes made to this node."), + PYTHON_METHOD(ptVaultNode, saveAll, "Params: cb=None,cbContext=0\nSaves this node and all its children nodes."), + PYTHON_METHOD_NOARGS(ptVaultNode, forceSave, "Force the current node to save immediately"), + PYTHON_METHOD(ptVaultNode, sendTo, "Params: destID,cb=None,cbContext=0\nSend this node to inbox at 'destID'"), + + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToFolderNode, "Returns this ptVaultNode as ptVaultFolderNode"), + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToPlayerInfoListNode, "Returns this ptVaultNode as ptVaultPlayerInfoListNode"), + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToImageNode, "Returns this ptVaultNode as ptVaultImageNode"), + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToTextNoteNode, "Returns this ptVaultNode as ptVaultTextNoteNode"), + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToAgeLinkNode, "Returns this ptVaultNode as ptVaultAgeLinkNode"), + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToChronicleNode, "Returns this ptVaultNode as ptVaultChronicleNode"), + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToPlayerInfoNode, "Returns this ptVaultNode as ptVaultPlayerInfoNode"), + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToMarkerGameNode, "Returns this ptVaultNode as ptVaultMarkerNode"), + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToAgeInfoNode, "Returns this ptVaultNode as ptVaultAgeInfoNode"), + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToAgeInfoListNode, "Returns this ptVaultNode as ptVaultAgeInfoListNode"), + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToSDLNode, "Returns this ptVaultNode as a ptVaultSDLNode"), + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToPlayerNode, "Returns this ptVaultNode as a ptVaultPlayerNode"), +#ifndef BUILDING_PYPLASMA + PYTHON_METHOD_NOARGS(ptVaultNode, upcastToSystemNode, "Returns this ptVaultNode as a ptVaultSystemNode"), +#endif +PYTHON_END_METHODS_TABLE; + +// Type structure definition +#define ptVaultNode_COMPARE PYTHON_NO_COMPARE +#define ptVaultNode_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptVaultNode_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptVaultNode_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptVaultNode_STR PYTHON_NO_STR +#define ptVaultNode_RICH_COMPARE PYTHON_DEFAULT_RICH_COMPARE(ptVaultNode) +#define ptVaultNode_GETSET PYTHON_NO_GETSET +#define ptVaultNode_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptVaultNode, "Vault node class"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptVaultNode, pyVaultNode); + +// required functions for PyObject interoperability +PyObject *pyVaultNode::New(RelVaultNode* nfsNode) +{ + ptVaultNode *newObj = (ptVaultNode*)ptVaultNode_type.tp_new(&ptVaultNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultNode, pyVaultNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultNode, pyVaultNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRef.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRef.cpp new file mode 100644 index 00000000..eccf643d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRef.cpp @@ -0,0 +1,150 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultNodeRef - a wrapper class to provide interface to the plVaultNodeRef +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultNodeRef.h" +#ifndef BUILDING_PYPLASMA +#include "pyVault.h" +#endif +#include "pyVaultNode.h" +#include "pyVaultPlayerInfoNode.h" + +#include "../plVault/plVault.h" + + +////////////////////////////////////////////////////////////////////// + + +// should only be created from C++ side +pyVaultNodeRef::pyVaultNodeRef(RelVaultNode * parent, RelVaultNode * child) +: fParent(parent) +, fChild(child) +{ + fParent->IncRef(); + fChild->IncRef(); +} + +pyVaultNodeRef::pyVaultNodeRef(int) +: fParent(nil) +, fChild(nil) +{ +} + +pyVaultNodeRef::~pyVaultNodeRef() +{ + if (fParent) + fParent->DecRef(); + if (fChild) + fChild->DecRef(); +} + + +/////////////////////////////////////////////////////////////////////////// + +PyObject* pyVaultNodeRef::GetParent ( void ) +{ + return pyVaultNode::New(fParent); +} + +PyObject* pyVaultNodeRef::GetChild( void ) +{ + return pyVaultNode::New(fChild); +} + +unsigned pyVaultNodeRef::GetParentID () { + if (!fParent) + return 0; + return fParent->nodeId; +} + +unsigned pyVaultNodeRef::GetChildID () { + if (!fChild) + return 0; + return fChild->nodeId; +} + +unsigned pyVaultNodeRef::GetSaverID () { + if (!fParent || !fChild) + return 0; + + unsigned saverId = 0; + if (RelVaultNode * child = VaultGetNodeIncRef(fChild->nodeId)) { + saverId = child->GetRefOwnerId(fParent->nodeId); + child->DecRef(); + } + return saverId; +} + +PyObject * pyVaultNodeRef::GetSaver () { + if (!fParent || !fChild) + return 0; + + RelVaultNode * saver = nil; + if (RelVaultNode * child = VaultGetNodeIncRef(fChild->nodeId)) { + if (unsigned saverId = child->GetRefOwnerId(fParent->nodeId)) { + // Find the player info node representing the saver + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_PlayerInfo); + VaultPlayerInfoNode access(templateNode); + access.SetPlayerId(saverId); + saver = VaultGetNodeIncRef(templateNode); + + if (!saver) { + ARRAY(unsigned) nodeIds; + VaultFindNodesAndWait(templateNode, &nodeIds); + if (nodeIds.Count() > 0) { + VaultFetchNodesAndWait(nodeIds.Ptr(), nodeIds.Count()); + saver = VaultGetNodeIncRef(nodeIds[0]); + } + } + + templateNode->DecRef(); + } + child->DecRef(); + } + if (!saver) + PYTHON_RETURN_NONE; + + PyObject * result = pyVaultPlayerInfoNode::New(saver); + saver->DecRef(); + return result; +} + +bool pyVaultNodeRef::BeenSeen () { + return false; +} + +void pyVaultNodeRef::SetSeen (bool v) { + if (!fParent || !fChild) + return; + + fParent->SetSeen(fChild->nodeId, v); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRef.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRef.h new file mode 100644 index 00000000..48f32ce4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRef.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultNodeRef_h_ +#define _pyVaultNodeRef_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultNodeRef - a wrapper class to provide interface to the plVaultNodeRef +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" + +#include +#include "pyGlueHelpers.h" + +struct RelVaultNode; + +class pyVaultNodeRef +{ + RelVaultNode * fParent; + RelVaultNode * fChild; + +protected: + // should only be created from C++ side + pyVaultNodeRef(RelVaultNode * parent, RelVaultNode * child); + pyVaultNodeRef(int =0 ); + +public: + ~pyVaultNodeRef(); + + RelVaultNode * GetParentNode () const { return fParent; } + RelVaultNode * GetChildNode () const { return fChild; } + + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; // so we can subclass + PYTHON_CLASS_NEW_FRIEND(ptVaultNodeRef); + static PyObject *New(RelVaultNode * parent, RelVaultNode * child); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultNodeRef object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultNodeRef); // converts a PyObject to a pyVaultNodeRef (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + PyObject * GetParent (); + PyObject * GetChild (); + PyObject * GetSaver (); // returns pyVaultPlayerInfoNode + bool BeenSeen (); + void SetSeen (bool v); + + unsigned GetParentID (); + unsigned GetChildID (); + unsigned GetSaverID (); +}; + +#endif // _pyVaultNodeRef_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRefGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRefGlue.cpp new file mode 100644 index 00000000..51030862 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultNodeRefGlue.cpp @@ -0,0 +1,134 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultNodeRef.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultNodeRef, pyVaultNodeRef); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultNodeRef, pyVaultNodeRef) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultNodeRef) + +PYTHON_NO_INIT_DEFINITION(ptVaultNodeRef) + + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNodeRef, getParent) +{ + return self->fThis->GetParent(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNodeRef, getChild) +{ + return self->fThis->GetChild(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNodeRef, getParentID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetParentID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNodeRef, getChildID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetChildID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNodeRef, getSaver) +{ + return self->fThis->GetSaver(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNodeRef, getSaverID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetSaverID()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNodeRef, beenSeen) +{ + return PyLong_FromUnsignedLong(self->fThis->BeenSeen()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultNodeRef, setSeen) +{ + self->fThis->SetSeen(true); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptVaultNodeRef) + PYTHON_METHOD_NOARGS(ptVaultNodeRef, getParent, "Returns a ptVaultNode that is the parent of the reference"), + PYTHON_METHOD_NOARGS(ptVaultNodeRef, getChild, "Returns a ptVaultNode that is the child of this reference"), + PYTHON_METHOD_NOARGS(ptVaultNodeRef, getParentID, "Returns id of the parent node"), + PYTHON_METHOD_NOARGS(ptVaultNodeRef, getChildID, "Returns id of the child node"), + PYTHON_METHOD_NOARGS(ptVaultNodeRef, getSaver, "Returns a ptVaultPlayerInfoNode of player that created this relationship"), + PYTHON_METHOD_NOARGS(ptVaultNodeRef, getSaverID, "Returns id of player that created this relationship"), + PYTHON_METHOD_NOARGS(ptVaultNodeRef, beenSeen, "Returns true until we reimplement this"), + PYTHON_METHOD_NOARGS(ptVaultNodeRef, setSeen, "Does nothing until we reimplement this"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +#define ptVaultNodeRef_COMPARE PYTHON_NO_COMPARE +#define ptVaultNodeRef_AS_NUMBER PYTHON_NO_AS_NUMBER +#define ptVaultNodeRef_AS_SEQUENCE PYTHON_NO_AS_SEQUENCE +#define ptVaultNodeRef_AS_MAPPING PYTHON_NO_AS_MAPPING +#define ptVaultNodeRef_RICH_COMPARE PYTHON_NO_RICH_COMPARE +#define ptVaultNodeRef_STR PYTHON_NO_STR +#define ptVaultNodeRef_GETSET PYTHON_NO_GETSET +#define ptVaultNodeRef_BASE PYTHON_NO_BASE +PLASMA_CUSTOM_TYPE(ptVaultNodeRef, "Vault node relationship pseudo class"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptVaultNodeRef, pyVaultNodeRef); + +// required functions for PyObject interoperability +PyObject *pyVaultNodeRef::New(RelVaultNode * parent, RelVaultNode * child) +{ + ptVaultNodeRef *newObj = (ptVaultNodeRef*)ptVaultNodeRef_type.tp_new(&ptVaultNodeRef_type, NULL, NULL); + if (newObj->fThis->fParent) + newObj->fThis->fParent->DecRef(); + if (newObj->fThis->fChild) + newObj->fThis->fChild->DecRef(); + newObj->fThis->fParent = parent; + newObj->fThis->fChild = child; + if (newObj->fThis->fParent) + newObj->fThis->fParent->IncRef(); + if (newObj->fThis->fChild) + newObj->fThis->fChild->IncRef(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultNodeRef, pyVaultNodeRef) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultNodeRef, pyVaultNodeRef) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultNodeRef::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultNodeRef); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNode.cpp new file mode 100644 index 00000000..19578e95 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNode.cpp @@ -0,0 +1,155 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultPlayerInfoListNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultPlayerInfoListNode.h" +#ifndef BUILDING_PYPLASMA +#include "pyVault.h" +#endif +#include "pyVaultFolderNode.h" +#include "pyVaultPlayerInfoNode.h" +#include "pyVaultNodeRef.h" + +#include "../plVault/plVault.h" + +#include + +// should only be created from C++ side +pyVaultPlayerInfoListNode::pyVaultPlayerInfoListNode(RelVaultNode* nfsNode) +: pyVaultFolderNode(nfsNode) +{ +} + +//create from the Python side +pyVaultPlayerInfoListNode::pyVaultPlayerInfoListNode(int n) +: pyVaultFolderNode(n) +{ + fNode->SetNodeType(plVault::kNodeType_PlayerInfoList); +} + +//================================================================== +// class RelVaultNode : public plVaultFolderNode +// +hsBool pyVaultPlayerInfoListNode::HasPlayer( UInt32 playerID ) +{ + if (!fNode) + return false; + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_PlayerInfo); + VaultPlayerInfoNode access(templateNode); + access.SetPlayerId(playerID); + + RelVaultNode * rvn = fNode->GetChildNodeIncRef(templateNode, 1); + if (rvn) + rvn->DecRef(); + + templateNode->DecRef(); + return (rvn != nil); +} + +hsBool pyVaultPlayerInfoListNode::AddPlayer( UInt32 playerID ) +{ + if (HasPlayer(playerID)) + return true; + + if (!fNode) + return false; + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_PlayerInfo); + VaultPlayerInfoNode access(templateNode); + access.SetPlayerId(playerID); + + ARRAY(unsigned) nodeIds; + VaultLocalFindNodes(templateNode, &nodeIds); + + if (!nodeIds.Count()) + VaultFindNodesAndWait(templateNode, &nodeIds); + + if (nodeIds.Count()) + VaultAddChildNodeAndWait(fNode->nodeId, nodeIds[0], VaultGetPlayerId()); + + templateNode->DecRef(); + return nodeIds.Count() != 0; +} + +void pyVaultPlayerInfoListNode::RemovePlayer( UInt32 playerID ) +{ + if (!fNode) + return; + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_PlayerInfo); + VaultPlayerInfoNode access(templateNode); + access.SetPlayerId(playerID); + + if (RelVaultNode * rvn = fNode->GetChildNodeIncRef(templateNode, 1)) { + VaultRemoveChildNode(fNode->nodeId, rvn->nodeId, nil, nil); + rvn->DecRef(); + } + + templateNode->DecRef(); +} + +PyObject * pyVaultPlayerInfoListNode::GetPlayer( UInt32 playerID ) +{ + if (!fNode) + PYTHON_RETURN_NONE; + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_PlayerInfo); + VaultPlayerInfoNode access(templateNode); + access.SetPlayerId(playerID); + + PyObject * result = nil; + if (RelVaultNode * rvn = fNode->GetChildNodeIncRef(templateNode, 1)) { + result = pyVaultPlayerInfoNode::New(rvn); + rvn->DecRef(); + } + + templateNode->DecRef(); + + if (!result) + PYTHON_RETURN_NONE; + + return result; +} + + +void pyVaultPlayerInfoListNode::Sort() +{ + hsAssert(false, "eric, port me"); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNode.h new file mode 100644 index 00000000..0eac977e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNode.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultPlayerInfoListNode_h_ +#define _pyVaultPlayerInfoListNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultPlayerInfoListNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyVaultFolderNode.h" + +struct RelVaultNode; +class pyVaultPlayerInfoNode; + + +class pyVaultPlayerInfoListNode : public pyVaultFolderNode +{ +protected: + // should only be created from C++ side + pyVaultPlayerInfoListNode(RelVaultNode* nfsNode); + + //create from the Python side + pyVaultPlayerInfoListNode(int n=0); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultPlayerInfoListNode); + static PyObject *New(RelVaultNode* nfsNode); + static PyObject *New(int n=0); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultPlayerInfoListNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultPlayerInfoListNode); // converts a PyObject to a pyVaultPlayerInfoListNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + +//================================================================== +// class RelVaultNode : public plVaultFolderNode +// + virtual hsBool HasPlayer( UInt32 playerID ); + hsBool AddPlayer( UInt32 playerID ); + void RemovePlayer( UInt32 playerID ); + PyObject * GetPlayer( UInt32 playerID ); // returns pyVaultPlayerInfoNode + + void Sort(); + +}; + + +#endif // _pyVaultPlayerInfoListNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNodeGlue.cpp new file mode 100644 index 00000000..68a93892 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoListNodeGlue.cpp @@ -0,0 +1,189 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultPlayerInfoListNode.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultPlayerInfoListNode, pyVaultPlayerInfoListNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultPlayerInfoListNode, pyVaultPlayerInfoListNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultPlayerInfoListNode) + +PYTHON_INIT_DEFINITION(ptVaultPlayerInfoListNode, args, keywords) +{ + int n = 0; + if (!PyArg_ParseTuple(args, "|i", &n)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects an optional int"); + PYTHON_RETURN_INIT_ERROR; + } + // we don't really do anything? Not according to the associated constructor. Odd... + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoListNode, playerlistHasPlayer, args) +{ + unsigned long playerID; + if (!PyArg_ParseTuple(args, "l", &playerID)) + { + PyErr_SetString(PyExc_TypeError, "playerlistHasPlayer expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->HasPlayer(playerID)); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoListNode, playerlistAddPlayer, args) +{ + unsigned long playerID; + if (!PyArg_ParseTuple(args, "l", &playerID)) + { + PyErr_SetString(PyExc_TypeError, "playerlistAddPlayer expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->AddPlayer(playerID)); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoListNode, playerlistRemovePlayer, args) +{ + unsigned long playerID; + if (!PyArg_ParseTuple(args, "l", &playerID)) + { + PyErr_SetString(PyExc_TypeError, "playerlistRemovePlayer expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->RemovePlayer(playerID); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoListNode, playerlistGetPlayer, args) +{ + unsigned long playerID; + if (!PyArg_ParseTuple(args, "l", &playerID)) + { + PyErr_SetString(PyExc_TypeError, "playerlistGetPlayer expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetPlayer(playerID); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoListNode, hasPlayer, args) +{ + unsigned long playerID; + if (!PyArg_ParseTuple(args, "l", &playerID)) + { + PyErr_SetString(PyExc_TypeError, "hasPlayer expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->HasPlayer(playerID)); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoListNode, addPlayer, args) +{ + unsigned long playerID; + if (!PyArg_ParseTuple(args, "l", &playerID)) + { + PyErr_SetString(PyExc_TypeError, "addPlayer expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->AddPlayer(playerID)); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoListNode, removePlayer, args) +{ + unsigned long playerID; + if (!PyArg_ParseTuple(args, "l", &playerID)) + { + PyErr_SetString(PyExc_TypeError, "removePlayer expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->RemovePlayer(playerID); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoListNode, getPlayer, args) +{ + unsigned long playerID; + if (!PyArg_ParseTuple(args, "l", &playerID)) + { + PyErr_SetString(PyExc_TypeError, "getPlayer expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetPlayer(playerID); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptVaultPlayerInfoListNode, sort, Sort) + +PYTHON_START_METHODS_TABLE(ptVaultPlayerInfoListNode) + // legacy glue + PYTHON_METHOD(ptVaultPlayerInfoListNode, playerlistHasPlayer, "Params: playerID\nLEGACY: Returns whether the 'playerID' is a member of this player info list node."), + PYTHON_METHOD(ptVaultPlayerInfoListNode, playerlistAddPlayer, "Params: playerID\nLEGACY: Adds playerID player to this player info list node."), + PYTHON_METHOD(ptVaultPlayerInfoListNode, playerlistRemovePlayer, "Params: playerID\nLEGACY: Removes playerID player from this player info list node."), + PYTHON_METHOD(ptVaultPlayerInfoListNode, playerlistGetPlayer, "Params: playerID\nLEGACY: Gets the player info node for the specified player."), + // new glue + PYTHON_METHOD(ptVaultPlayerInfoListNode, hasPlayer, "Params: playerID\nReturns whether the 'playerID' is a member of this player info list node."), + PYTHON_METHOD(ptVaultPlayerInfoListNode, addPlayer, "Params: playerID\nAdds playerID player to this player info list node."), + PYTHON_METHOD(ptVaultPlayerInfoListNode, removePlayer, "Params: playerID\nRemoves playerID player from this player info list node."), + PYTHON_METHOD(ptVaultPlayerInfoListNode, getPlayer, "Params: playerID\nGets the player info node for the specified player."), + PYTHON_BASIC_METHOD(ptVaultPlayerInfoListNode, sort, "Sorts the player list by some means...?"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultPlayerInfoListNode, pyVaultFolderNode, "Params: n=0\nPlasma vault player info list node"); + +// required functions for PyObject interoperability +PyObject *pyVaultPlayerInfoListNode::New(RelVaultNode* nfsNode) +{ + ptVaultPlayerInfoListNode *newObj = (ptVaultPlayerInfoListNode*)ptVaultPlayerInfoListNode_type.tp_new(&ptVaultPlayerInfoListNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PyObject *pyVaultPlayerInfoListNode::New(int n /* =0 */) +{ + ptVaultPlayerInfoListNode *newObj = (ptVaultPlayerInfoListNode*)ptVaultPlayerInfoListNode_type.tp_new(&ptVaultPlayerInfoListNode_type, NULL, NULL); + // oddly enough, nothing to do here + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultPlayerInfoListNode, pyVaultPlayerInfoListNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultPlayerInfoListNode, pyVaultPlayerInfoListNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultPlayerInfoListNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultPlayerInfoListNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNode.cpp new file mode 100644 index 00000000..df238d10 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNode.cpp @@ -0,0 +1,180 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultPlayerInfoNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultPlayerInfoNode.h" +#ifndef BUILDING_PYPLASMA +#include "pyVault.h" +#endif + +#include "../plVault/plVault.h" + +// should only be created from C++ side +pyVaultPlayerInfoNode::pyVaultPlayerInfoNode(RelVaultNode* nfsNode) +: pyVaultNode(nfsNode) +, ansiPlayerName(nil) +, ansiAgeInstName(nil) +{ +} + +//create from the Python side +pyVaultPlayerInfoNode::pyVaultPlayerInfoNode() +: pyVaultNode(NEWZERO(RelVaultNode)) +, ansiPlayerName(nil) +, ansiAgeInstName(nil) +{ + fNode->SetNodeType(plVault::kNodeType_PlayerInfo); +} + +pyVaultPlayerInfoNode::~pyVaultPlayerInfoNode () { + FREE(ansiPlayerName); + FREE(ansiAgeInstName); +} + +//================================================================== +// class RelVaultNode : public plVaultNode +// +void pyVaultPlayerInfoNode::Player_SetPlayerID( UInt32 plyrid ) +{ + if (!fNode) + return; + + VaultPlayerInfoNode playerInfo(fNode); + playerInfo.SetPlayerId(plyrid); +} + +UInt32 pyVaultPlayerInfoNode::Player_GetPlayerID( void ) +{ + if (!fNode) + return 0; + + VaultPlayerInfoNode playerInfo(fNode); + return playerInfo.playerId; +} + +void pyVaultPlayerInfoNode::Player_SetPlayerName( const char * name ) +{ + if (!fNode) + return; + + wchar * wStr = StrDupToUnicode(name); + VaultPlayerInfoNode playerInfo(fNode); + playerInfo.SetPlayerName(wStr); + FREE(wStr); +} + +const char * pyVaultPlayerInfoNode::Player_GetPlayerName( void ) +{ + if (!fNode) + return ""; + + VaultPlayerInfoNode playerInfo(fNode); + if (!playerInfo.playerName) + return ""; + + FREE(ansiPlayerName); + ansiPlayerName = StrDupToAnsi(playerInfo.playerName); + return ansiPlayerName; +} + +// age the player is currently in, if any. +void pyVaultPlayerInfoNode::Player_SetAgeInstanceName( const char * agename ) +{ + if (!fNode) + return; + + wchar * wStr = StrDupToUnicode(agename); + VaultPlayerInfoNode playerInfo(fNode); + playerInfo.SetAgeInstName(wStr); + FREE(wStr); +} + +const char * pyVaultPlayerInfoNode::Player_GetAgeInstanceName( void ) +{ + if (!fNode) + return ""; + + VaultPlayerInfoNode playerInfo(fNode); + if (!playerInfo.ageInstName) + return ""; + + FREE(ansiAgeInstName); + ansiAgeInstName = StrDupToAnsi(playerInfo.ageInstName); + return ansiAgeInstName; +} + +void pyVaultPlayerInfoNode::Player_SetAgeGuid( const char * guidtext) +{ + if (!fNode) + return; + + Uuid ageInstId; + GuidFromString(guidtext, &ageInstId); + VaultPlayerInfoNode playerInfo(fNode); + playerInfo.SetAgeInstUuid(ageInstId); +} + +const char * pyVaultPlayerInfoNode::Player_GetAgeGuid( void ) +{ + if (!fNode) + return ""; + + VaultPlayerInfoNode playerInfo(fNode); + GuidToString(playerInfo.ageInstUuid, ansiAgeInstUuid, arrsize(ansiAgeInstUuid)); + return ansiAgeInstUuid; +} + +// online status +void pyVaultPlayerInfoNode::Player_SetOnline( bool b ) +{ + if (!fNode) + return; + + VaultPlayerInfoNode playerInfo(fNode); + playerInfo.SetOnline(b); +} + +hsBool pyVaultPlayerInfoNode::Player_IsOnline( void ) +{ + if (!fNode) + return false; + + VaultPlayerInfoNode playerInfo(fNode); + return playerInfo.online; +} + +int pyVaultPlayerInfoNode::Player_GetCCRLevel( void ) +{ + if (!fNode) + return 0; + + VaultPlayerInfoNode playerInfo(fNode); + return playerInfo.ccrLevel; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNode.h new file mode 100644 index 00000000..f1e97647 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNode.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultPlayerInfoNode_h_ +#define _pyVaultPlayerInfoNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultPlayerInfoNode - a wrapper class to provide interface to the plVaultPlayerInfoNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultNode.h" +#include +#include "pyGlueHelpers.h" + +class pyVaultPlayerInfoNode : public pyVaultNode +{ + mutable char * ansiPlayerName; + mutable char * ansiAgeInstName; + mutable char ansiAgeInstUuid[256]; + +protected: + // should only be created from C++ side + pyVaultPlayerInfoNode(RelVaultNode * node); + + //create from the Python side + pyVaultPlayerInfoNode(); + +public: + ~pyVaultPlayerInfoNode (); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultPlayerInfoNode); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(RelVaultNode * node); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultPlayerInfoNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultPlayerInfoNode); // converts a PyObject to a pyVaultPlayerInfoNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + +//================================================================== +// class plVaultPlayerInfoNode : public plVaultNode +// + void Player_SetPlayerID( UInt32 plyrid ); + UInt32 Player_GetPlayerID( void ); + void Player_SetPlayerName( const char * name ); + const char * Player_GetPlayerName( void ); + + // age the player is currently in, if any. + void Player_SetAgeInstanceName( const char * agename ); + const char * Player_GetAgeInstanceName( void ); + void Player_SetAgeGuid( const char * guidtext); + const char * Player_GetAgeGuid( void ); + // online status + void Player_SetOnline( bool b ); + hsBool Player_IsOnline( void ); + + int Player_GetCCRLevel( void ); +}; + +#endif // _pyVaultPlayerInfoNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNodeGlue.cpp new file mode 100644 index 00000000..4cbd157e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerInfoNodeGlue.cpp @@ -0,0 +1,175 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultPlayerInfoNode.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultPlayerInfoNode, pyVaultPlayerInfoNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultPlayerInfoNode, pyVaultPlayerInfoNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultPlayerInfoNode) + +PYTHON_INIT_DEFINITION(ptVaultPlayerInfoNode, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoNode, playerSetID, args) +{ + unsigned long playerID; + if (!PyArg_ParseTuple(args, "l", &playerID)) + { + PyErr_SetString(PyExc_TypeError, "playerSetID expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->Player_SetPlayerID(playerID); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerInfoNode, playerGetID) +{ + return PyLong_FromUnsignedLong(self->fThis->Player_GetPlayerID()); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoNode, playerSetName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "playerSetName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Player_SetPlayerName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerInfoNode, playerGetName) +{ + return PyString_FromString(self->fThis->Player_GetPlayerName()); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoNode, playerSetAgeInstanceName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "playerSetAgeInstanceName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Player_SetAgeInstanceName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerInfoNode, playerGetAgeInstanceName) +{ + return PyString_FromString(self->fThis->Player_GetAgeInstanceName()); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoNode, playerSetAgeGuid, args) +{ + char* guid; + if (!PyArg_ParseTuple(args, "s", &guid)) + { + PyErr_SetString(PyExc_TypeError, "playerSetAgeGuid expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Player_SetAgeGuid(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerInfoNode, playerGetAgeGuid) +{ + return PyString_FromString(self->fThis->Player_GetAgeGuid()); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerInfoNode, playerSetOnline, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "playerSetOnline expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->Player_SetOnline(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerInfoNode, playerIsOnline) +{ + PYTHON_RETURN_BOOL(self->fThis->Player_IsOnline()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerInfoNode, playerGetCCRLevel) +{ + return PyInt_FromLong(self->fThis->Player_GetCCRLevel()); +} + +PYTHON_START_METHODS_TABLE(ptVaultPlayerInfoNode) + PYTHON_METHOD(ptVaultPlayerInfoNode, playerSetID, "Params: playerID\nNot sure this should be used. Sets the playerID for this player info node."), + PYTHON_METHOD_NOARGS(ptVaultPlayerInfoNode, playerGetID, "Returns the player ID for this player info node."), + PYTHON_METHOD(ptVaultPlayerInfoNode, playerSetName, "Params: name\nNot sure this should be used. Sets the player name of this player info node."), + PYTHON_METHOD_NOARGS(ptVaultPlayerInfoNode, playerGetName, "Returns the player name of this player info node."), + PYTHON_METHOD(ptVaultPlayerInfoNode, playerSetAgeInstanceName, "Params: name\nNot sure this should be used. Sets the name of the age where the player is for this player info node."), + PYTHON_METHOD_NOARGS(ptVaultPlayerInfoNode, playerGetAgeInstanceName, "Returns the name of the Age where the player is for this player info node."), + PYTHON_METHOD(ptVaultPlayerInfoNode, playerSetAgeGuid, "Params: guidString\nNot sure this should be used. Sets the guid for this player info node."), + PYTHON_METHOD_NOARGS(ptVaultPlayerInfoNode, playerGetAgeGuid, "Returns the guid as a string of where the player is for this player info node."), + PYTHON_METHOD(ptVaultPlayerInfoNode, playerSetOnline, "Params: state\nNot sure this should be used. Sets the state of the player online status for this player info node."), + PYTHON_METHOD_NOARGS(ptVaultPlayerInfoNode, playerIsOnline, "Returns the online status of the player for this player info node."), + PYTHON_METHOD_NOARGS(ptVaultPlayerInfoNode, playerGetCCRLevel, "Returns the ccr level of the player for this player info node."), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultPlayerInfoNode, pyVaultNode, "Plasma vault folder node"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptVaultPlayerInfoNode, pyVaultPlayerInfoNode) + +PyObject *pyVaultPlayerInfoNode::New(RelVaultNode* nfsNode) +{ + ptVaultPlayerInfoNode *newObj = (ptVaultPlayerInfoNode*)ptVaultPlayerInfoNode_type.tp_new(&ptVaultPlayerInfoNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultPlayerInfoNode, pyVaultPlayerInfoNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultPlayerInfoNode, pyVaultPlayerInfoNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultPlayerInfoNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultPlayerInfoNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNode.cpp new file mode 100644 index 00000000..5e5ec56b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNode.cpp @@ -0,0 +1,316 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultPlayerNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultPlayerNode.h" +#ifndef BUILDING_PYPLASMA +#include "pyVault.h" +#endif + +#include "../pfPython/pyAgeInfoStruct.h" +#include "../pfPython/pyVaultAgeLinkNode.h" +#include "../pfPython/pyVaultPlayerInfoNode.h" +#include "../pfPython/pyVaultPlayerInfoListNode.h" +#include "../pfPython/pyVaultFolderNode.h" +#include "../pfPython/pyVaultChronicleNode.h" +#include "../pfPython/pyVaultSDLNode.h" +#include "../pfPython/pyAgeLinkStruct.h" + +#include "../plVault/plVault.h" + +//============================================================================ +static PyObject * GetPlayerVaultFolder (unsigned folderType) { + PyObject * result = nil; + if (RelVaultNode * rvnPlr = VaultGetPlayerNodeIncRef()) { + if (RelVaultNode * rvnFldr = rvnPlr->GetChildFolderNodeIncRef(folderType, 1)) { + result = pyVaultFolderNode::New(rvnFldr); + rvnFldr->DecRef(); + } + rvnPlr->DecRef(); + } + + return result; +} + +pyVaultPlayerNode::pyVaultPlayerNode(RelVaultNode *nfsNode) +: pyVaultNode(nfsNode) +{ +} + +//create from the Python side +pyVaultPlayerNode::pyVaultPlayerNode() +: pyVaultNode(nil) // may not create player nodes from python +{ +} + +//================================================================== +// class RelVaultNode : public plVaultNode +// +PyObject *pyVaultPlayerNode::GetInbox() +{ + if (PyObject * result = GetPlayerVaultFolder(plVault::kInboxFolder)) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetAvatarOutfitFolder() +{ + if (PyObject * result = GetPlayerVaultFolder(plVault::kAvatarOutfitFolder)) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetAvatarClosetFolder() +{ + if (PyObject * result = GetPlayerVaultFolder(plVault::kAvatarClosetFolder)) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetChronicleFolder() +{ + if (PyObject * result = GetPlayerVaultFolder(plVault::kChronicleFolder)) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetAgeJournalsFolder() +{ + if (PyObject * result = GetPlayerVaultFolder(plVault::kAgeJournalsFolder)) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetIgnoreListFolder() +{ + if (PyObject * result = GetPlayerVaultFolder(plVault::kIgnoreListFolder)) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetBuddyListFolder() +{ + if (PyObject * result = GetPlayerVaultFolder(plVault::kBuddyListFolder)) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetPeopleIKnowAboutFolder() +{ + if (PyObject * result = GetPlayerVaultFolder(plVault::kPeopleIKnowAboutFolder)) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetAgesICanVisitFolder() +{ + if (PyObject * result = GetPlayerVaultFolder(plVault::kAgesICanVisitFolder)) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetAgesIOwnFolder() +{ + if (PyObject * result = GetPlayerVaultFolder(plVault::kAgesIOwnFolder)) + return result; + + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetPlayerInfo() +{ + if (RelVaultNode * rvn = VaultGetPlayerInfoNodeIncRef()) { + PyObject * result = pyVaultPlayerInfoNode::New(rvn); + rvn->DecRef(); + return result; + } + + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetLinkToMyNeighborhood() +{ + plAgeLinkStruct * link = NEW(plAgeLinkStruct); + + if (VaultGetLinkToMyNeighborhood(link)) { + PyObject * result = pyAgeLinkStruct::New(link); + return result; + } + + DEL(link); + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetLinkToCity() +{ + plAgeLinkStruct * link = NEW(plAgeLinkStruct); + + if (VaultGetLinkToCity(link)) { + PyObject * result = pyAgeLinkStruct::New(link); + return result; + } + + DEL(link); + PYTHON_RETURN_NONE; +} + +PyObject *pyVaultPlayerNode::GetOwnedAgeLink(const pyAgeInfoStruct *info) +{ + plAgeLinkStruct link; + if (VaultGetOwnedAgeLink(info->GetAgeInfo(), &link)) + return pyAgeLinkStruct::New(&link); + + PYTHON_RETURN_NONE; +} + +void pyVaultPlayerNode::RemoveOwnedAgeLink(const char* ageFilename) +{ + plAgeInfoStruct info; + info.SetAgeFilename(ageFilename); + VaultUnregisterOwnedAgeAndWait(&info); +} + +PyObject *pyVaultPlayerNode::GetVisitAgeLink(const pyAgeInfoStruct *info) +{ + if (RelVaultNode * rvn = VaultGetVisitAgeLinkIncRef(info->GetAgeInfo())) { + PyObject * result = pyVaultAgeLinkNode::New(rvn); + rvn->DecRef(); + return result; + } + + PYTHON_RETURN_NONE; +} + +void pyVaultPlayerNode::RemoveVisitAgeLink(const char *guidstr) +{ + Uuid uuid; + GuidFromString(guidstr, &uuid); + plAgeInfoStruct info; + info.SetAgeInstanceGuid(&plUUID(uuid)); + VaultUnregisterOwnedAgeAndWait(&info); +} + +PyObject *pyVaultPlayerNode::FindChronicleEntry(const char *entryName) +{ + wchar wStr[MAX_PATH]; + StrToUnicode(wStr, entryName, arrsize(wStr)); + if (RelVaultNode * rvn = VaultFindChronicleEntryIncRef(wStr)) { + PyObject * result = pyVaultChronicleNode::New(rvn); + rvn->DecRef(); + return result; + } + + PYTHON_RETURN_NONE; +} + +void pyVaultPlayerNode::SetPlayerName(const char *value) +{ + hsAssert(false, "python may not change a player's name this way"); +} + +std::string pyVaultPlayerNode::GetPlayerName() +{ + if (!fNode) + return ""; + + VaultPlayerNode player(fNode); + char ansiStr[MAX_PATH]; + StrToAnsi(ansiStr, player.playerName, arrsize(ansiStr)); + return ansiStr; +} + +void pyVaultPlayerNode::SetAvatarShapeName(const char *value) +{ + hsAssert(false, "python may not change a player's avatar this way"); +} + +std::string pyVaultPlayerNode::GetAvatarShapeName() +{ + if (!fNode) + return ""; + + VaultPlayerNode player(fNode); + char ansiStr[MAX_PATH]; + StrToAnsi(ansiStr, player.avatarShapeName, arrsize(ansiStr)); + return ansiStr; +} + +void pyVaultPlayerNode::SetDisabled(bool value) +{ + hsAssert(false, "python may not change a player's disable state this way"); +} + +bool pyVaultPlayerNode::IsDisabled() +{ + if (!fNode) + return false; + + VaultPlayerNode player(fNode); + return player.disabled; +} + +void pyVaultPlayerNode::SetOnlineTime(UInt32 value) +{ + hsAssert(false, "python may not change a player's online time this way"); +} + +UInt32 pyVaultPlayerNode::GetOnlineTime() +{ + if (!fNode) + return 0; + + VaultPlayerNode player(fNode); + return player.onlineTime; +} + +void pyVaultPlayerNode::SetExplorer (bool b) { + if (!fNode) + return; + + VaultPlayerNode player(fNode); + player.SetExplorer(b); +} + +hsBool pyVaultPlayerNode::IsExplorer () { + if (!fNode) + return false; + + VaultPlayerNode player(fNode); + return player.explorer; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNode.h new file mode 100644 index 00000000..76996b3c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNode.h @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyVaultPlayerNode_h_ +#define pyVaultPlayerNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultPlayerNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +#include "../pfPython/pyVaultNode.h" +#include "../pfPython/pyVaultPlayerInfoNode.h" + +class pyAgeInfoStruct; +struct RelVaultNode; + +class pyVaultPlayerNode : public pyVaultNode +{ +protected: + // should only be created from C++ side + pyVaultPlayerNode(RelVaultNode *nfsNode); + + //create from the Python side + pyVaultPlayerNode(); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultPlayerNode); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(RelVaultNode *nfsNode); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultPlayerNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultPlayerNode); // converts a PyObject to a pyVaultPlayerNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + +//================================================================== +// class plVaultPlayerInfoNode : public plVaultNode +// + PyObject *GetInbox(); // returns pyVaultFolderNode + PyObject *GetPlayerInfo(); // returns pyVaultPlayerInfoNode + PyObject *GetAvatarOutfitFolder(); // returns pyVaultFolderNode + PyObject *GetAvatarClosetFolder(); // returns pyVaultFolderNode + PyObject *GetChronicleFolder(); // returns pyVaultFolderNode + PyObject *GetAgeJournalsFolder(); // returns pyVaultFolderNode + PyObject *GetIgnoreListFolder(); // returns pyVaultPlayerInfoListNode + PyObject *GetBuddyListFolder(); // returns pyVaultPlayerInfoListNode + PyObject *GetPeopleIKnowAboutFolder(); // returns pyVaultPlayerInfoListNode + PyObject *GetAgesICanVisitFolder(); // returns pyVaultFolderNode + PyObject *GetAgesIOwnFolder(); // returns pyVaultFolderNode + + PyObject *GetLinkToMyNeighborhood(); // returns pyVaultAgeLinkNode + PyObject *GetLinkToCity(); // returns pyVaultAgeLinkNode + + PyObject *GetOwnedAgeLink(const pyAgeInfoStruct *info); // returns pyVaultAgeLinkNode + void RemoveOwnedAgeLink(const char* guid); + + PyObject *GetVisitAgeLink(const pyAgeInfoStruct *info); // returns pyVaultAgeLinkNode + void RemoveVisitAgeLink(const char* guid); + + PyObject *FindChronicleEntry(const char *entryName); // returns pyVaultChronicleNode + + void SetPlayerName(const char *value); + std::string GetPlayerName(); + + void SetAvatarShapeName(const char *value); + std::string GetAvatarShapeName(); + + void SetDisabled(bool value); + bool IsDisabled(); + + void SetOnlineTime(UInt32 value); + UInt32 GetOnlineTime(); + + void SetExplorer (bool b); + hsBool IsExplorer (); +}; + +#endif // pyVaultPlayerNode_h_ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNodeGlue.cpp new file mode 100644 index 00000000..5653dd25 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultPlayerNodeGlue.cpp @@ -0,0 +1,326 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultPlayerNode.h" +#include "pyAgeInfoStruct.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultPlayerNode, pyVaultPlayerNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultPlayerNode, pyVaultPlayerNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultPlayerNode) + +PYTHON_INIT_DEFINITION(ptVaultPlayerNode, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getInbox) +{ + return self->fThis->GetInbox(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getPlayerInfo) +{ + return self->fThis->GetPlayerInfo(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getAvatarOutfitFolder) +{ + return self->fThis->GetAvatarOutfitFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getAvatarClosetFolder) +{ + return self->fThis->GetAvatarClosetFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getChronicleFolder) +{ + return self->fThis->GetChronicleFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getAgeJournalsFolder) +{ + return self->fThis->GetAgeJournalsFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getIgnoreListFolder) +{ + return self->fThis->GetIgnoreListFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getBuddyListFolder) +{ + return self->fThis->GetBuddyListFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getPeopleIKnowAboutFolder) +{ + return self->fThis->GetPeopleIKnowAboutFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getAgesICanVisitFolder) +{ + return self->fThis->GetAgesICanVisitFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getAgesIOwnFolder) +{ + return self->fThis->GetAgesIOwnFolder(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getLinkToMyNeighborhood) +{ + return self->fThis->GetLinkToMyNeighborhood(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getLinkToCity) +{ + return self->fThis->GetLinkToCity(); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerNode, getOwnedAgeLink, args) +{ + PyObject* infoObj = NULL; + if (!PyArg_ParseTuple(args, "O", &infoObj)) + { + PyErr_SetString(PyExc_TypeError, "getOwnedAgeLink expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeInfoStruct::Check(infoObj)) + { + PyErr_SetString(PyExc_TypeError, "getOwnedAgeLink expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + pyAgeInfoStruct* info = pyAgeInfoStruct::ConvertFrom(infoObj); + return self->fThis->GetOwnedAgeLink(info); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerNode, removeOwnedAgeLink, args) +{ + char* guid; + if (!PyArg_ParseTuple(args, "s", &guid)) + { + PyErr_SetString(PyExc_TypeError, "removeOwnedAgeLink expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->RemoveOwnedAgeLink(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerNode, getVisitAgeLink, args) +{ + PyObject* infoObj = NULL; + if (!PyArg_ParseTuple(args, "O", &infoObj)) + { + PyErr_SetString(PyExc_TypeError, "getVisitAgeLink expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeInfoStruct::Check(infoObj)) + { + PyErr_SetString(PyExc_TypeError, "getVisitAgeLink expects a ptAgeInfoStruct"); + PYTHON_RETURN_ERROR; + } + pyAgeInfoStruct* info = pyAgeInfoStruct::ConvertFrom(infoObj); + return self->fThis->GetVisitAgeLink(info); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerNode, removeVisitAgeLink, args) +{ + char* guid; + if (!PyArg_ParseTuple(args, "s", &guid)) + { + PyErr_SetString(PyExc_TypeError, "removeVisitAgeLink expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->RemoveVisitAgeLink(guid); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerNode, findChronicleEntry, args) +{ + char* entryName; + if (!PyArg_ParseTuple(args, "s", &entryName)) + { + PyErr_SetString(PyExc_TypeError, "findChronicleEntry expects a string"); + PYTHON_RETURN_ERROR; + } + return self->fThis->FindChronicleEntry(entryName); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerNode, setPlayerName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "setPlayerName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetPlayerName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getPlayerName) +{ + return PyString_FromString(self->fThis->GetPlayerName().c_str()); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerNode, setAvatarShapeName, args) +{ + char* name; + if (!PyArg_ParseTuple(args, "s", &name)) + { + PyErr_SetString(PyExc_TypeError, "setAvatarShapeName expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAvatarShapeName(name); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getAvatarShapeName) +{ + return PyString_FromString(self->fThis->GetAvatarShapeName().c_str()); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerNode, setDisabled, args) +{ + char stateFlag; + if (!PyArg_ParseTuple(args, "b", &stateFlag)) + { + PyErr_SetString(PyExc_TypeError, "setDisabled expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetDisabled(stateFlag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, isDisabled) +{ + PYTHON_RETURN_BOOL(self->fThis->IsDisabled()); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerNode, setOnlineTime, args) +{ + unsigned long time; + if (!PyArg_ParseTuple(args, "l", &time)) + { + PyErr_SetString(PyExc_TypeError, "setOnlineTime expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetOnlineTime(time); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, getOnlineTime) +{ + return PyLong_FromUnsignedLong(self->fThis->GetOnlineTime()); +} + +PYTHON_METHOD_DEFINITION(ptVaultPlayerNode, setExplorer, args) +{ + char explorer; + if (!PyArg_ParseTuple(args, "b", &explorer)) + { + PyErr_SetString(PyExc_TypeError, "setExplorer expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetExplorer(explorer != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultPlayerNode, isExplorer) +{ + PYTHON_RETURN_BOOL(self->fThis->IsExplorer()); +} + + +PYTHON_BASIC_METHOD_DEFINITION(ptVaultPlayerNode, save, Save) + +PYTHON_START_METHODS_TABLE(ptVaultPlayerNode) + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getInbox, "Returns the player's inbox"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getPlayerInfo, "Returns ptVaultPlayerInfoNode"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getAvatarOutfitFolder, "Returns ptVaultFolderNode"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getAvatarClosetFolder, "Returns ptVaultFolderNode"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getChronicleFolder, "Returns ptVaultFolderNode"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getAgeJournalsFolder, "Returns ptVaultFolderNode"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getIgnoreListFolder, "Returns ptVaultPlayerInfoListNode"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getBuddyListFolder, "Returns ptVaultPlayerInfoListNode"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getPeopleIKnowAboutFolder, "Returns ptVaultPlayerInfoListNode"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getAgesICanVisitFolder, "Returns ptVaultFolderNode"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getAgesIOwnFolder, "Returns ptVaultFolderNode"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getLinkToMyNeighborhood, "Returns ptVaultAgeLinkNode"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getLinkToCity, "Returns ptVaultAgeLinkNode"), + PYTHON_METHOD(ptVaultPlayerNode, getOwnedAgeLink, "Params: info\nReturns pyVaultAgeLinkNode"), + PYTHON_METHOD(ptVaultPlayerNode, removeOwnedAgeLink, "Params: guid\nRemoves the specified link"), + PYTHON_METHOD(ptVaultPlayerNode, getVisitAgeLink, "Params: info\nReturns pyVaultAgeLinkNode"), + PYTHON_METHOD(ptVaultPlayerNode, removeVisitAgeLink, "Params: guid\nRemoves the specified link"), + PYTHON_METHOD(ptVaultPlayerNode, findChronicleEntry, "Params: entryName\nReturns ptVaultChronicleNode"), + PYTHON_METHOD(ptVaultPlayerNode, setPlayerName, "Params: name\nSets the player's name"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getPlayerName, "Returns the player's name"), + PYTHON_METHOD(ptVaultPlayerNode, setAvatarShapeName, "Params: name\nSets the avatar's 'shape'"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getAvatarShapeName, "Returns the avatar's 'shape'"), + PYTHON_METHOD(ptVaultPlayerNode, setDisabled, "Params: state\nDisables/enables the avatar"), + PYTHON_METHOD(ptVaultPlayerNode, isDisabled, "Is the avatar disabled?"), + PYTHON_METHOD(ptVaultPlayerNode, setOnlineTime, "Params: time\nSets the avatar's online time"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, getOnlineTime, "Returns the avatar's online time"), + PYTHON_BASIC_METHOD(ptVaultPlayerNode, save, "Saves the node"), + PYTHON_METHOD(ptVaultPlayerNode, setExplorer, "Params: boolean\nset true for 'explorer', false for 'visitor'"), + PYTHON_METHOD_NOARGS(ptVaultPlayerNode, isExplorer, "Returns true for 'explorer', false for 'visitor'."), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultPlayerNode, pyVaultNode, "Plasma vault player node"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptVaultPlayerNode, pyVaultPlayerNode) + +PyObject *pyVaultPlayerNode::New(RelVaultNode* nfsNode) +{ + ptVaultPlayerNode *newObj = (ptVaultPlayerNode*)ptVaultPlayerNode_type.tp_new(&ptVaultPlayerNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultPlayerNode, pyVaultPlayerNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultPlayerNode, pyVaultPlayerNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultPlayerNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultPlayerNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNode.cpp new file mode 100644 index 00000000..4abe9113 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNode.cpp @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultSDLNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultSDLNode.h" +#include "pySDL.h" + +#include "../plVault/plVault.h" + +#include "../plSDL/plSDL.h" + +// should only be created from C++ side +pyVaultSDLNode::pyVaultSDLNode(RelVaultNode* nfsNode) +: pyVaultNode(nfsNode) +{ +} + +//create from the Python side +pyVaultSDLNode::pyVaultSDLNode() +: pyVaultNode(NEWZERO(RelVaultNode)) +{ + fNode->SetNodeType(plVault::kNodeType_SDL); +} + + +//================================================================== +// class RelVaultNode : public plVaultNode +// +void pyVaultSDLNode::SetIdent( int v ) +{ + if (!fNode) + return; + + VaultSDLNode sdl(fNode); + sdl.SetSdlIdent(v); +} + +int pyVaultSDLNode::GetIdent() const +{ + if (!fNode) + return 0; + + VaultSDLNode sdl(fNode); + return sdl.sdlIdent; +} + +PyObject * pyVaultSDLNode::GetStateDataRecord() const +{ + if (!fNode) + PYTHON_RETURN_NONE; + + VaultSDLNode sdl(fNode); + plStateDataRecord * rec = NEWZERO(plStateDataRecord); + if (sdl.GetStateDataRecord(rec)) + return pySDLStateDataRecord::New(rec); + else + DEL(rec); + + PYTHON_RETURN_NONE; +} + +void pyVaultSDLNode::InitStateDataRecord( const char* agename, int flags) +{ + if (!fNode) + return; + + wchar wStr[MAX_PATH]; + StrToUnicode(wStr, agename, arrsize(wStr)); + VaultSDLNode sdl(fNode); + sdl.InitStateDataRecord(wStr, flags); +} + +void pyVaultSDLNode::SetStateDataRecord( const pySDLStateDataRecord & rec, int writeOptions/*=0 */) +{ + if (!fNode) + return; + + VaultSDLNode sdl(fNode); + sdl.SetStateDataRecord(rec.GetRec(), writeOptions); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNode.h new file mode 100644 index 00000000..4c70cbaa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNode.h @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultSDLNode_h_ +#define _pyVaultSDLNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultSDLNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyVaultNode.h" + +struct RelVaultNode; +class pySDLStateDataRecord; + + +class pyVaultSDLNode : public pyVaultNode +{ +protected: + // should only be created from C++ side + pyVaultSDLNode(RelVaultNode* nfsNode); + + //create from the Python side + pyVaultSDLNode(); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultSDLNode); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(RelVaultNode* nfsNode); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultSDLNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultSDLNode); // converts a PyObject to a pyVaultSDLNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + +//================================================================== +// class RelVaultNode : public plVaultNode +// + int GetIdent() const; + void SetIdent( int v ); + void InitStateDataRecord( const char* agename, int flags); + + PyObject * GetStateDataRecord() const; // returns pySDLStateDataRecord + void SetStateDataRecord( const pySDLStateDataRecord & rec, int writeOptions=0 ); +}; + +#endif // _pyVaultSDLNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNodeGlue.cpp new file mode 100644 index 00000000..5d4ba1d4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSDLNodeGlue.cpp @@ -0,0 +1,134 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultSDLNode.h" +#include "pySDL.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultSDLNode, pyVaultSDLNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultSDLNode, pyVaultSDLNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultSDLNode) + +PYTHON_INIT_DEFINITION(ptVaultSDLNode, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultSDLNode, getIdent) +{ + return PyInt_FromLong(self->fThis->GetIdent()); +} + +PYTHON_METHOD_DEFINITION(ptVaultSDLNode, setIdent, args) +{ + int v; + if (!PyArg_ParseTuple(args, "i", &v)) + { + PyErr_SetString(PyExc_TypeError, "setIdent expects an integer"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetIdent(v); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultSDLNode, initStateDataRecord, args) +{ + char* fileName; + int flags; + if (!PyArg_ParseTuple(args, "si", &fileName, &flags)) + { + PyErr_SetString(PyExc_TypeError, "initStateDataRecord expects a string and an int"); + PYTHON_RETURN_ERROR; + } + self->fThis->InitStateDataRecord(fileName, flags); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultSDLNode, getStateDataRecord) +{ + return self->fThis->GetStateDataRecord(); +} + +PYTHON_METHOD_DEFINITION(ptVaultSDLNode, setStateDataRecord, args) +{ + PyObject* recObj = NULL; + int writeOptions = 0; + if (!PyArg_ParseTuple(args, "O|i", &recObj, &writeOptions)) + { + PyErr_SetString(PyExc_TypeError, "setStateDataRecord expects a ptSDLStateDataRecord and an optional int"); + PYTHON_RETURN_ERROR; + } + if (!pySDLStateDataRecord::Check(recObj)) + { + PyErr_SetString(PyExc_TypeError, "setStateDataRecord expects a ptSDLStateDataRecord and an optional int"); + PYTHON_RETURN_ERROR; + } + pySDLStateDataRecord* rec = pySDLStateDataRecord::ConvertFrom(recObj); + self->fThis->SetStateDataRecord(*rec, writeOptions); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptVaultSDLNode) + PYTHON_METHOD_NOARGS(ptVaultSDLNode, getIdent, "UNKNOWN"), + PYTHON_METHOD(ptVaultSDLNode, setIdent, "Params: v\nUNKNOWN"), + PYTHON_METHOD(ptVaultSDLNode, initStateDataRecord, "Params: filename,flags\nRead the SDL Rec from File if needed"), + PYTHON_METHOD_NOARGS(ptVaultSDLNode, getStateDataRecord, "Returns the ptSDLStateDataRecord associated with this node"), + PYTHON_METHOD(ptVaultSDLNode, setStateDataRecord, "Params: rec,writeOptions=0\nSets the ptSDLStateDataRecord"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultSDLNode, pyVaultNode, "Plasma vault SDL node"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptVaultSDLNode, pyVaultSDLNode) + +PyObject *pyVaultSDLNode::New(RelVaultNode* nfsNode) +{ + ptVaultSDLNode *newObj = (ptVaultSDLNode*)ptVaultSDLNode_type.tp_new(&ptVaultSDLNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultSDLNode, pyVaultSDLNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultSDLNode, pyVaultSDLNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultSDLNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultSDLNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNode.cpp new file mode 100644 index 00000000..b2e1f2aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNode.cpp @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultSystemNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultSystemNode.h" +#ifndef BUILDING_PYPLASMA +#include "pyVault.h" +#endif +#include "pyVaultAgeLinkNode.h" +#include "pyVaultFolderNode.h" + +#include "../plVault/plVault.h" + +// should only be created from C++ side +pyVaultSystemNode::pyVaultSystemNode(RelVaultNode* nfsNode) +: pyVaultNode(nfsNode) +{ +} + +//create from the Python side +pyVaultSystemNode::pyVaultSystemNode() +: pyVaultNode(nil) // may not create this node type from python +{ +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNode.h new file mode 100644 index 00000000..573eba9a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNode.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultSystemNode_h_ +#define _pyVaultSystemNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultSystemNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include +#include "pyGlueHelpers.h" + +#include "pyVaultNode.h" + +struct RelVaultNode; + +class pyVaultSystemNode : public pyVaultNode +{ +protected: + // should only be created from C++ side + pyVaultSystemNode(RelVaultNode* nfsNode); + + //create from the Python side + pyVaultSystemNode(); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultSystemNode); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(RelVaultNode* nfsNode); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultSystemNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultSystemNode); // converts a PyObject to a pyVaultSystemNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + +}; + +#endif // _pyVaultSystemNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNodeGlue.cpp new file mode 100644 index 00000000..f077aa4a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultSystemNodeGlue.cpp @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultSystemNode.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultSystemNode, pyVaultSystemNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultSystemNode, pyVaultSystemNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultSystemNode) + +PYTHON_INIT_DEFINITION(ptVaultSystemNode, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_START_METHODS_TABLE(ptVaultSystemNode) + // no methods... +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultSystemNode, pyVaultNode, "Plasma vault system node"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptVaultSystemNode, pyVaultSystemNode) + +PyObject *pyVaultSystemNode::New(RelVaultNode* nfsNode) +{ + ptVaultSystemNode *newObj = (ptVaultSystemNode*)ptVaultSystemNode_type.tp_new(&ptVaultSystemNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultSystemNode, pyVaultSystemNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultSystemNode, pyVaultSystemNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultSystemNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultSystemNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNode.cpp new file mode 100644 index 00000000..6434219a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNode.cpp @@ -0,0 +1,219 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////// +// +// pyVaultTextNoteNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "pyVaultTextNoteNode.h" +#ifndef BUILDING_PYPLASMA +#include "pyVault.h" +#endif +#include "pyVaultAgeLinkNode.h" +#include "pyVaultFolderNode.h" + +#include "../plVault/plVault.h" + +// should only be created from C++ side +pyVaultTextNoteNode::pyVaultTextNoteNode(RelVaultNode* nfsNode) +: pyVaultNode(nfsNode) +{ +} + +//create from the Python side +pyVaultTextNoteNode::pyVaultTextNoteNode() +: pyVaultNode(NEWZERO(RelVaultNode)) +{ + fNode->SetNodeType(plVault::kNodeType_TextNote); +} + + +//================================================================== +// class RelVaultNode : public plVaultNode +// +void pyVaultTextNoteNode::Note_SetTitle( const char * text ) +{ + if (!fNode) + return; + + wchar wStr[MAX_PATH] = L""; + if (text) + StrToUnicode(wStr, text, arrsize(wStr)); + VaultTextNoteNode textNote(fNode); + textNote.SetNoteTitle(wStr); +} + +void pyVaultTextNoteNode::Note_SetTitleW( const wchar_t * text ) +{ + if (!fNode) + return; + + VaultTextNoteNode textNote(fNode); + textNote.SetNoteTitle(text); +} + +std::string pyVaultTextNoteNode::Note_GetTitle( void ) +{ + if (!fNode) + return ""; + + char * aStr = nil; + VaultTextNoteNode textNote(fNode); + if (textNote.noteTitle) { + std::string result; + aStr = StrDupToAnsi(textNote.noteTitle); + result = aStr; + FREE(aStr); + return result; + } + return ""; +} + +std::wstring pyVaultTextNoteNode::Note_GetTitleW( void ) +{ + if (!fNode) + return L""; + + VaultTextNoteNode textNote(fNode); + if (textNote.noteTitle) + return textNote.noteTitle; + return L""; +} + +void pyVaultTextNoteNode::Note_SetText( const char * text ) +{ + if (!fNode) + return; + + wchar * wStr = nil; + if (text) + wStr = StrDupToUnicode(text); + VaultTextNoteNode textNote(fNode); + textNote.SetNoteText(wStr); + FREE(wStr); +} + +void pyVaultTextNoteNode::Note_SetTextW( const wchar_t * text ) +{ + if (!fNode) + return; + + VaultTextNoteNode textNote(fNode); + textNote.SetNoteText(text); +} + +std::string pyVaultTextNoteNode::Note_GetText( void ) +{ + if (!fNode) + return ""; + + char * aStr = nil; + VaultTextNoteNode textNote(fNode); + if (textNote.noteText) { + std::string result; + aStr = StrDupToAnsi(textNote.noteText); + result = aStr; + FREE(aStr); + return result; + } + return ""; +} + +std::wstring pyVaultTextNoteNode::Note_GetTextW( void ) +{ + if (!fNode) + return L""; + + VaultTextNoteNode textNote(fNode); + if (textNote.noteText) + return textNote.noteText; + return L""; +} + +void pyVaultTextNoteNode::Note_SetType( Int32 type ) +{ + if (!fNode) + return; + + VaultTextNoteNode textNote(fNode); + textNote.SetNoteType(type); +} + +Int32 pyVaultTextNoteNode::Note_GetType( void ) +{ + if (!fNode) + return 0; + + VaultTextNoteNode textNote(fNode); + return textNote.noteType; +} + +void pyVaultTextNoteNode::Note_SetSubType( Int32 type ) +{ + if (!fNode) + return; + + VaultTextNoteNode textNote(fNode); + textNote.SetNoteSubType(type); +} + +Int32 pyVaultTextNoteNode::Note_GetSubType( void ) +{ + if (!fNode) + return 0; + + VaultTextNoteNode textNote(fNode); + return textNote.noteSubType; +} + +PyObject * pyVaultTextNoteNode::GetDeviceInbox() const +{ + if (!fNode) + PYTHON_RETURN_NONE; + + hsAssert(false, "eric, port me"); + PYTHON_RETURN_NONE; +} + +void pyVaultTextNoteNode::SetDeviceInbox( const char * devName, PyObject * cbObject, UInt32 cbContext ) +{ + if (!fNode) + return; + + pyVaultNode::pyVaultNodeOperationCallback * cb = NEWZERO(pyVaultNode::pyVaultNodeOperationCallback)( cbObject ); + cb->VaultOperationStarted( cbContext ); + + wchar wDev[MAX_PATH]; + StrToUnicode(wDev, devName, arrsize(wDev)); + + if (RelVaultNode * rvn = VaultAgeSetDeviceInboxAndWaitIncRef(wDev, DEFAULT_DEVICE_INBOX)) { + cb->SetNode(rvn); + rvn->DecRef(); + } + + cb->VaultOperationComplete( cbContext, cb->GetNode() ? hsOK : hsFail ); // cbHolder deletes itself here. +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNode.h new file mode 100644 index 00000000..f362cb70 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNode.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pyVaultTextNoteNode_h_ +#define _pyVaultTextNoteNode_h_ + +////////////////////////////////////////////////////////////////////// +// +// pyVaultTextNoteNode - a wrapper class to provide interface to the RelVaultNode +// +////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include +#include "pyGlueHelpers.h" + +#include "pyVaultNode.h" + +struct RelVaultNode; +class pyVaultAgeLinkNode; + + +class pyVaultTextNoteNode : public pyVaultNode +{ +protected: + // should only be created from C++ side + pyVaultTextNoteNode(RelVaultNode* nfsNode); + + //create from the Python side + pyVaultTextNoteNode(); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptVaultTextNoteNode); + PYTHON_CLASS_NEW_DEFINITION; + static PyObject *New(RelVaultNode* nfsNode); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVaultTextNoteNode object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVaultTextNoteNode); // converts a PyObject to a pyVaultTextNoteNode (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + +//================================================================== +// class RelVaultNode : public plVaultNode +// + void Note_SetTitle( const char * text ); + void Note_SetTitleW( const wchar_t * text ); + std::string Note_GetTitle( void ); + std::wstring Note_GetTitleW( void ); + void Note_SetText( const char * text ); + void Note_SetTextW( const wchar_t * text ); + std::string Note_GetText( void ); + std::wstring Note_GetTextW( void ); + void Note_SetType( Int32 type ); + Int32 Note_GetType( void ); + + void Note_SetSubType( Int32 type ); + Int32 Note_GetSubType( void ); + + PyObject * GetDeviceInbox() const; // returns pyVaultFolderNode + void SetDeviceInbox( const char * devName, PyObject * cb=nil, UInt32 cbContext=0 ); +}; + +#endif // _pyVaultTextNoteNode_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNodeGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNodeGlue.cpp new file mode 100644 index 00000000..634dee64 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyVaultTextNoteNodeGlue.cpp @@ -0,0 +1,323 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVaultTextNoteNode.h" + +#include "../plVault/plVault.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVaultTextNoteNode, pyVaultTextNoteNode); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVaultTextNoteNode, pyVaultTextNoteNode) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVaultTextNoteNode) + +PYTHON_INIT_DEFINITION(ptVaultTextNoteNode, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptVaultTextNoteNode, noteSetTitle, args) +{ + char* title; + if (!PyArg_ParseTuple(args, "s", &title)) + { + PyErr_SetString(PyExc_TypeError, "noteSetTitle expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Note_SetTitle(title); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultTextNoteNode, noteGetTitle) +{ + return PyString_FromString(self->fThis->Note_GetTitle().c_str()); +} + +PYTHON_METHOD_DEFINITION(ptVaultTextNoteNode, noteSetText, args) +{ + char* text; + if (!PyArg_ParseTuple(args, "s", &text)) + { + PyErr_SetString(PyExc_TypeError, "noteSetText expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Note_SetText(text); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultTextNoteNode, noteGetText) +{ + return PyString_FromString(self->fThis->Note_GetText().c_str()); +} + +PYTHON_METHOD_DEFINITION(ptVaultTextNoteNode, noteSetType, args) +{ + long nodeType; + if (!PyArg_ParseTuple(args, "l", &nodeType)) + { + PyErr_SetString(PyExc_TypeError, "noteSetType expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->Note_SetType(nodeType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultTextNoteNode, noteGetType) +{ + return PyLong_FromLong(self->fThis->Note_GetType()); +} + +PYTHON_METHOD_DEFINITION(ptVaultTextNoteNode, noteSetSubType, args) +{ + long nodeType; + if (!PyArg_ParseTuple(args, "l", &nodeType)) + { + PyErr_SetString(PyExc_TypeError, "noteSetSubType expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->Note_SetSubType(nodeType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultTextNoteNode, noteGetSubType) +{ + return PyLong_FromLong(self->fThis->Note_GetSubType()); +} + +PYTHON_METHOD_DEFINITION(ptVaultTextNoteNode, setTitle, args) +{ + char* title; + if (!PyArg_ParseTuple(args, "s", &title)) + { + PyErr_SetString(PyExc_TypeError, "setTitle expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Note_SetTitle(title); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultTextNoteNode, setTitleW, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "setTitleW expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* title = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, title, strLen); + title[strLen] = L'\0'; + self->fThis->Note_SetTitleW(title); + delete [] title; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* title = PyString_AsString(textObj); + self->fThis->Note_SetTitle(title); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "setTitleW expects a unicode string"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultTextNoteNode, getTitle) +{ + return PyString_FromString(self->fThis->Note_GetTitle().c_str()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultTextNoteNode, getTitleW) +{ + std::wstring retVal = self->fThis->Note_GetTitleW(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_METHOD_DEFINITION(ptVaultTextNoteNode, setText, args) +{ + char* text; + if (!PyArg_ParseTuple(args, "s", &text)) + { + PyErr_SetString(PyExc_TypeError, "setText expects a string"); + PYTHON_RETURN_ERROR; + } + self->fThis->Note_SetText(text); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVaultTextNoteNode, setTextW, args) +{ + PyObject* textObj; + if (!PyArg_ParseTuple(args, "O", &textObj)) + { + PyErr_SetString(PyExc_TypeError, "setTextW expects a unicode string"); + PYTHON_RETURN_ERROR; + } + if (PyUnicode_Check(textObj)) + { + int strLen = PyUnicode_GetSize(textObj); + wchar_t* text = TRACKED_NEW wchar_t[strLen + 1]; + PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen); + text[strLen] = L'\0'; + self->fThis->Note_SetTextW(text); + delete [] text; + PYTHON_RETURN_NONE; + } + else if (PyString_Check(textObj)) + { + // we'll allow this, just in case something goes weird + char* text = PyString_AsString(textObj); + self->fThis->Note_SetText(text); + PYTHON_RETURN_NONE; + } + PyErr_SetString(PyExc_TypeError, "setTextW expects a unicode string"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultTextNoteNode, getText) +{ + return PyString_FromString(self->fThis->Note_GetText().c_str()); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultTextNoteNode, getTextW) +{ + std::wstring retVal = self->fThis->Note_GetTextW(); + return PyUnicode_FromWideChar(retVal.c_str(), retVal.length()); +} + +PYTHON_METHOD_DEFINITION(ptVaultTextNoteNode, setType, args) +{ + long nodeType; + if (!PyArg_ParseTuple(args, "l", &nodeType)) + { + PyErr_SetString(PyExc_TypeError, "setType expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->Note_SetType(nodeType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultTextNoteNode, getType) +{ + return PyLong_FromLong(self->fThis->Note_GetType()); +} + +PYTHON_METHOD_DEFINITION(ptVaultTextNoteNode, setSubType, args) +{ + long nodeType; + if (!PyArg_ParseTuple(args, "l", &nodeType)) + { + PyErr_SetString(PyExc_TypeError, "setSubType expects a long"); + PYTHON_RETURN_ERROR; + } + self->fThis->Note_SetSubType(nodeType); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultTextNoteNode, getSubType) +{ + return PyLong_FromLong(self->fThis->Note_GetSubType()); +} + +PYTHON_METHOD_DEFINITION(ptVaultTextNoteNode, setDeviceInbox, args) +{ + char* inboxName; + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "s|Ol", &inboxName, &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "setDeviceInbox expects a string, an optional object, and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetDeviceInbox(inboxName, cb, context); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVaultTextNoteNode, getDeviceInbox) +{ + return self->fThis->GetDeviceInbox(); +} + +PYTHON_START_METHODS_TABLE(ptVaultTextNoteNode) + // legacy glue + PYTHON_METHOD(ptVaultTextNoteNode, noteSetTitle, "Params: title\nLEGACY\nSets the title of this text note node."), + PYTHON_METHOD_NOARGS(ptVaultTextNoteNode, noteGetTitle, "LEGACY\nReturns the title of this text note node."), + PYTHON_METHOD(ptVaultTextNoteNode, noteSetText, "Params: text\nLEGACY\nSets text of the this text note node."), + PYTHON_METHOD_NOARGS(ptVaultTextNoteNode, noteGetText, "LEGACY\nReturns the text of this text note node."), + PYTHON_METHOD(ptVaultTextNoteNode, noteSetType, "Params: type\nLEGACY\nSets the type of text note for this text note node."), + PYTHON_METHOD_NOARGS(ptVaultTextNoteNode, noteGetType, "LEGACY\nReturns the type of text note for this text note node."), + PYTHON_METHOD(ptVaultTextNoteNode, noteSetSubType, "Params: subType\nLEGACY\nSets the subtype of the this text note node."), + PYTHON_METHOD_NOARGS(ptVaultTextNoteNode, noteGetSubType, "LEGACY\nReturns the subtype of this text note node."), + // new glue + PYTHON_METHOD(ptVaultTextNoteNode, setTitle, "Params: title\nSets the title of this text note node."), + PYTHON_METHOD(ptVaultTextNoteNode, setTitleW, "Params: title\nUnicode version of setTitle"), + PYTHON_METHOD_NOARGS(ptVaultTextNoteNode, getTitle, "Returns the title of this text note node."), + PYTHON_METHOD_NOARGS(ptVaultTextNoteNode, getTitleW, "Unicode version of getTitle"), + PYTHON_METHOD(ptVaultTextNoteNode, setText, "Params: text\nSets text of the this text note node."), + PYTHON_METHOD(ptVaultTextNoteNode, setTextW, "Params: text\nUnicode version of setText"), + PYTHON_METHOD_NOARGS(ptVaultTextNoteNode, getText, "Returns the text of this text note node."), + PYTHON_METHOD_NOARGS(ptVaultTextNoteNode, getTextW, "Unicode version of getText."), + PYTHON_METHOD(ptVaultTextNoteNode, setType, "Params: type\nSets the type of text note for this text note node."), + PYTHON_METHOD_NOARGS(ptVaultTextNoteNode, getType, "Returns the type of text note for this text note node."), + PYTHON_METHOD(ptVaultTextNoteNode, setSubType, "Params: subType\nSets the subtype of the this text note node."), + PYTHON_METHOD_NOARGS(ptVaultTextNoteNode, getSubType, "Returns the subtype of this text note node."), + PYTHON_METHOD(ptVaultTextNoteNode, setDeviceInbox, "Params: inboxName,cb=None,cbContext=0\nSets the device inbox"), + PYTHON_METHOD_NOARGS(ptVaultTextNoteNode, getDeviceInbox, "Returns a ptVaultFolderNode"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptVaultTextNoteNode, pyVaultNode, "Plasma vault text note node"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptVaultTextNoteNode, pyVaultTextNoteNode) + +PyObject *pyVaultTextNoteNode::New(RelVaultNode* nfsNode) +{ + ptVaultTextNoteNode *newObj = (ptVaultTextNoteNode*)ptVaultTextNoteNode_type.tp_new(&ptVaultTextNoteNode_type, NULL, NULL); + if (newObj->fThis->fNode) + newObj->fThis->fNode->DecRef(); + newObj->fThis->fNode = nfsNode; + if (newObj->fThis->fNode) + newObj->fThis->fNode->IncRef(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVaultTextNoteNode, pyVaultTextNoteNode) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVaultTextNoteNode, pyVaultTextNoteNode) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVaultTextNoteNode::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVaultTextNoteNode); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSet.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSet.cpp new file mode 100644 index 00000000..5896471f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSet.cpp @@ -0,0 +1,931 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyWaveSet.h" +#include "../plDrawable/plWaveSet7.h" + +pyWaveSet::pyWaveSet(plKey key) +{ + fWaterKey = key; +} + +pyWaveSet::pyWaveSet(pyKey& key) +{ + fWaterKey = key.getKey(); +} + +// -------------------------------------------------------------------------------- +// Geometric wave parameters. These are all safe to twiddle at any time or speed. +// The new settings take effect as new waves are spawned. + +void pyWaveSet::SetGeoMaxLength(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetGeoMaxLength(s, secs); + } + } +} + +void pyWaveSet::SetGeoMinLength(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetGeoMinLength(s, secs); + } + } +} + +void pyWaveSet::SetGeoAmpOverLen(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetGeoAmpOverLen(s, secs); + } + } +} + +void pyWaveSet::SetGeoChop(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetGeoChop(s, secs); + } + } +} + +void pyWaveSet::SetGeoAngleDev(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetGeoAngleDev(s, secs); + } + } +} + +// -------------------------------------------------------------------------------- +// Texture wave parameters. Safe to twiddle any time or speed. +// The new settings take effect as new waves are spawned. + +void pyWaveSet::SetTexMaxLength(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetTexMaxLength(s, secs); + } + } +} + +void pyWaveSet::SetTexMinLength(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetTexMinLength(s, secs); + } + } +} + +void pyWaveSet::SetTexAmpOverLen(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetTexAmpOverLen(s, secs); + } + } +} + +void pyWaveSet::SetTexChop(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetTexChop(s, secs); + } + } +} + +void pyWaveSet::SetTexAngleDev(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetTexAngleDev(s, secs); + } + } +} +// -------------------------------------------------------------------------------- +// The size in feet of one tile of the ripple texture. If you change this (I don't +// recommend it), you need to change it very slowly or it will look very stupid. + +void pyWaveSet::SetRippleScale(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetRippleScale(s, secs); + } + } +} + +// -------------------------------------------------------------------------------- +// The direction the wind is blowing (waves will be more or less perpindicular to wind dir). +// Change somewhat slowly, like over 30 seconds. + +void pyWaveSet::SetWindDir(const pyVector3& s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetWindDir(s.fVector, secs); + } + } +} + +// -------------------------------------------------------------------------------- +// Change these gently, effect is immediate. + +void pyWaveSet::SetSpecularNoise(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetSpecularNoise(s, secs); + } + } +} + +void pyWaveSet::SetSpecularStart(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetSpecularStart(s, secs); + } + } +} + +void pyWaveSet::SetSpecularEnd(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetSpecularEnd(s, secs); + } + } +} + +// -------------------------------------------------------------------------------- +// Water Height is overriden if the ref object is animated. + +void pyWaveSet::SetWaterHeight(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetWaterHeight(s, secs); + } + } +} + +// -------------------------------------------------------------------------------- +// Water Offset and DepthFalloff are complicated, and not immediately interesting to animate. + +void pyWaveSet::SetWaterOffset(const pyVector3& s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetWaterOffset(s.fVector, secs); + } + } +} + +void pyWaveSet::SetOpacOffset(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetOpacOffset(s, secs); + } + } +} + +void pyWaveSet::SetReflOffset(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetReflOffset(s, secs); + } + } +} + +void pyWaveSet::SetWaveOffset(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetWaveOffset(s, secs); + } + } +} + +void pyWaveSet::SetDepthFalloff(const pyVector3& s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetDepthFalloff(s.fVector, secs); + } + } +} + +void pyWaveSet::SetOpacFalloff(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetOpacFalloff(s, secs); + } + } +} + +void pyWaveSet::SetReflFalloff(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetReflFalloff(s, secs); + } + } +} + +void pyWaveSet::SetWaveFalloff(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetWaveFalloff(s, secs); + } + } +} + +// -------------------------------------------------------------------------------- +// Max and Min Atten aren't very interesting, and will probably go away. + +void pyWaveSet::SetMaxAtten(const pyVector3& s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetMaxAtten(s.fVector, secs); + } + } +} + +void pyWaveSet::SetMinAtten(const pyVector3& s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetMinAtten(s.fVector, secs); + } + } +} + +// -------------------------------------------------------------------------------- +// Water colors, adjust slowly, effect is immediate. + +void pyWaveSet::SetWaterTint(pyColor& s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetWaterTint(s.getColor(), secs); + } + } +} + +void pyWaveSet::SetWaterOpacity(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetWaterOpacity(s, secs); + } + } +} + +void pyWaveSet::SetSpecularTint(pyColor& s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetSpecularTint(s.getColor(), secs); + } + } +} + +void pyWaveSet::SetSpecularMute(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetSpecularMute(s, secs); + } + } +} + +// -------------------------------------------------------------------------------- +// The environment map is essentially projected onto a sphere. Moving the center of +// the sphere north will move the reflections north, changing the radius of the +// sphere effects parallax in the obvious way. + +void pyWaveSet::SetEnvCenter(const pyPoint3& s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetEnvCenter(s.fPoint, secs); + } + } +} + +void pyWaveSet::SetEnvRadius(hsScalar s, hsScalar secs) +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + waveset->SetEnvRadius(s, secs); + } + } +} + +// -------------------------------------------------------------------------------- + +// ================================================================================ +// Get Functions +// ================================================================================ + +// -------------------------------------------------------------------------------- +hsScalar pyWaveSet::GetGeoMaxLength() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetGeoMaxLength(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetGeoMinLength() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetGeoMinLength(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetGeoAmpOverLen() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetGeoAmpOverLen(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetGeoChop() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetGeoChop(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetGeoAngleDev() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetGeoAngleDev(); + } + } + + return -1; +} + +// -------------------------------------------------------------------------------- + +hsScalar pyWaveSet::GetTexMaxLength() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetTexMaxLength(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetTexMinLength() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetTexMinLength(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetTexAmpOverLen() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetTexAmpOverLen(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetTexChop() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetTexChop(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetTexAngleDev() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetTexAngleDev(); + } + } + + return -1; +} + +// -------------------------------------------------------------------------------- + +hsScalar pyWaveSet::GetRippleScale() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetRippleScale(); + } + } + + return -1; +} + +// -------------------------------------------------------------------------------- + +PyObject* pyWaveSet::GetWindDir() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return pyVector3::New(waveset->GetWindDir()); + } + } + + PYTHON_RETURN_NONE; +} + +// -------------------------------------------------------------------------------- + +hsScalar pyWaveSet::GetSpecularNoise() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetSpecularNoise(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetSpecularStart() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetSpecularStart(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetSpecularEnd() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetSpecularEnd(); + } + } + + return -1; +} + +// -------------------------------------------------------------------------------- + +hsScalar pyWaveSet::GetWaterHeight() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetWaterHeight(); + } + } + + return -1; +} + +// -------------------------------------------------------------------------------- + +PyObject* pyWaveSet::GetWaterOffset() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return pyVector3::New(waveset->GetWaterOffset()); + } + } + + PYTHON_RETURN_NONE; +} + +hsScalar pyWaveSet::GetOpacOffset() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetOpacOffset(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetReflOffset() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetReflOffset(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetWaveOffset() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetWaveOffset(); + } + } + + return -1; +} + +PyObject* pyWaveSet::GetDepthFalloff() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return pyVector3::New(waveset->GetDepthFalloff()); + } + } + + PYTHON_RETURN_NONE; +} + +hsScalar pyWaveSet::GetOpacFalloff() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetOpacFalloff(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetReflFalloff() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetReflFalloff(); + } + } + + return -1; +} + +hsScalar pyWaveSet::GetWaveFalloff() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetWaveFalloff(); + } + } + + return -1; +} + +// -------------------------------------------------------------------------------- + +PyObject* pyWaveSet::GetMaxAtten() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return pyVector3::New(waveset->GetMaxAtten()); + } + } + + PYTHON_RETURN_NONE; +} + +PyObject* pyWaveSet::GetMinAtten() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return pyVector3::New(waveset->GetMinAtten()); + } + } + + PYTHON_RETURN_NONE; +} + +// -------------------------------------------------------------------------------- + +PyObject* pyWaveSet::GetWaterTint() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return pyColor::New(waveset->GetWaterTint()); + } + } + + PYTHON_RETURN_NONE; +} + +hsScalar pyWaveSet::GetWaterOpacity() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetWaterOpacity(); + } + } + + return -1; +} + +PyObject* pyWaveSet::GetSpecularTint() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return pyColor::New(waveset->GetSpecularTint()); + } + } + + PYTHON_RETURN_NONE; +} + +hsScalar pyWaveSet::GetSpecularMute() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetSpecularMute(); + } + } + + return -1; +} + +// -------------------------------------------------------------------------------- + +PyObject* pyWaveSet::GetEnvCenter() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return pyPoint3::New(waveset->GetEnvCenter()); + } + } + + PYTHON_RETURN_NONE; +} + +hsScalar pyWaveSet::GetEnvRadius() const +{ + if (fWaterKey) + { + plWaveSet7* waveset = plWaveSet7::ConvertNoRef(fWaterKey->ObjectIsLoaded()); + if (waveset) + { + return waveset->GetEnvRadius(); + } + } + + return -1; +} + +// -------------------------------------------------------------------------------- \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSet.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSet.h new file mode 100644 index 00000000..a24cfeaf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSet.h @@ -0,0 +1,203 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyWaveSet_h +#define pyWaveSet_h + +#include "pyKey.h" +#include "pyGeometry3.h" +#include "pyColor.h" + +#include +#include "pyGlueHelpers.h" + +////////////////////////////////////////////////////////////////////// +// +// pyWaveSet - a wrapper class to provide interface to wave sets +// +////////////////////////////////////////////////////////////////////// + +/* +// Getters and Setters for Python twiddling + // + // First a way to set new values. The secs parameter says how long to take + // blending to the new value from the current value. + // + + + + + // Skipping the shore parameters, because they are never used. + + + + + // Now a way to get current values. See the accompanying Setter for notes on + // what the parameter means. + // + + + + + + + + */ + +class pyWaveSet +{ +private: + plKey fWaterKey; + +protected: + pyWaveSet(): fWaterKey(nil) {} // for python glue only, do NOT call + pyWaveSet(plKey key); + pyWaveSet(pyKey& key); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptWaveSet); + static PyObject *New(plKey key); + static PyObject *New(pyKey& key); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyWaveSet object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyWaveSet); // converts a PyObject to a pyWaveSet (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + void setKey(pyKey& key) {fWaterKey = key.getKey();} // for python glue only, do NOT call + + // ============================================================================== + // Set functions + // ============================================================================== + + // Geometric wave parameters. These are all safe to twiddle at any time or speed. + // The new settings take effect as new waves are spawned. + + void SetGeoMaxLength(hsScalar s, hsScalar secs=0); + void SetGeoMinLength(hsScalar s, hsScalar secs=0); + void SetGeoAmpOverLen(hsScalar s, hsScalar secs=0); + void SetGeoChop(hsScalar s, hsScalar secs=0); + void SetGeoAngleDev(hsScalar s, hsScalar secs=0); + + // Texture wave parameters. Safe to twiddle any time or speed. + // The new settings take effect as new waves are spawned. + + void SetTexMaxLength(hsScalar s, hsScalar secs=0); + void SetTexMinLength(hsScalar s, hsScalar secs=0); + void SetTexAmpOverLen(hsScalar s, hsScalar secs=0); + void SetTexChop(hsScalar s, hsScalar secs=0); + void SetTexAngleDev(hsScalar s, hsScalar secs=0); + + // The size in feet of one tile of the ripple texture. If you change this (I don't + // recommend it), you need to change it very slowly or it will look very stupid. + void SetRippleScale(hsScalar s, hsScalar secs=0); + + // The direction the wind is blowing (waves will be more or less perpindicular to wind dir). + // Change somewhat slowly, like over 30 seconds. + void SetWindDir(const pyVector3& s, hsScalar secs=0); + + // Change these gently, effect is immediate. + void SetSpecularNoise(hsScalar s, hsScalar secs=0); + void SetSpecularStart(hsScalar s, hsScalar secs=0); + void SetSpecularEnd(hsScalar s, hsScalar secs=0); + + // Water Height is overriden if the ref object is animated. + void SetWaterHeight(hsScalar s, hsScalar secs=0); + + // Water Offset and DepthFalloff are complicated, and not immediately interesting to animate. + void SetWaterOffset(const pyVector3& s, hsScalar secs=0); + void SetOpacOffset(hsScalar s, hsScalar secs=0); + void SetReflOffset(hsScalar s, hsScalar secs=0); + void SetWaveOffset(hsScalar s, hsScalar secs=0); + void SetDepthFalloff(const pyVector3& s, hsScalar secs=0); + void SetOpacFalloff(hsScalar s, hsScalar secs=0); + void SetReflFalloff(hsScalar s, hsScalar secs=0); + void SetWaveFalloff(hsScalar s, hsScalar secs=0); + + // Max and Min Atten aren't very interesting, and will probably go away. + void SetMaxAtten(const pyVector3& s, hsScalar secs=0); + void SetMinAtten(const pyVector3& s, hsScalar secs=0); + + // Water colors, adjust slowly, effect is immediate. + void SetWaterTint(pyColor& s, hsScalar secs=0); + void SetWaterOpacity(hsScalar s, hsScalar secs=0); + void SetSpecularTint(pyColor& s, hsScalar secs=0); + void SetSpecularMute(hsScalar s, hsScalar secs=0); + + // The environment map is essentially projected onto a sphere. Moving the center of + // the sphere north will move the reflections north, changing the radius of the + // sphere effects parallax in the obvious way. + void SetEnvCenter(const pyPoint3& s, hsScalar secs=0); + void SetEnvRadius(hsScalar s, hsScalar secs=0); + + // ============================================================================== + // Get functions + // ============================================================================== + + hsScalar GetGeoMaxLength() const; + hsScalar GetGeoMinLength() const; + hsScalar GetGeoAmpOverLen() const; + hsScalar GetGeoChop() const; + hsScalar GetGeoAngleDev() const; + + hsScalar GetTexMaxLength() const; + hsScalar GetTexMinLength() const; + hsScalar GetTexAmpOverLen() const; + hsScalar GetTexChop() const; + hsScalar GetTexAngleDev() const; + + hsScalar GetRippleScale() const; + + PyObject* GetWindDir() const; // returns pyVector3 + + hsScalar GetSpecularNoise() const; + hsScalar GetSpecularStart() const; + hsScalar GetSpecularEnd() const; + + hsScalar GetWaterHeight() const; + + PyObject* GetWaterOffset() const; // returns pyVector3 + hsScalar GetOpacOffset() const; + hsScalar GetReflOffset() const; + hsScalar GetWaveOffset() const; + PyObject* GetDepthFalloff() const; // returns pyVector3 + hsScalar GetOpacFalloff() const; + hsScalar GetReflFalloff() const; + hsScalar GetWaveFalloff() const; + + PyObject* GetMaxAtten() const; // returns pyVector3 + PyObject* GetMinAtten() const; // returns pyVector3 + + PyObject* GetWaterTint() const; // returns pyColor + hsScalar GetWaterOpacity() const; + PyObject* GetSpecularTint() const; // returns pyColor + hsScalar GetSpecularMute() const; + + PyObject* GetEnvCenter() const; // returns pyPoint3 + hsScalar GetEnvRadius() const; +}; + + +#endif // pyWaveSet_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp new file mode 100644 index 00000000..2f372100 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfPython/pyWaveSetGlue.cpp @@ -0,0 +1,225 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyWaveSet.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptWaveSet, pyWaveSet); + +PYTHON_DEFAULT_NEW_DEFINITION(ptWaveSet, pyWaveSet) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptWaveSet) + +PYTHON_INIT_DEFINITION(ptWaveSet, args, keywords) +{ + PyObject* keyObj = NULL; + if (!PyArg_ParseTuple(args, "O", &keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyKey::Check(keyObj)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptKey"); + PYTHON_RETURN_INIT_ERROR; + } + pyKey* key = pyKey::ConvertFrom(keyObj); + self->fThis->setKey(*key); + PYTHON_RETURN_INIT_OK; +} + +// some macros to make all this easier +// ---------------------------------------------------------------------------- + +#define WAVESET_FLOAT_DEF(funcSuffix) \ +PYTHON_METHOD_DEFINITION(ptWaveSet, set##funcSuffix, args) \ +{ \ + float s, secs = 0; \ + if (!PyArg_ParseTuple(args, "f|f", &s, &secs)) \ + { \ + PyErr_SetString(PyExc_TypeError, "set" #funcSuffix " expects a float and an optional float"); \ + PYTHON_RETURN_ERROR; \ + } \ + self->fThis->Set##funcSuffix(s, secs); \ + PYTHON_RETURN_NONE; \ +} \ +PYTHON_METHOD_DEFINITION_NOARGS(ptWaveSet, get##funcSuffix) \ +{ \ + return PyFloat_FromDouble(self->fThis->Get##funcSuffix()); \ +} + +#define WAVESET_FLOAT(funcSuffix) \ +PYTHON_METHOD(ptWaveSet, set##funcSuffix, "Params: s, secs = 0\nSets the attribute to s over secs time"), \ +PYTHON_METHOD_NOARGS(ptWaveSet, get##funcSuffix, "Returns the attribute's value") + +#define WAVESET_OBJ_DEF(funcSuffix, pyObjType, cObjType) \ +PYTHON_METHOD_DEFINITION(ptWaveSet, set##funcSuffix, args) \ +{ \ + PyObject* sObj = NULL; \ + float secs = 0; \ + if (!PyArg_ParseTuple(args, "O|f", &sObj, &secs)) \ + { \ + PyErr_SetString(PyExc_TypeError, "set" #funcSuffix " expects a " #pyObjType " and an optional float"); \ + PYTHON_RETURN_ERROR; \ + } \ + if (!cObjType::Check(sObj)) \ + { \ + PyErr_SetString(PyExc_TypeError, "set" #funcSuffix " expects a " #pyObjType " and an optional float"); \ + PYTHON_RETURN_ERROR; \ + } \ + cObjType* s = cObjType::ConvertFrom(sObj); \ + self->fThis->Set##funcSuffix(*s, secs); \ + PYTHON_RETURN_NONE; \ +} \ +PYTHON_METHOD_DEFINITION_NOARGS(ptWaveSet, get##funcSuffix) \ +{ \ + return self->fThis->Get##funcSuffix(); \ +} + +#define WAVESET_OBJ(funcSuffix) \ +PYTHON_METHOD(ptWaveSet, set##funcSuffix, "Params: s, secs = 0\nSets the attribute to s over secs time"), \ +PYTHON_METHOD_NOARGS(ptWaveSet, get##funcSuffix, "Returns the attribute's value") + +// ---------------------------------------------------------------------------- +// now onto the glue functions + +WAVESET_FLOAT_DEF(GeoMaxLength) +WAVESET_FLOAT_DEF(GeoMinLength) +WAVESET_FLOAT_DEF(GeoAmpOverLen) +WAVESET_FLOAT_DEF(GeoChop) +WAVESET_FLOAT_DEF(GeoAngleDev) + +WAVESET_FLOAT_DEF(TexMaxLength) +WAVESET_FLOAT_DEF(TexMinLength) +WAVESET_FLOAT_DEF(TexAmpOverLen) +WAVESET_FLOAT_DEF(TexChop) +WAVESET_FLOAT_DEF(TexAngleDev) + +WAVESET_FLOAT_DEF(RippleScale) +WAVESET_OBJ_DEF(WindDir, ptVector3, pyVector3) + +WAVESET_FLOAT_DEF(SpecularNoise) +WAVESET_FLOAT_DEF(SpecularStart) +WAVESET_FLOAT_DEF(SpecularEnd) + +WAVESET_FLOAT_DEF(WaterHeight) + +WAVESET_OBJ_DEF(WaterOffset, ptVector3, pyVector3) +WAVESET_FLOAT_DEF(OpacOffset) +WAVESET_FLOAT_DEF(ReflOffset) +WAVESET_FLOAT_DEF(WaveOffset) + +WAVESET_OBJ_DEF(DepthFalloff, ptVector3, pyVector3) +WAVESET_FLOAT_DEF(OpacFalloff) +WAVESET_FLOAT_DEF(ReflFalloff) +WAVESET_FLOAT_DEF(WaveFalloff) + +WAVESET_OBJ_DEF(MaxAtten, ptVector3, pyVector3) +WAVESET_OBJ_DEF(MinAtten, ptVector3, pyVector3) + +WAVESET_OBJ_DEF(WaterTint, ptColor, pyColor) +WAVESET_FLOAT_DEF(WaterOpacity) +WAVESET_OBJ_DEF(SpecularTint, ptColor, pyColor) +WAVESET_FLOAT_DEF(SpecularMute) + +WAVESET_OBJ_DEF(EnvCenter, ptPoint3, pyPoint3) +WAVESET_FLOAT_DEF(EnvRadius) + +PYTHON_START_METHODS_TABLE(ptWaveSet) + WAVESET_FLOAT(GeoMaxLength), + WAVESET_FLOAT(GeoMinLength), + WAVESET_FLOAT(GeoAmpOverLen), + WAVESET_FLOAT(GeoChop), + WAVESET_FLOAT(GeoAngleDev), + + WAVESET_FLOAT(TexMaxLength), + WAVESET_FLOAT(TexMinLength), + WAVESET_FLOAT(TexAmpOverLen), + WAVESET_FLOAT(TexChop), + WAVESET_FLOAT(TexAngleDev), + + WAVESET_FLOAT(RippleScale), + WAVESET_OBJ(WindDir), + + WAVESET_FLOAT(SpecularNoise), + WAVESET_FLOAT(SpecularStart), + WAVESET_FLOAT(SpecularEnd), + + WAVESET_FLOAT(WaterHeight), + + WAVESET_OBJ(WaterOffset), + WAVESET_FLOAT(OpacOffset), + WAVESET_FLOAT(ReflOffset), + WAVESET_FLOAT(WaveOffset), + + WAVESET_OBJ(DepthFalloff), + WAVESET_FLOAT(OpacFalloff), + WAVESET_FLOAT(ReflFalloff), + WAVESET_FLOAT(WaveFalloff), + + WAVESET_OBJ(MaxAtten), + WAVESET_OBJ(MinAtten), + + WAVESET_OBJ(WaterTint), + WAVESET_FLOAT(WaterOpacity), + WAVESET_OBJ(SpecularTint), + WAVESET_FLOAT(SpecularMute), + + WAVESET_OBJ(EnvCenter), + WAVESET_FLOAT(EnvRadius), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptWaveSet, "Params:key\nCreates a new ptWaveSet"); + +// required functions for PyObject interoperability +PyObject *pyWaveSet::New(plKey key) +{ + ptWaveSet *newObj = (ptWaveSet*)ptWaveSet_type.tp_new(&ptWaveSet_type, NULL, NULL); + newObj->fThis->fWaterKey = key; + return (PyObject*)newObj; +} + +PyObject *pyWaveSet::New(pyKey &key) +{ + ptWaveSet *newObj = (ptWaveSet*)ptWaveSet_type.tp_new(&ptWaveSet_type, NULL, NULL); + newObj->fThis->fWaterKey = key.getKey(); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptWaveSet, pyWaveSet) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptWaveSet, pyWaveSet) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyWaveSet::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptWaveSet); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp new file mode 100644 index 00000000..b5fcfadf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.cpp @@ -0,0 +1,630 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsSTLStream.h" +#include "hsResMgr.h" +#include "plgDispatch.h" +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" +#include "../pnAsyncCore/pnAsyncCore.h" +#include "../pnNetCli/pnNetCli.h" +#include "../plNetGameLib/plNetGameLib.h" +#include "../plFile/plFileUtils.h" +#include "../plFile/plStreamSource.h" +#include "../plNetCommon/plNetCommon.h" +#include "../plProgressMgr/plProgressMgr.h" +#include "../plMessage/plPreloaderMsg.h" +#include "../plMessage/plNetCommMsgs.h" +#include "pfSecurePreloader.h" + +#include "../plNetClientComm/plNetClientComm.h" + +extern hsBool gDataServerLocal; + + +// Max number of concurrent file downloads +static const unsigned kMaxConcurrency = 1; + +pfSecurePreloader * pfSecurePreloader::fInstance; + +/////////////////////////////////////////////////////////////////////////////// +// Callback routines for the network code + +// Called when a file's info is retrieved from the server +static void DefaultFileListRequestCallback(ENetError result, void* param, const NetCliAuthFileInfo infoArr[], unsigned infoCount) +{ + bool success = !IS_NET_ERROR(result); + + std::vector filenames; + std::vector sizes; + if (success) + { + filenames.reserve(infoCount); + sizes.reserve(infoCount); + for (unsigned curFile = 0; curFile < infoCount; curFile++) + { + filenames.push_back(infoArr[curFile].filename); + sizes.push_back(infoArr[curFile].filesize); + } + } + ((pfSecurePreloader*)param)->RequestFinished(filenames, sizes, success); +} + +// Called when a file download is either finished, or failed +static void DefaultFileRequestCallback(ENetError result, void* param, const wchar filename[], hsStream* stream) +{ + // Retry download unless shutting down or file not found + switch (result) { + case kNetSuccess: + ((pfSecurePreloader*)param)->FinishedDownload(filename, true); + break; + + case kNetErrFileNotFound: + case kNetErrRemoteShutdown: + ((pfSecurePreloader*)param)->FinishedDownload(filename, false); + break; + + default: + stream->Rewind(); + NetCliAuthFileRequest( + filename, + stream, + &DefaultFileRequestCallback, + param + ); + break; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// Our custom stream for writing directly to disk securely, and updating the +// progress bar. Does NOT support reading (cause it doesn't need to) +class Direct2DiskStream : public hsUNIXStream +{ +protected: + wchar * fWriteFileName; + + pfSecurePreloader* fPreloader; + +public: + Direct2DiskStream(pfSecurePreloader* preloader); + ~Direct2DiskStream(); + + virtual hsBool Open(const char* name, const char* mode = "wb"); + virtual hsBool Open(const wchar* name, const wchar* mode = L"wb"); + virtual hsBool Close(); + virtual UInt32 Read(UInt32 byteCount, void* buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); +}; + + +Direct2DiskStream::Direct2DiskStream(pfSecurePreloader* preloader) : +fWriteFileName(nil), +fPreloader(preloader) +{} + +Direct2DiskStream::~Direct2DiskStream() +{ + Close(); +} + +hsBool Direct2DiskStream::Open(const char* name, const char* mode) +{ + wchar* wName = hsStringToWString(name); + wchar* wMode = hsStringToWString(mode); + hsBool ret = Open(wName, wMode); + delete [] wName; + delete [] wMode; + return ret; +} + +hsBool Direct2DiskStream::Open(const wchar* name, const wchar* mode) +{ + if (0 != wcscmp(mode, L"wb")) { + hsAssert(0, "Unsupported open mode"); + return false; + } + + fWriteFileName = TRACKED_NEW(wchar[wcslen(name) + 1]); + wcscpy(fWriteFileName, name); + +// LogMsg(kLogPerf, L"Opening disk file %S", fWriteFileName); + return hsUNIXStream::Open(name, mode); +} + +hsBool Direct2DiskStream::Close() +{ + delete [] fWriteFileName; + fWriteFileName = nil; + return hsUNIXStream::Close(); +} + +UInt32 Direct2DiskStream::Read(UInt32 bytes, void* buffer) +{ + hsAssert(0, "not implemented"); + return 0; // we don't read +} + +UInt32 Direct2DiskStream::Write(UInt32 bytes, const void* buffer) +{ +// LogMsg(kLogPerf, L"Writing %u bytes to disk file %S", bytes, fWriteFileName); + fPreloader->UpdateProgressBar(bytes); + return hsUNIXStream::Write(bytes, buffer); +} + + +/////////////////////////////////////////////////////////////////////////////// +// secure preloader class implementation + +// closes and deletes all streams +void pfSecurePreloader::ICleanupStreams() +{ + if (fD2DStreams.size() > 0) + { + std::map::iterator curStream; + for (curStream = fD2DStreams.begin(); curStream != fD2DStreams.end(); curStream++) + { + curStream->second->Close(); + delete curStream->second; + curStream->second = nil; + } + fD2DStreams.clear(); + } +} + +// queues a single file to be preloaded (does nothing if already preloaded) +void pfSecurePreloader::RequestSingleFile(std::wstring filename) +{ + fileRequest request; + ZERO(request); + request.fType = fileRequest::kSingleFile; + request.fPath = filename; + request.fExt = L""; + + fRequests.push_back(request); +} + +// queues a group of files to be preloaded (does nothing if already preloaded) +void pfSecurePreloader::RequestFileGroup(std::wstring dir, std::wstring ext) +{ + fileRequest request; + ZERO(request); + request.fType = fileRequest::kFileList; + request.fPath = dir; + request.fExt = ext; + + fRequests.push_back(request); +} + +// preloads all requested files from the server (does nothing if already preloaded) +void pfSecurePreloader::Start() +{ + if (gDataServerLocal) { + // using local data, don't do anything + plPreloaderMsg * msg = TRACKED_NEW plPreloaderMsg(); + msg->fSuccess = true; + msg->Send(); + return; + } + + NetCliAuthGetEncryptionKey(fEncryptionKey, 4); // grab the encryption key from the server + + fNetError = false; + + // make sure we are all cleaned up + ICleanupStreams(); + fTotalDataReceived = 0; + + // update the progress bar for downloading + if (!fProgressBar) + fProgressBar = plProgressMgr::GetInstance()->RegisterOperation((hsScalar)(fRequests.size()), "Getting file info...", plProgressMgr::kUpdateText, false, true); + + for (unsigned curRequest = 0; curRequest < fRequests.size(); curRequest++) + { + fNumInfoRequestsRemaining++; // increment the counter + if (fRequests[curRequest].fType == fileRequest::kSingleFile) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // in internal releases, we can use on-disk files if they exist + if (plFileUtils::FileExists(fRequests[curRequest].fPath.c_str())) + { + fileInfo info; + info.fOriginalNameAndPath = fRequests[curRequest].fPath; + info.fSizeInBytes = plFileUtils::GetFileSize(info.fOriginalNameAndPath.c_str()); + info.fDownloading = false; + info.fDownloaded = false; + info.fLocal = true; + + // generate garbled name + wchar_t pathBuffer[MAX_PATH + 1]; + wchar_t filename[arrsize(pathBuffer)]; + GetTempPathW(arrsize(pathBuffer), pathBuffer); + GetTempFileNameW(pathBuffer, L"CYN", 0, filename); + info.fGarbledNameAndPath = filename; + + fTotalDataDownload += info.fSizeInBytes; + + fFileInfoMap[info.fOriginalNameAndPath] = info; + } + // internal client will still request it, even if it exists locally, + // so that things get updated properly +#endif // PLASMA_EXTERNAL_RELEASE + NetCliAuthFileListRequest( + fRequests[curRequest].fPath.c_str(), + nil, + &DefaultFileListRequestCallback, + (void*)this + ); + } + else + { +#ifndef PLASMA_EXTERNAL_RELEASE + // in internal releases, we can use on-disk files if they exist + // Build the search string as "dir\\*.ext" + wchar searchStr[MAX_PATH]; + + PathAddFilename(searchStr, fRequests[curRequest].fPath.c_str(), L"*", arrsize(searchStr)); + PathSetExtension(searchStr, searchStr, fRequests[curRequest].fExt.c_str(), arrsize(searchStr)); + + ARRAY(PathFind) paths; + PathFindFiles(&paths, searchStr, kPathFlagFile); // find all files that match + + // convert it to our little file info array + PathFind* curFile = paths.Ptr(); + PathFind* lastFile = paths.Term(); + while (curFile != lastFile) { + fileInfo info; + info.fOriginalNameAndPath = curFile->name; + info.fSizeInBytes = (UInt32)curFile->fileLength; + info.fDownloading = false; + info.fDownloaded = false; + info.fLocal = true; + + // generate garbled name + wchar_t pathBuffer[MAX_PATH + 1]; + wchar_t filename[arrsize(pathBuffer)]; + GetTempPathW(arrsize(pathBuffer), pathBuffer); + GetTempFileNameW(pathBuffer, L"CYN", 0, filename); + info.fGarbledNameAndPath = filename; + + fTotalDataDownload += info.fSizeInBytes; + + fFileInfoMap[info.fOriginalNameAndPath] = info; + curFile++; + } +#endif // PLASMA_EXTERNAL_RELEASE + + NetCliAuthFileListRequest( + fRequests[curRequest].fPath.c_str(), + fRequests[curRequest].fExt.c_str(), + &DefaultFileListRequestCallback, + (void*)this + ); + } + } +} + +// closes all file pointers and cleans up after itself +void pfSecurePreloader::Cleanup() +{ + ICleanupStreams(); + + fRequests.clear(); + fFileInfoMap.clear(); + + fNumInfoRequestsRemaining = 0; + fTotalDataDownload = 0; + fTotalDataReceived = 0; + + DEL(fProgressBar); + fProgressBar = nil; +} + +//============================================================================ +void pfSecurePreloader::RequestFinished(const std::vector & filenames, const std::vector & sizes, bool succeeded) +{ + fNetError |= !succeeded; + + if (succeeded) + { + unsigned count = 0; + for (int curFile = 0; curFile < filenames.size(); curFile++) + { + if (fFileInfoMap.find(filenames[curFile]) != fFileInfoMap.end()) + continue; // if it is a duplicate, ignore it (the duplicate is probably one we found locally) + + fileInfo info; + info.fOriginalNameAndPath = filenames[curFile]; + info.fSizeInBytes = sizes[curFile]; + info.fDownloading = false; + info.fDownloaded = false; + info.fLocal = false; // if we get here, it was retrieved remotely + + // generate garbled name + wchar_t pathBuffer[MAX_PATH + 1]; + wchar_t filename[arrsize(pathBuffer)]; + GetTempPathW(arrsize(pathBuffer), pathBuffer); + GetTempFileNameW(pathBuffer, L"CYN", 0, filename); + info.fGarbledNameAndPath = filename; + + fTotalDataDownload += info.fSizeInBytes; + + fFileInfoMap[info.fOriginalNameAndPath] = info; + ++count; + } + LogMsg(kLogPerf, "Added %u files to secure download queue", count); + } + if (fProgressBar) + fProgressBar->Increment(1.f); + + --fNumInfoRequestsRemaining; // even if we fail, decrement the counter + + if (succeeded) { + DEL(fProgressBar); + fProgressBar = plProgressMgr::GetInstance()->RegisterOperation((hsScalar)(fTotalDataDownload), "Downloading...", plProgressMgr::kUpdateText, false, true); + + // Issue some file download requests (up to kMaxConcurrency) + IIssueDownloadRequests(); + } + else { + IPreloadComplete(); + } +} + +//============================================================================ +void pfSecurePreloader::IIssueDownloadRequests () { + + std::map::iterator curFile; + for (curFile = fFileInfoMap.begin(); curFile != fFileInfoMap.end(); curFile++) + { + // Skip files already downloaded or currently downloading + if (curFile->second.fDownloaded || curFile->second.fDownloading) + continue; + + std::wstring filename = curFile->second.fOriginalNameAndPath; +#ifndef PLASMA_EXTERNAL_RELEASE + // in internal releases, we can use on-disk files if they exist + if (plFileUtils::FileExists(filename.c_str())) + { + // don't bother streaming, just make the secure stream using the local file + + // a local key overrides the server-downloaded key + UInt32 localKey[4]; + bool hasLocalKey = plFileUtils::GetSecureEncryptionKey(filename.c_str(), localKey, arrsize(localKey)); + hsStream* stream = nil; + if (hasLocalKey) + stream = plSecureStream::OpenSecureFile(filename.c_str(), 0, localKey); + else + stream = plSecureStream::OpenSecureFile(filename.c_str(), 0, fEncryptionKey); + + // add it to the stream source + bool added = plStreamSource::GetInstance()->InsertFile(filename.c_str(), stream); + if (!added) + DEL(stream); // wasn't added, so nuke our local copy + + // and make sure the vars are set up right + curFile->second.fDownloaded = true; + curFile->second.fLocal = true; + } + else +#endif + { + // Enforce concurrency limit + if (fNumDownloadRequestsRemaining >= kMaxConcurrency) + break; + + curFile->second.fDownloading = true; + curFile->second.fDownloaded = false; + curFile->second.fLocal = false; + + // create and setup the stream + Direct2DiskStream* fileStream = TRACKED_NEW Direct2DiskStream(this); + fileStream->Open(curFile->second.fGarbledNameAndPath.c_str(), L"wb"); + fD2DStreams[filename] = (hsStream*)fileStream; + + // request the file from the server + LogMsg(kLogPerf, L"Requesting secure file:%s", filename.c_str()); + ++fNumDownloadRequestsRemaining; + NetCliAuthFileRequest( + filename.c_str(), + (hsStream*)fileStream, + &DefaultFileRequestCallback, + this + ); + } + } + + if (!fNumDownloadRequestsRemaining) + IPreloadComplete(); +} + +void pfSecurePreloader::UpdateProgressBar(UInt32 bytesReceived) +{ + fTotalDataReceived += bytesReceived; + if (fTotalDataReceived > fTotalDataDownload) + fTotalDataReceived = fTotalDataDownload; // shouldn't happen... but just in case + + if (fProgressBar) + fProgressBar->Increment((hsScalar)bytesReceived); +} + +void pfSecurePreloader::FinishedDownload(std::wstring filename, bool succeeded) +{ + for (;;) + { + if (fFileInfoMap.find(filename) == fFileInfoMap.end()) + { + // file doesn't exist... abort + succeeded = false; + break; + } + + fFileInfoMap[filename].fDownloading = false; + + // close and delete the writer stream (even if we failed) + fD2DStreams[filename]->Close(); + delete fD2DStreams[filename]; + fD2DStreams.erase(fD2DStreams.find(filename)); + + if (succeeded) + { + // open a secure stream to that file + hsStream* stream = plSecureStream::OpenSecureFile( + fFileInfoMap[filename].fGarbledNameAndPath.c_str(), + plSecureStream::kRequireEncryption | plSecureStream::kDeleteOnExit, // force delete and encryption + fEncryptionKey + ); + + bool addedToSource = plStreamSource::GetInstance()->InsertFile(filename.c_str(), stream); + if (!addedToSource) + DEL(stream); // cleanup if it wasn't added + + fFileInfoMap[filename].fDownloaded = true; + break; + } + + // file download failed, clean up after it + + // delete the temporary file + if (plFileUtils::FileExists(fFileInfoMap[filename].fGarbledNameAndPath.c_str())) + plFileUtils::RemoveFile(fFileInfoMap[filename].fGarbledNameAndPath.c_str(), true); + + // and remove it from the info map + fFileInfoMap.erase(fFileInfoMap.find(filename)); + break; + } + + fNetError |= !succeeded; + --fNumDownloadRequestsRemaining; + LogMsg(kLogPerf, L"Received secure file:%s, success:%s", filename.c_str(), succeeded ? L"Yep" : L"Nope"); + + if (!succeeded) + IPreloadComplete(); + else + // Issue some file download requests (up to kMaxConcurrency) + IIssueDownloadRequests(); +} + +//============================================================================ +void pfSecurePreloader::INotifyAuthReconnected () { + + // The secure file download network protocol will now just pick up downloading + // where it left off before the reconnect, so no need to reset in-progress files. + + /* + std::map::iterator curFile; + for (curFile = fFileInfoMap.begin(); curFile != fFileInfoMap.end(); curFile++) { + + // Reset files that were currently downloading + if (curFile->second.fDownloading) + curFile->second.fDownloading = false; + } + + if (fNumDownloadRequestsRemaining > 0) { + + LogMsg(kLogPerf, L"pfSecurePreloader: Auth reconnected, resetting in-progress file downloads"); + + // Issue some file download requests (up to kMaxConcurrency) + IIssueDownloadRequests(); + } + */ +} + +//============================================================================ +void pfSecurePreloader::IPreloadComplete () { + DEL(fProgressBar); + fProgressBar = nil; + + plPreloaderMsg * msg = TRACKED_NEW plPreloaderMsg(); + msg->fSuccess = !fNetError; + msg->Send(); +} + +//============================================================================ +hsBool pfSecurePreloader::MsgReceive (plMessage * msg) { + + if (plNetCommAuthConnectedMsg * authMsg = plNetCommAuthConnectedMsg::ConvertNoRef(msg)) { + + INotifyAuthReconnected(); + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + +//============================================================================ +pfSecurePreloader * pfSecurePreloader::GetInstance () { + + if (!fInstance) { + + fInstance = NEWZERO(pfSecurePreloader); + fInstance->RegisterAs(kSecurePreloader_KEY); + } + + return fInstance; +} + +//============================================================================ +bool pfSecurePreloader::IsInstanced () { + + return fInstance != nil; +} + +//============================================================================ +void pfSecurePreloader::Init () { + + if (!fInitialized) { + + fInitialized = true; + plgDispatch::Dispatch()->RegisterForExactType(plNetCommAuthConnectedMsg::Index(), GetKey()); + } +} + +//============================================================================ +void pfSecurePreloader::Shutdown () { + + if (fInitialized) { + + fInitialized = false; + plgDispatch::Dispatch()->UnRegisterForExactType(plNetCommAuthConnectedMsg::Index(), GetKey()); + } + + if (fInstance) { + + fInstance->UnRegister(); + fInstance = nil; + } +} + +//============================================================================ +pfSecurePreloader::pfSecurePreloader () { +} + +//============================================================================ +pfSecurePreloader::~pfSecurePreloader () { + + Cleanup(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.h new file mode 100644 index 00000000..c7ee104a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloader.h @@ -0,0 +1,118 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __pfSecurePreloader_h__ +#define __pfSecurePreloader_h__ + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsCritSect.h" +#include "hsStream.h" +#include "../plFile/plSecureStream.h" +#include "../pnKeyedObject/hsKeyedObject.h" + + +class plOperationProgress; + +/////////////////////////////////////////////////////////////////////////////// +// pfSecurePreloader - a class for handling files we want downloaded from the +// server into a temporary directory, secured, and deleted on exit. Puts stuff +// into plStreamSource for us +/////////////////////////////////////////////////////////////////////////////// +class pfSecurePreloader : public hsKeyedObject +{ +private: + static pfSecurePreloader * fInstance; + + struct fileRequest + { + enum requestType {kSingleFile, kFileList}; + requestType fType; + std::wstring fPath; // filename if kSingleFile, path if kFileList + std::wstring fExt; // blank if kSingleFile, extension if kFileList + }; + std::vector fRequests; + + struct fileInfo + { + std::wstring fOriginalNameAndPath; // the human-readable name + std::wstring fGarbledNameAndPath; // the garbled temp name of the file on disk + UInt32 fSizeInBytes; // the total size of the file + bool fDownloading; // is this file currently downloading? + bool fDownloaded; // is this file completely downloaded? + bool fLocal; // is the file a local copy? + }; + std::map fFileInfoMap; // key is human-readable name + std::map fD2DStreams; // direct-to-disk streams, only used while downloading from the server + + UInt32 fNumInfoRequestsRemaining; // the number of file info requests that are still pending + UInt32 fNumDownloadRequestsRemaining; // the number of file download requests that are still pending + UInt32 fTotalDataDownload; // the amount of data we need to download, for progress bar tracking + UInt32 fTotalDataReceived; // the amount of data we have already preloaded, for progress bar tracking + bool fNetError; + bool fInitialized; + + UInt32 fEncryptionKey[4]; // encryption key for all the secure files + + plOperationProgress* fProgressBar; + + void IIssueDownloadRequests (); + void IPreloadComplete (); + + void ICleanupStreams(); // closes and deletes all streams + + void INotifyAuthReconnected (); + + pfSecurePreloader (); + +public: + CLASSNAME_REGISTER(pfSecurePreloader); + GETINTERFACE_ANY(pfSecurePreloader, hsKeyedObject); + + ~pfSecurePreloader (); + + void Init (); + void Shutdown (); + + // Client interface functions + void RequestSingleFile(std::wstring filename); // queues a single file to be preloaded (does nothing if already preloaded) + void RequestFileGroup(std::wstring dir, std::wstring ext); // queues a group of files to be preloaded (does nothing if already preloaded) + void Start(); // sends all queued requests (does nothing if already preloaded) + void Cleanup(); // closes all file pointers and cleans up after itself + + // Functions for the network callbacks + void RequestFinished(const std::vector & filenames, const std::vector & sizes, bool succeeded); + void UpdateProgressBar(UInt32 bytesReceived); + void FinishedDownload(std::wstring filename, bool succeeded); + + // Instance handling + static pfSecurePreloader * GetInstance (); + static bool IsInstanced (); + + // hsKeyedObject + hsBool MsgReceive (plMessage * msg); +}; + +#endif // __pfSecurePreloader_h__ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h new file mode 100644 index 00000000..012055d2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/FeatureLib/pfSecurePreloader/pfSecurePreloaderCreatable.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_FEATURELIB_PFSECUREPRELOADER_PFSECUREPRELOADERCREATABLE_H +#define PLASMA20_SOURCES_PLASMA_FEATURELIB_PFSECUREPRELOADER_PFSECUREPRELOADERCREATABLE_H + +#include "../pnFactory/plCreator.h" + +#include "pfSecurePreloader.h" +REGISTER_NONCREATABLE(pfSecurePreloader); + + +#endif // PLASMA20_SOURCES_PLASMA_FEATURELIB_PFSECUREPRELOADER_PFSECUREPRELOADERCREATABLE_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfArray.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfArray.h new file mode 100644 index 00000000..4fa132e2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfArray.h @@ -0,0 +1,153 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _DEV_ARRAY_H +#define _DEV_ARRAY_H + + +namespace dev +{ + + +/** Very simple dynamic array. */ +template class Array +{ +public: + /** Creates an empty array. */ + Array() : + m_data(0), m_len(0), m_cap(0) + { + } + + /** Creates an array of specified size. */ + explicit Array( int size ) : + m_data(0), m_len(0), m_cap(0) + { + setSize( size ); + } + + /// + ~Array() + { + delete[] m_data; + } + + /** Appends an item at the end of the array. */ + void add( const T& item ) + { + if ( m_len+1 > m_cap ) + setCapacity( m_len + 1 ); + m_data[m_len++] = item; + } + + /** Resizes the array. */ + void setSize( int size ) + { + if ( size > m_cap ) + setCapacity( size ); + m_len = size; + } + + /** Returns ith item. */ + T& operator[]( int i ) + { + return m_data[i]; + } + + /** Returns pointer to the first element in the vector. */ + T* begin() + { + return m_data; + } + + /** Returns pointer to one beyond the last element in the vector. */ + T* end() + { + return m_data + m_len; + } + + /** Returns number of items in the array. */ + int size() const + { + return m_len; + } + + /** Returns ith item. */ + const T& operator[]( int i ) const + { + return m_data[i]; + } + + /** Returns pointer to the first element in the vector. */ + const T* begin() const + { + return m_data; + } + + /** Returns pointer to one beyond the last element in the vector. */ + const T* end() const + { + return m_data + m_len; + } + +private: + T* m_data; + int m_len; + int m_cap; + + void setCapacity( int cap ) + { + ++cap; + if ( cap < 8 ) + cap = 8; + else if ( cap < m_cap*2 ) + cap = m_cap*2; + m_cap = cap; + + T* data = TRACKED_NEW T[cap]; + for ( int i = 0 ; i < m_len ; ++i ) + data[i] = m_data[i]; + delete[] m_data; + m_data = data; + } +}; + + +} // dev + + +#endif // _DEV_ARRAY_H +/* + * Copyright (c) 2001 Jani Kajala + * + * Permission to use, copy, modify, distribute and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. + * Jani Kajala makes no representations about the suitability + * of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFile.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFile.cpp new file mode 100644 index 00000000..fbea958e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFile.cpp @@ -0,0 +1,362 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "pfMapFile.h" +#include "pfMapFileEntry.h" +#include "pfTextFile.h" +#include "pfArray.h" +#include +#include +#include +#ifdef WIN32 +#include +#endif + +//----------------------------------------------------------------------------- + +namespace dev +{ + + +class MapFile::MapFileImpl +{ +public: + long loadAddr; + char name[256]; + Array segments; + Array entries; + + MapFileImpl( const char* filename ) : + loadAddr(0), m_file( filename ), m_err( MapFile::ERROR_NONE ) + { + m_file.readString( name, sizeof(name) ); + + char buf[1024]; + while ( m_file.readString(buf,sizeof(buf)) ) + { + if ( !strcmp("Preferred",buf) ) + parseLoadAddress(); + else if ( !strcmp("Start",buf) ) + parseSegments(); + else if ( !strcmp("Address",buf) ) + parseEntries(); + else + m_file.skipLine(); + } + + std::sort( segments.begin(), segments.end() ); + std::sort( entries.begin(), entries.end() ); + } + + ~MapFileImpl() + { + } + + ErrorType error() const + { + if ( m_err != MapFile::ERROR_NONE ) + return m_err; + + switch ( m_file.error() ) + { + case TextFile::ERROR_OPEN: return MapFile::ERROR_OPEN; + case TextFile::ERROR_READ: return MapFile::ERROR_READ; + case TextFile::ERROR_PARSE: return MapFile::ERROR_PARSE; + default: return MapFile::ERROR_NONE; + } + } + + int line() const + { + if ( m_err != MapFile::ERROR_NONE ) + return m_errLine; + + return m_file.line(); + } + +private: + TextFile m_file; + MapFile::ErrorType m_err; + int m_errLine; + + /** + * Returns true if the next line is empty. + */ + bool nextLineEmpty() + { + m_file.skipLine(); + char ch; + while ( m_file.peekChar(&ch) && isspace(ch) && ch != '\n' ) + m_file.readChar( &ch ); + if ( m_file.peekChar(&ch) && ch == '\n' ) + return true; + return false; + } + + /** + * Parses specified string. + * Sets error if parsed string doesnt match. + */ + void parse( const char* str ) + { + char buf[256]; + m_file.readString( buf, sizeof(buf) ); + if ( strcmp(str,buf) ) + { + m_err = MapFile::ERROR_PARSE; + m_errLine = m_file.line(); + } + } + + /** + * Parses specified character. + * Sets error if parsed character doesnt match. + */ + void parse( char ch ) + { + char ch2; + if ( !m_file.readChar(&ch2) || ch2 != ch ) + { + m_err = MapFile::ERROR_PARSE; + m_errLine = m_file.line(); + } + } + + /** + * Example: + * (Preferred) load address is 00400000 + */ + void parseLoadAddress() + { + parse( "load" ); parse( "address" ); parse( "is" ); + loadAddr = m_file.readHex(); + } + + /** + * Example: + * (Start) Length Name Class + * 0001:00000000 00002c05H .text CODE + */ + void parseSegments() + { + parse( "Length" ); + parse( "Name" ); + parse( "Class" ); + m_file.skipWhitespace(); + + while ( !error() ) + { + int seg = m_file.readHex(); + parse( ':' ); + int offs = m_file.readHex(); + int len = m_file.readHex(); + parse( 'H' ); + char buf[256]; + m_file.readString( buf, sizeof(buf) ); + segments.add( MapFileEntry(seg,offs,len,buf) ); + + // break at empty line + if ( nextLineEmpty() ) + break; + } + } + + /** + * Example: + * (Address) Publics by Value Rva+Base Lib:Object + * 0001:000001a0 ?stackTrace@@YAXXZ 004011a0 f main.obj + */ + void parseEntries() + { + parse( "Publics" ); parse( "by" ); parse( "Value" ); + parse( "Rva+Base" ); + parse( "Lib:Object" ); + m_file.skipWhitespace(); + + while ( !error() ) + { + int seg = m_file.readHex(); + parse( ':' ); + int offs = m_file.readHex(); + char buf[256]; + m_file.readString( buf, sizeof(buf) ); + char* entryname = buf; + + // chop entry name at @@ + char* end = strstr( entryname, "@@" ); + if ( end ) + *end = 0; + // skip preceding ?01.. + while ( isdigit(*entryname) || *entryname == '?' || *entryname == '$' ) + ++entryname; + // conv @ -> . + for ( char* str = entryname ; *str ; ++str ) + if ( *str == '@' ) + *str = '.'; + + // Added 9.5.02 mcn - Reverse the order of the symbols to be more natural + if( strlen( entryname ) < 512 ) + { + static char newName[ 512 ]; + char *search; + newName[ 0 ] = 0; + while( ( search = strrchr( entryname, '.' ) ) != 0 ) + { + *search = 0; + if( newName[ 0 ] != 0 ) + strcat( newName, "::" ); + strcat( newName, search + 1 ); + } + if( newName[ 0 ] != 0 ) + strcat( newName, "::" ); + strcat( newName, entryname ); + + entryname = newName; + } + + + entries.add( MapFileEntry(seg,offs,0,entryname) ); + + // break at empty line + if ( nextLineEmpty() ) + break; + } + } +}; + +//----------------------------------------------------------------------------- + +MapFile::MapFile( const char* filename ) +{ + m_this = TRACKED_NEW MapFileImpl( filename ); +} + +MapFile::~MapFile() +{ + delete m_this; +} + +long MapFile::loadAddress() const +{ + return m_this->loadAddr; +} + +const MapFileEntry& MapFile::getSegment( int i ) const +{ + return m_this->segments[i]; +} + +const MapFileEntry& MapFile::getEntry( int i ) const +{ + return m_this->entries[i]; +} + +int MapFile::segments() const +{ + return m_this->segments.size(); +} + +int MapFile::entries() const +{ + return m_this->entries.size(); +} + +MapFile::ErrorType MapFile::error() const +{ + return m_this->error(); +} + +int MapFile::line() const +{ + return m_this->line(); +} + +int MapFile::findEntry( long addr ) const +{ + for ( int j = 0 ; j < segments() ; ++j ) + { + const MapFileEntry& segment = getSegment( j ); + long section = segment.section(); + long segmentBegin = loadAddress() + (segment.section() << 12) + segment.offset(); + long segmentEnd = segmentBegin + segment.length(); + + if ( addr >= segmentBegin && addr < segmentEnd ) + { + for ( int i = entries()-1 ; i >= 0 ; --i ) + { + const MapFileEntry entry = getEntry( i ); + if ( entry.section() == section ) + { + long entryAddr = loadAddress() + (entry.section() << 12) + entry.offset(); + if ( entryAddr <= addr ) + return i; + } + } + } + } + return -1; +} + +void MapFile::getModuleMapFilename( char* buffer, int bufferSize ) +{ + int len = 0; + buffer[len] = 0; + +#ifdef WIN32 + // get name of the exe/dll + len = GetModuleFileName( GetModuleHandle(0), buffer, bufferSize-1 ); + buffer[len] = 0; +#endif + + // remove .exe or .dll extension + if ( len > 3 && + (!strcmp(buffer+len-4,".exe") || !strcmp(buffer+len-4,".EXE") || + !strcmp(buffer+len-4,".DLL") || !strcmp(buffer+len-4,".dll")) ) + { + buffer[len-4] = 0; + } + + // append .map extension + if ( (int)strlen(buffer)+4 < bufferSize ) + { + strcat( buffer, ".map" ); + } +} + + +} // dev +/* + * Copyright (c) 2001 Jani Kajala + * + * Permission to use, copy, modify, distribute and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. + * Jani Kajala makes no representations about the suitability + * of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFile.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFile.h new file mode 100644 index 00000000..d547ddc5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFile.h @@ -0,0 +1,119 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _DEV_MAPFILE_H +#define _DEV_MAPFILE_H + + +namespace dev +{ + + +class MapFileEntry; + + +/** + * Linker generated module map file parser. + */ +class MapFile +{ +public: + /** Error code. */ + enum ErrorType + { + /** No error. */ + ERROR_NONE, + /** File open failed. */ + ERROR_OPEN, + /** File reading failed. */ + ERROR_READ, + /** Syntax error. */ + ERROR_PARSE + }; + + /** Reads a map file. */ + explicit MapFile( const char* filename ); + + /// + ~MapFile(); + + /** Returns preferred load address. */ + long loadAddress() const; + + /** Returns ith entry from the map file. */ + const MapFileEntry& getEntry( int i ) const; + + /** Returns ith segment from the map file. */ + const MapFileEntry& getSegment( int i ) const; + + /** Returns number of segments in the map file. */ + int segments() const; + + /** Returns number of entries in the map file. */ + int entries() const; + + /** Returns error code or 0 (ERROR_NONE) if no error. */ + ErrorType error() const; + + /** Returns line number of last successful read character. */ + int line() const; + + /** + * Finds entry which contains specified address. + * @return Entry index or -1 if not found. + */ + int findEntry( long addr ) const; + + /** + * Returns current module name, with map extension. + * The output buffer is always 0-terminated. + */ + static void getModuleMapFilename( char* buffer, int bufferSize ); + +private: + class MapFileImpl; + MapFileImpl* m_this; + + MapFile( const MapFile& ); + MapFile& operator=( const MapFile& ); +}; + + +} // dev + + +#endif // _DEV_MAPFILE_H +/* + * Copyright (c) 2001 Jani Kajala + * + * Permission to use, copy, modify, distribute and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. + * Jani Kajala makes no representations about the suitability + * of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFileEntry.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFileEntry.cpp new file mode 100644 index 00000000..48a14271 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFileEntry.cpp @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pfMapFileEntry.h" +#include + +//----------------------------------------------------------------------------- + +namespace dev +{ + + +MapFileEntry::MapFileEntry() +{ + m_sec = 0; + m_addr = 0; + m_len = 0; + m_name[0] = 0; +} + +MapFileEntry::MapFileEntry( long section, long offset, long length, const char* name ) +{ + m_sec = section; + m_addr = offset; + m_len = length; + + strncpy( m_name, name, MAX_NAME ); + m_name[MAX_NAME] = 0; +} + +long MapFileEntry::section() const +{ + return m_sec; +} + +long MapFileEntry::offset() const +{ + return m_addr; +} + +long MapFileEntry::length() const +{ + return m_len; +} + +const char* MapFileEntry::name() const +{ + return m_name; +} + +bool MapFileEntry::operator<( const MapFileEntry& other ) const +{ + if ( m_sec < other.m_sec ) + return true; + if ( m_sec > other.m_sec ) + return false; + return m_addr < other.m_addr; +} + + +} // dev +/* + * Copyright (c) 2001 Jani Kajala + * + * Permission to use, copy, modify, distribute and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. + * Jani Kajala makes no representations about the suitability + * of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFileEntry.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFileEntry.h new file mode 100644 index 00000000..25eb9cc1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfMapFileEntry.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _DEV_MAPFILEENTRY_H +#define _DEV_MAPFILEENTRY_H + + +namespace dev +{ + + +/** An entry in the map file. */ +class MapFileEntry +{ +public: + /** Class constants. */ + enum Constants + { + /** Maximum number of characters in map file entry name. */ + MAX_NAME = 256 + }; + + /// + MapFileEntry(); + + /** Creates an entry with specified section, offset, length and name. */ + MapFileEntry( long section, long offset, long length, const char* name ); + + /** Returns section of the entry. */ + long section() const; + + /** Returns offset of the entry. */ + long offset() const; + + /** Returns length of the entry (only defined for segments). */ + long length() const; + + /** Returns name of the entry. */ + const char* name() const; + + /** Returns true if the offset of this entry is before the other one. */ + bool operator<( const MapFileEntry& other ) const; + +private: + long m_sec; + long m_addr; + long m_len; + char m_name[MAX_NAME+1]; +}; + + +} // dev + + +#endif // _DEV_MAPFILEENTRY_H +/* + * Copyright (c) 2001 Jani Kajala + * + * Permission to use, copy, modify, distribute and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. + * Jani Kajala makes no representations about the suitability + * of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfPrintStackTrace.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfPrintStackTrace.cpp new file mode 100644 index 00000000..8fa7c161 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfPrintStackTrace.cpp @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pfPrintStackTrace.h" +#include "pfStackTrace.h" +#include "pfMapFile.h" +#include +#include + +#pragma optimize( "y", off ) + +//----------------------------------------------------------------------------- + +using namespace dev; + +//----------------------------------------------------------------------------- + +/** + * Prints stack trace to user defined buffer. + * Always terminates the buffer with 0. + */ +void printStackTrace( char* buffer, int bufferSize, unsigned long stackPtr, unsigned long opPtr ) +{ + // find out map file name + char modname[500]; + MapFile::getModuleMapFilename( modname, sizeof(modname) ); + + // parse map file + static char buf[5000]; + MapFile map( modname ); + switch ( map.error() ) + { + case MapFile::ERROR_OPEN: sprintf( buf, "Failed to open map file %s\n", modname ); break; + case MapFile::ERROR_READ: sprintf( buf, "Error while reading map file %s(%i)\n", modname, map.line() ); break; + case MapFile::ERROR_PARSE: sprintf( buf, "Parse error in map file %s(%i)\n", modname, map.line() ); break; + default: break; + } + + // print stack trace to buffer + if ( !map.error() ) + { + MapFile* maps[] = {&map}; + StackTrace::printStackTrace( maps, 1, 1, 16, buf, sizeof(buf), stackPtr, opPtr ); + } + else + { + // 9.5.2002 mcn - Even if we can't open the map file, still print out the stack trace for later reference + StackTrace::printStackTrace( 0, 0, 1, 16, buf, sizeof(buf), stackPtr, opPtr ); + } + + // copy to user buffer + if ( bufferSize > 0 ) + { + if( buffer[ 0 ] == 0 ) + strncpy( buffer, buf, bufferSize ); + else + strncat( buffer, buf, bufferSize ); + buffer[bufferSize-1] = 0; + } +} + +/* + * Copyright (c) 2001 Jani Kajala + * + * Permission to use, copy, modify, distribute and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. + * Jani Kajala makes no representations about the suitability + * of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfPrintStackTrace.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfPrintStackTrace.h new file mode 100644 index 00000000..9c2645c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfPrintStackTrace.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _PRINTSTACKTRACE_H +#define _PRINTSTACKTRACE_H + + +/** + * Prints formatted call stack to the user defined buffer, + * always terminating the buffer with 0. + * Uses stack frame to find out the caller function address and + * the map file to find out the function name. + */ +void printStackTrace( char* buffer, int bufferSize, unsigned long stackPtr = 0, unsigned long opPtr = 0 ); + + +#endif // _PRINTSTACKTRACE_H +/* + * Copyright (c) 2001 Jani Kajala + * + * Permission to use, copy, modify, distribute and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. + * Jani Kajala makes no representations about the suitability + * of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfStackTrace.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfStackTrace.cpp new file mode 100644 index 00000000..f692e7ed --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfStackTrace.cpp @@ -0,0 +1,215 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pfStackTrace.h" +#include "pfMapFile.h" +#include "pfMapFileEntry.h" +#include +#include + +#pragma optimize( "y", off ) + +//----------------------------------------------------------------------------- + +#define MAX_DEPTH 32 + +//----------------------------------------------------------------------------- + +namespace dev +{ + +static long getCallerFromStack( unsigned long stackPtr, int index ) +{ +#if /*defined(_DEBUG) && */defined(_MSC_VER) && defined(_M_IX86) + + long caller = 0; + __asm + { + mov ebx, stackPtr + mov ecx, index + inc ecx + xor eax, eax +StackTrace_getCaller_next: + mov eax, [ebx+4] + mov ebx, [ebx] + test eax,eax + jz StackTrace_getCallerFromStack_done + dec ecx + jnz StackTrace_getCaller_next +StackTrace_getCallerFromStack_done: + mov caller, eax + } + return caller; + +#else + + return 0; + +#endif +} + +static long getCaller( int index ) +{ +#if /*defined(_DEBUG) && */defined(_MSC_VER) && defined(_M_IX86) + + long caller = 0; + __asm + { + mov ebx, ebp + mov ecx, index + inc ecx + xor eax, eax +StackTrace_getCaller_next: + mov eax, [ebx+4] + mov ebx, [ebx] + test eax,eax + jz StackTrace_getCaller_done + dec ecx + jnz StackTrace_getCaller_next +StackTrace_getCaller_done: + mov caller, eax + } + return caller; + +#else + + return 0; + +#endif +} + +int StackTrace::printStackTrace( MapFile** map, int maps, + int initLevel, int maxDepth, + char* buffer, int bufferSize, unsigned long stackPtr, unsigned long opPtr ) +{ + if ( maxDepth > MAX_DEPTH ) + maxDepth = MAX_DEPTH; + bool sucks = false; + + // list callers + long callersAddr[MAX_DEPTH]; + int callers = 0; + int i; + for ( i = initLevel ; i < maxDepth ; ++i ) + { + long addr; + if( stackPtr != 0 ) + { + if( i == initLevel ) + addr = opPtr; + else + addr = getCallerFromStack( stackPtr, i - initLevel - 1 ); + } + else + addr = getCaller( i ); + callersAddr[callers++] = addr; + + // end tracing here if the entry is not in a map file + if( map != 0 ) + { + int entry = -1; + for ( int j = 0 ; j < maps ; ++j ) + { + entry = map[j]->findEntry( addr ); + if ( -1 != entry ) + break; + } + if ( -1 == entry ) + { + sucks = true; + break; + } + } + } + + + int needed = 0; + if ( bufferSize > 0 ) + *buffer = 0; + + sprintf( buffer, "Call stack (%d levels%s):\r\n", callers - initLevel, sucks ? ", truncated" : "" ); + needed = strlen( buffer ); + + // output call stack + for ( i = initLevel ; i < callers ; ++i ) + { + long addr = callersAddr[callers-i-1]; + + // find entry info + int entry = -1; + const MapFile* entryMap = 0; + for ( int j = 0 ; j < maps ; ++j ) + { + entry = map[j]->findEntry( addr ); + if ( -1 != entry ) + { + entryMap = map[j]; + break; + } + } + + // format entry to tempory buf + char buf[MapFileEntry::MAX_NAME+MAX_DEPTH+20]; // name + margin + hex number + buf[0] = 0; + for ( int k = initLevel-1 ; k < i ; ++k ) + strcat( buf, " " ); + if ( !entryMap ) + sprintf( buf+strlen(buf), "0x%08X\r\n", addr ); + else + { + const MapFileEntry &en = entryMap->getEntry( entry ); + long entryAddr = entryMap->loadAddress() + (en.section() << 12) + en.offset(); + + sprintf( buf+strlen(buf), "%s (0x%08X + 0x%08X)\r\n", en.name(), entryAddr, addr - entryAddr ); + } + + // append temporary buf to output buffer if space left + needed += strlen( buf ); + if ( needed < bufferSize ) + strcat( buffer, buf ); + } + + // terminate output buffer + if ( needed < bufferSize ) + buffer[needed] = 0; + else if ( bufferSize > 0 ) + buffer[bufferSize-1] = 0; + return needed; +} + + +} // dev +/* + * Copyright (c) 2001 Jani Kajala + * + * Permission to use, copy, modify, distribute and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. + * Jani Kajala makes no representations about the suitability + * of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfStackTrace.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfStackTrace.h new file mode 100644 index 00000000..55858c69 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfStackTrace.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _DEV_STACKTRACE_H +#define _DEV_STACKTRACE_H + + +namespace dev +{ + + +class MapFile; + + +/** Stack tracing utility. */ +class StackTrace +{ +public: + /** + * Prints formatted call stack to the user buffer. + * Always terminates the user buffer with 0. + * + * @param map Array of pointers to map files. + * @param maps Number of map files. + * @param initLevel Number of functions to skip before starting the tracing. + * @param maxDepth Maximum number of levels in the stack trace. + * @param buffer [out] Output buffer for the formatted stack trace. + * @param bufferSize Size of the output buffer. + * @return Needed buffer size. + */ + static int printStackTrace( MapFile** map, int maps, + int initLevel, int maxDepth, + char* buffer, int bufferSize, unsigned long stackPtr = 0, unsigned long opPtr = 0 ); +}; + + +} // dev + + +#endif // _DEV_STACKTRACE_H +/* + * Copyright (c) 2001 Jani Kajala + * + * Permission to use, copy, modify, distribute and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. + * Jani Kajala makes no representations about the suitability + * of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfTextFile.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfTextFile.cpp new file mode 100644 index 00000000..5a1c1840 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfTextFile.cpp @@ -0,0 +1,286 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "pfTextFile.h" +#include +#include + +//----------------------------------------------------------------------------- + +namespace dev +{ + + +class TextFile::TextFileImpl +{ +public: + TextFile::ErrorType err; + int line; + + explicit TextFileImpl( const char* filename ) + { + err = TextFile::ERROR_NONE; + line = 1; + m_peeked = false; + m_peekedChar = 0; + m_file = fopen( filename, "rt" ); + + if ( !m_file ) + err = TextFile::ERROR_OPEN; + } + + ~TextFileImpl() + { + if ( m_file ) + { + fclose( m_file ); + m_file = 0; + } + } + + bool eof() const + { + if ( err ) + return true; + return 0 != feof(m_file); + } + + bool peekChar( char* ch ) + { + if ( err ) + return false; + + if ( !m_peeked ) + { + int c = getc( m_file ); + if ( EOF != c ) + { + m_peeked = true; + m_peekedChar = (char)c; + } + else + { + if ( ferror(m_file) ) + err = TextFile::ERROR_READ; + } + } + + if ( m_peeked ) + *ch = m_peekedChar; + return m_peeked; + } + + bool readChar( char* ch ) + { + if ( err ) + return false; + + bool more = peekChar( ch ); + m_peeked = false; + if ( more && *ch == '\n' ) + ++line; + return more; + } + + bool skipWhitespace() + { + if ( err ) + return false; + + char ch; + while ( peekChar(&ch) ) + { + if ( !isspace(ch) ) + break; + readChar( &ch ); + } + return !eof(); + } + + bool readString( char* buf, int size ) + { + if ( err ) + return false; + + skipWhitespace(); + + int count = 0; + char ch; + while ( peekChar(&ch) ) + { + if ( isspace(ch) ) + break; + if ( count+1 < size ) + buf[count++] = ch; + readChar( &ch ); + } + if ( size > 0 ) + buf[count] = 0; + return count > 0; + } + + void skipLine() + { + if ( err ) + return; + + char ch; + while ( readChar(&ch) ) + { + if ( ch == '\n' ) + break; + } + } + + long readHex() + { + if ( err ) + return 0; + + skipWhitespace(); + + // hex must start with alphanumeric character + char ch; + if ( !peekChar(&ch) || !isalnum(ch) ) + { + err = TextFile::ERROR_PARSE; + return 0; + } + + long x = 0; + while ( peekChar(&ch) ) + { + switch ( ch ) + { + case '0': x <<= 4; x += 0; break; + case '1': x <<= 4; x += 1; break; + case '2': x <<= 4; x += 2; break; + case '3': x <<= 4; x += 3; break; + case '4': x <<= 4; x += 4; break; + case '5': x <<= 4; x += 5; break; + case '6': x <<= 4; x += 6; break; + case '7': x <<= 4; x += 7; break; + case '8': x <<= 4; x += 8; break; + case '9': x <<= 4; x += 9; break; + case 'a': + case 'A': x <<= 4; x += 0xA; break; + case 'b': + case 'B': x <<= 4; x += 0xB; break; + case 'c': + case 'C': x <<= 4; x += 0xC; break; + case 'd': + case 'D': x <<= 4; x += 0xD; break; + case 'e': + case 'E': x <<= 4; x += 0xE; break; + case 'f': + case 'F': x <<= 4; x += 0xF; break; + default: return x; + } + readChar( &ch ); + } + return x; + } + +private: + bool m_peeked; + char m_peekedChar; + FILE* m_file; + + TextFileImpl( const TextFileImpl& ); + TextFileImpl& operator=( const TextFileImpl& ); +}; + +//----------------------------------------------------------------------------- + +TextFile::TextFile( const char* filename ) +{ + m_this = TRACKED_NEW TextFileImpl( filename ); +} + +TextFile::~TextFile() +{ + delete m_this; +} + +bool TextFile::readString( char* buf, int size ) +{ + return m_this->readString( buf, size ); +} + +void TextFile::skipLine() +{ + m_this->skipLine(); +} + +long TextFile::readHex() +{ + return m_this->readHex(); +} + +bool TextFile::skipWhitespace() +{ + return m_this->skipWhitespace(); +} + +TextFile::ErrorType TextFile::error() const +{ + return m_this->err; +} + +bool TextFile::readChar( char* ch ) +{ + return m_this->readChar( ch ); +} + +bool TextFile::peekChar( char* ch ) +{ + return m_this->peekChar( ch ); +} + +bool TextFile::eof() const +{ + return m_this->eof(); +} + +int TextFile::line() const +{ + return m_this->line; +} + + +} // dev +/* + * Copyright (c) 2001 Jani Kajala + * + * Permission to use, copy, modify, distribute and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. + * Jani Kajala makes no representations about the suitability + * of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfTextFile.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfTextFile.h new file mode 100644 index 00000000..dc74b6a4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfStackTrace/pfTextFile.h @@ -0,0 +1,127 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _DEV_TEXTFILE_H +#define _DEV_TEXTFILE_H + + +namespace dev +{ + + +/** + * ASCII-7 text file parser. Doesnt throw exceptions. + */ +class TextFile +{ +public: + /** Error code. */ + enum ErrorType + { + /** No error. */ + ERROR_NONE, + /** File open failed. */ + ERROR_OPEN, + /** File reading failed. */ + ERROR_READ, + /** Syntax error. */ + ERROR_PARSE + }; + + /** Opens a file. */ + explicit TextFile( const char* filename ); + + /// + ~TextFile(); + + /** + * Reads a single character. + * @return true if read ok. + */ + bool readChar( char* ch ); + + /** + * Peeks a single character. + * @return true if peek ok. + */ + bool peekChar( char* ch ); + + /** + * Reads whitespace delimited string. + * If the string doesnt fit to the buffer then + * the rest of the string is skipped. Buffer + * is always 0-terminated. + * @param buf [out] Pointer to string buffer. + * @param size String buffer size. Must be larger than 0. + * @return false if end-of-file reached before any characters was read. + */ + bool readString( char* buf, int size ); + + /** Skips the rest of the line. */ + void skipLine(); + + /** Reads hex integer. Skips preceding whitespace. */ + long readHex(); + + /** + * Skips whitespace characters. + * @return false if end-of-file reached. + */ + bool skipWhitespace(); + + /** Returns true if end-of-file have been reached. */ + bool eof() const; + + /** Returns error code or 0 (ERROR_NONE) if no error. */ + ErrorType error() const; + + /** Returns line number of last successful read character. */ + int line() const; + +private: + class TextFileImpl; + TextFileImpl* m_this; + + TextFile( const TextFile& ); + TextFile& operator=( const TextFile& ); +}; + + +} // dev + + +#endif // _DEV_TEXTFILE_H +/* + * Copyright (c) 2001 Jani Kajala + * + * Permission to use, copy, modify, distribute and sell this + * software and its documentation for any purpose is hereby + * granted without fee, provided that the above copyright notice + * appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation. + * Jani Kajala makes no representations about the suitability + * of this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/pfSurfaceCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/pfSurfaceCreatable.h new file mode 100644 index 00000000..c629d78a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/pfSurfaceCreatable.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pfSurfaceCreatable_inc +#define pfSurfaceCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plLayerMovie.h" + +REGISTER_NONCREATABLE( plLayerMovie ); + +#include "plLayerBink.h" + +REGISTER_CREATABLE( plLayerBink ); + +#include "plLayerAVI.h" + +REGISTER_CREATABLE( plLayerAVI ); + +#include "plFadeOpacityLay.h" + +REGISTER_CREATABLE( plFadeOpacityLay ); + +#include "plFadeOpacityMod.h" + +REGISTER_CREATABLE( plFadeOpacityMod ); + +#include "plDistOpacityMod.h" + +REGISTER_CREATABLE( plDistOpacityMod ); + +#endif // pfSurfaceCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plDistOpacityMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plDistOpacityMod.cpp new file mode 100644 index 00000000..eb7b5409 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plDistOpacityMod.cpp @@ -0,0 +1,296 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#include "hsTypes.h" + +#include "plDistOpacityMod.h" + +#include "plFadeOpacityLay.h" +#include "../plSurface/hsGMaterial.h" + +#include "../plDrawable/plAccessGeometry.h" +#include "../plDrawable/plAccessSpan.h" + +#include "../plMessage/plMatRefMsg.h" + +// If we're tracking the camera +#include "../plMessage/plRenderMsg.h" +#include "plPipeline.h" + +// If we're tracking the avater +#include "../plMessage/plAvatarMsg.h" +#include "../plAvatar/plArmatureMod.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "hsQuat.h" + +plDistOpacityMod::plDistOpacityMod() +: fSetup(false) +{ + fDists[kNearTrans] = 0; + fDists[kNearOpaq] = 0; + fDists[kFarOpaq] = 0; + fDists[kFarTrans] = 0; + + fRefPos.Set(0, 0, 0); +} + +plDistOpacityMod::~plDistOpacityMod() +{ +} + +void plDistOpacityMod::SetKey(plKey k) +{ + plSingleModifier::SetKey(k); + + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plArmatureUpdateMsg::Index(), GetKey()); +} + + +hsScalar plDistOpacityMod::ICalcOpacity(const hsPoint3& targPos, const hsPoint3& refPos) const +{ + hsScalar dist = hsVector3(&targPos, &refPos).Magnitude(); + + if( dist > fDists[kFarTrans] ) + return 0; + if( dist < fDists[kNearTrans] ) + return 0; + + if( dist > fDists[kFarOpaq] ) + { + dist -= fDists[kFarOpaq]; + dist /= (fDists[kFarTrans] - fDists[kFarOpaq]); + hsAssert(dist >= 0, "unexpected interpolation param - neg"); + hsAssert(dist <= 1.f, "unexpected interpolation param - > one"); + + return 1.f - dist; + } + + if( dist < fDists[kNearOpaq] ) + { + dist -= fDists[kNearTrans]; + dist /= (fDists[kNearOpaq] - fDists[kNearTrans]); + hsAssert(dist >= 0, "unexpected interpolation param - neg"); + hsAssert(dist <= 1.f, "unexpected interpolation param - > one"); + + return dist; + } + + return 1.f; +} + +void plDistOpacityMod::ISetOpacity() +{ + if( !GetTarget() ) + return; + + if( !fSetup ) + ISetup(); + + hsScalar opacity = ICalcOpacity(GetTarget()->GetLocalToWorld().GetTranslate(), fRefPos); + + const int num = fFadeLays.GetCount(); + int i; + for( i = 0; i < num; i++ ) + fFadeLays[i]->SetOpacity(opacity); + +} + +hsBool plDistOpacityMod::MsgReceive(plMessage* msg) +{ + plArmatureUpdateMsg* arm = plArmatureUpdateMsg::ConvertNoRef(msg); + if( arm && arm->IsLocal() ) + { + arm->fArmature->GetPositionAndRotationSim(&fRefPos, nil); + + return true; + } + + plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg); + if( rend ) + { + if( HasFlag(kTrackCamera) ) + fRefPos = rend->Pipeline()->GetViewPositionWorld(); + + ISetOpacity(); + + return true; + } + + plGenRefMsg* ref = plGenRefMsg::ConvertNoRef(msg); + if( ref ) + { + switch(ref->fType) + { + case kRefFadeLay: + if( ref->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + plFadeOpacityLay* lay = plFadeOpacityLay::ConvertNoRef(ref->GetRef()); + int idx = fFadeLays.Find(lay); + if( idx != fFadeLays.kMissingIndex ) + fFadeLays.Remove(idx); + } + break; + }; + return true; + } + + return plSingleModifier::MsgReceive(msg); +} + +void plDistOpacityMod::Read(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Read(s, mgr); + + int i; + for( i = 0; i < kNumDists; i++ ) + fDists[i] = s->ReadSwapScalar(); + + ICheckDists(); + + fSetup = false; +} + +void plDistOpacityMod::Write(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Write(s, mgr); + + int i; + for( i = 0; i < kNumDists; i++ ) + s->WriteSwapScalar(fDists[i]); +} + +void plDistOpacityMod::SetTarget(plSceneObject* so) +{ + plSingleModifier::SetTarget(so); + + fSetup = false; +} + +void plDistOpacityMod::SetFarDist(hsScalar opaque, hsScalar transparent) +{ + fDists[kFarOpaq] = opaque; + fDists[kFarTrans] = transparent; + + ICheckDists(); +} + +void plDistOpacityMod::SetNearDist(hsScalar transparent, hsScalar opaque) +{ + fDists[kNearOpaq] = opaque; + fDists[kNearTrans] = transparent; + + ICheckDists(); +} + +class MatLayer +{ +public: + hsGMaterial* fMat; + plLayerInterface* fLay; +}; + + +void plDistOpacityMod::ISetup() +{ + fFadeLays.Reset(); + + plSceneObject* so = GetTarget(); + if( !so ) + return; + + const plDrawInterface* di = so->GetDrawInterface(); + if( !di ) + return; + + hsTArray todo; + + hsTArray src; + plAccessGeometry::Instance()->OpenRO(di, src, false); + + // We are guaranteed that each Max object will be given a unique + // copy of materials and associated layers. But within an object, + // a given layer may be shared across materials. + // So we'll build a list of the layers that need a FadeOpacityLay applied, + // making sure we don't include any layer more than once (strip repeats). + // This would be grossly inefficient if the numbers involved weren't all + // very small. So an n^2 search isn't bad if n <= 2. + int i; + for( i = 0; i < src.GetCount(); i++ ) + { + hsGMaterial* mat = src[i].GetMaterial(); + + int j; + for( j = 0; j < mat->GetNumLayers(); j++ ) + { + plLayerInterface* lay = mat->GetLayer(j); + if( !j || !(lay->GetZFlags() & hsGMatState::kZNoZWrite) || (lay->GetMiscFlags() & hsGMatState::kMiscRestartPassHere) ) + { + int k; + for( k = 0; k < todo.GetCount(); k++ ) + { + if( lay == todo[k].fLay ) + break; + } + if( k == todo.GetCount() ) + { + MatLayer* push = todo.Push(); + push->fMat = mat; + push->fLay = lay; + } + } + } + } + + plAccessGeometry::Instance()->Close(src); + + for( i = 0; i < todo.GetCount(); i++ ) + { + hsGMaterial* mat = todo[i].fMat; + plLayerInterface* lay = todo[i].fLay; + + plFadeOpacityLay* fade = TRACKED_NEW plFadeOpacityLay; + + hsgResMgr::ResMgr()->NewKey(lay->GetKey()->GetName(), fade, lay->GetKey()->GetUoid().GetLocation()); + + fade->AttachViaNotify(lay); + + // We should add a ref or something here if we're going to hold on to this (even though we created and "own" it). + fFadeLays.Append(fade); + + plMatRefMsg* msg = TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnReplace, i, plMatRefMsg::kLayer); + msg->SetOldRef(lay); + hsgResMgr::ResMgr()->SendRef(fade, msg, plRefFlags::kActiveRef); + + plGenRefMsg* toMe = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefFadeLay); + hsgResMgr::ResMgr()->SendRef(fade, toMe, plRefFlags::kPassiveRef); + } + + fSetup = true; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plDistOpacityMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plDistOpacityMod.h new file mode 100644 index 00000000..1544adb8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plDistOpacityMod.h @@ -0,0 +1,112 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDistOpacityMod_inc +#define plDistOpacityMod_inc + +#include "hsGeometry3.h" +#include "../pnModifier/plSingleModifier.h" +#include "hsTemplates.h" + +class plPipeline; +class plRenderMsg; +class plFadeOpacityLay; + + +class plDistOpacityMod : public plSingleModifier +{ +public: + + enum { + kTrackAvatar, + kTrackCamera + }; +protected: + + enum { + kRefFadeLay + }; + + // Volatile flag, whether we're setup yet or not. + UInt8 fSetup; + + enum { + kNearTrans, + kNearOpaq, + kFarOpaq, + kFarTrans, + + kNumDists + }; + hsScalar fDists[kNumDists]; + + hsPoint3 fRefPos; + + hsTArray fFadeLays; + + // We only act in response to messages. + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return false; } + + hsScalar ICalcOpacity(const hsPoint3& targPos, const hsPoint3& refPos) const; + void ISetOpacity(); + + void ISetup(); + + void ICheckDists() + { + hsAssert(fDists[kNearTrans] <= fDists[kNearOpaq], "Bad transition values"); + hsAssert(fDists[kNearOpaq] <= fDists[kFarOpaq], "Bad transition values"); + hsAssert(fDists[kFarOpaq] <= fDists[kFarTrans], "Bad transition values"); + } + +public: + plDistOpacityMod(); + virtual ~plDistOpacityMod(); + + CLASSNAME_REGISTER( plDistOpacityMod ); + GETINTERFACE_ANY( plDistOpacityMod, plSingleModifier ); + + virtual void SetKey(plKey k); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual void SetTarget(plSceneObject* so); + + // Rules are: + // NearTrans <= NearOpaq <= FarOpaque <= FarTrans + void SetFarDist(hsScalar opaque, hsScalar transparent); + void SetNearDist(hsScalar transparent, hsScalar opaque); + + hsScalar GetFarTransparent() const { return fDists[kFarTrans]; } + hsScalar GetNearTransparent() const { return fDists[kNearTrans]; } + hsScalar GetFarOpaque() const { return fDists[kFarOpaq]; } + hsScalar GetNearOpaque() const { return fDists[kNearOpaq]; } +}; + +#endif // plDistOpacityMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityLay.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityLay.cpp new file mode 100644 index 00000000..ba016ce7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityLay.cpp @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" + +#include "plFadeOpacityLay.h" + +plFadeOpacityLay::plFadeOpacityLay() +: fOpScale(1.f) +{ + fOwnedChannels |= kOpacity; + fOpacity = TRACKED_NEW hsScalar; +} + +plFadeOpacityLay::~plFadeOpacityLay() +{ +} + +UInt32 plFadeOpacityLay::Eval(double secs, UInt32 frame, UInt32 ignore) +{ + UInt32 ret = plLayerInterface::Eval(secs, frame, ignore); + + if( fUnderLay ) + { + if( GetBlendFlags() & hsGMatState::kBlendAdd ) + { + *fRuntimeColor = fUnderLay->GetRuntimeColor() * fOpScale; + } + else + { + *fOpacity = fUnderLay->GetOpacity() * fOpScale; + } + } + + return ret; +} + +void plFadeOpacityLay::Read(hsStream* s, hsResMgr* mgr) +{ + plLayerInterface::Read(s, mgr); + + fOpScale = s->ReadSwapScalar(); +} + +void plFadeOpacityLay::Write(hsStream* s, hsResMgr* mgr) +{ + plLayerInterface::Write(s, mgr); + + s->WriteSwapScalar(fOpScale); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityLay.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityLay.h new file mode 100644 index 00000000..6c38da00 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityLay.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plFadeOpacityLay_inc +#define plFadeOpacityLay_inc + +#include "../plSurface/plLayerInterface.h" + + +class plFadeOpacityLay : public plLayerInterface +{ +protected: + + hsScalar fOpScale; +public: + + plFadeOpacityLay(); + virtual ~plFadeOpacityLay(); + + CLASSNAME_REGISTER( plFadeOpacityLay ); + GETINTERFACE_ANY( plFadeOpacityLay, plLayerInterface ); + + virtual UInt32 Eval(double secs, UInt32 frame, UInt32 ignore); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + void SetOpacity(hsScalar f) { fOpScale = f; } + hsScalar GetOpacity() const { return fOpScale; } +}; + +#endif // plFadeOpacityLay_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityMod.cpp new file mode 100644 index 00000000..a66ea6cf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityMod.cpp @@ -0,0 +1,376 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plFadeOpacityMod.h" + +#include "plFadeOpacityLay.h" + +#include "../plMessage/plRenderMsg.h" +#include "../plMessage/plMatRefMsg.h" + +#include "../plSurface/hsGMaterial.h" + +#include "../plDrawable/plVisLOSMgr.h" +#include "../plDrawable/plAccessGeometry.h" +#include "../plDrawable/plAccessSpan.h" +#include "../plDrawable/plDrawableSpans.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" + +#include "../plScene/plVisMgr.h" + +#include "plgDispatch.h" +#include "plPipeline.h" +#include "hsBounds.h" +#include "hsTimer.h" +#include "hsResMgr.h" + +/* + curr = (t - t0) / fadeUp; + curr = 1. - (t - t0) / fadeDown; + + switch from fadeUp to fadeDown + curr = 1 - (t - t0) / fadeDown; + t - t0 = (1 - curr) * fadeDown + t0 = t - (1 - curr) * fadeDown; + + switch from fadeDown to fadeUp + curr = (t - t0) / fadeUp; + curr * fadeUp = t - t0; + t0 = t - curr * fadeUp; +*/ + +hsBool plFadeOpacityMod::fLOSCheckDisabled = false; + +const hsScalar kDefFadeUp(5.f); +const hsScalar kDefFadeDown(1.f); + +plFadeOpacityMod::plFadeOpacityMod() +: fFadeUp(kDefFadeUp), + fFadeDown(kDefFadeDown), + fOpCurrent(1.f), + fStart(0), + fFade(kImmediate), + fLastEye(0.f, 0.f, 0.f), + fSetup(false) +{ +} + +plFadeOpacityMod::~plFadeOpacityMod() +{ +} + +hsBool plFadeOpacityMod::MsgReceive(plMessage* msg) +{ + plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg); + if( rend ) + { + IOnRenderMsg(rend); + return true; + } + return plSingleModifier::MsgReceive(msg); +} + +void plFadeOpacityMod::SetKey(plKey k) +{ + hsKeyedObject::SetKey(k); + if( k ) + { + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + } +} + + +void plFadeOpacityMod::Read(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Read(s, mgr); + + fFadeUp = s->ReadSwapScalar(); + fFadeDown = s->ReadSwapScalar(); +} + +void plFadeOpacityMod::Write(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Write(s, mgr); + + s->WriteSwapScalar(fFadeUp); + s->WriteSwapScalar(fFadeDown); +} + +void plFadeOpacityMod::SetTarget(plSceneObject* so) +{ + plSingleModifier::SetTarget(so); + + fSetup = false; +} + +hsBool plFadeOpacityMod::IShouldCheck(plPipeline* pipe) +{ + if (pipe->TestVisibleWorld(GetTarget())) + return true; + + fFade = kImmediate; + + return false; +} + +void plFadeOpacityMod::IOnRenderMsg(plRenderMsg* rend) +{ + // Okay, are we set up enough to proceed? + if( !IReady() ) + return; + + // Okay, we're going to check. + + hsBool hit = false; + + if( !fLOSCheckDisabled ) + { + // Should we check to see if we're visible before anything else? + if( !IShouldCheck(rend->Pipeline()) ) + return; + + hsPoint3 eyePos = rend->Pipeline()->GetViewPositionWorld(); + hsPoint3 ourPos = IGetOurPos(); + + if( fFade != kImmediate ) + { + // If we've moved more than 3 feet in a frame, we'll consider this a + // camera cut. In that case, don't fade up or down, just go there. + const hsScalar kCutMagSquared = 3.f * 3.f; + if( hsVector3(&eyePos, &fLastEye).MagnitudeSquared() > kCutMagSquared ) + fFade = kImmediate; + } + fLastEye = eyePos; + + // Cast the ray from eye to us. + plVisHit hitInfo; + hit = plVisLOSMgr::Instance()->Check(eyePos, ourPos, hitInfo); + } + + // If the ray made it, fade us up + // Else fade us down. + if( !hit ) + IFadeUp(); + else + IFadeDown(); + + ISetOpacity(); +} + +hsBool plFadeOpacityMod::IReady() +{ + plSceneObject* so = GetTarget(); + if( !so ) + return false; + + if( !so->GetDrawInterface() ) + return false; + + if( !fSetup ) + ISetup(so); + + if( !fFadeLays.GetCount() ) + return false; + + return true; +} + +hsPoint3 plFadeOpacityMod::IGetOurPos() +{ + hsAssert(GetTarget(), "Weed out target-less earlier"); + + if( HasFlag(kBoundsCenter) ) + { + return GetTarget()->GetDrawInterface()->GetWorldBounds().GetCenter(); + } + else + { + return GetTarget()->GetLocalToWorld().GetTranslate(); + } +} + +void plFadeOpacityMod::ICalcOpacity() +{ + double t = hsTimer::GetSysSeconds(); + switch( fFade ) + { + case kFadeUp: + fOpCurrent = (hsScalar)(t - fStart); + if( fOpCurrent > fFadeUp ) + { + fOpCurrent = 1.f; + fFade = kUp; + } + else + { + fOpCurrent /= fFadeUp; + } + break; + case kFadeDown: + fOpCurrent = (hsScalar)(t - fStart); + if( fOpCurrent > fFadeDown ) + { + fOpCurrent = 0.f; + fFade = kDown; + } + else + { + fOpCurrent = 1.f - fOpCurrent / fFadeDown; + } + break; + case kUp: + case kDown: + break; + case kImmediate: + default: + hsAssert(false, "Invalid state"); + break; + }; +} + +void plFadeOpacityMod::ISetOpacity() +{ + ICalcOpacity(); + + const int num = fFadeLays.GetCount(); + int i; + for( i = 0; i < num; i++ ) + fFadeLays[i]->SetOpacity(fOpCurrent); +} + +void plFadeOpacityMod::IFadeUp() +{ + const double t = hsTimer::GetSysSeconds(); + switch( fFade ) + { + case kImmediate: + fOpCurrent = 1.f; + fFade = kUp; + break; + case kFadeDown: + { + fStart = t - fOpCurrent * fFadeUp; + + fFade = kFadeUp; + } + break; + case kDown: + { + fStart = t; + fFade = kFadeUp; + } + break; + case kUp: + case kFadeUp: + break; + default: + hsAssert(false, "Bad State"); + break; + }; +} + +void plFadeOpacityMod::IFadeDown() +{ + const double t = hsTimer::GetSysSeconds(); + switch( fFade ) + { + case kImmediate: + fOpCurrent = 0.f; + fFade = kDown; + break; + case kFadeUp: + { + fStart = t - (1.f - fOpCurrent) * fFadeDown; + + fFade = kFadeDown; + } + break; + case kUp: + { + fStart = t; + fFade = kFadeDown; + } + case kFadeDown: + case kDown: + break; + default: + hsAssert(false, "Bad State"); + break; + }; +} + +void plFadeOpacityMod::ISetup(plSceneObject* so) +{ + fFadeLays.Reset(); + + if( !so ) + return; + + const plDrawInterface* di = so->GetDrawInterface(); + if( !di ) + return; + + hsTArray src; + plAccessGeometry::Instance()->OpenRO(di, src, false); + + int i; + for( i = 0; i < src.GetCount(); i++ ) + { + hsGMaterial* mat = src[i].GetMaterial(); + + int j; + for( j = 0; j < mat->GetNumLayers(); j++ ) + { + plLayerInterface* lay = mat->GetLayer(j); + if( !j || !(lay->GetZFlags() & hsGMatState::kZNoZWrite) || (lay->GetMiscFlags() & hsGMatState::kMiscRestartPassHere) ) + { + plFadeOpacityLay* fade = NEW(plFadeOpacityLay); + + hsgResMgr::ResMgr()->NewKey(lay->GetKey()->GetName(), fade, lay->GetKey()->GetUoid().GetLocation()); + + fade->AttachViaNotify(lay); + + // We should add a ref or something here if we're going to hold on to this (even though we created and "own" it). + fFadeLays.Append(fade); + + plMatRefMsg* msg = NEW(plMatRefMsg)(mat->GetKey(), plRefMsg::kOnReplace, i, plMatRefMsg::kLayer); + msg->SetOldRef(lay); + hsgResMgr::ResMgr()->SendRef(fade, msg, plRefFlags::kActiveRef); + + plGenRefMsg* toMe = NEW(plGenRefMsg)(GetKey(), plRefMsg::kOnRequest, 0, kRefFadeLay); + hsgResMgr::ResMgr()->SendRef(fade, toMe, plRefFlags::kPassiveRef); + } + } + } + + plAccessGeometry::Instance()->Close(src); + + fSetup = true; + fFade = kImmediate; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityMod.h new file mode 100644 index 00000000..dace06a4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plFadeOpacityMod.h @@ -0,0 +1,120 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plFadeOpacityMod_inc +#define plFadeOpacityMod_inc + +#include "hsGeometry3.h" +#include "../pnModifier/plSingleModifier.h" +#include "hsTemplates.h" + +class plPipeline; +class plRenderMsg; +class plFadeOpacityLay; + +class plFadeOpacityMod : public plSingleModifier +{ +public: + enum { + kBoundsCenter = 1 + }; + +protected: + + enum { + kRefFadeLay + }; + + // Input parameters + hsScalar fFadeUp; + hsScalar fFadeDown; + + // Internal fade state + enum FadeState { + kUp = 0, + kDown = 1, + kFadeUp = 2, + kFadeDown = 3, + kImmediate = 4 + }; + hsScalar fOpCurrent; + + double fStart; + FadeState fFade; + UInt8 fSetup; + + hsPoint3 fLastEye; + + // The target layers + hsTArray fFadeLays; + + // A global to turn the whole thing off for debug/perf + static hsBool fLOSCheckDisabled; + + void IOnRenderMsg(plRenderMsg* rend); + hsBool IReady(); + hsBool IShouldCheck(plPipeline* pipe); + hsPoint3 IGetOurPos(); + void ICalcOpacity(); + void ISetOpacity(); + void IFadeUp(); + void IFadeDown(); + void ISetup(plSceneObject* so); + + // We only act in response to messages. + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return false; } + +public: + plFadeOpacityMod(); + virtual ~plFadeOpacityMod(); + + CLASSNAME_REGISTER( plFadeOpacityMod ); + GETINTERFACE_ANY( plFadeOpacityMod, plSingleModifier ); + + virtual void SetKey(plKey k); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual void SetTarget(plSceneObject* so); + + void FadeUp(); + void FadeDown(); + void Fade(hsBool up) { if( up ) FadeUp(); else FadeDown(); } + + void SetFadeUp(hsScalar f) { fFadeUp = f; } + hsScalar GetFadeUp() const { return fFadeUp; } + + void SetFadeDown(hsScalar f) { fFadeDown = f; } + hsScalar GetFadeDown() const { return fFadeDown; } + + static hsBool GetLOSCheckDisabled() { return fLOSCheckDisabled; } + static void SetLOSCheckDisabled(hsBool on) { fLOSCheckDisabled = on; } +}; + +#endif // plFadeOpacityMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plGrabCubeMap.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plGrabCubeMap.cpp new file mode 100644 index 00000000..720871e1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plGrabCubeMap.cpp @@ -0,0 +1,150 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" + +#include "plGrabCubeMap.h" + +#include "plPipeline.h" +#include "plDrawable.h" + +#include "hsMatrix44.h" +#include "hsGeometry3.h" +#include "hsColorRGBA.h" +#include "hsBounds.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" + +#include "../plGImage/plMipmap.h" + +#include "../plJPEG/plJPEG.h" + +#include "../plMessage/plRenderRequestMsg.h" + +plGrabCubeRenderRequest::plGrabCubeRenderRequest() +: fQuality(75) +{ +} + +void plGrabCubeRenderRequest::Render(plPipeline* pipe, plPageTreeMgr* pageMgr) +{ + if( !fFileName[0] ) + return; + + plRenderRequest::Render(pipe, pageMgr); + + pipe->EndRender(); + + plMipmap mipmap; + if( pipe->CaptureScreen(&mipmap) ) + { + plJPEG::Instance().SetWriteQuality(fQuality); + + plJPEG::Instance().WriteToFile(fFileName, &mipmap); + } + + pipe->BeginRender(); +} + + +void plGrabCubeMap::GrabCube(plPipeline* pipe, plSceneObject* obj, const char* pref, const hsColorRGBA& clearColor, UInt8 q) +{ + hsPoint3 center; + if( obj && !(obj->GetLocalToWorld().fFlags & hsMatrix44::kIsIdent) ) + { + center = obj->GetLocalToWorld().GetTranslate(); + } + else if( obj && obj->GetDrawInterface() ) + { + center = obj->GetDrawInterface()->GetWorldBounds().GetCenter(); + } + else + { + center = pipe->GetCameraToWorld().GetTranslate(); + } + ISetupRenderRequests(pipe, center, pref, clearColor, q); +} + +void plGrabCubeMap::GrabCube(plPipeline* pipe, const hsPoint3& center, const char* pref, const hsColorRGBA& clearColor, UInt8 q) +{ + ISetupRenderRequests(pipe, center, pref, clearColor, q); +} + +void plGrabCubeMap::ISetupRenderRequests(plPipeline* pipe, const hsPoint3& center, const char* pref, const hsColorRGBA& clearColor, UInt8 q) const +{ + hsMatrix44 worldToCameras[6]; + hsMatrix44 cameraToWorlds[6]; + hsMatrix44::MakeEnvMapMatrices(center, worldToCameras, cameraToWorlds); + + UInt32 renderState + = plPipeline::kRenderNormal + | plPipeline::kRenderClearColor + | plPipeline::kRenderClearDepth; + + float hither; + float yon; + pipe->GetDepth(hither, yon); + + const char* suff[6] = { + "LF", + "RT", + "BK", + "FR", + "UP", + "DN" }; + + int i; + for( i = 0; i < 6; i++ ) + { + plGrabCubeRenderRequest* req = TRACKED_NEW plGrabCubeRenderRequest; + req->SetRenderState(renderState); + + req->SetDrawableMask(plDrawable::kNormal); + req->SetSubDrawableMask(plDrawable::kSubAllTypes); + + req->SetHither(hither); + req->SetYon(yon); + + req->SetFovX(90.f); + req->SetFovY(90.f); + + req->SetClearColor(clearColor); + req->SetClearDepth(1.f); + + req->SetClearDrawable(nil); + req->SetRenderTarget(nil); + + req->SetCameraTransform(worldToCameras[i], cameraToWorlds[i]); + + req->fQuality = q; + sprintf(req->fFileName, "%s_%s.jpg", pref, suff[i]); + + plRenderRequestMsg* reqMsg = TRACKED_NEW plRenderRequestMsg(nil, req); + reqMsg->Send(); + hsRefCnt_SafeUnRef(req); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plGrabCubeMap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plGrabCubeMap.h new file mode 100644 index 00000000..621e716a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plGrabCubeMap.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plGrabCubeMap_inc +#define plGrabCubeMap_inc + +#include "../plScene/plRenderRequest.h" + +class plSceneObject; +class plPipeline; +class plPageTreeMgr; + +struct hsMatrix44; +struct hsPoint3; +struct hsColorRGBA; + +class plGrabCubeRenderRequest : public plRenderRequest +{ +public: + plGrabCubeRenderRequest(); + + char fFileName[256]; + UInt8 fQuality; + + // This function is called after the render request is processed by the client + virtual void Render(plPipeline* pipe, plPageTreeMgr* pageMgr); +}; + +class plGrabCubeMap +{ +protected: + void ISetupRenderRequests(plPipeline* pipe, const hsPoint3& center, const char* pref, const hsColorRGBA& clearColor, UInt8 q) const; + +public: + plGrabCubeMap() {} + void GrabCube(plPipeline* pipe, plSceneObject* obj, const char* pref, const hsColorRGBA& clearColor, UInt8 q=75); + void GrabCube(plPipeline* pipe, const hsPoint3& pos, const char* pref, const hsColorRGBA& clearColor, UInt8 q=75); +}; + + +#endif // plGrabCubeMap_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerAVI.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerAVI.cpp new file mode 100644 index 00000000..1f37dd79 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerAVI.cpp @@ -0,0 +1,232 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsConfig.h" + +#if HS_BUILD_FOR_WIN32 +#define WIN32_EXTRA_LEAN +#define WIN32_LEAN_AND_MEAN +#ifndef _WINDOWS_H_ // redundant include guard to minimize compile times +#define _WINDOWS_H_ +#include +#endif // _WINDOWS_H_ +#include "vfw.h" +#endif // HS_BUILD_FOR_WIN32 + +#include "hsTypes.h" +#include "plLayerAVI.h" +#include "../plGImage/plMipmap.h" + +#if HS_BUILD_FOR_WIN32 +class plAVIFileInfo +{ +public: + plAVIFileInfo() : fAVIStream(nil), fGetFrame(0) {} + IAVIStream* fAVIStream; + AVISTREAMINFO fAVIStreamInfo; + PGETFRAME fGetFrame; // Where in the stream to get the next frame +}; +#else // HS_BUILD_FOR_WIN32 +struct plAVIFileInfo +{ +}; +#endif // HS_BUILD_FOR_WIN32 + +static hsBool ICopySourceToTexture24(BITMAPINFO* bmi, plMipmap* t); +static hsBool ICopySourceToTexture16(BITMAPINFO* bmi, plMipmap* t); + + +plLayerAVI::plLayerAVI() +{ + fAVIInfo = TRACKED_NEW plAVIFileInfo; +} + +plLayerAVI::~plLayerAVI() +{ + ICloseMovie(); + + delete fAVIInfo; +} + + +hsBool plLayerAVI::IInit() +{ +#if HS_BUILD_FOR_WIN32 + int ret = AVIStreamOpenFromFile( &fAVIInfo->fAVIStream, + fMovieName, + streamtypeVIDEO, + 0, + OF_READ, + NULL); + + if (ret) + return ISetFault("Error opening AVI"); + + if( !(fAVIInfo->fGetFrame = AVIStreamGetFrameOpen(fAVIInfo->fAVIStream, NULL)) ) + return ISetFault("Error positioning AVI"); + + if( AVIStreamInfo(fAVIInfo->fAVIStream, &fAVIInfo->fAVIStreamInfo, sizeof(AVISTREAMINFO)) ) + return ISetFault("Error getting AVI info"); + + BITMAPINFO* bmi; + if( !(bmi = (BITMAPINFO*)AVIStreamGetFrame(fAVIInfo->fGetFrame, fCurrentFrame >= 0 ? fCurrentFrame : 0)) ) + return ISetFault("Can't get first frame"); + ISetSize(bmi->bmiHeader.biWidth, bmi->bmiHeader.biHeight); + + Int32 endFrame = fAVIInfo->fAVIStreamInfo.dwLength-1; + hsScalar length = float(endFrame) * float(fAVIInfo->fAVIStreamInfo.dwScale) + / float(fAVIInfo->fAVIStreamInfo.dwRate); + ISetLength(length); +#endif + + return false; +} + +Int32 plLayerAVI::ISecsToFrame(hsScalar secs) +{ + float timeScale = float(fAVIInfo->fAVIStreamInfo.dwRate) / float(fAVIInfo->fAVIStreamInfo.dwScale); + return Int32(secs * timeScale + 0.5f); +} + +hsBool plLayerAVI::IGetCurrentFrame() +{ + if( !fAVIInfo->fAVIStream ) + IInit(); + + ICheckBitmap(); + + BITMAPINFO* bmi; + if( !(bmi = (BITMAPINFO*)AVIStreamGetFrame(fAVIInfo->fGetFrame, fCurrentFrame)) ) + return ISetFault("Can't get frame"); + + switch(bmi->bmiHeader.biBitCount) + { + case 16: + return ICopySourceToTexture16(bmi, plMipmap::ConvertNoRef(GetTexture())); + break; + case 24: + return ICopySourceToTexture24(bmi, plMipmap::ConvertNoRef(GetTexture())); + break; + default: + return ISetFault("Unknown AVI color depth"); + } + return true; +} + +static hsBool ICopySourceToTexture16(BITMAPINFO* bmi, plMipmap* b) +{ + hsAssert( b != nil, "nil mipmap passed to ICopySourceToTexture16()" ); + + UInt16* pSrc = (UInt16*)( bmi->bmiHeader.biSize + (BYTE*)bmi ); + + UInt32* pix = (UInt32*)b->GetImage(); + pix += b->GetWidth() * b->GetHeight(); + + int width = bmi->bmiHeader.biWidth; + int height = bmi->bmiHeader.biHeight; + + int useHeight = hsMinimum(height, b->GetHeight()); + int useWidth = hsMinimum(width, b->GetWidth()); + int i; + for( i = 0; i < useHeight; i++ ) + { + UInt16* src = pSrc; + pSrc += width; + + pix -= b->GetWidth(); + UInt32* tPix = pix; + int j; + for( j = 0; j < useWidth; j++ ) + { + *tPix = ((*src & 0x001f) << 3) // up 3 + | ((*src & 0x03e0) << 6) // down 5 up 3 up 8 + | ((*src & 0x7c00) << 9) // down 10 up 3 up 16 + | (0xff << 24); // alpha + src++; + ++tPix; + } + } + + return false; +} + +static hsBool ICopySourceToTexture24(BITMAPINFO* bmi, plMipmap* b) +{ + hsAssert( b != nil, "nil mipmap passed to ICopySourceToTexture24()" ); + + unsigned char* pSrc = (unsigned char*)( bmi->bmiHeader.biSize + (BYTE*)bmi ); + + hsRGBAColor32* pix = (hsRGBAColor32*)b->GetImage(); + pix += b->GetWidth() * b->GetHeight(); + + int width = bmi->bmiHeader.biWidth; + int height = bmi->bmiHeader.biHeight; + + int useHeight = hsMinimum(height, b->GetHeight()); + int useWidth = hsMinimum(width, b->GetWidth()); + int i; + + for( i = 0; i < useHeight; i++ ) + { + unsigned char* src = pSrc; + pSrc += width * 3; + + pix -= b->GetWidth(); + hsRGBAColor32* tPix = pix; + int j; + for( j = 0; j < useWidth; j++ ) + { + tPix->b = *src++; + tPix->g = *src++; + tPix->r = *src++; + ++tPix; + } + } + + return false; +} + +hsBool plLayerAVI::ICloseMovie() +{ + if( fAVIInfo->fGetFrame ) + AVIStreamGetFrameClose(fAVIInfo->fGetFrame); + fAVIInfo->fGetFrame = 0; + + if( fAVIInfo->fAVIStream ) + AVIStreamRelease(fAVIInfo->fAVIStream); + + fAVIInfo->fAVIStream = nil; + + return false; +} + +hsBool plLayerAVI::IRelease() +{ + ICloseMovie(); + + return false; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerAVI.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerAVI.h new file mode 100644 index 00000000..cbf2762f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerAVI.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLayerAVI_inc +#define plLayerAVI_inc + +#include "plLayerMovie.h" + +class plAVIFileInfo; + +class plLayerAVI : public plLayerMovie +{ +protected: + + plAVIFileInfo* fAVIInfo; + + hsBool ICloseMovie(); + + virtual Int32 ISecsToFrame(hsScalar secs); + virtual hsBool IInit(); + virtual hsBool IGetCurrentFrame(); + virtual hsBool IRelease(); + +public: + plLayerAVI(); + virtual ~plLayerAVI(); + + CLASSNAME_REGISTER( plLayerAVI ); + GETINTERFACE_ANY( plLayerAVI, plLayerMovie ); + +}; + + + +#endif // plLayerAVI_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerMovie.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerMovie.cpp new file mode 100644 index 00000000..4ae606b1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerMovie.cpp @@ -0,0 +1,209 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLayerMovie.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "hsUtils.h" + +#include "../plMessage/plAnimCmdMsg.h" +#include "../plGImage/plMipmap.h" +#include "../plPipeline/hsGDeviceRef.h" + +plLayerMovie::plLayerMovie() +: fMovieName(nil), + fCurrentFrame(-1), + fLength(0), + fWidth(32), + fHeight(32) +{ + fOwnedChannels |= kTexture; + fTexture = TRACKED_NEW plBitmap*; + *fTexture = nil; + +// fTimeConvert.SetOwner(this); +} + +plLayerMovie::~plLayerMovie() +{ + delete [] fMovieName; + + delete *fTexture; +} + +hsBool plLayerMovie::ISetFault(const char* errStr) +{ +#ifdef HS_DEBUGGING + char buff[256]; + sprintf(buff, "ERROR %s: %s\n", fMovieName, errStr); + hsStatusMessage(buff); +#endif // HS_DEBUGGING + *fMovieName = 0; + return true; +} + +hsBool plLayerMovie::ISetLength(hsScalar secs) +{ + fLength = secs; + return false; +} + +int plLayerMovie::GetWidth() const +{ + plMipmap *mip = plMipmap::ConvertNoRef( GetTexture() ); + return mip ? mip->GetWidth() : 0; +} + +int plLayerMovie::GetHeight() const +{ + plMipmap *mip = plMipmap::ConvertNoRef( GetTexture() ); + return mip ? mip->GetHeight() : 0; +} + +hsBool plLayerMovie::ISetSize(int width, int height) +{ + fWidth = width; + fHeight = height; + return false; +} + +hsBool plLayerMovie::ISetupBitmap() +{ + if( !GetTexture() ) + { + plMipmap* b = TRACKED_NEW plMipmap( fWidth, fHeight, plMipmap::kARGB32Config, 1 ); + memset(b->GetImage(), 0x10, b->GetHeight() * b->GetRowBytes() ); + b->SetFlags( b->GetFlags() | plMipmap::kDontThrowAwayImage ); + + char name[ 256 ]; + sprintf( name, "%s_BMap", fMovieName ); + hsgResMgr::ResMgr()->NewKey( name, b, plLocation::kGlobalFixedLoc ); + + *fTexture = (plBitmap *)b; + } + + return false; +} + +hsBool plLayerMovie::ICheckBitmap() +{ + if( !GetTexture() ) + ISetupBitmap(); + + return false; +} + +hsBool plLayerMovie::IMovieIsIdle() +{ + IRelease(); + + return false; +} + +hsBool plLayerMovie::ICurrentFrameDirty(double wSecs) +{ + hsScalar secs = fTimeConvert.WorldToAnimTime(wSecs); + UInt32 frame = ISecsToFrame(secs); + if( frame == fCurrentFrame ) + return false; + fCurrentFrame = frame; + + return true; +} + +UInt32 plLayerMovie::Eval(double wSecs, UInt32 frame, UInt32 ignore) +{ + UInt32 dirty = plLayerAnimation::Eval(wSecs, frame, ignore); + + if( !IGetFault() && !(ignore & kTexture) ) + { + if( ICurrentFrameDirty(wSecs) ) + { + if( IGetCurrentFrame() ) + ISetFault("Getting current frame"); + + if( GetTexture() ) + { + hsGDeviceRef* ref = GetTexture()->GetDeviceRef(); + if( ref ) + ref->SetDirty(true); + } + } + else + if( IsStopped() ) + { + IMovieIsIdle(); + } + + dirty |= kTexture; + } + return dirty; +} + +void plLayerMovie::Read(hsStream* s, hsResMgr* mgr) +{ + plLayerAnimation::Read(s, mgr); + + delete [] fMovieName; + int len = s->ReadSwap32(); + if( len ) + { + fMovieName = TRACKED_NEW char[len+1]; + s->Read(len, fMovieName); + fMovieName[len] = 0; + } + else + { + hsAssert(false, "Reading empty string for movie name"); + fMovieName = nil; + } +} + +void plLayerMovie::Write(hsStream* s, hsResMgr* mgr) +{ + plLayerAnimation::Write(s, mgr); + + int len = hsStrlen(fMovieName); + s->WriteSwap32(len); + if( len ) + s->Write(len, fMovieName); +} + +void plLayerMovie::SetMovieName(const char* n) +{ + delete [] fMovieName; + fMovieName = hsStrcpy(n); +} + +hsBool plLayerMovie::MsgReceive(plMessage* msg) +{ + return plLayerAnimation::MsgReceive(msg); +} + +void plLayerMovie::DefaultMovie() +{ +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerMovie.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerMovie.h new file mode 100644 index 00000000..b67bb34f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/FeatureLib/pfSurface/plLayerMovie.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLayerMovie_inc +#define plLayerMovie_inc + +#include "../plSurface/plLayerAnimation.h" +#include "../plInterp/plAnimTimeConvert.h" + +class plMessage; +class hsStream; +class hsResMgr; + +class plLayerMovie : public plLayerAnimation +{ +protected: + char* fMovieName; + +// plAnimTimeConvert fTimeConvert; + + Int32 fCurrentFrame; + hsScalar fLength; + UInt32 fWidth, fHeight; + + virtual Int32 ISecsToFrame(hsScalar secs) = 0; + + hsBool IGetFault() const { return !(fMovieName && *fMovieName); } + hsBool ISetFault(const char* errStr); + hsBool ICheckBitmap(); + hsBool IMovieIsIdle(); // will call IRelease(); + hsBool ISetupBitmap(); + hsBool ISetSize(int w, int h); + hsBool ISetLength(hsScalar secs); + hsBool ICurrentFrameDirty(double wSecs); + + virtual hsBool IInit() = 0; // Load header etc, must call ISetSize(w, h), ISetLength(s) + virtual hsBool IGetCurrentFrame() = 0; // Load fCurrentFrame into bitmap + virtual hsBool IRelease() = 0; // release any system resources. +public: + plLayerMovie(); + virtual ~plLayerMovie(); + + CLASSNAME_REGISTER( plLayerMovie ); + GETINTERFACE_ANY( plLayerMovie, plLayerAnimation ); + + virtual UInt32 Eval(double secs, UInt32 frame, UInt32 ignore); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + hsBool IsStopped() { return fTimeConvert.IsStopped(); } + + void SetMovieName(const char* n); + const char* GetMovieName() const { return fMovieName; } + + virtual hsBool MsgReceive(plMessage* msg); + + // Movie specific + int GetWidth() const; + int GetHeight() const; + hsScalar GetLength() const { return fLength; } + + virtual void DefaultMovie(); +}; + +#endif // plLayerMovie_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsGMatState.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsGMatState.h new file mode 100644 index 00000000..1308f244 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsGMatState.h @@ -0,0 +1,253 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsGMatState_inc +#define hsGMatState_inc + +#include "hsColorRGBA.h" + +class hsStream; + +class hsGMatState { +public: +enum hsGMatBlendFlags { + kBlendTest = 0x1, // dev + // Rest of blends are mutually exclusive + kBlendAlpha = 0x2, // dev + kBlendMult = 0x4, // dev + kBlendAdd = 0x8, // dev + kBlendAddColorTimesAlpha = 0x10, // dev + kBlendAntiAlias = 0x20, + kBlendDetail = 0x40, + kBlendNoColor = 0x80, // dev + kBlendMADD = 0x100, + kBlendDot3 = 0x200, + kBlendAddSigned = 0x400, + kBlendAddSigned2X = 0x800, + kBlendMask = kBlendAlpha + | kBlendMult + | kBlendAdd + | kBlendAddColorTimesAlpha + | kBlendDetail + | kBlendMADD + | kBlendDot3 + | kBlendAddSigned + | kBlendAddSigned2X, + kBlendInvertAlpha = 0x1000, // dev + kBlendInvertColor = 0x2000, // dev + kBlendAlphaMult = 0x4000, + kBlendAlphaAdd = 0x8000, + kBlendNoVtxAlpha = 0x10000, + kBlendNoTexColor = 0x20000, + kBlendNoTexAlpha = 0x40000, + kBlendInvertVtxAlpha = 0x80000, // Invert ONLY the vertex alpha source + kBlendAlphaAlways = 0x100000, // Alpha test always passes (even for alpha=0). + kBlendInvertFinalColor = 0x200000, + kBlendInvertFinalAlpha = 0x400000, + kBlendEnvBumpNext = 0x800000, + kBlendSubtract = 0x1000000, + kBlendRevSubtract = 0x2000000, + kBlendAlphaTestHigh = 0x4000000 +}; +enum hsGMatClampFlags { + + kClampTextureU = 0x1, // dev + kClampTextureV = 0x2, // dev + kClampTexture = 0x3 // dev +}; + +enum hsGMatShadeFlags { + + kShadeSoftShadow = 0x1, // view, dev + kShadeNoProjectors = 0x2, // projector + kShadeEnvironMap = 0x4, // dev, load + kShadeVertexShade = 0x20, // dev + kShadeNoShade = 0x40, // view,dev + kShadeBlack = kShadeNoShade, + kShadeSpecular = 0x80, // view, dev + //kShadeNoFog = 0x100, // dev + kShadeWhite = 0x200, + kShadeSpecularAlpha = 0x400, + kShadeSpecularColor = 0x800, + kShadeSpecularHighlight = 0x1000, + kShadeVertColShade = 0x2000, + kShadeInherit = 0x4000, + kShadeIgnoreVtxIllum = 0x8000, + kShadeEmissive = 0x10000, // Moved here 8.27 mcn. Only really sane to use with kMiscEndPassHere + kShadeReallyNoFog = 0x20000 +}; + +enum hsGMatZFlags { + kZIncLayer = 0x1, // dev + kZClearZ = 0x4, // dev + kZNoZRead = 0x8, // dev + kZNoZWrite = 0x10, + kZMask = kZNoZWrite | kZClearZ | kZNoZRead, + kZLODBias = 0x20 +}; + +enum hsGMatMiscFlags { + kMiscWireFrame = 0x1, // dev (running out of bits) + kMiscDrawMeshOutlines = 0x2, // dev, currently unimplemented + kMiscTwoSided = 0x4, // view,dev + kMiscDrawAsSplats = 0x8, // dev? bwt + kMiscAdjustPlane = 0x10, + kMiscAdjustCylinder = 0x20, + kMiscAdjustSphere = 0x40, + kMiscAdjust = kMiscAdjustPlane | kMiscAdjustCylinder| kMiscAdjustSphere, + kMiscTroubledLoner = 0x80, + kMiscBindSkip = 0x100, + kMiscBindMask = 0x200, + kMiscBindNext = 0x400, + kMiscLightMap = 0x800, + kMiscUseReflectionXform = 0x1000, // Use the calculated reflection environment + // texture transform instead of layer->GetTransform() + kMiscPerspProjection = 0x2000, + kMiscOrthoProjection = 0x4000, + kMiscProjection = kMiscPerspProjection | kMiscOrthoProjection, + + kMiscRestartPassHere = 0x8000, // Tells pipeline to start a new pass beginning with this layer + // Kinda like troubledLoner, but only cuts off lower layers, not + // higher ones (kMiscBindNext sometimes does this by implication) + + kMiscBumpLayer = 0x10000, + kMiscBumpDu = 0x20000, + kMiscBumpDv = 0x40000, + kMiscBumpDw = 0x80000, + kMiscBumpChans = kMiscBumpDu | kMiscBumpDv | kMiscBumpDw, + + kMiscNoShadowAlpha = 0x100000, + kMiscUseRefractionXform = 0x200000, // Use a refraction-like hack. + kMiscCam2Screen = 0x400000, // Expects tex coords to be XYZ in camera space. Does a cam to screen (not NDC) projection + // and swaps Z with W, so that the texture projection can produce projected 2D screen coordinates. + + kAllMiscFlags = 0xffffffff +}; +enum StateIdx { + kBlend, + kClamp, + kShade, + kZ, + kMisc +}; + UInt32 fBlendFlags; + UInt32 fClampFlags; + UInt32 fShadeFlags; + UInt32 fZFlags; + UInt32 fMiscFlags; + + static hsBool Differs(UInt32 mine, UInt32 hers, UInt32 mask) + { + return (mine & mask) ^ (hers & mask); + } + + static hsBool Differs(UInt32 mine, UInt32 hers) + { + return mine ^ hers; + } + + hsBool operator!=(const hsGMatState& other) + { + return ((fBlendFlags ^ other.fBlendFlags) + | (fClampFlags ^ other.fClampFlags) + | (fShadeFlags ^ other.fShadeFlags) + | (fZFlags ^ other.fZFlags) + | (fMiscFlags ^ other.fMiscFlags)); + } + UInt32 Value(int i) const + { + switch(i) + { + case kBlend: + return fBlendFlags; + case kClamp: + return fClampFlags; + case kShade: + return fShadeFlags; + case kZ: + return fZFlags; + case kMisc: + return fMiscFlags; + } + hsAssert(false, "Bad param"); + return fBlendFlags; + } + UInt32& operator[](const int i) + { + switch(i) + { + case kBlend: + return fBlendFlags; + case kClamp: + return fClampFlags; + case kShade: + return fShadeFlags; + case kZ: + return fZFlags; + case kMisc: + return fMiscFlags; + } + hsAssert(false, "Bad param"); + return fBlendFlags; + } + hsGMatState& operator|=(const hsGMatState& other) + { + fBlendFlags |= other.fBlendFlags; + fClampFlags |= other.fClampFlags; + fShadeFlags |= other.fShadeFlags; + fZFlags |= other.fZFlags; + fMiscFlags |= other.fMiscFlags; + return *this; + } + hsGMatState& operator+=(const hsGMatState& other) + { + return operator|=(other); + } + hsGMatState& operator-=(const hsGMatState& other) + { + fBlendFlags &= ~other.fBlendFlags; + fClampFlags &= ~other.fClampFlags; + fShadeFlags &= ~other.fShadeFlags; + fZFlags &= ~other.fZFlags; + fMiscFlags &= ~other.fMiscFlags; + return *this; + } + + inline void Read(hsStream* s); + inline void Write(hsStream* s); + + hsGMatState(UInt32 blend=0, UInt32 clamp=0, UInt32 shade=0, UInt32 z=0, UInt32 misc=0) + : fBlendFlags(blend), + fClampFlags(clamp), + fShadeFlags(shade), + fZFlags(z), + fMiscFlags(misc) {} + void Reset() { fBlendFlags = fClampFlags = fShadeFlags = fZFlags = fMiscFlags = 0; } + inline void Clear(const hsGMatState& state); + inline void Composite(const hsGMatState& want, const hsGMatState& on, const hsGMatState& off); +}; + + +#endif // hsGMatState_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsGMatState.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsGMatState.inl new file mode 100644 index 00000000..2d8a4786 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsGMatState.inl @@ -0,0 +1,53 @@ + +#ifndef hsGMatStateInline_inc +#define hsGMatStateInline_inc + +#include "hsGMatState.h" +#include "hsStream.h" + +void hsGMatState::Read(hsStream* s) +{ + fBlendFlags = s->ReadSwap32(); + fClampFlags = s->ReadSwap32(); + fShadeFlags = s->ReadSwap32(); + fZFlags = s->ReadSwap32(); + fMiscFlags = s->ReadSwap32(); +} + +void hsGMatState::Write(hsStream* s) +{ + s->WriteSwap32(fBlendFlags); + s->WriteSwap32(fClampFlags); + s->WriteSwap32(fShadeFlags); + s->WriteSwap32(fZFlags); + s->WriteSwap32(fMiscFlags); +} + +void hsGMatState::Clear(const hsGMatState& state) +{ + fBlendFlags &= ~state.fBlendFlags; + fClampFlags &= ~state.fClampFlags; + fShadeFlags &= ~state.fShadeFlags; + fZFlags &= ~state.fZFlags; + fMiscFlags &= ~state.fMiscFlags; +} + +void hsGMatState::Composite(const hsGMatState& want, const hsGMatState& on, const hsGMatState& off) +{ + fBlendFlags = want.fBlendFlags & ~off.fBlendFlags; + if( !(fBlendFlags & (kBlendMask & ~(kBlendAlpha|kBlendAntiAlias))) ) + fBlendFlags |= on.fBlendFlags; + + fClampFlags = (want.fClampFlags | on.fClampFlags) & ~off.fClampFlags; + + fShadeFlags = (want.fShadeFlags | on.fShadeFlags) & ~off.fShadeFlags; +#if 0 // This restriction is only valid for glide - handle in glideDevice + if( fBlendFlags & (kBlendAntiAlias | kBlendAlpha) ) + fShadeFlags &= ~(kShadeSpecularAlpha | kShadeSpecularHighlight); +#endif // This restriction is only valid for glide - handle in glideDevice + + fZFlags = (want.fZFlags | on.fZFlags) & ~off.fZFlags; + fMiscFlags = (want.fMiscFlags | on.fMiscFlags) & ~off.fMiscFlags; +} + +#endif // hsGMatStateInline_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsResMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsResMgr.h new file mode 100644 index 00000000..16376fd8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsResMgr.h @@ -0,0 +1,141 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsResMgr_inc +#define hsResMgr_inc + +#include "hsTypes.h" +#include "hsRefCnt.h" +#include "plLoadMask.h" +#include "plRefFlags.h" +#include "../pnKeyedObject/plKey.h" + +class hsStream; +class plKeyImp; +class hsKeyedObject; +class plRefMsg; +class plUoid; +class plLocation; +class plCreatable; +class plDispatchBase; + +class hsResMgr : public hsRefCnt +{ +public: + //--------------------------- + // Load and Unload + //--------------------------- + virtual void Load (const plKey& objKey)=0; // places on list to be loaded + virtual hsBool Unload(const plKey& objKey)=0; // Unregisters (deletes) an object, Return true if successful + virtual plKey CloneKey(const plKey& objKey)=0; + + //--------------------------- + // Finding Functions + //--------------------------- + virtual plKey FindKey(const plUoid& uoid)=0; + + //--------------------------- + // Establish reference linkage + //--------------------------- + virtual hsBool AddViaNotify(const plKey& sentKey, plRefMsg* msg, plRefFlags::Type flags)=0; + virtual hsBool AddViaNotify(plRefMsg* msg, plRefFlags::Type flags)=0; // msg->fRef->GetKey() == sentKey + + virtual hsBool SendRef(const plKey& key, plRefMsg* refMsg, plRefFlags::Type flags)=0; + virtual hsBool SendRef(hsKeyedObject* ko, plRefMsg* refMsg, plRefFlags::Type flags)=0; + + //--------------------------- + // Reading and Writing keys + //--------------------------- + // Read a Key in, and Notify me when the Object is loaded + virtual plKey ReadKeyNotifyMe(hsStream* s, plRefMsg* retMsg, plRefFlags::Type flags)=0; + // Just read the Key data in and find a match in the registry and return it. + virtual plKey ReadKey(hsStream* s)=0; + + // For convenience you can write a key using the KeyedObject or the Key...same result + virtual void WriteKey(hsStream* s, hsKeyedObject* obj)=0; + virtual void WriteKey(hsStream* s, const plKey& key)=0; + + //--------------------------- + // Reading and Writing Objects directly + //--------------------------- + virtual plCreatable* ReadCreatable(hsStream* s)=0; + virtual void WriteCreatable(hsStream* s, plCreatable* cre)=0; + + virtual plCreatable* ReadCreatableVersion(hsStream* s)=0; + virtual void WriteCreatableVersion(hsStream* s, plCreatable* cre)=0; + + //--------------------------- + // Registry Modification Functions + //--------------------------- + virtual plKey NewKey(const char* name, hsKeyedObject* object, const plLocation& loc, const plLoadMask& m = plLoadMask::kAlways)=0; + virtual plKey NewKey(plUoid& newUoid, hsKeyedObject* object)=0; + + virtual plDispatchBase* Dispatch()=0; + + virtual void BeginShutdown() {} + +protected: + friend class hsKeyedObject; + friend class plKey; + friend class plKeyImp; + friend class plArmatureMod; // Temp hack until a findkey/clone issue is fixed. -Bob + + virtual plKey ReRegister(const char *nm, const plUoid& oid)=0; + virtual hsBool ReadObject(plKeyImp* key)=0; // plKeys call this when needed + + // Sets a key as used or unused in the registry. When all keys in a page of a + // particular type in an page are unused, we can free the memory associated with them. + // Only called by plKeyImp + virtual void IKeyReffed(plKeyImp* key)=0; + virtual void IKeyUnreffed(plKeyImp* key)=0; + +protected: // hsgResMgr only + friend class hsgResMgr; + virtual hsBool IReset()=0; + virtual hsBool IInit()=0; + virtual void IShutdown()=0; +}; + + +class hsgResMgr +{ +private: + static hsResMgr* fResMgr; + +public: + static hsResMgr* ResMgr() { return (hsResMgr*)fResMgr; } + + static plDispatchBase* Dispatch() { hsAssert(fResMgr, "No resmgr"); return fResMgr->Dispatch(); } + + static hsBool Init(hsResMgr* m); + static hsBool Reset() { return fResMgr->IReset(); } + static void Shutdown(); + + static hsBool SendRef(plKey& key, plRefMsg* refMsg, plRefFlags::Type flags) { return fResMgr->SendRef(key, refMsg, flags); } + static hsBool SendRef(hsKeyedObject* ko, plRefMsg* refMsg, plRefFlags::Type flags) { return fResMgr->SendRef(ko, refMsg, flags); } +}; + +#endif // hsResMgr_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsTimer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsTimer.h new file mode 100644 index 00000000..279825c7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/hsTimer.h @@ -0,0 +1,156 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsTimer_Defined +#define hsTimer_Defined + +#include "hsWide.h" +#include "hsScalar.h" + +#if !HS_CAN_USE_FLOAT +#error "Unsupported without double's" +#endif // !HS_CAN_USE_FLOAT + +class plTimerShare +{ +protected: + mutable hsBool fFirstTime; + mutable hsWide fRawTimeZero; + mutable hsBool fResetSmooth; + + enum { + kSmoothBuffLen = 10 + }; + double fSmoothBuff[kSmoothBuffLen]; + int fCurrSlot; + + hsScalar fSysTimeScale; + double fRealSeconds; + double fSysSeconds; + hsScalar fDelSysSeconds; + hsScalar fFrameTimeInc; + hsBool fRunningFrameTime; + hsScalar fTimeClampSecs; + hsScalar fSmoothingClampSecs; + hsBool fClamping; + + hsWide* FactorInTimeZero(hsWide* ticks) const; + + double GetSeconds() const; + double GetMilliSeconds() const; + + hsWide* GetRawTicks(hsWide* ticks) const; + + double RawTicksToDSeconds(const hsWide& ticks); + hsWide DSecondsToRawTicks(double secs); + + hsScalar GetDelSysSeconds() const { return fDelSysSeconds; } + double GetSysSeconds() const { return fSysSeconds; } + double IncSysSeconds(); + + void SetRealTime(hsBool realTime); + hsBool IsRealTime() const { return !fRunningFrameTime; } + + void SetFrameTimeInc(hsScalar inc) { fFrameTimeInc = inc; } + + void SetTimeScale(hsScalar s) { fSysTimeScale = s; } + hsScalar GetTimeScale() const { return fSysTimeScale; } + + void SetTimeClamp(hsScalar secs) { fTimeClampSecs = secs; } + void SetSmoothingCap(hsScalar secs) { fSmoothingClampSecs = secs; } + hsScalar GetTimeClamp() const { return fTimeClampSecs; } + hsBool IsClamping() const { return fClamping; } + + friend class hsTimer; +public: + plTimerShare(); + ~plTimerShare(); +}; + +class hsTimer +{ +protected: + static const double fPrecTicksPerSec; + static const hsWide fRawBase; + + static hsWide IInitRawBase(); + + static plTimerShare* fTimer; +public: + + static hsBool VerifyRawBase() { return fRawBase == IInitRawBase(); } + static const hsWide& GetRawBase() { return fRawBase; } + + static hsWide* GetRawTicks(hsWide* ticks) { return fTimer->GetRawTicks(ticks); } + + static double GetSeconds() { return fTimer->GetSeconds(); } + static double GetMilliSeconds() { return fTimer->GetMilliSeconds(); } + + static double RawTicksToDSeconds(const hsWide& ticks) { return fTimer->RawTicksToDSeconds(ticks); } + static hsWide DSecondsToRawTicks(double secs) { return fTimer->DSecondsToRawTicks(secs); } + + static hsScalar GetDelSysSeconds() { return fTimer->GetDelSysSeconds(); } + static double GetSysSeconds() { return fTimer->GetSysSeconds(); } + + static double IncSysSeconds() { return fTimer->IncSysSeconds(); } + + static void SetRealTime(hsBool realTime) { fTimer->SetRealTime(realTime); } + static hsBool IsRealTime() { return fTimer->IsRealTime(); } + + static void SetFrameTimeInc(hsScalar inc) { fTimer->SetFrameTimeInc(inc); } + + static void SetTimeScale(hsScalar s) { fTimer->SetTimeScale(s); } + static hsScalar GetTimeScale() { return fTimer->GetTimeScale(); } + + static void SetTimeClamp(hsScalar secs) { fTimer->SetTimeClamp(secs); } + static void SetTimeSmoothingClamp(hsScalar secs) { fTimer->SetSmoothingCap(secs); } + static hsScalar GetTimeClamp() { return fTimer->GetTimeClamp(); } + static hsBool IsClamping() { return fTimer->IsClamping(); } + + /////////////////////////// + // Precision timer routines - these are stateless and implemented as statics. + /////////////////////////// + static UInt32 GetPrecTickCount(); + static double GetPrecTicksPerSec(); + static UInt32 PrecSecsToTicks(hsScalar secs); + static double PrecTicksToSecs(UInt32 ticks); + static double PrecTicksToHz(UInt32 ticks); + + // If you need to time something longer than 20 seconds, use this instead of + // the precision timer. It works the same, it just gives you full resolution. + static UInt64 GetFullTickCount(); + static float FullTicksToMs(UInt64 ticks); + + // + // Pass GetTheTimer() into other process space, and then call SetTheTimer() on it. + static void SetTheTimer(plTimerShare* timer); + static plTimerShare* GetTheTimer() { return fTimer; } +}; + + + +#endif + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plAudible.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plAudible.h new file mode 100644 index 00000000..8210fed8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plAudible.h @@ -0,0 +1,102 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAudible_inc +#define plAudible_inc + +#include "hsTemplates.h" +#include "../pnKeyedObject/hsKeyedObject.h" + +class plSound; +class hsStream; +class hsResMgr; +struct hsMatrix44; +struct hsVector3; +struct hsPoint3; +class hsBounds3Ext; +class plSoundMsg; +class plEventCallbackMsg; + +class plAudible : public hsKeyedObject +{ +public: + + CLASSNAME_REGISTER( plAudible ); + GETINTERFACE_ANY( plAudible, hsKeyedObject ); + + virtual plAudible& SetProperty(int prop, hsBool on) = 0; + virtual hsBool GetProperty(int prop) = 0; + + virtual plAudible& SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index = -1) { return *this; } + + virtual void Read(hsStream* s, hsResMgr* mgr){hsKeyedObject::Read(s, mgr);} + virtual void Write(hsStream* s, hsResMgr* mgr){hsKeyedObject::Write(s, mgr);} + + virtual void SetSceneObject(plKey obj) = 0; + virtual plKey GetSceneObject() const = 0; + + // These two should only be called by the SceneNode + virtual void SetSceneNode(plKey node) = 0; + virtual plKey GetSceneNode() const = 0; + + virtual void Play(int index = -1) = 0; + virtual void SynchedPlay(int index = -1) = 0; + virtual void Stop(int index = -1) = 0; + virtual void FastForwardPlay(int index = -1) = 0; + virtual void FastForwardToggle( int index = -1) = 0; + virtual void SetMin(const hsScalar m,int index = -1) = 0; // sets minimum falloff distance + virtual void SetMax(const hsScalar m,int index = -1) = 0; // sets maximum falloff distance + virtual hsScalar GetMin(int index = -1) const = 0; + virtual hsScalar GetMax(int index = -1) const = 0; + virtual void SetVelocity(const hsVector3 vel,int index = -1) = 0; + virtual hsVector3 GetVelocity(int index = -1) const = 0; + virtual hsPoint3 GetPosition(int index = -1) = 0; + virtual void SetLooping(hsBool loop,int index = -1) = 0; // sets continuous loop or stops looping + virtual hsBool IsPlaying(int index = -1) = 0; + virtual void SetTime(double t, int index = -1) = 0; + virtual void Activate() = 0; + virtual void DeActivate() = 0; + virtual void RemoveCallbacks(plSoundMsg* pMsg) = 0; + virtual void AddCallbacks(plSoundMsg* pMsg) = 0; + virtual void GetStatus(plSoundMsg* pMsg) = 0; + virtual int GetNumSounds() const = 0; + virtual plSound* GetSound(int i) const = 0; + virtual int GetSoundIndex(const char *keyname) const = 0; + virtual void Init(hsBool isLocal){;} + virtual void SetVolume(const float volume,int index = -1) = 0; + virtual void SetMuted( hsBool muted, int index = -1 ) = 0; + virtual void ToggleMuted( int index = -1 ) = 0; + virtual void SetTalkIcon(int index, UInt32 str) = 0; + virtual void ClearTalkIcon() = 0; + virtual void SetFilename(int index, const char *filename, hsBool isCompressed) = 0; // set filename for a streaming sound + virtual void SetFadeIn( const int type, const float length, int index = -1 ) = 0; + virtual void SetFadeOut( const int type, const float length, int index = -1 ) = 0; + +protected: + hsTArray fCallbacks; +}; + +#endif // plAudible_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCCRMgrBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCCRMgrBase.h new file mode 100644 index 00000000..61b382e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCCRMgrBase.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plCCRMgrBase_inc +#define plCCRMgrBase_inc + +// +// Abstract base class for CCR Mgr +// + +#include "../pnKeyedObject/hsKeyedObject.h" + +class plCCRMgrBase : public hsKeyedObject +{ +protected: + static plCCRMgrBase* fBaseInstance; +public: + static plCCRMgrBase* GetInstance() { return fBaseInstance; } +}; + + +#endif // plCCRMgrBase_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plClassIndexMacros.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plClassIndexMacros.h new file mode 100644 index 00000000..4b35a1e8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plClassIndexMacros.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plClassIndexMacros_inc +#define plClassIndexMacros_inc + + +// Macros for deriving the class index enum from the class name. +// These should always be used in place of 'plFooIDX'. +// Place before includes to avoid recursive include prob. +// --For use within plCreatableIndex +#define CLASS_INDEX(plClassName) plClassName##IDX +// --For use within plExternalCreatableIndex +#define EXTERN_CLASS_INDEX(plClassName) plClassName##IDX +// --For use outside of plCreatableIndex +#define CLASS_INDEX_SCOPED(plClassName) plCreatableIndex::CLASS_INDEX(plClassName) +// --For use outside of plExternalCreatableIndex +#define EXTERN_CLASS_INDEX_SCOPED(plClassName) plExternalCreatableIndex::EXTERN_CLASS_INDEX(plClassName) + +// Macros for the start and end of the class index list +#define CLASS_INDEX_LIST_START const int KEYED_OBJ_DELINEATOR = 512; \ + const int EXTERNAL_KEYED_DLL_BEGIN = 256; \ + const int EXTERNAL_KEYED_DLL_END = 511; \ + const int EXTERNAL_NONKEYED_DLL_BEGIN = 1436; \ + const int EXTERNAL_NONKEYED_DLL_END = 1536; \ + class plCreatableIndex { public: enum { +#define CLASS_INDEX_LIST_END plNumClassIndices = EXTERNAL_NONKEYED_DLL_END + 1, }; }; +// Macro to mark which class index is the start of the nonkeyed object section +#define CLASS_INDEX_NONKEYED_OBJ_START kKeyedObjDelineator = KEYED_OBJ_DELINEATOR-1, + +#define CLASS_INDEX_DATABASE_STRUCT_INDEXES_START kDatabaseStructIndexesStart, +#define CLASS_INDEX_DATABASE_STRUCT_INDEXES_END kDatabaseStructIndexesEnd=kDatabaseStructIndexesStart+100, + + +#endif // plClassIndexMacros_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h new file mode 100644 index 00000000..d0c89e92 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h @@ -0,0 +1,936 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCreatableIndex_inc +#define plCreatableIndex_inc + +#include "plClassIndexMacros.h" // for CLASS_INDEX macro defn + +CLASS_INDEX_LIST_START + //--------------------------------------------------------------------- + // hsKeyedObjects should appear in the first section of the enum list + // And everything else in the next section + // Otherwise you will get an Assert + //--------------------------------------------------------------------- + CLASS_INDEX(plSceneNode), + CLASS_INDEX(plSceneObject), + CLASS_INDEX(hsKeyedObject), + CLASS_INDEX(plBitmap), + CLASS_INDEX(plMipmap), + CLASS_INDEX(plCubicEnvironmap), + CLASS_INDEX(plLayer), + CLASS_INDEX(hsGMaterial), + CLASS_INDEX(plParticleSystem), + CLASS_INDEX(plParticleEffect), + CLASS_INDEX(plParticleCollisionEffectBeat), + CLASS_INDEX(plParticleFadeVolumeEffect), + CLASS_INDEX(plBoundInterface), + CLASS_INDEX(plRenderTarget), + CLASS_INDEX(plCubicRenderTarget), + CLASS_INDEX(plCubicRenderTargetModifier), + CLASS_INDEX(plObjInterface), + CLASS_INDEX(plAudioInterface), + CLASS_INDEX(plAudible), + CLASS_INDEX(plAudibleNull), + CLASS_INDEX(plWinAudible), + CLASS_INDEX(plCoordinateInterface), + CLASS_INDEX(plDrawInterface), + CLASS_INDEX(plDrawable), + CLASS_INDEX(plDrawableMesh), + CLASS_INDEX(plDrawableIce), + CLASS_INDEX(plPhysical), + CLASS_INDEX(plPhysicalMesh), + CLASS_INDEX(plSimulationInterface), + CLASS_INDEX(plCameraModifier), + CLASS_INDEX(plModifier), + CLASS_INDEX(plSingleModifier), + CLASS_INDEX(plSimpleModifier), + CLASS_INDEX(pfSecurePreloader), + CLASS_INDEX(UNUSED_plRandomTMModifier), + CLASS_INDEX(plInterestingModifier), + CLASS_INDEX(plDetectorModifier), + CLASS_INDEX(plSimplePhysicalMesh), + CLASS_INDEX(plCompoundPhysicalMesh), + CLASS_INDEX(plMultiModifier), + CLASS_INDEX(plSynchedObject), + CLASS_INDEX(plSoundBuffer), + CLASS_INDEX(UNUSED_plAliasModifier), + CLASS_INDEX(plPickingDetector), + CLASS_INDEX(plCollisionDetector), + CLASS_INDEX(plLogicModifier), + CLASS_INDEX(plConditionalObject), + CLASS_INDEX(plANDConditionalObject), + CLASS_INDEX(plORConditionalObject), + CLASS_INDEX(plPickedConditionalObject), + CLASS_INDEX(plActivatorConditionalObject), + CLASS_INDEX(plTimerCallbackManager), + CLASS_INDEX(plKeyPressConditionalObject), + CLASS_INDEX(plAnimationEventConditionalObject), + CLASS_INDEX(plControlEventConditionalObject), + CLASS_INDEX(plObjectInBoxConditionalObject), + CLASS_INDEX(plLocalPlayerInBoxConditionalObject), + CLASS_INDEX(plObjectIntersectPlaneConditionalObject), + CLASS_INDEX(plLocalPlayerIntersectPlaneConditionalObject), + CLASS_INDEX(plPortalDrawable), + CLASS_INDEX(plPortalPhysical), + CLASS_INDEX(plSpawnModifier), + CLASS_INDEX(plFacingConditionalObject), + CLASS_INDEX(plPXPhysical), + CLASS_INDEX(plViewFaceModifier), + CLASS_INDEX(plLayerInterface), + CLASS_INDEX(plLayerWrapper), + CLASS_INDEX(plLayerAnimation), + CLASS_INDEX(plLayerDepth), + CLASS_INDEX(plLayerMovie), + CLASS_INDEX(plLayerBink), + CLASS_INDEX(plLayerAVI), + CLASS_INDEX(plSound), + CLASS_INDEX(plWin32Sound), + CLASS_INDEX(plLayerOr), + CLASS_INDEX(plAudioSystem), + CLASS_INDEX(plDrawableSpans), + CLASS_INDEX(UNUSED_plDrawablePatchSet), + CLASS_INDEX(plInputManager), + CLASS_INDEX(plLogicModBase), + CLASS_INDEX(plFogEnvironment), + CLASS_INDEX(plNetApp), + CLASS_INDEX(plNetClientMgr), + CLASS_INDEX(pl2WayWinAudible), + CLASS_INDEX(plLightInfo), + CLASS_INDEX(plDirectionalLightInfo), + CLASS_INDEX(plOmniLightInfo), + CLASS_INDEX(plSpotLightInfo), + CLASS_INDEX(plLightSpace), + CLASS_INDEX(plNetClientApp), + CLASS_INDEX(plNetServerApp), + CLASS_INDEX(plClient), + CLASS_INDEX(UNUSED_plCompoundTMModifier), + CLASS_INDEX(plCameraBrain), + CLASS_INDEX(plCameraBrain_Default), + CLASS_INDEX(plCameraBrain_Drive), + CLASS_INDEX(plCameraBrain_Fixed), + CLASS_INDEX(plCameraBrain_FixedPan), + CLASS_INDEX(pfGUIClickMapCtrl), + CLASS_INDEX(plListener), + CLASS_INDEX(plAvatarMod), + CLASS_INDEX(plAvatarAnim), + CLASS_INDEX(plAvatarAnimMgr), + CLASS_INDEX(plOccluder), + CLASS_INDEX(plMobileOccluder), + CLASS_INDEX(plLayerShadowBase), + CLASS_INDEX(plLimitedDirLightInfo), + CLASS_INDEX(plAGAnim), + CLASS_INDEX(plAGModifier), + CLASS_INDEX(plAGMasterMod), + CLASS_INDEX(plCameraBrain_Avatar), + CLASS_INDEX(plCameraRegionDetector), + CLASS_INDEX(plCameraBrain_FP), + CLASS_INDEX(plLineFollowMod), + CLASS_INDEX(plLightModifier), + CLASS_INDEX(plOmniModifier), + CLASS_INDEX(plSpotModifier), + CLASS_INDEX(plLtdDirModifier), + CLASS_INDEX(plSeekPointMod), + CLASS_INDEX(plOneShotMod), + CLASS_INDEX(plRandomCommandMod), + CLASS_INDEX(plRandomSoundMod), + CLASS_INDEX(plPostEffectMod), + CLASS_INDEX(plObjectInVolumeDetector), + CLASS_INDEX(plResponderModifier), + CLASS_INDEX(plAxisAnimModifier), + CLASS_INDEX(plLayerLightBase), + CLASS_INDEX(plFollowMod), + CLASS_INDEX(plTransitionMgr), + CLASS_INDEX(UNUSED___plInventoryMod), + CLASS_INDEX(UNUSED___plInventoryObjMod), + CLASS_INDEX(plLinkEffectsMgr), + CLASS_INDEX(plWin32StreamingSound), + CLASS_INDEX(UNUSED___plPythonMod), + CLASS_INDEX(plActivatorActivatorConditionalObject), + CLASS_INDEX(plSoftVolume), + CLASS_INDEX(plSoftVolumeSimple), + CLASS_INDEX(plSoftVolumeComplex), + CLASS_INDEX(plSoftVolumeUnion), + CLASS_INDEX(plSoftVolumeIntersect), + CLASS_INDEX(plSoftVolumeInvert), + CLASS_INDEX(plWin32LinkSound), + CLASS_INDEX(plLayerLinkAnimation), + CLASS_INDEX(plArmatureMod), + CLASS_INDEX(plCameraBrain_Freelook), + CLASS_INDEX(plHavokConstraintsMod), + CLASS_INDEX(plHingeConstraintMod), + CLASS_INDEX(plWheelConstraintMod), + CLASS_INDEX(plStrongSpringConstraintMod), + CLASS_INDEX(plArmatureLODMod), + CLASS_INDEX(plWin32StaticSound), + CLASS_INDEX(pfGameGUIMgr), + CLASS_INDEX(pfGUIDialogMod), + CLASS_INDEX(plCameraBrain1), + CLASS_INDEX(plVirtualCam1), + CLASS_INDEX(plCameraModifier1), + CLASS_INDEX(plCameraBrain1_Drive), + CLASS_INDEX(plCameraBrain1_POA), + CLASS_INDEX(plCameraBrain1_Avatar), + CLASS_INDEX(plCameraBrain1_Fixed), + CLASS_INDEX(plCameraBrain1_POAFixed), + CLASS_INDEX(pfGUIButtonMod), + CLASS_INDEX(plPythonFileMod), + CLASS_INDEX(pfGUIControlMod), + CLASS_INDEX(plExcludeRegionModifier), + CLASS_INDEX(pfGUIDraggableMod), + CLASS_INDEX(plVolumeSensorConditionalObject), + CLASS_INDEX(plVolActivatorConditionalObject), + CLASS_INDEX(plMsgForwarder), + CLASS_INDEX(plBlower), + CLASS_INDEX(pfGUIListBoxMod), + CLASS_INDEX(pfGUITextBoxMod), + CLASS_INDEX(pfGUIEditBoxMod), + CLASS_INDEX(plDynamicTextMap), + CLASS_INDEX(plSittingModifier), + CLASS_INDEX(pfGUIUpDownPairMod), + CLASS_INDEX(pfGUIValueCtrl), + CLASS_INDEX(pfGUIKnobCtrl), + CLASS_INDEX(plAvLadderMod), + CLASS_INDEX(plCameraBrain1_FirstPerson), + CLASS_INDEX(plCloneSpawnModifier), + CLASS_INDEX(plClothingItem), + CLASS_INDEX(plClothingOutfit), + CLASS_INDEX(plClothingBase), + CLASS_INDEX(plClothingMgr), + CLASS_INDEX(pfGUIDragBarCtrl), + CLASS_INDEX(pfGUICheckBoxCtrl), + CLASS_INDEX(pfGUIRadioGroupCtrl), + CLASS_INDEX(pfPlayerBookMod), + CLASS_INDEX(pfGUIDynDisplayCtrl), + CLASS_INDEX(UNUSED_plLayerProject), + CLASS_INDEX(plInputInterfaceMgr), + CLASS_INDEX(plRailCameraMod), + CLASS_INDEX(plMultistageBehMod), + CLASS_INDEX(plCameraBrain1_Circle), + CLASS_INDEX(plParticleWindEffect), + CLASS_INDEX(plAnimEventModifier), + CLASS_INDEX(plAutoProfile), + CLASS_INDEX(pfGUISkin), + CLASS_INDEX(plAVIWriter), + CLASS_INDEX(plParticleCollisionEffect), + CLASS_INDEX(plParticleCollisionEffectDie), + CLASS_INDEX(plParticleCollisionEffectBounce), + CLASS_INDEX(plInterfaceInfoModifier), + CLASS_INDEX(plSharedMesh), + CLASS_INDEX(plArmatureEffectsMgr), + CLASS_INDEX(pfMarkerMgr), + CLASS_INDEX(plVehicleModifier), + CLASS_INDEX(plParticleLocalWind), + CLASS_INDEX(plParticleUniformWind), + CLASS_INDEX(plInstanceDrawInterface), + CLASS_INDEX(plShadowMaster), + CLASS_INDEX(plShadowCaster), + CLASS_INDEX(plPointShadowMaster), + CLASS_INDEX(plDirectShadowMaster), + CLASS_INDEX(plSDLModifier), + CLASS_INDEX(plPhysicalSDLModifier), + CLASS_INDEX(plClothingSDLModifier), + CLASS_INDEX(plAvatarSDLModifier), + CLASS_INDEX(plAGMasterSDLModifier), + CLASS_INDEX(plPythonSDLModifier), + CLASS_INDEX(plLayerSDLModifier), + CLASS_INDEX(plAnimTimeConvertSDLModifier), + CLASS_INDEX(plResponderSDLModifier), + CLASS_INDEX(plSoundSDLModifier), + CLASS_INDEX(plResManagerHelper), + CLASS_INDEX(plAvatarPhysicalSDLModifier), + CLASS_INDEX(plArmatureEffect), + CLASS_INDEX(plArmatureEffectFootSound), + CLASS_INDEX(plEAXListenerMod), + CLASS_INDEX(plDynaDecalMgr), + CLASS_INDEX(plObjectInVolumeAndFacingDetector), + CLASS_INDEX(plDynaFootMgr), + CLASS_INDEX(plDynaRippleMgr), + CLASS_INDEX(plDynaBulletMgr), + CLASS_INDEX(plDecalEnableMod), + CLASS_INDEX(plPrintShape), + CLASS_INDEX(plDynaPuddleMgr), + CLASS_INDEX(pfGUIMultiLineEditCtrl), + CLASS_INDEX(plLayerAnimationBase), + CLASS_INDEX(plLayerSDLAnimation), + CLASS_INDEX(plATCAnim), + CLASS_INDEX(plAgeGlobalAnim), + CLASS_INDEX(plSubworldRegionDetector), + CLASS_INDEX(plAvatarMgr), + CLASS_INDEX(plNPCSpawnMod), + CLASS_INDEX(plActivePrintShape), + CLASS_INDEX(plExcludeRegionSDLModifier), + CLASS_INDEX(plLOSDispatch), + CLASS_INDEX(plDynaWakeMgr), + CLASS_INDEX(plSimulationMgr), + CLASS_INDEX(plWaveSet7), + CLASS_INDEX(plPanicLinkRegion), + CLASS_INDEX(plWin32GroupedSound), + CLASS_INDEX(plFilterCoordInterface), + CLASS_INDEX(plStereizer), + CLASS_INDEX( plCCRMgr ), + CLASS_INDEX( plCCRSpecialist ), + CLASS_INDEX( plCCRSeniorSpecialist ), + CLASS_INDEX( plCCRShiftSupervisor ), + CLASS_INDEX( plCCRGameOperator ), + CLASS_INDEX(plShader), + CLASS_INDEX(plDynamicEnvMap), + CLASS_INDEX(plSimpleRegionSensor), + CLASS_INDEX(plMorphSequence), + CLASS_INDEX(plEmoteAnim), + CLASS_INDEX(plDynaRippleVSMgr), + CLASS_INDEX(UNUSED_plWaveSet6), + CLASS_INDEX(pfGUIProgressCtrl), + CLASS_INDEX(plMaintainersMarkerModifier), + CLASS_INDEX(plMorphSequenceSDLMod), + CLASS_INDEX(plMorphDataSet), + CLASS_INDEX(plHardRegion), + CLASS_INDEX(plHardRegionPlanes), + CLASS_INDEX(plHardRegionComplex), + CLASS_INDEX(plHardRegionUnion), + CLASS_INDEX(plHardRegionIntersect), + CLASS_INDEX(plHardRegionInvert), + CLASS_INDEX(plVisRegion), + CLASS_INDEX(plVisMgr), + CLASS_INDEX(plRegionBase), + CLASS_INDEX(pfGUIPopUpMenu), + CLASS_INDEX(pfGUIMenuItem), + CLASS_INDEX(plCoopCoordinator), + CLASS_INDEX(plFont), + CLASS_INDEX(plFontCache), + CLASS_INDEX(plRelevanceRegion), + CLASS_INDEX(plRelevanceMgr), + CLASS_INDEX(pfJournalBook), + CLASS_INDEX(plLayerTargetContainer), + CLASS_INDEX(plImageLibMod), + CLASS_INDEX(plParticleFlockEffect), + CLASS_INDEX(plParticleSDLMod), + CLASS_INDEX(plAgeLoader), + CLASS_INDEX(plWaveSetBase), + CLASS_INDEX(plPhysicalSndGroup), + CLASS_INDEX(pfBookData), + CLASS_INDEX(plDynaTorpedoMgr), + CLASS_INDEX(plDynaTorpedoVSMgr), + CLASS_INDEX(plClusterGroup), + CLASS_INDEX(plGameMarkerModifier), + CLASS_INDEX(plLODMipmap), + CLASS_INDEX(plSwimDetector), + CLASS_INDEX(plFadeOpacityMod), + CLASS_INDEX(plFadeOpacityLay), + CLASS_INDEX(plDistOpacityMod), + CLASS_INDEX(plArmatureModBase), + CLASS_INDEX(plSwimRegionInterface), + CLASS_INDEX(plSwimCircularCurrentRegion), + CLASS_INDEX(plParticleFollowSystemEffect), + CLASS_INDEX(plSwimStraightCurrentRegion), + CLASS_INDEX(pfObjectFlocker), + CLASS_INDEX(plGrassShaderMod), + CLASS_INDEX(plDynamicCamMap), + CLASS_INDEX(plRidingAnimatedPhysicalDetector), + CLASS_INDEX(plVolumeSensorConditionalObjectNoArbitration), +//--------------------------------------------------------- +// Keyed objects above this line, unkeyed (such as messages) below.. +//--------------------------------------------------------- + + CLASS_INDEX_NONKEYED_OBJ_START + CLASS_INDEX(plObjRefMsg), + CLASS_INDEX(plNodeRefMsg), + CLASS_INDEX(plMessage), + CLASS_INDEX(plRefMsg), + CLASS_INDEX(plGenRefMsg), + CLASS_INDEX(plTimeMsg), + CLASS_INDEX(plAnimCmdMsg), + CLASS_INDEX(plParticleUpdateMsg), + CLASS_INDEX(plLayRefMsg), + CLASS_INDEX(plMatRefMsg), + CLASS_INDEX(plCameraMsg), + CLASS_INDEX(plInputEventMsg), + CLASS_INDEX(plKeyEventMsg), + CLASS_INDEX(plMouseEventMsg), + CLASS_INDEX(plEvalMsg), + CLASS_INDEX(plTransformMsg), + CLASS_INDEX(plControlEventMsg), + CLASS_INDEX(plVaultCCRNode), + CLASS_INDEX(plLOSRequestMsg), + CLASS_INDEX(plLOSHitMsg), + CLASS_INDEX(plSingleModMsg), + CLASS_INDEX(plMultiModMsg), + CLASS_INDEX(plAvatarPhysicsEnableCallbackMsg), + CLASS_INDEX(plMemberUpdateMsg), + CLASS_INDEX(plNetMsgPagingRoom), + CLASS_INDEX(plActivatorMsg), + CLASS_INDEX(plDispatch), + CLASS_INDEX(plReceiver), + CLASS_INDEX(plMeshRefMsg), + CLASS_INDEX(hsGRenderProcs), + CLASS_INDEX(hsSfxAngleFade), + CLASS_INDEX(hsSfxDistFade), + CLASS_INDEX(hsSfxDistShade), + CLASS_INDEX(hsSfxGlobalShade), + CLASS_INDEX(hsSfxIntenseAlpha), + CLASS_INDEX(hsSfxObjDistFade), + CLASS_INDEX(hsSfxObjDistShade), + CLASS_INDEX(hsDynamicValue), + CLASS_INDEX(hsDynamicScalar), + CLASS_INDEX(hsDynamicColorRGBA), + CLASS_INDEX(hsDynamicMatrix33), + CLASS_INDEX(hsDynamicMatrix44), + CLASS_INDEX(plOmniSqApplicator), + CLASS_INDEX(plPreResourceMsg), + CLASS_INDEX(UNUSED_hsDynamicColorRGBA), + CLASS_INDEX(UNUSED_hsDynamicMatrix33), + CLASS_INDEX(UNUSED_hsDynamicMatrix44), + CLASS_INDEX(plController), + CLASS_INDEX(plLeafController), + CLASS_INDEX(plCompoundController), + CLASS_INDEX(UNUSED_plRotController), + CLASS_INDEX(UNUSED_plPosController), + CLASS_INDEX(UNUSED_plScalarController), + CLASS_INDEX(UNUSED_plPoint3Controller), + CLASS_INDEX(UNUSED_plScaleValueController), + CLASS_INDEX(UNUSED_plQuatController), + CLASS_INDEX(UNUSED_plMatrix33Controller), + CLASS_INDEX(UNUSED_plMatrix44Controller), + CLASS_INDEX(UNUSED_plEaseController), + CLASS_INDEX(UNUSED_plSimpleScaleController), + CLASS_INDEX(UNUSED_plSimpleRotController), + CLASS_INDEX(plCompoundRotController), + CLASS_INDEX(UNUSED_plSimplePosController), + CLASS_INDEX(plCompoundPosController), + CLASS_INDEX(plTMController), + CLASS_INDEX(hsFogControl), + CLASS_INDEX(plIntRefMsg), + CLASS_INDEX(plCollisionReactor), + CLASS_INDEX(plCorrectionMsg), + CLASS_INDEX(plPhysicalModifier), + CLASS_INDEX(plPickedMsg), + CLASS_INDEX(plCollideMsg), + CLASS_INDEX(plTriggerMsg), + CLASS_INDEX(plInterestingModMsg), + CLASS_INDEX(plDebugKeyEventMsg), + CLASS_INDEX(plPhysicalProperties_DEAD), + CLASS_INDEX(plSimplePhys), + CLASS_INDEX(plMatrixUpdateMsg), + CLASS_INDEX(plCondRefMsg), + CLASS_INDEX(plTimerCallbackMsg), + CLASS_INDEX(plEventCallbackMsg), + CLASS_INDEX(plSpawnModMsg), + CLASS_INDEX(plSpawnRequestMsg), + CLASS_INDEX(plLoadCloneMsg), + CLASS_INDEX(plEnableMsg), + CLASS_INDEX(plWarpMsg), + CLASS_INDEX(plAttachMsg), + CLASS_INDEX(pfConsole), + CLASS_INDEX(plRenderMsg), + CLASS_INDEX(plAnimTimeConvert), + CLASS_INDEX(plSoundMsg), + CLASS_INDEX(plInterestingPing), + CLASS_INDEX(plNodeCleanupMsg), + CLASS_INDEX(plSpaceTree), + CLASS_INDEX(plNetMessage), + CLASS_INDEX(plNetMsgJoinReq), + CLASS_INDEX(plNetMsgJoinAck), + CLASS_INDEX(plNetMsgLeave), + CLASS_INDEX(plNetMsgPing), + CLASS_INDEX(plNetMsgRoomsList), + CLASS_INDEX(plNetMsgGroupOwner), + CLASS_INDEX(plNetMsgGameStateRequest), + CLASS_INDEX(plNetMsgSessionReset), + CLASS_INDEX(plNetMsgOmnibus), + CLASS_INDEX(plNetMsgObject), + CLASS_INDEX(plCCRInvisibleMsg), + CLASS_INDEX(plLinkInDoneMsg), + CLASS_INDEX(plNetMsgGameMessage), + CLASS_INDEX(plNetMsgStream), + CLASS_INDEX(plAudioSysMsg), + CLASS_INDEX(plDispatchBase), + CLASS_INDEX(plServerReplyMsg), + CLASS_INDEX(plDeviceRecreateMsg), + CLASS_INDEX(plNetMsgStreamHelper), + CLASS_INDEX(plNetMsgObjectHelper), + CLASS_INDEX(plIMouseXEventMsg), + CLASS_INDEX(plIMouseYEventMsg), + CLASS_INDEX(plIMouseBEventMsg), + CLASS_INDEX(plLogicTriggerMsg), + CLASS_INDEX(plPipeline), + CLASS_INDEX(plDXPipeline), + CLASS_INDEX(plNetMsgVoice), + CLASS_INDEX(plLightRefMsg), + CLASS_INDEX(plNetMsgStreamedObject), + CLASS_INDEX(plNetMsgSharedState), + CLASS_INDEX(plNetMsgTestAndSet), + CLASS_INDEX(plNetMsgGetSharedState), + CLASS_INDEX(plSharedStateMsg), + CLASS_INDEX(plNetGenericServerTask), + CLASS_INDEX(plNetClientMgrMsg), + CLASS_INDEX(plLoadAgeMsg), + CLASS_INDEX(plMessageWithCallbacks), + CLASS_INDEX(plClientMsg), + CLASS_INDEX(plClientRefMsg), + CLASS_INDEX(plNetMsgObjStateRequest), + CLASS_INDEX(plCCRPetitionMsg), + CLASS_INDEX(plVaultCCRInitializationTask), + CLASS_INDEX(plNetServerMsg), + CLASS_INDEX(plNetServerMsgWithContext), + CLASS_INDEX(plNetServerMsgRegisterServer), + CLASS_INDEX(plNetServerMsgUnregisterServer), + CLASS_INDEX(plNetServerMsgStartProcess), + CLASS_INDEX(plNetServerMsgRegisterProcess), + CLASS_INDEX(plNetServerMsgUnregisterProcess), + CLASS_INDEX(plNetServerMsgFindProcess), + CLASS_INDEX(plNetServerMsgProcessFound), + CLASS_INDEX(plNetMsgRoutingInfo), + CLASS_INDEX(plNetServerSessionInfo), + CLASS_INDEX(plSimulationMsg), + CLASS_INDEX(plSimulationSynchMsg), + CLASS_INDEX(plHKSimulationSynchMsg), + CLASS_INDEX(plAvatarMsg), + CLASS_INDEX(plAvTaskMsg), + CLASS_INDEX(plAvSeekMsg), + CLASS_INDEX(plAvOneShotMsg), + CLASS_INDEX(plSatisfiedMsg), + CLASS_INDEX(plNetMsgObjectListHelper), + CLASS_INDEX(plNetMsgObjectUpdateFilter), + CLASS_INDEX(plProxyDrawMsg), + CLASS_INDEX(plSelfDestructMsg), + CLASS_INDEX(plSimInfluenceMsg ), + CLASS_INDEX(plForceMsg ), + CLASS_INDEX(plOffsetForceMsg ), + CLASS_INDEX(plTorqueMsg ), + CLASS_INDEX(plImpulseMsg ), + CLASS_INDEX(plOffsetImpulseMsg ), + CLASS_INDEX(plAngularImpulseMsg ), + CLASS_INDEX(plDampMsg ), + CLASS_INDEX(plShiftMassMsg ), + CLASS_INDEX(plSimStateMsg ), + CLASS_INDEX(plFreezeMsg ), + CLASS_INDEX(plEventGroupMsg ), + CLASS_INDEX(plSuspendEventMsg ), + CLASS_INDEX(plNetMsgMembersListReq), + CLASS_INDEX(plNetMsgMembersList), + CLASS_INDEX(plNetMsgMemberInfoHelper), + CLASS_INDEX(plNetMsgMemberListHelper), + CLASS_INDEX(plNetMsgMemberUpdate), + CLASS_INDEX(plNetMsgServerToClient), + CLASS_INDEX(plNetMsgCreatePlayer), + CLASS_INDEX(plNetMsgAuthenticateHello), + CLASS_INDEX(plNetMsgAuthenticateChallenge), + CLASS_INDEX(plConnectedToVaultMsg), + CLASS_INDEX(plCCRCommunicationMsg), + CLASS_INDEX(plNetMsgInitialAgeStateSent), + CLASS_INDEX(plInitialAgeStateLoadedMsg), + CLASS_INDEX(plNetServerMsgFindServerBase), + CLASS_INDEX(plNetServerMsgFindServerReplyBase), + CLASS_INDEX(plNetServerMsgFindAuthServer), + CLASS_INDEX(plNetServerMsgFindAuthServerReply), + CLASS_INDEX(plNetServerMsgFindVaultServer), + CLASS_INDEX(plNetServerMsgFindVaultServerReply), + CLASS_INDEX(plAvTaskSeekDoneMsg), + CLASS_INDEX(plNCAgeJoinerMsg), + CLASS_INDEX(plNetServerMsgVaultTask), + CLASS_INDEX(plNetMsgVaultTask), + CLASS_INDEX(plAgeLinkStruct), + CLASS_INDEX(plVaultAgeInfoNode), + CLASS_INDEX(plNetMsgStreamableHelper), + CLASS_INDEX(plNetMsgReceiversListHelper), + CLASS_INDEX(plNetMsgListenListUpdate), + CLASS_INDEX(plNetServerMsgPing), + CLASS_INDEX(plNetMsgAlive), + CLASS_INDEX(plNetMsgTerminated), + CLASS_INDEX(plSDLModifierMsg), + CLASS_INDEX(plNetMsgSDLState), + CLASS_INDEX(plNetServerMsgSessionReset), + CLASS_INDEX(plCCRBanLinkingMsg), + CLASS_INDEX(plCCRSilencePlayerMsg), + CLASS_INDEX(plRenderRequestMsg), + CLASS_INDEX(plRenderRequestAck), + CLASS_INDEX(plNetMember), + CLASS_INDEX(plNetGameMember), + CLASS_INDEX(plNetTransportMember), + CLASS_INDEX(plConvexVolume), + CLASS_INDEX(plParticleGenerator), + CLASS_INDEX(plSimpleParticleGenerator), + CLASS_INDEX(plParticleEmitter), + CLASS_INDEX(plAGChannel), + CLASS_INDEX(plMatrixChannel), + CLASS_INDEX(plMatrixTimeScale), + CLASS_INDEX(plMatrixBlend), + CLASS_INDEX(plMatrixControllerChannel), + CLASS_INDEX(plQuatPointCombine), + CLASS_INDEX(plPointChannel), + CLASS_INDEX(plPointConstant), + CLASS_INDEX(plPointBlend), + CLASS_INDEX(plQuatChannel), + CLASS_INDEX(plQuatConstant), + CLASS_INDEX(plQuatBlend), + CLASS_INDEX(plLinkToAgeMsg), + CLASS_INDEX(plPlayerPageMsg), + CLASS_INDEX(plCmdIfaceModMsg), + CLASS_INDEX(plNetServerMsgPlsUpdatePlayer), + CLASS_INDEX(plListenerMsg), + CLASS_INDEX(plAnimPath), + CLASS_INDEX(plClothingUpdateBCMsg), + CLASS_INDEX(plNotifyMsg), + CLASS_INDEX(plFakeOutMsg), + CLASS_INDEX(plCursorChangeMsg), + CLASS_INDEX(plNodeChangeMsg), + CLASS_INDEX(UNUSED_plAvEnableMsg), + CLASS_INDEX(plLinkCallbackMsg), + CLASS_INDEX(plTransitionMsg), + CLASS_INDEX(plConsoleMsg), + CLASS_INDEX(plVolumeIsect), + CLASS_INDEX(plSphereIsect), + CLASS_INDEX(plConeIsect), + CLASS_INDEX(plCylinderIsect), + CLASS_INDEX(plParallelIsect), + CLASS_INDEX(plConvexIsect), + CLASS_INDEX(plComplexIsect), + CLASS_INDEX(plUnionIsect), + CLASS_INDEX(plIntersectionIsect), + CLASS_INDEX(plModulator), + CLASS_INDEX(UNUSED___plInventoryMsg), + CLASS_INDEX(plLinkEffectsTriggerMsg), + CLASS_INDEX(plLinkEffectBCMsg), + CLASS_INDEX(plResponderEnableMsg), + CLASS_INDEX(plNetServerMsgHello), + CLASS_INDEX(plNetServerMsgHelloReply), + CLASS_INDEX(plNetServerMember), + CLASS_INDEX(plResponderMsg), + CLASS_INDEX(plOneShotMsg), + CLASS_INDEX(plVaultAgeInfoListNode), + CLASS_INDEX(plNetServerMsgServerRegistered), + CLASS_INDEX(plPointTimeScale), + CLASS_INDEX(plPointControllerChannel), + CLASS_INDEX(plQuatTimeScale), + CLASS_INDEX(plAGApplicator), + CLASS_INDEX(plMatrixChannelApplicator), + CLASS_INDEX(plPointChannelApplicator), + CLASS_INDEX(plLightDiffuseApplicator), + CLASS_INDEX(plLightAmbientApplicator), + CLASS_INDEX(plLightSpecularApplicator), + CLASS_INDEX(plOmniApplicator), + CLASS_INDEX(plQuatChannelApplicator), + CLASS_INDEX(plScalarChannel), + CLASS_INDEX(plScalarTimeScale), + CLASS_INDEX(plScalarBlend), + CLASS_INDEX(plScalarControllerChannel), + CLASS_INDEX(plScalarChannelApplicator), + CLASS_INDEX(plSpotInnerApplicator), + CLASS_INDEX(plSpotOuterApplicator), + CLASS_INDEX(plNetServerMsgPlsRoutableMsg), + CLASS_INDEX(_UNUSED_plPuppetBrainMsg), + CLASS_INDEX(plATCEaseCurve), + CLASS_INDEX(plConstAccelEaseCurve), + CLASS_INDEX(plSplineEaseCurve), + CLASS_INDEX(plVaultAgeInfoInitializationTask), + CLASS_INDEX(pfGameGUIMsg), + CLASS_INDEX(plNetServerMsgVaultRequestGameState), + CLASS_INDEX(plNetServerMsgVaultGameState), + CLASS_INDEX(plNetServerMsgVaultGameStateSave), + CLASS_INDEX(plNetServerMsgVaultGameStateSaved), + CLASS_INDEX(plNetServerMsgVaultGameStateLoad), + CLASS_INDEX(plNetClientTask), + CLASS_INDEX(plNetMsgSDLStateBCast), + CLASS_INDEX(plReplaceGeometryMsg), + CLASS_INDEX(plNetServerMsgExitProcess), + CLASS_INDEX(plNetServerMsgSaveGameState), + CLASS_INDEX(plDniCoordinateInfo), + CLASS_INDEX(plNetMsgGameMessageDirected), + CLASS_INDEX(plLinkOutUnloadMsg), + CLASS_INDEX(plScalarConstant), + CLASS_INDEX(plMatrixConstant), + CLASS_INDEX(plAGCmdMsg), + CLASS_INDEX(plParticleTransferMsg), + CLASS_INDEX(plParticleKillMsg), + CLASS_INDEX(plExcludeRegionMsg), + CLASS_INDEX(plOneTimeParticleGenerator), + CLASS_INDEX(plParticleApplicator), + CLASS_INDEX(plParticleLifeMinApplicator), + CLASS_INDEX(plParticleLifeMaxApplicator), + CLASS_INDEX(plParticlePPSApplicator), + CLASS_INDEX(plParticleAngleApplicator), + CLASS_INDEX(plParticleVelMinApplicator), + CLASS_INDEX(plParticleVelMaxApplicator), + CLASS_INDEX(plParticleScaleMinApplicator), + CLASS_INDEX(plParticleScaleMaxApplicator), + CLASS_INDEX(plDynamicTextMsg), + CLASS_INDEX(plCameraTargetFadeMsg), + CLASS_INDEX(plAgeLoadedMsg), + CLASS_INDEX(plPointControllerCacheChannel), + CLASS_INDEX(plScalarControllerCacheChannel), + CLASS_INDEX(plLinkEffectsTriggerPrepMsg), + CLASS_INDEX(plLinkEffectPrepBCMsg), + CLASS_INDEX(plAvatarInputStateMsg), + CLASS_INDEX(plAgeInfoStruct), + CLASS_INDEX(plSDLNotificationMsg), + CLASS_INDEX(plNetClientConnectAgeVaultTask), + CLASS_INDEX(plLinkingMgrMsg), + CLASS_INDEX(plVaultNotifyMsg), + CLASS_INDEX(plPlayerInfo), + CLASS_INDEX(plSwapSpansRefMsg), + CLASS_INDEX(pfKI), + CLASS_INDEX(plDISpansMsg), + CLASS_INDEX(plNetMsgCreatableHelper), + CLASS_INDEX(plCreatableUuid), + CLASS_INDEX(plNetMsgRequestMyVaultPlayerList), + CLASS_INDEX(plDelayedTransformMsg), + CLASS_INDEX(plSuperVNodeMgrInitTask), + CLASS_INDEX(plElementRefMsg), + CLASS_INDEX(plClothingMsg), + CLASS_INDEX(plEventGroupEnableMsg), + CLASS_INDEX(pfGUINotifyMsg), + CLASS_INDEX(UNUSED_plAvBrain), + CLASS_INDEX(plArmatureBrain), + CLASS_INDEX(plAvBrainHuman), + CLASS_INDEX(plAvBrainCritter), + CLASS_INDEX(plAvBrainDrive), + CLASS_INDEX(plAvBrainSample), + CLASS_INDEX(plAvBrainGeneric), + CLASS_INDEX(plPreloaderMsg), + CLASS_INDEX(plAvBrainLadder), + CLASS_INDEX(plInputIfaceMgrMsg), + CLASS_INDEX(pfKIMsg), + CLASS_INDEX(plRemoteAvatarInfoMsg), + CLASS_INDEX(plMatrixDelayedCorrectionApplicator), + CLASS_INDEX(plAvPushBrainMsg), + CLASS_INDEX(plAvPopBrainMsg), + CLASS_INDEX(plRoomLoadNotifyMsg), + CLASS_INDEX(plAvTask), + CLASS_INDEX(plAvAnimTask), + CLASS_INDEX(plAvSeekTask), + CLASS_INDEX(plNetCommAuthConnectedMsg), + CLASS_INDEX(plAvOneShotTask), + CLASS_INDEX(UNUSED_plAvEnableTask), + CLASS_INDEX(plAvTaskBrain), + CLASS_INDEX(plAnimStage), + CLASS_INDEX(plNetClientMember), + CLASS_INDEX(plNetClientCommTask), + CLASS_INDEX(plNetServerMsgAuthRequest), + CLASS_INDEX(plNetServerMsgAuthReply), + CLASS_INDEX(plNetClientCommAuthTask), + CLASS_INDEX(plClientGuid), + CLASS_INDEX(plNetMsgVaultPlayerList), + CLASS_INDEX(plNetMsgSetMyActivePlayer), + CLASS_INDEX(plNetServerMsgRequestAccountPlayerList), + CLASS_INDEX(plNetServerMsgAccountPlayerList), + CLASS_INDEX(plNetMsgPlayerCreated), + CLASS_INDEX(plNetServerMsgVaultCreatePlayer), + CLASS_INDEX(plNetServerMsgVaultPlayerCreated), + CLASS_INDEX(plNetMsgFindAge), + CLASS_INDEX(plNetMsgFindAgeReply), + CLASS_INDEX(plNetClientConnectPrepTask), + CLASS_INDEX(plNetClientAuthTask), + CLASS_INDEX(plNetClientGetPlayerVaultTask), + CLASS_INDEX(plNetClientSetActivePlayerTask), + CLASS_INDEX(plNetClientFindAgeTask), + CLASS_INDEX(plNetClientLeaveTask), + CLASS_INDEX(plNetClientJoinTask), + CLASS_INDEX(plNetClientCalibrateTask), + CLASS_INDEX(plNetMsgDeletePlayer), + CLASS_INDEX(plNetServerMsgVaultDeletePlayer), + CLASS_INDEX(plNetCoreStatsSummary), + CLASS_INDEX(plCreatableGenericValue), + CLASS_INDEX(plCreatableListHelper), + CLASS_INDEX(plCreatableStream), + CLASS_INDEX(plAvBrainGenericMsg), + CLASS_INDEX(plAvTaskSeek), + CLASS_INDEX(plAGInstanceCallbackMsg), + CLASS_INDEX(plArmatureEffectMsg), + CLASS_INDEX(plArmatureEffectStateMsg), + CLASS_INDEX(plShadowCastMsg), + CLASS_INDEX(plBoundsIsect), + CLASS_INDEX(plResMgrHelperMsg), + CLASS_INDEX(plNetCommAuthMsg), + CLASS_INDEX(plNetCommFileListMsg), + CLASS_INDEX(plNetCommFileDownloadMsg), + CLASS_INDEX(plNetCommLinkToAgeMsg), + CLASS_INDEX(plNetCommPlayerListMsg), + CLASS_INDEX(plNetCommActivePlayerMsg), + CLASS_INDEX(plNetCommCreatePlayerMsg), + CLASS_INDEX(plNetCommDeletePlayerMsg), + CLASS_INDEX(plNetCommPublicAgeListMsg), + CLASS_INDEX(plNetCommPublicAgeMsg), + CLASS_INDEX(plNetCommRegisterAgeMsg), + CLASS_INDEX(plVaultAdminInitializationTask), + CLASS_INDEX(plMultistageModMsg), + CLASS_INDEX(plSoundVolumeApplicator), + CLASS_INDEX(plCutter), + CLASS_INDEX(plBulletMsg), + CLASS_INDEX(plDynaDecalEnableMsg), + CLASS_INDEX(plOmniCutoffApplicator), + CLASS_INDEX(plArmatureUpdateMsg), + CLASS_INDEX(plAvatarFootMsg), + CLASS_INDEX(plNetOwnershipMsg), + CLASS_INDEX(plNetMsgRelevanceRegions), + CLASS_INDEX(plParticleFlockMsg), + CLASS_INDEX(plAvatarBehaviorNotifyMsg), + CLASS_INDEX(plATCChannel), + CLASS_INDEX(plScalarSDLChannel), + CLASS_INDEX(plLoadAvatarMsg), + CLASS_INDEX(plAvatarSetTypeMsg), + CLASS_INDEX(plNetMsgLoadClone), + CLASS_INDEX(plNetMsgPlayerPage), + CLASS_INDEX(plVNodeInitTask), + CLASS_INDEX(plRippleShapeMsg), + CLASS_INDEX(plEventManager), + CLASS_INDEX(plVaultNeighborhoodInitializationTask), + CLASS_INDEX(plNetServerMsgAgentRecoveryRequest), + CLASS_INDEX(plNetServerMsgFrontendRecoveryRequest), + CLASS_INDEX(plNetServerMsgBackendRecoveryRequest), + CLASS_INDEX(plNetServerMsgAgentRecoveryData), + CLASS_INDEX(plNetServerMsgFrontendRecoveryData), + CLASS_INDEX(plNetServerMsgBackendRecoveryData), + CLASS_INDEX(plSubWorldMsg), + CLASS_INDEX(plMatrixDifferenceApp), + CLASS_INDEX(plAvatarSpawnNotifyMsg), + // ** NOTE **: DO NOT INSERT CLASS_INDEXES HERE. Ignore all this junk + // and continue adding them at the bottom of the enum. - EAp + // -- Reserve a block of class indexes for auto-generated server database structs -- + CLASS_INDEX_DATABASE_STRUCT_INDEXES_START + CLASS_INDEX_DATABASE_STRUCT_INDEXES_END + CLASS_INDEX(plVaultGameServerInitializationTask), + CLASS_INDEX(plNetClientFindDefaultAgeTask), + CLASS_INDEX(plVaultAgeNode), + CLASS_INDEX(plVaultAgeInitializationTask), + CLASS_INDEX(plSetListenerMsg), + CLASS_INDEX(plVaultSystemNode), + CLASS_INDEX(plAvBrainSwim), + CLASS_INDEX(plNetMsgVault), + CLASS_INDEX(plNetServerMsgVault), + CLASS_INDEX(plVaultTask), // =1066 + CLASS_INDEX(plVaultConnectTask), + CLASS_INDEX(plVaultNegotiateManifestTask), + CLASS_INDEX(plVaultFetchNodesTask), + CLASS_INDEX(plVaultSaveNodeTask), + CLASS_INDEX(plVaultFindNodeTask), + CLASS_INDEX(plVaultAddNodeRefTask), + CLASS_INDEX(plVaultRemoveNodeRefTask), + CLASS_INDEX(plVaultSendNodeTask), + CLASS_INDEX(plVaultNotifyOperationCallbackTask), + CLASS_INDEX(plVNodeMgrInitializationTask), + CLASS_INDEX(plVaultPlayerInitializationTask), + CLASS_INDEX(plNetVaultServerInitializationTask), + CLASS_INDEX(plCommonNeighborhoodsInitTask), + CLASS_INDEX(plVaultNodeRef), + CLASS_INDEX(plVaultNode), + CLASS_INDEX(plVaultFolderNode), + CLASS_INDEX(plVaultImageNode), + CLASS_INDEX(plVaultTextNoteNode), + CLASS_INDEX(plVaultSDLNode), + CLASS_INDEX(plVaultAgeLinkNode), + CLASS_INDEX(plVaultChronicleNode), + CLASS_INDEX(plVaultPlayerInfoNode), + CLASS_INDEX(plVaultMgrNode), + CLASS_INDEX(plVaultPlayerNode), + CLASS_INDEX(plSynchEnableMsg), + CLASS_INDEX(plNetVaultServerNode), + CLASS_INDEX(plVaultAdminNode), + CLASS_INDEX(plVaultGameServerNode), + CLASS_INDEX(plVaultPlayerInfoListNode), + CLASS_INDEX(plAvatarStealthModeMsg), + CLASS_INDEX(plEventCallbackInterceptMsg), + CLASS_INDEX(plDynamicEnvMapMsg), + CLASS_INDEX(plClimbMsg), + CLASS_INDEX(plIfaceFadeAvatarMsg), + CLASS_INDEX(plAvBrainClimb), + CLASS_INDEX(plSharedMeshBCMsg), + CLASS_INDEX(plNetVoiceListMsg), + CLASS_INDEX(plSwimMsg), + CLASS_INDEX(plMorphDelta), + CLASS_INDEX(plMatrixControllerCacheChannel), + CLASS_INDEX(plVaultMarkerNode), + CLASS_INDEX(pfMarkerMsg), + CLASS_INDEX(plPipeResMakeMsg), + CLASS_INDEX(plPipeRTMakeMsg), + CLASS_INDEX(plPipeGeoMakeMsg), + CLASS_INDEX(plAvCoopMsg), + CLASS_INDEX(plAvBrainCoop), + CLASS_INDEX(plSimSuppressMsg), + CLASS_INDEX(plVaultMarkerListNode), + CLASS_INDEX(UNUSED_plAvTaskOrient), + CLASS_INDEX(plAgeBeginLoadingMsg), + CLASS_INDEX(plSetNetGroupIDMsg), + CLASS_INDEX(pfBackdoorMsg), + CLASS_INDEX(plAIMsg), + CLASS_INDEX(plAIBrainCreatedMsg), + CLASS_INDEX(plStateDataRecord), + CLASS_INDEX(plNetClientCommDeletePlayerTask), + CLASS_INDEX(plNetMsgSetTimeout), + CLASS_INDEX(plNetMsgActivePlayerSet), + CLASS_INDEX(plNetClientCommSetTimeoutTask), + CLASS_INDEX(plNetRoutableMsgOmnibus), + CLASS_INDEX(plNetMsgGetPublicAgeList), + CLASS_INDEX(plNetMsgPublicAgeList), + CLASS_INDEX(plNetMsgCreatePublicAge), + CLASS_INDEX(plNetMsgPublicAgeCreated), + CLASS_INDEX(plNetServerMsgEnvelope), + CLASS_INDEX(plNetClientCommGetPublicAgeListTask), + CLASS_INDEX(plNetClientCommCreatePublicAgeTask), + CLASS_INDEX(plNetServerMsgPendingMsgs), + CLASS_INDEX(plNetServerMsgRequestPendingMsgs), + CLASS_INDEX(plDbInterface), + CLASS_INDEX(plDbProxyInterface), + CLASS_INDEX(plDBGenericSQLDB), + CLASS_INDEX(pfGameMgrMsg), + CLASS_INDEX(pfGameCliMsg), + CLASS_INDEX(pfGameCli), + CLASS_INDEX(pfGmTicTacToe), + CLASS_INDEX(pfGmHeek), + CLASS_INDEX(pfGmMarker), + CLASS_INDEX(pfGmBlueSpiral), + CLASS_INDEX(pfGmClimbingWall), + CLASS_INDEX(plAIArrivedAtGoalMsg), + CLASS_INDEX(pfGmVarSync), + CLASS_INDEX(plNetMsgRemovePublicAge), + CLASS_INDEX(plNetMsgPublicAgeRemoved), + CLASS_INDEX(plNetClientCommRemovePublicAgeTask), + CLASS_INDEX(plCCRMessage), + CLASS_INDEX(plAvOneShotLinkTask), + CLASS_INDEX(plNetAuthDatabase), + CLASS_INDEX(plAvatarOpacityCallbackMsg), + CLASS_INDEX(plAGDetachCallbackMsg), + CLASS_INDEX(pfMovieEventMsg), + CLASS_INDEX(plMovieMsg), + CLASS_INDEX(plPipeTexMakeMsg), + CLASS_INDEX(plEventLog), + CLASS_INDEX(plDbEventLog), + CLASS_INDEX(plSyslogEventLog), + CLASS_INDEX(plCaptureRenderMsg), + CLASS_INDEX(plAgeLoaded2Msg), + CLASS_INDEX(plPseudoLinkEffectMsg), + CLASS_INDEX(plPseudoLinkAnimTriggerMsg), + CLASS_INDEX(plPseudoLinkAnimCallbackMsg), + CLASS_INDEX(__UNUSED__pfClimbingWallMsg), + CLASS_INDEX(plClimbEventMsg), + CLASS_INDEX(__UNUSED__plAvBrainQuab), + CLASS_INDEX(plAccountUpdateMsg), + CLASS_INDEX(plLinearVelocityMsg), + CLASS_INDEX(plAngularVelocityMsg), + CLASS_INDEX(plRideAnimatedPhysMsg), + CLASS_INDEX(plAvBrainRideAnimatedPhysical), +CLASS_INDEX_LIST_END + +#endif // plCreatableIndex_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCreatableStrings.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCreatableStrings.h new file mode 100644 index 00000000..2ef46481 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plCreatableStrings.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plCreatableStrings.h - Handy header file that declares a class with two +// static public string arrays representing the names +// of the classes before and after keyedObjDelineator +// in plCreatableIndex.h. Note the cunning (and humble) +// use of macros to avoid having to change this file +// whenever plCreatableIndex.h changes. +// +// Should only be included once, probably in plFactory.cpp +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plCreatableStrings_h +#define _plCreatableStrings_h + +// Step 0: Undefine plCreatableIndex_inc, since we're doing something sneaky with it + +#undef plCreatableIndex_inc + + +// Step 1: Define plCreator_inc, so the plCreator.h include in plCI.h won't do anything + +#define plCreator_inc + + +// Step 2: Define the CLASS_INDEX macros so we generate our class. The class name +// is plCreatableStrings and the arrays are fKeyedStrings and fNonKeyedStrings + +#undef CLASS_INDEX_LIST_START +#undef CLASS_INDEX +#undef CLASS_INDEX_NONKEYED_OBJ_START +#undef CLASS_INDEX_LIST_END + +#define CLASS_INDEX_LIST_START class plCreatableStrings { public:\ + static char *fKeyedStrings[]; static char *fNonKeyedStrings[]; static char *fNonKeyedPostDBStrings[];\ + }; \ + char *plCreatableStrings::fKeyedStrings[] = { +#define CLASS_INDEX(ci) #ci +#define CLASS_INDEX_NONKEYED_OBJ_START }; char *plCreatableStrings::fNonKeyedStrings[] = { +#define CLASS_INDEX_LIST_END }; + +#undef CLASS_INDEX_DATABASE_STRUCT_INDEXES_START +#undef CLASS_INDEX_DATABASE_STRUCT_INDEXES_END +#define CLASS_INDEX_DATABASE_STRUCT_INDEXES_START +#define CLASS_INDEX_DATABASE_STRUCT_INDEXES_END }; char *plCreatableStrings::fNonKeyedPostDBStrings[] = { + +// Step 3: Include plCI.h + +#include "plCreatableIndex.h" + + +#endif // _plCreatableStrings_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plDrawable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plDrawable.h new file mode 100644 index 00000000..b4c2b5c4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plDrawable.h @@ -0,0 +1,186 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plDrawable_inc +#define plDrawable_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "plLoadMask.h" +#include "hsTemplates.h" +#include "plRenderLevel.h" + +class plSpaceTree; +class hsStream; +class hsResMgr; +struct hsMatrix44; +class hsBounds3Ext; +class plPipeline; +class plGeometrySpan; +class hsGMaterial; +class plParticleEmitter; +class plAccessSpan; + +class plDrawableCriteria +{ +public: + plDrawableCriteria(UInt32 crit, const plRenderLevel& lev, const plLoadMask& m, UInt32 ty=1 /* Normal */ ) : fCriteria(crit), fLevel(lev), fType(ty), fLoadMask(m) {} + UInt32 fCriteria; + plRenderLevel fLevel; + UInt32 fType; + plLoadMask fLoadMask; +}; + +class plDrawable : public hsKeyedObject +{ +public: + enum { + // Renumber these the next time we bump major version #s (no reason to do it now) + kPropNoDraw = 0x01, + kPropUNUSED = 0x02, + kPropSortSpans = 0x04, + kPropSortFaces = 0x08, + kPropVolatile = 0x10, // Means that spans DEFAULT to kPropVolatile, but if this + // is not set, spans can still be volatile + kPropNoReSort = 0x20, // Don't do sorting of spans for optimization. + kPropPartialSort = 0x40, // Sort spans on an individual basis. + kPropCharacter = 0x80, // Lights want to know if this is in the general class of "characters" + kPropSortAsOne = 0x100, + kPropHasVisLOS = 0x200 + }; + + // Criteria for drawables. Used when searching through a sceneNode for a particular drawable + enum + { + kCritStatic = 0x01, + kCritSortSpans = 0x02, + kCritSortFaces = 0x08, + kCritCharacter = 0x10 + }; + // Types of drawables for rendering filtering. + enum plDrawableType { + kNormal = 0x00000001, + kNonDrawable = 0x00000002, //e.g. a light, affects drawing, but isn't drawn. + kEnviron = 0x00000004, + + // Proxies in the upper 16 bits. + kLightProxy = 0x00010000, + kOccluderProxy = 0x00020000, + kAudibleProxy = 0x00040000, + kPhysicalProxy = 0x00080000, + kCoordinateProxy = 0x00100000, + kOccSnapProxy = 0x00200000, + kGenericProxy = 0x00400000, + kCameraProxy = 0x00800000, + kAllProxies = 0x00ff0000, + + kAllTypes = 0xffffffff + }; + enum plSubDrawableType { + kSubNormal = 0x00000001, + kSubNonDrawable = 0x00000002, + kSubEnviron = 0x00000004, + + kSubAllTypes = 0xffffffff + }; + + enum plAccessFlags { + kReadSrc = 0x1, + kWriteDst = 0x2, + kWriteSrc = 0x4 + }; + + enum MsgTypes + { + kMsgMaterial, + kMsgDISpans, // UNUSED + kMsgFogEnviron, + kMsgPermaLight, + kMsgPermaProj, + kMsgPermaLightDI, + kMsgPermaProjDI + }; + + + + CLASSNAME_REGISTER( plDrawable ); + GETINTERFACE_ANY( plDrawable, hsKeyedObject ); + + virtual plDrawable& SetProperty( int prop, hsBool on ) = 0; + virtual hsBool GetProperty( int prop ) const = 0; + + virtual plDrawable& SetProperty( UInt32 index, int prop, hsBool on ) = 0; + virtual hsBool GetProperty( UInt32 index, int prop ) const = 0; + + virtual plDrawable& SetNativeProperty( int prop, hsBool on ) = 0; + virtual hsBool GetNativeProperty( int prop ) const = 0; + + virtual plDrawable& SetNativeProperty( UInt32 index, int prop, hsBool on ) = 0; + virtual hsBool GetNativeProperty( UInt32 index, int prop ) const = 0; + + virtual plDrawable& SetSubType( UInt32 index, plSubDrawableType t, hsBool on ) = 0; + virtual UInt32 GetSubType( UInt32 index ) const = 0; // returns or of all spans with this index (index==-1 is all spans). + + virtual UInt32 GetType( void ) const = 0; + virtual void SetType( UInt32 type ) = 0; + + virtual void SetRenderLevel(const plRenderLevel& l) = 0; + virtual const plRenderLevel& GetRenderLevel() const = 0; + + virtual plDrawable& SetTransform( UInt32 index, const hsMatrix44& l2w, const hsMatrix44& w2l ) = 0; + virtual const hsMatrix44& GetLocalToWorld( UInt32 span = (UInt32)-1 ) const = 0; + virtual const hsMatrix44& GetWorldToLocal( UInt32 span = (UInt32)-1 ) const = 0; + + virtual const hsBounds3Ext& GetLocalBounds( UInt32 index = (UInt32)-1 ) const = 0; + virtual const hsBounds3Ext& GetWorldBounds( UInt32 index = (UInt32)-1 ) const = 0; + virtual const hsBounds3Ext& GetMaxWorldBounds( UInt32 index = (UInt32)-1 ) const = 0; + + virtual plSpaceTree* GetSpaceTree() const = 0; + virtual void SetDISpanVisSet(UInt32 diIndex, hsKeyedObject* reg, hsBool on) = 0; + + // Taking span index. DI Index doesn't make sense here, because one object's DI can dereference into many materials etc. + virtual hsGMaterial* GetSubMaterial(int index) const = 0; + virtual hsBool GetSubVisDists(int index, hsScalar& minDist, hsScalar& maxDist) const = 0; // return true if span invisible before minDist and/or after maxDist + + // Should implement hsKeyedObject Read/Write/Save/Load as well + + // These two should only be called by the SceneNode + virtual void SetSceneNode(plKey node) = 0; + virtual plKey GetSceneNode() const = 0; + + /// Funky particle system functions + virtual UInt32 CreateParticleSystem( UInt32 maxNumEmitters, UInt32 maxNumParticles, hsGMaterial *material ) = 0; + virtual void ResetParticleSystem( UInt32 index ) = 0; + virtual void AssignEmitterToParticleSystem( UInt32 index, plParticleEmitter *emitter ) = 0; + + /// EXPORT-ONLY + + // Called by the sceneNode to determine if we match the criteria + virtual hsBool DoIMatch( const plDrawableCriteria& crit ) = 0; + + // Take the list of triMeshes and convert them to buffers, building a list of spans for each + virtual void Optimize( void ) = 0; +}; + +#endif // plDrawable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plNetServerDatabaseStructClassIndexes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plNetServerDatabaseStructClassIndexes.h new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plNetServerDatabaseStructClassIndexes.h @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPhysical.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPhysical.h new file mode 100644 index 00000000..c5ee7d17 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPhysical.h @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPhysical_inc +#define plPhysical_inc + +#include "../pnNetCommon/plSynchedObject.h" +#include "hsTemplates.h" + +struct hsMatrix44; +struct hsPoint3; +struct hsVector3; +class hsQuat; +class plPhysicalSndGroup; +class plDrawableSpans; +class hsGMaterial; + +// Primary interface object for physics functionality. A physical corresponds to +// a single rigid body in a simulation. (Note that there can be multiple +// simulations.) The plPhysical is reached through the simulation interface on a +// plSceneObject +// +// Any function that ends with 'Sim' gets or sets a simulation space value. If +// the physical is in the main world, this will be the same as a global value, +// but if it's in a subworld, it will be relative to that. +class plPhysical : public plSynchedObject +{ +public: + CLASSNAME_REGISTER(plPhysical); + GETINTERFACE_ANY(plPhysical, plSynchedObject); + + virtual plPhysical& SetProperty(int prop, hsBool b) = 0; + virtual hsBool GetProperty(int prop) const = 0; + + virtual void SetObjectKey(plKey oKey) = 0; + virtual plKey GetObjectKey() const = 0; + + // These two should only be called by the SceneNode + virtual void SetSceneNode(plKey node) = 0; + virtual plKey GetSceneNode() const = 0; + + virtual hsBool GetLinearVelocitySim(hsVector3& vel) const = 0; + virtual void SetLinearVelocitySim(const hsVector3& vel) = 0; + virtual void ClearLinearVelocity() = 0; + + virtual hsBool GetAngularVelocitySim(hsVector3& vel) const = 0; + virtual void SetAngularVelocitySim(const hsVector3& vel) = 0; + + virtual void SetHitForce(const hsVector3& force, const hsPoint3& pos)=0; + /** Standard plasma transform interface, in global coordinates by convention. + If you send in the same matrix that the physical last sent out in its correction message, + it will be ignored as an "echo" -- UNLESS you set force to true, in which case the transform + will be applied regardless. + Set force to true if you don't want the transform to be ignored for any reason. Without it, + this will ignore the incoming transform if it's the same one it sent out last time. */ + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, hsBool force=false) = 0; + virtual void GetTransform(hsMatrix44& l2w, hsMatrix44& w2l) = 0; + + // From plSimDefs::Group + virtual int GetGroup() const = 0; + + // Flags in plSimDefs::plLOSDB + virtual void AddLOSDB(UInt16 flag) = 0; + virtual void RemoveLOSDB(UInt16 flag) = 0; + virtual UInt16 GetAllLOSDBs() = 0; + virtual hsBool IsInLOSDB(UInt16 flag) = 0; + + // Return the key of our subworld. May be a nil key. + virtual plKey GetWorldKey() const = 0; + + virtual plPhysicalSndGroup* GetSoundGroup() const = 0; + + virtual void GetPositionSim(hsPoint3& pos) const = 0; + + /** Send the current simulation location to the coordinate interface. + If the simulation is a subworld, this will also factor in the subworld's + transform. The sent transform is global. "isSynchUpdate" tells us if this + is an update due to receiving remote state.*/ + virtual void SendNewLocation(hsBool synchTransform = false, hsBool isSynchUpdate = false) = 0; + + // For the physics SDL only. For Set, any of the values may be set to nil, if + // they're not used. + virtual void GetSyncState(hsPoint3& pos, hsQuat& rot, hsVector3& linV, hsVector3& angV) = 0; + virtual void SetSyncState(hsPoint3* pos, hsQuat* rot, hsVector3* linV, hsVector3* angV) = 0; + + virtual hsScalar GetMass() = 0; + // I wish I could think of a better way to do this, but this is how it's + // going to be for now. + virtual void ExcludeRegionHack(hsBool cleared) = 0; + + virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) = 0; +}; + +#endif // plPhysical_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPipeResReq.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPipeResReq.h new file mode 100644 index 00000000..2bd38580 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPipeResReq.h @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPipeResReq_inc +#define plPipeResReq_inc + +//#define PIPERES_VERBOSE +#ifdef PIPERES_VERBOSE +#define PIPELOG(a) hsStatusMessage(a) +#else // PIPERES_VERBOSE +#define PIPELOG(a) +#endif // PIPERES_VERBOSE + +class plPipeResReq +{ +protected: + hsBool fReq; + + plPipeResReq() : fReq(false) {} + + static plPipeResReq& Instance(); + +public: + + static void Request() { PIPELOG("Request"); Instance().fReq = true; } + static hsBool Check() { PIPELOG("Check"); return Instance().fReq; } + static void Clear() { PIPELOG("Clear"); Instance().fReq = false; } +}; + +#endif // plPipeResReq_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPipeline.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPipeline.h new file mode 100644 index 00000000..b147002b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plPipeline.h @@ -0,0 +1,346 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPipeline_inc +#define plPipeline_inc + +#include "../pnFactory/plCreatable.h" +#include "hsGMatState.h" +#include "hsTemplates.h" +#include "hsStlUtils.h" + +#define MIN_WIDTH 640 +#define MIN_HEIGHT 480 + +#define DEFAULT_WIDTH 800 +#define DEFAULT_HEIGHT 600 +#define DEFAULT_WINDOWED false +#define DEFAULT_COLORDEPTH 32 +#define DEFAULT_ANTIALIASAMOUNT 0 +#define DEFAULT_ANISOLEVEL 0 +#define DEFAULT_TEXTUREQUALITY 2 +#define DEFAULT_VIDEOQUALITY 2 +#define DEFAULT_SHADOWS 0 +#define DEFAULT_PLANARREFLECTIONS 0 + + +struct hsPoint3; +struct hsVector3; +struct hsMatrix44; +struct hsColorRGBA; +class hsBounds3Ext; +class hsGMaterial; +class plDrawPrim; +class plRenderTarget; +class hsG3DDevice; +class hsGView3; +class plDrawable; +class plGBufferGroup; +class plLayerInterface; +class plSpaceTree; +class plCullPoly; +class plRenderRequest; +class plShadowSlave; +class plAccessSpan; +class plTextFont; +class plVertexSpan; +class plDrawableSpans; +class plSceneObject; +class plClothingOutfit; +class hsGDeviceRef; +class plFogEnvironment; +class plLightInfo; +class plMipmap; +class plVisMgr; + +class plViewTransform; + + +struct PipelineParams +{ + PipelineParams(): + Width(DEFAULT_WIDTH), + Height(DEFAULT_HEIGHT), + Windowed(DEFAULT_WINDOWED), + ColorDepth(DEFAULT_COLORDEPTH), + AntiAliasingAmount(DEFAULT_ANTIALIASAMOUNT), + AnisotropicLevel(DEFAULT_ANISOLEVEL), + TextureQuality(DEFAULT_TEXTUREQUALITY), + VideoQuality(DEFAULT_VIDEOQUALITY), + Shadows(DEFAULT_SHADOWS), + PlanarReflections(DEFAULT_PLANARREFLECTIONS), +#ifndef PLASMA_EXTERNAL_RELEASE + ForceSecondMonitor(false), +#endif // PLASMA_EXTERNAL_RELEASE + VSync(false) + { + } + + int Width; + int Height; + hsBool Windowed; + int ColorDepth; + int AntiAliasingAmount; + int AnisotropicLevel; + int TextureQuality; + int VideoQuality; + int Shadows; + int PlanarReflections; + hsBool VSync; +#ifndef PLASMA_EXTERNAL_RELEASE + hsBool ForceSecondMonitor; +#endif // PLASMA_EXTERNAL_RELEASE +}; + +class plDisplayMode +{ +public: + int Width; + int Height; + int ColorDepth; +}; + +class plPipeline : public plCreatable +{ +public: + + CLASSNAME_REGISTER( plPipeline ); + GETINTERFACE_ANY( plPipeline, plCreatable ); + + // Typical 3D device + // + // PreRender - fill out the visList with which spans from drawable will be drawn. + // visList is write only. On output, visList is UNSORTED visible spans. + // Called once per scene render (maybe multiple times per frame). + // Returns true if rendering should proceed. + virtual hsBool PreRender(plDrawable* drawable, hsTArray& visList, plVisMgr* visMgr=nil) = 0; + // PrepForRender - perform any processing on the drawable data nessecary before rendering. + // visList is read only. On input, visList is SORTED visible spans, and is ALL spans which will be drawn this render. + // Called once per scene render. + // Returns true if rendering should proceed. + virtual hsBool PrepForRender(plDrawable* drawable, hsTArray& visList, plVisMgr* visMgr=nil) = 0; + // Render - draw the drawable to the current render target. + // visList is read only. On input, visList is SORTED visible spans. May not be the complete list of visible spans + // for this drawable. + // Called multiple times per scene render e.g.: + // Render(drawable0, visList0) // visList0 contains furthest spans in drawable0 + // Render(drawable1, visList1) // visList1 contains spans from drawable1 between drawable0's visList0 and visList2 + // Render(drawable0, visList2) // visList2 contains closest spans in drawable0. + virtual void Render(plDrawable* d, const hsTArray& visList) = 0; + // Draw - Convenience wrapper for standalone renders. Calls PreRender, PrepForRender, Render. Currently for internal + // use only, but may prove useful for procedurals (render to texture). + virtual void Draw(plDrawable* d) = 0; + + // Device-specific ref creation. Includes buffers and fonts + virtual plTextFont *MakeTextFont( char *face, UInt16 size ) = 0; + + // Create and/or Refresh geometry buffers + virtual void CheckVertexBufferRef(plGBufferGroup* owner, UInt32 idx) = 0; + virtual void CheckIndexBufferRef(plGBufferGroup* owner, UInt32 idx) = 0; + + virtual hsBool OpenAccess(plAccessSpan& dst, plDrawableSpans* d, const plVertexSpan* span, hsBool readOnly) = 0; + virtual hsBool CloseAccess(plAccessSpan& acc) = 0; + + virtual void CheckTextureRef(plLayerInterface* lay) = 0; + + // Default fog settings + virtual void SetDefaultFogEnviron( plFogEnvironment *fog ) = 0; + virtual const plFogEnvironment &GetDefaultFogEnviron( void ) const = 0; + + virtual void RegisterLight(plLightInfo* light) = 0; + virtual void UnRegisterLight(plLightInfo* light) = 0; + + enum RenderTargetFlags + { + kRTMainScreen = 0x0000, + kRTOffscreen = 0x0001, + kRTTexture = 0x0002, + kRTPerspProjected = 0x0004, + kRTOrthoProjected = 0x0008, + kRTProjected = kRTPerspProjected | kRTOrthoProjected + }; + enum RenderStateSettings + { + kRenderNormal = 0x0, + kRenderProjection = 0x1, + kRenderShadow = 0x2, + kRenderBaseLayerOnly = 0x4, + kRenderNoPiggyBacks = 0x8, + kRenderClearColor = 0x10, + kRenderClearDepth = 0x20, + kRenderOrthogonal = 0x40, + kRenderNoProjection = 0x80, + kRenderNoLights = 0x100, + kRenderShadowErase = 0x200 + }; + + virtual void PushRenderRequest(plRenderRequest* req) = 0; + virtual void PopRenderRequest(plRenderRequest* req) = 0; + + virtual void ClearRenderTarget( plDrawable* d ) = 0; // nil d reverts to ClearRenderTarget(nil, nil). + virtual void ClearRenderTarget(const hsColorRGBA* col = nil, const hsScalar* depth = nil) = 0; // col/depth are overrides for current default. + virtual void SetClear(const hsColorRGBA* col=nil, const hsScalar* depth=nil) = 0; // sets the default clear for current render target. + virtual hsColorRGBA GetClearColor() const = 0; + virtual hsScalar GetClearDepth() const = 0; + virtual hsGDeviceRef *MakeRenderTargetRef( plRenderTarget *owner ) = 0; + virtual void PushRenderTarget( plRenderTarget *target ) = 0; + virtual plRenderTarget *PopRenderTarget( void ) = 0; + + virtual hsBool BeginRender() = 0; + virtual hsBool EndRender() = 0; + virtual void RenderScreenElements( void ) = 0; + + virtual hsBool BeginDrawable(plDrawable* d) = 0; + virtual hsBool EndDrawable(plDrawable* d) = 0; + + virtual void BeginVisMgr(plVisMgr* visMgr) = 0; + virtual void EndVisMgr(plVisMgr* visMgr) = 0; + + virtual hsBool IsFullScreen() const = 0; + virtual UInt32 Width() const = 0; + virtual UInt32 Height() const = 0; + virtual UInt32 ColorDepth() const = 0; + virtual void Resize( UInt32 width, UInt32 height ) = 0; + + // Culling. Might be used in Update before bothering to do any serious computation. + virtual hsBool TestVisibleWorld(const hsBounds3Ext& wBnd) = 0; + virtual hsBool TestVisibleWorld(const plSceneObject* sObj) = 0; + virtual hsBool HarvestVisible(plSpaceTree* space, hsTArray& visList) = 0; + virtual hsBool SubmitOccluders(const hsTArray& polyList) = 0; + + virtual void SetDebugFlag( UInt32 flag, hsBool on ) = 0; + virtual hsBool IsDebugFlagSet( UInt32 flag ) const = 0; + virtual void SetMaxCullNodes(UInt16 n) = 0; // Debug/analysis only + virtual UInt16 GetMaxCullNodes() const = 0; // Debug/analysis only + + // Properties + enum Properties + { + kPropDontDeleteTextures = 0x00000001 // Keeps the pipeline from deleting textures on + // MakeTextureRef, regardless of the kUserOwnsBitmap flag + }; + + virtual hsBool CheckResources() = 0; // Do we need to call LoadResources? + virtual void LoadResources() = 0; + + virtual void SetProperty( UInt32 prop, hsBool on ) = 0; + virtual hsBool GetProperty( UInt32 prop ) const = 0; + virtual UInt32 GetMaxLayersAtOnce() const = 0; + + // Drawable type mask + virtual void SetDrawableTypeMask( UInt32 mask ) = 0; + virtual UInt32 GetDrawableTypeMask( void ) const = 0; + virtual void SetSubDrawableTypeMask( UInt32 mask ) = 0; + virtual UInt32 GetSubDrawableTypeMask( void ) const = 0; + + // View state + virtual hsPoint3 GetViewPositionWorld() const = 0; + virtual hsVector3 GetViewAcrossWorld() const = 0; + virtual hsVector3 GetViewUpWorld() const = 0; + virtual hsVector3 GetViewDirWorld() const = 0; + virtual void GetViewAxesWorld(hsVector3 axes[3] /* ac,up,at */ ) const = 0; + + virtual void GetFOV(hsScalar& fovX, hsScalar& fovY) const = 0; + virtual void SetFOV(hsScalar fovX, hsScalar fovY) = 0; + + virtual void GetSize(hsScalar& width, hsScalar& height) const = 0; + virtual void SetSize(hsScalar width, hsScalar height) = 0; + + virtual void GetDepth(hsScalar& hither, hsScalar& yon) const = 0; + virtual void SetDepth(hsScalar hither, hsScalar yon) = 0; + + virtual void SetZBiasScale( hsScalar scale ) = 0; + virtual hsScalar GetZBiasScale( void ) const = 0; + + virtual const hsMatrix44& GetWorldToCamera() const = 0; + virtual const hsMatrix44& GetCameraToWorld() const = 0; + virtual void SetWorldToCamera(const hsMatrix44& w2c, const hsMatrix44& c2w) = 0; + + virtual const hsMatrix44& GetWorldToLocal() const = 0; + virtual const hsMatrix44& GetLocalToWorld() const = 0; + + virtual const plViewTransform& GetViewTransform() const = 0; + + virtual void ScreenToWorldPoint( int n, UInt32 stride, Int32 *scrX, Int32 *scrY, + hsScalar dist, UInt32 strideOut, hsPoint3 *worldOut ) = 0; + + virtual void RefreshMatrices( void ) = 0; + virtual void RefreshScreenMatrices( void ) = 0; + + // Overrides, always push returns whatever is necessary to restore on pop. + virtual hsGMaterial* PushOverrideMaterial(hsGMaterial* mat) = 0; + virtual void PopOverrideMaterial(hsGMaterial* restore) = 0; + virtual hsGMaterial* GetOverrideMaterial() const = 0; + + virtual plLayerInterface* AppendLayerInterface(plLayerInterface* li, hsBool onAllLayers = false) = 0; + virtual plLayerInterface* RemoveLayerInterface(plLayerInterface* li, hsBool onAllLayers = false) = 0; + + virtual UInt32 GetMaterialOverrideOn(hsGMatState::StateIdx category) const = 0; + virtual UInt32 GetMaterialOverrideOff(hsGMatState::StateIdx category) const = 0; + + virtual hsGMatState PushMaterialOverride(const hsGMatState& state, hsBool on) = 0; + virtual hsGMatState PushMaterialOverride(hsGMatState::StateIdx cat, UInt32 which, hsBool on) = 0; + virtual void PopMaterialOverride(const hsGMatState& restore, hsBool on) = 0; + virtual const hsGMatState& GetMaterialOverride(hsBool on) const = 0; + + virtual hsColorOverride PushColorOverride(const hsColorOverride& over) = 0; + virtual void PopColorOverride(const hsColorOverride& restore) = 0; + virtual const hsColorOverride& GetColorOverride() const = 0; + + virtual void SubmitShadowSlave(plShadowSlave* slave) = 0; + virtual void SubmitClothingOutfit(plClothingOutfit* co) = 0; + + // These all return true if the gamma was successfully set. + virtual hsBool SetGamma(hsScalar eR, hsScalar eG, hsScalar eB) = 0; + virtual hsBool SetGamma(const UInt16* const tabR, const UInt16* const tabG, const UInt16* const tabB) = 0; // len table = 256. + virtual hsBool SetGamma(hsScalar e) { return SetGamma(e, e, e); } + virtual hsBool SetGamma(const UInt16* const table) { return SetGamma(table, table, table); } + + // flipVertical is for the AVI writer, which wants it's frames upside down + virtual hsBool CaptureScreen( plMipmap *dest, bool flipVertical = false, UInt16 desiredWidth = 0, UInt16 desiredHeight = 0 ) = 0; + + // Returns an un-named (GetKey()==nil) mipmap same dimensions as targ. You are responsible for deleting said mipMap. + virtual plMipmap* ExtractMipMap(plRenderTarget* targ) = 0; + + /// Error handling + virtual const char *GetErrorString( void ) = 0; + + // info about current rendering device + virtual void GetSupportedDisplayModes(std::vector *res, int ColorDepth = 32 ) = 0; + virtual int GetMaxAnisotropicSamples() = 0; + virtual int GetMaxAntiAlias(int Width, int Height, int ColorDepth) = 0; + int GetDesktopWidth() { return fDesktopParams.Width; } + int GetDesktopHeight() { return fDesktopParams.Height; } + int GetDesktopColorDepth() { return fDesktopParams.ColorDepth; } + PipelineParams *GetDefaultParams() { return &fDefaultPipeParams; } + + virtual void ResetDisplayDevice(int Width, int Height, int ColorDepth, hsBool Windowed, int NumAASamples, int MaxAnisotropicSamples, hsBool vSync = false ) = 0; + static PipelineParams fDefaultPipeParams; + static PipelineParams fInitialPipeParams; + plDisplayMode fDesktopParams; +}; + +#endif // plPipeline_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfile.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfile.h new file mode 100644 index 00000000..b4a78598 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfile.h @@ -0,0 +1,236 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plProfile_h_inc +#define plProfile_h_inc + +#include "hsTypes.h" + +#ifndef PLASMA_EXTERNAL_RELEASE +#define PL_PROFILE_ENABLED +#endif + +// +// These macros are all you should need to use to profile your code. They are +// compiled out in external release mode for maximum performance. +// +// plProfile_Create* should only be used at global scope, not in functions. +// Use plProfile_Extern if the plProfile_Create* for a variable is in another source file. +// +// --Examples-- +// +// plProfile_CreateCounter("Num Foobars", "Test", NumFoobars); +// void SomeFunc1() +// { +// plProfile_Inc(NumFoobars); +// plProfile_IncCount(NumFoobars, 5); +// } +// +// plProfile_CreateTimer("Foobar Time", "Test", FoobarTime); +// void SomeFunc2() +// { +// plProfile_BeginTiming(FoobarTime); +// (execute some code...) +// plProfile_EndTiming(FoobarTime); +// +// plProfile_BeginLap(FoobarTime, pKeyedObj->GetKeyName()); +// pKeyedObj->DoStuff(); +// plProfile_EndLap(FoobarTime, pKeyedObj->GetKeyName()); +// } +// + +#ifdef PL_PROFILE_ENABLED + +#define plProfile_CreateTimerNoReset(name, group, varName) plProfileVar gProfileVar##varName(name, group, plProfileVar::kDisplayTime | plProfileVar::kDisplayNoReset) +#define plProfile_CreateTimer(name, group, varName) plProfileVar gProfileVar##varName(name, group, plProfileVar::kDisplayTime) +#define plProfile_CreateAsynchTimer(name, group, varName) plProfileVar gProfileVar##varName(name, group, plProfileVar::kDisplayTime | plProfileVar::kDisplayResetEveryBegin | plProfileVar::kDisplayNoReset) +#define plProfile_BeginTiming(varName) gProfileVar##varName.BeginTiming() +#define plProfile_EndTiming(varName) gProfileVar##varName.EndTiming() +#define plProfile_BeginLap(varName, lapName) gProfileVar##varName.BeginLap(lapName) +#define plProfile_EndLap(varName, lapName) gProfileVar##varName.EndLap(lapName) + +#define plProfile_CreateCounter(name, group, varName) plProfileVar gProfileVar##varName(name, group, plProfileVar::kDisplayCount) +#define plProfile_CreateCounterNoReset(name, group, varName) plProfileVar gProfileVar##varName(name, group, plProfileVar::kDisplayCount | plProfileVar::kDisplayNoReset) +#define plProfile_Inc(varName) gProfileVar##varName.Inc() +#define plProfile_IncCount(varName, count) gProfileVar##varName.Inc(count) +#define plProfile_Dec(varName) gProfileVar##varName.Dec() +#define plProfile_Set(varName, value) gProfileVar##varName.Set(value) + +#define plProfile_CreateMemCounter(name, group, varName) plProfileVar gProfileVar##varName(name, group, plProfileVar::kDisplayMem | plProfileVar::kDisplayNoReset) +#define plProfile_CreateMemCounterReset(name, group, varName) plProfileVar gProfileVar##varName(name, group, plProfileVar::kDisplayMem) +#define plProfile_NewMem(varName, memAmount) gProfileVar##varName.NewMem(memAmount) +#define plProfile_DelMem(varName, memAmount) gProfileVar##varName.DelMem(memAmount) + +#define plProfile_StopVar(varName) gProfileVar##varName.Stop() +#define plProfile_StartVar(varName) gProfileVar##varName.Start() + +#define plProfile_Extern(varName) extern plProfileVar gProfileVar##varName + +#else + +#define plProfile_CreateTimerNoReset(name, group, varName) +#define plProfile_CreateTimer(name, group, varName) +#define plProfile_CreateAsynchTimer(name, group, varName) +#define plProfile_BeginTiming(varName) +#define plProfile_EndTiming(varName) +#define plProfile_BeginLap(varName, lapName) +#define plProfile_EndLap(varName, lapName) + +#define plProfile_CreateCounter(name, group, varName) +#define plProfile_CreateCounterNoReset(name, group, varName) +#define plProfile_Inc(varName) +#define plProfile_IncCount(varName, count) +#define plProfile_Dec(varName) +#define plProfile_Set(varName, value) + +#define plProfile_CreateMemCounter(name, group, varName) +#define plProfile_CreateMemCounterReset(name, group, varName) +#define plProfile_NewMem(varName, memAmount) +#define plProfile_DelMem(varName, memAmount) + +#define plProfile_StopVar(varName) +#define plProfile_StartVar(varName) + +#define plProfile_Extern(varName) + +#endif + +class plProfileLaps; + +class plProfileBase +{ +public: + enum + { + kDisplayCount = 0x1, + kDisplayTime = 0x2, + kDisplayMem = 0x4, + kDisplayNoReset = 0x8, + kDisplayFPS = 0x10, + kDisplayLaps = 0x20, + kDisplaySelected = 0x40, + kDisplayResetEveryBegin = 0x80 + }; + +protected: + const char* fName; // Name of timer + + UInt32 fValue; + + UInt32 fAvgCount; + UInt64 fAvgTotal; + UInt32 fLastAvg; + UInt32 fMax; + hsBool fActive; + hsBool fRunning; + UInt8 fDisplayFlags; + + // Number of times EndTiming was called. Can be used to combine timing and counting in one timer + UInt32 fTimerSamples; + + void IAddAvg(); + + void IPrintValue(UInt32 value, char* buf, hsBool printType); + +public: + plProfileBase(); + virtual ~plProfileBase(); + + virtual void BeginFrame(); + virtual void EndFrame(); + + void UpdateAvg(); + + UInt32 GetValue(); + + void PrintValue(char* buf, hsBool printType=true); + void PrintAvg(char* buf, hsBool printType=true); + void PrintMax(char* buf, hsBool printType=true); + + UInt32 GetTimerSamples() const { return fTimerSamples; } + + const char* GetName() { return fName; } + + void SetActive(hsBool s) { fActive = s; } + + void Stop() { fRunning = false; } + void Start() { fRunning = true; } + + UInt8 GetDisplayFlags() const { return fDisplayFlags; } + + void ResetMax() { fMax = 0; } +}; + +class plProfileVar : public plProfileBase +{ +protected: + const char* fGroup; + plProfileLaps* fLaps; + hsBool fLapsActive; + + plProfileVar() {} + + void IBeginTiming(); + void IEndTiming(); + + void IBeginLap(const char* lapName); + void IEndLap(const char* lapName); + +public: + // Name is the timer name. Each timer group gets its own plStatusLog + plProfileVar(const char *name, const char* group, UInt8 flags); + ~plProfileVar(); + + // For timing + void BeginTiming() { if (fActive && fRunning) IBeginTiming(); } + void EndTiming() { if (fActive && fRunning) IEndTiming(); } + + void NewMem(UInt32 memAmount) { fValue += memAmount; } + void DelMem(UInt32 memAmount) { fValue -= memAmount; } + + // For Counting + void Inc(int i = 1) { fValue += i;} + void Dec(int i = 1) { fValue -= i;} + + void Set(UInt32 value) { fValue = value; } + + // + // For multiple timings per frame of the same thing ie. Each particle system + // + // Will output to log like + // Timername : lapCnt: (lapName) : 3.22 msec + // + void BeginLap(const char* lapName) { if(fActive && fRunning) IBeginLap(lapName); } + void EndLap(const char* lapName) { if(fActive && fRunning) IEndLap(lapName); } + + const char* GetGroup() { return fGroup; } + + plProfileLaps* GetLaps() { return fLaps; } + + // Enable Lap Sampling + void SetLapsActive(hsBool s) { fLapsActive = s; } +}; + +#endif // plProfile_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfileManager.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfileManager.cpp new file mode 100644 index 00000000..e6f78d3a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfileManager.cpp @@ -0,0 +1,519 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plProfileManager.h" +#include "plProfile.h" +#include "hsTimer.h" + +#include "hsUtils.h" + +static UInt32 gCyclesPerMS = 0; + +#ifdef HS_BUILD_FOR_WIN32 +#define USE_FAST_TIMER +#endif + +#ifdef USE_FAST_TIMER + +#pragma warning (push) +#pragma warning (disable : 4035) // disable no return value warning + +__forceinline UInt32 GetPentiumCounter() +{ + __asm + { + xor eax,eax // VC won't realize that eax is modified w/out this + // instruction to modify the val. + // Problem shows up in release mode builds + _emit 0x0F // Pentium high-freq counter to edx;eax + _emit 0x31 // only care about low 32 bits in eax + + xor edx,edx // so VC gets that edx is modified + } +} + +#pragma warning (pop) + +#include "hsWindows.h" + +static UInt32 GetProcSpeed() +{ + const char* keypath[] = + { + "HARDWARE", + "DESCRIPTION", + "System", + "CentralProcessor", + "0" + }; + + HKEY hKey = HKEY_LOCAL_MACHINE; + + int numKeys = sizeof(keypath) / sizeof(char*); + for (int i = 0; i < numKeys; i++) + { + HKEY thisKey = NULL; + hsBool success = (RegOpenKeyEx(hKey, keypath[i], 0, KEY_READ, &thisKey) == ERROR_SUCCESS); + + RegCloseKey(hKey); + hKey = thisKey; + + if (!success) + return 0; + } + + DWORD value=0, size=sizeof(DWORD); + hsBool success = (RegQueryValueEx(hKey, "~MHz", 0, NULL, (BYTE*)&value, &size) == ERROR_SUCCESS); + RegCloseKey(hKey); + + return value*1000000; +} + +UInt32 GetProcSpeedAlt() +{ + const UInt32 kSamplePeriodMS = 250; + + // Raise priority to avoid interference from other threads. + int priority = GetThreadPriority(GetCurrentThread()); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + + UInt32 startTicks, endTicks; + UInt64 pcStart, pcEnd; + + // Count number of processor cycles inside the specified interval + QueryPerformanceCounter((LARGE_INTEGER*)&pcStart); + startTicks = plProfileManager::GetTime(); + Sleep(kSamplePeriodMS); + endTicks = plProfileManager::GetTime(); + QueryPerformanceCounter((LARGE_INTEGER*)&pcEnd); + + // Restore thread priority. + SetThreadPriority(GetCurrentThread(), priority); + + // Calculate Rdtsc/PerformanceCounter ratio; + UInt32 numTicks = endTicks - startTicks; + UInt64 pcDiff = pcEnd - pcStart; + + double ratio = double(numTicks) / double(pcDiff); + UInt64 pcFreq; + QueryPerformanceFrequency((LARGE_INTEGER*)&pcFreq); + + // Calculate CPU frequency. + UInt64 cpuFreq = UInt64(pcFreq * ratio); + + return (UInt32)cpuFreq; +} + +#define GetProfileTicks() GetPentiumCounter() + +#else + +#define GetProfileTicks() hsTimer::GetPrecTickCount() + +#endif // USE_FAST_TIMER + +#define TicksToMSec(t) (float(t) / float(gCyclesPerMS)) +#define MSecToTicks(t) (float(t) * float(gCyclesPerMS)) + +plProfileManager::plProfileManager() : fLastAvgTime(0), fProcessorSpeed(0) +{ +#ifdef USE_FAST_TIMER + fProcessorSpeed = GetProcSpeed(); + // Registry stuff only works on NT OS's, have to calc it otherwise + if (fProcessorSpeed == 0) + fProcessorSpeed = GetProcSpeedAlt(); + + gCyclesPerMS = fProcessorSpeed / 1000; +#else + gCyclesPerMS = hsTimer::GetPrecTicksPerSec() / 1000; +#endif +} + +plProfileManager::~plProfileManager() +{ +} + +plProfileManager& plProfileManager::Instance() +{ + static plProfileManager theInstance; + return theInstance; +} + +void plProfileManager::AddTimer(plProfileVar* var) +{ + fVars.push_back(var); +} + +static UInt32 kAvgMilliseconds = 1000; + +void plProfileManager::SetAvgTime(UInt32 avgMS) +{ + kAvgMilliseconds = avgMS; +} + +static plProfileVar gVarEFPS("EFPS", "General", plProfileVar::kDisplayTime | plProfileVar::kDisplayFPS); + +void plProfileManager::BeginFrame() +{ + for (int i = 0; i < fVars.size(); i++) + { + fVars[i]->BeginFrame(); + if (fVars[i]->GetLaps()) + fVars[i]->GetLaps()->BeginFrame(); + } + + gVarEFPS.BeginTiming(); +} + +void plProfileManager::EndFrame() +{ + gVarEFPS.EndTiming(); + + hsBool updateAvgs = false; + + // If enough time has passed, update the averages + double curTime = hsTimer::GetMilliSeconds(); + if (curTime - fLastAvgTime > kAvgMilliseconds) + { + fLastAvgTime = curTime; + updateAvgs = true; + } + + int i; + + // + // Update all the variables + // + for (i = 0; i < fVars.size(); i++) + { + plProfileVar* var = fVars[i]; + + if (updateAvgs) + { + // Timers that reset at every BeginTiming() call don't want to average over frames + if (!hsCheckBits(var->GetDisplayFlags(), plProfileBase::kDisplayResetEveryBegin)) + { + var->UpdateAvg(); + if (var->GetLaps()) + var->GetLaps()->UpdateAvgs(); + } + } + + var->EndFrame(); + + if (var->GetLaps()) + var->GetLaps()->EndFrame(); + } +} + +UInt32 plProfileManager::GetTime() +{ + return GetProfileTicks(); +} + +/////////////////////////////////////////////////////////////////////////////// + +plProfileBase::plProfileBase() : + fName(nil), + fDisplayFlags(0), + fValue(0), + fTimerSamples(0), + fAvgCount(0), + fAvgTotal(0), + fLastAvg(0), + fMax(0), + fActive(false), + fRunning(true) +{ +} + +plProfileBase::~plProfileBase() +{ +} + +void plProfileBase::BeginFrame() +{ + if (!hsCheckBits(fDisplayFlags, kDisplayNoReset)) + fValue = 0; + fTimerSamples = 0; +} + +void plProfileBase::EndFrame() +{ + fAvgCount++; + fAvgTotal += fValue; + fMax = hsMaximum(fMax, fValue); +} + +void plProfileBase::UpdateAvg() +{ + if (fAvgCount > 0) + { + fLastAvg = (UInt32)(fAvgTotal / fAvgCount); + fAvgCount = 0; + fAvgTotal = 0; + } +} + +UInt32 plProfileBase::GetValue() +{ + if (hsCheckBits(fDisplayFlags, kDisplayTime)) + return (UInt32)TicksToMSec(fValue); + else + return fValue; +} + +// Stolen from plMemTracker.cpp +static const char *insertCommas(unsigned int value) +{ + static char str[30]; + memset(str, 0, sizeof(str)); + + sprintf(str, "%u", value); + if (strlen(str) > 3) + { + memmove(&str[strlen(str)-3], &str[strlen(str)-4], 4); + str[strlen(str) - 4] = ','; + } + if (strlen(str) > 7) + { + memmove(&str[strlen(str)-7], &str[strlen(str)-8], 8); + str[strlen(str) - 8] = ','; + } + if (strlen(str) > 11) + { + memmove(&str[strlen(str)-11], &str[strlen(str)-12], 12); + str[strlen(str) - 12] = ','; + } + + return str; +} + +void plProfileBase::IPrintValue(UInt32 value, char* buf, hsBool printType) +{ + if (hsCheckBits(fDisplayFlags, kDisplayCount)) + { + if (printType) + { + const char* valueStr = insertCommas(value); + strcpy(buf, valueStr); + } + else + sprintf(buf, "%u", value); + } + else if (hsCheckBits(fDisplayFlags, kDisplayFPS)) + { + sprintf(buf, "%.1f", 1000.0f / TicksToMSec(value)); + } + else if (hsCheckBits(fDisplayFlags, kDisplayTime)) + { + sprintf(buf, "%.1f", TicksToMSec(value)); + if (printType) + strcat(buf, " ms"); + } + else if (hsCheckBits(fDisplayFlags, kDisplayMem)) + { + if (printType) + { + if (value > (1024*1000)) + sprintf(buf, "%.1f MB", float(value) / (1024.f * 1024.f)); + else if (value > 1024) + sprintf(buf, "%d KB", value / 1024); + else + sprintf(buf, "%d b", value); + } + else + sprintf(buf, "%u", value); + } +} + +void plProfileBase::PrintValue(char* buf, hsBool printType) +{ + IPrintValue(fValue, buf, printType); +} + +void plProfileBase::PrintAvg(char* buf, hsBool printType) +{ + IPrintValue(fLastAvg, buf, printType); +} + +void plProfileBase::PrintMax(char* buf, hsBool printType) +{ + IPrintValue(fMax, buf, printType); +} + +//////////////////////////////////////////////////////////////////////////////// + + +plProfileLaps::LapInfo* plProfileLaps::IFindLap(const char* lapName) +{ + static int lastSearch = 0; + + int i; + for (i = lastSearch; i < fLapTimes.size(); i++) + { + if(fLapTimes[i].GetName() == lapName) + { + lastSearch = i; + return &fLapTimes[i]; + } + } + + if(lastSearch > fLapTimes.size()) lastSearch = fLapTimes.size(); + for (i = 0; i < lastSearch; i++) + { + if(fLapTimes[i].GetName() == lapName) + { + lastSearch = i; + return &fLapTimes[i]; + } + } + return nil; +} + +void plProfileLaps::BeginLap(UInt32 curValue, const char* name) +{ + LapInfo* lap = IFindLap(name); + if (!lap) + { + // Technically we shouldn't hold on to this pointer. However, I think + // it will be ok in all cases, so I'll wait until this blows up + LapInfo info(name); + fLapTimes.push_back(info); + lap = &(*(fLapTimes.end()-1)); + } + + lap->fUsedThisFrame = true; + lap->BeginTiming(curValue); +} + +void plProfileLaps::EndLap(UInt32 curValue, const char* name) +{ + LapInfo* lap = IFindLap(name); + + // There's a lap timer around the input code. You display it with "Stats.ShowLaps Update Input" + // Since the command activates the timer INSIDE the lap, the first call to this function fails to + // find it. (the timer wasn't active when BeginLap was called) + if (lap) + lap->EndTiming(curValue); +} + +void plProfileLaps::BeginFrame() +{ + for (int i = 0; i < fLapTimes.size(); i++) + { + fLapTimes[i].BeginFrame(); + fLapTimes[i].fUsedThisFrame = false; + } +} + +void plProfileLaps::EndFrame() +{ + for (int i = 0; i < fLapTimes.size(); i++) + { + fLapTimes[i].EndFrame(); + if (!fLapTimes[i].fUsedThisFrame) + { + char buf[200]; + sprintf(buf, "Dropping unused lap %s", fLapTimes[i].GetName()); + hsStatusMessage(buf); + fLapTimes.erase(fLapTimes.begin()+i); + i--; + } + } +} + +void plProfileLaps::UpdateAvgs() +{ + for (int i = 0; i < fLapTimes.size(); i++) + fLapTimes[i].UpdateAvg(); +} + +int plProfileLaps::GetNumLaps() +{ +// std::sort(fLapTimes.begin(), fLapTimes.end()); + return fLapTimes.size(); +} + +plProfileBase* plProfileLaps::GetLap(int i) +{ + return &fLapTimes[i]; +} + + +/////////////////////////////////////////////////////////////////////////////// + + +plProfileVar::plProfileVar(const char *name, const char* group, UInt8 flags) : + fGroup(group), + fLaps(nil) +{ + fName = name; + fDisplayFlags = flags; + plProfileManager::Instance().AddTimer(this); + fLapsActive = 0; +} + +plProfileVar::~plProfileVar() +{ + delete fLaps; +} + +void plProfileVar::IBeginLap(const char* lapName) +{ + if (!fLaps) + fLaps = TRACKED_NEW plProfileLaps; + fDisplayFlags |= kDisplayLaps; + if(fLapsActive) + fLaps->BeginLap(fValue, lapName); + BeginTiming(); +} + +void plProfileVar::IEndLap(const char* lapName) +{ + EndTiming(); + if(fLapsActive) + fLaps->EndLap(fValue, lapName); +} + +void plProfileVar::IBeginTiming() +{ + if( hsCheckBits( fDisplayFlags, kDisplayResetEveryBegin ) ) + fValue = 0; + + fValue -= GetProfileTicks(); +} + +void plProfileVar::IEndTiming() +{ + fValue += GetProfileTicks(); + + fTimerSamples++; + + // If we reset every BeginTiming(), then we want to average all the timing calls + // independent of framerate + if (hsCheckBits(fDisplayFlags, plProfileBase::kDisplayResetEveryBegin)) + UpdateAvg(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfileManager.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfileManager.h new file mode 100644 index 00000000..43990e82 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plProfileManager.h @@ -0,0 +1,96 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plProfileManager_h_inc +#define plProfileManager_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "plProfile.h" + +class plProfileManager +{ +protected: + friend class plProfileManagerFull; + + typedef std::vector VarVec; + VarVec fVars; + + double fLastAvgTime; + + UInt32 fProcessorSpeed; + + plProfileManager(); + +public: + ~plProfileManager(); + + static plProfileManager& Instance(); + + void AddTimer(plProfileVar* var); // Called by plProfileVar + + void BeginFrame(); // Call begin frame on all timers + void EndFrame(); // Call end frame on all timers + + void SetAvgTime(UInt32 avgMS); + + UInt32 GetProcessorSpeed() { return fProcessorSpeed; } + + // Backdoor for hack timers in calculated profiles + static UInt32 GetTime(); +}; + +class plProfileLaps +{ +protected: + class LapInfo : public plProfileBase + { + protected: + + public: + bool fUsedThisFrame; + LapInfo(const char* name) { fName = name; fDisplayFlags = kDisplayTime; } + bool operator<(const LapInfo& rhs) const { return fLastAvg < rhs.fLastAvg; } + + void BeginTiming(UInt32 value) { fValue -= value; } + void EndTiming(UInt32 value) { fValue += value; fTimerSamples++; } + }; + std::vector fLapTimes; + + LapInfo* IFindLap(const char* lapName); + +public: + void BeginLap(UInt32 curValue, const char* name); + void EndLap(UInt32 curValue, const char* name); + + void BeginFrame(); + void EndFrame(); + void UpdateAvgs(); + + int GetNumLaps(); + plProfileBase* GetLap(int i); +}; + +#endif // plProfileManager_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plRefFlags.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plRefFlags.h new file mode 100644 index 00000000..ac8cb1b8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plRefFlags.h @@ -0,0 +1,37 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plRefFlags_inc +#define plRefFlags_inc + +namespace plRefFlags +{ + enum Type { + kActiveRef, // If one is resident, return it, else create and load one to return. Keep it loaded till I release. + kPassiveRef // Just notify me when it is loaded or unloaded until I release. + }; +}; + +#endif // plRefFlags_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plgDispatch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plgDispatch.h new file mode 100644 index 00000000..59b9887f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/plgDispatch.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDispatchBase_inc +#define plDispatchBase_inc + +#include "../pnFactory/plCreatable.h" + +class plMessage; +class plKey; + +class plDispatchBase : public plCreatable +{ +public: + CLASSNAME_REGISTER( plDispatchBase ); + GETINTERFACE_ANY( plDispatchBase, plCreatable ); + + virtual void RegisterForType(UInt16 hClass, const plKey& receiver) = 0; + virtual void RegisterForExactType(UInt16 hClass, const plKey& receiver) = 0; + + virtual void UnRegisterForType(UInt16 hClass, const plKey& receiver) = 0; + virtual void UnRegisterForExactType(UInt16 hClass, const plKey& receiver) = 0; + + virtual void UnRegisterAll(const plKey& receiver) = 0; + + virtual hsBool MsgSend(plMessage* msg, hsBool async=false) = 0; + virtual void MsgQueue(plMessage* msg)=0; // Used by other thread to Send Messages, they are handled as soon as Practicable + virtual void MsgQueueProcess() = 0; + virtual void MsgQueueOnOff(hsBool) = 0; // Turn on or off Queued Messages, if off, uses MsgSend Immediately (for plugins) + + virtual hsBool SetMsgBuffering(hsBool on) = 0; // On starts deferring msg delivery until buffering is set to off again. +}; + +class plgDispatch +{ +public: + static plDispatchBase* Dispatch(); + static hsBool MsgSend(plMessage* msg, hsBool async = false) { return Dispatch()->MsgSend(msg, async); } +}; + + +#endif // plDispatchBase_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnAllCreatables.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnAllCreatables.h new file mode 100644 index 00000000..f4b32356 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnAllCreatables.h @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pnAllCreatables_inc +#define pnAllCreatables_inc + +#include "pnNucleusCreatables.h" +#include "../../Apps/plClient/plClientCreatable.h" + +#endif // pnAllCreatables_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnNucleusCreatables.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnNucleusCreatables.h new file mode 100644 index 00000000..6a01c406 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnNucleusCreatables.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pnNucleusCreatables_inc +#define pnNucleusCreatables_inc + +#include "../pnFactory/plCreator.h" + +#include "plPipeline.h" + +REGISTER_NONCREATABLE( plPipeline ); + +#include "plAudible.h" +REGISTER_NONCREATABLE( plAudible ); + +#include "plDrawable.h" +REGISTER_NONCREATABLE( plDrawable ); + +#include "plPhysical.h" +REGISTER_NONCREATABLE( plPhysical ); + +#include "plgDispatch.h" +REGISTER_NONCREATABLE( plDispatchBase ); + +#include "../pnDispatch/pnDispatchCreatable.h" +#include "../pnKeyedObject/pnKeyedObjectCreatable.h" +#include "../pnMessage/pnMessageCreatable.h" +#include "../pnModifier/pnModifierCreatable.h" +#include "../pnNetCommon/pnNetCommonCreatable.h" +#include "../pnSceneObject/pnSceneObjectCreatable.h" +#include "../pnTimer/pnTimerCreatable.h" + +#endif // pnNucleusCreatables_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnSingletons.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnSingletons.cpp new file mode 100644 index 00000000..df19c507 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/inc/pnSingletons.cpp @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "plPipeResReq.h" + +hsResMgr* hsgResMgr::fResMgr = nil; + +plDispatchBase* plgDispatch::Dispatch() +{ + return hsgResMgr::Dispatch(); +} + +hsBool hsgResMgr::Init(hsResMgr* m) +{ + hsRefCnt_SafeAssign(fResMgr, m); + hsRefCnt_SafeUnRef(m); + if (!m->IInit()) + return false; + return true; +} + +void hsgResMgr::Shutdown() +{ + if (fResMgr) + { + if (fResMgr->RefCnt() <= 1) + { + fResMgr->IShutdown(); + hsRefCnt_SafeUnRef(fResMgr); + fResMgr = nil; + } + else + { + hsRefCnt_SafeUnRef(fResMgr); + } + } +} + +plPipeResReq& plPipeResReq::Instance() +{ + static plPipeResReq r; + return r; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.cpp new file mode 100644 index 00000000..c064b90e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.cpp @@ -0,0 +1,161 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef SERVER + + +#include "hsTypes.h" +#include "pnAddrInfo.h" +#include +#if HS_BUILD_FOR_UNIX +#include +#include +#include +#include +#include +#endif + +const pnAddrInfo* pnAddrInfo::GetInterface() +{ + static pnAddrInfo addrinfo; + return &addrinfo; +} + +struct addrinfo* pnAddrInfo::GetAddrByNameSimple(const char* name, const int family) +{ + struct addrinfo* info; + struct addrinfo hints; + memset(&hints,0,sizeof(hints)); + hints.ai_family = family; + hints.ai_flags = AI_CANONNAME; + if (Get(name,NULL,&hints,&info) != 0) + info = NULL; + return info; +}; + +int pnAddrInfo::Get(const char* node, const char* service, const struct addrinfo* hints, struct addrinfo** res) +{ + return getaddrinfo(node,service,hints,res); +} + +void pnAddrInfo::Free(struct addrinfo* res) +{ + freeaddrinfo(res); +} + +const char* pnAddrInfo::GetErrorString(int errcode) +{ + return gai_strerror(errcode); +} + +////////////////////////////////// +//// UNIX +////////////////////////////////// + +#if HS_BUILD_FOR_UNIX +pnAddrInfo::pnAddrInfo() +{ +} + +pnAddrInfo::~pnAddrInfo() +{ +} + +void pnAddrInfo::GetLocalAddrs(List& addrslist, bool incLoopBack) +{ + struct ifconf info; + struct ifreq infos[128]; + + info.ifc_len = sizeof(infos); + info.ifc_req = infos; + + int s = socket(AF_INET, SOCK_DGRAM, 0); + + if (ioctl(s,SIOCGIFCONF,&info) == 0) + { + int i; + int entries = info.ifc_len / sizeof(struct ifreq); + + for (i = 0; i < entries; i++) + { + struct in_addr addr = ((struct sockaddr_in *)&(infos[i].ifr_addr))->sin_addr; + + if (incLoopBack || (addr.s_addr != htonl(INADDR_LOOPBACK))) + { + addrslist.push_back(inet_ntoa(addr)); + } + } + } + + close(s); +} + +#endif + +//////////////////////////////////// +//// Windows +//////////////////////////////////// + +#if HS_BUILD_FOR_WIN32 +pnAddrInfo::pnAddrInfo() +{ + WSADATA data; + WSAStartup(MAKEWORD( 2, 2 ), &data); +} + +pnAddrInfo::~pnAddrInfo() +{ + WSACleanup(); +} + +void pnAddrInfo::GetLocalAddrs(List& addrslist, bool incLoopBack) +{ + INTERFACE_INFO infos[128]; // get at most 128 interfaces + const unsigned int bufSize = sizeof(infos); + DWORD retSize; + + SOCKET s = socket(AF_INET, SOCK_DGRAM, 0); + + if (WSAIoctl(s,SIO_GET_INTERFACE_LIST,NULL,NULL,(void*)infos,bufSize,&retSize,NULL,NULL) == 0) + { + unsigned int entries = retSize/sizeof(INTERFACE_INFO); + int i; + for (i = 0; i < entries; i++) + { + if (infos[i].iiFlags & IFF_UP) + { + struct in_addr addr = infos[i].iiAddress.AddressIn.sin_addr; + if (incLoopBack || (addr.s_addr != htonl(INADDR_LOOPBACK))) + addrslist.push_back(inet_ntoa(addr)); + } + } + } + + closesocket(s); +} +#endif + + +#endif // SERVER diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.h new file mode 100644 index 00000000..e8c3bad5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAddrInfo/pnAddrInfo.h @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PN_ADDR_INFO_H +#define PN_ADDR_INFO_H + +#ifndef SERVER + +// A wrapper library for getaddrinfo +// made to support Windows 98, Me, 2k +// Unix and XP use native code + +#if HS_BUILD_FOR_UNIX +#include +#include +#include +#elif HS_BUILD_FOR_WIN32 +#include +#include +#else +#error "pnAddrInfo Not Implemented!" +#endif +#include "hsTypes.h" +#include "hsStlUtils.h" + + +class pnAddrInfo +{ +public: + typedef std::list List; + + pnAddrInfo(); + ~pnAddrInfo(); + + static const pnAddrInfo* GetInterface(); + + static struct addrinfo* GetAddrByNameSimple(const char* name, const int family = PF_INET); + static int Get(const char* node, const char* service, const struct addrinfo* hints, struct addrinfo** res); + static const char* GetErrorString(int errcode); + static void Free(struct addrinfo* res); + + static void GetLocalAddrs(List& addrslist, bool incLoopBack = false); +}; + +#endif // SERVER + +#endif // PN_ADDR_INFO_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Pch.h new file mode 100644 index 00000000..8cb9468b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Pch.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PCH_H + + +#include "pnUtils/pnUtils.h" +#include "pnProduct/pnProduct.h" +#include "pnNetBase/pnNetBase.h" +#include "Private/pnAcAllIncludes.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcAllIncludes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcAllIncludes.h new file mode 100644 index 00000000..01d36f13 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcAllIncludes.h @@ -0,0 +1,42 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcAllIncludes.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACALLINCLUDES_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcAllIncludes.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACALLINCLUDES_H + + +#include "pnAcCore.h" +#include "pnAcIo.h" +#include "pnAcLog.h" +#include "pnAcThread.h" +#include "pnAcTimer.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcCore.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcCore.h new file mode 100644 index 00000000..e859e80f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcCore.h @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcCore.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACCORE_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcCore.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACCORE_H + + +/**************************************************************************** +* +* Library initialization +* +***/ + +void AsyncCoreInitialize (); +void AsyncCoreDestroy (unsigned waitMs); + +/***************************************************************************** +* +* Performance counters +* +***/ + +enum EAsyncPerfCounter { + // Sockets + kAsyncPerfSocketsCurr, + kAsyncPerfSocketsTotal, + kAsyncPerfSocketBytesWriteQueued, + kAsyncPerfSocketBytesWaitQueued, + kAsyncPerfSocketConnAttemptsOutCurr, + kAsyncPerfSocketConnAttemptsOutTotal, + kAsyncPerfSocketConnAttemptsInCurr, + kAsyncPerfSocketConnAttemptsInTotal, + kAsyncPerfSocketDisconnectBacklog, + kAsyncPerfSocketDisconnectInvalidConnType, + kAsyncPerfNameLookupAttemptsCurr, + kAsyncPerfNameLookupAttemptsTotal, + + // Files + kAsyncPerfFilesCurr, + kAsyncPerfFilesTotal, + kAsyncPerfFileBytesReadQueued, + kAsyncPerfFileBytesWriteQueued, + + // Threads + kAsyncPerfThreadsCurr, + kAsyncPerfThreadsTotal, + + // Thread tasks + kAsyncPerfThreadTaskListCount, + kAsyncPerfThreadTaskThreadsDesired, + kAsyncPerfThreadTaskThreadsRunning, + kAsyncPerfThreadTaskThreadsActive, + + kNumAsyncPerfCounters +}; + +long AsyncPerfGetCounter (unsigned id); + + +/***************************************************************************** +* +* Misc functions +* +***/ + +void AsyncSignalShutdown (); +void AsyncWaitForShutdown (); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcIo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcIo.h new file mode 100644 index 00000000..3cd468c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcIo.h @@ -0,0 +1,475 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcIo.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACIO_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcIo.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACIO_H + + +/**************************************************************************** +* +* Global types and constants +* +***/ + +typedef struct AsyncIdStruct * AsyncId; +typedef struct AsyncFileStruct * AsyncFile; +typedef struct AsyncSocketStruct * AsyncSocket; +typedef struct AsyncCancelIdStruct * AsyncCancelId; + +const unsigned kAsyncSocketBufferSize = 1460; + +enum EFileError { + kFileSuccess, + kFileErrorInvalidParameter, + kFileErrorFileNotFound, + kFileErrorPathNotFound, + kFileErrorAccessDenied, + kFileErrorSharingViolation, + kNumFileErrors +}; + +EFileError AsyncGetLastFileError (); + +const wchar * FileErrorToString (EFileError error); + + +/**************************************************************************** +* +* File notifications +* +***/ + +enum EAsyncNotifyFile { + kNotifyFileFlush, + kNotifyFileRead, + kNotifyFileWrite, + kNotifyFileSequence, + kNumFileNotifications +}; + +struct AsyncNotifyFile { + void * param; + AsyncId asyncId; +}; + +struct AsyncNotifyFileConnect : AsyncNotifyFile { + qword fileSize; + qword fileLastWriteTime; +}; + +struct AsyncNotifyFileFlush : AsyncNotifyFile { + EFileError error; + qword truncateSize; +}; + +struct AsyncNotifyFileRead : AsyncNotifyFile { + qword offset; + byte * buffer; + unsigned bytes; +}; + +typedef AsyncNotifyFileRead AsyncNotifyFileWrite; + +struct AsyncNotifyFileSequence : AsyncNotifyFile { + // no additional fields +}; + +typedef void (* FAsyncNotifyFileProc)( + AsyncFile file, + EAsyncNotifyFile code, + AsyncNotifyFile * notify, + void ** userState +); + + +/**************************************************************************** +* +* File I/O functions +* +***/ + +// Desired access +const unsigned kAsyncFileReadAccess = 0x80000000; +const unsigned kAsyncFileWriteAccess = 0x40000000; +// Open mode (creation disposition) +const unsigned kAsyncFileModeCreateNew = 1; +const unsigned kAsyncFileModeCreateAlways = 2; +const unsigned kAsyncFileModeOpenExisting = 3; +const unsigned kAsyncFileModeOpenAlways = 4; +// Share mode flags +const unsigned kAsyncFileShareRead = 0x00000001; +const unsigned kAsyncFileShareWrite = 0x00000002; + +AsyncFile AsyncFileOpen ( + const wchar fullPath[], + FAsyncNotifyFileProc notifyProc, + EFileError * error, + unsigned desiredAccess, + unsigned openMode, + unsigned shareModeFlags, // optional + void * userState, // optional + qword * fileSize, // optional + qword * fileLastWriteTime // optional +); + +// Use with AsyncFileDelete/AsyncFileFlushBuffers +const qword kAsyncFileDontTruncate = (qword) -1; + +// This function may ONLY be called when there is no outstanding I/O against a file +// and no more I/O will be initiated against it. This function guarantees that it +// will close the system file handle before it returns to that another open against +// the same filename can succeed. +void AsyncFileClose ( + AsyncFile file, + qword truncateSize +); + +void AsyncFileSetLastWriteTime ( + AsyncFile file, + qword lastWriteTime +); + +qword AsyncFileGetLastWriteTime ( + const wchar fileName[] +); + +// Truncation occurs atomically, any writes which occur after +// AsyncFileFlushBuffers will be queued until the truncation completes +AsyncId AsyncFileFlushBuffers ( + AsyncFile file, + qword truncateSize, + bool notify, + void * param +); + +const unsigned kAsyncFileRwNotify = 1<<0; +const unsigned kAsyncFileRwSync = 1<<1; + +AsyncId AsyncFileRead ( + AsyncFile file, + qword offset, + void * buffer, + unsigned bytes, + unsigned flags, + void * param +); + +// Buffer must stay valid until I/O is completed +AsyncId AsyncFileWrite ( + AsyncFile file, + qword offset, + const void * buffer, + unsigned bytes, + unsigned flags, + void * param +); + +// Inserts a "null operation" into the list of reads and writes. The callback +// will be called when all preceding operations have successfully completed. +AsyncId AsyncFileCreateSequence ( + AsyncFile file, + bool notify, + void * param +); + +enum EFileSeekFrom { + kFileSeekFromBegin, + kFileSeekFromCurrent, + kFileSeekFromEnd, + kNumFileSeekFroms +}; + +bool AsyncFileSeek ( + AsyncFile file, + qword distance, + EFileSeekFrom seekFrom +); + + +/**************************************************************************** +* +* Socket connect packet +* +***/ + +#include +struct AsyncSocketConnectPacket { + byte connType; + word hdrBytes; + dword buildId; + dword buildType; + dword branchId; + Uuid productId; +}; +#include + + +/**************************************************************************** +* +* Socket event notifications +* +***/ + +enum EAsyncNotifySocket { + kNotifySocketConnectFailed, + kNotifySocketConnectSuccess, + kNotifySocketDisconnect, + kNotifySocketListenSuccess, + kNotifySocketRead, + kNotifySocketWrite +}; + +struct AsyncNotifySocket { + void * param; + AsyncId asyncId; +}; + +struct AsyncNotifySocketConnect : AsyncNotifySocket { + NetAddress localAddr; + NetAddress remoteAddr; + unsigned connType; +}; + +struct AsyncNotifySocketListen : AsyncNotifySocketConnect { + unsigned buildId; + unsigned buildType; + unsigned branchId; + Uuid productId; + NetAddress addr; + byte * buffer; + unsigned bytes; + unsigned bytesProcessed; +}; + +struct AsyncNotifySocketRead : AsyncNotifySocket { + byte * buffer; + unsigned bytes; + unsigned bytesProcessed; +}; + +typedef AsyncNotifySocketRead AsyncNotifySocketWrite; + +typedef bool (* FAsyncNotifySocketProc) ( // return false to disconnect + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +); + + +/**************************************************************************** +* +* Connection type functions +* +***/ + +// These codes may not be changed unless ALL servers and clients are +// simultaneously replaced; so basically forget it =) +enum EConnType { + kConnTypeNil = 0, + + // For test applications + kConnTypeDebug = 1, + + // Binary connections + kConnTypeCliToAuth = 10, + kConnTypeCliToGame = 11, + kConnTypeSrvToAgent = 12, + kConnTypeSrvToMcp = 13, + kConnTypeSrvToVault = 14, + kConnTypeSrvToDb = 15, + kConnTypeCliToFile = 16, + kConnTypeSrvToState = 17, + kConnTypeSrvToLog = 18, + kConnTypeSrvToScore = 19, + kConnTypeCliToCsr = 20, + kConnTypeSimpleNet = 21, + kConnTypeCliToGateKeeper = 22, + + // Text connections + kConnTypeAdminInterface = 97, // 'a' + + kNumConnTypes +}; +COMPILER_ASSERT_HEADER(EConnType, kNumConnTypes < 256); + +#define IS_TEXT_CONNTYPE(c) \ + (((int)(c)) == kConnTypeAdminInterface) + + +void AsyncSocketRegisterNotifyProc ( + byte connType, + FAsyncNotifySocketProc notifyProc, + unsigned buildId = 0, + unsigned buildType = 0, + unsigned branchId = 0, + const Uuid & productId = kNilGuid +); + +void AsyncSocketUnregisterNotifyProc ( + byte connType, + FAsyncNotifySocketProc notifyProc, + unsigned buildId = 0, + unsigned buildType = 0, + unsigned branchId = 0, + const Uuid & productId = kNilGuid +); + +FAsyncNotifySocketProc AsyncSocketFindNotifyProc ( + const byte buffer[], + unsigned bytes, + unsigned * bytesProcessed, + unsigned * connType, + unsigned * buildId, + unsigned * buildType, + unsigned * branchId, + Uuid * productId +); + + +/**************************************************************************** +* +* Socket functions +* +***/ + +void AsyncSocketConnect ( + AsyncCancelId * cancelId, + const NetAddress & netAddr, + FAsyncNotifySocketProc notifyProc, + void * param = nil, + const void * sendData = nil, + unsigned sendBytes = 0, + unsigned connectMs = 0, // 0 => use default value + unsigned localPort = 0 // 0 => don't bind local port +); + +// Due to the asynchronous nature of sockets, the connect may complete +// before the cancel does... you have been warned. +void AsyncSocketConnectCancel ( + FAsyncNotifySocketProc notifyProc, + AsyncCancelId cancelId +); + +void AsyncSocketDisconnect ( + AsyncSocket sock, + bool hardClose +); + +// This function must only be called after receiving a kNotifySocketDisconnect +void AsyncSocketDelete (AsyncSocket sock); + +// Returns false of socket has been closed +bool AsyncSocketSend ( + AsyncSocket sock, + const void * data, + unsigned bytes +); + +// Buffer must stay valid until I/O has completed +// Returns false if socket has been closed +bool AsyncSocketWrite ( + AsyncSocket sock, + const void * buffer, + unsigned bytes, + void * param +); + +// This function must only be called from with a socket notification callback. +// Calling at any other time is a crash bug waiting to happen! +void AsyncSocketSetNotifyProc ( + AsyncSocket sock, + FAsyncNotifySocketProc notifyProc +); + +// A backlog of zero (the default) means that no buffering is performed when +// the TCP send buffer is full, and the send() function will close the socket +// on send fail +void AsyncSocketSetBacklogAlloc ( + AsyncSocket sock, + unsigned bufferSize +); + +// On failure, returns 0 +// On success, returns bound port (if port number was zero, returns assigned port) +// For connections that will use kConnType* connections, set notifyProc = nil; +// the handler will be found when connection packet is received. +// for connections with hard-coded behavior, set the notifyProc here (e.g. for use +// protocols like SNMP on port 25) +unsigned AsyncSocketStartListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc = nil +); +void AsyncSocketStopListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc = nil +); + +void AsyncSocketEnableNagling ( + AsyncSocket sock, + bool enable +); + + +/**************************************************************************** +* +* Dns functions +* +***/ + +typedef void (* FAsyncLookupProc) ( + void * param, + const wchar name[], + unsigned addrCount, + const NetAddress addrs[] +); + +void AsyncAddressLookupName ( + AsyncCancelId * cancelId, + FAsyncLookupProc lookupProc, + const wchar name[], + unsigned port, + void * param +); + +void AsyncAddressLookupAddr ( + AsyncCancelId * cancelId, + FAsyncLookupProc lookupProc, + const NetAddress & address, + void * param +); + +void AsyncAddressLookupCancel ( + FAsyncLookupProc lookupProc, + AsyncCancelId cancelId +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcLog.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcLog.cpp new file mode 100644 index 00000000..61a2a6bc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcLog.cpp @@ -0,0 +1,174 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcLog.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + +static const unsigned kMaxHandlers = 8; +static CCritSect s_critsect; +static FLogHandler s_asyncHandlers[kMaxHandlers]; + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +static void Dispatch (ELogSeverity severity, const wchar msg[]) { + + // Dispatch to default debug handler + char dbg[1024]; + StrToAnsi(dbg, msg, arrsize(dbg)); + DEBUG_MSG(dbg); + + // We don't need to enter a critical section to read the handlers because they + // are atomically set and cleared + for (unsigned i = 0; i < arrsize(s_asyncHandlers); ++i) { + if (FLogHandler asyncHandler = s_asyncHandlers[i]) + asyncHandler(severity, msg); + } +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +void LogRegisterHandler (FLogHandler callback) { + ASSERT(callback); + + unsigned i; + s_critsect.Enter(); + for (i = 0; i < arrsize(s_asyncHandlers); ++i) { + if (!s_asyncHandlers[i]) { + s_asyncHandlers[i] = callback; + break; + } + } + s_critsect.Leave(); + + #ifdef HS_DEBUGGING + if (i >= arrsize(s_asyncHandlers)) + FATAL("Maximum number of log handlers exceeded."); + #endif +} + +//=========================================================================== +void LogUnregisterHandler (FLogHandler callback) { + s_critsect.Enter(); + for (unsigned i = 0; i < arrsize(s_asyncHandlers); ++i) { + if (s_asyncHandlers[i] == callback) { + s_asyncHandlers[i] = nil; + break; + } + } + s_critsect.Leave(); +} + +//=========================================================================== +void __cdecl LogMsg (ELogSeverity severity, const char format[], ...) { + ASSERT(format); + + va_list args; + va_start(args, format); + LogMsgV(severity, format, args); + va_end(args); +} + +//=========================================================================== +void __cdecl LogMsg (ELogSeverity severity, const wchar format[], ...) { + ASSERT(format); + + va_list args; + va_start(args, format); + LogMsgV(severity, format, args); + va_end(args); +} + +//=========================================================================== +void LogMsgV (ELogSeverity severity, const char format[], va_list args) { + ASSERT(format); + + char msg[1024]; + StrPrintfV(msg, arrsize(msg), format, args); + + wchar uniMsg[1024]; + StrToUnicode(uniMsg, msg, arrsize(uniMsg)); + + Dispatch(severity, uniMsg); +} + +//=========================================================================== +void LogMsgV (ELogSeverity severity, const wchar format[], va_list args) { + ASSERT(format); + ASSERT(args); + + wchar msg[1024]; + StrPrintfV(msg, arrsize(msg), format, args); + + Dispatch(severity, msg); +} + +//============================================================================ +#ifdef HS_DEBUGGING +void LogMsgDebug (const char format[], ...) { + ASSERT(format); + + va_list args; + va_start(args, format); + LogMsgV(kLogDebug, format, args); + va_end(args); +} +#endif + +//============================================================================ +#ifdef HS_DEBUGGING +void LogMsgDebug (const wchar format[], ...) { + ASSERT(format); + + va_list args; + va_start(args, format); + LogMsgV(kLogDebug, format, args); + va_end(args); +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcLog.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcLog.h new file mode 100644 index 00000000..f1bad3f9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcLog.h @@ -0,0 +1,111 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcLog.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACLOG_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcLog.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACLOG_H + + +/**************************************************************************** +* +* Log API +* +***/ + +enum ELogSeverity { + // For indicating design problems + kLogDebug, + + // For indicating performance warnings + // (e.g. transaction failed, retrying...) + kLogPerf, + + // For indicating error conditions that change program behavior + // (e.g. socket connect failed) + kLogError, + + // For indicating failures that may lead to program termination + // (e.g. out of memory) + kLogFatal, + + kNumLogSeverity +}; + +void LogMsg (ELogSeverity severity, const char format[], ...); +void LogMsg (ELogSeverity severity, const wchar format[], ...); +void LogMsgV (ELogSeverity severity, const char format[], va_list args); +void LogMsgV (ELogSeverity severity, const wchar format[], va_list args); + +void LogBreakOnErrors (bool breakOnErrors); + +void AsyncLogInitialize ( + const wchar logDirName[], + bool breakOnErrors +); +void AsyncLogDestroy (); +void AsyncLogFlush (); + +void AsyncLogGetDirectory (wchar * dest, unsigned destChars); + + +// Low(er) level log API; call this from your LogHander function +// if you want to use the asynchronous log facility. +void AsyncLogWriteMsg ( + const wchar facility[], + ELogSeverity severity, + const wchar msg[] +); + +// FLogHandler must be capable of handling multiple threads and re-entrancy +typedef void (* FLogHandler) (ELogSeverity severity, const wchar msg[]); + +void LogRegisterHandler (FLogHandler callback); +void LogUnregisterHandler (FLogHandler callback); + + +/**************************************************************************** +* +* Debugging API +* +***/ + +#ifdef HS_DEBUGGING + + void LogMsgDebug (const char format[], ...); + void LogMsgDebug (const wchar format[], ...); + +#else + + inline void LogMsgDebug (const char *, ...) { } + inline void LogMsgDebug (const wchar *, ...) { } + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcThread.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcThread.h new file mode 100644 index 00000000..d59c86c7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcThread.h @@ -0,0 +1,130 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcThread.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACTHREAD_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcThread.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACTHREAD_H + + +/**************************************************************************** +* +* Type definitions +* +***/ + +// for IoWaitId/TimerCreate/TimerUpdate +const unsigned kAsyncTimeInfinite = (unsigned) -1; + +#ifdef _MSC_VER +#define THREADCALL __stdcall +#else +#define THREADCALL __cdecl +#endif + +struct AsyncThread; +typedef unsigned (THREADCALL * FAsyncThreadProc)(AsyncThread * thread); + + +// Threads are also allowed to set the workTimeMs field of their +// structure to a nonzero value for "on", and IO_TIME_INFINITE for +// "off" to avoid the overhead of calling these functions. Note +// that this function may not be called for the main thread. I +// suggest that application code not worry that timeMs might +// "accidentally" equal the IO_TIME_INFINITE value, as it only +// happens for one millisecond every 49 days. +struct AsyncThread { + LINK(AsyncThread) link; + FAsyncThreadProc proc; + void * handle; + void * argument; + unsigned workTimeMs; + wchar name[16]; +}; + +/***************************************************************************** +* +* Thread functions +* +***/ + +void * AsyncThreadCreate ( + FAsyncThreadProc proc, + void * argument, + const wchar name[] +); + +// This function should ONLY be called during shutdown while waiting for things to expire +void AsyncSleep (unsigned sleepMs); + + +/***************************************************************************** +* +* Thread task functions +* +***/ + +enum EThreadTaskPriority { + kThreadTaskPriorityNormal = 1, + kNumThreadTaskPriorities +}; + +const unsigned kThreadTaskMinThreads = 5; +const unsigned kThreadTaskDefThreads = 100; +const unsigned kThreadTaskMaxThreads = 1000; + +struct AsyncThreadTaskList; + +typedef void (* FAsyncThreadTask)( + void * param, + ENetError error +); + + +void AsyncThreadTaskInitialize (unsigned threads); +void AsyncThreadTaskDestroy (); + +unsigned AsyncThreadTaskGetThreadCount (); +void AsyncThreadTaskSetThreadCount (unsigned threads); + +AsyncThreadTaskList * AsyncThreadTaskListCreate (); +void AsyncThreadTaskListDestroy ( + AsyncThreadTaskList * taskList, + ENetError error +); + +void AsyncThreadTaskAdd ( + AsyncThreadTaskList * taskList, + FAsyncThreadTask callback, + void * param, + const wchar debugStr[], + EThreadTaskPriority priority = kThreadTaskPriorityNormal +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcTimer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcTimer.h new file mode 100644 index 00000000..e99c44ed --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcTimer.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcTimer.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACTIMER_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/Private/pnAcTimer.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PRIVATE_PNACTIMER_H + + +/***************************************************************************** +* +* Timer functions +* +* Timers are repeatedly called back at a scheduled interval. Not that all +* timer procedures share the same thread, so timer procedures should: +* +* 1) Not be called too frequently +* 2) Not take too long to run or block for a long time +* +***/ + +struct AsyncTimer; + +// Return callbackMs to wait that long until next callback. +// Return kAsyncTimeInfinite to stop callbacks (note: does not destroy Timer structure) +typedef unsigned (* FAsyncTimerProc)(void * param); + +// 1) Timer procs do not get starved by I/O, they are called periodically. +// 2) Timer procs will never be called by multiple threads simultaneously. +void AsyncTimerCreate ( + AsyncTimer ** timer, + FAsyncTimerProc timerProc, + unsigned callbackMs, + void * param = nil +); + +// Timer procs can be in the process of getting called in +// another thread during the unregister function -- be careful! +// -- waitComplete = will wait until the timer has been unregistered and is +// no longer in the process of being called before returning. The flag may only +// be set by init/destruct threads, not I/O worker threads. In addition, extreme +// care should be used to avoid a deadlock when this flag is set; in general, it +// is a good idea not to hold any locks or critical sections when setting the flag. +const unsigned kAsyncTimerDestroyWaitComplete = 1<<0; +void AsyncTimerDelete ( + AsyncTimer * timer, + unsigned flags = 0 +); +void AsyncTimerDeleteCallback ( + AsyncTimer * timer, + FAsyncTimerProc destroyProc +); + +// To set the time value for a timer, use this function with flags = 0. +// To set the time to MoreRecentOf(nextTimerCallbackMs, callbackMs), use SetPriorityHigher +const unsigned kAsyncTimerUpdateSetPriorityHigher = 1<<0; +void AsyncTimerUpdate ( + AsyncTimer * timer, + unsigned callbackMs, + unsigned flags = 0 +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/pnAsyncCore.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/pnAsyncCore.h new file mode 100644 index 00000000..2f0e21ca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/pnAsyncCore.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCore/pnAsyncCore.h +* +* Asynchronous APIs: +* Sockets +* Files +* Threads +* Logging facility +* Callback facility +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PNASYNCCORE_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PNASYNCCORE_H + + +#include "Private/pnAcAllIncludes.h" + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCORE_PNASYNCCORE_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Pch.h new file mode 100644 index 00000000..b191019f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Pch.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PCH_H + +#include "pnUtils/pnUtils.h" +#include "pnProduct/pnProduct.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" + +#ifdef SERVER +#include "pnCrash/pnCrash.h" // deadlock API +#endif + +#include "Private/pnAceInt.h" +#include "Private/W9x/pnAceW9x.h" +#include "Private/Nt/pnAceNt.h" +#include "Private/Unix/pnAceUx.h" + +#include +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNt.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNt.cpp new file mode 100644 index 00000000..145f9a5b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNt.cpp @@ -0,0 +1,487 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNt.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +#include "pnAceNtInt.h" + + +namespace Nt { + +/**************************************************************************** +* +* Private data +* +***/ + +// Use non-allocated arrays for worker threads since they're used so frequently. +const unsigned kMaxWorkerThreads = 32; // handles 8-processor computer w/hyperthreading + +static bool s_running; +static HANDLE s_waitEvent; + +static long s_ioThreadCount; +static HANDLE s_ioThreadHandles[kMaxWorkerThreads]; + +static HANDLE s_ioPort; +static unsigned s_pageSizeMask; + + +/**************************************************************************** +* +* Waitable event handles +* +***/ + +//=========================================================================== +CNtWaitHandle::CNtWaitHandle () { + m_refCount = 1; + m_event = CreateEvent( + (LPSECURITY_ATTRIBUTES) nil, + true, // manual reset + false, // initial state + (LPCTSTR) nil + ); +} + +//=========================================================================== +CNtWaitHandle::~CNtWaitHandle () { + CloseHandle(m_event); +} + +//=========================================================================== +void CNtWaitHandle::IncRef () { + InterlockedIncrement(&m_refCount); +} + +//=========================================================================== +void CNtWaitHandle::DecRef () { + if (!InterlockedDecrement(&m_refCount)) + DEL(this); +} + +//=========================================================================== +bool CNtWaitHandle::WaitForObject (unsigned timeMs) const { + return WAIT_TIMEOUT != WaitForSingleObject(m_event, timeMs); +} + +//=========================================================================== +void CNtWaitHandle::SignalObject () const { + SetEvent(m_event); +} + + +/**************************************************************************** +* +* OPERATIONS +* +***/ + +//=========================================================================== +static void INtOpDispatch ( + NtObject * ntObj, + Operation * op, + dword bytes +) { + for (;;) { + switch (op->opType) { + case kOpConnAttempt: + INtSocketOpCompleteSocketConnect((NtOpConnAttempt *) op); + // operation not associated with ntObj so there is no next operation. + // operation has already been deleted by OpCompleteSocketConnect. + return; + + case kOpQueuedSocketWrite: + INtSocketOpCompleteQueuedSocketWrite((NtSock *) ntObj, (NtOpSocketWrite *) op); + // operation converted into kOpSocketWrite so we cannot move + // to next operation until write operation completes + return; + + case kOpSocketRead: + ASSERT(bytes != (dword) -1); + INtSocketOpCompleteSocketRead((NtSock *) ntObj, bytes); + return; + + case kOpSocketWrite: + ASSERT(bytes != (dword) -1); + INtSocketOpCompleteSocketWrite((NtSock *) ntObj, (NtOpSocketWrite *) op); + break; + + case kOpQueuedFileRead: + case kOpQueuedFileWrite: + INtFileOpCompleteQueuedReadWrite((NtFile *) ntObj, (NtOpFileReadWrite *) op); + // operation converted into kOpFileWrite so we cannot move + // to next operation until write operation completes + return; + + case kOpFileRead: + case kOpFileWrite: + ASSERT(bytes != (dword) -1); + if (!INtFileOpCompleteReadWrite((NtFile *) ntObj, (NtOpFileReadWrite *) op, bytes)) + return; + break; + + case kOpFileFlush: + INtFileOpCompleteFileFlush((NtFile *) ntObj, (NtOpFileFlush *) op); + break; + + case kOpSequence: + INtFileOpCompleteSequence((NtFile *) ntObj, (NtOpFileSequence *) op); + break; + + DEFAULT_FATAL(opType); + } + + // if this operation is not at the head of the list then it can't be completed + // because nextCompleteSequence would be prematurely incremented. Instead + // convert the operation to OP_NULL, which will get completed when it reaches + // the head of the list. + ntObj->critsect.Enter(); + if (ntObj->opList.Prev(op)) { + // setting the completion flag must be done inside the critical section + // because it will be checked by sibling operations when they have the + // critical section. + op->pending = 0; + ntObj->critsect.Leave(); + return; + } + + // complete processing this event, and, since we're still inside the critical + // section, finish all completed operations since we don't have to leave the + // critical section to do so. This is a big win because a single operation + // that takes a long time to complete can backlog a long list of completed ops. + bool continueDispatch; + for (;;) { + // wake up any other threads waiting on this event + CNtWaitHandle * signalComplete = op->signalComplete; + op->signalComplete = nil; + + // since this operation is at the head of the list we can complete it + if (op->asyncId && !++ntObj->nextCompleteSequence) + ++ntObj->nextCompleteSequence; + Operation * next = ntObj->opList.Next(op); + ntObj->opList.Delete(op); + op = next; + + // set event *after* operation is complete + if (signalComplete) { + signalComplete->SignalObject(); + signalComplete->DecRef(); + } + + // if we just deleted the last operation then stop dispatching + if (!op) { + continueDispatch = false; + break; + } + + // opTypes >= kOpSequence complete when they reach the head of the list + continueDispatch = op->opType >= kOpSequence; + if (op->pending) + break; + + InterlockedDecrement(&ntObj->ioCount); + } + ntObj->critsect.Leave(); + + INtConnCompleteOperation(ntObj); + + if (!continueDispatch) + break; + + // certain operations which depend upon the value of bytes (reads & writes) + // can only be dispatched when they are completed normally. To ensure that + // we're not accidentally processing an operation that shouldn't be executed, + // set the bytes field to an invalid value. + bytes = (dword) -1; + } +} + +//=========================================================================== +static unsigned THREADCALL NtWorkerThreadProc (AsyncThread * thread) { + ref(thread); + + ThreadDenyBlock(); + + unsigned sleepMs = INFINITE; + while (s_running) { + + // process I/O operations + { + dword bytes; + NtObject * ntObj; + Operation * op; + (void) GetQueuedCompletionStatus( + s_ioPort, + &bytes, + #ifdef _WIN64 + (PULONG_PTR) &ntObj, + #else + (LPDWORD) &ntObj, + #endif + (LPOVERLAPPED *) &op, + sleepMs + ); + + if (op) { + // Queue for deadlock detection + #ifdef SERVER + void * check = CrashAddDeadlockCheck(thread->handle, L"pnAceNt.NtWorkerThread"); + #endif + + // Dispatch event to app + INtOpDispatch(ntObj, op, bytes); + + // Unqueue from deadlock detection + #ifdef SERVER + CrashRemoveDeadlockCheck(check); + #endif + + sleepMs = 0; + continue; + } + } + + sleepMs = INFINITE; + continue; + } + + return 0; +} + + +/**************************************************************************** +* +* Module functions +* +***/ + +//=========================================================================== +void INtConnPostOperation (NtObject * ntObj, Operation * op, unsigned bytes) { + PostQueuedCompletionStatus( + s_ioPort, + bytes, + #ifdef _WIN64 + (ULONG_PTR) ntObj, + #else + (DWORD) ntObj, + #endif + &op->overlapped + ); +} + +//=========================================================================== +AsyncId INtConnSequenceStart (NtObject * ntObj) { + unsigned result; + if (0 == (result = ++ntObj->nextStartSequence)) + result = ++ntObj->nextStartSequence; + return (AsyncId) result; +} + +//=========================================================================== +bool INtConnInitialize (NtObject * ntObj) { + if (!CreateIoCompletionPort(ntObj->handle, s_ioPort, (DWORD) ntObj, 0)) { + LogMsg(kLogFatal, "CreateIoCompletionPort failed"); + return false; + } + + return true; +} + +//=========================================================================== +void INtConnCompleteOperation (NtObject * ntObj) { + // are we completing the last operation for this object? + if (InterlockedDecrement(&ntObj->ioCount)) + return; + + DWORD err = GetLastError(); + ref(err); + switch (ntObj->ioType) { + case kNtFile: + INtFileDelete((NtFile *) ntObj); + break; + + case kNtSocket: + INtSockDelete((NtSock *) ntObj); + break; + + default: + LogMsg(kLogError, "NtConnCompleteOp %p %u", ntObj, ntObj->ioType); + break; + } +} + +/***************************************************************************** +* +* Module exports +* +***/ + +//=========================================================================== +void NtInitialize () { + // ensure initialization only occurs once + if (s_running) + return; + s_running = true; + + // create a cleanup event + s_waitEvent = CreateEvent( + (LPSECURITY_ATTRIBUTES) 0, + true, // manual reset + false, // initial state off + (LPCTSTR) nil // name + ); + if (!s_waitEvent) + ErrorFatal(__LINE__, __FILE__, "CreateEvent %#x", GetLastError()); + + // create IO completion port + if (0 == (s_ioPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0))) + ErrorFatal(__LINE__, __FILE__, "CreateIoCompletionPort %#x", GetLastError()); + + // calculate number of IO worker threads to create + if (!s_pageSizeMask) { + SYSTEM_INFO si; + GetSystemInfo(&si); + s_pageSizeMask = si.dwPageSize - 1; + + // Set worker thread count + s_ioThreadCount = si.dwNumberOfProcessors * 2; + if (s_ioThreadCount > kMaxWorkerThreads) { + s_ioThreadCount = kMaxWorkerThreads; + LogMsg(kLogError, "kMaxWorkerThreads too small!"); + } + } + + // create IO worker threads + for (long thread = 0; thread < s_ioThreadCount; thread++) { + s_ioThreadHandles[thread] = (HANDLE) AsyncThreadCreate( + NtWorkerThreadProc, + (void *) thread, + L"NtWorkerThread" + ); + } + + INtFileInitialize(); + INtSocketInitialize(); +} + +//=========================================================================== +// DANGER: calling this function will slam closed any files which are still open. +// MOST PROGRAMS DO NOT NEED TO CALL THIS FUNCTION. In general, the best way to +// shut down the program is to simply let the atexit() handler take care of it. +void NtDestroy (unsigned exitThreadWaitMs) { + // cleanup modules that post completion notifications as part of their shutdown + INtFileStartCleanup(); + INtSocketStartCleanup(exitThreadWaitMs); + + // cleanup worker threads + s_running = false; + + if (s_ioPort) { + // Post a completion notification to worker threads to wake them up + long thread; + for (thread = 0; thread < s_ioThreadCount; thread++) + PostQueuedCompletionStatus(s_ioPort, 0, 0, 0); + + // Close each thread + for (thread = 0; thread < s_ioThreadCount; thread++) { + if (s_ioThreadHandles[thread]) { + WaitForSingleObject(s_ioThreadHandles[thread], exitThreadWaitMs); + CloseHandle(s_ioThreadHandles[thread]); + s_ioThreadHandles[thread] = nil; + } + } + + // Cleanup port + CloseHandle(s_ioPort); + s_ioPort = 0; + } + + if (s_waitEvent) { + CloseHandle(s_waitEvent); + s_waitEvent = 0; + } + + INtFileDestroy(); + INtSocketDestroy(); +} + +//=========================================================================== +void NtSignalShutdown () { + SetEvent(s_waitEvent); +} + +//=========================================================================== +void NtWaitForShutdown () { + if (s_waitEvent) + WaitForSingleObject(s_waitEvent, INFINITE); +} + +} using namespace Nt; + + +/**************************************************************************** +* +* Public exports +* +***/ + +//=========================================================================== +void NtGetApi (AsyncApi * api) { + api->initialize = NtInitialize; + api->destroy = NtDestroy; + api->signalShutdown = NtSignalShutdown; + api->waitForShutdown = NtWaitForShutdown; + api->sleep = NtSleep; + + api->fileOpen = NtFileOpen; + api->fileClose = NtFileClose; + api->fileRead = NtFileRead; + api->fileWrite = NtFileWrite; + api->fileFlushBuffers = NtFileFlushBuffers; + api->fileSetLastWriteTime = NtFileSetLastWriteTime; + api->fileGetLastWriteTime = NtFileGetLastWriteTime; + api->fileCreateSequence = NtFileCreateSequence; + api->fileSeek = NtFileSeek; + + api->socketConnect = NtSocketConnect; + api->socketConnectCancel = NtSocketConnectCancel; + api->socketDisconnect = NtSocketDisconnect; + api->socketDelete = NtSocketDelete; + api->socketSend = NtSocketSend; + api->socketWrite = NtSocketWrite; + api->socketSetNotifyProc = NtSocketSetNotifyProc; + api->socketSetBacklogAlloc = NtSocketSetBacklogAlloc; + api->socketStartListening = NtSocketStartListening; + api->socketStopListening = NtSocketStopListening; + api->socketEnableNagling = NtSocketEnableNagling; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNt.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNt.h new file mode 100644 index 00000000..86edf5cb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNt.h @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNt.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_NT_PNACENT_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNt.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_NT_PNACENT_H + + +#ifdef HS_BUILD_FOR_WIN32 + +/**************************************************************************** +* +* Nt API functions +* +***/ + +void NtGetApi (AsyncApi * api); + +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtFile.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtFile.cpp new file mode 100644 index 00000000..edfcf08f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtFile.cpp @@ -0,0 +1,994 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtFile.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +#include "pnAceNtInt.h" + + +namespace Nt { + +/**************************************************************************** +* +* Private +* +***/ + +// Must be a multiple of largest possible disk sector size +const unsigned kSplitRwBytes = 4 * 1024 * 1024; + + +struct NtOpFileReadWrite : Operation { + NtOpFileReadWrite * masterOp; + unsigned win32Bytes; + AsyncNotifyFileRead rw; +}; + +struct NtOpFileFlush : Operation { + AsyncNotifyFileFlush flush; +}; + +struct NtOpFileSequence : Operation { + AsyncNotifyFileSequence sequence; +}; + +struct NtFile : NtObject { + FAsyncNotifyFileProc notifyProc; + LINK(NtFile) openLink; // protected by s_fileCrit + LINK(NtFile) pendLink; // protected by s_fileCrit + unsigned queueWrites; + unsigned sectorSizeMask; + wchar fullPath[MAX_PATH]; + + NtFile (); + ~NtFile (); +}; + +static long s_fileOps; +static CNtCritSect s_fileCrit; +static LISTDECL(NtFile, openLink) s_openFiles; +static LISTDECL(NtFile, pendLink) s_pendFiles; +static AsyncTimer * s_timer; + + +//=========================================================================== +inline NtFile::NtFile () { + PerfAddCounter(kAsyncPerfFilesCurr, 1); + PerfAddCounter(kAsyncPerfFilesTotal, 1); +} + +//=========================================================================== +NtFile::~NtFile () { + PerfSubCounter(kAsyncPerfFilesCurr, 1); +} + +//=========================================================================== +static void FatalOnNonRecoverableError ( + const NtOpFileReadWrite & op, + unsigned error +) { + switch (error) { + case ERROR_NO_SYSTEM_RESOURCES: + case ERROR_NONPAGED_SYSTEM_RESOURCES: + case ERROR_PAGED_SYSTEM_RESOURCES: + case ERROR_WORKING_SET_QUOTA: + case ERROR_PAGEFILE_QUOTA: + case ERROR_COMMITMENT_LIMIT: + return; + } + + ASSERT((op.opType == kOpFileRead) || (op.opType == kOpFileWrite)); + ErrorFatal( + __LINE__, __FILE__, + "Disk %s failed, error: %u", + op.opType == kOpFileRead ? "read" : "write", + error + ); +} + +//=========================================================================== +static unsigned INtFileTimerProc (void *) { + if (!s_pendFiles.Head()) + return INFINITE; + + if (!s_fileCrit.TryEnter()) + return 10; + + // dequeue head of list + NtFile * file = s_pendFiles.Head(); + if (file) + s_pendFiles.Unlink(file); + s_fileCrit.Leave(); + if (!file) + return INFINITE; + + // retry operation + ASSERT(file->opList.Head()); + ASSERT((file->opList.Head()->opType == kOpQueuedFileRead) + || (file->opList.Head()->opType == kOpQueuedFileWrite) + ); + INtFileOpCompleteQueuedReadWrite(file, (NtOpFileReadWrite *) file->opList.Head()); + return 0; +} + +//=========================================================================== +static void HandleFailedOp ( + NtFile * file, + NtOpFileReadWrite * op, + unsigned error +) { + ASSERT((op->opType == kOpFileRead) || (op->opType == kOpFileWrite)); + + // break the operation into a bunch of sub-operations if it hasn't already been done + unsigned subOperations = 0; + LISTDECL(NtOpFileReadWrite, link) opList; + if (!op->masterOp) { + // setup master operation to read the start of the buffer; this + // ensures that op->rw.* is unchanged for the master operation, + // which is important for the user notification callback + op->masterOp = op; + op->win32Bytes = min(kSplitRwBytes, op->rw.bytes); + unsigned position = op->win32Bytes; + + // create sub-operations to read the rest of the buffer + for (; position < op->rw.bytes; ++subOperations) { + NtOpFileReadWrite * childOp = NEW(NtOpFileReadWrite); + childOp->overlapped.hEvent = op->overlapped.hEvent ? CreateEvent(nil, true, false, nil) : nil; + childOp->overlapped.Offset = (dword) ((op->rw.offset + position) & 0xffffffff); + childOp->overlapped.OffsetHigh = (dword) ((op->rw.offset + position) >> 32); + childOp->opType = op->opType; + childOp->asyncId = 0; + childOp->notify = false; + childOp->pending = 1; + childOp->signalComplete = nil; + childOp->masterOp = op; + childOp->win32Bytes = min(kSplitRwBytes, op->rw.bytes - position); + childOp->rw.param = nil; + childOp->rw.asyncId = 0; + childOp->rw.offset = op->rw.offset + position; + childOp->rw.buffer = op->rw.buffer + position; + childOp->rw.bytes = childOp->win32Bytes; + opList.Link(childOp, kListTail); + position += childOp->win32Bytes; + } + + InterlockedExchangeAdd(&file->ioCount, (long) subOperations); + } + + bool autoComplete = true; + unsigned eventCount = 0; + HANDLE events[MAXIMUM_WAIT_OBJECTS]; + + file->critsect.Enter(); + + // start with the master operation since it points to the start of the buffer + NtOpFileReadWrite * childOp = op; + op->pending += subOperations; + for (;;) { + // if we're not repeating the previous operation then dequeue a new one + if (!childOp) { + if (nil == (childOp = opList.Head())) + break; + opList.Unlink(childOp); + file->opList.Link(childOp, kListLinkBefore, op); + } + + // issue the operation + bool result; + const HANDLE hEvent = childOp->overlapped.hEvent; + if (childOp->opType == kOpFileRead) { + result = ReadFile( + file->handle, + childOp->rw.buffer, + childOp->win32Bytes, + 0, + &childOp->overlapped + ); + } + else { + ASSERT(childOp->opType == kOpFileWrite); + result = WriteFile( + file->handle, + childOp->rw.buffer, + childOp->win32Bytes, + 0, + &childOp->overlapped + ); + } + + if (!result && ((error = GetLastError()) != ERROR_IO_PENDING)) { + FatalOnNonRecoverableError(*childOp, error); + + if (eventCount) { + LogMsg(kLogError, "HandleFailedOp1 failed"); + // wait for other operations to complete on this file before retrying + } + else if (childOp->overlapped.hEvent) { + LogMsg(kLogError, "HandleFailedOp2 failed"); + // wait a while and retry operation again + Sleep(10); + continue; + } + else { + // convert operation into pending operation + const EOpType opType = (childOp->opType == kOpFileRead) + ? kOpQueuedFileRead + : kOpQueuedFileWrite; + childOp->opType = opType; + + // convert all other operations into pending operations + while (nil != (childOp = opList.Head())) { + childOp->opType = opType; + opList.Unlink(childOp); + file->opList.Link(childOp, kListLinkBefore, op); + } + + // if there is an operation at the head of the list that will complete + // without help then it will autostart the operations we queued + autoComplete = file->opList.Head()->opType != opType; + break; + } + } + else { + // operation was successful + childOp = nil; + + // if we didn't fill the synchronous event array then continue issuing operations + if (nil == (events[eventCount] = hEvent)) + continue; + if (++eventCount < arrsize(events)) + continue; + } + + // wait for all synchronous operations to complete + if (eventCount) { + file->critsect.Leave(); + WaitForMultipleObjects(eventCount, events, true, INFINITE); + for (unsigned i = 0; i < eventCount; ++i) + CloseHandle(events[i]); + eventCount = 0; + file->critsect.Enter(); + } + } + file->critsect.Leave(); + + if (eventCount) { + WaitForMultipleObjects(eventCount, events, true, INFINITE); + for (unsigned i = 0; i < eventCount; ++i) + CloseHandle(events[i]); + } + else if (!autoComplete) { + s_fileCrit.Enter(); + s_pendFiles.Link(file, kListTail); + s_fileCrit.Leave(); + + AsyncTimerUpdate(s_timer, 0, kAsyncTimerUpdateSetPriorityHigher); + } +} + +//=========================================================================== +static void InternalFileSetSize (NtObject * file, qword size) { + LONG sizeHigh = (long) (size >> 32); + DWORD seek = SetFilePointer(file->handle, (dword) size, &sizeHigh, FILE_BEGIN); + if ((seek != (DWORD) -1) || (GetLastError() == NO_ERROR)) + SetEndOfFile(file->handle); +} + + +/**************************************************************************** +* +* Module functions +* +***/ + +//=========================================================================== +void INtFileInitialize () { + AsyncTimerCreate(&s_timer, INtFileTimerProc, INFINITE); +} + +//=========================================================================== +void INtFileStartCleanup () { + // wait until outstanding file I/O is complete + for (;; Sleep(10)) { + if (s_fileOps) + continue; + if (AsyncPerfGetCounter(kAsyncPerfFileBytesReadQueued)) + continue; + if (AsyncPerfGetCounter(kAsyncPerfFileBytesWriteQueued)) + continue; + if (volatile bool pending = (s_pendFiles.Head() != nil)) + continue; + break; + } + + // slam closed any files which are still open + for (;;) { + s_fileCrit.Enter(); + NtFile * file = s_openFiles.Head(); + if (file) + s_openFiles.Unlink(file); + s_fileCrit.Leave(); + if (!file) + break; + + char msg[256 + MAX_PATH]; + StrPrintf(msg, arrsize(msg), "Error: file '%S' still open", file->fullPath); + ErrorAssert(__LINE__, __FILE__, msg); + + file->notifyProc = nil; + INtConnCompleteOperation(file); + } +} + +//=========================================================================== +void INtFileDestroy () { + if (s_timer) { + AsyncTimerDelete(s_timer, kAsyncTimerDestroyWaitComplete); + s_timer = nil; + } +} + +//=========================================================================== +void INtFileDelete ( + NtFile * file +) { + file->critsect.Enter(); + if (file->handle != INVALID_HANDLE_VALUE) { + CloseHandle(file->handle); + file->handle = INVALID_HANDLE_VALUE; + } + file->critsect.Leave(); + + DEL(file); +} + +//=========================================================================== +void INtFileOpCompleteQueuedReadWrite ( + NtFile * file, + NtOpFileReadWrite * op +) { + bool result; + const HANDLE hEvent = op->overlapped.hEvent; + switch (op->opType) { + case kOpQueuedFileRead: + op->opType = kOpFileRead; + // fall through + + case kOpFileRead: + result = ReadFile( + file->handle, + op->rw.buffer, + op->win32Bytes, + 0, + &op->overlapped + ); + break; + + case kOpQueuedFileWrite: + op->opType = kOpFileWrite; + // fall through + + case kOpFileWrite: + result = WriteFile( + file->handle, + op->rw.buffer, + op->win32Bytes, + 0, + &op->overlapped + ); + break; + + DEFAULT_FATAL(opType); + } + + unsigned error; + if (!result && ((error = GetLastError()) != ERROR_IO_PENDING)) { + FatalOnNonRecoverableError(*op, error); + HandleFailedOp(file, op, error); + } + else if (hEvent) { + WaitForSingleObject(hEvent, INFINITE); + CloseHandle(hEvent); + } +} + +//=========================================================================== +bool INtFileOpCompleteReadWrite ( + NtFile * file, + NtOpFileReadWrite * op, + unsigned bytes +) { + // adjust outstanding bytes + if (bytes != op->win32Bytes) { + if (!file->sectorSizeMask) + ErrorFatal(__LINE__, __FILE__, "Disk %s failed", op->opType == kOpFileRead ? "read" : "write"); + if (op->opType == kOpFileRead) + MemZero(op->rw.buffer + bytes, op->win32Bytes - bytes); + } + + if (op->masterOp) { + bool bail = false; + file->critsect.Enter(); + + // if this is a child operation (!op->asyncId) then + // decrement the master operation's pending count + if (!op->asyncId && (--op->masterOp->pending == 1)) { + if (!op->masterOp->masterOp) + INtConnPostOperation(file, op->masterOp, op->masterOp->win32Bytes); + } + // this is the master operation; wait until all the child operations complete + else if (op->pending != 1) { + op->masterOp->masterOp = nil; + bail = true; + } + file->critsect.Leave(); + if (bail) + return false; + } + + // callback notification procedure if requested + if (op->notify) { + // before we dispatch the operation to the handler, change its + // type to indicate that the operation is being dispatched + op->notify = false; + file->notifyProc( + (AsyncFile) file, + op->opType == kOpFileRead ? kNotifyFileRead : kNotifyFileWrite, + &op->rw, + &file->userState + ); + } + + PerfSubCounter( + op->opType == kOpFileRead ? kAsyncPerfFileBytesReadQueued : kAsyncPerfFileBytesWriteQueued, + op->win32Bytes + ); + return true; +} + +//=========================================================================== +void INtFileOpCompleteFileFlush ( + NtFile * file, + NtOpFileFlush * op +) { + ASSERT(file->ioType == kNtFile); + + // complete flush operation + if (!FlushFileBuffers(file->handle)) + op->flush.error = AsyncGetLastFileError(); + else + op->flush.error = kFileSuccess; + + if (op->flush.truncateSize != kAsyncFileDontTruncate) + InternalFileSetSize(file, op->flush.truncateSize); + + // start any queued writes which were waiting for this flush operation to + // complete, but only complete any writes up to the next flush operation + file->critsect.Enter(); + --file->queueWrites; + for (Operation * scan = file->opList.Head(); scan; scan = file->opList.Next(scan)) { + if (scan->opType == kOpQueuedFileWrite) + INtFileOpCompleteQueuedReadWrite(file, (NtOpFileReadWrite *) scan); + else if ((scan->opType == kOpFileFlush) && (scan != op)) + break; + } + file->critsect.Leave(); + + if (op->notify) { + op->notify = false; + file->notifyProc((AsyncFile) file, kNotifyFileFlush, &op->flush, &file->userState); + } + InterlockedDecrement(&s_fileOps); +} + +//=========================================================================== +void INtFileOpCompleteSequence ( + NtFile * file, + NtOpFileSequence * op +) { + if (op->notify) { + op->notify = false; + file->notifyProc((AsyncFile) file, kNotifyFileSequence, &op->sequence, &file->userState); + } + InterlockedDecrement(&s_fileOps); +} + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +AsyncFile NtFileOpen ( + const wchar fullPath[], + FAsyncNotifyFileProc notifyProc, + EFileError * error, + unsigned desiredAccess, + unsigned openMode, + unsigned shareModeFlags, + void * userState, + qword * fileSize, + qword * fileLastWriteTime +) { + unsigned attributeFlags = 0; + attributeFlags |= FILE_FLAG_OVERLAPPED; + + HANDLE handle = CreateFileW( + fullPath, + desiredAccess, + shareModeFlags, + nil, // plSecurityAttributes + openMode, + attributeFlags, + nil // hTemplateFile + ); + *error = AsyncGetLastFileError(); + + if (INVALID_HANDLE_VALUE == handle) + return nil; + + // don't allow users to open devices like "LPT1", etc. + if (GetFileType(handle) != FILE_TYPE_DISK) { + LogMsg(kLogFatal, "!FILE_TYPE_DISK"); + *error = kFileErrorFileNotFound; + CloseHandle(handle); + return nil; + } + + // get file size + DWORD sizeHi, sizeLo = GetFileSize(handle, &sizeHi); + if ((sizeLo == (DWORD) -1) && (NO_ERROR != GetLastError())) { + *error = AsyncGetLastFileError(); + LogMsg(kLogFatal, "GetFileSize"); + CloseHandle(handle); + return nil; + } + const qword size = ((qword) sizeHi << (qword) 32) | (qword) sizeLo; + + qword lastWriteTime; + ASSERT(sizeof(lastWriteTime) >= sizeof(FILETIME)); + GetFileTime(handle, nil, nil, (FILETIME *) &lastWriteTime); + + // allocate and initialize a new file + NtFile * conn = NEWZERO(NtFile); + conn->ioType = kNtFile; + conn->handle = handle; + conn->notifyProc = notifyProc; + conn->ioCount = 1; + conn->queueWrites = 0; + conn->userState = userState; + conn->sectorSizeMask = 0; + + conn->closed = false; + StrCopy(conn->fullPath, fullPath, arrsize(conn->fullPath)); + + if (!INtConnInitialize(conn)) { + *error = kFileErrorFileNotFound; + conn->notifyProc = nil; + INtConnCompleteOperation(conn); + return nil; + } + + // add to list of open files + s_fileCrit.Enter(); + s_openFiles.Link(conn); + s_fileCrit.Leave(); + + // return out parameters + if (fileSize) + *fileSize = size; + if (fileLastWriteTime) + *fileLastWriteTime = lastWriteTime; + return (AsyncFile) conn; +} + +//=========================================================================== +AsyncId NtFileRead ( + AsyncFile conn, + qword offset, + void * buffer, + unsigned bytes, + unsigned flags, + void * param +) { + NtFile * file = (NtFile *) conn; + ASSERT(file->ioType == kNtFile); + ASSERT(file->handle != INVALID_HANDLE_VALUE); + ASSERT((flags & (kAsyncFileRwNotify|kAsyncFileRwSync)) != (kAsyncFileRwNotify|kAsyncFileRwSync)); + ASSERT(! (offset & file->sectorSizeMask)); + ASSERT(! (bytes & file->sectorSizeMask)); + ASSERT(! ((unsigned_ptr) buffer & file->sectorSizeMask)); + + // Normally, I/O events do not complete until both the WIN32 operation has completed + // and the callback notification has occurred. A deadlock can occur if a thread attempts + // to perform a series of operations and then waits for those operations to complete if + // that thread holds a critical section, because all the I/O worker threads cannot + // enter that critical section to complete their required notification callbacks. To + // enable the sequential thread to perform a wait operation, we set the event field + // into the Overlapped structure, because the event will be signaled prior to the + // potentially deadlocking callback notification. + NtOpFileReadWrite * op = NEW(NtOpFileReadWrite); + op->overlapped.Offset = (dword) (offset & 0xffffffff); + op->overlapped.OffsetHigh = (dword) (offset >> 32); + op->overlapped.hEvent = (flags & kAsyncFileRwSync) ? CreateEvent(nil, true, false, nil) : nil; + op->opType = kOpFileRead; + op->notify = (flags & kAsyncFileRwNotify) != 0; + op->pending = 1; + op->signalComplete = nil; + op->masterOp = nil; + op->win32Bytes = bytes; + op->rw.param = param; + op->rw.offset = offset; + op->rw.buffer = (byte *) buffer; + op->rw.bytes = bytes; + + InterlockedIncrement(&file->ioCount); + PerfAddCounter(kAsyncPerfFileBytesReadQueued, bytes); + + file->critsect.Enter(); + const AsyncId asyncId = op->rw.asyncId = op->asyncId = INtConnSequenceStart(file); + file->opList.Link(op, kListTail); + file->critsect.Leave(); + + INtFileOpCompleteQueuedReadWrite(file, op); + + return asyncId; +} + +//=========================================================================== +// buffer must stay valid until I/O is completed +AsyncId NtFileWrite ( + AsyncFile conn, + qword offset, + const void * buffer, + unsigned bytes, + unsigned flags, + void * param +) { + NtFile * file = (NtFile *) conn; + ASSERT(file->ioType == kNtFile); + ASSERT(file->handle != INVALID_HANDLE_VALUE); + ASSERT((flags & (kAsyncFileRwNotify|kAsyncFileRwSync)) != (kAsyncFileRwNotify|kAsyncFileRwSync)); + ASSERT(! (offset & file->sectorSizeMask)); + ASSERT(! (bytes & file->sectorSizeMask)); + ASSERT(! ((unsigned_ptr) buffer & file->sectorSizeMask)); + + // Normally, I/O events do not complete until both the WIN32 operation has completed + // and the callback notification has occurred. A deadlock can occur if a thread attempts + // to perform a series of operations and then waits for those operations to complete if + // that thread holds a critical section, because all the I/O worker threads cannot + // enter that critical section to complete their required notification callbacks. To + // enable the sequential thread to perform a wait operation, we set the event field + // into the Overlapped structure, because the event will be signaled prior to the + // potentially deadlocking callback notification. + NtOpFileReadWrite * op = NEW(NtOpFileReadWrite); + op->overlapped.Offset = (dword) (offset & 0xffffffff); + op->overlapped.OffsetHigh = (dword) (offset >> 32); + op->overlapped.hEvent = (flags & kAsyncFileRwSync) ? CreateEvent(nil, true, false, nil) : nil; + op->opType = kOpFileWrite; + op->notify = (flags & kAsyncFileRwNotify) != 0; + op->pending = 1; + op->signalComplete = nil; + op->masterOp = nil; + op->win32Bytes = bytes; + op->rw.param = param; + op->rw.offset = offset; + op->rw.buffer = (byte *) buffer; + op->rw.bytes = bytes; + + InterlockedIncrement(&file->ioCount); + PerfAddCounter(kAsyncPerfFileBytesWriteQueued, bytes); + + // to avoid a potential deadlock, we MUST issue the write if the SYNC flag is set + file->critsect.Enter(); + ASSERT(!file->queueWrites || !op->overlapped.hEvent); + const bool startOperation = !file->queueWrites || op->overlapped.hEvent; + if (!startOperation) + op->opType = kOpQueuedFileWrite; + const AsyncId asyncId = op->asyncId = op->rw.asyncId = INtConnSequenceStart(file); + file->opList.Link(op, kListTail); + file->critsect.Leave(); + + if (startOperation) + INtFileOpCompleteQueuedReadWrite(file, op); + + return asyncId; +} + +//=========================================================================== +AsyncId NtFileFlushBuffers ( + AsyncFile conn, + qword truncateSize, + bool notify, + void * param +) { + NtFile * file = (NtFile *) conn; + ASSERT(file); + ASSERT(file->ioType == kNtFile); + ASSERT(file->handle != INVALID_HANDLE_VALUE); + ASSERT((truncateSize == kAsyncFileDontTruncate) || !(truncateSize & file->sectorSizeMask)); + + // create new operation + NtOpFileFlush * op = NEW(NtOpFileFlush); + file->critsect.Enter(); + + // write operations cannot complete while a flush is in progress + ++file->queueWrites; + + // init Operation + const AsyncId asyncId = INtConnSequenceStart(file); + op->overlapped.Offset = 0; + op->overlapped.OffsetHigh = 0; + op->overlapped.hEvent = nil; + op->opType = kOpFileFlush; + op->asyncId = asyncId; + op->notify = notify; + op->pending = 1; + op->signalComplete = nil; + file->opList.Link(op, kListTail); + + // init OpFileFlush + op->flush.param = param; + op->flush.asyncId = asyncId; + op->flush.error = kFileSuccess; + op->flush.truncateSize = truncateSize; + + InterlockedIncrement(&s_fileOps); + InterlockedIncrement(&file->ioCount); + + // if there are other operations already on the list we can't complete this one + if (op != file->opList.Head()) + op = nil; + + file->critsect.Leave(); + + // If the operation is at the head of the + // list then issue it for immediate complete + if (op) + INtConnPostOperation(file, op, 0); + return asyncId; +} + +//=========================================================================== +void NtFileClose ( + AsyncFile conn, + qword truncateSize +) { + NtFile * file = (NtFile *) conn; + ASSERT(file); + ASSERT(file->ioType == kNtFile); + + file->critsect.Enter(); + { + { + // AsyncFileClose guarantees that when it returns the file handle will be + // closed so that an immediate call to AsyncFileOpen will succeed. In order + // to successfully close the file handle immediately, we must ensure that + // there is be no active I/O on the file; either no operations on list, or + // only operations on list which are being dispatched or have been dispatched. + ASSERT(!file->pendLink.IsLinked()); + for (Operation * op = file->opList.Head(); op; op = file->opList.Next(op)) { + // skip completed operations + if (!op->pending) + continue; + + // skip operations which are "technically complete" + if (!op->notify) + continue; + + ErrorAssert(__LINE__, __FILE__, "AsyncFileClose: File has pending I/O!"); + break; + } + + // make sure the user doesn't attempt to close the file twice + ASSERT(!file->closed); + file->closed = true; + } + + if (truncateSize != kAsyncFileDontTruncate) + InternalFileSetSize(file, truncateSize); + + ASSERT(file->handle != INVALID_HANDLE_VALUE); + CloseHandle(file->handle); + file->handle = INVALID_HANDLE_VALUE; + } + file->critsect.Leave(); + + // remove file from list of open files + s_fileCrit.Enter(); + ASSERT(!file->pendLink.IsLinked()); + s_openFiles.Unlink(file); + s_fileCrit.Leave(); + + INtConnCompleteOperation(file); +} + +//=========================================================================== +void NtFileSetLastWriteTime ( + AsyncFile conn, + qword lastWriteTime +) { + NtFile * file = (NtFile *) conn; + ASSERT(file); + ASSERT(file->ioType == kNtFile); + + file->critsect.Enter(); + ASSERT(file->handle != INVALID_HANDLE_VALUE); + SetFileTime(file->handle, nil, nil, (FILETIME *) &lastWriteTime); + file->critsect.Leave(); +} + +//=========================================================================== +qword NtFileGetLastWriteTime ( + const wchar fileName[] +) { + WIN32_FILE_ATTRIBUTE_DATA info; + bool f = GetFileAttributesExW(fileName, GetFileExInfoStandard, &info); + return f ? *((qword *) &info.ftLastWriteTime) : 0; +} + +//=========================================================================== +// Inserts a "null operation" into the list of reads and writes. The callback +// will be called when all preceding operations have successfully completed. +AsyncId NtFileCreateSequence ( + AsyncFile conn, + bool notify, + void * param +) { + NtFile * file = (NtFile *) conn; + ASSERT(file); + ASSERT(file->ioType == kNtFile); + + // create new operation + NtOpFileSequence * op = NEW(NtOpFileSequence); + file->critsect.Enter(); + + // init Operation + const AsyncId asyncId = INtConnSequenceStart(file); + op->overlapped.Offset = 0; + op->overlapped.OffsetHigh = 0; + op->overlapped.hEvent = nil; + op->opType = kOpSequence; + op->asyncId = asyncId; + op->notify = notify; + op->pending = 1; + op->signalComplete = nil; + file->opList.Link(op, kListTail); + + // init OpFileSequence + op->sequence.param = param; + op->sequence.asyncId = asyncId; + + InterlockedIncrement(&s_fileOps); + InterlockedIncrement(&file->ioCount); + + // if there are other operations already on the list we can't complete this one + if (op != file->opList.Head()) + op = nil; + + file->critsect.Leave(); + + // If the operation is at the head of the + // list then issue it for immediate complete + if (op) + INtConnPostOperation(file, op, 0); + return asyncId; +} + +//=========================================================================== +// This function allows the caller to wait until an I/O operation completes for +// a file. However, it is an EXTREMELY DANGEROUS function, so you should follow +// these rules to avoid a deadlock: +// 1. AsyncWaitId CAN NEVER be called in response to an I/O completion notification +// callback (a call to an FAsyncNotifyFileProc), because if all I/O threads were +// blocking for I/O there would be no threads left to complete the I/O. +// 2. AsyncWaitId CAN NEVER be called from a timer callback for the same reason as #1. +// 3. AsyncWaitId can be called from inside an idle callback (FAsyncIdleProc), because +// only half of the I/O threads can be inside an idle callback at the same time, +// which leaves the other half available to complete I/O. +// 4. When calling AsyncWaitId, the thread which makes the call MUST NOT hold any +// locks (critical section or reader/writer locks) which would cause an I/O +// thread to block while completing I/O that might be needed to complete the +// I/O operation that is being waited. That means not only the specific I/O +// operation that is being waited, but also any I/O that will call the same +// FAsyncNotifyFileProc. +// 5. Spin-blocking (calling AsyncWaitId in a loop with a small timeout value) IS NOT +// a solution to the deadlock problem, it will still create a deadlock because +// the I/O thread is still fully occupied and cannot complete any I/O +bool NtFileWaitId (AsyncFile conn, AsyncId asyncId, unsigned timeoutMs) { + NtFile * file = (NtFile *) conn; + ASSERT(asyncId); + ASSERT(file); + ASSERT(file->ioType == kNtFile); + ASSERT(file->handle != INVALID_HANDLE_VALUE); + + ThreadAssertCanBlock(__FILE__, __LINE__); + + // has the AsyncId already completed? + if (file->nextCompleteSequence - (long) asyncId >= 0) + return true; + + // is this a non-blocking wait? + if (!timeoutMs) + return false; + + // find the I/O operation the user is waiting for + CNtWaitHandle * signalComplete = nil; + file->critsect.Enter(); + for (Operation * op = file->opList.Head(); op; op = file->opList.Next(op)) { + if (asyncId != op->asyncId) + continue; + + // create an object to wait on + if (!op->signalComplete) + op->signalComplete = NEW(CNtWaitHandle); + signalComplete = op->signalComplete; + signalComplete->IncRef(); + break; + } + file->critsect.Leave(); + + // if we didn't find or create a signal then the operation must have + // completed just before we managed to enter the critical section + if (!signalComplete) + return true; + + const bool result = signalComplete->WaitForObject(timeoutMs); + signalComplete->DecRef(); + return result; +} + +//============================================================================ +bool NtFileSeek ( + AsyncFile conn, + qword distance, + EFileSeekFrom from +) { + COMPILER_ASSERT(kFileSeekFromBegin == FILE_BEGIN); + COMPILER_ASSERT(kFileSeekFromCurrent == FILE_CURRENT); + COMPILER_ASSERT(kFileSeekFromEnd == FILE_END); + + NtFile * file = (NtFile *) conn; + LONG low = (LONG)(distance % 0x100000000ul); + LONG high = (LONG)(distance / 0x100000000ul); + dword result = SetFilePointer(file->handle, low, &high, from); + if ((result == (dword)-1) && (GetLastError() != NO_ERROR)) { + LogMsg(kLogFatal, "failed: SetFilePointer"); + return false; + } + else + return true; +} + +} // namespace Nt diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtInt.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtInt.h new file mode 100644 index 00000000..4f769382 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtInt.h @@ -0,0 +1,320 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtInt.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_NT_PNACENTINT_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtInt.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_NT_PNACENTINT_H + + +namespace Nt { + +/**************************************************************************** +* +* Type definitions +* +***/ + +enum EIoType { + kNtFile, + kNtSocket, + kIoTypes +}; + +enum EOpType { + // Completed by GetQueuedCompletionStatus + kOpConnAttempt, + kOpSocketRead, + kOpSocketWrite, + kOpFileRead, + kOpFileWrite, + + // opType >= kOpSequence complete when they reach the head of the list + kOpSequence, + kOpFileFlush, + kOpQueuedFileRead, + kOpQueuedFileWrite, + kOpQueuedSocketWrite, + kNumOpTypes +}; + +class CNtCritSect : public CCritSect { +public: + BOOL TryEnter () { return TryEnterCriticalSection(&m_handle); } +}; + +class CNtWaitHandle { + long m_refCount; + HANDLE m_event; + +public: + CNtWaitHandle (); + ~CNtWaitHandle (); + void IncRef (); + void DecRef (); + bool WaitForObject (unsigned timeMs) const; + void SignalObject () const; +}; + +struct Operation { + OVERLAPPED overlapped; + EOpType opType; + AsyncId asyncId; + bool notify; + unsigned pending; + CNtWaitHandle * signalComplete; + LINK(Operation) link; + + #ifdef HS_DEBUGGING + ~Operation () { + ASSERT(!signalComplete); + } + #endif +}; + +struct NtObject { + CNtCritSect critsect; + EIoType ioType; + HANDLE handle; + void * userState; + LISTDECL(Operation, link) opList; + long nextCompleteSequence; + long nextStartSequence; + long ioCount; + bool closed; +}; + + +/**************************************************************************** +* +* Nt.cpp internal functions +* +***/ + +void INtWakeupMainIoThreads (); +void INtConnPostOperation (NtObject * ntObj, Operation * op, unsigned bytes); +AsyncId INtConnSequenceStart (NtObject * ntObj); +bool INtConnInitialize (NtObject * ntObj); +void INtConnCompleteOperation (NtObject * ntObj); + + +/***************************************************************************** +* +* NtFile.cpp internal functions +* +***/ + +struct NtFile; +struct NtOpFileFlush; +struct NtOpFileReadWrite; +struct NtOpFileSequence; + +void INtFileInitialize (); +void INtFileStartCleanup (); +void INtFileDestroy (); + +void INtFileDelete ( + NtFile * file +); + +bool INtFileOpCompleteReadWrite ( + NtFile * ioConn, + NtOpFileReadWrite * op, + unsigned bytes +); +void INtFileOpCompleteQueuedReadWrite ( + NtFile * ioConn, + NtOpFileReadWrite * op +); +void INtFileOpCompleteFileFlush ( + NtFile * ioConn, + NtOpFileFlush * op +); +void INtFileOpCompleteSequence ( + NtFile * ioConn, + NtOpFileSequence * op +); + +void INtFileStartCleanup (); + + +/***************************************************************************** +* +* NtSocket.cpp internal functions +* +***/ + +struct NtSock; +struct NtOpConnAttempt; +struct NtOpSocketWrite; + +void INtSocketInitialize (); +void INtSocketStartCleanup (unsigned exitThreadWaitMs); +void INtSocketDestroy (); + +void INtSockDelete ( + NtSock * sock +); + +void INtSocketOpCompleteSocketConnect ( + NtOpConnAttempt * op +); +void INtSocketOpCompleteSocketRead ( + NtSock * sock, + unsigned bytes +); +void INtSocketOpCompleteSocketWrite ( + NtSock * sock, + NtOpSocketWrite * op +); +bool INtSocketOpCompleteQueuedSocketWrite ( + NtSock * sock, + NtOpSocketWrite * op +); + + +/***************************************************************************** +* +* NT Async API functions +* +***/ + +void NtInitialize (); +void NtDestroy (unsigned exitThreadWaitMs); +void NtSignalShutdown (); +void NtWaitForShutdown (); +void NtSleep (unsigned sleepMs); +AsyncFile NtFileOpen ( + const wchar fullPath[], + FAsyncNotifyFileProc notifyProc, + EFileError * error, + unsigned desiredAccess, + unsigned openMode, + unsigned shareModeFlags, + void * userState, + qword * fileSize, + qword * fileLastWriteTime +); +void NtFileClose ( + AsyncFile file, + qword truncateSize +); +void NtFileSetLastWriteTime ( + AsyncFile file, + qword lastWriteTime +); +qword NtFileGetLastWriteTime ( + const wchar fileName[] +); +AsyncId NtFileFlushBuffers ( + AsyncFile file, + qword truncateSize, + bool notify, + void * param +); +AsyncId NtFileRead ( + AsyncFile file, + qword offset, + void * buffer, + unsigned bytes, + unsigned flags, + void * param +); +AsyncId NtFileWrite ( + AsyncFile file, + qword offset, + const void *buffer, + unsigned bytes, + unsigned flags, + void * param +); +AsyncId NtFileCreateSequence ( + AsyncFile file, + bool notify, + void * param +); +bool NtFileSeek ( + AsyncFile file, + qword distance, + EFileSeekFrom from +); +void NtSocketConnect ( + AsyncCancelId * cancelId, + const NetAddress & netAddr, + FAsyncNotifySocketProc notifyProc, + void * param, + const void * sendData, + unsigned sendBytes, + unsigned connectMs, + unsigned localPort +); +void NtSocketConnectCancel ( + FAsyncNotifySocketProc notifyProc, + AsyncCancelId cancelId +); +void NtSocketDisconnect ( + AsyncSocket sock, + bool hardClose +); +void NtSocketDelete (AsyncSocket sock); +bool NtSocketSend ( + AsyncSocket sock, + const void * data, + unsigned bytes +); +bool NtSocketWrite ( + AsyncSocket sock, + const void * buffer, + unsigned bytes, + void * param +); +void NtSocketSetNotifyProc ( + AsyncSocket sock, + FAsyncNotifySocketProc notifyProc +); +void NtSocketSetBacklogAlloc ( + AsyncSocket sock, + unsigned bufferSize +); +unsigned NtSocketStartListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +); +void NtSocketStopListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +); +void NtSocketEnableNagling ( + AsyncSocket conn, + bool enable +); + +} // namespace Nt diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtSocket.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtSocket.cpp new file mode 100644 index 00000000..6fa44fc1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtSocket.cpp @@ -0,0 +1,1467 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtSocket.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +#include "pnAceNtInt.h" + + +namespace Nt { + +/**************************************************************************** +* +* Private +* +***/ + +// how long to wait for connect() to complete +static const unsigned kConnectTimeMs = 10*1000; + +static const int kTcpSndBufSize = 64*1024-1; +static const int kTcpRcvBufSize = 64*1024-1; +static const int kListenBacklog = 400; + +// wait before checking for backlog problems +static const unsigned kBacklogInitMs = 3*60*1000; + +// destroy a connection if it has a backlog "problem" +static const unsigned kBacklogFailMs = 2*60*1000; + +static const unsigned kMinBacklogBytes = 4 * 1024; + +struct NtListener { + LINK(NtListener) nextPort; + SOCKET hSocket; + NetAddress addr; + FAsyncNotifySocketProc notifyProc; + int listenCount; + + ~NtListener () { + if (hSocket != INVALID_SOCKET) + closesocket(hSocket); + } +}; + +struct NtOpConnAttempt : Operation { + AsyncCancelId cancelId; + bool canceled; + unsigned localPort; + NetAddress remoteAddr; + FAsyncNotifySocketProc notifyProc; + void * param; + SOCKET hSocket; + unsigned failTimeMs; + unsigned sendBytes; + byte sendData[1]; // actually [sendBytes] + // no additional fields +}; + +struct NtOpSocketWrite : Operation { + unsigned queueTimeMs; + unsigned bytesAlloc; + AsyncNotifySocketWrite write; +}; + +struct NtOpSocketRead : Operation { + AsyncNotifySocketRead read; +}; + +struct NtSock : NtObject { + LINK(NtSock) link; + NetAddress addr; + unsigned closeTimeMs; + unsigned connType; + FAsyncNotifySocketProc notifyProc; + unsigned bytesLeft; + NtOpSocketRead opRead; + unsigned backlogAlloc; + unsigned initTimeMs; + byte buffer[kAsyncSocketBufferSize]; + + NtSock (); + ~NtSock (); +}; + + +static CNtCritSect s_listenCrit; +static LISTDECL(NtListener, nextPort) s_listenList; +static LISTDECL(NtOpConnAttempt, link) s_connectList; +static bool s_runListenThread; +static unsigned s_nextConnectCancelId = 1; +static HANDLE s_listenThread; +static HANDLE s_listenEvent; + + +const unsigned kCloseTimeoutMs = 8*1000; +static CNtCritSect s_socketCrit; +static AsyncTimer * s_socketTimer; +static LISTDECL(NtSock, link) s_socketList; + + +//=========================================================================== +inline NtSock::NtSock () { + PerfAddCounter(kAsyncPerfSocketsCurr, 1); + PerfAddCounter(kAsyncPerfSocketsTotal, 1); +} + +//=========================================================================== +NtSock::~NtSock () { + // Make sure socket can only be deleted after receiving NOTIFY_DISCONNECT + ASSERT(closed); + + // To avoid a race condition, the socket must be unlinked from + // the soft disconnect list prior to closing the handle + if (link.IsLinked()) { + s_socketCrit.Enter(); + link.Unlink(); + s_socketCrit.Leave(); + } + + if (handle != INVALID_HANDLE_VALUE) + closesocket((SOCKET) handle); + + PerfSubCounter(kAsyncPerfSocketsCurr, 1); +} + +//=========================================================================== +// must be called inside s_listenCrit +static bool ListenPortIncrement ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc, + int count +) { + NtListener * listener; + for (listener = s_listenList.Head(); listener; listener = s_listenList.Next(listener)) { + if (!NetAddressEqual(listener->addr, listenAddr)) + continue; + if (listener->notifyProc != notifyProc) + continue; + + listener->listenCount += count; + ASSERT(listener->listenCount >= 0); + break; + } + return listener != 0; +} + +//=========================================================================== +static void SocketGetAddresses ( + NtSock * sock, + NetAddress * localAddr, + NetAddress * remoteAddr +) { + // NetAddress may be bigger than sockaddr_in so start by zeroing the whole thing + ZEROPTR(localAddr); + ZEROPTR(remoteAddr); + + // don't have to enter critsect or validate socket before referencing it + // because this routine is called before the user has a chance to close it + int nameLen = sizeof(*localAddr); + if (getsockname((SOCKET) sock->handle, (sockaddr *) localAddr, &nameLen)) + LogMsg(kLogError, "getsockname failed"); + + nameLen = sizeof(*remoteAddr); + if (getpeername((SOCKET) sock->handle, (sockaddr *) remoteAddr, &nameLen)) + LogMsg(kLogError, "getpeername failed"); +} + +//=========================================================================== +static void SocketStartAsyncRead (NtSock * sock) { + // enter critical section in case someone attempts to close socket from another thread + bool readResult; + sock->critsect.Enter(); + if (sock->handle != INVALID_HANDLE_VALUE) { + InterlockedIncrement(&sock->ioCount); + readResult = ReadFile( + sock->handle, + sock->buffer + sock->bytesLeft, + sizeof(sock->buffer) - sock->bytesLeft, + 0, + &sock->opRead.overlapped + ); + } + else { + readResult = true; + } + sock->critsect.Leave(); + + DWORD err = GetLastError(); + if (!readResult && (err != ERROR_IO_PENDING)) + InterlockedDecrement(&sock->ioCount); +} + +//=========================================================================== +static bool SocketDispatchRead (NtSock * sock) { +// LogMsg(kLogPerf, L"Nt sock %p recv %u bytes", sock, sock->opRead.read.bytes); + + // put "fast case" first -- connType already established + if (sock->notifyProc) + return sock->notifyProc((AsyncSocket) sock, kNotifySocketRead, &sock->opRead.read, &sock->userState); + + ASSERT(sock->opRead.read.buffer == sock->buffer); + ASSERT(sock->opRead.read.bytes); + + // make sure there's an event procedure to handle this event + AsyncNotifySocketListen notify; + unsigned bytesProcessed; + sock->notifyProc = AsyncSocketFindNotifyProc( + sock->opRead.read.buffer, + sock->opRead.read.bytes, + &bytesProcessed, + ¬ify.connType, + ¬ify.buildId, + ¬ify.buildType, + ¬ify.branchId, + ¬ify.productId + ); + if (!sock->notifyProc) + return false; + + // perform kNotifySocketListenSuccess + SocketGetAddresses(sock, ¬ify.localAddr, ¬ify.remoteAddr); + notify.param = nil; + notify.asyncId = 0; + notify.addr = sock->addr; + sock->userState = nil; + sock->connType = notify.connType; + notify.buffer = sock->opRead.read.buffer + bytesProcessed; + notify.bytes = sock->opRead.read.bytes - bytesProcessed; + notify.bytesProcessed = 0; + if (!sock->notifyProc((AsyncSocket) sock, kNotifySocketListenSuccess, ¬ify, &sock->userState)) + return false; + bytesProcessed += notify.bytesProcessed; + + // if we didn't use up all the bytes, dispatch a read operation + if (0 != (sock->opRead.read.bytes -= bytesProcessed)) { + sock->opRead.read.buffer += bytesProcessed; + if (!sock->notifyProc((AsyncSocket) sock, kNotifySocketRead, &sock->opRead.read, &sock->userState)) + return false; + } + + // add bytes used by IOsFindListenProc and kNotifySocketListenSuccess + sock->opRead.read.bytesProcessed += bytesProcessed; + return true; +} + +//=========================================================================== +static NtOpSocketWrite * SocketQueueAsyncWrite ( + NtSock * sock, + const byte * data, + unsigned bytes +) { + // check for data backlog + Operation * opCurr; + for (opCurr = sock->opList.Head(); opCurr; opCurr = sock->opList.Next(opCurr)) { + if (opCurr->opType != kOpQueuedSocketWrite) + continue; + NtOpSocketWrite * firstQueuedWrite = (NtOpSocketWrite *) opCurr; + + unsigned currTimeMs = TimeGetMs(); + if (((long) (currTimeMs - firstQueuedWrite->queueTimeMs) >= (long) kBacklogFailMs) + && ((long) (currTimeMs - sock->initTimeMs) >= (long) kBacklogInitMs) + ) { + PerfAddCounter(kAsyncPerfSocketDisconnectBacklog, 1); + + if (sock->connType) { + LogMsg( + kLogPerf, + "Backlog, c:%u q:%u, i:%u", + sock->connType, + currTimeMs - firstQueuedWrite->queueTimeMs, + currTimeMs - sock->initTimeMs + ); + } + NtSocketDisconnect((AsyncSocket) sock, true); + return nil; + } + + break; + } + + // if the last buffer still has space available then add data to it + if (opCurr) { + for (opCurr = sock->opList.Tail(); opCurr; opCurr = sock->opList.Prev(opCurr)) { + if (opCurr->opType != kOpQueuedSocketWrite) + continue; + NtOpSocketWrite * lastQueuedWrite = (NtOpSocketWrite *) opCurr; + + unsigned bytesLeft = lastQueuedWrite->bytesAlloc - lastQueuedWrite->write.bytes; + bytesLeft = min(bytesLeft, bytes); + if (bytesLeft) { + PerfAddCounter(kAsyncPerfSocketBytesWaitQueued, bytesLeft); + MemCopy(lastQueuedWrite->write.buffer + lastQueuedWrite->write.bytes, data, bytesLeft); + lastQueuedWrite->write.bytes += bytesLeft; + data += bytesLeft; + if (0 == (bytes -= bytesLeft)) + return lastQueuedWrite; + } + break; + } + } + + // allocate a buffer large enough to hold the data, plus + // extra space in case more data needs to be queued later + unsigned bytesAlloc = max(bytes, sock->backlogAlloc); + bytesAlloc = max(bytesAlloc, kMinBacklogBytes); + NtOpSocketWrite * op = new(ALLOC(sizeof(NtOpSocketWrite) + bytesAlloc)) NtOpSocketWrite; + + // init Operation + const AsyncId asyncId = INtConnSequenceStart(sock); + op->overlapped.Offset = 0; + op->overlapped.OffsetHigh = 0; + op->overlapped.hEvent = nil; + op->opType = kOpQueuedSocketWrite; + op->asyncId = asyncId; + op->notify = false; + op->pending = 1; + op->signalComplete = nil; + sock->opList.Link(op, kListTail); + + // init OpWrite + op->queueTimeMs = TimeGetMs(); + op->bytesAlloc = bytesAlloc; + op->write.param = nil; + op->write.asyncId = asyncId; + op->write.buffer = (byte *) (op + 1); + op->write.bytes = bytes; + op->write.bytesProcessed = bytes; + MemCopy(op->write.buffer, data, bytes); + + InterlockedIncrement(&sock->ioCount); + PerfAddCounter(kAsyncPerfSocketBytesWaitQueued, bytes); + + return op; +} + +//=========================================================================== +static NtSock * SocketInitCommon (SOCKET hSocket) { + // make socket non-blocking + u_long nonBlocking = true; + if (ioctlsocket(hSocket, FIONBIO, &nonBlocking)) + LogMsg(kLogError, "ioctlsocket failed (make non-blocking)"); + + // set socket buffer sizes + int result = setsockopt( + hSocket, + SOL_SOCKET, + SO_SNDBUF, + (const char *) &kTcpSndBufSize, + sizeof(kTcpSndBufSize) + ); + if (result) + LogMsg(kLogError, "setsockopt(send) failed (set send buffer size)"); + + result = setsockopt( + hSocket, + SOL_SOCKET, + SO_RCVBUF, + (const char *) &kTcpRcvBufSize, + sizeof(kTcpRcvBufSize) + ); + if (result) + LogMsg(kLogError, "setsockopt(recv) failed (set recv buffer size)"); + + // allocate a new socket + NtSock * sock = NEWZERO(NtSock); + sock->ioType = kNtSocket; + sock->handle = (HANDLE) hSocket; + sock->initTimeMs = TimeGetMs(); + sock->ioCount = 1; + sock->opRead.opType = kOpSocketRead; + + return sock; +} + +//=========================================================================== +static bool SocketInitConnect ( + NtSock * const sock, + NtOpConnAttempt const & op +) { + bool notified = false; + for (;;) { + // attach to I/O completion port + if (!INtConnInitialize(sock)) + break; + + // send initial data + if (op.sendBytes && !NtSocketSend((AsyncSocket) sock, op.sendData, op.sendBytes)) + break; + + // Determine connType + for (;op.sendBytes;) { + sock->connType = op.sendData[0]; + if (IS_TEXT_CONNTYPE(sock->connType)) + break; + + if (op.sendBytes < sizeof(AsyncSocketConnectPacket)) + return false; + + if (sock->connType != ((const AsyncSocketConnectPacket *) op.sendData)->connType) + return false; + + break; + } + + // perform callback notification + notified = true; + AsyncNotifySocketConnect notify; + SocketGetAddresses(sock, ¬ify.localAddr, ¬ify.remoteAddr); + notify.param = op.param; + notify.asyncId = 0; + notify.connType = sock->connType; + sock->notifyProc = op.notifyProc; + if (!sock->notifyProc((AsyncSocket) sock, kNotifySocketConnectSuccess, ¬ify, &sock->userState)) + break; + + // start reading from the socket + SocketStartAsyncRead(sock); + break; + } + + INtConnCompleteOperation(sock); + return notified; +} + +//=========================================================================== +static void SocketInitListen ( + NtSock * const sock, + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +) { + for (;;) { + // attach to I/O completion port + if (!INtConnInitialize(sock)) + break; + + sock->addr = listenAddr; + + if (notifyProc) { + // perform kNotifySocketListenSuccess + AsyncNotifySocketListen notify; + SocketGetAddresses(sock, ¬ify.localAddr, ¬ify.remoteAddr); + notify.param = nil; + notify.asyncId = 0; + notify.connType = 0; + notify.buildId = 0; + notify.buildType = 0; + notify.branchId = 0; + notify.productId = 0; + notify.addr = listenAddr; + notify.buffer = sock->opRead.read.buffer; + notify.bytes = 0; + notify.bytesProcessed = 0; + sock->notifyProc = notifyProc; + if (!sock->notifyProc((AsyncSocket) sock, kNotifySocketListenSuccess, ¬ify, &sock->userState)) + break; + } + + // start reading from the socket + SocketStartAsyncRead(sock); + break; + } + + INtConnCompleteOperation(sock); +} + +//=========================================================================== +static SOCKET ListenSocket (NetAddress * listenAddr) { + // create a new socket to listen + SOCKET s; + if (INVALID_SOCKET == (s = socket(AF_INET, SOCK_STREAM, 0))) { + LogMsg(kLogError, "socket create failed"); + return INVALID_SOCKET; + } + + for (;;) { // actually for (ONCE) + /* this code was an attempt to enable the server to close and then re-open the same port + for listening. It doesn't appear to work, and moreover, it causes the bind to signal + success even though the port cannot be opened (for example, because another application + has already opened the port). + static const BOOL s_reuseAddr = true; + setsockopt( + s, + SOL_SOCKET, + SO_REUSEADDR, + (const char *) &s_reuseAddr, + sizeof(s_reuseAddr) + ); + static const LINGER s_linger = { true, 0 }; + setsockopt( + s, + SOL_SOCKET, + SO_LINGER, + (const char *) &s_linger, + sizeof(s_linger) + ); + */ + + NetAddressNode node = NetAddressGetNode(*listenAddr); + unsigned port = NetAddressGetPort(*listenAddr); + + // bind socket to port + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons((word)port); + addr.sin_addr.S_un.S_addr = htonl(node); + MemZero(addr.sin_zero, sizeof(addr.sin_zero)); + if (bind(s, (sockaddr *) &addr, sizeof(addr))) { + wchar str[32]; + NetAddressToString(*listenAddr, str, arrsize(str), kNetAddressFormatAll); + LogMsg(kLogError, "bind to addr %s failed (err %u)", str, WSAGetLastError()); + break; + } + + // get portNumber if unknown + if (!port) { + int addrLen = sizeof(addr); + if (getsockname(s, (sockaddr *) &addr, &addrLen)) { + LogMsg(kLogError, "getsockname failed"); + break; + } + + if (0 == (port = ntohs(((const sockaddr_in *) &addr)->sin_port))) { + LogMsg(kLogError, "bad listen port"); + break; + } + } + + // make socket non-blocking + u_long nonBlocking = true; + if (ioctlsocket(s, FIONBIO, &nonBlocking)) + LogMsg(kLogError, "ioctlsocket failed (make non-blocking)"); + + if (listen(s, kListenBacklog)) { + LogMsg(kLogError, "socket listen failed"); + break; + } + + // success! + NetAddressSetPort(port, listenAddr); + return s; + } + + // failure! + closesocket(s); + NetAddressSetPort(0, listenAddr); + return INVALID_SOCKET; +} + +//=========================================================================== +// must be called while inside s_listenCrit! +static void ListenPrepareListeners (fd_set * readfds) { + FD_ZERO(readfds); + for (NtListener *next, *port = s_listenList.Head(); port; port = next) { + next = s_listenList.Next(port); + + // destroy unused ports + if (!port->listenCount) { + DEL(port); + continue; + } + + // add port to listen list + ASSERT(port->hSocket != INVALID_SOCKET); + #pragma warning(disable:4127) // ignore "do { } while (0)" warning + FD_SET(port->hSocket, readfds); + #pragma warning(default:4127) + } +} + +//=========================================================================== +static SOCKET ConnectSocket (unsigned localPort, const NetAddress & addr) { + SOCKET s; + if (INVALID_SOCKET == (s = socket(AF_INET, SOCK_STREAM, 0))) { + LogMsg(kLogError, "socket create failed"); + return INVALID_SOCKET; + } + + for (;;) { // actually for (ONCE) + // make socket non-blocking + u_long nonBlocking = true; + if (ioctlsocket(s, FIONBIO, &nonBlocking)) + LogMsg(kLogError, "ioctlsocket failed (make non-blocking)"); + + // bind socket to port + if (localPort) { + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons((word) localPort); + addr.sin_addr.S_un.S_addr = INADDR_ANY; + MemZero(addr.sin_zero, sizeof(addr.sin_zero)); + if (bind(s, (sockaddr *) &addr, sizeof(addr))) { + LogMsg(kLogError, "bind(port %u) failed (%u)", localPort, WSAGetLastError()); + break; + } + } + + if (connect(s, (const sockaddr *) &addr, sizeof(addr))) { + if (WSAGetLastError() != WSAEWOULDBLOCK) { + LogMsg(kLogError, "sockegt connect failed"); + break; + } + } + + // success! + return s; + } + + // failure! + closesocket(s); + return INVALID_SOCKET; +} + +//=========================================================================== +static void ListenPrepareConnectors (fd_set * writefds) { + const unsigned currTimeMs = TimeGetMs(); + + FD_ZERO(writefds); + for (NtOpConnAttempt *next, *op = s_connectList.Head(); op; op = next) { + next = s_connectList.Next(op); + + // if the socket has taken too long to connect then abort attempt + if (op->hSocket != INVALID_SOCKET) { + if ((int) (currTimeMs - op->failTimeMs) > 0) + op->canceled = true; + } + + // if this connection attempt has been canceled then post + // operation to worker threads to complete the callback + if (op->canceled) { + if (op->hSocket) { + closesocket(op->hSocket); + op->hSocket = INVALID_SOCKET; + } + s_connectList.Unlink(op); + INtConnPostOperation(nil, op, 0); + continue; + } + + // open new sockets + if (op->hSocket == INVALID_SOCKET) { + if (INVALID_SOCKET == (op->hSocket = ConnectSocket(op->localPort, op->remoteAddr))) { + s_connectList.Unlink(op); + INtConnPostOperation(nil, op, 0); + continue; + } + + // start failure timer + op->failTimeMs += currTimeMs; + } + + // add socket to fdset + #pragma warning(disable:4127) // ignore "do { } while (0)" warning + FD_SET(op->hSocket, writefds); + #pragma warning(default:4127) + } +} + +//=========================================================================== +static unsigned THREADCALL ListenThreadProc (AsyncThread *) { + fd_set readfds; + fd_set writefds; + for (;;) { + s_listenCrit.Enter(); + ListenPrepareListeners(&readfds); + ListenPrepareConnectors(&writefds); + s_listenCrit.Leave(); + if (!s_runListenThread) + break; + + // wait until there is something on listen or connect list + if (!readfds.fd_count && !writefds.fd_count) { + WaitForSingleObject(s_listenEvent, INFINITE); + continue; + } + + // wait for connection or timeout + const struct timeval timeout = { 0, 250*1000 }; // seconds, microseconds + int result = select(0, &readfds, &writefds, 0, &timeout); + if (result == SOCKET_ERROR) { + LogMsg(kLogError, "socket select failed"); + continue; + } + if (!result) + continue; + + s_listenCrit.Enter(); + + // complete listen() operations + unsigned count = 0; + for (NtListener * listener = s_listenList.Head(); listener; listener = s_listenList.Next(listener)) { + if (FD_ISSET(listener->hSocket, &readfds)) { + SOCKET s; + while (INVALID_SOCKET != (s = accept(listener->hSocket, 0, 0))) { + SocketInitListen( + SocketInitCommon(s), + listener->addr, + listener->notifyProc + ); + ++count; + } + } + } + PerfAddCounter(kAsyncPerfSocketConnAttemptsInTotal, count); + + // complete connect() operations + for (NtOpConnAttempt *next, *op = s_connectList.Head(); op; op = next) { + next = s_connectList.Next(op); + + if (FD_ISSET(op->hSocket, &writefds)) { + s_connectList.Unlink(op); + INtConnPostOperation(nil, op, 0); + } + } + + s_listenCrit.Leave(); + } + + // cleanup all connectors + s_listenCrit.Enter(); + for (NtOpConnAttempt * op; (op = s_connectList.Head()) != nil; s_connectList.Unlink(op)) { + if (op->hSocket != INVALID_SOCKET) { + closesocket(op->hSocket); + op->hSocket = nil; + } + INtConnPostOperation(nil, op, 0); + } + s_listenCrit.Leave(); + + return 0; +} + +//=========================================================================== +static void StartListenThread () { + if (s_listenThread) + return; + + s_listenEvent = CreateEvent( + (LPSECURITY_ATTRIBUTES) nil, + false, // auto-reset event + false, // initial state unsignaled + (LPCTSTR) nil + ); + ASSERT(s_listenEvent); + + // create a low-priority thread to listen on ports + s_runListenThread = true; + s_listenThread = (HANDLE) AsyncThreadCreate( + ListenThreadProc, + nil, + L"NtListenThread" + ); +} + +//=========================================================================== +#ifdef HS_DEBUGGING +#include +static void __cdecl DumpInvalidData ( + const wchar filename[], + unsigned bytes, + const byte data[], + const char fmt[], + ... +) { + ref(filename); + ref(bytes); + ref(data); + ref(fmt); + + wchar path[MAX_PATH]; + PathGetProgramDirectory(path, arrsize(path)); + PathAddFilename(path, path, L"Log", arrsize(path)); + PathAddFilename(path, path, filename, arrsize(path)); + if (FILE * f = _wfopen(path, L"wb")) { + va_list args; + va_start(args, fmt); + vfprintf(f, fmt, args); + va_end(args); + fwrite(data, bytes, 1, f); + fclose(f); + } +} +#endif // ifdef HS_DEBUGGING + +//=========================================================================== +static void HardCloseSocket (SOCKET sock) { + static const LINGER s_linger = { true, 0 }; + setsockopt( + sock, + SOL_SOCKET, + SO_LINGER, + (const char *) &s_linger, + sizeof(s_linger) + ); + closesocket(sock); +} + +//=========================================================================== +static unsigned SocketCloseTimerCallback (void *) { + ARRAY(SOCKET) sockets; + + unsigned sleepMs; + unsigned currTimeMs = TimeGetMs(); + s_socketCrit.Enter(); + for (;;) { + // If there are no more sockets pending destruction then + // wait forever; the timer will be restarted when the + // next socket is queued onto the list. + NtSock * sock = s_socketList.Head(); + if (!sock) { + sleepMs = kAsyncTimeInfinite; + break; + } + + // Wait until the socket close timer expires + if (0 < (signed) (sleepMs = sock->closeTimeMs - currTimeMs)) + break; + + // Get the socket (safely) + HANDLE handle; + sock->critsect.Enter(); + { + handle = sock->handle; + sock->handle = INVALID_HANDLE_VALUE; + } + sock->critsect.Leave(); + if (handle != INVALID_HANDLE_VALUE) + sockets.Push((SOCKET) handle); + + // To avoid a race condition, this unlink must occur + // after the socket handle has been cleared + s_socketList.Unlink(sock); + } + s_socketCrit.Leave(); + + // Abortive close all open sockets; any unsent data is lost + SOCKET * cur = sockets.Ptr(); + SOCKET * end = sockets.Term(); + for (; cur < end; ++cur) + HardCloseSocket(*cur); + + // Don't run too frequently + return max(sleepMs, 2000); +} + + +/**************************************************************************** +* +* Module functions +* +***/ + +//=========================================================================== +void INtSocketInitialize () { + AsyncTimerCreate( + &s_socketTimer, + SocketCloseTimerCallback, + kAsyncTimeInfinite + ); +} + +//=========================================================================== +void INtSocketStartCleanup (unsigned exitThreadWaitMs) { + s_runListenThread = false; + if (s_listenThread) { + SetEvent(s_listenEvent); + WaitForSingleObject(s_listenThread, exitThreadWaitMs); + CloseHandle(s_listenThread); + s_listenThread = nil; + } + if (s_listenEvent) { + CloseHandle(s_listenEvent); + s_listenEvent = nil; + } + + s_listenCrit.Enter(); + ASSERT(!s_connectList.Head()); + ASSERT(!s_listenList.Head()); + s_listenCrit.Leave(); +} + +//=========================================================================== +void INtSocketDestroy () { + if (s_socketTimer) { + AsyncTimerDelete(s_socketTimer, kAsyncTimerDestroyWaitComplete); + s_socketTimer = nil; + } +} + +//=========================================================================== +void INtSockDelete ( + NtSock * sock +) { + ASSERT(!sock->closed); + sock->closed = true; + + if (sock->notifyProc) { + // We have to be extremely careful from this point because + // sockets can be deleted during the notification callback. + // After this call, the application becomes responsible for + // calling NtSocketDelete at some later point in time. + FAsyncNotifySocketProc notifyProc = sock->notifyProc; + sock->notifyProc = nil; + notifyProc((AsyncSocket) sock, kNotifySocketDisconnect, nil, &sock->userState); + DWORD err = GetLastError(); + ref(err); + } + else { + // Since the no application notification procedure was + // ever set, the socket can now be deleted safely. + NtSocketDelete((AsyncSocket) sock); + } +} + +//=========================================================================== +void INtSocketOpCompleteSocketConnect (NtOpConnAttempt * op) { + + // connect socket to local end + bool notified; + if (op->hSocket != INVALID_SOCKET) { + notified = SocketInitConnect( + SocketInitCommon(op->hSocket), + *op + ); + } + else { + notified = false; + } + + // handle connection failure + if (!notified) { + AsyncNotifySocketConnect failed; + failed.param = op->param; + failed.connType = op->sendData[0]; + failed.remoteAddr = op->remoteAddr; + ZERO(failed.localAddr); + op->notifyProc(nil, kNotifySocketConnectFailed, &failed, nil); + } + + // we can delete the operation outside an NtConn * critical + // section because it isn't linked into an opList + // and because connection attempts are not waitable + ASSERT(!op->link.IsLinked()); + ASSERT(!op->signalComplete); + DEL(op); + + PerfSubCounter(kAsyncPerfSocketConnAttemptsOutCurr, 1); +} + +//=========================================================================== +void INtSocketOpCompleteSocketRead ( + NtSock * sock, + unsigned bytes +) { + ASSERT(sock->ioType == kNtSocket); + + for (ONCE) { + // a zero-byte read means the socket is going + // to shutdown, so don't start another read + if (!bytes) + break; + + // add new bytes to buffer bytes + sock->bytesLeft += bytes; + + // dispatch data + sock->opRead.read.param = nil; + sock->opRead.read.asyncId = 0; + sock->opRead.read.buffer = sock->buffer; + sock->opRead.read.bytes = sock->bytesLeft; + sock->opRead.read.bytesProcessed = 0; + + if (sock->connType == kConnTypeCliToAuth) { + int x = 0; + ref(x); + } + + if (!SocketDispatchRead(sock)) + break; + + // if only some of the bytes were used then shift + // remaining bytes down otherwise clear buffer. + if (0 != (sock->bytesLeft -= sock->opRead.read.bytesProcessed)) { + + if ((sock->bytesLeft > sizeof(sock->buffer)) + || ((sock->opRead.read.bytesProcessed + sock->bytesLeft) > sizeof(sock->buffer)) + ) { + #ifdef HS_DEBUGGING + static long s_once; + if (!AtomicAdd(&s_once, 1)) { + DumpInvalidData( + L"NtSockErr.log", + sizeof(sock->buffer), + sock->buffer, + "SocketDispatchRead error for %p: %d %d %d\r\n", + sock->notifyProc, + sock->bytesLeft, + sock->opRead.read.bytes, + sock->opRead.read.bytesProcessed + ); + } + #endif // ifdef HS_DEBUGGING + + LogMsg( + kLogError, + "SocketDispatchRead error for %p: %d %d %d\r\n", + sock->notifyProc, + sock->bytesLeft, + sock->opRead.read.bytes, + sock->opRead.read.bytesProcessed + ); + break; + } + + if (sock->opRead.read.bytesProcessed) { + MemMove( + sock->buffer, + sock->buffer + sock->opRead.read.bytesProcessed, + sock->bytesLeft + ); + } + + // make sure there's enough space left in the buffer for another read + if (sock->bytesLeft >= sizeof(sock->buffer)) + break; + } + + SocketStartAsyncRead(sock); + } + + INtConnCompleteOperation(sock); +} + +//=========================================================================== +void INtSocketOpCompleteSocketWrite ( + NtSock * sock, + NtOpSocketWrite * op +) { + PerfSubCounter(kAsyncPerfSocketBytesWriteQueued, op->write.bytes); + + // callback notification procedure if requested + if (op->notify) { + if (!sock->notifyProc((AsyncSocket) sock, kNotifySocketWrite, &op->write, &sock->userState)) + NtSocketDisconnect((AsyncSocket) sock, false); + } +} + +//=========================================================================== +bool INtSocketOpCompleteQueuedSocketWrite ( + NtSock * sock, + NtOpSocketWrite * op +) { + PerfSubCounter(kAsyncPerfSocketBytesWaitQueued, op->write.bytes); + PerfAddCounter(kAsyncPerfSocketBytesWriteQueued, op->write.bytes); + + // must enter critical section in case someone attempts to close socket from another thread + sock->critsect.Enter(); + op->opType = kOpSocketWrite; + ASSERT(!op->overlapped.hEvent); + const bool writeResult = WriteFile( + sock->handle, + op->write.buffer, + op->write.bytes, + 0, + &op->overlapped + ); + sock->critsect.Leave(); + +// LogMsg(kLogPerf, L"Nt sock %p wrote %u bytes", sock, op->write.bytes); + + if (!writeResult && (GetLastError() != ERROR_IO_PENDING)) { + op->write.bytesProcessed = 0; + + // No further operations must be allowed to complete. The disconnect + // must occur before posting a completion notification for this + // operation, because otherwise another thread might delete the socket + // before this disconnect could complete (race condition). + NtSocketDisconnect((AsyncSocket) sock, true); + + // complete operation by posting it + INtConnPostOperation(sock, op, 0); + return false; + } + + return true; +} + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +unsigned NtSocketStartListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +) { + s_listenCrit.Enter(); + StartListenThread(); + NetAddress addr = listenAddr; + for (;;) { + // if the port is already open then just increment the reference count + if (ListenPortIncrement(addr, notifyProc, 1)) + break; + + SOCKET s; + if (INVALID_SOCKET == (s = ListenSocket(&addr))) + break; + + // create a new listener record + NtListener * listener = s_listenList.New(kListTail, nil, __FILE__, __LINE__); + listener->hSocket = s; + listener->addr = addr; + listener->notifyProc = notifyProc; + listener->listenCount = 1; + break; + } + s_listenCrit.Leave(); + + unsigned port = NetAddressGetPort(addr); + if (port) + SetEvent(s_listenEvent); + + return port; +} + +//=========================================================================== +void NtSocketStopListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +) { + s_listenCrit.Enter(); + ListenPortIncrement(listenAddr, notifyProc, -1); + s_listenCrit.Leave(); +} + +//=========================================================================== +void NtSocketConnect ( + AsyncCancelId * cancelId, + const NetAddress & netAddr, + FAsyncNotifySocketProc notifyProc, + void * param, + const void * sendData, + unsigned sendBytes, + unsigned connectMs, + unsigned localPort +) { + ASSERT(notifyProc); + + // create async connection record with enough extra bytes for sendData + NtOpConnAttempt * op = + new(ALLOC(sizeof(NtOpConnAttempt) - sizeof(op->sendData) + sendBytes)) NtOpConnAttempt; + + // init Operation + op->overlapped.Offset = 0; + op->overlapped.OffsetHigh = 0; + op->overlapped.hEvent = nil; + op->opType = kOpConnAttempt; + op->asyncId = nil; + op->notify = true; + op->pending = 1; + op->signalComplete = nil; + + // init OpConnAttempt + op->canceled = false; + op->localPort = localPort; + op->remoteAddr = netAddr; + op->notifyProc = notifyProc; + op->param = param; + op->hSocket = INVALID_SOCKET; + op->failTimeMs = connectMs ? connectMs : kConnectTimeMs; + if (0 != (op->sendBytes = sendBytes)) + MemCopy(op->sendData, sendData, sendBytes); + else + op->sendData[0] = kConnTypeNil; + + PerfAddCounter(kAsyncPerfSocketConnAttemptsOutCurr, 1); + PerfAddCounter(kAsyncPerfSocketConnAttemptsOutTotal, 1); + + s_listenCrit.Enter(); + StartListenThread(); + + // get cancel id; we can avoid checking for zero by always using an odd number + ASSERT(s_nextConnectCancelId & 1); + s_nextConnectCancelId += 2; + + *cancelId = op->cancelId = (AsyncCancelId) s_nextConnectCancelId; + s_connectList.Link(op, kListTail); + s_listenCrit.Leave(); + SetEvent(s_listenEvent); +} + +//=========================================================================== +// due to the asynchronous nature sockets, the connect may occur +// before the cancel can complete... you have been warned +void NtSocketConnectCancel ( + FAsyncNotifySocketProc notifyProc, + AsyncCancelId cancelId // nil = cancel all with specified notifyProc +) { + s_listenCrit.Enter(); + for (NtOpConnAttempt * op = s_connectList.Head(); op; op = s_connectList.Next(op)) { + if (cancelId && (op->cancelId != cancelId)) + continue; + if (op->notifyProc != notifyProc) + continue; + op->canceled = true; + } + s_listenCrit.Leave(); +} + +//=========================================================================== +// This function must ONLY be called after receiving a NOTIFY_DISCONNECT message +// for a socket. After a NOTIFY_DISCONNECT, the socket will fail all I/O initiated +// against it, but will otherwise continue to exist. The memory for the socket will +// only be freed when NtSocketDelete is called. +void NtSocketDelete (AsyncSocket conn) { + NtSock * sock = (NtSock *) conn; + if (sock->ioType != kNtSocket) { + LogMsg(kLogError, "NtSocketDelete %u %p", sock->ioType, sock->notifyProc); + return; + } + + DEL(sock); +} + +//=========================================================================== +void NtSocketDisconnect (AsyncSocket conn, bool hardClose) { + NtSock * sock = (NtSock *) conn; + ASSERT(sock->ioType == kNtSocket); + + // must enter critical section in case someone attempts to close socket from another thread + HANDLE handle; + sock->critsect.Enter(); + if (hardClose) { + // Prepare to close the socket immediately once we leave the critsect + handle = sock->handle; + sock->handle = INVALID_HANDLE_VALUE; + + // Mark the socket closed in such a way that, if it has already been + // soft closed, the mark won't invalidate the ordering of s_socketList + sock->closeTimeMs |= 1; + } + else if (!sock->closeTimeMs) { + // The socket hasn't been closed previously; perform shutdown, + // and mark the socket closed with a time value that indicates + // its ordering in s_socketList; + handle = INVALID_HANDLE_VALUE; + sock->closeTimeMs = (TimeGetMs() + kCloseTimeoutMs) | 1; + shutdown((SOCKET) sock->handle, SD_SEND); + } + else { + // The socket has already been closed previously + handle = INVALID_HANDLE_VALUE; + hardClose = true; + } + sock->critsect.Leave(); + + if (handle != INVALID_HANDLE_VALUE) { + HardCloseSocket((SOCKET) handle); + } + else if (!hardClose) { + // Add the socket to the close list in sorted order by close time; + // if this socket is the first on the list then start the timer + bool startTimer; + s_socketCrit.Enter(); + { + s_socketList.Link(sock, kListTail); + startTimer = s_socketList.Head() == sock; + } + s_socketCrit.Leave(); + + // If this is the first item queued in the socket list then start timer. + // This operation should be safe to perform outside the critical section + // because s_socketTimer should not be deleted before application shutdown + if (startTimer) + AsyncTimerUpdate(s_socketTimer, kCloseTimeoutMs); + } +} + +//=========================================================================== +bool NtSocketSend ( + AsyncSocket conn, + const void * data, + unsigned bytes +) { + NtSock * sock = (NtSock *) conn; + ASSERT(sock); + ASSERT(data); + ASSERT(bytes); + ASSERT(sock->ioType == kNtSocket); + +// LogMsg(kLogPerf, L"Nt sock %p sending %u bytes", sock, bytes); + + bool result; + sock->critsect.Enter(); + for (;;) { + // Is the socket closing? + if (sock->closeTimeMs) { + result = false; + break; + } + + // if there isn't any data queued, send this batch immediately + bool dataQueued = sock->opList.Head() != nil; + if (!dataQueued) { + int bytesSent = send((SOCKET) sock->handle, (const char *) data, bytes, 0); + if (bytesSent != SOCKET_ERROR) { + result = true; + + // if we sent all the data then exit + if ((unsigned) bytesSent >= bytes) + break; + + // subtract the data we already sent + data = (const byte *) data + bytesSent; + bytes -= bytesSent; + // and queue it below + } + else if (WSAEWOULDBLOCK != WSAGetLastError()) { + // an error occurred -- destroy connection + NtSocketDisconnect((AsyncSocket) sock, true); + result = false; + break; + } + } + + NtOpSocketWrite * op = SocketQueueAsyncWrite(sock, (const byte *) data, bytes); + if (op && !dataQueued) + result = INtSocketOpCompleteQueuedSocketWrite(sock, op); + else + result = true; + break; + } + sock->critsect.Leave(); + + return result; +} + +//=========================================================================== +bool NtSocketWrite ( + AsyncSocket conn, + const void * buffer, + unsigned bytes, + void * param +) { + NtSock * sock = (NtSock *) conn; + ASSERT(buffer); + ASSERT(bytes); + ASSERT(sock->ioType == kNtSocket); + +// LogMsg(kLogPerf, L"Nt sock %p writing %u bytes", sock, bytes); + + bool result; + sock->critsect.Enter(); + for (;;) { + // Is the socket closing? + if (sock->closeTimeMs) { + result = false; + break; + } + + // init Operation + NtOpSocketWrite * op = NEW(NtOpSocketWrite); + op->overlapped.Offset = 0; + op->overlapped.OffsetHigh = 0; + op->overlapped.hEvent = nil; + op->opType = kOpQueuedSocketWrite; + op->asyncId = INtConnSequenceStart(sock); + op->notify = true; + op->pending = 1; + op->signalComplete = nil; + sock->opList.Link(op, kListTail); + + // init OpWrite + op->queueTimeMs = TimeGetMs(); + op->bytesAlloc = bytes; + op->write.param = param; + op->write.asyncId = op->asyncId; + op->write.buffer = (byte *) buffer; + op->write.bytes = bytes; + op->write.bytesProcessed = bytes; + PerfAddCounter(kAsyncPerfSocketBytesWaitQueued, bytes); + + InterlockedIncrement(&sock->ioCount); + + if (op == sock->opList.Head()) + result = INtSocketOpCompleteQueuedSocketWrite((NtSock *) sock, op); + else + result = true; + break; + } + sock->critsect.Leave(); + return result; +} + +//=========================================================================== +// -- use only for server<->client connections, not server<->server! +// -- Note that Nagling is enabled by default +void NtSocketEnableNagling (AsyncSocket conn, bool enable) { + NtSock * sock = (NtSock *) conn; + ASSERT(sock->ioType == kNtSocket); + + // must enter critical section in case someone attempts to close socket from another thread + sock->critsect.Enter(); + if (sock->handle != INVALID_HANDLE_VALUE) { + BOOL noDelay = !enable; + const int result = setsockopt( + (SOCKET) sock->handle, + IPPROTO_TCP, + TCP_NODELAY, + (const char *) &noDelay, + sizeof(noDelay) + ); + if (result) + LogMsg(kLogError, "setsockopt failed (nagling)"); + } + sock->critsect.Leave(); +} + +//=========================================================================== +void NtSocketSetNotifyProc ( + AsyncSocket conn, + FAsyncNotifySocketProc notifyProc +) { + NtSock * sock = (NtSock *) conn; + ASSERT(sock->ioType == kNtSocket); + ((NtSock *) sock)->notifyProc = notifyProc; +} + +//=========================================================================== +void NtSocketSetBacklogAlloc (AsyncSocket conn, unsigned bufferSize) { + NtSock * sock = (NtSock *) conn; + ASSERT(sock->ioType == kNtSocket); + ((NtSock *) sock)->backlogAlloc = bufferSize; +} + +} using namespace Nt; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtThread.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtThread.cpp new file mode 100644 index 00000000..d7d743d0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtThread.cpp @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Nt/pnAceNtThread.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +#include "pnAceNtInt.h" + + +namespace Nt { + +/***************************************************************************** +* +* Module exports +* +***/ + +//=========================================================================== +void NtSleep (unsigned sleepMs) { + ThreadAssertCanBlock(__FILE__, __LINE__); + Sleep(sleepMs); +} + +} using namespace Nt; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Unix/pnAceUx.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Unix/pnAceUx.h new file mode 100644 index 00000000..cd87b4ec --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Unix/pnAceUx.h @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Unix/pnAceUx.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_UNIX_PNACEUX_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Unix/pnAceUx.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_UNIX_PNACEUX_H + + +#ifdef HS_BUILD_FOR_UNIX + +/**************************************************************************** +* +* Win9x API functions +* +***/ + +void UxGetApi (AsyncApi * api); + +#endif // HS_BUILD_FOR_UNIX diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9x.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9x.cpp new file mode 100644 index 00000000..e2ab04a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9x.cpp @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9x.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +#include "pnAceW9xInt.h" + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +void W9xGetApi (AsyncApi * api) { + using namespace W9x; + + api->initialize = W9xThreadInitialize; + api->destroy = W9xThreadDestroy; + api->signalShutdown = W9xThreadSignalShutdown; + api->waitForShutdown = W9xThreadWaitForShutdown; + api->sleep = W9xThreadSleep; + + api->fileOpen = W9xFileOpen; + api->fileClose = W9xFileClose; + api->fileRead = W9xFileRead; + api->fileWrite = W9xFileWrite; + api->fileFlushBuffers = W9xFileFlushBuffers; + api->fileSetLastWriteTime = W9xFileSetLastWriteTime; + api->fileGetLastWriteTime = W9xFileGetLastWriteTime; + api->fileCreateSequence = W9xFileCreateSequence; + api->fileSeek = W9xFileSeek; + + api->socketConnect = W9xSocketConnect; + api->socketConnectCancel = W9xSocketConnectCancel; + api->socketDisconnect = W9xSocketDisconnect; + api->socketDelete = W9xSocketDelete; + api->socketSend = W9xSocketSend; + api->socketWrite = W9xSocketWrite; + api->socketSetNotifyProc = W9xSocketSetNotifyProc; + api->socketSetBacklogAlloc = W9xSocketSetBacklogAlloc; + api->socketStartListening = W9xSocketStartListening; + api->socketStopListening = W9xSocketStopListening; + api->socketEnableNagling = W9xSocketEnableNagling; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9x.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9x.h new file mode 100644 index 00000000..4145625c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9x.h @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9x.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_W9X_PNACEW9X_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9x.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_W9X_PNACEW9X_H + + +#ifdef HS_BUILD_FOR_WIN32 + +/**************************************************************************** +* +* Win9x API functions +* +***/ + +void W9xGetApi (AsyncApi * api); + +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xFile.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xFile.cpp new file mode 100644 index 00000000..c845d74e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xFile.cpp @@ -0,0 +1,502 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xFile.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +#include "pnAceW9xInt.h" + + +namespace W9x { + + +/**************************************************************************** +* +* FileOp +* +***/ + +struct FileOp { + EAsyncNotifyFile code; + bool notify; + union { + AsyncNotifyFileFlush flush; + AsyncNotifyFileRead read; + AsyncNotifyFileSequence sequence; + AsyncNotifyFileWrite write; + } data; +}; + + +/**************************************************************************** +* +* CFile +* +***/ + +class CFile : public CThreadDispObject { +private: + CCritSect m_critSect; + HANDLE m_handle; + FAsyncNotifyFileProc m_notifyProc; + void * m_userState; + +protected: + void Complete (void * op, CCritSect * critSect, AsyncId asyncId); + void Delete (void * op); + +public: + CFile ( + HANDLE handle, + FAsyncNotifyFileProc notifyProc, + void * userState + ); + ~CFile (); + + void Read ( + qword offset, + void * buffer, + unsigned bytes + ); + + void SetLastWriteTime (qword lastWriteTime); + + void Truncate (qword size); + + void Write ( + qword offset, + const void * buffer, + unsigned bytes + ); + + bool Seek (qword offset, EFileSeekFrom from); +}; + +//=========================================================================== +CFile::CFile ( + HANDLE handle, + FAsyncNotifyFileProc notifyProc, + void * userState +) : + m_handle(handle), + m_notifyProc(notifyProc), + m_userState(userState) +{ +} + +//=========================================================================== +CFile::~CFile () { + CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; +} + +//=========================================================================== +void CFile::Complete (void * op, CCritSect * critSect, AsyncId asyncId) { + FileOp * fileOp = (FileOp *)op; + + // Enter our local critical section and leave the global one + m_critSect.Enter(); + critSect->Leave(); + + // Complete the operation + switch (fileOp->code) { + + case kNotifyFileFlush: { + if (fileOp->data.flush.truncateSize != kAsyncFileDontTruncate) + Truncate(fileOp->data.flush.truncateSize); + BOOL result = FlushFileBuffers(m_handle); + fileOp->data.flush.error = result ? kFileSuccess : AsyncGetLastFileError(); + } + break; + + case kNotifyFileRead: + Read( + fileOp->data.read.offset, + fileOp->data.read.buffer, + fileOp->data.read.bytes + ); + break; + + case kNotifyFileWrite: + Write( + fileOp->data.write.offset, + fileOp->data.write.buffer, + fileOp->data.write.bytes + ); + break; + + } + + // Leave our local critical section + m_critSect.Leave(); + + // Dispatch a completion notification + if (fileOp->notify) { + fileOp->data.flush.asyncId = asyncId; + m_notifyProc( + (AsyncFile)this, + fileOp->code, + &fileOp->data.flush, + &m_userState + ); + } + +} + +//=========================================================================== +void CFile::Delete (void * op) { + FileOp * fileOp = (FileOp *)op; + DEL(fileOp); +} + +//=========================================================================== +void CFile::Read ( + qword offset, + void * buffer, + unsigned bytes +) { + + // Seek to the start of the read + Seek(offset, kFileSeekFromBegin); + + // Perform the read + DWORD bytesRead; + BOOL result = ReadFile( + m_handle, + buffer, + bytes, + &bytesRead, + nil // overlapped + ); + + // Handle errors + if (bytesRead != bytes) + MemZero((byte *)buffer + bytesRead, bytes - bytesRead); + if ( (!result && (GetLastError() != ERROR_IO_PENDING)) || + (bytesRead != bytes) ) + LogMsg(kLogFatal, "failed: ReadFile"); + +} + +//=========================================================================== +bool CFile::Seek (qword offset, EFileSeekFrom from) { + COMPILER_ASSERT(kFileSeekFromBegin == FILE_BEGIN); + COMPILER_ASSERT(kFileSeekFromCurrent == FILE_CURRENT); + COMPILER_ASSERT(kFileSeekFromEnd == FILE_END); + + LONG low = (LONG)(offset % 0x100000000ul); + LONG high = (LONG)(offset / 0x100000000ul); + dword result = SetFilePointer(m_handle, low, &high, from); + if ((result == (dword)-1) && (GetLastError() != NO_ERROR)) { + LogMsg(kLogFatal, "failed: SetFilePointer"); + return false; + } + else + return true; +} + +//=========================================================================== +void CFile::SetLastWriteTime (qword lastWriteTime) { + COMPILER_ASSERT(sizeof(lastWriteTime) == sizeof(FILETIME)); + SetFileTime(m_handle, nil, nil, (const FILETIME *)&lastWriteTime); +} + +//=========================================================================== +void CFile::Truncate (qword size) { + ASSERT(size != kAsyncFileDontTruncate); + + if (Seek(size, kFileSeekFromBegin) && !SetEndOfFile(m_handle)) + LogMsg(kLogFatal, "failed: SetEndOfFile"); +} + +//=========================================================================== +void CFile::Write ( + qword offset, + const void * buffer, + unsigned bytes +) { + + // Seek to the start of the write + Seek(offset, kFileSeekFromBegin); + + // Perform the write + DWORD bytesWritten; + BOOL result = WriteFile( + m_handle, + buffer, + bytes, + &bytesWritten, + nil // overlapped + ); + + // Handle errors + if ( (!result && (GetLastError() != ERROR_IO_PENDING)) || + (bytesWritten != bytes) ) { + LogMsg(kLogFatal, "failed: WriteFile"); + if (!result && (GetLastError() == ERROR_DISK_FULL)) { + MessageBox(nil, "Disk full!", "Error", MB_ICONSTOP | MB_SYSTEMMODAL); +// DebugDisableLeakChecking(); + ExitProcess(1); + } + } + +} + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +void W9xFileClose ( + AsyncFile file, + qword truncateSize +) { + + // Dereference the object + CFile * object = (CFile *)file; + + // If requested, truncate the file + if (truncateSize != kAsyncFileDontTruncate) + object->Truncate(truncateSize); + + // Close the file object + object->Close(); + +} + +//=========================================================================== +AsyncId W9xFileCreateSequence ( + AsyncFile file, + bool notify, + void * param +) { + + // Dereference the object + CFile * object = (CFile *)file; + + // Queue an operation + FileOp * op = NEW(FileOp); + op->code = kNotifyFileSequence; + op->notify = notify; + op->data.flush.param = param; + return object->Queue(op); + +} + +//=========================================================================== +AsyncId W9xFileFlushBuffers ( + AsyncFile file, + qword truncateSize, + bool notify, + void * param +) { + + // Dereference the object + CFile * object = (CFile *)file; + + // Queue an operation + FileOp * op = NEW(FileOp); + op->code = kNotifyFileFlush; + op->notify = notify; + op->data.flush.param = param; + op->data.flush.truncateSize = truncateSize; + // op->data.flush.error filled in upon completion + return object->Queue(op); + +} + +//=========================================================================== +AsyncFile W9xFileOpen ( + const wchar fullPath[], + FAsyncNotifyFileProc notifyProc, + EFileError * error, + unsigned desiredAccess, + unsigned openMode, + unsigned shareModeFlags, + void * userState, + qword * fileSize, + qword * fileLastWriteTime +) { + HANDLE fileHandle = CreateFileW( + fullPath, + desiredAccess, + shareModeFlags, + nil, // plSecurityAttributes + openMode, + 0, // attributeFlags + nil // hTemplateFile + ); + *error = AsyncGetLastFileError(); + + if (INVALID_HANDLE_VALUE == fileHandle) + return nil; + + // don't allow users to open devices like "LPT1", etc. + if (GetFileType(fileHandle) != FILE_TYPE_DISK) { + LogMsg(kLogFatal, "failed: !FILE_TYPE_DISK"); + *error = kFileErrorFileNotFound; + CloseHandle(fileHandle); + return nil; + } + + // Get the file size + DWORD sizeHi, sizeLo = GetFileSize(fileHandle, &sizeHi); + if ((sizeLo == (DWORD) -1) && (NO_ERROR != GetLastError())) { + *error = AsyncGetLastFileError(); + LogMsg(kLogFatal, "failed: GetFileSize"); + CloseHandle(fileHandle); + return nil; + } + const qword size = ((qword) sizeHi << (qword) 32) | (qword) sizeLo; + + qword lastWriteTime; + ASSERT(sizeof(lastWriteTime) >= sizeof(FILETIME)); + GetFileTime(fileHandle, nil, nil, (FILETIME *) &lastWriteTime); + + // Create a file object + CFile * object = NEW(CFile)( + fileHandle, + notifyProc, + userState + ); + + // return out parameters + if (fileSize) + *fileSize = size; + if (fileLastWriteTime) + *fileLastWriteTime = lastWriteTime; + return (AsyncFile)object; +} + +//=========================================================================== +AsyncId W9xFileRead ( + AsyncFile file, + qword offset, + void * buffer, + unsigned bytes, + unsigned flags, + void * param +) { + + // Dereference the object + CFile * object = (CFile *)file; + + // Perform synchronous operations immediately + if (flags & kAsyncFileRwSync) { + object->Read(offset, buffer, bytes); + return 0; + } + + // Queue asynchronous operations + else { + FileOp * op = NEW(FileOp); + op->code = kNotifyFileRead; + op->notify = (flags & kAsyncFileRwNotify) != 0; + op->data.read.param = param; + op->data.read.offset = offset; + op->data.read.buffer = (byte *)buffer; + op->data.read.bytes = bytes; + return object->Queue(op); + } + +} + +//=========================================================================== +void W9xFileSetLastWriteTime ( + AsyncFile file, + qword lastWriteTime +) { + + // Dereference the object + CFile * object = (CFile *)file; + + // Set the file time + object->SetLastWriteTime(lastWriteTime); + +} + +//=========================================================================== +qword W9xFileGetLastWriteTime ( + const wchar fileName[] +) { + WIN32_FILE_ATTRIBUTE_DATA info; + bool f = GetFileAttributesExW(fileName, GetFileExInfoStandard, &info); + return f ? *((qword *) &info.ftLastWriteTime) : 0; +} + +//=========================================================================== +AsyncId W9xFileWrite ( + AsyncFile file, + qword offset, + const void * buffer, + unsigned bytes, + unsigned flags, + void * param +) { + + // Dereference the object + CFile * object = (CFile *)file; + + // Perform synchronous operations immediately + if (flags & kAsyncFileRwSync) { + object->Write(offset, buffer, bytes); + return 0; + } + + // Queue asynchronous operations + else { + FileOp * op = NEW(FileOp); + op->code = kNotifyFileWrite; + op->notify = (flags & kAsyncFileRwNotify) != 0; + op->data.write.param = param; + op->data.write.offset = offset; + op->data.write.buffer = (byte *)buffer; + op->data.write.bytes = bytes; + return object->Queue(op); + } + +} + +//============================================================================ +bool W9xFileSeek ( + AsyncFile file, + qword distance, + EFileSeekFrom from +) { + CFile * object = (CFile *)file; + return object->Seek(distance, from); +} + + +} // namespace W9x diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xInt.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xInt.h new file mode 100644 index 00000000..8423812d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xInt.h @@ -0,0 +1,181 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xInt.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_W9X_PNACEW9XINT_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xInt.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_W9X_PNACEW9XINT_H + + +namespace W9x { + +/***************************************************************************** +* +* Internal types +* +***/ + +class CThreadDispObject : public AtomicRef { +public: + CThreadDispObject (); + virtual ~CThreadDispObject () { } + void Close (); + virtual void Complete (void * op, CCritSect * critSect, AsyncId asyncId) = 0; + virtual void Delete (void * op) = 0; + AsyncId Queue (void * op); +}; + + +/***************************************************************************** +* +* W9x internal async API +* +***/ + +void W9xThreadInitialize (); +void W9xThreadDestroy (unsigned exitThreadWaitMs); +void W9xThreadSignalShutdown (); +void W9xThreadWaitForShutdown (); +void W9xThreadSleep (unsigned sleepMs); +bool W9xThreadWaitId ( + AsyncFile file, + AsyncId asyncId, + unsigned timeoutMs +); + +AsyncFile W9xFileOpen ( + const wchar fullPath[], + FAsyncNotifyFileProc notifyProc, + EFileError * error, + unsigned desiredAccess, + unsigned openMode, + unsigned shareModeFlags, + void * userState, + qword * fileSize, + qword * fileLastWriteTime +); +void W9xFileClose ( + AsyncFile file, + qword truncateSize +); +void W9xFileSetLastWriteTime ( + AsyncFile file, + qword lastWriteTime +); +qword W9xFileGetLastWriteTime ( + const wchar fileName[] +); +AsyncId W9xFileFlushBuffers ( + AsyncFile file, + qword truncateSize, + bool notify, + void * param +); +AsyncId W9xFileRead ( + AsyncFile file, + qword offset, + void * buffer, + unsigned bytes, + unsigned flags, + void * param +); +AsyncId W9xFileWrite ( + AsyncFile file, + qword offset, + const void *buffer, + unsigned bytes, + unsigned flags, + void * param +); +AsyncId W9xFileCreateSequence ( + AsyncFile file, + bool notify, + void * param +); +bool W9xFileSeek ( + AsyncFile file, + qword distance, + EFileSeekFrom from +); +void W9xSocketConnect ( + AsyncCancelId * cancelId, + const NetAddress & netAddr, + FAsyncNotifySocketProc notifyProc, + void * param, + const void * sendData, + unsigned sendBytes, + unsigned connectMs, + unsigned localPort +); +void W9xSocketConnectCancel ( + FAsyncNotifySocketProc notifyProc, + AsyncCancelId cancelId +); +void W9xSocketDisconnect ( + AsyncSocket sock, + bool hardClose +); +void W9xSocketDelete (AsyncSocket sock); +void W9xSocketDestroy (); +bool W9xSocketSend ( + AsyncSocket sock, + const void * data, + unsigned bytes +); +bool W9xSocketWrite ( + AsyncSocket sock, + const void * buffer, + unsigned bytes, + void * param +); +void W9xSocketSetNotifyProc ( + AsyncSocket sock, + FAsyncNotifySocketProc notifyProc +); +void W9xSocketSetBacklogAlloc ( + AsyncSocket sock, + unsigned bufferSize +); +unsigned W9xSocketStartListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +); +void W9xSocketStopListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +); +void W9xSocketEnableNagling ( + AsyncSocket conn, + bool enable +); + + +} // namespace W9x diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xSocket.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xSocket.cpp new file mode 100644 index 00000000..4f5811fd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xSocket.cpp @@ -0,0 +1,1221 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xSocket.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +#include "pnAceW9xInt.h" + + +namespace W9x { + +/***************************************************************************** +* +* Private data +* +***/ + +static HWND s_window; + + +/**************************************************************************** +* +* CSocketMapper +* +***/ + +class CSocket; + +class CSocketMapper { +private: + struct Map { + AsyncCancelId sequence; + SOCKET sock; + UINT message; + CSocket * object; + FAsyncNotifySocketProc notifyProc; + }; + + ARRAY(Map) m_mapTable; + +public: + void Add ( + AsyncCancelId sequence, + CSocket * object, + UINT message, + SOCKET sock, + FAsyncNotifySocketProc notifyProc + ); + void Delete (CSocket * object); + CSocket * Find (AsyncCancelId sequence) const; + CSocket * Find (CSocket * object) const; + CSocket * Find (UINT message, SOCKET sock) const; + CSocket * Find (FAsyncNotifySocketProc notifyProc, unsigned index) const; + +}; + +//=========================================================================== +void CSocketMapper::Add ( + AsyncCancelId sequence, + CSocket * object, + UINT message, + SOCKET sock, + FAsyncNotifySocketProc notifyProc +) { + Map * mapping = m_mapTable.New(); + mapping->sequence = sequence; + mapping->object = object; + mapping->message = message; + mapping->sock = sock; + mapping->notifyProc = notifyProc; +} + +//=========================================================================== +void CSocketMapper::Delete (CSocket * object) { + for (unsigned index = m_mapTable.Count() - 1; index < m_mapTable.Count(); ) + if (m_mapTable[index].object == object) + if (index + 1 < m_mapTable.Count()) + m_mapTable[index] = m_mapTable.Pop(); + else + m_mapTable.Pop(); + else + --index; +} + +//=========================================================================== +CSocket * CSocketMapper::Find (AsyncCancelId sequence) const { + for (const Map * curr = m_mapTable.Ptr(), * term = m_mapTable.Term(); + curr != term; + ++curr) + if (curr->sequence == sequence) + return curr->object; + return nil; +} + +//=========================================================================== +CSocket * CSocketMapper::Find (CSocket * object) const { + for (const Map * curr = m_mapTable.Ptr(), * term = m_mapTable.Term(); + curr != term; + ++curr) + if (curr->object == object) + return curr->object; + return nil; +} + +//=========================================================================== +CSocket * CSocketMapper::Find (UINT message, SOCKET sock) const { + for (const Map * curr = m_mapTable.Ptr(), * term = m_mapTable.Term(); + curr != term; + ++curr) + if ((curr->message == message) && (curr->sock == sock)) + return curr->object; + return nil; +} + +//=========================================================================== +CSocket * CSocketMapper::Find (FAsyncNotifySocketProc notifyProc, unsigned index) const { + for (const Map * curr = m_mapTable.Ptr(), * term = m_mapTable.Term(); + curr != term; + ++curr) + if ((curr->notifyProc == notifyProc) && !index--) + return curr->object; + return nil; +} + +static CSocketMapper s_mapper; + + +/**************************************************************************** +* +* CSocket +* +***/ + +class CSocket { +private: + + // These constants are used to track whether a socket has been connected, + // and whether it has been disconnected. We do not track whether we have + // dispatched a connection failure notification, because socket objects + // are deleted immediately after dispatching that notification. + enum { kDispatchedConnectSuccess = (1 << 0) }; + enum { kDispatchedDisconnect = (1 << 1) }; + + struct Command { + LINK(Command) link; + enum Code { + CONNECT, + WRITE, + } code; + void * param; + union { + struct { + byte connType; + } connect; + struct { + const void * data; // pointer to application's data + unsigned bytes; + } write; + }; + }; + + // These variables are protected by the critical section + CCritSect m_critSect; + LISTDECL(Command, link) m_commandList; + ARRAY(byte) m_sendQueue; + + // These variables are never modified outside the constructor and + // destructor + UINT m_message; + FAsyncNotifySocketProc m_notifyProc; + AsyncCancelId m_sequence; + SOCKET m_sock; + + // These variables are only ever touched during a callback from the + // window procedure, which is single threaded + unsigned m_dispatched; + byte m_readBuffer[1460 * 2]; + unsigned m_readBytes; + void * m_userState; + +public: + CSocket ( + AsyncCancelId sequence, + UINT message, + SOCKET sock, + FAsyncNotifySocketProc notifyProc + ); + ~CSocket (); + + void EnableNagling (bool enable); + void Disconnect (bool hardClose); + + inline AsyncCancelId GetSequence () const; + inline bool IsConnected () const; + inline bool IsDisconnected () const; + + void OnClose (); + void OnConnect (); + void OnReadReady (); + void OnWriteReady (); + + void ProcessQueue (); + void QueueConnect ( + void * param, + byte connType + ); + void QueueWrite ( + void * param, + const void * data, + unsigned bytes + ); + + bool Send ( // returns false if disconnected + const void * data, + unsigned bytes + ); + +}; + +//=========================================================================== +CSocket::CSocket ( + AsyncCancelId sequence, + UINT message, + SOCKET sock, + FAsyncNotifySocketProc notifyProc +) : + m_dispatched(0), + m_notifyProc(notifyProc), + m_readBytes(0), + m_sequence(sequence), + m_message(message), + m_sock(sock), + m_userState(nil) +{ + + // Create a mapping for this socket + s_mapper.Add(sequence, this, message, sock, notifyProc); + +} + +//=========================================================================== +CSocket::~CSocket () { + + // If we dispatched a connect notification, verify that we also + // dispatched a disconnect notification + ASSERT(IsDisconnected() || !IsConnected()); + + // Delete the mapping for this socket + s_mapper.Delete(this); + + // Close the socket + closesocket(m_sock); + m_sock = INVALID_SOCKET; + + // Free memory + m_commandList.Clear(); + +} + +//=========================================================================== +void CSocket::EnableNagling (bool enable) { + BOOL arg = !enable; + int result = setsockopt( + m_sock, + IPPROTO_TCP, + TCP_NODELAY, + (const char *)&arg, + sizeof(arg) + ); + if (result) + LogMsg(kLogFatal, "failed: setsockopt"); +} + +//=========================================================================== +void CSocket::Disconnect (bool hardClose) { + + // This function is called outside the critical section, which is + // necessary to avoid deadlocks, so it must not modify the object state. + // The purpose of this function is to cause WinSock to generate a + // FD_CLOSE notification, which will initiate synchronous destruction + // of the socket. + + if (m_sock == INVALID_SOCKET) + return; + + // Shutdown the socket +// const unsigned SD_BOTH = 2; + shutdown(m_sock, SD_BOTH); + + // Windows always waits for an acknowledgement back from the other end + // of the connection, so we post our own FD_CLOSE notification if we need + // to terminate this socket immediately. Our notification processing code + // is robust against receiving the eventual FD_CLOSE from Windows even + // if another socket has been allocated in the interrim with the same + // socket handle. + if (hardClose) + PostMessage(s_window, m_message, m_sock, MAKELONG(FD_CLOSE, 0)); + +} + +//=========================================================================== +AsyncCancelId CSocket::GetSequence () const { + return m_sequence; +} + +//=========================================================================== +bool CSocket::IsConnected () const { + return (m_dispatched & kDispatchedConnectSuccess) != 0; +} + +//=========================================================================== +bool CSocket::IsDisconnected () const { + return (m_dispatched & kDispatchedDisconnect) != 0; +} + +//=========================================================================== +void CSocket::OnClose () { + + // If we haven't yet dispatched a connection notification, then + // dispatch a failure to connect rather than dispatching a disconnect + // notification + if (!IsConnected()) { + OnConnect(); + return; + } + + // Verify that we haven't already dispatched a disconnect notification + if (IsDisconnected()) + return; + + // Process any remaining queued commands + ProcessQueue(); + + // Remove the mapping for this object so that we will never dispatch + // another event to it + s_mapper.Delete(this); + + // Dispatch the disconnection notificaton + m_dispatched |= kDispatchedDisconnect; + m_notifyProc( + (AsyncSocket)this, + kNotifySocketDisconnect, + nil, // notify + &m_userState + ); + + // This function must not perform any processing after returning from + // the notification, because the application may have deleted the socket + // out from under us + +} + +//=========================================================================== +void CSocket::OnConnect () { + + // Verify that we haven't already dispatched a connection success + // message. We know that we haven't already dispatched a connection + // failure message, because the socket object would have already been + // deleted in that case. + if (IsConnected()) + return; + + // Verify that a connect command is queued + m_critSect.Enter(); + Command * command = m_commandList.Head(); + if (command && (command->code == command->CONNECT)) + m_commandList.Unlink(command); + else + command = nil; + m_critSect.Leave(); + if (!command) + return; + + // Check for an error + int error; + int errorLen = sizeof(error); + int result = getsockopt( + m_sock, + SOL_SOCKET, + SO_ERROR, + (char *)&error, + &errorLen + ); + if (result) + LogMsg(kLogFatal, "failed: getsockopt"); + + // Get addresses for the connection notification + AsyncNotifySocketConnect notify; + ZERO(notify.localAddr); + ZERO(notify.remoteAddr); + int nameLen = sizeof(notify.localAddr); + if (getsockname(m_sock, (sockaddr *)¬ify.localAddr, &nameLen)) + if (GetLastError() == WSAENOTCONN) + error = WSAENOTCONN; + else + LogMsg(kLogFatal, "failed: getsockname"); + nameLen = sizeof(notify.remoteAddr); + if (getpeername(m_sock, (sockaddr *)¬ify.remoteAddr, &nameLen)) + if (GetLastError() == WSAENOTCONN) + error = WSAENOTCONN; + else + LogMsg(kLogFatal, "failed: getpeername"); + + // Dispatch the connection notification + notify.param = command->param; + notify.asyncId = 0; + notify.connType = command->connect.connType; + bool notifyResult = m_notifyProc( + error ? nil : (AsyncSocket)this, + error ? kNotifySocketConnectFailed : kNotifySocketConnectSuccess, + ¬ify, + &m_userState + ); + + // Delete the connect command + DEL(command); + + // Handle failure to connect + if (error) { + + // Destroy the socket + DEL(this); + + } + + // Handle a successful connection + else { + + // Mark the socket as connected + m_dispatched |= kDispatchedConnectSuccess; + + // If the application requested the socket closed, disconnect it + if (!notifyResult) { + Disconnect(true); + OnClose(); + } + + } + +} + +//=========================================================================== +void CSocket::OnReadReady () { + for (;;) { + + // Issue the read + int recvResult = recv( + m_sock, + (char *)(m_readBuffer + m_readBytes), + sizeof(m_readBuffer) - m_readBytes, + 0 // flags + ); + if (recvResult == SOCKET_ERROR) + if (GetLastError() == WSAEWOULDBLOCK) + return; + else + LogMsg(kLogFatal, "failed: recv"); + + // If the socket reported disconnection, close it + if ((recvResult == SOCKET_ERROR) || !recvResult) { + Disconnect(true); + OnClose(); + return; + } + + // Update the read buffer state + m_readBytes += recvResult; + + // Dispatch a read notification + AsyncNotifySocketRead notify; + notify.param = nil; + notify.asyncId = 0; + notify.buffer = m_readBuffer; + notify.bytes = m_readBytes; + notify.bytesProcessed = 0; + bool notifyResult = m_notifyProc( + (AsyncSocket)this, + kNotifySocketRead, + ¬ify, + &m_userState + ); + + // If the application processed data, remove it from the read buffer + if (notify.bytesProcessed >= m_readBytes) + m_readBytes = 0; + else if (notify.bytesProcessed) { + MemMove( + &m_readBuffer[0], + &m_readBuffer[notify.bytesProcessed], + m_readBytes - notify.bytesProcessed + ); + m_readBytes -= notify.bytesProcessed; + } + + // If the application returned false from its notification procedure, + // disconnect + if (!notifyResult) { + Disconnect(false); + return; + } + + } +} + +//=========================================================================== +void CSocket::OnWriteReady () { + + // Verify that the socket is ready to send data + if (IsDisconnected() || !IsConnected()) + return; + + // Enter the critical section and verify that data is queued + m_critSect.Enter(); + if (!m_sendQueue.Bytes()) { + m_critSect.Leave(); + return; + } + + // Attempt to send queued data + int result = send( + m_sock, + (const char *)m_sendQueue.Ptr(), + m_sendQueue.Bytes(), + 0 + ); + if (result == SOCKET_ERROR) + if (GetLastError() == WSAEWOULDBLOCK) + result = 0; + else + LogMsg(kLogFatal, "failed: send"); + + // Dequeue sent bytes + if (result != SOCKET_ERROR) + if ((unsigned)result == m_sendQueue.Bytes()) + m_sendQueue.Clear(); + else if (result) { + MemMove( + &m_sendQueue[0], + &m_sendQueue[result], + m_sendQueue.Bytes() - result + ); + COMPILER_ASSERT(sizeof(m_sendQueue[0]) == sizeof(byte)); + m_sendQueue.SetCount(m_sendQueue.Count() - result); + } + + // Leave the critical section + m_critSect.Leave(); + + // If the send failed, close the socket + if (result == SOCKET_ERROR) { + Disconnect(true); + OnClose(); + } + + +} + +//=========================================================================== +void CSocket::ProcessQueue () { + + // Verify that the socket is connected + if (IsDisconnected() || !IsConnected()) + return; + + // Dispatch each command in the queue + for (;;) { + + // Remove the next command from the queue + m_critSect.Enter(); + Command * command = m_commandList.Head(); + if (command) + m_commandList.Unlink(command); + m_critSect.Leave(); + if (!command) + break; + + // Dispatch it + switch (command->code) { + + case command->WRITE: { + AsyncNotifySocketWrite notify; + notify.param = command->param; + notify.asyncId = 0; + notify.buffer = (byte *)command->write.data; + notify.bytes = command->write.bytes; + notify.bytesProcessed = 0; + bool notifyResult = m_notifyProc( + (AsyncSocket)this, + kNotifySocketWrite, + ¬ify, + &m_userState + ); + if (!notifyResult) + Disconnect(false); + } + break; + + DEFAULT_FATAL(command->code); + } + + // Delete the command + DEL(command); + + } + +} + +//=========================================================================== +void CSocket::QueueConnect ( + void * param, + byte connType +) { + ASSERT(!IsConnected() && !IsDisconnected()); + + Command * command = NEW(Command); + command->code = command->CONNECT; + command->param = param; + command->connect.connType = connType; + m_commandList.Link(command); +} + +//=========================================================================== +void CSocket::QueueWrite ( + void * param, + const void * data, + unsigned bytes +) { + ASSERT(!IsDisconnected()); + + Command * command = NEW(Command); + command->code = command->CONNECT; + command->param = param; + command->write.data = data; + command->write.bytes = bytes; + m_commandList.Link(command); +} + +//=========================================================================== +bool CSocket::Send ( // returns false if disconnected + const void * data, + unsigned bytes +) { + + // Verify that the socket has not been disconnected + if (IsDisconnected()) + return false; + + // Enter the critical section + m_critSect.Enter(); + + // Attempt to send the data immediately + int result; + if (IsConnected() && !m_sendQueue.Bytes()) { + result = send( + m_sock, + (const char *)data, + bytes, + 0 + ); + if ((result == SOCKET_ERROR) && (GetLastError() != WSAEWOULDBLOCK)) + LogMsg(kLogFatal, "failed: send"); + } + else + result = 0; + + // If we were unable to send the entire message, queue the unsent portion + if ((unsigned)result < bytes) { + m_sendQueue.Add( + (const byte *)data + result, + bytes - result + ); + } + + // Leave the critical section + m_critSect.Leave(); + + return true; +} + + +/**************************************************************************** +* +* Window procedure +* +***/ + +#define CLASS_NAME "AsyncSelectWindow" + +#define WM_CANCEL_CONNECT (WM_USER + 0) +#define WM_PROCESS_QUEUE (WM_USER + 1) + +static CCritSect s_critSect; +static HANDLE s_readyEvent; +static HANDLE s_thread; + +static unsigned THREADCALL W9xSocketThreadProc (AsyncThread *); + +static LRESULT CALLBACK WndProc ( + HWND window, + UINT message, + WPARAM wParam, + LPARAM lParam +); + +//=========================================================================== +static void OnCancelConnect ( + AsyncCancelId sequence +) { + + // Find the associated object + s_critSect.Enter(); + CSocket * object = s_mapper.Find(sequence); + s_critSect.Leave(); + + // Force the object to dispatch connect success or failure. We must not + // reference this object after the call to OnConnect() returns, because + // it may have been deleted. + if (object) + object->OnConnect(); + +} + +//=========================================================================== +static void OnProcessQueue ( + AsyncCancelId sequence +) { + + // Find the associated object + s_critSect.Enter(); + CSocket * object = s_mapper.Find(sequence); + s_critSect.Leave(); + + // Process the object's dispatch queue + if (object) + object->ProcessQueue(); + +} + +//=========================================================================== +static void OnSocketEvent ( + UINT message, + SOCKET sock, + long event +) { + + // Enter the critical section + s_critSect.Enter(); + + // Find the associated object + CSocket * object = s_mapper.Find(message, sock); + + // Verify that the object has not been disconnected. An object that + // has been disconnected can be deleted at any time, and therefore + // can't be referenced outside the critical section. However, the + // object cannot possibly have been disconnected here because objects + // once connected are only disconnected during a call to OnClose(), + // which is the last event we process for an object. + ASSERT(!object || !object->IsDisconnected()); + + // Leave the critical section + s_critSect.Leave(); + + // Dispatch the event + if (object) + switch (event) { + case FD_CLOSE: object->OnClose(); break; + case FD_CONNECT: object->OnConnect(); break; + case FD_READ: object->OnReadReady(); break; + case FD_WRITE: object->OnWriteReady(); break; + } + + // We must not reference the object after dispatching the event, + // because the event processor may have deleted it + +} + +//=========================================================================== +static void PostCancelConnect (AsyncCancelId sequence) { + s_critSect.Enter(); + if (s_window) + PostMessage(s_window, WM_CANCEL_CONNECT, 0, (LPARAM)sequence); + s_critSect.Leave(); +} + +//=========================================================================== +static void PostProcessQueue (AsyncCancelId sequence) { + s_critSect.Enter(); + if (s_window) + PostMessage(s_window, WM_PROCESS_QUEUE, 0, (LPARAM)sequence); + s_critSect.Leave(); +} + +//=========================================================================== +static void ShutdownWindow () { + s_critSect.Enter(); + + // Close the window + if (s_window) { + PostMessage(s_window, WM_CLOSE, 0, 0); + s_window = 0; + } + + // Close the thread + if (s_thread) { + HANDLE thread = s_thread; + s_thread = 0; + s_critSect.Leave(); + WaitForSingleObject(thread, INFINITE); + s_critSect.Enter(); + } + + // Close the ready event + if (s_readyEvent) { + CloseHandle(s_readyEvent); + s_readyEvent = 0; + } + + s_critSect.Leave(); +} + +//=========================================================================== +static HWND StartupWindow () { + + s_critSect.Enter(); + + // Check whether we need to create the window + if (!s_window) { + + // Check whether we need to create the thread + if (!s_thread) { + + // Create an object which the thread can use to signal creation + // of the window + ASSERT(!s_readyEvent); + s_readyEvent = CreateEvent(nil, TRUE, FALSE, nil); + + // Create a thread which will create the window and run its + // message loop + s_thread = (HANDLE) AsyncThreadCreate( + W9xSocketThreadProc, + nil, + L"W9xSockThread" + ); + + } + + // Wait for the thread to create the window + HANDLE event = s_readyEvent; + s_critSect.Leave(); + WaitForSingleObject(event, INFINITE); + s_critSect.Enter(); + + } + + // Return the window handle + HWND window = s_window; + s_critSect.Leave(); + return window; + +} + +//=========================================================================== +static unsigned THREADCALL W9xSocketThreadProc (AsyncThread *) { + ASSERT(!s_window); + + // Enter the critical section + s_critSect.Enter(); + + // Register the window class + HINSTANCE instance = (HINSTANCE)GetModuleHandle(nil); + WNDCLASS wndClass; + ZERO(wndClass); + wndClass.lpfnWndProc = WndProc; + wndClass.hInstance = instance; + wndClass.lpszClassName = CLASS_NAME; + RegisterClass(&wndClass); + + // Create the window + s_window = CreateWindow( + CLASS_NAME, + CLASS_NAME, + WS_OVERLAPPED, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + 0, // parent window + 0, // menu + instance, + nil // param + ); + + // Leave the critical section + s_critSect.Leave(); + + // Trigger an event to allow anyone blocking on window creation to proceed + SetEvent(s_readyEvent); + + // Dispatch messages + MSG msg; + while (GetMessage(&msg, nil, 0, 0)) + if (msg.message == WM_CLOSE) + break; + else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // Destroy the window + s_critSect.Enter(); + if (s_window) { + DestroyWindow(s_window); + s_window = 0; + } + s_critSect.Leave(); + + return 0; +} + +//=========================================================================== +static LRESULT CALLBACK WndProc ( + HWND window, + UINT message, + WPARAM wParam, + LPARAM lParam +) { + switch (message) { + + case WM_CANCEL_CONNECT: + OnCancelConnect((AsyncCancelId)lParam); + return 0; + + case WM_PROCESS_QUEUE: + OnProcessQueue((AsyncCancelId)lParam); + return 0; + + default: + if ((message >= WM_APP) && (message < 0xc000)) + OnSocketEvent(message, (SOCKET)wParam, WSAGETSELECTEVENT(lParam)); + else + return DefWindowProc(window, message, wParam, lParam); + break; + + } + return 0; +} + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +void W9xSocketConnect ( + AsyncCancelId * cancelId, + const NetAddress & netAddr, + FAsyncNotifySocketProc notifyProc, + void * param, + const void * sendData, + unsigned sendBytes, + unsigned connectMs, + unsigned localPort +) { + // Not supported for W9X + ref(connectMs); + ref(localPort); + + // If necessary, startup the window and message queue + HWND window = StartupWindow(); + + // Create a socket + SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (!sock) + LogMsg(kLogFatal, "failed: socket"); + + // Assign a sequence number + static unsigned s_sequence; + AsyncCancelId sequence = (AsyncCancelId)++s_sequence; + if (!sequence) + sequence = (AsyncCancelId)++s_sequence; + *cancelId = sequence; + + // Assign a message id + static UINT s_message; + UINT message = (s_message++ & 0x3fff) | WM_APP; // range 0x8000 - 0xbfff + + // Create a socket object + CSocket * object = NEW(CSocket)( + sequence, + message, + sock, + notifyProc + ); + + // Queue a connect notification for the socket + object->QueueConnect( + param, + sendBytes ? ((const byte *)sendData)[0] : (byte)0 + ); + + // Queue sending data + if (sendBytes) + object->Send(sendData, sendBytes); + + // Setup notifications for the socket + int result = WSAAsyncSelect( + sock, + window, + message, + FD_CLOSE | FD_CONNECT | FD_READ | FD_WRITE + ); + if (result) + LogMsg(kLogFatal, "failed: WSAAsyncSelect"); + + // Start the connection + if ( connect(sock, (const sockaddr *)&netAddr, sizeof(sockaddr)) && + (GetLastError() != WSAEWOULDBLOCK) ) + LogMsg(kLogFatal, "failed: connect. err=%u", GetLastError()); + +} + +//=========================================================================== +void W9xSocketConnectCancel ( + FAsyncNotifySocketProc notifyProc, + AsyncCancelId cancelId +) { + + // Enter the critical section + s_critSect.Enter(); + + // If a cancel id was given, cancel that one connection + if (cancelId) + PostCancelConnect(cancelId); + + // Otherwise, cancel all connections using the specified notification + // procedure + else + for (unsigned index = 0; ; ++index) { + + // Find the next socket object + CSocket * object = s_mapper.Find(notifyProc, index); + if (!object) + break; + + // If the object exists and is not yet connected, cancel it + if (object) + PostCancelConnect(object->GetSequence()); + + } + + // Leave the critical section + s_critSect.Leave(); + +} + +//=========================================================================== +void W9xSocketDelete ( + AsyncSocket sock +) { + + // Dereference the object + CSocket * object = (CSocket *)sock; + + // Delete the object + s_critSect.Enter(); + DEL(object); + s_critSect.Leave(); + +} + +//=========================================================================== +void W9xSocketDestroy () { + + // Shutdown the window and message queue + ShutdownWindow(); + +} + +//=========================================================================== +void W9xSocketEnableNagling ( + AsyncSocket sock, + bool enable +) { + + // Dereference the object + CSocket * object = (CSocket *)sock; + + // set nagling state + object->EnableNagling(enable); + +} + +//=========================================================================== +void W9xSocketDisconnect ( + AsyncSocket sock, + bool hardClose +) { + + // Dereference the object + CSocket * object = (CSocket *)sock; + + // Disconnect + object->Disconnect(hardClose); + +} + +//=========================================================================== +unsigned W9xSocketStartListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +) { + ref(listenAddr); + ref(notifyProc); + return 0; + +} + +//=========================================================================== +void W9xSocketStopListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +) { + ref(listenAddr); + ref(notifyProc); +} + +//=========================================================================== +bool W9xSocketSend ( + AsyncSocket sock, + const void * data, + unsigned bytes +) { + + // Dereference the object + CSocket * object = (CSocket *)sock; + + // Send the data + return object->Send(data, bytes); + +} + +//=========================================================================== +void W9xSocketSetNotifyProc ( + AsyncSocket sock, + FAsyncNotifySocketProc notifyProc +) { + ref(sock); + ref(notifyProc); + + // This provider does not allow changing the notification procedure + FATAL("SocketSetNotifyProc"); +} + +//=========================================================================== +void W9xSocketSetBacklogAlloc ( + AsyncSocket sock, + unsigned bufferSize +) { + + // This provider does not limit the maximum backlog allocation + ref(sock); + ref(bufferSize); + +} + +//=========================================================================== +bool W9xSocketWrite ( + AsyncSocket sock, + const void * data, + unsigned bytes, + void * param +) { + + // Dereference the object + CSocket * object = (CSocket *)sock; + + // Send the data + if (!object->Send(data, bytes)) + return false; + + // Queue a completion notification + object->QueueWrite(param, data, bytes); + + // Trigger processing the queue + PostProcessQueue(object->GetSequence()); + + return true; +} + + +} // namespace W9x diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xThread.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xThread.cpp new file mode 100644 index 00000000..4bde22e4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xThread.cpp @@ -0,0 +1,437 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/W9x/pnAceW9xThread.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +#include "pnAceW9xInt.h" + + +namespace W9x { + +/***************************************************************************** +* +* Private data +* +***/ + +const unsigned kThreadCount = 2; + +static CCritSect s_critSect; +static bool s_destroying; +static HANDLE s_destroyEvent; +static unsigned s_sequence; +static HANDLE s_shutdownEvent; +static HANDLE s_signalEvent; +static HANDLE s_thread[kThreadCount]; + + +/**************************************************************************** +* +* ThreadWaitRec +* +***/ + +struct ThreadWaitRec { + HANDLE event; + LINK(ThreadWaitRec) link; +}; + + +/**************************************************************************** +* +* CThreadDispRec +* +***/ + +class CThreadDispRec { +private: + CThreadDispObject * m_object; + void * m_op; + AsyncId m_ioId; + LISTDECL(ThreadWaitRec, link) m_waitList; + +public: + LINK(CThreadDispRec) m_link; + + CThreadDispRec ( + CThreadDispObject * object, + void * op, + AsyncId * asyncId + ); + ~CThreadDispRec (); + + void Complete (CCritSect * critSect); + AsyncId GetId () const { return m_ioId; } + void LinkWait (ThreadWaitRec * wait); + +}; + +static LISTDECL(CThreadDispRec, m_link) s_dispList; +static LISTDECL(CThreadDispRec, m_link) s_dispInProcList; + +//=========================================================================== +CThreadDispRec::CThreadDispRec ( + CThreadDispObject * object, + void * op, + AsyncId * asyncId +) : + m_object(object), + m_op(op) +{ + s_critSect.Enter(); + + // Verify that this module is not being destroyed + ASSERT(!s_destroying); + + // Increment the owning object's reference count + object->IncRef(); + + // Assign an id + m_ioId = (AsyncId)++s_sequence; + if (!m_ioId) + m_ioId = (AsyncId)++s_sequence; + *asyncId = m_ioId; + + // Link this record to the dispatch list + s_dispList.Link(this); + + s_critSect.Leave(); +} + +//=========================================================================== +CThreadDispRec::~CThreadDispRec () { + + // Delete the operation data + CThreadDispObject * object = m_object; + object->Delete(m_op); + + s_critSect.Enter(); + + // Unlink this record + m_link.Unlink(); + + // Wake up all threads blocking on this operation. We must unlink each + // wait record before we signal it. + for (ThreadWaitRec * rec; (rec = m_waitList.Head()) != nil; ) { + m_waitList.Unlink(rec); + SetEvent(rec->event); + } + + // Decrement the owning object's reference count + object->DecRef(); + + s_critSect.Leave(); + +} + +//=========================================================================== +void CThreadDispRec::Complete (CCritSect * critSect) { + m_object->Complete(m_op, critSect, m_ioId); +} + +//=========================================================================== +void CThreadDispRec::LinkWait (ThreadWaitRec * wait) { + + // The caller should have already claimed the critical section before + // calling this function + + m_waitList.Link(wait); + +} + + +/**************************************************************************** +* +* CThreadDispObject +* +***/ + +//=========================================================================== +CThreadDispObject::CThreadDispObject () { + IncRef(); +} + +//=========================================================================== +void CThreadDispObject::Close () { + s_critSect.Enter(); + DecRef(); + s_critSect.Leave(); +} + +//=========================================================================== +AsyncId CThreadDispObject::Queue (void * op) { + AsyncId asyncId = 0; + NEW(CThreadDispRec)(this, op, &asyncId); + SetEvent(s_signalEvent); + return asyncId; +} + + +/**************************************************************************** +* +* Thread procedure +* +***/ + +//=========================================================================== +static unsigned CALLBACK W9xThreadProc (AsyncThread *) { + + // Perform the main thread loop + for (;;) { + unsigned timeout = (unsigned)-1; + + // If an operation is queued, complete it and dispatch a notification. + // The code that processes the operation is responsible for leaving + // our critical section. This ensures that operations are completed + // in order. + s_critSect.Enter(); + CThreadDispRec * rec = s_dispList.Head(); + if (rec) { + s_dispInProcList.Link(rec); + rec->Complete(&s_critSect); + DEL(rec); + timeout = 0; + } + else { + s_critSect.Leave(); + } + + + // Consume events, check for destruction, and block if we have + // nothing to do. + HANDLE events[] = {s_destroyEvent, s_signalEvent}; + dword result = WaitForMultipleObjects( + arrsize(events), + events, + FALSE, + INFINITE + ); + if (result == WAIT_OBJECT_0) + return 0; + } +} + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +void W9xThreadDestroy ( + unsigned exitThreadWaitMs +) { + + // Wait until all outstanding I/O is complete. We allow new I/O + // operations to be queued while old ones are completing. + s_critSect.Enter(); + while (s_dispList.Head() || s_dispInProcList.Head()) { + s_critSect.Leave(); + Sleep(10); + s_critSect.Enter(); + } + + // Once all I/O operations are complete, we disallow any future + // I/O operations from being queued. + s_destroying = true; + s_critSect.Leave(); + + // Signal thread destruction + if (s_destroyEvent) + SetEvent(s_destroyEvent); + + // Wait for thread destruction + for (unsigned thread = kThreadCount; thread--; ) + if (s_thread[thread]) { + + // Wait for the thread to terminate + WaitForSingleObject(s_thread[thread], exitThreadWaitMs); + + // Close the thread handle + CloseHandle(s_thread[thread]); + s_thread[thread] = 0; + + } + + // Destroy internal modules + W9xSocketDestroy(); + + // Destroy events + if (s_destroyEvent) { + CloseHandle(s_destroyEvent); + s_destroyEvent = 0; + } + if (s_shutdownEvent) { + CloseHandle(s_shutdownEvent); + s_shutdownEvent = 0; + } + if (s_signalEvent) { + CloseHandle(s_signalEvent); + s_signalEvent = 0; + } + +} + +//=========================================================================== +void W9xThreadInitialize () { + + // Reset static variables + s_destroying = false; + + // Create a manual reset event to use for signaling thread destruction + if (s_destroyEvent) + ResetEvent(s_destroyEvent); + else { + s_destroyEvent = CreateEvent(nil, TRUE, FALSE, nil); + ASSERT(s_destroyEvent); + } + + // Create an auto-reset event to use for signaling the thread to process + // notifications + if (!s_signalEvent) { + s_signalEvent = CreateEvent(nil, FALSE, FALSE, nil); + ASSERT(s_signalEvent); + } + + // Create a manual reset event to use for signaling application shutdown + if (s_shutdownEvent) + ResetEvent(s_shutdownEvent); + else { + s_shutdownEvent = CreateEvent(nil, TRUE, FALSE, nil); + ASSERT(s_shutdownEvent); + } + + // Create threads + for (unsigned thread = 0; thread < kThreadCount; ++thread) { + if (!s_thread[thread]) { + s_thread[thread] = (HANDLE) AsyncThreadCreate( + W9xThreadProc, + (void *) thread, + L"W9xWorkerThread" + ); + } + } + +} + +//=========================================================================== +void W9xThreadSignalShutdown () { + SetEvent(s_shutdownEvent); +} + +//=========================================================================== +void W9xThreadSleep ( + unsigned sleepMs +) { + Sleep(sleepMs); +} + +//=========================================================================== +void W9xThreadWaitForShutdown () { + + // We know that the applicaton is finished initializing at this point. + // While it was still initializing, it may have returned an infinite + // sleep time from the idle procedure, which would prevent us from ever + // calling it again. Therefore, we trigger an idle callback here. + SetEvent(s_signalEvent); + + // Wait for the application to signal shutdown + WaitForSingleObject(s_shutdownEvent, INFINITE); + +} + +//=========================================================================== +bool W9xThreadWaitId ( + AsyncFile file, + AsyncId asyncId, + unsigned timeoutMs +) { + ref(file); + + // Find a pending I/O operation with the given id + s_critSect.Enter(); + CThreadDispRec * disp; + for (disp = s_dispList.Head(); disp && (disp->GetId() != asyncId); disp = s_dispList.Next(disp)) + ; + if (!disp) + for (disp = s_dispInProcList.Head(); disp && (disp->GetId() != asyncId); disp = s_dispInProcList.Next(disp)) + ; + + // If we couldn't find the given id, the operation must have already + // completed, so return true. + if (!disp) { + s_critSect.Leave(); + return true; + } + + // The operation has not completed. If the timeout is zero, return + // false. + if (!timeoutMs) { + s_critSect.Leave(); + return false; + } + + // Create a wait event + HANDLE event = CreateEvent(nil, FALSE, FALSE, nil); + + // Create a wait record and link it to the I/O operation + ThreadWaitRec wait; + wait.event = event; + disp->LinkWait(&wait); + s_critSect.Leave(); + + // Wait for the operation to complete + DWORD result = WaitForSingleObject(event, timeoutMs); + + // If the operation completed then the dispatcher unlinked our wait + // record before signaling it. We can simply free the event and return. + if (result == WAIT_OBJECT_0) { + CloseHandle(event); + return true; + } + + // Unlink our wait record from the I/O operation + s_critSect.Enter(); + wait.link.Unlink(); + s_critSect.Leave(); + + // Free the event + CloseHandle(event); + + // Return false, because the operation did not complete during the + // timeout period + return false; + +} + +} // namespace W9x diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Win32/pnAceW32Dns.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Win32/pnAceW32Dns.cpp new file mode 100644 index 00000000..619e27bb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Win32/pnAceW32Dns.cpp @@ -0,0 +1,384 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Win32/pnAceW32Dns.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + +enum { + WM_LOOKUP_EXIT = WM_APP, + WM_LOOKUP_FOUND_HOST +}; + +const unsigned kMaxLookupName = 128; + +struct Lookup { + LINK(Lookup) link; + AsyncCancelId cancelId; + HANDLE cancelHandle; + FAsyncLookupProc lookupProc; + unsigned port; + void * param; + wchar name[kMaxLookupName]; + char buffer[MAXGETHOSTSTRUCT]; +}; + +static CCritSect s_critsect; +static LISTDECL(Lookup, link) s_lookupList; +static HANDLE s_lookupThread; +static HWND s_lookupWindow; +static unsigned s_nextLookupCancelId = 1; + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +static void LookupProcess (Lookup * lookup, unsigned error) { + unsigned count = 0; + NetAddress * addrs = nil; + for (ONCE) { + if (error) + break; + + const HOSTENT & host = * (HOSTENT *) lookup->buffer; + if (host.h_addrtype != AF_INET) + break; + if (host.h_length != sizeof(in_addr)) + break; + if (!host.h_addr_list) + break; + + in_addr const * const * const inAddr = (in_addr **) host.h_addr_list; + + // count the number of addresses + while (inAddr[count]) + ++count; + + // allocate a buffer large enough to hold all the addresses + addrs = (NetAddress *) _alloca(sizeof(*addrs) * count); + MemZero(addrs, sizeof(*addrs) * count); + + // fill in address data + const word port = htons((word) lookup->port); + for (unsigned i = 0; i < count; ++i) { + sockaddr_in * inetaddr = (sockaddr_in *) &addrs[i]; + inetaddr->sin_family = AF_INET; + inetaddr->sin_addr = *inAddr[i]; + inetaddr->sin_port = port; + } + + if (host.h_name && host.h_name[0]) + StrToUnicode(lookup->name, host.h_name, arrsize(lookup->name)); + } + + if (lookup->lookupProc) + lookup->lookupProc(lookup->param, lookup->name, count, addrs); + + // we can delete the operation outside an IoConn critical + // section because it isn't linked into an ioConn opList + // and because connection attempts are not waitable + ASSERT(!lookup->link.IsLinked()); + DEL(lookup); + PerfSubCounter(kAsyncPerfNameLookupAttemptsCurr, 1); +} + +//=========================================================================== +static void LookupFindAndProcess (HANDLE cancelHandle, unsigned error) { + // find the operation for this cancel handle + Lookup * lookup; + s_critsect.Enter(); + for (lookup = s_lookupList.Head(); lookup; lookup = s_lookupList.Next(lookup)) { + if (lookup->cancelHandle == cancelHandle) { + lookup->cancelHandle = nil; + s_lookupList.Unlink(lookup); + break; + } + } + s_critsect.Leave(); + if (lookup) + LookupProcess(lookup, error); +} + +//=========================================================================== +static unsigned THREADCALL LookupThreadProc (AsyncThread * thread) { + static const char WINDOW_CLASS[] = "AsyncLookupWnd"; + WNDCLASS wc; + ZERO(wc); + wc.lpfnWndProc = DefWindowProc; + wc.hInstance = GetModuleHandle(0); + wc.lpszClassName = WINDOW_CLASS; + RegisterClass(&wc); + + s_lookupWindow = CreateWindow( + WINDOW_CLASS, + WINDOW_CLASS, + WS_OVERLAPPED, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + (HWND)0, + (HMENU) 0, + wc.hInstance, + 0 + ); + if (!s_lookupWindow) + ErrorFatal(__LINE__, __FILE__, "CreateWindow %#x", GetLastError()); + + HANDLE lookupStartEvent = (HANDLE) thread->argument; + SetEvent(lookupStartEvent); + + MSG msg; + while (GetMessage(&msg, s_lookupWindow, 0, 0)) { + if (msg.message == WM_LOOKUP_FOUND_HOST) + LookupFindAndProcess((HANDLE) msg.wParam, HIWORD(msg.lParam)); + else if (msg.message == WM_LOOKUP_EXIT) + break; + else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + // fail all pending name lookups + for (;;) { + s_critsect.Enter(); + Lookup * lookup = s_lookupList.Head(); + if (lookup) { + WSACancelAsyncRequest(lookup->cancelHandle); + lookup->cancelHandle = nil; + s_lookupList.Unlink(lookup); + } + s_critsect.Leave(); + if (!lookup) + break; + + LookupProcess(lookup, (unsigned) -1); + } + + // cleanup + DestroyWindow(s_lookupWindow); + s_lookupWindow = nil; + + return 0; +} + +//=========================================================================== +static void StartLookupThread () { + if (s_lookupThread) + return; + + // create a shutdown event + HANDLE lookupStartEvent = CreateEvent( + (LPSECURITY_ATTRIBUTES) 0, + true, // manual reset + false, // initial state off + (LPCTSTR) 0 // name + ); + if (!lookupStartEvent) + ErrorFatal(__LINE__, __FILE__, "CreateEvent %#x", GetLastError()); + + // create a thread to perform lookups + s_lookupThread = (HANDLE) AsyncThreadCreate( + LookupThreadProc, + lookupStartEvent, + L"AsyncLookupThread" + ); + + WaitForSingleObject(lookupStartEvent, INFINITE); + CloseHandle(lookupStartEvent); + ASSERT(s_lookupWindow); +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//=========================================================================== +void DnsDestroy (unsigned exitThreadWaitMs) { + if (s_lookupThread) { + PostMessage(s_lookupWindow, WM_LOOKUP_EXIT, 0, 0); + WaitForSingleObject(s_lookupThread, exitThreadWaitMs); + CloseHandle(s_lookupThread); + s_lookupThread = nil; + ASSERT(!s_lookupWindow); + } +} + + +/***************************************************************************** +* +* Public functions +* +***/ + +//=========================================================================== +void AsyncAddressLookupName ( + AsyncCancelId * cancelId, // out + FAsyncLookupProc lookupProc, + const wchar name[], + unsigned port, + void * param +) { + ASSERT(lookupProc); + ASSERT(name); + + PerfAddCounter(kAsyncPerfNameLookupAttemptsCurr, 1); + PerfAddCounter(kAsyncPerfNameLookupAttemptsTotal, 1); + + // Get name/port + char ansiName[kMaxLookupName]; + StrToAnsi(ansiName, name, arrsize(ansiName)); + if (char * portStr = StrChr(ansiName, ':')) { + if (unsigned newPort = StrToUnsigned(portStr + 1, nil, 10)) + port = newPort; + *portStr = 0; + } + + // Initialize lookup + Lookup * lookup = NEW(Lookup); + lookup->lookupProc = lookupProc; + lookup->port = port; + lookup->param = param; + StrCopy(lookup->name, name, arrsize(lookup->name)); + + s_critsect.Enter(); + { + // Start the lookup thread if it wasn't started already + StartLookupThread(); + s_lookupList.Link(lookup); + + // get cancel id; we can avoid checking for zero by always using an odd number + ASSERT(s_nextLookupCancelId & 1); + s_nextLookupCancelId += 2; + *cancelId = lookup->cancelId = (AsyncCancelId) s_nextLookupCancelId; + + // Perform async lookup + lookup->cancelHandle = WSAAsyncGetHostByName( + s_lookupWindow, + WM_LOOKUP_FOUND_HOST, + ansiName, + &lookup->buffer[0], + sizeof(lookup->buffer) + ); + if (!lookup->cancelHandle) { + PostMessage(s_lookupWindow, WM_LOOKUP_FOUND_HOST, nil, (unsigned) -1); + } + } + s_critsect.Leave(); +} + +//=========================================================================== +void AsyncAddressLookupAddr ( + AsyncCancelId * cancelId, // out + FAsyncLookupProc lookupProc, + const NetAddress & address, + void * param +) { + ASSERT(lookupProc); + + PerfAddCounter(kAsyncPerfNameLookupAttemptsCurr, 1); + PerfAddCounter(kAsyncPerfNameLookupAttemptsTotal, 1); + + // Initialize lookup + Lookup * lookup = NEW(Lookup); + lookup->lookupProc = lookupProc; + lookup->port = 1; + lookup->param = param; + NetAddressToString( + address, + lookup->name, + arrsize(lookup->name), + kNetAddressFormatNodeNumber + ); + + s_critsect.Enter(); + { + // Start the lookup thread if it wasn't started already + StartLookupThread(); + s_lookupList.Link(lookup); + + // get cancel id; we can avoid checking for zero by always using an odd number + ASSERT(s_nextLookupCancelId & 1); + s_nextLookupCancelId += 2; + *cancelId = lookup->cancelId = (AsyncCancelId) s_nextLookupCancelId; + + // Perform async lookup + u_long addr = ((const sockaddr_in *) &address)->sin_addr.S_un.S_addr; + lookup->cancelHandle = WSAAsyncGetHostByAddr( + s_lookupWindow, + WM_LOOKUP_FOUND_HOST, + (const char *) &addr, + sizeof(addr), + AF_INET, + &lookup->buffer[0], + sizeof(lookup->buffer) + ); + if (!lookup->cancelHandle) { + PostMessage(s_lookupWindow, WM_LOOKUP_FOUND_HOST, nil, (unsigned) -1); + } + } + s_critsect.Leave(); +} + +//=========================================================================== +void AsyncAddressLookupCancel ( + FAsyncLookupProc lookupProc, + AsyncCancelId cancelId // nil = cancel all with specified lookupProc +) { + s_critsect.Enter(); + for (Lookup * lookup = s_lookupList.Head(); lookup; lookup = s_lookupList.Next(lookup)) { + if (lookup->lookupProc && (lookup->lookupProc != lookupProc)) + continue; + if (cancelId && (lookup->cancelId != cancelId)) + continue; + if (!lookup->cancelHandle) + continue; + + // cancel this request + WSACancelAsyncRequest(lookup->cancelHandle); + lookup->cancelHandle = nil; + + // initiate user callback + PostMessage(s_lookupWindow, WM_LOOKUP_FOUND_HOST, nil, (unsigned) -1); + } + s_critsect.Leave(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Win32/pnAceW32Thread.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Win32/pnAceW32Thread.cpp new file mode 100644 index 00000000..9dd3c585 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Win32/pnAceW32Thread.cpp @@ -0,0 +1,264 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Win32/pnAceW32Thread.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + +struct AsyncThreadTaskList : AtomicRef { + ENetError error; + AsyncThreadTaskList (); + ~AsyncThreadTaskList (); +}; + +struct ThreadTask { + AsyncThreadTaskList * taskList; + FAsyncThreadTask callback; + void * param; + wchar debugStr[256]; +}; + +static HANDLE s_taskPort; + + +/***************************************************************************** +* +* AsyncThreadTaskList +* +***/ + +//=========================================================================== +AsyncThreadTaskList::AsyncThreadTaskList () +: error(kNetSuccess) +{ + PerfAddCounter(kAsyncPerfThreadTaskListCount, 1); +} + +//============================================================================ +AsyncThreadTaskList::~AsyncThreadTaskList () { + PerfSubCounter(kAsyncPerfThreadTaskListCount, 1); +} + + +/***************************************************************************** +* +* Local functions +* +***/ + +/**************************************************************************** +* +* ThreadTaskProc +* +***/ + +//=========================================================================== +static unsigned THREADCALL ThreadTaskProc (AsyncThread * thread) { + ref(thread); + + PerfAddCounter(kAsyncPerfThreadTaskThreadsActive, 1); + + for (;;) { + long desired = AsyncPerfGetCounter(kAsyncPerfThreadTaskThreadsDesired); + if (AsyncPerfGetCounter(kAsyncPerfThreadTaskThreadsRunning) > desired) { + long runningCount = PerfSubCounter(kAsyncPerfThreadTaskThreadsRunning, 1) - 1; + if (runningCount >= desired) { + if (runningCount > desired) + PostQueuedCompletionStatus(s_taskPort, 0, 0, 0); + break; + } + PerfAddCounter(kAsyncPerfThreadTaskThreadsRunning, 1); + } + + // Get the next work item + DWORD bytes; + ThreadTask * task; + LPOVERLAPPED op; + PerfSubCounter(kAsyncPerfThreadTaskThreadsActive, 1); + (void) GetQueuedCompletionStatus( + s_taskPort, + &bytes, + #ifdef _WIN64 + (PULONG_PTR) &task, + #else + (LPDWORD) &task, + #endif + &op, + INFINITE + ); + PerfAddCounter(kAsyncPerfThreadTaskThreadsActive, 1); + + if (task) { + #ifdef SERVER + void * check = CrashAddDeadlockCheck(thread->handle, task->debugStr); + #endif + + task->callback(task->param, task->taskList->error); + + #ifdef SERVER + CrashRemoveDeadlockCheck(check); + #endif + + task->taskList->DecRef("task"); + DEL(task); + } + } + PerfSubCounter(kAsyncPerfThreadTaskThreadsActive, 1); + + return 0; +} + +//=========================================================================== +static unsigned THREADCALL FirstThreadTaskProc (AsyncThread * param) { + while (AsyncPerfGetCounter(kAsyncPerfThreadTaskThreadsRunning) < AsyncPerfGetCounter(kAsyncPerfThreadTaskThreadsDesired)) { + PerfAddCounter(kAsyncPerfThreadTaskThreadsRunning, 1); + AsyncThreadCreate(ThreadTaskProc, nil, L"AsyncThreadTaskList"); + } + + return ThreadTaskProc(param); +} + +/***************************************************************************** +* +* Module functions +* +***/ + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void AsyncThreadTaskInitialize (unsigned threads) { + // Create completion port + s_taskPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0); + ASSERT(s_taskPort); + + // Create threads + AsyncThreadTaskSetThreadCount(threads); +} + +//============================================================================ +void AsyncThreadTaskDestroy () { + ASSERT(!AsyncPerfGetCounter(kAsyncPerfThreadTaskListCount)); + + if (s_taskPort) { + PerfSetCounter(kAsyncPerfThreadTaskThreadsDesired, 0); + PostQueuedCompletionStatus(s_taskPort, 0, 0, 0); + + // Wait until all threads have exited + while (AsyncPerfGetCounter(kAsyncPerfThreadTaskThreadsActive)) + AsyncSleep(10); + while (AsyncPerfGetCounter(kAsyncPerfThreadTaskThreadsRunning)) + AsyncSleep(10); + + // Cleanup completion port + CloseHandle(s_taskPort); + s_taskPort = nil; + } +} + +//=========================================================================== +unsigned AsyncThreadTaskGetThreadCount () { + return AsyncPerfGetCounter(kAsyncPerfThreadTaskThreadsDesired); +} + +//=========================================================================== +void AsyncThreadTaskSetThreadCount (unsigned threads) { + ASSERT(threads >= kThreadTaskMinThreads); + ASSERT(threads <= kThreadTaskMaxThreads); + + if (AsyncPerfGetCounter(kAsyncPerfThreadTaskThreadsDesired) == (long) threads) + return; + PerfSetCounter(kAsyncPerfThreadTaskThreadsDesired, (long) threads); + + if (AsyncPerfGetCounter(kAsyncPerfThreadTaskThreadsRunning) < AsyncPerfGetCounter(kAsyncPerfThreadTaskThreadsDesired)) { + PerfAddCounter(kAsyncPerfThreadTaskThreadsRunning, 1); + AsyncThreadCreate(FirstThreadTaskProc, nil, L"ThreadTaskList"); + } + else { + PostQueuedCompletionStatus(s_taskPort, 0, 0, 0); + } +} + +//=========================================================================== +AsyncThreadTaskList * AsyncThreadTaskListCreate () { + ASSERT(s_taskPort); + AsyncThreadTaskList * taskList = NEW(AsyncThreadTaskList); + taskList->IncRef("TaskList"); + return taskList; +} + +//=========================================================================== +void AsyncThreadTaskListDestroy ( + AsyncThreadTaskList * taskList, + ENetError error +) { + ASSERT(taskList); + ASSERT(error); + ASSERT(!taskList->error); + + taskList->error = error; + taskList->DecRef(); // REF:TaskList +} + +//=========================================================================== +void AsyncThreadTaskAdd ( + AsyncThreadTaskList * taskList, + FAsyncThreadTask callback, + void * param, + const wchar debugStr[], + EThreadTaskPriority priority /* = kThreadTaskPriorityNormal */ +) { + ASSERT(s_taskPort); + ASSERT(taskList); + ASSERT(callback); + ASSERT(priority == kThreadTaskPriorityNormal); + ref(priority); + + // Allocate a new task record + ThreadTask * task = NEW(ThreadTask); + task->taskList = taskList; + task->callback = callback; + task->param = param; + StrCopy(task->debugStr, debugStr, arrsize(task->debugStr)); // this will be sent with the deadlock checker email if this thread exceeds time set in plServer.ini + taskList->IncRef("Task"); + + PostQueuedCompletionStatus(s_taskPort, 0, (DWORD) task, NULL); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/pnAceInt.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/pnAceInt.h new file mode 100644 index 00000000..2f52b49a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/pnAceInt.h @@ -0,0 +1,264 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/pnAceInt.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_PNACEINT_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/pnAceInt.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNASYNCCOREEXE_PRIVATE_PNACEINT_H + + +/***************************************************************************** +* +* Core.cpp +* +***/ + +// Performance counter functions +long PerfAddCounter (unsigned id, unsigned n); +long PerfSubCounter (unsigned id, unsigned n); +long PerfSetCounter (unsigned id, unsigned n); + + +/***************************************************************************** +* +* Dns.cpp +* +***/ + +void DnsDestroy (unsigned exitThreadWaitMs); + + +/***************************************************************************** +* +* Thread.cpp +* +***/ + +void ThreadDestroy (unsigned exitThreadWaitMs); + + +/***************************************************************************** +* +* Timer.cpp +* +***/ + +void TimerDestroy (unsigned exitThreadWaitMs); + + +/**************************************************************************** +* +* Async API function types +* +***/ + +// Core +typedef void (* FInitialize) (); +typedef void (* FDestroy) (unsigned exitThreadWaitMs); +typedef void (* FSignalShutdown) (); +typedef void (* FWaitForShutdown) (); +typedef void (* FSleep) (unsigned sleepMs); + +// Files +typedef AsyncFile (* FAsyncFileOpen) ( + const wchar fullPath[], + FAsyncNotifyFileProc notifyProc, + EFileError * error, + unsigned desiredAccess, + unsigned openMode, + unsigned shareModeFlags, + void * userState, + qword * fileSize, + qword * fileLastWriteTime +); + +typedef void (* FAsyncFileClose) ( + AsyncFile file, + qword truncateSize +); + +typedef void (* FAsyncFileSetLastWriteTime) ( + AsyncFile file, + qword lastWriteTime +); + +typedef qword (* FAsyncFileGetLastWriteTime) ( + const wchar fileName[] +); + +typedef AsyncId (* FAsyncFileFlushBuffers) ( + AsyncFile file, + qword truncateSize, + bool notify, + void * param +); + +typedef AsyncId (* FAsyncFileRead) ( + AsyncFile file, + qword offset, + void * buffer, + unsigned bytes, + unsigned flags, + void * param +); + +typedef AsyncId (* FAsyncFileWrite) ( + AsyncFile file, + qword offset, + const void * buffer, + unsigned bytes, + unsigned flags, + void * param +); + +typedef AsyncId (* FAsyncFileCreateSequence) ( + AsyncFile file, + bool notify, + void * param +); + +typedef bool (* FAsyncFileSeek) ( + AsyncFile file, + qword distance, + EFileSeekFrom from +); + +typedef bool (* FAsyncFileWaitId) ( + AsyncFile file, + AsyncId asyncId, + unsigned timeoutMs +); + +// Sockets +typedef void (* FAsyncSocketConnect) ( + AsyncCancelId * cancelId, + const NetAddress & netAddr, + FAsyncNotifySocketProc notifyProc, + void * param, + const void * sendData, + unsigned sendBytes, + unsigned connectMs, + unsigned localPort +); + +typedef void (* FAsyncSocketConnectCancel) ( + FAsyncNotifySocketProc notifyProc, + AsyncCancelId cancelId +); + +typedef void (* FAsyncSocketDisconnect) ( + AsyncSocket sock, + bool hardClose +); + +typedef void (* FAsyncSocketDelete) (AsyncSocket sock); + +typedef bool (* FAsyncSocketSend) ( + AsyncSocket sock, + const void * data, + unsigned bytes +); + +typedef bool (* FAsyncSocketWrite) ( + AsyncSocket sock, + const void * buffer, + unsigned bytes, + void * param +); + +typedef void (* FAsyncSocketSetNotifyProc) ( + AsyncSocket sock, + FAsyncNotifySocketProc notifyProc +); + +typedef void (* FAsyncSocketSetBacklogAlloc) ( + AsyncSocket sock, + unsigned bufferSize +); + +typedef unsigned (* FAsyncSocketStartListening) ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +); + +typedef void (* FAsyncSocketStopListening) ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +); + +typedef void (* FAsyncSocketEnableNagling) ( + AsyncSocket conn, + bool enable +); + + + +/**************************************************************************** +* +* I/O API +* +***/ + +struct AsyncApi { + + // Init + FInitialize initialize; + FDestroy destroy; + FSignalShutdown signalShutdown; + FWaitForShutdown waitForShutdown; + FSleep sleep; + + // Files + FAsyncFileOpen fileOpen; + FAsyncFileClose fileClose; + FAsyncFileRead fileRead; + FAsyncFileWrite fileWrite; + FAsyncFileFlushBuffers fileFlushBuffers; + FAsyncFileSetLastWriteTime fileSetLastWriteTime; + FAsyncFileGetLastWriteTime fileGetLastWriteTime; + FAsyncFileCreateSequence fileCreateSequence; + FAsyncFileSeek fileSeek; + + // Sockets + FAsyncSocketConnect socketConnect; + FAsyncSocketConnectCancel socketConnectCancel; + FAsyncSocketDisconnect socketDisconnect; + FAsyncSocketDelete socketDelete; + FAsyncSocketSend socketSend; + FAsyncSocketWrite socketWrite; + FAsyncSocketSetNotifyProc socketSetNotifyProc; + FAsyncSocketSetBacklogAlloc socketSetBacklogAlloc; + FAsyncSocketStartListening socketStartListening; + FAsyncSocketStopListening socketStopListening; + FAsyncSocketEnableNagling socketEnableNagling; +}; + +extern AsyncApi g_api; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceCore.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceCore.cpp new file mode 100644 index 00000000..9cbc53c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceCore.cpp @@ -0,0 +1,230 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceCore.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private data +* +***/ + +static long s_perf[kNumAsyncPerfCounters]; + + +/**************************************************************************** +* +* Module data exports +* +***/ + +AsyncApi g_api; +bool s_transgaming; + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static void DoTransgamingCheck () { +#ifdef HS_BUILD_FOR_WIN32 +#ifdef CLIENT + + HMODULE hMod = GetModuleHandle("ntdll"); + if (!hMod) + return; + + s_transgaming = GetProcAddress(hMod, "IsTransgaming") != nil; + +#endif +#endif +} + +//=========================================================================== +static void IAsyncInitUseW9x () { + ref(IAsyncInitUseW9x); +#ifdef HS_BUILD_FOR_WIN32 + W9xGetApi(&g_api); +#else + ErrorFatal("W9x I/O Not supported on this platform"); +#endif +} + +//=========================================================================== +static void IAsyncInitUseNt () { + ref(IAsyncInitUseNt); +#ifdef HS_BUILD_FOR_WIN32 + NtGetApi(&g_api); +#else + ErrorFatal("Nt I/O Not supported on this platform"); +#endif +} + +//=========================================================================== +static void IAsyncInitUseUnix () { + ref(IAsyncInitUseUnix); +#ifdef HS_BUILD_FOR_UNIX + #error Unix I/O not implemented yet + UxGetApi(&g_api); +#else + ErrorFatal(__LINE__, __FILE__, "Unix I/O Not supported on this platform"); +#endif +} + +//=========================================================================== +static void IAsyncInitForClient () { + ref(IAsyncInitForClient); +#ifdef HS_BUILD_FOR_WIN32 + DoTransgamingCheck(); + if (s_transgaming) { + IAsyncInitUseW9x(); + } + else { + IAsyncInitUseNt(); + } +#elif HS_BUILD_FOR_UNIX + IAsyncInitUseUnix(); +#else + ErrorFatal("AsyncCore: No default implementation for this platform"); +#endif +} + +//=========================================================================== +static void IAsyncInitForServer () { + ref(IAsyncInitForServer); +#ifdef HS_BUILD_FOR_WIN32 + IAsyncInitUseNt(); +#elif HS_BUILD_FOR_UNIX + IAsyncInitUseUnix(); +#else + ErrorFatal("AsyncCore: No default implementation for this platform"); +#endif +} + + +/***************************************************************************** +* +* Module exports +* +***/ + +//============================================================================ +long PerfAddCounter (unsigned id, unsigned n) { + ASSERT(id < kNumAsyncPerfCounters); + return AtomicAdd(&s_perf[id], n); +} + +//============================================================================ +long PerfSubCounter (unsigned id, unsigned n) { + ASSERT(id < kNumAsyncPerfCounters); + return AtomicAdd(&s_perf[id], -(signed)n); +} + +//============================================================================ +long PerfSetCounter (unsigned id, unsigned n) { + ASSERT(id < kNumAsyncPerfCounters); + return AtomicSet(&s_perf[id], n); +} + + +/***************************************************************************** +* +* Public exports +* +***/ + +//=========================================================================== +void AsyncCoreInitialize () { + ASSERTMSG(!g_api.initialize, "AsyncCore already initialized"); + +#ifdef HS_BUILD_FOR_WIN32 + // Initialize WinSock + WSADATA wsaData; + if (WSAStartup(0x101, &wsaData)) + ErrorFatal(__LINE__, __FILE__, "WSA startup failed"); + if (wsaData.wVersion != 0x101) + ErrorFatal(__LINE__, __FILE__, "WSA version failed"); +#endif + +#ifdef CLIENT + IAsyncInitForClient(); +#elif SERVER + IAsyncInitForServer(); +#else +# error "Neither CLIENT nor SERVER defined. Cannot configure AsyncCore for target" +#endif + + ASSERT(g_api.initialize); + g_api.initialize(); +} + +//============================================================================ +void AsyncCoreDestroy (unsigned waitMs) { + if (g_api.destroy) { + g_api.destroy(waitMs); + } + + DnsDestroy(waitMs); + TimerDestroy(waitMs); + ThreadDestroy(waitMs); + + ZERO(g_api); +} + +//============================================================================ +void AsyncSignalShutdown () { + ASSERT(g_api.signalShutdown); + g_api.signalShutdown(); +} + +//============================================================================ +void AsyncWaitForShutdown () { + ASSERT(g_api.waitForShutdown); + g_api.waitForShutdown(); +} + +//============================================================================ +void AsyncSleep (unsigned sleepMs) { + ASSERT(g_api.sleep); + g_api.sleep(sleepMs); +} + +//============================================================================ +long AsyncPerfGetCounter (unsigned id) { + COMPILER_ASSERT(arrsize(s_perf) == kNumAsyncPerfCounters); + ASSERT(id < kNumAsyncPerfCounters); + return s_perf[id]; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceIo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceIo.cpp new file mode 100644 index 00000000..9f1c4083 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceIo.cpp @@ -0,0 +1,590 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceIo.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/**************************************************************************** +* +* ISocketConnHash +* +***/ + +// socket notification procedures + +// connection data format: +// byte connType; +// dword buildId; [optional] +// dword branchId; [optional] +// dword buildType; [optional] +// Uuid productId; [optional] +const unsigned kConnHashFlagsIgnore = 0x01; +const unsigned kConnHashFlagsExactMatch = 0x02; +struct ISocketConnHash { + unsigned connType; + unsigned buildId; + unsigned buildType; + unsigned branchId; + Uuid productId; + unsigned flags; + + unsigned GetHash () const; + bool operator== (const ISocketConnHash & rhs) const; +}; + +struct ISocketConnType : ISocketConnHash { + HASHLINK(ISocketConnType) hashlink; + FAsyncNotifySocketProc notifyProc; +}; + + +static CLock s_notifyProcLock; +static HASHTABLEDECL( + ISocketConnType, + ISocketConnHash, + hashlink +) s_notifyProcs; + + +//=========================================================================== +unsigned ISocketConnHash::GetHash () const { + CHashValue hash; + hash.Hash32(connType); +/* + if (buildId) + hash.Hash32(buildId); + if (buildType) + hash.Hash32(buildType); + if (branchId) + hash.Hash32(branchId); + if (productId != kNilGuid) + hash.Hash(&productId, sizeof(productId)); +*/ + return hash.GetHash(); +} + +//=========================================================================== +bool ISocketConnHash::operator== (const ISocketConnHash & rhs) const { + ASSERT(flags & kConnHashFlagsIgnore); + + for (;;) { + // Check connType + if (connType != rhs.connType) + break; + + // Check buildId + if (buildId != rhs.buildId) { + if (rhs.flags & kConnHashFlagsExactMatch) + break; + if (buildId) + break; + } + + // Check buildType + if (buildType != rhs.buildType) { + if (rhs.flags & kConnHashFlagsExactMatch) + break; + if (buildType) + break; + } + + // Check branchId + if (branchId != rhs.branchId) { + if (rhs.flags & kConnHashFlagsExactMatch) + break; + if (branchId) + break; + } + + // Check productId + if (productId != rhs.productId) { + if (rhs.flags & kConnHashFlagsExactMatch) + break; + if (productId != kNilGuid) + break; + } + + // Success! + return true; + } + + // Failed! + return false; +} + +//=========================================================================== +static unsigned GetConnHash ( + ISocketConnHash * hash, + const byte buffer[], + unsigned bytes +) { + if (!bytes) + return 0; + + if (IS_TEXT_CONNTYPE(buffer[0])) { + hash->connType = buffer[0]; + hash->buildId = 0; + hash->buildType = 0; + hash->branchId = 0; + hash->productId = 0; + hash->flags = 0; + + // one byte consumed + return 1; + } + else { + if (bytes < sizeof(AsyncSocketConnectPacket)) + return 0; + + const AsyncSocketConnectPacket & connect = * (const AsyncSocketConnectPacket *) buffer; + if (connect.hdrBytes < sizeof(connect)) + return 0; + + hash->connType = connect.connType; + hash->buildId = connect.buildId; + hash->buildType = connect.buildType; + hash->branchId = connect.branchId; + hash->productId = connect.productId; + hash->flags = 0; + + return connect.hdrBytes; + } +} + + +/**************************************************************************** +* +* Public exports +* +***/ + +//=========================================================================== +EFileError AsyncGetLastFileError () { + const unsigned error = GetLastError(); + switch (error) { + + case NO_ERROR: + return kFileSuccess; + + case ERROR_FILE_NOT_FOUND: + return kFileErrorFileNotFound; + + case ERROR_ACCESS_DENIED: + case ERROR_FILE_EXISTS: + case ERROR_ALREADY_EXISTS: + return kFileErrorAccessDenied; + + case ERROR_SHARING_VIOLATION: + return kFileErrorSharingViolation; + + case ERROR_BAD_NETPATH: + case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_NAME: + case ERROR_BAD_NET_NAME: + case ERROR_CANT_ACCESS_DOMAIN_INFO: + case ERROR_NETWORK_UNREACHABLE: + case ERROR_HOST_UNREACHABLE: + return kFileErrorPathNotFound; + } + + LogMsg(kLogPerf, "Unexpected Win32 error [%#x]", error); + + return kFileErrorPathNotFound; +} + +//============================================================================ +const wchar * FileErrorToString (EFileError error) { + + static wchar * s_fileErrorStrings[] = { + L"FileSuccess", + L"FileErrorInvalidParameter", + L"FileErrorFileNotFound", + L"FileErrorPathNotFound", + L"FileErrorAccessDenied", + L"FileErrorSharingViolation", + }; + COMPILER_ASSERT(kNumFileErrors == arrsize(s_fileErrorStrings)); + + return s_fileErrorStrings[error]; +} + +//============================================================================ +AsyncFile AsyncFileOpen ( + const wchar fullPath[], + FAsyncNotifyFileProc notifyProc, + EFileError * error, + unsigned desiredAccess, + unsigned openMode, + unsigned shareModeFlags, + void * userState, + qword * fileSize, + qword * fileLastWriteTime +) { + ASSERT(g_api.fileOpen); + return g_api.fileOpen( + fullPath, + notifyProc, + error, + desiredAccess, + openMode, + shareModeFlags, + userState, + fileSize, + fileLastWriteTime + ); +} + +//============================================================================ +void AsyncFileClose ( + AsyncFile file, + qword truncateSize +) { + ASSERT(g_api.fileClose); + g_api.fileClose(file, truncateSize); +} + +//============================================================================ +void AsyncFileSetLastWriteTime ( + AsyncFile file, + qword lastWriteTime +) { + ASSERT(g_api.fileSetLastWriteTime); + g_api.fileSetLastWriteTime(file, lastWriteTime); +} + +//============================================================================ +qword AsyncFileGetLastWriteTime ( + const wchar fileName[] +) { + ASSERT(g_api.fileGetLastWriteTime); + return g_api.fileGetLastWriteTime(fileName); +} + +//============================================================================ +AsyncId AsyncFileFlushBuffers ( + AsyncFile file, + qword truncateSize, + bool notify, + void * param +) { + ASSERT(g_api.fileFlushBuffers); + return g_api.fileFlushBuffers(file, truncateSize, notify, param); +} + +//============================================================================ +AsyncId AsyncFileRead ( + AsyncFile file, + qword offset, + void * buffer, + unsigned bytes, + unsigned flags, + void * param +) { + ASSERT(g_api.fileRead); + return g_api.fileRead( + file, + offset, + buffer, + bytes, + flags, + param + ); +} + +//============================================================================ +AsyncId AsyncFileWrite ( + AsyncFile file, + qword offset, + const void * buffer, + unsigned bytes, + unsigned flags, + void * param +) { + ASSERT(g_api.fileWrite); + return g_api.fileWrite( + file, + offset, + buffer, + bytes, + flags, + param + ); +} + +//============================================================================ +AsyncId AsyncFileCreateSequence ( + AsyncFile file, + bool notify, + void * param +) { + ASSERT(g_api.fileCreateSequence); + return g_api.fileCreateSequence(file, notify, param); +} + +//============================================================================ +bool AsyncFileSeek ( + AsyncFile file, + qword distance, + EFileSeekFrom seekFrom +) { + ASSERT(g_api.fileSeek); + return g_api.fileSeek(file, distance, seekFrom); +} + +//=========================================================================== +void AsyncSocketConnect ( + AsyncCancelId * cancelId, + const NetAddress & netAddr, + FAsyncNotifySocketProc notifyProc, + void * param, + const void * sendData, + unsigned sendBytes, + unsigned connectMs, + unsigned localPort +) { + ASSERT(g_api.socketConnect); + g_api.socketConnect( + cancelId, + netAddr, + notifyProc, + param, + sendData, + sendBytes, + connectMs, + localPort + ); +} + +//=========================================================================== +void AsyncSocketConnectCancel ( + FAsyncNotifySocketProc notifyProc, + AsyncCancelId cancelId +) { + ASSERT(g_api.socketConnectCancel); + g_api.socketConnectCancel(notifyProc, cancelId); +} + +//=========================================================================== +void AsyncSocketDisconnect ( + AsyncSocket sock, + bool hardClose +) { + ASSERT(g_api.socketDisconnect); + g_api.socketDisconnect(sock, hardClose); +} + +//=========================================================================== +void AsyncSocketDelete (AsyncSocket sock) { + + ASSERT(g_api.socketDelete); + g_api.socketDelete(sock); +} + +//=========================================================================== +bool AsyncSocketSend ( + AsyncSocket sock, + const void * data, + unsigned bytes +) { + ASSERT(g_api.socketSend); + return g_api.socketSend(sock, data, bytes); +} + +//=========================================================================== +bool AsyncSocketWrite ( + AsyncSocket sock, + const void * buffer, + unsigned bytes, + void * param +) { + ASSERT(g_api.socketWrite); + return g_api.socketWrite(sock, buffer, bytes, param); +} + +//=========================================================================== +void AsyncSocketSetNotifyProc ( + AsyncSocket sock, + FAsyncNotifySocketProc notifyProc +) { + ASSERT(g_api.socketSetNotifyProc); + g_api.socketSetNotifyProc(sock, notifyProc); +} + +//=========================================================================== +void AsyncSocketSetBacklogAlloc ( + AsyncSocket sock, + unsigned bufferSize +) { + ASSERT(g_api.socketSetBacklogAlloc); + g_api.socketSetBacklogAlloc(sock, bufferSize); +} + +//=========================================================================== +unsigned AsyncSocketStartListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +) { + ASSERT(g_api.socketStartListening); + return g_api.socketStartListening(listenAddr, notifyProc); +} + +//=========================================================================== +void AsyncSocketStopListening ( + const NetAddress & listenAddr, + FAsyncNotifySocketProc notifyProc +) { + ASSERT(g_api.socketStopListening); + g_api.socketStopListening(listenAddr, notifyProc); +} + +//============================================================================ +void AsyncSocketEnableNagling ( + AsyncSocket sock, + bool enable +) { + ASSERT(g_api.socketEnableNagling); + g_api.socketEnableNagling(sock, enable); +} + +//=========================================================================== +void AsyncSocketRegisterNotifyProc ( + byte connType, + FAsyncNotifySocketProc notifyProc, + unsigned buildId, + unsigned buildType, + unsigned branchId, + const Uuid & productId +) { + ASSERT(connType != kConnTypeNil); + ASSERT(notifyProc); + + // Perform memory allocation outside lock + ISocketConnType * ct = NEW(ISocketConnType); + ct->notifyProc = notifyProc; + ct->connType = connType; + ct->buildId = buildId; + ct->buildType = buildType; + ct->branchId = branchId; + ct->productId = productId; + ct->flags = kConnHashFlagsIgnore; + + s_notifyProcLock.EnterWrite(); + { + s_notifyProcs.Add(ct); + } + s_notifyProcLock.LeaveWrite(); +} + +//=========================================================================== +void AsyncSocketUnregisterNotifyProc ( + byte connType, + FAsyncNotifySocketProc notifyProc, + unsigned buildId, + unsigned buildType, + unsigned branchId, + const Uuid & productId +) { + ISocketConnHash hash; + hash.connType = connType; + hash.buildId = buildId; + hash.buildType = buildType; + hash.branchId = branchId; + hash.productId = productId; + hash.flags = kConnHashFlagsExactMatch; + + ISocketConnType * scan; + s_notifyProcLock.EnterWrite(); + { + scan = s_notifyProcs.Find(hash); + for (; scan; scan = s_notifyProcs.FindNext(hash, scan)) { + if (scan->notifyProc != notifyProc) + continue; + + // Unlink the object so it can be deleted outside the lock + s_notifyProcs.Unlink(scan); + break; + } + } + s_notifyProcLock.LeaveWrite(); + + // perform memory deallocation outside the lock + DEL(scan); +} + +//=========================================================================== +FAsyncNotifySocketProc AsyncSocketFindNotifyProc ( + const byte buffer[], + unsigned bytes, + unsigned * bytesProcessed, + unsigned * connType, + unsigned * buildId, + unsigned * buildType, + unsigned * branchId, + Uuid * productId +) { + for (;;) { + // Get the connType + ISocketConnHash hash; + *bytesProcessed = GetConnHash(&hash, buffer, bytes); + if (!*bytesProcessed) + break; + + // Lookup notifyProc based on connType + FAsyncNotifySocketProc proc; + s_notifyProcLock.EnterRead(); + if (const ISocketConnType * scan = s_notifyProcs.Find(hash)) + proc = scan->notifyProc; + else + proc = nil; + s_notifyProcLock.LeaveRead(); + if (!proc) + break; + + // Success! + *connType = hash.connType; + *buildId = hash.buildId; + *buildType = hash.buildType; + *branchId = hash.branchId; + *productId = hash.productId; + return proc; + } + + // Failure! + PerfAddCounter(kAsyncPerfSocketDisconnectInvalidConnType, 1); + *bytesProcessed = 0; + *connType = 0; + *buildId = 0; + *buildType = 0; + *branchId = 0; + *productId = 0; + return nil; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceLog.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceLog.cpp new file mode 100644 index 00000000..64d589c8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceLog.cpp @@ -0,0 +1,478 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceLog.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + +#if defined(PLASMA_EXTERNAL_RELEASE) && (BUILD_TYPE == BUILD_TYPE_LIVE) + // If this is an external live build then don't write log files + #define ACELOG_NO_LOG_FILES +#endif + + +namespace AsyncLog { + +/**************************************************************************** +* +* Private +* +***/ + +static const unsigned kLogFlushMs = 10 * 1000; + +enum ELogType { +#ifdef SERVER + kLogTypeDebug, + kLogTypePerf, + kLogTypeError, +#else + kLogTypeDebug, +#endif + kNumLogTypes +}; + +static bool s_breakOnErrors; +static wchar s_directory[MAX_PATH]; +static CCritSect s_logCrit[kNumLogTypes]; +static char * s_logBuf[kNumLogTypes]; +static unsigned s_logPos[kNumLogTypes]; +static qword s_logWritePos[kNumLogTypes]; +static TimeDesc s_logTime[kNumLogTypes]; +static unsigned s_logWriteMs[kNumLogTypes]; +static AsyncFile s_logFile[kNumLogTypes]; +static long s_opsPending; +static bool s_running; +static AsyncTimer * s_timer; + +static unsigned s_logSize[kNumLogTypes] = { +#ifdef SERVER + 64 * 1024, + 64 * 1024, + 8 * 1024, +#else + 64 * 1024, +#endif +}; + +static const wchar * s_logNameFmt[kNumLogTypes] = { +#ifdef SERVER + L"Dbg%02u%02u%02u.%s.log", + L"Inf%02u%02u%02u.%s.log", + L"Err%02u%02u%02u.%s.log", +#else + L"%s%02u%02u%02u.%s.log", +#endif +}; + +static ELogType s_logSeverityToType[kNumLogSeverity] = { +#ifdef SERVER + kLogTypeDebug, // kLogDebug + kLogTypePerf, // kLogPerf + kLogTypeError, // kLogError + kLogTypeError, // kLogFatal +#else + kLogTypeDebug, // kLogDebug + kLogTypeDebug, // kLogPerf + kLogTypeDebug, // kLogError + kLogTypeDebug, // kLogFatal +#endif +}; + +static char * s_logSeverityToText[kNumLogSeverity] = { + "Debug", + "Info", + "Error", + "Fatal", +}; + + +/**************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static void LogFileNotifyProc ( + AsyncFile file, + EAsyncNotifyFile code, + AsyncNotifyFile * notify, + void ** userState +) { + ref(file); + ref(userState); + + switch (code) { + case kNotifyFileWrite: + FREE(notify->param); + AtomicAdd(&s_opsPending, -1); + break; + + case kNotifyFileFlush: + AsyncFileClose(file, kAsyncFileDontTruncate); + AtomicAdd(&s_opsPending, -1); + break; + + DEFAULT_FATAL(code); + } +} + +//============================================================================ +static void AllocLogBuffer_CS (unsigned index) { + ref(AllocLogBuffer_CS); + + ASSERT(!s_logBuf[index]); + s_logBuf[index] = (char *)ALLOC(s_logSize[index]); + s_logPos[index] = 0; + + if (!s_logBuf[index]) + ErrorAssert(__LINE__, __FILE__, "Out of memory"); +} + +//============================================================================ +static void FreeLogBuffer_CS (unsigned index) { + ref(FreeLogBuffer_CS); + + if (s_logBuf[index]) { + FREE(s_logBuf[index]); + s_logBuf[index] = nil; + } +} + +//============================================================================ +static void GetLogFilename ( + unsigned index, + TimeDesc timeDesc, + wchar * filename, + unsigned chars +) { + StrPrintf( + filename, + chars, + s_logNameFmt[index], +#ifndef SERVER + ProductShortName(), +#endif + timeDesc.year % 100, + timeDesc.month, + timeDesc.day, + BuildTypeString() + ); + PathAddFilename(filename, s_directory, filename, chars); +} + +//============================================================================ +static bool OpenLogFile_CS (unsigned index) { + if (s_logFile[index] != nil) + return true; + + // Build filename + wchar filename[MAX_PATH]; + GetLogFilename( + index, + s_logTime[index], + filename, + arrsize(filename) + ); + + // Open file + qword fileTime; + EFileError fileError; + bool fileExist = PathDoesFileExist(filename); + s_logFile[index] = AsyncFileOpen( + filename, + LogFileNotifyProc, + &fileError, + kAsyncFileWriteAccess, + kAsyncFileModeOpenAlways, + kAsyncFileShareRead, + nil, // userState + &s_logWritePos[index], + &fileTime + ); + + if (s_logFile[index] == nil) + return false; + + TimeGetDesc(fileTime, &s_logTime[index]); + s_logWriteMs[index] = TimeGetMs(); + + // Seek to end of file + AsyncFileSeek(s_logFile[index], s_logWritePos[index], kFileSeekFromBegin); + + // If this is a new file, write Byte Order Mark + if (!fileExist) { + static const char s_bom[] = "\xEF\xBB\xBF"; + AsyncFileWrite( + s_logFile[index], + s_logWritePos[index], + s_bom, + arrsize(s_bom)- 1, + kAsyncFileRwSync, // perform blocking write + nil // param + ); + s_logWritePos[index] += arrsize(s_bom) - 1; + } + + // Write a sentinel in case there are multiple runs in one day + static const char s_logOpened[] = "Log Opened\r\n"; + AsyncFileWrite( + s_logFile[index], + s_logWritePos[index], + s_logOpened, + arrsize(s_logOpened)- 1, + kAsyncFileRwSync, // perform blocking write + nil + ); + s_logWritePos[index] += arrsize(s_logOpened) - 1; + + return true; +} + +//============================================================================ +static void WriteLogFile_CS (unsigned index, bool close) { + unsigned flags = kAsyncFileRwSync; // kAsyncFileRwNotify + if (s_logPos[index]) { + if (OpenLogFile_CS(index)) { + AsyncFileWrite( + s_logFile[index], + s_logWritePos[index], + s_logBuf[index], + s_logPos[index], + flags, + s_logBuf[index] + ); + if (flags == kAsyncFileRwSync) + DEL(s_logBuf[index]); + else + AtomicAdd(&s_opsPending, 1); + s_logWritePos[index] += s_logPos[index]; + s_logWriteMs[index] = TimeGetMs(); + s_logBuf[index] = nil; + s_logPos[index] = 0; + } + } + + if (close && s_logFile[index]) { + if (flags == kAsyncFileRwNotify) { + AtomicAdd(&s_opsPending, 1); + AsyncFileFlushBuffers( + s_logFile[index], + kAsyncFileDontTruncate, + true, + nil + ); + } + else { + AsyncFileClose( + s_logFile[index], + kAsyncFileDontTruncate + ); + } + s_logFile[index] = nil; + } +} + +//============================================================================ +static void FlushLogFile_CS ( + unsigned index, + TimeDesc timeDesc +) { + ref(FlushLogFile_CS); + + bool close = !s_running || (s_logTime[index].day != timeDesc.day); + WriteLogFile_CS(index, close); + if (close) + s_logTime[index] = timeDesc; +} + +//============================================================================ +static unsigned FlushLogsTimerCallback (void *) { + ref(FlushLogsTimerCallback); + + AsyncLogFlush(); + return kAsyncTimeInfinite; +} + + +} using namespace AsyncLog; + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//============================================================================ +void AsyncLogInitialize ( + const wchar logDirName[], + bool breakOnErrors +) { + s_running = true; + + // Save options + s_breakOnErrors = breakOnErrors; + + // Build log directory name +#ifdef SERVER + PathGetProgramDirectory(s_directory, arrsize(s_directory)); +#else + PathGetUserDirectory(s_directory, arrsize(s_directory)); +#endif + PathAddFilename(s_directory, s_directory, logDirName, arrsize(s_directory)); + +#ifndef ACELOG_NO_LOG_FILES + // Create log directory + if (kPathCreateDirSuccess != PathCreateDirectory(s_directory, 0)) + PathRemoveFilename(s_directory, s_directory, arrsize(s_directory)); + + // Allocate log buffers + for (unsigned index = 0; index < kNumLogTypes; ++index) { + s_logCrit[index].Enter(); + AllocLogBuffer_CS(index); + s_logCrit[index].Leave(); + } + + AsyncTimerCreate(&s_timer, FlushLogsTimerCallback, kAsyncTimeInfinite, nil); +#endif // ndef ACELOG_NO_LOG_FILES +} + +//============================================================================ +void AsyncLogDestroy () { + s_running = false; + +#ifndef ACELOG_NO_LOG_FILES + AsyncTimerDelete(s_timer, kAsyncTimerDestroyWaitComplete); + + for (unsigned index = 0; index < kNumLogTypes; ++index) { + s_logCrit[index].Enter(); + { + WriteLogFile_CS(index, true); + FreeLogBuffer_CS(index); + } + s_logCrit[index].Leave(); + } + while (s_opsPending) + AsyncSleep(10); +#endif // ndef ACELOG_NO_LOG_FILES +} + +//============================================================================ +void AsyncLogFlush () { +#ifndef ACELOG_NO_LOG_FILES + TimeDesc timeDesc; + TimeGetDesc(TimeGetTime(), &timeDesc); + + for (unsigned index = 0; index < kNumLogTypes; ++index) { + s_logCrit[index].Enter(); + FlushLogFile_CS(index, timeDesc); + s_logCrit[index].Leave(); + } +#endif // ndef ACELOG_NO_LOG_FILES +} + +//============================================================================ +void LogBreakOnErrors (bool breakOnErrors) { + s_breakOnErrors = breakOnErrors; +} + +//============================================================================ +void AsyncLogWriteMsg ( + const wchar facility[], + ELogSeverity severity, + const wchar msg[] +) { + ref(facility); + ref(severity); + ref(msg); + + if (!s_running) + return; + +#ifndef ACELOG_NO_LOG_FILES + TimeDesc timeDesc; + TimeGetDesc(TimeGetTime(), &timeDesc); + + char buffer[2048]; + const unsigned chars = StrPrintf( + buffer, + arrsize(buffer), + "%02u/%02u/%02u % 2u:%02u:%02u [%S] %s %S\r\n", + timeDesc.month, + timeDesc.day, + timeDesc.year % 100, + timeDesc.hour, + timeDesc.minute, + timeDesc.second, + facility, + s_logSeverityToText[severity], + msg + ); + + unsigned index = s_logSeverityToType[severity]; + s_logCrit[index].Enter(); + { + // If day changed then write and flush file + if (s_logTime[index].day != timeDesc.day) + FlushLogFile_CS(index, timeDesc); + // Otherwise if the buffer is full then write to file + else if (s_logPos[index] + chars > s_logSize[index]) + WriteLogFile_CS(index, false); + + // Allocate log buffer if necessary + if (!s_logBuf[index]) + AllocLogBuffer_CS(index); + + // Add new data to the log buffer + MemCopy(s_logBuf[index] + s_logPos[index], buffer, chars); + s_logPos[index] += chars; + + // Write, flush and close file immediately if this is a fatal error + if (severity == kLogFatal) + WriteLogFile_CS(index, true); + + // Drop to debugger if this is an error msg and that option was specified + if (s_breakOnErrors && severity >= kLogError) + DebugBreakIfDebuggerPresent(); + } + s_logCrit[index].Leave(); + + // Queue flush + AsyncTimerUpdate(s_timer, kLogFlushMs, kAsyncTimerUpdateSetPriorityHigher); +#endif // ndef ACELOG_NO_LOG_FILES +} + +//============================================================================ +void AsyncLogGetDirectory (wchar * dest, unsigned destChars) { + ASSERT(dest); + StrCopy(dest, s_directory, destChars); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceThread.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceThread.cpp new file mode 100644 index 00000000..95adca47 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceThread.cpp @@ -0,0 +1,120 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceThread.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private data +* +***/ + + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +static unsigned CALLBACK CreateThreadProc (LPVOID param) { + PerfAddCounter(kAsyncPerfThreadsTotal, 1); + PerfAddCounter(kAsyncPerfThreadsCurr, 1); + + // Initialize thread + AsyncThread * thread = (AsyncThread *) param; + + // Call thread procedure + unsigned result = thread->proc(thread); + + // Cleanup thread + DEL(thread); + + PerfSubCounter(kAsyncPerfThreadsCurr, 1); + return result; +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void ThreadDestroy (unsigned exitThreadWaitMs) { + + unsigned bailAt = TimeGetMs() + exitThreadWaitMs; + while (AsyncPerfGetCounter(kAsyncPerfThreadsCurr) && signed(bailAt - TimeGetMs()) > 0) + AsyncSleep(10); +} + + +/***************************************************************************** +* +* Public exports +* +***/ + +//=========================================================================== +void * AsyncThreadCreate ( + FAsyncThreadProc threadProc, + void * argument, + const wchar name[] +) { + AsyncThread * thread = NEW(AsyncThread); + thread->proc = threadProc; + thread->handle = nil; + thread->argument = argument; + thread->workTimeMs = kAsyncTimeInfinite; + StrCopy(thread->name, name, arrsize(thread->name)); + + // Create thread suspended + unsigned threadId; + HANDLE handle = (HANDLE) _beginthreadex( + (LPSECURITY_ATTRIBUTES) 0, + 0, // stack size + CreateThreadProc, + thread, // argument + 0, // initFlag + &threadId + ); + if (!handle) { + LogMsg(kLogFatal, "%s (%u)", __FILE__, GetLastError()); + ErrorFatal(__LINE__, __FILE__, "_beginthreadex failed"); + } + + thread->handle = handle; + return handle; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceTimer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceTimer.cpp new file mode 100644 index 00000000..7cab20aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceTimer.cpp @@ -0,0 +1,352 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceTimer.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/**************************************************************************** +* +* Private +* +***/ + +// timer callbacks +struct AsyncTimer { + PRIORITY_TIME(AsyncTimer) priority; + FAsyncTimerProc timerProc; + FAsyncTimerProc destroyProc; + void * param; + LINK(AsyncTimer) deleteLink; +}; + +static CCritSect s_timerCrit; +static FAsyncTimerProc s_timerCurr; +static HANDLE s_timerThread; +static HANDLE s_timerEvent; +static bool s_running; + +static PRIQDECL( + AsyncTimer, + PRIORITY_TIME(AsyncTimer), + priority +) s_timerProcs; + +static LISTDECL( + AsyncTimer, + deleteLink +) s_timerDelete; + + +/**************************************************************************** +* +* Timer implementation +* +***/ + +//=========================================================================== +static void UpdateTimer ( + AsyncTimer * timer, + unsigned timeMs, + unsigned flags +) { + // If the timer isn't already linked then it doesn't + // matter whether kAsyncTimerUpdateSetPriorityHigher is + // set; just add the timer to the queue + if (!timer->priority.IsLinked()) { + timer->priority.Set(timeMs); + s_timerProcs.Enqueue(timer); + } + else if (((flags & kAsyncTimerUpdateSetPriorityHigher) == 0) + || !timer->priority.IsPriorityHigher(timeMs) + ) { + timer->priority.Set(timeMs); + } +} + +//=========================================================================== +static unsigned CallTimerProc (AsyncTimer * t, FAsyncTimerProc timerProc) { + // Cache parameters to make timer callback outside critical section + s_timerCurr = timerProc; + + // Leave critical section to make timer callback + s_timerCrit.Leave(); + + unsigned sleepMs = s_timerCurr(t->param); + s_timerCurr = nil; + + s_timerCrit.Enter(); + + return sleepMs; +} + +//=========================================================================== +// inline because it is called only once +static inline unsigned RunTimers () { + unsigned currTimeMs = TimeGetMs(); + for (;;) { + // Delete old timers + while (AsyncTimer * t = s_timerDelete.Head()) { + if (t->destroyProc) + CallTimerProc(t, t->destroyProc); + DEL(t); + } + + // Get first timer to run + AsyncTimer * t = s_timerProcs.Root(); + if (!t) + return INFINITE; + + // If it isn't time to run this timer then exit + unsigned sleepMs; + if (0 < (signed) (sleepMs = (unsigned) t->priority.Get() - currTimeMs)) + return sleepMs; + + // Remove from timer queue and call timer + s_timerProcs.Dequeue(); + sleepMs = CallTimerProc(t, t->timerProc); + + // Note if return is kAsyncTimeInfinite, we do not remove the timer + // from the queue. Some users depend on the fact that they can + // call AsyncTimerUpdate and not get overridden by a return from the + // handler at the same time. + + // Requeue timer + currTimeMs = TimeGetMs(); + if (sleepMs != kAsyncTimeInfinite) + UpdateTimer(t, sleepMs + currTimeMs, kAsyncTimerUpdateSetPriorityHigher); + } +} + +//=========================================================================== +static unsigned THREADCALL TimerThreadProc (AsyncThread *) { + do { + s_timerCrit.Enter(); + const unsigned sleepMs = RunTimers(); + s_timerCrit.Leave(); + + WaitForSingleObject(s_timerEvent, sleepMs); + } while (s_running); + return 0; +} + +//=========================================================================== +// inline because it is called only once +static inline void InitializeTimer () { + if (!s_timerThread) { + s_running = true; + + s_timerEvent = CreateEvent( + (LPSECURITY_ATTRIBUTES) nil, + false, // auto-reset event + false, // initial state = off + (LPCTSTR) nil + ); + if (!s_timerEvent) + ErrorFatal(__LINE__, __FILE__, "CreateEvent %u", GetLastError()); + + s_timerThread = (HANDLE) AsyncThreadCreate( + TimerThreadProc, + nil, + L"AsyncTimerThread" + ); + } +} + + +/**************************************************************************** +* +* Module functions +* +***/ + +//=========================================================================== +void TimerDestroy (unsigned exitThreadWaitMs) { + s_running = false; + + if (s_timerThread) { + SetEvent(s_timerEvent); + WaitForSingleObject(s_timerThread, exitThreadWaitMs); + CloseHandle(s_timerThread); + s_timerThread = nil; + } + + if (s_timerEvent) { + CloseHandle(s_timerEvent); + s_timerEvent = nil; + } + + // Cleanup any timers that have been stopped but not deleted + s_timerCrit.Enter(); + while (AsyncTimer * t = s_timerDelete.Head()) { + if (t->destroyProc) + CallTimerProc(t, t->destroyProc); + DEL(t); + } + s_timerCrit.Leave(); + + if (AsyncTimer * timer = s_timerProcs.Root()) + ErrorFatal(__LINE__, __FILE__, "TimerProc not destroyed: %p", timer->timerProc); +} + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +// 1. Timer procs do not get starved by I/O, they are called periodically. +// 2. Timer procs will never be called by multiple threads simultaneously. +void AsyncTimerCreate ( + AsyncTimer ** timer, + FAsyncTimerProc timerProc, + unsigned callbackMs, + void * param +) { + ASSERT(timer); + ASSERT(timerProc); + + // Allocate timer outside critical section + AsyncTimer * t = NEW(AsyncTimer); + t->timerProc = timerProc; + t->destroyProc = nil; + t->param = param; + t->priority.Set(TimeGetMs() + callbackMs); + + // Set result pointer before queueing timer + // so that the value is set before a callback + *timer = t; + + bool setEvent; + s_timerCrit.Enter(); + { + InitializeTimer(); + + // Does this timer need to be queued? + if (callbackMs != kAsyncTimeInfinite) + s_timerProcs.Enqueue(t); + + // Does the timer thread need to be awakened? + setEvent = t == s_timerProcs.Root(); + } + s_timerCrit.Leave(); + + if (setEvent) + SetEvent(s_timerEvent); +} + +//=========================================================================== +// Timer procs can be in the process of getting called in +// another thread during the unregister function -- be careful! +// -- waitComplete = will wait until the timer has been unregistered and is +// no longer in the process of being called before returning. The flag may only +// be set by init/destruct threads, not I/O worker threads. In addition, extreme +// care should be used to avoid a deadlock when this flag is set; in general, it +// is a good idea not to hold any locks or critical sections when setting the flag. +void AsyncTimerDelete ( + AsyncTimer * timer, + unsigned flags +) { + // If the timer has already been destroyed then exit + ASSERT(timer); + + // Wait for timer before exiting function? + FAsyncTimerProc timerProc; + if (flags & kAsyncTimerDestroyWaitComplete) + timerProc = timer->timerProc; + else + timerProc = nil; + + AsyncTimerDeleteCallback(timer, nil); + + // Wait until the timer procedure completes + if (timerProc) { + // ensure that I/O worker threads don't call this function with waitComplete=true + // to prevent a possible deadlock of a timer callback waiting for itself to complete + ThreadAssertCanBlock(__FILE__, __LINE__); + + while (s_timerCurr == timerProc) + Sleep(1); + } +} + +//=========================================================================== +void AsyncTimerDeleteCallback ( + AsyncTimer * timer, + FAsyncTimerProc destroyProc +) { + // If the timer has already been destroyed then exit + ASSERT(timer); + ASSERT(!timer->deleteLink.IsLinked()); + + // Link the timer to the deletion list + s_timerCrit.Enter(); + { + timer->destroyProc = destroyProc; + s_timerDelete.Link(timer); + } + s_timerCrit.Leave(); + + // Force the timer thread to wake up and perform the deletion + if (destroyProc) + SetEvent(s_timerEvent); +} + +//=========================================================================== +// To set the time value for a timer, use this function with flags = 0. +// To set the time to MoreRecentOf(nextTimerCallbackMs, callbackMs), use SETPRIORITYHIGHER +void AsyncTimerUpdate ( + AsyncTimer * timer, + unsigned callbackMs, + unsigned flags +) { + ASSERT(timer); + + bool setEvent; + s_timerCrit.Enter(); + { + if (callbackMs != kAsyncTimeInfinite) { + UpdateTimer(timer, callbackMs + TimeGetMs(), flags); + setEvent = timer == s_timerProcs.Root(); + } + else { + if ((flags & kAsyncTimerUpdateSetPriorityHigher) == 0) + timer->priority.Unlink(); + setEvent = false; + } + } + s_timerCrit.Leave(); + + if (setEvent) + SetEvent(s_timerEvent); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/Pch.h new file mode 100644 index 00000000..afa9e13b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/Pch.h @@ -0,0 +1,38 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCrash/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCRASH_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnCrash/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCRASH_PCH_H + + +#include "pnUtils/pnUtils.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.bat b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.bat new file mode 100644 index 00000000..06217a45 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.bat @@ -0,0 +1,6 @@ +@echo off +:Top + echo Running pnCrash.py + C:\Python23\python pnCrash.py + Sleep 1 +goto :Top diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.cpp new file mode 100644 index 00000000..b9257dfb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.h new file mode 100644 index 00000000..50b6a890 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.h @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCRASH_PNCRASH_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCRASH_PNCRASH_H + + +/***************************************************************************** +* +* Crash API +* +***/ + +void CrashExceptionDump (const char occasion[], void * info); + +void CrashSetEmailParameters ( + const char smtp[], + const char sender[], + const char recipients[], // separate multiple recipients with semicolons + const char username[], // optional (see auth notes in pnMail.h) + const char password[], // optional (see auth notes in pnMail.h) + const char replyTo[] // optional +); + +void * CrashAddModule ( + unsigned_ptr address, + unsigned buildId, + unsigned branchId, + const wchar name[], + const wchar buildString[] +); + +void CrashRemoveModule ( + void * module +); + + +/***************************************************************************** +* +* Deadlock detection (server only) +* +***/ + +#ifdef SERVER +void * CrashAddDeadlockCheck (void * thread, const wchar debugstr[] ); +void CrashRemoveDeadlockCheck (void * check); +void CrashSetDeadlockCheckTimes (unsigned emailSec, unsigned terminateSec); +// returns previous setting +bool CrashEnableDeadlockChecking (bool enable); +void CrashDeadlockCheckNow (); +void CrashSendThreadReport (); +#endif + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCRASH_PNCRASH_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.py b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.py new file mode 100644 index 00000000..e1483655 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrash/pnCrash.py @@ -0,0 +1,453 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +import sys, os, re, poplib, smtplib, time + + +#***************************************************************************** +#* +#* Private data +#* +#*** + +s_pop3Srv = 'catherine.cyan.com' +s_pop3User = 'crashhandler' +s_pop3Pass = 'crashhandler' + +s_smtpSrv = 'catherine.cyan.com' +#s_smtpRecipients = ['eric@cyan.com'] +s_smtpRecipients = ['crash2@cyan.com'] + +#s_mapFileBasePath = 'P:\\Plasma20\\Scripts\\Server\\Admin\\'; +s_mapFileBasePath = '\\\\dirtcake\\MapFiles\\'; + +s_modules = {} +s_mapfiles = {} +s_sortedModuleKeys = [] + + +#***************************************************************************** +#* +#* Local functions +#* +#*** + +#============================================================================= +def UndecorateCppName (name): + name = re.sub('^\?', '', name, 1) + name = re.sub('^_', '', name, 1) + name = re.sub('@', ' ', name) + name = re.sub('\s+', ' ', name) + return name + +#============================================================================= +# returns index of the first entry for which 'eval' returns false +def bsearch (a, eval): + low = 0 + high = len(a) + + if low != high: + while True: + mid = (low + high) // 2 + result = eval(a[mid]) + if result > 0: + if mid == low: + break + low = mid + else: + high = mid + if mid == low: + break + return high + + +#***************************************************************************** +#* +#* Function class +#* +#*** + +class Function: + + #========================================================================= + def __init__ (self, name, baseAddr, relocAddr, module): + self.name = UndecorateCppName(name) + self.baseAddr = baseAddr + self.relocAddr = relocAddr + self.module = module + #print "Function: %08x,%s,%s" % (self.relocAddr, self.module, self.name) + + +#***************************************************************************** +#* +#* Mapfile class +#* +#*** + +class Mapfile: + #========================================================================= + def __init__ (self, filename): + self.filename = filename + self.prefLoadAddr = 0 + self.functions = {} + self.sortedFunctionKeys = [] + + try: + file = open(filename, 'rb') +# print "mapfile opened " + filename + lines = file.readlines() + file.close() + self.ParseMapfile(lines) + except IOError: +# print 'mapfile not found: ' + filename + pass + + #========================================================================= + def ParseMapfile (self, lines): + # Preferred load address is xxxxxxxx + pattern = re.compile('\s*Preferred.*([\dA-Fa-f]{8})') + for line in lines: + match = pattern.match(line) + if not match: + continue + self.prefLoadAddr = int(match.group(1), 16) + break + + # section:baseAddr functionName relocAddr flags module + pattern = re.compile('^\s?([\dA-Fa-f]{4}:[\dA-Fa-f]{8})\s+(\S+)\s+([\dA-Fa-f]{8})\s(.)\s.\s(\S+)') + for line in lines: + match = pattern.match(line) + if not match: + continue + baseAddr = match.group(1) + name = match.group(2) + relocAddr = int(match.group(3), 16) + if match.group(4) != 'f': + continue + module = match.group(5) + if self.functions.has_key(relocAddr): +# print " duplicate: %s %08x %s" % (baseAddr, relocAddr, name) +# print " with : %s %08x %s" % (self.functions[relocAddr].baseAddr, self.functions[relocAddr].relocAddr, self.functions[relocAddr].name) + pass + else: + self.functions[relocAddr] = Function(name, baseAddr, relocAddr, module) + + self.sortedFunctionKeys = self.functions.keys() + self.sortedFunctionKeys.sort() +# print " function count: %u" % (len(self.sortedFunctionKeys)) + + #========================================================================= + def FindNearestFunction (self, relocAddr): + + #===================================================================== + def eval (a, b=relocAddr): + return b > a + + index = bsearch(self.sortedFunctionKeys, eval) + if index > 0 and index < len(self.sortedFunctionKeys): + return self.functions[self.sortedFunctionKeys[index-1]] + return Function(" relocAddr %x" % (relocAddr), 0, relocAddr, self.filename) + +#============================================================================= +def LoadMapfile (mapfilename): + global s_mapfiles + if not s_mapfiles.has_key(mapfilename): + s_mapfiles[mapfilename] = Mapfile(mapfilename) + return s_mapfiles[mapfilename] + + +#***************************************************************************** +#* +#* Module class +#* +#*** + +class Module: + #========================================================================= + def __init__ (self, loadAddr, name, buildId, buildMark): +# print 'module: %s:%u, loadaddr: %08x' % (name, buildId, loadAddr) + self.loadAddr = loadAddr + self.name = name + self.buildMark = buildMark + self.buildId = buildId + mapfilename = s_mapFileBasePath + buildMark + '\\' + name.split('.')[0].split('_')[0] + '.map' + self.mapfile = LoadMapfile(mapfilename) + + #========================================================================= + def FindNearestFunction (self, trueAddr): + relocAddr = trueAddr + self.mapfile.prefLoadAddr - self.loadAddr +# print 'searching for function near Pc:%08x, Ra:%08x in %s' % (trueAddr, relocAddr, self.mapfile.filename) + function = self.mapfile.FindNearestFunction(relocAddr) + if function: +# print " found function %s, Ra:%08x, %s" % (function.baseAddr, function.relocAddr, function.name) + return function + print "Function not found for Pc:%08x, Ra:%08x in %s" % (trueAddr, relocAddr, self.mapfile.filename) + return None + + +#============================================================================= +def FindNearestModule (loadAddr): + + #========================================================================= + def eval (a, b=loadAddr): + return b > a + + global s_modules + global s_sortedModuleKeys + + if len(s_sortedModuleKeys) == 0: + return None + + index = bsearch(s_sortedModuleKeys, eval) + if index > 0 and index <= len(s_sortedModuleKeys): + return s_modules[s_sortedModuleKeys[index-1]] + + print "Module not found for loadAddr %08x" % (loadAddr) + return Module(loadAddr, " loadAddr %x" %(loadAddr), 0, "") + + + +#***************************************************************************** +#* +#* pnCrash class +#* +#*** + +class pnCrash: + #========================================================================= + def SendCrash (self, msgLines, smtp): + pass + + #========================================================================= + def GetModules (self, msgLines): + global s_modules + global s_sortedModuleKeys + # Parse modules and open mapfiles + pattern = re.compile('^([\dA-Fa-f]{8})\s(.*)\((\d+)\.\d+\.(.*)\)') + for line in msgLines: + match = pattern.match(line) + if not match: + continue + loadAddr = int(match.group(1), 16) + filename = os.path.basename(match.group(2)).strip() + buildId = int(match.group(3)) + buildMark = match.group(4) + #assert not s_modules.has_key(loadAddr) + s_modules[loadAddr] = Module(loadAddr, filename, buildId, buildMark) + s_sortedModuleKeys = s_modules.keys() + s_sortedModuleKeys.sort() + + # if no modules found, use the reporting application's info + if len(s_sortedModuleKeys) == 0: + buildId = 0 + loadAddr = int('00400000', 16) # temp hack + patternApp = re.compile('^App\s+:\s+(.*)') + patternBuildMark = re.compile('^BuildMark\s+:\s+(.*)') + for line in msgLines: + if patternApp: + match = patternApp.match(line) + if match: + patternApp = None + filename = os.path.basename(match.group(1)).strip().strip('\r\n') + continue + else: + match = patternBuildMark.match(line) + if match: + buildMark = match.group(1).strip().strip('\r\n') +# print 'No modules found, adding %s, %08x, %u, %s' % (filename, loadAddr, buildId, buildMark) + s_modules[loadAddr] = Module(loadAddr, filename, buildId, buildMark) + s_sortedModuleKeys = s_modules.keys() + s_sortedModuleKeys.sort() + break + + + #========================================================================= + def ConvertTraces (self, msgLines): + lines = [] + callstack = [] + intrace = False + + pattern = re.compile('Pc:([\dA-Fa-f]{8})\s+Fr:[\dA-Fa-f]{8}\s+Rt:([\dA-Fa-f]{8})') + for rawline in msgLines: + line = rawline.strip('\r\n') + if line.find('|') >= 0: # a previous conversion, skip over it + continue + lines.append(line) + match = pattern.match(line) + if not match: + if intrace: + callstack.append('') + lines += callstack + callstack = [] + lines.append('') + intrace = False + continue + if not intrace: + callstack.append('|--------> Callstack <--------') + intrace = True + returnAddr = int(match.group(2), 16) + if not returnAddr: + continue + programCounter = int(match.group(1), 16) + module = FindNearestModule(programCounter) + function = module.FindNearestFunction(programCounter) + callstack.append('| %s, %s:%s' % (function.name, function.module, module.name)) + + if intrace: + callstack.append('') + lines += callstack + callstack = [] + intrace = False + + return lines + + + #========================================================================= + def ProcessClientCrash(self, msgLines): + lines = [] + fd = open('untranslated.txt', 'w') + for x in msgLines: + fd.write(x) + fd.write('\n') + fd.close() + os.spawnl(os.P_WAIT, 'plStackTrace.exe', 'plStackTrace.exe', 'untranslated.txt') + fd = open('out.txt', 'r') + lines = fd.readlines() + return lines + + + #========================================================================= + def ProcessCrash (self, msgLines, smtp): + global s_modules + global s_sortedModuleKeys + + subject = '[Crash]' + pattern = re.compile('^Subject: (.*)$') + serverPattern = re.compile('App : plServer.exe') + serverCrash = 0 + + for line in msgLines: + match = serverPattern.match(line) + if match: + serverCrash = 1 + break + + if not serverCrash: + subject = 'Client Crash Report' + + for line in msgLines: + match = pattern.match(line) + if match: + subject = match.group(1) + subject.strip('\r\n') + + from_ = 'crash2@cyan.com' + pattern = re.compile('^From: (.*)$') + for line in msgLines: + match = pattern.match(line) + if match: + from_ = match.group(1) + from_.strip('\r\n') + break + + print "Rcvd: %s from %s" % (subject, from_) + + smtp.helo() + smtp.mail(from_, []) + for rcpt in s_smtpRecipients: + smtp.rcpt(rcpt, []) + + lines = [] + lines.append('From: %s' % (from_)) + lines.append('Subject: %s' % (subject)) + lines.append('') + + if not serverCrash: + lines += self.ProcessClientCrash(msgLines) # process client callstack + for x in range(len(lines)): + lines[x] = lines[x].strip('\r\n') + + if serverCrash: + s_modules.clear() + s_sortedModuleKeys = [] + self.GetModules(msgLines) + lines += self.ConvertTraces(msgLines) + s_sortedModuleKeys = [] + s_modules.clear() + + msg = '\n'.join([line for line in lines]) + smtp.data(msg) + print "Sent: %s from %s" % (subject, from_) + + + #========================================================================= + def GetAndProcessCrashes (self): + global s_mapfiles + s_mapfiles.clear() + +# print "initializing pop3" + pop3 = poplib.POP3(s_pop3Srv) + pop3.user(s_pop3User) + pop3.pass_(s_pop3Pass) + pop3.list() + (numMsgs, totalSize) = pop3.stat() + + if numMsgs > 0: +# print "initializing smtp" + smtp = smtplib.SMTP(s_smtpSrv) + +# print "retrieving emails" + for i in range(1, numMsgs + 1): + (header, msg, octets) = pop3.retr(i) + self.ProcessCrash(msg, smtp) + pop3.dele(i) + + smtp.quit() + + pop3.quit() + + s_mapfiles.clear() + + #========================================================================= + def RunForever (self): + global s_modules + s_modules[0] = Module(0, "", 0, "") + while 1: + self.GetAndProcessCrashes() + # sleep for an interruptable minute + for i in range(1, 20): + time.sleep(1) + + + +#***************************************************************************** +#* +#* Program entry point +#* +#*** + +#============================================================================= +if __name__ == '__main__': + pnCrash().RunForever() diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Pch.h new file mode 100644 index 00000000..9fcf439d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Pch.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCRASHEXE_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCRASHEXE_PCH_H + + +#include "pnUtils/pnUtils.h" +#include "pnProduct/pnProduct.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" +#include "pnMail/pnMail.h" + +#include +#include + +#ifdef HS_BUILD_FOR_WIN32 +#include "Win32/W32Int.h" +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/W32Int.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/W32Int.h new file mode 100644 index 00000000..4e910dc6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/W32Int.h @@ -0,0 +1,131 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCRASHEXE_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCRASHEXE_INTERN_H + + +namespace Crash { + + +/***************************************************************************** +* +* Mail +* +***/ + +void CrashSendEmail ( + const char smtp[], + const char sender[], + const char recipients[], + const char replyTo[], + const char username[], + const char password[], + const char programName[], + const char errorType[], + const char logBuffer[] +); + + + +/***************************************************************************** +* +* ImgHlp +* +***/ + +// make our own IMAGEAPI type because the real definition uses DECLSPEC_IMPORT +#define MYIMAGEAPI __stdcall + +class CImageHelp { +private: + HMODULE m_libInst; + HANDLE m_process; + char * m_appName; + char m_appPath[MAX_PATH]; + +public: + CImageHelp (HINSTANCE instance); + ~CImageHelp (); + + inline HANDLE Process () const { + return m_process; + } + + inline const char * GetProgramPath () const { + return m_appPath; + } + + inline const char * GetProgramName () const { + return m_appName; + } + + BOOL (MYIMAGEAPI * SymGetModuleInfo)( + HANDLE hProcess, + DWORD dwAddr, + IMAGEHLP_MODULE * ModuleInfo + ); + + BOOL (MYIMAGEAPI * StackWalk)( + DWORD MachineType, + HANDLE hProcess, + HANDLE hThread, + LPSTACKFRAME StackFrame, + LPVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, + PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE TranslateAddress + ); + + LPVOID (MYIMAGEAPI * SymFunctionTableAccess)( + HANDLE hProcess, + DWORD AddrBase + ); + + DWORD (MYIMAGEAPI * SymGetModuleBase)( + HANDLE hProcess, + DWORD dwAddr + ); + + BOOL (MYIMAGEAPI * SymGetSymFromAddr)( + HANDLE hProcess, + DWORD Address, + LPDWORD Displacement, + PIMAGEHLP_SYMBOL Symbol + ); +}; + + + +} using namespace Crash; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreError.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreError.cpp new file mode 100644 index 00000000..9d806e8c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreError.cpp @@ -0,0 +1,1067 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/pnCreError.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +namespace Crash { +/***************************************************************************** +* +* Private +* +***/ + +struct Module { + LINK(Module) link; + + unsigned_ptr address; + unsigned buildId; + unsigned branchId; + wchar * name; + wchar * buildString; + + ~Module () { FREE(name); FREE(buildString); } +}; + +struct EmailParams : AtomicRef { + char * smtp; + char * sender; + char * recipients; + char * username; + char * password; + char * replyTo; + + ~EmailParams () { + FREE(smtp); + FREE(sender); + FREE(recipients); + FREE(username); + FREE(password); + FREE(replyTo); + } +}; + +struct ErrLog { + unsigned pos; + wchar name[MAX_PATH]; + char buffer[512*1024]; + char terminator; +}; + +struct DeadlockCheck { + LINK(DeadlockCheck) link; + HANDLE thread; + unsigned deadlockEmailMs; + unsigned deadlockTerminateMs; + bool deadlocked; + bool emailSent; + wchar debugStr[256]; +}; + + +/***************************************************************************** +* +* Private data +* +***/ + +// Assertion results from ProcessErrorLog() +const unsigned kErrFlagAssertionBreakpoint = 0x01; +const unsigned kErrFlagAssertionExitProgram = 0x02; + +// Exception results from ProcessErrorLog() +const unsigned kErrFlagExceptionBreakpoint = 0x04; +const unsigned kErrFlagExceptionExecHandler = 0x08; + +static const char s_unknown[] = "*unknown*"; +static const char s_sectionFmt_s[] = "--------> %s <--------\r\n"; + +static const unsigned kStackDumpBytes = 1024; +static const unsigned kCodeDumpBytes = 64; + +static unsigned s_deadlockEmailMs; // sends an email if a thread exceeds this time. If set to zero this is disabled +static unsigned s_deadlockTerminateMs; // kills the process if a thread exceeds this time. If set to zero this is disabled +static CCritSect * s_critsect; +static LISTDECL(Module, link) s_modules; +static EmailParams * s_params; +static bool s_running; + +static bool s_deadlockEnabled; +static unsigned s_nextDeadlockCheck; +static LISTDECL(DeadlockCheck, link) s_deadlockList; + +static CCritSect s_spareCrit; +static TSpareList s_deadlockSpares; + + +#define SAFE_CRITSECT_ENTER() if (s_critsect) s_critsect->Enter() +#define SAFE_CRITSECT_LEAVE() if (s_critsect) s_critsect->Leave() + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//============================================================================ +static void DelayDeadlockChecking () { + // Delay deadlock checking for the next 2 minutes + s_nextDeadlockCheck = 1 | (TimeGetMs() + 2 * 60 * 1000); +} + +//============================================================================ +static void ReplaceEmailParams (EmailParams * newParams) { + + if (newParams) + newParams->IncRef(); + + SAFE_CRITSECT_ENTER(); + { + SWAP(newParams, s_params); + } + SAFE_CRITSECT_LEAVE(); + + if (newParams) + newParams->DecRef(); +} + +//============================================================================ +static EmailParams * GetEmailParamsIncRef () { + ref(GetEmailParamsIncRef); + + EmailParams * params; + + SAFE_CRITSECT_ENTER(); + { + if (nil != (params = s_params)) + params->IncRef(); + } + SAFE_CRITSECT_LEAVE(); + + return params; +}; + +//============================================================================ +#ifdef _M_IX86 +static void __declspec(naked) CrashFunc () { + + *(int *) 0 = 0; +} +#endif // def _M_IX86 + +//============================================================================ +#ifdef _M_IX86 +static void MakeThreadCrashOnResume (HANDLE handle) { + + // Point the thread's instruction pointer to CrashFunc + // so that it will crash once we resume it. + CONTEXT context; + context.ContextFlags = CONTEXT_CONTROL; + GetThreadContext(handle, &context); + context.Eip = (DWORD) CrashFunc; + SetThreadContext(handle, &context); +} +#endif // def _M_IX86 + +//============================================================================ +static inline const char * GetExceptionString (unsigned code) { + #if defined(HS_DEBUGGING) || defined(SERVER) + switch (code) { + #define EXCEPTION(x) case EXCEPTION_##x: return #x; + EXCEPTION(ACCESS_VIOLATION) + EXCEPTION(DATATYPE_MISALIGNMENT) + EXCEPTION(BREAKPOINT) + EXCEPTION(SINGLE_STEP) + EXCEPTION(ARRAY_BOUNDS_EXCEEDED) + EXCEPTION(FLT_DENORMAL_OPERAND) + EXCEPTION(FLT_DIVIDE_BY_ZERO) + EXCEPTION(FLT_INEXACT_RESULT) + EXCEPTION(FLT_INVALID_OPERATION) + EXCEPTION(FLT_OVERFLOW) + EXCEPTION(FLT_STACK_CHECK) + EXCEPTION(FLT_UNDERFLOW) + EXCEPTION(INT_DIVIDE_BY_ZERO) + EXCEPTION(INT_OVERFLOW) + EXCEPTION(PRIV_INSTRUCTION) + EXCEPTION(IN_PAGE_ERROR) + EXCEPTION(ILLEGAL_INSTRUCTION) + EXCEPTION(NONCONTINUABLE_EXCEPTION) + EXCEPTION(STACK_OVERFLOW) + EXCEPTION(INVALID_DISPOSITION) + EXCEPTION(GUARD_PAGE) + EXCEPTION(INVALID_HANDLE) + #undef EXCEPTION + + default: + return s_unknown; + } + #else // if defined(HS_DEBUGGING) || defined(SERVER) + { + ref(code); + return ""; + } + #endif // if defined(HS_DEBUGGING) || defined(SERVER) +} + +//============================================================================ +static void LogWriteToDisk (ErrLog * const log) { + + HANDLE file = CreateFileW( + log->name, + GENERIC_WRITE, + FILE_SHARE_READ, + (LPSECURITY_ATTRIBUTES) nil, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + (HANDLE) nil + ); + if (file != INVALID_HANDLE_VALUE) { + DWORD bytesWritten; + SetFilePointer(file, 0, 0, FILE_END); + WriteFile(file, log->buffer, StrLen(log->buffer), &bytesWritten, 0); + CloseHandle(file); + } +} + +//============================================================================ +static unsigned ProcessErrorLog ( + ErrLog * const log, + const char programName[], + const char errorType[] +) { + ref(programName); + ref(errorType); + + LogWriteToDisk(log); + + // Servers email the error and continue running + #ifdef SERVER + { + // @@@ TODO: Write log to file here + + if (EmailParams * params = GetEmailParamsIncRef()) { + CrashSendEmail( + params->smtp, + params->sender, + params->recipients, + params->username, + params->password, + params->replyTo, + programName, + errorType, + log->buffer + ); + params->DecRef(); + } + return kErrFlagAssertionBreakpoint | kErrFlagExceptionExecHandler; + } + + // Client programs display an error dialog giving the user a choice of + // sending the error or not + #else + { + // Todo: make a dialog box to handle this + return kErrFlagAssertionExitProgram | kErrFlagExceptionExecHandler; + } + #endif +} + +//============================================================================ +static unsigned ProcessBreakException () { + + #ifdef SERVER + + // Servers running as daemons should attempt to keep running + if (ErrorGetOption(kErrOptNonGuiAsserts)) + return kErrFlagExceptionExecHandler; + else + return kErrFlagExceptionBreakpoint; + + #else + + return kErrFlagExceptionBreakpoint; + + #endif +} + +//============================================================================ +static ErrLog * CreateErrLog () { + // Allocate log memory + ErrLog * log = (ErrLog *) VirtualAlloc(nil, sizeof(*log), MEM_COMMIT, PAGE_READWRITE); + log->pos = 0; + log->terminator = 0; + + // Initialize log filename + wchar srcName[MAX_PATH]; + SYSTEMTIME currTime; + GetLocalTime(&currTime); + StrPrintf( + srcName, + arrsize(srcName), + L"Crash%02u%02u%02u.log", + currTime.wYear % 100, + currTime.wMonth, + currTime.wDay + ); + + // Set log directory + filename + AsyncLogGetDirectory(log->name, arrsize(log->name)); + PathAddFilename(log->name, log->name, srcName, arrsize(log->name)); + + return log; +} + +//============================================================================ +static void DestroyErrLog (ErrLog * errLog) { + VirtualFree(errLog, 0, MEM_RELEASE); +} + +//============================================================================ +static void __cdecl LogPrintf ( + ErrLog * const log, + const char fmt[], + ... +) { + va_list args; + va_start(args, fmt); + char * pos = log->buffer + log->pos; + unsigned len = StrPrintfV( + pos, + arrsize(log->buffer) - log->pos, + fmt, + args + ); + va_end(args); + log->pos += len; +} + +//============================================================================ +static inline void LogSectionHeader ( + ErrLog * const log, + const char section[] +) { + LogPrintf(log, s_sectionFmt_s, section); +} + +//============================================================================ +#ifdef _M_IX86 +static void LogStackWalk ( + ErrLog * const log, + const CImageHelp & ih, + HANDLE hThread, + const CONTEXT & ctx +) { + STACKFRAME stk; + ZeroMemory(&stk, sizeof(stk)); + stk.AddrPC.Offset = ctx.Eip; + stk.AddrPC.Mode = AddrModeFlat; + stk.AddrStack.Offset = ctx.Esp; + stk.AddrStack.Mode = AddrModeFlat; + stk.AddrFrame.Offset = ctx.Ebp; + stk.AddrFrame.Mode = AddrModeFlat; + + LogSectionHeader(log, "Trace"); + for (unsigned i = 0; i < 100; i++) { + const bool result = ih.StackWalk( + IMAGE_FILE_MACHINE_I386, + ih.Process(), + hThread, + &stk, + nil, // context + nil, // read memory routine + ih.SymFunctionTableAccess, + ih.SymGetModuleBase, + nil // translate address routine + ); + if (!result) + break; + + LogPrintf( + log, + "Pc:%08x Fr:%08x Rt:%08x Arg:%08x %08x %08x %08x ", + stk.AddrPC.Offset, + stk.AddrFrame.Offset, + stk.AddrReturn.Offset, + stk.Params[0], + stk.Params[1], + stk.Params[2], + stk.Params[3] + ); + + LogPrintf(log, "\r\n"); + } + + LogPrintf(log, "\r\n"); +} +#endif // _M_IX86 + +//============================================================================ +static void LogThread ( + ErrLog * const log, + const CImageHelp & ih, + HANDLE hThread, + const wchar name[], + const CONTEXT & ctx +) { + char threadName[256]; + StrPrintf(threadName, arrsize(threadName), "%SThread %#x", name, hThread); + LogSectionHeader(log, threadName); + + LogPrintf( + log, + "eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\r\n" + "esi=%08lx edi=%08lx\r\n" + "eip=%08lx esp=%08lx ebp=%08lx\r\n" + "cs=%04lx ss=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx efl=%08lx\r\n\r\n", + ctx.Eax, ctx.Ebx, ctx.Ecx, ctx.Edx, ctx.Esi, ctx.Edi, + ctx.Eip, ctx.Esp, ctx.Ebp, ctx.EFlags, + ctx.SegCs, ctx.SegSs, ctx.SegDs, ctx.SegEs, ctx.SegFs, ctx.SegGs, ctx.EFlags + ); + + #ifdef _M_IX86 + LogStackWalk(log, ih, hThread, ctx); + #else + ref(ih); + ref(hThread); + #endif +} + +//============================================================================ +static void LogCrashInfo ( + const char msg[], + ErrLog * const log, + const CImageHelp & ih +) { + // Log application information + { + LogSectionHeader(log, "Program"); + + wchar productIdStr[64]; + GuidToString(ProductId(), productIdStr, arrsize(productIdStr)); + + wchar productBuildTag[128]; + ProductString(productBuildTag, arrsize(productBuildTag)); + + SYSTEMTIME currtime; + GetLocalTime(&currtime); + LogPrintf( + log, + // beginning of format string + "App : %s\r\n" + "Build : " + #ifdef PLASMA_EXTERNAL_RELEASE + "External " + #else + "Internal " + #endif + #ifdef HS_DEBUGGING + "Debug" + #else + "Release" + #endif + "\r\n" + "BuildMark : %S\r\n" + "ProductTag : %S\r\n" + "ProductId : %S\r\n" + "Crashed : %d/%d/%d %02d:%02d:%02d\r\n" + "Msg : %s\r\n" + "\r\n", + // end of format string + ih.GetProgramName(), + ProductBuildString(), + productBuildTag, + productIdStr, + currtime.wMonth, + currtime.wDay, + currtime.wYear, + currtime.wHour, + currtime.wMinute, + currtime.wSecond, + msg + ); + } + + // Log system information + { + LogSectionHeader(log, "System"); + + char machineName[128]; + DWORD len = arrsize(machineName); + if (!GetComputerName(machineName, &len)) + StrCopy(machineName, s_unknown, arrsize(machineName)); + LogPrintf(log, "Machine : %s\r\n", machineName); + + for (;;) { + WSADATA wsaData; + if (WSAStartup(0x101, &wsaData) || (wsaData.wVersion != 0x101)) + break; + + wchar ipAddress[256]; + NetAddressNode addrNodes[16]; + unsigned addrCount = NetAddressGetLocal(16, addrNodes); + LogPrintf(log, "IpAddrs : "); + for (unsigned i = 0; i < addrCount; ++i) { + NetAddressNodeToString(addrNodes[i], ipAddress, arrsize(ipAddress)); + LogPrintf(log, "%S, ", ipAddress); + } + LogPrintf(log, "\r\n"); + + WSACleanup(); + break; + } + + SYSTEM_INFO si; + GetSystemInfo(&si); + DWORD ver = GetVersion(); + + LogPrintf( + log, + "OS Version : %u.%u\r\n" + "CPU Count : %u\r\n" + "\r\n", + LOBYTE(LOWORD(ver)), HIBYTE(LOWORD(ver)), + si.dwNumberOfProcessors + ); + } + + // Log loaded modules + { + LogSectionHeader(log, "Modules"); + SAFE_CRITSECT_ENTER(); + for (const Module * p = s_modules.Head(); p; p = p->link.Next()) { + LogPrintf( + log, + "%p %S (%u.%u.%S)\r\n", + p->address, + p->name, + p->buildId, + p->branchId, + p->buildString + ); + } + SAFE_CRITSECT_LEAVE(); + LogPrintf(log, "\r\n"); + } +} + +//============================================================================ +static LONG ProcessException (const char occasion[], EXCEPTION_POINTERS * ep) { + + // log non-breakpont exceptions + unsigned result; + if (ep->ExceptionRecord->ExceptionCode != EXCEPTION_BREAKPOINT) { + + // if this is a stack fault, allocate a new stack so we have + // enough space to log the error. + static void * newStack; + #define STACKBYTES (64 * 1024) + if (ep->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW) { + newStack = VirtualAlloc(nil, STACKBYTES, MEM_COMMIT, PAGE_READWRITE); + __asm { + mov eax, [newStack] // get new memory block + add eax, STACKBYTES - 4 // point to end of block + mov [eax], esp // save current stack pointer + mov esp, eax // set new stack pointer + } + } + + DelayDeadlockChecking(); + + // Allocate error log memory + ErrLog * log = CreateErrLog(); + + // get crash log data + static const char s_exception[] = "Exception"; + LogSectionHeader(log, "Crash"); + LogPrintf( + log, + "%s: %08x %s\r\n", + s_exception, + ep->ExceptionRecord->ExceptionCode, + GetExceptionString(ep->ExceptionRecord->ExceptionCode) + ); + if (ep->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { + LogPrintf( + log, + "Memory at address %08x could not be %s\r\n", + ep->ExceptionRecord->ExceptionInformation[1], + ep->ExceptionRecord->ExceptionInformation[0] ? "written" : "read" + ); + } + LogPrintf(log, "\r\n"); + + CImageHelp ih((HINSTANCE)ModuleGetInstance()); + LogCrashInfo(occasion, log, ih); + + // log crashed thread + LogThread( + log, + ih, + GetCurrentThread(), + L"", + *ep->ContextRecord + ); + + // display the error + result = ProcessErrorLog( + log, + ih.GetProgramName(), + occasion + ); + + DestroyErrLog(log); + ErrorSetOption(kErrOptDisableMemLeakChecking, true); + + // if this is a stack overflow, restore original stack + if (ep->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW) { + __asm { + mov eax, [newStack] // get new memory block + add eax, STACKBYTES - 4 // point to end of block + mov esp, [eax] // restore old stack pointer + } + + VirtualFree(newStack, 0, MEM_RELEASE); + } + } + else { + result = ProcessBreakException(); + } + + // If this is a debug build and the user pressed the "debug" button + // then we return EXCEPTION_CONTINUE_SEARCH so the program will + // activate just-in-time debugging, or barring that, bring up the + // Microsoft error handler. + if (result & kErrFlagExceptionBreakpoint) + return EXCEPTION_CONTINUE_SEARCH; + + return EXCEPTION_EXECUTE_HANDLER; +} + + +//============================================================================ +static LONG WINAPI ExceptionFilter (EXCEPTION_POINTERS * ep) { + ref(ExceptionFilter); + + LONG result = ProcessException("Unhandled Exception", ep); + + // If the instruction pointer is inside CrashFunc then this exception + // is a deadlock that couldn't be resumed, so terminate the program. + #ifdef _M_IX86 + if ((ep->ContextRecord->Eip >= (DWORD) CrashFunc) + && (ep->ContextRecord->Eip <= (DWORD) CrashFunc + 5)) + TerminateProcess(GetCurrentProcess(), 1); + #else + # error "ExceptionFilter not implemented for this CPU" + #endif // def _M_IX86 + + return result; +} + +//============================================================================ +static void ProcessDeadlock_CS (const char occasion[], bool crashIt = true) { + + unsigned currTimeMs = TimeGetMs(); + unsigned crashCount = 0; + + // Suspend all threads so that we can dump their callstacks + DeadlockCheck * next, * check = s_deadlockList.Head(); + for (; check; check = next) { + next = s_deadlockList.Next(check); + SuspendThread(check->thread); + } + + // Allocate error log memory + ErrLog * log = CreateErrLog(); + + // Log report header and system data + CImageHelp ih((HINSTANCE)ModuleGetInstance()); + LogCrashInfo(occasion, log, ih); + + // Log all threads + if (!s_deadlockList.Head()) + LogPrintf(log, "*** No threads queued for deadlock check ***\r\n\r\n"); + + check = s_deadlockList.Head(); + for (; check; check = next) { + next = s_deadlockList.Next(check); + CONTEXT ctx; + ctx.ContextFlags = CONTEXT_SEGMENTS | CONTEXT_INTEGER | CONTEXT_CONTROL; + GetThreadContext( + check->thread, + &ctx + ); + if (true == (check->deadlocked = (int)(check->deadlockEmailMs - currTimeMs) < 0) && !check->emailSent && s_deadlockEmailMs) { + LogPrintf(log, "This thread has hit the min deadlock check time\r\n"); + check->emailSent = true; + } + else if (true == (check->deadlocked = (int)(check->deadlockTerminateMs - currTimeMs) < 0) && s_deadlockTerminateMs) { + LogPrintf(log, "This thread has hit the max deadlock check time\r\n"); + if (crashIt) { + MakeThreadCrashOnResume(check->thread); + ++crashCount; + } + } + + if(check->deadlocked) { + LogPrintf(log, "Debug information: "); + LogPrintf(log, "%S\r\n", check->debugStr); + } + + LogThread( + log, + ih, + check->thread, + L"", + ctx + ); + } + + (void) ProcessErrorLog( + log, + ih.GetProgramName(), + occasion + ); + + DestroyErrLog(log); + ErrorSetOption(kErrOptDisableMemLeakChecking, true); + + // Resume all threads + unsigned elapsedMs = TimeGetMs() - currTimeMs; + check = s_deadlockList.Head(); + for (; check; check = next) { + next = s_deadlockList.Next(check); + + if (check->deadlocked && crashIt) + // remove 'check' from list since we're crashing the thread on resume + check->link.Unlink(); + else { + // Update deadlock time to offset by the amount of time we had them suspended. + check->deadlockEmailMs += elapsedMs; + check->deadlockTerminateMs += elapsedMs; + } + + ResumeThread(check->thread); + } + + // Allow the resumed thread a bit of time to crash and + // send the resulting email then terminate the process + if (crashCount) { + Sleep(60 * 1000); + TerminateProcess(GetCurrentProcess(), 1); + } +} + +//============================================================================ +static void DeadlockCheckProc (void *) { + ref(DeadlockCheckProc); + + while (s_running) { + Sleep(5 * 1000); + + if (!s_deadlockEnabled) + continue; + + unsigned currTimeMs = TimeGetMs(); + + // Check for a forced delay in deadlock checking + if (s_nextDeadlockCheck && (int)(s_nextDeadlockCheck - currTimeMs) > 0) + continue; + s_nextDeadlockCheck = 0; + + SAFE_CRITSECT_ENTER(); + for (;;) { + DeadlockCheck * next, * check = s_deadlockList.Head(); + for (; check; check = next) { + next = s_deadlockList.Next(check); + if ((int)(check->deadlockEmailMs - currTimeMs) <= 0 && !check->emailSent && s_deadlockEmailMs) { + // we have hit our minimum deadlock check time. Send and email but dont crash the process, yet. + ProcessDeadlock_CS("DeadlockChecker", false); + check->emailSent = true; + break; + } + else if ((int)(check->deadlockTerminateMs - currTimeMs) <= 0 && s_deadlockTerminateMs){ + // we have hit out max deadlock check time. This will send an email and crash the process so the service can restart. + ProcessDeadlock_CS("DeadlockChecker(Process Terminated)"); + break; + } + } + break; + } + SAFE_CRITSECT_LEAVE(); + } +} + +//============================================================================ +#ifdef SERVER +static void StartDeadlockThread () { + (void)_beginthread(DeadlockCheckProc, 0, nil); +} +#endif + +//============================================================================ +#ifdef SERVER +static void DeadlockCheckNowProc (void *) { + SAFE_CRITSECT_ENTER(); + { + ProcessDeadlock_CS("Deadlock Check"); + } + SAFE_CRITSECT_LEAVE(); +} +#endif + +//============================================================================ +#ifdef SERVER +static void ThreadReportProc (void *) { + SAFE_CRITSECT_ENTER(); + { + ProcessDeadlock_CS("Thread Report", false); + } + SAFE_CRITSECT_LEAVE(); +} +#endif + +//============================================================================ +static void pnCrashExeShutdown () { + s_running = false; + + ReplaceEmailParams(nil); + ASSERT(!s_deadlockList.Head()); + s_deadlockSpares.CleanUp(); +} + +//============================================================================ +AUTO_INIT_FUNC(pnCrashExe) { + // The critical section has to be initialized + // before program startup and never freed + static byte rawMemory[sizeof CCritSect]; + s_critsect = new(rawMemory) CCritSect; + + s_running = true; + + atexit(pnCrashExeShutdown); + + #ifdef SERVER + SetUnhandledExceptionFilter(ExceptionFilter); + StartDeadlockThread(); + #endif +} + + + +/***************************************************************************** +* +* Module functions +* +***/ + + +} // namespace Crash + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void CrashExceptionDump (const char occasion[], void * info) { + + (void) ProcessException(occasion, (EXCEPTION_POINTERS *) info); +} + +//============================================================================ +void CrashSetEmailParameters ( + const char smtp[], + const char sender[], + const char recipients[], + const char username[], + const char password[], + const char replyTo[] +) { + ASSERT(smtp); + ASSERT(sender); + ASSERT(recipients); + + EmailParams * params = NEWZERO(EmailParams); + + params->smtp = StrDup(smtp); + params->sender = StrDup(sender); + params->recipients = StrDup(recipients); + if (username) + params->username = StrDup(username); + if (password) + params->password = StrDup(password); + if (replyTo) + params->replyTo = StrDup(replyTo); + + ReplaceEmailParams(params); +} + +//============================================================================ +void * CrashAddModule ( + unsigned_ptr address, + unsigned buildId, + unsigned branchId, + const wchar name[], + const wchar buildString[] +) { + ASSERT(name); + Module * module = NEWZERO(Module); + + module->address = address; + module->buildId = buildId; + module->branchId = branchId; + module->name = StrDup(name); + module->buildString = StrDup(buildString); + // trim trailing spaces from buildString + for (unsigned i = StrLen(buildString) - 1; i > 0; --i) { + if (module->buildString[i] != L' ') + break; + module->buildString[i] = 0; + } + + SAFE_CRITSECT_ENTER(); + { + s_modules.Link(module); + } + SAFE_CRITSECT_LEAVE(); + + return module; +} + +//============================================================================ +void CrashRemoveModule ( + void * param +) { + Module * module = (Module *) param; + SAFE_CRITSECT_ENTER(); + { + DEL(module); + } + SAFE_CRITSECT_LEAVE(); +} + + +/***************************************************************************** +* +* Deadlock detection (server only) +* +***/ + +//============================================================================ +#ifdef SERVER +void * CrashAddDeadlockCheck ( + void * thread, + const wchar debugStr[] +) { + s_spareCrit.Enter(); + DeadlockCheck * check = (DeadlockCheck *)s_deadlockSpares.Alloc(); + s_spareCrit.Leave(); + + (void) new(check) DeadlockCheck; + + check->deadlockEmailMs = TimeGetMs() + s_deadlockEmailMs; + check->deadlockTerminateMs = TimeGetMs() + s_deadlockTerminateMs; + check->thread = (HANDLE) thread; + check->emailSent = false; + StrCopy(check->debugStr, debugStr, arrsize(check->debugStr)); + + SAFE_CRITSECT_ENTER(); + { + s_deadlockList.Link(check); + } + SAFE_CRITSECT_LEAVE(); + + return check; +} +#endif + +//============================================================================ +#ifdef SERVER +void CrashRemoveDeadlockCheck ( + void * ptr +) { + ASSERT(ptr); + DeadlockCheck * check = (DeadlockCheck *)ptr; + SAFE_CRITSECT_ENTER(); + { + s_deadlockList.Unlink(check); + } + SAFE_CRITSECT_LEAVE(); + + check->~DeadlockCheck(); + s_spareCrit.Enter(); + s_deadlockSpares.Free(check); + s_spareCrit.Leave(); +} +#endif + +//============================================================================ +#ifdef SERVER +bool CrashEnableDeadlockChecking ( + bool enable +) { + SWAP(s_deadlockEnabled, enable); + return enable; +} +#endif + +//============================================================================ +#ifdef SERVER +void CrashSetDeadlockCheckTimes(unsigned emailSec, unsigned terminateSec) { + if(!emailSec && !terminateSec) + CrashEnableDeadlockChecking(false); + else { + CrashEnableDeadlockChecking(true); + s_deadlockEmailMs = emailSec * 1000; + s_deadlockTerminateMs = terminateSec * 1000; + } +} +#endif + +//============================================================================ +#ifdef SERVER +void CrashDeadlockCheckNow () { + // Perform in a thread not queued for deadlock checking + (void)_beginthread(DeadlockCheckNowProc, 0, nil); +} +#endif + +//============================================================================ +#ifdef SERVER +void CrashSendThreadReport () { + // Perform in a thread not queued for deadlock checking + (void)_beginthread(ThreadReportProc, 0, nil); +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreGui.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreGui.cpp new file mode 100644 index 00000000..ff0e8c73 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreGui.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/pnCreGui.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreMail.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreMail.cpp new file mode 100644 index 00000000..8c62961b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreMail.cpp @@ -0,0 +1,198 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreMail.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +//============================================================================ +namespace Crash { +//============================================================================ + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static bool CreateInheritablePipe ( + HANDLE * read, + HANDLE * write, + bool inheritRead +) { + // create pipe for std error read/write + if (!CreatePipe(read, write, (LPSECURITY_ATTRIBUTES) NULL, 0)) + return false; + + // make the appropriate handle inheritable + HANDLE hProcess = GetCurrentProcess(); + HANDLE * inherit = inheritRead ? read : write; + bool result = DuplicateHandle( + hProcess, + *inherit, + hProcess, + inherit, + 0, + true, // make inheritable + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS + ); + if (!result) + return false; + + return true; +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void CrashSendEmail ( + const char smtp[], + const char sender[], + const char recipients[], + const char replyTo[], + const char username[], + const char password[], + const char programName[], + const char errorType[], + const char logBuffer[] +) { + enum { + IN_CHILD, + IN_PARENT, + NUM_PIPES + }; + + unsigned i; + + HANDLE pipes[NUM_PIPES]; + for (i = 0; i < arrsize(pipes); ++i) + pipes[i] = INVALID_HANDLE_VALUE; + + + for (;;) { + // create pipes for Server -> StdIn -> Client + if (!CreateInheritablePipe(&pipes[IN_CHILD], &pipes[IN_PARENT], true)) + break; + + char subject[512]; + StrPrintf( + subject, + arrsize(subject), + "\"[Crash] %s, %s\"", + programName, + errorType + ); + + char cmdLine[512]; + StrPrintf( + cmdLine, + arrsize(cmdLine), + "plMail.exe -smtp %s -sender %s", + smtp, + sender + ); + + if (replyTo && replyTo[0]) { + StrPack(cmdLine, " -replyTo ", arrsize(cmdLine)); + StrPack(cmdLine, replyTo, arrsize(cmdLine)); + } + + if (username && username[0]) { + StrPack(cmdLine, " -u ", arrsize(cmdLine)); + StrPack(cmdLine, username, arrsize(cmdLine)); + } + + if (password && password[0]) { + StrPack(cmdLine, " -p ", arrsize(cmdLine)); + StrPack(cmdLine, password, arrsize(cmdLine)); + } + + StrPack(cmdLine, " ", arrsize(cmdLine)); + StrPack(cmdLine, recipients, arrsize(cmdLine)); + + StrPack(cmdLine, " ", arrsize(cmdLine)); + StrPack(cmdLine, subject, arrsize(cmdLine)); + + // create process + STARTUPINFO si; + ZERO(si); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdInput = pipes[IN_CHILD]; + si.hStdOutput = INVALID_HANDLE_VALUE; + si.hStdError = INVALID_HANDLE_VALUE; + PROCESS_INFORMATION pi; + BOOL result = CreateProcess( + NULL, + cmdLine, + (LPSECURITY_ATTRIBUTES) NULL, + (LPSECURITY_ATTRIBUTES) NULL, + true, // => inherit handles + NORMAL_PRIORITY_CLASS, + NULL, + NULL, + &si, + &pi + ); + if (!result) + break; + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + // Write output data + DWORD written, length = StrLen(logBuffer); + WriteFile(pipes[IN_PARENT], logBuffer, length, &written, NULL); + + // complete + break; + } + + // cleanup pipes + for (i = 0; i < arrsize(pipes); ++i) { + if (pipes[i] != INVALID_HANDLE_VALUE) + CloseHandle(pipes[i]); + } +} + + + +//============================================================================ +} // namespace Crash +//============================================================================ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreTools.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreTools.cpp new file mode 100644 index 00000000..6408140f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/Win32/pnCreTools.cpp @@ -0,0 +1,185 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCrashExe/pnCreTools.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +//============================================================================ +namespace Crash { +//============================================================================ + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +static BOOL MYIMAGEAPI iSymGetModuleInfo ( + HANDLE, + DWORD, + IMAGEHLP_MODULE * ModuleInfo +) { + ZEROPTR(ModuleInfo); + return false; +} + +//=========================================================================== +static BOOL MYIMAGEAPI iStackWalk ( + DWORD, + HANDLE, + HANDLE, + LPSTACKFRAME, + LPVOID, + PREAD_PROCESS_MEMORY_ROUTINE, + PFUNCTION_TABLE_ACCESS_ROUTINE, + PGET_MODULE_BASE_ROUTINE, + PTRANSLATE_ADDRESS_ROUTINE +) { + return false; +} + +//=========================================================================== +static LPVOID MYIMAGEAPI iSymFunctionTableAccess ( + HANDLE, + DWORD +) { + return nil; +} + +//=========================================================================== +static DWORD MYIMAGEAPI iSymGetModuleBase ( + HANDLE, + DWORD +) { + return 0; +} + +//=========================================================================== +static BOOL MYIMAGEAPI iSymGetSymFromAddr ( + HANDLE, + DWORD, + LPDWORD Displacement, + PIMAGEHLP_SYMBOL Symbol +) { + *Displacement = 0; + ZEROPTR(Symbol); + return false; +} + + +/***************************************************************************** +* +* CImageHelp +* +***/ + +//============================================================================ +CImageHelp::CImageHelp (HINSTANCE instance) +: m_libInst(nil), + m_process(nil) +{ + // get program directory and filename + if (!GetModuleFileNameA(instance, m_appPath, arrsize(m_appPath))) + m_appPath[0] = 0; + if (nil != (m_appName = StrChrR(m_appPath, '\\'))) + *m_appName++ = '\0'; + else + m_appName = m_appPath; + + // initialize symbols + for (;;) { + if (nil == (m_libInst = LoadLibrary("ImageHlp.dll"))) + break; + + // initialize symbols for this process + BOOL (MYIMAGEAPI * SymInitialize)(HANDLE hProcess, char * path, BOOL fInvade); + * (FARPROC *) &SymInitialize = GetProcAddress(m_libInst, "SymInitialize"); + if (!SymInitialize) + break; + + HANDLE hProcess = GetCurrentProcess(); + if (!SymInitialize(hProcess, m_appPath, false)) + break; + + // import functions + FARPROC proc; + #define BIND(x) * (FARPROC *) &(x) = ((proc = GetProcAddress(m_libInst, #x)) != nil) ? proc : (FARPROC) i##x + BIND(SymGetModuleInfo); + BIND(StackWalk); + BIND(SymFunctionTableAccess); + BIND(SymGetModuleBase); + BIND(SymGetSymFromAddr); + #undef BIND + + // success + m_process = hProcess; + return; + } + + // failure! + if (m_libInst) { + FreeLibrary(m_libInst); + m_libInst = nil; + } + + #define INIT(x) x = i##x + INIT(SymGetModuleInfo); + INIT(StackWalk); + INIT(SymFunctionTableAccess); + INIT(SymGetModuleBase); + INIT(SymGetSymFromAddr); + #undef INIT +} + +//============================================================================ +CImageHelp::~CImageHelp () { + if (m_process) { + BOOL (MYIMAGEAPI * SymCleanup)(HANDLE hProcess); + * (FARPROC *) &SymCleanup = GetProcAddress(m_libInst, "SymCleanup"); + if (SymCleanup) + SymCleanup(m_process); + m_process = 0; + } + + if (m_libInst) { + FreeLibrary(m_libInst); + m_libInst = 0; + } +} + + + +//============================================================================ +} // namespace Crash +//============================================================================ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/Pch.h new file mode 100644 index 00000000..71e2dec5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/Pch.h @@ -0,0 +1,40 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCSRCLI_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCSRCLI_PCH_H + + +#include "pnCsrCli.h" + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/pnCsrCli.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/pnCsrCli.cpp new file mode 100644 index 00000000..1f3a22bf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/pnCsrCli.cpp @@ -0,0 +1,253 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/pnCsrCli.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + + +/***************************************************************************** +* +* Local data +* +***/ + +static bool s_running; +static CCritSect s_critsect; +static SimpleNetConn * s_conn; +static FCsrCliOnError s_onError; + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static SimpleNetConn * GetConnIncRef () { + + SimpleNetConn * conn; + s_critsect.Enter(); + { + if (nil != (conn = s_conn)) + SimpleNetConnIncRef(conn); + } + s_critsect.Leave(); + return conn; +} + +//============================================================================ +static bool SimpleNetOnMsg ( + SimpleNetConn * , + SimpleNet_MsgHeader * +) { + LogMsg(kLogPerf, L"pnCsrCli: Rcvd unexpected message from peer"); + return false; +} + +//============================================================================ +static void SimpleNetOnError ( + SimpleNetConn * , + ENetError error +) { + if (!s_running) + return; + + LogMsg(kLogPerf, L"pnCsrCli: NetError: %s", NetErrorToString(error)); + + if (error == kNetErrDisconnected) + CsrCliDisconnect(); + + s_onError(error); +} + +//============================================================================ +static void SimpleNetOnConnect ( + void * param, + SimpleNetConn * conn, + ENetError result +) { + FCsrCliOnConnect onConnect = (FCsrCliOnConnect)param; + + LogMsg(kLogPerf, L"pnCsrCli: OnConnect: %s", NetErrorToString(result)); + + if (s_conn) + CsrCliDisconnect(); + + if (IS_NET_SUCCESS(result)) { + s_critsect.Enter(); + { + s_conn = conn; + } + s_critsect.Leave(); + } + + if (onConnect) + onConnect(result); +} + +//============================================================================ +static void Send_ExecConsoleCmd (const char cmd[]) { + + SimpleNetConn * conn = GetConnIncRef(); + if (!conn) + return; + + unsigned cmdBytes = StrBytes(cmd); + + CsrNet_ExecConsoleCmd * msg; + + unsigned msgBytes + = sizeof(*msg) + - sizeof(msg->cmd) + + cmdBytes + + sizeof(cmd[0]) + ; + + msg = new(_alloca(msgBytes)) CsrNet_ExecConsoleCmd(); + msg->messageBytes = msgBytes; + + StrCopy(msg->cmd, cmd, cmdBytes / sizeof(cmd[0])); + msg->cmd[cmdBytes] = 0; + + SimpleNetSend(conn, msg); + SimpleNetConnDecRef(conn); +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void CsrCliInitialize (FCsrCliOnError onError) { + + ASSERT(!s_running); + ASSERT(onError); + + s_running = true; + s_onError = onError; + + SimpleNetInitialize(); + SimpleNetCreateChannel(kSimpleNetChannelCsr, SimpleNetOnMsg, SimpleNetOnError); +}; + +//============================================================================ +void CsrCliShutdown () { + + s_running = false; + s_onError = nil; + + CsrCliDisconnect(); + SimpleNetDestroyChannel(kSimpleNetChannelCsr); + SimpleNetShutdown(); +} + +//============================================================================ +void CsrCliStartConnecting ( + const wchar addr[], + FCsrCliOnConnect onConnect +) { + ASSERT(s_running); + + CsrCliDisconnect(); + SimpleNetStartConnecting(kSimpleNetChannelCsr, addr, SimpleNetOnConnect, onConnect); +} + +//============================================================================ +void CsrCliDisconnect () { + + SimpleNetConn * conn = nil; + s_critsect.Enter(); + { + SWAP(conn, s_conn); + } + s_critsect.Leave(); + if (conn) + SimpleNetDisconnect(conn); +} + +//============================================================================ +void CsrCliToggleAvatarPhysical () { + + ASSERT(s_running); + + Send_ExecConsoleCmd("Avatar.Physics.TogglePhysical"); +} + +//============================================================================ +void CsrCliWarpPlayerHere (unsigned playerId) { + + ASSERT(s_running); + + char cmd[1024]; + StrPrintf(cmd, arrsize(cmd), "CCR.WarpPlayerHere %u", playerId); + Send_ExecConsoleCmd(cmd); +} + +//============================================================================ +void CsrCliWarpToPlayer (unsigned playerId) { + + ASSERT(s_running); + + char cmd[1024]; + StrPrintf(cmd, arrsize(cmd), "CCR.WarpToPlayer %u", playerId); + Send_ExecConsoleCmd(cmd); +} + +//============================================================================ +void CsrCliLinkPlayerHere (unsigned playerId) { + + ASSERT(s_running); + + char cmd[1024]; + StrPrintf(cmd, arrsize(cmd), "CCR.LinkPlayerHere %u", playerId); + Send_ExecConsoleCmd(cmd); +} + +//============================================================================ +void CsrCliLinkToPlayer (unsigned playerId) { + + ASSERT(s_running); + + char cmd[1024]; + StrPrintf(cmd, arrsize(cmd), "CCR.LinkToPlayer %u", playerId); + Send_ExecConsoleCmd(cmd); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/pnCsrCli.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/pnCsrCli.h new file mode 100644 index 00000000..a809f868 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/pnCsrCli.h @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCsrCli/pnCsrCli.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCSRCLI_PNCSRCLI_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCSRCLI_PNCSRCLI_H + + +#include "pnCsrNet/pnCsrNet.h" + + +/***************************************************************************** +* +* pnCsrCli +* - Connects to pfCsrSrv in remote CSR client. +* - Sends commands to pfCsrSrv for remote execution. +* +***/ + +typedef void (*FCsrCliOnError) (ENetError error); + +void CsrCliInitialize (FCsrCliOnError onError); +void CsrCliShutdown (); + +typedef void (*FCsrCliOnConnect) (ENetError result); + +void CsrCliStartConnecting ( + const wchar addr[], + FCsrCliOnConnect onConnect +); +void CsrCliDisconnect (); + +void CsrCliToggleAvatarPhysical (); +void CsrCliWarpPlayerHere (unsigned playerId); +void CsrCliWarpToPlayer (unsigned playerId); +void CsrCliLinkPlayerHere (unsigned playerId); +void CsrCliLinkToPlayer (unsigned playerId); + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCSRCLI_PNCSRCLI_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/Pch.h new file mode 100644 index 00000000..1f9ea0da --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/Pch.h @@ -0,0 +1,38 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCSRNET_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCSRNET_PCH_H + + +#include "pnCsrNet.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/pnCsrNet.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/pnCsrNet.cpp new file mode 100644 index 00000000..4dccce64 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/pnCsrNet.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/pnCsrNet.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/pnCsrNet.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/pnCsrNet.h new file mode 100644 index 00000000..8f0954bd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/pnCsrNet.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnCsrNet/pnCsrNet.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCSRNET_PNCSRNET_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCSRNET_PNCSRNET_H + + +#include "pnSimpleNet/pnSimpleNet.h" + + +/***************************************************************************** +* +* CSR Client Automation - Types and Constants +* +***/ + +// Newer CSR game clients must remain compatible with older CSR tools, +// therefore these values may not change. Only append to this enum. +enum { + kCsrNet_ExecConsoleCmd, +}; + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + +#define CSRNET_MSG(a) \ + CsrNet_##a () : SimpleNet_MsgHeader(kSimpleNetChannelCsr, kCsrNet_##a) { } + +struct CsrNet_ExecConsoleCmd : SimpleNet_MsgHeader { + CSRNET_MSG (ExecConsoleCmd); + + char cmd[1]; // null-terminated string +}; + +#undef CSRNET_MSG +//============================================================================ +// END PACKED DATA STRUCTURES +//============================================================================ +#include + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNCSRNET_PNCSRNET_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatch.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatch.cpp new file mode 100644 index 00000000..b8d61278 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatch.cpp @@ -0,0 +1,630 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsResMgr.h" +#include "plDispatch.h" +#define PLMESSAGE_PRIVATE +#include "../pnMessage/plMessage.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsTimer.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "plDispatchLogBase.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnNetCommon/plSynchedObject.h" +#include "../pnNetCommon/pnNetCommon.h" +#include "hsThread.h" +#include "plProfile.h" + +plProfile_CreateTimer("MsgReceive", "Update", MsgReceive); +plProfile_CreateTimer(" TimeMsg", "Update", TimeMsg); +plProfile_CreateTimer(" EvalMsg", "Update", EvalMsg); +plProfile_CreateTimer(" TransformMsg", "Update", TransformMsg); +plProfile_CreateTimer(" CameraMsg", "Update", CameraMsg); + +class plMsgWrap +{ +public: + plMsgWrap** fBack; + plMsgWrap* fNext; + hsTArray fReceivers; + + plMessage* fMsg; + + plMsgWrap(plMessage* msg) : fMsg(msg) { hsRefCnt_SafeRef(msg); } + virtual ~plMsgWrap() { hsRefCnt_SafeUnRef(fMsg); } + + plMsgWrap& ClearReceivers() { fReceivers.SetCount(0); return *this; } + plMsgWrap& AddReceiver(const plKey& rcv) + { + hsAssert(rcv, "Trying to send mail to nil receiver"); + fReceivers.Append(rcv); return *this; + } + const plKey& GetReceiver(int i) const { return fReceivers[i]; } + UInt32 GetNumReceivers() const { return fReceivers.GetCount(); } +}; + +Int32 plDispatch::fNumBufferReq = 0; +hsBool plDispatch::fMsgActive = false; +plMsgWrap* plDispatch::fMsgCurrent = nil; +plMsgWrap* plDispatch::fMsgHead = nil; +plMsgWrap* plDispatch::fMsgTail = nil; +hsTArray plDispatch::fMsgWatch; +MsgRecieveCallback plDispatch::fMsgRecieveCallback = nil; + +hsMutex plDispatch::fMsgCurrentMutex; // mutex for fMsgCurrent +hsMutex plDispatch::fMsgDispatchLock; // mutex for IMsgDispatch + + +plDispatch::plDispatch() +: fOwner(nil), fFutureMsgQueue(nil), fQueuedMsgOn(true) +{ +} + +plDispatch::~plDispatch() +{ + int i; + for( i = 0; i < fRegisteredExactTypes.GetCount(); i++ ) + delete fRegisteredExactTypes[i]; + + ITrashUndelivered(); + +} + +void plDispatch::ITrashUndelivered() +{ + while( fFutureMsgQueue ) + { + plMsgWrap* nuke = fFutureMsgQueue; + fFutureMsgQueue = fFutureMsgQueue->fNext; + hsRefCnt_SafeUnRef(nuke->fMsg); + delete nuke; + } + + // If we're the main dispatch, any unsent messages at this + // point are just trashed. Slave dispatches just go away and + // leave their messages to be delivered when the main dispatch + // gets around to it. + if( this == plgDispatch::Dispatch() ) + { + while( fMsgHead ) + { + plMsgWrap* nuke = fMsgHead; + fMsgHead = fMsgHead->fNext; + // hsRefCnt_SafeUnRef(nuke->fMsg); // MOOSE - done in plMsgWrap dtor + delete nuke; + } + + // reset static members which we just deleted - MOOSE + fMsgCurrent=fMsgHead=fMsgTail=nil; + + fMsgActive = false; + } +} + +plMsgWrap* plDispatch::IInsertToQueue(plMsgWrap** curr, plMsgWrap* isert) +{ + isert->fNext = *curr; + isert->fBack = curr; + if( *curr ) + (*curr)->fBack = &isert->fNext; + *curr = isert; + return isert; +} + +plMsgWrap* plDispatch::IDequeue(plMsgWrap** head, plMsgWrap** tail) +{ + plMsgWrap* retVal = *head; + if( *head ) + { + *head = (*head)->fNext; + if( *head ) + (*head)->fBack = head; + } + if( tail && (*tail == retVal) ) + *tail = *head; + return retVal; +} + +hsBool plDispatch::ISortToDeferred(plMessage* msg) +{ + plMsgWrap* msgWrap = TRACKED_NEW plMsgWrap(msg); + if( !fFutureMsgQueue ) + { + if( IGetOwner() ) + plgDispatch::Dispatch()->RegisterForExactType(plTimeMsg::Index(), IGetOwnerKey()); + + IInsertToQueue(&fFutureMsgQueue, msgWrap); + return false; + } + if( fFutureMsgQueue->fMsg->fTimeStamp > msgWrap->fMsg->fTimeStamp ) + { + IInsertToQueue(&fFutureMsgQueue, msgWrap); + return false; + } + plMsgWrap* after = fFutureMsgQueue; + while( after->fNext && (after->fNext->fMsg->fTimeStamp < msgWrap->fMsg->fTimeStamp) ) + after = after->fNext; + + IInsertToQueue(&after->fNext, msgWrap); + + return false; +} + +void plDispatch::ICheckDeferred(double secs) +{ + while( fFutureMsgQueue && (fFutureMsgQueue->fMsg->fTimeStamp < secs) ) + { + plMsgWrap* send = IDequeue(&fFutureMsgQueue, nil); + MsgSend(send->fMsg); + delete send; + } + + int timeIdx = plTimeMsg::Index(); + if( IGetOwner() + && !fFutureMsgQueue + && + ( + (timeIdx >= fRegisteredExactTypes.GetCount()) + || + !fRegisteredExactTypes[plTimeMsg::Index()] + ) + ) + plgDispatch::Dispatch()->UnRegisterForExactType(plTimeMsg::Index(), IGetOwnerKey()); +} + +hsBool plDispatch::IListeningForExactType(UInt16 hClass) +{ + if( (hClass == plTimeMsg::Index()) && fFutureMsgQueue ) + return true; + + return false; +} + +void plDispatch::IMsgEnqueue(plMsgWrap* msgWrap, hsBool async) +{ + fMsgCurrentMutex.Lock(); + +#ifdef HS_DEBUGGING + if( msgWrap->fMsg->HasBCastFlag(plMessage::kMsgWatch) ) + fMsgWatch.Append(msgWrap->fMsg); +#endif // HS_DEBUGGING + + if( fMsgTail ) + fMsgTail = IInsertToQueue(&fMsgTail->fNext, msgWrap); + else + fMsgTail = IInsertToQueue(&fMsgHead, msgWrap); + fMsgCurrentMutex.Unlock(); + + if( !async ) + // Test for fMsgActive in IMsgDispatch(), properly wrapped inside a mutex -mcn + IMsgDispatch(); +} + +// On starts deferring msg delivery until buffering is set to off again. +hsBool plDispatch::SetMsgBuffering(hsBool on) +{ + fMsgCurrentMutex.Lock(); + if( on ) + { + hsAssert(fNumBufferReq || !fMsgActive, "Can't start deferring message delivery while delivering messages. See mf"); + if( !fNumBufferReq && fMsgActive ) + { + fMsgCurrentMutex.Unlock(); + return false; + } + + fNumBufferReq++; + fMsgActive = true; + fMsgCurrentMutex.Unlock(); + } + else if( !--fNumBufferReq ) + { + fMsgActive = false; + fMsgCurrentMutex.Unlock(); + IMsgDispatch(); + } + hsAssert(fNumBufferReq >= 0, "Mismatched number of on/off dispatch buffering requests"); + + return true; +} + +void plDispatch::IMsgDispatch() +{ + if( !fMsgDispatchLock.TryLock() ) + return; + + if( fMsgActive ) + { + fMsgDispatchLock.Unlock(); + return; + } + + fMsgActive = true; + int responseLevel=0; + + fMsgCurrentMutex.Lock(); + + plMsgWrap* origTail = fMsgTail; + while( fMsgCurrent = fMsgHead ) + { + IDequeue(&fMsgHead, &fMsgTail); + fMsgCurrentMutex.Unlock(); + + plMessage* msg = fMsgCurrent->fMsg; + hsBool nonLocalMsg = msg && msg->HasBCastFlag(plMessage::kNetNonLocal); + +#ifdef HS_DEBUGGING + int watchIdx = fMsgWatch.Find(msg); + if( fMsgWatch.kMissingIndex != watchIdx ) + { + fMsgWatch.Remove(watchIdx); +#if HS_BUILD_FOR_WIN32 + __asm { int 3 } +#endif // HS_BUILD_FOR_WIN32 + } +#endif // HS_DEBUGGING + + static UInt64 startTicks = 0; + if (plDispatchLogBase::IsLogging()) + startTicks = hsTimer::GetFullTickCount(); + + int i, numReceivers=0; + for( i = 0; fMsgCurrent && i < fMsgCurrent->GetNumReceivers(); i++ ) + { + const plKey& rcvKey = fMsgCurrent->GetReceiver(i); + plReceiver* rcv = rcvKey ? plReceiver::ConvertNoRef(rcvKey->ObjectIsLoaded()) : nil; + if( rcv ) + { + if (nonLocalMsg) + { + // localOnly objects should not get remote messages + plSynchedObject* synchedObj = plSynchedObject::ConvertNoRef(rcv); + if (synchedObj && !synchedObj->IsNetSynched() ) + { + continue; + } + + if (plNetObjectDebuggerBase::GetInstance()) + { // log net msg if this is a debug object + hsKeyedObject* ko = hsKeyedObject::ConvertNoRef(rcv); + if (plNetObjectDebuggerBase::GetInstance()->IsDebugObject(ko)) + { + hsLogEntry(plNetObjectDebuggerBase::GetInstance()->LogMsg( + xtl::format(" object:%s, GameMessage %s st=%.3f rt=%.3f", + ko->GetKeyName(), msg->ClassName(), hsTimer::GetSysSeconds(), hsTimer::GetSeconds()).c_str())); + } + } + } + +#ifndef PLASMA_EXTERNAL_RELEASE + UInt32 rcvTicks = hsTimer::GetPrecTickCount(); + + // Object could be deleted by this message, so we need to log this stuff now + const char* keyname = "(unknown)"; + const char* className = "(unknown)"; + UInt32 clonePlayerID = 0; + if (plDispatchLogBase::IsLoggingLong()) + { + hsKeyedObject* ko = hsKeyedObject::ConvertNoRef(rcv); + if (ko) + { + keyname = ko->GetKeyName(); + clonePlayerID = ko->GetKey()->GetUoid().GetClonePlayerID(); + className = ko->ClassName(); + } + } +#endif // PLASMA_EXTERNAL_RELEASE + + #ifdef HS_DEBUGGING + if (msg->GetBreakBeforeDispatch()) + DebugBreakIfDebuggerPresent(); + #endif + + plProfile_BeginTiming(MsgReceive); + rcv->MsgReceive(msg); + plProfile_EndTiming(MsgReceive); + +#ifndef PLASMA_EXTERNAL_RELEASE + if (plDispatchLogBase::IsLoggingLong()) + { + rcvTicks = hsTimer::GetPrecTickCount() - rcvTicks; + + float rcvTime = (float)(hsTimer::PrecTicksToSecs(rcvTicks) * 1000.f); + // If the receiver takes more than 5 ms to process its message, log it + if (rcvTime > 5.f) + plDispatchLogBase::GetInstance()->LogLongReceive(keyname, className, clonePlayerID, msg, rcvTime); + } +#endif // PLASMA_EXTERNAL_RELEASE + + numReceivers++; + + if (fMsgRecieveCallback != nil) + fMsgRecieveCallback(); + } + } + + // for message logging +// if (plDispatchLogBase::IsLogging()) +// { +// float sendTime = hsTimer::FullTicksToMs(hsTimer::GetFullTickCount() - startTicks); +// +// plDispatchLogBase::GetInstance()->DumpMsg(msg, numReceivers, (int)sendTime, responseLevel*2 /* indent */); +// if (origTail==fMsgCurrent) +// { // if we deliver more msgs after this, they must be response msgs +// responseLevel++; +// origTail = fMsgTail; +// } +// } + + fMsgCurrentMutex.Lock(); + + delete fMsgCurrent; + // TEMP + fMsgCurrent = (class plMsgWrap *)0xdeadc0de; + } + fMsgCurrentMutex.Unlock(); + + fMsgActive = false; + fMsgDispatchLock.Unlock(); +} + +// +// returns true if msg has been consumed and deleted +// +hsBool plDispatch::IMsgNetPropagate(plMessage* msg) +{ + fMsgCurrentMutex.Lock(); + + // Make sure cascaded messages all have the same net flags + plNetClientApp::InheritNetMsgFlags(fMsgCurrent ? fMsgCurrent->fMsg : nil, msg, false); + + fMsgCurrentMutex.Unlock(); + + // Decide if msg should go out over the network. + // If kNetForce is used, this message should always go out over the network, even if it's already + // part of a net cascade. We still want to inherit net status flags (but ignore them) + // so that response messages obey cascading rules. In other words, we are not + // halting the cascade, just overriding the send rule for this message. + if( msg->HasBCastFlag(plMessage::kNetPropagate) && + (!msg->HasBCastFlag(plMessage::kNetSent) || + msg->HasBCastFlag(plMessage::kNetForce) || + msg->HasBCastFlag(plMessage::kNetNonDeterministic) || + msg->HasBCastFlag(plMessage::kCCRSendToAllPlayers )) ) + { + // send it off... + hsAssert(!msg->HasBCastFlag(plMessage::kNetStartCascade), "initial net cascade msg getting sent over the net again?"); + if (plNetClientApp::GetInstance() && plNetClientApp::GetInstance()->ISendGameMessage(msg)>=0) + msg->SetBCastFlag(plMessage::kNetSent); + } + + // Decide if msg should get sent locally + if (!msg->HasBCastFlag(plMessage::kLocalPropagate)) + { + hsRefCnt_SafeUnRef(msg); + return true; + } + + // since we've already checked this property, and the msg will be dispatched locally, + // it should not start any more net cascades. + msg->SetBCastFlag(plMessage::kNetStartCascade, false); + + return false; +} + +hsBool plDispatch::MsgSend(plMessage* msg, hsBool async) +{ + if( IMsgNetPropagate(msg) ) + return true; + + plTimeMsg* timeMsg; + if( msg->GetTimeStamp() > hsTimer::GetSysSeconds() ) + return ISortToDeferred(msg); + else if( timeMsg = plTimeMsg::ConvertNoRef(msg) ) + ICheckDeferred(timeMsg->DSeconds()); + + plMsgWrap* msgWrap = TRACKED_NEW plMsgWrap(msg); + hsRefCnt_SafeUnRef(msg); + + // broadcast + if( msg->HasBCastFlag(plMessage::kBCastByExactType) | msg->HasBCastFlag(plMessage::kBCastByType) ) + { + int idx = msg->ClassIndex(); + if( idx < fRegisteredExactTypes.GetCount() ) + { + plTypeFilter* filt = fRegisteredExactTypes[idx]; + if( filt ) + { + int j; + for( j = 0; j < filt->fReceivers.GetCount(); j++ ) + { + msgWrap->AddReceiver(filt->fReceivers[j]); + } + if( msg->HasBCastFlag(plMessage::kClearAfterBCast) ) + { + delete filt; + fRegisteredExactTypes[idx] = nil; + } + } + } + } + // Direct communique + else + if( msg->GetNumReceivers() ) + { + msgWrap->fReceivers = msg->fReceivers; + } + IMsgEnqueue(msgWrap, async); + + return true; +} +void plDispatch::MsgQueueOnOff(hsBool sw) +{ + fQueuedMsgOn = sw; +} +void plDispatch::MsgQueue(plMessage* msg) +{ + if (fQueuedMsgOn) + { + fQueuedMsgListMutex.Lock(); + hsAssert(msg,"Message missing"); + fQueuedMsgList.push_back(msg); + fQueuedMsgListMutex.Unlock(); + } + else + MsgSend(msg, false); +} + +void plDispatch::MsgQueueProcess() +{ + // Process all messages on Queue, unlock while sending them + // this would allow other threads to put new messages on the list while we send() + while (1) + { + plMessage * pMsg = nil; + fQueuedMsgListMutex.Lock(); + int size = fQueuedMsgList.size(); + if (size) + { pMsg = fQueuedMsgList.front(); + fQueuedMsgList.pop_front(); + } + fQueuedMsgListMutex.Unlock(); + if (pMsg) + { MsgSend(pMsg, false); + } + if (!size) + break; + } +} + +void plDispatch::RegisterForType(UInt16 hClass, const plKey& receiver) +{ + int i; + for( i = 0; i < plFactory::GetNumClasses(); i++ ) + { + if( plFactory::DerivesFrom(hClass, i) ) + RegisterForExactType(i, receiver); + } +} + +void plDispatch::RegisterForExactType(UInt16 hClass, const plKey& receiver) +{ + int idx = hClass; + fRegisteredExactTypes.ExpandAndZero(idx+1); + plTypeFilter* filt = fRegisteredExactTypes[idx]; + if( !filt ) + { + filt = TRACKED_NEW plTypeFilter; + fRegisteredExactTypes[idx] = filt; + filt->fHClass = hClass; + } + + if( filt->fReceivers.kMissingIndex == filt->fReceivers.Find(receiver) ) + filt->fReceivers.Append(receiver); +} + +void plDispatch::UnRegisterForType(UInt16 hClass, const plKey& receiver) +{ + int i; + for( i = 0; i < fRegisteredExactTypes.GetCount(); i++ ) + { + if( plFactory::DerivesFrom(hClass, i) ) + IUnRegisterForExactType(i , receiver); + } + +} + +hsBool plDispatch::IUnRegisterForExactType(int idx, const plKey& receiver) +{ + hsAssert(idx < fRegisteredExactTypes.GetCount(), "Out of range should be filtered before call to internal"); + plTypeFilter* filt = fRegisteredExactTypes[idx]; + if (!filt) + return false; + int j; + for( j = 0; j < filt->fReceivers.GetCount(); j++ ) + { + if( receiver == filt->fReceivers[j] ) + { + if( filt->fReceivers.GetCount() > 1 ) + { + if( j < filt->fReceivers.GetCount() - 1 ) + filt->fReceivers[j] = filt->fReceivers[filt->fReceivers.GetCount() - 1]; + filt->fReceivers[filt->fReceivers.GetCount()-1] = nil; + filt->fReceivers.SetCount(filt->fReceivers.GetCount()-1); + } + else + { + delete filt; + fRegisteredExactTypes[idx] = nil; + } + + break; + } + } + return false; +} + +void plDispatch::UnRegisterAll(const plKey& receiver) +{ + int i; + for( i = 0; i < fRegisteredExactTypes.GetCount(); i++ ) + { + plTypeFilter* filt = fRegisteredExactTypes[i]; + if( filt ) + { + int idx = filt->fReceivers.Find(receiver); + if( idx != filt->fReceivers.kMissingIndex ) + { + if( filt->fReceivers.GetCount() > 1 ) + { + if( idx < filt->fReceivers.GetCount() - 1 ) + filt->fReceivers[idx] = filt->fReceivers[filt->fReceivers.GetCount() - 1]; + filt->fReceivers[filt->fReceivers.GetCount()-1] = nil; + filt->fReceivers.SetCount(filt->fReceivers.GetCount()-1); + } + else + { + delete filt; + fRegisteredExactTypes[i] = nil; + } + } + } + } +} + +void plDispatch::UnRegisterForExactType(UInt16 hClass, const plKey& receiver) +{ + int idx = hClass; + if( idx >= fRegisteredExactTypes.GetCount() ) + return; + plTypeFilter* filt = fRegisteredExactTypes[idx]; + if( !filt ) + return; + + IUnRegisterForExactType(idx, receiver); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatch.h new file mode 100644 index 00000000..442ec86c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatch.h @@ -0,0 +1,138 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDispatch_inc +#define plDispatch_inc +#include "hsTemplates.h" +#include "hsStlUtils.h" +#include "plgDispatch.h" +#include "hsThread.h" +#include "../pnKeyedObject/hsKeyedObject.h" + +#pragma warning(disable: 4284) + +class hsResMgr; +class plMessage; +class plKey; + +class plTypeFilter +{ +public: + plTypeFilter() : fHClass(0) {} + + UInt16 fHClass; + hsTArray fReceivers; +}; + +class plMsgWrap; + +typedef void (*MsgRecieveCallback)(); + +class plDispatch : public plDispatchBase +{ +protected: + + hsKeyedObject* fOwner; + + plMsgWrap* fFutureMsgQueue; + static Int32 fNumBufferReq; + static plMsgWrap* fMsgCurrent; + static hsMutex fMsgCurrentMutex; // mutex for above + static hsMutex fMsgDispatchLock; // mutex for IMsgDispatch + static plMsgWrap* fMsgHead; + static plMsgWrap* fMsgTail; + static hsBool fMsgActive; + static hsTArray fMsgWatch; + static MsgRecieveCallback fMsgRecieveCallback; + + hsTArray fRegisteredExactTypes; + std::list fQueuedMsgList; + hsMutex fQueuedMsgListMutex; // mutex for above + hsBool fQueuedMsgOn; // Turns on or off Queued Messages, Plugins need them off + + hsKeyedObject* IGetOwner() { return fOwner; } + plKey IGetOwnerKey() { return IGetOwner() ? IGetOwner()->GetKey() : nil; } + int IFindType(UInt16 hClass); + int IFindSender(const plKey& sender); + hsBool IUnRegisterForExactType(int idx, const plKey& receiver); + + static plMsgWrap* IInsertToQueue(plMsgWrap** back, plMsgWrap* isert); + static plMsgWrap* IDequeue(plMsgWrap** head, plMsgWrap** tail); + + hsBool IMsgNetPropagate(plMessage* msg); + + static void IMsgDispatch(); + static void IMsgEnqueue(plMsgWrap* msgWrap, hsBool async); + + hsBool ISortToDeferred(plMessage* msg); + void ICheckDeferred(double stamp); + hsBool IListeningForExactType(UInt16 hClass); + + void ITrashUndelivered(); // Just pitches them, doesn't try to deliver. + +public: + plDispatch(); + virtual ~plDispatch(); + + CLASSNAME_REGISTER( plDispatch ); + GETINTERFACE_ANY( plDispatch, plCreatable ); + + virtual void RegisterForType(UInt16 hClass, const plKey& receiver); + virtual void RegisterForExactType(UInt16 hClass, const plKey& receiver); + + virtual void UnRegisterForType(UInt16 hClass, const plKey& receiver); + virtual void UnRegisterForExactType(UInt16 hClass, const plKey& receiver); + + virtual void UnRegisterAll(const plKey& receiver); + + virtual hsBool MsgSend(plMessage* msg, hsBool async=false); + virtual void MsgQueue(plMessage* msg); // Used by other thread to Send Messages, they are handled as soon as Practicable + virtual void MsgQueueProcess(); + virtual void MsgQueueOnOff(hsBool ); // Turn on or off Queued Messages, if off, uses MsgSend Immediately + + virtual hsBool SetMsgBuffering(hsBool on); // On starts deferring msg delivery until buffering is set to off again. + + static void SetMsgRecieveCallback(MsgRecieveCallback callback) { fMsgRecieveCallback = callback; } +}; + +class plNullDispatch : public plDispatch +{ +public: + + virtual void RegisterForExactType(UInt16 hClass, const plKey& receiver) {} + virtual void RegisterForType(UInt16 hClass, const plKey& receiver) {} + + virtual void UnRegisterForExactType(UInt16 hClass, const plKey& receiver) {} + virtual void UnRegisterForType(UInt16 hClass, const plKey& receiver) {} + + + virtual hsBool MsgSend(plMessage* msg) {} + virtual void MsgQueue(plMessage* msg){} + virtual void MsgQueueProcess(){} + +}; + +#endif // plDispatch_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatchLogBase.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatchLogBase.cpp new file mode 100644 index 00000000..84952561 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatchLogBase.cpp @@ -0,0 +1,29 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plDispatchLogBase.h" + +plDispatchLogBase* plDispatchLogBase::fInstance = nil; +UInt32 plDispatchLogBase::fFlags = 0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatchLogBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatchLogBase.h new file mode 100644 index 00000000..4e9f092b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/plDispatchLogBase.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plDispatchLogBase_inc +#define plDispatchLogBase_inc + +#include "hsTypes.h" + +// +// For debugging messaging code. +// +class plMessage; +class plReceiver; + +class plDispatchLogBase +{ +public: + enum Flags + { + kInclude = 0x1, + kLogLongReceives = 0x2, + }; + +protected: + static UInt32 fFlags; + static plDispatchLogBase* fInstance; + +public: + static plDispatchLogBase* GetInstance() { return fInstance; } + + virtual ~plDispatchLogBase() {} + + static void SetFlags(UInt32 f) { fFlags=f; } + static UInt32 GetFlags() { return fFlags; } + + static bool IsLogging() { return fInstance != nil; } + static bool IsLoggingLong() { return (fFlags & kLogLongReceives) != 0; } + + virtual void AddFilterType(UInt16 type)=0; + virtual void AddFilterExactType(UInt16 type)=0; + + virtual void RemoveFilterType(UInt16 type)=0; + virtual void RemoveFilterExactType(UInt16 type)=0; + + virtual void LogStatusBarChange(const char* name, const char* action)=0; + virtual void LogLongReceive(const char* keyname, const char* className, UInt32 clonePlayerID, plMessage* msg, float ms)=0; + + virtual void DumpMsg(plMessage* msg, int numReceivers, int sendTime, Int32 indent)=0; +}; + +#endif // plDispatchLogBase_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/pnDispatchCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/pnDispatchCreatable.h new file mode 100644 index 00000000..b3c82955 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnDispatch/pnDispatchCreatable.h @@ -0,0 +1,36 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pnDispatchCreatable_inc +#define pnDispatchCreatable_inc + +#include "../pnFactory/plCreatable.h" + +#include "plDispatch.h" + +REGISTER_CREATABLE( plDispatch ); + +#endif // pnDispatchCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plCreatable.h new file mode 100644 index 00000000..b87a1653 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plCreatable.h @@ -0,0 +1,285 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCreatable_inc +#define plCreatable_inc + +#include "hsRefCnt.h" +#include "plFactory.h" + +class plCreator; +class hsStream; +class hsResMgr; + + +class plCreatable : public hsRefCnt +{ +public: + virtual const char* ClassName() const = 0; + virtual plCreatable* GetInterface(UInt16 hClass) { return nil; } + virtual const plCreatable* GetConstInterface(UInt16 hClass) const { return nil; } + static hsBool HasBaseClass(UInt16 hBase) { return false; } + virtual UInt16 ClassIndex() const = 0; + + virtual void Read(hsStream* s, hsResMgr* mgr) {} + virtual void Write(hsStream* s, hsResMgr* mgr) {} + + // WriteVersion writes the current version of this creatable and ReadVersion will read in + // any previous version. + virtual void ReadVersion(hsStream* s, hsResMgr* mgr) { hsAssert(0, "ReadVersion not implemented!"); } + virtual void WriteVersion(hsStream* s, hsResMgr* mgr) { hsAssert(0, "WriteVersion not implemented!"); } +}; + + +// Macros: +// NOTE: Comfortable use of these macros assumes the compiler is comfortable eating +// a spurious semi-colon (;) following a curly brace. If that isn't the case, they +// can easily be wrapped in something like do { original macro } while(0) or the like. +// +// Normal setup for a class: +// In public section of class declaration, insert the following two macros: +// CLASSNAME_REGISTER( myClassName ); +// GETINTERFACE_ANY( myClassName, classIWasDerivedFromName ); +// Then in the *Creatable.h file for that library (e.g. plSurfaceCreatable.h), add macro +// REGISTER_CREATABLE( myClassName ) +// Finally, add an enum to the plCreatableIndex.h file using CLASS_INDEX(className) +// ( e.g. CLASS_INDEX(hsGMaterial) ) +// +// CLASSNAME_REGISTER( plClassName ) - Sets up identification for this +// class. The exposed methods are +// static UInt16 Index() - returns the index for that class. +// virtual UInt16 ClassIndex() - returns index for this object's class. +// static plClassName* Convert(plCreatable* c) - if c exposes an interface +// as plClassName, return that, else nil. Incs the ref count of the object. +// static plClassName* ConvertNoRef(plCreatable* c) - Same as Convert(), but +// doesn't inc the ref count. +// static plClassName* Create() - returns a new object of type plClassName +// Insert into public section of class definition. +// +// Normally one of the next 3 macros should follow CLASSNAME_REGISTER +// GETINTERFACE_ANY - allows an interface to an object as plClassName if an object +// is or is derived from plClassName. +// GETINTERFACE_EXACT - allows an interface as plClassName only if the type is +// exactly of type plClassName +// GETINTERFACE_NONE - Never provide an interface as plClassName. +// Instead of using these macros, the class can provide a method +// virtual plCreatable* GetInterface(UInt16 hClass) which returns an object of +// type matching class handle hClass. +// Insert into public section of class definition (like right after CLASSNAME_REGISTER). +// +// REGISTER_CREATABLE( plClassName ) - normal creatable type, any you can instantiate. +// or +// REGISTER_NONCREATABLE( plClassName ) - can't be created either because it's pure virtual +// or just doesn't want to be creatable. It's Create member returns nil. But Convert +// may return an interface, depending on the GETINTERFACE above. +// - This line is the only exposure to the plCreator. +// This will define a Creator for class plClassName, instantiate it as a static, and register +// it with the Factory. The registration also sets the class index value in the plCreator +// subclass, as well as in the class being registered. +// Put after includes in the *Creatable.h file for the library the class belongs to.. +// +// USAGE: +// There is a method of identifying an object's type. You should rarely need it, +// using Create() and Convert() instead. +// ClassIndex() the class handle is an immutable index to this class. It provides an +// instantaneous lookup. It may be stored, loaded, sent over the wire, etc. +// +// Create() +// If you know what type object you want to create at compile time, use +// ::Create() +// But if you have a class index at run-time (e.g. loaded from file), use +// plCreatable* plFactory::Create(hClass); +// The ultra-safe way to do this is: +// plCreatable* tmp = plFactory::Create(idx); +// plWantClassName* p = plWantClassName::Convert(tmp); +// hsRefCnt_SafeUnRef(tmp); +// +// If you have a fred interface to an object f, and want a wilma interface, use +// fred* f = fred::Create(); more likely f was passed in. +// wilma* w = wilma::Convert(f) +// NOTE that two strange things may be true here: +// 1) f != nil, w == nil +// either fred's not really derived from wilma, +// or fred doesn't like to be cast down, +// or wilma just doesn't want to expose an interface. +// 2) f != nil, w != nil, and f != w +// fred has pulled a sneaky and created a wilma to return. +// so unrelated classes can still "Convert" as one another. +// +// +//////////////////////////// +// EAp - 01/10/2003 +// Added macros to support multiple AUX interfaces primarily, +// but they are not limited to that. Usage example: +// +// plBeginInterfaceMap( plMyClass, plBaseClass ); +// plAddInterfaceAux( plFooClass, fFooMember ); +// plAddInterfaceAux( plBarClass, fBarMember ); +// plAddInterface( plSomeOtherClass ); +// plEndInterfaceMap(); +// + + +#define CLASSNAME_REGISTER( plClassName ) \ +public: \ + virtual const char* ClassName() const { return #plClassName; } \ +private: \ + static UInt16 plClassName##ClassIndex; \ + static void SetClassIndex(UInt16 hClass) { \ + plClassName##ClassIndex = hClass; \ + } \ +public: \ + virtual UInt16 ClassIndex() const { \ + return plClassName::Index(); \ + } \ + static UInt16 Index() { \ + return plClassName##ClassIndex; \ + } \ + static plClassName * Create() { \ + return (plClassName*)plFactory::Create(plClassName##ClassIndex); \ + } \ + static plClassName * ConvertNoRef(plCreatable* c) { \ + plClassName* retVal = c \ + ? (plClassName *)c->GetInterface(plClassName##ClassIndex) \ + : nil; \ + return retVal; \ + } \ + static const plClassName * ConvertNoRef(const plCreatable* c) { \ + const plClassName* retVal = c \ + ? (const plClassName *)c->GetConstInterface(plClassName##ClassIndex) \ + : nil; \ + return retVal; \ + } \ + static plClassName * Convert(plCreatable* c) { \ + plClassName* retVal = ConvertNoRef(c); \ + hsRefCnt_SafeRef(retVal); \ + return retVal; \ + } \ + static hsBool HasDerivedClass(UInt16 hDer) { \ + return plFactory::DerivesFrom(plClassName##ClassIndex, hDer); \ + } \ + friend class plClassName##__Creator; + +#define GETINTERFACE_ANY( plClassName, plBaseName ) \ +static hsBool HasBaseClass(UInt16 hBaseClass) { \ + if( hBaseClass == plClassName##ClassIndex ) \ + return true; \ + else \ + return plBaseName::HasBaseClass(hBaseClass); \ + } \ +virtual plCreatable* GetInterface(UInt16 hClass) { \ + if( hClass == plClassName##ClassIndex ) \ + return this; \ + else \ + return plBaseName::GetInterface(hClass); \ +} \ +virtual const plCreatable* GetConstInterface(UInt16 hClass) const { \ + if( hClass == plClassName##ClassIndex ) \ + return this; \ + else \ + return plBaseName::GetConstInterface(hClass); \ +} + +#define GETINTERFACE_EXACT( plClassName ) \ + static hsBool HasBaseClass(UInt16 hBaseClass) { \ + return hBaseClass == plClassName##ClassIndex; \ + } \ +virtual plCreatable* GetInterface(UInt16 hClass) { \ + return hClass == plClassName##ClassIndex ? this : nil; \ +} \ +virtual const plCreatable* GetConstInterface(UInt16 hClass) const { \ + return hClass == plClassName##ClassIndex ? this : nil; \ +} + +#define GETINTERFACE_NONE( plClassName ) \ +static hsBool HasBaseClass(UInt16 hBaseClass) { return false; } \ +virtual plCreatable* GetInterface(UInt16 hClass) { \ + return nil; \ +} \ +virtual const plCreatable* GetConstInterface(UInt16 hClass) const { \ + return nil; \ +} + +// +// Macro for converting to base class OR a class member +// +#define GETINTERFACE_ANY_AUX( plClassName, plBaseName, plAuxClassName, plAuxClassMember ) \ +static hsBool HasBaseClass(UInt16 hBaseClass) { \ + if( hBaseClass == plClassName##ClassIndex ) \ + return true; \ + else \ + return plBaseName::HasBaseClass(hBaseClass); \ + } \ +virtual plCreatable* GetInterface(UInt16 hClass) { \ + if( hClass == plClassName##ClassIndex ) \ + return this; \ + else \ + if (hClass == plAuxClassName::Index()) \ + return &plAuxClassMember; \ + else \ + return plBaseName::GetInterface(hClass); \ +} \ +virtual const plCreatable* GetConstInterface(UInt16 hClass) const { \ + if( hClass == plClassName##ClassIndex ) \ + return this; \ + else \ + if (hClass == plAuxClassName::Index()) \ + return &plAuxClassMember; \ + else \ + return plBaseName::GetConstInterface(hClass); \ +} + +#define plBeginInterfaceMap( plClassName, plBaseName ) \ +static hsBool HasBaseClass(UInt16 hBaseClass) { \ + if( hBaseClass == plClassName##ClassIndex ) \ + return true; \ + else \ + return plBaseName::HasBaseClass(hBaseClass); \ + } \ +virtual plCreatable* GetInterface(UInt16 hClass) { \ + /* NOTE: pulling const off the ptr should be ok, right? */ \ + return const_cast( GetConstInterface( hClass ) ); \ +} \ +virtual const plCreatable* GetConstInterface(UInt16 hClass) const { \ + typedef plBaseName MyBaseClass; \ + if( hClass == plClassName##ClassIndex ) \ + return this + +#define plAddInterface( plClassName ) \ + else if ( hClass == plClassName::Index() ) \ + return plClassName::GetConstInterface(hClass) + +#define plAddInterfaceAux( plAuxClassName, plAuxClassMember ) \ + else if ( hClass == plAuxClassName::Index() ) \ + return &plAuxClassMember + +#define plEndInterfaceMap() \ + else \ + return MyBaseClass::GetConstInterface(hClass); \ +} + + +#endif // plCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plCreator.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plCreator.h new file mode 100644 index 00000000..52c8ead7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plCreator.h @@ -0,0 +1,171 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCreator_inc +#define plCreator_inc + +#include "plClassIndexMacros.h" +#include "plCreatableIndex.h" +#include "plFactory.h" + +class plCreatable; + +class plCreator +{ +private: +protected: + +public: + plCreator() { } + virtual ~plCreator() { } + + virtual plCreatable* Create() const = 0; + virtual UInt16 ClassIndex() = 0; + virtual const char* ClassName() const = 0; + virtual hsBool HasBaseClass(UInt16 hBase) = 0; + + friend class plFactory; +}; + +#define REGISTER_CREATABLE( plClassName ) \ + \ +class plClassName##__Creator : public plCreator \ +{ \ +public: \ + plClassName##__Creator() \ + { \ + plFactory::Register( CLASS_INDEX_SCOPED(plClassName), this); \ + plClassName::SetClassIndex(ClassIndex()); \ + } \ + virtual ~plClassName##__Creator() \ + { \ + plFactory::UnRegister(CLASS_INDEX_SCOPED(plClassName), this); \ + } \ + \ + virtual hsBool HasBaseClass(UInt16 hBase) { return plClassName::HasBaseClass(hBase); } \ + \ + virtual UInt16 ClassIndex() { return CLASS_INDEX_SCOPED(plClassName); } \ + virtual const char* ClassName() const { return #plClassName; } \ + \ + virtual plCreatable* Create() const { return TRACKED_NEW plClassName; } \ + \ +}; \ +static plClassName##__Creator static##plClassName##__Creator; \ +UInt16 plClassName::plClassName##ClassIndex = 0; // + +#define REGISTER_NONCREATABLE( plClassName ) \ + \ +class plClassName##__Creator : public plCreator \ +{ \ +public: \ + plClassName##__Creator() \ + { \ + plFactory::Register( CLASS_INDEX_SCOPED(plClassName), this); \ + plClassName::SetClassIndex(ClassIndex()); \ + } \ + virtual ~plClassName##__Creator() \ + { \ + plFactory::UnRegister(CLASS_INDEX_SCOPED(plClassName), this); \ + } \ + \ + virtual hsBool HasBaseClass(UInt16 hBase) { return plClassName::HasBaseClass(hBase); } \ + \ + virtual UInt16 ClassIndex() { return CLASS_INDEX_SCOPED(plClassName); } \ + virtual const char* ClassName() const { return #plClassName; } \ + \ + virtual plCreatable* Create() const { return nil; } \ + \ +}; \ +static plClassName##__Creator static##plClassName##__Creator; \ +UInt16 plClassName::plClassName##ClassIndex = 0; // + +#define DECLARE_EXTERNAL_CREATABLE( plClassName ) \ + \ +class plClassName##__Creator : public plCreator \ +{ \ +public: \ + plClassName##__Creator() \ + { \ + } \ + virtual ~plClassName##__Creator() \ + { \ + plFactory::UnRegister(EXTERN_CLASS_INDEX_SCOPED(plClassName), this); \ + } \ + void Register() \ + { \ + plFactory::Register( EXTERN_CLASS_INDEX_SCOPED(plClassName), this); \ + plClassName::SetClassIndex(ClassIndex()); \ + } \ + void UnRegister() \ + { \ + plFactory::UnRegister(EXTERN_CLASS_INDEX_SCOPED(plClassName), this); \ + } \ + \ + virtual hsBool HasBaseClass(UInt16 hBase) { return plClassName::HasBaseClass(hBase); } \ + \ + virtual UInt16 ClassIndex() { return EXTERN_CLASS_INDEX_SCOPED(plClassName); } \ + virtual const char* ClassName() const { return #plClassName; } \ + \ + virtual plCreatable* Create() const { return TRACKED_NEW plClassName; } \ + \ +}; \ +static plClassName##__Creator static##plClassName##__Creator; \ +UInt16 plClassName::plClassName##ClassIndex = 0; // + +#define REGISTER_EXTERNAL_CREATABLE(plClassName) \ +static##plClassName##__Creator.Register(); // + +#define UNREGISTER_EXTERNAL_CREATABLE(plClassName) \ +plFactory::UnRegister(EXTERN_CLASS_INDEX_SCOPED(plClassName), &static##plClassName##__Creator); + +#define REGISTER_EXTERNAL_NONCREATABLE( plClassName ) \ + \ +class plClassName##__Creator : public plCreator \ +{ \ +public: \ + plClassName##__Creator() \ + { \ + plFactory::Register( EXTERN_CLASS_INDEX_SCOPED(plClassName), this); \ + plClassName::SetClassIndex(ClassIndex()); \ + } \ + virtual ~plClassName##__Creator() \ + { \ + plFactory::UnRegister(EXTERN_CLASS_INDEX_SCOPED(plClassName), this); \ + } \ + \ + virtual hsBool HasBaseClass(UInt16 hBase) { return plClassName::HasBaseClass(hBase); } \ + \ + virtual UInt16 ClassIndex() { return EXTERN_CLASS_INDEX_SCOPED(plClassName); } \ + virtual const char* ClassName() const { return #plClassName; } \ + \ + virtual plCreatable* Create() const { return nil; } \ + \ +}; \ +static plClassName##__Creator static##plClassName##__Creator; \ +UInt16 plClassName::plClassName##ClassIndex = 0; // + + +#endif // plCreator_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plFactory.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plFactory.cpp new file mode 100644 index 00000000..42310738 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plFactory.cpp @@ -0,0 +1,341 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#define PLFACTORY_PRIVATE +#include "hsTypes.h" +#include "plFactory.h" +#include "hsStream.h" +#include "plCreatable.h" +#include "plCreator.h" +#include "hsUtils.h" + +// For class names +#include "../NucleusLib/inc/plCreatableStrings.h" + + +static plFactory* theFactory = nil; + +plFactory::plFactory() +{ + fCreators.SetCountAndZero(plCreatableIndex::plNumClassIndices); +} + +plFactory::~plFactory() +{ +} + +hsBool plFactory::ICreateTheFactory() +{ + if( theFactory ) + return true; + + theFactory = TRACKED_NEW plFactory; + + return theFactory != nil; +} + +UInt16 plFactory::IGetNumClasses() +{ + return plCreatableIndex::plNumClassIndices; +} + +void plFactory::IForceShutdown() +{ + int i; + for( i = 0; i < fCreators.GetCount(); i++ ) + { + if( fCreators[i] ) + { + hsRefCnt_SafeUnRef(this); + fCreators[i] = nil; + } + } +} + +void plFactory::IShutdown() +{ + delete theFactory; + theFactory = nil; +} + +UInt16 plFactory::IRegister(UInt16 hClass, plCreator* worker) +{ + delete fCreators[hClass]; + fCreators[hClass] = worker; + return hClass; +} + +// +// return true if creator exists +// +bool plFactory::CanCreate(UInt16 hClass) +{ + if( hClass & 0x8000 ) // nil creatable + return false; + + if( !theFactory && !ICreateTheFactory() ) // no factory + return false; + + if( hClass >= theFactory->IGetNumClasses() ) // invalid index + return false; + + return ( theFactory->fCreators[ hClass ] != nil ); // check creator +} + +plCreatable* plFactory::ICreate(UInt16 hClass) +{ + if (CanCreate(hClass)) + { + return fCreators[hClass]->Create(); + } + + if (!(hClass & 0x8000)) + { + hsAssert( false, "Invalid class index or nil creator : plFactory::Create()" ); + } + return nil; +} + +void plFactory::UnRegister(UInt16 hClass, plCreator* worker) +{ + if( theFactory ) + { + theFactory->IUnRegister(hClass); + hsRefCnt_SafeUnRef(theFactory); + if( theFactory->RefCnt() < 2 ) + IShutdown(); + } +} + +void plFactory::IUnRegister(UInt16 hClass) +{ + fCreators[hClass] = nil; +} + +UInt16 plFactory::Register(UInt16 hClass, plCreator* worker) +{ + if( !theFactory && !ICreateTheFactory() ) + return nil; + + hsRefCnt_SafeRef(theFactory); + return theFactory->IRegister(hClass, worker); +} + +plCreatable* plFactory::Create(UInt16 hClass) +{ + if( !theFactory && !ICreateTheFactory() ) + return nil; + + return theFactory->ICreate(hClass); +} + + + +UInt16 plFactory::GetNumClasses() +{ + if( !theFactory && !ICreateTheFactory() ) + return 0; + + return theFactory->IGetNumClasses(); +} + +hsBool plFactory::IDerivesFrom(UInt16 hBase, UInt16 hDer) +{ + if( hDer >= fCreators.GetCount() ) + return false; + + return fCreators[hDer] ? fCreators[hDer]->HasBaseClass(hBase) : false; +} + +hsBool plFactory::DerivesFrom(UInt16 hBase, UInt16 hDer) +{ + if( !theFactory && !ICreateTheFactory() ) + return 0; + + return theFactory->IDerivesFrom(hBase, hDer); +} + +// slow lookup for things like console +UInt16 plFactory::FindClassIndex(const char* className) +{ + int numClasses=GetNumClasses(); + + if (className && theFactory) + { + int i; + for( i = 0; i < theFactory->fCreators.GetCount(); i++ ) + { + if( theFactory->fCreators[i] && !_stricmp(className, theFactory->fCreators[i]->ClassName()) ) + { + return theFactory->fCreators[i]->ClassIndex(); + } + } + } + return numClasses; // err +} + + +hsBool plFactory::IIsValidClassIndex(UInt16 hClass) +{ + return ( hClass < fCreators.GetCount() ); +} + +hsBool plFactory::IsValidClassIndex(UInt16 hClass) +{ + return theFactory->IIsValidClassIndex(hClass); +} + +void plFactory::SetTheFactory(plFactory* fac) +{ + // There are four cases here. + // 1) Our factory is nil, and we're being given one to use + // Just take it and ref it. + // 2) Our factory is non-nil, and we're being given on to use + // Ours is bogus, pitch it and use the new one. + // 3) Our factory is non-nil, and we're being given a nil one + // Means we're being shut down. Unref the old one. If + // the refcnt drops to one, we're the last one out, so + // go ahead and delete it. + // 4) Our factory is nil and the new one is nil + // Shouldn't happen, but if it does, just ignore it. + if( !theFactory && fac ) + { + hsRefCnt_SafeAssign(theFactory, fac); + } + else if( theFactory && fac ) + { + theFactory->IForceShutdown(); + hsRefCnt_SafeAssign(theFactory, fac); + } + else if( theFactory && !fac ) + { + hsRefCnt_SafeUnRef(theFactory); + if( theFactory->RefCnt() < 2 ) + delete theFactory; + theFactory = nil; + } + +} + +plFactory* plFactory::GetTheFactory() +{ + if( !theFactory && !ICreateTheFactory() ) + return nil; + + return theFactory; +} + +// For my own nefarious purposes... hsStatusMessage plCreatableIndex +const char *plFactory::GetNameOfClass(UInt16 type) +{ + if( type < GetNumClasses() ) + { + if( theFactory->fCreators[ type ] ) + return theFactory->fCreators[ type ]->ClassName(); + + static int keyedStringsSize = sizeof(plCreatableStrings::fKeyedStrings)/4; + // If we don't have a creator yet, try falling back on plCreatableStrings + if( type < KEYED_OBJ_DELINEATOR && type= KEYED_OBJ_DELINEATOR && theFactory->fCreators[iter]) + { + char Buffer[512]; + sprintf(Buffer, "Object %s is a hsKeyedObject, Must appear before 'KEYED_OBJ_DELINEATOR' in plCreatableIndex.h\n",GetNameOfClass(iter)); + hsStatusMessage(Buffer); + bogus = true; + } + + } + else + { + if(iter < KEYED_OBJ_DELINEATOR && theFactory->fCreators[iter]) + { + char Buffer[512]; + sprintf(Buffer, "Object %s is NOT a hsKeyedObject, Must appear after 'KEYED_OBJ_DELINEATOR' in plCreatableIndex.h\n",GetNameOfClass(iter)); + hsStatusMessage(Buffer); + bogus = true; + + } + } + } + hsAssert(!bogus,"The class(s) you just added to plCreatableIndex.h in wrong spot, see output window"); + +} + +void plFactory::Validate(UInt16 keyIndex) +{ + theFactory->IValidate(keyIndex); + +} + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plFactory.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plFactory.h new file mode 100644 index 00000000..d384cf0f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnFactory/plFactory.h @@ -0,0 +1,96 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plFactory_inc +#define plFactory_inc + +#ifdef PLFACTORY_PRIVATE + #include "hsTemplates.h" +#endif +#include "hsRefCnt.h" +#include "hsTypes.h" + +class plCreator; +class plCreatable; +class hsStream; +class hsResMgr; + +class plFactory : public hsRefCnt +{ +#ifdef PLFACTORY_PRIVATE +private: + hsTArray fCreators; + + void IForceShutdown(); + void IUnRegister(UInt16 hClass); + UInt16 IRegister(UInt16 hClass, plCreator* worker); + hsBool IIsEmpty(); + UInt16 IGetNumClasses(); + plCreatable* ICreate(UInt16 hClass); + hsBool IDerivesFrom(UInt16 hBase, UInt16 hDer); + hsBool IIsValidClassIndex(UInt16 hClass); + + static hsBool ICreateTheFactory(); + static void IShutdown(); + + plFactory(); + ~plFactory(); +#endif + +public: + // Don't use this unless you're initializing a DLL + friend class plClient; + static plFactory* GetTheFactory(); + + + static UInt16 Register(UInt16 hClass, plCreator* worker); // returns hClass + static void UnRegister(UInt16 hClass, plCreator* worker); + + static bool CanCreate(UInt16 hClass); // return true if creator exists. doesn't assert + static plCreatable* Create(UInt16 hClass); + + static hsBool DerivesFrom(UInt16 hBase, UInt16 hDer); + + static UInt16 GetNumClasses(); + + static UInt16 FindClassIndex(const char* className); // slow lookup for things like console + + static hsBool IsValidClassIndex(UInt16 hClass); + + // Don't call this unless you're a DLL being initialized. + static void SetTheFactory(plFactory* fac); + + static const char *GetNameOfClass(UInt16 type); + +#ifdef HS_DEBUGGING + void IValidate(UInt16 keyIndex); + static void Validate(UInt16 keyIndex); +#endif +}; + + + +#endif // plFactory_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/BlueSpiral/pnGmBlueSpiral.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/BlueSpiral/pnGmBlueSpiral.cpp new file mode 100644 index 00000000..b3393947 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/BlueSpiral/pnGmBlueSpiral.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/BlueSpiral/pnGmBlueSpiral.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/BlueSpiral/pnGmBlueSpiral.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/BlueSpiral/pnGmBlueSpiral.h new file mode 100644 index 00000000..e682731c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/BlueSpiral/pnGmBlueSpiral.h @@ -0,0 +1,122 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/BlueSpiral/pnGmBlueSpiral.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_BLUESPIRAL_PNGMBLUESPIRAL_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/BlueSpiral/pnGmBlueSpiral.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_BLUESPIRAL_PNGMBLUESPIRAL_H + + +/***************************************************************************** +* +* BlueSpiral +* +***/ + +enum EBlueSpiralInitResult { + kBlueSpiralInitSuccess, + kBlueSpiralInitError, + kNumBlueSpiralInitResults +}; + + +//============================================================================ +// Game type id +//============================================================================ + +const Uuid kGameTypeId_BlueSpiral = Uuid(L"5ff98165-913e-4fd1-a2c2-9c7f31be2cc8"); + + +//============================================================================ +// Network message ids +//============================================================================ + +// Cli2Srv message ids +enum { + kCli2Srv_BlueSpiral_StartGame = kCli2Srv_NumGameMsgIds, + kCli2Srv_BlueSpiral_HitCloth, +}; + +// Srv2Cli message ids +enum { + kSrv2Cli_BlueSpiral_ClothOrder = kSrv2Cli_NumGameMsgIds, + kSrv2Cli_BlueSpiral_SuccessfulHit, + kSrv2Cli_BlueSpiral_GameWon, + kSrv2Cli_BlueSpiral_GameOver, // sent on time out and incorrect entry + kSrv2Cli_BlueSpiral_GameStarted, +}; + + +//============================================================================ +// Begin networked data scructures +#include +//============================================================================ + + //======================================================================== + // Message parameters + //======================================================================== + struct BlueSpiral_CreateParam { + // empty + }; + + //======================================================================== + // Tic-Tac-Toe message structures + //======================================================================== + + // Cli2Srv + struct Cli2Srv_BlueSpiral_StartGame : GameMsgHeader { + // empty + }; + struct Cli2Srv_BlueSpiral_HitCloth : GameMsgHeader { + byte clothNum; // the cloth we hit, 0..6 + }; + + // Srv2Cli + struct Srv2Cli_BlueSpiral_ClothOrder : GameMsgHeader { + byte order[7]; // each value is the cloth to hit, 0..6, the order is the order in the array + }; + struct Srv2Cli_BlueSpiral_SuccessfulHit : GameMsgHeader { + // empty + }; + struct Srv2Cli_BlueSpiral_GameWon : GameMsgHeader { + // empty + }; + struct Srv2Cli_BlueSpiral_GameOver : GameMsgHeader { + // empty + }; + struct Srv2Cli_BlueSpiral_GameStarted : GameMsgHeader { + bool startSpin; // if true, start spinning the door thingy + }; + +//============================================================================ +// End networked data structures +#include +//============================================================================ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/ClimbingWall/pnGmClimbingWall.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/ClimbingWall/pnGmClimbingWall.cpp new file mode 100644 index 00000000..ab3c9d15 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/ClimbingWall/pnGmClimbingWall.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/ClimbingWall/pnGmClimbingWall.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/ClimbingWall/pnGmClimbingWall.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/ClimbingWall/pnGmClimbingWall.h new file mode 100644 index 00000000..de58f7ca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/ClimbingWall/pnGmClimbingWall.h @@ -0,0 +1,168 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/ClimbingWall/pnGmClimbingWall.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_CLIMBINGWALL_PNGMCLIMBINGWALL_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/ClimbingWall/pnGmClimbingWall.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_CLIMBINGWALL_PNGMCLIMBINGWALL_H + + +/***************************************************************************** +* +* Climbing Wall +* +***/ + +enum EClimbingWallInitResult { + kClimbingWallInitSuccess, + kClimbingWallInitError, + kNumClimbingWallInitResults +}; +enum EClimbingWallReadyType { + kClimbingWallReadyNumBlockers, + kClimbingWallReadyBlockers, +}; + +const unsigned kClimbingWallMaxBlockers = 20; // TODO: Adjust this to the right size +const int kClimbingWallNoBlocker = -1; // the value of a slot in the blocker array when no blocker is in that slot + + +//============================================================================ +// Game type id +//============================================================================ + +const Uuid kGameTypeId_ClimbingWall = Uuid(L"6224cdf4-3556-4740-b7cd-d637562d07be"); + + +//============================================================================ +// Network message ids +//============================================================================ + +// Cli2Srv message ids +enum { + kCli2Srv_ClimbingWall_ChangeNumBlockers = kCli2Srv_NumGameMsgIds, + kCli2Srv_ClimbingWall_Ready, + kCli2Srv_ClimbingWall_BlockerChanged, + kCli2Srv_ClimbingWall_Reset, + kCli2Srv_ClimbingWall_PlayerEntered, + kCli2Srv_ClimbingWall_FinishedGame, + kCli2Srv_ClimbingWall_Panic, +}; + +// Srv2Cli message ids +enum { + kSrv2Cli_ClimbingWall_NumBlockersChanged = kSrv2Cli_NumGameMsgIds, + kSrv2Cli_ClimbingWall_Ready, + kSrv2Cli_ClimbingWall_BlockersChanged, + kSrv2Cli_ClimbingWall_PlayerEntered, + kSrv2Cli_ClimbingWall_SuitMachineLocked, + kSrv2Cli_ClimbingWall_GameOver, +}; + + +//============================================================================ +// Begin networked data structures +#include +//============================================================================ + + //======================================================================== + // Message parameters + //======================================================================== + struct ClimbingWall_CreateParam { + // no params + }; + + //======================================================================== + // Climbing Wall message structures + //======================================================================== + + // Cli2Srv + struct Cli2Srv_ClimbingWall_ChangeNumBlockers : GameMsgHeader { + int amountToAdjust; // + or - value to adjust the number of blockers by + }; + struct Cli2Srv_ClimbingWall_Ready : GameMsgHeader { + byte readyType; // the type of ready this message represents (EClimbingWallReadyType) + byte teamNumber; // the team that you are saying is ready (1 or 2) + }; + struct Cli2Srv_ClimbingWall_BlockerChanged : GameMsgHeader { + byte teamNumber; // the team that is adjusting their blockers + byte blockerNumber; // the number of the blocker that was added/removed + bool added; // was the blocker added, or removed? + }; + struct Cli2Srv_ClimbingWall_Reset : GameMsgHeader { + // + }; + struct Cli2Srv_ClimbingWall_PlayerEntered : GameMsgHeader { + byte teamNumber; // the team this player is playing for + }; + struct Cli2Srv_ClimbingWall_FinishedGame : GameMsgHeader { + // + }; + struct Cli2Srv_ClimbingWall_Panic : GameMsgHeader { + // + }; + + // Srv2Cli + struct Srv2Cli_ClimbingWall_NumBlockersChanged : GameMsgHeader { + byte newBlockerCount; // the new number of blocker we are playing with + bool localOnly; // only adjust your local display, don't net prop + }; + struct Srv2Cli_ClimbingWall_Ready : GameMsgHeader { + byte readyType; // the type of ready this message represents (EClimbingWallReadyType) + bool team1Ready; + bool team2Ready; + bool localOnly; // only adjust your local display, don't net prop + }; + struct Srv2Cli_ClimbingWall_BlockersChanged : GameMsgHeader { + byte teamNumber; // the team this set of blockers is for + int blockersSet[kClimbingWallMaxBlockers]; // which blockers are set + bool localOnly; // only adjust your local display, don't net prop + }; + struct Srv2Cli_ClimbingWall_PlayerEntered : GameMsgHeader { + // + }; + struct Srv2Cli_ClimbingWall_SuitMachineLocked : GameMsgHeader { + bool team1MachineLocked; + bool team2MachineLocked; + bool localOnly; // only adjust your local display, don't net prop + }; + struct Srv2Cli_ClimbingWall_GameOver : GameMsgHeader { + byte teamWon; // which team won the game + int team1Blockers[kClimbingWallMaxBlockers]; + int team2Blockers[kClimbingWallMaxBlockers]; + bool localOnly; // only adjust your local display, don't net prop + }; + + +//============================================================================ +// End networked data structures +#include +//============================================================================ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Heek/pnGmHeek.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Heek/pnGmHeek.cpp new file mode 100644 index 00000000..7e352e93 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Heek/pnGmHeek.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Heek/pnGmHeek.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Heek/pnGmHeek.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Heek/pnGmHeek.h new file mode 100644 index 00000000..94376bc2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Heek/pnGmHeek.h @@ -0,0 +1,187 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Heek/pnGmHeek.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_HEEK_PNGMHEEK_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Heek/pnGmHeek.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_HEEK_PNGMHEEK_H + + +/***************************************************************************** +* +* Heek +* +***/ + +enum EHeekInitResult { + kHeekInitSuccess, + kHeekInitError, + kNumHeekInitResults +}; +enum EHeekChoice { + kHeekRock, + kHeekPaper, + kHeekScissors, + kNumHeekChoices +}; +enum EHeekSeqFinished { + kHeekCountdownSeq, + kHeekChoiceAnimSeq, + kHeekGameWinAnimSeq, + kNumHeekSeq +}; +enum EHeekLightState { + kHeekLightOn, + kHeekLightOff, + kHeekLightFlash, + kNumHeekLightStates +}; +enum EHeekCountdownState { + kHeekCountdownStart, + kHeekCountdownStop, + kHeekCountdownIdle, + kNumHeekCountdownStates +}; + +//============================================================================ +// Game type id +//============================================================================ + +const Uuid kGameTypeId_Heek = Uuid(L"9d83c2e2-7835-4477-9aaa-22254c59a753"); + + +//============================================================================ +// Network message ids +//============================================================================ + +// Cli2Srv message ids +enum { + kCli2Srv_Heek_PlayGame = kCli2Srv_NumGameMsgIds, // Sent when a player wants to join in the game (instead of observing) + kCli2Srv_Heek_LeaveGame, // Sent when a player is done playing (and starts observing) + kCli2Srv_Heek_Choose, // Sent when a player choses a move + kCli2Srv_Heek_SeqFinished, // Sent when a client-side animation ends +}; + +// Srv2Cli message ids +enum { + kSrv2Cli_Heek_PlayGame = kSrv2Cli_NumGameMsgIds, // Sent when the server allows or disallows a player to play + kSrv2Cli_Heek_Goodbye, // Sent when the server confirms the player leaving + kSrv2Cli_Heek_Welcome, // Sent to everyone when a new player joins + kSrv2Cli_Heek_Drop, // Sent when the admin needs to reset a position + kSrv2Cli_Heek_Setup, // Sent on link-in so observers see the correct game state (fast-forwarded) + kSrv2Cli_Heek_LightState, // Sent to a player when a light he owns changes state (animated) + kSrv2Cli_Heek_InterfaceState, // Sent to a player when his buttons change state (animated) + kSrv2Cli_Heek_CountdownState, // Sent to the admin to adjust the countdown state + kSrv2Cli_Heek_WinLose, // Sent to a player when he wins or loses a hand + kSrv2Cli_Heek_GameWin, // Sent to the admin when a game is won + kSrv2Cli_Heek_PointUpdate, // Sent to a player when their points change +}; + + +//============================================================================ +// Begin networked data structures +#include +//============================================================================ + + //======================================================================== + // Message parameters + //======================================================================== + // No creation parameters + + //======================================================================== + // Heek message structures + //======================================================================== + + // Cli2Srv + struct Cli2Srv_Heek_PlayGame : GameMsgHeader { + byte position; // 0...4 + dword points; + wchar name[256]; + }; + struct Cli2Srv_Heek_LeaveGame : GameMsgHeader { + // no extra data + }; + struct Cli2Srv_Heek_Choose : GameMsgHeader { + byte choice; // kHeekRock...kHeekScissors + }; + struct Cli2Srv_Heek_SeqFinished : GameMsgHeader { + byte seqFinished; // kHeekCountdownSeq...kHeekGameWinSeq + }; + + // Srv2Cli + struct Srv2Cli_Heek_PlayGame : GameMsgHeader { + bool isPlaying; + bool isSinglePlayer; + bool enableButtons; + }; + struct Srv2Cli_Heek_Goodbye : GameMsgHeader { + // no extra data + }; + struct Srv2Cli_Heek_Welcome : GameMsgHeader { + dword points; + dword rank; + wchar name[256]; + }; + struct Srv2Cli_Heek_Drop : GameMsgHeader { + byte position; // 0...4 + }; + struct Srv2Cli_Heek_Setup : GameMsgHeader { + byte position; // 0...4 + bool buttonState; + bool lightOn[6]; + }; + struct Srv2Cli_Heek_LightState : GameMsgHeader { + byte lightNum; + byte state; // kHeekLightOn...kHeekLightFlash + }; + struct Srv2Cli_Heek_InterfaceState : GameMsgHeader { + bool buttonsEnabled; + }; + struct Srv2Cli_Heek_CountdownState : GameMsgHeader { + byte state; // kHeekCountdownStart...kHeekCountdownIdle + }; + struct Srv2Cli_Heek_WinLose : GameMsgHeader { + bool win; + byte choice; // kHeekRock...kHeekScissors + }; + struct Srv2Cli_Heek_GameWin : GameMsgHeader { + byte choice; // kHeekRock...kHeekScissors + }; + struct Srv2Cli_Heek_PointUpdate : GameMsgHeader { + bool displayUpdate; + dword points; + dword rank; + }; + +//============================================================================ +// End networked data structures +#include +//============================================================================ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Intern.h new file mode 100644 index 00000000..7da9466d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Intern.h @@ -0,0 +1,35 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGamesCommon/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMESCOMMON_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnGamesCommon/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMESCOMMON_INTERN_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Marker/pnGmMarker.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Marker/pnGmMarker.cpp new file mode 100644 index 00000000..2de7ac4b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Marker/pnGmMarker.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Marker/pnGmMarker.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Marker/pnGmMarker.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Marker/pnGmMarker.h new file mode 100644 index 00000000..c5c77d22 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Marker/pnGmMarker.h @@ -0,0 +1,213 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Marker/pnGmMarker.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_MARKER_PNGMMARKER_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Marker/pnGmMarker.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_MARKER_PNGMMARKER_H + + +/***************************************************************************** +* +* Marker +* +***/ + +enum EMarkerInitResult { + kMarkerInitSuccess, + kMarkerInitError, + kNumMarkerInitResults +}; +enum EMarkerGameType { + kMarkerGameQuest, + kMarkerGameCGZ, // this is a quest game, but differentiating between the two on the client side makes some things easier + kMarkerGameCapture, + kMarkerGameCaptureAndHold, + kNumMarkerGameTypes +}; + + +//============================================================================ +// Game type id +//============================================================================ + +const Uuid kGameTypeId_Marker = Uuid(L"000b2c39-0319-4be1-b06c-7a105b160fcf"); + + +//============================================================================ +// Network message ids +//============================================================================ + +// Cli2Srv message ids +enum { + kCli2Srv_Marker_StartGame = kCli2Srv_NumGameMsgIds, + kCli2Srv_Marker_PauseGame, + kCli2Srv_Marker_ResetGame, + kCli2Srv_Marker_ChangeGameName, + kCli2Srv_Marker_ChangeTimeLimit, + kCli2Srv_Marker_DeleteGame, + kCli2Srv_Marker_AddMarker, + kCli2Srv_Marker_DeleteMarker, + kCli2Srv_Marker_ChangeMarkerName, + kCli2Srv_Marker_CaptureMarker, +}; + +// Srv2Cli message ids +enum { + kSrv2Cli_Marker_TemplateCreated = kSrv2Cli_NumGameMsgIds, + kSrv2Cli_Marker_TeamAssigned, + kSrv2Cli_Marker_GameType, + kSrv2Cli_Marker_GameStarted, + kSrv2Cli_Marker_GamePaused, + kSrv2Cli_Marker_GameReset, + kSrv2Cli_Marker_GameOver, + kSrv2Cli_Marker_GameNameChanged, + kSrv2Cli_Marker_TimeLimitChanged, + kSrv2Cli_Marker_GameDeleted, + kSrv2Cli_Marker_MarkerAdded, + kSrv2Cli_Marker_MarkerDeleted, + kSrv2Cli_Marker_MarkerNameChanged, + kSrv2Cli_Marker_MarkerCaptured, +}; + + +//============================================================================ +// Begin networked data structures +#include +//============================================================================ + + //======================================================================== + // Message parameters + //======================================================================== + struct Marker_CreateParam { + byte gameType; // member of EMarkerGameType + wchar gameName[256]; + dword timeLimit; + wchar templateID[80]; // empty if creating a new game, guid if a quest game and we need to grab the data from the state server + }; + + //======================================================================== + // Tic-Tac-Toe message structures + //======================================================================== + + // Cli2Srv + struct Cli2Srv_Marker_StartGame : GameMsgHeader { + // nothing + }; + struct Cli2Srv_Marker_PauseGame : GameMsgHeader { + // nothing + }; + struct Cli2Srv_Marker_ResetGame : GameMsgHeader { + // nothing + }; + struct Cli2Srv_Marker_ChangeGameName : GameMsgHeader { + wchar gameName[256]; + }; + struct Cli2Srv_Marker_ChangeTimeLimit : GameMsgHeader { + dword timeLimit; + }; + struct Cli2Srv_Marker_DeleteGame : GameMsgHeader { + // nothing + }; + struct Cli2Srv_Marker_AddMarker : GameMsgHeader { + double x; + double y; + double z; + wchar name[256]; + wchar age[80]; + }; + struct Cli2Srv_Marker_DeleteMarker : GameMsgHeader { + dword markerID; + }; + struct Cli2Srv_Marker_ChangeMarkerName : GameMsgHeader { + dword markerID; + wchar markerName[256]; + }; + struct Cli2Srv_Marker_CaptureMarker : GameMsgHeader { + dword markerID; + }; + + // Srv2Cli + struct Srv2Cli_Marker_TemplateCreated : GameMsgHeader { + wchar templateID[80]; + }; + struct Srv2Cli_Marker_TeamAssigned : GameMsgHeader { + byte teamNumber; // 1 or 2 + }; + struct Srv2Cli_Marker_GameType : GameMsgHeader { + byte gameType; // member of EMarkerGameType + }; + struct Srv2Cli_Marker_GameStarted : GameMsgHeader { + // nothing + }; + struct Srv2Cli_Marker_GamePaused : GameMsgHeader { + dword timeLeft; // 0 if quest game, since they don't have a timer + }; + struct Srv2Cli_Marker_GameReset : GameMsgHeader { + // nothing + }; + struct Srv2Cli_Marker_GameOver : GameMsgHeader { + // nothing + }; + struct Srv2Cli_Marker_GameNameChanged : GameMsgHeader { + wchar newName[256]; + }; + struct Srv2Cli_Marker_TimeLimitChanged : GameMsgHeader { + dword newTimeLimit; + }; + struct Srv2Cli_Marker_GameDeleted : GameMsgHeader { + bool failed; // did the delete fail? + }; + struct Srv2Cli_Marker_MarkerAdded : GameMsgHeader { + double x; + double y; + double z; + dword markerID; + wchar name[256]; + wchar age[80]; + }; + struct Srv2Cli_Marker_MarkerDeleted : GameMsgHeader { + dword markerID; + }; + struct Srv2Cli_Marker_MarkerNameChanged : GameMsgHeader { + dword markerID; + wchar newName[256]; + }; + struct Srv2Cli_Marker_MarkerCaptured : GameMsgHeader { + dword markerID; + byte team; // 0 for no team, or for quest games + }; + + +//============================================================================ +// End networked data structures +#include +//============================================================================ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Pch.h new file mode 100644 index 00000000..e9560ea3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/Pch.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGamesCommon/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMESCOMMON_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnGamesCommon/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMESCOMMON_PCH_H + + +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" +#include "../pnProduct/pnProduct.h" + +#include "pnGameMgr.h" +#include "Intern.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/TicTacToe/pnGmTicTacToe.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/TicTacToe/pnGmTicTacToe.cpp new file mode 100644 index 00000000..6a41f73d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/TicTacToe/pnGmTicTacToe.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/TicTacToe/pnGmTicTacToe.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/TicTacToe/pnGmTicTacToe.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/TicTacToe/pnGmTicTacToe.h new file mode 100644 index 00000000..1952321e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/TicTacToe/pnGmTicTacToe.h @@ -0,0 +1,120 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/TicTacToe/pnGmTicTacToe.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_TICTACTOE_PNGMTICTACTOE_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/TicTacToe/pnGmTicTacToe.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_TICTACTOE_PNGMTICTACTOE_H + + +/***************************************************************************** +* +* Tic-Tac-Toe +* +***/ + +enum ETTTInitResult { + kTTTInitSuccess, + kTTTInitError, + kNumTTTInitResults +}; +enum ETTTGameResult { + kTTTGameResultWinner, // there was a winning player + kTTTGameResultTied, // players tied (a "cat's game") + kTTTGameResultGave, // other player left the game + kTTTGameResultError, // something bad happened on the server + kNumTTTGameResults +}; + +//============================================================================ +// Game type id +//============================================================================ + +const Uuid kGameTypeId_TicTacToe = Uuid(L"a7236529-11d8-4758-9368-59cb43445a83"); + + +//============================================================================ +// Network message ids +//============================================================================ + +// Cli2Srv message ids +enum { + kCli2Srv_TTT_MakeMove = kCli2Srv_NumGameMsgIds, +}; + +// Srv2Cli message ids +enum { + kSrv2Cli_TTT_GameStarted = kSrv2Cli_NumGameMsgIds, + kSrv2Cli_TTT_GameOver, + kSrv2Cli_TTT_MoveMade, +}; + + +//============================================================================ +// Begin networked data scructures +#include +//============================================================================ + + //======================================================================== + // Message parameters + //======================================================================== + struct TTT_CreateParam { + byte playerCount; // 1 or 2 + }; + + //======================================================================== + // Tic-Tac-Toe message structures + //======================================================================== + + // Cli2Srv + struct Cli2Srv_TTT_MakeMove : GameMsgHeader { + byte row; // 1..3 + byte col; // 1..3 + }; + + // Srv2Cli + struct Srv2Cli_TTT_GameStarted : GameMsgHeader { + bool yourTurn; // randomly selected first player + }; + struct Srv2Cli_TTT_GameOver : GameMsgHeader { + ETTTGameResult result; + dword winnerId; + }; + struct Srv2Cli_TTT_MoveMade : GameMsgHeader { + dword playerId; + byte row; // 1..3 + byte col; // 1..3 + }; + +//============================================================================ +// End networked data structures +#include +//============================================================================ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/VarSync/pnGmVarSync.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/VarSync/pnGmVarSync.cpp new file mode 100644 index 00000000..c4724b74 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/VarSync/pnGmVarSync.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/VarSync/pnGmVarSync.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/VarSync/pnGmVarSync.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/VarSync/pnGmVarSync.h new file mode 100644 index 00000000..7620bf5b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/VarSync/pnGmVarSync.h @@ -0,0 +1,141 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/VarSync/pnGmVarSync.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_VARSYNC_PNGMVARSYNC_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/VarSync/pnGmVarSync.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_VARSYNC_PNGMVARSYNC_H + + +/***************************************************************************** +* +* Var Sync +* +***/ + +enum EVarSyncInitResult { + kVarSyncInitSuccess, + kVarSyncInitError, + kNumVarSyncInitResults +}; + + +//============================================================================ +// Game type id +//============================================================================ + +const Uuid kGameTypeId_VarSync = Uuid(L"475c2e9b-a245-4106-a047-9b25d41ff333"); + + +//============================================================================ +// Network message ids +//============================================================================ + +// Cli2Srv message ids +enum { + kCli2Srv_VarSync_SetStringVar = kCli2Srv_NumGameMsgIds, + kCli2Srv_VarSync_SetNumericVar, + kCli2Srv_VarSync_RequestAllVars, + kCli2Srv_VarSync_CreateStringVar, + kCli2Srv_VarSync_CreateNumericVar, +}; + +// Srv2Cli message ids +enum { + kSrv2Cli_VarSync_StringVarChanged = kSrv2Cli_NumGameMsgIds, + kSrv2Cli_VarSync_NumericVarChanged, + kSrv2Cli_VarSync_AllVarsSent, + kSrv2Cli_VarSync_StringVarCreated, + kSrv2Cli_VarSync_NumericVarCreated, +}; + + +//============================================================================ +// Begin networked data structures +#include +//============================================================================ + + //======================================================================== + // Message parameters + //======================================================================== + struct VarSync_CreateParam { + }; + + //======================================================================== + // VarSync message structures + //======================================================================== + + // Cli2Srv + struct Cli2Srv_VarSync_SetStringVar : GameMsgHeader { + unsigned long varID; + wchar varValue[256]; + }; + struct Cli2Srv_VarSync_SetNumericVar : GameMsgHeader { + unsigned long varID; + double varValue; + }; + struct Cli2Srv_VarSync_RequestAllVars : GameMsgHeader { + }; + struct Cli2Srv_VarSync_CreateStringVar : GameMsgHeader { + wchar varName[256]; + wchar varValue[256]; + }; + struct Cli2Srv_VarSync_CreateNumericVar : GameMsgHeader { + wchar varName[256]; + double varValue; + }; + + // Srv2Cli + struct Srv2Cli_VarSync_StringVarChanged : GameMsgHeader { + unsigned long varID; + wchar varValue[256]; + }; + struct Srv2Cli_VarSync_NumericVarChanged : GameMsgHeader { + unsigned long varID; + double varValue; + }; + struct Srv2Cli_VarSync_AllVarsSent : GameMsgHeader { + }; + struct Srv2Cli_VarSync_StringVarCreated : GameMsgHeader { + wchar varName[256]; + unsigned long varID; + wchar varValue[256]; + }; + struct Srv2Cli_VarSync_NumericVarCreated : GameMsgHeader { + wchar varName[256]; + unsigned long varID; + double varValue; + }; + +//============================================================================ +// End networked data structures +#include +//============================================================================ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/pnGameMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/pnGameMgr.cpp new file mode 100644 index 00000000..02d00e65 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/pnGameMgr.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/pnGameMgr.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/pnGameMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/pnGameMgr.h new file mode 100644 index 00000000..43cb307e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/pnGameMgr.h @@ -0,0 +1,252 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnGameMgr/pnGameMgr.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_PNGAMEMGR_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_PNGAMEMGR_H + + +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" +#include "../pnAsyncCore/pnAsyncCore.h" +#include "../pnNetCli/pnNetCli.h" +#include "../pnProduct/pnProduct.h" +#include "../pnKeyedObject/plKey.h" + +#include "hsGeometry3.h" + + +/***************************************************************************** +* +* GameMgr +* +***/ + +const unsigned kGameMgrGlobalGameIdFlag = !((unsigned)-1 - 1); // 0x10000000 + +//============================================================================ +// EGameJoinError +//============================================================================ +enum EGameJoinError { + kGameJoinSuccess, + kGameJoinErrNotExist, + kGameJoinErrInitFailed, + kGameJoinErrGameStarted, + kGameJoinErrGameOver, + kGameJoinErrMaxPlayers, + kGameJoinErrAlreadyJoined, + kGameJoinErrNoInvite, + kNumGameJoinErrors +}; + +//============================================================================ +// EGameInviteError +//============================================================================ +enum EGameInviteError { + kGameInviteSuccess, + kGameInviteErrNotOwner, + kGameInviteErrAlreadyInvited, + kGameInviteErrAlreadyJoined, + kGameInviteErrGameStarted, + kGameInviteErrGameOver, + kGameInviteErrGameFull, + kGameInviteErrNoJoin, // GameSrv reports the player may not join right now + kNumGameInviteErrors +}; + +//============================================================================ +// Game create/join options +//============================================================================ +/* + If set : Anyone may join; no invite necessary. + Not set : Only players with invites may join. +*/ +const unsigned kGameCreatePublic = 1<<0; +/* + If set : Anyone may invite others to play. + Not set : Only the game owner may send invites. +*/ +const unsigned kGameCreateOpen = 1<<1; +/* + If set : Player joins or creates the "common" instance of the game. In + this case, the 'newGameId' field is not meaningful. If the + common instance doesn't exist, it'll be created on-the-fly and + the player will receive a GameCreated message as well as the + normal GameJoined. This allows the game to be initialized once + when first instanced. + Not set : A game with the specified gameId must exist on the server. + Depending on the options set during the game's creation, the + player may need to have been sent an invite. Also, the game may + not be in a state where it allows new players to join. Player + receives a GameJoined reply in any case. Inspect the 'result' + field to see whether the join was successful. +*/ +const unsigned kGameJoinCommon = 1<<2; +/* +*/ +const unsigned kGameJoinObserver = 1<<3; + + +//============================================================================ +// GameMgr Network message ids +//============================================================================ +enum { + kCli2Srv_GameMgr_CreateGame, + kCli2Srv_GameMgr_JoinGame, +}; +enum { + kSrv2Cli_GameMgr_GameInstance, // Internal, not sent out in pfGameMgrMsg + kSrv2Cli_GameMgr_InviteReceived, + kSrv2Cli_GameMgr_InviteRevoked, +}; + +//============================================================================ +// GameCli/Srv Network message ids +//============================================================================ +enum { + kCli2Srv_Game_LeaveGame, + kCli2Srv_Game_Invite, + kCli2Srv_Game_Uninvite, + // Cli2Srv msgIds for specific games must begin with this value. See TicTacToe for example + kCli2Srv_NumGameMsgIds +}; +enum { + kSrv2Cli_Game_PlayerJoined, + kSrv2Cli_Game_PlayerLeft, + kSrv2Cli_Game_InviteFailed, + kSrv2Cli_Game_OwnerChange, + // Srv2Cli msgIds for specific games must begin with this value. See TicTacToe for example + kSrv2Cli_NumGameMsgIds +}; + + +//============================================================================ +// Begin networked data scructures +#include +//============================================================================ + + struct GameMsgHeader { + dword messageId; + dword transId; + dword recvGameId; // 0 --> GameMgr, non-zero --> GameSrv + dword messageBytes; + }; + + //======================================================================== + // GameMgr message structures + //======================================================================== + + // Cli2Srv + struct Cli2Srv_GameMgr_CreateGame : GameMsgHeader { + Uuid gameTypeId; + dword createOptions; + dword createDataBytes; + byte createData[1]; // [createDataBytes] + }; + struct Cli2Srv_GameMgr_JoinGame : GameMsgHeader { + // Field ordering here is vitally important, see pfGameMgr::JoinGame for explanation + dword newGameId; + dword createOptions; + Uuid gameTypeId; + dword createDataBytes; + byte createData[1]; // [createDataBytes] + }; + + // Srv2Cli + struct Srv2Cli_GameMgr_GameInstance : GameMsgHeader { + EGameJoinError result; + dword ownerId; + Uuid gameTypeId; + dword newGameId; + }; + struct Srv2Cli_GameMgr_InviteReceived : GameMsgHeader { + dword inviterId; + Uuid gameTypeId; + dword newGameId; + }; + struct Srv2Cli_GameMgr_InviteRevoked : GameMsgHeader { + dword inviterId; + Uuid gameTypeId; + dword newGameId; + }; + + + //======================================================================== + // GameCli/Srv message structures + //======================================================================== + + // Cli2Srv + struct Cli2Srv_Game_LeaveGame : GameMsgHeader { + }; + struct Cli2Srv_Game_Invite : GameMsgHeader { + dword playerId; + }; + struct Cli2Srv_Game_Uninvite : GameMsgHeader { + dword playerId; + }; + + // Srv2Cli + struct Srv2Cli_Game_PlayerJoined : GameMsgHeader { + dword playerId; + }; + struct Srv2Cli_Game_PlayerLeft : GameMsgHeader { + dword playerId; + }; + struct Srv2Cli_Game_InviteFailed : GameMsgHeader { + dword inviteeId; + dword operationId; + EGameInviteError error; + }; + struct Srv2Cli_Game_OwnerChange : GameMsgHeader { + dword ownerId; + }; + + +//============================================================================ +// End networked data structures +#include +//============================================================================ + + +/***************************************************************************** +* +* Games +* +***/ + +#include "TicTacToe/pnGmTicTacToe.h" +#include "Heek/pnGmHeek.h" +#include "Marker/pnGmMarker.h" +#include "BlueSpiral/pnGmBlueSpiral.h" +#include "ClimbingWall/pnGmClimbingWall.h" +#include "VarSync/pnGmVarSync.h" + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNGAMEMGR_PNGAMEMGR_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Intern.h new file mode 100644 index 00000000..fbfabd77 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Intern.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_INTERN_H + + +/***************************************************************************** +* +* Ini +* +***/ + +struct IniValue { + ARRAY(wchar *) fArgs; + IniKey * fKey; + unsigned fIndex; + unsigned fLineNum; + + IniValue (IniKey * key, unsigned lineNum); + ~IniValue (); +}; + +struct IniKey { + HASHLINK(IniKey) fLink; + ARRAY(IniValue *) fValues; + IniSection * fSection; + wchar fName[1]; // variable length + // no more fields + + IniKey (IniSection * section, const wchar name[]); + ~IniKey (); + + unsigned GetHash () const; + bool operator== (const CHashKeyStrPtrI & rhs) const; +}; + +struct IniSection { + HASHTABLEDECL(IniKey, CHashKeyStrPtrI, fLink) fKeys; + HASHLINK(IniSection) fLink; + wchar fName[1]; // variable length + // no more fields + + IniSection (const wchar name[]); + ~IniSection (); + + unsigned GetHash () const; + bool operator== (const CHashKeyStrPtrI & rhs) const; +}; + +struct Ini { + HASHTABLEDECL(IniSection, CHashKeyStrPtrI, fLink) fSections; + + ~Ini (); +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Pch.h new file mode 100644 index 00000000..034ab67b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Pch.h @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PCH_H + + +#include "pnUtils/pnUtils.h" +#include "pnProduct/pnProduct.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" + +#include "Private/pnIniAllIncludes.h" +#include "Intern.h" + + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniAllIncludes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniAllIncludes.h new file mode 100644 index 00000000..33c77907 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniAllIncludes.h @@ -0,0 +1,40 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniAllIncludes.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PRIVATE_PNINIALLINCLUDES_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniAllIncludes.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PRIVATE_PNINIALLINCLUDES_H + + +#include "pnIniCore.h" +#include "pnIniChange.h" +#include "pnIniSrv.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniChange.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniChange.h new file mode 100644 index 00000000..103a5f3f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniChange.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniChange.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PRIVATE_PNINICHANGE_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniChange.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PRIVATE_PNINICHANGE_H + + +/***************************************************************************** +* +* Change notifications for files in the "./Config" directory +* +***/ + +struct IniChangeReg; + +typedef void (* FIniFileChangeCallback)(const wchar fullPath[]); + +void IniChangeInitialize ( + const wchar dir[] = L"Config" +); +void IniChangeDestroy (); + +void IniChangeAdd ( + const wchar filename[], // just filename, no path or extension + FIniFileChangeCallback callback, + IniChangeReg ** reg +); +void IniChangeRemove ( + IniChangeReg * reg, + bool wait +); +void IniChangeSignal ( + IniChangeReg * reg, + bool wait +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniCore.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniCore.cpp new file mode 100644 index 00000000..48a27d2f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniCore.cpp @@ -0,0 +1,770 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniCore.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +static wchar * TrimWhitespace (wchar * name) { + while (isspace((char) *name)) + ++name; + + for (wchar * term = name; *term; ++term) { + if (isspace((char) *term)) { + *term = 0; + break; + } + } + + return name; +} + + +/**************************************************************************** +* +* IniValue +* +***/ + +//=========================================================================== +IniValue::IniValue (IniKey * key, unsigned lineNum) +: fKey(key) +, fLineNum(lineNum) +{ + fIndex = key->fValues.Add(this); +} + +//=========================================================================== +IniValue::~IniValue () { + wchar ** cur = fArgs.Ptr(); + wchar ** end = fArgs.Term(); + for (; cur < end; ++cur) + FREE(*cur); +} + +//=========================================================================== +static void AddValueString ( + IniValue * value, + const wchar src[] +) { + unsigned chars = StrLen(src) + 1; + wchar * dst = ALLOCA(wchar, chars); + StrTokenize(&src, dst, chars, L" \t\r\n\""); + value->fArgs.Add(StrDup(dst)); +} + + +/**************************************************************************** +* +* IniKey +* +***/ + +//=========================================================================== +IniKey::IniKey (IniSection * section, const wchar name[]) +: fSection(section) +{ + StrCopy(fName, name, (unsigned) -1); + fSection->fKeys.Add(this); +} + +//=========================================================================== +IniKey::~IniKey () { + IniValue ** cur = fValues.Ptr(); + IniValue ** end = fValues.Term(); + for (; cur < end; ++cur) + DEL(*cur); +} + +//=========================================================================== +inline unsigned IniKey::GetHash () const { + return StrHashI(fName); +} + +//=========================================================================== +inline bool IniKey::operator== (const CHashKeyStrPtrI & rhs) const { + return !StrCmpI(fName, rhs.GetString()); +} + +//=========================================================================== +static IniValue * AddKeyValue ( + IniSection * section, + wchar * string, + unsigned lineNum +) { + string = TrimWhitespace(string); + + // Find or create the key + IniKey * key = section->fKeys.Find(string); + if (!key) { + key = new(ALLOC( + sizeof(*key) - sizeof(key->fName) + StrBytes(string) + )) IniKey(section, string); + } + + // Add a new value holder for the key + return NEW(IniValue)(key, lineNum); +} + + +/**************************************************************************** +* +* IniSection +* +***/ + +//=========================================================================== +IniSection::IniSection (const wchar name[]) { + StrCopy(fName, name, (unsigned) -1); +} + +//=========================================================================== +IniSection::~IniSection () { + fKeys.Clear(); +} + +//=========================================================================== +inline unsigned IniSection::GetHash () const { + return StrHashI(fName); +} + +//=========================================================================== +inline bool IniSection::operator== (const CHashKeyStrPtrI & rhs) const { + return !StrCmpI(fName, rhs.GetString()); +} + +//=========================================================================== +static IniSection * AddSection ( + Ini * ini, + wchar * string +) { + // Find or create the section + IniSection * section = ini->fSections.Find(string); + if (!section) { + section = new(ALLOC( + sizeof(*section) - sizeof(section->fName) + StrBytes(string) + )) IniSection(string); + ini->fSections.Add(section); + } + return section; +} + + +/**************************************************************************** +* +* Ini +* +***/ + +//=========================================================================== +Ini::~Ini () { + fSections.Clear(); +} + + +/**************************************************************************** +* +* ParseBuffer +* +***/ + +//=========================================================================== +static void ParseBuffer ( + Ini * ini, + const wchar buffer[] +) { + + const wchar SECTION_OPEN_CHAR = '['; + const wchar SECTION_CLOSE_CHAR = ']'; + const wchar EQUIVALENCE_CHAR = '='; + const wchar VALUE_SEPARATOR = ','; + const wchar COMMENT_CHAR = ';'; + const wchar QUOTE_CHAR = '\"'; + const wchar NEWLINE = '\n'; + + enum { + STATE_BEGIN, + STATE_NEWLINE, + STATE_SECTION, + STATE_STRIP_TRAILING, + STATE_KEY, + STATE_VALUE, + } state = STATE_BEGIN; + + IniSection * section = nil; + IniValue * value = nil; + const wchar * start = nil; + bool valInQuotes = false; + wchar dst[512]; + dst[0] = 0; + + for (unsigned lineNum = 1;; ++buffer) { + + // Get next character + unsigned chr = *buffer; + if (!chr) + break; + if (chr == '\r') + continue; + if (chr == '\n') + ++lineNum; + if (chr == '\t') + chr = ' '; + + switch (state) { + case STATE_BEGIN: + ASSERT(chr == UNICODE_BOM); + state = STATE_NEWLINE; + break; + + case STATE_NEWLINE: + if (chr == NEWLINE) + break; + if (chr == ' ') + break; + if (chr == SECTION_OPEN_CHAR) { + start = buffer + 1; + state = STATE_SECTION; + } + else if (chr == COMMENT_CHAR) { + state = STATE_STRIP_TRAILING; + } + else { + start = buffer; + state = STATE_KEY; + } + break; + + case STATE_SECTION: + if (chr == NEWLINE) { + state = STATE_NEWLINE; + break; + } + + if (chr == SECTION_CLOSE_CHAR) { + StrCopy(dst, start, min(buffer - start + 1, arrsize(dst))); + section = AddSection(ini, dst); + state = STATE_STRIP_TRAILING; + } + break; + + case STATE_STRIP_TRAILING: + if (chr == NEWLINE) + state = STATE_NEWLINE; + break; + + case STATE_KEY: + if (chr == NEWLINE) { + state = STATE_NEWLINE; + break; + } + if (chr != EQUIVALENCE_CHAR) + break; + + if (!section) { + state = STATE_STRIP_TRAILING; + break; + } + + StrCopy(dst, start, min(buffer - start + 1, arrsize(dst))); + value = AddKeyValue(section, dst, lineNum); + start = buffer + 1; + state = STATE_VALUE; + valInQuotes = false; + break; + + case STATE_VALUE: + if (chr == QUOTE_CHAR) + valInQuotes = !valInQuotes; + if ((valInQuotes || chr != VALUE_SEPARATOR) && (chr != NEWLINE)) + break; + if (!value) { + state = chr == NEWLINE ? STATE_NEWLINE : STATE_STRIP_TRAILING; + break; + } + if (valInQuotes) { + state = chr == NEWLINE ? STATE_NEWLINE : STATE_STRIP_TRAILING; + break; + } + + StrCopy(dst, start, min(buffer - start + 1, arrsize(dst))); + AddValueString(value, dst); + if (chr == VALUE_SEPARATOR) + start = buffer + 1; + else + state = STATE_NEWLINE; + break; + } + } + + // cleanup current value + if (state == STATE_VALUE) { + StrCopy(dst, start, min(buffer - start + 1, arrsize(dst))); + AddValueString(value, dst); + } +} + + +/**************************************************************************** +* +* ParseFile +* +***/ + +//=========================================================================== +static void IniFileNotifyProc ( + AsyncFile , + EAsyncNotifyFile , + AsyncNotifyFile * , + void ** +) { +} + +//=========================================================================== +static bool ParseFile ( + Ini * ini, + const wchar fileName[] +) { + // Open file + qword fileSize; + qword fileLastWriteTime; + EFileError error; + AsyncFile file = AsyncFileOpen( + fileName, + IniFileNotifyProc, + &error, + kAsyncFileReadAccess, + kAsyncFileModeOpenExisting, + kAsyncFileShareRead, + nil, + &fileSize, + &fileLastWriteTime + ); + if (!file) + return false; + + bool result; + if (fileSize > 256 * 1024) { + result = false; + } + else if (!fileSize) { + result = true; + } + else { + // Read entire file into memory and NULL terminate wchar + byte * buffer = (byte *) ALLOC((unsigned) fileSize + sizeof(wchar)); + AsyncFileRead(file, 0, buffer, (unsigned) fileSize, kAsyncFileRwSync, nil); + * (wchar *) &buffer[fileSize] = 0; + + // Convert to unicode if necessary + if (* (wchar *) buffer != UNICODE_BOM) { + byte * src = buffer; + // Allocate two extra spaces for UNICODE_BOM and terminator + unsigned newBufferSize = ((unsigned) fileSize + 2) * sizeof(wchar); + + // Allocate new buffer + wchar * dst = (wchar *) ALLOC(newBufferSize); + + // If it's UTF-8 file,convert to Unicode + if (StrCmpI((char *)buffer, UTF8_BOM, StrLen(UTF8_BOM)) == 0) { + // StrUtf8ToUnicode will convert UTF8_BOM to UNICODE_BOM + StrUtf8ToUnicode(dst, (char *)src, newBufferSize); + } + else { + // do simple conversion to Unicode + dst[0] = UNICODE_BOM; + for (unsigned index = 0;; ++index) { + if (0 == (dst[index + 1] = src[index])) + break; + } + } + + FREE(src); + buffer = (byte *) dst; + } + + ParseBuffer(ini, (const wchar *) buffer); + FREE(buffer); + result = true; + } + + AsyncFileClose(file, kAsyncFileDontTruncate); + return result; +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +Ini * IniOpen ( + const wchar fileName[] +) { + Ini * ini = NEW(Ini); + if (!ParseFile(ini, fileName)) { + IniClose(ini); + return nil; + } + return ini; +} + +//=========================================================================== +void IniClose (Ini * ini) { + DEL(ini); +} + +//=========================================================================== +const IniSection * IniGetFirstSection ( + const Ini * ini, + wchar * name, + unsigned chars +) { + if (chars) + *name = 0; + if (!ini) + return nil; + + const IniSection * section = ini->fSections.Head(); + if (section) + StrCopy(name, section->fName, chars); + return section; +} + +//=========================================================================== +const IniSection * IniGetNextSection ( + const IniSection * section, + wchar * name, + unsigned chars +) { + if (chars) + *name = 0; + if (!section) + return nil; + + section = section->fLink.Next(); + if (section) + StrCopy(name, section->fName, chars); + return section; +} + +//=========================================================================== +const IniSection * IniGetSection ( + const Ini * ini, + const wchar name[] +) { + if (!ini) + return nil; + + return ini->fSections.Find( + CHashKeyStrPtrI(name) + ); +} + +//=========================================================================== +const IniKey * IniGetFirstKey ( + const IniSection * section, + wchar * name, + unsigned chars +) { + if (chars) + *name = 0; + if (!section) + return nil; + + const IniKey * key = section->fKeys.Head(); + if (key) + StrCopy(name, key->fName, chars); + return key; +} + +//============================================================================ +const IniKey * IniGetFirstKey ( + const Ini * ini, + const wchar sectionName[], + wchar * name, + unsigned chars +) { + if (const IniSection * section = IniGetSection(ini, sectionName)) + return IniGetFirstKey(section, name, chars); + + return nil; +} + +//=========================================================================== +const IniKey * IniGetNextKey ( + const IniKey * key, + wchar * name, + unsigned chars +) { + if (chars) + *name = 0; + if (!key) + return nil; + + key = key->fLink.Next(); + if (key) + StrCopy(name, key->fName, chars); + return key; +} + +//=========================================================================== +const IniKey * IniGetKey ( + const IniSection * section, + const wchar name[] +) { + if (!section) + return nil; + + return section->fKeys.Find( + CHashKeyStrPtrI(name) + ); +} + +//=========================================================================== +const IniValue * IniGetFirstValue ( + const IniKey * key, + unsigned * lineNum +) { + if (lineNum) + *lineNum = 0; + + const IniValue * value = nil; + for (;;) { + if (!key) + break; + + value = key->fValues[0]; + if (lineNum) + *lineNum = value->fLineNum; + break; + } + + return value; +} + +//=========================================================================== +const IniValue * IniGetFirstValue ( + const IniSection * section, + const wchar keyName[], + unsigned * lineNum +) { + const IniValue * value = nil; + if (lineNum) + *lineNum = 0; + + for (;;) { + if (!section) + break; + + const IniKey * key = section->fKeys.Find( + CHashKeyStrPtrI(keyName) + ); + value = IniGetFirstValue(key, lineNum); + break; + } + + return value; +} + +//=========================================================================== +const IniValue * IniGetFirstValue ( + const Ini * ini, + const wchar sectionName[], + const wchar keyName[], + unsigned * lineNum +) { + const IniValue * value = nil; + if (lineNum) + *lineNum = 0; + + for (;;) { + if (!ini) + break; + + const IniSection * section = ini->fSections.Find( + CHashKeyStrPtrI(sectionName) + ); + + value = IniGetFirstValue(section, keyName, lineNum); + break; + } + + return value; +} + +//=========================================================================== +const IniValue * IniGetNextValue ( + const IniValue * value, + unsigned * lineNum +) { + if (lineNum) + *lineNum = 0; + + const IniKey * key = value->fKey; + if (value->fIndex + 1 < key->fValues.Count()) { + value = key->fValues[value->fIndex + 1]; + if (lineNum) + *lineNum = value->fLineNum; + } + else { + value = nil; + } + + return value; +} + +//=========================================================================== +bool IniGetUnsigned ( + const IniValue * value, + unsigned * result, + unsigned index, + unsigned defaultValue +) { + ASSERT(result); + + for (;;) { + if (!value) + break; + + wchar str[32]; + if (!IniGetString(value, str, arrsize(str), index, nil)) + break; + + if (!str[0]) + break; + + *result = StrToUnsigned(str, nil, 0); + return true; + } + + *result = defaultValue; + return false; +} + +//=========================================================================== +bool IniGetString ( + const IniValue * value, + wchar * result, + unsigned resultChars, + unsigned index, + const wchar defaultValue[] +) { + ASSERT(result); + + bool found = value && index < value->fArgs.Count(); + + if (found) + StrCopy(result, value->fArgs[index], resultChars); + else if (defaultValue) + StrCopy(result, defaultValue, resultChars); + + return found; +} + +//=========================================================================== +bool IniGetUuid ( + const IniValue * value, + Uuid * uuid, + unsigned index, + const Uuid & defaultValue +) { + wchar str[128]; + if (IniGetString(value, str, arrsize(str), index, nil)) + return GuidFromString(str, uuid); + else + *uuid = defaultValue; + + return false; +} + +//=========================================================================== +unsigned IniGetBoundedValue ( + const IniValue * value, + const wchar section[], + const wchar key[], + unsigned index, + unsigned minVal, + unsigned maxVal, + unsigned defVal +) { + ref(key); + ref(section); + + if (!value) + return defVal; + + unsigned result; + IniGetUnsigned(value, &result, index, defVal); + if ((result < minVal) || (result > maxVal)) { + result = defVal; + } + + return result; +} + +//=========================================================================== +unsigned IniGetBoundedValue ( + const Ini * ini, + const wchar section[], + const wchar key[], + unsigned index, + unsigned minVal, + unsigned maxVal, + unsigned defVal +) { + unsigned lineNum; + const IniValue * value = IniGetFirstValue( + ini, + section, + key, + &lineNum + ); + + return IniGetBoundedValue( + value, + section, + key, + index, + minVal, + maxVal, + defVal + ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniCore.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniCore.h new file mode 100644 index 00000000..caff337d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniCore.h @@ -0,0 +1,154 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniCore.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PRIVATE_PNINICORE_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniCore.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PRIVATE_PNINICORE_H + + +/***************************************************************************** +* +* Ini file parsing functions +* +***/ + +struct Ini; +struct IniKey; +struct IniValue; +struct IniSection; + + +// File +Ini * IniOpen ( + const wchar filename[] +); +void IniClose ( + Ini * ini +); + +// Section +const IniSection * IniGetFirstSection ( + const Ini * ini, + wchar * name, + unsigned chars +); +const IniSection * IniGetNextSection ( + const IniSection * section, + wchar * name, + unsigned chars +); +const IniSection * IniGetSection ( + const Ini * ini, + const wchar name[] +); + +// Key +const IniKey * IniGetFirstKey ( + const IniSection * section, + wchar * name, + unsigned chars +); +const IniKey * IniGetFirstKey ( + const Ini * ini, + const wchar sectionName[], + wchar * name, + unsigned chars +); +const IniKey * IniGetNextKey ( + const IniKey * key, + wchar * name, + unsigned chars +); +const IniKey * IniGetKey ( + const IniSection * ini, + const wchar name[] +); + +// Value +const IniValue * IniGetFirstValue ( + const IniKey * key, + unsigned * iter +); +const IniValue * IniGetFirstValue ( + const IniSection * section, + const wchar keyName[], + unsigned * iter +); +const IniValue * IniGetFirstValue ( + const Ini * ini, + const wchar sectionName[], + const wchar keyName[], + unsigned * iter +); +const IniValue * IniGetNextValue ( + const IniValue * value, + unsigned * iter +); + +// Data +bool IniGetUnsigned ( + const IniValue * value, + unsigned * result, + unsigned index = 0, + unsigned defaultValue = 0 +); +bool IniGetString ( + const IniValue * value, + wchar * result, + unsigned resultChars, + unsigned index = 0, + const wchar defaultValue[] = nil +); +bool IniGetUuid ( + const IniValue * value, + Uuid * result, + unsigned index = 0, + const Uuid & defaultValue = kNilGuid +); + +// Bounded values +unsigned IniGetBoundedValue ( + const Ini * ini, + const wchar sectionName[], + const wchar keyName[], + unsigned index, + unsigned minVal, + unsigned maxVal, + unsigned defVal +); +unsigned IniGetBoundedValue ( + const IniValue * value, + unsigned index, + unsigned minVal, + unsigned maxVal, + unsigned defVal +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniSrv.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniSrv.cpp new file mode 100644 index 00000000..89535d66 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniSrv.cpp @@ -0,0 +1,40 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniSrv.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +#ifdef SERVER + + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniSrv.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniSrv.h new file mode 100644 index 00000000..dcbf193e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniSrv.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniSrv.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PRIVATE_PNINISRV_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnIni/Private/pnIniSrv.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PRIVATE_PNINISRV_H + + +#ifdef SERVER + + +/***************************************************************************** +* +* ServerRights API +* +***/ + +// These values may never change because server modules +// must use the same values as plServer.exe +enum EServerRights { + kSrvRightsNone = 0, + kSrvRightsBasic = 1, + kSrvRightsServer = 2, + kNumSrvRights +}; + +EServerRights SrvIniGetServerRightsByNode (NetAddressNode addrNode); +EServerRights SrvIniGetServerRights (const NetAddress & addr); +void SrvIniParseServerRights (Ini * ini); + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/pnIni.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/pnIni.h new file mode 100644 index 00000000..a5e21c5e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIni/pnIni.h @@ -0,0 +1,37 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIni/pnIni.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PNINI_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PNINI_H + +#include "Private/pnIniAllIncludes.h" + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINI_PNINI_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Pch.h new file mode 100644 index 00000000..9727e75c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Pch.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINIEXE_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNINIEXE_PCH_H + + +#include "pnUtils/pnUtils.h" +#include "pnProduct/pnProduct.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" +#include "pnIni/pnIni.h" + +#include "pnIni/Intern.h" + +#ifdef SERVER +#include "pnCrash/pnCrash.h" +#endif + + + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Private/Win32/pnW32IniChange.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Private/Win32/pnW32IniChange.cpp new file mode 100644 index 00000000..a80b3244 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Private/Win32/pnW32IniChange.cpp @@ -0,0 +1,381 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Private/Win32/pnW32IniChange.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + + +#ifdef HS_BUILD_FOR_WIN32 + +/***************************************************************************** +* +* Private +* +***/ + +struct IniChangeFile; + +struct IniChangeReg { + LINK(IniChangeReg) fLink; + FIniFileChangeCallback fNotify; + qword fLastWriteTime; + wchar fFileName[MAX_PATH]; +}; + +static CLock s_lock; +static HANDLE s_event; +static HANDLE s_signal; +static HANDLE s_thread; +static HANDLE s_change; +static bool s_running; +static IniChangeReg * s_dispatch; +static wchar s_directory[MAX_PATH]; +static LISTDECL(IniChangeReg, fLink) s_callbacks; + + +/**************************************************************************** +* +* Change notification +* +***/ + +//=========================================================================== +static qword GetFileTimestamp (const wchar fileName[]) { + HANDLE find; + WIN32_FIND_DATAW fd; + qword lastWriteTime; + if (INVALID_HANDLE_VALUE != (find = FindFirstFileW(fileName, &fd))) { + COMPILER_ASSERT(sizeof(lastWriteTime) == sizeof(fd.ftLastWriteTime)); + lastWriteTime = * (const qword *) &fd.ftLastWriteTime; + FindClose(find); + } + else { + lastWriteTime = 1; // any non-zero, non-valid number + } + + return lastWriteTime; +} + +//=========================================================================== +static void ChangeDispatch_WL (IniChangeReg * marker) { + + while (nil != (s_dispatch = s_callbacks.Next(marker))) { + // Move the marker to the next location + s_callbacks.Link(marker, kListLinkAfter, s_dispatch); + + // If the file record time matches the file data time + // then there's no need to reprocess the callbacks + qword lastWriteTime = GetFileTimestamp(s_dispatch->fFileName); + if (s_dispatch->fLastWriteTime == lastWriteTime) + continue; + s_dispatch->fLastWriteTime = lastWriteTime; + + // Leave lock to perform callback + s_lock.LeaveWrite(); + s_dispatch->fNotify(s_dispatch->fFileName); + s_lock.EnterWrite(); + } + + // List traversal complete + SetEvent(s_signal); +} + +//=========================================================================== +static unsigned THREADCALL IniSrvThreadProc (AsyncThread * thread) { + ref(thread); + + IniChangeReg marker; + marker.fNotify = nil; + s_lock.EnterWrite(); + s_callbacks.Link(&marker, kListHead); + s_lock.LeaveWrite(); + + HANDLE handles[2]; + handles[0] = s_change; + handles[1] = s_event; + unsigned sleepMs = INFINITE; + for (;;) { + + // Wait until something happens + unsigned result = WaitForMultipleObjects( + arrsize(handles), + handles, + false, + sleepMs + ); + if (!s_running) + break; + + // reset the sleep time + sleepMs = INFINITE; + + if (result == WAIT_OBJECT_0) { + if (!FindNextChangeNotification(s_change)) { + LogMsg( + kLogError, + "IniSrv: FindNextChangeNotification() failed %#x", + GetLastError() + ); + break; + } + + // a change notification occurs when a file is created, even + // though it may take a number of seconds before the file data + // has been copied. Wait a few seconds after the last change + // notification so that files have a chance to stabilize. + sleepMs = 5 * 1000; + + // When the timeout occurs, reprocess the entire list + s_lock.EnterWrite(); + s_callbacks.Link(&marker, kListHead); + s_lock.LeaveWrite(); + } + else if ((result == WAIT_OBJECT_0 + 1) || (result == WAIT_TIMEOUT)) { + // Queue for deadlock check + #ifdef SERVER + void * check = CrashAddDeadlockCheck(thread->handle, L"plW32IniChange.NtWorkerThreadProc"); + #endif + + s_lock.EnterWrite(); + ChangeDispatch_WL(&marker); + s_lock.LeaveWrite(); + + // Unqueue for deadlock check + #ifdef SERVER + CrashRemoveDeadlockCheck(check); + #endif + } + else { + LogMsg( + kLogError, + "IniChange: WaitForMultipleObjects failed %#x", + GetLastError() + ); + break; + } + } + + // Cleanup + s_lock.EnterWrite(); + s_callbacks.Unlink(&marker); + s_lock.LeaveWrite(); + return 0; +} + + +/**************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +void IniChangeInitialize (const wchar dir[]) { + ASSERT(!s_running); + s_running = true; + + const char * function; + for (;;) { + // Create the config directory + PathGetProgramDirectory(s_directory, arrsize(s_directory)); + PathAddFilename(s_directory, s_directory, dir, arrsize(s_directory)); + if (EPathCreateDirError error = PathCreateDirectory(s_directory, 0)) + LogMsg(kLogError, "IniChange: CreateDir failed %u", error); + + // Open change notification for directory + s_change = FindFirstChangeNotificationW( + s_directory, + false, // watchSubTree = false + FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME + ); + if (!s_change) { + function = "FindFirstChangeNotification"; + break; + } + + // create thread event + s_event = CreateEvent( + (LPSECURITY_ATTRIBUTES) 0, + false, // auto-reset + false, // initial state off + (LPCTSTR) 0 // name + ); + if (!s_event) { + function = "CreateEvent"; + break; + } + + // create signal event + s_signal = CreateEvent( + (LPSECURITY_ATTRIBUTES) 0, + true, // manual-reset + false, // initial state off + (LPCTSTR) 0 // name + ); + if (!s_signal) { + function = "CreateEvent"; + break; + } + + // create thread + s_thread = (HANDLE) AsyncThreadCreate( + IniSrvThreadProc, + nil, + L"IniSrvChange" + ); + if (!s_thread) { + function = "AsyncThreadCreate"; + break; + } + + // Success! + return; + } + + // Failure! + LogMsg( + kLogError, + "IniChange: %s failed (%#x)", + function, + GetLastError() + ); +} + +//=========================================================================== +void IniChangeDestroy () { + s_running = false; + + if (s_thread) { + SetEvent(s_event); + WaitForSingleObject(s_thread, INFINITE); + CloseHandle(s_thread); + s_thread = nil; + } + if (s_event) { + CloseHandle(s_event); + s_event = nil; + } + if (s_signal) { + CloseHandle(s_signal); + s_signal = nil; + } + if (s_change) { + FindCloseChangeNotification(s_change); + s_change = nil; + } + + ASSERT(!s_callbacks.Head()); +} + +//=========================================================================== +void IniChangeAdd ( + const wchar fileName[], + FIniFileChangeCallback callback, + IniChangeReg ** changePtr +) { + ASSERT(fileName); + ASSERT(callback); + ASSERT(changePtr); + ASSERT(s_running); + + // Create a callback record + IniChangeReg * change = NEW(IniChangeReg); + change->fNotify = callback; + change->fLastWriteTime = 0; + PathAddFilename( + change->fFileName, + s_directory, + fileName, + arrsize(change->fFileName) + ); + PathRemoveExtension(change->fFileName, change->fFileName, arrsize(change->fFileName)); + PathAddExtension(change->fFileName, change->fFileName, L".ini", arrsize(change->fFileName)); + + // Set result before callback to avoid race condition + *changePtr = change; + + // Signal change record for immediate callback + // and wait for callback completion + IniChangeSignal(change, true); +} + +//=========================================================================== +void IniChangeRemove ( + IniChangeReg * change, + bool wait +) { + ASSERT(change); + + s_lock.EnterWrite(); + { + // Wait until the callback is no longer being dispatched + if (wait) { + while (s_dispatch == change) { + s_lock.LeaveWrite(); + AsyncSleep(10); + s_lock.EnterWrite(); + } + } + + // Remove change object from list so that + // it can be deleted outside the lock + change->fLink.Unlink(); + } + s_lock.LeaveWrite(); + + // Delete object outside critical section + DEL(change); +} + +//=========================================================================== +void IniChangeSignal ( + IniChangeReg * change, + bool wait +) { + ASSERT(change); + + s_lock.EnterWrite(); + { + s_callbacks.Link(change, kListTail); + change->fLastWriteTime = 0; + ResetEvent(s_signal); + } + s_lock.LeaveWrite(); + + // Wake up the change thread to process this request + SetEvent(s_event); + + // Wait until the request has been processed + if (wait) + WaitForSingleObject(s_signal, INFINITE); +} + +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Private/pnIniSrv.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Private/pnIniSrv.cpp new file mode 100644 index 00000000..be674620 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Private/pnIniSrv.cpp @@ -0,0 +1,202 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Private/pnIniSrv.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + +#ifdef SERVER + +/***************************************************************************** +* +* Internal +* +***/ + +const unsigned CLASS_C_SUBNET_MASK = 0xFFFFFF00; +const NetAddressNode LOOPBACK_ADDRESS_NODE = 0x7F000001; + + +//============================================================================ +struct PrivilegedAddressBlock : THashKeyVal { + HASHLINK(PrivilegedAddressBlock) link; + + NetAddressNode startAddress; + NetAddressNode endAddress; + EServerRights serverRights; +}; + +//============================================================================ + +#define ADDRESS_BLOCK_TABLE HASHTABLEDECL(PrivilegedAddressBlock, THashKeyVal, link) + +static CCritSect s_critsect; +static ADDRESS_BLOCK_TABLE s_addressBlocks; + + +//============================================================================ +static void SrvRightsDestroy () { + s_critsect.Enter(); + { + s_addressBlocks.Clear(); + } + s_critsect.Leave(); +} + +//============================================================================ +AUTO_INIT_FUNC(InitSrvRightsIni) { + atexit(SrvRightsDestroy); +} + +//============================================================================ +static EServerRights GetServerRightsFromString(const wchar string[]) { + if (StrCmpI(string, L"Server") == 0) + return kSrvRightsServer; + else if (StrCmpI(string, L"Basic") == 0) + return kSrvRightsBasic; + else + return kSrvRightsNone; +} + +static void IAddAddressBlock(ADDRESS_BLOCK_TABLE & addrList, NetAddressNode startAddr, NetAddressNode endAddr, EServerRights srvRights) { + PrivilegedAddressBlock* addrBlock = NEW(PrivilegedAddressBlock); + + addrBlock->startAddress = startAddr; + addrBlock->serverRights = srvRights; + + if (endAddr == 0) + addrBlock->endAddress = addrBlock->startAddress; + else + addrBlock->endAddress = endAddr; + + if ( (addrBlock->startAddress & CLASS_C_SUBNET_MASK) != (addrBlock->endAddress & CLASS_C_SUBNET_MASK) ) { + LogMsg(kLogDebug, L"IniSrv: Error creating privileged address block - start address and end address aren't from the same subnet."); + DEL(addrBlock); + } + else { + addrBlock->SetValue(startAddr & CLASS_C_SUBNET_MASK); + addrList.Add(addrBlock); + } +} + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +EServerRights SrvIniGetServerRightsByNode (NetAddressNode addrNode) { + EServerRights retVal = kSrvRightsBasic; + unsigned addrSubNet = (addrNode & CLASS_C_SUBNET_MASK); + + s_critsect.Enter(); + { + PrivilegedAddressBlock* addrBlock = s_addressBlocks.Find(addrSubNet); + while (addrBlock) { + if (addrBlock->startAddress <= addrNode && addrNode <= addrBlock->endAddress) { + retVal = addrBlock->serverRights; + break; + } + + addrBlock = s_addressBlocks.FindNext(addrSubNet, addrBlock); + } + } + s_critsect.Leave(); + + return retVal; +} + +//============================================================================ +EServerRights SrvIniGetServerRights (const NetAddress & addr) { + NetAddressNode addrNode = NetAddressGetNode(addr); + + return SrvIniGetServerRightsByNode(addrNode); +} + +//============================================================================ +void SrvIniParseServerRights (Ini * ini) { + unsigned iter; + const IniValue *value; + ADDRESS_BLOCK_TABLE newaddresstable; + ADDRESS_BLOCK_TABLE removeaddresstable; + + value = IniGetFirstValue( + ini, + L"Privileged Addresses", + L"Addr", + &iter + ); + + // add ini file address blocks + while (value) { + wchar valStr[20]; + NetAddressNode start; + NetAddressNode end; + EServerRights rights; + + IniGetString(value, valStr, arrsize(valStr), 0); + start = NetAddressNodeFromString(valStr, nil); + + IniGetString(value, valStr, arrsize(valStr), 1); + end = NetAddressNodeFromString(valStr, nil); + + IniGetString(value, valStr, arrsize(valStr), 2); + rights = GetServerRightsFromString(valStr); + + IAddAddressBlock(newaddresstable, start, end, rights); + + value = IniGetNextValue(value, &iter); + } + + // Add local addresses and loopback + NetAddressNode nodes[16]; + unsigned count = NetAddressGetLocal(arrsize(nodes), nodes); + + for (unsigned i = 0; i < count; ++i) { + IAddAddressBlock(newaddresstable, nodes[i], nodes[i], kSrvRightsServer); + } + IAddAddressBlock(newaddresstable, LOOPBACK_ADDRESS_NODE, LOOPBACK_ADDRESS_NODE, kSrvRightsServer); + + s_critsect.Enter(); + { + while (PrivilegedAddressBlock* addrBlock = s_addressBlocks.Head()) + removeaddresstable.Add(addrBlock); + + while (PrivilegedAddressBlock* addrBlock = newaddresstable.Head()) + s_addressBlocks.Add(addrBlock); + } + s_critsect.Leave(); + + removeaddresstable.Clear(); +} + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plControlDefinition.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plControlDefinition.h new file mode 100644 index 00000000..88329f2f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plControlDefinition.h @@ -0,0 +1,133 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plControlDefinition_inc +#define plControlDefinition_inc + +#include "plControlEventCodes.h" +#include "plKeyDef.h" +#include "hsGeometry3.h" + +// flags for control event codes +enum +{ + kControlFlagNormal = 0x00000001, + kControlFlagNoRepeat = 0x00000002, + kControlFlagDownEvent = 0x00000004, + kControlFlagUpEvent = 0x00000008, + kControlFlagToggle = 0x00000010, + kControlFlagXAxisEvent = 0x00000020, + kControlFlagYAxisEvent = 0x00000040, + kControlFlagRangePos = 0x00000080, + kControlFlagRangeNeg = 0x00000100, + // = 0x00000200, + // = 0x00000400, + kControlFlagConsoleCommand = 0x00000800, + kControlFlagLeftButton = 0x00001000, + kControlFlagRightButton = 0x00002000, + kControlFlagLeftButtonEx = 0x00004000, + kControlFlagRightButtonEx = 0x00008000, + kControlFlagBoxDisable = 0x00010000, + kControlFlagInBox = 0x00020000, + kControlFlagCapsLock = 0x00040000, + kControlFlagNetPropagate = 0x00080000, // Not supported anymore, but save the flag. + kControlFlagLeftButtonUp = 0x00100000, + kControlFlagRightButtonUp = 0x00200000, + kControlFlagRightButtonRepeat = 0x00400000, + kControlFlagLeftButtonRepeat = 0x00800000, + kControlFlagNoDeactivate = 0x01000000, + kControlFlagDelta = 0x02000000, + kControlFlagMiddleButton = 0x04000000, + kControlFlagMiddleButtonEx = 0x08000000, + kControlFlagMiddleButtonRepeat = 0x10000000, + kControlFlagMiddleButtonUp = 0x20000000, +}; + +// mouse button flags +enum +{ + kLeftButtonDown = 0x0001, + kLeftButtonUp = 0x0002, + kRightButtonDown = 0x0004, + kRightButtonUp = 0x0008, + kMiddleButtonDown = 0x0010, + kMiddleButtonUp = 0x0020, + kWheelPos = 0x0040, + kWheelNeg = 0x0080, + KWheelButtonDown = 0x0100, + kWheelButtonUp = 0x0200, + kLeftButtonRepeat = 0x0400, + kRightButtonRepeat = 0x0800, + kMiddleButtonRepeat = 0x1000, + kLeftButtonDblClk = 0x2000, + kRightButtonDblClk = 0x4000, + kAnyButtonDown = kLeftButtonDown | kRightButtonDown | kMiddleButtonDown, +}; + +// mouse cursor flags +enum +{ + kMouseNormal = 0x0000, + kMouseClickable = 0x0001, +}; + + +struct Win32keyConvert +{ + UInt32 fVKey; + char* fKeyName; +}; + +struct CommandConvert +{ + ControlEventCode fCode; + char* fDesc; +}; + + +struct plMouseInfo +{ + plMouseInfo(ControlEventCode _code, UInt32 _flags, hsPoint4 _box, char* _desc) + { + fCode = _code; + fControlFlags = _flags; + fBox = _box; + fControlDescription = _desc; + } + plMouseInfo(ControlEventCode _code, UInt32 _flags, hsScalar pt1, hsScalar pt2, hsScalar pt3, hsScalar pt4, char* _desc) + { + fCode = _code; + fControlFlags = _flags; + fBox.Set(pt1,pt2,pt3,pt4); + fControlDescription = _desc; + } + ControlEventCode fCode; + UInt32 fControlFlags; + hsPoint4 fBox; + char* fControlDescription; +}; + + +#endif // plControlDefinition_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plControlEventCodes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plControlEventCodes.h new file mode 100644 index 00000000..7412e050 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plControlEventCodes.h @@ -0,0 +1,146 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plControlEventCodes_inc +#define plControlEventCodes_inc + + +// list of all controls that could potentially +// be mapped to a key, a mouse button, etc + +// NOTE: +// NOTE: +// NOTE: PLEASE READ BEFORE MODIFYING THIS FILE!!!!!!!!!!!!!!!!!!! +// NOTE: +// NOTE: +// Please put new control key events at the end of this enum (just before END_CONTROLS). +// This way the corresponding Python constants will match more often +// and cause much less stress on game play programmers who's telescopes stop working +// Thanks! + +enum ControlEventCode +{ + // button controls + B_CONTROL_ACTION = 0, + B_CONTROL_ACTION_MOUSE, + B_CONTROL_JUMP, + B_CONTROL_MOVE_FORWARD, + B_CONTROL_MOVE_BACKWARD, + B_CONTROL_STRAFE_LEFT, + B_CONTROL_STRAFE_RIGHT, + B_CONTROL_MOVE_UP, + B_CONTROL_MOVE_DOWN, + B_CONTROL_ROTATE_LEFT, + B_CONTROL_ROTATE_RIGHT, + B_CONTROL_ROTATE_UP, + B_CONTROL_ROTATE_DOWN, + B_CONTROL_MODIFIER_FAST, + B_CONTROL_ALWAYS_RUN, // This is no longer a bindable key. It is hard-coded to caps-lock + B_CONTROL_EQUIP, + B_CONTROL_DROP, + B_CONTROL_TURN_TO, + B_TOGGLE_DRIVE_MODE, + B_CAMERA_MOVE_FORWARD, + B_CAMERA_MOVE_BACKWARD, + B_CAMERA_MOVE_UP, + B_CAMERA_MOVE_DOWN, + B_CAMERA_MOVE_LEFT, + B_CAMERA_MOVE_RIGHT, + B_CAMERA_PAN_UP, + B_CAMERA_PAN_DOWN, + B_CAMERA_PAN_LEFT, + B_CAMERA_PAN_RIGHT, + B_CAMERA_MOVE_FAST, + B_CAMERA_ROTATE_RIGHT, + B_CAMERA_ROTATE_LEFT, + B_CAMERA_ROTATE_UP, + B_CAMERA_ROTATE_DOWN, + B_CAMERA_RECENTER, + B_CAMERA_DRIVE_SPEED_UP, + B_CAMERA_DRIVE_SPEED_DOWN, + B_CAMERA_ZOOM_IN, + B_CAMERA_ZOOM_OUT, + B_SET_CONSOLE_MODE, + B_CONTROL_CONSOLE_COMMAND, + B_CONTROL_TOGGLE_PHYSICAL, + B_CONTROL_PICK, + // axis controls + A_CONTROL_MOVE, + A_CONTROL_TURN, + A_CONTROL_MOUSE_X, + A_CONTROL_MOUSE_Y, + // special controls + S_SET_CURSOR_UP, + S_SET_CURSOR_DOWN, + S_SET_CURSOR_RIGHT, + S_SET_CURSOR_LEFT, + S_SET_CURSOR_POISED, + S_SET_CURSOR_HIDDEN, + S_SET_CURSOR_UNHIDDEN, + S_SET_CURSOR_ARROW, + S_SEARCH_FOR_PICKABLE, + S_INCREASE_MIC_VOL, + S_DECREASE_MIC_VOL, + S_PUSH_TO_TALK, + S_SET_THIRD_PERSON_MODE, + S_SET_FIRST_PERSON_MODE, + S_SET_WALK_MODE, + S_SET_FREELOOK, + S_SET_CONSOLE_SINGLE, + S_SET_CONSOLE_HIDDEN, + // Inventory controls + dead__B_CONTROL_SET_EQUIPED_STATE, + dead__B_CONTROL_SCROLL_UP_LIST, + dead__B_CONTROL_SCROLL_DOWN_LIST, + dead__B_CONTROL_SET_INVENTORY_ACTIVE, + dead__B_CONTROL_SET_INVENTORY_DISACTIVE, + dead__B_CONTROL_REMOVE_INV_OBJECT, + dead__B_CONTROL_ENABLE_OBJECT, + // Avatar emote controls + B_CONTROL_EMOTE, + B_CONTROL_EXIT_MODE, + // new controls key events + B_CONTROL_DIVE, + B_CAMERA_PAN_TO_CURSOR, + B_CONTROL_OPEN_KI, + B_CONTROL_OPEN_BOOK, + B_CONTROL_EXIT_GUI_MODE, + B_CONTROL_MODIFIER_STRAFE, + B_CONTROL_CAMERA_WALK_PAN, + S_SET_BASIC_MODE, // Opposite of walk mode + B_CONTROL_IGNORE_AVATARS, //anti-griefing thing + B_CONTROL_LADDER_INVERTED, // used by ladders to invert forward/backward + B_CONTROL_CONSUMABLE_JUMP, // used to limit the avatar to one jump per keypress + S_SET_WALK_BACK_MODE, + S_SET_WALK_BACK_LB_MODE, + S_SET_CURSOR_UPWARD, + S_SET_LADDER_CONTROL, + S_CLEAR_LADDER_CONTROL, + END_CONTROLS +}; + + + +#endif // plControlEventCodes_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plInputMap.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plInputMap.cpp new file mode 100644 index 00000000..fb3a2a02 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plInputMap.cpp @@ -0,0 +1,1096 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// plInputDevice.cpp +#include "STRING" + +#include "plInputMap.h" +#include "plKeyMap.h" +#include "hsUtils.h" +#include "../plResMgr/plLocalization.h" + +ControlEventCode plInputMap::ConvertCharToControlCode(const char* c) +{ + for (int i = 0; fCmdConvert[i].fCode != END_CONTROLS; i++) + { + if (stricmp(fCmdConvert[i].fDesc, c) == 0) + return (fCmdConvert[i].fCode); + } + return (END_CONTROLS); +} + +const char *plInputMap::ConvertControlCodeToString( ControlEventCode code ) +{ + for( int i = 0; fCmdConvert[ i ].fCode != END_CONTROLS; i++ ) + { + if( fCmdConvert[ i ].fCode == code ) + return fCmdConvert[ i ].fDesc; + } + return nil; +} + +// +// plMouseMap +// + + +plMouseMap::~plMouseMap() +{ + for (int i = 0; i < fMap.Count(); i++) + delete(fMap[i]); + fMap.SetCountAndZero(0); +} + +int plMouseMap::AddMapping(plMouseInfo* pNfo) +{ + for (int i = 0; i < fMap.Count(); i++) + { + if (fMap[i] == pNfo) + return -1; + } + fMap.Append(pNfo); + return (fMap.Count() - 1); +} + + +//// plKeyBinding and Smaller Classes //////////////////////////////////////// + +plKeyCombo::plKeyCombo() +{ + fKey = KEY_UNMAPPED; + fFlags = 0; +} + +hsBool plKeyCombo::IsSatisfiedBy(const plKeyCombo &combo) const +{ + if (fKey != combo.fKey) + return false; + if ((fFlags & kShift) && !(combo.fFlags & kShift)) + return false; + if ((fFlags & kCtrl) && !(combo.fFlags & kCtrl)) + return false; + + return true; +} + +// Handy konstant for plKeyCombos +plKeyCombo plKeyCombo::kUnmapped = plKeyCombo( KEY_UNMAPPED, 0 ); + + + +plKeyBinding::plKeyBinding() +{ + fCode = END_CONTROLS; + fCodeFlags = 0; + fKey1 = plKeyCombo::kUnmapped; + fKey2 = plKeyCombo::kUnmapped; + fString = nil; +} + +plKeyBinding::plKeyBinding( ControlEventCode code, UInt32 codeFlags, const plKeyCombo &key1, const plKeyCombo &key2, const char *string /*= nil*/ ) +{ + fCode = code; + fCodeFlags = codeFlags; + fKey1 = key1; + fKey2 = key2; + fString = ( string == nil ) ? nil : hsStrcpy( string ); +} + +plKeyBinding::~plKeyBinding() +{ + delete [] fString; +} + +const plKeyCombo &plKeyBinding::GetMatchingKey( plKeyDef keyDef ) const +{ + if (fKey1.fKey == keyDef) + return fKey1; + if (fKey2.fKey == keyDef) + return fKey2; + + return plKeyCombo::kUnmapped; +} + +void plKeyBinding::SetKey1( const plKeyCombo &newCombo ) +{ + fKey1 = newCombo; +} + +void plKeyBinding::SetKey2( const plKeyCombo &newCombo ) +{ + fKey2 = newCombo; +} + +void plKeyBinding::ClearKeys( void ) +{ + fKey1 = fKey2 = plKeyCombo::kUnmapped; +} + +hsBool plKeyBinding::HasUnmappedKey() const +{ + return fKey1.fKey == KEY_UNMAPPED || fKey2.fKey == KEY_UNMAPPED; +} + +////////////////////////////////////////////////////////////////////////////// +//// plKeyMap Implementation ///////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +plKeyMap::plKeyMap() +{ +} + +plKeyMap::~plKeyMap() +{ + ClearAll(); +} + +void plKeyMap::ClearAll( void ) +{ + UInt32 i; + + + for( i = 0; i < fBindings.GetCount(); i++ ) + delete fBindings[ i ]; + fBindings.Reset(); +} + +//// AddCode ////////////////////////////////////////////////////////////////////////// +// Adds a given control code to the map. Once you add it, you can't change its flags. +// Returns false if the code is already present + +hsBool plKeyMap::AddCode( ControlEventCode code, UInt32 codeFlags ) +{ + if( IFindBinding( code ) != nil ) + return false; + + fBindings.Append( TRACKED_NEW plKeyBinding( code, codeFlags, plKeyCombo::kUnmapped, plKeyCombo::kUnmapped ) ); + return true; +} + +//// AddConsoleCommand /////////////////////////////////////////////////////// +// Same but for console commands. No flags b/c console commands always use +// the same flags. + +hsBool plKeyMap::AddConsoleCommand( const char *command ) +{ + if( IFindConsoleBinding( command ) != nil ) + return false; + + fBindings.Append( TRACKED_NEW plKeyBinding( B_CONTROL_CONSOLE_COMMAND, + kControlFlagDownEvent | kControlFlagNoRepeat | kControlFlagNoDeactivate, + plKeyCombo::kUnmapped, plKeyCombo::kUnmapped, + command ) ); + return true; +} + +//// IFindBinding //////////////////////////////////////////////////////////// +// Find the binding for a given code. + +plKeyBinding *plKeyMap::IFindBinding( ControlEventCode code ) const +{ + UInt32 i; + + + for( i = 0; i < fBindings.GetCount(); i++ ) + { + if( fBindings[ i ]->GetCode() == code ) + return fBindings[ i ]; + } + + return nil; +} + +//// IFindBindingByKey /////////////////////////////////////////////////////// +// Find the binding for a given key. + +plKeyBinding *plKeyMap::IFindBindingByKey( const plKeyCombo &combo ) const +{ + UInt32 i; + + + for( i = 0; i < fBindings.GetCount(); i++ ) + { + if( fBindings[ i ]->GetKey1() == combo || fBindings[ i ]->GetKey2() == combo ) + return fBindings[ i ]; + } + + return nil; +} + +// Find ALL bindings that could be triggered by this combo. Meaning that if we have multiple +// bindings for a key with different shift/ctrl combinations, we want any that are satisfied with +// the given combo. +// We guarantee that the first binding in the result array is that one with priority. +void plKeyMap::IFindAllBindingsByKey(const plKeyCombo &combo, hsTArray &result) const +{ + UInt32 i; + UInt8 bestScore = 0; + for (i = 0; i < fBindings.GetCount(); i++) + { + hsBool s1, s2; + s1 = fBindings[i]->GetKey1().IsSatisfiedBy(combo); + s2 = fBindings[i]->GetKey2().IsSatisfiedBy(combo); + if (s1 || s2) + { + UInt8 myScore = 0; + if (s1) + myScore = fBindings[i]->GetKey1().fFlags; + if (s2 && (fBindings[i]->GetKey2().fFlags > myScore)) + myScore = fBindings[i]->GetKey2().fFlags; + + if (myScore >= bestScore) + result.Insert(0, fBindings[i]); + else + result.Append(fBindings[i]); + } + } +} + +//// IFindConsoleBinding ///////////////////////////////////////////////////// +// You should be able to figure this out by now. + +plKeyBinding *plKeyMap::IFindConsoleBinding( const char *command ) const +{ + UInt32 i; + + + for( i = 0; i < fBindings.GetCount(); i++ ) + { + if( fBindings[ i ]->GetCode() == B_CONTROL_CONSOLE_COMMAND ) + { + if( stricmp( fBindings[ i ]->GetExtendedString(), command ) == 0 ) + return fBindings[ i ]; + } + } + + return nil; +} + +//// IActuallyBind /////////////////////////////////////////////////////////// +// Does the nitty gritty of binding by pref + +void plKeyMap::IActuallyBind( plKeyBinding *binding, const plKeyCombo &combo, BindPref pref ) +{ + // Bind according to preference + switch( pref ) + { + case kNoPreference: + // Pick a free slot, or 1st one if none + if( binding->GetKey1() == plKeyCombo::kUnmapped ) + binding->SetKey1( combo ); + else if( binding->GetKey2() == plKeyCombo::kUnmapped ) + binding->SetKey2( combo ); + else + binding->SetKey1( combo ); + break; + + case kNoPreference2nd: + // Pick a free slot, or 2nd one if none + if( binding->GetKey1() == plKeyCombo::kUnmapped ) + binding->SetKey1( combo ); + else if( binding->GetKey2() == plKeyCombo::kUnmapped ) + binding->SetKey2( combo ); + else + binding->SetKey2( combo ); + break; + + case kFirstAlways: + // Always bind to the first key + binding->SetKey1( combo ); + break; + + case kSecondAlways: + // You get the idea + binding->SetKey2( combo ); + break; + + default: + hsAssert( false, "Invalid bind preference in IActuallyBind()" ); + } +} + +//// BindKey ///////////////////////////////////////////////////////////////// +// Adds a key binding to a given code. Returns false if the code isn't in +// this map or if key is already mapped. + +hsBool plKeyMap::BindKey( const plKeyCombo &combo, ControlEventCode code, BindPref pref /*= kNoPreference*/ ) +{ + plKeyBinding* binding = nil; + + // Control combos are ok. Binding directly to control is not. + if ( combo.fKey == KEY_CTRL ) + return false; + + // unless we are bindind to unmappped... + if ( combo.fKey != KEY_UNMAPPED) + { + // Make sure key isn't already used + binding = IFindBindingByKey( combo ); + if( binding != nil ) + return false; + } + + // Get binding for this code + binding = IFindBinding( code ); + if( binding == nil ) + return false; + + IActuallyBind( binding, combo, pref ); + return true; +} + +//// BindKeyToConsoleCmd ///////////////////////////////////////////////////// +// Console command version + +hsBool plKeyMap::BindKeyToConsoleCmd( const plKeyCombo &combo, const char *command, BindPref pref /*= kNoPreference*/ ) +{ + plKeyBinding* binding = nil; + + // Control combos are ok. Binding directly to control is not. + if ( combo.fKey == KEY_CTRL ) + return false; + + // unless we are bindind to unmappped... + if ( combo.fKey != KEY_UNMAPPED) + { + // Make sure key isn't already used + binding = IFindBindingByKey( combo ); + if( binding != nil ) + return false; + } + + // Get binding for this code + binding = IFindConsoleBinding( command ); + if( binding == nil ) + return false; + + IActuallyBind( binding, combo, pref ); + return true; +} + +//// FindBinding ///////////////////////////////////////////////////////////// +// Searches for the binding for a given code. Returns nil if not found + +const plKeyBinding *plKeyMap::FindBinding( ControlEventCode code ) const +{ + return IFindBinding( code ); +} + +//// FindBindingByKey //////////////////////////////////////////////////////// +// Same thing but by key + +const plKeyBinding *plKeyMap::FindBindingByKey( const plKeyCombo &combo ) const +{ + return IFindBindingByKey( combo ); +} + +// Same thing, but returns multiple matches (see IFindAllBindingsByKey) +void plKeyMap::FindAllBindingsByKey( const plKeyCombo &combo, hsTArray &result ) const +{ + hsTArray bindings; + IFindAllBindingsByKey( combo, bindings ); + + int i; + for (i = 0; i < bindings.GetCount(); i++) + result.Append(bindings[i]); +} + + +const plKeyBinding* plKeyMap::FindConsoleBinding( const char *command ) const +{ + return IFindConsoleBinding(command); +} + +//// EnsureKeysClear ///////////////////////////////////////////////////////// +// Basically UnmapKey(), but for two keys at once and won't assert if you +// give it unmapped keys + +void plKeyMap::EnsureKeysClear( const plKeyCombo &key1, const plKeyCombo &key2 ) +{ + if( key1 != plKeyCombo::kUnmapped ) + UnmapKey( key1 ); + if( key2 != plKeyCombo::kUnmapped ) + UnmapKey( key2 ); +} + +//// UnmapKey //////////////////////////////////////////////////////////////// +// Unmaps the given key, no matter what binding it is in + +void plKeyMap::UnmapKey( const plKeyCombo &combo ) +{ + if( combo == plKeyCombo::kUnmapped ) + { + hsAssert( false, "Trying to unbind invalid key" ); + return; + } + + plKeyBinding *binding; + + // Just in case we're in multiple bindings, even tho we are guarding against + // that + while( ( binding = IFindBindingByKey( combo ) ) != nil ) + { + if( binding->GetKey1() == combo ) + binding->SetKey1( plKeyCombo::kUnmapped ); + if( binding->GetKey2() == combo ) + binding->SetKey2( plKeyCombo::kUnmapped ); + } +} + +//// UnmapBinding //////////////////////////////////////////////////////////// +// Unmaps the keys for a single binding + +void plKeyMap::UnmapBinding( ControlEventCode code ) +{ + plKeyBinding *binding = IFindBinding( code ); + if( binding != nil ) + binding->ClearKeys(); +} + +//// UnmapAllBindings //////////////////////////////////////////////////////// +// Unmaps all the bindings, but leaves the code records themselves + +void plKeyMap::UnmapAllBindings( void ) +{ + UInt32 i; + + for( i = 0; i < fBindings.GetCount(); i++ ) + fBindings[ i ]->ClearKeys(); +} + +//// EraseBinding //////////////////////////////////////////////////////////// +// Erases the given code binding. Note: should never really be used, but +// provided here for completeness + +void plKeyMap::EraseBinding( ControlEventCode code ) +{ + UInt32 i; + + + for( i = 0; i < fBindings.GetCount(); i++ ) + { + if( fBindings[ i ]->GetCode() == code ) + { + delete fBindings[ i ]; + fBindings.Remove( i ); + return; + } + } +} + + +char* plKeyMap::ConvertVKeyToChar( UInt32 vk ) +{ + Win32keyConvert* keyConvert = &fKeyConversionEnglish[0]; + switch (plLocalization::GetLanguage()) + { + case plLocalization::kFrench: + keyConvert = &fKeyConversionFrench[0]; + break; + case plLocalization::kGerman: + keyConvert = &fKeyConversionGerman[0]; + break; +// case plLocalization::kSpanish: +// keyConvert = &fKeyConversionSpanish[0]; +// break; +// case plLocalization::kItalian: +// keyConvert = &fKeyConversionItalian[0]; +// break; + + // default is English + + } + for (int i = 0; keyConvert[i].fVKey != 0xffffffff; i++) + { + if (keyConvert[i].fVKey == vk) + return (keyConvert[i].fKeyName); + } + + return nil; +} + +plKeyDef plKeyMap::ConvertCharToVKey( const char *c ) +{ + Win32keyConvert* keyConvert = &fKeyConversionEnglish[0]; + switch (plLocalization::GetLanguage()) + { + case plLocalization::kFrench: + keyConvert = &fKeyConversionFrench[0]; + break; + case plLocalization::kGerman: + keyConvert = &fKeyConversionGerman[0]; + break; +// case plLocalization::kSpanish: +// keyConvert = &fKeyConversionSpanish[0]; +// break; +// case plLocalization::kItalian: +// keyConvert = &fKeyConversionItalian[0]; +// break; + + // default is English + + } + for (int i = 0; keyConvert[i].fVKey != 0xffffffff; i++) + { + if (stricmp(keyConvert[i].fKeyName, c) == 0) + return (plKeyDef)(keyConvert[i].fVKey); + } + + // Is it just a single character? + if( isalnum( *c ) && strlen( c ) == 1 ) + return (plKeyDef)toupper( *c ); + + // if we didn't find anything yet... + // ...then look thru all the other language mappings that we know about, + // ...just in case they keep switching languages on us + if ( plLocalization::GetLanguage() != plLocalization::kEnglish) + { + for (int i = 0; fKeyConversionEnglish[i].fVKey != 0xffffffff; i++) + { + if (stricmp(fKeyConversionEnglish[i].fKeyName, c) == 0) + return (plKeyDef)(fKeyConversionEnglish[i].fVKey); + } + } + if ( plLocalization::GetLanguage() != plLocalization::kFrench) + { + for (int i = 0; fKeyConversionFrench[i].fVKey != 0xffffffff; i++) + { + if (stricmp(fKeyConversionFrench[i].fKeyName, c) == 0) + return (plKeyDef)(fKeyConversionFrench[i].fVKey); + } + } + if ( plLocalization::GetLanguage() != plLocalization::kGerman) + { + for (int i = 0; fKeyConversionGerman[i].fVKey != 0xffffffff; i++) + { + if (stricmp(fKeyConversionGerman[i].fKeyName, c) == 0) + return (plKeyDef)(fKeyConversionGerman[i].fVKey); + } + } + /* + if ( plLocalization::GetLanguage() != plLocalization::kSpanish) + { + for (int i = 0; fKeyConversionSpanish[i].fVKey != 0xffffffff; i++) + { + if (stricmp(fKeyConversionSpanish[i].fKeyName, c) == 0) + return (plKeyDef)(fKeyConversionSpanish[i].fVKey); + } + } + if ( plLocalization::GetLanguage() != plLocalization::kItalian) + { + for (int i = 0; fKeyConversionItalian[i].fVKey != 0xffffffff; i++) + { + if (stricmp(fKeyConversionItalian[i].fKeyName, c) == 0) + return (plKeyDef)(fKeyConversionItalian[i].fVKey); + } + } + */ + + // finally, just give up... unmapped! + return KEY_UNMAPPED; +} + +const char* plKeyMap::GetStringCtrl() +{ + switch (plLocalization::GetLanguage()) + { + case plLocalization::kFrench: + return "Ctrl+"; + break; + case plLocalization::kGerman: + return "Strg+"; + break; +/* case plLocalization::kSpanish: + return "Ctrl+"; + break; + case plLocalization::kItalian: + return "Ctrl+"; + break; +*/ + // default is English + + } + return "Ctrl+"; +} + +const char* plKeyMap::GetStringShift() +{ + switch (plLocalization::GetLanguage()) + { + case plLocalization::kFrench: + return "Maj+"; + break; + case plLocalization::kGerman: + return "Umschalt+"; + break; +/* case plLocalization::kSpanish: + return "Mayúsculas+"; + break; + case plLocalization::kItalian: + return "Shift+"; + break; +*/ + // default is English + + } + return "Shift+"; +} + +const char* plKeyMap::GetStringUnmapped() +{ + switch (plLocalization::GetLanguage()) + { + case plLocalization::kFrench: + return "(NonDéfini)"; + break; + case plLocalization::kGerman: + return "(NichtZugewiesen)"; + break; +/* case plLocalization::kSpanish: + return "(SinMapear)"; + break; + case plLocalization::kItalian: + return "(NonAssegnato)"; + break; +*/ + // default is English + + } + return "(unmapped)"; +} + +// If the binding has one of these keys, but not the other, go and bind the other +// (if there's an unmapped space for it). +void plKeyMap::HandleAutoDualBinding(plKeyDef key1, plKeyDef key2) +{ + ICheckAndBindDupe(key1, key2); + ICheckAndBindDupe(key2, key1); +} + +void plKeyMap::ICheckAndBindDupe(plKeyDef origKey, plKeyDef dupeKey) +{ + hsTArray bindings; + plKeyCombo combo; + combo.fKey = origKey; + IFindAllBindingsByKey(combo, bindings); + + int i; + for (i = 0; i < bindings.GetCount(); i++) + { + plKeyBinding *binding = bindings[i]; + if (binding->HasUnmappedKey()) + { + combo = binding->GetMatchingKey(origKey); + combo.fKey = dupeKey; + if (IFindBindingByKey(combo) == nil) + IActuallyBind(binding, combo, kNoPreference); + } + } +} + +Win32keyConvert plKeyMap::fKeyConversionEnglish[] = +{ + { VK_F1, "F1"}, + { VK_F2, "F2"}, + { VK_F3, "F3"}, + { VK_F4, "F4"}, + { VK_F5, "F5"}, + { VK_F6, "F6"}, + { VK_F7, "F7"}, + { VK_F8, "F8"}, + { VK_F9, "F9"}, + { VK_F10, "F10"}, + { VK_F11, "F11"}, + { VK_F12, "F12"}, + { VK_ESCAPE, "Esc"}, + { VK_TAB, "Tab"}, + { VK_UP, "UpArrow"}, + { VK_DOWN, "DownArrow"}, + { VK_LEFT, "LeftArrow"}, + { VK_RIGHT, "RightArrow"}, + { VK_BACK, "Backspace"}, + { VK_RETURN, "Enter"}, + { VK_PAUSE, "Pause"}, + { VK_CAPITAL, "CapsLock"}, + { VK_PRIOR, "PageUp"}, + { VK_NEXT, "PageDn"}, + { VK_END, "End"}, + { VK_HOME, "Home"}, + { VK_SNAPSHOT, "PrintScrn"}, + { VK_INSERT, "Insert"}, + { VK_DELETE, "Delete"}, + { VK_NUMPAD0, "NumPad0"}, + { VK_NUMPAD1, "NumPad1"}, + { VK_NUMPAD2, "NumPad2"}, + { VK_NUMPAD3, "NumPad3"}, + { VK_NUMPAD4, "NumPad4"}, + { VK_NUMPAD5, "NumPad5"}, + { VK_NUMPAD6, "NumPad6"}, + { VK_NUMPAD7, "NumPad7"}, + { VK_NUMPAD8, "NumPad8"}, + { VK_NUMPAD9, "NumPad9"}, + { VK_MULTIPLY, "NumPad*"}, + { VK_ADD, "NumPad+"}, + { VK_SUBTRACT, "NumPad-"}, + { VK_DECIMAL, "NumPad."}, + { VK_DIVIDE, "NumPad/"}, + { VK_SPACE, "SpaceBar"}, + { VK_OEM_COMMA, "Comma"}, + { VK_OEM_PERIOD,"Period"}, + { VK_OEM_MINUS, "Minus"}, + { VK_OEM_PLUS, "Plus"}, + { VK_SHIFT, "Shift" }, + // not valid outside USA + { VK_OEM_1, "Semicolon"}, + { VK_OEM_2, "ForewardSlash"}, + { VK_OEM_3, "Tilde"}, + { VK_OEM_4, "LeftBracket"}, + { VK_OEM_5, "Backslash"}, + { VK_OEM_6, "RightBracket"}, + { VK_OEM_7, "Quote"}, + + { 0xffffffff, "Unused"}, +}; + +Win32keyConvert plKeyMap::fKeyConversionFrench[] = +{ + { VK_F1, "F1"}, + { VK_F2, "F2"}, + { VK_F3, "F3"}, + { VK_F4, "F4"}, + { VK_F5, "F5"}, + { VK_F6, "F6"}, + { VK_F7, "F7"}, + { VK_F8, "F8"}, + { VK_F9, "F9"}, + { VK_F10, "F10"}, + { VK_F11, "F11"}, + { VK_F12, "F12"}, + { VK_ESCAPE, "Échap"}, + { VK_TAB, "Tab"}, + { VK_UP, "FlècheHaut"}, + { VK_DOWN, "FlècheBas"}, + { VK_LEFT, "FlècheGauche"}, + { VK_RIGHT, "FlècheDroite"}, + { VK_BACK, "Retour"}, + { VK_RETURN, "Entrée"}, + { VK_PAUSE, "Pause"}, + { VK_CAPITAL, "CapsLock"}, + { VK_PRIOR, "PagePréc"}, + { VK_NEXT, "PageSuiv"}, + { VK_END, "Fin"}, + { VK_HOME, "Origine"}, + { VK_SNAPSHOT, "ImprÉcran"}, + { VK_INSERT, "Inser"}, + { VK_DELETE, "Suppr"}, + { VK_NUMPAD0, "PavNum0"}, + { VK_NUMPAD1, "PavNum1"}, + { VK_NUMPAD2, "PavNum2"}, + { VK_NUMPAD3, "PavNum3"}, + { VK_NUMPAD4, "PavNum4"}, + { VK_NUMPAD5, "PavNum5"}, + { VK_NUMPAD6, "PavNum6"}, + { VK_NUMPAD7, "PavNum7"}, + { VK_NUMPAD8, "PavNum8"}, + { VK_NUMPAD9, "PavNum9"}, + { VK_MULTIPLY, "PavNum*"}, + { VK_ADD, "PavNum+"}, + { VK_SUBTRACT, "PavNum-"}, + { VK_DECIMAL, "PavNum."}, + { VK_DIVIDE, "PavNum/"}, + { VK_SPACE, "Espace"}, + { VK_OEM_COMMA, "Virgule"}, + { VK_OEM_PERIOD,"Point"}, + { VK_OEM_MINUS, "Moins"}, + { VK_OEM_PLUS, "Plus"}, + { VK_SHIFT, "Maj" }, + // not valid outside USA + { VK_OEM_1, "Point-virgule"}, + { VK_OEM_2, "BarreOblique"}, + { VK_OEM_3, "Tilde"}, + { VK_OEM_4, "ParenthèseG"}, + { VK_OEM_5, "BarreInverse"}, + { VK_OEM_6, "ParenthèseD"}, + { VK_OEM_7, "Guillemet"}, + + { 0xffffffff, "Unused"}, +}; + +Win32keyConvert plKeyMap::fKeyConversionGerman[] = +{ + { VK_F1, "F1"}, + { VK_F2, "F2"}, + { VK_F3, "F3"}, + { VK_F4, "F4"}, + { VK_F5, "F5"}, + { VK_F6, "F6"}, + { VK_F7, "F7"}, + { VK_F8, "F8"}, + { VK_F9, "F9"}, + { VK_F10, "F10"}, + { VK_F11, "F11"}, + { VK_F12, "F12"}, + { VK_ESCAPE, "Esc"}, + { VK_TAB, "Tab"}, + { VK_UP, "PfeilHoch"}, + { VK_DOWN, "PfeilRunter"}, + { VK_LEFT, "PfeilLinks"}, + { VK_RIGHT, "PfeilRechts"}, + { VK_BACK, "Backspace"}, + { VK_RETURN, "Enter"}, + { VK_PAUSE, "Pause"}, + { VK_CAPITAL, "Feststelltaste"}, + { VK_PRIOR, "BildHoch"}, + { VK_NEXT, "BildRunter"}, + { VK_END, "Ende"}, + { VK_HOME, "Pos1"}, + { VK_SNAPSHOT, "Druck"}, + { VK_INSERT, "Einf"}, + { VK_DELETE, "Entf"}, + { VK_NUMPAD0, "ZB0"}, + { VK_NUMPAD1, "ZB1"}, + { VK_NUMPAD2, "ZB2"}, + { VK_NUMPAD3, "ZB3"}, + { VK_NUMPAD4, "ZB4"}, + { VK_NUMPAD5, "ZB5"}, + { VK_NUMPAD6, "ZB6"}, + { VK_NUMPAD7, "ZB7"}, + { VK_NUMPAD8, "ZB8"}, + { VK_NUMPAD9, "ZB9"}, + { VK_MULTIPLY, "ZB*"}, + { VK_ADD, "ZB+"}, + { VK_SUBTRACT, "ZB-"}, + { VK_DECIMAL, "ZB."}, + { VK_DIVIDE, "ZB/"}, + { VK_SPACE, "Leertaste"}, + { VK_OEM_COMMA, "Komma"}, + { VK_OEM_PERIOD,"Punkt"}, + { VK_OEM_MINUS, "Minus"}, + { VK_OEM_PLUS, "Plus"}, + { VK_SHIFT, "Umschalt" }, + // not valid outside USA + { VK_OEM_1, "Ü"}, + { VK_OEM_2, "#"}, + { VK_OEM_3, "Ö"}, + { VK_OEM_4, "ß"}, + { VK_OEM_5, "Backslash"}, + { VK_OEM_6, "Akzent"}, + { VK_OEM_7, "Ä"}, + + { 0xffffffff, "Unused"}, +}; + +/* +Win32keyConvert plKeyMap::fKeyConversionSpanish[] = +{ + { VK_F1, "F1"}, + { VK_F2, "F2"}, + { VK_F3, "F3"}, + { VK_F4, "F4"}, + { VK_F5, "F5"}, + { VK_F6, "F6"}, + { VK_F7, "F7"}, + { VK_F8, "F8"}, + { VK_F9, "F9"}, + { VK_F10, "F10"}, + { VK_F11, "F11"}, + { VK_F12, "F12"}, + { VK_ESCAPE, "Esc"}, + { VK_TAB, "Tabulador"}, + { VK_UP, "CursorArriba"}, + { VK_DOWN, "CursorAbajo"}, + { VK_LEFT, "CursorIzquierdo"}, + { VK_RIGHT, "CursorDerecho"}, + { VK_BACK, "Retroceso"}, + { VK_RETURN, "Intro"}, + { VK_PAUSE, "Pausa"}, + { VK_CAPITAL, "BloqMayús"}, + { VK_PRIOR, "RePág"}, + { VK_NEXT, "AVPág"}, + { VK_END, "Fin"}, + { VK_HOME, "Inicio"}, + { VK_SNAPSHOT, "ImprPetSis"}, + { VK_INSERT, "Insert"}, + { VK_DELETE, "Supr"}, + { VK_NUMPAD0, "TecNum0"}, + { VK_NUMPAD1, "TecNum1"}, + { VK_NUMPAD2, "TecNum2"}, + { VK_NUMPAD3, "TecNum3"}, + { VK_NUMPAD4, "TecNum4"}, + { VK_NUMPAD5, "TecNum5"}, + { VK_NUMPAD6, "TecNum6"}, + { VK_NUMPAD7, "TecNum7"}, + { VK_NUMPAD8, "TecNum8"}, + { VK_NUMPAD9, "TecNum9"}, + { VK_MULTIPLY, "TecNum*"}, + { VK_ADD, "TecNum+"}, + { VK_SUBTRACT, "TecNum-"}, + { VK_DECIMAL, "TecNum."}, + { VK_DIVIDE, "TecNum/"}, + { VK_SPACE, "BarraEspacio"}, + { VK_OEM_COMMA, "Coma"}, + { VK_OEM_PERIOD,"Punto"}, + { VK_OEM_MINUS, "Menos"}, + { VK_OEM_PLUS, "Más"}, + { VK_SHIFT, "Mayúsculas" }, + // not valid outside USA + { VK_OEM_1, "PuntoYComa"}, + { VK_OEM_2, "Barra"}, + { VK_OEM_3, "Tilde"}, + { VK_OEM_4, "AbrirParéntesis"}, + { VK_OEM_5, "BarraInvertida"}, + { VK_OEM_6, "CerrarParéntesis"}, + { VK_OEM_7, "Comillas"}, + + { 0xffffffff, "Unused"}, +}; + +Win32keyConvert plKeyMap::fKeyConversionItalian[] = +{ + { VK_F1, "F1"}, + { VK_F2, "F2"}, + { VK_F3, "F3"}, + { VK_F4, "F4"}, + { VK_F5, "F5"}, + { VK_F6, "F6"}, + { VK_F7, "F7"}, + { VK_F8, "F8"}, + { VK_F9, "F9"}, + { VK_F10, "F10"}, + { VK_F11, "F11"}, + { VK_F12, "F12"}, + { VK_ESCAPE, "Esc"}, + { VK_TAB, "Tab"}, + { VK_UP, "FrecciaSu"}, + { VK_DOWN, "FrecciaGiù"}, + { VK_LEFT, "FrecciaSx"}, + { VK_RIGHT, "FrecciaDx"}, + { VK_BACK, "Backspace"}, + { VK_RETURN, "Invio"}, + { VK_PAUSE, "Pausa"}, + { VK_CAPITAL, "BlocMaiusc"}, + { VK_PRIOR, "PagSu"}, + { VK_NEXT, "PagGiù"}, + { VK_END, "Fine"}, + { VK_HOME, "Home"}, + { VK_SNAPSHOT, "Stamp"}, + { VK_INSERT, "Ins"}, + { VK_DELETE, "Canc"}, + { VK_NUMPAD0, "TastNum0"}, + { VK_NUMPAD1, "TastNum1"}, + { VK_NUMPAD2, "TastNum2"}, + { VK_NUMPAD3, "TastNum3"}, + { VK_NUMPAD4, "TastNum4"}, + { VK_NUMPAD5, "TastNum5"}, + { VK_NUMPAD6, "TastNum6"}, + { VK_NUMPAD7, "TastNum7"}, + { VK_NUMPAD8, "TastNum8"}, + { VK_NUMPAD9, "TastNum9"}, + { VK_MULTIPLY, "TastNum*"}, + { VK_ADD, "TastNum+"}, + { VK_SUBTRACT, "TastNum-"}, + { VK_DECIMAL, "TastNum."}, + { VK_DIVIDE, "TastNum/"}, + { VK_SPACE, "Spazio"}, + { VK_OEM_COMMA, "Virgola"}, + { VK_OEM_PERIOD,"Punto"}, + { VK_OEM_MINUS, "Meno"}, + { VK_OEM_PLUS, "QuadraDx"}, + { VK_SHIFT, "Shift" }, + // not valid outside USA + { VK_OEM_1, "QuadraSx"}, + { VK_OEM_2, "ù"}, + { VK_OEM_3, "ò"}, + { VK_OEM_4, "Apostrofo"}, + { VK_OEM_5, "\\"}, + { VK_OEM_6, "ì"}, + { VK_OEM_7, "à"}, + + { 0xffffffff, "Unused"}, +}; +*/ + + + +CommandConvert plInputMap::fCmdConvert[] = +{ + + { B_CONTROL_ACTION, "Use Key" }, + { B_CONTROL_JUMP, "Jump Key" }, + { B_CONTROL_DIVE, "Dive Key" }, + { B_CONTROL_MOVE_FORWARD, "Walk Forward" }, + { B_CONTROL_MOVE_BACKWARD, "Walk Backward" }, + { B_CONTROL_STRAFE_LEFT, "Strafe Left" }, + { B_CONTROL_STRAFE_RIGHT, "Strafe Right" }, + { B_CONTROL_MOVE_UP, "Move Up" }, + { B_CONTROL_MOVE_DOWN, "Move Down" }, + { B_CONTROL_ROTATE_LEFT, "Turn Left" }, + { B_CONTROL_ROTATE_RIGHT, "Turn Right" }, + { B_CONTROL_ROTATE_UP, "Turn Up" }, + { B_CONTROL_ROTATE_DOWN, "Turn Down" }, + { B_CONTROL_MODIFIER_FAST, "Fast Modifier" }, + { B_CONTROL_EQUIP, "PickUp Item" }, + { B_CONTROL_DROP, "Drop Item" }, + { B_TOGGLE_DRIVE_MODE, "Drive" }, + { B_CONTROL_ALWAYS_RUN, "Always Run" }, + { B_CAMERA_MOVE_FORWARD, "Camera Forward"}, + { B_CAMERA_MOVE_BACKWARD, "Camera Backward"}, + { B_CAMERA_MOVE_UP, "Camera Up"}, + { B_CAMERA_MOVE_DOWN, "Camera Down"}, + { B_CAMERA_MOVE_LEFT, "Camera Left"}, + { B_CAMERA_MOVE_RIGHT, "Camera Right"}, + { B_CAMERA_MOVE_FAST, "Camera Fast"}, + { B_CAMERA_ROTATE_RIGHT, "Camera Yaw Right"}, + { B_CAMERA_ROTATE_LEFT, "Camera Yaw Left"}, + { B_CAMERA_ROTATE_UP, "Camera Pitch Up"}, + { B_CAMERA_ROTATE_DOWN, "Camera Pitch Down"}, + { B_CAMERA_PAN_UP, "Camera Pan Up"}, + { B_CAMERA_PAN_DOWN, "Camera Pan Down"}, + { B_CAMERA_PAN_LEFT, "Camera Pan Left"}, + { B_CAMERA_PAN_RIGHT, "Camera Pan Right"}, + { B_CAMERA_PAN_TO_CURSOR, "Camera Pan To Cursor"}, + { B_CAMERA_RECENTER, "Camera Recenter"}, + { B_SET_CONSOLE_MODE, "Console"}, + { B_CAMERA_DRIVE_SPEED_UP, "Decrease Camera Drive Speed" }, + { B_CAMERA_DRIVE_SPEED_DOWN, "Increase Camera Drive Speed" }, + { S_INCREASE_MIC_VOL, "Increase Microphone Sensitivity" }, + { S_DECREASE_MIC_VOL, "Decrease Microphone Sensitivity" }, + { S_PUSH_TO_TALK, "Push to talk" }, + { S_SET_WALK_MODE, "Set Walk Mode" }, + { B_CONTROL_TURN_TO, "Turn To Click" }, + { B_CONTROL_TOGGLE_PHYSICAL, "Toggle Physical" }, + { S_SET_FIRST_PERSON_MODE, "Toggle First Person" }, + { B_CAMERA_ZOOM_IN, "Camera Zoom In" }, + { B_CAMERA_ZOOM_OUT, "Camera Zoom Out" }, + { B_CONTROL_EXIT_MODE, "Exit Mode" }, + { B_CONTROL_OPEN_KI, "Open KI" }, + { B_CONTROL_OPEN_BOOK, "Open Player Book" }, + { B_CONTROL_EXIT_GUI_MODE, "Exit GUI Mode" }, + { B_CONTROL_MODIFIER_STRAFE, "Strafe Modifier" }, + + + { END_CONTROLS, ""}, + +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plInputMap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plInputMap.h new file mode 100644 index 00000000..40b62c4f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plInputMap.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// plInputDevice.h + +#ifndef PL_INPUT_MAP_H +#define PL_INPUT_MAP_H + +#include "hsTypes.h" +#include "plControlDefinition.h" +#include "hsTemplates.h" + +class plInputMap +{ +public: + static CommandConvert fCmdConvert[]; + static ControlEventCode ConvertCharToControlCode(const char* c); + static const char *ConvertControlCodeToString( ControlEventCode code ); +}; + +class plMouseMap : public plInputMap +{ +public: + ~plMouseMap(); + hsTArray fMap; + int AddMapping(plMouseInfo* pNfo); +}; + +#endif // PL_INPUT_DEVICE_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plKeyDef.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plKeyDef.h new file mode 100644 index 00000000..d88d72a6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plKeyDef.h @@ -0,0 +1,157 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plKeyDef_inc +#define plKeyDef_inc + +#include "hsConfig.h" + +#if HS_BUILD_FOR_WIN32 + +#include "windows.h" + +#define VK_BACK_QUOTE 0xc0 +// +// keyboard definitions: +// +// map O.S. specific keyboard defines +// to plasma key defines here... +// +// + +// for win32: + +enum plKeyDef +{ + KEY_A = 'A', + KEY_B = 'B', + KEY_C = 'C', + KEY_D = 'D', + KEY_E = 'E', + KEY_F = 'F', + KEY_G = 'G', + KEY_H = 'H', + KEY_I = 'I', + KEY_J = 'J', + KEY_K = 'K', + KEY_L = 'L', + KEY_M = 'M', + KEY_N = 'N', + KEY_O = 'O', + KEY_P = 'P', + KEY_Q = 'Q', + KEY_R = 'R', + KEY_S = 'S', + KEY_T = 'T', + KEY_U = 'U', + KEY_V = 'V', + KEY_W = 'W', + KEY_X = 'X', + KEY_Y = 'Y', + KEY_Z = 'Z', + KEY_0 = 0x30, + KEY_1 = 0x31, + KEY_2 = 0x32, + KEY_3 = 0x33, + KEY_4 = 0x34, + KEY_5 = 0x35, + KEY_6 = 0x36, + KEY_7 = 0x37, + KEY_8 = 0x38, + KEY_9 = 0x39, + KEY_F1 = VK_F1, + KEY_F2 = VK_F2, + KEY_F3 = VK_F3, + KEY_F4 = VK_F4, + KEY_F5 = VK_F5, + KEY_F6 = VK_F6, + KEY_F7 = VK_F7, + KEY_F8 = VK_F8, + KEY_F9 = VK_F9, + KEY_F10 = VK_F10, + KEY_F11 = VK_F11, + KEY_F12 = VK_F12, + KEY_ESCAPE = VK_ESCAPE, + KEY_TAB = VK_TAB, + KEY_SHIFT = VK_SHIFT, + KEY_CTRL = VK_CONTROL, + KEY_ALT = VK_MENU, + KEY_UP = VK_UP, + KEY_DOWN = VK_DOWN, + KEY_LEFT = VK_LEFT, + KEY_RIGHT = VK_RIGHT, + KEY_BACKSPACE = VK_BACK, + KEY_ENTER = VK_RETURN, + KEY_PAUSE = VK_PAUSE, + KEY_CAPSLOCK = VK_CAPITAL, + KEY_PAGEUP = VK_PRIOR, + KEY_PAGEDOWN = VK_NEXT, + KEY_END = VK_END, + KEY_HOME = VK_HOME, + KEY_PRINTSCREEN = VK_SNAPSHOT, + KEY_INSERT = VK_INSERT, + KEY_DELETE = VK_DELETE, + KEY_NUMPAD0 = VK_NUMPAD0, + KEY_NUMPAD1 = VK_NUMPAD1, + KEY_NUMPAD2 = VK_NUMPAD2, + KEY_NUMPAD3 = VK_NUMPAD3, + KEY_NUMPAD4 = VK_NUMPAD4, + KEY_NUMPAD5 = VK_NUMPAD5, + KEY_NUMPAD6 = VK_NUMPAD6, + KEY_NUMPAD7 = VK_NUMPAD7, + KEY_NUMPAD8 = VK_NUMPAD8, + KEY_NUMPAD9 = VK_NUMPAD9, + KEY_NUMPAD_MULTIPLY = VK_MULTIPLY, + KEY_NUMPAD_ADD = VK_ADD, + KEY_NUMPAD_SUBTRACT = VK_SUBTRACT, + KEY_NUMPAD_PERIOD = VK_DECIMAL, + KEY_NUMPAD_DIVIDE = VK_DIVIDE, + KEY_SPACE = VK_SPACE, + KEY_COMMA = VK_OEM_COMMA, + KEY_PERIOD = VK_OEM_PERIOD, + KEY_DASH = VK_OEM_MINUS, + KEY_EQUAL = VK_OEM_PLUS, + + // these are only good in the US of A... + KEY_SEMICOLON = VK_OEM_1, + KEY_SLASH = VK_OEM_2, + KEY_TILDE = VK_OEM_3, + KEY_LBRACKET = VK_OEM_4, + KEY_BACKSLASH = VK_OEM_5, + KEY_RBRACKET = VK_OEM_6, + KEY_QUOTE = VK_OEM_7, + KEY_UNMAPPED = 0xffffffff, +}; + + +#elif HS_BUILD_FOR_UNIX + +enum plKeyDef +{ + KEY_UNMAPPED = 0xffffffff, +}; +#endif + +#endif // plKeyDef_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plKeyMap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plKeyMap.h new file mode 100644 index 00000000..abe3659c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plKeyMap.h @@ -0,0 +1,223 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plKeyMap - Generic class that defines a mapping of 1-or-2 key codes to +// ControlEventCodes +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plKeyMap_h +#define _plKeyMap_h + + +#include "hsTypes.h" +#include "plInputMap.h" +#include "plControlEventCodes.h" + +#include "hsTemplates.h" + + +//// plKeyCombo ////////////////////////////////////////////////////////////// +// Tiny class/data type representing a single key combo. Ex. shift+C + +class plKeyCombo +{ + public: + plKeyDef fKey; + UInt8 fFlags; + + // The ordering of this lets us treat the flags as a priority number. + // kCtrl + kShift > kCtrl > kShift > no flags + enum Flags + { + kShift = 0x01, + kCtrl = 0x02 + }; + + static plKeyCombo kUnmapped; + + plKeyCombo(); + plKeyCombo( plKeyDef k, UInt8 flags = 0 ) : fKey( k ), fFlags( flags ) { } + + hsBool IsSatisfiedBy(const plKeyCombo &combo) const; + + hsBool operator==( const plKeyCombo &rhs ) const { return ( fKey == rhs.fKey ) && ( fFlags == rhs.fFlags ); } + hsBool operator!=( const plKeyCombo &rhs ) const { return ( fKey != rhs.fKey ) || ( fFlags != rhs.fFlags ); } +}; + +//// For the Particularly Lazy... //////////////////////////////////////////// + +class plShiftKeyCombo : public plKeyCombo +{ + public: + plShiftKeyCombo( plKeyDef k ) : plKeyCombo( k, kShift ) {} +}; + +class plCtrlKeyCombo : public plKeyCombo +{ + public: + plCtrlKeyCombo( plKeyDef k ) : plKeyCombo( k, kCtrl ) {} +}; + +class plCtrlShiftKeyCombo : public plKeyCombo +{ + public: + plCtrlShiftKeyCombo( plKeyDef k ) : plKeyCombo( k, kCtrl | kShift ) {} +}; + +//// plKeyBinding //////////////////////////////////////////////////////////// +// Record for a single binding of 1-or-2 keys to a ControlEventCode, with +// optional string if necessary (for, say console command bindings) + +class plKeyBinding +{ + protected: + + ControlEventCode fCode; + UInt32 fCodeFlags; // Needed? + plKeyCombo fKey1; // KEY_UNMAPPED for not-used + plKeyCombo fKey2; + char *fString; + + public: + + plKeyBinding(); + plKeyBinding( ControlEventCode code, UInt32 codeFlags, const plKeyCombo &key1, const plKeyCombo &key2, const char *string = nil ); + virtual ~plKeyBinding(); + + ControlEventCode GetCode( void ) const { return fCode; } + UInt32 GetCodeFlags( void ) const { return fCodeFlags; } + const plKeyCombo &GetKey1( void ) const { return fKey1; } + const plKeyCombo &GetKey2( void ) const { return fKey2; } + const char *GetExtendedString( void ) const { return fString; } + const plKeyCombo &GetMatchingKey( plKeyDef keyDef ) const; + + void SetKey1( const plKeyCombo &newCombo ); + void SetKey2( const plKeyCombo &newCombo ); + void ClearKeys( void ); + hsBool HasUnmappedKey() const; +}; + +//// plKeyMap //////////////////////////////////////////////////////////////// +// Basically an array of plKeyBindings with some extra helpers + +class plKeyMap : public plInputMap +{ + public: + + // Konstants for the bind preference + enum BindPref + { + kNoPreference = 0, // Just bind to any free one, else first + kNoPreference2nd, // Bind to a free one, or second key if no free one + kFirstAlways, // Bind to first key no matter what + kSecondAlways // Ditto but for 2nd key + }; + + protected: + + hsTArray fBindings; + + plKeyBinding *IFindBindingByKey( const plKeyCombo &combo ) const; + void IFindAllBindingsByKey( const plKeyCombo &combo, hsTArray &result ) const; + plKeyBinding *IFindBinding( ControlEventCode code ) const; + plKeyBinding *IFindConsoleBinding( const char *command ) const; + + void IActuallyBind( plKeyBinding *binding, const plKeyCombo &combo, BindPref pref ); + void ICheckAndBindDupe( plKeyDef origKey, plKeyDef dupeKey ); + + public: + + plKeyMap(); + virtual ~plKeyMap(); + + // Adds a given control code to the map. Once you add it, you can't change its flags. Returns false if the code is already present + hsBool AddCode( ControlEventCode code, UInt32 codeFlags ); + + // Same but for console commands. No flags b/c console commands always use the same flags + hsBool AddConsoleCommand( const char *command ); + + + // Adds a key binding to a given code. Returns false if the code isn't in this map or if key is already mapped. + hsBool BindKey( const plKeyCombo &combo, ControlEventCode code, BindPref pref = kNoPreference ); + + // Console command version + hsBool BindKeyToConsoleCmd( const plKeyCombo &combo, const char *command, BindPref pref = kNoPreference ); + + + // Searches for the binding for a given code. Returns nil if not found + const plKeyBinding *FindBinding( ControlEventCode code ) const; + + // Searches for the binding by key. Returns nil if not found + const plKeyBinding *FindBindingByKey( const plKeyCombo &combo ) const; + + // Finds multiple bindings (when there are unneeded ctrl/shift flags) + void FindAllBindingsByKey( const plKeyCombo &combo, hsTArray &result ) const; + + // Searches for the binding by console command. Returns nil if not found + const plKeyBinding* plKeyMap::FindConsoleBinding( const char *command ) const; + + // Make sure the given keys are clear of bindings, i.e. not used + void EnsureKeysClear( const plKeyCombo &key1, const plKeyCombo &key2 ); + + // Unmaps the given key, no matter what binding it is in + void UnmapKey( const plKeyCombo &combo ); + + // Unmaps the keys for a single binding + void UnmapBinding( ControlEventCode code ); + + // Unmaps all the bindings, but leaves the code records themselves + void UnmapAllBindings( void ); + + // Erases the given code binding. Note: should never really be used, but provided here for completeness + void EraseBinding( ControlEventCode code ); + + // Clears ALL bindings + void ClearAll( void ); + + static const char* GetStringCtrl(); + static const char* GetStringShift(); + static const char* GetStringUnmapped(); + + + UInt32 GetNumBindings( void ) const { return fBindings.GetCount(); } + const plKeyBinding &GetBinding( UInt32 i ) const { return *fBindings[ i ]; } + void HandleAutoDualBinding( plKeyDef key1, plKeyDef key2 ); + + static char *ConvertVKeyToChar( UInt32 vk ); + static plKeyDef ConvertCharToVKey( const char *c ); + + static Win32keyConvert fKeyConversionEnglish[]; + static Win32keyConvert fKeyConversionFrench[]; + static Win32keyConvert fKeyConversionGerman[]; + //static Win32keyConvert fKeyConversionSpanish[]; + //static Win32keyConvert fKeyConversionItalian[]; + +}; + + +#endif // _plKeyMap_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plOSMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plOSMsg.h new file mode 100644 index 00000000..c53e2fb0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnInputCore/plOSMsg.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plOSMsg_inc +#define plOSMsg_inc + +#include + +// +// This enum wraps all of the OS messages +// that we care about for this particular +// platform - add as necessary... +// + +// for Win32: + +enum plOSMsg +{ + KEYDOWN = WM_KEYDOWN, + KEYUP = WM_KEYUP, + MOUSEMOVE = WM_MOUSEMOVE, + L_BUTTONDN = WM_LBUTTONDOWN, + L_BUTTONUP = WM_LBUTTONUP, + R_BUTTONDN = WM_RBUTTONDOWN, + R_BUTTONUP = WM_RBUTTONUP, + MOUSEWHEEL = 0x020A, + L_BUTTONDBLCLK = WM_LBUTTONDBLCLK, + R_BUTTONDBLCLK = WM_RBUTTONDBLCLK, + SYSKEYDOWN = WM_SYSKEYDOWN, + SYSKEYUP = WM_SYSKEYUP, + M_BUTTONDN = WM_MBUTTONDOWN, + M_BUTTONUP = WM_MBUTTONUP, +}; + + +// +// generic structure that we can use to describe +// the state of the mouse on any platform. +// +// + +struct plMouseState +{ + enum + { + kLeftButton = 0x0001, + kRightButton = 0x0002, + kMiddleButton = 0x0004, + }; + float fX; + float fY; + UInt32 fButtonState; +}; + + +#endif // plOSMsg diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/hsKeyedObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/hsKeyedObject.cpp new file mode 100644 index 00000000..84465715 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/hsKeyedObject.cpp @@ -0,0 +1,175 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsKeyedObject.h" +#include "plKeyImp.h" +#include "hsResMgr.h" +#include "../pnDispatch/plDispatch.h" +#include "../pnMessage/plSelfDestructMsg.h" + +void hsKeyedObject::SetKey(plKey k) +{ + if (fpKey != nil) + { + hsAssert(k == nil || k == fpKey, "Changing an object's key is not allowed"); + ((plKeyImp*)fpKey)->SetObjectPtr(nil); + } + + fpKey = k; + + if (fpKey != nil) + ((plKeyImp*)fpKey)->SetObjectPtr(this); +} + +hsBool hsKeyedObject::SendRef(plRefMsg* refMsg, plRefFlags::Type flags) +{ + plKey key = GetKey(); // for linux build + return hsgResMgr::SendRef(key, refMsg, flags); +} + +const char* hsKeyedObject::GetKeyName() const +{ + if (fpKey) + return fpKey->GetName(); + else + return "(unknown)"; +} + +hsKeyedObject::~hsKeyedObject() +{ + if( fpKey && fpKey->ObjectIsLoaded() ) + { + // If our key is pointing to an object (presumably back to us), + // then UnRegister will call SetObjectPtr(nil) will unregister the key (and us), which will + // decrement our RefCnt. Unfortunately, we are here because of a call + // to our destructor, in which case we don't want to go back into our + // destructor again. So we'll just up the RefCnt, plKey::UnRegister will dec it back to 1. + hsRefCnt_SafeRef(fpKey->ObjectIsLoaded()); + } + UnRegister(); +} + +void hsKeyedObject::UnRegister() +{ + if (fpKey) + { + if (plgDispatch::Dispatch()) + plgDispatch::Dispatch()->UnRegisterAll(fpKey); + + ((plKeyImp *)fpKey)->SetObjectPtr(nil); + } +} + +plKey hsKeyedObject::RegisterAs(plFixedKeyId fixedKey) +{ + plUoid meUoid(fixedKey); + + hsAssert(meUoid.GetClassType() == ClassIndex(), "Registering as wrong type!"); + plKey key = hsgResMgr::ResMgr()->FindKey(meUoid); + if (key == nil) + { + key = hsgResMgr::ResMgr()->NewKey(meUoid, this); + } + else + { + SetKey(key); + } + + return key; +} + + +void hsKeyedObject::UnRegisterAs(plFixedKeyId fixedKey) +{ + plUoid uoid(fixedKey); + UnRegisterAsManual(uoid); +} + +plKey hsKeyedObject::RegisterAsManual(plUoid& meUoid, const char* p) +{ + hsAssert(meUoid.GetClassType() == ClassIndex(),"Registering as wrong type!"); + // Really should be a NewKey() call just for fixed keys, so change this once player rooms behave + plKey pkey = hsgResMgr::ResMgr()->ReRegister(p,meUoid); + + if (pkey) + SetKey(pkey); + return pkey; +} + + +void hsKeyedObject::UnRegisterAsManual(plUoid& inUoid) +{ + if (fpKey) + { + plUoid myUoid = fpKey->GetUoid(); + if (!(inUoid == myUoid)) + { +#if !HS_BUILD_FOR_UNIX // disable for unix servers + char inStr[255], myStr[255]; + inUoid.StringIze(inStr); + myUoid.StringIze(myStr); + hsAssert(false, + xtl::format("Request to Unregister wrong FixedKey, keyName=%s, inUoid=%s, myUoid=%s", + fpKey->GetName() ? fpKey->GetName() : "?", inStr, myStr).c_str()); +#endif + } + ((plKeyImp*)fpKey)->UnRegister(); + } +} + +void hsKeyedObject::Validate() +{ + const char* msg = "KeyedObject invalid!"; + + if (fpKey) + { + hsAssert(fpKey->GetObjectPtr() == this, msg); + } +} + +void hsKeyedObject::Read(hsStream* s, hsResMgr* mgr) +{ + plKey pK = mgr->ReadKey(s); + SetKey(pK); +} + +void hsKeyedObject::Write(hsStream* s, hsResMgr* mgr) +{ + hsAssert(GetKey(),"hsKeyedObject:Must have a key!"); + mgr->WriteKey(s, fpKey); +} + +hsBool hsKeyedObject::MsgReceive(plMessage* msg) +{ + plSelfDestructMsg* nuke = plSelfDestructMsg::ConvertNoRef(msg); + if (nuke) + { + hsAssert(RefCnt() == 1, "Trying to selfdestruct with bogus refcnt"); + hsRefCnt_SafeUnRef(this); + + return true; + } + return plReceiver::MsgReceive(msg); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/hsKeyedObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/hsKeyedObject.h new file mode 100644 index 00000000..6219199a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/hsKeyedObject.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsKeyedObject_h_inc +#define hsKeyedObject_h_inc + +#include "plReceiver.h" +#include "plRefFlags.h" + +#include "plKey.h" +#include "plFixedKey.h" + +class hsStream; +class plRefMsg; +class plUoid; + +class hsKeyedObject : public plReceiver +{ +public: + hsKeyedObject() : fpKey(nil) {} + virtual ~hsKeyedObject(); + + CLASSNAME_REGISTER(hsKeyedObject); + GETINTERFACE_ANY(hsKeyedObject, plReceiver); + + const plKey& GetKey() const { return fpKey; } + const char* GetKeyName() const; + + virtual void Validate(); + virtual hsBool IsFinal() { return true; }; // experimental; currently "is ready to process Loads" + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + //---------------------- + // Send a reference to GetKey() via enclosed message. See plKey::SendRef() + //---------------------- + hsBool SendRef(plRefMsg* refMsg, plRefFlags::Type flags); + + //---------------------------------------- + // Fixed key functions + // The following are to be matched pairs! + //---------------------------------------- + plKey RegisterAs(plFixedKeyId fixedKey); // a fixed Key value from plFixedKey.h + void UnRegisterAs(plFixedKeyId fixedKey); + + // used when manually loading the player room + plKey RegisterAsManual(plUoid& uoid, const char* p); + void UnRegisterAsManual(plUoid& uoid); + + // If you want clone keys to share a type of object, override this function for it. + // (You can also return a new object that shares only some of the original's data) + virtual hsKeyedObject* GetSharedObject() { return nil; } + +protected: + friend class plResManager; + + virtual void SetKey(plKey k); + void UnRegister(); + +private: + plKey fpKey; +}; + +#endif // hsKeyedObject_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp new file mode 100644 index 00000000..af19b116 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp @@ -0,0 +1,165 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plFixedKey +// +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsUtils.h" +#include "plUoid.h" +#include + +//// plKeySeed /////////////////////////////////////////////////////////////// +// Our seed struct. Defined here so it doesn't have to be in the .h file + +struct plKeySeed +{ + plFixedKeyId feFixedKey; + // NOTE: The following fields are broken out to make adding to the fixed key list easier. + // However, what they really are, are just the fields of plUoid (including plLocation) + UInt16 fType; + const char *fObj; + + hsBool Match( plKeySeed *p ) + { + if( ( fType == p->fType ) && stricmp( p->fObj, fObj ) == 0 ) + { + return true; + } + return false; + } +}; + +#include "plFixedKey.h" +#include "plCreatableIndex.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnFactory/plCreator.h" + +// Rules for SeedList: + // 1) Must be in the Same order as enum fixedKey + // 2) For now at least, all your fixed keys get put into the kGlobalFixedLoc room, reserved just for fixed keys + // 2) Be sure your ClassIndex CLASS_INDEX(plSceneObject) matches the type of object you want to have the fixedKey + // 3) Make sure the Obj is unique for this location/Type Combo... (validated at runtime) + +plKeySeed SeedList[] = { + // Key Enum Type Obj + + { kFirst_Fixed_KEY, CLASS_INDEX_SCOPED( plSceneObject ), "kFirst_Fixed_KEY" }, + + { kLOSObject_KEY, CLASS_INDEX_SCOPED( plLOSDispatch ), "kLOSObject_KEY", }, + { kTimerCallbackManager_KEY, CLASS_INDEX_SCOPED( plTimerCallbackManager ), "kTimerCallbackManager_KEY", }, + { kConsoleObject_KEY, CLASS_INDEX_SCOPED( pfConsole ), "kConsoleObject_KEY", }, + { kAudioSystem_KEY, CLASS_INDEX_SCOPED( plAudioSystem ), "kAudioSystem_KEY", }, + { kInput_KEY, CLASS_INDEX_SCOPED( plInputManager ), "kInput_KEY", }, + { kClient_KEY, CLASS_INDEX_SCOPED( plClient ), "kClient_KEY", }, + { kNetClientMgr_KEY, CLASS_INDEX_SCOPED( plNetClientMgr ), "kNetClientMgr_KEY", }, + { kListenerMod_KEY, CLASS_INDEX_SCOPED( plListener ), "kListenerMod_KEY", }, + { kTransitionMgr_KEY, CLASS_INDEX_SCOPED( plTransitionMgr ), "kTransitionMgr_KEY", }, + { kLinkEffectsMgr_KEY, CLASS_INDEX_SCOPED( plLinkEffectsMgr ), "kLinkEffectsMgr_KEY", }, + { kGameGUIMgr_KEY, CLASS_INDEX_SCOPED( pfGameGUIMgr ), "kGameGUIMgr_KEY", }, + { kGameGUIDynamicDlg_KEY, CLASS_INDEX_SCOPED( plSceneNode ), "kGameGUIDynamicDlg_KEY", }, + { kVirtualCamera1_KEY, CLASS_INDEX_SCOPED( plVirtualCam1 ), "kVirtualCamera_KEY", }, + { kDefaultCameraMod1_KEY, CLASS_INDEX_SCOPED( plCameraModifier1 ), "kDefaultCameraMod1_KEY", }, + { kKIGUIGlue_KEY, CLASS_INDEX_SCOPED( pfKI ), "kKIGUIGlue_KEY", }, + { kClothingMgr_KEY, CLASS_INDEX_SCOPED( plClothingMgr ), "kClothingMgr_KEY", }, + { kInputInterfaceMgr_KEY, CLASS_INDEX_SCOPED( plInputInterfaceMgr ), "kInputInterfaceMgr_KEY", }, + { kAVIWriter_KEY, CLASS_INDEX_SCOPED( plAVIWriter ), "kAVIWriter_KEY", }, + { kResManagerHelper_KEY, CLASS_INDEX_SCOPED( plResManagerHelper ), "kResManagerHelper_KEY", }, + { kAvatarMgr_KEY, CLASS_INDEX_SCOPED( plAvatarMgr ), "kAvatarMgr_KEY", }, + { kSimulationMgr_KEY, CLASS_INDEX_SCOPED( plSimulationMgr ), "kSimulationMgr_KEY", }, + { kTransitionCamera_KEY, CLASS_INDEX_SCOPED( plCameraModifier1 ), "kTransitionCamera_KEY", }, + { kCCRMgr_KEY, CLASS_INDEX_SCOPED( plCCRMgr ), "kCCRMgr_KEY", }, + { kNetClientCloneRoom_KEY, CLASS_INDEX_SCOPED( plSceneNode ), "kNetClientCloneRoom_KEY", }, + { kMarkerMgr_KEY, CLASS_INDEX_SCOPED( pfMarkerMgr ), "kMarkerMgr_KEY", }, + { kAutoProfile_KEY, CLASS_INDEX_SCOPED( plAutoProfile ), "kAutoProfile_KEY", }, + { kGlobalVisMgr_KEY, CLASS_INDEX_SCOPED( plVisMgr ), "kGlobalVisMgr_KEY", }, + { kFontCache_KEY, CLASS_INDEX_SCOPED( plFontCache ), "kFontCache_KEY", }, + { kRelevanceMgr_KEY, CLASS_INDEX_SCOPED( plRelevanceMgr ), "kRelevanceMgr_KEY", }, + { kJournalBookMgr_KEY, CLASS_INDEX_SCOPED( pfJournalBook ), "kJournalBookMgr_KEY" }, + { kAgeLoader_KEY, CLASS_INDEX_SCOPED( plAgeLoader), "kAgeLoader_KEY" }, + { kBuiltIn3rdPersonCamera_KEY, CLASS_INDEX_SCOPED( plCameraModifier1 ), "kBuiltIn3rdPersonCamera_KEY", }, + { kSecurePreloader_KEY, CLASS_INDEX_SCOPED( pfSecurePreloader ), "kSecurePreloader_KEY", }, + + + { kLast_Fixed_KEY, CLASS_INDEX_SCOPED( plSceneObject ), "kLast_Fixed_KEY", } +}; + + +//// plFixedKeyValidator ///////////////////////////////////////////////////// +// Static class that validates the fixed key list on startup, to make sure +// you didn't mess up the array. + +class plFixedKeyValidator +{ + private: + static plFixedKeyValidator fValidator; + + plFixedKeyValidator::plFixedKeyValidator() + { + // verify that each Seed is in the correct spot...via the enum... + int i; + for (i= kFirst_Fixed_KEY; i < kLast_Fixed_KEY; i++) + { + plKeySeed *p= &SeedList[i]; + hsAssert(i == p->feFixedKey, "The fixed key table in plFixedKey.h is invalid (a fixed key is misplaced). Please ensure the list follows the given restrictions."); + } + // check for duplicates + for (i= kFirst_Fixed_KEY; i < kLast_Fixed_KEY; i++) + { + for (int c = i+1; c < kLast_Fixed_KEY; c++) + { + hsAssert(!SeedList[i].Match(&SeedList[c]), + "The fixed key table in plFixedKey.h is invalid (there are duplicate fixed keys). Please ensure the list follows the given restrictions."); + } + } + } +}; + +plFixedKeyValidator plFixedKeyValidator::fValidator; + +//// The plUoid Fixed-Key Constructor //////////////////////////////////////// +// Put here because a) it's fixedKey dependant and b) it ensures that this +// file gets compiled. + +plUoid::plUoid(plFixedKeyId fixedkey) +{ + hsAssert(fixedkey < kLast_Fixed_KEY, "Request for Fixed key is out of Range"); + + fObjectName = nil; + Invalidate(); + + plKeySeed* p= &SeedList[fixedkey]; + + fLocation = plLocation::kGlobalFixedLoc; + fClassType = p->fType; + fObjectName = hsStrcpy(p->fObj); + fObjectID = 0; + fCloneID = 0; + fClonePlayerID = 0; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h new file mode 100644 index 00000000..10ad5cb6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLFIXEDKEY_H +#define PLFIXEDKEY_H + + + // Using Fixed Key feature: + // Add a new fixedkey to the enum list below. + // Then add, to the Seed list (in plFixedKey.cpp) in the Corresponding + // position, (don't screw up, it will be validated...) + // The "Full Address" data for you Fixed key (see rules in plFixedKey.cpp) + +enum plFixedKeyId +{ + kFirst_Fixed_KEY, + + kLOSObject_KEY, + kTimerCallbackManager_KEY, + kConsoleObject_KEY, + kAudioSystem_KEY, + kInput_KEY, + kClient_KEY, + kNetClientMgr_KEY, + kListenerMod_KEY, + kTransitionMgr_KEY, + kLinkEffectsMgr_KEY, + kGameGUIMgr_KEY, + kGameGUIDynamicDlg_KEY, + kVirtualCamera1_KEY, + kDefaultCameraMod1_KEY, + kKIGUIGlue_KEY, + kClothingMgr_KEY, + kInputInterfaceMgr_KEY, + kAVIWriter_KEY, + kResManagerHelper_KEY, + kAvatarMgr_KEY, + kSimulationMgr_KEY, + kTransitionCamera_KEY, + kCCRMgr_KEY, + kNetClientCloneRoom_KEY, + kMarkerMgr_KEY, + kAutoProfile_KEY, + kGlobalVisMgr_KEY, + kFontCache_KEY, + kRelevanceMgr_KEY, + kJournalBookMgr_KEY, + kAgeLoader_KEY, + kBuiltIn3rdPersonCamera_KEY, + kSecurePreloader_KEY, + + kLast_Fixed_KEY +}; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKey.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKey.cpp new file mode 100644 index 00000000..08e9c2f2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKey.cpp @@ -0,0 +1,286 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plKey - An opaque pointer to actual key data, so that we can keep track +// of how many people have pointers (plKeys) to key data (plKeyImp) +// and destroy the key data when it's been fully unreffed. +// +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plKey.h" +#include "plUoid.h" +#include +#include "hsResMgr.h" + +#define TRACK_REFS 0 // MEMLEAKFISH + +#if TRACK_REFS +#include "plCreatableIndex.h" +#include "plClassIndexMacros.h" +#include "plTweak.h" + +int mlfTrack = 1; + +static const char* keyNameToLookFor = "AgeSDLHook"; +static const UInt16 CLASS_TO_TRACK = CLASS_INDEX_SCOPED(plSceneObject); +static const int kCloneID = 0; +static const int kClonePlayerID = 0; +static plKeyData* lastData = nil; +static const int kLocSeq = -1; + +class keyDataFriend : public plKeyData +{ +public: + UInt16 RefCount() const { return fRefCount; } +}; + +static int IsTracked(const plKeyData* keyData) +{ + if( mlfTrack && keyData ) + { + if( keyData->GetUoid().GetObjectName() && !stricmp(keyData->GetUoid().GetObjectName(), keyNameToLookFor) + && (keyData->GetUoid().GetClassType() == CLASS_TO_TRACK) ) + { + if( (kCloneID < 0) + ||(kCloneID == keyData->GetUoid().GetCloneID()) ) + { + if( (kLocSeq < 0) + ||(kLocSeq == keyData->GetUoid().GetLocation().GetSequenceNumber()) ) + { + plConst(UInt16) kMinRefCount(0); + const keyDataFriend* kdf = (keyDataFriend*)keyData; + if( kdf->RefCount() > kMinRefCount ) + { + return true; + } + } + } + } + } + return false; +} + +static const char* CloneString(const plKeyData* keyData) +{ + static char buff[256]; + if( keyData ) + { + sprintf(buff, "CID:%d, CPID:%d LOC:%d", + keyData->GetUoid().GetCloneID(), + keyData->GetUoid().GetClonePlayerID(), + keyData->GetUoid().GetLocation().GetSequenceNumber()); + } + else + { + sprintf(buff, "nil"); + } + return buff; +} +#endif + + +plKey::plKey() : fKeyData(nil) +{ +} + +plKey::plKey(void* ptr) : fKeyData(nil) +{ + hsAssert(!ptr, "Attempting to publically construct a non-nil key"); +} + +plKey::plKey(const plKey& rhs) : fKeyData(rhs.fKeyData) +{ +#if TRACK_REFS // FOR DEBUGGING ONLY + if( IsTracked(fKeyData) ) + { + char msg[ 512 ]; + sprintf( msg, "C: Key %s %s is being constructed using the plKey(plKey&) constructor", keyNameToLookFor, CloneString(fKeyData) ); + //hsAssert( false, msg ); + hsStatusMessageF(msg); + } +#endif + IIncRef(); +} + +plKey::plKey(plKeyData* data, hsBool ignore) : fKeyData(data) +{ +#if TRACK_REFS // FOR DEBUGGING ONLY + if( IsTracked(fKeyData) ) + { + char msg[ 512 ]; + sprintf( msg, "C: Key %s %s is being constructed using the plKey(plKeyData*, hsBool) constructor", keyNameToLookFor, CloneString(fKeyData) ); + //hsAssert( false, msg ); + hsStatusMessageF(msg); + } +#endif + IIncRef(); +} + +plKey::~plKey() +{ +#if TRACK_REFS // FOR DEBUGGING ONLY + if( IsTracked(fKeyData) ) + { + char msg[ 512 ]; + sprintf( msg, "D: Key %s %s is being destructed", keyNameToLookFor, CloneString(fKeyData) ); + //hsAssert( false, msg ); + hsStatusMessageF(msg); + } +#endif + IDecRef(); +} + +plKey &plKey::operator=( const plKey &rhs ) +{ +#if TRACK_REFS // FOR DEBUGGING ONLY + if( fKeyData != rhs.fKeyData ) + { + if( IsTracked(rhs.fKeyData) ) + { + char msg[ 512 ]; + if (fKeyData == nil) + sprintf( msg, "=: Key %s %s is being assigned to a nil key", keyNameToLookFor, CloneString(rhs.fKeyData) ); + else + sprintf( msg, "=: Key %s %s is being assigned to %s", keyNameToLookFor, CloneString(rhs.fKeyData), fKeyData->GetUoid().GetObjectName() ); + //hsAssert( false, msg ); + hsStatusMessageF(msg); + } + if( IsTracked(fKeyData) ) + { + char msg[ 512 ]; + if (fKeyData == nil) + sprintf( msg, "=: Nil key is being assigned to %s %s", keyNameToLookFor, CloneString(fKeyData) ); + else + sprintf( msg, "=: Key %s %s is being assigned to %s", fKeyData->GetUoid().GetObjectName(), CloneString(fKeyData), keyNameToLookFor ); + //hsAssert( false, msg ); + hsStatusMessageF(msg); + } + } +#endif + if( fKeyData != rhs.fKeyData ) + { + IDecRef(); + fKeyData = rhs.fKeyData; + IIncRef(); + } + + return *this; +} + +hsBool plKey::operator==( const plKey &rhs ) const +{ + return fKeyData == rhs.fKeyData; +} + +hsBool plKey::operator==( const plKeyData *rhs ) const +{ + return fKeyData == rhs; +} + +plKeyData *plKey::operator->() const +{ + return fKeyData; +} + +plKeyData &plKey::operator*() const +{ + return *fKeyData; +} + +void plKey::IIncRef() +{ + if (!fKeyData) + return; + + hsAssert(fKeyData->fRefCount < 0xffff, "Too many refs to plKeyImp"); + fKeyData->fRefCount++; + +#if TRACK_REFS // FOR DEBUGGING ONLY + if( IsTracked(fKeyData) ) + { + char msg[ 512 ]; + plConst(int) kMaxCnt(30); + if( fKeyData->fRefCount > kMaxCnt ) + *msg = 0; + if( lastData && (fKeyData != lastData) ) + *msg = 0; + lastData = fKeyData; + sprintf( msg, "+: Key %s %s is being reffed! Refcount: %d", keyNameToLookFor, CloneString(fKeyData), fKeyData->fRefCount ); + //hsAssert( false, msg ); + hsStatusMessageF(msg); + } +#endif + + if (fKeyData->fRefCount == 1) + hsgResMgr::ResMgr()->IKeyReffed((plKeyImp*)fKeyData); +} + +void plKey::IDecRef() +{ + if (!fKeyData) + return; + + hsAssert(fKeyData->fRefCount, "Dec'ing ref on unreffed key"); + fKeyData->fRefCount--; + +#if TRACK_REFS // FOR DEBUGGING ONLY + if( IsTracked(fKeyData) ) + { + char msg[ 512 ]; + plConst(int) kMinCnt(2); + if( fKeyData->fRefCount < kMinCnt ) + *msg = 0; + if( lastData && (fKeyData != lastData) ) + *msg = 0; + lastData = fKeyData; + sprintf( msg, "-: Key %s %s is being de-reffed! Refcount: %d", keyNameToLookFor, CloneString(fKeyData), fKeyData->fRefCount ); + //hsAssert( false, msg ); + hsStatusMessageF(msg); + if( fKeyData->fRefCount == 0 ) + *msg = 0; + } +#endif + + if (fKeyData->fRefCount == 0) + hsgResMgr::ResMgr()->IKeyUnreffed((plKeyImp*)fKeyData); +} + +//// plKeyData /////////////////////////////////////////////////////////////// +// Our base class of key data + +plKeyData::plKeyData() +{ + fRefCount = 0; +} + +plKeyData::~plKeyData() +{ + // hsAssert(fRefCount == 0, "Deleting key data when someone still has a ref to it"); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKey.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKey.h new file mode 100644 index 00000000..1db9ba8e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKey.h @@ -0,0 +1,141 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plKey_h_inc +#define plKey_h_inc + +#include "hsTypes.h" +#include "plRefFlags.h" + +class hsKeyedObject; +class plRefMsg; +class plUoid; +class hsBitVector; + +//// plKey /////////////////////////////////////////////////////////////////// +// Pointer to a plKeyData struct, which is a handle to a keyedObject + +class plKeyData; +class plKeyImp; + +class plKey +{ +public: + // Constructors and destructors and copy stuff + plKey(); + plKey(const plKey& rhs); + plKey(void* ptr); // For constructing a nil key + ~plKey(); + plKey& operator=(const plKey& rhs); + + hsBool operator==(const plKey& rhs) const; + hsBool operator==(const plKeyData* rhs) const; + hsBool operator!=(const plKey& rhs) const { return !(*this == rhs); } + hsBool operator!=(const plKeyData* rhs) const { return !(*this == rhs); } + + plKeyData* operator->() const; + plKeyData& operator*() const; + + operator plKeyImp*() const { return (plKeyImp*)fKeyData; } + + static plKey Make(plKeyData* data) { return plKey(data, false); } + +protected: + // Pointer to our real key + plKeyData* fKeyData; + void IIncRef(); + void IDecRef(); + + // Internal constructor, extra param is to distinguish it from the void* constructor + plKey(plKeyData* data, hsBool ignore); +}; + +//// plKeyData /////////////////////////////////////////////////////////////// +// Base virtual class that provides the essential functionality you would +// want from a plKey-ish thing. + +class plKeyData +{ +public: + virtual const plUoid& GetUoid() const=0; + virtual const char* GetName() const=0; + + virtual hsKeyedObject* GetObjectPtr()=0; + virtual hsKeyedObject* ObjectIsLoaded() const=0; + virtual hsKeyedObject* VerifyLoaded() = 0; + + //---------------------- + // Allow a keyed object to behave as if it has an active ref when in fact the object + // should only be active ref'ed by a non-keyed parent. Essentially just bumps/decs + // the active ref count to facilitate normal object creation/destruction + //---------------------- + virtual hsKeyedObject* RefObject(plRefFlags::Type flags = plRefFlags::kActiveRef)=0; + virtual void UnRefObject(plRefFlags::Type flags = plRefFlags::kActiveRef)=0; + + //---------------------- + // Release has two behaviors, depending on whether the ref is active or passive: + // Active - Release decs the ActiveRefCnt. When it gets to zero, the object will be deleted. + // Passive - Unregisters my interest in when the object is created or destroyed. + //---------------------- + virtual void Release(plKey targetKey)=0; + + virtual UInt16 GetActiveRefs() const = 0; + + virtual UInt16 GetNumNotifyCreated() const = 0; + virtual plRefMsg* GetNotifyCreated(int i) const = 0; + virtual const hsBitVector& GetActiveBits() const = 0; + +protected: + // Protected so only the registry can create it + plKeyData(); + virtual ~plKeyData(); + +#ifdef HS_DEBUGGING + // Debugging info fields + const char* fIDName; + const char* fClassType; +#endif + + //// RefCount Stuff ////////////////////////////////////////////////////////// + // The refcounts on plKeyData/plKeyImps are zero-based. When you first create + // a new plKeyImp (which should ONLY ever be done inside the resMgr), it gets + // a refcount of zero. Assigning a new plKey to represent it bumps it to 1, + // and when that key goes away, the refcount drops to zero and the ResManager + // is notified of the fact and may delete the keyImp. + // So the only refcounts on keys outside of the resMgr should be one or more; + // only inside the resMgr should there EVER exist keys with a refcount of 0. + // + // Using our own refCount system instead of hsRefCnt allows us to make it all + // protected, so that only plKey can actually ref/unref, which is as it should + // be. + + // Only one class should ever touch this... + friend class plKey; + + // Refcount--the number of plKeys that have pointers to us. + UInt16 fRefCount; +}; + +#endif // plKey_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKeyImp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKeyImp.cpp new file mode 100644 index 00000000..7b998bf7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKeyImp.cpp @@ -0,0 +1,649 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plKeyImp.h" +#include "hsStream.h" +#include "hsKeyedObject.h" +#include "hsResMgr.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plSelfDestructMsg.h" +#include "hsTimer.h" +#include "plProfile.h" +#include "plgDispatch.h" + +plProfile_CreateMemCounter("Keys", "Memory", KeyMem); + +static UInt32 CalcKeySize(plKeyImp* key) +{ + UInt32 nameLen = 0; + if (key->GetUoid().GetObjectName()) + nameLen = strlen(key->GetUoid().GetObjectName()) + 1; + return sizeof(plKeyImp) + nameLen; +} + +//#define LOG_ACTIVE_REFS +#ifdef LOG_ACTIVE_REFS +#include "plCreatableIndex.h" +static const char* kObjName = "GUI_District_OptionsMenuGUI"; +static UInt16 kClassType = CLASS_INDEX_SCOPED(plSceneNode); +static UInt32 kCloneID = 0; +hsBool IsTrackedKey(const plKeyImp* key) +{ + return hsStrEQ(key->GetName(), kObjName) && key->GetUoid().GetClassType() == kClassType && key->GetUoid().GetCloneID() == kCloneID; +} +#endif + +plKeyImp::plKeyImp() : + fObjectPtr(nil), + fStartPos(-1), + fDataLen(-1), + fNumActiveRefs(0), + fPendingRefs(1), + fCloneOwner(nil) +{ +#ifdef HS_DEBUGGING + fIDName = nil; + fClassType = nil; +#endif +} + +plKeyImp::plKeyImp(plUoid u, UInt32 pos,UInt32 len): + fUoid(u), + fObjectPtr(nil), + fStartPos(pos), + fDataLen(len), + fNumActiveRefs(0), + fPendingRefs(1), + fCloneOwner(nil) +{ + plProfile_NewMem(KeyMem, CalcKeySize(this)); + +#ifdef HS_DEBUGGING + fIDName = fUoid.GetObjectName(); + fClassType = plFactory::GetNameOfClass( fUoid.GetClassType() ); +#endif +} + +plKeyImp::~plKeyImp() +{ + plProfile_DelMem(KeyMem, CalcKeySize(this)); + +#if defined(HS_DEBUGGING) && 0 + // Colin debugging + char buf[512]; + sprintf(buf, "0x%x %s %s\n", this, fIDName, fClassType); + hsStatusMessage(buf); +#endif + + hsAssert(fObjectPtr == nil, "Deleting non-nil key! Bad idea!"); + + if (fCloneOwner != nil) + { + // Must be a clone, remove us from our parent list + ((plKeyImp*)fCloneOwner)->RemoveClone(this); + } + + for (int i = 0; i < fClones.GetCount(); i++) + { + if (fClones[i]) + fClones[i]->UnRegister(); + } + fClones.Reset(); + + // This is normally empty by now, but if we never got loaded, + // there will be unsent ref messages in the NotifyCreated list + ClearNotifyCreated(); +} + +void plKeyImp::SetUoid(const plUoid& uoid) +{ + fUoid = uoid; +#ifdef HS_DEBUGGING + fIDName = fUoid.GetObjectName(); + fClassType = plFactory::GetNameOfClass(fUoid.GetClassType()); +#endif +} + +const char* plKeyImp::GetName() const +{ + return fUoid.GetObjectName(); +} + +hsKeyedObject* plKeyImp::GetObjectPtr() +{ + return ObjectIsLoaded(); +} + +hsKeyedObject* plKeyImp::ObjectIsLoaded() const +{ + return this ? fObjectPtr : nil; +} + +// Copy the contents of p for cloning process +void plKeyImp::CopyForClone(const plKeyImp *p, UInt32 playerID, UInt32 cloneID) +{ + fObjectPtr = nil; // the clone object start as nil + fUoid = p->GetUoid(); // we will set the UOID the same to start + +#ifdef HS_DEBUGGING + fIDName = fUoid.GetObjectName(); + fClassType = plFactory::GetNameOfClass( fUoid.GetClassType() ); +#endif + + fStartPos = p->GetStartPos(); + fDataLen = p->GetDataLen(); + fUoid.SetClone(playerID, cloneID); +} + +hsKeyedObject* plKeyImp::VerifyLoaded() +{ + if (!fObjectPtr) + hsgResMgr::ResMgr()->ReadObject(this); + + return fObjectPtr; +} + +//// Read/Write ////////////////////////////////////////////////////////////// +// The actual key read/writes for the index file, the only time the whole +// key is ever actually stored. + +void plKeyImp::Read(hsStream* s) +{ + fUoid.Read(s); + s->ReadSwap(&fStartPos); + s->ReadSwap(&fDataLen); + + plProfile_NewMem(KeyMem, CalcKeySize(this)); + +#ifdef HS_DEBUGGING + fIDName = fUoid.GetObjectName(); + fClassType = plFactory::GetNameOfClass(fUoid.GetClassType()); +#endif +} + +void plKeyImp::SkipRead(hsStream* s) +{ + plUoid tempUoid; + tempUoid.Read(s); + s->ReadSwap32(); + s->ReadSwap32(); +} + +void plKeyImp::Write(hsStream* s) +{ + fUoid.Write(s); + s->WriteSwap(fStartPos); + s->WriteSwap(fDataLen); + if (fStartPos == (UInt32)-1) + int foo = 0; +} + +//// WriteObject ///////////////////////////////////////////////////////////// +// Writes the key's object to the already opened stream + +void plKeyImp::WriteObject(hsStream* stream) +{ + hsKeyedObject* ko = ObjectIsLoaded(); + if (ko == nil) + { + // Mark the key as not written + fStartPos = (UInt32)-1; + fDataLen = (UInt32)-1; + return; + } + + fStartPos = stream->GetPosition(); + hsgResMgr::ResMgr()->WriteCreatable(stream, ko); + fDataLen = stream->GetPosition() - fStartPos; +} + +void plKeyImp::UnRegister() // called from plRegistry +{ + plKey safeRefUntilWereDone = plKey::Make(this); + + hsKeyedObject* ko = ObjectIsLoaded(); + if (ko) + { + INotifyDestroyed(); + fObjectPtr = nil; + fNumActiveRefs = 0; + + hsRefCnt_SafeUnRef(ko); + } + IClearRefs(); + ClearNotifyCreated(); +}; + +hsKeyedObject* plKeyImp::RefObject(plRefFlags::Type flags) +{ + if ((flags == plRefFlags::kPassiveRef) && !ObjectIsLoaded()) + return nil; + +#ifdef LOG_ACTIVE_REFS + if (IsTrackedKey(this)) + hsStatusMessageF("@@@ RefObject adding active ref to %s (%d total)", kObjName, fNumActiveRefs+1); +#endif // LOG_ACTIVE_REFS + + IncActiveRefs(); + + return VerifyLoaded(); // load object on demand +} + +void plKeyImp::UnRefObject(plRefFlags::Type flags) +{ + // Rather than using hsRefCnt's, make Ref and + // UnRef work with ActiveRef system + if ( (flags == plRefFlags::kPassiveRef) && !ObjectIsLoaded()) + return; + +#ifdef LOG_ACTIVE_REFS + if (IsTrackedKey(this)) + hsStatusMessageF("@@@ UnRefObject releasing active ref to %s (%d total)", kObjName, fNumActiveRefs-1); +#endif // LOG_ACTIVE_REFS + DecActiveRefs(); + + if( !GetActiveRefs() ) + { + INotifyDestroyed(); + + IClearRefs(); + ClearNotifyCreated(); + + plKey key=plKey::Make( this ); // for linux build + plSelfDestructMsg* nuke = TRACKED_NEW plSelfDestructMsg( key ); + plgDispatch::Dispatch()->MsgSend(nuke); + } +} + +hsKeyedObject* plKeyImp::SetObjectPtr(hsKeyedObject* p) +{ + hsKeyedObject* retVal = nil; + + // If our object is the only one with a ref to us, this function will crash, so we + // make sure we have an extra ref, just like in UnRegister(). + plKey safeRefUntilWereDone = plKey::Make(this); + + if (p) + { +#ifdef HS_DEBUGGING + if (fClassType) + { + char str[2048]; + sprintf(str, "Mismatch of class (we are a %s, given a %s)", fClassType, p->ClassName()); + hsAssert(fClassType == p->ClassName() || strcmp(fClassType, p->ClassName()) == 0, str); // points to static + } + else + fClassType = p->ClassName(); +#endif + + hsAssert(!fObjectPtr, "Setting an ObjectPtr thats already Set!"); + + retVal = fObjectPtr = p; + } + else + { + if (fObjectPtr) + UnRegister(); + + fObjectPtr = nil; + retVal = nil; + } + + return retVal; +} + +void plKeyImp::ClearNotifyCreated() +{ + for (int i = 0; i < fNotifyCreated.GetCount(); i++) + hsRefCnt_SafeUnRef(fNotifyCreated[i]); + fNotifyCreated.Reset(); + fNotified.Reset(); + fActiveRefs.Reset(); +} + +void plKeyImp::AddNotifyCreated(plRefMsg* msg, plRefFlags::Type flags) +{ + if (!(flags == plRefFlags::kPassiveRef)) + { +#ifdef LOG_ACTIVE_REFS + if (IsTrackedKey(this)) + { + hsStatusMessageF("@@@ %s(%s) adding active ref to %s (%d total)", msg->GetReceiver(0)->GetName(), + plFactory::GetNameOfClass(msg->GetReceiver(0)->GetUoid().GetClassType()), kObjName, fNumActiveRefs+1); + } +#endif // LOG_ACTIVE_REFS + + IncActiveRefs(); + SetActiveRef(GetNumNotifyCreated()); + } + + hsRefCnt_SafeRef(msg); + fNotifyCreated.Append(msg); +} + +void plKeyImp::RemoveNotifyCreated(int i) +{ + hsRefCnt_SafeUnRef(fNotifyCreated[i]); + fNotifyCreated.Remove(i); + + fNotified.RemoveBit(i); + fActiveRefs.RemoveBit(i); +} + +void plKeyImp::AddRef(plKeyImp* key) const +{ + fPendingRefs++; + fRefs.Append(key); +} + + +void plKeyImp::RemoveRef(plKeyImp* key) const +{ + int idx = fRefs.Find(key); + if (fRefs.kMissingIndex != idx) + fRefs.Remove(idx); +} + +void plKeyImp::AddClone(plKeyImp* key) +{ + hsAssert(!GetClone(key->GetUoid().GetClonePlayerID(), key->GetUoid().GetCloneID()), + "Adding a clone which is already there?"); + + key->fCloneOwner = plKey::Make(this); + fClones.Append(key); +} + +void plKeyImp::RemoveClone(plKeyImp* key) const +{ + if (key->GetUoid().IsClone()) + { + int idx = fClones.Find(key); + if (idx != -1) + { + fClones.Remove(idx); + key->fCloneOwner = nil; + } + } +} + +plKey plKeyImp::GetClone(UInt32 playerID, UInt32 cloneID) const +{ + for (int i = 0; i < fClones.GetCount(); i++) + { + plKeyImp* cloneKey = fClones[i]; + if (cloneKey + && cloneKey->GetUoid().GetCloneID() == cloneID + && cloneKey->GetUoid().GetClonePlayerID() == playerID) + return plKey::Make(cloneKey); + } + + return plKey(); +} + +UInt32 plKeyImp::GetNumClones() +{ + return fClones.GetCount(); +} + +plKey plKeyImp::GetCloneByIdx(UInt32 idx) +{ + if (idx < fClones.GetCount()) + return plKey::Make(fClones[idx]); + + return nil; +} + +void plKeyImp::SatisfyPending(plRefMsg* msg) const +{ + for (int i = 0; i < msg->GetNumReceivers(); i++) + ((plKeyImp*)msg->GetReceiver(i))->SatisfyPending(); +} + +void plKeyImp::SatisfyPending() const +{ + hsAssert(fPendingRefs > 0, "Have more requests satisfied than we made"); + if (!--fPendingRefs) + { +#ifdef PL_SEND_SATISFIED + plSatisfiedMsg* msg = TRACKED_NEW plSatisfiedMsg(this); + plgDispatch::MsgSend(msg); +#endif // PL_SEND_SATISFIED + } +} + +void plKeyImp::ISetupNotify(plRefMsg* msg, plRefFlags::Type flags) +{ + msg->SetSender(nil); + + AddNotifyCreated(msg, flags); + + hsAssert(msg->GetNumReceivers(), "nil object getting a reference"); + for (int i = 0; i < msg->GetNumReceivers(); i++) + ((plKeyImp*)msg->GetReceiver(i))->AddRef(plKey::Make(this)); +} + +void plKeyImp::SetupNotify(plRefMsg* msg, plRefFlags::Type flags) +{ + hsKeyedObject* ko = ObjectIsLoaded(); + + ISetupNotify(msg, flags); + + // the KeyedObject is already Loaded, so we better go ahead and send the notify message just added. + if (ko) + { + hsRefCnt_SafeRef(ko); + msg->SetRef(ko); + msg->SetTimeStamp(hsTimer::GetSysSeconds()); + + // Add ref, since Dispatch will unref this msg but we want to keep using it. + hsRefCnt_SafeRef(msg); + + SetNotified(GetNumNotifyCreated()-1); + SatisfyPending(msg); + +#ifdef LOAD_IN_THREAD // test for resLoader + plgDispatch::Dispatch()->MsgQueue(msg); +#else + plgDispatch::Dispatch()->MsgSend(msg); +#endif + + hsRefCnt_SafeUnRef(ko); + } + hsRefCnt_SafeUnRef(msg); +} + +// We could just call NotifyCreated() on all our fRefs, and then fix +// up fNotified to only get set when the message actually was delivered (i.e. +// refMsg->GetReceiver(0)->GetObjectPtr() != nil. But that only really works +// if we guarantee the refMsg->GetNumReceivers() == 1. +// This looks like it'll take forever to run, but this is only called right +// when our object has just been loaded, at which time normally fRefs.GetCount() == 0. +void plKeyImp::INotifySelf(hsKeyedObject* ko) +{ + for (int i = 0; i < fRefs.GetCount(); i++) + { + hsKeyedObject* rcv = fRefs[i]->GetObjectPtr(); + if (rcv) + { + for (int j = 0; j < fRefs[i]->fNotifyCreated.GetCount(); j++) + { + plRefMsg* refMsg = fRefs[i]->fNotifyCreated[j]; + if (refMsg && refMsg->GetRef() && !fRefs[i]->IsNotified(j)) + { + hsAssert(refMsg->GetRef() == rcv, "Ref message out of sync with its ref"); + + // GetNumReceivers() should always be 1 for a refMsg. + for (int k = 0; k < refMsg->GetNumReceivers(); k++) + { + if (&(*refMsg->GetReceiver(k)) == (plKeyData*)this) + { + fRefs[i]->SetNotified(j); + fRefs[i]->SatisfyPending(refMsg); + + hsRefCnt_SafeRef(refMsg); + plgDispatch::MsgSend(refMsg); + break; + } + } + } + } + } + } +} + +void plKeyImp::NotifyCreated() +{ + hsKeyedObject* ko = GetObjectPtr(); + hsRefCnt_SafeRef(ko); + hsAssert(ko, "Notifying of created before on nil object"); + + INotifySelf(ko); + + for (int i = 0; i < GetNumNotifyCreated(); i++) + { + if (!IsNotified(i) && GetNotifyCreated(i)->GetReceiver(0)->GetObjectPtr()) + { + plRefMsg* msg = GetNotifyCreated(i); + msg->SetRef(ko); + msg->SetTimeStamp(hsTimer::GetSysSeconds()); + msg->SetContext(plRefMsg::kOnCreate); + hsRefCnt_SafeRef(msg); + + SetNotified(i); + SatisfyPending(msg); + + plgDispatch::MsgSend(msg); + } + } + hsRefCnt_SafeUnRef(ko); +} + +void plKeyImp::INotifyDestroyed() +{ + hsKeyedObject* ko = GetObjectPtr(); + hsAssert(ko, "Notifying of destroy on already destroyed"); + int i; + for( i = 0; i < GetNumNotifyCreated(); i++ ) + { + hsAssert(ko, "Notifying of destroy on already destroyed"); + plRefMsg* msg = GetNotifyCreated(i); + msg->SetRef(ko); + msg->SetTimeStamp(hsTimer::GetSysSeconds()); + msg->SetContext(plRefMsg::kOnDestroy); + hsRefCnt_SafeRef(msg); + msg->Send(); + } + fNotified.Clear(); +} + +void plKeyImp::IClearRefs() +{ + while (GetNumRefs()) + IRelease(GetRef(0)); + fRefs.Reset(); + + for (int i = 0; i < GetNumNotifyCreated(); i++) + { + plRefMsg* msg = GetNotifyCreated(i); + for (int j = 0; j < msg->GetNumReceivers(); j++) + ((plKeyImp*)msg->GetReceiver(j))->RemoveRef(this); + } +} + +void plKeyImp::Release(plKey targetKey) +{ + IRelease((plKeyImp*)targetKey); +} + +void plKeyImp::IRelease(plKeyImp* iTargetKey) +{ + // Remove the target key from my ref list + RemoveRef(iTargetKey); + + // Inspect the target key to find whether it is supposed to send a message + // to me on destruction, and to find out if I have an active of passive + // ref on this key. Not sure why I don't track my own active/passive ref states + hsBool isActive = false; + int iTarg = -1; + for (int i = 0; (iTarg < 0) && (i < iTargetKey->GetNumNotifyCreated()); i++) + { + plMessage* rcvMsg = iTargetKey->GetNotifyCreated(i); + for (int j = 0; j < rcvMsg->GetNumReceivers(); j++) + { + if (&(*rcvMsg->GetReceiver(j)) == (plKeyData*)this) + { + isActive = iTargetKey->IsActiveRef(iTarg = i); + break; + } + } + } + + if (iTarg < 0) + { + // If it doesn't send me a message on destruction, I am assuming I don't have an + // active ref on the key (seems to be always true, but should we depend on it?) + // Since it doesn't send me a message, and won't be destroyed by the release, no + // need to do anything more here + return; + } + + // If releasing this target causes its destruction, we'll notify + // everyone that target is dead. Otherwise, we'll remove + // releaser from target's notify list and notify releaser + // it's been removed. Object doesn't actually get deleted until + // it receives the SelfDestruct msg, by which time everyone referencing + // it has been notified it is going away. +#ifdef LOG_ACTIVE_REFS + if (isActive && IsTrackedKey(iTargetKey)) + hsStatusMessageF("@@@ %s(%s) releasing active ref on %s (%d total)", GetName(), plFactory::GetNameOfClass(GetUoid().GetClassType()), kObjName, iTargetKey->fNumActiveRefs-1); +#endif // LOG_ACTIVE_REFS + + if (isActive && iTargetKey->GetActiveRefs() && !iTargetKey->DecActiveRefs()) + { + iTargetKey->INotifyDestroyed(); + + iTargetKey->IClearRefs(); + iTargetKey->ClearNotifyCreated(); + + plKey key = plKey::Make(iTargetKey); + plSelfDestructMsg* nuke = TRACKED_NEW plSelfDestructMsg(key); + plgDispatch::Dispatch()->MsgSend(nuke); + } + else + { + plRefMsg* refMsg = iTargetKey->GetNotifyCreated(iTarg); + hsRefCnt_SafeRef(refMsg); + iTargetKey->RemoveNotifyCreated(iTarg); + if (refMsg) + { + refMsg->SetRef(iTargetKey->ObjectIsLoaded()); + refMsg->SetTimeStamp(hsTimer::GetSysSeconds()); + refMsg->SetContext(plRefMsg::kOnRemove); + hsRefCnt_SafeRef(refMsg); + plgDispatch::MsgSend(refMsg); + } + hsRefCnt_SafeUnRef(refMsg); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKeyImp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKeyImp.h new file mode 100644 index 00000000..ca04a43f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plKeyImp.h @@ -0,0 +1,161 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plKeyImp_inc +#define plKeyImp_inc + +#include "plKey.h" +#include "hsTemplates.h" +#include "plUoid.h" +#include "hsBitVector.h" +#include "plRefFlags.h" + +//------------------------------------ +// plKey is a handle to a keyedObject +//------------------------------------ +class plKeyImp : public plKeyData +{ +public: + plKeyImp(); + plKeyImp(plUoid, UInt32 pos,UInt32 len); + virtual ~plKeyImp(); + + virtual const plUoid& GetUoid() const { return fUoid; } + virtual const char* GetName() const; + + virtual hsKeyedObject* GetObjectPtr(); + virtual hsKeyedObject* ObjectIsLoaded() const; + virtual hsKeyedObject* VerifyLoaded(); + + // called before writing to disk so that static keys can have faster lookups (int compare instead of string compare) + void SetObjectID(UInt32 id) {fUoid.SetObjectID(id);} + + //---------------------- + // I/O + // ResMgr performs read, so it can search for an existing instance.... + //---------------------- + void Read(hsStream* s); + void Write(hsStream* s); + void WriteObject(hsStream* s); + // For when you need to skip over a key in a stream + static void SkipRead(hsStream* s); + + UInt32 GetStartPos() const { return fStartPos; } // for ResMgr to read the Objects + UInt32 GetDataLen() const { return fDataLen; } // for ResMgr to read the Objects + + //---------------------- + // Allow a keyed object to behave as if it has an active ref when in fact the object + // should only be active ref'ed by a non-keyed parent. Essentially just bumps/decs + // the active ref count to facilitate normal object creation/destruction + //---------------------- + virtual hsKeyedObject* RefObject(plRefFlags::Type flags = plRefFlags::kActiveRef); + virtual void UnRefObject(plRefFlags::Type flags = plRefFlags::kActiveRef); + + //---------------------- + // Release has two behaviors, depending on whether the ref is active or passive: + // Active - Release decs the ActiveRefCnt. When it gets to zero, the object will be deleted. + // Passive - Unregisters my interest in when the object is created or destroyed. + //---------------------- + virtual void Release(plKey targetKey); + + void UnRegister(); + void SetUoid(const plUoid& uoid); + void SetupNotify(plRefMsg* msg, plRefFlags::Type flags); + + // hsKeyedObject use only! + hsKeyedObject* SetObjectPtr(hsKeyedObject* p); + + //////////////////////////////////////////////////////////////////////////// + // ResManager/Registry use only! + // + + //---------------------- + // Clone access + //---------------------- + void AddClone(plKeyImp* c); + void RemoveClone(plKeyImp* c) const; + plKey GetClone(UInt32 playerID, UInt32 cloneID) const; + void CopyForClone(const plKeyImp* p, UInt32 playerID, UInt32 cloneID); // Copy the contents of p for cloning process + + UInt32 GetNumClones(); + plKey GetCloneByIdx(UInt32 idx); + plKey GetCloneOwner() { return fCloneOwner; } + + void NotifyCreated(); + void ISetupNotify(plRefMsg* msg, plRefFlags::Type flags); // Setup notifcations for reference, don't send anything. + + void AddRef(plKeyImp* key) const; + UInt16 GetNumRefs() const { return fRefs.GetCount(); } + plKeyImp* GetRef(int i) const { return fRefs[i]; } + void RemoveRef(plKeyImp *key) const; + + virtual UInt16 GetActiveRefs() const { return fNumActiveRefs; } + virtual UInt16 GetNumNotifyCreated() const { return fNotifyCreated.GetCount(); } + virtual plRefMsg* GetNotifyCreated(int i) const { return fNotifyCreated[i]; } + virtual const hsBitVector& GetActiveBits() const { return fActiveRefs; } + +protected: + void AddNotifyCreated(plRefMsg* msg, plRefFlags::Type flags); + void ClearNotifyCreated(); + UInt16 GetNumNotifyCreated() { return fNotifyCreated.GetCount(); } + plRefMsg* GetNotifyCreated(int i) { return fNotifyCreated[i]; } + void RemoveNotifyCreated(int i); + + UInt16 IncActiveRefs() { return ++fNumActiveRefs; } + UInt16 DecActiveRefs() { return fNumActiveRefs ? --fNumActiveRefs : 0; } + + hsBool IsActiveRef(int i) const { return fActiveRefs.IsBitSet(i) != 0; } + void SetActiveRef(int i, hsBool on=true) { fActiveRefs.SetBit(i, on); } + hsBool IsNotified(int i) const { return fNotified.IsBitSet(i) != 0; } + void SetNotified(int i, hsBool on=true) { fNotified.SetBit(i, on); } + + void SatisfyPending(plRefMsg* msg) const; + void SatisfyPending() const; + + void INotifySelf(hsKeyedObject* ko); + void INotifyDestroyed(); + void IClearRefs(); + + void IRelease(plKeyImp* keyImp); + + hsKeyedObject* fObjectPtr; + + // These fields are the ones actually saved to disk + plUoid fUoid; + UInt32 fStartPos; // where I live in the Datafile + UInt32 fDataLen; // Length in the Datafile + + // Following used by hsResMgr to notify on defered load or when a passive ref is destroyed. + UInt16 fNumActiveRefs; // num active refs on me + hsBitVector fActiveRefs; // Which of notify created are active refs + hsBitVector fNotified; // which of notifycreated i've already notified. + hsTArray fNotifyCreated; // people to notify when I'm created or destroyed + mutable hsTArray fRefs; // refs I've made (to be released when I'm unregistered). + mutable Int16 fPendingRefs; // Outstanding requests I have out. + mutable hsTArray fClones; // clones of me + mutable plKey fCloneOwner; // pointer for clones back to the owning key +}; + +#endif // hsRegistry_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plMsgForwarder.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plMsgForwarder.cpp new file mode 100644 index 00000000..29dce4e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plMsgForwarder.cpp @@ -0,0 +1,208 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plMsgForwarder.h" +#include "hsResMgr.h" +#include "../pnMessage/plMessage.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnNetCommon/plSynchedObject.h" + +#include "../pnMessage/plSelfDestructMsg.h" +#include "../pnMessage/plMessageWithCallbacks.h" + + +class plForwardCallback +{ +public: + hsTArray fOrigReceivers; + int fNumCallbacks; + hsBool fNetPropogate; +}; + +plMsgForwarder::plMsgForwarder() +{ +} + +plMsgForwarder::~plMsgForwarder() +{ + CallbackMap::iterator i = fCallbacks.begin(); + + for (; i != fCallbacks.end(); i++) + delete (*i).second; +} + +void plMsgForwarder::Read(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Read(s, mgr); + + int numKeys = s->ReadSwap32(); + fForwardKeys.Reset(); + fForwardKeys.Expand(numKeys); + fForwardKeys.SetCount(numKeys); + for (int i = 0; i < numKeys; i++) + { + plKey key = mgr->ReadKey(s); + fForwardKeys[i] = key; + } +} + +void plMsgForwarder::Write(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Write(s, mgr); + + int numKeys = fForwardKeys.Count(); + s->WriteSwap32(numKeys); + for (int i = 0; i < numKeys; i++) + mgr->WriteKey(s, fForwardKeys[i]); +} + +hsBool plMsgForwarder::MsgReceive(plMessage* msg) +{ + // Self destruct messages are for us only + plSelfDestructMsg *selfMsg = plSelfDestructMsg::ConvertNoRef(msg); + if (selfMsg) + return hsKeyedObject::MsgReceive(msg); + + // If this is a callback message, it needs to be handled differently + if (IForwardCallbackMsg(msg)) + return true; + + // All other messages are forwarded + IForwardMsg(msg); + return true; +} + +hsBool plMsgForwarder::IForwardCallbackMsg(plMessage *msg) +{ + // Only process as a callback message if it is one, AND it has callbacks + plMessageWithCallbacks *callbackMsg = plMessageWithCallbacks::ConvertNoRef(msg); + if (callbackMsg && callbackMsg->GetNumCallbacks() > 0) + { + for (int i = 0; i < callbackMsg->GetNumCallbacks(); i++) + { + plEventCallbackMsg *event = callbackMsg->GetEventCallback(i); + hsAssert(event, "Message forwarder only supports event callback messages"); + if (event) + { + plForwardCallback *fc = TRACKED_NEW plForwardCallback; + fc->fNumCallbacks = fForwardKeys.Count(); + + // Turn off net propagate the callbacks to us will all be local. Only the + // callback we send will go over the net + fc->fNetPropogate = (event->HasBCastFlag(plMessage::kNetPropagate) != 0); + event->SetBCastFlag(plMessage::kNetPropagate, false); + + for (int j = 0; j < event->GetNumReceivers(); j++) + fc->fOrigReceivers.Append((plKey)event->GetReceiver(j)); + + event->ClearReceivers(); + event->AddReceiver(GetKey()); + + fCallbacks[event] = fc; + +#if 0 + hsStatusMessageF("Adding CBMsg, eventSender=%s, eventRemoteMsg=%d\n", + event->GetSender() ? event->GetSender()->GetName() : "nil", fc->fNetPropogate); +#endif + } + } +#if 0 + hsStatusMessageF("Fwding CBMsg, sender=%s, remoteMsg=%d", + msg->GetSender() ? msg->GetSender()->GetName() : "nil", msg->HasBCastFlag(plMessage::kNetNonLocal)); +#endif + IForwardMsg(callbackMsg); + + return true; + } + + // Callback from one of our forward keys. Don't send the final callback to the original + // requester until all forward keys have reported in. + plEventCallbackMsg *eventMsg = plEventCallbackMsg::ConvertNoRef(msg); + if (eventMsg) + { + CallbackMap::const_iterator it = fCallbacks.find(eventMsg); + if (it != fCallbacks.end()) + { + plForwardCallback *fc = it->second; + if (--fc->fNumCallbacks == 0) + { + hsStatusMessageF("plEventCallbackMsg received, erasing, sender=%s, remoteMsg=%d\n", + msg->GetSender() ? msg->GetSender()->GetName() : "nil", msg->HasBCastFlag(plMessage::kNetNonLocal)); + + fCallbacks.erase(eventMsg); + + plUoid uoid = GetKey()->GetUoid(); + hsBool locallyOwned = (plNetClientApp::GetInstance()->IsLocallyOwned(uoid) != plSynchedObject::kNo); + + // If the callback was originally net propagated, and we own this forwarder, net propagate the callback + if (fc->fNetPropogate && locallyOwned) + eventMsg->SetBCastFlag(plMessage::kNetPropagate); + + eventMsg->ClearReceivers(); + eventMsg->AddReceivers(fc->fOrigReceivers); + eventMsg->SetSender(GetKey()); + hsRefCnt_SafeRef(eventMsg); + eventMsg->Send(); + + delete fc; + } + } + else + { + hsStatusMessageF("! Unknown plEventCallbackMsg received, sender=%s, remoteMsg=%d\n", + msg->GetSender() ? msg->GetSender()->GetName() : "nil", msg->HasBCastFlag(plMessage::kNetNonLocal)); + hsAssert(0, "Unknown plEventCallbackMsg received"); + } + return true; + } + + return false; +} + +void plMsgForwarder::IForwardMsg(plMessage *msg) +{ + // Back up the message's original receivers + hsTArray oldKeys; + for (int i = 0; i < msg->GetNumReceivers(); i++) + oldKeys.Append((plKey)msg->GetReceiver(i)); + + // Set to our receivers and send + hsRefCnt_SafeRef(msg); + msg->ClearReceivers(); + msg->AddReceivers(fForwardKeys); + msg->Send(); + + // Reset back to the original receivers. This is so we don't screw up objects + // who reuse their messages + msg->ClearReceivers(); + msg->AddReceivers(oldKeys); +} + +void plMsgForwarder::AddForwardKey(plKey key) +{ + if (fForwardKeys.Find(key) == fForwardKeys.kMissingIndex) + fForwardKeys.Append(key); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plMsgForwarder.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plMsgForwarder.h new file mode 100644 index 00000000..855fdffc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plMsgForwarder.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plMsgForwarder_h_inc +#define plMsgForwarder_h_inc + +#include "hsKeyedObject.h" +#include "hsTemplates.h" +#include "hsStlUtils.h" + + +class plMessageWithCallbacks; +class plForwardCallback; + +class plMsgForwarder : public hsKeyedObject +{ +protected: + hsTArray fForwardKeys; + + typedef std::map CallbackMap; + CallbackMap fCallbacks; + + void IForwardMsg(plMessage *msg); + hsBool IForwardCallbackMsg(plMessage *msg); + +public: + plMsgForwarder(); + ~plMsgForwarder(); + + CLASSNAME_REGISTER(plMsgForwarder); + GETINTERFACE_ANY(plMsgForwarder, hsKeyedObject); + + void Read(hsStream* s, hsResMgr* mgr); + void Write(hsStream* s, hsResMgr* mgr); + + hsBool MsgReceive(plMessage* msg); + + void AddForwardKey(plKey key); +}; + +#endif // plMsgForwarder_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plReceiver.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plReceiver.h new file mode 100644 index 00000000..317896f6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plReceiver.h @@ -0,0 +1,45 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plReceiver_inc +#define plReceiver_inc + +#include "../pnFactory/plCreatable.h" + +class plMessage; + +class plReceiver : public plCreatable +{ +public: + plReceiver() {} + + CLASSNAME_REGISTER( plReceiver ); + GETINTERFACE_ANY( plReceiver, plCreatable ); + + virtual hsBool MsgReceive(plMessage* msg) { return false; } +}; + +#endif // plReceiver_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plTempKey.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plTempKey.cpp new file mode 100644 index 00000000..ee1cdb85 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plTempKey.cpp @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "plTempKey.h" +#include "hsKeyedObject.h" +#include "plUoid.h" + + + +plTempKey::plTempKey(hsKeyedObject *pO,const char *nm) +{ + hsAssert(pO,"plTempKey, Need Object!"); + pO->SetKey(this); + plLocation loc( plLocation::kGlobalFixedLoc ); + fUoid = plUoid( loc, pO->ClassIndex(), "temp"); + fUoid.SetTemporary( true ); // Must set temp flag! + +#ifdef HS_DEBUGGING + fIDName = fUoid.GetObjectName(); + fClassType = plFactory::GetNameOfClass( fUoid.GetClassType() ); +#endif +} + +plTempKey::~plTempKey() +{ + UnRegister(); + delete fObjectPtr; + fObjectPtr = nil; +} + +void plTempKey::VerifyLoaded() const +{ + // Can't do a whole lot in this case for tempKeys :) +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plTempKey.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plTempKey.h new file mode 100644 index 00000000..9887033b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plTempKey.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plTempKey_inc +#define plTempKey_inc + +#include "plKeyImp.h" + +class hsKeyedObject; + + //------------------------------------ + // plTempKey is a handle to a keyedObject, which is not registered + // The key may be handed to others to send messages. + // when done with a plTempKey call ReleaseTemporary (from Base class plKey) + //------------------------------------ + +class plTempKey : public plKeyImp +{ + +public: + plTempKey(hsKeyedObject *pO,const char *nm=nil); + plTempKey(plUoid u) { fUoid=u; } // used server side + ~plTempKey(); // USE plKey->ReleaseTemporary to delete this... + + virtual void VerifyLoaded() const; +}; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.cpp new file mode 100644 index 00000000..f61f7d8b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.cpp @@ -0,0 +1,268 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plUoid.h" +#include "hsStream.h" +#include "hsUtils.h" + +//// plLocation ////////////////////////////////////////////////////////////// + +const plLocation plLocation::kGlobalFixedLoc(plLocation::kGlobalFixedLocIdx); +const plLocation plLocation::kLocalStartLoc(plLocation::kLocalLocStartIdx); +const plLocation plLocation::kLocalEndLoc(plLocation::kLocalLocEndIdx); +const plLocation plLocation::kNormalStartLoc(plLocation::kNormalLocStartIdx); +const plLocation plLocation::kGlobalServerLoc(plLocation::kGlobalServerLocIdx, plLocation::kReserved); +const plLocation plLocation::kInvalidLoc; + +plLocation::plLocation(const plLocation& toCopyFrom) +{ + *this = toCopyFrom; +} + +void plLocation::Read(hsStream* s) +{ + s->LogReadSwap(&fSequenceNumber, "Location Sequence Number"); + s->LogReadSwap(&fFlags, "Location Flags"); +} + +void plLocation::Write(hsStream* s) const +{ + s->WriteSwap(fSequenceNumber); + s->WriteSwap(fFlags); +} + +plLocation& plLocation::operator=(const plLocation& rhs) +{ + fSequenceNumber = rhs.fSequenceNumber; + fFlags = rhs.fFlags; + return *this; +} + +hsBool plLocation::operator==(const plLocation& u) const +{ + // Ignore the itinerant flag when comparing, because + return (fSequenceNumber == u.fSequenceNumber) && ((fFlags & ~kItinerant) == (u.fFlags & ~kItinerant)); +} + +void plLocation::Set(UInt32 seqNum) +{ + fSequenceNumber = seqNum; +} + +void plLocation::Invalidate() +{ + fSequenceNumber = kInvalidLocIdx; + fFlags = 0; // Set to kInvalid? +} + +hsBool plLocation::IsValid() const +{ + return (fSequenceNumber == kInvalidLocIdx) ? false : true; +} + +hsBool plLocation::IsReserved() const +{ + return hsCheckBits(fFlags, kReserved); +} + +hsBool plLocation::IsItinerant() const +{ + return hsCheckBits(fFlags, kItinerant); +} + +hsBool plLocation::IsVirtual() const +{ + // This returns whether the location is "virtual", i.e. isn't a true room per se. Like fixed keys + if (fSequenceNumber == kGlobalFixedLocIdx) + return true; + + return false; +} + +// THIS SHOULD BE FOR DEBUGGING ONLY +char* plLocation::StringIze(char* str) const // Format to displayable string +{ + sprintf(str, "S0x%xF0x%x", fSequenceNumber, int(fFlags)); + return str; +} + +plLocation plLocation::MakeReserved(UInt32 number) +{ + return plLocation(kReservedLocAvailableStart + number, kReserved); +} + +plLocation plLocation::MakeNormal(UInt32 number) +{ + return plLocation(kNormalLocStartIdx + number); +} + +//// plUoid ////////////////////////////////////////////////////////////////// + +plUoid::plUoid(const plLocation& location, UInt16 classType, const char* objectName, const plLoadMask& m) +{ + fObjectName = nil; + Invalidate(); + + fLocation = location; + fClassType = classType; + fObjectName = hsStrcpy(objectName); + fLoadMask = m; + fClonePlayerID = 0; +} + +plUoid::plUoid(const plUoid& src) +{ + fObjectName = nil; + Invalidate(); + *this = src; +} + +plUoid::~plUoid() +{ + Invalidate(); +} + +void plUoid::Read(hsStream* s) +{ + hsAssert(fObjectName == nil, "Reading over an old uoid? You're just asking for trouble, aren't you?"); + + // first read contents flags + UInt8 contents = s->ReadByte(); + + fLocation.Read(s); + + // conditional loadmask read + if (contents & kHasLoadMask) + fLoadMask.Read(s); + else + fLoadMask.SetAlways(); + + s->LogReadSwap(&fClassType, "ClassType"); + s->LogReadSwap(&fObjectID, "ObjectID"); + s->LogSubStreamPushDesc("ObjectName"); + fObjectName = s->LogReadSafeString(); + + // conditional cloneIDs read + if (contents & kHasCloneIDs) + { + s->LogReadSwap( &fCloneID ,"CloneID"); + UInt16 dummy; + s->LogReadSwap(&dummy, "dummy"); // To avoid breaking format + s->LogReadSwap( &fClonePlayerID ,"ClonePlayerID"); + } + else + { + fCloneID = 0; + fClonePlayerID = 0; + } +} + +void plUoid::Write(hsStream* s) const +{ + // first write contents byte + UInt8 contents = IsClone() ? kHasCloneIDs : 0; + if (fLoadMask.IsUsed()) + contents |= kHasLoadMask; + s->WriteByte(contents); + + fLocation.Write(s); + + // conditional loadmask write + if (contents & kHasLoadMask) + fLoadMask.Write(s); + + s->WriteSwap( fClassType ); + s->WriteSwap( fObjectID ); + s->WriteSafeString( fObjectName ); + + // conditional cloneIDs write + if (contents & kHasCloneIDs) + { + s->WriteSwap(fCloneID); + UInt16 dummy = 0; + s->WriteSwap(dummy); // to avoid breaking format + s->WriteSwap(fClonePlayerID); + } +} + +void plUoid::Invalidate() +{ + fObjectID = 0; + fCloneID = 0; + fClonePlayerID = 0; + fClassType = 0; + if (fObjectName) + delete [] fObjectName; + fObjectName = nil; + fLocation.Invalidate(); + fLoadMask = plLoadMask::kAlways; + +} + +hsBool plUoid::IsValid() const +{ + if (!fLocation.IsValid() || fObjectName == nil) + return false; + + return true; +} + +hsBool plUoid::operator==(const plUoid& u) const +{ + return fLocation == u.fLocation + && fLoadMask == u.fLoadMask + && fClassType == u.fClassType + && hsStrEQ(fObjectName, u.fObjectName) + && fObjectID == u.fObjectID + && fCloneID == u.fCloneID + && fClonePlayerID == u.fClonePlayerID; +} + +plUoid& plUoid::operator=(const plUoid& rhs) +{ + fObjectID = rhs.fObjectID; + fCloneID = rhs.fCloneID; + fClonePlayerID = rhs.fClonePlayerID; + fClassType = rhs.fClassType; + if (fObjectName) + delete [] fObjectName; + fObjectName = hsStrcpy(rhs.fObjectName); + fLocation = rhs.fLocation; + fLoadMask = rhs.fLoadMask; + + return *this; +} + +// THIS SHOULD BE FOR DEBUGGING ONLY +char* plUoid::StringIze(char* str) const // Format to displayable string +{ + sprintf(str, "(0x%x:0x%x:%s:C:[%lu,%lu])", + fLocation.GetSequenceNumber(), + int(fLocation.GetFlags()), + fObjectName, + GetClonePlayerID(), + GetCloneID()); + return str; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.h new file mode 100644 index 00000000..87e90d22 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/plUoid.h @@ -0,0 +1,179 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plUoid - A Unique Object IDentifier -- basically, each unique Uoid refers +// to one and exactly one object. +// To define such, it contains three elements: +// - A plLocation, which specifies an (age,chapter,page) combo +// (as a sequence number) +// - A creatable class type (from plFactory) +// - An object name +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef plUoid_h_inc +#define plUoid_h_inc + +#include "hsTypes.h" +#include "plFixedKey.h" +#include "plLoadMask.h" + +class hsStream; + +//// plLocation ////////////////////////////////////////////////////////////// + +class plLocation +{ +public: + enum LocFlags + { + kLocalOnly = 0x1, // Set if nothing in the room saves state. + kVolatile = 0x2, // Set is nothing in the room persists when the server exits. + kReserved = 0x4, + kBuiltIn = 0x8, + kItinerant = 0x10, + }; + +protected: + UInt32 fSequenceNumber; + UInt16 fFlags; + + enum + { + kGlobalFixedLocIdx = 0, // Fixed keys go here, think of as "global,fixed,keys" + kSceneViewerLocIdx = 1, + + kLocalLocStartIdx = 3, // These are a range of #s that go to local, testing-only pages. + kLocalLocEndIdx = 32, // You can't go over the network with any keys with these locs. + + kNormalLocStartIdx = kLocalLocEndIdx + 1, + + kReservedLocStart = 0xff000000, // Reserved locations are ones that aren't real game locations, + kGlobalServerLocIdx = kReservedLocStart, // Global pool room for the server. Only the server gets this one + + kReservedLocAvailableStart = kGlobalServerLocIdx + 1, // This is the start of the *really* available ones + kReservedLocEnd = 0xfffffffe, // But instead act as a holding place for data + + kInvalidLocIdx = 0xffffffff + }; + + plLocation(UInt32 seqNum, UInt16 flags=0) : fFlags(flags) { Set(seqNum); } + +public: + plLocation() { Invalidate(); } + plLocation(const plLocation& toCopyFrom); + ~plLocation() {} + + void Invalidate(); + hsBool IsValid() const; + hsBool IsReserved() const; + hsBool IsItinerant() const; + void Set(UInt32 seqNum); + UInt32 GetSequenceNumber() const { return fSequenceNumber; } + hsBool IsVirtual() const; + + void SetFlags(UInt16 flags) { fFlags |= flags; } + UInt16 GetFlags() const { return fFlags; } + + void Read(hsStream* s); + void Write(hsStream* s) const; + + hsBool operator==(const plLocation& loc) const; + hsBool operator!=(const plLocation& loc) const { return !(loc == *this); } + plLocation& operator=(const plLocation& loc); + bool operator<(const plLocation& loc ) const { return fSequenceNumber < loc.fSequenceNumber; } + + // THIS SHOULD BE FOR DEBUGGING ONLY + char* StringIze(char* str) const; // Format to displayable string. Returns the same string for convenience + + static plLocation MakeReserved(UInt32 number); + static plLocation MakeNormal(UInt32 number); + + static const plLocation kGlobalFixedLoc; + static const plLocation kSceneViewerLoc; + static const plLocation kLocalStartLoc; + static const plLocation kLocalEndLoc; + static const plLocation kNormalStartLoc; + static const plLocation kGlobalServerLoc; + static const plLocation kInvalidLoc; +}; + +//// plUoid ////////////////////////////////////////////////////////////////// + +class plUoid +{ +public: + plUoid() { fObjectName = nil; Invalidate(); } + plUoid(const plLocation& location, UInt16 classType, const char* objectName, const plLoadMask& m=plLoadMask::kAlways); + plUoid(plFixedKeyId fixedKey); + plUoid(const plUoid& src); + ~plUoid(); + + const plLocation& GetLocation() const { return fLocation; } + UInt16 GetClassType() const { return fClassType; } + const char* GetObjectName() const { return fObjectName; } + const plLoadMask& GetLoadMask() const { return fLoadMask; } + + void Read(hsStream* s); + void Write(hsStream* s) const; + + void Invalidate(); + hsBool IsValid() const; + + plUoid& operator=(const plUoid& u); + hsBool operator==(const plUoid& u) const; + hsBool operator!=(const plUoid& u) const { return !operator==(u); } + + hsBool IsClone() const { return fCloneID != 0; } + UInt32 GetClonePlayerID() const { return fClonePlayerID; } + UInt32 GetCloneID() const { return fCloneID; } + void SetClone(UInt32 playerID, UInt32 cloneID) { hsAssert(cloneID < 0xffff, "Clone id too high"); fCloneID = UInt16(cloneID); fClonePlayerID = playerID; } + + UInt32 GetObjectID() const { return fObjectID; } + // Export time only. Only plRegistryKeyList should call this. + void SetObjectID(UInt32 id) { fObjectID = id; } + + // THIS SHOULD BE FOR DEBUGGING ONLY + char* StringIze(char* str) const; // Format to displayable string + +protected: + enum ContentsFlags // for read/write functions + { + kHasCloneIDs = 0x1, + kHasLoadMask = 0x2, + }; + + UInt32 fObjectID; + UInt32 fClonePlayerID; // The ID of the player who made this clone + UInt16 fCloneID; // The ID of this clone (unique per client) + UInt16 fClassType; + char* fObjectName; + plLocation fLocation; + plLoadMask fLoadMask; +}; + +#endif // plUoid_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/pnKeyedObjectCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/pnKeyedObjectCreatable.h new file mode 100644 index 00000000..d60a6987 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnKeyedObject/pnKeyedObjectCreatable.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pnKeyedObject_inc +#define pnKeyedObject_inc + +#include "../pnFactory/plCreator.h" + +#include "hsKeyedObject.h" +REGISTER_CREATABLE( hsKeyedObject ); + +#include "plReceiver.h" + +REGISTER_NONCREATABLE( plReceiver ); + +#include "plMsgForwarder.h" +REGISTER_CREATABLE(plMsgForwarder); + +#endif // pnKeyedObject_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/Pch.h new file mode 100644 index 00000000..d870f361 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/Pch.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnMail/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNMAIL_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnMail/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNMAIL_PCH_H + + +#include "pnUtils/pnUtils.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" + +#include "pnMail.h" + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/pnMail.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/pnMail.cpp new file mode 100644 index 00000000..c019b63b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/pnMail.cpp @@ -0,0 +1,714 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnMail/pnMail.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + +enum EMailStep { + kMailStepConnect, // wait for 220, then send "helo" command + kMailStepAuth, // wait for 250, then send "AUTH PLAIN " + kMailStepSender, // wait for 235/250, then send "mail from:" command + kMailStepRecipient, // wait for 250, then send "rcpt to:" command + kMailStepStartLetter, // wait for 250, then send "data" command + kMailStepLetter, // wait for 354, then send letter + kMailStepQuit, // wait for 250, then send "quit" command + kMailStepDisconnect, // wait for 221, then disconnect +}; + + +struct MailTransaction { + LINK(MailTransaction) link; + AsyncSocket sock; + const char ** stepTable; + EMailStep step; + unsigned subStep; + EMailError error; + FMailResult callback; + void * param; + + char * smtp; + char * sender; + char * recipient; + char * replyTo; + char * subject; + char * bodyEncoding; + char * body; + char * auth; + char buffer[1]; +}; + +static bool MailNotifyProc ( + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +); + +static void __cdecl Send ( + AsyncSocket sock, + const char str[], + ... +); + + +/***************************************************************************** +* +* Private data +* +***/ + +static bool s_shutdown; +static CCritSect s_critsect; +static LISTDECL(MailTransaction, link) s_mail; + + +//=========================================================================== +// Authenticated email sequence +// -> "220 catherine.cyan.com ESMTP\r\n" +// <- "HELO cyan.com\r\n" +// -> "250 catherine.cyan.com\r\n" +// <- "AUTH PLAIN XXXXXXXXX\r\n" +// -> "235 go ahead\r\n" +// <- "MAIL FROM:\r\n" +// -> "250 ok mail from accepted\r\n" +// <- "RCPT TO:\r\n" +// -> "250 ok recipient accepted\r\n" +// <- "DATA\r\n" +// -> "354 ok, go ahead:\r\n" +// -> + "\r\n.\r\n" +// -> "250 message accepted for delivery\r\n" +// <- "QUIT\r\n" +// -> "221 catherine.cyan.com\r\n" +//=========================================================================== +static const char * s_waitStrAuth[] = { + "220 ", // kMailStepConnect + "250 ", // kMailStepAuth + "235 ", // kMailStepSender + "250 ", // kMailStepRecipient + "250 ", // kMailStepStartLetter + "354 ", // kMailStepLetter + "250 ", // kMailStepQuit + "221 ", // kMailStepDisconnect +}; + +//=========================================================================== +// Unauthenticated email seqeunce +// -> "220 catherine.cyan.com ESMTP\r\n" +// <- "HELO cyan.com\r\n" +// -> "250 catherine.cyan.com\r\n" +// <- "MAIL FROM:\r\n" +// -> "250 ok mail from accepted\r\n" +// <- "RCPT TO:\r\n" +// -> "250 ok recipient accepted\r\n" +// <- "DATA\r\n" +// -> "354 ok, go ahead:\r\n" +// -> + "\r\n.\r\n" +// -> "250 message accepted for delivery\r\n" +// <- "QUIT\r\n" +// -> "221 catherine.cyan.com\r\n" +//=========================================================================== +static const char * s_waitStrNoAuth[] = { + "220 ", // kMailStepConnect + nil, // kMailStepAuth + "250 ", // kMailStepSender + "250 ", // kMailStepRecipient + "250 ", // kMailStepStartLetter + "354 ", // kMailStepLetter + "250 ", // kMailStepQuit + "221 ", // kMailStepDisconnect +}; + + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +static bool AdvanceStep ( + MailTransaction * transaction, + AsyncSocket sock +) { + switch (transaction->step) { + case kMailStepConnect: { + const char * host = transaction->sender; + const char * separator = StrChr(host, '@'); + if (separator) + host = separator + 1; + Send(sock, "helo ", host, "\r\n", nil); + transaction->step = kMailStepSender; + + if (transaction->auth[0]) + transaction->step = kMailStepAuth; + } + break; + + case kMailStepAuth: + Send(sock, "AUTH PLAIN ", transaction->auth, "\r\n", nil); + transaction->step = kMailStepSender; + break; + + case kMailStepSender: + Send(sock, "mail from:<", transaction->sender, ">\r\n", nil); + transaction->step = kMailStepRecipient; + break; + + case kMailStepRecipient: { + const char * start = transaction->recipient + transaction->subStep; + while (*start == ' ') + ++start; + const char * term = StrChr(start, ';'); + if (term) { + char * buffer = ALLOCA(char, term + 1 - start); + StrCopy(buffer, start, term + 1 - start); + Send(sock, "rcpt to:<", buffer, ">\r\n", nil); + transaction->subStep = term + 1 - transaction->recipient; + } + else { + Send(sock, "rcpt to:<", start, ">\r\n", nil); + transaction->step = kMailStepStartLetter; + transaction->subStep = 0; + } + } + break; + + case kMailStepStartLetter: + Send(sock, "data\r\n", nil); + transaction->step = kMailStepLetter; + break; + + case kMailStepLetter: + if (transaction->replyTo[0]) { + Send( + sock, + "Reply-to: ", + transaction->replyTo, + "\r\n", + nil + ); + } + if (transaction->bodyEncoding) { + Send( + sock, + "From: ", + transaction->sender, + "\r\nTo: ", + transaction->recipient, + "\r\nSubject: ", + transaction->subject, + "\r\nContent-Type: text/plain; charset=", + transaction->bodyEncoding, + "\r\n\r\n", + transaction->body, + "\r\n.\r\n", + nil + ); + } + else { + Send( + sock, + "From: ", + transaction->sender, + "\r\nTo: ", + transaction->recipient, + "\r\nSubject: ", + transaction->subject, + "\r\n\r\n", + transaction->body, + "\r\n.\r\n", + nil + ); + } + transaction->step = kMailStepQuit; + break; + + case kMailStepQuit: + Send(sock, "quit\r\n", nil); + transaction->step = kMailStepDisconnect; + break; + + case kMailStepDisconnect: + return false; + + DEFAULT_FATAL(transaction->step); + } + + return true; +} + +//=========================================================================== +static bool NotifyRead ( + AsyncSocket sock, + AsyncNotifySocketRead * read, + MailTransaction * transaction +) { + + // Parse available lines looking for an acknowledgement of last command + const char * compareStr = transaction->stepTable[transaction->step]; + unsigned compareLen = StrLen(compareStr); + unsigned offset = 0; + for (;;) { + const char * source = (const char *)&read->buffer[offset]; + + // Search for an end-of-line marker + const char * eol = StrChr(source, '\n', read->bytes - offset); + if (!eol) + break; + offset += (eol + 1 - source); + + // Search for an acknowledgement + if (!StrCmp(source, compareStr, compareLen)) { + read->bytesProcessed = offset; + return AdvanceStep(transaction, sock); + } + + // An error was received; log it and bail + LogMsg( + kLogError, + "Mail: step %u error '%.*s'", + transaction->step, + eol - source - 1, + source + ); + transaction->error = kMailErrServerError; + return false; + } + + // We did not find an acknowledgement, so skip past fully-received lines + // and wait for more data to arrive + read->bytesProcessed = offset; + return true; + +} + +//=========================================================================== +static void DestroyTransaction (MailTransaction * transaction) { + + // Remove transaction from list so that it can + // be safely deleted outside the critical section + s_critsect.Enter(); + { + s_mail.Unlink(transaction); + } + s_critsect.Leave(); + + // Perform callback if requested + if (transaction->callback) { + transaction->callback( + transaction->param, + transaction->error + ); + } + + DEL(transaction); +} + +//=========================================================================== +static void MailLookupProc ( + void * param, + const wchar * , + unsigned addrCount, + const NetAddress addrs[] +) { + + // If no address was found, cancel the transaction + MailTransaction * transaction = (MailTransaction *) param; + if (!addrCount) { + LogMsg(kLogError,"Mail: failed to resolve %s", transaction->smtp); + transaction->error = kMailErrDnsFailed; + DestroyTransaction(transaction); + return; + } + + // Initiate a connection + AsyncCancelId cancelId; + AsyncSocketConnect( + &cancelId, + addrs[0], + MailNotifyProc, + transaction + ); + +} + +//=========================================================================== +static bool MailNotifyProc ( + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +) { + switch (code) { + case kNotifySocketConnectFailed: { + MailTransaction * transaction = (MailTransaction *)notify->param; + LogMsg(kLogError, "Mail: unable to connect to %s", transaction->smtp); + transaction->error = kMailErrConnectFailed; + DestroyTransaction(transaction); + } + break; + + case kNotifySocketConnectSuccess: { + MailTransaction * transaction = (MailTransaction *) notify->param; + *userState = notify->param; + transaction->sock = sock; + } + break; + + case kNotifySocketRead: { + MailTransaction * transaction = (MailTransaction *) *userState; + return NotifyRead(sock, (AsyncNotifySocketRead *) notify, transaction); + } + break; + + case kNotifySocketDisconnect: { + MailTransaction * transaction = (MailTransaction *) *userState; + if ((transaction->step != kMailStepDisconnect) && !transaction->error) { + transaction->error = kMailErrDisconnected; + LogMsg(kLogError, "Mail: unexpected disconnection from %s", transaction->smtp); + } + DestroyTransaction(transaction); + AsyncSocketDelete(sock); + } + break; + } + + return !s_shutdown; +} + +//=========================================================================== +static void __cdecl Send ( + AsyncSocket sock, + const char str[], + ... +) { + // Count bytes + unsigned bytes = 1; + { + va_list argList; + va_start(argList, str); + for (const char * source = str; source; source = va_arg(argList, const char *)) + bytes += StrLen(source); + va_end(argList); + } + + // Allocate string buffer + char * packed; + const unsigned kStackBufSize = 8 * 1024; + if (bytes > kStackBufSize) + packed = (char *) ALLOC(bytes); + else + packed = (char *) _alloca(bytes); + + // Pack the string + { + va_list argList; + va_start(argList, str); + char * dest = packed; + for (const char * source = str; source; source = va_arg(argList, const char *)) + dest += StrCopyLen(dest, source, packed + bytes - dest); + va_end(argList); + } + + // Send the string + AsyncSocketSend(sock, packed, bytes - 1); + + // Free the string + if (bytes > kStackBufSize) + FREE(packed); +} + +//=========================================================================== +static void IMail ( + const char * stepTable[], + const char smtp[], + const char sender[], + const char recipient[], + const char replyTo[], + const char subject[], + const char body[], + const char auth[], + EMailEncoding bodyEncoding, + FMailResult callback, + void * param +) { + static const char * utf8 = "\"utf-8\""; + const char * encodingStr; + + if (bodyEncoding == kMailEncodeUtf8) + encodingStr = utf8; + else + encodingStr = ""; + + // Calculate string lengths + unsigned lenSmtp = StrLen(smtp); + unsigned lenSender = StrLen(sender); + unsigned lenRecipient = StrLen(recipient); + unsigned lenReplyTo = StrLen(replyTo); + unsigned lenSubject = StrLen(subject); + unsigned lenBodyEncoding = StrLen(encodingStr); + unsigned lenBody = StrLen(body); + unsigned lenAuth = StrLen(auth); + + // Calculate the required buffer size for all strings + unsigned bytes = ( + lenSmtp + 1 + + lenSender + 1 + + lenRecipient + 1 + + lenReplyTo + 1 + + lenSubject + 1 + + lenBody + 1 + + lenAuth + 1 + ) * sizeof(char); + + if (lenBodyEncoding) + bytes += (lenBodyEncoding + 1) * sizeof(char); + + // Create a transaction record + MailTransaction * transaction = new( + ALLOC(offsetof(MailTransaction, buffer) + bytes) + ) MailTransaction; + transaction->stepTable = stepTable; + transaction->sock = nil; + transaction->step = kMailStepConnect; + transaction->subStep = 0; + transaction->error = kMailSuccess; + transaction->callback = callback; + transaction->param = param; + + unsigned offset = 0; + transaction->smtp = transaction->buffer + offset; + offset += StrCopyLen(transaction->smtp, smtp, lenSmtp + 1) + 1; + transaction->sender = transaction->buffer + offset; + offset += StrCopyLen(transaction->sender, sender, lenSender + 1) + 1; + transaction->recipient = transaction->buffer + offset; + offset += StrCopyLen(transaction->recipient, recipient, lenRecipient + 1) + 1; + transaction->replyTo = transaction->buffer + offset; + offset += StrCopyLen(transaction->replyTo, replyTo, lenReplyTo + 1) + 1; + transaction->subject = transaction->buffer + offset; + offset += StrCopyLen(transaction->subject, subject, lenSubject + 1) + 1; + if (lenBodyEncoding) { + transaction->bodyEncoding = transaction->buffer + offset; + offset += StrCopyLen(transaction->bodyEncoding, encodingStr, lenBodyEncoding + 1) + 1; + } + else { + transaction->bodyEncoding = nil; + } + transaction->body = transaction->buffer + offset; + offset += StrCopyLen(transaction->body, body, lenBody + 1) + 1; + transaction->auth = transaction->buffer + offset; + offset += StrCopyLen(transaction->auth, auth, lenAuth + 1) + 1; + ASSERT(offset == bytes); + + // Start the transaction with a dns lookup + const unsigned kSmtpPort = 25; + wchar smtpName[256]; + StrToUnicode(smtpName, smtp, arrsize(smtpName)); + + // Add transaction to global list + bool shutdown; + s_critsect.Enter(); + { + shutdown = s_shutdown; + s_mail.Link(transaction); + } + s_critsect.Leave(); + + if (shutdown) { + DestroyTransaction(transaction); + } + else { + NetAddress addr; + if (NetAddressFromString(&addr, smtpName, kSmtpPort)) { + AsyncCancelId cancelId; + AsyncSocketConnect( + &cancelId, + addr, + MailNotifyProc, + transaction + ); + } + else { + AsyncCancelId cancelId; + AsyncAddressLookupName( + &cancelId, + MailLookupProc, + smtpName, + kSmtpPort, + transaction + ); + } + } +} + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +void MailEncodePassword ( + const char username[], + const char password[], + ARRAY(char) * emailAuth +) { + // base64_encode("\0#{user}\0#{secret}") + emailAuth->Reserve(512); + emailAuth->Push(0); + emailAuth->Add(username, StrBytes(username)); + emailAuth->Add(password, StrBytes(password)); + unsigned srcChars = emailAuth->Bytes(); + + // Allocate space for encoded data + unsigned dstChars = Base64EncodeSize(srcChars); + char * dstData = emailAuth->New(dstChars); + + // Encode data and move it back to the front of the array + dstChars = Base64Encode( + srcChars, + (const byte *) emailAuth->Ptr(), + dstChars, + dstData + ); + emailAuth->Move( + 0, + srcChars, + dstChars + ); + emailAuth->SetCountFewer(dstChars); +} + +//=========================================================================== +void Mail ( + const char smtp[], + const char sender[], + const char recipient[], // multiple recipients separated by semicolons + const char subject[], + const char body[], + const char username[], + const char password[], + const char replyTo[], + EMailEncoding bodyEncoding, + FMailResult callback, + void * param +) { + s_shutdown = false; + + // Get email authorization + const char * auth; + const char ** stepTable; + ARRAY(char) authBuffer; + if (!password || !*password) { + // No password is specified, use non-authenticated email + auth = ""; + stepTable = s_waitStrNoAuth; + } + else if (!username || !*username) { + // No username specified, user is providing the base64 encoded secret + auth = password; + stepTable = s_waitStrAuth; + } + else { + MailEncodePassword(username, password, &authBuffer); + auth = authBuffer.Ptr(); + stepTable = s_waitStrAuth; + } + + IMail( + stepTable, + smtp, + sender, + recipient, + replyTo ? replyTo : "", + subject, + body, + auth, + bodyEncoding, + callback, + param + ); +} + +//=========================================================================== +void MailStop () { + s_critsect.Enter(); + { + s_shutdown = true; + MailTransaction * transaction = s_mail.Head(); + for (; transaction; transaction = s_mail.Next(transaction)) { + if (transaction->sock) + AsyncSocketDisconnect(transaction->sock, true); + transaction->error = kMailErrClientCanceled; + } + } + s_critsect.Leave(); + + AsyncAddressLookupCancel( + MailLookupProc, + 0 // cancel all + ); + AsyncSocketConnectCancel( + MailNotifyProc, + 0 // cancel all + ); +} + +//=========================================================================== +bool MailQueued () { + bool queued; + s_critsect.Enter(); + queued = s_mail.Head() != nil; + s_critsect.Leave(); + return queued; +} + +//============================================================================ +const wchar * MailErrorToString (EMailError error) { + + switch (error) { + case kMailSuccess: return L"kMailSuccess"; + case kMailErrDnsFailed: return L"kMailErrDnsFailed"; + case kMailErrConnectFailed: return L"kMailErrConnectFailed"; + case kMailErrDisconnected: return L"kMailErrDisconnected"; + case kMailErrClientCanceled: return L"kMailErrClientCanceled"; + case kMailErrServerError: return L"kMailErrServerError"; + DEFAULT_FATAL(error); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/pnMail.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/pnMail.h new file mode 100644 index 00000000..f5538e21 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMail/pnMail.h @@ -0,0 +1,96 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnMail/pnMail.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNMAIL_PNMAIL_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNMAIL_PNMAIL_H + + +/***************************************************************************** +* +* Mail API +* +***/ + +enum EMailEncoding { + kMailEncodeNone, + kMailEncodeAscii, + kMailEncodeUtf8, +}; + +enum EMailError { + kMailSuccess, + kMailErrDnsFailed, + kMailErrConnectFailed, + kMailErrDisconnected, + kMailErrClientCanceled, + kMailErrServerError, +}; + + +typedef void (* FMailResult)(void * param, EMailError result); + + +void MailStop (); +bool MailQueued (); + +const wchar * MailErrorToString (EMailError error); + + +//============================================================================ +// AUTHENTICATION NOTES +// username, password +// NULL NULL --> no authentication +// NULL string --> username/password assumed to be pre-encoded with MailEncodePassword +// string string --> username/password contain appropriate values for snmp server +//============================================================================ + +void Mail ( + const char smtp[], + const char sender[], + const char recipients[], // separate multiple recipients with semicolons + const char subject[], + const char body[], + const char username[], // optional (see auth notes above) + const char password[], // optional (see auth notes above) + const char replyTo[], // optional + EMailEncoding encoding, + FMailResult callback, // optional + void * param // optional +); + +void MailEncodePassword ( + const char username[], + const char password[], + ARRAY(char) * emailAuth +); + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNMAIL_PNMAIL_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plAttachMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plAttachMsg.h new file mode 100644 index 00000000..e00ddc21 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plAttachMsg.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAttachMsg_inc +#define plAttachMsg_inc + +#include "plRefMsg.h" +#include "hsStream.h" + +class hsResMgr; + +class plAttachMsg : public plRefMsg +{ +public: + plAttachMsg() {} + // child is the plSceneObject being added as a child to the receiver. If rcv is not loaded, this will have no effect. + // flags should be either: + // plRefMsg::kOnRequest - I'm adding this child to the receiver + // plRefMsg::kOnRemove - I'm detaching this child from the receiver + plAttachMsg(const plKey &rcv, hsKeyedObject* child, UInt8 context, const plKey snd=nil) : plRefMsg(rcv, context) { SetSender(snd); SetRef(child); } + + CLASSNAME_REGISTER( plAttachMsg ); + GETINTERFACE_ANY( plAttachMsg, plRefMsg ); + +}; + +#endif // plAttachMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plAudioSysMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plAudioSysMsg.h new file mode 100644 index 00000000..1e0fe106 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plAudioSysMsg.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAudioSysMsg_inc +#define plAudioSysMsg_inc + +#include "plMessage.h" +#include "hsStream.h" +#include "hsResMgr.h" + +class plKey; + +class plAudioSysMsg : public plMessage +{ + int fAudFlag; + plKey pObj; + hsBool fBoolFlag; +public: + enum + { + kActivate = 0, + kDeActivate, + kDestroy, + kInitialize, + kPing, + kSetVol, + kMuteAll, + kUnmuteAll, + kRequestMuteState, + kChannelVolChanged, + kRegisteringSound, + kForceListUpdate + }; + + + plAudioSysMsg() : pObj(nil){SetBCastFlag(plMessage::kBCastByExactType);} + plAudioSysMsg(const plKey &s) : pObj(nil){SetBCastFlag(plMessage::kBCastByExactType);SetSender(s);} + plAudioSysMsg(int i) : pObj(nil){fAudFlag = i; SetBCastFlag(plMessage::kBCastByExactType );} + plAudioSysMsg(const plKey &s, + const plKey &r, + const double* t) : pObj(nil){SetBCastFlag(plMessage::kBCastByExactType);} + ~plAudioSysMsg(){;} + + CLASSNAME_REGISTER( plAudioSysMsg ); + GETINTERFACE_ANY( plAudioSysMsg, plMessage ); + + int GetAudFlag() { return fAudFlag; } + plKey GetSceneObject() { return pObj; } + void SetSceneObject(plKey &k) { pObj = k; } + + hsBool GetBoolFlag() { return fBoolFlag; } + void SetBoolFlag( hsBool b ) { fBoolFlag = b; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + stream->WriteSwap(fAudFlag); + mgr->WriteKey(stream, pObj); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->ReadSwap(&fAudFlag); + pObj = mgr->ReadKey(stream); + } +}; + +#endif // plAudioSysMsg diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCameraMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCameraMsg.cpp new file mode 100644 index 00000000..372f4b1f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCameraMsg.cpp @@ -0,0 +1,157 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plCameraMsg.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plKey.h" + + +// +// +// camera modifier message implementation +// +// + +plCameraMsg::plCameraMsg() : +fNewCam(nil), +fTriggerer(nil), +fTransTime(0), +fSubject(nil), +fPipe(nil), +fConfig(nil), +fActivated(false) +{ +} + +plCameraMsg::plCameraMsg(const plKey &s, const plKey &r, const double* t) : +fNewCam(nil), +fTriggerer(nil), +fTransTime(0), +fSubject(nil), +fPipe(nil), +fConfig(nil), +fActivated(false), +plMessage(s, r, t) +{ +} + +// IO +void plCameraMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + fCmd.Read(stream); + fTransTime = stream->ReadSwapDouble(); + fActivated = stream->ReadBool(); + fNewCam = mgr->ReadKey(stream); + fTriggerer = mgr->ReadKey(stream); + fConfig.Read(stream); +} + +void plCameraMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + fCmd.Write(stream); + stream->WriteSwapDouble(fTransTime); + stream->WriteBool(fActivated); + mgr->WriteKey(stream, fNewCam); + mgr->WriteKey(stream, fTriggerer); + fConfig.Write(stream); +} + +void plCameraConfig::Read(hsStream* stream) +{ + fAccel = stream->ReadSwapFloat(); + fDecel = stream->ReadSwapFloat(); + fVel = stream->ReadSwapFloat(); + fFPAccel = stream->ReadSwapFloat(); + fFPDecel = stream->ReadSwapFloat(); + fFPVel = stream->ReadSwapFloat(); + fFOVw = stream->ReadSwapFloat(); + fFOVh = stream->ReadSwapFloat(); + fOffset.fX = stream->ReadSwapFloat(); + fOffset.fY = stream->ReadSwapFloat(); + fOffset.fZ = stream->ReadSwapFloat(); + fWorldspace = stream->ReadBool(); +} + +void plCameraConfig::Write(hsStream* stream) +{ + stream->WriteSwapFloat(fAccel); + stream->WriteSwapFloat(fDecel); + stream->WriteSwapFloat(fVel); + stream->WriteSwapFloat(fFPAccel); + stream->WriteSwapFloat(fFPDecel); + stream->WriteSwapFloat(fFPVel); + stream->WriteSwapFloat(fFOVw); + stream->WriteSwapFloat(fFOVh); + stream->WriteSwapFloat(fOffset.fX); + stream->WriteSwapFloat(fOffset.fY); + stream->WriteSwapFloat(fOffset.fZ); + stream->WriteBool(fWorldspace); +} + + + + + + + +// IO +void plCameraTargetFadeMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + fSubject = mgr->ReadKey(stream); + fFadeOut = stream->ReadBool(); +} + +void plCameraTargetFadeMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + mgr->WriteKey(stream, fSubject); + stream->WriteBool(fFadeOut); +} + +// IO +void plIfaceFadeAvatarMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + fSubject = mgr->ReadKey(stream); + fFadeOut = stream->ReadBool(); + fEnable = stream->ReadBool(); + fDisable = stream->ReadBool(); +} + +void plIfaceFadeAvatarMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + mgr->WriteKey(stream, fSubject); + stream->WriteBool(fFadeOut); + stream->WriteBool(fEnable); + stream->WriteBool(fDisable); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCameraMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCameraMsg.h new file mode 100644 index 00000000..3314b6a0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCameraMsg.h @@ -0,0 +1,245 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCameraMsg_inc +#define plCameraMsg_inc + +// +// camera message class +// +#include "../pnMessage/plMessage.h" +#include "hsBitVector.h" +#include "hsGeometry3.h" + +class plSceneObject; +class plPipeline; +class hsStream; +class hsResMgr; + +class plCameraConfig +{ +public: + + plCameraConfig() : + fAccel(0),fDecel(0), + fVel(0),fFPAccel(0), + fFPDecel(0),fFPVel(0), + fFOVw(0),fFOVh(0),fType(0),fWorldspace(false){fOffset.Set(0,0,0);} + + plCameraConfig(int flags) : + fAccel(0),fDecel(0), + fVel(0),fFPAccel(0), + fFPDecel(0),fFPVel(0), + fFOVw(0),fFOVh(0),fType(0),fWorldspace(false) { fType |= flags;fOffset.Set(0,0,0);} + + + enum + { + kOffset = 0x0001, + kSpeeds = 0x0002, + kFOV = 0x0004, + }; + + int fType; + + hsPoint3 fOffset; + hsScalar fAccel; + hsScalar fDecel; + hsScalar fVel; + hsScalar fFPAccel; + hsScalar fFPDecel; + hsScalar fFPVel; + hsScalar fFOVw, fFOVh; + hsBool fWorldspace; + + void Read(hsStream* stream); + void Write(hsStream* stream); + +}; + +class plCameraTargetFadeMsg : public plMessage +{ +protected: + + plKey fSubject; + hsBool fFadeOut; + +public: + + plKey GetSubjectKey() { return fSubject; } + + void SetSubjectKey(const plKey &x) { fSubject = x; } + hsBool FadeOut() { return fFadeOut; } + void SetFadeOut(hsBool b) { fFadeOut = b; } + + + plCameraTargetFadeMsg(){;} + plCameraTargetFadeMsg(const plKey &s, + const plKey &r, + const double* t){;} + + CLASSNAME_REGISTER( plCameraTargetFadeMsg ); + GETINTERFACE_ANY( plCameraTargetFadeMsg, plMessage ); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + +}; + +class plCameraMsg : public plMessage +{ +protected: + + plKey fNewCam; + plKey fTriggerer; + double fTransTime; + plSceneObject* fSubject; + plPipeline* fPipe; + plCameraConfig fConfig; + + hsBool fActivated; + +public: + + plKey GetNewCam() { return fNewCam; } + plKey GetTriggerer() { return fTriggerer; } + double GetTransTime() { return fTransTime; } + plSceneObject* GetSubject() { return fSubject; } + plPipeline* GetPipeline() { return fPipe; } + hsBool GetActivated() { return fActivated; } + plCameraConfig* GetConfig() { return &fConfig; } + + void SetNewCam(const plKey &x) { fNewCam = x; } + void SetTriggerer(const plKey &x) { fTriggerer = x; } + void SetTransTime(double x) { fTransTime = x; } + void SetSubject(plSceneObject* x) { fSubject = x; } + void SetPipeline(plPipeline* x) { fPipe = x; } + void SetActivated(hsBool x) { fActivated = x; } + + plCameraMsg(); + plCameraMsg(const plKey &s, + const plKey &r, + const double* t); + + CLASSNAME_REGISTER( plCameraMsg ); + GETINTERFACE_ANY( plCameraMsg, plMessage ); + + enum ModCmds + { + kSetSubject = 0, + kCameraMod, + kSetAsPrimary, + kTransitionTo, + kPush, + kPop, +// kSetOffset, +// kRegionOffset, + kEntering, +// kSetFirstPerson, +// kRegionFirstPerson, +// kRegionPush, + kCut, +// kModDestroy, + kResetOnEnter, + kResetOnExit, + kChangeParams, + kWorldspace, + kCreateNewDefaultCam, + kRegionPushCamera, + kRegionPopCamera, + kRegionPushPOA, + kRegionPopPOA, + kFollowLocalPlayer, + kResponderTrigger, + kSetFOV, + kAddFOVKeyframe, + kStartZoomIn, + kStartZoomOut, + kStopZoom, + kSetAnimated, + kPythonOverridePush, + kPythonOverridePop, + kPythonOverridePushCut, + kPythonSetFirstPersonOverrideEnable, + kPythonUndoFirstPerson, + kUpdateCameras, + kResponderSetThirdPerson, + kResponderUndoThirdPerson, + kNonPhysOn, + kNonPhysOff, + kResetPanning, + kNumCmds + }; + + hsBitVector fCmd; + + hsBool Cmd(int n) { return fCmd.IsBitSet(n); } + void SetCmd(int n) { fCmd.SetBit(n); } + void ClearCmd() { fCmd.Clear(); } + void ClearCmd(int n) { fCmd.ClearBit(n); } + + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + +}; + + +class plIfaceFadeAvatarMsg : public plMessage +{ +protected: + + plKey fSubject; + hsBool fFadeOut; + hsBool fEnable, fDisable; + +public: + + plKey GetSubjectKey() { return fSubject; } + + void SetSubjectKey(const plKey &x) { fSubject = x; } + hsBool FadeOut() { return fFadeOut; } + void SetFadeOut(hsBool b) { fFadeOut = b; } + void Enable() { fEnable = true; } + void Disable() { fDisable = true; } + hsBool GetEnable() { return fEnable; } + hsBool GetDisable() { return fDisable; } + + plIfaceFadeAvatarMsg() : fEnable(false),fDisable(false){;} + plIfaceFadeAvatarMsg(const plKey &s, + const plKey &r, + const double* t): fEnable(false),fDisable(false){;} + + CLASSNAME_REGISTER( plIfaceFadeAvatarMsg ); + GETINTERFACE_ANY( plIfaceFadeAvatarMsg, plMessage ); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + +}; + +#endif // plCameraMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.cpp new file mode 100644 index 00000000..8698fc2f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.cpp @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plClientMsg.h" + +void plClientMsg::IReset() +{ + fMsgFlag = 0; + fAgeName = nil; +} + +void plClientMsg::AddRoomLoc(plLocation loc) +{ + if (loc.IsValid()) + fRoomLocs.push_back(loc); + else + hsStatusMessage("Trying to load an invalid room, ignoring"); +} + +void plClientMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + hsAssert(0, "Shouldn't read a plClientMsg"); +} + +void plClientMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + hsAssert(0, "Shouldn't write a plClientMsg"); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.h new file mode 100644 index 00000000..9a3c276c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plClientMsg.h @@ -0,0 +1,153 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plClientMsg_inc +#define plClientMsg_inc + +#include "../pnMessage/plMessage.h" +#include "../pnMessage/plRefMsg.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "hsUtils.h" +#include "hsStlUtils.h" +#include "../pnKeyedObject/plUoid.h" + +// +// Handles various types of client (app) msgs, relating +// to loading rooms, players, camera, and progress bars +// +class plClientMsg : public plMessage +{ + int fMsgFlag; + char* fAgeName; + std::vector fRoomLocs; + + void IReset(); + + class GraphicsSettings + { + public: + GraphicsSettings() : fWidth (800), fHeight(600), fColorDepth(32), fWindowed(false), fNumAASamples(0), + fMaxAnisoSamples(0), fVSync(false) {} + int fWidth; + int fHeight; + int fColorDepth; + hsBool fWindowed; + int fNumAASamples; + int fMaxAnisoSamples; + hsBool fVSync; + }; + + +public: + enum + { + kLoadRoom, + kLoadRoomHold, + kUnloadRoom, + kLoadNextRoom, // For internal client use only + + kLoadAgeKeys, + kReleaseAgeKeys, + + kQuit, // exit the app + kInitComplete, + kDisableRenderScene, + kEnableRenderScene, + kResetGraphicsDevice, + kSetGraphicsDefaults, + }; + + // graphics settings fields + GraphicsSettings fGraphicsSettings; + + + plClientMsg() { IReset();} + plClientMsg(const plKey &s) { IReset();} + plClientMsg(int i) { IReset(); fMsgFlag = i; } + plClientMsg(const plKey &s, const plKey &r, const double* t) { IReset(); } + ~plClientMsg() { delete [] fAgeName; } + + CLASSNAME_REGISTER(plClientMsg); + GETINTERFACE_ANY(plClientMsg, plMessage); + + int GetClientMsgFlag() const { return fMsgFlag; } + + void AddRoomLoc(plLocation loc); + + // Used for kLoadAgeKeys, kLetGoOfAgeKeys only + const char* GetAgeName() const { return fAgeName; } + void SetAgeName(const char* age) { delete [] fAgeName; fAgeName = hsStrcpy(age); } + + int GetNumRoomLocs() { return fRoomLocs.size(); } + const plLocation& GetRoomLoc(int i) const { return fRoomLocs[i]; } + const std::vector& GetRoomLocs() { return fRoomLocs; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +class plClientRefMsg : public plRefMsg +{ + +public: + enum + { + kLoadRoom = 0, + kLoadRoomHold, + kManualRoom, + }; + + plClientRefMsg(): fType(-1), fWhich(-1) {}; + + plClientRefMsg(const plKey &r, UInt8 refMsgFlags, Int8 which , Int8 type) + : plRefMsg(r, refMsgFlags), fType(type), fWhich(which) {} + + + CLASSNAME_REGISTER( plClientRefMsg ); + GETINTERFACE_ANY( plClientRefMsg, plRefMsg ); + + Int8 fType; + Int8 fWhich; + + // IO - not really applicable to ref msgs, but anyway + void Read(hsStream* stream, hsResMgr* mgr) + { + plRefMsg::Read(stream, mgr); + stream->ReadSwap(&fType); + stream->ReadSwap(&fWhich); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plRefMsg::Write(stream, mgr); + stream->WriteSwap(fType); + stream->WriteSwap(fWhich); + } +}; + + +#endif // plClientMsg diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCmdIfaceModMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCmdIfaceModMsg.h new file mode 100644 index 00000000..5c4ec9ee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCmdIfaceModMsg.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCmdIfaceModMsg_inc +#define plCmdIfaceModMsg_inc + +#include "plMessage.h" +#include "hsBitVector.h" +#include "hsResMgr.h" +#include "hsStream.h" + + +class plControlConfig; + +class plCmdIfaceModMsg : public plMessage +{ +protected: + +public: + plCmdIfaceModMsg() : fInterface(nil), fIndex(0), fControlCode(0){SetBCastFlag(plMessage::kBCastByExactType);} + plCmdIfaceModMsg(const plKey* s, + const plKey* r, + const double* t) : fInterface(nil){;} + + CLASSNAME_REGISTER( plCmdIfaceModMsg ); + GETINTERFACE_ANY( plCmdIfaceModMsg, plMessage ); + + enum + { + kAdd = 0, + kRemove, + kPushInterface, + kPopInterface, + kIndexCallback, + kDisableMouseControls, + kEnableMouseControls, + kDisableControlCode, + kEnableControlCode, + kNumCmds + }; + + hsBitVector fCmd; + plControlConfig* fInterface; + UInt32 fControlCode; + int fIndex; + + hsBool Cmd(int n) { return fCmd.IsBitSet(n); } + void SetCmd(int n) { fCmd.SetBit(n); } + void ClearCmd() { fCmd.Clear(); } + void ClearCmd(int n) { fCmd.ClearBit(n); } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + } + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + } + +}; + +#endif // plCmdIfaceModMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCollisionMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCollisionMsg.h new file mode 100644 index 00000000..973e45ff --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCollisionMsg.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCollisionMsg_inc +#define plCollisionMsg_inc + +#include "plMessage.h" + +class hsStream; +class hsResMgr; + + +class plCollisionMsg : public plMessage +{ +public: + plCollisionMsg() : plMessage() {} + plCollisionMsg(const plKey* s, const plKey* r); + + CLASSNAME_REGISTER( plCollisionMsg ); + GETINTERFACE_ANY( plCollisionMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); } +}; + +#endif // plCollisionMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCorrectionMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCorrectionMsg.h new file mode 100644 index 00000000..aba27317 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCorrectionMsg.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCorrectionMsg_inc +#define plCorrectionMsg_inc + +#include "plMessage.h" +#include "hsMatrix44.h" + +class plCorrectionMsg : public plMessage +{ +public: + plCorrectionMsg() : plMessage(nil, nil, nil) { } + plCorrectionMsg(plKey &r, const hsMatrix44& l2w, const hsMatrix44& w2l, hsBool dirtySynch = false) + : plMessage(nil, r, nil), + fLocalToWorld(l2w), + fWorldToLocal(w2l), + fDirtySynch(dirtySynch) + { } + + CLASSNAME_REGISTER( plCorrectionMsg ); + GETINTERFACE_ANY( plCorrectionMsg, plMessage ); + + hsMatrix44 fLocalToWorld; + hsMatrix44 fWorldToLocal; + + hsBool fDirtySynch; + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fLocalToWorld.Read(stream); + fWorldToLocal.Read(stream); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + fLocalToWorld.Write(stream); + fWorldToLocal.Write(stream); + } + + +}; + +#endif // plCorrectionMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCursorChangeMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCursorChangeMsg.h new file mode 100644 index 00000000..bcb7f546 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plCursorChangeMsg.h @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCursorChangeMsg_inc +#define plCursorChangeMsg_inc + +// +// this message is to fake out a gadget to see if it would potentially trigger... +// +#include "../pnMessage/plMessage.h" +#include "hsBitVector.h" + +class hsStream; +class hsResMgr; + +class plCursorChangeMsg : public plMessage +{ +protected: + +public: + plCursorChangeMsg() : fType(0),fPriority(0){;} + plCursorChangeMsg(int i, int p) { fType = i;fPriority =p; } + plCursorChangeMsg(const plKey &s, + const plKey &r, + const double* t) : fType(0),fPriority(0){;} + + CLASSNAME_REGISTER( plCursorChangeMsg ); + GETINTERFACE_ANY( plCursorChangeMsg, plMessage ); + + enum + { + kNoChange = 0, + kCursorUp, + kCursorLeft, + kCursorRight, + kCursorDown, + kCursorPoised, + kCursorClicked, + kCursorUnClicked, + kCursorHidden, + kCursorOpen, + kCursorGrab, + kCursorArrow, + kNullCursor + }; + + int fType; + int fPriority; + + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fType = stream->ReadSwap32(); + fPriority = stream->ReadSwap32(); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwap32(fType); + stream->WriteSwap32(fPriority); + } + +}; + +#endif // plCursorChangeMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plDISpansMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plDISpansMsg.h new file mode 100644 index 00000000..f2b23114 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plDISpansMsg.h @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plDISpansMsg_inc +#define plDISpansMsg_inc + +#include "plMessage.h" +#include "hsStream.h" + +class hsKeyedObject; + +class plDISpansMsg : public plMessage +{ +public: + enum { + kAddingSpan, + kRemovingSpan + }; + UInt8 fType; + + enum { + kLeaveEmptyDrawable = 0x1 + }; + UInt8 fFlags; + + Int32 fIndex; + + plDISpansMsg() : plMessage(), fType(0), fFlags(0), fIndex(-1) {} + plDISpansMsg(const plKey &r, UInt8 type, int index, int flags) : plMessage(nil, r, nil), fType(type), fIndex(index), fFlags(flags) {} + + CLASSNAME_REGISTER( plDISpansMsg ); + GETINTERFACE_ANY( plDISpansMsg, plMessage ); + + void Read(hsStream* stream, hsResMgr* mgr) {} + void Write(hsStream* stream, hsResMgr* mgr) {} +}; + +#endif // plDISpansMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnableMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnableMsg.h new file mode 100644 index 00000000..692a6e3e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnableMsg.h @@ -0,0 +1,114 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plEnableMsg_inc +#define plEnableMsg_inc + +#include "plMessage.h" +#include "hsBitVector.h" + +class hsStream; + +class plEnableMsg : public plMessage +{ + +public: + enum + { + kDisable = 0, + kEnable, + kDrawable, + kPhysical, + kAudible, + kAll, + kByType + }; + + hsBitVector fCmd; + hsBitVector fTypes; + + hsBool Cmd(int n) const { return fCmd.IsBitSet(n); } + void SetCmd(int n) { fCmd.SetBit(n); } + void ClearCmd() { fCmd.Clear(); } + + void AddType(UInt16 t) { fTypes.SetBit(t); } + void RemoveType(UInt16 t) { fTypes.ClearBit(t); } + hsBool Type(UInt16 t) const { return fTypes.IsBitSet(t); } + const hsBitVector& Types() const { return fTypes; } + + plEnableMsg() { } + plEnableMsg(const plKey &s, int which , int type) : plMessage() + { SetCmd(which); SetCmd(type); } + + CLASSNAME_REGISTER( plEnableMsg ); + GETINTERFACE_ANY( plEnableMsg, plMessage ); + + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fCmd.Read(stream); + fTypes.Read(stream); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + fCmd.Write(stream); + fTypes.Write(stream); + } + + enum MsgContentFlags + { + kCmd, + kTypes, + }; + + void ReadVersion(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgReadVersion(stream, mgr); + hsBitVector contentFlags; + contentFlags.Read(stream); + + if (contentFlags.IsBitSet(kCmd)) + fCmd.Read(stream); + if (contentFlags.IsBitSet(kTypes)) + fTypes.Read(stream); + } + + void WriteVersion(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWriteVersion(stream, mgr); + hsBitVector contentFlags; + contentFlags.SetBit(kCmd); + contentFlags.SetBit(kTypes); + contentFlags.Write(stream); + + fCmd.Write(stream); + fTypes.Write(stream); + } +}; + +#endif // plEnableMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnvEffectMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnvEffectMsg.cpp new file mode 100644 index 00000000..eda9034e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnvEffectMsg.cpp @@ -0,0 +1,141 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plEnvEffectMsg.h" + +/* Die, die, everybody die! + + +// real-time effects (non environmental); +plAudioEffectMsg::plAudioEffectMsg() : +fEffect(0), +delayModPct(0), +feedbackPct(0), +lfOscillator(0), +feedbackDelay(0), +waveform(0), +phaseDifferential(0), +gainDB(0), +attack(0), +release(0), +compThreshhold(0), +compRatio(0), +attackPreDelay(0), +intensity(0), +effectCenter(0), +effectWidth(0), +lfCutoff(0), +leftFeedbackDelay(0), +rightFeedbackDelay(0), +swapLeftRightDelay(0), +modulationRate(0) +{ + SetBCastFlag(plMessage::kPropagateToModifiers); +} + +plAudioEffectMsg::plAudioEffectMsg(const plKey &s,const plKey &r,const double* t) : +fEffect(0), +wetDryPct(0), +delayModPct(0), +feedbackPct(0), +lfOscillator(0), +feedbackDelay(0), +waveform(0), +phaseDifferential(0), +gainDB(0), +attack(0), +release(0), +compThreshhold(0), +compRatio(0), +attackPreDelay(0), +intensity(0), +effectCenter(0), +effectWidth(0), +lfCutoff(0), +leftFeedbackDelay(0), +rightFeedbackDelay(0), +swapLeftRightDelay(0), +modulationRate(0) + +{ + SetBCastFlag(plMessage::kPropagateToModifiers); +} + +// IO +void plAudioEffectMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plEnvEffectMsg::Read(stream, mgr); + stream->ReadSwap(&fEffect); + stream->ReadSwap(&wetDryPct); + stream->ReadSwap(&delayModPct); + stream->ReadSwap(&feedbackPct); + stream->ReadSwap(&lfOscillator); + stream->ReadSwap(&feedbackDelay); + stream->ReadSwap(&waveform); + stream->ReadSwap(&phaseDifferential); + stream->ReadSwap(&gainDB); + stream->ReadSwap(&attack); + stream->ReadSwap(&release); + stream->ReadSwap(&compThreshhold); + stream->ReadSwap(&compRatio); + stream->ReadSwap(&attackPreDelay); + stream->ReadSwap(&intensity); + stream->ReadSwap(&effectCenter); + stream->ReadSwap(&effectWidth); + stream->ReadSwap(&lfCutoff); + stream->ReadSwap(&leftFeedbackDelay); + stream->ReadSwap(&rightFeedbackDelay); + stream->ReadSwap(&swapLeftRightDelay); + stream->ReadSwap(&modulationRate); +} + +void plAudioEffectMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plEnvEffectMsg::Write(stream, mgr); + stream->WriteSwap(fEffect); + stream->WriteSwap(wetDryPct); + stream->WriteSwap(delayModPct); + stream->WriteSwap(feedbackPct); + stream->WriteSwap(lfOscillator); + stream->WriteSwap(feedbackDelay); + stream->WriteSwap(waveform); + stream->WriteSwap(phaseDifferential); + stream->WriteSwap(gainDB); + stream->WriteSwap(attack); + stream->WriteSwap(release); + stream->WriteSwap(compThreshhold); + stream->WriteSwap(compRatio); + stream->WriteSwap(attackPreDelay); + stream->WriteSwap(intensity); + stream->WriteSwap(effectCenter); + stream->WriteSwap(effectWidth); + stream->WriteSwap(lfCutoff); + stream->WriteSwap(leftFeedbackDelay); + stream->WriteSwap(rightFeedbackDelay); + stream->WriteSwap(swapLeftRightDelay); + stream->WriteSwap(modulationRate); + +} +*/ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnvEffectMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnvEffectMsg.h new file mode 100644 index 00000000..ed2b0560 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEnvEffectMsg.h @@ -0,0 +1,214 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plEnvEffectMsg_inc +#define plEnvEffectMsg_inc + +/* I'm dead, hear me cry + +#include "../pnMessage/plMessage.h" +#include "hsStream.h" + +class hsResMgr; + + +class plEnvEffectMsg : public plMessage +{ + + hsBool fEnable; + hsScalar fPriority; + +public: + plEnvEffectMsg() : fPriority(1.0), fEnable( true ) { SetBCastFlag(plMessage::kPropagateToModifiers); } + + plEnvEffectMsg(const plKey &s, + const plKey &r, + const double* t) : fPriority(1.0), fEnable( true ) {SetBCastFlag(plMessage::kPropagateToModifiers);} + + ~plEnvEffectMsg(){;} + + CLASSNAME_REGISTER( plEnvEffectMsg ); + GETINTERFACE_ANY( plEnvEffectMsg, plMessage ); + + hsBool Enabled() { return fEnable; } + void Enable(hsBool b) { fEnable = b; } + void SetPriority(hsScalar p) { fPriority = p; } + hsScalar GetPriority() { return fPriority; } + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + stream->ReadSwap(&fEnable); + stream->ReadSwap(&fPriority); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwap(fEnable); + stream->WriteSwap(fPriority); + } +}; + + +class plEnvAudioEffectMsg : public plEnvEffectMsg +{ +public: + + int fPreset; + + Int32 fRoomAtten, fRoomHFAtten; + hsScalar fRoomRolloffFactor; + + hsScalar fDecayTime, fDecayHFRatio; + + Int32 fReflect; + hsScalar fReflectDelay; + + Int32 fReverb; + hsScalar fReverbDelay; + + hsScalar fDiffusion, fDensity; + hsScalar fHFReference; + + plEnvAudioEffectMsg(){SetBCastFlag(plMessage::kPropagateToModifiers);} + plEnvAudioEffectMsg(const plKey &s, + const plKey &r, + const double* t){SetBCastFlag(plMessage::kPropagateToModifiers);} + + ~plEnvAudioEffectMsg(){;} + + CLASSNAME_REGISTER( plEnvAudioEffectMsg ); + GETINTERFACE_ANY( plEnvAudioEffectMsg, plEnvEffectMsg ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plEnvEffectMsg::Read(stream, mgr); + stream->ReadSwap(&fPreset); + + stream->ReadSwap( &fRoomAtten ); + stream->ReadSwap( &fRoomHFAtten ); + stream->ReadSwap( &fRoomRolloffFactor ); + + stream->ReadSwap( &fDecayTime ); + stream->ReadSwap( &fDecayHFRatio ); + stream->ReadSwap( &fReflect ); + stream->ReadSwap( &fReflectDelay ); + stream->ReadSwap( &fReverb ); + stream->ReadSwap( &fReverbDelay ); + + stream->ReadSwap( &fDiffusion ); + stream->ReadSwap( &fDensity ); + stream->ReadSwap( &fHFReference ); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plEnvEffectMsg::Write(stream, mgr); + stream->WriteSwap(fPreset); + + stream->WriteSwap( fRoomAtten ); + stream->WriteSwap( fRoomHFAtten ); + stream->WriteSwap( fRoomRolloffFactor ); + + stream->WriteSwap( fDecayTime ); + stream->WriteSwap( fDecayHFRatio ); + stream->WriteSwap( fReflect ); + stream->WriteSwap( fReflectDelay ); + stream->WriteSwap( fReverb ); + stream->WriteSwap( fReverbDelay ); + + stream->WriteSwap( fDiffusion ); + stream->WriteSwap( fDensity ); + stream->WriteSwap( fHFReference ); + } +}; + +class plAudioEffectMsg : public plEnvEffectMsg +{ +public: + + + enum effectType + { + kChorus = 0, + kCompressor, + kDistortion, + kEcho, + kFlanger, + kGargle, + kReverb, + kIDL32, + }; + + enum waveShape + { + kTriangular = 0, + kSquare, + kSine, + }; + + int fEffect; + int wetDryPct; + int delayModPct; + int feedbackPct; + int lfOscillator; + int feedbackDelay; + int waveform; + int phaseDifferential; + int gainDB; + hsScalar attack; + int release; + int compThreshhold; + int compRatio; + hsScalar attackPreDelay; + int intensity; + int effectCenter; + int effectWidth; + int lfCutoff; + int leftFeedbackDelay; + int rightFeedbackDelay; + hsBool swapLeftRightDelay; + int modulationRate; + + + plAudioEffectMsg(); + plAudioEffectMsg(const plKey &s, + const plKey &r, + const double* t); + + ~plAudioEffectMsg(){;} + + CLASSNAME_REGISTER( plAudioEffectMsg ); + GETINTERFACE_ANY( plAudioEffectMsg, plEnvEffectMsg ); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +*/ +#endif // plEnvEffectMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEventCallbackMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEventCallbackMsg.h new file mode 100644 index 00000000..1450c5d5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plEventCallbackMsg.h @@ -0,0 +1,118 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plEventCallbackMsg_inc +#define plEventCallbackMsg_inc + +#include "hsStream.h" +#include "plMessage.h" + +enum CallbackEvent +{ + kStart = 0, + kStop, + kReverse, + kTime, + kLoop, + kBegin, + kEnd, + kEventEnd, + kSingleFrameAdjust, + kSingleFrameEval +}; + +class plEventCallbackMsg : public plMessage +{ +protected: + +public: + hsScalar fEventTime; // the time for time events + CallbackEvent fEvent; // the event + Int16 fIndex; // the index of the object we want the event to come from + // (where applicable, required for sounds) + Int16 fRepeats; // -1 for infinite repeats, 0 for one call, no repeats + Int16 fUser; // User defined data, useful for keeping track of multiple callbacks + + plEventCallbackMsg() : fEventTime(0.0f), fEvent((CallbackEvent)0), fRepeats(-1), fUser(0), fIndex(0) {;} + plEventCallbackMsg (const plKey &s, + const plKey &r, + const double* t) : + plMessage(s, r, t), + fEventTime(0.0f), fEvent((CallbackEvent)0), fRepeats(-1), fUser(0), fIndex(0) {;} + + plEventCallbackMsg(const plKey &receiver, CallbackEvent e, int idx=0, hsScalar t=0, Int16 repeats=-1, UInt16 user=0) : + plMessage(nil, receiver, nil), fEvent(e), fIndex(idx), fEventTime(t), fRepeats(repeats), fUser(user) {} + + ~plEventCallbackMsg(){;} + + CLASSNAME_REGISTER( plEventCallbackMsg ); + GETINTERFACE_ANY( plEventCallbackMsg, plMessage ); + + // IO + virtual void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fEventTime = stream->ReadSwapFloat(); + fEvent = (CallbackEvent)stream->ReadSwap16(); + fIndex = stream->ReadSwap16(); + fRepeats = stream->ReadSwap16(); + fUser = stream->ReadSwap16(); + } + virtual void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwapFloat(fEventTime); + stream->WriteSwap16((Int16)fEvent); + stream->WriteSwap16(fIndex); + stream->WriteSwap16(fRepeats); + stream->WriteSwap16(fUser); + } +}; + +// For when you want to send callbacks, but someone other than the sender/receiver +// needs to modify them along the way. +class plEventCallbackInterceptMsg : public plEventCallbackMsg +{ +protected: + plMessage *fMsg; + +public: + plMessage *GetMessage() { return fMsg; } + void SetMessage(plMessage *msg) { fMsg = msg; hsRefCnt_SafeRef(msg); } + void SendMessage() { fMsg->SendAndKeep(); } + + plEventCallbackInterceptMsg() : plEventCallbackMsg(), fMsg(nil) {} + ~plEventCallbackInterceptMsg() { hsRefCnt_SafeUnRef(fMsg); fMsg = nil; } + + CLASSNAME_REGISTER( plEventCallbackInterceptMsg ); + GETINTERFACE_ANY( plEventCallbackInterceptMsg, plEventCallbackMsg ); + + virtual void Read(hsStream *stream, hsResMgr *mgr) { plEventCallbackMsg::Read(stream, mgr); } + virtual void Write(hsStream *stream, hsResMgr *mgr) { plEventCallbackMsg::Write(stream, mgr); } +}; + + +#endif // plEventCallbackMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plFakeOutMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plFakeOutMsg.h new file mode 100644 index 00000000..811109b7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plFakeOutMsg.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plFakeOutMsg_inc +#define plFakeOutMsg_inc + +// +// this message is to fake out a gadget to see if it would potentially trigger... +// +#include "../pnMessage/plMessage.h" +#include "hsBitVector.h" + +class hsStream; +class hsResMgr; + +class plFakeOutMsg : public plMessage +{ +protected: + +public: + plFakeOutMsg(){SetBCastFlag(plMessage::kPropagateToModifiers);} + plFakeOutMsg(const plKey &s, + const plKey &r, + const double* t){SetBCastFlag(plMessage::kPropagateToModifiers);} + + CLASSNAME_REGISTER( plFakeOutMsg ); + GETINTERFACE_ANY( plFakeOutMsg, plMessage ); + + enum + { + kNumCmds = 0, + }; + + hsBitVector fCmd; + + hsBool Cmd(int n) { return fCmd.IsBitSet(n); } + void SetCmd(int n) { fCmd.SetBit(n); } + void ClearCmd() { fCmd.Clear(); } + void ClearCmd(int n) { fCmd.ClearBit(n); } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fCmd.Read(stream); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + fCmd.Write(stream); + } + +}; + +#endif // plFakeOutMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plIntRefMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plIntRefMsg.h new file mode 100644 index 00000000..7392cd7d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plIntRefMsg.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plIntRefMsg_inc +#define plIntRefMsg_inc + +#include "plRefMsg.h" +#include "hsStream.h" + +class hsResMgr; + + +class plIntRefMsg : public plRefMsg +{ +public: + enum { + kOwner = 1, + kTarget = 2, + kChild = 3, + kDrawable = 4, + kPhysical = 5, + kAudible = 6, + kChildObject = 7, + + kNumRefTypes + }; + + plIntRefMsg() : fType(-1), fWhich(-1), fIdx(-1) {} + plIntRefMsg(const plKey &r, UInt8 flags, Int32 which, Int8 type, Int8 idx=-1) : plRefMsg(r, flags), fWhich((Int16)which), fType(type), fIdx(idx) {} + + CLASSNAME_REGISTER( plIntRefMsg ); + GETINTERFACE_ANY( plIntRefMsg, plRefMsg ); + + Int8 fType; + Int8 fIdx; + Int16 fWhich; + + // IO - not really applicable to ref msgs, but anyway + void Read(hsStream* stream, hsResMgr* mgr) + { + plRefMsg::Read(stream, mgr); + stream->ReadSwap(&fType); + stream->ReadSwap(&fWhich); + stream->ReadSwap(&fIdx); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plRefMsg::Write(stream, mgr); + stream->WriteSwap(fType); + stream->WriteSwap(fWhich); + stream->WriteSwap(fIdx); + } +}; + +#endif // plIntRefMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessage.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessage.cpp new file mode 100644 index 00000000..f47c897c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessage.cpp @@ -0,0 +1,375 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#define PLMESSAGE_PRIVATE +#include "plMessage.h" +#include "hsStream.h" +#include "../pnKeyedObject/plKey.h" +#include "hsResMgr.h" +#include "hsTimer.h" +#include "hsTemplates.h" +#include "plgDispatch.h" +#include "hsBitVector.h" +#include + +plMessage::plMessage() +: fSender(nil), + fBCastFlags(kLocalPropagate), + fTimeStamp(0), + fNetRcvrPlayerIDs(nil), + dispatchBreak(false) +{ +} + +plMessage::plMessage(const plKey &s, + const plKey &r, + const double* t) +: fSender(s), + fBCastFlags(kLocalPropagate), + fNetRcvrPlayerIDs(nil), + dispatchBreak(false) +{ + if( r ) + { + fReceivers.SetCount(1); + fReceivers[0] = r; + } + fTimeStamp = t ? *t : hsTimer::GetSysSeconds(); +} + +plMessage::~plMessage() +{ + delete fNetRcvrPlayerIDs; +} + +plMessage& plMessage::SetNumReceivers(int n) { fReceivers.SetCount(n); return *this; } +UInt32 plMessage::GetNumReceivers() const { return fReceivers.GetCount(); } +const plKey& plMessage::GetReceiver(int i) const { return fReceivers[i]; } +plMessage& plMessage::RemoveReceiver(int i) { fReceivers.Remove(i); return *this; } + +plMessage& plMessage::ClearReceivers() { fReceivers.SetCount(0); return *this; } +plMessage& plMessage::AddReceiver(const plKey &r) { fReceivers.Append(r); return *this; } + +plMessage& plMessage::AddReceivers(const hsTArray& rList) +{ + int i; + for( i = 0; i < rList.GetCount(); i++ ) + AddReceiver(rList[i]); + + return *this; +} + +hsBool plMessage::Send(const plKey r, hsBool async) +{ + if( r ) + AddReceiver(r); + return plgDispatch::MsgSend(this,async); +} + +hsBool plMessage::SendAndKeep(const plKey r, hsBool async) +{ + Ref(); + return Send(r, async); +} + +void plMessage::IMsgRead(hsStream* s, hsResMgr* mgr) +{ + plCreatable::Read(s, mgr); + + fSender = mgr->ReadKey(s); + int n; + s->LogReadSwap(&n,"NumberOfReceivers"); + fReceivers.SetCount(n); + int i; + for( i = 0; i < fReceivers.GetCount(); i++ ) + fReceivers[i] = mgr->ReadKey(s); + + s->LogReadSwap(&fTimeStamp,"TimeStamp"); // read as double + s->LogReadSwap(&fBCastFlags, "BCastFlags"); +} + +void plMessage::IMsgWrite(hsStream* s, hsResMgr* mgr) +{ + plCreatable::Write(s, mgr); + + mgr->WriteKey(s,fSender); + s->WriteSwap32(fReceivers.GetCount()); + int i; + for( i = 0; i < fReceivers.GetCount(); i++ ) + mgr->WriteKey(s,fReceivers[i]); + + s->WriteSwap(fTimeStamp); // write as double + s->WriteSwap32(fBCastFlags); +} + +enum MsgFlags +{ + kMsgSender, + kMsgReceivers, + kMsgTimeStamp, + kMsgBCastFlags, +}; + +void plMessage::IMsgReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kMsgSender)) + fSender = mgr->ReadKey(s); + + if (contentFlags.IsBitSet(kMsgReceivers)) + { + int n = s->ReadSwap32(); + fReceivers.SetCount(n); + int i; + for( i = 0; i < fReceivers.GetCount(); i++ ) + fReceivers[i] = mgr->ReadKey(s); + } + + if (contentFlags.IsBitSet(kMsgTimeStamp)) + s->ReadSwap(&fTimeStamp); // read as double + + if (contentFlags.IsBitSet(kMsgBCastFlags)) + fBCastFlags = s->ReadSwap32(); +} + +void plMessage::IMsgWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kMsgSender); + contentFlags.SetBit(kMsgReceivers); + contentFlags.SetBit(kMsgTimeStamp); + contentFlags.SetBit(kMsgBCastFlags); + contentFlags.Write(s); + + // kMsgSender + mgr->WriteKey(s,fSender); + + // kMsgReceivers + s->WriteSwap32(fReceivers.GetCount()); + int i; + for( i = 0; i < fReceivers.GetCount(); i++ ) + mgr->WriteKey(s,fReceivers[i]); + + // kMsgTimeStamp + s->WriteSwap(fTimeStamp); // write as double + + // kMsgBCastFlags + s->WriteSwap32(fBCastFlags); +} + +void plMessage::AddNetReceiver( UInt32 plrID ) +{ + if ( !fNetRcvrPlayerIDs ) + fNetRcvrPlayerIDs = TRACKED_NEW std::vector; + fNetRcvrPlayerIDs->push_back( plrID ); +} + +void plMessage::AddNetReceivers( const std::vector & plrIDs ) +{ + if ( !fNetRcvrPlayerIDs ) + fNetRcvrPlayerIDs = TRACKED_NEW std::vector; + std::copy( plrIDs.begin(), plrIDs.end(), std::back_inserter( *fNetRcvrPlayerIDs ) ); +} + +///////////////////////////////////////////////////////////////// + +// STATIC +int plMsgStdStringHelper::Poke(const std::string & stringref, hsStream* stream, const UInt32 peekOptions) +{ + plMessage::plStrLen strlen; + hsAssert( stringref.length()<0xFFFF, "buf too big for plMsgStdStringHelper" ); + strlen = stringref.length(); + stream->WriteSwap(strlen); + if (strlen) + stream->Write(strlen,stringref.data()); + return stream->GetPosition(); +} + +int plMsgStdStringHelper::PokeBig(const std::string & stringref, hsStream* stream, const UInt32 peekOptions) +{ + UInt32 strlen = stringref.length(); + stream->WriteSwap(strlen); + if (strlen) + stream->Write(strlen,stringref.data()); + return stream->GetPosition(); +} + +int plMsgStdStringHelper::Poke(const char * buf, UInt32 bufsz, hsStream* stream, const UInt32 peekOptions) +{ + plMessage::plStrLen strlen; + hsAssert( bufsz<0xFFFF, "buf too big for plMsgStdStringHelper" ); + strlen = (plMessage::plStrLen)bufsz; + stream->WriteSwap(strlen); + if (strlen) + stream->Write(strlen,buf); + return stream->GetPosition(); +} + +int plMsgStdStringHelper::PokeBig(const char * buf, UInt32 bufsz, hsStream* stream, const UInt32 peekOptions) +{ + stream->WriteSwap(bufsz); + if (bufsz) + stream->Write(bufsz,buf); + return stream->GetPosition(); +} + +// STATIC +int plMsgStdStringHelper::Peek(std::string & stringref, hsStream* stream, const UInt32 peekOptions) +{ + plMessage::plStrLen strlen; + stream->LogSubStreamStart("push this"); + stream->LogReadSwap(&strlen,"StrLen"); + stringref.erase(); + if (strlen <= stream->GetSizeLeft()) + { + stringref.resize(strlen); + if (strlen){ + stream->LogRead(strlen,(void*)stringref.data(),"StdString"); + stream->LogStringString(xtl::format("Value: %s", stringref.data()).c_str()); + } + } + else + { + hsAssert( false, "plMsgStdStringHelper::Peek: overflow peeking string." ); + } + stream->LogSubStreamEnd(); + return stream->GetPosition(); +} + +int plMsgStdStringHelper::PeekBig(std::string & stringref, hsStream* stream, const UInt32 peekOptions) +{ + UInt32 bufsz; + stream->LogSubStreamStart("push this"); + stream->LogReadSwap(&bufsz,"Bufsz"); + stringref.erase(); + if (bufsz <= stream->GetSizeLeft()) + { + stringref.resize(bufsz); + if (bufsz){ + stream->LogRead(bufsz,(void*)stringref.data(),"StdString"); + stream->LogStringString(xtl::format("Value: %s", stringref.data()).c_str()); + } + } + else + { + hsAssert( false, "plMsgStdStringHelper::PeekBig: overflow peeking string." ); + } + stream->LogSubStreamEnd(); + return stream->GetPosition(); +} + +///////////////////////////////////////////////////////////////// + +// STATIC +int plMsgXtlStringHelper::Poke(const xtl::istring & stringref, hsStream* stream, const UInt32 peekOptions) +{ + plMessage::plStrLen strlen; + strlen = stringref.length(); + stream->WriteSwap(strlen); + if (strlen) + stream->Write(strlen,stringref.data()); + return stream->GetPosition(); +} + +// STATIC +int plMsgXtlStringHelper::Peek(xtl::istring & stringref, hsStream* stream, const UInt32 peekOptions) +{ + plMessage::plStrLen strlen; + stream->LogSubStreamStart("push me"); + stream->LogReadSwap(&strlen,"StrLen"); + stringref.erase(); + if (strlen <= stream->GetSizeLeft()) + { + stringref.resize(strlen); + if (strlen){ + stream->LogRead(strlen,(void*)stringref.data(),"XtlString"); + stream->LogStringString(xtl::format("Value: %s", stringref.data()).c_str()); + } + } + stream->LogSubStreamEnd(); + return stream->GetPosition(); +} + + +///////////////////////////////////////////////////////////////// + +// STATIC +int plMsgCStringHelper::Poke(const char * str, hsStream* stream, const UInt32 peekOptions) +{ + plMessage::plStrLen strlen; + strlen = (str)?hsStrlen(str):0; + stream->WriteSwap(strlen); + if (strlen) + stream->Write(strlen,str); + return stream->GetPosition(); +} + +// STATIC +int plMsgCStringHelper::Peek(char *& str, hsStream* stream, const UInt32 peekOptions) +{ + plMessage::plStrLen strlen; + stream->LogSubStreamStart("push me"); + stream->LogReadSwap(&strlen,"StrLen"); + delete [] str; + str = nil; + if (strlen <= stream->GetSizeLeft()) + { + if (strlen) + { + str = TRACKED_NEW char[strlen+1]; + str[strlen] = '\0'; + if (strlen) { + stream->LogRead(strlen,str,"CString"); + stream->LogStringString(xtl::format("Value: %s",str).c_str()); + } + } + } + stream->LogSubStreamEnd(); + return stream->GetPosition(); +} + + +///////////////////////////////////////////////////////////////// + +// STATIC +int plMsgCArrayHelper::Poke(const void * buf, UInt32 bufsz, hsStream* stream, const UInt32 peekOptions) +{ + stream->Write(bufsz,buf); + return stream->GetPosition(); +} + +// STATIC +int plMsgCArrayHelper::Peek(void * buf, UInt32 bufsz, hsStream* stream, const UInt32 peekOptions) +{ + stream->LogSubStreamStart("push me"); + stream->LogRead(bufsz,buf,"CArray"); + stream->LogSubStreamEnd(); + return stream->GetPosition(); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessage.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessage.h new file mode 100644 index 00000000..979a815a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessage.h @@ -0,0 +1,196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMessage_inc +#define plMessage_inc + +#include "../pnFactory/plCreatable.h" +#include "../pnKeyedObject/plKey.h" +#include "hsTemplates.h" +#include "hsStlUtils.h" + +class plKey; +class hsStream; + +// Base class for messages only has enough info to route it +// and send it over the wire (Read/Write). +class plMessage : public plCreatable +{ +public: + typedef UInt16 plStrLen; + + enum plBCastFlags { + kBCastNone = 0x0, + kBCastByType = 0x1, // To everyone registered for this msg type or msgs this is derived from + kBCastUNUSED_0 = 0x2, // Obsolete option (never used). Was BCastBySender + kPropagateToChildren = 0x4, // Propagate down through SceneObject heirarchy + kBCastByExactType = 0x8, // To everyone registered for this exact msg type. + kPropagateToModifiers = 0x10, // Send the msg to an object and all its modifier + kClearAfterBCast = 0x20, // Clear registration for this type after sending this msg + kNetPropagate = 0x40, // Propagate this message over the network (remotely) + kNetSent = 0x80, // Internal use-This msg has been sent over the network + kNetUseRelevanceRegions = 0x100, // Used along with NetPropagate to filter the msg bcast using relevance regions + kNetForce = 0x200, // Used along with NetPropagate to force the msg to go out (ie. ignore cascading rules) + kNetNonLocal = 0x400, // Internal use-This msg came in over the network (remote msg) + kLocalPropagate = 0x800, // Propagate this message locally (ON BY DEFAULT) + kNetNonDeterministic = kNetForce, // This msg is a non-deterministic response to another msg + kMsgWatch = 0x1000, // Debug only - will break in dispatch before sending this msg + kNetStartCascade = 0x2000, // Internal use-msg is non-local and initiates a cascade of net msgs. This bit is not inherited or computed, it's a property. + kNetAllowInterAge = 0x4000, // If rcvr is online but not in current age, they will receive msg courtesy of pls routing. + kNetSendUnreliable = 0x8000, // Don't use reliable send when net propagating + kCCRSendToAllPlayers = 0x10000, // CCRs can send a plMessage to all online players. + kNetCreatedRemotely = 0x20000, // kNetSent and kNetNonLocal are inherited by child messages sent off while processing a net-propped + // parent. This flag ONLY gets sent on the actual message that went across the wire. + + }; +private: + bool dispatchBreak; + + friend class plDispatch; + friend class plDispatchLog; + +protected: + plKey fSender; + hsTArray fReceivers; + double fTimeStamp; + + UInt32 fBCastFlags; + std::vector* fNetRcvrPlayerIDs; + + void IMsgRead(hsStream* stream, hsResMgr* mgr); // default read implementation + void IMsgWrite(hsStream* stream, hsResMgr* mgr); // default write implementation + + void IMsgReadVersion(hsStream* stream, hsResMgr* mgr); + void IMsgWriteVersion(hsStream* stream, hsResMgr* mgr); + +public: + plMessage(); + plMessage(const plKey &s, + const plKey &r, + const double* t); + + virtual ~plMessage(); + + CLASSNAME_REGISTER( plMessage ); + GETINTERFACE_ANY( plMessage, plCreatable ); + + // These must be implemented by all derived message classes (hence pure). + // Derived classes should call the base-class default read/write implementation, + // so the derived Read() should call plMessage::IMsgRead(). + virtual void Read(hsStream* stream, hsResMgr* mgr) = 0; + virtual void Write(hsStream* stream, hsResMgr* mgr) = 0; + + const plKey GetSender() const { return fSender; } + plMessage& SetSender(const plKey &s) { fSender = s; return *this; } + + plMessage& SetNumReceivers(int n); + UInt32 GetNumReceivers() const ; + const plKey& GetReceiver(int i) const; + plMessage& RemoveReceiver(int i); + + plMessage& ClearReceivers(); + plMessage& AddReceiver(const plKey &r); + plMessage& AddReceivers(const hsTArray& rList); + + hsBool Send(const plKey r=nil, hsBool async=false); // Message will self-destruct after send. + hsBool SendAndKeep(const plKey r=nil, hsBool async=false); // Message won't self-destruct after send. + + const double GetTimeStamp() const { return fTimeStamp; } + plMessage& SetTimeStamp(double t) { fTimeStamp = t; return *this; } + + hsBool HasBCastFlag(UInt32 f) const { return 0 != (fBCastFlags & f); } + plMessage& SetBCastFlag(UInt32 f, hsBool on=true) { if( on )fBCastFlags |= f; else fBCastFlags &= ~f; return *this; } + + void SetAllBCastFlags(UInt32 f) { fBCastFlags=f; } + UInt32 GetAllBCastFlags() const { return fBCastFlags; } + + void AddNetReceiver( UInt32 plrID ); + void AddNetReceivers( const std::vector & plrIDs ); + std::vector* GetNetReceivers() const { return fNetRcvrPlayerIDs; } + + // just before dispatching this message, drop into debugger + void SetBreakBeforeDispatch (bool on) { dispatchBreak = on; } + bool GetBreakBeforeDispatch () const { return dispatchBreak; } +}; + + + +///////////////////////////////////////////////////////////////// +// Helpers for reading/writing these types: +// std::string +// xtl::istring +// c strings (char *) +// c arrays (type []) + + +///////////////////////////////////////////////////////////////// +// reads/writes your std::string field + +struct plMsgStdStringHelper +{ + static int Poke(const std::string & stringref, hsStream* stream, const UInt32 peekOptions=0); + static int PokeBig(const std::string & stringref, hsStream* stream, const UInt32 peekOptions=0); + static int Poke(const char * buf, UInt32 bufsz, hsStream* stream, const UInt32 peekOptions=0); + static int PokeBig(const char * buf, UInt32 bufsz, hsStream* stream, const UInt32 peekOptions=0); + static int Peek(std::string & stringref, hsStream* stream, const UInt32 peekOptions=0); + static int PeekBig(std::string & stringref, hsStream* stream, const UInt32 peekOptions=0); +}; + +///////////////////////////////////////////////////////////////// +// reads/writes your xtl::istring field + +struct plMsgXtlStringHelper +{ + static int Poke(const xtl::istring & stringref, hsStream* stream, const UInt32 peekOptions=0); + static int Peek(xtl::istring & stringref, hsStream* stream, const UInt32 peekOptions=0); +}; + +///////////////////////////////////////////////////////////////// +// reads/writes your char * field + +struct plMsgCStringHelper +{ + static int Poke(const char * str, hsStream* stream, const UInt32 peekOptions=0); + // deletes str and reallocates. you must delete [] str; + static int Peek(char *& str, hsStream* stream, const UInt32 peekOptions=0); +}; + +///////////////////////////////////////////////////////////////// +// reads/writes your type [] field +// don't use with byte ordered types like Int16,32. +// fine for Int8, char, and IEEE formatted types like float, double. + +struct plMsgCArrayHelper +{ + static int Poke(const void * buf, UInt32 bufsz, hsStream* stream, const UInt32 peekOptions=0); + static int Peek(void * buf, UInt32 bufsz, hsStream* stream, const UInt32 peekOptions=0); +}; + + + + + +#endif // plMessage_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessageWithCallbacks.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessageWithCallbacks.cpp new file mode 100644 index 00000000..3c176b2c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessageWithCallbacks.cpp @@ -0,0 +1,138 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsResMgr.h" +#include "plMessageWithCallbacks.h" +#include "plEventCallbackMsg.h" +#include "../pnNetCommon/plSynchedObject.h" +#include "plgDispatch.h" +#include "hsBitVector.h" + +plMessageWithCallbacks::~plMessageWithCallbacks() +{ + Clear(); +} + +void plMessageWithCallbacks::AddCallback(plMessage* e) +{ + hsRefCnt_SafeRef(e); + + // make sure callback msgs have the same net propagate properties as the container msg + e->SetBCastFlag(plMessage::kNetPropagate, HasBCastFlag(plMessage::kNetPropagate)); + + fCallbacks.Append(e); +} + +void plMessageWithCallbacks::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + Clear(); + + // read count + int n = stream->ReadSwap32(); + fCallbacks.SetCount(n); + + // read callbacks + int i; + for( i = 0; i < n; i++ ) + { + fCallbacks[i] = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); + } +} + +void plMessageWithCallbacks::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + // write count + int n=fCallbacks.GetCount(); + stream->WriteSwap32(n); + + // write callbacks + int i; + for( i = 0; i < fCallbacks.GetCount(); i++ ) + mgr->WriteCreatable( stream, fCallbacks[i] ); +} + +enum MsgWithCallbacksFlags +{ + kMsgWithCBsCallbacks, +}; + +void plMessageWithCallbacks::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(s, mgr); + + Clear(); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kMsgWithCBsCallbacks)) + { + // read count + int n = s->ReadSwap32(); + fCallbacks.SetCount(n); + + for (int i = 0; i < n; i++) + fCallbacks[i] = plMessage::ConvertNoRef(mgr->ReadCreatableVersion(s)); + } +} + +void plMessageWithCallbacks::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kMsgWithCBsCallbacks); + contentFlags.Write(s); + + // write count + int n = fCallbacks.GetCount(); + s->WriteSwap32(n); + + // write callbacks + for (int i = 0; i < n; i++) + mgr->WriteCreatableVersion(s, fCallbacks[i]); +} + +void plMessageWithCallbacks::Clear() +{ + int i; + for( i = 0; i < fCallbacks.GetCount(); i++ ) + hsRefCnt_SafeUnRef(fCallbacks[i]); + fCallbacks.SetCount(0); +} + +void plMessageWithCallbacks::SendCallbacks() +{ + int i; + for (i = fCallbacks.GetCount() - 1; i >= 0; i--) + { + plgDispatch::MsgSend(fCallbacks[i]); + fCallbacks.Remove(i); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessageWithCallbacks.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessageWithCallbacks.h new file mode 100644 index 00000000..57d2eac7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMessageWithCallbacks.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plMessageWithCB_inc +#define plMessageWithCB_inc + +#include "plMessage.h" +#include "hsTemplates.h" +#include "plEventCallbackMsg.h" +// +// Base class for messages which contain callbacks +// + +class plSynchedObject; +class plEventCallbackMsg; +class hsStream; +class hsResMgr; +class plMessageWithCallbacks : public plMessage +{ +private: + hsTArray fCallbacks; +public: + plMessageWithCallbacks() {} + plMessageWithCallbacks(const plKey &s, const plKey &r, const double* t) : plMessage(s,r,t) {} + ~plMessageWithCallbacks(); + + CLASSNAME_REGISTER( plMessageWithCallbacks ); + GETINTERFACE_ANY( plMessageWithCallbacks, plMessage ); + + void Clear(); + + void AddCallback(plMessage* e); // will RefCnt the message e. + int GetNumCallbacks() const { return fCallbacks.GetCount(); } + plMessage* GetCallback(int i) const { return fCallbacks[i]; } + plEventCallbackMsg* GetEventCallback(int i) const { return plEventCallbackMsg::ConvertNoRef(fCallbacks[i]); } + void SendCallbacks(); + +#if 0 + // returns true if ok to send in a networked situations + static hsBool NetOKToSend(plSynchedObject* sender, plEventCallbackMsg* cbmsg); +#endif + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +#endif // plMessageWithCB_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMultiModMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMultiModMsg.h new file mode 100644 index 00000000..e549c0f0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plMultiModMsg.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMultiModMsg_inc +#define plMultiModMsg_inc + +#include "plMessage.h" +#include "hsBitVector.h" + +class hsStream; +class hsResMgr; + + +class plMultiModMsg : public plMessage +{ +public: + plMultiModMsg() + : plMessage(nil, nil, nil) {} + plMultiModMsg(const plKey &s, + const plKey &r, + const double* t) + : plMessage(s, r, t) {} + ~plMultiModMsg() {} + + CLASSNAME_REGISTER( plMultiModMsg ); + GETINTERFACE_ANY( plMultiModMsg, plMessage ); + + enum ModCmds + { + }; + + hsBitVector fCmd; + + hsBool Cmd(int n) { return fCmd.IsBitSet(n); } + void SetCmd(int n) { fCmd.SetBit(n); } + void ClearCmd() { fCmd.Clear(); } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fCmd.Read(stream); + } + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + fCmd.Write(stream); + } +}; + +#endif // plMultiModMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeChangeMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeChangeMsg.cpp new file mode 100644 index 00000000..06660893 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeChangeMsg.cpp @@ -0,0 +1,45 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plNodeChangeMsg.h" +#include "hsStream.h" +#include "hsResMgr.h" + + +void plNodeChangeMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + IMsgRead(stream, mgr); + + fNodeKey = mgr->ReadKey(stream); +} + +void plNodeChangeMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + IMsgWrite(stream, mgr); + + mgr->WriteKey(stream, fNodeKey); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeChangeMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeChangeMsg.h new file mode 100644 index 00000000..bf6e1af1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeChangeMsg.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plNodeChangeMsg_inc +#define plNodeChangeMsg_inc + +#include "plMessage.h" + +class plNodeChangeMsg : public plMessage +{ +protected: + plKey fNodeKey; + +public: + plNodeChangeMsg() : fNodeKey(nil) {} + plNodeChangeMsg(plKey s, plKey &r, plKey node, double* t=nil) + : plMessage(s, r, t), fNodeKey(node) {} + + CLASSNAME_REGISTER( plNodeChangeMsg ); + GETINTERFACE_ANY( plNodeChangeMsg, plMessage ); + + plKey GetNodeKey() const { return fNodeKey; } + void SetNodeKey(plKey &k) { fNodeKey = k; } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif //plNodeChangeMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeRefMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeRefMsg.h new file mode 100644 index 00000000..11623822 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNodeRefMsg.h @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plNodeRefMsg_inc +#define plNodeRefMsg_inc + +#include "plRefMsg.h" + +class plNodeRefMsg : public plGenRefMsg +{ +public: + enum { + kDrawable, + kPhysical, + kAudible, + kObject, + kNeighbor, + kLight, + kOccluder, + kGeneric + }; + + plNodeRefMsg() {} + plNodeRefMsg(const plKey &r, UInt8 flags, Int8 which, Int8 type) : plGenRefMsg(r, flags, which, type) {} + + CLASSNAME_REGISTER( plNodeRefMsg ); + GETINTERFACE_ANY( plNodeRefMsg, plGenRefMsg ); +}; + +#endif // plNodeRefMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNotifyMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNotifyMsg.cpp new file mode 100644 index 00000000..c9c5ab7b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNotifyMsg.cpp @@ -0,0 +1,1759 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +///////////////////////////////////////////////////////////////////////////// +// +// MESSAGE : plNotifyMsg +// PARAMETERS : none +// +// PURPOSE : This is the message that notifies someone (either a responder or activator) +// : that some event or transition of state has happened +// +// + +#include "plNotifyMsg.h" + + +plNotifyMsg::plNotifyMsg(const plKey &s, const plKey &r) +{ + fSender = s; + AddReceiver(r); + IInit(); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : IInit and ~plNotifyMsg +// PARAMETERS : none +// +// PURPOSE : Initialization (called from constructors) and destructor +// +void plNotifyMsg::IInit() +{ + SetBCastFlag(plMessage::kNetPropagate); + fType = kActivator; + fState = 0.0f; // start state at (completely) false + fID = 0; +} + +plNotifyMsg::~plNotifyMsg() +{ + ClearEvents(); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddEvent +// PARAMETERS : ed - pointer to event that needs to be added (be recreating) +// +// PURPOSE : Add an event record to this notify message +// +// +void plNotifyMsg::AddEvent( proEventData* ed ) +{ + switch ( ed->fEventType ) + { + case proEventData::kCollision: + { + proCollisionEventData *evt = (proCollisionEventData *)ed; + AddCollisionEvent(evt->fEnter, evt->fHitter, evt->fHittee ); + } + break; + + case proEventData::kSpawned: + { + proSpawnedEventData *evt = (proSpawnedEventData *)ed; + AddSpawnedEvent( evt->fSpawner, evt->fSpawnee ); + } + break; + + case proEventData::kPicked: + { + proPickedEventData *evt = (proPickedEventData *)ed; + AddPickEvent( evt->fPicker, evt->fPicked, evt->fEnabled, evt->fHitPoint ); + } + break; + + case proEventData::kContained: + { + proContainedEventData *evt = (proContainedEventData *)ed; + AddContainerEvent( evt->fContained, evt->fContainer, evt->fEntering ); + } + break; + + case proEventData::kCallback: + { + proCallbackEventData *evt = (proCallbackEventData *)ed; + AddCallbackEvent( evt->fEventType ); + } + break; + + case proEventData::kResponderState: + { + proResponderStateEventData *evt = (proResponderStateEventData *)ed; + AddResponderStateEvent( evt->fState ); + } + break; + + case proEventData::kMultiStage: + { + proMultiStageEventData *evt = (proMultiStageEventData *)ed; + AddMultiStageEvent( evt->fStage, evt->fEvent, evt->fAvatar ); + } + break; + + case proEventData::kCoop: + { + proCoopEventData *evt = (proCoopEventData *)ed; + AddCoopEvent( evt->fID, evt->fSerial); + } + + case proEventData::kControlKey: + { + proControlKeyEventData *evt = (proControlKeyEventData *)ed; + AddControlKeyEvent( evt->fControlKey, evt->fDown ); + } + break; + + case proEventData::kFacing: + { + proFacingEventData *evt = (proFacingEventData *)ed; + AddFacingEvent( evt->fFacer, evt->fFacee, evt->dot, evt->enabled ); + } + break; + + case proEventData::kActivate: + { + proActivateEventData *evt = (proActivateEventData *)ed; + AddActivateEvent( evt->fActivate ); + } + break; + + case proEventData::kVariable: + { + proVariableEventData *evt = (proVariableEventData *)ed; + switch (evt->fDataType) + { + case proEventData::kNumber: + AddVariableEvent(evt->fName, evt->fNumber); + break; + case proEventData::kKey: + AddVariableEvent(evt->fName, evt->fKey); + break; + } + } + break; + case proEventData::kClickDrag: + { + proClickDragEventData* evt = (proClickDragEventData*)ed; + AddClickDragEvent(evt->picker, evt->picked, evt->animPos); + } + break; + case proEventData::kOfferLinkingBook: + { + proOfferLinkingBookEventData* evt = (proOfferLinkingBookEventData*)ed; + AddOfferBookEvent(evt->offerer, evt->targetAge, evt->offeree); + } + case proEventData::kBook: + { + proBookEventData* evt = (proBookEventData*)ed; + AddBookEvent( evt->fEvent, evt->fLinkID ); + } + case proEventData::kClimbingBlockerHit: + { + proClimbingBlockerHitEventData* evt = (proClimbingBlockerHitEventData*)ed; + AddHitClimbingBlockerEvent(evt->fBlockerKey); + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddCollisionEvent +// PARAMETERS : enter - true for when entering collision and false when exiting collision +// : other - the plKey of the other object involved (Hitter) +// : self - the plKey to probably us (Hittee) +// +// PURPOSE : Add a collision event record to this notify message +// : Remove like event records, only the last one counts +// +// NOTE: To test for duplicate record, it only checks for records of the same type +// : Eventually, it might be wise to check if the same 'self' key also? +// +void plNotifyMsg::AddCollisionEvent( hsBool enter, const plKey &other, const plKey &self, hsBool onlyOneCollision ) +{ + // if this is the normal case of there can only be one collision, then get rid of any others + if ( onlyOneCollision ) + { + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kCollision ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + } + + // create the collision event record + proCollisionEventData* pED = TRACKED_NEW proCollisionEventData; + pED->fEnter = enter; + pED->fHitter = other; + pED->fHittee = self; + fEvents.Append(pED); // then add it to the list of event records +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddCallbackEvent +// PARAMETERS : event - the event type, as enumerated in plEventCallbackMsg.h +// +// +void plNotifyMsg::AddCallbackEvent( Int32 event ) +{ + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kCallback ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + + // create the collision event record + proCallbackEventData* pED = TRACKED_NEW proCallbackEventData; + pED->fEventType = event; + fEvents.Append(pED); // then add it to the list of event records +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddResponderStateEvent +// PARAMETERS : state - the state for the responder to switch to before triggering +// +// +void plNotifyMsg::AddResponderStateEvent( Int32 state ) +{ + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kResponderState ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + + // create the collision event record + proResponderStateEventData* pED = TRACKED_NEW proResponderStateEventData; + pED->fState = state; + fEvents.Append(pED); // then add it to the list of event records +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddMultiStageEvent +// PARAMETERS : stage - the stage the multistage behavior is on +// : event - what was the event that happened +// +// +void plNotifyMsg::AddMultiStageEvent( Int32 stage, Int32 event, const plKey& avatar ) +{ + // we can have multi events of this type + // create the mutlistage event record + proMultiStageEventData* pED = TRACKED_NEW proMultiStageEventData; + pED->fStage = stage; + pED->fEvent = event; + pED->fAvatar = avatar; + fEvents.Append(pED); // then add it to the list of event records +} + +void plNotifyMsg::AddCoopEvent(UInt32 id, UInt16 serial) +{ + proCoopEventData *pED = TRACKED_NEW proCoopEventData; + pED->fID = id; + pED->fSerial = serial; + fEvents.Append(pED); +} + +void plNotifyMsg::AddSpawnedEvent (const plKey &spawner, const plKey &spawnee) +{ + proSpawnedEventData* pED = TRACKED_NEW proSpawnedEventData(); + pED->fSpawner = spawner; + pED->fSpawnee = spawnee; + fEvents.Append(pED); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddActivateEvent +// PARAMETERS : activate - true or false +// +// PURPOSE : Sometimes you just want a yes or no +// : Remove like event records, only the last one counts +// +// NOTE: To test for duplicate record, it only checks for records of the same type +// : Eventually, it might be wise to check if the same 'self' key also? +// +void plNotifyMsg::AddActivateEvent( hsBool activate ) +{ + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kActivate ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + + // create the collision event record + proActivateEventData* pED = TRACKED_NEW proActivateEventData; + pED->fActive = true; + pED->fActivate = activate; + fEvents.Append(pED); // then add it to the list of event records +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddPickEvent +// PARAMETERS : other - the plKey of the other object involved (Picker) +// : self - the plKey to probably us (Picked) +// +// PURPOSE : Add a pick event record to this notify message +// : Remove like event records, only the last one counts +// +// NOTE: To test for duplicate record, it only checks for records of the same type +// : Eventually, it might be wise to check if the same 'self' key also? +// +void plNotifyMsg::AddPickEvent( const plKey &other, const plKey& self, hsBool enabled, hsPoint3 hitPoint ) +{ + + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kPicked ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + + // create the pick event record + proPickedEventData* pED = TRACKED_NEW proPickedEventData; + pED->fPicker = other; + pED->fPicked = self; + pED->fEnabled = enabled; + pED->fHitPoint = hitPoint; + + fEvents.Append(pED); // then add it to the list of event records +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddContainerEvent +// PARAMETERS : container - the plKey of the object contained +// : contained - the plKey of the containing volume +// +// PURPOSE : Add a container event record to this notify message +// : Remove like event records, only the last one counts +// +// NOTE: To test for duplicate record, it only checks for records of the same type +// : Eventually, it might be wise to check if the same 'self' key also? +// +void plNotifyMsg::AddContainerEvent( const plKey &container, const plKey &contained, hsBool entering ) +{ + + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kContained ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + + // create the pick event record + proContainedEventData* pED = TRACKED_NEW proContainedEventData; + pED->fContained = contained; + pED->fContainer = container; + pED->fEntering = entering; + fEvents.Append(pED); // then add it to the list of event records +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddFacingEvent +// PARAMETERS : other - the plKey of the other object involved (Facer) +// : self - the plKey to probably us (Facee) +// : dot - the dot prod. of the facing angle +// +// PURPOSE : Add a facing event record to this notify message +// : Remove like event records, only the last one counts +// +// NOTE: To test for duplicate record, it only checks for records of the same type +// : Eventually, it might be wise to check if the same 'self' key also? +// +void plNotifyMsg::AddFacingEvent( const plKey &other, const plKey &self, hsScalar dot, hsBool enabled ) +{ + + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kFacing ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + + // create the pick event record + proFacingEventData* pED = TRACKED_NEW proFacingEventData; + pED->fFacer = other; + pED->fFacee = self; + pED->dot = dot; + pED->enabled = enabled; + fEvents.Append(pED); // then add it to the list of event records +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddControlKeyEvent +// PARAMETERS : id - identification, could be the controlkey that was hit +// : down - whether the control is going down or up in action +// +// PURPOSE : Add a control key event record to this notify message +// : Remove like event records, only the last one counts +// +// NOTE: To test for duplicate record, it only checks for records of the same type +// : Eventually, it might be wise to check if the same 'self' key also? +// +void plNotifyMsg::AddControlKeyEvent( Int32 key, hsBool down ) +{ + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kControlKey ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + + // create the control key event record + proControlKeyEventData* pED = TRACKED_NEW proControlKeyEventData; + pED->fControlKey = key; + pED->fDown = down; + fEvents.Append(pED); // then add it to the list of event records +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddVariableEvent +// PARAMETERS : name - name of the variable +// : number - the value of the variable as a number +// +// PURPOSE : Add a variable event record to this notify message +// +void plNotifyMsg::AddVariableEvent( const char* name, hsScalar number ) +{ + // create the control key event record + proVariableEventData* pED = TRACKED_NEW proVariableEventData; + pED->fName = hsStrcpy(nil,name); +// pED->fName = (char*)name; + pED->fDataType = proEventData::kNumber; + pED->fNumber = number; + fEvents.Append(pED); // then add it to the list of event records +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddVariableEvent +// PARAMETERS : name - name of the variable +// : number - the value of the variable as a number +// +// PURPOSE : Add a variable event record to this notify message +// +void plNotifyMsg::AddVariableEvent( const char* name, const plKey &key ) +{ + // create the control key event record + proVariableEventData* pED = TRACKED_NEW proVariableEventData; + pED->fName = hsStrcpy(nil,name); +// pED->fName = (char*)name; + pED->fDataType = proEventData::kKey; + pED->fKey = key; + fEvents.Append(pED); // then add it to the list of event records +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddClickDragEvent +// PARAMETERS : picker - always the local player +// : pickee - the click/draggable +// : animPos - 0.0 to 1.0 % of the way we are through the animation +// +// PURPOSE : Add a click/drag event record to this notify message +// : Remove like event records, only the last one counts +// +// NOTE: To test for duplicate record, it only checks for records of the same type +// : Eventually, it might be wise to check if the same 'self' key also? +// +void plNotifyMsg::AddClickDragEvent( const plKey& dragger, const plKey& dragee, hsScalar animPos ) +{ + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kClickDrag ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + + // create the control key event record + proClickDragEventData* pED = TRACKED_NEW proClickDragEventData; + pED->picked = dragee; + pED->picker = dragger; + pED->animPos = animPos; + fEvents.Append(pED); // then add it to the list of event records +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddOfferBookEvent +// PARAMETERS : offerer - the book offerer - the local player on the sender's machine, a remote avatar at the receiver's end +// : targetAge - the age we are offering. this # is taken from the konstant list of age link panels in xLinkingBookPopupGUI.py +// +// PURPOSE : Add an OfferBookEvent - note this message should NOT EVER be locally delivered - Networked ONLY and only to a specific net transport member. +// : Remove like event records, only the last one counts +// +// NOTE: To test for duplicate record, it only checks for records of the same type +// : Eventually, it might be wise to check if the same 'self' key also? +// +void plNotifyMsg::AddOfferBookEvent(const plKey& offerer, int targetAge, int offeree) +{ + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kOfferLinkingBook ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + + // create the control key event record + proOfferLinkingBookEventData* pED = TRACKED_NEW proOfferLinkingBookEventData; + pED->offerer = offerer; + pED->targetAge = targetAge; + pED->offeree = offeree; + fEvents.Append(pED); // then add it to the list of event records +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddBookEvent +// PARAMETERS : event - The type of event we are, as defined by the enum in pfJournalBook.h +// : linkID - For image link event types, the link ID as defined in the esHTML source. Otherwise, unused. +// +// PURPOSE : Add an OfferBookEvent - note this message should NOT EVER be locally delivered - Networked ONLY and only to a specific net transport member. +// : Remove like event records, only the last one counts +// +// NOTE: To test for duplicate record, it only checks for records of the same type +// : Eventually, it might be wise to check if the same 'self' key also? +// +void plNotifyMsg::AddBookEvent( UInt32 event, UInt32 linkID /*=0*/) +{ + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kBook ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + + // create the control key event record + proBookEventData* pED = TRACKED_NEW proBookEventData; + pED->fEvent = event; + pED->fLinkID = linkID; + fEvents.Append(pED); // then add it to the list of event records +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : AddClimbingBlockerHit +// PARAMETERS : fBlockerKey - the key of the blocker we hit +// +// PURPOSE : this is to notify python we hit a specific climbing blocker +// : +// +// NOTE: To test for duplicate record, it only checks for records of the same type +// : Eventually, it might be wise to check if the same 'self' key also? +// +void plNotifyMsg::AddHitClimbingBlockerEvent(const plKey &blocker) +{ + // remove records that are like the one being added + int num_recs = fEvents.GetCount(); + if ( num_recs > 0 ) + { + int i; + for ( i=0; ifEventType == proEventData::kClimbingBlockerHit ) + { + // remove it + delete fEvents[i]; + fEvents.Remove(i); + // then jump out.. the count is no longer good and anyway there should only be one of the same type + break; + } + } + } + + // create the control key event record + proClimbingBlockerHitEventData* pED = TRACKED_NEW proClimbingBlockerHitEventData; + pED->fBlockerKey = blocker; + fEvents.Append(pED); // then add it to the list of event records +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : FindEventRecord +// PARAMETERS : eventtype - the event type record that we are looking for +// +// PURPOSE : Find the first record in the event records that is of type eventtype +// +proEventData* plNotifyMsg::FindEventRecord( Int32 eventtype ) +{ + // make sure that its a legal event type + if ( eventtype >= 0 && eventtype < proEventData::kNone ) + { + // loop thru the event records looking for what they want + int num_recs = fEvents.GetCount(); + int i; + for ( i=0; ifEventType == eventtype ) + return pEDTest; + } + } + return nil; +} + + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : ClearEvents +// PARAMETERS : none +// +// PURPOSE : clear all the event records +// +void plNotifyMsg::ClearEvents() +{ + // clean up fEvent records + int i; + for( i = 0; i < fEvents.GetCount(); i++ ) + delete fEvents[i]; + fEvents.Reset(); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Read +// PARAMETERS : stream - where to read the data from +// : mgr - resource manager (for special help) +// +// PURPOSE : Read object from stream +// +void plNotifyMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + // read in the static data + fType = stream->ReadSwap32(); + stream->ReadSwap(&fState); + fID = stream->ReadSwap32(); + // read in the variable part of the message + Int32 numberEDs = stream->ReadSwap32(); + fEvents.SetCountAndZero(numberEDs); + if ( numberEDs > 0 ) + { + int i; + for ( i=0 ; i < numberEDs ; i++ ) + { + proEventData* pED = proEventData::Read( stream, mgr ); + fEvents[i] = pED; + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : Write +// PARAMETERS : stream - where to write the data to +// : mgr - resource manager (for special help) +// +// PURPOSE : Write object from stream +// +void plNotifyMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + // write static data + stream->WriteSwap32(fType); + stream->WriteSwap(fState); + stream->WriteSwap32(fID); + // then write the variable data + Int32 numberEDs = fEvents.Count(); + stream->WriteSwap32(numberEDs); + if ( numberEDs > 0 ) + { + // write out each record + int i; + for ( i=0 ; i < numberEDs; i++ ) + { + fEvents[i]->Write(stream,mgr); + } + } + +} + +enum NotifyMsgFlags +{ + kNotifyMsgType, + kNotifyMsgState, + kNotifyMsgID, + kNotifyMsgEDs, +}; + +#include "../pnNetCommon/plNetApp.h" + +void plNotifyMsg::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kNotifyMsgType)) + fType = s->ReadSwap32(); + + if (contentFlags.IsBitSet(kNotifyMsgState)) + s->ReadSwap(&fState); + + if (contentFlags.IsBitSet(kNotifyMsgID)) + fID = s->ReadSwap32(); + + if (contentFlags.IsBitSet(kNotifyMsgEDs)) + { + // read in the variable part of the message + Int32 numberEDs = s->ReadSwap32(); + fEvents.SetCountAndZero(numberEDs); + if (numberEDs > 0) + { + for (int i = 0; i < numberEDs ; i++) + { + proEventData* pED = proEventData::ReadVersion(s, mgr); + fEvents[i] = pED; + } + } + } + + plKey avKey = GetAvatarKey(); + if (plNetClientApp::GetInstance() && avKey == plNetClientApp::GetInstance()->GetLocalPlayerKey()) + { + SetBCastFlag(plMessage::kNetStartCascade, true); + SetBCastFlag(plMessage::kNetNonLocal | plMessage::kNetPropagate, false); + } +} + +void plNotifyMsg::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kNotifyMsgType); + contentFlags.SetBit(kNotifyMsgState); + contentFlags.SetBit(kNotifyMsgID); + contentFlags.SetBit(kNotifyMsgEDs); + contentFlags.Write(s); + + // kNotifyMsgType + s->WriteSwap32(fType); + + // kNotifyMsgState + s->WriteSwap(fState); + + // kNotifyMsgID + s->WriteSwap32(fID); + + // kNotifyMsgEDs + Int32 numberEDs = fEvents.Count(); + s->WriteSwap32(numberEDs); + if (numberEDs > 0) + { + // write out each record + for (int i = 0; i < numberEDs; i++) + { + fEvents[i]->WriteVersion(s, mgr); + } + } +} + + +plKey plNotifyMsg::GetAvatarKey() +{ + for (int i = 0; i < fEvents.GetCount(); i++) + { + proEventData *event = fEvents[i]; + + switch (event->fEventType) + { + case proEventData::kCollision: + return ( (proCollisionEventData *)event )->fHitter; + + case proEventData::kPicked: + return ( (proPickedEventData *)event )->fPicker; + + case proEventData::kSpawned: + return ( (proSpawnedEventData *)event )->fSpawnee; + + case proEventData::kMultiStage: + return ( (proMultiStageEventData *)event )->fAvatar; + } + } + + return nil; +} + + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + + +proEventData* proEventData::ICreateEventDataType(Int32 type) +{ + switch (type) + { + case kCollision: return TRACKED_NEW proCollisionEventData; + case kPicked: return TRACKED_NEW proPickedEventData; + case kControlKey: return TRACKED_NEW proControlKeyEventData; + case kVariable: return TRACKED_NEW proVariableEventData; + case kFacing: return TRACKED_NEW proFacingEventData; + case kContained: return TRACKED_NEW proContainedEventData; + case kActivate: return TRACKED_NEW proActivateEventData; + case kCallback: return TRACKED_NEW proCallbackEventData; + case kResponderState: return TRACKED_NEW proResponderStateEventData; + case kMultiStage: return TRACKED_NEW proMultiStageEventData; + case kCoop: return TRACKED_NEW proCoopEventData; + case kSpawned: return TRACKED_NEW proSpawnedEventData; + case kOfferLinkingBook: return TRACKED_NEW proOfferLinkingBookEventData; + case kBook: return TRACKED_NEW proBookEventData; + case kClimbingBlockerHit: return TRACKED_NEW proClimbingBlockerHitEventData; + } + + return nil; +} + +//// proEventData::Read ///////////////////////////////////////////////////// +// Static function on proEventData that reads in a derived event data type +// from the given stream and returns it. + +proEventData* proEventData::Read( hsStream *stream, hsResMgr *mgr ) +{ + Int32 evtType = stream->ReadSwap32(); + + proEventData* data = ICreateEventDataType(evtType); + + if (data != nil) + data->IRead(stream, mgr); + + return data; +} + +void proEventData::Write(hsStream *stream, hsResMgr *mgr) +{ + stream->WriteSwap32(fEventType); + IWrite(stream, mgr); +} + +enum proEventDataFlags +{ + kProEventDataType, +}; + +proEventData* proEventData::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProEventDataType)) + { + Int32 evtType = s->ReadSwap32(); + + proEventData* data = ICreateEventDataType(evtType); + + if (data != nil) + data->IReadVersion(s, mgr); + + return data; + } + + return nil; +} + +void proEventData::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProEventDataType); + contentFlags.Write(s); + + // kProEventDataType + s->WriteSwap32(fEventType); + + IWriteVersion(s, mgr); +} + +void proCollisionEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fEnter = stream->ReadBool(); + fHitter = mgr->ReadKey(stream); + fHittee = mgr->ReadKey(stream); +} + +void proCollisionEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteBool(fEnter); + mgr->WriteKey(stream, fHitter); + mgr->WriteKey(stream, fHittee); +} + +enum proCollisionFlags +{ + kProCollisionEnter, + kProCollisionHitter, + kProCollisionHittee, +}; + +void proCollisionEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProCollisionEnter)) + fEnter = s->ReadBool(); + if (contentFlags.IsBitSet(kProCollisionHitter)) + fHitter = mgr->ReadKey(s); + if (contentFlags.IsBitSet(kProCollisionHittee)) + fHittee = mgr->ReadKey(s); +} + +void proCollisionEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProCollisionEnter); + contentFlags.SetBit(kProCollisionHitter); + contentFlags.SetBit(kProCollisionHittee); + contentFlags.Write(s); + + // kProCollisionEnter + s->WriteBool(fEnter); + // kProCollisionHitter + mgr->WriteKey(s, fHitter); + // kProCollisionHittee + mgr->WriteKey(s, fHittee); +} + +void proPickedEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fPicker = mgr->ReadKey(stream); + fPicked = mgr->ReadKey(stream); + fEnabled = stream->ReadBool(); + fHitPoint.Read(stream); +} + +void proPickedEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + mgr->WriteKey(stream, fPicker); + mgr->WriteKey(stream, fPicked); + stream->WriteBool(fEnabled); + fHitPoint.Write(stream); +} + +enum ProPickedFlags +{ + kProPickedPicker, + kProPickedPicked, + kProPickedEnabled, + kProPickedHitPoint, +}; + +void proPickedEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProPickedPicker)) + fPicker = mgr->ReadKey(s); + if (contentFlags.IsBitSet(kProPickedPicked)) + fPicked = mgr->ReadKey(s); + if (contentFlags.IsBitSet(kProPickedEnabled)) + fEnabled = s->ReadBool(); + if (contentFlags.IsBitSet(kProPickedHitPoint)) + fHitPoint.Read(s); +} + +void proPickedEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProPickedPicker); + contentFlags.SetBit(kProPickedPicked); + contentFlags.SetBit(kProPickedEnabled); + contentFlags.SetBit(kProPickedHitPoint); + contentFlags.Write(s); + + // kProPickedPicker + mgr->WriteKey(s, fPicker); + // kProPickedPicked + mgr->WriteKey(s, fPicked); + // kProPickedEnabled + s->WriteBool(fEnabled); + // kProPickedHitPoint + fHitPoint.Write(s); +} + +void proSpawnedEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fSpawner = mgr->ReadKey(stream); + fSpawnee = mgr->ReadKey(stream); +} + +void proSpawnedEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + mgr->WriteKey(stream, fSpawner); + mgr->WriteKey(stream, fSpawnee); +} + +enum ProSpawnedFlags +{ + kProSpawnedSpawner, + kProSpawnedSpawnee, +}; + +void proSpawnedEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProSpawnedSpawner)) + fSpawner = mgr->ReadKey(s); + if (contentFlags.IsBitSet(kProSpawnedSpawnee)) + fSpawnee = mgr->ReadKey(s); +} + +void proSpawnedEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProSpawnedSpawner); + contentFlags.SetBit(kProSpawnedSpawnee); + contentFlags.Write(s); + + // kProSpawnedSpawner + mgr->WriteKey(s, fSpawner); + // kProSpawnedSpawnee + mgr->WriteKey(s, fSpawnee); +} + +void proControlKeyEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fControlKey = stream->ReadSwap32(); + fDown = stream->ReadBool(); +} +void proControlKeyEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteSwap32(fControlKey); + stream->WriteBool(fDown); +} + +enum ProControlFlags +{ + kProControlKey, + kProControlDown, +}; + +void proControlKeyEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProControlKey)) + fControlKey = s->ReadSwap32(); + if (contentFlags.IsBitSet(kProControlDown)) + fDown = s->ReadBool(); +} +void proControlKeyEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProControlKey); + contentFlags.SetBit(kProControlDown); + contentFlags.Write(s); + + // kProControlKey + s->WriteSwap32(fControlKey); + // kProControlDown + s->WriteBool(fDown); +} + + +void proVariableEventData::IInit() +{ + fName = nil; +} +void proVariableEventData::IDestruct() +{ + if ( fName != nil ) + delete [] fName; + fName = nil; +} + +void proVariableEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fName = stream->ReadSafeString(); + fDataType = stream->ReadSwap32(); + fNumber = stream->ReadSwapScalar(); + fKey = mgr->ReadKey(stream); +} + +void proVariableEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteSafeString(fName); + stream->WriteSwap32(fDataType); + stream->WriteSwapScalar(fNumber); + mgr->WriteKey(stream, fKey); +} + +enum ProVariableFlags +{ + kProVariableName, + kProVariableDataType, + kProVariableNumber, + kProVariableKey, +}; + +void proVariableEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProVariableName)) + fName = s->ReadSafeString(); + if (contentFlags.IsBitSet(kProVariableDataType)) + fDataType = s->ReadSwap32(); + if (contentFlags.IsBitSet(kProVariableNumber)) + fNumber = s->ReadSwapScalar(); + if (contentFlags.IsBitSet(kProVariableKey)) + fKey = mgr->ReadKey(s); +} + +void proVariableEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProVariableName); + contentFlags.SetBit(kProVariableDataType); + contentFlags.SetBit(kProVariableNumber); + contentFlags.SetBit(kProVariableKey); + contentFlags.Write(s); + + // kProVariableName + s->WriteSafeString(fName); + // kProVariableDataType + s->WriteSwap32(fDataType); + // kProVariableNumber + s->WriteSwapScalar(fNumber); + // kProVariableKey + mgr->WriteKey(s, fKey); +} + +void proFacingEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fFacer = mgr->ReadKey(stream); + fFacee = mgr->ReadKey(stream); + dot = stream->ReadSwapScalar(); + enabled = stream->ReadBool(); +} + +void proFacingEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + mgr->WriteKey(stream, fFacer); + mgr->WriteKey(stream, fFacee); + stream->WriteSwapScalar(dot); + stream->WriteBool(enabled); +} + +enum ProFacingFlags +{ + kProFacingFacer, + kProFacingFacee, + kProFacingDot, + kProFacingEnabled, +}; + +void proFacingEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProFacingFacer)) + fFacer = mgr->ReadKey(s); + if (contentFlags.IsBitSet(kProFacingFacee)) + fFacee = mgr->ReadKey(s); + if (contentFlags.IsBitSet(kProFacingDot)) + dot = s->ReadSwapScalar(); + if (contentFlags.IsBitSet(kProFacingEnabled)) + enabled = s->ReadBool(); +} + +void proFacingEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProFacingFacer); + contentFlags.SetBit(kProFacingFacee); + contentFlags.SetBit(kProFacingDot); + contentFlags.SetBit(kProFacingEnabled); + contentFlags.Write(s); + + // kProFacingFacer + mgr->WriteKey(s, fFacer); + // kProFacingFacee + mgr->WriteKey(s, fFacee); + // kProFacingDot + s->WriteSwapScalar(dot); + // kProFacingEnabled + s->WriteBool(enabled); +} + +void proContainedEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fContained = mgr->ReadKey(stream); + fContainer = mgr->ReadKey(stream); + fEntering = stream->ReadBool(); +} + +void proContainedEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + mgr->WriteKey(stream, fContained); + mgr->WriteKey(stream, fContainer); + stream->WriteBool(fEntering); +} + +enum ProContainedFlags +{ + kProContainedContained, + kProContainedContainer, + kProContainedEntering, +}; + +void proContainedEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProContainedContained)) + fContained = mgr->ReadKey(s); + if (contentFlags.IsBitSet(kProContainedContainer)) + fContainer = mgr->ReadKey(s); + if (contentFlags.IsBitSet(kProContainedEntering)) + fEntering = s->ReadBool(); +} + +void proContainedEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProContainedContained); + contentFlags.SetBit(kProContainedContainer); + contentFlags.SetBit(kProContainedEntering); + contentFlags.Write(s); + + // kProContainedContained + mgr->WriteKey(s, fContained); + // kProContainedContainer + mgr->WriteKey(s, fContainer); + // kProContainedEntering + s->WriteBool(fEntering); +} + +void proActivateEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fActive = stream->ReadBool(); + fActivate = stream->ReadBool(); +} + +void proActivateEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteBool(fActive); + stream->WriteBool(fActivate); +} + +enum ProActivateFlags +{ + kProActivateActive, + kProActivateActivate, +}; + +void proActivateEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProActivateActive)) + fActive = s->ReadBool(); + if (contentFlags.IsBitSet(kProActivateActivate)) + fActivate = s->ReadBool(); +} + +void proActivateEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProActivateActive); + contentFlags.SetBit(kProActivateActivate); + contentFlags.Write(s); + + // kProActivateActive + s->WriteBool(fActive); + // kProActivateActivate + s->WriteBool(fActivate); +} + +void proCallbackEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fEventType = stream->ReadSwap32(); +} + +void proCallbackEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteSwap32(fEventType); +} + +enum ProCallbackFlags +{ + kProCallbackEventType, +}; + +void proCallbackEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProCallbackEventType)) + fEventType = s->ReadSwap32(); +} + +void proCallbackEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProCallbackEventType); + contentFlags.Write(s); + + // kProCallbackEventType + s->WriteSwap32(fEventType); +} + +void proResponderStateEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fState = stream->ReadSwap32(); +} + +void proResponderStateEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteSwap32(fState); +} + +enum ProResponderFlags +{ + kProResponderState, +}; + +void proResponderStateEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProResponderState)) + fState = s->ReadSwap32(); +} + +void proResponderStateEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProResponderState); + contentFlags.Write(s); + + // kProResponderState + s->WriteSwap32(fState); +} + +void proMultiStageEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fStage = stream->ReadSwap32(); + fEvent = stream->ReadSwap32(); + fAvatar = mgr->ReadKey(stream); +} + +void proMultiStageEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteSwap32(fStage); + stream->WriteSwap32(fEvent); + mgr->WriteKey(stream, fAvatar); +} + +enum ProMultiStageFlags +{ + kProMultiStageStage, + kProMultiStageEvent, + kProMultiStageAvatar, +}; + +void proMultiStageEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProMultiStageStage)) + fStage = s->ReadSwap32(); + if (contentFlags.IsBitSet(kProMultiStageEvent)) + fEvent = s->ReadSwap32(); + if (contentFlags.IsBitSet(kProMultiStageAvatar)) + fAvatar = mgr->ReadKey(s); +} + +void proMultiStageEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProMultiStageStage); + contentFlags.SetBit(kProMultiStageEvent); + contentFlags.SetBit(kProMultiStageAvatar); + contentFlags.Write(s); + + // kProMultiStageStage + s->WriteSwap32(fStage); + // kProMultiStageEvent + s->WriteSwap32(fEvent); + // kProMultiStageAvatar + mgr->WriteKey(s, fAvatar); +} + +void proCoopEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fID = stream->ReadSwap32(); + fSerial = stream->ReadSwap16(); +} + +void proCoopEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteSwap32(fID); + stream->WriteSwap16(fSerial); +} + +enum ProCoopFlags +{ + kProCoopID, + kProCoopSerial +}; + +void proCoopEventData::IReadVersion(hsStream* stream, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(stream); + + if(contentFlags.IsBitSet(kProCoopID)) + fID = stream->ReadSwap32(); + if(contentFlags.IsBitSet(kProCoopSerial)) + fSerial = stream->ReadSwap16(); +} + +void proCoopEventData::IWriteVersion(hsStream* stream, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProCoopID); + contentFlags.SetBit(kProCoopSerial); + contentFlags.Write(stream); + + stream->WriteSwap32(fID); + stream->WriteSwap16(fSerial); + +} + +void proOfferLinkingBookEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + mgr->WriteKey(stream, offerer); + stream->WriteSwap32(targetAge); + stream->WriteSwap32(offeree); +} + +void proOfferLinkingBookEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + offerer = mgr->ReadKey(stream); + targetAge = stream->ReadSwap32(); + offeree = stream->ReadSwap32(); +} + +enum ProOfferFlags +{ + kProOfferOfferer, + kProOfferTargetAge, + kProOfferOfferee, +}; + +void proOfferLinkingBookEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProOfferOfferer); + contentFlags.SetBit(kProOfferTargetAge); + contentFlags.SetBit(kProOfferOfferee); + contentFlags.Write(s); + + mgr->WriteKey(s, offerer); + s->WriteSwap32(targetAge); + s->WriteSwap32(offeree); +} + +void proOfferLinkingBookEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if(contentFlags.IsBitSet(kProOfferOfferer)) + offerer = mgr->ReadKey(s); + if(contentFlags.IsBitSet(kProOfferTargetAge)) + targetAge = s->ReadSwap32(); + if(contentFlags.IsBitSet(kProOfferOfferee)) + offeree = s->ReadSwap32(); +} + +void proBookEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteSwap32(fEvent); + stream->WriteSwap32(fLinkID); +} + +void proBookEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fEvent = stream->ReadSwap32(); + fLinkID = stream->ReadSwap32(); +} + +enum ProBookFlags +{ + kProBookEvent, + kProBookLinkID, +}; + +void proBookEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProBookEvent); + contentFlags.SetBit(kProBookLinkID); + contentFlags.Write(s); + + s->WriteSwap32( fEvent ); + s->WriteSwap32( fLinkID ); +} + +void proBookEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if(contentFlags.IsBitSet(kProBookEvent)) + fEvent = s->ReadSwap32(); + if(contentFlags.IsBitSet(kProBookLinkID)) + fLinkID = s->ReadSwap32(); +} + + +void proClimbingBlockerHitEventData::IRead(hsStream* stream, hsResMgr* mgr) +{ + fBlockerKey = mgr->ReadKey(stream); +} + +void proClimbingBlockerHitEventData::IWrite(hsStream* stream, hsResMgr* mgr) +{ + mgr->WriteKey(stream, fBlockerKey); +} + +enum proClimbingBlockerHitFlags +{ + + kProClimbingBlockerKey, +}; + +void proClimbingBlockerHitEventData::IReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kProClimbingBlockerKey)) + fBlockerKey = mgr->ReadKey(s);; +} + +void proClimbingBlockerHitEventData::IWriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kProClimbingBlockerKey); + contentFlags.Write(s); + + // kProClimbingBlockerKey + mgr->WriteKey(s, fBlockerKey); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNotifyMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNotifyMsg.h new file mode 100644 index 00000000..51bc1adc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plNotifyMsg.h @@ -0,0 +1,386 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plNotifyMsg_h_ +#define _plNotifyMsg_h_ + +#include "plMessage.h" +#include "hsResMgr.h" +#include "../pnModifier/plSingleModifier.h" +#include "hsUtils.h" +#include "hsGeometry3.h" + + +// +// repeatable Event data in Notify message +// +// Updated 4.13.2002 mcn - Unions are all well and good, but only work on simple data types, and since +// plKey now isn't so simple... + +class proEventData +{ +public: + // keep this enum synchronized with the list at the top of PlasmaTypes.py + enum eventType + { + kCollision=1, + kPicked =2, + kControlKey=3, + kVariable=4, + kFacing=5, + kContained=6, + kActivate=7, + kCallback=8, + kResponderState=9, + kMultiStage=10, + kSpawned=11, + kClickDrag=12, + kCoop=13, + kOfferLinkingBook=14, + kBook=15, + kClimbingBlockerHit=16, + kNone + }; + + proEventData( Int32 evtType = kNone ) : fEventType( evtType ) + { + } + virtual ~proEventData() {} + + enum dataType + { + kNumber=1, + kKey, + kNotta + }; + + // what the multstage event types are for multistage.fEvent + enum multiStageEventType + { + kEnterStage=1, + kBeginingOfLoop, + kAdvanceNextStage, + kRegressPrevStage, + kNothing + }; + + Int32 fEventType; // what type of event (evenType enum) + + static proEventData* ICreateEventDataType(Int32 type); + + static proEventData* Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + static proEventData* ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); + +protected: + virtual void IInit() {} + virtual void IDestruct() {} + virtual void IRead(hsStream* stream, hsResMgr* mgr) {} + virtual void IWrite(hsStream* stream, hsResMgr* mgr) {} + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr) {} + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr) {} +}; + +// The following macro makes it easy (or easier) to define new event data types +#define proEventType(type) class pro##type##EventData : public proEventData { public: pro##type##EventData() : proEventData( k##type ) {IInit();} virtual ~pro##type##EventData(){IDestruct();} + +proEventType(Collision) + hsBool fEnter; // entering? (false is exit) + plKey fHitter; // collision hitter (other probably player) + plKey fHittee; // collision hittee (probably us) + +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(Picked) + plKey fPicker; // who picked it (problably player) + plKey fPicked; // object that was picked + hsBool fEnabled; // pick (true) or un-pick (false) + hsPoint3 fHitPoint; // where on the picked object that it was picked on + +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(Spawned) + plKey fSpawner; // who spawned it (typicall plNPCSpawnMod) + plKey fSpawnee; // what was spawned (typically a scene object with an armature mod) + +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(ControlKey) + Int32 fControlKey; // what control key was hit + hsBool fDown; // was the key going down (false if going up) + +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(Variable) + char* fName; // name of variable + Int32 fDataType; // type of data + + // Can't be a union, sadly, but it isn't that much of a waste of space... + hsScalar fNumber; // if its a number + plKey fKey; // if its a plKey (pointer to something) + + +protected: + virtual void IInit(); + virtual void IDestruct(); + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(Facing) + plKey fFacer; // what was facing + plKey fFacee; // what was being faced + hsScalar dot; // the dot prod of their view vectors + hsBool enabled; // Now meets facing requirement (true) or no longer meets requirement (false) + +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(Contained) + plKey fContained; // who's inside of + plKey fContainer; // this containing volume + hsBool fEntering; // entering volume (true) or exiting (false) + +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(Activate) + hsBool fActive; // Whether or not to use the data in this field + hsBool fActivate; // the data + +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(Callback) + Int32 fEventType; // enumerated in plEventCallbackMsg.h + +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(ResponderState) + Int32 fState; // what state the responder should be switched to before triggering + +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(MultiStage) + Int32 fStage; + Int32 fEvent; + plKey fAvatar; // who was running the stage + +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(Coop) + UInt32 fID; // player ID of the initiator + UInt16 fSerial; // serial number for the initiator +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + + +proEventType(ClickDrag) + plKey picker; // always the local avatar in this case + plKey picked; + hsScalar animPos; // 0.0 to 1.0 animation percentage +}; + +proEventType(OfferLinkingBook) + plKey offerer; // the avatar offering the linking book to you + int targetAge; // the age the book is for - taken from konstant list of age buttons in xLinkingBookPopupGUI.py + int offeree; // who we are offering the book to +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(Book) + UInt32 fEvent; // The type of event. See pfJournalBook.h for enumsu + UInt32 fLinkID; // The link ID of the image clicked, if an image link event, otherwise unused +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + +proEventType(ClimbingBlockerHit) + plKey fBlockerKey; // collision hittee (probably us) + +protected: + virtual void IRead(hsStream* stream, hsResMgr* mgr); + virtual void IWrite(hsStream* stream, hsResMgr* mgr); + + virtual void IReadVersion(hsStream* s, hsResMgr* mgr); + virtual void IWriteVersion(hsStream* s, hsResMgr* mgr); +}; + + +///////////////////////////////////////////////////////////////////////////// +// +// MESSAGE : plNotifyMsg +// PARAMETERS : none +// +// PURPOSE : This is the message that notifies someone (either a responder or activator) +// : that some event or transition of state has happened +// +// +class plNotifyMsg : public plMessage +{ +protected: + virtual void IInit(); + +public: + plNotifyMsg() { IInit(); } + plNotifyMsg(const plKey &s, const plKey &r); + ~plNotifyMsg(); + + CLASSNAME_REGISTER( plNotifyMsg ); + GETINTERFACE_ANY( plNotifyMsg, plMessage ); + + // data area + enum notificationType + { + kActivator=0, + kVarNotification, + kNotifySelf, + + kResponderFF, // Fast forward + kResponderChangeState, // Change state without triggering + }; + Int32 fType; // what type of notification + hsScalar fState; // state of the notifier 0.0=false, 1.0=true + Int32 fID; // special ID mostly for responder State transitions + hsTArray fEvents;// list of events with data + + void SetType(notificationType type) { fType = type; } + void SetState(hsScalar state) { fState = state; } + + // event records for the notify message + void AddEvent( proEventData* ed); + void AddCollisionEvent( hsBool enter, const plKey &other, const plKey &self, hsBool onlyOneCollision=true ); + void AddPickEvent( const plKey &other, const plKey& self, hsBool enabled, hsPoint3 hitPoint ); + void AddControlKeyEvent( Int32 key, hsBool down ); + void AddVariableEvent( const char* name, hsScalar number ); + void AddVariableEvent( const char *name, const plKey &key); + void AddFacingEvent( const plKey &other, const plKey &self, hsScalar dot, hsBool enabled); + void AddContainerEvent( const plKey &container, const plKey &contained, hsBool entering); + void AddActivateEvent( hsBool activate ); + void AddCallbackEvent( Int32 event ); + void AddResponderStateEvent( Int32 state ); + void AddMultiStageEvent( Int32 stage, Int32 event, const plKey& avatar ); + void AddCoopEvent(UInt32 id, UInt16 serial); + void AddSpawnedEvent (const plKey &spawner, const plKey &spawned); + void AddClickDragEvent(const plKey& dragger, const plKey& dragee, hsScalar animPos); + void AddOfferBookEvent(const plKey& offerer, int targetAge, int offeree); + void AddBookEvent( UInt32 event, UInt32 linkID = 0 ); + void AddHitClimbingBlockerEvent(const plKey &blocker); + proEventData* FindEventRecord( Int32 eventtype ); + Int32 GetEventCount() { return fEvents.Count(); } + proEventData* GetEventRecord(Int32 i) { return fEvents[i]; } + void ClearEvents(); + + // Searches the event records for an event triggered by an avatar, and returns that key + plKey GetAvatarKey(); + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + + + +#endif // _plNotifyMsg_h_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plObjRefMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plObjRefMsg.h new file mode 100644 index 00000000..93f79e36 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plObjRefMsg.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plObjRefMsg_inc +#define plObjRefMsg_inc + +#include "plRefMsg.h" +#include "hsStream.h" + +class hsResMgr; + + +class plObjRefMsg : public plRefMsg +{ + +public: + enum { + kModifier = 0, + kInterface = 4 + }; + + plObjRefMsg(): fType(-1), fWhich(-1) {}; + + plObjRefMsg(const plKey &r, UInt8 refMsgFlags, Int8 which , Int8 type) + : plRefMsg(r, refMsgFlags), fType(type), fWhich(which) {} + + + CLASSNAME_REGISTER( plObjRefMsg ); + GETINTERFACE_ANY( plObjRefMsg, plRefMsg ); + + Int8 fType; + Int8 fWhich; + + // IO - not really applicable to ref msgs, but anyway + void Read(hsStream* stream, hsResMgr* mgr) + { + plRefMsg::Read(stream, mgr); + stream->ReadSwap(&fType); + stream->ReadSwap(&fWhich); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plRefMsg::Write(stream, mgr); + stream->WriteSwap(fType); + stream->WriteSwap(fWhich); + } +}; + +#endif // plObjRefMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plPipeResMakeMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plPipeResMakeMsg.h new file mode 100644 index 00000000..c3ec8f97 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plPipeResMakeMsg.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPipeResMakeMsg_inc +#define plPipeResMakeMsg_inc + +#include "../pnMessage/plMessage.h" + +class plPipeline; + +class plPipeResMakeMsg : public plMessage +{ +protected: + plPipeline* fPipe; +public: + + plPipeResMakeMsg() : plMessage(nil, nil, nil), fPipe(nil) { SetBCastFlag(kBCastByExactType); } + plPipeResMakeMsg(plPipeline* pipe) : plMessage(nil, nil, nil), fPipe(pipe) { SetBCastFlag(kBCastByExactType); } + + ~plPipeResMakeMsg() {} + + CLASSNAME_REGISTER( plPipeResMakeMsg ); + GETINTERFACE_ANY( plPipeResMakeMsg, plMessage ); + + plPipeline* Pipeline() const { return fPipe; } + + virtual void Read(hsStream* s, hsResMgr* mgr) { plMessage::IMsgRead(s, mgr); } + virtual void Write(hsStream* s, hsResMgr* mgr) { plMessage::IMsgWrite(s, mgr); } +}; + +class plPipeRTMakeMsg : public plPipeResMakeMsg +{ +public: + plPipeRTMakeMsg() : plPipeResMakeMsg() { } + plPipeRTMakeMsg(plPipeline* pipe) : plPipeResMakeMsg(pipe) { } + + ~plPipeRTMakeMsg() {} + + CLASSNAME_REGISTER( plPipeRTMakeMsg ); + GETINTERFACE_ANY( plPipeRTMakeMsg, plPipeResMakeMsg ); +}; + +class plPipeGeoMakeMsg : public plPipeResMakeMsg +{ +public: + plPipeGeoMakeMsg() : plPipeResMakeMsg(), fDefault(false) { } + plPipeGeoMakeMsg(plPipeline* pipe, hsBool def) : plPipeResMakeMsg(pipe), fDefault(def) { } + + ~plPipeGeoMakeMsg() {} + + CLASSNAME_REGISTER( plPipeGeoMakeMsg ); + GETINTERFACE_ANY( plPipeGeoMakeMsg, plPipeResMakeMsg ); + + hsBool fDefault; +}; + +class plPipeTexMakeMsg : public plPipeResMakeMsg +{ +public: + plPipeTexMakeMsg() : plPipeResMakeMsg() { } + plPipeTexMakeMsg(plPipeline* pipe) : plPipeResMakeMsg(pipe) { } + + ~plPipeTexMakeMsg() {} + + CLASSNAME_REGISTER( plPipeTexMakeMsg ); + GETINTERFACE_ANY( plPipeTexMakeMsg, plPipeResMakeMsg ); +}; + + +#endif // plPipeResMakeMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plPlayerPageMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plPlayerPageMsg.h new file mode 100644 index 00000000..e2590c8f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plPlayerPageMsg.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPlayerPageMsg_inc +#define plPlayerPageMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsResMgr.h" +#include "hsStream.h" + +class plKey; + +class plPlayerPageMsg : public plMessage +{ +protected: + +public: + plPlayerPageMsg() : fPlayer(nil),fLocallyOriginated(false),fUnload(false),fLastOut(false),fClientID(-1){;} + plPlayerPageMsg(const plKey &s, + const plKey &r, + const double* t) : fPlayer(nil),fLocallyOriginated(false),fUnload(false),fLastOut(false),fClientID(-1){;} + + CLASSNAME_REGISTER( plPlayerPageMsg ); + GETINTERFACE_ANY( plPlayerPageMsg, plMessage ); + + + plKey fPlayer; + hsBool fLocallyOriginated; + hsBool fUnload; + int fClientID; + hsBool fLastOut; + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fPlayer = mgr->ReadKey(stream); + fLocallyOriginated = stream->ReadBool(); + fLastOut = stream->ReadBool(); + fUnload = stream->ReadBool(); + fClientID = stream->ReadSwap32(); + } + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + mgr->WriteKey(stream, fPlayer); + stream->WriteBool(fLocallyOriginated); + stream->WriteBool(fLastOut); + stream->WriteBool(fUnload); + stream->WriteSwap32(fClientID); + } +}; + +#endif // plPlayerPageMsg_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plProxyDrawMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plProxyDrawMsg.cpp new file mode 100644 index 00000000..70eb76b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plProxyDrawMsg.cpp @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plProxyDrawMsg.h" +#include "hsStream.h" + +plProxyDrawMsg::plProxyDrawMsg() +: plMessage(nil, nil, nil), + fProxyFlags(0) +{ + SetBCastFlag(plMessage::kBCastByExactType); +} + +plProxyDrawMsg::plProxyDrawMsg(UInt16 flags) +: plMessage(nil, nil, nil), + fProxyFlags(flags) +{ + SetBCastFlag(plMessage::kBCastByExactType); +} + +plProxyDrawMsg::plProxyDrawMsg(plKey &rcv, UInt16 flags) +: plMessage(rcv, rcv, nil), + fProxyFlags(flags) +{ +} + +plProxyDrawMsg::~plProxyDrawMsg() +{ +} + +void plProxyDrawMsg::Read(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgRead(s, mgr); + fProxyFlags = s->ReadSwap16(); +} + +void plProxyDrawMsg::Write(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWrite(s, mgr); + s->WriteSwap16(fProxyFlags); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plProxyDrawMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plProxyDrawMsg.h new file mode 100644 index 00000000..225881af --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plProxyDrawMsg.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plProxyDrawMsg_inc +#define plProxyDrawMsg_inc + +#include "plMessage.h" + +class hsStream; +class hsResMgr; + +// Proxy Draw Msg's are sent out to tell +// proxies for the visual objects in the world to +// make themselves visible (or reclaim resources used +// to make themselves visible). This message should only +// be sent out by the core system. +class plProxyDrawMsg : public plMessage +{ +protected: + UInt16 fProxyFlags; + +public: + plProxyDrawMsg(); + plProxyDrawMsg(UInt16 flags); // for broadcast + plProxyDrawMsg(plKey &rcv, UInt16 flags); // send yourself an ack + ~plProxyDrawMsg(); + + CLASSNAME_REGISTER( plProxyDrawMsg ); + GETINTERFACE_ANY( plProxyDrawMsg, plMessage ); + + enum { + kCreate = 0x1, + kDestroy = 0x2, + kDetached = 0x4, + kToggle = 0x8, + + kLight = 0x10, + kPhysical = 0x20, + kOccluder = 0x40, + kAudible = 0x80, + kCoordinate = 0x100, + kCamera = 0x200, + + kAllTypes = kLight + | kPhysical + | kOccluder + | kAudible + | kCoordinate + | kCamera + }; + + UInt16 GetProxyFlags() const { return fProxyFlags; } + void SetProxyFlags(UInt16 f) { fProxyFlags = f; } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plProxyDrawMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRefMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRefMsg.cpp new file mode 100644 index 00000000..9703bb84 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRefMsg.cpp @@ -0,0 +1,99 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plRefMsg.h" +#include "hsStream.h" + +#include "hsResMgr.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/hsKeyedObject.h" + +plRefMsg::plRefMsg() +: fRef(nil), fOldRef(nil), fContext(0) +{ +} + +plRefMsg::plRefMsg(const plKey &r, UInt8 c) +: plMessage(nil, r, nil), fRef(nil), fOldRef(nil), fContext(c) +{ + if( !fContext ) + fContext = kOnCreate; +} + +plRefMsg::~plRefMsg() +{ + // Un ref fref and foldref. +} + +plRefMsg& plRefMsg::SetRef(hsKeyedObject* ref) +{ + fRef = ref; // ref count here! paulg + return *this; +} + +plRefMsg& plRefMsg::SetOldRef(hsKeyedObject* oldRef) +{ + fOldRef = oldRef; + // Ref here! + return *this; +} + +void plRefMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + stream->ReadSwap(&fContext); + + plKey key; + key = mgr->ReadKey(stream); + fRef = (key ? key->GetObjectPtr() : nil); + key = mgr->ReadKey(stream); + fOldRef = (key ? key->GetObjectPtr() : nil); +} + +void plRefMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwap(fContext); + + mgr->WriteKey(stream, (fRef ? fRef->GetKey() : nil)); + mgr->WriteKey(stream, (fOldRef ? fOldRef->GetKey() : nil)); +} + + +void plGenRefMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plRefMsg::Read(stream, mgr); + stream->ReadSwap(&fType); + fWhich = stream->ReadSwap32(); +} + +void plGenRefMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plRefMsg::Write(stream, mgr); + stream->WriteSwap(fType); + stream->WriteSwap32(fWhich); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRefMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRefMsg.h new file mode 100644 index 00000000..b22a9c5a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRefMsg.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plRefMsg_inc +#define plRefMsg_inc + +#include "plMessage.h" + +class hsStream; +class hsKeyedObject; + +class plRefMsg : public plMessage +{ +public: + enum Context + { + kOnCreate = 0x1, // fRef just created. + kOnDestroy = 0x2, // fRef about to be destroyed + kOnRequest = 0x4, // fRef wants to be added + kOnRemove = 0x8, // fRef has moved elsewhere + kOnReplace = 0x10 // fRef replaces fOldRef + }; + +protected: + hsKeyedObject* fRef; + hsKeyedObject* fOldRef; // on replace + + UInt8 fContext; +public: + plRefMsg(); + plRefMsg(const plKey &r, UInt8 c); + + virtual ~plRefMsg(); + + CLASSNAME_REGISTER( plRefMsg ); + GETINTERFACE_ANY( plRefMsg, plMessage ); + + plRefMsg& SetRef(hsKeyedObject* ref); + hsKeyedObject* GetRef() { return fRef; } + + plRefMsg& SetOldRef(hsKeyedObject* oldRef); + hsKeyedObject* GetOldRef() { return fOldRef; } + + plRefMsg& SetContext(UInt8 c) { fContext = c; return *this; } + UInt8 GetContext() { return fContext; } + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + + +class plGenRefMsg : public plRefMsg +{ +public: + plGenRefMsg() : fType(-1), fWhich(-1) {} + plGenRefMsg(const plKey &r, UInt8 c, Int32 which, Int8 type) : plRefMsg(r, c), fWhich(which), fType(type) {} + + CLASSNAME_REGISTER(plGenRefMsg); + GETINTERFACE_ANY(plGenRefMsg, plRefMsg); + + // User variables. You can put anything here, but the standard convention + // is an enum telling what type of ref it is in fType, and an index in + // fWhich, for keeping track of multiple refs of the same type. + Int8 fType; + Int32 fWhich; + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plRefMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRemoteAvatarInfoMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRemoteAvatarInfoMsg.h new file mode 100644 index 00000000..47750134 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plRemoteAvatarInfoMsg.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRemoteAvatarInfoMsg_inc +#define plRemoteAvatarInfoMsg_inc + +// +// this message is to fake out a gadget to see if it would potentially trigger... +// +#include "../pnMessage/plMessage.h" + +class hsStream; +class hsResMgr; +class plKey; + +class plRemoteAvatarInfoMsg : public plMessage +{ +protected: + + plKey fAvatar; +public: + plRemoteAvatarInfoMsg() : fAvatar(nil){SetBCastFlag(plMessage::kBCastByExactType);} + plRemoteAvatarInfoMsg(const plKey &s, + const plKey &r, + const double* t) : fAvatar(nil){SetBCastFlag(plMessage::kBCastByExactType);} + + CLASSNAME_REGISTER( plRemoteAvatarInfoMsg ); + GETINTERFACE_ANY( plRemoteAvatarInfoMsg, plMessage ); + + void SetAvatarKey(plKey p) { fAvatar = p; } + plKey GetAvatarKey() { return fAvatar; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fAvatar = mgr->ReadKey(stream); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + mgr->WriteKey(stream, fAvatar); + } +}; + +#endif // plRemoteAvatarInfoMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLModifierMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLModifierMsg.cpp new file mode 100644 index 00000000..5287d1bd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLModifierMsg.cpp @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSDLModifierMsg.h" +#include "../PubUtilLib/plSDL/plSDL.h" // ugh. + +plSDLModifierMsg::plSDLModifierMsg(const char* sdlName, Action a) : + fSDLName(nil), + fAction(a), + fState(nil), + fPlayerID(0), + fManageStateMem(false), + fFlags(0) +{ + SetSDLName(sdlName); + SetBCastFlag(plMessage::kPropagateToModifiers); +} + +plSDLModifierMsg::~plSDLModifierMsg() +{ + if ( fManageStateMem ) + delete fState; + delete [] fSDLName; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLModifierMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLModifierMsg.h new file mode 100644 index 00000000..e79b0c78 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLModifierMsg.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSDLModifierMsg_INC +#define plSDLModifierMsg_INC + +#include "../pnMessage/plMessage.h" +#include "hsUtils.h" + +// +// A msg sent to an SDL modifier to tell it send or recv state. +// +class hsStream; +class hsResMgr; +class plStateDataRecord; +class plSDLModifierMsg : public plMessage +{ +public: + enum Action + { + kActionNone = 0, + kRecv, + kSendToServer, + kSendToServerAndClients + }; + +protected: + char* fSDLName; // the state descriptor name (ie. "physical") + Action fAction; + plStateDataRecord* fState; // for recving state + bool fManageStateMem; // delete fState? + UInt32 fPlayerID; + UInt32 fFlags; + +public: + plSDLModifierMsg(const char* sdlName=nil, Action a=kActionNone); + ~plSDLModifierMsg(); + + CLASSNAME_REGISTER( plSDLModifierMsg ); + GETINTERFACE_ANY( plSDLModifierMsg, plMessage ); + + UInt32 GetFlags() const { return fFlags; } + void SetFlags(UInt32 f) { fFlags = f; } + + Action GetAction() const { return fAction; } + void SetAction(Action t) { fAction=t; } + + plStateDataRecord* GetState(bool unManageState=false) { if ( unManageState ) fManageStateMem=false; return fState; } + void SetState(plStateDataRecord* s, bool manageState) { fState=s; fManageStateMem=manageState; } + + const char* GetSDLName() const { return fSDLName; } + void SetSDLName(const char* s) { delete [] fSDLName; fSDLName=hsStrcpy(s); } + + UInt32 GetPlayerID() const { return fPlayerID; } + void SetPlayerID(UInt32 p) { fPlayerID=p; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { hsAssert(false, "local only msg"); } + void Write(hsStream* stream, hsResMgr* mgr) { hsAssert(false, "local only msg"); } +}; + +#endif // plSDLModifierMsg_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLNotificationMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLNotificationMsg.h new file mode 100644 index 00000000..d969e67a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSDLNotificationMsg.h @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSDLNotificationMsg_inc +#define plSDLNotificationMsg_inc + +#include "../pnMessage/plMessage.h" + +class plSimpleStateVariable; +class plSDLNotificationMsg : public plMessage +{ +public: + float fDelta; // change threshold + const plSimpleStateVariable* fVar; + std::string fSDLName; // name of state descriptor + int fPlayerID; // pid of the player who changed the data + std::string fHintString; // hint from the player who changed the data + + plSDLNotificationMsg() : fDelta(0), fVar(nil), fPlayerID(0) {} + ~plSDLNotificationMsg() { } + + CLASSNAME_REGISTER( plSDLNotificationMsg ); + GETINTERFACE_ANY( plSDLNotificationMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { hsAssert(false, "NA: LocalOnly msg"); } + void Write(hsStream* stream, hsResMgr* mgr) { hsAssert(false, "NA: LocalOnly msg"); } +}; + +#endif // plSDLNotificationMsg_in + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSatisfiedMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSatisfiedMsg.h new file mode 100644 index 00000000..3efd8306 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSatisfiedMsg.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSatisfiedMsg_inc +#define plSatisfiedMsg_inc + +#include "../pnMessage/plMessage.h" + +class plSatisfiedMsg : public plMessage +{ +public: + plSatisfiedMsg() {} + plSatisfiedMsg(const plKey &s) : plMessage(s, s, nil) {} + + CLASSNAME_REGISTER( plSatisfiedMsg ); + GETINTERFACE_ANY( plSatisfiedMsg, plMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr) { IMsgRead(stream, mgr); } + virtual void Write(hsStream* stream, hsResMgr* mgr) { IMsgWrite(stream, mgr); } + +}; + +#endif // plSatisfiedMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSelfDestructMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSelfDestructMsg.h new file mode 100644 index 00000000..dd0ca32a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSelfDestructMsg.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSelfDestructMsg_inc +#define plSelfDestructMsg_inc + +#include "plMessage.h" + +class plSelfDestructMsg : public plMessage +{ +protected: + plSelfDestructMsg(plKey &victim) : plMessage(victim, victim, nil) {} + + friend class plKeyImp; +public: + + plSelfDestructMsg() { hsAssert(false, "For system use only"); } + + CLASSNAME_REGISTER( plSelfDestructMsg ); + GETINTERFACE_ANY( plSelfDestructMsg, plMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr) { IMsgRead(stream, mgr); } + virtual void Write(hsStream* stream, hsResMgr* mgr) { IMsgWrite(stream, mgr); } + +}; + +#endif // plSelfDestructMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.cpp new file mode 100644 index 00000000..6b9b6fc2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.cpp @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plServerReplyMsg.h" +#include "hsStream.h" +#include "hsBitVector.h" + +void plServerReplyMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + stream->ReadSwap(&fType); +} + +void plServerReplyMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwap(fType); +} + +enum ServerReplyFlags +{ + kServerReplyType, +}; + +void plServerReplyMsg::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kServerReplyType)) + s->ReadSwap(&fType); +} + +void plServerReplyMsg::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kServerReplyType); + contentFlags.Write(s); + + // kServerReplyType + s->WriteSwap(fType); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h new file mode 100644 index 00000000..4fbdb7ab --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plServerReplyMsg.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plServerReplyMsg_inc +#define plServerReplyMsg_inc + +#include "plMessage.h" + +class hsStream; +class hsResMgr; + + +// +// Sent by server to confirm/deny a sharedState lock attempt. +// Used for trigger arbitration. +// Should not set the NetPropagate bcast flag, since it originates on the gameServer. +// +class plServerReplyMsg : public plMessage +{ + int fType; +public: + + enum + { + kUnInit = -1, + kDeny, + kAffirm, + }; + + void SetType(int t) { fType = t; } + int GetType() { return fType; } + + plServerReplyMsg() : fType(kUnInit) { } + plServerReplyMsg(const plKey &s, const plKey &r, const double* t) : plMessage(s,r,t), fType(kUnInit) { } + + CLASSNAME_REGISTER( plServerReplyMsg ); + GETINTERFACE_ANY( plServerReplyMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +#endif // plServerReplyMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSetNetGroupIDMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSetNetGroupIDMsg.h new file mode 100644 index 00000000..f51fc5be --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSetNetGroupIDMsg.h @@ -0,0 +1,45 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSetNetGroupIDMsg_h_inc +#define plSetNetGroupIDMsg_h_inc + +#include "plMessage.h" +#include "../pnNetCommon/plNetGroup.h" + +// Send this to a SynchedObject to change its NetGroupID +class plSetNetGroupIDMsg : public plMessage +{ +public: + CLASSNAME_REGISTER(plSetNetGroupIDMsg); + GETINTERFACE_ANY(plSetNetGroupIDMsg, plMessage); + + plNetGroupId fId; + + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); fId.Read(stream); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); fId.Write(stream); } +}; + +#endif // plSetNetGroupIDMsg_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSharedStateMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSharedStateMsg.h new file mode 100644 index 00000000..bc7ae16a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSharedStateMsg.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSharedStateMsg_inc +#define plSharedStateMsg_inc + +#include "plMessage.h" +#include "../pnNetCommon/plNetSharedState.h" + +class hsStream; +class hsResMgr; + +// +// Msg from server containing generic sharedState +// +class plSharedStateMsg : public plMessage +{ +protected: + plNetSharedState fSharedState; +public: + plSharedStateMsg() {} + plSharedStateMsg(const plKey &s, const plKey &r, const double* t) : plMessage(s,r,t) {} + + CLASSNAME_REGISTER( plSharedStateMsg ); + GETINTERFACE_ANY( plSharedStateMsg, plMessage ); + + void CopySharedState(plNetSharedState* ss) { fSharedState.Copy(ss); } + plNetSharedState* GetSharedState() { return &fSharedState; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); fSharedState.Write(stream); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); fSharedState.Read(stream); } +}; + +#endif // plSharedStateMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationMsg.cpp new file mode 100644 index 00000000..7d569643 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationMsg.cpp @@ -0,0 +1,37 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSimulationMsg.h" + +void plSimulationMsg::Read(hsStream* stream, hsResMgr *mgr) +{ + IMsgRead(stream, mgr); +} + +void plSimulationMsg::Write(hsStream* stream, hsResMgr *mgr) +{ + IMsgWrite(stream, mgr); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationMsg.h new file mode 100644 index 00000000..7b52ef8d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationMsg.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLSIMULATIONMSG_H +#define PLSIMULATIONMSG_H + +#include "../pnMessage/plMessage.h" + +// PLSIMULATIONMSG +// Virtual base class for all messages which are specific to the simulation interface +// (and the simulation pool objects) +// Most messages which are intended to go through the Simulation interface to the pool +// objects (without being explictly relayed by the Simulation interface) should subclass +// from this +class plSimulationMsg : public plMessage +{ +public: + // pass-through constructors + plSimulationMsg() : plMessage() {}; + plSimulationMsg(const plKey &sender, const plKey &receiver, const double *time) : plMessage(sender, receiver, time) {}; + +CLASSNAME_REGISTER( plSimulationMsg ); + GETINTERFACE_ANY( plSimulationMsg, plMessage ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + +#endif // PLSIMULATIONMSG_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationSynchMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationSynchMsg.h new file mode 100644 index 00000000..318d696f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSimulationSynchMsg.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLSIMULATIONSYNCHMSG_h +#define PLSIMULATIONSYNCHMSG_h + +#include "plMessage.h" + +// PLSIMULATIONSYNCHMSG +// periodically sent from one simulated object to another to ensure consistent state +// this is a completely virtual message type +// more meaningful sub-classes are created by specific physics systems +class plSimulationSynchMsg : public plMessage +{ +public: + + // ??? + // This message is not really creatable: it's abstract. It's designed to sneak + // havok-specific data through the generalized simulation logic + CLASSNAME_REGISTER( plSimulationSynchMsg ); + GETINTERFACE_ANY( plSimulationSynchMsg, plMessage ); + + // Don't be fooled: this class is *not* to be instantiated. + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); +}; + + +inline void plSimulationSynchMsg::Read(hsStream *s, hsResMgr *mgr) +{ + hsAssert(false, "plSimulationSynchMsg should never be instantiated directly"); +} + +inline void plSimulationSynchMsg::Write(hsStream *s, hsResMgr *mgr) +{ + hsAssert(false, "plSimulationSynchMsg should never be instantiated directly"); +} + +#endif // PLSIMULATIONSYNCHMSG_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSingleModMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSingleModMsg.h new file mode 100644 index 00000000..a69d9c60 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSingleModMsg.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSingleModMsg_inc +#define plSingleModMsg_inc + +#include "plMessage.h" +#include "hsBitVector.h" + +class hsStream; +class hsResMgr; + + +class plSingleModMsg : public plMessage +{ +public: + plSingleModMsg() + : plMessage(nil, nil, nil) {} + plSingleModMsg(const plKey &s, + const plKey &r, + const double* t) + : plMessage(s, r, t) {} + ~plSingleModMsg() {} + + CLASSNAME_REGISTER( plSingleModMsg ); + GETINTERFACE_ANY( plSingleModMsg, plMessage ); + + enum ModCmds + { + }; + + hsBitVector fCmd; + + hsBool Cmd(int n) { return fCmd.IsBitSet(n); } + void SetCmd(int n) { fCmd.SetBit(n); } + void ClearCmd() { fCmd.Clear(); } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fCmd.Read(stream); + } + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + fCmd.Write(stream); + } +}; + +#endif // plSingleModMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSoundMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSoundMsg.cpp new file mode 100644 index 00000000..c4866f14 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSoundMsg.cpp @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plSoundMsg.h" +#include "hsStream.h" + +plSoundMsg::~plSoundMsg() +{ + ClearCmd(); +} + + +void plSoundMsg::ClearCmd() +{ + plMessageWithCallbacks::Clear(); + fCmd.Clear(); +} + + +void plSoundMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessageWithCallbacks::Read(stream, mgr); + + fCmd.Read(stream); + stream->ReadSwap(&fBegin); + stream->ReadSwap(&fEnd); + fLoop = stream->ReadBool(); + fPlaying = stream->ReadBool(); + stream->ReadSwap(&fSpeed); + stream->ReadSwap(&fTime); + stream->ReadSwap(&fIndex); + stream->ReadSwap(&fRepeats); + stream->ReadSwap(&fNameStr); + stream->ReadSwap(&fVolume); + fFadeType = (plSoundMsg::FadeType)stream->ReadByte(); +} + +void plSoundMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessageWithCallbacks::Write(stream, mgr); + + fCmd.Write(stream); + stream->WriteSwap(fBegin); + stream->WriteSwap(fEnd); + stream->WriteBool(fLoop); + stream->WriteBool(fPlaying); + stream->WriteSwap(fSpeed); + stream->WriteSwap(fTime); + stream->WriteSwap(fIndex); + stream->WriteSwap(fRepeats); + stream->WriteSwap(fNameStr); + stream->WriteSwap(fVolume); + stream->WriteByte( (UInt8)fFadeType ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSoundMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSoundMsg.h new file mode 100644 index 00000000..877bb0df --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plSoundMsg.h @@ -0,0 +1,106 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSoundMsg_h +#define plSoundMsg_h + +#include "plMessageWithCallbacks.h" +#include "hsTemplates.h" +#include "hsBitVector.h" + +class plSoundMsg : public plMessageWithCallbacks +{ +private: + void IInit() { fLoop=false; fPlaying = false; fBegin=fEnd=fTime=fRepeats=0; fSpeed = 0.f; fVolume = 0.f; fIndex = -1; fNameStr = 0; fFadeType = kLinear; } +public: + plSoundMsg() + : plMessageWithCallbacks(nil, nil, nil) { IInit(); } + plSoundMsg(const plKey &s, + const plKey &r, + const double* t) + : plMessageWithCallbacks(s, r, t) { IInit(); } + ~plSoundMsg(); + + CLASSNAME_REGISTER( plSoundMsg ); + GETINTERFACE_ANY( plSoundMsg, plMessageWithCallbacks ); + + enum ModCmds + { + kPlay=0, + kStop, + kSetLooping, + kUnSetLooping, + kSetBegin, + kToggleState, + kAddCallbacks, + kRemoveCallbacks, + kGetStatus, + kGetNumSounds, + kStatusReply, + kGoToTime, + kSetVolume, + kSetTalkIcon, + kClearTalkIcon, + kSetFadeIn, + kSetFadeOut, + kIsLocalOnly, // Not really a command, just a flag + kSelectFromGroup, + kNumCmds, + kFastForwardPlay, + kFastForwardToggle, + kSynchedPlay, + }; + + hsBitVector fCmd; + + hsBool Cmd(int n) const { return fCmd.IsBitSet(n); } + void SetCmd(int n) { fCmd.SetBit(n); } + void ClearCmd(); + + double fBegin; + double fEnd; + hsBool fLoop; + hsScalar fSpeed; + double fTime; + int fIndex; + int fRepeats; + hsBool fPlaying; + UInt32 fNameStr; + hsScalar fVolume; // Range: 0 - silence, 1.f - loudest + + enum FadeType + { + kLinear, + kLogarithmic, + kExponential + } fFadeType; + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + + +#endif //plWin32Sound_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plTimeMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plTimeMsg.cpp new file mode 100644 index 00000000..ee2762b0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plTimeMsg.cpp @@ -0,0 +1,80 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plTimeMsg.h" +#include "hsTimer.h" + +plTimeMsg::plTimeMsg() +: plMessage(nil, nil, nil), fSeconds(0), fDelSecs(0) +{ +} + +plTimeMsg::plTimeMsg(const plKey &s, + const plKey &r, + const double* t, const hsScalar* d) +: plMessage(s, r, t) +{ + fSeconds = t ? *t : hsTimer::GetSysSeconds(); + fDelSecs = d ? *d : hsTimer::GetDelSysSeconds(); + + SetBCastFlag(plMessage::kBCastByExactType); +} + +plTimeMsg::~plTimeMsg() +{ +} + +plEvalMsg::plEvalMsg() +{ +} + +plEvalMsg::plEvalMsg(const plKey &s, + const plKey &r, + const double* t, const hsScalar* d) +: plTimeMsg(s, r, t, d) +{ +} + +plEvalMsg::~plEvalMsg() +{ +} + +plTransformMsg::plTransformMsg() +{ +} + +plTransformMsg::plTransformMsg(const plKey &s, + const plKey &r, + const double* t, const hsScalar* d) +: plTimeMsg(s, r, t, d) +{ + SetBCastFlag(plMessage::kClearAfterBCast); +} + +plTransformMsg::~plTransformMsg() +{ +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plTimeMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plTimeMsg.h new file mode 100644 index 00000000..a4626f54 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plTimeMsg.h @@ -0,0 +1,118 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plTimeMsg_inc +#define plTimeMsg_inc + +#include "plMessage.h" +#include "hsStream.h" + +class plTimeMsg : public plMessage +{ +protected: + double fSeconds; + hsScalar fDelSecs; + +public: + plTimeMsg(); + plTimeMsg(const plKey &s, + const plKey &r, + const double* t, const hsScalar* del); + ~plTimeMsg(); + + CLASSNAME_REGISTER( plTimeMsg ); + GETINTERFACE_ANY( plTimeMsg, plMessage ); + + plTimeMsg& SetSeconds(double s) { fSeconds = s; return *this; } + plTimeMsg& SetDelSeconds(hsScalar d) { fDelSecs = d; return *this; } + + double DSeconds() { return fSeconds; } + hsScalar DelSeconds() { return fDelSecs; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + stream->ReadSwap(&fSeconds); + stream->ReadSwap(&fDelSecs); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwap(fSeconds); + stream->WriteSwap(fDelSecs); + } +}; + +class plEvalMsg : public plTimeMsg +{ +public: + plEvalMsg(); + plEvalMsg(const plKey &s, + const plKey &r, + const double* t, const hsScalar* del); + ~plEvalMsg(); + + CLASSNAME_REGISTER( plEvalMsg ); + GETINTERFACE_ANY( plEvalMsg, plTimeMsg ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plTimeMsg::Read(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { plTimeMsg::Write(stream, mgr); } +}; + +class plTransformMsg : public plTimeMsg +{ +public: + plTransformMsg(); + plTransformMsg(const plKey &s, + const plKey &r, + const double* t, const hsScalar* del); + ~plTransformMsg(); + + CLASSNAME_REGISTER( plTransformMsg ); + GETINTERFACE_ANY( plTransformMsg, plTimeMsg ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plTimeMsg::Read(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { plTimeMsg::Write(stream, mgr); } +}; + +// Does the same thing as plTransformMsg, but as you might guess from the name, it's sent later. +// It's a separate message type so that objects can register for just the delayed message when +// it's broadcast. +class plDelayedTransformMsg : public plTransformMsg +{ +public: + plDelayedTransformMsg() : plTransformMsg() {} + plDelayedTransformMsg(const plKey &s, const plKey &r, const double* t, const hsScalar* del) : plTransformMsg(s, r, t, del) {} + + CLASSNAME_REGISTER( plDelayedTransformMsg ); + GETINTERFACE_ANY( plDelayedTransformMsg, plTransformMsg ); +}; + +#endif // plTimeMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plWarpMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plWarpMsg.h new file mode 100644 index 00000000..857e4a08 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/plWarpMsg.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plWarpMsg_inc +#define plWarpMsg_inc + +#include "hsStream.h" +#include "plMessage.h" +#include "hsMatrix44.h" + +class plWarpMsg : public plMessage +{ +private: + UInt32 fWarpFlags; + hsMatrix44 fTransform; +public: + enum WarpFlags + { + kFlushTransform = 0x1, + kZeroVelocity = 0x2 + }; + + plWarpMsg() { Clear(); } + plWarpMsg(const hsMatrix44& mat ){ Clear(); fTransform = mat; } + plWarpMsg(const plKey &s, + const plKey &r, + const double* t) { Clear(); } + plWarpMsg(const plKey &s, const plKey &r, UInt32 flags, const hsMatrix44 &mat) + : fWarpFlags(flags), fTransform(mat), plMessage(s, r, nil) + { }; + + ~plWarpMsg(){} + + CLASSNAME_REGISTER( plWarpMsg ); + GETINTERFACE_ANY( plWarpMsg, plMessage ); + + void Clear() { fWarpFlags=0; } + + UInt32 GetWarpFlags() { return fWarpFlags; } + void SetWarpFlags(UInt32 f) { fWarpFlags=f; } + + void SetTransform(const hsMatrix44& mat) { fTransform=mat; } + hsMatrix44& GetTransform() { return fTransform; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fTransform.Read(stream); + stream->ReadSwap(&fWarpFlags); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + fTransform.Write(stream); + stream->WriteSwap(fWarpFlags); + } +}; + +#endif // plWarpMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/pnMessageCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/pnMessageCreatable.h new file mode 100644 index 00000000..55f2e6bb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnMessage/pnMessageCreatable.h @@ -0,0 +1,183 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pnMessageCreatable_inc +#define pnMessageCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plMessage.h" + +REGISTER_NONCREATABLE( plMessage ); + +#include "plRefMsg.h" + +REGISTER_CREATABLE( plRefMsg ); +REGISTER_CREATABLE( plGenRefMsg ); + +#include "plObjRefMsg.h" + +REGISTER_CREATABLE( plObjRefMsg ); + +#include "plIntRefMsg.h" + +REGISTER_CREATABLE( plIntRefMsg ); + +#include "plNodeRefMsg.h" + +REGISTER_CREATABLE( plNodeRefMsg ); + +#include "plSatisfiedMsg.h" + +REGISTER_CREATABLE( plSatisfiedMsg ); + +#include "plSingleModMsg.h" + +REGISTER_CREATABLE( plSingleModMsg ); + +#include "plMultiModMsg.h" + +REGISTER_CREATABLE( plMultiModMsg ); + +#include "plTimeMsg.h" + +REGISTER_CREATABLE( plTimeMsg ); +REGISTER_CREATABLE( plEvalMsg ); +REGISTER_CREATABLE( plTransformMsg ); +REGISTER_CREATABLE( plDelayedTransformMsg ); + +#include "plWarpMsg.h" + +REGISTER_CREATABLE( plWarpMsg ); + +#include "plAttachMsg.h" + +REGISTER_CREATABLE( plAttachMsg ); + +#include "plCorrectionMsg.h" + +REGISTER_CREATABLE( plCorrectionMsg ); + +#include "plSoundMsg.h" + +REGISTER_CREATABLE( plSoundMsg ); + +#include "plAudioSysMsg.h" + +REGISTER_CREATABLE( plAudioSysMsg ); + +#include "plEnableMsg.h" + +REGISTER_CREATABLE( plEnableMsg ); + +#include "plServerReplyMsg.h" + +REGISTER_CREATABLE( plServerReplyMsg ); + +#include "plSharedStateMsg.h" +REGISTER_CREATABLE( plSharedStateMsg ); + +#include "plClientMsg.h" +REGISTER_CREATABLE( plClientMsg ); +REGISTER_CREATABLE( plClientRefMsg ); + +#include "plSimulationMsg.h" + +REGISTER_NONCREATABLE( plSimulationMsg ); + +#include "plSimulationSynchMsg.h" + +REGISTER_NONCREATABLE( plSimulationSynchMsg ); + +#include "plProxyDrawMsg.h" + +REGISTER_CREATABLE( plProxyDrawMsg ); + +#include "plEventCallbackMsg.h" + +REGISTER_CREATABLE( plEventCallbackMsg ); +REGISTER_CREATABLE( plEventCallbackInterceptMsg ); + +#include "plSelfDestructMsg.h" + +REGISTER_CREATABLE( plSelfDestructMsg ); + +#include "plCameraMsg.h" + +REGISTER_CREATABLE( plCameraMsg ); +REGISTER_CREATABLE( plCameraTargetFadeMsg ); +REGISTER_CREATABLE( plIfaceFadeAvatarMsg ); + +#include "plPlayerPageMsg.h" + +REGISTER_CREATABLE( plPlayerPageMsg ); + +#include "plCmdIfaceModMsg.h" + +REGISTER_CREATABLE( plCmdIfaceModMsg ); + +#include "plNotifyMsg.h" + +REGISTER_CREATABLE( plNotifyMsg ); + +#include "plFakeOutMsg.h" + +REGISTER_CREATABLE( plFakeOutMsg ); + +#include "plCursorChangeMsg.h" + +REGISTER_CREATABLE( plCursorChangeMsg ); + +#include "plNodeChangeMsg.h" + +REGISTER_CREATABLE( plNodeChangeMsg ); + +#include "plMessageWithCallbacks.h" +REGISTER_CREATABLE( plMessageWithCallbacks ); + +#include "plRemoteAvatarInfoMsg.h" +REGISTER_CREATABLE( plRemoteAvatarInfoMsg ); + +#include "plSDLModifierMsg.h" +REGISTER_CREATABLE(plSDLModifierMsg); + +#include "plSDLNotificationMsg.h" +REGISTER_CREATABLE(plSDLNotificationMsg); + +#include "plPipeResMakeMsg.h" +REGISTER_CREATABLE(plPipeResMakeMsg); +REGISTER_CREATABLE(plPipeRTMakeMsg); +REGISTER_CREATABLE(plPipeGeoMakeMsg); +REGISTER_CREATABLE(plPipeTexMakeMsg); + +#include "plDISpansMsg.h" +REGISTER_CREATABLE(plDISpansMsg); + +#include "plSetNetGroupIDMsg.h" +REGISTER_CREATABLE(plSetNetGroupIDMsg); + +#endif // pnMessageCreatable_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plConditionalObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plConditionalObject.cpp new file mode 100644 index 00000000..7cca1222 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plConditionalObject.cpp @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plConditionalObject.h" +#include "plLogicModBase.h" + +plConditionalObject::plConditionalObject() : +bSatisfied(false), +fToggle(false), +fLogicMod(nil), +fReset(false) +{ + +} + +plConditionalObject::~plConditionalObject() +{ + +} + + +///////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////// + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plConditionalObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plConditionalObject.h new file mode 100644 index 00000000..8b88ddaa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plConditionalObject.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plConditionalObject_inc +#define plConditionalObject_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsBitVector.h" +#include "../pnNetCommon/plSynchedValue.h" + +class plLogicModBase; + +class plConditionalObject : public hsKeyedObject +{ +private: + // since 'this' is not derived from synchedObject, its synched values must be associated + // with it's logicModifier (which is a synchedObject). Thus it's a synched value 'friend'. + hsBool bSatisfied; + hsBool fToggle; +public: + enum + { + kLocalElement = 0, + kNOT, + }; +protected: + plLogicModBase* fLogicMod; + hsBitVector fFlags; + hsBool fReset; +public: + plConditionalObject(); + virtual ~plConditionalObject(); + + CLASSNAME_REGISTER( plConditionalObject ); + GETINTERFACE_ANY( plConditionalObject, hsKeyedObject ); + + virtual void Read(hsStream* stream, hsResMgr* mgr) { hsKeyedObject::Read(stream, mgr); bSatisfied = stream->ReadBool(); fToggle = stream->ReadBool();} + virtual void Write(hsStream* stream, hsResMgr* mgr){ hsKeyedObject::Write(stream, mgr); stream->WriteBool( bSatisfied ); stream->WriteBool(fToggle);} + + virtual void SetLogicMod(plLogicModBase* pMod) { fLogicMod = pMod; } + +// virtual hsBool MsgReceive(plMessage* msg) = 0; + + virtual hsBool Satisfied() { if(HasFlag(kNOT)) return !bSatisfied; else return bSatisfied; } + void SetSatisfied(hsBool b) { bSatisfied=b; } + hsBool IsToggle() { return fToggle; } + void SetToggle(hsBool b) { fToggle = b; } + + // this is used if condtiton 1 is dependent on another condition's state at the + // time of a message coming into condition 1; + virtual hsBool Verify(plMessage* msg) { return true; } + + virtual void Evaluate() = 0; + + virtual void Reset() = 0; + virtual hsBool ResetOnTrigger() { return fReset; } + + hsBool HasFlag(int f) const { return fFlags.IsBitSet(f); } + void SetFlag(int f) { fFlags.SetBit(f); } + void ClearFlag(int which) { fFlags.ClearBit( which ); } + + +}; + + +#endif // plConditionalObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp new file mode 100644 index 00000000..b5b91a0b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.cpp @@ -0,0 +1,387 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plLogicModBase.h" +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "hsTimer.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnNetCommon/plGenericVar.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnNetCommon/plNetSharedState.h" +#include "../../PubUtilLib/plNetMessage/plNetMessage.h" // breaks project dependancy levels +#include "../pnMessage/plNotifyMsg.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plServerReplyMsg.h" + +void plLogicModBase::ConsoleTrigger(plKey playerKey) +{ + // Setup the event data in case this is a OneShot responder that needs it + proPickedEventData *ed = TRACKED_NEW proPickedEventData; + ed->fPicker = playerKey; + ed->fPicked = nil; + fNotify->AddEvent(ed); + + Trigger(false); + + // Whoops, trigger and untrigger use the same message, so if we do this right away + // it will just untrigger twice. So...uhh, we don't untrigger! +// UnTrigger(); +} + +void plLogicModBase::ConsoleRequestTrigger() +{ + RequestTrigger(); +} + +plLogicModBase::plLogicModBase() : +fCounter(0), +fCounterLimit(0), +fTimer(0.0f), +fNotify(nil), +fDisabled(false) +{ + fNotify = TRACKED_NEW plNotifyMsg; +} + +plLogicModBase::~plLogicModBase() +{ + int i; + for (i = 0; i < fCommandList.Count(); i++ ) + { + hsRefCnt_SafeUnRef(fCommandList[i]); + } + + hsRefCnt_SafeUnRef(fNotify); +} + +void plLogicModBase::AddTarget(plSceneObject* so) +{ + plSingleModifier::AddTarget(so); +} + +void plLogicModBase::RegisterForMessageType(UInt16 hClass) +{ + plgDispatch::Dispatch()->RegisterForExactType( hClass, GetKey() ); +} + +// +// Update generic shared state (which reflects trigger state) on server +// by sending TestAndSet request. By locking and unlocking the sharedState, +// we can guarantee that only one logicMod instance can trigger at a time. +// The server will confirm or deny our request to lock and set the state. +// +void plLogicModBase::IUpdateSharedState(bool triggered) const +{ + plNetSharedState ss("TrigState"); + plGenericVar* sv = TRACKED_NEW plGenericVar("Triggered"); + sv->Value().SetBool(triggered); // attempting to set trig state to true + ss.AddVar(sv); + + bool lock = triggered; + + // if unlocking, then the server does not need to store this state, since it's back to its default state + ss.SetServerMayDelete(!lock); + + plNetMsgTestAndSet ts; + ts.SetNetProtocol(kNetProtocolCli2Game); + ts.CopySharedState(&ss); + ts.ObjectInfo()->SetFromKey(GetKey()); + ts.SetLockRequest(lock); // if triggering, lock state, else unlock state + plNetClientApp::GetInstance()->SendMsg(&ts); + plNetClientApp::GetInstance()->DebugMsg("\tLM: Attempting to set logic mod shared lock to %s, t=%f\n", + triggered ? "Triggered" : "UnTriggered", hsTimer::GetSysSeconds()); +} + +hsBool plLogicModBase::MsgReceive(plMessage* msg) +{ + // read messages: + plServerReplyMsg* pSMsg = plServerReplyMsg::ConvertNoRef(msg); + if (pSMsg) + { + hsAssert(pSMsg->GetType() != plServerReplyMsg::kUnInit, "uninit server reply msg"); + +#if 1 + char str[256]; + sprintf(str, "LM: LogicModifier %s recvd trigger request reply:%s, wasRequesting=%d, t=%f\n", GetKeyName(), + pSMsg->GetType() == plServerReplyMsg::kDeny ? "denied" : "confirmed", + HasFlag(kRequestingTrigger), hsTimer::GetSysSeconds()); + plNetClientApp::GetInstance()->DebugMsg(str); +#endif + + if (pSMsg->GetType() == plServerReplyMsg::kDeny) + { + if (HasFlag(kRequestingTrigger)) + { + plNetClientApp::GetInstance()->DebugMsg("\tLM: Denied, clearing requestingTrigger"); + ClearFlag(kRequestingTrigger); + } + else + plNetClientApp::GetInstance()->DebugMsg("\tLM: Denied, but not requesting?"); + } + else + { + hsBool netRequest=false; // we're triggering as a result of a local activation + PreTrigger(netRequest); + IUpdateSharedState(false /* untriggering */); + } + return true; + } + + plEnableMsg* pEnable = plEnableMsg::ConvertNoRef(msg); + if (pEnable) + { + if (pEnable->Cmd(plEnableMsg::kDisable)) + fDisabled = true; + else + if (pEnable->Cmd(plEnableMsg::kEnable)) + { + ClearFlag(kTriggered); + ClearFlag(kRequestingTrigger); + + fDisabled = false; + } + return true; + } + + return plSingleModifier::MsgReceive(msg); +} + +void plLogicModBase::RequestTrigger(hsBool netRequest) +{ + if (HasFlag(kTriggered)) + { +#if 1 + char str[256]; + sprintf(str, "LM: %s ignoring RequestTrigger(), already triggered, t=%f\n", GetKeyName(), + hsTimer::GetSysSeconds()); + plNetClientApp::GetInstance()->DebugMsg(str); +#endif + return; + } + + if (HasFlag(kRequestingTrigger)) + { +#if 1 + char str[256]; + sprintf(str, "LM: %s ignoring RequestTrigger(), already requesting trigger, t=%f\n", GetKeyName(), + hsTimer::GetSysSeconds()); + plNetClientApp::GetInstance()->DebugMsg(str); +#endif + + return; + } + if ( plNetApp::GetInstance()->GetFlagsBit(plNetClientApp::kLocalTriggers)) + { + PreTrigger(false); + } + else + { + IUpdateSharedState(true /* triggering */); // request arbitration from server + SetFlag(kRequestingTrigger); + +#if 1 + char str[256]; + sprintf(str, "LM: %s Setting RequestingTriggert=%f\n", GetKeyName(), hsTimer::GetSysSeconds()); + plNetClientApp::GetInstance()->DebugMsg(str); +#endif + + } +} + +// +// return false is counter test fails +// +hsBool plLogicModBase::IEvalCounter() +{ + if (fCounterLimit > 0) + { + fCounter = fCounter + 1; + if (fCounter != fCounterLimit) + { + Reset(false); + return false; + } + } + return true; +} + +void plLogicModBase::PreTrigger(hsBool netRequest) +{ + if (fDisabled) + return; + + Trigger(netRequest); +} + +void plLogicModBase::Trigger(hsBool netRequest) +{ +#if 1 + char str[256]; + sprintf(str, "LogicModifier %s is triggering, activatorType=%d\n", + GetKeyName(), HasFlag(kTypeActivator)); + plNetClientApp::GetInstance()->DebugMsg(str); +#endif + + ClearFlag(kRequestingTrigger); + if (!HasFlag(kMultiTrigger)) + SetFlag(kTriggered); + fNotify->SetSender(this->GetKey()); + fNotify->SetState(1.0f); + fNotify->AddActivateEvent(true); +// hsRefCnt_SafeRef(fNotify); + plgDispatch::MsgSend( fNotify ); + CreateNotifyMsg(); + if (HasFlag(kOneShot)) + fDisabled = true; +} + +void plLogicModBase::UnTrigger() +{ + if (!HasFlag(kTriggered)) + return; + +#ifdef HS_DEBUGGING + char str[256]; + sprintf(str, "LogicModifier %s is Un-triggering, activatorType=%d\n", + GetKeyName(), HasFlag(kTypeActivator)); + plNetClientApp::GetInstance()->DebugMsg(str); +#endif + fNotify->SetSender(this->GetKey()); + fNotify->SetState(0.0f); + fNotify->AddActivateEvent(false); +// hsRefCnt_SafeRef(fNotify); + plgDispatch::MsgSend( fNotify ); + CreateNotifyMsg(); + Reset(true); +} + +void plLogicModBase::Reset(bool bCounterReset) +{ + ClearFlag(kTriggered); + if (bCounterReset) + fCounter = 0; +} + +void plLogicModBase::CreateNotifyMsg() +{ + fNotify = TRACKED_NEW plNotifyMsg; + for (int i = 0; i < fReceiverList.Count(); i++) + fNotify->AddReceiver(fReceiverList[i]); +} + +void plLogicModBase::AddNotifyReceiver(plKey receiver) +{ + fReceiverList.Append(receiver); + fNotify->AddReceiver(receiver); +} + +void plLogicModBase::Read(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Read(stream, mgr); + int n = stream->ReadSwap32(); + fCommandList.SetCountAndZero(n); + for(int i = 0; i < n; i++ ) + { + plMessage* pMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); + fCommandList[i] = pMsg; + } + if (fNotify) + delete fNotify; + plNotifyMsg* pNMsg = plNotifyMsg::ConvertNoRef(mgr->ReadCreatable(stream)); + fNotify = pNMsg; + + fFlags.Read(stream); + fDisabled = stream->Readbool(); + for (int d = 0; d < fNotify->GetNumReceivers(); d++) + fReceiverList.Append(fNotify->GetReceiver(d)); +} + +void plLogicModBase::Write(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Write(stream, mgr); + stream->WriteSwap32(fCommandList.GetCount()); + for(int i = 0; i < fCommandList.GetCount(); i++ ) + mgr->WriteCreatable( stream, fCommandList[i] ); + mgr->WriteCreatable( stream, fNotify ); + fFlags.Write(stream); + stream->Writebool(fDisabled); +} + + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Maintainers Marker Component +// +// +#if 0 +#include "../plModifier/plMaintainersMarkerModifier.h" + +//Class that accesses the paramblock below. +class plMaintainersMarkerComponent : public plComponent +{ +public: + plMaintainersMarkerComponent(); + void DeleteThis() { delete this; } + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary. +CLASS_DESC(plMaintainersMarkerComponent, gMaintainersDesc, "Maintainers Marker", "MaintainersMarker", COMP_TYPE_TYPE, Class_ID(0x7d7f1f72, 0x405355f5)) + +//The MAX paramblock stuff below +ParamBlockDesc2 gMaintainersBk +( + 1, _T("maintainersMarker"), 0, &gMaintainersDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + end +); + +plMaintainersMarkerComponent::plMaintainersMarkerComponent() +{ + fClassDesc = &gMaintainersDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plMaintainersMarkerComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetForceLocal(true); + return true; +} +hsBool plMaintainersMarkerComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plMaintainersMarkerModifier* pSpawn = TRACKED_NEW plMaintainersMarkerModifier; + node->AddModifier(pSpawn); + return true; +} + +hsBool plMaintainersMarkerComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h new file mode 100644 index 00000000..773cd96a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h @@ -0,0 +1,112 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLogicModBase_inc +#define plLogicModBase_inc + +#include "plSingleModifier.h" +#include "../pnNetCommon/plSynchedValue.h" +#include "hsTemplates.h" + +class plConditionalObject; +class plSceneObject; +class plNotifyMsg; +class plVolumeSensorConditionalObjectNoArbitration; +class plLogicModBase : public plSingleModifier +{ +public: + enum Flags + { + kLocalElement = 0, + kReset, + kTriggered, + kOneShot, + kRequestingTrigger, + kTypeActivator, // this LogicMod is part of an Activator Component (not a Responder) + kMultiTrigger, + }; + +protected: + hsTArray fCommandList; + hsTArray fReceiverList; + UInt32 fCounterLimit; + hsScalar fTimer; + hsBitVector fFlags; + UInt32 fCounter; + plNotifyMsg* fNotify; + bool fDisabled; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) {return false;} + void IUpdateSharedState(bool triggered) const; + hsBool IEvalCounter(); + virtual void PreTrigger(hsBool netRequest); + virtual void Trigger(hsBool netRequest); + virtual void UnTrigger(); + + void CreateNotifyMsg(); + +public: + friend plVolumeSensorConditionalObjectNoArbitration; + plLogicModBase(); + ~plLogicModBase(); + CLASSNAME_REGISTER( plLogicModBase ); + GETINTERFACE_ANY( plLogicModBase, plSingleModifier ); + + void AddTarget(plSceneObject* so); + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + virtual hsBool VerifyConditions(plMessage* msg) { return true;} + + virtual void Reset(bool bCounterReset); + + void SetDisabled(bool disabled) { fDisabled = disabled; } + bool Disabled() { return fDisabled; } + + plNotifyMsg* GetNotify() { return fNotify; } + + void AddCommand(plMessage* msg) { fCommandList.Append(msg); } + void SetOneShot(hsBool b) { if (b) SetFlag(kOneShot); else ClearFlag(kOneShot); } + void RegisterForMessageType(UInt16 hClass); + + virtual void RequestTrigger(hsBool netRequest=false); + virtual void RequestUnTrigger() { UnTrigger(); } + + hsBool HasFlag(int f) const { return fFlags.IsBitSet(f); } + void SetFlag(int f) { fFlags.SetBit(f); } + void ClearFlag(int which) { fFlags.ClearBit(which); } + + void AddNotifyReceiver(plKey receiver); + + // for debug purposes only! + void ConsoleTrigger(plKey playerKey); + void ConsoleRequestTrigger(); +}; + + + +#endif // plLogicModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plModifier.cpp new file mode 100644 index 00000000..e19620c8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plModifier.cpp @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plModifier.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../pnMessage/plTimeMsg.h" + +plModifier::plModifier() +{ +} + +plModifier::~plModifier() +{ +} + +plDrawInterface* plModifier::IGetTargetDrawInterface(int iTarg) const +{ + return GetTarget(iTarg) ? GetTarget(iTarg)->GetVolatileDrawInterface() : nil; +} + +plSimulationInterface* plModifier::IGetTargetSimulationInterface(int iTarg) const +{ + return GetTarget(iTarg) ? GetTarget(iTarg)->GetVolatileSimulationInterface() : nil; +} + +plCoordinateInterface* plModifier::IGetTargetCoordinateInterface(int iTarg) const +{ + return GetTarget(iTarg) ? GetTarget(iTarg)->GetVolatileCoordinateInterface() : nil; +} + +plAudioInterface* plModifier::IGetTargetAudioInterface(int iTarg) const +{ + return GetTarget(iTarg) ? GetTarget(iTarg)->GetVolatileAudioInterface() : nil; +} + +plObjInterface* plModifier::IGetTargetGenericInterface(int iTarg, UInt32 classIdx) const +{ + return GetTarget(iTarg) ? GetTarget(iTarg)->GetVolatileGenericInterface((UInt16)classIdx) : nil; +} + +plModifier* plModifier::IGetTargetModifier(int iTarg, int iMod) const +{ + return GetTarget(iTarg) ? GetTarget(iTarg)->GetVolatileModifier(iMod) : nil; +} + +hsBool plModifier::MsgReceive(plMessage* msg) +{ + plEvalMsg* eval = plEvalMsg::ConvertNoRef(msg); + if( eval ) + { + UInt32 dirty = ~0L; + IEval(eval->DSeconds(), eval->DelSeconds(), dirty); + return true; + } + + return plSynchedObject::MsgReceive(msg); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plModifier.h new file mode 100644 index 00000000..376f4cea --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plModifier.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plModifier_inc +#define plModifier_inc + +#include "../pnNetCommon/plSynchedObject.h" + +class hsStream; +class hsResMgr; +class plMessage; +class plSceneObject; +class plObjInterface; +class plDrawInterface; +class plSimulationInterface; +class plCoordinateInterface; +class plAudioInterface; +struct hsMatrix44; + +class plModifier : public plSynchedObject +{ +protected: + + plDrawInterface* IGetTargetDrawInterface(int iTarg) const; + plSimulationInterface* IGetTargetSimulationInterface(int iTarg) const; + plCoordinateInterface* IGetTargetCoordinateInterface(int iTarg) const; + plAudioInterface* IGetTargetAudioInterface(int iTarg) const; + plObjInterface* IGetTargetGenericInterface(int iTarg, UInt32 classIdx) const; + plModifier* IGetTargetModifier(int iTarg, int iMod) const; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) = 0; // called only by owner object's Eval() + + friend class plSceneObject; +public: + + plModifier(); + virtual ~plModifier(); + + CLASSNAME_REGISTER( plModifier ); + GETINTERFACE_ANY( plModifier, plSynchedObject ); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual int GetNumTargets() const = 0; + virtual plSceneObject* GetTarget(int iTarg) const = 0; + virtual void AddTarget(plSceneObject* so) = 0; + virtual void RemoveTarget(plSceneObject* so) = 0; + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) {} + +}; + +#endif // plModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plMultiModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plMultiModifier.cpp new file mode 100644 index 00000000..2c63afed --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plMultiModifier.cpp @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plMultiModifier.h" + +#include "hsStream.h" + +plMultiModifier::plMultiModifier() +{ +} + +plMultiModifier::~plMultiModifier() +{ +} + +void plMultiModifier::Read(hsStream* s, hsResMgr* mgr) +{ + plModifier::Read(s, mgr); + fFlags.Read(s); +} + +void plMultiModifier::Write(hsStream* s, hsResMgr* mgr) +{ + plModifier::Write(s, mgr); + fFlags.Write(s); +} + +void plMultiModifier::RemoveTarget(plSceneObject* so) +{ + for (int i=0; i< fTargets.Count(); i++) + { + if (fTargets[i] == so) + { + fTargets.Remove(i); + return; + } + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plMultiModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plMultiModifier.h new file mode 100644 index 00000000..7609de9e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plMultiModifier.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMultiModifier_inc +#define plMultiModifier_inc + +#include "plModifier.h" +#include "hsBitVector.h" +#include "../pnNetCommon/plSynchedValue.h" +#include "hsTemplates.h" + +class plSceneObject; +class plMultiModMsg; + +class plMultiModifier : public plModifier +{ +protected: + hsTArray fTargets; + hsBitVector fFlags; + +public: + plMultiModifier(); + virtual ~plMultiModifier(); + + CLASSNAME_REGISTER( plMultiModifier ); + GETINTERFACE_ANY( plMultiModifier, plModifier ); + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) = 0; + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual int GetNumTargets() const { return fTargets.Count(); } + virtual plSceneObject* GetTarget(int w) const { hsAssert(w < GetNumTargets(), "Bad target"); return fTargets[w]; } + virtual void AddTarget(plSceneObject* so) {fTargets.Append(so);} + virtual void RemoveTarget(plSceneObject* so); + + hsBool HasFlag(int f) const { return fFlags.IsBitSet(f); } + plMultiModifier& SetFlag(int f) { fFlags.SetBit(f); return *this; } + +}; + +#endif // plMultiModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plSingleModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plSingleModifier.cpp new file mode 100644 index 00000000..1a5133d0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plSingleModifier.cpp @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plSingleModifier.h" + +#include "hsStream.h" + +plSingleModifier::plSingleModifier() +: + fTarget(nil) +{ +} + +plSingleModifier::~plSingleModifier() +{ +} + +void plSingleModifier::Read(hsStream* s, hsResMgr* mgr) +{ + plModifier::Read(s, mgr); + fFlags.Read(s); +} + +void plSingleModifier::Write(hsStream* s, hsResMgr* mgr) +{ + plModifier::Write(s, mgr); + + fFlags.Write(s); + +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plSingleModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plSingleModifier.h new file mode 100644 index 00000000..a218d8c6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/plSingleModifier.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSingleModifier_inc +#define plSingleModifier_inc + +#include "plModifier.h" +#include "hsBitVector.h" +#include "../pnNetCommon/plSynchedValue.h" + +class plSceneObject; +class plSingleModMsg; + +class plSingleModifier : public plModifier +{ +protected: + plSceneObject* fTarget; + hsBitVector fFlags; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) = 0; + +public: + plSingleModifier(); + virtual ~plSingleModifier(); + + CLASSNAME_REGISTER( plSingleModifier ); + GETINTERFACE_ANY( plSingleModifier, plModifier ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual int GetNumTargets() const { return 1; } + virtual plSceneObject* GetTarget(int iTarg) const {return fTarget;} + virtual void AddTarget(plSceneObject* so) {SetTarget(so);} + virtual void RemoveTarget(plSceneObject* so) {fTarget = 0;} + + + virtual plSceneObject* GetTarget() const { return fTarget; } + virtual void SetTarget(plSceneObject* so) { fTarget = so; } + + hsBool HasFlag(int f) const { return fFlags.IsBitSet(f); } + plSingleModifier& SetFlag(int f) { fFlags.SetBit(f); return *this; } + plSingleModifier& ClearFlag(int f) { fFlags.ClearBit(f); return *this; } + +}; + +#endif // plSingleModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/pnModifierCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/pnModifierCreatable.h new file mode 100644 index 00000000..d29569d4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnModifier/pnModifierCreatable.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pnModifierCreatable_inc +#define pnModifierCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plModifier.h" + +REGISTER_NONCREATABLE( plModifier ); + +#include "plSingleModifier.h" + +REGISTER_NONCREATABLE( plSingleModifier ); + +#include "plMultiModifier.h" + +REGISTER_NONCREATABLE( plMultiModifier ); + +#include "plConditionalObject.h" + +REGISTER_NONCREATABLE( plConditionalObject ); + +#include "plLogicModBase.h" + +REGISTER_NONCREATABLE( plLogicModBase ); + + + +#endif // pnModifierCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Intern.h new file mode 100644 index 00000000..0b21e630 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Intern.h @@ -0,0 +1,38 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_INTERN_H + + +#include "Private/pnNbAllIncludes.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Pch.h new file mode 100644 index 00000000..3f3b43c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Pch.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PCH_H + + +#include "../pnUtils/pnUtils.h" +#include "../pnProduct/pnProduct.h" + +#include "Private/pnNbAllIncludes.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAges.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAges.cpp new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAges.cpp @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAges.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAges.h new file mode 100644 index 00000000..d3ad9148 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAges.h @@ -0,0 +1,35 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAges.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBAGES_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAges.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBAGES_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAllIncludes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAllIncludes.h new file mode 100644 index 00000000..c591cfb9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAllIncludes.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbAllIncludes.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBALLINCLUDES_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBALLINCLUDES_H + + +#include "../pnProduct/pnProduct.h" + +#include "pnNbConst.h" +#include "pnNbError.h" +#include "pnNbProtocol.h" +#include "pnNbSrvs.h" + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBALLINCLUDES_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbEchoMsgs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbEchoMsgs.h new file mode 100644 index 00000000..bac9d791 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbEchoMsgs.h @@ -0,0 +1,153 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbEchoMsgs.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBECHOMSGS_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbEchoMsgs.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBECHOMSGS_H + + +//============================================================================ +// Begin Echo server data types +//============================================================================ +namespace Echo { + + +/***************************************************************************** +* +* @@@: Shared data types for echo server/client demo +* +***/ + + +const unsigned kEchoMsgFlagBroadcast = 1<<0; +const unsigned kEchoMsgFlagEchoBack = 1<<1; + + +enum EEchoNetBufferType { + kEchoNetBufferInvalidType, + kEchoNetBufferMsg, + kNumEchoNetBufferTypes +}; + +enum EEchoMsgType { + kEchoInvalidMsgType, + kEchoJoinRequestMsg, + kEchoJoinReplyMsg, + kEchoPlayerJoinedMsg, + kEchoPlayerLeftMsg, + kEchoChatMsg, + kEchoGameDataBufferMsg, + kNumEchoMsgTypes +}; +COMPILER_ASSERT(kNumEchoMsgTypes < (byte)-1); + + +//============================================================================ +// Begin packed data structures +//============================================================================ +#include + +struct EchoMsg { + EchoMsg (const EEchoMsgType & type, unsigned flags = 0) + : type((byte)type) + , flags(flags) + { } + + byte type; + dword flags; +}; + +struct EchoJoinRequestMsg : EchoMsg { + EchoJoinRequestMsg () + : EchoMsg(kEchoJoinRequestMsg) + { } + + wchar playerName[64]; +}; + +struct EchoJoinReplyMsg : EchoMsg { + EchoJoinReplyMsg () + : EchoMsg(kEchoJoinReplyMsg) + { } + + dword playerId; +}; + +struct EchoPlayerJoinedMsg : EchoMsg { + EchoPlayerJoinedMsg () + : EchoMsg(kEchoPlayerJoinedMsg, kEchoMsgFlagBroadcast) + { } + + dword playerId; + wchar playerName[64]; +}; + +struct EchoPlayerLeftMsg : EchoMsg { + EchoPlayerLeftMsg () + : EchoMsg(kEchoPlayerLeftMsg, kEchoMsgFlagBroadcast) + { } + + dword playerId; +}; + +struct EchoChatMsg : EchoMsg { + EchoChatMsg () + : EchoMsg(kEchoChatMsg, kEchoMsgFlagBroadcast|kEchoMsgFlagEchoBack) + { } + + dword fromPlayerId; + dword msgChars; + wchar msgBuffer[1]; // [msgChars], actually + // no more fields after variable length allocation +}; + +struct EchoGameDataBufferMsg : EchoMsg { + EchoGameDataBufferMsg () + : EchoMsg(kEchoGameDataBufferMsg, kEchoMsgFlagBroadcast|kEchoMsgFlagEchoBack) + { } + + dword fromPlayerId; + dword bufferBytes; + byte bufferData[1]; // [bufferBytes], actually + // no more fields after variable length allocation +}; + +//============================================================================ +// End packed data structures +//============================================================================ +#include + + +//============================================================================ +// End Echo server data types +//============================================================================ +} using namespace Echo; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbError.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbError.cpp new file mode 100644 index 00000000..0b63d1ca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbError.cpp @@ -0,0 +1,176 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbError.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exported functions +* +***/ + +//============================================================================ +// These errors should only be used in debugging. They should not be shown +// in release clients because they are not localized +const wchar * NetErrorToString (ENetError code) { + + static wchar * s_errors[] = { + L"Success", // kNetSuccess + L"Internal Error", // kNetErrInternalError + L"No Response From Server", // kNetErrTimeout + L"Invalid Server Data", // kNetErrBadServerData + L"Age Not Found", // kNetErrAgeNotFound + L"Network Connection Failed", // kNetErrConnectFailed + L"Disconnected From Server", // kNetErrDisconnected + L"File Not Found", // kNetErrFileNotFound + L"Old Build", // kNetErrOldBuildId + L"Remote Shutdown", // kNetErrRemoteShutdown + L"Database Timeout", // kNetErrTimeoutOdbc + L"Account Already Exists", // kNetErrAccountAlreadyExists + L"Player Already Exists", // kNetErrPlayerAlreadyExists + L"Account Not Found", // kNetErrAccountNotFound + L"Player Not Found", // kNetErrPlayerNotFound + L"Invalid Parameter", // kNetErrInvalidParameter + L"Name Lookup Failed", // kNetErrNameLookupFailed + L"Logged In Elsewhere", // kNetErrLoggedInElsewhere + L"Vault Node Not Found", // kNetErrVaultNodeNotFound + L"Max Players On Account", // kNetErrMaxPlayersOnAcct + L"Authentication Failed", // kNetErrAuthenticationFailed + L"State Object Not Found", // kNetErrStateObjectNotFound + L"Login Denied", // kNetErrLoginDenied + L"Circular Reference", // kNetErrCircularReference + L"Account Not Activated", // kNetErrAccountNotActivated + L"Key Already Used", // kNetErrKeyAlreadyUsed + L"Key Not Found", // kNetErrKeyNotFound + L"Activation Code Not Found", // kNetErrActivationCodeNotFound + L"Player Name Invalid", // kNetErrPlayerNameInvalid + L"Not Supported", // kNetErrNotSupported + L"Service Forbidden", // kNetErrServiceForbidden + L"Auth Token Too Old", // kNetErrAuthTokenTooOld + L"Must Use GameTap Client", // kNetErrMustUseGameTapClient + L"Too Many Failed Logins", // kNetErrTooManyFailedLogins + L"GameTap: Connection Failed", // kNetErrGameTapConnectionFailed + L"GameTap: Too Many Auth Options", // kNetErrGTTooManyAuthOptions + L"GameTap: Missing Parameter", // kNetErrGTMissingParameter + L"GameTap: Server Error", // kNetErrGTServerError + L"Account has been banned", // kNetErrAccountBanned + L"Account kicked by CCR", // kNetErrKickedByCCR + L"Wrong score type for operation", // kNetErrScoreWrongType + L"Not enough points", // kNetErrScoreNotEnoughPoints + L"Non-fixed score already exists", // kNetErrScoreAlreadyExists + L"No score data found", // kNetErrScoreNoDataFound + L"Invite: Couldn't find player", // kNetErrInviteNoMatchingPlayer + L"Invite: Too many hoods", // kNetErrInviteTooManyHoods + L"Payments not up to date", // kNetErrNeedToPay + L"Server Busy", // kNetErrServerBusy + L"Vault Node Access Violation", // kNetErrVaultNodeAccessViolation + }; + COMPILER_ASSERT(arrsize(s_errors) == kNumNetErrors); + + if ((unsigned)code >= arrsize(s_errors)) { + if (code == kNetPending) + return L"Pending"; + return L"Unknown Error"; + } + + return s_errors[code]; +} + +//============================================================================ +// These errors should only be used in debugging. They should not be shown +// in release clients because they are not localized +const wchar * NetErrorAsString (ENetError code) { + + #define ERROR_STRING(e) L#e + static wchar * s_errors[] = { + ERROR_STRING(kNetSuccess), + ERROR_STRING(kNetErrInternalError), + ERROR_STRING(kNetErrTimeout), + ERROR_STRING(kNetErrBadServerData), + ERROR_STRING(kNetErrAgeNotFound), + ERROR_STRING(kNetErrConnectFailed), + ERROR_STRING(kNetErrDisconnected), + ERROR_STRING(kNetErrFileNotFound), + ERROR_STRING(kNetErrOldBuildId), + ERROR_STRING(kNetErrRemoteShutdown), + ERROR_STRING(kNetErrTimeoutOdbc), + ERROR_STRING(kNetErrAccountAlreadyExists), + ERROR_STRING(kNetErrPlayerAlreadyExists), + ERROR_STRING(kNetErrAccountNotFound), + ERROR_STRING(kNetErrPlayerNotFound), + ERROR_STRING(kNetErrInvalidParameter), + ERROR_STRING(kNetErrNameLookupFailed), + ERROR_STRING(kNetErrLoggedInElsewhere), + ERROR_STRING(kNetErrVaultNodeNotFound), + ERROR_STRING(kNetErrMaxPlayersOnAcct), + ERROR_STRING(kNetErrAuthenticationFailed), + ERROR_STRING(kNetErrStateObjectNotFound), + ERROR_STRING(kNetErrLoginDenied), + ERROR_STRING(kNetErrCircularReference), + ERROR_STRING(kNetErrAccountNotActivated), + ERROR_STRING(kNetErrKeyAlreadyUsed), + ERROR_STRING(kNetErrKeyNotFound), + ERROR_STRING(kNetErrActivationCodeNotFound), + ERROR_STRING(kNetErrPlayerNameInvalid), + ERROR_STRING(kNetErrNotSupported), + ERROR_STRING(kNetErrServiceForbidden), + ERROR_STRING(kNetErrAuthTokenTooOld), + ERROR_STRING(kNetErrMustUseGameTapClient), + ERROR_STRING(kNetErrTooManyFailedLogins), + ERROR_STRING(kNetErrGameTapConnectionFailed), + ERROR_STRING(kNetErrGTTooManyAuthOptions), + ERROR_STRING(kNetErrGTMissingParameter), + ERROR_STRING(kNetErrGTServerError), + ERROR_STRING(kNetErrAccountBanned), + ERROR_STRING(kNetErrKickedByCCR), + ERROR_STRING(kNetErrScoreWrongType), + ERROR_STRING(kNetErrScoreNotEnoughPoints), + ERROR_STRING(kNetErrScoreAlreadyExists), + ERROR_STRING(kNetErrScoreNoDataFound), + ERROR_STRING(kNetErrInviteNoMatchingPlayer), + ERROR_STRING(kNetErrInviteTooManyHoods), + ERROR_STRING(kNetErrNeedToPay), + ERROR_STRING(kNetErrServerBusy), + ERROR_STRING(kNetErrVaultNodeAccessViolation), + }; + COMPILER_ASSERT(arrsize(s_errors) == kNumNetErrors); + + if ((unsigned)code >= arrsize(s_errors)) { + if (code == kNetPending) + return ERROR_STRING(kNetPending); + return L"ErrUnknown"; + } + #undef ERROR_STRING + + return s_errors[code]; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbError.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbError.h new file mode 100644 index 00000000..273b246a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbError.h @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbError.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBERROR_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbError.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBERROR_H + + +/***************************************************************************** +* +* Net Error +* +***/ + +// These codes may not be changed unless ALL servers, clients and databases +// are simultaneously updated; so basically forget it =) +enum ENetError { + // codes <= 0 are not errors + kNetPending = -1, + kNetSuccess = 0, + + // codes > 0 are errors + kNetErrInternalError = 1, + kNetErrTimeout = 2, + kNetErrBadServerData = 3, + kNetErrAgeNotFound = 4, + kNetErrConnectFailed = 5, + kNetErrDisconnected = 6, + kNetErrFileNotFound = 7, + kNetErrOldBuildId = 8, + kNetErrRemoteShutdown = 9, + kNetErrTimeoutOdbc = 10, + kNetErrAccountAlreadyExists = 11, + kNetErrPlayerAlreadyExists = 12, + kNetErrAccountNotFound = 13, + kNetErrPlayerNotFound = 14, + kNetErrInvalidParameter = 15, + kNetErrNameLookupFailed = 16, + kNetErrLoggedInElsewhere = 17, + kNetErrVaultNodeNotFound = 18, + kNetErrMaxPlayersOnAcct = 19, + kNetErrAuthenticationFailed = 20, + kNetErrStateObjectNotFound = 21, + kNetErrLoginDenied = 22, + kNetErrCircularReference = 23, + kNetErrAccountNotActivated = 24, + kNetErrKeyAlreadyUsed = 25, + kNetErrKeyNotFound = 26, + kNetErrActivationCodeNotFound = 27, + kNetErrPlayerNameInvalid = 28, + kNetErrNotSupported = 29, + kNetErrServiceForbidden = 30, + kNetErrAuthTokenTooOld = 31, + kNetErrMustUseGameTapClient = 32, + kNetErrTooManyFailedLogins = 33, + kNetErrGameTapConnectionFailed = 34, + kNetErrGTTooManyAuthOptions = 35, + kNetErrGTMissingParameter = 36, + kNetErrGTServerError = 37, + kNetErrAccountBanned = 38, + kNetErrKickedByCCR = 39, + kNetErrScoreWrongType = 40, + kNetErrScoreNotEnoughPoints = 41, + kNetErrScoreAlreadyExists = 42, + kNetErrScoreNoDataFound = 43, + kNetErrInviteNoMatchingPlayer = 44, + kNetErrInviteTooManyHoods = 45, + kNetErrNeedToPay = 46, + kNetErrServerBusy = 47, + kNetErrVaultNodeAccessViolation = 48, + + // Be sure to add strings for additional error codes in pnNbError.cpp + // (don't worry, the compiler will tell you if one is missing ;) + kNumNetErrors, + + // Net messages require ENetError to be sizeof(dword) + kNetErrorForceDword = (dword) -1 +}; + +COMPILER_ASSERT_HEADER(pnNbError, sizeof(ENetError) == sizeof(dword)); + +#define IS_NET_ERROR(a) (((int)(a)) > kNetSuccess) +#define IS_NET_SUCCESS(a) (((int)(a)) == kNetSuccess) + + +const wchar * NetErrorToString (ENetError code); // user-friendly string +const wchar * NetErrorAsString (ENetError code); // string version of enum identifier diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbProtocol.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbProtocol.cpp new file mode 100644 index 00000000..7205c230 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbProtocol.cpp @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbProtocol.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +const wchar * NetProtocolToString (ENetProtocol protocol) { + + #define PROTOCOL_STRING(p) { p, L#p } + static struct { ENetProtocol protocol; const wchar *name; } s_protocols[] = { + PROTOCOL_STRING(kNetProtocolNil), + + // For test applications + PROTOCOL_STRING(kNetProtocolDebug), + + // Client connections + { kNetProtocolCli2Csr, L"GateKeeper Server" }, + { kNetProtocolCli2Csr, L"Csr Server" }, + { kNetProtocolCli2Auth, L"Auth Server" }, + { kNetProtocolCli2Game, L"Game Server" }, + { kNetProtocolCli2File, L"File Server" }, + PROTOCOL_STRING(kNetProtocolCli2Unused_01), + + // Server connections + PROTOCOL_STRING(kNetProtocolSrvConn), + PROTOCOL_STRING(kNetProtocolSrv2Mcp), + PROTOCOL_STRING(kNetProtocolSrv2Vault), + PROTOCOL_STRING(kNetProtocolSrv2Db), + PROTOCOL_STRING(kNetProtocolSrv2State), + PROTOCOL_STRING(kNetProtocolSrv2Log), + PROTOCOL_STRING(kNetProtocolSrv2Score), + }; + #undef PROTOCOL_STRING + + for (unsigned i = 0; i < arrsize(s_protocols); ++i) + if (s_protocols[i].protocol == protocol) + return s_protocols[i].name; + + return L"Unknown protocol id"; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbProtocol.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbProtocol.h new file mode 100644 index 00000000..11eb5938 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbProtocol.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbProtocol.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBPROTOCOL_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbProtocol.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBPROTOCOL_H + + +/***************************************************************************** +* +* Net protocols +* +***/ + +const unsigned kNetProtocolServerBit = 0x80; + +// These codes may not be changed unless ALL servers and clients are +// simultaneously replaced; so basically forget it =) +enum ENetProtocol { + kNetProtocolNil = 0, + + // For test applications + kNetProtocolDebug = 1, + + // Client connections + kNetProtocolCli2GateKeeper = 2, + kNetProtocolCli2Csr = 3, + kNetProtocolCli2Auth = 4, + kNetProtocolCli2Game = 5, + kNetProtocolCli2File = 6, + kNetProtocolCli2Unused_01 = 7, + + // Server connections + kNetProtocolSrvConn = 0 | kNetProtocolServerBit, + kNetProtocolSrv2Mcp = 1 | kNetProtocolServerBit, + kNetProtocolSrv2Vault = 2 | kNetProtocolServerBit, + kNetProtocolSrv2Db = 3 | kNetProtocolServerBit, + kNetProtocolSrv2State = 4 | kNetProtocolServerBit, + kNetProtocolSrv2Log = 5 | kNetProtocolServerBit, + kNetProtocolSrv2Score = 6 | kNetProtocolServerBit, +}; + +// NOTE: When adding a new net protocol, be sure to update +// NetProtocolToString as well. Unfortunately, the compiler +// cannot enforce this since the protocol values are not +// numerically sequential. +const wchar * NetProtocolToString (ENetProtocol protocol); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.cpp new file mode 100644 index 00000000..2e59d8d1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.cpp @@ -0,0 +1,262 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + +#ifndef BUILD_TYPE +# error "pnProduct not included" +#endif + + +/***************************************************************************** +* +* Local data +* +***/ + +//============================================================================ +// Auth +//============================================================================ +static const wchar * s_authAddrs[] = { + +#if BUILD_TYPE == BUILD_TYPE_DEV + L"shard" +#elif BUILD_TYPE == BUILD_TYPE_QA + L"marrim" +#elif BUILD_TYPE == BUILD_TYPE_TEST + L"test-auth.urulive.com" +#elif BUILD_TYPE == BUILD_TYPE_BETA + L"beta-auth.urulive.com" +#elif BUILD_TYPE == BUILD_TYPE_LIVE + L"184.73.198.22" //L"auth.urulive.com" +#else +# error "Unknown build type" +#endif + +}; +static wchar s_authAddrCmdLine[64]; +static const wchar * s_authAddrsOverride[] = { + s_authAddrCmdLine +}; + + +//============================================================================ +// File +//============================================================================ +static const wchar * s_fileAddrs[] = { + +#if BUILD_TYPE == BUILD_TYPE_DEV + L"shard" +#elif BUILD_TYPE == BUILD_TYPE_QA + L"marrim" +#elif BUILD_TYPE == BUILD_TYPE_TEST + L"test-file.urulive.com" +#elif BUILD_TYPE == BUILD_TYPE_BETA + L"beta-file.urulive.com" +#elif BUILD_TYPE == BUILD_TYPE_LIVE + L"67.202.54.141" //unused +#else +# error "Unknown build type" +#endif + +}; +static wchar s_fileAddrCmdLine[64]; +static const wchar * s_fileAddrsOverride[] = { + s_fileAddrCmdLine +}; + + +//============================================================================ +// Csr +//============================================================================ +static const wchar * s_csrAddrs[] = { + +#if BUILD_TYPE == BUILD_TYPE_DEV + L"localhost" +#elif BUILD_TYPE == BUILD_TYPE_QA + L"localhost" +#elif BUILD_TYPE == BUILD_TYPE_TEST + L"localhost" +#elif BUILD_TYPE == BUILD_TYPE_BETA + L"beta-csr.urulive.com" +#elif BUILD_TYPE == BUILD_TYPE_LIVE + L"localhost" +#else +# error "Unknown build type" +#endif + +}; +static wchar s_csrAddrCmdLine[64]; +static const wchar * s_csrAddrsOverride[] = { + s_csrAddrCmdLine +}; + + +//============================================================================ +// GateKeeper +//============================================================================ +static const wchar * s_gateKeeperAddrs[] = { + +#if BUILD_TYPE == BUILD_TYPE_DEV + L"localhost" +#elif BUILD_TYPE == BUILD_TYPE_QA + L"localhost" +#elif BUILD_TYPE == BUILD_TYPE_TEST + L"localhost" +#elif BUILD_TYPE == BUILD_TYPE_BETA + L"beta-csr.urulive.com" +#elif BUILD_TYPE == BUILD_TYPE_LIVE + L"184.73.198.22" +#else +# error "Unknown build type" +#endif + +}; +static wchar s_gateKeeperAddrCmdLine[64]; +static const wchar * s_gateKeeperAddrsOverride[] = { + s_gateKeeperAddrCmdLine +}; + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +// Auth +//============================================================================ +unsigned GetAuthSrvHostnames (const wchar *** addrs) { + + if (s_authAddrCmdLine[0]) { + *addrs = s_authAddrsOverride; + return arrsize(s_authAddrsOverride); + } + else { + *addrs = s_authAddrs; + return arrsize(s_authAddrs); + } +} + +//============================================================================ +void SetAuthSrvHostname (const wchar addr[]) { + + StrCopy(s_authAddrCmdLine, addr, arrsize(s_authAddrCmdLine)); +} + +//============================================================================ +bool AuthSrvHostnameOverride () { + + return s_authAddrCmdLine[0]; +} + +//============================================================================ +// File +//============================================================================ +unsigned GetFileSrvHostnames (const wchar *** addrs) { + + if (s_fileAddrCmdLine[0]) { + *addrs = s_fileAddrsOverride; + return arrsize(s_fileAddrsOverride); + } + else { + *addrs = s_fileAddrs; + return arrsize(s_fileAddrs); + } +} + +//============================================================================ +void SetFileSrvHostname (const wchar addr[]) { + + StrCopy(s_fileAddrCmdLine, addr, arrsize(s_fileAddrCmdLine)); +} + +//============================================================================ +bool FileSrvHostnameOverride () { + + return s_fileAddrCmdLine[0]; +} + +//============================================================================ +// Csr +//============================================================================ +unsigned GetCsrSrvHostnames (const wchar *** addrs) { + + if (s_csrAddrCmdLine[0]) { + *addrs = s_csrAddrsOverride; + return arrsize(s_csrAddrsOverride); + } + else { + *addrs = s_csrAddrs; + return arrsize(s_csrAddrs); + } +} + +//============================================================================ +void SetCsrSrvHostname (const wchar addr[]) { + + StrCopy(s_csrAddrCmdLine, addr, arrsize(s_csrAddrCmdLine)); +} + +//============================================================================ +bool CsrSrvHostnameOverride () { + + return s_csrAddrCmdLine[0]; +} + + +//============================================================================ +// GateKeeper +//============================================================================ +unsigned GetGateKeeperSrvHostnames (const wchar *** addrs) { + + if (s_gateKeeperAddrCmdLine[0]) { + *addrs = s_gateKeeperAddrsOverride; + return arrsize(s_gateKeeperAddrsOverride); + } + else { + *addrs = s_gateKeeperAddrs; + return arrsize(s_gateKeeperAddrs); + } +} + +//============================================================================ +void SetGateKeeperSrvHostname (const wchar addr[]) { + StrCopy(s_gateKeeperAddrCmdLine, addr, arrsize(s_gateKeeperAddrCmdLine)); +} + +//============================================================================ +bool GateKeeperSrvHostnameOverride () { + return s_gateKeeperAddrCmdLine[0]; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.h new file mode 100644 index 00000000..dc69be73 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.h @@ -0,0 +1,90 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBSRVS_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/Private/pnNbSrvs.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PRIVATE_PNNBSRVS_H + + +/***************************************************************************** +* +* Server types +* +***/ + +// These codes may not be changed unless ALL servers and clients are +// simultaneously replaced; so basically forget it =) +enum ESrvType { + kSrvTypeNone = 0, + + kSrvTypeClient = 1, + kSrvTypeAuth = 2, + kSrvTypeGame = 3, + kSrvTypeVault = 4, + kSrvTypeDb = 5, + kSrvTypeMcp = 6, + kSrvTypeState = 7, + kSrvTypeFile = 8, + kSrvTypeLog = 9, + kSrvTypeDll = 10, + kSrvTypeScore = 11, + kSrvTypeCsr = 12, + kSrvTypeGateKeeper = 13, + + kNumSrvTypes, + + // Enforce network message field size + kNetSrvForceDword = (unsigned)-1 +}; + + +/***************************************************************************** +* +* Front-end server hostnames +* +***/ + +unsigned GetAuthSrvHostnames (const wchar *** addrs); // returns addrCount +void SetAuthSrvHostname (const wchar addr[]); +bool AuthSrvHostnameOverride (); + +unsigned GetFileSrvHostnames (const wchar *** addrs); // returns addrCount +void SetFileSrvHostname (const wchar addr[]); +bool FileSrvHostnameOverride (); + +unsigned GetCsrSrvHostnames (const wchar *** addrs); // returns addrCount +void SetCsrSrvHostname (const wchar addr[]); +bool CsrSrvHostnameOverride (); + +unsigned GetGateKeeperSrvHostnames (const wchar *** addrs); // returns addrCount +void SetGateKeeperSrvHostname (const wchar addr[]); +bool GateKeeperSrvHostnameOverride (); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbAuthKey.hpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbAuthKey.hpp new file mode 100644 index 00000000..6671a6e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbAuthKey.hpp @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/************************************************************* +* +* This file was auto-generated by plDhKeyGen.exe +* +***/ + +static const unsigned kDhGValue = 41; + +static const byte kDhNData[] = { + 0x2d, 0x17, 0x19, 0x42, 0xeb, 0x71, 0x8f, 0x91, + 0x29, 0x7c, 0x61, 0x88, 0x43, 0x75, 0xe5, 0xee, + 0x72, 0xfe, 0x45, 0x1b, 0x43, 0xc3, 0x8e, 0xb9, + 0x47, 0x5e, 0x03, 0xc8, 0x0c, 0x78, 0xb7, 0xe4, + 0x4d, 0x31, 0x5b, 0xcb, 0x66, 0xc2, 0x54, 0x1a, + 0x0a, 0x61, 0x11, 0x57, 0x38, 0x66, 0x9b, 0x34, + 0x6b, 0xab, 0x6d, 0x12, 0x12, 0x38, 0x87, 0xc5, + 0x3f, 0x20, 0xbe, 0x97, 0xa3, 0xa6, 0x56, 0x8f +}; +COMPILER_ASSERT(sizeof(kDhNData) == kNetDiffieHellmanKeyBits / 8); + +static const byte kDhXData[] = { + 0x21, 0xcf, 0x1d, 0xc6, 0x08, 0xc0, 0x23, 0xad, + 0x53, 0x36, 0xce, 0x61, 0x25, 0xdd, 0xb9, 0x55, + 0x05, 0xc1, 0xbb, 0x8f, 0xf4, 0x0d, 0x59, 0xf9, + 0x20, 0x27, 0x9a, 0xee, 0xfb, 0x23, 0x5b, 0xeb, + 0xe5, 0xec, 0x01, 0x55, 0x2e, 0xd5, 0x64, 0xef, + 0xea, 0x43, 0xb1, 0x9e, 0xb9, 0x8c, 0x75, 0x3f, + 0xda, 0xb2, 0xbb, 0xb3, 0x6a, 0x3d, 0xcd, 0xbc, + 0xfa, 0x6f, 0x03, 0xf3, 0x55, 0xd8, 0xe9, 0x1b +}; +COMPILER_ASSERT(sizeof(kDhXData) == kNetDiffieHellmanKeyBits / 8); + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbConst.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbConst.h new file mode 100644 index 00000000..98788718 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbConst.h @@ -0,0 +1,152 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbConst.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PNNBCONST_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbConst.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PNNBCONST_H + + +/***************************************************************************** +* +* Global constants +* +***/ + +//============================================================================ +// Network constants +//============================================================================ +const unsigned kNetLegacyClientPort = 80; +const unsigned kNetDefaultClientPort = 14617; +const unsigned kNetDefaultServerPort = 14618; +const unsigned kNetDefaultSimpleNetPort = 14620; +const unsigned kMaxTcpPacketSize = 1460; + +//============================================================================ +// Crypto constants +//============================================================================ +const unsigned kNetMaxSymmetricSeedBytes = 7; // 56 bits +const unsigned kNetDiffieHellmanKeyBits = 512; +//COMPILER_ASSERT_HEADER(DH, IS_POW2(kNetDiffieHellmanKeyBits)); + +//============================================================================ +// Data constants +//============================================================================ +const unsigned kMaxPasswordLength = 16; +const unsigned kMaxAccountPassLength = kMaxPasswordLength; +const unsigned kMaxAccountNameLength = 64; +const unsigned kMaxPlayerNameLength = 40; +const unsigned kMaxAgeNameLength = 64; +const unsigned kMaxVaultNodeStringLength = 64; +const unsigned kMaxVaultNodeTypeStringLength = 24; +const unsigned kMaxVaultTreeDepth = 255; +const unsigned kMaxPlayersPerAccount = 6; +const unsigned kMaxStateObjectName = 64; +const unsigned kMaxLogEventName = 64; +const unsigned kMaxLogAddrLength = 16; +const unsigned kMaxPublisherAuthKeyLength = 64; +const unsigned kMaxGTOSIdLength = 8; +const unsigned kMaxGameScoreNameLength = 64; +const unsigned kMaxEmailAddressLength = 64; + +/***************************************************************************** +* +* Account Flags +* +***/ + +// Billing flags +const unsigned kBillingTypeFree = 0 << 0; +const unsigned kBillingTypePaidSubscriber = 1 << 0; +const unsigned kBillingTypeGameTap = 1 << 1; + +struct AccountRoleInfo { + unsigned Role; + char* Descriptor; +}; + +// Account role flags +const unsigned kAccountRoleDisabled = 0 << 0; +const unsigned kAccountRoleAdmin = 1 << 0; +const unsigned kAccountRoleDeveloper = 1 << 1; +const unsigned kAccountRoleBetaTester = 1 << 2; +const unsigned kAccountRoleUser = 1 << 3; +const unsigned kAccountRoleSpecialEvent = 1 << 4; +const unsigned kAccountRoleBanned = 1 << 16; + +// update the following whenever a new end-user account role is added +const unsigned kAccountRolesAllUserFlags = kAccountRoleBetaTester | kAccountRoleUser | kAccountRoleSpecialEvent; + +const AccountRoleInfo kAccountRoles[] = { + { kAccountRoleBetaTester, "Beta Tester" }, + { kAccountRoleUser, "User" }, + { kAccountRoleSpecialEvent, "Special Event" }, + + { kAccountRolesAllUserFlags, "End" } +}; + + +/***************************************************************************** +* +* Csr +* +***/ + +enum ECsrFlags { + kCsrFlagAdmin = 1 << 0, + kCsrFlagDisabled = 1 << 1, + kCsrFlagServer = 1 << 2, +}; + + +/***************************************************************************** +* +* Game Score Types +* +***/ + +enum EGameScoreTypes { + kScoreTypeFixed = 0, + kScoreTypeAccumulative, + kScoreTypeAccumAllowNegative, +}; + +enum EScoreRankGroups { + kScoreRankGroupIndividual = 0, + kScoreRankGroupNeighborhood, +}; + +enum EScoreTimePeriods { + kScoreTimePeriodOverall = 0, + kScoreTimePeriodYear, + kScoreTimePeriodMonth, + kScoreTimePeriodDay +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbCsrKey.hpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbCsrKey.hpp new file mode 100644 index 00000000..68a84db2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbCsrKey.hpp @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/************************************************************* +* +* This file was auto-generated by plDhKeyGen.exe +* +***/ + +static const unsigned kDhGValue = 97; + +static const byte kDhNData[] = { + 0xbd, 0xf8, 0x03, 0x17, 0xed, 0x27, 0x74, 0x0d, + 0x76, 0x8e, 0x7a, 0x8c, 0x88, 0xeb, 0xec, 0xde, + 0xd7, 0x73, 0x37, 0x8f, 0x4b, 0xbc, 0xae, 0x75, + 0xf8, 0xda, 0x22, 0xee, 0x50, 0xc9, 0xb3, 0x69, + 0x97, 0x0b, 0xe5, 0x28, 0x98, 0x91, 0xf7, 0x44, + 0x97, 0xdf, 0x70, 0xf1, 0x86, 0x6c, 0xfa, 0x03, + 0x7c, 0x47, 0x73, 0xb1, 0x62, 0x00, 0x42, 0x99, + 0xf0, 0xc6, 0xe7, 0x05, 0xd8, 0xcd, 0xd6, 0x6d +}; +COMPILER_ASSERT(sizeof(kDhNData) == kNetDiffieHellmanKeyBits / 8); + +static const byte kDhXData[] = { + 0x03, 0x1d, 0x83, 0x62, 0x36, 0x92, 0x63, 0xa6, + 0xa8, 0x66, 0x31, 0xbe, 0x28, 0x42, 0xc2, 0xe6, + 0x86, 0x98, 0x3a, 0x81, 0x13, 0x0e, 0xb7, 0x96, + 0x5e, 0x27, 0x87, 0x6a, 0x06, 0x68, 0x51, 0x21, + 0xf3, 0x74, 0xe9, 0x11, 0xe9, 0x73, 0x5f, 0xdb, + 0x37, 0x2a, 0x2d, 0x49, 0xa4, 0x76, 0x12, 0x06, + 0x58, 0x36, 0x59, 0x6d, 0xbb, 0x59, 0x57, 0x2c, + 0x34, 0xe4, 0x5e, 0xd4, 0x2c, 0x78, 0xc3, 0x34 +}; +COMPILER_ASSERT(sizeof(kDhXData) == kNetDiffieHellmanKeyBits / 8); + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbGameKey.hpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbGameKey.hpp new file mode 100644 index 00000000..58ec88e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbGameKey.hpp @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/************************************************************* +* +* This file was auto-generated by plDhKeyGen.exe +* +***/ + +static const unsigned kDhGValue = 73; + +static const byte kDhNData[] = { + 0x38, 0xa4, 0xf7, 0x69, 0x43, 0xd7, 0xa6, 0xc7, + 0x27, 0x72, 0x3b, 0x12, 0x6a, 0x76, 0x09, 0x5d, + 0xaa, 0x9a, 0x38, 0x44, 0xde, 0x84, 0x22, 0xc4, + 0xde, 0x3f, 0xfd, 0x50, 0x26, 0xea, 0xbb, 0x20, + 0xd6, 0xad, 0x2e, 0xcd, 0xfe, 0xf2, 0x70, 0x1b, + 0x19, 0xd1, 0x82, 0x15, 0xd7, 0xcb, 0x33, 0x60, + 0x20, 0xbc, 0x43, 0xc9, 0x55, 0x0b, 0xc9, 0x20, + 0xd9, 0x24, 0xd2, 0x49, 0xd0, 0x28, 0x4b, 0x90 +}; +COMPILER_ASSERT(sizeof(kDhNData) == kNetDiffieHellmanKeyBits / 8); + +static const byte kDhXData[] = { + 0xf9, 0xb2, 0x93, 0xbd, 0x02, 0xbd, 0x72, 0x48, + 0xa9, 0x21, 0xbd, 0x09, 0x3e, 0x44, 0x65, 0x3e, + 0xc2, 0xfd, 0x5a, 0xf0, 0x99, 0x3b, 0x3f, 0x5c, + 0x47, 0x76, 0x5c, 0x1f, 0x84, 0xd8, 0x01, 0x68, + 0xdc, 0x5f, 0xb6, 0xba, 0xc8, 0xfd, 0x79, 0x98, + 0x62, 0x93, 0x36, 0x7f, 0x14, 0xcf, 0x33, 0x67, + 0x84, 0x04, 0xcf, 0xa3, 0x7a, 0x65, 0xe9, 0x68, + 0x4f, 0x58, 0x58, 0xb3, 0x8f, 0x3d, 0xdb, 0x3d +}; +COMPILER_ASSERT(sizeof(kDhXData) == kNetDiffieHellmanKeyBits / 8); + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbGateKeeperKey.hpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbGateKeeperKey.hpp new file mode 100644 index 00000000..388acf3c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNbGateKeeperKey.hpp @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* This file auto-generated by plDhKeyGen.exe +* +***/ + +static const unsigned kDhGValue = 4; + +static const byte kDhNData[] = { + 0x8d, 0xfa, 0x35, 0xe6, 0xf8, 0x7a, 0x50, 0x50, + 0xab, 0x25, 0x4b, 0x81, 0xd1, 0xd7, 0x7a, 0x23, + 0xa3, 0x9a, 0x21, 0x0f, 0x34, 0xab, 0x66, 0x2e, + 0x16, 0x98, 0x55, 0xb6, 0xfc, 0x49, 0xd5, 0x50, + 0xdc, 0xb8, 0x4c, 0x4d, 0xc7, 0xdb, 0xf1, 0x1c, + 0x15, 0x4c, 0x55, 0xf5, 0x92, 0x0d, 0x6a, 0xec, + 0x60, 0xbc, 0x55, 0xfa, 0x29, 0x2f, 0x6f, 0xc3, + 0xd7, 0x21, 0x80, 0xa3, 0x6b, 0x44, 0x23, 0xb5, +}; +COMPILER_ASSERT(sizeof(kDhNData) == kNetDiffieHellmanKeyBits / 8); + +static const byte kDhXData[] = { + 0xb3, 0x88, 0xff, 0x0b, 0x90, 0x70, 0x2b, 0x2e, + 0x07, 0xbc, 0x62, 0x98, 0x83, 0x9d, 0x0f, 0x05, + 0x39, 0xfa, 0x35, 0x39, 0xa9, 0xf3, 0xb3, 0xfc, + 0xcd, 0x5e, 0xa9, 0xa6, 0x61, 0x0f, 0x9b, 0x38, + 0x0f, 0x9c, 0xbe, 0xa0, 0xbe, 0x6f, 0x7f, 0xe4, + 0x7c, 0xcb, 0xc4, 0x09, 0x6c, 0x8d, 0xce, 0x47, + 0x68, 0x82, 0x32, 0xc5, 0x89, 0x94, 0xf9, 0xca, + 0x69, 0x69, 0xd0, 0x60, 0x19, 0xb7, 0xf3, 0x1a, +}; +COMPILER_ASSERT(sizeof(kDhXData) == kNetDiffieHellmanKeyBits / 8); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNetBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNetBase.h new file mode 100644 index 00000000..69359e62 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNetBase.h @@ -0,0 +1,45 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetBase/pnNetBase.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PNNETBASE_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PNNETBASE_H + + +/***************************************************************************** +* +* Module includes +* +***/ + +#include "Private/pnNbAllIncludes.h" + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETBASE_PNNETBASE_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Intern.h new file mode 100644 index 00000000..b7fc4279 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Intern.h @@ -0,0 +1,114 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETCLI_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETCLI_INTERN_H + + +namespace pnNetCli { + +/***************************************************************************** +* +* Channel +* +***/ + +struct NetMsgChannel; + +NetMsgChannel * NetMsgChannelLock ( + unsigned protocol, + bool server, + unsigned * largestRecv +); +void NetMsgChannelUnlock ( + NetMsgChannel * channel +); +const NetMsgInitRecv * NetMsgChannelFindRecvMessage ( + NetMsgChannel * channel, + unsigned messageId +); +const NetMsgInitSend * NetMsgChannelFindSendMessage ( + NetMsgChannel * channel, + unsigned messageId +); +void NetMsgChannelGetDhConstants ( + const NetMsgChannel * channel, + unsigned * dh_g, + const BigNum ** dh_xa, // client: dh_x server: dh_a + const BigNum ** dh_n +); + + +/***************************************************************************** +* +* Encrypt +* +***/ + +void NetMsgCryptClientStart ( + NetMsgChannel * channel, + unsigned seedBytes, + const byte seedData[], + BigNum * clientSeed, + BigNum * serverSeed +); + +void NetMsgCryptServerConnect ( + NetMsgChannel * channel, + unsigned seedBytes, + const byte seedData[], + BigNum * clientSeed +); + + +/***************************************************************************** +* +* Utils +* +***/ + +class CInputAccumulator { + ARRAY(byte) buffer; + byte * curr; + +public: + CInputAccumulator (); + void Add (unsigned count, const byte * data); + bool Get (unsigned count, void * dest); // returns false if request cannot be fulfilled + bool Eof () const; + void Clear (); + void Compact (); +}; + + +} using namespace pnNetCli; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Pch.h new file mode 100644 index 00000000..6b741de8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Pch.h @@ -0,0 +1,45 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETCLI_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETCLI_PCH_H + + +#include "pnUtils/pnUtils.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" + +#include "pnNetCli.h" +#include "Intern.h" + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcChannel.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcChannel.cpp new file mode 100644 index 00000000..e5f3dd4d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcChannel.cpp @@ -0,0 +1,400 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcChannel.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +namespace pnNetCli { + +/***************************************************************************** +* +* Private +* +***/ + +struct ChannelCrit { + ~ChannelCrit (); + ChannelCrit (); + inline void Enter () { m_critsect.Enter(); } + inline void Leave () { m_critsect.Leave(); } + inline void EnterSafe () { if (m_init) m_critsect.Enter(); } + inline void LeaveSafe () { if (m_init) m_critsect.Leave(); } + +private: + bool m_init; + CCritSect m_critsect; +}; + +struct NetMsgChannel : AtomicRef { + LINK(NetMsgChannel) m_link; + unsigned m_protocol; + bool m_server; + + // Message definitions + unsigned m_largestRecv; + ARRAY(NetMsgInitSend) m_sendMsgs; + ARRAY(NetMsgInitRecv) m_recvMsgs; + + // Diffie-Hellman constants + unsigned m_dh_g; + BigNum m_dh_xa; // client: dh_x server: dh_a + BigNum m_dh_n; +}; + +static ChannelCrit s_channelCrit; +static LIST(NetMsgChannel) * s_channels; + + +/**************************************************************************** +* +* ChannelCrit +* +***/ + +//=========================================================================== +ChannelCrit::ChannelCrit () { + m_init = true; +} + +//=========================================================================== +ChannelCrit::~ChannelCrit () { + EnterSafe(); + if (s_channels) { + while (NetMsgChannel * const channel = s_channels->Head()) { + s_channels->Unlink(channel); + channel->DecRef("ChannelLink"); + } + + DEL(s_channels); + s_channels = nil; + } + LeaveSafe(); +} + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +// Returns max size of message in bytes +static unsigned ValidateMsg (const NetMsg & msg) { + ASSERT(msg.fields); + ASSERT(msg.count); + + unsigned maxBytes = sizeof(word); // for message id + bool prevFieldWasVarCount = false; + + for (unsigned i = 0; i < msg.count; i++) { + const NetMsgField & field = msg.fields[i]; + + for (;;) { + bool gotVarCount = false; + bool gotVarField = false; + if (field.type == kNetMsgFieldVarCount) { + if (gotVarField || gotVarCount) + FATAL("Msg definition may only include one variable length field"); + gotVarCount = true; + break; + } + if (field.type == kNetMsgFieldVarPtr || field.type == kNetMsgFieldRawVarPtr) { + if (gotVarField || gotVarCount) + FATAL("Msg definition may only include one variable length field"); + if (!prevFieldWasVarCount) + FATAL("Variable length field must preceded by variable length count field"); + gotVarField = true; + break; + } + if (gotVarField) + FATAL("Variable length field must be the last field in message definition"); + break; + } + + prevFieldWasVarCount = false; + switch (field.type) { + case kNetMsgFieldInteger: + maxBytes += sizeof(qword); + break; + + case kNetMsgFieldReal: + maxBytes += sizeof(double); + break; + + case kNetMsgFieldVarPtr: + case kNetMsgFieldRawVarPtr: + break; + + case kNetMsgFieldVarCount: + prevFieldWasVarCount = true; + // fall-thru... + case kNetMsgFieldString: + case kNetMsgFieldPtr: + case kNetMsgFieldRawPtr: + case kNetMsgFieldData: + case kNetMsgFieldRawData: + maxBytes += msg.fields[i].count * msg.fields[i].size; + break; + + DEFAULT_FATAL(field.type); + } + } + + return maxBytes; +} + + +//=========================================================================== +template +static unsigned MaxMsgId (const T msgs[], unsigned count) { + unsigned maxMsgId = 0; + + for (unsigned i = 0; i < count; i++) { + ASSERT(msgs[i].msg.count); + maxMsgId = max(msgs[i].msg.messageId, maxMsgId); + } + return maxMsgId; +} + +//=========================================================================== +static void AddSendMsgs_CS ( + NetMsgChannel * channel, + const NetMsgInitSend src[], + unsigned count +) { + channel->m_sendMsgs.GrowToFit(MaxMsgId(src, count), true); + + for (const NetMsgInitSend * term = src + count; src < term; ++src) { + NetMsgInitSend * const dst = &channel->m_sendMsgs[src[0].msg.messageId]; + + // check to ensure that the message id isn't already used + ASSERT(!dst->msg.count); + + *dst = *src; + ValidateMsg(dst->msg); + } +} + +//=========================================================================== +static void AddRecvMsgs_CS ( + NetMsgChannel * channel, + const NetMsgInitRecv src[], + unsigned count +) { + channel->m_recvMsgs.GrowToFit(MaxMsgId(src, count), true); + + for (const NetMsgInitRecv * term = src + count; src < term; ++src) { + ASSERT(src->recv); + NetMsgInitRecv * const dst = &channel->m_recvMsgs[src[0].msg.messageId]; + + // check to ensure that the message id isn't already used + ASSERT(!dst->msg.count); + + // copy the message handler + *dst = *src; + + const unsigned bytes = ValidateMsg(dst->msg); + channel->m_largestRecv = max(channel->m_largestRecv, bytes); + } +} + +//=========================================================================== +static NetMsgChannel * FindChannel_CS (unsigned protocol, bool server) { + if (!s_channels) + return nil; + + NetMsgChannel * channel = s_channels->Head(); + for (; channel; channel = s_channels->Next(channel)) { + if ((channel->m_protocol == protocol) && (channel->m_server == server)) + break; + } + + return channel; +} + +//=========================================================================== +static NetMsgChannel * FindOrCreateChannel_CS (unsigned protocol, bool server) { + if (!s_channels) { + s_channels = NEW(LIST(NetMsgChannel)); + s_channels->SetLinkOffset(offsetof(NetMsgChannel, m_link)); + } + + // find or create protocol + NetMsgChannel * channel = FindChannel_CS(protocol, server); + if (!channel) { + channel = NEW(NetMsgChannel); + channel->m_protocol = protocol; + channel->m_server = server; + channel->m_largestRecv = 0; + + s_channels->Link(channel); + channel->IncRef("ChannelLink"); + } + + return channel; +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +NetMsgChannel * NetMsgChannelLock ( + unsigned protocol, + bool server, + unsigned * largestRecv +) { + NetMsgChannel * channel; + s_channelCrit.Enter(); + if (nil != (channel = FindChannel_CS(protocol, server))) { + *largestRecv = channel->m_largestRecv; + channel->IncRef("ChannelLock"); + } + else { + *largestRecv = 0; + } + s_channelCrit.Leave(); + return channel; +} + +//============================================================================ +void NetMsgChannelUnlock ( + NetMsgChannel * channel +) { + s_channelCrit.Enter(); + { + channel->DecRef("ChannelLock"); + } + s_channelCrit.Leave(); +} + +//============================================================================ +const NetMsgInitRecv * NetMsgChannelFindRecvMessage ( + NetMsgChannel * channel, + unsigned messageId +) { + // Is message in range? + if (messageId >= channel->m_recvMsgs.Count()) + return nil; + + // Is message defined? + const NetMsgInitRecv * recvMsg = &channel->m_recvMsgs[messageId]; + if (!recvMsg->msg.count) + return nil; + + // Success! + return recvMsg; +} + +//============================================================================ +const NetMsgInitSend * NetMsgChannelFindSendMessage ( + NetMsgChannel * channel, + unsigned messageId +) { + // Is message in range? + ASSERT(messageId < channel->m_sendMsgs.Count()); + + // Is message defined? + const NetMsgInitSend * sendMsg = &channel->m_sendMsgs[messageId]; + ASSERTMSG(sendMsg->msg.count, "NetMsg not found for send"); + + return sendMsg; +} + +//============================================================================ +void NetMsgChannelGetDhConstants ( + const NetMsgChannel * channel, + unsigned * dh_g, + const BigNum ** dh_xa, + const BigNum ** dh_n +) { + *dh_g = channel->m_dh_g; + *dh_xa = &channel->m_dh_xa; + *dh_n = &channel->m_dh_n; +} + + +} // namespace pnNetCli + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +void NetMsgProtocolRegister ( + unsigned protocol, + bool server, + const NetMsgInitSend sendMsgs[], + unsigned sendMsgCount, + const NetMsgInitRecv recvMsgs[], + unsigned recvMsgCount, + unsigned dh_g, + const BigNum & dh_xa, // client: dh_x server: dh_a + const BigNum & dh_n +) { + s_channelCrit.EnterSafe(); + { + NetMsgChannel * channel = FindOrCreateChannel_CS(protocol, server); + + // make sure no connections have been established on this protocol, otherwise + // we'll be modifying a live data structure; NetCli's don't lock their protocol + // to operate on it once they have linked to it! + ASSERT(channel->GetRefCount() == 1); + + channel->m_dh_g = dh_g; + channel->m_dh_xa = dh_xa; + channel->m_dh_n = dh_n; + + if (sendMsgCount) + AddSendMsgs_CS(channel, sendMsgs, sendMsgCount); + if (recvMsgCount) + AddRecvMsgs_CS(channel, recvMsgs, recvMsgCount); + } + s_channelCrit.LeaveSafe(); +} + +//=========================================================================== +void NetMsgProtocolDestroy (unsigned protocol, bool server) { + s_channelCrit.EnterSafe(); + if (NetMsgChannel * channel = FindChannel_CS(protocol, server)) { + s_channels->Unlink(channel); + channel->DecRef("ChannelLink"); + } + s_channelCrit.LeaveSafe(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcCli.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcCli.cpp new file mode 100644 index 00000000..ebfe3579 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcCli.cpp @@ -0,0 +1,1035 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcCli.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + +//#define NCCLI_DEBUGGING +#ifdef NCCLI_DEBUGGING +# pragma message("Compiling pnNetCli with debugging on") +# define NCCLI_LOG LogMsg +#else +# define NCCLI_LOG NULL_STMT +#endif + +//#define NO_ENCRYPTION + +namespace pnNetCli { + +/***************************************************************************** +* +* Private types and constants +* +***/ + +enum ENetCliMode { + kNetCliModeServerStart, + kNetCliModeClientStart, + kNetCliModeEncrypted, + kNumNetCliModes +}; + +} using namespace pnNetCli; + + +/***************************************************************************** +* +* Opaque types +* +***/ + +// connection structure attached to each socket +struct NetCli : THashKeyVal { + + // communication channel + AsyncSocket sock; + ENetProtocol protocol; + NetMsgChannel * channel; + bool server; + + // message queue + LINK(NetCli) link; + NetCliQueue * queue; + + // message send/recv + const NetMsgInitRecv * recvMsg; + const NetMsgField * recvField; + unsigned recvFieldBytes; + bool recvDispatch; + byte * sendCurr; // points into sendBuffer + CInputAccumulator input; + + // Message encryption + ENetCliMode mode; + FNetCliEncrypt encryptFcn; + byte seed[kNetMaxSymmetricSeedBytes]; + CryptKey * cryptIn; + CryptKey * cryptOut; + void * encryptParam; + + // Message buffers + byte sendBuffer[kAsyncSocketBufferSize]; + ARRAY(byte) recvBuffer; +}; + +struct NetCliQueue { + LISTDECL(NetCli, link) list; + unsigned lastSendMs; + unsigned flushTimeMs; +}; + + +namespace pnNetCli { + +/***************************************************************************** +* +* Private data +* +***/ + + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//============================================================================ +static void PutBufferOnWire (NetCli * cli, void * data, unsigned bytes) { + + byte * temp, * heap = NULL; + ref(temp); + + if (cli->mode == kNetCliModeEncrypted) { + // Encrypt data... +#ifndef NO_ENCRYPTION + if (bytes <= 2048) + // byte count is small, use stack-based buffer + temp = ALLOCA(byte, bytes); + else + // byte count is large, use heap-based buffer + temp = heap = (byte *)ALLOC(bytes); + + MemCopy(temp, data, bytes); + CryptEncrypt(cli->cryptOut, bytes, temp); + data = temp; +#endif + } + if (cli->sock) + AsyncSocketSend(cli->sock, data, bytes); + + // free heap buffer (if any) + FREE(heap); +} + +//============================================================================ +static void FlushSendBuffer (NetCli * cli) { + const unsigned bytes = cli->sendCurr - cli->sendBuffer; + ASSERT(bytes <= arrsize(cli->sendBuffer)); + PutBufferOnWire(cli, cli->sendBuffer, bytes); + cli->sendCurr = cli->sendBuffer; +} + +//=========================================================================== +static void AddToSendBuffer ( + NetCli * cli, + unsigned bytes, + void const * const data +) { + byte const * src = (byte const *) data; + + if (bytes > arrsize(cli->sendBuffer)) { + // Let the OS fragment oversize buffers + FlushSendBuffer(cli); + void * heap = ALLOC(bytes); + MemCopy(heap, data, bytes); + PutBufferOnWire(cli, heap, bytes); + FREE(heap); + } + else { + for (;;) { + // calculate the space left in the output buffer and use it + // to determine the maximum number of bytes that will fit + unsigned const left = &cli->sendBuffer[arrsize(cli->sendBuffer)] - cli->sendCurr; + unsigned const copy = min(bytes, left); + + // copy the data into the buffer + for (unsigned i = 0; i < copy; ++i) + cli->sendCurr[i] = src[i]; + cli->sendCurr += copy; + ASSERT(cli->sendCurr - cli->sendBuffer <= sizeof(cli->sendBuffer)); + + // if we copied all the data then bail + if (copy < left) + break; + + src += copy; + bytes -= copy; + + FlushSendBuffer(cli); + } + } +} + +//============================================================================ +static void BufferedSendData ( + NetCli * cli, + const unsigned_ptr msg[], + unsigned fieldCount +) { + #define ASSERT_MSG_VALID(expr) \ + ASSERTMSG(expr, "Invalid message definition"); + + #define WRITE_SWAPPED_INT(t,c) { \ + ASSERT(sizeof(t) == sizeof(c)); \ + t endianCount = Endian((t)c); \ + AddToSendBuffer(cli, sizeof(t), (const void *) &endianCount); \ + } + + + ASSERT(cli); + ASSERT(msg); + ASSERT(fieldCount); + + if (!cli->sock) + return; + + unsigned_ptr const * const msgEnd = msg + fieldCount; + ref(msgEnd); + + const NetMsgInitSend * sendMsg = NetMsgChannelFindSendMessage(cli->channel, msg[0]); + ASSERT(msg[0] == sendMsg->msg.messageId); + ASSERT(fieldCount-1 == sendMsg->msg.count); + + // insert messageId into command stream + const word msgId = (word) msg[0]; + WRITE_SWAPPED_INT(word, msgId); + ++msg; + ASSERT_MSG_VALID(msg < msgEnd); + + // insert fields into command stream + dword varCount = 0; + dword varSize = 0; + const NetMsgField * cmd = sendMsg->msg.fields; + const NetMsgField * cmdEnd = cmd + sendMsg->msg.count; + for (; cmd < cmdEnd; ++msg, ++cmd) { + switch (cmd->type) { + case kNetMsgFieldInteger: { + const unsigned count = cmd->count ? cmd->count : 1; + const unsigned bytes = cmd->size * count; + void * temp = ALLOCA(byte, bytes); + + if (count == 1) + // Single values are passed by value + EndianCopy(temp, (const byte *) msg, count, cmd->size); + else + // Value arrays are passed in by ptr + EndianCopy(temp, (const byte *) *msg, count, cmd->size); + + // Write values to send buffer + AddToSendBuffer(cli, bytes, temp); + } + break; + + case kNetMsgFieldReal: { + const unsigned count = cmd->count ? cmd->count : 1; + const unsigned bytes = cmd->size * count; + + if (count == 1) + // Single values are passed in by value + AddToSendBuffer(cli, bytes, (const void *) msg); + else + // Value arrays are passed in by ptr + AddToSendBuffer(cli, bytes, (const void *) *msg); + } + break; + + case kNetMsgFieldString: { + // Use less-than instead of less-or-equal because + // we reserve one space for the NULL terminator + const word length = (word) StrLen((const wchar *) *msg); + ASSERT_MSG_VALID(length < cmd->count); + // Write actual string length + WRITE_SWAPPED_INT(word, length); + // Write string data + AddToSendBuffer(cli, length * sizeof(wchar), (const void *) *msg); + } + break; + + case kNetMsgFieldData: + case kNetMsgFieldRawData: { + // write values to send buffer + AddToSendBuffer(cli, cmd->count * cmd->size, (const void *) *msg); + } + break; + + case kNetMsgFieldVarCount: { + ASSERT(!varCount); + ASSERT(!varSize); + // remember the element size + varSize = cmd->size; + // write the actual element count + varCount = (dword) *msg; + WRITE_SWAPPED_INT(dword, varCount); + } + break; + + case kNetMsgFieldVarPtr: + case kNetMsgFieldRawVarPtr: { + ASSERT(varSize); + // write var sized array + AddToSendBuffer(cli, varCount * varSize, (const void *) *msg); + varCount = 0; + varSize = 0; + } + break; + + case kNetMsgFieldPtr: + case kNetMsgFieldRawPtr: { + // write values + AddToSendBuffer(cli, cmd->count * cmd->size, (const void *) *msg); + } + break; + + DEFAULT_FATAL(cmd->type); + } + } + + // prepare to flush this connection + if (cli->queue) + cli->queue->list.Link(cli); +} + +//=========================================================================== +static bool DispatchData (NetCli * cli, void * param) { + + word msgId = 0; + while (!cli->input.Eof()) { + // if we're not already decompressing a message, start new message + if (!cli->recvMsg) { + // get next message id + if (!cli->input.Get(sizeof(msgId), &msgId)) + goto NEED_MORE_DATA; + + msgId = Endian(msgId); + + if (nil == (cli->recvMsg = NetMsgChannelFindRecvMessage(cli->channel, msgId))) + goto ERR_NO_HANDLER; + + // prepare to start decompressing new fields + ASSERT(!cli->recvField); + ASSERT(!cli->recvFieldBytes); + cli->recvField = cli->recvMsg->msg.fields; + cli->recvBuffer.ZeroCount(); + cli->recvBuffer.Reserve(kAsyncSocketBufferSize); + + // store the message id as dword into the destination buffer + dword * recvMsgId = (dword *) cli->recvBuffer.New(sizeof(dword)); + *recvMsgId = msgId; + } + + for ( + const NetMsgField * end = cli->recvMsg->msg.fields + cli->recvMsg->msg.count; + cli->recvField < end; + ++cli->recvField + ) { + switch (cli->recvField->type) { + case kNetMsgFieldInteger: { + const unsigned count + = cli->recvField->count + ? cli->recvField->count + : 1; + + // Get integer values + const unsigned bytes = count * cli->recvField->size; + byte * data = cli->recvBuffer.New(bytes); + if (!cli->input.Get(bytes, data)) { + cli->recvBuffer.ShrinkBy(bytes); + goto NEED_MORE_DATA; + } + + // Byte-swap integers + EndianConvert( + data, + count, + cli->recvField->size + ); + + // Field complete + } + break; + + case kNetMsgFieldReal: { + const unsigned count + = cli->recvField->count + ? cli->recvField->count + : 1; + + // Get float values + const unsigned bytes = count * cli->recvField->size; + byte * data = cli->recvBuffer.New(bytes); + if (!cli->input.Get(bytes, data)) { + cli->recvBuffer.ShrinkBy(bytes); + goto NEED_MORE_DATA; + } + + // Field complete + } + break; + + case kNetMsgFieldData: + case kNetMsgFieldRawData: { + // Read fixed-length data into destination buffer + const unsigned bytes = cli->recvField->count * cli->recvField->size; + byte * data = cli->recvBuffer.New(bytes); + if (!cli->input.Get(bytes, data)) { + cli->recvBuffer.ShrinkBy(bytes); + goto NEED_MORE_DATA; + } + + // Field complete + } + break; + + case kNetMsgFieldVarCount: { + // Read var count field into destination buffer + const unsigned bytes = sizeof(dword); + byte * data = cli->recvBuffer.New(bytes); + if (!cli->input.Get(bytes, data)) { + cli->recvBuffer.ShrinkBy(bytes); + goto NEED_MORE_DATA; + } + + // Byte-swap value + EndianConvert((dword *) data, 1); + + // Prepare to read var-length field + cli->recvFieldBytes = *(dword *)data * cli->recvField->size; + + // Field complete + } + break; + + case kNetMsgFieldVarPtr: + case kNetMsgFieldRawVarPtr: { + // Read var-length data into destination buffer + const unsigned bytes = cli->recvFieldBytes; + byte * data = cli->recvBuffer.New(bytes); + if (!cli->input.Get(bytes, data)) { + cli->recvBuffer.ShrinkBy(bytes); + goto NEED_MORE_DATA; + } + + // Field complete + cli->recvFieldBytes = 0; + } + break; + + case kNetMsgFieldString: { + if (!cli->recvFieldBytes) { + // Read string length + word length; + if (!cli->input.Get(sizeof(word), &length)) + goto NEED_MORE_DATA; + cli->recvFieldBytes = Endian(length) * sizeof(wchar); + + // Validate size. Use >= instead of > to leave room for the NULL terminator. + if (cli->recvFieldBytes >= cli->recvField->count * cli->recvField->size) + goto ERR_BAD_COUNT; + } + + const unsigned bytes = cli->recvField->count * cli->recvField->size; + byte * data = cli->recvBuffer.New(bytes); + // Read compressed string data (less than full field length) + if (!cli->input.Get(cli->recvFieldBytes, data)) { + cli->recvBuffer.ShrinkBy(bytes); + goto NEED_MORE_DATA; + } + + // Insert NULL terminator + * (wchar *)(data + cli->recvFieldBytes) = 0; + + // IDEA: fill the remainder with a freaky byte pattern + + // Field complete + cli->recvFieldBytes = 0; + } + break; + } + } + + // dispatch message to handler function + NCCLI_LOG(kLogPerf, L"pnNetCli: Dispatching. msg: %S. cli: %p", cli->recvMsg ? cli->recvMsg->msg.name : "(unknown)", cli); + if (!cli->recvMsg->recv(cli->recvBuffer.Ptr(), cli->recvBuffer.Count(), param)) + goto ERR_DISPATCH_FAILED; + + // prepare to start next message + cli->recvMsg = nil; + cli->recvField = 0; + cli->recvFieldBytes = 0; + + // Release oversize message buffer + if (cli->recvBuffer.Count() > kAsyncSocketBufferSize) + cli->recvBuffer.Clear(); + } + + return true; + +// these are used for convenience in setting breakpoints +NEED_MORE_DATA: + NCCLI_LOG(kLogPerf, L"pnNetCli: NEED_MORE_DATA. msg: %S (%u). cli: %p", cli->recvMsg ? cli->recvMsg->msg.name : "(unknown)", msgId, cli); + return true; + +ERR_BAD_COUNT: + LogMsg(kLogError, L"pnNetCli: ERR_BAD_COUNT. msg: %S (%u). cli: %p", cli->recvMsg ? cli->recvMsg->msg.name : "(unknown)", msgId, cli); + return false; + +ERR_NO_HANDLER: + LogMsg(kLogError, L"pnNetCli: ERR_NO_HANDLER. msg: %S (%u). cli: %p", cli->recvMsg ? cli->recvMsg->msg.name : "(unknown)", msgId, cli); + return false; + +ERR_DISPATCH_FAILED: + LogMsg(kLogError, L"pnNetCli: ERR_DISPATCH_FAILED. msg: %S (%u). cli: %p", cli->recvMsg ? cli->recvMsg->msg.name : "(unknown)", msgId, cli); + return false; +} + + +namespace Connect { +/***************************************************************************** +* +* NetCli connect protocol +* +***/ + +#include +enum { + kNetCliCli2SrvConnect, + kNetCliSrv2CliEncrypt, + kNetCliSrv2CliError, + kNumNetCliMsgs +}; + +struct NetCli_PacketHeader { + byte message; + byte length; +}; + +struct NetCli_Cli2Srv_Connect : NetCli_PacketHeader { + byte dh_y_data[kNetDiffieHellmanKeyBits / 8]; +}; + +struct NetCli_Srv2Cli_Encrypt : NetCli_PacketHeader { + byte serverSeed[kNetMaxSymmetricSeedBytes]; +}; + +struct NetCli_Srv2Cli_Error : NetCli_PacketHeader { + dword error; // ENetError +}; +#include + + +//=========================================================================== +static void CreateSymmetricKey ( + unsigned serverBytes, + const byte * serverSeed, + unsigned clientBytes, + const byte * clientSeed, + unsigned outputBytes, + byte * outputSeed +) { + ASSERT(clientBytes == kNetMaxSymmetricSeedBytes); + ASSERT(serverBytes == kNetMaxSymmetricSeedBytes); + ASSERT(outputBytes == kNetMaxSymmetricSeedBytes); + for (unsigned i = 0; i < outputBytes; ++i) + outputSeed[i] = (byte) (clientSeed[i] ^ serverSeed[i]); +} + +//============================================================================ +static void ClientConnect (NetCli * cli) { + + // Initiate diffie-hellman for client + BigNum clientSeed; + BigNum serverSeed; + NetMsgCryptClientStart( + cli->channel, + sizeof(cli->seed), + cli->seed, + &clientSeed, + &serverSeed + ); + + // Save client seed + { + ZERO(cli->seed); + unsigned bytes; + const void * data = clientSeed.GetData(&bytes); + MemCopy(cli->seed, data, min(bytes, sizeof(cli->seed))); + } + + // Send server seed + if (cli->sock) { + unsigned bytes; + NetCli_Cli2Srv_Connect msg; + const void * data = serverSeed.GetData(&bytes); + ASSERTMSG(bytes <= sizeof(msg.dh_y_data), "4"); + msg.message = kNetCliCli2SrvConnect; + msg.length = (byte) (sizeof(msg) - sizeof(msg.dh_y_data) + bytes); + MemCopy(msg.dh_y_data, data, bytes); + AsyncSocketSend(cli->sock, &msg, msg.length); + } +} + +//============================================================================ +static bool ServerRecvConnect ( + NetCli * cli, + const NetCli_PacketHeader & pkt +) { + // Validate connection state + if (cli->mode != kNetCliModeServerStart) + return false; + + // Validate message size + const NetCli_Cli2Srv_Connect & msg = + * (const NetCli_Cli2Srv_Connect *) &pkt; + if (pkt.length < sizeof(msg)) + return false; + + // Send the server seed to the client (unencrypted) + if (cli->sock) { + NetCli_Srv2Cli_Encrypt reply; + reply.message = kNetCliSrv2CliEncrypt; + reply.length = sizeof(reply); + MemCopy(reply.serverSeed, cli->seed, sizeof(reply.serverSeed)); + AsyncSocketSend(cli->sock, &reply, sizeof(reply)); + } + + // Compute client seed + byte clientSeed[kNetMaxSymmetricSeedBytes]; + { + BigNum clientSeedValue; + NetMsgCryptServerConnect( + cli->channel, + msg.length - sizeof(pkt), + msg.dh_y_data, + &clientSeedValue + ); + + ZERO(clientSeed); + unsigned bytes; + const void * data = clientSeedValue.GetData(&bytes); + MemCopy(clientSeed, data, min(bytes, sizeof(clientSeed))); + } + + // Create the symmetric key from a combination + // of the client seed and the server seed + byte sharedSeed[kNetMaxSymmetricSeedBytes]; + CreateSymmetricKey( + sizeof(cli->seed), cli->seed, // server seed + sizeof(clientSeed), clientSeed, // client seed + sizeof(sharedSeed), sharedSeed // combined seed + ); + + // Switch to encrypted mode + cli->mode = kNetCliModeEncrypted; + cli->cryptIn = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); + cli->cryptOut = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); + + return cli->encryptFcn(kNetSuccess, cli->encryptParam); +} + +//============================================================================ +static bool ClientRecvEncrypt ( + NetCli * cli, + const NetCli_PacketHeader & pkt +) { + // Validate connection state + if (cli->mode != kNetCliModeClientStart) + return false; + + // Validate message size + const NetCli_Srv2Cli_Encrypt & msg = + * (const NetCli_Srv2Cli_Encrypt *) &pkt; + if (pkt.length != sizeof(msg)) + return false; + + // Create the symmetric key from a combination + // of the client seed and the server seed + byte sharedSeed[kNetMaxSymmetricSeedBytes]; + CreateSymmetricKey( + sizeof(msg.serverSeed), msg.serverSeed, // server seed + sizeof(cli->seed), cli->seed, // client seed + sizeof(sharedSeed), sharedSeed // combined seed + ); + + // Switch to encrypted mode + cli->mode = kNetCliModeEncrypted; + cli->cryptIn = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); + cli->cryptOut = CryptKeyCreate(kCryptRc4, sizeof(sharedSeed), sharedSeed); + + return cli->encryptFcn(kNetSuccess, cli->encryptParam); +} + +//============================================================================ +static bool ClientRecvError ( + NetCli * cli, + const NetCli_PacketHeader & pkt +) { + // Validate connection state + if (cli->mode != kNetCliModeClientStart) + return false; + + // Validate message size + const NetCli_Srv2Cli_Error & msg = + * (const NetCli_Srv2Cli_Error *) &pkt; + if (pkt.length < sizeof(msg)) + return false; + + cli->encryptFcn((ENetError) msg.error, cli->encryptParam); + return false; +} + +//============================================================================ +typedef bool (* FNetCliPacket)( + NetCli * cli, + const NetCli_PacketHeader & pkt +); + +#if 0 + +#ifdef SERVER +static const FNetCliPacket s_recvTbl[kNumNetCliMsgs] = { + ServerRecvConnect, + nil, + nil, +}; +#endif + +#ifdef CLIENT +static const FNetCliPacket s_recvTbl[kNumNetCliMsgs] = { + nil, + ClientRecvEncrypt, + ClientRecvError, +}; +#endif + +#else // 0 + +static const FNetCliPacket s_recvTbl[kNumNetCliMsgs] = { + ServerRecvConnect, + ClientRecvEncrypt, + ClientRecvError, +}; + +#endif // 0 + +//=========================================================================== +static unsigned DispatchPacket ( + NetCli * cli, + unsigned bytes, + const byte data[] +) { + for (;;) { + const NetCli_PacketHeader & pkt = * (const NetCli_PacketHeader *) data; + if (bytes < sizeof(pkt)) + break; + if (pkt.length > bytes) + break; + if (pkt.message >= kNumNetCliMsgs) + break; + if (!s_recvTbl[pkt.message]) + break; + if (!s_recvTbl[pkt.message](cli, pkt)) + break; + + // Success! + return pkt.length; + } + + // Failure! + return 0; +} + +} // namespace Connect + + +/***************************************************************************** +* +* NetCli implementation +* +***/ + +//=========================================================================== +static void ResetSendRecv (NetCli * cli) { + cli->recvMsg = nil; + cli->recvField = nil; + cli->recvFieldBytes = 0; + cli->recvDispatch = true; + cli->sendCurr = cli->sendBuffer; + cli->recvBuffer.Clear(); + cli->input.Clear(); +} + +//=========================================================================== +static NetCli * ConnCreate ( + AsyncSocket sock, + unsigned protocol, + ENetCliMode mode +) { + // find channel + unsigned largestRecv; + NetMsgChannel * channel = NetMsgChannelLock( + protocol, + mode == kNetCliModeServerStart, + &largestRecv + ); + if (!channel) + return nil; + + NetCli * const cli = NEWZERO(NetCli); + cli->sock = sock; + cli->protocol = (ENetProtocol) protocol; + cli->channel = channel; + cli->mode = mode; + cli->SetValue(kNilGuid); + + ResetSendRecv(cli); + + return cli; +} + +//=========================================================================== +static void SetConnSeed ( + NetCli * cli, + unsigned seedBytes, + const byte seedData[] +) { + if (seedBytes) + MemCopy(cli->seed, seedData, min(sizeof(cli->seed), seedBytes)); + else + CryptCreateRandomSeed(sizeof(cli->seed), cli->seed); +} + +} using namespace pnNetCli; + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +NetCli * NetCliConnectAccept ( + AsyncSocket sock, + unsigned protocol, + bool unbuffered, + FNetCliEncrypt encryptFcn, + unsigned seedBytes, + const byte seedData[], + void * encryptParam +) { + // Create connection + NetCli * cli = ConnCreate(sock, protocol, kNetCliModeClientStart); + if (cli) { + AsyncSocketEnableNagling(sock, !unbuffered); + cli->encryptFcn = encryptFcn; + cli->encryptParam = encryptParam; + SetConnSeed(cli, seedBytes, seedData); + Connect::ClientConnect(cli); + } + return cli; +} + +//============================================================================ +#ifdef SERVER +NetCli * NetCliListenAccept ( + AsyncSocket sock, + unsigned protocol, + bool unbuffered, + FNetCliEncrypt encryptFcn, + unsigned seedBytes, + const byte seedData[], + void * encryptParam +) { + // Create connection + NetCli * cli = ConnCreate(sock, protocol, kNetCliModeServerStart); + if (cli) { + AsyncSocketEnableNagling(sock, !unbuffered); + cli->encryptFcn = encryptFcn; + cli->encryptParam = encryptParam; + SetConnSeed(cli, seedBytes, seedData); + } + return cli; +} +#endif + +//============================================================================ +#ifdef SERVER +void NetCliListenReject ( + AsyncSocket sock, + ENetError error +) { + if (sock) { + Connect::NetCli_Srv2Cli_Error response; + response.message = Connect::kNetCliSrv2CliError; + response.length = sizeof(response); + response.error = error; + AsyncSocketSend(sock, &response, sizeof(response)); + } +} +#endif + +//============================================================================ +void NetCliClearSocket (NetCli * cli) { + cli->sock = nil; +} + +//============================================================================ +void NetCliSetQueue ( + NetCli * cli, + NetCliQueue * queue +) { + cli->queue = queue; +} + +//============================================================================ +void NetCliDisconnect ( + NetCli * cli, + bool hardClose +) { + // send any existing messages and allow + // the socket layer to complete sending data + if (!hardClose) + NetCliFlush(cli); + + if (cli->sock) + AsyncSocketDisconnect(cli->sock, hardClose); + + // don't allow any more messages to be received + cli->recvDispatch = false; +} + +//============================================================================ +void NetCliDelete ( + NetCli * cli, + bool deleteSocket +) { + NetMsgChannelUnlock(cli->channel); + + if (cli->sock && deleteSocket) + AsyncSocketDelete(cli->sock); + + if (cli->cryptIn) + CryptKeyClose(cli->cryptIn); + if (cli->cryptOut) + CryptKeyClose(cli->cryptOut); + + cli->input.Clear(); + cli->recvBuffer.Clear(); + + DEL(cli); +} + +//============================================================================ +void NetCliFlush ( + NetCli * cli +) { + if (cli->sendCurr != cli->sendBuffer) + FlushSendBuffer(cli); +} + +//============================================================================ +void NetCliSend ( + NetCli * cli, + const unsigned_ptr msg[], + unsigned count +) { + BufferedSendData(cli, msg, count); +} + +//============================================================================ +bool NetCliDispatch ( + NetCli * cli, + const byte data[], + unsigned bytes, + void * param +) { + if (!cli->recvDispatch) + return false; + + do { + if (cli->mode == kNetCliModeEncrypted) { + // Decrypt data... + byte * temp, * heap = NULL; + ref(temp); + +#ifndef NO_ENCRYPTION + if (bytes <= 2048) + // byte count is small, use stack-based buffer + temp = ALLOCA(byte, bytes); + else + // byte count is large, use heap-based buffer + temp = heap = (byte *)ALLOC(bytes); + + MemCopy(temp, data, bytes); + CryptDecrypt(cli->cryptIn, bytes, temp); + data = temp; +#endif + + // Add data to accumulator and dispatch + cli->input.Add(bytes, data); + bool result = DispatchData(cli, param); + ref(result); + +#ifdef SERVER + cli->recvDispatch = result; +#endif + + // free heap buffer (if any) + FREE(heap); + + cli->input.Compact(); + return cli->recvDispatch; + } + + // Dispatch connect packets until encryption starts + unsigned used = Connect::DispatchPacket(cli, bytes, data); + if (!used) + return false; + + data += used; + bytes -= used; + + } while (bytes); + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcEncrypt.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcEncrypt.cpp new file mode 100644 index 00000000..c8acd62f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcEncrypt.cpp @@ -0,0 +1,135 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcEncrypt.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +namespace pnNetCli { + +/**************************************************************************** +* +* Diffie-Hellman constants +* + + // g and n are pregenerated and published + // (built into both client and server software) + BigNum g(4); + BigNum n; n.RandPrime(kKeyBits, &seed); + + // a and x are pregenerated; a is built into server software, and x is + // built into client software + BigNum a; a.Rand(kKeyBits, &seed); + BigNum x; x.PowMod(g, a, n); + + // client chooses b and y on connect, and sends y to the server + BigNum b; b.Rand(kKeyBits, &seed); + BigNum y; y.PowMod(g, b, n); + + // server computes key: k = y^a mod n + BigNum ka; ka.PowMod(y, a, n); + + // client computes key: k = x^b mod n + BigNum kb; kb.PowMod(x, b, n); + +***/ + +COMPILER_ASSERT(IS_POW2(kNetDiffieHellmanKeyBits)); + + +/***************************************************************************** +* +* Private +* +***/ + +//============================================================================ +// TODO: Cache computed keys +static void GetCachedServerKey ( + NetMsgChannel * channel, + BigNum * ka, + const BigNum & dh_y +) { + // Get diffie-hellman constants + unsigned DH_G; + const BigNum * DH_A; + const BigNum * DH_N; + NetMsgChannelGetDhConstants(channel, &DH_G, &DH_A, &DH_N); + + // Compute the result + ka->PowMod(dh_y, *DH_A, *DH_N); +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void NetMsgCryptClientStart ( + NetMsgChannel * channel, + unsigned seedBytes, + const byte seedData[], + BigNum * clientSeed, + BigNum * serverSeed +) { + unsigned DH_G; + const BigNum * DH_X; + const BigNum * DH_N; + NetMsgChannelGetDhConstants(channel, &DH_G, &DH_X, &DH_N); + + // Client chooses b and y on connect + BigNum g(DH_G); + BigNum seed(seedBytes, seedData); + BigNum b; b.Rand(kNetDiffieHellmanKeyBits, &seed); + + // Client computes key: kb = x^b mod n + clientSeed->PowMod(*DH_X, b, *DH_N); + + // Client sends y to server + serverSeed->PowMod(g, b, *DH_N); +} + +//============================================================================ +void NetMsgCryptServerConnect ( + NetMsgChannel * channel, + unsigned seedBytes, + const byte seedData[], + BigNum * clientSeed +) { + // Server computes client key: ka = y^a mod n + const BigNum dh_y(seedBytes, seedData); + GetCachedServerKey(channel, clientSeed, dh_y); +} + +} // namespace pnNetCli diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcUtils.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcUtils.cpp new file mode 100644 index 00000000..ff9e6723 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcUtils.cpp @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNcUtils.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* CInputAccumulator +* +***/ + +//============================================================================ +CInputAccumulator::CInputAccumulator () { + buffer.Reserve(1); + curr = buffer.Ptr(); +} + +//============================================================================ +void CInputAccumulator::Add (unsigned count, const byte * data) { +// LogMsg(kLogPerf, L"Adding %u bytes to accumulator %p", count, this); + unsigned offset = curr - buffer.Ptr(); + buffer.Add(data, count); + curr = buffer.Ptr() + offset; +} + +//============================================================================ +bool CInputAccumulator::Get (unsigned count, void * dest) { + if (curr + count > buffer.Term()) + return false; +// LogMsg(kLogPerf, L"Removing %u bytes from accumulator %p", count, this); + MemCopy(dest, curr, count); + curr += count; + return true; +} + +//============================================================================ +bool CInputAccumulator::Eof () const { + return curr >= buffer.Ptr() + buffer.Count(); +} + +//============================================================================ +void CInputAccumulator::Clear () { + buffer.SetCount(0); + curr = buffer.Ptr(); +} + +//============================================================================ +void CInputAccumulator::Compact () { + unsigned diff = curr - buffer.Ptr(); + unsigned newCount = buffer.Count() - diff; + buffer.Move(0, diff, newCount); + buffer.SetCount(newCount); + curr = buffer.Ptr(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNetCli.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNetCli.h new file mode 100644 index 00000000..eeffc66d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNetCli.h @@ -0,0 +1,423 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetCli/pnNetCli.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETCLI_PNNETCLI_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETCLI_PNNETCLI_H + + +/***************************************************************************** +* +* + +How to create a message sender/receiver: + +1. Define message ids + + enum { + kMsgMoveObject, + kMsgPlayerJoin, + kMsgPing, + kMsgPingReply, + // etc. + }; + + +2. Define packed message structures + + #include + struct MoveObject { + dword messageId; + dword objectId; + float newPos[3]; + }; + struct PlayerJoin { + dword messageId; + dword playerId; + wchar name[kPlayerNameMaxLength]; + byte data[kPlayerDataMaxLength]; + dword vaultDataLen; + byte vaultData[1]; // vaultData[vaultDataLen], actually + // no more fields after variable-length data + }; + struct Ping { + dword messageId; + dword pingTimeMs; + }; + struct PingReply { + dword messageId; + dword pingTimeMs; + } + #include + + +3. Define message fields + + static const NetMsgField kFieldObjectId = NET_MSG_FIELD_DWORD(); + static const NetMsgField kFieldPlayerId = NET_MSG_FIELD_DWORD(); + static const NetMsgField kFieldObjectPos = NET_MSG_FIELD_FLOAT_ARRAY(3); + static const NetMsgField kFieldPlayerName = NET_MSG_FIELD_STRING(kPlayerNameMaxLength); + static const NetMsgField kFieldPlayerData = NET_MSG_FIELD_PTR(kPlayerDataMaxLength); + static const NetMsgField kFieldVaultDataLen = NET_MSG_FIELD_VAR_COUNT(kVaultDataMaxLength); + static const NetMsgField kFieldVaultData = NET_MSG_FIELD_VAR_PTR(); + static const NetMsgField kFieldPingTimeMs = NET_MSG_FIELD_DWORD(); + + +4. Build field and message description blocks + + // MoveObject + static const NetMsgField s_moveObjectFields[] = { + kFieldObjectId, + kFieldObjectPos, + }; + static const NetMsg s_msgMoveObject = NET_MSG(kMsgMoveObject, s_msgMoveObjectFields); + + // PlayerJoin + static const NetMsgField s_playerJoinFields[] = { + kFieldPlayerId, + kFieldPlayerName, + kFieldPlayerData, + kFieldVaultDataLen, + kFieldVaultData, + }; + static const NetMsg s_playerJoin = NET_MSG(kMsgPlayerJoin, s_msgPlayerJoin); + + + +5. Register message description blocks + +// for server: + static const NetMsgInitSend s_srvSend[] = { + { s_moveObject }, + { s_playerJoin }, + { s_ping }, + }; + + static const NetMsgInitRecv s_srvRecv[] = { + { s_pingReply, RecvMsgPingReply }, + }; + + s_srvConn = NetMsgProtocolRegister( + kNetProtocolCliToGame, + true, + s_srvSend, arrsize(s_srvSend), + s_srvRecv, arrsize(s_srvRecv) + ); + + +// for client: + static const NetMsgInitSend s_cliSend[] = { + { s_pingReply }, + }; + + static const NetMsgInitRecv s_cliRecv[] = { + { s_moveObject, RecvMsgMoveObject }, + { s_playerJoin, RecvMsgPlayerJoin }, + { s_ping, RecvMsgPing }, + }; + + s_cliConn = NetMsgProtocolRegister( + kNetProtocolCliToGame, + false, + s_cliSend, arrsize(s_cliSend), + s_cliRecv, arrsize(s_cliRecv) + ); + + +5. Send messages + + static void SendMoveObject (NetCli client, const Object * obj) { + const unsigned_ptr msgMoveObject[] = { + kMsgMoveObject, + obj->id, + 3, (unsigned_ptr) &obj->pos, + }; + NetCliSend(client, msgMoveObject, arrsize(msgMoveObject)); + } + + static void SendPlayerJoin (NetCli client, const Player * player) { + const unsigned_ptr msgPlayerJoin[] = { + kMsgPlayerJoin, + player->name, + player->data, + player->vault->Count(), + player->vault->Ptr() + }; + NetCliSend(client, msgPlayerJoin, arrsize(msgPlayerJoin)); + }; + + static void SendPing (NetCli player) { + const unsigned_ptr msgPing[] = { + kMsgPing, + TimeGetMs(), + }; + NetCliSend(player, msgPing, arrsize(msgPing)); + } + + +6. Receive messages + + bool RecvMsgPing (const byte msg[], NetCli * from, void * param) { + Player * player = (Player *) param; + const Ping * ping = (const Ping *) msg; + const unsigned_ptr msgPingReply[] = { + kMsgPingReply, + ping->pingTimeMs, + }; + MsgConnSend(from, msgPingReply, arrsize(msgPingReply)); + } + +* +***/ + + +/***************************************************************************** +* +* Constants +* +***/ + + + +/***************************************************************************** +* +* Field definition types +* +***/ + +enum ENetMsgFieldType { + // Compressable fields + kNetMsgFieldInteger, + kNetMsgFieldReal, + kNetMsgFieldString, // variable length unicode string + kNetMsgFieldData, // data with length <= sizeof(dword) + kNetMsgFieldPtr, // pointer to fixed length data + kNetMsgFieldVarPtr, // pointer to variable length data + + // Non-compressible fields (images, sounds, encrypted data, etc) + kNetMsgFieldRawData, // data with length <= sizeof(dword) + kNetMsgFieldRawPtr, // pointer to fixed length data + kNetMsgFieldRawVarPtr, // pointer to variable length data + + kNetMsgFieldVarCount, // count for kNetMsgFieldVarPtr and kNetMsgFieldRawVarPtr + + kNumNetMsgFieldTypes +}; + + +struct NetMsgField { + ENetMsgFieldType type; // element type + unsigned count; // max number of elements + unsigned size; // element size +}; + +struct NetMsg { + const char * name; + unsigned messageId; + const NetMsgField * fields; + unsigned count; +}; + +// Opaque types +struct NetCli; +struct NetCliQueue; + + +/***************************************************************************** +* +* Msg and field definition macros +* +***/ + +#define NET_MSG(msgId, msgFields) { #msgId, msgId, msgFields, arrsize(msgFields) } + +#define NET_MSG_FIELD(type, count, size) { type, count, size } + +#define NET_MSG_FIELD_BYTE() NET_MSG_FIELD(kNetMsgFieldInteger, 0, sizeof(byte)) +#define NET_MSG_FIELD_WORD() NET_MSG_FIELD(kNetMsgFieldInteger, 0, sizeof(word)) +#define NET_MSG_FIELD_DWORD() NET_MSG_FIELD(kNetMsgFieldInteger, 0, sizeof(dword)) +#define NET_MSG_FIELD_QWORD() NET_MSG_FIELD(kNetMsgFieldInteger, 0, sizeof(qword)) +#define NET_MSG_FIELD_FLOAT() NET_MSG_FIELD(kNetMsgFieldReal, 0, sizeof(float)) +#define NET_MSG_FIELD_DOUBLE() NET_MSG_FIELD(kNetMsgFieldReal, 0, sizeof(double)) + +#define NET_MSG_FIELD_BYTE_ARRAY(maxCount) NET_MSG_FIELD(kNetMsgFieldInteger, maxCount, sizeof(byte)) +#define NET_MSG_FIELD_WORD_ARRAY(maxCount) NET_MSG_FIELD(kNetMsgFieldInteger, maxCount, sizeof(word)) +#define NET_MSG_FIELD_DWORD_ARRAY(maxCount) NET_MSG_FIELD(kNetMsgFieldInteger, maxCount, sizeof(dword)) +#define NET_MSG_FIELD_QWORD_ARRAY(maxCount) NET_MSG_FIELD(kNetMsgFieldInteger, maxCount, sizeof(qword)) +#define NET_MSG_FIELD_FLOAT_ARRAY(maxCount) NET_MSG_FIELD(kNetMsgFieldReal, maxCount, sizeof(float)) +#define NET_MSG_FIELD_DOUBLE_ARRAY(maxCount) NET_MSG_FIELD(kNetMsgFieldReal, maxCount, sizeof(double)) + +#define NET_MSG_FIELD_STRING(maxLength) NET_MSG_FIELD(kNetMsgFieldString, maxLength, sizeof(wchar)) + +#define NET_MSG_FIELD_DATA(maxBytes) NET_MSG_FIELD(kNetMsgFieldData, maxBytes, 1) +#define NET_MSG_FIELD_PTR(maxBytes) NET_MSG_FIELD(kNetMsgFieldPtr, maxBytes, 1) +#define NET_MSG_FIELD_RAW_DATA(maxBytes) NET_MSG_FIELD(kNetMsgFieldRawData, maxBytes, 1) +#define NET_MSG_FIELD_RAW_PTR(maxBytes) NET_MSG_FIELD(kNetMsgFieldRawPtr, maxBytes, 1) + +#define NET_MSG_FIELD_VAR_PTR() NET_MSG_FIELD(kNetMsgFieldVarPtr, 0, 0) +#define NET_MSG_FIELD_RAW_VAR_PTR() NET_MSG_FIELD(kNetMsgFieldRawVarPtr, 0, 0) + +#define NET_MSG_FIELD_VAR_COUNT(elemSize, maxCount) NET_MSG_FIELD(kNetMsgFieldVarCount, maxCount, elemSize) + + +/***************************************************************************** +* +* Message channels +* +***/ + +struct NetMsgInitSend { + NetMsg msg; +}; +struct NetMsgInitRecv { + NetMsg msg; + bool (* recv)(const byte msg[], unsigned bytes, void * param); +}; + +void NetMsgProtocolRegister ( + unsigned protocol, // from pnNetBase/pnNbProtocol.h + bool server, + const NetMsgInitSend sendMsgs[], // messages this program can send + unsigned sendMsgCount, + const NetMsgInitRecv recvMsgs[], // messages this program can receive + unsigned recvMsgCount, + // Diffie-Hellman keys + unsigned dh_g, + const BigNum & dh_xa, // client: dh_x server: dh_a + const BigNum & dh_n +); + +void NetMsgProtocolDestroy ( + unsigned protocol, + bool server +); + + +/***************************************************************************** +* +* NetCliQueue +* +***/ + +NetCliQueue * NetCliQueueCreate ( + unsigned flushTimeMs +); + +void NetCliQueueDestroy ( + NetCliQueue * queue +); + +void NetCliQueueFlush ( + NetCliQueue * queue +); + +float NetCliQueueQueryFlush ( + NetCliQueue * queue +); + + +/***************************************************************************** +* +* NetCli +* +***/ + +typedef bool (* FNetCliEncrypt) ( + ENetError error, + void * encryptParam +); + +NetCli * NetCliConnectAccept ( + AsyncSocket sock, + unsigned protocol, + bool unbuffered, + FNetCliEncrypt encryptFcn, + unsigned seedBytes, // optional + const byte seedData[], // optional + void * encryptParam // optional +); + +#ifdef SERVER +NetCli * NetCliListenAccept ( + AsyncSocket sock, + unsigned protocol, + bool unbuffered, + FNetCliEncrypt encryptFcn, + unsigned seedBytes, // optional + const byte seedData[], // optional + void * encryptParam // optional +); +#endif + +#ifdef SERVER +void NetCliListenReject ( + AsyncSocket sock, + ENetError error +); +#endif + +void NetCliClearSocket ( + NetCli * cli +); + +void NetCliSetQueue ( + NetCli * cli, + NetCliQueue * queue +); + +void NetCliDisconnect ( + NetCli * cli, + bool hardClose +); + +void NetCliDelete ( + NetCli * cli, + bool deleteSocket +); + +void NetCliFlush ( + NetCli * cli +); + +void NetCliSend ( + NetCli * cli, + const unsigned_ptr msg[], + unsigned count +); + +bool NetCliDispatch ( + NetCli * cli, + const byte buffer[], + unsigned bytes, + void * param +); + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETCLI_PNNETCLI_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plGenericVar.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plGenericVar.cpp new file mode 100644 index 00000000..fbc9fe5f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plGenericVar.cpp @@ -0,0 +1,345 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "plGenericVar.h" +#include "hsMemory.h" +#include "hsStlUtils.h" + +////////////////////////////////////////////////////// +// plGenericType +////////////////////////////////////////////////////// + +// reset runtime state, not inherent state +void plGenericType::Reset() +{ + fI=0; +} + +void plGenericType::CopyFrom(const plGenericType& c) +{ + IDeallocString(); + fType = c.fType; + if (fType==kString || fType==kAny) + { + fS=hsStrcpy(c.fS); + } + else + { + HSMemory::BlockMove((void*)&c.fI, (void*)&fI, 4); + } +} + +//// Conversion Functions //////////////////////////////////////////////////// + +const Int32 & plGenericType::IToInt( void ) const +{ + hsAssert( fType == kInt || fType == kAny, "Trying to use a non-int parameter as an int!" ); + + static Int32 i; + if( fType == kAny ) + { + hsAssert( fS != nil, "Weird parameter during conversion" ); + i = atoi( fS ); + return i; + } + + return fI; +} + +const UInt32 & plGenericType::IToUInt( void ) const +{ + hsAssert( fType == kUInt || fType == kAny, "Trying to use a non-int parameter as an int!" ); + + static UInt32 i; + if( fType == kAny ) + { + hsAssert( fS != nil, "Weird parameter during conversion" ); + i = atoi( fS ); + return i; + } + + return fU; +} + +const double & plGenericType::IToDouble( void ) const +{ + hsAssert( fType == kDouble || fType == kAny, "Trying to use a non-float parameter as a Double!" ); + + static double d; + if( fType == kAny ) + { + hsAssert( fS != nil, "Weird parameter during conversion" ); + d = atof( fS ); + return d; + } + + return fD; +} + +const float & plGenericType::IToFloat( void ) const +{ + hsAssert( fType == kFloat || fType == kAny, "Trying to use a non-float parameter as a float!" ); + + static float f; + if( fType == kAny ) + { + hsAssert( fS != nil, "Weird parameter during conversion" ); + f = (float)atof( fS ); + return f; + } + + return fF; +} + +const bool & plGenericType::IToBool( void ) const +{ + hsAssert( fType == kBool || fType == kAny, "Trying to use a non-bool parameter as a bool!" ); + + static bool b; + if( fType == kAny ) + { + hsAssert( fS != nil, "Weird parameter during conversion" ); + if( atoi( fS ) > 0 || stricmp( fS, "true" ) == 0 ) + b = true; + else + b = false; + + return b; + } + + return fB; +} + +const plGenericType::CharPtr & plGenericType::IToString( void ) const +{ + hsAssert( fType == kString || fType == kAny, "Trying to use a non-string parameter as a string!" ); + + return fS; +} + +const char & plGenericType::IToChar( void ) const +{ + hsAssert( fType == kChar || fType == kAny, "Trying to use a non-char parameter as a char!" ); + + static char c; + if( fType == kAny ) + { + hsAssert( fS != nil, "Weird parameter during conversion" ); + c = fS[ 0 ]; + return c; + } + + return fC; +} + +void plGenericType::Read(hsStream* s) +{ + IDeallocString(); + s->ReadSwap(&fType); + + switch ( fType ) + { + case kString: + case kAny: + fS=s->ReadSafeString(); + break; + case kBool: + {Int8 b; + s->ReadSwap( &b ); + fB = b?true:false;} + break; + case kChar: + s->ReadSwap( &fC ); + break; + case kInt : + s->ReadSwap( &fI ); + break; + case kUInt: + s->ReadSwap( &fU ); + break; + case kFloat: + s->ReadSwap( &fF ); + break; + case kDouble: + s->ReadSwap( &fD ); + break; + case kNone : + break; + } +} + +void plGenericType::Write(hsStream* s) +{ + s->WriteSwap(fType); + + switch ( fType ) + { + case kString: + case kAny: + s->WriteSafeString(fS); + break; + case kBool: + {Int8 b = fB?1:0; + s->WriteSwap( b );} + break; + case kChar: + s->WriteSwap( fC ); + break; + case kInt : + s->WriteSwap( fI ); + break; + case kUInt: + s->WriteSwap( fU ); + break; + case kFloat: + s->WriteSwap( fF ); + break; + case kDouble: + s->WriteSwap( fD ); + break; + case kNone : + break; + } +} + +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +void plGenericVar::Read(hsStream* s) +{ + delete [] fName; + fName = s->ReadSafeString(); + fValue.Read(s); +} + +void plGenericVar::Write(hsStream* s) +{ + s->WriteSafeString(fName); + fValue.Write(s); +} + + +////////////////////////////////// + +void plGenericType::SetVar(Types t, unsigned int size, void* val) +{ + fType = t; + + switch (t) + { + case kInt : + { + hsAssert(size <= sizeof(fI), "plGenericType::SetVar size too large for int"); + memcpy(&fI, val, size); + break; + } + case kUInt : + { + hsAssert(size <= sizeof(fU), "plGenericType::SetVar size too large for unsigned int"); + memcpy(&fU, val, size); + break; + } + case kFloat : + { + hsAssert(size <= sizeof(fF), "plGenericType::SetVar size too large for float"); + memcpy(&fF, val, size); + break; + } + case kDouble : + { + hsAssert(size <= sizeof(fD), "plGenericType::SetVar size too large for double"); + memcpy(&fD, val, size); + break; + } + case kBool : + { + hsAssert(size <= sizeof(fB), "plGenericType::SetVar size too large for bool"); + memcpy(&fB, val, size); + break; + } + case kChar : + { + hsAssert(size <= sizeof(fC), "plGenericType::SetVar size too large for char"); + memcpy(&fC, val, size); + break; + } + case kString : + { + delete [] fS; + fS = TRACKED_NEW char[size+1]; + memcpy(fS,val,size); + fS[size] = 0; + break; + } + case kNone : + break; + default: + hsAssert(false,"plGenericType::SetVar unknown type"); + } +} + + +std::string plGenericType::GetAsStdString() const +{ + std::string s; + + switch (fType) + { + case kInt : + { + xtl::format(s,"%d",fI); + break; + } + case kBool : + case kUInt : + { + xtl::format(s,"%u",fType==kBool?fB:fU); + break; + } + case kFloat : + case kDouble : + { + xtl::format(s,"%f",fType==kDouble?fD:fF); + break; + } + case kChar : + { + xtl::format(s,"%c",fC); + break; + } + case kAny : + case kString : + { + s = fS; + break; + } + case kNone : + break; + default: + hsAssert(false,"plGenericType::GetAsStdString unknown type"); + } + + return s; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plGenericVar.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plGenericVar.h new file mode 100644 index 00000000..16a2ba18 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plGenericVar.h @@ -0,0 +1,181 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plGenericVar_inc +#define plGenericVar_inc + +#include "hsTypes.h" +#include "hsUtils.h" +#include "hsStlUtils.h" +#include "../pnFactory/plCreatable.h" + +class hsStream; + +// +// a generic (unioned) type +// +class plGenericType +{ +public: + typedef char* CharPtr; + +protected: + union + { + Int32 fI; + UInt32 fU; + float fF; + double fD; + bool fB; + CharPtr fS; + char fC; + }; + +public: + + enum Types + { + kInt = 0, + kFloat, + kBool, + kString, + kChar, + kAny, + kUInt, + kDouble, + kNone = 0xff + }; + +protected: + UInt8 fType; + + const Int32 &IToInt( void ) const; + const UInt32 &IToUInt( void ) const; + const float &IToFloat( void ) const; + const double &IToDouble( void ) const; + const bool &IToBool( void ) const; + const CharPtr &IToString( void ) const; + const char &IToChar( void ) const; + + void IDeallocString() { if (fType==kString || fType==kAny) {delete [] fS; fS=nil;} } +public: + + plGenericType() : fType(kNone) { Reset(); } + plGenericType(const plGenericType& c) { CopyFrom(c); } + virtual ~plGenericType() { IDeallocString(); } + + plGenericType& operator=(const plGenericType& c) { CopyFrom(c); return *this; } + + void CopyFrom(const plGenericType& c); + virtual void Reset(); + operator Int32() const { return IToInt(); } + operator UInt32() const { return IToUInt(); } + operator double() const { return IToDouble(); } + operator float() const { return IToFloat(); } + operator bool() const { return IToBool(); } + operator const CharPtr() const { return IToString(); } + operator char() const { return IToChar(); } + operator unsigned int() const { return IToUInt(); } + operator int() const { return IToInt(); } + + void SetType(Types t) { fType=t; } + UInt8 GetType( void ) const { return fType; } + + std::string GetAsStdString() const; + + // implicit set + void Set( Int32 i ) { fI = i; fType = kInt; } + void Set( UInt32 i ) { fU = i; fType = kUInt; } + void Set( float f ) { fF = f; fType = kFloat; } + void Set( double d ) { fD = d; fType = kDouble; } + void Set( bool b ) { fB = b; fType = kBool; } + void Set( CharPtr s ) { IDeallocString(); fS = hsStrcpy(s); fType = kString; } + void Set( char c ) { fC = c; fType = kChar; } + + // explicit set + void SetInt( Int32 i ) { fI = i; fType = kInt; } + void SetUInt( UInt32 i ) { fU = i; fType = kUInt; } + void SetFloat( float f ) { fF = f; fType = kFloat; } + void SetDouble( double d ) { fD = d; fType = kDouble; } + void SetBool( bool b ) { fB = b; fType = kBool; } + void SetString( CharPtr s ) { IDeallocString(); fS = hsStrcpy(s); fType = kString; } + void SetChar( char c ) { fC = c; fType = kChar; } + void SetAny( CharPtr s ) { IDeallocString(); fS = hsStrcpy(s); fType = kAny; } + void SetNone( void ) { fType = kNone; } + void SetVar(Types t, unsigned int size, void* val); + + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); +}; + +// +// a generic variable (similar to pfConsoleCmdParam) +// +class plGenericVar +{ +protected: + plGenericType fValue; + char* fName; +public: + plGenericVar(const plGenericVar &c) : fName(nil) { CopyFrom(c); } + plGenericVar(const char* name=nil) : fName(nil) { SetName(name); } + virtual ~plGenericVar() { delete [] fName; } + + virtual void Reset() { Value().Reset(); } // reset runtime state, not inherent state + plGenericVar& operator=(const plGenericVar &c) { CopyFrom(c); return *this; } + void CopyFrom(const plGenericVar &c) { delete [] fName; fName=hsStrcpy(c.GetName()); fValue=c.Value(); } + const char* GetName() const { return fName; } + void SetName(const char* n) { delete [] fName; fName = hsStrcpy(n); } + plGenericType& Value() { return fValue; } + const plGenericType& Value() const { return fValue; } + + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); +}; + + +// A creatable wrapper for plGenericType + +class plCreatableGenericValue : public plCreatable +{ +public: + plGenericType fValue; + CLASSNAME_REGISTER( plCreatableGenericValue ); + GETINTERFACE_ANY( plCreatableGenericValue, plCreatable ); + void Read(hsStream* s, hsResMgr* mgr) { fValue.Read(s);} + void Write(hsStream* s, hsResMgr* mgr) { fValue.Write(s);} + plGenericType& Value() { return fValue; } + const plGenericType& Value() const { return fValue; } + operator Int32() const { return (Int32)fValue; } + operator UInt32() const { return (UInt32)fValue; } + operator float() const { return (float)fValue; } + operator double() const { return (double)fValue; } + operator bool() const { return (bool)fValue; } + operator const char *() const { return (const char *)fValue; } + operator char() const { return (char)fValue; } +}; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetAddress.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetAddress.cpp new file mode 100644 index 00000000..a5db02c6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetAddress.cpp @@ -0,0 +1,149 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef SERVER + +#include "plNetAddress.h" +#include "pnNetCommon.h" + + +plNetAddress::plNetAddress() +{ + Clear(); +} + + +plNetAddress::plNetAddress(UInt32 addr, int port) +{ + Clear(); + SetHost(addr); + SetPort(port); +} + + +plNetAddress::plNetAddress(const char * addr, int port) +{ + Clear(); + SetHost(addr); + SetPort(port); +} + + +bool plNetAddress::SetAnyAddr() +{ + fAddr.sin_family = AF_INET; + fAddr.sin_addr.s_addr = INADDR_ANY; + return true; +} + + +bool plNetAddress::SetAnyPort() +{ + fAddr.sin_family = AF_INET; + fAddr.sin_port = htons(0); + return true; +} + + +bool plNetAddress::SetPort(int port) +{ + fAddr.sin_family = AF_INET; + fAddr.sin_port = htons(port); + return true; +} + + +void plNetAddress::Clear() +{ + memset(&fAddr,0,sizeof(fAddr)); + fAddr.sin_family = AF_INET; + fAddr.sin_addr.s_addr = INADDR_ANY; +} + + +int plNetAddress::GetPort() const +{ + return ntohs(fAddr.sin_port); +} + + +std::string plNetAddress::GetHostString() const +{ + return std::string(pnNetCommon::GetTextAddr(fAddr.sin_addr.s_addr)); +} + +UInt32 plNetAddress::GetHost() const +{ + return fAddr.sin_addr.s_addr; +} + + +std::string plNetAddress::GetHostWithPort() const +{ + static const int buf_len = 1024; + char buf[buf_len]; + sprintf(buf,"%s:%d",pnNetCommon::GetTextAddr(fAddr.sin_addr.s_addr),GetPort()); + return std::string(buf); +} + + +bool plNetAddress::SetHost(const char * hostname) +{ + fAddr.sin_addr.s_addr = pnNetCommon::GetBinAddr(hostname); + fAddr.sin_family = AF_INET; + return true; +} + +bool plNetAddress::SetHost(UInt32 addr) +{ + memcpy(&fAddr.sin_addr, &addr,sizeof(addr)); + fAddr.sin_family = AF_INET; + return true; +} + +std::string plNetAddress::AsString() const +{ + char buf[100] = ""; + sprintf(buf,"IP:%s:%d",pnNetCommon::GetTextAddr(fAddr.sin_addr.s_addr),GetPort()); + return std::string(buf); +} + + +void plNetAddress::Read(hsStream * s) +{ + s->ReadSwap((UInt32*)&fAddr.sin_addr.s_addr); + s->ReadSwap(&fAddr.sin_port); + s->ReadSwap(&fAddr.sin_family); +} + +void plNetAddress::Write(hsStream * s) +{ + s->WriteSwap((UInt32)fAddr.sin_addr.s_addr); + s->WriteSwap(fAddr.sin_port); + s->WriteSwap(fAddr.sin_family); +} + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetAddress.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetAddress.h new file mode 100644 index 00000000..3ac6db8f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetAddress.h @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef SERVER + +#ifndef plNetAddress_h_inc +#define plNetAddress_h_inc + +#include "hsUtils.h" +#include "hsStlUtils.h" +#include "hsStream.h" + +#if defined(HS_BUILD_FOR_WIN32) +#include "hsWindows.h" +#elif defined( HS_BUILD_FOR_UNIX ) +#include +#include +#include +#include +#else +#error "Must Include net stuff for this OS here" +#endif + + +#ifdef SetPort +#undef SetPort +#endif + +typedef sockaddr_in AddressType; + +class plNetAddress +{ + // fAddr must be first field + AddressType fAddr; + +public: + plNetAddress(); + plNetAddress(UInt32 addr, int port); + plNetAddress(const char * addr, int port); + virtual ~plNetAddress(){} + + void Clear(); + bool SetAnyAddr(); + bool SetAnyPort(); + bool SetPort(int port); + bool SetHost(const char * hostname); + bool SetHost(UInt32 ip4addr); + int GetPort() const; + std::string GetHostString() const; + UInt32 GetHost() const; + std::string GetHostWithPort() const; + const AddressType & GetAddressInfo() const { return fAddr; } + AddressType & GetAddressInfo() { return fAddr; } + + std::string AsString() const; + + void Read(hsStream * stream); + void Write(hsStream * stream); +}; + + + +#endif // plNetAddress_h_inc +#endif // SERVER diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetApp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetApp.cpp new file mode 100644 index 00000000..3cc26b10 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetApp.cpp @@ -0,0 +1,153 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetApp.h" +#include "hsStlUtils.h" +#include "../pnMessage/plMessage.h" + +plNetApp* plNetApp::fInstance = nil; +plNetObjectDebuggerBase* plNetObjectDebuggerBase::fInstance=nil; + +// +// STATIC +// +plNetApp* plNetApp::GetInstance() +{ + return fInstance; +} + +void plNetApp::SetInstance(plNetApp* app) +{ + fInstance = app; +} + +bool plNetApp::StaticErrorMsg(const char* fmt, ...) +{ + if ( !GetInstance() ) + return true; + va_list args; + va_start(args, fmt); + return GetInstance()->ErrorMsgV(fmt, args); +} + +bool plNetApp::StaticDebugMsg(const char* fmt, ...) +{ + if ( !GetInstance() ) + return true; + va_list args; + va_start(args, fmt); + return GetInstance()->DebugMsgV(fmt, args); +} + +bool plNetApp::StaticWarningMsg(const char* fmt, ...) +{ + if ( !GetInstance() ) + return true; + va_list args; + va_start(args, fmt); + return GetInstance()->WarningMsgV(fmt, args); +} + +bool plNetApp::StaticAppMsg(const char* fmt, ...) +{ + if ( !GetInstance() ) + return true; + va_list args; + va_start(args, fmt); + return GetInstance()->AppMsgV(fmt, args); +} + + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// + +plNetClientApp::plNetClientApp() : fCCRLevel(0) +{ + +} + +// +// STATIC FXN +// Inherit net cascade state. Msg version +// +void plNetClientApp::InheritNetMsgFlags(const plMessage* parentMsg, plMessage* childMsg, bool startCascade) +{ + if (childMsg) + { + UInt32 childMsgFlags = childMsg->GetAllBCastFlags(); + InheritNetMsgFlags(parentMsg ? parentMsg->GetAllBCastFlags() : 0, &childMsgFlags, startCascade); + childMsg->SetAllBCastFlags(childMsgFlags); + } +} + +// +// STATIC FXN +// Inherit net cascade state. Flags version +// Set startCasCascade=true if called from outside the dispatcher, so that +// the dispatcher won't mess with the flags when it goes to send out the msg. +// +void plNetClientApp::InheritNetMsgFlags(UInt32 parentMsgFlags, UInt32* childMsgFlags, bool startCascade) +{ + if (!(*childMsgFlags & plMessage::kNetStartCascade)) + { + if (parentMsgFlags & plMessage::kNetSent) + *childMsgFlags |= plMessage::kNetSent; + else + *childMsgFlags &= ~plMessage::kNetSent; + + if (parentMsgFlags & plMessage::kNetNonLocal) + *childMsgFlags |= plMessage::kNetNonLocal; + else + *childMsgFlags &= ~plMessage::kNetNonLocal; + + if (startCascade) + *childMsgFlags |= plMessage::kNetStartCascade; + else + *childMsgFlags &= ~plMessage::kNetStartCascade; + } +} + +// +// STATIC +// +void plNetClientApp::UnInheritNetMsgFlags(plMessage* msg) +{ + msg->SetBCastFlag( plMessage::kCCRSendToAllPlayers, 0 ); + // if msg was propagated from another client...(and not originated on the server) + if (msg && msg->HasBCastFlag(plMessage::kNetPropagate)) + { + // This msg (and all it's responses) should not be resent, since we just recvd it. + // Make sure it's marked for localPropagation now that it has arrived. + // Also flag it as a remote (non-local) msg + msg->SetBCastFlag(plMessage::kNetSent | + plMessage::kNetNonLocal | + plMessage::kLocalPropagate | + plMessage::kNetStartCascade); + + // clear the 'force' option, so it doesn't get sent out again + msg->SetBCastFlag(plMessage::kNetForce | plMessage::kNetNonDeterministic, 0); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetApp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetApp.h new file mode 100644 index 00000000..34b5b648 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetApp.h @@ -0,0 +1,245 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetApp_h +#define plNetApp_h + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsBitVector.h" +#include "plNetGroup.h" + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plUoid.h" + + +#include "../../PubUtilLib/plStatusLog/plLoggable.h" + +#include + +#define plVerifyConditionRet(NetApp,cond,ret,str) \ + do { \ + if (!(cond)) { \ + char * _str_ = str; \ + (NetApp)->ErrorMsg(_str_); \ + hsAssert(cond,_str_); \ + return ret; \ + } \ + } while (0) + +#define plVerifyCondition(NetApp,cond,str) \ + plVerifyConditionRet(NetApp,cond,hsFail,str) + + +class plNetMember; +class plSynchedObject; +class plKey; +class plNetMessage; + +typedef std::vector plNetMemberList; +typedef std::vector plNetPlayerIDList; + +// +// Common baseclasses for client and server net apps +// + +class plNetApp : public hsKeyedObject, public plLoggable // so it can recv messages +{ +protected: + static plNetApp* fInstance; + hsBitVector fFlagsVec; +public: + enum FlagBits // common to both client and server + { + kNullSend=0, + kNetCoreSingleThreaded, + kScreenMessages, // filter out illegal net game messages, used by gameserver&client + + FLAG_CEILING = 10 // stay below this or conflict with client/server specific flags + }; + + static plNetApp* GetInstance(); + static void SetInstance(plNetApp* app); + + plNetApp() {} + virtual ~plNetApp() {} + + CLASSNAME_REGISTER( plNetApp ); + GETINTERFACE_ANY( plNetApp, hsKeyedObject); + + virtual void Shutdown() {} + + void SetFlagsBit(int b, hsBool on=true) { fFlagsVec.SetBit(b, on); } + bool GetFlagsBit(int b) const { return fFlagsVec.IsBitSet(b) ? true : false; } + + static bool StaticWarningMsg(const char* fmt, ...); + static bool StaticErrorMsg(const char* fmt, ...); + static bool StaticDebugMsg(const char* fmt, ...); + static bool StaticAppMsg(const char* fmt, ...); +}; + +// +// base netApp class specific to client code +// +class plVaultPlayerNode; +class plNetClientApp : public plNetApp +{ +private: + int fCCRLevel; // 0 for players, 1-4 for CCRs + + friend class plDispatch; + + virtual int ISendGameMessage(plMessage* msg) { hsAssert(false, "stub"); return hsFail; } + +public: + enum ClientFlagBits + { + kDeferMsgs = FLAG_CEILING, // currently ignoring most msgs because the game is not active + kDisabled, // client. networking has been disabled + kDisableOnNextUpdate, // client. disable networking in next update call + kLocalTriggers, // marks all triggers as local only + kEchoVoice, // send voice packets back to the speaker + kRequestP2P, // wants to play peer to peer + kIndirectMember, // behind a firewall, can't player P2P + kSendingVoice, // sending a voice packet + kSendingActions, // sending a game message + kAllowTimeOut, // allow clients to timeout and be kicked off the server + kAllowAuthTimeOut, // allow clients to timeout while authenticating + kPlayingGame, // set when client is actively part of an age. + kShowLists, // debug info on-screen + kShowRooms, // debug info on-screen + kShowAvatars, // Avatar position/orientation info + kShowRelevanceRegions, // debug info on-screen + kConnectedToVault, // initial connection to vault achieved + kBanLinking, // player is not allowed to link + kSilencePlayer, // player's outbound communication is shutoff + kConsoleOutput, // net log output is echoed to console + kLoadingInitialAgeState, // set when we first link in to an age and are recving its initial state + kLaunchedFromSetup, // set if we were launched from the setup program + kCCRVaultConnected, // set if we've connected to the CCR vault + kNetClientCommInited, // set if the netClientComm interface has been initialized + kNeedToSendInitialAgeStateLoadedMsg,// internal use only, when we need to send plInitialAgeStateLoadedMsg + kNeedToSendAgeLoadedMsg, + kDemoMode, // set if this is a demo - limited play + kNeedInitialAgeStateCount, // the server must tell us how many age states to expect + kLinkingToOfflineAge, // set if we're linking to the startup age + }; + + CLASSNAME_REGISTER( plNetClientApp ); + GETINTERFACE_ANY( plNetClientApp, plNetApp); + + plNetClientApp(); + + // statics + static plNetClientApp* GetInstance() { return plNetClientApp::ConvertNoRef(fInstance); } + static const plNetClientApp* GetConstInstance() { return plNetClientApp::ConvertNoRef(fInstance); } + static void InheritNetMsgFlags(const plMessage* parentMsg, plMessage* childMsg, bool startCascade); + static void InheritNetMsgFlags(UInt32 parentMsgFlags, UInt32* childMsgFlags, bool startCascade); + static void UnInheritNetMsgFlags(plMessage* msg); + + // functions that all net client apps should implement + virtual int SendMsg(plNetMessage* msg) = 0; + virtual UInt32 GetPlayerID() const = 0; + virtual const char * GetPlayerName( const plKey avKey=nil ) const = 0; + + // commonly used net client app functions + virtual float GetCurrentAgeTimeOfDayPercent() const { hsAssert(false, "stub"); return 0.; } + virtual bool ObjectInLocalAge(const plSynchedObject* obj) const { hsAssert(false, "stub"); return false; } + virtual UInt8 GetJoinOrder() const { hsAssert(false, "stub"); return 0; } + virtual hsBool IsRemotePlayerKey(const plKey p, int* idx=nil) { hsAssert(false, "stub"); return false; } + virtual plKey GetLocalPlayerKey() const { hsAssert(false, "stub"); return nil; } + virtual plSynchedObject* GetLocalPlayer(hsBool forceLoad=false) const { hsAssert(false, "stub"); return nil; } + virtual plNetGroupId SelectNetGroup(plSynchedObject* objIn, plKey groupKey) { hsAssert(false, "stub"); return plNetGroup::kNetGroupUnknown; } + virtual int IsLocallyOwned(const plSynchedObject* obj) const { hsAssert(false, "stub"); return 0; } + virtual int IsLocallyOwned(const plUoid&) const { hsAssert(false, "stub"); return 0; } + virtual plNetGroupId GetEffectiveNetGroup(const plSynchedObject* obj) const { hsAssert(false, "stub"); return plNetGroup::kNetGroupUnknown; } + virtual int Update(double secs) { return hsOK;} + virtual const char* GetServerLogTimeAsString(std::string& ts) const { hsAssert(false, "stub"); return nil; } + virtual plUoid GetAgeSDLObjectUoid(const char* ageName) const { hsAssert(false, "stub"); return plUoid(); } + virtual void StayAlive(double secs) {} + virtual void QueueDisableNet( bool showDlg, const char msg[] ) {} + + bool IsEnabled() const { return !GetFlagsBit(kDisabled); } + bool InDemoMode() const { return GetFlagsBit(kDemoMode); } + bool IsLoadingInitialAgeState() const { return GetFlagsBit(kLoadingInitialAgeState); } + void SetLaunchedFromSetup(bool b) { SetFlagsBit(kLaunchedFromSetup, b); } + bool GetLaunchedFromSetup() const { return GetFlagsBit(kLaunchedFromSetup); } + + // CCR stuff +#ifdef PLASMA_EXTERNAL_RELEASE + void SetCCRLevel(int level) { } + int GetCCRLevel() const { return 0; } + bool AmCCR() const { return false; } +#else + void SetCCRLevel(int level) { fCCRLevel=level; } + int GetCCRLevel() const { return fCCRLevel; } + bool AmCCR() const { return (fCCRLevel>0); } +#endif +}; + +// +// base netApp class specific to server code +// +class plNetServerApp : public plNetApp +{ +public: + enum ServerFlagBits + { + kLastFlagBitsValue = FLAG_CEILING, // get past plNetApp flags + kDone, // exit update loop. + kDumpStats, // dump stats to log file + kDisableStateLogging, // used by gameserver + kGameStateIsDirty, // used by gameserver + kDumpConfigDoc, // dump config options queries to log file + kProtectedServer, // set by a protected lobby + kRequireProtectedCCRs, // CCRS must have logged in thru a protected lobby, used by gameserver + kProcessedPendingMsgs, // Used by front-end server + }; + + CLASSNAME_REGISTER( plNetServerApp ); + GETINTERFACE_ANY( plNetServerApp, plNetApp); + + virtual int SendMsg(plNetMessage* msg) = 0; +}; + +// +// abstract base class for net client object debugger +// +class plNetObjectDebuggerBase +{ +private: + static plNetObjectDebuggerBase* fInstance; +public: + static plNetObjectDebuggerBase* GetInstance() { return fInstance; } + static void SetInstance(plNetObjectDebuggerBase* i) { fInstance=i; } + virtual bool IsDebugObject(const hsKeyedObject* obj) const = 0; + virtual void LogMsgIfMatch(const char* msg) const = 0; // write to status log if there's a string match + virtual void LogMsg(const char* msg) const = 0; + + virtual bool GetDebugging() const = 0; + virtual void SetDebugging(bool b) = 0; +}; + +#endif // plNetApp_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetCommonStats.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetCommonStats.h new file mode 100644 index 00000000..505cfc9b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetCommonStats.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetCommonStats_inc +#define plNetCommonStats_inc +/* +// +// abstract class for NetCore stats +// +class hsStream; +class plNetCommonStats +{ +public: + plNetCommonStats() {} + virtual ~plNetCommonStats() {} + + virtual void Read(hsStream* s) = 0; + virtual void Write(hsStream* s) = 0; + virtual plNetCommonStats* Copy() const = 0; +}; +*/ +#endif // plNetCommonStats_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetGroup.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetGroup.cpp new file mode 100644 index 00000000..40d6a538 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetGroup.cpp @@ -0,0 +1,32 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetGroup.h" + +plNetGroupId plNetGroup::kNetGroupUnknown( plLocation::MakeReserved( 0 ) ); +plNetGroupId plNetGroup::kNetGroupLocalPlayer( plLocation::MakeReserved( 1 ), 1); +plNetGroupId plNetGroup::kNetGroupRemotePlayer( plLocation::MakeReserved( 2 ) , 1); +plNetGroupId plNetGroup::kNetGroupLocalPhysicals( plLocation::MakeReserved( 3 ) , 1); +plNetGroupId plNetGroup::kNetGroupRemotePhysicals( plLocation::MakeReserved( 4 ) , 1); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetGroup.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetGroup.h new file mode 100644 index 00000000..bfd69e53 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetGroup.h @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetGroup_h +#define plNetGroup_h + +#include "../pnKeyedObject/plUoid.h" +#include "hsStream.h" +#include "hsStlUtils.h" + +class plNetGroupId +{ +private: + enum NetGroupConstants + { + kNetGroupConstant = 0x01, + kNetGroupLocal = 0x02, + }; + + plLocation fId; + UInt8 fFlags; + std::string fDesc; // description of room +public: + + plNetGroupId() : fFlags(0) {} + plNetGroupId(const plLocation& id, const UInt8 flags) : fId(id), fFlags(flags) { } + plNetGroupId(const plLocation& id) : fId(id), fFlags(0) { } + + hsBool IsConstant() { return (fFlags & kNetGroupConstant) != 0; } + void SetConstant(hsBool constantGroup) { fFlags &= constantGroup ? kNetGroupConstant : 0; } + + plLocation& Room() { return fId; } + const char* GetDesc() const { return fDesc.c_str(); } + void SetDesc(const char* c) { fDesc = c; } + + hsBool operator==(const plNetGroupId& netGroup) const { return fId == netGroup.fId; } + hsBool operator!=(const plNetGroupId& netGroup) const { return fId != netGroup.fId; } + bool operator<(const plNetGroupId& netGroup) const { return fId < netGroup.fId; } + + // read and write to hsStream + void Write(hsStream *s) const { fId.Write(s); s->WriteSwap(fFlags); } + void Read(hsStream *s) { fId.Read(s); s->LogReadSwap(&fFlags,"GroupId Flags"); } +}; + +namespace plNetGroup +{ + extern plNetGroupId kNetGroupLocalPlayer; + extern plNetGroupId kNetGroupRemotePlayer; + extern plNetGroupId kNetGroupUnknown; + extern plNetGroupId kNetGroupLocalPhysicals; + extern plNetGroupId kNetGroupRemotePhysicals; +} + +#endif // plNetGroup_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetServers.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetServers.cpp new file mode 100644 index 00000000..21c8752b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetServers.cpp @@ -0,0 +1,128 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plNetServers.h" + + +// Server Defns + +const char* plNetServerConstants::ServerPrograms[] = +{ +#ifndef HS_DEBUGGING +#if HS_BUILD_FOR_WIN32 + "plNetServerAgent.exe", + "plNetLobbyServer.exe", + "plNetGameServer.exe", + "plNetVaultServer.exe", + "plNetAuthServer.exe", + "plNetAdminServer.exe", + "plNetLookupServer.exe", +#elif HS_BUILD_FOR_UNIX + "plNetServerAgent", + "plNetLobbyServer", + "plNetGameServer", + "plNetVaultServer", + "plNetAuthServer", + "plNetAdminServer", + "plNetLookupServer", +#else +#error "No servers work on this Platform!" +#endif +#else // Debug +#if HS_BUILD_FOR_WIN32 + "plNetServerAgent_dbg.exe", + "plNetLobbyServer_dbg.exe", + "plNetGameServer_dbg.exe", + "plNetVaultServer_dbg.exe", + "plNetAuthServer_dbg.exe", + "plNetAdminServer_dbg.exe", + "plNetLookupServer_dbg.exe", +#elif HS_BUILD_FOR_UNIX + "plNetServerAgent.dbg", + "plNetLobbyServer.dbg", + "plNetGameServer.dbg", + "plNetVaultServer.dbg", + "plNetAuthServer.dbg", + "plNetAdminServer.dbg", + "plNetLookupServer.dbg", +#else +#error "No servers work on this Platform!" +#endif +#endif +}; + +// +// STATIC +// +const char* plNetServerConstants::GetServerName(int type) +{ + switch(type) + { + default: +// hsAssert(false, "unknown type"); // not the right place to catch this problem. + return "UNKNOWN"; + case kAgent: + return plNetServerAgentConstants::GetName(); + case kLobby: + return plNetLobbyServerConstants::GetName(); + case kGame: + return plNetGameServerConstants::GetName(); + case kVault: + return plNetVaultServerConstants::GetName(); + case kAuth: + return plNetAuthServerConstants::GetName(); + case kAdmin: + return plNetAdminServerConstants::GetName(); + case kLookup: + return plNetLookupServerConstants::GetName(); + case kClient: + return "plClient"; + } +} + +UInt16 plNetServerConstants::GetPort(int type) +{ + switch(type) + { + default: +// hsAssert(false, "unknown type"); // not the right place to catch this problem. + return 0; + case kGame: + return 0; + case kAgent: + return plNetServerAgentConstants::GetPort(); + case kLobby: + return plNetLobbyServerConstants::GetPort(); + case kVault: + return plNetVaultServerConstants::GetPort(); + case kAuth: + return plNetAuthServerConstants::GetPort(); + case kAdmin: + return plNetAdminServerConstants::GetPort(); + case kLookup: + return plNetLookupServerConstants::GetPort(); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetServers.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetServers.h new file mode 100644 index 00000000..af871243 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetServers.h @@ -0,0 +1,148 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_NET_SERVERS_H +#define PL_NET_SERVERS_H + +// +// Server Declarations and Constants +// + +#include "hsTypes.h" + +// +// Windows Class Name for All Servers +// +#define PARABLE_WINCLASSNAME "ParableServer" +#define PARABLE_PLS_WINCLASSNAME "ParableServer_PLS" + + +class plNetServerConstants +{ +public: + enum ServerTypes // Changing the order WILL affect Data including Databases!!! + { + kInvalidLo, + kAgent, + kLobby, + kGame, + kVault, + kAuth, + kAdmin, + kLookup, + + kClient, + kInvalidHi, + }; +private: + + static const char* ServerPrograms[]; + +public: + static const char* GetServerExe(int type) { return (type > kInvalidLo && type < kInvalidHi)?ServerPrograms[type-1]:nil; } + static const char* GetServerName(int type); + static UInt16 GetPort(int type); + static const char* GetServerTypeStr(int type) + { + switch(type) + { + case kAgent: return "kAgent"; + case kLobby: return "kLobby"; + case kGame: return "kGame"; + case kVault: return "kVault"; + case kAuth: return "kAuth"; + case kAdmin: return "kAdmin"; + case kLookup: return "kLookup"; + case kClient: return "kClient"; + default: return "???"; + } + } +}; + + +class plNetServerAgentConstants +{ +public: + static const char* GetName() { return "Server_Agent"; } + static const UInt16 GetPort() { return 4800; } + static const plNetServerConstants::ServerTypes GetType() { return plNetServerConstants::kAgent; } +}; + + +class plNetLookupServerConstants +{ +public: + static const char* GetName() { return "Lookup_Server"; } + static const UInt16 GetPort() { return 2000; } + static const plNetServerConstants::ServerTypes GetType() { return plNetServerConstants::kLookup; } +}; + + +class plNetLobbyServerConstants +{ +public: + static const char* GetName() { return "Generated_Lobby"; } + static const UInt16 GetPort() { return 5000; } + static const plNetServerConstants::ServerTypes GetType() { return plNetServerConstants::kLobby; } +}; + + +class plNetVaultServerConstants +{ +public: + static const char* GetName() { return "Vault_Server"; } + static const UInt16 GetPort() { return 2001; } + static const plNetServerConstants::ServerTypes GetType() { return plNetServerConstants::kVault; } +}; + + +class plNetAuthServerConstants +{ +public: + static const char* GetName() { return "Auth_Server"; } + static const UInt16 GetPort() { return 2002; } + static const plNetServerConstants::ServerTypes GetType() { return plNetServerConstants::kAuth; } +}; + + +class plNetAdminServerConstants +{ +public: + static const char* GetName() { return "Admin_Server"; } + static const UInt16 GetPort() { return 2003; } + static const plNetServerConstants::ServerTypes GetType() { return plNetServerConstants::kAdmin; } +}; + +class plNetGameServerConstants +{ +public: + static const char* GetName() { return "Game_Server"; } + static const UInt16 GetLowPort() { return 5001;} + static const UInt16 GetHighPort() { return 6001;} + static const plNetServerConstants::ServerTypes GetType() { return plNetServerConstants::kGame; } +}; + + +#endif //PL_NET_SERVERS_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetSharedState.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetSharedState.cpp new file mode 100644 index 00000000..d7c330d5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetSharedState.cpp @@ -0,0 +1,99 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "plNetSharedState.h" +#include "plGenericVar.h" +#include "../pnMessage/plMessage.h" + +plNetSharedState::plNetSharedState(char* name) : fServerMayDelete(false) +{ + SetName(name); +} + +plNetSharedState::~plNetSharedState() +{ + Reset(); +} + +void plNetSharedState::Reset() +{ + int i; + for(i=0;iGetName()); + + SetServerMayDelete(ss->GetServerMayDelete()); + + // copy vars + int i; + for(i=0;iGetNumVars();i++) + { + plGenericVar* sv = TRACKED_NEW plGenericVar; + *sv = *(ss->GetVar(i)); + AddVar(sv); + } +} + +void plNetSharedState::Read(hsStream* stream) +{ + Reset(); + + plMsgStdStringHelper::Peek(fName, stream); + Int32 num=stream->ReadSwap32(); + fServerMayDelete = stream->Readbool(); + + fVars.reserve(num); + int i; + for(i=0;iRead(stream); + AddVar(v); + } +} + +void plNetSharedState::Write(hsStream* stream) +{ + plMsgStdStringHelper::Poke(fName, stream); + Int32 num=GetNumVars(); + stream->WriteSwap32(num); + + stream->Writebool(fServerMayDelete); + int i; + for(i=0;iWrite(stream); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetSharedState.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetSharedState.h new file mode 100644 index 00000000..90a24b4d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plNetSharedState.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetSharedState_inc +#define plNetSharedState_inc + +#include "hsTypes.h" +#include "hsUtils.h" +#include "hsStlUtils.h" + +class hsStream; + +// +// used to exchange generic state with the server +// +class plGenericVar; +class plNetSharedState +{ +protected: + std::string fName; + std::vector fVars; + bool fServerMayDelete; // ok to delete (don't save) since this state is equivalent to the default state +public: + + plNetSharedState(char* name=nil); + virtual ~plNetSharedState(); + + virtual void Copy(plNetSharedState* ss); + void Reset(); + + int GetNumVars() const { return fVars.size(); } + plGenericVar* GetVar(int i) const { return fVars[i]; } + + void RemoveVar(int i) { fVars.erase(fVars.begin()+i); } + void AddVar(plGenericVar* v) { fVars.push_back(v); } + + const char* GetName() const { return fName.c_str(); } + void SetName(const char* n) { if (n) fName=n; } + + bool GetServerMayDelete() const { return fServerMayDelete; } + void SetServerMayDelete(bool d) { fServerMayDelete=d; } + + // IO + virtual void Read(hsStream* stream); + virtual void Write(hsStream* stream); +}; + +#endif // plNetSharedState_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSDLTypes.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSDLTypes.cpp new file mode 100644 index 00000000..465d0ee4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSDLTypes.cpp @@ -0,0 +1,39 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSDLTypes.h" + +const char kSDLPhysical[]="Physical"; +const char kSDLAGMaster[]="AGMaster"; +const char kSDLResponder[]="Responder"; +const char kSDLClothing[]="Clothing"; +const char kSDLAvatar[]="Avatar"; +const char kSDLAvatarPhysical[]="AvatarPhysical"; +const char kSDLLayer[]="Layer"; +const char kSDLSound[]="Sound"; +const char kSDLXRegion[]="XRegion"; +const char kSDLMorphSequence[]="MorphSequence"; +const char kSDLParticleSystem[]="ParticleSystem"; +const char kSDLCloneMessage[]="CloneMessage"; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSDLTypes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSDLTypes.h new file mode 100644 index 00000000..79f3a7e4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSDLTypes.h @@ -0,0 +1,42 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSDLTypes_inc +#define plSDLTypes_inc + +extern const char kSDLPhysical[]; +extern const char kSDLAGMaster[]; +extern const char kSDLResponder[]; +extern const char kSDLClothing[]; +extern const char kSDLAvatar[]; +extern const char kSDLAvatarPhysical[]; +extern const char kSDLLayer[]; +extern const char kSDLSound[]; +extern const char kSDLXRegion[]; +extern const char kSDLMorphSequence[]; +extern const char kSDLParticleSystem[]; +extern const char kSDLCloneMessage[]; + +#endif // plSDLTypes_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedObject.cpp new file mode 100644 index 00000000..0671bada --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedObject.cpp @@ -0,0 +1,600 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSynchedObject.h" +#include "plSynchedValue.h" +#include "plNetApp.h" +#include "plNetGroup.h" +#include "hsResMgr.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnMessage/plSDLModifierMsg.h" +#include "../pnMessage/plSetNetGroupIDMsg.h" + +#include + +// statics +plSynchedObject* plSynchedObject::fStaticSynchedObj=nil; +std::vector plSynchedObject::fDirtyStates; +std::vector plSynchedObject::fSynchStateStack; + +plSynchedObject::plSynchedObject() : + fSynchFlags(0), +#ifdef USE_SYNCHED_VALUES + fSynchedValueAddrOffsets(nil), + fNumSynchedValues(0), + fSynchedValueFriends(nil), + fNumSynchedValueFriends(0), +#endif + fNetGroup(plNetGroup::kNetGroupUnknown) +{ + fStaticSynchedObj=this; +} + +plSynchedObject::~plSynchedObject() +{ +#ifdef USE_SYNCHED_VALUES + delete [] fSynchedValueAddrOffsets; + delete [] fSynchedValueFriends; +#endif +} + +hsBool plSynchedObject::MsgReceive(plMessage* msg) +{ + plSetNetGroupIDMsg* setNetGroupID = plSetNetGroupIDMsg::ConvertNoRef(msg); + if (setNetGroupID) + { + SetNetGroupConstant(setNetGroupID->fId); + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + +#ifdef USE_SYNCHED_VALUES +plSynchedValueBase* plSynchedObject::GetSynchedValue(int i) const +{ + if (i>2; + hsAssert(hsABS(addrOff) < (UInt32)(1<<(sizeof(AddrOffsetType)<<3)), "address offset overflow"); + IAppendSynchedValueAddrOffset((AddrOffsetType)addrOff); + Int32 idx = fNumSynchedValues-1; + hsAssert(idx<256, "index too big"); + return (UInt8)idx; +} + +hsBool plSynchedObject::RemoveSynchedValue(plSynchedValueBase* v) +{ + int i; + for(i=0;iSetFlags(synchFlags); + hsAssert(GetKey(), "nil key on synchedObject?"); + sdlMsg->Send(GetKey()); +} + +// +// Tell an object to send an sdl state update. +// The request will get queued (returns true) +// +hsBool plSynchedObject::DirtySynchState(const char* SDLStateName, UInt32 synchFlags /*SendSDLStateFlags*/) +{ + if (!IOKToDirty(SDLStateName)) + { +#if 0 + if (plNetClientApp::GetInstance()) + plNetClientApp::GetInstance()->DebugMsg("NotOKToDirty - Not queueing SDL state, obj %s, sdl %s", + GetKeyName(), SDLStateName); +#endif + return false; + } + + if (!IOKToNetwork(SDLStateName, &synchFlags)) + { +#if 0 + if (plNetClientApp::GetInstance()) + plNetClientApp::GetInstance()->DebugMsg("LocalOnly Object - Not queueing SDL msg, obj %s, sdl %s", + GetKeyName(), SDLStateName); +#endif + return false; + } + + if (!(synchFlags & kSkipLocalOwnershipCheck)) + { + int localOwned=IsLocallyOwned(); + if (localOwned==plSynchedObject::kNo) + return false; + if (localOwned==plSynchedObject::kYes) + synchFlags |= kSkipLocalOwnershipCheck; // don't have to check again + else + { + if (plNetClientApp::GetInstance()) + plNetClientApp::GetInstance()->DebugMsg("Queueing SDL state with 'maybe' ownership, obj %s, sdl %s", + GetKeyName(), SDLStateName); + } + } + + if (synchFlags & kSendImmediately) + { + SendSDLStateMsg(SDLStateName, synchFlags); + } + else + { + IAddDirtyState(GetKey(), SDLStateName, synchFlags); + } + return true; +} + +// +// STATIC +// add state defn if not already there. +// if there adjust flags if necessary +// +void plSynchedObject::IAddDirtyState(plKey objKey, const char* sdlName, UInt32 sendFlags) +{ + bool found=false; + std::vector::iterator it=fDirtyStates.begin(); + for( ; it != fDirtyStates.end(); it++) + { + if ((*it).fObjKey==objKey && !stricmp((*it).fSDLName.c_str(), sdlName)) + { + if (sendFlags & kForceFullSend) + (*it).fSendFlags |= kForceFullSend; + if (sendFlags & kBCastToClients) + (*it).fSendFlags |= kBCastToClients; + found=true; + break; + } + } + + if (!found) + { + StateDefn state(objKey, sendFlags, sdlName); + fDirtyStates.push_back(state); + } + else + { +#if 0 + plNetClientApp::GetInstance()->DebugMsg("Not queueing diplicate request for SDL state, obj %s, sdl %s", + objKey->GetName(), sdlName); +#endif + } +} + +// +// STATIC +// +void plSynchedObject::IRemoveDirtyState(plKey objKey, const char* sdlName) +{ + std::vector::iterator it=fDirtyStates.begin(); + for( ; it != fDirtyStates.end(); it++) + { + if ((*it).fObjKey==objKey && !stricmp((*it).fSDLName.c_str(), sdlName)) + { + fDirtyStates.erase(it); + break; + } + } +} + +void plSynchedObject::SetNetGroupConstant(plNetGroupId netGroup) +{ + ClearSynchFlagsBit(kHasConstantNetGroup); + SetNetGroup(netGroup); // may recurse + SetSynchFlagsBit(kHasConstantNetGroup); +} + +plNetGroupId plSynchedObject::SelectNetGroup(plKey rmKey) +{ + return plNetClientApp::GetInstance() ? + plNetClientApp::GetInstance()->SelectNetGroup(this, rmKey) : plNetGroup::kNetGroupUnknown; +} + +plNetGroupId plSynchedObject::GetEffectiveNetGroup() const +{ + return plNetClientApp::GetInstance() ? + plNetClientApp::GetInstance()->GetEffectiveNetGroup(this) : plNetGroup::kNetGroupLocalPlayer; +} + +int plSynchedObject::IsLocallyOwned() const +{ + return plNetClientApp::GetInstance() ? + plNetClientApp::GetInstance()->IsLocallyOwned(this) : kYes; +} + +void plSynchedObject::Read(hsStream* stream, hsResMgr* mgr) +{ + hsKeyedObject::Read(stream, mgr); + fNetGroup = GetKey()->GetUoid().GetLocation(); + + stream->ReadSwap(&fSynchFlags); + if (fSynchFlags & kExcludePersistentState) + { + Int16 num; + stream->ReadSwap(&num); + fSDLExcludeList.clear(); + int i; + for(i=0;iReadSwap(&num); + fSDLVolatileList.clear(); + int i; + for(i=0;iWriteSwap(fSynchFlags); + + if (fSynchFlags & kExcludePersistentState) + { + Int16 num=fSDLExcludeList.size(); + stream->WriteSwap(num); + + SDLStateList::iterator it=fSDLExcludeList.begin(); + for(; it != fSDLExcludeList.end(); it++) + { + plMsgStdStringHelper::Poke(*it, stream); + } + } + + if (fSynchFlags & kHasVolatileState) + { + Int16 num=fSDLVolatileList.size(); + stream->WriteSwap(num); + + SDLStateList::iterator it=fSDLVolatileList.begin(); + for(; it != fSDLVolatileList.end(); it++) + { + plMsgStdStringHelper::Poke(*it, stream); + } + } +} + + +// +// static +// +hsBool plSynchedObject::PopSynchDisabled() +{ + if (fSynchStateStack.size()) + { + hsBool ret=fSynchStateStack.back(); + fSynchStateStack.pop_back(); + return ret; + } + else + { + hsAssert(false, "invalid stack size?"); + } + return true; // disabled +} + +#ifdef USE_DIRTY_NOTIFIERS +void plSynchedObject::AddDirtyNotifier(plDirtyNotifier* dn) +{ + if (dn) + { + std::vector::iterator it=std::find(fDirtyNotifiers.begin(), fDirtyNotifiers.end(), dn); + if (it == fDirtyNotifiers.end()) // not there + { + dn->SetSynchedObjKey(GetKey()); + fDirtyNotifiers.push_back(dn); + } + } +} + +void plSynchedObject::RemoveDirtyNotifier(plDirtyNotifier* dn) +{ + if (dn) + { + std::vector::iterator it=std::find(fDirtyNotifiers.begin(), fDirtyNotifiers.end(), dn); + if (it != fDirtyNotifiers.end()) // its there + fDirtyNotifiers.erase(it); + } +} + +void plSynchedObject::CallDirtyNotifiers() +{ + int i; + for(i=0;iCallback(); +} +#else +void plSynchedObject::CallDirtyNotifiers() {} +#endif + +// +// return true if it's ok to dirty this object +// +bool plSynchedObject::IOKToDirty(const char* SDLStateName) const +{ + // is synching disabled? + bool synchDisabled = (GetSynchDisabled()!=0); + if (synchDisabled) + return false; + + // is object dirtyAble? + bool dontDirty = (fSynchFlags & kDontDirty) != 0; + if (dontDirty) + return false; + + // is object in a LocalOnly age? + if (plNetClientApp::GetConstInstance() && plNetClientApp::GetConstInstance()->ObjectInLocalAge(this)) + return false; + + return true; // OK to dirty +} + +// +// return true if this object should send his SDL msg (for persistence or synch) over the net +// +bool plSynchedObject::IOKToNetwork(const char* sdlName, UInt32* synchFlags) const +{ + // determine destination + bool dstServerOnly=false, dstClientsOnly=false, dstClientsAndServer=false; + + if ((*synchFlags) & kBCastToClients) + { // bcasting to clients and server + if ((*synchFlags) & kDontPersistOnServer) + dstClientsOnly=true; + else + dstClientsAndServer=true; + } + else + { // not bcasting, must be sending to server only + hsAssert( ((*synchFlags) & kDontPersistOnServer)==0, "invalid synchedObject msg flag"); + dstServerOnly=true; + } + + bool netSynched = IsNetSynched(); + bool inExcludeList = IsInSDLExcludeList(sdlName); + + // + // check if ok to network based on destination + // + if (dstClientsOnly) + { + return netSynched; + } + + if (dstClientsAndServer) + { + if ( !netSynched ) + { + *synchFlags &= ~kBCastToClients; // don't send to clients + } + if ( inExcludeList ) + { + *synchFlags |= kDontPersistOnServer; // don't store on server + } + + return !inExcludeList || netSynched; + } + + if (dstServerOnly) + { + return !inExcludeList; + } + + hsAssert(false, "how did I get here"); + return false; +} + +plSynchedObject::SDLStateList::const_iterator plSynchedObject::IFindInSDLStateList(const SDLStateList& list, const char* sdlName) const +{ + if (!sdlName) + return list.end(); // false + + SDLStateList::const_iterator it = list.begin(); + for(; it != list.end(); it++) + if (!_stricmp((*it).c_str(), sdlName)) + return it; + + return it; // .end(), false +} + +/////////////////////////// +// EXCLUDE LIST +/////////////////////////// + +void plSynchedObject::AddToSDLExcludeList(const char* sdlName) +{ + if (sdlName) + { + if (IFindInSDLStateList(fSDLExcludeList, sdlName)==fSDLExcludeList.end()) + { + fSDLExcludeList.push_back(sdlName); // Don't dupe sdlName, std::string will copy + fSynchFlags |= kExcludePersistentState; + } + } +} + +void plSynchedObject::RemoveFromSDLExcludeList(const char* sdlName) +{ + SDLStateList::const_iterator it=IFindInSDLStateList(fSDLExcludeList, sdlName); + if (it != fSDLExcludeList.end()) + { + fSDLExcludeList.erase(fSDLExcludeList.begin()+(it-fSDLExcludeList.begin())); + if (fSDLExcludeList.size()==0) + fSynchFlags &= ~kExcludePersistentState; + } +} + +bool plSynchedObject::IsInSDLExcludeList(const char* sdlName) const +{ + if ((fSynchFlags & kExcludeAllPersistentState) != 0) + return true; + + if ((fSynchFlags & kExcludePersistentState) == 0) + return false; + + SDLStateList::const_iterator it=IFindInSDLStateList(fSDLExcludeList, sdlName); + return (it != fSDLExcludeList.end()); +} + +/////////////////////////// +// VOLATILE LIST +/////////////////////////// + +void plSynchedObject::AddToSDLVolatileList(const char* sdlName) +{ + if (sdlName) + { + if (IFindInSDLStateList(fSDLVolatileList,sdlName)==fSDLVolatileList.end()) + { + fSDLVolatileList.push_back(sdlName); // Don't dupe sdlName, std::string will copy + fSynchFlags |= kHasVolatileState; + } + } +} + +void plSynchedObject::RemoveFromSDLVolatileList(const char* sdlName) +{ + SDLStateList::const_iterator it=IFindInSDLStateList(fSDLVolatileList,sdlName); + if (it != fSDLVolatileList.end()) + { + fSDLVolatileList.erase(fSDLVolatileList.begin()+(it-fSDLVolatileList.begin())); + if (fSDLVolatileList.size()==0) + fSynchFlags &= ~kHasVolatileState; + } +} + +bool plSynchedObject::IsInSDLVolatileList(const char* sdlName) const +{ + if ((fSynchFlags & kAllStateIsVolatile) != 0) + return true; + + if ((fSynchFlags & kHasVolatileState) == 0) + return false; + + SDLStateList::const_iterator it=IFindInSDLStateList(fSDLVolatileList,sdlName); + return (it != fSDLVolatileList.end()); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedObject.h new file mode 100644 index 00000000..87561d1d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedObject.h @@ -0,0 +1,270 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLSYNCHOBJ_inc +#define PLSYNCHOBJ_inc + +#include "hsTypes.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plKey.h" +#include "hsStlUtils.h" +#include "plNetGroup.h" + +///////////////////////////////////// +// plSynchedObject +// one per savable object +///////////////////////////////////// +class hsStream; +class plDirtyNotifier; +class plSynchedObject : public hsKeyedObject +{ +public: + enum LocallyOwnedAnswer + { + kNo=false, + kYes=true + }; + + enum Flags + { + kDontDirty = 0x1, + kSendReliably = 0x2, // object wants reliable send + kHasConstantNetGroup = 0x4, // has a constant net group. + kDontSynchGameMessages = 0x8, // don't send/recv game actions + kExcludePersistentState = 0x10, // don't send SDL state msgs to server, check exclude list + kExcludeAllPersistentState=0x20, // don't send ANY type of SDL state + kLocalOnly = (kExcludeAllPersistentState | kDontSynchGameMessages), // localOnly in all respects + kHasVolatileState = 0x40, // server won't save this state on shutdown + kAllStateIsVolatile = 0x80 + }; + + enum SDLSendFlags + { + kBCastToClients = 0x1, + kForceFullSend = 0x2, + kSkipLocalOwnershipCheck= 0x4, + kSendImmediately = 0x8, + kDontPersistOnServer = 0x10, // for an SDL bcast msg which is used for synching only, not persisting + kUseRelevanceRegions = 0x20, + kNewState = 0x40, + kIsAvatarState = 0x80, + }; + + struct StateDefn + { + plKey fObjKey; + UInt32 fSendFlags; + std::string fSDLName; + + plSynchedObject* GetObject() const { return plSynchedObject::ConvertNoRef(fObjKey->ObjectIsLoaded()); } + StateDefn() : fObjKey(nil),fSendFlags(0) {} + StateDefn(plKey k, UInt32 f, const char* sdlName) : fObjKey(k),fSendFlags(f) { fSDLName=sdlName; } + }; + +private: + typedef std::vector SDLStateList; + SDLStateList fSDLExcludeList; + SDLStateList fSDLVolatileList; + UInt32 fSynchFlags; + + plNetGroupId fNetGroup; + + static std::vector fSynchStateStack; + static plSynchedObject* fStaticSynchedObj; // static which temporarily holds address of each object's synchMgr + static std::vector fDirtyStates; + + static void IRemoveDirtyState(plKey o, const char* sdlName); + static void IAddDirtyState(plKey o, const char* sdlName, UInt32 sendFlags); + bool IOKToDirty(const char* SDLStateName) const; + SDLStateList::const_iterator IFindInSDLStateList(const SDLStateList& list, const char* sdlName) const; +protected: + bool IOKToNetwork(const char* sdlName, UInt32* synchFlags) const; +public: + plSynchedObject(); + virtual ~plSynchedObject(); + + CLASSNAME_REGISTER( plSynchedObject ); + GETINTERFACE_ANY( plSynchedObject, hsKeyedObject); + + virtual hsBool MsgReceive(plMessage* msg); + + // getters + int GetSynchFlags() const { return fSynchFlags; } + plNetGroupId GetNetGroup() const { return fNetGroup; }; + plNetGroupId GetEffectiveNetGroup() const; + + // setters + void SetSynchFlagsBit(UInt32 f) { fSynchFlags |= f; } + virtual void SetNetGroupConstant(plNetGroupId netGroup); + virtual void SetNetGroup(plNetGroupId netGroup) { fNetGroup = netGroup; } + plNetGroupId SelectNetGroup(plKey groupKey); + + virtual hsBool DirtySynchState(const char* sdlName, UInt32 sendFlags); + void SendSDLStateMsg(const char* SDLStateName, UInt32 synchFlags); // don't use, only for net code + + void ClearSynchFlagsBit(UInt32 f) { fSynchFlags &= ~f; } + + // static + static hsBool GetSynchDisabled() { return fSynchStateStack.size() ? fSynchStateStack.back() : true; } + static void PushSynchDisabled(hsBool b) { fSynchStateStack.push_back(b); } + static hsBool PopSynchDisabled(); + static plSynchedObject* GetStaticSynchedObject() { return fStaticSynchedObj; } + static Int32 GetNumDirtyStates() { return fDirtyStates.size(); } + static plSynchedObject::StateDefn* GetDirtyState(Int32 i) { return &fDirtyStates[i]; } + static void ClearDirtyState(std::vector& carryOver) { fDirtyStates=carryOver; } + + // IO +// void SendCreationMsg(double secs); +// void SendDestructionMsg(double secs) ; + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + int IsLocallyOwned() const; // returns yes/no/maybe + + // disable net synching only + bool IsNetSynched() const { return (fSynchFlags & kDontSynchGameMessages)==0; } + void SetNetSynched(bool b) { if (!b) fSynchFlags |= kDontSynchGameMessages; else fSynchFlags &= ~kDontSynchGameMessages; } + + // disable net synching AND persisting + bool IsLocalOnly() const { return (fSynchFlags & kLocalOnly)==0; } + void SetLocalOnly(bool b) { if (b) fSynchFlags |= kLocalOnly; else fSynchFlags &= ~kLocalOnly; } + + // disable particular types of persistence + void AddToSDLExcludeList(const char*); + void RemoveFromSDLExcludeList(const char*); + bool IsInSDLExcludeList(const char*) const; + + // make volatile particular types of state + void AddToSDLVolatileList(const char*); + void RemoveFromSDLVolatileList(const char*); + bool IsInSDLVolatileList(const char*) const; + + // + // synched value stuff, currently unused + // current size is 16 + numValue bytes*2 + numFriends*4 bytes + // +#ifdef USE_SYNCHED_VALUES +public: + typedef UInt16 AddrOffsetType; + typedef UInt8 NumSynchedValuesType; + typedef UInt16 FlagsType; + friend class plSynchedValueBase; + +private: + AddrOffsetType* fSynchedValueAddrOffsets; // represent dwords offsets + NumSynchedValuesType fNumSynchedValues; + + // array of friends + plSynchedValueBase** fSynchedValueFriends; + NumSynchedValuesType fNumSynchedValueFriends; + + // dirty callback notifiers + std::vector fDirtyNotifiers; + + void IAppendSynchedValueAddrOffset(AddrOffsetType synchedValueAddrOffset); + void IAppendSynchedValueFriend(plSynchedValueBase* v); + plSynchedValueBase* IGetSynchedValue(NumSynchedValuesType i) const + { return (plSynchedValueBase*)((Int32)this + (fSynchedValueAddrOffsets[i]<<2)); } + plSynchedValueBase* IGetSynchedValueFriend(NumSynchedValuesType i) const + { return fSynchedValueFriends[i]; } + +public: + Int32 GetNumSynchedValues() const { return fNumSynchedValues+fNumSynchedValueFriends; } + plSynchedValueBase* GetSynchedValue(int i) const; + + UInt8 RegisterSynchedValue(plSynchedValueBase* v); + hsBool RemoveSynchedValue(plSynchedValueBase* v); // handles SVFriends too + void RegisterSynchedValueFriend(plSynchedValueBase* v); +#endif + +#ifdef USE_DIRTY_NOTIFIERS + // dirty CB notifiers + void AddDirtyNotifier(plDirtyNotifier* dn); + void RemoveDirtyNotifier(plDirtyNotifier* dn); +#endif + void CallDirtyNotifiers(); +}; + +// +// helper class to set dirty tracking on/off within scope +// +class plSynchEnabler +{ +public: + plSynchEnabler(hsBool enable) { plSynchedObject::PushSynchDisabled(!enable); } + ~plSynchEnabler() { plSynchedObject::PopSynchDisabled(); } +}; + +#ifdef USE_DIRTY_NOTIFIERS +/////////////////////////////////// +// plDirtyNotifier - When a synchedObj +// gets dirty, this callback will be called. +/////////////////////////////////// +class plDirtyNotifier +{ +protected: + plKey fSynchedObjKey; + void* fUserData; +public: + plDirtyNotifier() : fSynchedObjKey(nil),fUserData(nil) {} + virtual ~plDirtyNotifier() + { + if (fSynchedObjKey) + { + plSynchedObject* so = plSynchedObject::ConvertNoRef(fSynchedObjKey->ObjectIsLoaded()); + if (so) + so->RemoveDirtyNotifier(this); + } + } + + void SetSynchedObjKey(plKey k) { fSynchedObjKey=k; } // should be set + void SetUserData(void* v) { fUserData=v;} // optional + + plKey GetSynchedObjKey() { return fSynchedObjKey; } + void* GetUserData() { return fUserData;} + + // override + virtual void Callback() = 0; +}; +#endif + +// +// MACROS +// + +#ifdef USE_SYNCHED_VALUES +#define SYNCHED_VALUE(type) plSynchedValue +#define SYNCHED_TARRAY(type) plSynchedTArray +#define SYNCHED_VALUE_FRIEND(type) plSynchedValueFriend +#define SYNCHED_TARRAY_FRIEND(type) plSynchedTArrayFriend +#else +#define SYNCHED_VALUE(type) type +#define SYNCHED_TARRAY(type) hsTArray +#define SYNCHED_VALUE_FRIEND(type) type +#define SYNCHED_TARRAY_FRIEND(type) hsTArray +#endif + +#endif // PLSYNCHOBJ_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedValue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedValue.cpp new file mode 100644 index 00000000..06781a45 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedValue.cpp @@ -0,0 +1,153 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSynchedValue.h" + +#ifdef USE_SYNCHED_VALUES +#include "../pnKeyedObject/plKey.h" +#include "hsBitVector.h" +#include "../pnSceneObject/plSceneObject.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +#define ISaveOrLoadSimpleType() \ +{ \ + if (save) \ + stream->WriteSwap(v); \ + else \ + stream->ReadSwap(&v); \ + return v; \ +} + +// +// baseclass save/load methods for various types +// +hsBitVector plSynchedValueBase::ISaveOrLoad(hsBitVector& v, hsBool32 save, hsStream* stream, hsResMgr* mgr) +{ + if (save) + v.Write(stream); + else + v.Read(stream); + return v; +} + +hsScalar plSynchedValueBase::ISaveOrLoad(hsScalar v, hsBool32 save, hsStream* stream, hsResMgr* mgr) + ISaveOrLoadSimpleType(); + +double plSynchedValueBase::ISaveOrLoad(double v, hsBool32 save, hsStream* stream, hsResMgr* mgr) + ISaveOrLoadSimpleType(); + +Int32 plSynchedValueBase::ISaveOrLoad(Int32 v, hsBool32 save, hsStream* stream, hsResMgr* mgr) + ISaveOrLoadSimpleType(); + +UInt32 plSynchedValueBase::ISaveOrLoad(UInt32 v, hsBool32 save, hsStream* stream, hsResMgr* mgr) + ISaveOrLoadSimpleType(); + +int plSynchedValueBase::ISaveOrLoad(int v, hsBool32 save, hsStream* stream, hsResMgr* mgr) + ISaveOrLoadSimpleType(); + +bool plSynchedValueBase::ISaveOrLoad(bool v, hsBool32 save, hsStream* stream, hsResMgr* mgr) + ISaveOrLoadSimpleType(); + +// +// save or load a key. return the key. +// +const plKey plSynchedValueBase::ISaveOrLoad(const plKey key, hsBool32 save, hsStream* stream, hsResMgr* mgr) +{ + if (save) + { + if (key) + { + stream->WriteByte(1); + // I need to write a key to MY stream... +#if 0 // DEBUG + Int32 len = hsStrlen(key->GetName()); + stream->WriteSwap32(len); + stream->Write(len, key->GetName()); +#endif + key->GetUoid().Write(stream); + } + else + { + stream->WriteByte(0); + } + return key; + } + else + { + Int32 has=stream->ReadByte(); + if (has) + { + // read a key from MY stream +#if 0 // DEBUG + Int32 len = stream->ReadSwap32(); + char tmp[256]; + hsAssert(len<256, "key name overflow"); + stream->Read(len, tmp); +#endif + plUoid uoid; + uoid.Read(stream); + return mgr->FindKey(uoid); + } + else + return (nil); + } + return nil; +} + +hsKeyedObject* plSynchedValueBase::ISaveOrLoad(hsKeyedObject* obj, hsBool32 save, hsStream* stream, hsResMgr* mgr) +{ + plKey key = obj ? obj->GetKey() : nil; + key = ISaveOrLoad(key, save, stream, mgr); + return key ? key->ObjectIsLoaded() : nil; +} + +plSceneNode* plSynchedValueBase::ISaveOrLoad(plSceneNode* obj, hsBool32 save, hsStream* stream, hsResMgr* mgr) +{ + // return plSceneNode::ConvertNoRef(ISaveOrLoad(hsKeyedObject::ConvertNoRef(obj), save, stream, mgr)); + hsAssert(false, "SceneNode synchedValues currently not implemented"); + return nil; +} + +plSceneObject* plSynchedValueBase::ISaveOrLoad(plSceneObject* obj, hsBool32 save, hsStream* stream, hsResMgr* mgr) +{ return plSceneObject::ConvertNoRef(ISaveOrLoad(hsKeyedObject::ConvertNoRef(obj), save, stream, mgr)); } + +plCoordinateInterface* plSynchedValueBase::ISaveOrLoad(const plCoordinateInterface* cInt, hsBool32 save, hsStream* stream, hsResMgr* mgr) +{ + plSceneObject* obj = ISaveOrLoad(cInt ? cInt->fOwner : nil, save, stream, mgr); + return obj ? obj->fCoordinateInterface : nil; +} + +#else + +// dummy function to prevent a linker warning complaining about no public symbols if the +// contents of the file get compiled out via pre-processor +void _preventLNK4221WarningStub() +{ +} + +#endif // USE_SYNCHED_VALUES + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedValue.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedValue.h new file mode 100644 index 00000000..5a371604 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/plSynchedValue.h @@ -0,0 +1,396 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_SYNCHEDVALUE_inc +#define PL_SYNCHEDVALUE_inc + +#include "plSynchedObject.h" + +#ifdef USE_SYNCHED_VALUES +#include "hsTypes.h" +#include "hsStream.h" + +#include "hsBitVector.h" +#include "hsMemory.h" +// +// Defines a class for variable types which need to automatically be synchronized +// (replicated) over the network. +// current size is 8 bytes +// + +// +// ----------------------------------------------------- +// SYNCHED VALUE BASE CLASS +// ----------------------------------------------------- +// +class hsResMgr; +class plSceneNode; +class plSceneObject; +class plCoordinateInterface; +class plSynchedValueBase +{ +public: + enum Flags // 16 bits + { + kValueIsDirty = 0x1, + kValueSendOnlyOnce = 0x2, // perm flag + kValueHasBeenSent = 0x4, // perm flag + kRegistered = 0x8, // perm flag + kDontDirty = 0x10 // perm flag + }; + +protected: + Int16 fSynchedObjectAddrOffset; // this could represent dwords instead of byte offsets + UInt16 fFlags; + + void IConstruct() // too bad this can't be virtual (because it's called from ctor) + { + // The synchMgr for the class that owns us is constructed first and the staticMgr + // is set to his address so we can automatically get at it during construction. + fFlags=0; + Int32 off = (Int32)plSynchedObject::GetStaticSynchedObject() - (Int32)this; + if ( hsABS(off) < (1<<(sizeof(fSynchedObjectAddrOffset)<<3)) ) + fSynchedObjectAddrOffset = (Int16)off; + else + fSynchedObjectAddrOffset=-1; + } + + hsBool32 IOKToDirty() + { + if (fFlags & (kDontDirty | kValueIsDirty)) + return false; + return GetSynchedObject() ? GetSynchedObject()->IOKToDirty() : false; + } + virtual void ISaveOrLoad(hsBool32 save, hsStream* stream, hsResMgr* mgr) = 0; + + // save/load methods for different types + const plKey ISaveOrLoad(const plKey key, hsBool32 save, hsStream* stream, hsResMgr* mgr); + hsKeyedObject* ISaveOrLoad(hsKeyedObject* obj, hsBool32 save, hsStream* stream, hsResMgr* mgr); + plSceneNode* ISaveOrLoad(plSceneNode* obj, hsBool32 save, hsStream* stream, hsResMgr* mgr); + plSceneObject* ISaveOrLoad(plSceneObject* obj, hsBool32 save, hsStream* stream, hsResMgr* mgr); + Int32 ISaveOrLoad(Int32 v, hsBool32 save, hsStream* stream, hsResMgr* mgr); + UInt32 ISaveOrLoad(UInt32 v, hsBool32 save, hsStream* stream, hsResMgr* mgr); + bool ISaveOrLoad(bool v, hsBool32 save, hsStream* stream, hsResMgr* mgr); + int ISaveOrLoad(int v, hsBool32 save, hsStream* stream, hsResMgr* mgr); // or hsBool32 + hsScalar ISaveOrLoad(hsScalar v, hsBool32 save, hsStream* stream, hsResMgr* mgr); + double ISaveOrLoad(double v, hsBool32 save, hsStream* stream, hsResMgr* mgr); + hsBitVector ISaveOrLoad(hsBitVector& v, hsBool32 save, hsStream* stream, hsResMgr* mgr); + plCoordinateInterface* ISaveOrLoad(const plCoordinateInterface* cInt, hsBool32 save, hsStream* stream, hsResMgr* mgr); +public: + plSynchedValueBase() { IConstruct(); } + virtual ~plSynchedValueBase() {} + + // getters + virtual plSynchedObject* GetSynchedObject() + { + hsAssert(fSynchedObjectAddrOffset!=-1, "invalid synchedObject address offset"); + plSynchedObject* so = fSynchedObjectAddrOffset == -1 ? nil : (plSynchedObject*)((Int32)this+fSynchedObjectAddrOffset); + if (!(fFlags & kRegistered) && so) + { + so->RegisterSynchedValue(this); + fFlags |= kRegistered; + } + return so; + } + UInt16 GetFlags() { return fFlags; } + + // setters + void SetFlags(UInt16 f) { fFlags=f; } + + void MakeDirty() { SetFlags(GetFlags() | kValueIsDirty); } + void MakeClean() { SetFlags(GetFlags() & ~kValueIsDirty); } + + void DirtyIfNecessary() + { + if (IOKToDirty()) + { + MakeDirty(); // dirty value + if (GetSynchedObject()) + GetSynchedObject()->DirtySynchState(nil, 0); // dirty owner + } + } + + // save/load + static void Save(plSynchedValueBase& obj, hsStream* stream, hsResMgr* mgr) { obj.ISaveOrLoad(true, stream, mgr); } + static void Load(plSynchedValueBase& obj, hsStream* stream, hsResMgr* mgr) { obj.ISaveOrLoad(false, stream, mgr); } +}; + +// +// ----------------------------------- +// SYNCHED VALUE TEMPLATE +// ----------------------------------- +// +template +class plSynchedValue : public plSynchedValueBase +{ +protected: + T fValue; + + void ISaveOrLoad(hsBool32 save, hsStream* stream, hsResMgr* mgr) + { fValue=(T)plSynchedValueBase::ISaveOrLoad(fValue, save, stream, mgr); } // default method + +public: + + plSynchedValue() {} + plSynchedValue(const T& v) : plSynchedValueBase() { fValue=v; } + plSynchedValue(const plSynchedValue& pRHS) : plSynchedValueBase() { fValue=pRHS.GetValue(); } // copy ctor + + // conversion operators + operator T() const { return fValue; } + T* operator &() const { return &fValue; } + T* operator &() { return &fValue; } + + // equality + hsBool32 operator==(const T& other) const { return fValue==(T)other; } + hsBool32 operator!=(const T& other) const { return !(*this == other); } + + // other operators + T operator++() { DirtyIfNecessary(); return ++fValue; } + T operator++(int) { DirtyIfNecessary(); return fValue++; } // postfix + T operator--() { DirtyIfNecessary(); return --fValue; } + T operator--(int) { DirtyIfNecessary(); return fValue--; } // postfix + T operator+=(const T& other) { return SetValue(fValue+other); } + T operator*=(const T& other) { return SetValue(fValue*other); } + T operator/=(const T& other) { return SetValue(fValue/other); } + + // these return reference in the event they are bitvector types + T& operator&=(const T& other) { return SetValue(fValue&other); } + T& operator|=(const T& other) { return SetValue(fValue|other); } + T& operator^=(const T& other) { return SetValue(fValue^other); } + T& operator-=(const T& other) { return SetValue(fValue-other); } + + const T& operator=(const T& v){ return SetValue(v); } + +#if HS_BUILD_FOR_WIN32 +#pragma warning( push ) +#pragma warning( disable : 4284 ) // disable annoying warnings in release build for non pointer types +#endif + // for pointer types, which are allowed to change the object pointed to + T operator->(void) { return fValue; } +#if HS_BUILD_FOR_WIN32 +#pragma warning( pop ) +#endif + // asignment, can use instead of setvalue + const T& operator=(const plSynchedValue& pRHS ) { return SetValue(pRHS.GetValue()); } + + // setters + T& SetValue(const T& v) // return true if changed value + { + if (v != fValue) // dont dirty unless value changes + { + fValue=v; + DirtyIfNecessary(); + } + return fValue; + } + + // getters + const T& GetValue() const { return fValue; } + + // for hsBitVector + hsBool32 IsBitSet(UInt32 which) const { return fValue.IsBitSet(which); } + hsBool32 SetBit(UInt32 which, hsBool32 on = true) + { hsBool32 bitSet = IsBitSet(which); + if ( (on && !bitSet) || (!on && bitSet) ) + DirtyIfNecessary(); + return fValue.SetBit(which, on); + } + void Read(hsStream* s) { fValue.Read(s); } + void Write(hsStream* s) const { fValue.Write(s); } + void Clear() { DirtyIfNecessary(); fValue.Clear(); } + hsBool32 ClearBit(UInt32 which) { if (fValue.IsBitSet(which)) DirtyIfNecessary(); return fValue.ClearBit(which); } + void Reset() { if (fValue.GetSize()!=0) DirtyIfNecessary(); fValue.Reset(); } + hsBool32 ToggleBit(UInt32 which) { DirtyIfNecessary(); return fValue.ToggleBit(which); } + UInt32 GetSize() { return fValue.GetSize(); } +}; + +////////////////////////////////////// +// Synched Value Friend - allows a synched value to be contained +// in an object which is not a synchedObject. Uses a pointer instead +// of an computer the addr offset of it's associated synchedObject. +// This one is 4 bytes bigger than regular synched values. +////////////////////////////////////// +template +class plSynchedValueFriend : public plSynchedValue +{ +protected: + plSynchedObject** fSynchedObject; +public: + plSynchedValueFriend() : fSynchedObject(nil) { } + // this is explicit so it won't be invoked instead of operator()= + explicit plSynchedValueFriend(const T& v) : plSynchedValue(v),fSynchedObject(nil) { } + plSynchedValueFriend(const plSynchedValueFriend& pRHS) : plSynchedValue(pRHS) + { fSynchedObject = pRHS.fSynchedObject; } + ~plSynchedValueFriend() + { + if (GetSynchedObject()) + GetSynchedObject()->RemoveSynchedValue(this); + } + + // this isn't inherited for some reason + const T& operator=(const T& v) { return SetValue(v); } + + plSynchedObject* GetSynchedObject() + { + hsAssert(fSynchedObject, "nil synched object, need to SetSynchedObjectPtrAddr?"); + + if (*fSynchedObject && !(fFlags & kRegistered)) + { + (*fSynchedObject)->RegisterSynchedValueFriend(this); + fFlags |= kRegistered; + } + return *fSynchedObject; + } + + void SetSynchedObjectPtrAddr(plSynchedObject** so) + { + hsAssert(!(fFlags & kRegistered), "SynchedValueFriend already registered?"); + fSynchedObject=so; + } +}; + +///////////////////////////////////// +// Synched TArray Template +///////////////////////////////////// +#include "hsTemplates.h" +template +class plSynchedTArray : public plSynchedValueBase +{ +private: + hsTArray fValueList; + + void ISaveOrLoad(hsBool32 save, hsStream* stream, hsResMgr* mgr); +public: + enum { kMissingIndex = hsTArray::kMissingIndex }; + plSynchedTArray() {} + ~plSynchedTArray() {} + + // conversion operators + operator T() const { return fValueList; } + + // common methods + const T& operator[](int i) const { return Get(i); } + + const T& Get(int i) const { return fValueList.Get(i); } + void Set(int i, const T& item) { if (fValueList[i] != item) DirtyIfNecessary(); fValueList[i]=item; } + void Append(const T& item) { fValueList.Append(item); DirtyIfNecessary(); } + T* Insert(int index) { fValueList.Insert(index); DirtyIfNecessary(); } + void Remove(int index) { fValueList.Remove(index); DirtyIfNecessary(); } + int Count() const { return fValueList.Count(); } + int GetCount() const { return Count(); } + void Reset() { if (fValueList.GetCount() != 0) DirtyIfNecessary(); fValueList.Reset(); } + void SetCountAndZero(int count) { if (count || GetCount()) DirtyIfNecessary(); fValueList.SetCountAndZero(count); } + void SetCount(int count) { if (count || GetCount()) DirtyIfNecessary(); fValueList.SetCount(count); } + void ExpandAndZero(int count) { if (count || GetCount()) DirtyIfNecessary(); fValueList.ExpandAndZero(count); } + int Find(const T& item) const { return fValueList.Find(item); } + T* Push() { DirtyIfNecessary(); return fValueList.Push(); } + void Push(const T& item) { DirtyIfNecessary(); return fValueList.Push(item); } + T Pop() { DirtyIfNecessary(); return fValueList.Pop(); } + const T& Peek() const { return fValue.Peek(); } + T* DetachArray() { DirtyIfNecessary(); return fValueList.DetachArray(); } + T* AcquireArray() { DirtyIfNecessary(); return fValueList.AcquireArray(); } +}; + +// +// inlines +// +template inline +void plSynchedTArray::ISaveOrLoad(hsBool32 save, hsStream* stream, hsResMgr* mgr) +{ + if (save) + { + // write out size of array + Int32 i, num = fValueList.GetCount(); + stream->WriteSwap(num); + for(i=0;iReadSwap(&num); + + for(i=0;i +class plSynchedTArrayFriend : public plSynchedTArray +{ +protected: + plSynchedObject** fSynchedObject; +public: + plSynchedTArrayFriend() : fSynchedObject(nil) { } + + plSynchedObject* GetSynchedObject() + { + hsAssert(fSynchedObject, "nil synched object, need to SetSynchedObjectPtrAddr?"); + + if (*fSynchedObject && !(fFlags & kRegistered)) + { + (*fSynchedObject)->RegisterSynchedValueFriend(this); + fFlags |= kRegistered; + } + return *fSynchedObject; + } + + void SetSynchedObjectPtrAddr(plSynchedObject** so) + { + hsAssert(!(fFlags & kRegistered), "SynchedValueTArrayFriend already registered?"); + fSynchedObject=so; + } + + +#if 0 + // + // redefine operators since they are not inherited + // + + // conversion operators + operator T() const { return fValueList; } + + // common methods + const T& operator[](int i) const { return Get(i); } +#endif +}; +#endif // USE_SYNCHED_VALUES + +#endif // PL_SYNCHEDVALUE_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommon.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommon.cpp new file mode 100644 index 00000000..4958c273 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommon.cpp @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pnNetCommon.h" +#include "../pnAddrInfo/pnAddrInfo.h" +#ifdef HS_BUILD_FOR_WIN32 +# include "hsWindows.h" +#elif HS_BUILD_FOR_UNIX +# include +# include +# include +# include +# include // for memcpy +#else +#error "Not implemented for this platform" +#endif + +namespace pnNetCommon +{ + +#ifndef SERVER + +// NOTE: On Win32, WSAStartup() must be called before GetTextAddr() will work. +const char * GetTextAddr(UInt32 binAddr) +{ + in_addr in; + memcpy(&in,&binAddr,sizeof(binAddr)); + return inet_ntoa(in); +} + +// NOTE: On Win32, WSAStartup() must be called before GetBinAddr() will work. +UInt32 GetBinAddr(const char * textAddr) +{ + UInt32 addr = 0; + + if (!textAddr) + return addr; + + struct addrinfo * ai = NULL; + + addr = inet_addr(textAddr); + if(addr == INADDR_NONE) + { + ai = pnAddrInfo::GetAddrByNameSimple(textAddr); + if(ai!= NULL) + memcpy(&addr,(void*)(&(((sockaddr_in*)(ai->ai_addr))->sin_addr)),sizeof(addr)); + pnAddrInfo::Free(ai); + } + + return addr; +} + +#endif + +} // pnNetCommon namespace + + + +//////////////////////////////////////////////////////////////////// + +void plCreatableStream::Write( hsStream* stream, hsResMgr* mgr ) +{ + fStream.Rewind(); + std::string buf; + UInt32 len = fStream.GetEOF(); + stream->WriteSwap( len ); + buf.resize( len ); + fStream.Read( len, (void*)buf.data() ); + stream->Write( len, (const void*)buf.data() ); + fStream.Rewind(); +} + +void plCreatableStream::Read( hsStream* stream, hsResMgr* mgr ) +{ + fStream.Rewind(); + std::string buf; + UInt32 len; + stream->LogReadSwap( &len,"CreatableStream Len"); + buf.resize( len ); + stream->LogRead( len, (void*)buf.data(),"CreatableStream Data"); + fStream.Write( len, (const void*)buf.data() ); + fStream.Rewind(); +} + +//////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommon.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommon.h new file mode 100644 index 00000000..5363572f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommon.h @@ -0,0 +1,131 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pnNetCommon_h_inc +#define pnNetCommon_h_inc + +#include "hsConfig.h" +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsRefCnt.h" +#include "hsStream.h" +#include "../pnFactory/plCreatable.h" + +// +// main logging switch +// +#ifndef PLASMA_EXTERNAL_RELEASE +# define NET_LOGGING +#endif + +#ifndef hsLogEntry +# ifdef NET_LOGGING +# define hsLogEntry(x) x +# else +# define hsLogEntry(x) +# endif +#endif + +#define hsDbgLogEntry(x) +#ifdef NET_LOGGING +# ifdef HS_DEBUGGING +# undef hsDbgLogEntry +# define hsDbgLogEntry(x) x +# endif +#endif + +#define Bool2Int(value) ((value)?1:0) +#define Int2Bool(value) ((value)?true:false) + + +/////////////////////////////////////////////////////////////////// + +namespace pnNetCommon +{ +#ifndef SERVER + + UInt32 GetBinAddr(const char * textAddr); + const char * GetTextAddr(UInt32 binAddr); + +#endif // SERVER +} + +/////////////////////////////////////////////////////////////////// + +class plCreatableStream : public plCreatable +{ + hsRAMStream fStream; +public: + CLASSNAME_REGISTER( plCreatableStream ); + GETINTERFACE_ANY( plCreatableStream, plCreatable ); + void Read( hsStream* stream, hsResMgr* mgr=nil ); + void Write( hsStream* stream, hsResMgr* mgr=nil ); + hsStream * GetStream( void ) { return &fStream;} +}; + + +/////////////////////////////////////////////////////////////////// +// hsTempRef is incomplete. This type fills in some of the gaps +// (like symmetrical ref/unref and correct self-assign) + +#ifndef SERVER + +template +class plSafePtr +{ + T * fPtr; +public: + plSafePtr(T * ptr = nil): fPtr(ptr) {hsRefCnt_SafeRef(fPtr);} + ~plSafePtr() { hsRefCnt_SafeUnRef(fPtr); } + operator T*() const { return fPtr; } + operator T*&() { return fPtr; } + operator const T&() const { return *fPtr; } + operator bool() const { return fPtr!=nil;} + T * operator->() const { return fPtr; } + T * operator *() const { return fPtr; } + T * operator=(T * ptr) + { + hsRefCnt_SafeRef(ptr); + hsRefCnt_SafeUnRef(fPtr); + fPtr = ptr; + return fPtr; + } + void Attach(T * ptr) + { + if (fPtr==ptr) + return; + hsRefCnt_SafeUnRef(fPtr); + fPtr = ptr; + } + void Detach() { fPtr=nil;} +}; + +#endif // SERVER + + +#endif // pnNetCommon_h_inc + +/////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommonCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommonCreatable.h new file mode 100644 index 00000000..89da8e82 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetCommon/pnNetCommonCreatable.h @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pnNetCommonCreatable_inc +#define pnNetCommonCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plSynchedObject.h" +REGISTER_CREATABLE( plSynchedObject ); + +#include "plNetApp.h" +REGISTER_NONCREATABLE( plNetApp ); +REGISTER_NONCREATABLE( plNetClientApp ); +REGISTER_NONCREATABLE( plNetServerApp ); + +#include "plGenericVar.h" +REGISTER_CREATABLE( plCreatableGenericValue ); +#include "pnNetCommon.h" +REGISTER_CREATABLE( plCreatableStream ); + + +#endif // pnNetCommonCreatable_inc + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Intern.h new file mode 100644 index 00000000..5f4f69c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Intern.h @@ -0,0 +1,127 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETDIAG_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETDIAG_INTERN_H + + +namespace ND { + +extern HMODULE g_lib; +extern const wchar g_version[]; + + +//============================================================================ +enum { + kDiagSrvAuth, + kDiagSrvFile, + kNumDiagSrvs +}; + +//============================================================================ +inline unsigned NetProtocolToSrv (ENetProtocol protocol) { + + switch (protocol) { + case kNetProtocolCli2Auth: return kDiagSrvAuth; + case kNetProtocolCli2File: return kDiagSrvFile; + default: return kNumDiagSrvs; + } +} + +//============================================================================ +inline const wchar * SrvToString (unsigned srv) { + + switch (srv) { + case kDiagSrvAuth: return L"Auth"; + case kDiagSrvFile: return L"File"; + DEFAULT_FATAL(srv); + } +} + +} using namespace ND; + + +//============================================================================ +struct NetDiag : AtomicRef { + + bool destroyed; + CCritSect critsect; + wchar * hosts[kNumDiagSrvs]; + unsigned nodes[kNumDiagSrvs]; + + ~NetDiag (); + + void Destroy (); + void SetHost (unsigned srv, const wchar host[]); +}; + + +/***************************************************************************** +* +* SYS +* +***/ + +void SysStartup (); +void SysShutdown (); + + +/***************************************************************************** +* +* DNS +* +***/ + +void DnsStartup (); +void DnsShutdown (); + + +/***************************************************************************** +* +* ICMP +* +***/ + +void IcmpStartup (); +void IcmpShutdown (); + + +/***************************************************************************** +* +* TCP +* +***/ + +void TcpStartup (); +void TcpShutdown (); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Pch.h new file mode 100644 index 00000000..f51628e6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Pch.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETDIAG_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETDIAG_PCH_H + +#include "pnUtils/pnUtils.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" +#include "pnProduct/pnProduct.h" +#include "pnNetCli/pnNetCli.h" + +#define USES_PROTOCOL_CLI2AUTH +#define USES_PROTOCOL_CLI2FILE +#include "pnNetProtocol/pnNetProtocol.h" + +#include "pnNetDiag.h" +#include "Intern.h" + +#include +#include +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdDns.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdDns.cpp new file mode 100644 index 00000000..0b4379a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdDns.cpp @@ -0,0 +1,180 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdDns.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Internal types +* +***/ + +struct DNSParam { + NetDiag * diag; + FNetDiagDumpProc dump; + FNetDiagTestCallback callback; + void * param; + ENetProtocol protocol; + unsigned srv; +}; + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//============================================================================ +static void LookupCallback ( + void * param, + const wchar name[], + unsigned addrCount, + const NetAddress addrs[] +) { + ref(name); + + DNSParam * p = (DNSParam *)param; + if (addrCount) { + unsigned node = NetAddressGetNode(addrs[0]); + p->diag->critsect.Enter(); + { + p->diag->nodes[p->srv] = node; + } + p->diag->critsect.Leave(); + wchar nodeStr[64]; + NetAddressNodeToString(p->diag->nodes[p->srv], nodeStr, arrsize(nodeStr)); + p->dump(L"[DNS] Success. %s --> %s", name, nodeStr); + p->callback( + p->diag, + p->protocol, + kNetSuccess, + p->param + ); + } + else { + p->diag->critsect.Enter(); + { + // if the hostname still matches, then clear the node + if (p->diag->hosts[p->srv] && 0 == StrCmp(p->diag->hosts[p->srv], name)) + p->diag->nodes[p->srv] = 0; + } + p->diag->critsect.Leave(); + p->dump(L"[DNS] Failed to resolve hostname %s", name); + p->callback( + p->diag, + p->protocol, + kNetErrNameLookupFailed, + p->param + ); + } + p->diag->DecRef("DNS"); + DEL(p); +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void DnsStartup () { +} + +//============================================================================ +void DnsShutdown () { +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void NetDiagDns ( + NetDiag * diag, + ENetProtocol protocol, + FNetDiagDumpProc dump, + FNetDiagTestCallback callback, + void * param +) { + ASSERT(diag); + ASSERT(dump); + ASSERT(callback); + + unsigned srv = NetProtocolToSrv(protocol); + if (srv == kNumDiagSrvs) { + dump(L"[DNS] Unsupported protocol: %s", NetProtocolToString(protocol)); + callback(diag, protocol, kNetErrNotSupported, param); + return; + } + + wchar * host = nil; + diag->critsect.Enter(); + { + if (diag->hosts[srv]) + host = StrDup(diag->hosts[srv]); + } + diag->critsect.Leave(); + + if (!host) { + dump(L"[DNS] No hostname set for protocol: %s", NetProtocolToString(protocol)); + callback(diag, protocol, kNetSuccess, param); + return; + } + + diag->IncRef("DNS"); + dump(L"[DNS] Looking up %s...", host); + + DNSParam * dnsParam = NEWZERO(DNSParam); + dnsParam->diag = diag; + dnsParam->srv = srv; + dnsParam->protocol = protocol; + dnsParam->dump = dump; + dnsParam->callback = callback; + dnsParam->param = param; + + AsyncCancelId cancelId; + AsyncAddressLookupName( + &cancelId, + LookupCallback, + host, + 0, + dnsParam + ); + FREE(host); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdIcmp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdIcmp.cpp new file mode 100644 index 00000000..e26c151d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdIcmp.cpp @@ -0,0 +1,246 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdIcmp.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + +typedef HANDLE (PASCAL FAR * FIcmpCreateFile) (); +typedef DWORD (PASCAL FAR * FIcmpSendEcho) ( + HANDLE icmpHandle, + DWORD destinationAddress, + LPVOID requestData, + WORD requestSize, + PIP_OPTION_INFORMATION options, + LPVOID replyBuffer, + DWORD replySize, + DWORD timeoutMs +); + +struct PingParam { + NetDiag * diag; + FNetDiagDumpProc dump; + FNetDiagTestCallback callback; + void * param; + ENetProtocol protocol; + unsigned srv; +}; + + + +/***************************************************************************** +* +* Local data +* +***/ + +static const unsigned kPingCount = 5; +static const unsigned kPayloadBytes = 32; + +static FIcmpCreateFile IcmpCreateFile; +static FIcmpSendEcho IcmpSendEcho; + +static byte s_payload[kPayloadBytes]; + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static const wchar * IpStatusToString (ULONG status) { + + switch (status) { + case IP_SUCCESS: return L"IP_SUCCESS"; + case IP_BUF_TOO_SMALL: return L"IP_BUF_TOO_SMALL"; + case IP_DEST_NET_UNREACHABLE: return L"IP_DEST_NET_UNREACHABLE"; + case IP_DEST_HOST_UNREACHABLE: return L"IP_DEST_HOST_UNREACHABLE"; + case IP_DEST_PROT_UNREACHABLE: return L"IP_DEST_PROT_UNREACHABLE"; + case IP_DEST_PORT_UNREACHABLE: return L"IP_DEST_PORT_UNREACHABLE"; + case IP_NO_RESOURCES: return L"IP_NO_RESOURCES"; + case IP_BAD_OPTION: return L"IP_BAD_OPTION"; + case IP_HW_ERROR: return L"IP_HW_ERROR"; + case IP_PACKET_TOO_BIG: return L"IP_PACKET_TOO_BIG"; + case IP_REQ_TIMED_OUT: return L"IP_REQ_TIMED_OUT"; + case IP_BAD_REQ: return L"IP_BAD_REQ"; + case IP_BAD_ROUTE: return L"IP_BAD_ROUTE"; + case IP_TTL_EXPIRED_TRANSIT: return L"IP_TTL_EXPIRED_TRANSIT"; + case IP_TTL_EXPIRED_REASSEM: return L"IP_TTL_EXPIRED_REASSEM"; + case IP_PARAM_PROBLEM: return L"IP_PARAM_PROBLEM"; + case IP_SOURCE_QUENCH: return L"IP_SOURCE_QUENCH"; + case IP_OPTION_TOO_BIG: return L"IP_OPTION_TOO_BIG"; + case IP_BAD_DESTINATION: return L"IP_BAD_DESTINATION"; + default: return L"Unknown error"; + } +} + + +//============================================================================ +static void __cdecl PingThreadProc (void * param) { + + PingParam * p = (PingParam *)param; + + HANDLE icmp = IcmpCreateFile(); + if (!icmp) { + p->dump(L"[ICMP] Failed to create ICMP handle"); + p->callback(p->diag, p->protocol, kNetErrFileNotFound, p->param); + return; + } + + char addr[64]; + wchar waddr[64]; + NetAddressNodeToString(p->diag->nodes[p->srv], waddr, arrsize(waddr)); + StrToAnsi(addr, waddr, arrsize(addr)); + + ENetError result = kNetSuccess; + + byte reply[kPayloadBytes + sizeof(ICMP_ECHO_REPLY)]; + + for (unsigned i = 0; i < kPingCount; ++i) { + DWORD retval = IcmpSendEcho( + icmp, + inet_addr(addr), + s_payload, + sizeof(s_payload), + NULL, + reply, + sizeof(reply), + 4000 + ); + + PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)reply; + + if (retval) { + p->dump(L"[ICMP] Reply from %s. ms=%u", waddr, pEchoReply->RoundTripTime); + } + else { + result = kNetErrConnectFailed; + p->dump(L"[ICMP] No reply from %s. %s", waddr, IpStatusToString(pEchoReply->Status)); + } + } + + p->callback(p->diag, p->protocol, result, p->param); + p->diag->DecRef("ICMP"); + DEL(p); +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void IcmpStartup () { + + if (g_lib) { + IcmpCreateFile = (FIcmpCreateFile)GetProcAddress(g_lib, "IcmpCreateFile"); + IcmpSendEcho = (FIcmpSendEcho)GetProcAddress(g_lib, "IcmpSendEcho"); + } + MemSet(s_payload, (byte)((unsigned_ptr)&s_payload >> 4), arrsize(s_payload)); +} + +//============================================================================ +void IcmpShutdown () { + + IcmpCreateFile = nil; + IcmpSendEcho = nil; +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void NetDiagIcmp ( + NetDiag * diag, + ENetProtocol protocol, + FNetDiagDumpProc dump, + FNetDiagTestCallback callback, + void * param +) { + ASSERT(diag); + ASSERT(dump); + ASSERT(callback); + + if (!IcmpCreateFile || !IcmpSendEcho) { + dump(L"[ICMP] Failed to load IP helper API"); + callback(diag, protocol, kNetErrNotSupported, param); + return; + } + + unsigned srv = NetProtocolToSrv(protocol); + if (srv == kNumDiagSrvs) { + dump(L"[ICMP] Unsupported protocol: %s", NetProtocolToString(protocol)); + callback(diag, protocol, kNetErrNotSupported, param); + return; + } + + unsigned node = 0; + diag->critsect.Enter(); + { + node = diag->nodes[srv]; + } + diag->critsect.Leave(); + + if (!node) { + dump(L"[ICMP] No address set for protocol: %s", NetProtocolToString(protocol)); + callback(diag, protocol, kNetSuccess, param); + return; + } + + wchar nodeStr[64]; + NetAddressNodeToString(node, nodeStr, arrsize(nodeStr)); + dump(L"[ICMP] Pinging %s with %u bytes of data...", nodeStr, kPayloadBytes); + + PingParam * pingParam = NEWZERO(PingParam); + pingParam->diag = diag; + pingParam->srv = srv; + pingParam->protocol = protocol; + pingParam->dump = dump; + pingParam->callback = callback; + pingParam->param = param; + + diag->IncRef("ICMP"); + _beginthread(PingThreadProc, 0, pingParam); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdTcp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdTcp.cpp new file mode 100644 index 00000000..fd6ee57a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdTcp.cpp @@ -0,0 +1,851 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNdTcp.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + +namespace AuthKey { +// This file excluded from pre-compiled header because it is auto-generated by the build server. +#include "pnNetBase/pnNbAuthKey.hpp" +} // AuthKey + +/***************************************************************************** +* +* Local types +* +***/ + +struct AuthConn : AtomicRef { + NetDiag * diag; + FNetDiagDumpProc dump; + FNetDiagTestCallback callback; + void * param; + AsyncSocket sock; + AsyncCancelId cancelId; + NetCli * cli; + long pingsInRoute; + long pingsCompleted; + bool done; + ENetError error; + + ~AuthConn (); +}; + +struct AuthTrans : THashKeyVal { + HASHLINK(AuthTrans) link; + AuthConn * conn; + unsigned pingAtMs; + + AuthTrans (AuthConn * conn); + ~AuthTrans (); +}; + +struct FileConn : AtomicRef { + NetDiag * diag; + FNetDiagDumpProc dump; + FNetDiagTestCallback callback; + void * param; + AsyncSocket sock; + AsyncCancelId cancelId; + ARRAY(byte) recvBuffer; + long pingsInRoute; + long pingsCompleted; + bool done; + ENetError error; + + ~FileConn (); +}; + +struct FileTrans : THashKeyVal { + HASHLINK(FileTrans) link; + FileConn * conn; + unsigned pingAtMs; + + FileTrans (FileConn * conn); + ~FileTrans (); +}; + + + +/***************************************************************************** +* +* Local data +* +***/ + +static const unsigned kPingTimeoutMs = 5000; +static const unsigned kTimeoutCheckMs = 100; +static const unsigned kMaxPings = 15; + + +static long s_authProtocolRegistered; +static unsigned s_transId; +static CCritSect s_critsect; +static bool s_shutdown; +static byte s_payload[32]; +static AsyncTimer * s_timer; + +static HASHTABLEDECL( + AuthTrans, + THashKeyVal, + link +) s_authTrans; + +static HASHTABLEDECL( + FileTrans, + THashKeyVal, + link +) s_fileTrans; + + +/***************************************************************************** +* +* Cli2Auth protocol +* +***/ + +//============================================================================ +static bool Recv_PingReply ( + const byte msg[], + unsigned bytes, + void * +) { + ref(bytes); + + const Auth2Cli_PingReply & reply = *(const Auth2Cli_PingReply *)msg; + + AuthTrans * trans; + s_critsect.Enter(); + { + if (bytes < sizeof(Auth2Cli_PingReply)) { + // beta6 compatibility + if (nil != (trans = s_authTrans.Tail())) + s_authTrans.Unlink(trans); + } + else if (nil != (trans = s_authTrans.Find(reply.transId))) + s_authTrans.Unlink(trans); + } + s_critsect.Leave(); + + if (trans) { + unsigned replyAtMs = TimeGetMs(); + trans->conn->dump(L"[TCP] Reply from SrvAuth. ms=%u", replyAtMs - trans->pingAtMs); + DEL(trans); + return true; + } + else { + return false; + } +} + +//============================================================================ +#define MSG(s) kNetMsg_Cli2Auth_##s +static NetMsgInitSend s_send[] = { + { MSG(PingRequest) }, +}; + +#undef MSG +#define MSG(s) kNetMsg_Auth2Cli_##s, Recv_##s +static NetMsgInitRecv s_recv[] = { + { MSG(PingReply) }, +}; +#undef MSG + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static unsigned TimerCallback (void *) { + + unsigned timeMs = TimeGetMs(); + s_critsect.Enter(); + { + ENetError error = kNetErrTimeout; + {for (AuthTrans * next, * curr = s_authTrans.Head(); curr; curr = next) { + next = s_authTrans.Next(curr); + unsigned diff = timeMs - curr->pingAtMs; + if (diff > kPingTimeoutMs) { + if (!curr->conn->error) + curr->conn->error = error; + curr->conn->dump(L"[TCP] No reply from SrvAuth: %u, %s (ms=%u)", error, NetErrorToString(error), diff); + DEL(curr); + } + }} + {for (FileTrans * next, * curr = s_fileTrans.Head(); curr; curr = next) { + next = s_fileTrans.Next(curr); + unsigned diff = timeMs - curr->pingAtMs; + if (diff > kPingTimeoutMs) { + if (!curr->conn->error) + curr->conn->error = error; + curr->conn->dump(L"[TCP] No reply from SrvFile: %u, %s (ms=%u)", error, NetErrorToString(error), diff); + DEL(curr); + } + }} + } + s_critsect.Leave(); + + return kTimeoutCheckMs; +} + +//============================================================================ +static void AuthPingProc (void * param) { + + AuthConn * conn = (AuthConn *)param; + + while (!conn->done && conn->pingsCompleted < kMaxPings) { + + if (!conn->pingsInRoute) { + + AuthTrans * trans = NEW(AuthTrans)(conn); + trans->pingAtMs = TimeGetMs(); + + s_critsect.Enter(); + for (;;) { + if (conn->done) { + conn->pingsCompleted = kMaxPings; + DEL(trans); + break; + } + while (++s_transId == 0) + NULL_STMT; + trans->SetValue(s_transId); + s_authTrans.Add(trans); + + const unsigned_ptr msg[] = { + kCli2Auth_PingRequest, + trans->pingAtMs, + trans->GetValue(), + sizeof(s_payload), + (unsigned_ptr) s_payload, + }; + + NetCliSend(conn->cli, msg, arrsize(msg)); + NetCliFlush(conn->cli); + break; + } + s_critsect.Leave(); + } + AsyncSleep(10); + } + + s_critsect.Enter(); + { + conn->done = true; + AsyncSocketDisconnect(conn->sock, true); + NetCliDelete(conn->cli, false); + conn->cli = nil; + } + s_critsect.Leave(); + + conn->DecRef("Pinging"); +} + +//============================================================================ +static bool AuthConnEncrypt (ENetError error, void * param) { + + AuthConn * conn = (AuthConn *)param; + + if (IS_NET_SUCCESS(error)) { + conn->dump(L"[TCP] SrvAuth stream encrypted."); + conn->dump(L"[TCP] Pinging SrvAuth with 32 bytes of data..."); + conn->IncRef("Pinging"); + _beginthread(AuthPingProc, 0, conn); + } + else { + conn->dump(L"[TCP] SrvAuth stream encryption failed: %u, %s", error, NetErrorToString(error)); + } + + return IS_NET_SUCCESS(error); +} + +//============================================================================ +static void NotifyAuthConnSocketConnect (AuthConn * conn) { + + conn->dump(L"[TCP] SrvAuth socket established, encrypting stream..."); + + conn->TransferRef("Connecting", "Connected"); + conn->cli = NetCliConnectAccept( + conn->sock, + kNetProtocolCli2Auth, + false, + AuthConnEncrypt, + 0, + nil, + conn + ); +} + +//============================================================================ +static void NotifyAuthConnSocketConnectFailed (AuthConn * conn) { + + conn->error = kNetErrConnectFailed; + + conn->cancelId = 0; + conn->dump(L"[TCP] SrvAuth socket connection failed %u, %s", conn->error, NetErrorToString(conn->error)); + + conn->DecRef("Connecting"); +} + +//============================================================================ +static void NotifyAuthConnSocketDisconnect (AuthConn * conn) { + + if (!conn->done && !conn->error) + conn->error = kNetErrDisconnected; + + conn->cancelId = 0; + conn->dump(L"[TCP] SrvAuth socket closed: %u, %s", conn->error, NetErrorToString(conn->error)); + + HASHTABLEDECL( + AuthTrans, + THashKeyVal, + link + ) authTrans; + + s_critsect.Enter(); + { + conn->done = true; + while (AuthTrans * trans = s_authTrans.Head()) + authTrans.Add(trans); + } + s_critsect.Leave(); + + while (AuthTrans * trans = authTrans.Head()) { + conn->dump(L"[TCP] No reply from SrvAuth: %u, %s", conn->error, NetErrorToString(conn->error)); + DEL(trans); + } + + conn->DecRef("Connected"); +} + +//============================================================================ +static bool NotifyAuthConnSocketRead (AuthConn * conn, AsyncNotifySocketRead * read) { + + NetCliDispatch(conn->cli, read->buffer, read->bytes, conn); + read->bytesProcessed += read->bytes; + + return true; +} + +//============================================================================ +static bool AuthSocketNotifyCallback ( + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +) { + bool result = true; + AuthConn * conn; + + switch (code) { + case kNotifySocketConnectSuccess: + conn = (AuthConn *) notify->param; + *userState = conn; + conn->sock = sock; + conn->cancelId = 0; + NotifyAuthConnSocketConnect(conn); + break; + + case kNotifySocketConnectFailed: + conn = (AuthConn *) notify->param; + NotifyAuthConnSocketConnectFailed(conn); + break; + + case kNotifySocketDisconnect: + conn = (AuthConn *) *userState; + NotifyAuthConnSocketDisconnect(conn); + break; + + case kNotifySocketRead: + conn = (AuthConn *) *userState; + result = NotifyAuthConnSocketRead(conn, (AsyncNotifySocketRead *) notify); + break; + } + + return result; +} + +//============================================================================ +static bool Recv_File2Cli_ManifestReply (FileConn * conn, const File2Cli_ManifestReply & msg) { + + ref(conn); + + FileTrans * trans; + s_critsect.Enter(); + { + if (nil != (trans = s_fileTrans.Find(msg.transId))) + s_fileTrans.Unlink(trans); + } + s_critsect.Leave(); + + if (trans) { + unsigned replyAtMs = TimeGetMs(); + trans->conn->dump(L"[TCP] Reply from SrvFile. ms=%u", replyAtMs - trans->pingAtMs); + DEL(trans); + return true; + } + else { + return false; + } +} + +//============================================================================ +static void FilePingProc (void * param) { + + FileConn * conn = (FileConn *)param; + + while (!conn->done && conn->pingsCompleted < kMaxPings) { + + if (!conn->pingsInRoute) { + + FileTrans * trans = NEW(FileTrans)(conn); + trans->pingAtMs = TimeGetMs(); + + s_critsect.Enter(); + for (;;) { + if (conn->done) { + conn->pingsCompleted = kMaxPings; + DEL(trans); + break; + } + while (++s_transId == 0) + NULL_STMT; + trans->SetValue(s_transId); + s_fileTrans.Add(trans); + + Cli2File_ManifestRequest msg; + StrCopy(msg.group, L"External", arrsize(msg.group)); + msg.messageId = kCli2File_ManifestRequest; + msg.transId = trans->GetValue(); + msg.messageBytes = sizeof(msg); + msg.buildId = 0; + + AsyncSocketSend(conn->sock, &msg, sizeof(msg)); + break; + } + s_critsect.Leave(); + } + AsyncSleep(10); + } + + s_critsect.Enter(); + { + conn->done = true; + AsyncSocketDisconnect(conn->sock, true); + } + s_critsect.Leave(); + + conn->DecRef("Pinging"); +} + +//============================================================================ +static void NotifyFileConnSocketConnect (FileConn * conn) { + + conn->TransferRef("Connecting", "Connected"); + + conn->dump(L"[TCP] SrvFile socket established"); + conn->dump(L"[TCP] Pinging SrvFile..."); + conn->IncRef("Pinging"); + _beginthread(FilePingProc, 0, conn); +} + +//============================================================================ +static void NotifyFileConnSocketConnectFailed (FileConn * conn) { + + conn->error = kNetErrConnectFailed; + + conn->cancelId = 0; + conn->dump(L"[TCP] SrvFile socket connection failed %u, %s", conn->error, NetErrorToString(conn->error)); + + conn->DecRef("Connecting"); +} + +//============================================================================ +static void NotifyFileConnSocketDisconnect (FileConn * conn) { + + if (!conn->done && !conn->error) + conn->error = kNetErrDisconnected; + + conn->cancelId = 0; + conn->dump(L"[TCP] SrvFile socket closed: %u, %s", conn->error, NetErrorToString(conn->error)); + + HASHTABLEDECL( + FileTrans, + THashKeyVal, + link + ) fileTrans; + + s_critsect.Enter(); + { + conn->done = true; + while (FileTrans * trans = s_fileTrans.Head()) + fileTrans.Add(trans); + } + s_critsect.Leave(); + + while (FileTrans * trans = fileTrans.Head()) { + conn->dump(L"[TCP] No reply from SrvFile: %u, %s", conn->error, NetErrorToString(conn->error)); + DEL(trans); + } + + conn->DecRef("Connected"); +} + +//============================================================================ +static bool NotifyFileConnSocketRead (FileConn * conn, AsyncNotifySocketRead * read) { + + conn->recvBuffer.Add(read->buffer, read->bytes); + read->bytesProcessed += read->bytes; + + for (;;) { + if (conn->recvBuffer.Count() < sizeof(dword)) + return true; + + dword msgSize = *(dword *)conn->recvBuffer.Ptr(); + if (conn->recvBuffer.Count() < msgSize) + return true; + + const Cli2File_MsgHeader * msg = (const Cli2File_MsgHeader *) conn->recvBuffer.Ptr(); + + if (msg->messageId != kFile2Cli_ManifestReply) { + conn->dump(L"[TCP] SrvFile received unexpected message. id: %u", msg->messageId); + return false; + } + + if (!Recv_File2Cli_ManifestReply(conn, *(const File2Cli_ManifestReply *)msg)) + return false; + + conn->recvBuffer.Move(0, msgSize, conn->recvBuffer.Count() - msgSize); + conn->recvBuffer.ShrinkBy(msgSize); + } +} + +//============================================================================ +static bool FileSocketNotifyCallback ( + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +) { + bool result = true; + FileConn * conn; + + switch (code) { + case kNotifySocketConnectSuccess: + conn = (FileConn *) notify->param; + *userState = conn; + conn->sock = sock; + conn->cancelId = 0; + NotifyFileConnSocketConnect(conn); + break; + + case kNotifySocketConnectFailed: + conn = (FileConn *) notify->param; + NotifyFileConnSocketConnectFailed(conn); + break; + + case kNotifySocketDisconnect: + conn = (FileConn *) *userState; + NotifyFileConnSocketDisconnect(conn); + break; + + case kNotifySocketRead: + conn = (FileConn *) *userState; + result = NotifyFileConnSocketRead(conn, (AsyncNotifySocketRead *) notify); + break; + } + + return result; +} + +//============================================================================ +static void StartAuthTcpTest ( + NetDiag * diag, + const NetAddress & addr, + FNetDiagDumpProc dump, + FNetDiagTestCallback callback, + void * param +) { + if (0 == AtomicSet(&s_authProtocolRegistered, 1)) { + MemSet( + s_payload, + (byte)((unsigned_ptr)&s_payload >> 4), + sizeof(s_payload) + ); + NetMsgProtocolRegister( + kNetProtocolCli2Auth, + false, + s_send, arrsize(s_send), + s_recv, arrsize(s_recv), + AuthKey::kDhGValue, + BigNum(sizeof(AuthKey::kDhXData), AuthKey::kDhXData), + BigNum(sizeof(AuthKey::kDhNData), AuthKey::kDhNData) + ); + } + + wchar addrStr[128]; + NetAddressToString(addr, addrStr, arrsize(addrStr), kNetAddressFormatAll); + dump(L"[TCP] Connecting to SrvAuth at %s...", addrStr); + + diag->IncRef("TCP"); + + AuthConn * conn = NEWZERO(AuthConn); + conn->diag = diag; + conn->dump = dump; + conn->callback = callback; + conn->param = param; + conn->IncRef("Connecting"); + + Cli2Auth_Connect connect; + connect.hdr.connType = (byte) kConnTypeCliToAuth; + connect.hdr.hdrBytes = sizeof(connect.hdr); + connect.hdr.buildId = BuildId(); + connect.hdr.buildType = BuildType(); + connect.hdr.branchId = BranchId(); + connect.hdr.productId = ProductId(); + connect.data.token = kNilGuid; + connect.data.dataBytes = sizeof(connect.data); + + AsyncSocketConnect( + &conn->cancelId, + addr, + AuthSocketNotifyCallback, + conn, + &connect, + sizeof(connect), + 0, + 0 + ); +} + +//============================================================================ +static void StartFileTcpTest ( + NetDiag * diag, + const NetAddress & addr, + FNetDiagDumpProc dump, + FNetDiagTestCallback callback, + void * param +) { + wchar addrStr[128]; + NetAddressToString(addr, addrStr, arrsize(addrStr), kNetAddressFormatAll); + dump(L"[TCP] Connecting to SrvFile at %s...", addrStr); + + diag->IncRef("TCP"); + + FileConn * conn = NEWZERO(FileConn); + conn->diag = diag; + conn->dump = dump; + conn->callback = callback; + conn->param = param; + conn->IncRef("Connecting"); + + Cli2File_Connect connect; + connect.hdr.connType = kConnTypeCliToFile; + connect.hdr.hdrBytes = sizeof(connect.hdr); + connect.hdr.buildId = 0; + connect.hdr.buildType = BuildType(); + connect.hdr.branchId = BranchId(); + connect.hdr.productId = ProductId(); + connect.data.buildId = BuildId(); + connect.data.serverType = kSrvTypeNone; + connect.data.dataBytes = sizeof(connect.data); + + AsyncSocketConnect( + &conn->cancelId, + addr, + FileSocketNotifyCallback, + conn, + &connect, + sizeof(connect), + 0, + 0 + ); + +} + + +/***************************************************************************** +* +* AuthConn +* +***/ + +//============================================================================ +AuthConn::~AuthConn () { + if (cli) + NetCliDelete(cli, false); + if (sock) + AsyncSocketDelete(sock); + callback(diag, kNetProtocolCli2Auth, error, param); + diag->DecRef("TCP"); +} + + +/***************************************************************************** +* +* AuthTrans +* +***/ + +//============================================================================ +AuthTrans::AuthTrans (AuthConn * conn) +: conn(conn) +{ + conn->IncRef("Ping"); + AtomicAdd(&conn->pingsInRoute, 1); +} + +//============================================================================ +AuthTrans::~AuthTrans () { + + AtomicAdd(&conn->pingsCompleted, 1); + AtomicAdd(&conn->pingsInRoute, -1); + conn->DecRef("Ping"); +} + + +/***************************************************************************** +* +* FileConn +* +***/ + +//============================================================================ +FileConn::~FileConn () { + if (sock) + AsyncSocketDelete(sock); + callback(diag, kNetProtocolCli2File, error, param); + diag->DecRef("TCP"); +} + + +/***************************************************************************** +* +* FileTrans +* +***/ + +//============================================================================ +FileTrans::FileTrans (FileConn * conn) +: conn(conn) +{ + conn->IncRef("Ping"); + AtomicAdd(&conn->pingsInRoute, 1); +} + +//============================================================================ +FileTrans::~FileTrans () { + + AtomicAdd(&conn->pingsCompleted, 1); + AtomicAdd(&conn->pingsInRoute, -1); + conn->DecRef("Ping"); +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void TcpStartup () { + + s_shutdown = false; + AsyncTimerCreate(&s_timer, TimerCallback, 0, nil); +} + +//============================================================================ +void TcpShutdown () { + + s_shutdown = true; + AsyncTimerDeleteCallback(s_timer, TimerCallback); + s_timer = nil; +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void NetDiagTcp ( + NetDiag * diag, + ENetProtocol protocol, + unsigned port, + FNetDiagDumpProc dump, + FNetDiagTestCallback callback, + void * param +) { + ASSERT(diag); + ASSERT(dump); + ASSERT(callback); + + unsigned srv = NetProtocolToSrv(protocol); + if (srv == kNumDiagSrvs) { + dump(L"[TCP] Unsupported protocol: %s", NetProtocolToString(protocol)); + callback(diag, protocol, kNetErrNotSupported, param); + return; + } + + unsigned node; + NetAddress addr; + diag->critsect.Enter(); + { + node = diag->nodes[srv]; + } + diag->critsect.Leave(); + + if (!node) { + dump(L"[TCP] No address set for protocol: %s", NetProtocolToString(protocol)); + callback(diag, protocol, kNetSuccess, param); + return; + } + + NetAddressFromNode(node, port, &addr); + + switch (protocol) { + case kNetProtocolCli2Auth: + StartAuthTcpTest(diag, addr, dump, callback, param); + break; + + case kNetProtocolCli2File: + StartFileTcpTest(diag, addr, dump, callback, param); + break; + + DEFAULT_FATAL(protocol); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetDiag.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetDiag.cpp new file mode 100644 index 00000000..63062bd7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetDiag.cpp @@ -0,0 +1,151 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetDiag.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +namespace ND { + +/***************************************************************************** +* +* Local data +* +***/ + + +/***************************************************************************** +* +* Module data +* +***/ + +HMODULE g_lib; +//const wchar g_version[] = L"miasma"; +//const wchar g_version[] = L"ectomorph"; +const wchar g_version[] = L"solvent"; + +} // namespace ND + + +/***************************************************************************** +* +* NetDiag +* +***/ + +//============================================================================ +NetDiag::~NetDiag () { + + for (unsigned srv = 0; srv < kNumDiagSrvs; ++srv) + FREE(hosts[srv]); +} + +//============================================================================ +void NetDiag::SetHost (unsigned srv, const wchar host[]) { + + critsect.Enter(); + { + FREE(hosts[srv]); + + if (host) + hosts[srv] = StrDup(host); + else + hosts[srv] = nil; + + nodes[srv] = 0; + } + critsect.Leave(); +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void NetDiagInitialize () { + + g_lib = LoadLibrary("Iphlpapi.dll"); + + SysStartup(); + DnsStartup(); + IcmpStartup(); + TcpStartup(); + +} + +//============================================================================ +void NetDiagDestroy () { + + TcpShutdown(); + IcmpShutdown(); + DnsShutdown(); + SysShutdown(); + + if (g_lib) { + FreeLibrary(g_lib); + g_lib = nil; + } +} + +//============================================================================ +NetDiag * NetDiagCreate () { + + NetDiag * diag = NEWZERO(NetDiag); + diag->IncRef("Lifetime"); + return diag; +} + +//============================================================================ +void NetDiagDelete (NetDiag * diag) { + + ASSERT(!diag->destroyed); + diag->destroyed = true; + diag->DecRef("Lifetime"); +} + +//============================================================================ +void NetDiagSetHost ( + NetDiag * diag, + ENetProtocol protocol, + const wchar host[] +) { + ASSERT(diag); + + unsigned srv = NetProtocolToSrv(protocol); + if (srv == kNumDiagSrvs) + return; + + diag->SetHost(srv, host); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetDiag.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetDiag.h new file mode 100644 index 00000000..4d4e77a1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetDiag.h @@ -0,0 +1,129 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetDiag.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETDIAG_PNNETDIAG_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETDIAG_PNNETDIAG_H + +#define PNNETDIAG_INCLUDED + +#ifdef PLNETGAMELIB_INCLUDED +#error "pnNetDiag and plNetGameLib libraries may not be included in the same project because they invalidate each other's pnNetCli settings" +#endif + + +/***************************************************************************** +* +* Module +* +***/ + +void NetDiagInitialize (); +void NetDiagDestroy (); + + +/***************************************************************************** +* +* NetDiag +* +***/ + +struct NetDiag; + +NetDiag * NetDiagCreate (); +void NetDiagDelete (NetDiag * diag); +void NetDiagSetHost ( + NetDiag * diag, + ENetProtocol protocol, + const wchar name[] +); + +typedef void ( __cdecl * FNetDiagDumpProc)( + const wchar fmt[], + ... +); +typedef void (*FNetDiagTestCallback)( + NetDiag * diag, + ENetProtocol protocol, + ENetError result, + void * param +); + + +//============================================================================ +// Test: SYS +// Gather system information +//============================================================================ +void NetDiagSys ( + NetDiag * diag, + FNetDiagDumpProc dump, + FNetDiagTestCallback callback, + void * param +); + +//============================================================================ +// Test: DNS +// Lookup server address +//============================================================================ +void NetDiagDns ( + NetDiag * diag, + ENetProtocol protocol, + FNetDiagDumpProc dump, + FNetDiagTestCallback callback, + void * param +); + +//============================================================================ +// Test: ICMP +// Send out 5 sequential ICMP ping packets to the server +//============================================================================ +void NetDiagIcmp ( + NetDiag * diag, + ENetProtocol protocol, + FNetDiagDumpProc dump, + FNetDiagTestCallback callback, + void * param +); + +//============================================================================ +// Test: TCP +// Connect to server and measure bandwidth +//============================================================================ +void NetDiagTcp ( + NetDiag * diag, + ENetProtocol protocol, + unsigned port, // 0 --> use default client port + FNetDiagDumpProc dump, + FNetDiagTestCallback callback, + void * param +); + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETDIAG_PNNETDIAG_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetSys.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetSys.cpp new file mode 100644 index 00000000..ccf505c8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetSys.cpp @@ -0,0 +1,161 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetDiag/pnNetSys.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + +typedef DWORD (PASCAL FAR * FGetAdaptersInfo)( + PIP_ADAPTER_INFO pAdapterInfo, + PULONG pOutBufLen +); + + + +/***************************************************************************** +* +* Local data +* +***/ + +static FGetAdaptersInfo GetAdaptersInfo; + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void SysStartup () { + + if (g_lib) { + GetAdaptersInfo = (FGetAdaptersInfo)GetProcAddress(g_lib, "GetAdaptersInfo"); + } +} + +//============================================================================ +void SysShutdown () { + + GetAdaptersInfo = nil; +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void NetDiagSys ( + NetDiag * diag, + FNetDiagDumpProc dump, + FNetDiagTestCallback callback, + void * param +) { + ASSERT(diag); + ASSERT(dump); + ASSERT(callback); + + { // Timestamp + wchar str[256]; + qword time = TimeGetTime(); + TimePrettyPrint(time, arrsize(str), str); + dump(L"[SYS] Time: %s UTC", str); + } + + { // Command line + dump(L"[SYS] Cmdline: %s", AppGetCommandLine()); + } + + { // Product + wchar product[128]; + ProductString(product, arrsize(product)); + dump(L"[SYS] Product: %s", product); + } + + { // pnNetDiag version + dump(L"[SYS] Cognomen: '%s'", g_version); + } + + { // OS + OSVERSIONINFOEX info; + info.dwOSVersionInfoSize = sizeof(info); + GetVersionEx((OSVERSIONINFO*)&info); + dump(L"[SYS] OS Version: %u.%u", info.dwMajorVersion, info.dwMinorVersion); + dump(L"[SYS] OS Patch: %u.%u (%S)", info.wServicePackMajor, info.wServicePackMinor, info.szCSDVersion); + } + + { // System + word cpuCaps; + dword cpuVendor[3]; + word cpuSignature; + CpuGetInfo(&cpuCaps, cpuVendor, &cpuSignature); + SYSTEM_INFO info; + GetSystemInfo(&info); + dump(L"[SYS] CPU Count: %u", info.dwNumberOfProcessors); + dump(L"[SYS] CPU Vendor: %.*S", sizeof(cpuVendor), cpuVendor); + } + + { // Adapters + if (!GetAdaptersInfo) { + dump(L"[SYS] Failed to load IP helper API"); + callback(diag, kNetProtocolNil, kNetErrNotSupported, param); + return; + } + + ULONG ulOutBufLen = 0; + GetAdaptersInfo(nil, &ulOutBufLen); + PIP_ADAPTER_INFO pInfo = (PIP_ADAPTER_INFO)ALLOC(ulOutBufLen); + PIP_ADAPTER_INFO pAdapter; + if (GetAdaptersInfo(pInfo, &ulOutBufLen) == NO_ERROR) { + pAdapter = pInfo; + while (pAdapter) { + dump(L"[SYS] NIC: %S", pAdapter->Description); + pAdapter = pAdapter->Next; + } + callback(diag, kNetProtocolNil, kNetSuccess, param); + } + else { + dump(L"[SYS] Error getting adaper list"); + callback(diag, kNetProtocolNil, kNetErrFileNotFound, param); + } + FREE(pInfo); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Intern.h new file mode 100644 index 00000000..f96c685c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Intern.h @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETLOG_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETLOG_INTERN_H + + + +/***************************************************************************** +* +* pnNlApi.cpp +* +***/ + +const NetLogEvent *NetLogFindEvent (unsigned type, ESrvType srvType); + + +/***************************************************************************** +* +* pnNlCli.cpp +* +***/ + +void NetLogCliInitialize (ESrvType srvType); +void NetLogCliShutdown (); +void NetLogCliDestroy (); +void NetLogCliSendEvent (const NetLogEvent &event, va_list args); + +/***************************************************************************** +* +* pnNlSrv.cpp +* +***/ + +void NetLogSrvInitialize (); +void NetLogSrvShutdown (); +void NetLogSrvDestroy (); \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Pch.h new file mode 100644 index 00000000..20c886f3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Pch.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETLOG_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETLOG_PCH_H + + +// NucleusLib +#include "pnUtils/pnUtils.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" +#include "pnNetCli/pnNetCli.h" + +#ifdef SERVER +#include "pnIni/pnIni.h" +#include "../ServerLib/psUtils/psUtils.h" +#endif + +#define USES_PROTOCOL_SRV2LOG +#include "pnNetProtocol/pnNetProtocol.h" +#include "pnProduct/pnProduct.h" + +// Local +#include "pnNetLog.h" +#include "Intern.h" + +// System +#include // _alloca diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNetLog.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNetLog.h new file mode 100644 index 00000000..82b8b4d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNetLog.h @@ -0,0 +1,148 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNetLog.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETLOG_PNNETLOG_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNetLog.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETLOG_PNNETLOG_H + +#define MAX_NAME_LEN 64 + + +/***************************************************************************** +* +* + Look at psLogEvents/psLeAuth for an example of how to create a new LogEvent +* +***/ + + +/***************************************************************************** +* +* Log Template Definitions +* +***/ + +enum ELogParamType { + kLogParamInt, // (int) + kLogParamUnsigned, // (unsigned) + kLogParamFloat, // (float) + kLogParamUuid, // Uuid + kLogParamStringW, // (const wchar *) + kLogParamLong, // (long) or (dword) + kLogParamLongLong, // (long long) or (qword) + kNumLogParamTypes +}; + +struct NetLogField { + ELogParamType type; // element type + wchar name[MAX_PATH]; +}; + +struct NetLogEvent { + ESrvType srvType; + unsigned logEventType; + const wchar * eventName; + const NetLogField * fields; + unsigned numFields; +}; + +struct NetLogSrvField { + const wchar *name; + const wchar *data; +}; + +#define NET_LOG_FIELD_INT(name) { kLogParamInt, name } +#define NET_LOG_FIELD_UNSIGNED(name) { kLogParamUnsigned, name } +#define NET_LOG_FIELD_FLOAT(name) { kLogParamFloat, name } +#define NET_LOG_FIELD_STRING(name) { kLogParamStringW, name } +#define NET_LOG_FIELD_UUID(name) { kLogParamUuid, name } +#define NET_LOG_FIELD_LONG(name) { kLogParamLong, name } +#define NET_LOG_FIELD_LONGLONG(name) { kLogParamLongLong, name } + +#define NET_LOG_EVENT_AUTH(name) { kSrvTypeAuth, kLogEventId_##name, L#name, kLogEventFields_##name, arrsize( kLogEventFields_##name )} +#define NET_LOG_EVENT_GAME(name) { kSrvTypeGame, kLogEventId_##name, L#name, kLogEventFields_##name, arrsize( kLogEventFields_##name )} +#define NET_LOG_EVENT_MCP(name) { kSrvTypeMcp, kLogEventId_##name, L#name, kLogEventFields_##name, arrsize( kLogEventFields_##name )} +#define NET_LOG_EVENT_DB(name) { kSrvTypeDb, kLogEventId_##name, L#name, kLogEventFields_##name, arrsize( kLogEventFields_##name )} + + +/***************************************************************************** +* +* pnNlApi.cpp +* +***/ + +void NetLogInitialize (ESrvType srvType); +void NetLogShutdown (); +void NetLogDestroy (); +void NetLogRegisterEvents (const NetLogEvent events[], unsigned count); + +// Should only be called by psLogEvents - look there for logging functions +void NetLogSendEvent ( + unsigned type, + ... +); + + +/***************************************************************************** +* +* pnNlConn.cpp +* +***/ + +enum { + kNlCliNumConn, + kNlCliNumTrans, + kNlCliNumPendingSaves, + kNlCliNumPerf, +}; + +long NlCliGetPerf (unsigned index); + + +/***************************************************************************** +* +* pnNlSrv.cpp +* +***/ + +struct LogConn; +enum { + kNlSrvPerfConnCount, + kNlSrvPerfConnDenied, + kNlSrvNumPerf +}; + +typedef void (*NlSrvCallback)(const NetLogEvent *event, const ARRAY(wchar) &, unsigned, NetAddressNode &, qword, unsigned, unsigned); +long NetLogSrvGetPerf (unsigned index); +void NetLogSrvRegisterCallback(NlSrvCallback callback); +void LogConnIncRef (LogConn * conn); +void LogConnDecRef (LogConn * conn); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlApi.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlApi.cpp new file mode 100644 index 00000000..f892910e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlApi.cpp @@ -0,0 +1,215 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlApi.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private Data +* +***/ + +struct EventHash { + unsigned eventType; + ESrvType srvType; + + inline EventHash ( + unsigned eventType, + ESrvType srvType + ); + + inline dword GetHash () const; + inline bool operator== (const EventHash & rhs) const; +}; + +struct NetLogEventHash : EventHash { + const NetLogEvent * event; + + NetLogEventHash( + unsigned eventType, + ESrvType srvType, + const NetLogEvent *event + ); + HASHLINK(NetLogEventHash) link; +}; + +static CCritSect s_critsect; +static ESrvType s_srvType; +static HASHTABLEDECL(NetLogEventHash, EventHash, link) s_registeredEvents; + + +/***************************************************************************** +* +* NetLogEventHash +* +***/ + +//============================================================================ +NetLogEventHash::NetLogEventHash ( + unsigned eventType, + ESrvType srvType, + const NetLogEvent *event +) : EventHash(eventType, srvType), + event(event) +{ +} + + +/***************************************************************************** +* +* Event Hash +* +***/ + +//============================================================================ +inline EventHash::EventHash ( + unsigned eventType, + ESrvType srvType +) : eventType(eventType) +, srvType(srvType) +{ +} + +//============================================================================ +inline dword EventHash::GetHash () const { + CHashValue hash(this, sizeof(*this)); + return hash.GetHash(); +} + +//============================================================================ +inline bool EventHash::operator== (const EventHash & rhs) const { + return + eventType == rhs.eventType && + srvType == rhs.srvType; +} +/***************************************************************************** +* +* Private Functions +* +***/ + +//============================================================================ +static void NetLogUnRegisterEvents () { + HASHTABLEDECL(NetLogEventHash, EventHash, link) tempHashTable; + s_critsect.Enter(); + { + while(NetLogEventHash *hash = s_registeredEvents.Head()) { + tempHashTable.Add(hash); + } + } + s_critsect.Leave(); + + while(NetLogEventHash *hash = tempHashTable.Head()) { + delete hash; + } +} + + +/***************************************************************************** +* +* Public Functions +* +***/ + +//============================================================================ +void NetLogInitialize (ESrvType srvType) { + s_srvType = srvType; + if(s_srvType == kSrvTypeLog) + NetLogSrvInitialize(); + else + NetLogCliInitialize(srvType); +} + +//============================================================================ +void NetLogShutdown () { + if(s_srvType == kSrvTypeLog) + NetLogSrvShutdown(); + else + NetLogCliShutdown(); + NetLogUnRegisterEvents(); +} + +//============================================================================ +void NetLogDestroy () { + if(s_srvType == kSrvTypeLog) + NetLogSrvDestroy(); + else + NetLogCliDestroy(); +} + +//============================================================================ +void NetLogRegisterEvents (const NetLogEvent events[], unsigned count) { + NetLogEventHash *hash; + HASHTABLEDECL(NetLogEventHash, EventHash, link) tempHashTable; + + for(unsigned i = 0; i < count; ++i) { + hash = NEW(NetLogEventHash)(events[i].logEventType, events[i].srvType, &events[i]); + tempHashTable.Add(hash); + } + s_critsect.Enter(); + { + while(NetLogEventHash *hash = tempHashTable.Head()) { + s_registeredEvents.Add(hash); + } + } + s_critsect.Leave(); +} + +//============================================================================ +const NetLogEvent *NetLogFindEvent (unsigned type, ESrvType srvType) { + NetLogEventHash *hash; + s_critsect.Enter(); + { + hash = s_registeredEvents.Find(EventHash(type, srvType)); + } + s_critsect.Leave(); + return hash ? hash->event : nil; +} + +//============================================================================ +void NetLogSendEvent ( + unsigned type + ... +) { + const NetLogEvent *event = NetLogFindEvent(type, s_srvType); + + if(event) { + va_list args; + va_start(args, type); + NetLogCliSendEvent(*event, args); + va_end(args); + } + else { + LogMsg( kLogError, "unable to log event, event not found SrvType: %d EventType: %d.", s_srvType, type); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlCli.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlCli.cpp new file mode 100644 index 00000000..536c428c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlCli.cpp @@ -0,0 +1,542 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlCli.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + +struct NetLogConn : SrvConn { + NetAddress netaddr; + ESrvType srvType; + + NetLogConn (const NetAddress & addr, ESrvType srvType); + ~NetLogConn (); + + void Connect ( + AsyncCancelId * cancelId, + FAsyncNotifySocketProc notifyProc, + void * param + ); +}; + +struct LogMsgTrans : SrvTrans { + Srv2Log_LogMsg *msgBuffer; + + LogMsgTrans(); + ~LogMsgTrans(); + bool OnTransReply ( + ENetError * error, + SrvMsgHeader * msg + ); +}; + +struct LogConnEventNode { + LINK(LogConnEventNode) link; + Srv2Log_LogMsg * msg; + unsigned sendTimeMs; + + LogConnEventNode(Srv2Log_LogMsg *msg, unsigned timeStampMs); +}; + + +/***************************************************************************** +* +* Private Data +* +***/ + +static NetLogConn * s_conn; +static CCritSect s_critsect; +static IniChangeReg * s_change; +static ESrvType s_srvType; +static LISTDECL(LogConnEventNode, link) s_eventQueue; +static AsyncTimer * s_timer; +static long s_perf[kNlCliNumPerf]; +static bool s_running; + +static const unsigned kIssueSaveMs = 100; +static const unsigned kMaxNumberOfTransactions = 2000; + + +/***************************************************************************** +* +* Private Functions +* +***/ + +//============================================================================ +static NetLogConn * GetConnIncRef () { + NetLogConn * conn; + s_critsect.Enter(); + { + if (nil != (conn = s_conn)) + conn->IncRef(); + } + s_critsect.Leave(); + return conn; +} + +//============================================================================ +static void NetLogConnConnect (NetAddress & addr, ESrvType srvType) { + NetLogConn *conn = SRV_CONN_ALLOC(NetLogConn)(addr, srvType); + + s_critsect.Enter(); + { + SWAP(s_conn, conn); + } + s_critsect.Leave(); + + if (conn) + conn->Destroy(); +} + +//============================================================================ +static void NetLogConnDisconnect () { + NetLogConn *conn = nil; + + s_critsect.Enter(); + { + SWAP(s_conn, conn); + } + s_critsect.Leave(); + + if (conn) + conn->Destroy(); +} + +//============================================================================ +static void AddEventNode (Srv2Log_LogMsg *msg) { + LogConnEventNode *node = NEW(LogConnEventNode)(msg, TimeGetMs() + kIssueSaveMs); + s_critsect.Enter(); + { + s_eventQueue.Link(node); + } + s_critsect.Leave(); + + AsyncTimerUpdate( + s_timer, + kIssueSaveMs, + kAsyncTimerUpdateSetPriorityHigher + ); +} + +//============================================================================ +static void ParseIni (Ini * ini) { + unsigned iter; + const IniValue * value = IniGetFirstValue( + ini, + L"Server Locations", + L"LogSrv", + &iter + ); + + if (value) { + wchar addrStr[32]; + IniGetString(value, addrStr, arrsize(addrStr), 0, nil); + NetAddress addr; + NetAddressFromString(&addr, addrStr, kNetDefaultServerPort); + NetLogConnConnect(addr, s_srvType); + } +} + +//============================================================================ +static void IniChangeCallback (const wchar fullPath[]) { + Ini * ini = IniOpen(fullPath); + ParseIni(ini); + IniClose(ini); +} + +//============================================================================ +static unsigned TimerCallback (void *) { + LISTDECL(LogConnEventNode, link) sendList; + unsigned sleepMs = kAsyncTimeInfinite; + + s_critsect.Enter(); + { + int allowedNumTrans = kMaxNumberOfTransactions - s_perf[kNlCliNumPendingSaves]; // find the number of transactions that we can send based on the number in transit, and the max number allowed + if(allowedNumTrans < 0) // this could be negative, if so set to zero + allowedNumTrans = 0; + + dword currTime = TimeGetMs(); + LogConnEventNode *hash; + int timeDiff; + + // Add pending saves, up to the max allowed per update + for(;;) { + if(!allowedNumTrans && s_running) { + sleepMs = 5000; // we are at our max number of transactions sleep for 5 seconds and try again + break; + } + + hash = s_eventQueue.Head(); + if(!hash) + break; // no messages left. We will wait until another message comes in before another timer update + + timeDiff = hash->sendTimeMs - currTime; + + // nodes are naturally ordered by increasing sendTimeMs + if(!s_running || (timeDiff <= 0)) { + sendList.Link(hash); + --allowedNumTrans; + } + else { + sleepMs = timeDiff; + break; + } + } + } + s_critsect.Leave(); + + while(LogConnEventNode *node = sendList.Head()) { + LogMsgTrans * trans = SRV_TRANS_ALLOC(LogMsgTrans); + trans->msgBuffer = node->msg; + + if (NetLogConn * conn = GetConnIncRef()) { + conn->SendRequest(trans, node->msg); + conn->DecRef(); + } + else { + trans->TransCancel(kNetErrTimeout); + } + delete node; + } + return sleepMs; +} + +//============================================================================ +static unsigned CalcArgsLength (const NetLogEvent &event, va_list args) { + unsigned length = 0; + unsigned paramType = kNumLogParamTypes; // invalidate + unsigned field = 0; + + for(unsigned i = 0; i < event.numFields * 2; ++i) { + if(!(i % 2)) { + paramType = va_arg(args, unsigned); + continue; + } + + //validate parameter type + if(paramType != (unsigned)event.fields[field].type) { + length = 0; + LogMsg( kLogError, "Log parameter types do not match for event: %s parameter: %s?", event.eventName, event.fields[field].name); + break; + } + + switch(event.fields[field].type) { + case kLogParamInt: { + va_arg(args, int); + length += sizeof(int); + } + break; + + case kLogParamUnsigned: { + va_arg(args, unsigned); + length += sizeof(unsigned); + } + break; + + case kLogParamFloat: { + va_arg(args, float); + length += sizeof(float); + } + break; + + case kLogParamLong: { + va_arg(args, long); + length += sizeof(long); + } + break; + + case kLogParamLongLong: { + va_arg(args, long long); + length += sizeof(long long); + } + break; + + case kLogParamUuid: { + va_arg(args, Uuid); + length += sizeof(Uuid); + } + break; + + case kLogParamStringW: { + wchar *str = va_arg(args, wchar *); + if(!str) + str = L""; + length += StrBytes(str); + + } + break; + + default: + hsAssert(false, "Unknown argument type in log statement"); + return 0; + } + ++field; + } + return length; +} + + +/***************************************************************************** +* +* NetLogConn +* +***/ + +//============================================================================ +NetLogConn::NetLogConn (const NetAddress & addr, ESrvType srvType) +: netaddr(addr), + srvType(srvType) +{ + AtomicAdd(&s_perf[kNlCliNumConn], 1); + SetAutoPing(); + AutoReconnect(); +} + +//============================================================================ +NetLogConn::~NetLogConn () { + AtomicAdd(&s_perf[kNlCliNumConn], -1); +} + +//============================================================================ +void NetLogConn::Connect ( + AsyncCancelId * cancelId, + FAsyncNotifySocketProc notifyProc, + void * param +) { + // Connect to remote server + Srv2Log_Connect connect; + connect.hdr.connType = kConnTypeSrvToLog; + connect.hdr.hdrBytes = sizeof(connect.hdr); + connect.hdr.buildId = BuildId(); + connect.hdr.buildType = BuildType(); + connect.hdr.branchId = BranchId(); + connect.hdr.productId = ProductId(); + connect.data.dataBytes = sizeof(connect.data); + connect.data.buildId = BuildId(); + connect.data.srvType = srvType; + connect.data.buildType = BuildType(); + connect.data.productId = ProductId(); + + AsyncSocketConnect( + cancelId, + netaddr, + notifyProc, + param, + &connect, + sizeof(connect) + ); +} + + +/***************************************************************************** +* +* LogMsgTrans +* +***/ + +//============================================================================ +LogMsgTrans::LogMsgTrans () +: msgBuffer(nil) +{ + AtomicAdd(&s_perf[kNlCliNumTrans], 1); +} + +//============================================================================ +LogMsgTrans::~LogMsgTrans () { + AtomicAdd(&s_perf[kNlCliNumTrans], -1); +} + +//============================================================================ +bool LogMsgTrans::OnTransReply ( + ENetError * error, + SrvMsgHeader * msg +) { + ref(msg); + bool result; + if (msg->protocolId != kNetProtocolSrv2Log) { + result = SrvTrans::OnTransReply(error, msg); + } + else { + result = true; + } + + if(IS_NET_ERROR(*error) && s_running) { + AddEventNode(msgBuffer); + } + else { + FREE(msgBuffer); + } + return true; +} + + +/***************************************************************************** +* +* LogConnEventNode +* +***/ + +//============================================================================ +LogConnEventNode::LogConnEventNode (Srv2Log_LogMsg *msg, unsigned sendTimeMs) +: msg(msg), + sendTimeMs(sendTimeMs) +{ +} + + +/***************************************************************************** +* +* Protected +* +***/ + +//============================================================================ +void NetLogCliInitialize (ESrvType srvType) { + s_running = true; + s_srvType = srvType; + AsyncTimerCreate( + &s_timer, + TimerCallback, + kAsyncTimeInfinite, + nil + ); + IniChangeAdd(L"plServer", IniChangeCallback, &s_change); +} + +//============================================================================ +void NetLogCliShutdown () { + s_running = false; + if(s_change) { + IniChangeRemove(s_change, true); + s_change = false; + } + if(s_timer) { + AsyncTimerDeleteCallback(s_timer, TimerCallback); + s_timer = nil; + } + NetLogConnDisconnect(); +} + +//============================================================================ +void NetLogCliDestroy () { + while(s_perf[kNlCliNumTrans]) + AsyncSleep(10); + while(s_perf[kNlCliNumConn]) + AsyncSleep(10); +} + +//============================================================================ +void NetLogCliSendEvent (const NetLogEvent &event, va_list args) { + Srv2Log_LogMsg *msg; + unsigned length = CalcArgsLength(event, args); + if(!length) + return; + + CSrvPackBuffer pack( + sizeof(*msg) + + length + ); + + msg = (Srv2Log_LogMsg *) pack.Alloc(sizeof(*msg)); + msg->transId = 0; + msg->protocolId = kNetProtocolSrv2Log; + msg->messageId = kSrv2Log_LogMsg; + msg->eventType = event.logEventType; + msg->timestamp = TimeGetLocalTime(); + + unsigned field = 0; + unsigned paramType = kNumLogParamTypes; + + // if we get here the template parameters have already been validated by CalcLength, no need to do that again. + for(unsigned i = 0; i < event.numFields * 2; ++i) { + if(!(i % 2)) { + paramType = va_arg(args, unsigned); + continue; + } + switch(event.fields[field].type) { + case kLogParamInt: { + int i = va_arg(args, int); + pack.AddData(&i, sizeof(int)); + } + break; + + case kLogParamUnsigned: { + unsigned u = va_arg(args, unsigned); + pack.AddData(&u, sizeof(unsigned)); + } + break; + + case kLogParamFloat: { + float f = va_arg(args, float); + pack.AddData(&f, sizeof(float)); + } + break; + + case kLogParamLong: { + long l = va_arg(args, long); + pack.AddData(&l, sizeof(long)); + } + break; + + case kLogParamLongLong: { + long long ll = va_arg(args, long long); + pack.AddData(&ll, sizeof(long long)); + } + break; + + case kLogParamUuid: { + Uuid uuid = va_arg(args, Uuid); + pack.AddData(&uuid, sizeof(Uuid)); + } + break; + + case kLogParamStringW: { + wchar *str = va_arg(args, wchar *); + if(!str) + str = L""; + pack.AddString(str); + } + break; + } + ++field; + } + + msg->messageBytes = pack.Size(); + AddEventNode(msg); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlSrv.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlSrv.cpp new file mode 100644 index 00000000..01990595 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlSrv.cpp @@ -0,0 +1,587 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetLog/pnNlSrv.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + +struct EventHash { + unsigned eventType; + ESrvType srvType; + + inline EventHash ( + unsigned eventType, + ESrvType srvType + ); + + inline dword GetHash () const; + inline bool operator== (const EventHash & rhs) const; +}; + +struct NetLogEventHash : EventHash { + const NetLogEvent * event; + + NetLogEventHash( + unsigned eventType, + ESrvType srvType, + const NetLogEvent *event + ); + HASHLINK(NetLogEventHash) link; +}; + +struct LogConn : SrvConn { + LINK(LogConn) link; + ESrvType srvType; + unsigned buildId; + NetAddressNode addr; + unsigned buildType; + unsigned productId; + + // Ctor + LogConn ( + AsyncSocket sock, + void ** userState, + NetAddressNode nodeNumber, + unsigned buildId, + ESrvType srvType, + unsigned buildType, + unsigned productId + ); + ~LogConn (); + + bool OnSrvMsg (SrvMsgHeader * msg); + void OnDisconnect (); + + bool Recv_Srv2Log_LogMsg( const Srv2Log_LogMsg & msg ); +}; + + +/***************************************************************************** +* +* Private Data +* +***/ + +static IniChangeReg * s_change; +static long s_perf[kNlSrvNumPerf]; +static CCritSect s_critsect; +static bool s_running; +static LISTDECL(LogConn, link) s_conns; +void (*NetLogSrvCallback)(const NetLogEvent *event, const ARRAY(wchar) &, unsigned, NetAddressNode &, qword, unsigned, unsigned ) ; + + +/***************************************************************************** +* +* Private Functions +* +***/ + +//============================================================================ +static void ParseIni (Ini * ini) { + unsigned iter; + const IniValue *value; + + value = IniGetFirstValue( + ini, + L"", + L"", + &iter + ); +} + +//============================================================================ +static void IniChangeCallback (const wchar fullPath[]) { + Ini * ini = IniOpen(fullPath); + ParseIni(ini); + IniClose(ini); +} + +//=========================================================================== +static bool SocketNotifyProc ( + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +) { + // If the socket is successfully connected then only + // kNotifySocketListenSuccess will arrive here, as + // the service routine will be changed by SrvConn. + if (code != kNotifySocketListenSuccess) { + if (code == kNotifySocketDisconnect) + AsyncSocketDelete(sock); + return false; + } + + // TODO: Verify this is from a valid server + AsyncNotifySocketListen * listen = (AsyncNotifySocketListen *) notify; + EServerRights rights = SrvIniGetServerRights(listen->remoteAddr); + if (rights < kSrvRightsServer) { + LogMsgDebug("LogConn: insufficient server rights"); + AtomicAdd(&s_perf[kNlSrvPerfConnDenied], 1); + return false; + } + + NetAddressNode nodeNumber = NetAddressGetNode(listen->remoteAddr); + + // Parse the connect message + Srv2Log_ConnData connect; + if (!Srv2LogValidateConnect(listen, &connect)) { + LogMsgDebug("LogConn: invalid connect packet"); + AtomicAdd(&s_perf[kNlSrvPerfConnDenied], 1); + return false; + } + + // Create connection record + LogConn * conn = SRV_CONN_ALLOC(LogConn)( + sock, + userState, + nodeNumber, + connect.buildId, + (ESrvType) connect.srvType, + connect.buildType, + connect.productId + ); + + // Add this connection + bool result; + s_critsect.Enter(); + { + s_conns.Link(conn); + result = s_running; + } + s_critsect.Leave(); + + return result; +} + + +/***************************************************************************** +* +* LogConn implementation +* +***/ + +//============================================================================ +LogConn::LogConn ( + AsyncSocket sock, + void ** userState, + NetAddressNode nodeNumber, + unsigned buildId, + ESrvType srvType, + unsigned buildType, + unsigned productId +) : srvType(srvType), + buildId(buildId), + buildType(buildType), + productId(productId) +{ + ref(nodeNumber); + AtomicAdd(&s_perf[kNlSrvPerfConnCount], 1); + SetAutoTimeout(); + NotifyListen(sock, userState); + addr = nodeNumber; +} + +//============================================================================ +LogConn::~LogConn () { + AtomicAdd(&s_perf[kNlSrvPerfConnCount], -1); +} + +//============================================================================ +void LogConn::OnDisconnect () { + // Accepting side just destroys the connection. Connecting side + // will reconnect if that's what should happen. + Destroy(); +} + +//============================================================================ +bool LogConn::OnSrvMsg (SrvMsgHeader * msg) { + // Pass along messages not intended for us + if (msg->protocolId != kNetProtocolSrv2Log) + return SrvConn::OnSrvMsg(msg); + + bool status = false; + #define DISPATCH(a) case k##a: status = Recv_##a(*(const a*)msg); break + switch (msg->messageId) { + DISPATCH(Srv2Log_LogMsg); + + default: + status = false; + break; + } + #undef DISPATCH + + return status; +} + +//============================================================================ +bool LogConn::Recv_Srv2Log_LogMsg(const Srv2Log_LogMsg & msg ) { + CSrvUnpackBuffer unpack(&msg, msg.messageBytes); + (void)unpack.GetData(sizeof(msg)); + unsigned length = 0; + ARRAY(wchar) databuf; + wchar data[256]; + const void *pData = 0; + + SendReply(msg.transId, kNetSuccess); + if(!s_running) + return true; + + // WARNING: each parameter type needs to be able to handle the case where GetData returns a nil pointer for backward compatability + + const NetLogEvent *event = NetLogFindEvent(msg.eventType, srvType); + if(event) { + for(unsigned i = 0; i < event->numFields; ++i) { + if(!event->fields[i].name) + { + LogMsg(kLogError, "Failed logging event because of nil param name: %s", event->eventName); + return true; + } + + switch(event->fields[i].type) { + case kLogParamInt: { + int i = 0; + pData = unpack.GetData(sizeof(int)); + if(!pData) + continue; + + i = *(int *)pData; + StrPrintf(data, arrsize(data), L"%d", i); + length += StrLen(data) + 1; + } + break; + + case kLogParamUnsigned: { + unsigned u = 0; + pData = unpack.GetData(sizeof(unsigned)); + if(!pData) + continue; + + u = *(unsigned *)pData; + StrPrintf(data, arrsize(data), L"%d", u); + length += StrLen(data) + 1; + } + break; + + case kLogParamFloat: { + float f = 0; + pData = unpack.GetData(sizeof(float)); + if(!pData) + continue; + + f = *(float *)pData; + StrPrintf(data, arrsize(data), L"%f", f); + length += StrLen(data) + 1; + } + break; + + case kLogParamLong: { + long l = 0; + pData = unpack.GetData(sizeof(long)); + if(!pData) + continue; + + l = *(long *)pData; + StrPrintf(data, arrsize(data), L"%ld", l); + length += StrLen(data) + 1; + } + break; + + case kLogParamLongLong: { + long long ll = 0; + pData = unpack.GetData(sizeof(long)); + if(!pData) + continue; + ll = *(long *)pData; + StrPrintf(data, arrsize(data), L"%lld", ll); + length += StrLen(data) + 1; + } + break; + + case kLogParamUuid: { + Uuid uuid = 0; + pData = unpack.GetData(sizeof(Uuid)); + if(!pData) + continue; + uuid = *(Uuid *)pData; + StrPrintf(data, arrsize(data), L"%s", uuid.data); + length += StrLen(data) + 1; + } + break; + + case kLogParamStringW: { + const wchar *str = unpack.GetString(); + if(!str) { + continue; + } + length += StrLen(str) + 1; + } + break; + } + length += StrLen(event->fields[i].name) + 1; // this must happen after the parameter check so we can opt out of saving a non existant parameter + } + + databuf.Reserve(length + 1); + + { + CSrvUnpackBuffer unpack(&msg, msg.messageBytes); + (void)unpack.GetData(sizeof(msg)); + for(unsigned i = 0; i < event->numFields; ++i){ + + // the parameter name needs to be written before the data. Also, we need to be able to opt out of writing a parameter. + switch(event->fields[i].type) { + case kLogParamInt: { + pData = unpack.GetData(sizeof(int)); + if(!pData) + continue; + + int val = *(int *)pData; + + // log event name + databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); + databuf.Add(0); + + // log event data + StrPrintf(data, arrsize(data), L"%d", val); + databuf.Add(data, StrLen(data)); + databuf.Add(0); + } + break; + + case kLogParamUnsigned: { + pData = unpack.GetData(sizeof(unsigned)); + if(!pData) + continue; + + unsigned u = *(unsigned *)pData; + + // log event name + databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); + databuf.Add(0); + + // log event data + StrPrintf(data, arrsize(data), L"%d", u); + databuf.Add(data, StrLen(data)); + databuf.Add(0); + } + break; + + case kLogParamFloat: { + pData = unpack.GetData(sizeof(float)); + if(!pData) + continue; + + float f = *(float *)pData; + + // log event name + databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); + databuf.Add(0); + + // log event data + StrPrintf(data, arrsize(data), L"%f", f); + databuf.Add(data, StrLen(data)); + databuf.Add(0); + } + break; + + case kLogParamLong: { + pData = unpack.GetData(sizeof(long)); + if(!pData) + continue; + + long l = *(long *)pData; + + // log event name + databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); + databuf.Add(0); + + // log event data + StrPrintf(data, arrsize(data), L"%ld", l); + databuf.Add(data, StrLen(data)); + databuf.Add(0); + } + break; + + case kLogParamLongLong: { + pData = unpack.GetData(sizeof(long long)); + if(!pData) + continue; + + long long ll = *(long long *)pData; + + // log event name + databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); + databuf.Add(0); + + // log event data + StrPrintf(data, arrsize(data), L"%lld", ll); + databuf.Add(data, StrLen(data)); + databuf.Add(0); + } + break; + + case kLogParamUuid: { + pData = unpack.GetData(sizeof(Uuid)); + if(!pData) + continue; + + Uuid uuid = *(Uuid *)pData; + + // log event name + databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); + databuf.Add(0); + + // log event data + GuidToString(uuid, data, arrsize(data)); + databuf.Add(data, StrLen(data)); + databuf.Add(0); + } + break; + + case kLogParamStringW: { + const wchar *str = unpack.GetString(); + if(!str) { + continue; + } + + // log event name + databuf.Add(event->fields[i].name, StrLen(event->fields[i].name)); + databuf.Add(0); + + // log event data + databuf.Add(str, StrLen(str)); + databuf.Add(0); + } + break; + } + } + } + databuf.Add(0); + + if(NetLogSrvCallback) { + NetLogSrvCallback( + event, + databuf, + buildId, + addr, + msg.timestamp, + productId, + buildType + ); + } + } + else + { + LogMsg(kLogError, "Unable to log event - event not found. type: %d from server: %d. If it is a new event the log server needs to be updated.", msg.eventType, srvType); + } + + return true; +} + + +/***************************************************************************** +* +* Module exports +* +***/ + +//============================================================================ +void NetLogSrvInitialize () { + s_running = true; + AsyncSocketRegisterNotifyProc( + kConnTypeSrvToLog, + SocketNotifyProc, + 0, // Accept all buildIds + 0, // Accept all buildTypes + 0, // Accept all branchIds + 0 // Accept all product Ids + ); + IniChangeAdd(L"Log", IniChangeCallback, &s_change); +} + +//============================================================================ +void NetLogSrvShutdown () { + if(s_change) { + IniChangeRemove(s_change, true); + s_change = false; + } + + s_running = false; + AsyncSocketUnregisterNotifyProc( + kConnTypeSrvToLog, + SocketNotifyProc, + 0, // Accept all buildIds + 0, + 0, // Accept all branchIds + 0 + ); +} + +//============================================================================ +void NetLogSrvDestroy () { + + while(s_perf[kNlSrvPerfConnCount]) + AsyncSleep(10); +} + +//============================================================================ +void NetLogSrvRegisterCallback( void (*NlSrvCallback)(const NetLogEvent *, const ARRAY(wchar) &, unsigned, NetAddressNode &, qword, unsigned, unsigned )) { + NetLogSrvCallback = NlSrvCallback; +} + +//============================================================================ +void LogConnIncRef (LogConn * conn) { + conn->IncRef(); +} + +//============================================================================ +void LogConnDecRef (LogConn * conn) { + conn->DecRef(); +} + + +/***************************************************************************** +* +* Public exports +* +***/ + +//============================================================================ +long NetLogSrvGetPerf (unsigned index) { + ASSERT(index < kNlSrvNumPerf); + return s_perf[index]; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Intern.h new file mode 100644 index 00000000..513a3d20 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Intern.h @@ -0,0 +1,36 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_INTERN_H + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Pch.h new file mode 100644 index 00000000..8e02ae00 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Pch.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PCH_H + + +#include "pnUtils/pnUtils.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" +#include "pnNetCli/pnNetCli.h" + +#ifdef SERVER +#include "pnIni/pnIni.h" +#include "../ServerLib/psUtils/psUtils.h" // for SrvMsgHeader, ugh +#endif + +#include "Private/pnNpAllIncludes.h" +#include "Intern.h" + + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Auth/pnNpCli2Auth.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Auth/pnNpCli2Auth.cpp new file mode 100644 index 00000000..4499bba6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Auth/pnNpCli2Auth.cpp @@ -0,0 +1,705 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/pnNpCli2Auth.cpp +* +***/ + +#define USES_PROTOCOL_CLI2AUTH +#include "../../../Pch.h" +#pragma hdrstop + + +namespace Cli2Auth { +/***************************************************************************** +* +* Cli2Auth message definitions +* +***/ + +static const NetMsgField kPingRequestFields[] = { + kNetMsgFieldTimeMs, // pingTimeMs + NET_MSG_FIELD_DWORD(), // transId + NET_MSG_FIELD_VAR_COUNT(1, 64 * 1024), // payloadBytes + NET_MSG_FIELD_VAR_PTR(), // payload +}; + +static const NetMsgField kClientRegisterRequestFields[] = { + kNetMsgFieldBuildId, // buildId +}; + +static const NetMsgField kAccountExistsRequestFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldAccountName, // accountName +}; + +static const NetMsgField kAcctLoginRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // clientChallenge + kNetMsgFieldAccountName, // accountName + kNetMsgFieldShaDigest, // challenge + NET_MSG_FIELD_STRING(kMaxPublisherAuthKeyLength), // authToken + NET_MSG_FIELD_STRING(kMaxGTOSIdLength), // os +}; + +static const NetMsgField kAgeRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(kMaxAgeNameLength), // ageName + kNetMsgFieldUuid, // ageInstId +}; + +static const NetMsgField kAcctCreateRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(kMaxAccountNameLength), // accountName + kNetMsgFieldShaDigest, // namePassHash + NET_MSG_FIELD_DWORD(), // accountFlags + NET_MSG_FIELD_DWORD(), // billingType +}; + +static const NetMsgField kAcctCreateFromKeyRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(kMaxAccountNameLength), // accountName + kNetMsgFieldShaDigest, // namePassHash + kNetMsgFieldUuid, // key + NET_MSG_FIELD_DWORD(), // billingType +}; + +static const NetMsgField kPlayerCreateRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(kMaxPlayerNameLength), // playerName + NET_MSG_FIELD_STRING(MAX_PATH), // avatarShape + NET_MSG_FIELD_STRING(MAX_PATH), // friendInvite +}; + +static const NetMsgField kPlayerDeleteRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // playerInt +}; + +static const NetMsgField kUpgradeVisitorRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // playerInt +}; + +static const NetMsgField kAcctSetPlayerRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // playerInt +}; + +static const NetMsgField kAcctChangePasswordRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(kMaxAccountNameLength), // accountName + kNetMsgFieldShaDigest, // namePassHash +}; + +static const NetMsgField kAcctSetRolesRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(kMaxAccountNameLength), // accountName + NET_MSG_FIELD_DWORD(), // accountFlags +}; + +static const NetMsgField kAcctSetBillingTypeRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(kMaxAccountNameLength), // accountName + NET_MSG_FIELD_DWORD(), // billingType +}; + +static const NetMsgField kAcctActivateRequestFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldUuid, // activationKey +}; + +static const NetMsgField kFileListRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(MAX_PATH), // directory + NET_MSG_FIELD_STRING(MAX_EXT), // ext +}; + +static const NetMsgField kFileDownloadRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(MAX_PATH), // filename +}; + +static const NetMsgField kFileDownloadChunkAckFields[] = { + kNetMsgFieldTransId, // transId +}; + +static const NetMsgField kVaultFetchNodeRefsFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // nodeId +}; + +static const NetMsgField kVaultNodeAddFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // parentId + NET_MSG_FIELD_DWORD(), // childId + NET_MSG_FIELD_DWORD(), // ownerId +}; + +static const NetMsgField kVaultNodeRemoveFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // parentId + NET_MSG_FIELD_DWORD(), // childId +}; + +static const NetMsgField kVaultNodeSaveFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // nodeId + kNetMsgFieldUuid, // revId + NET_MSG_FIELD_VAR_COUNT(1, 1024 * 1024), // nodeBytes + NET_MSG_FIELD_VAR_PTR(), // nodeBuffer +}; + +static const NetMsgField kVaultNodeCreateFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_VAR_COUNT(1, 1024 * 1024), // nodeBytes + NET_MSG_FIELD_VAR_PTR(), // nodeBuffer +}; + +static const NetMsgField kVaultNodeFetchFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // nodeId +}; + +static const NetMsgField kVaultInitAgeRequestFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldUuid, // ageInstId + kNetMsgFieldUuid, // parentAgeInstId + NET_MSG_FIELD_STRING(MAX_PATH), // ageFilename + NET_MSG_FIELD_STRING(MAX_PATH), // ageInstName + NET_MSG_FIELD_STRING(MAX_PATH), // ageUserName + NET_MSG_FIELD_STRING(1024), // ageDesc + NET_MSG_FIELD_DWORD(), // ageSequenceNumber + NET_MSG_FIELD_DWORD(), // ageLanguage +}; + +static const NetMsgField kVaultNodeFindFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_VAR_COUNT(1, 1024 * 1024), // nodeBytes + NET_MSG_FIELD_VAR_PTR(), // nodeBuffer +}; + +static const NetMsgField kVaultSetSeenFields[] = { + NET_MSG_FIELD_DWORD(), // parentId + NET_MSG_FIELD_DWORD(), // childId + NET_MSG_FIELD_BYTE(), // seen +}; + +static const NetMsgField kVaultSendNodeFields[] = { + NET_MSG_FIELD_DWORD(), // srcNodeId + NET_MSG_FIELD_DWORD(), // dstPlayerId +}; + +static const NetMsgField kGetPublicAgeListFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(kMaxAgeNameLength), // ageFilename +}; + +static const NetMsgField kSetAgePublicFields[] = { + NET_MSG_FIELD_DWORD(), // ageInfoId + NET_MSG_FIELD_BYTE(), // publicOrNot +}; + +static const NetMsgField kPropagateBufferFields[] = { + NET_MSG_FIELD_DWORD(), // type + NET_MSG_FIELD_VAR_COUNT(1, 1024 * 1024), // bytes + NET_MSG_FIELD_VAR_PTR(), // buffer +}; + +static const NetMsgField kClientSetCCRLevelFields[] = { + NET_MSG_FIELD_DWORD(), // ccrLevel +}; + +static const NetMsgField kLogPythonTracebackFields[] = { + NET_MSG_FIELD_STRING(1024), // traceback text +}; + +static const NetMsgField kLogStackDumpFields[] = { + NET_MSG_FIELD_STRING(1024), // stackdump text +}; + +static const NetMsgField kLogClientDebuggerConnectFields[] = { + NET_MSG_FIELD_DWORD(), // nothing +}; + +static const NetMsgField kSetPlayerBanStatusRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // playerId + NET_MSG_FIELD_DWORD(), // banned +}; + +static const NetMsgField kKickPlayerFields[] = { + NET_MSG_FIELD_DWORD(), // playerId +}; + +static const NetMsgField kChangePlayerNameRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // playerId + NET_MSG_FIELD_STRING(kMaxPlayerNameLength), // newName +}; + +static const NetMsgField kSendFriendInviteRequestFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldUuid, // inviteUuid + NET_MSG_FIELD_STRING(kMaxEmailAddressLength), // emailAddress + NET_MSG_FIELD_STRING(kMaxPlayerNameLength), // toPlayer +}; + +static const NetMsgField kScoreCreateFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // ownerId + NET_MSG_FIELD_STRING(kMaxGameScoreNameLength), // gameName + NET_MSG_FIELD_DWORD(), // gameType + NET_MSG_FIELD_DWORD(), // value +}; + +static const NetMsgField kScoreDeleteFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // scoreId +}; + +static const NetMsgField kScoreGetScoresFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // ownerId + NET_MSG_FIELD_STRING(kMaxGameScoreNameLength), // gameName +}; + +static const NetMsgField kScoreAddPointsFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // scoreId + NET_MSG_FIELD_DWORD(), // numPoints +}; + +static const NetMsgField kScoreTransferPointsFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // srcScoreId + NET_MSG_FIELD_DWORD(), // destScoreId + NET_MSG_FIELD_DWORD(), // numPoints +}; + +static const NetMsgField kScoreSetPointsFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // scoreId + NET_MSG_FIELD_DWORD(), // numPoints +}; + +static const NetMsgField kScoreGetRanksFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // ownerId + NET_MSG_FIELD_DWORD(), // scoreGroup + NET_MSG_FIELD_DWORD(), // parentFolderId + NET_MSG_FIELD_STRING(kMaxGameScoreNameLength), // gameName + NET_MSG_FIELD_DWORD(), // timePeriod + NET_MSG_FIELD_DWORD(), // numResults + NET_MSG_FIELD_DWORD(), // pageNumber + NET_MSG_FIELD_DWORD(), // sortDesc +}; + + +/***************************************************************************** +* +* Auth2Cli message fields +* +***/ + +static const NetMsgField kPingReplyFields[] = { + kNetMsgFieldTimeMs, // pingTimeMs + NET_MSG_FIELD_DWORD(), // transId + NET_MSG_FIELD_VAR_COUNT(1, 64 * 1024), // payloadBytes + NET_MSG_FIELD_VAR_PTR(), // payload +}; + +static const NetMsgField kClientRegisterReplyFields[] = { + NET_MSG_FIELD_DWORD(), // serverChallenge +}; + +static const NetMsgField kAccountExistsReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_BYTE(), // account exists +}; + +static const NetMsgField kServerAddrFields[] = { + kNetMsgFieldNetNode, // srvAddr + kNetMsgFieldUuid, // token +}; + +static const NetMsgField kNotifyNewBuildFields[] = { + NET_MSG_FIELD_DWORD(), // foo +}; + +static const NetMsgField kAcctPlayerInfoFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // playerInt + NET_MSG_FIELD_STRING(kMaxPlayerNameLength), // playerName + NET_MSG_FIELD_STRING(kMaxVaultNodeStringLength), // avatarShape + NET_MSG_FIELD_DWORD(), // explorer +}; + +static const NetMsgField kAcctLoginReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + kNetMsgFieldUuid, // accountId + NET_MSG_FIELD_DWORD(), // accountFlags + NET_MSG_FIELD_DWORD(), // billingType + NET_MSG_FIELD_DWORD_ARRAY(4), // encryptionKey +}; + +static const NetMsgField kAgeReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_DWORD(), // ageMcpId + kNetMsgFieldUuid, // ageInstId + NET_MSG_FIELD_DWORD(), // ageVaultId + kNetMsgFieldNetNode, // gameSrvNode +}; + +static const NetMsgField kAcctCreateReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + kNetMsgFieldUuid // accountId +}; + +static const NetMsgField kAcctCreateFromKeyReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + kNetMsgFieldUuid, // accountId + kNetMsgFieldUuid // activationKey +}; + +static const NetMsgField kPlayerCreateReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_DWORD(), // playerInt + NET_MSG_FIELD_DWORD(), // explorer + NET_MSG_FIELD_STRING(kMaxPlayerNameLength), // playerName + NET_MSG_FIELD_STRING(kMaxVaultNodeStringLength), // avatarShape +}; + +static const NetMsgField kPlayerDeleteReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kUpgradeVisitorReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kAcctSetPlayerReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kAcctChangePasswordReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kAcctSetRolesReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kAcctSetBillingTypeReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kAcctActivateReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kFileListReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_VAR_COUNT(sizeof(wchar), 1024 * 1024), // wcharCount + NET_MSG_FIELD_VAR_PTR(), // fileData +}; + +static const NetMsgField kFileDownloadChunkFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_DWORD(), // totalFileSize + NET_MSG_FIELD_DWORD(), // chunkOffset + NET_MSG_FIELD_VAR_COUNT(1, kMaxTcpPacketSize), // chunkSize + NET_MSG_FIELD_VAR_PTR(), // chunkData +}; + +static const NetMsgField kKickedOffFields[] = { + kNetMsgFieldENetError, // reason +}; + +static const NetMsgField kVaultNodeFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_VAR_COUNT(1, 1024 * 1024), // nodeBytes + NET_MSG_FIELD_VAR_PTR(), // nodeBuffer +}; + +static const NetMsgField kVaultNodeRefsFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_VAR_COUNT(sizeof(NetVaultNodeRef), 1024 * 1024), // refCount + NET_MSG_FIELD_VAR_PTR(), // refs +}; + +static const NetMsgField kVaultNodeCreatedFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_DWORD(), // nodeId +}; + +static const NetMsgField kVaultNodeRefsFetchedFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_VAR_COUNT(sizeof(NetVaultNodeRef), 1024 * 1024), // refCount + NET_MSG_FIELD_VAR_PTR(), // refs +}; + +static const NetMsgField kVaultNodeFetchedFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_VAR_COUNT(1, 1024 * 1024), // nodeBytes + NET_MSG_FIELD_VAR_PTR(), // nodeBuffer +}; + +static const NetMsgField kVaultNodeChangedFields[] = { + NET_MSG_FIELD_DWORD(), // nodeId + kNetMsgFieldUuid, // revisionId +}; + +static const NetMsgField kVaultNodeAddedFields[] = { + NET_MSG_FIELD_DWORD(), // parentId + NET_MSG_FIELD_DWORD(), // childId + NET_MSG_FIELD_DWORD(), // ownerId +}; + +static const NetMsgField kVaultNodeRemovedFields[] = { + NET_MSG_FIELD_DWORD(), // parentId + NET_MSG_FIELD_DWORD(), // childId +}; + +static const NetMsgField kVaultNodeDeletedFields[] = { + NET_MSG_FIELD_DWORD(), // nodeId +}; + +static const NetMsgField kVaultSaveNodeReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kVaultAddNodeReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kVaultRemoveNodeReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kVaultInitAgeReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_DWORD(), // ageVaultId + NET_MSG_FIELD_DWORD(), // ageInfoVaultId +}; + +static const NetMsgField kVaultNodeFindReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_VAR_COUNT(sizeof(dword), 512), // nodeIdCount + NET_MSG_FIELD_VAR_PTR(), // nodeIds +}; + +static const NetMsgField kPublicAgeListFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_VAR_COUNT(sizeof(NetAgeInfo), 512), // ageCount + NET_MSG_FIELD_VAR_PTR(), // ages +}; + +static const NetMsgField kSetPlayerBanStatusReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kChangePlayerNameReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kSendFriendInviteReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kScoreCreateReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_DWORD(), // scoreId + NET_MSG_FIELD_DWORD(), // createdTime +}; + +static const NetMsgField kScoreDeleteReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kScoreGetScoresReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_DWORD(), // scoreCount + NET_MSG_FIELD_VAR_COUNT(1, 1024 * 1024), // nodeBytes + NET_MSG_FIELD_VAR_PTR(), // nodeBuffer +}; + +static const NetMsgField kScoreAddPointsReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kScoreTransferPointsReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kScoreSetPointsReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +static const NetMsgField kScoreGetRanksReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + NET_MSG_FIELD_DWORD(), // rankCount + NET_MSG_FIELD_VAR_COUNT(1, 1024 * 1024), // nodeBytes + NET_MSG_FIELD_VAR_PTR(), // nodeBuffer +}; + +} using namespace Cli2Auth; + + +/***************************************************************************** +* +* Exported data +* +***/ + + +const NetMsg kNetMsg_Cli2Auth_PingRequest = NET_MSG(kCli2Auth_PingRequest, kPingRequestFields); +const NetMsg kNetMsg_Cli2Auth_ClientRegisterRequest = NET_MSG(kCli2Auth_ClientRegisterRequest, kClientRegisterRequestFields); +const NetMsg kNetMsg_Cli2Auth_AccountExistsRequest = NET_MSG(kCli2Auth_AccountExistsRequest, kAccountExistsRequestFields); +const NetMsg kNetMsg_Cli2Auth_AcctLoginRequest = NET_MSG(kCli2Auth_AcctLoginRequest, kAcctLoginRequestFields); +const NetMsg kNetMsg_Cli2Auth_AcctCreateRequest = NET_MSG(kCli2Auth_AcctCreateRequest, kAcctCreateRequestFields); +const NetMsg kNetMsg_Cli2Auth_AcctCreateFromKeyRequest = NET_MSG(kCli2Auth_AcctCreateFromKeyRequest, kAcctCreateFromKeyRequestFields); +const NetMsg kNetMsg_Cli2Auth_PlayerCreateRequest = NET_MSG(kCli2Auth_PlayerCreateRequest, kPlayerCreateRequestFields); +const NetMsg kNetMsg_Cli2Auth_PlayerDeleteRequest = NET_MSG(kCli2Auth_PlayerDeleteRequest, kPlayerDeleteRequestFields); +const NetMsg kNetMsg_Cli2Auth_UpgradeVisitorRequest = NET_MSG(kCli2Auth_UpgradeVisitorRequest, kUpgradeVisitorRequestFields); +const NetMsg kNetMsg_Cli2Auth_AcctSetPlayerRequest = NET_MSG(kCli2Auth_AcctSetPlayerRequest, kAcctSetPlayerRequestFields); +const NetMsg kNetMsg_Cli2Auth_AcctChangePasswordRequest = NET_MSG(kCli2Auth_AcctChangePasswordRequest, kAcctChangePasswordRequestFields); +const NetMsg kNetMsg_Cli2Auth_AcctSetRolesRequest = NET_MSG(kCli2Auth_AcctSetRolesRequest, kAcctSetRolesRequestFields); +const NetMsg kNetMsg_Cli2Auth_AcctSetBillingTypeRequest = NET_MSG(kCli2Auth_AcctSetBillingTypeRequest, kAcctSetBillingTypeRequestFields); +const NetMsg kNetMsg_Cli2Auth_AcctActivateRequest = NET_MSG(kCli2Auth_AcctActivateRequest, kAcctActivateRequestFields); +const NetMsg kNetMsg_Cli2Auth_AgeRequest = NET_MSG(kCli2Auth_AgeRequest, kAgeRequestFields); +const NetMsg kNetMsg_Cli2Auth_FileListRequest = NET_MSG(kCli2Auth_FileListRequest, kFileListRequestFields); +const NetMsg kNetMsg_Cli2Auth_FileDownloadRequest = NET_MSG(kCli2Auth_FileDownloadRequest, kFileDownloadRequestFields); +const NetMsg kNetMsg_Cli2Auth_FileDownloadChunkAck = NET_MSG(kCli2Auth_FileDownloadChunkAck, kFileDownloadChunkAckFields); +const NetMsg kNetMsg_Cli2Auth_VaultFetchNodeRefs = NET_MSG(kCli2Auth_VaultFetchNodeRefs, kVaultFetchNodeRefsFields); +const NetMsg kNetMsg_Cli2Auth_VaultNodeAdd = NET_MSG(kCli2Auth_VaultNodeAdd, kVaultNodeAddFields); +const NetMsg kNetMsg_Cli2Auth_VaultNodeRemove = NET_MSG(kCli2Auth_VaultNodeRemove, kVaultNodeRemoveFields); +const NetMsg kNetMsg_Cli2Auth_VaultNodeCreate = NET_MSG(kCli2Auth_VaultNodeCreate, kVaultNodeCreateFields); +const NetMsg kNetMsg_Cli2Auth_VaultNodeSave = NET_MSG(kCli2Auth_VaultNodeSave, kVaultNodeSaveFields); +const NetMsg kNetMsg_Cli2Auth_VaultNodeFetch = NET_MSG(kCli2Auth_VaultNodeFetch, kVaultNodeFetchFields); +const NetMsg kNetMsg_Cli2Auth_VaultInitAgeRequest = NET_MSG(kCli2Auth_VaultInitAgeRequest, kVaultInitAgeRequestFields); +const NetMsg kNetMsg_Cli2Auth_VaultNodeFind = NET_MSG(kCli2Auth_VaultNodeFind, kVaultNodeFindFields); +const NetMsg kNetMsg_Cli2Auth_VaultSetSeen = NET_MSG(kCli2Auth_VaultSetSeen, kVaultSetSeenFields); +const NetMsg kNetMsg_Cli2Auth_VaultSendNode = NET_MSG(kCli2Auth_VaultSendNode, kVaultSendNodeFields); +const NetMsg kNetMsg_Cli2Auth_GetPublicAgeList = NET_MSG(kCli2Auth_GetPublicAgeList, kGetPublicAgeListFields); +const NetMsg kNetMsg_Cli2Auth_SetAgePublic = NET_MSG(kCli2Auth_SetAgePublic, kSetAgePublicFields); +const NetMsg kNetMsg_Cli2Auth_PropagateBuffer = NET_MSG(kCli2Auth_PropagateBuffer, kPropagateBufferFields); +const NetMsg kNetMsg_Cli2Auth_ClientSetCCRLevel = NET_MSG(kCli2Auth_ClientSetCCRLevel, kClientSetCCRLevelFields); +const NetMsg kNetMsg_Cli2Auth_LogPythonTraceback = NET_MSG(kCli2Auth_LogPythonTraceback, kLogPythonTracebackFields); +const NetMsg kNetMsg_Cli2Auth_LogStackDump = NET_MSG(kCli2Auth_LogStackDump, kLogStackDumpFields); +const NetMsg kNetMsg_Cli2Auth_LogClientDebuggerConnect = NET_MSG(kCli2Auth_LogClientDebuggerConnect, kLogClientDebuggerConnectFields); +const NetMsg kNetMsg_Cli2Auth_SetPlayerBanStatusRequest = NET_MSG(kCli2Auth_SetPlayerBanStatusRequest, kSetPlayerBanStatusRequestFields); +const NetMsg kNetMsg_Cli2Auth_KickPlayer = NET_MSG(kCli2Auth_KickPlayer, kKickPlayerFields); +const NetMsg kNetMsg_Cli2Auth_ChangePlayerNameRequest = NET_MSG(kCli2Auth_ChangePlayerNameRequest, kChangePlayerNameRequestFields); +const NetMsg kNetMsg_Cli2Auth_SendFriendInviteRequest = NET_MSG(kCli2Auth_SendFriendInviteRequest, kSendFriendInviteRequestFields); +const NetMsg kNetMsg_Cli2Auth_ScoreCreate = NET_MSG(kCli2Auth_ScoreCreate, kScoreCreateFields); +const NetMsg kNetMsg_Cli2Auth_ScoreDelete = NET_MSG(kCli2Auth_ScoreDelete, kScoreDeleteFields); +const NetMsg kNetMsg_Cli2Auth_ScoreGetScores = NET_MSG(kCli2Auth_ScoreGetScores, kScoreGetScoresFields); +const NetMsg kNetMsg_Cli2Auth_ScoreAddPoints = NET_MSG(kCli2Auth_ScoreAddPoints, kScoreAddPointsFields); +const NetMsg kNetMsg_Cli2Auth_ScoreTransferPoints = NET_MSG(kCli2Auth_ScoreTransferPoints, kScoreTransferPointsFields); +const NetMsg kNetMsg_Cli2Auth_ScoreSetPoints = NET_MSG(kCli2Auth_ScoreSetPoints, kScoreSetPointsFields); +const NetMsg kNetMsg_Cli2Auth_ScoreGetRanks = NET_MSG(kCli2Auth_ScoreGetRanks, kScoreGetRanksFields); + +const NetMsg kNetMsg_Auth2Cli_PingReply = NET_MSG(kAuth2Cli_PingReply, kPingReplyFields); +const NetMsg kNetMsg_Auth2Cli_ClientRegisterReply = NET_MSG(kAuth2Cli_ClientRegisterReply, kClientRegisterReplyFields); +const NetMsg kNetMsg_Auth2Cli_AccountExistsReply = NET_MSG(kAuth2Cli_AccountExistsReply, kAccountExistsReplyFields); +const NetMsg kNetMsg_Auth2Cli_ServerAddr = NET_MSG(kAuth2Cli_ServerAddr, kServerAddrFields); +const NetMsg kNetMsg_Auth2Cli_NotifyNewBuild = NET_MSG(kAuth2Cli_NotifyNewBuild, kNotifyNewBuildFields); +const NetMsg kNetMsg_Auth2Cli_AcctPlayerInfo = NET_MSG(kAuth2Cli_AcctPlayerInfo, kAcctPlayerInfoFields); +const NetMsg kNetMsg_Auth2Cli_AcctLoginReply = NET_MSG(kAuth2Cli_AcctLoginReply, kAcctLoginReplyFields); +const NetMsg kNetMsg_Auth2Cli_AcctCreateReply = NET_MSG(kAuth2Cli_AcctCreateReply, kAcctCreateReplyFields); +const NetMsg kNetMsg_Auth2Cli_AcctCreateFromKeyReply = NET_MSG(kAuth2Cli_AcctCreateFromKeyReply, kAcctCreateFromKeyReplyFields); +const NetMsg kNetMsg_Auth2Cli_PlayerCreateReply = NET_MSG(kAuth2Cli_PlayerCreateReply, kPlayerCreateReplyFields); +const NetMsg kNetMsg_Auth2Cli_PlayerDeleteReply = NET_MSG(kAuth2Cli_PlayerDeleteReply, kPlayerDeleteReplyFields); +const NetMsg kNetMsg_Auth2Cli_UpgradeVisitorReply = NET_MSG(kAuth2Cli_UpgradeVisitorReply, kUpgradeVisitorReplyFields); +const NetMsg kNetMsg_Auth2Cli_AcctSetPlayerReply = NET_MSG(kAuth2Cli_AcctSetPlayerReply, kAcctSetPlayerReplyFields); +const NetMsg kNetMsg_Auth2Cli_AcctChangePasswordReply = NET_MSG(kAuth2Cli_AcctChangePasswordReply, kAcctChangePasswordReplyFields); +const NetMsg kNetMsg_Auth2Cli_AcctSetRolesReply = NET_MSG(kAuth2Cli_AcctSetRolesReply, kAcctSetRolesReplyFields); +const NetMsg kNetMsg_Auth2Cli_AcctSetBillingTypeReply = NET_MSG(kAuth2Cli_AcctSetBillingTypeReply, kAcctSetBillingTypeReplyFields); +const NetMsg kNetMsg_Auth2Cli_AcctActivateReply = NET_MSG(kAuth2Cli_AcctActivateReply, kAcctActivateReplyFields); +const NetMsg kNetMsg_Auth2Cli_AgeReply = NET_MSG(kAuth2Cli_AgeReply, kAgeReplyFields); +const NetMsg kNetMsg_Auth2Cli_FileListReply = NET_MSG(kAuth2Cli_FileListReply, kFileListReplyFields); +const NetMsg kNetMsg_Auth2Cli_FileDownloadChunk = NET_MSG(kAuth2Cli_FileDownloadChunk, kFileDownloadChunkFields); +const NetMsg kNetMsg_Auth2Cli_KickedOff = NET_MSG(kAuth2Cli_KickedOff, kKickedOffFields); +const NetMsg kNetMsg_Auth2Cli_VaultNodeRefsFetched = NET_MSG(kAuth2Cli_VaultNodeRefsFetched, kVaultNodeRefsFetchedFields); +const NetMsg kNetMsg_Auth2Cli_VaultNodeCreated = NET_MSG(kAuth2Cli_VaultNodeCreated, kVaultNodeCreatedFields); +const NetMsg kNetMsg_Auth2Cli_VaultNodeFetched = NET_MSG(kAuth2Cli_VaultNodeFetched, kVaultNodeFetchedFields); +const NetMsg kNetMsg_Auth2Cli_VaultNodeChanged = NET_MSG(kAuth2Cli_VaultNodeChanged, kVaultNodeChangedFields); +const NetMsg kNetMsg_Auth2Cli_VaultNodeAdded = NET_MSG(kAuth2Cli_VaultNodeAdded, kVaultNodeAddedFields); +const NetMsg kNetMsg_Auth2Cli_VaultNodeRemoved = NET_MSG(kAuth2Cli_VaultNodeRemoved, kVaultNodeRemovedFields); +const NetMsg kNetMsg_Auth2Cli_VaultNodeDeleted = NET_MSG(kAuth2Cli_VaultNodeDeleted, kVaultNodeDeletedFields); +const NetMsg kNetMsg_Auth2Cli_VaultSaveNodeReply = NET_MSG(kAuth2Cli_VaultSaveNodeReply, kVaultSaveNodeReplyFields); +const NetMsg kNetMsg_Auth2Cli_VaultAddNodeReply = NET_MSG(kAuth2Cli_VaultAddNodeReply, kVaultAddNodeReplyFields); +const NetMsg kNetMsg_Auth2Cli_VaultRemoveNodeReply = NET_MSG(kAuth2Cli_VaultRemoveNodeReply, kVaultRemoveNodeReplyFields); +const NetMsg kNetMsg_Auth2Cli_VaultInitAgeReply = NET_MSG(kAuth2Cli_VaultInitAgeReply, kVaultInitAgeReplyFields); +const NetMsg kNetMsg_Auth2Cli_VaultNodeFindReply = NET_MSG(kAuth2Cli_VaultNodeFindReply, kVaultNodeFindReplyFields); +const NetMsg kNetMsg_Auth2Cli_PublicAgeList = NET_MSG(kAuth2Cli_PublicAgeList, kPublicAgeListFields); +const NetMsg kNetMsg_Auth2Cli_PropagateBuffer = NET_MSG(kAuth2Cli_PropagateBuffer, kPropagateBufferFields); +const NetMsg kNetMsg_Auth2Cli_SetPlayerBanStatusReply = NET_MSG(kAuth2Cli_SetPlayerBanStatusReply, kSetPlayerBanStatusReplyFields); +const NetMsg kNetMsg_Auth2Cli_ChangePlayerNameReply = NET_MSG(kAuth2Cli_ChangePlayerNameReply, kChangePlayerNameReplyFields); +const NetMsg kNetMsg_Auth2Cli_SendFriendInviteReply = NET_MSG(kAuth2Cli_SendFriendInviteReply, kSendFriendInviteReplyFields); +const NetMsg kNetMsg_Auth2Cli_ScoreCreateReply = NET_MSG(kAuth2Cli_ScoreCreateReply, kScoreCreateReplyFields); +const NetMsg kNetMsg_Auth2Cli_ScoreDeleteReply = NET_MSG(kAuth2Cli_ScoreDeleteReply, kScoreDeleteReplyFields); +const NetMsg kNetMsg_Auth2Cli_ScoreGetScoresReply = NET_MSG(kAuth2Cli_ScoreGetScoresReply, kScoreGetScoresReplyFields); +const NetMsg kNetMsg_Auth2Cli_ScoreAddPointsReply = NET_MSG(kAuth2Cli_ScoreAddPointsReply, kScoreAddPointsReplyFields); +const NetMsg kNetMsg_Auth2Cli_ScoreTransferPointsReply = NET_MSG(kAuth2Cli_ScoreTransferPointsReply, kScoreTransferPointsReplyFields); +const NetMsg kNetMsg_Auth2Cli_ScoreSetPointsReply = NET_MSG(kAuth2Cli_ScoreSetPointsReply, kScoreSetPointsReplyFields); +const NetMsg kNetMsg_Auth2Cli_ScoreGetRanksReply = NET_MSG(kAuth2Cli_ScoreGetRanksReply, kScoreGetRanksReplyFields); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Auth/pnNpCli2Auth.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Auth/pnNpCli2Auth.h new file mode 100644 index 00000000..154b22e9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Auth/pnNpCli2Auth.h @@ -0,0 +1,1033 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/pnNpCli2Auth.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_PNNPCLI2AUTH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/pnNpCli2Auth.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_PNNPCLI2AUTH_H + + +// kNetProtocolCli2Auth messages (must be <= (word)-1) +enum { + // Global + kCli2Auth_PingRequest, + + // Client + kCli2Auth_ClientRegisterRequest, + kCli2Auth_ClientSetCCRLevel, + + // Account + kCli2Auth_AcctLoginRequest, + kCli2Auth_AcctSetEulaVersion, + kCli2Auth_AcctSetDataRequest, + kCli2Auth_AcctSetPlayerRequest, + kCli2Auth_AcctCreateRequest, + kCli2Auth_AcctChangePasswordRequest, + kCli2Auth_AcctSetRolesRequest, + kCli2Auth_AcctSetBillingTypeRequest, + kCli2Auth_AcctActivateRequest, + kCli2Auth_AcctCreateFromKeyRequest, + + // Player + kCli2Auth_PlayerDeleteRequest, + kCli2Auth_PlayerUndeleteRequest, + kCli2Auth_PlayerSelectRequest, + kCli2Auth_PlayerRenameRequest, + kCli2Auth_PlayerCreateRequest, + kCli2Auth_PlayerSetStatus, + kCli2Auth_PlayerChat, + kCli2Auth_UpgradeVisitorRequest, + kCli2Auth_SetPlayerBanStatusRequest, + kCli2Auth_KickPlayer, + kCli2Auth_ChangePlayerNameRequest, + kCli2Auth_SendFriendInviteRequest, + + // Vault + kCli2Auth_VaultNodeCreate, + kCli2Auth_VaultNodeFetch, + kCli2Auth_VaultNodeSave, + kCli2Auth_VaultNodeDelete, + kCli2Auth_VaultNodeAdd, + kCli2Auth_VaultNodeRemove, + kCli2Auth_VaultFetchNodeRefs, + kCli2Auth_VaultInitAgeRequest, + kCli2Auth_VaultNodeFind, + kCli2Auth_VaultSetSeen, + kCli2Auth_VaultSendNode, + + // Ages + kCli2Auth_AgeRequest, + + // File-related + kCli2Auth_FileListRequest, + kCli2Auth_FileDownloadRequest, + kCli2Auth_FileDownloadChunkAck, + + // Game + kCli2Auth_PropagateBuffer, + + + // Public ages + kCli2Auth_GetPublicAgeList, + kCli2Auth_SetAgePublic, + + // Log Messages + kCli2Auth_LogPythonTraceback, + kCli2Auth_LogStackDump, + kCli2Auth_LogClientDebuggerConnect, + + // Score + kCli2Auth_ScoreCreate, + kCli2Auth_ScoreDelete, + kCli2Auth_ScoreGetScores, + kCli2Auth_ScoreAddPoints, + kCli2Auth_ScoreTransferPoints, + kCli2Auth_ScoreSetPoints, + kCli2Auth_ScoreGetRanks, + + kCli2Auth_AccountExistsRequest, + + kNumCli2AuthMessages +}; +COMPILER_ASSERT_HEADER(Cli2Auth, kNumCli2AuthMessages <= (word)-1); + +enum { + // Global + kAuth2Cli_PingReply, + kAuth2Cli_ServerAddr, + kAuth2Cli_NotifyNewBuild, + + // Client + kAuth2Cli_ClientRegisterReply, + + // Account + kAuth2Cli_AcctLoginReply, + kAuth2Cli_AcctData, + kAuth2Cli_AcctPlayerInfo, + kAuth2Cli_AcctSetPlayerReply, + kAuth2Cli_AcctCreateReply, + kAuth2Cli_AcctChangePasswordReply, + kAuth2Cli_AcctSetRolesReply, + kAuth2Cli_AcctSetBillingTypeReply, + kAuth2Cli_AcctActivateReply, + kAuth2Cli_AcctCreateFromKeyReply, + + // Player + kAuth2Cli_PlayerList, + kAuth2Cli_PlayerChat, + kAuth2Cli_PlayerCreateReply, + kAuth2Cli_PlayerDeleteReply, + kAuth2Cli_UpgradeVisitorReply, + kAuth2Cli_SetPlayerBanStatusReply, + kAuth2Cli_ChangePlayerNameReply, + kAuth2Cli_SendFriendInviteReply, + + // Friends + kAuth2Cli_FriendNotify, + + // Vault + kAuth2Cli_VaultNodeCreated, + kAuth2Cli_VaultNodeFetched, + kAuth2Cli_VaultNodeChanged, + kAuth2Cli_VaultNodeDeleted, + kAuth2Cli_VaultNodeAdded, + kAuth2Cli_VaultNodeRemoved, + kAuth2Cli_VaultNodeRefsFetched, + kAuth2Cli_VaultInitAgeReply, + kAuth2Cli_VaultNodeFindReply, + kAuth2Cli_VaultSaveNodeReply, + kAuth2Cli_VaultAddNodeReply, + kAuth2Cli_VaultRemoveNodeReply, + + // Ages + kAuth2Cli_AgeReply, + + // File-related + kAuth2Cli_FileListReply, + kAuth2Cli_FileDownloadChunk, + + // Game + kAuth2Cli_PropagateBuffer, + + // Admin + kAuth2Cli_KickedOff, + + // Public ages + kAuth2Cli_PublicAgeList, + + // Score + kAuth2Cli_ScoreCreateReply, + kAuth2Cli_ScoreDeleteReply, + kAuth2Cli_ScoreGetScoresReply, + kAuth2Cli_ScoreAddPointsReply, + kAuth2Cli_ScoreTransferPointsReply, + kAuth2Cli_ScoreSetPointsReply, + kAuth2Cli_ScoreGetRanksReply, + + kAuth2Cli_AccountExistsReply, + + kNumAuth2CliMessages +}; +COMPILER_ASSERT_HEADER(Cli2Auth, kNumAuth2CliMessages <= (word)-1); + + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Cli2Auth connect packet +* +***/ + +struct Cli2Auth_ConnData { + dword dataBytes; + Uuid token; +}; +struct Cli2Auth_Connect { + AsyncSocketConnectPacket hdr; + Cli2Auth_ConnData data; +}; + + +/***************************************************************************** +* +* Cli2Auth message structures +* +***/ + +// PingRequest +extern const NetMsg kNetMsg_Cli2Auth_PingRequest; +struct Cli2Auth_PingRequest { + dword messageId; + dword pingTimeMs; + dword transId; + dword payloadBytes; + byte payload[1]; // [payloadBytes] +}; + +// ClientRegisterRequest +extern const NetMsg kNetMsg_Cli2Auth_ClientRegisterRequest; +struct Cli2Auth_ClientRegisterRequest { + dword messageId; + dword buildId; +}; + +// AccountExists +extern const NetMsg kNetMsg_Cli2Auth_AccountExistsRequest; +struct Cli2Auth_AccountExistsRequest { + dword messageId; + dword transId; + wchar accountName[kMaxAccountNameLength]; +}; + +// LoginRequest +extern const NetMsg kNetMsg_Cli2Auth_AcctLoginRequest; +struct Cli2Auth_AcctLoginRequest { + dword messageId; + dword transId; + dword clientChallenge; + wchar acctName[kMaxAccountNameLength]; + ShaDigest challengeHash; + wchar authToken[kMaxPublisherAuthKeyLength]; + wchar os[kMaxGTOSIdLength]; +}; + +// AgeRequest +extern const NetMsg kNetMsg_Cli2Auth_AgeRequest; +struct Cli2Auth_AgeRequest { + dword messageId; + dword transId; + wchar ageName[kMaxAgeNameLength]; + Uuid ageUuid; +}; + +// AcctCreateRequest +extern const NetMsg kNetMsg_Cli2Auth_AcctCreateRequest; +struct Cli2Auth_AcctCreateRequest { + dword messageId; + dword transId; + wchar accountName[kMaxAccountNameLength]; + ShaDigest namePassHash; + dword accountFlags; + dword billingType; +}; + +// AcctCreateFromKeyRequest +extern const NetMsg kNetMsg_Cli2Auth_AcctCreateFromKeyRequest; +struct Cli2Auth_AcctCreateFromKeyRequest { + dword messageId; + dword transId; + wchar accountName[kMaxAccountNameLength]; + ShaDigest namePassHash; + Uuid key; + dword billingType; +}; + +// CreatePlayerRequest +extern const NetMsg kNetMsg_Cli2Auth_PlayerCreateRequest; +struct Cli2Auth_PlayerCreateRequest { + dword messageId; + dword transId; + wchar playerName[kMaxPlayerNameLength]; + wchar avatarShape[MAX_PATH]; + wchar friendInvite[MAX_PATH]; +}; + +extern const NetMsg kNetMsg_Cli2Auth_PlayerDeleteRequest; +struct Cli2Auth_PlayerDeleteRequest { + dword messageId; + dword transId; + dword playerId; +}; + +extern const NetMsg kNetMsg_Cli2Auth_UpgradeVisitorRequest; +struct Cli2Auth_UpgradeVisitorRequest { + dword messageId; + dword transId; + dword playerId; +}; + +// SetPlayerRequest +extern const NetMsg kNetMsg_Cli2Auth_AcctSetPlayerRequest; +struct Cli2Auth_AcctSetPlayerRequest { + dword messageId; + dword transId; + dword playerInt; +}; + +// ChangePasswordRequest +extern const NetMsg kNetMsg_Cli2Auth_AcctChangePasswordRequest; +struct Cli2Auth_AcctChangePasswordRequest { + dword messageId; + dword transId; + wchar accountName[kMaxAccountNameLength]; + ShaDigest namePassHash; +}; + +// AcctSetRolesRequest +extern const NetMsg kNetMsg_Cli2Auth_AcctSetRolesRequest; +struct Cli2Auth_AcctSetRolesRequest { + dword messageId; + dword transId; + wchar accountName[kMaxAccountNameLength]; + dword accountFlags; +}; + +// AcctSetBillingTypeRequest +extern const NetMsg kNetMsg_Cli2Auth_AcctSetBillingTypeRequest; +struct Cli2Auth_AcctSetBillingTypeRequest { + dword messageId; + dword transId; + wchar accountName[kMaxAccountNameLength]; + dword billingType; +}; + +// AcctActivateRequest +extern const NetMsg kNetMsg_Cli2Auth_AcctActivateRequest; +struct Cli2Auth_AcctActivateRequest { + dword messageId; + dword transId; + Uuid activationKey; +}; + +// FileListRequest +extern const NetMsg kNetMsg_Cli2Auth_FileListRequest; +struct Cli2Auth_FileListRequest { + dword messageId; + dword transId; + wchar directory[MAX_PATH]; + wchar ext[MAX_EXT]; +}; + +// FileDownloadRequest +extern const NetMsg kNetMsg_Cli2Auth_FileDownloadRequest; +struct Cli2Auth_FileDownloadRequest { + dword messageId; + dword transId; + wchar filename[MAX_PATH]; +}; + +// FileDownloadChunkAck +extern const NetMsg kNetMsg_Cli2Auth_FileDownloadChunkAck; +struct Cli2Auth_FileDownloadChunkAck { + dword messageId; + dword transId; +}; + +// VaultFetchNodeRefs +extern const NetMsg kNetMsg_Cli2Auth_VaultFetchNodeRefs; +struct Cli2Auth_VaultFetchNodeRefs { + dword messageId; + dword transId; + dword nodeId; +}; + +// VaultNodeAdd +extern const NetMsg kNetMsg_Cli2Auth_VaultNodeAdd; +struct Cli2Auth_VaultNodeAdd { + dword messageId; + dword transId; + dword parentId; + dword childId; + dword ownerId; +}; + +// VaultNodeRemove +extern const NetMsg kNetMsg_Cli2Auth_VaultNodeRemove; +struct Cli2Auth_VaultNodeRemove { + dword messageId; + dword transId; + dword parentId; + dword childId; +}; + +// VaultNodeSave +extern const NetMsg kNetMsg_Cli2Auth_VaultNodeSave; +struct Cli2Auth_VaultNodeSave { + dword messageId; + dword transId; + dword nodeId; + Uuid revisionId; + dword nodeBytes; + byte nodeBuffer[1]; +}; + +// VaultNodeCreate +extern const NetMsg kNetMsg_Cli2Auth_VaultNodeCreate; +struct Cli2Auth_VaultNodeCreate { + dword messageId; + dword transId; + dword nodeBytes; + byte nodeBuffer[1]; +}; + +// VaultNodeFetch +extern const NetMsg kNetMsg_Cli2Auth_VaultNodeFetch; +struct Cli2Auth_VaultNodeFetch { + dword messageId; + dword transId; + dword nodeId; +}; + +// VaultInitAgeRequest +extern const NetMsg kNetMsg_Cli2Auth_VaultInitAgeRequest; +struct Cli2Auth_VaultInitAgeRequest { + dword messageId; + dword transId; + Uuid ageInstId; + Uuid parentAgeInstId; + wchar ageFilename[MAX_PATH]; + wchar ageInstName[MAX_PATH]; + wchar ageUserName[MAX_PATH]; + wchar ageDesc[1024]; + dword ageSequenceNumber; + dword ageLanguage; +}; + +// VaultNodeFind +extern const NetMsg kNetMsg_Cli2Auth_VaultNodeFind; +struct Cli2Auth_VaultNodeFind { + dword messageId; + dword transId; + dword nodeBytes; + byte nodeBuffer[1]; +}; + +// VaultSetSeen +extern const NetMsg kNetMsg_Cli2Auth_VaultSetSeen; +struct Cli2Auth_VaultSetSeen { + dword messageId; + dword parentId; + dword childId; + byte seen; +}; + +// VaultSendNode +extern const NetMsg kNetMsg_Cli2Auth_VaultSendNode; +struct Cli2Auth_VaultSendNode { + dword messageId; + dword srcNodeId; + dword dstPlayerId; +}; + +// GetPublicAgeList +extern const NetMsg kNetMsg_Cli2Auth_GetPublicAgeList; +struct Cli2Auth_GetPublicAgeList { + dword messageId; + dword transId; + wchar ageFilename[kMaxAgeNameLength]; +}; + +extern const NetMsg kNetMsg_Cli2Auth_SetAgePublic; +struct Cli2Auth_SetAgePublic { + dword messageId; + dword ageInfoId; + byte publicOrNot; +}; + +// PropagateBuffer +extern const NetMsg kNetMsg_Cli2Auth_PropagateBuffer; +struct Cli2Auth_PropagateBuffer { + dword messageId; + dword type; + dword bytes; + byte buffer[1]; // [bytes], actually + // no more fields +}; + +extern const NetMsg kNetMsg_Cli2Auth_ClientSetCCRLevel; +struct Cli2Auth_ClientSetCCRLevel { + dword messageId; + dword ccrLevel; +}; + +extern const NetMsg kNetMsg_Cli2Auth_LogPythonTraceback; +struct Cli2Auth_LogPythonTraceback { + dword messageId; + wchar traceback[1024]; +}; + +extern const NetMsg kNetMsg_Cli2Auth_LogStackDump; +struct Cli2Auth_LogStackDump { + dword messageId; + wchar stackdump[1024]; +}; + +extern const NetMsg kNetMsg_Cli2Auth_LogClientDebuggerConnect; +struct Cli2Auth_LogClientDebuggerConnect { + dword messageId; +}; + +extern const NetMsg kNetMsg_Cli2Auth_SetPlayerBanStatusRequest; +struct Cli2Auth_SetPlayerBanStatusRequest { + dword messageId; + dword transId; + dword playerId; + dword banned; +}; + +extern const NetMsg kNetMsg_Cli2Auth_KickPlayer; +struct Cli2Auth_KickPlayer { + dword messageId; + dword playerId; +}; + +extern const NetMsg kNetMsg_Cli2Auth_ChangePlayerNameRequest; +struct Cli2Auth_ChangePlayerNameRequest { + dword messageId; + dword transId; + dword playerId; + wchar newName[kMaxPlayerNameLength]; +}; + +extern const NetMsg kNetMsg_Cli2Auth_SendFriendInviteRequest; +struct Cli2Auth_SendFriendInviteRequest { + dword messageId; + dword transId; + Uuid inviteUuid; + wchar emailAddress[kMaxEmailAddressLength]; + wchar toName[kMaxPlayerNameLength]; +}; + +extern const NetMsg kNetMsg_Cli2Auth_ScoreCreate; +struct Cli2Auth_ScoreCreate { + dword messageId; + dword transId; + dword ownerId; + wchar gameName[kMaxGameScoreNameLength]; + dword gameType; + dword scoreValue; +}; + +extern const NetMsg kNetMsg_Cli2Auth_ScoreDelete; +struct Cli2Auth_ScoreDelete { + dword messageId; + dword transId; + dword scoreId; +}; + +extern const NetMsg kNetMsg_Cli2Auth_ScoreGetScores; +struct Cli2Auth_ScoreGetScores { + dword messageId; + dword transId; + dword ownerId; + wchar gameName[kMaxGameScoreNameLength]; +}; + +extern const NetMsg kNetMsg_Cli2Auth_ScoreAddPoints; +struct Cli2Auth_ScoreAddPoints { + dword messageId; + dword transId; + dword scoreId; + dword numPoints; +}; + +extern const NetMsg kNetMsg_Cli2Auth_ScoreTransferPoints; +struct Cli2Auth_ScoreTransferPoints { + dword messageId; + dword transId; + dword srcScoreId; + dword destScoreId; + dword numPoints; +}; + +extern const NetMsg kNetMsg_Cli2Auth_ScoreSetPoints; +struct Cli2Auth_ScoreSetPoints { + dword messageId; + dword transId; + dword scoreId; + dword numPoints; +}; + +extern const NetMsg kNetMsg_Cli2Auth_ScoreGetRanks; +struct Cli2Auth_ScoreGetRanks { + dword messageId; + dword transId; + dword ownerId; + dword scoreGroup; + dword parentFolderId; + wchar gameName[kMaxGameScoreNameLength]; + dword timePeriod; + dword numResults; + dword pageNumber; + dword sortDesc; +}; + + +/***************************************************************************** +* +* Auth2Cli message structures +* +***/ + +// PingReply +extern const NetMsg kNetMsg_Auth2Cli_PingReply; +struct Auth2Cli_PingReply { + dword messageId; + dword pingTimeMs; + dword transId; + dword payloadBytes; + byte payload[1]; // [payloadBytes] +}; + +// ClientRegisterReply +extern const NetMsg kNetMsg_Auth2Cli_ClientRegisterReply; +struct Auth2Cli_ClientRegisterReply { + dword messageId; + dword serverChallenge; +}; + +// AccountExists +extern const NetMsg kNetMsg_Auth2Cli_AccountExistsReply; +struct Auth2Cli_AccountExistsReply { + dword messageId; + dword transId; + ENetError result; + byte exists; +}; + +// ServerAddr +extern const NetMsg kNetMsg_Auth2Cli_ServerAddr; +struct Auth2Cli_ServerAddr { + dword messageId; + NetAddressNode srvAddr; + Uuid token; +}; + +extern const NetMsg kNetMsg_Auth2Cli_NotifyNewBuild; +struct Auth2Cli_NotifyNewBuild { + dword foo; // msgs must have at least one field +}; + +// AcctPlayerInfo +extern const NetMsg kNetMsg_Auth2Cli_AcctPlayerInfo; +struct Auth2Cli_AcctPlayerInfo { + dword messageId; + dword transId; + dword playerInt; + wchar playerName[kMaxPlayerNameLength]; + wchar avatarShape[kMaxVaultNodeStringLength]; + dword explorer; +}; + +// LoginReply +extern const NetMsg kNetMsg_Auth2Cli_AcctLoginReply; +struct Auth2Cli_AcctLoginReply { + dword messageId; + dword transId; + ENetError result; + Uuid accountId; + dword accountFlags; + dword billingType; + dword encryptionKey[4]; +}; + +// AgeReply +extern const NetMsg kNetMsg_Auth2Cli_AgeReply; +struct Auth2Cli_AgeReply { + dword messageId; + dword transId; + ENetError result; + dword ageMcpId; + Uuid ageInstId; + dword ageVaultId; + NetAddressNode gameSrvNode; +}; + +// AcctCreateReply +extern const NetMsg kNetMsg_Auth2Cli_AcctCreateReply; +struct Auth2Cli_AcctCreateReply { + dword messageId; + dword transId; + ENetError result; + Uuid accountId; +}; + +// AcctCreateFromKeyReply +extern const NetMsg kNetMsg_Auth2Cli_AcctCreateFromKeyReply; +struct Auth2Cli_AcctCreateFromKeyReply { + dword messageId; + dword transId; + ENetError result; + Uuid accountId; + Uuid activationKey; +}; + +// CreatePlayerReply +extern const NetMsg kNetMsg_Auth2Cli_PlayerCreateReply; +struct Auth2Cli_PlayerCreateReply { + dword messageId; + dword transId; + ENetError result; + dword playerInt; + dword explorer; + wchar playerName[kMaxPlayerNameLength]; + wchar avatarShape[kMaxVaultNodeStringLength]; +}; + +// DeletePlayerReply +extern const NetMsg kNetMsg_Auth2Cli_PlayerDeleteReply; +struct Auth2Cli_PlayerDeleteReply { + dword messageId; + dword transId; + ENetError result; +}; + +// DeletePlayerReply +extern const NetMsg kNetMsg_Auth2Cli_UpgradeVisitorReply; +struct Auth2Cli_UpgradeVisitorReply { + dword messageId; + dword transId; + ENetError result; +}; + +// SetPlayerReply +extern const NetMsg kNetMsg_Auth2Cli_AcctSetPlayerReply; +struct Auth2Cli_AcctSetPlayerReply { + dword messageId; + dword transId; + ENetError result; +}; + +// AcctChangePasswordReply +extern const NetMsg kNetMsg_Auth2Cli_AcctChangePasswordReply; +struct Auth2Cli_AcctChangePasswordReply { + dword messageId; + dword transId; + ENetError result; +}; + +// AcctSetRolesReply +extern const NetMsg kNetMsg_Auth2Cli_AcctSetRolesReply; +struct Auth2Cli_AcctSetRolesReply { + dword messageId; + dword transId; + ENetError result; +}; + +// AcctSetBillingTypeReply +extern const NetMsg kNetMsg_Auth2Cli_AcctSetBillingTypeReply; +struct Auth2Cli_AcctSetBillingTypeReply { + dword messageId; + dword transId; + ENetError result; +}; + +// AcctActivateReply +extern const NetMsg kNetMsg_Auth2Cli_AcctActivateReply; +struct Auth2Cli_AcctActivateReply { + dword messageId; + dword transId; + ENetError result; +}; + +// FileListReply +extern const NetMsg kNetMsg_Auth2Cli_FileListReply; +struct Auth2Cli_FileListReply { + dword messageId; + dword transId; + ENetError result; + dword wcharCount; + wchar fileData[1]; // [wcharCount], actually + // no more fields +}; + +// FileDownloadChunk +extern const NetMsg kNetMsg_Auth2Cli_FileDownloadChunk; +struct Auth2Cli_FileDownloadChunk { + dword messageId; + dword transId; + ENetError result; + dword fileSize; + dword chunkOffset; + dword chunkSize; + byte chunkData[1]; // [chunkSize], actually + // no more fields +}; + +// KickedOff +extern const NetMsg kNetMsg_Auth2Cli_KickedOff; +struct Auth2Cli_KickedOff { + dword messageId; + ENetError reason; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultNodeRefsFetched; +struct Auth2Cli_VaultNodeRefsFetched { + dword messageId; + dword transId; + ENetError result; + dword refCount; + NetVaultNodeRef refs[1]; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultNodeCreated; +struct Auth2Cli_VaultNodeCreated { + dword messageId; + dword transId; + ENetError result; + dword nodeId; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultNodeFetched; +struct Auth2Cli_VaultNodeFetched { + dword messageId; + dword transId; + ENetError result; + dword nodeBytes; + byte nodeBuffer[1]; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultNodeChanged; +struct Auth2Cli_VaultNodeChanged { + dword messageId; + dword nodeId; + Uuid revisionId; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultNodeAdded; +struct Auth2Cli_VaultNodeAdded { + dword messageId; + dword parentId; + dword childId; + dword ownerId; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultNodeRemoved; +struct Auth2Cli_VaultNodeRemoved { + dword messageId; + dword parentId; + dword childId; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultNodeDeleted; +struct Auth2Cli_VaultNodeDeleted { + dword messageId; + dword nodeId; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultSaveNodeReply; +struct Auth2Cli_VaultSaveNodeReply { + dword messageId; + dword transId; + ENetError result; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultAddNodeReply; +struct Auth2Cli_VaultAddNodeReply { + dword messageId; + dword transId; + ENetError result; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultRemoveNodeReply; +struct Auth2Cli_VaultRemoveNodeReply { + dword messageId; + dword transId; + ENetError result; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultInitAgeReply; +struct Auth2Cli_VaultInitAgeReply { + dword messageId; + dword transId; + ENetError result; + dword ageVaultId; + dword ageInfoVaultId; +}; + +extern const NetMsg kNetMsg_Auth2Cli_VaultNodeFindReply; +struct Auth2Cli_VaultNodeFindReply { + dword messageId; + dword transId; + ENetError result; + dword nodeIdCount; + dword nodeIds[1]; +}; + +// PublicAgeList +extern const NetMsg kNetMsg_Auth2Cli_PublicAgeList; +struct Auth2Cli_PublicAgeList { + dword messageId; + dword transId; + ENetError result; + dword ageCount; + NetAgeInfo ages[1]; // [ageCount], actually +}; + +// PropagateBuffer +extern const NetMsg kNetMsg_Auth2Cli_PropagateBuffer; +struct Auth2Cli_PropagateBuffer { + dword messageId; + dword type; + dword bytes; + byte buffer[1]; // [bytes], actually + // no more fields +}; + +// SetPlayerBanStatusReply +extern const NetMsg kNetMsg_Auth2Cli_SetPlayerBanStatusReply; +struct Auth2Cli_SetPlayerBanStatusReply { + dword messageId; + dword transId; + ENetError result; +}; + +// ChangePlayerNameReply +extern const NetMsg kNetMsg_Auth2Cli_ChangePlayerNameReply; +struct Auth2Cli_ChangePlayerNameReply { + dword messageId; + dword transId; + ENetError result; +}; + +// SendFriendInviteReply +extern const NetMsg kNetMsg_Auth2Cli_SendFriendInviteReply; +struct Auth2Cli_SendFriendInviteReply { + dword messageId; + dword transId; + ENetError result; +}; + +// ScoreCreateReply +extern const NetMsg kNetMsg_Auth2Cli_ScoreCreateReply; +struct Auth2Cli_ScoreCreateReply { + dword messageId; + dword transId; + ENetError result; + dword scoreId; + dword createdTime; +}; + +// ScoreDeleteReply +extern const NetMsg kNetMsg_Auth2Cli_ScoreDeleteReply; +struct Auth2Cli_ScoreDeleteReply { + dword messageId; + dword transId; + ENetError result; +}; + +// ScoreGetScoresReply +extern const NetMsg kNetMsg_Auth2Cli_ScoreGetScoresReply; +struct Auth2Cli_ScoreGetScoresReply { + dword messageId; + dword transId; + ENetError result; + dword scoreCount; + dword byteCount; + byte buffer[1]; // [byteCount], actually + // no more fields +}; + +// ScoreAddPoints +extern const NetMsg kNetMsg_Auth2Cli_ScoreAddPointsReply; +struct Auth2Cli_ScoreAddPointsReply { + dword messageId; + dword transId; + ENetError result; +}; + +// ScoreTransferPoints +extern const NetMsg kNetMsg_Auth2Cli_ScoreTransferPointsReply; +struct Auth2Cli_ScoreTransferPointsReply { + dword messageId; + dword transId; + ENetError result; +}; + +// ScoreSetPoints +extern const NetMsg kNetMsg_Auth2Cli_ScoreSetPointsReply; +struct Auth2Cli_ScoreSetPointsReply { + dword messageId; + dword transId; + ENetError result; +}; + +// ScoreGetRanksReply +extern const NetMsg kNetMsg_Auth2Cli_ScoreGetRanksReply; +struct Auth2Cli_ScoreGetRanksReply { + dword messageId; + dword transId; + ENetError result; + dword rankCount; + dword byteCount; + byte buffer[1]; // [byteCount], actually + // no more fields +}; + +//============================================================================ +// END PACKED DATA STRUCTURES +//============================================================================ +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Csr/pnNpCli2Csr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Csr/pnNpCli2Csr.cpp new file mode 100644 index 00000000..f68f45aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Csr/pnNpCli2Csr.cpp @@ -0,0 +1,106 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Csr/pnNpCli2Csr.cpp +* +***/ + +#define USES_PROTOCOL_CLI2CSR +#include "../../../Pch.h" +#pragma hdrstop + + +namespace Cli2Csr { +/***************************************************************************** +* +* Cli2Csr message field definitions +* +***/ + +static const NetMsgField kPingRequestFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldTimeMs, // pingTimeMs + NET_MSG_FIELD_VAR_COUNT(1, 64 * 1024), // payloadBytes + NET_MSG_FIELD_VAR_PTR(), // payload +}; + +static const NetMsgField kRegisterRequestFields[] = { + kNetMsgFieldTransId, // transId +}; + +static const NetMsgField kLoginRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // clientChallenge + kNetMsgFieldAccountName, // csrName + kNetMsgFieldShaDigest, // challenge +}; + + +/***************************************************************************** +* +* Csr2Cli message field definitions +* +***/ + +static const NetMsgField kPingReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldTimeMs, // pingTimeMs + NET_MSG_FIELD_VAR_COUNT(1, 64 * 1024), // payloadBytes + NET_MSG_FIELD_VAR_PTR(), // payload +}; + +static const NetMsgField kRegisterReplyFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // serverChallenge + NET_MSG_FIELD_DWORD(), // latestBuildId +}; + +static const NetMsgField kLoginReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result + kNetMsgFieldUuid, // csrId + NET_MSG_FIELD_DWORD(), // csrFlags +}; + + +} using namespace Cli2Csr; + + +/***************************************************************************** +* +* Exports +* +***/ + +const NetMsg kNetMsg_Cli2Csr_PingRequest = NET_MSG(kCli2Csr_PingRequest, kPingRequestFields); +const NetMsg kNetMsg_Cli2Csr_RegisterRequest = NET_MSG(kCli2Csr_RegisterRequest, kRegisterRequestFields); +const NetMsg kNetMsg_Cli2Csr_LoginRequest = NET_MSG(kCli2Csr_LoginRequest, kLoginRequestFields); + +const NetMsg kNetMsg_Csr2Cli_PingReply = NET_MSG(kCsr2Cli_PingReply, kPingReplyFields); +const NetMsg kNetMsg_Csr2Cli_RegisterReply = NET_MSG(kCsr2Cli_RegisterReply, kRegisterReplyFields); +const NetMsg kNetMsg_Csr2Cli_LoginReply = NET_MSG(kCsr2Cli_LoginReply, kLoginReplyFields); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Csr/pnNpCli2Csr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Csr/pnNpCli2Csr.h new file mode 100644 index 00000000..a75c8234 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Csr/pnNpCli2Csr.h @@ -0,0 +1,162 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Csr/pnNpCli2Csr.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_CLI2CSR_PNNPCLI2CSR_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Csr/pnNpCli2Csr.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_CLI2CSR_PNNPCLI2CSR_H + + +/***************************************************************************** +* +* kNetProtocolCli2Csr message ids +* +***/ + +// Because SrvCsr must remain backward compatible with all client builds, +// the following enum values may never change under any circumstances. + +//============================================================================ +// Cli2Csr + +enum { + // Misc + kCli2Csr_PingRequest = 0, + // Encrypt + kCli2Csr_RegisterRequest = 1, + // Login + kCli2Csr_LoginRequest = 2, + // Patch + kCli2Csr_PatchRequest = 3, + + kNumCli2CsrMessages +}; +COMPILER_ASSERT_HEADER(Cli2Scr, kNumCli2CsrMessages <= (word)-1); + + +//============================================================================ +// Csr2Cli + +enum { + // Misc + kCsr2Cli_PingReply = 0, + // Encrypt + kCsr2Cli_RegisterReply = 1, + // Login + kCsr2Cli_LoginReply = 2, + // Patch + kCli2Csr_PatchReply = 3, + + kNumCsr2CliMessages +}; +COMPILER_ASSERT_HEADER(Cli2Scr, kNumCsr2CliMessages <= (word)-1); + + +/***************************************************************************** +* +* Networked structures +* +***/ +#include + +//============================================================================ +// Connect packet + +struct Cli2Csr_ConnData { + dword dataBytes; +}; +struct Cli2Csr_Connect { + AsyncSocketConnectPacket hdr; + Cli2Csr_ConnData data; +}; + +//============================================================================ +// Message header + +struct Cli2Csr_MsgHeader { + dword messageId; + dword transId; +}; + +//============================================================================ +// Cli --> Csr message structures + +// PingRequest +extern const NetMsg kNetMsg_Cli2Csr_PingRequest; +struct Cli2Csr_PingRequest : Cli2Csr_MsgHeader { + dword pingTimeMs; + dword payloadBytes; + byte payload[1]; // [payloadBytes] +}; + +// RegisterRequest +extern const NetMsg kNetMsg_Cli2Csr_RegisterRequest; +struct Cli2Csr_RegisterRequest : Cli2Csr_MsgHeader { +}; + +// LoginRequest +extern const NetMsg kNetMsg_Cli2Csr_LoginRequest; +struct Cli2Csr_LoginRequest : Cli2Csr_MsgHeader { + dword clientChallenge; + wchar csrName[kMaxAccountNameLength]; + ShaDigest challengeHash; +}; + + +//============================================================================ +// Csr --> Cli message structures + +// PingReply +extern const NetMsg kNetMsg_Csr2Cli_PingReply; +struct Csr2Cli_PingReply : Cli2Csr_MsgHeader { + dword pingTimeMs; + dword payloadBytes; + byte payload[1]; // [payloadBytes] +}; + +// RegisterReply +extern const NetMsg kNetMsg_Csr2Cli_RegisterReply; +struct Csr2Cli_RegisterReply : Cli2Csr_MsgHeader { + dword serverChallenge; + dword csrBuildId; // buildId of the latest csr client +}; + +// LoginReply +extern const NetMsg kNetMsg_Csr2Cli_LoginReply; +struct Csr2Cli_LoginReply : Cli2Csr_MsgHeader { + ENetError result; + Uuid csrId; + dword csrFlags; +}; + + +#include + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2File/pnNpCli2File.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2File/pnNpCli2File.cpp new file mode 100644 index 00000000..c32e13b4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2File/pnNpCli2File.cpp @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2File/pnNpCli2File.cpp +* +***/ + +#define USES_PROTOCOL_CLI2FILE +#include "../../../Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2File/pnNpCli2File.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2File/pnNpCli2File.h new file mode 100644 index 00000000..793f115c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2File/pnNpCli2File.h @@ -0,0 +1,195 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2File/pnNpCli2File.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_CLI2FILE_PNNPCLI2FILE_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2File/pnNpCli2File.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_CLI2FILE_PNNPCLI2FILE_H + + +// Because SrvFile must remain backward compatible with all client builds, +// the following enum values may never change under any circumstances. + +// kNetProtocolCli2File messages +enum { + // Global + kCli2File_PingRequest = 0, + + // File server-related + kCli2File_BuildIdRequest = 10, + // 11 through 19 skipped + + // Cache-related + kCli2File_ManifestRequest = 20, + kCli2File_FileDownloadRequest = 21, + kCli2File_ManifestEntryAck = 22, + kCli2File_FileDownloadChunkAck = 23, + // 24 through 29 skipped + + kCli2File_UNUSED_1 = 30, +}; + +enum { + // Global + kFile2Cli_PingReply = 0, + + // File server-related + kFile2Cli_BuildIdReply = 10, + kFile2Cli_BuildIdUpdate = 11, + // 12 through 19 skipped + + // Cache-related + kFile2Cli_ManifestReply = 20, + kFile2Cli_FileDownloadReply = 21, + // 22 through 29 skipped + + kFile2Cli_UNUSED_1 = 30, +}; + +// we have a constant build id so all clients can connect to us, no matter what version +static const unsigned kFileSrvBuildId = 0; + + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Cli2File connect packet +* +***/ + +struct Cli2File_ConnData { + dword dataBytes; + dword buildId; + unsigned serverType; +}; +struct Cli2File_Connect { + AsyncSocketConnectPacket hdr; + Cli2File_ConnData data; +}; + +struct Cli2File_MsgHeader { + dword messageBytes; + dword messageId; +}; + + +/***************************************************************************** +* +* Cli2File message structures +* +***/ + +// PingRequest +struct Cli2File_PingRequest : Cli2File_MsgHeader { + dword pingTimeMs; +}; + +// BuildIdRequest +struct Cli2File_BuildIdRequest : Cli2File_MsgHeader { + dword transId; +}; + +// ManifestRequest +struct Cli2File_ManifestRequest : Cli2File_MsgHeader { + dword transId; + wchar group[MAX_PATH]; + unsigned buildId; // 0 = newest +}; +struct Cli2File_ManifestEntryAck : Cli2File_MsgHeader { + dword transId; + dword readerId; +}; + +// FileDownloadRequest +struct Cli2File_FileDownloadRequest : Cli2File_MsgHeader { + dword transId; + wchar filename[MAX_PATH]; + unsigned buildId; // 0 = newest +}; +struct Cli2File_FileDownloadChunkAck : Cli2File_MsgHeader { + dword transId; + dword readerId; +}; + + +/***************************************************************************** +* +* File2Cli message structures +* +***/ + +// PingReply +struct File2Cli_PingReply : Cli2File_MsgHeader { + dword pingTimeMs; +}; + +// BuildIdReply +struct File2Cli_BuildIdReply : Cli2File_MsgHeader { + dword transId; + ENetError result; + unsigned buildId; +}; + +// BuildIdUpdate +struct File2Cli_BuildIdUpdate : Cli2File_MsgHeader { + unsigned buildId; +}; + +// ManifestReply +struct File2Cli_ManifestReply : Cli2File_MsgHeader { + dword transId; + ENetError result; + dword readerId; + dword numFiles; // total number of files + dword wcharCount; // size of the buffer + wchar manifestData[1]; // manifestData[wcharCount], actually +}; + +// FileDownloadReply +struct File2Cli_FileDownloadReply : Cli2File_MsgHeader { + dword transId; + ENetError result; + dword readerId; + dword totalFileSize; + dword byteCount; + byte fileData[1]; // fileData[byteCount], actually +}; + + +//============================================================================ +// END PACKED DATA STRUCTURES +//============================================================================ +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Game/pnNpCli2Game.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Game/pnNpCli2Game.cpp new file mode 100644 index 00000000..e5a9a4c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Game/pnNpCli2Game.cpp @@ -0,0 +1,101 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Game/pnNpCli2Game.cpp +* +***/ + +#define USES_PROTOCOL_CLI2GAME +#include "../../../Pch.h" +#pragma hdrstop + + +namespace Cli2Game { +/***************************************************************************** +* +* Cli2Game message definitions +* +***/ + +static const NetMsgField kPingRequestFields[] = { + kNetMsgFieldTimeMs, // pingTimeMs +}; + +static const NetMsgField kJoinAgeRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_DWORD(), // ageMcpId + kNetMsgFieldUuid, // accountUuid + NET_MSG_FIELD_DWORD(), // playerInt +}; + +static const NetMsgField kPropagateBufferFields[] = { + NET_MSG_FIELD_DWORD(), // type + NET_MSG_FIELD_VAR_COUNT(1, 1024 * 1024), // bytes + NET_MSG_FIELD_VAR_PTR(), // buffer +}; + +static const NetMsgField kGameMgrMsgFields[] = { + NET_MSG_FIELD_VAR_COUNT(1, 1024 * 1024), // bytes + NET_MSG_FIELD_VAR_PTR(), // buffer +}; + + +/***************************************************************************** +* +* Game2Cli message fields +* +***/ + +static const NetMsgField kPingReplyFields[] = { + kNetMsgFieldTimeMs, // pingTimeMs +}; + +static const NetMsgField kJoinAgeReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldENetError, // result +}; + +} using namespace Cli2Game; + + +/***************************************************************************** +* +* Exported data +* +***/ + +// Cli2Game +const NetMsg kNetMsg_Cli2Game_PingRequest = NET_MSG(kCli2Game_PingRequest, kPingRequestFields); +const NetMsg kNetMsg_Cli2Game_JoinAgeRequest = NET_MSG(kCli2Game_JoinAgeRequest, kJoinAgeRequestFields); +const NetMsg kNetMsg_Cli2Game_PropagateBuffer = NET_MSG(kCli2Game_PropagateBuffer, kPropagateBufferFields); +const NetMsg kNetMsg_Cli2Game_GameMgrMsg = NET_MSG(kCli2Game_GameMgrMsg, kGameMgrMsgFields); + +// Game2Cli +const NetMsg kNetMsg_Game2Cli_PingReply = NET_MSG(kGame2Cli_PingReply, kPingReplyFields); +const NetMsg kNetMsg_Game2Cli_JoinAgeReply = NET_MSG(kGame2Cli_JoinAgeReply, kJoinAgeReplyFields); +const NetMsg kNetMsg_Game2Cli_PropagateBuffer = NET_MSG(kGame2Cli_PropagateBuffer, kPropagateBufferFields); +const NetMsg kNetMsg_Game2Cli_GameMgrMsg = NET_MSG(kGame2Cli_GameMgrMsg, kGameMgrMsgFields); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Game/pnNpCli2Game.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Game/pnNpCli2Game.h new file mode 100644 index 00000000..bba697f8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Game/pnNpCli2Game.h @@ -0,0 +1,178 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Game/pnNpCli2Game.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_CLI2GAME_PNNPCLI2GAME_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2Game/pnNpCli2Game.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_CLI2GAME_PNNPCLI2GAME_H + + +// kNetProtocolCli2Game messages +enum { + // Global + kCli2Game_PingRequest, + + // Age + kCli2Game_JoinAgeRequest, + + // Game + kCli2Game_PropagateBuffer, + kCli2Game_GameMgrMsg, + + kNumCli2GameMessages +}; +COMPILER_ASSERT_HEADER(Cli2Game, kNumCli2GameMessages <= (word)-1); + +enum { + // Global + kGame2Cli_PingReply, + + // Age + kGame2Cli_JoinAgeReply, + + // Game + kGame2Cli_PropagateBuffer, + kGame2Cli_GameMgrMsg, + + kNumGame2CliMessages +}; +COMPILER_ASSERT_HEADER(Cli2Game, kNumGame2CliMessages <= (word)-1); + + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Cli2Game connect packet +* +***/ + +struct Cli2Game_ConnData { + dword dataBytes; + Uuid accountUuid; + Uuid ageUuid; +}; +struct Cli2Game_Connect { + AsyncSocketConnectPacket hdr; + Cli2Game_ConnData data; +}; + + +/***************************************************************************** +* +* Cli2Game message structures +* +***/ + +// PingRequest +extern const NetMsg kNetMsg_Cli2Game_PingRequest; +struct Cli2Game_PingRequest { + dword messageId; + dword pingTimeMs; +}; + +// JoinAgeRequest +extern const NetMsg kNetMsg_Cli2Game_JoinAgeRequest; +struct Cli2Game_JoinAgeRequest { + dword messageId; + dword transId; + dword ageMcpId; + Uuid accountUuid; + dword playerInt; +}; + +// PropagateBuffer +extern const NetMsg kNetMsg_Cli2Game_PropagateBuffer; +struct Cli2Game_PropagateBuffer { + dword messageId; + dword type; + dword bytes; + byte buffer[1]; // actually, buffer[bytes] + // no more fields +}; + +// GameMgrMsg +extern const NetMsg kNetMsg_Cli2Game_GameMgrMsg; +struct Cli2Game_GameMgrMsg { + dword messageId; + dword bytes; + byte buffer[1]; // actually: buffer[bytes] +}; + + +/***************************************************************************** +* +* Game2Cli message structures +* +***/ + +// PingReply +extern const NetMsg kNetMsg_Game2Cli_PingReply; +struct Game2Cli_PingReply{ + dword messageId; + dword pingTimeMs; +}; + +// JoinAgeReply +extern const NetMsg kNetMsg_Game2Cli_JoinAgeReply; +struct Game2Cli_JoinAgeReply { + dword messageId; + dword transId; + ENetError result; +}; + +// PropagateBuffer +extern const NetMsg kNetMsg_Game2Cli_PropagateBuffer; +struct Game2Cli_PropagateBuffer { + dword messageId; + dword type; + dword bytes; + byte buffer[1]; // actually, buffer[bytes] + // no more fields +}; + +// GameMgrMsg +extern const NetMsg kNetMsg_Game2Cli_GameMgrMsg; +struct Game2Cli_GameMgrMsg { + dword messageId; + dword bytes; + byte buffer[1]; // actually: buffer[bytes] +}; + + +//============================================================================ +// END PACKED DATA STRUCTURES +//============================================================================ +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2GateKeeper/pnNpCli2GateKeeper.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2GateKeeper/pnNpCli2GateKeeper.cpp new file mode 100644 index 00000000..d9feb9e0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2GateKeeper/pnNpCli2GateKeeper.cpp @@ -0,0 +1,94 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/pnNpCli2Auth.cpp +* +***/ + +#define USES_PROTOCOL_CLI2GATEKEEPER +#include "../../../Pch.h" +#pragma hdrstop + + +namespace Cli2GateKeeper { + +/***************************************************************************** +* +* Cli2GateKeeper message field definitions +* +***/ + +static const NetMsgField kPingRequestFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldTimeMs, // pingTimeMs + NET_MSG_FIELD_VAR_COUNT(1, 64 * 1024), // payloadBytes + NET_MSG_FIELD_VAR_PTR(), // payload +}; + +static const NetMsgField kFileSrvIpAddressRequestFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_BYTE(), // isPatcher +}; + +static const NetMsgField kAuthSrvIpAddressRequestFields[] = { + kNetMsgFieldTransId, // transId +}; + + +/***************************************************************************** +* +* GateKeeper2Cli message field definitions +* +***/ + +static const NetMsgField kPingReplyFields[] = { + kNetMsgFieldTransId, // transId + kNetMsgFieldTimeMs, // pingTimeMs + NET_MSG_FIELD_VAR_COUNT(1, 64 * 1024), // payloadBytes + NET_MSG_FIELD_VAR_PTR(), // payload +}; + +static const NetMsgField kFileSrvIpAddressReplyFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(24), // IpAddress +}; + +static const NetMsgField kAuthSrvIpAddressReplyFields[] = { + kNetMsgFieldTransId, // transId + NET_MSG_FIELD_STRING(24), // IpAddress +}; + +} using namespace Cli2GateKeeper; + + +const NetMsg kNetMsg_Cli2GateKeeper_PingRequest = NET_MSG(kCli2GateKeeper_PingRequest, kPingRequestFields); +const NetMsg kNetMsg_Cli2GateKeeper_FileSrvIpAddressRequest = NET_MSG(kCli2GateKeeper_FileSrvIpAddressRequest, kFileSrvIpAddressRequestFields); +const NetMsg kNetMsg_Cli2GateKeeper_AuthSrvIpAddressRequest = NET_MSG(kCli2GateKeeper_AuthSrvIpAddressRequest, kAuthSrvIpAddressRequestFields); + +const NetMsg kNetMsg_GateKeeper2Cli_PingReply = NET_MSG(kGateKeeper2Cli_PingReply, kPingReplyFields); +const NetMsg kNetMsg_GateKeeper2Cli_FileSrvIpAddressReply = NET_MSG(kGateKeeper2Cli_FileSrvIpAddressReply, kFileSrvIpAddressReplyFields); +const NetMsg kNetMsg_GateKeeper2Cli_AuthSrvIpAddressReply = NET_MSG(kGateKeeper2Cli_AuthSrvIpAddressReply, kAuthSrvIpAddressReplyFields); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2GateKeeper/pnNpCli2GateKeeper.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2GateKeeper/pnNpCli2GateKeeper.h new file mode 100644 index 00000000..d0b9bae5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Cli2GateKeeper/pnNpCli2GateKeeper.h @@ -0,0 +1,141 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/pnNpCli2GateKeeper.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_PNNPCLI2GATEKEEPER_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/pnNpCli2GateKeeper.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_PNNPCLI2GATEKEEPER_H + + +// kNetProtocolCli2GateKeeper messages (must be <= (word)-1) +enum { + // Global + kCli2GateKeeper_PingRequest, + kCli2GateKeeper_FileSrvIpAddressRequest, + kCli2GateKeeper_AuthSrvIpAddressRequest, + + kNumCli2GateKeeperMessages +}; +COMPILER_ASSERT_HEADER(Cli2GateKeeper, kNumCli2GateKeeperMessages <= (word)-1); + +enum { + // Global + kGateKeeper2Cli_PingReply, + kGateKeeper2Cli_FileSrvIpAddressReply, + kGateKeeper2Cli_AuthSrvIpAddressReply, + + kNumGateKeeper2CliMessages +}; +COMPILER_ASSERT_HEADER(Cli2GateKeeper, kNumGateKeeper2CliMessages <= (word)-1); + + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Cli2GateKeeper connect packet +* +***/ + +struct Cli2GateKeeper_ConnData { + dword dataBytes; + Uuid token; +}; + +struct Cli2GateKeeper_Connect { + AsyncSocketConnectPacket hdr; + Cli2GateKeeper_ConnData data; +}; + +//============================================================================ +// Cli --> GateKeeper message structures + +// PingRequest +extern const NetMsg kNetMsg_Cli2GateKeeper_PingRequest; +struct Cli2GateKeeper_PingRequest { + dword messageId; + dword transId; + dword pingTimeMs; + dword payloadBytes; + byte payload[1]; // [payloadBytes] +}; + +// FileSrvIpAddressRequest +extern const NetMsg kNetMsg_Cli2GateKeeper_FileSrvIpAddressRequest; +struct Cli2GateKeeper_FileSrvIpAddressRequest { + dword messageId; + dword transId; + byte isPatcher; +}; + +// AuthSrvIpAddressRequest +extern const NetMsg kNetMsg_Cli2GateKeeper_AuthSrvIpAddressRequest; +struct Cli2GateKeeper_AuthSrvIpAddressRequest { + dword messageId; + dword transId; +}; + + +//============================================================================ +// GateKeeper --> Cli message structures + +// PingReply +extern const NetMsg kNetMsg_GateKeeper2Cli_PingReply; +struct GateKeeper2Cli_PingReply { + dword messageId; + dword transId; + dword pingTimeMs; + dword payloadBytes; + byte payload[1]; // [payloadBytes] +}; + +// FileSrvIpAddressReply +extern const NetMsg kNetMsg_GateKeeper2Cli_FileSrvIpAddressReply; +struct GateKeeper2Cli_FileSrvIpAddressReply { + dword messageId; + dword transId; + wchar address[24]; +}; + + +// FileSrvIpAddressReply +extern const NetMsg kNetMsg_GateKeeper2Cli_AuthSrvIpAddressReply; +struct GateKeeper2Cli_AuthSrvIpAddressReply { + dword messageId; + dword transId; + wchar address[24]; +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Db/pnNpSrv2Db.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Db/pnNpSrv2Db.cpp new file mode 100644 index 00000000..97580b49 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Db/pnNpSrv2Db.cpp @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Db/pnNpSrv2Db.cpp +* +***/ + +#ifdef SERVER + +#define USES_PROTOCOL_SRV2DB +#include "../../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +bool Srv2DbValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2Db_ConnData * connectPtr +) { + // Ensure that there are enough bytes for the header + const Srv2Db_ConnData & connect = * (const Srv2Db_ConnData *) listen->buffer; + + // Validate message size + const unsigned kMinStructSize = sizeof(dword) * 1; + if (listen->bytes < kMinStructSize) + return false; + if (listen->bytes < connect.dataBytes) + return false; + + // Validate connect server type + if (! (connect.srvType == kSrvTypeAuth + || connect.srvType == kSrvTypeVault + || connect.srvType == kSrvTypeState + || connect.srvType == kSrvTypeScore + || connect.srvType == kSrvTypeCsr) + ) { + return false; + } + + ZEROPTR(connectPtr); + MemCopy(connectPtr, &connect, min(sizeof(*connectPtr), connect.dataBytes)); + + listen->bytesProcessed += connect.dataBytes; + return true; +} + +#endif // SERVER diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Db/pnNpSrv2Db.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Db/pnNpSrv2Db.h new file mode 100644 index 00000000..4269f7f2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Db/pnNpSrv2Db.h @@ -0,0 +1,553 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Db/pnNpSrv2Db.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2DB_PNNPSRV2DB_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Db/pnNpSrv2Db.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2DB_PNNPSRV2DB_H + + + +// kNetProtocolSrv2Db messages +// Because SrvDb must remain compatible with older auth builds, these message ids +// must not change unless all front-end servers are synchronously replaced. +enum { + // Account + kSrv2Db_AccountCreateRequest = 0, + kSrv2Db_AccountLoginRequest = 1, + kSrv2Db_AccountLogout = 2, + kSrv2Db_AccountChangePasswordRequest = 3, + kSrv2Db_AccountSetRolesRequest = 4, + kSrv2Db_AccountSetBillingTypeRequest = 5, + kSrv2Db_AccountActivateRequest = 6, + kSrv2Db_AccountCreateFromKeyRequest = 7, + kSrv2Db_AccountLockPlayerNameRequest = 8, + kSrv2Db_SetPlayerBanStatusRequest = 9, + kSrv2Db_AccountLoginRequest2 = 10, + kSrv2Db_AccountExistsRequest = 11, + + // VaultNodes + kSrv2Db_VaultNodeCreateRequest = 20, + kSrv2Db_VaultNodeFetchRequest = 21, + kSrv2Db_VaultNodeSaveRequest = 22, + kSrv2Db_VaultNodeDeleteRequest = 23, + kSrv2Db_VaultNodeFindRequest = 24, + kSrv2Db_VaultSendNode = 25, + kSrv2Db_SetAgeSequenceNumRequest = 26, + kSrv2Db_VaultNodeChanged = 27, + kSrv2Db_FetchInviterInfo = 28, + + // VaultNodeRefs + kSrv2Db_VaultNodeAddRefs = 40, + kSrv2Db_VaultNodeDelRefs = 41, + kSrv2Db_VaultNodeGetChildRefs = 42, + kSrv2Db_VaultNodeGetParentRefs = 43, + + // State Objects + kSrv2Db_StateSaveObject = 60, + kSrv2Db_StateDeleteObject = 61, + kSrv2Db_StateFetchObject = 62, + + // Public ages + kSrv2Db_GetPublicAgeInfoIds = 80, + kSrv2Db_SetAgePublic = 81, + + // Score + kSrv2Db_ScoreCreate = 100, + kSrv2Db_ScoreFindScoreIds = 101, + kSrv2Db_ScoreFetchScores = 102, + kSrv2Db_ScoreSave = 103, + kSrv2Db_ScoreDelete = 104, + kSrv2Db_ScoreGetRanks = 105, + + // Vault Notifications + kSrv2Db_PlayerOnline = 120, + kSrv2Db_PlayerOffline = 121, + kSrv2Db_AgeOnline = 122, + kSrv2Db_AgeOffline = 123, + + // CSR + kSrv2Db_CsrAcctInfoRequest = 140, +}; + +enum { + // Account + kDb2Srv_AccountCreateReply = 0, + kDb2Srv_AccountLoginReply = 1, + kDb2Srv_AccountCreateFromKeyReply = 2, + kDb2Srv_AccountExistsReply = 3, + + // VaultNodes + kDb2Srv_VaultNodeCreateReply = 20, + kDb2Srv_VaultNodeFetchReply = 21, + kDb2Srv_VaultNodeSaveReply = 22, + kDb2Srv_VaultNodeDeleteReply = 23, + kDb2Srv_VaultNodeFindReply = 24, + kDb2Srv_SetAgeSequenceNumReply = 25, + kDb2Srv_FetchInviterInfoReply = 26, + + // VaultNodeRefs + kDb2Srv_VaultNodeRefs = 40, + + // State Objects + kDb2Srv_StateObjectFetched = 60, + + // Vault Notification + kDb2Srv_NotifyVaultNodeChanged = 80, + kDb2Srv_NotifyVaultNodeAdded = 81, + kDb2Srv_NotifyVaultNodeRemoved = 82, + kDb2Srv_NotifyVaultNodeDeleted = 83, + + // Public ages + kDb2Srv_PublicAgeInfoIds = 100, + + // Score + kDb2Srv_ScoreCreateReply = 120, + kDb2Srv_ScoreFindScoreIdsReply = 121, + kDb2Srv_ScoreFetchScoresReply = 122, + kDb2Srv_ScoreDeleteReply = 123, + kDb2Srv_ScoreGetRanksReply = 124, + + // CSR + kDb2Srv_CsrAcctInfoReply = 140, +}; + + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Srv2Db connect packet +* +***/ + +struct Srv2Db_ConnData { + dword dataBytes; + dword srvType; +}; +struct Srv2Db_Connect { + AsyncSocketConnectPacket hdr; + Srv2Db_ConnData data; +}; + + +/***************************************************************************** +* +* Srv2Db packets +* +***/ + + +struct Srv2Db_AccountExistsRequest : SrvMsgHeader { + wchar accountName[kMaxAccountNameLength]; +}; + +struct Srv2Db_AccountCreateRequest : SrvMsgHeader { + wchar accountName[kMaxAccountNameLength]; + ShaDigest namePassHash; + dword billingType; + dword accountFlags; + wchar foreignAcctId[kMaxPublisherAuthKeyLength]; +}; + +struct Srv2Db_AccountCreateFromKeyRequest : SrvMsgHeader { + wchar accountName[kMaxAccountNameLength]; + ShaDigest namePassHash; + Uuid key; + dword billingType; +}; + +struct Srv2Db_AccountLoginRequest : SrvMsgHeader { + wchar accountName[kMaxAccountNameLength]; +}; + +struct Srv2Db_AccountLoginRequest2 : SrvMsgHeader { + wchar accountName[kMaxAccountNameLength]; + dword buildId; +}; + +struct Srv2Db_AccountLogout : SrvMsgHeader { + Uuid accountUuid; + dword timeLoggedMins; +}; + +struct Srv2Db_AccountChangePasswordRequest : SrvMsgHeader { + wchar accountName[kMaxAccountNameLength]; + ShaDigest namePassHash; +}; + +struct Srv2Db_AccountSetRolesRequest : SrvMsgHeader { + wchar accountName[kMaxAccountNameLength]; + dword accountFlags; +}; + +struct Srv2Db_AccountSetBillingTypeRequest : SrvMsgHeader { + wchar accountName[kMaxAccountNameLength]; + dword billingType; +}; + +struct Srv2Db_AccountActivateRequest : SrvMsgHeader { + Uuid activationKey; +}; + +struct Srv2Db_AccountLockPlayerNameRequest :SrvMsgHeader { + wchar playerName[kMaxPlayerNameLength]; + Uuid accountUuid; +}; + +struct Srv2Db_VaultNodeCreateRequest : SrvMsgHeader { + Uuid accountUuid; + dword creatorId; + dword nodeBytes; + byte nodeBuffer[1]; +}; + +struct Srv2Db_VaultNodeFetchRequest : SrvMsgHeader { + dword nodeId; +}; + +struct Srv2Db_VaultNodeChanged : SrvMsgHeader { + dword nodeId; + Uuid revisionId; +}; + +struct Srv2Db_VaultNodeSaveRequest : SrvMsgHeader { + Uuid revisionId; + dword nodeId; + unsigned playerCheckId; + unsigned isRequestFromAuth; + dword nodeBytes; + byte buffer[1]; // buffer[bytes], actually + // no more fields after var length alloc +}; + +struct Srv2Db_VaultNodeDeleteRequest : SrvMsgHeader { + dword nodeId; + unsigned playerCheckId; + unsigned isRequestFromAuth; +}; + + +struct Srv2Db_VaultNodeFindRequest : SrvMsgHeader { + // Template node to match + dword nodeBytes; + byte nodeBuffer[1]; // [nodeBytes], actually + // no more fields after var length alloc +}; + +struct Srv2Db_VaultNodeAddRefs : SrvMsgHeader { + dword refCount; + NetVaultNodeRef refs[1]; + // no more fields after var length alloc +}; + +struct Srv2Db_VaultNodeDelRefs : SrvMsgHeader { + dword refCount; + unsigned playerCheckId; + unsigned isRequestFromAuth; + NetVaultNodeRef refs[1]; + // no more fields after var length alloc +}; + +struct Srv2Db_VaultNodeGetChildRefs : SrvMsgHeader { + dword nodeId; + dword maxDepth; +}; + +struct Srv2Db_VaultNodeGetParentRefs : SrvMsgHeader { + dword nodeId; + dword maxDepth; +}; + +struct Srv2Db_VaultSendNode : SrvMsgHeader { + dword srcPlayerId; // sender + dword srcNodeId; // sent item + dword dstPlayerId; // recipient +}; + +struct Srv2Db_SetAgeSequenceNumRequest : SrvMsgHeader { + dword nodeId; + wchar ageInstName[kMaxAgeNameLength]; + wchar ageUserName[kMaxAgeNameLength]; +}; + +struct Srv2Db_StateSaveObject : SrvMsgHeader { + dword buildId; + Uuid ownerId; + wchar objectName[kMaxStateObjectName]; + dword objectDataBytes; + byte objectData[1]; + // no more fields after var length alloc +}; + +struct Srv2Db_StateDeleteObject : SrvMsgHeader { + Uuid ownerId; + wchar objectName[kMaxStateObjectName]; +}; + +struct Srv2Db_StateFetchObject : SrvMsgHeader { + Uuid ownerId; + wchar objectName[kMaxStateObjectName]; +}; + +struct Srv2Db_GetPublicAgeInfoIds : SrvMsgHeader { + wchar ageName[kMaxAgeNameLength]; +}; + +struct Srv2Db_SetAgePublic : SrvMsgHeader { + dword playerId; + dword ageInfoId; + byte publicOrNot; +}; + +struct Srv2Db_SetPlayerBanStatusRequest : SrvMsgHeader { + dword playerId; + dword banned; +}; + +struct Srv2Db_ScoreCreate : SrvMsgHeader { + dword ownerId; + wchar gameName[kMaxGameScoreNameLength]; + dword gameType; + dword value; +}; + +struct Srv2Db_ScoreDelete : SrvMsgHeader { + dword scoreId; +}; + +struct Srv2Db_ScoreFindScoreIds : SrvMsgHeader { + dword ownerId; + wchar gameName[kMaxGameScoreNameLength]; +}; + +struct Srv2Db_ScoreFetchScores : SrvMsgHeader { + dword scoreCount; + dword scoreIds[1]; // [scoreCount], actually + // no more fields after var length alloc +}; + +struct Srv2Db_ScoreSave : SrvMsgHeader { + dword scoreId; + dword value; +}; + +struct Srv2Db_ScoreGetRanks : SrvMsgHeader { + dword ownerId; + dword scoreGroup; + dword parentFolderId; + wchar gameName[kMaxGameScoreNameLength]; + dword timePeriod; + dword numResults; + dword pageNumber; + dword sortDesc; +}; + +struct Srv2Db_PlayerOnline : SrvMsgHeader { + dword playerId; +}; + +struct Srv2Db_PlayerOffline : SrvMsgHeader { + dword playerId; +}; + +struct Srv2Db_AgeOnline : SrvMsgHeader { + Uuid ageInstId; +}; + +struct Srv2Db_AgeOffline : SrvMsgHeader { + Uuid ageInstId; +}; + +struct Srv2Db_CsrAcctInfoRequest : SrvMsgHeader { + wchar csrName[kMaxAccountNameLength]; +}; + +struct Srv2Db_FetchInviterInfo : SrvMsgHeader { + Uuid inviteUuid; +}; + + +/***************************************************************************** +* +* Db2Srv packets +* +***/ + +struct Db2Srv_AccountExistsReply : SrvMsgHeader { + byte exists; +}; + +struct Db2Srv_AccountCreateReply : SrvMsgHeader { + Uuid accountUuid; +}; + +struct Db2Srv_AccountCreateFromKeyReply : SrvMsgHeader { + Uuid accountUuid; + Uuid activationKey; +}; + +struct Db2Srv_AccountLoginReply : SrvMsgHeader { + Uuid accountUuid; + dword accountFlags; + dword billingType; + ShaDigest namePassHash; +}; + +struct Db2Srv_VaultNodeCreateReply : SrvMsgHeader { + dword nodeId; +}; + +struct Db2Srv_VaultNodeFetchReply : SrvMsgHeader { + dword nodeBytes; + byte buffer[1]; + // no more fields after var length alloc +}; + +struct Db2Srv_VaultNodeFindReply : SrvMsgHeader { + // out: ids of matching nodes + dword nodeIdCount; + dword nodeIds[1]; // [nodeIdCount], actually + // no more fields after var length alloc +}; + +struct Db2Srv_VaultNodeRefs : SrvMsgHeader { + dword refCount; + NetVaultNodeRef refs[1]; + // no more fields after var length alloc +}; + +struct Db2Srv_SetAgeSequenceNumReply : SrvMsgHeader { + dword sequenceNum; +}; + +struct Db2Srv_FetchInviterInfoReply : SrvMsgHeader { + Uuid hoodInstance; +}; + +struct Db2Srv_StateObjectFetched : SrvMsgHeader { + dword buildId; + Uuid ownerId; + wchar objectName[kMaxStateObjectName]; + dword objectDataBytes; + byte objectData[1]; + // no more fields after var length alloc +}; + +struct Db2Srv_NotifyVaultNodeChanged : SrvMsgHeader { + dword nodeId; + Uuid revId; + dword notifyIdCount; + dword notifyIds[1]; +}; + +struct Db2Srv_NotifyVaultNodeAdded : SrvMsgHeader { + NetVaultNodeRef ref; + dword notifyIdCount; + dword notifyIds[1]; +}; + +struct Db2Srv_NotifyVaultNodeRemoved : SrvMsgHeader { + dword parentId; + dword childId; + dword notifyIdCount; + dword notifyIds[1]; +}; + +struct Db2Srv_NotifyVaultNodeDeleted : SrvMsgHeader { + dword nodeId; + dword notifyIdCount; + dword notifyIds[1]; +}; + +struct Db2Srv_PublicAgeInfoIds : SrvMsgHeader { + dword idCount; + dword ids[1]; // [idCount], actually + // no more fields after var length alloc +}; + +struct Db2Srv_ScoreCreateReply : SrvMsgHeader { + dword scoreId; + dword createdTime; +}; + +struct Db2Srv_ScoreDeleteReply : SrvMsgHeader { + dword ownerId; + wchar gameName[kMaxGameScoreNameLength]; +}; + +struct Db2Srv_ScoreFindScoreIdsReply : SrvMsgHeader { + dword idCount; + dword scoreIds[1]; // [idCount], actually + // no more fields after var length alloc +}; + +struct Db2Srv_ScoreFetchScoresReply : SrvMsgHeader { + dword scoreCount; + dword byteCount; + byte buffer[1]; // [byteCount], actually + // no more fields after var length alloc +}; + +struct Db2Srv_ScoreGetRanksReply : SrvMsgHeader { + dword rankCount; + dword byteCount; + byte buffer[1]; // [byteCount], actually + // no more fields after var length alloc +}; + +struct Db2Srv_CsrAcctInfoReply : SrvMsgHeader { + Uuid csrId; + dword csrFlags; + ShaDigest namePassHash; +}; + + +//============================================================================ +// END PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Srv2Db functions +* +***/ + +bool Srv2DbValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2Db_ConnData * connectPtr +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Log/pnNpSrv2Log.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Log/pnNpSrv2Log.cpp new file mode 100644 index 00000000..9bee375c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Log/pnNpSrv2Log.cpp @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Log/pnNpSrv2Log.cpp +* +***/ + +#ifdef SERVER + +#define USES_PROTOCOL_SRV2LOG + +#include "../../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +bool Srv2LogValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2Log_ConnData * connectPtr +) { + // Ensure that there are enough bytes for the header + const Srv2Log_ConnData & connect = * (const Srv2Log_ConnData *) listen->buffer; + + // Validate message size + const unsigned kMinStructSize = sizeof(dword) * 3; + if (listen->bytes < kMinStructSize) + return false; + if (listen->bytes < connect.dataBytes) + return false; + + // Validate connect server type + if (!(connect.srvType == kSrvTypeAuth || connect.srvType == kSrvTypeGame || connect.srvType == kSrvTypeVault || connect.srvType == kSrvTypeDb || + connect.srvType == kSrvTypeMcp || connect.srvType == kSrvTypeState || connect.srvType == kSrvTypeFile || connect.srvType == kSrvTypeDll)) + return false; + + ZEROPTR(connectPtr); + MemCopy(connectPtr, &connect, min(sizeof(*connectPtr), connect.dataBytes)); + + listen->bytesProcessed += connect.dataBytes; + return true; +} + +#endif // SERVER + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Log/pnNpSrv2Log.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Log/pnNpSrv2Log.h new file mode 100644 index 00000000..8c99a00e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Log/pnNpSrv2Log.h @@ -0,0 +1,104 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Log/pnNpSrv2Log.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2LOG_PNNPSRV2LOG_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Log/pnNpSrv2Log.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2LOG_PNNPSRV2LOG_H + + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + +// Srv2Log +enum { + kSrv2Log_LogMsg = 0, +}; + +// Log2Srv +enum { + +}; + + +/***************************************************************************** +* +* Srv2State connect packet +* +***/ + +struct Srv2Log_ConnData { + dword dataBytes; + dword buildId; + dword srvType; + dword buildType; + dword productId; +}; + +struct Srv2Log_Connect { + AsyncSocketConnectPacket hdr; + Srv2Log_ConnData data; +}; + + +/***************************************************************************** +* +* Srv2Log message structures +* +***/ + +struct Srv2Log_LogMsg : SrvMsgHeader { + unsigned eventType; + qword timestamp; +}; + + + +//============================================================================ +// END PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Srv2State functions +* +***/ + +bool Srv2LogValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2Log_ConnData * connectPtr +); + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.cpp new file mode 100644 index 00000000..ef0d84d5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.cpp @@ -0,0 +1,71 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.cpp +* +***/ + +#ifdef SERVER + +#define USES_PROTOCOL_SRV2MCP +#include "../../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +bool Srv2McpValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2Mcp_ConnData * connectPtr +) { + // Ensure that there are enough bytes for the header + const Srv2Mcp_ConnData & connect = * (const Srv2Mcp_ConnData *) listen->buffer; + + // Validate message size + const unsigned kMinStructSize = sizeof(dword) * 3; + if (listen->bytes < kMinStructSize) + return false; + if (listen->bytes < connect.dataBytes) + return false; + + // Validate connect server type + if (!(connect.srvType == kSrvTypeAuth || connect.srvType == kSrvTypeGame)) + return false; + + ZEROPTR(connectPtr); + MemCopy(connectPtr, &connect, min(sizeof(*connectPtr), connect.dataBytes)); + + listen->bytesProcessed += connect.dataBytes; + return true; +} + +#endif // SERVER diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.h new file mode 100644 index 00000000..8f62fc4a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.h @@ -0,0 +1,251 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2MCP_PNNPSRV2MCP_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Mcp/pnNpSrv2Mcp.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2MCP_PNNPSRV2MCP_H + + +// kNetProtocolSrv2Mcp messages +// Because SrvMcp must remain compatible with older auth builds, these message ids +// must not change unless all front-end servers are synchronously replaced. +enum { + // Age + kSrv2Mcp_AgeJoinRequest = 0, + kSrv2Mcp_PlayerLoggedOut = 1, + kSrv2Mcp_PlayerLoggedIn = 2, + kSrv2Mcp_AgeSpawned = 3, + kSrv2Mcp_AgeDied = 4, + kSrv2Mcp_KickPlayer = 5, + + // Account + kSrv2Mcp_AccountLoginRequest = 20, + kSrv2Mcp_AccountLogout = 21, + kSrv2Mcp_AccountSetPlayer = 22, + + // Messaging + kSrv2Mcp_PropagateBuffer = 30, +}; + +enum { + // Age + kMcp2Srv_AgeJoinReply = 0, + kMcp2Srv_AgeSpawnRequest = 1, + kMcp2Srv_AgeAddPlayerRequest = 2, + kMcp2Srv_AgeRemovePlayerRequest = 3, + kMcp2Srv_AgeUnspawn = 4, + kMcp2Srv_AgeSpawnedReply = 5, + + // Account + kMcp2Srv_AccountLoginReply = 20, + kMcp2Srv_AccountNotifyKicked = 21, + + // Messaging + kMcp2Srv_PropagateBuffer = 30, + + // AgeSDL + kMcp2Srv_NotifyAgeSDLChanged = 40, +}; + + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Srv2Mcp connect packet +* +***/ + +struct Srv2Mcp_ConnData { + dword dataBytes; + dword buildId; + dword srvType; + dword publicAddr; +}; +struct Srv2Mcp_Connect { + AsyncSocketConnectPacket hdr; + Srv2Mcp_ConnData data; +}; + + +/***************************************************************************** +* +* Srv2Mcp message structures +* +***/ + +struct Srv2Mcp_AgeJoinRequest : SrvMsgHeader { + wchar ageName[kMaxAgeNameLength]; + Uuid ageUuid; + Uuid accountUuid; + dword playerInt; + byte ccrLevel; + wchar playerName[kMaxPlayerNameLength]; + dword buildId; +}; + +struct Srv2Mcp_PlayerLoggedIn : SrvMsgHeader { + dword ageMcpId; + Uuid ageUuid; + Uuid accountUuid; + wchar playerName[kMaxPlayerNameLength]; + dword playerInt; +}; + +struct Srv2Mcp_PlayerLoggedOut : SrvMsgHeader { + dword ageMcpId; + Uuid accountUuid; + dword playerInt; +}; + +struct Srv2Mcp_AgeSpawned : SrvMsgHeader { + wchar ageName[kMaxAgeNameLength]; + Uuid ageUuid; + dword buildId; +}; + +struct Srv2Mcp_AgeDied : SrvMsgHeader { + dword ageMcpId; +}; + +struct Srv2Mcp_AccountLoginRequest : SrvMsgHeader { + wchar accountName[kMaxAccountNameLength]; + Uuid accountUuid; +}; + +struct Srv2Mcp_AccountLogout : SrvMsgHeader { + Uuid accountUuid; +}; + +struct Srv2Mcp_AccountSetPlayer : SrvMsgHeader { + Uuid accountUuid; + dword playerInt; +}; + +struct Srv2Mcp_PropagateBuffer : SrvMsgHeader { + dword type; + dword bufferLength; + dword numRecvrs; + // packed data: + // byte netMessage[]; + // dword playerlist[]; +}; + +struct Srv2Mcp_KickPlayer : SrvMsgHeader { + dword playerInt; +}; + +/***************************************************************************** +* +* Mcp2Srv message structures +* +***/ + +struct Mcp2Srv_AgeJoinReply : SrvMsgHeader { + dword ageMcpId; + Uuid ageUuid; + NetAddressNode gameSrvNode; +}; + +struct Mcp2Srv_AgeSpawnRequest : SrvMsgHeader { + wchar ageName[kMaxAgeNameLength]; + dword ageMcpId; + Uuid ageUuid; + dword buildId; +}; + +struct Mcp2Srv_AgeUnspawn : SrvMsgHeader { + dword ageMcpId; +}; + +struct Mcp2Srv_AgeAddPlayerRequest : SrvMsgHeader { + dword ageMcpId; + Uuid accountUuid; + dword playerInt; + byte ccrLevel; + wchar playerName[kMaxPlayerNameLength]; +}; + +struct Mcp2Srv_AgeRemovePlayerRequest : SrvMsgHeader { + Uuid ageMcpId; + Uuid accountUuid; + dword playerInt; +}; + +struct Mcp2Srv_AgeSpawnedReply : SrvMsgHeader { + dword ageMcpId; // assigns a new mcpId if age wasn't found (in case of a reconnect) +}; + +struct Mcp2Srv_AccountLoginReply : SrvMsgHeader { + dword acctMcpId; +}; + +struct Mcp2Srv_AccountNotifyKicked : SrvMsgHeader { + Uuid accountUuid; + dword acctMcpId; + ENetError reason; +}; + +struct Mcp2Srv_NotifyAgeSDLChanged : SrvMsgHeader { + dword ageMcpId; + byte global; +}; + +struct Mcp2Srv_PropagateBuffer : SrvMsgHeader { + dword ageMcpId; + dword type; + dword bytes; + byte buffer[1]; // actually, buffer[bytes] + // no more fields +}; + + +//============================================================================ +// END PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Srv2Mcp functions +* +***/ + +bool Srv2McpValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2Mcp_ConnData * connectPtr +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Score/pnNpSrv2Score.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Score/pnNpSrv2Score.cpp new file mode 100644 index 00000000..f1852a52 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Score/pnNpSrv2Score.cpp @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Score/pnNpSrv2Score.cpp +* +***/ + +#ifdef SERVER + +#define USES_PROTOCOL_SRV2SCORE + +#include "../../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +bool Srv2ScoreValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2Score_ConnData * connectPtr +) { + // Ensure that there are enough bytes for the header + const Srv2Score_ConnData & connect = * (const Srv2Score_ConnData *) listen->buffer; + + // Validate message size + const unsigned kMinStructSize = sizeof(dword) * 3; + if (listen->bytes < kMinStructSize) + return false; + if (listen->bytes < connect.dataBytes) + return false; + + // Validate connect server type + if (!(connect.srvType == kSrvTypeAuth || connect.srvType == kSrvTypeGame)) + return false; + + ZEROPTR(connectPtr); + MemCopy(connectPtr, &connect, min(sizeof(*connectPtr), connect.dataBytes)); + + listen->bytesProcessed += connect.dataBytes; + return true; +} + +#endif // SERVER diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Score/pnNpSrv2Score.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Score/pnNpSrv2Score.h new file mode 100644 index 00000000..5794c3c6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Score/pnNpSrv2Score.h @@ -0,0 +1,169 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Score/pnNpSrv2Score.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2SCORE_PNNPSRV2SCORE_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Score/pnNpSrv2Score.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2SCORE_PNNPSRV2SCORE_H + + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + +// kNetProtocolSrv2Score messages +enum { + kSrv2Score_ScoreCreate = 0, + kSrv2Score_ScoreGetScores = 1, + kSrv2Score_ScoreAddPoints = 2, + kSrv2Score_ScoreTransferPoints = 3, + kSrv2Score_ScoreSetPoints = 4, + kSrv2Score_ScoreDelete = 5, + kSrv2Score_ScoreGetRanks = 6, +}; + +enum { + kScore2Srv_ScoreCreateReply = 0, + kScore2Srv_ScoreGetScoresReply = 1, + kScore2Srv_ScoreDeleteReply = 2, + kScore2Srv_ScoreGetRanksReply = 3, +}; + + +/***************************************************************************** +* +* Srv2Score connect packet +* +***/ + +struct Srv2Score_ConnData { + dword dataBytes; + dword buildId; + dword srvType; +}; +struct Srv2Score_Connect { + AsyncSocketConnectPacket hdr; + Srv2Score_ConnData data; +}; + + +/***************************************************************************** +* +* Srv2Score message structures +* +***/ + +struct Srv2Score_ScoreCreate : SrvMsgHeader { + dword ownerId; + wchar gameName[kMaxGameScoreNameLength]; + dword gameType; + dword value; +}; + +struct Srv2Score_ScoreDelete : SrvMsgHeader { + dword scoreId; +}; + +struct Srv2Score_ScoreGetScores : SrvMsgHeader { + dword ownerId; + wchar gameName[kMaxGameScoreNameLength]; +}; + +struct Srv2Score_ScoreAddPoints : SrvMsgHeader { + dword scoreId; + dword numPoints; +}; + +struct Srv2Score_ScoreTransferPoints : SrvMsgHeader { + dword srcScoreId; + dword destScoreId; + dword numPoints; +}; + +struct Srv2Score_ScoreSetPoints : SrvMsgHeader { + dword scoreId; + dword numPoints; +}; + +struct Srv2Score_ScoreGetRanks : SrvMsgHeader { + dword ownerId; + dword scoreGroup; + dword parentFolderId; + wchar gameName[kMaxGameScoreNameLength]; + dword timePeriod; + dword numResults; + dword pageNumber; + dword sortDesc; +}; + + +/***************************************************************************** +* +* Score2Srv message structures +* +***/ + +struct Score2Srv_ScoreCreateReply : SrvMsgHeader { + dword scoreId; + dword createdTime; +}; + +struct Score2Srv_ScoreGetScoresReply : SrvMsgHeader { + dword scoreCount; + dword byteCount; + byte buffer[1]; +}; + +struct Score2Srv_ScoreGetRanksReply : SrvMsgHeader { + dword rankCount; + dword byteCount; + byte buffer[1]; +}; + + +//============================================================================ +// END PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Srv2Score functions +* +***/ + +bool Srv2ScoreValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2Score_ConnData * connectPtr +); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2State/pnNpSrv2State.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2State/pnNpSrv2State.cpp new file mode 100644 index 00000000..e83bcf44 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2State/pnNpSrv2State.cpp @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2State/pnNpSrv2State.cpp +* +***/ + +#ifdef SERVER + +#define USES_PROTOCOL_SRV2STATE + +#include "../../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +bool Srv2StateValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2State_ConnData * connectPtr +) { + // Ensure that there are enough bytes for the header + const Srv2State_ConnData & connect = * (const Srv2State_ConnData *) listen->buffer; + + // Validate message size + const unsigned kMinStructSize = sizeof(dword) * 3; + if (listen->bytes < kMinStructSize) + return false; + if (listen->bytes < connect.dataBytes) + return false; + + // Validate connect server type + if (!(connect.srvType == kSrvTypeAuth || connect.srvType == kSrvTypeGame)) + return false; + + ZEROPTR(connectPtr); + MemCopy(connectPtr, &connect, min(sizeof(*connectPtr), connect.dataBytes)); + + listen->bytesProcessed += connect.dataBytes; + return true; +} + +#endif // SERVER diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2State/pnNpSrv2State.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2State/pnNpSrv2State.h new file mode 100644 index 00000000..60ec677d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2State/pnNpSrv2State.h @@ -0,0 +1,131 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2State/pnNpSrv2State.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2STATE_PNNPSRV2STATE_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2State/pnNpSrv2State.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2STATE_PNNPSRV2STATE_H + + +// kNetProtocolSrv2State messages +// Because SrvState must remain compatible with older auth builds, these message ids +// must not change unless all front-end servers are synchronously replaced. +enum { + kSrv2State_SaveObject = 0, + kSrv2State_DeleteObject = 1, + kSrv2State_FetchObject = 2, +}; + +enum { + kState2Srv_ObjectFetched = 0, +}; + + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Srv2State connect packet +* +***/ + +struct Srv2State_ConnData { + dword dataBytes; + dword buildId; + dword srvType; +}; +struct Srv2State_Connect { + AsyncSocketConnectPacket hdr; + Srv2State_ConnData data; +}; + + +/***************************************************************************** +* +* Srv2State message structures +* +***/ + +struct Srv2State_FetchObject : SrvMsgHeader { + Uuid ownerId; + wchar objectName[kMaxStateObjectName]; +}; + +struct Srv2State_SaveObject : SrvMsgHeader { + Uuid ownerId; + wchar objectName[kMaxStateObjectName]; + dword objectDataBytes; + byte objectData[1]; // objectData[objectDataBytes], actually + // no more fields after var length alloc +}; + +struct Srv2State_DeleteObject : SrvMsgHeader { + Uuid ownerId; + wchar objectName[kMaxStateObjectName]; +}; + + +/***************************************************************************** +* +* State2Srv message structures +* +***/ + +struct State2Srv_ObjectFetched : SrvMsgHeader { + dword objectDataBytes; + byte objectData[1]; + // no more fields after var length alloc +}; + + +//============================================================================ +// END PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Srv2State functions +* +***/ + +bool Srv2StateValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2State_ConnData * connectPtr +); + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Vault/pnNpSrv2Vault.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Vault/pnNpSrv2Vault.cpp new file mode 100644 index 00000000..8f8f2ce6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Vault/pnNpSrv2Vault.cpp @@ -0,0 +1,71 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Vault/pnNpSrv2Vault.cpp +* +***/ + +#ifdef SERVER + +#define USES_PROTOCOL_SRV2VAULT +#include "../../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +bool Srv2VaultValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2Vault_ConnData * connectPtr +) { + // Ensure that there are enough bytes for the header + const Srv2Vault_ConnData & connect = * (const Srv2Vault_ConnData *) listen->buffer; + + // Validate message size + const unsigned kMinStructSize = sizeof(dword) * 3; + if (listen->bytes < kMinStructSize) + return false; + if (listen->bytes < connect.dataBytes) + return false; + + // Validate connect server type + if (!(connect.srvType == kSrvTypeAuth || connect.srvType == kSrvTypeGame || connect.srvType == kSrvTypeMcp)) + return false; + + ZEROPTR(connectPtr); + MemCopy(connectPtr, &connect, min(sizeof(*connectPtr), connect.dataBytes)); + + listen->bytesProcessed += connect.dataBytes; + return true; +} + +#endif // SERVER diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Vault/pnNpSrv2Vault.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Vault/pnNpSrv2Vault.h new file mode 100644 index 00000000..6c2b4b9b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Vault/pnNpSrv2Vault.h @@ -0,0 +1,452 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Vault/pnNpSrv2Vault.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2VAULT_PNNPSRV2VAULT_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/Protocols/Srv2Vault/pnNpSrv2Vault.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PROTOCOLS_SRV2VAULT_PNNPSRV2VAULT_H + + +// kNetProtocolSrv2Db messages +// Because SrvVault must remain compatible with older auth builds, these message ids +// must not change unless all front-end servers are synchronously replaced. +enum { + // Player creation + kSrv2Vault_PlayerCreateRequest = 0, + kSrv2Vault_PlayerDeleteRequest = 1, + kSrv2Vault_UpgradeVisitorRequest = 2, + kSrv2Vault_ChangePlayerNameRequest = 3, + + // Account + kSrv2Vault_AccountLoginRequest = 20, + kSrv2Vault_AccountLogout = 21, + + // NodeRefs + kSrv2Vault_FetchChildNodeRefs = 40, + kSrv2Vault_NodeAdd = 41, + kSrv2Vault_NodeRemove = 42, + kSrv2Vault_NodeAdd2 = 43, + kSrv2Vault_NodeRemove2 = 44, + + // Nodes + kSrv2Vault_NodeFetch = 60, + kSrv2Vault_CreateNodeRequest = 61, + kSrv2Vault_NodeSave = 62, + kSrv2Vault_DeleteNodeRequest = 63, + kSrv2Vault_NodeFindRequest = 64, + kSrv2Vault_SendNode = 65, + kSrv2Vault_NodeSave2 = 66, + + // Notification targets + kSrv2Vault_RegisterPlayerVault = 80, + kSrv2Vault_UnregisterPlayerVault = 81, + kSrv2Vault_RegisterAgeVault = 82, + kSrv2Vault_UnregisterAgeVault = 83, + kSrv2Vault_AgeInitRequest = 84, + + // Public ages + kSrv2Vault_GetPublicAgeList = 100, + kSrv2Vault_SetAgePublic = 101, + kSrv2Vault_CurrentPopulationReply = 102, + // MCP Notifications + kSrv2Vault_AccountOnline = 140, + kSrv2Vault_AccountOffline = 141, + kSrv2Vault_PlayerOnline = 142, + kSrv2Vault_PlayerOffline = 143, + kSrv2Vault_AgeOnline = 144, + kSrv2Vault_AgeOffline = 145, + kSrv2Vault_PlayerJoinedAge = 146, + kSrv2Vault_PlayerLeftAge = 147, +}; + +enum { + // Player creation + kVault2Srv_PlayerCreateReply = 0, + kVault2Srv_PlayerDeleteReply = 1, + kVault2Srv_UpgradeVisitorReply = 2, + kVault2Srv_ChangePlayerNameReply = 3, + + // Account + kVault2Srv_AccountLoginReply = 20, + + // NodeRefs + kVault2Srv_NodeRefsFetched = 40, + + // Nodes + kVault2Srv_NodeFetched = 60, + kVault2Srv_NodeCreated = 61, + kVault2Srv_NodeFindReply = 62, + + // Notification + kVault2Srv_NodeChanged = 80, + kVault2Srv_NodeAdded = 81, + kVault2Srv_NodeRemoved = 82, + kVault2Srv_NodeDeleted = 83, + + // Notification targets + kVault2Srv_AgeInitReply = 102, + + // Public ages + kVault2Srv_PublicAgeList = 120, + kVault2Srv_CurrentPopulationRequest = 121, + + // AgeSDL + kVault2Srv_NotifyAgeSDLChanged = 140, +}; + + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Srv2Vault connect packet +* +***/ + +struct Srv2Vault_ConnData { + dword dataBytes; + dword buildId; + dword srvType; +}; +struct Srv2Vault_Connect { + AsyncSocketConnectPacket hdr; + Srv2Vault_ConnData data; +}; + + +/***************************************************************************** +* +* Srv2Vault packets +* +***/ + +struct Srv2Vault_PlayerCreateRequest : SrvMsgHeader { + Uuid accountUuid; + wchar playerName[kMaxPlayerNameLength]; + wchar avatarShape[MAX_PATH]; + wchar friendInvite[MAX_PATH]; + byte explorer; +}; + +struct Srv2Vault_PlayerDeleteRequest : SrvMsgHeader { + Uuid accountId; + dword playerId; +}; + +struct Srv2Vault_UpgradeVisitorRequest : SrvMsgHeader { + Uuid accountId; + dword playerId; +}; + +struct Srv2Vault_AccountLoginRequest : SrvMsgHeader { + Uuid accountUuid; +}; + +struct Srv2Vault_AccountLogout : SrvMsgHeader { + Uuid accountUuid; +}; + +struct Srv2Vault_FetchChildNodeRefs : SrvMsgHeader { + dword parentId; + dword maxDepth; +}; + +struct Srv2Vault_NodeFetch : SrvMsgHeader { + dword nodeId; +}; + +struct Srv2Vault_CreateNodeRequest : SrvMsgHeader { + Uuid accountId; + dword creatorId; + dword nodeBytes; + byte nodeBuffer[1]; +}; + +struct Srv2Vault_DeleteNodeRequest : SrvMsgHeader { + dword nodeId; + unsigned playerCheckId; + unsigned isRequestFromAuth; +}; + +struct Srv2Vault_NodeSave : SrvMsgHeader { + dword nodeId; + unsigned playerCheckId; + unsigned isRequestFromAuth; + Uuid revisionId; + dword nodeBytes; + byte nodeBuffer[1]; +}; + +struct Srv2Vault_NodeSave2 : SrvMsgHeader { + dword nodeId; + unsigned playerCheckId; + unsigned isRequestFromAuth; + Uuid revisionId; + dword nodeBytes; + byte nodeBuffer[1]; +}; + +struct Srv2Vault_NodeAdd : SrvMsgHeader { + NetVaultNodeRef ref; +}; + +struct Srv2Vault_NodeAdd2 : SrvMsgHeader { + NetVaultNodeRef ref; +}; + +struct Srv2Vault_NodeRemove : SrvMsgHeader { + dword parentId; + dword childId; + unsigned playerCheckId; + unsigned isRequestFromAuth; +}; + +struct Srv2Vault_NodeRemove2 : SrvMsgHeader { + dword parentId; + dword childId; + unsigned playerCheckId; + unsigned isRequestFromAuth; +}; + +struct Srv2Vault_NodeFindRequest : SrvMsgHeader { + // Template node to match + dword nodeBytes; + byte nodeBuffer[1]; // [nodeBytes], actually + // no more fields after var length alloc +}; + +struct Srv2Vault_SendNode : SrvMsgHeader { + dword srcPlayerId; // sender + dword srcNodeId; // sent item + dword dstPlayerId; // recipient +}; + +struct Srv2Vault_RegisterPlayerVault : SrvMsgHeader { + Uuid accountId; + dword playerId; +}; + +struct Srv2Vault_UnregisterPlayerVault : SrvMsgHeader { + Uuid accountId; +}; + +struct Srv2Vault_RegisterAgeVault : SrvMsgHeader { + Uuid accountId; + dword ageId; // age's vault node id +}; + +struct Srv2Vault_UnregisterAgeVault : SrvMsgHeader { + Uuid accountId; +}; + +struct Srv2Vault_AgeInitRequest : SrvMsgHeader { + Uuid accountId; + dword playerId; + Uuid ageInstId; + Uuid parentAgeInstId; + dword ageLanguage; + dword ageSequenceNumber; +// packed fields: + // wchar ageFilename[] + // wchar ageInstName[] + // wchar ageUserName[] + // wchar ageDesc[] +}; + +struct Srv2Vault_GetPublicAgeList : SrvMsgHeader { + wchar ageName[kMaxAgeNameLength]; +}; + +struct Srv2Vault_SetAgePublic : SrvMsgHeader { + dword playerId; + dword ageInfoId; + byte publicOrNot; +}; + +struct Srv2Vault_CurrentPopulationReply : SrvMsgHeader { + dword ageCount; + unsigned agePopulations[1]; // [ageCount], actually + // no more fields after var length alloc +}; + +struct Srv2Vault_ChangePlayerNameRequest : SrvMsgHeader { + Uuid accountId; + dword playerId; + wchar newName[kMaxPlayerNameLength]; +}; + +struct Srv2Vault_AccountOnline : SrvMsgHeader { + Uuid acctId; + dword buildId; + dword authNode; +}; + +struct Srv2Vault_AccountOffline : SrvMsgHeader { + Uuid acctId; + dword buildId; +}; + +struct Srv2Vault_PlayerOnline : SrvMsgHeader { + Uuid acctId; + dword buildId; + dword playerId; +}; + +struct Srv2Vault_PlayerOffline : SrvMsgHeader { + dword playerId; + dword buildId; +}; + +struct Srv2Vault_AgeOnline : SrvMsgHeader { + Uuid ageInstId; + dword buildId; + dword gameNode; +}; + +struct Srv2Vault_AgeOffline : SrvMsgHeader { + Uuid ageInstId; + dword buildId; +}; + +struct Srv2Vault_PlayerJoinedAge : SrvMsgHeader { + dword playerId; + Uuid ageInstId; + dword buildId; +}; + +struct Srv2Vault_PlayerLeftAge : SrvMsgHeader { + dword playerId; + dword buildId; +}; + + + +/***************************************************************************** +* +* Vault2Srv packets +* +***/ + +struct Vault2Srv_PlayerCreateReply : SrvMsgHeader { + dword playerId; +}; + +struct Vault2Srv_AccountLoginReply : SrvMsgHeader { + dword playerInfoCount; + SrvPlayerInfo playerInfos[1]; +}; + +struct Vault2Srv_NodeRefsFetched : SrvMsgHeader { + dword refCount; + NetVaultNodeRef refs[1]; +}; + +struct Vault2Srv_NodeFetched : SrvMsgHeader { + dword nodeBytes; + byte nodeBuffer[1]; +}; + +struct Vault2Srv_NodeCreated : SrvMsgHeader { + dword nodeId; +}; + +struct Vault2Srv_NodeChanged : SrvMsgHeader { + dword nodeId; + Uuid revisionId; + Uuid accountId; // the notify target +}; + +struct Vault2Srv_NodeAdded : SrvMsgHeader { + NetVaultNodeRef ref; + Uuid accountId; // the notify target +}; + +struct Vault2Srv_NodeRemoved : SrvMsgHeader { + dword parentId; + dword childId; + Uuid accountId; // the notify target +}; + +struct Vault2Srv_NodeDeleted : SrvMsgHeader { + dword nodeId; + Uuid accountId; // the notify target +}; + +struct Vault2Srv_NodeFindReply : SrvMsgHeader { + // out: ids of matching nodes + dword nodeIdCount; + dword nodeIds[1]; // [nodeIdCount], actually + // no more fields after var length alloc +}; + +struct Vault2Srv_AgeInitReply : SrvMsgHeader { + dword ageNodeId; + dword ageInfoNodeId; + Uuid accountId; // the requestor +}; + +struct Vault2Srv_PublicAgeList : SrvMsgHeader { + dword ageCount; + NetAgeInfo ages[1]; // [ageCount], actually + // no more fields after var length alloc +}; + +struct Vault2Srv_NotifyAgeSDLChanged : SrvMsgHeader { + wchar ageName[kMaxAgeNameLength]; + Uuid ageInstId; +}; + +struct Vault2Srv_CurrentPopulationRequest : SrvMsgHeader { + dword ageCount; + Uuid ageInstIds[1]; // [ageCount], actually + // no more fields after var length alloc +}; + +//============================================================================ +// END PACKED DATA STRUCTURES +//============================================================================ +#include + + +/***************************************************************************** +* +* Srv2Vault functions +* +***/ + +bool Srv2VaultValidateConnect ( + AsyncNotifySocketListen * listen, + Srv2Vault_ConnData * connectPtr +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpAllIncludes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpAllIncludes.h new file mode 100644 index 00000000..b63e85ff --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpAllIncludes.h @@ -0,0 +1,104 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpAllIncludes.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PNNPALLINCLUDES_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpAllIncludes.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PNNPALLINCLUDES_H + +#if defined(USES_PROTOCOL_CLI2AUTH) || defined(USES_PROTOCOL_CLI2GAME) || defined(USES_PROTOCOL_CLI2CSR) || defined(USES_PROTOCOL_CLI2GATEKEEPER) +# define USES_NETCLI +#endif + +#if defined(USES_PROTOCOL_SRV2VAULT) || defined(USES_PROTOCOL_SRV2DB) || defined(USES_PROTOCOL_SRV2MCP) || defined(USES_PROTOCOL_SRV2STATE) || defined(USES_PROTOCOL_SRV2LOG) || defined(USES_PROTOCOL_SRV2SCORE) +# define USES_NETSRV +#endif + +#include "pnNpCommon.h" + + + +#ifdef USES_PROTOCOL_CLI2FILE +# include "Protocols/Cli2File/pnNpCli2File.h" +#endif + + +#ifdef USES_NETCLI +# ifdef USES_PROTOCOL_CLI2AUTH +# include "Protocols/Cli2Auth/pnNpCli2Auth.h" +# endif + +# ifdef USES_PROTOCOL_CLI2GAME +# include "Protocols/Cli2Game/pnNpCli2Game.h" +# endif + +# ifdef USES_PROTOCOL_CLI2CSR +# include "Protocols/Cli2Csr/pnNpCli2Csr.h" +# endif + +# ifdef USES_PROTOCOL_CLI2GATEKEEPER +# include "Protocols/Cli2GateKeeper/pnNpCli2GateKeeper.h" +# endif +#endif // def USES_NETCLI + + +#ifdef SERVER +# ifdef USES_NETSRV +// for SrvMsgHeader definition +# include "../../NucleusLib/pnIni/pnIni.h" // psSrvConn needs ini types +# include "../../ServerLib/psUtils/psUtils.h" + +# ifdef USES_PROTOCOL_SRV2VAULT +# include "Protocols/Srv2Vault/pnNpSrv2Vault.h" +# endif + +# ifdef USES_PROTOCOL_SRV2DB +# include "Protocols/Srv2Db/pnNpSrv2Db.h" +# endif + +# ifdef USES_PROTOCOL_SRV2MCP +# include "Protocols/Srv2Mcp/pnNpSrv2Mcp.h" +# endif + +# ifdef USES_PROTOCOL_SRV2STATE +# include "Protocols/Srv2State/pnNpSrv2State.h" +# endif + +# ifdef USES_PROTOCOL_SRV2SCORE +# include "Protocols/Srv2Score/pnNpSrv2Score.h" +# endif + +# ifdef USES_PROTOCOL_SRV2LOG +# include "Protocols/Srv2Log/pnNpSrv2Log.h" +# endif + +# endif // def USES_NETSRV +#endif // def SERVER diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpCommon.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpCommon.cpp new file mode 100644 index 00000000..660632f6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpCommon.cpp @@ -0,0 +1,1201 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpCommon.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + + +namespace pnNpCommon { + +// Verify our qword constants were properly inited as such. +COMPILER_ASSERT(NetVaultNode::kBlob_2); + + + +/***************************************************************************** +* +* Local data +* +***/ + +const unsigned kNumBlobFields = 4; + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//============================================================================ +template +static inline void IReadValue (T * value, byte ** buffer, unsigned * bufsz) { + ASSERT(*bufsz >= sizeof(T)); + *value = *(T *)*buffer; + *buffer += sizeof(T); + *bufsz -= sizeof(T); +} + +//============================================================================ +template +static inline void IReadArray (T ** buf, unsigned * elems, byte ** buffer, unsigned * bufsz) { + dword bytes; + IReadValue(&bytes, buffer, bufsz); + ASSERT(bytes % sizeof(T) == 0); + *elems = bytes / sizeof(T); + T * src = (T *)*buffer; + DEL(*buf); + *buf = (T *)ALLOC(bytes); + MemCopy(*buf, src, bytes); + *buffer += bytes; + *bufsz -= bytes; +} + +//============================================================================ +template +static inline void IReadString (T ** buf, byte ** buffer, unsigned * bufsz) { + unsigned elems; + IReadArray(buf, &elems, buffer, bufsz); + // ensure the string is null-terminated + if (elems) + (*buf)[elems-1] = 0; +} + +//============================================================================ +template +static inline void IWriteValue (const T & value, ARRAY(byte) * buffer) { + T * ptr = (T *) buffer->New(sizeof(T)); + *ptr = value; +} + +//============================================================================ +template +static inline void IWriteArray (const T buf[], unsigned elems, ARRAY(byte) * buffer) { + unsigned bytes = elems * sizeof(T); + IWriteValue(bytes, buffer); + T * dst = (T *) buffer->New(bytes); + MemCopy(dst, buf, bytes); +} + +//============================================================================ +template +static inline void IWriteString (const T str[], ARRAY(byte) * buffer) { + IWriteArray(str, StrLen(str) + 1, buffer); +} + +//============================================================================ +template +static inline bool ICompareValue (const T & lhs, const T & rhs) { + return lhs == rhs; +} + +//============================================================================ +template +static inline bool ICompareString (const T lhs[], const T rhs[]) { + if (!lhs && !rhs) + return true; + if (!lhs || !rhs) + return false; + return 0 == StrCmp(lhs, rhs); +} + +//============================================================================ +template +static inline bool ICompareStringI (const T lhs[], const T rhs[]) { + if (!lhs && !rhs) + return true; + if (!lhs || !rhs) + return false; + return 0 == StrCmpI(lhs, rhs); +} + +//============================================================================ +static inline bool ICompareArray (const byte lhs[], const byte rhs[]) { + ref(lhs); + ref(rhs); + return false; +} + +//============================================================================ +template +static inline void ICopyValue (T * plhs, const T & rhs) { + *plhs = rhs; +} + +//============================================================================ +template +static inline void ICopyString (T ** plhs, const T rhs[]) { + FREE(*plhs); + if (rhs) + *plhs = StrDup(rhs); + else + *plhs = StrDup(""); +} + +//============================================================================ +static inline void ICopyString (wchar ** plhs, const wchar rhs[]) { + FREE(*plhs); + if (rhs) + *plhs = StrDup(rhs); + else + *plhs = StrDup(L""); +} + +//============================================================================ +template +static bool IStrSqlEscape (const T src[], T * dst, unsigned dstChars) { + + // count the number of ' chars + unsigned ticks = 0; + { + const T * cur = src; + while (*cur) { + if (*cur == L'\'') + ++ticks; + cur++; + } + } + + unsigned reqChars = StrLen(src) + ticks + 1; + + if (dstChars < reqChars) + // failure! + return false; + + T * cur = dst; + + // copy src to dst, escaping ' chars + while (*src) { + if (*src == L'\'') { + *cur++ = L'\''; + *cur++ = *src++; + continue; + } + *cur++ = *src++; + } + + // null-terminate dst string + *cur = 0; + + // success! + return true; +} + + +} using namespace pnNpCommon; + + +/***************************************************************************** +* +* NetGameScore +* +***/ + +//============================================================================ +unsigned NetGameScore::Read(const byte inbuffer[], unsigned bufsz, byte** end) { + + byte * buffer = const_cast(inbuffer); + byte * start = buffer; + + wchar* tempstr = nil; + + IReadValue(&scoreId, &buffer, &bufsz); + IReadValue(&ownerId, &buffer, &bufsz); + IReadValue(&createdTime, &buffer, &bufsz); + IReadValue(&gameType, &buffer, &bufsz); + IReadValue(&value, &buffer, &bufsz); + IReadString(&tempstr, &buffer, &bufsz); + + StrCopy(gameName, tempstr, arrsize(gameName)); + DEL(tempstr); + + if (end) + *end = buffer; + + return buffer - start; +} + +//============================================================================ +unsigned NetGameScore::Write(ARRAY(byte) * buffer) const { + + unsigned pos = buffer->Count(); + + IWriteValue(scoreId, buffer); + IWriteValue(ownerId, buffer); + IWriteValue(createdTime, buffer); + IWriteValue(gameType, buffer); + IWriteValue(value, buffer); + IWriteString(gameName, buffer); + + return buffer->Count() - pos; +} + +//============================================================================ +void NetGameScore::CopyFrom(const NetGameScore & score) { + scoreId = score.scoreId; + ownerId = score.ownerId; + createdTime = score.createdTime; + gameType = score.gameType; + value = score.value; + StrCopy(gameName, score.gameName, arrsize(gameName)); +} + +/***************************************************************************** +* +* NetGameRank +* +***/ + +//============================================================================ +unsigned NetGameRank::Read(const byte inbuffer[], unsigned bufsz, byte** end) { + + byte * buffer = const_cast(inbuffer); + byte * start = buffer; + + wchar* tempstr = nil; + + IReadValue(&rank, &buffer, &bufsz); + IReadValue(&score, &buffer, &bufsz); + IReadString(&tempstr, &buffer, &bufsz); + + StrCopy(name, tempstr, arrsize(name)); + DEL(tempstr); + + if (end) + *end = buffer; + + return buffer - start; +} + +//============================================================================ +unsigned NetGameRank::Write(ARRAY(byte) * buffer) const { + + unsigned pos = buffer->Count(); + + IWriteValue(rank, buffer); + IWriteValue(score, buffer); + IWriteString(name, buffer); + + return buffer->Count() - pos; +} + +//============================================================================ +void NetGameRank::CopyFrom(const NetGameRank & fromRank) { + rank = fromRank.rank; + score = fromRank.score; + StrCopy(name, fromRank.name, arrsize(name)); +} + +/***************************************************************************** +* +* NetVaultNode +* +***/ + +//============================================================================ +static void DeallocNodeFields (NetVaultNode * node) { + for (qword fieldFlag = 1; fieldFlag; fieldFlag <<= 1) { + if (fieldFlag > node->fieldFlags) + break; + + #define DELFIELD(f, v) case (qword)(NetVaultNode::f): DEL(node->v); node->v = nil; break + switch (fieldFlag & node->fieldFlags) { + DELFIELD(kCreateAgeName, createAgeName); + DELFIELD(kString64_1, string64_1); + DELFIELD(kString64_2, string64_2); + DELFIELD(kString64_3, string64_3); + DELFIELD(kString64_4, string64_4); + DELFIELD(kString64_5, string64_5); + DELFIELD(kString64_6, string64_6); + DELFIELD(kIString64_1, istring64_1); + DELFIELD(kIString64_2, istring64_2); + DELFIELD(kText_1, text_1); + DELFIELD(kText_2, text_2); + DELFIELD(kBlob_1, blob_1); + DELFIELD(kBlob_2, blob_2); + default: break; + } + } +} + +//============================================================================ +NetVaultNode::NetVaultNode () { + ASSERTMSG(!fieldFlags, "NetVaultNode instances must be allocated with NEWZERO"); +} + +//============================================================================ +NetVaultNode::~NetVaultNode () { + DeallocNodeFields(this); +} + +//============================================================================ +unsigned NetVaultNode::Read_LCS (const byte inbuffer[], unsigned bufsz, unsigned rwOpts) { + + DeallocNodeFields(this); + + byte * buffer = const_cast(inbuffer); + byte * start = buffer; + + IReadValue(&fieldFlags, &buffer, &bufsz); + + for (qword bit = 1; bit; bit <<= 1) { + + // if we've passed all fields on the node then bail + if (bit > fieldFlags) + break; + + // if this field isn't in the set to be read, then continue to next + if (!(bit & fieldFlags)) + continue; + + #define READ(flag, func, varptr) case flag: func(varptr, &buffer, &bufsz); break + #define READARR(flag, func, varptr, lenptr) case flag: func(varptr, lenptr, &buffer, &bufsz); break + switch (bit) { + READ(kNodeId, IReadValue, &nodeId); + READ(kCreateTime, IReadValue, &createTime); + READ(kModifyTime, IReadValue, &modifyTime); + READ(kCreateAgeName,IReadString, &createAgeName); + READ(kCreateAgeUuid,IReadValue, &createAgeUuid); + READ(kCreatorAcct, IReadValue, &creatorAcct); + READ(kCreatorId, IReadValue, &creatorId); + READ(kNodeType, IReadValue, &nodeType); + READ(kInt32_1, IReadValue, &int32_1); + READ(kInt32_2, IReadValue, &int32_2); + READ(kInt32_3, IReadValue, &int32_3); + READ(kInt32_4, IReadValue, &int32_4); + READ(kUInt32_1, IReadValue, &uint32_1); + READ(kUInt32_2, IReadValue, &uint32_2); + READ(kUInt32_3, IReadValue, &uint32_3); + READ(kUInt32_4, IReadValue, &uint32_4); + READ(kUuid_1, IReadValue, &uuid_1); + READ(kUuid_2, IReadValue, &uuid_2); + READ(kUuid_3, IReadValue, &uuid_3); + READ(kUuid_4, IReadValue, &uuid_4); + READ(kString64_1, IReadString, &string64_1); + READ(kString64_2, IReadString, &string64_2); + READ(kString64_3, IReadString, &string64_3); + READ(kString64_4, IReadString, &string64_4); + READ(kString64_5, IReadString, &string64_5); + READ(kString64_6, IReadString, &string64_6); + READ(kIString64_1, IReadString, &istring64_1); + READ(kIString64_2, IReadString, &istring64_2); + READ(kText_1, IReadString, &text_1); + READ(kText_2, IReadString, &text_2); + READARR(kBlob_1, IReadArray, &blob_1, &blob_1Length); + READARR(kBlob_2, IReadArray, &blob_2, &blob_2Length); + DEFAULT_FATAL(bit); + } + #undef READARR + #undef READ + } + + if (rwOpts & kRwUpdateDirty) + dirtyFlags = fieldFlags; + else + dirtyFlags = 0; + + return buffer - start; +} + +//============================================================================ +unsigned NetVaultNode::Write_LCS (ARRAY(byte) * buffer, unsigned rwOpts) { + + unsigned pos = buffer->Count(); + + qword flags = fieldFlags; + + if (rwOpts & kRwDirtyOnly) + flags &= dirtyFlags; + + if (!flags) + return 0; + + IWriteValue(flags, buffer); + + for (qword bit = 1; bit; bit <<= 1) { + + // if we've passed all fields on the node then bail + if (bit > flags) + break; + + // if this field isn't in the set to be written, then continue to next + if (!(bit & flags)) + continue; + + #define WRITE(flag, func, var) case flag: func(var, buffer); break + #define WRITEARR(flag, func, var, len) case flag: func(var, len, buffer); break + switch (bit) { + WRITE(kNodeId, IWriteValue, nodeId ); + WRITE(kCreateTime, IWriteValue, createTime ); + WRITE(kModifyTime, IWriteValue, modifyTime ); + WRITE(kCreateAgeName, IWriteString, createAgeName ? createAgeName : L"" ); + WRITE(kCreateAgeUuid, IWriteValue, createAgeUuid ); + WRITE(kCreatorAcct, IWriteValue, creatorAcct ); + WRITE(kCreatorId, IWriteValue, creatorId ); + WRITE(kNodeType, IWriteValue, nodeType ); + WRITE(kInt32_1, IWriteValue, int32_1 ); + WRITE(kInt32_2, IWriteValue, int32_2 ); + WRITE(kInt32_3, IWriteValue, int32_3 ); + WRITE(kInt32_4, IWriteValue, int32_4 ); + WRITE(kUInt32_1, IWriteValue, uint32_1 ); + WRITE(kUInt32_2, IWriteValue, uint32_2 ); + WRITE(kUInt32_3, IWriteValue, uint32_3 ); + WRITE(kUInt32_4, IWriteValue, uint32_4 ); + WRITE(kUuid_1, IWriteValue, uuid_1 ); + WRITE(kUuid_2, IWriteValue, uuid_2 ); + WRITE(kUuid_3, IWriteValue, uuid_3 ); + WRITE(kUuid_4, IWriteValue, uuid_4 ); + WRITE(kString64_1, IWriteString, string64_1 ? string64_1 : L"" ); + WRITE(kString64_2, IWriteString, string64_2 ? string64_2 : L"" ); + WRITE(kString64_3, IWriteString, string64_3 ? string64_3 : L"" ); + WRITE(kString64_4, IWriteString, string64_4 ? string64_4 : L"" ); + WRITE(kString64_5, IWriteString, string64_5 ? string64_5 : L"" ); + WRITE(kString64_6, IWriteString, string64_6 ? string64_6 : L"" ); + WRITE(kIString64_1, IWriteString, istring64_1 ? istring64_1 : L"" ); + WRITE(kIString64_2, IWriteString, istring64_2 ? istring64_2 : L"" ); + WRITE(kText_1, IWriteString, text_1 ? text_1 : L"" ); + WRITE(kText_2, IWriteString, text_2 ? text_2 : L"" ); + WRITEARR(kBlob_1, IWriteArray, blob_1, blob_1Length); + WRITEARR(kBlob_2, IWriteArray, blob_2, blob_2Length); + DEFAULT_FATAL(bit); + } + #undef WRITEARR + #undef WRITE + } + + if (rwOpts & kRwUpdateDirty) + dirtyFlags = 0; + // else, preserve existing dirtyFlags value + + return buffer->Count() - pos; +} + +//============================================================================ +bool NetVaultNode::Matches (const NetVaultNode * other) { + for (qword bit = 1; bit; bit <<= 1) { + // if bit has gone past all set fields on the other node without failing, return true + if (bit > other->fieldFlags) + return true; + + // if the other node does not have the field, then continue to next field + if (!(bit & other->fieldFlags)) + continue; + + // if we don't have the field (but the other node does), then return false + if (!(bit & fieldFlags)) + return false; + + #define COMPARE(flag, func, var) case flag: if (!func(var, other->var)) return false; break + switch (bit) { + COMPARE(kNodeId, ICompareValue, nodeId); + COMPARE(kCreateTime, ICompareValue, createTime); + COMPARE(kModifyTime, ICompareValue, modifyTime); + COMPARE(kCreateAgeName, ICompareStringI, createAgeName); + COMPARE(kCreateAgeUuid, ICompareValue, createAgeUuid); + COMPARE(kCreatorAcct, ICompareValue, creatorAcct); + COMPARE(kCreatorId, ICompareValue, creatorId); + COMPARE(kNodeType, ICompareValue, nodeType); + COMPARE(kInt32_1, ICompareValue, int32_1); + COMPARE(kInt32_2, ICompareValue, int32_2); + COMPARE(kInt32_3, ICompareValue, int32_3); + COMPARE(kInt32_4, ICompareValue, int32_4); + COMPARE(kUInt32_1, ICompareValue, uint32_1); + COMPARE(kUInt32_2, ICompareValue, uint32_2); + COMPARE(kUInt32_3, ICompareValue, uint32_3); + COMPARE(kUInt32_4, ICompareValue, uint32_4); + COMPARE(kUuid_1, ICompareValue, uuid_1); + COMPARE(kUuid_2, ICompareValue, uuid_2); + COMPARE(kUuid_3, ICompareValue, uuid_3); + COMPARE(kUuid_4, ICompareValue, uuid_4); + COMPARE(kString64_1, ICompareString, string64_1); + COMPARE(kString64_2, ICompareString, string64_2); + COMPARE(kString64_3, ICompareString, string64_3); + COMPARE(kString64_4, ICompareString, string64_4); + COMPARE(kString64_5, ICompareString, string64_5); + COMPARE(kString64_6, ICompareString, string64_6); + COMPARE(kIString64_1, ICompareStringI, istring64_1); + COMPARE(kIString64_2, ICompareStringI, istring64_2); + COMPARE(kText_1, ICompareString, text_1); + COMPARE(kText_2, ICompareString, text_2); + COMPARE(kBlob_1, ICompareArray, blob_1); + COMPARE(kBlob_2, ICompareArray, blob_2); + DEFAULT_FATAL(bit); + } + #undef COMPARE + } + return true; +} + +//============================================================================ +void NetVaultNode::CopyFrom (const NetVaultNode * other, unsigned copyOpts) { + if (this == other) + return; + + qword origDirtyFlags = dirtyFlags; + + for (qword bit = 1; bit; bit <<= 1) { + // we already have a value for this field... + if (bit & fieldFlags) { + if (!(copyOpts & kCopyOverwrite)) + continue; // don't overwrite our field value + } + + // other does not have a value for this field... + if (!(bit & other->fieldFlags)) { + // clear our field? + if (!(copyOpts & kCopyClear)) + continue; + // clear our field. + if (bit & fieldFlags) { + #define _ZERO(flag, func, var, z) case flag: func(bit, this, &var, z); break + #define _ZEROSTRING(flag, func, var, z) case flag: func(bit, this, &var, z, kMaxVaultNodeStringLength); break + #define _ZEROCLOB(flag, func, var, z) case flag: func(bit, this, &var, z, (unsigned)-1); break + #define _ZEROARR(flag, func, var, varlen) case flag: func(bit, this, &var, &varlen, nil, 0); break + switch (bit) { + _ZERO(kNodeId, IVaultNodeSetValue, nodeId, (unsigned)0); + _ZERO(kCreateTime, IVaultNodeSetValue, createTime, (unsigned)0); + _ZERO(kModifyTime, IVaultNodeSetValue, modifyTime, (unsigned)0); + _ZEROSTRING(kCreateAgeName, IVaultNodeSetString, createAgeName, L""); + _ZERO(kCreateAgeUuid, IVaultNodeSetValue, createAgeUuid, kNilGuid); + _ZERO(kCreatorAcct, IVaultNodeSetValue, creatorAcct, kNilGuid); + _ZERO(kCreatorId, IVaultNodeSetValue, creatorId, (unsigned)0); + _ZERO(kNodeType, IVaultNodeSetValue, nodeType, (unsigned)0); + _ZERO(kInt32_1, IVaultNodeSetValue, int32_1, (signed)0); + _ZERO(kInt32_2, IVaultNodeSetValue, int32_2, (signed)0); + _ZERO(kInt32_3, IVaultNodeSetValue, int32_3, (signed)0); + _ZERO(kInt32_4, IVaultNodeSetValue, int32_4, (signed)0); + _ZERO(kUInt32_1, IVaultNodeSetValue, uint32_1, (unsigned)0); + _ZERO(kUInt32_2, IVaultNodeSetValue, uint32_2, (unsigned)0); + _ZERO(kUInt32_3, IVaultNodeSetValue, uint32_3, (unsigned)0); + _ZERO(kUInt32_4, IVaultNodeSetValue, uint32_4, (unsigned)0); + _ZERO(kUuid_1, IVaultNodeSetValue, uuid_1, kNilGuid); + _ZERO(kUuid_2, IVaultNodeSetValue, uuid_2, kNilGuid); + _ZERO(kUuid_3, IVaultNodeSetValue, uuid_3, kNilGuid); + _ZERO(kUuid_4, IVaultNodeSetValue, uuid_4, kNilGuid); + _ZEROSTRING(kString64_1, IVaultNodeSetString, string64_1, L""); + _ZEROSTRING(kString64_2, IVaultNodeSetString, string64_2, L""); + _ZEROSTRING(kString64_3, IVaultNodeSetString, string64_3, L""); + _ZEROSTRING(kString64_4, IVaultNodeSetString, string64_4, L""); + _ZEROSTRING(kString64_5, IVaultNodeSetString, string64_5, L""); + _ZEROSTRING(kString64_6, IVaultNodeSetString, string64_6, L""); + _ZEROSTRING(kIString64_1, IVaultNodeSetString, istring64_1, L""); + _ZEROSTRING(kIString64_2, IVaultNodeSetString, istring64_2, L""); + _ZEROCLOB(kText_1, IVaultNodeSetString, text_1, L""); + _ZEROCLOB(kText_2, IVaultNodeSetString, text_2, L""); + _ZEROARR(kBlob_1, IVaultNodeSetBlob, blob_1, blob_1Length); + _ZEROARR(kBlob_2, IVaultNodeSetBlob, blob_2, blob_2Length); + DEFAULT_FATAL(bit); + } + #undef _ZEROARR + #undef _ZEROCLOB + #undef _ZEROSTRING + #undef _ZERO + } + } + + #define COPY(flag, func, var) case flag: func(bit, this, &var, other->var); break + #define COPYSTRING(flag, func, var) case flag: func(bit, this, &var, other->var, kMaxVaultNodeStringLength); break + #define COPYCLOB(flag, func, var) case flag: func(bit, this, &var, other->var, (unsigned)-1); break + #define COPYARR(flag, func, var, varlen) case flag: func(bit, this, &var, &varlen, other->var, other->varlen); break + switch (bit) { + COPY(kNodeId, IVaultNodeSetValue, nodeId ); + COPY(kCreateTime, IVaultNodeSetValue, createTime ); + COPY(kModifyTime, IVaultNodeSetValue, modifyTime ); + COPYSTRING(kCreateAgeName, IVaultNodeSetString, createAgeName ); + COPY(kCreateAgeUuid, IVaultNodeSetValue, createAgeUuid ); + COPY(kCreatorAcct, IVaultNodeSetValue, creatorAcct ); + COPY(kCreatorId, IVaultNodeSetValue, creatorId ); + COPY(kNodeType, IVaultNodeSetValue, nodeType ); + COPY(kInt32_1, IVaultNodeSetValue, int32_1 ); + COPY(kInt32_2, IVaultNodeSetValue, int32_2 ); + COPY(kInt32_3, IVaultNodeSetValue, int32_3 ); + COPY(kInt32_4, IVaultNodeSetValue, int32_4 ); + COPY(kUInt32_1, IVaultNodeSetValue, uint32_1 ); + COPY(kUInt32_2, IVaultNodeSetValue, uint32_2 ); + COPY(kUInt32_3, IVaultNodeSetValue, uint32_3 ); + COPY(kUInt32_4, IVaultNodeSetValue, uint32_4 ); + COPY(kUuid_1, IVaultNodeSetValue, uuid_1 ); + COPY(kUuid_2, IVaultNodeSetValue, uuid_2 ); + COPY(kUuid_3, IVaultNodeSetValue, uuid_3 ); + COPY(kUuid_4, IVaultNodeSetValue, uuid_4 ); + COPYSTRING(kString64_1, IVaultNodeSetString, string64_1 ); + COPYSTRING(kString64_2, IVaultNodeSetString, string64_2 ); + COPYSTRING(kString64_3, IVaultNodeSetString, string64_3 ); + COPYSTRING(kString64_4, IVaultNodeSetString, string64_4 ); + COPYSTRING(kString64_5, IVaultNodeSetString, string64_5 ); + COPYSTRING(kString64_6, IVaultNodeSetString, string64_6 ); + COPYSTRING(kIString64_1, IVaultNodeSetString, istring64_1 ); + COPYSTRING(kIString64_2, IVaultNodeSetString, istring64_2 ); + COPYCLOB(kText_1, IVaultNodeSetString, text_1 ); + COPYCLOB(kText_2, IVaultNodeSetString, text_2 ); + COPYARR(kBlob_1, IVaultNodeSetBlob, blob_1, blob_1Length); + COPYARR(kBlob_2, IVaultNodeSetBlob, blob_2, blob_2Length); + DEFAULT_FATAL(bit); + } + #undef COPYARR + #undef COPYCLOB + #undef COPYSTRING + #undef COPY + } + + if (!(copyOpts & kCopySetDirty)) + dirtyFlags = origDirtyFlags; +} + +//============================================================================ +void NetVaultNode::SetNodeId (unsigned v) { + IVaultNodeSetValue(kNodeId, this, &nodeId, v); +} + +//============================================================================ +void NetVaultNode::SetCreateTime (unsigned v) { + IVaultNodeSetValue(kCreateTime, this, &createTime, v); +} + +//============================================================================ +void NetVaultNode::SetModifyTime (unsigned v) { + IVaultNodeSetValue(kModifyTime, this, &modifyTime, v); +} + +//============================================================================ +void NetVaultNode::SetCreateAgeName (const wchar v[]) { + IVaultNodeSetString(kCreateAgeName, this, &createAgeName, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void NetVaultNode::SetCreateAgeUuid (const Uuid & v) { + IVaultNodeSetValue(kCreateAgeUuid, this, &createAgeUuid, v); +} + +//============================================================================ +void NetVaultNode::SetCreatorAcct (const Uuid & v) { + IVaultNodeSetValue(kCreatorAcct, this, &creatorAcct, v); +} + +//============================================================================ +void NetVaultNode::SetCreatorId (unsigned v) { + IVaultNodeSetValue(kCreatorId, this, &creatorId, v); +} + +//============================================================================ +void NetVaultNode::SetNodeType (unsigned v) { + IVaultNodeSetValue(kNodeType, this, &nodeType, v); +} + +//============================================================================ +void NetVaultNode::SetInt32_1 (int v) { + IVaultNodeSetValue(kInt32_1, this, &int32_1, v); +} + +//============================================================================ +void NetVaultNode::SetInt32_2 (int v) { + IVaultNodeSetValue(kInt32_2, this, &int32_2, v); +} + +//============================================================================ +void NetVaultNode::SetInt32_3 (int v) { + IVaultNodeSetValue(kInt32_3, this, &int32_3, v); +} + +//============================================================================ +void NetVaultNode::SetInt32_4 (int v) { + IVaultNodeSetValue(kInt32_4, this, &int32_4, v); +} + +//============================================================================ +void NetVaultNode::SetUInt32_1 (unsigned v) { + IVaultNodeSetValue(kUInt32_1, this, &uint32_1, v); +} + +//============================================================================ +void NetVaultNode::SetUInt32_2 (unsigned v) { + IVaultNodeSetValue(kUInt32_2, this, &uint32_2, v); +} + +//============================================================================ +void NetVaultNode::SetUInt32_3 (unsigned v) { + IVaultNodeSetValue(kUInt32_3, this, &uint32_3, v); +} + +//============================================================================ +void NetVaultNode::SetUInt32_4 (unsigned v) { + IVaultNodeSetValue(kUInt32_4, this, &uint32_4, v); +} + +//============================================================================ +void NetVaultNode::SetUuid_1 (const Uuid & v) { + IVaultNodeSetValue(kUuid_1, this, &uuid_1, v); +} + +//============================================================================ +void NetVaultNode::SetUuid_2 (const Uuid & v) { + IVaultNodeSetValue(kUuid_2, this, &uuid_2, v); +} + +//============================================================================ +void NetVaultNode::SetUuid_3 (const Uuid & v) { + IVaultNodeSetValue(kUuid_3, this, &uuid_3, v); +} + +//============================================================================ +void NetVaultNode::SetUuid_4 (const Uuid & v) { + IVaultNodeSetValue(kUuid_4, this, &uuid_4, v); +} + +//============================================================================ +void NetVaultNode::SetString64_1 (const wchar v[]) { + IVaultNodeSetString(kString64_1, this, &string64_1, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void NetVaultNode::SetString64_2 (const wchar v[]) { + IVaultNodeSetString(kString64_2, this, &string64_2, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void NetVaultNode::SetString64_3 (const wchar v[]) { + IVaultNodeSetString(kString64_3, this, &string64_3, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void NetVaultNode::SetString64_4 (const wchar v[]) { + IVaultNodeSetString(kString64_4, this, &string64_4, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void NetVaultNode::SetString64_5 (const wchar v[]) { + IVaultNodeSetString(kString64_5, this, &string64_5, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void NetVaultNode::SetString64_6 (const wchar v[]) { + IVaultNodeSetString(kString64_6, this, &string64_6, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void NetVaultNode::SetIString64_1 (const wchar v[]) { + IVaultNodeSetString(kIString64_1, this, &istring64_1, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void NetVaultNode::SetIString64_2 (const wchar v[]) { + IVaultNodeSetString(kIString64_2, this, &istring64_2, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void NetVaultNode::SetText_1 (const wchar v[]) { + IVaultNodeSetString(kText_1, this, &text_1, v, (unsigned)-1); +} + +//============================================================================ +void NetVaultNode::SetText_2 (const wchar v[]) { + IVaultNodeSetString(kText_2, this, &text_2, v, (unsigned)-1); +} + +//============================================================================ +void NetVaultNode::SetBlob_1 (const byte v[], unsigned len) { + IVaultNodeSetBlob(kBlob_1, this, &blob_1, &blob_1Length, v, len); +} + +//============================================================================ +void NetVaultNode::SetBlob_2 (const byte v[], unsigned len) { + IVaultNodeSetBlob(kBlob_2, this, &blob_2, &blob_2Length, v, len); +} + +//============================================================================ +void NetVaultNode::SetText (qword fieldFlag, const wchar v[]) { + switch (fieldFlag) { + case kText_1: SetText_1(v); break; + case kText_2: SetText_2(v); break; + DEFAULT_FATAL(fieldFlag); + } +} + +//============================================================================ +void NetVaultNode::SetBlob (qword fieldFlag, const byte v[], unsigned len) { + switch (fieldFlag) { + case kBlob_1: SetBlob_1(v, len); break; + case kBlob_2: SetBlob_2(v, len); break; + DEFAULT_FATAL(fieldFlag); + } +} + + +/***************************************************************************** +* +* NetVaultNodeFieldArray +* +***/ + +//============================================================================ +NetVaultNodeFieldArray::NetVaultNodeFieldArray (NetVaultNode * node) +: node(node) +{ + node->IncRef("FieldArray"); + fields.Add(Field(&node->nodeId, L"NodeId")); + fields.Add(Field(&node->createTime, L"CreateTime")); + fields.Add(Field(&node->modifyTime, L"ModifyTime")); + fields.Add(Field(&node->createAgeName, L"CreateAgeName")); + fields.Add(Field(&node->createAgeUuid, L"CreateAgeUuid")); + fields.Add(Field(&node->creatorAcct, L"CreatorAcctId")); + fields.Add(Field(&node->creatorId, L"CreatorId")); + fields.Add(Field(&node->nodeType, L"NodeType")); + fields.Add(Field(&node->int32_1, L"Int32_1")); + fields.Add(Field(&node->int32_2, L"Int32_2")); + fields.Add(Field(&node->int32_3, L"Int32_3")); + fields.Add(Field(&node->int32_4, L"Int32_4")); + fields.Add(Field(&node->uint32_1, L"UInt32_1")); + fields.Add(Field(&node->uint32_2, L"UInt32_2")); + fields.Add(Field(&node->uint32_3, L"UInt32_3")); + fields.Add(Field(&node->uint32_4, L"UInt32_4")); + fields.Add(Field(&node->uuid_1, L"Uuid_1")); + fields.Add(Field(&node->uuid_2, L"Uuid_2")); + fields.Add(Field(&node->uuid_3, L"Uuid_3")); + fields.Add(Field(&node->uuid_4, L"Uuid_4")); + fields.Add(Field(&node->string64_1, L"String64_1")); + fields.Add(Field(&node->string64_2, L"String64_2")); + fields.Add(Field(&node->string64_3, L"String64_3")); + fields.Add(Field(&node->string64_4, L"String64_4")); + fields.Add(Field(&node->string64_5, L"String64_5")); + fields.Add(Field(&node->string64_6, L"String64_6")); + fields.Add(Field(&node->istring64_1, L"IString64_1")); + fields.Add(Field(&node->istring64_2, L"IString64_2")); + fields.Add(Field(&node->text_1, L"Text_1")); + fields.Add(Field(&node->text_2, L"Text_2")); + fields.Add(Field(&node->blob_1, L"Blob_1")); + fields.Add(Field(&node->blob_2, L"Blob_2")); +} + +//============================================================================ +NetVaultNodeFieldArray::~NetVaultNodeFieldArray () { + fields.Clear(); + node->DecRef("FieldArray"); +} + +//============================================================================ +void * NetVaultNodeFieldArray::GetFieldAddress (qword bit) { + ASSERT(bit); + + unsigned index = 0; + for (qword b = bit; b > 1; b >>= 1) + ++index; + + // do not return blob fields + if (index < fields.Count() - kNumBlobFields) + return fields[index].addr; + else + return nil; +} + +//============================================================================ +const wchar * NetVaultNodeFieldArray::GetFieldName (qword bit) { + ASSERT(bit); + + unsigned index = 0; + for (qword b = bit; b > 1; b >>= 1) + ++index; + + ASSERT(index < fields.Count()); + return fields[index].name; +} + +//============================================================================ +void NetVaultNodeFieldArray::GetFieldValueString_LCS ( + qword bit, + wchar * dst, + unsigned dstChars +) { + void * fieldAddr = GetFieldAddress(bit); + + switch (bit) { + case NetVaultNode::kNodeId: + case NetVaultNode::kCreatorId: + case NetVaultNode::kCreateTime: + case NetVaultNode::kModifyTime: + case NetVaultNode::kNodeType: + case NetVaultNode::kUInt32_1: + case NetVaultNode::kUInt32_2: + case NetVaultNode::kUInt32_3: + case NetVaultNode::kUInt32_4: + StrPrintf(dst, dstChars, L"%u", *(unsigned *)fieldAddr); + break; + + case NetVaultNode::kInt32_1: + case NetVaultNode::kInt32_2: + case NetVaultNode::kInt32_3: + case NetVaultNode::kInt32_4: + StrPrintf(dst, dstChars, L"%i", *(int *)fieldAddr); + break; + + case NetVaultNode::kCreateAgeUuid: + case NetVaultNode::kCreatorAcct: + case NetVaultNode::kUuid_1: + case NetVaultNode::kUuid_2: + case NetVaultNode::kUuid_3: + case NetVaultNode::kUuid_4: { + wchar tmp[64]; + GuidToHex(*(Uuid *)fieldAddr, tmp, arrsize(tmp)); + StrPrintf(dst, dstChars, L"hextoraw('%s')", tmp); + } + break; + + case NetVaultNode::kCreateAgeName: + case NetVaultNode::kString64_1: + case NetVaultNode::kString64_2: + case NetVaultNode::kString64_3: + case NetVaultNode::kString64_4: + case NetVaultNode::kString64_5: + case NetVaultNode::kString64_6: + case NetVaultNode::kIString64_1: + case NetVaultNode::kIString64_2: { + wchar * tmp = ALLOCA(wchar, dstChars); + IStrSqlEscape(*(wchar **)fieldAddr, tmp, dstChars); + StrPrintf(dst, dstChars, L"'%s'", tmp); + } + break; + +// FIELD(Text_1); +// FIELD(Text_2); +// FIELD(Blob_1); +// FIELD(Blob_2); + DEFAULT_FATAL(bit); + } +} + +//============================================================================ +void NetVaultNodeFieldArray::BuildWhereClause_LCS ( + EWhereCondition condition, + wchar * dst, + unsigned dstChars +) { + if (!dstChars) + return; + + dst[0] = 0; + + static const wchar * s_conditionStrs[] = { + L" AND ", + L" OR " + }; + + unsigned fieldCount = 0; + for (qword bit = 1; bit; bit <<= 1) { + if (!(bit & node->fieldFlags)) + continue; + + if (fieldCount++) + StrPack(dst, s_conditionStrs[condition], dstChars); + + wchar str[256]; + GetFieldValueString_LCS(bit, str, arrsize(str)); + + StrPack(dst, GetFieldName(bit), dstChars); + StrPack(dst, L"=", dstChars); + StrPack(dst, str, dstChars); + } +} + +//============================================================================ +NetVaultNodeFieldArray::ESqlType NetVaultNodeFieldArray::GetSqlType_LCS (qword bit) { + switch (bit) { + case NetVaultNode::kNodeId: + case NetVaultNode::kCreatorId: + case NetVaultNode::kCreateTime: + case NetVaultNode::kModifyTime: + case NetVaultNode::kNodeType: + case NetVaultNode::kUInt32_1: + case NetVaultNode::kUInt32_2: + case NetVaultNode::kUInt32_3: + case NetVaultNode::kUInt32_4: + return kSqlUInt32; + + case NetVaultNode::kInt32_1: + case NetVaultNode::kInt32_2: + case NetVaultNode::kInt32_3: + case NetVaultNode::kInt32_4: + return kSqlInt32; + + case NetVaultNode::kCreateAgeUuid: + case NetVaultNode::kCreatorAcct: + case NetVaultNode::kUuid_1: + case NetVaultNode::kUuid_2: + case NetVaultNode::kUuid_3: + case NetVaultNode::kUuid_4: + return kSqlUuid; + + case NetVaultNode::kCreateAgeName: + case NetVaultNode::kString64_1: + case NetVaultNode::kString64_2: + case NetVaultNode::kString64_3: + case NetVaultNode::kString64_4: + case NetVaultNode::kString64_5: + case NetVaultNode::kString64_6: + case NetVaultNode::kIString64_1: + case NetVaultNode::kIString64_2: + return kSqlString; + + case NetVaultNode::kText_1: + case NetVaultNode::kText_2: + return kSqlCLob; + +// case NetVaultNode::kBlob_1: +// case NetVaultNode::kBlob_1: +// return kSqlBlob: + + default: + return kSqlInvalid; + } +} + + +/***************************************************************************** +* +* CSrvPackBuffer +* +***/ + +//============================================================================ +CSrvPackBuffer::CSrvPackBuffer (unsigned bytes) { + m_data = (byte *)ALLOC(bytes); + m_pos = m_data; + m_end = m_pos + bytes; +} + +//============================================================================ +void * CSrvPackBuffer::Alloc (unsigned bytes) { + ASSERT((signed) bytes >= 0); + ASSERT(m_pos + bytes <= m_end); + + byte * pos = m_pos; + m_pos += bytes; + return pos; +} + +//============================================================================ +void CSrvPackBuffer::AddData (const void * ptr, unsigned bytes) { + MemCopy(Alloc(bytes), ptr, bytes); +} + +//============================================================================ +void CSrvPackBuffer::AddString (const wchar str[]) { + AddData(str, StrBytes(str)); +} + +//============================================================================ +void CSrvPackBuffer::AddDWordArray (const dword * arr, unsigned count) { + // Don't let large counts cause pointer wrap + count &= 0x00ffffff; + AddData(arr, count * sizeof(arr[0])); +} + +//============================================================================ +void CSrvPackBuffer::AddDWordArray (const unsigned * arr, unsigned count) { + COMPILER_ASSERT(sizeof(unsigned) == sizeof(dword)); + // Don't let large counts cause pointer wrap + count &= 0x00ffffff; + AddData(arr, count * sizeof(arr[0])); +} + +//============================================================================ +unsigned CSrvPackBuffer::Size () { + return m_pos - m_data; +} + + +/***************************************************************************** +* +* CSrvUnpackBuffer +* +***/ + +//============================================================================ +CSrvUnpackBuffer::CSrvUnpackBuffer (const void * buffer, unsigned count) { + m_pos = (const byte *) buffer; + m_end = m_pos + count; +} + +//============================================================================ +const void * CSrvUnpackBuffer::GetData (unsigned bytes) { + for (;;) { + const byte * result = m_pos; + m_pos += bytes; + + if (m_pos < result) + break; + if (m_pos > m_end) + break; + + return result; + } + + m_end = nil; + return nil; +} + +//============================================================================ +const wchar * CSrvUnpackBuffer::GetString () { + + if (m_end) { + const wchar * end = (const wchar *) (m_end - sizeof(wchar) + 1); + for (const wchar * cur = (const wchar *) m_pos; cur < end; ) { + if (*cur++) + continue; + + const wchar * pos = (const wchar *) m_pos; + m_pos = (const byte *) cur; + return pos; + } + } + + m_end = NULL; + return NULL; +} + +//============================================================================ +const dword * CSrvUnpackBuffer::GetDWordArray (unsigned count) { + // Don't let large counts cause pointer wrap + if (count & 0x00ffffff) + return (const dword *)GetData(count * sizeof(dword)); + + m_end = nil; + return nil; +} + +//============================================================================ +unsigned CSrvUnpackBuffer::BytesLeft () { + return m_end ? m_end - m_pos : 0; +} + +//============================================================================ +bool CSrvUnpackBuffer::ParseError () { + return !m_end; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpCommon.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpCommon.h new file mode 100644 index 00000000..1bdbb34a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpCommon.h @@ -0,0 +1,483 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpCommon.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PNNPCOMMON_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/Private/pnNpCommon.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PRIVATE_PNNPCOMMON_H + + +/***************************************************************************** +* +* Client message field types +* +***/ + +#ifdef USES_NETCLI + +const NetMsgField kNetMsgFieldAccountName = NET_MSG_FIELD_STRING(kMaxAccountNameLength); +const NetMsgField kNetMsgFieldPlayerName = NET_MSG_FIELD_STRING(kMaxPlayerNameLength); +const NetMsgField kNetMsgFieldShaDigest = NET_MSG_FIELD_RAW_DATA(sizeof(ShaDigest)); +const NetMsgField kNetMsgFieldUuid = NET_MSG_FIELD_DATA(sizeof(Uuid)); +const NetMsgField kNetMsgFieldTransId = NET_MSG_FIELD_DWORD(); +const NetMsgField kNetMsgFieldTimeMs = NET_MSG_FIELD_DWORD(); +const NetMsgField kNetMsgFieldENetError = NET_MSG_FIELD_DWORD(); +const NetMsgField kNetMsgFieldEAgeId = NET_MSG_FIELD_DWORD(); +const NetMsgField kNetMsgFieldNetNode = NET_MSG_FIELD_DWORD(); +const NetMsgField kNetMsgFieldBuildId = NET_MSG_FIELD_DWORD(); + +#endif + + +/***************************************************************************** +* +* Player information structures +* +***/ + +#include +struct SrvPlayerInfo { + unsigned playerInt; + wchar playerName[kMaxPlayerNameLength]; + wchar avatarShape[kMaxVaultNodeStringLength]; + unsigned explorer; +}; +#include + + +/***************************************************************************** +* +* NetAgeInfo +* +***/ + +struct NetAgeInfo { + Uuid ageInstId; + wchar ageFilename[kMaxAgeNameLength]; + wchar ageInstName[kMaxAgeNameLength]; + wchar ageUserName[kMaxAgeNameLength]; + wchar ageDesc[1024]; + dword ageSequenceNumber; + dword ageLanguage; + dword population; // only used with GetPublicAgeList query results + dword currentPopulation; // only used with GetPublicAgeList query results +}; + +/***************************************************************************** +* +* NetGameScore +* +***/ + +struct NetGameScore { + unsigned scoreId; + unsigned ownerId; + UInt32 createdTime; + wchar gameName[kMaxGameScoreNameLength]; + unsigned gameType; + int value; + + unsigned Read (const byte inbuffer[], unsigned bufsz, byte** end = nil); // returns number of bytes read + unsigned Write (ARRAY(byte) * buffer) const; // returns number of bytes written + + void CopyFrom (const NetGameScore & score); +}; + +/***************************************************************************** +* +* NetGameRank +* +***/ + +struct NetGameRank { + unsigned rank; + int score; + wchar name[kMaxPlayerNameLength]; + + unsigned Read (const byte inbuffer[], unsigned bufsz, byte** end = nil); // returns number of bytes read + unsigned Write (ARRAY(byte) * buffer) const; // returns number of bytes written + + void CopyFrom (const NetGameRank & fromRank); +}; + +/***************************************************************************** +* +* Server vault structures +* +***/ + +//============================================================================ +// NetVaultNode +//============================================================================ +// Threaded apps: App is responsible for locking node->critsect before accessing *any* field in this struct +struct NetVaultNode : AtomicRef { + enum RwOptions { + kRwDirtyOnly = 1<<0, // READ : No meaning + // WRITE: Only write fields marked dirty + kRwUpdateDirty = 1<<1, // READ : Set dirty flag on fields read from stream + // WRITE: Clear dirty flag on fields written to stream + }; + + enum CopyOptions { + kCopySetDirty = 1<<0, // set dirty flag on changed dst fields + kCopyOverwrite = 1<<1, // overwrite fields for which dst node already has values + kCopyClear = 1<<2, // clear dst fields for which src node does not have values + }; + + // These flag values must not change unless all servers are + // simultaneously replaced, so basically forget it. + static const qword kNodeId = (qword)1<< 0; + static const qword kCreateTime = (qword)1<< 1; + static const qword kModifyTime = (qword)1<< 2; + static const qword kCreateAgeName = (qword)1<< 3; + static const qword kCreateAgeUuid = (qword)1<< 4; + static const qword kCreatorAcct = (qword)1<< 5; + static const qword kCreatorId = (qword)1<< 6; + static const qword kNodeType = (qword)1<< 7; + static const qword kInt32_1 = (qword)1<< 8; + static const qword kInt32_2 = (qword)1<< 9; + static const qword kInt32_3 = (qword)1<<10; + static const qword kInt32_4 = (qword)1<<11; + static const qword kUInt32_1 = (qword)1<<12; + static const qword kUInt32_2 = (qword)1<<13; + static const qword kUInt32_3 = (qword)1<<14; + static const qword kUInt32_4 = (qword)1<<15; + static const qword kUuid_1 = (qword)1<<16; + static const qword kUuid_2 = (qword)1<<17; + static const qword kUuid_3 = (qword)1<<18; + static const qword kUuid_4 = (qword)1<<19; + static const qword kString64_1 = (qword)1<<20; + static const qword kString64_2 = (qword)1<<21; + static const qword kString64_3 = (qword)1<<22; + static const qword kString64_4 = (qword)1<<23; + static const qword kString64_5 = (qword)1<<24; + static const qword kString64_6 = (qword)1<<25; + static const qword kIString64_1 = (qword)1<<26; + static const qword kIString64_2 = (qword)1<<27; + // blobs always come last + static const qword kText_1 = (qword)1<<28; + static const qword kText_2 = (qword)1<<29; + static const qword kBlob_1 = (qword)1<<30; + static const qword kBlob_2 = (qword)1<<31; + + CCritSect critsect; + + qword fieldFlags; + qword dirtyFlags; + + Uuid revisionId; + + // Treat these as read-only or node flag fields will become invalid + // Threaded apps: Must be accessed with node->critsect locked + unsigned nodeId; + unsigned createTime; + unsigned modifyTime; + wchar * createAgeName; + Uuid createAgeUuid; + Uuid creatorAcct; // accountId of node creator + unsigned creatorId; // playerId of node creator + unsigned nodeType; + int int32_1; + int int32_2; + int int32_3; + int int32_4; + unsigned uint32_1; + unsigned uint32_2; + unsigned uint32_3; + unsigned uint32_4; + Uuid uuid_1; + Uuid uuid_2; + Uuid uuid_3; + Uuid uuid_4; + wchar * string64_1; + wchar * string64_2; + wchar * string64_3; + wchar * string64_4; + wchar * string64_5; + wchar * string64_6; + wchar * istring64_1; + wchar * istring64_2; + wchar * text_1; + wchar * text_2; + byte * blob_1; unsigned blob_1Length; + byte * blob_2; unsigned blob_2Length; + + NetVaultNode (); + ~NetVaultNode (); + + // Threaded apps: Must be called with node->critsect locked + unsigned Read_LCS (const byte buffer[], unsigned bufsz, unsigned rwOpts); // returns number of bytes read + unsigned Write_LCS (ARRAY(byte) * buffer, unsigned rwOpts); // returns number of bytes written + + bool Matches (const NetVaultNode * other); + void CopyFrom (const NetVaultNode * other, unsigned copyOpts); + + // Threaded apps: Must be called with node->critsect locked + void SetNodeId (unsigned v); + void SetCreateTime (unsigned v); + void SetModifyTime (unsigned v); + void SetCreateAgeName (const wchar v[]); + void SetCreateAgeUuid (const Uuid & v); + void SetCreatorAcct (const Uuid & v); + void SetCreatorId (unsigned v); + void SetNodeType (unsigned v); + void SetInt32_1 (int v); + void SetInt32_2 (int v); + void SetInt32_3 (int v); + void SetInt32_4 (int v); + void SetUInt32_1 (unsigned v); + void SetUInt32_2 (unsigned v); + void SetUInt32_3 (unsigned v); + void SetUInt32_4 (unsigned v); + void SetUuid_1 (const Uuid & v); + void SetUuid_2 (const Uuid & v); + void SetUuid_3 (const Uuid & v); + void SetUuid_4 (const Uuid & v); + void SetString64_1 (const wchar v[]); + void SetString64_2 (const wchar v[]); + void SetString64_3 (const wchar v[]); + void SetString64_4 (const wchar v[]); + void SetString64_5 (const wchar v[]); + void SetString64_6 (const wchar v[]); + void SetIString64_1 (const wchar v[]); + void SetIString64_2 (const wchar v[]); + void SetText_1 (const wchar v[]); + void SetText_2 (const wchar v[]); + void SetBlob_1 (const byte v[], unsigned len); + void SetBlob_2 (const byte v[], unsigned len); + + void SetText (qword fieldFlag, const wchar v[]); + void SetBlob (qword fieldFlag, const byte v[], unsigned len); + + // These are only here to aid macro expansions (naming case matches field flags) + inline unsigned GetNodeId () const { return nodeId; } + inline unsigned GetCreateTime () const { return createTime; } + inline unsigned GetModifyTime () const { return modifyTime; } + inline wchar * GetCreateAgeName () const { return createAgeName; } + inline Uuid GetCreateAgeUuid () const { return createAgeUuid; } + inline Uuid GetCreatorAcct () const { return creatorAcct; } + inline unsigned GetCreatorId () const { return creatorId; } + inline unsigned GetNodeType () const { return nodeType; } + inline int GetInt32_1 () const { return int32_1; } + inline int GetInt32_2 () const { return int32_2; } + inline int GetInt32_3 () const { return int32_3; } + inline int GetInt32_4 () const { return int32_4; } + inline unsigned GetUInt32_1 () const { return uint32_1; } + inline unsigned GetUInt32_2 () const { return uint32_2; } + inline unsigned GetUInt32_3 () const { return uint32_3; } + inline unsigned GetUInt32_4 () const { return uint32_4; } + inline Uuid GetUuid_1 () const { return uuid_1; } + inline Uuid GetUuid_2 () const { return uuid_2; } + inline Uuid GetUuid_3 () const { return uuid_3; } + inline Uuid GetUuid_4 () const { return uuid_4; } + inline wchar * GetString64_1 () const { return string64_1; } + inline wchar * GetString64_2 () const { return string64_2; } + inline wchar * GetString64_3 () const { return string64_3; } + inline wchar * GetString64_4 () const { return string64_4; } + inline wchar * GetString64_5 () const { return string64_5; } + inline wchar * GetString64_6 () const { return string64_6; } + inline wchar * GetIString64_1 () const { return istring64_1; } + inline wchar * GetIString64_2 () const { return istring64_2; } + // no blob "getters" +}; + + +//============================================================================ +inline void IVaultNodeSetString ( + qword bit, + NetVaultNode * node, + char ** pdst, + const char src[], + unsigned chars +) { + FREE(*pdst); + if (src && src[0]) + *pdst = StrDupLen(src, chars); + else + *pdst = StrDupLen("", chars); + node->fieldFlags |= bit; + node->dirtyFlags |= bit; +} + +//============================================================================ +inline void IVaultNodeSetString ( + qword bit, + NetVaultNode * node, + wchar ** pdst, + const wchar src[], + unsigned chars +) { + FREE(*pdst); + if (src && src[0]) + *pdst = StrDupLen(src, chars); + else + *pdst = StrDupLen(L"", chars); + node->fieldFlags |= bit; + node->dirtyFlags |= bit; +} + +//============================================================================ +template +inline void IVaultNodeSetValue ( + qword bit, + NetVaultNode * node, + T * pdst, + const T & src +) { + *pdst = src; + node->fieldFlags |= bit; + node->dirtyFlags |= bit; +} + +//============================================================================ +inline void IVaultNodeSetBlob ( + qword bit, + NetVaultNode * node, + byte ** pdst, + unsigned * pdstLen, + const byte src[], + unsigned srcLen +) { + FREE(*pdst); + if (src) { + *pdst = (byte*)MEMDUP(src, srcLen); + *pdstLen = srcLen; + } + else { + *pdst = nil; + *pdstLen = 0; + } + node->fieldFlags |= bit; + node->dirtyFlags |= bit; +} + + +//============================================================================ +// NetVaultNodeFieldArray +//============================================================================ +struct NetVaultNodeFieldArray { + enum EWhereCondition { + kAnd, + kOr + }; + enum ESqlType { + kSqlInvalid, + kSqlInt32, + kSqlUInt32, + kSqlUuid, + kSqlString, + kSqlCLob, + KSqlBlob, + }; + + struct Field { + void * addr; + const wchar * name; + Field (void * addr, const wchar name[]) + : addr(addr), name(name) + { } + }; + ARRAY(Field) fields; + NetVaultNode * node; + + NetVaultNodeFieldArray (NetVaultNode * node); + ~NetVaultNodeFieldArray (); + + void * GetFieldAddress (qword bit); + const wchar * GetFieldName (qword bit); + + // client must lock node's local critical section before calling these. + void GetFieldValueString_LCS (qword bit, wchar * dst, unsigned dstChars); + void BuildWhereClause_LCS (EWhereCondition condition, wchar * dst, unsigned dstChars); + ESqlType GetSqlType_LCS (qword bit); +}; + + +//============================================================================ +// NetVaultNodeRef (packed because is sent over wire directly) +//============================================================================ +#include +struct NetVaultNodeRef { + unsigned parentId; + unsigned childId; + unsigned ownerId; + bool seen; +}; +#include + +//============================================================================ +// SrvPackBuffer +//============================================================================ + +// Allocate a CSrvPackBuffer on the heap with one extra dword to allow for padding +#define SRV_ALLOC_BUFFER(bytes) \ + new(ALLOC(sizeof(CSrvPackBuffer) + (bytes) + sizeof(dword))) \ + CSrvPackBuffer(bytes + sizeof(dword)) + +// Allocate a CSrvPackBuffer on the stack with one extra dword to allow for padding +#define SRV_ALLOCA_BUFFER(bytes) \ + new(_alloca(sizeof(CSrvPackBuffer) + (bytes) + sizeof(dword))) \ + CSrvPackBuffer(bytes + sizeof(dword)) + +class CSrvPackBuffer { +public: + CSrvPackBuffer (unsigned bytes); + + void * Alloc (unsigned bytes); + void AddData (const void * ptr, unsigned bytes); + void AddString (const wchar str[]); + void AddDWordArray (const dword * arr, unsigned count); + void AddDWordArray (const unsigned * arr, unsigned count); + // add new "Add..." methods here as needed + + unsigned Size (); + +private: + byte * m_pos; + byte * m_end; + byte * m_data; +}; + +class CSrvUnpackBuffer { +public: + CSrvUnpackBuffer (const void * buffer, unsigned count); + + const void * GetData (unsigned bytes); + const wchar * GetString (); + const dword * GetDWordArray (unsigned count); + + unsigned BytesLeft (); + bool ParseError (); + +private: + const byte * m_pos; + const byte * m_end; +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/pnNetProtocol.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/pnNetProtocol.h new file mode 100644 index 00000000..81daaff3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/pnNetProtocol.h @@ -0,0 +1,39 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnNetProtocol/pnNetProtocol.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PNNETPROTOCOL_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PNNETPROTOCOL_H + + +#include "Private/pnNpAllIncludes.h" + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNNETPROTOCOL_PNNETPROTOCOL_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Intern.h new file mode 100644 index 00000000..dc72fcfe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Intern.h @@ -0,0 +1,35 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNORALIB_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNORALIB_INTERN_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Pch.h new file mode 100644 index 00000000..f5b2a198 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Pch.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNORALIB_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNORALIB_PCH_H + + +// NucleusLib +#include "pnUtils/pnUtils.h" +#include "pnProduct/pnProduct.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" +#include "pnNetCli/pnNetCli.h" +#include "pnIni/pnIni.h" + +// OCCI +#define WIN32COMMON +#include "occi.h" + +// Local +#include "pnOraLib.h" +#include "Intern.h" + +// System +#include +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/pnOraLib.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/pnOraLib.cpp new file mode 100644 index 00000000..a3252db2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/pnOraLib.cpp @@ -0,0 +1,552 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/pnOraLib.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +//#define ORACONN_TRACKING +//#define ORASTMT_TRACKING + +#ifdef ORACONN_TRACKING +#pragma message("pnOraLib: Compiling with ORACONN_TRACKING") +#endif +#ifdef ORASTMT_TRACKING +#pragma message("pnOraLib: Compiling with ORASTMT_TRACKING") +#endif + + +/***************************************************************************** +* +* Private +* +***/ + +struct OraInitData { + unsigned OraStmtCacheSize; + wchar OraUsername[128]; + wchar OraPassword[128]; + wchar OraConnectString[256]; + + OraInitData( + unsigned stmtCacheSize, + const wchar username[], + const wchar password[], + const wchar connectString[] + ); +}; + +struct OraEnv { + occi::Environment * occiEnv; + occi::StatelessConnectionPool * occiPool; +}; + +struct OraConnPool { + bool needReinit; + long perf[kOcciNumPerf]; + CCritSect critsect; + OraEnv oraEnv; + OraInitData * data; + + void Initialize_CS (); + void Shutdown_CS (); + void ReInitialize_CS (); + + void Initialize ( + const wchar username[], + const wchar password[], + const wchar connectString[], + unsigned stmtCacheSize + ); + void Shutdown (); + OraConn * GetConn (const wchar tag[]); + void FreeConn (OraConn *& conn); + + long GetPerf (long index); +}; + + +/***************************************************************************** +* +* Private Data +* +***/ + +static const unsigned kDefaultConnTimeoutSecs = 60 * 30; // half-hour +static const unsigned kDefaultConnsPerEnv = 10; + +enum { + kPerfOraConnCount, + kPerfOraStmtCount, + kNumPerf +}; + +static OraConnPool s_connPool; +static long s_perf[kNumPerf]; + + +/***************************************************************************** +* +* Local functions +* +***/ + +/***************************************************************************** +* +* OraInitData +* +***/ + +//============================================================================ +OraInitData::OraInitData ( + unsigned stmtCacheSize, + const wchar username[], + const wchar password[], + const wchar connectString[] +) { + OraStmtCacheSize = stmtCacheSize; + StrCopy(OraUsername, username, arrsize(OraUsername)); + StrCopy(OraPassword, password, arrsize(OraPassword)); + StrCopy(OraConnectString, connectString, arrsize(OraConnectString)); +} + + +/***************************************************************************** +* +* OraConnPool +* +***/ + +//============================================================================ +void OraConnPool::ReInitialize_CS () { + + Shutdown_CS(); + Initialize_CS(); + + needReinit = false; +} + +//============================================================================ +void OraConnPool::Initialize_CS () { + + ASSERT(data); + + try { + const unsigned threads = AsyncThreadTaskGetThreadCount(); + const unsigned maxConns = threads; + const unsigned minConns = 0; + const unsigned incConns = max(1, maxConns - 1); + + // Some memory allocated by createEnvironment is leaked, + // so we disable memory tracking while calling it. + MemPushDisableTracking(); + oraEnv.occiEnv = occi::Environment::createEnvironment( + "OCCIUTF16", + "OCCIUTF16", + occi::Environment::THREADED_MUTEXED, + nil + ); + MemPopDisableTracking(); + + // Create the connection pool + oraEnv.occiPool = oraEnv.occiEnv->createStatelessConnectionPool( + data->OraUsername, + data->OraPassword, + data->OraConnectString, + maxConns, + minConns, + incConns, + occi::StatelessConnectionPool::HOMOGENEOUS + ); + + // Turn on statement caching + oraEnv.occiPool->setStmtCacheSize(data->OraStmtCacheSize); + oraEnv.occiPool->setTimeOut(kDefaultConnTimeoutSecs); + // If no connections are available, block until one becomes available + oraEnv.occiPool->setBusyOption(occi::StatelessConnectionPool::WAIT); + } + catch (exception & e) { + OraLogError(L"OraConnPool::Initialize", e); + ErrorFatal(__LINE__, __FILE__, "Failed to initialize occi connection pool"); + } +} + +//============================================================================ +void OraConnPool::Shutdown_CS () { + + if (oraEnv.occiEnv) { + try { + // If we aren't connected to oracle, then closing the connection + // pool causes oracle to make a pure virtual function call when + // closing the environment. + if (!needReinit) + oraEnv.occiEnv->terminateStatelessConnectionPool(oraEnv.occiPool, occi::StatelessConnectionPool::SPD_FORCE); + occi::Environment::terminateEnvironment(oraEnv.occiEnv); + } + catch (exception & e) { + OraLogError(L"OraConnPool::Shutdown", e); + } + oraEnv.occiEnv = nil; + oraEnv.occiPool = nil; + } +} + +//============================================================================ +void OraConnPool::Initialize ( + const wchar username[], + const wchar password[], + const wchar connectString[], + unsigned stmtCacheSize +) { + data = NEW(OraInitData)(stmtCacheSize, username, password, connectString); + + critsect.Enter(); + { + Initialize_CS(); + } + critsect.Leave(); +} + +//============================================================================ +void OraConnPool::Shutdown () { + + critsect.Enter(); + { + Shutdown_CS(); + } + critsect.Leave(); + + DEL(data); +} + +//============================================================================ +OraConn * OraConnPool::GetConn (const wchar tag[]) { + + OraConn * oraConn = NEWZERO(OraConn); + if (tag) + StrCopy(oraConn->tag, tag, arrsize(oraConn->tag)); + + critsect.Enter(); + for (;;) try { + if (needReinit) { + if (GetPerf(kOcciPerfBusyConns)) + // Wait for all connections to be freed by the app + // before environment reinitialization. + break; + + ReInitialize_CS(); + } + + if (!oraEnv.occiEnv) + break; + + if (nil != (oraConn->occiConn = oraEnv.occiPool->getConnection())) + oraConn->oraEnv = &oraEnv; + + break; + } + catch (exception & e) { + OraLogError(L"OraConnPool::GetConn", e); + oraConn = nil; + break; + } + critsect.Leave(); + + for (;;) { + if (!oraConn || !oraConn->occiConn) { + DEL(oraConn); + oraConn = nil; + LogMsg(kLogError, L"OraConnPool::GetConn: Failed to aquire a database connection"); + break; + } + + const unsigned connCount = AtomicAdd(&s_perf[kPerfOraConnCount], 1) + 1; + ref(connCount); + + #ifdef ORACONN_TRACKING + LogMsg(kLogPerf, L"OraGetConn: %u, %p, %s", connCount, oraConn, oraConn->tag); + #endif + + break; + } + + return oraConn; +} + +//============================================================================ +void OraConnPool::FreeConn (OraConn *& oraConn) { + + if (oraConn) try { + occi::Connection * occiConn = oraConn->occiConn; + occi::StatelessConnectionPool * occiPool = oraConn->oraEnv->occiPool; + + const unsigned connCount = AtomicAdd(&s_perf[kPerfOraConnCount], -1) - 1; + ref(connCount); + +#ifdef ORACONN_TRACKING + LogMsg(kLogPerf, L"OraFreeConn: %u, %p, %s", connCount, oraConn, oraConn->tag); +#endif + + DEL(oraConn); + + try { + occiConn->commit(); + } + catch (exception & e) { + OraLogError(L"OraConnPool::FreeConn [commit]", e); + needReinit = true; + } + + occiPool->releaseConnection(occiConn); + } + catch (exception & e) { + OraLogError(L"OraConnPool::FreeConn [release]", e); + } + + oraConn = nil; +} + +//============================================================================ +long OraConnPool::GetPerf (long index) { + + if (!oraEnv.occiEnv) + return 0; + if (!oraEnv.occiPool) + return 0; + + switch (index) { + case kOcciPerfOpenConns: return oraEnv.occiPool->getOpenConnections(); + case kOcciPerfBusyConns: return oraEnv.occiPool->getBusyConnections(); + DEFAULT_FATAL(index); + } +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void OraLogError (const wchar sql[], const exception & e) { + + wchar buffer[1024]; + const char * tmp = e.what(); + + // Some exceptions we catch are actually unicode strings in a char buffer, but others aren't. + if (tmp[0] && !tmp[1]) { + const wchar * wtmp = (const wchar *)tmp; + StrCopy(buffer, wtmp, arrsize(buffer)); + } + else { + StrToUnicode(buffer, tmp, arrsize(buffer)); + } + LogMsg(kLogError, L"%s, %s", sql, buffer); +} + +//============================================================================ +void OraGetShaDigest ( + occi::Statement * oraStmt, + unsigned index, + ShaDigest * digest +) { + occi::Bytes bytes = oraStmt->getBytes(index); + const unsigned length = bytes.length(); + ASSERT(length == msizeof(ShaDigest, data)); + bytes.getBytes((byte *)digest->data, length); +} + +//============================================================================ +void OraSetShaDigest ( + occi::Statement * oraStmt, + unsigned index, + const ShaDigest & digest +) { + occi::Bytes bytes((byte *)&digest, sizeof(digest)); + oraStmt->setBytes(index, bytes); +} + +//============================================================================ +void OraBindString ( + occi::Statement * oraStmt, + unsigned index, + wchar * buffer, + unsigned chars, + ub2 * length, + sb2 * indicator +) { + oraStmt->setDataBuffer( + index, + buffer, + occi::OCCI_SQLT_STR, + chars * sizeof(buffer[0]), + length, + indicator + ); +} + +//============================================================================ +void OraBindString ( + occi::ResultSet * rs, + unsigned index, + wchar * buffer, + unsigned chars, + ub2 * length, + sb2 * indicator +) { + rs->setDataBuffer( + index, + buffer, + occi::OCCI_SQLT_STR, + chars * sizeof(buffer[0]), + length, + indicator + ); +} + +//============================================================================ +void OraGetUuid ( + occi::Statement * oraStmt, + unsigned index, + Uuid * uuid +) { + occi::Bytes bytes = oraStmt->getBytes(index); + if (const unsigned length = bytes.length()) { + ASSERT(length == msizeof(Uuid, data)); + byte * buf = ALLOCA(byte, length); + bytes.getBytes(buf, length); + GuidFromHex(buf, length, uuid); + } + else { + GuidClear(uuid); + } +} + +//============================================================================ +void OraSetUuid ( + occi::Statement * oraStmt, + unsigned index, + const Uuid & uuid +) { + occi::Bytes bytes((byte *)&uuid, sizeof(uuid)); + oraStmt->setBytes(index, bytes); +} + +//============================================================================ +OraConn * OraGetConn (const wchar tag[]) { + + // grabs a cached connection if available, otherwise creates a new one on-the-fly + return s_connPool.GetConn(tag); +} + +//============================================================================ +void OraFreeConn (OraConn *& oraConn) { + + s_connPool.FreeConn(oraConn); +} + +//============================================================================ +occi::Statement * OraGetStmt ( + OraConn * oraConn, + const wchar sql[] +) { + occi::Statement * oraStmt = nil; + try { + // grabs a matching cached statement if available, otherwise creates a new one + oraStmt = oraConn->occiConn->createStatement(sql); + // auto-commit causes an exception to be thrown when writing blobs: "ORA-01002: fetch out of sequence" + oraStmt->setAutoCommit(false); + + const unsigned stmtCount = AtomicAdd(&s_perf[kPerfOraStmtCount], 1) + 1; + ref(stmtCount); + + #ifdef ORASTMT_TRACKING + LogMsg(kLogPerf, L"OraGetStmt: %u, %p, %s", stmtCount, oraStmt, sql); + #endif + } + catch (exception & e) { + OraLogError(L"OraGetStmt", e); + } + return oraStmt; +} + +//============================================================================ + void OraFreeStmt (occi::Statement *& oraStmt) { + if (oraStmt) { + try { + // caches the statement + oraStmt->getConnection()->terminateStatement(oraStmt); + + const unsigned stmtCount = AtomicAdd(&s_perf[kPerfOraStmtCount], -1) - 1; + ref(stmtCount); + + #ifdef ORASTMT_TRACKING + LogMsg(kLogPerf, L"OraFreeStmt: %u, %p", stmtCount, oraStmt); + #endif + } + catch (exception & e) { + OraLogError(L"OraFreeStmt", e); + } + oraStmt = nil; + } +} + +//============================================================================ +void OraInitialize ( + const wchar username[], + const wchar password[], + const wchar connectString[], + unsigned stmtCacheSize +) { + // Connect to the database + s_connPool.Initialize( + username, + password, + connectString, + stmtCacheSize + ); +} + +//============================================================================ +void OraShutdown () { + +} + +//============================================================================ +void OraDestroy () { + s_connPool.Shutdown(); +} + +//============================================================================ +long OraLibGetOcciPerf (unsigned index) { + return s_connPool.GetPerf(index); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/pnOraLib.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/pnOraLib.h new file mode 100644 index 00000000..ff18cac0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/pnOraLib.h @@ -0,0 +1,128 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnOraLib/pnOraLib.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNORALIB_PNORALIB_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNORALIB_PNORALIB_H + +// OCCI +#define WIN32COMMON +#include "occi.h" +using namespace oracle; +struct OraEnv; + +enum { + kOcciPerfOpenConns, + kOcciPerfBusyConns, + kOcciNumPerf +}; + +long OraLibGetOcciPerf (unsigned index); + + +struct OraConn { +// treat all fields as read-only + OraEnv * oraEnv; + occi::Connection * occiConn; + wchar tag[128]; +}; + + +/***************************************************************************** +* +* OraLib +* +***/ + +void OraLogError (const wchar sql[], const exception & e); + +void OraGetShaDigest ( + occi::Statement * oraStmt, + unsigned index, + ShaDigest * digest +); + +void OraSetShaDigest ( + occi::Statement * oraStmt, + unsigned index, + const ShaDigest & digest +); + +void OraBindString ( + occi::Statement * oraStmt, + unsigned index, + wchar * buffer, + unsigned chars, + ub2 * length, + sb2 * indicator +); + +void OraBindString ( + occi::ResultSet * rs, + unsigned index, + wchar * buffer, + unsigned chars, + ub2 * length, + sb2 * indicator +); + +void OraGetUuid ( + occi::Statement * oraStmt, + unsigned index, + Uuid * uuid +); + +void OraSetUuid ( + occi::Statement * oraStmt, + unsigned index, + const Uuid & uuid +); + +OraConn * OraGetConn (const wchar tag[] = nil); + +void OraFreeConn (OraConn *& oraConn); + +occi::Statement * OraGetStmt ( + OraConn * oraConn, + const wchar sql[] +); + +void OraFreeStmt (occi::Statement *& oraStmt); + +void OraInitialize ( + const wchar username[], + const wchar password[], + const wchar connectString[], + unsigned stmtCacheSize +); +void OraShutdown (); +void OraDestroy (); + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNORALIB_PNORALIB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Pch.h new file mode 100644 index 00000000..76bbcece --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Pch.h @@ -0,0 +1,40 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PCH_H + + +#include "pnUtils/pnUtils.h" + +#include "Private/pnPrAllIncludes.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrAllIncludes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrAllIncludes.h new file mode 100644 index 00000000..cd259d7e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrAllIncludes.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrAllIncludes.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRALLINCLUDES_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRALLINCLUDES_H + + +#include "../pnUtils/pnUtils.h" + +#include "pnPrBuildId.h" +#include "pnPrBuildType.h" +#include "pnPrBranchId.h" +#include "pnPrProductId.h" +#include "pnPrBuildString.h" + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRALLINCLUDES_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBranchId.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBranchId.cpp new file mode 100644 index 00000000..4fdaafd6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBranchId.cpp @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBranchId.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private definitions +* +***/ + +#ifndef PATCHER + +// This value is manually changed upon each branch so that client applications +// built from this branch may not connect to servers built from another. +#define BRANCH_ID 1 +COMPILER_ASSERT(BRANCH_ID != 0); + +#else + +// Patcher branchId may never change, otherwise its CRC would differ from +// branch to branch, causing unnecessary self-patching to occur. +#define BRANCH_ID 0 + +#endif + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +unsigned BranchId () { + return BRANCH_ID; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBranchId.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBranchId.h new file mode 100644 index 00000000..053775ac --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBranchId.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBranchId.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRBRANCHID_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBranchId.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRBRANCHID_H + + +/***************************************************************************** +* +* BranchId functions +* +***/ + +unsigned BranchId (); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildId.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildId.cpp new file mode 100644 index 00000000..cb08e303 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildId.cpp @@ -0,0 +1,71 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildId.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private definitions +* +***/ + +// This line must NEVER be modified manually; it is automatically updated +// by the build server. +#define BUILD_ID 897 +COMPILER_ASSERT(BUILD_ID != 0); + + +/***************************************************************************** +* +* Private data +* +***/ + +static unsigned s_buildIdOverride; + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +unsigned BuildId () { + return s_buildIdOverride ? s_buildIdOverride : BUILD_ID; +} + +//============================================================================ +void OverrideBuildId (unsigned buildId) { + s_buildIdOverride = buildId; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildId.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildId.h new file mode 100644 index 00000000..7907d69d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildId.h @@ -0,0 +1,45 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildId.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRBUILDID_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildId.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRBUILDID_H + + +/***************************************************************************** +* +* BuildId functions +* +***/ + +unsigned BuildId (); +void OverrideBuildId (unsigned buildId); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildString.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildString.cpp new file mode 100644 index 00000000..17c8c8fd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildString.cpp @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildString.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +const wchar * ProductBuildString () { + // This string is replaced by plMarkBuild.exe with the following string: + // "date:YYMMDD time:HHMMSS user:%USERNAME%" + static const wchar PRODUCT_BUILD_STRING[64] = { + L"PRODUCT_BUILD_STRING " + }; + return PRODUCT_BUILD_STRING; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildString.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildString.h new file mode 100644 index 00000000..10160e5a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildString.h @@ -0,0 +1,45 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildString.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRBUILDSTRING_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildString.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRBUILDSTRING_H + + + +/***************************************************************************** +* +* BuildString functions +* +***/ + +const wchar * ProductBuildString (); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildType.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildType.cpp new file mode 100644 index 00000000..096833a7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildType.cpp @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildType.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +unsigned BuildType () { + return BUILD_TYPE; +} + +//============================================================================ +const wchar * BuildTypeString () { + +#if BUILD_TYPE == BUILD_TYPE_DEV + return L"Dev"; +#elif BUILD_TYPE == BUILD_TYPE_QA + return L"QA"; +#elif BUILD_TYPE == BUILD_TYPE_TEST + return L"Test"; +#elif BUILD_TYPE == BUILD_TYPE_BETA + return L"Beta"; +#elif BUILD_TYPE == BUILD_TYPE_LIVE + return L"Live"; +#else +# error "Unknown build type" +#endif + +} + +//============================================================================ +const wchar *BuildTypeServerStatusPath () { + + #if BUILD_TYPE == BUILD_TYPE_DEV + return nil; + #elif BUILD_TYPE == BUILD_TYPE_QA + return nil; + #elif BUILD_TYPE == BUILD_TYPE_TEST + return nil; + #elif BUILD_TYPE == BUILD_TYPE_BETA + return L"/serverstatus/moulbeta.php"; + #elif BUILD_TYPE == BUILD_TYPE_LIVE + return L"/serverstatus/moullive.php"; + #else + # error "Unknown build type" + #endif + +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildType.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildType.h new file mode 100644 index 00000000..878a07d8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildType.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildType.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRBUILDTYPE_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrBuildType.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRBUILDTYPE_H + + +/***************************************************************************** +* +* BuildType definitions +* +***/ +#ifndef BUILD_TYPE +#define BUILD_TYPE BUILD_TYPE_DEV +#endif + +#define BUILD_TYPE_DEV 10 +#define BUILD_TYPE_QA 20 +#define BUILD_TYPE_TEST 30 +#define BUILD_TYPE_BETA 40 +#define BUILD_TYPE_LIVE 50 + + +/***************************************************************************** +* +* BuildType functions +* +***/ + +unsigned BuildType (); +const wchar * BuildTypeString (); +const wchar * BuildTypeServerStatusPath (); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrProductId.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrProductId.cpp new file mode 100644 index 00000000..3b5c3909 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrProductId.cpp @@ -0,0 +1,125 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrProductId.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private data +* +***/ + +namespace Product { + +namespace Uru { + static const wchar kCoreName[] = L"UruLive"; + static const wchar kShortName[] = L"UruLive"; + static const wchar kLongName[] = L"Uru Live"; + static const Uuid kId(L"ea489821-6c35-4bd0-9dae-bb17c585e680"); +} + +// @@@: add your product namespace here + +} + +/***************************************************************************** +* +* Private definitions +* +***/ + +#if BUILD_PRODUCT_ID == BUILD_PRODUCT_ID_URU + +#define PRODUCT_CORE_NAME Product::Uru::kCoreName +#define PRODUCT_SHORT_NAME Product::Uru::kShortName +#define PRODUCT_LONG_NAME Product::Uru::kLongName +#define PRODUCT_ID Product::Uru::kId + +#else + +#error "No product id defined" + +#endif + + + +/***************************************************************************** +* +* Exported functions +* +***/ + +//============================================================================ +const Uuid & ProductId () { + return PRODUCT_ID; +} + +//============================================================================ +const wchar * ProductCoreName () { + return PRODUCT_CORE_NAME; +} + +//============================================================================ +const wchar * ProductShortName () { + return PRODUCT_SHORT_NAME; +} + +//============================================================================ +const wchar * ProductLongName () { + return PRODUCT_LONG_NAME; +} + + +//============================================================================ +void ProductString (wchar * dest, unsigned destChars) { + // Example: "UruLive.Beta.2.214 - External.Release" + StrPrintf( + dest, + destChars, + L"%s.%s.%u.%u - %s.%s", + ProductCoreName(), + BuildTypeString(), + BranchId(), + BuildId(), + #ifdef PLASMA_EXTERNAL_RELEASE + L"External", + #else + L"Internal", + #endif + #ifdef HS_DEBUGGING + L"Debug" + #else + L"Release" + #endif + ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrProductId.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrProductId.h new file mode 100644 index 00000000..96f6a7b8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrProductId.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrProductId.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRPRODUCTID_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/Private/pnPrProductId.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PRIVATE_PNPRPRODUCTID_H + + +/***************************************************************************** +* +* Global data +* +***/ + +//============================================================================ +// Build productIds +// +// These values must never be stored on disk or sent over the network, +// use the value returned from ProductId() for that purpose. +//============================================================================ +#define BUILD_PRODUCT_ID_URU 1 +// @@@ Add your build product id above this line + + + +//============================================================================ +// Default to Uru for lack of a better idea +//============================================================================ +#ifndef BUILD_PRODUCT_ID +# define BUILD_PRODUCT_ID BUILD_PRODUCT_ID_URU +#endif + + + +/***************************************************************************** +* +* Product functions +* +***/ + +const Uuid & ProductId (); +const wchar * ProductCoreName (); // e.g: L"Uru" +const wchar * ProductShortName (); // e.g: L"Uru" (filename/registry friendly) +const wchar * ProductLongName (); // e.g: L"Uru: Ages Beyond Myst" (human friendly) + + +// Returns: "..." +// Example: "Uru.Beta.3.204" +void ProductString (wchar * dest, unsigned destChars); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/pnProduct.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/pnProduct.h new file mode 100644 index 00000000..f3730227 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnProduct/pnProduct.h @@ -0,0 +1,39 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnProduct/pnProduct.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PNPRODUCT_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PNPRODUCT_H + + +#include "Private/pnPrAllIncludes.h" + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNPRODUCT_PNPRODUCT_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plAudioInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plAudioInterface.cpp new file mode 100644 index 00000000..3c8bf773 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plAudioInterface.cpp @@ -0,0 +1,361 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plAudioInterface.h" +#include "plAudible.h" +#include "../pnMessage/plAudioSysMsg.h" +#include "../pnMessage/plSoundMsg.h" +#include "hsBounds.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plKey.h" +#include "plSceneObject.h" +#include "plgDispatch.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plIntRefMsg.h" +#include "plCoordinateInterface.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plProxyDrawMsg.h" +#include "../pnNetCommon/plNetApp.h" +#include "hsTimer.h" + + +plAudioInterface::plAudioInterface() +: fAudible(nil), fAudibleInited( false ) +{ + fRegisteredForASysMsg = false; +} + +plAudioInterface::~plAudioInterface() +{ + +} + +void plAudioInterface::SetProperty(int prop, hsBool on) +{ + plObjInterface::SetProperty(prop, on); + + if( fAudible ) + fAudible->SetProperty(prop, on); +} + +plSound* plAudioInterface::GetSound(int i) const +{ + if( fAudible ) + return fAudible->GetSound(i); + return nil; +} + +int plAudioInterface::GetSoundIndex(const char *keyname) +{ + if( fAudible ) + return fAudible->GetSoundIndex(keyname); + else + return -1; +} + +int plAudioInterface::GetNumSounds() const +{ + if( fAudible ) + return fAudible->GetNumSounds(); + return 0; +} + +// set the filename of sound[index] within the audible +void plAudioInterface::SetSoundFilename(int index, const char *filename, bool isCompressed) +{ + if(fAudible) + fAudible->SetFilename(index, filename, isCompressed); +} + +void plAudioInterface::ISetSceneNode(plKey key) +{ + if( fAudible ) + { + fAudible->SetSceneNode(key); + if( !fAudibleInited ) + { + int isLocal = IsLocallyOwned(); + + plKey localKey = ( plNetClientApp::GetInstance() != nil ) ? plNetClientApp::GetInstance()->GetLocalPlayerKey() : nil; + if( fOwner && fOwner->GetKey() == localKey ) + isLocal = true; + else + isLocal = false; + + fAudible->Init( isLocal );//( isLocal == plSynchedObject::kYes ) ); + fAudibleInited = true; + } + } +} + +void plAudioInterface::ISetOwner(plSceneObject* owner) +{ + plObjInterface::ISetOwner(owner); + + if( owner && !fRegisteredForASysMsg ) + { + plgDispatch::Dispatch()->RegisterForExactType(plAudioSysMsg::Index(), GetKey()); + fRegisteredForASysMsg = true; + } + else if( owner == nil && fRegisteredForASysMsg ) + { + plgDispatch::Dispatch()->UnRegisterForExactType(plAudioSysMsg::Index(), GetKey()); + fRegisteredForASysMsg = false; + } + + if (fAudible) + fAudible->SetSceneObject(owner ? owner->GetKey() : nil); +} + + +void plAudioInterface::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + if( fAudible ) + fAudible->SetTransform(l2w, w2l); +} + +void plAudioInterface::Read(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Read(s, mgr); + + plIntRefMsg* refMsg = TRACKED_NEW plIntRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plIntRefMsg::kAudible); + mgr->ReadKeyNotifyMe(s, refMsg, plRefFlags::kActiveRef); +} + +void plAudioInterface::Write(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Write(s, mgr); + + mgr->WriteKey(s,fAudible); +} + +void plAudioInterface::ISetAudible(plAudible* aud) +{ + fAudible = aud; + if( fAudible ) + { + fAudible->SetSceneNode(GetSceneNode()); + if (fOwner) + fAudible->SetSceneObject(fOwner->GetKey()); + } + + plAudioSysMsg* pMsg = TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kPing ); + pMsg->SetSender(GetKey()); +// pMsg->SetBCastFlag(plMessage::kBCastByExactType, false); + plgDispatch::MsgSend( pMsg ); +} + +void plAudioInterface::IRemoveAudible(plAudible* aud) +{ + hsAssert(aud == fAudible, "Removing Audible I don't have"); + fAudible = nil; +} + +hsBool plAudioInterface::MsgReceive(plMessage* msg) +{ + plIntRefMsg* intRefMsg = plIntRefMsg::ConvertNoRef(msg); + if( intRefMsg ) + { + switch( intRefMsg->fType ) + { + case plIntRefMsg::kAudible: + if( intRefMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + IRemoveAudible(plAudible::ConvertNoRef(intRefMsg->GetRef())); + } + else + { + ISetAudible(plAudible::ConvertNoRef(intRefMsg->GetRef())); + } + return true; + default: + break; + } + } + + plSoundMsg* pSoundMsg = plSoundMsg::ConvertNoRef( msg ); + if (pSoundMsg) + { + if (!fAudible) + return false; + if (pSoundMsg->Cmd( plSoundMsg::kAddCallbacks)) + fAudible->AddCallbacks( pSoundMsg ); + if (pSoundMsg->Cmd( plSoundMsg::kRemoveCallbacks)) + fAudible->RemoveCallbacks( pSoundMsg ); + if (pSoundMsg->Cmd( plSoundMsg::kStop ) ) + fAudible->Stop(pSoundMsg->fIndex); + if (pSoundMsg->Cmd( plSoundMsg::kGoToTime ) ) + fAudible->SetTime(pSoundMsg->fTime, pSoundMsg->fIndex); + if (pSoundMsg->Cmd( plSoundMsg::kPlay ) ) + fAudible->Play(pSoundMsg->fIndex); + if(pSoundMsg->Cmd( plSoundMsg::kSynchedPlay)) + fAudible->SynchedPlay(pSoundMsg->fIndex); + if (pSoundMsg->Cmd( plSoundMsg::kSetLooping ) ) + fAudible->SetLooping(true,pSoundMsg->fIndex); + if (pSoundMsg->Cmd( plSoundMsg::kUnSetLooping ) ) + fAudible->SetLooping(false,pSoundMsg->fIndex); + if (pSoundMsg->Cmd( plSoundMsg::kToggleState ) ) + { + if (fAudible->IsPlaying(pSoundMsg->fIndex)) + fAudible->Stop(pSoundMsg->fIndex); + else + fAudible->Play(pSoundMsg->fIndex); + } + if (pSoundMsg->Cmd( plSoundMsg::kGetStatus ) ) + { + fAudible->GetStatus(pSoundMsg); + } + if (pSoundMsg->Cmd( plSoundMsg::kGetNumSounds ) ) + { + plSoundMsg* pReply = TRACKED_NEW plSoundMsg; + pReply->fIndex = fAudible->GetNumSounds(); + pReply->AddReceiver(pSoundMsg->GetSender()); + pReply->SetCmd( plSoundMsg::kGetNumSounds ); + plgDispatch::MsgSend(pReply); + } + if( pSoundMsg->Cmd( plSoundMsg::kSetVolume ) ) + { + fAudible->SetVolume( pSoundMsg->fVolume, pSoundMsg->fIndex ); + } + if ( pSoundMsg->Cmd( plSoundMsg::kSetTalkIcon ) ) + { + fAudible->SetTalkIcon(pSoundMsg->fIndex, pSoundMsg->fNameStr); + } + if ( pSoundMsg->Cmd( plSoundMsg::kClearTalkIcon ) ) + { + fAudible->ClearTalkIcon(); + } + if ( pSoundMsg->Cmd( plSoundMsg::kSetFadeIn ) ) + { + fAudible->SetFadeIn( (int)pSoundMsg->fFadeType, pSoundMsg->fVolume, pSoundMsg->fIndex ); + } + if ( pSoundMsg->Cmd( plSoundMsg::kSetFadeOut ) ) + { + fAudible->SetFadeOut( (int)pSoundMsg->fFadeType, pSoundMsg->fVolume, pSoundMsg->fIndex ); + } + if( pSoundMsg->Cmd( plSoundMsg::kFastForwardPlay ) ) + { + fAudible->FastForwardPlay(pSoundMsg->fIndex); + } + if(pSoundMsg->Cmd(plSoundMsg::kFastForwardToggle) ) + { + fAudible->FastForwardToggle(pSoundMsg->fIndex); + } + + return true; + } + + + plAudioSysMsg* pASMsg = plAudioSysMsg::ConvertNoRef( msg ); + if (pASMsg) + { + if (pASMsg->GetAudFlag() == plAudioSysMsg::kActivate) + { + if( fAudible ) + { + if( !fAudibleInited ) + { + int isLocal = IsLocallyOwned(); + fAudible->Init( ( isLocal == plSynchedObject::kYes ) ); + fAudibleInited = true; + } + if( !fAudibleInited ) + { + // Arrgh, can't activate yet, so attempt to re-activate some time in the near future + pASMsg = TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kActivate ); + pASMsg->SetBCastFlag( plMessage::kBCastByExactType, false ); + pASMsg->SetBCastFlag( plMessage::kNetPropagate, false ); + pASMsg->SetTimeStamp( hsTimer::GetSysSeconds() + 1.f ); + pASMsg->Send( GetKey() ); + return true; + } + fAudible->Activate(); + if (GetOwner() && GetOwner()->GetCoordinateInterface()) + { + hsMatrix44 l2w = GetOwner()->GetCoordinateInterface()->GetLocalToWorld(); + hsMatrix44 w2l = GetOwner()->GetCoordinateInterface()->GetWorldToLocal();; + fAudible->SetTransform(l2w,w2l); + } + } + } + if (pASMsg->GetAudFlag() == plAudioSysMsg::kDeActivate) + { + if( fAudible ) + fAudible->DeActivate(); + } + if( pASMsg->GetAudFlag() == plAudioSysMsg::kMuteAll ) + { + if( fAudible ) + fAudible->SetMuted( true ); + } + else if( pASMsg->GetAudFlag() == plAudioSysMsg::kUnmuteAll ) + { + if( fAudible ) + fAudible->SetMuted( false ); + } + else if( pASMsg->GetAudFlag() == plAudioSysMsg::kChannelVolChanged ) + { + if( fAudible ) + return fAudible->MsgReceive( msg ); + } + } + + plEnableMsg* pEnableMsg = plEnableMsg::ConvertNoRef( msg ); + if (pEnableMsg) + { + SetProperty( kDisable, pEnableMsg->Cmd(kDisable) ); + return true; + } + + // proxyDrawMsg handling--just pass it on to the audible + plProxyDrawMsg *pdMsg = plProxyDrawMsg::ConvertNoRef( msg ); + if( pdMsg != nil ) + { + if( fAudible ) + return fAudible->MsgReceive( pdMsg ); + + return true; + } + + return plObjInterface::MsgReceive(msg); +} + + +void plAudioInterface::ReleaseData() +{ + if (fAudible) + { + // To get rid of our data, we need to release our active ref and tell the SceneNode + // to dump it. It will autodestruct after those two active refs are released, unless + // someone else has a ref on it as well (in which case we don't want to be nuking it + // anyway). + fAudible->SetSceneNode(nil); + // Audible key is gone already, I guess the audioInterface doesn't have a ref -Colin +// GetKey()->Release(fAudible->GetKey()); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plAudioInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plAudioInterface.h new file mode 100644 index 00000000..b832c865 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plAudioInterface.h @@ -0,0 +1,94 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#ifndef plAudioInterface_inc +#define plAudioInterface_inc + +#include "plObjInterface.h" + +class plSound; +class plAudible; +class hsStream; +class hsResMgr; +struct hsMatrix44; +class hsBounds3Ext; + + +class plAudioInterface : public plObjInterface +{ +public: + // Props inc by 1 (bit shift in bitvector). + enum plAudioProperties { + kDisable = 0, // prop 0 is always disable, declared in plObjInterface + + kNumProps // last in the list + }; +protected: + plAudible* fAudible; // references into system pools + + hsBool fRegisteredForASysMsg, fAudibleInited; + + void ISetAudible(plAudible* aud); + void IRemoveAudible(plAudible* aud); + + virtual void ISetOwner(plSceneObject* owner); + virtual void ISetSceneNode(plKey node); + + friend class plSceneObject; + +public: + plAudioInterface(); + ~plAudioInterface(); + + CLASSNAME_REGISTER( plAudioInterface ); + GETINTERFACE_ANY( plAudioInterface, plObjInterface ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void SetProperty(int prop, hsBool on); + Int32 GetNumProperties() const { return kNumProps; } + + plSound* GetSound(int i) const; + int GetNumSounds() const; + + virtual hsBool MsgReceive(plMessage* msg); + + // for export only!!!!! + plAudible* GetAudible() const { return fAudible; } + /// don't call this otherwise! + + // Transform settable only, if you want it get it from the coordinate interface. + void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual void ReleaseData( void ); + void SetSoundFilename(int index, const char *filename, bool isCompressed); + int GetSoundIndex(const char *keyname); +}; + + +#endif // plAudioInterface_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plCoordinateInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plCoordinateInterface.cpp new file mode 100644 index 00000000..0df4501e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plCoordinateInterface.cpp @@ -0,0 +1,644 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plCoordinateInterface.h" +#include "plDrawInterface.h" +#include "plSimulationInterface.h" +#include "plAudioInterface.h" +#include "../pnMessage/plWarpMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plCorrectionMsg.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../pnNetCommon/plSDLTypes.h" +#include "plSceneObject.h" +#include "hsResMgr.h" +#include "plgDispatch.h" +#include "../pnKeyedObject/plKey.h" +#include "hsStream.h" + +#include "plProfile.h" + +UInt8 plCoordinateInterface::fTransformPhase = plCoordinateInterface::kTransformPhaseNormal; +hsBool plCoordinateInterface::fDelayedTransformsEnabled = true; + +plCoordinateInterface::plCoordinateInterface() +: fParent(nil), + fReason(kReasonUnknown) +{ + fLocalToParent.Reset(); + fParentToLocal.Reset(); + + fLocalToWorld.Reset(); + fWorldToLocal.Reset(); + + fState = 0; +} + +plCoordinateInterface::~plCoordinateInterface() +{ + if( fParent ) + fParent->IRemoveChild(IGetOwner()); + int i; + for( i = fChildren.GetCount()-1; i >= 0; i-- ) + IRemoveChild(i); +} + +void plCoordinateInterface::ISetSceneNode(plKey newNode) +{ + int i; + for( i = 0; i < fChildren.GetCount(); i++ ) + { + if( fChildren[i] ) + fChildren[i]->SetSceneNode(newNode); + } +} + +void plCoordinateInterface::ISetOwner(plSceneObject* so) +{ + plObjInterface::ISetOwner(so); + + IDirtyTransform(); + fReason |= kReasonUnknown; +} + +void plCoordinateInterface::ISetParent(plCoordinateInterface* par) +{ + fParent = par; + + // This won't have any effect if my owner is NetGroupConstant + if( fParent ) + ISetNetGroupRecur(fParent->GetNetGroup()); + + IDirtyTransform(); + fReason |= kReasonUnknown; +} + +plCoordinateInterface* plCoordinateInterface::GetChild(int i) const +{ + return fChildren[i] ? fChildren[i]->GetVolatileCoordinateInterface() : nil; +} + +void plCoordinateInterface::IRemoveChild(int i) +{ + if( fChildren[i] ) + { + plCoordinateInterface* childCI = fChildren[i]->GetVolatileCoordinateInterface(); + if( childCI ) + childCI->ISetParent(nil); + } + fChildren.Remove(i); +} + +void plCoordinateInterface::IRemoveChild(plSceneObject* child) +{ + int idx = fChildren.Find(child); + if( idx != fChildren.kMissingIndex ) + IRemoveChild(idx); +} + +void plCoordinateInterface::ISetChild(plSceneObject* child, int which) +{ + hsAssert(child, "Setting a nil child"); + plCoordinateInterface* childCI = child->GetVolatileCoordinateInterface(); + hsAssert(childCI, "Child with no coordinate interface"); + childCI->ISetParent(this); + + if( which < 0 ) + which = fChildren.GetCount(); + fChildren.ExpandAndZero(which+1); + fChildren[which] = child; + + // If we can't delay our transform update, neither can any of our parents. + if (!childCI->GetProperty(kDelayedTransformEval)) + { + plCoordinateInterface *current = childCI->GetParent(); + while (current) + { + current->SetProperty(kDelayedTransformEval, false); + current = current->GetParent(); + } + } +} + +void plCoordinateInterface::IAddChild(plSceneObject* child) +{ + ISetChild(child, -1); +} + +void plCoordinateInterface::IAttachChild(plSceneObject* child, UInt8 flags) +{ + hsAssert(child, "Attaching a nil child"); + plCoordinateInterface* childCI = child->GetVolatileCoordinateInterface(); + hsAssert(childCI, "Owner without CoordinateInterface being attached"); + + if (childCI->GetParent() == this) + return; // We're already attached! Who told us to do this? + + hsMatrix44 l2w = childCI->GetLocalToWorld(); + hsMatrix44 w2l = childCI->GetWorldToLocal(); + + if( childCI->GetParent() ) + childCI->GetParent()->IDetachChild(child, flags | kAboutToAttach); + + childCI->IUnRegisterForTransformMessage(); + + IAddChild(child); + + if( flags & kMaintainWorldPosition ) + childCI->WarpToWorld(l2w,w2l); +} + +void plCoordinateInterface::IDetachChild(plSceneObject* child, UInt8 flags) +{ + hsAssert(child, "Detaching a nil child"); + plCoordinateInterface* childCI = child->GetVolatileCoordinateInterface(); + hsAssert(childCI, "Owner without CoordinateInterface being attached"); + + hsMatrix44 l2w = childCI->GetLocalToWorld(); + hsMatrix44 w2l = childCI->GetWorldToLocal(); + + GetKey()->Release(child->GetKey()); + if( IGetOwner() && IGetOwner()->GetKey() ) + IGetOwner()->GetKey()->Release(child->GetKey()); + IRemoveChild(child); + + if( flags & kMaintainWorldPosition ) + childCI->WarpToWorld(l2w,w2l); + + // If the child was keeping us from delaying our transform, + // maybe we can, now that it's gone. + if (!childCI->GetProperty(kDelayedTransformEval)) + IUpdateDelayProp(); +} + +/* + * A few notes on the delay transform properties... + * + * The kCanEverDelayTransform prop is independent of any parents/children. + * It means this particular node must always update its transform in response + * to a plTransformMsg. It is intended for objects with physics, because they + * need to be up-to-date before the simulationMgr updates the physical world. + * + * The kDelayedTransformEval prop is for nodes that are free of physics. (So no + * physical descendants either). If the property is set, we won't update our + * transform until AFTER the simulationMgr does its work. + * + * When we attach a child that can't delay its eval (at the moment), we recurse + * up to the root, turning off the kDelayedTransformEval prop as we go. When we + * remove such a child, we check if that child was the only reason we weren't + * delaying our transform. If so, we update ourself and tell our parent to check. + * + * BTW: The POINT of all this is that when we update our l2w transforms because + * we're animated, and then we update AGAIN after a parent node of ours involved + * in physics gets a slight nudge, the first update becomes pointless. The + * delay prop bookkeeping keeps us from doing the wasted calculations. And since + * nearly all bones on the avatar are in this exact situation, it's worth doing. + */ +void plCoordinateInterface::IUpdateDelayProp() +{ + int i; + if (!GetProperty(kCanEverDelayTransform)) + return; + + for (i = 0; i < GetNumChildren(); i++) + { + // If we still have a child that needs the delay... + if (!GetChild(i)->GetProperty(kDelayedTransformEval)) + return; + } + + // Cool, we can delay now, which means maybe our parent can too. + SetProperty(kDelayedTransformEval, true); + if (GetParent()) + GetParent()->IUpdateDelayProp(); +} + +plCoordinateInterface* plCoordinateInterface::IGetRoot() +{ + return fParent ? fParent->IGetRoot() : this; +} + +void plCoordinateInterface::IRegisterForTransformMessage(hsBool delayed) +{ + if( IGetOwner() ) + { + if ((delayed || fTransformPhase == kTransformPhaseDelayed) && fDelayedTransformsEnabled) + plgDispatch::Dispatch()->RegisterForExactType(plDelayedTransformMsg::Index(), IGetOwner()->GetKey()); + else + plgDispatch::Dispatch()->RegisterForExactType(plTransformMsg::Index(), IGetOwner()->GetKey()); + } +} + +void plCoordinateInterface::IUnRegisterForTransformMessage() +{ + if( IGetOwner() ) + plgDispatch::Dispatch()->UnRegisterForExactType(plTransformMsg::Index(), IGetOwner()->GetKey()); +} + + +void plCoordinateInterface::IDirtyTransform() +{ + fState |= kTransformDirty; + + IGetRoot()->IRegisterForTransformMessage(GetProperty(kDelayedTransformEval)); +} + +void plCoordinateInterface::MultTransformLocal(const hsMatrix44& move, const hsMatrix44& invMove) +{ + fReason |= kReasonUnknown; + fLocalToParent = move * fLocalToParent; + fParentToLocal = fParentToLocal * invMove; + + IDirtyTransform(); +} + +void plCoordinateInterface::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + fReason |= kReasonUnknown; + + if( fParent ) + { + SetLocalToParent(fParent->GetWorldToLocal() * l2w, w2l * fParent->GetLocalToWorld()); + } + else + { + SetLocalToParent(l2w, w2l); + } +} + +void plCoordinateInterface::SetTransformPhysical(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + // since we use public interfaces to do the details + // AND those public interfaces could be called by anyone + // AND those public interfaces therefore have to set their own "reason" for the transform change + // THEREFORE: we need to preserve the "reason" flags before we call the public interfaces + // so that we don't get reasonPhysics + reasonUnknown, just reasonPhysics + UInt16 oldReason = fReason; + + if( fParent ) + { + SetLocalToParent(fParent->GetWorldToLocal() * l2w, w2l * fParent->GetLocalToWorld()); + } + else + { + SetLocalToParent(l2w, w2l); + } + fReason = oldReason | kReasonPhysics; +} + +UInt16 plCoordinateInterface::GetReasons() +{ + return fReason; +} + +void plCoordinateInterface::ClearReasons() +{ + fReason = nil; +} + +void plCoordinateInterface::SetLocalToParent(const hsMatrix44& l2p, const hsMatrix44& p2l) +{ + fReason |= kReasonUnknown; + fLocalToParent = l2p; + fParentToLocal = p2l; + + IDirtyTransform(); +} + +void plCoordinateInterface::WarpToLocal(const hsMatrix44& l2p, const hsMatrix44& p2l) +{ + fReason |= kReasonUnknown; + SetLocalToParent(l2p, p2l); + + // update physical state when an object is warped + if (IGetOwner()) + IGetOwner()->DirtySynchState(kSDLPhysical, 0); +} + +void plCoordinateInterface::WarpToWorld(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + fReason |= kReasonUnknown; + if( fParent ) + { + hsMatrix44 l2p = fParent->GetWorldToLocal() * l2w; + hsMatrix44 p2l = w2l * fParent->GetLocalToWorld(); + WarpToLocal(l2p, p2l); + } + else + { + WarpToLocal(l2w, w2l); + } +} + +plProfile_CreateCounter("CITrans", "Object", CITrans); +plProfile_CreateCounter(" CIRecalc", "Object", CIRecalc); +plProfile_CreateCounter(" CIDirty", "Object", CIDirty); +plProfile_CreateCounter(" CISet", "Object", CISet); + +plProfile_CreateTimer("CITransT", "Object", CITransT); +plProfile_CreateTimer(" CIRecalcT", "Object", CIRecalcT); +plProfile_CreateTimer(" CIDirtyT", "Object", CIDirtyT); +plProfile_CreateTimer(" CISetT", "Object", CISetT); + +static inline hsMatrix44 IMatrixMul34(const hsMatrix44& lhs, const hsMatrix44& rhs) +{ + hsMatrix44 ret; + ret.NotIdentity(); + ret.fMap[3][0] = ret.fMap[3][1] = ret.fMap[3][2] = 0; + ret.fMap[3][3] = 1.f; + + ret.fMap[0][0] = lhs.fMap[0][0] * rhs.fMap[0][0] + + lhs.fMap[0][1] * rhs.fMap[1][0] + + lhs.fMap[0][2] * rhs.fMap[2][0]; + + ret.fMap[0][1] = lhs.fMap[0][0] * rhs.fMap[0][1] + + lhs.fMap[0][1] * rhs.fMap[1][1] + + lhs.fMap[0][2] * rhs.fMap[2][1]; + + ret.fMap[0][2] = lhs.fMap[0][0] * rhs.fMap[0][2] + + lhs.fMap[0][1] * rhs.fMap[1][2] + + lhs.fMap[0][2] * rhs.fMap[2][2]; + + ret.fMap[0][3] = lhs.fMap[0][0] * rhs.fMap[0][3] + + lhs.fMap[0][1] * rhs.fMap[1][3] + + lhs.fMap[0][2] * rhs.fMap[2][3] + + lhs.fMap[0][3]; + + ret.fMap[1][0] = lhs.fMap[1][0] * rhs.fMap[0][0] + + lhs.fMap[1][1] * rhs.fMap[1][0] + + lhs.fMap[1][2] * rhs.fMap[2][0]; + + ret.fMap[1][1] = lhs.fMap[1][0] * rhs.fMap[0][1] + + lhs.fMap[1][1] * rhs.fMap[1][1] + + lhs.fMap[1][2] * rhs.fMap[2][1]; + + ret.fMap[1][2] = lhs.fMap[1][0] * rhs.fMap[0][2] + + lhs.fMap[1][1] * rhs.fMap[1][2] + + lhs.fMap[1][2] * rhs.fMap[2][2]; + + ret.fMap[1][3] = lhs.fMap[1][0] * rhs.fMap[0][3] + + lhs.fMap[1][1] * rhs.fMap[1][3] + + lhs.fMap[1][2] * rhs.fMap[2][3] + + lhs.fMap[1][3]; + + ret.fMap[2][0] = lhs.fMap[2][0] * rhs.fMap[0][0] + + lhs.fMap[2][1] * rhs.fMap[1][0] + + lhs.fMap[2][2] * rhs.fMap[2][0]; + + ret.fMap[2][1] = lhs.fMap[2][0] * rhs.fMap[0][1] + + lhs.fMap[2][1] * rhs.fMap[1][1] + + lhs.fMap[2][2] * rhs.fMap[2][1]; + + ret.fMap[2][2] = lhs.fMap[2][0] * rhs.fMap[0][2] + + lhs.fMap[2][1] * rhs.fMap[1][2] + + lhs.fMap[2][2] * rhs.fMap[2][2]; + + ret.fMap[2][3] = lhs.fMap[2][0] * rhs.fMap[0][3] + + lhs.fMap[2][1] * rhs.fMap[1][3] + + lhs.fMap[2][2] * rhs.fMap[2][3] + + lhs.fMap[2][3]; + + return ret; +} + +void plCoordinateInterface::IRecalcTransforms() +{ + plProfile_IncCount(CIRecalc, 1); + plProfile_BeginTiming(CIRecalcT); + if( fParent ) + { +#if 0 + fLocalToWorld = fParent->GetLocalToWorld() * fLocalToParent; + fWorldToLocal = fParentToLocal * fParent->GetWorldToLocal(); +#else + fLocalToWorld = IMatrixMul34(fParent->GetLocalToWorld(), fLocalToParent); + fWorldToLocal = IMatrixMul34(fParentToLocal, fParent->GetWorldToLocal()); +#endif + } + else + { + fLocalToWorld = fLocalToParent; + fWorldToLocal = fParentToLocal; + } + plProfile_EndTiming(CIRecalcT); +} + +void plCoordinateInterface::ITransformChanged(hsBool force, UInt16 reasons, hsBool checkForDelay) +{ + plProfile_IncCount(CITrans, 1); + plProfile_BeginTiming(CITransT); + + // inherit reasons for transform change from our parents + fReason |= reasons; + + UInt16 propagateReasons = fReason; + + hsBool process = !(checkForDelay && GetProperty(kDelayedTransformEval)) || !fDelayedTransformsEnabled; + + if (process) + { + if( fState & kTransformDirty ) + force = true; + } + + if( force ) + { + IRecalcTransforms(); + + plProfile_IncCount(CISet, 1); + plProfile_BeginTiming(CISetT); + if( IGetOwner() ) + { + IGetOwner()->ISetTransform(fLocalToWorld, fWorldToLocal); + } + plProfile_EndTiming(CISetT); + fState &= ~kTransformDirty; + } + + plProfile_EndTiming(CITransT); + if (process) + { + int i; + for( i = 0; i < fChildren.GetCount(); i++ ) + { + if( fChildren[i] && fChildren[i]->GetVolatileCoordinateInterface() ) + fChildren[i]->GetVolatileCoordinateInterface()->ITransformChanged(force, propagateReasons, checkForDelay); + } + } + else if (force) + { + plProfile_IncCount(CIDirty, 1); + plProfile_BeginTiming(CITransT); + // Our parent is dirty and we're bailing out on evaluating right now. + // Need to ensure we'll be evaluated in the delay pass + plProfile_BeginTiming(CIDirtyT); + IDirtyTransform(); + plProfile_EndTiming(CIDirtyT); + plProfile_EndTiming(CITransT); + } +} + +void plCoordinateInterface::FlushTransform(hsBool fromRoot) +{ + if( fromRoot ) + IGetRoot()->ITransformChanged(false, 0, false); + else + ITransformChanged(false, 0, false); +} + +void plCoordinateInterface::ISetNetGroupRecur(plNetGroupId netGroup) +{ + if( !IGetOwner() ) + return; + + if( IGetOwner()->GetSynchFlags() & kHasConstantNetGroup ) + return; + + IGetOwner()->plSynchedObject::SetNetGroup(netGroup); + + int i; + for( i = 0; i < GetNumChildren(); i++ ) + { + if( GetChild(i) ) + { + GetChild(i)->ISetNetGroupRecur(netGroup); + } + } +} + + +void plCoordinateInterface::Read(hsStream* stream, hsResMgr* mgr) +{ + plObjInterface::Read(stream, mgr); + + fLocalToParent.Read(stream); + fParentToLocal.Read(stream); + + fLocalToWorld.Read(stream); + fWorldToLocal.Read(stream); + + int n = stream->ReadSwap32(); + int i; + for( i = 0; i < n; i++ ) + { + plIntRefMsg* refMsg = TRACKED_NEW plIntRefMsg(GetKey(), plRefMsg::kOnCreate, -1, plIntRefMsg::kChildObject); + mgr->ReadKeyNotifyMe(stream,refMsg, plRefFlags::kPassiveRef); + } +} + +void plCoordinateInterface::Write(hsStream* stream, hsResMgr* mgr) +{ + plObjInterface::Write(stream, mgr); + + fLocalToParent.Write(stream); + fParentToLocal.Write(stream); + + fLocalToWorld.Write(stream); + fWorldToLocal.Write(stream); + + stream->WriteSwap32(fChildren.GetCount()); + int i; + for( i = 0; i < fChildren.GetCount(); i++ ) + mgr->WriteKey(stream, fChildren[i]); + +} + +hsBool plCoordinateInterface::MsgReceive(plMessage* msg) +{ + hsBool retVal = false; + + plIntRefMsg* intRefMsg; + plCorrectionMsg* corrMsg; + + // warp message + plWarpMsg* pWarpMsg = plWarpMsg::ConvertNoRef(msg); + if (pWarpMsg) + { + hsMatrix44 l2w = pWarpMsg->GetTransform(); + hsMatrix44 inv; + l2w.GetInverse(&inv); + WarpToWorld(l2w,inv); + if (pWarpMsg->GetWarpFlags() & plWarpMsg::kFlushTransform) + ITransformChanged(false, kReasonUnknown, false); + return true; + } + else if( intRefMsg = plIntRefMsg::ConvertNoRef(msg) ) + { + switch( intRefMsg->fType ) + { + case plIntRefMsg::kChildObject: + case plIntRefMsg::kChild: + { + plSceneObject* co = nil; + if( intRefMsg->fType == plIntRefMsg::kChildObject ) + { + co = plSceneObject::ConvertNoRef(intRefMsg->GetRef()); + } + else + { + plCoordinateInterface* ci = plCoordinateInterface::ConvertNoRef(intRefMsg->GetRef()); + co = ci ? ci->IGetOwner() : nil; + } + if( intRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnReplace) ) + { + ISetChild(co, intRefMsg->fWhich); + } + else if( intRefMsg->GetContext() & plRefMsg::kOnDestroy ) + { + IRemoveChild(co); + } + else if( intRefMsg->GetContext() & plRefMsg::kOnRequest ) + { + IAttachChild(co, kMaintainWorldPosition|kMaintainSceneNode); + } + else if( intRefMsg->GetContext() & plRefMsg::kOnRemove ) + { + IDetachChild(co, kMaintainWorldPosition|kMaintainSceneNode); + } + } + return true; + default: + break; + } + } + else if( corrMsg = plCorrectionMsg::ConvertNoRef(msg) ) + { + SetTransformPhysical(corrMsg->fLocalToWorld, corrMsg->fWorldToLocal); + + if(corrMsg->fDirtySynch) + { + if (IGetOwner()) + IGetOwner()->DirtySynchState(kSDLPhysical, 0); + } + + return true; + } + + return plObjInterface::MsgReceive(msg); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plCoordinateInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plCoordinateInterface.h new file mode 100644 index 00000000..7c032b2c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plCoordinateInterface.h @@ -0,0 +1,187 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#ifndef plCoordinateInterface_inc +#define plCoordinateInterface_inc + +#include "plObjInterface.h" +#include "hsTemplates.h" +#include "hsMatrix44.h" +#include "../pnNetCommon/plSynchedValue.h" + +class hsStream; +class hsResMgr; + + +class plCoordinateInterface : public plObjInterface +{ +public: + enum plCoordinateProperties { + kDisable = 0, // prop 0 is always disable, declared in plObjInterface + kCanEverDelayTransform = 1, // we can sometimes delay our transform eval (i.e. we're on a physics object) + kDelayedTransformEval = 2, // we're currently registering for the DelayedTransformMsg (we, and all our + // descendants, have the kCanEverDelayTransform prop) + + kNumProps // last in the list + }; + + enum plCoordinateTransformPhases + { + kTransformPhaseNormal, + kTransformPhaseDelayed, + }; + +protected: + enum { + kTransformDirty = 0x1, + kWarp = 0x2, + + kMaxState = 0xffff + }; + enum plAttachFlags { + kMaintainWorldPosition = 0x1, + kMaintainSceneNode = 0x2, + kAboutToAttach = 0x4 + }; + enum Reason { + kReasonUnknown = 0x1, // somebody moved us + kReasonPhysics = 0x2, // physics moved us + kMaxReasons = 0xffff // sixteen bits + }; + + // Set by the client in IUpdate(). This tells us where we are in the update loop so that we know + // which transform message to register for when our transform is dirtied. + static UInt8 fTransformPhase; + + // Temp debugging tool, so we can quickly (dis/en)able delayed transforms at runtime. + static hsBool fDelayedTransformsEnabled; + + UInt16 fState; + UInt16 fReason; // why we've changed position (if we have) + + hsTArray fChildren; + plCoordinateInterface* fParent; // if this changes, marks us as dirty + + hsMatrix44 fLocalToParent; + hsMatrix44 fParentToLocal; + + hsMatrix44 fLocalToWorld; + hsMatrix44 fWorldToLocal; + + virtual void ISetOwner(plSceneObject* so); + + virtual void ISetParent(plCoordinateInterface* par); // don't use, use AddChild on parent + virtual void ISetSceneNode(plKey newNode); + // objectToo moves the sceneObject to the new room, else just move the data and remove + // the object from whatever room he's in. + + // Network only strange functions. Do not emulate or generalize this functionality. + void ISetNetGroupRecur(plNetGroupId netGroup); + + virtual void ISetChild(plSceneObject* child, int which); // sets parent on child + virtual void IAddChild(plSceneObject* child); // sets parent on child + virtual void IRemoveChild(plSceneObject* child); // removes this as parent of child + virtual void IRemoveChild(int i); // removes this as parent of child + virtual void IAttachChild(plSceneObject* child, UInt8 flags); // physically attaches child to us + virtual void IDetachChild(plSceneObject* child, UInt8 flags); // physically detach this child from us + virtual void IUpdateDelayProp(); // Called whenever a child is added/removed + + virtual void IRecalcTransforms(); // Called by ITransformChanged when we need to re-examine our relationship with our parent. + virtual void ITransformChanged(hsBool force, UInt16 reasons, hsBool checkForDelay); // called by SceneObject on TransformChanged messsage + + void IDirtyTransform(); + void IRegisterForTransformMessage(hsBool delayed); + void IUnRegisterForTransformMessage(); + plCoordinateInterface* IGetRoot(); + + friend class plSceneObject; + +public: + plCoordinateInterface(); + ~plCoordinateInterface(); + + CLASSNAME_REGISTER( plCoordinateInterface ); + GETINTERFACE_ANY( plCoordinateInterface, plObjInterface ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + virtual void SetLocalToParent(const hsMatrix44& l2p, const hsMatrix44& p2l); + // special version for setting transform from physics. + // separate to keep from changing interface to add "reason" parameter + virtual void SetTransformPhysical(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual void MultTransformLocal(const hsMatrix44& move, const hsMatrix44& invMove); + virtual void WarpToWorld(const hsMatrix44& l2w, const hsMatrix44& w2l); + virtual void WarpToLocal(const hsMatrix44& l2p, const hsMatrix44& p2l); + + // Force an immediate re-sync of the transforms in the hierarchy this object belongs to, + // as opposed to waiting for the plTransformMsg to resync. + // There are two uses for this: + // a) You need the transforms for this object to be valid NOW, can't wait till after a TransforMsg + // In this case, you want to flush from the root, because something higher up the hierarchy + // may be dirty, which invalidates this object's transforms + // b) You've just dirtied this object's transform, and need it to propagate downward from here NOW + // In this case, fromRoot should be false, because you haven't dirtied anything higher up + // the hierarchy. + // Another way to look at it is, if the transforms for the tree were correct before you messed with + // this object, you only need to flush the transforms for this object and its recursive children, + // so fromRoot=false. + // If the entire tree is potentially dirty and you need to read from it, you want the entire tree + // synced up, so fromRoot=true. + // fromRoot=true is always safe, just potentially wasteful, so if you don't know, use fromRoot=true or + // preferably, don't use this function. + void FlushTransform(hsBool fromRoot=true); + + virtual const hsMatrix44& GetLocalToParent() const { return fLocalToParent; } + virtual const hsMatrix44& GetParentToLocal() const { return fParentToLocal; } + + virtual const hsMatrix44& GetLocalToWorld() const { return fLocalToWorld; } + virtual const hsMatrix44& GetWorldToLocal() const { return fWorldToLocal; } + + virtual const hsPoint3 GetWorldPos() const { return fLocalToWorld.GetTranslate(); } + + virtual int GetNumChildren() const { return fChildren.GetCount(); } + virtual plCoordinateInterface* GetChild(int i) const; + virtual plCoordinateInterface* GetParent() const { return fParent; } + + virtual hsBool MsgReceive(plMessage* msg); + + UInt16 GetReasons(); + void ClearReasons(); + + Int32 GetNumProperties() const { return kNumProps; } + static UInt8 GetTransformPhase() { return fTransformPhase; } + static void SetTransformPhase(UInt8 phase) { fTransformPhase = phase; } + + static hsBool GetDelayedTransformsEnabled() { return fDelayedTransformsEnabled; } + static void SetDelayedTransformsEnabled(hsBool val) { fDelayedTransformsEnabled = val; } +}; + + +#endif // plCoordinateInterface_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plDrawInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plDrawInterface.cpp new file mode 100644 index 00000000..347ebd3f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plDrawInterface.cpp @@ -0,0 +1,371 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plDrawInterface.h" +#include "plDrawable.h" +#include "hsBounds.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "plgDispatch.h" +#include "plSceneObject.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../pnMessage/plDISpansMsg.h" + +plDrawInterface::plDrawInterface() +{ +} + +plDrawInterface::~plDrawInterface() +{ + +} + +void plDrawInterface::SetDrawableMeshIndex( UInt8 which, UInt32 index ) +{ + ICheckDrawableIndex(which); + + fDrawableIndices[which] = index; +} + +void plDrawInterface::SetProperty(int prop, hsBool on) +{ + plObjInterface::SetProperty(prop, on); + + int i; + for( i = 0; i < fDrawables.GetCount(); i++ ) + { + if( fDrawables[i] ) + fDrawables[i]->SetProperty(fDrawableIndices[i], prop, on); + } +} + +void plDrawInterface::ISetSceneNode(plKey newNode) +{ + int i; + for( i = 0; i < fDrawables.GetCount(); i++ ) + { + if( fDrawables[i] ) + fDrawables[i]->SetSceneNode(newNode); + } +} + +void plDrawInterface::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + if( !GetProperty(kDisable) ) + { + int i; + for( i = 0; i < fDrawables.GetCount(); i++ ) + { + if( fDrawables[i] ) + fDrawables[i]->SetTransform( fDrawableIndices[i], l2w, w2l ); + } + } +} + +const hsBounds3Ext plDrawInterface::GetLocalBounds() const +{ + hsBounds3Ext retVal; + retVal.MakeEmpty(); + int i; + for( i = 0; i < fDrawables.GetCount(); i++ ) + { + if( fDrawables[i] ) + retVal.Union(&fDrawables[i]->GetLocalBounds(fDrawableIndices[i])); + } + return retVal; +} + +const hsBounds3Ext plDrawInterface::GetWorldBounds() const +{ + hsBounds3Ext retVal; + retVal.MakeEmpty(); + int i; + for( i = 0; i < fDrawables.GetCount(); i++ ) + { + if( fDrawables[i] ) + retVal.Union(&fDrawables[i]->GetWorldBounds(fDrawableIndices[i])); + } + return retVal; +} + +const hsBounds3Ext plDrawInterface::GetMaxWorldBounds() const +{ + hsBounds3Ext retVal; + retVal.MakeEmpty(); + int i; + for( i = 0; i < fDrawables.GetCount(); i++ ) + { + if( fDrawables[i] ) + retVal.Union(&fDrawables[i]->GetMaxWorldBounds(fDrawableIndices[i])); + } + return retVal; +} + +void plDrawInterface::Read(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Read(s, mgr); + + int nDrawables = s->ReadSwap32(); + if (nDrawables > 0) + ICheckDrawableIndex(nDrawables-1); + int i; + for( i = 0; i < fDrawables.GetCount(); i++ ) + { + fDrawableIndices[i] = s->ReadSwap32(); + + plIntRefMsg* refMsg = TRACKED_NEW plIntRefMsg(GetKey(), plRefMsg::kOnCreate, i, plIntRefMsg::kDrawable); + mgr->ReadKeyNotifyMe(s,refMsg, plRefFlags::kActiveRef); + } + + int nReg = s->ReadSwap32(); + fRegions.SetCountAndZero(nReg); + for( i = 0; i < nReg; i++ ) + { + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefVisRegion); + mgr->ReadKeyNotifyMe(s, refMsg, plRefFlags::kActiveRef); + } +} + +void plDrawInterface::Write(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Write(s, mgr); + + s->WriteSwap32(fDrawables.GetCount()); + int i; + for( i = 0; i < fDrawables.GetCount(); i++ ) + { + s->WriteSwap32(fDrawableIndices[i]); + + mgr->WriteKey(s, fDrawables[i]); + } + + s->WriteSwap32(fRegions.GetCount()); + for( i = 0; i < fRegions.GetCount(); i++ ) + { + mgr->WriteKey(s, fRegions[i]); + } +} + +//// ReleaseData ////////////////////////////////////////////////////////////// +// Called by SceneViewer to release the data for this given object (when +// its parent sceneObject is deleted). + +void plDrawInterface::ReleaseData( void ) +{ + int i; + for( i = 0; i < fDrawables.GetCount(); i++ ) + { + if( fDrawables[i] && (fDrawableIndices[i] != UInt32(-1)) ) + { + plDISpansMsg* diMsg = TRACKED_NEW plDISpansMsg(fDrawables[i]->GetKey(), plDISpansMsg::kRemovingSpan, fDrawableIndices[i], 0); + diMsg->SetSender(GetKey()); + diMsg->Send(); + } + //fDrawableIndices[i] = UInt32(-1); + fDrawables.Reset(); + fDrawableIndices.Reset(); + } +} + +void plDrawInterface::ICheckDrawableIndex(UInt8 which) +{ + if( which >= fDrawableIndices.GetCount() ) + { + fDrawables.ExpandAndZero(which+1); + + int n = fDrawableIndices.GetCount(); + fDrawableIndices.ExpandAndZero(which+1); + int i; + for( i = n; i <= which; i++ ) + fDrawableIndices[i] = UInt32(-1); + } +} + +void plDrawInterface::ISetDrawable(UInt8 which, plDrawable* dr) +{ + ICheckDrawableIndex(which); + fDrawables[which] = dr; + + if( dr ) + dr->SetSceneNode(GetSceneNode()); + + // We might read the vis regions before the drawables, so + // we have to check for any already loaded. + ISetVisRegions(which); + +#ifdef HS_DEBUGGING + if( fDrawableIndices[which] != (UInt32)-1 ) + { + plDISpansMsg* diMsg = TRACKED_NEW plDISpansMsg(dr->GetKey(), plDISpansMsg::kAddingSpan, fDrawableIndices[which], 0); + diMsg->SetSender(GetKey()); + diMsg->Send(); + } +#endif +} + +void plDrawInterface::IRemoveDrawable(plDrawable *dr) +{ + int idx = fDrawables.Find(dr); + if( fDrawables.kMissingIndex != idx ) + { + fDrawables[idx] = nil; + fDrawableIndices[idx] = UInt32(-1); + } + else + { + hsAssert(false, "Trying to remove a drawable that doesn't belong to us"); + } +} + +void plDrawInterface::ISetVisRegion(hsKeyedObject* reg, hsBool on) +{ + int i; + for( i = 0; i < fDrawables.GetCount(); i++ ) + { + if( fDrawables[i] && (fDrawableIndices[i] != UInt32(-1)) ) + { + fDrawables[i]->SetDISpanVisSet(fDrawableIndices[i], reg, on); + } + } + int idx = fRegions.Find(reg); + if( on ) + { + if( idx == fRegions.kMissingIndex ) + fRegions.Append(reg); + } + else + { + if( idx != fRegions.kMissingIndex ) + fRegions.Remove(idx); + } + +} + +void plDrawInterface::ISetVisRegions(int iDraw) +{ + if( fDrawables[iDraw] && (fDrawableIndices[iDraw] != UInt32(-1)) ) + { + int i; + for( i = 0; i < fRegions.GetCount(); i++ ) + { + fDrawables[iDraw]->SetDISpanVisSet(fDrawableIndices[iDraw], fRegions[i], true); + } + } +} + +// Export only. Use messages for runtime +void plDrawInterface::SetDrawable(UInt8 which, plDrawable *dr) +{ + if( dr ) + { + // This is a little convoluted, but it makes GCC happy and doesn't hurt anybody. + plIntRefMsg* intRefMsg = TRACKED_NEW plIntRefMsg(GetKey(), plRefMsg::kOnCreate, which, plIntRefMsg::kDrawable); + plRefMsg* refMsg = intRefMsg; +// hsgResMgr::ResMgr()->SendRef(dr->GetKey(), intRefMsg, plRefFlags::kActiveRef); // THIS WON'T COMPILE UNDER GCC + hsgResMgr::ResMgr()->SendRef(dr, refMsg, plRefFlags::kActiveRef); + } + else + { + ISetDrawable(which, nil); + } +} + +hsBool plDrawInterface::MsgReceive(plMessage* msg) +{ + plIntRefMsg* intRefMsg = plIntRefMsg::ConvertNoRef(msg); + if( intRefMsg ) + { + switch( intRefMsg->fType ) + { + case plIntRefMsg::kDrawable: + if( intRefMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + IRemoveDrawable(plDrawable::ConvertNoRef(intRefMsg->GetRef())); + } + else + { + ISetDrawable((UInt8)intRefMsg->fWhich, plDrawable::ConvertNoRef(intRefMsg->GetRef())); + } + return true; + default: + break; + } + } + plGenRefMsg* genRefMsg = plGenRefMsg::ConvertNoRef(msg); + if( genRefMsg ) + { + switch( genRefMsg->fType ) + { + case kRefVisRegion: + if( genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + ISetVisRegion(genRefMsg->GetRef(), true); + else + ISetVisRegion(genRefMsg->GetRef(), false); + break; + default: + break; + } + return true; + } + plEnableMsg* pEnableMsg = plEnableMsg::ConvertNoRef( msg ); + if (pEnableMsg) + { + SetProperty(kDisable, pEnableMsg->Cmd(plEnableMsg::kDisable)); + if( GetOwner() ) + SetTransform(GetOwner()->GetLocalToWorld(), GetOwner()->GetWorldToLocal()); + return true; + } + return plObjInterface::MsgReceive(msg); +} + +void plDrawInterface::SetUpForParticleSystem( UInt32 maxNumEmitters, UInt32 maxNumParticles, hsGMaterial *material, hsTArray& lights ) +{ + hsAssert( fDrawables[0] != nil, "No drawable to use for particle system!" ); + SetDrawableMeshIndex( 0, fDrawables[0]->CreateParticleSystem( maxNumEmitters, maxNumParticles, material ) ); + int i; + for( i = 0; i < lights.GetCount(); i++ ) + { + hsgResMgr::ResMgr()->AddViaNotify(lights[i], TRACKED_NEW plGenRefMsg(fDrawables[0]->GetKey(), plRefMsg::kOnCreate, fDrawableIndices[0], plDrawable::kMsgPermaLightDI), plRefFlags::kPassiveRef); + } + + ISetVisRegions(0); +} + +void plDrawInterface::ResetParticleSystem( void ) +{ + hsAssert( fDrawables[0] != nil, "No drawable to use for particle system!" ); + fDrawables[0]->ResetParticleSystem( fDrawableIndices[0] ); +} + +void plDrawInterface::AssignEmitterToParticleSystem( plParticleEmitter *emitter ) +{ + hsAssert( fDrawables[0] != nil, "No drawable to use for particle system!" ); + fDrawables[0]->AssignEmitterToParticleSystem( fDrawableIndices[0], emitter ); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plDrawInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plDrawInterface.h new file mode 100644 index 00000000..95bbb1f5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plDrawInterface.h @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDrawInterface_inc +#define plDrawInterface_inc + +#include "plObjInterface.h" + +class plDrawable; +class hsStream; +class hsResMgr; +struct hsMatrix44; +class hsBounds3Ext; +class hsGMaterial; +class plParticleEmitter; + +class plDrawInterface : public plObjInterface +{ +public: + // Props inc by 1 (bit shift in bitvector). + enum plDrawProperties { + kDisable = 0, + + kNumProps // last in the list + }; + enum { + kRefVisRegion + }; + +protected: + hsTArray fDrawables; + hsTArray fDrawableIndices; + + hsTArray fRegions; + + void ISetVisRegions(int iDraw); + void ISetVisRegion(hsKeyedObject* ref, hsBool on); + void ISetDrawable(UInt8 which, plDrawable* dr); + void IRemoveDrawable(plDrawable* dr); + void ISetSceneNode(plKey newNode); + virtual void ICheckDrawableIndex(UInt8 which); + + friend class plSceneObject; + +public: + plDrawInterface(); + virtual ~plDrawInterface(); + + CLASSNAME_REGISTER( plDrawInterface ); + GETINTERFACE_ANY( plDrawInterface, plObjInterface ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void SetProperty(int prop, hsBool on); + Int32 GetNumProperties() const { return kNumProps; } + + // Transform settable only, if you want it get it from the coordinate interface. + void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + // Bounds are gettable only, they are computed on the drawable. + const hsBounds3Ext GetLocalBounds() const; + const hsBounds3Ext GetWorldBounds() const; + const hsBounds3Ext GetMaxWorldBounds() const; + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void ReleaseData( void ); + + /// Funky particle system functions + void SetUpForParticleSystem( UInt32 maxNumEmitters, UInt32 maxNumParticles, hsGMaterial *material, hsTArray& lights ); + void ResetParticleSystem( void ); + void AssignEmitterToParticleSystem( plParticleEmitter *emitter ); + + /// EXPORT-ONLY + void SetDrawable(UInt8 which, plDrawable* dr); + plDrawable* GetDrawable( UInt8 which ) const { return which < fDrawables.GetCount() ? fDrawables[which] : nil; } + UInt32 GetNumDrawables() const { return fDrawables.GetCount(); } + // Sets the triMesh index to be used when referring to our spans in the drawable + void SetDrawableMeshIndex( UInt8 which, UInt32 index ); + UInt32 GetDrawableMeshIndex( UInt8 which ) const { return which < fDrawableIndices.GetCount() ? fDrawableIndices[which] : UInt32(-1); } +}; + + +#endif // plDrawInterface_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plObjInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plObjInterface.cpp new file mode 100644 index 00000000..60852afb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plObjInterface.cpp @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plObjInterface.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plKey.h" +#include "plSceneObject.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../pnMessage/plEnableMsg.h" + + +plObjInterface::plObjInterface() +: fOwner(nil) +{ +} + +plObjInterface::~plObjInterface() +{ +} + +void plObjInterface::ISetOwner(plSceneObject* owner) +{ + if( fOwner != owner ) + { + fOwner = owner; + if( fOwner ) + fOwner->ISetInterface(this); + } +} + +void plObjInterface::Read(hsStream* s, hsResMgr* mgr) +{ + plSynchedObject::Read(s, mgr); + + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plIntRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plIntRefMsg::kOwner), plRefFlags::kPassiveRef); + + fProps.Read(s); +} + +void plObjInterface::Write(hsStream* s, hsResMgr* mgr) +{ + plSynchedObject::Write(s, mgr); + + mgr->WriteKey(s, fOwner); + fProps.Write(s); +} + +hsBool plObjInterface::MsgReceive(plMessage* msg) +{ + hsBool retVal = false; + + plEnableMsg* enaMsg = plEnableMsg::ConvertNoRef(msg); + if( enaMsg ) + { + SetProperty(kDisable, enaMsg->Cmd(plEnableMsg::kDisable)); + return true; + } + plIntRefMsg* intRefMsg = plIntRefMsg::ConvertNoRef(msg); + if( intRefMsg ) + { + switch( intRefMsg->fType ) + { + case plIntRefMsg::kOwner: + if( intRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + plSceneObject* owner = plSceneObject::ConvertNoRef(intRefMsg->GetRef()); + ISetOwner(owner); + } + else if( intRefMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + ISetOwner(nil); + } + break; + } + } + return plSynchedObject::MsgReceive(msg); +} + +// +// assign and update my properties from the bitVector passed in +// +void plObjInterface::ISetAllProperties(const hsBitVector& b) +{ +// if (&b != &fProps) // don't copy if they are the same variable + + fProps = b; + + int i; + for(i=0;i. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plInterface_inc +#define plInterface_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnMessage/plRefMsg.h" +#include "plSceneObject.h" +#include "hsStream.h" +#include "../pnNetCommon/plSynchedObject.h" +#include "../pnNetCommon/plSynchedValue.h" +#include "hsBitVector.h" + +class hsResMgr; +class plDrawInterface; +class plSimulationInterface; +class plCoordinateInterface; +class plAudioInterface; + +// +// Since interfaces are keyed objects with valid uoids and +// may have dynamic data (coordinate interface), they are +// synched (saved) objects +// +class plObjInterface : public plSynchedObject +{ +protected: + enum { + kDisable = 0x0 // Derived interfaces duplicate this, so if you add more here, they need to know. + }; + friend class plSynchedValueBase; + friend class plSceneObject; +protected: + plSceneObject* fOwner; + hsBitVector fProps; + + // SetSceneNode just called by owner. If we're an interface to external data, + // we need to pass the change on. Otherwise, do nothing. + virtual void ISetSceneNode(plKey node) {} + plSceneObject* IGetOwner() const { return fOwner; } + virtual void ISetOwner(plSceneObject* owner); + void ISetAllProperties(const hsBitVector& b); + + plDrawInterface* IGetOwnerDrawInterface() { return fOwner ? fOwner->GetVolatileDrawInterface() : nil; } + plSimulationInterface* IGetOwnerSimulationInterface() { return fOwner ? fOwner->GetVolatileSimulationInterface() : nil; } + plCoordinateInterface* IGetOwnerCoordinateInterface() { return fOwner ? fOwner->GetVolatileCoordinateInterface() : nil; } + plAudioInterface* IGetOwnerAudioInterface() { return fOwner ? fOwner->GetVolatileAudioInterface() : nil; } +public: + + plObjInterface(); + ~plObjInterface(); + + CLASSNAME_REGISTER( plObjInterface ); + GETINTERFACE_ANY( plObjInterface, plSynchedObject ); + + virtual hsBool MsgReceive(plMessage* msg); + + const plSceneObject* GetOwner() const { return IGetOwner(); } + plKey GetOwnerKey() const { return IGetOwner() ? IGetOwner()->GetKey() : nil; } + + virtual plKey GetSceneNode() const { return IGetOwner() ? IGetOwner()->GetSceneNode() : nil; } + + // override SetProperty to pass the prop down to the pool objects + virtual void SetProperty(int prop, hsBool on) { fProps.SetBit(prop, on); } + + // shouldn't need to override GetProperty() + hsBool GetProperty(int prop) const { return fProps.IsBitSet(prop); } + virtual Int32 GetNumProperties() const = 0; + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) = 0; + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual void ReleaseData( void ) { } +}; + + +#endif // plInterface_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSceneObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSceneObject.cpp new file mode 100644 index 00000000..9205c68d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSceneObject.cpp @@ -0,0 +1,870 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plSceneObject.h" +#include "plDrawInterface.h" +#include "plSimulationInterface.h" +#include "plCoordinateInterface.h" +#include "plAudioInterface.h" +#include "../pnDispatch/plDispatch.h" +#include "../pnModifier/plModifier.h" +#include "../pnMessage/plMessage.h" +#include "../pnMessage/plRefMsg.h" +#include "plDrawable.h" +#include "plPhysical.h" +#include "plAudible.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plCorrectionMsg.h" +#include "../pnMessage/plWarpMsg.h" +#include "../pnMessage/plSoundMsg.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plAttachMsg.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../pnMessage/plSimulationSynchMsg.h" +#include "../pnMessage/plSimulationMsg.h" +#include "../pnMessage/plNodeChangeMsg.h" +#include "../pnMessage/plSelfDestructMsg.h" +#include "../pnKeyedObject/plKey.h" + +#include "hsStream.h" +#include "hsResMgr.h" +#include "plCreatableIndex.h" // For plLightInfo::Index(), so we don't have to include plLightInfo.h + + + +int dbgCurrentTest = 0; + +plSceneObject::plSceneObject() +: fDrawInterface(nil), + fSimulationInterface(nil), + fCoordinateInterface(nil), + fAudioInterface(nil), + fSceneNode(nil) +{ +} + +plSceneObject::~plSceneObject() +{ + SetDrawInterface(nil); + SetSimulationInterface(nil); + SetCoordinateInterface(nil); + SetAudioInterface(nil); + + IRemoveAllGenerics(); + + int i; + int knt; + + knt = fModifiers.GetCount(); + for( i = 0; i < knt; i++ ) + { + if( fModifiers[i] ) + fModifiers[i]->RemoveTarget(this); + } +} + +void plSceneObject::Read(hsStream* stream, hsResMgr* mgr) +{ + plSynchedObject::Read(stream, mgr); + + // Interfaces will attach themselves to us on read. + // DI + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + // SI + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + // CI + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + // AI + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + int i; + + int nGen = stream->ReadSwap32(); + fGenerics.SetCount(0); + for( i = 0; i < nGen; i++ ) + { + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + } + + plObjRefMsg* refMsg; + + int nOldMods=fModifiers.GetCount(); // existng modifiers created during interface loading + int nNewMods = stream->ReadSwap32(); + fModifiers.ExpandAndZero(nOldMods+nNewMods); // reserve space for new modifiers+existing modifiers + for( i = nOldMods; i < nOldMods+nNewMods; i++ ) + { + refMsg = TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, i, plObjRefMsg::kModifier); + mgr->ReadKeyNotifyMe(stream,refMsg, plRefFlags::kActiveRef); + } + + plKey nodeKey = mgr->ReadKey(stream); +// SetSceneNode(nodeKey); + fSceneNode = nodeKey; + +} + +void plSceneObject::Write(hsStream* stream, hsResMgr* mgr) +{ + plSynchedObject::Write(stream, mgr); + + mgr->WriteKey(stream, fDrawInterface); + mgr->WriteKey(stream, fSimulationInterface); + mgr->WriteKey(stream, fCoordinateInterface); + mgr->WriteKey(stream, fAudioInterface); + + int i; + + stream->WriteSwap32(fGenerics.GetCount()); + for( i = 0; i < fGenerics.GetCount(); i++ ) + mgr->WriteKey(stream, fGenerics[i]); + + for( i = fModifiers.GetCount() - 1; i >= 0; i--) + if (fModifiers[i]->GetKey() == nil) + RemoveModifier(fModifiers[i]); + + stream->WriteSwap32(fModifiers.GetCount()); + for( i = 0; i < fModifiers.GetCount(); i++ ) + mgr->WriteKey(stream,fModifiers[i]); + + mgr->WriteKey(stream, fSceneNode); +} + +//// ReleaseData ////////////////////////////////////////////////////////////// +// Called by SceneViewer to release the data for this sceneObject (really +// just a switchboard). + +void plSceneObject::ReleaseData( void ) +{ + if( fDrawInterface ) + fDrawInterface->ReleaseData(); + if( fSimulationInterface ) + fSimulationInterface->ReleaseData(); + if( fCoordinateInterface ) + fCoordinateInterface->ReleaseData(); + if( fAudioInterface ) + fAudioInterface->ReleaseData(); + + int i; + for( i = 0; i < fGenerics.GetCount(); i++ ) + { + if( fGenerics[i] ) + fGenerics[i]->ReleaseData(); + } +} + +void plSceneObject::FlushTransform() +{ + if( fCoordinateInterface ) + fCoordinateInterface->FlushTransform(); +} + +#include "plProfile.h" + +plProfile_CreateTimer("SOTrans", "Object", SOTrans); +plProfile_CreateTimer(" SODITrans", "Object", SODITrans); +plProfile_CreateTimer(" SOSITrans", "Object", SOSITrans); +plProfile_CreateTimer(" SOAITrans", "Object", SOAITrans); +plProfile_CreateTimer(" SOGITrans", "Object", SOGITrans); +plProfile_CreateTimer(" SOMOTrans", "Object", SOMOTrans); + + +void plSceneObject::ISetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + plProfile_BeginTiming(SOTrans); + plProfile_BeginTiming(SODITrans); + if( fDrawInterface ) + fDrawInterface->SetTransform(l2w, w2l); + plProfile_EndTiming(SODITrans); + + plProfile_BeginTiming(SOSITrans); + if( fSimulationInterface ) + { + if(fCoordinateInterface) + { + UInt16 whyTransformed = fCoordinateInterface->GetReasons(); + if(whyTransformed != plCoordinateInterface::kReasonPhysics) + { + // if we were transformed by anything but physics, let physics know + // otherwise we're not even going to tell physics + fSimulationInterface->SetTransform(l2w, w2l); + } else { + int moreSunshine = 10; + } + fCoordinateInterface->ClearReasons(); + } else { + int somethingToBreakOn = 10; + // if there's not coordinate interface, there's no reason to move the simulation interface + } + } + plProfile_EndTiming(SOSITrans); + + plProfile_BeginTiming(SOAITrans); + if( fAudioInterface ) + fAudioInterface->SetTransform(l2w, w2l); + plProfile_EndTiming(SOAITrans); + + plProfile_BeginTiming(SOGITrans); + int i; + for( i = 0; i < fGenerics.GetCount(); i++ ) + { + if( fGenerics[i] ) + fGenerics[i]->SetTransform(l2w, w2l); + } + plProfile_EndTiming(SOGITrans); + + + plProfile_BeginTiming(SOMOTrans); + for( i = 0; i < fModifiers.GetCount(); i++ ) + { + if( fModifiers[i] ) + fModifiers[i]->SetTransform(l2w, w2l); + } + plProfile_EndTiming(SOMOTrans); + plProfile_EndTiming(SOTrans); +} + +void plSceneObject::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + if( fCoordinateInterface ) + fCoordinateInterface->SetTransform(l2w, w2l); +} + +hsMatrix44 plSceneObject::GetLocalToWorld() const +{ + hsMatrix44 l2w; + if( fCoordinateInterface ) + l2w = fCoordinateInterface->GetLocalToWorld(); + else + l2w.Reset(); + return l2w; +} + +hsMatrix44 plSceneObject::GetWorldToLocal() const +{ + hsMatrix44 w2l; + if( fCoordinateInterface ) + w2l = fCoordinateInterface->GetWorldToLocal(); + else + w2l.Reset(); + return w2l; +} + +hsMatrix44 plSceneObject::GetLocalToParent() const +{ + hsMatrix44 l2p; + if( fCoordinateInterface ) + l2p = fCoordinateInterface->GetLocalToParent(); + else + l2p.Reset(); + return l2p; +} + +hsMatrix44 plSceneObject::GetParentToLocal() const +{ + hsMatrix44 p2l; + if( fCoordinateInterface ) + p2l = fCoordinateInterface->GetParentToLocal(); + else + p2l.Reset(); + return p2l; +} + +void plSceneObject::IAddModifier(plModifier* mo, int i) +{ + if( !mo ) + return; + + if( i < 0 ) + i = fModifiers.GetCount(); + fModifiers.ExpandAndZero(i+1); + fModifiers.Set(i, mo); + mo->AddTarget(this); +} + +void plSceneObject::IRemoveModifier(plModifier* mo) +{ + if( !mo ) + return; + + int idx = fModifiers.Find(mo); + if( idx != fModifiers.kMissingIndex ) + { + mo->RemoveTarget(this); + fModifiers.Remove(idx); + } +} + +void plSceneObject::ISetInterface(plObjInterface* iface) +{ + hsAssert(iface, "Setting nil interface"); + if( plDrawInterface::ConvertNoRef(iface) ) + ISetDrawInterface(plDrawInterface::ConvertNoRef(iface)); + else if( plSimulationInterface::ConvertNoRef(iface) ) + ISetSimulationInterface(plSimulationInterface::ConvertNoRef(iface)); + else if( plCoordinateInterface::ConvertNoRef(iface) ) + ISetCoordinateInterface(plCoordinateInterface::ConvertNoRef(iface)); + else if( plAudioInterface::ConvertNoRef(iface) ) + ISetAudioInterface(plAudioInterface::ConvertNoRef(iface)); + else + IAddGeneric(iface); + + if( iface ) + iface->ISetSceneNode(GetSceneNode()); +} + +void plSceneObject::IRemoveInterface(Int16 idx, plObjInterface* who) +{ + if( plFactory::DerivesFrom(plDrawInterface::Index(), idx) ) + ISetDrawInterface(nil); + else if( plFactory::DerivesFrom(plSimulationInterface::Index(), idx) ) + ISetSimulationInterface(nil); + else if( plFactory::DerivesFrom(plCoordinateInterface::Index(), idx) ) + ISetCoordinateInterface(nil); + else if( plFactory::DerivesFrom(plAudioInterface::Index(), idx) ) + ISetAudioInterface(nil); + else + IRemoveGeneric(who); +} + +hsBool plSceneObject::IPropagateToModifiers(plMessage* msg) +{ + hsBool retVal = false; + int i; + int nMods = fModifiers.GetCount(); + + for( i = 0; i < nMods; i++ ) + { + if( fModifiers[i] ) + { + plModifier *mod = fModifiers[i]; + hsBool modRet = mod->MsgReceive(msg); + retVal |= modRet; + } + } + return retVal; +} + +hsBool plSceneObject::Eval(double secs, hsScalar delSecs) +{ + UInt32 dirty = ~0L; + hsBool retVal = false; + int i; + for( i = 0; i < fModifiers.GetCount(); i++ ) + { + if( fModifiers[i] ) + retVal |= fModifiers[i]->IEval(secs, delSecs, dirty); + } + return retVal; +} + +void plSceneObject::SetSceneNode(plKey newNode) +{ + plKey curNode=GetSceneNode(); + if( curNode == newNode ) + return; + if( fDrawInterface ) + fDrawInterface->ISetSceneNode(newNode); + if( fSimulationInterface ) + fSimulationInterface->ISetSceneNode(newNode); + if( fAudioInterface ) + fAudioInterface->ISetSceneNode(newNode); + if( fCoordinateInterface ) + fCoordinateInterface->ISetSceneNode(newNode); + + int i; + for( i = 0; i < GetNumGenerics(); i++ ) + { + if( fGenerics[i] ) + fGenerics[i]->ISetSceneNode(newNode); + } + + if( newNode ) + { + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(newNode, plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kObject); + plKey key = GetKey(); // for linux build + hsgResMgr::ResMgr()->AddViaNotify(key, refMsg, plRefFlags::kActiveRef); + } + if( curNode) + { + curNode->Release(GetKey()); + } + fSceneNode = newNode; +} + +plKey plSceneObject::GetSceneNode() const +{ + return fSceneNode; +} + +void plSceneObject::SetNetGroup(plNetGroupId netGroup) +{ + if( !fCoordinateInterface ) + plSynchedObject::SetNetGroup(netGroup); + else + fCoordinateInterface->ISetNetGroupRecur(netGroup); +} + +const plModifier* plSceneObject::GetModifierByType(UInt16 classIdx) const +{ + int i; + for (i = 0; i < fModifiers.GetCount(); i++) + { + plModifier * mod = fModifiers[i]; + if (mod && plFactory::DerivesFrom(classIdx, mod->ClassIndex())) + return mod; + } + + return nil; +} + +hsBool plSceneObject::MsgReceive(plMessage* msg) +{ + +#if 0 // objects are only in the nil room when they are being paged out + // TEMP - until we have another way to neutralize objects + // for an object in the 'nil' room, ignore most msgs + if (GetSceneNode()==nil && !plNodeChangeMsg::ConvertNoRef(msg) && + !plRefMsg::ConvertNoRef(msg)&& + !plSelfDestructMsg::ConvertNoRef(msg)) + return false; +#endif + + hsBool retVal = false; + // If it's a bcast, let our own dispatcher find who's interested. + plTransformMsg* trans; + plEvalMsg* eval = plEvalMsg::ConvertNoRef(msg); + plAttachMsg* att = nil; + if( eval ) + { + // Switched things over so that modifiers register for the eval message themselves, + // and can be smart about not evaluating if they know it's unneccessary. + + //Eval(eval->DSeconds(), eval->DelSeconds()); + return true; + } + else + if( trans = plTransformMsg::ConvertNoRef(msg) ) // also catches the derived plDelayedTransformMsg + { + if( fCoordinateInterface ) + { + // flush any dirty transforms + fCoordinateInterface->ITransformChanged(false, 0, trans->ClassIndex() == plTransformMsg::Index()); + } + return true; + } + else + if( att = plAttachMsg::ConvertNoRef(msg) ) + { + if( fCoordinateInterface ) + { + plSceneObject *child = plSceneObject::ConvertNoRef(att->GetRef()); + + if( child ) + { + if( !fCoordinateInterface ) + { + // If we have no coordinate interface, we could make ourselves one here, + // but for now it's an error. + hsAssert(false, "Trying to attach a child when we have no coordinateInterface"); + return true; + } + if( !child->GetVolatileCoordinateInterface() ) + { + // If the child has no coordinate interface, we could add one to it here, + // but for now it's an error. + hsAssert(false, "Trying to attach a child who has no coordinateInterface"); + return true; + } + plIntRefMsg* intRefMsg = TRACKED_NEW plIntRefMsg(fCoordinateInterface->GetKey(), att->GetContext(), -1, plIntRefMsg::kChildObject); + intRefMsg->SetRef(child); + hsgResMgr::ResMgr()->AddViaNotify(intRefMsg, plRefFlags::kPassiveRef); + } + } + } + else // Am I the final destination? + { + retVal = IMsgHandle(msg); + } + + if( msg->HasBCastFlag(plMessage::kPropagateToModifiers) ) + { + retVal |= IPropagateToModifiers(msg); + } + + if (msg->HasBCastFlag(plMessage::kPropagateToChildren)) + { + const plCoordinateInterface* ci = GetCoordinateInterface(); + for (int i = 0; i < ci->GetNumChildren(); i++) + { + plSceneObject* child = (plSceneObject*)ci->GetChild(i)->GetOwner(); + if (child) + { + hsBool modRet = child->MsgReceive(msg); + retVal |= modRet; + } + } + } + + // Might want an option to propagate messages to children here. + + return plSynchedObject::MsgReceive(msg); +} + +hsBool plSceneObject::IMsgHandle(plMessage* msg) +{ + // To start with, plSceneObject only handles messages to add or remove + // references. Current references are other plSceneObjects and plModifiers + plObjRefMsg* refMsg = plObjRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + switch( refMsg->fType ) + { + case plObjRefMsg::kModifier: + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + plModifier* mod = plModifier::ConvertNoRef(refMsg->GetRef()); + IAddModifier(mod, refMsg->fWhich); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + plModifier* mod = (plModifier*)refMsg->GetRef(); + IRemoveModifier(mod); + } + } + return true; + case plObjRefMsg::kInterface: + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + plObjInterface* oi = plObjInterface::ConvertNoRef(refMsg->GetRef()); + ISetInterface(oi); + } + else + if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + plObjInterface* oi = (plObjInterface*)refMsg->GetRef(); + // TODO - This will crash if oi's already been deleted + IRemoveInterface(oi->ClassIndex(), oi); + } + } + return true; + } + + + return false; + } + plNodeChangeMsg* nodeChange = plNodeChangeMsg::ConvertNoRef(msg); + if( nodeChange ) + { + SetSceneNode(nodeChange->GetNodeKey()); + return true; + } + + if( plIntRefMsg::ConvertNoRef(msg) ) + { + if (fCoordinateInterface) + fCoordinateInterface->MsgReceive(msg); + if (fAudioInterface) + fAudioInterface->MsgReceive(msg); + return true; + } + + if (plCorrectionMsg::ConvertNoRef(msg)) + { + hsAssert(fCoordinateInterface, "Unimplemented, also need to register this one we just made with the resource manager"); + + if (fCoordinateInterface) + fCoordinateInterface->MsgReceive(msg); + + return true; + } + + // check for generic enable/disable message (passed to interfaces) + + plEnableMsg* pEnableMsg = plEnableMsg::ConvertNoRef( msg ); + if (pEnableMsg) + { + if( pEnableMsg->Cmd(plEnableMsg::kDrawable) ) + { + plDrawInterface* di = GetVolatileDrawInterface(); + if( di ) + di->MsgReceive(msg); + plObjInterface* li = GetVolatileGenericInterface(CLASS_INDEX_SCOPED(plLightInfo)); + if( li ) + li->MsgReceive(msg); + } + if ( pEnableMsg->Cmd(plEnableMsg::kAll) && GetDrawInterface() ) + GetVolatileDrawInterface()->MsgReceive(msg); + + if ( pEnableMsg->Cmd( plEnableMsg::kPhysical ) || pEnableMsg->Cmd( plEnableMsg::kAll) ) + { + if ( GetSimulationInterface() ) + { + GetVolatileSimulationInterface()->MsgReceive(msg); + } + else + { + // if someone is trying to disable the physics on a sceneobject that doesn't have a physics interface... + // they might be trying to disable the avatar when the PhysX controller is being used + // ...so, look to see if this is an avatar object, and tell the avatar to disable the physics + IPropagateToModifiers(msg); + } + } + + if ( (pEnableMsg->Cmd( plEnableMsg::kAudible ) || pEnableMsg->Cmd( plEnableMsg::kAll )) && GetAudioInterface() ) + GetVolatileAudioInterface()->MsgReceive(msg); + + if( pEnableMsg->Cmd( plEnableMsg::kAll ) ) + { + IPropagateToGenerics(pEnableMsg); + } + else if( pEnableMsg->Cmd( plEnableMsg::kByType ) ) + { + IPropagateToGenerics(pEnableMsg->Types(), pEnableMsg); + } + return true; + } + + // warp message + if( plWarpMsg::ConvertNoRef(msg) ) + { + // if there's a simulation interface, it needs to know about the warp + // *** it would probably be better if it got this from the coordinate interface, as + // *** only the coordinate interface knows to propagate it to children. + // if(fSimulationInterface) + // { + // fSimulationInterface->MsgReceive(msg); + // } + + // the coordinate interface always gets the warp + if (fCoordinateInterface) + { + fCoordinateInterface->MsgReceive(msg); + } + return true; + } + + if ( plSimulationMsg::ConvertNoRef(msg) ) + { + if(fSimulationInterface) + { + fSimulationInterface->MsgReceive(msg); + } + return true; + } + + // audio message + if (plSoundMsg::ConvertNoRef(msg) ) + { + if( fAudioInterface ) + return(GetVolatileAudioInterface()->MsgReceive(msg)); + return true; + } + + return false; +} + +void plSceneObject::IPropagateToGenerics(const hsBitVector& types, plMessage* msg) +{ + hsBitIterator iter(types); + int i; + for( i = 0; i < fGenerics.GetCount(); i++ ) + { + if( fGenerics[i] ) + { + for( iter.Begin(); !iter.End(); iter.Advance() ) + { + if( plFactory::DerivesFrom(iter.Current(), fGenerics[i]->ClassIndex()) ) + { + fGenerics[i]->MsgReceive(msg); + break; + } + } + } + } +} + +void plSceneObject::IPropagateToGenerics(plMessage* msg) +{ + int i; + for( i = 0; i < fGenerics.GetCount(); i++ ) + { + if( fGenerics[i] ) + fGenerics[i]->MsgReceive(msg); + } +} + +plObjInterface* plSceneObject::GetVolatileGenericInterface(UInt16 classIdx) const +{ + int i; + for( i = 0; i < fGenerics.GetCount(); i++ ) + { + if( fGenerics[i] && plFactory::DerivesFrom(classIdx, fGenerics[i]->ClassIndex()) ) + return fGenerics[i]; + } + return nil; +} + +void plSceneObject::IAddGeneric(plObjInterface* gen) +{ + if( gen ) + { + if( fGenerics.kMissingIndex == fGenerics.Find(gen) ) + { + fGenerics.Append(gen); + gen->ISetOwner(this); + } + } +} + +void plSceneObject::IRemoveGeneric(plObjInterface* gen) +{ + if( gen ) + { + int idx = fGenerics.Find(gen); + if( fGenerics.kMissingIndex != idx ) + { + gen->ISetOwner(nil); + fGenerics.Remove(idx); + } + } +} + +void plSceneObject::IRemoveAllGenerics() +{ + int i; + for( i = 0; i < fGenerics.GetCount(); i++ ) + { + if( fGenerics[i] ) + fGenerics[i]->ISetOwner(nil); + } + fGenerics.Reset(); +} + +void plSceneObject::ISetDrawInterface(plDrawInterface* di) +{ + if( fDrawInterface != di ) + { + if( fDrawInterface ) + fDrawInterface->ISetOwner(nil); + + fDrawInterface = di; + if( di ) + di->ISetOwner(this); + } +} + +void plSceneObject::ISetSimulationInterface(plSimulationInterface* si) +{ + if( fSimulationInterface != si ) + { + if( fSimulationInterface ) + fSimulationInterface->ISetOwner(nil); + + fSimulationInterface = si; + if( si ) + si->ISetOwner(this); + } +} + +void plSceneObject::ISetAudioInterface(plAudioInterface* ai) +{ + if( fAudioInterface != ai ) + { + if( fAudioInterface ) + fAudioInterface->ISetOwner(nil); + fAudioInterface = ai; + if( ai ) + ai->ISetOwner(this); + } +} + +void plSceneObject::ISetCoordinateInterface(plCoordinateInterface* ci) +{ + if( fCoordinateInterface != ci ) + { + if( fCoordinateInterface ) + fCoordinateInterface->ISetOwner(nil); + + fCoordinateInterface = ci; + if( ci ) + ci->ISetOwner(this); + } +} + +// +// "is ready to process Loads"? Check base class and modifiers. +// +hsBool plSceneObject::IsFinal() +{ + if (!plSynchedObject::IsFinal()) + return false; + + int i; + for(i=0;iIsFinal()) + return false; + + return true; +} + +// Export only. Interfaces perm on object. +void plSceneObject::SetDrawInterface(plDrawInterface* di) +{ + ISetDrawInterface(di); +} + +void plSceneObject::SetSimulationInterface(plSimulationInterface* si) +{ + ISetSimulationInterface(si); +} + +void plSceneObject::SetAudioInterface(plAudioInterface* ai) +{ + ISetAudioInterface(ai); +} + +void plSceneObject::SetCoordinateInterface(plCoordinateInterface* ci) +{ + ISetCoordinateInterface(ci); +} + +void plSceneObject::AddModifier(plModifier* mo) +{ + IAddModifier(mo, fModifiers.GetCount()); +} + +void plSceneObject::RemoveModifier(plModifier* mo) +{ + IRemoveModifier(mo); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSceneObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSceneObject.h new file mode 100644 index 00000000..d9577020 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSceneObject.h @@ -0,0 +1,165 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSceneObject_inc +#define plSceneObject_inc + +#include "hsBitVector.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnNetCommon/plSynchedObject.h" +#include "../pnNetCommon/plSynchedValue.h" +#include "../pnModifier/plModifier.h" +#include "hsStream.h" + +class plObjInterface; +class plDrawInterface; +class plSimulationInterface; +class plCoordinateInterface; +class plAudioInterface; +class plMessage; +class hsStream; +class hsResMgr; +class plMessage; +class plDispatchBase; +struct hsMatrix44; + +// The following two aren't dragging the Conversion side into the runtime. +// They are just to let the converter do things we don't want done at runtime. +// Nice that we can make friends with these opaque classes. +class plMaxNode; +class plMaxNodeBase; + +class plSceneObject : public plSynchedObject { + friend class plSynchedValueBase; +private: + plDrawInterface* GetVolatileDrawInterface() { return fDrawInterface; } + plSimulationInterface* GetVolatileSimulationInterface() { return fSimulationInterface; } + plCoordinateInterface* GetVolatileCoordinateInterface() { return fCoordinateInterface; } + plAudioInterface* GetVolatileAudioInterface() { return fAudioInterface; } + plObjInterface* GetVolatileGenericInterface(UInt16 classIdx) const; + + plModifier* GetVolatileModifier(int i) { return fModifiers[i]; } + + plKey fSceneNode; + + friend class plModifier; + friend class plCoordinateInterface; + friend class plObjInterface; + friend class plMaxNode; + friend class plMaxNodeBase; + + hsBool IMsgHandle(plMessage* msg); +protected: + + plDrawInterface* fDrawInterface; + plSimulationInterface* fSimulationInterface; + plCoordinateInterface* fCoordinateInterface; + plAudioInterface* fAudioInterface; + + hsTArray fModifiers; + + hsTArray fGenerics; + + void ISetDrawInterface(plDrawInterface* di); + void ISetSimulationInterface(plSimulationInterface* si); + void ISetAudioInterface(plAudioInterface* ai); + void ISetCoordinateInterface(plCoordinateInterface* ci); + + void IAddGeneric(plObjInterface* gen); + void IRemoveGeneric(plObjInterface* gen); + void IRemoveAllGenerics(); + void IPropagateToGenerics(plMessage* msg); + void IPropagateToGenerics(const hsBitVector& types, plMessage* msg); + + void IAddModifier(plModifier* mo, int i); + void IRemoveModifier(plModifier* mo); + hsBool IPropagateToModifiers(plMessage* msg); + + void ISetInterface(plObjInterface* iface); + void IRemoveInterface(plObjInterface* iface); + void IRemoveInterface(Int16 idx, plObjInterface* iface=nil); + + void ISetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + +public: + + plSceneObject(); + virtual ~plSceneObject(); + + CLASSNAME_REGISTER( plSceneObject ); + GETINTERFACE_ANY( plSceneObject, plSynchedObject ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual const plDrawInterface* GetDrawInterface() const { return fDrawInterface; } + virtual const plSimulationInterface* GetSimulationInterface() const { return fSimulationInterface; } + virtual const plCoordinateInterface* GetCoordinateInterface() const { return fCoordinateInterface; } + virtual const plAudioInterface* GetAudioInterface() const { return fAudioInterface; } + + int GetNumGenerics() const { return fGenerics.GetCount(); } + const plObjInterface* GetGeneric(int i) const { return fGenerics[i]; } + + plObjInterface* GetGenericInterface(UInt16 classIdx) const { return GetVolatileGenericInterface(classIdx); } + + int GetNumModifiers() const { return fModifiers.GetCount(); } + const plModifier* GetModifier(int i) const { return fModifiers[i]; } + const plModifier* GetModifierByType(UInt16 classIdx) const; + + virtual hsBool MsgReceive(plMessage* msg); + virtual hsBool Eval(double secs, hsScalar del); + + void SetSceneNode(plKey newNode); + plKey GetSceneNode() const; + + // Network only strange function. Do not emulate or generalize this functionality. + virtual void SetNetGroup(plNetGroupId netGroup); + + virtual void ReleaseData( void ); + + // Force an immediate re-sync of the transforms in the hierarchy this object belongs to, + // as opposed to waiting for the plTransformMsg to resync. + void FlushTransform(); + void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + hsMatrix44 GetLocalToWorld() const; + hsMatrix44 GetWorldToLocal() const; + hsMatrix44 GetLocalToParent() const; + hsMatrix44 GetParentToLocal() const; + + hsBool IsFinal(); // "is ready to process Loads" + + // Export only + virtual void SetDrawInterface(plDrawInterface* di); + virtual void SetSimulationInterface(plSimulationInterface* si); + virtual void SetAudioInterface(plAudioInterface* ai); + virtual void SetCoordinateInterface(plCoordinateInterface* ci); + + virtual void AddModifier(plModifier* mo); + virtual void RemoveModifier(plModifier* mo); +}; + +#endif // plSceneObject_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSimulationInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSimulationInterface.cpp new file mode 100644 index 00000000..7c0108b5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSimulationInterface.cpp @@ -0,0 +1,193 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSimulationInterface.h" + +#include "plgDispatch.h" +#include "plPhysical.h" +#include "hsBounds.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "plSceneObject.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../pnMessage/plWarpMsg.h" +#include "../pnMessage/plSimulationSynchMsg.h" +#include "../pnMessage/plSimulationMsg.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnKeyedObject/plKey.h" + +plSimulationInterface::plSimulationInterface() : fPhysical(nil) +{ +} + +plSimulationInterface::~plSimulationInterface() +{ +} + +void plSimulationInterface::ISetSceneNode(plKey newNode) +{ + if (fPhysical) + fPhysical->SetSceneNode(newNode); +} + +void plSimulationInterface::SetProperty(int prop, hsBool on) +{ + plObjInterface::SetProperty(prop, on); // set the property locally + + if (fPhysical) + fPhysical->SetProperty(prop, on ? true : false); +} + +void plSimulationInterface::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + if (fPhysical) + fPhysical->SetTransform(l2w, w2l); +} + +void plSimulationInterface::ClearLinearVelocity() +{ + if (fPhysical) + fPhysical->ClearLinearVelocity(); +} + +void plSimulationInterface::Read(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Read(s, mgr); + + // fProps is already read by plObjInterface, but we'll do it again here to + // avoid breaking the format + fProps.Read(s); + // Also unnecessary + int poop = s->ReadSwap32(); + + plIntRefMsg* refMsg = TRACKED_NEW plIntRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plIntRefMsg::kPhysical, 0); + mgr->ReadKeyNotifyMe(s, refMsg, plRefFlags::kActiveRef); +} + +void plSimulationInterface::Write(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Write(s, mgr); + + // Legacy crap + fProps.Write(s); + s->WriteSwap32(0); + + mgr->WriteKey(s, fPhysical); +} + +void plSimulationInterface::ReleaseData() +{ + plPhysical *physical = fPhysical; + + if (physical) + { + // To get rid of our data, we need to release our active ref and tell the SceneNode + // to dump it. It will autodestruct after those two active refs are released, unless + // someone else has a ref on it as well (in which case we don't want to be nuking it + // anyway). + if (physical->GetSceneNode()) + { + plKey nodeKey = physical->GetSceneNode(); + + nodeKey->Release(physical->GetKey()); + } + + GetKey()->Release(physical->GetKey()); + } +} + +hsBool plSimulationInterface::MsgReceive(plMessage* msg) +{ + plIntRefMsg* intRefMsg = plIntRefMsg::ConvertNoRef(msg); + if (intRefMsg) + { + switch (intRefMsg->fType) + { + case plIntRefMsg::kPhysical: + if (intRefMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove)) + { + plPhysical* phys = plPhysical::ConvertNoRef(intRefMsg->GetRef()); + // *** for some reason, we sometimes get a null pointer here + // *** if we do, we're just going to ignore it for now + if (phys) + { + hsAssert(phys == fPhysical, "Removing Physical I don't have"); + fPhysical = nil; + } + } + else + { + fPhysical = plPhysical::ConvertNoRef(intRefMsg->GetRef()); + } + return true; + } + } + + plEnableMsg* pEnableMsg = plEnableMsg::ConvertNoRef(msg); + if (pEnableMsg) + { + SetProperty(kDisable, pEnableMsg->Cmd(kDisable)); + SetProperty(kPinned, pEnableMsg->Cmd(kDisable)); + return true; + } + + plWarpMsg* pWarpMsg = plWarpMsg::ConvertNoRef(msg); + if (pWarpMsg) + { + if (fPhysical) + { + hsMatrix44 l2w = pWarpMsg->GetTransform(); + hsMatrix44 inv; + l2w.GetInverse(&inv); + SetTransform(l2w, inv); + if (pWarpMsg->GetWarpFlags() & plWarpMsg::kZeroVelocity) + { + ClearLinearVelocity(); + } + } + } + + plSimulationMsg* pSimMsg = plSimulationMsg::ConvertNoRef(msg); + if (pSimMsg) + { + if (fPhysical) + fPhysical->MsgReceive(pSimMsg); + } + + return plObjInterface::MsgReceive(msg); +} + +// Export only. Use messages for runtime. +void plSimulationInterface::SetPhysical(plPhysical* ph) +{ + fPhysical = ph; +} + +plPhysical* plSimulationInterface::GetPhysical() const +{ + return fPhysical; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSimulationInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSimulationInterface.h new file mode 100644 index 00000000..cee7cde8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/plSimulationInterface.h @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSimulationInterface_inc +#define plSimulationInterface_inc + +#include "plObjInterface.h" + +class plPhysical; +struct hsMatrix44; +class hsBounds3Ext; + +class plSimulationInterface : public plObjInterface +{ +public: + // Props inc by 1 (bit shift in bitvector). + enum plSimulationProperties { + // prop 0 is always disable, , declared in plObjInterface + kDisable = 0, // no more interaction. no interference detection + kWeightless_DEAD, // unaffected by gravity, but not massless + kPinned, // stop moving. keep colliding. + kWarp_DEAD, // keep moving, no colliding (a pattern is emerging here...) + kUpright_DEAD, // stand upright (mainly for the player) + kPassive, // don't push new positions to sceneobject + kRotationForces_DEAD, // rotate using forces + kCameraAvoidObject_DEAD, // camera will try and fly around this obsticle + kPhysAnim, // this object is animated, and the animation can apply force + kStartInactive, // deactive this object at start time. will reactivate when hit + kNoSynchronize, // don't synchronize this physical + kSuppressed_DEAD, // physical still exists but is not in simulation: no forces, contact, or reports + kNoOwnershipChange, // Don't automatically change net ownership on this physical when it is collided with + kAvAnimPushable, // Something the avatar should react to and push against + kNumProps + }; + +protected: + plPhysical* fPhysical; + + friend class plSceneObject; + + virtual void ISetSceneNode(plKey newNode); + +public: + plSimulationInterface(); + ~plSimulationInterface(); + + CLASSNAME_REGISTER( plSimulationInterface ); + GETINTERFACE_ANY( plSimulationInterface, plObjInterface ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void SetProperty(int prop, hsBool on); + Int32 GetNumProperties() const { return kNumProps; } + + // Transform settable only, if you want it get it from the coordinate interface. + void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + // Bounds are gettable only, they are computed on the physical. + const hsBounds3Ext GetLocalBounds(); + const hsBounds3Ext GetWorldBounds() const; + const hsBounds3Ext GetMaxWorldBounds(); + void ClearLinearVelocity(); + + virtual hsBool MsgReceive(plMessage* msg); + + // Export only. + void SetPhysical(plPhysical* phys); + void ReleaseData(); + + plPhysical* GetPhysical() const; +}; + + +#endif // plSimulationInterface_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/pnSceneObjectCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/pnSceneObjectCreatable.h new file mode 100644 index 00000000..34334001 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSceneObject/pnSceneObjectCreatable.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSceneObjectCreatable_inc +#define plSceneObjectCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plObjInterface.h" + +REGISTER_NONCREATABLE( plObjInterface ); + +#include "plAudioInterface.h" + +REGISTER_CREATABLE( plAudioInterface ); + +#include "plCoordinateInterface.h" + +REGISTER_CREATABLE( plCoordinateInterface ); + +#include "plDrawInterface.h" + +REGISTER_CREATABLE( plDrawInterface ); + +#include "plSimulationInterface.h" + +REGISTER_CREATABLE( plSimulationInterface ); + +#include "plSceneObject.h" + +REGISTER_CREATABLE( plSceneObject ); + + +#endif // plSceneObjectCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/Pch.h new file mode 100644 index 00000000..89910a90 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/Pch.h @@ -0,0 +1,38 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSIMPLENET_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSIMPLENET_PCH_H + + +#include "pnSimpleNet.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.cpp new file mode 100644 index 00000000..1fe800e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.cpp @@ -0,0 +1,631 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local types +* +***/ + +struct SimpleNetConn : AtomicRef { + LINK(SimpleNetConn) link; + AsyncSocket sock; + AsyncCancelId cancelId; + unsigned channelId; + bool abandoned; + struct ConnectParam * connectParam; + + SimpleNet_MsgHeader * oversizeMsg; + ARRAY(byte) oversizeBuffer; + + ~SimpleNetConn () { + ASSERT(!link.IsLinked()); + } +}; + +struct SimpleNetChannel : AtomicRef, THashKeyVal { + HASHLINK(SimpleNetChannel) link; + + FSimpleNetOnMsg onMsg; + FSimpleNetOnError onError; + + LISTDECL(SimpleNetConn, link) conns; + + SimpleNetChannel (unsigned channel) + : THashKeyVal(channel) + { } + ~SimpleNetChannel () { + ASSERT(!link.IsLinked()); + ASSERT(!conns.Head()); + } +}; + +struct ConnectParam { + SimpleNetChannel * channel; + FSimpleNetOnConnect callback; + void * param; + + ~ConnectParam () { + if (channel) + channel->DecRef(); + } +}; + + +/***************************************************************************** +* +* Local data +* +***/ + +static bool s_running; +static CCritSect s_critsect; +static FSimpleNetQueryAccept s_queryAccept; +static void * s_queryAcceptParam; + +static HASHTABLEDECL( + SimpleNetChannel, + THashKeyVal, + link +) s_channels; + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static void NotifyConnSocketConnect (SimpleNetConn * conn) { + + conn->TransferRef("Connecting", "Connected"); + + conn->connectParam->callback( + conn->connectParam->param, + conn, + kNetSuccess + ); + + DEL(conn->connectParam); + conn->connectParam = nil; +} + +//============================================================================ +static void NotifyConnSocketConnectFailed (SimpleNetConn * conn) { + + s_critsect.Enter(); + { + conn->link.Unlink(); + } + s_critsect.Leave(); + + conn->connectParam->callback( + conn->connectParam->param, + nil, + kNetErrConnectFailed + ); + + DEL(conn->connectParam); + conn->connectParam = nil; + + conn->DecRef("Connecting"); + conn->DecRef("Lifetime"); +} + +//============================================================================ +static void NotifyConnSocketDisconnect (SimpleNetConn * conn) { + + bool abandoned; + SimpleNetChannel * channel; + s_critsect.Enter(); + { + abandoned = conn->abandoned; + if (nil != (channel = s_channels.Find(conn->channelId))) + channel->IncRef(); + conn->link.Unlink(); + } + s_critsect.Leave(); + + if (channel && !abandoned) { + channel->onError(conn, kNetErrDisconnected); + channel->DecRef(); + } + + conn->DecRef("Connected"); +} + +//============================================================================ +static bool NotifyConnSocketRead (SimpleNetConn * conn, AsyncNotifySocketRead * read) { + + SimpleNetChannel * channel; + s_critsect.Enter(); + { + if (nil != (channel = s_channels.Find(conn->channelId))) + channel->IncRef(); + } + s_critsect.Leave(); + + if (!channel) + return false; + + bool result = true; + + const byte * curr = read->buffer; + const byte * term = curr + read->bytes; + + while (curr < term) { + // Reading oversize msg? + if (conn->oversizeBuffer.Count()) { + unsigned spaceLeft = conn->oversizeMsg->messageBytes - conn->oversizeBuffer.Count(); + unsigned copyBytes = min(spaceLeft, term - curr); + conn->oversizeBuffer.Add(curr, copyBytes); + + curr += copyBytes; + + // Wait until we have received the entire message + if (copyBytes != spaceLeft) + break; + + // Dispatch oversize msg + if (!channel->onMsg(conn, conn->oversizeMsg)) { + result = false; + break; + } + + conn->oversizeBuffer.SetCount(0); + continue; + } + + // Wait until we receive the entire message header + if (term - curr < sizeof(SimpleNet_MsgHeader)) + break; + + SimpleNet_MsgHeader * msg = (SimpleNet_MsgHeader *) read->buffer; + + // Sanity check message size + if (msg->messageBytes < sizeof(*msg)) { + result = false; + break; + } + + // Handle oversized messages + if (msg->messageBytes > kAsyncSocketBufferSize) { + + conn->oversizeBuffer.SetCount(msg->messageBytes); + conn->oversizeMsg = (SimpleNet_MsgHeader *) conn->oversizeBuffer.Ptr(); + *conn->oversizeMsg = *msg; + + curr += sizeof(*msg); + continue; + } + + // Wait until we have received the entire message + const byte * msgTerm = (const byte *) curr + msg->messageBytes; + if (msgTerm > term) + break; + curr = msgTerm; + + // Dispatch msg + if (!channel->onMsg(conn, msg)) { + result = false; + break; + } + } + + // Return count of bytes we processed + read->bytesProcessed = curr - read->buffer; + + channel->DecRef(); + return result; +} + +//============================================================================ +static bool AsyncNotifySocketProc ( + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +) { + bool result = true; + SimpleNetConn * conn; + + switch (code) { + case kNotifySocketListenSuccess: { + + AsyncNotifySocketListen * listen = (AsyncNotifySocketListen *) notify; + + const SimpleNet_ConnData & connect = *(const SimpleNet_ConnData *) listen->buffer; + listen->bytesProcessed += sizeof(connect); + + SimpleNetChannel * channel; + s_critsect.Enter(); + { + if (nil != (channel = s_channels.Find(connect.channelId))) + channel->IncRef(); + } + s_critsect.Leave(); + + if (!channel) + break; + + conn = NEWZERO(SimpleNetConn); + conn->channelId = channel->GetValue(); + conn->IncRef("Lifetime"); + conn->IncRef("Connected"); + conn->sock = sock; + *userState = conn; + + bool accepted = s_queryAccept( + s_queryAcceptParam, + channel->GetValue(), + conn, + listen->remoteAddr + ); + + if (!accepted) { + SimpleNetDisconnect(conn); + } + else { + s_critsect.Enter(); + { + channel->conns.Link(conn); + } + s_critsect.Leave(); + } + + channel->DecRef(); + } + break; + + case kNotifySocketConnectSuccess: { + conn = (SimpleNetConn *) notify->param; + *userState = conn; + bool abandoned; + + s_critsect.Enter(); + { + conn->sock = sock; + conn->cancelId = 0; + abandoned = conn->abandoned; + } + s_critsect.Leave(); + + if (abandoned) + AsyncSocketDisconnect(sock, true); + else + NotifyConnSocketConnect(conn); + } + break; + + case kNotifySocketConnectFailed: + conn = (SimpleNetConn *) notify->param; + NotifyConnSocketConnectFailed(conn); + break; + + case kNotifySocketDisconnect: + conn = (SimpleNetConn *) *userState; + NotifyConnSocketDisconnect(conn); + break; + + case kNotifySocketRead: + conn = (SimpleNetConn *) *userState; + result = NotifyConnSocketRead(conn, (AsyncNotifySocketRead *) notify); + break; + } + + return result; +} + +//============================================================================ +static void Connect (const NetAddress & addr, ConnectParam * cp) { + + SimpleNetConn * conn = NEWZERO(SimpleNetConn); + conn->channelId = cp->channel->GetValue(); + conn->connectParam = cp; + conn->IncRef("Lifetime"); + conn->IncRef("Connecting"); + + s_critsect.Enter(); + { + cp->channel->conns.Link(conn); + + SimpleNet_Connect connect; + connect.hdr.connType = kConnTypeSimpleNet; + connect.hdr.hdrBytes = sizeof(connect.hdr); + connect.hdr.buildId = BuildId(); + connect.hdr.buildType = BuildType(); + connect.hdr.branchId = BranchId(); + connect.hdr.productId = ProductId(); + connect.data.channelId = cp->channel->GetValue(); + + AsyncSocketConnect( + &conn->cancelId, + addr, + AsyncNotifySocketProc, + conn, + &connect, + sizeof(connect) + ); + + conn = nil; + cp = nil; + } + s_critsect.Leave(); + + DEL(conn); + DEL(cp); +} + +//============================================================================ +static void AsyncLookupCallback ( + void * param, + const wchar name[], + unsigned addrCount, + const NetAddress addrs[] +) { + ref(name); + + ConnectParam * cp = (ConnectParam *)param; + + if (!addrCount) { + if (cp->callback) + cp->callback(cp->param, nil, kNetErrNameLookupFailed); + DEL(cp); + return; + } + + Connect(addrs[0], (ConnectParam *)param); +} + + +/***************************************************************************** +* +* Exported functions +* +***/ + +//============================================================================ +void SimpleNetInitialize () { + + s_running = true; + + AsyncSocketRegisterNotifyProc( + kConnTypeSimpleNet, + AsyncNotifySocketProc + ); +} + +//============================================================================ +void SimpleNetShutdown () { + + s_running = false; + + ASSERT(!s_channels.Head()); + + AsyncSocketUnregisterNotifyProc( + kConnTypeSimpleNet, + AsyncNotifySocketProc + ); +} + +//============================================================================ +void SimpleNetConnIncRef (SimpleNetConn * conn) { + + ASSERT(s_running); + ASSERT(conn); + + conn->IncRef(); +} + +//============================================================================ +void SimpleNetConnDecRef (SimpleNetConn * conn) { + + ASSERT(s_running); + ASSERT(conn); + + conn->DecRef(); +} + +//============================================================================ +bool SimpleNetStartListening ( + FSimpleNetQueryAccept queryAccept, + void * param +) { + ASSERT(s_running); + ASSERT(queryAccept); + ASSERT(!s_queryAccept); + + s_queryAccept = queryAccept; + s_queryAcceptParam = param; + + NetAddress addr; + NetAddressFromNode(0, kNetDefaultSimpleNetPort, &addr); + return (0 != AsyncSocketStartListening(addr, nil)); +} + +//============================================================================ +void SimpleNetStopListening () { + + ASSERT(s_running); + + NetAddress addr; + NetAddressFromNode(0, kNetDefaultSimpleNetPort, &addr); + AsyncSocketStopListening(addr, nil); + + s_queryAccept = nil; + s_queryAcceptParam = nil; +} + +//============================================================================ +void SimpleNetCreateChannel ( + unsigned channelId, + FSimpleNetOnMsg onMsg, + FSimpleNetOnError onError +) { + ASSERT(s_running); + + SimpleNetChannel * channel = NEWZERO(SimpleNetChannel)(channelId); + channel->IncRef(); + + s_critsect.Enter(); + { + #ifdef HS_DEBUGGING + { + SimpleNetChannel * existing = s_channels.Find(channelId); + ASSERT(!existing); + } + #endif + + channel->onMsg = onMsg; + channel->onError = onError; + s_channels.Add(channel); + channel->IncRef(); + } + s_critsect.Leave(); + + channel->DecRef(); +} + +//============================================================================ +void SimpleNetDestroyChannel (unsigned channelId) { + + ASSERT(s_running); + + SimpleNetChannel * channel; + s_critsect.Enter(); + { + if (nil != (channel = s_channels.Find(channelId))) { + s_channels.Unlink(channel); + while (SimpleNetConn * conn = channel->conns.Head()) { + SimpleNetDisconnect(conn); + channel->conns.Unlink(conn); + } + } + } + s_critsect.Leave(); + + if (channel) + channel->DecRef(); +} + +//============================================================================ +void SimpleNetStartConnecting ( + unsigned channelId, + const wchar addr[], + FSimpleNetOnConnect onConnect, + void * param +) { + ASSERT(s_running); + ASSERT(onConnect); + + ConnectParam * cp = NEW(ConnectParam); + cp->callback = onConnect; + cp->param = param; + + s_critsect.Enter(); + { + if (nil != (cp->channel = s_channels.Find(channelId))) + cp->channel->IncRef(); + } + s_critsect.Leave(); + + ASSERT(cp->channel); + + // Do we need to lookup the address? + const wchar * name = addr; + while (unsigned ch = *name) { + ++name; + if (!(isdigit(ch) || ch == L'.' || ch == L':')) { + + AsyncCancelId cancelId; + AsyncAddressLookupName( + &cancelId, + AsyncLookupCallback, + addr, + kNetDefaultSimpleNetPort, + cp + ); + break; + } + } + if (!name[0]) { + NetAddress netAddr; + NetAddressFromString(&netAddr, addr, kNetDefaultSimpleNetPort); + Connect(netAddr, cp); + } +} + +//============================================================================ +void SimpleNetDisconnect ( + SimpleNetConn * conn +) { + ASSERT(s_running); + ASSERT(conn); + + s_critsect.Enter(); + { + conn->abandoned = true; + if (conn->sock) { + AsyncSocketDisconnect(conn->sock, true); + conn->sock = nil; + } + else if (conn->cancelId) { + AsyncSocketConnectCancel(AsyncNotifySocketProc, conn->cancelId); + conn->cancelId = nil; + } + } + s_critsect.Leave(); + + conn->DecRef("Lifetime"); +} + +//============================================================================ +void SimpleNetSend ( + SimpleNetConn * conn, + SimpleNet_MsgHeader * msg +) { + ASSERT(s_running); + ASSERT(msg); + ASSERT(msg->messageBytes != (dword)-1); + ASSERT(conn); + + s_critsect.Enter(); + { + if (conn->sock) + AsyncSocketSend(conn->sock, msg, msg->messageBytes); + } + s_critsect.Leave(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.h new file mode 100644 index 00000000..d297703a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.h @@ -0,0 +1,167 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnSimpleNet/pnSimpleNet.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSIMPLENET_PNSIMPLENET_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSIMPLENET_PNSIMPLENET_H + + +#include "pnUtils/pnUtils.h" +#include "pnProduct/pnProduct.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" + +/***************************************************************************** +* +* SimpleNet: +* - TCP only, Nagle buffered only. +* - Asynchronous callbacks only. +* - Not encrypted, not compressed, no transaction support. +* - Good for trivial networked applications only. Examples include: +* - CSR client automation by CSR tools. +* - Bob and I (eap) talked about 3dsmax automating +* the client for the purpose of reloading reexported +* assets. With SimpleNet, the client could be running +* on a separate machine than Max. +* +***/ + +// Because newer clients must remain compatible with older servers, +// these values may never change. +enum ESimpleNetChannel { + kSimpleNetChannelNil = 0, + kSimpleNetChannelCsr = 1, + kSimpleNetChannelMax = 2, + + kMaxSimpleNetChannels +}; +COMPILER_ASSERT_HEADER(ESimpleNetChannel, kMaxSimpleNetChannels <= 0xff); + + +//============================================================================ +// BEGIN PACKED DATA STRUCTURES +//============================================================================ +#include + +//============================================================================ +// Connect packet + +struct SimpleNet_ConnData { + unsigned channelId; +}; +struct SimpleNet_Connect { + AsyncSocketConnectPacket hdr; + SimpleNet_ConnData data; +}; + +//============================================================================ +// Message header + +struct SimpleNet_MsgHeader { +private: + dword channelId; +public: + dword messageId; + dword messageBytes; + + SimpleNet_MsgHeader (dword channelId, dword messageId) + : channelId(channelId) + , messageId(messageId) + #ifdef HS_DEBUGGING + , messageBytes((dword)-1) + #endif + { } +}; + + +/***************************************************************************** +* +* Simple Network API +* +***/ + +struct SimpleNetConn; + +void SimpleNetInitialize (); +void SimpleNetShutdown (); + +void SimpleNetConnIncRef (SimpleNetConn * conn); +void SimpleNetConnDecRef (SimpleNetConn * conn); + +typedef bool (*FSimpleNetOnMsg) ( // return false to disconnect socket + SimpleNetConn * conn, + SimpleNet_MsgHeader * msg +); +typedef void (*FSimpleNetOnError) ( + SimpleNetConn * conn, + ENetError error +); +typedef void (*FSimpleNetOnConnect) ( + void * param, + SimpleNetConn * conn, + ENetError result +); +typedef bool (*FSimpleNetQueryAccept) ( // return true to accept incoming connection + void * param, + unsigned channel, + SimpleNetConn * conn, + const NetAddress & addr +); + +void SimpleNetCreateChannel ( + unsigned channel, + FSimpleNetOnMsg onMsg, + FSimpleNetOnError onError +); +void SimpleNetDestroyChannel ( + unsigned channel +); + +bool SimpleNetStartListening ( + FSimpleNetQueryAccept queryAccept, + void * param +); +void SimpleNetStopListening (); + +void SimpleNetStartConnecting ( + unsigned channel, + const wchar addr[], + FSimpleNetOnConnect onConnect, + void * param +); +void SimpleNetDisconnect ( + SimpleNetConn * conn +); +void SimpleNetSend ( + SimpleNetConn * conn, + SimpleNet_MsgHeader * msg +); + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSIMPLENET_PNSIMPLENET_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Intern.h new file mode 100644 index 00000000..510a81b1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Intern.h @@ -0,0 +1,35 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_INTERN_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Pch.h new file mode 100644 index 00000000..2dd50210 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Pch.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_PCH_H + +#include "pnUtils/pnUtils.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" + +// Include ODBC API +#include +#include + +#include "Private/pnSqlAllIncludes.h" +#include "Intern.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlAllIncludes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlAllIncludes.h new file mode 100644 index 00000000..696b1869 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlAllIncludes.h @@ -0,0 +1,39 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlAllIncludes.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_PRIVATE_PNSQLALLINCLUDES_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlAllIncludes.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_PRIVATE_PNSQLALLINCLUDES_H + + +#include "pnSqlConn.h" +#include "pnSqlUtil.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlConn.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlConn.cpp new file mode 100644 index 00000000..88626f42 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlConn.cpp @@ -0,0 +1,1423 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlConn.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + +//#define LOG_SQL_STMTS +//#define ODBC_TRACING +#define ORACLE_FREE_DRIVER + + +/***************************************************************************** +* +* Private +* +***/ + +static unsigned kConnectionTaskCount = 10; +static unsigned kCheckDeadTaskCount = 5; + +static unsigned kLoginTimeoutSecs = 30; +static unsigned kConnectionTimeoutSecs = 60; + +struct SqlState { + SQLCHAR buffer[6]; +}; + +struct SqlStmtHash { + FSqlBindPrepare prepare; + + SqlStmtHash (); + SqlStmtHash (FSqlBindPrepare prepare); + dword GetHash () const; + bool operator== (const SqlStmtHash & rhs) const; +}; + +static const unsigned kStmtFlagBindColMode = 1<<0; +static const unsigned kStmtFlagBindParamMode = 1<<1; +static const unsigned kStmtFlagDriverConnected = 1<<2; +static const unsigned kStmtFlagDelete = 1<<3; +static const unsigned kStmtFlagReset = 1<<4; + +struct SqlStmt : SqlStmtHash { + HASHLINK(SqlStmt) hashLink; + LINK(SqlStmt) listLink; + SqlConn * conn; + SQLHDBC hdbc; + SQLHSTMT hstmt; + unsigned flags; + int bindCol; + int bindParam; + FSqlConnErrorHandler errHandler; + void * errHandlerParam; + unsigned sequence; + unsigned timeoutSecs; + byte * data; + wchar debug[128]; + + SqlStmt (SqlConn * conn); + ~SqlStmt (); + SqlStmt & operator= (const SqlStmt &); // not impl. + bool Initialize (const wchar connectStr[]); + void Reset (); + bool QueryDead (unsigned method); +}; + +static const unsigned kConnFlagInvalid = 1<<0; + +struct SqlConn { + CCritSect critsect; + SQLHENV henv; + unsigned flags; + unsigned desiredConns; + unsigned currentConns; + unsigned connectTasks; + unsigned checkDeadTasks; + unsigned sequence; + unsigned timeoutSecs; + AsyncThreadTaskList * taskList; + wchar * connectStr; + + LISTDECL( + SqlStmt, + listLink + ) stmts; + + HASHTABLEDECL( + SqlStmt, + SqlStmtHash, + hashLink + ) freeStmts; + + SqlConn ( + const wchar connectStr[], + SQLHENV henv + ); + ~SqlConn (); + + bool GrowConnections_CS (unsigned attempts); + void AsyncGrowConnections_CS (); + + void CheckDead_CS (); + void AsyncCheckDead_CS (); +}; + + +/***************************************************************************** +* +* Private data +* +***/ + +static long s_perf[kSqlConnNumPerf]; + + +/***************************************************************************** +* +* Logging +* +***/ + +//============================================================================ +static bool EnumDiagRec ( + SQLSMALLINT handleType, + SQLHANDLE handle, + unsigned index, + unsigned code, + SQLSMALLINT destChars, + SQLCHAR dest[], + SQLINTEGER * native, + SqlState * state +) { + SQLSMALLINT msgLen; + int result = SQLGetDiagRec( + handleType, + handle, + (SQLSMALLINT) index, + state->buffer, + native, + dest, + destChars, + &msgLen + ); + + if (result == SQL_NO_DATA) + return false; + + if (!SQL_SUCCEEDED(result)) { + LogMsg(kLogError, "Sql(%u) %d, DiagRec", code, result); + return false; + } + + return true; +} + +//============================================================================ +static void LogErr ( + int result, + SQLSMALLINT handleType, + SQLHANDLE handle, + const wchar function[], + const wchar command[] +) { + if (SQL_SUCCEEDED(result) && result != SQL_SUCCESS_WITH_INFO) + return; + if (result == SQL_NEED_DATA) + return; + if (result == SQL_NO_DATA) + return; + + static long s_code = 1; + long code = AtomicAdd(&s_code, 1); + ELogSeverity severity = result == SQL_SUCCESS_WITH_INFO ? kLogPerf : kLogError; + LogMsg(severity, "Sql(%u) %d, %S, %S", code, result, function, command); + + for (int index = 1;; ++index) { + // Get next message + SqlState state; + SQLINTEGER native; + SQLCHAR msgStr[512]; + const bool indexValid = EnumDiagRec( + handleType, + handle, + index, + code, + arrsize(msgStr), + msgStr, + &native, + &state + ); + if (!indexValid) + break; + + LogMsg(severity, " Sql(%u) %d, %s, %s", code, native, state.buffer, msgStr); + } +} + +//============================================================================ +static void LogStmtError ( + int result, + SqlStmt * stmt, + const wchar function[] +) { + if (result == SQL_SUCCESS) + return; + if (result == SQL_NEED_DATA) + return; + if (result == SQL_NO_DATA) + return; + + // Once a statement has an error, it is possible for it to get "wedged"; + // so reset the statement after this transaction to make it happy. + stmt->flags |= kStmtFlagReset; + + // Get any diagnostics from SQL + ARRAY(char) diagRecs; + for (int index = 1;; ++index) { + // Get next message + SqlState state; + SQLINTEGER native; + SQLCHAR msgStr[512]; + const bool indexValid = EnumDiagRec( + SQL_HANDLE_STMT, + stmt->hstmt, + index, + 0, + arrsize(msgStr), + msgStr, + &native, + &state + ); + + if (!indexValid) + break; + + diagRecs.Add((const char *)msgStr, StrLen((const char *) msgStr) + 1); + } + diagRecs.Add(L'\0'); + + // If the statement doesn't have a handler, handle it the default way + if (!stmt->errHandler) { + LogErr(result, SQL_HANDLE_STMT, stmt->hstmt, function, stmt->debug); + return; + } + + // Send the error to the statement's handler + stmt->errHandler( + result, + function, + stmt->debug, + diagRecs.Ptr(), + stmt->errHandlerParam + ); +} + + +/***************************************************************************** +* +* SqlStmtHash +* +***/ + +//============================================================================ +inline SqlStmtHash::SqlStmtHash () +: prepare(nil) +{} + +//============================================================================ +inline SqlStmtHash::SqlStmtHash (FSqlBindPrepare prepare) +: prepare(prepare) +{} + +//============================================================================ +inline dword SqlStmtHash::GetHash () const { + return (dword) prepare; +} + +//============================================================================ +inline bool SqlStmtHash::operator== (const SqlStmtHash & rhs) const { + return prepare == rhs.prepare; +} + + +/***************************************************************************** +* +* SqlStmt +* +***/ + +//============================================================================ +SqlStmt::SqlStmt (SqlConn * conn) +: conn(conn) +, hdbc(nil) +, hstmt(nil) +, flags(0) +, bindCol(0) +, bindParam(0) +, timeoutSecs(0) +, data(nil) +, errHandler(nil) +, errHandlerParam(nil) +{ + debug[0] = 0; + + if (conn) { + // This is a real statement, so add it to the statement count + ++conn->currentConns; + sequence = conn->sequence; + } + else { + // This statement is a list marker object + sequence = 0; + } +} + +//============================================================================ +SqlStmt::~SqlStmt () { + int result; + + Reset(); + + if (hstmt) { + result = SQLFreeHandle(SQL_HANDLE_STMT, hstmt); + LogStmtError(result, this, L"SQLFreeHandle"); + AtomicAdd(&s_perf[kSqlConnPerfConnCurrent], -1); + } + + if (flags & kStmtFlagDriverConnected) { + ASSERT(hdbc); + result = SQLDisconnect(hdbc); + LogErr(result, SQL_HANDLE_DBC, hdbc, L"SQLDisconnect", debug); + } + + if (hdbc) { + result = SQLFreeHandle(SQL_HANDLE_DBC, hdbc); + LogErr(result, SQL_HANDLE_DBC, hdbc, L"SQLFreeHandle", debug); + } + + if (conn) + --conn->currentConns; +} + +//============================================================================ +bool SqlStmt::Initialize (const wchar connectStr[]) { + ASSERT(!hdbc); + + for (;;) { + // Allocate connection handle + int result; + result = SQLAllocHandle( + SQL_HANDLE_DBC, + conn->henv, + &hdbc + ); + LogErr(result, SQL_HANDLE_ENV, conn->henv, L"SQLAllocHandle(dbc)", debug); + if (!SQL_SUCCEEDED(result)) + break; + + // Set login timeout + /* -- Crashes EasySoft Oracle driver + -- Not supported by Oracle's driver + result = SQLSetConnectAttr( + hdbc, + SQL_ATTR_LOGIN_TIMEOUT, + (SQLPOINTER) &kLoginTimeoutSecs, + SQL_IS_UINTEGER + ); + LogErr(result, SQL_HANDLE_DBC, hdbc, L"SQLSetConnectAttr(loginTimeout)", debug); + if (!SQL_SUCCEEDED(result)) + break; + */ + +#ifdef ODBC_TRACING + result = SQLSetConnectAttr( + hdbc, + SQL_ATTR_TRACEFILE, + "odbc.log", + SQL_NTS + ); + LogErr(result, SQL_HANDLE_DBC, hdbc, L"SQLSetConnectAttr(tracefile)", debug); + result = SQLSetConnectAttr( + hdbc, + SQL_ATTR_TRACE, + (SQLPOINTER) SQL_OPT_TRACE_ON, + SQL_IS_INTEGER + ); + LogErr(result, SQL_HANDLE_DBC, hdbc, L"SQLSetConnectAttr(trace)", debug); +#endif + + /* -- Crashes EasySoft Oracle driver + -- Not supported by Oracle's driver + result = SQLSetConnectAttr( + hdbc, + SQL_ATTR_CONNECTION_TIMEOUT, + (SQLPOINTER) &kConnectionTimeoutSecs, + SQL_IS_UINTEGER + ); + LogErr(result, SQL_HANDLE_DBC, hdbc, L"SQLSetConnectAttr(connectTimeout)", debug); + if (!SQL_SUCCEEDED(result)) + break; + */ + + // Perform driver connection + SQLWCHAR outConnectStr[1024]; + SQLSMALLINT outConnectLen; + result = SQLDriverConnectW( + hdbc, + (HWND) nil, + (SQLWCHAR *) connectStr, + SQL_NTS, + outConnectStr, + (SQLSMALLINT) arrsize(outConnectStr), + &outConnectLen, + SQL_DRIVER_NOPROMPT + ); + LogErr(result, SQL_HANDLE_DBC, hdbc, L"SQLDriverConnect()", debug); + if (!SQL_SUCCEEDED(result)) + break; + flags |= kStmtFlagDriverConnected; + + // Create statement for connection + result = SQLAllocHandle( + SQL_HANDLE_STMT, + hdbc, + &hstmt + ); + LogStmtError(result, this, L"SQLAllocHandle(stmt)"); + if (!SQL_SUCCEEDED(result)) { + ASSERT(!hstmt); + break; + } + + // Success! + AtomicAdd(&s_perf[kSqlConnPerfConnCurrent], 1); + return true; + } + + // Failure! + return false; +} + +//============================================================================ +void SqlStmt::Reset () { + flags &= ~kStmtFlagReset; + prepare = nil; + + SqlConnBindColReset(this); + SqlConnBindParameterReset(this); + + #ifdef ORACLE_FREE_DRIVER + { // Oracle's driver can't handle cached stmts, so blow it all away. + int result; + + // Destroy old statement + if (hstmt) { + result = SQLFreeHandle(SQL_HANDLE_STMT, hstmt); + LogStmtError(result, this, L"SQLFreeHandle"); + } + + // Create new statement + result = SQLAllocHandle( + SQL_HANDLE_STMT, + hdbc, + &hstmt + ); + LogStmtError(result, this, L"SQLAllocHandle(stmt)"); + if (!SQL_SUCCEEDED(result)) + ASSERT(!hstmt); + } + #endif // ORACLE_FREE_DRIVER + + FREE(data); + data = nil; + debug[0] = 0; +} + +//============================================================================ +bool SqlStmt::QueryDead (unsigned method) { + SQLUINTEGER dead; + int result = SQLGetConnectAttr( + hdbc, + method, + &dead, + SQL_IS_UINTEGER, + nil + ); + LogErr(result, SQL_HANDLE_DBC, hdbc, L"SQLGetConnectAttr(dead)", debug); + if (!SQL_SUCCEEDED(result)) + return false; + + return dead == SQL_CD_TRUE; +} + + +/***************************************************************************** +* +* StmtConn +* +***/ + +//============================================================================ +SqlConn::SqlConn ( + const wchar connectStr[], + SQLHENV henv +) : henv(henv) +, flags(0) +, desiredConns(0) +, currentConns(0) +, connectTasks(0) +, checkDeadTasks(0) +, sequence(0) +, timeoutSecs(0) +, connectStr(StrDup(connectStr)) +, taskList(AsyncThreadTaskListCreate()) +{} + +//=========================================================================== +SqlConn::~SqlConn () { + ASSERT(!freeStmts.Head()); + ASSERT(!stmts.Head()); + DEL(connectStr); + SQLFreeHandle(SQL_HANDLE_ENV, henv); + AsyncThreadTaskListDestroy(taskList, kNetErrRemoteShutdown); + AtomicAdd(&s_perf[kSqlConnPerfConnDesired], - (long) desiredConns); +} + + +/**************************************************************************** +* +* Connection management functions +* +***/ + +//============================================================================ +bool SqlConn::GrowConnections_CS (unsigned attempts) { + for (unsigned i = 0; i < attempts; ++i) { + // If the maximum number of connections has been reached then bail + if (currentConns >= desiredConns) + break; + + // If the server decided to shut down then bail + if (flags & kConnFlagInvalid) + break; + + // Leave the critical section to perform connection + SqlStmt * stmt = NEW(SqlStmt)(this); + + // Ensure that the connection string is valid + // for the duration of the initialization by + // creating a reference to the string. + critsect.Leave(); + bool result = stmt->Initialize(connectStr); + critsect.Enter(); + + // If the connection failed then bail + if (!result) { + DEL(stmt); + return false; + } + + // Add new connection to free list + freeStmts.Add(stmt); + stmts.Link(stmt); + } + + // Return true only if all connection attempts were successful + return true; +} + +//============================================================================ +static void SqlConnConnectCallback (void * param, ENetError error) { + SqlConn & conn = * (SqlConn *) param; + + AtomicAdd(&s_perf[kSqlConnPerfConnPending], 1); + conn.critsect.Enter(); + { + if (!error) + conn.GrowConnections_CS((unsigned) -1); + conn.connectTasks -= 1; + } + conn.critsect.Leave(); + AtomicAdd(&s_perf[kSqlConnPerfConnPending], -1); +} + +//============================================================================ +void SqlConn::AsyncGrowConnections_CS () { + // If the conn structure is being destroyed + // then it cannot initiate any new connections + if (flags & kConnFlagInvalid) + return; + + // How many additional tasks need to be started in + // order to reach the desired number of connections? + unsigned tasks = desiredConns - currentConns - connectTasks; + if ((int) tasks <= 0) + return; + + // Set upper bound on connection tasks + if (connectTasks + tasks > kConnectionTaskCount) + tasks = kConnectionTaskCount - connectTasks; + + // Initiate connection tasks + connectTasks += tasks; + for (unsigned i = 0; i < tasks; ++i) { + AsyncThreadTaskAdd( + taskList, + SqlConnConnectCallback, + this, + L"pnSqlConn.SqlConn::AsyncGrowConnections_CS" + ); + } +} + + +/**************************************************************************** +* +* CheckDead management functions +* +***/ + +//============================================================================ +void SqlConn::CheckDead_CS () { + // Use a list-position marker to allow this function to + // safely walk through the linked list even though it + // leaves the critical section. + SqlStmt marker(nil); + stmts.Link(&marker, kListHead); + while (SqlStmt * stmt = marker.listLink.Next()) { + // Move the marker to the next location + stmts.Link(&marker, kListLinkAfter, stmt); + + // The statement can't be used if either: + // - it is in use + // - it is a marker + if (!stmt->hashLink.IsLinked()) + continue; + + // If the statement has already been processed during + // this generation then do not process it again + if ((int) (sequence - stmt->sequence) <= 0) + continue; + + // Prevent this statement from being used during check + stmt->sequence = sequence; + stmt->hashLink.Unlink(); + + // Leave the critical section to check connection + critsect.Leave(); + bool dead = stmt->QueryDead(SQL_ATTR_CONNECTION_DEAD); + critsect.Enter(); + + // Deallocate the statement if it's bogus + if (dead) { + DEL(stmt); + AsyncGrowConnections_CS(); + } + else { + freeStmts.Add(stmt); + } + + // If the server decided to shut down then bail + if (flags & kConnFlagInvalid) + break; + } +} + +//============================================================================ +static void SqlConnCheckDeadCallback (void * param, ENetError error) { + SqlConn & conn = * (SqlConn *) param; + + AtomicAdd(&s_perf[kSqlConnPerfConnCheckDead], 1); + conn.critsect.Enter(); + { + if (!error) + conn.CheckDead_CS(); + conn.checkDeadTasks -= 1; + } + conn.critsect.Leave(); + AtomicAdd(&s_perf[kSqlConnPerfConnCheckDead], -1); +} + +//============================================================================ +void SqlConn::AsyncCheckDead_CS () { + // Don't start new checking tasks while there are others outstanding + if (checkDeadTasks) + return; + + // Start the next generation of check-dead tasks + ++sequence; + checkDeadTasks = kCheckDeadTaskCount; + for (unsigned i = 0; i < kCheckDeadTaskCount; ++i) { + AsyncThreadTaskAdd( + taskList, + SqlConnCheckDeadCallback, + this, + L"pnSqlConn.SqlConn::AsyncCheckDead_CS" + ); + } +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +SqlConn * SqlConnCreate ( + const wchar connectStr[], + unsigned maxConnections, + unsigned transTimeoutSeconds +) { + ASSERT(connectStr); + ASSERT(maxConnections); + + /* Connection pooling not needed because connections + are held for the entire duration of the program. + SQLSetEnvAttr( + nil, + SQL_ATTR_CONNECTION_POOLING, + (SQLPOINTER) SQL_CP_ONE_PER_DRIVER, + SQL_IS_INTEGER + ); + */ + + // Allocate environment + SQLHENV henv; + int result = SQLAllocHandle( + SQL_HANDLE_ENV, + SQL_NULL_HANDLE, + &henv + ); + LogErr(result, SQL_HANDLE_ENV, henv, L"SQLAllocHandle(env)", L""); + if (!SQL_SUCCEEDED(result)) + return nil; + + // Set ODBC version + result = SQLSetEnvAttr( + henv, + SQL_ATTR_ODBC_VERSION, + (SQLPOINTER) SQL_OV_ODBC3, + 0 + ); + LogErr(result, SQL_HANDLE_ENV, henv, L"SQLSetEnvAttr(version)", L""); + + // Allocate a connection record + SqlConn * conn = NEW(SqlConn)(connectStr, henv); + + // Update configuration options + conn->critsect.Enter(); + { + // Update timeout + conn->timeoutSecs = transTimeoutSeconds; + + // Update connection count + int delta = (int) (maxConnections - conn->desiredConns); + AtomicAdd(&s_perf[kSqlConnPerfConnDesired], delta); + conn->desiredConns += delta; + + // Create more connections + conn->AsyncGrowConnections_CS(); + } + conn->critsect.Leave(); + + return conn; +} + +//============================================================================ +void SqlConnShutdown (SqlConn * conn) { + conn->critsect.Enter(); + { + conn->flags |= kConnFlagInvalid; + SqlStmt * stmt = conn->stmts.Head(); + for (; stmt; stmt = conn->stmts.Next(stmt)) + SQLCancel(stmt->hstmt); + } + conn->critsect.Leave(); +} + +//============================================================================ +void SqlConnDestroy (SqlConn * conn) { + for (;; AsyncSleep(1)) { + + bool wait; + conn->critsect.Enter(); + { + // Signal shutdown + conn->flags |= kConnFlagInvalid; + + // Delete free and disconnected statements + conn->freeStmts.Clear(); + + // Wait until tasks are complete and statements have been destroyed + wait = conn->currentConns || conn->connectTasks || conn->checkDeadTasks; + } + conn->critsect.Leave(); + if (wait) + continue; + + // Complete! + break; + } + + DEL(conn); +} + +//============================================================================ +void SqlConnFreeStmt (SqlStmt * stmt) { + ASSERT(stmt); + ASSERT(stmt->listLink.IsLinked()); + ASSERT(!stmt->hashLink.IsLinked()); + + SqlConnCloseCursor(stmt); + + #ifdef ORACLE_FREE_DRIVER + { // Oracle's driver can't handle cached stmts, so blow it all away. + stmt->flags |= kStmtFlagReset; + } + #endif // ORACLE_FREE_DRIVER + + // If this statement was used for an ad hoc query + // then it can't be cached, so reset the bindings + if (!stmt->prepare || (stmt->flags & kStmtFlagReset)) + stmt->Reset(); + + bool dead = (stmt->flags & kStmtFlagDelete) || stmt->QueryDead(SQL_ATTR_CONNECTION_DEAD); + SqlConn * conn = stmt->conn; + conn->critsect.Enter(); + { + if (stmt->flags & kStmtFlagDelete) { + DEL(stmt); + conn->AsyncGrowConnections_CS(); + } + else if (dead) { + DEL(stmt); + conn->AsyncCheckDead_CS(); + } + else { + conn->freeStmts.Add(stmt); + } + } + conn->critsect.Leave(); +} + +//============================================================================ +void SqlStmtSetAttr ( + SqlStmt * stmt, + int attr, + void * valPtr, + int strLen +) { + ASSERT(stmt); + ASSERT(!stmt->prepare); + int result = SQLSetStmtAttr( + stmt->hstmt, + (SQLINTEGER)attr, + (SQLPOINTER)valPtr, + (SQLINTEGER)strLen + ); + LogErr(result, SQL_HANDLE_STMT, stmt->hstmt, L"SQLSetStmtAttr", nil); +} + + +//============================================================================ +SqlStmt * SqlConnAllocStmt (SqlConn * conn) { + return SqlConnAllocStmt(conn, nil, nil); +} + +//============================================================================ +SqlStmt * SqlConnAllocStmt ( + SqlConn * conn, + FSqlBindPrepare prepare, + byte ** bindData // [OUT] +) { + ASSERT(prepare || !bindData); + if (!conn) + return nil; + + SqlStmt * stmt; + FSqlBindPrepare reset = nil; + FSqlBindPrepare cached = prepare; + unsigned timeoutSeconds = 0; + conn->critsect.Enter(); + { + // Get the transaction timeout value + timeoutSeconds = conn->timeoutSecs; + + for (;;) { + // Is the server permanently dead? + if (conn->flags & kConnFlagInvalid) { + prepare = nil; + stmt = nil; + break; + } + + // If there is already a cached statement that has + // been prepared with the same data then recycle it + if (prepare && (nil != (stmt = conn->freeStmts.Find(SqlStmtHash(prepare))))) { + *bindData = stmt->data; + prepare = nil; + conn->freeStmts.Unlink(stmt); + break; + } + + // If there is a statement that hasn't been prepared then use it now + if (nil != (stmt = conn->freeStmts.Find(SqlStmtHash(nil)))) { + ASSERT(!stmt->data); + conn->freeStmts.Unlink(stmt); + break; + } + + // Take the least recently used item from the table and reinitialize + if (nil != (stmt = conn->freeStmts.Head())) { + ASSERT(stmt->prepare); + ASSERT(stmt->data); + reset = stmt->prepare; + conn->freeStmts.Unlink(stmt); + break; + } + + if (conn->desiredConns == conn->currentConns) { + // Since there aren't enough statements on the free list, + // allocate new connections to the server. Since this is + // a very time-consuming operation, and it means that there + // isn't a surplus of connections for caching (more connections + // than threads, so that statements can be cached), this is a + // very serious error. + LogMsg(kLogError, "SqlConn: no free statements"); + AtomicAdd(&s_perf[kSqlConnPerfConnDesired], 50); + conn->desiredConns += 50; + } + + // Don't allow too many threads to be performing connection tasks + // so that this module doesn't starve the rest of the system + bool result = conn->connectTasks < kConnectionTaskCount; + if (result) { + // There is a crash bug where when the first connection to the + // server is allocated and then immediately used, but it occurs + // on the second use of the statement. I have no idea why this + // bug occurs. + ++conn->connectTasks; + conn->AsyncGrowConnections_CS(); + result = conn->GrowConnections_CS(5); + --conn->connectTasks; + } + + // Did the connection attempt fail? + if (!result) { + prepare = nil; + stmt = nil; + break; + } + } + + // If at any time there aren't connections then create more of them! + if (stmt && (conn->desiredConns != conn->currentConns)) + conn->AsyncGrowConnections_CS(); + } + conn->critsect.Leave(); + + // If no statement could be allocated then bail + if (!stmt) + return nil; + + // Ensure that statement was removed from the free list + ASSERT(!stmt->hashLink.IsLinked()); + + if (reset) { + stmt->Reset(); + } + + if (stmt->timeoutSecs != timeoutSeconds) { + stmt->timeoutSecs = timeoutSeconds; + int result = SQLSetConnectAttr( + stmt->hdbc, + SQL_ATTR_QUERY_TIMEOUT, + (SQLPOINTER) timeoutSeconds, + SQL_IS_UINTEGER + ); + LogErr(result, SQL_HANDLE_DBC, stmt->hdbc, L"SQLSetConnectAttr(queryTimeout)", stmt->debug); + } + + if (prepare) { + ARRAY(byte) data; + prepare(stmt, &data); + stmt->prepare = prepare; + stmt->data = data.Detach(); + *bindData = stmt->data; + AtomicAdd(&s_perf[kSqlConnPerfCacheMisses], 1); + } + else if (cached) { + AtomicAdd(&s_perf[kSqlConnPerfCacheHits], 1); + } + + return stmt; +} + +//============================================================================ +void SqlConnSetErrorHandler ( + SqlStmt * stmt, + FSqlConnErrorHandler handler, + void * userParam +) { + stmt->errHandler = handler; + stmt->errHandlerParam = userParam; +} + +//============================================================================ +void SqlConnBindColReset (SqlStmt * stmt) { + ASSERT(!stmt->prepare); + + if (stmt->bindCol) { + SQLFreeStmt(stmt->hstmt, SQL_UNBIND); + stmt->bindCol = 0; + } + + if (stmt->flags & kStmtFlagBindColMode) { + SqlConnBindColMode( + stmt, + nil, // rowCountPtr + 0, // structSize + 1, // arraySize + nil // statusArrayPtr + ); + stmt->flags &= ~kStmtFlagBindColMode; + } +} + +//============================================================================ +void SqlConnBindColMode ( + SqlStmt * stmt, + SQLUINTEGER * rowCount, // SQL_ATTR_ROWS_FETCHED_PTR + SQLUINTEGER structSize, // SQL_ATTR_ROW_BIND_TYPE + SQLUINTEGER arraySize, // SQL_ATTR_ROW_ARRAY_SIZE + SQLUSMALLINT * statusArray // SQL_ATTR_ROW_STATUS_PTR +) { + ASSERT(!stmt->prepare); + + // Bind mode set to non-default value, so it + // must be reset before statement is re-used + stmt->flags |= kStmtFlagBindColMode; + + // Set rowCount + int result = SQLSetStmtAttr(stmt->hstmt, SQL_ATTR_ROWS_FETCHED_PTR, rowCount, 0); + LogStmtError(result, stmt, L"SQLSetStmtAttr"); + + // Set row/col binding + result = SQLSetStmtAttr(stmt->hstmt, SQL_ATTR_ROW_BIND_TYPE, (SQLPOINTER) structSize, 0); + LogStmtError(result, stmt, L"SQLSetStmtAttr"); + + // Set number of elements in parameter arrays + result = SQLSetStmtAttr(stmt->hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) arraySize, 0); + LogStmtError(result, stmt, L"SQLSetStmtAttr"); + + // Set status array + result = SQLSetStmtAttr(stmt->hstmt, SQL_ATTR_ROW_STATUS_PTR, statusArray, 0); + LogStmtError(result, stmt, L"SQLSetStmtAttr"); +} + +//============================================================================ +void SqlConnBindCol ( + SqlStmt * stmt, + SQLINTEGER targetType, + SQLPOINTER targetValue, + SQLINTEGER bufferLength, + SQLINTEGER * indPtr +) { + ASSERT(!stmt->prepare); + + int result = SQLBindCol( + stmt->hstmt, + (SQLUSMALLINT) ++stmt->bindCol, + (SQLSMALLINT) targetType, + targetValue, + bufferLength, + indPtr + ); + LogStmtError(result, stmt, L"SQLBindCol"); +} + +//============================================================================ +void SqlConnBindParameterReset (SqlStmt * stmt) { + ASSERT(!stmt->prepare); + + if (stmt->bindParam) { + SQLFreeStmt(stmt->hstmt, SQL_RESET_PARAMS); + stmt->bindParam = 0; + } + + if (stmt->flags & kStmtFlagBindParamMode) { + SqlConnBindParameterMode( + stmt, + nil, // processCountPtr + 0, // structSize + 1, // arraySize + nil // statusArrayPtr + ); + stmt->flags &= ~kStmtFlagBindParamMode; + } + +} + +//============================================================================ +void SqlConnBindParameterMode ( + SqlStmt * stmt, + SQLUINTEGER * processCount, // SQL_ATTR_PARAMS_PROCESSED_PTR + SQLUINTEGER structSize, // SQL_ATTR_PARAM_BIND_TYPE + SQLUINTEGER arraySize, // SQL_ATTR_PARAMSET_SIZE + SQLUSMALLINT * statusArray // SQL_ATTR_PARAM_STATUS_PTR +) { + ASSERT(!stmt->prepare); + + // Bind mode set to non-default value, so it + // must be reset before statement is re-used + stmt->flags |= kStmtFlagBindParamMode; + + // Set processCount + int result = SQLSetStmtAttr(stmt->hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, processCount, 0); + LogStmtError(result, stmt, L"SQLSetStmtAttr"); + + // Set row/col binding + result = SQLSetStmtAttr(stmt->hstmt, SQL_ATTR_PARAM_BIND_TYPE, (SQLPOINTER) structSize, 0); + LogStmtError(result, stmt, L"SQLSetStmtAttr"); + + // Set number of elements in parameter arrays + result = SQLSetStmtAttr(stmt->hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) arraySize, 0); + LogStmtError(result, stmt, L"SQLSetStmtAttr"); + + // Set status array + result = SQLSetStmtAttr(stmt->hstmt, SQL_ATTR_PARAM_STATUS_PTR, statusArray, 0); + LogStmtError(result, stmt, L"SQLSetStmtAttr"); +} + +//============================================================================ +void SqlConnBindParameter ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLINTEGER valueType, + SQLINTEGER parameterType, + SQLUINTEGER columnSize, + SQLINTEGER decimalDigits, + SQLPOINTER parameterValuePtr, + SQLINTEGER bufferLength, + SQLINTEGER * indPtr +) { + ASSERT(!stmt->prepare); + int result = SQLBindParameter( + stmt->hstmt, + (SQLUSMALLINT) ++stmt->bindParam, + (SQLSMALLINT) inputOutputType, + (SQLSMALLINT) valueType, + (SQLSMALLINT) parameterType, + columnSize, + (SQLSMALLINT) decimalDigits, + parameterValuePtr, + bufferLength, + indPtr + ); + LogStmtError(result, stmt, L"SQLBindParameter"); +} + +//============================================================================ +void SqlConnBindParameterInt ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLINTEGER * parameterValuePtr, + SQLINTEGER * indPtr +) { + SqlConnBindParameter( + stmt, + inputOutputType, + SQL_C_LONG, + SQL_INTEGER, + 0, + 0, + parameterValuePtr, + 0, + indPtr + ); +} + +//============================================================================ +void SqlConnBindParameterUInt ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLUINTEGER * parameterValuePtr, + SQLINTEGER * indPtr +) { + SqlConnBindParameter( + stmt, + inputOutputType, + SQL_C_ULONG, + SQL_INTEGER, + 0, + 0, + parameterValuePtr, + 0, + indPtr + ); +} + +//============================================================================ +void SqlConnBindParameterString ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLUINTEGER columnSize, // bytes, NOT chars + SQLWCHAR * parameterValuePtr, + SQLINTEGER * indPtr +) { + SqlConnBindParameter( + stmt, + inputOutputType, + SQL_C_WCHAR, + SQL_WVARCHAR, + columnSize, + 0, + parameterValuePtr, + inputOutputType == SQL_PARAM_OUTPUT ? columnSize : 0, + indPtr + ); +} + +//============================================================================ +void SqlConnBindParameterStringA ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLUINTEGER columnSize, // bytes, NOT chars + SQLCHAR * parameterValuePtr, + SQLINTEGER * indPtr +) { + SqlConnBindParameter( + stmt, + inputOutputType, + SQL_C_CHAR, + SQL_VARCHAR, + columnSize, + 0, + parameterValuePtr, + inputOutputType == SQL_PARAM_OUTPUT ? columnSize : 0, + indPtr + ); +} + +//============================================================================ +int SqlConnExecDirect ( + SqlStmt * stmt, + const wchar string[] +) { + #ifdef LOG_SQL_STMTS + LogMsg(kLogDebug, L"SQL: %s", string); + #endif + StrCopy(stmt->debug, string, arrsize(stmt->debug)); + int result = SQLExecDirectW(stmt->hstmt, const_cast(string), SQL_NTS); + LogStmtError(result, stmt, L"SqlExecute"); + return result; +} + +//============================================================================ +int SqlConnPrepare ( + SqlStmt * stmt, + const wchar string[] +) { + // Using {call ...} or {?= call ...} with SqlPrepare doesn't allow + // the ODBC expression parser to perform escape sequence fixup for + // return value and output parameters; to call stored procedures, + // use SqlConnExecDirect instead + #ifdef HS_DEBUGGING + ASSERT(string[0] != L'{'); + #endif + + StrCopy(stmt->debug, string, arrsize(stmt->debug)); + int result = SQLPrepareW(stmt->hstmt, const_cast(string), SQL_NTS); + LogStmtError(result, stmt, L"SqlPrepare"); + return result; +} + +//============================================================================ +int SqlConnExecute (SqlStmt * stmt) { + int result = SQLExecute(stmt->hstmt); + LogStmtError(result, stmt, L"SQLExecute"); + return result; +} + +//============================================================================ +int SqlConnFetch (SqlStmt * stmt) { + int result = SQLFetch(stmt->hstmt); + LogStmtError(result, stmt, L"SQLFetch"); + return result; +} + +//============================================================================ +int SqlConnMoreResults (SqlStmt * stmt) { + int result = SQLMoreResults(stmt->hstmt); + LogStmtError(result, stmt, L"SQLMoreResults"); + return result; +} + +//============================================================================ +int SqlConnParamData ( + SqlStmt * stmt, + SQLPOINTER * put +) { + int result = SQLParamData(stmt->hstmt, put); + LogStmtError(result, stmt, L"SQLParamData"); + return result; +} + +//============================================================================ +bool SqlConnPutData ( + SqlStmt * stmt, + SQLPOINTER data, + SQLINTEGER bytes +) { + int result = SQLPutData(stmt->hstmt, data, bytes); + LogStmtError(result, stmt, L"SQLPutData"); + return SQL_SUCCEEDED(result); +} + +//============================================================================ +int SqlConnGetData ( + SqlStmt * stmt, + SQLINTEGER columnNumber, + SQLINTEGER targetType, + SQLPOINTER targetValue, + SQLINTEGER bufferLength, + SQLINTEGER * indPtr +) { + int result = SQLGetData( + stmt->hstmt, + (SQLUSMALLINT) columnNumber, + (SQLSMALLINT) targetType, + targetValue, + bufferLength, + indPtr + ); + LogStmtError(result, stmt, L"SQLGetData"); + return result; +} + +//============================================================================ +void SqlConnCloseCursor (SqlStmt * stmt) { + int result = SQLFreeStmt(stmt->hstmt, SQL_CLOSE); + LogStmtError(result, stmt, L"SQLFreeStmt"); +} + +//============================================================================ +int SqlConnNumResultCols ( + SqlStmt * stmt +) { + // @@@ TODO: Ensure that statement has been executed + SQLSMALLINT colCount = 0; + int result = SQLNumResultCols( + stmt->hstmt, + &colCount + ); + LogStmtError(result, stmt, L"SQLNumResultCols"); + + return colCount; +} + +//============================================================================ +int SqlConnDescribeCol ( + SqlStmt * stmt, + SQLSMALLINT columnNumber, + SQLWCHAR * columnName, + SQLSMALLINT bufferLength, + SQLSMALLINT * nameLengthPtr, + SQLSMALLINT * dataTypePtr, + SQLUINTEGER * columnSizePtr, + SQLSMALLINT * decimalDigitsPtr, + SQLSMALLINT * nullablePtr +) { + + int result = SQLDescribeColW( + stmt->hstmt, + columnNumber, + columnName, + bufferLength, + nameLengthPtr, + dataTypePtr, + columnSizePtr, + decimalDigitsPtr, + nullablePtr + ); + LogStmtError(result, stmt, L"SQLDescribeCol"); + return result; + +} + +//============================================================================ +unsigned SqlConnGetPerf (unsigned id) { + ASSERT(id < arrsize(s_perf)); + return (unsigned) s_perf[id]; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlConn.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlConn.h new file mode 100644 index 00000000..b2a44c26 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlConn.h @@ -0,0 +1,292 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlConn.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_PRIVATE_PNSQLCONN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlConn.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_PRIVATE_PNSQLCONN_H + + +/***************************************************************************** +* +* Types +* +***/ + +struct SqlConn; +struct SqlStmt; +struct SqlTrans; + +typedef void (* FSqlBindPrepare)( + SqlStmt * stmt, + ARRAY(byte) * bindData +); + + +/***************************************************************************** +* +* Init API +* +***/ + +SqlConn * SqlConnCreate ( + const wchar connectStr[], + unsigned maxConnections, + unsigned transTimeoutMs +); +void SqlConnShutdown (SqlConn * conn); +void SqlConnDestroy (SqlConn * conn); + + +/***************************************************************************** +* +* Ad-hoc query API +* +* For queries that will be performed and therefore don't want to incur +* binding and preparing overhead. +* +***/ + +SqlStmt * SqlConnAllocStmt ( + SqlConn * conn +); +void SqlConnFreeStmt ( + SqlStmt * stmt +); +void SqlStmtSetAttr ( + SqlStmt * stmt, + int attr, + void * valPtr, + int strLen +); + + +/***************************************************************************** +* +* "Optimized" query API +* +* The "prepare" function will only be called once for a statement, and it +* will retain its bindings and memory set by the prepare function until +* SqlConnDisconnect is called (i.e. until program shutdown). +* +* +* +***/ + +SqlStmt * SqlConnAllocStmt ( + SqlConn * conn, + FSqlBindPrepare prepare, + byte ** bindData // [OUT] +); + + +/***************************************************************************** +* +* Error handling +* +***/ + +typedef void (* FSqlConnErrorHandler)( + int result, + const wchar function[], + const wchar command[], + const char diagnostics[], // double-null terminated + void * userParam +); + +void SqlConnSetErrorHandler ( + SqlStmt * stmt, + FSqlConnErrorHandler handler, + void * userParam +); + + +/***************************************************************************** +* +* Output binding +* +***/ + +// Reset all changes done by SqlConnBindCol*() functions +void SqlConnBindColReset (SqlStmt * stmt); + +// Obtain multiple rows in a result set by rows or columns +void SqlConnBindColMode ( + SqlStmt * stmt, + SQLUINTEGER * rowCount, // SQL_ATTR_ROWS_FETCHED_PTR + SQLUINTEGER structSize, // SQL_ATTR_ROW_BIND_TYPE + SQLUINTEGER arraySize, // SQL_ATTR_ROW_ARRAY_SIZE + SQLUSMALLINT * statusArray // SQL_ATTR_ROW_STATUS_PTR +); + +// Bind parameters of various types +void SqlConnBindCol ( + SqlStmt * stmt, + SQLINTEGER targetType, + SQLPOINTER targetValue, + SQLINTEGER bufferLength, + SQLINTEGER * indPtr +); + + +/***************************************************************************** +* +* Parameter binding +* +***/ + +// Reset all changes done by SqlConnBindParameter*() functions +void SqlConnBindParameterReset (SqlStmt * stmt); + +// Set multiple rows in an output set by rows or columns +void SqlConnBindParameterMode ( + SqlStmt * stmt, + SQLUINTEGER * processCount, // SQL_ATTR_PARAMS_PROCESSED_PTR + SQLUINTEGER structSize, // SQL_ATTR_PARAM_BIND_TYPE + SQLUINTEGER arraySize, // SQL_ATTR_PARAMSET_SIZE + SQLUSMALLINT * statusArray // SQL_ATTR_PARAM_STATUS_PTR +); + +// Bind parameters of various types +void SqlConnBindParameter ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLINTEGER valueType, + SQLINTEGER parameterType, + SQLUINTEGER columnSize, + SQLINTEGER decimalDigits, + SQLPOINTER parameterValuePtr, + SQLINTEGER bufferLength, + SQLINTEGER * indPtr +); +void SqlConnBindParameterInt ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLINTEGER * parameterValuePtr, + SQLINTEGER * indPtr +); +void SqlConnBindParameterUInt ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLUINTEGER * parameterValuePtr, + SQLINTEGER * indPtr +); +void SqlConnBindParameterString ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLUINTEGER columnSize, // bytes, NOT chars + SQLWCHAR * parameterValuePtr, + SQLINTEGER * indPtr +); +void SqlConnBindParameterStringA ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLUINTEGER columnSize, // bytes, NOT chars + SQLCHAR * parameterValuePtr, + SQLINTEGER * indPtr +); + +/**************************************************************************** +* +* Execution +* +***/ + +int SqlConnExecDirect ( + SqlStmt * stmt, + const wchar string[] +); +int SqlConnPrepare ( + SqlStmt * stmt, + const wchar string[] +); +int SqlConnExecute ( + SqlStmt * stmt +); +int SqlConnFetch ( + SqlStmt * stmt +); +int SqlConnMoreResults ( + SqlStmt * stmt +); +int SqlConnParamData ( + SqlStmt * stmt, + SQLPOINTER * put +); +bool SqlConnPutData ( + SqlStmt * stmt, + SQLPOINTER data, + SQLINTEGER bytes +); +int SqlConnGetData ( + SqlStmt * stmt, + SQLINTEGER columnNumber, + SQLINTEGER targetType, + SQLPOINTER targetValue, + SQLINTEGER bufferLength, + SQLINTEGER * indPtr +); +void SqlConnCloseCursor ( + SqlStmt * stmt +); +int SqlConnNumResultCols ( + SqlStmt * stmt +); +int SqlConnDescribeCol ( + SqlStmt * stmt, + SQLSMALLINT columnNumber, + SQLWCHAR * columnName, + SQLSMALLINT bufferLength, + SQLSMALLINT * nameLengthPtr, + SQLSMALLINT * dataTypePtr, + SQLUINTEGER * columnSizePtr, + SQLSMALLINT * decimalDigitsPtr, + SQLSMALLINT * nullablePtr +); + + +/***************************************************************************** +* +* Performance API +* +***/ + +enum { + kSqlConnPerfConnDesired, + kSqlConnPerfConnCurrent, + kSqlConnPerfConnPending, + kSqlConnPerfConnCheckDead, + kSqlConnPerfCacheHits, + kSqlConnPerfCacheMisses, + kSqlConnNumPerf +}; + +unsigned SqlConnGetPerf (unsigned id); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlUtil.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlUtil.cpp new file mode 100644 index 00000000..4425b8a0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlUtil.cpp @@ -0,0 +1,180 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlUtil.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +void SqlConnBindParameterInputGuid ( + SqlStmt * stmt, + Uuid * uuid, + SQLINTEGER * uuidLen +) { + SqlConnBindParameter( + stmt, + SQL_PARAM_INPUT, + SQL_C_BINARY, + SQL_BINARY, + sizeof(Uuid), + 0, + uuid, + sizeof(Uuid), + uuidLen + ); +} + +//=========================================================================== +void SqlConnBindParameterOutputGuid ( + SqlStmt * stmt, + Uuid * uuid, + SQLINTEGER * uuidLen +) { + SqlConnBindParameter( + stmt, + SQL_PARAM_OUTPUT, + SQL_C_BINARY, + SQL_BINARY, + sizeof(Uuid), + 0, + uuid, + sizeof(Uuid), + uuidLen + ); +} + +//=========================================================================== +void SqlConnBindParameterBigInt ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLBIGINT * parameterValuePtr, + SQLINTEGER * indPtr +) { + SqlConnBindParameter( + stmt, + inputOutputType, + SQL_C_SBIGINT, + SQL_BIGINT, + sizeof(qword), + 0, + parameterValuePtr, + 0, + indPtr + ); +} + +//=========================================================================== +bool SqlConnGetBlobData ( + SqlStmt * stmt, + unsigned colIndex, + ARRAY(byte) * buffer, + unsigned * bytesAdded +) { + // Since a number of routines use this function, set the chunk size to be + // fairly large to avoid the necessity of reallocing the block, and further + // to prevent memory fragmentation by allocating variously sized blocks. + // In for a penny, in for a pound... + const unsigned CHUNK_SIZE = 32 * 1024; + SQLINTEGER length = CHUNK_SIZE - 1; + unsigned bytes = buffer->Bytes(); + *bytesAdded = 0 - bytes; + for (;;) { + // Allocate memory that is evenly aligned to CHUNK_SIZE, which + // reduces memory fragmentation by allocating on 32K multiples. + // Use SetCount() instead of New() to ensure that this is true + // even if the buffer already contains data. + buffer->SetCount( + MathNextMultiplePow2( + buffer->Bytes() + length, + CHUNK_SIZE + ) + ); + + // Read column data + int result = SqlConnGetData( + stmt, + colIndex, + SQL_C_BINARY, + buffer->Ptr() + bytes, + buffer->Bytes() - bytes, + &length + ); + + // Handle SQL_NO_TOTAL and SQL_NULL_DATA values + if (length < 0) { + buffer->ZeroCount(); + *bytesAdded = 0; + return result == SQL_SUCCESS; + } + + // Keep getting data until we have the entire buffer + if (result == SQL_SUCCESS_WITH_INFO) { + // length contains the number of remaining bytes + bytes = buffer->Bytes(); + continue; + } + + // Is the buffer complete? + if (result == SQL_SUCCESS) { + buffer->SetCountFewer(length + bytes); + *bytesAdded += buffer->Bytes(); + return true; + } + + // An error occurred + break; + } + + buffer->ZeroCount(); + *bytesAdded = 0; + return false; +} + +//=========================================================================== +int SqlConnPutBlobData ( + SqlStmt * stmt, + unsigned bytes, + const byte data[] +) { + int result; + SQLPOINTER putPtr; + while (SQL_NEED_DATA == (result = SqlConnParamData(stmt, &putPtr))) { + if (!SqlConnPutData(stmt, const_cast(data), bytes)) + return SQL_ERROR; + } + return result; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlUtil.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlUtil.h new file mode 100644 index 00000000..00fcc58a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlUtil.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlUtil.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_PRIVATE_PNSQLUTIL_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/Private/pnSqlUtil.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_PRIVATE_PNSQLUTIL_H + + +/***************************************************************************** +* +* SqlUtil API +* +***/ + +void SqlConnBindParameterInputGuid ( + SqlStmt * stmt, + Uuid * uuid, + SQLINTEGER * uuidLen +); +void SqlConnBindParameterOutputGuid ( + SqlStmt * stmt, + Uuid * uuid, + SQLINTEGER * uuidLen +); +void SqlConnBindParameterBigInt ( + SqlStmt * stmt, + SQLINTEGER inputOutputType, + SQLBIGINT * parameterValuePtr, + SQLINTEGER * indPtr +); +bool SqlConnGetBlobData ( + SqlStmt * stmt, + unsigned colIndex, + ARRAY(byte) * buffer, + unsigned * bytesAdded +); +int SqlConnPutBlobData ( + SqlStmt * stmt, + unsigned bytes, + const byte data[] +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/pnSqlLib.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/pnSqlLib.h new file mode 100644 index 00000000..b0927333 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/pnSqlLib.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnSqlLib/pnSqlLib.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_PNSQLLIB_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_PNSQLLIB_H + + +// Include ODBC API +#include +#include + +#include "Private/pnSqlAllIncludes.h" + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNSQLLIB_PNSQLLIB_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/hsTimer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/hsTimer.cpp new file mode 100644 index 00000000..debca396 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/hsTimer.cpp @@ -0,0 +1,405 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTimer.h" +#include "hsUtils.h" + +#if HS_BUILD_FOR_MAC +#include +#endif + +#include "plTweak.h" + +// +// plTimerShare - the actual worker. All process spaces should share a single +// plTimerShare to keep time synchronized across spaces. +// +plTimerShare::plTimerShare() +: fFirstTime(true), + fSysSeconds(0), + fRealSeconds(0), + fDelSysSeconds(0), + fFrameTimeInc(0.03f), + fSysTimeScale(1.f), + fTimeClampSecs(0.1f), + fSmoothingClampSecs(-1.0f), + fRunningFrameTime(false), + fClamping(false), + fResetSmooth(true), + fCurrSlot(0) +{ +} + +plTimerShare::~plTimerShare() +{ +} + +double plTimerShare::GetSeconds() const +{ + hsWide ticks; + return hsTimer::GetRawTicks(&ticks)->AsDouble() / hsTimer::GetRawBase().AsDouble(); +} + +double plTimerShare::GetMilliSeconds() const +{ + return GetSeconds() * 1.e3; +} + +hsWide plTimerShare::DSecondsToRawTicks(double secs) +{ + hsWide retVal; + double ticks = secs * hsTimer::GetRawBase().AsDouble(); + double hi = ticks / double(65536) / double(65536); + ticks -= hi; + retVal.fHi = Int32(hi); + retVal.fLo = Int32(ticks); + return retVal; +} + +double plTimerShare::RawTicksToDSeconds(const hsWide& ticks) +{ + return ticks.AsDouble() / hsTimer::GetRawBase().AsDouble(); +} + + +inline hsWide* plTimerShare::FactorInTimeZero(hsWide* ticks) const +{ + if( fFirstTime ) + { + fFirstTime = false; + fRawTimeZero = *ticks; + ticks->Set(0, 0); + } + else + { + ticks->Sub(&fRawTimeZero); + } + return ticks; +} + +double plTimerShare::IncSysSeconds() +{ + if( fRunningFrameTime ) + { + fDelSysSeconds = fFrameTimeInc * fSysTimeScale; + fSysSeconds += fDelSysSeconds; + + fResetSmooth = true; + } + else if( fSmoothingClampSecs >= 0 ) + { + double t = GetSeconds(); + hsScalar delSys = hsScalar(t - fRealSeconds); + fClamping = ( (fTimeClampSecs > 0) && (delSys > fTimeClampSecs) ); + if (fClamping) + { + delSys = fTimeClampSecs; + } + delSys *= fSysTimeScale; + if( fDelSysSeconds > 0 && fDelSysSeconds < fSmoothingClampSecs ) + { + const hsScalar kFrac = 0.1f; + const hsScalar kOneMinusFrac = 1.f-kFrac; + delSys *= kFrac; + delSys += fDelSysSeconds * kOneMinusFrac; + } + if (delSys > 4.0f && delSys < 5.0f) + { + //got that mysterious bug, (Win2k? certain CPU's?) try again... +#if HS_BUILD_FOR_WIN32 + int count = 10; + while( delSys >= fDelSysSeconds * 2 && count > 0 ) + { + fRealSeconds = t; + t = GetSeconds(); + delSys = hsScalar(t - fRealSeconds); + count--; + } +#endif + } + fDelSysSeconds = delSys; + fSysSeconds += fDelSysSeconds; + fRealSeconds = t; + + fResetSmooth = true; + } + else + { + double t = GetSeconds(); + plCONST(int) kSmoothBuffUsed(kSmoothBuffLen); + + if( fResetSmooth ) + { + int i; + for( i = 0; i < kSmoothBuffUsed; i++ ) + fSmoothBuff[i] = t; + fResetSmooth = false; + } + + if( ++fCurrSlot >= kSmoothBuffUsed ) + fCurrSlot = 0; + fSmoothBuff[fCurrSlot] = t; + + double avg = 0; + int j; + for( j = 0; j < kSmoothBuffUsed; j++ ) + { + avg += fSmoothBuff[j]; + } + avg /= double(kSmoothBuffUsed); + + plCONST(hsScalar) kMaxSmoothable(0.15f); + fDelSysSeconds = hsScalar(avg - fRealSeconds) * fSysTimeScale; + if( fDelSysSeconds > kMaxSmoothable * fSysTimeScale ) + { + avg = t; + fDelSysSeconds = hsScalar(avg - fRealSeconds) * fSysTimeScale; + fResetSmooth = true; + } + fSysSeconds += fDelSysSeconds; + fRealSeconds = avg; + } + return fSysSeconds; +} + +void plTimerShare::SetRealTime(hsBool realTime) +{ + fRunningFrameTime = !realTime; + if( realTime ) + { + fRealSeconds = GetSeconds(); + } +} + +#if HS_BUILD_FOR_WIN32 + +#include + +hsWide* plTimerShare::GetRawTicks(hsWide* ticks) const +{ + LARGE_INTEGER large; + + if (::QueryPerformanceCounter(&large)) + { + ticks->Set(large.HighPart, large.LowPart); + } + else + { + ticks->Set(0, ::GetTickCount()); + } + + return FactorInTimeZero(ticks); +} + +hsWide hsTimer::IInitRawBase() +{ + hsWide base; + LARGE_INTEGER large; + if (::QueryPerformanceFrequency(&large)) + base.Set(large.HighPart, large.LowPart); + else + base.Set(0, 1000); + + return base; +} + +#elif HS_BUILD_FOR_MAC + +#include +#include + +//#define HS_USE_TICKCOUNT +hsWide* plTimerShare::GetRawTicks(hsWide* ticks) +{ +#ifndef HS_USE_TICKCOUNT + UnsignedWide ns = AbsoluteToNanoseconds(UpTime()); + ticks->Set(ns.hi, ns.lo); +#else + ticks->Set(0, TickCount()); +#endif + return FactorInTimeZero(ticks); +} + +hsWide plTimerShare::IInitRawBase() +{ + hsWide base; +#ifndef HS_USE_TICKCOUNT + base.Set(0, 1000000000L); +#else + base.Set(0, 60); +#endif + return base; +} + +#elif HS_BUILD_FOR_UNIX + +#include + +#define kMicroSecondsUnit 1000000 +static UInt32 gBaseTime = 0; + +hsWide* plTimerShare::GetRawTicks(hsWide* ticks) const +{ + timeval tv; + + (void)::gettimeofday(&tv, nil); + if (gBaseTime == 0) + gBaseTime = tv.tv_sec; + + ticks->Mul(tv.tv_sec - gBaseTime, kMicroSecondsUnit)->Add(tv.tv_usec); + return ticks; +} + +hsWide hsTimer::IInitRawBase() +{ + hsWide base; + base.Set(0, kMicroSecondsUnit); + return base; +} + + +#elif HS_BUILD_FOR_PS2 + +extern unsigned long psTimerGetCount(); +//#define kTickMul (150000000) // kTickMul/kTickDiv :: 4577.636719 +#define kTickMul (100000000) // kTickMul/kTickDiv :: 3051.757813 // for debugger +#define kTickDiv (256*128) + + +hsWide* plTimerShare::GetRawTicks(hsWide* ticks) +{ + unsigned long t= psTimerGetCount(); + ticks->Set( (Int32)(t>>32), (Int32)(t&((1ul<<32)-1))); + return ticks; +} + +hsWide plTimerShare::IInitRawBase() +{ + hsWide base; + base.Set(0, kTickMul/kTickDiv ); + return base; +} + +#endif + +// +// hsTimer - thin static interface to plTimerShare. Also keeps a couple of +// constants. +// +static plTimerShare staticTimer; +plTimerShare* hsTimer::fTimer = &staticTimer; // until overridden. +const double hsTimer::fPrecTicksPerSec = hsTimer::GetPrecTicksPerSec(); +const hsWide hsTimer::fRawBase = hsTimer::IInitRawBase(); + +void hsTimer::SetTheTimer(plTimerShare* timer) +{ + fTimer = timer; +} + +/////////////////////////// +// Precision timer routines +// These remain as statics +// since they are stateless +// anyway. +/////////////////////////// + +double hsTimer::GetPrecTicksPerSec() +{ +#if HS_BUILD_FOR_WIN32 + LARGE_INTEGER freq; + if( !QueryPerformanceFrequency(&freq) ) + { + return 1000.f; + } + return ((double) freq.LowPart); +#endif +#if HS_BUILD_FOR_MAC + return 1000.f; +#endif +#if HS_BUILD_FOR_PS2 + return 1000.f; +#endif + + return 1; +} + +UInt32 hsTimer::GetPrecTickCount() +{ +#if HS_BUILD_FOR_WIN32 + LARGE_INTEGER ti; + if( !QueryPerformanceCounter(&ti) ) + return GetTickCount(); + + return ti.LowPart; +#endif +#if HS_BUILD_FOR_MACPPC + return hsTimer::GetMSeconds(); +#endif + +#if HS_BUILD_FOR_PS2 + return hsTimer::GetMSeconds(); +#endif +} +UInt32 hsTimer::PrecSecsToTicks(hsScalar secs) +{ + return (UInt32)(((double)secs) * fPrecTicksPerSec); +} +double hsTimer::PrecTicksToSecs(UInt32 ticks) +{ + return ((double)ticks) / fPrecTicksPerSec; +} +double hsTimer::PrecTicksToHz(UInt32 ticks) +{ + return fPrecTicksPerSec / ((double)ticks); +} + +UInt64 hsTimer::GetFullTickCount() +{ +#if HS_BUILD_FOR_WIN32 + LARGE_INTEGER ticks; + QueryPerformanceCounter(&ticks); + return ticks.QuadPart; +#else + return 0; +#endif +} + +float hsTimer::FullTicksToMs(UInt64 ticks) +{ +#ifdef HS_BUILD_FOR_WIN32 + static UInt64 ticksPerTenthMs = 0; + + if (ticksPerTenthMs == 0) + { + LARGE_INTEGER perfFreq; + QueryPerformanceFrequency(&perfFreq); + ticksPerTenthMs = perfFreq.QuadPart / 10000; + } + + return float(ticks / ticksPerTenthMs) / 10.f; +#else + return 0.f; +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimedValue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimedValue.cpp new file mode 100644 index 00000000..df035042 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimedValue.cpp @@ -0,0 +1,31 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#include "hsTypes.h" +#include "hsTimer.h" +#include "plTimedValue.h" + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimedValue.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimedValue.h new file mode 100644 index 00000000..6395d6fc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimedValue.h @@ -0,0 +1,155 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plTimedValue_inc +#define plTimedValue_inc + +#include "hsTimer.h" + +class hsStream; + +// plTimedValue +// To use, replace your member var of type T with plTimedValue. +// You can then pretty much treat it as normal. To set it to interpolate +// to a new value over secs seconds, use Set(newVal, secs). +// For I/O, see plTimedSimple and plTimedCompound. +template class plTimedValue +{ +protected: + T fGoal; + T fInit; + double fStart; + hsScalar fInvSecs; + +public: + plTimedValue() {} + plTimedValue(const plTimedValue& o) { Set(o, 0.f); } + plTimedValue(const T& v) { Set(v, 0.f); } + + plTimedValue& operator=(const plTimedValue& o) { return Set(o, 0.f); } + plTimedValue& operator=(const T& v) { return Set(v, 0.f); } + + plTimedValue& Set(const T& v, hsScalar secs=0); + + operator T () const { return Value(); } + + T Value() const; + +}; + +// Read/Writable version of plTimedValue, for intrinsic types (e.g. int, float, bool). +// Must be a type that hsStream has an overloaded ReadSwap/WriteSwap defined. +template class plTimedSimple : public plTimedValue +{ +public: + plTimedSimple& operator=(const plTimedValue& o) { return Set(o, 0.f); } + plTimedSimple& operator=(const T& v) { return Set(v, 0.f); } + + plTimedSimple& Set(const T& v, hsScalar secs=0) { plTimedValue::Set(v, secs); return *this; } + + void Read(hsStream* s); + void Write(hsStream* s) const; +}; + +// Read/Writable version of plTimedValue, for compound types (e.g. hsVector3, hsColorRGBA). +// May be any type that has Read(hsStream*)/Write(hsStream*) defined. +template class plTimedCompound : public plTimedValue +{ +public: + plTimedCompound& operator=(const plTimedValue& o) { return Set(o, 0.f); } + plTimedCompound& operator=(const T& v) { return Set(v, 0.f); } + + plTimedCompound& Set(const T& v, hsScalar secs=0) { plTimedValue::Set(v, secs); return *this; } + + void Read(hsStream* s); + void Write(hsStream* s) const; +}; + +template +plTimedValue& plTimedValue::Set(const T& v, hsScalar secs) +{ + if( secs <= 0 ) + { + fGoal = fInit = v; + fInvSecs = 0; + } + else + { + fInit = Value(); + fStart = hsTimer::GetSysSeconds(); + fInvSecs = 1.f / secs; + fGoal = v; + } + return *this; +} + +template +T plTimedValue::Value() const +{ + if( fInvSecs > 0 ) + { + hsScalar t = (hsScalar)((hsTimer::GetSysSeconds() - fStart) * fInvSecs); + hsAssert(t >= 0, "Moving back in time"); + + if( t < 1.f ) + return fGoal * t + fInit * (1.f - t); + + } + return fGoal; +} + + +template +void plTimedSimple::Read(hsStream* s) +{ + T val; + s->ReadSwap(&val); + Set(val, 0.f); +} + +template +void plTimedSimple::Write(hsStream* s) const +{ + T val = Value(); + s->WriteSwap(val); +} + +template +void plTimedCompound::Read(hsStream* s) +{ + T val; + val.Read(s); + Set(val, 0.f); +} + +template +void plTimedCompound::Write(hsStream* s) const +{ + T val = Value(); + val.Write(s); +} + +#endif // plTimedValue_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimerCallbackManager.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimerCallbackManager.cpp new file mode 100644 index 00000000..3b877cd6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimerCallbackManager.cpp @@ -0,0 +1,183 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plTimerCallbackManager.h" +#include "../pnMessage/plTimeMsg.h" +#include "plgDispatch.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "hsTimer.h" + +plTimerCallbackManager::plTimerCallbackManager() +{ +} + +plTimerCallbackManager::~plTimerCallbackManager() +{ + while (fCallbacks.GetCount() > 0) + delete fCallbacks.Pop(); +} + +hsBool plTimerCallbackManager::MsgReceive(plMessage* msg) +{ + plTimeMsg* pTimeMsg = plTimeMsg::ConvertNoRef(msg); + int i = fCallbacks.Count(); + if (pTimeMsg ) + { + if(i) + { + i--; + if (pTimeMsg->GetTimeStamp() >= fCallbacks[i]->fTime) + { + plgDispatch::MsgSend( fCallbacks[i]->fMsg ); + + // Set it nil so the TimerCallback destructor doesn't unRef it + fCallbacks[i]->fMsg = nil; + + delete(fCallbacks[i]); + fCallbacks.SetCount(i); + } + } + return true; + } + return hsKeyedObject::MsgReceive(msg); +} + +plTimerCallback* plTimerCallbackManager::NewTimer(hsScalar time, plMessage* pMsg) +{ + plTimerCallback* t = TRACKED_NEW plTimerCallback( hsTimer::GetSysSeconds() + time, pMsg ); + fCallbacks.Append(t); + // sort them + for (int i = 0; i < fCallbacks.Count(); i++) + { + for (int j = i + 1; j < fCallbacks.Count(); j++) + { +#if 0 + hsScalar a = fCallbacks[i]->fTime; + hsScalar b = fCallbacks[j]->fTime; +#endif + if (fCallbacks[i]->fTime < fCallbacks[j]->fTime) + { + plTimerCallback* pTemp = fCallbacks[i]; + fCallbacks[i] = fCallbacks[j]; + fCallbacks[j] = pTemp; + } + } + } + return t; +} + +hsBool plTimerCallbackManager::CancelCallback(plTimerCallback* pTimer) +{ + for (int i = 0; i < fCallbacks.Count(); i++) + { + if (fCallbacks[i] == pTimer) + { fCallbacks.Remove(i); + return true; + } + } + return false; +} + +hsBool plTimerCallbackManager::CancelCallbacksToKey(const plKey& key) +{ + const plKey rKey; + bool removed = false; + + for (int i = fCallbacks.Count() - 1; i >= 0 ; i--) + { + for (int j = 0; j < fCallbacks[i]->fMsg->GetNumReceivers(); j++) + { + const plKey rKey = fCallbacks[i]->fMsg->GetReceiver(j); + + if (rKey == key) + { + delete fCallbacks[i]; + fCallbacks.Remove(i); + removed = true; + break; + } + } + } + + return removed; +} + +void plTimerCallbackManager::Read(hsStream* stream, hsResMgr* mgr) +{ +} +void plTimerCallbackManager::Write(hsStream* stream, hsResMgr* mgr) +{ +} + + + +plTimerCallback::plTimerCallback(double time, plMessage* pMsg) : +fTime(time), +fMsg(pMsg) +{ +} + +plTimerCallback::~plTimerCallback() +{ + if (fMsg) + hsRefCnt_SafeUnRef(fMsg); + fMsg = nil; +} + +void plTimerCallback::Read(hsStream* stream, hsResMgr* mgr) +{ +} +void plTimerCallback::Write(hsStream* stream, hsResMgr* mgr) +{ +} + + +plTimerCallbackManager* plgTimerCallbackMgr::fMgr = nil; + +void plgTimerCallbackMgr::Init() +{ + fMgr = TRACKED_NEW plTimerCallbackManager; + fMgr->RegisterAs( kTimerCallbackManager_KEY ); // fixedKey from plFixedKey.h + plgDispatch::Dispatch()->RegisterForExactType( plTimeMsg::Index(), fMgr->GetKey() ); +} + +hsBool plgTimerCallbackMgr::CancelCallback(plTimerCallback* pTimer) +{ + return (fMgr->CancelCallback(pTimer)); +} + +hsBool plgTimerCallbackMgr::CancelCallbacksToKey(const plKey& key) +{ + return (fMgr->CancelCallbacksToKey(key)); +} + +void plgTimerCallbackMgr::Shutdown() +{ + fMgr->UnRegisterAs(kTimerCallbackManager_KEY); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimerCallbackManager.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimerCallbackManager.h new file mode 100644 index 00000000..7a3e1929 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/plTimerCallbackManager.h @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plTimerCallbackManager_Defined +#define plTimerCallbackManager_Defined + +#include "hsScalar.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsTemplates.h" + +class plMessage; + +class plTimerCallback +{ +public: + + plTimerCallback(double time, plMessage* pMsg); + ~plTimerCallback(); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + double fTime; + plMessage* fMsg; +}; + +class plTimerCallbackManager : public hsKeyedObject +{ +public: + + plTimerCallbackManager(); + ~plTimerCallbackManager(); + + CLASSNAME_REGISTER( plTimerCallbackManager ); + GETINTERFACE_ANY( plTimerCallbackManager, hsKeyedObject ); + + virtual plTimerCallback* NewTimer(hsScalar time, plMessage* pMsg); + hsBool CancelCallback(plTimerCallback* pTimer); + hsBool CancelCallbacksToKey(const plKey& key); + + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + +private: + + hsTArray fCallbacks; +}; + +class plgTimerCallbackMgr +{ +private: + static plTimerCallbackManager* fMgr; +public: + + static void Init(); + static void Shutdown(); + static plTimerCallbackManager* Mgr() { return fMgr; } + + // External modifier use only + static void SetTheTimerCallbackMgr(plTimerCallbackManager *mgr) { fMgr = mgr; } + + static plTimerCallback* NewTimer(hsScalar time, plMessage* pMsg) { return (fMgr->NewTimer(time, pMsg)); } + static hsBool CancelCallback(plTimerCallback* pTimer); + static hsBool CancelCallbacksToKey(const plKey& key); +}; + + +#endif + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBranchDate.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBranchDate.cpp new file mode 100644 index 00000000..de1d6fd6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBranchDate.cpp @@ -0,0 +1,28 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pnBuildDates.h" + +char pnBuildDates::fBranchDate[] = "Pre-release"; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBuildDates.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBuildDates.cpp new file mode 100644 index 00000000..4f57425b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBuildDates.cpp @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pnBuildDates.cpp // +// // +// Thanks to there not being a pre-build step in MSVC, we have to do this // +// kluge instead. Basically, as a post-build step, we run a custom app // +// that inserts the date and time as string resources into the client EXE. // +// Then, on startup, the static class here gets inited and we load the // +// string resources into our char strings. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsWindows.h" +#include "../pnTimer/pnBuildDates.h" + +char pnBuildDates::fBuildDate[ 128 ] = __DATE__; +char pnBuildDates::fBuildTime[ 128 ] = __TIME__; + +// This forces our constructor to run on app load +static pnBuildDates staticObjToForceResLoad; + +#if HS_BUILD_FOR_WIN32 +HINSTANCE sModuleHandle = GetModuleHandle( NULL ); +#endif + +pnBuildDates::pnBuildDates() +{ + // Note; these are int konstants so we don't have to worry about sharing symbols + // across apps (in this case, our custom app that updates these strings on post-build) + IGetString( 1000, fBuildDate, sizeof( fBuildDate ) - 1 ); + IGetString( 1001, fBuildTime, sizeof( fBuildTime ) - 1 ); +} + +void pnBuildDates::IGetString( int resID, char *destBuffer, int size ) +{ +#if HS_BUILD_FOR_WIN32 + HRSRC rsrc = ::FindResource( sModuleHandle, MAKEINTRESOURCE( resID ), RT_RCDATA ); + + if( rsrc != NULL ) + { + HGLOBAL handle = ::LoadResource( sModuleHandle, rsrc ); + + if( handle != NULL ) + { + char *str = (char *)::LockResource( handle ); + strncpy( destBuffer, str, size ); + UnlockResource( handle ); + } + } +#endif +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBuildDates.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBuildDates.h new file mode 100644 index 00000000..d74b9122 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnBuildDates.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// pnBuildDates Header // +// // +// This is a tiny cpp/h wrapper for the build/branch dates. Basically, // +// this header defines two string konstants that at runtime will be the // +// build and branch date/timestamps. The pnBranchDate.cpp file is // +// regenerated on branch to fill in the branch date. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _pnBuildDates_h +#define _pnBuildDates_h + +class pnBuildDates +{ + private: + + void IGetString( int resID, char *destBuffer, int size ); + + public: + static char fBuildDate[]; + static char fBuildTime[]; + static char fBranchDate[]; + + pnBuildDates(); +}; + +#endif //_pnBuildDates_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnTimerCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnTimerCreatable.h new file mode 100644 index 00000000..dfdc3af4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnTimer/pnTimerCreatable.h @@ -0,0 +1,38 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pnTimerCreatable_inc +#define pnTimerCreatable_inc + +#include "../pnFactory/plCreator.h" + + +#include "plTimerCallbackManager.h" + +REGISTER_CREATABLE( plTimerCallbackManager ); + +#endif // pnTimerCreatable_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Intern.h new file mode 100644 index 00000000..d38420ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Intern.h @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_INTERN_H + + + +/***************************************************************************** +* +* Crypt +* +***/ + +namespace Crypt { + +//============================================================================ +class KeyBase { +public: + virtual void Codec (bool encrypt, ARRAY(byte) * dest, unsigned sourceBytes, const void * sourceData) = 0; + virtual unsigned GetBlockSize () const = 0; +}; + +//============================================================================ +class KeyRc4 : public KeyBase { +private: + unsigned m_x; + unsigned m_y; + byte m_state[256]; + + void Initialize (unsigned bytes, const void * data); + +public: + KeyRc4 (unsigned bytes, const void * data) { Initialize(bytes, data); } + + void Codec (bool encrypt, ARRAY(byte) * dest, unsigned sourceBytes, const void * sourceData); + unsigned GetBlockSize () const { return 1; } + + static void KeyGen ( + unsigned randomBytes, + const void * randomData, + ARRAY(byte) * privateData + ); +}; + + +} // namespace Crypt diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Pch.h new file mode 100644 index 00000000..9893d065 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Pch.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PCH_H + +#include "Private/pnUtAllIncludes.h" +#include "Intern.h" + +#include "pnProduct/pnProduct.h" + +#include + +#ifdef HS_BUILD_FOR_WIN32 +#pragma warning(push, 3) +#include +#include +#include // for SHGetSpecialFolderPath +#pragma warning(pop) +#endif + +#include // _alloca diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxStr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxStr.cpp new file mode 100644 index 00000000..5a0b7c94 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxStr.cpp @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxStr.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + + +#ifdef HS_BUILD_FOR_UNIX + + +#else + +// Dummy function to prevent a linker warning complaining about no public symbols if the +// contents of the file get compiled out via pre-processor +void UxStrPreventLNK4221Warning () { +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxSync.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxSync.cpp new file mode 100644 index 00000000..bc539c90 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxSync.cpp @@ -0,0 +1,45 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxSync.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +#ifdef HS_BUILD_FOR_UNIX + + +#else + +// Dummy function to prevent a linker warning complaining about no public symbols if the +// contents of the file get compiled out via pre-processor +void UxSyncPreventLNK4221Warning () { +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxUuid.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxUuid.cpp new file mode 100644 index 00000000..62941c8e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxUuid.cpp @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Unix/pnUtUxUuid.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Uuid Unix implementation +* +***/ + +#ifdef HS_BUILD_FOR_UNIX + +#include + +COMPILER_ASSERT(sizeof(Uuid) >= sizeof(uuid_t)); + +#else + +// Dummy function to prevent a linker warning complaining about no public symbols if the +// contents of the file get compiled out via pre-processor +void UxUuidPreventLNK4221Warning () { +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/W32Int.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/W32Int.h new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/W32Int.h @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Addr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Addr.cpp new file mode 100644 index 00000000..ecf3e8fb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Addr.cpp @@ -0,0 +1,373 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Addr.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + +// hardcoded byte ordering -- Intel only +#ifdef _M_IX86 + + const unsigned kHostClassALoopbackAddr = 0x7f000001; // 127.0.0.1 + const unsigned kHostClassALoopbackMask = 0x00ffffff; + const unsigned kNetClassALoopbackAddr = 0x0100007f; // 127.0.0.1 + const unsigned kNetClassALoopbackMask = 0xffffff00; + + const unsigned kHostClassANatAddr = 0x000000a0; // 10.0.0.0 - 10.255.255.255 + const unsigned kHostClassANatMask = 0x000000ff; + const unsigned kNetClassANatAddr = 0x0a000000; // 10.0.0.0 - 10.255.255.255 + const unsigned kNetClassANatMask = 0xff000000; + + const unsigned kHostClassBNetAddr = 0x000010ac; // 172.16.0.0 - 172.31.255.255 + const unsigned kHostClassBNetMask = 0x0000f0ff; + const unsigned kNetClassBNetAddr = 0xac100000; // 172.16.0.0 - 172.31.255.255 + const unsigned kNetClassBNetMask = 0xfff00000; + + const unsigned kHostClassCNatAddr = 0x0000a8c0; // 192.168.0.0 - 192.168.255.255 + const unsigned kHostClassCNatMask = 0x0000ffff; + const unsigned kNetClassCNatAddr = 0xc0a80000; // 192.168.0.0 - 192.168.255.255 + const unsigned kNetClassCNatMask = 0xffff0000; + +#else + +#error "Must implement for this architecture" + +#endif // ifdef _M_IX86 + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +// Address sort order: +// (highest) +// externally visible address +// 10.0.0.0 - 10.255.255.255 +// 172.16.0.0 - 172.31.255.255 +// 192.168.0.0 - 192.168.255.255 +// 127.0.0.0 - 127.0.0.255 +// (lowest) +static int NetAddressNodeSortValueNetOrder (NetAddressNode addr) { + ref(NetAddressNodeSortValueNetOrder); + // Loopback addresses + if ((addr & kNetClassALoopbackMask) == (kNetClassALoopbackAddr & kNetClassALoopbackMask)) + return 4; + + // Private addresses + if ((addr & kNetClassCNatMask) == (kNetClassCNatAddr & kNetClassCNatMask)) + return 3; + if ((addr & kNetClassBNetMask) == (kNetClassBNetAddr & kNetClassBNetMask)) + return 2; + if ((addr & kNetClassANatMask) == (kNetClassANatAddr & kNetClassANatMask)) + return 1; + + // Public addresses + return 0; +} + +//=========================================================================== +static int NetAddressNodeSortValueHostOrder (NetAddressNode addr) { + // Loopback addresses + if ((addr & kHostClassALoopbackMask) == (kHostClassALoopbackAddr & kHostClassALoopbackMask)) + return 4; + + // Private addresses + if ((addr & kHostClassCNatMask) == (kHostClassCNatAddr & kHostClassCNatMask)) + return 3; + if ((addr & kHostClassBNetMask) == (kHostClassBNetAddr & kHostClassBNetMask)) + return 2; + if ((addr & kHostClassANatMask) == (kHostClassANatAddr & kHostClassANatMask)) + return 1; + + // Public addresses + return 0; +} + +//=========================================================================== +static NetAddressNode NodeFromString (const wchar * string[]) { + // skip leading whitespace + const wchar * str = *string; + while (iswspace(*str)) + ++str; + + // This function handles partial ip addresses (61.33) + // as well as full dotted quads. The address can be + // terminated by whitespace or ':' as well as '\0' + byte data[4]; + * (dword *) data = 0; + for (unsigned i = sizeof(data); i--; ) { + if (!iswdigit(*str)) + return (unsigned)-1; + + unsigned value = StrToUnsigned(str, &str, 10); + if (value >= 256) + return (unsigned)-1; + data[i] = (byte) value; + + if (!*str || (*str == ':') || iswspace(*str)) + break; + + static const wchar s_separator[] = L"\0..."; + if (*str++ != s_separator[i]) + return (unsigned)-1; + } + + *string = str; + return * (NetAddressNode *) &data[0]; +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +int NetAddressCompare (const NetAddress & a1, const NetAddress & a2) { + const sockaddr_in & i1 = * (const sockaddr_in *) &a1; + const sockaddr_in & i2 = * (const sockaddr_in *) &a2; + + int d = i1.sin_addr.S_un.S_addr - i2.sin_addr.S_un.S_addr; + return d ? d : i1.sin_port - i2.sin_port; +} + +//=========================================================================== +bool NetAddressSameSystem (const NetAddress & a1, const NetAddress & a2) { + const sockaddr_in & i1 = * (const sockaddr_in *) &a1; + const sockaddr_in & i2 = * (const sockaddr_in *) &a2; + return i1.sin_addr.S_un.S_addr == i2.sin_addr.S_un.S_addr; +} + +//=========================================================================== +unsigned NetAddressHash (const NetAddress & addr) { + // by using only the node number as the hash value, users can safely use + // hash value to find addresses by either using either "SameSystem" or "Equal" + const sockaddr_in & iAddr = * (const sockaddr_in *) &addr; + return iAddr.sin_addr.S_un.S_addr; +} + +//=========================================================================== +void NetAddressToString ( + const NetAddress & addr, + wchar * str, + unsigned chars, + ENetAddressFormat format +) { + ASSERT(str); + + static const wchar * s_fmts[] = { + L"%S", // kNetAddressFormatNodeNumber + L"%S:%u", // kNetAddressFormatAll + }; + ASSERT(format < arrsize(s_fmts)); + const sockaddr_in & inetaddr = * (const sockaddr_in *) &addr; + StrPrintf( + str, + chars, + s_fmts[format], + inet_ntoa(inetaddr.sin_addr), + ntohs(inetaddr.sin_port) + ); +} + +//=========================================================================== +bool NetAddressFromString (NetAddress * addr, const wchar str[], unsigned defaultPort) { + ASSERT(addr); + ASSERT(str); + + // NetAddress is bigger than sockaddr_in so start by zeroing the whole thing + ZEROPTR(addr); + + for (;;) { + NetAddressNode node = NodeFromString(&str); + if (node == (unsigned)-1) + break; + + if (*str == L':') + defaultPort = StrToUnsigned(str + 1, nil, 10); + + sockaddr_in * inetaddr = (sockaddr_in *) addr; + inetaddr->sin_family = AF_INET; + inetaddr->sin_port = htons((word) defaultPort); + inetaddr->sin_addr.S_un.S_addr = htonl(node); + // inetaddr->sin_zero already zeroed + + return true; + } + + // address already zeroed + return false; +} + +//=========================================================================== +unsigned NetAddressGetPort ( + const NetAddress & addr +) { + return ntohs(((sockaddr_in *) &addr)->sin_port); +} + +//=========================================================================== +void NetAddressSetPort ( + unsigned port, + NetAddress * addr +) { + ((sockaddr_in *) addr)->sin_port = htons((word) port); +} + +//============================================================================ +NetAddressNode NetAddressGetNode (const NetAddress & addr) { + return ntohl(((const sockaddr_in *) &addr)->sin_addr.S_un.S_addr); +} + +//=========================================================================== +void NetAddressFromNode ( + NetAddressNode node, + unsigned port, + NetAddress * addr +) { + ZEROPTR(addr); + sockaddr_in * inetaddr = (sockaddr_in *) addr; + inetaddr->sin_family = AF_INET; + inetaddr->sin_addr.S_un.S_addr = htonl(node); + inetaddr->sin_port = htons((word) port); +} + +//=========================================================================== +void NetAddressNodeToString ( + NetAddressNode node, + wchar * str, + unsigned chars +) { + in_addr addr; + addr.S_un.S_addr = htonl(node); + StrPrintf(str, chars, L"%S", inet_ntoa(addr)); +} + +//=========================================================================== +NetAddressNode NetAddressNodeFromString ( + const wchar string[], + const wchar * endPtr[] +) { + if (!endPtr) + endPtr = &string; + *endPtr = string; + return NodeFromString(endPtr); +} + +//=========================================================================== +void NetAddressGetLoopback ( + unsigned port, + NetAddress * addr +) { + NetAddressFromNode( + kHostClassALoopbackAddr, + port, + addr + ); +} + +//=========================================================================== +unsigned NetAddressGetLocal ( + unsigned count, + NetAddressNode addresses[] +) { + ASSERT(count); + ASSERT(addresses); + + for (;;) { + // Get local computer name + char name[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD size = arrsize(name); + if (!GetComputerName(name, &size)) + StrCopy(name, "localhost", arrsize(name)); + + // Get IPv4 addresses for local system + const struct hostent * host = gethostbyname(name); + if (!host || !host->h_name) + break; + host = gethostbyname(host->h_name); + if (!host) + break; + if (host->h_length != sizeof(dword)) + break; + + // Count total number of addresses + unsigned found = 0; + const dword ** addr = (const dword **) host->h_addr_list; + for (; *addr; ++addr) + ++found; + if (!found) + break; + + // Create a buffer to sort the addresses + NetAddressNode * dst; + if (found > count) + dst = ALLOCA(NetAddressNode, found); + else + dst = addresses; + + // Fill address buffer + const dword * src = * (const dword **) host->h_addr_list; + for (unsigned index = 0; index < found; ++index) + dst[index] = ntohl(src[index]); + + // Sort addresses by priority + QSORT( + NetAddressNode, + dst, + found, + NetAddressNodeSortValueHostOrder(elem1) - NetAddressNodeSortValueHostOrder(elem2) + ); + + // Return the number of addresses the user actually requested + if (found > count) { + for (unsigned index = 0; index < count; ++index) + addresses[index] = dst[index]; + return count; + } + + return found; + } + + // Initialize with a valid value + addresses[0] = kHostClassALoopbackAddr; + return 1; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Dll.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Dll.cpp new file mode 100644 index 00000000..5f46ad3f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Dll.cpp @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Dll.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Misc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Misc.cpp new file mode 100644 index 00000000..1ce98712 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Misc.cpp @@ -0,0 +1,207 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Misc.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +/***************************************************************************** +* +* Private +* +***/ +static MEMORYSTATUSEX s_memstatus; + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +const wchar * AppGetCommandLine () { + return GetCommandLineW(); +} + +//============================================================================ +void MachineGetName (wchar *computerName, unsigned int length) { + DWORD len = length; + GetComputerNameW(computerName, &len); +} + +/***************************************************************************** +* +* System status +* +***/ + +//============================================================================ +void MemoryGetStatus (MemoryStatus * status) { + MEMORYSTATUSEX mem; + mem.dwLength = sizeof(mem); + GlobalMemoryStatusEx(&mem); + + const qword BYTES_PER_MB = 1024 * 1024; + status->totalPhysMB = unsigned(mem.ullTotalPhys / BYTES_PER_MB); + status->availPhysMB = unsigned(mem.ullAvailPhys / BYTES_PER_MB); + status->totalPageFileMB = unsigned(mem.ullTotalPageFile / BYTES_PER_MB); + status->availPageFileMB = unsigned(mem.ullAvailPageFile / BYTES_PER_MB); + status->totalVirtualMB = unsigned(mem.ullTotalVirtual / BYTES_PER_MB); + status->availVirtualMB = unsigned(mem.ullAvailVirtual / BYTES_PER_MB); + status->memoryLoad = mem.dwMemoryLoad; +} + +//============================================================================ +void DiskGetStatus (ARRAY(DiskStatus) * disks) { + for (;;) { + DWORD length = GetLogicalDriveStrings(0, NULL); + if (!length || length > 2048) + break; + + wchar * buffer = ALLOCA(wchar, length + 1); + if (!GetLogicalDriveStringsW(length, buffer)) + break; + + for (; *buffer; buffer += StrLen(buffer) + 1) { + UINT driveType = GetDriveTypeW(buffer); + if (driveType != DRIVE_FIXED) + continue; + + ULARGE_INTEGER freeBytes; + ULARGE_INTEGER totalBytes; + if (!GetDiskFreeSpaceExW(buffer, &freeBytes, &totalBytes, NULL)) + continue; + + DiskStatus status; + StrCopy(status.name, buffer, arrsize(status.name)); + + const qword BYTES_PER_MB = 1024 * 1024; + status.totalSpaceMB = unsigned(totalBytes.QuadPart / BYTES_PER_MB); + status.freeSpaceMB = unsigned(freeBytes.QuadPart / BYTES_PER_MB); + + disks->Add(status); + } + break; + } +} + +//============================================================================ +// Loosely taken from MS's cpuid code sample +void CpuGetInfo ( + word * cpuCaps, + dword * cpuVendor, + word * cpuSignature +) { + dword signature = 0; + dword extended = 0; + dword flags[2] = { 0, 0 }; + cpuVendor[0] = 0; + + _asm { + // Detect if cpuid instruction is supported by attempting + // to change the ID bit of EFLAGS + pushfd + pop eax // get EFLAGS + mov ecx, eax // store copy of original EFLAGS + xor eax, 0x200000 // flip ID bit + push eax + popfd // replace EFLAGS + pushfd // get EFLAGS + pop eax + xor eax, ecx + je DONE + + // Get processor id (GenuineIntel, AuthenticAMD, etc) + xor eax, eax + cpuid + mov edi, cpuVendor + mov [edi + 0], ebx + mov [edi + 4], edx + mov [edi + 8], ecx + + // Check if capability flags are supported + cmp eax, 1 + jl DONE + + // Get processor capability flags and signature + mov eax, 1 + cpuid + mov signature, eax + mov [flags + 0], edx + mov [flags + 4], ecx + + // Check for extended capabilities + mov eax, 0x80000000 + cpuid + cmp eax, 0x80000001 + jl DONE + + // Get extended capabilities + mov eax, 0x80000001 + cpuid + mov extended, edx + +DONE: + } + + // Decode capability flags + const static struct CpuCap { + word cpuFlag; + byte field; + byte bit; + } s_caps[] = { + // feature field bit + // ------- ----- --- + { kCpuCapCmov, 0, 15 }, + { kCpuCapEst, 1, 7 }, + { kCpuCapHtt, 0, 28 }, + { kCpuCapMmx, 0, 23 }, + { kCpuCapPsn, 0, 18 }, + { kCpuCapSse, 0, 25 }, + { kCpuCapSse2, 0, 26 }, + { kCpuCapSse3, 1, 0 }, + { kCpuCapTsc, 0, 4 }, + }; + for (unsigned i = 0; i < arrsize(s_caps); ++i) { + const CpuCap & cap = s_caps[i]; + if (flags[cap.field] & (1 << cap.bit)) + *cpuCaps |= cap.cpuFlag; + } + + // Copy signature + *cpuSignature = word(signature & 0xfff); + + // If this is an AMD CPU, check for 3DNow support + const char * vendorAmd = "AuthenticAMD"; + if (!MemCmp(vendorAmd, cpuVendor, 12)) { + if (extended & (1 << 31)) + *cpuCaps |= kCpuCap3dNow; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Path.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Path.cpp new file mode 100644 index 00000000..72a0e650 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Path.cpp @@ -0,0 +1,845 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Path.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local functions +* +***/ + +// make sure our definition is at least as big as the compiler's definition +COMPILER_ASSERT(MAX_PATH >= _MAX_PATH); + + +//=========================================================================== +static inline bool IsSlash (wchar c) { + return (c == L'\\') || (c == L'/'); +} + +//=========================================================================== +static inline wchar ConvertSlash (wchar c) { + return c != L'/' ? c : L'\\'; +} + +//=========================================================================== +static inline bool IsUncPath (const wchar path[]) { + return IsSlash(path[0]) && IsSlash(path[1]); +} + +//=========================================================================== +static const wchar * SkipUncDrive (const wchar path[]) { + // UNC drive: "//server/share" + + // skip over leading "//" + path += 2; + + // scan forward to end of server name + for (;; ++path) { + if (!*path) + return path; + if (IsSlash(*path)) + break; + } + + // skip over '/' + ++path; + + // skip over share name + for (;; ++path) { + if (!*path) + return path; + if (IsSlash(*path)) + return path; + } +} + +//=========================================================================== +static wchar * PathSkipOverSeparator (wchar * path) { + for (; *path; ++path) { + if (IsSlash(*path)) + return path + 1; + } + + return path; +} + +//=========================================================================== +static unsigned CommonPrefixLength ( + const wchar src1[], + const wchar src2[] +) { + ASSERT(src1); + ASSERT(src2); + + wchar const * const base = src1; + const wchar * common = nil; + for (;;) { + // Are the next components equal in length? + const wchar * next1 = PathSkipOverSeparator(const_cast(src1)); + const wchar * next2 = PathSkipOverSeparator(const_cast(src2)); + const int componentLen = next1 - src1; + if (componentLen != (next2 - src2)) + break; + + // Are the next components equal in value? + if (!StrCmpI(src1, src2, componentLen)) + common = next1; + else + break; + + if (!*next1) + break; + src1 = next1 + 1; + + if (!*next2) + break; + src2 = next2 + 1; + } + + if (!common) + return 0; + + // Compute length of common subchunk; + // if it is "C:" convert it to "C:\" + unsigned commonLen = common - base; + if ((commonLen == 2) && (base[1] == L':')) + ++commonLen; + return commonLen; +} + +//=========================================================================== +static void GetProgramName ( + void * instance, + wchar * dst, + unsigned dstChars +) { + ASSERT(dst); + ASSERT(dstChars); + + if (!GetModuleFileNameW((HINSTANCE) instance, dst, dstChars)) { + ErrorAssert(__LINE__, __FILE__, "GetModuleName failed"); + *dst = 0; + } +} + + +/**************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +void PathGetModuleName ( + wchar * dst, + unsigned dstChars +) { + GetProgramName(ModuleGetInstance(), dst, dstChars); +} + +//=========================================================================== +void PathGetProgramName ( + wchar * dst, + unsigned dstChars +) { + GetProgramName(nil, dst, dstChars); +} + +//=========================================================================== +bool PathFromString ( + wchar * dst, + const wchar src[], + unsigned dstChars +) { + ASSERT(dst); + ASSERT(src); + ASSERT(dstChars); + + for (;;) { + // enable src and dst to be the same buffer + wchar temp[MAX_PATH]; + if (dst == src) { + StrCopy(temp, src, arrsize(temp)); + src = temp; + } + + DWORD const result = GetFullPathNameW(src, dstChars, dst, 0); + if (!result) + break; + if (dstChars < result) + break; + if (!dst[0]) + break; + + return true; + } + + *dst = 0; + return false; +} + +//=========================================================================== +bool PathFromString ( + wchar * dst, // ASSERT(dst); + const wchar src[], // ASSERT(src); + unsigned dstChars, // ASSERT(dstChars); + const wchar baseDir[] // ASSERT(baseDir); +) { + ASSERT(baseDir); + ASSERT(dstChars); + + // Save current directory + wchar curr[MAX_PATH]; + PathGetCurrentDirectory(curr, arrsize(curr)); + + // Perform string conversion from specified directory + bool result; + if (0 != (result = PathSetCurrentDirectory(baseDir))) + result = PathFromString(dst, src, dstChars); + else + *dst = 0; + + // Restore directory + PathSetCurrentDirectory(curr); + return result; +} + +//=========================================================================== +// this function was originally derived from _tsplitpath in the MSVCRT library, +// but has been updated to support UNC paths and to avoid blasting off the end +// of the buffers. +void PathSplitPath ( + const wchar path[], + wchar * drive, + wchar * dir, + wchar * fname, + wchar * ext +) { + ASSERT(path); + ASSERT(path != drive); + ASSERT(path != dir); + ASSERT(path != fname); + ASSERT(path != ext); + + // check for UNC path + if (IsUncPath(path)) { + const wchar * pathStart = path; + path = SkipUncDrive(path); + + if (drive) + StrCopy(drive, pathStart, min(MAX_DRIVE, path - pathStart + 1)); + } + // regular DOS path + else if (path[0] && (path[1] == L':')) { + if (drive) { + ASSERT(MAX_DRIVE >= 3); + drive[0] = path[0]; + drive[1] = L':'; + drive[2] = L'\0'; + } + + path += 2; // skip over 'C' ':' + } + else if (drive) { + *drive = 0; + } + + // extract path string, if any. Path now points to the first character + // of the path, if any, or the filename or extension, if no path was + // specified. Scan ahead for the last occurence, if any, of a '/' or + // '\' path separator character. If none is found, there is no path. + // We will also note the last '.' character found, if any, to aid in + // handling the extension. + const wchar *last_slash = nil, *last_dot = nil, *p = path; + for (; *p; p++) { + if (IsSlash(*p)) + last_slash = p + 1; // point to one beyond for later copy + else if (*p == L'.') + last_dot = p; + } + + if (last_slash) { + if (dir) + StrCopy(dir, path, min(MAX_DIR, last_slash - path + 1)); + path = last_slash; + } + else if (dir) { + *dir = 0; + } + + // extract file name and extension, if any. Path now points to the + // first character of the file name, if any, or the extension if no + // file name was given. Dot points to the '.' beginning the extension, + // if any. + if (last_dot && (last_dot >= path)) { + if (fname) + StrCopy(fname, path, min(MAX_FNAME, last_dot - path + 1)); + if (ext) + StrCopy(ext, last_dot, MAX_EXT); + } + else { + if (fname) + StrCopy(fname, path, MAX_FNAME); + if (ext) + *ext = 0; + } +} + +//=========================================================================== +void PathMakePath ( + wchar * path, + unsigned chars, + const wchar drive[], + const wchar dir[], + const wchar fname[], + const wchar ext[] +) { + ASSERT(path); + ASSERT(path != drive); + ASSERT(path != dir); + ASSERT(path != fname); + ASSERT(path != ext); + + // save space for string terminator + if (!chars--) + return; + + // copy drive + if (drive && *drive && chars) { + do { + *path++ = ConvertSlash(*drive++); + } while (--chars && *drive); + ASSERT(!IsSlash(path[-1])); + } + + // copy directory + if (dir && *dir && chars) { + do { + *path++ = ConvertSlash(*dir++); + } while (--chars && *dir); + + // add trailing backslash + if (chars && (path[-1] != '\\')) { + *path++ = L'\\'; + chars--; + } + } + + // copy filename + if (fname && *fname && chars) { + // skip leading backslash + if (IsSlash(*fname)) + ++fname; + + do { + *path++ = ConvertSlash(*fname++); + } while (--chars && *fname); + } + + // copy extension + if (ext && *ext && chars) { + if (*ext != L'.') { + *path++ = L'.'; + chars--; + } + while (chars-- && *ext) + *path++ = ConvertSlash(*ext++); + } + + // add string terminator + *path = L'\0'; +} + +//=========================================================================== +bool PathMakeRelative ( + wchar *dst, + unsigned fromFlags, // 0 or kPathFlagDirectory + const wchar from[], + unsigned toFlags, // 0 or kPathFlagDirectory + const wchar to[], + unsigned dstChars +) { + ASSERT(dst); + ASSERT(from); + ASSERT(to); + ASSERT(dstChars); + *dst = 0; + + unsigned prefixLength = CommonPrefixLength(from, to); + if (!prefixLength) + return false; + + wchar fromBuf[MAX_PATH]; + if (fromFlags & kPathFlagDirectory) + StrCopy(fromBuf, from, arrsize(fromBuf)); + else + PathRemoveFilename(fromBuf, from, arrsize(fromBuf)); + + wchar toBuf[MAX_PATH]; + if (toFlags & kPathFlagDirectory) + StrCopy(toBuf, to, arrsize(toBuf)); + else + PathRemoveFilename(toBuf, to, arrsize(toBuf)); + + const wchar * curr = fromBuf + prefixLength; + if (*curr) { + // build ..\.. part of the path + if (IsSlash(*curr)) + curr++; // skip slash + + while (*curr) { + curr = PathSkipOverSeparator(const_cast(curr)); + StrPack(dst, *curr ? L"..\\" : L"..", dstChars); + } + } + else { + StrCopy(dst, L".", dstChars); + } + + if (to[prefixLength]) { + // deal with root case + if (!IsSlash(to[prefixLength])) + --prefixLength; + + ASSERT(IsSlash(to[prefixLength])); + StrPack(dst, to + prefixLength, dstChars); + } + + return true; +} + +//=========================================================================== +bool PathIsRelative ( + const wchar src[] +) { + ASSERT(src); + if (!src[0]) + return true; + if (IsSlash(src[0])) + return false; + if (src[1] == L':') + return false; + return true; +} + +//=========================================================================== +const wchar * PathFindFilename ( + const wchar path[] +) { + ASSERT(path); + + if (IsUncPath(path)) + path = SkipUncDrive(path); + + const wchar * last_slash = path; + for (const wchar * p = path; *p; p++) { + if ((*p == L'/') || (*p == L'\\') || (*p == L':')) + last_slash = p + 1; + } + + return last_slash; +} + +//=========================================================================== +const wchar * PathFindExtension ( + const wchar path[] +) { + ASSERT(path); + + const wchar * last_dot = 0; + const wchar * p = PathFindFilename(path); + for ( ; *p; p++) { + if (*p == L'.') + last_dot = p; + } + + return last_dot ? last_dot : p; +} + +//=========================================================================== +void PathGetCurrentDirectory ( + wchar * dst, + unsigned dstChars +) { + ASSERT(dst); + ASSERT(dstChars); + + DWORD result = GetCurrentDirectoryW(dstChars, dst); + if (!result || (result >= dstChars)) { + ErrorAssert(__LINE__, __FILE__, "GetDir failed"); + *dst = 0; + } +} + +//=========================================================================== +void PathGetTempDirectory ( + wchar * dst, + unsigned dstChars +) { + ASSERT(dst); + ASSERT(dstChars); + + DWORD result = GetTempPathW(dstChars, dst); + if (!result || (result >= dstChars)) + StrCopy(dst, L"C:\\temp\\", dstChars); +} + +//============================================================================ +void PathGetUserDirectory ( + wchar * dst, + unsigned dstChars +) { + ASSERT(dst); + ASSERT(dstChars); + + wchar temp[MAX_PATH]; // GetSpecialFolder path requires a buffer of MAX_PATH size or larger + if (SHGetSpecialFolderPathW(NULL, temp, CSIDL_PERSONAL, TRUE) == FALSE) + StrCopy(temp, L"C:\\", arrsize(temp)); + + // append the product name + PathAddFilename(dst, temp, ProductLongName(), dstChars); + +#if BUILD_TYPE != BUILD_TYPE_LIVE + // non-live builds live in a subdir + PathAddFilename(dst, dst, BuildTypeString(), dstChars); +#endif + + // ensure it exists + if (!PathDoesDirectoryExist(dst)) + PathCreateDirectory(dst, kPathCreateDirFlagEntireTree); +} + +//============================================================================ +void PathGetLogDirectory ( + wchar * dst, + unsigned dstChars +) { + ASSERT(dst); + ASSERT(dstChars); + PathGetUserDirectory(dst, dstChars); + PathAddFilename(dst, dst, L"Log", dstChars); + if (!PathDoesDirectoryExist(dst)) + PathCreateDirectory(dst, kPathCreateDirFlagEntireTree); +} + +//============================================================================ +void PathGetInitDirectory ( + wchar * dst, + unsigned dstChars +) { + ASSERT(dst); + ASSERT(dstChars); + PathGetUserDirectory(dst, dstChars); + PathAddFilename(dst, dst, L"Init", dstChars); + if (!PathDoesDirectoryExist(dst)) + PathCreateDirectory(dst, kPathCreateDirFlagEntireTree); +} + +//=========================================================================== +bool PathSetCurrentDirectory ( + const wchar path[] +) { + ASSERT(path); + return SetCurrentDirectoryW(path) != 0; +} + +//=========================================================================== +void PathSetProgramDirectory () { + wchar dir[MAX_PATH]; + PathGetProgramDirectory(dir, arrsize(dir)); + PathSetCurrentDirectory(dir); +} + +//=========================================================================== +void PathFindFiles ( + ARRAY(PathFind) * paths, + const wchar fileSpec[], + unsigned pathFlags +) { + ASSERT(paths); + ASSERT(fileSpec); + + HANDLE find; + WIN32_FIND_DATAW fd; + wchar directory[MAX_PATH]; + PathRemoveFilename(directory, fileSpec, arrsize(directory)); + if (INVALID_HANDLE_VALUE == (find = FindFirstFileW(fileSpec, &fd))) { + DWORD err = GetLastError(); + if ((err != ERROR_FILE_NOT_FOUND) && (err != ERROR_PATH_NOT_FOUND)) + ASSERTMSG(err, "PathFindFiles failed"); + } + else { + // find all the items in the current directory + do { + unsigned fileFlags = 0; + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (! (pathFlags & kPathFlagDirectory)) + continue; + + // don't add "." and ".." + if (fd.cFileName[0] == L'.') { + if (!fd.cFileName[1]) + continue; + if (fd.cFileName[1] == L'.' && !fd.cFileName[2]) + continue; + } + + fileFlags = kPathFlagDirectory; + } + else { + if (! (pathFlags & kPathFlagFile)) + continue; + fileFlags = kPathFlagFile; + } + if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) { + if (! (pathFlags & kPathFlagHidden)) + continue; + fileFlags |= kPathFlagHidden; + } + if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) { + if (! (pathFlags & kPathFlagSystem)) + continue; + fileFlags |= kPathFlagSystem; + } + + // add this one to the list of found files + PathFind * pf = paths->New(); + pf->flags = fileFlags; + pf->fileLength = ((qword) fd.nFileSizeHigh << 32) | fd.nFileSizeLow; + pf->lastWriteTime = * (const qword *) &fd.ftLastWriteTime; + PathAddFilename(pf->name, directory, fd.cFileName, arrsize(pf->name)); + } while (FindNextFileW(find, &fd)); + FindClose(find); + } + + // check for directory recursing + if ((pathFlags & kPathFlagRecurse) || StrStr(fileSpec, L"**")) { + // recurse directories + } + else { + return; + } + + wchar dirSpec[MAX_PATH]; + PathAddFilename(dirSpec, directory, L"*", arrsize(dirSpec)); + if (INVALID_HANDLE_VALUE == (find = FindFirstFileW(dirSpec, &fd))) { + DWORD err = GetLastError(); + if ((err != ERROR_FILE_NOT_FOUND) && (err != ERROR_PATH_NOT_FOUND)) + ErrorAssert(__LINE__, __FILE__, "PathFindFiles failed"); + return; + } + + // find all the directories in the current directory + const wchar * spec = PathFindFilename(fileSpec); + do { + if (! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + continue; + } + if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) { + if (! (pathFlags & kPathFlagHidden)) + continue; + } + if (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) { + if (! (pathFlags & kPathFlagSystem)) + continue; + } + + // don't recurse "." and ".." + if (fd.cFileName[0] == L'.') { + if (!fd.cFileName[1]) + continue; + if (fd.cFileName[1] == L'.' && !fd.cFileName[2]) + continue; + } + + // recursively search subdirectory + PathAddFilename(dirSpec, directory, fd.cFileName, arrsize(dirSpec)); + PathAddFilename(dirSpec, dirSpec, spec, arrsize(dirSpec)); + PathFindFiles(paths, dirSpec, pathFlags); + + } while (FindNextFileW(find, &fd)); + FindClose(find); +} + +//=========================================================================== +EPathCreateDirError PathCreateDirectory (const wchar path[], unsigned flags) { + ASSERT(path); + + // convert from relative path to full path + wchar dir[MAX_PATH]; + if (!PathFromString(dir, path, arrsize(dir))) { + return kPathCreateDirErrInvalidPath; + } + + // are we going to build the entire directory tree? + wchar * dirEnd; + if (flags & kPathCreateDirFlagEntireTree) { + dirEnd = dir; + + // skip over leading slashes in UNC paths + while (IsSlash(*dirEnd)) + ++dirEnd; + + // skip forward to first directory + dirEnd = PathSkipOverSeparator(dirEnd); + } + // we're only creating the very last entry in the path + else { + dirEnd = dir + StrLen(dir); + } + + bool result = true; + for (wchar saveChar = L' '; saveChar; *dirEnd++ = saveChar) { + // find the end of the current directory string and terminate it + dirEnd = PathSkipOverSeparator(dirEnd); + saveChar = *dirEnd; + *dirEnd = 0; + + // create the directory and track the result from the last call + result = CreateDirectoryW(dir, (LPSECURITY_ATTRIBUTES) nil); + } + + // if we successfully created the directory then we're done + if (result) { + // Avoid check for kPathCreateDirFlagOsError + COMPILER_ASSERT(kPathCreateDirSuccess == NO_ERROR); + return kPathCreateDirSuccess; + } + + unsigned error = GetLastError(); + switch (error) { + case ERROR_ACCESS_DENIED: + return kPathCreateDirErrAccessDenied; + + case ERROR_ALREADY_EXISTS: { + DWORD attrib; + if (0xffffffff == (attrib = GetFileAttributesW(dir))) + return kPathCreateDirErrInvalidPath; + + if (! (attrib & FILE_ATTRIBUTE_DIRECTORY)) + return kPathCreateDirErrFileWithSameName; + + if (flags & kPathCreateDirFlagCreateNew) + return kPathCreateDirErrDirExists; + } + return kPathCreateDirSuccess; + + default: + return kPathCreateDirErrInvalidPath; + } +} + +//=========================================================================== +void PathDeleteDirectory (const wchar path[], unsigned flags) { + ASSERT(path); + + // convert from relative path to full path + wchar dir[MAX_PATH]; + if (!PathFromString(dir, path, arrsize(dir))) + return; + + for (;;) { + // Important: in order to ensure that we don't delete NTFS + // partition links, we must ensure that this is a directory! + dword attributes = GetFileAttributesW(dir); + if (attributes == (dword) -1) + break; + if ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + break; + if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) + break; + + if (!RemoveDirectoryW(dir)) + break; + + if ((flags & kPathCreateDirFlagEntireTree) == 0) + break; + + wchar * filename = PathFindFilename(dir); + if (!filename) + break; + + // Move up one level in the directory hierarchy + unsigned oldLength = StrLen(dir); + while ((filename > dir) && IsSlash(filename[-1])) + --filename; + *filename = 0; + if (oldLength == StrLen(dir)) + break; + } +} + +//=========================================================================== +bool PathDoesFileExist (const wchar fileName[]) { + dword attributes = GetFileAttributesW(fileName); + if (attributes == (dword) -1) + return false; + if (attributes & FILE_ATTRIBUTE_DIRECTORY) + return false; + return true; +} + +//============================================================================ +bool PathDoesDirectoryExist (const wchar directory[]) { + dword attributes = GetFileAttributesW(directory); + if (attributes == (dword) -1) + return false; + if (attributes & FILE_ATTRIBUTE_DIRECTORY) + return true; + return false; +} + +//=========================================================================== +bool PathDeleteFile ( + const wchar file[] +) { + return DeleteFileW(file) != 0; +} + +//=========================================================================== +bool PathMoveFile ( + const wchar src[], + const wchar dst[] +) { + return MoveFileW(src, dst) != 0; +} + +//=========================================================================== +bool PathCopyFile ( + const wchar src[], + const wchar dst[] +) { + return CopyFileW(src, dst, FALSE) != 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Str.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Str.cpp new file mode 100644 index 00000000..cf9f7c7e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Str.cpp @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Str.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +unsigned StrToAnsi (char * dest, const wchar source[], unsigned destChars) { + return StrToAnsi(dest, source, destChars, CP_ACP); +} + +//=========================================================================== +unsigned StrToAnsi (char * dest, const wchar source[], unsigned destChars, unsigned codePage) { + ASSERT(destChars != (unsigned)-1); + ASSERT(dest != nil); + + int result = WideCharToMultiByte(codePage, 0, source, -1, dest, destChars, nil, nil); + if (result) + return result - 1; // return number of characters not including null terminator + else if (destChars) { + dest[destChars - 1] = 0; // null terminate the destination buffer + return destChars - 1; + } + else + return 0; +} + +//=========================================================================== +unsigned StrToUnicode (wchar * dest, const char source[], unsigned destChars) { + return StrToUnicode(dest, source, destChars, CP_ACP); +} + +//=========================================================================== +unsigned StrToUnicode (wchar * dest, const char source[], unsigned destChars, unsigned codePage) { + ASSERT(destChars != (unsigned)-1); + ASSERT(dest != nil); + + int result = MultiByteToWideChar(codePage, 0, source, -1, dest, destChars); + if (result) + return result - 1; // return number of characters not including null terminator + else if (destChars) { + dest[destChars - 1] = 0; // null terminate the destination buffer + return destChars - 1; + } + else + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Sync.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Sync.cpp new file mode 100644 index 00000000..2b7e8d85 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Sync.cpp @@ -0,0 +1,359 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Sync.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + + +/**************************************************************************** +* +* Spin lock functions +* +***/ + +//=========================================================================== +static inline void EnterSpinLock (long * spinLock) { + for (;;) + if (*spinLock < 0) + if (!InterlockedIncrement(spinLock)) + return; + else + InterlockedDecrement(spinLock); +} + +//=========================================================================== +static inline void LeaveSpinLock (long * spinLock) { + InterlockedDecrement(spinLock); +} + + +/**************************************************************************** +* +* CLockWaitSet / CLockWaitSetAllocator +* +***/ + +class CLockWaitSet { +private: + unsigned m_refCount; + HANDLE m_waitEvent; + +public: + LINK(CLockWaitSet) link; + + inline CLockWaitSet (); + inline ~CLockWaitSet (); + inline void DecRef (); + inline void IncRef (); + inline void Signal (); + inline void Wait (); +}; + +class CLockWaitSetAllocator { +private: + CLockWaitSet m_array[256]; + CLockWaitSetAllocator * m_prev; + LISTDECL(CLockWaitSet, link) m_spareList; + LISTDECL(CLockWaitSet, link) m_usedList; + + static CLockWaitSetAllocator * s_allocator; + static long s_spinLock; + +public: + CLockWaitSetAllocator (CLockWaitSetAllocator * prev); + ~CLockWaitSetAllocator (); + static CLockWaitSet * Alloc (); + static void Free (CLockWaitSet * waitSet); + static void __cdecl Shutdown (); +}; + +CLockWaitSetAllocator * CLockWaitSetAllocator::s_allocator; +long CLockWaitSetAllocator::s_spinLock = -1; + +//=========================================================================== +CLockWaitSet::CLockWaitSet () { + m_refCount = 0; + m_waitEvent = CreateEvent(nil, true, false, nil); +} + +//=========================================================================== +CLockWaitSet::~CLockWaitSet () { + ASSERT(!m_refCount); + CloseHandle(m_waitEvent); + m_waitEvent = 0; +} + +//=========================================================================== +void CLockWaitSet::DecRef () { + ASSERT(m_refCount); + if (!--m_refCount) { + ResetEvent(m_waitEvent); + CLockWaitSetAllocator::Free(this); + } +} + +//=========================================================================== +void CLockWaitSet::IncRef () { + ++m_refCount; +} + +//=========================================================================== +void CLockWaitSet::Signal () { + ASSERT(m_refCount); + SetEvent(m_waitEvent); +} + +//=========================================================================== +void CLockWaitSet::Wait () { + ASSERT(m_refCount); + WaitForSingleObject(m_waitEvent, INFINITE); +} + +//=========================================================================== +CLockWaitSetAllocator::CLockWaitSetAllocator (CLockWaitSetAllocator * prev) { + m_prev = prev; + if (prev) { + m_spareList.Link(&prev->m_spareList); + m_usedList.Link(&prev->m_usedList); + } + for (unsigned index = arrsize(m_array); index--; ) + m_spareList.Link(&m_array[index]); +} + +//=========================================================================== +CLockWaitSetAllocator::~CLockWaitSetAllocator () { + DEL(m_prev); +} + +//=========================================================================== +CLockWaitSet * CLockWaitSetAllocator::Alloc () { + EnterSpinLock(&s_spinLock); + + // If there is no active allocator or if the active allocator is full, + // create a new one + if (!s_allocator || !s_allocator->m_spareList.Head()) { + if (!s_allocator) + atexit(Shutdown); + s_allocator = NEW(CLockWaitSetAllocator)(s_allocator); + } + + // Get an available wait set from the active allocator + CLockWaitSet * waitSet = s_allocator->m_spareList.Head(); + s_allocator->m_usedList.Link(waitSet); + + LeaveSpinLock(&s_spinLock); + return waitSet; +} + +//=========================================================================== +void CLockWaitSetAllocator::Free (CLockWaitSet * waitSet) { + EnterSpinLock(&s_spinLock); + + // Return this wait set to the active allocator's spare list + ASSERT(s_allocator); + s_allocator->m_spareList.Link(waitSet); + + LeaveSpinLock(&s_spinLock); +} + +//=========================================================================== +void CLockWaitSetAllocator::Shutdown () { + EnterSpinLock(&s_spinLock); + + // Free all allocators + while (s_allocator) { + CLockWaitSetAllocator * prev = s_allocator->m_prev; + DEL(s_allocator); + s_allocator = prev; + } + + LeaveSpinLock(&s_spinLock); +} + + +/**************************************************************************** +* +* CLock +* +***/ + +//=========================================================================== +CLock::CLock () { + m_waitSet = nil; + m_spinLock = -1; + m_readerCount = 0; + m_writerCount = 0; +} + +//=========================================================================== +CLock::~CLock () { + ASSERT(!m_waitSet); + ASSERT(m_spinLock == -1); + ASSERT(!m_readerCount); + ASSERT(!m_writerCount); +} + +//=========================================================================== +void CLock::EnterRead () { + EnterSpinLock(&m_spinLock); + for (;;) { + + // If there are no writers, claim this lock for reading + if (!m_writerCount) { + ++m_readerCount; + break; + } + + // Otherwise, wait until the existing writer releases the lock + CLockWaitSet * waitSet = m_waitSet = (m_waitSet ? m_waitSet : CLockWaitSetAllocator::Alloc()); + waitSet->IncRef(); + LeaveSpinLock(&m_spinLock); + waitSet->Wait(); + EnterSpinLock(&m_spinLock); + waitSet->DecRef(); + + } + LeaveSpinLock(&m_spinLock); +} + +//=========================================================================== +void CLock::EnterWrite () { + EnterSpinLock(&m_spinLock); + for (;;) { + + // If there are no readers or writers, claim this lock for writing + if (!m_readerCount && !m_writerCount) { + ++m_writerCount; + break; + } + + // Otherwise, wait until the existing writer or all existing readers + // release the lock + CLockWaitSet * waitSet = m_waitSet = (m_waitSet ? m_waitSet : CLockWaitSetAllocator::Alloc()); + waitSet->IncRef(); + LeaveSpinLock(&m_spinLock); + waitSet->Wait(); + EnterSpinLock(&m_spinLock); + waitSet->DecRef(); + + } + LeaveSpinLock(&m_spinLock); +} + +//=========================================================================== +void CLock::LeaveRead () { + EnterSpinLock(&m_spinLock); + + // If this is the last reader, signal waiting threads to try claiming + // the lock again + ASSERT(m_readerCount); + if (!--m_readerCount) + if (m_waitSet) { + m_waitSet->Signal(); + m_waitSet = nil; + } + + LeaveSpinLock(&m_spinLock); +} + +//=========================================================================== +void CLock::LeaveWrite () { + EnterSpinLock(&m_spinLock); + + // This is the last writer. Signal waiting threads to try claiming the + // lock again. + ASSERT(m_writerCount == 1); + --m_writerCount; + if (m_waitSet) { + m_waitSet->Signal(); + m_waitSet = nil; + } + + LeaveSpinLock(&m_spinLock); +} + + +/***************************************************************************** +* +* CEvent +* +***/ + +//============================================================================ +CEvent::CEvent ( + ECEventResetBehavior resetType, + bool initialSet +) { + m_handle = CreateEvent( + nil, // security attributes + (resetType == kEventManualReset) ? true : false, + initialSet, + nil // name + ); +} + +//============================================================================ +CEvent::~CEvent () { + (void) CloseHandle(m_handle); +} + +//============================================================================ +void CEvent::Signal () { + SetEvent(m_handle); +} + +//============================================================================ +void CEvent::Reset () { + ResetEvent(m_handle); +} + +//============================================================================ +bool CEvent::Wait (unsigned waitMs) { + ThreadAssertCanBlock(__FILE__, __LINE__); + return WaitForSingleObject(m_handle, waitMs) == WAIT_OBJECT_0; +} + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +long AtomicAdd (long * value, long increment) { + return InterlockedExchangeAdd(value, increment); +} + +//=========================================================================== +long AtomicSet (long * value, long set) { + return InterlockedExchange(value, set); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Time.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Time.cpp new file mode 100644 index 00000000..6b2dfc7a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Time.cpp @@ -0,0 +1,146 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Time.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static void FormatTime ( + qword time, + wchar const dateFmt[], + wchar const timeFmt[], + unsigned chars, + wchar * buffer +) { + COMPILER_ASSERT(sizeof(FILETIME) == sizeof(qword)); + + SYSTEMTIME sysTime; + FileTimeToSystemTime((FILETIME *)&time, &sysTime); + + unsigned offset = GetDateFormatW( + LOCALE_SYSTEM_DEFAULT, + 0, + &sysTime, + dateFmt, + buffer, + chars + ); + + if (timeFmt) { + // if we printed any characters, move offset back to overwrite the string terminator + if (offset) + --offset; + + offset += GetTimeFormatW( + LOCALE_SYSTEM_DEFAULT, + 0, + &sysTime, + timeFmt, + buffer + offset, + chars - offset + ); + } + + // if we didn't print any characters, NULL terminate the buffer + if (!offset && chars) + buffer[0] = 0; +} + + +/***************************************************************************** +* +* Exported functions +* +***/ + +#ifdef HS_BUILD_FOR_WIN32 + +//=========================================================================== +void TimeGetDesc ( + qword time, + TimeDesc * desc +) { + ASSERT(desc); + + SYSTEMTIME sysTime; + COMPILER_ASSERT(sizeof(qword) == sizeof(FILETIME)); + FileTimeToSystemTime((FILETIME *) &time, &sysTime); + + desc->year = sysTime.wYear; + desc->month = sysTime.wMonth; + desc->day = sysTime.wDay; + desc->dayOfWeek = sysTime.wDayOfWeek; + desc->hour = sysTime.wHour; + desc->minute = sysTime.wMinute; + desc->second = sysTime.wSecond; +} + +//============================================================================ +qword TimeGetTime () { + qword time; + COMPILER_ASSERT(sizeof(qword) == sizeof(FILETIME)); + GetSystemTimeAsFileTime((FILETIME *) &time); + return time; +} + +//============================================================================ +qword TimeGetLocalTime () { + qword time; + COMPILER_ASSERT(sizeof(qword) == sizeof(FILETIME)); + GetSystemTimeAsFileTime((FILETIME *) &time); + FileTimeToLocalFileTime((FILETIME *) &time, (FILETIME *) &time); + return time; +} + +//============================================================================ +void TimePrettyPrint ( + qword time, + unsigned chars, + wchar * buffer +) { + FormatTime( + time, + L"ddd MMM dd',' yyyy ", + L"hh':'mm':'ss tt", + chars, + buffer + ); +} + + +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Uuid.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Uuid.cpp new file mode 100644 index 00000000..b16b3714 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Uuid.cpp @@ -0,0 +1,171 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Uuid.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +#if 0 + +COMPILER_ASSERT(sizeof(Uuid) >= sizeof(GUID)); + +void Uuid::Clear() +{ + UuidCreateNil( (GUID *)this ); +} + +int Uuid::CompareTo( const Uuid * v ) const +{ + RPC_STATUS s; + return UuidCompare( (GUID *)this, (GUID *)v, &s ); +} + +bool Uuid::IsEqualTo( const Uuid * v ) const +{ + return ( CompareTo( v )==0 ); +} + +void Uuid::CopyFrom( const Uuid * v ) +{ + memcpy( (void*)fData, (const void*)v->fData, sizeof(fData) ); +} + +bool Uuid::IsNull() const +{ + RPC_STATUS s; + return 1 == UuidIsNil( (GUID *)this, &s ); +} + +bool Uuid::FromString( const char * str ) +{ + Clear(); + if ( !str ) + return false; + return RPC_S_OK == UuidFromString( (unsigned char *)str, (GUID *)this ); +} + +bool Uuid::ToString( std::string & out ) const +{ + out = ""; + unsigned char * ubuf; + RPC_STATUS s; + s = UuidToString( (GUID *) this, &ubuf ); + bool success = ( s==RPC_S_OK ); + if ( success ) + out = (char*)ubuf; + RpcStringFree( &ubuf ); + return success; +} + +// static +Uuid Uuid::Generate() +{ + Uuid result; + UuidCreate( (GUID *)&result ); + return result; +} + +#endif + + +#ifdef HS_BUILD_FOR_WIN32 + +/***************************************************************************** +* +* Exports +* +***/ + +COMPILER_ASSERT(sizeof(Uuid) >= sizeof(GUID)); + +//============================================================================ +Uuid GuidGenerate () { + Uuid result; + UuidCreate( (GUID *)&result ); + return result; +} + +//============================================================================ +void GuidClear (Uuid * uuid) { + UuidCreateNil((GUID *)uuid); +} + +//============================================================================ +bool GuidFromString (const wchar str[], Uuid * uuid) { + ASSERT(uuid); + COMPILER_ASSERT(sizeof(wchar) == sizeof(unsigned short)); + return RPC_S_OK == UuidFromStringW((unsigned short *) str, (GUID *) uuid); +} + +//============================================================================ +bool GuidFromString (const char str[], Uuid * uuid) { + ASSERT(uuid); + return RPC_S_OK == UuidFromStringA((unsigned char *) str, (GUID *) uuid); +} + +//============================================================================ +int GuidCompare (const Uuid & a, const Uuid & b) { + RPC_STATUS s; + return UuidCompare((GUID *)&a, (GUID *)&b, &s); +} + +//============================================================================ +bool GuidIsNil (const Uuid & uuid) { + RPC_STATUS s; + return 1 == UuidIsNil((GUID *)&uuid, &s ); +} + +//============================================================================ +const wchar * GuidToString (const Uuid & uuid, wchar * dst, unsigned chars) { + wchar * src; + RPC_STATUS s; + s = UuidToStringW( (GUID *) &uuid, &src ); + if (RPC_S_OK == s) + StrCopy(dst, src, chars); + else + StrCopy(dst, L"", chars); + RpcStringFreeW(&src); + return dst; +} + +//============================================================================ +const char * GuidToString (const Uuid & uuid, char * dst, unsigned chars) { + byte * src; + RPC_STATUS s; + s = UuidToStringA( (GUID *) &uuid, &src ); + if (RPC_S_OK == s) + StrCopy(dst, (char *)src, chars); + else + StrCopy(dst, "", chars); + RpcStringFreeA(&src); + return dst; +} + +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAddr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAddr.cpp new file mode 100644 index 00000000..92fe721b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAddr.cpp @@ -0,0 +1,80 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAddr.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* CNetAddressHash +* +***/ + +//============================================================================ +CNetAddressHash::CNetAddressHash ( + const NetAddress & addr +) : m_addr(addr) +, m_equals(nil) +{ } + +//============================================================================ +CNetAddressHash::CNetAddressHash ( + const NetAddress & addr, + FNetAddressEqualityProc equals +) : m_addr(addr) +, m_equals(equals) +{ } + +//============================================================================ +bool CNetAddressHash::operator== (const CNetAddressHash & rhs) const { + ASSERT(m_equals); + return m_equals(m_addr, rhs.m_addr); +} + +//============================================================================ +unsigned CNetAddressHash::GetHash () const { + return NetAddressHash(m_addr); +} + +//============================================================================ +const NetAddress & CNetAddressHash::GetAddr () const { + return m_addr; +} + + +/***************************************************************************** +* +* Exported data +* +***/ + +NetAddress kNilNetAddress; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAddr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAddr.h new file mode 100644 index 00000000..3a33b111 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAddr.h @@ -0,0 +1,154 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAddr.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTADDR_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAddr.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTADDR_H + + +/***************************************************************************** +* +* Types and constants +* +***/ + + +struct NetAddress { + byte data[24]; +}; + +typedef unsigned NetAddressNode; + + +extern NetAddress kNilNetAddress; + +typedef bool (*FNetAddressEqualityProc)( + const NetAddress & a1, + const NetAddress & a2 +); + + +class CNetAddressHash { + NetAddress m_addr; + FNetAddressEqualityProc m_equals; +public: + CNetAddressHash ( + const NetAddress & addr + ); + CNetAddressHash ( + const NetAddress & addr, + FNetAddressEqualityProc equals + // Useful values for 'equals': + // NetAddressEqual --> address node and port numbers match + // NetAddressSameSystem --> address node numbers match + ); + void operator= (const CNetAddressHash & rhs) const; // not impl. + bool operator== (const CNetAddressHash & rhs) const; + unsigned GetHash () const; + const NetAddress & GetAddr () const; +}; + + +/***************************************************************************** +* +* Functions +* +***/ + +enum ENetAddressFormat { + kNetAddressFormatNodeNumber, + kNetAddressFormatAll, + kNumNetAddressFormats +}; + +unsigned NetAddressHash (const NetAddress & addr); + +int NetAddressCompare (const NetAddress & a1, const NetAddress & a2); +bool NetAddressSameSystem (const NetAddress & a1, const NetAddress & a2); +inline bool NetAddressEqual (const NetAddress & a1, const NetAddress & a2) { + return NetAddressCompare(a1, a2) == 0; +} + +void NetAddressToString ( + const NetAddress & addr, + wchar * str, + unsigned chars, + ENetAddressFormat format +); + +// 'str' must be in the form of a dotted IP address (IPv4 or IPv6) +// - names which require DNS lookup will cause the function to return false +bool NetAddressFromString ( + NetAddress * addr, + const wchar str[], + unsigned defaultPort +); + +unsigned NetAddressGetPort ( + const NetAddress & addr +); +void NetAddressSetPort ( + unsigned port, + NetAddress * addr +); + +void NetAddressNodeToString ( + NetAddressNode node, + wchar * str, + unsigned chars +); +NetAddressNode NetAddressNodeFromString ( + const wchar string[], + const wchar * endPtr[] +); + +NetAddressNode NetAddressGetNode ( + const NetAddress & addr +); +void NetAddressFromNode ( + NetAddressNode node, + unsigned port, + NetAddress * addr +); + +void NetAddressGetLoopback ( + unsigned port, + NetAddress * addr +); + +// Returns number of addresses set, which is guaranteed to be non-zero. +// Furthermore, it sorts the addresses so that loopback and NAT addresses +// are at the end of the array, and "real" addresses are at the beginning. +unsigned NetAddressGetLocal ( + unsigned count, + NetAddressNode addresses[] +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAllIncludes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAllIncludes.h new file mode 100644 index 00000000..9004f2cf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAllIncludes.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtAllIncludes.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTALLINCLUDES_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTALLINCLUDES_H + + +#include "pnUtCoreLib.h" // must be first in list +#include "pnUtPragma.h" +#include "pnUtAddr.h" +#include "pnUtUuid.h" +#include "pnUtMath.h" +#include "pnUtSort.h" +#include "pnUtArray.h" +#include "pnUtList.h" +#include "pnUtHash.h" +#include "pnUtPriQ.h" +#include "pnUtSync.h" +#include "pnUtTime.h" +#include "pnUtTls.h" +#include "pnUtStr.h" +#include "pnUtRef.h" +#include "pnUtPath.h" +#include "pnUtBigNum.h" +#include "pnUtCmd.h" +#include "pnUtMisc.h" +#include "pnUtCrypt.h" +#include "pnUtEndian.h" +#include "pnUtSpareList.h" +#include "pnUtSubst.h" +#include "pnUtRand.h" +#include "pnUtBase64.h" +#include "pnUtSkipList.h" + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTALLINCLUDES_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtArray.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtArray.cpp new file mode 100644 index 00000000..521072e8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtArray.cpp @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtArray.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/**************************************************************************** +* +* CBaseArray +* +***/ + +//=========================================================================== +unsigned CBaseArray::CalcAllocGrowth (unsigned newAlloc, unsigned oldAlloc, unsigned * chunkSize) { + + // If this is the initial allocation, or if the new allocation is more + // than twice as big as the old allocation and larger than the chunk + // size, then allocate exactly the amount of memory requested + if (!oldAlloc || (newAlloc >= max(2 * oldAlloc, *chunkSize))) + return newAlloc; + + // Otherwise, allocate memory beyond what was requested in preparation + // for future requests, so that we can reduce the time spent performing + // memory management + + // For small allocations, double the size of the buffer each time + if (newAlloc < *chunkSize) + return max(newAlloc, 2 * oldAlloc); + + // For larger allocations, grow by the chunk size each time + if (oldAlloc + *chunkSize > newAlloc) { + + // If the application appears to be growing the array a chunk size + // at a time and has allocated at least 16 chunks, double the chunk + // size + if (newAlloc >= 16 * *chunkSize) + *chunkSize *= 2; + + return oldAlloc + *chunkSize; + } + unsigned remainder = newAlloc % *chunkSize; + if (remainder) + return newAlloc + *chunkSize - remainder; + else + return newAlloc; + +} + +//=========================================================================== +void * CBaseArray::ReallocPtr (void * ptr, unsigned bytes) { + ref(ptr); + void * newPtr = nil; + if (bytes) { + newPtr = ALLOCFLAGS(bytes, ARR_MEMORY_FLAGS); + } + return newPtr; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtArray.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtArray.h new file mode 100644 index 00000000..02891106 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtArray.h @@ -0,0 +1,1071 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtArray.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTARRAY_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtArray.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTARRAY_H + + +/**************************************************************************** +* +* Macros +* +***/ + +#define ARRAY(type) TArray< type, TArrayCopyBits< type > > +#define ARRAYOBJ(type) TArray< type, TArrayCopyObject< type > > +#define FARRAY(type) TFArray< type, TArrayCopyBits< type > > +#define FARRAYOBJ(type) TFArray< type, TArrayCopyObject< type > > + +#define SORTARRAYFIELD(type, keyType, field) TSortArray< type, TArrayCopyBits< type >, keyType, offsetof(type, field)> +#define SORTARRAYFIELDOBJ(type, keyType, field) TSortArray< type, TArrayCopyObject< type >, keyType, offsetof(type, field)> +#define SORTARRAYTYPE(type) TSortArray< type, TArrayCopyBits< type >, type, 0> +#define SORTARRAYTYPEOBJ(type) TSortArray< type, TArrayCopyObject< type >, type, 0> + +#define ARR_MEMORY_FLAGS 0 /*| kMemIgnoreBlock*/ + + +/**************************************************************************** +* +* CBuffer +* +***/ + +template +class TBuffer { + +protected: + T * m_data; + +public: + inline TBuffer (); + inline TBuffer (unsigned count); + inline TBuffer (const void * source, unsigned count); + inline TBuffer (const TBuffer & source); + inline ~TBuffer (); + inline TBuffer & operator= (const TBuffer & source); + inline bool operator== (const TBuffer & source) const; + inline T & operator[] (unsigned index); + inline T operator[] (unsigned index) const; + inline void Attach (T * source, unsigned count); + inline unsigned Bytes () const; + inline void Clear (); + inline unsigned Count () const; + inline T * Detach (); + inline void Fill (byte value); + inline T * Ptr (); + inline const T * Ptr () const; + inline void Set (const T * source, unsigned count); + inline void SetBytes (unsigned bytes); + inline void SetCount (unsigned count); + inline void Zero (); + +}; + +//=========================================================================== +template +TBuffer::TBuffer () { + m_data = nil; +} + +//=========================================================================== +template +TBuffer::TBuffer (unsigned count) { + m_data = nil; + SetCount(count); +} + +//=========================================================================== +template +TBuffer::TBuffer (const void * source, unsigned count) { + m_data = nil; + SetCount(count); + memcpy(m_data, source, count * sizeof(T)); +} + +//=========================================================================== +template +TBuffer::TBuffer (const TBuffer & source) { + m_data = nil; + unsigned bytes = source.Bytes(); + SetBytes(bytes); + if (bytes) + memcpy(m_data, source.m_data, bytes); +} + +//=========================================================================== +template +TBuffer::~TBuffer () { + if (m_data) + FREEFLAGS(m_data, ARR_MEMORY_FLAGS); +} + +//=========================================================================== +template +TBuffer & TBuffer::operator= (const TBuffer & source) { + unsigned newBytes = source.Bytes(); + if (newBytes != Bytes()) + SetBytes(newBytes); + if (&source != this) + memcpy(m_data, source.m_data, newBytes); + return *this; +} + +//=========================================================================== +template +bool TBuffer::operator== (const TBuffer & source) const { + unsigned size = MemSize(m_data); + return (size == MemSize(source.m_data)) && !MemCmp(m_data, source.m_data, size); +} + +//=========================================================================== +template +T & TBuffer::operator[] (unsigned index) { + ASSERT(index < Count()); + return m_data[index]; +} + +//=========================================================================== +template +T TBuffer::operator[] (unsigned index) const { + ASSERT(index < Count()); + return m_data[index]; +} + +//=========================================================================== +template +void TBuffer::Attach (T * source, unsigned count) { + if (m_data) + FREEFLAGS(m_data, ARR_MEMORY_FLAGS); + m_data = source; + ASSERT(MemSize(source) >= count * sizeof(T)); +} + +//=========================================================================== +template +unsigned TBuffer::Bytes () const { + return m_data ? MemSize(m_data) : 0; +} + +//=========================================================================== +template +void TBuffer::Clear () { + if (m_data) { + FREEFLAGS(m_data, ARR_MEMORY_FLAGS); + m_data = nil; + } +} + +//=========================================================================== +template +unsigned TBuffer::Count () const { + return m_data ? (MemSize(m_data) / sizeof(T)) : 0; +} + +//=========================================================================== +template +T * TBuffer::Detach () { + T * result = m_data; + m_data = nil; + return result; +} + +//=========================================================================== +template +void TBuffer::Fill (byte value) { + if (m_data) + MemSet(m_data, value, Bytes()); +} + +//=========================================================================== +template +T * TBuffer::Ptr () { + return m_data; +} + +//=========================================================================== +template +const T * TBuffer::Ptr () const { + return m_data; +} + +//=========================================================================== +template +void TBuffer::Set (const T * source, unsigned count) { + SetCount(count); + memcpy(m_data, source, count * sizeof(T)); +} + +//=========================================================================== +template +void TBuffer::SetBytes (unsigned bytes) { + if (bytes) + m_data = (T *)REALLOCFLAGS(m_data, bytes, ARR_MEMORY_FLAGS); + else if (m_data) { + FREEFLAGS(m_data, ARR_MEMORY_FLAGS); + m_data = nil; + } +} + +//=========================================================================== +template +void TBuffer::SetCount (unsigned count) { + SetBytes(count * sizeof(T)); +} + +//=========================================================================== +template +void TBuffer::Zero () { + if (m_data) + MemZero(m_data, Bytes()); +} + +typedef TBuffer CBuffer; + + +/**************************************************************************** +* +* CBaseArray +* +***/ + +class CBaseArray { +protected: + unsigned CalcAllocGrowth (unsigned newAlloc, unsigned oldAlloc, unsigned * chunkSize); + void * ReallocPtr (void * ptr, unsigned bytes); +}; + + +/**************************************************************************** +* +* TArrayCopyBits +* +***/ + +template +class TArrayCopyBits { +public: + inline static void Assign (T * dest, const T source[], unsigned count); + inline static void Construct (T * dest) { ref(dest); } + inline static void Construct (T * dest, unsigned count) { ref(dest); ref(count); } + inline static void CopyConstruct (T * dest, const T & source); + inline static void CopyConstruct (T * dest, const T source[], unsigned count); + inline static void Destruct (T * dest) { ref(dest); } + inline static void Destruct (T * dest, unsigned count) { ref(dest); ref(count); } +}; + +//=========================================================================== +template +void TArrayCopyBits::Assign (T * dest, const T source[], unsigned count) { + MemMove(dest, source, count * sizeof(T)); +} + +//=========================================================================== +template +void TArrayCopyBits::CopyConstruct (T * dest, const T & source) { + *dest = source; +} + +//=========================================================================== +template +void TArrayCopyBits::CopyConstruct (T * dest, const T source[], unsigned count) { + ASSERT((dest + count <= source) || (source + count <= dest)); + memcpy(dest, source, count * sizeof(T)); +} + + +/**************************************************************************** +* +* TArrayCopyObject +* +***/ + +template +class TArrayCopyObject { +public: + inline static void Assign (T * dest, const T source[], unsigned count); + inline static void Construct (T * dest); + inline static void Construct (T * dest, unsigned count); + inline static void CopyConstruct (T * dest, const T & source); + inline static void CopyConstruct (T * dest, const T source[], unsigned count); + inline static void Destruct (T * dest); + inline static void Destruct (T * dest, unsigned count); +}; + +//=========================================================================== +template +void TArrayCopyObject::Assign (T * dest, const T source[], unsigned count) { + if (dest > source) + for (unsigned loop = count; loop--; ) + dest[loop] = source[loop]; + else if (dest < source) + for (unsigned loop = 0; loop < count; ++loop) + dest[loop] = source[loop]; +} + +//=========================================================================== +template +void TArrayCopyObject::Construct (T * dest) { + new(dest) T; +} + +//=========================================================================== +template +void TArrayCopyObject::Construct (T * dest, unsigned count) { + for (unsigned loop = 0; loop < count; ++loop) + new(&dest[loop]) T; +} + +//=========================================================================== +template +void TArrayCopyObject::CopyConstruct (T * dest, const T & source) { + new(dest) T(source); +} + +//=========================================================================== +template +void TArrayCopyObject::CopyConstruct (T * dest, const T source[], unsigned count) { + ASSERT((dest + count <= source) || (source + count <= dest)); + for (unsigned loop = 0; loop < count; ++loop) + new(&dest[loop]) T(source[loop]); +} + +//=========================================================================== +template +void TArrayCopyObject::Destruct (T * dest) { + ref(dest); + dest->~T(); +} + +//=========================================================================== +template +void TArrayCopyObject::Destruct (T * dest, unsigned count) { + ref(dest); + for (unsigned loop = count; loop--; ) + dest[loop].~T(); +} + + +/**************************************************************************** +* +* TFArray +* +***/ + +template +class TFArray : protected CBaseArray { +protected: + T * m_data; + unsigned m_alloc; + unsigned m_count; + + inline void AdjustSize (unsigned newAlloc, unsigned newCount); + +public: + inline TFArray (); + inline TFArray (unsigned count); + inline TFArray (const T * source, unsigned count); + inline TFArray (const TFArray & source); + inline ~TFArray (); + inline TFArray & operator= (const TFArray & source); + inline bool operator== (const TFArray & source) const; + inline T & operator[] (unsigned index); + inline const T & operator[] (unsigned index) const; + inline void Attach (T * source, unsigned count); + inline void AttachTemp (T * source, unsigned count); + inline unsigned Bytes () const; + inline void Clear (); + inline unsigned Count () const; + inline T * Detach (); + inline void Fill (byte value); + inline T * Ptr (); + inline const T * Ptr () const; + inline void Set (const T * source, unsigned count); + inline void SetArray (const TFArray & source); + inline void SetCount (unsigned count); + inline T * Term (); + inline const T * Term () const; + inline T * Top (); + inline const T * Top () const; + inline void Zero (); + inline void ZeroCount (); + inline void ZeroRange (unsigned index, unsigned count); + +}; + +//=========================================================================== +template +TFArray::TFArray () { + m_alloc = 0; + m_count = 0; + m_data = nil; +} + +//=========================================================================== +template +TFArray::TFArray (unsigned count) { + m_alloc = m_count = count; + if (count) { + m_data = (T *)ALLOCFLAGS(count * sizeof(T), ARR_MEMORY_FLAGS); + C::Construct(m_data, count); + } + else + m_data = nil; +} + +//=========================================================================== +template +TFArray::TFArray (const T * source, unsigned count) { + m_alloc = m_count = count; + if (count) { + m_data = (T *)ALLOCFLAGS(count * sizeof(T), ARR_MEMORY_FLAGS); + C::CopyConstruct(m_data, source, count); + } + else + m_data = nil; +} + +//=========================================================================== +template +TFArray::TFArray (const TFArray & source) { + m_alloc = m_count = source.m_count; + if (m_count) { + m_data = (T *)ALLOCFLAGS(m_count * sizeof(T), ARR_MEMORY_FLAGS); + C::CopyConstruct(m_data, source.m_data, m_count); + } + else + m_data = nil; +} + +//=========================================================================== +template +TFArray::~TFArray () { + Clear(); +} + +//=========================================================================== +template +TFArray & TFArray::operator= (const TFArray & source) { + if (&source == this) + return *this; + AdjustSize(source.m_count, 0); + C::CopyConstruct(m_data, source.m_data, source.m_count); + m_count = source.m_count; + return *this; +} + +//=========================================================================== +template +inline bool TFArray::operator== (const TFArray & source) const { + if (m_count != source.m_count) + return false; + for (unsigned index = 0; index < m_count; ++index) + if (!((*this)[index] == source[index])) + return false; + return true; +} + +//=========================================================================== +template +T & TFArray::operator[] (unsigned index) { + ASSERT(index < m_count); + return m_data[index]; +} + +//=========================================================================== +template +const T & TFArray::operator[] (unsigned index) const { + ASSERT(index < m_count); + return m_data[index]; +} + +//=========================================================================== +template +void TFArray::AdjustSize (unsigned newAlloc, unsigned newCount) { + + // Destruct elements if the array is shrinking + if (m_count > newCount) { + C::Destruct(m_data + newCount, m_count - newCount); + m_count = newCount; + } + + // Change the memory allocation size if necessary + if (m_alloc != newAlloc) { + T * newData = (T *)ReallocPtr(m_data, newAlloc * sizeof(T)); + if (newData != m_data) { + C::CopyConstruct(newData, m_data, m_count); + C::Destruct(m_data, m_count); + if (m_data) + FREEFLAGS(m_data, ARR_MEMORY_FLAGS); + } + m_alloc = newAlloc; + m_data = newData; + } + + // Construct elements if the array is growing + if (m_count < newCount) { + C::Construct(m_data + m_count, newCount - m_count); + m_count = newCount; + } + +} + +//=========================================================================== +template +void TFArray::Attach (T * source, unsigned count) { + C::Destruct(m_data, m_count); + if (m_data) + FREEFLAGS(m_data, ARR_MEMORY_FLAGS); + m_data = source; + m_alloc = MemSize(source) / sizeof(T); + m_count = count; + ASSERT(m_alloc >= m_count); +} + +//=========================================================================== +template +void TFArray::AttachTemp (T * source, unsigned count) { + C::Destruct(m_data, m_count); + if (m_data) + FREEFLAGS(m_data, ARR_MEMORY_FLAGS); + m_data = source; + m_alloc = count; + m_count = count; +} + +//=========================================================================== +template +unsigned TFArray::Bytes () const { + return m_count * sizeof(T); +} + +//=========================================================================== +template +void TFArray::Clear () { + C::Destruct(m_data, m_count); + if (m_data) + FREEFLAGS(m_data, ARR_MEMORY_FLAGS); + m_data = nil; + m_alloc = m_count = 0; +} + +//=========================================================================== +template +unsigned TFArray::Count () const { + return m_count; +} + +//=========================================================================== +template +T * TFArray::Detach () { + T * result = m_data; + m_data = nil; + m_alloc = 0; + m_count = 0; + return result; +} + +//=========================================================================== +template +void TFArray::Fill (byte value) { + C::Destruct(m_data, m_count); + MemSet(m_data, value, m_count * sizeof(T)); + C::Construct(m_data, m_count); +} + +//=========================================================================== +template +T * TFArray::Ptr () { + return m_data; +} + +//=========================================================================== +template +const T * TFArray::Ptr () const { + return m_data; +} + +//=========================================================================== +template +void TFArray::Set (const T * source, unsigned count) { + AdjustSize(count, 0); + C::CopyConstruct(m_data, source, count); + m_count = count; +} + +//=========================================================================== +template +void TFArray::SetArray (const TFArray & source) { + AdjustSize(source.m_count, 0); + C::CopyConstruct(m_data, source.m_data, source.m_count); + m_count = source.m_count; +} + +//=========================================================================== +template +void TFArray::SetCount (unsigned count) { + AdjustSize(count, count); +} + +//=========================================================================== +template +T * TFArray::Term () { + return m_data + m_count; +} + +//=========================================================================== +template +const T * TFArray::Term () const { + return m_data + m_count; +} + +//=========================================================================== +template +T * TFArray::Top () { + ASSERT(m_count); + return m_data + m_count - 1; +} + +//=========================================================================== +template +const T * TFArray::Top () const { + ASSERT(m_count); + return m_data + m_count - 1; +} + +//=========================================================================== +template +void TFArray::Zero () { + C::Destruct(m_data, m_count); + MemZero(m_data, m_count * sizeof(T)); + C::Construct(m_data, m_count); +} + +//=========================================================================== +template +void TFArray::ZeroCount () { + C::Destruct(m_data, m_count); + m_count = 0; +} + +//=========================================================================== +template +void TFArray::ZeroRange (unsigned index, unsigned count) { + ASSERT(index + count <= m_count); + C::Destruct(m_data + index, count); + MemZero(m_data + index, count * sizeof(T)); + C::Construct(m_data + index, count); +} + + +/**************************************************************************** +* +* TArray +* +***/ + +template +class TArray : public TFArray { + +private: + unsigned m_chunkSize; + + inline void AdjustSizeChunked (unsigned newAlloc, unsigned newCount); + +public: + inline TArray (); + inline TArray (const char file[], int line); + inline TArray (unsigned count); + inline TArray (const T * source, unsigned count); + inline TArray (const TArray & source); + inline TArray & operator= (const TArray & source); + inline unsigned Add (const T & source); + inline unsigned Add (const T * source, unsigned count); + inline unsigned AddArray (const TArray & source); + inline void Copy (unsigned destIndex, unsigned sourceIndex, unsigned count); + inline void DeleteOrdered (unsigned index); + inline void DeleteUnordered (unsigned index); + inline void GrowToCount (unsigned count, bool zero); + inline void GrowToFit (unsigned index, bool zero); + inline void ShrinkBy (unsigned count); + inline void Move (unsigned destIndex, unsigned sourceIndex, unsigned count); + inline T * New (); + inline T * New (unsigned count); + inline void Push (const T & source); + inline T Pop (); + inline void Reserve (unsigned additionalCount); + inline void Set (const T * source, unsigned count); + inline void SetChunkSize (unsigned chunkSize); + inline void SetCount (unsigned count); + inline void SetCountFewer (unsigned count); + inline void Trim (); + +}; + +//=========================================================================== +template +TArray::TArray () : TFArray() { + m_chunkSize = max(1, 256 / sizeof(T)); +} + +//=========================================================================== +template +TArray::TArray (const char file[], int line) : TFArray(file, line) { + m_chunkSize = max(1, 256 / sizeof(T)); +} + +//=========================================================================== +template +TArray::TArray (unsigned count) : TFArray(count) { + m_chunkSize = max(1, 256 / sizeof(T)); +} + +//=========================================================================== +template +TArray::TArray (const T * source, unsigned count) : TFArray(source, count) { + m_chunkSize = max(1, 256 / sizeof(T)); +} + +//=========================================================================== +template +TArray::TArray (const TArray & source) : TFArray(source) { + m_chunkSize = source.m_chunkSize; +} + +//=========================================================================== +template +TArray & TArray::operator= (const TArray & source) { + if (&source == this) + return *this; + m_chunkSize = source.m_chunkSize; + AdjustSize(max(m_alloc, source.m_count), 0); + C::CopyConstruct(m_data, source.m_data, source.m_count); + m_count = source.m_count; + return *this; +} + +//=========================================================================== +template +unsigned TArray::Add (const T & source) { + unsigned index = m_count; + Push(source); + return index; +} + +//=========================================================================== +template +unsigned TArray::Add (const T * source, unsigned count) { + unsigned index = m_count; + AdjustSizeChunked(m_count + count, m_count); + C::CopyConstruct(&m_data[m_count], source, count); + m_count += count; + return index; +} + +//=========================================================================== +template +unsigned TArray::AddArray (const TArray & source) { + unsigned index = m_count; + AdjustSizeChunked(m_count + source.m_count, m_count); + C::CopyConstruct(&m_data[m_count], source.m_data, source.m_count); + m_count += source.m_count; + return index; +} + +//=========================================================================== +template +void TArray::AdjustSizeChunked (unsigned newAlloc, unsigned newCount) { + + // Disallow shrinking the allocation + if (newAlloc <= m_alloc) + newAlloc = m_alloc; + + // Process growing the allocation + else + newAlloc = CalcAllocGrowth(newAlloc, m_alloc, &m_chunkSize); + + // Perform the allocation + AdjustSize(newAlloc, newCount); + +} + +//=========================================================================== +template +void TArray::Copy (unsigned destIndex, unsigned sourceIndex, unsigned count) { + + // Copy the data to the destination + ASSERT(destIndex + count <= m_count); + ASSERT(sourceIndex + count <= m_count); + C::Assign(m_data + destIndex, m_data + sourceIndex, count); + +} + +//=========================================================================== +template +void TArray::DeleteOrdered (unsigned index) { + ASSERT(index < m_count); + if (index + 1 < m_count) + C::Assign(&m_data[index], &m_data[index + 1], m_count - index - 1); + C::Destruct(&m_data[--m_count]); +} + +//=========================================================================== +template +void TArray::DeleteUnordered (unsigned index) { + ASSERT(index < m_count); + if (index + 1 < m_count) + C::Assign(&m_data[index], &m_data[m_count - 1], 1); + C::Destruct(&m_data[--m_count]); +} + +//=========================================================================== +template +void TArray::GrowToCount (unsigned count, bool zero) { + if (count <= m_count) + return; + AdjustSizeChunked(count, m_count); + if (zero) + memset(m_data + m_count, 0, (count - m_count) * sizeof(T)); + C::Construct(m_data + m_count, count - m_count); + m_count = count; +} + +//=========================================================================== +template +void TArray::GrowToFit (unsigned index, bool zero) { + GrowToCount(index + 1, zero); +} + +//=========================================================================== +template +void TArray::ShrinkBy (unsigned count) { + ASSERT(count <= m_count); + C::Destruct(m_data + m_count - count, count); + m_count -= count; +} + +//=========================================================================== +template +void TArray::Move (unsigned destIndex, unsigned sourceIndex, unsigned count) { + + // Copy the data to the destination + ASSERT(destIndex + count <= m_count); + ASSERT(sourceIndex + count <= m_count); + C::Assign(m_data + destIndex, m_data + sourceIndex, count); + + // Remove it from the source + if (destIndex >= sourceIndex) { + C::Destruct(m_data + sourceIndex, min(count, destIndex - sourceIndex)); + C::Construct(m_data + sourceIndex, min(count, destIndex - sourceIndex)); + } + else { + unsigned overlap = (destIndex + count > sourceIndex) ? (destIndex + count - sourceIndex) : 0; + ASSERT(overlap <= count); + C::Destruct(m_data + sourceIndex + overlap, count - overlap); + C::Construct(m_data + sourceIndex + overlap, count - overlap); + } + +} + +//=========================================================================== +template +T * TArray::New () { + AdjustSizeChunked(m_count + 1, m_count + 1); + return &m_data[m_count - 1]; +} + +//=========================================================================== +template +T * TArray::New (unsigned count) { + AdjustSizeChunked(m_count + count, m_count + count); + return &m_data[m_count - count]; +} + +//=========================================================================== +template +void TArray::Push (const T & source) { + AdjustSizeChunked(m_count + 1, m_count); + C::CopyConstruct(&m_data[m_count], source); + ++m_count; +} + +//=========================================================================== +template +T TArray::Pop () { + ASSERT(m_count); + T result = m_data[--m_count]; + C::Destruct(m_data + m_count); + return result; +} + +//=========================================================================== +template +void TArray::Reserve (unsigned additionalCount) { + AdjustSizeChunked(max(m_alloc, m_count + additionalCount), m_count); +} + +//=========================================================================== +template +void TArray::Set (const T * source, unsigned count) { + AdjustSizeChunked(count, 0); + C::CopyConstruct(m_data, source, count); + m_count = count; +} + +//=========================================================================== +template +void TArray::SetChunkSize (unsigned chunkSize) { + m_chunkSize = chunkSize; +} + +//=========================================================================== +template +void TArray::SetCount (unsigned count) { + AdjustSizeChunked(max(m_alloc, count), count); +} + +//=========================================================================== +template +void TArray::SetCountFewer (unsigned count) { + ASSERT(count <= m_count); + C::Destruct(m_data + count, m_count - count); + m_count = count; +} + +//=========================================================================== +template +void TArray::Trim () { + AdjustSize(m_count, m_count); +} + + +/**************************************************************************** +* +* TSortArray +* +***/ + +template +class TSortArray : public TArray { +private: + inline static K & SortKey (T & rec) { return *(K *)((byte *)&rec + OFFSET); } + inline static const K & SortKey (const T & rec) { return *(const K *)((const byte *)&rec + OFFSET); } + +public: + inline bool Delete (K sortKey); + inline T * Find (K sortKey) { unsigned index; return Find(sortKey, &index); } + inline T * Find (K sortKey, unsigned * index); + inline const T * Find (K sortKey) const { unsigned index; return Find(sortKey, &index); } + inline const T * Find (K sortKey, unsigned * index) const; + inline T * Insert (K sortKey, unsigned index); + inline void Sort (); + +}; + +//=========================================================================== +template +bool TSortArray::Delete (K sortKey) { + + // Find the correct position for this key + unsigned index; + BSEARCH(T, Ptr(), Count(), (sortKey > SortKey(elem)), &index); + + // Verify that an entry exists for this key + unsigned count = Count(); + if ((index >= count) || (SortKey((*this)[index]) != sortKey)) + return false; + + // Delete the entry + DeleteOrdered(index); + + return true; +} + +//=========================================================================== +template +T * TSortArray::Find (K sortKey, unsigned * index) { + + // Find the correct position for this key + BSEARCH(T, Ptr(), Count(), (sortKey > SortKey(elem)), index); + if (*index >= Count()) + return nil; + + // Check whether the key is at that position + T & elem = (*this)[*index]; + return (SortKey(elem) == sortKey) ? &elem : nil; + +} + +//=========================================================================== +template +const T * TSortArray::Find (K sortKey, unsigned * index) const { + + // Find the correct position for this key + BSEARCH(T, Ptr(), Count(), (sortKey > SortKey(elem)), index); + if (*index >= Count()) + return nil; + + // Check whether the key is at that position + const T & elem = (*this)[*index]; + return (SortKey(elem) == sortKey) ? &elem : nil; + +} + +//=========================================================================== +template +T * TSortArray::Insert (K sortKey, unsigned index) { + + // Insert a new entry at this position + unsigned count = Count(); + SetCount(count + 1); + if (index < count) + Move(index + 1, index, count - index); + + // Fill in the new entry + T & elem = (*this)[index]; + SortKey(elem) = sortKey; + + return &elem; +} + +//=========================================================================== +template +void TSortArray::Sort () { + T * ptr = Ptr(); + unsigned count = Count(); + QSORT( + T, + ptr, + count, + SortKey(elem1) > SortKey(elem2) + ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBase64.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBase64.cpp new file mode 100644 index 00000000..687f9f15 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBase64.cpp @@ -0,0 +1,170 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBase64.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + +static const char kEncode64[] = { +// 0000000000111111111122222222223333333333444444444455555555556666 +// 0123456789012345678901234567890123456789012345678901234567890123 + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" +}; + +// Note that the decode table contains one special entry: +// The '-' character (0x2d) maps to 63 just like '/' (0x2f) +// so that URLs will work with Base64Decode when we implement them. +#define kTerminator 127 +#define xx kTerminator +static const char kDecode64[] = { +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx, + xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx, + xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,62,xx,63,xx,63, + 52,53,54,55,56,57,58,59,60,61,xx,xx,xx,xx,xx,xx, + xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, + 15,16,17,18,19,20,21,22,23,24,25,xx,xx,xx,xx,xx, + xx,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50,51,xx,xx,xx,xx,xx, + xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx, + xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx, + xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx, + xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx, + xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx, + xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx, + xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx, + xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx, +}; +#undef xx + +static const char kFillchar = '='; + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +unsigned Base64Encode ( + unsigned srcChars, + const byte srcData[], + unsigned dstChars, + char * dstData +) { + ASSERT(srcData); + ASSERT(dstChars >= Base64EncodeSize(srcChars)); + ASSERT(dstData); + + ref(dstChars); + + const char * dstBase = dstData; + const byte * srcTerm = srcData + srcChars; + for (;;) switch (srcTerm - srcData) { + case 0: + *dstData++ = 0; + return dstData - dstBase; + + case 1: + *dstData++ = kEncode64[ ((srcData[0] >> 2) & 0x3f) ]; + *dstData++ = kEncode64[ ((srcData[0] << 4) & 0x30) ]; + *dstData++ = kFillchar; + *dstData++ = kFillchar; + *dstData++ = 0; + return dstData - dstBase; + + case 2: + *dstData++ = kEncode64[ ((srcData[0] >> 2) & 0x3f) ]; + *dstData++ = kEncode64[ ((srcData[0] << 4) & 0x30) + ((srcData[1] >> 4) & 0x0f) ]; + *dstData++ = kEncode64[ ((srcData[1] << 2) & 0x3c) ]; + *dstData++ = kFillchar; + *dstData++ = 0; + return dstData - dstBase; + + default: + *dstData++ = kEncode64[ ((srcData[0] >> 2) & 0x3f) ]; + *dstData++ = kEncode64[ ((srcData[0] << 4) & 0x30) + ((srcData[1] >> 4) & 0x0f) ]; + *dstData++ = kEncode64[ ((srcData[1] << 2) & 0x3c) + ((srcData[2] >> 6) & 0x03) ]; + *dstData++ = kEncode64[ (srcData[2] & 0x3f) ]; + srcData += 3; + break; + } +} + +//============================================================================ +unsigned Base64Decode ( + unsigned srcChars, + const char srcData[], + unsigned dstChars, + byte * dstData +) { + ASSERT(srcData); + ASSERT(dstChars >= Base64DecodeSize(srcChars)); + ASSERT(dstData); + ref(dstChars); + + const byte * dstBase = dstData; + const char * srcTerm = srcData + srcChars; + while (srcTerm - srcData >= 4) { + + *dstData++ = (byte) ( + (kDecode64[srcData[0]] << 2 & 0xfc) + +(kDecode64[srcData[1]] >> 4 & 0x03) + ); + + if (kDecode64[srcData[2]] == kTerminator) + break; + + *dstData++ = (byte) ( + (kDecode64[srcData[1]] << 4 & 0xf0) + +(kDecode64[srcData[2]] >> 2 & 0x0f) + ); + + if (kDecode64[srcData[3]] == kTerminator) + break; + + *dstData++ = (byte) ( + (kDecode64[srcData[2]] << 6 & 0xc0) + +(kDecode64[srcData[3]]) + ); + + srcData += 4; + } + + return dstData - dstBase; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBase64.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBase64.h new file mode 100644 index 00000000..12d5fff5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBase64.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBase64.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTBASE64_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBase64.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTBASE64_H + + + +/***************************************************************************** +* +* Base64 Codec API +* +***/ + +const unsigned kBase64EncodeBlock = 4; +const unsigned kBase64EncodeMultiple = 3; + +inline unsigned Base64EncodeSize (unsigned srcChars) { + return srcChars * kBase64EncodeBlock / kBase64EncodeMultiple + kBase64EncodeBlock; +} +unsigned Base64Encode ( + unsigned srcChars, + const byte srcData[], + unsigned dstChars, + char * dstData +); + +inline unsigned Base64DecodeSize (unsigned srcChars) { + return srcChars * kBase64EncodeMultiple / kBase64EncodeBlock + kBase64EncodeMultiple; +} +unsigned Base64Decode ( + unsigned srcChars, + const char srcData[], + unsigned dstChars, + byte * dstData +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.cpp new file mode 100644 index 00000000..98d55f56 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.cpp @@ -0,0 +1,1382 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/**************************************************************************** +* +* Constants and macros +* +***/ + +const unsigned VAL_BITS = 8 * sizeof(BigNum::Val); +const BigNum::DVal VAL_RANGE = ((BigNum::DVal)1) << VAL_BITS; + +#define LOW(dval) ((Val)(dval)) +#define HIGH(dval) ((Val)((dval) / VAL_RANGE)) +#define PACK(low, high) ((DVal)((high) * VAL_RANGE + (low))) + +#define ALLOC_TEMP(struct, count) \ + (struct).UseTempAlloc((Val *)_alloca((count) * sizeof(Val)), count) + + +/**************************************************************************** +* +* BigNum private methods +* +***/ + +//=========================================================================== +void BigNum::SetVal (unsigned index, Val value) { + ARRAY(Val)::operator[](index) = value; +} + +//=========================================================================== +void BigNum::SetVal (unsigned index, DVal value, Val * carry) { + ARRAY(Val)::operator[](index) = LOW(value); + *carry = HIGH(value); +} + +//=========================================================================== +void BigNum::Trim (unsigned count) { + ASSERT(count <= Count()); + while (count && !ARRAY(Val)::operator[](count - 1)) + --count; + SetCountFewer(count); +} + +//=========================================================================== +BigNum * BigNum::UseTempAlloc (Val * ptr, unsigned count) { + m_isTemp = true; + AttachTemp(ptr, count); + return this; +} + + +/**************************************************************************** +* +* BigNum public methods +* +***/ + +//=========================================================================== +BigNum::BigNum () : + m_isTemp(false) +{ +} + +//=========================================================================== +BigNum::BigNum (const BigNum & a) : + m_isTemp(false) +{ + Set(a); +} + +//=========================================================================== +BigNum::BigNum (unsigned a) : + m_isTemp(false) +{ + Set(a); +} + +//=========================================================================== +BigNum::BigNum (unsigned bytes, const void * data) : + m_isTemp(false) +{ + FromData(bytes, data); +} + +//=========================================================================== +BigNum::BigNum (const wchar str[], Val radix) : + m_isTemp(false) +{ + FromStr(str, radix); +} + +//=========================================================================== +BigNum::~BigNum () { + if (m_isTemp) + Detach(); +} + +//=========================================================================== +void BigNum::Add (const BigNum & a, Val b) { + // this = a + b + + const unsigned count = a.Count(); + GrowToCount(count + 1, true); + unsigned index = 0; + Val carry = b; + for (; index < count; ++index) + SetVal(index, (DVal)((DVal)a[index] + (DVal)carry), &carry); + if (carry) + SetVal(index++, carry); + Trim(index); + +} + +//=========================================================================== +void BigNum::Add (const BigNum & a, const BigNum & b) { + // this = a + b + + const unsigned aCount = a.Count(); + const unsigned bCount = b.Count(); + const unsigned count = aCount + bCount; + GrowToCount(count + 1, true); + unsigned index = 0; + Val carry = 0; + for (; index < count; ++index) { + Val aVal = (index < aCount) ? a[index] : (Val)0; + Val bVal = (index < bCount) ? b[index] : (Val)0; + SetVal(index, (DVal)((DVal)aVal + (DVal)bVal + (DVal)carry), &carry); + } + if (carry) + SetVal(index++, carry); + Trim(index); + +} + +//=========================================================================== +int BigNum::Compare (Val a) const { + // -1 if (this < a) + // 0 if (this == a) + // 1 if (this > a) + + // Handle the case where this number has more digits than the comparand + const unsigned count = Count(); + ASSERT(!count || (*this)[count - 1]); + if (count > 1) + return 1; + + // Handle the case where this number has fewer digits than the comparand + if (!count) + return a ? -1 : 0; + + // Handle the case where both numbers share the same number of digits + Val thisVal = (*this)[0]; + return (thisVal > a) ? 1 : (thisVal < a) ? -1 : 0; + +} + +//=========================================================================== +int BigNum::Compare (const BigNum & a) const { + // -1 if (this < a) + // 0 if (this == a) + // 1 if (this > a) + + // Handle the case where this number has more digits than the comparand + const unsigned thisCount = Count(); + const unsigned compCount = a.Count(); + ASSERT(!thisCount || (*this)[thisCount - 1]); + ASSERT(!compCount || a[compCount - 1]); + if (thisCount > compCount) + return 1; + + // Handle the case where this number has fewer digits than the comparand + if (thisCount < compCount) + return -1; + + // Handle the case where both numbers share the same number of digits + for (unsigned index = thisCount; index--; ) { + Val thisVal = (*this)[index]; + Val compVal = a[index]; + if (thisVal == compVal) + continue; + return (thisVal > compVal) ? 1 : -1; + } + return 0; + +} + +//=========================================================================== +void BigNum::Div (const BigNum & a, Val b, Val * remainder) { + // this = a / b, remainder = a % b + + const unsigned count = a.Count(); + SetCount(count); + *remainder = 0; + for (unsigned index = count; index--; ) { + DVal value = PACK(a[index], *remainder); + SetVal(index, (Val)(value / b)); + *remainder = (Val)(value % b); + } + Trim(count); + +} + +//=========================================================================== +void BigNum::Div (const BigNum & a, const BigNum & b, BigNum * remainder) { + // this = a / b, remainder = a % b + // either this or remainder may be nil + + ASSERT(this != remainder); + + // Check for division by zero + ASSERT(b.Count() && b[b.Count() - 1]); + + // Normalize the operands so that the highest bit is set in the most + // significant word of the denominator + const unsigned shift = 8 * sizeof(Val) - MathHighBitPos(b[b.Count() - 1]) - 1; + BigNum aaBuffer; + BigNum bbBuffer; + BigNum * aa = shift ? ALLOC_TEMP(aaBuffer, a.Count() + 1) : (BigNum *)&a; + BigNum * bb = shift ? ALLOC_TEMP(bbBuffer, b.Count() + 1) : (BigNum *)&b; + if (shift) { + aa->Shl(a, shift); + bb->Shl(b, shift); + } + + // Perform the division + DivNormalized(*aa, *bb, remainder); + + // Denormalize the remainder + if (remainder) + remainder->Shr(*remainder, shift); + +} + +//=========================================================================== +void BigNum::DivNormalized (const BigNum & a, const BigNum & b, BigNum * remainder) { + // this = a / b, remainder = a % b + // either this or remainder may be nil + // high bit of b must be set + + ASSERT(this != remainder); + + // Check for division by zero + ASSERT(b.Count() && b[b.Count() - 1]); + + // Verify that the operands are normalized + ASSERT(MathHighBitPos(b[b.Count() - 1]) == 8 * sizeof(Val) - 1); + + // Handle the case where the denominator is greater than the numerator + if ((b.Count() > a.Count()) || (b.Compare(a) > 0)) { + if (remainder && (remainder != &a)) + remainder->Set(a); + if (this) + ZeroCount(); + return; + } + + // Create a distinct buffer for the denominator if necessary + BigNum denomTemp; + BigNum * denom = ((&b != this) && (&b != remainder)) ? (BigNum *)&b : ALLOC_TEMP(denomTemp, b.Count()); + denom->Set(b); + + // Store the numerator into the remainder buffer + BigNum numerTemp; + BigNum * numer = remainder ? remainder : ALLOC_TEMP(numerTemp, a.Count()); + numer->Set(a); + + // Prepare the destination buffer + const unsigned numerCount = numer->Count(); + const unsigned denomCount = denom->Count(); + if (this) + this->SetCount(numerCount + 1 - denomCount); + + // Calculate the quotient one word at a time + DVal t = (DVal)((DVal)(*denom)[denomCount - 1] + (DVal)1); + for (unsigned quotientIndex = numerCount + 1 - denomCount; quotientIndex--; ) { + + // Calculate the approximate value of the next quotient word, + // erring on the side of underestimation + Val low = (*numer)[quotientIndex + denomCount - 1]; + Val high = (quotientIndex + denomCount < numerCount) ? (*numer)[quotientIndex + denomCount] : (Val)0; + ASSERT(high < t); + Val quotient = (Val)(PACK(low, high) / t); + + // Calculate the product of the denominator and this quotient word + // (using zero for all lower quotient words) and subtract the product + // from the current numerator + if (quotient) { + Val borrow = 0; + Val carry = 0; + for (unsigned denomIndex = 0; denomIndex != denomCount; ++denomIndex) { + DVal product = (DVal)(Mul((*denom)[denomIndex], quotient) + carry); + carry = HIGH(product); + numer->SetVal(quotientIndex + denomIndex, (DVal)((DVal)(*numer)[quotientIndex + denomIndex] - (DVal)LOW(product) - (DVal)borrow), &borrow); + borrow = (Val)((Val)0 - (Val)borrow); + } + if (quotientIndex + denomCount != numerCount) { + numer->SetVal(quotientIndex + denomCount, (DVal)((DVal)(*numer)[quotientIndex + denomIndex] - (DVal)carry - (DVal)borrow), &borrow); + carry = 0; + } + ASSERT(!carry); + ASSERT(!borrow); + } + + // Check whether we underestimated the quotient word, and adjust + // it if necessary + for (;;) { + + // Test whether the current numerator is still greater than or + // equal to the denominator + if ((quotientIndex + denomCount == numerCount) || !(*numer)[quotientIndex + denomCount]) { + bool numerLess = false; + for (unsigned denomIndex = denomCount; !numerLess && denomIndex--; ) { + Val numerVal = (*numer)[quotientIndex + denomIndex]; + Val denomVal = (*denom)[denomIndex]; + numerLess = (numerVal < denomVal); + if (numerVal != denomVal) + break; + } + if (numerLess) + break; + } + + // Increment the quotient by one, and correct the current + // numerator for this adjustment by subtracting the denominator + ++quotient; + Val borrow = 0; + for (unsigned denomIndex = 0; denomIndex != denomCount; ++denomIndex) { + numer->SetVal(quotientIndex + denomIndex, (DVal)((DVal)(*numer)[quotientIndex + denomIndex] - (DVal)(*denom)[denomIndex] - (DVal)borrow), &borrow); + borrow = (Val)((Val)0 - (Val)borrow); + } + if (borrow) + numer->SetVal(quotientIndex + denomCount, (DVal)((DVal)(*numer)[quotientIndex + denomCount] - (DVal)borrow), &borrow); + ASSERT(!borrow); + + } + ASSERT((quotientIndex + denomCount == numerCount) || !(*numer)[quotientIndex + denomCount]); + + // Store the final quotient word + if (this) + this->SetVal(quotientIndex, quotient); + + } + + // The final remainder is the remaining portion of the numerator + if (remainder) { + ASSERT(remainder == numer); + remainder->Trim(denomCount); + } + + // Trim the result + if (this) + this->Trim(numerCount + 1 - denomCount); + +} + +//=========================================================================== +void BigNum::FromData (unsigned bytes, const void * data) { + ASSERT(data || !bytes); + + // Calculate the number of words required to hold the data + unsigned count = (bytes + sizeof(Val) - 1) / sizeof(Val); + SetCount(count); + + // Fill in whole words + unsigned index = 0; + unsigned offset = 0; + for (; offset + sizeof(Val) <= bytes; ++index, offset += sizeof(Val)) + SetVal(index, *(const Val *)((const byte *)data + offset)); + + // Fill in the final partial word + if (offset < bytes) { + Val value = 0; + MemCopy(&value, (const byte *)data + offset, bytes - offset); + SetVal(index, value); + } + +} + +//=========================================================================== +void BigNum::FromStr (const wchar str[], Val radix) { + ASSERT(str); + + // Decode the prefix + if (str[0] == L'0') { + if ((str[1] == L'x') || (str[1] == L'X')) { + str += 2; + if (!radix) + radix = 16; + } + else if ((str[1] >= L'0') && (str[1] <= L'9')) { + str += 1; + if (!radix) + radix = 8; + } + else if (!radix) { + ZeroCount(); + return; + } + } + else if (!radix) + radix = 10; + + // Decode the number + ZeroCount(); + for (; *str; ++str) { + + // Decode the next character + Val value; + if ((*str >= L'0') && (*str <= '9')) + value = (Val)(*str - L'0'); + else if ((*str >= L'a') && (*str <= L'z')) + value = (Val)(*str + 10 - L'a'); + else if ((*str >= L'A') && (*str <= L'Z')) + value = (Val)(*str + 10 - L'A'); + else + break; + if (value >= radix) + break; + + // Apply it to the result + Mul(*this, radix); + Add(*this, value); + + } + +} + +//=========================================================================== +void BigNum::Gcd (const BigNum & a, const BigNum & b) { + + // Allocate working copies of a and b + BigNum aa; + BigNum bb; + unsigned maxCount = max(a.Count(), b.Count()); + ALLOC_TEMP(aa, maxCount + 1); + ALLOC_TEMP(bb, maxCount + 1); + aa.Set(a); + bb.Set(b); + + // Find the greatest common denominator using Euclid's algorithm + Set(bb); + while (aa.Count()) { + Set(aa); + aa.Mod(bb, aa); + bb.Set(*this); + } + +} + +//=========================================================================== +const void * BigNum::GetData (unsigned * bytes) const { + if (bytes) + *bytes = Bytes(); + return Ptr(); +} + +//=========================================================================== +unsigned BigNum::HighBitPos () const { + // returns the position of the highest set bit, or -1 if no bits are set + + for (unsigned index = Count(); index--; ) { + Val val = (*this)[index]; + if (!val) + continue; + return index * 8 * sizeof(Val) + MathHighBitPos(val); + } + + return (unsigned)-1; +} + +//=========================================================================== +bool BigNum::InverseMod (const BigNum & a, const BigNum & b) { + // finds value for this such that (a ^ -1) == (this mod b) + // returns false if a has no inverse modulo b + + // Verify that a and b are nonzero + ASSERT(a.Count()); + ASSERT(b.Count()); + + // Verify that a is less than b + ASSERT(a.Compare(b) < 0); + + // Verify that either a or b is odd. If both are even then they cannot + // possibly be relatively prime, so there cannot be a solution. + if (!(a.IsOdd() || b.IsOdd())) + return false; + + // Allocate buffers for intermediate values + BigNum uArray[3]; + BigNum tArray[3]; + BigNum * u = uArray; + BigNum * t = tArray; + + // Find the inverse using the extended Euclidean algorithm + u[0].SetOne(); + u[1].SetZero(); + u[2].Set(b); + t[0].Set(a); + t[1].Sub(b, 1); + t[2].Set(a); + do { + do { + if (!u[2].IsOdd()) { + if (u[0].IsOdd() || u[1].IsOdd()) { + u[0].Add(u[0], a); + u[1].Add(u[1], b); + } + u[0].Shr(u[0], 1); + u[1].Shr(u[1], 1); + u[2].Shr(u[2], 1); + } + if (!t[2].IsOdd() || (u[2].Compare(t[2]) < 0)) + SWAP(u, t); + } while (!u[2].IsOdd()); + + while ((u[0].Compare(t[0]) < 0) || (u[1].Compare(t[1]) < 0)) { + u[0].Add(u[0], a); + u[1].Add(u[1], b); + } + + u[0].Sub(u[0], t[0]); + u[1].Sub(u[1], t[1]); + u[2].Sub(u[2], t[2]); + } while (t[2].Count()); + + while ((u[0].Compare(a) >= 0) && (u[1].Compare(b) >= 0)) { + u[0].Sub(u[0], a); + u[1].Sub(u[1], b); + } + + // If the greatest common denominator is not one then there is no + // solution + if (u[2].Compare(1)) + return false; + + // Return the solution + Sub(b, u[1]); + return true; + +} + +//=========================================================================== +bool BigNum::IsMultiple (Val a) const { + // returns true if (this % a) == 0 + + DVal remainder = 0; + for (unsigned index = Count(); index--; ) + remainder = (DVal)(PACK((*this)[index], remainder) % a); + + return !remainder; +} + +//=========================================================================== +bool BigNum::IsOdd () const { + // returns true if this is an odd number + + return Count() ? (*this)[0] & 1 : false; +} + +//=========================================================================== +bool BigNum::IsPrime () const { + // returns true if there is a strong likelihood that this is prime + + // Verify that the number is odd, or is exactly equal to two + if (!Count() || (!((*this)[0] & 1) && ((Count() > 1) || ((*this)[0] > 2)))) + return false; + + // Verify that the number is not evenly divisible by a small prime + static const Val smallPrimes[] = {3, 5, 7, 11}; + unsigned loop; + for (loop = 0; loop != arrsize(smallPrimes); ++loop) + if (IsMultiple(smallPrimes[loop])) + return false; + if (Compare(smallPrimes[arrsize(smallPrimes)-1]) <= 0) + return true; + + // Rabin-Miller Test + + // Calculate b, where b is the number of times 2 divides (this - 1) + BigNum this_1; + ALLOC_TEMP(this_1, Count()); + this_1.Sub(*this, 1); + const unsigned b = this_1.LowBitPos(); + + // Calculate m, such that this == 1 + 2 ^ b * m + BigNum m; + ALLOC_TEMP(m, Count()); + m.Shr(this_1, b); + + // For a number of witnesses, test whether the witness demonstrates this + // number to be composite via Fermat's Little Theorem, or has a + // nontrivial square root mod n + static const Val witnesses[] = {3, 5, 7}; + BigNum z; + ALLOC_TEMP(z, 2 * (Count() + 1)); + for (loop = 0; loop != arrsize(witnesses); ++loop) { + + // Initialize z to (witness ^ m % this) + z.PowMod(witnesses[loop], m, *this); + + // This passes the test if (z == 1) + if (!z.Compare(1)) + continue; + + for (unsigned j = 0; z.Compare(this_1); ) { + + // This fails the test if we reach b iterations. + ++j; + if (j == b) + return false; + + // Square z. This fails the test if z mod this equals 1. + z.MulMod(z, z, *this); + if (!z.Compare(1)) + return false; + + } + + } + + return true; +} + +//=========================================================================== +unsigned BigNum::LowBitPos () const { + // returns the position of the lowest set bit, or -1 if no bits are set + + for (unsigned index = 0, count = Count(); index < count; ++index) { + Val val = (*this)[index]; + if (!val) + continue; + for (unsigned bit = 0; ; ++bit) + if (val & (1 << bit)) + return index * 8 * sizeof(Val) + bit; + } + + return (unsigned)-1; +} + +//=========================================================================== +void BigNum::Mod (const BigNum & a, const BigNum & b) { + // this = a % b + + ((BigNum *)nil)->Div(a, b, this); +} + +//=========================================================================== +void BigNum::ModNormalized (const BigNum & a, const BigNum & b) { + // this = a % b + // high bit of b must be set + + ((BigNum *)nil)->DivNormalized(a, b, this); +} + +//=========================================================================== +BigNum::DVal BigNum::Mul (BigNum::Val a, BigNum::Val b) { + // returns a * b + + return (DVal)a * (DVal)b; +} + +//=========================================================================== +void BigNum::Mul (const BigNum & a, Val b) { + // this = a * b + + const unsigned count = a.Count(); + GrowToCount(count + 1, true); + unsigned index = 0; + Val carry = 0; + for (; index < count; ++index) + SetVal(index, (DVal)(Mul(a[index], b) + carry), &carry); + if (carry) + SetVal(index++, carry); + Trim(index); + +} + +//=========================================================================== +void BigNum::Mul (const BigNum & a, const BigNum & b) { + // this = a * b + + const unsigned aCount = a.Count(); + const unsigned bCount = b.Count(); + const unsigned count = aCount + bCount; + SetCount(count); + if (!count) + return; + + // We perform the multiplication from left to right, so that we don't + // overwrite any operand words before they're used in the case that + // the destination is not distinct from either of the operands + SetVal(count - 1, 0); + for (unsigned index = count - 1; index--; ) { + + // Iterate every combination of aIndex + bIndex that adds up to + // index, and sum the products of those words + DVal value = 0; + const unsigned aStart = (index < bCount) ? 0 : (index + 1 - bCount); + const unsigned aTerm = min(index + 1, aCount); + for (unsigned aIndex = aStart; aIndex != aTerm; ++aIndex) { + + // Accumulate the product of this pair of words + value = (DVal)(Mul(a[aIndex], b[index - aIndex]) + value); + + // If the product exceeds the word size, apply carry + Val carry = HIGH(value); + for (unsigned carryIndex = index + 1; carry; ++carryIndex) + SetVal(carryIndex, (DVal)((DVal)(*this)[carryIndex] + (DVal)carry), &carry); + value = LOW(value); + + } + + // Store the sum of products as the final value for index + SetVal(index, LOW(value)); + + } + + Trim(count); + +} + +//=========================================================================== +void BigNum::MulMod (const BigNum & a, const BigNum & b, const BigNum & c) { + // this = a * b % c + + if (this != &c) { + Mul(a, b); + Mod(*this, c); + } + else { + BigNum temp; + ALLOC_TEMP(temp, a.Count() + b.Count()); + temp.Mul(a, b); + Mod(temp, c); + } + +} + +//=========================================================================== +void BigNum::PowMod (Val a, const BigNum & b, const BigNum & c) { + // this = a ^ b % c + + // Verify that b is distinct from this + BigNum bbBuffer; + const BigNum & bb = (&b != this) ? b : bbBuffer; + if (&bb != &b) { + ALLOC_TEMP(bbBuffer, b.Count()); + bbBuffer.Set(b); + } + + // Generate a table which may allow us to process two bits at once + Val aMult[4] = { + 1, + a, + (Val)(a * a), + (Val)(a * a * a) + }; + bool overflow = (aMult[2] < a) || (aMult[3] < a) || (c.Compare(aMult[3]) <= 0); + + // Normalize the denominator so that the high bit is set. The result + // will be built shifted an equivalent amount. + const unsigned shift = 8 * sizeof(Val) - MathHighBitPos(c[c.Count() - 1]) - 1; + BigNum cc; + ALLOC_TEMP(cc, c.Count() + 1); + cc.Shl(c, shift); + + // Perform the exponentiation from left to right two bits at a time + if (!overflow) { + SetBits(shift, 1); + bool anySet = false; + for (unsigned index = bb.Count(); index--; ) + for (unsigned bit = 8 * sizeof(Val); bit; ) { + bit -= 2; + + if (anySet) { + Square(*this); + Shr(*this, shift); + ModNormalized(*this, cc); + Square(*this); + Shr(*this, shift); + ModNormalized(*this, cc); + } + + unsigned entry = (bb[index] >> bit) & 3; + if (entry) { + Mul(*this, aMult[entry]); + ModNormalized(*this, cc); + anySet = true; + } + + } + } + + // Perform the exponentiation from left to right a single bit at a time + else { + SetBits(shift, 1); + bool anySet = false; + for (unsigned index = bb.Count(); index--; ) + for (unsigned bit = 8 * sizeof(Val); bit--; ) { + + if (anySet) { + Square(*this); + ModNormalized(*this, cc); + } + + if (bb[index] & (1 << bit)) { + Mul(*this, a); + ModNormalized(*this, cc); + anySet = true; + } + + } + } + + // Denormalize the result + Shr(*this, shift); + +} + +//=========================================================================== +void BigNum::PowMod (const BigNum & a, const BigNum & b, const BigNum & c) { + // this = a ^ b % c + + // Verify that a and b are distinct from this + BigNum distinctTemp; + const BigNum & aa = (&a != this) ? a : distinctTemp; + const BigNum & bb = (&b != this) ? b : distinctTemp; + if ((&aa != &a) || (&bb != &b)) { + ALLOC_TEMP(distinctTemp, Count()); + distinctTemp.Set(*this); + } + + // Generate a table which will allow us to process two bits at once + BigNum a2; + BigNum a3; + ALLOC_TEMP(a2, 2 * aa.Count() + 1); + ALLOC_TEMP(a3, 3 * aa.Count() + 1); + a2.Mul(aa, aa); + a2.Mod(a2, c); + a3.Mul(aa, a2); + a3.Mod(a3, c); + const BigNum * aMult[] = { + nil, + &aa, + &a2, + &a3 + }; + + // Normalize the denominator so that the high bit is set. The result + // will be built shifted an equivalent amount. + const unsigned shift = 8 * sizeof(Val) - MathHighBitPos(c[c.Count() - 1]) - 1; + BigNum cc; + ALLOC_TEMP(cc, c.Count() + 1); + cc.Shl(c, shift); + + // Perform the exponentiation from left to right two bits at a time + SetBits(shift, 1); + bool anySet = false; + for (unsigned index = bb.Count(); index--; ) + for (unsigned bit = 8 * sizeof(Val); bit; ) { + bit -= 2; + + if (anySet) { + Square(*this); + Shr(*this, shift); + ModNormalized(*this, cc); + Square(*this); + Shr(*this, shift); + ModNormalized(*this, cc); + } + + unsigned entry = (bb[index] >> bit) & 3; + if (entry) { + Mul(*this, *aMult[entry]); + ModNormalized(*this, cc); + anySet = true; + } + + } + + // Denormalize the result + Shr(*this, shift); + +} + +//=========================================================================== +void BigNum::Rand (const BigNum & a, BigNum * seed) { + // this = random number less than a + + ASSERT(seed != &a); + ASSERT(seed != this); + + // Verify that a is distinct from this + BigNum distinctTemp; + const BigNum & aa = (&a != this) ? a : distinctTemp; + if (&aa != &a) { + ALLOC_TEMP(distinctTemp, a.Count()); + distinctTemp.Set(a); + } + + // Count the number of bits in a + unsigned bits = aa.HighBitPos() + 1; + + for (;;) { + + // Generate a random number with the same number of bits as a + Rand(bits, seed); + + // Check whether the number is less than a + if (Compare(aa) < 0) + break; + + } + +} + +//=========================================================================== +void BigNum::Rand (unsigned bits, BigNum * seed) { + // this = random number with bits or fewer bits + + ASSERT(seed != this); + + // Prepare the output buffer + const unsigned count = (bits + 8 * sizeof(Val) - 1) / (8 * sizeof(Val)); + SetCount(count); + if (!count) + return; + + // Prepare the seed + unsigned seedCount = seed->Count(); + if (!seedCount) + seed->SetCount(++seedCount); + unsigned seedIndex = 0; + + // Produce a random number with the correct number of words + for (unsigned index = 0; index < count; ++index) { + + // Read the next word of the seed + dword randValue = (*seed)[seedIndex] ^ ((index == seedIndex) ? 0x075bd924 : 0); + + // Produce one word of randomness, 16 bits at a time + Val value = 0; + for (unsigned bit = 0; bit < 8 * sizeof(Val); bit += 16) { + const dword A = 0xbc8f; + const dword Q = 0xadc8; + const dword R = 0x0d47; + + dword div = randValue / Q; + randValue = A * (randValue - Q * div) - R * div; + randValue &= 0x7fffffff; + + value |= (randValue & 0xffff) << bit; + } + + // Store the random word + SetVal(index, value); + + // Update the seed and move to the seed next word + seed->SetVal(seedIndex, (Val)randValue); + if (++seedIndex >= seedCount) + seedIndex = 0; + + } + + // Mask the final word to contain the correct number of bits + Val mask = (Val)((Val)-1 >> (count * 8 * sizeof(Val) - bits)); + SetVal(count - 1, (Val)((*this)[count - 1] & mask)); + + // Trim the result + Trim(count); + + // Rotate the seed so the next unused seed word will be the first seed + // word used in the next random operation + if (seedIndex) { + BigNum saved; + ALLOC_TEMP(saved, seedCount); + saved.Set(*seed); + + for (unsigned index = 0; index < seedCount; ++index) + (*seed)[index] = saved[(index + seedIndex) % seedCount]; + } + +} + +//=========================================================================== +void BigNum::RandPrime (unsigned bits, BigNum * seed) { + + // Calculate the required number of words to hold the generated number + unsigned count = (bits + 8 * sizeof(Val) - 1) / (8 * sizeof(Val)); + + // For large bit counts, calculate the prime number as 2 * q * n + 1, + // where q is a random prime with fewer bits, and n is a random number + // chosen as follows: + // n >= (2 ^ (bits - 1) - 1) / (2 * q) + // n < (2 ^ bits - 1) / (2 * q) + if (bits > 128) { + + // Choose a prime number q, and multiply it by 2 + BigNum q_2; + ALLOC_TEMP(q_2, count / 2 + 2); + q_2.RandPrime(bits / 2, seed); + q_2.Mul(q_2, 2); + + // Calculate the lower bound + BigNum lowerBound; + ALLOC_TEMP(lowerBound, count + 1); + lowerBound.SetBits(0, bits - 1); + lowerBound.Div(lowerBound, q_2, nil); + + // Calculate the upper bound + BigNum upperBound; + ALLOC_TEMP(upperBound, count + 1); + upperBound.SetBits(0, bits); + upperBound.Div(upperBound, q_2, nil); + + // Calculate the number of bits in the upper bound + unsigned upperBoundBits = upperBound.HighBitPos() + 1; + + for (;;) { + + // Choose a random number between the lower and upper bounds + Rand(upperBoundBits, seed); + if (Compare(upperBound) >= 0) + continue; + if (Compare(lowerBound) < 0) + continue; + + // Calculate 2 * q * n + 1 + Mul(*this, q_2); + Add(*this, 1); + + // Test whether the result is prime + if (IsPrime()) + break; + + } + + } + + // For small bit counts, choose a random number with the requested + // number of bits, then keep incrementing it until we find a prime + else { + + // Define the upper bound for a number with the requested number + // of bits + BigNum bound; + ALLOC_TEMP(bound, count + 1); + bound.SetBits(bits, 1); + + for (;;) { + + // Choose a random number with (bits - 1) bits + Rand(bits - 1, seed); + + // Subtract it from the upper bound to generate a number with + // the high bit set + Sub(bound, *this); + + // Keep incrementing the number until we find a prime + if (!IsOdd()) + Add(*this, 1); + while (!IsPrime()) + Add(*this, 2); + + // If the number reached or exceeded the upper bound, try again + if (Compare(bound) < 0) + break; + + } + + } + +} + +//=========================================================================== +void BigNum::Set (const BigNum & a) { + // this = a + + if (&a == this) + return; + + const unsigned count = a.Count(); + SetCount(count); + for (unsigned index = count; index--; ) + SetVal(index, a[index]); + +} + +//=========================================================================== +void BigNum::Set (unsigned a) { + // this = a + + ZeroCount(); + if (a) + for (unsigned index = 0; ; ++index) { + SetCount(index + 1); + SetVal(index, LOW(a)); + if (a < VAL_RANGE) + break; + a = (unsigned)(a / VAL_RANGE); + } + +} + +//=========================================================================== +void BigNum::SetBits (unsigned setBitsOffset, unsigned setBitsCount) { + // this = binary [1...][0...], where 'setBitsOffset' is the number of + // zero bits and 'setBitsCount' is the number of one bits + + if (!setBitsCount) { + ZeroCount(); + return; + } + + const unsigned setBitsTerm = setBitsOffset + setBitsCount - 1; + const unsigned bitsPerWord = 8 * sizeof(Val); + const unsigned firstSetWord = setBitsOffset / bitsPerWord; + const unsigned lastSetWord = (setBitsOffset + setBitsCount - 1) / bitsPerWord; + Val firstSetMask = (Val)((Val)-1 << (setBitsOffset % bitsPerWord)); + Val lastSetMask = (Val)((Val)-1 >> (bitsPerWord - setBitsTerm % bitsPerWord - 1)); + if (firstSetWord == lastSetWord) + firstSetMask = lastSetMask = (Val)(firstSetMask & lastSetMask); + + SetCount(lastSetWord + 1); + unsigned index = 0; + for (; index < firstSetWord; ++index) + SetVal(index, 0); + SetVal(index++, firstSetMask); + if (firstSetWord == lastSetWord) + return; + for (; index < lastSetWord; ++index) + SetVal(index, (Val)-1); + SetVal(index, lastSetMask); + +} + +//=========================================================================== +void BigNum::SetOne () { + // this = 1 + + SetCount(1); + SetVal(0, 1); +} + +//=========================================================================== +void BigNum::SetZero () { + // this = 0 + + ZeroCount(); +} + +//=========================================================================== +void BigNum::Shl (const BigNum & a, unsigned b) { + // this = a << b + + ASSERT(b < 8 * sizeof(Val)); + if (!b) { + Set(a); + return; + } + const unsigned bInv = 8 * sizeof(Val) - b; + + const unsigned count = a.Count(); + SetCount(count + 1); + Val curr = 0; + for (unsigned index = count; index >= 1; --index) { + Val next = a[index - 1]; + SetVal(index, (Val)((next >> bInv) | (curr << b))); + curr = next; + } + SetVal(0, (Val)(curr << b)); + Trim(count + 1); + +} + +//=========================================================================== +void BigNum::Shr (const BigNum & a, unsigned b) { + // this = a >> b + + ASSERT(b < 8 * sizeof(Val)); + if (!b) { + Set(a); + return; + } + const unsigned bInv = 8 * sizeof(Val) - b; + + const unsigned count = a.Count(); + SetCount(count); + if (!count) + return; + + Val curr = a[0]; + for (unsigned index = 0; index + 1 < count; ++index) { + Val next = a[index + 1]; + SetVal(index, (Val)((next << bInv) | (curr >> b))); + curr = next; + } + SetVal(count - 1, (Val)(curr >> b)); + Trim(count); + +} + +//=========================================================================== +void BigNum::Square (const BigNum & a) { + // this = a * a + + const unsigned aCount = a.Count(); + const unsigned count = 2 * aCount; + SetCount(count); + if (!count) + return; + + // We perform the multiplication from left to right, so that we don't + // overwrite any operand words before they're used in the case that + // the destination is not distinct from the operand + SetVal(count - 1, 0); + for (unsigned index = count - 1; index--; ) { + + // Iterate every combination of source indices that adds up to + // index, and sum the products of those words + DVal value = 0; + unsigned aIndex = (index < aCount) ? 0 : (index + 1 - aCount); + unsigned bIndex; + for (; (int)((bIndex = index - aIndex) - aIndex) >= 0; ++aIndex) { + + // Calculate the product of this pair of words + DVal product = Mul(a[aIndex], a[bIndex]); + + // Add the product to the sum. If it exceeds the word size, + // apply carry. + value = (DVal)(value + product); + Val carry = HIGH(value); + unsigned carryIndex; + for (carryIndex = index + 1; carry; ++carryIndex) + SetVal(carryIndex, (DVal)((DVal)(*this)[carryIndex] + (DVal)carry), &carry); + value = LOW(value); + + // If this pair of words should be multiplied twice, add the + // product again. + if (aIndex == bIndex) + continue; + value = (DVal)(value + product); + carry = HIGH(value); + for (carryIndex = index + 1; carry; ++carryIndex) + SetVal(carryIndex, (DVal)((DVal)(*this)[carryIndex] + (DVal)carry), &carry); + value = LOW(value); + + } + + // Store the sum of products as the final value for index + SetVal(index, LOW(value)); + + } + + Trim(count); + +} + +//=========================================================================== +void BigNum::Sub (const BigNum & a, Val b) { + // this = a - b + + const unsigned count = a.Count(); + SetCount(count); + Val borrow = b; + for (unsigned index = 0; index < count; ++index) { + SetVal(index, (DVal)((DVal)a[index] - (DVal)borrow), &borrow); + borrow = (Val)((Val)0 - (Val)borrow); + } + ASSERT(!borrow); + Trim(index); + +} + +//=========================================================================== +void BigNum::Sub (const BigNum & a, const BigNum & b) { + // this = a - b + + const unsigned count = a.Count(); + const unsigned bCount = b.Count(); + GrowToCount(count, true); + Val borrow = 0; + for (unsigned index = 0; index < count; ++index) { + Val bVal = (index < bCount) ? b[index] : (Val)0; + SetVal(index, (DVal)((DVal)a[index] - (DVal)bVal - (DVal)borrow), &borrow); + borrow = (Val)((Val)0 - (Val)borrow); + } + ASSERT(!borrow); + Trim(index); + +} + +//=========================================================================== +void BigNum::ToStr (BigNum * buffer, Val radix) const { + ASSERT(this != buffer); + + // Calculate the number of characters in the prefix + unsigned prefixChars; + if (radix == 16) + prefixChars = 2; + else if (radix == 8) + prefixChars = 1; + else + prefixChars = 0; + + // Preallocate space for the output string + unsigned charsPerVal = 0; + for (Val testVal = (Val)-1; testVal; testVal = (Val)(testVal / radix)) + ++charsPerVal; + const unsigned charsTotal = max(1, Count()) * charsPerVal + prefixChars + 1; + buffer->SetCount((charsTotal * sizeof(wchar) + sizeof(Val) - 1) / sizeof(Val)); + + // Build the prefix + wchar * prefix = (wchar *)buffer->Ptr(); + if (prefixChars) { + prefix[0] = L'0'; + if (radix == 16) + prefix[1] = L'x'; + else + ASSERT(prefixChars == 1); + } + + // Build the number starting with the least significant digit + wchar * start = prefix + prefixChars; + wchar * curr = start; + BigNum work; + ALLOC_TEMP(work, Count()); + work.Set(*this); + do { + + // Extract the next value + Val remainder; + work.Div(work, radix, &remainder); + + // Encode it as a character in the output string + if (remainder >= 10) + *curr++ = (wchar)(L'a' + (unsigned)remainder - 10); + else + *curr++ = (wchar)(L'0' + (unsigned)remainder); + + } while (work.Count()); + *curr = 0; + + // Reverse the order of the output string + for (wchar * left = start, * right = curr - 1; left < right; ++left, --right) + SWAP(*left, *right); + +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.h new file mode 100644 index 00000000..c3f731ee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.h @@ -0,0 +1,110 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTBIGNUM_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTBIGNUM_H + + +/***************************************************************************** +* +* BigNum class +* +***/ + +class BigNum : private ARRAY(dword) { +public: + typedef dword Val; // must match base array + typedef qword DVal; // must be twice as large as Val + +private: + bool m_isTemp; + + void DivNormalized (const BigNum & a, const BigNum & b, BigNum * remainder); + void ModNormalized (const BigNum & a, const BigNum & b); + + inline static DVal Mul (Val a, Val b); + + inline void SetVal (unsigned index, Val value); + inline void SetVal (unsigned index, DVal value, Val * carry); + inline void Trim (unsigned count); + inline BigNum * UseTempAlloc (Val * ptr, unsigned count); + +public: + BigNum (); + BigNum (const BigNum & a); + BigNum (unsigned a); + BigNum (unsigned bytes, const void * data); + BigNum (const wchar str[], Val radix); + ~BigNum (); + + // Constant parameters need not be distinct from the destination or from + // each other + + void Add (const BigNum & a, Val b); + void Add (const BigNum & a, const BigNum & b); + int Compare (Val a) const; + int Compare (const BigNum & a) const; + void Div (const BigNum & a, Val b, Val * remainder); + void Div (const BigNum & a, const BigNum & b, BigNum * remainder); + void FromData (unsigned bytes, const void * data); + void FromStr (const wchar str[], Val radix); + void Gcd (const BigNum & a, const BigNum & b); + const void * GetData (unsigned * bytes) const; + unsigned HighBitPos () const; + bool InverseMod (const BigNum & a, const BigNum & b); + bool IsMultiple (Val a) const; + bool IsOdd () const; + bool IsPrime () const; + unsigned LowBitPos () const; + void Mod (const BigNum & a, const BigNum & b); + void Mul (const BigNum & a, Val b); + void Mul (const BigNum & a, const BigNum & b); + void MulMod (const BigNum & a, const BigNum & b, const BigNum & c); + void PowMod (Val a, const BigNum & b, const BigNum & c); + void PowMod (const BigNum & a, const BigNum & b, const BigNum & c); + void Rand (const BigNum & a, BigNum * seed); + void Rand (unsigned bits, BigNum * seed); + void RandPrime (unsigned bits, BigNum * seed); + void Set (const BigNum & a); + void Set (unsigned a); + void SetBits (unsigned setBitsOffset, unsigned setBitsCount); + void SetOne (); + void SetZero (); + void Shl (const BigNum & a, unsigned b); + void Shr (const BigNum & a, unsigned b); + void Square (const BigNum & a); + void Sub (const BigNum & a, Val b); + void Sub (const BigNum & a, const BigNum & b); + void ToStr (BigNum * buffer, Val radix) const; + +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.cpp new file mode 100644 index 00000000..606f9bc7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.cpp @@ -0,0 +1,683 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + +#define WHITESPACE L" \"\t\r\n\x1A" +#define FLAGS L"-/" +#define SEPARATORS L"=:" +#define TOGGLES L"+-" +#define ALL WHITESPACE FLAGS SEPARATORS TOGGLES + +static const unsigned kMaxTokenLength = MAX_PATH; + +struct CmdArgData { + CmdArgDef def; + union { + bool boolVal; + float floatVal; + int intVal; + const wchar * strVal; + unsigned unsignedVal; + } val; + wchar * buffer; + unsigned nameChars; + bool isSpecified; + + ~CmdArgData () { + if (buffer) + FREE(buffer); + } +}; + +struct CmdTokState { + CCmdParser * parser; + unsigned pendingIndex; + unsigned unflaggedIndex; +}; + +class CICmdParser { +private: + FARRAYOBJ(CmdArgData) m_argArray; + FARRAY(unsigned) m_idLookupArray; + unsigned m_requiredCount; + FARRAY(unsigned) m_unflaggedArray; + + inline bool CheckFlag (unsigned flags, unsigned flag, unsigned mask) const; + void Error (const CmdTokState * state, ECmdError errorCode, const wchar arg[], const wchar value[]) const; + bool LookupFlagged (const wchar ** name, unsigned * lastIndex) const; + bool ProcessValue (CmdTokState * state, unsigned index, const wchar str[]); + void SetDefaultValue (CmdArgData & arg); + bool TokenizeFlags (CmdTokState * state, const wchar str[]); + +public: + CICmdParser (const CmdArgDef def[], unsigned defCount); + bool CheckAllRequiredArguments (CmdTokState * state); + const CmdArgData * FindArgById (unsigned id) const; + const CmdArgData * FindArgByName (const wchar name[]) const; + bool Tokenize (CmdTokState * state, const wchar str[]); + +}; + + +/***************************************************************************** +* +* CICmdParser implementation +* +***/ + +//=========================================================================== +bool CICmdParser::CheckFlag (unsigned flags, unsigned flag, unsigned mask) const { + return ((flags & mask) == flag); +} + +//=========================================================================== +bool CICmdParser::CheckAllRequiredArguments (CmdTokState * state) { + bool result = (state->unflaggedIndex >= m_requiredCount); + if (!result) + Error(state, kCmdErrorTooFewArgs, nil, nil); + return result; +} + +//=========================================================================== +CICmdParser::CICmdParser (const CmdArgDef def[], unsigned defCount) { + unsigned loop; + + // Save the argument definitions + unsigned maxId = 0; + unsigned unflaggedCount = 0; + m_argArray.SetCount(defCount); + for (loop = 0; loop < defCount; ++loop) { + + // Check whether this argument is flagged + bool flagged = CheckFlag(def[loop].flags, kCmdArgFlagged, kCmdMaskArg); + + // Disallow names on unflagged arguments + ASSERT(flagged || !def[loop].name); + + // Store the argument data + CmdArgData & arg = m_argArray[loop]; + arg.def = def[loop]; + arg.buffer = nil; + arg.nameChars = def[loop].name ? StrLen(def[loop].name) : 0; + arg.isSpecified = false; + SetDefaultValue(arg); + maxId = max(maxId, def[loop].id); + + // Track the number of unflagged arguments + if (!flagged) + ++unflaggedCount; + + } + + // Build the id lookup table + unsigned idTableSize = min(maxId + 1, defCount * 2); + m_idLookupArray.SetCount(idTableSize); + m_idLookupArray.Zero(); + for (loop = 0; loop < defCount; ++loop) + if (def[loop].id < idTableSize) + m_idLookupArray[def[loop].id] = loop; + + // Build the unflagged array + unsigned unflaggedIndex = 0; + m_unflaggedArray.SetCount(unflaggedCount); + for (loop = 0; loop < defCount; ++loop) + if (CheckFlag(def[loop].flags, kCmdArgRequired, kCmdMaskArg)) + m_unflaggedArray[unflaggedIndex++] = loop; + m_requiredCount = unflaggedIndex; + for (loop = 0; loop < defCount; ++loop) + if (!(CheckFlag(def[loop].flags, kCmdArgFlagged, kCmdMaskArg) || + CheckFlag(def[loop].flags, kCmdArgRequired, kCmdMaskArg))) + m_unflaggedArray[unflaggedIndex++] = loop; + +} + +//=========================================================================== +void CICmdParser::Error (const CmdTokState * state, ECmdError errorCode, const wchar arg[], const wchar value[]) const { + + // Compose the error text + // (This text is only provided as a shortcut for trivial applications that + // don't want to compose their own text. Normally, an application would + // compose error text using its own localized strings.) + unsigned chars = 256 + (arg ? StrLen(arg) : 0) + (value ? StrLen(value) : 0); + wchar * buffer = (wchar *)ALLOC(chars * sizeof(wchar)); + switch (errorCode) { + + case kCmdErrorInvalidArg: + StrPrintf(buffer, chars, L"Invalid argument: %s", arg); + break; + + case kCmdErrorInvalidValue: + StrPrintf(buffer, chars, L"Argument %s invalid value: %s", arg, value); + break; + + case kCmdErrorTooFewArgs: + StrPrintf(buffer, chars, L"Too few arguments"); + break; + + case kCmdErrorTooManyArgs: + StrPrintf(buffer, chars, L"Too many arguments: %s", arg); + break; + + DEFAULT_FATAL(errorCode); + } + + // Call the error handler + state->parser->OnError(buffer, errorCode, arg, value); + + // Free memory + FREE(buffer); + +} + +//=========================================================================== +const CmdArgData * CICmdParser::FindArgById (unsigned id) const { + + // Search for the argument with this id + unsigned index; + if (id < m_idLookupArray.Count()) + index = m_idLookupArray[id]; + else + for (index = 0; index < m_argArray.Count(); ++index) + if (m_argArray[index].def.id == id) + break; + + // Verify that we found the correct argument + if ( (index >= m_argArray.Count()) || + (m_argArray[index].def.id != id) ) + return nil; + + // Return the argument data + return &m_argArray[index]; + +} + +//=========================================================================== +const CmdArgData * CICmdParser::FindArgByName (const wchar name[]) const { + + // Search for an argument with this name + unsigned index = (unsigned)-1; + if (!LookupFlagged(&name, &index)) + return nil; + + // Return the argument data + return &m_argArray[index]; + +} + +//=========================================================================== +bool CICmdParser::LookupFlagged (const wchar ** name, unsigned * lastIndex) const { + unsigned argCount = m_argArray.Count(); + unsigned chars = StrLen(*name); + unsigned bestIndex = (unsigned)-1; + unsigned bestChars = 0; + + // Check whether this argument is a suffix to any previously + // provided prefix in this token + for (unsigned prevChars = (*lastIndex != (unsigned)-1) ? m_argArray[*lastIndex].nameChars : 0; + (prevChars != (unsigned)-1) && !bestChars; + --prevChars) { + const CmdArgData & prev = prevChars ? m_argArray[*lastIndex] : *(const CmdArgData *)nil; + + // Find this argument in the list + for (unsigned index = 0; index < argCount; ++index) { + const CmdArgData & arg = m_argArray[index]; + + // Ignore non-flagged arguments + if (!CheckFlag(arg.def.flags, kCmdArgFlagged, kCmdMaskArg)) + continue; + + // Ignore this argument if it wouldn't beat the previous best match + if (arg.nameChars < bestChars + prevChars) + continue; + + // Ignore this argument if it doesn't match the prefix + bool caseSensitive = CheckFlag(arg.def.flags, kCmdCaseSensitive, kCmdCaseSensitive); + if ( prevChars && + ( (prevChars >= arg.nameChars) || + ( caseSensitive && StrCmp(arg.def.name, prev.def.name, prevChars)) || + (!caseSensitive && StrCmpI(arg.def.name, prev.def.name, prevChars)) ) ) + continue; + + // Ignore this argument if it doesn't match the suffix + if ( ( caseSensitive && StrCmp(*name, arg.def.name + prevChars, arg.nameChars - prevChars)) || + (!caseSensitive && StrCmpI(*name, arg.def.name + prevChars, arg.nameChars - prevChars)) ) + continue; + + // Track the best match + bestIndex = index; + bestChars = arg.nameChars - prevChars; + if (bestChars == chars) + break; + + } + + } + + // Return the result + *name += bestChars; + *lastIndex = bestIndex; + return (bestChars != 0); +} + +//=========================================================================== +bool CICmdParser::ProcessValue (CmdTokState * state, unsigned index, const wchar str[]) { + CmdArgData & arg = m_argArray[index]; + arg.isSpecified = true; + unsigned argType = arg.def.flags & kCmdMaskType; + switch (argType) { + + case kCmdTypeBool: + if (*str == '+') + arg.val.boolVal = true; + else if (*str == '-') + arg.val.boolVal = false; + else if (!*str) + arg.val.boolVal = CheckFlag(arg.def.flags, kCmdBoolSet, kCmdMaskBool); + else + Error(state, kCmdErrorInvalidValue, arg.def.name, str); + break; + + case kCmdTypeFloat: + { + const wchar * endPtr; + arg.val.floatVal = StrToFloat(str, &endPtr); + if (*endPtr) + Error(state, kCmdErrorInvalidValue, arg.def.name, str); + } + break; + + case kCmdTypeInt: + { + const wchar * endPtr; + arg.val.intVal = StrToInt(str, &endPtr); + if (*endPtr) + Error(state, kCmdErrorInvalidValue, arg.def.name, str); + } + break; + + case kCmdTypeString: + if (arg.buffer) + FREE(arg.buffer); + arg.buffer = StrDup(str); + arg.val.strVal = arg.buffer; + break; + + case kCmdTypeUnsigned: + { + const wchar * endPtr; + arg.val.unsignedVal = StrToUnsigned(str, &endPtr, 10); + if (*endPtr) + Error(state, kCmdErrorInvalidValue, arg.def.name, str); + } + break; + + DEFAULT_FATAL(argType); + + } + return true; +} + +//=========================================================================== +void CICmdParser::SetDefaultValue (CmdArgData & arg) { + unsigned argType = arg.def.flags & kCmdMaskType; + switch (argType) { + + case kCmdTypeBool: + arg.val.boolVal = !CheckFlag(arg.def.flags, kCmdBoolSet, kCmdMaskBool); + break; + + case kCmdTypeInt: + arg.val.intVal = 0; + break; + + case kCmdTypeUnsigned: + arg.val.unsignedVal = 0; + break; + + case kCmdTypeFloat: + arg.val.floatVal = 0.0f; + break; + + case kCmdTypeString: + arg.val.strVal = L""; + break; + + DEFAULT_FATAL(argType); + + } +} + +//=========================================================================== +bool CICmdParser::Tokenize (CmdTokState * state, const wchar str[]) { + wchar buffer[kMaxTokenLength]; + bool result = true; + while (result && StrTokenize(&str, buffer, arrsize(buffer), WHITESPACE)) { + + // If the previous argument is awaiting a value, then use this token + // as the value + if (state->pendingIndex != (unsigned)-1) { + result = ProcessValue(state, state->pendingIndex, buffer); + state->pendingIndex = (unsigned)-1; + continue; + } + + // Identify and process flagged parameters + if (StrChr(FLAGS, buffer[0]) && TokenizeFlags(state, buffer)) + continue; + + // Process unflagged parameters + if (state->unflaggedIndex < m_unflaggedArray.Count()) { + result = ProcessValue(state, m_unflaggedArray[state->unflaggedIndex++], buffer); + continue; + } + + // Process extra parameters + if (state->parser->OnExtra(buffer)) + continue; + + // Process invalid parameters + Error(state, kCmdErrorTooManyArgs, buffer, nil); + result = false; + break; + + } + return result; +} + +//=========================================================================== +bool CICmdParser::TokenizeFlags (CmdTokState * state, const wchar str[]) { + + // Process each separately flagged token within the string + wchar buffer[kMaxTokenLength]; + bool result = true; + while (result && StrTokenize(&str, buffer, arrsize(buffer), ALL)) { + if (!buffer[0]) + continue; + + // Process each flag within the token + unsigned lastIndex = (unsigned)-1; + const wchar * bufferPtr = buffer; + while (result) { + + // Lookup the argument name + result = LookupFlagged(&bufferPtr, &lastIndex); + if (!result) { + Error(state, kCmdErrorInvalidArg, bufferPtr, nil); + result = false; + break; + } + + // If this argument is boolean, allow it to share a common prefix + // with the next argument. In this case there is no place for + // the user to provide a value, so use the default value. + if (*bufferPtr && + CheckFlag(m_argArray[lastIndex].def.flags, kCmdTypeBool, kCmdMaskType)) { + result = ProcessValue(state, lastIndex, L""); + continue; + } + + break; + } + if (!result) + break; + + // Check for an argument value provided using a separator + if (*str && StrChr(SEPARATORS, *str)) { + result = ProcessValue(state, lastIndex, str + 1); + break; + } + + // Process values for boolean arguments + if (CheckFlag(m_argArray[lastIndex].def.flags, kCmdTypeBool, kCmdMaskType)) { + + // Check for a value provided with a toggle + if (*str && StrChr(TOGGLES, *str)) { + wchar tempStr[] = {*str, 0}; + result = ProcessValue(state, lastIndex, tempStr); + ++str; + continue; + } + + // Check for a default value + else { + result = ProcessValue(state, lastIndex, L""); + continue; + } + + } + + // Process values for non-boolean arguments + else { + + // Check for an argument value immediately following the name + if (*bufferPtr) { + result = ProcessValue(state, lastIndex, bufferPtr); + break; + } + + // Check for an argument value in the next token + else { + state->pendingIndex = lastIndex; + break; + } + + } + + } + return result; +} + + +/***************************************************************************** +* +* CCmdParser implementation +* +***/ + +//=========================================================================== +CCmdParser::CCmdParser () { + fParser = nil; +} + +//=========================================================================== +CCmdParser::CCmdParser (const CmdArgDef def[], unsigned defCount) { + Initialize(def, defCount); +} + +//=========================================================================== +CCmdParser::~CCmdParser () { + DEL(fParser); +} + +//=========================================================================== +bool CCmdParser::GetBool (unsigned id) const { + return fParser->FindArgById(id)->val.boolVal; +} + +//=========================================================================== +bool CCmdParser::GetBool (const wchar name[]) const { + return fParser->FindArgByName(name)->val.boolVal; +} + +//=========================================================================== +float CCmdParser::GetFloat (unsigned id) const { + return fParser->FindArgById(id)->val.floatVal; +} + +//=========================================================================== +float CCmdParser::GetFloat (const wchar name[]) const { + return fParser->FindArgByName(name)->val.floatVal; +} + +//=========================================================================== +int CCmdParser::GetInt (unsigned id) const { + return fParser->FindArgById(id)->val.intVal; +} + +//=========================================================================== +int CCmdParser::GetInt (const wchar name[]) const { + return fParser->FindArgByName(name)->val.intVal; +} + +//=========================================================================== +const wchar * CCmdParser::GetString (unsigned id) const { + return fParser->FindArgById(id)->val.strVal; +} + +//=========================================================================== +const wchar * CCmdParser::GetString (const wchar name[]) const { + return fParser->FindArgByName(name)->val.strVal; +} + +//=========================================================================== +unsigned CCmdParser::GetUnsigned (unsigned id) const { + return fParser->FindArgById(id)->val.unsignedVal; +} + +//=========================================================================== +unsigned CCmdParser::GetUnsigned (const wchar name[]) const { + return fParser->FindArgByName(name)->val.unsignedVal; +} + +//=========================================================================== +void CCmdParser::Initialize (const CmdArgDef def[], unsigned defCount) { + fParser = NEW(CICmdParser)(def, defCount); +} + +//=========================================================================== +bool CCmdParser::IsSpecified (unsigned id) const { + if (const CmdArgData * data = fParser->FindArgById(id)) + return data->isSpecified; + return false; +} + +//=========================================================================== +bool CCmdParser::IsSpecified (const wchar name[]) const { + if (const CmdArgData * data = fParser->FindArgByName(name)) + return data->isSpecified; + return false; +} + +//=========================================================================== +void CCmdParser::OnError (const wchar str[], ECmdError errorCode, const wchar arg[], const wchar value[]) { + ref(str); + ref(errorCode); + ref(arg); + ref(value); +} + +//=========================================================================== +bool CCmdParser::OnExtra (const wchar str[]) { + ref(str); + return false; +} + +//=========================================================================== +bool CCmdParser::Parse (const wchar cmdLine[]) { + // If no command line was passed, use the application's command line, + // skipping past the program name + if (!cmdLine) { + cmdLine = AppGetCommandLine(); + StrTokenize(&cmdLine, nil, 0, WHITESPACE); + while (*cmdLine == L' ') + ++cmdLine; + } + + // Process the command line + CmdTokState state = { + this, + (unsigned)-1, // pending index + 0 // unflagged index + }; + bool result; + result = fParser->Tokenize(&state, cmdLine); + if (result) + result = fParser->CheckAllRequiredArguments(&state); + + return result; +} + + +/**************************************************************************** +* +* CCmdParserSimple +* +***/ + + +//=========================================================================== +CCmdParserSimple::CCmdParserSimple ( + unsigned requiredStringCount, + unsigned optionalStringCount, + const wchar flaggedBoolNames[] // double null terminated if used +) { + + // Count the number of flagged arguments + unsigned flaggedBoolCount = 0; + const wchar * curr; + if (flaggedBoolNames) + for (curr = flaggedBoolNames; *curr; curr += StrLen(curr) + 1) + ++flaggedBoolCount; + + // Build the argument definition array + unsigned totalCount = requiredStringCount + optionalStringCount + flaggedBoolCount; + FARRAY(CmdArgDef) argDef(totalCount); + unsigned index = 0; + for (; index < requiredStringCount; ++index) { + argDef[index].flags = kCmdArgRequired | kCmdTypeString; + argDef[index].name = nil; + argDef[index].id = index + 1; + } + for (; index < requiredStringCount + optionalStringCount; ++index) { + argDef[index].flags = kCmdArgOptional | kCmdTypeString; + argDef[index].name = nil; + argDef[index].id = index + 1; + } + for (curr = flaggedBoolNames; index < totalCount; ++index) { + argDef[index].flags = kCmdArgFlagged | kCmdTypeBool; + argDef[index].name = curr; + argDef[index].id = 0; + curr += StrLen(curr) + 1; + } + + // Initialize the parser + Initialize(argDef.Ptr(), argDef.Count()); + +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.h new file mode 100644 index 00000000..8f9e2b7e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.h @@ -0,0 +1,128 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTCMD_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCmd.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTCMD_H + + +/***************************************************************************** +* +* Constants +* +***/ + +// Sets of mutually exclusive flags +const unsigned kCmdArgFlagged = 0x0 << 0; // default +const unsigned kCmdArgOptional = 0x1 << 0; +const unsigned kCmdArgRequired = 0x2 << 0; +const unsigned kCmdMaskArg = 0xf << 0; + +const unsigned kCmdTypeBool = 0x0 << 4; // default +const unsigned kCmdTypeInt = 0x1 << 4; +const unsigned kCmdTypeUnsigned = 0x2 << 4; +const unsigned kCmdTypeFloat = 0x3 << 4; +const unsigned kCmdTypeString = 0x4 << 4; +const unsigned kCmdMaskType = 0xf << 4; + +const unsigned kCmdBoolSet = 0x0 << 8; // default +const unsigned kCmdBoolUnset = 0x1 << 8; +const unsigned kCmdMaskBool = 0xf << 8; + +// Other flags +const unsigned kCmdCaseSensitive = 0x1 << 28; + + +// Error codes +enum ECmdError { + kCmdErrorInvalidArg, + kCmdErrorInvalidValue, + kCmdErrorTooFewArgs, + kCmdErrorTooManyArgs, + kNumCmdErrors +}; + + +/***************************************************************************** +* +* Types +* +***/ + +struct CmdArgDef { + unsigned flags; + const wchar * name; // must be compile-time constant + unsigned id; +}; + +class CCmdParser { + class CICmdParser * fParser; + + static void DispatchError (const wchar str[], ECmdError errorCode, const wchar arg[], const wchar value[], void * param); + static bool DispatchExtra (const wchar str[], void * param); + +protected: + CCmdParser (); + void Initialize (const CmdArgDef def[], unsigned defCount); + +public: + CCmdParser (const CmdArgDef def[], unsigned defCount); + virtual ~CCmdParser (); + + bool GetBool (unsigned id) const; + bool GetBool (const wchar name[]) const; + float GetFloat (unsigned id) const; + float GetFloat (const wchar name[]) const; + int GetInt (unsigned id) const; + int GetInt (const wchar name[]) const; + const wchar * GetString (unsigned id) const; + const wchar * GetString (const wchar name[]) const; + unsigned GetUnsigned (unsigned id) const; + unsigned GetUnsigned (const wchar name[]) const; + bool IsSpecified (unsigned id) const; + bool IsSpecified (const wchar name[]) const; + + virtual void OnError (const wchar str[], ECmdError errorCode, const wchar arg[], const wchar value[]); + virtual bool OnExtra (const wchar str[]); + + bool Parse (const wchar cmdLine[] = nil); +}; + +class CCmdParserSimple : public CCmdParser { +public: + CCmdParserSimple ( + unsigned requiredStringCount, + unsigned optionalStringCount, + const wchar flaggedBoolNames[] // double null terminated if used + ); + +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCoreLib.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCoreLib.h new file mode 100644 index 00000000..e72ce18e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCoreLib.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCoreLib.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTCORELIB_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCoreLib.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTCORELIB_H + +#pragma warning(push, 0) + +#include "HeadSpin.h" +#include "hsWindows.h" +#include "hsCritSect.h" +#include "hsStream.h" + +#pragma warning(pop) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.cpp new file mode 100644 index 00000000..8422d681 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.cpp @@ -0,0 +1,641 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + +#include "openssl/md5.h" +#include "openssl/sha.h" + +// OpenSSL's RC4 algorithm has bugs and randomly corrupts data +//#define OPENSSL_RC4 +#ifdef OPENSSL_RC4 +#include "openssl/rc4.h" +#endif + +/***************************************************************************** +* +* Opaque types +* +***/ + +struct CryptKey { + ECryptAlgorithm algorithm; + void * handle; +}; + + +/***************************************************************************** +* +* Private +* +***/ + +namespace Crypt { + +ShaDigest s_shaSeed; + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//============================================================================ +void Md5Process ( + void * dest, + unsigned sourceCount, + const unsigned sourceBytes[], + const void * sourcePtrs[] +) { + // initialize digest + MD5_CTX md5; + MD5_Init(&md5); + + // hash data streams + for (unsigned index = 0; index < sourceCount; ++index) + MD5_Update(&md5, sourcePtrs[index], sourceBytes[index]); + + // complete hashing + MD5_Final((unsigned char *)dest, &md5); +} + +//============================================================================ +void ShaProcess ( + void * dest, + unsigned sourceCount, + const unsigned sourceBytes[], + const void * sourcePtrs[] +) { + // initialize digest + SHA_CTX sha; + SHA_Init(&sha); + + // hash data streams + for (unsigned index = 0; index < sourceCount; ++index) + SHA_Update(&sha, sourcePtrs[index], sourceBytes[index]); + + // complete hashing + SHA_Final((unsigned char *)dest, &sha); +} + +//============================================================================ +void Sha1Process ( + void * dest, + unsigned sourceCount, + const unsigned sourceBytes[], + const void * sourcePtrs[] +) { + // initialize digest + SHA_CTX sha; + SHA1_Init(&sha); + + // hash data streams + for (unsigned index = 0; index < sourceCount; ++index) + SHA1_Update(&sha, sourcePtrs[index], sourceBytes[index]); + + // complete hashing + SHA1_Final((unsigned char *)dest, &sha); +} + + +/***************************************************************************** +* +* RC4 +* +***/ + +#ifdef OPENSSL_RC4 +//============================================================================ +static void Rc4Codec ( + CryptKey * key, + bool encrypt, + ARRAY(byte) * dest, + unsigned sourceBytes, + const void * sourceData +) { + ref(encrypt); // RC4 uses the same algorithm to both encrypt and decrypt + dest->SetCount(sourceBytes); + RC4((RC4_KEY *)key->handle, sourceBytes, (const unsigned char *)sourceData, dest->Ptr()); +} + +//============================================================================ +static void Rc4Codec ( + CryptKey * key, + bool encrypt, + unsigned bytes, + void * data +) { + ref(encrypt); // RC4 uses the same algorithm to both encrypt and decrypt + byte * temp = ALLOCA(byte, bytes); + RC4((RC4_KEY *)key->handle, bytes, (const unsigned char *)data, temp); + MemCopy(data, temp, bytes); +} + +#else // OPENSSL_RC4 + +//=========================================================================== +void KeyRc4::Codec (bool encrypt, ARRAY(byte) * dest, unsigned sourceBytes, const void * sourceData) { + ref(encrypt); // RC4 uses the same algorithm to both encrypt and decrypt + dest->SetCount(sourceBytes); + + byte * destDataPtr = (byte *)dest->Ptr(); + const byte * sourceDataPtr = (const byte *)sourceData; + + for (unsigned index = 0; index < sourceBytes; ++index) { + m_x = (m_x + 1) & 0xff; + m_y = (m_state[m_x] + m_y) & 0xff; + SWAP(m_state[m_x], m_state[m_y]); + + const unsigned offset = (m_state[m_x] + m_state[m_y]) & 0xff; + destDataPtr[index] = (byte)(sourceDataPtr[index] ^ m_state[offset]); + } +} + +//=========================================================================== +void KeyRc4::KeyGen ( + unsigned randomBytes, + const void * randomData, + ARRAY(byte) * privateData +) { + // Allocate an output digest + struct Digest { dword data[5]; }; + privateData->SetCount(sizeof(Digest)); + Digest * digest = (Digest *)privateData->Ptr(); + + // Perform the hash + { + // Initialize the hash values with the repeating pattern of random + // data + unsigned offset = 0; + for (; offset < sizeof(Digest); ++offset) + ((byte *)digest)[offset] = ((const byte *)randomData)[offset % randomBytes]; + for (; offset < randomBytes; ++offset) + ((byte *)digest)[offset % sizeof(Digest)] ^= ((const byte *)randomData)[offset]; + + // 32-bit rotate left + #ifdef _MSC_VER + #define ROTL(n, X) _rotl(X, n) + #else + #define ROTL(n, X) (((X) << (n)) | ((X) >> (32 - (n)))) + #endif + #define f1(x,y,z) (z ^ (x & (y ^ z))) // Rounds 0-19 + #define K1 0x5A827999L // Rounds 0-19 + #define subRound(a, b, c, d, e, f, k, data) (e += ROTL(5, a) + f(b, c, d) + k + data, b = ROTL(30, b)) + + // first five subrounds from SHA1 + dword A = 0x67452301; + dword B = 0xEFCDAB89; + dword C = 0x98BADCFE; + dword D = 0x10325476; + dword E = 0xC3D2E1F0; + subRound(A, B, C, D, E, f1, K1, digest->data[ 0]); + subRound(E, A, B, C, D, f1, K1, digest->data[ 1]); + subRound(D, E, A, B, C, f1, K1, digest->data[ 2]); + subRound(C, D, E, A, B, f1, K1, digest->data[ 3]); + subRound(B, C, D, E, A, f1, K1, digest->data[ 4]); + digest->data[0] += A; + digest->data[1] += B; + digest->data[2] += C; + digest->data[3] += D; + digest->data[4] += E; + } +} + +//=========================================================================== +void KeyRc4::Initialize (unsigned bytes, const void * data) { + ASSERT(bytes); + ASSERT(data); + + // Initialize key with default values + { + m_x = 0; + m_y = 0; + for (unsigned offset = 0; offset < arrsize(m_state); ++offset) + m_state[offset] = (byte) offset; + } + + // Seed key from digest + { + unsigned index1 = 0; + unsigned index2 = 0; + for (unsigned offset = 0; offset < arrsize(m_state); ++offset) { + ASSERT(index1 < bytes); + index2 = (((const byte *)data)[index1] + m_state[offset] + index2) & 0xff; + SWAP(m_state[offset], m_state[index2]); + if (++index1 == bytes) + index1 = 0; + } + } +} + +#endif // OPENSSL_RC4 + +} using namespace Crypt; + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void CryptDigest ( + ECryptAlgorithm algorithm, + void * dest, // must be sized to the algorithm's digest size + const unsigned sourceBytes, + const void * sourceData +) { + CryptDigest( + algorithm, + dest, + 1, + &sourceBytes, + &sourceData + ); +} + +//============================================================================ +void CryptDigest ( + ECryptAlgorithm algorithm, + void * dest, // must be sized to the algorithm's digest size + unsigned sourceCount, + const unsigned sourceBytes[], // [sourceCount] + const void * sourcePtrs[] // [sourceCount] +) { + switch (algorithm) { + case kCryptMd5: + Md5Process(dest, sourceCount, sourceBytes, sourcePtrs); + break; + + case kCryptSha: + ShaProcess(dest, sourceCount, sourceBytes, sourcePtrs); + break; + + case kCryptSha1: + Sha1Process(dest, sourceCount, sourceBytes, sourcePtrs); + break; + + DEFAULT_FATAL(algorithm); + } +} + +//============================================================================ +CryptKey * CryptKeyCreate ( + ECryptAlgorithm algorithm, + unsigned bytes, + const void * data +) { + CryptKey * key = nil; + switch (algorithm) { + case kCryptRc4: { + #ifdef OPENSSL_RC4 + RC4_KEY * rc4 = NEW(RC4_KEY); + RC4_set_key(rc4, bytes, (const unsigned char *)data); + key = NEW(CryptKey); + key->algorithm = kCryptRc4; + key->handle = rc4; + #else + KeyRc4 * rc4 = NEWZERO(KeyRc4)(bytes, data); + key = NEW(CryptKey); + key->algorithm = kCryptRc4; + key->handle = rc4; + #endif + } + break; + + case kCryptRsa: // Not implemented; fall-thru to FATAL +// break; + + DEFAULT_FATAL(algorithm); + } + + return key; +} + +//=========================================================================== +// Not exposed in header because is not used at the moment and I don't want a big rebuild right now :) +void CryptKeyGenerate ( + ECryptAlgorithm algorithm, + unsigned keyBits, // used for algorithms with variable key strength + unsigned randomBytes, + const void * randomData, + ARRAY(byte) * privateData, + ARRAY(byte) * publicData // only for public key cryptography +) { + // Allocate and fill in private and/or public key classes + switch (algorithm) { + + case kCryptRc4: + KeyRc4::KeyGen( + randomBytes, + randomData, + privateData + ); + break; + + case kCryptRsa: + ref(keyBits); + ref(publicData); + #if 0 + KeyRsa::KeyGen( + keyBits, + randomBytes, + randomData, + privateData, + publicData + ); + break; + #endif // fall thru to fatal... + + DEFAULT_FATAL(algorithm); + } +} + +//============================================================================ +void CryptKeyClose ( + CryptKey * key +) { + if (!key) + return; + + DEL(key->handle); + DEL(key); +} + +//============================================================================ +unsigned CryptKeyGetBlockSize ( + CryptKey * key +) { + switch (key->algorithm) { + case kCryptRc4: { + #ifdef OPENSSL_RC4 + return 1; + #else + KeyRc4 * rc4 = (KeyRc4 *)key->handle; + return rc4->GetBlockSize(); + #endif + } + break; + + case kCryptRsa: // Not implemented; fall-thru to FATAL +// return RsaGetBlockSize(key); + + DEFAULT_FATAL(algorithm); + } +} + +//============================================================================ +void CryptCreateRandomSeed ( + unsigned bytes, + byte * data +) { + COMPILER_ASSERT(SHA_DIGEST_LENGTH == 20); + + // Combine seed with input data + { + unsigned seedIndex = 0; + unsigned dataIndex = 0; + unsigned cur = 0; + unsigned end = max(bytes, sizeof(s_shaSeed)); + for (; cur < end; ++cur) { + ((byte *) &s_shaSeed)[seedIndex] ^= data[dataIndex]; + if (++seedIndex >= sizeof(s_shaSeed)) + seedIndex = 0; + if (++dataIndex >= bytes) + dataIndex = 0; + } + + s_shaSeed.data[2] ^= (dword) &bytes; + s_shaSeed.data[3] ^= (dword) bytes; + s_shaSeed.data[4] ^= (dword) data; + } + + // Hash seed + ShaDigest digest; + CryptDigest(kCryptSha, &digest, sizeof(s_shaSeed), &s_shaSeed); + + // Update output with contents of digest + { + unsigned src = 0; + unsigned dst = 0; + unsigned cur = 0; + unsigned end = max(bytes, sizeof(digest)); + for (; cur < end; ++cur) { + data[dst] ^= ((const byte *) &digest)[src]; + if (++src >= sizeof(digest)) + src = 0; + if (++dst >= bytes) + dst = 0; + } + } + + // Combine seed with digest + s_shaSeed.data[0] ^= digest.data[0]; + s_shaSeed.data[1] ^= digest.data[1]; + s_shaSeed.data[2] ^= digest.data[2]; + s_shaSeed.data[3] ^= digest.data[3]; + s_shaSeed.data[4] ^= digest.data[4]; +} + +//============================================================================ +void CryptHashPassword ( + const wchar username[], + const wchar password[], + ShaDigest * namePassHash +) { + unsigned passlen = StrLen(password); + unsigned userlen = StrLen(username); + + wchar * buffer = ALLOCA(wchar, passlen + userlen); + StrCopy(buffer, password, passlen); + StrCopy(buffer + passlen, username, userlen); + StrLower(buffer + passlen); // lowercase the username + + CryptDigest( + kCryptSha, + namePassHash, + (userlen + passlen) * sizeof(buffer[0]), + buffer + ); +} + +//============================================================================ +void CryptHashPasswordChallenge ( + unsigned clientChallenge, + unsigned serverChallenge, + const ShaDigest & namePassHash, + ShaDigest * challengeHash +) { + #include + struct { + dword clientChallenge; + dword serverChallenge; + ShaDigest namePassHash; + } buffer; + #include + buffer.clientChallenge = clientChallenge; + buffer.serverChallenge = serverChallenge; + buffer.namePassHash = namePassHash; + CryptDigest(kCryptSha, challengeHash, sizeof(buffer), &buffer); +} + +//============================================================================ +void CryptCreateFastWeakChallenge ( + unsigned * challenge, + unsigned val1, + unsigned val2 +) { + s_shaSeed.data[0] ^= TimeGetMs(); // looping time + s_shaSeed.data[0] ^= _rotl(s_shaSeed.data[0], 1); + s_shaSeed.data[0] ^= (unsigned) TimeGetTime(); // global time + s_shaSeed.data[0] ^= _rotl(s_shaSeed.data[0], 1); + s_shaSeed.data[0] ^= *challenge; // unknown + s_shaSeed.data[0] ^= _rotl(s_shaSeed.data[0], 1); + s_shaSeed.data[0] ^= (unsigned) challenge; // variable address + s_shaSeed.data[0] ^= _rotl(s_shaSeed.data[0], 1); + s_shaSeed.data[0] ^= val1; + s_shaSeed.data[0] ^= _rotl(s_shaSeed.data[0], 1); + s_shaSeed.data[0] ^= val2; + *challenge = s_shaSeed.data[0]; +} + +//============================================================================ +void CryptEncrypt ( + CryptKey * key, + ARRAY(byte) * dest, + unsigned sourceBytes, + const void * sourceData +) { + switch (key->algorithm) { + case kCryptRc4: { + #ifdef OPENSSL_RC4 + Rc4Codec(key, true, dest, sourceBytes, sourceData); + #else + KeyRc4 * rc4 = (KeyRc4 *)key->handle; + rc4->Codec(true, dest, sourceBytes, sourceData); + #endif + } + break; + + case kCryptRsa: // Not implemented; fall-thru to FATAL +// RsaCodec(key, true, dest, sourceBytes, sourceData); +// break; + + DEFAULT_FATAL(key->algorithm); + } +} + +//============================================================================ +void CryptEncrypt ( + CryptKey * key, + unsigned bytes, + void * data +) { + ASSERT(1 == CryptKeyGetBlockSize(key)); + + switch (key->algorithm) { + case kCryptRc4: { + #ifdef OPENSSL_RC4 + Rc4Codec(key, true, bytes, data); + #else + ARRAY(byte) dest; + dest.Reserve(bytes); + CryptEncrypt(key, &dest, bytes, data); + MemCopy(data, dest.Ptr(), bytes); + #endif + } + break; + + case kCryptRsa: // Not implemented; fall-thru to FATAL +// RsaCodec(key, true, dest, sourceBytes, sourceData); +// break; + + DEFAULT_FATAL(key->algorithm); + } +} + +//============================================================================ +void CryptDecrypt ( + CryptKey * key, + ARRAY(byte) * dest, + unsigned sourceBytes, + const void * sourceData +) { + switch (key->algorithm) { + case kCryptRc4: { + #ifdef OPENSSL_RC4 + Rc4Codec(key, false, dest, sourceBytes, sourceData); + #else + KeyRc4 * rc4 = (KeyRc4 *)key->handle; + rc4->Codec(false, dest, sourceBytes, sourceData); + #endif + } + break; + + case kCryptRsa: // Not implemented; fall-thru to FATAL +// RsaCodec(key, false, dest, sourceBytes, sourceData); +// break; + + DEFAULT_FATAL(key->algorithm); + } +} + +//============================================================================ +void CryptDecrypt ( + CryptKey * key, + unsigned bytes, + void * data +) { + ASSERT(1 == CryptKeyGetBlockSize(key)); + + switch (key->algorithm) { + case kCryptRc4: { + #ifdef OPENSSL_RC4 + Rc4Codec(key, false, bytes, data); + #else + ARRAY(byte) dest; + dest.Reserve(bytes); + CryptDecrypt(key, &dest, bytes, data); + MemCopy(data, dest.Ptr(), bytes); + #endif + } + break; + + case kCryptRsa: // Not implemented; fall-thru to FATAL +// RsaCodec(key, false, dest, sourceBytes, sourceData); +// break; + + DEFAULT_FATAL(key->algorithm); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.h new file mode 100644 index 00000000..ccbe1559 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.h @@ -0,0 +1,166 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTCRYPT_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtCrypt.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTCRYPT_H + + +/***************************************************************************** +* +* Types and constants +* +***/ + +struct CryptKey; + +enum ECryptAlgorithm { + kCryptSha, + kCryptSha1, + kCryptMd5, + kCryptRc4, + kCryptRsa, + kNumCryptAlgorithms +}; + +struct ShaDigest { + dword data[5]; +}; + + +/***************************************************************************** +* +* Digest functions +* +***/ + +void CryptDigest ( + ECryptAlgorithm algorithm, + void * dest, // must be sized to the algorithm's digest size + const unsigned sourceBytes, + const void * sourceData +); + +void CryptDigest ( + ECryptAlgorithm algorithm, + void * dest, // must be sized to the algorithm's digest size + unsigned sourceCount, + const unsigned sourceBytes[], // [sourceCount] + const void * sourcePtrs[] // [sourceCount] +); + + +/***************************************************************************** +* +* Key generation +* +***/ + +CryptKey * CryptKeyCreate ( + ECryptAlgorithm algorithm, + unsigned bytes, + const void * data +); + +void CryptKeyClose ( + CryptKey * key +); + +void CryptKeyGenerate ( + ECryptAlgorithm algorithm, + unsigned keyBits, // used for algorithms with variable key strength + unsigned randomBytes, + const void * randomData, + ARRAY(byte) * privateData, + ARRAY(byte) * publicData // only for public key cryptography +); + +unsigned CryptKeyGetBlockSize ( + CryptKey * key +); + +void CryptCreateRandomSeed ( + unsigned bytes, + byte * data +); + +void CryptHashPassword ( + const wchar username[], + const wchar password[], + ShaDigest * namePassHash +); + +void CryptHashPasswordChallenge ( + unsigned clientChallenge, + unsigned serverChallenge, + const ShaDigest & namePassHash, + ShaDigest * challengeHash +); + +void CryptCreateFastWeakChallenge ( + unsigned * challenge, + unsigned val1, + unsigned val2 +); + + +/***************************************************************************** +* +* Encryption and Decryption +* +***/ + +void CryptEncrypt ( + CryptKey * key, + ARRAY(byte) * dest, + unsigned sourceBytes, + const void * sourceData +); + +void CryptEncrypt ( + CryptKey * key, + unsigned bytes, + void * data +); + +void CryptDecrypt ( + CryptKey * key, + ARRAY(byte) * dest, // padded out to the algorithm's block size + unsigned sourceBytes, + const void * sourceData +); + +void CryptDecrypt ( + CryptKey * key, + unsigned bytes, + void * data +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtEndian.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtEndian.cpp new file mode 100644 index 00000000..e7ccde8d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtEndian.cpp @@ -0,0 +1,130 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtEndian.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Big endian functions +* +***/ + +#ifdef BIG_ENDIAN + +//=========================================================================== +void EndianConvert (word * array, unsigned count) { + for (; count--; ++array) + *array = Endian(*array); +} + +//=========================================================================== +void EndianConvert (dword * array, unsigned count) { + for (; count--; ++array) + *array = Endian(*array); +} + +//=========================================================================== +void EndianConvert (qword * array, unsigned count) { + for (; count--; ++array) + *array = Endian(*array); +} + +//============================================================================ +void EndianConvert (byte * data, unsigned elemCount, unsigned elemBytes) { + switch (elemBytes) { + case sizeof(byte): + break; + + case sizeof(word): + EndianConvert((word *)data, elemCount); + break; + + case sizeof(dword): + EndianConvert((dword *)data, elemCount); + break; + + case sizeof(qword): + EndianConvert((qword *)data, elemCount); + break; + + DEFAULT_FATAL(elemBytes); + } +} + +//=========================================================================== +void EndianCopy (unsigned * dst, const word src[], unsigned count) { + for (; count--; ++src, ++dst) + *dst = (unsigned)Endian(*src); +} + +//=========================================================================== +void EndianCopy (unsigned * dst, const dword src[], unsigned count) { + for (; count--; ++src, ++dst) + *dst = (unsigned)Endian(*src); +} + +//=========================================================================== +void EndianCopy (unsigned * dst, const qword src[], unsigned count) { + for (; count--; ++src, ++dst) + *dst = (unsigned)Endian(*src); +} + +//============================================================================ +void EndianCopy ( + void * dst, + const byte src[], + unsigned elemCount, + unsigned elemBytes +) { + switch (elemBytes) { + case sizeof(byte): + MemCopy(dst, src, elemCount); + break; + + case sizeof(word): + EndianCopy((word *)dst, (const word *)src, elemCount); + break; + + case sizeof(dword): + EndianCopy((dword *)dst, (const dword *)src, elemCount); + break; + + case sizeof(qword): + EndianCopy((qword *)dst, (const qword *)src, elemCount); + break; + + DEFAULT_FATAL(elemBytes); + } +} + +#endif // BIG_ENDIAN diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtEndian.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtEndian.h new file mode 100644 index 00000000..4e104188 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtEndian.h @@ -0,0 +1,198 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtEndian.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTENDIAN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtEndian.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTENDIAN_H + +// NOTE: Because we predominantly run on little-endian CPUs, we don't +// convert to "network order" when sending integers across the network +// (tcp uses big-endian regardless of underlying hardware) and instead +// use little-endian as the "native" language of our network messages. + + +/***************************************************************************** +* +* Types and constants +* +***/ + +#ifdef _M_IX86 +# define LITTLE_ENDIAN 1 +#else +# define BIG_ENDIAN 1 +// That was a pretty weak check for endian-ness, if it +// failed then we probably need to strengthen it a bit. +# error "Are you sure this is a big-endian CPU?" +#endif + + +/***************************************************************************** +* +* Little endian functions +* +***/ + +#ifdef LITTLE_ENDIAN + +//============================================================================ +inline word Endian (word value) { return value; } +inline dword Endian (dword value) { return value; } +inline qword Endian (qword value) { return value; } + +//=========================================================================== +inline void EndianConvert ( + word * array, + unsigned count +) { + ref(array); + ref(count); + return; +} + +//=========================================================================== +inline void EndianConvert ( + dword * array, + unsigned count +) { + ref(array); + ref(count); + return; +} + +//=========================================================================== +inline void EndianConvert ( + qword * array, + unsigned count +) { + ref(array); + ref(count); + return; +} + +//=========================================================================== +inline void EndianConvert ( + byte * data, + unsigned elemCount, + unsigned elemBytes +) { + ref(data); + ref(elemCount); + ref(elemBytes); + return; +} + +//=========================================================================== +inline void EndianCopy ( + word * dst, + const word src[], + unsigned count +) { + MemCopy(dst, src, count * sizeof(word)); +} + +//=========================================================================== +inline void EndianCopy ( + dword * dst, + const dword src[], + unsigned count +) { + MemCopy(dst, src, count * sizeof(dword)); +} + +//=========================================================================== +inline void EndianCopy ( + qword * dst, + const qword src[], + unsigned count +) { + MemCopy(dst, src, count * sizeof(qword)); +} + +//=========================================================================== +inline void EndianCopy ( + void * dst, + const byte src[], + unsigned elemCount, + unsigned elemBytes +) { + MemCopy(dst, src, elemCount * elemBytes); +} + + + +#endif // LITTLE_ENDIAN + + +/***************************************************************************** +* +* Big endian functions +* +***/ + +#ifdef BIG_ENDIAN + +//=========================================================================== +inline word Endian (word value) { + return (value >> 8) | (value << 8); +} + +//=========================================================================== +inline dword Endian (dword value) { + return ((value) << 24) | + ((value & 0x0000ff00) << 8) | + ((value & 0x00ff0000) >> 8) | + ((value) >> 24); +} + +//=========================================================================== +inline qword Endian (qword value) { + return ((value) << 56) | + ((value & 0x000000000000ff00) << 40) | + ((value & 0x0000000000ff0000) << 24) | + ((value & 0x00000000ff000000) << 8) | + ((value & 0x000000ff00000000) >> 8) | + ((value & 0x0000ff0000000000) >> 24) | + ((value & 0x00ff000000000000) >> 40) | + ((value) >> 56); +} + +void EndianConvert (word * array, unsigned count); +void EndianConvert (dword * array, unsigned count); +void EndianConvert (qword * array, unsigned count); +void EndianConvert (byte * data, unsigned elemCount, unsigned elemBytes); +void EndianCopy (word * dst, const word src[], unsigned count); +void EndianCopy (dword * dst, const dword src[], unsigned count); +void EndianCopy (qword * dst, const dword src[], unsigned count); +void EndianCopy (void * dst, const byte src[], unsigned elemCount, unsigned elemBytes); + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.cpp new file mode 100644 index 00000000..eb6e841f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.cpp @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/**************************************************************************** +* +* CHashValue +* +***/ + +// These random values were generated by the radioactive decay based +// random number generator at www.fourmilab.ch +const dword CHashValue::s_hashTable[] = { + 0xe8fd2035, 0xaf1add63, 0x049fb872, 0xcf9bb8eb, 0xc30d2a72, 0x15efaec1, 0xd250c7d9, 0xaf3c60a8, + 0x17ae32ff, 0x4089cd9e, 0x91dd6936, 0x093f880d, 0x9608ae8f, 0x452c0e11, 0xb6840ffd, 0x3e36c913, + 0x393114fd, 0xa72556b2, 0x7c338fb7, 0x4e445027, 0x2864eace, 0x9b0a17d6, 0x108da74b, 0xf2c479c1, + 0x288a43ac, 0x241e8411, 0x12ace782, 0xfb015799, 0x8b4dd597, 0x97199bc0, 0x621f0cce, 0x1658553e, + 0x99697839, 0xe3efb551, 0xbc1a2625, 0x54583c22, 0x9693d685, 0x612f910a, 0x080ccee8, 0xd00a43c9, + 0x86e6984d, 0x793245cf, 0x5335afa0, 0xdb8734d8, 0xfe5ab506, 0x3aae2ec1, 0x0aac22a3, 0xf3cd2c16, + 0x3d3039d1, 0x070b576d, 0x3a624bff, 0x0e185383, 0x78316efa, 0xafbef9ad, 0x556130cc, 0x54813111, + 0xc0e59be8, 0x30010241, 0x2cfa9040, 0x5a039832, 0x68a8a31d, 0xac786303, 0xe81b7dea, 0x2e3d7f5a, + 0xabb30a5c, 0xab08fef0, 0xf88cdc9d, 0x962d8361, 0x82ae270c, 0xc45a6e9c, 0x506a3f62, 0xed7c8f10, + 0x64631545, 0xea26488a, 0xd39f06f7, 0xafa35b6b, 0xdd1f83fb, 0x60b57a99, 0x636373ee, 0x05e82732, + 0xb2eaf275, 0x4763208c, 0x49499166, 0x8e436c6a, 0x3a4f831a, 0x57b7c11e, 0xbd751298, 0x6217ffbc, + 0x789efe70, 0x91d695cc, 0xa7e9049e, 0x12b74cdb, 0x40a2d8e6, 0x4dd33013, 0x506ec265, 0x81b2a421, + 0xdf98bac4, 0x554e33d0, 0x514decf9, 0x4374274c, 0x70b09e64, 0xac951473, 0xd6bb35eb, 0xa65ed4cf, + 0x71f724ac, 0x91e8da43, 0xe386dcee, 0x45bc6b20, 0x08ddf47a, 0xadac9571, 0x44d3cddf, 0x535ace85, + 0x5ac801cc, 0x89e90941, 0xa0507200, 0xe4b2a9b2, 0x00922b39, 0x2848f374, 0xfbe97b80, 0x77ea2e00, + 0x05eea617, 0x2bf0baf7, 0x0c97f929, 0x4d3190c0, 0x31f58de0, 0x7cae5dc4, 0x39f33590, 0x9cd39b3f, + 0x98b0bf46, 0x393169f1, 0x9f8271da, 0x0b85462f, 0xb8b81857, 0xed66ce2c, 0x6f97f3bb, 0x87e8c7dd, + 0x55741d88, 0x9ccd43b8, 0xe537d98a, 0x64a28550, 0x165ba5bf, 0xe4229568, 0x1af7c624, 0x059b9f7a, + 0x38129d4a, 0x73dca9ba, 0xe0185118, 0x48560fdb, 0xb7d0ec6b, 0x1acd6d4b, 0x84ab7a10, 0xcd9bf830, + 0x539d6be2, 0xfdcb65f4, 0x183a4dd7, 0xc4425aa4, 0xa3934d5f, 0xf71b8023, 0x30c109f7, 0x512e5128, + 0x7decdea5, 0xd3aded88, 0x34288710, 0x0c07a16e, 0xec0299da, 0x1e738f1b, 0xd7b898e1, 0x1b7318fd, + 0x3b67392d, 0x60da77b3, 0x614d4804, 0xb854468a, 0x4dbfc9fd, 0x85185833, 0x56095260, 0xb85d0771, + 0xbfe579e7, 0x51ca011b, 0xcebd2983, 0x4d56cda1, 0x5ec08b9a, 0x6bf9aa6f, 0x7da1e2c0, 0x4499dd84, + 0x95ca5ba3, 0xd0f9e77b, 0x5d099253, 0xbe943272, 0x1a87fe96, 0x29584d77, 0x0344f269, 0x2bdafede, + 0x4ababc94, 0x7a06acb7, 0x0a4c1efe, 0x8699f00f, 0x0f74e313, 0x0469ac17, 0x80f17875, 0xa6aecf16, + 0x0d772a15, 0x60eb0850, 0xa852be33, 0xe1574793, 0x7847204c, 0x1cea62ac, 0xb5948e41, 0x0e574ae9, + 0xdeb24de3, 0xe6472a0b, 0xaaaaf355, 0xa271aeae, 0xf3d5d209, 0x4f8fa676, 0x25ff71c9, 0x3f38d7df, + 0x0a8cd458, 0x5c6ad602, 0x06d0c0ec, 0x0d84ac0f, 0xf3cc4a59, 0xd6f04d8c, 0x1b3c3229, 0xc8281f6d, + 0x9410dd7c, 0x502519d1, 0x4449a76a, 0x88ba67b6, 0x8f710894, 0x7b63230e, 0xc095db28, 0x155a4ac7, + 0x0d418a5d, 0xe2b69e59, 0xeab4ac50, 0x0de06aae, 0x60f272fa, 0x408aefd8, 0x01c3435a, 0x0880c1e3, + 0x4f23137b, 0x9dfd6434, 0xd1e25d94, 0xbad4c88a, 0x0746edf9, 0x8103a9aa, 0xc8c73617, 0xe0f2759a, + 0x00161c79, 0xd4545360, 0x1763cc5b, 0x296361fa, 0xbc35858d, 0xdaed5e93, 0x0b9d0aed, 0x01c45a64, +}; + \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.h new file mode 100644 index 00000000..d39fd3cc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.h @@ -0,0 +1,724 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTHASH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtHash.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTHASH_H + + +/**************************************************************************** +* +* Macros +* +***/ + +// Define a field inside an object that is used to link it into a hash table +#define HASHLINK(object) THashLink< object > + +// Define a POINTER to a hash table, not a hash table +#define HASHTABLE(object,key) THashTable< object, key > + +// Define a hash table: +// - starts with kSlotMinCount rows +// - can grow to kDefaultSlotMaxCount rows +// (hash table grows when a row contains more than kGrowOnListSize entries +#define HASHTABLEDECL(object,key,link) THashTableDecl< object, key, offsetof(object,link), 0 > + +// Define a hash table in situations when a forward reference prevents use of HASHTABLEDECL +// - Size characteristics are identical to HASHTABLEDECL +#define HASHTABLEDYN(object,key) THashTableDyn< object, key > + +// Define a hash table with: +// - starts with +// - row table never grows +#define HASHTABLEDECLSIZE(object,key,link,size) THashTableDecl + + +/**************************************************************************** +* +* Forward declarations +* +***/ + +template +class THashLink; + +template +class TBaseHashTable; + + +/**************************************************************************** +* +* THashLink +* +***/ + +template +class THashLink { + friend class TBaseHashTable; + +private: + unsigned m_hash; + LINK(T) m_linkToFull; + LINK(T) m_linkToSlot; + +public: + inline bool IsLinked () const; + inline T * Next (); + inline const T * Next () const; + inline T * Prev (); + inline const T * Prev () const; + inline void Unlink (); +}; + +//=========================================================================== +template +bool THashLink::IsLinked () const { + return m_linkToFull.IsLinked(); +} + +//=========================================================================== +template +T * THashLink::Next () { + return m_linkToFull.Next(); +} + +//=========================================================================== +template +const T * THashLink::Next () const { + return m_linkToFull.Next(); +} + +//=========================================================================== +template +T * THashLink::Prev () { + return m_linkToFull.Prev(); +} + +//=========================================================================== +template +const T * THashLink::Prev () const { + return m_linkToFull.Prev(); +} + +//=========================================================================== +template +void THashLink::Unlink () { + m_linkToFull.Unlink(); + m_linkToSlot.Unlink(); +} + + +/**************************************************************************** +* +* TBaseHashTable +* +***/ + +template +class TBaseHashTable { + +private: + enum { kSlotMinCount = 8 }; + enum { kDefaultSlotMaxCount = 1024 }; + enum { kGrowOnListSize = 5 }; + + LIST(T) m_fullList; + int m_linkOffset; + FARRAYOBJ(LIST(T)) m_slotListArray; + unsigned m_slotMask; // always set to a power of two minus one + unsigned m_slotMaxCount; + + inline bool CheckGrowTable (LIST(T) * slotList); + inline const THashLink & GetLink (const T * object) const; + inline THashLink & GetLink (T * object); + inline void SetSlotCount (unsigned count); + +protected: + inline unsigned GetHash (const T * object) const; + inline unsigned & GetHash (T * object); + inline const LIST(T) & GetSlotList (unsigned hash) const; + inline LIST(T) & GetSlotList (unsigned hash); + inline void SetLinkOffset (int linkOffset, unsigned maxSize); + inline void SetSlotMaxCount (unsigned count); + +public: + inline TBaseHashTable (); + inline TBaseHashTable (const TBaseHashTable & source); + inline TBaseHashTable & operator= (const TBaseHashTable & source); + + inline void Add (T * object, unsigned hash); + inline void Clear (); + inline void Delete (T * object); + inline T * Head (); + inline const T * Head () const; + inline T * Next (const T * object); + inline const T * Next (const T * object) const; + inline void Order (T * linkedObject, ELinkType linkType, T * existingObject); + inline T * Prev (const T * object); + inline const T * Prev (const T * object) const; + inline T * Tail (); + inline const T * Tail () const; + inline void Unlink (T * object); + +}; + +//=========================================================================== +template +TBaseHashTable::TBaseHashTable () { + m_slotMask = 0; + m_slotMaxCount = kDefaultSlotMaxCount; + // more initialization done during call to SetLinkOffset() +} + +//=========================================================================== +template +TBaseHashTable::TBaseHashTable (const TBaseHashTable & source) { + ref(source); +#ifdef HS_DEBUGGING + FATAL("No copy constructor"); +#endif + TBaseHashTable(); +} + +//=========================================================================== +template +TBaseHashTable & TBaseHashTable::operator= (const TBaseHashTable & source) { + ref(source); +#ifdef HS_DEBUGGING + FATAL("No assignment operator"); +#endif + return *this; +} + +//=========================================================================== +template +void TBaseHashTable::Add (T * object, unsigned hash) { + GetHash(object) = hash; + + LIST(T) * list = &GetSlotList(hash); + if (CheckGrowTable(list)) + list = &GetSlotList(hash); + + m_fullList.Link(object); + list->Link(object); +} + +//=========================================================================== +template +bool TBaseHashTable::CheckGrowTable (LIST(T) * list) { + + unsigned nextCount = (m_slotMask + 1) * 2; + if (nextCount > m_slotMaxCount) + return false; + + unsigned listCount = 0; + for (T * curr = list->Head(); curr; curr = list->Next(curr)) + ++listCount; + + if (listCount + 1 < kGrowOnListSize) + return false; + + SetSlotCount(nextCount); + + return true; +} + +//=========================================================================== +template +void TBaseHashTable::Clear () { + m_fullList.Clear(); +} + +//=========================================================================== +template +void TBaseHashTable::Delete (T * object) { + DEL(object); +} + +//=========================================================================== +template +unsigned TBaseHashTable::GetHash (const T * object) const { + return GetLink(object).m_hash; +} + +//=========================================================================== +template +unsigned & TBaseHashTable::GetHash (T * object) { + return GetLink(object).m_hash; +} + +//=========================================================================== +template +const THashLink & TBaseHashTable::GetLink (const T * object) const { + return *(const THashLink *)((const byte *)object + m_linkOffset); +} + +//=========================================================================== +template +THashLink & TBaseHashTable::GetLink (T * object) { + return *(THashLink *)((byte *)object + m_linkOffset); +} + +//=========================================================================== +template +const LIST(T) & TBaseHashTable::GetSlotList (unsigned hash) const { + return m_slotListArray[hash & m_slotMask]; +} + +//=========================================================================== +template +LIST(T) & TBaseHashTable::GetSlotList (unsigned hash) { + return m_slotListArray[hash & m_slotMask]; +} + +//=========================================================================== +template +T * TBaseHashTable::Head () { + return m_fullList.Head(); +} + +//=========================================================================== +template +const T * TBaseHashTable::Head () const { + return m_fullList.Head(); +} + +//=========================================================================== +template +T * TBaseHashTable::Next (const T * object) { + return m_fullList.Next(object); +} + +//=========================================================================== +template +const T * TBaseHashTable::Next (const T * object) const { + return m_fullList.Next(object); +} + +//=========================================================================== +template +void TBaseHashTable::Order (T * linkedObject, ELinkType linkType, T * existingObject) { + THashLink & link = GetLink(linkedObject); + ref(link); + ASSERT(link.m_linkToFull.IsLinked()); + m_fullList.Link(linkedObject, linkType, existingObject); +} + +//=========================================================================== +template +T * TBaseHashTable::Prev (const T * object) { + return m_fullList.Prev(object); +} + +//=========================================================================== +template +const T * TBaseHashTable::Prev (const T * object) const { + return m_fullList.Prev(object); +} + +//=========================================================================== +template +void TBaseHashTable::SetLinkOffset (int linkOffset, unsigned maxSize) { + ASSERT(!m_fullList.Head()); + ASSERT(!m_slotListArray.Count()); + ASSERT(!m_slotMask); + + m_linkOffset = linkOffset; + m_fullList.SetLinkOffset(m_linkOffset + offsetof(THashLink, m_linkToFull)); + + if (!m_slotMask) + SetSlotCount(max(kSlotMinCount, MathNextPow2(maxSize))); +} + +//=========================================================================== +template +void TBaseHashTable::SetSlotCount (unsigned count) { + ASSERT(!(count & (count - 1))); // power of two + ASSERT(count >= 2); + + if (count == m_slotMask + 1) + return; + m_slotMask = count - 1; + + m_slotListArray.ZeroCount(); + m_slotListArray.SetCount(count); + for (unsigned loop = 0; loop < count; ++loop) + m_slotListArray[loop].SetLinkOffset(m_linkOffset + offsetof(THashLink, m_linkToSlot)); + + for (T * curr = Head(); curr; curr = Next(curr)) + GetSlotList(GetHash(curr)).Link(curr); +} + +//=========================================================================== +template +void TBaseHashTable::SetSlotMaxCount (unsigned count) { + if (count) + m_slotMaxCount = max(kSlotMinCount, count); +} + +//=========================================================================== +template +T * TBaseHashTable::Tail () { + return m_fullList.Tail(); +} + +//=========================================================================== +template +const T * TBaseHashTable::Tail () const { + return m_fullList.Tail(); +} + +//=========================================================================== +template +void TBaseHashTable::Unlink (T * object) { + THashLink & link = GetLink(object); + link.Unlink(); +} + + +/**************************************************************************** +* +* THashTable +* +***/ + +template +class THashTable : public TBaseHashTable { + +public: + inline void Add (T * object); + inline void Add (T * object, unsigned hash); + inline T * Find (const K & key); + inline T * FindNext (const K & key, T * object); + inline const T * Find (const K & key) const; + inline const T * FindNext (const K & key, const T * object) const; + inline T * Unduplicate (T * object, const K & key); + +}; + +//=========================================================================== +template +inline void THashTable::Add (T * object) { + TBaseHashTable::Add(object, object->GetHash()); +} + +//=========================================================================== +template +inline void THashTable::Add (T * object, unsigned hash) { + TBaseHashTable::Add(object, hash); +} + +//=========================================================================== +template +T * THashTable::Find (const K & key) { + return (T *)((const THashTable *)this)->Find(key); +} + +//=========================================================================== +template +T * THashTable::FindNext (const K & key, T * object) { + return (T *)((const THashTable *)this)->FindNext(key, object); +} + +//=========================================================================== +template +const T * THashTable::Find (const K & key) const { + unsigned hash = key.GetHash(); + const LIST(T) & slotList = GetSlotList(hash); + for (const T * curr = slotList.Head(); curr; curr = slotList.Next(curr)) + if ((GetHash(curr) == hash) && (*curr == key)) + return curr; + return nil; +} + +//=========================================================================== +template +const T * THashTable::FindNext (const K & key, const T * object) const { + unsigned hash = key.GetHash(); + const LIST(T) & slotList = GetSlotList(hash); + for (const T * curr = slotList.Next(object); curr; curr = slotList.Next(curr)) + if ((GetHash(curr) == hash) && (*curr == key)) + return curr; + return nil; +} + +//=========================================================================== +template +T * THashTable::Unduplicate (T * object, const K & key) { + T * existing = Find(key); + if (existing) { + DEL(object); + return existing; + } + else { + Add(object); + return object; + } +} + + +/**************************************************************************** +* +* THashTableDecl +* +***/ + +template +class THashTableDecl : public THashTable { + +public: + inline THashTableDecl (); + +}; + +//=========================================================================== +template +THashTableDecl::THashTableDecl () { + SetLinkOffset(linkOffset, maxSize); + SetSlotMaxCount(maxSize); +} + + +/**************************************************************************** +* +* THashTableDyn +* +***/ + +template +class THashTableDyn : public THashTable { + +public: + void Initialize (int linkOffset, unsigned maxSize = 0); + +}; + +//=========================================================================== +template +void THashTableDyn::Initialize (int linkOffset, unsigned maxSize) { + SetLinkOffset(linkOffset, maxSize); + SetSlotMaxCount(maxSize); +} + + +/**************************************************************************** +* +* THashKeyVal +* +***/ + +template +class THashKeyVal { +public: + THashKeyVal () : m_value(0) { } + THashKeyVal (const T & value) : m_value(value) { } + bool operator== (const THashKeyVal & rhs) const { + return m_value == rhs.m_value; + } + unsigned GetHash () const { + CHashValue hash(&m_value, sizeof(m_value)); + return hash.GetHash(); + } + const T & GetValue () const { return m_value; } + void SetValue (const T & value) { m_value = value; } + +protected: + T m_value; +}; + + +/**************************************************************************** +* +* CHashKeyStrPtr / CHashKeyStrPtrI +* +***/ + +//=========================================================================== +template +class THashKeyStrBase { +public: + const C * GetString () const { + return m_str; + } + +protected: + THashKeyStrBase () : m_str(nil) { } + THashKeyStrBase (const C str[]) : m_str(str) { } + virtual ~THashKeyStrBase () { } + + const C * m_str; +}; + +//=========================================================================== +template +class THashKeyStrCmp : public THashKeyStrBase { +public: + bool operator== (const THashKeyStrCmp & rhs) const { + return StrCmp(m_str, rhs.m_str) == 0; + } + unsigned GetHash () const { + return StrHash(m_str); + } + +protected: + THashKeyStrCmp () { } + THashKeyStrCmp (const C str[]) : THashKeyStrBase(str) { } +}; + +//=========================================================================== +template +class THashKeyStrCmpI : public THashKeyStrBase { +public: + bool operator== (const THashKeyStrCmpI & rhs) const { + return StrCmpI(m_str, rhs.m_str) == 0; + } + unsigned GetHash () const { + return StrHashI(m_str); + } +protected: + + THashKeyStrCmpI () { } + THashKeyStrCmpI (const C str[]) : THashKeyStrBase(str) { } +}; + + +/**************************************************************************** +* +* THashKeyStrPtr +* +***/ + +template +class THashKeyStrPtr : public T { +public: + THashKeyStrPtr () { } + THashKeyStrPtr (const C str[]) : T(str) { } + void SetString (const C str[]) { + m_str = str; + } +}; + +typedef THashKeyStrPtr< wchar, THashKeyStrCmp > CHashKeyStrPtr; +typedef THashKeyStrPtr< wchar, THashKeyStrCmpI > CHashKeyStrPtrI; +typedef THashKeyStrPtr< char, THashKeyStrCmp > CHashKeyStrPtrChar; +typedef THashKeyStrPtr< char, THashKeyStrCmpI > CHashKeyStrPtrCharI; + + +/**************************************************************************** +* +* THashKeyStr +* +***/ + +template +class THashKeyStr : public T { +public: + THashKeyStr () { } + THashKeyStr (const C str[]) { SetString(str); } + THashKeyStr (const THashKeyStr &); // intentionally unimplemented + THashKeyStr & operator= (const THashKeyStr &); // intentionally unimplemented + ~THashKeyStr () { + SetString(nil); + } + void SetString (const C str[]) { // deprecated + if (m_str) + FREE(const_cast(m_str)); + if (str) + m_str = StrDup(str); + else + m_str = nil; + } +}; + +typedef THashKeyStr< wchar, THashKeyStrCmp > CHashKeyStr; +typedef THashKeyStr< wchar, THashKeyStrCmpI > CHashKeyStrI; +typedef THashKeyStr< char, THashKeyStrCmp > CHashKeyStrChar; +typedef THashKeyStr< char, THashKeyStrCmpI > CHashKeyStrCharI; + + +/**************************************************************************** +* +* CHashValue +* +***/ + +class CHashValue { +private: + static const dword s_hashTable[]; + + dword m_result; + + inline void Construct () { m_result = 0x325d1eae; } + +public: + static dword LookupHashBits (unsigned value) { ASSERT(value < 0x100); return s_hashTable[value]; } + + inline CHashValue () { Construct() ; } + inline CHashValue (const CHashValue & source) { m_result = source.m_result; } + inline CHashValue (const void * data, unsigned bytes) { Construct(); Hash(data, bytes); } + inline CHashValue & operator= (const CHashValue & source) { m_result = source.m_result; return *this; } + inline bool operator== (const CHashValue & source) const { return (m_result == source.m_result); } + + inline dword GetHash () const { return m_result; } + + __forceinline void Hash (const void * data, unsigned bytes); + __forceinline void Hash8 (unsigned data); + __forceinline void Hash16 (unsigned data); + __forceinline void Hash32 (unsigned data); + +}; + +//=========================================================================== +void CHashValue::Hash (const void * data, unsigned bytes) { + for (const byte * curr = (const byte *)data, * term = curr + bytes; curr != term; ++curr) + Hash8(*curr); +} + +//=========================================================================== +void CHashValue::Hash8 (unsigned data) { + m_result += s_hashTable[m_result >> 24] ^ (m_result >> 6) ^ s_hashTable[data & 0xff]; +} + +//=========================================================================== +void CHashValue::Hash16 (unsigned data) { + Hash8(data); + Hash8(data >> 8); +} + +//=========================================================================== +void CHashValue::Hash32 (unsigned data) { + Hash8(data); + Hash8(data >> 8); + Hash8(data >> 16); + Hash8(data >> 24); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.cpp new file mode 100644 index 00000000..37eea930 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.cpp @@ -0,0 +1,113 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/**************************************************************************** +* +* CBaseList +* +***/ + +//=========================================================================== +void CBaseList::Link (CBaseList * list, byte * afterNode, byte * beforeNode, ELinkType linkType, byte * existingNode) { + + // Verify that the two lists share a common link offset + ASSERT(m_linkOffset != LINK_OFFSET_UNINIT); + ASSERT(m_linkOffset == list->m_linkOffset); + + // Verify that the two lists are distinct + CBaseLink * sourceTerminator = &list->m_terminator; + ASSERT(sourceTerminator != &m_terminator); + + // Find the first and last nodes to move from the source list + CBaseLink * afterLink = afterNode ? GetLink(afterNode) : sourceTerminator; + CBaseLink * beforeLink = beforeNode ? GetLink(beforeNode) : sourceTerminator; + CBaseLink * firstLink = afterLink->NextLink(); + CBaseLink * lastLink = beforeLink->m_prevLink; + if (lastLink == afterLink) + return; + ASSERT(firstLink != beforeLink); + + // Store nodes for later use in linking + byte * firstNode = afterLink->m_next; + byte * lastNextNode = lastLink->m_next; + ASSERT(firstNode); + ASSERT(lastNextNode); + + // Find the previous and next nodes in the destination list which will + // bound all of the nodes of the source list + CBaseLink * existingLink = existingNode ? GetLink(existingNode) : &m_terminator; + CBaseLink * prevLink, * nextLink; + switch (linkType) { + + case kListLinkAfter: + prevLink = existingLink; + nextLink = existingLink->NextLink(); + break; + + case kListLinkBefore: + prevLink = existingLink->m_prevLink; + nextLink = existingLink; + break; + + DEFAULT_FATAL(linkType); + + } + + // Update the first and last nodes of the moved range to point to the + // previous and next nodes in the destination list + firstLink->m_prevLink = prevLink; + lastLink->m_next = prevLink->m_next; + + // Update the previous and next nodes in the destination list to point to + // the first and last nodes of the source range + nextLink->m_prevLink = lastLink; + prevLink->m_next = firstNode; + + // Update the before and after links from the source list to point to + // each other + afterLink->m_next = lastNextNode; + beforeLink->m_prevLink = afterLink; + +} + +//=========================================================================== +void CBaseList::UnlinkAll () { + for (CBaseLink * link = m_terminator.m_prevLink, * prev; link != &m_terminator; link = prev) { + prev = link->m_prevLink; + link->InitializeLinksWithOffset(m_linkOffset); + } + m_terminator.InitializeLinksWithOffset(m_linkOffset); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.h new file mode 100644 index 00000000..f37674a0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.h @@ -0,0 +1,634 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTLIST_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtList.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTLIST_H + + +/**************************************************************************** +* +* Constants +* +***/ + +enum ELinkType { + kListUnlinked, + kListLinkAfter, + kListLinkBefore, + kListHead = kListLinkAfter, + kListTail = kListLinkBefore +}; + + +/**************************************************************************** +* +* Macros +* +***/ + +#define LINK(class) TLink< class > +#define LIST(class) TList< class > +#define LISTDECL(class,field) TListDecl< class, offsetof(class,field) > +#define LISTDYN(class) TListDyn< class > + + +/**************************************************************************** +* +* Forward declarations +* +***/ + +template +class TLink; + +template +class TList; + + +/**************************************************************************** +* +* CBaseLink +* +***/ + +class CBaseLink { + friend class CBaseList; + + inline static bool TermCheck (byte * ptr); + inline static byte * TermMark (byte * ptr); + inline static byte * TermUnmarkAlways (byte * ptr); + +protected: + CBaseLink * volatile m_prevLink; + byte * volatile m_next; + + inline int CalcLinkOffset () const; + inline void InitializeLinks (); + inline void InitializeLinksWithOffset (int linkOffset); + inline void InsertAfter (byte * node, CBaseLink * prevLink, int linkOffset); + inline void InsertBefore (byte * node, CBaseLink * nextLink); + inline byte * Next () const; + inline byte * NextIgnoreTerm () const; + inline byte * NextUnchecked () const; + inline CBaseLink * NextLink () const; + inline CBaseLink * NextLink (int linkOffset) const; + inline byte * Prev () const; + inline void UnlinkFromNeighbors (); + +public: + inline CBaseLink (); + inline CBaseLink (const CBaseLink & source); + inline ~CBaseLink (); + inline CBaseLink & operator= (const CBaseLink & source); + inline bool IsLinked () const; + inline void Unlink (); + +}; + +//=========================================================================== +CBaseLink::CBaseLink () { + InitializeLinks(); +} + +//=========================================================================== +CBaseLink::CBaseLink (const CBaseLink & source) { + ref(source); +#ifdef HS_DEBUGGING + if (source.IsLinked()) + FATAL("No copy constructor"); +#endif + InitializeLinks(); +} + +//=========================================================================== +CBaseLink::~CBaseLink () { + UnlinkFromNeighbors(); +} + +//=========================================================================== +CBaseLink & CBaseLink::operator= (const CBaseLink & source) { + ref(source); +#ifdef HS_DEBUGGING + FATAL("No assignment operator"); +#endif + return *this; +} + +//=========================================================================== +int CBaseLink::CalcLinkOffset () const { + return (int)((byte *)this - m_prevLink->NextIgnoreTerm()); +} + +//=========================================================================== +void CBaseLink::InitializeLinks () { + ASSERT(!((unsigned_ptr)this & 3)); + m_prevLink = this; + m_next = TermMark((byte *)this); +} + +//=========================================================================== +void CBaseLink::InitializeLinksWithOffset (int linkOffset) { + m_prevLink = this; + m_next = TermMark((byte *)this - linkOffset); +} + +//=========================================================================== +void CBaseLink::InsertAfter (byte * node, CBaseLink * prevLink, int linkOffset) { + UnlinkFromNeighbors(); + m_prevLink = prevLink; + m_next = prevLink->m_next; + prevLink->NextLink(linkOffset)->m_prevLink = this; + prevLink->m_next = node; +} + +//=========================================================================== +void CBaseLink::InsertBefore (byte * node, CBaseLink * nextLink) { + UnlinkFromNeighbors(); + m_prevLink = nextLink->m_prevLink; + m_next = m_prevLink->m_next; + nextLink->m_prevLink->m_next = node; + nextLink->m_prevLink = this; +} + +//=========================================================================== +bool CBaseLink::IsLinked () const { + return (m_prevLink != this); +} + +//=========================================================================== +byte * CBaseLink::Next () const { + return TermCheck(m_next) ? nil : m_next; +} + +//=========================================================================== +byte * CBaseLink::NextIgnoreTerm () const { + return TermUnmarkAlways(m_next); +} + +//=========================================================================== +byte * CBaseLink::NextUnchecked () const { + return m_next; +} + +//=========================================================================== +CBaseLink * CBaseLink::NextLink () const { + return (CBaseLink *)(NextIgnoreTerm() + CalcLinkOffset()); +} + +//=========================================================================== +CBaseLink * CBaseLink::NextLink (int linkOffset) const { + ASSERT(linkOffset == CalcLinkOffset()); + return (CBaseLink *)(NextIgnoreTerm() + linkOffset); +} + +//=========================================================================== +byte * CBaseLink::Prev () const { + return m_prevLink->m_prevLink->Next(); +} + +//=========================================================================== +bool CBaseLink::TermCheck (byte * ptr) { + return (unsigned_ptr)ptr & 1; +} + +//=========================================================================== +byte * CBaseLink::TermMark (byte * ptr) { + // Converts an unmarked pointer to a marked pointer + ASSERT(!TermCheck(ptr)); + return (byte *)((unsigned_ptr)ptr + 1); +} + +//=========================================================================== +byte * CBaseLink::TermUnmarkAlways (byte * ptr) { + // Returns an unmarked pointer regardless of whether the source pointer + // was marked on unmarked + return (byte *)((unsigned_ptr)ptr & ~1); +} + +//=========================================================================== +void CBaseLink::Unlink () { + UnlinkFromNeighbors(); + InitializeLinks(); +} + +//=========================================================================== +void CBaseLink::UnlinkFromNeighbors () { + NextLink()->m_prevLink = m_prevLink; + m_prevLink->m_next = m_next; +} + + +/**************************************************************************** +* +* TLink +* +***/ + +template +class TLink : public CBaseLink { + +public: + inline T * Next (); + inline const T * Next () const; + inline T * NextUnchecked (); + inline const T * NextUnchecked () const; + inline T * Prev (); + inline const T * Prev () const; + +}; + +//=========================================================================== +template +T * TLink::Next () { + return (T *)CBaseLink::Next(); +} + +//=========================================================================== +template +const T * TLink::Next () const { + return (const T *)CBaseLink::Next(); +} + +//=========================================================================== +template +T * TLink::NextUnchecked () { + return (T *)CBaseLink::NextUnchecked(); +} + +//=========================================================================== +template +const T * TLink::NextUnchecked () const { + return (const T *)CBaseLink::NextUnchecked(); +} + +//=========================================================================== +template +T * TLink::Prev () { + return (T *)CBaseLink::Prev(); +} + +//=========================================================================== +template +const T * TLink::Prev () const { + return (const T *)CBaseLink::Prev(); +} + + +/**************************************************************************** +* +* CBaseList +* +***/ + +class CBaseList { + +private: + enum { LINK_OFFSET_UNINIT = 0xDDDDDDDD }; + + int m_linkOffset; + CBaseLink m_terminator; + +protected: + inline CBaseLink * GetLink (const byte * node) const; + inline byte * Head () const; + inline bool IsLinked (const byte * node) const; + inline void Link (byte * node, ELinkType linkType, byte * existingNode); + void Link (CBaseList * list, byte * afterNode, byte * beforeNode, ELinkType linkType, byte * existingNode); + inline byte * Next (const byte * node) const; + inline byte * NextUnchecked (const byte * node) const; + inline byte * Prev (byte * node) const; + inline byte * Tail () const; + inline void Unlink (byte * node); + +public: + inline CBaseList (); + inline CBaseList (const CBaseList & source); + inline ~CBaseList (); + inline CBaseList & operator= (const CBaseList & source); + inline void SetLinkOffset (int linkOffset); + void UnlinkAll (); + +}; + +//=========================================================================== +CBaseList::CBaseList () { + m_linkOffset = LINK_OFFSET_UNINIT; +} + +//=========================================================================== +CBaseList::CBaseList (const CBaseList & source) { + m_linkOffset = LINK_OFFSET_UNINIT; + ref(source); +} + +//=========================================================================== +CBaseList::~CBaseList () { + if (m_terminator.IsLinked()) + UnlinkAll(); +} + +//=========================================================================== +CBaseList & CBaseList::operator= (const CBaseList & source) { + ref(source); + return *this; +} + +//=========================================================================== +CBaseLink * CBaseList::GetLink (const byte * node) const { + return (CBaseLink *)(node + m_linkOffset); +} + +//=========================================================================== +byte * CBaseList::Head () const { + return m_terminator.Next(); +} + +//=========================================================================== +bool CBaseList::IsLinked (const byte * node) const { + ASSERT(node); + return GetLink(node)->IsLinked(); +} + +//=========================================================================== +void CBaseList::Link (byte * node, ELinkType linkType, byte * existingNode) { + ASSERT(node != existingNode); + ASSERT(m_linkOffset != LINK_OFFSET_UNINIT); + ASSERT((linkType == kListLinkAfter) || (linkType == kListLinkBefore)); + if (linkType == kListLinkAfter) + GetLink(node)->InsertAfter(node, existingNode ? GetLink(existingNode) : &m_terminator, m_linkOffset); + else + GetLink(node)->InsertBefore(node, existingNode ? GetLink(existingNode) : &m_terminator); +} + +//=========================================================================== +byte * CBaseList::Next (const byte * node) const { + ASSERT(node); + return GetLink(node)->Next(); +} + +//=========================================================================== +byte * CBaseList::NextUnchecked (const byte * node) const { + ASSERT(node); + return GetLink(node)->NextUnchecked(); +} + +//=========================================================================== +byte * CBaseList::Prev (byte * node) const { + ASSERT(node); + return GetLink(node)->Prev(); +} + +//=========================================================================== +void CBaseList::SetLinkOffset (int linkOffset) { + ASSERT(!Head()); + + m_linkOffset = linkOffset; + m_terminator.InitializeLinksWithOffset(linkOffset); +} + +//=========================================================================== +byte * CBaseList::Tail () const { + return m_terminator.Prev(); +} + +//=========================================================================== +void CBaseList::Unlink (byte * node) { + ASSERT(node); + GetLink(node)->Unlink(); +} + + +/**************************************************************************** +* +* TList +* +***/ + +template +class TList : public CBaseList { + +private: + inline T * NewFlags (unsigned flags, ELinkType linkType, T * existingNode, const char file[], int line); + +public: + inline void Clear (); + inline void Delete (T * node); + inline T * Head (); + inline const T * Head () const; + inline bool IsLinked (const T * node) const; + inline void Link (T * node, ELinkType linkType = kListTail, T * existingNode = nil); + inline void Link (TList * list, ELinkType linkType = kListTail, T * existingNode = nil); + inline void Link (TList * list, T * afterNode, T * beforeNode, ELinkType linkType = kListTail, T * existingNode = nil); + inline T * New (ELinkType linkType = kListTail, T * existingNode = nil, const char file[] = nil, int line = 0); + inline T * NewZero (ELinkType linkType = kListTail, T * existingNode = nil, const char file[] = nil, int line = 0); + inline T * Next (const T * node); + inline const T * Next (const T * node) const; + inline T * NextUnchecked (const T * node); + inline const T * NextUnchecked (const T * node) const; + inline T * Prev (const T * node); + inline const T * Prev (const T * node) const; + inline T * Tail (); + inline const T * Tail () const; + inline void Unlink (T * node); + +}; + +//=========================================================================== +template +void TList::Clear () { + for (T * curr; (curr = Head()) != nil; Delete(curr)) + ; +} + +//=========================================================================== +template +void TList::Delete (T * node) { + DEL(node); +} + +//=========================================================================== +template +T * TList::Head () { + return (T *)CBaseList::Head(); +} + +//=========================================================================== +template +const T * TList::Head () const { + return (const T *)CBaseList::Head(); +} + +//=========================================================================== +template +bool TList::IsLinked (const T * node) const { + return CBaseList::IsLinked((const byte *)node); +} + +//=========================================================================== +template +void TList::Link (T * node, ELinkType linkType, T * existingNode) { + CBaseList::Link((byte *)node, linkType, (byte *)existingNode); +} + +//=========================================================================== +template +void TList::Link (TList * list, ELinkType linkType, T * existingNode) { + CBaseList::Link(list, nil, nil, linkType, (byte *)existingNode); +} + +//=========================================================================== +template +void TList::Link (TList * list, T * afterNode, T * beforeNode, ELinkType linkType, T * existingNode) { + CBaseList::Link(list, (byte *)afterNode, (byte *)beforeNode, linkType, (byte *)existingNode); +} + +//=========================================================================== +template +inline T * TList::Next (const T * node) { + return (T *)CBaseList::Next((byte *)node); +} + +//=========================================================================== +template +inline const T * TList::Next (const T * node) const { + return (const T *)CBaseList::Next((byte *)node); +} + +//=========================================================================== +template +inline T * TList::NextUnchecked (const T * node) { + return (T *)CBaseList::NextUnchecked((byte *)node); +} + +//=========================================================================== +template +inline const T * TList::NextUnchecked (const T * node) const { + return (const T *)CBaseList::NextUnchecked((byte *)node); +} + +//=========================================================================== +template +inline T * TList::New (ELinkType linkType, T * existingNode, const char file[], int line) { + return NewFlags(0, linkType, existingNode, file, line); +} + +//=========================================================================== +template +inline T * TList::NewFlags (unsigned flags, ELinkType linkType, T * existingNode, const char file[], int line) { + if (!file) { + file = __FILE__; + line = __LINE__; + } + T * node = new(MemAlloc(sizeof(T), flags, file, line)) T; + if (linkType != kListUnlinked) + Link(node, linkType, existingNode); + return node; +} + +//=========================================================================== +template +inline T * TList::NewZero (ELinkType linkType, T * existingNode, const char file[], int line) { + return NewFlags(MEM_ZERO, linkType, existingNode, file, line); +} + +//=========================================================================== +template +inline T * TList::Prev (const T * node) { + return (T *)CBaseList::Prev((byte *)node); +} + +//=========================================================================== +template +inline const T * TList::Prev (const T * node) const { + return (const T *)CBaseList::Prev((byte *)node); +} + +//=========================================================================== +template +inline T * TList::Tail () { + return (T *)CBaseList::Tail(); +} + +//=========================================================================== +template +inline const T * TList::Tail () const { + return (const T *)CBaseList::Tail(); +} + +//=========================================================================== +template +inline void TList::Unlink (T * node) { + CBaseList::Unlink((byte *)node); +} + + +/**************************************************************************** +* +* TListDecl +* +***/ + +template +class TListDecl : public TList { + +public: + inline TListDecl (); + +}; + +//=========================================================================== +template +TListDecl::TListDecl () { + SetLinkOffset(linkOffset); +} + + +/**************************************************************************** +* +* TListDyn +* +***/ + +template +class TListDyn : public TList { + +public: + void Initialize (int linkOffset); + +}; + +//=========================================================================== +template +void TListDyn::Initialize (int linkOffset) { + SetLinkOffset(linkOffset); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMath.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMath.cpp new file mode 100644 index 00000000..d4399519 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMath.cpp @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMath.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exported bit manipulation functions +* +***/ + +//=========================================================================== +#ifndef _M_IX86 + +unsigned MathHighBitPos (dword val) { + ASSERT(val); + double f = (double)val; + return (*((dword *)&f + 1) >> 20) - 1023; +} + +#else + +__declspec(naked) unsigned __fastcall MathHighBitPos (dword) { + __asm { + bsr eax, ecx + ret 0 + }; +} + +#endif + +//=========================================================================== +#ifndef _M_IX86 + +unsigned MathLowBitPos (dword val) { + val &= ~(val - 1); // clear all but the low bit + ASSERT(val); + double f = (double)val; + return (*((dword *)&f + 1) >> 20) - 1023; +} + +#else + +__declspec(naked) unsigned __fastcall MathLowBitPos (dword) { + __asm { + bsf eax, ecx + ret 0 + }; +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMath.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMath.h new file mode 100644 index 00000000..03ef9fb2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMath.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMath.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTMATH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMath.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTMATH_H + + +/***************************************************************************** +* +* Calling conventions +* +***/ + +#ifdef _M_IX86 +#define MATHCALL __fastcall +#else +#define MATHCALL +#endif + + +/***************************************************************************** +* +* Bit manipulation functions +* +***/ + +unsigned MATHCALL MathLowBitPos (dword val); +unsigned MATHCALL MathHighBitPos (dword val); + +//=========================================================================== +inline unsigned MathBitCount (dword val) { + val = val - ((val >> 1) & 033333333333) - ((val >> 2) & 011111111111); + val = ((val + (val >> 3)) & 030707070707); + val = val + (val >> 6); + val = (val + (val >> 12) + (val >> 24)) & 077; + return val; +} + +//=========================================================================== +inline unsigned MathBitMaskCreate (unsigned count) { + ASSERT(count <= 8 * sizeof(unsigned)); + return count ? ((2 << (count - 1)) - 1) : 0; +} + +//=========================================================================== +inline dword MathHighBitValue (dword val) { + return val ? 1 << MathHighBitPos(val) : 0; +} + +//=========================================================================== +inline bool MathIsPow2 (unsigned val) { + return !(val & (val - 1)); +} + +//=========================================================================== +inline unsigned MathLowBitValue (unsigned val) { + return val & ~(val - 1); +} + +//=========================================================================== +inline unsigned MathNextMultiplePow2 (unsigned val, unsigned multiple) { + ASSERT(multiple); + ASSERT(MathIsPow2(multiple)); + return (val + (multiple - 1)) & ~(multiple - 1); +} + +//=========================================================================== +inline dword MathNextPow2 (dword val) { + return MathIsPow2(val) ? val : 1 << (MathHighBitPos(val) + 1); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMisc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMisc.cpp new file mode 100644 index 00000000..1b020c30 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMisc.cpp @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMisc.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private data +* +***/ + +static void * s_moduleInstance; + + +/***************************************************************************** +* +* Public functions +* +***/ + +//============================================================================ +void ModuleSetInstance (void * instance) { + s_moduleInstance = instance; +} + +//============================================================================ +void * ModuleGetInstance () { + return s_moduleInstance; +} + + +/***************************************************************************** +* +* Dll initialization +* +***/ + +//============================================================================ +#if HS_BUILD_FOR_WIN32 +BOOL WINAPI PreDllMain (HANDLE handle, DWORD reason, LPVOID) { + if (reason == DLL_PROCESS_ATTACH) { + ModuleSetInstance(handle); + } + return true; +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMisc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMisc.h new file mode 100644 index 00000000..489064c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMisc.h @@ -0,0 +1,150 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMisc.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTMISC_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtMisc.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTMISC_H + + +/***************************************************************************** +* +* Constants +* +***/ + +const wchar UNICODE_BOM = 0xfeff; // Unicode byte-order mark +const char UTF8_BOM[] = "\xef\xbb\xbf"; + + +/***************************************************************************** +* +* Module instance functions +* +***/ + +void ModuleSetInstance (void * instance); +void * ModuleGetInstance (); + + +/***************************************************************************** +* +* Command line functions +* +***/ + +const wchar * AppGetCommandLine (); + + +/***************************************************************************** +* +* System info functions +* +***/ +void MachineGetName (wchar * computerName, unsigned length = 32); + + +/***************************************************************************** +* +* Misc types +* +***/ + +// used to dump the internal state of a module +typedef void (__cdecl * FStateDump)( + void * param, + const wchar fmt[], + ... +); + + +/***************************************************************************** +* +* Dll initialization +* +***/ + +#if HS_BUILD_FOR_WIN32 +#define SRV_MODULE_PRE_INIT() \ + extern BOOL WINAPI PreDllMain (HANDLE handle, DWORD reason, LPVOID); \ + extern "C" BOOL (WINAPI *_pRawDllMain)(HANDLE, DWORD, LPVOID) = PreDllMain +#endif + + +/***************************************************************************** +* +* System status +* +***/ + +struct MemoryStatus { + unsigned totalPhysMB; // total physical memory + unsigned availPhysMB; // free physical memory + unsigned totalPageFileMB; // total page file size + unsigned availPageFileMB; // free page file size + unsigned totalVirtualMB; // total virtual address space for calling process + unsigned availVirtualMB; // available virtual address space for calling process + unsigned memoryLoad; // 0..100 +}; +void MemoryGetStatus (MemoryStatus * status); + +struct DiskStatus { + wchar name[16]; + unsigned totalSpaceMB; + unsigned freeSpaceMB; +}; + +void DiskGetStatus (ARRAY(DiskStatus) * disks); + + +void CpuGetInfo ( + word * cpuCaps, + dword * cpuVendor, + word * cpuSignature +); + +// CPU capability flags +const unsigned kCpuCap3dNow = 1<<0; +const unsigned kCpuCapCmov = 1<<1; // conditional move +const unsigned kCpuCapEst = 1<<2; // enhanced speed step +const unsigned kCpuCapHtt = 1<<3; // hyperthreading +const unsigned kCpuCapMmx = 1<<4; // multimedia extensions +const unsigned kCpuCapPsn = 1<<5; // processor serial number +const unsigned kCpuCapSse = 1<<6; // streaming SIMD extensions +const unsigned kCpuCapSse2 = 1<<7; +const unsigned kCpuCapSse3 = 1<<8; +const unsigned kCpuCapTsc = 1<<9; // time stamp counter + +// Macros for packing and unpacking CPU signature +#define CPU_SIGNATURE(family, model, stepping) ((stepping) | (model << 4) | (family << 8)) +#define CPU_SIGNATURE_FAMILY(sig) ((sig >> 8) & 0xf) +#define CPU_SIGNATURE_MODEL(sig) ((sig >> 4) & 0xf) +#define CPU_SIGNATURE_STEPPING(sig) (sig & 0xf) diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPath.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPath.cpp new file mode 100644 index 00000000..715f5753 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPath.cpp @@ -0,0 +1,274 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPath.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +void PathGetProgramDirectory ( + wchar *dst, + unsigned dstChars +) { + ASSERT(dst); + ASSERT(dstChars); + + PathGetProgramName(dst, dstChars); + PathRemoveFilename(dst, dst, dstChars); +} + +//=========================================================================== +void PathAddFilename ( + wchar *dst, + const wchar src[], + const wchar fname[], + unsigned dstChars +) { + ASSERT(dst); + ASSERT(dstChars); + + wchar temp[MAX_PATH]; + if (dst == src) { + StrCopy(temp, src, arrsize(temp)); + src = temp; + } + else if (dst == fname) { + StrCopy(temp, fname, arrsize(temp)); + fname = temp; + } + + PathMakePath(dst, dstChars, 0, src, fname, 0); +} + +//=========================================================================== +void PathRemoveFilename ( + wchar *dst, + const wchar src[], + unsigned dstChars +) { + ASSERT(dst); + ASSERT(src); + ASSERT(dstChars); + + wchar drive[MAX_DRIVE]; + wchar dir[MAX_DIR]; + PathSplitPath(src, drive, dir, 0, 0); + PathMakePath(dst, dstChars, drive, dir, 0, 0); +} + +//=========================================================================== +void PathRemoveExtension ( + wchar *dst, + const wchar src[], + unsigned dstChars +) { + ASSERT(dst); + ASSERT(src); + ASSERT(dstChars); + + wchar drive[MAX_DRIVE]; + wchar dir[MAX_DIR]; + wchar fname[MAX_FNAME]; + PathSplitPath(src, drive, dir, fname, 0); + PathMakePath(dst, dstChars, drive, dir, fname, 0); +} + +//=========================================================================== +void PathSetExtension ( + wchar *dst, + const wchar src[], + const wchar ext[], + unsigned dstChars +) { + ASSERT(dst); + ASSERT(src); + ASSERT(dst != ext); + ASSERT(dstChars); + + wchar drive[MAX_DRIVE]; + wchar dir[MAX_DIR]; + wchar fname[MAX_FNAME]; + PathSplitPath(src, drive, dir, fname, 0); + PathMakePath(dst, dstChars, drive, dir, fname, ext); +} + +//=========================================================================== +void PathAddExtension ( + wchar *dst, + const wchar src[], + const wchar ext[], + unsigned dstChars +) { + ASSERT(dst); + ASSERT(src); + ASSERT(dst != ext); + ASSERT(dstChars); + + wchar drive[MAX_DRIVE]; + wchar dir[MAX_DIR]; + wchar fname[MAX_FNAME]; + wchar oldext[MAX_EXT]; + PathSplitPath(src, drive, dir, fname, oldext); + PathMakePath( + dst, + dstChars, + drive, + dir, + fname, + oldext[0] ? oldext : ext + ); +} + +//=========================================================================== +void PathRemoveDirectory ( + wchar *dst, + const wchar src[], + unsigned dstChars +) { + ASSERT(dst); + ASSERT(src); + ASSERT(dstChars); + + wchar fname[MAX_FNAME]; + wchar ext[MAX_EXT]; + PathSplitPath(src, 0, 0, fname, ext); + PathMakePath(dst, dstChars, 0, 0, fname, ext); +} + + +/***************************************************************************** +* +* Email formatting functions +* +***/ + +//============================================================================ +void PathSplitEmail ( + const wchar emailAddr[], + wchar * user, + unsigned userChars, + wchar * domain, + unsigned domainChars, + wchar * tld, + unsigned tldChars, + wchar * subDomains, + unsigned subDomainChars, + unsigned subDomainCount +) { + ASSERT(emailAddr); + + #define SUB_DOMAIN(i) subDomains[(i) * subDomainChars] + + // null-terminate all output parameters + if (userChars) { + ASSERT(user); + user[0] = 0; + } + if (domainChars) { + ASSERT(domain); + domain[0] = 0; + } + if (tldChars) { + ASSERT(tld); + tld[0] = 0; + } + if (subDomainChars || subDomainCount) { + ASSERT(subDomains); + for (unsigned i = 0; i < subDomainCount; ++i) + SUB_DOMAIN(i) = 0; + } + + // bail now if email address is zero-length + unsigned len = StrLen(emailAddr); + if (!len) + return; + + // copy email address so we can tokenize it + wchar * tmp = ALLOCA(wchar, len + 1); + StrCopy(tmp, emailAddr, len + 1); + const wchar * work = tmp; + + // parse user + wchar token[MAX_PATH]; + if (!StrTokenize(&work, token, arrsize(token), L"@")) + return; + + // copy user to output parameter + if (userChars) + StrCopy(user, token, userChars); + + // skip past the '@' symbol + if (!*work++) + return; + + // parse all domains + ARRAY(wchar *) arr; + while (StrTokenize(&work, token, arrsize(token), L".")) { + unsigned toklen = StrLen(token); + wchar * str = ALLOCA(wchar, toklen + 1); + StrCopy(str, token, toklen + 1); + arr.Add(str); + } + + // copy domains to output parameters + unsigned index = 0; + if (arr.Count() > 2) { + // all domains except for the last two are sub-domains + for (index = 0; index < arr.Count() - 2; ++index) { + if (index < subDomainCount) + if (subDomains) + StrCopy(&SUB_DOMAIN(index), arr[index], subDomainChars); + } + } + if (arr.Count() > 1) { + // second to last domain is the primary domain + if (domain) + StrCopy(domain, arr[index], domainChars); + // last comes the top level domain + ++index; + if (tld) + StrCopy(tld, arr[index], tldChars); + } + else if (arr.Count() == 1) { + // if only one domain, return it as a sub-domain + if (index < subDomainCount) + if (subDomains) + StrCopy(&SUB_DOMAIN(index), arr[index], subDomainChars); + } + + #undef SUB_DOMAIN +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPath.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPath.h new file mode 100644 index 00000000..6c00b41a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPath.h @@ -0,0 +1,309 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPath.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTPATH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPath.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTPATH_H + + +/***************************************************************************** +* +* Path definitions +* +***/ + +#ifndef MAX_PATH +#define MAX_PATH 260 +#endif + +#define MAX_DRIVE 256 +#define MAX_DIR 256 +#define MAX_FNAME 256 +#define MAX_EXT 256 + + + +const unsigned kPathFlagFile = 1<<0; +const unsigned kPathFlagDirectory = 1<<1; +const unsigned kPathFlagHidden = 1<<2; +const unsigned kPathFlagSystem = 1<<3; +const unsigned kPathFlagRecurse = 1<<4; // also set if "**" used in filespec + +struct PathFind { + unsigned flags; + qword fileLength; + qword lastWriteTime; + wchar name[MAX_PATH]; +}; + + +/***************************************************************************** +* +* Path "get" functions +* +***/ + +void PathFindFiles ( + ARRAY(PathFind) * paths, + const wchar fileSpec[], + unsigned pathFlags +); +void PathGetProgramName ( + wchar * dst, + unsigned dstChars +); +void PathGetModuleName ( + wchar * dst, + unsigned dstChars +); + +// this function will use the current directory if is a relative path +// (see PathSetCurrentDirectory/PathGetCurrentDirectory) +bool PathFromString ( + wchar * dst, + const wchar src[], + unsigned dstChars +); +bool PathFromString ( + wchar * dst, + const wchar src[], + unsigned dstChars, + const wchar baseDir[] +); + +bool PathDoesFileExist ( + const wchar fileName[] +); +bool PathDoesDirectoryExist ( + const wchar directory[] +); + +bool PathDeleteFile ( + const wchar file[] +); +bool PathMoveFile ( + const wchar src[], + const wchar dst[] +); +bool PathCopyFile ( + const wchar src[], + const wchar dst[] +); + + +/***************************************************************************** +* +* Path building functions +* +***/ + +void PathSplitPath ( + const wchar path[], + wchar * drive, + wchar * dir, + wchar * fname, + wchar * ext +); + +void PathMakePath ( + wchar * path, + unsigned chars, + const wchar drive[], + const wchar dir[], + const wchar fname[], + const wchar ext[] +); + +// c:\dir1 + dir2\file.txt => c:\dir1\dir2\file.txt +void PathAddFilename ( + wchar * dst, + const wchar src[], + const wchar fname[], + unsigned dstChars +); + +// c:\dir1\dir2\file.txt => c:\dir1\dir2\ * note trailing backslash +void PathRemoveFilename ( + wchar * dst, + const wchar src[], + unsigned dstChars +); + +// c:\file.txt => c:\dir1\dir2\file +void PathRemoveExtension ( + wchar * dst, + const wchar src[], + unsigned dstChars +); + +// c:\file + .out => c:\file.out +// c:\file. + .out => c:\file.out +// c:\file.txt + .out => c:\file.out +void PathSetExtension ( + wchar * dst, + const wchar src[], + const wchar ext[], + unsigned dstChars +); + +// c:\file + .out => c:\file.out +// c:\file. + .out => c:\file. +// c:\file.txt + .out => c:\file.txt +void PathAddExtension ( + wchar * dst, + const wchar src[], + const wchar ext[], + unsigned dstChars +); + +// c:\dir1\dir2\file.txt => file.txt +void PathRemoveDirectory ( + wchar * dst, + const wchar src[], + unsigned dstChars +); + +// c:\dir1\dir2\file.txt - c:\dir1 => .\dir2\file.txt +// c:\dir1\dir2\file1.txt - c:\dir1\dir4\file2.txt => ..\dir4\file2.txt +bool PathMakeRelative ( + wchar * dst, + unsigned fromFlags, + const wchar from[], + unsigned toFlags, + const wchar to[], + unsigned dstChars +); +bool PathIsRelative ( + const wchar src[] +); + +const wchar * PathFindFilename (const wchar path[]); +inline wchar * PathFindFilename (wchar * path) { + return const_cast(PathFindFilename(const_cast(path))); +} + +const wchar * PathFindExtension (const wchar path[]); +inline wchar * PathFindExtension (wchar * path) { + return const_cast(PathFindExtension(const_cast(path))); +} + + +/***************************************************************************** +* +* Directory functions +* +***/ + +// Create directory +enum EPathCreateDirError { + kPathCreateDirSuccess, + kPathCreateDirErrInvalidPath, + kPathCreateDirErrAccessDenied, + kPathCreateDirErrFileWithSameName, + kPathCreateDirErrDirExists, // Directory exists and kPathCreateDirFlagCreateNew was specified +}; + +// Setting this flag causes the function to create the entire directory +// tree from top to bottom. Clearing this flag causes the function to +// create only the last entry in the path. +const unsigned kPathCreateDirFlagEntireTree = 1<<0; + +// Setting this flag causes the function to create the last entry in the path +// ONLY if it doesn't already exist. If it does exist the function will return +// kPathCreateDirErrDirExistes. +const unsigned kPathCreateDirFlagCreateNew = 1<<1; + + +EPathCreateDirError PathCreateDirectory ( + const wchar path[], + unsigned flags +); +void PathDeleteDirectory ( + const wchar path[], + unsigned flags +); + + +// Set directory +bool PathSetCurrentDirectory (const wchar path[]); +void PathSetProgramDirectory (); + + +// Get directory +void PathGetProgramDirectory ( + wchar * dst, + unsigned dstChars +); +void PathGetCurrentDirectory ( + wchar * dst, + unsigned dstChars +); +void PathGetTempDirectory ( + wchar * dst, + unsigned dstChars +); + + +// Product and user-specific common directory locations +void PathGetUserDirectory ( + wchar * dst, + unsigned dstChars +); +void PathGetLogDirectory ( + wchar * dst, + unsigned dstChars +); +void PathGetInitDirectory ( + wchar * dst, + unsigned dstChars +); + + +/***************************************************************************** +* +* Email formatting functions +* +***/ + +// you may send nil for any fields you don't care about +void PathSplitEmail ( + const wchar emailAddr[], + wchar * user, + unsigned userChars, + wchar * domain, + unsigned domainChars, + wchar * tld, + unsigned tldChars, + wchar * subDomains, // (wchar *)&subs --> wchar subs[16][256]; + unsigned subDomainChars, // arrsize(subs[0]) --> 256 + unsigned subDomainCount // arrsize(subs) --> 16 +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPragma.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPragma.h new file mode 100644 index 00000000..2bc0d401 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPragma.h @@ -0,0 +1,39 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPragma.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTPRAGMA_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPragma.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTPRAGMA_H + + +#pragma warning(disable: 4800) // 'type' : forcing value to bool 'true' or 'false' (performance warning) +#pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPriQ.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPriQ.h new file mode 100644 index 00000000..276703fb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPriQ.h @@ -0,0 +1,471 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPriQ.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTPRIQ_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtPriQ.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTPRIQ_H + + +/**************************************************************************** +* +* Macros +* +***/ + +#define PRIORITY_TIME(class) TPriorityTime< class > +#define PRIORITY_NUMERIC(class,type) TPriorityNumeric< class,type > + +#define PRIQ(class,priority) TPriorityQueue< class,priority > +#define PRIQDECL(class,priority,field) TPriorityQueueDecl< class,priority,offsetof(class,field) > +#define PRIQDYN(class,priority) TPriorityQueueDyn< class,priority > + + +/**************************************************************************** +* +* class TPriorityQueue +* +***/ + +template +class TBasePriority; + +template +class TPriorityQueue { + +public: + TPriorityQueue (); + ~TPriorityQueue (); + + C * const & operator[] (unsigned index) const; + + void Clear (); + unsigned Count () const; + C * Delete (C * object); + C * Dequeue (); + void Enqueue (C * object); + C * const * Ptr () const; + C * Root () const; + C * const * Term () const; + void UnlinkAll (); + +public: + // Intentionally unimplemented + TPriorityQueue (TPriorityQueue const &); + TPriorityQueue const & operator= (TPriorityQueue const &); + +protected: + void SetLinkOffset (int offset); + +private: + unsigned IndexChild (unsigned index) const; + unsigned IndexParent (unsigned index) const; + void Link (unsigned index); + P * Priority (C * object); + P const * Priority (C const * object) const; + void Remove (unsigned index); + void Unlink (unsigned index); + + enum { LINK_OFFSET_UNINIT = 0xdddddddd }; + + int m_linkOffset; + ARRAY(C *) m_array; + + friend TBasePriority; +}; + +//=========================================================================== +template +inline C * const & TPriorityQueue::operator[] (unsigned index) const { + return m_array[index]; +} + +//=========================================================================== +template +inline TPriorityQueue::TPriorityQueue () : + m_linkOffset(LINK_OFFSET_UNINIT) { +} + +//=========================================================================== +template +inline TPriorityQueue::~TPriorityQueue () { + UnlinkAll(); +} + +//=========================================================================== +template +inline void TPriorityQueue::Clear () { + + // Deleting an object could cause other objects in the queue to be deleted + // so we can't make any assumptions about indices or counts of items in the array + while (C * head = Dequeue()) + DEL(head); + + m_array.Clear(); +} + +//=========================================================================== +template +inline unsigned TPriorityQueue::Count () const { + return m_array.Count(); +} + +//=========================================================================== +template +C * TPriorityQueue::Delete (C * object) { + + // get the object's priority queue and position + P * priority = Priority(object); + const TPriorityQueue * queue = priority->GetLink(); + unsigned index = priority->GetIndex(); + + // delete the object + DEL(object); + + // return the next object in that queue + if (queue && (index < queue->Count())) + return (*queue)[index]; + else + return nil; + +} + +//=========================================================================== +template +C * TPriorityQueue::Dequeue () { + if (!m_array.Count()) + return nil; + C * value = m_array[0]; + Remove(0); + return value; +} + +//=========================================================================== +template +void TPriorityQueue::Enqueue (C * object) { + P * priority = Priority(object); + + // Verify that the object is not already linked into a priority queue. + // The original implementation of this function silently refused to + // enqueue at a new priority if the object was already in this queue. + // Since this behavior requires callers to check whether the object is + // already enqueued, we now simply assert that. + ASSERT(!priority->IsLinked()); + + unsigned index = m_array.Add(object); + unsigned parent = IndexParent(index); + + // shift value toward root + while (index && priority->IsPriorityHigher(*Priority(m_array[parent]))) { + m_array[index] = m_array[parent]; + Link(index); + index = parent; + parent = IndexParent(index); + } + + // assign and link the new value + m_array[index] = object; + Link(index); +} + +//=========================================================================== +template +inline unsigned TPriorityQueue::IndexChild (unsigned index) const { + return (index << 1) + 1; +} + +//=========================================================================== +template +inline unsigned TPriorityQueue::IndexParent (unsigned index) const { + return (index - 1) >> 1; +} + +//=========================================================================== +template +inline void TPriorityQueue::Link (unsigned index) { + Priority(m_array[index])->Link(this, index); +} + +//=========================================================================== +template +inline P * TPriorityQueue::Priority (C * object) { + ASSERT(m_linkOffset != LINK_OFFSET_UNINIT); + return (P *)((byte *)object + m_linkOffset); +} + +//=========================================================================== +template +inline P const * TPriorityQueue::Priority (C const * object) const { + ASSERT(m_linkOffset != LINK_OFFSET_UNINIT); + return (P const *)((byte const *)object + m_linkOffset); +} + +//=========================================================================== +template +inline C * const * TPriorityQueue::Ptr () const { + return m_array.Ptr(); +} + +//=========================================================================== +template +void TPriorityQueue::Remove (unsigned index) { + + // reset the priority link fields + Unlink(index); + + // save the terminal leaf node + C * value = m_array.Pop(); + P * priority = Priority(value); + + const unsigned count = m_array.Count(); + if (count == index) + return; + + // rebalance upwards from the position of the deleted entry + unsigned parent; + unsigned entry = index; + if (entry && priority->IsPriorityHigher(*Priority(m_array[parent = IndexParent(entry)]))) { + do { + m_array[entry] = m_array[parent]; + Link(entry); + entry = parent; + } while (entry && priority->IsPriorityHigher(*Priority(m_array[parent = IndexParent(entry)]))); + m_array[entry] = value; + Link(entry); + + entry = index; + value = m_array[index]; + priority = Priority(value); + } + + // rebalance downwards from the position of the deleted entry + for (;;) { + unsigned child = IndexChild(entry); + if (child >= count) + break; + + unsigned sibling = child + 1; + if ( (sibling < count) && + (Priority(m_array[sibling])->IsPriorityHigher(*Priority(m_array[child]))) ) + child = sibling; + + if (priority->IsPriorityHigher(*Priority(m_array[child]))) + break; + + m_array[entry] = m_array[child]; + Link(entry); + entry = child; + } + + m_array[entry] = value; + Link(entry); +} + +//=========================================================================== +template +inline C * TPriorityQueue::Root () const { + return m_array.Count() ? m_array[0] : nil; +} + +//=========================================================================== +template +inline void TPriorityQueue::SetLinkOffset (int offset) { + ASSERT(m_linkOffset == LINK_OFFSET_UNINIT); + m_linkOffset = offset; +} + +//=========================================================================== +template +inline C * const * TPriorityQueue::Term () const { + return m_array.Term(); +} + +//=========================================================================== +template +inline void TPriorityQueue::Unlink (unsigned index) { + Priority(m_array[index])->Link(nil, 0); +} + +//=========================================================================== +template +inline void TPriorityQueue::UnlinkAll () { + for (unsigned loop = m_array.Count(); loop--; ) + Unlink(loop); + m_array.ZeroCount(); +} + + +/**************************************************************************** +* +* TPriorityQueueDecl +* +***/ + +template +class TPriorityQueueDecl : public TPriorityQueue { +public: + TPriorityQueueDecl () { SetLinkOffset(linkOffset); } +}; + + +/**************************************************************************** +* +* TPriorityQueueDyn +* +***/ + +template +class TPriorityQueueDyn : public TPriorityQueue { +public: + void Initialize (int linkOffset) { SetLinkOffset(linkOffset); } +}; + + +/**************************************************************************** +* +* class TBasePriority +* +***/ + +template +class TBasePriority { + +public: + TBasePriority () : m_queue(nil), m_index(0) { } + virtual ~TBasePriority () { Unlink(); } + + void Unlink () { if (m_queue) m_queue->Remove(m_index); } + bool IsLinked () const { return m_queue != nil; } + +public: + TBasePriority (const TBasePriority &); + const TBasePriority & operator= (const TBasePriority &); + +protected: + void Relink (); + +private: + void Link (TPriorityQueue * queue, unsigned index); + const TPriorityQueue * GetLink () const { return m_queue; } + unsigned GetIndex () const { return m_index; } + +private: + TPriorityQueue * m_queue; + unsigned m_index; + + friend TPriorityQueue; +}; + +//=========================================================================== +template +inline void TBasePriority::Link (TPriorityQueue * queue, unsigned index) { + m_queue = queue; + m_index = index; +} + +//=========================================================================== +template +void TBasePriority::Relink () { + + // cache m_queue, since m_queue->Remove() will set it to nil + TPriorityQueue * queue = m_queue; + if (!queue) + return; + C * object = (*queue)[m_index]; + queue->Remove(m_index); + queue->Enqueue(object); +} + + +/**************************************************************************** +* +* class TPriorityNumeric +* +***/ + +template +class TPriorityNumeric : public TBasePriority< C, TPriorityNumeric > { + +public: + TPriorityNumeric () : m_value(0) { } + TPriorityNumeric (T value) : m_value(value) { } + void Set (T value) { + if (value == m_value) + return; + m_value = value; + Relink(); + } + T Get () const { + return m_value; + } + bool IsPriorityHigher (const TPriorityNumeric & source) { + return m_value > source.m_value; + } + bool IsPriorityHigher (T value) const { + return m_value > value; + } + +private: + T m_value; +}; + +/**************************************************************************** +* +* class TPriorityTime +* +***/ + +template +class TPriorityTime : public TBasePriority< C, TPriorityTime > { + +public: + TPriorityTime () : m_time(0) { } + TPriorityTime (unsigned time) : m_time(time) { } + + void Set (unsigned time) { + if (m_time == time) + return; + m_time = time; + Relink(); + } + unsigned Get () const { + return m_time; + } + bool IsPriorityHigher (const TPriorityTime & source) const { + return (int)(m_time - source.m_time) < 0; + } + bool IsPriorityHigher (unsigned time) const { + return (int)(m_time - time) < 0; + } + +private: + unsigned m_time; +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRand.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRand.cpp new file mode 100644 index 00000000..dc971b4e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRand.cpp @@ -0,0 +1,169 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRand.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private +* +***/ + +class RandomContext { + dword m_seed; + dword m_value; + + void UpdateValue (); + +public: + RandomContext (); + + void Reset (); + void SetSeed (unsigned seed); + float GetFloat (); + float GetFloat (float minVal, float maxVal); + unsigned GetUnsigned (); + unsigned GetUnsigned (unsigned minVal, unsigned maxVal); +}; + + +/***************************************************************************** +* +* Private data +* +***/ + +static const dword kDefaultRandomSeed = 0x075bd924; + +static RandomContext s_random; + + +/***************************************************************************** +* +* RandomContext +* +***/ + +//============================================================================ +RandomContext::RandomContext () +: m_seed(kDefaultRandomSeed) +{ + Reset(); +} + +//============================================================================ +void RandomContext::UpdateValue () { + const dword A = 0xbc8f; + const dword Q = 0xadc8; + const dword R = 0x0d47; + + dword div = m_value / Q; + m_value = A * (m_value - Q * div) - R * div; + if (m_value > kRandomMax) + m_value -= kRandomMax + 1; + if (!m_value) + m_value = kDefaultRandomSeed; +} + +//============================================================================ +void RandomContext::Reset () { + m_value = m_seed; +} + +//============================================================================ +void RandomContext::SetSeed (unsigned seed) { + // Never allow a seed of zero + m_seed = seed ? seed : kDefaultRandomSeed; + Reset(); +} + +//============================================================================ +float RandomContext::GetFloat () { + UpdateValue(); + return m_value * (1.0f / kRandomMax); +} + +//============================================================================ +float RandomContext::GetFloat (float minVal, float maxVal) { + float value = GetFloat(); + return minVal + value * (maxVal - minVal); +} + +//============================================================================ +unsigned RandomContext::GetUnsigned () { + UpdateValue(); + return (unsigned)m_value; +} + +//============================================================================ +unsigned RandomContext::GetUnsigned (unsigned minVal, unsigned maxVal) { + unsigned value = GetUnsigned(); + return minVal + value % (maxVal - minVal + 1); +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void RandReset () { + s_random.Reset(); +} + +//============================================================================ +void RandSetSeed (unsigned seed) { + s_random.SetSeed(seed); +} + +//============================================================================ +float RandFloat () { + return s_random.GetFloat(); +} + +//============================================================================ +float RandFloat (float minVal, float maxVal) { + return s_random.GetFloat(minVal, maxVal); +} + +//============================================================================ +unsigned RandUnsigned () { + return s_random.GetUnsigned(); +} + +//============================================================================ +unsigned RandUnsigned (unsigned minVal, unsigned maxVal) { + return s_random.GetUnsigned(minVal, maxVal); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRand.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRand.h new file mode 100644 index 00000000..63d78741 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRand.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRand.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTRAND_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRand.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTRAND_H + + +/***************************************************************************** +* +* Psuedo-random number generator +* +***/ + +const dword kRandomMax = 0x7fffffff; + +void RandReset (); +void RandSetSeed (unsigned seed); +float RandFloat (); +float RandFloat (float minVal, float maxVal); +unsigned RandUnsigned (); +unsigned RandUnsigned (unsigned minVal, unsigned maxVal); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRef.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRef.h new file mode 100644 index 00000000..2ff3ef29 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRef.h @@ -0,0 +1,169 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRef.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTREF_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtRef.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTREF_H + + +/***************************************************************************** +* +* Debug macros +* +***/ + +#ifdef REFCOUNT_DEBUGGING + #define REFTRACE DEBUG_MSG +#else + #define REFTRACE NULL_STMT +#endif + + +/**************************************************************************** +* +* AtomicRef +* Thread safe reference count +* +***/ + +class AtomicRef { +#ifdef HS_DEBUGGING + bool zeroed; +#endif +public: + inline AtomicRef () + : m_ref(0) + #ifdef HS_DEBUGGING + , zeroed(false) + #endif + {} + + inline void AcknowledgeZeroRef () { + #ifdef HS_DEBUGGING + zeroed = false; + #endif + } + + inline long IncRef () { + #ifdef HS_DEBUGGING + ASSERT(!zeroed); + #endif + long prev = AtomicAdd(&m_ref, 1); + REFTRACE("Inc %p: %u", this, prev+1); + return prev+1; + } + inline long IncRef (const char tag[]) { + #ifdef HS_DEBUGGING + ASSERT(!zeroed); + #endif + long prev = AtomicAdd(&m_ref, 1); + ref(tag); + REFTRACE("Inc %p %s: %u", this, tag, prev+1); + return prev+1; + } + inline long IncRef (unsigned n) { + #ifdef HS_DEBUGGING + ASSERT(!zeroed); + #endif + long prev = AtomicAdd(&m_ref, n); + REFTRACE("Inc %p: %u", this, prev+n); + return prev+n; + } + inline long IncRef (unsigned n, const char tag[]) { + #ifdef HS_DEBUGGING + ASSERT(!zeroed); + #endif + long prev = AtomicAdd(&m_ref, n); + ref(tag); + REFTRACE("Inc %p %s: %u", this, tag, prev+n); + return prev+n; + } + + inline long DecRef () { + #ifdef HS_DEBUGGING + ASSERT(!zeroed); + #endif + long prev; + if ((prev = AtomicAdd(&m_ref, -1)) == 1) { + #ifdef HS_DEBUGGING + zeroed = true; + #endif + OnZeroRef(); + } + REFTRACE("Dec %p: %u", this, prev-1); + return prev-1; + } + inline long DecRef (const char tag[]) { + #ifdef HS_DEBUGGING + ASSERT(!zeroed); + #endif + long prev; + if ((prev = AtomicAdd(&m_ref, -1)) == 1) { + #ifdef HS_DEBUGGING + zeroed = true; + #endif + OnZeroRef(); + } + ref(tag); + REFTRACE("Dec %p %s: %u", this, tag, prev-1); + return prev-1; + } + + inline void TransferRef ( + const char oldTag[], + const char newTag[] + ) { + #ifdef HS_DEBUGGING + ASSERT(!zeroed); + #endif + ref(oldTag); + ref(newTag); + REFTRACE("Inc %p %s: (xfer)", this, newTag); + REFTRACE("Dec %p %s: (xfer)", this, oldTag); + } + + inline unsigned GetRefCount () { + return m_ref; + } + + inline virtual void OnZeroRef () { + DEL(this); + } + +protected: + inline virtual ~AtomicRef () { + ASSERT(!m_ref); + } + +private: + long m_ref; +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSkipList.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSkipList.h new file mode 100644 index 00000000..c77f427c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSkipList.h @@ -0,0 +1,461 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSkipList.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSKIPLIST_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSkipList.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSKIPLIST_H + + +/***************************************************************************** +* +* Macros +* +***/ + +#define SKIPLIST(type, keyType, keyField, cmp) TSkipList< type, keyType, offsetof(type, keyField), cmp > +#define SKIPLIST_NUMERIC(type, keyType, keyField) SKIPLIST(type, keyType, keyField, TSkipListNumericCmp) +#define SKIPLIST_STRING(type, keyType, keyField) SKIPLIST(type, keyType, keyField, TSkipListStringCmp) +#define SKIPLIST_STRINGI(type, keyType, keyField) SKIPLIST(type, keyType, keyField, TSkipListStringCmpI) + + + +/***************************************************************************** +* +* Typedefs +* +***/ + +typedef void * SkipListTag; + + +/***************************************************************************** +* +* Comparers +* +***/ + + +template +class TSkipListNumericCmp { +public: + static bool Eq (const K & a, const K & b) { return a == b; } + static bool Lt (const K & a, const K & b) { return a < b; } +}; + +template +class TSkipListStringCmp { +public: + static bool Eq (const K & a, const K & b) { return StrCmp(a, b, (unsigned)-1) == 0; } + static bool Lt (const K & a, const K & b) { return StrCmp(a, b, (unsigned)-1) < 0; } +}; + +template +class TSkipListStringCmpI { +public: + static bool Eq (const K & a, const K & b) { return StrCmpI(a, b, (unsigned)-1) == 0; } + static bool Lt (const K & a, const K & b) { return StrCmpI(a, b, (unsigned)-1) < 0; } +}; + + +/***************************************************************************** +* +* TSkipList +* +***/ + +template +class TSkipList { +private: + enum { kMaxLevels = 32 }; + + template + struct TNode { + const K * key; + T * object; + unsigned level; + TNode * prev; + TNode * next[1]; // variable size array + }; + typedef TNode Node; + + unsigned m_level; + Node * m_head; + Node * m_stop; + unsigned m_randomBits; + unsigned m_randomsLeft; + + Node * AllocNode (unsigned levels); + void FreeNode (Node * node); + unsigned RandomLevel (); + +public: + inline TSkipList (); + inline ~TSkipList (); + inline void Clear (); + inline void Delete (T * object); + inline T * Find (const K & key, SkipListTag * tag = nil) const; + inline T * FindNext (SkipListTag * tag) const; + inline T * Head (SkipListTag * tag) const; + inline T * Next (SkipListTag * tag) const; + inline T * Prev (SkipListTag * tag) const; + inline T * Tail (SkipListTag * tag) const; + inline void Link (T * object); + inline void Unlink (T * object); + inline void Unlink (SkipListTag * tag); + inline void UnlinkAll (); + + #ifdef HS_DEBUGGING + inline void Print () const; + #endif +}; + + +/***************************************************************************** +* +* TSkipList private member functions +* +***/ + +//============================================================================ +template +typename TSkipList::TNode * TSkipList::AllocNode (unsigned level) { + + unsigned size = offsetof(Node, next) + (level + 1) * sizeof(Node); + Node * node = (Node *)ALLOC(size); + node->level = level; + return node; +} + +//============================================================================ +template +void TSkipList::FreeNode (TNode * node) { + + FREE(node); +} + +//============================================================================ +template +unsigned TSkipList::RandomLevel () { + + unsigned level = 0; + unsigned bits = 0; + + while (!bits) { + bits = m_randomBits % 4; + if (!bits) + ++level; + m_randomBits >>= 2; + m_randomsLeft -= 2; + if (!m_randomsLeft) { + m_randomBits = RandUnsigned(); + m_randomsLeft = 30; + } + } + + return level; +} + + +/***************************************************************************** +* +* TSkipList public member functions +* +***/ + +//============================================================================ +template +TSkipList::TSkipList () { + + m_level = 0; + m_head = AllocNode(kMaxLevels); + m_stop = AllocNode(0); + m_randomBits = RandUnsigned(); + m_randomsLeft = 30; + + // Initialize header and stop skip node pointers + m_stop->prev = m_head; + m_stop->object = nil; + m_stop->next[0] = nil; + m_head->object = nil; + for (unsigned index = 0; index < kMaxLevels; ++index) + m_head->next[index] = m_stop; +} + +//============================================================================ +template +TSkipList::~TSkipList () { + + UnlinkAll(); + ASSERT(m_stop->prev == m_head); + FreeNode(m_head); + FreeNode(m_stop); +} + +//============================================================================ +template +void TSkipList::Clear () { + + Node * ptr = m_head->next[0]; + while (ptr != m_stop) { + Node * next = ptr->next[0]; + DEL(ptr->object); + FreeNode(ptr); + ptr = next; + } + + m_stop->prev = m_head; + for (unsigned index = 0; index < kMaxLevels; ++index) + m_head->next[index] = m_stop; + m_level = 0; +} + +//============================================================================ +template +void TSkipList::Delete (T * object) { + + Unlink(object); + DEL(object); +} + +//============================================================================ +template +T * TSkipList::Find (const K & key, SkipListTag * tag) const { + + Node * node = m_head; + + m_stop->key = &key; + for (int level = (int)m_level; level >= 0; --level) + while (Cmp::Lt(*node->next[level]->key, key)) + node = node->next[level]; + + node = node->next[0]; + if (node != m_stop && Cmp::Eq(*node->key, *m_stop->key)) { + if (tag) + *tag = node; + return node->object; + } + else { + if (tag) + *tag = nil; + return nil; + } +} + +//============================================================================ +template +T * TSkipList::FindNext (SkipListTag * tag) const { + + Node * node = (Node *)*tag; + + m_stop->key = node->key; + for (int level = (int)node->level; level >= 0; --level) + while (Cmp::Lt(*node->next[level]->key, *m_stop->key)) + node = node->next[level]; + + node = node->next[0]; + if (node != m_stop && Cmp::Eq(*node->key, *m_stop->key)) { + *tag = node; + return node->object; + } + else { + *tag = nil; + return nil; + } +} + +//============================================================================ +template +T * TSkipList::Head (SkipListTag * tag) const { + + ASSERT(tag); + Node * first = m_head->next[0]; + if (first == m_stop) { + *tag = nil; + return nil; + } + + *tag = first; + return first->object; +} + +//============================================================================ +template +T * TSkipList::Next (SkipListTag * tag) const { + + ASSERT(tag); + Node * node = (Node *)*tag; + ASSERT(node); + if (node->next[0] == m_stop) { + *tag = nil; + return nil; + } + + *tag = node->next[0]; + return node->next[0]->object; +} + +//============================================================================ +template +T * TSkipList::Prev (SkipListTag * tag) const { + + ASSERT(tag); + Node * node = (Node *)*tag; + ASSERT(node); + if (node->prev == m_head) { + *tag = nil; + return nil; + } + + *tag = node->prev; + return node->prev->object; +} + +//============================================================================ +template +T * TSkipList::Tail (SkipListTag * tag) const { + + ASSERT(tag); + Node * last = m_stop->prev; + if (last == m_head) { + *tag = nil; + return nil; + } + + *tag = last; + return last->object; +} + +//============================================================================ +template +void TSkipList::Link (T * object) { + + const K * key = (const K *)((const byte *)object + keyOffset); + + // Find the node's insertion point + m_stop->key = key; + Node * update[kMaxLevels]; + Node * node = m_head; + for (int level = (int)m_level; level >= 0; --level) { + while (Cmp::Lt(*node->next[level]->key, *key)) + node = node->next[level]; + update[level] = node; + } + node = node->next[0]; + + { + // Select a level for the skip node + unsigned newLevel = RandomLevel(); + if (newLevel > m_level) { + if (m_level < kMaxLevels - 1) { + newLevel = ++m_level; + update[newLevel] = m_head; + } + else + newLevel = m_level; + } + + // Create the node and insert it into the skip list + Node * node = AllocNode(newLevel); + node->key = key; + node->object = object; + for (unsigned level = newLevel; level >= 1; --level) { + node->next[level] = update[level]->next[level]; + update[level]->next[level] = node; + } + node->prev = update[0]; + node->next[0] = update[0]->next[0]; + update[0]->next[0]->prev = node; + update[0]->next[0] = node; + } +} + +//============================================================================ +template +void TSkipList::Unlink (T * object) { + + const K * key = (const K *)((const byte *)object + keyOffset); + + Node * node = m_head; + Node * update[kMaxLevels]; + int level = m_level; + + for (;;) { + // Find the node being unlinked + m_stop->key = key; + for (; level >= 0; --level) { + while (Cmp::Lt(*node->next[level]->key, *key)) + node = node->next[level]; + update[level] = node; + } + node = node->next[0]; + + // Node wasn't found so do nothing + if (*node->key != *key || node == m_stop) + return; + + if (node->object == object) + break; + } + + // Update all links + for (level = m_level; level >= 1; --level) { + if (update[level]->next[level] != node) + continue; + update[level]->next[level] = node->next[level]; + } + ASSERT(update[0]->next[0] == node); + node->next[0]->prev = update[0]; + update[0]->next[0] = node->next[0]; + + // Update header + while (m_level && m_head->next[m_level] == m_stop) + --m_level; + + FreeNode(node); +} + +//============================================================================ +template +void TSkipList::UnlinkAll () { + + Node * ptr = m_head->next[0]; + while (ptr != m_stop) { + Node * next = ptr->next[0]; + FreeNode(ptr); + ptr = next; + } + + m_stop->prev = m_head; + for (unsigned index = 0; index < kMaxLevels; ++index) + m_head->next[index] = m_stop; + m_level = 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSort.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSort.h new file mode 100644 index 00000000..199f96d4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSort.h @@ -0,0 +1,225 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSort.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSORT_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSort.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSORT_H + + +/**************************************************************************** +* +* QSORT +* +* This version of QuickSort is similar to the one in the C runtime library, +* but is implemented as a macro to allow more flexible usage. +* +* With the C runtime library version, when data external to the sort array +* is needed to make sorting decisions, that data must be stored in file- or +* global-scope variables. This creates thread safety problems which can +* only be resolved through the use of synchronization objects. The version +* of QuickSort provided here does not require function calls to make +* sorting decisions, so all data can be kept in stack variables. +* +* The expression used for making comparisons allows the same return values +* as the comparison function used by the C runtime library, and can in fact +* be a function call to a comparison function that was originally designed +* for use by the C runtime library. +* > 0 if elem1 greater than elem2 +* = 0 if elem1 equivalent to elem2 +* < 0 if elem1 less than elem2 +* +* However, this implementation of QuickSort never requires a distinction +* between the case where elem1 is less than elem2 and the case where elem1 +* is equivalent to elem2, so it is possible to use the following more +* efficient return values: +* > 0 if elem1 is greater than elem2 +* <= 0 if elem1 is less than or equivalent to elem2 +* +***/ + +//=========================================================================== +#define QSORT(T, ptr, count, expr) { \ + \ + /* Largest possible stack count required is 1 + log2(size) */ \ + T * loStack[32]; \ + T * hiStack[32]; \ + unsigned stackPos = 0; \ + \ + if ((count) >= 2) { \ + T * lo = (ptr); \ + T * hi = lo + (count); \ + for (;;) { \ + \ + /* Pick a partitioning element */ \ + T * mid = lo + (hi - lo) / 2; \ + \ + /* Swap it to the beginning of the array */ \ + SWAP(*mid, *lo); \ + \ + /* Partition the array into three pieces, one consisting of */ \ + /* elements <= the partitioning element, one of elements */ \ + /* equal to it, and one of elements >= to it. */ \ + T * loPart = lo; \ + T * hiPart = hi; \ + for (;;) { \ + /* val(i) <= val(lo) for lo <= i <= loPart */ \ + /* val(i) >= val(lo) for hiPart <= i <= hi */ \ + \ + for (;;) { \ + if (++loPart == hi) \ + break; \ + T const & elem1 = *loPart; \ + T const & elem2 = *lo; \ + int result = (expr); \ + if (result > 0) \ + break; \ + } \ + \ + for (;;) { \ + if (--hiPart == lo) \ + break; \ + T const & elem1 = *lo; \ + T const & elem2 = *hiPart; \ + int result = (expr); \ + if (result > 0) \ + break; \ + } \ + \ + if (hiPart < loPart) \ + break; \ + \ + /* val(loPart) > val(lo) */ \ + /* val(hiPart) < val(lo) */ \ + \ + SWAP(*loPart, *hiPart); \ + \ + /* val(loPart) < val(lo) */ \ + /* val(hiPart) > val(lo) */ \ + } \ + \ + /* val(i) <= val(lo) for lo <= i <= hiPart */ \ + /* val(i) == val(lo) for hiPart < i < loPart */ \ + /* val(i) >= val(lo) for loPart <= i <= hi */ \ + \ + /* Put the partitioning element in place */ \ + SWAP(*lo, *hiPart); \ + \ + /* val(i) <= val(hiPart) for lo <= i < hiPart */ \ + /* val(i) == val(lo) for hiPart <= i < loPart */ \ + /* val(i) >= val(hiPart) for loPart <= i < hi */ \ + \ + /* Sort the subarrays [lo, hiPart-1] and [loPart, hi]. */ \ + /* We sort the smaller one first to minimize stack usage. */ \ + if (hiPart - lo >= hi - loPart) { \ + if (lo + 1 < hiPart) { \ + /* Store the bigger subarray */ \ + loStack[stackPos] = lo; \ + hiStack[stackPos] = hiPart; \ + ++stackPos; \ + } \ + if (loPart + 1 < hi) { \ + /* Sort the smaller subarray */ \ + lo = loPart; \ + continue; \ + } \ + } \ + else { \ + if (loPart + 1 < hi) { \ + /* Store the bigger subarray */ \ + loStack[stackPos] = loPart; \ + hiStack[stackPos] = hi; \ + ++stackPos; \ + } \ + if (lo + 1 < hiPart) { \ + /* Sort the smaller subarray */ \ + hi = hiPart; \ + continue; \ + } \ + } \ + \ + /* Pop the next subarray off the stack */ \ + if (stackPos--) { \ + lo = loStack[stackPos]; \ + hi = hiStack[stackPos]; \ + continue; \ + } \ + \ + break; \ + } \ + } \ +} + + +/**************************************************************************** +* +* BSEARCH +* +* This macro binary searches a sorted array to find an existing entry or +* the position where a TRACKED_NEW entry should be placed. It returns the index of +* the first entry for which the expression is false (zero or negative), or +* count if the expression is true (positive) for all entries. +* +* Typically the expression will return: +* > 0 if (sortKey > elem) +* <= 0 if (sortKey <= elem) +* +* The final parameter to the macro is the address of a variable which is +* filled with the resulting index. +* +***/ + +//=========================================================================== +#define BSEARCH(T, ptr, count, expr, addrOfIndex) { \ + \ + const T * low = (ptr); \ + const T * high = (ptr) + (count); /* first entry for which */ \ + /* expr is false */ \ + \ + if (low != high) \ + for (;;) { \ + const T & elem = *(low + (high - low) / 2); \ + int result = (expr); \ + if (result > 0) { \ + if (&elem == low) \ + break; \ + low = &elem; \ + } \ + else { \ + high = &elem; \ + if (&elem == low) \ + break; \ + } \ + } \ + \ + *(addrOfIndex) = high - (ptr); \ + \ +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSpareList.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSpareList.cpp new file mode 100644 index 00000000..611ecc23 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSpareList.cpp @@ -0,0 +1,155 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSpareList.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +CBaseSpareList::CBaseSpareList () +: m_allocHead(nil), + m_spareHead(nil), + m_chunkSize(0) +{ + #ifdef SPARELIST_TRACK_MEMORY + m_unfreedObjects = 0; + #endif +} + +//=========================================================================== +void * CBaseSpareList::Alloc (unsigned objectSize, const char typeName[]) { + // if there aren't any spare nodes available then make more + if (!m_spareHead) + GrowSpareList(objectSize, typeName); + + // dequeue the head of the spare list + void * const object = m_spareHead; + m_spareHead = m_spareHead->spareNext; + #ifdef SPARELIST_TRACK_MEMORY + m_unfreedObjects++; + #endif + + // initialize memory to a freaky value in debug mode + #ifdef HS_DEBUGGING + MemSet(object, (byte) ((unsigned) object >> 4), objectSize); + #endif + + return object; +} + +//=========================================================================== +void CBaseSpareList::Free (void * object, unsigned objectSize) { + // initialize memory to a freaky value in debug mode + #ifdef HS_DEBUGGING + MemSet(object, (byte) ((unsigned) object >> 4), objectSize); + #else + ref(objectSize); + #endif + + // link memory block onto head of spare list + ((SpareNode *) object)->spareNext = m_spareHead; + m_spareHead = (SpareNode *) object; + #ifdef SPARELIST_TRACK_MEMORY + m_unfreedObjects--; + #endif +} + +//=========================================================================== +void CBaseSpareList::GrowSpareList (unsigned objectSize, const char typeName[]) { + // Grow the allocation by a substantial amount each time + // to reduce the time spent in memory managament + m_chunkSize *= 2; + const unsigned MIN_ALLOC = max(1, 256/objectSize); + const unsigned MAX_ALLOC = max(512, 32*1024/objectSize); + if (m_chunkSize < MIN_ALLOC) + m_chunkSize = MIN_ALLOC; + else if (m_chunkSize > MAX_ALLOC) + m_chunkSize = MAX_ALLOC; + + // allocate a block of memory to hold a bunch + // of T-objects, but allocate them as "raw" memory + AllocNode * allocNode = (AllocNode *) MemAlloc( + sizeof(AllocNode) + objectSize * m_chunkSize, + 0, // flags + typeName, // file + 0 // line + ); + + // link allocation onto head of allocation list + allocNode->allocNext = m_allocHead; + m_allocHead = allocNode; + + // chain newly created raw memory units together onto the spare list + SpareNode * spareCurr = (SpareNode *) (allocNode + 1); + SpareNode * spareEnd = (SpareNode *) ((byte *) spareCurr + objectSize * m_chunkSize); + do { + spareCurr->spareNext = m_spareHead; + m_spareHead = spareCurr; + spareCurr = (SpareNode *) ((byte *) spareCurr + objectSize); + } while (spareCurr < spareEnd); +} + +//=========================================================================== +void CBaseSpareList::CleanUp (const char typeName[]) { + // warn of resource leaks + #ifdef SPARELIST_TRACK_MEMORY + if (m_unfreedObjects && !ErrorGetOption(kErrOptDisableMemLeakChecking)) { + #ifdef CLIENT + { + char buffer[256]; + StrPrintf(buffer, arrsize(buffer), "Memory leak: %s", typeName); + FATAL(buffer); + } + #else + { + DEBUG_MSG("Memory leak: %s", typeName); + } + #endif + } + #else + ref(typeName); + #endif + + // walk chain of AllocNodes and free each of them + while (m_allocHead) { + AllocNode * allocNext = m_allocHead->allocNext; + FREE(m_allocHead); + m_allocHead = allocNext; + } + + m_spareHead = nil; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSpareList.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSpareList.h new file mode 100644 index 00000000..fa1af543 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSpareList.h @@ -0,0 +1,131 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSpareList.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSPARELIST_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSpareList.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSPARELIST_H + + + +#ifdef HS_DEBUGGING +#define SPARELIST_TRACK_MEMORY +#endif + + +/**************************************************************************** +* +* CBaseSpareList +* +***/ + +class CBaseSpareList { +public: + CBaseSpareList (); + +protected: + struct SpareNode { + SpareNode * spareNext; + }; + SpareNode * m_spareHead; + + void * Alloc (unsigned objectSize, const char typeName[]); + void CleanUp (const char typeName[]); + void Free (void * object, unsigned objectSize); + +private: + union AllocNode { + AllocNode * allocNext; + qword align; + }; + AllocNode * m_allocHead; + unsigned m_chunkSize; + + #ifdef SPARELIST_TRACK_MEMORY + unsigned m_unfreedObjects; + #endif + + void GrowSpareList (unsigned objectSize, const char typeName[]); +}; + + +/**************************************************************************** +* +* TSpareList +* +***/ + +template +class TSpareList : public CBaseSpareList { +private: + enum { OBJECT_SIZE = MAX(sizeof(T), sizeof(SpareNode)) }; + +public: + ~TSpareList () { CleanUp(); } + + void * Alloc (); + void CleanUp (); + void Delete (T * node); + void Free (T * node); + T * New (); +}; + + +//=========================================================================== +template +void * TSpareList::Alloc () { + return CBaseSpareList::Alloc(OBJECT_SIZE, typeid(T).raw_name()); +} + +//=========================================================================== +template +void TSpareList::CleanUp () { + CBaseSpareList::CleanUp(typeid(T).raw_name()); +} + +//=========================================================================== +template +void TSpareList::Delete (T * node) { + node->~T(); + CBaseSpareList::Free(node, OBJECT_SIZE); +} + +//=========================================================================== +template +void TSpareList::Free (T * node) { + CBaseSpareList::Free(node, OBJECT_SIZE); +} + +//=========================================================================== +template +T * TSpareList::New () { + return new(CBaseSpareList::Alloc(OBJECT_SIZE, typeid(T).raw_name())) T; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.cpp new file mode 100644 index 00000000..cec6c1ac --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.cpp @@ -0,0 +1,756 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private data +* +***/ + +// These random values were generated by the radioactive decay based +// random number generator at www.fourmilab.ch +static dword s_hashValue[] = { + 0xc30d2a72, 0x15efaec1, 0xd250c7d9, 0xaf3c60a8, + 0x9608ae8f, 0x452c0e11, 0xb6840ffd, 0x3e36c913, + 0x2864eace, 0x9b0a17d6, 0x108da74b, 0xf2c479c1, + 0x8b4dd597, 0x97199bc0, 0x621f0cce, 0x1658553e, +}; + + +/**************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +template +static chartype * IStrDup (const chartype str[]) { + unsigned chars = IStrLen(str) + 1; + chartype * buffer = (chartype *)ALLOC(chars * sizeof(chartype)); + IStrCopy(buffer, str, chars); + return buffer; +} + +//=========================================================================== +template +static chartype * IStrDupLen (const chartype str[], unsigned chars) { + unsigned len = IStrLen(str) + 1; + if (len > chars) + len = chars; + chartype * buffer = (chartype *)ALLOC(len * sizeof(chartype)); + IStrCopy(buffer, str, len); + return buffer; +} + +//=========================================================================== +template +static chartype * IStrChr (chartype * str, findchartype ch, unsigned chars) { + for (; chars--; ++str) + if (*str == ch) + return str; + else if (!*str) + break; + return nil; +} + +//=========================================================================== +template +static chartype * IStrChrR (chartype * str, findchartype ch) { + chartype * start = str; + for (; *str; ++str) + NULL_STMT; + while (str-- > start) + if (*str == ch) + return str; + return NULL; +} + +//=========================================================================== +static inline bool ICharUnicodeToUtf8 (char ** dest, const wchar * source[], unsigned destChars) { + unsigned ch = *(*source)++; + bool result = false; + if (ch < 0x80) { + if (destChars >= 1) { + *(*dest)++ = (char)ch; + result = true; + } + } + else if (ch < 0x800) { + if (destChars >= 2) { + *(*dest)++ = (char)(0xc0 | (ch >> 6)); + *(*dest)++ = (char)(0x80 | (ch & 0x3f)); + result = true; + } + } + else { + if (destChars >= 3) { + *(*dest)++ = (char)(0xe0 | (ch >> 12)); + *(*dest)++ = (char)(0x80 | ((ch >> 6) & 0x3f)); + *(*dest)++ = (char)(0x80 | (ch & 0x3f)); + result = true; + } + } + return result; +} + +//=========================================================================== +static inline void ICharUtf8ToUnicode (wchar ** dest, const char * source[]) { + unsigned result, remaining; + if ((**source & 0xf0) == 0xe0) { + result = *(*source)++ & 0x0f; + remaining = 2; + } + else if ((**source & 0xe0) == 0xc0) { + result = *(*source)++ & 0x1f; + remaining = 1; + } + else if ((**source & 0x80) == 0x00) { + result = *(*source)++; + remaining = 0; + } + else { + // unsupported code sequence (>0xffff) + ++(*source); + return; + } + for (; remaining-- && *source; ++*source) + if ((**source & 0xc0) == 0x80) + result = (result << 6) | (**source & 0x3f); + *(*dest)++ = (wchar)result; +} + +//=========================================================================== +template +static unsigned IStrPrintfValidate (chartype * dest, unsigned count, int result) { + if (!count) + return 0; + ASSERT(result <= (int)count); + if ((result < 0) || (result == (int)count)) { + dest[count - 1] = 0; + return count - 1; + } + else + return (unsigned)result; +} + +//=========================================================================== +template +static int IStrCmp (const chartype str1[], const chartype str2[], unsigned chars) { + for (; chars--; ++str1, ++str2) { + if (*str1 != *str2) + return (*str1 > *str2) ? 1 : -1; + if (!*str1) + return 0; + } + return 0; +} + +//=========================================================================== +template +static int IStrCmpI (const chartype str1[], const chartype str2[], unsigned chars) { + while (chars--) { + chartype ch1 = CharLowerFast(*str1++); + chartype ch2 = CharLowerFast(*str2++); + if (ch1 != ch2) + return (ch1 > ch2) ? 1 : -1; + if (!ch1) + return 0; + } + return 0; +} + +//=========================================================================== +template +static void IStrCopy (chartype * dest, const chartype source[], unsigned chars) { + while ((chars > 1) && ((*dest = *source++) != 0)) { + --chars; + ++dest; + } + if (chars) + *dest = 0; +} + +//=========================================================================== +// returns StrLen(dest) +template +static unsigned IStrCopyLen (chartype * dest, const chartype source[], unsigned chars) { + chartype * const start = dest; + while ((chars > 1) && ((*dest = *source++) != 0)) { + --chars; + ++dest; + } + if (chars) + *dest = 0; + return dest - start; +} + +//=========================================================================== +template +static void IStrPack (chartype * dest, const chartype source[], unsigned chars) { + while ((chars > 1) && *dest) { + --chars; + ++dest; + } + while ((chars > 1) && ((*dest = *source++) != 0)) { + --chars; + ++dest; + } + if (chars) + *dest = 0; +} + +//=========================================================================== +template +static chartype * IStrStr (chartype source[], const chartype match[]) { + if (!*match) + return source; + + for (chartype * curr = source; *curr; ++curr) { + chartype * s1 = curr; + const chartype * s2 = match; + while (*s1 && *s2 && *s1 == *s2) + s1++, s2++; + if (!*s2) + return curr; + } + + return nil; +} + + +//=========================================================================== +template +static chartype * IStrStrI (chartype source[], const chartype match[]) { + if (!*match) + return source; + + for (chartype * curr = source; *curr; ++curr) { + chartype * s1 = curr; + const chartype * s2 = match; + while (*s1 && *s2 && (CharLowerFast(*s1) == CharLowerFast(*s2))) + s1++, s2++; + if (!*s2) + return curr; + } + + return nil; +} + +//=========================================================================== +template +static unsigned IStrLen (const chartype str[]) { + unsigned chars = 0; + for (; *str++; ++chars) + NULL_STMT; + return chars; +} + +//=========================================================================== +template +static void IStrLower (chartype * dest, unsigned chars) { + while ((chars > 1) && ((*dest = CharLowerFast(*dest)) != 0)) { + --chars; + ++dest; + } +} + +//=========================================================================== +template +static void IStrLower (chartype * dest, const chartype source[], unsigned chars) { + while ((chars > 1) && ((*dest = CharLowerFast(*source)) != 0)) { + --chars; + ++dest; + ++source; + } + if (chars) + *dest = 0; +} + +//=========================================================================== +template +static dword IStrHash (const chartype str[], unsigned chars) { + dword temp0 = 0xE2C15C9D; + dword temp1 = 0x2170A28A; + dword result = 0x325D1EAE; + for (unsigned ch; chars-- && ((ch = (unsigned)*str) != 0); ++str) { + temp0 = (temp0 << 3) ^ ch; + temp1 += s_hashValue[temp0 & 0x0F]; + result ^= temp0 + temp1; + } + return result; +} + +//=========================================================================== +template +static dword IStrHashI (const chartype str[], unsigned chars) { + dword temp0 = 0xE2C15C9D; + dword temp1 = 0x2170A28A; + dword result = 0x325D1EAE; + for (unsigned ch; chars-- && ((ch = (unsigned)*str) != 0); ++str) { + if ((ch >= 'a') && (ch <= 'z')) + ch = ch + 'A' - 'a'; + temp0 = (temp0 << 3) ^ ch; + temp1 += s_hashValue[temp0 & 0x0F]; + result ^= temp0 + temp1; + } + return result; +} + +//=========================================================================== +template +static bool IStrTokenize (const chartype * source[], chartype * dest, unsigned chars, const chartype whitespace[], unsigned maxWhitespaceSkipCount) { + + // Skip past leading whitespace + bool inQuotes = false; + unsigned whitespaceSkipped = 0; + while (**source && IStrChr(whitespace, **source, (unsigned)-1) && whitespaceSkipped < maxWhitespaceSkipCount) { + inQuotes = (**source == '\"'); + ++*source; + ++whitespaceSkipped; + if (inQuotes) + break; + } + + // Copy the token + unsigned offset = 0; + while (**source && + ((inQuotes && (**source != '\"')) || !IStrChr(whitespace, **source, (unsigned)-1))) { + if (offset + 1 < chars) + dest[offset++] = **source; + ++*source; + } + + // Skip past the terminating quote + if (inQuotes && (**source == '\"')) + ++*source; + + // Null terminate the destination buffer + if (chars) { + ASSERT(offset < chars); + dest[offset] = 0; + } + + // Upon return, 'source' is guaranteed to point to the first character + // following the returned token (and following any closing quotes) + + return (offset || inQuotes); +} + +//=========================================================================== +template +static bool IStrTokenize (const chartype * source[], ARRAY(chartype) * destArray, const chartype whitespace[], unsigned maxWhitespaceSkipCount) { + + // Verify that the destination array is empty + ASSERT(!destArray->Count()); + + // Skip past leading whitespace + bool inQuotes = false; + unsigned whitespaceSkipped = 0; + while (**source && IStrChr(whitespace, **source, (unsigned)-1) && whitespaceSkipped < maxWhitespaceSkipCount) { + inQuotes = (**source == '\"'); + ++*source; + ++whitespaceSkipped; + if (inQuotes) + break; + } + + // Copy the token + bool added = false; + while (**source && + ((inQuotes && (**source != '\"')) || !IStrChr(whitespace, **source, (unsigned)-1))) { + destArray->Add(**source); + added = true; + ++*source; + } + + // Skip past the terminating quote + if (inQuotes && (**source == '\"')) + ++*source; + + // Null terminate the destination array + destArray->Add(0); + + // Upon return, 'source' is guaranteed to point to the first character + // following the returned token (and following any closing quotes) + + return (added || inQuotes); +} + + +/**************************************************************************** +* +* Exported functions +* +***/ + +//=========================================================================== +char * StrDup (const char str[]) { + return IStrDup(str); +} + +//=========================================================================== +wchar * StrDup (const wchar str[]) { + return IStrDup(str); +} + +//=========================================================================== +char * StrDupLen (const char str[], unsigned chars) { + return IStrDupLen(str, chars); +} + +//=========================================================================== +wchar * StrDupLen (const wchar str[], unsigned chars) { + return IStrDupLen(str, chars); +} + +//============================================================================ +wchar * StrDupToUnicode (const char str[]) { + unsigned bytes = StrBytes(str) * sizeof(wchar); + wchar * dst = (wchar*)ALLOC(bytes); + StrToUnicode(dst, str, bytes / sizeof(wchar)); + return dst; +} + +//============================================================================ +char * StrDupToAnsi (const wchar str[]) { + unsigned bytes = StrBytes(str) / sizeof(wchar); + char * dst = (char*)ALLOC(bytes); + StrToAnsi(dst, str, bytes); + return dst; +} + +//=========================================================================== +unsigned StrBytes (const char str[]) { // includes space for terminator + return (IStrLen(str) + 1) * sizeof(str[0]); +} + +//=========================================================================== +unsigned StrBytes (const wchar str[]) { // includes space for terminator + return (IStrLen(str) + 1) * sizeof(str[0]); +} + +//=========================================================================== +char * StrChr (char * str, char ch, unsigned chars) { + return IStrChr(str, ch, chars); +} + +//=========================================================================== +wchar * StrChr (wchar * str, wchar ch, unsigned chars) { + return IStrChr(str, ch, chars); +} + +//=========================================================================== +const char * StrChr (const char str[], char ch, unsigned chars) { + return IStrChr(str, ch, chars); +} + +//=========================================================================== +const wchar * StrChr (const wchar str[], wchar ch, unsigned chars) { + return IStrChr(str, ch, chars); +} + +//=========================================================================== +char * StrChrR (char * str, char ch) { + return IStrChrR(str, ch); +} + +//=========================================================================== +wchar * StrChrR (wchar * str, wchar ch) { + return IStrChrR(str, ch); +} + +//=========================================================================== +const char * StrChrR (const char str[], char ch) { + return IStrChrR(str, ch); +} + +//=========================================================================== +const wchar * StrChrR (const wchar str[], wchar ch) { + return IStrChrR(str, ch); +} + +//=========================================================================== +unsigned StrPrintf (char * dest, unsigned count, const char format[], ...) { + va_list argList; + va_start(argList, format); + int result = _vsnprintf((char *)dest, count, (const char *)format, argList); + va_end(argList); + return IStrPrintfValidate(dest, count, result); +} + +//=========================================================================== +unsigned StrPrintf (wchar * dest, unsigned count, const wchar format[], ...) { + va_list argList; + va_start(argList, format); + int result = _vsnwprintf(dest, count, format, argList); + va_end(argList); + return IStrPrintfValidate(dest, count, result); +} + +//=========================================================================== +unsigned StrPrintfV (char * dest, unsigned count, const char format[], va_list args) { + int result = _vsnprintf(dest, count, format, args); + return IStrPrintfValidate(dest, count, result); +} + +//=========================================================================== +unsigned StrPrintfV (wchar * dest, unsigned count, const wchar format[], va_list args) { + int result = _vsnwprintf(dest, count, format, args); + return IStrPrintfValidate(dest, count, result); +} + +//=========================================================================== +int StrCmp (const char str1[], const char str2[], unsigned chars) { + return IStrCmp(str1, str2, chars); +} + +//=========================================================================== +int StrCmp (const wchar str1[], const wchar str2[], unsigned chars) { + return IStrCmp(str1, str2, chars); +} + +//=========================================================================== +int StrCmpI (const char str1[], const char str2[], unsigned chars) { + return IStrCmpI(str1, str2, chars); +} + +//=========================================================================== +int StrCmpI (const wchar str1[], const wchar str2[], unsigned chars) { + return IStrCmpI(str1, str2, chars); +} + +//=========================================================================== +void StrCopy (char * dest, const char source[], unsigned chars) { + IStrCopy(dest, source, chars); +} + +//=========================================================================== +void StrCopy (wchar * dest, const wchar source[], unsigned chars) { + IStrCopy(dest, source, chars); +} + +//=========================================================================== +unsigned StrCopyLen (char * dest, const char source[], unsigned chars) { + return IStrCopyLen(dest, source, chars); +} + +//=========================================================================== +unsigned StrCopyLen (wchar * dest, const wchar source[], unsigned chars) { + return IStrCopyLen(dest, source, chars); +} + +//=========================================================================== +void StrPack (char * dest, const char source[], unsigned chars) { + IStrPack(dest, source, chars); +} + +//=========================================================================== +void StrPack (wchar * dest, const wchar source[], unsigned chars) { + IStrPack(dest, source, chars); +} + +//=========================================================================== +char * StrStr (char * source, const char match[]) { + return IStrStr(source, match); +} + +//=========================================================================== +const char * StrStr (const char source[], const char match[]) { + return IStrStr(source, match); +} + +//=========================================================================== +wchar * StrStr (wchar * source, const wchar match[]) { + return IStrStr(source, match); +} + +//=========================================================================== +const wchar * StrStr (const wchar source[], const wchar match[]) { + return IStrStr(source, match); +} + +//=========================================================================== +char * StrStrI (char * source, const char match[]) { + return IStrStrI(source, match); +} + +//=========================================================================== +const char * StrStrI (const char source[], const char match[]) { + return IStrStrI(source, match); +} + +//=========================================================================== +wchar * StrStrI (wchar * source, const wchar match[]) { + return IStrStrI(source, match); +} + +//=========================================================================== +const wchar * StrStrI (const wchar source[], const wchar match[]) { + return IStrStrI(source, match); +} + +//=========================================================================== +unsigned StrLen (const char str[]) { + return IStrLen(str); +} + +//=========================================================================== +unsigned StrLen (const wchar str[]) { + return IStrLen(str); +} + +//=========================================================================== +unsigned StrUnicodeToUtf8 (char * dest, const wchar source[], unsigned destChars) { + char * destCurr = dest; + char * destTerm = dest + destChars; + while (*source && (destCurr + 1 < destTerm)) + if (!ICharUnicodeToUtf8(&destCurr, &source, destTerm - destCurr - 1)) + break; + if (destCurr < destTerm) + *destCurr = 0; + return destCurr - dest; // dest chars not including null terminator +} + +//=========================================================================== +unsigned StrUtf8ToUnicode (wchar * dest, const char source[], unsigned destChars) { + wchar * destCurr = dest; + wchar * destTerm = dest + destChars; + while (*source && (destCurr + 1 < destTerm)) + ICharUtf8ToUnicode(&destCurr, &source); + if (destCurr < destTerm) + *destCurr = 0; + return destCurr - dest; // dest chars not including null terminator +} + +//============================================================================ +float StrToFloat (const char source[], const char ** endptr) { + return (float) strtod(source, const_cast(endptr)); +} + +//=========================================================================== +float StrToFloat (const wchar source[], const wchar ** endptr) { + return (float) wcstod(source, const_cast(endptr)); +} + +//=========================================================================== +int StrToInt (const char source[], const char ** endptr) { + return strtol(source, const_cast(endptr), 0); +} + +//=========================================================================== +int StrToInt (const wchar source[], const wchar ** endptr) { + return wcstol(source, const_cast(endptr), 0); +} + +//=========================================================================== +unsigned StrToUnsigned (char source[], char ** endptr, int radix) { + return strtoul(source, const_cast(endptr), radix); +} + +//=========================================================================== +unsigned StrToUnsigned (wchar source[], wchar ** endptr, int radix) { + return wcstoul(source, const_cast(endptr), radix); +} + +//=========================================================================== +unsigned StrToUnsigned (const char source[], const char ** endptr, int radix) { + return strtoul(source, const_cast(endptr), radix); +} + +//=========================================================================== +unsigned StrToUnsigned (const wchar source[], const wchar ** endptr, int radix) { + return wcstoul(source, const_cast(endptr), radix); +} + +//=========================================================================== +void StrLower (char * dest, unsigned chars) { + IStrLower(dest, chars); +} + +//=========================================================================== +void StrLower (wchar * dest, unsigned chars) { + IStrLower(dest, chars); +} + +//=========================================================================== +void StrLower (char * dest, const char source[], unsigned chars) { + IStrLower(dest, source, chars); +} + +//=========================================================================== +void StrLower (wchar * dest, const wchar source[], unsigned chars) { + IStrLower(dest, source, chars); +} + +//============================================================================ +dword StrHash (const char str[], unsigned chars) { + return IStrHash(str, chars); +} + +//=========================================================================== +dword StrHash (const wchar str[], unsigned chars) { + return IStrHash(str, chars); +} + +//=========================================================================== +dword StrHashI (const char str[], unsigned chars) { + return IStrHashI(str, chars); +} + +//=========================================================================== +dword StrHashI (const wchar str[], unsigned chars) { + return IStrHashI(str, chars); +} + +//=========================================================================== +bool StrTokenize (const char * source[], char * dest, unsigned chars, const char whitespace[], unsigned maxWhitespaceSkipCount) { + return IStrTokenize(source, dest, chars, whitespace, maxWhitespaceSkipCount); +} + +//=========================================================================== +bool StrTokenize (const wchar * source[], wchar * dest, unsigned chars, const wchar whitespace[], unsigned maxWhitespaceSkipCount) { + return IStrTokenize(source, dest, chars, whitespace, maxWhitespaceSkipCount); +} + +//=========================================================================== +bool StrTokenize (const char * source[], ARRAY(char) * destArray, const char whitespace[], unsigned maxWhitespaceSkipCount) { + return IStrTokenize(source, destArray, whitespace, maxWhitespaceSkipCount); +} + +//=========================================================================== +bool StrTokenize (const wchar * source[], ARRAY(wchar) * destArray, const wchar whitespace[], unsigned maxWhitespaceSkipCount) { + return IStrTokenize(source, destArray, whitespace, maxWhitespaceSkipCount); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.h new file mode 100644 index 00000000..125841d5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.h @@ -0,0 +1,140 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSTR_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtStr.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSTR_H + + +/***************************************************************************** +* +* String functions +* +***/ + +inline char CharLowerFast (char ch) { return ((ch >= 'A') && (ch <= 'Z')) ? (char )(ch + 'a' - 'A') : ch; } +inline wchar CharLowerFast (wchar ch) { return ((ch >= L'A') && (ch <= L'Z')) ? (wchar)(ch + L'a' - L'A') : ch; } + +inline char CharUpperFast (char ch) { return ((ch >= 'a') && (ch <= 'z')) ? (char )(ch + 'A' - 'a') : ch; } +inline wchar CharUpperFast (wchar ch) { return ((ch >= L'a') && (ch <= L'z')) ? (wchar)(ch + L'A' - L'a') : ch; } + +unsigned StrBytes (const char str[]); // includes space for terminator +unsigned StrBytes (const wchar str[]); // includes space for terminator + +char * StrChr (char * str, char ch, unsigned chars = (unsigned)-1); +wchar * StrChr (wchar * str, wchar ch, unsigned chars = (unsigned)-1); +const char * StrChr (const char str[], char ch, unsigned chars = (unsigned)-1); +const wchar * StrChr (const wchar str[], wchar ch, unsigned chars = (unsigned)-1); + +unsigned StrPrintf (char * dest, unsigned count, const char format[], ...); +unsigned StrPrintf (wchar * dest, unsigned count, const wchar format[], ...); + +unsigned StrPrintfV (char * dest, unsigned count, const char format[], va_list args); +unsigned StrPrintfV (wchar * dest, unsigned count, const wchar format[], va_list args); + +unsigned StrLen (const char str[]); +unsigned StrLen (const wchar str[]); + +char * StrDup (const char str[]); +wchar * StrDup (const wchar str[]); + +char * StrDupLen (const char str[], unsigned chars); +wchar * StrDupLen (const wchar str[], unsigned chars); + +wchar * StrDupToUnicode (const char str[]); +char * StrDupToAnsi (const wchar str[]); + +int StrCmp (const char str1[], const char str2[], unsigned chars = (unsigned)-1); +int StrCmp (const wchar str1[], const wchar str2[], unsigned chars = (unsigned)-1); + +int StrCmpI (const char str1[], const char str2[], unsigned chars = (unsigned)-1); +int StrCmpI (const wchar str1[], const wchar str2[], unsigned chars = (unsigned)-1); + +char * StrStr (char * source, const char match[]); +const char * StrStr (const char source[], const char match[]); +wchar * StrStr (wchar * source, const wchar match[]); +const wchar * StrStr (const wchar source[], const wchar match[]); + +char * StrStrI (char * source, const char match[]); +const char * StrStrI (const char source[], const char match[]); +wchar * StrStrI (wchar * source, const wchar match[]); +const wchar * StrStrI (const wchar source[], const wchar match[]); + +char * StrChrR (char * str, char ch); +wchar * StrChrR (wchar * str, wchar ch); +const char * StrChrR (const char str[], char ch); +const wchar * StrChrR (const wchar str[], wchar ch); + +void StrCopy (char * dest, const char source[], unsigned chars); +void StrCopy (wchar * dest, const wchar source[], unsigned chars); + +unsigned StrCopyLen (char * dest, const char source[], unsigned chars); +unsigned StrCopyLen (wchar * dest, const wchar source[], unsigned chars); + +void StrPack (char * dest, const char source[], unsigned chars); +void StrPack (wchar * dest, const wchar source[], unsigned chars); + +unsigned StrToAnsi (char * dest, const wchar source[], unsigned destChars); +unsigned StrToAnsi (char * dest, const wchar source[], unsigned destChars, unsigned codePage); + +unsigned StrToUnicode (wchar * dest, const char source[], unsigned destChars); +unsigned StrToUnicode (wchar * dest, const char source[], unsigned destChars, unsigned codePage); + +unsigned StrUnicodeToUtf8 (char * dest, const wchar source[], unsigned destChars); +unsigned StrUtf8ToUnicode (wchar * dest, const char source[], unsigned destChars); + +float StrToFloat (const char source[], const char ** endptr); +float StrToFloat (const wchar source[], const wchar ** endptr); + +int StrToInt (const char source[], const char ** endptr); +int StrToInt (const wchar source[], const wchar ** endptr); + +unsigned StrToUnsigned (char source[], char ** endptr, int radix); +unsigned StrToUnsigned (wchar source[], wchar ** endptr, int radix); +unsigned StrToUnsigned (const char source[], const char ** endptr, int radix); +unsigned StrToUnsigned (const wchar source[], const wchar ** endptr, int radix); + +void StrLower (char * dest, unsigned chars = (unsigned) -1); +void StrLower (wchar * dest, unsigned chars = (unsigned) -1); +void StrLower (char * dest, const char source[], unsigned chars); +void StrLower (wchar * dest, const wchar source[], unsigned chars); + +dword StrHash (const char str[], unsigned chars = (unsigned)-1); +dword StrHash (const wchar str[], unsigned chars = (unsigned)-1); + +dword StrHashI (const char str[], unsigned chars = (unsigned)-1); +dword StrHashI (const wchar str[], unsigned chars = (unsigned)-1); + +bool StrTokenize (const char * source[], char * dest, unsigned chars, const char whitespace[], unsigned maxWhitespaceSkipCount = (unsigned)-1); +bool StrTokenize (const wchar * source[], wchar * dest, unsigned chars, const wchar whitespace[], unsigned maxWhitespaceSkipCount = (unsigned)-1); +bool StrTokenize (const char * source[], ARRAY(char) * destArray, const char whitespace[], unsigned maxWhitespaceSkipCount = (unsigned)-1); +bool StrTokenize (const wchar * source[], ARRAY(wchar) * destArray, const wchar whitespace[], unsigned maxWhitespaceSkipCount = (unsigned)-1); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSubst.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSubst.cpp new file mode 100644 index 00000000..d1fe9bdf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSubst.cpp @@ -0,0 +1,286 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSubst.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + +#define SUBST_BLOCK SubstParsedData::SubstBlock + +/***************************************************************************** +* +* Internal functions +* +***/ + +//============================================================================ +template +bool IVarSubstitute ( + ARRAY(chartype) * dst, + const chartype src[], + unsigned varCount, + const chartype * varNames[], // [varCount] + const chartype * varValues[] // [varCount] +) { + ASSERT(dst); + ASSERT(src); + ASSERT(varNames); + ASSERT(varValues); + + dst->Reserve(StrLen(src) * 5/4); + + bool result = true; + while (*src) { + // Copy non-substituted strings and escape %% symbols + if ((*src != L'%') || (*++src == L'%')) { + dst->Push(*src++); + continue; + } + + // Find variable definition + const chartype * varStart = src; + const chartype * varEnd = StrChr(varStart, L'%'); + if (!varEnd) { + // Skip % character and continue + result = false; + continue; + } + + // Validate variable name length + chartype varBuffer[256]; + if (varEnd - varStart >= arrsize(varBuffer)) { + result = false; + src = varEnd + 1; + continue; + } + + // Copy variable name excluding trailing '%' + StrCopy(varBuffer, varStart, varEnd - varStart + 1); + src = varEnd + 1; + + // Find the variable value and perform substitution + bool found = false; + for (unsigned i = 0; i < varCount; ++i) { + if (StrCmp(varBuffer, varNames[i])) + continue; + dst->Add(varValues[i], StrLen(varValues[i])); + found = true; + break; + } + + // Check that variable definition exists + result = result && found; + } + + // Terminate string + dst->Push(0); + return result; +} + +//============================================================================ +template +bool IParseForSubst ( + SubstParsedData * dest, + const chartype src[] +) { + const chartype * current = src; + bool result = true; + while (*current) { + // Copy non-substituted strings and escape %% symbols + if ((*current != L'%') || (*++current == L'%')) { + current++; + continue; + } + + // Find variable definition + const chartype * varStart = current; + const chartype * varEnd = StrChr(varStart, L'%'); + if (!varEnd) { + // Skip % character and continue + result = false; + continue; + } + + // We've found a variable, copy the current data to a new object + if (current != src) { + int strLen = (current - src) - 1; + SUBST_BLOCK * block = NEW(SUBST_BLOCK); + block->isVar = false; + block->strLen = strLen; + block->data = (chartype*)ALLOCZERO((strLen + 1) * sizeof(chartype)); + MemCopy(block->data, src, strLen * sizeof(chartype)); + + dest->blocks.Add(block); + } + + // Validate variable name length + chartype varBuffer[256]; + if (varEnd - varStart >= arrsize(varBuffer)) { + result = false; + src = current = varEnd + 1; + continue; + } + + // Copy variable name excluding trailing '%' + int strLen = (varEnd - varStart); + SUBST_BLOCK * block = NEW(SUBST_BLOCK); + block->isVar = true; + block->strLen = strLen; + block->data = (chartype*)ALLOCZERO((strLen + 1) * sizeof(chartype)); + MemCopy(block->data, varStart, strLen * sizeof(chartype)); + + dest->blocks.Add(block); + + src = current = varEnd + 1; + } + + // Check and see if there's any data remaining + if (current != src) { + int strLen = (current - src); + SUBST_BLOCK * block = NEW(SUBST_BLOCK); + block->isVar = false; + block->strLen = strLen; + block->data = (chartype*)ALLOCZERO((strLen + 1) * sizeof(chartype)); + MemCopy(block->data, src, strLen * sizeof(chartype)); + + dest->blocks.Add(block); + } + + return result; +} + +//============================================================================ +template +bool IVarSubstPreParsed ( + ARRAY(chartype) * dst, + const SubstParsedData * src, + unsigned varCount, + const chartype * varNames[], // [varCount] + const chartype * varValues[] // [varCount] +) { + unsigned approxTotalSize = 0; + for (unsigned i = 0; i < src->blocks.Count(); ++i) { + approxTotalSize += src->blocks[i]->strLen; + } + + dst->Reserve(approxTotalSize * 5/4); + + bool foundAll = true; + for (unsigned blockIndex = 0; blockIndex < src->blocks.Count(); ++blockIndex) { + SUBST_BLOCK * block = src->blocks[blockIndex]; + if (block->isVar) { + bool found = false; + for (unsigned varIndex = 0; varIndex < varCount; ++varIndex) { + if (StrCmp(block->data, varNames[varIndex])) { + continue; + } + + dst->Add(varValues[varIndex], StrLen(varValues[varIndex])); + found = true; + break; + } + + foundAll &= found; + } + else { + dst->Add(block->data, block->strLen); + } + } + dst->Push(0); + + return foundAll; +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +bool ParseForSubst ( + SubstParsedData * dest, + const wchar src[] +) { + return IParseForSubst(dest, src); +} + +//============================================================================ +bool ParseForSubst ( + SubstParsedData * dest, + const char src[] +) { + return IParseForSubst(dest, src); +} + +//============================================================================ +bool VarSubstitute ( + ARRAY(wchar) * dst, + const wchar src[], + unsigned varCount, + const wchar * varNames[], // [varCount] + const wchar * varValues[] // [varCount] +) { + return IVarSubstitute(dst, src, varCount, varNames, varValues); +} + +//============================================================================ +bool VarSubstitute ( + ARRAY(char) * dst, + const char src[], + unsigned varCount, + const char * varNames[], // [varCount] + const char * varValues[] // [varCount] +) { + return IVarSubstitute(dst, src, varCount, varNames, varValues); +} + +//============================================================================ +bool VarSubstitute ( + ARRAY(wchar) * dst, + const SubstParsedData * src, + unsigned varCount, + const wchar * varNames[], // [varCount] + const wchar * varValues[] // [varCount] +) { + return IVarSubstPreParsed(dst, src, varCount, varNames, varValues); +} + +//============================================================================ +bool VarSubstitute ( + ARRAY(char) * dst, + const SubstParsedData * src, + unsigned varCount, + const char * varNames[], // [varCount] + const char * varValues[] // [varCount] +) { + return IVarSubstPreParsed(dst, src, varCount, varNames, varValues); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSubst.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSubst.h new file mode 100644 index 00000000..a80f9da0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSubst.h @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSubst.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSUBST_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSubst.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSUBST_H + +template +struct SubstParsedData { + template + struct SubstBlock { + bool isVar; + chartype * data; + unsigned strLen; + + SubstBlock() + : isVar(false) + , data(nil) + { + } + + ~SubstBlock() { + FREE(data); + } + }; + + ARRAY(SubstBlock*) blocks; + + ~SubstParsedData() { + for (unsigned i = 0; i < blocks.Count(); ++i) { + SubstBlock * block = blocks[i]; + DEL(block); + } + } +}; + +bool ParseForSubst ( + SubstParsedData * dest, + const wchar src[] +); +bool ParseForSubst ( + SubstParsedData * dest, + const char src[] +); + +// Return value is for validation purposes only; it may be ignored +bool VarSubstitute ( + ARRAY(wchar) * dst, + const wchar src[], + unsigned varCount, + const wchar * varNames[], // [varCount] + const wchar * varValues[] // [varCount] +); +bool VarSubstitute ( + ARRAY(char) * dst, + const char src[], + unsigned varCount, + const char * varNames[], // [varCount] + const char * varValues[] // [varCount] +); +bool VarSubstitute ( + ARRAY(wchar) * dst, + const SubstParsedData * src, + unsigned varCount, + const wchar * varNames[], // [varCount] + const wchar * varValues[] // [varCount] +); +bool VarSubstitute ( + ARRAY(char) * dst, + const SubstParsedData * src, + unsigned varCount, + const char * varNames[], // [varCount] + const char * varValues[] // [varCount] +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSync.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSync.h new file mode 100644 index 00000000..40109376 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSync.h @@ -0,0 +1,118 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSync.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSYNC_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtSync.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTSYNC_H + + +/**************************************************************************** +* +* Atomic operations +* +***/ + +// *value += increment; return original value of *value; thread safe +long AtomicAdd (long * value, long increment); + +// *value = value; return original value of *value; thread safe +long AtomicSet (long * value, long set); + + +#define ATOMIC_ONCE(code) { \ + static long s_count = 1; \ + if (AtomicSet(&s_count, 0)) \ + code; \ +} // + + +/**************************************************************************** +* +* CLock +* (reader/writer lock) +* +***/ + +class CLockWaitSet; + +class CLock { +private: + CLockWaitSet * m_waitSet; + long m_spinLock; + unsigned m_readerCount; + unsigned m_writerCount; + +public: + CLock (); + ~CLock (); + void EnterRead (); + void EnterWrite (); + void LeaveRead (); + void LeaveWrite (); +}; + + +/***************************************************************************** +* +* CEvent +* +***/ + +#ifdef HS_BUILD_FOR_WIN32 +typedef HANDLE EventHandle; +#else +# error "CEvent: Not implemented on this platform" +#endif + +const unsigned kEventWaitForever = (unsigned)-1; + +enum ECEventResetBehavior { + kEventManualReset, + kEventAutoReset, +}; + +class CEvent { + EventHandle m_handle; +public: + CEvent ( + ECEventResetBehavior resetType, + bool initialSet = false + ); + ~CEvent (); + + void Signal (); + void Reset (); + bool Wait (unsigned waitMs); + + const EventHandle & Handle () const { return m_handle; } +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTime.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTime.cpp new file mode 100644 index 00000000..b92a8344 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTime.cpp @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTime.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +//=========================================================================== +void TimeGetElapsedDesc ( + dword minutesElapsed, + TimeElapsedDesc * desc +) { + + const unsigned kMinutesPerHour = 60; + const unsigned kMinutesPerDay = 1440; + const unsigned kMinutesPerWeek = 10080; + const unsigned kMinutesPerMonth = 43830; + const unsigned kMinutesPerYear = 525960; + + dword & elapsed = minutesElapsed; + desc->years = (elapsed / kMinutesPerYear); elapsed -= desc->years * kMinutesPerYear; + desc->months = (elapsed / kMinutesPerMonth); elapsed -= desc->months * kMinutesPerMonth; + desc->weeks = (elapsed / kMinutesPerWeek); elapsed -= desc->weeks * kMinutesPerWeek; + desc->days = (elapsed / kMinutesPerDay); elapsed -= desc->days * kMinutesPerDay; + desc->hours = (elapsed / kMinutesPerHour); elapsed -= desc->hours * kMinutesPerHour; + desc->minutes = elapsed; + +} + +//============================================================================ +dword TimeGetSecondsSince2001Utc () { + qword time = TimeGetTime(); + dword seconds = (dword)((time - kTime1601To2001) / kTimeIntervalsPerSecond); + return seconds; +} + +//============================================================================ +dword TimeGetSecondsSince1970Utc () { + qword time = TimeGetTime(); + dword seconds = (dword)((time - kTime1601To1970) / kTimeIntervalsPerSecond); + return seconds; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTime.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTime.h new file mode 100644 index 00000000..5e87d3d8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTime.h @@ -0,0 +1,113 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTime.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTTIME_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTime.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTTIME_H + + +/***************************************************************************** +* +* Time formatting functions +* +***/ + +struct TimeDesc { + unsigned year; + unsigned month; // [1, 12] + unsigned day; + unsigned dayOfWeek; // [0, 6] + unsigned hour; // [0, 23] + unsigned minute; // [0, 59] + unsigned second; // [0, 59] +}; + +struct TimeElapsedDesc { + unsigned years; + unsigned months; // [0, 12] + unsigned days; // [0, 7] + unsigned weeks; // [0, 6] + unsigned hours; // [0, 23] + unsigned minutes; // [0, 59] +}; + +void TimeGetDesc ( + qword time, + TimeDesc * desc +); + +void TimeGetElapsedDesc ( + dword minutesElapsed, + TimeElapsedDesc * desc +); + +void TimePrettyPrint ( + qword time, + unsigned chars, + wchar * buffer +); + + +/***************************************************************************** +* +* Time query functions +* +***/ + +const qword kTimeIntervalsPerMs = 10000; +const qword kTimeIntervalsPerSecond = 1000 * kTimeIntervalsPerMs; +const qword kTimeIntervalsPerMinute = 60 * kTimeIntervalsPerSecond; +const qword kTimeIntervalsPerHour = 60 * kTimeIntervalsPerMinute; +const qword kTimeIntervalsPerDay = 24 * kTimeIntervalsPerHour; + +// millisecond timer; wraps ~49 days +dword TimeGetMs (); + +// 100 nanosecond intervals; won't wrap in our lifetimes +qword TimeGetTime (); +qword TimeGetLocalTime (); + +// Minutes elapsed since 2001 UTC +dword TimeGetMinutes (); + +// Seconds elapsed since 00:00:00 January 1, 2001 UTC +dword TimeGetSecondsSince2001Utc (); + +// Seconds elapsed since 00:00:00 January 1, 1970 UTC (the Unix Epoch) +dword TimeGetSecondsSince1970Utc (); + + +// These magic numbers taken from Microsoft's "Shared Source CLI implementation" source code. +// http://msdn.microsoft.com/library/en-us/Dndotnet/html/mssharsourcecli.asp + +static const qword kTime1601To1970 = 11644473600 * kTimeIntervalsPerSecond; +static const qword kTime1601To2001 = 12622780800 * kTimeIntervalsPerSecond; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTls.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTls.cpp new file mode 100644 index 00000000..d6d70a5d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTls.cpp @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTls.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Exports +* +***/ + +#ifdef HS_BUILD_FOR_WIN32 + +//============================================================================ +void ThreadLocalAlloc (unsigned * id) { + ASSERT(id); + *id = TlsAlloc(); +} + +//============================================================================ +void ThreadLocalFree (unsigned id) { + (void)TlsFree(id); +} + +//============================================================================ +void * ThreadLocalGetValue (unsigned id) { + return TlsGetValue(id); +} + +//============================================================================ +void ThreadLocalSetValue (unsigned id, void * value) { + TlsSetValue(id, value); +} + +#else +# error "TLS not implemented for this platform" +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTls.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTls.h new file mode 100644 index 00000000..98e196a9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTls.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTls.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTTLS_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTls.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTTLS_H + + +/***************************************************************************** +* +* Thread local storage functions +* +***/ + +const unsigned kTlsInvalidValue = (unsigned) -1; + +void ThreadLocalAlloc (unsigned * id); +void ThreadLocalFree (unsigned id); +void * ThreadLocalGetValue (unsigned id); +void ThreadLocalSetValue (unsigned id, void * value); + + +// Thread capability functions - prevents deadlocks and performance +// bottlenecks by disallowing some threads certain operations. +void ThreadAllowBlock (); +void ThreadDenyBlock (); +void ThreadAssertCanBlock (const char file[], int line); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTypes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTypes.h new file mode 100644 index 00000000..ad609014 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTypes.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTypes.h +* +* +* By Eric Anderson (10/10/2005) +* Copyright 2005 Cyan Worlds, Inc. +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTTYPES_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtTypes.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTTYPES_H + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtUuid.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtUuid.cpp new file mode 100644 index 00000000..110385a4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtUuid.cpp @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtUuid.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +const Uuid kNilGuid; + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +Uuid::Uuid (const wchar str[]) { + + GuidFromString(str, this); +} + +//============================================================================ +Uuid::Uuid (const byte buf[], unsigned length) { + + GuidFromHex(buf, length, this); +} + +//============================================================================ +unsigned GuidHash (const Uuid & uuid) { + + CHashValue hash(&uuid.data, sizeof(uuid.data)); + return hash.GetHash(); +} + +//============================================================================ +static const wchar s_hexChars[] = L"0123456789ABCDEF"; +const wchar * GuidToHex (const Uuid & uuid, wchar * dst, unsigned chars) { + + wchar * str = ALLOCA(wchar, sizeof(uuid.data) * 2 + 1); + wchar * cur = str; + + for (unsigned i = 0; i < sizeof(uuid.data); ++i) { + *cur++ = s_hexChars[(uuid.data[i] >> 4) & 0x0f]; + *cur++ = s_hexChars[uuid.data[i] & 0x0f]; + } + *cur = 0; + + StrCopy(dst, str, chars); + return dst; +} + +//============================================================================ +bool GuidFromHex (const byte buf[], unsigned length, Uuid * uuid) { + + ASSERT(length == msizeof(Uuid, data)); + MemCopy(uuid->data, buf, msizeof(Uuid, data)); + return true; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtUuid.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtUuid.h new file mode 100644 index 00000000..821ee7fc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtUuid.h @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtUuid.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTUUID_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtUuid.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PRIVATE_PNUTUUID_H + + +/***************************************************************************** +* +* Types +* +***/ + +struct Uuid; + +/***************************************************************************** +* +* Constants +* +***/ + +extern const Uuid kNilGuid; + + +/***************************************************************************** +* +* Functions +* +***/ + +// Using 'Guid' here instead of 'Uuid' to avoid name clash with windows API =( + +Uuid GuidGenerate (); +void GuidClear (Uuid * uuid); +bool GuidFromString (const wchar str[], Uuid * uuid); +bool GuidFromString (const char str[], Uuid * uuid); +int GuidCompare (const Uuid & a, const Uuid & b); +inline bool GuidsAreEqual (const Uuid & a, const Uuid & b) { return 0 == GuidCompare(a, b); } +bool GuidIsNil (const Uuid & uuid); +unsigned GuidHash (const Uuid & uuid); +const wchar * GuidToString (const Uuid & uuid, wchar * dst, unsigned chars); // returns dst +const char * GuidToString (const Uuid & uuid, char * dst, unsigned chars); // returns dst +const wchar * GuidToHex (const Uuid & uuid, wchar * dst, unsigned chars); // returns dst +bool GuidFromHex (const byte buf[], unsigned length, Uuid * uuid); + + +/***************************************************************************** +* +* Uuid +* +***/ + +#include +struct Uuid { + union { + dword dwords[4]; + byte data[16]; + }; + + Uuid () {} + Uuid (const wchar str[]); + Uuid (const byte buf[], unsigned length); + operator bool () const { return !GuidIsNil(*this); } + inline bool operator ! () const { return GuidIsNil(*this); } + inline bool operator < (const Uuid & rhs) const { return GuidCompare(*this, rhs) < 0; } + inline bool operator == (const Uuid & rhs) const { return GuidsAreEqual(*this, rhs); } + inline bool operator == (int rhs) const { ref(rhs); ASSERT(!rhs); return GuidsAreEqual(*this, kNilGuid); } + inline bool operator != (const Uuid & rhs) const { return !GuidsAreEqual(*this, rhs); } + inline bool operator != (int rhs) const { ref(rhs); ASSERT(!rhs); return !GuidsAreEqual(*this, kNilGuid); } +}; +#include + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/pnUtils.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/pnUtils.cpp new file mode 100644 index 00000000..f7082e74 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/pnUtils.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/pnUtils.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/pnUtils.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/pnUtils.h new file mode 100644 index 00000000..a238a12c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtils/pnUtils.h @@ -0,0 +1,39 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/pnUtils.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PNUTILS_H +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PNUTILS_H + + +#include "Private/pnUtAllIncludes.h" + + +#endif // PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILS_PNUTILS_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Intern.h new file mode 100644 index 00000000..85edd09d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Intern.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILSEXE_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILSEXE_INTERN_H + + +namespace pnUtilsExe { + +/***************************************************************************** +* +* OsTime.cpp +* +***/ + +dword TimeGetTickCount (); + + +} // namespace pnUtilsExe diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Pch.h new file mode 100644 index 00000000..ab1c8a7c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Pch.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILSEXE_PCH_H +#error "Header $/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_NUCLEUSLIB_PNUTILSEXE_PCH_H + + +#include "../pnUtils/Pch.h" +#include "Intern.h" + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/Unix/pnUteUxTime.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/Unix/pnUteUxTime.cpp new file mode 100644 index 00000000..e55a5bd2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/Unix/pnUteUxTime.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/Unix/pnUteUxTime.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/Win32/pnUteW32Time.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/Win32/pnUteW32Time.cpp new file mode 100644 index 00000000..a3449653 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/Win32/pnUteW32Time.cpp @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/Win32/pnUteW32Time.cpp +* +***/ + +#include "../../Pch.h" +#pragma hdrstop + +#ifdef HS_BUILD_FOR_WIN32 + +namespace pnUtilsExe { + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +dword TimeGetTickCount () { + return GetTickCount(); +} + + +} // namespace pnUtilsExe + +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/pnUteTime.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/pnUteTime.cpp new file mode 100644 index 00000000..d20a47b7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/pnUteTime.cpp @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/pnUteTime.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +namespace pnUtilsExe { + +/***************************************************************************** +* +* Time adjustment functions +* +* For debug builds, adjust the initial time value so that the high +* bit or the time value itself is about to wrap, to catch application +* bugs that don't handle wrapping or depend on the high bit's value. +* +***/ + +static dword s_adjustment; + +//=========================================================================== +static void InitializeAdjustment () { + ASSERT(!s_adjustment); + dword currTime = TimeGetTickCount(); + dword startBits = (currTime & 0x80) ? 0x7fff0000 : 0xffff0000; + dword startMask = 0xffff0000; + s_adjustment = (((currTime & ~startMask) | startBits) - currTime) | 1; + ASSERT(s_adjustment); +} + +//=========================================================================== +AUTO_INIT_FUNC(AutoInitializeAdjustment) { + if (!s_adjustment) + InitializeAdjustment(); +} + +} using namespace pnUtilsExe; + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +dword TimeGetMs () { +#ifdef HS_DEBUGGING + + // For debug builds, return an adjusted timer value + if (!s_adjustment) + InitializeAdjustment(); + return TimeGetTickCount() + s_adjustment; + +#else + + // For release builds, just return the operating system's timer + return TimeGetTickCount(); + +#endif +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/pnUteTls.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/pnUteTls.cpp new file mode 100644 index 00000000..0fa7265c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/pnUteTls.cpp @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/NucleusLib/pnUtilsExe/Private/pnUteTls.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +/***************************************************************************** +* +* Private data +* +***/ + +static unsigned s_tlsNoBlock = kTlsInvalidValue; + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static void ThreadCapsInitialize () { + ThreadLocalAlloc(&s_tlsNoBlock); +} + +//============================================================================ +static void ThreadCapsDestroy () { + if (s_tlsNoBlock != kTlsInvalidValue) { + ThreadLocalFree(s_tlsNoBlock); + s_tlsNoBlock = kTlsInvalidValue; + } +} + +//============================================================================ +AUTO_INIT_FUNC(InitThreadCaps) { + ThreadCapsInitialize(); + atexit(ThreadCapsDestroy); +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void ThreadAllowBlock () { + ThreadLocalSetValue(s_tlsNoBlock, (void *) false); +} + +//============================================================================ +void ThreadDenyBlock () { + ThreadLocalSetValue(s_tlsNoBlock, (void *) true); +} + +//============================================================================ +void ThreadAssertCanBlock (const char file[], int line) { + if (ThreadLocalGetValue(s_tlsNoBlock)) + ErrorAssert(line, file, "This thread may not block"); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/inc/plAllCreatables.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/inc/plAllCreatables.h new file mode 100644 index 00000000..e2ca6ae6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/inc/plAllCreatables.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAllCreatables_inc +#define plAllCreatables_inc + +#include "../plGImage/plGImageCreatable.h" +#include "../plGLight/plGLightCreatable.h" +#include "../plInterp/plInterpCreatable.h" +#include "../plInputCore/plInputCoreCreatable.h" +#include "../plPipeline/plPipelineCreatable.h" +#include "../plResMgr/plResMgrCreatable.h" +#include "../plSurface/plSurfaceCreatable.h" +#include "../plNetClient/plNetClientCreatable.h" +#include "../plNetClientComm/plNetClientCommCreatable.h" +#include "../plMessage/plMessageCreatable.h" +#include "../plAudible/plAudibleCreatable.h" +#include "../plDrawable/plDrawableCreatable.h" +#include "../plPhysical/plPhysicalCreatable.h" +#include "../plModifier/plModifierCreatable.h" +#include "../plScene/plSceneCreatable.h" +#include "../plPhysX/plPhysXCreatable.h" +#include "../plAudio/plAudioCreatable.h" +#include "../plAudioCore/plAudioCoreCreatable.h" +#include "../plParticleSystem/plParticleCreatable.h" +#include "../plNetCommon/plNetCommonCreatable.h" +#include "../plVault/plVaultCreatable.h" +#include "../plAvatar/plAvatarCreatable.h" +#include "../plIntersect/plIntersectCreatable.h" +#include "../plNetMessage/plNetMessageCreatable.h" +#include "../plStatGather/plStatGatherCreatable.h" +#include "../plSDL/plSDLCreatable.h" +#include "../plAgeLoader/plAgeLoaderCreatable.h" + +#endif // plAllCreatables_inc + \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeDescription.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeDescription.cpp new file mode 100644 index 00000000..d494bb02 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeDescription.cpp @@ -0,0 +1,549 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsStream.h" +#include "plAgeDescription.h" +#include "hsUtils.h" +#include "hsStlUtils.h" +#include "../plFile/hsFiles.h" +#include "../plFile/plInitFileReader.h" +#include "../plFile/plEncryptedStream.h" +#include "hsStringTokenizer.h" +#include +#include + + +const UInt32 plAgePage::kInvalidSeqSuffix = (UInt32)-1; + +plAgePage::plAgePage( const char *name, UInt32 seqSuffix, Byte flags ) +{ + fName = name != nil ? hsStrcpy( name ) : nil; + fSeqSuffix = seqSuffix; + fFlags = flags; +} + +plAgePage::plAgePage( char *stringFrom ) : fName(nil) +{ + SetFromString( stringFrom ); +} + +plAgePage::plAgePage() +{ + fName = nil; + fFlags = 0; + fSeqSuffix = 0; +} + +plAgePage::plAgePage( const plAgePage &src ) : fName(nil) +{ + fName = src.fName != nil ? hsStrcpy( src.fName ) : nil; + fSeqSuffix = src.fSeqSuffix; + fFlags = src.fFlags; +} + +plAgePage::~plAgePage() +{ + delete [] fName; +} + +plAgePage &plAgePage::operator=( const plAgePage &src ) +{ + delete [] fName; + fName = src.fName != nil ? hsStrcpy( src.fName ) : nil; + fSeqSuffix = src.fSeqSuffix; + fFlags = src.fFlags; + + return *this; +} + +void plAgePage::SetFlags(Byte f, bool on) +{ + if (on) + hsSetBits(fFlags, f); + else + hsClearBits(fFlags, f); +} + +// now preservs original string +hsBool plAgePage::SetFromString( const char *stringIn ) +{ + char *c, seps[] = ", \n"; + std::string string = stringIn; + + // Parse. Format is going to be "pageName[,seqSuffix[,flags]]" + c = strtok( (char*)string.c_str(), seps ); + if( c == nil ) + return false; + + delete [] fName; + fName = hsStrcpy( c ); + + // Look for seqSuffix + c = strtok( nil, seps ); + if( c != nil ) + { + fSeqSuffix = atoi( c ); + + // Look for flags + c = strtok( nil, seps ); + if( c != nil ) + { + fFlags = atoi( c ); + } + else + fFlags = 0; + } + else + { + fSeqSuffix = kInvalidSeqSuffix; + fFlags = 0; + } + + return true; +} + +char *plAgePage::GetAsString( void ) const +{ + static char str[ 256 ]; + + + // Format is "pageName[,seqSuffix[,flags]]" + if( fFlags != 0 ) + sprintf( str, "%s,%d,%d", fName, fSeqSuffix, fFlags ); + else + sprintf( str, "%s,%d", fName, fSeqSuffix ); + return str; +} + + +// +// plAgeDescription +// +// +// + +// static +char plAgeDescription::kAgeDescPath[]={"dat"PATH_SEPARATOR_STR}; +char *plAgeDescription::fCommonPages[] = { "Textures", "BuiltIn" }; + +// Also gotta init the separators for our helper reading function +plAgeDescription::plAgeDescription() : plInitSectionTokenReader() +{ + IInit(); +} + + +plAgeDescription::~plAgeDescription() +{ + IDeInit(); +} + +void plAgeDescription::IDeInit() +{ + ClearPageList(); + delete [] fName; +} + +plAgeDescription::plAgeDescription( const char *fileNameToReadFrom ) : plInitSectionTokenReader() +{ + ReadFromFile(fileNameToReadFrom); +} + +// +// Reads from a file, returns false if failed. +// +bool plAgeDescription::ReadFromFile( const char *fileNameToReadFrom ) +{ + IInit(); + + hsStream* stream = plEncryptedStream::OpenEncryptedFile(fileNameToReadFrom); + if( !stream ) + return false; + + Read( stream ); + stream->Close(); + delete stream; + + SetAgeNameFromPath( fileNameToReadFrom ); + return true; +} + +void plAgeDescription::SetAgeNameFromPath( const char *path ) +{ + delete [] fName; + + if( path == nil ) + { + fName = nil; + return; + } + + // Construct our name from the path + char *pathSep1 = strrchr( path, '\\' ); + char *pathSep2 = strrchr( path, '/' ); + if( pathSep2 > pathSep1 ) + pathSep1 = pathSep2; + if( pathSep1 == nil ) + pathSep1 = (char *)path; + else + pathSep1++; // Get past the actual character we found + + char temp[ 512 ]; + strcpy( temp, pathSep1 ); + char *end = strrchr( temp, '.' ); + if( end != nil ) + *end = 0; + + fName = hsStrcpy( temp ); +} + +void plAgeDescription::IInit( void ) +{ + fName = nil; + fDayLength = 24.0f; + fMaxCapacity = -1; + fLingerTime = 180; // seconds + fSeqPrefix = 0; + fReleaseVersion = 0; + fStart.SetMode( plUnifiedTime::kLocal ); + + fPageIterator = -1; +} + +struct SzDelete { void operator()(char * str) { delete [] str;} }; +void plAgeDescription::ClearPageList() +{ + fPages.Reset(); +} + + +void plAgeDescription::AppendPage( const char *name, int seqSuffix, Byte flags ) +{ + fPages.Append( plAgePage( name, ( seqSuffix == -1 ) ? fPages.GetCount() : (UInt32)seqSuffix, flags ) ); +} + +void plAgeDescription::SeekFirstPage( void ) +{ + fPageIterator = 0; +} + +plAgePage *plAgeDescription::GetNextPage( void ) +{ + plAgePage *ret = nil; + + + if( fPageIterator >= 0 && fPageIterator < fPages.GetCount() ) + { + ret = &fPages[ fPageIterator++ ]; + if( fPageIterator >= fPages.GetCount() ) + fPageIterator = -1; + } + + return ret; +} + +void plAgeDescription::RemovePage( const char *page ) +{ + int i; + + for( i = 0; i < fPages.GetCount(); i++ ) + { + if( strcmp( page, fPages[ i ].GetName() ) == 0 ) + { + fPages.Remove( i ); + return; + } + } +} + +plAgePage *plAgeDescription::FindPage( const char *name ) const +{ + int i; + + + for( i = 0; i < fPages.GetCount(); i++ ) + { + if( strcmp( name, fPages[ i ].GetName() ) == 0 ) + return &fPages[ i ]; + } + + return nil; +} + +plLocation plAgeDescription::CalcPageLocation( const char *page ) const +{ + plAgePage *ap = FindPage( page ); + if( ap != nil ) + { + // Combine our sequence # together + Int32 combined; + hsAssert(abs(fSeqPrefix) < 0xFF, "Age sequence prefex is out of range!"); // sequence prefix can NOT be larger or equal to 1-byte max value + UInt32 suffix = ap->GetSeqSuffix(); + hsAssert(suffix <= 0xFFFF, "Page sequence number is out of range!"); // page sequence number can NOT be larger then 2-byte max value + if( fSeqPrefix < 0 ) // we are a global age + combined = -(Int32)( ( ( -fSeqPrefix ) << 16 ) + suffix ); + else + combined = ( fSeqPrefix << 16 ) + suffix; + + // Now, our 32 bit number looks like the following: + // 0xRRAAPPPP + // - RR is FF when reserved, and 00 when normal + // - AA is the one byte for age sequence prefix (FF not allowed because 0xFFFFFFFFFF is reserved for invalid sequence number) + // - PPPP is the two bytes for page sequence number + + if( IsGlobalAge() ) + return plLocation::MakeReserved( (UInt32)combined ); + else + { + plLocation ret = plLocation::MakeNormal( combined ); + if (page && !stricmp(page, "builtin")) + ret.SetFlags(plLocation::kBuiltIn); + return ret; + } + } + + // Just make a blank (invalid) one + plLocation loc; + return loc; +} + +// +// Writes the Age Description File +// +void plAgeDescription::Write(hsStream* stream) const +{ + char buf[256]; + + // Write the date/time + sprintf(buf, "StartDateTime=%010u\n", fStart.GetSecs()); + stream->WriteString(buf); + + // Write the day length + sprintf(buf, "DayLength=%f\n", fDayLength); + stream->WriteString(buf); + + // Write the max capacity + sprintf(buf, "MaxCapacity=%d\n", fMaxCapacity); + stream->WriteString(buf); + + // Write the linger time + sprintf(buf, "LingerTime=%d\n", fLingerTime); + stream->WriteString(buf); + + // Write out the sequence prefix + sprintf( buf, "SequencePrefix=%d\n", fSeqPrefix ); + stream->WriteString( buf ); + + // Write out the release version + sprintf( buf, "ReleaseVersion=%d\n", fReleaseVersion ); + stream->WriteString( buf ); + + // Write out the pages + int i; + for( i = 0; i < fPages.GetCount(); i++ ) + { + sprintf(buf, "Page=%s\n", fPages[ i ].GetAsString() ); + stream->WriteString(buf); + } +} + +// Somewhat of an overkill, but I created it, so I better use it. +// The really nifty (or scary, depending on your viewpoint) thing is that, since +// we only have one section, we can safely use ourselves as the section reader. +// Later I might just add section readers with function pointers to avoid this need entirely + +const char *plAgeDescription::GetSectionName( void ) const +{ + return "AgeInfo"; +} + +hsBool plAgeDescription::IParseToken( const char *token, hsStringTokenizer *tokenizer, UInt32 userData ) +{ + char *tok; + + if( !stricmp( token, "StartDateTime" ) ) + { + if( ( tok = tokenizer->next() ) != nil ) + { + char buf[11]; + strncpy(buf, tok, 10); buf[10] = '\0'; + fStart.SetSecs(atoi(buf)); + } + } + else if (!stricmp(token, "DayLength")) + { + if( ( tok = tokenizer->next() ) != nil ) + fDayLength = (float)atof(tok); + } + else if (!stricmp(token, "Page")) + { + fPages.Append( plAgePage( tokenizer->GetRestOfString() ) ); + if( fPages[ fPages.GetCount() - 1 ].GetSeqSuffix() == plAgePage::kInvalidSeqSuffix ) + fPages[ fPages.GetCount() - 1 ].SetSeqSuffix( fPages.GetCount() ); + } + else if (!stricmp(token, "MaxCapacity")) + { + if( ( tok = tokenizer->next() ) != nil ) + fMaxCapacity = atoi(tok); + } + else if (!stricmp(token, "LingerTime")) + { + if( ( tok = tokenizer->next() ) != nil ) + fLingerTime = atoi(tok); + } + else if( !stricmp(token, "SequencePrefix")) + { + if( ( tok = tokenizer->next() ) != nil ) + fSeqPrefix = atoi(tok); + } + else if( !stricmp(token, "ReleaseVersion")) + { + if( ( tok = tokenizer->next() ) != nil ) + fReleaseVersion = atoi(tok); + } + + return true; +} + +// +// Reads the Age Description File +// +void plAgeDescription::Read(hsStream* stream) +{ + plInitSectionReader *sections[] = { (plInitSectionReader *)this, nil }; + + plInitFileReader reader( stream, sections ); + + if( !reader.IsOpen() ) + { + hsAssert( false, "Unable to open age description file for reading" ); + return; + } + + reader.Parse(); + reader.Close(); +} + +// +// What is the current time in the age (in secs), based on dayLength. +// +int plAgeDescription::GetAgeTimeOfDaySecs(const plUnifiedTime& earthCurrentTime) const +{ + double elapsedSecs = GetAgeElapsedSeconds(earthCurrentTime); + int secsInADay = (int)(fDayLength * 60 * 60); + int ageTime = (int)elapsedSecs % secsInADay; + return ageTime; +} + +// +// What is the current time in the age (from 0-1), based on dayLength. +// +float plAgeDescription::GetAgeTimeOfDayPercent(const plUnifiedTime& earthCurrentTime) const +{ + double elapsedSecs = GetAgeElapsedSeconds(earthCurrentTime); + int secsInADay = (int)(fDayLength * 60 * 60); + double ageTime = fmod(elapsedSecs, secsInADay); + float percent=(float)(ageTime/secsInADay); + if (percent<0.f) + percent=0.f; + if (percent>1.f) + percent=1.f; + return percent; +} + +// +// How old is the age in days. +// +double plAgeDescription::GetAgeElapsedDays(plUnifiedTime earthCurrentTime) const +{ + earthCurrentTime.SetMicros(0); // Ignore micros for this calculation + + double elapsedSecs = GetAgeElapsedSeconds(earthCurrentTime); + return elapsedSecs / 60.0 / 60.0 / fDayLength; // days and fractions +} + +// +// How many seconds have elapsed since the age was born. or how old is the age (in secs) +// +double plAgeDescription::GetAgeElapsedSeconds(const plUnifiedTime & earthCurrentTime) const +{ + plUnifiedTime elapsed = earthCurrentTime - fStart; + return elapsed.GetSecsDouble(); +} + + // Static functions for the available common pages + enum CommonPages + { + kTextures, + kGlobal + }; + +const char *plAgeDescription::GetCommonPage( int pageType ) +{ + hsAssert( pageType < kNumCommonPages, "Invalid page type in GetCommonPage()" ); + return fCommonPages[ pageType ]; +} + +void plAgeDescription::AppendCommonPages( void ) +{ + UInt32 startSuffix = 0xffff, i; + + + if( IsGlobalAge() ) + return; + + for( i = 0; i < kNumCommonPages; i++ ) + fPages.Append( plAgePage( fCommonPages[ i ], startSuffix - i, 0 ) ); +} + +void plAgeDescription::CopyFrom(const plAgeDescription& other) +{ + IDeInit(); + fName = hsStrcpy(other.GetAgeName()); + int i; + for(i=0;i. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_AGE_DESCRIPTION_H +#define PL_AGE_DESCRIPTION_H + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "hsUtils.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "../pnKeyedObject/plUoid.h" +#include "../plFile/plInitFileReader.h" + +// +// Age Definition File Reader/Writer +// +class hsStream; + +class plAgePage +{ + protected: + char *fName; + UInt32 fSeqSuffix; + Byte fFlags; + + public: + + static const UInt32 kInvalidSeqSuffix; + + enum Flags + { + kPreventAutoLoad = 0x01, + kLoadIfSDLPresent = 0x02, + kIsLocalOnly = 0x04, + kIsVolatile = 0x08, + }; + + plAgePage( const char *name, UInt32 seqSuffix, Byte flags ); + plAgePage( char *stringFrom ); + plAgePage( const plAgePage &src ); + plAgePage(); + ~plAgePage(); + + const char *GetName( void ) const { return fName; } + UInt32 GetSeqSuffix( void ) const { return fSeqSuffix; } + Byte GetFlags( void ) const { return fFlags; } + + void SetSeqSuffix( UInt32 s ) { fSeqSuffix = s; } + void SetFlags(Byte f, bool on=true); + + hsBool SetFromString( const char *string ); + char *GetAsString( void ) const; + + plAgePage &operator=( const plAgePage &src ); +}; + +// Derived from plInitSectionTokenReader so we can do nifty things with reading the files + +class plAgeDescription : public plInitSectionTokenReader +{ +private: + + char *fName; + + Int32 fPageIterator; + hsTArray fPages; + + plUnifiedTime fStart; + + float fDayLength; + short fMaxCapacity; + short fLingerTime; // seconds game instance should linger after last player leaves. -1 means never exit. + + Int32 fSeqPrefix; + UInt32 fReleaseVersion; // 0 for pre-release, 1+ for actual released ages + + static char *fCommonPages[]; + + void IInit( void ); + void IDeInit( void ); + + // Overload for plInitSectionTokenReader + virtual hsBool IParseToken( const char *token, hsStringTokenizer *tokenizer, UInt32 userData ); + +public: + static char kAgeDescPath[]; + + plAgeDescription(); + plAgeDescription( const char *fileNameToReadFrom ); + plAgeDescription( const plAgeDescription &src ) + { + IInit(); + CopyFrom( src ); + } + ~plAgeDescription(); + + bool ReadFromFile( const char *fileNameToReadFrom ) ; + void Read(hsStream* stream); + void Write(hsStream* stream) const; + + // Overload for plInitSectionTokenReader + virtual const char *GetSectionName( void ) const; + + const char *GetAgeName( void ) const { return fName; } + void SetAgeNameFromPath( const char *path ); + void SetAgeName(const char* ageName) { delete [] fName; fName=hsStrcpy(ageName); } + + // Page list + void ClearPageList(); + void RemovePage( const char *page ); + void AppendPage( const char *name, int seqSuffix = -1, Byte flags = 0 ); + + void SeekFirstPage( void ); + plAgePage *GetNextPage( void ); + int GetNumPages() const { return fPages.GetCount(); } + plAgePage *FindPage( const char *name ) const; + bool FindLocation(const plLocation& loc) const; + plLocation CalcPageLocation( const char *page ) const; + + // Getters + short GetStartMonth() const { return fStart.GetMonth(); } + short GetStartDay() const { return fStart.GetDay(); } + short GetStartYear() const { return fStart.GetYear(); } + short GetStartHour() const { return fStart.GetHour(); } + short GetStartMinute() const { return fStart.GetMinute(); } + short GetStartSecond() const { return fStart.GetSecond(); } + short GetMaxCapacity() const { return fMaxCapacity; } + short GetLingerTime() const { return fLingerTime;} + + float GetDayLength() const { return fDayLength; } + + Int32 GetSequencePrefix( void ) const { return fSeqPrefix; } + UInt32 GetReleaseVersion( void ) const { return fReleaseVersion; } + hsBool IsGlobalAge( void ) const { return ( fSeqPrefix < 0 ) ? true : false; } + + // Setters + hsBool SetStart(short year, short month, short day, short hour, short minute, short second) + { return fStart.SetTime(year,month,day,hour,minute,second); } + + void SetDayLength(const float l) { fDayLength = l; } + void SetMaxCapacity(const short m) { fMaxCapacity=m; } + void SetLingerTime(const short v) { fLingerTime=v;} + void SetSequencePrefix( Int32 p ) { fSeqPrefix = p; } + void SetReleaseVersion( UInt32 v ) { fReleaseVersion = v; } + + // calculations + double GetAgeElapsedDays(plUnifiedTime earthCurrentTime) const; + double GetAgeElapsedSeconds(const plUnifiedTime & earthCurrentTime) const; + int GetAgeTimeOfDaySecs(const plUnifiedTime& earthCurrentTime) const; + float GetAgeTimeOfDayPercent(const plUnifiedTime& earthCurrentTime) const; + + // Static functions for the available common pages + enum CommonPages + { + kTextures = 0, + kGlobal, + kNumCommonPages + }; + + static const char *GetCommonPage( int pageType ); + + void AppendCommonPages( void ); + void CopyFrom(const plAgeDescription& other); + + plAgeDescription &operator=( const plAgeDescription &src ) + { + CopyFrom( src ); + return *this; + } +}; + + + +#endif //PL_AGE_DESCRIPTION_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeManifest.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeManifest.cpp new file mode 100644 index 00000000..29fbd702 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeManifest.cpp @@ -0,0 +1,248 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plManifest - Collection of version-specific info about an age, such // +// as the actual files constructing it, timestamps, and // +// release versions. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plAgeManifest.h" +#include "hsUtils.h" + +#include "../plFile/hsFiles.h" +#include "../plFile/plFileUtils.h" +#include "../plFile/plInitFileReader.h" +#include "hsStringTokenizer.h" + + +//// plManifestFile /////////////////////////////////////////////////////// + +plManifestFile::plManifestFile(const char* name, const char* serverPath, const plMD5Checksum& check, UInt32 size, UInt32 zippedSize, UInt32 flags, bool md5Now) : + fChecksum(check), + fSize(size), + fZippedSize(zippedSize), + fFlags(flags), + fMd5Checked(md5Now) +{ + fName = name; + fServerPath = serverPath; + + if (md5Now) + { + DoMd5Check(); + } +} + +plManifestFile::~plManifestFile() +{ +} + +void plManifestFile::DoMd5Check() +{ + if (plFileUtils::FileExists(fName.c_str())) + { + plMD5Checksum localFile(fName.c_str()); + fIsLocalUpToDate = (localFile == fChecksum); + fLocalExists = true; + } + else + { + fIsLocalUpToDate = false; + fLocalExists = false; + } + + fMd5Checked = true; +} + +bool plManifestFile::IsLocalUpToDate() +{ + if (!fMd5Checked) + DoMd5Check(); + + return fIsLocalUpToDate; +} + +bool plManifestFile::LocalExists() +{ + if (!fMd5Checked) + DoMd5Check(); + + return fLocalExists; +} + +//// plManifest /////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// + +char* plManifest::fTimeFormat = "%m/%d/%y %H:%M:%S"; +static const UInt32 kLatestFormatVersion = 5; + +plManifest::plManifest() +{ + fFormatVersion = kLatestFormatVersion; + fAgeName = nil; +} + +plManifest::~plManifest() +{ + IReset(); +} + +void plManifest::IReset() +{ + fFormatVersion = 0; + + delete [] fAgeName; + + int i; + for (i = 0; i < fFiles.GetCount(); i++) + delete fFiles[i]; + fFiles.Reset(); +} + +//// Read and helpers //////////////////////////////////////////////////////// + +class plVersSection : public plInitSectionTokenReader +{ +protected: + plManifest* fDest; + + virtual const char* GetSectionName() const { return "version"; } + + virtual hsBool IParseToken(const char* token, hsStringTokenizer* tokenizer, UInt32 userData) + { + if (stricmp(token, "format") == 0) + fDest->SetFormatVersion(atoi(tokenizer->next())); + + return true; + } + +public: + plVersSection(plManifest* dest) : plInitSectionTokenReader(), fDest(dest) {} +}; + +class plGenericSection : public plInitSectionTokenReader +{ +protected: + plManifest* fDest; + + virtual void AddFile(plManifestFile* file) = 0; + + plManifestFile* IReadManifestFile(const char* token, hsStringTokenizer* tokenizer, UInt32 userData, bool isPage) + { + char name[256]; + strcpy(name, token); + UInt32 size = atoi(tokenizer->next()); + plMD5Checksum sum; + sum.SetFromHexString(tokenizer->next()); + UInt32 flags = atoi(tokenizer->next()); + UInt32 zippedSize = 0; + if (hsCheckBits(flags, plManifestFile::kFlagZipped)) + zippedSize = atoi(tokenizer->next()); + + return TRACKED_NEW plManifestFile(name, "", sum, size, zippedSize, flags); + } + + virtual hsBool IParseToken(const char* token, hsStringTokenizer* tokenizer, UInt32 userData) + { + plManifestFile* file = IReadManifestFile(token, tokenizer, userData, false); + AddFile(file); + return true; + } + +public: + plGenericSection(plManifest* dest) : plInitSectionTokenReader(), fDest(dest) {} +}; + +class plBaseSection : public plGenericSection +{ +public: + plBaseSection(plManifest* dest) : plGenericSection(dest) {} + +protected: + virtual void AddFile(plManifestFile* file) { fDest->AddFile(file); } + virtual const char* GetSectionName() const { return "base"; } +}; + + +bool plManifest::Read(hsStream* stream) +{ + plVersSection versReader(this); + plBaseSection baseReader(this); + + plInitSectionReader* readers[] = { &versReader, &baseReader, nil }; + + plInitFileReader reader(readers, 4096); // Allow extra long lines + reader.SetUnhandledSectionReader(&baseReader); + + // manifests don't need to be encrypted + reader.SetRequireEncrypted(false); + + if (!reader.Open(stream)) + return false; + + // Clear out before we read + IReset(); + + if (!reader.Parse()) + return false; + + return true; +} + +bool plManifest::Read(const char* filename) +{ + plVersSection versReader(this); + plBaseSection baseReader(this); + + plInitSectionReader* readers[] = { &versReader, &baseReader, nil }; + + plInitFileReader reader(readers, 4096); // Allow extra long lines + reader.SetUnhandledSectionReader(&baseReader); + + // manifests don't need to be encrypted + reader.SetRequireEncrypted(false); + + if (!reader.Open(filename)) + return false; + + // Clear out before we read + IReset(); + + if (!reader.Parse()) + return false; + + return true; +} + +void plManifest::AddFile(plManifestFile* file) +{ + fFiles.Append(file); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeManifest.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeManifest.h new file mode 100644 index 00000000..035cece0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeDescription/plAgeManifest.h @@ -0,0 +1,118 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plAgeManifest - Collection of version-specific info about an age, such // +// as the actual files constructing it, timestamps, and // +// release versions. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plAgeManifest_h +#define _plAgeManifest_h + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "hsUtils.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "../plFile/plInitFileReader.h" +#include "../plEncryption/plChecksum.h" + + +//// Small Container Classes for a Single File /////////////////////////////// + +class plManifestFile +{ +protected: + std::string fName; + std::string fServerPath; + plMD5Checksum fChecksum; + UInt32 fSize; + UInt32 fZippedSize; + UInt32 fFlags; + + bool fMd5Checked; + bool fIsLocalUpToDate; + bool fLocalExists; + +public: + // fUser flags + enum + { + // Sound files only + kSndFlagCacheSplit = 1<<0, + kSndFlagStreamCompressed = 1<<1, + kSndFlagCacheStereo = 1<<2, + // Any file + kFlagZipped = 1<<3, + }; + + plManifestFile(const char* name, const char* serverPath, const plMD5Checksum& check, UInt32 size, UInt32 zippedSize, UInt32 flags, bool md5Now = true); + virtual ~plManifestFile(); + + const char* GetName() const { return fName.c_str(); } + const char* GetServerPath() const { return fServerPath.c_str(); } + const plMD5Checksum& GetChecksum() const { return fChecksum; } + UInt32 GetDiskSize() const { return fSize; } + UInt32 GetDownloadSize() const { return hsCheckBits(fFlags, kFlagZipped) ? fZippedSize : fSize; } + UInt32 GetFlags() const { return fFlags; } + + void DoMd5Check(); + bool IsLocalUpToDate(); + bool LocalExists(); +}; + +//// Actual Manifest Class /////////////////////////////////////////////////// + +class plManifest +{ +protected: + UInt32 fFormatVersion; + char* fAgeName; // Mostly just for debugging + + hsTArray fFiles; + + void IReset(); + +public: + static char* fTimeFormat; // Standard string for the printed version of our timestamps + + void SetFormatVersion(UInt32 v) { fFormatVersion = v; } + void AddFile(plManifestFile* file); + + plManifest(); + virtual ~plManifest(); + + bool Read(const char* filename); + bool Read(hsStream* stream); + + UInt32 GetFormatVersion() const { return fFormatVersion; } + + UInt32 GetNumFiles() const { return fFiles.GetCount(); } + const plManifestFile& GetFile(UInt32 i) const { return *fFiles[i]; } +}; + +#endif //_plAgeManifest_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp new file mode 100644 index 00000000..212e9262 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.cpp @@ -0,0 +1,455 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAgeLoader.h" +#include "hsStream.h" +#include "plgDispatch.h" +#include "hsResMgr.h" +//#include "hsTimer.h" +#include "plResPatcher.h" +#include "plBackgroundDownloader.h" +#include "process.h" // for getpid() + +#include "../pnProduct/pnProduct.h" + +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plClientMsg.h" +#include "../pnNetCommon/plNetApp.h" + +#include "../plScene/plRelevanceMgr.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plAgeDescription/plAgeDescription.h" +#include "../plSDL/plSDL.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plResMgr/plRegistryHelpers.h" +#include "../plResMgr/plRegistryNode.h" +#include "../plResMgr/plResManager.h" +#include "../plFile/plEncryptedStream.h" + +/// TEMP HACK TO LOAD CONSOLE INIT FILES ON AGE LOAD +#include "../plMessage/plConsoleMsg.h" +#include "../plMessage/plLoadAvatarMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" + + +extern hsBool gDataServerLocal; +extern hsBool gUseBackgroundDownloader; + +// static +plAgeLoader* plAgeLoader::fInstance=nil; + +// +// CONSTRUCT +// +plAgeLoader::plAgeLoader() : + fInitialAgeState(nil), + fFlags(0) +{ +} + +// +// DESTRUCT +// +plAgeLoader::~plAgeLoader() +{ + delete fInitialAgeState; + fInitialAgeState=nil; + + if ( PendingAgeFniFiles().size() ) + plNetClientApp::StaticErrorMsg( "~plAgeLoader(): %d pending age fni files", PendingAgeFniFiles().size() ); + if ( PendingPageOuts().size() ) + plNetClientApp::StaticErrorMsg( "~plAgeLoader(): %d pending page outs", PendingPageOuts().size() ); + + ClearPageExcludeList(); // Clear our debugging exclude list, just to be tidy + + if (fInstance==this) + SetInstance(nil); +} + +void plAgeLoader::Shutdown() +{ + +} + +void plAgeLoader::Init() +{ + RegisterAs( kAgeLoader_KEY ); + plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plClientMsg::Index(), GetKey()); + + if (!gDataServerLocal && gUseBackgroundDownloader) + plBackgroundDownloader::StartThread(); +} + +// +// STATIC +// +plAgeLoader* plAgeLoader::GetInstance() +{ + return fInstance; +} + +// +// STATIC +// +void plAgeLoader::SetInstance(plAgeLoader* inst) +{ + fInstance=inst; +} + +// +// Plasma Msg Handler +// +hsBool plAgeLoader::MsgReceive(plMessage* msg) +{ + plInitialAgeStateLoadedMsg *stateMsg = plInitialAgeStateLoadedMsg::ConvertNoRef( msg ); + if( stateMsg != nil ) + { + // done receiving the initial state of the age from the server + return true; + } + + plClientMsg* clientMsg = plClientMsg::ConvertNoRef(msg); + if (clientMsg && clientMsg->GetClientMsgFlag()==plClientMsg::kInitComplete) + { + ExecPendingAgeFniFiles(); // exec age-specific fni files + return true; + } + + + return plReceiver::MsgReceive(msg); +} + +// +// read in the age desc file and page in/out the rooms belonging to the specified age. +// return false on error +// +//============================================================================ +bool plAgeLoader::LoadAge(const char ageName[]) +{ + return ILoadAge(ageName); +} + +//============================================================================ +bool plAgeLoader::UpdateAge(const char ageName[]) +{ + bool result = true; + + if (!gDataServerLocal) + { + plResPatcher myPatcher(ageName); + result = myPatcher.Update(); + } + + return result; +} + +//============================================================================ +void plAgeLoader::NotifyAgeLoaded( bool loaded ) +{ + if ( loaded ) + fFlags &= ~kLoadingAge; + else + fFlags &= ~kUnLoadingAge; + + plAgeLoadedMsg * msg = TRACKED_NEW plAgeLoadedMsg; + msg->fLoaded = loaded; + msg->Send(); +} + + +//// ILoadAge //////////////////////////////////////////////////////////////// +// Does the loading-specific stuff for queueing an age to load + +bool plAgeLoader::ILoadAge(const char ageName[]) +{ + plNetClientApp* nc = plNetClientApp::GetInstance(); + ASSERT(!nc->GetFlagsBit(plNetClientApp::kPlayingGame)); + + StrCopy(fAgeName, ageName, arrsize(fAgeName)); + + nc->DebugMsg( "Net: Loading age %s", fAgeName); + + if ((fFlags & kLoadMask) != 0) + ErrorFatal(__LINE__, __FILE__, "Fatal Error:\nAlready loading or unloading an age.\n%S will now exit.", ProductShortName()); + + fFlags |= kLoadingAge; + + plAgeBeginLoadingMsg* ageBeginLoading = TRACKED_NEW plAgeBeginLoadingMsg(); + ageBeginLoading->Send(); + + /////////////////////////////////////////////////////// + + + /// Step 1: Update all of the dat files for this age + /* + UpdateAge(fAgeName); + */ + + /// Step 2: Load the keys for this age, so we can find sceneNodes for them + // exec age .fni file when data is done loading + char consoleIniName[ 256 ]; + sprintf( consoleIniName, "dat\\%s.fni", fAgeName); + fPendingAgeFniFiles.push_back( consoleIniName ); + + char csvName[256]; + sprintf(csvName, "dat\\%s.csv", fAgeName); + fPendingAgeCsvFiles.push_back(csvName); + + plSynchEnabler p( false ); // turn off dirty tracking while in this function + + hsStream* stream=GetAgeDescFileStream(fAgeName); + if (!stream) + { + nc->ErrorMsg("Failed loading age. Age desc file %s has nil stream", fAgeName); + fFlags &= ~kLoadingAge; + return false; + } + + plAgeDescription ad; + ad.Read(stream); + ad.SetAgeName(fAgeName); + stream->Close(); + delete stream; + ad.SeekFirstPage(); + + plAgePage *page; + plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY ); + + // Copy, exclude pages we want excluded, and collect our scene nodes + fCurAgeDescription.CopyFrom(ad); + while( ( page = ad.GetNextPage() ) != nil ) + { + if( IsPageExcluded( page, fAgeName) ) + continue; + + plKey roomKey = plKeyFinder::Instance().FindSceneNodeKey( fAgeName, page->GetName() ); + if( roomKey != nil ) + AddPendingPageInRoomKey( roomKey ); + } + ad.SeekFirstPage(); + + + // Tell the client to load-and-hold all the keys for this age, to make the loading process work better + plClientMsg *loadAgeKeysMsg = TRACKED_NEW plClientMsg( plClientMsg::kLoadAgeKeys ); + loadAgeKeysMsg->SetAgeName( fAgeName); + loadAgeKeysMsg->Send( clientKey ); + + // + // Load the Age's SDL Hook object (and it's python modifier) + // + plUoid oid=nc->GetAgeSDLObjectUoid(fAgeName); + plKey ageSDLObjectKey = hsgResMgr::ResMgr()->FindKey(oid); + if (ageSDLObjectKey) + hsgResMgr::ResMgr()->AddViaNotify(ageSDLObjectKey, TRACKED_NEW plGenRefMsg(nc->GetKey(), plRefMsg::kOnCreate, -1, + plNetClientMgr::kAgeSDLHook), plRefFlags::kActiveRef); + + int nPages = 0; + + plClientMsg* pMsg1 = TRACKED_NEW plClientMsg(plClientMsg::kLoadRoom); + pMsg1->SetAgeName(fAgeName); + + // Loop and ref! + while( ( page = ad.GetNextPage() ) != nil ) + { + if( IsPageExcluded( page, fAgeName) ) + { + nc->DebugMsg( "\tExcluding page %s\n", page->GetName() ); + continue; + } + + nPages++; + + pMsg1->AddRoomLoc(ad.CalcPageLocation(page->GetName())); + nc->DebugMsg("\tPaging in room %s\n", page->GetName()); + } + + pMsg1->Send(clientKey); + + // Send the client a message to let go of the extra keys it was holding on to + plClientMsg *dumpAgeKeys = TRACKED_NEW plClientMsg( plClientMsg::kReleaseAgeKeys ); + dumpAgeKeys->SetAgeName( fAgeName); + dumpAgeKeys->Send( clientKey ); + + if ( nPages==0 ) + { + // age is done loading because it has no pages? + fFlags &= ~kLoadingAge; + } + + return true; +} + +//// plUnloadAgeCollector //////////////////////////////////////////////////// +// Registry page iterator to collect all the loaded pages of a given age +// Note: we have to do an IterateAllPages(), since we want to also catch +// pages that are partially loaded, which are skipped in the vanilla +// IteratePages() call. + +class plUnloadAgeCollector : public plRegistryPageIterator +{ + public: + hsTArray fPages; + const char *fAge; + + plUnloadAgeCollector( const char *a ) : fAge( a ) {} + + virtual hsBool EatPage( plRegistryPageNode *page ) + { + if( fAge && stricmp( page->GetPageInfo().GetAge(), fAge ) == 0 ) + { + fPages.Append( page ); + } + + return true; + } +}; + +//// IUnloadAge ////////////////////////////////////////////////////////////// +// Does the UNloading-specific stuff for queueing an age to unload. +// Far simpler that ILoadAge :) + +bool plAgeLoader::IUnloadAge() +{ + plNetClientApp* nc = plNetClientApp::GetInstance(); + nc->DebugMsg( "Net: Unloading age %s", fAgeName); + + hsAssert( (fFlags & kLoadMask)==0, "already loading or unloading an age?"); + fFlags |= kUnLoadingAge; + + plAgeBeginLoadingMsg* msg = TRACKED_NEW plAgeBeginLoadingMsg(); + msg->fLoading = false; + msg->Send(); + + // Note: instead of going from the .age file, we just want a list of what + // is REALLY paged in for this age. So ask the resMgr! + plUnloadAgeCollector collector( fAgeName); + // WARNING: unsafe cast here, but it's ok, until somebody is mean and makes a non-plResManager resMgr + ( (plResManager *)hsgResMgr::ResMgr() )->IterateAllPages( &collector ); + + // Dat was easy... + plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY ); + + // Build up a list of all the rooms we're going to page out + plKeyVec newPageOuts; + + int i; + for( i = 0; i < collector.fPages.GetCount(); i++ ) + { + plRegistryPageNode *page = collector.fPages[ i ]; + + plKey roomKey = plKeyFinder::Instance().FindSceneNodeKey( page->GetPageInfo().GetLocation() ); + if( roomKey != nil && roomKey->ObjectIsLoaded() ) + { + nc->DebugMsg( "\tPaging out room %s\n", page->GetPageInfo().GetPage() ); + newPageOuts.push_back(roomKey); + } + } + + // Put them in our pending page outs + for( i = 0; i < newPageOuts.size(); i++ ) + fPendingPageOuts.push_back(newPageOuts[i]); + + // ...then send the unload messages. That way we ensure the list is complete + // before any messages get processed + for( i = 0; i < newPageOuts.size(); i++ ) + { + plClientMsg *pMsg1 = TRACKED_NEW plClientMsg( plClientMsg::kUnloadRoom ); + pMsg1->AddRoomLoc(newPageOuts[i]->GetUoid().GetLocation()); + pMsg1->Send( clientKey ); + } + + if ( newPageOuts.size()==0 ) + { + // age is done unloading because it has no pages? + NotifyAgeLoaded( false ); + } + + return true; +} + + +void plAgeLoader::ExecPendingAgeFniFiles() +{ + int i; + for (i=0;iParseCsvInput(stream); + stream->Close(); + delete stream; + } + } + fPendingAgeCsvFiles.clear(); +} + +// +// return alloced stream or nil +// static +// +hsStream* plAgeLoader::GetAgeDescFileStream(const char* ageName) +{ + if (!ageName) + return nil; + + char ageDescFileName[256]; + sprintf(ageDescFileName, "dat\\%s.age", ageName); + + hsStream* stream = plEncryptedStream::OpenEncryptedFile(ageDescFileName); + if (!stream) + { + char str[256]; + sprintf(str, "Can't find age desc file %s", ageDescFileName); + hsAssert(false, str); + return nil; + } + + return stream; +} + +// +// sent from server with joinAck +// +void plAgeLoader::ISetInitialAgeState(plStateDataRecord* s) +{ + hsAssert(fInitialAgeState != s, "duplicate initial age state"); + delete fInitialAgeState; + fInitialAgeState=s; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.h new file mode 100644 index 00000000..7cb17121 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoader.h @@ -0,0 +1,129 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAgeLoader_h +#define plAgeLoader_h + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plAgeDescription/plAgeDescription.h" + +#include "../plUUID/plUUID.h" + +// +// A singleton class which manages loading and unloading ages and operations associated with that +// + +// fwd decls +class plStateDataRecord; +class plMessage; +class plOperationProgress; + +class plAgeLoader : public hsKeyedObject +{ + friend class plNetClientMsgHandler; + friend class plNetClientJoinTask; +private: + typedef std::vector plKeyVec; + typedef std::vector plStringVec; + + enum Flags + { + kLoadingAge = 0x1, + kUnLoadingAge = 0x2, + kLoadMask = (kLoadingAge | kUnLoadingAge) + }; + + static plAgeLoader* fInstance; + + UInt32 fFlags; + plStringVec fPendingAgeFniFiles; // list of age .fni files to be parsed + plStringVec fPendingAgeCsvFiles; // list of age .csv files to be parsed + plKeyVec fPendingPageIns; // keys of rooms which are currently being paged in. + plKeyVec fPendingPageOuts; // keys of rooms which are currently being paged out. + plAgeDescription fCurAgeDescription; + plStateDataRecord* fInitialAgeState; + char fAgeName[kMaxAgeNameLength]; + + bool ILoadAge(const char ageName[]); + bool IUnloadAge(); + void ISetInitialAgeState(plStateDataRecord* s); // sent from server with joinAck + const plStateDataRecord* IGetInitialAgeState() const { return fInitialAgeState; } + +public: + plAgeLoader(); + ~plAgeLoader(); + + CLASSNAME_REGISTER( plAgeLoader); + GETINTERFACE_ANY( plAgeLoader, hsKeyedObject); + + static plAgeLoader* GetInstance(); + static void SetInstance(plAgeLoader* inst); + static hsStream* GetAgeDescFileStream(const char* ageName); + + void Init(); + void Shutdown(); + hsBool MsgReceive(plMessage* msg); + bool LoadAge(const char ageName[]); + bool UnloadAge() { return IUnloadAge(); } + bool UpdateAge(const char ageName[]); + void NotifyAgeLoaded( bool loaded ); + + const plKeyVec& PendingPageOuts() const { return fPendingPageOuts; } + const plKeyVec& PendingPageIns() const { return fPendingPageIns; } + const plStringVec& PendingAgeCsvFiles() const { return fPendingAgeCsvFiles; } + const plStringVec& PendingAgeFniFiles() const { return fPendingAgeFniFiles; } + + void AddPendingPageInRoomKey(plKey r); + bool RemovePendingPageInRoomKey(plKey r); + bool IsPendingPageInRoomKey(plKey p, int* idx=nil); + + void ExecPendingAgeFniFiles(); + void ExecPendingAgeCsvFiles(); + + // Fun debugging exclude commands (to prevent certain pages from loading) + void ClearPageExcludeList( void ); + void AddExcludedPage( const char *pageName, const char *ageName = nil ); + bool IsPageExcluded( const plAgePage *page, const char *ageName = nil ); + + const plAgeDescription &GetCurrAgeDesc( void ) const { return fCurAgeDescription; } + + // paging + void FinishedPagingInRoom(plKey* rmKey, int numRms); // call when finished paging in/out a room + void StartPagingOutRoom(plKey* rmKey, int numRms); // call when starting to page in/out a room + void FinishedPagingOutRoom(plKey* rmKey, int numRms); + // Called on page-in-hold rooms, since we don't want them actually paging out in the NCM (i.e. sending info to the server) + void IgnorePagingOutRoom(plKey* rmKey, int numRms); + + bool IsLoadingAge(){ return (fFlags & (kUnLoadingAge | kLoadingAge)); } +}; + +#endif // plAgeLoader_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderCreatable.h new file mode 100644 index 00000000..8c865af2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderCreatable.h @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAgeLoaderCreatable_inc +#define plAgeLoaderCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plAgeLoader.h" +REGISTER_CREATABLE( plAgeLoader); + +#endif // plAgeLoaderCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderPaging.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderPaging.cpp new file mode 100644 index 00000000..d20969c6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plAgeLoaderPaging.cpp @@ -0,0 +1,321 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAgeLoader.h" + +#include "hsTimer.h" +#include "hsResMgr.h" +#include "plgDispatch.h" + +#include + +#include "../pnNetCommon/plNetApp.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plMessage/plAgeLoadedMsg.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plProgressMgr/plProgressMgr.h" +#include "../plSDL/plSDL.h" +#include "../pnDispatch/plDispatch.h" +#include "../plResMgr/plResManager.h" + +#include "../plNetClient/plNetClientMgr.h" + +// +// if room is reserved or for animations, don't report it to the server. +// the server only cares about rooms which have real, networked objects in them. +// The server already assigns a global room to everyone for things like avatar and builtin state . +// +bool ReportRoomToServer(const plKey &key) +{ + plLocation keyLoc=key->GetUoid().GetLocation(); + bool skip=(keyLoc.IsReserved() || keyLoc.IsVirtual() || + // HACK ALERT - replace with new uoid type flags + (key->GetName() && + (!strnicmp(key->GetName(), "global", 6) || + strstr(key->GetName(), "_Male") || + strstr(key->GetName(), "_Female") + ) + ) + ); + + if (skip) + hsLogEntry(plNetApp::StaticDebugMsg("Not reporting room %s to server, reserved=%d, virtual=%d", + key->GetName(), keyLoc.IsReserved(), keyLoc.IsVirtual())); + + return !skip; +} + +// +// call when finished paging in a room. +// +void plAgeLoader::FinishedPagingInRoom(plKey* rmKey, int numRms) +{ + if (numRms==0) + return; + + unsigned pendingPageIns = PendingPageIns().size(); + + plNetClientApp* nc = plNetClientApp::GetInstance(); + + // Send a msg to the server indicating that we have this room paged in + plNetMsgPagingRoom * pagingMsg = TRACKED_NEW plNetMsgPagingRoom; + pagingMsg->SetNetProtocol(kNetProtocolCli2Game); + int i; + for(i=0;iAddRoom(key); + hsLogEntry(nc->DebugMsg("\tSending PageIn/RequestState msg, room=%s\n", key->GetName())); + } + if( pagingMsg->GetNumRooms() > 0 ) // all rooms were reserved + { + plNetClientMgr * mgr = plNetClientMgr::GetInstance(); + mgr->AddPendingPagingRoomMsg( pagingMsg ); + } + else + delete pagingMsg; + + // If any of these rooms were queued for load by us, then we may be done loading the age. + if (pendingPageIns != PendingPageIns().size()) + { + bool ageLoaded = (PendingPageIns().size()==0) && (fFlags & kLoadingAge); + if (ageLoaded) + { + plAgeLoaded2Msg * msg = TRACKED_NEW plAgeLoaded2Msg; + msg->Send(); + // join task will call NotifyAgeLoaded for us later + } + } +} + +// +// called by the client when a room is finished paging out +// +void plAgeLoader::FinishedPagingOutRoom(plKey* rmKey, int numRms) +{ + plNetClientApp* nc = plNetClientApp::GetInstance(); + nc->StayAlive(hsTimer::GetSysSeconds()); // alive + + int i; + for(i=0;iDebugMsg("Finished paging out room %s", rmKey[i]->GetName()); + } + } + + if (PendingPageOuts().size() == 0 && (fFlags & kUnLoadingAge)) + { + NotifyAgeLoaded( false ); + } + +} + + +// +// call when starting to page out a room. +// allows server to transfer ownership of room objects to someone else. +// +void plAgeLoader::StartPagingOutRoom(plKey* rmKey, int numRms) +{ + plNetClientApp* nc = plNetClientApp::GetInstance(); + + plNetMsgPagingRoom pagingMsg; + pagingMsg.SetNetProtocol(kNetProtocolCli2Game); + pagingMsg.SetPagingOut(true); + int i; + for(i=0;iDebugMsg("\tSending PageOut msg, room=%s", rmKey[i]->GetName()); + } + + if (!pagingMsg.GetNumRooms()) // all rooms were reserved + return; + + nc->SendMsg(&pagingMsg); +} + +// Client telling us that this page isn't going to get the start/finish combo +// on page out, most likely because it was a load-and-hold, not a load. So take +// it from our pending list but don't actually process it +// Note: right now it's just a dup of FinishPagingOutRoom(), but since the latter +// might change later, we go ahead and dup to avoid unnecessary bugs later +void plAgeLoader::IgnorePagingOutRoom(plKey* rmKey, int numRms) +{ + plNetClientApp* nc = plNetClientApp::GetInstance(); + nc->StayAlive(hsTimer::GetSysSeconds()); // alive + + int i; + for(i=0;iDebugMsg("Ignoring paged out room %s", rmKey[i]->GetName()); + } + } + + if (PendingPageOuts().size() == 0 && (fFlags & kUnLoadingAge)) + { + NotifyAgeLoaded( false ); + } +} + +/////////////////////////////////// + +bool plAgeLoader::IsPendingPageInRoomKey(plKey pKey, int *idx) +{ + if (pKey) + { + plKeyVec::iterator result=std::find(fPendingPageIns.begin(), fPendingPageIns.end(), pKey); + bool found = result!=fPendingPageIns.end(); + if (idx) + *idx = found ? result-fPendingPageIns.begin() : -1; + return found; + } + return false; +} + +void plAgeLoader::AddPendingPageInRoomKey(plKey pKey) +{ + if (!IsPendingPageInRoomKey(pKey)) + { + fPendingPageIns.push_back(pKey); + } +} + +bool plAgeLoader::RemovePendingPageInRoomKey(plKey pKey) +{ + int idx; + if (IsPendingPageInRoomKey(pKey, &idx)) + { + fPendingPageIns.erase(fPendingPageIns.begin()+idx); // remove key from list + return true; + } + return false; +} + +////////////////////////////////////////////////////////////////////////////// +//// Page Exclusion ////////////////////////////////////////////////////////// +// // +// Fun debugging exclude commands (to prevent certain pages from loading) // +// // +////////////////////////////////////////////////////////////////////////////// + +class plExcludePage +{ + public: + char *fPageName; + char *fAgeName; + + plExcludePage() { fPageName = nil; fAgeName = nil; } + plExcludePage( char *p, char *a ) + { + fPageName = p; + fAgeName = a; + } +}; + +static hsTArray sExcludeList; + +void plAgeLoader::ClearPageExcludeList( void ) +{ + int i; + + + for( i = 0; i < sExcludeList.GetCount(); i++ ) + { + delete [] sExcludeList[ i ].fPageName; + delete [] sExcludeList[ i ].fAgeName; + } +} + +void plAgeLoader::AddExcludedPage( const char *pageName, const char *ageName ) +{ + char *p = hsStrcpy( pageName ); + char *a = nil; + if( ageName != nil ) + a = hsStrcpy( ageName ); + + sExcludeList.Append( plExcludePage( p, a ) ); +} + +bool plAgeLoader::IsPageExcluded( const plAgePage *page, const char *ageName ) +{ + // check page flags + if (page->GetFlags() & plAgePage::kPreventAutoLoad) + return true; + + // check exclude list + const char* pageName = page->GetName(); + int i; + for( i = 0; i < sExcludeList.GetCount(); i++ ) + { + if( stricmp( pageName, sExcludeList[ i ].fPageName ) == 0 ) + { + if( ageName == nil || sExcludeList[ i ].fAgeName == nil || + stricmp( ageName, sExcludeList[ i ].fAgeName ) == 0 ) + { + return true; + } + } + } + + // Check if pages are excluded due to age SDL vars + if (page->GetFlags() & plAgePage::kLoadIfSDLPresent) + { + if (IGetInitialAgeState()) + { + plSimpleStateVariable* sdVar = IGetInitialAgeState()->FindVar(pageName); + if (!sdVar) + return true; // no sdl var, exclude + + bool value; + sdVar->Get(&value); + return value ? false : true; // exclude if var is false + } + else + return true; // no age state, exclude + } + + return false; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plBackgroundDownloader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plBackgroundDownloader.cpp new file mode 100644 index 00000000..fc68b08c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plBackgroundDownloader.cpp @@ -0,0 +1,529 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plBackgroundDownloader.h" + +#include + +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" +#include "../plEncryption/plChecksum.h" + +#include "../NucleusLib/inc/hsResMgr.h" + +#include "../plAgeDescription/plAgeManifest.h" +#include "../plResMgr/plResManager.h" +#include "../plFile/plFileUtils.h" +#include "../plFile/plEncryptedStream.h" +#include "../plCompression/plZlibStream.h" +#include "../plAudioCore/plAudioFileReader.h" +#include "../plProgressMgr/plProgressMgr.h" + +#include "../pnAsyncCore/pnAsyncCore.h" +#include "../pnNetCli/pnNetCli.h" +#include "../plNetGameLib/plNetGameLib.h" + +#include "../pnDispatch/plDispatch.h" +#include "../plStatusLog/plStatusLog.h" + +static const unsigned kMaxDownloadTries = 10; +static const wchar s_manifest[] = L"AllAges"; + +plBackgroundDownloader* plBackgroundDownloader::fInstance = NULL; + +hsBool gUseBackgroundDownloader = false; + +//============================================================================ +enum DownloaderLogType +{ + kHeader, + kInfo, + kMajorStatus, + kStatus, + kError, +}; +void BackgroundDownloaderLog(DownloaderLogType type, const char* format, ...) +{ + UInt32 color = 0; + switch (type) + { + case kHeader: color = plStatusLog::kWhite; break; + case kInfo: color = plStatusLog::kBlue; break; + case kMajorStatus: color = plStatusLog::kYellow; break; + case kStatus: color = plStatusLog::kGreen; break; + case kError: color = plStatusLog::kRed; break; + } + + static plStatusLog* gStatusLog = nil; + if (!gStatusLog) + { + gStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog( + 20, + "bgdownload.log", + plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kDeleteForMe); + } + + va_list args; + va_start(args, format); + + gStatusLog->AddLineV(color, format, args); + + va_end(args); +} + +//============================================================================ +class plBGDownloadStream : public plZlibStream +{ +public: + plBGDownloadStream() : plZlibStream() {} + virtual ~plBGDownloadStream() {} + + virtual UInt32 Write(UInt32 byteCount, const void* buffer); +}; + +UInt32 plBGDownloadStream::Write(UInt32 byteCount, const void* buffer) +{ + return plZlibStream::Write(byteCount, buffer); +} + +//============================================================================ +static void DownloadFileCallback(ENetError result, void* param, const wchar filename[], hsStream* writer) +{ + plBackgroundDownloader* bgdownloader = (plBackgroundDownloader*)param; + + // Retry download unless shutting down or file not found + switch (result) { + case kNetSuccess: + writer->Close(); + bgdownloader->DoneWithFile(true); + break; + + case kNetErrFileNotFound: + case kNetErrRemoteShutdown: + writer->Close(); + bgdownloader->DoneWithFile(false); + break; + + default: + writer->Rewind(); + NetCliFileDownloadRequest( + filename, + writer, + DownloadFileCallback, + param + ); + break; + } +} + +static void ManifestCallback(ENetError result, void* param, const wchar group[], const NetCliFileManifestEntry manifest[], unsigned entryCount) +{ + plBackgroundDownloader* bgdownloader = (plBackgroundDownloader*)param; + bgdownloader->DoneWithManifest(result == kNetSuccess, manifest, entryCount); +} + +//============================================================================ +plBackgroundDownloader* plBackgroundDownloader::GetInstance() +{ + return fInstance; +} + +void plBackgroundDownloader::ThreadMain(void * param) +{ + Init(); + + plBackgroundDownloader::GetInstance()->Run(); + plBackgroundDownloader::GetInstance()->CleanUp(); + + Shutdown(); +} + +void plBackgroundDownloader::StartThread() +{ + _beginthread(plBackgroundDownloader::ThreadMain, 0, NULL); +} + +void plBackgroundDownloader::Init() +{ + fInstance = TRACKED_NEW plBackgroundDownloader(); +} + +void plBackgroundDownloader::Shutdown() +{ + delete fInstance; + fInstance = NULL; +} + +plBackgroundDownloader::plBackgroundDownloader() +{ + BackgroundDownloaderLog(kHeader, "--- Starting background download ---"); + + fBGDownloaderRun = CreateEvent( + NULL, // default security attributes + TRUE, // manual-reset event + FALSE, // initial state is signaled + NULL // unnamed + ); + + fBGDownloaderIsPaused = CreateEvent( + NULL, // default security attributes + FALSE, // manual-reset event + TRUE, // initial state is signaled + NULL // unnamed + ); +} + +plBackgroundDownloader::~plBackgroundDownloader() +{ + HANDLE runHandle = fBGDownloaderRun; + fBGDownloaderRun = NULL; + CloseHandle(runHandle); + + HANDLE pausedHandle = fBGDownloaderIsPaused; + fBGDownloaderIsPaused = NULL; + CloseHandle(pausedHandle); + + BackgroundDownloaderLog(kHeader, "--- Background download done ---"); +} + +UInt32 plBackgroundDownloader::IGetDownloadSize() +{ + if (!IGetDataManifest()) + return 0; + + UInt32 downloadSize = 0; + UInt32 downloadFiles = 0; + for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i) + { + plManifestFile* mfsFile = (*i); + + if (!mfsFile->IsLocalUpToDate()) + { + downloadFiles++; + downloadSize += mfsFile->GetDownloadSize(); + } + } + + BackgroundDownloaderLog(kInfo, "Got download stats, %d files, %d bytes", downloadFiles, downloadSize); + + return downloadSize; +} + +bool plBackgroundDownloader::CheckFreeSpace(UInt32 bytesNeeded) +{ +#ifdef HS_BUILD_FOR_WIN32 + ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, neededBytes; + if (GetDiskFreeSpaceEx(NULL, &freeBytesAvailable, &totalNumberOfBytes, NULL)) + { + neededBytes.HighPart = 0; + neededBytes.LowPart = bytesNeeded; + + if (neededBytes.QuadPart > freeBytesAvailable.QuadPart) + { + BackgroundDownloaderLog(kInfo, "Not enough disk space (asked for %d bytes)", bytesNeeded); + return false; + } + } +#endif // HS_BUILD_FOR_WIN32 + + return true; +} + +bool plBackgroundDownloader::IDecompressSound(plManifestFile* mfsFile, bool noOverwrite) +{ + UInt32 flags = mfsFile->GetFlags(); + + if ( (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit) || hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) && stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "ogg") == 0) + { + plAudioFileReader* reader = plAudioFileReader::CreateReader(mfsFile->GetName(), plAudioCore::kAll, plAudioFileReader::kStreamNative); + if (!reader) + { + BackgroundDownloaderLog(kInfo, "Unable to create audio file reader for %s", mfsFile->GetName()); + return false; + } + + UInt32 size = reader->GetDataSize(); + delete reader; + + // Make sure we have enough free space + if (!CheckFreeSpace(size)) + return false; + + if (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit)) + plAudioFileReader::CacheFile(mfsFile->GetName(), true, noOverwrite); + if (hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) + plAudioFileReader::CacheFile(mfsFile->GetName(), false, noOverwrite); + } + + return true; +} + +bool plBackgroundDownloader::Run() +{ + // Wait to be signaled that we've gotten at least as far as the startup age + WaitForSingleObject(fBGDownloaderRun, INFINITE); + + IGetDataManifest(); + + plFileUtils::CreateDir("dat"); + plFileUtils::CreateDir("sfx"); + + bool result = true; + plResManager* resMgr = ((plResManager*)hsgResMgr::ResMgr()); + + for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i) + { + plManifestFile* mfsFile = (*i); + + if (!mfsFile->IsLocalUpToDate()) + { + if (!CheckFreeSpace(mfsFile->GetDiskSize())) + return false; + + FileType type = IGetFile(mfsFile); + if (type == kPrp) + { + // Checks for existence before attempting to remove + resMgr->RemoveSinglePage(mfsFile->GetName()); + if (!resMgr->FindSinglePage(mfsFile->GetName())) + { + resMgr->AddSinglePage(mfsFile->GetName()); + } + } + else if (type == kOther) + { + if (!IDecompressSound(mfsFile, false)) + { + char text[MAX_PATH]; + StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName()); + BackgroundDownloaderLog(kInfo, text ); + hsAssert(false, text); + result = false; + } + } + else + { + char text[MAX_PATH]; + StrPrintf(text, arrsize(text), "Failed downloading file: %s", mfsFile->GetName()); + BackgroundDownloaderLog(kInfo, text ); + hsAssert(false, text); + result = false; + } + } + } + + return result; +} + +void plBackgroundDownloader::CleanUp() +{ + BackgroundDownloaderLog(kMajorStatus, "Cleaning up background downloader..." ); + + for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i) + { + plManifestFile* file = (*i); + delete file; + } + fMfsVec.clear(); +} + +void plBackgroundDownloader::Pause() +{ + if (fBGDownloaderRun != NULL && fBGDownloaderIsPaused != NULL) + { + ResetEvent(fBGDownloaderRun); + WaitForSingleObject(fBGDownloaderIsPaused, INFINITE); + + BackgroundDownloaderLog(kStatus, "--- Background download paused ---"); + } +} + +void plBackgroundDownloader::UnPause() +{ + if (fBGDownloaderRun != NULL && fBGDownloaderIsPaused != NULL) + { + SetEvent(fBGDownloaderRun); + + BackgroundDownloaderLog(kStatus, "--- Background download resumed ---"); + } +} + +plBackgroundDownloader::FileType plBackgroundDownloader::IGetFile(const plManifestFile* mfsFile) +{ + BackgroundDownloaderLog(kInfo, " Setting up to download file %s", mfsFile->GetName()); + + bool downloadDone = false; + wchar* wServerPath = hsStringToWString(mfsFile->GetServerPath()); + int numTries = 0; + + while (!downloadDone) + { + if (WaitForSingleObject(fBGDownloaderRun, 0) == WAIT_TIMEOUT) + SignalObjectAndWait(fBGDownloaderIsPaused, fBGDownloaderRun, INFINITE, FALSE); + + if (numTries >= kMaxDownloadTries) + { + BackgroundDownloaderLog(kInfo, " Max download tries exceeded (%d). Aborting download...", kMaxDownloadTries); + return kFail; + } + + plBGDownloadStream* downloadStream = TRACKED_NEW plBGDownloadStream(); + if (!downloadStream->Open(mfsFile->GetName(), "wb")) + { + BackgroundDownloaderLog(kInfo, " Unable to create file. Aborting download..."); + return kFail; + } + + BackgroundDownloaderLog(kInfo, " Downloading file %s...", mfsFile->GetName()); + + fSuccess = false; + fDoneWithFile = false; + NetCliFileDownloadRequest( + wServerPath, + downloadStream, + DownloadFileCallback, + this + ); + + while (!fDoneWithFile) { + AsyncSleep(100); + } + + if (!fSuccess) { + // remove partial file and die (server didn't have the file or server is shutting down) + plFileUtils::RemoveFile(mfsFile->GetName(), true); + BackgroundDownloaderLog(kError, " File %s failed to download.", mfsFile->GetName()); + } + else { + AsyncSleep(100); + if (downloadStream->DecompressedOk()) { + BackgroundDownloaderLog(kInfo, " Decompress successful." ); + // download and decompress successful, do a md5 check on the resulting file + plMD5Checksum localMD5(mfsFile->GetName()); + if (localMD5 != mfsFile->GetChecksum()) { + plFileUtils::RemoveFile(mfsFile->GetName(), true); + BackgroundDownloaderLog(kError, " File %s MD5 check FAILED.", mfsFile->GetName()); + // don't set downloadDone so we attempt to re-download from the server + } + else { + BackgroundDownloaderLog(kInfo, " MD5 check succeeded."); + downloadDone = true; + } + } + else { + plFileUtils::RemoveFile(mfsFile->GetName(), true); + BackgroundDownloaderLog(kError, " File %s failed to decompress.", mfsFile->GetName()); + // don't set downloadDone so we attempt to re-download from the server + } + } + + delete downloadStream; + + ++numTries; + } + delete [] wServerPath; + + if (!fSuccess) + return kFail; + + if (stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "prp") == 0) + return kPrp; + + return kOther; +} + +bool plBackgroundDownloader::IGetDataManifest() +{ + if (fMfsVec.size() > 0) + return true; + + BackgroundDownloaderLog(kMajorStatus, "Downloading new manifest from data server..." ); + + fSuccess = false; + unsigned numTries = 0; + while (!fSuccess) + { + numTries++; + fDoneWithFile = false; + NetCliFileManifestRequest(ManifestCallback, this, s_manifest); + while (!fDoneWithFile) + { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (!fSuccess) + { + fMfsVec.clear(); // clear out any bad data + if (numTries > kMaxDownloadTries) + break; // abort + } + } + + if (fSuccess) + BackgroundDownloaderLog(kStatus, "New manifest read; number of files: %d", fMfsVec.size() ); + else + BackgroundDownloaderLog(kStatus, "Failed to download manifest after trying %d times", kMaxDownloadTries); + + return fSuccess; +} + +void plBackgroundDownloader::DoneWithFile(bool success) +{ + fDoneWithFile = true; + fSuccess = success; +} + +void plBackgroundDownloader::DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount) +{ + BackgroundDownloaderLog(kStatus, "New age manifest received. Reading..."); + + if (success) + { + for (unsigned i = 0; i < entryCount; i++) + { + char* name = hsWStringToString(manifestEntires[i].clientName); + char* serverPath = hsWStringToString(manifestEntires[i].downloadName); + char* md5Str = hsWStringToString(manifestEntires[i].md5); + int size = manifestEntires[i].fileSize; + int zipsize = manifestEntires[i].zipSize; + int flags = manifestEntires[i].flags; + if (stricmp(plFileUtils::GetFileExt(name), "gz")) + flags |= plManifestFile::kFlagZipped; // add zipped flag if necessary + + plMD5Checksum sum; + sum.SetFromHexString(md5Str); + fMfsVec.push_back(TRACKED_NEW plManifestFile(name, serverPath, sum, size, zipsize, flags, false)); + + delete [] name; + delete [] serverPath; + delete [] md5Str; + } + } + + fDoneWithFile = true; + fSuccess = success; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plBackgroundDownloader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plBackgroundDownloader.h new file mode 100644 index 00000000..81559b8c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plBackgroundDownloader.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plBackgroundDownloader_h_inc +#define plBackgroundDownloader_h_inc + +#include + +#include "hsTypes.h" + +class plManifestFile; +struct NetCliFileManifestEntry; + +class plBackgroundDownloader +{ +protected: + static plBackgroundDownloader* fInstance; + static void Init(); + static void Shutdown(); + static void ThreadMain(void * param); + + plBackgroundDownloader(); + ~plBackgroundDownloader(); + + HANDLE fBGDownloaderRun; + HANDLE fBGDownloaderIsPaused; + + enum FileType {kFail, kPrp, kOther}; + + typedef std::vector MfsFileVec; + MfsFileVec fMfsVec; + +public: + + bool fDoneWithFile; + bool fSuccess; + + bool IGetDataManifest(); + FileType IGetFile(const plManifestFile* mfsFile); + UInt32 IGetDownloadSize(); + bool IDecompressSound(plManifestFile* mfsFile, bool noOverwrite = false); + +public: + static plBackgroundDownloader* GetInstance(); + static void StartThread(); + + bool Run(); + void CleanUp(); + + void Pause(); + void UnPause(); + + static bool CheckFreeSpace(UInt32 bytesNeeded); + + // called by download callbacks to tell it we are done with the current file + void DoneWithFile(bool success); + void DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount); +}; + +#endif //plBackgroundDownloader_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp new file mode 100644 index 00000000..1d41ef5d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.cpp @@ -0,0 +1,502 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plResPatcher.h" + +#include "../NucleusLib/inc/hsResMgr.h" + +#include "../plAgeDescription/plAgeManifest.h" +#include "../plResMgr/plResManager.h" +#include "../plFile/plFileUtils.h" +#include "../plFile/plEncryptedStream.h" +#include "../plCompression/plZlibStream.h" +#include "../plAudioCore/plAudioFileReader.h" +#include "../plProgressMgr/plProgressMgr.h" + +#include "../pnAsyncCore/pnAsyncCore.h" +#include "../pnNetCli/pnNetCli.h" +#include "../plNetGameLib/plNetGameLib.h" + +#include "../pnDispatch/plDispatch.h" +#include "../plStatusLog/plStatusLog.h" + +static const unsigned kMaxDownloadTries = 10; + +////////////////////////////////////////////////////////////////////////////// + +class plDownloadStream : public plZlibStream +{ +private: + plOperationProgress* fProgress; + unsigned fBytesReceived; +public: + plDownloadStream(plOperationProgress* progress) : fProgress(progress), fBytesReceived(0), plZlibStream() {} + virtual ~plDownloadStream() {} + + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + + void RewindProgress() {fProgress->Increment(-(hsScalar)fBytesReceived);} // rewind the progress bar by as far as we got +}; + +UInt32 plDownloadStream::Write(UInt32 byteCount, const void* buffer) +{ + fProgress->Increment((hsScalar)byteCount); + fBytesReceived += byteCount; + + return plZlibStream::Write(byteCount, buffer); +} + +////////////////////////////////////////////////////////////////////////////// + +static void DownloadFileCallback(ENetError result, void* param, const wchar filename[], hsStream* writer) +{ + plResPatcher* patcher = (plResPatcher*)param; + + // Retry download unless shutting down or file not found + switch (result) { + case kNetSuccess: + writer->Close(); + patcher->DoneWithFile(true); + break; + + case kNetErrFileNotFound: + case kNetErrRemoteShutdown: + writer->Close(); + patcher->DoneWithFile(false); + break; + + default: + writer->Rewind(); + NetCliFileDownloadRequest( + filename, + writer, + DownloadFileCallback, + param + ); + break; + } + +} + +static void ManifestCallback(ENetError result, void* param, const wchar group[], const NetCliFileManifestEntry manifest[], unsigned entryCount) +{ + plResPatcher* patcher = (plResPatcher*)param; + patcher->DoneWithManifest(result == kNetSuccess, manifest, entryCount); +} + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plResPatcher::plResPatcher(const char* ageToPatch, bool showAgeName) +{ + fAgeToPatch = ageToPatch; + fAlwaysShowAgeName = showAgeName; + IInit(); +} + +void plResPatcher::IInit() +{ + PatcherLog(kHeader, "--- Starting patch process for %s ---", fAgeToPatch.c_str()); +} + +plResPatcher::~plResPatcher() +{ + PatcherLog(kHeader, "--- Patch process done for %s ---", fAgeToPatch.c_str()); + + for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i) + { + plManifestFile* file = (*i); + delete file; + } + fMfsVec.clear(); +} + +UInt32 plResPatcher::IGetDownloadSize() +{ + if (!IGetAgeManifest()) + return 0; + +#ifdef PLASMA_EXTERNAL_RELEASE + bool showAgeName = fAlwaysShowAgeName; +#else + bool showAgeName = true; +#endif + + char msg[128]; + if (!fAgeToPatch.empty()) + { + if (showAgeName) + sprintf(msg, "Checking age %s...", fAgeToPatch.c_str()); + else + strcpy(msg, "Checking age..."); + } + else + sprintf(msg, "Checking..."); + + plOperationProgress* progress = plProgressMgr::GetInstance()->RegisterOperation((hsScalar)(fMfsVec.size()), msg, plProgressMgr::kNone, false, true); + + UInt32 downloadSize = 0; + UInt32 downloadFiles = 0; + for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i) + { + plManifestFile* mfsFile = (*i); + + if (!mfsFile->IsLocalUpToDate()) + { + downloadFiles++; + downloadSize += mfsFile->GetDownloadSize(); + } + + progress->Increment(1.f); + } + + delete progress; + + PatcherLog(kInfo, "Got download stats, %d files, %d bytes", downloadFiles, downloadSize); + + return downloadSize; +} + +bool plResPatcher::CheckFreeSpace(UInt32 bytesNeeded) +{ +#ifdef HS_BUILD_FOR_WIN32 + ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, neededBytes; + if (GetDiskFreeSpaceEx(NULL, &freeBytesAvailable, &totalNumberOfBytes, NULL)) + { + neededBytes.HighPart = 0; + neededBytes.LowPart = bytesNeeded; + + if (neededBytes.QuadPart > freeBytesAvailable.QuadPart) + { + PatcherLog(kInfo, "Not enough disk space (asked for %d bytes)", bytesNeeded); + return false; + } + } +#endif // HS_BUILD_FOR_WIN32 + + return true; +} + +bool plResPatcher::IDecompressSound(plManifestFile* mfsFile, bool noOverwrite) +{ + UInt32 flags = mfsFile->GetFlags(); + + if ( (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit) || hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) && stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "ogg") == 0) + { + plAudioFileReader* reader = plAudioFileReader::CreateReader(mfsFile->GetName(), plAudioCore::kAll, plAudioFileReader::kStreamNative); + if (!reader) + { + PatcherLog(kInfo, "Unable to create audio file reader for %s", mfsFile->GetName()); + return false; + } + + UInt32 size = reader->GetDataSize(); + delete reader; + + // Make sure we have enough free space + if (!CheckFreeSpace(size)) + return false; + + if (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit)) + plAudioFileReader::CacheFile(mfsFile->GetName(), true, noOverwrite); + if (hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) + plAudioFileReader::CacheFile(mfsFile->GetName(), false, noOverwrite); + } + + return true; +} + +bool plResPatcher::Update() +{ + UInt32 downloadSize = IGetDownloadSize(); + // if download size is 0, nothing to download, but we still need to tell the res manager about the files + + plFileUtils::CreateDir("dat"); + plFileUtils::CreateDir("sfx"); + + if (!CheckFreeSpace(downloadSize)) + return false; + +#ifdef PLASMA_EXTERNAL_RELEASE + bool showAgeName = fAlwaysShowAgeName; +#else + bool showAgeName = true; +#endif + + char msg[128]; + if (!fAgeToPatch.empty()) + { + if (showAgeName) + sprintf(msg, "Downloading %s data...", fAgeToPatch.c_str()); + else + strcpy(msg, "Downloading age data..."); + } + else + sprintf(msg, "Downloading..."); + + plOperationProgress* progress = plProgressMgr::GetInstance()->RegisterOverallOperation((hsScalar)downloadSize, msg, plProgressMgr::kUpdateText, true); + + bool result = true; + plResManager* resMgr = ((plResManager*)hsgResMgr::ResMgr()); + + for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i) + { + plManifestFile* mfsFile = (*i); + + if (!mfsFile->IsLocalUpToDate()) + { + FileType type = IGetFile(mfsFile, progress); + if (type == kPrp) + { + // Checks for existence before attempting to remove + resMgr->RemoveSinglePage(mfsFile->GetName()); + } + else if (type == kOther) + { + if (!IDecompressSound(mfsFile, false)) + { + char text[MAX_PATH]; + StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName()); + PatcherLog(kInfo, text ); + hsAssert(false, text); + result = false; + } + } + else + { + char text[MAX_PATH]; + StrPrintf(text, arrsize(text), "Failed downloading file: %s", mfsFile->GetName()); + PatcherLog(kInfo, text ); + hsAssert(false, text); + result = false; + } + } + else + { + if (!IDecompressSound(mfsFile, true)) + { + char text[MAX_PATH]; + StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName()); + PatcherLog(kInfo, text ); + hsAssert(false, text); + result = false; + } + } + + if (!resMgr->FindSinglePage(mfsFile->GetName()) && stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "prp") == 0) + { + resMgr->AddSinglePage(mfsFile->GetName()); + } + } + + PatcherLog(kMajorStatus, "Cleaning up patcher..." ); + delete progress; + + return result; +} + +plResPatcher::FileType plResPatcher::IGetFile(const plManifestFile* mfsFile, plOperationProgress* progressBar) +{ + PatcherLog(kInfo, " Setting up to download file %s", mfsFile->GetName()); + + bool downloadDone = false; + wchar* wServerPath = hsStringToWString(mfsFile->GetServerPath()); + int numTries = 0; + + while (!downloadDone) + { + if (numTries >= kMaxDownloadTries) + { + PatcherLog(kInfo, " Max download tries exceeded (%d). Aborting download...", kMaxDownloadTries); + return kFail; + } + + plDownloadStream downloadStream(progressBar); + if (!downloadStream.Open(mfsFile->GetName(), "wb")) + { + PatcherLog(kInfo, " Unable to create file. Aborting download..."); + return kFail; + } + + PatcherLog(kInfo, " Downloading file %s...", mfsFile->GetName()); + + fSuccess = false; + fDoneWithFile = false; + NetCliFileDownloadRequest( + wServerPath, + &downloadStream, + DownloadFileCallback, + this + ); + + while (!fDoneWithFile) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (!fSuccess) { + // remove partial file and die (server didn't have the file or server is shutting down) + downloadStream.RewindProgress(); + plFileUtils::RemoveFile(mfsFile->GetName(), true); + PatcherLog(kError, " File %s failed to download.", mfsFile->GetName()); + downloadDone = true; + } + else { + if (downloadStream.DecompressedOk()) { + PatcherLog(kInfo, " Decompress successful." ); + // download and decompress successful, do a md5 check on the resulting file + plMD5Checksum localMD5(mfsFile->GetName()); + if (localMD5 != mfsFile->GetChecksum()) { + downloadStream.RewindProgress(); + downloadStream.Close(); + plFileUtils::RemoveFile(mfsFile->GetName(), true); + PatcherLog(kError, " File %s MD5 check FAILED.", mfsFile->GetName()); + // don't set downloadDone so we attempt to re-download from the server + } + else { + downloadStream.Close(); + PatcherLog(kInfo, " MD5 check succeeded."); + downloadDone = true; + } + } + else { + downloadStream.RewindProgress(); + downloadStream.Close(); + plFileUtils::RemoveFile(mfsFile->GetName(), true); + PatcherLog(kError, " File %s failed to decompress.", mfsFile->GetName()); + // don't set downloadDone so we attempt to re-download from the server + } + } + ++numTries; + } + FREE(wServerPath); + + if (!fSuccess) + return kFail; + + if (stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "prp") == 0) + return kPrp; + + return kOther; +} + +bool plResPatcher::IGetAgeManifest() +{ + if (fMfsVec.size() > 0) + return true; + + PatcherLog(kMajorStatus, "Downloading new manifest from data server..." ); + + fSuccess = false; + wchar* group = hsStringToWString(fAgeToPatch.c_str()); + unsigned numTries = 0; + while (!fSuccess) + { + numTries++; + fDoneWithFile = false; + NetCliFileManifestRequest(ManifestCallback, this, group); + while (!fDoneWithFile) + { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (!fSuccess) + { + fMfsVec.clear(); // clear out any bad data + if (numTries > kMaxDownloadTries) + break; // abort + } + } + delete [] group; + + if (fSuccess) + PatcherLog(kStatus, "New age manifest read; number of files: %d", fMfsVec.size() ); + else + PatcherLog(kStatus, "Failed to download manifest after trying %d times", kMaxDownloadTries); + + return fSuccess; +} + +void plResPatcher::DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount) +{ + PatcherLog(kStatus, "New age manifest received. Reading..."); + + if (success) + { + for (unsigned i = 0; i < entryCount; i++) + { + char* name = hsWStringToString(manifestEntires[i].clientName); + char* serverPath = hsWStringToString(manifestEntires[i].downloadName); + char* md5Str = hsWStringToString(manifestEntires[i].md5); + int size = manifestEntires[i].fileSize; + int zipsize = manifestEntires[i].zipSize; + int flags = manifestEntires[i].flags; + if (stricmp(plFileUtils::GetFileExt(name), "gz")) + flags |= plManifestFile::kFlagZipped; // add zipped flag if necessary + + plMD5Checksum sum; + sum.SetFromHexString(md5Str); + fMfsVec.push_back(TRACKED_NEW plManifestFile(name, serverPath, sum, size, zipsize, flags)); + + delete [] name; + delete [] serverPath; + delete [] md5Str; + } + } + + fDoneWithFile = true; + fSuccess = success; +} + +void PatcherLog(PatcherLogType type, const char* format, ...) +{ + UInt32 color = 0; + switch (type) + { + case kHeader: color = plStatusLog::kWhite; break; + case kInfo: color = plStatusLog::kBlue; break; + case kMajorStatus: color = plStatusLog::kYellow; break; + case kStatus: color = plStatusLog::kGreen; break; + case kError: color = plStatusLog::kRed; break; + } + + static plStatusLog* gStatusLog = nil; + if (!gStatusLog) + { + gStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog( + 20, + "patcher.log", + plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kDeleteForMe); + } + + va_list args; + va_start(args, format); + + gStatusLog->AddLineV(color, format, args); + + va_end(args); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h new file mode 100644 index 00000000..1868b371 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAgeLoader/plResPatcher.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plResPatcher_h_inc +#define plResPatcher_h_inc + +#include "hsStlUtils.h" + +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" +#include "../plEncryption/plChecksum.h" + + +class plManifest; +class plManifestFile; +class plOperationProgress; +struct NetCliFileManifestEntry; + +class plResPatcher +{ +protected: + enum FileType {kFail, kPrp, kOther}; + std::string fAgeToPatch; + + typedef std::vector MfsFileVec; + MfsFileVec fMfsVec; + + bool fDoneWithFile; + bool fSuccess; + bool fAlwaysShowAgeName; + + void IInit(); + static void ILog(UInt32 type, const char* format, ...); + + FileType IGetFile(const plManifestFile* mfsFile, plOperationProgress* progressBar); + bool IGetAgeManifest(); + + UInt32 IGetDownloadSize(); + + bool IDecompressSound(plManifestFile* mfsFile, bool noOverwrite = false); + +public: + plResPatcher(const char* ageToPatch, bool showAgeName = false); + ~plResPatcher(); + + bool Update(); + + static bool CheckFreeSpace(UInt32 bytesNeeded); + + // called by download callbacks to tell it we are done with the current file + void DoneWithFile(bool success) {fDoneWithFile = true; fSuccess = success;} + void DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount); +}; + +enum PatcherLogType +{ + kHeader, + kInfo, + kMajorStatus, + kStatus, + kError, +}; +void PatcherLog(PatcherLogType type, const char* format, ...); + +#endif // _plResPatcher_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleCreatable.h new file mode 100644 index 00000000..4ef034e1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleCreatable.h @@ -0,0 +1,42 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAudibleCreatable_inc +#define plAudibleCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plAudibleNull.h" + +REGISTER_CREATABLE( plAudibleNull ); + +#include "plWinAudible.h" + +REGISTER_CREATABLE( plWinAudible ); +REGISTER_CREATABLE( pl2WayWinAudible ); + + +#endif // plAudibleCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleNull.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleNull.cpp new file mode 100644 index 00000000..f14dbcc6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleNull.cpp @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsGeometry3.h" +#include "plAudibleNull.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnKeyedObject/plKey.h" + +hsVector3 plAudibleNull::GetVelocity(int index) const +{ + hsVector3 ret(0,0,0); + return(ret); +} +hsPoint3 plAudibleNull::GetPosition(int index) +{ + hsPoint3 ret(0,0,0); + return(ret); +} + +void plAudibleNull::SetSceneNode(plKey newNode) +{ + plKey oldNode = GetSceneNode(); + if( oldNode == newNode ) + return; + + if( newNode ) + { + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(newNode, plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kAudible); + hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kActiveRef); + } + if( oldNode ) + { + oldNode->Release(GetKey()); + } +} + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleNull.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleNull.h new file mode 100644 index 00000000..8e205c90 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plAudibleNull.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAudibleNull_inc +#define plAudibleNull_inc + +struct hsVector3; +struct hsPoint3; + +#include "plAudible.h" + +class plAudibleNull : public plAudible +{ +public: + + plAudibleNull() : fSceneNode(nil),fSceneObj(nil) {;} + + CLASSNAME_REGISTER( plAudibleNull ); + GETINTERFACE_ANY( plAudibleNull, plAudible ); + + virtual plKey GetSceneNode() const { return fSceneNode; } + virtual void SetSceneNode(plKey newNode); + + virtual plKey GetSceneObject() const { return fSceneObj; } + virtual void SetSceneObject(plKey newNode) { } + + virtual plAudible& SetProperty(int prop, hsBool on) { return *this; } + virtual hsBool GetProperty(int prop) { return false; } + + void Play(int index = -1){;} + void SynchedPlay(int index = -1) {;} + void Stop(int index = -1){;} + void FastForwardPlay(int index = -1){;} + void FastForwardToggle(int index = -1){;} + void SetMin(const hsScalar m,int index = -1){;} // sets minimum falloff distance + void SetMax(const hsScalar m,int index = -1){;} // sets maximum falloff distance + virtual plAudible& SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index = -1){return *this;} + hsScalar GetMin(int index = -1) const{return 0;} + hsScalar GetMax(int index = -1) const{return 0;} + void SetVelocity(const hsVector3 vel,int index = -1){;} + hsVector3 GetVelocity(int index = -1) const; + hsPoint3 GetPosition(int index = -1); + void SetLooping(hsBool loop,int index = -1){;} // sets continuous loop or stops looping + hsBool IsPlaying(int index = -1){return false;} + virtual void SetTime(double t, int index = -1) {} + virtual void Activate(){} + virtual void DeActivate(){} + virtual void GetStatus(plSoundMsg* pMsg) {;} + virtual int GetNumSounds() const {return 0;} + virtual plSound* GetSound(int i) const { return nil; } + virtual int GetSoundIndex(const char *keyname) const { return -1; } + virtual void SetVolume(const float volume,int index = -1) {;} + virtual void SetFilename(int index, const char *filename, hsBool isCompressed){} + + virtual void RemoveCallbacks(plSoundMsg* pMsg) {} + virtual void AddCallbacks(plSoundMsg* pMsg) {} + + virtual void SetMuted( hsBool muted, int index = -1 ) {;} + virtual void ToggleMuted( int index = -1 ) {;} + virtual void SetTalkIcon(int index, UInt32 str){;} + virtual void ClearTalkIcon(){;} + + virtual void SetFadeIn( const int type, const float length, int index = -1 ) {} + virtual void SetFadeOut( const int type, const float length, int index = -1 ) {} + +protected: + plKey fSceneNode, fSceneObj; +}; + +#endif // plAudibleNull_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudible.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudible.cpp new file mode 100644 index 00000000..0cdb78f8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudible.cpp @@ -0,0 +1,771 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsGeometry3.h" +#include "plWinAudible.h" +#include "hsMatrix44.h" +#include "hsTimer.h" +#include "../plAudio/plSound.h" +#include "../plAudio/plWin32Sound.h" +#include "../plAudio/plVoiceChat.h" +#include "../plAudio/plAudioSystem.h" +#include "../plAudio/plWin32StreamingSound.h" +#include "../pnMessage/plSoundMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plAudioSysMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plKey.h" +#include "hsQuat.h" + +#include "plgDispatch.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../pnMessage/plCmdIfaceModMsg.h" +#include "../pnMessage/plProxyDrawMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../pnInputCore/plControlEventCodes.h" +#include "../plModifier/plSoundSDLModifier.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plStatusLog/plStatusLog.h" + +#define SND_INDEX_CHECK( index, ret ) \ + if( index >= fSoundObjs.GetCount() ) \ + { \ + hsStatusMessageF( "ERROR: Sound index out of range (index %d, count %d)\n", index, fSoundObjs.GetCount() ); \ + return ret; \ + } + +#define SND_APPLY_LOOP( index, func, ret ) \ + if( index != -1 ) \ + { \ + SND_INDEX_CHECK( index, ret ); \ + if( fSoundObjs[ index ] != nil ) \ + fSoundObjs[ index ]->func; \ + } \ + else \ + { \ + for( int i = 0; i < fSoundObjs.Count(); i++ ) \ + { \ + if( fSoundObjs[ i ] != nil ) \ + fSoundObjs[ i ]->func; \ + } \ + } + +// Visualization +#include "plWinAudibleProxy.h" + +plWinAudible::plWinAudible() +: fSceneNode(nil), fSceneObj(nil), fSDLMod(nil) +{ + fProxyGen = TRACKED_NEW plWinAudibleProxy; + fProxyGen->Init(this); + + fLocalToWorld.Reset(); +} + +plWinAudible::~plWinAudible() +{ + // delete SDL modifier + if (fSceneObj) + { + plSceneObject* so=plSceneObject::ConvertNoRef(fSceneObj->ObjectIsLoaded()); + if (so) + so->RemoveModifier(fSDLMod); + } + delete fSDLMod; + + delete fProxyGen; + for (int i = 0; i < fSoundObjs.Count(); i++) + delete(fSoundObjs[i]); + fSoundObjs.SetCountAndZero(0); + +} + +void plWinAudible::SetSceneObject(plKey obj) +{ + plKey oldKey = nil; + // remove old SDL mod + if (fSDLMod && fSceneObj && fSceneObj != obj) + { + oldKey = fSceneObj; + plSceneObject* so=plSceneObject::ConvertNoRef(fSceneObj->ObjectIsLoaded()); + if (so) + so->RemoveModifier(fSDLMod); + delete fSDLMod; + fSDLMod=nil; + } + + fSceneObj = obj; + plSceneObject* so=plSceneObject::ConvertNoRef(obj ? obj->ObjectIsLoaded() : nil); + if (so) + { + so->RemoveModifier(fSDLMod); + delete fSDLMod; + fSDLMod=TRACKED_NEW plSoundSDLModifier; + so->AddModifier(fSDLMod); + } + + for( int i = 0; i < fSoundObjs.Count(); i++ ) + { + if( fSoundObjs[ i ] != nil && fSoundObjs[ i ]->GetKey() != nil ) + { + if( obj != nil ) + { + plGenRefMsg *replaceMsg = TRACKED_NEW plGenRefMsg( fSoundObjs[ i ]->GetKey(), plRefMsg::kOnReplace, 0, plSound::kRefParentSceneObject ); + hsgResMgr::ResMgr()->AddViaNotify( obj, replaceMsg, plRefFlags::kPassiveRef ); + } + else if( oldKey != nil ) + fSoundObjs[ i ]->GetKey()->Release( oldKey ); + } + } +} + +void plWinAudible::SetSceneNode(plKey newNode) +{ + plKey oldNode = GetSceneNode(); + if( oldNode == newNode ) + return; + + if( !oldNode ) + Activate(); + + if( newNode ) + { + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(newNode, plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kAudible); + hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kPassiveRef); + + } + if( oldNode ) + { + oldNode->Release(GetKey()); + } + + if( !newNode ) + { + DeActivate(); + } + fSceneNode = newNode; +} + +void plWinAudible::GetStatus(plSoundMsg* pMsg) +{ + if (pMsg->fIndex == -1) + { + for (int i = 0; i < fSoundObjs.Count(); i++) + { + plSoundMsg* pReply = fSoundObjs[i]->GetStatus(pMsg); + pReply->fIndex = i; + plgDispatch::MsgSend(pReply); + } + } + else + if (pMsg->fIndex < fSoundObjs.Count()) + { + plSoundMsg* pReply = fSoundObjs[pMsg->fIndex]->GetStatus(pMsg); + pReply->fIndex = pMsg->fIndex; + plgDispatch::MsgSend(pReply); + } +} + +hsBool plWinAudible::AddSound( plSound *pSnd, int index, hsBool is3D ) +{ + hsAssert(pSnd->GetKey() != nil, "Adding a new sound with no key."); + if (plgAudioSys::Active()) + { + hsgResMgr::ResMgr()->AddViaNotify( pSnd->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, index, 0 ), plRefFlags::kActiveRef ); + return true; + } + + else + { + pSnd->SetProperty( plSound::kPropIs3DSound, is3D ); + hsgResMgr::ResMgr()->AddViaNotify( pSnd->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, index, 0 ), plRefFlags::kActiveRef ); + return true; + } + +} + +/* Unused +int plWinAudible::AddSoundFromResource(plSound *pSnd, void* addr, Int32 size, hsBool is3D ) +{ + //plWin32Sound* pSnd = TRACKED_NEW plWin32Sound; + //IAssignSoundKey( pSnd, GetKey() ? GetKeyName() : "", fSoundObjs.Count() - 1 ); + + if (plgAudioSys::Active()) + { + int ret = pSnd->GetSoundFromMemory(addr, size, is3D ); + if (ret) + { + fSoundObjs.Append(pSnd); + return (fSoundObjs.Count() -1 ); + } + } + + delete pSnd; + return -1; +} +*/ +plAudible& plWinAudible::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index) +{ + fLocalToWorld = l2w; + plAudible::SetTransform(l2w, w2l); + hsVector3 v = l2w.GetAxis(hsMatrix44::kUp); + v*=-1; + hsPoint3 pos = l2w.GetTranslate(); + + if (index != -1) + { + SND_INDEX_CHECK( index, (*this) ); + if( fSoundObjs[ index ] != nil ) + { + fSoundObjs[index]->SetPosition( pos ); + fSoundObjs[index]->SetConeOrientation( v.fX,v.fY,v.fZ ); + } + } + else + { + for (int i = 0; i < fSoundObjs.Count(); i++) + { + if( fSoundObjs[ i ] != nil ) + { + fSoundObjs[i]->SetConeOrientation( v.fX,v.fY,v.fZ ); + fSoundObjs[i]->SetPosition( pos ); + } + } + } + fProxyGen->SetTransform(l2w, w2l); + + return (*this); + +} + +void plWinAudible::SetTime(double t, int index) +{ + SND_APPLY_LOOP( index, SetTime( t ), ; ); +} + +void plWinAudible::SynchedPlay(int index) +{ + for(int i = 0; i < fSoundObjs.Count(); ++i) + { + if(fSoundObjs[i]->IsPlaying()) + { + fSoundObjs[index]->SynchedPlay(fSoundObjs[i]->GetByteOffset()); + return; + } + } +} + +void plWinAudible::Play(int index ) +{ + // hsStatusMessageF( "Playing sound %s, index %d, time=%f\n", GetKeyName(), index, hsTimer::GetSeconds()); + SND_APPLY_LOOP( index, Play(), ; ); +} + +void plWinAudible::FastForwardPlay(int index /* = -1 */) +{ + SND_APPLY_LOOP(index, FastForwardPlay(), ; ); +} + +void plWinAudible::FastForwardToggle(int index /* = -1 */) +{ + SND_APPLY_LOOP(index, FastForwardToggle(), ; ); +} + +void plWinAudible::SetOuterVol(const int v, int index) +{ + SND_APPLY_LOOP( index, SetOuterVolume( v ), ; ); +} + +void plWinAudible::SetConeAngles(int inner, int outer, int index) +{ + SND_APPLY_LOOP( index, SetConeAngles( inner, outer ), ; ); +} + +void plWinAudible::Stop(int index) +{ + SND_APPLY_LOOP( index, Stop(), ; ); +} + +void plWinAudible::SetMin(const hsScalar m,int index) +{ + SND_APPLY_LOOP( index, SetMin( (int)m ), ; ); +} + +void plWinAudible::SetMax(const hsScalar m,int index) +{ + SND_APPLY_LOOP( index, SetMax( (int)m ), ; ); +} + +// Takes a 0-1.f as the volume (platform independent) +void plWinAudible::SetVolume(const float volume,int index ) +{ + SND_APPLY_LOOP( index, SetVolume( volume ), ; ); +} + +void plWinAudible::SetMuted( hsBool muted, int index ) +{ + SND_APPLY_LOOP( index, SetMuted( muted ), ; ); +} + +void plWinAudible::ToggleMuted( int index ) +{ + if( index != -1 ) + { + SND_INDEX_CHECK( index, ; ); + if( fSoundObjs[ index ] != nil ) + fSoundObjs[ index ]->SetMuted( !fSoundObjs[ index ]->IsMuted() ); + } + else + { + for( int i = 0; i < fSoundObjs.Count(); i++ ) + { + if( fSoundObjs[ i ] != nil ) + fSoundObjs[ i ]->SetMuted( !fSoundObjs[ i ]->IsMuted() ); + } + } +} + +hsScalar plWinAudible::GetMin(int index) const +{ + return (hsScalar)(fSoundObjs[index]->GetMin()); +} + +hsScalar plWinAudible::GetMax(int index) const +{ + return (hsScalar)(fSoundObjs[index]->GetMax()); +} + +void plWinAudible::SetVelocity(const hsVector3 vel,int index) +{ + SND_APPLY_LOOP( index, SetVelocity( vel ), ; ); +} + +hsVector3 plWinAudible::GetVelocity(int index) const +{ + return(fSoundObjs[index]->GetVelocity()); +} + +hsPoint3 plWinAudible::GetPosition(int index) +{ + return( fSoundObjs[index]->GetPosition() ); +} + +void plWinAudible::SetLooping(hsBool loop,int index) +{ + SND_APPLY_LOOP( index, SetProperty( plSound::kPropLooping, loop ), ; ); +} + +void plWinAudible::SetFadeIn( const int type, const float length, int index ) +{ + SND_APPLY_LOOP( index, SetFadeInEffect( (plSound::plFadeParams::Type)type, length ), ; ); +} + +void plWinAudible::SetFadeOut( const int type, const float length, int index ) +{ + SND_APPLY_LOOP( index, SetFadeOutEffect( (plSound::plFadeParams::Type)type, length ), ; ); +} + +void plWinAudible::SetFilename(int index, const char *filename, hsBool isCompressed) +{ + if(index < 0 || index >= fSoundObjs.Count()) + { + hsStatusMessageF( "ERROR: Sound index out of range (index %d, count %d)\n", index, fSoundObjs.GetCount() ); + return; + } + plWin32StreamingSound *pStreamingSound = plWin32StreamingSound::ConvertNoRef(fSoundObjs[ index ]); + if(pStreamingSound) + { + pStreamingSound->SetFilename(filename, isCompressed); + } + else + { + plStatusLog::AddLineS("audio.log", "Cannot set filename of non-streaming sound. %s", fSoundObjs[ index ]->GetKeyName()); + } +} + +hsBool plWinAudible::IsPlaying(int index) +{ + int count = fSoundObjs.Count(); + + if(index < count) + { + if (index == -1) + { + for (int i = 0; i < fSoundObjs.Count(); i++) + { + if( fSoundObjs[ i ] != nil && fSoundObjs[i]->IsPlaying() ) + return true; + } + return false; + } + + if( fSoundObjs[ index ] == nil ) + return false; + + return(fSoundObjs[index]->IsPlaying()); + } else { + return false; + } +} + +void plWinAudible::Read(hsStream* s, hsResMgr* mgr) +{ + plAudible::Read(s, mgr); + int n = s->ReadSwap32(); + fSoundObjs.SetCountAndZero(n); + for(int i = 0; i < n; i++ ) + { + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, i, 0); + mgr->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef); + //plSound* pSnd = plSound::ConvertNoRef(mgr->ReadCreatable(s)); + //IAssignSoundKey( pSnd, GetKey() ? GetKeyName() : "", i ); + //if (plWin32LinkSound::ConvertNoRef(pSnd)) + // plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectBCMsg::Index(), pSnd->GetKey()); + //fSoundObjs[i] = pSnd; + } + + plKey pSceneKey = mgr->ReadKey(s); + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(pSceneKey, plRefMsg::kOnCreate, -1, plNodeRefMsg::kAudible); + mgr->AddViaNotify(GetKey(), refMsg, plRefFlags::kPassiveRef); +} + +void plWinAudible::IAssignSoundKey( plSound *sound, const char *name, UInt32 i ) +{ + char keyName[ 256 ]; + + sprintf( keyName, "%s_%d", name, i ); + hsgResMgr::ResMgr()->NewKey( keyName, sound, GetKey() ? GetKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc ); +} + +void plWinAudible::Write(hsStream* s, hsResMgr* mgr) +{ + plAudible::Write(s, mgr); + s->WriteSwap32(fSoundObjs.GetCount()); + for(int i = 0; i < fSoundObjs.GetCount(); i++ ) +// mgr->WriteCreatable( s, fSoundObjs[i] ); + mgr->WriteKey(s, fSoundObjs[i]); + + mgr->WriteKey(s, fSceneNode); +} + +void plWinAudible::Activate() +{ + for (int i = 0; i < fSoundObjs.Count(); i++) + if (fSoundObjs[i] != nil) + fSoundObjs[i]->Activate(); +} + + +void plWinAudible::DeActivate() +{ + for (int i = 0; i < fSoundObjs.Count(); i++) + { + if (fSoundObjs[i] != nil) + { + fSoundObjs[i]->DeActivate(); + } + } +} + +hsBool plWinAudible::MsgReceive(plMessage* msg) +{ + plGenRefMsg *refMsg; + if (refMsg = plGenRefMsg::ConvertNoRef(msg)) + { + plSound *snd; + if (snd = plSound::ConvertNoRef(refMsg->GetRef())) + { + int index = refMsg->fWhich; + if( refMsg->GetContext() & (plRefMsg::kOnCreate | plRefMsg::kOnRequest) ) + { + int i = fSoundObjs.Count(); + if (index >= i) + { + fSoundObjs.ExpandAndZero(index + 1); + } + fSoundObjs[index] = plSound::ConvertNoRef(refMsg->GetRef()); + if (plgAudioSys::Active()) + fSoundObjs[index]->Activate(); + } + else if( refMsg->GetContext() & (plRefMsg::kOnRemove | plRefMsg::kOnDestroy) ) + { + int i = fSoundObjs.Count(); + if (index < i) + { + fSoundObjs[index] = nil; + } + } + return true; + } + } + + plAudioSysMsg *sysMsg = plAudioSysMsg::ConvertNoRef( msg ); + if( sysMsg ) + { + if( sysMsg->GetAudFlag() == plAudioSysMsg::kChannelVolChanged ) + { + SND_APPLY_LOOP( -1, RefreshVolume(), false; ); + } + } + + // proxyDrawMsg handling--just pass it on to the proxy object + plProxyDrawMsg *pdMsg = plProxyDrawMsg::ConvertNoRef( msg ); + if( pdMsg != nil ) + { + if( fProxyGen ) + return fProxyGen->MsgReceive( pdMsg ); + + return true; + } + + return plAudible::MsgReceive(msg); +} + +void plWinAudible::RemoveCallbacks(plSoundMsg* pMsg) +{ + // sanity check + if (pMsg->fIndex >= fSoundObjs.Count()) + return; + if( pMsg->fIndex < 0 ) + { + int i; + for( i = 0; i < fSoundObjs.Count(); i++ ) + fSoundObjs[i]->RemoveCallbacks(pMsg); + } + else + { + fSoundObjs[pMsg->fIndex]->RemoveCallbacks(pMsg); + } +} + +void plWinAudible::AddCallbacks(plSoundMsg* pMsg) +{ + // sanity check + if (pMsg->fIndex >= fSoundObjs.Count()) + return; + int i; + for( i = 0; i < pMsg->GetNumCallbacks(); i++ ) + { + pMsg->GetEventCallback(i)->fIndex = pMsg->fIndex; + pMsg->GetEventCallback(i)->SetSender(GetKey()); + } + if( pMsg->fIndex < 0 ) + { + for( i = 0; i < fSoundObjs.Count(); i++ ) + fSoundObjs[i]->AddCallbacks(pMsg); + } + else + { + fSoundObjs[pMsg->fIndex]->AddCallbacks(pMsg); + } +} + +int plWinAudible::GetSoundIndex(const char *keyname) const +{ + for( int i = 0; i < fSoundObjs.Count(); i++) + { + if(!fSoundObjs[i]) continue; + if(!strcmp(fSoundObjs[i]->GetKeyName(), keyname )) + { + return i; + } + } + return -1; +} + +plSound* plWinAudible::GetSound(int i) const +{ + return fSoundObjs[i]; +} + +// Visualization +plDrawableSpans* plWinAudible::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + plDrawableSpans* myDraw = addTo; + int i; + for( i = 0; i < fSoundObjs.Count(); i++ ) + myDraw = fSoundObjs[i]->CreateProxy(fLocalToWorld, mat, idx, myDraw); + return myDraw; +} + + +// +// 2-way win audible (for local players) +// + +pl2WayWinAudible::pl2WayWinAudible() : +fVoicePlayer(nil), +fVoiceRecorder(nil), +fActive(false) +{ +} + +pl2WayWinAudible::~pl2WayWinAudible() +{ + DeActivate(); + if (fVoicePlayer) + delete fVoicePlayer; + if(fVoiceRecorder) + delete fVoiceRecorder; +} + +hsBool pl2WayWinAudible::MsgReceive(plMessage* msg) +{ + plEvalMsg* pMsg = plEvalMsg::ConvertNoRef(msg); + if (pMsg && fVoiceRecorder) + { + fVoiceRecorder->Update(pMsg->GetTimeStamp()); +// fVoxIO->Update(); + return true; + } + + plControlEventMsg* pCtrlMsg = plControlEventMsg::ConvertNoRef(msg); + if (pCtrlMsg) + { + if (pCtrlMsg->GetControlCode() == S_PUSH_TO_TALK && fVoiceRecorder) + fVoiceRecorder->SetMikeOpen(pCtrlMsg->ControlActivated()); + return true; + } + + return plWinAudible::MsgReceive(msg); +} + +void pl2WayWinAudible::Init(hsBool isLocal) +{ + if (!fVoicePlayer) + { + if(!isLocal) + fVoicePlayer = TRACKED_NEW plVoicePlayer; + } + if(!fVoiceRecorder) + { + if(isLocal) + { + fVoiceRecorder = TRACKED_NEW plVoiceRecorder; + } + } + Activate(); +} + +void pl2WayWinAudible::Activate() +{ + if(fActive) + return; + + plWinAudible::Activate(); + if(fVoicePlayer) + { + fVoicePlayer->GetSoundPtr()->Activate(); + fActive = true; + } + if (fVoiceRecorder) + { + plCmdIfaceModMsg* pModMsg = TRACKED_NEW plCmdIfaceModMsg; + pModMsg->SetBCastFlag(plMessage::kBCastByExactType); + pModMsg->SetSender(GetKey()); + pModMsg->SetCmd(plCmdIfaceModMsg::kAdd); + plgDispatch::MsgSend(pModMsg); + fActive = true; + } +} + +void pl2WayWinAudible::DeActivate() +{ + if(!fActive) + return; + + plWinAudible::DeActivate(); + if(fVoicePlayer) + { + fVoicePlayer->GetSoundPtr()->DeActivate(); + fActive = false; + } + if (fVoiceRecorder) + { + plCmdIfaceModMsg* pModMsg = TRACKED_NEW plCmdIfaceModMsg; + pModMsg->SetBCastFlag(plMessage::kBCastByExactType); + pModMsg->SetSender(GetKey()); + pModMsg->SetCmd(plCmdIfaceModMsg::kRemove); + plgDispatch::MsgSend(pModMsg); + fActive = false; + } +} + +void pl2WayWinAudible::Read(hsStream* s, hsResMgr* mgr) +{ + plWinAudible::Read(s, mgr); + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +void pl2WayWinAudible::PlayNetworkedSpeech(const char* addr, Int32 size, int numFrames, BYTE flags) +{ + if (fVoicePlayer) + { + if (!(flags & VOICE_ENCODED)) + fVoicePlayer->PlaybackUncompressedVoiceMessage((BYTE*)addr, size); + else + fVoicePlayer->PlaybackVoiceMessage((BYTE*)addr, size, numFrames); + } +} + +plAudible& pl2WayWinAudible::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index) +{ + plWinAudible::SetTransform(l2w, w2l, index); + if (fVoicePlayer) + { + hsPoint3 pt = l2w.GetTranslate(); + + // adjust to head level + pt.fZ += 6.33f; + fVoicePlayer->SetPosition( pt ); + hsPoint3 v(l2w.GetAxis(hsMatrix44::kView)); + fVoicePlayer->SetOrientation(v); + } + return (*this); +} + +void pl2WayWinAudible::SetVelocity(const hsVector3 vel,int index) +{ + plWinAudible::SetVelocity(vel, index); + if (fVoicePlayer) + fVoicePlayer->SetVelocity( vel ); +} + +void pl2WayWinAudible::SetTalkIcon(int index, UInt32 str) +{ + if (fVoicePlayer) + fVoicePlayer->SetTalkIcon(index,str); +} + +void pl2WayWinAudible::ClearTalkIcon() +{ + if (fVoicePlayer) + fVoicePlayer->ClearTalkIcon(); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudible.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudible.h new file mode 100644 index 00000000..ccc26a95 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudible.h @@ -0,0 +1,151 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plWinAudible_inc +#define plWinAudible_inc + +#include "plAudible.h" +#include "hsTemplates.h" +#include "hsMatrix44.h" + +class plSound; +class hsResMgr; +class plWinAudibleProxy; +class plDrawableSpans; +class hsGMaterial; +class plMaxNode; +class plSoundSDLModifier; +class plVoiceRecorder; +class plVoicePlayer; + +class plWinAudible : public plAudible +{ +public: + plWinAudible(); + ~plWinAudible(); + + CLASSNAME_REGISTER( plWinAudible ); + GETINTERFACE_ANY( plWinAudible, plAudible ); + + virtual plKey GetSceneNode() const { return fSceneNode; } + virtual void SetSceneNode(plKey newNode); + + virtual plKey GetSceneObject() const { return fSceneObj; } + virtual void SetSceneObject(plKey obj); + + virtual plAudible& SetProperty(int prop, hsBool on) { return *this; } + virtual hsBool GetProperty(int prop) { return false; } + + virtual plAudible& SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index = -1); + + void Play(int index = -1); + void SynchedPlay(int index = -1); + void Stop(int index = -1); + void FastForwardPlay(int index = -1); + void FastForwardToggle(int index = -1); + void SetMin(const hsScalar m,int index = -1); // sets minimum falloff distance + void SetMax(const hsScalar m,int index = -1); // sets maximum falloff distance + void SetPosition(const hsPoint3 p, int index = -1); + hsScalar GetMin(int index = -1) const; + hsScalar GetMax(int index = -1) const; + virtual void SetVelocity(const hsVector3 vel,int index = -1); + hsVector3 GetVelocity(int index = -1) const; + hsPoint3 GetPosition(int index = -1); + void SetLooping(hsBool loop,int index = -1); // sets continuous loop or stops looping + hsBool IsPlaying(int index = -1); + void SetTime(double t, int index = -1); + void SetOuterVol(const int v,int index = -1); // volume for the outer cone (if applicable) + void SetConeAngles(int inner, int outer,int index = -1); + void RemoveCallbacks(plSoundMsg* pMsg); + void AddCallbacks(plSoundMsg* pMsg); + hsBool AddSound(plSound *pSnd, int index,hsBool is3D); + int AddSoundFromResource(plSound *pSnd, void* addr, Int32 size, hsBool is3D); + virtual void GetStatus(plSoundMsg* pMsg); + virtual int GetNumSounds() const {return fSoundObjs.Count();} + virtual plSound* GetSound(int i) const; + virtual int GetSoundIndex(const char *keyname) const; + virtual void SetVolume(const float volume,int index = -1); + virtual void SetMuted( hsBool muted, int index = -1 ); + virtual void ToggleMuted( int index = -1 ); + virtual void SetTalkIcon(int index, UInt32 str){;} + virtual void ClearTalkIcon(){;} + void SetFilename(int index, const char *filename, hsBool isCompressed); + + virtual void SetFadeIn( const int type, const float length, int index = -1 ); + virtual void SetFadeOut( const int type, const float length, int index = -1 ); + + virtual hsBool MsgReceive(plMessage* pMsg); + + virtual void Activate(); + virtual void DeActivate(); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + // Visualization + virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); + +private: + hsTArray fSoundObjs; + plKey fSceneNode; + plWinAudibleProxy* fProxyGen; + hsMatrix44 fLocalToWorld; + plSoundSDLModifier* fSDLMod; + plKey fSceneObj; + void IAssignSoundKey( plSound *sound, const char *name, UInt32 i ); +}; + + +class pl2WayWinAudible : public plWinAudible +{ +public: + + pl2WayWinAudible(); + ~pl2WayWinAudible(); + + CLASSNAME_REGISTER( pl2WayWinAudible ); + GETINTERFACE_ANY( pl2WayWinAudible, plWinAudible ); + + virtual hsBool MsgReceive(plMessage* pMsg); + virtual void Init(hsBool isLocal); + virtual void Activate(); + virtual void DeActivate(); + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void PlayNetworkedSpeech(const char* addr, Int32 size, int numFrames, unsigned char flags); + + virtual plAudible& SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index = -1); + virtual void SetVelocity(const hsVector3 vel,int index = -1); + virtual void SetTalkIcon(int index, UInt32 str); + virtual void ClearTalkIcon(); + +protected: + plVoicePlayer* fVoicePlayer; + plVoiceRecorder *fVoiceRecorder; + bool fActive; + +}; + +#endif // plWinAudible_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudibleProxy.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudibleProxy.cpp new file mode 100644 index 00000000..33bab6b6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudibleProxy.cpp @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plWinAudibleProxy.h" +#include "plWinAudible.h" + +#include "../plDrawable/plDrawableGenerator.h" +#include "../pnMessage/plProxyDrawMsg.h" + +plWinAudibleProxy::plWinAudibleProxy() +: plProxyGen(hsColorRGBA().Set(0.2f,0.2f,0.8f,1.f), hsColorRGBA().Set(1.f,0.5f,0.5f,1.f), 0.2f), + fOwner(nil) +{ +} + +plWinAudibleProxy::~plWinAudibleProxy() +{ +} + +hsBool plWinAudibleProxy::Init(plWinAudible* aud) +{ + plProxyGen::Init(aud); + + fOwner = aud; + fProxyMsgType = plProxyDrawMsg::kAudible; + + return fOwner != nil; +} + +plKey plWinAudibleProxy::IGetNode() const +{ + return fOwner ? fOwner->GetSceneNode() : nil; +} + +plDrawableSpans* plWinAudibleProxy::ICreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + if( fOwner ) + { + return fOwner->CreateProxy(mat, idx, addTo); + } + return nil; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudibleProxy.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudibleProxy.h new file mode 100644 index 00000000..9954b66d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudible/plWinAudibleProxy.h @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plWinAudibleProxy_inc +#define plWinAudibleProxy_inc + +#include "../plDrawable/plProxyGen.h" + +class plWinAudible; + +class plWinAudibleProxy : public plProxyGen +{ +public: + plWinAudibleProxy(); + virtual ~plWinAudibleProxy(); + + hsBool Init(plWinAudible* aud); + +protected: + plWinAudible* fOwner; + virtual plDrawableSpans* ICreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo=nil); + virtual plKey IGetNode() const; +}; + +#endif // plWinAudibleProxy_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.cpp new file mode 100644 index 00000000..08a152e6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.cpp @@ -0,0 +1,220 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plAudioCaps - Utility class to query and detect available audio options.// +// // +////////////////////////////////////////////////////////////////////////////// + +#include "HeadSpin.h" +#include "al.h" +#include "alc.h" +#include "plEAXEffects.h" + +#include "plAudioCaps.h" +#include +#include +#include + +#include "../plStatusLog/plStatusLog.h" + +#define MAX_NUM_SOURCES 128 +#define kLogMe if( fLog != nil ) fLog->AddLineF( +#define MAX_AUDIOCARD_NAME 256 + +////////////////////////////////////////////////////////////////////////////// +//// Detector Functions ////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +plAudioCaps plAudioCapsDetector::fCaps; +hsBool plAudioCapsDetector::fGotCaps = false; + +plStatusLog *plAudioCapsDetector::fLog = nil; + +plAudioCapsDetector::plAudioCapsDetector() +{ +} + +plAudioCapsDetector::~plAudioCapsDetector() +{ +} + +//// Detect ////////////////////////////////////////////////////////////////// +// Our big function that does all of our work + +plAudioCaps &plAudioCapsDetector::Detect( hsBool logIt, hsBool init ) +{ + // If we already have the device capabilities, just return them + if(fGotCaps) return fCaps; + fCaps.fIsAvailable = true; + + ALCdevice * device; + ALCcontext * context; + + if(init) + { + device = alcOpenDevice(0); + if(!device) + { + fCaps.fIsAvailable = false; + } + + context = alcCreateContext(device, 0); + if(alGetError() != AL_NO_ERROR) + { + fCaps.fIsAvailable = false; + } + alcMakeContextCurrent(context); + if(alGetError() != AL_NO_ERROR) + { + fCaps.fIsAvailable = false; + } + } + + EnumerateAudioDevices(); + + if( logIt ) + fLog = plStatusLogMgr::GetInstance().CreateStatusLog( 30, "audioCaps.log" ); + else + fLog = nil; + + kLogMe 0xff00ff00, "Starting audio caps detection..." ); + + // find the max number of sources + ALuint sources[MAX_NUM_SOURCES]; + ALuint i = 0; + for(; i < MAX_NUM_SOURCES; i++) + { + alGenSources(1, &sources[i]); + if(alGetError() != AL_NO_ERROR) + break; + fCaps.fMaxNumSources++; + } + alDeleteSources(i, sources); + kLogMe 0xffffffff, "Max Number of sources: %d", i); + plStatusLog::AddLineS("audio.log", "Max Number of sources: %d", i); + + // Detect EAX support + kLogMe 0xff00ff00, "Attempting to detect EAX support..." ); + fCaps.fEAXAvailable = IDetectEAX( ); + + kLogMe 0xff00ff00, "Audio caps detection COMPLETE." ); + delete fLog; + + fGotCaps = true; // We've got the device capabilities + + if(init) + { + alcMakeContextCurrent(nil); + alcDestroyContext(context); + alcCloseDevice(device); + } + return fCaps; +} + +void plAudioCapsDetector::EnumerateAudioDevices() +{ + ICreateDevEnum *pDevEnum; + IEnumMoniker *pEnumMon; + IMoniker *pMoniker; + ULONG cFetched; + HRESULT hr; + char audioCardName[MAX_AUDIOCARD_NAME]; + short *pShort; + + // Enumerate audio devices + hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pDevEnum); + if(SUCCEEDED(hr)) + { + hr = pDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, &pEnumMon, 0); + if(SUCCEEDED(hr)) + { + while(pEnumMon->Next(1, &pMoniker, &cFetched) == S_OK) + { + if(pMoniker) + { + IPropertyBag *pPropBag; + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag); + if(SUCCEEDED(hr)) + { + VARIANT varName; + VariantInit(&varName); + hr = pPropBag->Read(L"FriendlyName", &varName, 0); + memset(audioCardName, 0, MAX_AUDIOCARD_NAME); + pShort = varName.piVal; + + // copy from wide character array to char array + for(int i = 0; *pShort != 0 && i < MAX_AUDIOCARD_NAME; pShort++, i++) + { + audioCardName[i] = (char)*pShort; + } + + if(SUCCEEDED(hr)) + { + plStatusLog::AddLineS("audiocaps.log", audioCardName ); + } + VariantClear(&varName); + pPropBag->Release(); + } + pMoniker->Release(); + } + } + pEnumMon->Release(); + } + pDevEnum->Release(); + } +} + +//// IDetectEAX ////////////////////////////////////////////////////////////// +// Attempt to actually init the EAX listener.Note that we can potentially do +// this even if we didn't load the EAX Unified driver (we could just be +// running EAX 3.0 native), so you can really just think of the EAX Unified +// init code above as a way of trying to make sure this line here will +// succeed as often as possible. + +hsBool plAudioCapsDetector::IDetectEAX( ) +{ + hsBool gotSupport = true; + + if(!alIsExtensionPresent((ALchar *)"EAX4.0")) // is eax 4 supported + { + if(!alIsExtensionPresent((ALchar *) "EAX4.0Emulated")) // is an earlier version of eax supported + { + kLogMe 0xff00ff00, "EAX not supported"); + gotSupport = false; + } + else + { + fCaps.fEAXUnified = true; + kLogMe 0xff00ff00, "EAX 4 Emulated supported"); + } + } + else + { + kLogMe 0xff00ff00, "EAX 4 available"); + } + return gotSupport; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.h new file mode 100644 index 00000000..3bff4594 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plAudioCaps - Utility class to query and detect available audio options.// +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plAudioCaps_h +#define _plAudioCaps_h + +#include "hsTypes.h" +#include "hsTemplates.h" + +class plAudioCapsDetector; +class plStatusLog; + +class plAudioCaps +{ +public: + + plAudioCaps() { Clear(); } + + void Clear( void ) + { + fIsAvailable = false; + fEAXAvailable = false; + fEAXUnified = false; + fMaxNumSources = 0; + } + + hsBool IsAvailable( void ) const { return fIsAvailable; } + hsBool IsEAXAvailable( void ) const { return fEAXAvailable; } + hsBool UsingEAXUnified( void ) const { return fEAXUnified; } + unsigned GetMaxNumVoices() { return fMaxNumSources; } + +protected: + friend class plAudioCapsDetector; + + hsBool fIsAvailable, fEAXAvailable, fEAXUnified; + unsigned fMaxNumSources; +}; + +class plAudioCapsDetector +{ +public: + plAudioCapsDetector(); + virtual ~plAudioCapsDetector(); + + static plAudioCaps &Detect( hsBool log = false, hsBool init = false ); + +protected: + static plStatusLog *fLog; + static plAudioCaps fCaps; + static hsBool fGotCaps; + + static hsBool IDetectEAX( ); + static void EnumerateAudioDevices(); +}; + +#endif //_plAudioCaps_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCreatable.h new file mode 100644 index 00000000..55e24882 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioCreatable.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAudioCreatable_inc +#define plAudioCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plAudioSystem.h" + +REGISTER_CREATABLE( plAudioSystem ); + +#include "plSound.h" + +REGISTER_NONCREATABLE( plSound ); +REGISTER_CREATABLE( plSoundVolumeApplicator ); + +#include "plWin32Sound.h" +#include "plWin32StaticSound.h" +#include "plWin32StreamingSound.h" +#include "plWin32GroupedSound.h" + +REGISTER_NONCREATABLE( plWin32Sound ); +REGISTER_CREATABLE( plWin32StaticSound ); +REGISTER_CREATABLE( plWin32LinkSound ); +REGISTER_CREATABLE( plWin32StreamingSound ); +REGISTER_CREATABLE( plWin32GroupedSound ); + +#include "plEAXListenerMod.h" +REGISTER_CREATABLE( plEAXListenerMod ); + +#include "plAudioReaderCreatable.h" + +#endif // plAudioCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioReaderCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioReaderCreatable.h new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioReaderCreatable.h @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp new file mode 100644 index 00000000..93884b3f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp @@ -0,0 +1,1279 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "al.h" +#include "alc.h" +#include "efx.h" +#include +#include + +#include "hsTimer.h" +#include "hsGeometry3.h" +#include "plgDispatch.h" +#include "plProfile.h" +#include "../plStatusLog/plStatusLog.h" + +#include "plSound.h" +#include "plAudioCaps.h" +#include "plAudioSystem.h" +#include "plDSoundBuffer.h" +#include "plEAXEffects.h" +#include "plEAXListenerMod.h" +#include "plVoiceChat.h" + +#include "../pnMessage/plAudioSysMsg.h" +#include "../plMessage/plRenderMsg.h" +#include "../pnMessage/plRefMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../pnMessage/plTimeMsg.h" + +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnKeyedObject/plKey.h" + + +#define SAFE_RELEASE(p) if(p){ p->Release(); p = nil; } +#define FADE_TIME 3 +#define MAX_NUM_SOURCES 128 +#define UPDATE_TIME_MS 100 + +plProfile_CreateTimer("EAX Update", "Sound", SoundEAXUpdate); +plProfile_CreateTimer("Soft Update", "Sound", SoundSoftUpdate); +plProfile_CreateCounter("Max Sounds", "Sound", SoundMaxNum); +plProfile_CreateTimer("AudioUpdate", "RenderSetup", AudioUpdate); + +typedef std::vector::iterator DeviceIter; + +//// Internal plSoftSoundNode Class Definition /////////////////////////////// +class plSoftSoundNode +{ + public: + const plKey fSoundKey; + hsScalar fRank; + + plSoftSoundNode *fNext; + plSoftSoundNode **fPrev; + + plSoftSoundNode *fSortNext; + plSoftSoundNode **fSortPrev; + + plSoftSoundNode( const plKey s ) : fSoundKey( s ) { fNext = nil; fPrev = nil; } + ~plSoftSoundNode() { Unlink(); } + + void Link( plSoftSoundNode **prev ) + { + fNext = *prev; + fPrev = prev; + if( fNext != nil ) + fNext->fPrev = &fNext; + *prev = this; + } + + void Unlink( void ) + { + if( fPrev != nil ) + { + *fPrev = fNext; + if( fNext ) + fNext->fPrev = fPrev; + fPrev = nil; + fNext = nil; + } + } + + void SortedLink( plSoftSoundNode **prev, hsScalar rank ) + { + fRank = rank; + + fSortNext = *prev; + fSortPrev = prev; + if( fSortNext != nil ) + fSortNext->fSortPrev = &fSortNext; + *prev = this; + } + + // Larger values are first in the list + void AddToSortedLink( plSoftSoundNode *toAdd, hsScalar rank ) + { + if( fRank > rank ) + { + if( fSortNext != nil ) + fSortNext->AddToSortedLink( toAdd, rank ); + else + { + toAdd->SortedLink( &fSortNext, rank ); + } + } + else + { + // Cute trick here... + toAdd->SortedLink( fSortPrev, rank ); + } + } + + void BootSourceOff( void ) + { + plSound *sound = plSound::ConvertNoRef( fSoundKey->ObjectIsLoaded() ); + if( sound != nil ) + { + sound->ForceUnregisterFromAudioSys(); + } + } +}; + +// plAudioSystem ////////////////////////////////////////////////////////////////////////// + +Int32 plAudioSystem::fMaxNumSounds = 16; +Int32 plAudioSystem::fNumSoundsSlop = 8; + +plAudioSystem::plAudioSystem() : +fStartTime(0), +fListenerInit(false), +fSoftRegionSounds(nil), +fActiveSofts(nil), +fCurrDebugSound(nil), +fDebugActiveSoundDisplay(nil), +fUsingEAX(false), +fRestartOnDestruct(false), +fWaitingForShutdown(false), +fActive(false), +fDisplayNumBuffers(false), +fStartFade(0), +fFadeLength(FADE_TIME), +fCaptureDevice(nil), +fLastUpdateTimeMs(0) +{ + fCurrListenerPos.Set( -1.e30, -1.e30, -1.e30 ); + //fCommittedListenerPos.Set( -1.e30, -1.e30, -1.e30 ); + fLastPos.Set(100, 100, 100); +} + +plAudioSystem::~plAudioSystem() +{ +} + +void plAudioSystem::IEnumerateDevices() +{ + fDeviceList.clear(); + plStatusLog::AddLineS("audio.log", "--Audio Devices --" ); + char *devices = (char *)alcGetString(nil, ALC_DEVICE_SPECIFIER); + int major, minor; + + while(*devices != nil) + { + ALCdevice *device = alcOpenDevice(devices); + if (device) + { + ALCcontext *context = alcCreateContext(device, NULL); + if (context) + { + alcMakeContextCurrent(context); + // if new actual device name isn't already in the list, then add it... + bool bNewName = true; + for (DeviceIter i = fDeviceList.begin(); i != fDeviceList.end(); i++) + { + if (strcmp((*i).GetDeviceName(), devices) == 0) + { + bNewName = false; + } + } + if ((bNewName)) + { + alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(int), &major); + alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(int), &minor); + plStatusLog::AddLineS("audio.log", "%s OpenAL ver: %d.%d", devices, major, minor ); + + // filter out any devices that aren't openal 1.1 compliant + if(major > 1 || (major == 1 && minor >= 1)) + { + hsBool supportsEAX = false; + if(alIsExtensionPresent((ALchar *)"EAX4.0") || alIsExtensionPresent((ALchar *) "EAX4.0Emulated")) + { + supportsEAX = true; + } + DeviceDescriptor desc(devices, supportsEAX); + fDeviceList.push_back(desc); + } + } + alcMakeContextCurrent(nil); + alcDestroyContext(context); + } + alcCloseDevice(device); + } + devices += strlen(devices) + 1; + } + + DeviceDescriptor temp("", 0); + // attempt to order devices + for(unsigned i = 0; i < fDeviceList.size(); ++i) + { + if(strstr(fDeviceList[i].GetDeviceName(), "Software")) + { + temp = fDeviceList[i]; + fDeviceList[i] = fDeviceList[0]; + fDeviceList[0] = temp; + } + if(strstr(fDeviceList[i].GetDeviceName(), "Hardware")) + { + temp = fDeviceList[i]; + fDeviceList[i] = fDeviceList[1]; + fDeviceList[1] = temp; + } + } +} + +//// Init //////////////////////////////////////////////////////////////////// +hsBool plAudioSystem::Init( hsWindowHndl hWnd ) +{ + plgAudioSys::fRestarting = false; + static hsBool firstTimeInit = true; + plStatusLog::AddLineS( "audio.log", plStatusLog::kBlue, "ASYS: -- Init --" ); + + fMaxNumSources = 0; + plStatusLog::AddLineS( "audio.log", plStatusLog::kGreen, "ASYS: Detecting caps..." ); + plSoundBuffer::Init(); + + // Set the maximum number of sounds based on priority cutoff slider + SetMaxNumberOfActiveSounds(); + const char *deviceName = plgAudioSys::fDeviceName.c_str(); + hsBool useDefaultDevice = true; + + if(firstTimeInit) + { + IEnumerateDevices(); + firstTimeInit = false; + } + + if(!fDeviceList.size()) + { + plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: ERROR Unable to query any devices, is openal installed?" ); + return false; + } + + // shouldn't ever happen, but just in case + if(!deviceName) + plgAudioSys::SetDeviceName(DEFAULT_AUDIO_DEVICE_NAME); + + for(DeviceIter i = fDeviceList.begin(); i != fDeviceList.end(); i++) + { + if(!strcmp((*i).GetDeviceName(), deviceName)) + { + useDefaultDevice = false; + break; + } + } + + if(useDefaultDevice) + { + // if no device has been specified we will use the "Generic Software" device by default. If "Generic Software" is unavailable(which can happen) we select the first available device + // We want to use software by default since some audio cards have major problems with hardware + eax. + const char *defaultDev = fDeviceList.front().GetDeviceName(); + for(DeviceIter i = fDeviceList.begin(); i != fDeviceList.end(); i++) + { + if(!strcmp(DEFAULT_AUDIO_DEVICE_NAME, (*i).GetDeviceName())) + { + defaultDev = DEFAULT_AUDIO_DEVICE_NAME; + break; + } + } + + plgAudioSys::SetDeviceName(defaultDev, false); + fDevice = alcOpenDevice(defaultDev); + plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: %s device selected", defaultDev ); + deviceName = defaultDev; + } + else + { + plgAudioSys::SetDeviceName(deviceName, false); + fDevice = alcOpenDevice(deviceName); + plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: %s device selected", deviceName ); + } + if(!fDevice) + { + plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: ERROR initializing OpenAL" ); + return false; + } + + fContext = alcCreateContext(fDevice, 0); + alcMakeContextCurrent(fContext); + + ALenum error; + if(alGetError() != AL_NO_ERROR) + { + plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: ERROR alcMakeContextCurrent failed" ); + return false; + } + + plStatusLog::AddLineS("audio.log", "OpenAL vendor: %s", alGetString(AL_VENDOR)); + plStatusLog::AddLineS("audio.log", "OpenAL version: %s", alGetString(AL_VERSION)); + plStatusLog::AddLineS("audio.log", "OpenAL renderer: %s", alGetString(AL_RENDERER)); + plStatusLog::AddLineS("audio.log", "OpenAL extensions: %s", alGetString(AL_EXTENSIONS)); + plAudioCaps caps = plAudioCapsDetector::Detect(); + + if(strcmp(deviceName, DEFAULT_AUDIO_DEVICE_NAME)) + { + // we are using a hardware device, set priority based on number of hardware voices + unsigned int numVoices = caps.GetMaxNumVoices(); + + if(numVoices < 16) + plgAudioSys::SetPriorityCutoff(3); + + SetMaxNumberOfActiveSounds(); + } + + // setup capture device + ALCsizei bufferSize = FREQUENCY * 2 * BUFFER_LEN_SECONDS; // times 2 for 16-bit format + //const char *dev = alcGetString(fDevice, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); + fCaptureDevice = alcCaptureOpenDevice(nil, FREQUENCY, AL_FORMAT_MONO16, bufferSize); + + fMaxNumSources = caps.GetMaxNumVoices(); + + // attempt to init the EAX listener. + if( plgAudioSys::fEnableEAX ) + { + fUsingEAX = plEAXListener::GetInstance().Init(); + if( fUsingEAX ) + { + plStatusLog::AddLineS( "audio.log", plStatusLog::kGreen, "ASYS: EAX support detected and enabled." ); + } + else + { + plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, "ASYS: EAX support NOT detected. EAX effects disabled." ); + } + } + else + fUsingEAX = false; + + plProfile_Set(SoundMaxNum, fMaxNumSounds); + + alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); + + error = alGetError(); + + fWaitingForShutdown = false; + plgDispatch::Dispatch()->RegisterForExactType( plAgeLoadedMsg::Index(), GetKey() ); + return true; +} + +//// Shutdown //////////////////////////////////////////////////////////////// + +void plAudioSystem::Shutdown() +{ + plStatusLog::AddLineS( "audio.log", plStatusLog::kBlue, "ASYS: -- Shutdown --" ); + + plSoundBuffer::Shutdown(); + + // Delete our active sounds list + delete fDebugActiveSoundDisplay; + fDebugActiveSoundDisplay = nil; + + // Unregister soft sounds + while( fSoftRegionSounds != nil ) + { + fSoftRegionSounds->BootSourceOff(); + delete fSoftRegionSounds; + } + while( fActiveSofts != nil ) + { + fActiveSofts->BootSourceOff(); + delete fActiveSofts; + } + + while( fEAXRegions.GetCount() > 0 ) + { + if( fEAXRegions[ 0 ] != nil && fEAXRegions[ 0 ]->GetKey() != nil ) + { + GetKey()->Release( fEAXRegions[ 0 ]->GetKey() ); + } + fEAXRegions.Remove( 0 ); + } + plEAXListener::GetInstance().ClearProcessCache(); + + plSound::SetCurrDebugPlate( nil ); + fCurrDebugSound = nil; + + // Reset this, just in case + fPendingRegisters.Reset(); + + //fListenerInit = false; + + if( fUsingEAX ) + { + plEAXListener::GetInstance().Shutdown(); + } + + alcCaptureStop(fCaptureDevice); + alcCaptureCloseDevice(fCaptureDevice); + fCaptureDevice = nil; + + alcMakeContextCurrent(nil); + alcDestroyContext(fContext); + alcCloseDevice(fDevice); + fContext = nil; + fDevice = nil; + + fStartTime = 0; + fUsingEAX = false; + fCurrListenerPos.Set( -1.e30, -1.e30, -1.e30 ); + //fCommittedListenerPos.Set( -1.e30, -1.e30, -1.e30 ); + + if( fRestartOnDestruct ) + { + fRestartOnDestruct = false; + plgAudioSys::Activate( true ); + } + + plgDispatch::Dispatch()->UnRegisterForExactType(plAgeLoadedMsg::Index(), GetKey() ); + fWaitingForShutdown = false; +} + +int plAudioSystem::GetNumAudioDevices() +{ + return fDeviceList.size(); +} + +const char *plAudioSystem::GetAudioDeviceName(int index) +{ + if(index < 0 || index >= fDeviceList.size()) + { + hsAssert(false, "Invalid index passed to GetAudioDeviceName"); + return nil; + } + return fDeviceList[index].GetDeviceName(); +} + +hsBool plAudioSystem::SupportsEAX(const char *deviceName) +{ + for(DeviceIter i = fDeviceList.begin(); i != fDeviceList.end(); i++) + { + if(!strcmp((*i).GetDeviceName(), deviceName)) + { + return (*i).SupportsEAX(); + } + } + return false; +} + +void plAudioSystem::SetDistanceModel(int i) +{ + switch(i) + { + case 0: alDistanceModel(AL_NONE); break; + case 1: alDistanceModel(AL_INVERSE_DISTANCE ); break; + case 2: alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); break; + case 3: alDistanceModel(AL_LINEAR_DISTANCE ); break; + case 4: alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED ); break; + case 5: alDistanceModel(AL_EXPONENT_DISTANCE ); break; + case 6: alDistanceModel(AL_EXPONENT_DISTANCE_CLAMPED); break; + } +} + +// Set the number of active sounds the audio system is allowed to play, based on the priority cutoff +void plAudioSystem::SetMaxNumberOfActiveSounds() +{ + UInt16 priorityCutoff = plgAudioSys::GetPriorityCutoff(); + int maxNumSounds = 24; + + // Keep this to a reasonable amount based on the users hardware, since we want the sounds to be played in hardware + if(maxNumSounds > fMaxNumSources && fMaxNumSources != 0 ) + maxNumSounds = fMaxNumSources / 2; + + // Allow a certain number of sounds based on a user specified setting. + // Above that limit, we want 1/2 more sounds (8 for the max of 16) for a slop buffer, + // if they fit, so that sounds that shouldn't be playing can still play in case they suddenly pop + // back into priority range (so we don't incur performance hits when sounds hover on + // the edge of being high enough priority to play) + fMaxNumSounds = maxNumSounds; + fNumSoundsSlop = fMaxNumSounds / 2; + + plStatusLog::AddLineS( "audio.log", "Max Number of Sounds Set to: %d", fMaxNumSounds); +} + +void plAudioSystem::SetListenerPos(const hsPoint3 pos) +{ + fCurrListenerPos = pos; + alListener3f(AL_POSITION, pos.fX, pos.fZ, -pos.fY); // negate z coord, since openal uses opposite handedness +} + +void plAudioSystem::SetListenerVelocity(const hsVector3 vel) +{ + alListener3f(AL_VELOCITY, 0, 0, 0); // no doppler shift +} + +void plAudioSystem::SetListenerOrientation(const hsVector3 view, const hsVector3 up) +{ + ALfloat orientation[] = { view.fX, view.fZ, -view.fY, up.fX, up.fZ, -up.fY }; + alListenerfv(AL_ORIENTATION, orientation); +} + +void plAudioSystem::SetActive( hsBool b ) +{ + fActive = b; + if( fActive ) + { + // Clear to send activate message (if listener not inited yet, delay until then) + plgDispatch::MsgSend( TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kActivate ) ); + } +} + +//// IRegisterSoftSound ////////////////////////////////////////////////////// +// Note: each sound might kick another off the top-n-ranked-sounds list, so +// we call IUpdateSoftSounds() each time one is added. Ugly as sin, but at +// least all the calc code is in one place. Possible optimization in the +// future: when calling IUpdate(), any sounds that are already active don't +// need to be recalced, just resorted. +void plAudioSystem::RegisterSoftSound( const plKey soundKey ) +{ + plSoftSoundNode *node = TRACKED_NEW plSoftSoundNode( soundKey ); + node->Link( &fSoftRegionSounds ); + + fCurrDebugSound = nil; + plSound::SetCurrDebugPlate( nil ); +} + +//// IUnregisterSoftSound //////////////////////////////////////////////////// + +void plAudioSystem::UnregisterSoftSound( const plKey soundKey ) +{ + plSoftSoundNode *node; + for( node = fActiveSofts; node != nil; node = node->fNext ) + { + if( node->fSoundKey == soundKey ) + { + delete node; + return; + } + } + + for( node = fSoftRegionSounds; node != nil; node = node->fNext ) + { + if( node->fSoundKey == soundKey ) + { + delete node; + return; + } + } + + // We might have unregistered it ourselves on destruction, so don't bother + + fCurrDebugSound = nil; + plSound::SetCurrDebugPlate( nil ); +} + +//// IUpdateSoftSounds /////////////////////////////////////////////////////// +// OK, so the listener moved. Since our sound soft volumes are all based on +// the listener's position, we have to update all sounds with soft volumes +// now. This involves some steps: +// - Determining what volumes the listener has moved out of +// - Determining what volumes the listener has changed position in +// - Determining what volumes the listener has entered +// - Updating the levels of all sounds associated with all of the above +// volumes +// The first two tests are easy, since we'll have kept track of what volumes +// the listener was in last time. The last part is the tricky one and the +// one that will kill us in performance if we're not careful. However, we +// can first check the bounding box of the sounds in question, since outside +// of them the soft volume won't have any effect. The last part is simply a +// matter of querying the sound volumes based on the listener's position and +// setting the soft volume attenuation on the sound to that level. +// +// Note: for each sound that is still in range, we call CalcSoftVolume() and +// use the resulting value to rank each sound. Then, we take the top n sounds +// and disable the rest. We *could* rank by distance to listener, which would +// be far faster; however, we could have a sound (or background music) that +// is technically closest to the listener but completely faded by a soft volume, +// thus needlessly cutting off another sound that might be more important. +// This way is slower, but better quality. +// Also note: to differentiate between two sounds in the same soft volume at +// full strength, we divide the soft volume ranks by the distSquared, thus +// giving us a reasonable approximation at a good rank... + +void plAudioSystem::IUpdateSoftSounds( const hsPoint3 &newPosition ) +{ + plSoftSoundNode *node, *myNode; + hsScalar distSquared, rank; + plSoftSoundNode *sortedList = nil; + Int32 i; + + plProfile_BeginTiming(SoundSoftUpdate); + + // Check the sounds the listener is already inside of. If we moved out, stop sound, else + // just change attenuation + for( node = fActiveSofts; node != nil; ) + { + plSound *sound = plSound::ConvertNoRef( node->fSoundKey->ObjectIsLoaded() ); + + bool notActive = false; + if(sound) + sound->Update(); + + // Quick checks for not-active + if( sound == nil ) + notActive = true; + else if( !sound->IsWithinRange( newPosition, &distSquared ) ) + notActive = true; + else if(sound->GetPriority() > plgAudioSys::GetPriorityCutoff()) + notActive = true; + + if(plgAudioSys::fMutedStateChange) + { + sound->SetMuted(plgAudioSys::fMuted); + } + + if( !notActive ) + { + /// Our initial guess is that it's enabled... + sound->CalcSoftVolume( true, distSquared ); + rank = sound->GetVolumeRank(); + if( rank <= 0.f ) + notActive = true; + else + { + /// Queue up in our sorted list... + if( sortedList == nil ) + node->SortedLink( &sortedList, (10.0f - sound->GetPriority()) * rank ); + else + sortedList->AddToSortedLink( node, (10.0f - sound->GetPriority()) * rank ); + /// Still in radius, so consider it still "active". + node = node->fNext; + } + } + + if( notActive ) + { + /// Moved out of range of the sound--stop the sound entirely and move it to our + /// yeah-they're-registered-but-not-active list + myNode = node; + node = node->fNext; + myNode->Unlink(); + myNode->Link( &fSoftRegionSounds ); + + /// We know this sound won't be enabled, so skip the Calc() call + if( sound != nil ) + sound->UpdateSoftVolume( false ); + } + } + + // Now check remaining sounds to see if the listener moved into them + for( node = fSoftRegionSounds; node != nil; ) + { + if( !fListenerInit ) + { + node = node->fNext; + continue; + } + + plSound *sound = plSound::ConvertNoRef( node->fSoundKey->ObjectIsLoaded() ); + if( !sound || sound->GetPriority() > plgAudioSys::GetPriorityCutoff() ) + { + node = node->fNext; + continue; + } + + sound->Update(); + if(plgAudioSys::fMutedStateChange) + { + sound->SetMuted(plgAudioSys::fMuted); + } + + if( sound->IsWithinRange( newPosition, &distSquared ) ) + { + /// Our initial guess is that it's enabled... + sound->CalcSoftVolume( true, distSquared ); + rank = sound->GetVolumeRank(); + + if( rank > 0.f ) + { + /// We just moved into its range, so move it to our active list and start the sucker + myNode = node; + node = node->fNext; + myNode->Unlink(); + myNode->Link( &fActiveSofts ); + + /// Queue up in our sorted list... + if( sortedList == nil ) + myNode->SortedLink( &sortedList, (10.0f - sound->GetPriority()) * rank ); + else + sortedList->AddToSortedLink( myNode, (10.0f - sound->GetPriority()) * rank ); + } + else + { + /// Do NOT notify sound, since we were outside of its range and still are + // (but if we're playing, we shouldn't be, so better update) + if( sound->IsPlaying() ) + sound->UpdateSoftVolume( false ); + + node = node->fNext; + } + } + else + { + /// Do NOT notify sound, since we were outside of its range and still are + node = node->fNext; + sound->Disable(); // ensure that dist attenuation is set to zero so we don't accidentally play + } + } + + plgAudioSys::fMutedStateChange = false; + /// Go through sorted list, enabling only the first n sounds and disabling the rest + // DEBUG: Create a screen-only statusLog to display which sounds are what + if( fDebugActiveSoundDisplay == nil ) + fDebugActiveSoundDisplay = plStatusLogMgr::GetInstance().CreateStatusLog( 32, "Active Sounds", plStatusLog::kDontWriteFile | plStatusLog::kDeleteForMe | plStatusLog::kFilledBackground ); + fDebugActiveSoundDisplay->Clear(); + + if(fDisplayNumBuffers) + fDebugActiveSoundDisplay->AddLineF(0xffffffff, "Num Buffers: %d", plDSoundBuffer::GetNumBuffers() ); + fDebugActiveSoundDisplay->AddLine("Not streamed", plStatusLog::kGreen); + fDebugActiveSoundDisplay->AddLine("Disk streamed", plStatusLog::kYellow); + fDebugActiveSoundDisplay->AddLine("RAM streamed", plStatusLog::kWhite); + fDebugActiveSoundDisplay->AddLine("Ogg streamed", plStatusLog::kRed); + fDebugActiveSoundDisplay->AddLine("Incidentals", 0xff00ffff); + fDebugActiveSoundDisplay->AddLine("--------------------"); + + for( i = 0; sortedList != nil && i < fMaxNumSounds; sortedList = sortedList->fSortNext ) + { + plSound *sound = plSound::ConvertNoRef( sortedList->fSoundKey->GetObjectPtr() ); + if(!sound) continue; + + /// Notify sound that it really is still enabled + sound->UpdateSoftVolume( true ); + + UInt32 color = plStatusLog::kGreen; + switch (sound->GetStreamType()) + { + case plSound::kStreamFromDisk: color = plStatusLog::kYellow; break; + case plSound::kStreamFromRAM: color = plStatusLog::kWhite; break; + case plSound::kStreamCompressed: color = plStatusLog::kRed; break; + } + if(sound->GetType() == plgAudioSys::kVoice) color = 0xffff8800; + if(sound->IsPropertySet(plSound::kPropIncidental)) color = 0xff00ffff; + + if( fUsingEAX && sound->GetEAXSettings().IsEnabled() ) + { + fDebugActiveSoundDisplay->AddLineF( + color, + "%d %1.2f %1.2f (%d occ) %s", + sound->GetPriority(), + sortedList->fRank, + sound->GetVolume() ? sound->GetVolumeRank() / sound->GetVolume() : 0, + sound->GetEAXSettings().GetCurrSofts().GetOcclusion(), + sound->GetKeyName() + ); + } + else + { + fDebugActiveSoundDisplay->AddLineF( + color, + "%d %1.2f %1.2f %s", + sound->GetPriority(), + sortedList->fRank, + sound->GetVolume() ? sound->GetVolumeRank() / sound->GetVolume() : 0, + sound->GetKeyName() + ); + } + i++; + } + + for( ; sortedList != nil; sortedList = sortedList->fSortNext, i++ ) + { + plSound *sound = plSound::ConvertNoRef( sortedList->fSoundKey->GetObjectPtr() ); + if(!sound) continue; + + /// These unlucky sounds don't get to play (yet). Also, be extra mean + /// and pretend we're updating for "the first time", which will force them to + /// stop immediately + // Update: since being extra mean can incur a nasty performance hit when sounds hover back and + // forth around the fMaxNumSounds mark, we have a "slop" allowance: i.e. sounds that we're going + // to say shouldn't be playing but we'll let them play for a bit anyway just in case they raise + // in priority. So only be mean to the sounds outside this slop range + sound->UpdateSoftVolume( false, ( i < fMaxNumSounds + fNumSoundsSlop ) ? false : true ); + fDebugActiveSoundDisplay->AddLineF( + 0xff808080, + "%d %1.2f %s", + sound->GetPriority(), + sound->GetVolume() ? sound->GetVolumeRank() / sound->GetVolume() : 0, + sound->GetKeyName() + ); + } + + plProfile_EndTiming(SoundSoftUpdate); +} + +void plAudioSystem::NextDebugSound( void ) +{ + plSoftSoundNode *node; + + if( fCurrDebugSound == nil ) + fCurrDebugSound = ( fSoftRegionSounds == nil ) ? fActiveSofts : fSoftRegionSounds; + else + { + node = fCurrDebugSound; + fCurrDebugSound = fCurrDebugSound->fNext; + if( fCurrDebugSound == nil ) + { + // Trace back to find which list we were in + for( fCurrDebugSound = fSoftRegionSounds; fCurrDebugSound != nil; fCurrDebugSound = fCurrDebugSound->fNext ) + { + if( fCurrDebugSound == node ) // Was in first list, move to 2nd + { + fCurrDebugSound = fActiveSofts; + break; + } + } + // else Must've been in 2nd list, so keep nil + } + } + + if( fCurrDebugSound != nil ) + plSound::SetCurrDebugPlate( fCurrDebugSound->fSoundKey ); + else + plSound::SetCurrDebugPlate( nil ); +} + +void plAudioSystem::SetFadeLength(float lengthSec) +{ + fFadeLength = lengthSec; +} + +hsBool plAudioSystem::MsgReceive(plMessage* msg) +{ + if(plTimeMsg *time = plTimeMsg::ConvertNoRef( msg ) ) + { + if(!plgAudioSys::IsMuted()) + { + double currTime = hsTimer::GetSeconds(); + if(fStartFade == 0) + { + plStatusLog::AddLineS("audio.log", "Starting Fade %f", currTime); + } + if((currTime - fStartFade) > fFadeLength) + { + fStartFade = 0; + plgDispatch::Dispatch()->UnRegisterForExactType( plTimeMsg::Index(), GetKey() ); + plStatusLog::AddLineS("audio.log", "Stopping Fade %f", currTime); + plgAudioSys::SetGlobalFadeVolume( 1.0 ); + } + else + { + plgAudioSys::SetGlobalFadeVolume( (hsScalar)((currTime-fStartFade) / fFadeLength) ); + } + } + return true; + } + + if (plAudioSysMsg* pASMsg = plAudioSysMsg::ConvertNoRef( msg )) + { + if (pASMsg->GetAudFlag() == plAudioSysMsg::kPing && fListenerInit) + { + plAudioSysMsg* pMsg = TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kActivate ); + pMsg->AddReceiver( pASMsg->GetSender() ); + pMsg->SetBCastFlag(plMessage::kBCastByExactType, false); + plgDispatch::MsgSend( pMsg ); + return true; + } + else if (pASMsg->GetAudFlag() == plAudioSysMsg::kSetVol) + { + return true; + } + else if( pASMsg->GetAudFlag() == plAudioSysMsg::kDestroy ) + { + if( fWaitingForShutdown ) + Shutdown(); + return true; + } + else if( pASMsg->GetAudFlag() == plAudioSysMsg::kUnmuteAll ) + { + if( !pASMsg->HasBCastFlag( plMessage::kBCastByExactType ) ) + plgAudioSys::SetMuted( false ); + return true; + } + } + + if(plRenderMsg* pRMsg = plRenderMsg::ConvertNoRef( msg )) + { + //if( fListener ) + { + plProfile_BeginLap(AudioUpdate, this->GetKey()->GetUoid().GetObjectName()); + if(hsTimer::GetMilliSeconds() - fLastUpdateTimeMs > UPDATE_TIME_MS) + { + IUpdateSoftSounds( fCurrListenerPos ); + + if( fUsingEAX ) + { + plProfile_BeginTiming(SoundEAXUpdate); + plEAXListener::GetInstance().ProcessMods( fEAXRegions ); + plProfile_EndTiming(SoundEAXUpdate); + } + //fCommittedListenerPos = fCurrListenerPos; + } + plProfile_EndLap(AudioUpdate, this->GetKey()->GetUoid().GetObjectName()); + } + + return true; + } + + if(plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg )) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fEAXRegions.Append( plEAXListenerMod::ConvertNoRef( refMsg->GetRef() ) ); + plEAXListener::GetInstance().ClearProcessCache(); + } + else if( refMsg->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) ) + { + int idx = fEAXRegions.Find( plEAXListenerMod::ConvertNoRef( refMsg->GetRef() ) ); + if( idx != fEAXRegions.kMissingIndex ) + fEAXRegions.Remove( idx ); + plEAXListener::GetInstance().ClearProcessCache(); + } + return true; + } + + if(plAgeLoadedMsg *pALMsg = plAgeLoadedMsg::ConvertNoRef(msg)) + { + if(!pALMsg->fLoaded) + { + fLastPos = fCurrListenerPos; + fListenerInit = false; + } + else + { + fListenerInit = true; + } + } + + return hsKeyedObject::MsgReceive(msg); +} + +// plgAudioSystem ////////////////////////////////////////////////////////////////////// + +plAudioSystem* plgAudioSys::fSys = nil; +hsBool plgAudioSys::fInit = false; +hsBool plgAudioSys::fActive = false; +hsBool plgAudioSys::fUseHardware = false; +hsBool plgAudioSys::fMuted = true; +hsBool plgAudioSys::fDelayedActivate = false; +hsBool plgAudioSys::fEnableEAX = false; +hsWindowHndl plgAudioSys::fWnd = nil; +hsScalar plgAudioSys::fChannelVolumes[ kNumChannels ] = { 1.f, 1.f, 1.f, 1.f, 1.f, 1.f }; +hsScalar plgAudioSys::f2D3DBias = 0.75f; +UInt32 plgAudioSys::fDebugFlags = 0; +hsScalar plgAudioSys::fStreamingBufferSize = 2.f; +hsScalar plgAudioSys::fStreamFromRAMCutoff = 10.f; +UInt8 plgAudioSys::fPriorityCutoff = 9; // We cut off sounds above this priority +hsBool plgAudioSys::fEnableExtendedLogs = false; +hsScalar plgAudioSys::fGlobalFadeVolume = 1.f; +hsBool plgAudioSys::fLogStreamingUpdates = false; +std::string plgAudioSys::fDeviceName; +hsBool plgAudioSys::fRestarting = false; +hsBool plgAudioSys::fMutedStateChange = false; + +void plgAudioSys::Init(hsWindowHndl hWnd) +{ + fSys = TRACKED_NEW plAudioSystem; + fSys->RegisterAs( kAudioSystem_KEY ); + plgDispatch::Dispatch()->RegisterForExactType( plAudioSysMsg::Index(), fSys->GetKey() ); + plgDispatch::Dispatch()->RegisterForExactType( plRenderMsg::Index(), fSys->GetKey() ); + + if(hsPhysicalMemory() <= 380) + { + plStatusLog::AddLineS("audio.log", "StreamFromRam Disabled"); + fStreamFromRAMCutoff = 4; + } + fWnd = hWnd; + + if(fMuted) + SetGlobalFadeVolume(0.0f); + + if( fDelayedActivate ) + Activate( true ); +} + +void plgAudioSys::SetActive(hsBool b) +{ + fActive = b; +} + +void plgAudioSys::SetMuted( hsBool b ) +{ + fMuted = b; + fMutedStateChange = true; + + if(fMuted) + SetGlobalFadeVolume(0.0f); + else + SetGlobalFadeVolume(1.0); +} + +void plgAudioSys::SetUseHardware(hsBool b) +{ + fUseHardware = b; + if( fActive ) + Restart(); +} + +void plgAudioSys::EnableEAX( hsBool b ) +{ + fEnableEAX = b; + if( fActive ) + Restart(); +} + +void plgAudioSys::SetAudioMode(AudioMode mode) +{ + if(mode == kDisabled) + { + Activate(false); + return; + } + else if(mode == kSoftware) + { + fActive = true; + fUseHardware = false; + fEnableEAX = false; + } + else if(mode == kHardware) + { + fActive = true; + fUseHardware = true; + fEnableEAX = false; + } + else if(mode == kHardwarePlusEAX) + { + fActive = true; + fUseHardware = true; + fEnableEAX = true; + } + Restart(); +} + +int plgAudioSys::GetAudioMode() +{ + if (fActive) + { + if (fUseHardware) + { + if (fEnableEAX) + { + return kHardwarePlusEAX; + } + else + { + return kHardware; + } + } + else + { + return kSoftware; + } + } + else + { + return kDisabled; + } +} + +void plgAudioSys::Restart( void ) +{ + if( fSys ) + { + fSys->fRestartOnDestruct = true; + Activate( false ); + fRestarting = true; + } +} + +void plgAudioSys::Shutdown() +{ + Activate( false ); + if( fSys ) + { + fSys->UnRegisterAs( kAudioSystem_KEY ); + } +} + +void plgAudioSys::Activate(hsBool b) +{ + if( fSys == nil ) + { + fDelayedActivate = true; + return; + } + + if (b == fInit) + return; + if (!fActive) + return; + if( b ) + { + plStatusLog::AddLineS( "audio.log", plStatusLog::kBlue, "ASYS: -- Attempting audio system init --" ); + if( !fSys->Init( fWnd ) ) + { + // Cannot init audio system. Don't activate + return; + } + fInit = true; + fSys->SetActive( true ); + + if( !IsMuted() ) + { + SetMuted( true ); + plAudioSysMsg *msg = TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kUnmuteAll ); + msg->SetTimeStamp( hsTimer::GetSysSeconds() ); + msg->AddReceiver( fSys->GetKey() ); + msg->SetBCastFlag( plMessage::kBCastByExactType, false ); + msg->Send(); + } + return; + } + + fSys->SetActive( false ); + + plStatusLog::AddLineS( "audio.log", plStatusLog::kBlue, "ASYS: -- Sending deactivate/destroy messages --" ); + plgDispatch::MsgSend( TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kDeActivate ) ); + + // Send ourselves a shutdown message, so that the deactivates get processed first + fSys->fWaitingForShutdown = true; + plAudioSysMsg *msg = TRACKED_NEW plAudioSysMsg( plAudioSysMsg::kDestroy ); + msg->SetBCastFlag( plMessage::kBCastByExactType, false ); + msg->Send( fSys->GetKey() ); +// fSys->Shutdown(); + + fInit = false; +} + +void plgAudioSys::SetChannelVolume( ASChannel chan, hsScalar vol ) +{ + fChannelVolumes[ chan ] = vol; +} + +void plgAudioSys::SetGlobalFadeVolume( hsScalar vol ) +{ + if(!fMuted) + fGlobalFadeVolume = vol; + else + fGlobalFadeVolume = 0; +} + +hsScalar plgAudioSys::GetChannelVolume( ASChannel chan ) +{ + return fChannelVolumes[ chan ]; +} + +void plgAudioSys::NextDebugSound( void ) +{ + fSys->NextDebugSound(); +} + +void plgAudioSys::Set2D3DBias( hsScalar bias ) +{ + f2D3DBias = bias; +} + +hsScalar plgAudioSys::Get2D3Dbias() +{ + return f2D3DBias; +} + +void plgAudioSys::SetDeviceName(const char *device, hsBool restart /* = false */) +{ + fDeviceName = device; + if(restart) + Restart(); +} + +int plgAudioSys::GetNumAudioDevices() +{ + if(fSys) + return fSys->GetNumAudioDevices(); + return 0; +} + +const char *plgAudioSys::GetAudioDeviceName(int index) +{ + if(fSys) + { + return fSys->GetAudioDeviceName(index); + } + return nil; +} + +ALCdevice *plgAudioSys::GetCaptureDevice() +{ + if(fSys) + { + return fSys->fCaptureDevice; + } + return nil; +} + +hsBool plgAudioSys::SupportsEAX(const char *deviceName) +{ + if(fSys) + { + return fSys->SupportsEAX(deviceName); + } + return nil; +} + +void plgAudioSys::RegisterSoftSound( const plKey soundKey ) +{ + if(fSys) + { + fSys->RegisterSoftSound(soundKey); + } +} + +void plgAudioSys::UnregisterSoftSound( const plKey soundKey ) +{ + if(fSys) + { + fSys->UnregisterSoftSound(soundKey); + } +} + +void plgAudioSys::SetListenerPos(const hsPoint3 pos) +{ + if(fSys) + { + fSys->SetListenerPos(pos); + } +} + +void plgAudioSys::SetListenerVelocity(const hsVector3 vel) +{ + if(fSys) + { + fSys->SetListenerVelocity(vel); + } +} + +void plgAudioSys::SetListenerOrientation(const hsVector3 view, const hsVector3 up) +{ + if(fSys) + { + fSys->SetListenerOrientation(view, up); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h new file mode 100644 index 00000000..ba5a7dc4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h @@ -0,0 +1,264 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAudioSystem_h +#define plAudioSystem_h + +#include "hsStlUtils.h" +#include "hsWindowHndl.h" +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "../pnKeyedObject/hsKeyedObject.h" + +#define DEFAULT_AUDIO_DEVICE_NAME "Generic Software" + +typedef wchar_t WCHAR; + +class plSound; +class plSoftSoundNode; +class plgAudioSys; +class plStatusLog; +class plEAXListenerMod; + +typedef struct ALCdevice_struct ALCdevice; +typedef struct ALCcontext_struct ALCcontext; + + +class DeviceDescriptor +{ +public: + DeviceDescriptor(const char *name, hsBool supportsEAX): + fDeviceName(name), + fSupportsEAX(supportsEAX) + { + } + const char *GetDeviceName() { return fDeviceName.c_str();} + hsBool SupportsEAX() { return fSupportsEAX; } + +private: + std::string fDeviceName; + hsBool fSupportsEAX; +}; + +class plAudioSystem : public hsKeyedObject +{ +public: + plAudioSystem(); + ~plAudioSystem(); + + CLASSNAME_REGISTER( plAudioSystem ); + GETINTERFACE_ANY( plAudioSystem, hsKeyedObject ); + + enum + { + kThreadSndRef = 0, + kRefEAXRegion + }; + + hsBool Init(hsWindowHndl hWnd); + void Shutdown(); + + void SetActive( hsBool b ); + + void SetListenerPos(const hsPoint3 pos); + void SetListenerVelocity(const hsVector3 vel); + void SetListenerOrientation(const hsVector3 view, const hsVector3 up); + void SetMaxNumberOfActiveSounds(); // sets the max number of active sounds based on the priority cutoff + void SetDistanceModel(int i); + + virtual hsBool MsgReceive(plMessage* msg); + double GetTime(); + + void NextDebugSound( void ); + hsPoint3 GetCurrListenerPos( void ) const { return fCurrListenerPos; } + + int GetNumAudioDevices(); + const char *GetAudioDeviceName(int index); + hsBool SupportsEAX(const char *deviceName); + + void SetFadeLength(float lengthSec); + +protected: + + friend class plgAudioSys; + + ALCdevice * fDevice; + ALCcontext * fContext; + ALCdevice * fCaptureDevice; + + plSoftSoundNode *fSoftRegionSounds; + plSoftSoundNode *fActiveSofts; + plStatusLog *fDebugActiveSoundDisplay; + + static Int32 fMaxNumSounds, fNumSoundsSlop; // max number of sounds the engine is allowed to audibly play. Different than fMaxNumSources. That is the max number of sounds the audio card can play + plSoftSoundNode *fCurrDebugSound; + hsTArray fPendingRegisters; + + hsPoint3 fCurrListenerPos;//, fCommittedListenerPos; + hsBool fActive, fUsingEAX, fRestartOnDestruct, fWaitingForShutdown; + __int64 fStartTime; + + hsTArray fMyRefs; + hsTArray fEAXRegions; + + hsPoint3 fLastPos; + hsBool fAvatarPosSet; // used for listener stuff + + hsBool fDisplayNumBuffers; + + std::vector fDeviceList; // list of openal device names + + double fStartFade; + float fFadeLength; + unsigned int fMaxNumSources; // max openal sources + double fLastUpdateTimeMs; + + void RegisterSoftSound( const plKey soundKey ); + void UnregisterSoftSound( const plKey soundKey ); + void IUpdateSoftSounds( const hsPoint3 &newPosition ); + UInt32 IScaleVolume(float volume); + void IEnumerateDevices(); + +public: + hsBool fListenerInit; +}; + +class plgAudioSys +{ +public: + enum ASChannel + { + kSoundFX, + kAmbience, + kBgndMusic, + kGUI, + kNPCVoice, + kVoice, + kNumChannels + }; + + enum DebugFlags + { + kDisableRightSelect = 0x00000001, + kDisableLeftSelect = 0x00000002 + }; + + enum AudioMode + { + kDisabled, + kSoftware, + kHardware, + kHardwarePlusEAX, + }; + static void Init(hsWindowHndl hWnd); + static hsBool Hardware() { return fUseHardware; } + static void SetUseHardware(hsBool b); + static void SetActive(hsBool b); + static void SetMuted( hsBool b ); + static void EnableEAX( hsBool b ); + static hsBool Active() { return fInit; } + static void Shutdown(); + static void Activate(hsBool b); + static hsBool IsMuted( void ) { return fMuted; } + static hsWindowHndl hWnd() { return fWnd; } + static plAudioSystem* Sys() { return fSys; } + static void Restart( void ); + static hsBool UsingEAX( void ) { return fSys->fUsingEAX; } + + static void NextDebugSound( void ); + + static void SetChannelVolume( ASChannel chan, hsScalar vol ); + static hsScalar GetChannelVolume( ASChannel chan ); + + static void Set2D3DBias( hsScalar bias ); + static hsScalar Get2D3Dbias(); + + static void SetGlobalFadeVolume( hsScalar vol ); + static hsScalar GetGlobalFadeVolume( void ) { return fGlobalFadeVolume; } + + static void SetDebugFlag( UInt32 flag, hsBool set = true ) { if( set ) fDebugFlags |= flag; else fDebugFlags &= ~flag; } + static hsBool IsDebugFlagSet( UInt32 flag ) { return fDebugFlags & flag; } + static void ClearDebugFlags( void ) { fDebugFlags = 0; } + + static hsScalar GetStreamingBufferSize( void ) { return fStreamingBufferSize; } + static void SetStreamingBufferSize( hsScalar size ) { fStreamingBufferSize = size; } + + static UInt8 GetPriorityCutoff( void ) { return fPriorityCutoff; } + static void SetPriorityCutoff( UInt8 cut ) { fPriorityCutoff = cut; if(fSys) fSys->SetMaxNumberOfActiveSounds(); } + + static hsBool AreExtendedLogsEnabled( void ) { return fEnableExtendedLogs; } + static void EnableExtendedLogs( hsBool e ) { fEnableExtendedLogs = e; } + + static hsScalar GetStreamFromRAMCutoff( void ) { return fStreamFromRAMCutoff; } + static void SetStreamFromRAMCutoff( hsScalar c ) { fStreamFromRAMCutoff = c; } + + static void SetListenerPos(const hsPoint3 pos); + static void SetListenerVelocity(const hsVector3 vel); + static void SetListenerOrientation(const hsVector3 view, const hsVector3 up); + + static void ShowNumBuffers(hsBool b) { if(fSys) fSys->fDisplayNumBuffers = b; } + + static void SetAudioMode(AudioMode mode); + static int GetAudioMode(); + static hsBool LogStreamingUpdates() { return fLogStreamingUpdates; } + static void SetLogStreamingUpdates(hsBool logUpdates) { fLogStreamingUpdates = logUpdates; } + static void SetDeviceName(const char *device, hsBool restart = false); + static const char *GetDeviceName() { return fDeviceName.c_str(); } + static int GetNumAudioDevices(); + static const char *GetAudioDeviceName(int index); + static ALCdevice *GetCaptureDevice(); + static hsBool SupportsEAX(const char *deviceName); + static void RegisterSoftSound( const plKey soundKey ); + static void UnregisterSoftSound( const plKey soundKey ); + + static hsBool IsRestarting() {return fRestarting;} + +private: + friend class plAudioSystem; + + static plAudioSystem* fSys; + static hsBool fInit; + static hsBool fActive; + static hsBool fMuted; + static hsWindowHndl fWnd; + static hsBool fUseHardware; + static hsBool fDelayedActivate; + static hsScalar fChannelVolumes[ kNumChannels ]; + static hsScalar fGlobalFadeVolume; + static UInt32 fDebugFlags; + static hsBool fEnableEAX; + static hsScalar fStreamingBufferSize; + static UInt8 fPriorityCutoff; + static hsBool fEnableExtendedLogs; + static hsScalar fStreamFromRAMCutoff; + static hsScalar f2D3DBias; + static hsBool fLogStreamingUpdates; + static std::string fDeviceName; + static hsBool fRestarting; + static hsBool fMutedStateChange; + +}; + +#endif //plAudioSystem_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp new file mode 100644 index 00000000..ad62b1b9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp @@ -0,0 +1,783 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDSoundBuffer - Simple wrapper class for a DirectSound buffer. // +// Allows us to simplify all the work done behind the // +// scenes in plWin32BufferThread. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsThread.h" +#include "plDSoundBuffer.h" +#include "al.h" + +#include "plgDispatch.h" +#include "plAudioSystem.h" +#include "../plAudioCore/plAudioCore.h" +#include "../plAudioCore/plAudioFileReader.h" +#include "plEAXEffects.h" + +#include "plProfile.h" + +#include "../plStatusLog/plStatusLog.h" + +#include + +UInt32 plDSoundBuffer::fNumBuffers = 0; +plProfile_CreateCounterNoReset( "Playing", "Sound", SoundPlaying ); +plProfile_CreateCounterNoReset( "Allocated", "Sound", NumAllocated ); + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plDSoundBuffer::plDSoundBuffer( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool isLooping, hsBool tryStatic, bool streaming ) +{ + fLooping = isLooping; + fValid = false; + fBufferDesc = nil; + + fLockPtr = nil; + fLockLength = 0; + + fStreaming = streaming; + + buffer = 0; + source = 0; + for(int i = 0; i < STREAMING_BUFFERS; ++i) + { + streamingBuffers[i] = 0; + } + + IAllocate( size, bufferDesc, enable3D, tryStatic ); + fNumBuffers++; +} + +plDSoundBuffer::~plDSoundBuffer() +{ + IRelease(); + fNumBuffers--; + +} + +//// IAllocate /////////////////////////////////////////////////////////////// + +void plDSoundBuffer::IAllocate( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool tryStatic ) +{ + // Create a DSound buffer description + fBufferDesc = TRACKED_NEW DSBUFFERDESC; + fBufferDesc->dwSize = sizeof( DSBUFFERDESC ); + + fBufferDesc->dwBufferBytes = size; + fBufferDesc->dwReserved = 0; + + fBufferDesc->lpwfxFormat = TRACKED_NEW WAVEFORMATEX; + fBufferDesc->lpwfxFormat->cbSize = 0; + fBufferDesc->lpwfxFormat->nAvgBytesPerSec = bufferDesc.fAvgBytesPerSec; + fBufferDesc->lpwfxFormat->nBlockAlign = bufferDesc.fBlockAlign; + fBufferDesc->lpwfxFormat->nChannels = bufferDesc.fNumChannels; + fBufferDesc->lpwfxFormat->nSamplesPerSec = bufferDesc.fNumSamplesPerSec; + fBufferDesc->lpwfxFormat->wBitsPerSample = bufferDesc.fBitsPerSample; + fBufferDesc->lpwfxFormat->wFormatTag = bufferDesc.fFormatTag; + + // Do we want to try EAX? + if( plgAudioSys::UsingEAX() ) + fEAXSource.Init( this ); + + fValid = true; + plProfile_Inc( NumAllocated ); +} + +//// IRelease //////////////////////////////////////////////////////////////// + +void plDSoundBuffer::IRelease( void ) +{ + if( IsPlaying() ) + Stop(); + + // Release stuff + fEAXSource.Release(); + alSourcei(source, AL_BUFFER, nil); + alDeleteSources(1, &source); + if(buffer) + alDeleteBuffers( 1, &buffer ); + else + alDeleteBuffers(STREAMING_BUFFERS, streamingBuffers); + source = 0; + buffer = 0; + + alGetError(); + + memset(streamingBuffers, 0, STREAMING_BUFFERS * sizeof(unsigned)); + if( fBufferDesc != nil ) + { + delete fBufferDesc->lpwfxFormat; + fBufferDesc->lpwfxFormat = nil; + } + + delete fBufferDesc; + fBufferDesc = nil; + + fValid = false; + plProfile_Dec( NumAllocated ); +} + + +/***************************************************************************** +* +* OpenAL +* +***/ + +int plDSoundBuffer::IGetALFormat(unsigned bitsPerSample, unsigned int numChannels) +{ + int format = 0; + switch(bitsPerSample) + { + case 8: + format = (numChannels == 1) ? AL_FORMAT_MONO8 : AL_FORMAT_STEREO8; + break; + case 16: + format = (numChannels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; + break; + } + + return format; +} + +bool plDSoundBuffer::FillBuffer(void *data, unsigned bytes, plWAVHeader *header) +{ + if(source) + { + alSourcei(source, AL_BUFFER, nil); + alDeleteSources(1, &source); + } + if(buffer) + alDeleteBuffers(1, &buffer); + source = 0; + buffer = 0; + + ALenum format = IGetALFormat(fBufferDesc->lpwfxFormat->wBitsPerSample, fBufferDesc->lpwfxFormat->nChannels); + ALenum error = alGetError(); + alGenBuffers(1, &buffer); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error); + return false; + } + + alBufferData(buffer, format, data, bytes, header->fNumSamplesPerSec ); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to fill sound buffer %d", error); + return false; + } + alGenSources(1, &source); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source); + return false; + } + + // Just make it quiet for now + SetScalarVolume(0); + + alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048); + alGetError(); + if( error != AL_NO_ERROR ) + { + return false; + } + + alSourcei(source, AL_BUFFER, buffer); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to attach buffer to source %d", error); + return false; + } + return true; +} + + +//============================================================================ +// OpenAL Streaming functions +//============================================================================ + +// this function is used when restarting the audio system. It is needed to restart a streaming source from where it left off +bool plDSoundBuffer::SetupStreamingSource(plAudioFileReader *stream) +{ + unsigned char data[STREAM_BUFFER_SIZE]; + unsigned int size; + ALenum error; + + alGetError(); + int numBuffersToQueue = 0; + + // fill buffers with data + for( int i = 0; i < STREAMING_BUFFERS; i++ ) + { + size = stream->NumBytesLeft() < STREAM_BUFFER_SIZE ? stream->NumBytesLeft() : STREAM_BUFFER_SIZE; + if(!size) + { + if(IsLooping()) + { + stream->SetPosition(0); + } + } + + stream->Read(size, data); + numBuffersToQueue++; + + alGenBuffers( 1, &streamingBuffers[i] ); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error); + return false; + } + + ALenum format = IGetALFormat(fBufferDesc->lpwfxFormat->wBitsPerSample, fBufferDesc->lpwfxFormat->nChannels); + alBufferData( streamingBuffers[i], format, data, size, fBufferDesc->lpwfxFormat->nSamplesPerSec ); + if( (error = alGetError()) != AL_NO_ERROR ) + plStatusLog::AddLineS("audio.log", "alBufferData"); + } + + // Generate AL Source + alGenSources( 1, &source ); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source); + return false; + } + alSourcei(source, AL_BUFFER, nil); + SetScalarVolume(0); + + + alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048); + alGetError(); + if( error != AL_NO_ERROR ) + { + return false; + } + + alSourceQueueBuffers( source, numBuffersToQueue, streamingBuffers ); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to queue buffers %d", error); + return false; + } + return true; +} + +// this function is used when starting up a streaming sound, as opposed to restarting it due to an audio system restart. +bool plDSoundBuffer::SetupStreamingSource(void *data, unsigned bytes) +{ + unsigned char bufferData[STREAM_BUFFER_SIZE]; + unsigned int size; + ALenum error; + char *pData = (char *)data; + + alGetError(); + int numBuffersToQueue = 0; + + // fill buffers with data + for( int i = 0; i < STREAMING_BUFFERS; i++ ) + { + size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE; + if(!size) + break; + + MemCopy(bufferData, pData, size); + pData += size; + bytes-= size; + numBuffersToQueue++; + + alGenBuffers( 1, &streamingBuffers[i] ); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error); + return false; + } + + ALenum format = IGetALFormat(fBufferDesc->lpwfxFormat->wBitsPerSample, fBufferDesc->lpwfxFormat->nChannels); + alBufferData( streamingBuffers[i], format, bufferData, size, fBufferDesc->lpwfxFormat->nSamplesPerSec ); + if( (error = alGetError()) != AL_NO_ERROR ) + plStatusLog::AddLineS("audio.log", "alBufferData"); + } + + // Generate AL Source + alGenSources( 1, &source ); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source); + return false; + } + alSourcei(source, AL_BUFFER, nil); + SetScalarVolume(0); + + alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048); + alGetError(); + if( error != AL_NO_ERROR ) + { + return false; + } + + alSourceQueueBuffers( source, numBuffersToQueue, streamingBuffers ); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to queue buffers %d", error); + return false; + } + return true; +} + +//============================================================================ +int plDSoundBuffer::BuffersProcessed() +{ + if(alIsSource(source)==AL_FALSE) + { + plStatusLog::AddLineS("audio.log", "BuffersProcessed, source invalid"); + return 0; + } + ALint processed = 0; + alGetSourcei( source, AL_BUFFERS_PROCESSED, &processed ); + if(alGetError() != AL_NO_ERROR) + { + plStatusLog::AddLineS("audio.log", "alGetSourcei failed"); + } + + return processed; +} + +//============================================================================ +int plDSoundBuffer::BuffersQueued() +{ + if(alIsSource(source)==AL_FALSE) return 0; + ALint queued = 0; + alGetSourcei( source, AL_BUFFERS_QUEUED, &queued ); + alGetError(); + + return queued; + +} + +//============================================================================ +bool plDSoundBuffer::StreamingFillBuffer(plAudioFileReader *stream) +{ + if(!source) + return false; + + ALenum error; + ALuint bufferId; + unsigned char data[STREAM_BUFFER_SIZE]; + int buffersProcessed = BuffersProcessed(); + hsBool finished = false; + + for(int i = 0; i < buffersProcessed; i++) + { + alSourceUnqueueBuffers( source, 1, &bufferId ); + if( (error = alGetError()) != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to unqueue buffer %d", error); + return false; + } + + if(!finished) + { + if(stream->NumBytesLeft() == 0) + { + // if at anytime we run out of data, and we are looping, reset the data stream and continue to fill buffers + if(IsLooping()) + { + stream->SetPosition(0); // we are looping, so reset data stream, and keep filling buffers + } + else + { + finished = true; // no more data, but we could still be playing, so we don't want to stop the sound yet + } + } + + if(!finished) + { unsigned int size = stream->NumBytesLeft() < STREAM_BUFFER_SIZE ? stream->NumBytesLeft() : STREAM_BUFFER_SIZE; + stream->Read(size, data); + + ALenum format = IGetALFormat(fBufferDesc->lpwfxFormat->wBitsPerSample, fBufferDesc->lpwfxFormat->nChannels); + alBufferData( bufferId, format, data, size, fBufferDesc->lpwfxFormat->nSamplesPerSec ); + if( (error = alGetError()) != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to copy data to sound buffer %d", error); + return false; + } + + alSourceQueueBuffers( source, 1, &bufferId ); + if( (error = alGetError()) != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to queue buffer %d", error); + return false; + } + } + } + } + if(!IsPlaying() && !finished) + { + alSourcePlay(source); + } + alGetError(); + return true; +} + +/***************************************************************************** +* +* Voice playback functions +* +***/ + +bool plDSoundBuffer::GetAvailableBufferId(unsigned *bufferId) +{ + if(mAvailableBuffers.empty()) + { + return false; + } + *bufferId = mAvailableBuffers.front(); + mAvailableBuffers.pop_front(); + return true; +} + +bool plDSoundBuffer::SetupVoiceSource() +{ + ALenum error; + alGetError(); + + // Generate AL Buffers + alGenBuffers( STREAMING_BUFFERS, streamingBuffers ); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error); + return false; + } + + for( int i = 0; i < STREAMING_BUFFERS; i++ ) + { + mAvailableBuffers.push_back(streamingBuffers[i]); + } + + // Generate AL Source + alGenSources( 1, &source ); + error = alGetError(); + if( error != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source); + return false; + } + + SetScalarVolume(0); + + alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048); + alGetError(); + if( error != AL_NO_ERROR ) + { + return false; + } + alSourcei(source, AL_BUFFER, nil); + alGetError(); + //alSourcei(source, AL_PITCH, 0); + + // dont queue any buffers here + return true; +} + +//============================================================================ +void plDSoundBuffer::UnQueueVoiceBuffers() +{ + unsigned buffersProcessed = BuffersProcessed(); + if(buffersProcessed) + plStatusLog::AddLineS("audio.log", "unqueuing buffers %d", buffersProcessed); + for(int i = 0; i < buffersProcessed; i++) + { + ALuint unQueued; + alSourceUnqueueBuffers( source, 1, &unQueued ); + if(alGetError() == AL_NO_ERROR) + { + mAvailableBuffers.push_back(unQueued); + } + else + { + plStatusLog::AddLineS("audio.log", "Failed to unqueue buffer"); + } + } +} + +//============================================================================ +bool plDSoundBuffer::VoiceFillBuffer(void *data, unsigned bytes, unsigned bufferId) +{ + if(!source) + return false; + + ALenum error; + unsigned int size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE; + + ALenum format = IGetALFormat(fBufferDesc->lpwfxFormat->wBitsPerSample, fBufferDesc->lpwfxFormat->nChannels); + alBufferData( bufferId, format, data, size, fBufferDesc->lpwfxFormat->nSamplesPerSec ); + if( (error = alGetError()) != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to copy data to sound buffer %d", error); + return false; + } + alSourceQueueBuffers( source, 1, &bufferId ); + if( (error = alGetError()) != AL_NO_ERROR ) + { + plStatusLog::AddLineS("audio.log", "Failed to queue buffer %d", error); + return false; + } + if(!IsPlaying()) + { + alSourcePlay(source); + } + alGetError(); + + return true; +} + +//// SetLooping ////////////////////////////////////////////////////////////// + +void plDSoundBuffer::SetLooping( hsBool loop ) +{ + fLooping = loop; +} + +void plDSoundBuffer::SetMinDistance( int dist ) +{ + alSourcei(source, AL_REFERENCE_DISTANCE, dist); + ALenum error; + if((error = alGetError()) != AL_NO_ERROR) + plStatusLog::AddLineS("audio.log", "Failed to set min distance"); +} + +void plDSoundBuffer::SetMaxDistance( int dist ) +{ + alSourcei(source, AL_MAX_DISTANCE, dist); + ALenum error; + if((error = alGetError()) != AL_NO_ERROR) + plStatusLog::AddLineS("audio.log", "Failed to set min distance"); +} + +//// Play //////////////////////////////////////////////////////////////////// + +void plDSoundBuffer::Play( void ) +{ + if(!source) + return; + ALenum error = alGetError(); // clear error + + // we dont want openal to loop our streaming buffers, or the buffer will loop back on itself. We will handle looping in the streaming sound + if(fLooping && !fStreaming) + alSourcei(source, AL_LOOPING, AL_TRUE); + else + alSourcei(source, AL_LOOPING, AL_FALSE); + + error = alGetError(); + alSourcePlay(source); + error = alGetError(); + if(error != AL_NO_ERROR) + plStatusLog::AddLineS("voice.log", "Play failed"); + + plProfile_Inc( SoundPlaying ); + +} + +//// Stop //////////////////////////////////////////////////////////////////// + +void plDSoundBuffer::Stop( void ) +{ + if(!source) + return; + alSourceStop(source); + alGetError(); + plProfile_Dec( SoundPlaying ); +} + +//============================================================================ +void plDSoundBuffer::SetPosition(float x, float y, float z) +{ + alSource3f(source, AL_POSITION, x, y, -z); // negate z coord, since openal uses opposite handedness + alGetError(); +} + +//============================================================================ +void plDSoundBuffer::SetOrientation(float x, float y, float z) +{ + alSource3f(source, AL_ORIENTATION, x, y, -z); // negate z coord, since openal uses opposite handedness + alGetError(); +} + +//============================================================================ +void plDSoundBuffer::SetVelocity(float x, float y, float z) +{ + alSource3f(source, AL_VELOCITY, 0, 0, 0); // no doppler shift + alGetError(); +} + +//============================================================================ +void plDSoundBuffer::SetConeAngles(int inner, int outer) +{ + alSourcei(source, AL_CONE_INNER_ANGLE, inner); + alSourcei(source, AL_CONE_OUTER_ANGLE, outer); + alGetError(); +} + +//============================================================================ +void plDSoundBuffer::SetConeOrientation(float x, float y, float z) +{ + alSource3f(source, AL_DIRECTION, x, y, -z); // negate z coord, since openal uses opposite handedness + alGetError(); +} + +//============================================================================ +// vol range: -5000 - 0 +void plDSoundBuffer::SetConeOutsideVolume(int vol) +{ + float volume = (float)vol / 5000.0f + 1.0f; // mb to scalar + alSourcef(source, AL_CONE_OUTER_GAIN, volume); + alGetError(); +} + +//============================================================================ +void plDSoundBuffer::Rewind() +{ + alSourceRewind(source); + alGetError(); +} + +//// IsPlaying /////////////////////////////////////////////////////////////// + +hsBool plDSoundBuffer::IsPlaying( void ) +{ + ALint state = AL_STOPPED; + alGetSourcei(source, AL_SOURCE_STATE, &state); + alGetError(); + return state == AL_PLAYING; +} + +//// IsEAXAccelerated //////////////////////////////////////////////////////// + +hsBool plDSoundBuffer::IsEAXAccelerated( void ) const +{ + return fEAXSource.IsValid(); +} + +//// BytePosToMSecs ////////////////////////////////////////////////////////// + +UInt32 plDSoundBuffer::BytePosToMSecs( UInt32 bytePos ) const +{ + return (UInt32)(bytePos * 1000 / (hsScalar)fBufferDesc->lpwfxFormat->nAvgBytesPerSec); +} + +//// GetBufferBytePos //////////////////////////////////////////////////////// + +UInt32 plDSoundBuffer::GetBufferBytePos( hsScalar timeInSecs ) const +{ + hsAssert( fBufferDesc != nil && fBufferDesc->lpwfxFormat != nil, "Nil buffer description when calling GetBufferBytePos()" ); + + UInt32 byte = (UInt32)( timeInSecs * (hsScalar)fBufferDesc->lpwfxFormat->nSamplesPerSec ); + byte *= fBufferDesc->lpwfxFormat->nBlockAlign; + + return byte; +} + +//// GetLengthInBytes //////////////////////////////////////////////////////// + +UInt32 plDSoundBuffer::GetLengthInBytes( void ) const +{ + return (UInt32)fBufferDesc->dwBufferBytes; +} + +//// SetEAXSettings ////////////////////////////////////////////////////////// + +void plDSoundBuffer::SetEAXSettings( plEAXSourceSettings *settings, hsBool force ) +{ + fEAXSource.SetFrom( settings, source, force ); +} + +//// GetBlockAlign /////////////////////////////////////////////////////////// + +UInt8 plDSoundBuffer::GetBlockAlign( void ) const +{ + return ( fBufferDesc != nil && fBufferDesc->lpwfxFormat != nil ) ? fBufferDesc->lpwfxFormat->nBlockAlign : 0; +} + +//// SetScalarVolume ///////////////////////////////////////////////////////// +// Sets the volume, but on a range from 0 to 1 + +void plDSoundBuffer::SetScalarVolume( hsScalar volume ) +{ + if(source) + { + ALenum error; + alSourcef(source, AL_GAIN, volume); + if((error = alGetError()) != AL_NO_ERROR) + plStatusLog::AddLineS("audio.log", "failed to set volume on source %d", error); + } +} + +unsigned plDSoundBuffer::GetByteOffset() +{ + ALint bytes; + alGetSourcei(source, AL_BYTE_OFFSET, &bytes); + ALenum error = alGetError(); + return bytes; +} + +float plDSoundBuffer::GetTimeOffsetSec() +{ + float time; + alGetSourcef(source, AL_SEC_OFFSET, &time); + ALenum error = alGetError(); + return time; +} + +void plDSoundBuffer::SetTimeOffsetSec(float seconds) +{ + alSourcef(source, AL_SEC_OFFSET, seconds); + ALenum error = alGetError(); +} + +void plDSoundBuffer::SetTimeOffsetBytes(unsigned bytes) +{ + alSourcef(source, AL_BYTE_OFFSET, bytes); + ALenum error = alGetError(); +} + \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h new file mode 100644 index 00000000..83dfc780 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h @@ -0,0 +1,152 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDSoundBuffer - Simple wrapper class for a DirectSound buffer. // +// Allows us to simplify all the work done behind the // +// scenes in plWin32BufferThread. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDSoundBuffer_h +#define _plDSoundBuffer_h + +#include "hsStlUtils.h" +#include "hsTemplates.h" +#include "plEAXEffects.h" +#define STREAMING_BUFFERS 16 +#define STREAM_BUFFER_SIZE 4608*4 + +//#define VOICE_BUFFERS 4 +//#define VOICE_BUFFER_SIZE 4608 + +class plWAVHeader; +class plAudioFileReader; + +typedef struct tWAVEFORMATEX WAVEFORMATEX; +typedef struct _DSBUFFERDESC DSBUFFERDESC; + + +// Ported to OpenAL from DirectSound May 2006. Idealy the openal sources would be seperate from this class. +// OpenAl sound buffer, and source. +class plDSoundBuffer +{ +public: + plDSoundBuffer( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool looping, hsBool tryStatic = false, bool streaming = false ); + ~plDSoundBuffer(); + + void Play( void ); + void Stop( void ); + void Rewind() ; + + UInt32 GetLengthInBytes( void ) const; + void SetScalarVolume( hsScalar volume ); // Sets the volume, but on a range from 0 to 1 + + unsigned GetSource() { return source; } + void SetPosition(float x, float y, float z); + void SetOrientation(float x, float y, float z); + void SetVelocity(float x, float y, float z); + void SetConeAngles(int inner, int outer); + void SetConeOrientation(float x, float y, float z); + void SetConeOutsideVolume(int vol); + + void SetLooping( hsBool loop ); + void SetMinDistance( int dist); + void SetMaxDistance( int dist ); + + hsBool IsValid( void ) const { return fValid; } + hsBool IsPlaying( void ); + hsBool IsLooping( void ) const { return fLooping; } + hsBool IsEAXAccelerated( void ) const; + + bool FillBuffer(void *data, unsigned bytes, plWAVHeader *header); + + // Streaming support + bool SetupStreamingSource(plAudioFileReader *stream); + bool SetupStreamingSource(void *data, unsigned bytes); + int BuffersProcessed(); + bool StreamingFillBuffer(plAudioFileReader *stream); + + bool SetupVoiceSource(); + bool VoiceFillBuffer(void *data, unsigned bytes, unsigned buferId); + void UnQueueVoiceBuffers(); + + + unsigned GetByteOffset(); + UInt32 GetBufferBytePos( hsScalar timeInSecs ) const; + UInt32 BytePosToMSecs( UInt32 bytePos ) const; + + void SetEAXSettings( plEAXSourceSettings *settings, hsBool force = false ); + void SetTimeOffsetBytes(unsigned bytes); + UInt8 GetBlockAlign( void ) const; + static UInt32 GetNumBuffers() { return fNumBuffers; } + float GetDefaultMinDistance() { return fDefaultMinDistance; } + bool GetAvailableBufferId(unsigned *bufferId); + unsigned GetNumQueuedBuffers(){ return fNumQueuedBuffers;} // returns the max number of buffers queued on a source + + float GetTimeOffsetSec(); + void SetTimeOffsetSec(float seconds); + int BuffersQueued(); + +protected: + + enum BufferType + { + kStatic, + kStreaming, + kVoice, + }; + + BufferType fType; + hsBool fValid, fLooping; + UInt32 fLockLength; + void * fLockPtr; + + hsTArray fPosNotifys; + bool fStreaming; + DSBUFFERDESC* fBufferDesc; + + unsigned buffer; // used if this is not a streaming buffer + unsigned streamingBuffers[STREAMING_BUFFERS]; // used if this is a streaming buffer + std::list mAvailableBuffers; // used for doing our own buffer management. Specifically voice chat, since we dont want old buffers queued + + unsigned source; + unsigned int fStreamingBufferSize; + + plEAXSource fEAXSource; + + static UInt32 fNumBuffers; + static float fDefaultMinDistance; + + unsigned fNumQueuedBuffers; + hsScalar fPrevVolume; + + void IAllocate( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool tryStatic ); + void IRelease( void ); + int IGetALFormat(unsigned bitsPerSample, unsigned int numChannels); +}; + +#endif //_plDSoundBuffer_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.cpp new file mode 100644 index 00000000..77b25f57 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.cpp @@ -0,0 +1,679 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plEAXEffects - Various classes and wrappers to support EAX // +// acceleration. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsThread.h" +#include "plEAXEffects.h" +#include "../plAudioCore/plAudioCore.h" +#include "plDSoundBuffer.h" +#include "hsTemplates.h" +#include "plEAXListenerMod.h" +#include "hsStream.h" +#include "plAudioSystem.h" +#include + +#include +#include +#include +#include +#include +#include "../plStatusLog/plStatusLog.h" + +#define kDebugLog if( myLog != nil ) myLog->AddLineF( + +static EAXGet s_EAXGet; +static EAXSet s_EAXSet; + + +//// GetInstance ///////////////////////////////////////////////////////////// + +plEAXListener &plEAXListener::GetInstance( void ) +{ + static plEAXListener instance; + return instance; +} + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plEAXListener::plEAXListener() +{ + fInited = false; + ClearProcessCache(); +} + +plEAXListener::~plEAXListener() +{ + Shutdown(); +} + +//// Init //////////////////////////////////////////////////////////////////// + +hsBool plEAXListener::Init( void ) +{ + if( fInited ) + return true; + + if(!alIsExtensionPresent((ALchar *)"EAX4.0")) // is eax 4 supported + { + if(!alIsExtensionPresent((ALchar *) "EAX4.0Emulated")) // is an earlier version of eax supported + { + plStatusLog::AddLineS("audio.log", "EAX not supported"); + return false; + } + else + { + plStatusLog::AddLineS("audio.log", "EAX 4 Emulated supported"); + } + } + else + { + plStatusLog::AddLineS("audio.log", "EAX 4 available"); + } + + // EAX is supported + s_EAXGet = (EAXGet)alGetProcAddress((ALchar *)"EAXGet"); + s_EAXSet = (EAXSet)alGetProcAddress((ALchar *)"EAXSet"); + if(!s_EAXGet || ! s_EAXSet) + { + IFail( "EAX initialization failed", true ); + return false; + } + fInited = true; + +#if 1 + try + { + // Make an EAX call here to prevent problems on WDM driver + unsigned int lRoom = -10000; + + SetGlobalEAXProperty(DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ROOM, &lRoom, sizeof( unsigned int )); + } + catch ( ... ) + { + plStatusLog::AddLineS("audio.log", "Unable to set EAX Property Set, disabling EAX..."); + plgAudioSys::EnableEAX(false); + return false; + } +#endif + + ClearProcessCache(); + + return true; +} + +//// Shutdown //////////////////////////////////////////////////////////////// + +void plEAXListener::Shutdown( void ) +{ + if( !fInited ) + return; + + s_EAXSet = nil; + s_EAXGet = nil; + IRelease(); +} + + +bool plEAXListener::SetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize ) +{ + if(fInited) + { + return s_EAXSet(&guid, ulProperty, 0, pData, ulDataSize) == AL_NO_ERROR; + } + return false; +} + +bool plEAXListener::GetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize) +{ + if(fInited) + { + return s_EAXGet(&guid, ulProperty, 0, pData, ulDataSize) == AL_NO_ERROR; + } + return false; +} + +bool plEAXSource::SetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize) +{ + return s_EAXSet(&guid, ulProperty, source, pData, ulDataSize) == AL_NO_ERROR; +} + +bool plEAXSource::GetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize) +{ + return s_EAXGet(&guid, ulProperty, source, pData, ulDataSize) == AL_NO_ERROR; +} + + +//// IRelease //////////////////////////////////////////////////////////////// + +void plEAXListener::IRelease( void ) +{ + fInited = false; +} + +//// IFail /////////////////////////////////////////////////////////////////// + +void plEAXListener::IFail( hsBool major ) +{ + plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, + "ERROR in plEAXListener: Could not set global eax params"); + + if( major ) + IRelease(); +} + +void plEAXListener::IFail( const char *msg, hsBool major ) +{ + plStatusLog::AddLineS( "audio.log", plStatusLog::kRed, + "ERROR in plEAXListener: %s", msg ); + + if( major ) + IRelease(); +} + +//// IMuteProperties ///////////////////////////////////////////////////////// +// Mutes the given properties, so if you have some props that you want +// half strength, this function will do it for ya. + +void plEAXListener::IMuteProperties( EAXLISTENERPROPERTIES *props, hsScalar percent ) +{ + // We only mute the room, roomHF and roomLF, since those control the overall effect + // application. All three are a direct linear blend as defined by eax-util.cpp, so + // this should be rather easy + + hsScalar invPercent = 1.f - percent; + + // The old way, as dictated by EAX sample code... + props->lRoom = (int)( ( (float)EAXLISTENER_MINROOM * invPercent ) + ( (float)props->lRoom * percent ) ); + // The new way, as suggested by EAX guys... +// props->lRoom = (int)( 2000.f * log( invPercent ) ) + props->lRoom; + +// props->lRoomLF = (int)( ( (float)EAXLISTENER_MINROOMLF * invPercent ) + ( (float)props->lRoomLF * percent ) ); +// props->lRoomHF = (int)( ( (float)EAXLISTENER_MINROOMHF * invPercent ) + ( (float)props->lRoomHF * percent ) ); +} + +//// ClearProcessCache /////////////////////////////////////////////////////// +// Clears the cache settings used to speed up ProcessMods(). Call whenever +// the list of mods changed. + +void plEAXListener::ClearProcessCache( void ) +{ + fLastBigRegion = nil; + fLastModCount = -1; + fLastWasEmpty = false; + fLastSingleStrength = -1.f; +} + +//// ProcessMods ///////////////////////////////////////////////////////////// +// 9.13.02 mcn - Updated the caching method. Now fLastBigRegion will point +// to a region iff it's the only region from the last pass that had a +// strength > 0. The reason we can't do our trick before is because even if +// we have a region w/ strength 1, another region might power up from 1 and +// thus suddenly alter the total reverb settings. Thus, the only time we +// can wisely skip is if our current big region == fLastBigRegion *and* +// the total strength is the same. + +void plEAXListener::ProcessMods( hsTArray &modArray ) +{ + int i; + float totalStrength; + hsBool firstOne; + + plEAXListenerMod *thisBigRegion = nil; + EAXLISTENERPROPERTIES finalProps; + static int oldTime = timeGetTime(); // Get starting time + int newTime; + hsBool bMorphing = false; + + static plStatusLog *myLog = nil; + + if( myLog == nil && plgAudioSys::AreExtendedLogsEnabled() ) + myLog = plStatusLogMgr::GetInstance().CreateStatusLog( 30, "EAX Reverbs", plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | plStatusLog::kDontWriteFile ); + else if( myLog != nil && !plgAudioSys::AreExtendedLogsEnabled() ) + { + delete myLog; + myLog = nil; + } + + if( myLog != nil ) + myLog->Clear(); + + if( modArray.GetCount() != fLastModCount ) + { + kDebugLog "Clearing cache..." ); + ClearProcessCache(); // Code path changed, clear the entire cache + fLastModCount = modArray.GetCount(); + } + else + { + kDebugLog "" ); + } + + if( modArray.GetCount() > 0 ) + { + kDebugLog "%d regions to calc", modArray.GetCount() ); + + // Reset and find a new one if applicable + thisBigRegion = nil; + + // Accumulate settings from all the active listener regions (shouldn't be too many, we hope) + totalStrength = 0.f; + firstOne = true; + for( i = 0; i < modArray.GetCount(); i++ ) + { + float strength = modArray[ i ]->GetStrength(); + kDebugLog "%4.2f - %s", strength, modArray[ i ]->GetKey()->GetUoid().GetObjectName() ); + if( strength > 0.f ) + { + // fLastBigRegion will point to a region iff it's the only region w/ strength > 0 + if( totalStrength == 0.f ) + thisBigRegion = modArray[ i ]; + else + thisBigRegion = nil; + + if( firstOne ) + { + // First one, just init to it + memcpy( &finalProps, modArray[ i ]->GetListenerProps(), sizeof( finalProps ) ); + totalStrength = strength; + firstOne = false; + } + else + { + hsScalar scale = strength / ( totalStrength + strength ); + EAX3ListenerInterpolate( &finalProps, modArray[ i ]->GetListenerProps(), scale, &finalProps, false ); + totalStrength += strength; + bMorphing = true; + } + + if( totalStrength >= 1.f ) + break; + } + } + + if( firstOne ) + { + // No regions of strength > 0, so just make it quiet + kDebugLog "Reverb should be quiet" ); + if( fLastWasEmpty ) + return; + + memcpy( &finalProps, &EAX30_ORIGINAL_PRESETS[ ORIGINAL_GENERIC ], sizeof( EAXLISTENERPROPERTIES ) ); + finalProps.lRoom = EAXLISTENER_MINROOM; +// finalProps.lRoomLF = EAXLISTENER_MINROOMLF; +// finalProps.lRoomHF = EAXLISTENER_MINROOMHF; + fLastWasEmpty = true; + fLastBigRegion = nil; + fLastSingleStrength = -1.f; + } + else + { + fLastWasEmpty = false; + + if( thisBigRegion == fLastBigRegion && totalStrength == fLastSingleStrength ) + // Cached values should be the same, so we can bail at this point + return; + + fLastBigRegion = thisBigRegion; + fLastSingleStrength = ( thisBigRegion != nil ) ? totalStrength : -1.f; + + if( totalStrength < 1.f ) + { + kDebugLog "Total strength < 1; muting result" ); + // All of them together is less than full strength, so mute our result + IMuteProperties( &finalProps, totalStrength ); + } + } + } + else + { + kDebugLog "No regions at all; disabling reverb" ); + // No regions whatsoever, so disable listener props entirely + if( fLastWasEmpty ) + return; + + memcpy( &finalProps, &EAX30_ORIGINAL_PRESETS[ ORIGINAL_GENERIC ], sizeof( EAXLISTENERPROPERTIES ) ); + finalProps.lRoom = EAXLISTENER_MINROOM; +// finalProps.lRoomLF = EAXLISTENER_MINROOMLF; +// finalProps.lRoomHF = EAXLISTENER_MINROOMHF; + fLastWasEmpty = true; + } + + // if were morphing between regions, do 10th of a second check, otherwise just let it + // change due to opt out(caching) feature. + if(bMorphing) + { + newTime = timeGetTime(); + + // Update, at most, ten times per second + if((newTime - oldTime) < 100) return; + + oldTime = newTime; // update time + } +//finalProps.flAirAbsorptionHF *= 0.3048f; // Convert to feet + //kDebugLog "** Updating property set **" ); + + + if(!SetGlobalEAXProperty(DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, &finalProps, sizeof( finalProps ))) + { + IFail( false ); + } +} + + +////////////////////////////////////////////////////////////////////////////// +//// Source Settings ///////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plEAXSourceSettings::plEAXSourceSettings() +{ + fDirtyParams = kAll; + Enable( false ); +} + +plEAXSourceSettings::~plEAXSourceSettings() +{ +} + +//// Read/Write/Set ////////////////////////////////////////////////////////// + +void plEAXSourceSettings::Read( hsStream *s ) +{ + fEnabled = s->ReadBool(); + if( fEnabled ) + { + fRoom = s->ReadSwap16(); + fRoomHF = s->ReadSwap16(); + fRoomAuto = s->ReadBool(); + fRoomHFAuto = s->ReadBool(); + + fOutsideVolHF = s->ReadSwap16(); + + fAirAbsorptionFactor = s->ReadSwapFloat(); + fRoomRolloffFactor = s->ReadSwapFloat(); + fDopplerFactor = s->ReadSwapFloat(); + fRolloffFactor = s->ReadSwapFloat(); + + fSoftStarts.Read( s ); + fSoftEnds.Read( s ); + + fOcclusionSoftValue = -1.f; + SetOcclusionSoftValue( s->ReadSwapFloat() ); + + fDirtyParams = kAll; + } + else + Enable( false ); // Force init of params +} + +void plEAXSourceSettings::Write( hsStream *s ) +{ + s->WriteBool( fEnabled ); + if( fEnabled ) + { + s->WriteSwap16( fRoom ); + s->WriteSwap16( fRoomHF ); + s->WriteBool( fRoomAuto ); + s->WriteBool( fRoomHFAuto ); + + s->WriteSwap16( fOutsideVolHF ); + + s->WriteSwapFloat( fAirAbsorptionFactor ); + s->WriteSwapFloat( fRoomRolloffFactor ); + s->WriteSwapFloat( fDopplerFactor ); + s->WriteSwapFloat( fRolloffFactor ); + + fSoftStarts.Write( s ); + fSoftEnds.Write( s ); + + s->WriteSwapFloat( fOcclusionSoftValue ); + } +} + +void plEAXSourceSettings::SetRoomParams( Int16 room, Int16 roomHF, hsBool roomAuto, hsBool roomHFAuto ) +{ + fRoom = room; + fRoomHF = roomHF; + fRoomAuto = roomAuto; + fRoomHFAuto = roomHFAuto; + fDirtyParams |= kRoom; +} + +void plEAXSourceSettings::Enable( hsBool e ) +{ + fEnabled = e; + if( !e ) + { + fRoom = EAXBUFFER_MINROOM; + fRoomHF = EAXBUFFER_MINROOMHF; + fRoomAuto = true; + fRoomHFAuto = true; + + fOutsideVolHF = 0; + + fAirAbsorptionFactor = 1.f; + fRoomRolloffFactor = 0.f; + fDopplerFactor = 0.f; + fRolloffFactor = 0.f; + + fOcclusionSoftValue = 0.f; + fSoftStarts.Reset(); + fSoftEnds.Reset(); + fCurrSoftValues.Reset(); + fDirtyParams = kAll; + } +} + +void plEAXSourceSettings::SetOutsideVolHF( Int16 vol ) +{ + fOutsideVolHF = vol; + fDirtyParams |= kOutsideVolHF; +} + +void plEAXSourceSettings::SetFactors( hsScalar airAbsorption, hsScalar roomRolloff, hsScalar doppler, hsScalar rolloff ) +{ + fAirAbsorptionFactor = airAbsorption; + fRoomRolloffFactor = roomRolloff; + fDopplerFactor = doppler; + fRolloffFactor = rolloff; + fDirtyParams |= kFactors; +} + +void plEAXSourceSettings::SetOcclusionSoftValue( hsScalar value ) +{ + if( fOcclusionSoftValue != value ) + { + fOcclusionSoftValue = value; + IRecalcSofts( kOcclusion ); + fDirtyParams |= kOcclusion; + } +} + +void plEAXSourceSettings::IRecalcSofts( UInt8 whichOnes ) +{ + hsScalar percent, invPercent; + + if( whichOnes & kOcclusion ) + { + percent = fOcclusionSoftValue; + invPercent = 1.f - percent; + + Int16 occ = (Int16)( ( (float)fSoftStarts.GetOcclusion() * invPercent ) + ( (float)fSoftEnds.GetOcclusion() * percent ) ); + hsScalar lfRatio = (hsScalar)( ( fSoftStarts.GetOcclusionLFRatio() * invPercent ) + ( fSoftEnds.GetOcclusionLFRatio() * percent ) ); + hsScalar roomRatio = (hsScalar)( ( fSoftStarts.GetOcclusionRoomRatio() * invPercent ) + ( fSoftEnds.GetOcclusionRoomRatio() * percent ) ); + hsScalar directRatio = (hsScalar)( ( fSoftStarts.GetOcclusionDirectRatio() * invPercent ) + ( fSoftEnds.GetOcclusionDirectRatio() * percent ) ); + + fCurrSoftValues.SetOcclusion( occ, lfRatio, roomRatio, directRatio ); + } +} + + +////////////////////////////////////////////////////////////////////////////// +//// Source Soft Settings //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +void plEAXSourceSoftSettings::Reset( void ) +{ + fOcclusion = 0; + fOcclusionLFRatio = 0.25f; + fOcclusionRoomRatio = 1.5f; + fOcclusionDirectRatio = 1.f; +} + +void plEAXSourceSoftSettings::Read( hsStream *s ) +{ + s->ReadSwap( &fOcclusion ); + s->ReadSwap( &fOcclusionLFRatio ); + s->ReadSwap( &fOcclusionRoomRatio ); + s->ReadSwap( &fOcclusionDirectRatio ); +} + +void plEAXSourceSoftSettings::Write( hsStream *s ) +{ + s->WriteSwap( fOcclusion ); + s->WriteSwap( fOcclusionLFRatio ); + s->WriteSwap( fOcclusionRoomRatio ); + s->WriteSwap( fOcclusionDirectRatio ); +} + +void plEAXSourceSoftSettings::SetOcclusion( Int16 occ, hsScalar lfRatio, hsScalar roomRatio, hsScalar directRatio ) +{ + fOcclusion = occ; + fOcclusionLFRatio = lfRatio; + fOcclusionRoomRatio = roomRatio; + fOcclusionDirectRatio = directRatio; +} + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plEAXSource::plEAXSource() +{ + fInit = false; + +} + +plEAXSource::~plEAXSource() +{ + Release(); +} + +//// Init/Release //////////////////////////////////////////////////////////// + +void plEAXSource::Init( plDSoundBuffer *parent ) +{ + fInit = true; + // Init some default params + plEAXSourceSettings defaultParams; + SetFrom( &defaultParams, parent->GetSource() ); +} + +void plEAXSource::Release( void ) +{ + fInit = false; +} + +hsBool plEAXSource::IsValid( void ) const +{ + return true; +} + +//// SetFrom ///////////////////////////////////////////////////////////////// + +void plEAXSource::SetFrom( plEAXSourceSettings *settings, unsigned source, hsBool force ) +{ + UInt32 dirtyParams; + if(source == 0 || !fInit) + return; + + if( force ) + dirtyParams = plEAXSourceSettings::kAll; + else + dirtyParams = settings->fDirtyParams; + + // Do the params + if( dirtyParams & plEAXSourceSettings::kRoom ) + { + SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOM, &settings->fRoom, sizeof(settings->fRoom)); + SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOMHF, &settings->fRoomHF, sizeof(settings->fRoomHF)); + } + + if( dirtyParams & plEAXSourceSettings::kOutsideVolHF ) + { + SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, &settings->fOutsideVolHF, sizeof(settings->fOutsideVolHF)); + } + + if( dirtyParams & plEAXSourceSettings::kFactors ) + { + SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, &settings->fDopplerFactor, sizeof(settings->fDopplerFactor)); + SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, &settings->fRolloffFactor, sizeof(settings->fRolloffFactor)); + SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, &settings->fRoomRolloffFactor, sizeof(settings->fRoomRolloffFactor)); + SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, &settings->fAirAbsorptionFactor, sizeof(settings->fAirAbsorptionFactor)); + } + + if( dirtyParams & plEAXSourceSettings::kOcclusion ) + { + SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSION, &settings->GetCurrSofts().fOcclusion, sizeof(settings->GetCurrSofts().fOcclusion)); + SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, &settings->GetCurrSofts().fOcclusionLFRatio, sizeof(settings->GetCurrSofts().fOcclusionLFRatio)); + SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, &settings->GetCurrSofts().fOcclusionRoomRatio, sizeof(settings->GetCurrSofts().fOcclusionRoomRatio)); + SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, &settings->GetCurrSofts().fOcclusionDirectRatio, sizeof(settings->GetCurrSofts().fOcclusionDirectRatio)); + } + + settings->ClearDirtyParams(); + + // Do all the flags in one pass + DWORD flags; + + + if( GetSourceEAXProperty( source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_FLAGS, &flags, sizeof( DWORD )) ) + { + if( settings->GetRoomAuto() ) + flags |= EAXBUFFERFLAGS_ROOMAUTO; + else + flags &= ~EAXBUFFERFLAGS_ROOMAUTO; + + if( settings->GetRoomHFAuto() ) + flags |= EAXBUFFERFLAGS_ROOMHFAUTO; + else + flags &= ~EAXBUFFERFLAGS_ROOMHFAUTO; + + if( SetSourceEAXProperty( source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_FLAGS, &flags, sizeof( DWORD ) ) ) + { + return; // All worked, return here + } + + // Flag setting failed somehow + hsAssert( false, "Unable to set EAX buffer flags" ); + } +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.h new file mode 100644 index 00000000..663091c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.h @@ -0,0 +1,192 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plEAXEffects - Various classes and wrappers to support EAX // +// acceleration. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plEAXEffects_h +#define _plEAXEffects_h + + +#include "hsTypes.h" +#include "hsTemplates.h" + + +//// Listener Settings Class Definition /////////////////////////////////////// + +class plDSoundBuffer; +class plEAXListenerMod; + +typedef struct _EAXREVERBPROPERTIES EAXREVERBPROPERTIES; + +class plEAXListener +{ +public: + ~plEAXListener(); + static plEAXListener &GetInstance( void ); + + hsBool Init( void ); + void Shutdown( void ); + + bool SetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize ); + bool GetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize ); + + void ProcessMods( hsTArray &modArray ); + void ClearProcessCache( void ); + +protected: + plEAXListener(); + void IFail( hsBool major ); + void IFail( const char *msg, hsBool major ); + void IRelease( void ); + + void IMuteProperties( EAXREVERBPROPERTIES *props, hsScalar percent ); + + hsBool fInited; + + // Cache info + Int32 fLastModCount; + hsBool fLastWasEmpty; + hsScalar fLastSingleStrength; + plEAXListenerMod *fLastBigRegion; + +}; + +//// Soft Buffer Settings Class Definition //////////////////////////////////// +// Used to hold buffer settings that will be attenuated by a soft volume, +// to make the main settings class a bit cleaner + +class hsStream; +class plEAXSourceSoftSettings +{ +public: + Int16 fOcclusion; + hsScalar fOcclusionLFRatio, fOcclusionRoomRatio, fOcclusionDirectRatio; + + void Read( hsStream *s ); + void Write( hsStream *s ); + + void SetOcclusion( Int16 occ, hsScalar lfRatio, hsScalar roomRatio, hsScalar directRatio ); + Int16 GetOcclusion( void ) const { return fOcclusion; } + hsScalar GetOcclusionLFRatio( void ) const { return fOcclusionLFRatio; } + hsScalar GetOcclusionRoomRatio( void ) const { return fOcclusionRoomRatio; } + hsScalar GetOcclusionDirectRatio( void ) const { return fOcclusionDirectRatio; } + + void Reset( void ); +}; + +//// Buffer Settings Class Definition ///////////////////////////////////////// + +class plEAXSource; + +class plEAXSourceSettings +{ + public: + plEAXSourceSettings(); + virtual ~plEAXSourceSettings(); + + void Read( hsStream *s ); + void Write( hsStream *s ); + + void Enable( hsBool e ); + hsBool IsEnabled( void ) const { return fEnabled; } + + void SetRoomParams( Int16 room, Int16 roomHF, hsBool roomAuto, hsBool roomHFAuto ); + Int16 GetRoom( void ) const { return fRoom; } + Int16 GetRoomHF( void ) const { return fRoomHF; } + hsBool GetRoomAuto( void ) const { return fRoomAuto; } + hsBool GetRoomHFAuto( void ) const { return fRoomHFAuto; } + + void SetOutsideVolHF( Int16 vol ); + Int16 GetOutsideVolHF( void ) const { return fOutsideVolHF; } + + void SetFactors( hsScalar airAbsorption, hsScalar roomRolloff, hsScalar doppler, hsScalar rolloff ); + hsScalar GetAirAbsorptionFactor( void ) const { return fAirAbsorptionFactor; } + hsScalar GetRoomRolloffFactor( void ) const { return fRoomRolloffFactor; } + hsScalar GetDopplerFactor( void ) const { return fDopplerFactor; } + hsScalar GetRolloffFactor( void ) const { return fRolloffFactor; } + + plEAXSourceSoftSettings &GetSoftStarts( void ) { return fSoftStarts; } + plEAXSourceSoftSettings &GetSoftEnds( void ) { return fSoftEnds; } + + plEAXSourceSoftSettings &GetCurrSofts( void ) { return fCurrSoftValues; } + + void SetOcclusionSoftValue( hsScalar value ); + hsScalar GetOcclusionSoftValue( void ) const { return fOcclusionSoftValue; } + + void ClearDirtyParams( void ) const { fDirtyParams = 0; } + + protected: + friend class plEAXSource; + friend plEAXSourceSoftSettings; + + hsBool fEnabled; + Int16 fRoom, fRoomHF; + hsBool fRoomAuto, fRoomHFAuto; + Int16 fOutsideVolHF; + hsScalar fAirAbsorptionFactor, fRoomRolloffFactor, fDopplerFactor, fRolloffFactor; + plEAXSourceSoftSettings fSoftStarts, fSoftEnds, fCurrSoftValues; + hsScalar fOcclusionSoftValue; + mutable UInt32 fDirtyParams; + + enum ParamSets + { + kOcclusion = 0x01, + kRoom = 0x02, + kOutsideVolHF = 0x04, + kFactors = 0x08, + kAll = 0xff + }; + + void IRecalcSofts( UInt8 whichOnes ); +}; + +//// Source Class Definition ////////////////////////////////////////////////// + +class plEAXSource +{ +public: + friend plEAXSourceSettings; + friend plEAXSourceSoftSettings; + + plEAXSource(); + virtual ~plEAXSource(); + + void Init( plDSoundBuffer *parent ); + void Release( void ); + hsBool IsValid( void ) const; + bool SetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize); + bool GetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize); + void SetFrom( plEAXSourceSettings *settings, unsigned source, hsBool force = false ); + +private: + hsBool fInit; +}; + +#endif //_plEAXEffects_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.cpp new file mode 100644 index 00000000..2879c5e8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.cpp @@ -0,0 +1,228 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plEAXListenerMod // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plEAXListenerMod.h" +#include "../plIntersect/plSoftVolume.h" +#include "hsResMgr.h" +#include "plgDispatch.h" +#include "plAudioSystem.h" +#include "../pnMessage/plAudioSysMsg.h" + +#include + + +plEAXListenerMod::plEAXListenerMod() +{ + fListenerProps = TRACKED_NEW EAXREVERBPROPERTIES; + fSoftRegion = nil; + fRegistered = false; + fGetsMessages = false; + + memcpy( fListenerProps, &REVERB_ORIGINAL_PRESETS[ ORIGINAL_GENERIC ], sizeof( EAXREVERBPROPERTIES ) ); +} + +plEAXListenerMod::~plEAXListenerMod() +{ + // Tell the audio sys we're going away + IUnRegister(); + + // Unregister for audioSys messages + if( fGetsMessages ) + { + plgDispatch::Dispatch()->UnRegisterForExactType( plAudioSysMsg::Index(), GetKey() ); + fGetsMessages = false; + } + + delete fListenerProps; +} + +void plEAXListenerMod::IRegister( void ) +{ + if( !fGetsMessages ) + { + plgDispatch::Dispatch()->RegisterForExactType( plAudioSysMsg::Index(), GetKey() ); + fGetsMessages = true; + } + + if( fRegistered || GetKey() == nil ) + return; + + plKey sysKey = hsgResMgr::ResMgr()->FindKey( plUoid( kAudioSystem_KEY ) ); + if( sysKey != nil ) + { + plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( sysKey, plRefMsg::kOnCreate, 0, plAudioSystem::kRefEAXRegion ); + hsgResMgr::ResMgr()->AddViaNotify( GetKey(), refMsg, plRefFlags::kPassiveRef ); + fRegistered = true; + } +} + +void plEAXListenerMod::IUnRegister( void ) +{ + if( !fRegistered || GetKey() == nil ) + return; + + plKey sysKey = hsgResMgr::ResMgr()->FindKey( plUoid( kAudioSystem_KEY ) ); + if( sysKey != nil && GetKey() != nil ) + sysKey->Release( GetKey() ); + + fRegistered = false; +} + +hsBool plEAXListenerMod::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + IRegister(); + return false; +} + +hsBool plEAXListenerMod::MsgReceive( plMessage* pMsg ) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( pMsg ); + if( refMsg != nil ) + { + switch( refMsg->fType ) + { + case kRefSoftRegion: + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fSoftRegion = plSoftVolume::ConvertNoRef( refMsg->GetRef() ); + fSoftRegion->SetCheckListener(); + } + else if( refMsg->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) ) + { + fSoftRegion = nil; + } + break; + } + } + + plAudioSysMsg *sysMsg = plAudioSysMsg::ConvertNoRef( pMsg ); + if( sysMsg != nil ) + { + if( sysMsg->GetAudFlag() == plAudioSysMsg::kActivate ) + { + IRegister(); + } + else if( sysMsg->GetAudFlag() == plAudioSysMsg::kDeActivate ) + { + IUnRegister(); + } + + return true; + } + + return plSingleModifier::MsgReceive( pMsg ); +} + +void plEAXListenerMod::Read( hsStream* s, hsResMgr* mgr ) +{ + plSingleModifier::Read( s, mgr ); + + // Read in the soft region + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, 0, kRefSoftRegion ), plRefFlags::kActiveRef ); + + // Read the listener params + fListenerProps->ulEnvironment = s->ReadSwap32(); + fListenerProps->flEnvironmentSize = s->ReadSwapFloat(); + fListenerProps->flEnvironmentDiffusion = s->ReadSwapFloat(); + fListenerProps->lRoom = s->ReadSwap32(); + fListenerProps->lRoomHF = s->ReadSwap32(); + fListenerProps->lRoomLF = s->ReadSwap32(); + fListenerProps->flDecayTime = s->ReadSwapFloat(); + fListenerProps->flDecayHFRatio = s->ReadSwapFloat(); + fListenerProps->flDecayLFRatio = s->ReadSwapFloat(); + fListenerProps->lReflections = s->ReadSwap32(); + fListenerProps->flReflectionsDelay = s->ReadSwapFloat(); + //fListenerProps->vReflectionsPan; // early reflections panning vector + fListenerProps->lReverb = s->ReadSwap32(); // late reverberation level relative to room effect + fListenerProps->flReverbDelay = s->ReadSwapFloat(); + //fListenerProps->vReverbPan; // late reverberation panning vector + fListenerProps->flEchoTime = s->ReadSwapFloat(); + fListenerProps->flEchoDepth = s->ReadSwapFloat(); + fListenerProps->flModulationTime = s->ReadSwapFloat(); + fListenerProps->flModulationDepth = s->ReadSwapFloat(); + fListenerProps->flAirAbsorptionHF = s->ReadSwapFloat(); + fListenerProps->flHFReference = s->ReadSwapFloat(); + fListenerProps->flLFReference = s->ReadSwapFloat(); + fListenerProps->flRoomRolloffFactor = s->ReadSwapFloat(); + fListenerProps->ulFlags = s->ReadSwap32(); + + // Done reading, time to tell the audio sys we exist + IRegister(); +} + +void plEAXListenerMod::Write( hsStream* s, hsResMgr* mgr ) +{ + plSingleModifier::Write( s, mgr ); + + // Write the soft region key + mgr->WriteKey( s, fSoftRegion ); + + // Write the listener params + s->WriteSwap32( fListenerProps->ulEnvironment ); + s->WriteSwapFloat( fListenerProps->flEnvironmentSize ); + s->WriteSwapFloat( fListenerProps->flEnvironmentDiffusion ); + s->WriteSwap32( fListenerProps->lRoom ); + s->WriteSwap32( fListenerProps->lRoomHF ); + s->WriteSwap32( fListenerProps->lRoomLF ); + s->WriteSwapFloat( fListenerProps->flDecayTime ); + s->WriteSwapFloat( fListenerProps->flDecayHFRatio ); + s->WriteSwapFloat( fListenerProps->flDecayLFRatio ); + s->WriteSwap32( fListenerProps->lReflections ); + s->WriteSwapFloat( fListenerProps->flReflectionsDelay ); + //s->WriteSwapFloat( fListenerProps->vReflectionsPan; // early reflections panning vector + s->WriteSwap32( fListenerProps->lReverb ); // late reverberation level relative to room effect + s->WriteSwapFloat( fListenerProps->flReverbDelay ); + //s->WriteSwapFloat( fListenerProps->vReverbPan; // late reverberation panning vector + s->WriteSwapFloat( fListenerProps->flEchoTime ); + s->WriteSwapFloat( fListenerProps->flEchoDepth ); + s->WriteSwapFloat( fListenerProps->flModulationTime ); + s->WriteSwapFloat( fListenerProps->flModulationDepth ); + s->WriteSwapFloat( fListenerProps->flAirAbsorptionHF ); + s->WriteSwapFloat( fListenerProps->flHFReference ); + s->WriteSwapFloat( fListenerProps->flLFReference ); + s->WriteSwapFloat( fListenerProps->flRoomRolloffFactor ); + s->WriteSwap32( fListenerProps->ulFlags ); +} + + +void plEAXListenerMod::SetFromPreset( UInt32 preset ) +{ + memcpy( fListenerProps, &REVERB_ORIGINAL_PRESETS[ preset ], sizeof( EAXREVERBPROPERTIES ) ); +} + +float plEAXListenerMod::GetStrength( void ) +{ + if( fSoftRegion == nil ) + return 0.f; + + return fSoftRegion->GetListenerStrength(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.h new file mode 100644 index 00000000..9875ec43 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.h @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plEAXListenerMod Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plEAXListenerMod_h +#define _plEAXListenerMod_h + + +#include "../pnModifier/plSingleModifier.h" + +class plMessage; +class plSoftVolume; +typedef struct _EAXREVERBPROPERTIES EAXREVERBPROPERTIES; + +class plEAXListenerMod : public plSingleModifier +{ +public: + + plEAXListenerMod(); + virtual ~plEAXListenerMod(); + + CLASSNAME_REGISTER( plEAXListenerMod ); + GETINTERFACE_ANY( plEAXListenerMod, plSingleModifier ); + + enum Refs + { + kRefSoftRegion = 0, + }; + + virtual hsBool MsgReceive( plMessage* pMsg ); + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + float GetStrength( void ); + + EAXREVERBPROPERTIES * GetListenerProps( void ) { return fListenerProps; } + void SetFromPreset( UInt32 preset ); + +protected: + plSoftVolume *fSoftRegion; + EAXREVERBPROPERTIES *fListenerProps; + hsBool fRegistered, fGetsMessages; + + void IRegister( void ); + void IUnRegister( void ); + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval() +}; + +#endif // _plEAXListenerMod_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.cpp new file mode 100644 index 00000000..7af99edc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.cpp @@ -0,0 +1,342 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plOGGCodec - Plasma codec support for the OGG/Vorbis file format. // +// // +//// Notes /////////////////////////////////////////////////////////////////// +// // +// 2.7.2003 - Created by mcn. If only life were really this simple... // +// // +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include +#include +#include + +#include "hsTypes.h" +#include "plOGGCodec.h" + +#include "hsTimer.h" +#include "../pnNetCommon/plNetApp.h" + +plOGGCodec::DecodeFormat plOGGCodec::fDecodeFormat = plOGGCodec::k16bitSigned; +UInt8 plOGGCodec::fDecodeFlags = 0; + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plOGGCodec::plOGGCodec( const char *path, plAudioCore::ChannelSelect whichChan ) : fFileHandle( nil ) +{ + fOggFile = nil; + IOpen( path, whichChan ); + fCurHeaderPos = 0; + fHeadBuf = nil; +} + +void plOGGCodec::BuildActualWaveHeader() +{ + // Build an actual WAVE header for this ogg + int fmtSize = 16; + short fmt = 1; + int factsize = 4; + int factdata = 0; + int size = fDataSize+48; // size of data with header except for first four bytes + + fHeadBuf = (UInt8 *) ALLOC(56); + memcpy(fHeadBuf, "RIFF", 4); + memcpy(fHeadBuf+4, &size, 4); + memcpy(fHeadBuf+8, "WAVE", 4); + memcpy(fHeadBuf+12, "fmt ", 4); + memcpy(fHeadBuf+16, &fmtSize, 4); + memcpy(fHeadBuf+20, &fmt, 2); /* format */ + memcpy(fHeadBuf+22, &fHeader.fNumChannels, 2); + memcpy(fHeadBuf+24, &fHeader.fNumSamplesPerSec, 4); + memcpy(fHeadBuf+28, &fHeader.fAvgBytesPerSec, 4); + memcpy(fHeadBuf+32, &fHeader.fBlockAlign, 4); + memcpy(fHeadBuf+34, &fHeader.fBitsPerSample, 2); + memcpy(fHeadBuf+36, "fact", 4); + memcpy(fHeadBuf+40, &factsize, 4); + memcpy(fHeadBuf+44, &factdata, 4); + memcpy(fHeadBuf+48, "data", 4); + memcpy(fHeadBuf+52, &fDataSize, 4); +} + +bool plOGGCodec::ReadFromHeader(int numBytes, void *data) +{ + if(fCurHeaderPos < 56) + { + memcpy(data, fHeadBuf+fCurHeaderPos, numBytes); + fCurHeaderPos += numBytes; + return true; + } + return false; +} + +void plOGGCodec::IOpen( const char *path, plAudioCore::ChannelSelect whichChan ) +{ + hsAssert( path != nil, "Invalid path specified in plOGGCodec reader" ); + + // plNetClientApp::StaticDebugMsg("Ogg Open %s, t=%f, start", path, hsTimer::GetSeconds()); + + strncpy( fFilename, path, sizeof( fFilename ) ); + fWhichChannel = whichChan; + + /// Open the file as a plain binary stream + fFileHandle = fopen( path, "rb" ); + if( fFileHandle != nil ) + { + /// Create the OGG data struct + fOggFile = TRACKED_NEW OggVorbis_File; + + /// Open the OGG decompressor + if( ov_open( fFileHandle, fOggFile, NULL, 0 ) < 0 ) + { + IError( "Unable to open OGG source file" ); + return; + } + + /// Construct some header info from the ogg info + vorbis_info *vInfo = ov_info( fOggFile, -1 ); + + fHeader.fFormatTag = 1; + fHeader.fNumChannels = vInfo->channels; + fHeader.fNumSamplesPerSec = vInfo->rate; + + // Funny thing about the bits per sample: we get to CHOOSE. Go figure! + fHeader.fBitsPerSample = ( fDecodeFormat == k8bitUnsigned ) ? 8 : 16; + + // Why WAV files hold this info when it can be calculated is beyond me... + fHeader.fBlockAlign = ( fHeader.fBitsPerSample * fHeader.fNumChannels ) >> 3; + fHeader.fAvgBytesPerSec = fHeader.fNumSamplesPerSec * fHeader.fBlockAlign; + + + /// The size in bytes of our PCM data stream + /// Note: OGG sometimes seems to be off by 1 sample, which causes our reads to suck + /// because we end up waiting for 1 more sample than we actually have. So, on the + /// assumption that OGG is just slightly wrong sometimes, we just subtract 1 sample + /// from what it tells us. As Brice put it, who's going to miss 1/40,000'ths of a second? + fDataSize = (UInt32)(( ov_pcm_total( fOggFile, -1 ) - 1 ) * fHeader.fBlockAlign); + + /// Channel select + if( fWhichChannel != plAudioCore::kAll ) + { + fChannelAdjust = 2; + fChannelOffset = ( fWhichChannel == plAudioCore::kLeft ) ? 0 : 1; + } + else + { + fChannelAdjust = 1; + fChannelOffset = 0; + } + + + /// Construct our fake header for channel adjustment + fFakeHeader = fHeader; + fFakeHeader.fAvgBytesPerSec /= fChannelAdjust; + fFakeHeader.fNumChannels /= (UInt16)fChannelAdjust; + fFakeHeader.fBlockAlign /= (UInt16)fChannelAdjust; + + SetPosition( 0 ); + } +// plNetClientApp::StaticDebugMsg("Ogg Open %s, t=%f, end", path, hsTimer::GetSeconds()); +} + +plOGGCodec::~plOGGCodec() +{ + Close(); +} + +void plOGGCodec::Close( void ) +{ + // plNetClientApp::StaticDebugMsg("Ogg Close, t=%f, start", hsTimer::GetSeconds()); + FREE(fHeadBuf); + fHeadBuf = nil; + if( fOggFile != nil ) + { + ov_clear( fOggFile ); + DEL(fOggFile); + fOggFile = nil; + } + + if( fFileHandle != nil ) + { + fclose( fFileHandle ); + fFileHandle = nil; + } + // plNetClientApp::StaticDebugMsg("Ogg Close, t=%f, end", hsTimer::GetSeconds()); +} + +void plOGGCodec::IError( const char *msg ) +{ + hsAssert( false, msg ); + Close(); +} + +plWAVHeader &plOGGCodec::GetHeader( void ) +{ + hsAssert( IsValid(), "GetHeader() called on an invalid OGG file" ); + + return fFakeHeader; +} + +float plOGGCodec::GetLengthInSecs( void ) +{ + hsAssert( IsValid(), "GetLengthInSecs() called on an invalid OGG file" ); + + // Just query ogg directly...starting to see how cool ogg is yet? + return (float)ov_time_total( fOggFile, -1 ); +} + +hsBool plOGGCodec::SetPosition( UInt32 numBytes ) +{ + hsAssert( IsValid(), "GetHeader() called on an invalid OGG file" ); + + + if( !ov_seekable( fOggFile ) ) + { + hsAssert( false, "Trying to set position on an unseekable OGG stream!" ); + return false; + } + + // The numBytes position is in uncompressed space and should be sample-aligned anyway, + // so this should be just fine here. + ogg_int64_t newSample = ( numBytes / (fFakeHeader.fBlockAlign * fChannelAdjust) ); + + // Now please note how freaking easy it is here to do accurate or fast seeking... + // Also note that if we're doing our channel extraction, we MUST do it the accurate way + if( ( fDecodeFlags & kFastSeeking ) && fChannelAdjust == 1 ) + { + if( ov_pcm_seek_page( fOggFile, newSample ) != 0 ) + { + IError( "Unable to seek OGG stream" ); + return false; + } + } + else + { + if( ov_pcm_seek( fOggFile, newSample ) != 0 ) + { + IError( "Unable to seek OGG stream" ); + return false; + } + } + return true; +} + +hsBool plOGGCodec::Read( UInt32 numBytes, void *buffer ) +{ + hsAssert( IsValid(), "GetHeader() called on an invalid OGG file" ); +// plNetClientApp::StaticDebugMsg("Ogg Read, t=%f, start", hsTimer::GetSeconds()); + + int bytesPerSample = ( fDecodeFormat == k16bitSigned ) ? 2 : 1; + int isSigned = ( fDecodeFormat == k16bitSigned ) ? 1 : 0; + int currSection; + + if( fWhichChannel == plAudioCore::kAll ) + { + // Easy, just a straight read + char *uBuffer = (char *)buffer; + + while( numBytes > 0 ) + { + // Supposedly we should pay attention to currSection in case of bitrate changes, + // but hopefully we'll never have those.... + + long bytesRead = ov_read( fOggFile, uBuffer, numBytes, 0, bytesPerSample, isSigned, &currSection ); + + // Since our job is so simple, do some extra error checking + if( bytesRead == OV_HOLE ) + { + IError( "Unable to read from OGG file: missing data" ); + return false; + } + else if( bytesRead == OV_EBADLINK ) + { + IError( "Unable to read from OGG file: corrupt link" ); + return false; + } + else if( bytesRead == 0 ) + { + IError( "Unable to finish reading from OGG file: end of stream" ); + return false; + } + else if( bytesRead < 0 ) + { + IError( "Unable to read from OGG file: unknown error" ); + return false; + } + + numBytes -= bytesRead; + uBuffer += bytesRead; + } + } + else + { + /// Read in 4k chunks and extract + static char trashBuffer[ 4096 ]; + + long toRead, i, thisRead, sampleSize = fFakeHeader.fBlockAlign; + + for( ; numBytes > 0; ) + { + /// Read 4k worth of samples + toRead = ( sizeof( trashBuffer ) < numBytes * fChannelAdjust ) ? sizeof( trashBuffer ) : numBytes * fChannelAdjust; + + + thisRead = ov_read( fOggFile, (char *)trashBuffer, toRead, 0, bytesPerSample, isSigned, &currSection ); + if( thisRead < 0 ) + return false; + + /// Copy every other sample out + int sampleOffset = (fChannelOffset == 1) ? sampleSize : 0; + for (i = 0; i < thisRead; i += sampleSize * 2) + { + memcpy(buffer, &trashBuffer[i + sampleOffset], sampleSize); + buffer = (void*)((UInt8*)buffer + sampleSize); + + numBytes -= sampleSize; + } + } + } + +// plNetClientApp::StaticDebugMsg("Ogg Read, t=%f, end", hsTimer::GetSeconds()); + return true; +} + +UInt32 plOGGCodec::NumBytesLeft( void ) +{ + if(!IsValid()) + { + hsAssert( false, "GetHeader() called on an invalid OGG file" ); + return 0; + } + + return (UInt32)(( fDataSize - ( ov_pcm_tell( fOggFile ) * fHeader.fBlockAlign ) ) / fChannelAdjust); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.h new file mode 100644 index 00000000..0d3db1a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.h @@ -0,0 +1,106 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plOGGCodec - Plasma codec support for the OGG/Vorbis file format. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plOGGCodec_h +#define _plOGGCodec_h + +#include "../plAudioCore/plAudioFileReader.h" + + +//// Class Definition //////////////////////////////////////////////////////// + +struct OggVorbis_File; + +class plOGGCodec : public plAudioFileReader +{ +public: + + plOGGCodec( const char *path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll ); + virtual ~plOGGCodec(); + + enum DecodeFormat + { + k8bitUnsigned, + k16bitSigned + }; + + enum DecodeFlags + { + kFastSeeking = 0x01 + }; + + virtual plWAVHeader &GetHeader( void ); + + virtual void Close( void ); + + virtual UInt32 GetDataSize( void ) { return fDataSize / fChannelAdjust; } + virtual float GetLengthInSecs( void ); + + virtual hsBool SetPosition( UInt32 numBytes ); + virtual hsBool Read( UInt32 numBytes, void *buffer ); + virtual UInt32 NumBytesLeft( void ); + + virtual hsBool IsValid( void ) { return ( fOggFile != nil ) ? true : false; } + + static void SetDecodeFormat( DecodeFormat f ) { fDecodeFormat = f; } + static void SetDecodeFlag( UInt8 flag, hsBool on ) { if( on ) fDecodeFlags |= flag; else fDecodeFlags &= ~flag; } + static UInt8 GetDecodeFlags( void ) { return fDecodeFlags; } + void ResetWaveHeaderRef() { fCurHeaderPos = 0; } + void BuildActualWaveHeader(); + bool ReadFromHeader(int numBytes, void *data); // read from Actual wave header + +protected: + + enum + { + kPCMFormatTag = 1 + }; + + char fFilename[ 512 ]; + FILE *fFileHandle; + OggVorbis_File *fOggFile; + + plWAVHeader fHeader, fFakeHeader; + UInt32 fDataStartPos, fCurrDataPos, fDataSize; + + plAudioCore::ChannelSelect fWhichChannel; + UInt32 fChannelAdjust, fChannelOffset; + + static DecodeFormat fDecodeFormat; + static UInt8 fDecodeFlags; + UInt8 * fHeadBuf; + int fCurHeaderPos; + + void IError( const char *msg ); + void IOpen( const char *path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll ); +}; + +#endif //_plOGGCodec_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSound.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSound.cpp new file mode 100644 index 00000000..92bf0158 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSound.cpp @@ -0,0 +1,1528 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsUtils.h" +#include "hsResMgr.h" +#include "hsTimer.h" +#include "hsGeometry3.h" +#include "hsColorRGBA.h" +#include "plProfile.h" +#include "plgDispatch.h" + +#include "plAudioSystem.h" +#include "plSound.h" +#include "plWin32Sound.h" + +#include "../plAudioCore/plSoundBuffer.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plAudioSysMsg.h" +#include "../pnMessage/plSoundMsg.h" +#include "../plMessage/plListenerMsg.h" +#include "../plIntersect/plSoftVolume.h" +#include "../plStatusLog/plStatusLog.h" +#include "../plPipeline/plPlates.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnNetCommon/plSDLTypes.h" +#include "../plAvatar/plScalarChannel.h" +#include "../plAvatar/plAGModifier.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plAudioInterface.h" + +plProfile_CreateCounterNoReset( "Loaded", "Sound", SoundNumLoaded ); +plProfile_CreateCounterNoReset( "Waiting to Die", "Sound", WaitingToDie ); +plProfile_CreateAsynchTimer( "Sound Load Time", "Sound", SoundLoadTime ); + +plGraphPlate *plSound::fDebugPlate = nil; +plSound *plSound::fCurrDebugPlateSound = nil; +hsBool plSound::fLoadOnDemandFlag = true; +hsBool plSound::fLoadFromDiskOnDemand = true; +unsigned plSound::fIncidentalsPlaying = 0; + +plSound::plSound() : +fPlaying(false), +fActive(false), +fTime(0), +fMaxFalloff(0), +fMinFalloff(0), +fCurrVolume(0.f), +fOuterVol(0), +fOuterCone(360), +fInnerCone(360), +fLength(0.0f), +fDesiredVol(0.f), +fFading(false), +fRegisteredForTime(false), +fMuted(true), +fFadedVolume(0.f), +fSoftRegion(nil), +fSoftOcclusionRegion(nil), +fSoftVolume(0.f), +fCurrFadeParams(nil), +fRegistered(false), +fDistAttenuation(0.f), +fProperties(0), +fNotHighEnoughPriority(false), +fVirtualStartTime(0), +fOwningSceneObject(nil), +fPriority(0), +fType(plSound::kSoundFX), +fQueued(false), +fLoading(false), +fSynchedStartTimeSec(0), +fMaxVolume(0), +fFreeData(false) +{ + plProfile_Inc( SoundNumLoaded ); + f3DPosition.Set( 0.f, 0.f, 0.f ); + f3DVelocity.Set( 0.f, 0.f, 0.f ); + fDataBuffer = nil; + fDataBufferKey = nil; + fPlayOnReactivate = false; + fDataBufferLoaded = false; +} + +plSound::~plSound() +{ + IStopFade( true ); + plProfile_Dec( SoundNumLoaded ); +} + +void plSound::IPrintDbgMessage( const char *msg, hsBool isError ) +{ + static plStatusLog *ourLog = nil; + + // Print to our log file (create it if necessary) + if( ourLog == nil ) + { +// ourLog = plStatusLogMgr::GetInstance().CreateStatusLog( 15, +// "audio.log", plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | plStatusLog::kAlignToTop ); + } + + if( isError ) +// ourLog->AddLineF( plStatusLog::kRed, "ERROR: %s (%s)", msg, GetKey() ? GetKeyName() : "unkeyed" ); + ourLog->AddLineS( "audio.log", plStatusLog::kRed, "ERROR: %s (%s)", msg, GetKey() ? GetKeyName() : "unkeyed" ); + else +// ourLog->AddLineF( "%s (%s)", msg, GetKey() ? GetKeyName() : "unkeyed" ); + ourLog->AddLineS( "audio.log", "%s (%s)", msg, GetKey() ? GetKeyName() : "unkeyed" ); +} + +/////////////////////////////////////////////////////////// +// Called to send more values to the debug plate, assuming this is the right +// sound. Should be called every time any of the values change, which means +// the best place is inside ISetActualVolume(). Since that's a pure virtual, +// it makes the placement of the call a bit annoying, but oh well. +void plSound::IUpdateDebugPlate( void ) +{ + if( this == fCurrDebugPlateSound ) + { + if( fDebugPlate == nil ) + { + plPlateManager::Instance().CreateGraphPlate( &fDebugPlate ); + fDebugPlate->SetSize( 0.50, 0.25 ); + fDebugPlate->SetPosition( -0.5, 0 ); + fDebugPlate->SetDataRange( 0, 100, 100 ); + fDebugPlate->SetColors( 0x80202000 ); + fDebugPlate->SetTitle( (char *)GetKeyName() ); // Bleah + fDebugPlate->SetLabelText( "Desired", "Curr", "Soft", "Dist" ); + } + + fDebugPlate->SetVisible( true ); + fDebugPlate->AddData( (Int32)( fDesiredVol * 100.f ), + (Int32)( fCurrVolume * 100.f ), + (Int32)( fSoftVolume * 100.f ), + (Int32)( fDistAttenuation * 100.f ) ); + } +} + +void plSound::SetCurrDebugPlate( const plKey soundKey ) +{ + if( soundKey == nil ) + { + fCurrDebugPlateSound = nil; + if( fDebugPlate != nil ) + fDebugPlate->SetVisible( false ); + } + else + { + fCurrDebugPlateSound = plSound::ConvertNoRef( soundKey->GetObjectPtr() ); + if( fDebugPlate != nil ) + { + fDebugPlate->ClearData(); + fDebugPlate->SetVisible( true ); + fDebugPlate->SetTitle( (char *)fCurrDebugPlateSound->GetKeyName() ); // Bleah + } + } +} + +///////////////////////////////////////////////////////////////////// +// We don't keep track of the current time we should be at, but rather the +// time we started at. Since we're calling SetTime(), we should adjust the +// start time to be accurate for the time we want to be at now. Note: we +// don't actually move the buffer position unless it's loaded, since we +// don't want to force a load on a buffer just from a SetTime() call. +void plSound::SetTime( double t ) +{ + fVirtualStartTime = hsTimer::GetSysSeconds() - t; + + if( IActuallyLoaded() ) + ISetActualTime( t ); +} + +// Support for Fast forward responder +void plSound::FastForwardPlay() +{ + if(fProperties & kPropLooping) + { + Play(); + } +} + +void plSound::FastForwardToggle() +{ + if(fPlaying == true) + { + Stop(); + return; + } + FastForwardPlay(); +} + +//////////////////////////////////////////////////////////////////////// +// Our basic play function. Marks the sound as playing, and if we're actually +// allowed to play, will actually start the sound playing as well. +void plSound::Play() +{ + if(fLoading) // if we are loading there is no reason to do this. Play will be called, by Update(), once the data is loaded and this floag is set to false + return; + + if( !fActive ) + { + // We're not active, so we can't play, but mark to make sure we'll play once we do get activated + fPlayOnReactivate = true; + return; + } + + fPlaying = true; + + if(IPreLoadBuffer(true) == plSoundBuffer::kPending) + { + return; + } + + fVirtualStartTime = hsTimer::GetSysSeconds(); // When we "started", even if we really don't start + + // if the sound system is not active do a fake play so callbacks get sent + if(!plgAudioSys::Active()) + { + // Do the (fake) actual play + IActuallyPlay(); + } + if( IWillBeAbleToPlay() ) + { + IRefreshParams(); + + if( fFadeInParams.fLengthInSecs > 0 ) + { + IStartFade( &fFadeInParams); + } + else + { + // we're NOT fading!!!! + if( fFading ) + IStopFade(); + SetVolume( fDesiredVol ); + } + + // Do the actual play + IActuallyPlay(); + } +} + +void plSound::SynchedPlay(unsigned bytes ) +{ + if( fFading ) + IStopFade(); + if(fLoading) // the sound is loading, it will be played when loading is finished + return; + + if( !fActive ) + { + // We're not active, so we can't play, but mark to make sure we'll play once we do get activated + fPlayOnReactivate = true; + return; + } + + // Mark as playing, since we'll be calling ITryPlay() directly + fPlaying = true; + fPlayOnReactivate = false; + + if( IWillBeAbleToPlay() ) + { + SetStartPos(bytes); + Play(); + } +} + +///////////////////////////////////////////////////////////////// +// Used for synching play state. The state only knows that we're playing +// and what time we started at, so we use that to compute what time we should +// be at and update. Note that we also set our virtual start time to what +// we're given, NOT the current time, 'cause, well, duh, that should be our +// start time! +// So think of it as "Play() but act as if you started at *this* time"... +void plSound::SynchedPlay( hsScalar virtualStartTime ) +{ + if( fFading ) + IStopFade(); + + ISynchedPlay( virtualStartTime ); +} + +//////////////////////////////////////////////////////////////// +// Only want to do the fade hack when somebody outside synch()s us. +void plSound::ISynchedPlay( double virtualStartTime ) +{ + if(fLoading) // the sound is loading, it will be played when loading is finished + return; + + // Store our start time + fVirtualStartTime = virtualStartTime; + + if( !fActive ) + { + // We're not active, so we can't play, but mark to make sure we'll play once we do get activated + fPlayOnReactivate = true; + return; + } + + // Mark as playing, since we'll be calling ITryPlay() directly + fPlaying = true; + fPlayOnReactivate = false; + + // Do da synch, which will start us playing + if( IWillBeAbleToPlay() ) + { + ISynchToStartTime(); + } +} + +/////////////////////////////////////////////////////////// +// Takes the virtual start time and sets us to the real time we should be at, +// then starts us playing via ITryPlay(). +void plSound::ISynchToStartTime( void ) +{ + if( !plgAudioSys::Active() ) + return; + + // We don't want to do this until we're actually loaded, since that's when we'll know our + // REAL length (thanks to the inaccuracies of WMA compression) +// LoadSound( IsPropertySet( kPropIs3DSound ) ); + // Haha, the GetLength() call will do this for us + + // Calculate what time we should be at + double deltaTime = hsTimer::GetSysSeconds() - fVirtualStartTime; + double length = GetLength(); + + if( deltaTime > length || deltaTime < 0 ) + { + // Hmm, our time went past the length of sound, so handle that + if( IsPropertySet( kPropLooping ) ) + { + if( length <= 0 ) + deltaTime = 0; // Error, attempt to recover + else if( deltaTime < 0 ) + { + int numWholeParts = (int)( -deltaTime / length ); + deltaTime += length * ( numWholeParts + 1 ); + } + else + { + int numWholeParts = (int)( deltaTime / length ); + deltaTime -= length * (double)numWholeParts; + } + + //ISetActualTime( deltaTime ); + Play(); + } + else + // We already played and stopped virtually, so really mark us as stopped + Stop(); + } + else + { + // Easy 'nuf... + //ISetActualTime( deltaTime ); + Play(); + } +} + +void plSound::SetPosition(const hsPoint3 pos) +{ + f3DPosition = pos; +} + +void plSound::SetVelocity(const hsVector3 vel) +{ + f3DVelocity = vel; +} + +hsPoint3 plSound::GetPosition( void ) const +{ + return f3DPosition; +} + +hsVector3 plSound::GetVelocity( void ) const +{ + return f3DVelocity; +} + +void plSound::SetMin(const int m) +{ + fMinFalloff = m; +} + +void plSound::SetMax(const int m) +{ + fMaxFalloff = m; +} + +void plSound::SetOuterVolume(const int v) +{ + fOuterVol = v; +} + +void plSound::SetConeOrientation( hsScalar x, hsScalar y, hsScalar z ) +{ + fConeOrientation.Set( x, y, z ); +} + +void plSound::SetConeAngles( int inner, int outer ) +{ + fOuterCone = outer; + fInnerCone = inner; +} + +int plSound::GetMin() const +{ + return fMinFalloff; +} + +int plSound::GetMax() const +{ + return fMaxFalloff; +} + +void plSound::SetVolume(const float v) +{ + fDesiredVol = v; + + if( !fMuted && !fFading ) + fCurrVolume = fDesiredVol; + + RefreshVolume(); +} + +void plSound::RefreshVolume( void ) +{ + this->ISetActualVolume( fCurrVolume ); +} + +void plSound::SetMuted( hsBool muted ) +{ + if( muted != fMuted ) + { + fMuted = muted; + if( fMuted ) + fCurrVolume = 0.f; + else if( !fFading ) + fCurrVolume = fDesiredVol; + + RefreshVolume(); + } +} + +void plSound::IRefreshParams( void ) +{ + SetMax( fMaxFalloff ); + SetMin( fMinFalloff ); + SetOuterVolume( fOuterVol ); + SetConeAngles( fInnerCone, fOuterCone ); + SetConeOrientation( fConeOrientation.fX, fConeOrientation.fY, fConeOrientation.fZ); + SetPosition( f3DPosition ); + SetVelocity( f3DVelocity ); +} + +//////////////////////////////////////////////////////////////////////// +// The public interface to stopping, which also synchs the state with the +// server. +void plSound::Stop( void ) +{ + fPlaying = false; + + // if the audio data is loading while stop is called we need to make sure the sounds doesn't play, and the data is unloaded. + fPlayOnReactivate = false; + fFreeData = true; + + // Do we have an ending fade? + if( fFadeOutParams.fLengthInSecs > 0 && !plgAudioSys::IsRestarting() ) + { + IStartFade( &fFadeOutParams ); + } + else + { + if( fFading ) + IStopFade(); + + fCurrVolume = 0.f; + ISetActualVolume( fCurrVolume ); + IActuallyStop(); + } + if(fPlayWhenLoaded) + { + fPlayWhenLoaded = false; + } +} + +void plSound::IActuallyStop( void ) +{ + if( fLoadOnDemandFlag && !IsPropertySet( kPropDisableLOD ) && !IsPropertySet( kPropLoadOnlyOnCall ) ) + { + // If we're loading on demand, we want to unload on stop + IFreeBuffers(); + } +} + +void plSound::Update() +{ + if(fLoading) + { + plSoundBuffer::ELoadReturnVal retVal = IPreLoadBuffer(fPlayWhenLoaded); + + if(retVal == plSoundBuffer::kError) + { + fLoading = false; + fPlayWhenLoaded = false; + } + if(retVal == plSoundBuffer::kSuccess) + { + fLoading = false; + if(fPlayWhenLoaded) + Play(); + fPlayWhenLoaded = false; + + // ensure the sound data is released if the sound object was stopped while the audio data was being loaded. + if(fFreeData) + { + fFreeData = false; + FreeSoundData(); + } + } + } +} + +hsScalar plSound::IGetChannelVolume( void ) const +{ + hsScalar channelVol = plgAudioSys::GetChannelVolume( (plgAudioSys::ASChannel)fType ); + + // if not using hardware acceleration then apply 2D/3D bias to non 3D sounds + if( !plgAudioSys::Hardware() && !IsPropertySet( kPropIs3DSound ) ) + channelVol *= plgAudioSys::Get2D3Dbias(); + + if( IsPropertySet( kPropDontFade ) ) + return channelVol; + + return channelVol * plgAudioSys::GetGlobalFadeVolume(); +} + +void plSound::IStartFade( plFadeParams *params, hsScalar offsetIntoFade ) +{ + fFading = true; + + if( params == &fFadeOutParams ) + { + fFadeOutParams.fVolStart = fCurrVolume; + fFadeOutParams.fVolEnd = fFadedVolume; + fCurrFadeParams = &fFadeOutParams; + } + else if( params == &fFadeInParams ) + { + fFadeInParams.fVolStart = fCurrVolume; // Hopefully, we got to fFadedVolume, but maybe not + fFadeInParams.fVolEnd = fDesiredVol; + fCurrFadeParams = &fFadeInParams; + plStatusLog::AddLineS("audio.log", "Fading in %s", GetKeyName()); + } + else + fCurrFadeParams = params; + + fCurrFadeParams->fCurrTime = offsetIntoFade; + ISetActualVolume( fCurrFadeParams->InterpValue() ); + + if( !fRegisteredForTime ) + { + plgDispatch::Dispatch()->RegisterForExactType( plTimeMsg::Index(), GetKey() ); + fRegisteredForTime = true; + } +} + +void plSound::IStopFade( hsBool shuttingDown, hsBool SetVolEnd) +{ + if( fCurrFadeParams != nil ) + { + if( fCurrFadeParams == &fCoolSoftVolumeTrickParams ) + { + plProfile_Dec( WaitingToDie ); + } + + // This can cause problems if we've exited a soft region and are doing a soft volume fade. + // If the camera pops back into the region of this particular sound this will cause the soft volume to be zero, + // therefore not allowing the sound to play until the listener moves again(triggering another softsound update). + // So if this function is called from UpdateSoftSounds this will not be performed + if(SetVolEnd) + { + if( fCurrFadeParams->fFadeSoftVol ) + fSoftVolume = fCurrFadeParams->fVolEnd; + else + fCurrVolume = fCurrFadeParams->fVolEnd; + } + + if( !shuttingDown ) + ISetActualVolume( fCurrVolume ); + + fCurrFadeParams->fCurrTime = -1.f; + } + + fFading = false; + fCurrFadeParams = nil; + + // Fade done, unregister for time message + if( fRegisteredForTime ) + { + plgDispatch::Dispatch()->UnRegisterForExactType( plTimeMsg::Index(), GetKey() ); + fRegisteredForTime = false; + } +} + +hsBool plSound::MsgReceive( plMessage* pMsg ) +{ + plTimeMsg *time = plTimeMsg::ConvertNoRef( pMsg ); + if( time != nil ) + { + /// Time message for handling fade ins/outs + if( fCurrFadeParams == nil ) + return true; + + fCurrFadeParams->fCurrTime += time->DelSeconds(); + + if( fCurrFadeParams->fCurrTime >= fCurrFadeParams->fLengthInSecs ) + { + if( fCurrFadeParams->fFadeSoftVol ) + fSoftVolume = fCurrFadeParams->fVolEnd; + else + fCurrVolume = fCurrFadeParams->fVolEnd; + ISetActualVolume( fCurrVolume ); + fCurrFadeParams->fCurrTime = -1.f; + + // Fade done, unregister for time message + if( fRegisteredForTime ) + { + plgDispatch::Dispatch()->UnRegisterForExactType( plTimeMsg::Index(), GetKey() ); + fRegisteredForTime = false; + } + + // Note: if we're done, and we were fading out, we need to STOP + if( fCurrFadeParams->fStopWhenDone ) + { + // REALLY STOP + IActuallyStop(); + } + + if( fCurrFadeParams == &fCoolSoftVolumeTrickParams ) + { + plProfile_Dec( WaitingToDie ); + } + + // Done with this one! + fCurrFadeParams = nil; + fFading = false; + } + else + { + // Gotta interp + if( fCurrFadeParams->fFadeSoftVol ) + fSoftVolume = fCurrFadeParams->InterpValue(); + else + fCurrVolume = fCurrFadeParams->InterpValue(); + + ISetActualVolume( fCurrVolume ); + } + + return true; + } + + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef( pMsg ); + if( refMsg ) + { + if( refMsg->fType == kRefSoftVolume ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + ISetSoftRegion( plSoftVolume::ConvertNoRef(refMsg->GetRef()) ); + return true; + } + else if( refMsg->GetContext() & (plRefMsg::kOnRemove | plRefMsg::kOnDestroy) ) + { + ISetSoftRegion( nil ); + return true; + } + } + else if( refMsg->fType == kRefSoftOcclusionRegion ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + ISetSoftOcclusionRegion( plSoftVolume::ConvertNoRef( refMsg->GetRef() ) ); + return true; + } + else if( refMsg->GetContext() & (plRefMsg::kOnRemove | plRefMsg::kOnDestroy) ) + { + ISetSoftOcclusionRegion( nil ); + return true; + } + } + else if( refMsg->fType == kRefDataBuffer ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fDataBuffer = plSoundBuffer::ConvertNoRef( refMsg->GetRef() ); + SetLength( fDataBuffer->GetDataLengthInSecs() ); + } + else + fDataBuffer = nil; + + return true; + } + else if( refMsg->fType == kRefParentSceneObject ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fOwningSceneObject = plSceneObject::ConvertNoRef( refMsg->GetRef() ); + else + fOwningSceneObject = nil; + + return true; + } + } + + plSoundMsg *pSoundMsg = plSoundMsg::ConvertNoRef( pMsg ); + if( pSoundMsg != nil ) + { + if( pSoundMsg->Cmd( plSoundMsg::kAddCallbacks ) ) + { + AddCallbacks( pSoundMsg ); + return true; + } + else if( pSoundMsg->Cmd( plSoundMsg::kRemoveCallbacks ) ) + { + RemoveCallbacks( pSoundMsg ); + return true; + } + return false; + } + + plListenerMsg *listenMsg = plListenerMsg::ConvertNoRef( pMsg ); + if( listenMsg != nil ) + { + if( fSoftOcclusionRegion != nil ) + { + // The EAX settings have 0 as the start value and 1 as the end, and since start + // translates to "inside the soft region", it's reversed of what the region gives us + fEAXSettings.SetOcclusionSoftValue( 1.f - fSoftOcclusionRegion->GetListenerStrength() ); + IRefreshEAXSettings(); + } + return true; + } + + return plSynchedObject::MsgReceive( pMsg ); +} + +void plSound::ForceLoad() +{ + if( !IsPropertySet( kPropLoadOnlyOnCall ) ) + return; + + LoadSound( IsPropertySet( kPropIs3DSound ) ); +} + +void plSound::ForceUnload( void ) +{ + if( !IsPropertySet( kPropLoadOnlyOnCall ) ) + return; + + Stop(); + IFreeBuffers(); +} + +bool plSound::ILoadDataBuffer( void ) +{ + if(!fDataBufferLoaded) + { + plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->RefObject(); + if(!buffer) + { + hsAssert(false, "unable to load sound buffer"); + plStatusLog::AddLineS("audio.log", "Unable to load sound buffer: %s", GetKeyName()); + return false; + } + SetLength( buffer->GetDataLengthInSecs() ); + fDataBufferLoaded = true; + } + return true; +} + +void plSound::FreeSoundData() +{ + if(!fDataBufferKey) return; // for plugins + plSoundBuffer *buffer = (plSoundBuffer *) fDataBufferKey->ObjectIsLoaded(); + if(buffer) + { + buffer->UnLoad(); + } +} + +void plSound::IUnloadDataBuffer( void ) +{ + if(fDataBufferLoaded) + { + fDataBufferLoaded = false; + fDataBufferKey->UnRefObject(); + } +} + +///////////////////////////////////////////////////////////////////////// +// calling preload will cause the sound to play once loaded +plSoundBuffer::ELoadReturnVal plSound::IPreLoadBuffer( hsBool playWhenLoaded, hsBool isIncidental /* = false */ ) +{ + if(!ILoadDataBuffer()) + { + return plSoundBuffer::kError; + } + + plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded(); + + if(buffer && buffer->IsValid() ) + { + plProfile_BeginTiming( SoundLoadTime ); + plSoundBuffer::ELoadReturnVal retVal = buffer->AsyncLoad(buffer->HasFlag(plSoundBuffer::kStreamCompressed) ? plAudioFileReader::kStreamNative : plAudioFileReader::kStreamWAV); + if(retVal == plSoundBuffer::kPending) + { + fPlayWhenLoaded = playWhenLoaded; + fLoading = true; + } + + plProfile_EndTiming( SoundLoadTime ); + + return retVal; + } + else + { + return plSoundBuffer::kError; + } +} + +const char *plSound::GetFileName( void ) const +{ + if(fDataBufferKey->ObjectIsLoaded()) + { + return ((plSoundBuffer *)fDataBufferKey->ObjectIsLoaded())->GetFileName(); + } + + return nil; +} + +///////////////////////////////////////////////////////////////////////// +// WARNING: to do this right, we have to load the data buffer in, which could +// force an early load of the sound if you're just calling this on a whim. So +// it's best to call this right before you're going to load it anyway (or +// when it's already loaded). +// Note that if we already set the length (like at export time), we never need +// to load the sound, so the optimization at export time is all ready to plug- +// and-play... +double plSound::GetLength( void ) +{ + if( ( (double)fLength == 0.f ) ) + ILoadDataBuffer(); + + return fLength; +} + +void plSound::ISetSoftRegion( plSoftVolume *region ) +{ + /// We're either registering or unregistering + if( fSoftRegion == nil && region != nil ) + RegisterOnAudioSys(); + else if( fSoftRegion != nil && region == nil ) + UnregisterOnAudioSys(); + + fSoftRegion = region; + fSoftVolume = 0.f; // Set to zero until we can get our processing call +} + +void plSound::ISetSoftOcclusionRegion( plSoftVolume *region ) +{ + /// We're either registering or unregistering for listener messages + if( fSoftOcclusionRegion == nil && region != nil ) + { + plgDispatch::Dispatch()->RegisterForExactType( plListenerMsg::Index(), GetKey() ); + } + else if( fSoftOcclusionRegion != nil && region == nil ) + { + plgDispatch::Dispatch()->UnRegisterForExactType( plListenerMsg::Index(), GetKey() ); + } + + fSoftOcclusionRegion = region; +} + +///////////////////////////////////////////////////////////////////////// +// This function calculates our new softVolume value. Used both to update the +// said value and so the audio system can rank us in importance. +hsScalar plSound::CalcSoftVolume( hsBool enable, hsScalar distToListenerSquared ) +{ + // Do distance-based attenuation ourselves +#if MCN_HACK_OUR_ATTEN + if( IsPropertySet( kPropIs3DSound ) ) + { + hsScalar minDist = (hsScalar)GetMin(); + if( distToListenerSquared <= minDist * minDist ) + { + fDistAttenuation = 1.f; + } + else + { + hsScalar maxDist = (hsScalar)GetMax(); + if( distToListenerSquared >= maxDist * maxDist ) + { + fDistAttenuation = 0.f; + } + else + { + hsScalar d = (hsScalar)sqrt( distToListenerSquared ); + fDistAttenuation = minDist / d; + + // The following line ramps it to 0 at the maxDistance. Kinda klunky, but good for now I guess... +// fDistAttenuation *= 1.f - ( 1.f / ( maxDist - minDist ) ) * ( d - minDist ); + } + } + } + else + fDistAttenuation = 1.f; +#endif + // At the last 50% of our distance attenuation (squared, so it really is farther than that), + // ramp down to 0 so we don't get annoying popping when we stop stuff + if( IsPropertySet( kPropIs3DSound ) ) + { + hsScalar maxDistSquared = (hsScalar)( GetMax() * GetMax() ); + hsScalar distToStartSquared = (hsScalar)(maxDistSquared * 0.50); + + if( maxDistSquared < 0.f ) // Happens when the max distance is REALLY big + { + maxDistSquared = distToListenerSquared + 1.f; // :) + distToStartSquared = maxDistSquared; + } + + if( distToListenerSquared > maxDistSquared ) + fDistAttenuation = 0.f; + else if( distToListenerSquared > distToStartSquared ) + fDistAttenuation = ( maxDistSquared - distToListenerSquared ) / ( maxDistSquared - distToStartSquared ); + else + fDistAttenuation = 1.f; + + fDistToListenerSquared = distToListenerSquared; + } + else + { + fDistAttenuation = 1.f; + fDistToListenerSquared = 0.f; + } + + // Attenuate based on our soft region, if we have one + if( !enable ) + // We apparently don't know jack. Let the audioSystem's decision rule + fSoftVolume = 0.f; + else if( fSoftRegion != nil ) + fSoftVolume = fSoftRegion->GetListenerStrength(); + else + fSoftVolume = 1.f; + + return fSoftVolume; +} + +///////////////////////////////////////////////////////////////////////// +// Wee function for the audio system. This basically returns the effective +// current volume of this sound. Useful for doing things like ranking all +// sounds based on volume. +hsScalar plSound::GetVolumeRank( void ) +{ + if( !IsPlaying() && !this->IActuallyPlaying() ) + return 0.f; + + hsScalar rank = fSoftVolume * fDesiredVol; + + if( IsPropertySet( kPropIs3DSound ) ) + { + hsScalar minDistSquared = (hsScalar)( GetMin() * GetMin() ); + hsScalar maxDistSquared = (hsScalar) (GetMax() * GetMax()); + hsPoint3 listenerPos = plgAudioSys::Sys()->GetCurrListenerPos(); + if( fDistToListenerSquared > minDistSquared ) + { + hsScalar diff = maxDistSquared - minDistSquared; + rank *= fabs((fDistToListenerSquared - maxDistSquared)) / diff; + } + } + + return rank; +} + +///////////////////////////////////////////////////////////////////////// +// Tests to see whether, if we try to play this sound now, it'll actually +// be able to play. Takes into account whether the sound is within range +// of the listener and the current soft region value. +hsBool plSound::IWillBeAbleToPlay( void ) +{ + if( fSoftVolume == 0.f ) + return false; + + return IsWithinRange( plgAudioSys::Sys()->GetCurrListenerPos(), nil ); +} + +///////////////////////////////////////////////////////////////////////// +// Tests to see whether this sound is within range of the position given, +// ignoring soft volumes. +hsBool plSound::IsWithinRange( const hsPoint3 &listenerPos, hsScalar *distSquared ) +{ + if( !IsPropertySet( plSound::kPropIs3DSound ) ) + { + if( distSquared != nil ) + *distSquared = 1.f; + return true; + } + + hsVector3 distance; + hsPoint3 soundPos = GetPosition(); + + distance.Set( &listenerPos, &soundPos ); + + if( distSquared != nil ) + *distSquared = distance.MagnitudeSquared(); + + if( GetMax() == 1000000000 ) + return true; + + hsScalar soundRadius = (hsScalar)( GetMax() * GetMax() ); + + return ( distance.MagnitudeSquared() <= soundRadius ) ? true : false; +} + + +//// //////////////////////////////////////////////////////// +// Once the soft volume is calculated and our rank is computed, we can +// decide whether to actually enable or not. +// Note: we might have been "enabled" by our Calc call, but the ranking +// still could have disabled us, so we have to specify again whether +// we're enabled. +// Note: if we KNOW we're disabling this sound (out of range), Calc() doesn't +// have to be called at all, and we can simply call this function with +// enable = false. +void plSound::UpdateSoftVolume( hsBool enable, hsBool firstTime ) +{ + fNotHighEnoughPriority = !enable; + + // Don't do any of this special stuff that follows if we're not supposed to be playing + if( IsPlaying() ) + { + if( fSoftVolume * fDistAttenuation > 0.f && !fNotHighEnoughPriority ) + { + if( fCurrFadeParams == &fCoolSoftVolumeTrickParams ) + { + // Stop the fade, but since we are updating the softvolume with the intention of being audible + // tell StopFade not to set the soft volume to zero. + IStopFade(false, false); + } + if( !IActuallyPlaying() ) + { + // Must've been stopped from being out of range. Start up again... + + // Synch up to our start time. + // If this sound is auto starting and is background music, get the current time so we don't start + // with the play cursor already into the piece. + if(IsPropertySet(kPropAutoStart) && fType == kBackgroundMusic) fVirtualStartTime = hsTimer::GetSysSeconds(); + ISynchedPlay( fVirtualStartTime ); + } + } + else if( fCurrFadeParams != &fCoolSoftVolumeTrickParams && IActuallyPlaying() ) + { + // Start our special trick, courtesy of Brice. Basically, we don't + // stop the sound immediately, but rather let it get to the end of + // the sound and loop once more. This way, if we go away and come back soon + // enough, it will be continuing as we expect it to, but if we wait long enough, + // it'll stop, saving processing time. + + // Note: we just do it as a fade because it makes it easier on us that way! + fCoolSoftVolumeTrickParams.fCurrTime = 0.f; + fCoolSoftVolumeTrickParams.fLengthInSecs = firstTime ? 0.f : (hsScalar)fLength + ( (hsScalar)fLength - (hsScalar)GetTime() ); + fCoolSoftVolumeTrickParams.fStopWhenDone = true; + fCoolSoftVolumeTrickParams.fFadeSoftVol = true; + fCoolSoftVolumeTrickParams.fType = plFadeParams::kLinear; + fCoolSoftVolumeTrickParams.fVolStart = fSoftVolume; // Don't actually change the volume this way + fCoolSoftVolumeTrickParams.fVolEnd = 0.f; + + IStartFade( &fCoolSoftVolumeTrickParams ); + + plProfile_Inc( WaitingToDie ); + } + } + + RefreshVolume(); +} + +///////////////////////////////////////////////////////////////////////// +// Returns the current volume, attenuated +hsScalar plSound::QueryCurrVolume( void ) const +{ + return IAttenuateActualVolume( fCurrVolume ) * IGetChannelVolume(); +} + +///////////////////////////////////////////////////////////////////////// +// Used by ISetActualVolume(). Does the final attenuation on a volume before +// sending it to the sound processing. Only does soft regions for now. +hsScalar plSound::IAttenuateActualVolume( hsScalar volume ) const +{ + if( fNotHighEnoughPriority ) + return 0.f; + + volume *= fSoftVolume; + + if( IsPropertySet( kPropIs3DSound ) ) + volume *= fDistAttenuation; + + return volume; +} + +void plSound::Activate(hsBool forcePlay) +{ + // Our actual state... + fActive = true; + + // re-create the sound state here: + if( forcePlay || fPlayOnReactivate ) + { + ISynchedPlay( hsTimer::GetSysSeconds() ); + fPlayOnReactivate = false; + } + + RegisterOnAudioSys(); + SetMuted(plgAudioSys::IsMuted()); +} + +void plSound::DeActivate( void ) +{ + UnregisterOnAudioSys(); + + if( fActive ) + { + if( IsPlaying() ) + { + Stop(); + fPlayOnReactivate = true; + } + else + fPlayOnReactivate = false; + } + + fActive = false; +} + +///////////////////////////////////////////////////////////////////////// +// Tell the audio system about ourselves. +void plSound::RegisterOnAudioSys( void ) +{ + if( !fRegistered ) + { + plgAudioSys::RegisterSoftSound(GetKey()); + fRegistered = true; + } +} + +///////////////////////////////////////////////////////////////////////// +// Tell the audio system to stop caring about us +void plSound::UnregisterOnAudioSys( void ) +{ + if( fRegistered ) + { + plgAudioSys::UnregisterSoftSound(GetKey()); + fRegistered = false; + } +} + +///////////////////////////////////////////////////////////////////////// +// Called by the audio system when we've been booted off (audio system is +// shutting down). Normally, we should already be shut down, but in case +// we're not, this function makes sure everything is cleaned up before +// the audio system itself shuts down. +void plSound::ForceUnregisterFromAudioSys( void ) +{ + DeActivate(); + fRegistered = false; +} + +void plSound::Read(hsStream* s, hsResMgr* mgr) +{ + plSynchedObject::Read(s, mgr); + + IRead( s, mgr ); + + // If we're autostart, start playing + if( IsPropertySet( kPropAutoStart ) ) + Play(); + + // Make sure we synch or don't synch + if( IsPropertySet( kPropLocalOnly ) ) + SetLocalOnly(true); + + // If we're not playing, but we're going to, and we're going to fade in, + // then our current state is faded out, so set fFading + if( fFadeInParams.fLengthInSecs > 0 && !fPlaying ) + fFading = true; + else if( fFadeInParams.fLengthInSecs <= 0 && !fPlaying ) + fFading = false; + + ILoadDataBuffer(); // make sure our sound buffer is loaded + + // Force load on read + if( !fLoadOnDemandFlag || IsPropertySet( kPropDisableLOD ) ) + { + LoadSound( IsPropertySet( kPropIs3DSound ) ); + } + else + { + // Loading on demand, but we still need the length. But that's ok, we'll get it when we get the fDataBuffer ref. + // But we want to preload the data, so go ahead and do that + if( !fLoadFromDiskOnDemand && !IsPropertySet( kPropLoadOnlyOnCall ) && fPriority <= plgAudioSys::GetPriorityCutoff()) + { + IPreLoadBuffer(false); + } + } +} + +void plSound::Write(hsStream* s, hsResMgr* mgr) +{ + plSynchedObject::Write(s, mgr); + IWrite( s, mgr ); +} + +void plSound::IRead( hsStream *s, hsResMgr *mgr ) +{ + fPlaying = s->ReadBool(); + fVirtualStartTime = hsTimer::GetSysSeconds(); // Need if we're autostart + fTime = s->ReadSwapDouble(); + fMaxFalloff = s->ReadSwap32(); + fMinFalloff = s->ReadSwap32(); + s->ReadSwap( &fCurrVolume ); + s->ReadSwap( &fDesiredVol ); + + /// mcn debugging - Thanks to some of my older sound code, it's possible that a few volumes + /// will come in too large. This will only happen with scenes that were exported with that intermediate + /// code, which should be limited to my test scenes locally. Otherwise, things should be fine. This + /// is to compensate for those bogus files. (The fix is to reset the volume in MAX, since the bogusness + /// is in the range of the volume slider itself). + if( fDesiredVol > 1.f ) + fDesiredVol = 1.f; + if( fCurrVolume > 1.f ) + fCurrVolume = 1.f; + fMaxVolume = fDesiredVol; + + fOuterVol = s->ReadSwap32(); + fInnerCone = s->ReadSwap32(); + fOuterCone = s->ReadSwap32(); + s->ReadSwap( &fFadedVolume ); + s->ReadSwap( &fProperties ); + + fType = s->ReadByte(); + fPriority = s->ReadByte(); + + // HACK FOR OLDER EXPORTERS that thought Auto-start meant set fPlaying true + if( fPlaying ) + SetProperty( kPropAutoStart, true ); + + // Read in fade params + fFadeInParams.Read( s ); + fFadeOutParams.Read( s ); + + // Read in soft volume key + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, 0, kRefSoftVolume ), plRefFlags::kActiveRef ); + + // Read in the data buffer key + fDataBufferKey = mgr->ReadKey( s ); + + // EAX params + fEAXSettings.Read( s ); + + // EAX soft keys + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, 0, kRefSoftOcclusionRegion ), plRefFlags::kActiveRef ); +} + +void plSound::IWrite( hsStream *s, hsResMgr *mgr ) +{ + s->WriteBool(fPlaying); + s->WriteSwapDouble(fTime); + s->WriteSwap32(fMaxFalloff); + s->WriteSwap32(fMinFalloff); + s->WriteSwap( fCurrVolume ); + s->WriteSwap( fDesiredVol ); + s->WriteSwap32(fOuterVol); + s->WriteSwap32(fInnerCone); + s->WriteSwap32(fOuterCone); + s->WriteSwap( fFadedVolume ); + s->WriteSwap( fProperties ); + s->WriteByte( fType ); + s->WriteByte( fPriority ); + + // Write out fade params + fFadeInParams.Write( s ); + fFadeOutParams.Write( s ); + + // Write out soft volume key + mgr->WriteKey( s, fSoftRegion ); + + // Write out data buffer key + if(fDataBuffer) + mgr->WriteKey( s, fDataBuffer->GetKey() ); + else + mgr->WriteKey( s, fDataBufferKey ); + + // EAX params + fEAXSettings.Write( s ); + + // EAX Soft keys + mgr->WriteKey( s, fSoftOcclusionRegion ); +} + +void plSound::plFadeParams::Read( hsStream *s ) +{ + s->ReadSwap( &fLengthInSecs ); + s->ReadSwap( &fVolStart ); + s->ReadSwap( &fVolEnd ); + s->ReadSwap( &fType ); + s->ReadSwap( &fCurrTime ); + s->ReadSwap( &fStopWhenDone ); + s->ReadSwap( &fFadeSoftVol ); +} + +void plSound::plFadeParams::Write( hsStream *s ) +{ + s->WriteSwap( fLengthInSecs ); + s->WriteSwap( fVolStart ); + s->WriteSwap( fVolEnd ); + s->WriteSwap( fType ); + s->WriteSwap( fCurrTime ); + s->WriteSwap( fStopWhenDone ); + s->WriteSwap( fFadeSoftVol ); +} + +hsScalar plSound::plFadeParams::InterpValue( void ) +{ + hsScalar val; + + switch( fType ) + { + case kLinear: + val = ( ( fCurrTime / fLengthInSecs ) * ( fVolEnd - fVolStart ) ) + fVolStart; + break; + case kLogarithmic: + val = fCurrTime / fLengthInSecs; + val = ( ( val * val ) * ( fVolEnd - fVolStart ) ) + fVolStart; + break; + case kExponential: + val = fCurrTime / fLengthInSecs; + val = ( (hsScalar)sqrt( val ) * ( fVolEnd - fVolStart ) ) + fVolStart; + break; + default: + val = 0.f; + } + return val; +} + +void plSound::SetFadeInEffect( plSound::plFadeParams::Type type, hsScalar length ) +{ + fFadeInParams.fLengthInSecs = length; + fFadeInParams.fType = type; + fFadeInParams.fVolStart = 0.f; // Will be set when activated + fFadeInParams.fVolEnd = 1.f; // Will be set when activated + fFadeInParams.fCurrTime = -1.f; + + // If we're not playing, but we're going to, and we're going to fade in, + // then our current state is faded out, so set fFading + if( fFadeInParams.fLengthInSecs > 0 && !fPlaying ) + fFading = true; + else if( fFadeInParams.fLengthInSecs <= 0 && !fPlaying ) + fFading = false; +} + +void plSound::SetFadeOutEffect( plSound::plFadeParams::Type type, hsScalar length ) +{ + fFadeOutParams.fLengthInSecs = length; + fFadeOutParams.fType = type; + fFadeOutParams.fVolStart = 1.f; // Will be set when activated + fFadeOutParams.fVolEnd = 0.f; // Will be set when activated + fFadeOutParams.fCurrTime = -1.f; + fFadeOutParams.fStopWhenDone = true; +} + +plDrawableSpans* plSound::CreateProxy(const hsMatrix44& l2w, hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + plDrawableSpans* myDraw = addTo; + + if( fOuterCone < 360 ) + { + hsScalar len = (hsScalar)GetMax(); + hsScalar halfAng = hsScalarDegToRad(hsScalar(fInnerCone) * 0.5f); + hsScalar radius = len * tanf(halfAng); + if( fInnerCone < 180 ) + len = -len; + myDraw = plDrawableGenerator::GenerateConicalDrawable( + radius, + len, + mat, + l2w, + true, + &hsColorRGBA().Set(1.f, 0.5f, 0.5f, 1.f), + &idx, + myDraw); + + len = (hsScalar)GetMin(); + halfAng = hsScalarDegToRad(hsScalar(fOuterCone) * 0.5f); + radius = len * tanf(halfAng); + if( fOuterCone < 180 ) + len = -len; + + myDraw = plDrawableGenerator::GenerateConicalDrawable( + radius, + len, + mat, + l2w, + true, + &hsColorRGBA().Set(0.25f, 0.25f, 0.5f, 1.f), + &idx, + myDraw); + } + else + { + myDraw = plDrawableGenerator::GenerateSphericalDrawable( + hsPoint3(0,0,0), + (hsScalar)GetMin(), + mat, + l2w, + true, + &hsColorRGBA().Set(1.f, 0.5f, 0.5f, 1.f), + &idx, + myDraw); + + myDraw = plDrawableGenerator::GenerateSphericalDrawable( + hsPoint3(0,0,0), + (hsScalar)GetMax(), + mat, + l2w, + true, + &hsColorRGBA().Set(0.25f, 0.25f, 0.5f, 1.f), + &idx, + myDraw); + } + return myDraw; +} + + +// call when state has changed +hsBool plSound::DirtySynchState(const char* sdlName /* kSDLSound */, UInt32 sendFlags) +{ + /* + if( sdlName == nil ) + sdlName = kSDLSound; + + if( fOwningSceneObject != nil ) + return fOwningSceneObject->DirtySynchState(sdlName, sendFlags); +*/ + return false; +} + + +////////////////////////////////////////////////////////////////////////////// +//// plSoundVolumeApplicator ///////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +void plSoundVolumeApplicator::IApply( const plAGModifier *mod, double time ) +{ + plScalarChannel *chan = plScalarChannel::ConvertNoRef( fChannel ); + if(chan) + { + hsScalar volume = chan->Value( time ); + + hsScalar digitalVolume = (float)pow( 10.f, volume / 20.f ); + + // Find the audio interface and thus the plSound from it + plSceneObject *so = mod->GetTarget( 0 ); + if( so != nil ) + { + const plAudioInterface *ai = so->GetAudioInterface(); + if( ai != nil && fIndex < ai->GetNumSounds() ) + { + plSound *sound = ai->GetSound( fIndex ); + if( sound != nil ) + { + sound->SetVolume( digitalVolume ); + return; + } + } + } + } +} + +plAGApplicator *plSoundVolumeApplicator::CloneWithChannel( plAGChannel *channel ) +{ + plSoundVolumeApplicator *clone = (plSoundVolumeApplicator *)plAGApplicator::CloneWithChannel( channel ); + clone->fIndex = fIndex; + return clone; +} + +void plSoundVolumeApplicator::Write( hsStream *stream, hsResMgr *mgr ) +{ + plAGApplicator::Write( stream, mgr ); + stream->WriteSwap32( fIndex ); +} + +void plSoundVolumeApplicator::Read( hsStream *s, hsResMgr *mgr ) +{ + plAGApplicator::Read( s, mgr ); + fIndex = s->ReadSwap32(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSound.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSound.h new file mode 100644 index 00000000..1eebfc58 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSound.h @@ -0,0 +1,396 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSound.h - Base sound class header // +// // +//// History ///////////////////////////////////////////////////////////////// +// // +// 10.12.01 mcn - Added preliminary soft region (volume) support. // +// 7.12.02 mcn - Added EAX support // +// 7.15.02 mcn - Added support for animated volumes // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef plSound_h +#define plSound_h + +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "plEAXEffects.h" +#include "../pnNetCommon/plSynchedObject.h" +#include "../plAvatar/plAGChannel.h" +#include "../plAvatar/plAGApplicator.h" +#include "../plAudioCore/plSoundBuffer.h" + +class hsResMgr; +class hsStream; +class plSoundProxy; +class plDrawableSpans; +class hsGMaterial; +class plSoundMsg; +class plSoftVolume; +class plGraphPlate; +struct hsMatrix44; +class plSoundBuffer; +class plSceneObject; +class plSoundVolumeApplicator; + +// Set this to 1 to do our own distance attenuation (model doesn't work yet tho) +#define MCN_HACK_OUR_ATTEN 0 +#define MAX_INCIDENTALS 4 + +class plSound : public plSynchedObject +{ + friend class plSoundSDLModifier; + friend class plSoundVolumeApplicator; + +public: + plSound(); + virtual ~plSound(); + + CLASSNAME_REGISTER( plSound ); + GETINTERFACE_ANY( plSound, plSynchedObject ); + + enum Property + { + kPropIs3DSound = 0x00000001, + kPropDisableLOD = 0x00000002, + kPropLooping = 0x00000004, + kPropAutoStart = 0x00000008, + kPropLocalOnly = 0x00000010, // Disables network synching and triggering + kPropLoadOnlyOnCall = 0x00000020, // Only load and unload when we're told to + kPropFullyDisabled = 0x00000040, // This sound should never play while this is set + // Only plWin32LinkSound uses it. Placed here as a TODO though... + kPropDontFade = 0x00000080, + kPropIncidental = 0x00000100 // Incidental sound, will be played thru the incidental manager + }; + + enum Type + { + kStartType, + kSoundFX = kStartType, // For now, 3D sounds are always marked as this + kAmbience, + kBackgroundMusic, + kGUISound, + kNPCVoices, + kNumTypes + }; + + enum Refs + { + kRefSoftVolume = 0, + kRefDataBuffer, // plugins only + kRefParentSceneObject, + kRefSoftOcclusionRegion + }; + + enum + { + kSoftRegion = 0 + }; + + enum StreamType + { + kNoStream, + kStreamFromRAM, + kStreamFromDisk, + kStreamCompressed + }; + + class plFadeParams + { + friend class plSound; + + public: + enum Type + { + kLinear, + kLogarithmic, + kExponential + }; + + hsScalar fLengthInSecs; // Time to take to fade + hsScalar fVolStart; // Set one of these two for fade in/out, + hsScalar fVolEnd; // the other becomes the current volume + UInt8 fType; + hsBool fStopWhenDone; // Actually stop the sound once the fade is complete + hsBool fFadeSoftVol; // Fade the soft volume instead of fCurrVolume + + plFadeParams() { fLengthInSecs = 0.f; fCurrTime = -1.f; fStopWhenDone = false; fFadeSoftVol = false; fVolStart = fVolEnd = 0.f; fType = kLinear; } + + plFadeParams( Type type, hsScalar len, hsScalar start, hsScalar end ) + { + fLengthInSecs = len; fVolStart = start; fVolEnd = end; fType = type; + fStopWhenDone = false; + fFadeSoftVol = false; + } + + void Read( hsStream *s ); + void Write( hsStream *s ); + + hsScalar InterpValue( void ); + + protected: + hsScalar fCurrTime; // -1 if we aren't active, else it's how far we're into the animation + }; + + virtual hsBool LoadSound( hsBool is3D ) = 0; + hsScalar GetVirtualStartTime( void ) const { return (hsScalar)fVirtualStartTime; } + + virtual void Play(); + void SynchedPlay( unsigned bytes ); + void SynchedPlay( hsScalar virtualStartTime ); + virtual void Stop(); + virtual void FastForwardPlay(); + virtual void FastForwardToggle(); + virtual void SetMin(const int m); // sets minimum falloff distance + virtual void SetMax(const int m); // sets maximum falloff distance + virtual int GetMin() const; + virtual int GetMax() const; + virtual void SetVolume(const float volume); + virtual float GetVolume(void) const { return fCurrVolume; } + hsScalar GetMaxVolume() { return fMaxVolume; } + virtual hsBool IsPlaying() { return fPlaying; } + void SetTime(double t); + virtual double GetTime( void ) { return 0.f; } + virtual void Activate(hsBool forcePlay = false); + virtual void DeActivate(); + virtual void SetLength(double l) { fLength = l; } + virtual void SetMuted( hsBool muted ); + virtual hsBool IsMuted( void ) { return fMuted; } + void Disable() { fDistAttenuation = 0; } + virtual plSoundMsg* GetStatus(plSoundMsg* pMsg){return NULL;} + virtual void SetConeOrientation(hsScalar x, hsScalar y, hsScalar z); + virtual void SetOuterVolume( const int v ); // volume for the outer cone (if applicable) + virtual void SetConeAngles( int inner, int outer ); + virtual void SetPosition(const hsPoint3 pos); + virtual void SetVelocity(const hsVector3 vel); + virtual hsPoint3 GetPosition() const; + virtual hsVector3 GetVelocity() const; + + virtual void Update(); + + plSoundBuffer * GetDataBuffer( void ) const { return (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded(); } + hsScalar QueryCurrVolume( void ) const; // Returns the current volume, attenuated + + const char * GetFileName( void ) const; + virtual double GetLength(); + + void SetProperty( Property prop, hsBool on ) { if( on ) fProperties |= prop; else fProperties &= ~prop; } + hsBool IsPropertySet( Property prop ) const { return ( fProperties & prop ) ? true : false; } + + virtual void RefreshVolume( void ); + + virtual void SetStartPos(unsigned bytes) = 0; + virtual unsigned GetByteOffset(){return 0;} + virtual float GetActualTimeSec() = 0; + + virtual void AddCallbacks(plSoundMsg* pMsg) = 0; + virtual void RemoveCallbacks(plSoundMsg* pMsg) = 0; + + virtual UInt8 GetChannelSelect( void ) const { return 0; } // Only defined on Win32Sound right now, should be here tho + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual void SetFadeInEffect( plFadeParams::Type type, hsScalar length ); + virtual void SetFadeOutEffect( plFadeParams::Type type, hsScalar length ); + virtual hsScalar CalcSoftVolume( hsBool enable, hsScalar distToListenerSquared ); + virtual void UpdateSoftVolume( hsBool enable, hsBool firstTime = false ); + + virtual hsBool MsgReceive( plMessage* pMsg ); + virtual hsBool DirtySynchState( const char *sdlName = nil, UInt32 sendFlags = 0 ); // call when state has changed + + // Tests whether this sound is within range of the given position, not counting soft regions + hsBool IsWithinRange( const hsPoint3 &listenerPos, hsScalar *distSquared ); + + // Type setting and getting, from the Types enum + void SetType( UInt8 type ) { fType = type; } + UInt8 GetType( void ) const { return fType; } + + // Priority stuff + void SetPriority( UInt8 pri ) { fPriority = pri; } + UInt8 GetPriority( void ) const { return fPriority; } + + // Visualization + virtual plDrawableSpans* CreateProxy(const hsMatrix44& l2w, hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); + + // Forced loading/unloading (for when the audio system's LOD just doesn't cut it) + virtual void ForceLoad( ); + virtual void ForceUnload( void ); + + // Note: ONLY THE AUDIOSYS SHOULD CALL THIS. If you're not the audioSys, get lost. + static void SetCurrDebugPlate( const plKey soundKey ); + + void RegisterOnAudioSys( void ); + void UnregisterOnAudioSys( void ); + + // Also only for the audio system + hsScalar GetVolumeRank( void ); + void ForceUnregisterFromAudioSys( void ); + + static void SetLoadOnDemand( hsBool activate ) { fLoadOnDemandFlag = activate; } + static void SetLoadFromDiskOnDemand( hsBool activate ) { fLoadFromDiskOnDemand = activate; } + + const plEAXSourceSettings &GetEAXSettings( void ) const { return fEAXSettings; } + plEAXSourceSettings &GetEAXSettings( void ) { return fEAXSettings; } + virtual StreamType GetStreamType() const { return kNoStream; } + virtual void FreeSoundData(); + + +protected: + hsBool fPlaying; + hsBool fActive; + double fTime; + int fMaxFalloff; + int fMinFalloff; + hsScalar fCurrVolume; + hsScalar fDesiredVol; // Equal to fCurrVolume except when we're fading or muted + hsScalar fFadedVolume; + hsScalar fMaxVolume; + + int fOuterVol; + int fInnerCone; + int fOuterCone; + double fLength; + + int fProperties; + UInt8 fType; + UInt8 fPriority; + + hsBool fMuted, fFading, fRegisteredForTime, fPlayOnReactivate, fFreeData; + hsBool fNotHighEnoughPriority; // Set whenever the audioSys calls UpdateSoftVolume() with enable=false, + // thus indicating that we slipped off the top 16 most wanted list. + + // Do these need to be synched values? They weren't before... + hsVector3 fConeOrientation; + hsPoint3 f3DPosition; + hsVector3 f3DVelocity; + hsBool fPlayWhenLoaded; + + double fSynchedStartTimeSec; + + // Just around for reference and sending messages upward (synched state) + plSceneObject *fOwningSceneObject; + + // EAX Settings storage here + plEAXSourceSettings fEAXSettings; + hsBool fQueued; + + plFadeParams fFadeInParams, fFadeOutParams; + plFadeParams fCoolSoftVolumeTrickParams; + plFadeParams *fCurrFadeParams; + + plSoftVolume *fSoftRegion; + hsScalar fSoftVolume; + hsScalar fDistAttenuation, fDistToListenerSquared; + double fVirtualStartTime; + hsBool fRegistered; + static unsigned fIncidentalsPlaying; + + plSoftVolume *fSoftOcclusionRegion; + + plSoundBuffer *fDataBuffer; // Not always around + hsBool fDataBufferLoaded; + plKey fDataBufferKey; // Always around + + static plGraphPlate *fDebugPlate; + static plSound *fCurrDebugPlateSound; + + static hsBool fLoadOnDemandFlag, fLoadFromDiskOnDemand; + hsBool fLoading; + + void IUpdateDebugPlate( void ); + void IPrintDbgMessage( const char *msg, hsBool isErr = false ); + + virtual void ISetActualVolume(const float v) = 0; + virtual void IActuallyStop( void ); + virtual hsBool IActuallyPlaying( void ) = 0; + virtual void IActuallyPlay( void ) = 0; + virtual void IFreeBuffers( void ) = 0; + + //NOTE: if isIncidental is true the entire sound will be loaded. + virtual plSoundBuffer::ELoadReturnVal IPreLoadBuffer( hsBool playWhenLoaded, hsBool isIncidental = false ); + virtual void ISetActualTime( double t ) = 0; + + virtual hsBool IActuallyLoaded( void ) = 0; + virtual void IRefreshEAXSettings( hsBool force = false ) = 0; + + virtual hsScalar IGetChannelVolume( void ) const; + + void ISynchToStartTime( void ); + void ISynchedPlay( double virtualStartTime ); + void IStartFade( plFadeParams *params, hsScalar offsetIntoFade = 0.f ); + void IStopFade( hsBool shuttingDown = false, hsBool SetVolEnd = true); + + hsBool IWillBeAbleToPlay( void ); + + void ISetSoftRegion( plSoftVolume *region ); + hsScalar IAttenuateActualVolume( hsScalar volume ) const; + void ISetSoftOcclusionRegion( plSoftVolume *region ); + + // Override to make sure the buffer is available before the base class is called + virtual void IRefreshParams( void ); + + virtual bool ILoadDataBuffer( void ); + virtual void IUnloadDataBuffer( void ); + + //virtual void ISetMinDistance( const int m ) = 0; + //virtual void ISetMaxDistance( const int m ) = 0; + //virtual void ISetOuterVolume( const int v ) = 0; + //virtual void ISetConeAngles( int inner, int outer ) = 0; + //virtual void ISetActualConeOrient( hsVector3 &vector ) = 0; + //virtual void ISetVelocity( const hsVector3 vel ) = 0; + //virtual void ISetPosition( const hsPoint3 pos ) = 0; + + virtual void IRead( hsStream *s, hsResMgr *mgr ); + virtual void IWrite( hsStream *s, hsResMgr *mgr ); +}; + + +//// plSoundVolumeApplicator ///////////////////////////////////////////////// +// Tiny helper for handling animated volumes + +class plSoundVolumeApplicator : public plAGApplicator +{ +public: + plSoundVolumeApplicator() { } + plSoundVolumeApplicator( UInt32 index ) { fIndex = index; } + + CLASSNAME_REGISTER( plSoundVolumeApplicator ); + GETINTERFACE_ANY( plSoundVolumeApplicator, plAGApplicator ); + + virtual plAGApplicator *CloneWithChannel( plAGChannel *channel ); + virtual void Write( hsStream *stream, hsResMgr *mgr ); + virtual void Read( hsStream *s, hsResMgr *mgr ); + +protected: + UInt32 fIndex; + virtual void IApply( const plAGModifier *mod, double time ); +}; + +#endif //plWin32Sound_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.cpp new file mode 100644 index 00000000..129d534b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.cpp @@ -0,0 +1,183 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSoundEvent // +// // +//// Notes /////////////////////////////////////////////////////////////////// +// // +// 10.30.2001 - Created by mcn. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plSoundEvent.h" + +#include "plgDispatch.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../pnMessage/plSoundMsg.h" +#include "plSound.h" + +plSoundEvent::plSoundEvent( Types type, plSound *owner ) +{ + fType = type; + fBytePosTime = 0; + fOwner = owner; + fCallbacks.Reset(); + fCallbackEndingFlags.Reset(); +} + +plSoundEvent::plSoundEvent( Types type, UInt32 bytePos, plSound *owner ) +{ + fType = type; + fBytePosTime = bytePos; + fOwner = owner; + fCallbacks.Reset(); + fCallbackEndingFlags.Reset(); +} + +plSoundEvent::plSoundEvent() +{ + fType = kStart; + fBytePosTime = 0; + fOwner = nil; + fCallbacks.Reset(); + fCallbackEndingFlags.Reset(); +} + +plSoundEvent::~plSoundEvent() +{ + int i; + + + for( i = 0; i < fCallbacks.GetCount(); i++ ) + hsRefCnt_SafeUnRef( fCallbacks[ i ] ); +} + +void plSoundEvent::AddCallback( plEventCallbackMsg *msg ) +{ + hsRefCnt_SafeRef( msg ); + fCallbacks.Append( msg ); + fCallbackEndingFlags.Append( 0 ); +} + +hsBool plSoundEvent::RemoveCallback( plEventCallbackMsg *msg ) +{ + int idx = fCallbacks.Find( msg ); + if( idx != fCallbacks.kMissingIndex ) + { + hsRefCnt_SafeUnRef( msg ); + fCallbacks.Remove( idx ); + fCallbackEndingFlags.Remove( idx ); + return true; + } + + return false; +} + +void plSoundEvent::SendCallbacks( void ) +{ + int j; + plSoundMsg *sMsg; + + + for( j = fCallbacks.GetCount() - 1; j >= 0; j-- ) + { + plEventCallbackMsg *msg = fCallbacks[ j ]; + + if (!msg->HasBCastFlag(plMessage::kNetPropagate) || !fOwner || + fOwner->IsLocallyOwned() == plSynchedObject::kYes ) + { + /// Do this a bit differently so we can do our MsgSend last + sMsg = nil; + + // Ref to make sure the dispatcher doesn't delete it on us + hsRefCnt_SafeRef( msg ); + if( msg->fRepeats == 0 && fCallbackEndingFlags[ j ] == 0 ) + { + // Note: we get fancy here. We never want to remove the callback directly, + // because the sound won't know about it. So instead, send it a message to + // remove the callback for us + sMsg = TRACKED_NEW plSoundMsg(); + sMsg->SetBCastFlag( plMessage::kLocalPropagate, true ); + sMsg->AddReceiver( fOwner->GetKey() ); + sMsg->SetCmd( plSoundMsg::kRemoveCallbacks ); + sMsg->AddCallback( msg ); + } + + // If this isn't infinite, decrement the number of repeats + if( msg->fRepeats > 0 ) + msg->fRepeats--; + + // And finally... + if( fCallbackEndingFlags[ j ] == 0 ) + { + plgDispatch::MsgSend( msg, true ); + } + + if( sMsg != nil ) + { + plgDispatch::MsgSend( sMsg, true ); + fCallbackEndingFlags[ j ] = 0xff; // Our special flag to mean "hey, don't + // process this, just waiting for + // it to die" + } + } + } +} + +UInt32 plSoundEvent::GetNumCallbacks( void ) const +{ + return fCallbacks.GetCount(); +} + +int plSoundEvent::GetType( void ) const +{ + return (int)fType; +} + +void plSoundEvent::SetType( Types type ) +{ + fType = type; +} + +UInt32 plSoundEvent::GetTime( void ) const +{ + return fBytePosTime; +} + +plSoundEvent::Types plSoundEvent::GetTypeFromCallbackMsg( plEventCallbackMsg *msg ) +{ + switch( msg->fEvent ) + { + case ::kStart: return kStart; + case ::kTime: return kTime; + case ::kStop: return kStop; + case ::kLoop: return kLoop; + } + + return kStop; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.h new file mode 100644 index 00000000..37fc2edf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.h @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSoundEvent - Event node for handling callback thread stuff // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plSoundEvent_h +#define _plSoundEvent_h + +#include "hsTemplates.h" + +class plEventCallbackMsg; +class plSound; + +//// plSoundEvent //////////////////////////////////////////////////////////// +// Storage class for an event node. + +class plSoundEvent +{ +public: + + enum Types + { + kStart, + kStop, + kTime, + kLoop + }; + + plSoundEvent( Types type, plSound *owner ); + plSoundEvent( Types type, UInt32 bytePos, plSound *owner ); + plSoundEvent(); + ~plSoundEvent(); + + void AddCallback( plEventCallbackMsg *msg ); + hsBool RemoveCallback( plEventCallbackMsg *msg ); + + UInt32 GetNumCallbacks( void ) const; + int GetType( void ) const; + void SetType( Types type ); + UInt32 GetTime( void ) const; + + void SendCallbacks( void ); + + static Types GetTypeFromCallbackMsg( plEventCallbackMsg *msg ); + +protected: + + Types fType; + UInt32 fBytePosTime; + plSound *fOwner; + + hsTArray fCallbacks; + hsTArray fCallbackEndingFlags; +}; + + +#endif //_plSoundEvent_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.cpp new file mode 100644 index 00000000..017956e6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.cpp @@ -0,0 +1,698 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsWindows.h" +#include "hsTimer.h" +#include "hsResMgr.h" +#include "al.h" +#include "alc.h" +#include "plDSoundBuffer.h" +#include "speex.h" +#include "speex_bits.h" +#include "hsGeometry3.h" +#include "plVoiceChat.h" +#include "plAudioSystem.h" +#include "plgDispatch.h" +#include "../plAudible/plWinAudible.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plPipeline/plPlates.h" +#include "hsConfig.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plArmatureMod.h" +#include "hsQuat.h" +#include "../plAudioCore/plAudioCore.h" + +// DEBUG for printing to the console +#include "../plMessage/plConsoleMsg.h" +#include "../plPipeline/plDebugText.h" +#include "../plStatusLog/plStatusLog.h" + +#define MICROPHONE 121 +#define TALKING 122 +#define NUM_CHANNELS 1 +#define VOICE_STOP_MS 2000 +#define MAX_DATA_SIZE 1024 * 4 // 4 KB + +hsBool plVoiceRecorder::fCompress = true; +hsBool plVoiceRecorder::fRecording = true; +hsBool plVoiceRecorder::fNetVoice = false; +short plVoiceRecorder::fSampleRate = FREQUENCY; +hsScalar plVoiceRecorder::fRecordThreshhold = 200.0f; +hsBool plVoiceRecorder::fShowIcons = true; +hsBool plVoiceRecorder::fMicAlwaysOpen = false; +hsBool plVoicePlayer::fEnabled = true; + +plVoiceRecorder::plVoiceRecorder() +{ + plPlateManager::Instance().CreatePlate( &fDisabledIcon ); + fDisabledIcon->CreateFromResource( MAKEINTRESOURCE( MICROPHONE ) ); + fDisabledIcon->SetPosition(-0.90, -0.90); + fDisabledIcon->SetSize(0.0675, 0.09); + fDisabledIcon->SetVisible(false); + + plPlateManager::Instance().CreatePlate( &fTalkIcon ); + fTalkIcon->CreateFromResource( MAKEINTRESOURCE( TALKING ) ); + fTalkIcon->SetPosition(-0.9,-0.9); + fTalkIcon->SetSize(0.0675, 0.09); + fTalkIcon->SetVisible(false); +} + +plVoiceRecorder::~plVoiceRecorder() +{ + if(fDisabledIcon) + plPlateManager::Instance().DestroyPlate( fDisabledIcon); + fDisabledIcon = nil; + + if (fTalkIcon) + plPlateManager::Instance().DestroyPlate( fTalkIcon ); + fTalkIcon = nil; +} + +void plVoiceRecorder::IncreaseRecordingThreshhold() +{ + fRecordThreshhold += (100 * hsTimer::GetDelSysSeconds()); + if (fRecordThreshhold >= 10000.0f) + fRecordThreshhold = 10000.0f; + + plDebugText &txt = plDebugText::Instance(); + char str[256]; + sprintf(str, "RecordThreshhold %f\n", fRecordThreshhold); + txt.DrawString(400,300,str); +} + +void plVoiceRecorder::DecreaseRecordingThreshhold() +{ + fRecordThreshhold -= (100 * hsTimer::GetDelSysSeconds()); + if (fRecordThreshhold <= 50.0f) + fRecordThreshhold = 50.0f; + + plDebugText &txt = plDebugText::Instance(); + char str[256]; + sprintf(str, "RecordThreshhold %f\n", fRecordThreshhold); + txt.DrawString(400,300,str); +} + +// Set the quality of speex encoder +void plVoiceRecorder::SetQuality(int quality) +{ + char str[] = "Voice quality setting out of range. Must be between 1 and 10 inclusive"; + if(quality < 1 || quality > 10) + { + plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kAddLine, str ); + plgDispatch::MsgSend( cMsg ); + return; + } + + if(plSpeex::GetInstance()->IsUsingVBR()) + { + // Sets average bit rate between 4kb and 13kb + int AverageBitrate = quality * 1000 + 3000; + plSpeex::GetInstance()->SetABR(AverageBitrate); + } + else + { + plSpeex::GetInstance()->SetQuality(quality); + } +} + +// toggle variable bit rate +void plVoiceRecorder::SetVBR(bool vbr) +{ + plSpeex::GetInstance()->VBR(vbr); + SetQuality(plSpeex::GetInstance()->GetQuality()); // update proper quality param +} + +void plVoiceRecorder::SetComplexity(int c) +{ + char str[] = "Voice quality setting out of range. Must be between 1 and 10 inclusive"; + if(c < 1 || c > 10) + { + plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kAddLine, str ); + plgDispatch::MsgSend( cMsg ); + return; + } + plSpeex::GetInstance()->SetComplexity((UInt8) c); +} + +void plVoiceRecorder::SetENH(hsBool b) +{ + plSpeex::GetInstance()->SetENH(b); +} + +void plVoiceRecorder::SetMikeOpen(hsBool b) +{ + ALCdevice *device = plgAudioSys::GetCaptureDevice(); + if (fRecording && device) + { + if (b) + { + alcCaptureStart(device); + } + else + { + alcCaptureStop(device); + } + DrawTalkIcon(b); + fMikeOpen = b; + } + else + { + DrawDisabledIcon(b); // voice recording is unavailable or disabled + } +} + +void plVoiceRecorder::DrawDisabledIcon(hsBool b) +{ + if (!fDisabledIcon) + { + // at least try and make one here... + plPlateManager::Instance().CreatePlate( &fDisabledIcon ); + if (fDisabledIcon) + { + fDisabledIcon->CreateFromResource( MAKEINTRESOURCE( MICROPHONE ) ); + fDisabledIcon->SetPosition(-0.90, -0.90); + fDisabledIcon->SetSize(0.0675, 0.09); + fDisabledIcon->SetVisible(false); + } + } + + if (fDisabledIcon) + fDisabledIcon->SetVisible(b); +} + +void plVoiceRecorder::DrawTalkIcon(hsBool b) +{ + if (!fTalkIcon) + { + plPlateManager::Instance().CreatePlate( &fTalkIcon ); + if (fTalkIcon) + { fTalkIcon->CreateFromResource( MAKEINTRESOURCE( TALKING ) ); + fTalkIcon->SetPosition(-0.9,-0.9); + fTalkIcon->SetSize(0.0675, 0.09); + fTalkIcon->SetVisible(false); + } + } + + if (fTalkIcon) + { + fTalkIcon->SetVisible(b); + } +} + +void plVoiceRecorder::Update(double time) +{ + if(!fRecording) + return; + + int EncoderFrameSize = plSpeex::GetInstance()->GetFrameSize(); + if(EncoderFrameSize == -1) + return; + + ALCdevice *captureDevice = plgAudioSys::GetCaptureDevice(); + if(!captureDevice) + return; + + unsigned minSamples = EncoderFrameSize * 10; + + ALCint samples; + alcGetIntegerv(captureDevice, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples ); + + if (samples > 0) + { + if (samples >= minSamples) + { + int numFrames = (int)(samples / EncoderFrameSize); // the number of frames that have been captured + int totalSamples = numFrames * EncoderFrameSize; + + // cap uncompressed data + if(totalSamples > MAX_DATA_SIZE) + totalSamples = MAX_DATA_SIZE; + + // convert to correct units: + short *buffer = TRACKED_NEW short[totalSamples]; + + alcCaptureSamples(captureDevice, buffer, totalSamples); + + if (!CompressionEnabled()) + { + plNetMsgVoice pMsg; + pMsg.SetNetProtocol(kNetProtocolCli2Game); + pMsg.SetVoiceData((char *)buffer, totalSamples * sizeof(short)); + // set frame size here; + pMsg.SetPlayerID(plNetClientApp::GetInstance()->GetPlayerID()); + //if (false) //plNetClientApp::GetInstance()->GetFlagsBit(plNetClientApp::kEchoVoice)) + // pMsg.SetBit(plNetMessage::kEchoBackToSender); + plNetClientApp::GetInstance()->SendMsg(&pMsg); + + } + else // use the speex voice compression lib + { + UInt8 *packet = TRACKED_NEW UInt8[totalSamples]; // packet to send encoded data in + int packedLength = 0; // the size of the packet that will be sent + hsRAMStream ram; // ram stream to hold output data from speex + UInt8 numFrames = totalSamples / EncoderFrameSize; // number of frames to be encoded + + // encode the data using speex + plSpeex::GetInstance()->Encode(buffer, numFrames, &packedLength, &ram); + + if (packedLength) + { + // extract data from ram stream into packet + ram.Rewind(); + ram.Read(packedLength, packet); + plNetMsgVoice pMsg; + pMsg.SetNetProtocol(kNetProtocolCli2Game); + + pMsg.SetVoiceData((char *)packet, packedLength); + pMsg.SetPlayerID(plNetClientApp::GetInstance()->GetPlayerID()); + pMsg.SetFlag(VOICE_ENCODED); // Set encoded flag + pMsg.SetNumFrames(numFrames); + if (plNetClientApp::GetInstance()->GetFlagsBit(plNetClientApp::kEchoVoice)) + pMsg.SetBit(plNetMessage::kEchoBackToSender); + + plNetClientApp::GetInstance()->SendMsg(&pMsg); + } + delete[] packet; + } + delete[] buffer; + } + else if(!fMikeOpen) + { + short *buffer = TRACKED_NEW short[samples]; + // the mike has since closed, and there isn't enough data to meet our minimum, so throw this data out + alcCaptureSamples(captureDevice, buffer, samples); + delete[] buffer; + } + } +} + +plVoicePlayer::plVoicePlayer() +{ +} + +plVoicePlayer::~plVoicePlayer() +{ +} + +void plVoicePlayer::PlaybackUncompressedVoiceMessage(void* data, unsigned size) +{ + if(fEnabled) + { + if(!fSound.IsPlaying()) + { + fSound.Play(); + } + fSound.AddVoiceData(data, size); + } +} + +void plVoicePlayer::PlaybackVoiceMessage(void* data, unsigned size, int numFramesInBuffer) +{ + if(fEnabled) + { + int numBytes; // the number of bytes that speex decompressed the data to. + int bufferSize = numFramesInBuffer * plSpeex::GetInstance()->GetFrameSize(); + short *nBuff = TRACKED_NEW short[bufferSize]; + memset(nBuff, 0, bufferSize); + + // Decode the encoded voice data using speex + if(!plSpeex::GetInstance()->Decode((UInt8 *)data, size, numFramesInBuffer, &numBytes, nBuff)) + { + delete[] nBuff; + return; + } + + BYTE* newBuff; + newBuff = (BYTE*)nBuff; // Convert to byte data + PlaybackUncompressedVoiceMessage(newBuff, numBytes); // playback uncompressed data + delete[] nBuff; + } +} + +void plVoicePlayer::SetVelocity(const hsVector3 vel) +{ + fSound.SetVelocity(vel); +} + +void plVoicePlayer::SetPosition(const hsPoint3 pos) +{ + fSound.SetPosition(pos); +} + +void plVoicePlayer::SetOrientation(const hsPoint3 pos) +{ + fSound.SetConeOrientation(pos.fX, pos.fY, pos.fZ); +} + + +/***************************************************************************** +* +* plVoiceSound +* +***/ +unsigned plVoiceSound::fCount = 0; + +plVoiceSound::plVoiceSound() +{ + fInnerCone = 90; + fOuterCone = 240; + fOuterVol = -2000; + + fMinFalloff = 15; + fMaxFalloff = 75; + + fProperties = 0; + fCurrVolume = 1.0; + fDesiredVol = 1.0; + + fPriority = 1; + fType = plgAudioSys::kVoice; + + fEAXSettings.SetRoomParams(-1200, -100, 0, 0); + fLastUpdate = 0; + + char keyName[32]; + StrPrintf(keyName, arrsize(keyName), "VoiceSound_%d", fCount); + fCount++; + hsgResMgr::ResMgr()->NewKey(keyName, this, plLocation::kGlobalFixedLoc); +} + +plVoiceSound::~plVoiceSound() +{ +} + +hsBool plVoiceSound::LoadSound( hsBool is3D ) +{ + if( fFailed ) + return false; + if( !plgAudioSys::Active() || fDSoundBuffer ) + return false; + + if( fPriority > plgAudioSys::GetPriorityCutoff() ) + return false; // Don't set the failed flag, just return + + plWAVHeader header; + header.fFormatTag = WAVE_FORMAT_PCM; + header.fBitsPerSample = 16; + header.fNumChannels = 1; + header.fNumSamplesPerSec = FREQUENCY; + header.fBlockAlign = header.fNumChannels * header.fBitsPerSample / 2; + header.fAvgBytesPerSec = header.fNumSamplesPerSec * header.fBlockAlign; + + fDSoundBuffer = TRACKED_NEW plDSoundBuffer(0, header, true, false, false, true); + if(!fDSoundBuffer) + return false; + fDSoundBuffer->SetupVoiceSource(); + + IRefreshParams(); + IRefreshEAXSettings( true ); + fDSoundBuffer->SetScalarVolume(1.0); + return true; +} + +void plVoiceSound::Play() +{ + fPlaying = true; + if( IWillBeAbleToPlay() ) + { + IRefreshParams(); + SetVolume( fDesiredVol ); + IActuallyPlay(); + } +} + +void plVoiceSound::IDerivedActuallyPlay( void ) +{ + if( !fReallyPlaying ) + { + fDSoundBuffer->Play(); + fReallyPlaying = true; + } +} + +void plVoiceSound::AddVoiceData(void *data, unsigned bytes) +{ + unsigned size; + unsigned bufferId; + if(!fDSoundBuffer) + { + if(!LoadSound(true)) + { + return; + } + } + + fDSoundBuffer->UnQueueVoiceBuffers(); // attempt to unque any buffers that have finished + while(bytes > 0) + { + size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE; + if(!fDSoundBuffer->GetAvailableBufferId(&bufferId)) + break; // if there isn't any room for the data, it is currently thrown out + + fDSoundBuffer->VoiceFillBuffer(data, size, bufferId); + bytes -= size; + } + fLastUpdate = hsTimer::GetMilliSeconds(); +} + +void plVoiceSound::Update() +{ + if(IsPlaying()) + { + if((hsTimer::GetMilliSeconds() - fLastUpdate) > VOICE_STOP_MS) + { + Stop(); // terminating case for playback. Wait for x number of milliseconds, and stop. + } + } +} + +void plVoiceSound::IRefreshParams() +{ + plSound::IRefreshParams(); +} + + +/***************************************************************************** +* +* Speex Voice Encoding/Decoding +* +***/ + +plSpeex::plSpeex() : +fBits(nil), +fEncoderState(nil), +fDecoderState(nil), +fSampleRate(plVoiceRecorder::GetSampleRate()), +fFrameSize(-1), +fQuality(7), +fVBR(true), // variable bit rate on +fAverageBitrate(8000), // 8kb bitrate +fComplexity(3), +fENH(false), +fInitialized(false) +{ + fBits = TRACKED_NEW SpeexBits; + Init(kNarrowband); // if no one initialized us initialize using a narrowband encoder +} + +plSpeex::~plSpeex() +{ + Shutdown(); + delete fBits; + fBits = nil; +} + +hsBool plSpeex::Init(Mode mode) +{ + int enh = 1; + + // setup speex + speex_bits_init(fBits); + fBitsInit = true; + + if(mode == kNarrowband) + { + fEncoderState = speex_encoder_init(&speex_nb_mode); // narrowband + fDecoderState = speex_decoder_init(&speex_nb_mode); + } + else if(mode == kWideband) + { + fEncoderState = speex_encoder_init(&speex_wb_mode); + fDecoderState = speex_decoder_init(&speex_wb_mode); + } + + speex_encoder_ctl(fEncoderState, SPEEX_GET_FRAME_SIZE, &fFrameSize); // get frame size + speex_encoder_ctl(fEncoderState, SPEEX_SET_COMPLEXITY, &fComplexity); // 3 + speex_encoder_ctl(fEncoderState, SPEEX_SET_SAMPLING_RATE, &fSampleRate); // 8 khz + speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR_QUALITY, &fQuality); // 7 + speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR, &fVBR); // use variable bit rate + speex_encoder_ctl(fEncoderState, SPEEX_SET_ABR, &fAverageBitrate); // default to 8kb + + speex_decoder_ctl(fDecoderState, SPEEX_SET_ENH, &fENH); // perceptual enhancement + + fInitialized = true; + + return true; +} + +hsBool plSpeex::Shutdown() +{ + //shutdown speex + if(fDecoderState) + { + speex_decoder_destroy(fDecoderState); + fDecoderState = nil; + } + + if(fEncoderState) + { + speex_encoder_destroy(fEncoderState); + fEncoderState = nil; + } + + if(fBitsInit) + { + speex_bits_destroy(fBits); + fBitsInit = false; + } + fInitialized = false; + + return true; +} + +hsBool plSpeex::Encode(short *data, int numFrames, int *packedLength, hsRAMStream *out) +{ + *packedLength = 0; + + short *pData = data; // pointer to input data + float *input = TRACKED_NEW float[fFrameSize]; // input to speex - used as am intermediate array since speex requires float data + BYTE frameLength; // number of bytes speex compressed frame to + BYTE *frameData = TRACKED_NEW BYTE[fFrameSize]; // holds one frame of encoded data + + // encode data + for( int i = 0; i < numFrames; i++ ) + { + // convert input data to floats + for( int j = 0; j < fFrameSize; j++ ) + { + input[j] = pData[j]; + } + + speex_bits_reset(fBits); // reset bit structure + + // encode data using speex + speex_encode(fEncoderState, input, fBits); + frameLength = speex_bits_write(fBits, (char *)frameData, fFrameSize); + + // write data - length and bytes + out->WriteSwap(frameLength); + *packedLength += sizeof(frameLength); // add length of encoded frame + out->Write(frameLength, frameData); + *packedLength += frameLength; // update length + + pData += fFrameSize; // move input pointer + } + + delete[] frameData; + delete[] input; + return true; +} + +hsBool plSpeex::Decode(UInt8 *data, int size, int numFrames, int *numOutputBytes, short *out) +{ + if(!fInitialized) return false; + *numOutputBytes = 0; + + hsReadOnlyStream stream( size, data ); + float *speexOutput = TRACKED_NEW float[fFrameSize]; // holds output from speex + short *pOut = out; // pointer to output short buffer + + // create buffer for input data + BYTE *frameData = TRACKED_NEW BYTE[fFrameSize]; // holds the current frames data to be decoded + BYTE frameLen; // holds the length of the current frame being decoded. + + + // Decode data + for (int i = 0; i < numFrames; i++) + { + stream.ReadSwap( &frameLen ); // read the length of the current frame to be decoded + stream.Read( frameLen, frameData ); // read the data + + memset(speexOutput, 0, fFrameSize * sizeof(float)); + speex_bits_read_from(fBits, (char *)frameData, frameLen); // give data to speex + speex_decode(fDecoderState, fBits, speexOutput); // decode data + + for(int j = 0; j < fFrameSize; j++) + { + pOut[j] = (short)(speexOutput[j]); // convert floats to shorts + } + + pOut += fFrameSize; + } + + delete[] frameData; + delete[] speexOutput; + + *numOutputBytes = (numFrames * fFrameSize) * sizeof(short); // length of decoded voice data(out) in bytes + if(*numOutputBytes == 0) + return false; + + return true; +} + +// Sets variable bit rate on/off +void plSpeex::VBR(hsBool b) +{ + fVBR = b; + speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR, &fVBR); +} + + +// Sets the average bit rate +void plSpeex::SetABR(UInt32 abr) +{ + fAverageBitrate = abr; + speex_encoder_ctl(fEncoderState, SPEEX_SET_ABR, &fAverageBitrate); +} + +// Sets the quality of encoding +void plSpeex::SetQuality(UInt32 quality) +{ + fQuality = quality; + speex_encoder_ctl(fEncoderState, SPEEX_SET_QUALITY, &fQuality); +} + +void plSpeex::SetENH(hsBool b) +{ + fENH = b; + speex_decoder_ctl(fDecoderState, SPEEX_SET_ENH, &fENH); +} + +void plSpeex::SetComplexity(UInt8 c) +{ + fComplexity = c; + speex_encoder_ctl(fEncoderState, SPEEX_SET_COMPLEXITY, &fComplexity); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.h new file mode 100644 index 00000000..abb03da8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.h @@ -0,0 +1,196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plVoiceChat_h +#define plVoiceChat_h + +#include "hsTemplates.h" +#include "plWin32Sound.h" +#include "hsThread.h" + +// voice flags +#define VOICE_ENCODED ( 1 << 0 ) +#define VOICE_NARROWBAND ( 1 << 1 ) +#define VOICE_ENH ( 1 << 2 ) +#define BUFFER_LEN_SECONDS 4 +#define FREQUENCY 8000 + +struct hsVector3; +struct SpeexBits; +class plWinAudible; +class plPlate; +class plStatusLog; +class plSpeex; +typedef struct ALCdevice_struct ALCdevice; + + +// Sound used for playing back dynamic voice chat data. this allows us to hook voice chat into the audio system +class plVoiceSound : public plWin32Sound +{ +public: + plVoiceSound(); + ~plVoiceSound(); + hsBool LoadSound( hsBool is3D ); + void AddVoiceData(void *data, unsigned bytes); + void Update(); + void Play(); + virtual void SetStartPos(unsigned bytes){} + +private: + virtual bool ILoadDataBuffer( void ){ return true; } + virtual void IUnloadDataBuffer( void ){} + + virtual void IDerivedActuallyPlay( void ); + virtual void ISetActualTime( double t ){} + virtual float GetActualTimeSec() { return 0.0f; } + virtual void IRefreshParams( void ); + static unsigned fCount; + double fLastUpdate; +}; + +class plVoicePlayer +{ +public: + plVoicePlayer(); + ~plVoicePlayer(); + void PlaybackVoiceMessage(void* data, unsigned size, int numFramesInBuffer); + void PlaybackUncompressedVoiceMessage(void* data, unsigned size); + void SetVelocity(const hsVector3 vel); + void SetPosition(const hsPoint3 pos); + void SetOrientation(const hsPoint3 pos); + + void SetTalkIcon(int index, UInt32 str){} + void ClearTalkIcon(){} + plVoiceSound *GetSoundPtr() { return &fSound; } + static void Enable(hsBool enable) { fEnabled = enable; } + +private: + plVoiceSound fSound; + static hsBool fEnabled; +}; + +class plVoiceRecorder +{ +public: + plVoiceRecorder(); + ~plVoiceRecorder(); + + void Update(double time); + void SetMikeOpen(hsBool b); + void DrawTalkIcon(hsBool b); + void DrawDisabledIcon(hsBool b); + + void SetTalkIcon(int index, UInt32 str); + void ClearTalkIcon(); + + static hsBool RecordingEnabled() { return fRecording; } + static hsBool NetVoiceEnabled() { return fNetVoice; } + static hsBool CompressionEnabled() { return fCompress; } + static void EnablePushToTalk(hsBool b) { fMicAlwaysOpen = !b; } + static void EnableIcons(hsBool b) { fShowIcons = b; } + static void EnableRecording(hsBool b) { fRecording = b; } + static void EnableNetVoice(hsBool b) { fNetVoice = b; } + static void EnableCompression(hsBool b) { fCompress = b; } + static void SetSampleRate(short s) { fSampleRate = s; } + static void SetSquelch(hsScalar f) { fRecordThreshhold = f; } + + static void IncreaseRecordingThreshhold(); + static void DecreaseRecordingThreshhold(); + + static void SetQuality(int quality); // sets the quality of encoding + static void SetMode(int mode); // sets nb or wb mode + static void SetVBR(bool vbr); + static void SetComplexity(int c); + static void SetENH(hsBool b); + static short GetSampleRate() { return fSampleRate; } + +private: + + hsBool fMikeOpen; + hsBool fMikeJustClosed; + static hsBool fMicAlwaysOpen; + static hsBool fShowIcons; + static hsBool fCompress; + static hsBool fNetVoice; + static hsBool fRecording; + static short fSampleRate; + plPlate* fDisabledIcon; + plPlate* fTalkIcon; + static hsScalar fRecordThreshhold; +}; + + +// Speex voice encoder/decoder class +class plSpeex +{ +public: + ~plSpeex(); + + enum Mode + { + kNarrowband, + kWideband, + kUltraWideband + }; + static plSpeex *GetInstance() + { + static plSpeex instance; + return &instance; + } + + hsBool Init(Mode mode); + hsBool Shutdown(); + hsBool Encode(short *data, int numFrames, int *packedLength, hsRAMStream *out); + hsBool Decode(UInt8 *data, int size, int numFrames, int *numOutputBytes, short *out); + int GetFrameSize() { return fFrameSize; } + void VBR(hsBool b); // turn variable bit rate on/off + void SetVBR(UInt32 vbr); // Set variable bit rate quality + void ABR(hsBool b); // turn average bit rate on/off + void SetABR(UInt32 abr); // Set average bit rate quality + void SetQuality(UInt32 quality); // Set encoder quality + hsBool IsUsingVBR() { return fVBR; } + int GetQuality() { return fQuality; } + void SetENH(hsBool b); + void SetComplexity(UInt8 c); + + hsBool Initialized() { return fInitialized; } + +private: + plSpeex(); + SpeexBits* fBits; // main speex structure + hsBool fBitsInit; + void* fEncoderState; + void* fDecoderState; + int fSampleRate; + int fFrameSize; // frame size from speex - 160 for nb + int fQuality; // 0-10 speex encode quality + hsBool fVBR; // toggle variable bit rate + int fAverageBitrate; // n-bits per second + UInt8 fComplexity; // 1-10 sets cpu resources allowed for encoder + hsBool fENH; // perceptual enhancement + hsBool fInitialized; +}; + +#endif //plVoiceChat_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.cpp new file mode 100644 index 00000000..77b9ad52 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.cpp @@ -0,0 +1,210 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plWAVClipBuffer - Helper class for writing out WAV data in a buffered // +// manner, with support for clipping off the specified // +// amount at the end, but without knowing beforehand // +// exactly how much data we'll have. // +// // +// The algorithm goes something like this: we keep two buffers, both the // +// size of the amount we want to clip. We then start filling in the first // +// buffer, overflowing into the second buffer and wrapping back to the // +// first again in a circular fashion. When we fill up one buffer and are // +// about to advance to the next, we write that next buffer out. Why? // +// Because we know that, even if we got no more data in, we have enough // +// data in the first buffer to clip out the amount we want, so the other // +// half (which will have older data, being a circular buffer) can be // +// written out safely. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plWAVClipBuffer.h" +#include "hsStream.h" +#include "hsUtils.h" + +#include "plWavFile.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plWAVClipBuffer::plWAVClipBuffer( UInt32 clipSize, CWaveFile *outFile ) +{ + fBuffers[ 0 ] = fBuffers[ 1 ] = nil; + fFlushCalled = true; + Init( clipSize, outFile ); +} + +plWAVClipBuffer::~plWAVClipBuffer() +{ + IShutdown(); +} + +//// Init & IShutdown //////////////////////////////////////////////////////// + +void plWAVClipBuffer::Init( UInt32 clipSize, CWaveFile *outFile ) +{ + IShutdown(); + if( clipSize > 0 ) + { + fBuffers[ 0 ] = TRACKED_NEW UInt8[ clipSize ]; + fBuffers[ 1 ] = TRACKED_NEW UInt8[ clipSize ]; + memset( fBuffers[ 0 ], 0, clipSize ); + memset( fBuffers[ 1 ], 0, clipSize ); + } + fWhichBuffer = 0; + fBufferSize = clipSize; + fCursor = 0; + fFirstFlip = true; + fOutFile = outFile; + fFlushCalled = false; +} + +void plWAVClipBuffer::IShutdown( void ) +{ + hsAssert( fFlushCalled, "WAVClipBuffer shut down without flushing it!!!" ); + + delete [] fBuffers[ 0 ]; + delete [] fBuffers[ 1 ]; +} + +//// WriteData /////////////////////////////////////////////////////////////// +// The main workhorse; call this to add data to the buffer. + +hsBool plWAVClipBuffer::WriteData( UInt32 size, UInt8 *data ) +{ + while( size > 0 ) + { + UInt32 toWrite = fBufferSize - fCursor; + if( size < toWrite ) + { + // Just write, haven't filled a buffer yet + memcpy( fBuffers[ fWhichBuffer ] + fCursor, data, size ); + data += size; + fCursor += size; + return true; // All done! + } + + // Fill up to the end of a buffer, then flip + memcpy( fBuffers[ fWhichBuffer ] + fCursor, data, toWrite ); + data += toWrite; + fCursor += toWrite; + size -= toWrite; + + // Flip now... + fWhichBuffer = 1 - fWhichBuffer; + fCursor = 0; + + // Now we can write out this buffer, since it'll be old data and + // we have enough in the other buffer to clip with. The *only* + // time we don't want to do this is the first time we flip, since + // at that point, the buffer we just flipped to hasn't been filled yet. + // (Every time afterwards, we'll always be flipping to a buffer with old + // data). + if( fFirstFlip ) + fFirstFlip = false; + else + { + // Write it out before we overwrite it! + UINT written; + HRESULT hr = fOutFile->Write( fBufferSize, fBuffers[ fWhichBuffer ], &written ); + + if( FAILED( hr ) ) + { + hsAssert( false, "ERROR writing WMA stream to WAV file" ); + return false; + } + else if( written != fBufferSize ) + { + hsAssert( false, "Unable to write all of WMA stream to WAV file" ); + return false; + } + } + } + + // Cleanly got here, so just return success + return true; +} + +//// Flush /////////////////////////////////////////////////////////////////// +// Writes out the remaining data, minus our clip value (which is fBufferSize) +// So here's our situation: at this point, one of two things could be true: +// +// 1) We haven't received enough data to clip by, at which point we don't +// write any more and bail (this will be true if fFirstFlip is still true) +// +// 2) Our cursor is at 0, which means we have one filled buffer that hasn't been +// written out and our current buffer is empty. At this point, we discard the +// filled buffer (which is precisely the length we want to clip by) and we're done. +// +// 3) The buffer we're on should be partially filled, while the other one is older +// data. So, we want to write out the older data and the partial buffer all the way, +// except for the clip size. Since we can therefore never write out any data in the +// partial buffer (since that count will always be less than the clip size and thus be +// the second half of what we clip), we simply figure out how much of the other one we +// clip and write out the rest. + +hsBool plWAVClipBuffer::Flush( void ) +{ + fFlushCalled = true; + + if( fFirstFlip ) + return false; // We failed--not enough data to clip with + + if( fCursor == 0 ) + { + // Our current buffer is empty, so the other buffer is precisely what we clip. + // So just discard and return successfully + return true; + } + + // The hard case--we always discard the partial buffer we're on, so figure out + // how much we want to save of the other buffer. The math is: + // Partial buffer amount we're clipping = fCursor + // Amount of other buffer we're clipping = fBufferSize - fCursor + // Amount of other buffer we're writing = fBufferSize - ( fBufferSize - fCursor ) = fCursor + // Go figure :) + + UInt32 toWrite = fCursor; + + UINT written; + HRESULT hr = fOutFile->Write( toWrite, fBuffers[ 1 - fWhichBuffer ], &written ); + + if( FAILED( hr ) ) + { + hsAssert( false, "ERROR writing WMA stream to WAV file" ); + return false; + } + else if( written != toWrite ) + { + hsAssert( false, "Unable to write all of WMA stream to WAV file" ); + return false; + } + + // All done! + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.h new file mode 100644 index 00000000..271886f3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plWAVClipBuffer - Helper class for writing out WAV data in a buffered // +// manner, with support for clipping off the specified // +// amount at the end, but without knowing beforehand // +// exactly how much data we'll have. // +// // +// The algorithm goes something like this: we keep two buffers, both the // +// size of the amount we want to clip. We then start filling in the first // +// buffer, overflowing into the second buffer and wrapping back to the // +// first again in a circular fashion. When we fill up one buffer and are // +// about to advance to the next, we write that next buffer out. Why? // +// Because we know that, even if we got no more data in, we have enough // +// data in the first buffer to clip out the amount we want, so the other // +// half (which will have older data, being a circular buffer) can be // +// written out safely. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plWAVClipBuffer_h +#define _plWAVClipBuffer_h + +//// Class Definition //////////////////////////////////////////////////////// + +class CWaveFile; +class plWAVClipBuffer +{ +public: + + plWAVClipBuffer( UInt32 clipSize, CWaveFile *outFile ); + ~plWAVClipBuffer(); + + // Inits the buffer. Can re-init if you wish + void Init( UInt32 clipSize, CWaveFile *outFile ); + + // Writes/adds data to the buffer + hsBool WriteData( UInt32 size, UInt8 *data ); + + // Call at the end, flushes the buffer and performs the clipping + hsBool Flush( void ); + +protected: + UInt8 *fBuffers[ 2 ]; + UInt8 fWhichBuffer; // 0 or 1 + UInt32 fCursor, fBufferSize; + hsBool fFirstFlip, fFlushCalled; + + CWaveFile *fOutFile; + + void IShutdown( void ); +}; + +#endif //_plWAVClipBuffer_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWavFile.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWavFile.cpp new file mode 100644 index 00000000..2c0a5529 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWavFile.cpp @@ -0,0 +1,1088 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "plWavFile.h" + +#include +#include + +#include + +#pragma comment(lib, "winmm.lib") +#ifdef PATCHER +#define DXTRACE_ERR(str,hr) hr // I'm not linking in directx stuff to the just for this +#endif + +// if it looks like I lifted this class directly from Microsoft it's because that +// is exactly what I did. It's okay, though. Microsoft tells you to go ahead +// and do it in the DX8 documentation. They are SO nice. + +//----------------------------------------------------------------------------- +// Name: CWaveFile::CWaveFile() +// Desc: Constructs the class. Call Open() to open a wave file for reading. +// Then call Read() as needed. Calling the destructor or Close() +// will close the file. +//----------------------------------------------------------------------------- +CWaveFile::CWaveFile() +{ + m_pwfx = NULL; + m_hmmio = NULL; + m_dwSize = 0; + m_bIsReadingFromMemory = FALSE; + fSecsPerSample = 0; + +} + + + + +//----------------------------------------------------------------------------- +// Name: CWaveFile::~CWaveFile() +// Desc: Destructs the class +//----------------------------------------------------------------------------- +CWaveFile::~CWaveFile() +{ + Close(); + + if( !m_bIsReadingFromMemory ) + { + delete[] m_pwfx; + + } + + int i; + for(i = 0 ; i < fMarkers.size() ; i++) + { + delete [] fMarkers[i]->fName; + } +} + + + + +//----------------------------------------------------------------------------- +// Name: CWaveFile::Open() +// Desc: Opens a wave file for reading +//----------------------------------------------------------------------------- +HRESULT CWaveFile::Open(const char *strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags ) +{ + HRESULT hr; + + m_dwFlags = dwFlags; + m_bIsReadingFromMemory = FALSE; + + char fileName[MAX_PATH]; + sprintf(fileName, strFileName); + +#ifdef UNICODE + wchar_t * temp = hsStringToWString(fileName); + std::wstring wFileName = temp; + delete [] temp; +#endif + + if( m_dwFlags == WAVEFILE_READ ) + { + if( strFileName == NULL ) + return E_INVALIDARG; + delete[] m_pwfx; + +#ifdef UNICODE + m_hmmio = mmioOpen( (wchar_t*)wFileName.c_str(), NULL, MMIO_ALLOCBUF | MMIO_READ ); +#else + m_hmmio = mmioOpen( fileName, NULL, MMIO_ALLOCBUF | MMIO_READ ); +#endif + + if( NULL == m_hmmio ) + { + HRSRC hResInfo; + HGLOBAL hResData; + DWORD dwSize; + VOID* pvRes; + + // Loading it as a file failed, so try it as a resource +#ifdef UNICODE + if( NULL == ( hResInfo = FindResource( NULL, wFileName.c_str(), TEXT("WAVE") ) ) ) + { + if( NULL == ( hResInfo = FindResource( NULL, wFileName.c_str(), TEXT("WAV") ) ) ) + return DXTRACE_ERR( TEXT("FindResource"), E_FAIL ); + } +#else + if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAVE") ) ) ) + { + if( NULL == ( hResInfo = FindResource( NULL, strFileName, TEXT("WAV") ) ) ) + return DXTRACE_ERR( TEXT("FindResource"), E_FAIL ); + } +#endif + + if( NULL == ( hResData = LoadResource( NULL, hResInfo ) ) ) + return DXTRACE_ERR( TEXT("LoadResource"), E_FAIL ); + + if( 0 == ( dwSize = SizeofResource( NULL, hResInfo ) ) ) + return DXTRACE_ERR( TEXT("SizeofResource"), E_FAIL ); + + if( NULL == ( pvRes = LockResource( hResData ) ) ) + return DXTRACE_ERR( TEXT("LockResource"), E_FAIL ); + + CHAR* pData = TRACKED_NEW CHAR[ dwSize ]; + memcpy( pData, pvRes, dwSize ); + + MMIOINFO mmioInfo; + ZeroMemory( &mmioInfo, sizeof(mmioInfo) ); + mmioInfo.fccIOProc = FOURCC_MEM; + mmioInfo.cchBuffer = dwSize; + mmioInfo.pchBuffer = (CHAR*) pData; + + m_hmmio = mmioOpen( NULL, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ ); + } + + if( FAILED( hr = ReadMMIO() ) ) + { + // ReadMMIO will fail if its an not a wave file + mmioClose( m_hmmio, 0 ); + return DXTRACE_ERR( TEXT("ReadMMIO"), hr ); + } + + if( FAILED( hr = ResetFile() ) ) + return DXTRACE_ERR( TEXT("ResetFile"), hr ); + + // After the reset, the size of the wav file is m_ck.cksize so store it now + + + + + m_dwSize = m_ck.cksize; + + } + else + { +#ifdef UNICODE + m_hmmio = mmioOpen( (wchar_t*)wFileName.c_str(), NULL, MMIO_ALLOCBUF | + MMIO_READWRITE | + MMIO_CREATE ); +#else + m_hmmio = mmioOpen( fileName, NULL, MMIO_ALLOCBUF | + MMIO_READWRITE | + MMIO_CREATE ); +#endif + if( NULL == m_hmmio ) + return DXTRACE_ERR( TEXT("mmioOpen"), E_FAIL ); + + if( FAILED( hr = WriteMMIO( pwfx ) ) ) + { + mmioClose( m_hmmio, 0 ); + return DXTRACE_ERR( TEXT("WriteMMIO"), hr ); + } + + if( FAILED( hr = ResetFile() ) ) + return DXTRACE_ERR( TEXT("ResetFile"), hr ); + } + + return hr; +} + + + + +//----------------------------------------------------------------------------- +// Name: CWaveFile::OpenFromMemory() +// Desc: copy data to CWaveFile member variable from memory +//----------------------------------------------------------------------------- +HRESULT CWaveFile::OpenFromMemory( BYTE* pbData, ULONG ulDataSize, + WAVEFORMATEX* pwfx, DWORD dwFlags ) +{ + m_pwfx = pwfx; + m_ulDataSize = ulDataSize; + m_pbData = pbData; + m_pbDataCur = m_pbData; + m_bIsReadingFromMemory = TRUE; + + if( dwFlags != WAVEFILE_READ ) + return E_NOTIMPL; + + return S_OK; +} + + + +/* + + This defintion for a CuePoint was ripped from the internet somewhere. There are more defs at the end of this file which attempt to document + these wave file format extensions for storing markers. + + Cue Point-- + + The dwIdentifier field contains a unique number (ie, different than the ID number of any other CuePoint structure). This is used to associate + a CuePoint structure with other structures used in other chunks which will be described later. + + The dwPosition field specifies the position of the cue point within the "play order" (as determined by the Playlist chunk. See that chunk for + a discussion of the play order). + + The fccChunk field specifies the chunk ID of the Data or Wave List chunk which actually contains the waveform data to which this CuePoint + refers. If there is only one Data chunk in the file, then this field is set to the ID 'data'. On the other hand, if the file contains a Wave + List (which can contain both 'data' and 'slnt' chunks), then fccChunk will specify 'data' or 'slnt' depending upon in which type of chunk the + referenced waveform data is found. + + The dwChunkStart and dwBlockStart fields are set to 0 for an uncompressed WAVE file that contains one 'data' chunk. These fields are used + only for WAVE files that contain a Wave List (with multiple 'data' and 'slnt' chunks), or for a compressed file containing a 'data' chunk. + (Actually, in the latter case, dwChunkStart is also set to 0, and only dwBlockStart is used). Again, I want to emphasize that you can avoid + all of this unnecessary crap if you avoid hassling with compressed files, or Wave Lists, and instead stick to the sensible basics. + + The dwChunkStart field specifies the byte offset of the start of the 'data' or 'slnt' chunk which actually contains the waveform data to + which this CuePoint refers. This offset is relative to the start of the first chunk within the Wave List. (ie, It's the byte offset, within + the Wave List, of where the 'data' or 'slnt' chunk of interest appears. The first chunk within the List would be at an offset of 0). + + The dwBlockStart field specifies the byte offset of the start of the block containing the position. This offset is relative to the start of + the waveform data within the 'data' or 'slnt' chunk. + + The dwSampleOffset field specifies the sample offset of the cue point relative to the start of the block. In an uncompressed file, this + equates to simply being the offset within the waveformData array. Unfortunately, the WAVE documentation is much too ambiguous, and doesn't + define what it means by the term "sample offset". This could mean a byte offset, or it could mean counting the sample points (for example, + in a 16-bit wave, every 2 bytes would be 1 sample point), or it could even mean sample frames (as the loop offsets in AIFF are specified). + Who knows? The guy who conjured up the Cue chunk certainly isn't saying. I'm assuming that it's a byte offset, like the above 2 fields. +*/ + +class CuePoint +{ +public: + DWORD dwIdentifier; + DWORD dwPosition; + FOURCC fccChunk; + DWORD dwChunkStart; + DWORD dwBlockStart; + DWORD dwSampleOffset; + +public: + CuePoint(DWORD id, DWORD pos, FOURCC chk, DWORD ckSt, DWORD BkSt, DWORD SO) : + dwIdentifier(id), dwPosition(pos), fccChunk(chk), dwChunkStart(ckSt), dwBlockStart(BkSt), dwSampleOffset(SO) + {} + CuePoint(){} + + +}; + + + +// +// this struct is used to hold cue pts temporarily while we wait for the labels that match them +// +struct myCuePoint +{ + DWORD fId; + DWORD fOffset; +}; + + + +//----------------------------------------------------------------------------- +// Name: CWaveFile::ReadMMIO() +// Desc: Support function for reading from a multimedia I/O stream. +// m_hmmio must be valid before calling. This function uses it to +// update m_ckRiff, and m_pwfx. +//----------------------------------------------------------------------------- +HRESULT CWaveFile::ReadMMIO() +{ + MMCKINFO ckIn; // chunk info. for general use. + PCMWAVEFORMAT pcmWaveFormat; // Temp PCM structure to load in. + + m_pwfx = NULL; + + if( ( 0 != mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) ) + return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL ); + + // Check to make sure this is a valid wave file + if( (m_ckRiff.ckid != FOURCC_RIFF) || + (m_ckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E') ) ) + return DXTRACE_ERR( TEXT("mmioFOURCC"), E_FAIL ); + + // Search the input file for for the 'fmt ' chunk. + ckIn.ckid = mmioFOURCC('f', 'm', 't', ' '); + if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) ) + return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL ); + + // Expect the 'fmt' chunk to be at least as large as ; + // if there are extra parameters at the end, we'll ignore them + if( ckIn.cksize < (LONG) sizeof(PCMWAVEFORMAT) ) + return DXTRACE_ERR( TEXT("sizeof(PCMWAVEFORMAT)"), E_FAIL ); + + // Read the 'fmt ' chunk into . + if( mmioRead( m_hmmio, (HPSTR) &pcmWaveFormat, + sizeof(pcmWaveFormat)) != sizeof(pcmWaveFormat) ) + return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL ); + + // Allocate the waveformatex, but if its not pcm format, read the next + // word, and thats how many extra bytes to allocate. + if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM ) + { + m_pwfx = (WAVEFORMATEX*)( TRACKED_NEW CHAR[ sizeof( WAVEFORMATEX ) ] ); + if( NULL == m_pwfx ) + return DXTRACE_ERR( TEXT("m_pwfx"), E_FAIL ); + + // Copy the bytes from the pcm structure to the waveformatex structure + memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) ); + m_pwfx->cbSize = 0; + } + else + { + // Read in length of extra bytes. + WORD cbExtraBytes = 0L; + if( mmioRead( m_hmmio, (CHAR*)&cbExtraBytes, sizeof(WORD)) != sizeof(WORD) ) + return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL ); + + m_pwfx = (WAVEFORMATEX*)( TRACKED_NEW CHAR[ sizeof(WAVEFORMATEX) + cbExtraBytes ] ); + if( NULL == m_pwfx ) + return DXTRACE_ERR( TEXT("new"), E_FAIL ); + + // Copy the bytes from the pcm structure to the waveformatex structure + memcpy( m_pwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) ); + m_pwfx->cbSize = cbExtraBytes; + + // Now, read those extra bytes into the structure, if cbExtraAlloc != 0. + if( mmioRead( m_hmmio, (CHAR*)(((BYTE*)&(m_pwfx->cbSize))+sizeof(WORD)), + cbExtraBytes ) != cbExtraBytes ) + { + delete m_pwfx; + return DXTRACE_ERR( TEXT("mmioRead"), E_FAIL ); + } + } + + + + fSecsPerSample = 1.0/ (double)(pcmWaveFormat.wf.nSamplesPerSec) ; // * (((double)pcmWaveFormat.wBitsPerSample)/8.0); + + + // Ascend the input file out of the 'fmt ' chunk. + if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) ) + { + delete m_pwfx; + return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); + } + + + + // + // Here is where we attempt to parse sound indicies from the file for loop points. + // If there is no cue chunk then we just return OK + // + + ckIn.ckid = mmioFOURCC('c', 'u', 'e', ' '); + if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) ) + return S_OK; // No Cue Chunck so no point in reading the rest + +#if 0 + // Expect the 'cue ' chunk to be at least as large as ; + // if there are extra parameters at the end, we'll ignore them + if( ckIn.cksize < (long) ( sizeof(FOURCC) + 2*(sizeof(DWORD)) + sizeof(CuePoint) ) ) + return DXTRACE_ERR( TEXT("sizeof(CueChunk)"), E_FAIL ); +#endif + + DWORD* CueBuff = TRACKED_NEW DWORD[ckIn.cksize]; + DWORD Results; + Read((BYTE*)CueBuff, ckIn.cksize, &Results); + + std::vector myCueList; // Place to hold the cue points + + int numCuePoints = (ckIn.cksize - sizeof(DWORD))/sizeof(CuePoint); // this is how many there should be. + unsigned int i, j; + + for (i = 1, j = 0; i <= ckIn.cksize && j < numCuePoints ; i += sizeof(CuePoint)/(sizeof(DWORD)), j++) + + { + myCuePoint p; + p.fId = CueBuff[i]; // dwIdentifier + p.fOffset = CueBuff[i+1]; // dwPosition + myCueList.push_back(p); + } + + delete[] CueBuff; + + if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) ) + { + delete m_pwfx; + return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); + } + + + + // Goal --> Grab the label information below + ckIn.ckid = mmioFOURCC('a', 'd', 't', 'l'); + if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDLIST ) ) + return DXTRACE_ERR( TEXT("mmioDescend, with list"), E_FAIL ); + + + plSoundMarker *newMarker; + + BYTE *labelBuf = TRACKED_NEW BYTE [ckIn.cksize]; + + // Read the entire lable chunk and then lets parse out the individual lables. + Read(labelBuf, ckIn.cksize-4, &Results); + + BYTE *bp = labelBuf; + // Keep looking for labl chunks till we run out. + while(!strncmp("labl",(char*)bp,4)) + { + DWORD size = *(DWORD*)(bp + 4); + DWORD id = *(DWORD*)(bp + 8); + newMarker = TRACKED_NEW plSoundMarker; // Grab a new label + + int i; + + int numPts = myCueList.size(); + // + // Do we have a matching cue point for this label? + // + for(i = 0 ; i < numPts; i++) + { + if(id == myCueList[i].fId) + { + newMarker->fOffset = myCueList[i].fOffset * fSecsPerSample; + } + } + int stringSize = size - sizeof(DWORD); // text string is size of chunck - size of the size word + newMarker->fName = TRACKED_NEW char[ stringSize]; + + strcpy(newMarker->fName, (char*)(bp + 12)); + + fMarkers.push_back(newMarker); + bp += size + 8; + + // crappy fixup hack for odd length label records + if(size & 1 && !strncmp("labl", (char*)(bp +1), 4)) + bp++; + + fprintf(stderr,"Label name=%s Time =%f\n",newMarker->fName, newMarker->fOffset); + } + + delete [] labelBuf; + + if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) ) + { + delete m_pwfx; + return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); + } + + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: CWaveFile::GetSize() +// Desc: Retuns the size of the read access wave file +//----------------------------------------------------------------------------- +DWORD CWaveFile::GetSize() +{ + return m_dwSize; +} + + + + +//----------------------------------------------------------------------------- +// Name: CWaveFile::ResetFile() +// Desc: Resets the internal m_ck pointer so reading starts from the +// beginning of the file again +//----------------------------------------------------------------------------- +HRESULT CWaveFile::ResetFile() +{ + if( m_bIsReadingFromMemory ) + { + m_pbDataCur = m_pbData; + } + else + { + if( m_hmmio == NULL ) + return CO_E_NOTINITIALIZED; + + if( m_dwFlags == WAVEFILE_READ ) + { + // Seek to the data + if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof(FOURCC), + SEEK_SET ) ) + return DXTRACE_ERR( TEXT("mmioSeek"), E_FAIL ); + + // Search the input file for the 'data' chunk. + m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a'); + if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) ) + return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL ); + } + else + { + // Create the 'data' chunk that holds the waveform samples. + m_ck.ckid = mmioFOURCC('d', 'a', 't', 'a'); + m_ck.cksize = 0; + + if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) ) + return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL ); + + if( 0 != mmioGetInfo( m_hmmio, &m_mmioinfoOut, 0 ) ) + return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL ); + } + } + + return S_OK; +} + + +#define MCN_USE_NEW_READ_METHOD 0 + +//----------------------------------------------------------------------------- +// Name: CWaveFile::Read() +// Desc: Reads section of data from a wave file into pBuffer and returns +// how much read in pdwSizeRead, reading not more than dwSizeToRead. +// This uses m_ck to determine where to start reading from. So +// subsequent calls will be continue where the last left off unless +// Reset() is called. +//----------------------------------------------------------------------------- +HRESULT CWaveFile::Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead ) +{ + if( m_bIsReadingFromMemory ) + { + if( m_pbDataCur == NULL ) + return CO_E_NOTINITIALIZED; + if( pdwSizeRead != NULL ) + *pdwSizeRead = 0; + + if( (BYTE*)(m_pbDataCur + dwSizeToRead) > + (BYTE*)(m_pbData + m_ulDataSize) ) + { + dwSizeToRead = m_ulDataSize - (DWORD)(m_pbDataCur - m_pbData); + } + + CopyMemory( pBuffer, m_pbDataCur, dwSizeToRead ); + + if( pdwSizeRead != NULL ) + *pdwSizeRead = dwSizeToRead; + + return S_OK; + } + else + { + MMIOINFO mmioinfoIn; // current status of m_hmmio + + if( m_hmmio == NULL ) + return CO_E_NOTINITIALIZED; + if( pBuffer == NULL || pdwSizeRead == NULL ) + return E_INVALIDARG; + + if( pdwSizeRead != NULL ) + *pdwSizeRead = 0; + + if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) ) + return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL ); + + UINT cbDataIn = dwSizeToRead; + if( cbDataIn > m_ck.cksize ) + cbDataIn = m_ck.cksize; + + m_ck.cksize -= cbDataIn; + +#if !(MCN_USE_NEW_READ_METHOD) + for( DWORD cT = 0; cT < cbDataIn; cT++ ) + { + // Copy the bytes from the io to the buffer. + if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) + { + if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) ) + return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL ); + + if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) + return DXTRACE_ERR( TEXT("mmioinfoIn.pchNext"), E_FAIL ); + } + + // Actual copy. + *((BYTE*)pBuffer+cT) = *((BYTE*)mmioinfoIn.pchNext); + mmioinfoIn.pchNext++; + } +#else + // Attempt to do this a bit faster... 9.12.2001 mcn + for( DWORD cT = 0; cT < cbDataIn; ) + { + // Copy the bytes from the io to the buffer. + if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) + { + if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) ) + return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL ); + + if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) + return DXTRACE_ERR( TEXT("mmioinfoIn.pchNext"), E_FAIL ); + } + + // Actual copy + DWORD length = (DWORD)mmioinfoIn.pchEndRead - (DWORD)mmioinfoIn.pchNext; + if( cT + length > cbDataIn ) + length = cbDataIn - cT; + + memcpy( (BYTE*)pBuffer + cT, mmioinfoIn.pchNext, length ); + mmioinfoIn.pchNext += length; + cT += length; + } +#endif + + if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) ) + return DXTRACE_ERR( TEXT("mmioSetInfo"), E_FAIL ); + + if( pdwSizeRead != NULL ) + *pdwSizeRead = cbDataIn; + + return S_OK; + } +} + +//----------------------------------------------------------------------------- +// Name: CWaveFile::AdvanceWithoutRead() +// Desc: Identical to Read(), only doesn't actually read any data in. +//----------------------------------------------------------------------------- +HRESULT CWaveFile::AdvanceWithoutRead( DWORD dwSizeToRead, DWORD* pdwSizeRead ) +{ + if( m_bIsReadingFromMemory ) + { + if( m_pbDataCur == NULL ) + return CO_E_NOTINITIALIZED; + if( pdwSizeRead != NULL ) + *pdwSizeRead = 0; + + if( (BYTE*)(m_pbDataCur + dwSizeToRead) > + (BYTE*)(m_pbData + m_ulDataSize) ) + { + dwSizeToRead = m_ulDataSize - (DWORD)(m_pbDataCur - m_pbData); + } + + if( pdwSizeRead != NULL ) + *pdwSizeRead = dwSizeToRead; + + return S_OK; + } + else + { + MMIOINFO mmioinfoIn; // current status of m_hmmio + + if( m_hmmio == NULL ) + return CO_E_NOTINITIALIZED; + if( pdwSizeRead == NULL ) + return E_INVALIDARG; + + if( pdwSizeRead != NULL ) + *pdwSizeRead = 0; + + if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) ) + return DXTRACE_ERR( TEXT("mmioGetInfo"), E_FAIL ); + + UINT cbDataIn = dwSizeToRead; + if( cbDataIn > m_ck.cksize ) + cbDataIn = m_ck.cksize; + + m_ck.cksize -= cbDataIn; + +#if !(MCN_USE_NEW_READ_METHOD) + for( DWORD cT = 0; cT < cbDataIn; cT++ ) + { + // Copy the bytes from the io to the buffer. + if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) + { + if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) ) + return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL ); + + if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) + return DXTRACE_ERR( TEXT("mmioinfoIn.pchNext"), E_FAIL ); + } + + mmioinfoIn.pchNext++; + } +#else + // Attempt to do this a bit faster... 9.12.2001 mcn + for( DWORD cT = 0; cT < cbDataIn; ) + { + if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) + { + if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) ) + return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL ); + + if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ) + return DXTRACE_ERR( TEXT("mmioinfoIn.pchNext"), E_FAIL ); + } + + // Advance + DWORD length = (DWORD)mmioinfoIn.pchEndRead - (DWORD)mmioinfoIn.pchNext; + if( cT + length > cbDataIn ) + length = cbDataIn - cT; + + mmioinfoIn.pchNext += length; + cT += length; + } +#endif + + if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) ) + return DXTRACE_ERR( TEXT("mmioSetInfo"), E_FAIL ); + + if( pdwSizeRead != NULL ) + *pdwSizeRead = cbDataIn; + + return S_OK; + } +} + + +//----------------------------------------------------------------------------- +// Name: CWaveFile::IClose() +// Desc: Closes the wave file +//----------------------------------------------------------------------------- +HRESULT CWaveFile::IClose() +{ + if( m_dwFlags == WAVEFILE_READ ) + { + mmioClose( m_hmmio, 0 ); + m_hmmio = NULL; + } + else + { + m_mmioinfoOut.dwFlags |= MMIO_DIRTY; + + if( m_hmmio == NULL ) + return CO_E_NOTINITIALIZED; + + if( 0 != mmioSetInfo( m_hmmio, &m_mmioinfoOut, 0 ) ) + return DXTRACE_ERR( TEXT("mmioSetInfo"), E_FAIL ); + + // Ascend the output file out of the 'data' chunk -- this will cause + // the chunk size of the 'data' chunk to be written. + if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) ) + return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); + + // Do this here instead... + if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) ) + return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); + + mmioSeek( m_hmmio, 0, SEEK_SET ); + + if( 0 != (INT)mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) + return DXTRACE_ERR( TEXT("mmioDescend"), E_FAIL ); + + m_ck.ckid = mmioFOURCC('f', 'a', 'c', 't'); + + if( 0 == mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) ) + { + DWORD dwSamples = 0; + mmioWrite( m_hmmio, (HPSTR)&dwSamples, sizeof(DWORD) ); + mmioAscend( m_hmmio, &m_ck, 0 ); + } + + // Ascend the output file out of the 'RIFF' chunk -- this will cause + // the chunk size of the 'RIFF' chunk to be written. + if( 0 != mmioAscend( m_hmmio, &m_ckRiff, 0 ) ) + return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); + + mmioClose( m_hmmio, 0 ); + m_hmmio = NULL; + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: CWaveFile::WriteMMIO() +// Desc: Support function for reading from a multimedia I/O stream +// pwfxDest is the WAVEFORMATEX for this new wave file. +// m_hmmio must be valid before calling. This function uses it to +// update m_ckRiff, and m_ck. +//----------------------------------------------------------------------------- +HRESULT CWaveFile::WriteMMIO( WAVEFORMATEX *pwfxDest ) +{ + DWORD dwFactChunk; // Contains the actual fact chunk. Garbage until WaveCloseWriteFile. + MMCKINFO ckOut1; + + dwFactChunk = (DWORD)-1; + + // Create the output file RIFF chunk of form type 'WAVE'. + m_ckRiff.fccType = mmioFOURCC('W', 'A', 'V', 'E'); + m_ckRiff.cksize = 0; + + if( 0 != mmioCreateChunk( m_hmmio, &m_ckRiff, MMIO_CREATERIFF ) ) + return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL ); + + // We are now descended into the 'RIFF' chunk we just created. + // Now create the 'fmt ' chunk. Since we know the size of this chunk, + // specify it in the MMCKINFO structure so MMIO doesn't have to seek + // back and set the chunk size after ascending from the chunk. + m_ck.ckid = mmioFOURCC('f', 'm', 't', ' '); + m_ck.cksize = sizeof(PCMWAVEFORMAT); + + if( 0 != mmioCreateChunk( m_hmmio, &m_ck, 0 ) ) + return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL ); + + // Write the PCMWAVEFORMAT structure to the 'fmt ' chunk if its that type. + if( pwfxDest->wFormatTag == WAVE_FORMAT_PCM ) + { + if( mmioWrite( m_hmmio, (HPSTR) pwfxDest, + sizeof(PCMWAVEFORMAT)) != sizeof(PCMWAVEFORMAT)) + return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL ); + } + else + { + // Write the variable length size. + if( (UINT)mmioWrite( m_hmmio, (HPSTR) pwfxDest, + sizeof(*pwfxDest) + pwfxDest->cbSize ) != + ( sizeof(*pwfxDest) + pwfxDest->cbSize ) ) + return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL ); + } + + // Ascend out of the 'fmt ' chunk, back into the 'RIFF' chunk. + if( 0 != mmioAscend( m_hmmio, &m_ck, 0 ) ) + return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); + + // Now create the fact chunk, not required for PCM but nice to have. This is filled + // in when the close routine is called. + ckOut1.ckid = mmioFOURCC('f', 'a', 'c', 't'); + ckOut1.cksize = 0; + + if( 0 != mmioCreateChunk( m_hmmio, &ckOut1, 0 ) ) + return DXTRACE_ERR( TEXT("mmioCreateChunk"), E_FAIL ); + + if( mmioWrite( m_hmmio, (HPSTR)&dwFactChunk, sizeof(dwFactChunk)) != + sizeof(dwFactChunk) ) + return DXTRACE_ERR( TEXT("mmioWrite"), E_FAIL ); + + // Now ascend out of the fact chunk... + if( 0 != mmioAscend( m_hmmio, &ckOut1, 0 ) ) + return DXTRACE_ERR( TEXT("mmioAscend"), E_FAIL ); + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: CWaveFile::Write() +// Desc: Writes data to the open wave file +//----------------------------------------------------------------------------- +HRESULT CWaveFile::Write( UINT nSizeToWrite, BYTE* pbSrcData, UINT* pnSizeWrote ) +{ + UINT cT; + + if( m_bIsReadingFromMemory ) + return E_NOTIMPL; + if( m_hmmio == NULL ) + return CO_E_NOTINITIALIZED; + if( pnSizeWrote == NULL || pbSrcData == NULL ) + return E_INVALIDARG; + + *pnSizeWrote = 0; + + for( cT = 0; cT < nSizeToWrite; cT++ ) + { + if( m_mmioinfoOut.pchNext == m_mmioinfoOut.pchEndWrite ) + { + m_mmioinfoOut.dwFlags |= MMIO_DIRTY; + if( 0 != mmioAdvance( m_hmmio, &m_mmioinfoOut, MMIO_WRITE ) ) + return DXTRACE_ERR( TEXT("mmioAdvance"), E_FAIL ); + } + + *((BYTE*)m_mmioinfoOut.pchNext) = *((BYTE*)pbSrcData+cT); + (BYTE*)m_mmioinfoOut.pchNext++; + + (*pnSizeWrote)++; + } + + return S_OK; +} + + +// Overloads for plAudioFileReader (we only support writing for CWaveFile for now) +CWaveFile::CWaveFile( const char *path, plAudioCore::ChannelSelect whichChan ) +{ + m_pwfx = NULL; + m_hmmio = NULL; + m_dwSize = 0; + m_bIsReadingFromMemory = FALSE; + fSecsPerSample = 0; + + // Just a stub--do nothing +} + +hsBool CWaveFile::OpenForWriting( const char *path, plWAVHeader &header ) +{ + fHeader = header; + + WAVEFORMATEX winFormat; + winFormat.cbSize = 0; + winFormat.nAvgBytesPerSec = header.fAvgBytesPerSec; + winFormat.nBlockAlign = header.fBlockAlign; + winFormat.nChannels = header.fNumChannels; + winFormat.nSamplesPerSec = header.fNumSamplesPerSec; + winFormat.wBitsPerSample = header.fBitsPerSample; + winFormat.wFormatTag = header.fFormatTag; + + if( SUCCEEDED( Open( path, &winFormat, WAVEFILE_WRITE ) ) ) + return true; + + return false; +} + +plWAVHeader &CWaveFile::GetHeader( void ) +{ + return fHeader; +} + +void CWaveFile::Close( void ) +{ + IClose(); +} + +UInt32 CWaveFile::GetDataSize( void ) +{ + hsAssert( false, "Unsupported" ); + return 0; +} + +float CWaveFile::GetLengthInSecs( void ) +{ + hsAssert( false, "Unsupported" ); + return 0.f; +} + +hsBool CWaveFile::SetPosition( UInt32 numBytes ) +{ + hsAssert( false, "Unsupported" ); + return false; +} + +hsBool CWaveFile::Read( UInt32 numBytes, void *buffer ) +{ + hsAssert( false, "Unsupported" ); + return false; +} + +UInt32 CWaveFile::NumBytesLeft( void ) +{ + hsAssert( false, "Unsupported" ); + return 0; +} + +UInt32 CWaveFile::Write( UInt32 bytes, void *buffer ) +{ + UINT written; + Write( (DWORD)bytes, (BYTE *)buffer, &written ); + return (UInt32)written; +} + +hsBool CWaveFile::IsValid( void ) +{ + return true; +} + + +#if 0 + +THIS IS MORE STUFF having to do with WAV FILE format. It is just sitting here for documentation purposes. +/* + Cue Chunk-- + + The ID is always 'cue '. chunkSize is the number of bytes in the chunk, not counting the 8 bytes used by ID and Size fields. + The dwCuePoints field is the number of CuePoint structures in the Cue Chunk. If dwCuePoints is not 0, it is followed by that many + CuePoint structures, one after the other. Because all fields in a CuePoint structure are an even number of bytes, the length of any + CuePoint will always be even. Thus, CuePoints are packed together with no unused bytes between them. The CuePoints need not be placed + in any particular order. + + The Cue chunk is optional. No more than one Cue chunk can appear in a WAVE. +*/ + +class CueChunk +{ + +public: + FOURCC chunkID; + DWORD chunkSize; + DWORD dwCuePoints; + std::vector points; + +public: + CueChunk(DWORD ChunkSize) + { + chunkID = mmioFOURCC('c','u','e',' '); + chunkSize = ChunkSize; + dwCuePoints = (ChunkSize - (sizeof(DWORD)*1))/(sizeof(CuePoint)); + //points = NULL; + //points = TRACKED_NEW CuePoint[dwCuePoints]; + + } + //Cue + + ~CueChunk() {} //for(int i = 0; i < (int) dwCuePoints; i++) points.erase(i); } +}; + + +/* + LabelChunk-- + + The ID is always 'labl'. chunkSize is the number of bytes in the chunk, not counting the 8 bytes used by ID and Size fields nor any possible + pad byte needed to make the chunk an even size (ie, chunkSize is the number of remaining bytes in the chunk after the chunkSize field, not + counting any trailing pad byte). + The dwIdentifier field contains a unique number (ie, different than the ID number of any other Label chunk). This field should correspond + with the dwIndentifier field of some CuePoint stored in the Cue chunk. In other words, this Label chunk contains the text label associated + with that CuePoint structure with the same ID number. + + The dwText array contains the text label. It should be a null-terminated string. (The null byte is included in the chunkSize, therefore the + length of the string, including the null byte, is chunkSize - 4). +*/ + + +class LabelChunk +{ + +public: + FOURCC chunkID; + DWORD chunkSize; + + DWORD dwIdentifier; + char* dwText; +public: + LabelChunk(DWORD ChunkSize) + { + chunkID = mmioFOURCC('l','a','b','l'); + chunkSize = ChunkSize; + dwIdentifier = 0; + dwText = NULL; + } + + LabelChunk() + { + chunkID = mmioFOURCC('l','a','b','l'); + chunkSize = 0; + dwIdentifier = 0; + dwText = NULL; + } + + + ~LabelChunk() { delete[] dwText; } + +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWavFile.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWavFile.h new file mode 100644 index 00000000..5b67b875 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWavFile.h @@ -0,0 +1,114 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plWavFile_H +#define plWavFile_H + +#define WAVEFILE_READ 1 +#define WAVEFILE_WRITE 2 + +#include "hsTypes.h" +#include "hsWindows.h" +#include "hsStlUtils.h" +#include +#include "../plAudioCore/plAudioFileReader.h" + + +struct plSoundMarker +{ + char *fName; + double fOffset; // in Secs + + plSoundMarker () { fName = NULL;fOffset = 0.0; } + +}; + + +//----------------------------------------------------------------------------- +// Name: class CWaveFile +// Desc: Encapsulates reading or writing sound data to or from a wave file +//----------------------------------------------------------------------------- + + + +class CWaveFile : public plAudioFileReader +{ +public: + CWaveFile(); + ~CWaveFile(); + + HRESULT Open(const char *strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags ); + HRESULT OpenFromMemory( BYTE* pbData, ULONG ulDataSize, WAVEFORMATEX* pwfx, DWORD dwFlags ); + + HRESULT Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead ); + HRESULT AdvanceWithoutRead( DWORD dwSizeToRead, DWORD* pdwSizeRead ); + HRESULT Write( UINT nSizeToWrite, BYTE* pbData, UINT* pnSizeWrote ); + + DWORD GetSize(); + HRESULT ResetFile(); + WAVEFORMATEX* GetFormat() { return m_pwfx; }; + + DWORD GetNumMarkers() { return fMarkers.size() ; }; + plSoundMarker *GetSoundMarker(int i) { return fMarkers[i]; } + + + // Overloads for plAudioFileReader + CWaveFile( const char *path, plAudioCore::ChannelSelect whichChan ); + virtual hsBool OpenForWriting( const char *path, plWAVHeader &header ); + virtual plWAVHeader &GetHeader( void ); + virtual void Close( void ); + virtual UInt32 GetDataSize( void ); + virtual float GetLengthInSecs( void ); + + virtual hsBool SetPosition( UInt32 numBytes ); + virtual hsBool Read( UInt32 numBytes, void *buffer ); + virtual UInt32 NumBytesLeft( void ); + virtual UInt32 Write( UInt32 bytes, void *buffer ); + + virtual hsBool IsValid( void ); + WAVEFORMATEX* m_pwfx; // Pointer to WAVEFORMATEX structure + HMMIO m_hmmio; // MM I/O handle for the WAVE + MMCKINFO m_ck; // Multimedia RIFF chunk + MMCKINFO m_ckRiff; // Use in opening a WAVE file + DWORD m_dwSize; // The size of the wave file + MMIOINFO m_mmioinfoOut; + DWORD m_dwFlags; + BOOL m_bIsReadingFromMemory; + BYTE* m_pbData; + BYTE* m_pbDataCur; + ULONG m_ulDataSize; + + plWAVHeader fHeader; + + std::vector fMarkers; + double fSecsPerSample; + +protected: + HRESULT ReadMMIO(); + HRESULT WriteMMIO( WAVEFORMATEX *pwfxDest ); + HRESULT IClose(); +}; + +#endif // plWavFile_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.cpp new file mode 100644 index 00000000..b3a8fb40 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.cpp @@ -0,0 +1,393 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plWin32GroupedSound - Grouped version of a static sound. Lots of short // +// sounds stored in the buffer, all share the same // +// DSound playback buffer and only one plays at a // +// time. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" + +#include "plWin32GroupedSound.h" +#include "plDSoundBuffer.h" + +#include "plAudioSystem.h" +#include "../plAudioCore/plSoundBuffer.h" +#include "../plAudioCore/plSoundDeswizzler.h" +#include "plgDispatch.h" +#include "../pnMessage/plSoundMsg.h" + +#include "../plStatusLog/plStatusLog.h" +#include "plProfile.h" +#include "hsResMgr.h" + +plProfile_Extern( MemSounds ); +plProfile_Extern( StaticSndShoveTime ); +plProfile_Extern( StaticSwizzleTime ); + +///////////////////////////////////////////////////////////////////////////////////////////////// + +plWin32GroupedSound::plWin32GroupedSound() +{ + fCurrentSound = 0; +} + +plWin32GroupedSound::~plWin32GroupedSound() +{ + DeActivate(); +} + +void plWin32GroupedSound::SetPositionArray( UInt16 numSounds, UInt32 *posArray, hsScalar *volumeArray ) +{ + UInt16 i; + + + fStartPositions.SetCountAndZero( numSounds ); + fVolumes.SetCountAndZero( numSounds ); + for( i = 0; i < numSounds; i++ ) + { + fStartPositions[ i ] = posArray[ i ]; + fVolumes[ i ] = volumeArray[ i ]; + } +} + +//// IRead/IWrite //////////////////////////////////////////////////////////// + +void plWin32GroupedSound::IRead( hsStream *s, hsResMgr *mgr ) +{ + plWin32StaticSound::IRead( s, mgr ); + UInt16 i, n = s->ReadSwap16(); + fStartPositions.SetCountAndZero( n ); + fVolumes.SetCountAndZero( n ); + for( i = 0; i < n; i++ ) + { + fStartPositions[ i ] = s->ReadSwap32(); + fVolumes[ i ] = s->ReadSwapScalar(); + } +} + +void plWin32GroupedSound::IWrite( hsStream *s, hsResMgr *mgr ) +{ + plWin32StaticSound::IWrite( s, mgr ); + + s->WriteSwap16( fStartPositions.GetCount() ); + UInt16 i; + for( i = 0; i < fStartPositions.GetCount(); i++ ) + { + s->WriteSwap32( fStartPositions[ i ] ); + s->WriteSwapScalar( fVolumes[ i ] ); + } +} + +//// LoadSound /////////////////////////////////////////////////////////////// + +hsBool plWin32GroupedSound::LoadSound( hsBool is3D ) +{ + if( fFailed ) + return false; + + if( fPriority > plgAudioSys::GetPriorityCutoff() ) + return false; // Don't set the failed flag, just return + + if( !plgAudioSys::Active() || fDSoundBuffer != nil ) + return false; + + + // Debug flag #1 + if( fChannelSelect > 0 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableRightSelect ) ) + { + // Force a fail + fFailed = true; + return false; + } + + + // We need it to be resident to read in + plSoundBuffer::ELoadReturnVal retVal = IPreLoadBuffer(true); + plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded(); + if(!buffer) + { + return plSoundBuffer::kError; + } + + if( retVal == plSoundBuffer::kPending) // we are still reading data. + { + return true; + } + + // We need it to be resident to read in + if( retVal == plSoundBuffer::kError) + { + char str[ 256 ]; + sprintf( str, "Unable to open .wav file %s", fDataBufferKey ? fDataBufferKey->GetName() : "nil"); + IPrintDbgMessage( str, true ); + fFailed = true; + return false; + } + + SetProperty( kPropIs3DSound, is3D ); + + + plWAVHeader header = buffer->GetHeader(); + + // Debug flag #2 + if( fChannelSelect == 0 && header.fNumChannels > 1 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableLeftSelect ) ) + { + // Force a fail + fFailed = true; + return false; + } + + // Calculate the maximum size for our buffer. This will be the length of the longest sound we're going to + // have to play. + UInt16 i; + UInt32 maxSoundSize, len; + for( i = 1, maxSoundSize = 0; i < fStartPositions.GetCount(); i++ ) + { + len = fStartPositions[ i ] - fStartPositions[ i - 1 ]; + if( len > maxSoundSize ) + maxSoundSize = len; + } + len = buffer->GetDataLength() - fStartPositions[ fStartPositions.GetCount() - 1 ]; + if( len > maxSoundSize ) + maxSoundSize = len; + + // Based on that, allocate our buffer + UInt32 bufferSize = maxSoundSize - ( maxSoundSize % header.fBlockAlign ); + + if( header.fNumChannels > 1 && is3D ) + { + // We can only do a single channel of 3D sound. So copy over one (later) + bufferSize /= header.fNumChannels; + header.fBlockAlign /= header.fNumChannels; + header.fAvgBytesPerSec /= header.fNumChannels; + header.fNumChannels = 1; + } + fNumDestChannels = (UInt8)(header.fNumChannels); + fNumDestBytesPerSample = (UInt8)(header.fBlockAlign); + + // Create our DSound buffer (or rather, the wrapper around it) + fDSoundBuffer = TRACKED_NEW plDSoundBuffer( bufferSize, header, is3D, IsPropertySet( kPropLooping ), true ); + if( !fDSoundBuffer->IsValid() ) + { + char str[256]; + sprintf(str, "Can't create sound buffer for %s.wav. This could happen if the wav file is a stereo file. Stereo files are not supported on 3D sounds. If the file is not stereo then please report this error.", GetFileName()); + IPrintDbgMessage( str, true ); + fFailed = true; + + delete fDSoundBuffer; + fDSoundBuffer = nil; + + return false; + } + + IRefreshEAXSettings( true ); + + // Fill the buffer with whatever our current sound is. + IFillCurrentSound( 0 ); + + // Logging + char str[ 256 ]; + sprintf( str, " Grouped %s %s allocated (%d msec).", buffer->GetFileName() != nil ? "file" : "buffer", + buffer->GetFileName() != nil ? buffer->GetFileName() : buffer->GetKey()->GetUoid().GetObjectName(), + //fDSoundBuffer->IsHardwareAccelerated() ? "hardware" : "software", + //fDSoundBuffer->IsStaticVoice() ? "static" : "dynamic", +#ifdef PL_PROFILE_ENABLED + gProfileVarStaticSndShoveTime.GetValue() ); +#else + 0 ); +#endif + IPrintDbgMessage( str ); + if( GetKey() != nil && GetKeyName() != nil && strstr( GetKeyName(), "Footstep" ) != nil ) + ; + else + plStatusLog::AddLineS( "audioTimes.log", "%s (%s)", str, GetKey() ? GetKeyName() : "unkeyed" ); + + fTotalBytes = bufferSize; + + plProfile_NewMem( MemSounds, fTotalBytes ); + + // All done! +// if( fLoadFromDiskOnDemand ) +// IUnloadDataBuffer(); + FreeSoundData(); + return true; + +} + +//// GetSoundLength ////////////////////////////////////////////////////////// +// Gets the length (in seconds) of the given sound index from the group. + +hsScalar plWin32GroupedSound::GetSoundLength( Int16 soundIndex ) +{ + plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded(); + if(buffer) + { + return (hsScalar)IGetSoundByteLength( soundIndex ) / buffer->GetHeader().fAvgBytesPerSec; + } + + return 0; +} + +//// IGetSoundByteLength ///////////////////////////////////////////////////// +// Byte version of above. + +UInt32 plWin32GroupedSound::IGetSoundByteLength( Int16 soundIndex ) +{ + + if( soundIndex == fStartPositions.GetCount() - 1 ) + return ((plSoundBuffer *)fDataBufferKey->ObjectIsLoaded())->GetDataLength() - fStartPositions[ soundIndex ]; + else + return fStartPositions[ soundIndex + 1 ] - fStartPositions[ soundIndex ]; +} + +//// IGetDataPointer/Length ////////////////////////////////////////////////// +// Abstracting a few things here for the incidentalMgr + +void *plWin32GroupedSound::IGetDataPointer( void ) const +{ + return ( fDataBufferKey->ObjectIsLoaded() ) ? (void *)( (UInt8 *)((plSoundBuffer *)fDataBufferKey->ObjectIsLoaded())->GetData() + fStartPositions[ fCurrentSound ] ) : nil; +} + +UInt32 plWin32GroupedSound::IGetDataLength( void ) const +{ + return ( fDataBufferKey->ObjectIsLoaded() ) ? fCurrentSoundLength : 0; +} + +//// IFillCurrentSound /////////////////////////////////////////////////////// +// Fills the DSoundBuffer with data from the current sound from our sound +// group, optionally switching what our current sound is. + +void plWin32GroupedSound::IFillCurrentSound( Int16 newCurrent /*= -1*/ ) +{ + //void *dataPtr; + //UInt32 dataLength; + + if( !fDSoundBuffer && plgAudioSys::Active() ) + LoadSound( IsPropertySet( kPropIs3DSound ) ); + + plProfile_BeginTiming( StaticSndShoveTime ); + + // Make sure we're stopped first. Don't want to be filling while we're playing + Stop(); + + if( newCurrent != -1 ) + { + fCurrentSound = (UInt16)newCurrent; + + if( fCurrentSound >= fStartPositions.GetCount() ) + { + // Invalid index! + hsAssert( false, "Invalid index in plWin32GroupedSound::IFillCurrentSound()" ); + fCurrentSound = -1; + return; + } + + // Set our length based on the current sound + fCurrentSoundLength = IGetSoundByteLength( fCurrentSound ); + if( fDataBufferKey->ObjectIsLoaded() ) + SetLength( fCurrentSoundLength / ((plSoundBuffer *)fDataBufferKey->ObjectIsLoaded())->GetHeader().fAvgBytesPerSec ); + + // Update our volume as well + SetVolume( fVolumes[ fCurrentSound ] ); + } + + if( fDSoundBuffer != nil ) + { + /// Lock our buffer + //fDSoundBuffer->Lock( dataLength, dataPtr ); + + /// Copy or de-swizzle? + //if( fDataBuffer->GetHeader().fNumChannels == fNumDestChannels ) + { + // Just copy + //memcpy( dataPtr, (Byte *)fDataBuffer->GetData() + fStartPositions[ fCurrentSound ], fCurrentSoundLength ); + //dataPtr = (Byte *)dataPtr + fCurrentSoundLength; + //dataLength -= fCurrentSoundLength; + } + //else + { + // We're extracting a single channel of sound into our sound buffer, so it isn't a straight copy... + + /*plProfile_BeginTiming( StaticSwizzleTime ); + + plSoundDeswizzler deswiz( (Byte *)fDataBuffer->GetData() + fStartPositions[ fCurrentSound ], fCurrentSoundLength, + (UInt8)(fDataBuffer->GetHeader().fNumChannels), fNumDestBytesPerSample ); + + deswiz.Extract( fChannelSelect, dataPtr ); + + dataPtr = (Byte *)dataPtr + fCurrentSoundLength / fDataBuffer->GetHeader().fNumChannels; + dataLength -= fCurrentSoundLength / fDataBuffer->GetHeader().fNumChannels; + + plProfile_EndTiming( StaticSwizzleTime );*/ + } + + /// Fill the remaining part with empty space + //memset( dataPtr, 0, dataLength ); + + /// Finally, unlock! + //fDSoundBuffer->Unlock(); + } + + /// All done! + plProfile_EndTiming( StaticSndShoveTime ); +} + +void plWin32GroupedSound::IDerivedActuallyPlay( void ) +{ + // Ensure there's a stop notify for us + if( !fReallyPlaying ) + { + fDSoundBuffer->Play(); + fReallyPlaying = true; + } + + plSoundEvent *event = IFindEvent( plSoundEvent::kStart ); + if( event != nil ) + event->SendCallbacks(); +} + +hsBool plWin32GroupedSound::MsgReceive( plMessage* pMsg ) +{ + plSoundMsg *soundMsg = plSoundMsg::ConvertNoRef( pMsg ); + if( soundMsg != nil && soundMsg->Cmd( plSoundMsg::kSelectFromGroup ) ) + { + IFillCurrentSound( soundMsg->fIndex ); + return true; + } + else if( soundMsg != nil && soundMsg->Cmd( plSoundMsg::kPlay ) ) + { + Play(); + //plIncidentalMgr::GetInstance()->Play( this, plIncidentalMgr::kNormal ); + return true; + } + + return plWin32StaticSound::MsgReceive( pMsg ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.h new file mode 100644 index 00000000..f03a3fcc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.h @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plWin32GroupedSound - Grouped version of a static sound. Lots of short // +// sounds stored in the buffer, all share the same // +// DSound playback buffer and only one plays at a // +// time. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef plWin32GroupedSound_h +#define plWin32GroupedSound_h + +#include "plWin32StaticSound.h" + +class hsResMgr; +class plDSoundBuffer; +class plEventCallbackMsg; + +#include "plSoundEvent.h" + +class plWin32GroupedSound : public plWin32StaticSound +{ +public: + plWin32GroupedSound(); + ~plWin32GroupedSound(); + + CLASSNAME_REGISTER( plWin32GroupedSound ); + GETINTERFACE_ANY( plWin32GroupedSound, plWin32StaticSound ); + + virtual hsBool LoadSound( hsBool is3D ); + virtual hsBool MsgReceive( plMessage *pMsg ); + void SetPositionArray( UInt16 numSounds, UInt32 *posArray, hsScalar *volumeArray ); + hsScalar GetSoundLength( Int16 soundIndex ); + virtual double GetLength() { return GetSoundLength( fCurrentSound ); } + +protected: + UInt16 fCurrentSound; + UInt32 fCurrentSoundLength; + hsTArray fStartPositions; // In bytes + hsTArray fVolumes; + + // Some extra handy info for us + UInt8 fNumDestChannels, fNumDestBytesPerSample; + + virtual void IDerivedActuallyPlay( void ); + + virtual void IRead( hsStream *s, hsResMgr *mgr ); + virtual void IWrite( hsStream *s, hsResMgr *mgr ); + + UInt32 IGetSoundByteLength( Int16 soundIndex ); + void IFillCurrentSound( Int16 newCurrent = -1 ); + + // Abstracting a few things here for the incidentalMgr + virtual void * IGetDataPointer( void ) const; + virtual UInt32 IGetDataLength( void ) const; +}; + +#endif //plWin32GroupedSound_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp new file mode 100644 index 00000000..30da33f1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp @@ -0,0 +1,430 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include +#include "HeadSpin.h" +#include "hsGeometry3.h" +#include "hsTimer.h" +#include "hsResMgr.h" +#include "plgDispatch.h" + +#include "plProfile.h" +#include "plWin32Sound.h" +#include "plAudioSystem.h" +#include "plDSoundBuffer.h" +#include "plWavFile.h" + +#include "../plAudible/plWinAudible.h" +#include "../plNetMessage/plNetMessage.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnMessage/plSoundMsg.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../plPipeline/plPlates.h" +#include "../plStatusLog/plStatusLog.h" + +plProfile_CreateMemCounter("Sounds", "Memory", MemSounds); +plProfile_Extern(SoundPlaying); + +plWin32Sound::plWin32Sound() : +fFailed(false), +fPositionInited(false), +fAwaitingPosition(false), +fTotalBytes(0), +fReallyPlaying(false), +fChannelSelect(0), +fDSoundBuffer(nil) +{ +} + +plWin32Sound::~plWin32Sound() +{ +} + +void plWin32Sound::Activate( hsBool forcePlay ) +{ + if( fFailed ) + return; + + plSound::Activate( forcePlay ); +} + +void plWin32Sound::DeActivate() +{ + plSound::DeActivate(); + IFreeBuffers(); +} + +void plWin32Sound::IFreeBuffers( void ) +{ + if( fDSoundBuffer != nil ) + { + delete fDSoundBuffer; + fDSoundBuffer = nil; + plProfile_DelMem(MemSounds, fTotalBytes); + fTotalBytes = 0; + } + + fPositionInited = false; + fAwaitingPosition = false; +} + +void plWin32Sound::Update() +{ + plSound::Update(); +} + +void plWin32Sound::IActuallyPlay( void ) +{ + //plSound::Play(); + if (!fDSoundBuffer && plgAudioSys::Active()) + LoadSound( IsPropertySet( kPropIs3DSound ) ); + if(!fLoading ) + { + if (fDSoundBuffer && plgAudioSys::Active() ) + { + + // Sometimes base/derived classes can be annoying + IDerivedActuallyPlay(); + RefreshVolume(); + } + else + { + // If we can't load (for ex., if audio is off), then we act like we played and then stopped + // really fast. Thus, we need to send *all* of our callbacks off immediately and then Stop(). + UInt32 i; + for( i = 0; i < fSoundEvents.GetCount(); i++ ) + { + fSoundEvents[ i ]->SendCallbacks(); + } + + // Now stop, 'cause we played really really really really fast + fPlaying = false; + fPlayOnReactivate = IsPropertySet( kPropLooping ); + IActuallyStop(); + } + } +} + +void plWin32Sound::IActuallyStop() +{ + if( fReallyPlaying ) + { + if( fDSoundBuffer != nil ) + { + if(IsPropertySet(kPropIncidental)) + { + --fIncidentalsPlaying; + } + fDSoundBuffer->Stop(); + plStatusLog::AddLineS("impacts.log", "Stopping %s", GetKeyName()); + + } + fReallyPlaying = false; + } + else + { + if( fDSoundBuffer != nil && fDSoundBuffer->IsPlaying() ) + { + plStatusLog::AddLineS( "audio.log", 0xffff0000, "WARNING: BUFFER FLAGGED AS STOPPED BUT NOT STOPPED - %s", GetKey() ? GetKeyName() : nil ); + fDSoundBuffer->Stop(); + } + } + + // send callbacks + plSoundEvent *event = IFindEvent( plSoundEvent::kStop ); + if( event != nil ) + { + event->SendCallbacks(); + } + + plSound::IActuallyStop(); +} + +plSoundMsg* plWin32Sound::GetStatus(plSoundMsg* pMsg) +{ + plSoundMsg* pReply = TRACKED_NEW plSoundMsg; + pReply->AddReceiver( pMsg->GetSender() ); + pReply->SetCmd(plSoundMsg::kStatusReply); + pReply->fLoop = IsPropertySet( kPropLooping ); + pReply->fPlaying = IsPlaying(); + return pReply; +} + +void plWin32Sound::SetMin( const int m ) +{ + plSound::SetMin(m); + if(fDSoundBuffer) + { + fDSoundBuffer->SetMinDistance(m); + } +} + +void plWin32Sound::SetMax( const int m ) +{ + plSound::SetMax(m); + if( fDSoundBuffer ) + { + fDSoundBuffer->SetMaxDistance( m ); + } +} + +void plWin32Sound::SetOuterVolume( const int v ) +{ + plSound::SetOuterVolume(v); + if(fDSoundBuffer) + { + fDSoundBuffer->SetConeOutsideVolume(v); + } +} + +void plWin32Sound::SetConeAngles( int inner, int outer ) +{ + plSound::SetConeAngles(inner, outer); + if(fDSoundBuffer) + { + fDSoundBuffer->SetConeAngles(inner, outer); + } +} + +void plWin32Sound::SetConeOrientation( hsScalar x, hsScalar y, hsScalar z ) +{ + plSound::SetConeOrientation(x, y, z); + if(fDSoundBuffer) + { + fDSoundBuffer->SetConeOrientation(x, z, y); + } +} + +void plWin32Sound::SetVelocity( const hsVector3 vel ) +{ + plSound::SetVelocity(vel); + if( fDSoundBuffer) + fDSoundBuffer->SetVelocity(vel.fX, vel.fZ, vel.fY); +} + +void plWin32Sound::SetPosition( const hsPoint3 pos ) +{ + plSound::SetPosition(pos); + if(fDSoundBuffer) + { + // in openal sounds that are mono are played as positional. Since gui's may be positioned way off in space, the sound may not be audible. + // doing this allows us to play mono gui sounds and still hear them, since this attaches the sound to the listener. + if(fType == kGUISound) + { + hsPoint3 listenerPos = plgAudioSys::Sys()->GetCurrListenerPos(); + fDSoundBuffer->SetPosition(listenerPos.fX, listenerPos.fZ, listenerPos.fY); + } + else + { + fDSoundBuffer->SetPosition(pos.fX, pos.fZ, pos.fY); + } + } + + fPositionInited = true; + if( fAwaitingPosition ) + { + // If this is set, then we tried to set the volume before the position. Since + // this results in some ghastly sound popping, we wait until we set the position + // (here), and then call our volume again + RefreshVolume(); + fAwaitingPosition = false; + } +} + +void plWin32Sound::ISetActualVolume(const float volume) +{ + float vol = IAttenuateActualVolume( volume ) * IGetChannelVolume(); + if( fDSoundBuffer ) + { + if( fPositionInited || !IsPropertySet( kPropIs3DSound ) ) + { + fDSoundBuffer->SetScalarVolume( vol ); + } + else + { + // If position isn't inited, we don't want to set the volume yet, + // so set this flag so we know to set the volume once we DO get the position + fAwaitingPosition = true; + } + } + IUpdateDebugPlate(); // Byte me. +} + +////////////////////////////////////////////////////////////// +// The base class will make sure all our params are correct and up-to-date, +// but before it does its work, we have to make sure our buffer is ready to +// be updated. +void plWin32Sound::IRefreshParams( void ) +{ + if (!fDSoundBuffer && plgAudioSys::Active()) + LoadSound( IsPropertySet( kPropIs3DSound ) ); + + else + { + // There is a gap between the ds buffer stopping and the sound being marked as stopped. + // If the sound is asked to play again during this time frame it will never actually be + // played because it is still marked as playing. Not only that, but we'll lose a hardware voice too. + // This will fix that by starting it up again. + if(plgAudioSys::Active()) + fDSoundBuffer->Play(); + } + plSound::IRefreshParams(); +} + +void plWin32Sound::IRefreshEAXSettings( hsBool force ) +{ + if( fDSoundBuffer != nil ) + fDSoundBuffer->SetEAXSettings( &GetEAXSettings(), force ); +} + +void plWin32Sound::IAddCallback( plEventCallbackMsg *pMsg ) +{ + plSoundEvent::Types type = plSoundEvent::GetTypeFromCallbackMsg( pMsg ); + plSoundEvent *event; + + if( type == plSoundEvent::kTime ) + { + UInt32 byteTime = ( fDSoundBuffer != nil ) ? fDSoundBuffer->GetBufferBytePos( pMsg->fEventTime ) : 0; + + event = IFindEvent( type, byteTime ); + + if( event == nil ) + { + // Add a new sound event for this guy + event = TRACKED_NEW plSoundEvent( type, byteTime, this ); + //fDSoundBuffer->AddPosNotify( byteTime ); + fSoundEvents.Append( event ); + } + } + else + { + event = IFindEvent( type ); + + if( event == nil ) + { + // Add a new sound event for this guy + event = TRACKED_NEW plSoundEvent( type, this ); + fSoundEvents.Append( event ); + } + } + + event->AddCallback( pMsg ); +} + +void plWin32Sound::IRemoveCallback( plEventCallbackMsg *pMsg ) +{ + plSoundEvent::Types type = plSoundEvent::GetTypeFromCallbackMsg( pMsg ); + + for(int i = 0; i < fSoundEvents.GetCount(); ++i) + { + if( fSoundEvents[ i ]->GetType() == type ) + { + if( fSoundEvents[ i ]->RemoveCallback( pMsg ) ) + { + if( fSoundEvents[ i ]->GetNumCallbacks() == 0 ) + { + //if( fSoundEvents[ i ]->GetType() == plSoundEvent::kTime ) + //fDSoundBuffer->RemovePosNotify( fSoundEvents[ i ]->GetTime() ); + + delete fSoundEvents[ i ]; + fSoundEvents.Remove( i ); + } + break; + } + } + } +} + +plSoundEvent *plWin32Sound::IFindEvent( plSoundEvent::Types type, UInt32 bytePos ) +{ + for(int i = 0; i < fSoundEvents.GetCount(); ++i ) + { + if( fSoundEvents[ i ]->GetType() == type ) + { + if( type != plSoundEvent::kTime || bytePos == fSoundEvents[ i ]->GetTime() ) + return fSoundEvents[ i ]; + } + } + + return nil; +} + +void plWin32Sound::RemoveCallbacks(plSoundMsg* pSoundMsg) +{ + for(int i = 0; i < pSoundMsg->GetNumCallbacks(); ++i ) + IRemoveCallback( pSoundMsg->GetEventCallback( i ) ); +} + +void plWin32Sound::AddCallbacks(plSoundMsg* pSoundMsg) +{ + for(int i = 0; i < pSoundMsg->GetNumCallbacks(); ++i ) + IAddCallback( pSoundMsg->GetEventCallback( i ) ); +} + +hsBool plWin32Sound::MsgReceive( plMessage* pMsg ) +{ + plEventCallbackMsg *e = plEventCallbackMsg::ConvertNoRef( pMsg ); + if( e != nil ) + { + if( e->fEvent == kStop ) + { + fPlaying = false; + fPlayOnReactivate = false; // Just to make sure... + + // Do we have an ending fade? + if( fFadeOutParams.fLengthInSecs > 0 ) + { + IStartFade( &fFadeOutParams ); + plSoundEvent *event = IFindEvent( plSoundEvent::kStop ); + if( event != nil ) + event->SendCallbacks(); + } + else + { + if( fFading ) + IStopFade(); + + fCurrVolume = 0.f; + this->ISetActualVolume( fCurrVolume ); + } + this->IActuallyStop(); + return true; + } + } + return plSound::MsgReceive( pMsg ); +} + +void plWin32Sound::IRead( hsStream *s, hsResMgr *mgr ) +{ + plSound::IRead( s, mgr ); + fChannelSelect = s->ReadByte(); +} + +void plWin32Sound::IWrite( hsStream *s, hsResMgr *mgr ) +{ + plSound::IWrite( s, mgr ); + s->WriteByte( fChannelSelect ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.h new file mode 100644 index 00000000..1ed58280 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.h @@ -0,0 +1,120 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plWin32Sound_h +#define plWin32Sound_h + +#include "hsTemplates.h" +#include "plSound.h" +#include "hsThread.h" +#include "plSoundEvent.h" + +#define NUM_MAX_HANDLES 16 +#define REPEAT_INFINITE 0xffffffff + +struct hsVector3; +class hsResMgr; +class plEventCallbackMsg; +class plSoundMsg; +class DSoundCallbackHandle; +class plSoundEvent; +class plDSoundBuffer; + +class plWin32Sound : public plSound +{ +public: + + plWin32Sound(); + virtual ~plWin32Sound(); + + CLASSNAME_REGISTER( plWin32Sound ); + GETINTERFACE_ANY( plWin32Sound, plSound ); + + virtual void Activate(hsBool forcePlay = false); + virtual void DeActivate(); + + virtual void AddCallbacks(plSoundMsg* pMsg); + virtual void RemoveCallbacks(plSoundMsg* pMsg); + + virtual plSoundMsg* GetStatus(plSoundMsg* pMsg); + virtual hsBool MsgReceive(plMessage* pMsg); + virtual void Update(); + + virtual void SetMin(const int m); // sets minimum falloff distance + virtual void SetMax(const int m); // sets maximum falloff distance + virtual void SetConeOrientation(hsScalar x, hsScalar y, hsScalar z); + virtual void SetOuterVolume( const int v ); // volume for the outer cone (if applicable) + virtual void SetConeAngles( int inner, int outer ); + virtual void SetPosition(const hsPoint3 pos); + virtual void SetVelocity(const hsVector3 vel); + + enum ChannelSelect + { + kLeftChannel, + kRightChannel + }; + + // Selects a channel source from a multi-channel (stereo) file. Ignored if source is mono + void SetChannelSelect( ChannelSelect source ) { fChannelSelect = (UInt8)source; } + virtual UInt8 GetChannelSelect( void ) const { return fChannelSelect; } + +protected: + + plDSoundBuffer * fDSoundBuffer; + + hsBool fFailed; + hsBool fPositionInited, fAwaitingPosition; + hsBool fReallyPlaying; + UInt32 fTotalBytes; + + hsBool fWasPlaying; + + UInt8 fChannelSelect; // For selecting a mono channel from a stereo file + + hsTArray fSoundEvents; + + virtual void ISetActualVolume(const float v); + virtual void IActuallyStop( void ); + virtual hsBool IActuallyPlaying( void ) { return fReallyPlaying; } + virtual void IActuallyPlay( void ); + virtual void IFreeBuffers( void ); + virtual hsBool IActuallyLoaded( void ) { return ( fDSoundBuffer != nil ) ? true : false; } + + // Override to make sure the buffer is available before the base class is called + virtual void IRefreshParams( void ); + + virtual void IDerivedActuallyPlay( void ) = 0; + + virtual void IAddCallback( plEventCallbackMsg *pMsg ); + virtual void IRemoveCallback( plEventCallbackMsg *pMsg ); + plSoundEvent *IFindEvent( plSoundEvent::Types type, UInt32 bytePos = 0 ); + + virtual void IRead( hsStream *s, hsResMgr *mgr ); + virtual void IWrite( hsStream *s, hsResMgr *mgr ); + + virtual void IRefreshEAXSettings( hsBool force = false ); +}; + +#endif //plWin32Sound_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.cpp new file mode 100644 index 00000000..1692cc9c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.cpp @@ -0,0 +1,327 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "hsGeometry3.h" +#include "hsResMgr.h" +#include "plgDispatch.h" +#include "plProfile.h" +#include "plWin32StaticSound.h" +#include "plWin32Sound.h" +#include "plDSoundBuffer.h" +#include "plAudioSystem.h" +#include "../plAudioCore/plSoundBuffer.h" +#include "../plAudioCore/plSoundDeswizzler.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../pnMessage/plAudioSysMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../plMessage/plAvatarMsg.h" + +#include "../plPipeline/plPlates.h" +#include "../plStatusLog/plStatusLog.h" + +plProfile_Extern(MemSounds); +plProfile_CreateAsynchTimer( "Static Shove Time", "Sound", StaticSndShoveTime ); +plProfile_CreateAsynchTimer( "Static Swizzle Time", "Sound", StaticSwizzleTime ); + +plWin32StaticSound::plWin32StaticSound() +{ +} + +plWin32StaticSound::~plWin32StaticSound() +{ + DeActivate(); + IUnloadDataBuffer(); +} + +void plWin32StaticSound::Activate( hsBool forcePlay ) +{ + plWin32Sound::Activate( forcePlay ); +} + +void plWin32StaticSound::DeActivate() +{ + plWin32Sound::DeActivate(); +} + +hsBool plWin32StaticSound::LoadSound( hsBool is3D ) +{ + if (fFailed) + return false; + + if( fPriority > plgAudioSys::GetPriorityCutoff() ) + return false; // Don't set the failed flag, just return + + if (plgAudioSys::Active() && !fDSoundBuffer) + { + // Debug flag #1 + if( fChannelSelect > 0 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableRightSelect ) ) + { + // Force a fail + fFailed = true; + return false; + } + + // We need it to be resident to read in + plSoundBuffer::ELoadReturnVal retVal = IPreLoadBuffer(true); + plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded(); + if(!buffer) + { + return plSoundBuffer::kError; + } + + if( retVal == plSoundBuffer::kPending) // we are still reading data. + { + return true; + } + + if( retVal == plSoundBuffer::kError ) + { + char str[ 256 ]; + sprintf( str, "Unable to open .wav file %s", fDataBufferKey ? fDataBufferKey->GetName() : "nil"); + IPrintDbgMessage( str, true ); + fFailed = true; + return false; + } + + SetProperty( kPropIs3DSound, is3D ); + + plWAVHeader header = buffer->GetHeader(); + + // Debug flag #2 + if( fChannelSelect == 0 && header.fNumChannels > 1 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableLeftSelect ) ) + { + // Force a fail + fFailed = true; + return false; + } + UInt32 bufferSize = buffer->GetDataLength(); + + if( header.fNumChannels > 1 && is3D ) + { + // We can only do a single channel of 3D sound. So copy over one (later) + bufferSize /= header.fNumChannels; + header.fBlockAlign /= header.fNumChannels; + header.fAvgBytesPerSec /= header.fNumChannels; + header.fNumChannels = 1; + } + + hsBool tryStatic = true; + // If we want FX, we can't use a static voice, but EAX doesn't fit under that limitation :) + if( 0 ) + tryStatic = false; + + // Create our DSound buffer (or rather, the wrapper around it) + fDSoundBuffer = TRACKED_NEW plDSoundBuffer( bufferSize, header, is3D, IsPropertySet( kPropLooping ), tryStatic ); + if( !fDSoundBuffer->IsValid() ) + { + char str[256]; + sprintf(str, "Can't create sound buffer for %s.wav. This could happen if the wav file is a stereo file. Stereo files are not supported on 3D sounds. If the file is not stereo then please report this error.", GetFileName()); + IPrintDbgMessage( str, true ); + fFailed = true; + + delete fDSoundBuffer; + fDSoundBuffer = nil; + + return false; + } + + plProfile_BeginTiming( StaticSndShoveTime ); + + if(!fDSoundBuffer->FillBuffer(buffer->GetData(), buffer->GetDataLength(), &header)) + { + delete fDSoundBuffer; + fDSoundBuffer = nil; + plStatusLog::AddLineS("audio.log", "Could not play static sound, no voices left %s", GetKeyName()); + return false; + } + + plProfile_EndTiming( StaticSndShoveTime ); + IRefreshEAXSettings( true ); + + fTotalBytes = bufferSize; + + plProfile_NewMem(MemSounds, fTotalBytes); + + // get pertinent info + hsScalar length = (hsScalar)bufferSize / (hsScalar)header.fAvgBytesPerSec; + SetLength(length); + + if( fLoadFromDiskOnDemand && !IsPropertySet( kPropLoadOnlyOnCall ) ) + FreeSoundData(); + + return true; + } + return false; +} + +void plWin32StaticSound::Update() +{ + plWin32Sound::Update(); + if(fDSoundBuffer) + { + if(fPlaying) // we think we are playing + { + if(!fDSoundBuffer->IsPlaying()) // are we actually playing + { + Stop(); + } + } + } +} + +void plWin32StaticSound::IDerivedActuallyPlay( void ) +{ + // Ensure there's a stop notify for us + if( !fReallyPlaying ) + { + for(;;) + { + if(IsPropertySet(kPropIncidental)) + { + if(fIncidentalsPlaying >= MAX_INCIDENTALS) + break; + ++fIncidentalsPlaying; + } + + fDSoundBuffer->Play(); + fReallyPlaying = true; + break; + } + } + + plSoundEvent *event = IFindEvent( plSoundEvent::kStart ); + if( event != nil ) + event->SendCallbacks(); +} + +float plWin32StaticSound::GetActualTimeSec() +{ + if(fDSoundBuffer) + return fDSoundBuffer->GetTimeOffsetSec(); + return 0.0f; +} + +void plWin32StaticSound::ISetActualTime(double t) +{ + if( !fDSoundBuffer && plgAudioSys::Active()) + LoadSound( IsPropertySet( kPropIs3DSound ) ); + if( fDSoundBuffer ) + { + if(!t) + fDSoundBuffer->SetTimeOffsetSec((float)t); + } +} + +hsBool plWin32StaticSound::MsgReceive( plMessage* pMsg ) +{ + return plWin32Sound::MsgReceive( pMsg ); +} + +void plWin32StaticSound::IRemoveCallback( plEventCallbackMsg *pCBMsg ) +{ + plWin32Sound::IRemoveCallback( pCBMsg ); +} + +void plWin32StaticSound::IAddCallback( plEventCallbackMsg *pCBMsg ) +{ + if( plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStop && + plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStart ) + { + hsAssert( false, "Static sounds only support start and stop callbacks at this time." ); + return; + } + plWin32Sound::IAddCallback( pCBMsg ); +} + + +plWin32LinkSound::plWin32LinkSound() +{ + SetLocalOnly(true); // linking sounds already synch at a higher level + SetProperty( kPropDontFade, true ); +} + +void plWin32LinkSound::Read(hsStream* s, hsResMgr* mgr) +{ + plWin32StaticSound::Read(s, mgr); + + plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectBCMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAvatarStealthModeMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plPseudoLinkEffectMsg::Index(), GetKey()); + + SetLocalOnly(true); // linking sounds already synch at a higher level + SetProperty( kPropDontFade, true ); +} + +void plWin32LinkSound::Write(hsStream* s, hsResMgr* mgr) +{ + plWin32StaticSound::Write(s, mgr); +} + +hsBool plWin32LinkSound::MsgReceive( plMessage* pMsg ) +{ + plLinkEffectBCMsg *msg = plLinkEffectBCMsg::ConvertNoRef( pMsg ); + if( msg != nil && !msg->HasLinkFlag(plLinkEffectBCMsg::kMute)) + { + if (msg->fLinkKey->GetUoid().GetClonePlayerID() == GetKey()->GetUoid().GetClonePlayerID()) + { + if (!IsPropertySet(kPropFullyDisabled)) + { + ISetActualTime(0); + Play(); + //Activate(true); + } + } + return true; + } + + plPseudoLinkEffectMsg *psmsg = plPseudoLinkEffectMsg::ConvertNoRef( pMsg ); + if( psmsg != nil) + { + if (psmsg->fAvatarKey->GetUoid().GetClonePlayerID() == GetKey()->GetUoid().GetClonePlayerID()) + { + if (!IsPropertySet(kPropFullyDisabled)) + { + ISetActualTime(0); + //Play(); + Activate(true); + } + } + return true; + } + + plAvatarStealthModeMsg *sMsg = plAvatarStealthModeMsg::ConvertNoRef(pMsg); + if (sMsg) + { + if (sMsg->GetSender()->GetUoid().GetClonePlayerID() == GetKey()->GetUoid().GetClonePlayerID()) + { + SetProperty(kPropFullyDisabled, (sMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked)); + plNetApp::StaticDebugMsg("plWin32LinkSound: rcvd avatarStealth msg, cloaked=%d", sMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked); + } + return true; + } + + return plWin32StaticSound::MsgReceive( pMsg ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.h new file mode 100644 index 00000000..b1c47dc3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.h @@ -0,0 +1,80 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plWin32StaticSound_h +#define plWin32StaticSound_h + +#include "plWin32Sound.h" + +class hsResMgr; +class plDSoundBuffer; +class plEventCallbackMsg; + +#include "plSoundEvent.h" + +class plWin32StaticSound : public plWin32Sound +{ +public: + plWin32StaticSound(); + ~plWin32StaticSound(); + + CLASSNAME_REGISTER( plWin32StaticSound ); + GETINTERFACE_ANY( plWin32StaticSound, plWin32Sound ); + + virtual void Activate( hsBool forcePlay = false ); + virtual void DeActivate(); + virtual hsBool LoadSound( hsBool is3D ); + virtual void Update(); + virtual hsBool MsgReceive(plMessage* pMsg); + virtual void SetStartPos(unsigned bytes){} + +protected: + hsBool fRegisteredOnThread; + + virtual void IDerivedActuallyPlay( void ); + virtual void ISetActualTime( double t ); + virtual float GetActualTimeSec(); + + virtual void IAddCallback( plEventCallbackMsg *pCBMsg ); + virtual void IRemoveCallback( plEventCallbackMsg *pCBMsg ); + +}; + +// Same as a plWin32StaticSound, except this registers for a plLinkEffectBCMsg to play the sound on linking. +class plWin32LinkSound : public plWin32StaticSound +{ +public: + plWin32LinkSound(); + ~plWin32LinkSound() { } + + CLASSNAME_REGISTER( plWin32LinkSound ); + GETINTERFACE_ANY( plWin32LinkSound, plWin32StaticSound ); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + virtual hsBool MsgReceive(plMessage* pMsg); +}; + +#endif //plWin32StaticSound_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp new file mode 100644 index 00000000..8afdf44d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp @@ -0,0 +1,493 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "HeadSpin.h" +#include "hsTimer.h" +#include "hsGeometry3.h" +#include "plgDispatch.h" +#include "plProfile.h" + +#include "plWin32Sound.h" +#include "plWin32StreamingSound.h" +#include "plDSoundBuffer.h" +#include "plAudioSystem.h" + +#include "../plAudioCore/plAudioFileReader.h" +#include "../plAudioCore/plSoundBuffer.h" +#include "../plAudioCore/plSoundDeswizzler.h" +#include "../pnMessage/plSoundMsg.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../plStatusLog/plStatusLog.h" + +#define STREAMING_UPDATE_MS 200 + +plProfile_Extern(MemSounds); +plProfile_CreateAsynchTimer( "Stream Shove Time", "Sound", StreamSndShoveTime ); +plProfile_CreateAsynchTimer( "Stream Swizzle Time", "Sound", StreamSwizzleTime ); +plProfile_Extern( SoundLoadTime ); + +plWin32StreamingSound::plWin32StreamingSound() : +fDataStream(nil), +fBlankBufferFillCounter(0), +fDeswizzler(nil), +fStreamType(kNoStream), +fLastStreamingUpdate(0), +fStopping(false), +fPlayWhenStopped(false), +fStartPos(0) +{ + fBufferLengthInSecs = plgAudioSys::GetStreamingBufferSize(); +} + +plWin32StreamingSound::~plWin32StreamingSound() +{ + /// Call before we delete our dataStream + DeActivate(); + IUnloadDataBuffer(); + + delete fDataStream; + fDataStream = nil; + fSrcFilename[ 0 ] = 0; + + delete fDeswizzler; +} + +void plWin32StreamingSound::DeActivate() +{ + plWin32Sound::DeActivate(); +} + +// Change the filename used by this streaming sound, so we can play a different file +void plWin32StreamingSound::SetFilename(const char *filename, bool isCompressed) +{ + fNewFilename = filename; + fIsCompressed = isCompressed; + fFailed = false; // just in case the last sound failed to load turn this off so it can try to load the new sound +} + +////////////////////////////////////////////////////////////// +// Override, 'cause we don't want to actually LOAD the sound for streaming, +// just make sure it's decompressed and such and ready to stream. +plSoundBuffer::ELoadReturnVal plWin32StreamingSound::IPreLoadBuffer( hsBool playWhenLoaded, hsBool isIncidental /* = false */ ) +{ + if(fPlayWhenStopped) + return plSoundBuffer::kPending; + hsBool sfxPath = fNewFilename.size() ? false : true; + + if( fDataStream != nil && fNewFilename.size() == 0) + return plSoundBuffer::kSuccess; // Already loaded + + if(!ILoadDataBuffer()) + { + return plSoundBuffer::kError; + } + plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded(); + if(!buffer) + return plSoundBuffer::kError; + + // The databuffer also needs to know if the source is compressed or not + if(fNewFilename.length()) + { + buffer->SetFileName(fNewFilename.c_str()); + buffer->SetFlag(plSoundBuffer::kStreamCompressed, fIsCompressed); + fNewFilename.clear(); + if(fReallyPlaying) + { + fPlayWhenStopped = true; + return plSoundBuffer::kPending; + } + } + + if( buffer->IsValid() ) + { + plAudioFileReader::StreamType type = plAudioFileReader::kStreamNative; + fStreamType = kStreamCompressed; + + if(!buffer->HasFlag(plSoundBuffer::kStreamCompressed)) + { + if(buffer->GetDataLengthInSecs() > plgAudioSys::GetStreamFromRAMCutoff( )) + { + fStreamType = kStreamFromDisk; + type = plAudioFileReader::kStreamWAV; + } + else + { + fStreamType = kStreamFromRAM; + type = plAudioFileReader::kStreamRAM; + } + } + + if(!fStartPos) + { + if(buffer->AsyncLoad(type, isIncidental ? 0 : STREAMING_BUFFERS * STREAM_BUFFER_SIZE ) == plSoundBuffer::kPending) + { + fPlayWhenLoaded = playWhenLoaded; + fLoading = true; + return plSoundBuffer::kPending; + } + } + + char str[ 256 ]; + strncpy( fSrcFilename, buffer->GetFileName(), sizeof( fSrcFilename ) ); + bool streamCompressed = (buffer->HasFlag(plSoundBuffer::kStreamCompressed) != 0); + + delete fDataStream; + fDataStream = buffer->GetAudioReader(); + if(!fDataStream) + { + plAudioCore::ChannelSelect select = buffer->GetReaderSelect(); + + bool streamCompressed = (buffer->HasFlag(plSoundBuffer::kStreamCompressed) != 0); + + /// Open da file + CHAR strPath[ MAX_PATH ]; + + _getcwd(strPath, MAX_PATH); + if(sfxPath) + strcat( strPath, "\\sfx\\" ); + else + { + // if we've changing the filename don't append 'sfx', just append a '\' since a path to the folder is expected + strcat( strPath, "\\"); + } + strcat( strPath, fSrcFilename ); + fDataStream = plAudioFileReader::CreateReader(strPath, select,type); + } + + if( fDataStream == nil || !fDataStream->IsValid() ) + { + delete fDataStream; + fDataStream = nil; + return plSoundBuffer::kError; + } + + sprintf( str, " Readied file %s for streaming", fSrcFilename ); + IPrintDbgMessage( str ); + + // dont free sound data until we have a chance to use it in load sound + + return fDataStream ? plSoundBuffer::kSuccess : plSoundBuffer::kError; + } + + plStatusLog::AddLineS("audio.log", "EnsureLoadable failed for streaming sound %d", fDataBufferKey->GetName()); + return plSoundBuffer::kError; +} + +void plWin32StreamingSound::IFreeBuffers( void ) +{ + plWin32Sound::IFreeBuffers(); + if( fLoadFromDiskOnDemand && !IsPropertySet( kPropLoadOnlyOnCall ) ) + { + // if the audio system has just restarted, dont delete the datastream. This way we can pick up where we left off instead of restarting the sound. + if(!plgAudioSys::IsRestarting()) + { + // we are deleting the stream, we must release the sound data. + FreeSoundData(); + delete fDataStream; + fDataStream = nil; + } + fSrcFilename[ 0 ] = 0; + } +} + +/////////////////////////////////////////////////////////////////// +// Overload from plSound. Basically sets up the streaming file and fills the +// first half of our buffer. We'll fill the rest of the buffer as we get +// notifications for such. + +hsBool plWin32StreamingSound::LoadSound( hsBool is3D ) +{ + if( fFailed ) + return false; + if( !plgAudioSys::Active() || fDSoundBuffer ) + return false; + + if( fPriority > plgAudioSys::GetPriorityCutoff() ) + return false; // Don't set the failed flag, just return + + // Debug flag #1 + if( is3D && fChannelSelect > 0 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableRightSelect ) ) + { + // Force a fail + fFailed = true; + return false; + } + plSoundBuffer::ELoadReturnVal retVal = IPreLoadBuffer(true); + if(retVal == plSoundBuffer::kPending) + return true; + + if( retVal == plSoundBuffer::kError ) + { + char str[ 256 ]; + sprintf( str, "Unable to open streaming source %s", fDataBufferKey->GetName() ); + IPrintDbgMessage( str, true ); + fFailed = true; + return false; + } + + SetProperty( kPropIs3DSound, is3D ); + + plWAVHeader header = fDataStream->GetHeader(); + UInt32 bufferSize = (UInt32)(fBufferLengthInSecs * header.fAvgBytesPerSec); + + // Debug flag #2 + if( is3D && fChannelSelect == 0 && header.fNumChannels > 1 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableLeftSelect ) ) + { + // Force a fail + fFailed = true; + return false; + } + + if( header.fNumChannels > 1 && is3D ) + { + // We can only do a single channel of 3D sound. So copy over one (later) + bufferSize /= header.fNumChannels; + header.fBlockAlign /= header.fNumChannels; + header.fAvgBytesPerSec /= header.fNumChannels; + header.fNumChannels = 1; + } + + // Actually create the buffer now (always looping) + fDSoundBuffer = TRACKED_NEW plDSoundBuffer( bufferSize, header, is3D, IsPropertySet(kPropLooping), false, true ); + if( !fDSoundBuffer->IsValid() ) + { + fDataStream->Close(); + delete fDataStream; + fDataStream = nil; + + delete fDSoundBuffer; + fDSoundBuffer = nil; + + char str[256]; + sprintf(str, "Can't create sound buffer for %s.wav. This could happen if the wav file is a stereo file. Stereo files are not supported on 3D sounds. If the file is not stereo then please report this error.", GetFileName()); + IPrintDbgMessage( str, true ); + fFailed = true; + return false; + } + + fTotalBytes = (UInt32)bufferSize; + plProfile_NewMem(MemSounds, fTotalBytes); + + plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded(); + if(!buffer) + return false; + + bool setupSource = true; + if(!buffer->GetData() || fStartPos) + { + if(fStartPos && fStartPos <= fDataStream->NumBytesLeft()) + { + fDataStream->SetPosition(fStartPos); + plStatusLog::AddLineS("syncaudio.log", "startpos %d", fStartPos); + } + + // if we get here we are not starting from the beginning of the sound. We still have an audio loaded and need to pick up where we left off + if(!fDSoundBuffer->SetupStreamingSource(fDataStream)) + { + setupSource = false; + } + } + else + { + // this sound is starting from the beginning. Get the data and start it. + if(!fDSoundBuffer->SetupStreamingSource(buffer->GetData(), buffer->GetAsyncLoadLength())) + { + setupSource = false; + } + } + + if(!setupSource) + { + fDataStream->Close(); + delete fDataStream; + fDataStream = nil; + delete fDSoundBuffer; + fDSoundBuffer = nil; + + plStatusLog::AddLineS("audio.log", "Could not play streaming sound, no voices left %s", GetKeyName()); + return false; + } + FreeSoundData(); + + IRefreshEAXSettings( true ); + + // Debug info + char str[ 256 ]; + sprintf( str, " Streaming %s.", fSrcFilename); + IPrintDbgMessage( str ); + + plStatusLog::AddLineS( "audioTimes.log", 0xffffffff, "Streaming %4.2f secs of %s", fDataStream->GetLengthInSecs(), GetKey()->GetUoid().GetObjectName() ); + + // Get pertinent info + SetLength( (hsScalar)fDataStream->GetLengthInSecs() ); + + // Set up our deswizzler, if necessary + delete fDeswizzler; + if( fDataStream->GetHeader().fNumChannels != header.fNumChannels ) + fDeswizzler = TRACKED_NEW plSoundDeswizzler( (UInt32)(fBufferLengthInSecs * fDataStream->GetHeader().fAvgBytesPerSec), + (UInt8)(fDataStream->GetHeader().fNumChannels), + header.fBitsPerSample / 8 ); + else + fDeswizzler = nil; + + // LEAVE THE WAV FILE OPEN! (We *are* streaming, after all :) + return true; +} + +void plWin32StreamingSound::IStreamUpdate() +{ + if(!fReallyPlaying) + return; + + if(hsTimer::GetMilliSeconds() - fLastStreamingUpdate < STREAMING_UPDATE_MS) // filter out update requests so we aren't doing this more that we need to + return; + + plProfile_BeginTiming( StreamSndShoveTime ); + if(fDSoundBuffer) + { + if(fDSoundBuffer->BuffersQueued() == 0 && fDataStream->NumBytesLeft() == 0 && !fDSoundBuffer->IsLooping()) + { + // If we are fading out it's possible that we will hit this multiple times causing this sound to try and fade out multiple times, never allowing it to actually stop + if(!fStopping) + { + fStopping = true; + Stop(); + plProfile_EndTiming( StreamSndShoveTime ); + } + return; + } + + if(!fDSoundBuffer->StreamingFillBuffer(fDataStream)) + { + plStatusLog::AddLineS("audio.log", "%s Streaming buffer fill failed", GetKeyName()); + } + } + plProfile_EndTiming( StreamSndShoveTime ); +} + +void plWin32StreamingSound::Update() +{ + plWin32Sound::Update(); + IStreamUpdate(); +} + +void plWin32StreamingSound::IDerivedActuallyPlay( void ) +{ + fStopping = false; + if( !fReallyPlaying ) + { + for(;;) + { + if(fSynchedStartTimeSec) + { + // if we are synching to another sound this is our latency time + fDSoundBuffer->SetTimeOffsetSec((float)(hsTimer::GetSeconds() - fSynchedStartTimeSec)); + fSynchedStartTimeSec = 0; + } + + if(IsPropertySet(kPropIncidental)) + { + if(fIncidentalsPlaying >= MAX_INCIDENTALS) + break; + ++fIncidentalsPlaying; + } + fDSoundBuffer->Play(); + fReallyPlaying = true; + break; + } + } + + /// Send start callbacks + plSoundEvent *event = IFindEvent( plSoundEvent::kStart ); + if( event != nil ) + event->SendCallbacks(); +} + +void plWin32StreamingSound::IActuallyStop() +{ + fStopping = false; + plWin32Sound::IActuallyStop(); + if(fPlayWhenStopped) + { + fPlayWhenStopped = false; + Play(); + } +} + +unsigned plWin32StreamingSound::GetByteOffset() +{ + if(fDataStream && fDSoundBuffer) + { + unsigned bytesQueued = fDSoundBuffer->BuffersQueued() * STREAM_BUFFER_SIZE; + unsigned offset = fDSoundBuffer->GetByteOffset(); + long byteoffset = ((fDataStream->GetDataSize() - fDataStream->NumBytesLeft()) - bytesQueued) + offset; + + return byteoffset < 0 ? fDataStream->GetDataSize() - abs(byteoffset) : byteoffset; + } + return 0; +} + +float plWin32StreamingSound::GetActualTimeSec() +{ + if(fDataStream && fDSoundBuffer) + return fDSoundBuffer->BytePosToMSecs(fDataStream->NumBytesLeft()) / 1000.0f; + return 0.0f; +} + +void plWin32StreamingSound::SetStartPos(unsigned bytes) +{ + fStartPos = bytes; +} + +void plWin32StreamingSound::ISetActualTime( double t ) +{ + //fStartTimeSec = 0; + if(fDataStream && fDSoundBuffer) + fDataStream->SetPosition(fDSoundBuffer->GetBufferBytePos((float)t)); + //else + // fStartTimeSec = t; +} + +hsBool plWin32StreamingSound::MsgReceive( plMessage* pMsg ) +{ + return plWin32Sound::MsgReceive( pMsg ); +} + +void plWin32StreamingSound::IRemoveCallback( plEventCallbackMsg *pCBMsg ) +{ + plWin32Sound::IRemoveCallback( pCBMsg ); +} + +void plWin32StreamingSound::IAddCallback( plEventCallbackMsg *pCBMsg ) +{ + if( plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStop && + plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStart ) + { + hsAssert( false, "Streaming sounds only support start and stop callbacks at this time." ); + return; + } + plWin32Sound::IAddCallback( pCBMsg ); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.h new file mode 100644 index 00000000..09c2228c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.h @@ -0,0 +1,90 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plWin32StreamingSound_h +#define plWin32StreamingSound_h + +#include "plWin32Sound.h" +#include "../pnUtils/pnUtils.h" + +class plDSoundBuffer; +class DSoundCallbackHandle; +class plAudioFileReader; +class plStreamingSoundThread; +enum CallbackHandleType; +class plSoundDeswizzler; + +class plWin32StreamingSound : public plWin32Sound +{ +public: + plWin32StreamingSound(); + ~plWin32StreamingSound(); + + CLASSNAME_REGISTER( plWin32StreamingSound ); + GETINTERFACE_ANY( plWin32StreamingSound, plWin32Sound ); + + virtual void DeActivate(); + virtual hsBool LoadSound( hsBool is3D ); + virtual float GetActualTimeSec(); + virtual unsigned GetByteOffset(); + virtual StreamType GetStreamType() const { return fStreamType; } + virtual void SetFilename(const char *filename, bool isCompressed); + virtual void Update(); // temp + void StreamUpdate(); + virtual hsBool MsgReceive( plMessage *pMsg ); + +protected: + hsScalar fTimeAtBufferStart; + plAudioFileReader *fDataStream; + hsScalar fBufferLengthInSecs; + UInt8 fBlankBufferFillCounter; + plSoundDeswizzler *fDeswizzler; + char fSrcFilename[ 256 ]; + StreamType fStreamType; + bool fIsCompressed; // this applies only to the new sound file specified in fNewFilename, so we can play both ogg's and wav's + std::string fNewFilename; // allow the filename to be changed so we can play from a different source. + // ultimately this filename will be given to fDataBuffer, but since it's not always around we'll store it here + hsBool fStopping; + + double fLastStreamingUpdate; + bool fPlayWhenStopped; + unsigned fStartPos; + + hsScalar IGetTimeAtBufferStart( void ) { return fTimeAtBufferStart; } + virtual void SetStartPos(unsigned bytes); + + virtual void IDerivedActuallyPlay( void ); + void IActuallyStop(); + virtual void ISetActualTime( double t ); + + virtual void IAddCallback( plEventCallbackMsg *pMsg ); + virtual void IRemoveCallback( plEventCallbackMsg *pMsg ); + + virtual void IFreeBuffers( void ); + void IStreamUpdate(); + virtual plSoundBuffer::ELoadReturnVal IPreLoadBuffer( hsBool playWhenLoaded, hsBool isIncidental = false ); +}; + +#endif //plWin32StreamingSound_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.cpp new file mode 100644 index 00000000..ce4a871c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.cpp @@ -0,0 +1,361 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plWinMicLevel - Annoying class to deal with the annoying problem of // +// setting the microphone recording volume in Windows. // +// Yeah, you'd THINK there'd be some easier way... // +// // +//// Notes /////////////////////////////////////////////////////////////////// +// // +// 5.8.2001 - Created by mcn. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plWinMicLevel.h" +#include "hsWindows.h" +#include + + +//// Our Local Static Data /////////////////////////////////////////////////// + +int sNumMixers = 0; +HMIXER sMixerHandle = nil; +MIXERCAPS sMixerCaps; + +DWORD sMinValue = 0, sMaxValue = 0; +DWORD sVolControlID = 0; + + +//// Local Static Helpers //////////////////////////////////////////////////// + +hsBool IGetMuxMicVolumeControl( void ); +hsBool IGetBaseMicVolumeControl( void ); + +hsBool IGetControlValue( DWORD &value ); +hsBool ISetControlValue( DWORD value ); + +MIXERLINE *IGetLineByType( DWORD type ); +MIXERLINE *IGetLineByID( DWORD id ); +MIXERCONTROL *IGetControlByType( MIXERLINE *line, DWORD type ); +MIXERLINE *IGetMixerSubLineByType( MIXERCONTROL *mux, DWORD type ); + + +//// The Publics ///////////////////////////////////////////////////////////// + +hsScalar plWinMicLevel::GetLevel( void ) +{ + if( !CanSetLevel() ) + return -1; + + DWORD rawValue; + if( !IGetControlValue( rawValue ) ) + return -1; + + return (hsScalar)( rawValue - sMinValue ) / (hsScalar)( sMaxValue - sMinValue ); +} + +void plWinMicLevel::SetLevel( hsScalar level ) +{ + if( !CanSetLevel() ) + return; + + DWORD rawValue = (DWORD)(( level * ( sMaxValue - sMinValue ) ) + sMinValue); + + ISetControlValue( rawValue ); +} + +hsBool plWinMicLevel::CanSetLevel( void ) +{ + // Just to init + plWinMicLevel &instance = IGetInstance(); + + return ( sMixerHandle != nil ) ? true : false; +} + + +//// Protected Init Stuff //////////////////////////////////////////////////// + +plWinMicLevel &plWinMicLevel::IGetInstance( void ) +{ + static plWinMicLevel sInstance; + return sInstance; +} + +plWinMicLevel::plWinMicLevel() +{ + sMixerHandle = nil; + memset( &sMixerCaps, 0, sizeof( sMixerCaps ) ); + + // Get the number of mixers in the system + sNumMixers = ::mixerGetNumDevs(); + + // So long as we have one, open the first one + if( sNumMixers == 0 ) + return; + + if( ::mixerOpen( &sMixerHandle, 0, + nil, // Window handle to receive callback messages + nil, MIXER_OBJECTF_MIXER ) != MMSYSERR_NOERROR ) + { + sMixerHandle = nil; // Just to be sure + return; + } + + if( ::mixerGetDevCaps( (UINT)sMixerHandle, &sMixerCaps, sizeof( sMixerCaps ) ) != MMSYSERR_NOERROR ) + { + // Oh well, who cares + } + + // Try to get the Mux/mixer-based mic volume control first, since that seems to work better/more often/at all + if( !IGetMuxMicVolumeControl() ) + { + // Failed, so try getting the volume control from the base mic-in line + if( !IGetBaseMicVolumeControl() ) + { + IShutdown(); + return; + } + } +} + +plWinMicLevel::~plWinMicLevel() +{ + IShutdown(); +} + +void plWinMicLevel::IShutdown( void ) +{ + if( sMixerHandle != nil ) + ::mixerClose( sMixerHandle ); + + sMixerHandle = nil; +} + +//// IGetMuxMicVolumeControl ///////////////////////////////////////////////// +// Tries to get the volume control of the microphone subline of the MUX or +// mixer control of the WaveIn destination line of the audio system (whew!) +// Note: testing indcates that this works but the direct SRC_MICROPHONE +// doesn't, hence we try this one first. + +hsBool IGetMuxMicVolumeControl( void ) +{ + if( sMixerHandle == nil ) + return false; + + // Get the WaveIn destination line + MIXERLINE *waveInLine = IGetLineByType( MIXERLINE_COMPONENTTYPE_DST_WAVEIN ); + if( waveInLine == nil ) + return false; + + // Get the mixer or MUX controller from the line + MIXERCONTROL *control = IGetControlByType( waveInLine, MIXERCONTROL_CONTROLTYPE_MIXER ); + if( control == nil ) + control = IGetControlByType( waveInLine, MIXERCONTROL_CONTROLTYPE_MUX ); + if( control == nil ) + return false; + + // Get the microphone sub-component + // Note: this eventually calls IGetLineByType(), which destroys the waveInLine pointer we had before + MIXERLINE *micLine = IGetMixerSubLineByType( control, MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE ); + if( micLine == nil ) + return false; + + // Get the volume subcontroller + MIXERCONTROL *micVolCtrl = IGetControlByType( micLine, MIXERCONTROL_CONTROLTYPE_VOLUME ); + if( micVolCtrl == nil ) + return false; + + // Found it! store our values + char *dbgLineName = micLine->szName; + char *dbgControlName = micVolCtrl->szName; + sMinValue = micVolCtrl->Bounds.dwMinimum; + sMaxValue = micVolCtrl->Bounds.dwMaximum; + sVolControlID = micVolCtrl->dwControlID; + + return true; +} + +//// IGetBaseMicVolumeControl //////////////////////////////////////////////// +// Tries to get the volume control of the mic-in line. See +// IGetMuxMicVolumeControl for why we don't do this one first. + +hsBool IGetBaseMicVolumeControl( void ) +{ + if( sMixerHandle == nil ) + return false; + + // Get the mic source line + MIXERLINE *micLine = IGetLineByType( MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE ); + if( micLine == nil ) + return false; + + // Get the volume subcontroller + MIXERCONTROL *micVolCtrl = IGetControlByType( micLine, MIXERCONTROL_CONTROLTYPE_VOLUME ); + if( micVolCtrl == nil ) + return false; + + // Found it! store our values + char *dbgLineName = micLine->szName; + char *dbgControlName = micVolCtrl->szName; + sMinValue = micVolCtrl->Bounds.dwMinimum; + sMaxValue = micVolCtrl->Bounds.dwMaximum; + sVolControlID = micVolCtrl->dwControlID; + + return true; +} + + +//// IGetControlValue //////////////////////////////////////////////////////// +// Gets the raw value of the current volume control. + +hsBool IGetControlValue( DWORD &value ) +{ + if( sMixerHandle == nil ) + return false; + + MIXERCONTROLDETAILS_UNSIGNED mxcdVolume; + MIXERCONTROLDETAILS mxcd; + mxcd.cbStruct = sizeof( MIXERCONTROLDETAILS ); + mxcd.dwControlID = sVolControlID; + mxcd.cChannels = 1; + mxcd.cMultipleItems = 0; + mxcd.cbDetails = sizeof( MIXERCONTROLDETAILS_UNSIGNED ); + mxcd.paDetails = &mxcdVolume; + + if( ::mixerGetControlDetails( (HMIXEROBJ)sMixerHandle, &mxcd, + MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE ) != MMSYSERR_NOERROR ) + return false; + + value = mxcdVolume.dwValue; + return true; +} + +//// ISetControlValue //////////////////////////////////////////////////////// +// Sets the raw value of the current volume control. + +hsBool ISetControlValue( DWORD value ) +{ + if( sMixerHandle == nil ) + return false; + + MIXERCONTROLDETAILS_UNSIGNED mxcdVolume = { value }; + MIXERCONTROLDETAILS mxcd; + mxcd.cbStruct = sizeof( MIXERCONTROLDETAILS ); + mxcd.dwControlID = sVolControlID; + mxcd.cChannels = 1; + mxcd.cMultipleItems = 0; + mxcd.cbDetails = sizeof( MIXERCONTROLDETAILS_UNSIGNED ); + mxcd.paDetails = &mxcdVolume; + + if( ::mixerSetControlDetails( (HMIXEROBJ)sMixerHandle, &mxcd, + MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE ) != MMSYSERR_NOERROR ) + return false; + + return true; +} + + +//// Helper Functions //////////////////////////////////////////////////////// + +MIXERLINE *IGetLineByType( DWORD type ) +{ + static MIXERLINE mxl; + + mxl.cbStruct = sizeof( MIXERLINE ); + mxl.dwComponentType = type; + if( ::mixerGetLineInfo( (HMIXEROBJ)sMixerHandle, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE ) != MMSYSERR_NOERROR ) + return nil; + + return &mxl; +} + +MIXERLINE *IGetLineByID( DWORD id ) +{ + static MIXERLINE mxl; + + mxl.cbStruct = sizeof( MIXERLINE ); + mxl.dwLineID = id; + if( ::mixerGetLineInfo( (HMIXEROBJ)sMixerHandle, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_LINEID ) != MMSYSERR_NOERROR ) + return nil; + + return &mxl; +} + +MIXERCONTROL *IGetControlByType( MIXERLINE *line, DWORD type ) +{ + static MIXERCONTROL mxc; + + MIXERLINECONTROLS mxlc; + mxlc.cbStruct = sizeof( MIXERLINECONTROLS ); + mxlc.dwLineID = line->dwLineID; + mxlc.dwControlType = type; + mxlc.cControls = 1; + mxlc.cbmxctrl = sizeof( MIXERCONTROL ); + mxlc.pamxctrl = &mxc; + if( ::mixerGetLineControls( (HMIXEROBJ)sMixerHandle, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE ) != MMSYSERR_NOERROR ) + return nil; + + return &mxc; +} + +MIXERLINE *IGetMixerSubLineByType( MIXERCONTROL *mux, DWORD type ) +{ + // A mixer or MUX is really a combination of MORE lines. And beautifully, you can't + // just ask for a single one off of it, you have to ask for them all and search through yourself + MIXERCONTROLDETAILS_LISTTEXT *lineInfo = TRACKED_NEW MIXERCONTROLDETAILS_LISTTEXT[ mux->cMultipleItems ]; + if( lineInfo == nil ) + return nil; + + MIXERCONTROLDETAILS details; + details.cbStruct = sizeof( MIXERCONTROLDETAILS ); + details.dwControlID = mux->dwControlID; + details.cChannels = 1; + details.cMultipleItems = mux->cMultipleItems; + details.cbDetails = sizeof( MIXERCONTROLDETAILS_LISTTEXT ); + details.paDetails = lineInfo; + if( ::mixerGetControlDetails( (HMIXEROBJ)sMixerHandle, &details, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_LISTTEXT ) != MMSYSERR_NOERROR ) + { + delete [] lineInfo; + return nil; + } + + // Loop through and find the one with the right component type. But of course it doesn't give us that offhand... + for( unsigned int i = 0; i < mux->cMultipleItems; i++ ) + { + MIXERLINE *line = IGetLineByID( lineInfo[ i ].dwParam1 ); + if( line->dwComponentType == type ) + { + delete [] lineInfo; + return line; + } + } + + delete [] lineInfo; + return nil; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.h new file mode 100644 index 00000000..797caf8f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plWinMicLevel_h +#define _plWinMicLevel_h + +////////////////////////////////////////////////////////////////////////////// +// // +// plWinMicLevel - Annoying class to deal with the annoying problem of // +// setting the microphone recording volume in Windows. // +// Yeah, you'd THINK there'd be some easier way... // +// // +//// Notes /////////////////////////////////////////////////////////////////// +// // +// 5.8.2001 - Created by mcn. // +// // +////////////////////////////////////////////////////////////////////////////// + + +//// Class Definition //////////////////////////////////////////////////////// + +class plWinMicLevel +{ +public: + + ~plWinMicLevel(); + // Gets the microphone volume, range 0-1, -1 if error + static hsScalar GetLevel( void ); + + // Sets the microphone volume, range 0-1 + static void SetLevel( hsScalar level ); + + // Returns whether we can set the level + static hsBool CanSetLevel( void ); + +protected: + plWinMicLevel(); // Protected constructor for IGetInstance. Just to init some stuff + static plWinMicLevel &IGetInstance( void ); + void IShutdown( void ); +}; + +#endif // _plWinMicLevel_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioCore.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioCore.h new file mode 100644 index 00000000..f1bb07ab --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioCore.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plAudioCore - Core, common stuff that all of the audio system needs // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plAudioCore_h +#define _plAudioCore_h + +//// plWAVHeader Class /////////////////////////////////////////////////////// +// Just a small info class about WAV sound + +class plWAVHeader +{ + public: + UInt16 fFormatTag; + UInt16 fNumChannels; + UInt32 fNumSamplesPerSec; + UInt32 fAvgBytesPerSec; + UInt16 fBlockAlign; + UInt16 fBitsPerSample; + + enum + { + kPCMFormatTag = 1 + }; +}; + +//// plAudioCore Konstants /////////////////////////////////////////////////// + +namespace plAudioCore +{ + enum ChannelSelect + { + kAll = 0, + kLeft, + kRight + }; + +}; + + +#endif //_plAudioCore_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioCoreCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioCoreCreatable.h new file mode 100644 index 00000000..d0f6fa1b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioCoreCreatable.h @@ -0,0 +1,36 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAudioCoreCreatable_inc +#define plAudioCoreCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plSoundBuffer.h" + +REGISTER_CREATABLE( plSoundBuffer ); + +#endif // plAudioCoreCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioFileReader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioFileReader.cpp new file mode 100644 index 00000000..82d2c60d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioFileReader.cpp @@ -0,0 +1,169 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plAudioFileReader // +// // +//// Notes /////////////////////////////////////////////////////////////////// +// // +// 3.5.2001 - Created by mcn. // +// // +////////////////////////////////////////////////////////////////////////////// +#include + +#include "hsTypes.h" +#include "plAudioFileReader.h" +#include "plAudioCore.h" +//#include "hsTimer.h" +#include "hsUtils.h" +#include "../plFile/hsFiles.h" +#include "../plFile/plFileUtils.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "plBufferedFileReader.h" +#include "plFastWavReader.h" +#include "../plAudio/plOGGCodec.h" +#include "../plAudio/plWavFile.h" + +#define kCacheDirName "streamingCache" + +static void hsStrUpper(char *s) +{ + if (s) + { + int len = hsStrlen(s); + for (int i = 0; i < len; i++) + s[i] = toupper(s[i]); + } +} + +plAudioFileReader* plAudioFileReader::CreateReader(const char* path, plAudioCore::ChannelSelect whichChan, StreamType type) +{ + const char* ext = plFileUtils::GetFileExt(path); + + if (type == kStreamWAV) + { + bool isWav = (stricmp(ext, "wav") == 0); + // We want to stream a wav off disk, but this is a compressed file. + // Get the uncompressed path. Ignore the requested channel, since it + // will have already been split into two files if that is necessary. + if (!isWav) + { + char cachedPath[256]; + IGetCachedPath(path, cachedPath, whichChan); + plAudioFileReader *r = TRACKED_NEW plFastWAV(cachedPath, plAudioCore::kAll); + return r; + } + + plAudioFileReader *r = TRACKED_NEW plFastWAV(path, whichChan); + return r; + } + else if (type == kStreamRAM) + return TRACKED_NEW plBufferedFileReader(path, whichChan); + else if (type == kStreamNative) + return TRACKED_NEW plOGGCodec(path, whichChan); + + return nil; +} + +plAudioFileReader* plAudioFileReader::CreateWriter(const char* path, plWAVHeader& header) +{ + const char* ext = plFileUtils::GetFileExt(path); + + plAudioFileReader* writer = TRACKED_NEW CWaveFile(path, plAudioCore::kAll); + writer->OpenForWriting(path, header); + return writer; +} + +void plAudioFileReader::IGetCachedPath(const char* path, char* cachedPath, plAudioCore::ChannelSelect whichChan) +{ + // Get the file's path and add our streaming cache folder to it + strcpy(cachedPath, path); + plFileUtils::StripFile(cachedPath); + strcat(cachedPath, kCacheDirName"\\"); + + // Create the directory first + plFileUtils::CreateDir(cachedPath); + + // Get the path to the cached version of the file, without the extension + const char* fileName = plFileUtils::GetFileName(path); + const char* fileExt = plFileUtils::GetFileExt(fileName); + strncat(cachedPath, fileName, fileExt-fileName-1); + + if (whichChan == plAudioCore::kLeft) + strcat(cachedPath, "-Left.wav"); + else if (whichChan == plAudioCore::kRight) + strcat(cachedPath, "-Right.wav"); + else if (whichChan == plAudioCore::kAll) + strcat(cachedPath, ".wav"); +} + +void plAudioFileReader::ICacheFile(const char* path, bool noOverwrite, plAudioCore::ChannelSelect whichChan) +{ + char cachedPath[256]; + IGetCachedPath(path, cachedPath, whichChan); + if (!noOverwrite || !plFileUtils::FileExists(cachedPath)) + { + plAudioFileReader* reader = plAudioFileReader::CreateReader(path, whichChan, kStreamNative); + if (!reader || !reader->IsValid()) + { + delete reader; + return; + } + plAudioFileReader* writer = CreateWriter(cachedPath, reader->GetHeader()); + if (!writer || !writer->IsValid()) + { + delete reader; + delete writer; + return; + } + + UInt8 buffer[4096]; + UInt32 numLeft; + while ((numLeft = reader->NumBytesLeft()) > 0) + { + UInt32 toRead = (numLeft < sizeof(buffer)) ? numLeft : sizeof(buffer); + reader->Read(toRead, buffer); + writer->Write(toRead, buffer); + } + writer->Close(); + + delete writer; + delete reader; + } +} + +void plAudioFileReader::CacheFile(const char* path, bool splitChannels, bool noOverwrite) +{ + if (splitChannels) + { + ICacheFile(path, noOverwrite, plAudioCore::kLeft); + ICacheFile(path, noOverwrite, plAudioCore::kRight); + } + else + { + ICacheFile(path, noOverwrite, plAudioCore::kAll); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioFileReader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioFileReader.h new file mode 100644 index 00000000..76e7556e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plAudioFileReader.h @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plAudioFileReader - Microsoft's way of making our lives difficult when // +// reading in .WMA files. Hacking Winamp's plugins is // +// probably easier... // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plAudioFileReader_h +#define _plAudioFileReader_h + +#include "plAudioCore.h" +#include "hsTemplates.h" + +//// Class Definition //////////////////////////////////////////////////////// + +class plUnifiedTime; +class plWAVHeader; +class plAudioFileReader +{ +public: + virtual ~plAudioFileReader() {} + virtual plWAVHeader &GetHeader( void ) = 0; + + enum StreamType + { + kStreamRAM, // Stream from a WAV loaded into RAM + kStreamWAV, // Stream from a WAV on disk + kStreamNative, // Stream from the native type (ie, an Ogg on disk) + }; + + virtual void Open(){} + virtual void Close( void ) = 0; + + virtual UInt32 GetDataSize( void ) = 0; + virtual float GetLengthInSecs( void ) = 0; + + virtual hsBool SetPosition( UInt32 numBytes ) = 0; + virtual hsBool Read( UInt32 numBytes, void *buffer ) = 0; + virtual UInt32 NumBytesLeft( void ) = 0; + + virtual hsBool OpenForWriting( const char *path, plWAVHeader &header ) { return false; } + virtual UInt32 Write( UInt32 bytes, void *buffer ) { return 0; } + + virtual hsBool IsValid( void ) = 0; + + static plAudioFileReader* CreateReader(const char* path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll, StreamType type = kStreamWAV); + static plAudioFileReader* CreateWriter(const char* path, plWAVHeader& header); + + // Decompresses a compressed file to the cache directory + static void CacheFile(const char* path, bool splitChannels=false, bool noOverwrite=false); + +protected: + static void IGetCachedPath(const char* path, char* cachedPath, plAudioCore::ChannelSelect whichChan); + static void ICacheFile(const char* path, bool noOverwrite, plAudioCore::ChannelSelect whichChan); +}; + +#endif //_plAudioFileReader_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plBufferedFileReader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plBufferedFileReader.cpp new file mode 100644 index 00000000..2e8a9201 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plBufferedFileReader.cpp @@ -0,0 +1,166 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plBufferedFileReader - Reads in a given file into a RAM buffer, then // +// "reads" from that buffer as requested. Useless // +// for normal sounds, but perfect for streaming // +// from RAM. // +// // +//// Notes /////////////////////////////////////////////////////////////////// +// // +// 11.1.2002 - Created by mcn. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include "hsTypes.h" +#include "plBufferedFileReader.h" +//#include "plProfile.h" + + +//plProfile_CreateMemCounter( "BufferedRAM", "Sound", SndBufferedMem ); + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plBufferedFileReader::plBufferedFileReader( const char *path, plAudioCore::ChannelSelect whichChan ) +{ + // Init some stuff + fBufferSize = 0; + fBuffer = nil; + fCursor = 0; + + hsAssert( path != nil, "Invalid path specified in plBufferedFileReader" ); + + // Ask plAudioFileReader for another reader to get this file + // Note: have this reader do the chanSelect for us + plAudioFileReader *reader = plAudioFileReader::CreateReader( path, whichChan ); + if( reader == nil || !reader->IsValid() ) + { + delete reader; + IError( "Unable to open file to read in to RAM buffer" ); + return; + } + + fHeader = reader->GetHeader(); + + fBufferSize = reader->GetDataSize(); + fBuffer = TRACKED_NEW UInt8[ fBufferSize ]; + //plProfile_NewMem( SndBufferedMem, fBufferSize ); + if( fBuffer == nil ) + { + delete reader; + IError( "Unable to allocate RAM buffer" ); + return; + } + + if( !reader->Read( fBufferSize, fBuffer ) ) + { + delete reader; + IError( "Unable to read file into RAM buffer" ); + return; + } + + // All done! + delete reader; +} + +plBufferedFileReader::~plBufferedFileReader() +{ + Close(); +} + +void plBufferedFileReader::Close( void ) +{ + //plProfile_DelMem( SndBufferedMem, fBufferSize ); + + DEL(fBuffer);; + fBuffer = nil; + fBufferSize = 0; + fCursor = 0; +} + +void plBufferedFileReader::IError( const char *msg ) +{ + hsAssert( false, msg ); + Close(); +} + +plWAVHeader &plBufferedFileReader::GetHeader( void ) +{ + hsAssert( IsValid(), "GetHeader() called on an invalid RAM buffer" ); + + return fHeader; +} + +float plBufferedFileReader::GetLengthInSecs( void ) +{ + hsAssert( IsValid(), "GetLengthInSecs() called on an invalid RAM buffer" ); + + return (float)fBufferSize / (float)fHeader.fAvgBytesPerSec; +} + +hsBool plBufferedFileReader::SetPosition( UInt32 numBytes ) +{ + hsAssert( IsValid(), "SetPosition() called on an invalid RAM buffer" ); + + if( numBytes > fBufferSize ) + { + hsAssert( false, "Invalid position in SetPosition()" ); + return false; + } + + fCursor = numBytes; + return true; +} + +hsBool plBufferedFileReader::Read( UInt32 numBytes, void *buffer ) +{ + hsAssert( IsValid(), "Read() called on an invalid RAM buffer" ); + + + hsBool valid = true; + + if( fCursor + numBytes > fBufferSize ) + { + numBytes = fBufferSize - fCursor; + valid = false; + } + + memcpy( buffer, fBuffer + fCursor, numBytes ); + fCursor += numBytes; + + return valid; +} + +UInt32 plBufferedFileReader::NumBytesLeft( void ) +{ + hsAssert( IsValid(), "NumBytesLeft() called on an invalid RAM buffer" ); + + return fBufferSize - fCursor; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plBufferedFileReader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plBufferedFileReader.h new file mode 100644 index 00000000..8611ac8d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plBufferedFileReader.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plBufferedFileReader - Reads in a given file into a RAM buffer, then // +// "reads" from that buffer as requested. Useless // +// for normal sounds, but perfect for streaming // +// from RAM. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plBufferedFileReader_h +#define _plBufferedFileReader_h + +#include "../plAudioCore/plAudioFileReader.h" + + +//// Class Definition //////////////////////////////////////////////////////// + +class plBufferedFileReader : public plAudioFileReader +{ +public: + plBufferedFileReader( const char *path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll ); + virtual ~plBufferedFileReader(); + + virtual plWAVHeader &GetHeader( void ); + virtual void Close( void ); + virtual UInt32 GetDataSize( void ) { return fBufferSize; } + virtual float GetLengthInSecs( void ); + virtual hsBool SetPosition( UInt32 numBytes ); + virtual hsBool Read( UInt32 numBytes, void *buffer ); + virtual UInt32 NumBytesLeft( void ); + virtual hsBool IsValid( void ) { return ( fBuffer != nil ) ? true : false; } + +protected: + UInt32 fBufferSize; + UInt8 *fBuffer; + plWAVHeader fHeader; + UInt32 fCursor; + void IError( const char *msg ); +}; + +#endif //_plBufferedFileReader_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plFastWavReader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plFastWavReader.cpp new file mode 100644 index 00000000..9a0af433 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plFastWavReader.cpp @@ -0,0 +1,334 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plFastWavReader - Quick, dirty, and highly optimized class for reading // +// in the samples of a WAV file when you're in a hurry. // +// ONLY WORKS WITH PCM (i.e. uncompressed) DATA // +// // +//// Notes /////////////////////////////////////////////////////////////////// +// // +// 11.5.2001 - Created by mcn. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include "hsTypes.h" +#include "plFastWavReader.h" + + +//// Local Helpers /////////////////////////////////////////////////////////// + +class plRIFFChunk +{ + public: + char fID[ 4 ]; + UInt32 fSize; + + void Read( FILE *fp ) + { + fread( fID, 1, 4, fp ); + fread( &fSize, sizeof( UInt32 ), 1, fp ); + } + + bool IsA( const char *type ) + { + return ( memcmp( fID, type, 4 ) == 0 ) ? true : false; + } +}; + +class plRIFFHeader +{ + protected: + plRIFFChunk fChunk; + bool fValid; + char fFormat[ 4 ]; + + public: + plRIFFHeader( FILE *fp ) + { + fValid = false; + fChunk.Read( fp ); + if( fChunk.IsA( "RIFF" ) ) + { + if( fread( &fFormat, 1, 4, fp ) == 4 ) + { + fValid = true; + } + } + } + + bool IsValid( void ) + { + return fValid; + } + + bool IsA( const char *type ) + { + return ( memcmp( fFormat, type, 4 ) == 0 ) ? true : false; + } +}; + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plFastWAV::plFastWAV( const char *path, plAudioCore::ChannelSelect whichChan ) : fFileHandle( nil ) +{ + hsAssert( path != nil, "Invalid path specified in plFastWAV reader" ); + + strncpy( fFilename, path, sizeof( fFilename ) ); + fWhichChannel = whichChan; + + fFileHandle = fopen( path, "rb" ); + if( fFileHandle != nil ) + { + /// Read in our header and calc our start position + plRIFFHeader riffHdr( fFileHandle ); + + if( !riffHdr.IsValid() ) + { + IError( "Invalid RIFF file header in plFastWAV" ); + return; + } + + if( !riffHdr.IsA( "WAVE" ) ) + { + IError( "Invalid RIFF file type in plFastWAV" ); + return; + } + + fChunkStart = ftell( fFileHandle ); + + // Seek and read the "fmt " header + plRIFFChunk chunk; + if( !ISeekToChunk( "fmt ", &chunk ) ) + { + IError( "Unable to find fmt chunk in WAV file" ); + return; + } + + if( fread( &fHeader, 1, sizeof( plWAVHeader ), fFileHandle ) != sizeof( plWAVHeader ) ) + { + IError( "Invalid WAV file header in plFastWAV" ); + return; + } + + // Check format + if( fHeader.fFormatTag != kPCMFormatTag ) + { + IError( "Invalid format in plFastWAV" ); + return; + } + + // Seek to and get the position of the data chunk + if( !ISeekToChunk( "data", &chunk ) ) + { + IError( "Unable to find data chunk in WAV file" ); + return; + } + fDataStartPos = ftell( fFileHandle ); + fDataSize = chunk.fSize; + + // HACKY FIX FOR BAD WAV FILES + fDataSize -= ( fDataSize & ( fHeader.fBlockAlign - 1 ) ); + + if( fWhichChannel != plAudioCore::kAll ) + { + fChannelAdjust = 2; + fChannelOffset = ( fWhichChannel == plAudioCore::kLeft ) ? 0 : 1; + } + else + { + fChannelAdjust = 1; + fChannelOffset = 0; + } + + fFakeHeader = fHeader; + fFakeHeader.fAvgBytesPerSec /= fChannelAdjust; + fFakeHeader.fNumChannels /= (UInt16)fChannelAdjust; + fFakeHeader.fBlockAlign /= (UInt16)fChannelAdjust; + + SetPosition( 0 ); +// fCurrDataPos = 0; + } +} + +plFastWAV::~plFastWAV() +{ + if( fFileHandle != nil ) + fclose( fFileHandle ); +} + +bool plFastWAV::ISeekToChunk( const char *type, plRIFFChunk *c ) +{ + plRIFFChunk chunk; + + + // Start from chunk start and search through all the, well, chunks :) + fseek( fFileHandle, fChunkStart, SEEK_SET ); + while( !feof( fFileHandle ) ) + { + chunk.Read( fFileHandle ); + if( chunk.IsA( type ) ) + { + *c = chunk; + return true; + } + + // Seek past this one + fseek( fFileHandle, chunk.fSize, SEEK_CUR ); + } + + return false; +} + +void plFastWAV::Open() +{ + if(fFileHandle) + return; + + fFileHandle = fopen( fFilename, "rb" ); + if(!fFileHandle) + return; + + fCurrDataPos = 0; + + fseek( fFileHandle, fDataStartPos, SEEK_SET ); +} + +void plFastWAV::Close( void ) +{ + if( fFileHandle != nil ) + { + fclose( fFileHandle ); + fFileHandle = nil; + } +} + +void plFastWAV::IError( const char *msg ) +{ + hsAssert( false, msg ); + Close(); +} + +plWAVHeader &plFastWAV::GetHeader( void ) +{ + hsAssert( IsValid(), "GetHeader() called on an invalid WAV file" ); + + return fFakeHeader; +} + +float plFastWAV::GetLengthInSecs( void ) +{ + hsAssert( IsValid(), "GetLengthInSecs() called on an invalid WAV file" ); + + return (float)( fDataSize / fChannelAdjust ) / (float)fHeader.fAvgBytesPerSec; +} + +hsBool plFastWAV::SetPosition( UInt32 numBytes ) +{ + hsAssert( IsValid(), "GetHeader() called on an invalid WAV file" ); + + + fCurrDataPos = numBytes * fChannelAdjust + ( fChannelOffset * fHeader.fBlockAlign / fChannelAdjust ); + + hsAssert( fCurrDataPos <= fDataSize, "Invalid new position while seeking WAV file" ); + + return ( fseek( fFileHandle, fDataStartPos + fCurrDataPos, SEEK_SET ) == 0 ) ? true : false; +} + +hsBool plFastWAV::Read( UInt32 numBytes, void *buffer ) +{ + hsAssert( IsValid(), "GetHeader() called on an invalid WAV file" ); + + + if( fWhichChannel != plAudioCore::kAll ) + { + size_t numRead, sampleSize = fHeader.fBlockAlign / fChannelAdjust; + static UInt8 trashBuffer[ 32 ]; + + UInt32 numBytesFull = numBytes; + if( fCurrDataPos + ( numBytes * fChannelAdjust ) > fDataSize ) + numBytesFull -= sampleSize; + + for( numRead = 0; numRead < numBytesFull; numRead += sampleSize ) + { + size_t thisRead = fread( buffer, 1, sampleSize, fFileHandle ); + if( thisRead != sampleSize ) + return false; + +// fseek( fFileHandle, sampleSize * ( fChannelAdjust - 1 ), SEEK_CUR ); + thisRead = fread( trashBuffer, 1, sampleSize, fFileHandle ); + if( thisRead != sampleSize ) + return false; + + buffer = (void *)( (UInt8 *)buffer + sampleSize ); + fCurrDataPos += sampleSize * fChannelAdjust; + } + + if( numRead < numBytes ) + { + if( numBytes - numRead > sampleSize ) + { + hsAssert( false, "Invalid logic in plFastWAV::Read()" ); + return false; + } + + + // Must not have enough room left, so no more skipping + size_t thisRead = fread( buffer, 1, sampleSize, fFileHandle ); + + if( thisRead != sampleSize ) + return false; + + buffer = (void *)( (UInt8 *)buffer + sampleSize ); + fCurrDataPos += sampleSize; + } + + hsAssert( fCurrDataPos <= fDataSize, "Invalid new position while reading WAV file" ); + } + else + { + size_t numRead = fread( buffer, 1, numBytes, fFileHandle ); + + fCurrDataPos += numRead; + hsAssert( fCurrDataPos <= fDataSize, "Invalid new position while reading WAV file" ); + + if( numRead < numBytes ) + return false; + } + + return true; +} + +UInt32 plFastWAV::NumBytesLeft( void ) +{ + hsAssert( IsValid(), "GetHeader() called on an invalid WAV file" ); + hsAssert( fCurrDataPos <= fDataSize, "Invalid current position while reading WAV file" ); + + return ( fDataSize - fCurrDataPos ) / fChannelAdjust; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plFastWavReader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plFastWavReader.h new file mode 100644 index 00000000..da02cd46 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plFastWavReader.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plFastWavReader - Quick, dirty, and highly optimized class for reading // +// in the samples of a WAV file when you're in a hurry. // +// ONLY WORKS WITH PCM (i.e. uncompressed) DATA // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plFastWavReader_h +#define _plFastWavReader_h + +#include "plAudioFileReader.h" + + +//// Class Definition //////////////////////////////////////////////////////// + +class plRIFFChunk; + +class plFastWAV : public plAudioFileReader +{ +public: + plFastWAV( const char *path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll ); + virtual ~plFastWAV(); + + virtual plWAVHeader &GetHeader( void ); + + virtual void Open(); + virtual void Close( void ); + + virtual UInt32 GetDataSize( void ) { return fDataSize / fChannelAdjust; } + virtual float GetLengthInSecs( void ); + + virtual hsBool SetPosition( UInt32 numBytes ); + virtual hsBool Read( UInt32 numBytes, void *buffer ); + virtual UInt32 NumBytesLeft( void ); + + virtual hsBool IsValid( void ) { return ( fFileHandle != nil ) ? true : false; } + +protected: + enum + { + kPCMFormatTag = 1 + }; + + char fFilename[ 512 ]; + FILE * fFileHandle; + plWAVHeader fHeader, fFakeHeader; + UInt32 fDataStartPos, fCurrDataPos, fDataSize; + UInt32 fChunkStart; + plAudioCore::ChannelSelect fWhichChannel; + UInt32 fChannelAdjust, fChannelOffset; + + void IError( const char *msg ); + bool ISeekToChunk( const char *type, plRIFFChunk *c ); +}; + +#endif //_plFastWavReader_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp new file mode 100644 index 00000000..ec2eccdd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp @@ -0,0 +1,565 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "hsTypes.h" +#include "plSoundBuffer.h" + +#include "hsStream.h" +#include "hsUtils.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "../pnMessage/plRefMsg.h" +#include "../plFile/plFileUtils.h" +#include "../plFile/hsFiles.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "../pnUtils/pnUtils.h" +#include "../plStatusLog/plStatusLog.h" +#include "hsTimer.h" + +static bool s_running; +static LISTDECL(plSoundBuffer, link) s_loading; +static CCritSect s_critsect; +static CEvent s_event(kEventAutoReset); + +static void GetFullPath( const char filename[], char *destStr ) +{ + char path[ kFolderIterator_MaxPath ]; + + if( strchr( filename, '\\' ) != nil ) + strcpy( path, filename ); + else + sprintf( path, "sfx\\%s", filename ); + + strcpy( destStr, path ); +} + +//// IGetReader ////////////////////////////////////////////////////////////// +// Makes sure the sound is ready to load without any extra processing (like +// decompression or the like), then opens a reader for it. +// fullpath tells the function whether to append 'sfx' to the path or not (we don't want to do this if were providing the full path) +static plAudioFileReader *CreateReader( hsBool fullpath, const char filename[], plAudioFileReader::StreamType type, plAudioCore::ChannelSelect channel ) +{ + char path[512]; + if(fullpath) GetFullPath(filename, path); + else + strcpy(path, filename); + + plAudioFileReader* reader = plAudioFileReader::CreateReader(path, channel, type); + + if( reader == nil || !reader->IsValid() ) + { + delete reader; + return nil; + } + + return reader; +} + +// our loading thread +static void LoadCallback(void *) +{ + LISTDECL(plSoundBuffer, link) templist; + while(s_running) + { + s_critsect.Enter(); + { + while(plSoundBuffer *buffer = s_loading.Head()) + { + templist.Link(buffer); + } + } + s_critsect.Leave(); + + if(!templist.Head()) + { + s_event.Wait(kEventWaitForever); + } + else + { + plAudioFileReader *reader = nil; + while(plSoundBuffer *buffer = templist.Head()) + { + if(buffer->GetData()) + { + reader = CreateReader(true, buffer->GetFileName(), buffer->GetAudioReaderType(), buffer->GetReaderSelect()); + + if( reader ) + { + unsigned readLen = buffer->GetAsyncLoadLength() ? buffer->GetAsyncLoadLength() : buffer->GetDataLength(); + reader->Read( readLen, buffer->GetData() ); + buffer->SetAudioReader(reader); // give sound buffer reader, since we may need it later + } + else + buffer->SetError(); + } + + templist.Unlink(buffer); + buffer->SetLoaded(true); + } + } + } + + // we need to be sure that all buffers are removed from our load list when shutting this thread down or we will hang, + // since the sound buffer will wait to be destroyed until it is marked as loaded + s_critsect.Enter(); + { + while(plSoundBuffer *buffer = s_loading.Head()) + { + buffer->SetLoaded(true); + s_loading.Unlink(buffer); + } + } + s_critsect.Leave(); +} + +void plSoundBuffer::Init() +{ + s_running = true; + _beginthread(LoadCallback, 0, 0); +} + +void plSoundBuffer::Shutdown() +{ + s_running = false; + s_event.Signal(); +} + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plSoundBuffer::plSoundBuffer() +{ + IInitBuffer(); +} + +plSoundBuffer::plSoundBuffer( const char *fileName, UInt32 flags ) +{ + IInitBuffer(); + SetFileName( fileName ); + fFlags = flags; + fValid = IGrabHeaderInfo(); +} + +plSoundBuffer::~plSoundBuffer() +{ + // if we are loading a sound we need to wait for the loading thread to be completely done processing this buffer. + // otherwise it may try to access this buffer after it's been deleted + if(fLoading) + { + while(!fLoaded) + { + Sleep(10); + } + } + + ASSERT(!link.IsLinked()); + delete [] fFileName; + UnLoad(); +} + +void plSoundBuffer::IInitBuffer() +{ + fError = false; + fValid = false; + fFileName = nil; + fData = nil; + fDataLength = 0; + fFlags = 0; + fDataRead = 0; + fReader = nil; + fLoaded = 0; + fLoading = false; + fHeader.fFormatTag = 0; + fHeader.fNumChannels = 0; + fHeader.fNumSamplesPerSec = 0; + fHeader.fAvgBytesPerSec = 0; + fHeader.fBlockAlign = 0; + fHeader.fBitsPerSample = 0; +} + +//// GetDataLengthInSecs ///////////////////////////////////////////////////// + +hsScalar plSoundBuffer::GetDataLengthInSecs( void ) const +{ + return (hsScalar)fDataLength / (hsScalar)fHeader.fAvgBytesPerSec; +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void plSoundBuffer::Read( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Read( s, mgr ); + + s->ReadSwap( &fFlags ); + s->ReadSwap( &fDataLength ); + fFileName = s->ReadSafeString(); + + s->ReadSwap( &fHeader.fFormatTag ); + s->ReadSwap( &fHeader.fNumChannels ); + s->ReadSwap( &fHeader.fNumSamplesPerSec ); + s->ReadSwap( &fHeader.fAvgBytesPerSec ); + s->ReadSwap( &fHeader.fBlockAlign ); + s->ReadSwap( &fHeader.fBitsPerSample ); + + fValid = false; + if( !( fFlags & kIsExternal ) ) + { + fData = TRACKED_NEW UInt8[ fDataLength ]; + if( fData == nil ) + fFlags |= kIsExternal; + else + { + s->Read( fDataLength, fData ); + fValid = true; + SetLoaded(true); + } + } + else + { + fData = nil; +// fValid = EnsureInternal(); + fValid = true; + } +} + +void plSoundBuffer::Write( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Write( s, mgr ); + + if( fData == nil ) + fFlags |= kIsExternal; + + s->WriteSwap( fFlags ); + s->WriteSwap( fDataLength ); + + // Truncate the path to just a file name on write + if( fFileName != nil ) + { + char *nameOnly = strrchr( fFileName, '\\' ); + if( nameOnly != nil ) + s->WriteSafeString( nameOnly + 1 ); + else + s->WriteSafeString( fFileName ); + } + else + s->WriteSafeString( nil ); + + s->WriteSwap( fHeader.fFormatTag ); + s->WriteSwap( fHeader.fNumChannels ); + s->WriteSwap( fHeader.fNumSamplesPerSec ); + s->WriteSwap( fHeader.fAvgBytesPerSec ); + s->WriteSwap( fHeader.fBlockAlign ); + s->WriteSwap( fHeader.fBitsPerSample ); + + if( !( fFlags & kIsExternal ) ) + s->Write( fDataLength, fData ); +} + +//// SetFileName ///////////////////////////////////////////////////////////// + +void plSoundBuffer::SetFileName( const char *name ) +{ + if(fLoading) + { + hsAssert(false, "Unable to set SoundBuffer filename"); + return; + } + + delete [] fFileName; + if( name != nil ) + fFileName = hsStrcpy( name ); + else + fFileName = nil; + + // Data is no longer valid + UnLoad(); +} + + +//// GetReaderSelect ///////////////////////////////////////////////////////// +// Translates our flags into the ChannelSelect enum for plAudioFileReader + +plAudioCore::ChannelSelect plSoundBuffer::GetReaderSelect( void ) const +{ + if( fFlags & kOnlyLeftChannel ) + return plAudioCore::kLeft; + else if( fFlags & kOnlyRightChannel ) + return plAudioCore::kRight; + else + return plAudioCore::kAll; +} + +//// IGetFullPath //////////////////////////////////////////////////////////// +// Construct our current full path to our sound. + +void plSoundBuffer::IGetFullPath( char *destStr ) +{ + if(!fFileName) + { + *destStr = 0; + return; + } + char path[ kFolderIterator_MaxPath ]; + + + if( strchr( fFileName, '\\' ) != nil ) + strcpy( path, fFileName ); + else + sprintf( path, "sfx\\%s", fFileName ); + + strcpy( destStr, path ); +} + + +//============================================================================ +// Asyncload will queue up a buffer for loading in our loading list the first time it is called. +// It will load in "length" number of bytes, if length is non zero. If length is zero the entire file will be loaded +// When called subsequent times it will check to see if the data has been loaded. +// Returns kPending while still loading the file. Returns kSuccess when the data has been loaded. +// While a file is loading(fLoading == true, and fLoaded == false) a buffer, no paremeters of the buffer should be modified. +plSoundBuffer::ELoadReturnVal plSoundBuffer::AsyncLoad(plAudioFileReader::StreamType type, unsigned length /* = 0 */ ) +{ + if(!s_running) + return kError; // we cannot load the data since the load thread is no longer running + if(!fLoading && !fLoaded) + { + fAsyncLoadLength = length; + fStreamType = type; + if(fData == nil ) + { + fData = TRACKED_NEW UInt8[ fAsyncLoadLength ? fAsyncLoadLength : fDataLength ]; + if( fData == nil ) + return kError; + } + s_critsect.Enter(); + { + fLoading = true; + s_loading.Link(this); + } + s_critsect.Leave(); + s_event.Signal(); + } + if(fLoaded) + { + if(fLoading) // ensures we only do this stuff one time + { + ELoadReturnVal retVal = kSuccess; + if(fError) + { + retVal = kError; + fError = false; + } + if(fReader) + { + fHeader = fReader->GetHeader(); + SetDataLength(fReader->GetDataSize()); + } + + fFlags &= ~kIsExternal; + fLoading = false; + return retVal; + } + return kSuccess; + } + + return kPending; +} + +//// ForceNonInternal //////////////////////////////////////////////////////// +// destroys loaded, and frees data +void plSoundBuffer::UnLoad( void ) +{ + if(fLoaded) + int i = 0; + if(fLoading) + return; + + if(fReader) + fReader->Close(); + + delete fReader; + fReader = nil; + + delete [] fData; + fData = nil; + SetLoaded(false); + fFlags |= kIsExternal; + +} + +//// IRoundDataPos /////////////////////////////////////////////////////////// + +void plSoundBuffer::RoundDataPos( UInt32 &pos ) +{ + UInt32 extra = pos & ( fHeader.fBlockAlign - 1 ); + pos -= extra; +} + +// transfers ownership to caller +plAudioFileReader *plSoundBuffer::GetAudioReader() +{ + plAudioFileReader * reader = fReader; + fReader = nil; + return reader; +} + +// WARNING: called by the loader thread(only) +// the reader will be handed off for later use. This is useful for streaming sound if we want to load the first chunk of data +// and the continue streaming the file from disk. +void plSoundBuffer::SetAudioReader(plAudioFileReader *reader) +{ + if(fReader) + fReader->Close(); + delete fReader; + fReader = reader; +} + +void plSoundBuffer::SetLoaded(bool loaded) +{ + fLoaded = loaded; +} + + +/***************************************************************************** +* +* for plugins only +* +***/ + +//// SetInternalData ///////////////////////////////////////////////////////// + +void plSoundBuffer::SetInternalData( plWAVHeader &header, UInt32 length, UInt8 *data ) +{ + if(fLoading) return; + fFileName = nil; + fHeader = header; + fFlags = 0; + + fDataLength = length; + fData = TRACKED_NEW UInt8[ length ]; + memcpy( fData, data, length ); + + fValid = true; +} + +//// EnsureInternal ////////////////////////////////////////////////////////// +// for plugins only +plSoundBuffer::ELoadReturnVal plSoundBuffer::EnsureInternal() +{ + if( fData == nil ) + { + fData = TRACKED_NEW UInt8[fDataLength ]; + if( fData == nil ) + return kError; + } + if(!fReader) + fReader = IGetReader(true); + //else + // fReader->Open(); + + if( fReader == nil ) + return kError; + + unsigned readLen = fDataLength; + if( !fReader->Read( readLen, fData ) ) + { + delete [] fData; + fData = nil; + return kError; + } + + if(fReader) + { + fReader->Close(); + delete fReader; + fReader = nil; + } + return kSuccess; +} + +//// IGrabHeaderInfo ///////////////////////////////////////////////////////// +hsBool plSoundBuffer::IGrabHeaderInfo( void ) +{ + static char path[ 512 ]; + + if( fFileName != nil ) + { + IGetFullPath( path ); + + // Go grab from the WAV file + if(!fReader) + { + fReader = plAudioFileReader::CreateReader(path, GetReaderSelect(), plAudioFileReader::kStreamNative); + if( fReader == nil || !fReader->IsValid() ) + { + delete fReader; + fReader = nil; + return false; + } + } + + fHeader = fReader->GetHeader(); + fDataLength = fReader->GetDataSize(); + RoundDataPos( fDataLength ); + + fReader->Close(); + delete fReader; + fReader = nil; + } + + return true; +} + +//// IGetReader ////////////////////////////////////////////////////////////// +// Makes sure the sound is ready to load without any extra processing (like +// decompression or the like), then opens a reader for it. +// fullpath tells the function whether to append 'sfx' to the path or not (we don't want to do this if were providing the full path) +plAudioFileReader *plSoundBuffer::IGetReader( hsBool fullpath ) +{ + char path[512]; + if(fullpath) IGetFullPath(path); + else + strcpy(path, fFileName); + + // Go grab from the WAV file + plAudioFileReader::StreamType type = plAudioFileReader::kStreamWAV; + if (HasFlag(kStreamCompressed)) + type = plAudioFileReader::kStreamNative; + + plAudioFileReader* reader = plAudioFileReader::CreateReader(path, GetReaderSelect(), type); + + if( reader == nil || !reader->IsValid() ) + { + delete reader; + return nil; + } + + fHeader = reader->GetHeader(); + fDataLength = reader->GetDataSize(); + RoundDataPos( fDataLength ); + + return reader; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h new file mode 100644 index 00000000..bd89efcf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h @@ -0,0 +1,146 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSoundBuffer - Quick, dirty, and highly optimized class for reading // +// in the samples of a WAV file when you're in a hurry. // +// ONLY WORKS WITH PCM (i.e. uncompressed) DATA // +// // +// Now loads data asynchronously. When fLoading is true // +// do not touch any data in the soundbuffer // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plSoundBuffer_h +#define _plSoundBuffer_h + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "plAudioCore.h" +#include "plAudioFileReader.h" +#include "../pnUtils/pnUtils.h" + +//// Class Definition //////////////////////////////////////////////////////// + +class plUnifiedTime; +class plAudioFileReader; +class plSoundBuffer : public hsKeyedObject +{ +public: + plSoundBuffer(); + plSoundBuffer( const char *fileName, UInt32 flags = 0 ); + ~plSoundBuffer(); + + CLASSNAME_REGISTER( plSoundBuffer ); + GETINTERFACE_ANY( plSoundBuffer, hsKeyedObject ); + + LINK(plSoundBuffer) link; + + enum Flags + { + kIsExternal = 0x0001, + kAlwaysExternal = 0x0002, + kOnlyLeftChannel = 0x0004, + kOnlyRightChannel = 0x0008, + kStreamCompressed = 0x0010, + }; + + enum ELoadReturnVal + { + kSuccess, + kError, + kPending, + }; + + void RoundDataPos( UInt32 &pos ); + + virtual void Read( hsStream *s, hsResMgr *mgr ); + virtual void Write( hsStream *s, hsResMgr *mgr ); + + plWAVHeader &GetHeader( void ) { return fHeader; } + UInt32 GetDataLength( void ) const { return fDataLength; } + void SetDataLength(unsigned length) { fDataLength = length; } + void *GetData( void ) const { return fData; } + const char *GetFileName( void ) const { return fFileName; } + hsBool IsValid( void ) const { return fValid; } + hsScalar GetDataLengthInSecs( void ) const; + + void SetFileName( const char *name ); + hsBool HasFlag( UInt32 flag ) { return ( fFlags & flag ) ? true : false; } + void SetFlag( UInt32 flag, hsBool yes = true ) { if( yes ) fFlags |= flag; else fFlags &= ~flag; } + + // Must be called until return value is kSuccess. starts an asynchronous load first time called. returns kSuccess when finished. + ELoadReturnVal AsyncLoad( plAudioFileReader::StreamType type, unsigned length = 0 ); + void UnLoad( ); + + plAudioCore::ChannelSelect GetReaderSelect( void ) const; + + + static void Init(); + static void Shutdown(); + plAudioFileReader * GetAudioReader(); // transfers ownership to caller + void SetAudioReader(plAudioFileReader *reader); + void SetLoaded(bool loaded); + + plAudioFileReader::StreamType GetAudioReaderType() { return fStreamType; } + unsigned GetAsyncLoadLength() { return fAsyncLoadLength ? fAsyncLoadLength : fDataLength; } + + // for plugins only + void SetInternalData( plWAVHeader &header, UInt32 length, UInt8 *data ); + ELoadReturnVal EnsureInternal( ); + void SetError() { fError = true; } + +protected: + + // plSoundBuffers can be two ways--they can either have a filename and no + // data, in which case they reference a file in the sfx folder, or they + // can store the data directly + + void IInitBuffer(); + + hsBool IGrabHeaderInfo( void ); + void IAddBuffers( void *base, void *toAdd, UInt32 lengthInBytes, UInt8 bitsPerSample ); + void IGetFullPath( char *destStr ); + + UInt32 fFlags; + hsBool fValid; + UInt32 fDataRead; + char *fFileName; + + bool fLoaded; + bool fLoading; + bool fError; + + plAudioFileReader * fReader; + UInt8 * fData; + plWAVHeader fHeader; + UInt32 fDataLength; + UInt32 fAsyncLoadLength; + plAudioFileReader::StreamType fStreamType; + + // for plugins only + plAudioFileReader *IGetReader( hsBool fullpath ); +}; + +#endif //_plSoundBuffer_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundDeswizzler.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundDeswizzler.cpp new file mode 100644 index 00000000..1feefb7d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundDeswizzler.cpp @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSoundDeswizzler - Quick helper class to extract a single channel of // +// data from stereo (or more)-channel data. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plSoundDeswizzler.h" +#include + + +plSoundDeswizzler::plSoundDeswizzler( void *srcPtr, UInt32 srcLength, UInt8 numChannels, UInt32 sampleSize ) +{ + fNumSamples = srcLength / sampleSize; + fSampleSize = sampleSize; + fStride = fSampleSize * numChannels; + fData = (UInt8 *)srcPtr; + fOwnsData = false; +} + +plSoundDeswizzler::plSoundDeswizzler( UInt32 srcLength, UInt8 numChannels, UInt32 sampleSize ) +{ + fNumSamples = srcLength / sampleSize; + fSampleSize = sampleSize; + fStride = fSampleSize * numChannels; + fData = TRACKED_NEW UInt8[ srcLength ]; + fOwnsData = true; +} + +plSoundDeswizzler::~plSoundDeswizzler() +{ + if( fOwnsData ) + delete [] fData; +} + +void plSoundDeswizzler::Extract( UInt8 channelSelect, void *dest, UInt32 numBytesToProcess ) +{ + UInt8 *srcPtr = fData + channelSelect * fSampleSize; + UInt8 *destPtr = (UInt8 *)dest; + UInt32 i; + + + if( numBytesToProcess == 0 ) + numBytesToProcess = fNumSamples; + else + numBytesToProcess /= fStride; + + // Extract! + for( i = 0; i < numBytesToProcess; i++ ) + { + memcpy( destPtr, srcPtr, fSampleSize ); + destPtr += fSampleSize; + srcPtr += fStride; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundDeswizzler.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundDeswizzler.h new file mode 100644 index 00000000..25b53c18 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAudioCore/plSoundDeswizzler.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSoundDeswizzler - Quick helper class to extract a single channel of // +// data from stereo (or more)-channel data. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plSoundDeswizzler_h +#define _plSoundDeswizzler_h + +#include "hsTypes.h" + + +//// Class Definition //////////////////////////////////////////////////////// + +class plSoundDeswizzler +{ +public: + plSoundDeswizzler( void *srcPtr, UInt32 srcLength, UInt8 numChannels, UInt32 sampleSize ); + plSoundDeswizzler( UInt32 srcLength, UInt8 numChannels, UInt32 sampleSize ); + ~plSoundDeswizzler(); + + void *GetSourceBuffer( void ) const { return fData; } + void Extract( UInt8 channelSelect, void *destPtr, UInt32 numBytesToProcess = 0 ); + +protected: + UInt8 *fData; + UInt32 fNumSamples, fSampleSize, fStride; + hsBool fOwnsData; +}; + +#endif //_plSoundDeswizzler_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnim.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnim.cpp new file mode 100644 index 00000000..babe7b71 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnim.cpp @@ -0,0 +1,766 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// +// singular +#include "plAGAnim.h" + +// local +#include "plMatrixChannel.h" + +// global +#include "hsResMgr.h" + +// other +#include "../plMessage/plAnimCmdMsg.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// STATICS +// +///////////////////////////////////////////////////////////////////////////////////////// + +plAGAnim::plAnimMap plAGAnim::fAllAnims; + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plAGAnim +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor ------------ +// ----- +plAGAnim::plAGAnim() +: plSynchedObject() +{ + fName = nil; +} + +// ctor ------------------------------------------------------ +// ----- +plAGAnim::plAGAnim(const char *name, double start, double end) +: fStart((hsScalar)start), + fEnd((hsScalar)end) +{ + if (name == nil) + name = ""; + + fName = TRACKED_NEW char[strlen(name) + 1]; + strcpy(fName, name); +} + +// dtor ------------- +// ----- +plAGAnim::~plAGAnim() +{ + if (fName) + { + RemoveAnim(fName); + delete[] fName; + fName = nil; + } + + //int numChannels = fChannels.size(); + int numApps = fApps.size(); + + for (int i = 0; i < numApps; i++) + { + plAGApplicator *app = fApps[i]; + if (app) + { + plAGChannel *channel = app->GetChannel(); + if(channel) + delete channel; + + delete app; + } + } +} + +// GetChannelCount ------------------ +// ---------------- +int plAGAnim::GetChannelCount() const +{ + return fApps.size(); +} + +// GetChannel ------------------------------------- +// ----------- +plAGChannel * plAGAnim::GetChannel(int index) const +{ + plAGApplicator *app = fApps[index]; + return (app ? app->GetChannel() : nil); +} + +// GetChannel -------------------------------------------- +// ----------- +plAGChannel * plAGAnim::GetChannel(const char *name) const +{ + int appCount = fApps.size(); + + for(int i = 0; i < appCount; i++) + { + plAGApplicator *app = fApps[i]; + plAGChannel *channel = app->GetChannel(); + const char *channelName = app->GetChannelName(); + + if(stricmp(name, channelName) == 0) + { + return channel; + } + } + return nil; +} + +// GetApplicatorCount ------------------ +// ------------------- +int plAGAnim::GetApplicatorCount() const +{ + return fApps.size(); +} + +// GetApplicator ----------------------------------- +// -------------- +plAGApplicator *plAGAnim::GetApplicator(int i) const +{ + return fApps[i]; +} + +// AddApplicator ------------------------------- +// -------------- +int plAGAnim::AddApplicator(plAGApplicator *app) +{ + hsAssert(app->GetChannel(), "Adding an applicator with no channel"); + fApps.push_back(app); + + // return the index of the channel + return(fApps.size() - 1); +} + +// RemoveApplicator ------------------------ +// ----------------- +hsBool plAGAnim::RemoveApplicator(int index) +{ + hsAssert(index < fApps.size(), "Out of range index for plAGAnim::RemoveApp()"); + + if(index < fApps.size()) + { + fApps.erase(fApps.begin() + index); + return true; + } else { + return false; + } +} + +// ExtendToLength ---------------------------- +// --------------- +void plAGAnim::ExtendToLength(hsScalar length) +{ + if (length > GetEnd()) + SetEnd(length); +} + +// GetChannelName ------------------------------ +// --------------- +const char * plAGAnim::GetChannelName(int index) +{ + hsAssert(index < fApps.size(), "Out of range index for plAGAnim::GetChannelName()"); + + if(index < fApps.size()) + { + return fApps[index]->GetChannel()->GetName(); + } else { + return nil; + } +} + +// Read -------------------------------------------- +// ----- +void plAGAnim::Read(hsStream *stream, hsResMgr *mgr) +{ + plSynchedObject::Read(stream, mgr); + + // read in the name of the animation itself + fName = stream->ReadSafeString(); + + fStart = stream->ReadSwapScalar(); + fEnd = stream->ReadSwapScalar(); + + int numApps = stream->ReadSwap32(); + + fApps.reserve(numApps); // pre-allocate for performance + int i; + for (i = 0; i < numApps; i++) + { + plAGApplicator * app = plAGApplicator::ConvertNoRef(mgr->ReadCreatable(stream)); + app->SetChannel(plAGChannel::ConvertNoRef(mgr->ReadCreatable(stream))); + fApps.push_back(app); + } + plAGAnim::AddAnim(fName, this); +} + +// Write -------------------------------------------- +// ------ +void plAGAnim::Write(hsStream *stream, hsResMgr *mgr) +{ + plSynchedObject::Write(stream, mgr); + + stream->WriteSafeString(fName); + + stream->WriteSwapScalar(fStart); + stream->WriteSwapScalar(fEnd); + + int numApps = fApps.size(); + + stream->WriteSwap32(numApps); + + int i; + for (i = 0; i < numApps; i++) + { + plAGApplicator *app = fApps[i]; + hsAssert(app, "Missing applicator during write."); + plAGChannel *channel = nil; + if (app) + channel = app->GetChannel(); + + hsAssert(channel, "Missing channel during write."); + mgr->WriteCreatable(stream, app); + mgr->WriteCreatable(stream, channel); + } +} + +void plAGAnim::ClearAnimationRegistry() +{ + fAllAnims.clear(); +} + +// AddAnim ---------------------------------------------- +// -------- +void plAGAnim::AddAnim(const char * name, plAGAnim *anim) +{ + // Only register the animation if it's got a "real" name. Unnamed animations + // all get the same standard name. + if(strcmp(name, ENTIRE_ANIMATION_NAME) != 0) + { + hsAssert(anim, "registering nil anim"); + fAllAnims[name] = anim; + } +} + +// FindAnim ----------------------------------- +// --------- +plAGAnim * plAGAnim::FindAnim(const char *name) +{ + plAnimMap::iterator i = fAllAnims.find(name); + + if(i != fAllAnims.end()) + { + return (*i).second; + } else { + return nil; + } +} + +// RemoveAnim ------------------------------- +// ----------- +hsBool plAGAnim::RemoveAnim(const char *name) +{ + plAnimMap::iterator i = fAllAnims.find(name); + + if(i != fAllAnims.end()) + { + fAllAnims.erase(i); + return true; + } else { + return false; + } +} + +// DumpAnimationRegistry ------------- +// ---------------------- +void plAGAnim::DumpAnimationRegistry() +{ + plAnimMap::iterator i = fAllAnims.begin(); + int j = 0; + + do { + plAGAnim *anim = (*i).second; + const char *name = anim->GetName(); + hsStatusMessageF("GLOBAL ANIMS [%d]: <%s>", j++, name); + } while(++i != fAllAnims.end()); +} + +// SharesPinsWith ----------------------------------------- +// --------------- +hsBool plAGAnim::SharesPinsWith(const plAGAnim *anim) const +{ + int i, j; + for (i = 0; i < fApps.size(); i++) + { + for (j = 0; j < anim->fApps.size(); j++) + { + if (!strcmp(fApps[i]->GetChannelName(), anim->fApps[j]->GetChannelName()) && + fApps[i]->CanBlend(anim->fApps[j])) + { + return true; + } + } + } + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plATCAnim +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor -------------- +// ----- +plATCAnim::plATCAnim() +: plAGAnim() +{ +} + +// ctor -------------------------------------------------------- +// ----- +plATCAnim::plATCAnim(const char *name, double start, double end) +: plAGAnim(name, start, end), + fInitial(-1), + fAutoStart(true), + fLoopStart((hsScalar)start), + fLoopEnd((hsScalar)end), + fLoop(false), + fEaseInType(plAnimEaseTypes::kNoEase), + fEaseOutType(plAnimEaseTypes::kNoEase), + fEaseInLength(0), + fEaseOutLength(0), + fEaseInMin(0.f), + fEaseInMax(0.f), + fEaseOutMin(0.f), + fEaseOutMax(0.f) +{ +} + +// dtor --------------- +// ----- +plATCAnim::~plATCAnim() +{ + for (MarkerMap::iterator it = fMarkers.begin(); it != fMarkers.end(); it++) + delete [] (char*)it->first; + fMarkers.clear(); + for( LoopMap::iterator it2 = fLoops.begin(); it2 != fLoops.end(); it2++ ) + delete [] (char *)it2->first; + fLoops.clear(); + fStopPoints.clear(); +} + +// Read --------------------------------------------- +// ----- +void plATCAnim::Read(hsStream *stream, hsResMgr *mgr) +{ + plAGAnim::Read(stream, mgr); + + fInitial = stream->ReadSwapScalar(); + fAutoStart = stream->Readbool(); + fLoopStart = stream->ReadSwapScalar(); + fLoopEnd = stream->ReadSwapScalar(); + fLoop = stream->Readbool(); + + fEaseInType = stream->ReadByte(); + fEaseInMin = stream->ReadSwapScalar(); + fEaseInMax = stream->ReadSwapScalar(); + fEaseInLength = stream->ReadSwapScalar(); + fEaseOutType = stream->ReadByte(); + fEaseOutMin = stream->ReadSwapScalar(); + fEaseOutMax = stream->ReadSwapScalar(); + fEaseOutLength = stream->ReadSwapScalar(); + + int i; + int numMarkers = stream->ReadSwap32(); + for (i = 0; i < numMarkers; i++) + { + char *name = stream->ReadSafeString(); + float time = stream->ReadSwapFloat(); + fMarkers[name] = time; + } + + int numLoops = stream->ReadSwap32(); + for (i = 0; i < numLoops; i++) + { + char *name = stream->ReadSafeString(); + float begin = stream->ReadSwapScalar(); + float end = stream->ReadSwapScalar(); + fLoops[name] = std::pair(begin,end); + } + + int numStops = stream->ReadSwap32(); + for (i = 0; i < numStops; i++) + fStopPoints.push_back(stream->ReadSwapScalar()); +} + +// Write --------------------------------------------- +// ------ +void plATCAnim::Write(hsStream *stream, hsResMgr *mgr) +{ + plAGAnim::Write(stream, mgr); + + stream->WriteSwapScalar(fInitial); + stream->Writebool(fAutoStart); + stream->WriteSwapScalar(fLoopStart); + stream->WriteSwapScalar(fLoopEnd); + stream->Writebool(fLoop); + + stream->WriteByte(fEaseInType); + stream->WriteSwapScalar(fEaseInMin); + stream->WriteSwapScalar(fEaseInMax); + stream->WriteSwapScalar(fEaseInLength); + stream->WriteByte(fEaseOutType); + stream->WriteSwapScalar(fEaseOutMin); + stream->WriteSwapScalar(fEaseOutMax); + stream->WriteSwapScalar(fEaseOutLength); + + stream->WriteSwap32(fMarkers.size()); + for (MarkerMap::iterator it = fMarkers.begin(); it != fMarkers.end(); it++) + { + stream->WriteSafeString(it->first); + stream->WriteSwapFloat(it->second); + } + + stream->WriteSwap32(fLoops.size()); + for (LoopMap::iterator loopIt = fLoops.begin(); loopIt != fLoops.end(); loopIt++) + { + stream->WriteSafeString(loopIt->first); + std::pair& loop = loopIt->second; + stream->WriteSwapFloat(loop.first); + stream->WriteSwapFloat(loop.second); + } + + int i; + stream->WriteSwap32(fStopPoints.size()); + for (i = 0; i < fStopPoints.size(); i++) + stream->WriteSwapScalar(fStopPoints[i]); +} + +// CheckLoop -------------- +// ---------- +void plATCAnim::CheckLoop() +{ + if (fLoopStart == fLoopEnd) + { + fLoopStart = fStart; + fLoopEnd = fEnd; + } +} + +// AddLoop ------------------------------------------------------ +// -------- +void plATCAnim::AddLoop(const char *name, float start, float end) +{ + char *nameCpy = hsStrcpy(name); + fLoops[nameCpy] = std::pair(start, end); +} + +// GetLoop -------------------------------------------------------------- +// -------- +bool plATCAnim::GetLoop(const char *name, float &start, float &end) const +{ + LoopMap::const_iterator it = fLoops.find(name); + if (it != fLoops.end()) + { + const std::pair& loop = (*it).second; + start = loop.first; + end = loop.second; + return true; + } + + return false; +} + +// GetLoop -------------------------------------------------------- +// -------- +bool plATCAnim::GetLoop(UInt32 num, float &start, float &end) const +{ + if (num >= fLoops.size()) + return false; + + LoopMap::const_iterator it = fLoops.begin(); + + while (num > 0) + { + it++; + num--; + } + const std::pair& loop = (*it).second; + start = loop.first; + end = loop.second; + return true; +} + +// GetNumLoops ---------------------- +// ------------ +UInt32 plATCAnim::GetNumLoops() const +{ + return fLoops.size(); +} + +// AddMarker ------------------------------------------ +// ---------- +void plATCAnim::AddMarker(const char *name, float time) +{ + char *nameCpy = hsStrcpy(name); + fMarkers[nameCpy] = time; +} + +// GetMarker ------------------------------------- +// ---------- +float plATCAnim::GetMarker(const char *name) const +{ + if (fMarkers.find(name) != fMarkers.end()) + return (*fMarkers.find(name)).second; + return -1; +} + +// CopyMarkerNames ------------------------------------- +// ---------------- +void plATCAnim::CopyMarkerNames(std::vector &out) +{ + MarkerMap::iterator it = fMarkers.begin(); + + for (; it != fMarkers.end(); it++) + { + out.push_back(hsStrcpy((*it).first)); + } +} + +// AddStopPoint --------------------------- +// ------------- +void plATCAnim::AddStopPoint(hsScalar time) +{ + fStopPoints.push_back(time); +} + +// NumStopPoints ---------------- +// -------------- +UInt32 plATCAnim::NumStopPoints() +{ + return fStopPoints.size(); +} + +// GetStopPoint -------------------------- +// ------------- +hsScalar plATCAnim::GetStopPoint(UInt32 i) +{ + hsAssert(i < fStopPoints.size(), "Invalid index for GetStopPoint"); + return fStopPoints[i]; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plEmoteAnim +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor ------------------ +// ----- +plEmoteAnim::plEmoteAnim() +: fBodyUsage(kBodyFull) +{ +} + +// ctor ------------------------------------------------------------------------------ +// ----- +plEmoteAnim::plEmoteAnim(const char *animName, double begin, double end, float fadeIn, + float fadeOut, BodyUsage bodyUsage) +: plATCAnim(animName, begin, end), + fFadeIn(fadeIn), + fFadeOut(fadeOut), + fBodyUsage(bodyUsage) +{ +} + +// Read ----------------------------------------------- +// ----- +void plEmoteAnim::Read(hsStream *stream, hsResMgr *mgr) +{ + plATCAnim::Read(stream, mgr); + + // plAGAnim::RegisterEmote(fName, this); + fFadeIn = stream->ReadSwapScalar(); + fFadeOut = stream->ReadSwapScalar(); + fBodyUsage = static_cast(stream->ReadByte()); + +} + +// Write ----------------------------------------------- +// ------ +void plEmoteAnim::Write(hsStream *stream, hsResMgr *mgr) +{ + plATCAnim::Write(stream, mgr); + stream->WriteSwapScalar(fFadeIn); + stream->WriteSwapScalar(fFadeOut); + stream->WriteByte(static_cast(fBodyUsage)); +} + +// GetBodyUsage ---------------------------------------- +// ------------- +plEmoteAnim::BodyUsage plEmoteAnim::GetBodyUsage() const +{ + return fBodyUsage; +} + +// GetFadeIn ----------------------- +// ---------- +float plEmoteAnim::GetFadeIn() const +{ + return fFadeIn; +} + +// GetFadeOut ----------------------- +// ----------- +float plEmoteAnim::GetFadeOut() const +{ + return fFadeOut; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plAgeGlobalAnim +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor -------------------------- +// ----- +plAgeGlobalAnim::plAgeGlobalAnim() +: plAGAnim() +{ + fGlobalVarName = nil; +} + +// ctor -------------------------------------------------------------------- +// ----- +plAgeGlobalAnim::plAgeGlobalAnim(const char *name, double start, double end) +: plAGAnim(name, start, end), + fGlobalVarName(nil) +{ +} + +// dtor --------------------------- +// ----- +plAgeGlobalAnim::~plAgeGlobalAnim() +{ + delete [] fGlobalVarName; +} + +void plAgeGlobalAnim::SetGlobalVarName(char *name) +{ + delete [] fGlobalVarName; + fGlobalVarName = hsStrcpy(name); +} + + +// Read --------------------------------------------------- +// ----- +void plAgeGlobalAnim::Read(hsStream *stream, hsResMgr *mgr) +{ + plAGAnim::Read(stream, mgr); + + fGlobalVarName = stream->ReadSafeString(); +} + +// Write --------------------------------------------------- +// ------ +void plAgeGlobalAnim::Write(hsStream *stream, hsResMgr *mgr) +{ + plAGAnim::Write(stream, mgr); + + stream->WriteSafeString(fGlobalVarName); +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// UTILITIES +// +///////////////////////////////////////////////////////////////////////////////////////// + +// GetStartToEndTransform ----------------------------------------------- +bool GetStartToEndTransform(const plAGAnim *anim, hsMatrix44 *startToEnd, + hsMatrix44 *endToStart, const char *channelName) +{ + double start = 0.0f; // assumed + double end = anim->GetEnd(); + + GetRelativeTransform(anim, start, end, startToEnd, endToStart, channelName); + return true; +} + +// GetRelativeTransform --------------------------------------------------- +bool GetRelativeTransform(const plAGAnim *anim, double timeA, double timeB, + hsMatrix44 *a2b, hsMatrix44 *b2a, const char *channelName) +{ + bool result = false; + plAGChannel *maybeChannel = anim->GetChannel(channelName); + hsAssert(maybeChannel, "Couldn't find channel with given name."); + if(maybeChannel) + { + plMatrixChannel *channel = plMatrixChannel::ConvertNoRef(maybeChannel); + hsAssert(channel, "Found channel, but it's not a matrix channel."); + + if(channel) + { + hsMatrix44 matA; + hsMatrix44 matB; + + channel->Value(matA, timeA); + channel->Value(matB, timeB); + + if(a2b) // requested a transform from point A to point B + { + hsMatrix44 invA; + matA.GetInverse(&invA); + *a2b = invA * matB; + } + if(b2a) // requested a transform from point B to point A + { + hsMatrix44 invB; + matB.GetInverse(&invB); + *b2a = invB * matA; + + if(a2b) + { + hsMatrix44 invB2; + a2b->GetInverse(&invB2); + } + } + } + } + return result; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnim.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnim.h new file mode 100644 index 00000000..dab0b936 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnim.h @@ -0,0 +1,430 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLAGANIM_INC +#define PLAGANIM_INC + +/** \file plAGAnim.h + \brief The animation class for the AniGraph animation system + + \ingroup Avatar + \ingroup AniGraph +*/ +#pragma warning(disable: 4786) // don't care if mangled names are longer than 255 characters + +#include "../pnNetCommon/plSynchedObject.h" +#include "hsStlUtils.h" +#include "hsStlSortUtils.h" + +class plTMController; +class hsAffineParts; +class plAnimTimeConvert; +struct hsMatrix44; +class plEmoteAnim; +class plAGApplicator; +class plAGChannel; + +/** \class plAGAnim + This class holds reusable animation data. A single plAGAnim can be instanced + any number of times simultaneously. + In order to use a plAGAnim, you need a plAGMasterMod, which is a special type of + modifier which can be attached to multiple scene objects. A master mod is typically + applied to the root of a scene branch, but there is no requirement that all the scene + objects animated be children. + Each plAGAnim has a number of channels, each of which can animate a single parameter + of a single object. Channels are parametric, i.e. they can carry any data type. + A plAGAnim carries a name for the animation, and each channel is named as well. + Instancing a plAGAnim is done via a plAGAnimInstance. + \sa plAGAnimInstance plAGMasterMod plAGModifier + */ +class plAGAnim : public plSynchedObject +{ +public: + /** How much of the body does this emote use? This is handy information for + figuring out whether you can, say, wave while sitting down. */ + enum BodyUsage { + kBodyUnknown, + kBodyUpper, + kBodyFull, + kBodyLower, + kBodyMax, + kForceSize = 0xff + }; + + plAGAnim(); + /** Construct with name, start time, and end time (within the max note track) + */ + plAGAnim(const char *name, double begin, double end); + /** Destruct, freeing the underlying animation data. */ + virtual ~plAGAnim(); + + /** Return the total of number of channels supplied by this animation. + An object being animated by this animation does not have to have this + many channels; any which are not available will be ignored. + This is syntactic sugar: GetApplicatorCount will return the exact + same number, but some code is only interested in the channels and not + the applicators. */ + int GetChannelCount() const; + + /** Return the ith channel of the animation. Ordering is arbitrary but consistent. + It's currently breadth first base on the export algorithm, but don't count on this + remaining true. */ + plAGChannel * GetChannel(int i) const; + + /** Get the name of the channel having the given index. Useful for talking to an + an animation before it is applied and finding out what channels it's going to + affect. */ + virtual const char * GetChannelName(int index); + + /** Get channel by name. This corresponds to the name of the scene object this channel + will be attached to when the animation is applied. + This function is fairly slow and shouldn't be used often. */ + plAGChannel * GetChannel(const char *name) const; + + /** Return the number of applicators held by this animation. An applicator is used + to attach a channel to a sceneobject. */ + int GetApplicatorCount() const; + + /** Return the ith applicator in the channel. + Order is arbitrary but consistent, corresponding to processing order in the exporter. */ + plAGApplicator * GetApplicator(int i) const; // get applicator by index + + /** Add an applicator -- which must have a channel behind it. + Applicators are translator object which take the output of a + channel and apply it to a scene object. */ + int AddApplicator(plAGApplicator * app); + + /** Remove the ith applicator and its associated channel. Existing applicators + will be renumbered. */ + hsBool RemoveApplicator(int appNum); + + /** The name of the animation. This name is used in the avatar manager to reference + animations. Animations are generally indexed by name when they are loaded + by the avatar or from script, but most of the functions which take an animation + name (such as AttachAnimation on the plAGMasterMod) will also take an pointer + to a plAGAnim. */ + virtual const char * GetName() const { return fName; } + + /** Return the length of the animation; end - start. */ + virtual hsScalar GetLength() const { return fEnd - fStart; } + + /** Hacky function to extend the length of the animation to some minimum + length. Does nothing if the animation is already longer than this. */ + void ExtendToLength(hsScalar length); + + /** Return the start time for the beginning of the animation. The animation + contains no data prior to this time. + In practice, this always returns 0.0f and this function may be deprecated. */ + virtual hsScalar GetStart() const { return fStart; } + void SetStart(hsScalar start) { fStart = start; } + + /** Return the end time of the animation. Since start is typically 0, this usually + serves as the length of the animation as well. */ + virtual hsScalar GetEnd() const { return fEnd; } + void SetEnd(hsScalar end) { fEnd = end; } + + + /** Returns true if any applicator on the arg anim tries to use the + same pin (on the same object) as we do. */ + hsBool SharesPinsWith(const plAGAnim *anim) const; + + + // PLASMA PROTOCOL + // rtti + CLASSNAME_REGISTER( plAGAnim ); + GETINTERFACE_ANY( plAGAnim, plSynchedObject ); + + // *** temp hack to manage animation instances + /** Add the animation by name to a global static registry. + This functionality will possibly be added to the resource + manager. */ + static void AddAnim(const char * name, plAGAnim *anim); + /** See if there is an animation with the given name in the + global animation registry. */ + static plAGAnim *FindAnim(const char *name); + /** Remove the given animation from the registry. */ + static hsBool RemoveAnim(const char *name); + /** Clear the animation cache. Used when resetting the client + to a vanilla state, as when clearing the scene while + exporting. */ + static void ClearAnimationRegistry(); + /** Debugging utility. Prints out a list of all the animations + in the registry */ + static void DumpAnimationRegistry(); + + // persistance + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + +protected: + typedef std::vector ApplicatorVec; + ApplicatorVec fApps; /// our animation channels + float fBlend; /// requested blend factor + + hsScalar fStart; /// the start time of the beginning of the animation (usually 0) + hsScalar fEnd; /// the end time of the animation + + char *fName; /// the name of our animation + + // ??? Can this be moved to the resource manager? If it can manage an efficient + // string-based namespace per class, we could get rid of this. + typedef std::map plAnimMap; // + static plAnimMap fAllAnims; /// map of animation names to animations + + typedef std::map plEmoteMap; + static plEmoteMap fAllEmotes; +}; + +/////////////// +// PLATCANIM +/////////////// + +/** \class plATCAnim + The most common subclass of plAGAnim. + Represents an animation with a standard AnimTimeConvert + (i.e. stop/start/loop/etc animation) + */ +class plATCAnim : public plAGAnim +{ +public: + + + plATCAnim(); + /** Construct with name, start time, and end time (within the max note track) + Default is to start automatically, not loop, with no ease curves. */ + plATCAnim(const char *name, double begin, double end); + /** Destruct, freeing the underlying animation data. */ + virtual ~plATCAnim(); + + /** Returns the initial position of the "playback head" for this animation. + Animations are not required to start at their actual beginning but can, + for instance, start in the middle, play to the end, and then loop to the + beginning or to their loop start point. */ + virtual hsScalar GetInitial() const { return fInitial; } + void SetInitial(hsScalar initial) { fInitial = initial; } + + /** Does this animation start automatically when it's applied? */ + virtual bool GetAutoStart() const { return fAutoStart; } + void SetAutoStart(hsBool start) { fAutoStart = (start != 0); } + + /** If the animation loops, this is where it will restart the loop. Note that + loops do not have to start at the beginning of the animation. */ + virtual hsScalar GetLoopStart() const { return fLoopStart; } + void SetLoopStart(hsScalar start) { fLoopStart = start; } + + /** If the animation loops, this is the end point of the loop. After passing + this point, the animation will cycle around to GetLoopStart */ + virtual hsScalar GetLoopEnd() const { return fLoopEnd; } + void SetLoopEnd(hsScalar end) { fLoopEnd = end; } + + /** Does this animation loop?. Note that there may be multiple loop segments defined + within a given animation. */ + virtual bool GetLoop() const { return fLoop; } + void SetLoop(hsBool loop) { fLoop = (loop != 0); } + + /** Set the curve type for easing in. Easing is an optional feature which allows you + to make an animation slow down gradually when you stop it. + The types are defined in plAnimEaseTypes.h + */ + virtual UInt8 GetEaseInType() const { return fEaseInType; } + void SetEaseInType(UInt8 type) { fEaseInType = type; } + + /** Set the length of time the ease-in should take. */ + virtual hsScalar GetEaseInLength() const { return fEaseInLength; } + /** Set the length of time the ease-in should take. */ + void SetEaseInLength(hsScalar length) { fEaseInLength = length; } + + /** The minimum value used at the start of the ease in. */ + virtual hsScalar GetEaseInMin() const { return fEaseInMin; } + /** The minimum value used at the start of the ease in. */ + void SetEaseInMin(hsScalar length) { fEaseInMin = length; } + + /** The maximum value reached at the end of the ease in. */ + virtual hsScalar GetEaseInMax() const { return fEaseInMax; } + /** The maximum value reached at the end of the ease in. */ + void SetEaseInMax(hsScalar length) { fEaseInMax = length; } + + /** The curve type for the ease out. */ + virtual UInt8 GetEaseOutType() const { return fEaseOutType; } + /** The curve type for the ease out. */ + void SetEaseOutType(UInt8 type) { fEaseOutType = type; } + + /** The length of time for the ease out. */ + virtual hsScalar GetEaseOutLength() const { return fEaseOutLength; } + /** The length of time for the ease out. */ + void SetEaseOutLength(hsScalar length) { fEaseOutLength = length; } + + /** Minimum value reached in ease-out */ + virtual hsScalar GetEaseOutMin() const { return fEaseOutMin; } + /** Minimum value reached in ease-out */ + void SetEaseOutMin(hsScalar length) { fEaseOutMin = length; } + + /** Maximum value reached in ease-in */ + virtual hsScalar GetEaseOutMax() const { return fEaseOutMax; } + /** Maximum value reached in ease-in */ + void SetEaseOutMax(hsScalar length) { fEaseOutMax = length; } + + /** Animations can have multiple defined loop segments; these + are selected using animation control messages. + Each loop segment is named using markers in the notetrack. */ + void AddLoop(const char *name, float start, float end); + /** Get the loop having the given name. + \param start will return the start time of the loop. + \param end will hold the end time of the loop */ + bool GetLoop(const char *name, float &start, float &end) const; + /** Lets you get a loop by index instead of name. */ + bool GetLoop(UInt32 num, float &start, float &end) const; + /** Returns the number of loops defined on this anim. */ + UInt32 GetNumLoops() const; + + /** Add a marker to the animation. Markers can be used + for callbacks or for goto comands. A marker is a simple + name/time tuple. */ + void AddMarker(const char *name, float time); + /** Returns the time value of the marker named by name. */ + float GetMarker(const char *name) const; + void CopyMarkerNames(std::vector &out); + /** Add a stop point to the animation. A stop point is a + "detent" for playback - if the animation is stopping + near a stop point and fading out, the stop point will + override the fade, so that the animation stops precisely + at the defined time. */ + void AddStopPoint(hsScalar time); + /** Return the number of stop points defined for this animation. */ + UInt32 NumStopPoints(); + /** Get the time corresponding to the given stop point. Stop points + are numbered in the order they were added. */ + hsScalar GetStopPoint(UInt32 i); + /** Function to check for a zero-length loop, and set it to + the anim's start/end instead */ + void CheckLoop(); + + // PLASMA PROTOCOL + // rtti + CLASSNAME_REGISTER( plATCAnim ); + GETINTERFACE_ANY( plATCAnim, plAGAnim ); + + // persistance + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + +protected: + hsScalar fInitial; /// the position of the playback head + bool fAutoStart; /// does the animation start automatically? + hsScalar fLoopStart; /// when wrapping a loop, start here + hsScalar fLoopEnd; /// when you reach this point, loop back + bool fLoop; /// do we loop? + + UInt8 fEaseInType; /// the type (none/linear/spline) of our ease-in curve, if any + UInt8 fEaseOutType; /// the type (none/linear/spline) of our ease-out curve, if any + hsScalar fEaseInLength; /// the length of time our ease-in curve takes + hsScalar fEaseInMin; /// minimum (initial) value of our ease-in + hsScalar fEaseInMax; /// maximum (final) value of our ease-in + hsScalar fEaseOutLength; /// the length of time our ease-out curve takes + hsScalar fEaseOutMin; /// minimum (final) value of our ease-out + hsScalar fEaseOutMax; /// maximum (initial) value of our ease-out + + // a map from segment names to times + typedef std::map MarkerMap; + MarkerMap fMarkers; + + typedef std::map, stringSorter> LoopMap; + LoopMap fLoops; + + typedef std::vector ScalarMap; + ScalarMap fStopPoints; /// vector of stop points +}; + + + +/** \class plEmoteAnim + An animation to be used for emotes. + Automatically registers so that it can be played from the chat field. + */ +class plEmoteAnim : public plATCAnim +{ +public: + + plEmoteAnim(); + plEmoteAnim(const char *animName, double begin, double end, float fadeIn, float fadeOut, BodyUsage bodyUsage); + + BodyUsage GetBodyUsage() const; + hsScalar GetFadeIn() const; + hsScalar GetFadeOut() const; + + CLASSNAME_REGISTER( plEmoteAnim ); + GETINTERFACE_ANY( plEmoteAnim, plATCAnim ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + +protected: + BodyUsage fBodyUsage; // how much of the body is used by this emote? + hsScalar fFadeIn; // how fast to fade in the emote + hsScalar fFadeOut; // how fast to fade out the emote +}; + +////////////////// +// PLAGEGLOBALANIM +////////////////// + +/** \class plAgeGlobalAnim + An animation that bases its current position on a variable that global to the age, + like weather, time of day, etc. + */ + +class plAgeGlobalAnim : public plAGAnim +{ +public: + plAgeGlobalAnim(); + /** Construct with name, start time, and end time (within the max note track) + */ + plAgeGlobalAnim(const char *name, double begin, double end); + /** Destruct, freeing the underlying animation data. */ + virtual ~plAgeGlobalAnim(); + + const char * GetGlobalVarName() const { return fGlobalVarName; } + void SetGlobalVarName(char *name); + + // PLASMA PROTOCOL + // rtti + CLASSNAME_REGISTER( plAgeGlobalAnim ); + GETINTERFACE_ANY( plAgeGlobalAnim, plAGAnim ); + + // persistance + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + +protected: + char *fGlobalVarName; // Name of the SDL variable we animate on. +}; + +// USEFUL HELPER FUNCTIONS +bool GetStartToEndTransform(const plAGAnim *anim, hsMatrix44 *startToEnd, hsMatrix44 *endToStart, const char *channelName); +bool GetRelativeTransform(const plAGAnim *anim, double timeA, double timeB, hsMatrix44 *a2b, hsMatrix44 *b2a, const char *channelName); + + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnimInstance.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnimInstance.cpp new file mode 100644 index 00000000..237453b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnimInstance.cpp @@ -0,0 +1,606 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// + +// singular +#include "plAGAnimInstance.h" + +// local +#include "plAGAnim.h" +#include "plAGModifier.h" +#include "plAGMasterMod.h" + +// global +#include "hsTimer.h" // just when debugging for GetSysSeconds + +// other +#include "../pnNetCommon/plSDLTypes.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plOneShotCallbacks.h" +#include "../plModifier/plSDLModifier.h" +#include "../plSDL/plSDL.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// FLAGS +// +///////////////////////////////////////////////////////////////////////////////////////// + +// enable this to show blend trees before and after attaches and detaches +// #define SHOW_AG_CHANGES + +///////////////////////////////////////////////////////////////////////////////////////// +// +// STATIC +// +///////////////////////////////////////////////////////////////////////////////////////// +#ifdef TRACK_AG_ALLOCS +extern const char *gGlobalAnimName = nil; +extern const char *gGlobalChannelName = nil; +#endif // TRACK_AG_ALLOCS + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plAGAnimInstance +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor ------------------------------------------------------------------- +// ----- +plAGAnimInstance::plAGAnimInstance(plAGAnim * anim, plAGMasterMod * master, + hsScalar blend, UInt16 blendPriority, hsBool cache, + bool useAmplitude) +: fAnimation(anim), + fMaster(master), + fBlend(blend), + fAmplitude(useAmplitude ? 1.0f : -1.0f) +{ + int i; + fTimeConvert = nil; + plScalarChannel *timeChan = nil; +#ifdef TRACK_AG_ALLOCS + gGlobalAnimName = anim->GetName(); // for debug tracking... +#endif // TRACK_AG_ALLOCS + + plATCAnim *atcAnim = plATCAnim::ConvertNoRef(anim); + if (atcAnim) + { + fTimeConvert = TRACKED_NEW plAnimTimeConvert(); + fTimeConvert->Init(atcAnim, this, master); + timeChan = TRACKED_NEW plATCChannel(fTimeConvert); + } + else + { + timeChan = TRACKED_NEW plScalarSDLChannel(anim->GetLength()); + fSDLChannels.push_back((plScalarSDLChannel *)timeChan); + } + + int nInChannels = anim->GetChannelCount(); + + fCleanupChannels.push_back(timeChan); + +#ifdef SHOW_AG_CHANGES + hsStatusMessageF("\nAbout to Attach anim <%s>", GetName()); + fMaster->DumpAniGraph("bone_pelvis", false, hsTimer::GetSysSeconds()); +#endif + + for (i = 0; i < nInChannels; i++) + { + plAGApplicator * app = fAnimation->GetApplicator(i); + plAGChannel * inChannel = app->GetChannel(); + const char * channelName = app->GetChannelName(); + plAGModifier * channelMod = master->GetChannelMod(channelName); + + if(channelMod) { +#ifdef TRACK_AG_ALLOCS + gGlobalChannelName = channelName; +#endif // TRACK_AG_ALLOCS + + // we're going to be accumulating a chain of channels. + // curChannel will always point to the top one... + plAGChannel *topNode = inChannel; + + if(cache) + { + topNode = topNode->MakeCacheChannel(fTimeConvert); + IRegisterDetach(channelName, topNode); + } + + if(useAmplitude) + { + // amplitude is rarely used and expensive, so only alloc if asked + // first build a static copy of the incoming channel... + plAGChannel *zeroState = inChannel->MakeZeroState(); + IRegisterDetach(channelName, zeroState); + + // now make a blend node to blend the anim with its static copy + topNode = zeroState->MakeBlend(topNode, &fAmplitude, -1); + } + + // make a time scaler to localize time for this instance + topNode = topNode->MakeTimeScale(timeChan); + IRegisterDetach(channelName, topNode); + + channelMod->MergeChannel(app, topNode, &fBlend, this, blendPriority); + } + else + hsAssert(false, "Adding an animation with an invalid channel."); + } + fFadeBlend = fFadeAmp = false; + +#ifdef TRACK_AG_ALLOCS + gGlobalAnimName = nil; +#endif // TRACK_AG_ALLOCS +} + +// dtor ----------------------------- +// ----- +plAGAnimInstance::~plAGAnimInstance() +{ + delete fTimeConvert; +} + +// SearchForGlobals --------------------- +// ----------------- +void plAGAnimInstance::SearchForGlobals() +{ + const plAgeGlobalAnim *ageAnim = plAgeGlobalAnim::ConvertNoRef(fAnimation); + if (ageAnim != nil && fSDLChannels.size() > 0) + { + extern const plSDLModifier *ExternFindAgeSDL(); + const plSDLModifier *sdlMod = ExternFindAgeSDL(); + if (!sdlMod) + return; + + plSimpleStateVariable *var = sdlMod->GetStateCache()->FindVar(ageAnim->GetGlobalVarName()); + if (!var) + return; + + sdlMod->AddNotifyForVar(fMaster->GetKey(), ageAnim->GetGlobalVarName(), 0); + int i; + for (i = 0; i < fSDLChannels.size(); i++) + fSDLChannels[i]->SetVar(var); + } +} + +void plAGAnimInstance::IRegisterDetach(const char *channelName, plAGChannel *channel) +{ + plDetachMap::value_type newPair(channelName, channel); + fManualDetachChannels.insert(newPair); +} + +// SetCurrentTime --------------------------------------------------------------- +// --------------- +void plAGAnimInstance::SetCurrentTime(hsScalar localT, hsBool jump /* = false */) +{ + if (fTimeConvert) + fTimeConvert->SetCurrentAnimTime(localT, jump); +} + +// SeekRelative ------------------------------------ +// ------------- +void plAGAnimInstance::SeekRelative (hsScalar delta, hsBool jump) +{ + if(fTimeConvert) + { + hsScalar now = fTimeConvert->CurrentAnimTime(); + fTimeConvert->SetCurrentAnimTime (now + delta, jump); + } +} + +// Detach --------------------- +// ------- +void plAGAnimInstance::Detach() +{ + fMaster->DetachAnimation(this); +} + +// DetachChannels --------------------- +// --------------- +void plAGAnimInstance::DetachChannels() +{ +#ifdef SHOW_AG_CHANGES + hsStatusMessageF("\nAbout to DETACH anim <%s>", GetName()); + fMaster->DumpAniGraph("bone_pelvis", false, hsTimer::GetSysSeconds()); +#endif + plDetachMap::iterator i = fManualDetachChannels.begin(); + + while(i != fManualDetachChannels.end()) + { + const char *channelName = (*i).first; + plAGModifier *channelMod = fMaster->GetChannelMod(channelName, true); + + if(channelMod) + { + do { + plAGChannel *channel = (*i).second; + channelMod->DetachChannel(channel); + } while (i != fManualDetachChannels.end() && (*++i).first == channelName); + } else { + do { + } while (i != fManualDetachChannels.end() && (*++i).first == channelName); + } + } + + int cleanCount = fCleanupChannels.size(); + hsAssert(cleanCount, "No time controls when deleting animation"); + for (int j = 0; j < cleanCount; j++) + { + delete fCleanupChannels[j]; + } + fCleanupChannels.clear(); + +#ifdef SHOW_AG_CHANGES + hsStatusMessageF("\nFinished DETACHING anim <%s>", GetName()); + fMaster->DumpAniGraph("bone_pelvis", false, hsTimer::GetSysSeconds()); +#endif +} + +// SetBlend --------------------------------------- +// --------- +hsScalar plAGAnimInstance::SetBlend(hsScalar blend) +{ + float oldBlend = fBlend.Value(0.0, true); + if(oldBlend != blend && + (oldBlend == 0.0f || + blend == 0.0f || + oldBlend == 1.0f || + blend == 1.0f)) + { + fMaster->SetNeedCompile(true); + } + fBlend.Set(blend); + return blend; +} + +// GetBlend ------------------------- +// --------- +hsScalar plAGAnimInstance::GetBlend() +{ + return fBlend.Value(0); +} + +// SetAmplitude ------------------------------------- +// ------------- +hsScalar plAGAnimInstance::SetAmplitude(hsScalar amp) +{ + if(fAmplitude.Get() != -1.0f) + { + fAmplitude.Set(amp); + } + return amp; +} + +// GetAmplitude ------------------------- +// ------------- +hsScalar plAGAnimInstance::GetAmplitude() +{ + return fAmplitude.Value(0); +} + +// GetName ----------------------------- +// -------- +const char * plAGAnimInstance::GetName() +{ + if(fAnimation) + return fAnimation->GetName(); + else + return nil; +} + +// SetLoop ---------------------------------- +// -------- +void plAGAnimInstance::SetLoop(hsBool status) +{ + if (fTimeConvert) + fTimeConvert->Loop(status); +} + +// HandleCmd ---------------------------------------- +// ---------- +hsBool plAGAnimInstance::HandleCmd(plAnimCmdMsg *msg) +{ + if (fTimeConvert) + return fTimeConvert->HandleCmd(msg); + return false; +} + +// IsFinished ----------------------- +// ----------- +hsBool plAGAnimInstance::IsFinished() +{ + if (fTimeConvert) + return fTimeConvert->IsStopped(); + return false; +} + +// IsAtEnd ----------------------- +// -------- +hsBool plAGAnimInstance::IsAtEnd() +{ + if(fTimeConvert) + { + return fTimeConvert->CurrentAnimTime() == fTimeConvert->GetEnd(); + } + else + return false; +} + +// Start ----------------------------------- +// ------ +void plAGAnimInstance::Start(double timeNow) +{ + if (fTimeConvert) + { + if (timeNow < 0) + fTimeConvert->Start(); + else + fTimeConvert->Start(timeNow); + } +} + +// Stop --------------------- +// ----- +void plAGAnimInstance::Stop() +{ + if (fTimeConvert) + fTimeConvert->Stop(); +} + +// AttachCallbacks -------------------------------------------------- +// ---------------- +void plAGAnimInstance::AttachCallbacks(plOneShotCallbacks *callbacks) +{ + const plATCAnim *anim = plATCAnim::ConvertNoRef(fAnimation); + if (callbacks && anim) + { + plAnimCmdMsg animMsg; + animMsg.SetCmd(plAnimCmdMsg::kAddCallbacks); + + for (int i = 0; i < callbacks->GetNumCallbacks(); i++) + { + plOneShotCallbacks::plOneShotCallback& cb = callbacks->GetCallback(i); + + plEventCallbackMsg *eventMsg = TRACKED_NEW plEventCallbackMsg; + eventMsg->AddReceiver(cb.fReceiver); + eventMsg->fRepeats = 0; + eventMsg->fUser = cb.fUser; + + if (cb.fMarker) + { + float marker = anim->GetMarker(cb.fMarker); + hsAssert(marker != -1, "Bad marker name"); + eventMsg->fEventTime = marker; + eventMsg->fEvent = kTime; + } + else + { + eventMsg->fEvent = kStop; + } + + animMsg.AddCallback(eventMsg); + hsRefCnt_SafeUnRef(eventMsg); + } + + fTimeConvert->HandleCmd(&animMsg); + } +} + +// ProcessFade ------------------------------------- +// ------------ +void plAGAnimInstance::ProcessFade(hsScalar elapsed) +{ + if (fFadeBlend) { + hsScalar newBlend = ICalcFade(fFadeBlend, GetBlend(), fFadeBlendGoal, fFadeBlendRate, elapsed); + SetBlend(newBlend); + if(fFadeDetach && (newBlend == fFadeBlendGoal) && (fFadeBlendGoal == 0.0f) ) + { + fMaster->DetachAnimation(this); + return; + } + } + + if (fFadeAmp && fAmplitude.Get() != -1.0f) { + hsScalar curAmp = GetAmplitude(); + hsScalar newAmp = ICalcFade(fFadeAmp, curAmp, fFadeAmpGoal, fFadeAmpRate, elapsed); + SetAmplitude(newAmp); + } +} + +// ICalcFade --------------------------------------------------------------------- +// ---------- +hsScalar plAGAnimInstance::ICalcFade(hsBool &fade, hsScalar curVal, hsScalar goal, + hsScalar rate, hsScalar elapsed) +{ + hsScalar newVal; + hsScalar curStep = rate * elapsed; + if(rate > 0) { + newVal = __min(goal, curVal + curStep); + } else { + newVal = __max(goal, curVal + curStep); + } + + if(newVal == goal) + { + fade = false; + fMaster->DirtySynchState(kSDLAGMaster, 0); // send SDL state update to server + } + return newVal; +} + +// FadeAndDetach ------------------------------------------------- +// -------------- +void plAGAnimInstance::FadeAndDetach(hsScalar goal, hsScalar rate) +{ + ISetupFade(goal, rate, true, kFadeBlend); +} + +// Fade -------------------------------------------------------------------------------- +// ----- +void plAGAnimInstance::Fade(hsScalar goal, hsScalar rate, UInt8 type /* = kFadeBlend */) +{ + ISetupFade(goal, rate, false, type); +} + +// ISetupFade -------------------------------------------------------------------------- +// ----------- +void plAGAnimInstance::ISetupFade(hsScalar goal, hsScalar rate, bool detach, UInt8 type) +{ + if (rate == 0) + { + if (type == kFadeBlend) + { + SetBlend(goal); + fFadeBlend = false; + if(detach) { + fMaster->DetachAnimation(this); + } + } + else if (type == kFadeAmp) + { + SetAmplitude(goal); + fFadeAmp = false; + } + return; + } + + rate = (rate > 0 ? rate : -rate); // For old code that sends negative values + + hsScalar curVal = 0; + switch (type) + { + case kFadeBlend: + curVal = GetBlend(); + break; + case kFadeAmp: + curVal = GetAmplitude(); + break; + } + if (curVal > goal) + rate = -rate; + + switch (type) + { + case kFadeBlend: + fFadeBlend = true; + fFadeBlendGoal = goal; + fFadeBlendRate = rate; + fFadeDetach = detach; + break; + case kFadeAmp: + fFadeAmp = true; + fFadeAmpGoal = goal; + fFadeAmpRate = rate; + fFadeDetach = false; // only detach on blend fades, for the moment. + break; + } +} + +class agAlloc +{ +public: + agAlloc(plAGChannel *object, const char *chanName, const char *animName, UInt16 classIndex) + : fObject(object), + fClassIndex(classIndex) + { + fChannelName = hsStrcpy(chanName); + fAnimName = hsStrcpy(animName); + } + + ~agAlloc() + { + delete[] fChannelName; + delete[] fAnimName; + } + + plAGChannel *fObject; + char *fChannelName; + char *fAnimName; + UInt16 fClassIndex; +}; + +typedef std::map agAllocMap; +static agAllocMap gAGAllocs; + +void RegisterAGAlloc(plAGChannel *object, const char *chanName, const char *animName, UInt16 classIndex) +{ + gAGAllocs[object] = TRACKED_NEW agAlloc(object, chanName, animName, classIndex); +} + +void DumpAGAllocs() +{ + agAllocMap::iterator i = gAGAllocs.begin(); + agAllocMap::iterator theEnd = gAGAllocs.end(); + + hsStatusMessage("DUMPING AG ALLOCATIONS ================================================"); + + for ( ; i != theEnd; i++) + { + agAlloc * al = (*i).second; + + UInt16 realClassIndex = al->fObject->ClassIndex(); + + hsStatusMessageF("agAlloc: an: %s ch: %s, cl: %s", al->fAnimName, al->fChannelName, plFactory::GetNameOfClass(realClassIndex)); + + } + // it's not fast but it's safe and simple.. + i = gAGAllocs.begin(); + while(i != gAGAllocs.end()) + { + agAlloc * al = (*i).second; + delete al; + + i = gAGAllocs.erase(i); + } + hsStatusMessage("FINISHED DUMPING AG ALLOCATIONS *********************************************"); +} + +void UnRegisterAGAlloc(plAGChannel *object) +{ + agAllocMap::iterator i = gAGAllocs.find(object); + if(i != gAGAllocs.end()) + { + agAlloc * al = (*i).second; + + gAGAllocs.erase(i); + delete al; + } +} + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnimInstance.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnimInstance.h new file mode 100644 index 00000000..05ad2491 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGAnimInstance.h @@ -0,0 +1,278 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/** \file plAGAnimInstance.h + \brief The animation class for the AniGraph animation system + + \ingroup Avatar + \ingroup AniGraph +*/ + +#ifndef PLAGANIMINSTANCE_INC +#define PLAGANIMINSTANCE_INC + +// disable warning C4503: dcorated name length exceeded, name was truncated +// disable warning C4786: symbol greater than 255 characters, +#pragma warning(disable: 4503 4786) + +// templates +#include "hsStlUtils.h" +#include "hsStlSortUtils.h" + +// local +#include "plScalarChannel.h" + +// other +#include "../plInterp/plAnimTimeConvert.h" + +// declarations +class plAGChannel; +class plAGAnim; +class plAGMasterMod; +class plAGChannelApplicator; +class plOneShotCallbacks; + +///////////////// +// PLAGANIMINSTANCE +///////////////// +/** \class plAGAnimInstance + Whenever we attach an animation to a scene object hierarchy, we + create an activation record -- a plAGAnimInstance -- that remembers + all the ephemeral state associated with animation + Since animations have many channels and may involve blend operations, + one of the primary responsibilities of this class is to keep track of + all the animation node graphs that were created by the invocation of + this animation. + */ +class plAGAnimInstance { +public: + /** Used for the fade commands to select what to fade. */ + enum + { + kFadeBlend, /// Fade the blend strength + kFadeAmp, /// Fade the amplitude + } FadeType; + + /** Default constructor. */ + plAGAnimInstance(); + + /** Construct from an animation and a master modifier. + This attaches the animation channels to the channels of + the master modifier and creates all the bookkeeping structures + necessary to undo it later. */ + plAGAnimInstance(plAGAnim * anim, plAGMasterMod * master, hsScalar blend, UInt16 blendPriority, hsBool cache, bool useAmplitude); + + /** Destructor. Removes the animation from the scene objects it's attached to. */ + virtual ~plAGAnimInstance(); + + /** Returns the animation that this instance mediates. */ + const plAGAnim * GetAnimation() { return fAnimation; }; + + /** Returns the timeconvert object that controls the progress of time + in this animation. */ + plAnimTimeConvert *GetTimeConvert() { return fTimeConvert; } + + /** Set the speed of the animation. This is expressed as a fraction of + the speed with which the animation was defined. */ + void SetSpeed(hsScalar speed) { if (fTimeConvert) fTimeConvert->SetSpeed(speed); }; + + // \{ + /** + The current blend factor of the animation. This indicates the + priority of this animation as opposed to other animations which + were attached before it. Conceptually it may help to think of this + as a layer in an stack of animations, where the blend value is the + 'opacity' of this animation relative to the ones below it. + 1.0 represents full strength. + You may use values higher than 1.0, but this has not + yet been seen to have any practical utility whatsoever. Note that + even if an animation has a blend strength of 1.0, it may have another + animation on top/downstream from it that is masking it completely. */ + hsScalar SetBlend(hsScalar blend); + hsScalar GetBlend(); + // \} + + /** Set the strength of the animation with respect to its 0th frame. + This can be used to dampen the motion of the animation. + Animations must be designed to use this: frame 0 of the animation + must be a reasonable "default pose" as it will be blended with the + current frame of the animation to produce the result. */ + hsScalar SetAmplitude(hsScalar amp); + /** Get the current strength of the animation. */ + hsScalar GetAmplitude(); + + /** Make this animation loop (or not.) Note that the instance can loop + or not without regard to whether the plAGAnim it is based on loops. */ + void SetLoop(hsBool status); + + /** Interpret and respond to an animation command message. /sa plAnimCmdMsg */ + hsBool HandleCmd(plAnimCmdMsg *msg); + + /** Start playback of the animation. You may optionally provide the a world + time, which is needed for synchronizing the animation's timeline + with the global timeline. If timeNow is -1 (the default,) the system + time will be polled */ + void Start(double worldTimeNow = -1); + + /** Stop playback of the animation. */ + void Stop(); + + /** Move the playback head of the animation to a specific time. + Note that this time is in animation local time, not global time. + The "jump" parameter specifies whether or not to fire callbacks + that occur between the current time and the target time. */ + void SetCurrentTime(hsScalar newLocalTime, hsBool jump = false); + + /** Move the playback head by the specified relative amount within + the animation. This may cause looping. If the beginning or end + of the animation is reached an looping is not on, the movement + will pin. + \param jump if true, don't look for callbacks between old time and TRACKED_NEW */ + void SeekRelative(hsScalar delta, hsBool jump); + + /** Gradually fade the blend strength or amplitude of the animation. + \param goal is the desired blend strength + \param rate is in blend units per second + \type is either kFadeBlend or kFadeAmp */ + void Fade(hsScalar goal, hsScalar rate, UInt8 type = kFadeBlend); + + /** Fade the animation and detach it after the fade is complete. + Extremely useful for situations where the controlling logic + is terminating immediately but you want the animation to fade + out gradually. + \deprecated + */ + void FadeAndDetach(hsScalar goal, hsScalar rate); + + /** Has the animation terminated of natural causes? + Primarily used to see if an animation has played all the + way to the end, but will also return true if the animation + was stopped with a stop command */ + hsBool IsFinished(); + + /** Is the animation playback head positioned at the end. */ + hsBool IsAtEnd(); + + /** Get the name of the underlying animation. */ + const char * GetName(); + + /** Remove all channels from the master mode and remove us from + our master modifier. + Destructs the object! */ + void Detach(); + + /** Remove all the instance's channels from the modifiers they're attached to. + Typically called by the master mod prior to destructing the instance. */ + void DetachChannels(); + + /** Prune any unused branches out of the animation graph; add any + newly active branches back in. */ + void Optimize(); + + /** Convert the given world time to local animation time. + May include the effects of looping or wraparound. + If the local time passes the end of the animation, the returned + time will be pinned appropriately. */ + double WorldToAnimTime(double foo) { return (fTimeConvert ? fTimeConvert->WorldToAnimTimeNoUpdate(foo) : 0); }; + + /** Attach a sequence of callback messages to the animation instance. + Messages are each associated with a specific (local) time + in the animation and will be sent when playback passes that time. */ + void AttachCallbacks(plOneShotCallbacks *callbacks); + + void ProcessFade(hsScalar elapsed); // process any outstanding fades + void SearchForGlobals(); // Util function to setup SDL channels +protected: + /** Set up bookkeeping for a fade. */ + void ISetupFade(hsScalar goal, hsScalar rate, bool detach, UInt8 type); + + void IRegisterDetach(const char *channelName, plAGChannel *channel); + + const plAGAnim * fAnimation; + plAGMasterMod * fMaster; + + std::map fChannels; + + typedef std::multimap plDetachMap; + plDetachMap fManualDetachChannels; + + std::vector fCleanupChannels; + std::vector fSDLChannels; + + plScalarConstant fBlend; // blend factor vs. previous animations + plScalarConstant fAmplitude; // for animation scaling + + // Each activation gets its own timeline. + plAnimTimeConvert *fTimeConvert; + + hsBool fFadeBlend; /// we are fading the blend + hsScalar fFadeBlendGoal; /// what blend level we're trying to reach + hsScalar fFadeBlendRate; /// how fast are we fading in blend units per second (1 blend unit = full) + hsBool fFadeDetach; /// detach after fade is finished? (only used for blend fades) + + hsBool fFadeAmp; /// we are fading the amplitude + hsScalar fFadeAmpGoal; /// amplitude we're trying to reach + hsScalar fFadeAmpRate; /// how faster we're fading in blend units per second + + hsScalar ICalcFade(hsBool &fade, hsScalar curVal, hsScalar goal, hsScalar rate, hsScalar elapsed); + +}; + +//#ifdef _DEBUG +//#define TRACK_AG_ALLOCS // for now, automatically track AG allocations in debug +//#endif +#ifdef TRACK_AG_ALLOCS + +extern const char *gGlobalAnimName; +extern const char *gGlobalChannelName; + +void RegisterAGAlloc(plAGChannel *object, const char *chanName, const char *animName, UInt16 classIndex); +void UnRegisterAGAlloc(plAGChannel *object); +void DumpAGAllocs(); + +#endif // TRACK_AG_ALLOCS + +#endif // PLAGANIMINSTANCE_INC + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGApplicator.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGApplicator.cpp new file mode 100644 index 00000000..14a455f6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGApplicator.cpp @@ -0,0 +1,165 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAGApplicator.h" +#include "plAGModifier.h" +#include "hsResMgr.h" +#include "hsUtils.h" + +// ctor -------- +// ----- +plAGApplicator::plAGApplicator() +: fChannel(nil), + fChannelName(nil), + fEnabled(true) +{ +}; + +// ctor ------------------------------- +// ----- +plAGApplicator::plAGApplicator(const char *channelName) +: fChannel(nil), + fEnabled(true) +{ + fChannelName = hsStrcpy(channelName); +}; + +plAGApplicator::~plAGApplicator() +{ + if(fChannelName) + delete[] fChannelName; +} + +void plAGApplicator::Apply(const plAGModifier *mod, double time, hsBool force) +{ + if (fEnabled || force) + IApply(mod, time); +} + +void plAGApplicator::SetChannelName(const char *name) +{ + if(name) + fChannelName = hsStrcpy(name); +}; + + +const char * plAGApplicator::GetChannelName() +{ + return fChannelName; +}; + +plAGChannel *plAGApplicator::MergeChannel(plAGApplicator *app, plAGChannel *channel, + plScalarChannel *blend, int blendPriority) +{ + plAGChannel *result = nil; + if(fChannel) + { + if (CanCombine(app)) + result = fChannel->MakeCombine(channel); + else if (CanBlend(app)) + result = fChannel->MakeBlend(channel, blend, blendPriority); + } else { + result = channel; + } + + if (result && result != fChannel) + SetChannel(result); + + return result; +} + +plAGApplicator *plAGApplicator::CloneWithChannel(plAGChannel *channel) +{ + plAGApplicator *app = plAGApplicator::ConvertNoRef(plFactory::Create(ClassIndex())); + app->SetChannel(channel); + app->Enable(fEnabled); + app->SetChannelName(fChannelName); + return app; +} + +hsBool plAGApplicator::CanBlend(plAGApplicator *app) +{ + UInt16 ourClass = ClassIndex(); + UInt16 theirClass = app->ClassIndex(); + + return(ourClass == theirClass); + +// return(this->HasBaseClass(theirClass) +// || app->HasBaseClass(ourClass)); +} + + +void plAGApplicator::Write(hsStream *stream, hsResMgr *mgr) +{ + plCreatable::Write(stream, mgr); + + stream->WriteBool(fEnabled); + stream->WriteSafeString(fChannelName); +} + +void plAGApplicator::Read(hsStream *stream, hsResMgr *mgr) +{ + plCreatable::Read(stream, mgr); + + fEnabled = stream->ReadBool(); + fChannel = nil; // Whatever is reading this applicator in should know what channel to assign it + fChannelName = stream->ReadSafeString(); +} + +// IGETxI +// Gain access to of our modifier's target's interfaces. +// This is technically in violation of the principle that only modifiers can get non-const +// reference to their target's interfaces, +// BUT since the plAGApplicator architecture is wholly "owned" by the AGModifier, this +// seemed the most graceful way to do it without const_cast or modifying plModifier or plSceneObject + +// IGETAI +plAudioInterface * plAGApplicator::IGetAI(const plAGModifier *modifier) const +{ + return modifier->LeakAI(); +} + +// IGETCI +plCoordinateInterface * plAGApplicator::IGetCI(const plAGModifier* modifier) const +{ + return modifier->LeakCI(); +} + +// IGETDI +plDrawInterface * plAGApplicator::IGetDI(const plAGModifier * modifier) const +{ + return modifier->LeakDI(); +} + +// IGETSI +plSimulationInterface * plAGApplicator::IGetSI(const plAGModifier * modifier) const +{ + return modifier->LeakSI(); +} + +plObjInterface * plAGApplicator::IGetGI(const plAGModifier * modifier, UInt16 classIdx) const +{ + return modifier->LeakGI(classIdx); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGApplicator.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGApplicator.h new file mode 100644 index 00000000..e1d3c63e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGApplicator.h @@ -0,0 +1,154 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAGApplicator_h +#define plAGApplicator_h + +///////////////////////////////////////////////////////////////////////////////////////// +// +// FORWARDS +// +///////////////////////////////////////////////////////////////////////////////////////// + +class plAudioInterface; +class plCoordinateInterface; +class plDrawInterface; +class plSimulationInterface; +class plObjInterface; +class plAGModifier; + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// +#include "../pnFactory/plCreatable.h" +#include "plAvDefs.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// +class plAGChannel; +class plScalarChannel; + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEFINITIONS +// +///////////////////////////////////////////////////////////////////////////////////////// +/** \class plAGApplicator + Takes the end of a channel tree and applies it to a scene object. + A transform applicator takes a matrix-typed-tree and applies it to + the transform of the scene object. + Other applicators might take floats and use them to animate alpha channels, + etc. */ +class plAGApplicator : public plCreatable +{ +public: + // -- methods -- + /** Base constructor. */ + plAGApplicator(); + plAGApplicator(const char *channelName); + virtual ~plAGApplicator(); + + /** Return our single input channel. Applicators only ever + have a single input channel; you can always use blend + or combine nodes to merge channels before routing + them into the applicator. */ + plAGChannel *GetChannel() { return fChannel; } + + /** Set our input channel. Does not free the previous input channel. */ + void SetChannel(plAGChannel *channel) { fChannel = channel; } + + void SetChannelName(const char *name); + const char * GetChannelName(); + + /** Optionally suppress the action of this applicator. + The applicator can still be forced to apply using the force + paramater of the Apply function. */ + void Enable(hsBool on) { fEnabled = on; } + + /** Make a shallow copy of the applicator. Keep the same input channel + but do not clone the input channel. */ + virtual plAGApplicator *CloneWithChannel(plAGChannel *channel); + + /** What animation type do we have? Used to determine whether two + applicators are trying to animate the same thing or whether they + can coexist peacefully. */ + virtual plAGPinType GetPinType() { return kAGPinUnknown; } + + // Join the incoming channel (if possible) to ours + + + /** Determine whether the given applicator can be blended to ours. If so, this + would be effected by pulling the input channel from the other applicator, + blending it with our input channel via a new blend node, attaching that blend + node as our new input, and throwing the other applicator away. */ + virtual hsBool CanBlend(plAGApplicator *app); + /** Combine the two applicators if possible. \sa CanBlend */ + virtual plAGChannel * MergeChannel(plAGApplicator *app, plAGChannel *channel, + plScalarChannel *blend, int blendPriority); + + /** \bug It makes no sense for an applicator to combine because combination always + results in a different data type, which would require a different applicator. */ + virtual hsBool CanCombine(plAGApplicator *app) { return false; } + + /** Apply our channel's data to the scene object, via the modifier. + This is the only function that actually changes perceivable scene state. */ + void Apply(const plAGModifier *mod, double time, hsBool force = false); // Apply our channel's data to the modifier + + // this is pretty much a HACK to support applicators that want to stick around when + // their channel is gone so they can operate on the next channel that comes in + // the RIGHT way to do this is to make applicators support the Detach() protocol just + // like channels... + virtual hsBool AutoDelete() { return true; } // should we remove it when its input channel is gone? + + // PlOP + CLASSNAME_REGISTER( plAGApplicator ); + GETINTERFACE_ANY( plAGApplicator, plCreatable ); + + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *s, hsResMgr *mgr); + +protected: + // -- methods -- + virtual void IApply(const plAGModifier *mod, double time) = 0; + + // give derived classes access to the object interfaces + plAudioInterface * IGetAI(const plAGModifier *modifier) const; + plCoordinateInterface * IGetCI(const plAGModifier *modifier) const; + plDrawInterface * IGetDI(const plAGModifier *modifier) const; + plSimulationInterface * IGetSI(const plAGModifier *modifier) const; + plObjInterface * IGetGI(const plAGModifier *modifier, UInt16 classIdx) const; + + // -- members -- + plAGChannel *fChannel; + hsBool fEnabled; + char *fChannelName; +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGChannel.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGChannel.cpp new file mode 100644 index 00000000..af6bfcb7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGChannel.cpp @@ -0,0 +1,112 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// singular +#include "plAGChannel.h" + +// local +#include "plAGModifier.h" + +// global +#include "hsTypes.h" +#include "hsResMgr.h" + +#include "plAGAnimInstance.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plAGChannel +// +///////////////////////////////////////////////////////////////////////////////////////// + +plAGChannel::plAGChannel() +{ +#ifdef TRACK_AG_ALLOCS + fName = gGlobalAnimName; + RegisterAGAlloc(this, gGlobalChannelName, gGlobalAnimName, this->ClassIndex()); +#else // TRACK_AG_ALLOCS + fName = nil; +#endif // TRACK_AG_ALLOCS +} + +// DTOR +plAGChannel::~plAGChannel() +{ + // we do not own the "fName" string, so don't delete it! +#ifdef TRACK_AG_ALLOCS + UnRegisterAGAlloc(this); +#endif // TRACK_AG_ALLOCS +} + +// MAKECOMBINE +plAGChannel * plAGChannel::MakeCombine(plAGChannel *channelA) +{ + return nil; +} + +// MAKEBLEND +plAGChannel * plAGChannel::MakeBlend(plAGChannel *channelA, plScalarChannel *blend, int blendPriority) +{ + return nil; +} + +// DETACH +// If the channel being detached is us, let our caller know to replace us +// by return NIL. +plAGChannel * plAGChannel::Detach(plAGChannel *channel) +{ + if (this == channel) + { + return nil; + } else { + return this; + } +} + +// OPTIMIZE +plAGChannel * plAGChannel::Optimize(double time) +{ + // the basic channel can't optimize... + return this; +} + +// WRITE +void plAGChannel::Write(hsStream *stream, hsResMgr *mgr) +{ + plCreatable::Write(stream, mgr); + + stream->WriteSafeString(fName); +} + +// READ +void plAGChannel::Read(hsStream *stream, hsResMgr *mgr) +{ + plCreatable::Read(stream, mgr); + + fName = stream->ReadSafeString(); +} + +//////////////////////////////////////////////////////////////////////////////////// + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGChannel.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGChannel.h new file mode 100644 index 00000000..33fbc03c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGChannel.h @@ -0,0 +1,182 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/** \file plAGChannel.h + \brief The animation class for the AniGraph animation system + WHAT'S AG STAND FOR? + AG stands for animation graph. It's a directed acyclic graph of animation data sources and operations. + + WHAT'S A CHANNEL? + A channel is a stream of animation data. + A channel is a node in an animation graph. + Animation data can come from anything; keyframes, IK, physics, etc. + Channels are often grouped together using blending nodes. + Blending nodes are also a type of channel. + See where I'm going with this? + + HOW IS THIS DIFFERENT FROM PLCONTROLLER? + It's very similar, but it's designed to be extremely lightweight; animation graphs are set up and torn down + quickly and with great frequency. They are not persistent, and their only state (besides cache) + is their interconnectedness. + + HOW DO THEY GET SAVED? + Their client must recreate them at read/load time. Since they are stateless, all information necessary + to recreate them is necessarily held by the client. + + ARE THEY REFERENCE COUNTED? + No. The entire graph is owned by the creator. + Deleting the top node of a graph deletes the entire graph. + If we decide we want to share subgraphs, we'll add reference counting -- easy since cycles are illegal + + HOW DO THEY INTEGRATE WITH PLCONTROLLERS? + Once we decide the animation graph approach is workable and effective, we'll combine the plController + concept with the plAGChannel concept. + Until then, there will be a handful of channel types that adapt controllers into animation graphs. + + WHAT DOES "COMBINE" MEAN? + Merging more than one types of animation data into a single output that preserves the value + of both. + + WHAT DOES "BLEND" MEAN? + Merging two similar types of animation data using a weight to "fade" from one to the other + + WHAT IS ZEROSTATE? + This is a static channel that always returns the value at time zero. It is used as a reference for animation + scaling (amplitude). + + WHAT'S AN APPLICATOR? + In order to cut down on the number of derived channel classes for different Value() and Apply() behaviors, + applicators were created. At the end of each graph is an applicator, which specifies what should be done + with the result of a call to Value(). + + \ingroup Avatar + \ingroup AniGraph +*/ + +#ifndef PLAGCHANNEL_H +#define PLAGCHANNEL_H + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// + +#include "../pnFactory/plCreatable.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// FORWARDS +// +///////////////////////////////////////////////////////////////////////////////////////// + +class plAGModifier; +class plAnimTimeConvert; +class plAGChannel; +class plScalarChannel; + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEFINITIONS +// +///////////////////////////////////////////////////////////////////////////////////////// + +/** \class plAGChannel + An object that emits data of a specific type. Fundamental building + block of the animation graph. */ +class plAGChannel : public plCreatable +{ +public: + + // -- methods -- + /** Default constructor for the base class. */ + plAGChannel(); + + /** Free an allocated name. Does not release any upstream nodes. */ + virtual ~plAGChannel(); + + // AG PROTOCOL + /** Combine the given channel with this channel, allocating and returning + a new node which does the combination. It's up to the caller to + manage the lifetime of the new node. */ + virtual plAGChannel * MakeCombine(plAGChannel * channelB); + + /** Blend the given channel with this channel, using a third channel (which + must output a float/scalar value) to crossfade between the two. + As the blendChannel varies, the blend will vary. */ + virtual plAGChannel * MakeBlend(plAGChannel * channelB, plScalarChannel * blendChannel, int blendPriority); + + /** Create a "static clone" of this channel which always returns this channel's + value at time zero. */ + virtual plAGChannel * MakeZeroState() = 0; + + /** If we're potentially sharing this channel with other plAGMasterMods, we'll + want to insert a channel in the graph for cache info. This function returns + either the cache channel (replacing us) or ourself. */ + virtual plAGChannel * MakeCacheChannel(plAnimTimeConvert *atc) { return this; } + + /** Create a new channel which converts global time to local time + and attach it downstream from this channel. This allows you to + convert an animation from one timespace to another - critical for + blending. + local-time-animation <-- timescale <-- world-time-animation */ + virtual plAGChannel * MakeTimeScale(plScalarChannel *timeSource) = 0; + + /** Is the animation moving at the given world time? Takes into account + start/stop messages that haven't been applied yet, ease curves, etc. */ + virtual hsBool IsStoppedAt(double wSecs) { return true; } + + /** Detach the given channel from our graph. If this is the channel in + question, returns any upstream channels so they can be reattached. + If this is not the channel in question, passes the request upstream + and does any reattachment necessary. */ + virtual plAGChannel * Detach(plAGChannel * channel); + + /** Return the optimized version of this channel. May be a completely + different channel; will collapse out inactive subgraphs. */ + virtual plAGChannel * Optimize(double time); + + // \{ + /** The name of the channel is used to dynamically attach to sub-parts of an + object. */ + virtual const char * GetName() { return fName; }; + virtual void SetName(char * name) { fName = name; }; + // \} + + // PLASMA PROTOCOL + // rtti + CLASSNAME_REGISTER( plAGChannel ); + GETINTERFACE_ANY( plAGChannel, plCreatable ); + + // persistence + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *s, hsResMgr *mgr); + +protected: + const char * fName; + +}; + +#endif PLAGCHANNEL_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterMod.cpp new file mode 100644 index 00000000..161a6b68 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterMod.cpp @@ -0,0 +1,806 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// singular +#include "plAGMasterMod.h" + +// local +#include "plAGAnim.h" +#include "plAGAnimInstance.h" +#include "plAGModifier.h" +// #include "plAvatarAnim.h" +#include "plAGMasterSDLModifier.h" +#include "plMatrixChannel.h" + +// global +#include "hsResMgr.h" +#include "plgDispatch.h" + +// other +// #include "../pnMessage/plRefMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../pnMessage/plSDLModifierMsg.h" +#include "../pnMessage/plSDLNotificationMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +//////////////// +// PLAGMASTERMOD +//////////////// +// Coordinates the activities of a bunch of plAGModifiers +// std::map plAGMasterMod::fInstances; + +// CTOR +plAGMasterMod::plAGMasterMod() +: fTarget(nil), + fNeedEval(false), + fFirstEval(true), + fAGMasterSDLMod(nil), + fNeedCompile(false), + fIsGrouped(false), + fIsGroupMaster(false), + fMsgForwarder(nil) +{ +} + +// DTOR +plAGMasterMod::~plAGMasterMod() +{ +} + +void plAGMasterMod::Write(hsStream *stream, hsResMgr *mgr) +{ + plModifier::Write(stream, mgr); + + int length = 0; + stream->WriteSwap32(length); // backwards compatability. Nuke on next format change. + stream->WriteSwap32(fPrivateAnims.size()); + int i; + for (i = 0; i < fPrivateAnims.size(); i++) + { + mgr->WriteKey(stream, fPrivateAnims[i]->GetKey()); + } + stream->Writebool(fIsGrouped); + stream->Writebool(fIsGroupMaster); + if (fIsGroupMaster) + mgr->WriteKey(stream, fMsgForwarder->GetKey()); + + // maybe later... WriteCachedMessages(stream, mgr); +} + +void plAGMasterMod::Read(hsStream * stream, hsResMgr *mgr) +{ + plModifier::Read(stream, mgr); + + ////////////////////////////////////////// + int nameLength = stream->ReadSwap32(); // Unused. Nuke next format change. + char *junk = TRACKED_NEW char[nameLength+1]; // + stream->Read(nameLength, junk); // + junk[nameLength] = 0; // + delete [] junk; // + ////////////////////////////////////////// + + int numPrivateAnims = stream->ReadSwap32(); + fPrivateAnims.reserve(numPrivateAnims); // pre-allocate for performance + int i; + for (i = 0; i < numPrivateAnims; i++) + { + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPrivateAnim); + mgr->ReadKeyNotifyMe(stream, msg, plRefFlags::kActiveRef); + } + fIsGrouped = stream->Readbool(); + fIsGroupMaster = stream->Readbool(); + if (fIsGroupMaster) + { + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0); + mgr->ReadKeyNotifyMe(stream, msg, plRefFlags::kActiveRef); + } + + // maybe later... ReadCachedMessages(stream, mgr); +} + +// ADDTARGET +// Collect all the plAGModifiers from our children and attach private anims. +void plAGMasterMod::AddTarget(plSceneObject * object) +{ + plSynchEnabler p(false); // turn off dirty tracking while in this function + + fTarget = object; + int autoIdx = -1; + int initialIdx = -1; + int timeIdx = 0; + int i; + + for (i = 0; i < fPrivateAnims.size(); i++) + { + plATCAnim *atcAnim = plATCAnim::ConvertNoRef(fPrivateAnims[i]); + if (!atcAnim) + continue; + + if (atcAnim->GetAutoStart()) + autoIdx = i; + if (atcAnim->GetInitial() != -1) + initialIdx = i; + if (atcAnim->GetStart() < fPrivateAnims[timeIdx]->GetStart()) + timeIdx = i; + } + + int masterIdx; + if (autoIdx != -1) + masterIdx = autoIdx; // If something autostarts, it wins. + else if (initialIdx != -1) + masterIdx = initialIdx; // Otherwise, the fellow with the @initial point wins + else + masterIdx = timeIdx; // Default case: the earliest anim wins + + for (i = 0; i < fPrivateAnims.size(); i++) + { + AttachAnimationBlended(fPrivateAnims[i], i == masterIdx ? 1.f : 0.f); + } + + // Force one eval after we init + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + + if (!fIsGrouped || fIsGroupMaster) + { + // add sdl modifier + delete fAGMasterSDLMod; + fAGMasterSDLMod = TRACKED_NEW plAGMasterSDLModifier; + object->AddModifier(fAGMasterSDLMod); + } +} + +void plAGMasterMod::RemoveTarget(plSceneObject* o) +{ + hsAssert(o == fTarget, "Removing target I don't have"); + + DetachAllAnimations(); + + // remove sdl modifier + if (o) + { + if (fAGMasterSDLMod) + o->RemoveModifier(fAGMasterSDLMod); + } + delete fAGMasterSDLMod; + fAGMasterSDLMod=nil; + + fTarget = nil; +} + +#include "plProfile.h" + +plProfile_CreateTimer("ApplyAnimation", "Animation", ApplyAnimation); +plProfile_CreateTimer(" AffineValue", "Animation", AffineValue); +plProfile_CreateTimer(" AffineInterp", "Animation", AffineInterp); +plProfile_CreateTimer(" AffineBlend", "Animation", AffineBlend); +plProfile_CreateTimer(" AffineCompose", "Animation", AffineCompose); +plProfile_CreateTimer(" AffineApplicator", "Animation", MatrixApplicator); +plProfile_CreateTimer("AnimatingPhysicals", "Animation", AnimatingPhysicals); +plProfile_CreateTimer("StoppedAnimPhysicals", "Animation", StoppedAnimPhysicals); + +// IEVAL +hsBool plAGMasterMod::IEval(double secs, hsScalar del, UInt32 dirty) +{ + if (fFirstEval) + { + int i; + for (i = 0; i < fAnimInstances.size(); i++) + fAnimInstances[i]->SearchForGlobals(); + + fFirstEval = false; + } + ApplyAnimations(secs, del); + + // We might get registered for just a single eval. If we don't need to eval anymore, unregister + if (!fNeedEval) + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + + + return true; +} + +// APPLYANIMATIONS +void plAGMasterMod::ApplyAnimations(double time, hsScalar elapsed) +{ + plProfile_BeginLap(ApplyAnimation, this->GetKey()->GetUoid().GetObjectName()); + + // update any fades + for (int i = 0; i < fAnimInstances.size(); i++) + { + fAnimInstances[i]->ProcessFade(elapsed); + } + + AdvanceAnimsToTime(time); + + plProfile_EndLap(ApplyAnimation,this->GetKey()->GetUoid().GetObjectName()); +} + +void plAGMasterMod::AdvanceAnimsToTime(double time) +{ + if(fNeedCompile) + Compile(time); + + for(plChannelModMap::iterator j = fChannelMods.begin(); j != fChannelMods.end(); j++) + { + plAGModifier *mod = (*j).second; + mod->Apply(time); + } +} + +void plAGMasterMod::SetNeedCompile(bool needCompile) +{ + fNeedCompile = true; +} + +void plAGMasterMod::Compile(double time) +{ + plChannelModMap::iterator end = fChannelMods.end(); + fNeedCompile = false; + + for(plChannelModMap::iterator j = fChannelMods.begin(); j != end; j++) + { + plAGModifier *mod = (*j).second; + plAGApplicator *app = mod->GetApplicator(kAGPinTransform); + + if(app) { + plAGChannel *channel = app->GetChannel(); + if(channel) + { + plMatrixChannel *topChannel = plMatrixChannel::ConvertNoRef(channel); + if(topChannel) + topChannel->Optimize(time); + } + } + } +} + +void plAGMasterMod::DumpAniGraph(const char *justThisChannel, bool optimized, double time) +{ + plChannelModMap::iterator end = fChannelMods.end(); + fNeedCompile = false; + + for(plChannelModMap::iterator j = fChannelMods.begin(); j != end; j++) + { + plAGModifier *mod = (*j).second; + if(!justThisChannel || stricmp(justThisChannel, mod->GetChannelName()) == 0) + { + plAGApplicator *app = mod->GetApplicator(kAGPinTransform); + + if(app) { + plAGChannel *channel = app->GetChannel(); + if(channel) + { + plMatrixChannel *topChannel = plMatrixChannel::ConvertNoRef(channel); + if(topChannel) + { + hsStatusMessageF("AGModifier: <%s>", mod->GetChannelName()); + topChannel->Dump(1, optimized, time); + } + } + } + if(justThisChannel) + break; + } + } +} + +// GETCHANNELMOD(name) +// Get the modifier that controls the channel with the given name +plAGModifier * plAGMasterMod::GetChannelMod(const char * name, hsBool dontCache ) const +{ + plAGModifier * result = nil; + std::map::const_iterator i = fChannelMods.find(name); + + if (i != fChannelMods.end()) { + result = (*i).second; + } else { + plSceneObject *SO = GetTarget(0); + if(SO) { + result = IFindChannelMod(SO, name); + if(result && !dontCache) { + ICacheChannelMod(result); + } + } + } + return result; +} + +// CACHECHANNELMOD +plAGModifier * plAGMasterMod::ICacheChannelMod(plAGModifier *mod) const +{ + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0); + hsgResMgr::ResMgr()->SendRef(mod, msg, plRefFlags::kActiveRef); + + return mod; +} + +// IFINDAGMOD (sceneObject) +// See if there's an ag modifier on this sceneobject. +// Doesn't check for multiples; just returns the first one. +plAGModifier * plAGMasterMod::IFindChannelMod(const plSceneObject *SO, const char *name) const +{ + const plCoordinateInterface * CI = SO->GetCoordinateInterface(); + + const plAGModifier * constMod = static_cast(FindModifierByClass(SO, plAGModifier::Index())); + plAGModifier * mod = const_cast(constMod); + + if(mod) + { + const char *modName = mod->GetChannelName(); + if(stricmp(name, modName) == 0) + return mod; + } + + if(CI) + { + int childCount = CI->GetNumChildren(); + for (int i = 0; i < childCount; i++) + { + const plSceneObject * subChild = CI->GetChild(i)->GetOwner(); + plAGModifier * mod = IFindChannelMod(subChild, name); + + if(mod) + return mod; + } + } + return nil; +} + +// ATTACHANIMATIONBLENDED(anim, blend) +plAGAnimInstance * plAGMasterMod::AttachAnimationBlended(plAGAnim *anim, + hsScalar blendFactor /* = 0 */, + UInt16 blendPriority /* plAGMedBlendPriority */, + hsBool cache /* = false */) +{ + plAGAnimInstance *instance = nil; + plAnimVector::iterator i; + if(anim) + { + fNeedCompile = true; // need to recompile the graph since we're editing it... + for (i = fPrivateAnims.begin(); i != fPrivateAnims.end(); i++) + { + if (*i == anim) + break; + } + if (i == fPrivateAnims.end()) // Didn't find it. Ref it! + { + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPublicAnim); + hsgResMgr::ResMgr()->SendRef(anim, msg, plRefFlags::kActiveRef); + } + instance = TRACKED_NEW plAGAnimInstance(anim, this, blendFactor, blendPriority, cache, false); + fAnimInstances.push_back(instance); + + plATCAnim *atcAnim = plATCAnim::ConvertNoRef(anim); + if (atcAnim) + { + fATCAnimInstances.push_back(instance); + ISetupMarkerCallbacks(atcAnim, instance->GetTimeConvert()); + } + IRegForEval(HasRunningAnims()); + } + return instance; +} + +// ATTACHANIMATIONBLENDED(name, blend) +plAGAnimInstance * plAGMasterMod::AttachAnimationBlended(const char *name, hsScalar blendFactor /* = 0 */, UInt16 blendPriority, hsBool cache /* = false */) +{ + plAGAnimInstance *instance = nil; + plAGAnim *anim = plAGAnim::FindAnim(name); + + if(anim) + { + instance = AttachAnimationBlended(anim, blendFactor, blendPriority, cache); + } + return instance; +} + +void plAGMasterMod::PlaySimpleAnim(const char *name) +{ + plATCAnim *anim = plATCAnim::ConvertNoRef(plAGAnim::FindAnim(name)); + plAGAnimInstance *instance = nil; + if (anim) + { + if (FindAnimInstance(name)) + return; + + instance = AttachAnimationBlended(anim, 1.f, (UInt16)kAGMaxBlendPriority, false); + } + + if (instance) + { + instance->SetLoop(false); + instance->Start(); + + plAGDetachCallbackMsg *msg = TRACKED_NEW plAGDetachCallbackMsg(GetKey(), kStop); + msg->SetAnimName(name); + instance->GetTimeConvert()->AddCallback(msg); + hsRefCnt_SafeUnRef(msg); + } +} + +// FINDANIMINSTANCE +// Look for an animation instance of the given name on the modifier. +// If we need this to be fast, should make it a map rather than a vector +plAGAnimInstance * plAGMasterMod::FindAnimInstance(const char *name) +{ + plAGAnimInstance *result = nil; + + if (name) + { + for (int i = 0; i < fAnimInstances.size(); i++) + { + plAGAnimInstance *act = fAnimInstances[i]; + const char *eachName = act->GetName(); + + if( stricmp(eachName, name) == 0) + { + result = act; + break; + } + } + } + return result; +} + +// FINDORATTACHINSTANCE +plAGAnimInstance * plAGMasterMod::FindOrAttachInstance(const char *name, hsScalar blendFactor) +{ + plAGAnimInstance *result = FindAnimInstance(name); + if(result) + { + // if it's already attached, we need to set the blend + result->SetBlend(blendFactor); + } else { + result = AttachAnimationBlended(name, blendFactor); + } + return result; +} + + +// GETANIMINSTANCE +plAGAnimInstance * plAGMasterMod::GetAnimInstance(int i) +{ + return fAnimInstances[i]; +} + +// GETNUMANIMATIONS +int plAGMasterMod::GetNumAnimations() +{ + return fAnimInstances.size(); +} + +// GETNUMPRIVATEANIMATIONS +int plAGMasterMod::GetNumPrivateAnimations() +{ + return fPrivateAnims.size(); +} + +int plAGMasterMod::GetNumATCAnimations() +{ + return fATCAnimInstances.size(); +} + +plAGAnimInstance *plAGMasterMod::GetATCAnimInstance(int i) +{ + return fATCAnimInstances[i]; +} + +void plAGMasterMod::DetachAllAnimations() +{ + int nInstances = fAnimInstances.size(); + + for (int i = nInstances - 1; i >= 0; i--) + { + plAGAnimInstance * instance = fAnimInstances[i]; + if(instance) + { + DetachAnimation(instance); + // delete instance; + } + } + fAnimInstances.clear(); + fPrivateAnims.clear(); + fATCAnimInstances.clear(); +} + +// DETACHANIMATION(plAGAnimInstance *) +void plAGMasterMod::DetachAnimation(plAGAnimInstance *anim) +{ + plInstanceVector::iterator i; + plAnimVector::iterator j; + + fNeedCompile = true; // need to recompile the graph since we're editing it... + + for ( i = fAnimInstances.begin(); i != fAnimInstances.end(); i++) + { + plAGAnimInstance *instance = *i; + + if(instance == anim) + { + // DetachAnimation(instance); + instance->DetachChannels(); + + // Need to release it if it's not a private anim + const plAGAnim *agAnim = instance->GetAnimation(); + for (j = fPrivateAnims.begin(); j != fPrivateAnims.end(); j++) + { + if (*j == agAnim) + break; + } + if (j == fPrivateAnims.end()) // We didn't find it + GetKey()->Release(agAnim->GetKey()); + + delete instance; + i = fAnimInstances.erase(i); + break; + } + } + for ( i = fATCAnimInstances.begin(); i != fATCAnimInstances.end(); i++) + { + plAGAnimInstance *instance = *i; + + if(instance == anim) + { + i = fATCAnimInstances.erase(i); + break; + } + } +} + +// DETACHANIMATION(name) +void plAGMasterMod::DetachAnimation(const char *name) +{ + plAGAnimInstance *anim = FindAnimInstance(name); + if(anim) { + DetachAnimation(anim); + } +} + +void plAGMasterMod::DumpCurrentAnims(const char *header) +{ + if(header) + hsStatusMessageF("Dumping Armature Anim Stack: %s", header); + int nAnims = fAnimInstances.size(); + for(int i = nAnims - 1; i >= 0; i--) + { + plAGAnimInstance *inst = fAnimInstances[i]; + const char *name = inst->GetName(); + float blend = inst->GetBlend(); + + hsStatusMessageF("%d: %s with blend of %f\n", i, name, blend); + } +} + +// MSGRECEIVE +// receive trigger messages +hsBool plAGMasterMod::MsgReceive(plMessage* msg) +{ + plAnimCmdMsg* cmdMsg; + plAGCmdMsg* agMsg; + int i; + + plSDLNotificationMsg* nMsg = plSDLNotificationMsg::ConvertNoRef(msg); + if (nMsg) + { + // Force a single eval + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + return true; + } + + if (cmdMsg = plAnimCmdMsg::ConvertNoRef(msg)) + { + const char *targetName = cmdMsg->GetAnimName(); + + if (!targetName) + targetName = ENTIRE_ANIMATION_NAME; + + plAGAnimInstance *inst = FindAnimInstance(targetName); + if (inst != nil) + { + if (cmdMsg->CmdChangesAnimTime()) + { + for (i = 0; i < GetNumAnimations(); i++) + { + plAGAnimInstance *currInst = GetAnimInstance(i); + if (currInst != inst && currInst->GetAnimation()->SharesPinsWith(inst->GetAnimation())) + currInst->SetBlend(0); + } + inst->SetBlend(1); + } + + inst->HandleCmd(cmdMsg); + } + + return true; + } + + if (agMsg = plAGCmdMsg::ConvertNoRef(msg)) + { + if (agMsg->Cmd(plAGCmdMsg::kSetAnimTime)) + { + for (int i = 0; i < fAnimInstances.size(); i++) + { + plAGAnimInstance *inst = fAnimInstances[i]; + inst->SetCurrentTime(agMsg->fAnimTime, true); + } + + return true; + } + + plAGAnimInstance *inst = FindAnimInstance(agMsg->GetAnimName()); + if (inst != nil) + { + if (agMsg->Cmd(plAGCmdMsg::kSetBlend)) + inst->Fade(agMsg->fBlend, agMsg->fBlendRate, plAGAnimInstance::kFadeBlend); + if (agMsg->Cmd(plAGCmdMsg::kSetAmp)) + inst->Fade(agMsg->fAmp, agMsg->fAmpRate, plAGAnimInstance::kFadeAmp); + } + return true; + } + + plAGInstanceCallbackMsg *agicMsg = plAGInstanceCallbackMsg::ConvertNoRef(msg); + if (agicMsg != nil) + { + if (agicMsg->fEvent == kStart) + { + IRegForEval(true); + } + else if (agicMsg->fEvent == kStop) + { + if (!HasRunningAnims()) + IRegForEval(false); + } + else // Just force a single eval + { + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + } + return true; + } + + plAGDetachCallbackMsg *detachMsg = plAGDetachCallbackMsg::ConvertNoRef(msg); + if (detachMsg != nil) + { + DetachAnimation(detachMsg->GetAnimName()); + } + + plGenRefMsg *genRefMsg; + if (genRefMsg = plGenRefMsg::ConvertNoRef(msg)) + { + plAGAnim *anim; + if (anim = plAGAnim::ConvertNoRef(genRefMsg->GetRef())) + { + if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest)) + { + if (genRefMsg->fType == kPrivateAnim) + fPrivateAnims.push_back(anim); + } + else + { + if (genRefMsg->fType == kPrivateAnim) + { + plAnimVector::iterator i = fPrivateAnims.begin(); + for (i; i != fPrivateAnims.end(); i++) + { + plAGAnim *currAnim = *i; + + if(currAnim == anim) + { + i = fPrivateAnims.erase(i); + break; + } + } + } + } + + return true; + } + plAGModifier *agmod; + if (agmod = plAGModifier::ConvertNoRef(genRefMsg->GetRef())) + { + if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest)) + fChannelMods[agmod->GetChannelName()] = agmod; + else + fChannelMods.erase(agmod->GetChannelName()); + + return true; + } + plMsgForwarder *msgfwd; + if (msgfwd = plMsgForwarder::ConvertNoRef(genRefMsg->GetRef())) + { + if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest)) + fMsgForwarder = msgfwd; + else + fMsgForwarder = nil; + + return true; + } + } + plRefMsg* refMsg = plRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + AddTarget(plSceneObject::ConvertNoRef(refMsg->GetRef())); + else + RemoveTarget(plSceneObject::ConvertNoRef(refMsg->GetRef())); + + return true; + } + + return plModifier::MsgReceive(msg); +} + +void plAGMasterMod::IRegForEval(hsBool val) +{ + if (fNeedEval == val) + return; + + fNeedEval = val; + if (val) + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + else + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +hsBool plAGMasterMod::HasRunningAnims() +{ + int i; + hsBool needEval = false; + for (i = 0; i < fAnimInstances.size(); i++) + { + if (!fAnimInstances[i]->IsFinished()) + { + needEval = true; + break; + } + } + return needEval; +} + +// +// Send SDL sendState msg to object's plAGMasterSDLModifier +// +hsBool plAGMasterMod::DirtySynchState(const char* SDLStateName, UInt32 synchFlags) +{ + if(GetNumTargets() > 0 && (!fIsGrouped || fIsGroupMaster)) + { + plSceneObject *sObj = GetTarget(0); + if(sObj) + return sObj->DirtySynchState(SDLStateName, synchFlags); + } + return false; +} + +void plAGMasterMod::SetIsGroupMaster(bool master, plMsgForwarder* msgForwarder) +{ + fIsGroupMaster = master; + fMsgForwarder = msgForwarder; +} + +void plAGMasterMod::SetIsGrouped(bool grouped) +{ + fIsGrouped = true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterMod.h new file mode 100644 index 00000000..9ed493f9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterMod.h @@ -0,0 +1,252 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/** \file plAGMasterMod.h + \brief The animation class for the AniGraph animation system + + \ingroup Avatar + \ingroup AniGraph +*/ +#ifndef PLAGMASTERMOD_INC +#define PLAGMASTERMOD_INC + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// +#include "../pnModifier/plModifier.h" +#include "plAGChannel.h" +#include "plAvDefs.h" +#include "../pnKeyedObject/plMsgForwarder.h" + +// templates +#include "hsStlUtils.h" +#include "hsStlSortUtils.h" + + +class plAGModifier; +class plAGAnimInstance; +class plAGAnim; +class plATCAnim; +class plAGMasterSDLModifier; + +//////////////// +// +// PLAGMASTERMOD +// +//////////////// +/** \class plAGMasterMod + A modifier which can apply animations to scene objects. + Works together with a plAGModifier, which can apply animation to a + single scene object. A master modifier hooks up to a family of + ag modifiers to do its job. + An animation (plAGAnim) can have many different channels, but the + user can just pass it to a single master mod to apply it to all the + channels owned by that master. The master will take care of hooking + each channel up to an ag modifier. + The goal is to make an animation a conceptually (and practically) simple + object to work with, whether or not it has multiple channels. + + \sa plAGAnim, plAGAnimInstance, plAGModifier + */ +class plAGMasterMod : public plModifier +{ + friend class plAGMasterSDLModifier; +public: + /** Default constructor. Primarily for use by the class factory. */ + plAGMasterMod(); + /** Free the name and any other miscellany. */ + virtual ~plAGMasterMod(); + + /** Find an individual plAGModifier of the given name under our control. */ + plAGModifier * GetChannelMod(const char * name, hsBool dontCache = false) const; + + /** \name Managing Animations */ + // \{ + // AGANIM PROTOCOL + /** Attach the given animation object with the given blend factor. + If there's no animation already attached to blend with, the + animation will be attached at full strength. */ + plAGAnimInstance *AttachAnimationBlended(plAGAnim *anim, hsScalar blendFactor = 0, + UInt16 blendPriority = kAGMedBlendPriority, + hsBool cache = false); + + /** Look up the given animation by name and attach it + with the given blend factor. */ + plAGAnimInstance *AttachAnimationBlended(const char *name, hsScalar blendFactor = 0, + UInt16 blendPriority = kAGMedBlendPriority, + hsBool cache = false); + + /** Play a simple anim (one that doesn't affect root) once and auto detach. + Intended for Zandi's facial animations that run seperate from the behaviors. */ + void PlaySimpleAnim(const char *name); + + /** Detach the given animation instance. Does nothing + if the instance is not managed by this master mod. */ + void DetachAnimation(plAGAnimInstance *instance); + void DetachAllAnimations(); + + /** Detach the given animation by name. Searches for + any instances derived from animations with the + given name and removes them. */ + void DetachAnimation(const char *name); + // \} + + /** Print the current animation stack to the console. + Will list all the animations and their blend strengths. + Animations later in the list will mask animations earlier + in the list. */ + void DumpCurrentAnims(const char *header); + + /** Find and return any animation instances with the + given name on this master modifer. */ + plAGAnimInstance *FindAnimInstance(const char *name); + + /** Return the Ith animation instance, based on blend + order. Of dubious utility, but, y'know. */ + plAGAnimInstance *GetAnimInstance(int i); + + /** Attach the animation if it's not already attached. If + it is attached, return the instance. + Note that if it's attached by this function, it + will be on top of the stack, but if it was already + attached, it could be anywhere, including buried under + a bunch of other animations. If it's important that it be + on top of the stack, you may need to detach it first. */ + plAGAnimInstance *FindOrAttachInstance(const char *name, hsScalar blendFactor); + + /** Return the number of animations available. */ + int GetNumAnimations(); + /** Return the number of animations that are privately + owned by this modifier. + Animations may be either shared in a general pool, + or privately owned by a mastermod. */ + int GetNumPrivateAnimations(); + + int GetNumATCAnimations(); + plAGAnimInstance *GetATCAnimInstance(int i); + + /** Apply all our animations to all our parts. + \param timeNow is the current world time + \param elapsed is the time since the previous frame */ + void ApplyAnimations(double timeNow, hsScalar elapsed); + + /** Runs through our anims and applies them, without + processing fades. This is used when we load in anim + state from the server, and need to advance it to a + certain point before enabling callbacks */ + void AdvanceAnimsToTime(double time); + + /** Change the connectivity in the graph so that inactive animations are bypassed. + The original connectivity information is kept, so if the activity of different + animations is changed (such as by changing blend biases or adding new animations, + the graph can be compiled again to the correct state. */ + void Compile(double time); + + /** We've done something that invalidates the cached connectivity in the graph. + Mark this for fixup. */ + void SetNeedCompile(bool needCompile); + + /** List the animationg graph to stdOut, with a ASCII representation of the tree + structure. Done by recursively dumping the graph; some types of nodes will have + more output information than others. + */ + void DumpAniGraph(const char *channel, bool optimized, double time); + + /** Set whether or not this is the "group master" so grouped animations will only have + one member getting/setting sdl animation state in order to synch the anims + */ + void SetIsGrouped(bool grouped); + void SetIsGroupMaster(bool master, plMsgForwarder* msgForwarder); + + // PLASMA PROTOCOL + virtual int GetNumTargets() const { return fTarget ? 1 : 0; } + virtual plSceneObject* GetTarget(int w) const { /* hsAssert(w < GetNumTargets(), "Bad target"); */ return fTarget; } + virtual void AddTarget(plSceneObject * object); + virtual void RemoveTarget(plSceneObject * object); + + hsBool plAGMasterMod::MsgReceive(plMessage* msg); + + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream * stream, hsResMgr *mgr); + + hsBool HasRunningAnims(); + hsBool DirtySynchState(const char* SDLStateName, UInt32 synchFlags); + + CLASSNAME_REGISTER( plAGMasterMod ); + GETINTERFACE_ANY( plAGMasterMod, plModifier ); + +protected: + // -- methods -- + plAGModifier * ICacheChannelMod(plAGModifier *mod) const; + plAGModifier * IFindChannelMod(const plSceneObject *obj, const char *name) const; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + + virtual void IApplyDynamic() {}; // dummy function required by base class + + // Find markers in an anim for environment effects (footsteps) + virtual void ISetupMarkerCallbacks(plATCAnim *anim, plAnimTimeConvert *atc) {} + + // -- members + plSceneObject* fTarget; + + // a map from channel names to ag modifiers within our domain + typedef std::map plChannelModMap; + plChannelModMap fChannelMods; + + // instances which are currently attached to this master + typedef std::vector plInstanceVector; + plInstanceVector fAnimInstances; // animation instances + + // animations which are owned exclusively by this master + typedef std::vector plAnimVector; + plAnimVector fPrivateAnims; + + // animations that require AnimTimeConvert state to be synched + typedef std::vector plAnimVector; + plInstanceVector fATCAnimInstances; + + hsBool fFirstEval; + hsBool fNeedEval; + void IRegForEval(hsBool val); + + // SDL modifier which sends/recvs dynamics state + plAGMasterSDLModifier *fAGMasterSDLMod; + + bool fNeedCompile; + + bool fIsGrouped; + bool fIsGroupMaster; + plMsgForwarder* fMsgForwarder; + + enum { + kPrivateAnim, + kPublicAnim, + }; +}; + +#endif // PLAGMASTERMOD_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterSDLModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterSDLModifier.cpp new file mode 100644 index 00000000..6f959035 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterSDLModifier.cpp @@ -0,0 +1,195 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAGMasterSDLModifier.h" +#include "../plSDL/plSDL.h" +#include "../plInterp/plAnimTimeConvert.h" +#include "../pnSceneObject/plSceneObject.h" +#include "plAGMasterMod.h" +#include "plAGAnimInstance.h" +#include "plgDispatch.h" +#include "../pnMessage/plTimeMsg.h" +#include "hsTimer.h" +#include "../plMessage/plAnimCmdMsg.h" + +// static vars +char plAGMasterSDLModifier::AGMasterVarNames::kStrAtcs[]="atcs"; +char plAGMasterSDLModifier::AGMasterVarNames::kStrBlends[]="blends"; + +UInt32 plAGMasterSDLModifier::IApplyModFlags(UInt32 sendFlags) +{ + // ugly hack so bug light animation state isn't stored on the server + if (stricmp(GetTarget()->GetKeyName(), "RTOmni-BugLightTest") == 0) + return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState); + // ditto for the KI light + if (stricmp(GetTarget()->GetKeyName(), "RTOmniKILight") == 0) + return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState); + + return sendFlags; +} + +// +// copy blends values from current state into sdl +// +void plAGMasterSDLModifier::IPutBlends(plStateDataRecord* state, plAGMasterMod* agMaster) +{ + int numBlends = agMaster->GetNumPrivateAnimations(); // each private anim has a blend value + plSimpleStateVariable* blendsVar = state->FindVar(AGMasterVarNames::kStrBlends); + if (blendsVar->GetCount() != numBlends) + blendsVar->Alloc(numBlends); + + // sdl copy + int i; + for(i=0;iSet((UInt8)(agMaster->GetAnimInstance(i)->GetBlend() * 255), i); + } +} + + +// +// Copy atcs from current state into sdl +// +void plAGMasterSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + plSceneObject* sobj=GetTarget(); + hsAssert(sobj, "plAGMasterSDLModifier, nil target"); + + plAGMasterMod* agMaster=IGetObjectsAGMasterMod(sobj); + hsAssert(agMaster, "nil AGMasterMod"); + + if (agMaster) + { + IPutBlends(dstState, agMaster); + + int numAnims = agMaster->GetNumATCAnimations(); + plSDStateVariable* atcsVar = dstState->FindSDVar(AGMasterVarNames::kStrAtcs); + if (atcsVar->GetCount() != numAnims) + atcsVar->Resize(numAnims); + + // copy atcs to sdl + int i; + for(i=0;iGetStateDataRecord(i); + plAnimTimeConvert* animTimeConvert = agMaster->GetATCAnimInstance(i)->GetTimeConvert(); + + IPutATC(atcStateDataRec, animTimeConvert); + } + } +} + +// +// Given a scene object, find and return it's AGMasterMod +// +plAGMasterMod* plAGMasterSDLModifier::IGetObjectsAGMasterMod(plSceneObject* obj) +{ + int count = obj->GetNumModifiers(); + + for (int i = 0; i < count; i++) + { + plAGMasterMod * avMod = const_cast(plAGMasterMod::ConvertNoRef(obj->GetModifier(i))); + if(avMod) + return avMod; + } + + return nil; +} + +// +// Apply state in SDL record to current animation state +// +void plAGMasterSDLModifier::ISetCurrentBlends(const plStateDataRecord* state, plAGMasterMod* objAGMaster) +{ + // Check Blends + plSimpleStateVariable* blendsVar = state->FindVar(AGMasterVarNames::kStrBlends); + if (blendsVar->IsUsed()) + { + int i; + if (blendsVar->GetCount() != objAGMaster->GetNumPrivateAnimations()) + return; // bogus state + + for (i=0;iGetCount();i++) + { + UInt8 blend; + blendsVar->Get(&blend, i); + objAGMaster->GetAnimInstance(i)->SetBlend(blend / 255.f); + } + } +} + +// +// Change the object's animation state to reflect what is specified in the +// stateDataRecord. +// +void plAGMasterSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + plSceneObject* sobj=GetTarget(); + hsAssert(sobj, "plAGMasterSDLModifier, nil target"); + + plAGMasterMod* objAGMaster=IGetObjectsAGMasterMod(sobj); + hsAssert(objAGMaster, "can't find object's AGMasterSDLState"); + + ISetCurrentBlends(srcState, objAGMaster); + + plSDStateVariable* atcsVar = srcState->FindSDVar(AGMasterVarNames::kStrAtcs); + if (atcsVar->IsUsed()) + { + if (objAGMaster->GetNumATCAnimations() != atcsVar->GetCount()) + return; + + int i; + for(i=0;iGetCount(); i++) + { + plStateDataRecord* atcStateDataRec = atcsVar->GetStateDataRecord(i); + plAnimTimeConvert* objAtc = objAGMaster->GetATCAnimInstance(i)->GetTimeConvert(); // dst + ISetCurrentATC(atcStateDataRec, objAtc); + objAtc->EnableCallbacks(false); + } + objAGMaster->IRegForEval(objAGMaster->HasRunningAnims()); + + // Force one eval, then re-enable all the callbacks + double time = (hsTimer::GetSysSeconds() - hsTimer::GetDelSysSeconds()); + + if (objAGMaster->fIsGrouped && objAGMaster->fMsgForwarder) + { + hsScalar animTimeFromWorldTime = (objAGMaster->GetNumATCAnimations() > 0) ? objAGMaster->GetATCAnimInstance(0)->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) : 0.0f; + + plAGCmdMsg *msg = TRACKED_NEW plAGCmdMsg(); + msg->SetCmd(plAGCmdMsg::kSetAnimTime); + msg->fAnimTime = animTimeFromWorldTime; + msg->AddReceiver(objAGMaster->fMsgForwarder->GetKey()); + plgDispatch::MsgSend(msg); + } + else + { + objAGMaster->AdvanceAnimsToTime(time); + } + + for (i = 0; i < objAGMaster->GetNumATCAnimations(); i++) + objAGMaster->GetATCAnimInstance(i)->GetTimeConvert()->EnableCallbacks(true); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterSDLModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterSDLModifier.h new file mode 100644 index 00000000..78349a71 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGMasterSDLModifier.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAGMasterSDLModifier_inc +#define plAGMasterSDLModifier_inc + +#include "../plModifier/plAnimTimeConvertSDLModifier.h" + +// +// This modifier is responsible for sending and recving +// an object's animation state +// +class plAGMasterMod; +class plSceneObject; +class plStateDataRecord; + +class plAGMasterSDLModifier : public plAnimTimeConvertSDLModifier +{ +protected: + // var labels + struct AGMasterVarNames + { + static char kStrAtcs[]; // animTimeConverts + static char kStrBlends[]; + }; + + plAGMasterMod* IGetObjectsAGMasterMod(plSceneObject* obj); + + void IPutBlends(plStateDataRecord* state, plAGMasterMod* objAGMaster); + void ISetCurrentBlends(const plStateDataRecord* state, plAGMasterMod* objAGMaster); + + void IPutCurrentStateIn(plStateDataRecord* dstState); + void ISetCurrentStateFrom(const plStateDataRecord* srcState); + + UInt32 IApplyModFlags(UInt32 sendFlags); + +public: + CLASSNAME_REGISTER( plAGMasterSDLModifier); + GETINTERFACE_ANY( plAGMasterSDLModifier, plAnimTimeConvertSDLModifier); + + plAGMasterSDLModifier() {} + ~plAGMasterSDLModifier() {} + + const char* GetSDLName() const { return kSDLAGMaster; } +}; + +#endif // plAGMasterSDLModifier_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGModifier.cpp new file mode 100644 index 00000000..e0bf01e8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGModifier.cpp @@ -0,0 +1,306 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// local +#include "plAGModifier.h" +#include "plMatrixChannel.h" + + +// global +#include "hsResMgr.h" +#include "hsTimer.h" + +// other +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plSimulationInterface.h" + +///////////////// +// +// PLAGMODIFIER +// +///////////////// +// Applies animation graph output to a single scene object. + +// CTOR +plAGModifier::plAGModifier() +: plSingleModifier() +{ + fChannelName = nil; + fAutoApply = true; + fEnabled = true; +} + +// CTOR(name) +plAGModifier::plAGModifier(const char *name, hsBool autoApply) +: plSingleModifier(), fAutoApply(autoApply) +{ + fChannelName = hsStrcpy(name); + fEnabled = true; +} + +// DTOR +plAGModifier::~plAGModifier() +{ + if(fChannelName) + { + delete[] fChannelName; + fChannelName = nil; + } + + int i; + for (i = 0; i < fApps.size(); i++) + { + delete fApps[i]; + } +} + +// GETCHANNELNAME +const char * plAGModifier::GetChannelName() const +{ + return fChannelName; +} + +// ENABLE +void plAGModifier::Enable(hsBool val) +{ + fEnabled = val; +} + +// SETCHANNELNAME +void plAGModifier::SetChannelName(char * name) +{ + fChannelName = hsStrcpy(name); +} + +// IAPPLYCHANNELS (time) +// One AGModifier, although applied to a single scene object, can have +// multiple channels on it affecting, say, position, color, aroma, etc. +// +// There are cases where we want to call this and won't know the delta, +// we don't seem to ever need it for this function, so I'm taking it out. +// If you run into a case where you think it's necessary, see me. -Bob +void plAGModifier::Apply(double time) const +{ + if (!fEnabled) + return; + + for (int i = 0; i < fApps.size(); i++) + { + plAGApplicator *app = fApps[i]; + + app->Apply(this, time); + } +} + +// IEVAL +// Apply our channels to our scene object +hsBool plAGModifier::IEval(double time, hsScalar delta, UInt32 dirty) +{ + if(fAutoApply) { + // Apply(time, delta); + } + return true; +} + +// GETAPPLICATOR +plAGApplicator * plAGModifier::GetApplicator(plAGPinType pinType) const +{ + int numApps = fApps.size(); + + for (int i = 0; i < numApps; i++) + { + plAGApplicator *app = fApps[i]; + plAGPinType otherType = app->GetPinType(); + if(otherType == pinType) + return app; + } + return nil; +} + +// SETAPPLICATOR +void plAGModifier::SetApplicator(plAGApplicator *newApp) +{ + int numApps = fApps.size(); + plAGPinType newPinType = newApp->GetPinType(); + + // *** NOTE: this code is completely untested. Since I happened to be here + // I sketched out how it *should* work and implemented the base protocol. + // In reality, most of these code paths are not accessed now... + // -- mm + for(int i = 0; i < numApps; i++) + { + plAGApplicator *existingApp = fApps[i]; + plAGPinType extPinType = existingApp->GetPinType(); + + if(extPinType == newPinType) + { + hsStatusMessage("Two applicators accessing same pin type...congratulations for being the first to test this."); + // these applicators both try to set the same thing; try to merge them + + plAGChannel *newChannel = newApp->GetChannel(); + + hsAssert(newChannel = nil, "Trying to merge in new applicator which already has channel. Incomplete."); + + // *** right now I just want to support the case of putting in a new applicator - not merging animations + + plAGChannel *extChannel = existingApp->GetChannel(); + newApp->SetChannel(extChannel); + existingApp->SetChannel(nil); + fApps[i] = newApp; + + delete existingApp; + return; + + // NOTE: we should make these arbitrate, but I'm not going to right now because + // there's not currently an (easy) way to merge two applicators without allowing a blend. +// if(existingApp->CanCombine(newApp)) +// { +// // the existing applicator promises to provide the functionality we need...merge into it. +// existingApp->MergeChannel(newApp); +// } else { +// // couldn't merge into the existing channel; can we merge it into us instead? +// if(newApp->CanCombine(extApp)) +// { +// // okay, WE can provide the functionality of the existing applicator. +// fApps[i] = newApp; // take over its spot in the applicators +// newApp->MergeChannel(existingApp); // and merge it into us +// } +// } + } + } + // didn't find any conflicts; just add our app on the end + fApps.push_back(newApp); +} + +// MERGECHANNEL +// Intended as a replacement for attach/blend channel. You want to add a channel to this node, +// we do that for you. Don't ask us how, you shouldn't have to know. +plAGChannel * plAGModifier::MergeChannel(plAGApplicator *app, + plAGChannel *channel, + plScalarChannel *blend, + plAGAnimInstance *anim, + int priority) +{ + int numApps = fApps.size(); + plAGChannel * result = nil; + + for (int i = 0; i < numApps; i++) + { + plAGApplicator *existingApp = fApps[i]; + result = existingApp->MergeChannel(app, channel, blend, priority); + if (result) + return result; + } + + if (!result) + { + // didn't blend or combine with an existing channel; add a new channel + plAGApplicator *newApp = app->CloneWithChannel(channel); + fApps.push_back(newApp); + } + return result; +} + +// DETACHCHANNEL +hsBool plAGModifier::DetachChannel(plAGChannel * channel) +{ + plAppTable::iterator i = fApps.begin(); + hsBool done = false; + + for( ; i != fApps.end(); i++) + { + plAGApplicator *app = *i; + plAGChannel *existingChannel = app->GetChannel(); + if(existingChannel) + { + plAGChannel *replacementChannel = existingChannel->Detach(channel); + + if (existingChannel != replacementChannel) + { + app->SetChannel(replacementChannel); + if( ! replacementChannel && app->AutoDelete()) + { + plAppTable::iterator old = i; + i--; + fApps.erase(old); + delete app; + } + done = true; + break; + } + } + } + return done; +} + +// READ +void plAGModifier::Read(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Read(stream, mgr); + + // read in the name of the modifier + fChannelName = stream->ReadSafeString(); +} + +// WRITE +void plAGModifier::Write(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Write(stream, mgr); + + // write out the name of the modifier + stream->WriteSafeString(fChannelName); +} + +///////////////////////////////////////// +///////////////////////////////////////// +// +// MOVE +// +///////////////////////////////////////// +///////////////////////////////////////// + +const plModifier * FindModifierByClass(const plSceneObject *obj, int classID) +{ + if(obj) + { + int modCount = obj->GetNumModifiers(); + + for (int i = 0; i < modCount; i++) + { + const plModifier *mod = obj->GetModifier(i); + + if(mod) // modifier might not be loaded yet + { + if(mod->ClassIndex() == classID) + { + return mod; + } + } + } + } + return nil; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGModifier.h new file mode 100644 index 00000000..74733ead --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAGModifier.h @@ -0,0 +1,154 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/** \file plAGMasterMod.h + + \ingroup Avatar + \ingroup AniGraph +*/ +#ifndef PLAGMODIFIER_H +#define PLAGMODIFIER_H + +#include "hsTypes.h" // need for plSingleModifier +#include "../pnModifier/plSingleModifier.h" // inherited + +// local +#include "../plAvatar/plScalarChannel.h" + +// stl +#include "hsStlUtils.h" +#include "hsStlSortUtils.h" + +class plSceneObject; + +class plAGAnimInstance; +class plAGAnim; +class plAvatarAnim; +class plAnimCmdMsg; +class plOneShotCallbacks; + +/////////////// +// +// PLAGMODIFIER +// +/////////////// +/** \class plAGModifier + Animates a single scene object. + May apply multiple animations to that scene object, animating + the transform with one, colors with another, etc. + The basic model is that the ag modifier holds an array of + applicators, each of which is the root of a tree of animation + operations, such as a blend tree, etc. Each frame it fires + all its applicators, or (ideally) only those that need to be fired. + */ +class plAGModifier : public plSingleModifier +{ +public: + /** Default constructor, primarily for the class factory. */ + plAGModifier(); + + /** Construct given a name. This name will be used to match up + incoming channels with this modifier. You may also supply an + autoApply parameter, which indicates whether this modifier + should apply itself every frame, or only when explicitly asked to. */ + plAGModifier(const char *name, hsBool autoApply = true); + + /** It's a destructor. Destroys the name passed into the constructor, + and a bunch of other stuff you don't need to know anything about. */ + virtual ~plAGModifier(); + + /** Get the name of the channel controlled by this modifier. */ + const char * GetChannelName() const; + /** Change the channel name of the modifier. Will delete the previous + name. Will NOT remove any channels that are already attached, so + you could wind up with a modifier named "Fred" and a bunch of + channels attached to it that were intended for "Lamont." */ + void SetChannelName(char * name); + + /** Attach a TRACKED_NEW applicator to our modifier. Will arbitrate with existing + modifiers if necessary, based on pin type. May destruct existing applicators. */ + plAGChannel *MergeChannel(plAGApplicator *app, plAGChannel *chan, plScalarChannel *blend, + plAGAnimInstance *anim, int priority); + /** Remove the given channel. Will also remove the channel's applicator. */ + hsBool DetachChannel(plAGChannel * channel); + + /** Set the applicator for a modifier. + \deprecated */ + void SetApplicator(plAGApplicator *app); + /** Get the applicator for a given pintype. Note that an ag modifier can + only have one applicator of a given pin type attached at a time. + The exception is for the unknown pin type, which never conflicts + with any other pin type, including itself. */ + plAGApplicator *GetApplicator(plAGPinType pin) const; + + /** Apply the animation for our scene object. */ + void Apply(double time) const; + + /** Get the channel tied to our ith applicator */ + plAGChannel * GetChannel(int i) { return fApps[i]->GetChannel(); } + + void Enable(hsBool val); + + // PERSISTENCE + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plAGModifier ); + GETINTERFACE_ANY( plAGModifier, plSingleModifier ); + +protected: + typedef std::vector plAppTable; + plAppTable fApps; // the applicators (with respective channels) that we're applying to our scene object + + char *fChannelName; // name used for matching animation channels to this modifier + hsBool fAutoApply; // evaluate animation automatically during IEval call + hsBool fEnabled; // if not enabled, we don't eval any of our anims + + // APPLYING THE ANIMATION + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + + virtual hsBool IHandleCmd(plAnimCmdMsg* modMsg) { return false; } // only plAGMasterMod should handle these + virtual void IApplyDynamic() {}; // dummy function required by base class + + // INTERNAL ACCESSORS FOR SCENE OBJECT INTERFACES + plAudioInterface * LeakAI() const { return IGetTargetAudioInterface(0); }; + plCoordinateInterface * LeakCI() const { return IGetTargetCoordinateInterface(0); }; + plDrawInterface * LeakDI() const { return IGetTargetDrawInterface(0); }; + plSimulationInterface * LeakSI() const { return IGetTargetSimulationInterface(0); }; + plObjInterface * LeakGI(UInt32 classIdx) const { return IGetTargetGenericInterface(0, classIdx); } + + friend plAudioInterface * plAGApplicator::IGetAI(const plAGModifier * modifier) const; + friend plCoordinateInterface * plAGApplicator::IGetCI(const plAGModifier * modifier) const; + friend plDrawInterface * plAGApplicator::IGetDI(const plAGModifier * modifier) const; + friend plSimulationInterface * plAGApplicator::IGetSI(const plAGModifier * modifier) const; + friend plObjInterface * plAGApplicator::IGetGI(const plAGModifier * modifier, UInt16 classIdx) const; + +}; +const plModifier * FindModifierByClass(const plSceneObject *obj, int classID); + + +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp new file mode 100644 index 00000000..8c8c168b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.cpp @@ -0,0 +1,780 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAvCallbackAction.h" // must be first: references havok new + +// singular +#include "plAnimStage.h" + +// local +#include "plAvatarMgr.h" +#include "plAGAnim.h" +#include "plArmatureMod.h" +#include "plAGAnimInstance.h" +#include "plMatrixChannel.h" +#include "plAvBrainGeneric.h" +#include "plMultiStageBehMod.h" + +// global +#include "hsUtils.h" +#include "hsStlUtils.h" +#include "hsResMgr.h" +#include "hsTimer.h" + +#include + +// other +#include "../pnSceneObject/plSceneObject.h" +#include "../plMessage/plSimStateMsg.h" +#include "../plStatusLog/plStatusLog.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../plPipeline/plDebugText.h" + +#ifdef DEBUG_MULTISTAGE +#include "plAvatarMgr.h" +#include "../plStatusLog/plStatusLog.h" +#endif + + +class plAGAnim; + +// PLANIMSTAGE default ctor +plAnimStage::plAnimStage() +: fAnimName(nil), + fNotify(0), + fArmature(nil), + fBrain(nil), + fForwardType(kForwardNone), + fBackType(kBackNone), + fAdvanceType(kAdvanceNone), + fRegressType(kRegressNone), + fLoops(0), + fAnimInstance(nil), + fLocalTime(0.0f), + fLength(0.0f), + fCurLoop(0), + fAttached(false), + fDoAdvanceTo(false), + fAdvanceTo(0), + fDoRegressTo(false), + fRegressTo(0), + fMod(nil), + fSentNotifies(0), + fReverseOnIdle(false), + fDone(false) +{ +} + +plAnimStage::plAnimStage(const char *animName, UInt8 notify) +: fNotify(notify), + fArmature(nil), + fBrain(nil), + fForwardType(kForwardAuto), // different from default + fBackType(kBackNone), + fAdvanceType(kAdvanceAuto), // different from default + fRegressType(kRegressNone), + fLoops(0), + fAnimInstance(nil), + fLocalTime(0.0f), + fLength(0.0f), + fCurLoop(0), + fAttached(false), + fDoAdvanceTo(false), + fAdvanceTo(0), + fDoRegressTo(false), + fRegressTo(0), + fMod(nil), + fSentNotifies(0), + fReverseOnIdle(false), + fDone(false) +{ + fAnimName = hsStrcpy(animName); +} + + +// PLANIMSTAGE canonical ctor +plAnimStage::plAnimStage(const char *animName, + UInt8 notify, + ForwardType forward, + BackType back, + AdvanceType advance, + RegressType regress, + int loops) +: fArmature(nil), + fBrain(nil), + fNotify(notify), + fForwardType(forward), + fBackType(back), + fAdvanceType(advance), + fRegressType(regress), + fLoops(loops), + fAnimInstance(nil), + fLocalTime(0.0f), + fLength(0.0f), + fCurLoop(0), + fAttached(false), + fDoAdvanceTo(false), + fAdvanceTo(0), + fDoRegressTo(false), + fRegressTo(0), + fMod(nil), + fSentNotifies(0), + fReverseOnIdle(false), + fDone(false) +{ + fAnimName = hsStrcpy(animName); +} + +plAnimStage::plAnimStage(const char *animName, + UInt8 notify, + ForwardType forward, + BackType back, + AdvanceType advance, + RegressType regress, + int loops, + bool doAdvanceTo, + int advanceTo, + bool doRegressTo, + int regressTo) +: fArmature(nil), + fBrain(nil), + fNotify(notify), + fForwardType(forward), + fBackType(back), + fAdvanceType(advance), + fRegressType(regress), + fLoops(loops), + fAnimInstance(nil), + fLocalTime(0.0f), + fLength(0.0f), + fCurLoop(0), + fAttached(false), + fDoAdvanceTo(doAdvanceTo), + fAdvanceTo(advanceTo), + fDoRegressTo(doRegressTo), + fRegressTo(regressTo), + fMod(nil), + fSentNotifies(0), + fReverseOnIdle(false), + fDone(false) +{ + fAnimName = hsStrcpy(animName); +} + +// PLANIMSTAGE dtor +plAnimStage::~plAnimStage() +{ + if(fAnimName) + delete[] fAnimName; + + hsAssert(fAnimInstance == nil, "plAnimStage still has anim instance during destruction. (that's bad.)"); + // we could delete the animation instance here, but it should have been deleted already... + // *** check back in a while.... +} + +// operator= ---------------------------------------------------- +// ---------- +const plAnimStage& plAnimStage::operator=(const plAnimStage& src) +{ + fAnimName = hsStrcpy(src.fAnimName); + fNotify = src.fNotify; + fForwardType = src.fForwardType; + fBackType = src.fBackType; + fAdvanceType = src.fAdvanceType; + fRegressType = src.fRegressType; + fLoops = src.fLoops; + fDoAdvanceTo = src.fDoAdvanceTo; + fAdvanceTo = src.fAdvanceTo; + fDoRegressTo = src.fDoRegressTo; + fRegressTo = src.fRegressTo; + fMod = src.fMod; + + fAnimInstance = nil; + fLocalTime = 0.0f; + fLength = 0.0f; + fCurLoop = 0; + fAttached = false; + + fReverseOnIdle = src.fReverseOnIdle; + + return *this; +} + +// attach -------------------------------------------------------------------------------------------------------- +// ------- +plAGAnimInstance * plAnimStage::Attach(plArmatureMod *armature, plArmatureBrain *brain, float initialBlend, double time) +{ + // NOTE: you need to be able to detach an animstage and then re-attach it and have + // it wind up in exactly the same state it was in before - for loading and saving. + fBrain = brain; + fSentNotifies = 0; + fArmature = armature; + + if(fAnimInstance) + { + fAnimInstance->SetBlend(initialBlend); + } else { + plAGAnim *anim = armature->FindCustomAnim(fAnimName); + + if(anim) + { + fLength = anim->GetEnd(); + fAnimInstance = armature->AttachAnimationBlended(anim, initialBlend); + fAnimInstance->SetCurrentTime(fLocalTime); +#ifdef DEBUG_MULTISTAGE + char sbuf[256]; + sprintf(sbuf,"AnimStage::Attach - attaching stage %s",fAnimName); + plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf); +#endif + } else { + char buf[256]; + sprintf(buf, "Can't find animation <%s> for animation stage. Anything could happen.", fAnimName); + hsAssert(false, buf); +#ifdef DEBUG_MULTISTAGE + plAvatarMgr::GetInstance()->GetLog()->AddLine(buf); +#endif + } + } + + if(fAnimInstance) + { + fAnimInstance->Stop(); // we'll be setting the time directly. + fAnimatedHandle = (fAnimInstance->GetAnimation()->GetChannel("Handle") != nil); + fAttached = true; + // this is too early to send the enter notify. we're attached, but we may not + // have faded in yet. + // XXX ISendNotify(kNotifyEnter, proEventData::kEnterStage, armature, brain); + } + + return fAnimInstance; +} + +// SENDNOTIFY +hsBool plAnimStage::ISendNotify(UInt32 notifyMask, UInt32 notifyType, plArmatureMod *armature, plArmatureBrain *brain) +{ + // make sure the user has requested this type of notify + if(fNotify & notifyMask) + { + plKey avKey = armature->GetTarget(0)->GetKey(); + if (fMod) + avKey = fMod->GetKey(); + plNotifyMsg *msg = TRACKED_NEW plNotifyMsg(); + msg->SetSender(avKey); + + if (fMod) + { + msg->SetBCastFlag(plMessage::kNetPropagate, fMod->NetProp()); + msg->SetBCastFlag(plMessage::kNetForce, fMod->NetForce()); + } + else + { + msg->SetBCastFlag(plMessage::kNetPropagate, false); + msg->SetBCastFlag(plMessage::kNetForce, false); + } + + plAvBrainGeneric *genBrain = plAvBrainGeneric::ConvertNoRef(brain); + int stageNum = genBrain ? genBrain->GetStageNum(this) : -1; + msg->AddMultiStageEvent(stageNum, notifyType, armature->GetTarget(0)->GetKey()); + + if (! genBrain->RelayNotifyMsg(msg) ) + { + msg->UnRef(); // couldn't send; destroy... + } + + return true; + } + + return false; +} + +// DETACH +hsBool plAnimStage::Detach(plArmatureMod *armature) +{ + + hsBool result = false; + +#ifdef DEBUG_MULTISTAGE + char sbuf[256]; + sprintf(sbuf,"AnimStage::Detach - detaching stage %s",fAnimName); + plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf); +#endif +// hsStatusMessageF("Detaching plAnimStage <%s>", fAnimName); + if(fArmature) { + fArmature = nil; + + if(fAnimInstance) { + armature->DetachAnimation(fAnimInstance); // detach instantly + fAnimInstance = nil; + result = true; + } +#ifdef DEBUG_MULTISTAGE + } else { + char sbuf[256]; + sprintf(sbuf,"AnimStage::Detach: stage already detached"); + plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf); +#endif +// hsStatusMessageF("Detach: stage already detached."); + } + + fBrain = nil; + fAttached = false; + return result; +} + +void plAnimStage::Reset(double time, plArmatureMod *avMod, bool atStart) +{ + if(atStart) + SetLocalTime(0.0f, true); + else + SetLocalTime(fLength, true); + avMod->GetRootAnimator()->Reset(time); +} + +void plAnimStage::ResetAtTime(double globalTime, float localTime, plArmatureMod *avMod) +{ + SetLocalTime(localTime, true); + avMod->GetRootAnimator()->Reset(globalTime); +} + + +// MoveRelative ------------------------------ +// ------------- +// A true result means that the stage is done. +bool plAnimStage::MoveRelative(double time, float delta, float &overage, plArmatureMod *avMod) +{ + bool result; // true means the stage is done + + if(fLocalTime == 0.0f && delta >= 0.0f && !hsCheckBits(fSentNotifies, kNotifyEnter)) + { + // we send the "enter" notify if we're at the start and either moving forward + // or standing still. + ISendNotify(kNotifyEnter, proEventData::kEnterStage, avMod, fBrain); + hsSetBits(fSentNotifies, kNotifyEnter); + } + + // aborting... + if( fAdvanceType == kAdvanceOnMove && (avMod->HasMovementFlag() || avMod->ExitModeKeyDown())) + { // special case: advance when any key is pressed, regardless of position in stage. + ISendNotify(kNotifyAdvance, proEventData::kAdvanceNextStage, avMod, fBrain); + result = true; + } else { + if(delta == 0.0f) + { + return false; + } + else + if(delta < 0.0f) + result = IMoveBackward(time, delta, overage, avMod); + else + result = IMoveForward(time, delta, overage, avMod); + } + + return result; +} + +// IMoveBackward ------------------------------------------------------------------------------ +// -------------- +bool plAnimStage::IMoveBackward(double time, float delta, float &overrun, plArmatureMod *avMod) +{ + if (fLocalTime <= 0 && fRegressType == kRegressNone) + { + // If we're at the beginning, but not allowed to regress, we don't want to keep processing + // (and firing triggers). + return false; + } + + float target = fLocalTime + delta; + bool infiniteLoop = fLoops == -1; + bool loopsRemain = fCurLoop > 0 || infiniteLoop; + + // This must be here before we set the local time. + if (fAnimInstance->GetTimeConvert()) + fAnimInstance->GetTimeConvert()->Backwards(); + + if(target < 0) + { + SetLocalTime(0); // animation to beginning + avMod->GetRootAGMod()->Apply(time); // move avatar to beginning + if(loopsRemain) + { + // If a callback is on the last frame, it'll get triggered twice. Once for setting + // the anim at the end, and once when we play again. So we don't fire callbacks here. + SetLocalTime(fLength, true); // animation wraps to end + + avMod->GetRootAnimator()->Reset(time); // reset the root animator at the end + fCurLoop = infiniteLoop ? 0 : fCurLoop - 1; + target = fLength - (fmodf(-target, fLength)); // modularize negative number to discard + // extra loops (only one allowed) + } else { + // overrun = target + fLength; + // now we want to make sure that overrun goes negative when appropriate, rather than modularizing + overrun = target; + fDone = true; + return ITryRegress(avMod); + } + } + + overrun = 0.0f; + fLocalTime = target; + + fAnimInstance->SetCurrentTime(fLocalTime); + return false; // not done +} + + +// IMoveForward ------------------------------------------------------------------------------ +// ------------- +// It's currently not supported to advance the animation by more than its length in one frame. +// It wouldn't be too hard to add, but it clutters things up and it hasn't been shown to +// be necessary. +bool plAnimStage::IMoveForward(double time, float delta, float &overrun, plArmatureMod *avMod) +{ + if (fLocalTime >= fLength && fAdvanceType == kAdvanceNone) + { + // If we're at the end, but not allowed to advance, we don't want to keep processing + // (and firing triggers). + return false; + } + + // first get the target time in local time, ignoring overruns + float target = fLocalTime + delta; + + if (fAnimInstance->GetTimeConvert()) + fAnimInstance->GetTimeConvert()->Forewards(); + + + if (target > fLength) + { + // we're going to the end for sure, so do that first + SetLocalTime(fLength); + // we're going to swap in a new animation before the next eval, so force + // an apply of this one to make sure the avatar gets the necessary movement. + // we only apply on the root node to get the movement -- no need to animate + // the fingers as they'll be overwritten when the next animation is swapped in. + avMod->GetRootAGMod()->Apply(time); + + // are there *any* loops to be had? + bool loopsRemain = fCurLoop < fLoops || fLoops == -1; + if(loopsRemain) + { + SetLocalTime(0.0f, true); // animation back to beginning + avMod->GetRootAnimator()->Reset(time); // reset the root animator's frame cache + fCurLoop++; + target = fmodf(target, fLength); // discard extra loops (only one at a time allowed) + // target -= fLength; + } else { + overrun = target - fLength; + fDone = true; + return ITryAdvance(avMod); + } + } + + overrun = 0.0f; + fLocalTime = target; + + avMod->GetRootAGMod()->Apply(time); + fAnimInstance->SetCurrentTime(fLocalTime); + return false; // not done +} + +bool plAnimStage::ITryAdvance(plArmatureMod *avMod) +{ + bool stageDone = false; + + + // hsStatusMessageF("Sending advance message for stage <%s>\n", fAnimName); + if(fAdvanceType == kAdvanceAuto || fAdvanceType == kAdvanceOnMove) { + stageDone = true; + } + + if(!hsCheckBits(fSentNotifies, kNotifyAdvance)) + { + // we send the advance message at the point where we *would* advance, whether + // or not we actually do. this is misleading but better suited to actual current usage. + // we may want to rename this to "ReachedStageEnd" + ISendNotify(kNotifyAdvance, proEventData::kAdvanceNextStage, avMod, fBrain); + hsSetBits(fSentNotifies, kNotifyAdvance); + } + + return stageDone; +} + + +bool plAnimStage::ITryRegress(plArmatureMod *avMod) +{ + bool stageDone = false; + + // we send the advance message at the point where we *would* advance, whether + // or not we actually do. this is misleading but better suited to actual current usage. + // we may want to rename this to "ReachedStageEnd" + ISendNotify(kNotifyRegress, proEventData::kRegressPrevStage, avMod, fBrain); + + // hsStatusMessageF("Sending regress message for stage <%s>\n", fAnimName); + if(fRegressType == kRegressAuto) { + stageDone = true; + } + return stageDone; +} + + +// GETANIMNAME +const char * plAnimStage::GetAnimName() +{ + return fAnimName; +} + +// GETFORWARDTYPE +plAnimStage::ForwardType plAnimStage::GetForwardType() +{ + return fForwardType; +} + +// SETFORWARDTYPE +void plAnimStage::SetForwardType(ForwardType t) +{ + fForwardType = t; +} + +// GETBACKTYPE +plAnimStage::BackType plAnimStage::GetBackType() +{ + return fBackType; +} + +// SETBACKTYPE +void plAnimStage::SetBackType(BackType t) +{ + fBackType = t; +} + +// GETADVANCETYPE +plAnimStage::AdvanceType plAnimStage::GetAdvanceType() +{ + return fAdvanceType; +} + +// SETADVANCETYPE +void plAnimStage::SetAdvanceType(AdvanceType t) +{ + fAdvanceType = t; +} + +// GETREGRESSTYPE +plAnimStage::RegressType plAnimStage::GetRegressType() +{ + return fRegressType; +} + +// SETREGRESSTYPE +void plAnimStage::SetRegresstype(RegressType t) +{ + fRegressType = t; +} + +// GETNOTIFYFLAGS +UInt32 plAnimStage::GetNotifyFlags() +{ + return fNotify; +} + +// SETNOTIFYFLAGS +void plAnimStage::SetNotifyFlags(UInt32 newFlags) +{ + fNotify = (UInt8)newFlags; +} + +// GETNUMLOOPS +int plAnimStage::GetNumLoops() +{ + return fLoops; +} + +// SETNUMLOOPS +void plAnimStage::SetNumLoops(int loops) +{ + // I'm very suspicious of this if statement... + // 1. It's preceded by an assert whose condition is the opposite the error message + // (which I've now commented out) + // 2. It only matters if the avatar is currently in the stage. + // 3. It doesn't seem intuitive that if I'm currently on loop 3 and you + // change the number of loops to 6, that I should jump to the end. + // BUT... + // It's been like this for ages, so I'm not touching it until a break shows a problem. + // + //hsAssert(loops < fCurLoop, "Setting loopcount below current loop"); + if(loops >= fCurLoop) { + fCurLoop = loops; + } + fLoops = loops; +} + +// GETLOOPVALUE +int plAnimStage::GetLoopValue() +{ + return fCurLoop; +} + +// SETLOOPVALUE +void plAnimStage::SetLoopValue(int value) +{ + fCurLoop = value; +} + +// GETLOCALTIME +float plAnimStage::GetLocalTime() +{ + return fLocalTime; +} + +// SETLOCALTIME +void plAnimStage::SetLocalTime(float time, hsBool noCallbacks /* = false */) +{ + fLocalTime = time; + if(fAnimInstance) + fAnimInstance->SetCurrentTime(time, noCallbacks); +} + +// GETLENGTH +float plAnimStage::GetLength() +{ + return fLength; +} + +// GETISATTACHED +bool plAnimStage::GetIsAttached() +{ + return fAttached; +} + +// SETISATTACHED +void plAnimStage::SetIsAttached(bool status) +{ + fAttached = status; +} + +// GETNEXTSTAGE +int plAnimStage::GetNextStage(int curStage) +{ + if(fDoAdvanceTo) + { + return fAdvanceTo; + } else { + return curStage + 1; + } +} + +// GETPREVSTAGE +int plAnimStage::GetPrevStage(int curStage) +{ + if(fDoRegressTo) + { + return fRegressTo; + } else { + return curStage - 1; + } +} + +// DUMPDEBUG +void plAnimStage::DumpDebug(bool active, int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + std::string str; + + str += fAnimName; + str += " "; + + if(fLoops) { + sprintf(strBuf, "loop(%d/%d)", fCurLoop, fLoops); + str += strBuf; + } + + sprintf(strBuf, "time: (%f/%f)", fLocalTime, fLength); + str += strBuf; + + if(active) + debugTxt.DrawString(x, y, str.c_str(), 0, 255, 0); + else if(fAnimInstance) + debugTxt.DrawString(x, y, str.c_str()); + else + debugTxt.DrawString(x, y, str.c_str(), 255, 255, 0); + + y += lineHeight; +} + +// READ +void plAnimStage::Read(hsStream *stream, hsResMgr *mgr) +{ + delete [] fAnimName; + fAnimName = stream->ReadSafeString(); + fNotify = stream->ReadByte(); + fForwardType = (ForwardType)stream->ReadSwap32(); + fBackType = (BackType)stream->ReadSwap32(); + fAdvanceType = (AdvanceType)stream->ReadSwap32(); + fRegressType = (RegressType)stream->ReadSwap32(); + fLoops = stream->ReadSwap32(); + + fDoAdvanceTo = stream->Readbool(); + fAdvanceTo = stream->ReadSwap32(); + fDoRegressTo = stream->Readbool(); + fRegressTo = stream->ReadSwap32(); +} + +void plAnimStage::Write(hsStream *stream, hsResMgr *mgr) +{ + stream->WriteSafeString(fAnimName); + stream->WriteByte(fNotify); + stream->WriteSwap32(fForwardType); + stream->WriteSwap32(fBackType); + stream->WriteSwap32(fAdvanceType); + stream->WriteSwap32(fRegressType); + stream->WriteSwap32(fLoops); + + stream->Writebool(fDoAdvanceTo); + stream->WriteSwap32(fAdvanceTo); + stream->Writebool(fDoRegressTo); + stream->WriteSwap32(fRegressTo); +} + + +// SAVEAUX +void plAnimStage::SaveAux(hsStream *stream, hsResMgr *mgr) +{ + stream->WriteSwapScalar(fLocalTime); + stream->WriteSwapScalar(fLength); + stream->WriteSwap32(fCurLoop); + stream->Writebool(fAttached); + // no ephemeral stage at the moment +} + +// LOADAUX +void plAnimStage::LoadAux(hsStream *stream, hsResMgr *mgr, double time) +{ + fLocalTime = stream->ReadSwapScalar(); + fLength = stream->ReadSwapScalar(); + fCurLoop = stream->ReadSwap32(); + // This should actually be Readbool (lowercase), but I won't fix it since that + // would require a version change + fAttached = (stream->Readbool() != 0); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.h new file mode 100644 index 00000000..16817f7c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAnimStage.h @@ -0,0 +1,273 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/** \file plAnimStage.h + An intelligent avatar animation for use in a generic brain. + + \ingroup Avatar +*/ +#ifndef PL_ANIM_STAGE_INC +#define PL_ANIM_STAGE_INC + +#include "hsMatrix44.h" +#include "hsStlUtils.h" +#include "../pnFactory/plCreatable.h" + +class plMessage; +class plAGAnimInstance; +class plArmatureMod; +class plArmatureMod; +class hsStream; +class hsResMgr; +class plArmatureBrain; +class plDebugText; +class plMultistageBehMod; + +// PLANIMSTAGE +// In a multi-stage behavior, each stage is specified in one of these classes +/** \class plAnimStage + An animation stage is an intelligent wrapper for an avatar animation. + It provides specific control over: + - how the progress of the animation is controlled + - when the animation optionally sends messages about its progress + - whether the animation naturally progresses to the next animation after finishing + - how many times the animation loops + - whether the animation works in world space or a local space + */ +class plAnimStage : public plCreatable +{ +public: + // -- forward declarations of types -- + // Points to send notifications at + /** \enum NotifyType + Controls when notification messages are sent by the stage. + These can be used instead of or in combination with + normal animation callback messages. + Where the messages are sent is determined by the controlling + brain. + These are mask bits; you can have any number of them on at a time. + */ + enum NotifyType + { + kNotifyEnter = 0x1, /// Send when we enter the stage from + kNotifyLoop = 0x2, /// Sent each time the stage loops + kNotifyAdvance = 0x4, /// Sent when we advance out of the stage + kNotifyRegress = 0x8 /// Sent when we regress out of the stage + }; + + /** \enum ForwardType + What makes the stage progress in a forward direction? + You can only choose one of these, although a stage will always respond + to forward movement messages. */ + enum ForwardType { + kForwardNone, /// move forward only in response to messages + kForwardKey, /// move forward when the user presses the move forward key + kForwardAuto, /// move forward automatically + kForwardMax, + } fForwardType; + + /** \enum BackType + What makes the stage progress in a backward direction? + You can only choose one of these, although a stage will always respond + to backward movement message. */ + enum BackType { + kBackNone, /// move backward only in response to messages + kBackKey, /// move backward when the user presses the move backward key + kBackAuto, /// move backward automatically (only when entered from end) + kBackMax, + } fBackType; + + /** When do we advance to the next stage? + This is typically asked when we're reached the end of the current stage, + but for some advance types, you can interrupt the current stage and + advance automatically on certain events. */ + enum AdvanceType { + kAdvanceNone, /// advance only when told to via a message + kAdvanceOnMove, /// advance whenever the user hits a movement key + kAdvanceAuto, /// advance automatically at the end of the current stage + kAdvanceOnAnyKey, /// advance when any key is pressed + kAdvanceMax, + } fAdvanceType; + + /** When do we regress to the previous stage? + This is typically asked when we've reached the beginning of the current stage + and we still trying to move backwards. + For some regress types, however, you can abort this stage automatically when + certain events occur. */ + enum RegressType { + kRegressNone, // regress only when told to via a message + kRegressOnMove, // regress whenever the user hits a movement key + kRegressAuto, // regress automatically when we reach the beginning of the current + /// stage and we're still moving backwards + kRegressOnAnyKey, // regress when any key is pressed + kRegressMax, + } fRegressType; + + /** Default constructor, used primarily by the class factory. */ + plAnimStage(); + /** Canonical constructor. + \param animName The name of the animation controlled by this stage. + \param notify Flags for when to send notify messages + \param forward How to control forward movement + \param backward How to control backward movement + \param regress When to regress to the previous stage + \param advance When to advance to the next stage + \param loops How many times to loop this stage after its initial playthrough. + i.e. If you only want it to play once, set loops to 0 + To make it play forever, pass in -1 for loops. + \param localize Align the origin of the animation to the avatar's position + when the stage start's playing. Only relevant if the + animation attempts to reposition the avatar by having a + channel attached to the avatar's handle. + */ + plAnimStage(const char *animName, + UInt8 notify, + ForwardType forward, + BackType backward, + AdvanceType advance, + RegressType regress, + int loops); + + /** Most complete constructor. Specifies every aspect of the stage. Differs from + the next-most-complete constructor by the presence of doAdvanceTo, advanceTo, + doRegressTo, and regressTo. These parameters allow you to specify what stages + will be played after this one, depending on which direction the stage is + moving. + */ + plAnimStage(const char *animName, + UInt8 notify, + ForwardType forward, + BackType back, + AdvanceType advance, + RegressType regress, + int loops, + bool doAdvanceTo, + int advanceTo, + bool doRegressTo, + int regressTo); + + /** Simple and common constructor: moves forward automatically, advances automatically, + no reverse, no regress. + \param animName The name of the animation controlled by this stage. + \param notify Flags for when to send notify messages + */ + plAnimStage(const char *animName, UInt8 notify); + virtual ~plAnimStage(); + const plAnimStage& operator=(const plAnimStage& src); + + plAGAnimInstance * Attach(plArmatureMod *armature, plArmatureBrain *brain, float initialBlend, double time); + int Detach(plArmatureMod *armature); + void Reset(double time, plArmatureMod *avMod, bool atStart); + void ResetAtTime(double time, float localTime, plArmatureMod *avMod); + bool MoveRelative(double worldTime, float delta, float &overage, plArmatureMod *avMod); + + /** The name of the animation associated with this stage. */ + const char * GetAnimName(); + + ForwardType GetForwardType(); + void SetForwardType(ForwardType t); + BackType GetBackType(); + void SetBackType(BackType t); + AdvanceType GetAdvanceType(); + void SetAdvanceType(AdvanceType t); + RegressType GetRegressType(); + void SetRegresstype(RegressType t); + UInt32 GetNotifyFlags(); + void SetNotifyFlags(UInt32 newFlags); + int GetNumLoops(); + void SetNumLoops(int loops); + int GetLoopValue(); + void SetLoopValue(int loop); + float GetLocalTime(); + void SetLocalTime(float time, hsBool noCallbacks = false); + float GetLength(); + + void SetMod(plMultistageBehMod* p) { fMod = p; } // for returning ID#'s to python scripts: + plMultistageBehMod* GetMod() { return fMod; } + bool GetIsAttached(); + void SetIsAttached(bool status); + + int GetNextStage(int curStage); + int GetPrevStage(int curStage); + plAGAnimInstance *GetAnimInstance() const { return fAnimInstance; }; + bool GetReverseOnIdle() { return fReverseOnIdle; } + void SetReverseOnIdle(bool onOff) { fReverseOnIdle = onOff; } + void DumpDebug(bool active, int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); + + // STANDARD PLASMA PROTOCOL + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + virtual void SaveAux(hsStream *stream, hsResMgr *mgr); + virtual void LoadAux(hsStream *stream, hsResMgr *mgr, double time); + + CLASSNAME_REGISTER( plAnimStage ); + GETINTERFACE_ANY( plAnimStage, plCreatable); + +protected: + bool IMoveBackward(double worldTime, float delta, float &underrun, plArmatureMod *avMod); + bool IMoveForward(double worldTime, float delta, float &overrun, plArmatureMod *avMod); + + bool INeedFadeIn(); + bool INeedFadeOut(); + + bool ITryAdvance(plArmatureMod *avMod); + bool ITryRegress(plArmatureMod *avMod); + + hsBool ISendNotify(UInt32 notifyMask, UInt32 notifyType, plArmatureMod *armature, plArmatureBrain *brain); + + char *fAnimName; // the name of our animation + UInt8 fNotify; // flags for which events will cause notification events + int fLoops; // how many times will this animation loop (after initial playthrough?) + + bool fDoAdvanceTo; // advance to a specific stage instead of n + 1 + UInt32 fAdvanceTo; // the stage to advance to, provided fDoAdvanceTo is true + bool fDoRegressTo; // regress to a specific stage instaed of n - 1 + UInt32 fRegressTo; // the stage to regress true, provided fDoRegressTo is true + + // --- these are derived & kept for bookkeeping + plAGAnimInstance *fAnimInstance; + plArmatureMod *fArmature; + plArmatureBrain *fBrain; // only valid while attached + plMultistageBehMod *fMod; // where we came from + float fLocalTime; // our time within the animation + float fLength; // the length of our animation? + int fCurLoop; // which loop are we currently in? + bool fAttached; // in the middle of reloading + bool fAnimatedHandle; // this animation moves the handle + UInt8 fSentNotifies; // which notifies have we sent? + bool fReverseOnIdle; // reverse our key interpretation if we stop. this is a special + // case for down ladders, for which the forward button means "keep going down" + // if you hold it down the whole time, but means "go up" if you press it + // again after releasing it. + bool fDone; // We've reached the end of the anim -- may be fading out, or paused. + +}; + +class plAnimStageVec : public std::vector +{ +}; + +#endif // PL_ANIM_STAGE_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.cpp new file mode 100644 index 00000000..5152604d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.cpp @@ -0,0 +1,155 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#if 0 +// havok first +#include +#include + +#include "plAntiGravAction.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../plHavok1/plHKPhysical.h" +#include "../plAvatar/plSwimRegion.h" +#include "hsTimer.h" + +// This is meant to be a specific physicsAction for the swim behavior +plAntiGravAction::plAntiGravAction(plHKPhysical *physical, plAGApplicator *rootApp) : + plAnimatedCallbackAction(physical, rootApp), + fOnGround(false), + fBuoyancy(1.f), + fSurfaceHeight(0.f), + fCurrentRegion(nil), + fHadContacts(false) +{ +} + +plSimDefs::ActionType plAntiGravAction::GetType() +{ + return plSimDefs::kAntiGravAction; +} + +void plAntiGravAction::apply(Havok::Subspace &space, Havok::hkTime time) +{ + double elapsed = time.asDouble() - getRefresh().asDouble(); + setRefresh(time); + + IAdjustBuoyancy(); + Havok::RigidBody *body = fPhysical->GetBody(); + float mass = body->getMass(); + Havok::Vector3 gravity = space.getGravity(); + Havok::Vector3 force = -gravity * (mass * fBuoyancy); + body->applyForce(force); + + hsVector3 vel; + fPhysical->GetLinearVelocitySim(vel); + fAnimPosVel.fZ = vel.fZ; + + hsVector3 linCurrent(0.f, 0.f, 0.f); + hsScalar angCurrent = 0.f; + if (fCurrentRegion != nil) + fCurrentRegion->GetCurrent(fPhysical, linCurrent, angCurrent, (hsScalar)elapsed); + + int numContacts = fPhysical->GetNumContacts(); + fHadContacts = (numContacts > 0); + + const Havok::Vector3 straightUp(0.0f, 0.0f, 1.0f); + fOnGround = false; + int i; + for (i = 0; i < numContacts; i++) + { + const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); + hsScalar dotUp = straightUp.dot(contact->m_normal); + if (dotUp > .5) + { + fOnGround = true; + break; + } + } + + fPhysical->SetLinearVelocitySim(fAnimPosVel + linCurrent); + fPhysical->SetAngularVelocitySim(hsVector3(0.f, 0.f, fAnimAngVel + fTurnStr + angCurrent)); +} + +void plAntiGravAction::SetSurface(plSwimRegionInterface *region, hsScalar surfaceHeight) +{ + fCurrentRegion = region; + if (region != nil) + fSurfaceHeight = surfaceHeight; +} + +void plAntiGravAction::IAdjustBuoyancy() +{ + // "surface depth" refers to the depth our handle object should be below + // the surface for the avatar to be "at the surface" + static const float surfaceDepth = 4.0f; + // 1.0 = neutral buoyancy + // 0 = no buoyancy (normal gravity) + // 2.0 = opposite of gravity, floating upwards + static const float buoyancyAtSurface = 1.0f; + + if (fCurrentRegion == nil) + { + fBuoyancy = 0.f; + return; + } + + hsMatrix44 l2w, w2l; + fPhysical->GetTransform(l2w, w2l); + float depth = fSurfaceHeight - surfaceDepth - l2w.GetTranslate().fZ; + if (depth < -1) + fBuoyancy = 0.f; // Same as being above ground. Plain old gravity. + else if (depth < 0) + fBuoyancy = 1 + depth; + else + { + hsVector3 vel; + fPhysical->GetLinearVelocitySim(vel); + if (vel.fZ > 0) + { + if (vel.fZ > fCurrentRegion->fMaxUpwardVel) + { + vel.fZ = fCurrentRegion->fMaxUpwardVel; + fPhysical->SetLinearVelocitySim(vel); + } + else + { + if (depth > 1) + fBuoyancy = fCurrentRegion->fUpBuoyancy; + else + fBuoyancy = (fCurrentRegion->fUpBuoyancy - 1) * depth + 1; + } + } + else + { + if (depth > 1) + fBuoyancy = fCurrentRegion->fDownBuoyancy; + else + fBuoyancy = (fCurrentRegion->fDownBuoyancy - 1) * depth + 1; + } + } +} + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.h new file mode 100644 index 00000000..cc427fcd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAntiGravAction.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#if 0//ndef PL_ANTI_GRAV_ACTION_H +#define PL_ANTI_GRAV_ACTION_H + +#include "plAvCallbackAction.h" + +class plSwimRegionInterface; + +class plAntiGravAction : public plAnimatedCallbackAction +{ +public: + plAntiGravAction(plHKPhysical *physical, plAGApplicator *rootApp); + + /** Return the type of the action as defined in the enum plSimDefs::ActionType. + Used to retrieve actions by entity/type indexing, and to + reuse actions that can be shared between entities. */ + virtual plSimDefs::ActionType GetType(); + + /** Called by Havok at substep frequency. */ + void apply(Havok::Subspace &s, Havok::hkTime time); + + void SetSurface(plSwimRegionInterface *region, hsScalar surfaceHeight); + hsScalar GetBuoyancy() { return fBuoyancy; } + hsBool IsOnGround() { return fOnGround; } + hsBool HadContacts() { return fHadContacts; } + +protected: + void IAdjustBuoyancy(); + + hsBool fOnGround; + hsBool fHadContacts; + hsScalar fBuoyancy; + hsScalar fSurfaceHeight; + plSwimRegionInterface *fCurrentRegion; +}; + +#endif + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp new file mode 100644 index 00000000..2116059b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.cpp @@ -0,0 +1,325 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAvCallbackAction.h" + +#include "../plStatusLog/plStatusLog.h" +#include "plArmatureEffects.h" +#include "../pfMessage/plArmatureEffectMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "plArmatureMod.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../plAudio/plSound.h" +#include "../plAudio/plAudioSystem.h" +#include "../pfAudio/plRandomSoundMod.h" +#include "hsResMgr.h" +#include "plgDispatch.h" + +const char *plArmatureEffectsMgr::SurfaceStrings[] = +{ + "Dirt", + "Puddle", + "Water", + "Tile", + "Metal", + "WoodBridge", + "RopeLadder", + "Grass", + "Brush", + "HardWood", + "Rug", + "Stone", + "Mud", + "MetalLadder", + "WoodLadder", + "DeepWater", + "Maintainer(Glass)", + "Maintainer(Stone)", + "Swimming", + "(none)" // Keep this one last +}; + + +void plArmatureEffectsMgr::Read(hsStream *s, hsResMgr *mgr) +{ + hsKeyedObject::Read(s, mgr); + + int numEffects = s->ReadSwap32(); + while (numEffects > 0) + { + plRefMsg *msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef); + numEffects--; + } + + plgDispatch::Dispatch()->RegisterForExactType(plAvatarStealthModeMsg::Index(), GetKey()); +} + +void plArmatureEffectsMgr::Write(hsStream *s, hsResMgr *mgr) +{ + hsKeyedObject::Write(s, mgr); + + s->WriteSwap32(fEffects.GetCount()); + int i; + for (i = 0; i < fEffects.GetCount(); i++) + mgr->WriteKey(s, fEffects[i]->GetKey()); +} + +hsBool plArmatureEffectsMgr::MsgReceive(plMessage* msg) +{ + plEventCallbackInterceptMsg *iMsg = plEventCallbackInterceptMsg::ConvertNoRef(msg); + if (iMsg) + { + if (fEnabled) + iMsg->SendMessage(); + } + + plArmatureEffectMsg *eMsg = plArmatureEffectMsg::ConvertNoRef(msg); + plArmatureEffectStateMsg *sMsg = plArmatureEffectStateMsg::ConvertNoRef(msg); + if (eMsg || sMsg) + { + // Always handle state messages, but only trigger actual effects if we're enabled + if (sMsg || fEnabled) + { + int i; + for (i = 0; i < fEffects.GetCount(); i++) + fEffects[i]->HandleTrigger(msg); + } + + return true; + } + + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + plArmatureEffect *effect = plArmatureEffect::ConvertNoRef(refMsg->GetRef()); + if (effect) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fEffects.Append(effect); + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + fEffects.RemoveItem(effect); + + return true; + } + } + + plAvatarStealthModeMsg *stealthMsg = plAvatarStealthModeMsg::ConvertNoRef(msg); + if (stealthMsg && stealthMsg->GetSender() == fArmature->GetTarget(0)->GetKey()) + { + if (stealthMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked) + fEnabled = false; + else + fEnabled = true; + + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + +UInt32 plArmatureEffectsMgr::GetNumEffects() +{ + return fEffects.GetCount(); +} + +plArmatureEffect *plArmatureEffectsMgr::GetEffect(UInt32 num) +{ + return fEffects[num]; +} + +void plArmatureEffectsMgr::ResetEffects() +{ + int i; + for (i = 0; i < fEffects.GetCount(); i++) + fEffects[i]->Reset(); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Actual Effects +// +///////////////////////////////////////////////////////////////////////////// + +plArmatureEffectFootSound::plArmatureEffectFootSound() +{ + plArmatureEffectFootSurface *surface = TRACKED_NEW plArmatureEffectFootSurface; + surface->fID = plArmatureEffectsMgr::kFootNoSurface; + surface->fTrigger = nil; + fSurfaces.Append(surface); + int i; + for (i = 0; i < plArmatureEffectsMgr::kMaxSurface; i++) + { + fMods[i] = nil; + } + SetFootType(kFootTypeShoe); +} + +plArmatureEffectFootSound::~plArmatureEffectFootSound() +{ + int i; + for (i = 0; i < fSurfaces.GetCount(); i++) + delete fSurfaces[i]; +} + +void plArmatureEffectFootSound::Read(hsStream* s, hsResMgr* mgr) +{ + plArmatureEffect::Read(s, mgr); + + int count = s->ReadByte(); + int i; + for (i = 0; i < count; i++) + { + plGenRefMsg *msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, i, -1); + mgr->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef); + } +} + +UInt32 plArmatureEffectFootSound::IFindSurfaceByTrigger(plKey trigger) +{ + UInt32 i; + + // Skip index 0. It's the special "NoSurface" that should always be at the stack bottom + for (i = 1; i < fSurfaces.GetCount(); i++) + { + if (fSurfaces[i]->fTrigger == trigger) + return i; + } + + return -1; +} + +void plArmatureEffectFootSound::Write(hsStream* s, hsResMgr* mgr) +{ + plArmatureEffect::Write(s, mgr); + + s->WriteByte(plArmatureEffectsMgr::kMaxSurface); + int i; + for (i = 0; i < plArmatureEffectsMgr::kMaxSurface; i++) + mgr->WriteKey(s, (fMods[i] ? fMods[i]->GetKey() : nil)); +} + +hsBool plArmatureEffectFootSound::MsgReceive(plMessage* msg) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + plRandomSoundMod *rsMod = plRandomSoundMod::ConvertNoRef(refMsg->GetRef()); + if (rsMod) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fMods[refMsg->fWhich] = rsMod; + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + fMods[refMsg->fWhich] = nil; + + return true; + } + } + + return plArmatureEffect::MsgReceive(msg); +} + +hsBool plArmatureEffectFootSound::HandleTrigger(plMessage* msg) +{ + plArmatureEffectMsg *eMsg = plArmatureEffectMsg::ConvertNoRef(msg); + if (eMsg) + { + UInt32 curSurfaceIndex = fSurfaces[fSurfaces.GetCount() - 1]->fID; + + if (curSurfaceIndex < plArmatureEffectsMgr::kMaxSurface && fMods[curSurfaceIndex] != nil) + { + if (plgAudioSys::Active() && fActiveSurfaces.IsBitSet(curSurfaceIndex)) + { + fMods[curSurfaceIndex]->SetCurrentGroup(eMsg->fTriggerIdx); + plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg; + animMsg->AddReceiver(fMods[curSurfaceIndex]->GetKey()); + animMsg->SetCmd(plAnimCmdMsg::kContinue); + plgDispatch::MsgSend(animMsg); + } + } + + return true; + } + + plArmatureEffectStateMsg *sMsg = plArmatureEffectStateMsg::ConvertNoRef(msg); + if (sMsg) + { + // It doesn't matter if we're adding or removing a surface, our load/unload is the same: + // unload the old surface and load the new one + + if (sMsg->fAddSurface) + { + if (IFindSurfaceByTrigger(sMsg->GetSender()) == -1) // Check that it's not a repeat msg + { + plStatusLog::AddLineS("audio.log", "FTSP: Switching to surface - %s", + plArmatureEffectsMgr::SurfaceStrings[sMsg->fSurface]); + plArmatureEffectFootSurface *surface = TRACKED_NEW plArmatureEffectFootSurface; + surface->fID = sMsg->fSurface; + surface->fTrigger = sMsg->GetSender(); + fSurfaces.Append(surface); + } + } + else + { + UInt32 index = IFindSurfaceByTrigger(sMsg->GetSender()); + if (index != -1) + { + if (index == fSurfaces.GetCount() - 1) // It's the top on the stack + plStatusLog::AddLineS("audio.log", "FTSP: Switching to surface - %s", + plArmatureEffectsMgr::SurfaceStrings[fSurfaces[index - 1]->fID]); + delete fSurfaces[index]; + fSurfaces.Remove(index); + } + } + return true; + } + + return false; +} + +void plArmatureEffectFootSound::Reset() +{ + while (fSurfaces.GetCount() > 1) + delete fSurfaces.Pop(); +} + +void plArmatureEffectFootSound::SetFootType(UInt8 type) +{ + if (type == kFootTypeBare) + { + fActiveSurfaces.Clear(); + fActiveSurfaces.SetBit(plArmatureEffectsMgr::kFootDeepWater); + fActiveSurfaces.SetBit(plArmatureEffectsMgr::kFootPuddle); + fActiveSurfaces.SetBit(plArmatureEffectsMgr::kFootWater); + fActiveSurfaces.SetBit(plArmatureEffectsMgr::kFootSwimming); + } + else + { + fActiveSurfaces.Set(plArmatureEffectsMgr::kMaxSurface); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.h new file mode 100644 index 00000000..c3c2000b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureEffects.h @@ -0,0 +1,148 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/** \file plArmatureEffects.h + Manages environmental effects for the avatar. +*/ +#ifndef plArmatureEffects_inc +#define plArmatureEffects_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsTemplates.h" +#include "hsBitVector.h" + +class plArmatureMod; +class plArmatureEffect; +class plRandomSoundMod; + +/** \class plArmatureEffects + Passes key avatar events to external effects generators. + Currently used for footstep sounds only, but should eventually + generalize to water splashes, etc. + More to come...*/ +class plArmatureEffectsMgr : public hsKeyedObject +{ +protected: + hsTArray fEffects; + hsBool fEnabled; + +public: + + plArmatureEffectsMgr() : fArmature(nil), fEnabled(true) {} + virtual ~plArmatureEffectsMgr() {} + + CLASSNAME_REGISTER( plArmatureEffectsMgr ); + GETINTERFACE_ANY( plArmatureEffectsMgr, hsKeyedObject ); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + UInt32 GetNumEffects(); + plArmatureEffect *GetEffect(UInt32 num); + void ResetEffects(); + + plArmatureMod *fArmature; + + enum + { + kFootDirt, + kFootPuddle, + kFootWater, + kFootTile, + kFootMetal, + kFootWoodBridge, + kFootRopeLadder, + kFootGrass, + kFootBrush, + kFootHardWood, + kFootRug, + kFootStone, + kFootMud, + kFootMetalLadder, + kFootWoodLadder, + kFootDeepWater, + kFootMaintainerGlass, + kFootMaintainerStone, + kFootSwimming, + kMaxSurface, + kFootNoSurface = kMaxSurface, + }; + static const char *SurfaceStrings[]; +}; + +class plArmatureEffect : public hsKeyedObject +{ +public: + plArmatureEffect() {} + ~plArmatureEffect() {} + + CLASSNAME_REGISTER( plArmatureEffect ); + GETINTERFACE_ANY( plArmatureEffect, hsKeyedObject ); + + virtual hsBool HandleTrigger(plMessage* msg) = 0; + virtual void Reset() {} +}; + +class plArmatureEffectFootSurface +{ +public: + UInt8 fID; + plKey fTrigger; +}; + +class plArmatureEffectFootSound : public plArmatureEffect +{ +protected: + hsTArray fSurfaces; + hsBitVector fActiveSurfaces; + plRandomSoundMod *fMods[plArmatureEffectsMgr::kMaxSurface]; + + UInt32 IFindSurfaceByTrigger(plKey trigger); + +public: + plArmatureEffectFootSound(); + ~plArmatureEffectFootSound(); + + CLASSNAME_REGISTER( plArmatureEffectFootSound ); + GETINTERFACE_ANY( plArmatureEffectFootSound, plArmatureEffect ); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + virtual hsBool HandleTrigger(plMessage* msg); + virtual void Reset(); + void SetFootType(UInt8); + + enum + { + kFootTypeShoe, + kFootTypeBare, + }; +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp new file mode 100644 index 00000000..f0fe4837 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp @@ -0,0 +1,2840 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plArmatureMod.h" + +// local +#include "plAvBrain.h" +#include "plAvBrainUser.h" +#include "plAvatarMgr.h" +#include "plAGModifier.h" +#include "plAvatarClothing.h" +#include "plClothingSDLModifier.h" +#include "plAvatarSDLModifier.h" +#include "plAGAnim.h" +#include "plArmatureEffects.h" +#include "plAvBrainHuman.h" +#include "plMatrixChannel.h" +#include "plAvatarTasks.h" +#include "plAvCallbackAction.h" +#include "plAvBrainCritter.h" + +// global +#include "plgDispatch.h" +#include "hsQuat.h" +#include "hsTimer.h" + +// other +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../plInterp/plAnimTimeConvert.h" + +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plSDLModifierMsg.h" +#include "../pnMessage/plAttachMsg.h" +#include "../pnMessage/plWarpMsg.h" +#include "../pnMessage/plCorrectionMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pnMessage/plPipeResMakeMsg.h" + +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plAvatarFootMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plLoadAgeMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plListenerMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../plMessage/plParticleUpdateMsg.h" + +#include "../plParticleSystem/plParticleSystem.h" +#include "../plParticleSystem/plParticleSDLMod.h" + +#include "../pfMessage/plArmatureEffectMsg.h" +#include "../pfMessage/pfKIMsg.h" +#include "../plVault/plVault.h" + +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plKeyImp.h" + +#include "../plDrawable/plInstanceDrawInterface.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plSurface/plLayerAnimation.h" +#include "../plSurface/hsGMaterial.h" + +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClient/plNetClientMgr.h" // for CCR stuff.. +#include "../plNetClient/plNetLinkingMgr.h" +#include "../plModifier/plSpawnModifier.h" +#include "../plPipeline/plDebugText.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plAudio/plWin32StaticSound.h" +#include "../plAudio/plAudioSystem.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plInputCore/plAvatarInputInterface.h" +#include "../plInputCore/plSceneInputInterface.h" +#include "../plInputCore/plInputDevice.h" +#include "../pfCamera/plVirtualCamNeu.h" +#include "../plScene/plRelevanceMgr.h" +#include "../plMessage/plSimStateMsg.h" + +#include "../plGImage/plLODMipmap.h" +#include "plPipeline.h" +#include "plTweak.h" +#include "../plDrawable/plVisLOSMgr.h" + +int plArmatureModBase::fMinLOD = 0; // standard is 3 levels of LOD +double plArmatureModBase::fLODDistance = 50.0; + + +plArmatureModBase::plArmatureModBase() : + fWaitFlags(kNeedMesh | kNeedPhysics | kNeedApplicator | kNeedBrainActivation), + fController(nil), + fCurLOD(-1), + fRootAnimator(nil), + fDisabledPhysics(0), + fDisabledDraw(0) +{ +} + +plArmatureModBase::~plArmatureModBase() +{ + delete fController; + + while (fUnusedBones.size() > 0) + { + delete fUnusedBones.back(); + fUnusedBones.pop_back(); + } +} + +hsBool plArmatureModBase::MsgReceive(plMessage* msg) +{ + hsBool result = false; + + plArmatureBrain *curBrain = nil; + if (fBrains.size() > 0) + { + curBrain = fBrains.back(); + if(curBrain->MsgReceive(msg)) + return true; + } + + return plAGMasterMod::MsgReceive(msg); +} + +void plArmatureModBase::AddTarget(plSceneObject* so) +{ + plAGMasterMod::AddTarget(so); + + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +void plArmatureModBase::RemoveTarget(plSceneObject* so) +{ + int count = fBrains.size(); + for(int i = count - 1; i >= 0; i--) + { + plArmatureBrain *brain = fBrains[i]; + if (brain) + brain->Deactivate(); + delete brain; + fBrains.pop_back(); + } + + plAGMasterMod::RemoveTarget(so); +} + +hsBool plArmatureModBase::IEval(double time, hsScalar elapsed, UInt32 dirty) +{ + if (IsFinal()) + { + if (fBrains.size()) + { + plArmatureBrain *curBrain = fBrains.back(); + if (curBrain) + { + hsBool result = curBrain->Apply(time, elapsed); + if (!result) + { + PopBrain(); + delete curBrain; + } + } + } + AdjustLOD(); + } + else + IFinalize(); + + return true; +} + +void plArmatureModBase::Read(hsStream * stream, hsResMgr *mgr) +{ + plAGMasterMod::Read(stream, mgr); + + int i; + int meshKeyCount = stream->ReadSwap32(); + for(i = 0; i < meshKeyCount; i++) + { + plKey meshKey = mgr->ReadKey(stream); + fMeshKeys.push_back(meshKey); + + plKeyVector *vec = TRACKED_NEW plKeyVector; + int boneCount = stream->ReadSwap32(); + for(int j = 0; j < boneCount; j++) + vec->push_back(mgr->ReadKey(stream)); + fUnusedBones.push_back(vec); + } + + int nBrains = stream->ReadSwap32(); + for (i = 0; i < nBrains; i++) + { + plArmatureBrain * brain = (plArmatureBrain *)mgr->ReadCreatable(stream); + this->PushBrain(brain); + } +} + +void plArmatureModBase::Write(hsStream *stream, hsResMgr *mgr) +{ + plAGMasterMod::Write(stream, mgr); + + int i; + int meshKeyCount = fMeshKeys.size(); + stream->WriteSwap32(meshKeyCount); + for (i = 0; i < meshKeyCount; i++) + { + plKey meshKey = fMeshKeys[i]; + mgr->WriteKey(stream, meshKey); + + // Should be a list per mesh key + stream->WriteSwap32(fUnusedBones[i]->size()); + for(int j = 0; j < fUnusedBones[i]->size(); j++) + mgr->WriteKey(stream, (*fUnusedBones[i])[j]); + } + + int nBrains = fBrains.size(); + stream->WriteSwap32(nBrains); + for (i = 0; i < nBrains; i++) + { + mgr->WriteCreatable(stream, fBrains[i]); + } +} + +void plArmatureModBase::AddressMessageToDescendants(const plCoordinateInterface * CI, plMessage *msg) +{ + if (!CI) + return; + + msg->AddReceiver(CI->GetOwnerKey()); + for (int i = 0; i < CI->GetNumChildren(); i++) + AddressMessageToDescendants(CI->GetChild(i), msg); +} + +void plArmatureModBase::EnableDrawingTree(const plSceneObject *object, hsBool status) +{ + if (!object) + return; + + plEnableMsg *msg = TRACKED_NEW plEnableMsg; + if (status) + msg->SetCmd( plEnableMsg::kEnable ); + else + msg->SetCmd( plEnableMsg::kDisable ); + msg->SetCmd( plEnableMsg::kDrawable ); + + const plCoordinateInterface *pCI = object->GetCoordinateInterface(); + AddressMessageToDescendants(pCI, msg); + plgDispatch::MsgSend(msg); +} + +plKey plArmatureModBase::GetWorldKey() const +{ + if (fController) + return fController->GetSubworld(); + else + return nil; +} + +hsBool plArmatureModBase::ValidatePhysics() +{ + if (!fTarget) + return false; + + if (fController) + { + EnablePhysics(true); + fWaitFlags &= ~kNeedPhysics; + } + + return !(fWaitFlags & kNeedPhysics); +} + +hsBool plArmatureModBase::ValidateMesh() +{ + if (fWaitFlags & kNeedMesh) + { + fWaitFlags &= ~kNeedMesh; + int n = fMeshKeys.size(); + + for(int i = 0; i < n; i++) + { + plKey meshKey = fMeshKeys[i]; + plSceneObject *meshObj = (plSceneObject *)meshKey->GetObjectPtr(); + + if (!meshObj) + { + fWaitFlags |= kNeedMesh; + break; + } + hsBool visible = (i == fCurLOD ? true : false); + EnableDrawingTree(meshObj, visible); + } + } + + return !(fWaitFlags & kNeedMesh); +} + +void plArmatureModBase::PushBrain(plArmatureBrain *brain) +{ + plArmatureBrain *oldBrain = GetCurrentBrain(); + if (oldBrain) + oldBrain->Suspend(); + + + fBrains.push_back(brain); + // don't activate brains until we are attached to our target + // addTarget will do activation... + if (GetTarget(0)) + brain->Activate(this); + + DirtySynchState(kSDLAvatar, 0); +} + +void plArmatureModBase::PopBrain() +{ + plArmatureBrain *oldBrain = nil; + if (fBrains.size() > 0) + { + oldBrain = fBrains.back(); + oldBrain->Deactivate(); + fBrains.pop_back(); + } + + plArmatureBrain *newBrain = GetCurrentBrain(); + if (newBrain) + newBrain->Resume(); + + DirtySynchState(kSDLAvatar, 0); +} + +plArmatureBrain *plArmatureModBase::GetCurrentBrain() const +{ + plArmatureBrain *result = nil; + if (fBrains.size() > 0) + result = fBrains.back(); + + return result; +} + +plDrawable *plArmatureModBase::FindDrawable() const +{ + if (fMeshKeys[0] == nil) + return nil; + + plSceneObject *so = plSceneObject::ConvertNoRef(fMeshKeys[0]->ObjectIsLoaded()); + if (so == nil) + return nil; + + const plDrawInterface *di = so->GetDrawInterface(); + if (di && di->GetNumDrawables() > 0) + { + plDrawable *spans = plDrawable::ConvertNoRef(di->GetDrawable(0)); + if (spans) + return spans; + } + + const plInstanceDrawInterface *idi = plInstanceDrawInterface::ConvertNoRef(di); + if (idi) + return idi->GetInstanceDrawable(); + + return nil; +} + +void plArmatureModBase::LeaveAge() +{ + int nBrains = fBrains.size(); + for (int i = nBrains - 1; i >= 0; i--) + { + plArmatureBrain * curBrain = fBrains[i]; + if (curBrain->LeaveAge()) + { + if (curBrain == GetCurrentBrain()) + { + PopBrain(); + delete curBrain; + } + } + } +} + +hsBool plArmatureModBase::IsFinal() +{ + return !fWaitFlags; +} + +void plArmatureModBase::AdjustLOD() +{ + if (!IsDrawEnabled()) + return; + + hsPoint3 camPos = plVirtualCam1::Instance()->GetCameraPos(); + + plSceneObject * SO = GetTarget(0); + if (SO) + { + hsMatrix44 l2w = SO->GetLocalToWorld(); + hsPoint3 ourPos = l2w.GetTranslate(); + hsPoint3 delta = ourPos - camPos; + hsScalar distanceSquared = delta.MagnitudeSquared(); + if (distanceSquared < fLODDistance * fLODDistance) + SetLOD(__max(0, fMinLOD)); + else if (distanceSquared < fLODDistance * fLODDistance * 4.0) + SetLOD(__max(1, fMinLOD)); + else + SetLOD(2); + } +} + +// Should always be called from AdjustLOD +hsBool plArmatureModBase::SetLOD(int iNewLOD) +{ + if (iNewLOD >= fMeshKeys.size()) + iNewLOD = fMeshKeys.size() - 1; + + int oldLOD = fCurLOD; + if (iNewLOD != fCurLOD) + { + int oldLOD = fCurLOD; + if(fMeshKeys.size() > iNewLOD) + { + if (fCurLOD != -1) + { + plSceneObject * oldMesh = (plSceneObject *)fMeshKeys[oldLOD]->GetObjectPtr(); + EnableDrawingTree(oldMesh, false); + } + else + { + // just starting up; turn all except current off + for (int i = 0; i < fMeshKeys.size(); i++) + { + if (i != iNewLOD) + { + plKey offKey = fMeshKeys[i]; + plSceneObject * offMesh = (plSceneObject *)offKey->GetObjectPtr(); + EnableDrawingTree(offMesh, false); + } + } + } + + fCurLOD = iNewLOD; + plSceneObject * newMesh = (plSceneObject *)fMeshKeys[fCurLOD]->GetObjectPtr(); + EnableDrawingTree(newMesh, true); + + int boneLOD; + for (boneLOD = 0; boneLOD < fMeshKeys.size(); boneLOD++) + IEnableBones(boneLOD, boneLOD <= iNewLOD ? false: true); + } + } + return oldLOD; +} + +void plArmatureModBase::RefreshTree() +{ + fCurLOD = -1; + AdjustLOD(); +} + +int plArmatureModBase::AppendMeshKey(plKey meshKey) +{ + fMeshKeys.push_back(meshKey); + return fMeshKeys.size() - 1; +} + +int plArmatureModBase::AppendBoneVec(plKeyVector *boneVec) +{ + fUnusedBones.push_back(boneVec); + return fUnusedBones.size() - 1; +} + +UInt8 plArmatureModBase::GetNumLOD() const +{ + return fMeshKeys.size(); +} + +void plArmatureModBase::EnablePhysics(hsBool status, UInt16 reason /* = kDisableReasonUnknown */) +{ + if (status) + fDisabledPhysics &= ~reason; + else + fDisabledPhysics |= reason; + + hsBool newStatus = !fDisabledPhysics; + + if (fController) + fController->Enable(newStatus); +} + +// +// Enabling Kinematics (state=true) will have the affect of: +// - disabling outside forces (gravity and other physics objects pushing us) +// - but leave on collisions with detector regions +// Disabling Kinematics (state=false) means: +// - all outside forces will affect us and collisions on +// i.e. normal enabled physical +void plArmatureModBase::EnablePhysicsKinematic(hsBool status) +{ + if (fController) + fController->Kinematic(status); +} + +void plArmatureModBase::EnableDrawing(hsBool status, UInt16 reason /* = kDisableReasonUnknown */) +{ + hsBool oldStatus = !fDisabledDraw; + if (status) + fDisabledDraw &= ~reason; + else + fDisabledDraw |= reason; + + hsBool newStatus = !fDisabledDraw; + if (oldStatus == newStatus) + return; + + int i; + for (i = 0; i < fMeshKeys.size(); i++) + { + plSceneObject *obj = plSceneObject::ConvertNoRef(fMeshKeys[i]->ObjectIsLoaded()); + if (obj) + EnableDrawingTree(obj, newStatus); + } + + if (status) + fCurLOD = -1; // We just enabled all LOD. Need to force ourselves to recompute current LOD +} + +void plArmatureModBase::IFinalize() +{ + if (fWaitFlags & kNeedMesh) + ValidateMesh(); + if (fWaitFlags & kNeedPhysics) + ValidatePhysics(); + if (fWaitFlags & kNeedApplicator) + ICustomizeApplicator(); + if (fWaitFlags & kNeedBrainActivation) + { + int nBrains = fBrains.size(); + for (int i = 0; i < nBrains; i++) + { + // every brain gets activated, but all except the top brain + // also get suspended when the one above them is activated. + if (i > 0) + fBrains[i - 1]->Suspend(); + + fBrains[i]->Activate(this); + } + fWaitFlags &= ~kNeedBrainActivation; + } +} + +void plArmatureModBase::ICustomizeApplicator() +{ + const plAGModifier *agMod = plAGModifier::ConvertNoRef(FindModifierByClass(GetTarget(0), plAGModifier::Index())); + if (agMod) + { + plAGApplicator *app = agMod->GetApplicator(kAGPinTransform); + if (app) + { + plMatrixDifferenceApp *differ = plMatrixDifferenceApp::ConvertNoRef(app); + + if (differ) + { + fRootAnimator = differ; + fWaitFlags &= ~kNeedApplicator; + return; // already there + } + } + plAGModifier *volAGMod = const_cast(agMod); + plMatrixDifferenceApp *differ = TRACKED_NEW plMatrixDifferenceApp(); + + fRootAnimator = differ; + volAGMod->SetApplicator(differ); + differ->Enable(false); + fWaitFlags &= ~kNeedApplicator; + } +} + +void plArmatureModBase::IEnableBones(int lod, hsBool enable) +{ + if (lod < fUnusedBones.size()) + { + plKeyVector *vec = fUnusedBones[lod]; + int i; + for (i = 0; i < vec->size(); i++) + ((plAGModifier *)(*vec)[i]->GetObjectPtr())->Enable(enable); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +const char *plArmatureMod::BoneStrings[] = {"Male", "Female", "Critter", "Actor"}; + +const hsScalar plArmatureMod::kAvatarInputSynchThreshold = 10.f; +hsScalar plArmatureMod::fMouseTurnSensitivity = 1.f; +hsBool plArmatureMod::fClickToTurn = true; + +void plArmatureMod::IInitDefaults() +{ + fBoneRootAnimator = nil; + fRootAGMod = nil; + fFootSoundSOKey = nil; + fLinkSoundSOKey = nil; + fBodyType = kBoneBaseMale; + fClothingOutfit = nil; + fClothingSDLMod = nil; + fAvatarSDLMod = nil; + fAvatarPhysicalSDLMod = nil; + fEffects = nil; + fDebugOn = false; + fBoneMap = nil; + fStealthMode = plAvatarStealthModeMsg::kStealthVisible; + fStealthLevel = 0; + fMouseFrameTurnStrength = 0.f; + fSuspendInputCount = 0; + fMidLink = false; + fAlreadyPanicLinking = false; + fReverseFBOnIdle = false; + fFollowerParticleSystemSO = nil; + fPendingSynch = false; + fLastInputSynch = 0; + fOpaque = true; + fPhysHeight = 0.f; + fPhysWidth = 0.f; + fUpdateMsg = nil; + fRootName = nil; + fDontPanicLink = false; + fBodyAgeName = "GlobalAvatars"; + fBodyFootstepSoundPage = "Audio"; + fAnimationPrefix = "Male"; + fUserStr = ""; +} + +plArmatureMod::plArmatureMod() : plArmatureModBase() +{ + IInitDefaults(); + fWaitFlags |= kNeedAudio | kNeedCamera | kNeedSpawn; +} + +plArmatureMod::~plArmatureMod() +{ + delete fBoneMap; + delete [] fRootName; + + if (fUpdateMsg) + fUpdateMsg->UnRef(); +} + +void plArmatureMod::SetPositionAndRotationSim(const hsPoint3* position, const hsQuat* rotation) +{ + const plCoordinateInterface* subworldCI = nil; + if (fController) + subworldCI = fController->GetSubworldCI(); + + hsMatrix44 l2w, w2l; + + // If we need the position or rotation, grab it from the avatar + if (!position || !rotation) + { + // Get the current position of the avatar in sim space + hsMatrix44 avatarL2S = GetTarget(0)->GetLocalToWorld(); + if (subworldCI) + avatarL2S = subworldCI->GetWorldToLocal() * avatarL2S; + + if (!rotation) + l2w = avatarL2S; + else + rotation->MakeMatrix(&l2w); + + if (!position) + { + hsPoint3 simPos; + avatarL2S.GetTranslate(&simPos); + l2w.SetTranslate(&simPos); + } + else + l2w.SetTranslate(position); + } + else + { + rotation->MakeMatrix(&l2w); + l2w.SetTranslate(position); + } + + // We've got the requested position of the avatar in subworld space, convert + // it to world space if necessary + if (subworldCI) + l2w = subworldCI->GetLocalToWorld() * l2w; + l2w.GetInverse(&w2l); + + GetTarget(0)->SetTransform(l2w, w2l); + GetTarget(0)->FlushTransform(); +} + +void plArmatureMod::GetPositionAndRotationSim(hsPoint3* position, hsQuat* rotation) +{ + hsMatrix44 l2s = GetTarget(0)->GetLocalToWorld(); + + if (fController) + { + const plCoordinateInterface* subworldCI = fController->GetSubworldCI(); + if (subworldCI) + l2s = subworldCI->GetWorldToLocal() * l2s; + + if (position) + l2s.GetTranslate(position); + if (rotation) + rotation->SetFromMatrix(&l2s); + } +} + +const plSceneObject *plArmatureMod::FindBone(const char * name) const +{ + plSceneObject *result = nil; + + plAGModifier * mod = GetChannelMod(name); + + if (mod) + result = mod->GetTarget(0); + + return result; +} + +const plSceneObject *plArmatureMod::FindBone(UInt32 id) const +{ + if(fBoneMap) + return fBoneMap->FindBone(id); + else + return nil; +} + +void plArmatureMod::AddBoneMapping(UInt32 id, const plSceneObject *bone) +{ + if(!fBoneMap) + fBoneMap = TRACKED_NEW plAvBoneMap(); + + fBoneMap->AddBoneMapping(id, bone); +} + +void plArmatureMod::WindowActivate(bool active) +{ + if (!active) // We don't want the avatar to move while we don't have focus + { + if (!plAvatarMgr::GetInstance()) + return; + + plArmatureMod *localAv = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (localAv) + localAv->ClearInputFlags(true, true); + } +} + +char *plArmatureMod::fSpawnPointOverride = nil; + +void plArmatureMod::SetSpawnPointOverride( const char *overrideObjName ) +{ + delete [] fSpawnPointOverride; + if( overrideObjName == nil ) + fSpawnPointOverride = nil; + else + { + fSpawnPointOverride = hsStrcpy( overrideObjName ); + strlwr( fSpawnPointOverride ); + } +} + +int plArmatureMod::IFindSpawnOverride( void ) +{ + if( fSpawnPointOverride == nil || fSpawnPointOverride[ 0 ] == 0 ) + return -1; + int i; + plAvatarMgr *mgr = plAvatarMgr::GetInstance(); + for( i = 0; i < mgr->NumSpawnPoints(); i++ ) + { + char str2[ 256 ]; + strcpy(str2, mgr->GetSpawnPoint( i )->GetTarget(0)->GetKeyName()); + strlwr(str2); + + if (strstr(str2, fSpawnPointOverride) != nil) + return i; // Found it! + } + return -1; +} + +void plArmatureMod::Spawn(double timeNow) +{ + plAvatarMgr *mgr = plAvatarMgr::GetInstance(); + int numSpawnPoints = mgr->NumSpawnPoints(); + + int spawnNum = IFindSpawnOverride(); + if( spawnNum == -1 ) + { + spawnNum = plAvatarMgr::GetInstance()->FindSpawnPoint( "LinkInPointDefault" ); + if( spawnNum == -1 ) + { + spawnNum = plNetClientApp::GetInstance()->GetJoinOrder(); + } + } + + hsAssert(numSpawnPoints, "No spawn points! You are about to crash!"); + + if ( numSpawnPoints ) + spawnNum %= numSpawnPoints; + + ValidatePhysics(); + + SpawnAt(spawnNum, timeNow); +} + +void plArmatureMod::SpawnAt(int spawnNum, double time) +{ + plAvatarMgr *mgr = plAvatarMgr::GetInstance(); + const plSpawnModifier * spawn = mgr->GetSpawnPoint(spawnNum); + + hsMatrix44 l2w; + hsMatrix44 w2l; + if ( spawn ) + { + const plSceneObject * spawnObj = spawn->GetTarget(0); + l2w = spawnObj->GetLocalToWorld(); + w2l = spawnObj->GetWorldToLocal(); + } + + if (fController) + fController->ResetAchievedLinearVelocity(); + plCoordinateInterface* ci = (plCoordinateInterface*)GetTarget(0)->GetCoordinateInterface(); + l2w.RemoveScale(); + w2l.RemoveScale(); + ci->SetTransform(l2w, w2l); + ci->FlushTransform(); + + if (plVirtualCam1::Instance()) + plVirtualCam1::Instance()->SetCutNextTrans(); + + if (GetFollowerParticleSystemSO()) + { + // Since particles are in world space, if we've got some surrounding us, we've got to translate them to compensate for our warp. + plParticleSystem *sys = const_cast(plParticleSystem::ConvertNoRef(GetFollowerParticleSystemSO()->GetModifierByType(plParticleSystem::Index()))); + if (sys) + { + hsPoint3 trans = l2w.GetTranslate() - GetTarget(0)->GetLocalToWorld().GetTranslate(); + sys->TranslateAllParticles(trans); + } + } + + int nBrains = fBrains.size(); + for (int i = 0; i < nBrains; i++) + { + plArmatureBrain * curBrain = fBrains[i]; + curBrain->Spawn(time); + } + fWaitFlags &= ~kNeedSpawn; + + plAvatarSpawnNotifyMsg *notify = TRACKED_NEW plAvatarSpawnNotifyMsg(); + notify->SetTimeStamp(hsTimer::GetSysSeconds() + 0.1); + notify->SetBCastFlag(plMessage::kBCastByExactType); + notify->Send(); + + EnablePhysics(true); +} + +void plArmatureMod::SetFollowerParticleSystemSO(plSceneObject *follower) +{ + // TODO: Check for old one and clean up. + hsPoint3 trans = GetTarget(0)->GetLocalToWorld().GetTranslate() - follower->GetLocalToWorld().GetTranslate(); + + plWarpMsg *warp = TRACKED_NEW plWarpMsg(GetKey(), follower->GetKey(), plWarpMsg::kFlushTransform | plWarpMsg::kZeroVelocity, + GetTarget(0)->GetLocalToWorld()); + warp->Send(); + hsgResMgr::ResMgr()->AddViaNotify(follower->GetKey(), TRACKED_NEW plAttachMsg(GetTarget(0)->GetKey(), nil, plRefMsg::kOnRequest), plRefFlags::kActiveRef); + fFollowerParticleSystemSO = follower; + + plParticleSystem *sys = const_cast(plParticleSystem::ConvertNoRef(follower->GetModifierByType(plParticleSystem::Index()))); + if (sys) + { + sys->fMiscFlags |= plParticleSystem::kParticleSystemAlwaysUpdate; + sys->TranslateAllParticles(trans); + sys->SetAttachedToAvatar(true); + } +} + +plSceneObject *plArmatureMod::GetFollowerParticleSystemSO() +{ + return fFollowerParticleSystemSO; +} + +void plArmatureMod::RegisterForBehaviorNotify(plKey key) +{ + if (fNotifyKeys.Find(key) == fNotifyKeys.kMissingIndex) + fNotifyKeys.Append(key); +} + +void plArmatureMod::UnRegisterForBehaviorNotify(plKey key) +{ + fNotifyKeys.RemoveItem(key); +} + +void plArmatureMod::IFireBehaviorNotify(UInt32 type, hsBool behaviorStart) +{ + if (fNotifyKeys.GetCount() > 0) + { + plAvatarBehaviorNotifyMsg *msg = TRACKED_NEW plAvatarBehaviorNotifyMsg(); + msg->SetSender(GetKey()); + msg->AddReceivers(fNotifyKeys); + msg->fType = type; + msg->state = behaviorStart; + msg->Send(); + } +} + +void plArmatureMod::EnterAge(hsBool reSpawn) +{ + fMidLink = false; + fAlreadyPanicLinking = false; + EnablePhysics(true, kDisableReasonLinking); + ValidatePhysics(); + + // force regions to send an update whenever we enter an age + fOldRegionsICareAbout.Clear(); + fOldRegionsImIn.Clear(); + + if (GetFollowerParticleSystemSO()) + { + const plParticleSystem *sys = plParticleSystem::ConvertNoRef(GetFollowerParticleSystemSO()->GetModifierByType(plParticleSystem::Index())); + hsAssert(sys, "We have a particle system SO, but no system?"); + if (sys) + { + // Need to tell other clients about this + plLoadCloneMsg *clone = TRACKED_NEW plLoadCloneMsg(GetFollowerParticleSystemSO()->GetKey(), plAvatarMgr::GetInstance()->GetKey(), GetKey()->GetUoid().GetClonePlayerID(), true); + clone->SetBCastFlag(plMessage::kLocalPropagate, false); + clone->Send(); + + GetFollowerParticleSystemSO()->DirtySynchState(plParticleSDLMod::kStrNumParticles, plSynchedObject::kBCastToClients); + } + } + + ClearInputFlags(true, false); + if (reSpawn) + fWaitFlags |= kNeedSpawn; + + // In case we personal age linked out of a situation and didn't properly + // re-enable input... + plAvatarInputInterface::GetInstance()->EnableForwardMovement(true); + plAvatarInputInterface::GetInstance()->EnableJump(true); + while (fSuspendInputCount > 0) + ResumeInput(); + plAvatarInputInterface::GetInstance()->EnableMouseMovement(); +} + +void plArmatureMod::LeaveAge() +{ + plArmatureModBase::LeaveAge(); + + fMidLink = true; + + if (fController) + { + fController->LeaveAge(); + } + + if (GetFollowerParticleSystemSO()) + { + // Need to tell other clients to remove this + plLoadCloneMsg *clone = TRACKED_NEW plLoadCloneMsg(GetFollowerParticleSystemSO()->GetKey(), plAvatarMgr::GetInstance()->GetKey(), GetKey()->GetUoid().GetClonePlayerID(), false); + clone->SetBCastFlag(plMessage::kLocalPropagate, false); + clone->Send(); + } + + GetArmatureEffects()->ResetEffects(); + ClearInputFlags(true, false); +} + +void plArmatureMod::PanicLink(hsBool playLinkOutAnim /* = true */) +{ + // console override... just go back to the beginning + if (fDontPanicLink) + { + Spawn(0.f); + return; + } + + if (fAlreadyPanicLinking) + return; + fAlreadyPanicLinking = true; + + + plNetApp::StaticDebugMsg("plArmatureMod::PanicLink()"); + + // make the player book blink as they are linking out + pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kStartBookAlert ); + plgDispatch::MsgSend( msg ); + + // Can't depend on the anim to link if the human brain isn't ready to deal with it + plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(GetCurrentBrain()); + // If things break then uncomment the code below + if (!brain)// || brain->IsRunningTask()) + playLinkOutAnim = false; + + if (playLinkOutAnim) + { + plAvOneShotLinkTask *task = TRACKED_NEW plAvOneShotLinkTask; + + char *animName = MakeAnimationName("FallingLinkOut"); + task->SetAnimName(animName); + task->SetMarkerName("touch"); + + plAvTaskMsg *taskMsg = TRACKED_NEW plAvTaskMsg(GetKey(), GetKey(), task); + taskMsg->Send(); + + delete [] animName; + } + else + { + EnablePhysics(false, plArmatureMod::kDisableReasonLinking); + ILinkToPersonalAge(); + } +} + +void plArmatureMod::PersonalLink() +{ + // Can't depend on the anim to link if the human brain isn't ready to deal with it + plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(GetCurrentBrain()); + if (!brain || brain->IsRunningTask()) + ILinkToPersonalAge(); + else + { + plAvOneShotLinkTask *task = TRACKED_NEW plAvOneShotLinkTask; + char *animName = MakeAnimationName("PersonalLink"); + task->SetAnimName(animName); + task->SetMarkerName("touch"); + delete [] animName; + + plAvTaskMsg *taskMsg = TRACKED_NEW plAvTaskMsg(GetKey(), GetKey(), task); + taskMsg->SetBCastFlag(plMessage::kNetPropagate); + taskMsg->Send(); + } +} + +hsBool plArmatureMod::MsgReceive(plMessage* msg) +{ + hsBool result = false; + + plArmatureBrain *curBrain = nil; + if (fBrains.size() > 0) + { + curBrain = fBrains.back(); + if(curBrain->MsgReceive(msg)) + return true; + } + + plAvatarInputStateMsg *aisMsg = plAvatarInputStateMsg::ConvertNoRef(msg); + if (aisMsg) + { + IHandleInputStateMsg(aisMsg); + return true; + } + + plControlEventMsg *control = plControlEventMsg::ConvertNoRef(msg); + if(control) + { + IHandleControlMsg(control); + return true; + } + + plCorrectionMsg *corMsg = plCorrectionMsg::ConvertNoRef(msg); + if (corMsg) + { + hsMatrix44 &correction = corMsg->fWorldToLocal * GetTarget(0)->GetLocalToWorld(); + if (fBoneRootAnimator) + fBoneRootAnimator->SetCorrection(correction); + } + + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + plClothingOutfit *outfit = plClothingOutfit::ConvertNoRef(refMsg->GetRef()); + if (outfit) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fClothingOutfit = outfit; + fClothingOutfit->fAvatar = this; + plgDispatch::Dispatch()->RegisterForExactType(plPipeRTMakeMsg::Index(), fClothingOutfit->GetKey()); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + if (fClothingOutfit) + plgDispatch::Dispatch()->UnRegisterForExactType(plPipeRTMakeMsg::Index(), fClothingOutfit->GetKey()); + fClothingOutfit = nil; + outfit->fAvatar = nil; + } + return true; + } + plArmatureEffectsMgr *effects = plArmatureEffectsMgr::ConvertNoRef(refMsg->GetRef()); + if (effects) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fEffects = effects; + fEffects->fArmature = this; + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + fEffects->fArmature = nil; + fEffects = nil; + } + return true; + } + plSceneObject *so = plSceneObject::ConvertNoRef(refMsg->GetRef()); + if (so) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fClothToSOMap.ExpandAndZero(refMsg->fWhich + 1); + fClothToSOMap[refMsg->fWhich] = so; + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + fClothToSOMap[refMsg->fWhich] = nil; + } + return true; + } + } + + if (fEffects) + { + plArmatureEffectMsg *aeMsg = plArmatureEffectMsg::ConvertNoRef(msg); + plArmatureEffectStateMsg *aesMsg = plArmatureEffectStateMsg::ConvertNoRef(msg); + if (aeMsg || aesMsg) + return fEffects->MsgReceive(msg); + } + + plAttachMsg *aMsg = plAttachMsg::ConvertNoRef(msg); + if (aMsg) + { + GetTarget(0)->MsgReceive(aMsg); + return true; + } + + plEnableMsg *enMsg = plEnableMsg::ConvertNoRef(msg); + if(enMsg && enMsg->Type(plEnableMsg::kDrawable)) + { + hsBool enable = enMsg->Cmd(plEnableMsg::kEnable); + hsBool disable = enMsg->Cmd(plEnableMsg::kDisable); + + hsAssert(enable != disable, "Conflicting or missing commands to enable message"); + + if(enable != disable) + EnableDrawing(enable); + return true; + } + if(enMsg && enMsg->Cmd(plEnableMsg::kPhysical)) + { + EnablePhysics( enMsg->Cmd(plEnableMsg::kEnable)); + } + + plAvatarOpacityCallbackMsg *opacMsg = plAvatarOpacityCallbackMsg::ConvertNoRef(msg); + if (opacMsg) + { + plLayerLinkAnimation *linkAnim = IFindLayerLinkAnim(); + if (linkAnim) + { + bool trans = (linkAnim->GetTimeConvert().CurrentAnimTime() != 0.f); + ISetTransparentDrawOrder(trans); + } + return true; + } + + plAvatarPhysicsEnableCallbackMsg *epMsg = plAvatarPhysicsEnableCallbackMsg::ConvertNoRef(msg); + if (epMsg) + { + EnablePhysics(true); + return true; + } + + plAvatarStealthModeMsg *stealthMsg = plAvatarStealthModeMsg::ConvertNoRef(msg); + if (stealthMsg && stealthMsg->GetSender() == GetTarget(0)->GetKey() && + (stealthMsg->fLevel != fStealthLevel || stealthMsg->fMode != fStealthMode)) + { + fStealthMode = stealthMsg->fMode; + fStealthLevel = (stealthMsg->fMode == plAvatarStealthModeMsg::kStealthVisible) ? 0 : stealthMsg->fLevel; + + if (fStealthMode == plAvatarStealthModeMsg::kStealthCloaked) + { + if (fUpdateMsg) + fUpdateMsg->SetInvis(true); + } + else + { + if (fUpdateMsg) + fUpdateMsg->SetInvis(false); + } + + if (fEffects) + fEffects->MsgReceive(stealthMsg); + + if (stealthMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked) + EnableDrawing(false, kDisableReasonCCR); + else + EnableDrawing(true, kDisableReasonCCR); + + DirtySynchState(kSDLAvatar, 0); // changed invisibility state + plNetApp::StaticDebugMsg("ArmatureMod: rcvd avatarStealth msg, cloaked=%d", stealthMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked); + return true; + } + + plParticleTransferMsg *partMsg = plParticleTransferMsg::ConvertNoRef(msg); + if (partMsg) + { + // First, do we have the system? + plSceneObject *dstSysSO = GetFollowerParticleSystemSO(); + if (!dstSysSO || ((plKeyImp*)dstSysSO->GetKey())->GetCloneOwner() != partMsg->fSysSOKey) + { + // Need to clone and resend. + if (plNetClientApp::GetInstance()->GetLocalPlayer() != GetTarget(0)) + return true; // Only the local player can create the clone. + + // Clone is sent to all players. + plLoadCloneMsg *cloneMsg = TRACKED_NEW plLoadCloneMsg(partMsg->fSysSOKey->GetUoid(), plAvatarMgr::GetInstance()->GetKey(), GetKey()->GetUoid().GetClonePlayerID()); + cloneMsg->SetTriggerMsg(partMsg); + cloneMsg->SetBCastFlag(plMessage::kNetForce); + cloneMsg->Send(); + + // Expect to receive a clone message later. Return for now. + return true; + } + else + { + plParticleSystem *dstSys = const_cast(plParticleSystem::ConvertNoRef(dstSysSO->GetModifierByType(plParticleSystem::Index()))); + if (dstSys) + { + // Got the system. Time to steal particles! + plParticleSystem *srcSys = nil; + plSceneObject *srcSysSO = plSceneObject::ConvertNoRef(partMsg->fSysSOKey->ObjectIsLoaded()); + if (srcSysSO) + srcSys = const_cast(plParticleSystem::ConvertNoRef(srcSysSO->GetModifierByType(plParticleSystem::Index()))); + + // A nil source system is ok. It just won't copy anything. + int numToGen = partMsg->fNumToTransfer - dstSys->StealParticlesFrom(srcSys, partMsg->fNumToTransfer); + if (numToGen > 0) + { + dstSys->GenerateParticles(numToGen); + } + } + return true; + } + } + + plLoadAvatarMsg *avLoadMsg = plLoadAvatarMsg::ConvertNoRef(msg); + if (avLoadMsg) + { + hsBool isPlayer = avLoadMsg->GetIsPlayer(); + if (!isPlayer) + plgDispatch::Dispatch()->UnRegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); + + // see if we're being spawned explictly + plKey spawnPoint = avLoadMsg->GetSpawnPoint(); + + if (spawnPoint) + { + hsKeyedObject * spawnKO = spawnPoint->ObjectIsLoaded(); + if (spawnKO) + { + plSceneObject * spawnSO = plSceneObject::ConvertNoRef(spawnKO); + if(spawnSO) + { + hsMatrix44 l2w = spawnSO->GetLocalToWorld(); + plWarpMsg *warpM = TRACKED_NEW plWarpMsg(nil, GetTarget(0)->GetKey(), 0, l2w); + warpM->Send(); + fWaitFlags &= ~kNeedSpawn; + } + } + } + + // copy the user string over + const char* userStr = avLoadMsg->GetUserStr(); + if (userStr) + fUserStr = userStr; + else + fUserStr = ""; + + return true; + } + + plLoadCloneMsg *cloneMsg = plLoadCloneMsg::ConvertNoRef(msg); + if (cloneMsg) + { + if (cloneMsg->GetIsLoading()) + { + plSceneObject *so = plSceneObject::ConvertNoRef(cloneMsg->GetCloneKey()->ObjectIsLoaded()); + if (so) + { + so->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile); + plParticleSystem *sys = const_cast(plParticleSystem::ConvertNoRef(so->GetModifierByType(plParticleSystem::Index()))); + hsAssert(sys, "Modifier not loaded yet."); + if (sys) + { + sys->DisableGenerators(); + sys->MsgReceive(cloneMsg); // Let the system know to finish its init stuff + SetFollowerParticleSystemSO(so); + if (!plNetClientApp::GetInstance()->IsLoadingInitialAgeState()) + { + // Just happened. Redirect the trigger and transfer the particles + MsgReceive(cloneMsg->GetTriggerMsg()); + } + // otherwise we'll get SDL state about it and handle it in plParticleSDLMod + + return true; + } + } + } + else + { + if (cloneMsg->GetCloneKey() == GetFollowerParticleSystemSO()) + { + SetFollowerParticleSystemSO(nil); + return true; + } + } + } + + plLinkEffectBCMsg *linkBCMsg = plLinkEffectBCMsg::ConvertNoRef(msg); + if (linkBCMsg) + { + if (GetTarget(0)->GetKey() == linkBCMsg->fLinkKey) + { + if (linkBCMsg->HasLinkFlag(plLinkEffectBCMsg::kLeavingAge)) + { + fMidLink = true; + IFireBehaviorNotify(plHBehavior::kBehaviorTypeLinkOut, true); + } + else + IFireBehaviorNotify(plHBehavior::kBehaviorTypeLinkIn, true); + } + return true; + } + + plLinkInDoneMsg *doneMsg = plLinkInDoneMsg::ConvertNoRef(msg); + if (doneMsg) + { + IFireBehaviorNotify(plHBehavior::kBehaviorTypeLinkIn, false); + return true; + } + + plAgeLoadedMsg *ageLoadMsg = plAgeLoadedMsg::ConvertNoRef(msg); + if (ageLoadMsg && ageLoadMsg->fLoaded) + { + // only the local player gets these + NetworkSynch(hsTimer::GetSysSeconds(), true); + EnablePhysics(true); + return true; + } + + plAnimCmdMsg *cmdMsg = plAnimCmdMsg::ConvertNoRef(msg); + if (cmdMsg) + { + hsAssert(false, "Illegal use of plAnimCmdMsg on an avatar."); + return true; + } + + plSubWorldMsg* subMsg = plSubWorldMsg::ConvertNoRef(msg); + if (subMsg) + { + if (fController) + { + fController->SetSubworld(subMsg->fWorldKey); + DirtySynchState(kSDLAvatar, plSynchedObject::kBCastToClients); + } + return true; + } + + return plAGMasterMod::MsgReceive(msg); +} + +hsBool plArmatureMod::IHandleControlMsg(plControlEventMsg* pMsg) +{ + // Slight change in design here... + // Avatar input control messages are only sent locally. + // When things change (that a remote client would care about), + // we call SynchInputState and that sends off the bit vector + // for the state of all avatar input controls. + // + // This means a remote player will only know which controls are + // active at a certain point. It might miss a particular keypress. + // Don't rely on them receiving it. + + if (fSuspendInputCount > 0) + { + fQueuedCtrlMessages.push_back(pMsg); + pMsg->Ref(); + } + else + { + ControlEventCode moveCode = pMsg->GetControlCode(); + + hsBool flagChanged = false; + if(pMsg->ControlActivated()) + { + if (moveCode == B_CONTROL_TURN_TO && fClickToTurn) + { +#if 0 + // This will do an LOS and call TurnToPoint() + plSceneInputInterface::GetInstance()->RequestAvatarTurnToPointLOS(); +#else + plVisHit hit; + if( plVisLOSMgr::Instance()->CursorCheck(hit) ) + TurnToPoint(hit.fPos); +#endif + return true; + } + + // This control is intended for turning while walking/running. + // It is never net propagated. We just flag our physics state + // as dirty, and let the physics synch (with dead reckoning) handle it. + if (moveCode == A_CONTROL_TURN) + { + // Filter out the messages that are just the mouse recentering + if (pMsg->GetPct() < 0.4 && pMsg->GetPct() > -0.4) + { + fMouseFrameTurnStrength += pMsg->GetPct() * fMouseTurnSensitivity; + SynchIfLocal(hsTimer::GetSysSeconds(), false); + } + } + + if (!GetInputFlag( moveCode ) ) + { + SetInputFlag( moveCode, true ); + flagChanged = true; + + if (moveCode == B_CONTROL_JUMP) + SetInputFlag(B_CONTROL_CONSUMABLE_JUMP, true); + + if(plNetClientMgr::GetInstance()->AmCCR()) + { + // special case for clipping: synch every key change + SynchIfLocal( hsTimer::GetSysSeconds(), false); + } + } + } else { + if ( GetInputFlag( moveCode ) ) + { + SetInputFlag( moveCode, false ); + flagChanged = true; + + if (fReverseFBOnIdle && (moveCode == B_CONTROL_MOVE_FORWARD || moveCode == B_CONTROL_MOVE_BACKWARD)) + { + if (!GetInputFlag(B_CONTROL_MOVE_FORWARD) && !GetInputFlag(B_CONTROL_MOVE_BACKWARD)) + SetInputFlag(B_CONTROL_LADDER_INVERTED, true); + } + } + } + if (flagChanged && plAvatarInputStateMsg::IsCodeInMap(moveCode)) + SynchInputState(); + } + return true; +} + +void plArmatureMod::IHandleInputStateMsg(plAvatarInputStateMsg *msg) +{ + int i; + UInt32 curBit; + for (i = 0, curBit = 0x1; i < plAvatarInputStateMsg::fMapSize; i++, curBit <<= 1) + { + SetInputFlag(msg->fCodeMap[i], msg->fState & curBit); + } + +} + +void plArmatureMod::SynchInputState(UInt32 rcvID /* = kInvalidPlayerID */) +{ + if (plAvatarMgr::GetInstance()->GetLocalAvatar() != this) + return; + + plAvatarInputStateMsg *msg = TRACKED_NEW plAvatarInputStateMsg(); + int i; + UInt32 curBit; + for (i = 0, curBit = 0x1; i < plAvatarInputStateMsg::fMapSize; i++, curBit <<= 1) + { + if (GetInputFlag(msg->fCodeMap[i])) + msg->fState |= curBit; + } + msg->AddReceiver(GetKey()); + msg->SetBCastFlag(plMessage::kNetPropagate); + msg->SetBCastFlag(plMessage::kNetUseRelevanceRegions); + msg->SetBCastFlag(plMessage::kLocalPropagate, false); + msg->SetBCastFlag(plMessage::kNetSendUnreliable, true); + if (rcvID != kInvalidPlayerID) + msg->AddNetReceiver(rcvID); + + msg->Send(); + + fLastInputSynch = hsTimer::GetSysSeconds(); +} + +void plArmatureMod::ILinkToPersonalAge() +{ + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename( kPersonalAgeFilename ); + link.GetAgeInfo()->SetAgeInstanceName( kPersonalAgeInstanceName ); + + plSpawnPointInfo hutSpawnPoint; + hutSpawnPoint.SetName(kPersonalAgeLinkInPointCloset); + link.SetSpawnPoint(hutSpawnPoint); + + link.SetLinkingRules( plNetCommon::LinkingRules::kOriginalBook ); + plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( &link ); + pMsg->SetLinkInAnimName("PersonalBookEnter"); + pMsg->AddReceiver(nc->GetKey()); + pMsg->Send(); +} + +hsBool plArmatureMod::IEval(double time, hsScalar elapsed, UInt32 dirty) +{ + if (IsFinal()) + { + bool noOverlap = false; + + const plArmatureMod *localPlayer = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (plRelevanceMgr::Instance()->GetEnabled() && (localPlayer != nil)) + { + // (May decide to update this elsewhere instead.) + plRelevanceMgr::Instance()->SetRegionVectors(GetTarget(0)->GetLocalToWorld().GetTranslate(), fRegionsImIn, fRegionsICareAbout); + if (localPlayer != this) + { + if (!fRegionsImIn.Overlap(localPlayer->fRegionsICareAbout)) + { + noOverlap = true; + } + } + else // Bookkeeping for the local player... + { + hsBool update = false; + if (fOldRegionsICareAbout != fRegionsICareAbout) + { + update = true; + fOldRegionsICareAbout = fRegionsICareAbout; + } + if (fOldRegionsImIn != fRegionsImIn) + { + update = true; + fOldRegionsImIn = fRegionsImIn; + } + if (update) + { + // Send message to the server here. + plNetMsgRelevanceRegions relRegionsNetMsg; + relRegionsNetMsg.SetNetProtocol(kNetProtocolCli2Game); + relRegionsNetMsg.SetRegionsICareAbout(fRegionsICareAbout); + relRegionsNetMsg.SetRegionsImIn(fRegionsImIn); + plNetClientApp::GetInstance()->SendMsg(&relRegionsNetMsg); + } + } + } + + if (localPlayer == this) + { + if (time - fLastInputSynch > kAvatarInputSynchThreshold) + SynchInputState(); + } + + if (noOverlap) + { + EnablePhysics(false, kDisableReasonRelRegion); + EnableDrawing(false, kDisableReasonRelRegion); + } + else + { + EnablePhysics(true, kDisableReasonRelRegion); + EnableDrawing(true, kDisableReasonRelRegion); + } + + if (fMouseFrameTurnStrength == 0 && GetInputFlag(A_CONTROL_TURN)) + SetInputFlag(A_CONTROL_TURN, false); + + if (!fMidLink) + plArmatureModBase::IEval(time, elapsed, dirty); + + fUpdateMsg->Ref(); + fUpdateMsg->Send(); + + if (fPendingSynch) + NetworkSynch(time, false); + if (fDebugOn) + RefreshDebugDisplay(); + + fMouseFrameTurnStrength = 0.f; // Processed this frame. Clear it. + + // update our attached particle system if necessary + if (GetFollowerParticleSystemSO()) + { + plSceneObject* follower = GetFollowerParticleSystemSO(); + hsPoint3 trans = GetTarget(0)->GetLocalToWorld().GetTranslate() - follower->GetLocalToWorld().GetTranslate(); + if (trans.MagnitudeSquared() > 1) // we can be a bit fuzzy about this, since the particle system is rather large and people won't notice it being off + { + plWarpMsg *warp = TRACKED_NEW plWarpMsg(GetKey(), follower->GetKey(), plWarpMsg::kFlushTransform | plWarpMsg::kZeroVelocity, + GetTarget(0)->GetLocalToWorld()); + warp->Send(); + + plParticleSystem *sys = const_cast(plParticleSystem::ConvertNoRef(follower->GetModifierByType(plParticleSystem::Index()))); + if (sys) + { + sys->fMiscFlags |= plParticleSystem::kParticleSystemAlwaysUpdate; + sys->TranslateAllParticles(trans); + } + } + } + } + else + IFinalize(); + + return true; +} + +void plArmatureMod::AddTarget(plSceneObject* so) +{ + plArmatureModBase::AddTarget(so); + + plAvatarMgr::GetInstance()->AddAvatar(this); // register for easy lookup + plgDispatch::Dispatch()->RegisterForExactType(plAvatarMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectBCMsg::Index(), GetKey()); + + // all avatars will register for the age loaded message. + // only players need it, but we don't know that we're a player until it's too late to get it. + // non-players will unregister when they learn the truth. + if (IsLocallyOwned()) + plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); + + // attach a clothingSDLModifier to handle clothing saveState + delete fClothingSDLMod; + fClothingSDLMod = TRACKED_NEW plClothingSDLModifier; + fClothingSDLMod->SetClothingOutfit(GetClothingOutfit()); // ok if clothingOutfit is nil at this point + so->AddModifier(fClothingSDLMod); + + // add avatar sdl modifier + delete fAvatarSDLMod; + fAvatarSDLMod = TRACKED_NEW plAvatarSDLModifier; + so->AddModifier(fAvatarSDLMod); + + delete fAvatarPhysicalSDLMod; + fAvatarPhysicalSDLMod = TRACKED_NEW plAvatarPhysicalSDLModifier; + so->AddModifier(fAvatarPhysicalSDLMod); + + // At export time, this key will be nil. This is important, or else we'll overwrite the page the key comes from. + if (fFootSoundSOKey != nil) + hsgResMgr::ResMgr()->AddViaNotify(fFootSoundSOKey, TRACKED_NEW plAttachMsg(so->GetKey(), nil, plRefMsg::kOnRequest), plRefFlags::kActiveRef); + if (fLinkSoundSOKey != nil) + hsgResMgr::ResMgr()->AddViaNotify(fLinkSoundSOKey, TRACKED_NEW plAttachMsg(so->GetKey(), nil, plRefMsg::kOnRequest), plRefFlags::kActiveRef); + + if (fUpdateMsg) + fUpdateMsg->UnRef(); // delete an old one. + + fUpdateMsg = TRACKED_NEW plArmatureUpdateMsg(GetKey(), so->IsLocallyOwned(), true, this); +} + +void plArmatureMod::RemoveTarget(plSceneObject* so) +{ + if (so) + { + if (fClothingSDLMod) + so->RemoveModifier(fClothingSDLMod); + if (fAvatarSDLMod) + so->RemoveModifier(fAvatarSDLMod); + if (fAvatarPhysicalSDLMod) + so->RemoveModifier(fAvatarPhysicalSDLMod); + } + delete fClothingSDLMod; + fClothingSDLMod = nil; + delete fAvatarSDLMod; + fAvatarSDLMod = nil; + delete fAvatarPhysicalSDLMod; + fAvatarPhysicalSDLMod = nil; + + plArmatureModBase::RemoveTarget(so); +} + +void plArmatureMod::Write(hsStream *stream, hsResMgr *mgr) +{ + // Temporarily going around plArmatureModBase so that we don't + // break format + plAGMasterMod::Write(stream, mgr); + + mgr->WriteKey(stream, fMeshKeys[0]); + stream->WriteSafeString(fRootName); + int nBrains = fBrains.size(); + stream->WriteSwap32(nBrains); + for (int i = 0; i < nBrains; i++) + mgr->WriteCreatable(stream, fBrains[i]); + + if (fClothingOutfit == nil) + { + stream->WriteBool( false ); + } + else + { + stream->WriteBool( true ); + mgr->WriteKey(stream, fClothingOutfit->GetKey()); + } + + stream->WriteSwap32(fBodyType); + if( fEffects == nil ) + stream->WriteBool( false ); + else + { + stream->WriteBool( true ); + mgr->WriteKey(stream, fEffects->GetKey()); + } + + stream->WriteSwapFloat(fPhysHeight); + stream->WriteSwapFloat(fPhysWidth); + + stream->WriteSafeString(fAnimationPrefix.c_str()); + stream->WriteSafeString(fBodyAgeName.c_str()); + stream->WriteSafeString(fBodyFootstepSoundPage.c_str()); +} + +void plArmatureMod::Read(hsStream * stream, hsResMgr *mgr) +{ + // Temporarily going around plArmatureModBase so that we don't + // break format + plAGMasterMod::Read(stream, mgr); + + fMeshKeys.push_back(mgr->ReadKey(stream)); + + // read the root name string + fRootName = stream->ReadSafeString(); + + // read in the brains + int nBrains = stream->ReadSwap32(); + for (int i = 0; i < nBrains; i++) + { + plArmatureBrain * brain = (plArmatureBrain *)mgr->ReadCreatable(stream); + this->PushBrain(brain); + } + + if( stream->ReadBool() ) + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // plClothingBase + else + fClothingOutfit = nil; + + fBodyType = stream->ReadSwap32(); + + if( stream->ReadBool() ) + { + plKey effectMgrKey = mgr->ReadKey(stream); + mgr->AddViaNotify(effectMgrKey, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // plArmatureEffects + + // Attach the Footstep emitter scene object + hsResMgr *mgr = hsgResMgr::ResMgr(); + const char *age = fBodyAgeName.c_str(); + const char *page = fBodyFootstepSoundPage.c_str(); + const plLocation &gLoc = plKeyFinder::Instance().FindLocation(age, page); + + if (gLoc.IsValid()) + { + const plUoid &myUoid = GetKey()->GetUoid(); + plUoid SOUoid(gLoc, plSceneObject::Index(), "FootstepSoundObject"); + fFootSoundSOKey = mgr->FindKey(SOUoid); + if (fFootSoundSOKey) + { + // So it exists... but FindKey won't properly create our clone. So we do. + SOUoid.SetClone(myUoid.GetClonePlayerID(), myUoid.GetCloneID()); + fFootSoundSOKey = mgr->ReRegister(nil, SOUoid); + } + + // Add the effect to our effects manager + plUoid effectUoid(gLoc, plArmatureEffectFootSound::Index(), "FootstepSounds" ); + plKey effectKey = mgr->FindKey(effectUoid); + if (effectKey) + { + effectUoid.SetClone(myUoid.GetClonePlayerID(), myUoid.GetCloneID()); + effectKey = mgr->ReRegister(nil, effectUoid); + } + if (effectKey != nil) + mgr->AddViaNotify(effectKey, TRACKED_NEW plGenRefMsg(effectMgrKey, plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); + + // Get the linking sound + plUoid LinkUoid(gLoc, plSceneObject::Index(), "LinkSoundSource"); + fLinkSoundSOKey = mgr->FindKey(LinkUoid); + if (fLinkSoundSOKey) + { + LinkUoid.SetClone(myUoid.GetClonePlayerID(), myUoid.GetCloneID()); + fLinkSoundSOKey = mgr->ReRegister(nil, LinkUoid); + } + } + } + else + fEffects = nil; + + fPhysHeight = stream->ReadSwapFloat(); + fPhysWidth = stream->ReadSwapFloat(); + + char* temp = stream->ReadSafeString(); + fAnimationPrefix = temp; + delete [] temp; + + temp = stream->ReadSafeString(); + fBodyAgeName = temp; + delete [] temp; + + temp = stream->ReadSafeString(); + fBodyFootstepSoundPage = temp; + delete [] temp; + + plgDispatch::Dispatch()->RegisterForExactType(plAvatarStealthModeMsg::Index(), GetKey()); +} + +hsBool plArmatureMod::DirtySynchState(const char* SDLStateName, UInt32 synchFlags) +{ + // skip requests to synch non-avatar state + if (SDLStateName && stricmp(SDLStateName, kSDLAvatar)) + { + return false; + } + + synchFlags |= plSynchedObject::kForceFullSend; // TEMP + + if(GetNumTargets() > 0) + { + plSceneObject *sObj = GetTarget(0); + if(sObj) + sObj->DirtySynchState(SDLStateName, synchFlags); + } + return false; +} + +hsBool plArmatureMod::DirtyPhysicalSynchState(UInt32 synchFlags) +{ + synchFlags |= plSynchedObject::kForceFullSend; // TEMP + synchFlags |= plSynchedObject::kBCastToClients; + + if(GetNumTargets() > 0) + { + plSceneObject *sObj = GetTarget(0); + if(sObj) + sObj->DirtySynchState(kSDLAvatarPhysical, synchFlags); + } + return false; +} + +void plArmatureMod::IFinalize() +{ + plArmatureModBase::IFinalize(); + + if (fWaitFlags & kNeedAudio) + { + plSetListenerMsg *msg = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVelocity, GetTarget(0)->GetKey(), true ); + msg->Send(); + fWaitFlags &= ~kNeedAudio; + } + + if (fWaitFlags & kNeedCamera) + { + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetCmd(plCameraMsg::kCreateNewDefaultCam); + pMsg->SetCmd(plCameraMsg::kSetSubject); + pMsg->SetSubject(GetTarget(0)); + pMsg->SetBCastFlag( plMessage::kBCastByExactType ); + pMsg->Send(); + fWaitFlags &= ~kNeedCamera; + } + + if (fWaitFlags & kNeedSpawn) + { + Spawn(hsTimer::GetSysSeconds()); + fWaitFlags &= ~kNeedSpawn; + } +} + +void plArmatureMod::ICustomizeApplicator() +{ + plArmatureModBase::ICustomizeApplicator(); + + const plAGModifier *agMod = GetChannelMod("Bone_Root", true); + if (agMod) + { + // are there any applicators that manipulate the transform? + plAGApplicator *app = agMod->GetApplicator(kAGPinTransform); + if(app) + { + plMatrixDelayedCorrectionApplicator *corApp = plMatrixDelayedCorrectionApplicator::ConvertNoRef(app); + if (corApp) + { + fBoneRootAnimator = corApp; + fWaitFlags &= ~kNeedApplicator; + return; // already there + } + } + plAGModifier *volAGMod = const_cast(agMod); + fBoneRootAnimator = TRACKED_NEW plMatrixDelayedCorrectionApplicator(); + volAGMod->SetApplicator(fBoneRootAnimator); + fWaitFlags &= ~kNeedApplicator; + } +} + +const plSceneObject *plArmatureMod::GetClothingSO(UInt8 lod) const +{ + if (fClothToSOMap.GetCount() <= lod) + return nil; + + return fClothToSOMap[lod]; +} + +#define kSynchInterval 1 // synch once per second + +void plArmatureMod::NetworkSynch(double timeNow, int force) +{ + if (force || ((timeNow - fLastSynch) > kSynchInterval)) + { + // make sure state change gets sent out over the network + // avatar state should use relevance region filtering + UInt32 flags = kBCastToClients | kUseRelevanceRegions; + if (force) + flags |= kForceFullSend; + DirtyPhysicalSynchState(flags); + fLastSynch = timeNow; + fPendingSynch = false; + } + else + fPendingSynch = true; +} + +hsBool plArmatureMod::IsLocalAvatar() +{ + return plAvatarMgr::GetInstance()->GetLocalAvatar() == this; +} + +hsBool plArmatureMod::IsLocalAI() +{ + plAvBrainCritter* ai = plAvBrainCritter::ConvertNoRef(FindBrainByClass(plAvBrainCritter::Index())); + if (ai) + return ai->LocallyControlled(); + return false; // not an AI, obviously not local +} + +void plArmatureMod::SynchIfLocal(double timeNow, int force) +{ + if (IsLocalAvatar() || IsLocalAI()) + { + NetworkSynch(timeNow, force); + } +} + +plLayerLinkAnimation *plArmatureMod::IFindLayerLinkAnim() +{ + int i; + hsGMaterial *mat = fClothingOutfit->fMaterial; + if (!mat) + return nil; + + for (i = 0; i < mat->GetNumLayers(); i++) + { + plLayerInterface *li = mat->GetLayer(i); + while (li != nil) + { + plLayerLinkAnimation *anim = plLayerLinkAnimation::ConvertNoRef(li); + if (anim) + return anim; + + li = li->GetUnderLay(); + } + } + return nil; +} + +hsBool plArmatureMod::ValidatePhysics() +{ + if (!fTarget) + return false; + + if (!fController) + fController = plPhysicalControllerCore::Create(GetTarget(0)->GetKey(), fPhysHeight, fPhysWidth); + + if (fController) + { + if (GetTarget(0)->GetKey() == plNetClientApp::GetInstance()->GetLocalPlayerKey()) + { + // local avatars get added to a special LOS db just for them. + fController->SetLOSDB(plSimDefs::kLOSDBLocalAvatar); + } + else + { + // non-local avatars are in the same LOS db as clickables + fController->SetLOSDB(plSimDefs::kLOSDBUIItems); + } + + hsClearBits(fWaitFlags, kNeedPhysics); + return true; + } + else + { + return false; + } + +} + +hsBool plArmatureMod::ValidateMesh() +{ + if (fWaitFlags & kNeedMesh) + { + fWaitFlags &= ~kNeedMesh; + int n = fMeshKeys.size(); + + for(int i = 0; i < n; i++) + { + plKey meshKey = fMeshKeys[i]; + plSceneObject * meshObj = (plSceneObject *)meshKey->GetObjectPtr(); + + if( ! meshObj) + { + fWaitFlags |= kNeedMesh; + break; + } + hsBool visible = (i == fCurLOD) ? true : false; + + EnableDrawingTree(meshObj, visible); + + // If we haven't created the mapping yet... + if (fClothToSOMap.GetCount() <= i || fClothToSOMap[i] == nil) + { + plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, i, 0); + hsgResMgr::ResMgr()->SendRef(meshObj->GetKey(), refMsg, plRefFlags::kPassiveRef); + } + } + if (!strcmp(GetTarget(0)->GetKeyName(), "Yeesha")) + ISetTransparentDrawOrder(true); + else + ISetTransparentDrawOrder(false); + } + + return !(fWaitFlags & kNeedMesh); +} + +plArmatureBrain * plArmatureMod::GetNextBrain(plArmatureBrain *brain) +{ + plArmatureBrain * result = nil; + bool passedTarget = false; + + int count = fBrains.size(); + for(int i = count - 1; i >= 0; i--) + { + plArmatureBrain *curBrain = fBrains.at(i); + if(passedTarget) + result = curBrain; + else { + if(curBrain == brain) + passedTarget = true; + } + } + + return result; +} + +int plArmatureMod::GetBrainCount() +{ + return fBrains.size(); +} + +plArmatureBrain * plArmatureMod::FindBrainByClass(UInt32 classID) const +{ + int n = fBrains.size(); + + for(int i = 0; i < n; i++) + { + plArmatureBrain *brain = fBrains.at(i); + + if(brain->ClassIndex() == classID) + { + return brain; + } + } + return nil; +} + +void plArmatureMod::TurnToPoint(hsPoint3 &point) +{ + plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(FindBrainByClass(plAvBrainHuman::Index())); + if (brain) + brain->TurnToPoint(point); +} + +void plArmatureMod::SuspendInput() +{ + if (fSuspendInputCount == 0 && plNetClientApp::GetInstance()->GetLocalPlayer() == GetTarget(0)) + { + plAvatarInputInterface::GetInstance()->SuspendMouseMovement(); + PreserveInputState(); + ClearInputFlags(false, false); + } + + fSuspendInputCount++; +} + +void plArmatureMod::ResumeInput() +{ + if (fSuspendInputCount > 0) // decrementing an unsigned variable set to 0 is BAD + fSuspendInputCount--; + if(fSuspendInputCount == 0) + { + RestoreInputState(); + IProcessQueuedInput(); + if (plNetClientApp::GetInstance()->GetLocalPlayer() == GetTarget(0)) + plAvatarInputInterface::GetInstance()->EnableMouseMovement(); + } +} + +void plArmatureMod::IProcessQueuedInput() +{ + CtrlMessageVec::iterator i = fQueuedCtrlMessages.begin(); + CtrlMessageVec::iterator end = fQueuedCtrlMessages.end(); + while(i != end) + { + plControlEventMsg *ctrlMsg = *i; + IHandleControlMsg(ctrlMsg); + ctrlMsg->UnRef(); + (*i) = nil; + i++; + } + fQueuedCtrlMessages.clear(); +} + +void plArmatureMod::PreserveInputState() +{ + fMoveFlagsBackup = fMoveFlags; +} + +void plArmatureMod::RestoreInputState() +{ + fMoveFlags = fMoveFlagsBackup; + +} + +hsBool plArmatureMod::GetInputFlag (int which) const +{ + return fMoveFlags.IsBitSet(which); +} + +void plArmatureMod::SetInputFlag(int which, hsBool status) +{ + if(status) + { + fMoveFlags.SetBit(which); + } else { + fMoveFlags.ClearBit(which); + } +} + +hsBool plArmatureMod::HasMovementFlag() const +{ + return (fMoveFlags.IsBitSet(B_CONTROL_MOVE_FORWARD) || + fMoveFlags.IsBitSet(B_CONTROL_MOVE_BACKWARD) || + fMoveFlags.IsBitSet(B_CONTROL_ROTATE_LEFT) || + fMoveFlags.IsBitSet(B_CONTROL_ROTATE_RIGHT) || + fMoveFlags.IsBitSet(A_CONTROL_TURN) || + fMoveFlags.IsBitSet(B_CONTROL_STRAFE_LEFT) || + fMoveFlags.IsBitSet(B_CONTROL_STRAFE_RIGHT)); +} + +bool plArmatureMod::ForwardKeyDown() const +{ + return GetInputFlag(B_CONTROL_MOVE_FORWARD) ? true : false; +} + +bool plArmatureMod::BackwardKeyDown() const +{ + return GetInputFlag(B_CONTROL_MOVE_BACKWARD) ? true : false; +} + +bool plArmatureMod::StrafeLeftKeyDown() const +{ + return GetInputFlag(B_CONTROL_STRAFE_LEFT) ? true : false; +} + +bool plArmatureMod::StrafeRightKeyDown() const +{ + return GetInputFlag(B_CONTROL_STRAFE_RIGHT) ? true : false; +} + +bool plArmatureMod::FastKeyDown() const +{ + return ((GetInputFlag(B_CONTROL_MODIFIER_FAST) && !GetInputFlag(B_CONTROL_ALWAYS_RUN)) || + (!GetInputFlag(B_CONTROL_MODIFIER_FAST) && GetInputFlag(B_CONTROL_ALWAYS_RUN))); +} + +bool plArmatureMod::StrafeKeyDown() const +{ + return GetInputFlag(B_CONTROL_MODIFIER_STRAFE) ? true : false; +} + +bool plArmatureMod::TurnLeftKeyDown() const +{ + return GetInputFlag(B_CONTROL_ROTATE_LEFT) ? true : false; +} + +bool plArmatureMod::TurnRightKeyDown() const +{ + return GetInputFlag(B_CONTROL_ROTATE_RIGHT) ? true : false; +} + +bool plArmatureMod::JumpKeyDown() const +{ + return GetInputFlag(B_CONTROL_JUMP) ? true : false; +} + +bool plArmatureMod::ExitModeKeyDown() const +{ + return GetInputFlag(B_CONTROL_EXIT_MODE) ? true : false; +} + +void plArmatureMod::SetForwardKeyDown() +{ + SetInputFlag(B_CONTROL_MOVE_FORWARD, true); +} + +void plArmatureMod::SetBackwardKeyDown() +{ + SetInputFlag(B_CONTROL_MOVE_BACKWARD, true); +} + +void plArmatureMod::SetStrafeLeftKeyDown(bool status) +{ + SetInputFlag(B_CONTROL_STRAFE_LEFT, status); +} + +void plArmatureMod::SetStrafeRightKeyDown(bool status) +{ + SetInputFlag(B_CONTROL_STRAFE_RIGHT, status); +} + +void plArmatureMod::SetFastKeyDown() +{ + SetInputFlag(B_CONTROL_MODIFIER_FAST, true); +} + +void plArmatureMod::SetTurnLeftKeyDown(bool status) +{ + SetInputFlag(B_CONTROL_ROTATE_LEFT, status); +} + +void plArmatureMod::SetTurnRightKeyDown(bool status) +{ + SetInputFlag(B_CONTROL_ROTATE_RIGHT, status); +} + +void plArmatureMod::SetJumpKeyDown() +{ + SetInputFlag(B_CONTROL_JUMP, true); +} + +hsScalar plArmatureMod::GetTurnStrength() const +{ + return GetKeyTurnStrength() + GetAnalogTurnStrength(); +} + +hsScalar plArmatureMod::GetKeyTurnStrength() const +{ + if (StrafeKeyDown()) + return 0.f; + + return (TurnLeftKeyDown() ? 1.f : 0.f) + (TurnRightKeyDown() ? -1.f: 0.f); +} + +hsScalar plArmatureMod::GetAnalogTurnStrength() const +{ + if (StrafeKeyDown()) + return 0.f; + + hsScalar elapsed = hsTimer::GetDelSysSeconds(); + if (elapsed > 0) + return fMouseFrameTurnStrength / elapsed; + else + return 0; +} + +void plArmatureMod::SetReverseFBOnIdle(bool val) +{ + fReverseFBOnIdle = val; + if (val) + SetInputFlag(B_CONTROL_LADDER_INVERTED, !(ForwardKeyDown() || BackwardKeyDown())); + else + SetInputFlag(B_CONTROL_LADDER_INVERTED, false); +} + +hsBool plArmatureMod::IsFBReversed() +{ + return GetInputFlag(B_CONTROL_LADDER_INVERTED); +} + +void plArmatureMod::ClearInputFlags(bool saveAlwaysRun, bool clearBackup) +{ + bool alwaysRun = (fMoveFlags.IsBitSet(B_CONTROL_ALWAYS_RUN) != 0); + bool fast = (fMoveFlags.IsBitSet(B_CONTROL_MODIFIER_FAST) != 0); + fMoveFlags.Clear(); + + if (saveAlwaysRun) + { + if (alwaysRun) + fMoveFlags.SetBit(B_CONTROL_ALWAYS_RUN); + if (fast) + fMoveFlags.SetBit(B_CONTROL_MODIFIER_FAST); + } + + if (clearBackup) + { + alwaysRun = (fMoveFlagsBackup.IsBitSet(B_CONTROL_ALWAYS_RUN) != 0); + fast = (fMoveFlagsBackup.IsBitSet(B_CONTROL_MODIFIER_FAST) != 0); + fMoveFlagsBackup.Clear(); + + if (saveAlwaysRun) + { + if (alwaysRun) + fMoveFlagsBackup.SetBit(B_CONTROL_ALWAYS_RUN); + if (fast) + fMoveFlagsBackup.SetBit(B_CONTROL_MODIFIER_FAST); + } + } +} + +plAGModifier * plArmatureMod::GetRootAGMod() +{ + if(fRootAGMod) + return fRootAGMod; + + if(fTarget) + { + const plAGModifier * shit = plAGModifier::ConvertNoRef(FindModifierByClass(fTarget, plAGModifier::Index())); + fRootAGMod = const_cast(shit); + } + + return fRootAGMod; +} + +int plArmatureMod::GetCurrentGenericType() +{ + plAvBrainGeneric *brain = plAvBrainGeneric::ConvertNoRef(GetCurrentBrain()); + + if (!brain) + return plAvBrainGeneric::kNonGeneric; + else + return brain->GetType(); +} + +bool plArmatureMod::FindMatchingGenericBrain(const char *names[], int count) +{ + int i; + for (i = 0; i < GetBrainCount(); i++) + { + plAvBrainGeneric *brain = plAvBrainGeneric::ConvertNoRef(GetBrain(i)); + if (brain && brain->MatchAnimNames(names, count)) + return true; + } + return false; +} + +char *plArmatureMod::MakeAnimationName(const char *baseName) const +{ + std::string temp = fAnimationPrefix + baseName; + char *result = hsStrcpy(temp.c_str()); // why they want a new string I'll never know... but hey, too late to change it now + return result; +} + +char *plArmatureMod::GetRootName() +{ + return fRootName; +} + +void plArmatureMod::SetRootName(const char *name) +{ + delete [] fRootName; + fRootName = hsStrcpy(name); +} + +plAGAnim *plArmatureMod::FindCustomAnim(const char *baseName) const +{ + char *customName = MakeAnimationName(baseName); + plAGAnim *result = plAGAnim::FindAnim(customName); + delete[] customName; + return result; +} + +void plArmatureMod::ISetupMarkerCallbacks(plATCAnim *anim, plAnimTimeConvert *atc) +{ + std::vector markers; + anim->CopyMarkerNames(markers); + + int i; + for (i = 0; i < markers.size(); i++) + { + + hsScalar time = -1; + hsBool isLeft = false; + if (strstr(markers[i], "SndLeftFootDown") == markers[i]) + { + isLeft = true; + time = anim->GetMarker(markers[i]); + } + if (strstr(markers[i], "SndRightFootDown") == markers[i]) + time = anim->GetMarker(markers[i]); + + if (time >= 0) + { + plEventCallbackInterceptMsg *iMsg; + + plArmatureEffectMsg *msg = TRACKED_NEW plArmatureEffectMsg(fEffects->GetKey(), kTime); + msg->fEventTime = time; + msg->fTriggerIdx = AnimNameToIndex(anim->GetName()); + + iMsg = TRACKED_NEW plEventCallbackInterceptMsg(); + iMsg->AddReceiver(fEffects->GetKey()); + iMsg->fEventTime = time; + iMsg->fEvent = kTime; + iMsg->SetMessage(msg); + atc->AddCallback(iMsg); + hsRefCnt_SafeUnRef(msg); + hsRefCnt_SafeUnRef(iMsg); + + plAvatarFootMsg* foot = TRACKED_NEW plAvatarFootMsg(GetKey(), this, isLeft); + foot->fEventTime = time; + + iMsg = TRACKED_NEW plEventCallbackInterceptMsg(); + iMsg->AddReceiver(fEffects->GetKey()); + iMsg->fEventTime = time; + iMsg->fEvent = kTime; + iMsg->SetMessage(foot); + atc->AddCallback(iMsg); + hsRefCnt_SafeUnRef(foot); + hsRefCnt_SafeUnRef(iMsg); + } + + delete [] markers[i]; // done with this string, nuke it + } +} + +const char *plArmatureMod::GetAnimRootName(const char *name) +{ + return name + fAnimationPrefix.length(); +} + +Int8 plArmatureMod::AnimNameToIndex(const char *name) +{ + const char *rootName = GetAnimRootName(name); + Int8 result = -1; + + if (!strcmp(rootName, "Walk") || !strcmp(rootName, "WalkBack") || + !strcmp(rootName, "LadderDown") || !strcmp(rootName, "LadderDownOn") || + !strcmp(rootName, "LadderDownOff") || !strcmp(rootName, "LadderUp") || + !strcmp(rootName, "LadderUpOn") || !strcmp(rootName, "LadderUpOff") || + !strcmp(rootName, "SwimSlow") || !strcmp(rootName, "SwimBackward") || + !strcmp(rootName, "BallPushWalk")) + result = kWalk; + else if (!strcmp(rootName, "Run") || !strcmp(rootName, "SwimFast")) + result = kRun; + else if (!strcmp(rootName, "TurnLeft") || !strcmp(rootName, "TurnRight") || + !strcmp(rootName, "StepLeft") || !strcmp(rootName, "StepRight") || + !strcmp(rootName, "SideSwimLeft") || !strcmp(rootName, "SideSwimRight") || + !strcmp(rootName, "TreadWaterTurnLeft") || !strcmp(rootName, "TreadWaterTurnRight")) + result = kTurn; + else if (!strcmp(rootName, "GroundImpact") || !strcmp(rootName, "RunningImpact")) + result = kImpact; + else if (strstr(rootName, "Run")) // Critters + result = kRun; + else if (strstr(rootName, "Idle")) // Critters + result = kWalk; + + return result; +} + +hsBool plArmatureMod::IsInStealthMode() const +{ + return (fStealthMode != plAvatarStealthModeMsg::kStealthVisible); +} + +bool plArmatureMod::IsOpaque() +{ + return fOpaque; +} + +bool plArmatureMod::IsMidLink() +{ + return fMidLink; +} + +hsBool plArmatureMod::ConsumeJump() +{ + if (!GetInputFlag(B_CONTROL_CONSUMABLE_JUMP)) + return false; + + SetInputFlag(B_CONTROL_CONSUMABLE_JUMP, false); + return true; +} + +void plArmatureMod::ISetTransparentDrawOrder(bool val) +{ + if (fOpaque != val) + return; + + fOpaque = !val; + + plDrawableSpans *spans = plDrawableSpans::ConvertNoRef(FindDrawable()); + if (spans) + { + spans->SetNativeProperty(plDrawable::kPropPartialSort, true); + spans->SetNativeProperty(plDrawable::kPropSortAsOne, true); + if (val) + { + spans->SetNativeProperty(plDrawable::kPropSortSpans, true); + spans->SetRenderLevel(plRenderLevel(plRenderLevel::kBlendRendMajorLevel, plRenderLevel::kDefRendMinorLevel)); + } + else + { + spans->SetNativeProperty(plDrawable::kPropSortSpans, false); + spans->SetRenderLevel(plRenderLevel(0, plRenderLevel::kAvatarRendMinorLevel)); + } + } +} + +bool plArmatureMod::IsKILowestLevel() +{ + if ( GetKILevel() == pfKIMsg::kNanoKI ) + return true; + else + return false; +} + +int plArmatureMod::GetKILevel() +{ + return VaultGetKILevel(); +} + +void plArmatureMod::SetLinkInAnim(const char *animName) +{ + if (animName) + { + plAGAnim *anim = FindCustomAnim(animName); + fLinkInAnimKey = (anim ? anim->GetKey() : nil); + } + else + fLinkInAnimKey = nil; +} + +plKey plArmatureMod::GetLinkInAnimKey() const +{ + return fLinkInAnimKey; +} + + +////////////////////// +// +// PLARMATURELODMOD +// +////////////////////// + +plArmatureLODMod::plArmatureLODMod() +{ +} + +// CTOR (physical, name) +plArmatureLODMod::plArmatureLODMod(const char* root_name) +: plArmatureMod() +{ + fRootName = hsStrcpy(root_name); +} + +plArmatureLODMod::~plArmatureLODMod() +{ +} + +void plArmatureLODMod::Read(hsStream *stream, hsResMgr *mgr) +{ + plArmatureMod::Read(stream, mgr); + + fMeshKeys.clear(); + int meshKeyCount = stream->ReadSwap32(); + for(int i = 0; i < meshKeyCount; i++) + { + plKey meshKey = mgr->ReadKey(stream); + fMeshKeys.push_back(meshKey); + + plKeyVector *vec = TRACKED_NEW plKeyVector; + int boneCount = stream->ReadSwap32(); + for(int j = 0; j < boneCount; j++) + vec->push_back(mgr->ReadKey(stream)); + fUnusedBones.push_back(vec); + } +} + +// WRITE +void plArmatureLODMod::Write(hsStream *stream, hsResMgr *mgr) +{ + plArmatureMod::Write(stream, mgr); + + int meshKeyCount = fMeshKeys.size(); + stream->WriteSwap32(meshKeyCount); + + for(int i = 0; i < meshKeyCount; i++) + { + plKey meshKey = fMeshKeys[i]; + mgr->WriteKey(stream, meshKey); + + // Should be a list per mesh key + stream->WriteSwap32(fUnusedBones[i]->size()); + for(int j = 0; j < fUnusedBones[i]->size(); j++) + mgr->WriteKey(stream, (*fUnusedBones[i])[j]); + } +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +// DEBUG SUPPORT + +int plArmatureMod::RefreshDebugDisplay() +{ + plDebugText &debugTxt = plDebugText::Instance(); + char strBuf[ 2048 ]; + int lineHeight = debugTxt.GetFontSize() + 4; + UInt32 scrnWidth, scrnHeight; + + debugTxt.GetScreenSize( &scrnWidth, &scrnHeight ); + int y = 10; + int x = 10; + + DumpToDebugDisplay(x, y, lineHeight, strBuf, debugTxt); + return y; +} + +void plArmatureMod::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + sprintf(strBuf, "Armature <%s>:", fRootName); + debugTxt.DrawString(x, y, strBuf, 255, 128, 128); + y += lineHeight; + + plSceneObject * SO = GetTarget(0); + if(SO) + { + // global location + hsMatrix44 l2w = SO->GetLocalToWorld(); + hsPoint3 worldPos = l2w.GetTranslate(); + + char *opaque = IsOpaque() ? "yes" : "no"; + sprintf(strBuf, "position(world): %.2f, %.2f, %.2f Opaque: %3s", + worldPos.fX, worldPos.fY, worldPos.fZ, opaque); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + + hsPoint3 kPos; + char *kinematic = "n.a."; + const char* frozen = "n.a."; + if (fController) + { + fController->GetKinematicPosition(kPos); + kinematic = fController->IsKinematic() ? "on" : "off"; + } + sprintf(strBuf, "kinematc(world): %.2f, %.2f, %.2f Kinematic: %3s", + kPos.fX, kPos.fY, kPos.fZ,kinematic); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + + if (fController) + frozen = fController->IsEnabled() ? "no" : "yes"; + + // are we in a subworld? + plKey world = nil; + if (fController) + world = fController->GetSubworld(); + sprintf(strBuf, "In world: %s Frozen: %s", world ? world->GetName() : "nil", frozen); + debugTxt.DrawString(x,y, strBuf); + y+= lineHeight; + + if (fController) + { + hsPoint3 physPos; + GetPositionAndRotationSim(&physPos, nil); + const hsVector3& vel = fController->GetLinearVelocity(); + sprintf(strBuf, "position(physical): <%.2f, %.2f, %.2f> velocity: <%5.2f, %5.2f, %5.2f>", physPos.fX, physPos.fY, physPos.fZ, vel.fX, vel.fY, vel.fZ); + } + else + { + sprintf(strBuf, "position(physical): no controller"); + } + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + } + + DebugDumpMoveKeys(x, y, lineHeight, strBuf, debugTxt); + + int i; + for(i = 0; i < fBrains.size(); i++) + { + plArmatureBrain *brain = fBrains[i]; + brain->DumpToDebugDisplay(x, y, lineHeight, strBuf, debugTxt); + } + + if (fClothingOutfit) + { + y += lineHeight; + + debugTxt.DrawString(x, y, "ItemsWorn:"); + y += lineHeight; + strBuf[0] = '\0'; + int itemCount = 0; + for (i = 0; i < fClothingOutfit->fItems.GetCount(); i++) + { + if (itemCount == 0) + strcat(strBuf, " "); + + strcat(strBuf, fClothingOutfit->fItems[i]->fName); + itemCount++; + + if (itemCount == 4) + { + debugTxt.DrawString(x, y, strBuf); + itemCount = 0; + strBuf[0] = '\0'; + y += lineHeight; + } + + if (itemCount > 0) + strcat(strBuf, ", "); + } + if (itemCount > 0) + { + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + } + } + + if (plRelevanceMgr::Instance()->GetEnabled()) + { + y += lineHeight; + + debugTxt.DrawString(x, y, "Relevance Regions:"); + y += lineHeight; + sprintf(strBuf, " In: %s", plRelevanceMgr::Instance()->GetRegionNames(fRegionsImIn).c_str()); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + sprintf(strBuf, " Care about: %s", plRelevanceMgr::Instance()->GetRegionNames(fRegionsICareAbout).c_str()); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + } +} + +class plAvBoneMap::BoneMapImp +{ +public: + typedef std::map id2SceneObjectMap; + id2SceneObjectMap fMap; +}; + +plAvBoneMap::plAvBoneMap() +{ + fImp = TRACKED_NEW BoneMapImp; +} + +plAvBoneMap::~plAvBoneMap() +{ + delete fImp; +} + +const plSceneObject * plAvBoneMap::FindBone(UInt32 boneID) +{ + BoneMapImp::id2SceneObjectMap::iterator i = fImp->fMap.find(boneID); + const plSceneObject *result = nil; + + if(i != fImp->fMap.end()) + { + result = (*i).second; + } + return result; +} + +void plAvBoneMap::AddBoneMapping(UInt32 boneID, const plSceneObject *SO) +{ + (fImp->fMap)[boneID] = SO; +} + +void plArmatureMod::DebugDumpMoveKeys(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + char buff[256]; + sprintf(buff, "Mouse Input Map: %s", plAvatarInputInterface::GetInstance()->GetInputMapName()); + debugTxt.DrawString(x, y, buff); + y += lineHeight; + + sprintf(buff, "Turn strength: %.2f (key: %.2f, analog: %.2f)", GetTurnStrength(), GetKeyTurnStrength(), GetAnalogTurnStrength()); + debugTxt.DrawString(x, y, buff); + y += lineHeight; + + GetMoveKeyString(buff); + debugTxt.DrawString(x, y, buff); + y += lineHeight; +} + +void plArmatureMod::GetMoveKeyString(char *buff) +{ + sprintf(buff, "Move keys: "); + + if(FastKeyDown()) + strcat(buff, "FAST "); + if(StrafeKeyDown()) + strcat(buff, "STRAFE "); + if(ForwardKeyDown()) + strcat(buff, "FORWARD "); + if(BackwardKeyDown()) + strcat(buff, "BACKWARD "); + if(TurnLeftKeyDown()) + strcat(buff, "TURNLEFT "); + if(TurnRightKeyDown()) + strcat(buff, "TURNRIGHT "); + if(StrafeLeftKeyDown()) + strcat(buff, "STRAFELEFT "); + if(StrafeRightKeyDown()) + strcat(buff, "STRAFERIGHT "); + if(JumpKeyDown()) + strcat(buff, "JUMP "); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h new file mode 100644 index 00000000..f0416769 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h @@ -0,0 +1,469 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/** \file plArmatureMod.h + A modifier which manages multi-channel animation and has a physical body. + Designed for avatars; also good for vehicles, creatures, etc. +*/ + +// PLARMATUREMOD +// +// An armature is an object with both a physical presence (physics behavior) and articulated animated parts. +// (The parts are not themselves physical) +// An avatar is a type of armature, as is a critter, and anything else that moves around. +// +// This modifier combines multi-channel animation with blending (inherited from plAGMasterMod) +// with convenience functions for moving a physical body around and some specialized animation support + +#ifndef plArmatureMod_inc +#define plArmatureMod_inc + +#include "plAGMasterMod.h" + +// other local +#include "plAvDefs.h" + +#include "../pnSceneObject/plSimulationInterface.h" + +#include "hsMatrix44.h" +#include "../plNetCommon/plNetCommon.h" + +#include + +///////////////////////////////////////////////////////////////////////////////////////// +// +// FORWARDS +// +///////////////////////////////////////////////////////////////////////////////////////// + +class hsQuat; +class plMatrixChannel; +class plAGModifier; +class plAGMasterMod; +class plAGChannel; +class plClothingOutfit; +class plClothingSDLModifier; +class plAvatarSDLModifier; +class plMatrixDelayedCorrectionApplicator; +class plMatrixDifferenceApp; +class plDebugText; +class plArmatureEffectsMgr; +class plAvBoneMap; // below +class plDrawable; +class plControlEventMsg; +class plAvatarInputStateMsg; +class plLayerLinkAnimation; +class plPipeline; +class plArmatureBrain; +class plArmatureUpdateMsg; +class plPhysicalControllerCore; + +typedef std::vector plKeyVector; +typedef std::vector plBrainStack; + +class plArmatureModBase : public plAGMasterMod +{ +public: + plArmatureModBase(); + virtual ~plArmatureModBase(); + + CLASSNAME_REGISTER( plArmatureModBase ); + GETINTERFACE_ANY( plArmatureModBase, plAGMasterMod ); + + virtual hsBool MsgReceive(plMessage* msg); + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + plMatrixDifferenceApp *GetRootAnimator() { return fRootAnimator; } + plPhysicalControllerCore* GetController() const { return fController; } + plKey GetWorldKey() const; + virtual hsBool ValidatePhysics(); + virtual hsBool ValidateMesh(); + virtual void PushBrain(plArmatureBrain *brain); + virtual void PopBrain(); + plArmatureBrain *GetCurrentBrain() const; + plDrawable *FindDrawable() const; + virtual void LeaveAge(); + virtual hsBool IsFinal(); + + // LOD stuff + void AdjustLOD(); // see if we need to switch to a different resolution + hsBool SetLOD(int newLOD); // switch to a different resolution + void RefreshTree(); // Resend an LOD update to all our nodes (for when geometry changes) + int AppendMeshKey(plKey meshKey); + int AppendBoneVec(plKeyVector *boneVec); + UInt8 GetNumLOD() const; + + // A collection of reasons (flags) that things might be disabled. When all flags are gone + // The object is re-enabled. + enum + { + kDisableReasonUnknown = 0x0001, + kDisableReasonRelRegion = 0x0002, + kDisableReasonLinking = 0x0004, + kDisableReasonCCR = 0x0008, + kDisableReasonVehicle = 0x0010, + kDisableReasonGenericBrain = 0x0020, + }; + void EnablePhysics(hsBool status, UInt16 reason = kDisableReasonUnknown); + void EnablePhysicsKinematic(hsBool status); + void EnableDrawing(hsBool status, UInt16 reason = kDisableReasonUnknown); + hsBool IsPhysicsEnabled() { return fDisabledPhysics == 0; } + hsBool IsDrawEnabled() { return fDisabledDraw == 0; } + + + static void AddressMessageToDescendants(const plCoordinateInterface * CI, plMessage *msg); + static void EnableDrawingTree(const plSceneObject *object, hsBool status); + + static int fMinLOD; // throttle for lowest-indexed LOD + static double fLODDistance; // Distance for first LOD switch 2nd is 2x this distance (for now) + +protected: + virtual void IFinalize(); + virtual void ICustomizeApplicator(); + void IEnableBones(int lod, hsBool enable); + + // Some of these flags are only needed by derived classes, but I just want + // the one waitFlags variable. + enum + { + kNeedMesh = 0x01, + kNeedPhysics = 0x02, + kNeedAudio = 0x04, + kNeedCamera = 0x08, + kNeedSpawn = 0x10, + kNeedApplicator = 0x20, + kNeedBrainActivation = 0x40, + }; + UInt16 fWaitFlags; + + int fCurLOD; + plPhysicalControllerCore* fController; + plKeyVector fMeshKeys; + plBrainStack fBrains; + plMatrixDifferenceApp *fRootAnimator; + std::vector fUnusedBones; + UInt16 fDisabledPhysics; + UInt16 fDisabledDraw; +}; + +class plArmatureMod : public plArmatureModBase +{ + friend class plHBehavior; + friend class plAvatarSDLModifier; + friend class plAvatarPhysicalSDLModifier; + friend class plClothingSDLModifier; + friend class plAvOneShotLinkTask; + +public: + plArmatureMod(); + virtual ~plArmatureMod(); + + CLASSNAME_REGISTER( plArmatureMod ); + GETINTERFACE_ANY( plArmatureMod, plArmatureModBase ); + + virtual hsBool MsgReceive(plMessage* msg); + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + virtual hsBool ValidatePhysics(); + virtual hsBool ValidateMesh(); + + // Get or set the position of the avatar in simulation space. Set any + // arguments you don't care about to nil. + void SetPositionAndRotationSim(const hsPoint3* position, const hsQuat* rotation); + void GetPositionAndRotationSim(hsPoint3* position, hsQuat* rotation); + + hsBool IsLocalAvatar(); + hsBool IsLocalAI(); + virtual const plSceneObject *FindBone(const char * name) const; + virtual const plSceneObject *FindBone(UInt32 id) const; // use an id from an appropriate taxonomy, such as plAvBrainHuman::BoneID + virtual void AddBoneMapping(UInt32 id, const plSceneObject *bone); + plAGModifier *GetRootAGMod(); + plAGAnim *FindCustomAnim(const char *baseName) const; + + virtual void Spawn(double timeNow); + virtual void SpawnAt(int which, double timeNow); + virtual void EnterAge(hsBool reSpawn); + virtual void LeaveAge(); + virtual void PanicLink(hsBool playLinkOutAnim = true); + virtual void PersonalLink(); + + virtual bool ToggleDontPanicLinkFlag() { fDontPanicLink = fDontPanicLink ? false : true; return fDontPanicLink; } + + int GetBrainCount(); + plArmatureBrain *GetNextBrain(plArmatureBrain *brain); + plArmatureBrain *GetBrain(int index) { if(index <= fBrains.size()) return fBrains.at(index); else return nil; } + plArmatureBrain *FindBrainByClass(UInt32 classID) const; + + void TurnToPoint(hsPoint3 &point); + void SuspendInput(); + void ResumeInput(); + + UInt8 IsInputSuspended() { return fSuspendInputCount; } + void IProcessQueuedInput(); + void PreserveInputState(); + void RestoreInputState(); + hsBool GetInputFlag(int f) const; + void SetInputFlag(int which, hsBool status); + void ClearInputFlags(bool saveAlwaysRun, bool clearBackup); + hsBool HasMovementFlag() const; // Is any *movement* input flag on? + hsScalar GetTurnStrength() const; + hsScalar GetKeyTurnStrength() const; + hsScalar GetAnalogTurnStrength() const; + void SetReverseFBOnIdle(bool val); + hsBool IsFBReversed(); + + bool ForwardKeyDown() const; + bool BackwardKeyDown() const; + bool StrafeLeftKeyDown() const; + bool StrafeRightKeyDown() const; + bool StrafeKeyDown() const; + bool FastKeyDown() const; + bool TurnLeftKeyDown() const; + bool TurnRightKeyDown() const; + bool JumpKeyDown() const; + bool ExitModeKeyDown() const; + void SetForwardKeyDown(); + void SetBackwardKeyDown(); + void SetStrafeLeftKeyDown(bool on = true); + void SetStrafeRightKeyDown(bool on = true); + void SetFastKeyDown(); + void SetTurnLeftKeyDown(bool status = true); + void SetTurnRightKeyDown(bool status = true); + void SetJumpKeyDown(); + void DebugDumpMoveKeys(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); + void GetMoveKeyString(char *buff); + + void SynchIfLocal(double timeNow, int force); // Just physical state + void SynchInputState(UInt32 rcvID = kInvalidPlayerID); + hsBool DirtySynchState(const char* SDLStateName, UInt32 synchFlags ); + hsBool DirtyPhysicalSynchState(UInt32 synchFlags); + plClothingOutfit *GetClothingOutfit() const { return fClothingOutfit; } + plClothingSDLModifier *GetClothingSDLMod() const { return fClothingSDLMod; } + const plSceneObject *GetClothingSO(UInt8 lod) const; + plArmatureEffectsMgr *GetArmatureEffects() const { return fEffects; } + + enum + { + kWalk, + kRun, + kTurn, + kImpact, + kSwim, + }; + + const char *plArmatureMod::GetAnimRootName(const char *name); + Int8 AnimNameToIndex(const char *name); + void SetBodyType(int type) { fBodyType = type; } + int GetBodyType(int type) { return fBodyType; } + int GetCurrentGenericType(); + bool FindMatchingGenericBrain(const char *names[], int count); + char *MakeAnimationName(const char * baseName) const; + char *GetRootName(); + void SetRootName(const char *name); + + int RefreshDebugDisplay(); + void DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); + void SetDebugState(hsBool state) { fDebugOn = (state != 0); } + bool GetDebugState() { return fDebugOn; } + + virtual void RefreshTree() {} + hsBool IsInStealthMode() const; + int GetStealthLevel() const { return fStealthLevel; } + + bool IsOpaque(); + bool IsMidLink(); + hsBool ConsumeJump(); // returns true if the jump keypress was available to consume + + void SendBehaviorNotify(UInt32 type, hsBool start = true) { IFireBehaviorNotify(type,start); } + // Discovered a bug which makes these values horribly out of scale. So we do the rescale + // in the Get/Set functions for backwards compatability. + static void SetMouseTurnSensitivity(hsScalar val) { fMouseTurnSensitivity = val / 150.f; } + static hsScalar GetMouseTurnSensitivity() { return fMouseTurnSensitivity * 150.f; } + + static void SetSpawnPointOverride( const char *overrideObjName ); + static void WindowActivate(bool active); + void SetFollowerParticleSystemSO(plSceneObject *follower); + plSceneObject *GetFollowerParticleSystemSO(); + void RegisterForBehaviorNotify(plKey key); + void UnRegisterForBehaviorNotify(plKey key); + const hsBitVector& GetRelRegionCareAbout() const { return fRegionsICareAbout; } + const hsBitVector& GetRelRegionImIn() const { return fRegionsImIn; } + + bool IsKILowestLevel(); + int GetKILevel(); + void SetLinkInAnim(const char *animName); + plKey GetLinkInAnimKey() const; + + enum + { + kSwapTargetShadow, + kMaxSwapType + }; + enum + { + kBoneBaseMale = 0, + kBoneBaseFemale, + kBoneBaseCritter, // AI controlled avatar + kBoneBaseActor, // human controlled, non human avatar + kMaxBoneBase + }; + enum + { + kAvatarLOSGround, + kAvatarLOSSwimSurface, + }; + plMatrixDelayedCorrectionApplicator *fBoneRootAnimator; + + static const hsScalar kAvatarInputSynchThreshold; + static hsBool fClickToTurn; + static const char *BoneStrings[]; + + void SetPhysicalDims(hsScalar height, hsScalar width) { fPhysHeight = height; fPhysWidth = width; } + + void SetBodyAgeName(const char* ageName) {if (ageName) fBodyAgeName = ageName; else fBodyAgeName = "";} + void SetBodyFootstepSoundPage(const char* pageName) {if (pageName) fBodyFootstepSoundPage = pageName; else fBodyFootstepSoundPage = "";} + void SetAnimationPrefix(const char* prefix) {if (prefix) fAnimationPrefix = prefix; else fAnimationPrefix = "";} + + const char* GetUserStr() {return fUserStr.c_str();} + +protected: + void IInitDefaults(); + virtual void IFinalize(); + virtual void ICustomizeApplicator(); + virtual void ISetupMarkerCallbacks(plATCAnim *anim, plAnimTimeConvert *atc); + + void NetworkSynch(double timeNow, int force = 0); + hsBool IHandleControlMsg(plControlEventMsg* pMsg); + void IFireBehaviorNotify(UInt32 type, hsBool behaviorStart = true); + void IHandleInputStateMsg(plAvatarInputStateMsg *msg); + void ILinkToPersonalAge(); + int IFindSpawnOverride(void); + void ISetTransparentDrawOrder(bool val); + plLayerLinkAnimation *IFindLayerLinkAnim(); + + char *fRootName; // the name of the player root (from the max file) + hsBitVector fMoveFlags; // which keys/buttons are currently pressed + hsBitVector fMoveFlagsBackup; // a copy of fMoveFlags + typedef std::vector CtrlMessageVec; + CtrlMessageVec fQueuedCtrlMessages; // input messages we haven't processed + hsScalar fMouseFrameTurnStrength; // Sum turnage from mouse delta messages since last eval. + plKey fFootSoundSOKey; // The Scene Object we attach to targets for footstep sounds + plKey fLinkSoundSOKey; // Same thing for linking... wwwwawAWAWAwawa... + plKey fLinkInAnimKey; // Set when we link out, this is the anim to play (backwards) when we link in. + static hsScalar fMouseTurnSensitivity; + plArmatureUpdateMsg *fUpdateMsg; + + // Trying to be a good lad here and align all our bools and UInt8s... + bool fMidLink; // We're in between a LeaveAge and an EnterAge + bool fAlreadyPanicLinking; // Cleared when you enter an age. Prevents spamming the server with panic link requests. + bool fUnconsumedJump; // We've pressed the jump key, but haven't jumped yet + bool fReverseFBOnIdle; // see set/getters for comments + bool fPendingSynch; + bool fDebugOn; + bool fOpaque; + UInt8 fSuspendInputCount; + UInt8 fStealthMode; + int fStealthLevel; // you are invisible to other players/CCRs of lower stealthLevel + + double fLastInputSynch; + plAGModifier * fRootAGMod; + plAvBoneMap * fBoneMap; // uses id codes to look up bones. set up by the brain as needed. + double fLastSynch; + int fBodyType; + plClothingOutfit *fClothingOutfit; + plClothingSDLModifier *fClothingSDLMod; + plAvatarSDLModifier *fAvatarSDLMod; + plAvatarPhysicalSDLModifier *fAvatarPhysicalSDLMod; + hsTArray fClothToSOMap; + plArmatureEffectsMgr *fEffects; + plSceneObject *fFollowerParticleSystemSO; + static char *fSpawnPointOverride; + + // These vectors are used with relevance regions for culling out other objects + hsBitVector fRegionsImIn; + hsBitVector fRegionsICareAbout; + hsBitVector fOldRegionsImIn; + hsBitVector fOldRegionsICareAbout; + + hsTArray fNotifyKeys; + + // Extra info for creating our special physical at runtime + float fPhysHeight; + float fPhysWidth; + + bool fDontPanicLink; + + // strings for animations, age names, footstep sounds, etc + std::string fBodyAgeName; + std::string fBodyFootstepSoundPage; + std::string fAnimationPrefix; + + // user-defined string assigned to this avatar + std::string fUserStr; +}; + +// PLARMATURELOD +// This class has been phased into plArmatureModBase. It's left behind +// for backwards compatability. +class plArmatureLODMod : public plArmatureMod +{ +public: + // tors + plArmatureLODMod(); + plArmatureLODMod(const char * root_name); + virtual ~plArmatureLODMod(); + + CLASSNAME_REGISTER( plArmatureLODMod ); + GETINTERFACE_ANY( plArmatureLODMod, plArmatureMod ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + +class plAvBoneMap +{ +public: + plAvBoneMap(); + virtual ~plAvBoneMap(); + + const plSceneObject * FindBone(UInt32 boneID); // you probably want to use plAvBrainHuman::BoneID; + void AddBoneMapping(UInt32 boneID, const plSceneObject *SO); + +protected: + class BoneMapImp; // forward declaration to keep the header clean: see .cpp for implementation + BoneMapImp *fImp; // the thing that actually holds our map +}; + +#define TWO_PI (hsScalarPI * 2) + +#endif plArmatureMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBehaviors.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBehaviors.cpp new file mode 100644 index 00000000..de34bb8c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBehaviors.cpp @@ -0,0 +1,121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAvBehaviors.h" +#include "plAvBrainHuman.h" +#include "plArmatureMod.h" +#include "plAGAnimInstance.h" +#include "../plMessage/plAvatarMsg.h" + +#include "../plPipeline/plDebugText.h" + +plArmatureBehavior::plArmatureBehavior() : fAnim(nil), fArmature(nil), fBrain(nil), fIndex((UInt8)-1), fFlags(0) {} + +plArmatureBehavior::~plArmatureBehavior() +{ + if (fAnim) + fAnim->Detach(); +} + +void plArmatureBehavior::Init(plAGAnim *anim, hsBool loop, plArmatureBrain *brain, plArmatureModBase *armature, UInt8 index) +{ + fArmature = armature; + fBrain = brain; + fIndex = index; + fStrength.Set(0); + if (anim) + { + fAnim = fArmature->AttachAnimationBlended(anim, 0, 0, true); + fAnim->SetLoop(loop); + } +} + +void plArmatureBehavior::Process(double time, float elapsed) +{ +} + +void plArmatureBehavior::SetStrength(hsScalar val, hsScalar rate /* = 0.f */) +{ + hsScalar oldStrength = GetStrength(); + if (rate == 0) + fStrength.Set(val); + else + fStrength.Set(val, fabs(val - oldStrength) / rate); + + if (fAnim) + fAnim->Fade(val, rate); + if (val > 0 && oldStrength == 0) + IStart(); + else if (val == 0 && oldStrength > 0) + IStop(); +} + +hsScalar plArmatureBehavior::GetStrength() +{ + return fStrength.Value(); +} + +void plArmatureBehavior::Rewind() +{ + if (fAnim) + fAnim->SetCurrentTime(0.0f, true); +} + +void plArmatureBehavior::DumpDebug(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + hsScalar strength = GetStrength(); + const char *onOff = strength > 0 ? "on" : "off"; + char blendBar[11] = "||||||||||"; + int bars = (int)__min(10 * strength, 10); + blendBar[bars] = '\0'; + + if (fAnim) + { + const char *animName = fAnim->GetName(); + float time = fAnim->GetTimeConvert()->CurrentAnimTime(); + sprintf(strBuf, "%20s %3s time: %5.2f %s", animName, onOff, time, blendBar); + } + else + sprintf(strBuf, " Behavior %2d %3s %s", fIndex, onOff, blendBar); + + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; +} + + +void plArmatureBehavior::IStart() +{ + if (fAnim) + { + fAnim->SetCurrentTime(0.0f, true); + fAnim->Start(); + } +} + +void plArmatureBehavior::IStop() +{ + if (fFlags & kBehaviorFlagNotifyOnStop) + fBrain->OnBehaviorStop(fIndex); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBehaviors.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBehaviors.h new file mode 100644 index 00000000..582ef859 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBehaviors.h @@ -0,0 +1,71 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_AV_BEHAVIORS_H +#define PL_AV_BEHAVIORS_H + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnTimer/plTimedValue.h" + +class plAGAnim; +class plAGAnimInstance; +class plArmatureModBase; +class plArmatureBrain; +class plDebugText; + + +class plArmatureBehavior +{ +public: + plArmatureBehavior(); + virtual ~plArmatureBehavior(); + + void Init(plAGAnim *anim, hsBool loop, plArmatureBrain *brain, plArmatureModBase *armature, UInt8 index); + virtual void Process(double time, float elapsed); + virtual void SetStrength(hsScalar val, hsScalar rate = 0.f); // default instant change + virtual hsScalar GetStrength(); + virtual void Rewind(); + void DumpDebug(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); + + enum + { + kBehaviorFlagNotifyOnStop = 0x01, + }; + UInt32 fFlags; + +protected: + plAGAnimInstance *fAnim; + plArmatureModBase *fArmature; + plArmatureBrain *fBrain; + plTimedValue fStrength; + UInt8 fIndex; + + virtual void IStart(); + virtual void IStop(); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrain.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrain.cpp new file mode 100644 index 00000000..184522fd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrain.cpp @@ -0,0 +1,184 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// local +#include "plAvBrain.h" +#include "plAvBehaviors.h" +#include "plArmatureMod.h" +#include "plAvatarMgr.h" +#include "plAvatarTasks.h" + +// global +#include "hsGeometry3.h" +#include "hsQuat.h" + +// other +#include "../pnSceneObject/plSceneObject.h" +#include "../plPipeline/plDebugText.h" + +// messages +#include "../plMessage/plAvatarMsg.h" + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +plArmatureBrain::plArmatureBrain() : + fCurTask(nil), + fArmature(nil), + fAvMod(nil) +{ +} + +plArmatureBrain::~plArmatureBrain() +{ + while (fTaskQueue.size() > 0) + { + plAvTask *task = fTaskQueue.front(); + delete task; + fTaskQueue.pop_front(); + } + if (fCurTask) + delete fCurTask; +} + +hsBool plArmatureBrain::Apply(double timeNow, hsScalar elapsed) +{ + IProcessTasks(timeNow, elapsed); + fArmature->ApplyAnimations(timeNow, elapsed); + + return true; +} + +void plArmatureBrain::Activate(plArmatureModBase *armature) +{ + fArmature = armature; + fAvMod = plArmatureMod::ConvertNoRef(armature); +} + +void plArmatureBrain::QueueTask(plAvTask *task) +{ + if (task) + fTaskQueue.push_back(task); +} + +hsBool plArmatureBrain::LeaveAge() +{ + if (fCurTask) + fCurTask->LeaveAge(plArmatureMod::ConvertNoRef(fArmature)); + + plAvTaskQueue::iterator i = fTaskQueue.begin(); + for (; i != fTaskQueue.end(); i++) + { + plAvTask *task = *i; + task->LeaveAge(plArmatureMod::ConvertNoRef(fArmature)); // Give it a chance to do something before we nuke it. + delete task; + } + fTaskQueue.clear(); + return true; +} + +hsBool plArmatureBrain::IsRunningTask() const +{ + if (fCurTask) + return true; + if(fTaskQueue.size() > 0) + return true; + + return false; +} + +// Nothing for this class to read/write. These methods exist +// for backwards compatability with plAvBrain and plAvBrainUser +void plArmatureBrain::Write(hsStream *stream, hsResMgr *mgr) +{ + plCreatable::Write(stream, mgr); + + // plAvBrain + stream->WriteSwap32(0); + stream->WriteBool(false); + + // plAvBrainUser + stream->WriteSwap32(0); + stream->WriteSwapScalar(0.f); + stream->WriteSwapDouble(0.f); +} + +void plArmatureBrain::Read(hsStream *stream, hsResMgr *mgr) +{ + plCreatable::Read(stream, mgr); + + // plAvBrain + stream->ReadSwap32(); + if (stream->ReadBool()) + mgr->ReadKey(stream); + + // plAvBrainUser + stream->ReadSwap32(); + stream->ReadSwapScalar(); + stream->ReadSwapDouble(); +} + +// MSGRECEIVE +hsBool plArmatureBrain::MsgReceive(plMessage * msg) +{ + plAvTaskMsg *taskMsg = plAvTaskMsg::ConvertNoRef(msg); + if (taskMsg) + { + return IHandleTaskMsg(taskMsg); + } + return false; +} + +void plArmatureBrain::IProcessTasks(double time, hsScalar elapsed) +{ + if (!fCurTask || !fCurTask->Process(plArmatureMod::ConvertNoRef(fArmature), this, time, elapsed)) + { + if (fCurTask) + { + fCurTask->Finish(plArmatureMod::ConvertNoRef(fArmature), this, time, elapsed); + delete fCurTask; + fCurTask = nil; + } + + // need a new task + if (fTaskQueue.size() > 0) + { + plAvTask *newTask = fTaskQueue.front(); + if (newTask && newTask->Start(plArmatureMod::ConvertNoRef(fArmature), this, time, elapsed)) + { + fCurTask = newTask; + fTaskQueue.pop_front(); + } + // if we couldn't start the task, we'll keep trying until we can. + } + } +} + +hsBool plArmatureBrain::IHandleTaskMsg(plAvTaskMsg *msg) +{ + plAvTask *task = msg->GetTask(); + QueueTask(task); + return true; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrain.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrain.h new file mode 100644 index 00000000..d3b75a6a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrain.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLAVBRAIN_INC +#define PLAVBRAIN_INC + +#include "plAGModifier.h" +#include "hsTypes.h" +#include "hsTemplates.h" +#include "hsBitVector.h" +#include "hsGeometry3.h" +#include "hsResMgr.h" + +#include "../pnNetCommon/plSynchedObject.h" + +#pragma warning(disable: 4284) +#include + +class plArmatureModBase; +class plArmatureBehavior; +class plHKAction; +class plAvTask; +class plAvTaskMsg; +class plDebugText; + +class plArmatureBrain : public plCreatable +{ +public: + plArmatureBrain(); + virtual ~plArmatureBrain(); + + CLASSNAME_REGISTER( plArmatureBrain ); + GETINTERFACE_ANY( plArmatureBrain, plCreatable ); + + virtual hsBool Apply(double timeNow, hsScalar elapsed); + virtual void Activate(plArmatureModBase *armature); + virtual void Deactivate() {} + virtual void Suspend() {} + virtual void Resume() {} + virtual void Spawn(double timeNow) {} + virtual void OnBehaviorStop(UInt8 index) {} + virtual hsBool LeaveAge(); + virtual hsBool IsRunningTask() const; + virtual void QueueTask(plAvTask *task); + virtual void DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) {} + + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual hsBool MsgReceive(plMessage *msg); + +protected: + virtual void IProcessTasks(double time, hsScalar elapsed); + virtual hsBool IHandleTaskMsg(plAvTaskMsg *msg); + + typedef std::deque plAvTaskQueue; + plAvTaskQueue fTaskQueue; // FIFO queue of tasks we're working on + plAvTask *fCurTask; // the task we're working on right now + + plArmatureModBase *fArmature; + plArmatureMod *fAvMod; + hsTArray fBehaviors; +}; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.cpp new file mode 100644 index 00000000..02928b47 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.cpp @@ -0,0 +1,1035 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +/* + Some interesting special rules: + You can only transition from one animation to another at certain points. + + The climb, mount, and dismount animations must be at their beginning or end + to transition. + + If a climb or mount animation finishes and the key is not being held down, the idle + animation starts automatically. + + If a climb or mount finishes and the key is being held down, the brain will *try* + to transition to the same stage, effectively looping it. + + The idle can transition at any point. + + The Release and FallOff aniamtions can forcibly transition *any* animation. +*/ + +///////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////// + +// singular +#include "plAvBrainClimb.h" + +// local +#include "plAnimStage.h" +#include "plAGAnim.h" +#include "plAGAnimInstance.h" +#include "plArmatureMod.h" +#include "plMatrixChannel.h" +#include "plAvBrainHuman.h" + +// global +#include "hsTimer.h" + +// other +#include "../plPipeline/plDebugText.h" +#include "../plMessage/plSimStateMsg.h" +#include "../plMessage/plLOSHitMsg.h" +#include "../plMessage/plLOSRequestMsg.h" +#include "../plMessage/plClimbEventMsg.h" +#include "../pnNetCommon/plSDLTypes.h" + +///////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +///////////////////////////////////////////////////////////////// + +// CTOR default +plAvBrainClimb::plAvBrainClimb() +: fCurMode(kInactive), + fNextMode(kInactive), + fDesiredDirection(plClimbMsg::kUp), + fControlDir(0.0f), + + fAllowedDirections(plClimbMsg::kUp | plClimbMsg::kDown | plClimbMsg::kLeft | plClimbMsg::kRight), + fPhysicallyBlockedDirections(0), + fOldPhysicallyBlockedDirections(0), + fAllowedDismounts(0), + + fCurStage(nil), + fExitStage(nil), + fVerticalProbeLength(0.0f), + fHorizontalProbeLength(0.0f), + + fUp(nil), + fDown(nil), + fLeft(nil), + fRight(nil), + fMountUp(nil), + fMountDown(nil), + fMountLeft(nil), + fMountRight(nil), + fDismountUp(nil), + fDismountDown(nil), + fDismountLeft(nil), + fDismountRight(nil), + fIdle(nil), + fRelease(nil), + fFallOff(nil) +{ + IInitAnimations(); +} + +// PLAVBRAINCLIMB +plAvBrainClimb::plAvBrainClimb(Mode nextMode) +: fCurMode(kInactive), + fNextMode(nextMode), + fDesiredDirection(plClimbMsg::kUp), + fControlDir(0.0f), + + fAllowedDirections(plClimbMsg::kUp | plClimbMsg::kDown | plClimbMsg::kLeft | plClimbMsg::kRight), + fPhysicallyBlockedDirections(0), + fOldPhysicallyBlockedDirections(0), + fAllowedDismounts(0), + + fCurStage(nil), + fExitStage(nil), + fVerticalProbeLength(0.0f), + fHorizontalProbeLength(0.0f), + + fUp(nil), + fDown(nil), + fLeft(nil), + fRight(nil), + fMountUp(nil), + fMountDown(nil), + fMountLeft(nil), + fMountRight(nil), + fDismountUp(nil), + fDismountDown(nil), + fDismountLeft(nil), + fDismountRight(nil), + fIdle(nil), + fRelease(nil), + fFallOff(nil) +{ + IInitAnimations(); +} + + +plAvBrainClimb::~plAvBrainClimb() +{ + if(fAvMod) + { + if(fCurStage) + fCurStage->Detach(fAvMod); + if(fExitStage) + fExitStage->Detach(fAvMod); + } + if(fUp) delete fUp; + if(fDown) delete fDown; + if(fLeft) delete fLeft; + if(fRight) delete fRight; + if(fMountUp) delete fMountUp; + if(fMountDown) delete fMountDown; + if(fMountLeft) delete fMountLeft; + if(fMountRight) delete fMountRight; + if(fDismountUp) delete fDismountUp; + if(fDismountLeft) delete fDismountLeft; + if(fDismountRight) delete fDismountRight; + if(fIdle) delete fIdle; +// if(fRelease) delete fRelease; +// if(fFallOff) delete fFallOff; +} + +// ACTIVATE +void plAvBrainClimb::Activate(plArmatureModBase *avMod) +{ + plArmatureBrain::Activate(avMod); + ICalcProbeLengths(); + + fAvMod->GetRootAnimator()->Enable(true); + fAvMod->EnablePhysicsKinematic(true); +} + +void plAvBrainClimb::Deactivate() +{ + fAvMod->GetRootAnimator()->Enable(false); + fAvMod->EnablePhysicsKinematic(false); +} + +// APPLY +hsBool plAvBrainClimb::Apply(double time, hsScalar elapsed) +{ + hsBool result = true; + + IGetDesiredDirection(); + + float overage = 0.0f; // if we ran past the end of the current stage, remember how much + bool done = false; + + if(fExitStage) + done = IProcessExitStage(time, elapsed); + else + done = IAdvanceCurrentStage(time, elapsed, overage); + + if(done || fCurMode == kIdle) + { + // if the transition is to one of the terminal modes, we're going to abort + result = ITryStageTransition(time, overage); + } + + if(!result && fExitStage) + { + fExitStage->Detach(fAvMod); + } + + fAvMod->ApplyAnimations(time, elapsed); + + IProbeEnvironment(); + return result; +} + +// MSGRECEIVE +hsBool plAvBrainClimb::MsgReceive(plMessage *msg) +{ + plClimbMsg *climbMsg; + plLOSHitMsg *losMsg; + + if(climbMsg = plClimbMsg::ConvertNoRef(msg)) + { + return IHandleClimbMsg(climbMsg); + } else if(losMsg = plLOSHitMsg::ConvertNoRef(msg)) + { + return IHandleLOSMsg(losMsg); + } else { + return plArmatureBrain::MsgReceive(msg); + } +} + +// IHANDLECLIMBMSG +hsBool plAvBrainClimb::IHandleClimbMsg(plClimbMsg *msg) +{ + switch(msg->fCommand) + { + case plClimbMsg::kEnableClimb: + if(msg->fStatus) + this->fAllowedDirections |= msg->fDirection; + else + this->fAllowedDirections &= ~msg->fDirection; + break; + case plClimbMsg::kEnableDismount: + if(msg->fStatus) + this->fAllowedDismounts |= msg->fDirection; + else + this->fAllowedDismounts &= ~msg->fDirection; + break; + case plClimbMsg::kRelease: + IRelease(true); + break; + case plClimbMsg::kFallOff: + { + if(fCurMode != kReleasing + && fCurMode != kFallingOff + && fCurMode != kFinishing) + { + plClimbEventMsg* pMsg = TRACKED_NEW plClimbEventMsg; + pMsg->SetSender(msg->fTarget); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kLocalPropagate); + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + pMsg->Send(); + } + IRelease(false); + + break; + } + } + return true; +} + +// IHANDLELOSMSG +hsBool plAvBrainClimb::IHandleLOSMsg(plLOSHitMsg *msg) +{ + plClimbMsg::Direction blockDir = static_cast(msg->fRequestID); + // this is a weak test because someone else could be using the same bits to mean something different + // the real strategy is that we should only receive LOS messages of our own creation + hsBool oneOfOurs = blockDir == plClimbMsg::kUp || blockDir == plClimbMsg::kDown || blockDir == plClimbMsg::kLeft || blockDir == plClimbMsg::kRight; + if(oneOfOurs) + { + fPhysicallyBlockedDirections |= blockDir; + return true; + } else { + return false; + } +} + +// IPROCESSEXITSTAGE +bool plAvBrainClimb::IProcessExitStage(double time, float elapsed) +{ + plAGAnimInstance *ai = fExitStage->GetAnimInstance(); + hsBool animDone = ai->IsAtEnd(); + float unused; + + // if we have an exit stage running, move it instead of the base stage + if(!animDone) + fExitStage->MoveRelative(time, elapsed, unused, fAvMod); // only advance if it's not finished yet + + float curBlend = ai->GetBlend(); + + if(curBlend > .99) // reached peak strength + { + fCurStage->Detach(fAvMod); // remove the (now completely masked) underlying anim + fCurStage = nil; + ai->Fade(0, 2.0f); // start fading the exit anim + } else if(animDone && curBlend == 0.0f) { + return true; // finished and faded; we're really done now + } + return false; +} + +// IRELEASE +void plAvBrainClimb::IRelease(bool intentional) +{ + if(fCurMode != kReleasing + && fCurMode != kFallingOff + && fCurMode != kFinishing) + { + if(intentional) + { + // fNextMode = kReleasing; + fCurMode = kReleasing; + fExitStage = fRelease; + } else { + // fNextMode = kFallingOff; + fCurMode = kFallingOff; + fExitStage = fFallOff; + } + fNextMode = kFinishing; + double time = hsTimer::GetSysSeconds(); + // attach the exit stage atop the current stage. from here on out we'll only advance + // the current stage. + fAvMod->GetRootAnimator()->Enable(false); + fAvMod->EnablePhysicsKinematic(false); + fExitStage->Attach(fAvMod, this, 1.0f, time); + } +} + +// IADVANCECURRENTSTAGE +bool plAvBrainClimb::IAdvanceCurrentStage(double time, float elapsed, float &overage) +{ + bool stageDone = false; + if(fCurStage) + { + // elapsed tells us how far in time to move the animation + // we must combine it with the key state to figure out whether + // we're moving forward or backward in the animation + fControlDir = 0.0f; // 0 is still; -1 is backwards; 1 is forwards + + switch(fCurMode) + { + case kDismountingUp: // if dismounting or mounting become reversable, move + case kMountingUp: // these cases to be with "kClimbingUp"; same for the rest + case kDismountingRight: + case kMountingRight: + case kDismountingDown: + case kMountingDown: + case kDismountingLeft: + case kMountingLeft: + case kFallingOff: + case kReleasing: + case kFinishing: + case kIdle: + case kClimbingUp: + case kClimbingRight: + case kClimbingDown: + case kClimbingLeft: + fControlDir = 1.0f; // these guys all auto-advance + break; + case kInactive: + case kUnknown: + // fControlDir is already 0 + break; + default: + hsStatusMessage("Unknown mode in plAvBrainClimb::IAdvanceCurrentStage"); + } + float delta = elapsed * fControlDir; + stageDone = fCurStage->MoveRelative(time, delta, overage, fAvMod); + } else { + stageDone = true; + } + return stageDone; +} + +// ITRYSTAGETRANSITION +bool plAvBrainClimb::ITryStageTransition(double time, float overage) +{ +// hsStatusMessageF("Got overage %f", overage); + IChooseNextMode(); + + bool result = true; + // and vice versa + if(fCurStage && fCurStage != fIdle) + { + hsStatusMessage("Wrapping externally."); + bool atStart = overage >= 0.0f ? true : false; // if we went off the end, back to start + fCurStage->Reset(time, fAvMod, atStart); + // any time we start a stage besides idle, clear the climbing and dismount restrictions +// this->fAllowedDirections = plClimbMsg::kUp | plClimbMsg::kDown | plClimbMsg::kLeft | plClimbMsg::kRight; +// this->fAllowedDismounts = 0; + } + + if(fNextMode != fCurMode) + { + if(fCurStage) + fCurStage->Detach(fAvMod); + fCurStage = IGetStageFromMode(fNextMode); + if(fCurStage) + { + hsAssert(fCurStage, "Couldn't get next stage - mode has no stage. (Matt)"); + fCurMode = fNextMode; + if(fCurStage) + result = (fCurStage->Attach(fAvMod, this, 1.0f, time) != nil); + fAvMod->DirtySynchState(kSDLAvatar, 0); // write our new stage to the server + } else { + result = false; + } + } else { +// hsStatusMessage("Wrapping externally."); +// bool atStart = overage >= 0.0f ? true : false; // if we went off the end, back to start +// // and vice versa +// if(fCurStage) +// fCurStage->Reset(time, fAvMod, atStart); + } + + fNextMode = kUnknown; + + if(fCurStage) + { + if(overage < 0.0f) + { + float length = fCurStage->GetLength(); + fCurStage->SetLocalTime(length + overage); + } else { + fCurStage->SetLocalTime(overage); + } + fAvMod->GetRootAnimator()->Reset(time); + } + + return result; +} + +// ICHOOSENEXTMODE +bool plAvBrainClimb::IChooseNextMode() +{ + // bear in mind this is only called when we're at a point where + // we can change direction (usually because a climb loop has + // just finished) + switch (fCurMode) + { + case kInactive: + case kUnknown: + case kFinishing: + break; // no change + + case kDismountingUp: + case kDismountingDown: + case kDismountingLeft: + case kDismountingRight: + case kFallingOff: + case kReleasing: + fNextMode = kFinishing; + break; + case kMountingUp: + case kClimbingUp: + case kMountingDown: + case kClimbingDown: + case kMountingLeft: + case kClimbingLeft: + case kMountingRight: + case kClimbingRight: + case kIdle: + fNextMode = kIdle; + if(fAllowedDismounts & fDesiredDirection) + { + switch(fDesiredDirection) + { + case plClimbMsg::kUp: + fNextMode = kDismountingUp; + break; + case plClimbMsg::kDown: + fNextMode = kDismountingDown; + break; + case plClimbMsg::kLeft: + fNextMode = kDismountingLeft; + break; + case plClimbMsg::kRight: + fNextMode = kDismountingRight; + break; + case plClimbMsg::kCenter: + fNextMode = kIdle; + break; + default: + hsAssert(false, "Error in fDesiredDirection. (Matt)"); + } + } else if(fAllowedDirections & fDesiredDirection & ~fPhysicallyBlockedDirections) + { + switch(fDesiredDirection) + { + case plClimbMsg::kUp: + fNextMode = kClimbingUp; + break; + case plClimbMsg::kDown: + fNextMode = kClimbingDown; + break; + case plClimbMsg::kLeft: + fNextMode = kClimbingLeft; + break; + case plClimbMsg::kRight: + fNextMode = kClimbingRight; + break; + case plClimbMsg::kCenter: + fNextMode = kIdle; + break; + default: + hsAssert(false, "Error in fDesiredDirection. (Matt)"); + } + } + break; + default: + hsAssert(false, "Error in fCurMode. (Matt)"); + } + return true; +} + +// IGETSTAGE +plAnimStage * plAvBrainClimb::IGetStageFromMode(Mode mode) +{ + switch(mode) + { + case kClimbingUp: + return fUp; + case kClimbingDown: + return fDown; + case kClimbingLeft: + return fLeft; + case kClimbingRight: + return fRight; + case kMountingUp: + return fMountUp; + case kMountingDown: + return fMountDown; + case kMountingLeft: + return fMountLeft; + case kMountingRight: + return fMountRight; + case kDismountingUp: + return fDismountUp; + case kDismountingDown: + return fDismountDown; + case kDismountingLeft: + return fDismountLeft; + case kDismountingRight: + return fDismountRight; + case kIdle: + return fIdle; + case kReleasing: + return fRelease; + case kFallingOff: + return fFallOff; + case kInactive: + case kFinishing: + case kUnknown: + case kDone: + return nil; + default: + hsAssert(false, "Unknown mode."); + return nil; + } +} + +plAvBrainClimb::Mode plAvBrainClimb::IGetModeFromStage(plAnimStage *stage) +{ + if(stage == fUp) + return kClimbingUp; + else if(stage == fDown) + return kClimbingDown; + else if(stage == fLeft) + return kClimbingLeft; + else if(stage == fRight) + return kClimbingRight; + else if(stage == fMountUp) + return kMountingUp; + else if(stage == fMountDown) + return kMountingDown; + else if(stage == fMountLeft) + return kMountingLeft; + else if(stage == fMountRight) + return kMountingRight; + else if(stage == fDismountUp) + return kDismountingUp; + else if(stage == fDismountDown) + return kDismountingDown; + else if(stage == fDismountLeft) + return kDismountingLeft; + else if(stage == fDismountRight) + return kDismountingRight; + else if(stage == fIdle) + return kIdle; + else if(stage == fRelease) + return kReleasing; + else if(stage == fFallOff) + return kFallingOff; + else + return kUnknown; +} + +// IGETDESIREDDIRECTION +void plAvBrainClimb::IGetDesiredDirection() +{ + if(fAvMod->ForwardKeyDown()) { + fDesiredDirection = plClimbMsg::kUp; + } else if (fAvMod->BackwardKeyDown()) { + fDesiredDirection = plClimbMsg::kDown; + } else if (fAvMod->TurnLeftKeyDown()) { + fDesiredDirection = plClimbMsg::kLeft; + } else if (fAvMod->TurnRightKeyDown()) { + fDesiredDirection = plClimbMsg::kRight; + } else { + fDesiredDirection = plClimbMsg::kCenter; + } +} + +/** Look left, right, up, and down to see which directions are clear + for our movement. We could do this by positioning our actual collision + body and testing for hits, but it gives a lot more false positives *and* + we won't get the normals of intersection, so it will be more complex + to figure out which directions are actually blocked. + The approach here is to do a raycast in the aforementioned directions + and fail that direction if the raycast hits anything. */ +void plAvBrainClimb::IProbeEnvironment() +{ + hsMatrix44 l2w = fAvMod->GetTarget(0)->GetLocalToWorld(); + // we're just going to pull the axes out of the + + hsPoint3 up = hsPoint3(l2w.GetAxis(hsMatrix44::kUp) * fVerticalProbeLength); + hsPoint3 down = -up; + hsPoint3 right = hsPoint3(l2w.GetAxis(hsMatrix44::kRight) * fHorizontalProbeLength); + hsPoint3 left = -right; + hsPoint3 start = l2w.GetTranslate(); + + start.fZ += 3.0f; // move the origin from the feet to the bellybutton + up += start; + down += start; + left += start; + right += start; + + plKey ourKey = fAvMod->GetKey(); + + // *** would be cool if we could hint that these should be batched for spatial coherence optimization + plLOSRequestMsg *upReq = TRACKED_NEW plLOSRequestMsg(ourKey, start, up, plSimDefs::kLOSDBCustom, plLOSRequestMsg::kTestAny, plLOSRequestMsg::kReportHit); + upReq->SetRequestID(static_cast(plClimbMsg::kUp)); + upReq->Send(); + + plLOSRequestMsg *downReq = TRACKED_NEW plLOSRequestMsg(ourKey, start, down, plSimDefs::kLOSDBCustom, plLOSRequestMsg::kTestAny, plLOSRequestMsg::kReportHit); + downReq->SetRequestID(static_cast(plClimbMsg::kDown)); + downReq->Send(); + + plLOSRequestMsg *leftReq = TRACKED_NEW plLOSRequestMsg(ourKey, start, left, plSimDefs::kLOSDBCustom, plLOSRequestMsg::kTestAny, plLOSRequestMsg::kReportHit); + leftReq->SetRequestID(static_cast(plClimbMsg::kLeft)); + leftReq->SetRequestType(plSimDefs::kLOSDBCustom); + leftReq->Send(); + + plLOSRequestMsg *rightReq = TRACKED_NEW plLOSRequestMsg(ourKey, start, right, plSimDefs::kLOSDBCustom, plLOSRequestMsg::kTestAny, plLOSRequestMsg::kReportHit); + rightReq->SetRequestID(static_cast(plClimbMsg::kRight)); + rightReq->Send(); + + fOldPhysicallyBlockedDirections = fPhysicallyBlockedDirections; + fPhysicallyBlockedDirections = 0; // clear our blocks until the new reports come in.... +} + +// ICalcProbeLengths ------------------- +// ----------------- +void plAvBrainClimb::ICalcProbeLengths() +{ + // we assume that the up and down climbs go the same distance; + // same for the left and right climbs + plAGAnim *up = fAvMod->FindCustomAnim("ClimbUp"); + plAGAnim *left = fAvMod->FindCustomAnim("ClimbLeft"); + + hsMatrix44 upMove, leftMove; + + hsAssert(up, "Couldn't find ClimbUp animation."); + if(up) + { + GetStartToEndTransform(up, &upMove, nil, "Handle"); + fVerticalProbeLength = upMove.GetTranslate().fZ; + } else + fVerticalProbeLength = 4.0f; // guess + + hsAssert(left, "Couldn't find ClimbLeft animation."); + if(left) + { + GetStartToEndTransform(left, &leftMove, nil, "Handle"); + fHorizontalProbeLength = leftMove.GetTranslate().fX; + } else + fHorizontalProbeLength = 3.0f; // guess +} + +// IInitAnimations --------------------- +// --------------- +hsBool plAvBrainClimb::IInitAnimations() +{ + fUp = TRACKED_NEW plAnimStage("WallClimbUp", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressAuto, + 0); + fDown = TRACKED_NEW plAnimStage("WallClimbDown", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressAuto, + 0); + fLeft = TRACKED_NEW plAnimStage("WallClimbLeft", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressAuto, + 0); + fRight = TRACKED_NEW plAnimStage("WallClimbRight", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressAuto, + 0); + // the mounts + fMountUp = TRACKED_NEW plAnimStage("WallClimbMountUp", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + fMountDown = TRACKED_NEW plAnimStage("WallClimbMountDown", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + fMountLeft = TRACKED_NEW plAnimStage("WallClimbMountLeft", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + fMountRight = TRACKED_NEW plAnimStage("WallClimbMountRight", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + // and here's the dismount + fDismountUp = TRACKED_NEW plAnimStage("WallClimbDismountUp", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + fDismountDown = TRACKED_NEW plAnimStage("WallClimbDismountDown", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + fDismountLeft = TRACKED_NEW plAnimStage("WallClimbDismountLeft", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + fDismountRight = TRACKED_NEW plAnimStage("WallClimbDismountUp", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + // other + fIdle = TRACKED_NEW plAnimStage("WallClimbIdle", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + fRelease = TRACKED_NEW plAnimStage("WallClimbRelease", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + fFallOff = TRACKED_NEW plAnimStage("WallClimbFallOff", + plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// SDL-BASED PERSISTENCE +// +///////////////////////////////////////////////////////////////////////////////////////// +#include "../plSDL/plSDL.h" +#include "plAvatarSDLModifier.h" + +// SaveToSDL ----------------------------------------- +// --------- +void plAvBrainClimb::SaveToSDL(plStateDataRecord *sdl) +{ + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurMode)->Set(fCurMode); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrNextMode)->Set(fNextMode); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrAllowedDirections)->Set((int)fAllowedDirections); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrAllowedDismounts)->Set((int)fAllowedDismounts); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrVertProbeLength)->Set(fVerticalProbeLength); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrHorizProbeLength)->Set(fHorizontalProbeLength); + + bool curStageAttached = fCurStage && fCurStage->GetIsAttached(); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageAttached)->Set(curStageAttached); + if(curStageAttached) + { + // slightly abuse the "mode" semantics; it happens to work as a persistance format + Mode curStageAsMode = IGetModeFromStage(fCurStage); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStage)->Set(curStageAsMode); + + float curStageTime = fCurStage->GetLocalTime(); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageTime)->Set(curStageTime); + + float curStageBlend = fCurStage->GetAnimInstance()->GetBlend(); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageStrength)->Set(curStageBlend); + } + bool exitStageAttached = fExitStage && fExitStage->GetIsAttached(); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageAttached)->Set(exitStageAttached); + + if(exitStageAttached) + { + Mode exitStageAsMode = IGetModeFromStage(fExitStage); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStage)->Set(exitStageAsMode); + + float exitStageTime = fExitStage->GetLocalTime(); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageTime)->Set(exitStageTime); + + float exitStageBlend = fExitStage->GetAnimInstance()->GetBlend(); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageStrength)->Set(exitStageBlend); + } +} + +// LoadFromSDL ----------------------------------------- +// ----------- +void plAvBrainClimb::LoadFromSDL(const plStateDataRecord *sdl) +{ + double curTime = hsTimer::GetSysSeconds(); + + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStage)->Get((int*)&fCurMode); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrNextMode)->Get((int*)&fNextMode); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrAllowedDirections)->Get((int*)&fAllowedDirections); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrAllowedDismounts)->Get((int*)&fAllowedDismounts); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrVertProbeLength)->Get(&fVerticalProbeLength); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrHorizProbeLength)->Get(&fHorizontalProbeLength); + + bool curStageAttached = false; + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageAttached)->Get(&curStageAttached); + if(curStageAttached) + { + Mode *curStageMode; // distinct from curMode; this is just a mode-based representation of the current stage + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStage)->Get((int*)&curStageMode); + plAnimStage *curStage = this->IGetStageFromMode(fCurMode); + curStage->Attach(fAvMod, this, 0.0f, curTime); + + float curStageTime = 0.0f; + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageTime)->Get(&curStageTime); + curStage->ResetAtTime(curTime, curStageTime, fAvMod); // restart the relative-position sampler + + float curStageBlend = 0.0f; + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageStrength)->Get(&curStageBlend); + curStage->GetAnimInstance()->SetBlend(curStageBlend); + } + + bool exitStageAttached; + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageAttached)->Get(&exitStageAttached); + if(exitStageAttached) + { + Mode exitStageMode; // the exit stage, in mode form + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStage)->Get((int *)&exitStageMode); + plAnimStage *exitStage = this->IGetStageFromMode(exitStageMode); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageTime)->Get((int *)&fNextMode); + sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageStrength)->Get((int *)&fNextMode); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// READABLE TEXT FOR DEBUGGING +// +///////////////////////////////////////////////////////////////////////////////////////// + +// DumpToDebugDisplay -------------------------------------------------------------------------------------- +// ------------------ +void plAvBrainClimb::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + debugTxt.DrawString(x, y, "Brain type: Climb"); + y += lineHeight; + const char * worldDir = WorldDirStr(fDesiredDirection); + const char * modeStr = ModeStr(fCurMode); + + char buffy[256]; + sprintf(buffy, "direction: %s mode: %s controlDir: %f", worldDir, modeStr, fControlDir); + debugTxt.DrawString(x,y, buffy); + y += lineHeight; + + IDumpClimbDirections(x, y, lineHeight, strBuf, debugTxt); + IDumpDismountDirections(x, y, lineHeight, strBuf, debugTxt); + IDumpBlockedDirections(x, y, lineHeight, strBuf, debugTxt); + + fUp->DumpDebug(fUp == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fDown->DumpDebug(fDown == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fLeft->DumpDebug(fLeft == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fRight->DumpDebug(fRight == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fMountUp->DumpDebug(fMountUp == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fMountDown->DumpDebug(fMountDown == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fMountLeft->DumpDebug(fMountLeft == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fMountRight->DumpDebug(fMountRight == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fDismountUp->DumpDebug(fDismountUp == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fDismountDown->DumpDebug(fDismountDown == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fDismountLeft->DumpDebug(fDismountLeft == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fDismountRight->DumpDebug(fDismountRight == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fIdle->DumpDebug(fIdle == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fRelease->DumpDebug(fRelease == fCurStage, x, y, lineHeight, strBuf, debugTxt); + fFallOff->DumpDebug(fFallOff == fCurStage, x, y, lineHeight, strBuf, debugTxt); + +} + +// IDumpClimbDirections -------------------------------------------------------------------------------------- +// -------------------- +void plAvBrainClimb::IDumpClimbDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + const char * prolog = "Allowed directions: "; + std::string str; + + str = prolog; + if(fAllowedDirections & plClimbMsg::kUp) + str = str + "UP "; + if(fAllowedDirections & plClimbMsg::kDown) + str = str + "DOWN "; + if(fAllowedDirections & plClimbMsg::kLeft) + str = str + "LEFT "; + if(fAllowedDirections & plClimbMsg::kRight) + str = str + "RIGHT "; + + if(str.size() == strlen(prolog)) + str = str + "- NONE -"; + + debugTxt.DrawString(x, y, str.c_str()); + y += lineHeight; +} + +// IDumpDismountDirections -------------------------------------------------------------------------------------- +// ----------------------- +void plAvBrainClimb::IDumpDismountDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + const char * prolog = "Enabled dismounts: "; + std::string str; + + str = prolog; + if(fAllowedDismounts & plClimbMsg::kUp) + str = str + "UP "; + if(fAllowedDismounts & plClimbMsg::kDown) + str = str + "DOWN "; + if(fAllowedDismounts & plClimbMsg::kLeft) + str = str + "LEFT "; + if(fAllowedDismounts & plClimbMsg::kRight) + str = str + "RIGHT "; + + if(str.size() == strlen(prolog)) + str = str + "- NONE -"; + + debugTxt.DrawString(x, y, str.c_str()); + y += lineHeight; +} + +void plAvBrainClimb::IDumpBlockedDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + const char * prolog = "Physically blocked: "; + std::string str; + + str = prolog; + if(fOldPhysicallyBlockedDirections & plClimbMsg::kUp) + str = str + "UP "; + if(fOldPhysicallyBlockedDirections & plClimbMsg::kDown) + str = str + "DOWN "; + if(fOldPhysicallyBlockedDirections & plClimbMsg::kLeft) + str = str + "LEFT "; + if(fOldPhysicallyBlockedDirections & plClimbMsg::kRight) + str = str + "RIGHT "; + + if(str.size() == strlen(prolog)) + str = str + "- NONE -"; + + debugTxt.DrawString(x, y, str.c_str()); + y += lineHeight; +} + +const char * plAvBrainClimb::WorldDirStr(plClimbMsg::Direction dir) +{ + switch(dir) + { + case plClimbMsg::kUp: + return "Up"; + case plClimbMsg::kDown: + return "Down"; + case plClimbMsg::kLeft: + return "Left"; + case plClimbMsg::kRight: + return "Right"; + case plClimbMsg::kCenter: + return "Center"; + default: + return "WTF?"; + } +} + +const char *plAvBrainClimb::ModeStr(Mode mode) +{ + switch(mode) + { + case kInactive: + return "Inactive"; + case kUnknown: + return "Unknown"; + case kFinishing: + return "Finishing"; + case kDone: + return "Done"; + case kClimbingUp: + return "ClimbingUp"; + case kClimbingDown: + return "ClimbingDown"; + case kClimbingLeft: + return "ClimbingLeft"; + case kClimbingRight: + return "ClimbingRight"; + case kMountingUp: + return "MountingUp"; + case kMountingDown: + return "MountingDown"; + case kMountingLeft: + return "MountingLeft"; + case kMountingRight: + return "MountingRight"; + case kDismountingUp: + return "MountingUp"; + case kDismountingDown: + return "DismountingDown"; + case kDismountingLeft: + return "DismountingLeft"; + case kDismountingRight: + return "DismountingRight"; + case kIdle: + return "Idle"; + case kReleasing: + return "Releasing"; + case kFallingOff: + return "FallingOff"; + default: + return "WTF???!!!"; + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.h new file mode 100644 index 00000000..645ce191 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainClimb.h @@ -0,0 +1,211 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAvBrainClimb_Include +#define plAvBrainClimb_Include +#pragma once + +///////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////// + +#include "plAvBrain.h" +#include "../plMessage/plClimbMsg.h" + +///////////////////////////////////////////////////////////////// +// +// PROTOTYPES +// +///////////////////////////////////////////////////////////////// + +class plAnimStage; +class plLOSHitMsg; +class plStateDataRecord; + +///////////////////////////////////////////////////////////////// +// +// DECLARATION +// +///////////////////////////////////////////////////////////////// +class plAvBrainClimb : public plArmatureBrain +{ +public: + enum Mode { + kInactive, + kUnknown, + kFinishing, + kDone, + kClimbingUp, + kClimbingDown, + kClimbingLeft, + kClimbingRight, + kMountingUp, + kMountingDown, + kMountingLeft, + kMountingRight, + kDismountingUp, + kDismountingDown, + kDismountingLeft, + kDismountingRight, + kIdle, + kReleasing, + kFallingOff + }; + + plAvBrainClimb(); + plAvBrainClimb(Mode initialMode); + virtual ~plAvBrainClimb(); + + virtual void Activate(plArmatureModBase *avMod); + virtual void Deactivate(); + virtual hsBool Apply(double timeNow, hsScalar elapsed); + + virtual void SaveToSDL(plStateDataRecord *sdl); + virtual void LoadFromSDL(const plStateDataRecord *sdl); + + void DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); + const char * plAvBrainClimb::WorldDirStr(plClimbMsg::Direction dir); + const char *plAvBrainClimb::ModeStr(Mode mode); + + // plasma protocol + virtual hsBool MsgReceive(plMessage *msg); + + CLASSNAME_REGISTER( plAvBrainClimb ); + GETINTERFACE_ANY( plAvBrainClimb, plArmatureBrain); + +private: + bool IAdvanceCurrentStage(double time, float elapsed, float &overage); + bool ITryStageTransition(double time, float overage); + bool IChooseNextMode(); + + /** Handle a climb message. Note that the "start climbing" climb message is handled + by the human brain, since there's no climb brain there to hear it, since you + (by definition) haven't started climbing yet... */ + inline hsBool IHandleClimbMsg(plClimbMsg *msg); + inline hsBool IHandleLOSMsg(plLOSHitMsg *msg); + + /** Allow or block dismounting in the specified direction. */ + void IEnableDismount(plClimbMsg::Direction dir, bool status); + + /** Allow or block climbing in the specified direction. */ + void IEnableClimb(plClimbMsg::Direction dir, bool status); + + /** Figure out which directions we can go from our current position */ + void ICheckAllowedDirections(); + + /** Look left, right, up, and down to see which directions are clear + for our movement. We could do this by positioning our actual collision + body and testing for hits, but it gives a lot more false positives *and* + we won't get the normals of intersection, so it will be more complex + to figure out which directions are actually blocked. + The approach here is to do a raycast in the aforementioned directions + and fail that direction if the raycast hits anything. */ + void IProbeEnvironment(); + + /** When we probe to see if we can climb in some direction, we need to know + how much room we need in each direction. Right now this is hardcoded, + but we may switch to using the actual animation climb distances. */ + void ICalcProbeLengths(); + + /** Which direction are we trying to go? (up, down, left, or right) + Just looks at keyboard input. */ + void IGetDesiredDirection(); + + /** Let go of the wall and fall. */ + void IRelease(bool intentional); + + /** Decide how far forward or backward to move in the animation */ + float IGetAnimDelta(double time, float elapsed); + + /** Create all our animation stage objects. Doesn't actually apply any of the animations + to the avatar yet. */ + virtual hsBool IInitAnimations(); + + /** Find the animation stage corresponding with a mode */ + plAnimStage * IGetStageFromMode(Mode mode); + Mode IGetModeFromStage(plAnimStage *stage); + + /** The exit stage is a special second stage that runs concurrently with the others. + It's currently used to blend the "falloff" and "release" animations seamlessly + with the others. It has a few limitations: + - it's always expected to play forwards + - it doesn't pay attention to the keyboard + */ + bool IProcessExitStage(double time, float elapsed); + + void IDumpClimbDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); + void IDumpDismountDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); + void IDumpBlockedDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); + + //////////////////////////// + // + // MEMBERS + // + //////////////////////////// + + Mode fCurMode; + Mode fNextMode; + plClimbMsg::Direction fDesiredDirection; // up / down / left / right + float fControlDir; // 1.0 = move current stage forward -1.0 = move current stage back + UInt32 fAllowedDirections; + UInt32 fPhysicallyBlockedDirections; + UInt32 fOldPhysicallyBlockedDirections; // for debug display convenience + UInt32 fAllowedDismounts; + + float fVerticalProbeLength; + float fHorizontalProbeLength; + + // climbing stages + plAnimStage *fUp; + plAnimStage *fDown; + plAnimStage *fLeft; + plAnimStage *fRight; + plAnimStage *fMountUp; + plAnimStage *fMountDown; + plAnimStage *fMountLeft; + plAnimStage *fMountRight; + plAnimStage *fDismountUp; + plAnimStage *fDismountDown; + plAnimStage *fDismountLeft; + plAnimStage *fDismountRight; + plAnimStage *fIdle; + plAnimStage *fRelease; + plAnimStage *fFallOff; + +// /** Current position on the climbing grid. */ +// int fX; +// int fY; + + /** The stage that is currently executing. */ + plAnimStage *fCurStage; + + /** A second stage we use (simultaneously) when we need to blend (fall or release) animations. */ + plAnimStage *fExitStage; + +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCoop.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCoop.cpp new file mode 100644 index 00000000..023b331c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCoop.cpp @@ -0,0 +1,241 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// + +// singular +#include "plAvBrainCoop.h" + +// local +#include "plArmatureMod.h" +#include "plAnimStage.h" +#include "plAvTaskSeek.h" +#include "plAvatarMgr.h" + +// other +#include "../plScene/plSceneNode.h" +#include "../pnNetCommon/plNetApp.h" + +// messages +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plAvCoopMsg.h" +#include "../plMessage/plPickedMsg.h" +#include "../pnMessage/plNotifyMsg.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// STATIC +// +///////////////////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// CODE +// +///////////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plAvBrainCoop +// +///////////////////////////////////////////////////////////////////////////////////////// + +plAvBrainCoop::plAvBrainCoop() +: fInitiatorID(0), + fInitiatorSerial(0) +{ +} + +// plAvBrainCoop ---------------------------------------------------------- +// -------------- +plAvBrainCoop::plAvBrainCoop(UInt32 exitFlags, float fadeIn, float fadeOut, + MoveMode moveMode, plKey guestKey) +: plAvBrainGeneric(exitFlags, fadeIn, fadeOut, moveMode), + fGuestKey(guestKey) +{ + static UInt16 coopSerial = 0; + + // this particular constructor is only called by the initiator... + fInitiatorID = plNetClientApp::GetInstance()->GetPlayerID(); + fInitiatorSerial = coopSerial++; +} + +// plAvBrainCoop ---------------------------------------------------------- +// -------------- +plAvBrainCoop::plAvBrainCoop(UInt32 exitFlags, float fadeIn, float fadeOut, + MoveMode moveMode, UInt32 initiatorID, UInt16 initiatorSerial, + plKey hostKey) +: plAvBrainGeneric(exitFlags, fadeIn, fadeOut, moveMode), + fInitiatorID(initiatorID), fInitiatorSerial(initiatorSerial), + fHostKey(hostKey) +{ +} + +// MsgReceive ---------------------------------- +// ----------- +hsBool plAvBrainCoop::MsgReceive(plMessage *msg) +{ + plPickedMsg *pickM = plPickedMsg::ConvertNoRef(msg); + if(pickM) + { + if(fWaitingForClick) + { + fWaitingForClick = false; + // clicks are never network propagated, so we can be sure that the + // click was performed by the local player. + plKey localPlayer = plNetClientApp::GetInstance()->GetLocalPlayerKey(); + + if(localPlayer == fGuestKey) + { + plAvCoopMsg *coopM = TRACKED_NEW plAvCoopMsg(plAvCoopMsg::kGuestAccepted, fInitiatorID, fInitiatorSerial); + coopM->SetBCastFlag(plMessage::kNetPropagate); + coopM->Send(); + + } + } + } + return plAvBrainGeneric::MsgReceive(msg); +} + +// RelayNotifyMsg ---------------------------------- +// --------------- +bool plAvBrainCoop::RelayNotifyMsg(plNotifyMsg *msg) +{ + // add a coop event so the receiver will know what coop this message is from + msg->AddCoopEvent(fInitiatorID, fInitiatorSerial); + + proMultiStageEventData * mtevt = static_cast(msg->FindEventRecord(proEventData::kMultiStage)); + if(mtevt) + DebugMsg("COOP: Relaying multi-stage event to %d recipients (via plAvBrainCoop)", fRecipients.size()); + + if(fRecipients.size() != 0) + { + bool foundARecipient = false; + for (unsigned curRecipient = 0; curRecipient < fRecipients.size(); curRecipient++) + { + if (fRecipients[curRecipient]) + { + foundARecipient = true; + msg->AddReceiver(fRecipients[curRecipient]); + } + } + + if (!foundARecipient) + return false; + + msg->Send(); + return true; + } + else + return false; +} + + +void plAvBrainCoop::EnableGuestClick() +{ + fWaitingForClick = true; +} + +// GetInitiatorID -------------------- +// --------------- +UInt32 plAvBrainCoop::GetInitiatorID() +{ + return fInitiatorID; +} + +// GetInitiatorSerial -------------------- +// ------------------- +UInt16 plAvBrainCoop::GetInitiatorSerial() +{ + return fInitiatorSerial; +} + +// Read ------------------------------------------------- +// ----- +void plAvBrainCoop::Read(hsStream *stream, hsResMgr *mgr) +{ + plAvBrainGeneric::Read(stream, mgr); + + fInitiatorID = stream->ReadSwap32(); + fInitiatorSerial = stream->ReadSwap16(); + + if(stream->Readbool()) + { + fHostKey = mgr->ReadKey(stream); + } + if(stream->Readbool()) + { + fGuestKey = mgr->ReadKey(stream); + } + fWaitingForClick = stream->Readbool(); + + unsigned numRecipients = stream->ReadSwap16(); + for (unsigned i = 0; i < numRecipients; i++) + fRecipients.push_back(mgr->ReadKey(stream)); +} + +// Write ------------------------------------------------- +// ------ +void plAvBrainCoop::Write(hsStream *stream, hsResMgr *mgr) +{ + plAvBrainGeneric::Write(stream, mgr); + + stream->WriteSwap32(fInitiatorID); + stream->WriteSwap16(fInitiatorSerial); + + bool hasHostKey = (fHostKey != nil); + bool hasGuestKey = (fGuestKey != nil); + + stream->Writebool(hasHostKey); + if(hasHostKey) + mgr->WriteKey(stream, fHostKey); + + stream->Writebool(hasGuestKey); + if(hasGuestKey) + mgr->WriteKey(stream, fGuestKey); + + stream->Writebool(fWaitingForClick); + + stream->WriteSwap16(fRecipients.size()); + for (unsigned i = 0; i < fRecipients.size(); i++) + mgr->WriteKey(stream, fRecipients[i]); +} + +plKey plAvBrainCoop::GetRecipient() +{ + if (fRecipients.size() == 0) + return nil; + return fRecipients[0]; +} + +void plAvBrainCoop::SetRecipient(plKey &recipient) +{ + fRecipients.push_back(recipient); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCoop.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCoop.h new file mode 100644 index 00000000..d181126a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCoop.h @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLAVBRAINGENERIC_H +#define PLAVBRAINGENERIC_H + +#include "hsStlUtils.h" +#include "plAvBrainGeneric.h" + +/** \class plAvBrainCoop + This is currently no different whatsoever from plAvBrainGeneric, + but it's quite possible that it will need to vary, so we're + keeping it around for the moment. */ +class plAvBrainCoop : public plAvBrainGeneric +{ +public: + + // used only by the class factory... + plAvBrainCoop(); + + // use this constructor for a host brain; it sets up the unique ID + plAvBrainCoop(UInt32 exitFlags, float fadeIn, float fadeOut, MoveMode moveMode, plKey guestKey); + + // use this constructor for the guest brain, when you already have the unique ID + plAvBrainCoop(UInt32 exitFlags, float fadeIn, float fadeOut, MoveMode moveMode, + UInt32 initiatorID, UInt16 initiatorSerial, plKey hostKey); + + hsBool MsgReceive(plMessage *msg); + virtual bool RelayNotifyMsg(plNotifyMsg *msg); + void EnableGuestClick(); + + // rtti + CLASSNAME_REGISTER( plAvBrainCoop ); + GETINTERFACE_ANY( plAvBrainCoop, plAvBrainGeneric); + + // i/o + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + // stuff + UInt32 GetInitiatorID(); + UInt16 GetInitiatorSerial(); + + virtual plKey GetRecipient(); + virtual void SetRecipient(plKey &recipient); + +private: + UInt32 fInitiatorID; + UInt16 fInitiatorSerial; + + plKey fGuestKey; // only filled out if we are the host + plKey fHostKey; // only filled out if we are the guest + + bool fWaitingForClick; + + std::vector fRecipients; // we have an array for a slight hack so relto book sharing works +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp new file mode 100644 index 00000000..e024847f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.cpp @@ -0,0 +1,726 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" +#include "hsWindows.h" + +#include "plAvCallbackAction.h" +#include "plAvBrainCritter.h" +#include "plAvBrainHuman.h" +#include "plArmatureMod.h" +#include "plAvBehaviors.h" +#include "plAGAnim.h" +#include "plAGAnimInstance.h" +#include "plAvatarMgr.h" + +#include "plgDispatch.h" + +#include "../plMessage/plAIMsg.h" + +#include "../plPipeline/plDebugText.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plMath/plRandom.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetTransport/plNetTransportMember.h" + +/////////////////////////////////////////////////////////////////////////////// + +static plRandom sRandom; // random number generator + +const char kDefaultIdleAnimName[] = "Idle"; +const char kDefaultIdleBehName[] = "Idle"; +const char kDefaultRunAnimName[] = "Run"; +const char kDefaultRunBehName[] = "Run"; + +const float kLoudSoundMultiplyer = 2.0f; + +/////////////////////////////////////////////////////////////////////////////// + +class CritterBehavior : public plArmatureBehavior +{ + friend class plAvBrainCritter; + +public: + CritterBehavior(const std::string& name, bool randomStart = false, float fadeInLength = 2.f, float fadeOutLength = 2.f) : plArmatureBehavior(), + fAvMod(nil), fCritterBrain(nil), fName(name), fRandomStartPoint(randomStart), fFadeInLength(fadeInLength), fFadeOutLength(fadeOutLength) {} + virtual ~CritterBehavior() {} + + void Init(plAGAnim* anim, hsBool loop, plAvBrainCritter* brain, plArmatureMod* body, UInt8 index) + { + plArmatureBehavior::Init(anim, loop, brain, body, index); + fAvMod = body; + fCritterBrain = brain; + fAnimName = anim->GetName(); + } + + virtual hsBool PreCondition(double time, float elapsed) {return true;} + + hsScalar GetAnimLength() {return (fAnim->GetAnimation()->GetLength());} + void SetAnimTime(hsScalar time) {fAnim->SetCurrentTime(time, true);} + + std::string Name() const {return fName;} + std::string AnimName() const {return fAnimName;} + bool RandomStartPoint() const {return fRandomStartPoint;} + float FadeInLength() const {return fFadeInLength;} + float FadeOutLength() const {return fFadeOutLength;} + +protected: + virtual void IStart() + { + plArmatureBehavior::IStart(); + fAvMod->SynchIfLocal(hsTimer::GetSysSeconds(), false); + } + + virtual void IStop() + { + plArmatureBehavior::IStop(); + fAvMod->SynchIfLocal(hsTimer::GetSysSeconds(), false); + } + + plArmatureMod *fAvMod; + plAvBrainCritter *fCritterBrain; + + std::string fName; // user-created name for this behavior, also used as the index into the brain's behavior map + std::string fAnimName; // physical animation's name, for reference + bool fRandomStartPoint; // do we want this behavior to start at a random frame every time we start it? + float fFadeInLength; // how long to fade in this behavior + float fFadeOutLength; // how long to fade out this behavior +}; + +/////////////////////////////////////////////////////////////////////////////// + +plAvBrainCritter::plAvBrainCritter(): fCallbackAction(nil), fCurMode(kIdle), fNextMode(kIdle), fFadingNextBehavior(true), + fLocallyControlled(false), fAvoidingAvatars(false), fFinalGoalPos(0, 0, 0), fImmediateGoalPos(0, 0, 0), fDotGoal(0), + fAngRight(0) +{ + SightCone(hsScalarPI/2); // 90deg + StopDistance(1); + SightDistance(10); + HearingDistance(10); +} + +plAvBrainCritter::~plAvBrainCritter() +{ + for (int i = 0; i < fBehaviors.GetCount(); ++i) + { + delete fBehaviors[i]; + fBehaviors[i] = nil; + } + + delete fCallbackAction; + fCallbackAction = nil; + + fUserBehaviors.clear(); + fReceivers.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// + +hsBool plAvBrainCritter::Apply(double time, hsScalar elapsed) +{ + // update internal pathfinding variables + IEvalGoal(); + + if (fNextMode >= kIdle) + { + // next mode is set, fade out the previous mode and start up the new one + IFadeOutBehavior(); + IStartBehavior(); + } + else + IProcessBehavior(time, elapsed); // just continue with the currently running one + + // update our controller to keep us turned and moving to where we want to go + fCallbackAction->RecalcVelocity(time, time - elapsed); + fCallbackAction->SetTurnStrength(IGetTurnStrength(time)); + + return plArmatureBrain::Apply(time, elapsed); +} + +hsBool plAvBrainCritter::MsgReceive(plMessage* msg) +{ + return plArmatureBrain::MsgReceive(msg); +} + +/////////////////////////////////////////////////////////////////////////////// + +void plAvBrainCritter::Activate(plArmatureModBase* avMod) +{ + plArmatureBrain::Activate(avMod); + + // initialize our base "Run" and "Idle" behaviors + IInitBaseAnimations(); + + // create the controller if we haven't done so already + if (!fCallbackAction) + { + plSceneObject* avObj = fArmature->GetTarget(0); + plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); + plPhysicalControllerCore* controller = avMod->GetController(); + fCallbackAction = TRACKED_NEW plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); + fCallbackAction->ActivateController(); + } + + // tell people that care that we are good to go + plAIBrainCreatedMsg* brainCreated = TRACKED_NEW plAIBrainCreatedMsg(fArmature->GetKey()); + plgDispatch::MsgSend(brainCreated); +} + +void plAvBrainCritter::Deactivate() +{ + plArmatureBrain::Deactivate(); +} + +void plAvBrainCritter::Suspend() +{ + // fade out the previous behavior + CritterBehavior *behavior = (CritterBehavior*)fBehaviors[fCurMode]; + behavior->SetStrength(0.f, fFadingNextBehavior ? behavior->FadeOutLength() : 0.f); + + // fade in the idle + fNextMode = kIdle; + + plArmatureBrain::Suspend(); +} + +void plAvBrainCritter::Resume() +{ + // fade in the idle + fNextMode = kIdle; + + fCallbackAction->Reset(false); + + plArmatureBrain::Resume(); +} + +void plAvBrainCritter::AddBehavior(const std::string& animationName, const std::string& behaviorName, bool loop /* = true */, bool randomStartPos /* = true */, + float fadeInLen /* = 2.f */, float fadeOutLen /* = 2.f */) +{ + // grab the animations + plAGAnim* anim = fAvMod->FindCustomAnim(animationName.c_str()); + if (!anim) + return; // can't find it, die + + // create the behavior and set it up + CritterBehavior* behavior = TRACKED_NEW CritterBehavior(behaviorName, randomStartPos, fadeInLen, fadeOutLen); + fBehaviors.Push(behavior); + behavior->Init(anim, loop, this, fAvMod, fBehaviors.Count() - 1); + fUserBehaviors[behaviorName].push_back(fBehaviors.Count() - 1); +} + +void plAvBrainCritter::StartBehavior(const std::string& behaviorName, bool fade /* = true */) +{ + // make sure the new behavior exists + if (fUserBehaviors.find(behaviorName) == fUserBehaviors.end()) + return; + else + { + if (fUserBehaviors[behaviorName].size() == 0) + return; + } + + // remember the fade request + fFadingNextBehavior = fade; + + // pick our next behavior + fNextMode = IPickBehavior(behaviorName); +} + +bool plAvBrainCritter::RunningBehavior(const std::string& behaviorName) const +{ + // make sure the behavior exists + std::map >::const_iterator behaviorIterator = fUserBehaviors.find(behaviorName); + if (behaviorIterator == fUserBehaviors.end()) + return false; + else + { + if (behaviorIterator->second.size() == 0) + return false; + } + + // check all behaviors that use this tag and return true if we are running one of them + for (unsigned i = 0; i < behaviorIterator->second.size(); ++i) + { + if (fCurMode == behaviorIterator->second[i]) + return true; + } + return false; +} + +std::string plAvBrainCritter::BehaviorName(int behavior) const +{ + if ((behavior >= fBehaviors.Count()) || (behavior < 0)) + return ""; + return ((CritterBehavior*)fBehaviors[behavior])->Name(); +} + +std::string plAvBrainCritter::AnimationName(int behavior) const +{ + if ((behavior >= fBehaviors.Count()) || (behavior < 0)) + return ""; + return ((CritterBehavior*)fBehaviors[behavior])->AnimName(); +} + +std::string plAvBrainCritter::IdleBehaviorName() const +{ + return kDefaultIdleBehName; +} + +std::string plAvBrainCritter::RunBehaviorName() const +{ + return kDefaultRunBehName; +} + +void plAvBrainCritter::GoToGoal(hsPoint3 newGoal, bool avoidingAvatars /* = false */) +{ + fFinalGoalPos = newGoal; + fAvoidingAvatars = avoidingAvatars; + fNextMode = IPickBehavior(kRun); + // TODO: Pathfinding here! +} + +bool plAvBrainCritter::AtGoal() const +{ + // we are at our goal if our distance from it is less then or equal to our stopping distance + hsPoint3 creaturePos; + hsQuat creatureRot; + fAvMod->GetPositionAndRotationSim(&creaturePos, &creatureRot); + hsVector3 finalGoalVec(creaturePos - fFinalGoalPos); + return (finalGoalVec.MagnitudeSquared() <= fStopDistanceSquared); +} + +void plAvBrainCritter::SightCone(hsScalar coneRad) +{ + fSightConeAngle = coneRad; + + // calculate the minimum dot product for the cone of sight (angle/2 vector dotted with straight ahead) + hsVector3 straightVector(1, 0, 0), viewVector(1, 0, 0); + hsQuat rotation(fSightConeAngle/2, &hsVector3(0, 1, 0)); + viewVector = hsVector3(rotation.Rotate(&viewVector)); + viewVector.Normalize(); + fSightConeDotMin = straightVector * viewVector; +} + +void plAvBrainCritter::HearingDistance(hsScalar hearDis) +{ + fHearingDistance = hearDis; + fHearingDistanceSquared = fHearingDistance * fHearingDistance; + fLoudHearingDistanceSquared = (fHearingDistance * kLoudSoundMultiplyer) * (fHearingDistance * kLoudSoundMultiplyer); +} + +bool plAvBrainCritter::CanSeeAvatar(unsigned long id) const +{ + plArmatureMod* avatar = plAvatarMgr::GetInstance()->FindAvatarByPlayerID(id); + if (avatar) + return ICanSeeAvatar(avatar); + return false; +} + +bool plAvBrainCritter::CanHearAvatar(unsigned long id) const +{ + plArmatureMod* avatar = plAvatarMgr::GetInstance()->FindAvatarByPlayerID(id); + if (avatar) + return ICanHearAvatar(avatar); + return false; +} + +std::vector plAvBrainCritter::PlayersICanSee() const +{ + std::vector allPlayers = IGetAgePlayerIDList(); + std::vector onesICanSee; + for (unsigned i = 0; i < allPlayers.size(); ++i) + { + if (CanSeeAvatar(allPlayers[i])) + onesICanSee.push_back(allPlayers[i]); + } + return onesICanSee; +} + +std::vector plAvBrainCritter::PlayersICanHear() const +{ + std::vector allPlayers = IGetAgePlayerIDList(); + std::vector onesICanHear; + for (unsigned i = 0; i < allPlayers.size(); ++i) + { + if (CanHearAvatar(allPlayers[i])) + onesICanHear.push_back(allPlayers[i]); + } + return onesICanHear; +} + +hsVector3 plAvBrainCritter::VectorToPlayer(unsigned long id) const +{ + plArmatureMod* avatar = plAvatarMgr::GetInstance()->FindAvatarByPlayerID(id); + if (!avatar) + return hsVector3(0, 0, 0); + + hsPoint3 avPos; + hsQuat avRot; + avatar->GetPositionAndRotationSim(&avPos, &avRot); + + hsPoint3 creaturePos; + hsQuat creatureRot; + fAvMod->GetPositionAndRotationSim(&creaturePos, &creatureRot); + + return hsVector3(creaturePos - avPos); +} + +void plAvBrainCritter::AddReceiver(const plKey key) +{ + for (unsigned i = 0; i < fReceivers.size(); ++i) + { + if (fReceivers[i] == key) + return; // already in our list + } + fReceivers.push_back(key); +} + +void plAvBrainCritter::RemoveReceiver(const plKey key) +{ + for (unsigned i = 0; i < fReceivers.size(); ++i) + { + if (fReceivers[i] == key) + { + fReceivers.erase(fReceivers.begin() + i); + return; + } + } + return; // not found, do nothing +} + +void plAvBrainCritter::DumpToDebugDisplay(int& x, int& y, int lineHeight, char* strBuf, plDebugText& debugTxt) +{ + sprintf(strBuf, "Brain type: Critter"); + debugTxt.DrawString(x, y, strBuf, 0, 255, 255); + y += lineHeight; + + // extract the name from the behavior running + if (fBehaviors[fCurMode]) + sprintf(strBuf, "Mode: %s", ((CritterBehavior*)(fBehaviors[fCurMode]))->Name().c_str()); + else + sprintf(strBuf, "Mode: Unknown"); + + // draw it + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + for (int i = 0; i < fBehaviors.GetCount(); ++i) + fBehaviors[i]->DumpDebug(x, y, lineHeight, strBuf, debugTxt); +} + +/////////////////////////////////////////////////////////////////////////////// + +hsBool plAvBrainCritter::IInitBaseAnimations() +{ + // create the basic idle and run behaviors, and put them into our appropriate structures + plAGAnim* idle = fAvMod->FindCustomAnim(kDefaultIdleAnimName); + plAGAnim* run = fAvMod->FindCustomAnim(kDefaultRunAnimName); + + hsAssert(idle, "Creature is missing idle animation"); + hsAssert(run, "Creature is missing run animation"); + + fBehaviors.SetCountAndZero(kNumDefaultModes); + + CritterBehavior* behavior; + if (idle) + { + fBehaviors[kIdle] = behavior = TRACKED_NEW CritterBehavior(kDefaultIdleBehName, true); // starts at a random start point each time + behavior->Init(idle, true, this, fAvMod, kIdle); + fUserBehaviors[kDefaultIdleBehName].push_back(kIdle); + } + + if (run) + { + fBehaviors[kRun] = behavior = TRACKED_NEW CritterBehavior(kDefaultRunBehName); + behavior->Init(run, true, this, fAvMod, kRun); + fUserBehaviors[kDefaultRunBehName].push_back(kRun); + } + + return true; +} + +int plAvBrainCritter::IPickBehavior(int behavior) const +{ + if ((behavior >= fBehaviors.Count()) || (behavior < 0)) + return IPickBehavior(kDefaultIdleBehName); // do an idle if the behavior is invalid + + CritterBehavior* behaviorObj = (CritterBehavior*)(fBehaviors[behavior]); + return IPickBehavior(behaviorObj->Name()); +} + +int plAvBrainCritter::IPickBehavior(const std::string& behavior) const +{ + // make sure the behavior exists + std::map >::const_iterator behaviorIterator = fUserBehaviors.find(behavior); + if (behaviorIterator == fUserBehaviors.end()) + { + if (behavior != kDefaultIdleBehName) + return IPickBehavior(kDefaultIdleBehName); // do an idle if the behavior is invalid + return -1; // can't recover from being unable to find an idle! + } + else + { + unsigned numBehaviors = behaviorIterator->second.size(); + if (numBehaviors == 0) + { + if (behavior != kDefaultIdleBehName) + return IPickBehavior(kDefaultIdleBehName); // do an idle if the behavior is invalid + return -1; // can't recover from being unable to find an idle! + } + + // pick our behavior + unsigned index = sRandom.RandRangeI(0, numBehaviors - 1); + return behaviorIterator->second[index]; + } +} + +void plAvBrainCritter::IFadeOutBehavior() +{ + if ((fCurMode >= fBehaviors.Count()) || (fCurMode < 0)) + return; // invalid fCurMode + + // fade out currently playing behavior + CritterBehavior* behavior = (CritterBehavior*)fBehaviors[fCurMode]; + behavior->SetStrength(0.f, fFadingNextBehavior ? behavior->FadeOutLength() : 0.f); +} + +void plAvBrainCritter::IStartBehavior() +{ + if ((fNextMode >= fBehaviors.Count()) || (fNextMode < 0)) + return; // invalid fNextMode + + // fade in our behavior + CritterBehavior* behavior = (CritterBehavior*)fBehaviors[fNextMode]; + behavior->SetStrength(1.f, fFadingNextBehavior ? behavior->FadeInLength() : 0.f); + + // if we start at a random point, do so + if (behavior->RandomStartPoint()) + { + hsScalar newStart = sRandom.RandZeroToOne() * behavior->GetAnimLength(); + behavior->SetAnimTime(newStart); + } + + // clean up the internal variables + fCurMode = fNextMode; + fNextMode = -1; +} + +void plAvBrainCritter::IProcessBehavior(double time, float elapsed) +{ + // run the currently running behavior + CritterBehavior* behavior = (CritterBehavior*)fBehaviors[fCurMode]; + behavior->SetStrength(1.f, fFadingNextBehavior ? behavior->FadeInLength() : 0.f); + behavior->Process(time, elapsed); +} + +void plAvBrainCritter::IEvalGoal() +{ + // TODO: Implement pathfinding logic here + // (for now, this runs directly towards the goal) + fImmediateGoalPos = fFinalGoalPos; + + // where am I relative to my goal? + const plSceneObject* creatureObj = fArmature->GetTarget(0); + hsVector3 view(creatureObj->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView)); + + hsPoint3 creaturePos; + hsQuat creatureRot; + fAvMod->GetPositionAndRotationSim(&creaturePos, &creatureRot); + hsVector3 goalVec(creaturePos - fImmediateGoalPos); + goalVec.Normalize(); + fDotGoal = goalVec * view; // 1 = directly facing, 0 = 90 deg off, -1 = facing away + + // calculate a vector pointing to the creature's right + hsQuat invRot = creatureRot.Conjugate(); + hsPoint3 globRight = invRot.Rotate(&kAvatarRight); + fAngRight = globRight.InnerProduct(goalVec); // dot product, 1 = goal is 90 to the right, 0 = goal is in front or behind, -1 = goal is 90 to the left + + if (fAvoidingAvatars) + { + // check to see we can see anyone in our way (if we can't see them, we can't avoid them) + std::vector playersICanSee = IAvatarsICanSee(); + for (unsigned i = 0; i < playersICanSee.size(); ++i) + { + hsPoint3 avPos; + hsQuat avRot; + playersICanSee[i]->GetPositionAndRotationSim(&avPos, &avRot); + hsVector3 avVec(creaturePos - avPos); + avVec.Normalize(); + + hsScalar dotAv = avVec * goalVec; + if (dotAv > 0.5f) // within a 45deg angle in front of us + { + // a player is in the way, so we will change our "goal" to a 90deg angle from the player + // then we stop searching, since any other players in the way will just produce the same (or similar) result + avVec.fZ = goalVec.fZ = 0.f; + goalVec = goalVec % avVec; + fAngRight = globRight.InnerProduct(goalVec); + break; + } + } + } + + // are we at our final goal? + if (AtGoal()) + { + if (RunningBehavior(kDefaultRunBehName)) // don't do anything if we're not running! + { + // we're close enough, stop running and pick an idle + fNextMode = IPickBehavior(kIdle); + + // tell everyone who cares that we have arrived + for (unsigned i = 0; i < fReceivers.size(); ++i) + { + plAIArrivedAtGoalMsg* msg = TRACKED_NEW plAIArrivedAtGoalMsg(fArmature->GetKey(), fReceivers[i]); + msg->Goal(fFinalGoalPos); + msg->Send(); + } + } + } +} + +hsScalar plAvBrainCritter::IGetTurnStrength(double time) const +{ + if (!RunningBehavior(kDefaultRunBehName)) + return 0.0f; + + // am I directly facing my goal? + if (fDotGoal < -0.98) + return 0.f; + + if (fAngRight > 0.f) + return 1.f; + return -1.f; +} + +std::vector plAvBrainCritter::IGetAgePlayerIDList() const +{ + // make a list of non-local players + std::vector playerIDs; + std::map tempMap; // slightly hacky way to remove dups + plNetClientMgr* nc = plNetClientMgr::GetInstance(); + for (int i = 0; i < nc->TransportMgr().GetNumMembers(); ++i) + { + plNetTransportMember* mbr = nc->TransportMgr().GetMember(i); + unsigned long id = mbr->GetPlayerID(); + if (tempMap.find(id) == tempMap.end()) + { + playerIDs.push_back(id); + tempMap[id] = true; + } + } + // add the local player if he isn't already in the list + unsigned long localID = nc->GetPlayerID(); + if (tempMap.find(localID) == tempMap.end()) + playerIDs.push_back(localID); + + // return result + return playerIDs; +} + +bool plAvBrainCritter::ICanSeeAvatar(plArmatureMod* avatar) const +{ + // sight is a x deg cone in front of the critter, cuts off at a certain distance + hsPoint3 avPos; + hsQuat avRot; + avatar->GetPositionAndRotationSim(&avPos, &avRot); + + hsPoint3 creaturePos; + hsQuat creatureRot; + fAvMod->GetPositionAndRotationSim(&creaturePos, &creatureRot); + + hsVector3 avVec(creaturePos - avPos); + if (avVec.MagnitudeSquared() > fSightDistanceSquared) + return false; // too far away + avVec.Normalize(); + + const plSceneObject* creatureObj = fArmature->GetTarget(0); + hsVector3 view(creatureObj->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView)); + hsScalar avDot = view * avVec; + if (avDot < fSightConeDotMin) + return false; // out of our cone of view + return true; +} + +bool plAvBrainCritter::ICanHearAvatar(plArmatureMod* avatar) const +{ + // check to see if the avatar is being loud (running or jumping) + bool isLoud = false; + plAvBrainHuman* humanBrain = plAvBrainHuman::ConvertNoRef(avatar->FindBrainByClass(plAvBrainHuman::Index())); + if (humanBrain) + { + isLoud = humanBrain->IsBehaviorPlaying(plAvBrainHuman::kRun) || humanBrain->IsBehaviorPlaying(plAvBrainHuman::kStandingJump) || + humanBrain->IsBehaviorPlaying(plAvBrainHuman::kWalkingJump) || humanBrain->IsBehaviorPlaying(plAvBrainHuman::kRunningJump) || + humanBrain->IsBehaviorPlaying(plAvBrainHuman::kGroundImpact) || humanBrain->IsBehaviorPlaying(plAvBrainHuman::kRunningImpact); + } + + // hearing is 360 degrees around the critter, cuts off at a certain distance + hsPoint3 avPos; + hsQuat avRot; + avatar->GetPositionAndRotationSim(&avPos, &avRot); + + hsPoint3 creaturePos; + hsQuat creatureRot; + fAvMod->GetPositionAndRotationSim(&creaturePos, &creatureRot); + + hsVector3 avVec(creaturePos - avPos); + hsScalar distSq = avVec.MagnitudeSquared(); + if (distSq <= fHearingDistanceSquared) + return true; // within our normal hearing distance + else if (isLoud && (distSq <= fLoudHearingDistanceSquared)) + return true; // they are being loud, and within our loud hearing distance + return false; +} + +std::vector plAvBrainCritter::IAvatarsICanSee() const +{ + std::vector allPlayers = IGetAgePlayerIDList(); + std::vector onesICanSee; + for (unsigned i = 0; i < allPlayers.size(); ++i) + { + plArmatureMod* avatar = plAvatarMgr::GetInstance()->FindAvatarByPlayerID(allPlayers[i]); + if (!avatar) + continue; + + if (ICanSeeAvatar(avatar)) + onesICanSee.push_back(avatar); + } + return onesICanSee; +} + +std::vector plAvBrainCritter::IAvatarsICanHear() const +{ + std::vector allPlayers = IGetAgePlayerIDList(); + std::vector onesICanHear; + for (unsigned i = 0; i < allPlayers.size(); ++i) + { + plArmatureMod* avatar = plAvatarMgr::GetInstance()->FindAvatarByPlayerID(allPlayers[i]); + if (!avatar) + continue; + + if (ICanHearAvatar(avatar)) + onesICanHear.push_back(avatar); + } + return onesICanHear; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h new file mode 100644 index 00000000..3f663cb9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainCritter.h @@ -0,0 +1,168 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_AV_BRAIN_CRITTER_H +#define PL_AV_BRAIN_CRITTER_H + +#include "plAvBrain.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" + +class plArmatureMod; +class plWalkingController; +class plAIMsg; + +class plAvBrainCritter : public plArmatureBrain +{ +public: + enum CritterMode + { + kIdle = 0, + kRun, + kNumDefaultModes + // anything >= kNumDefaultModes is user + }; + + enum Anims + { + kIdleAnim = 0, + kRunAnim, + kNumDefaultAnims + // anything >= kNumDefaultAnims this is user + }; + + plAvBrainCritter(); + virtual ~plAvBrainCritter(); + + CLASSNAME_REGISTER(plAvBrainCritter); + GETINTERFACE_ANY(plAvBrainCritter, plArmatureBrain); + + hsBool Apply(double time, hsScalar elapsed); + hsBool MsgReceive(plMessage* msg); + + virtual void Activate(plArmatureModBase* avMod); + virtual void Deactivate(); + virtual void Suspend(); + virtual void Resume(); + + void AddBehavior(const std::string& animationName, const std::string& behaviorName, bool loop = true, bool randomStartPos = true, + float fadeInLen = 2.f, float fadeOutLen = 2.f); + void StartBehavior(const std::string& behaviorName, bool fade = true); + bool RunningBehavior(const std::string& behaviorName) const; + + void LocallyControlled(bool local) {fLocallyControlled = local;} + bool LocallyControlled() const {return fLocallyControlled;} + + std::string BehaviorName(int behavior) const; + std::string AnimationName(int behavior) const; + int CurBehavior() const {return fCurMode;} + int NextBehavior() const {return fNextMode;} + + std::string IdleBehaviorName() const; + std::string RunBehaviorName() const; + + void GoToGoal(hsPoint3 newGoal, bool avoidingAvatars = false); + hsPoint3 CurrentGoal() const {return fFinalGoalPos;} + bool AvoidingAvatars() const {return fAvoidingAvatars;} + bool AtGoal() const; + + void StopDistance(hsScalar stopDistance) {fStopDistance = stopDistance; fStopDistanceSquared = fStopDistance * fStopDistance;} + hsScalar StopDistance() const {return fStopDistance;} + + void SightCone(hsScalar coneRad); + hsScalar SightCone() const {return fSightConeAngle;} + void SightDistance(hsScalar sightDis) {fSightDistance = sightDis; fSightDistanceSquared = fSightDistance * fSightDistance;} + hsScalar SightDistance() const {return fSightDistance;} + void HearingDistance(hsScalar hearDis); + hsScalar HearingDistance() const {return fHearingDistance;} + + bool CanSeeAvatar(unsigned long id) const; + bool CanHearAvatar(unsigned long id) const; + + std::vector PlayersICanSee() const; + std::vector PlayersICanHear() const; + + hsVector3 VectorToPlayer(unsigned long id) const; + + void AddReceiver(const plKey key); + void RemoveReceiver(const plKey key); + + virtual void DumpToDebugDisplay(int& x, int& y, int lineHeight, char* strBuf, plDebugText& debugTxt); + + plWalkingController* GetCallbackAction() {return fCallbackAction;} + + // For the console + static bool fDrawDebug; + +protected: + virtual hsBool IInitBaseAnimations(); + + int IPickBehavior(int behavior) const; + int IPickBehavior(const std::string& behavior) const; + + void IFadeOutBehavior(); // fades out fCurMode + void IStartBehavior(); // fades in and initializes fNextMode, then sets fCurMode + void IProcessBehavior(double time, float elapsed); // processes fCurMode + void IEvalGoal(); + hsScalar IGetTurnStrength(double time) const; + + std::vector IGetAgePlayerIDList() const; + + bool ICanSeeAvatar(plArmatureMod* avatar) const; + bool ICanHearAvatar(plArmatureMod* avatar) const; + + std::vector IAvatarsICanSee() const; + std::vector IAvatarsICanHear() const; + + plWalkingController* fCallbackAction; + int fCurMode; // current behavior we are running + int fNextMode; // the next behavior to run (-1 if we aren't switching on next eval) + bool fFadingNextBehavior; // is the next behavior supposed to blend? + + bool fLocallyControlled; // is our local AI script the one making all the choices? + + bool fAvoidingAvatars; // are we avoiding avatars to the best of our ability when pathfinding? + hsPoint3 fFinalGoalPos; // the location we are pathfinding to + hsPoint3 fImmediateGoalPos; // the location of the point we are immediately going towards (not necessarily our final goal) + hsScalar fDotGoal; // dot product to our goal + hsScalar fAngRight; // dot product of our local right-hand vector to our goal + + hsScalar fStopDistance; // how close we need to get to our goal before stopping + hsScalar fStopDistanceSquared; // the above, squared, for faster calculation + + hsScalar fSightConeAngle; // in radians, the width of the cone we can see (/2 on each side of where we face, so 90deg cone is 45deg on each side) + hsScalar fSightConeDotMin; // the minimum dot-product of the cone we can see (1 - straight ahead only, 0 - 90deg either side, -1 - 180 behind, or full 360) + hsScalar fSightDistance; // how far away we can see (cone in front of us) + hsScalar fSightDistanceSquared; // the above, squared, for faster calculation + hsScalar fHearingDistance; // how far away we can hear (360 degrees) + hsScalar fHearingDistanceSquared; // the above, squared, for faster calculation + hsScalar fLoudHearingDistanceSquared; // how far away we can hear loud noises, squared, for faster calculation + + std::map > fUserBehaviors; // string is behavior name, internal vector is the list of behaviors that are randomly picked from + + std::vector fReceivers; // list of people that want messages from us +}; + +#endif // PL_AV_BRAIN_CRITTER_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp new file mode 100644 index 00000000..4f53e32e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.cpp @@ -0,0 +1,183 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// local includes +#include "plAvBrainDrive.h" +#include "plArmatureMod.h" +#include "plAvCallbackAction.h" + +// global includes +#include "hsTimer.h" +#include "hsGeometry3.h" + +// other includes +#include "hsQuat.h" +#include "../plMessage/plSimStateMsg.h" +#include "../pnMessage/plCameraMsg.h" + +// messages +#include "../plMessage/plInputEventMsg.h" + +// CTOR default +plAvBrainDrive::plAvBrainDrive() +: fMaxVelocity(20), fTurnRate(1) +{ + +} + +// CTOR max velocity, turn rate +plAvBrainDrive::plAvBrainDrive(hsScalar maxVelocity, hsScalar turnRate) +: fMaxVelocity(maxVelocity), fTurnRate(turnRate) +{ +} + +// ACTIVATE +void plAvBrainDrive::Activate(plArmatureModBase *avMod) +{ + plArmatureBrain::Activate(avMod); + + IEnablePhysics(false, avMod->GetTarget(0)->GetKey()); + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetCmd(plCameraMsg::kNonPhysOn); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->Send(); +} + +// DEACTIVATE +void plAvBrainDrive::Deactivate() +{ + if (fAvMod) + { + IEnablePhysics(true, fAvMod->GetTarget(0)->GetKey()); + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetCmd(plCameraMsg::kNonPhysOff); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->Send(); + } +} + +void plAvBrainDrive::IEnablePhysics(bool enable, plKey avKey) +{ + fAvMod->EnablePhysics(enable); +} + +// APPLY +hsBool plAvBrainDrive::Apply(double timeNow, hsScalar elapsed) +{ + plSceneObject * avSO = fAvMod->GetTarget(0); + hsScalar eTime = hsTimer::GetDelSysSeconds(); + hsMatrix44 targetMatrix = avSO->GetLocalToWorld(); + + hsPoint3 playerPos = targetMatrix.GetTranslate(); + hsVector3 view, up, right; + targetMatrix.GetAxis(&view, &up, &right); + hsScalar speed = fMaxVelocity; + hsScalar turn = fTurnRate; + + if (fAvMod->FastKeyDown()) + { + turn *= 0.25; + speed *= 3.5; + } + if (fAvMod->GetInputFlag(B_CONTROL_MOVE_FORWARD)) + { + playerPos += view * speed * eTime; + } + if (fAvMod->GetInputFlag(B_CONTROL_MOVE_BACKWARD)) + { + playerPos += view * speed * eTime * -1; + } + if (fAvMod->StrafeLeftKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnLeftKeyDown())) + { + playerPos += right * speed * eTime * -1; + } + if (fAvMod->StrafeRightKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnRightKeyDown())) + { + playerPos += right * speed * eTime; + } + if (fAvMod->GetInputFlag(B_CONTROL_MOVE_DOWN)) + { + playerPos += up * speed * eTime * -1; + } + if (fAvMod->GetInputFlag(B_CONTROL_MOVE_UP)) + { + playerPos += up * speed * eTime; + } + + hsPoint3 desiredPosition = playerPos; + // calculate rotation matrix + + hsVector3 rotUp(0,0,1); + hsVector3 rotRight(1,0,0); + hsMatrix44 rot; + hsScalar angle = 0; + + if ( fAvMod->GetInputFlag( B_CONTROL_ROTATE_RIGHT ) || fAvMod->GetInputFlag( B_CONTROL_ROTATE_LEFT ) || fAvMod->GetInputFlag( A_CONTROL_TURN ) ) + { + angle = fTurnRate * eTime * fAvMod->GetTurnStrength(); + } + + hsMatrix44 justRot(targetMatrix); + hsPoint3 zero(0,0,0); + justRot.SetTranslate(&zero); + + if( angle ) { + hsQuat q( angle, &rotUp ); + q.NormalizeIfNeeded(); + q.MakeMatrix( &rot ); + + justRot = rot * justRot; + + targetMatrix = rot * targetMatrix; + } + + // use the desired rotation matrix to set position and rotation: + hsMatrix44 inv; + targetMatrix.SetTranslate( &desiredPosition ); + targetMatrix.GetInverse( &inv ); + avSO->SetTransform( targetMatrix, inv ); + avSO->FlushTransform(); + + return true; +} + +// IHANDLECONTROLMSG +hsBool plAvBrainDrive::MsgReceive(plMessage* msg) +{ + plControlEventMsg *ctrlMsg = plControlEventMsg::ConvertNoRef(msg); + if(ctrlMsg) + { + if( ctrlMsg->ControlActivated() ) + { + if(ctrlMsg->GetControlCode() == B_CONTROL_TOGGLE_PHYSICAL) + { + fAvMod->PopBrain(); + return true; + } + } + } + return false; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.h new file mode 100644 index 00000000..a0245755 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainDrive.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/** \file plAvBrainDrive.h + */ +#ifndef AVBRAINDRIVE_INC +#define AVBRAINDRIVE_INC + +// base class +#include "plAvBrain.h" + +/** A simple brain used to steer the player around without regard + for physics. Used in production to quickly fly through a scene + without having to actually solve puzzles, jump, etc. + The 'Drive Brain' uses the same input keys as the avatar, with + a few secret additions for convenience. At the time of this + writing, you invoke the drive brain by pressing shift-P, and + then use the forward and back arrows to move and th e left and + right arrows to rotate. The 'u' and 'j' keys will move your avatar + vertically. + Gravity and collision are completely suspended for avatars in + drive mode, but they *can* still trigger region detectors if + you need to fire off triggers, puzzles, or whatnot. + Note that the drive brain inherits from the user brain, which + parses control input for us. +*/ +class plAvBrainDrive : public plArmatureBrain +{ +public: + plAvBrainDrive(); + /** Canonical constructer. Use this one. + \param maxVelocity The highest speed this avatar can fly at. + \param turnRate The speed at which we will turn, in radians per second. + */ + plAvBrainDrive(hsScalar maxVelocity, hsScalar turnRate); + + + // BRAIN PROTOCOL + /** Suspend physics and get in line to receive keyboard control messages. */ + virtual void Activate(plArmatureModBase *avMod); + + /** Restore physical reality and stop handling input messages */ + virtual void Deactivate(); + + /** Look at the key states and figure out if and how we should move */ + virtual hsBool Apply(double timeNow, hsScalar elapsed); // main control loop. called by avatar eval() + + // the user brain base handles most of the details of control messages, + // so this function just looks for the special command which gets us out + // of drive mode. + virtual hsBool MsgReceive(plMessage* pMsg); // handle control input from the user + + CLASSNAME_REGISTER( plAvBrainDrive ); + GETINTERFACE_ANY( plAvBrainDrive, plArmatureBrain ); + +protected: + void IEnablePhysics(bool enable, plKey avKey); + + hsScalar fMaxVelocity; + hsScalar fTurnRate; +}; + +#endif // AVBRAINDRIVE_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp new file mode 100644 index 00000000..bfaa5020 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.cpp @@ -0,0 +1,957 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAvCallbackAction.h" // havok-contaminated file: must go first + +// singular +#include "plAvBrainGeneric.h" + +// local +#include "plAnimStage.h" +#include "plArmatureMod.h" +// #include "plAvatarTasks.h" +#include "plAvTask.h" +#include "plAvTaskBrain.h" +#include "plAvBrainHuman.h" +#include "plAGAnimInstance.h" +#include "plMatrixChannel.h" + +// global +#include "hsTimer.h" +#include "plgDispatch.h" + +// other +#include "../pnNetCommon/plSDLTypes.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plSimStateMsg.h" +#include "../plMessage/plConsoleMsg.h" +#include "../plPipeline/plDebugText.h" +#include "../plInputCore/plAvatarInputInterface.h" +#include "../plMessage/plInputIfaceMgrMsg.h" + +#ifdef DEBUG_MULTISTAGE +#include "plAvatarMgr.h" +#include "../plStatusLog/plStatusLog.h" +#endif + +hsBool plAvBrainGeneric::fForce3rdPerson = true; +const hsScalar plAvBrainGeneric::kDefaultFadeIn = 6.f; // 1/6th of a second to fade in +const hsScalar plAvBrainGeneric::kDefaultFadeOut = 0.f; // instant fade out. + +// plAvBrainGeneric ---------------- +// ----------------- +plAvBrainGeneric::plAvBrainGeneric() +: fRecipient(nil), + fStages(TRACKED_NEW plAnimStageVec), + fCurStage(0), + fType(kGeneric), + fExitFlags(kExitNormal), + fMode(kEntering), + fForward(true), + fStartMessage(nil), + fEndMessage(nil), + fFadeIn(0.0f), + fFadeOut(0.0f), + fMoveMode(kMoveRelative), + fCallbackAction(nil), + fBodyUsage(plAGAnim::kBodyUnknown) +{ +} + +// plAvBrainGeneric -------------------------------------- +// ----------------- +plAvBrainGeneric::plAvBrainGeneric(plAnimStageVec *stages, + plMessage *startMessage, + plMessage *endMessage, + plKey recipient, + UInt32 exitFlags, + float fadeIn, + float fadeOut, + MoveMode moveMode) +: plArmatureBrain(), + fRecipient(recipient), + fStages(stages), + fCurStage(0), + fType(kGeneric), + fExitFlags(exitFlags), + fMode(kEntering), + fForward(true), + fStartMessage(startMessage), + fEndMessage(endMessage), + fFadeIn(fadeIn), + fFadeOut(fadeOut), + fMoveMode(moveMode), + fCallbackAction(nil), + fBodyUsage(plAGAnim::kBodyUnknown) +{ +} + +// plAvBrainGeneric +plAvBrainGeneric::plAvBrainGeneric(UInt32 exitFlags, float fadeIn, float fadeOut, MoveMode moveMode) +: fRecipient(nil), + fStages(nil), + fCurStage(0), + fType(kGeneric), + fExitFlags(exitFlags), + fMode(kEntering), + fForward(true), + fStartMessage(nil), + fEndMessage(nil), + fFadeIn(fadeIn), + fFadeOut(fadeOut), + fMoveMode(moveMode), + fCallbackAction(nil), + fBodyUsage(plAGAnim::kBodyUnknown) +{ + +} + + +// ~plAvBrainGeneric ---------------- +// ------------------ +plAvBrainGeneric::~plAvBrainGeneric() +{ + int fNumStages = fStages->size(); + + for(int i = 0; i < fNumStages; i++) + { + plAnimStage *stage = (*fStages)[i]; + (*fStages)[i] = nil; + stage->Detach(fAvMod); + delete stage; + } + delete fStages; +} + +// Activate ------------------------------------------- +// --------- +void plAvBrainGeneric::Activate(plArmatureModBase *avMod) +{ + plArmatureBrain::Activate(avMod); + + if ((GetType() == kEmote || GetType() == kAFK || GetType() == kSitOnGround) && fAvMod->IsLocalAvatar()) + { + plInputIfaceMgrMsg* msg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableClickables ); + plgDispatch::MsgSend(msg); + } + + int numStages = fStages->size(); + if (!numStages) + return; + plAnimStage *stage = (*fStages)[fCurStage]; + + bool useFadeIn = fFadeIn > 0.0f; + float initialBlend = useFadeIn ? 0.0f : 1.0f; + + if (GetType() == kEmote) + ((plArmatureMod*)avMod)->SendBehaviorNotify(plHBehavior::kBehaviorTypeEmote,true); + double worldTime = hsTimer::GetSysSeconds(); + + if (fMoveMode == kMoveRelative || fMoveMode == kMoveAbsolute) + { + // enable kinematic... ignore outside forces... but still collide with detector regions + fAvMod->EnablePhysicsKinematic( true ); + } + else if(fMoveMode == kMoveStandstill) + { + // Avatar stands still automatically now, so we do nothing here + } + if (stage->Attach(fAvMod, this, initialBlend, worldTime)) + { + if(fStartMessage) + { + fStartMessage->Send(); + fStartMessage = nil; + } + + if (plAvBrainGeneric::fForce3rdPerson && fAvMod->IsLocalAvatar()) + { + // create message to force 3rd person mode + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetCmd(plCameraMsg::kResponderSetThirdPerson); + pMsg->SetBCastFlag(plMessage::kNetPropagate, false); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + + } + + if (fType == kLadder && fAvMod->IsLocalAvatar()) + plAvatarInputInterface::GetInstance()->SetLadderMode(); + + if (fReverseFBControlsOnRelease) + fAvMod->SetReverseFBOnIdle(true); +} + +hsBool plAvBrainGeneric::IsRunningTask() +{ + if ( fStages->size() > 0 ) + return true; + return false; +} + +bool plAvBrainGeneric::MatchAnimNames(const char *names[], int count) +{ + if (count != GetStageCount()) + return false; + + int i; + for (i = 0; i < count; i++) + { + if (strcmp(names[i], GetStage(i)->GetAnimName())) + return false; // different names. + } + + return true; +} + +// Apply ---------------------------------------------------- +// ------ +hsBool plAvBrainGeneric::Apply(double time, hsScalar elapsed) +{ + hsBool result = false; + + switch(fMode) + { + case kAbort: + break; + case kEntering: + case kFadingIn: + result = IProcessFadeIn(time, elapsed); + break; + case kExit: + case kFadingOut: + // go through the fade logic whether or not we actually need a fade; + // centralizes some exit conditions. + result = IProcessFadeOut(time, elapsed); + break; + case kNormal: + result = IProcessNormal(time, elapsed); + break; + } + plArmatureBrain::Apply(time, elapsed); + return result; +} + +// Deactivate ----------------------- +// ----------- +void plAvBrainGeneric::Deactivate() +{ + if (fEndMessage) + { + fEndMessage->Send(); + fEndMessage = nil; + } + if (fMode != kAbort) // we're being forcibly removed... + IExitMoveMode(); + + if (fMoveMode == kMoveRelative || fMoveMode == kMoveAbsolute) + { + // re-enable normal physics... outside forces affect us + fAvMod->EnablePhysicsKinematic( false ); + } + else if (fMoveMode == kMoveStandstill) + { + // Avatar stands still automaticaly now, so we do nothing here + } + + if (fType == plAvBrainGeneric::kLadder && fAvMod->IsLocalAvatar()) + { + plAvatarInputInterface::GetInstance()->ClearLadderMode(); + } + + if (fReverseFBControlsOnRelease) + fAvMod->SetReverseFBOnIdle(false); + + if (plAvBrainGeneric::fForce3rdPerson && fAvMod->IsLocalAvatar()) + { + // create message to force 3rd person mode + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, false); + pMsg->SetCmd(plCameraMsg::kResponderUndoThirdPerson); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + + plArmatureBrain::Deactivate(); + + if ((GetType() == kEmote || GetType() == kAFK || GetType() == kSitOnGround) && fAvMod->IsLocalAvatar()) + { + plInputIfaceMgrMsg* msg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kEnableClickables ); + plgDispatch::MsgSend(msg); + } +} + +// GETRECIPIENT +plKey plAvBrainGeneric::GetRecipient() +{ + return fRecipient; +} + +// SETRECIPIENT +void plAvBrainGeneric::SetRecipient(const plKey &recipient) +{ + fRecipient = recipient; +} + +// RELAYNOTIFYMSG +bool plAvBrainGeneric::RelayNotifyMsg(plNotifyMsg *msg) +{ + if(fRecipient) + { + msg->AddReceiver(fRecipient); + msg->Send(); + return true; + } else { + return false; + } +} + +// IGetAnimDelta ------------------------------------------------ +// -------------- +float plAvBrainGeneric::IGetAnimDelta(double time, float elapsed) +{ + float delta = 0.0f; + plAnimStage *curStage = (*fStages)[fCurStage]; + plAnimStage::ForwardType forward = curStage->GetForwardType(); + plAnimStage::BackType back = curStage->GetBackType(); + bool fwdIsDown = (fReverseFBControlsOnRelease && fAvMod->IsFBReversed()) ? fAvMod->BackwardKeyDown() : fAvMod->ForwardKeyDown(); + hsBool backIsDown = (fReverseFBControlsOnRelease && fAvMod->IsFBReversed()) ? fAvMod->ForwardKeyDown() : fAvMod->BackwardKeyDown(); + + // forward with a key down gets top priority + if(forward == plAnimStage::kForwardKey && fwdIsDown) + { + // key drive forward, forward key is down + delta = elapsed; + fForward = true; + } else if(back == plAnimStage::kBackKey && backIsDown) + { + // key drive back, back key is down + delta = -elapsed; + fForward = false; + } else if (forward == plAnimStage::kForwardAuto && fForward) + { + // auto drive forward + delta = elapsed; + } else if (back == plAnimStage::kBackAuto && ! fForward) + { + // auto drive backward + delta = -elapsed; + } + return delta; +} + +// IProcessNormal ------------------------------------------------- +// --------------- +hsBool plAvBrainGeneric::IProcessNormal(double time, float elapsed) +{ + plAnimStage *curStage = (*fStages)[fCurStage]; + if(curStage) + { + float animDelta = IGetAnimDelta(time, elapsed); // how far to move the anim (may be negative) + float overage; + hsBool done = curStage->MoveRelative(time, animDelta, overage, fAvMod); + + if(done) + { + bool forward = animDelta > 0.0f; + int nextStage = forward ? curStage->GetNextStage(fCurStage) : curStage->GetPrevStage(fCurStage); + + if((nextStage == -1) || (nextStage >= fStages->size())) + { + // ran off one end; we're done. + fMode = kExit; + } else { + ISwitchStages(fCurStage, nextStage, overage, false, 0.0f, 1.0f, -1.0f, time); + } + } + + return true; + } else { + // current stage is missing; abort + return false; + } +} + +// IProcessFadeIn ------------------------------------------------- +// --------------- +hsBool plAvBrainGeneric::IProcessFadeIn(double time, float elapsed) +{ + plAnimStage *curStage = (*fStages)[fCurStage]; + + if(fMode != kFadingIn) + { + bool needFade = fFadeIn != 0.0f; + + if(fFadeIn == 0.0f) + { + IEnterMoveMode(time); // if fadeIn's not zero, we have to wait until fade's done + // before animating + } else { + curStage->GetAnimInstance()->Fade(1.0f, fFadeIn); + } + fMode = kFadingIn; + } else { + float curBlend = curStage->GetAnimInstance()->GetBlend(); + if(curBlend == 1.0f) + { + IEnterMoveMode(time); + } + } + return true; +} + +// IProcessFadeOut ------------------------------------------------- +// ---------------- +hsBool plAvBrainGeneric::IProcessFadeOut(double time, float elapsed) +{ + plAnimStage *curStage = (*fStages)[fCurStage]; + + if(fMode != kFadingOut) + { + // haven't actually started fading; see if we need to + if(fFadeOut > 0.0f) + { + plAGAnimInstance *curAnim = curStage->GetAnimInstance(); + if(curAnim) + { + curStage->GetAnimInstance()->Fade(0.0f, fFadeOut); + IExitMoveMode(); + fMode = kFadingOut; + } else { + fMode = kAbort; + return false; + } + } else { + curStage->Detach(fAvMod); + IExitMoveMode(); + fMode = kAbort; + } + } else { + // already fading; just keeping looking for the anim to zero out. + float curBlend = curStage->GetAnimInstance()->GetBlend(); + if(curBlend == 0.0f) + { + curStage->Detach(fAvMod); + fMode = kAbort; + } + } + return true; +} + +// ISwitchStages --------------------------------------------------------------------------------------------------- +// -------------- +hsBool plAvBrainGeneric::ISwitchStages(int oldStageNum, int newStageNum, float delta, hsBool setTime, float newTime, + float fadeNew, hsScalar fadeOld, double worldTime) +{ +#ifdef DEBUG_MULTISTAGE + char sbuf[256]; + sprintf(sbuf,"ISwitchStage - old=%d new=%d (fCurStage=%d)",oldStageNum,newStageNum,fCurStage); + plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf); +#endif + if(oldStageNum != newStageNum) { + plAnimStage *newStage = fStages->at(newStageNum); + plAnimStage *oldStage = fStages->at(oldStageNum); + if(setTime) + newStage->SetLocalTime(newTime); + + hsAssert(oldStageNum < fStages->size(), "PLAVBRAINGENERIC: Stage out of range."); + + oldStage->Detach(fAvMod); + newStage->Attach(fAvMod, this, 1.0f, worldTime); + + fCurStage = newStageNum; + fAvMod->DirtySynchState(kSDLAvatar, 0); // write our new stage to the server + } + if(setTime) { + plAnimStage *curStage = fStages->at(fCurStage); + curStage->SetLocalTime(newTime); + } + + if(fMoveMode == kMoveRelative) + fAvMod->GetRootAnimator()->Reset(worldTime); + return true; +} + +void plAvBrainGeneric::IEnterMoveMode(double time) +{ + if(fMoveMode == kMoveRelative) + { + fAvMod->GetRootAnimator()->Enable(true); + fAvMod->GetRootAnimator()->Reset(time); + } + fMode = kNormal; +} + +void plAvBrainGeneric::IExitMoveMode() +{ + if(fAvMod) + { + if(fMoveMode == kMoveRelative) + { + if(fAvMod->GetRootAnimator()) + fAvMod->GetRootAnimator()->Enable(false); + } + + if (fFadeOut == 0.f) + { + // if we're exiting instantly (no fade out) then the end of the animation expects to line up with + // the first frame of the idle animation, so we need to reset it. + plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(fAvMod->FindBrainByClass(plAvBrainHuman::Index())); + if (brain) + brain->ResetIdle(); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// MESSAGE HANDLING +// +///////////////////////////////////////////////////////////////////////////////////////// + +// MsgReceive ------------------------------------- +// ----------- +hsBool plAvBrainGeneric::MsgReceive(plMessage *msg) +{ + hsBool result = false; + + plAvBrainGenericMsg *genMsg = plAvBrainGenericMsg::ConvertNoRef(msg); + //plAvExitModeMsg *exitMsg = plAvExitModeMsg::ConvertNoRef(msg); + plAvTaskMsg *taskMsg = plAvTaskMsg::ConvertNoRef(msg); + plControlEventMsg *ctrlMsg = plControlEventMsg::ConvertNoRef(msg); + + + if(genMsg) + { + result = IHandleGenBrainMsg(genMsg); + } +// else if(exitMsg) { +// fMode = kExit; +// result = true; +// } + else if (taskMsg) { + result = IHandleTaskMsg(taskMsg); + } + else if (ctrlMsg && (fExitFlags & kExitAnyInput) ) { + fMode = kExit; + } + + if(result == false) // if still haven't handled msg + { + if(fMode == kExit) // if we're exiting + { + result = fAvMod->GetNextBrain(this)->MsgReceive(msg); // pass msg to next brain + } else { // otherwise + result = plArmatureBrain::MsgReceive(msg); // pass msg to base class + } + } + + return result; +} + +// IHandleGenBrainMsg ----------------------------------------------------- +// ------------------- +hsBool plAvBrainGeneric::IHandleGenBrainMsg(const plAvBrainGenericMsg *msg) +{ + hsBool setTime = msg->fSetTime; + float newTime = msg->fNewTime; + hsBool setDirection = msg->fSetDirection; + bool newDirection = msg->fNewDirection ? true : false; + double worldTime = hsTimer::GetSysSeconds(); + + switch(msg->fType) + { + case plAvBrainGenericMsg::kGotoStage: + { + int wantStage = msg->fWhichStage; +#ifdef DEBUG_MULTISTAGE + char sbuf[256]; + sprintf(sbuf,"GenericMsg - Goto Stage %d (oldstage %d)",wantStage,fCurStage); + plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf); +#endif + if(wantStage == -1) { + fMode = kExit; + } else { + int count = fStages->size(); + if(wantStage < count && wantStage >= 0) + { + ISwitchStages(fCurStage, wantStage, 0.0f, setTime, newTime, 1.0f, -1.0f, worldTime); + // direction is set within the brain, not the stage + if(setDirection) + fForward = newDirection; + } + } + } + break; + case plAvBrainGenericMsg::kNextStage: + { + int wantStage = fCurStage + 1; +#ifdef DEBUG_MULTISTAGE + char sbuf[256]; + sprintf(sbuf,"GenericMsg - Next Stage %d (oldstage %d)",wantStage,fCurStage); + plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf); +#endif + if(wantStage == fStages->size()) + { + fMode = kExit; // walked off the end of the brain + } else { + ISwitchStages(fCurStage, wantStage, 0.0f, setTime, newTime, 1.0f, -1.0f, worldTime); + if(setDirection) + fForward = newDirection; + } + } + break; + case plAvBrainGenericMsg::kPrevStage: + { + int wantStage = fCurStage - 1; +#ifdef DEBUG_MULTISTAGE + char sbuf[256]; + sprintf(sbuf,"GenericMsg - PrevStage %d (oldstage %d)",wantStage,fCurStage); + plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf); +#endif + if(wantStage < 0) + { + fMode = kExit; // walked off the beginning of the brain + } else { + ISwitchStages(fCurStage, wantStage, 0.0f, setTime, 0.0f, 1.0f, -1.0f, worldTime); + if(setDirection) + fForward = newDirection; + } + } + break; +#ifdef DEBUG_MULTISTAGE + default: + { + char sbuf[256]; + sprintf(sbuf,"GenericMsg - Unknown command %d ",msg->fType); + plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf); + } + break; +#endif + } + return true; +} + +hsBool plAvBrainGeneric::IHandleTaskMsg(plAvTaskMsg *msg) +{ + plAvTask *task = msg->GetTask(); + plAvTaskBrain *brainTask = plAvTaskBrain::ConvertNoRef(task); + + if(brainTask) + { + plArmatureBrain * brain = brainTask->GetBrain(); + + if(brain) + { + if(fExitFlags & kExitNewBrain) + { + // RULE 1: if kExitNewBrain, exit on any new brain + fMode = kExit; + return false; // we didn't consume the message + } else { + plAvBrainGeneric * gBrain = plAvBrainGeneric::ConvertNoRef(brain); + + if(gBrain && IBrainIsCompatible(gBrain)) + { + // RULE 2: if not kExitNewBrain and brain is compatible, apply it + QueueTask(brainTask); + return true; + } else { + if(fMode == kExit || fMode == kFadingOut) + { + // RULE 3: if brain is incompatible and we're exiting anyway, + // queue it to be next + fAvMod->GetNextBrain(this)->QueueTask(brainTask); + return true; + } + // RULE 4: if brain is incompatible and we're still running, ignore. + } + } + } else { + // no brain; it's an exit task, exit and CONSUME it. + fMode = kExit; + return true; + } + } else { + // note that this check has to come after the brain task check; if it's a brain + // task we need to examine it so we can say whether we consumed it or not. + // popbrain messages get consumed, even if we exit on any task. + if(fExitFlags & kExitAnyTask) + { + // RULE 4: if kExitAnyTask, exit on any task (but if it was an exit brain task, + // make sure to consume it ) + fMode = kExit; + return false; + } + } + + // RULE 4: if brain is incompatible and we're still running, ignore. + return false; +} + +bool plAvBrainGeneric::IBrainIsCompatible(plAvBrainGeneric *otherBrain) +{ + plAGAnim::BodyUsage otherUsage = otherBrain->GetBodyUsage(); + + switch(fBodyUsage) + { + case plAGAnim::kBodyUnknown: + return false; + break; + case plAGAnim::kBodyFull: + return false; + break; + case plAGAnim::kBodyUpper: + if(otherUsage == plAGAnim::kBodyLower) + return true; + else + return false; + break; + case plAGAnim::kBodyLower: + if(otherUsage == plAGAnim::kBodyUpper) + return true; + else + return false; + break; + } + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// READ/WRITE +// +///////////////////////////////////////////////////////////////////////////////////////// + +// Write ---------------------------------------------------- +// ------ +void plAvBrainGeneric::Write(hsStream *stream, hsResMgr *mgr) +{ + plArmatureBrain::Write(stream, mgr); + int numStages = fStages->size(); + stream->WriteSwap32(numStages); + + for(int i = 0; i < numStages; i++) + { + plAnimStage *stage = (*fStages)[i]; + plCreatable *cre = reinterpret_cast(stage); + mgr->WriteCreatable(stream, cre); // save base state + // ** replace this with Write(..) + stage->SaveAux(stream, mgr); // save ephemeral state + } + + stream->WriteSwap32(fCurStage); + stream->WriteSwap32(fType); + stream->WriteSwap32(fExitFlags); + stream->WriteByte(fMode); + stream->Writebool(fForward); + + if(fStartMessage) { + stream->WriteBool(true); + mgr->WriteCreatable(stream, fStartMessage); + } else { + stream->WriteBool(false); + } + + if(fEndMessage) { + stream->WriteBool(true); + mgr->WriteCreatable(stream, fEndMessage); + } else { + stream->WriteBool(false); + } + + stream->WriteSwapScalar(fFadeIn); + stream->WriteSwapScalar(fFadeOut); + stream->WriteByte(fMoveMode); + stream->WriteByte(fBodyUsage); + mgr->WriteKey(stream, fRecipient); +} + +// Read ---------------------------------------------------- +// ----- +void plAvBrainGeneric::Read(hsStream *stream, hsResMgr *mgr) +{ + plArmatureBrain::Read(stream, mgr); + int numStages = stream->ReadSwap32(); + + for(int i = 0; i < numStages; i++) + { + plCreatable *created = mgr->ReadCreatable(stream); // load base state + plAnimStage *stage = reinterpret_cast(created); + // Replace this with Read(..) + stage->LoadAux(stream, mgr, 0.0); // load ephemeral state + + fStages->push_back(stage); + } + + fCurStage = stream->ReadSwap32(); + fType = static_cast(stream->ReadSwap32()); + fExitFlags = stream->ReadSwap32(); + fMode = static_cast(stream->ReadByte()); + fForward = stream->Readbool(); + + if(stream->ReadBool()) { + fStartMessage = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); + } else { + fStartMessage = nil; + } + if(stream->ReadBool()) { + fEndMessage = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); + } else { + fEndMessage = nil; + } + + fFadeIn = stream->ReadSwapScalar(); + fFadeOut = stream->ReadSwapScalar(); + fMoveMode = static_cast(stream->ReadByte()); + fBodyUsage = static_cast(stream->ReadByte()); + fRecipient = mgr->ReadKey(stream); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// MINOR FNs / GETTERS & SETTERS +// +///////////////////////////////////////////////////////////////////////////////////////// + +// LeaveAge --------------------- +// --------- +hsBool plAvBrainGeneric::LeaveAge() +{ + IExitMoveMode(); + + fMode = kAbort; + return true; +} + +// AddStage -------------------------------------- +// --------- +int plAvBrainGeneric::AddStage(plAnimStage *stage) +{ + if(!fStages) + fStages = TRACKED_NEW plAnimStageVec; + fStages->push_back(stage); + return fStages->size() - 1; +} + +// GetStageNum -------------------------------------- +// ------------ +int plAvBrainGeneric::GetStageNum(plAnimStage *stage) +{ + int count = fStages->size(); + for(int i = 0; i < count; i++) + { + plAnimStage *any = (*fStages)[i]; + if(any == stage) + { + return i; + } + } + return -1; +} + +// GetCurStageNum -------------------- +// --------------- +int plAvBrainGeneric::GetCurStageNum() +{ + return fCurStage; +} + +// GetStageCount -------------------- +// -------------- +int plAvBrainGeneric::GetStageCount() +{ + return fStages->size(); +} + +// GetStage --------------------------------------- +// --------- +plAnimStage * plAvBrainGeneric::GetStage(int which) +{ + return fStages->at(which); +} + +// GetCurStage ------------------------------ +// ------------ +plAnimStage * plAvBrainGeneric::GetCurStage() +{ + return fStages->at(fCurStage); +} + +// SetType ------------------------------------------------------------------------------- +// -------- +plAvBrainGeneric::BrainType plAvBrainGeneric::SetType(plAvBrainGeneric::BrainType newType) +{ + BrainType oldType = fType; + fType = newType; + return oldType; +} + +// GetType -------------------------------------------- +// -------- +plAvBrainGeneric::BrainType plAvBrainGeneric::GetType() +{ + return fType; +} + +plAGAnim::BodyUsage plAvBrainGeneric::GetBodyUsage() +{ + return fBodyUsage; +} + +void plAvBrainGeneric::SetBodyUsage(plAGAnim::BodyUsage bodyUsage) +{ + fBodyUsage = bodyUsage; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEBUGGING +// +///////////////////////////////////////////////////////////////////////////////////////// + +// DumpToDebugDisplay ---------------------------------------------------------------------------------------- +// ------------------- +void plAvBrainGeneric::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + debugTxt.DrawString(x, y, "Brain type: Generic AKA Multistage"); + y += lineHeight; + + int stageCount = fStages->size(); + for(int i = 0; i < stageCount; i++) + { + plAnimStage *stage = (*fStages)[i]; + stage->DumpDebug(i == fCurStage, x, y, lineHeight, strBuf, debugTxt); + } +} + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h new file mode 100644 index 00000000..e899100d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainGeneric.h @@ -0,0 +1,314 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLAVBRAINGENERIC_INC +#define PLAVBRAINGENERIC_INC + +// base class +#include "plAvBrain.h" +#include "plAGAnim.h" + +class plAnimStage; +class plAnimStageVec; +class plAvBrainGenericMsg; +class plHorizontalFreezeAction; +class plNotifyMsg; + +/** \class plAvBrainGeneric + The generic brain is designed to manage complex (and simple) sequences of animations + used in detailed puzzle sequences and the like. + It implements a model wherein each animation is wrapped in an plAnimStage object, + which provides different means of user input, support for loop types, callbacks + at significant events, and scripted control of stage transitions. + Detailed object interactions such as climbing, sitting, interacting with levers, + and the like are all handled by generic brains with varying stages. +*/ +class plAvBrainGeneric : public plArmatureBrain +{ +public: + /** \enum BrainType + Sometimes the brains are truly generic and opaque, and sometimes + we need to be able to tell "what this brain does" at a summary level. + This enum lists our current standard brain types. + This data is not used internally by the brain; it's just stored + for the user. */ + enum BrainType { + kGeneric, /// type was not specified + kLadder, /// it's a ladder brain + kSit, /// it's a sit brain + kSitOnGround, /// sit on the ground emote + kEmote, /// yeah yeah + kAFK, + kNumBrainTypes, + kNonGeneric = kNumBrainTypes /// If we ask the avatar what its current generic type is, + /// we use this to signify that it's not currenty using + /// a generic brain. + }; + + /** \enum ExitFlag + The default behavior for a generic brain is to exit when all its + stages are completed. This set of bitwise indices defines other + conditions which can be used to exit a brain. + Note: For now, we're restricting the SDL for this to a byte. Change that if you add + too many flags. + */ + enum ExitFlag { + kExitNormal = 0, // for completeness; use this when you don't have an exit condition + kExitAnyTask = 0x01, // exit if anyone sends us a task + kExitNewBrain = 0x02, // exit if anyone sends us a new brain + kExitAnyInput = 0x04, // exit if we get any input control events + kExitMaxFlag = 0x08 + }; + + enum MoveMode { + kMoveAbsolute, // the animations are taken to be in world coordinates + kMoveRelative, // the animation is played as if the starting point is the origin + kMoveNormal, // the avatar root is moved by translating the animation to velocity + kMoveStandstill, // the avatar root is held immobile + kMaxMoveMode, + kForceSize = 0xff + }; + + enum Mode{ + kEntering = 0x01, // seeking, loading, or within the "enter" animation + kNormal = 0x02, // undistinguished animations are running + kFadingIn = 0x03, // fading in first stage; not running + kFadingOut = 0x04, // fading out last stage; not running + kExit = 0x05, // exit observing animation fadeouts, etc. + kAbort = 0x06, // exit immediately without updating animations + kMaxMode = 0x07, + kModeSize = 0xff + } fMode; + + static const hsScalar kDefaultFadeIn; + static const hsScalar kDefaultFadeOut; + + /** Default constructor for the class factory and descendants. */ + plAvBrainGeneric(); + + /** Canonical constructor - use this. + \param stages A vector of stages to use for this brain. Will be copied + into our internal vector. + \param enterMessage A message to send when the brain enters begins playing. + If you need finer control over callbacks, use stage callbacks. + \param exitMessage A message to send when the brain exits. + \param recipient Callbacks from the brain *and* the stages is sent to this key. + \param exitFlags A combination of exit conditions from the ExitFlag enum */ + plAvBrainGeneric(plAnimStageVec *stages, plMessage *enterMessage, plMessage *exitMessage, + plKey recipient, UInt32 exitFlags, float fadeIn, float fadeOut, MoveMode moveMode); + + /** Simplified constructor + \param exitFlags Indicates which conditions will cause the brain to exit. + \param fadeIn Rate (in blend units per second) of initial animation fade in. + \param fadeOut Rate (in blend units per second) of final animation fade out. + */ + plAvBrainGeneric(UInt32 exitFlags, float fadeIn, float fadeOut, MoveMode moveMode); + + /** Virtual destructor */ + virtual ~plAvBrainGeneric(); + + static hsBool fForce3rdPerson; + + /** Attaches our current stage and takes control of the armature. Note that + the brain may be in a "half-finished" state -- we may be receiving some + complex state from another client and therefore pushing on a brain that + is in the middle of its third stage, for example. */ + virtual void Activate(plArmatureModBase *avMod); + + /** Advance the current stage and swap in a new stage if necessary. */ + virtual hsBool Apply(double timeNow, hsScalar elapsed); + + /** Remove all our stages and release control of the armature. */ + virtual void Deactivate(); + + virtual plKey GetRecipient(); + virtual void SetRecipient(const plKey &recipient); + + /** Send a notify message to our recipient. + Some brains, such as the coop, will use this opportunity to add annotations + to the notify. */ + virtual bool RelayNotifyMsg(plNotifyMsg *msg); + + /** We're leaving the age. Clean up. */ + virtual hsBool LeaveAge(); + + virtual hsBool IsRunningTask(); + + /** Compare the names of the anims in our stages. + Return true on a match (order matters). */ + bool MatchAnimNames(const char *names[], int count); + + /** Add the given stage to the end of the stage sequence. + Returns the zero-based index of the stage. + */ + int AddStage(plAnimStage *stage); + + /** The sequence number of the *given* stage -- not the current stage. Useful + if you have a pointer to a stage and want to find out where in the sequence + it lives. Notice that since stage transitions can be nonlinear, there is + no guarantee that this will be the nth stage to play, or that it will play + at all. */ + int GetStageNum(plAnimStage *stage); + + /** Get the sequence number of the stage that's currently playing. */ + int GetCurStageNum(); + + void SetCurStageNum(int num) { fCurStage = num; }; + + /** How many stages are in this brain? */ + int GetStageCount(); + + /** Retrieve a stage via a sequence number. Returns nil if no such stage exists + Be extremely careful about retaining this pointer, as it may be deleted at + any time. */ + plAnimStage * GetStage(int which); + + /** Retrieve a pointer to the currently executing stage.Returns nil if no such stage exists + Be extremely careful about retaining this pointer, as it may be deleted at + any time. */ + plAnimStage * GetCurStage(); + + /** Set the user-specified type of the brain. Has no effect on brain behavior; + just a poor man's dynamic type system */ + BrainType SetType(BrainType newType); + + /** Get the user-specified type of the brain. Has no effect on brain behavior; + for external reference only. */ + BrainType GetType(); + + /** Is the brain moving "generally" forward? This is a fairly poorly-defined concept + that is only used by stages that have "auto" progress types, i.e. they + move forward if the brain is moving forward, and move backward if the brain + is moving backward. + /deprecated Avoid. will probably be deleted. */ + bool GetForward() const { return fForward; } + bool GetReverseFBControlsOnRelease() const { return fReverseFBControlsOnRelease; }; + void SetReverseFBControlsOnRelease(bool val) { fReverseFBControlsOnRelease = val; }; + + /** Returns a pointer to the message we will send when we start our brain. + Returns nil if there is no such message *or* if it has already been sent. + /bug This message is destructed after being delivered, so we need to + nil this pointer after sending it. + */ + plMessage* GetStartMessage() const { return fStartMessage; } + + /** Returns a pointer to the message we will send when we finish our brain. + Returns nil if there is no such message *or* if it has already been sent. + /bug This message is destructed after being delivered, so we need to + nil this pointer after sending it. + */ + plMessage* GetEndMessage() const { return fEndMessage; } + + /** Returns the bitvector holding our exit conditions. + /sa plAvBrainGeneric::ExitFlags */ + UInt32 GetExitFlags() const { return fExitFlags; } + + plAGAnim::BodyUsage GetBodyUsage(); + void SetBodyUsage(plAGAnim::BodyUsage bodyUsage); + + Mode GetMode() { return fMode; } + void SetMode(Mode mode) { fMode = mode; } + + float GetFadeIn() { return fFadeIn; } + float GetFadeOut() { return fFadeOut; } + + MoveMode GetMoveMode() { return fMoveMode; } + + /** Output the brain's status to the avatar debug screen. */ + virtual void DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); + + // plasma protocol + hsBool plAvBrainGeneric::MsgReceive(plMessage *msg); + CLASSNAME_REGISTER( plAvBrainGeneric ); + GETINTERFACE_ANY( plAvBrainGeneric, plArmatureBrain ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + +protected: + + ///////////////////////////////////////////////////////////////////////////////////// + // + // CODE + // + ///////////////////////////////////////////////////////////////////////////////////// + + hsBool IHandleGenBrainMsg(const plAvBrainGenericMsg *msg); + hsBool IHandleTaskMsg(plAvTaskMsg *msg); + + bool IBrainIsCompatible(plAvBrainGeneric *otherBrain); + + float IGetAnimDelta(double time, float elapsed); // how far should we move our animation this frame? + + hsBool IProcessNormal(double time, float elapsed); + + hsBool IProcessFadeIn(double time, float elapsed); + hsBool IProcessFadeOut(double time, float elapsed); + + hsBool ISwitchStages(int oldStage, int newStage, float delta, hsBool setTime, hsScalar newTime, + float fadeNew, hsScalar fadeOld, double worldTime); + + void IEnterMoveMode(double time); // we've just entered and we're about to begin animating. + void IExitMoveMode(); // we're done animating; clean up + + ///////////////////////////////////////////////////////////////////////////////////// + // + // DATA + // + ///////////////////////////////////////////////////////////////////////////////////// + + plKey fRecipient; // this guy gets all new notify messages + plAnimStageVec *fStages; // all the stages in our animation + int fCurStage; // which stage are we playing? (zero-based) + BrainType fType; // what type of brain are we? + UInt32 fExitFlags; // what will cause us to exit? + plHorizontalFreezeAction *fCallbackAction; + + bool fForward; // are we currently moving forward or backward through the stages? + // this is used by the "auto-" movement types in the stages + + plMessage *fStartMessage; // send this message when our brain is activated + plMessage *fEndMessage; // send this message when our brain is deactivated + + bool fReverseFBControlsOnRelease; // Tells the armature that we want to reverse the forward/backward controls + // if both are released. The armatureMod will tell us if this has actually happened + + float fFadeIn; // if non-zero, the rate to fade in the first animation + float fFadeOut; // if non-zero, the rate to fade out the last animation + + MoveMode fMoveMode; // how are we translating animation movement to + // movement in the world? + + plAGAnim::BodyUsage fBodyUsage; // how much of the body does this brain use? + // this is based strictly on compatibility; i.e. a brain + // that uses only the lower body might still animate the + // hands, but it is guaranteed to look right when overlaid + // by an anim that uses only the upper body. + +}; + + + +#endif // PLAVBRAINGENERIC_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp new file mode 100644 index 00000000..cde8713f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp @@ -0,0 +1,1453 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" +#include "hsWindows.h" + +#include "plAvCallbackAction.h" // subclasses a havok object; must be in first include section + + +#include "plAvBrainHuman.h" +#include "plAvBrainClimb.h" +#include "plAvBrainDrive.h" +#include "plAvBrainGeneric.h" +#include "plAvBrainSwim.h" +#include "plArmatureMod.h" +#include "plAGModifier.h" +#include "plMatrixChannel.h" +#include "plAvTask.h" +#include "plAvTaskBrain.h" +#include "plAvTaskSeek.h" +#include "plAGAnim.h" +#include "plAGAnimInstance.h" +#include "plAvatarMgr.h" +#include "plAnimStage.h" +#include "plAvatarClothing.h" + +#include "hsTimer.h" +#include "hsGeometry3.h" +#include "float.h" +#include "plPipeline.h" +#include "plgDispatch.h" +#include "hsQuat.h" +#include "plPhysical.h" +#include "../plStatusLog/plStatusLog.h" + +#include "../pnNetCommon/plNetApp.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plInputCore/plAvatarInputInterface.h" +#include "../plInputCore/plInputDevice.h" +#include "../plMath/plRandom.h" +#include "../plPipeline/plDebugText.h" +#include "../plNetClient/plNetLinkingMgr.h" + +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plClimbMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plLOSHitMsg.h" +#include "../plMessage/plLOSRequestMsg.h" +#include "../plMessage/plSimStateMsg.h" +#include "../plMessage/plSwimMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../pnMessage/plWarpMsg.h" +#include "../pnMessage/plProxyDrawMsg.h" +#include "../plMessage/plRideAnimatedPhysMsg.h" + +float plAvBrainHuman::fWalkTimeToMaxTurn = .3f; +float plAvBrainHuman::fRunTimeToMaxTurn = .1f; +float plAvBrainHuman::fWalkMaxTurnSpeed = 2.0f; +float plAvBrainHuman::fRunMaxTurnSpeed = 1.7; +plAvBrainHuman::TurnCurve plAvBrainHuman::fWalkTurnCurve = plAvBrainHuman::kTurnExponential; +plAvBrainHuman::TurnCurve plAvBrainHuman::fRunTurnCurve = plAvBrainHuman::kTurnExponential; + +const hsScalar plAvBrainHuman::kAirTimePanicThreshold = 10; // seconds + +void plAvBrainHuman::SetTimeToMaxTurn(float time, hsBool walk) +{ + if (walk) + fWalkTimeToMaxTurn = time; + else + fRunTimeToMaxTurn = time; +} + +float plAvBrainHuman::GetTimeToMaxTurn(hsBool walk) +{ + return (walk ? fWalkTimeToMaxTurn : fRunTimeToMaxTurn); +} + +void plAvBrainHuman::SetMaxTurnSpeed(float radsPerSec, hsBool walk) +{ + if (walk) + fWalkMaxTurnSpeed = radsPerSec; + else + fRunMaxTurnSpeed = radsPerSec; +} + +float plAvBrainHuman::GetMaxTurnSpeed(hsBool walk) +{ + return (walk ? fWalkMaxTurnSpeed : fRunMaxTurnSpeed); +} + +void plAvBrainHuman::SetTurnCurve(TurnCurve curve, hsBool walk) +{ + if (walk) + fWalkTurnCurve = curve; + else + fRunTurnCurve = curve; +} + +plAvBrainHuman::TurnCurve plAvBrainHuman::GetTurnCurve(hsBool walk) +{ + return (walk ? fWalkTurnCurve : fRunTurnCurve); +} + +plAvBrainHuman::plAvBrainHuman(bool isActor /* = false */) : + fHandleAGMod(nil), + fStartedTurning(-1.0f), + fCallbackAction(nil), + fPreconditions(0), + fIsActor(isActor) +{ +} + +hsBool plAvBrainHuman::Apply(double timeNow, hsScalar elapsed) +{ +#ifndef _DEBUG + try + { +#endif + // SetTurnStrength runs first to make sure it's set to a sane value + // (or cleared). RunStandardBehaviors may overwrite it. + fCallbackAction->SetTurnStrength(IGetTurnStrength(timeNow)); + RunStandardBehaviors(timeNow, elapsed); + fCallbackAction->RecalcVelocity(timeNow, timeNow - elapsed, (fPreconditions & plHBehavior::kBehaviorTypeNeedsRecalcMask)); + + plArmatureBrain::Apply(timeNow, elapsed); +#ifndef _DEBUG + } catch (...) + { + // just catch all the crashes on exit... + plStatusLog *log = plAvatarMgr::GetInstance()->GetLog(); + log->AddLine("plAvBrainHuman::Apply - crash caught"); + } +#endif + + return true; +} + +void plAvBrainHuman::Activate(plArmatureModBase *avMod) +{ + plArmatureBrain::Activate(avMod); + + IInitBoneMap(); + IInitAnimations(); + if (!fCallbackAction) + { + plSceneObject* avObj = fArmature->GetTarget(0); + plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); + plPhysicalControllerCore* controller = avMod->GetController(); + fCallbackAction = TRACKED_NEW plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); + fCallbackAction->ActivateController(); + } + + + plSceneObject *avSO = fAvMod->GetTarget(0); + hsBool isLocal = avSO->IsLocallyOwned(); + + if (fAvMod->GetClothingOutfit() && fAvMod->GetClothingOutfit()->fGroup != plClothingMgr::kClothingBaseNoOptions) + { + if (fAvMod->IsLocalAvatar()) + fAvMod->GetClothingOutfit()->ReadFromVault(); + else + { + fAvMod->GetClothingOutfit()->WearDefaultClothing(); + fAvMod->GetClothingOutfit()->ForceUpdate(true); + } + } + if (fAvMod == plAvatarMgr::GetInstance()->GetLocalAvatar()) + plAvatarInputInterface::GetInstance()->ForceAlwaysRun(plKeyboardDevice::GetInstance()->IsCapsLockKeyOn() != 0); +} + +void plAvBrainHuman::IInitBoneMap() +{ + struct tuple { + HumanBoneID fID; + const char * fName; + }; + + tuple tupleMap[] = + { + { Pelvis, "Bone_Root" }, + // left leg + { LThigh, "Bone_LThigh" }, + { LCalf, "Bone_LCalf" }, + { LFoot, "Bone_LFoot" }, + { LFootPrint, "Print_L Foot" }, + { LToe0, "Bone_LToe" }, + + // right leg + { RThigh, "Bone_RThigh" }, + { RCalf, "Bone_RCalf" }, + { RFoot, "Bone_RFoot" }, + { RFootPrint, "Print_R Foot" }, + { RToe0, "Bone_RToe" }, + + // spine and head, starting at base of spine + { Spine, "Bone_Spine0" }, + { TrunkPrint, "Print_Trunk" }, + { Spine1, "Bone_Spine1" }, + { Spine2, "Bone_Spine2" }, + { Neck, "Bone_Neck" }, + { Head, "Bone_Head" }, + { Jaw, "Bone_Jaw" }, + + // left face bones + { LMouthLower, "Bone_LMouthLower" }, + { RMouthLower, "Bone_RMouthLower" }, + { LBrowInner, "Bone_LBrowInner" }, + { LBrowOuter, "Bone_LBrowOuter" }, + { LCheek, "Bone_LCheek" }, + { LEye, "Bone_LEye" }, + { LEyeLid01, "Bone_LEyeLid1" }, + { LEyeLid02, "Bone_LEyeLid2" }, + { LMouthCorner, "Bone_LMouthCorner" }, + { LMouthUpper, "Bone_LMouthUpper" }, + + // right face bones + { RBrowInner, "Bone_RBrowInner" }, + { RBrowOuter, "Bone_RBrowOuter" }, + { RCheek, "Bone_RCheek" }, + { REye, "Bone_REye" }, + { REyeLid01, "Bone_REyeLid1" }, + { REyeLid02, "Bone_REyeLid2" }, + { RMouthCorner, "Bone_RMouthCorner" }, + { RMouthUpper, "Bone_RMouthUpper" }, + + // Left Arm + { LClavicle, "Bone_LClavicle" }, + { LUpperArm, "Bone_LUpperArm" }, + { LForearm, "Bone_LForearm" }, + { LHand, "Bone_LHand" }, + { LHandPrint, "Print_L Hand" }, + { LMiddleFinger1, "Bone_LMiddle1" }, + { LMiddleFinger2, "Bone_LMiddle2" }, + { LMiddleFinger3, "Bone_LMiddle3" }, + { LPinkyFinger1, "Bone_LPinky1" }, + { LPinkyFinger2, "Bone_LPinky2" }, + { LPinkyFinger3, "Bone_LPinky3" }, + { LPointerFinger1, "Bone_LPointer1" }, + { LPointerFinger2, "Bone_LPointer2" }, + { LPointerFinger3, "Bone_LPointer3" }, + { LRingFinger1, "Bone_LRing1" }, + { LRingFinger2, "Bone_LRing2" }, + { LRingFinger3, "Bone_LRing3" }, + { LThumb1, "Bone_LThumb1" }, + { LThumb2, "Bone_LThumb2" }, + { LThumb3, "Bone_LThumb3" }, + + // Right Arm + { RClavicle, "Bone_RClavicle" }, + { RUpperArm, "Bone_RUpperArm" }, + { RForearm, "Bone_RForearm" }, + { RHand, "Bone_RHand" }, + { RHandPrint, "Print_R Hand" }, + { RMiddleFinger1, "Bone_RMiddle1" }, + { RMiddleFinger2, "Bone_RMiddle2" }, + { RMiddleFinger3, "Bone_RMiddle3" }, + { RPinkyFinger1, "Bone_RPinky1" }, + { RPinkyFinger2, "Bone_RPinky2" }, + { RPinkyFinger3, "Bone_RPinky3" }, + { RPointerFinger1, "Bone_RPointer1" }, + { RPointerFinger2, "Bone_RPointer2" }, + { RPointerFinger3, "Bone_RPointer3" }, + { RRingFinger1, "Bone_RRing1" }, + { RRingFinger2, "Bone_RRing2" }, + { RRingFinger3, "Bone_RRing3" }, + { RThumb1, "Bone_RThumb1" }, + { RThumb2, "Bone_RThumb2" }, + { RThumb3, "Bone_RThumb3" }, + }; + + int numTuples = sizeof(tupleMap) / sizeof(tuple); + + for(int i = 0; i < numTuples; i++) + { + HumanBoneID id = tupleMap[i].fID; + const char * name = tupleMap[i].fName; + + const plSceneObject * bone = this->fAvMod->FindBone(name); + if( bone ) + { + fAvMod->AddBoneMapping(id, bone); + } + else + hsStatusMessageF("Couldn't find standard bone %s.", name); + } +} + +plAvBrainHuman::~plAvBrainHuman() +{ + int i; + for (i = 0; i < fBehaviors.GetCount(); i++) + delete fBehaviors[i]; + fBehaviors.Reset(); + + delete fCallbackAction; + fCallbackAction = nil; +} + +void plAvBrainHuman::Deactivate() +{ + // fAvMod will be nil here when exporting. + if (fAvMod) + plAvatarMgr::GetInstance()->RemoveAvatar(fAvMod); // unregister + + plArmatureBrain::Deactivate(); +} + +void plAvBrainHuman::Suspend() +{ + // Kind of hacky... but this is a rather rare case. + // If the user lets up on the PushToTalk key in another brain + // we'll miss the message to take off the animation. + char *chatAnimName = fAvMod->MakeAnimationName("Talk"); + plAGAnimInstance *anim = fAvMod->FindAnimInstance(chatAnimName); + delete [] chatAnimName; + if (anim) + anim->FadeAndDetach(0, 1); + + IdleOnly(); + plArmatureBrain::Suspend(); +} + +void plAvBrainHuman::Resume() +{ + // If we were in another brain when the key was pressed, we missed it. + if (fAvMod->GetInputFlag(S_PUSH_TO_TALK)) + IChatOn(); + + fCallbackAction->Reset(false); + + plArmatureBrain::Resume(); +} + +hsBool plAvBrainHuman::IHandleControlMsg(plControlEventMsg* msg) +{ + ControlEventCode moveCode = msg->GetControlCode(); + + if( msg->ControlActivated() ) + { + switch(moveCode) + { + case B_CONTROL_TOGGLE_PHYSICAL: + { +#ifndef PLASMA_EXTERNAL_RELEASE // external clients can't go non-physical + plAvBrainDrive *driver = TRACKED_NEW plAvBrainDrive(20, 1); + fAvMod->PushBrain(driver); +#endif + return true; + } + break; + case S_PUSH_TO_TALK: + fAvMod->SetInputFlag(S_PUSH_TO_TALK, true); + IChatOn(); + return true; + break; + } + } else { + switch(moveCode) + { + case S_PUSH_TO_TALK: + fAvMod->SetInputFlag(S_PUSH_TO_TALK, false); + IChatOff(); + return true; + break; + } + } + return false; +} + +bool plAvBrainHuman::IsMovingForward() +{ + if ((fBehaviors.Count() <= kWalk) || (fBehaviors.Count() <= kRun)) + return false; // behaviors aren't set up yet + return (fBehaviors[kWalk]->GetStrength() > 0 || fBehaviors[kRun]->GetStrength() > 0); +} + +bool plAvBrainHuman::IsBehaviorPlaying(int behavior) +{ + if ((behavior < 0) || (behavior >= fBehaviors.Count())) + return false; + if (!fBehaviors[behavior]) + return false; + return (fBehaviors[behavior]->GetStrength() > 0); +} + +void plAvBrainHuman::Write(hsStream *stream, hsResMgr *mgr) +{ + plArmatureBrain::Write(stream, mgr); + + stream->WriteBool(fIsActor); +} + +void plAvBrainHuman::Read(hsStream *stream, hsResMgr *mgr) +{ + plArmatureBrain::Read(stream, mgr); + + fIsActor = stream->ReadBool(); +} + +hsBool plAvBrainHuman::MsgReceive(plMessage * msg) +{ + plControlEventMsg *ctrlMsg = plControlEventMsg::ConvertNoRef(msg); + if (ctrlMsg) + { + return IHandleControlMsg(ctrlMsg); + } + + plClimbMsg *climb = plClimbMsg::ConvertNoRef(msg); + if (climb) { + return IHandleClimbMsg(climb); + } + + plSwimMsg *swim = plSwimMsg::ConvertNoRef(msg); + if (swim) + { + if (swim->GetIsEntering()) + { + plAvBrainSwim *swimBrain = TRACKED_NEW plAvBrainSwim(); + swimBrain->MsgReceive(swim); + fAvMod->PushBrain(swimBrain); + } + else + { + hsStatusMessage("Got non-entering swim message. Discarding."); + } + return true; + } + plRideAnimatedPhysMsg *ride = plRideAnimatedPhysMsg::ConvertNoRef(msg); + if(ride) + { + if(ride->Entering()) + { + //plAvBrainRideAnimatedPhysical *rideBrain = TRACKED_NEW plAvBrainRideAnimatedPhysical(); + //fAvMod->PushBrain(rideBrain); + delete fCallbackAction; + plSceneObject* avObj = fArmature->GetTarget(0); + plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); + plPhysicalControllerCore* controller = fAvMod->GetController(); + fCallbackAction= TRACKED_NEW plRidingAnimatedPhysicalController(avObj, agMod->GetApplicator(kAGPinTransform), controller); + fCallbackAction->ActivateController(); + + } + else + { + delete fCallbackAction; + plSceneObject* avObj = fArmature->GetTarget(0); + plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); + plPhysicalControllerCore* controller = fAvMod->GetController(); + fCallbackAction= TRACKED_NEW plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); + fCallbackAction->ActivateController(); + //hsStatusMessage("Got an exiting ride animated physical message"); + } + } + + return plArmatureBrain::MsgReceive(msg); +} + +hsBool plAvBrainHuman::IHandleClimbMsg(plClimbMsg *msg) +{ + bool isStartClimb = msg->fCommand == plClimbMsg::kStartClimbing; + if(isStartClimb) + { + // let's build a seek task to get us to the attach point + plKey seekTarget = msg->fTarget; + plAvTaskSeek *seekTask = TRACKED_NEW plAvTaskSeek(seekTarget); + QueueTask(seekTask); + + // now a brain task to start the actual climb. + plAvBrainClimb::Mode startMode; + switch(msg->fDirection) + { + case plClimbMsg::kUp: + startMode = plAvBrainClimb::kMountingUp; + break; + case plClimbMsg::kDown: + startMode = plAvBrainClimb::kMountingDown; + break; + case plClimbMsg::kLeft: + startMode = plAvBrainClimb::kMountingLeft; + break; + case plClimbMsg::kRight: + startMode = plAvBrainClimb::kMountingRight; + break; + } + plAvBrainClimb *brain = TRACKED_NEW plAvBrainClimb(startMode); + plAvTaskBrain *brainTask = TRACKED_NEW plAvTaskBrain(brain); + QueueTask(brainTask); + } + // ** potentially controversial: + // It's fairly easy for a human brain to hit a climb trigger - like when falling off a wall. + // When this happens, We should just eat the message to keep it from travelling any further. + // The argument against is that there might be a climb brain that actually wants the message, + // but if that were true the message would have been given to that climb brain first. + return true; +} + +hsScalar plAvBrainHuman::IGetTurnStrength(double timeNow) +{ + float result = 0.f; + float timeToMaxTurn, maxTurnSpeed; + plAvBrainHuman::TurnCurve turnCurve; + if (fAvMod->FastKeyDown()) + { + timeToMaxTurn = fRunTimeToMaxTurn; + maxTurnSpeed = fRunMaxTurnSpeed; + turnCurve = fRunTurnCurve; + } + else + { + timeToMaxTurn = fWalkTimeToMaxTurn; + maxTurnSpeed = fWalkMaxTurnSpeed; + turnCurve = fWalkTurnCurve; + } + + plArmatureBehavior * turnLeft = fBehaviors.Count() >= kMovingTurnLeft ? fBehaviors[kMovingTurnLeft] : nil; + plArmatureBehavior * turnRight = fBehaviors.Count() >= kMovingTurnRight ? fBehaviors[kMovingTurnRight] : nil; + + hsScalar turnLeftStrength = turnLeft ? turnLeft->GetStrength() : 0.f; + hsScalar turnRightStrength = turnRight ? turnRight->GetStrength() : 0.f; + + + // Turning based on keypress + if ((turnLeftStrength > 0.f) + || (turnRightStrength > 0.f) + || (!fCallbackAction->IsOnGround() + && !fCallbackAction->IsControlledFlight()) + ) { + float t = (float)(timeNow - fStartedTurning); + float turnSpeed; + if(t > timeToMaxTurn) + { + turnSpeed = maxTurnSpeed; + } else { + float n = t / timeToMaxTurn; // normalize + + switch(turnCurve) { + case kTurnLinear: + // linear + turnSpeed = n * maxTurnSpeed; + break; + case kTurnExponential: + // exponential + turnSpeed = (n * n) * maxTurnSpeed; + break; + case kTurnLogarithmic: + // logarithmic + turnSpeed = n > .1 ? log10(n * 10) * maxTurnSpeed : .00001f; + break; + default: + hsAssert(false, "What the heck?"); + turnSpeed = 0.0f; + } + + } + result += fAvMod->GetKeyTurnStrength() * turnSpeed; + } + + if (!fCallbackAction->IsControlledFlight()) + result += fAvMod->GetAnalogTurnStrength() * maxTurnSpeed; + + return result; +} + +hsBool plAvBrainHuman::IHandleTaskMsg(plAvTaskMsg *msg) +{ + if(plAvSeekMsg * seekM = plAvSeekMsg::ConvertNoRef(msg)) + { + // seek and subclasses always have a seek first + if(seekM->fSmartSeek) + { + // use smart seek + plAvTaskSeek * seek = TRACKED_NEW plAvTaskSeek(seekM); + QueueTask(seek); + } + else + if (!seekM->fNoSeek) + { + // use dumb seek + plAvSeekTask *seek = TRACKED_NEW plAvSeekTask(seekM->fSeekPoint, seekM->fAlignType, seekM->fAnimName); + QueueTask(seek); + } + // else don't seek at all. + + plAvOneShotMsg * oneshotM = plAvOneShotMsg::ConvertNoRef(msg); + if(oneshotM) + { + // if it's a oneshot, add the oneshot task as well + plAvOneShotTask *oneshot = TRACKED_NEW plAvOneShotTask(oneshotM, fAvMod, this); + QueueTask(oneshot); + } + } else if (plAvPushBrainMsg *pushM = plAvPushBrainMsg::ConvertNoRef(msg)) { + plAvTaskBrain * push = TRACKED_NEW plAvTaskBrain(pushM->fBrain); + QueueTask(push); + } else if (plAvPopBrainMsg *popM = plAvPopBrainMsg::ConvertNoRef(msg)) { + plAvTaskBrain * pop = TRACKED_NEW plAvTaskBrain(); + QueueTask(pop); + } else if (plAvTaskMsg *taskM = plAvTaskMsg::ConvertNoRef(msg)) { + plAvTask *task = taskM->GetTask(); + QueueTask(task); + } else { + hsStatusMessageF("Couldn't recognize task message type.\n"); + return plArmatureBrain::IHandleTaskMsg(msg); + } + return true; +} + +void plAvBrainHuman::ResetIdle() +{ + if (fBehaviors.Count() > kIdle) + fBehaviors[kIdle]->Rewind(); +} + +void plAvBrainHuman::IdleOnly(bool instantOff) +{ + if (!fCallbackAction) + return; + + hsScalar rate = instantOff ? 0.f : 1.f; + + int i; + for (i = kWalk; i < fBehaviors.GetCount(); i++) + fBehaviors[i]->SetStrength(0, rate); +} + +bool plAvBrainHuman::IsMovementZeroBlend() +{ + int i; + for (i = 0; i < fBehaviors.GetCount(); i++) + { + if (i == kIdle || i == kFall) + continue; + + if (fBehaviors[i]->GetStrength() > 0) + return false; + } + return true; +} + +void plAvBrainHuman::TurnToPoint(hsPoint3 point) +{ + if (!fCallbackAction->IsOnGround() || IsRunningTask() || fAvMod->GetCurrentBrain() != this || !IsMovementZeroBlend()) + return; + + hsPoint3 avPos; + fAvMod->GetTarget(0)->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(&avPos); + const plCoordinateInterface* subworldCI = nil; + if (fAvMod->GetController()) + subworldCI = fAvMod->GetController()->GetSubworldCI(); + if (subworldCI) + { + point = subworldCI->GetWorldToLocal() * point; + avPos = subworldCI->GetWorldToLocal() * avPos; + } + + plAvSeekMsg *msg = TRACKED_NEW plAvSeekMsg(nil, fAvMod->GetKey(), nil, 1.f, true); + hsClearBits(msg->fFlags, plAvSeekMsg::kSeekFlagForce3rdPersonOnStart); + hsSetBits(msg->fFlags, plAvSeekMsg::kSeekFlagNoWarpOnTimeout | plAvSeekMsg::kSeekFlagRotationOnly); + msg->fTargetLookAt = point; + msg->fTargetPos = avPos; + msg->SetBCastFlag(plMessage::kNetPropagate); + msg->Send(); +} + +void plAvBrainHuman::IChatOn() +{ + char *chatAnimName = fAvMod->MakeAnimationName("Talk"); + + // check that we aren't adding this twice... + if (!fAvMod->FindAnimInstance(chatAnimName)) + { + plKey avKey = fAvMod->GetKey(); + plAvAnimTask *animTask = TRACKED_NEW plAvAnimTask(chatAnimName, 0.0, 1.0, 1.0, 0.0, true, true, true); + if (animTask) + { + plAvTaskMsg *taskMsg = TRACKED_NEW plAvTaskMsg(avKey, avKey, animTask); + taskMsg->SetBCastFlag(plMessage::kNetPropagate); + taskMsg->Send(); + } + } + + delete [] chatAnimName; +} + +void plAvBrainHuman::IChatOff() +{ + char *chatAnimName = fAvMod->MakeAnimationName("Talk"); + plKey avKey = fAvMod->GetKey(); + plAvAnimTask *animTask = TRACKED_NEW plAvAnimTask(chatAnimName, -1.0); + if (animTask) + { + plAvTaskMsg *taskMsg = TRACKED_NEW plAvTaskMsg(avKey, avKey, animTask); + taskMsg->SetBCastFlag(plMessage::kNetPropagate); + taskMsg->Send(); + } + delete[] chatAnimName; +} + +hsBool plAvBrainHuman::IInitAnimations() +{ + hsBool result = false; + + plAGAnim *idle = fAvMod->FindCustomAnim("Idle"); + plAGAnim *walk = fAvMod->FindCustomAnim("Walk"); + plAGAnim *run = fAvMod->FindCustomAnim("Run"); + plAGAnim *walkBack = fAvMod->FindCustomAnim("WalkBack"); + plAGAnim *stepLeft = fAvMod->FindCustomAnim("StepLeft"); + plAGAnim *stepRight = fAvMod->FindCustomAnim("StepRight"); + plAGAnim *standingLeft = fAvMod->FindCustomAnim("TurnLeft"); + plAGAnim *standingRight = fAvMod->FindCustomAnim("TurnRight"); + plAGAnim *fall = fAvMod->FindCustomAnim("Fall"); + plAGAnim *standJump = fAvMod->FindCustomAnim("StandingJump"); + plAGAnim *walkJump = fAvMod->FindCustomAnim("WalkingJump"); + plAGAnim *runJump = fAvMod->FindCustomAnim("RunningJump"); + plAGAnim *groundImpact = fAvMod->FindCustomAnim("GroundImpact"); + plAGAnim *runningImpact = fAvMod->FindCustomAnim("RunningImpact"); + plAGAnim *movingLeft = nil; // fAvMod->FindCustomAnim("LeanLeft"); + plAGAnim *movingRight = nil; // fAvMod->FindCustomAnim("LeanRight"); + plAGAnim *pushWalk = fAvMod->FindCustomAnim("BallPushWalk"); + + //plAGAnim *pushIdle = fAvMod->FindCustomAnim("BallPushIdle"); + + const float kDefaultFade = 3.0; // most animations fade in and out in 1/4 of a second. + + if (idle && walk && run && walkBack && standingLeft && standingRight && stepLeft && stepRight) + { + plHBehavior *behavior; + fBehaviors.SetCountAndZero(kHuBehaviorMax); + fBehaviors[kIdle] = behavior = TRACKED_NEW Idle; + behavior->Init(idle, true, this, fAvMod, kDefaultFade, kDefaultFade, kIdle, plHBehavior::kBehaviorTypeIdle); + behavior->SetStrength(1.f, 0.f); + + fBehaviors[kWalk] = behavior = TRACKED_NEW Walk; + behavior->Init(walk, true, this, fAvMod, kDefaultFade, 5.f, kWalk, plHBehavior::kBehaviorTypeWalk); + + fBehaviors[kRun] = behavior = TRACKED_NEW Run; + behavior->Init(run, true, this, fAvMod, kDefaultFade, 2.0, kRun, plHBehavior::kBehaviorTypeRun); + + fBehaviors[kWalkBack] = behavior = TRACKED_NEW WalkBack; + behavior->Init(walkBack, true, this, fAvMod, kDefaultFade, kDefaultFade, kWalkBack, plHBehavior::kBehaviorTypeWalkBack); + + fBehaviors[kStandingTurnLeft] = behavior = TRACKED_NEW StandingTurnLeft; + behavior->Init(standingLeft, true, this, fAvMod, 3.0f, 6.0f, kStandingTurnLeft, plHBehavior::kBehaviorTypeTurnLeft); + + fBehaviors[kStandingTurnRight] = behavior = TRACKED_NEW StandingTurnRight; + behavior->Init(standingRight, true, this, fAvMod, 3.0f, 6.0f, kStandingTurnRight, plHBehavior::kBehaviorTypeTurnRight); + + fBehaviors[kStepLeft] = behavior = TRACKED_NEW StepLeft; + behavior->Init(stepLeft, true, this, fAvMod, kDefaultFade, kDefaultFade, kStepLeft, plHBehavior::kBehaviorTypeSidestepLeft); + + fBehaviors[kStepRight] = behavior = TRACKED_NEW StepRight; + behavior->Init(stepRight, true, this, fAvMod, kDefaultFade, kDefaultFade, kStepRight, plHBehavior::kBehaviorTypeSidestepRight); + + // Warning: Changing the blend times of the jump animations will affect the path you take, because until we're fully blended, + // we won't be using the full motion defined in the animation. This isn't an issue for standing jump, but you need to be + // aware of it for the walk/run jumps. + fBehaviors[kFall] = behavior = TRACKED_NEW Fall; + behavior->Init(fall, true, this, fAvMod, 1.0f, 10, kFall, plHBehavior::kBehaviorTypeFall); + + fBehaviors[kStandingJump] = behavior = TRACKED_NEW StandingJump; + behavior->Init(standJump, false, this, fAvMod, kDefaultFade, kDefaultFade, kStandingJump, plHBehavior::kBehaviorTypeStandingJump); + + fBehaviors[kWalkingJump] = behavior = TRACKED_NEW WalkingJump; + behavior->Init(walkJump, false, this, fAvMod, 10, 3.0, kWalkingJump, plHBehavior::kBehaviorTypeWalkingJump); + + fBehaviors[kRunningJump] = behavior = TRACKED_NEW RunningJump; + behavior->Init(runJump, false, this, fAvMod, 10, 2.0, kRunningJump, plHBehavior::kBehaviorTypeRunningJump); + + fBehaviors[kGroundImpact] = behavior = TRACKED_NEW GroundImpact; + behavior->Init(groundImpact, false, this, fAvMod, 6.0f, kDefaultFade, kGroundImpact, plHBehavior::kBehaviorTypeGroundImpact); + + fBehaviors[kRunningImpact] = behavior = TRACKED_NEW RunningImpact; + behavior->Init(runningImpact, false, this, fAvMod, 6.0f, kDefaultFade, kRunningImpact, plHBehavior::kBehaviorTypeRunningImpact); + + fBehaviors[kMovingTurnLeft] = behavior = TRACKED_NEW MovingTurnLeft; + behavior->Init(movingLeft, true, this, fAvMod, kDefaultFade, kDefaultFade, kMovingTurnLeft, plHBehavior::kBehaviorTypeMovingTurnLeft); + + fBehaviors[kMovingTurnRight] = behavior = TRACKED_NEW MovingTurnRight; + behavior->Init(movingRight, true, this, fAvMod, kDefaultFade, kDefaultFade, kMovingTurnRight, plHBehavior::kBehaviorTypeMovingTurnRight); + + fBehaviors[kPushWalk] = behavior = TRACKED_NEW PushWalk; + behavior->Init(pushWalk, true, this, fAvMod, kDefaultFade, kDefaultFade, kPushWalk, plHBehavior::kBehaviorTypePushWalk); + + //fBehaviors[kPushIdle] = behavior = TRACKED_NEW PushIdle; + //behavior->Init(pushIdle, true, this, fAvMod, kDefaultFade, kDefaultFade, kPushIdle, plHBehavior::kBehaviorTypePushIdle); + + result = true; + } + + return result; +} + +hsBool plAvBrainHuman::RunStandardBehaviors(double timeNow, float elapsed) +{ + int i; + for (i = 0; i < fBehaviors.GetCount(); i++) + { + plHBehavior *behavior = (plHBehavior*)fBehaviors[i]; + if (behavior->PreCondition(timeNow, elapsed)) + { + behavior->SetStrength(1.f, behavior->fFadeIn); + behavior->Process(timeNow, elapsed); + fPreconditions |= behavior->GetType(); + } + else + { + behavior->SetStrength(0.f, behavior->fFadeOut); + fPreconditions &= ~behavior->GetType(); + } + } + return true; +} + +void plAvBrainHuman::SetStartedTurning(double when) +{ + fStartedTurning = when; +} + +void plAvBrainHuman::Spawn(double timeNow) +{ + plArmatureBrain::Spawn(timeNow); + IdleOnly(true); // reset any behavior state we may have accumulated while waiting to spawn + + // IdleOnly will set the blends of all anims to zero, and setting the blends will tell the AGModifier + // that it needs to compile. Trouble is, the modifier only checks once per frame. MoveViaAnimation + // works on the physics timestep, and will get called before compilation happens. It will go straight + // to the old compiled channel, ignore the blends and still move via any anim that was playing before + // we linked (only for the first frame). + // + // Trouble is, that first frame is usually a large physics step which we don't fully resolve. This means + // we could miss our spawn point, or worse, spawn into collision geometry and fall through. + // + // So we force the modifier to recompile. + if (fAvMod) + fAvMod->Compile(timeNow); +} + +hsBool plAvBrainHuman::LeaveAge() +{ + plPhysicalControllerCore* controller = fAvMod->GetController(); + if(!controller->BehavingLikeAnAnimatedPhysical()) + { + controller->BehaveLikeAnimatedPhysical(true); + delete fCallbackAction; + plSceneObject* avObj = fArmature->GetTarget(0); + plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); + fCallbackAction= TRACKED_NEW plWalkingController(avObj, agMod->GetApplicator(kAGPinTransform), controller); + fCallbackAction->ActivateController(); + } + plArmatureBrain::LeaveAge(); + + + + // pin the physical so it doesn't fall when the world is deleted + fAvMod->EnablePhysics(false); + // this will get set to true when we hit ground + fCallbackAction->Reset(true); + return false; +} + +void plAvBrainHuman::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + sprintf(strBuf, "Brain type: Human"); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + + const char *grounded = fCallbackAction->IsOnGround() ? "yes" : "no"; + const char *falseGrounded = fCallbackAction->IsOnFalseGround() ? "yes" : "no"; + const char *pushing = (fCallbackAction->GetPushingPhysical() ? (fCallbackAction->GetFacingPushingPhysical() ? "facing" : "behind") : "none"); + sprintf(strBuf, "Ground: %3s, FalseGround: %3s, AirTime: %5.2f (Peak: %5.2f), PushingPhys: %6s", + grounded, falseGrounded, fCallbackAction->GetAirTime(), fCallbackAction->GetImpactTime(), pushing); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + + int i; + //strBuf[0] = '\0'; + //for (i = 0; i < 32; i++) + // strcat(strBuf, fPreconditions & (0x1 << i) ? "1" : "0"); + //debugTxt.DrawString(x, y, strBuf); + //y += lineHeight; + + for (i = 0; i < fBehaviors.GetCount(); i++) + fBehaviors[i]->DumpDebug(x, y, lineHeight, strBuf, debugTxt); + + debugTxt.DrawString(x, y, "Tasks:"); + y += lineHeight; + + if(fCurTask) + { + debugTxt.DrawString(x, y, "Current task:"); + y += lineHeight; + + int indentedX = x + 4; + fCurTask->DumpDebug("-", indentedX, y, lineHeight, strBuf, debugTxt); + } + int tasks = fTaskQueue.size(); + if(tasks > 0) + { + debugTxt.DrawString(x, y, "Tasks in the Queue:"); + y += lineHeight; + + int indentedX = x + 4; + + for (int i = 0; i < tasks; i++) + { + plAvTask *each = fTaskQueue[i]; + each->DumpDebug("-", indentedX, y, lineHeight, strBuf, debugTxt); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////// +// BEHAVIOR + +plHBehavior::plHBehavior() : + fAvMod(nil), + fHuBrain(nil), + fFadeIn(1.0f), + fFadeOut(-1.0f), + fMaxBlend(1.0f) +{ +} + +plHBehavior::~plHBehavior() +{ +} + +void plHBehavior::Init(plAGAnim *anim, hsBool loop, plAvBrainHuman *brain, + plArmatureMod *body, float fadeIn, float fadeOut, + UInt8 index, UInt32 type /* = 0 */) +{ + plArmatureBehavior::Init(anim, loop, brain, body, index); + fAvMod = body; + fHuBrain = brain; + fFadeIn = fadeIn; + fFadeOut = fadeOut; + fType = type; + + fStartMsgSent = false; // start message hasn't been sent yet + fStopMsgSent = false; // stop message hasn't been sent yet +} + +void plHBehavior::IStart() +{ + plArmatureBehavior::IStart(); + fAvMod->SynchIfLocal(hsTimer::GetSysSeconds(), false); + + if (!fStartMsgSent) + fAvMod->IFireBehaviorNotify(fType); + + fStartMsgSent = true; // we just sent a start message + fStopMsgSent = false; // we haven't sent a stop message yet +} + +void plHBehavior::IStop() +{ + plArmatureBehavior::IStop(); + fAvMod->SynchIfLocal(hsTimer::GetSysSeconds(), false); + + if (!fStopMsgSent) + fAvMod->IFireBehaviorNotify(fType, false); + + fStartMsgSent = false; // we haven't sent a start message yet + fStopMsgSent = true; // we just sent a stop message +} + +static plRandom sRandom; +void Idle::IStart() +{ + plHBehavior::IStart(); + if (fAnim) + { + hsScalar newStart = sRandom.RandZeroToOne() * fAnim->GetAnimation()->GetLength(); + fAnim->SetCurrentTime(newStart, true); + } +} + +hsBool Run::PreCondition(double time, float elapsed) +{ + if (fAnim) + { + if (fAvMod->ForwardKeyDown() && fAvMod->FastKeyDown() && fHuBrain->fCallbackAction->IsOnGround() && + (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + return true; + } + return false; +} + +hsBool Walk::PreCondition(double time, float elapsed) +{ + if (fAnim) + { + if (fAvMod->ForwardKeyDown() && !fAvMod->FastKeyDown() && fHuBrain->fCallbackAction->IsOnGround() && + (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + return true; + } + return false; +} + +hsBool WalkBack::PreCondition(double time, float elapsed) +{ + if (fAnim) + { + if (fAvMod->BackwardKeyDown() && !fAvMod->ForwardKeyDown() && fHuBrain->fCallbackAction->IsOnGround() && + (!fHuBrain->fCallbackAction->GetPushingPhysical() || fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + return true; + } + return false; +} + +hsBool StepLeft::PreCondition(double time, float elapsed) +{ + if (fAnim) + { + return ((fAvMod->StrafeLeftKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnLeftKeyDown())) && + !(fAvMod->StrafeRightKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnRightKeyDown())) && + !(fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && + fHuBrain->fCallbackAction->IsOnGround()); + } + return false; +} + +hsBool StepRight::PreCondition(double time, float elapsed) +{ + if (fAnim) + { + return ((fAvMod->StrafeRightKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnRightKeyDown())) && + !(fAvMod->StrafeLeftKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnLeftKeyDown())) && + !(fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && + fHuBrain->fCallbackAction->IsOnGround()); + } + return false; +} + +hsBool StandingTurnLeft::PreCondition(double time, float elapsed) +{ + if (fAnim) + { + if (fAvMod->TurnLeftKeyDown() && !fAvMod->TurnRightKeyDown() && !fAvMod->StrafeKeyDown() && + !fAvMod->ForwardKeyDown() && !fAvMod->BackwardKeyDown()) + { + return true; + } + } + return false; +} + +hsBool StandingTurnRight::PreCondition(double time, float elapsed) +{ + if (fAnim) + { + if (fAvMod->TurnRightKeyDown() && !fAvMod->TurnLeftKeyDown() && !fAvMod->StrafeKeyDown() && + !fAvMod->ForwardKeyDown() && !fAvMod->BackwardKeyDown()) + { + return true; + } + } + return false; +} + +void MovingTurn::IStart() +{ + plHBehavior::IStart(); + fHuBrain->SetStartedTurning(hsTimer::GetSysSeconds()); +} + +hsBool MovingTurnLeft::PreCondition(double time, float elapsed) +{ + if (fAvMod->GetTurnStrength() > 0) + { + if (fHuBrain->fCallbackAction->IsOnGround() && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && + (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + return true; + } + return false; +} + +hsBool MovingTurnRight::PreCondition(double time, float elapsed) +{ + if (fAvMod->GetTurnStrength() < 0) + { + if (fHuBrain->fCallbackAction->IsOnGround() && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown()) && + (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + return true; + } + + return false; +} + +void Jump::IStart() +{ + fHuBrain->fCallbackAction->EnableControlledFlight(true); + + plHBehavior::IStart(); +} + +void Jump::IStop() +{ + fHuBrain->fCallbackAction->EnableControlledFlight(false); + + plHBehavior::IStop(); +} + +hsBool StandingJump::PreCondition(double time, float elapsed) +{ + if (fAnim) + { + if (GetStrength() > 0.f) + { + if (!fHuBrain->fCallbackAction->IsControlledFlight() || + fAnim->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) >= fAnim->GetTimeConvert()->GetEnd()) + { + return false; + } + return !fAnim->IsFinished(); + } + else + { + if (fAvMod->JumpKeyDown() && + !fAvMod->ForwardKeyDown() && + fAnim->GetBlend() == 0.0f && + fHuBrain->fCallbackAction->IsOnGround()) + { + if (fAvMod->ConsumeJump()) + return true; + } + } + } + return false; +} + +hsBool WalkingJump::PreCondition(double time, float elapsed) +{ + if (fAnim) + { + if (GetStrength() > 0.f) + { + if (!fHuBrain->fCallbackAction->IsControlledFlight() || + fAnim->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) >= fAnim->GetTimeConvert()->GetEnd()) + { + return false; + } + return !fAnim->IsFinished(); + } + else + { + if (fAvMod->JumpKeyDown() && + !fAvMod->FastKeyDown() && + fAvMod->ForwardKeyDown() && + fAnim->GetBlend() == 0.0f && + fHuBrain->fCallbackAction->IsOnGround() && + (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + { + if (fAvMod->ConsumeJump()) + return true; + } + } + } + return false; +} + +hsBool RunningJump::PreCondition(double time, float elapsed) +{ + if (fAnim) + { + if (GetStrength() > 0.f) + { + if (!fHuBrain->fCallbackAction->IsControlledFlight() || + fAnim->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) >= fAnim->GetTimeConvert()->GetEnd()) + { + return false; + } + return !fAnim->IsFinished(); + } + else + { + if (fAvMod->JumpKeyDown() && + fAvMod->ForwardKeyDown() && + fAvMod->FastKeyDown() && + fAnim->GetBlend() == 0.0f && + fHuBrain->fCallbackAction->IsOnGround() && + (!fHuBrain->fCallbackAction->GetPushingPhysical() || !fHuBrain->fCallbackAction->GetFacingPushingPhysical())) + { + if (fAvMod->ConsumeJump()) + return true; + } + } + } + return false; +} + + +static const float kRunningImpactThresh = -1.0f; +static const float kFullImpactVel = 30.0f; // At this velocity (or greater) we blend the impact at full strength. +static const float kMinImpactVel = 10.f; + +// If we just test IsOnGround(), we do a lot of impacts while running down stairs, so the impact +// behaviors have a more forgiving threshold. +static const float kMinAirTime = .5f; + +RunningImpact::RunningImpact() : fDuration(0.0f) {} + +hsBool RunningImpact::PreCondition(double time, float elapsed) +{ + if (fDuration > 0.0f) + fDuration = fDuration - elapsed; + else if (fHuBrain->fCallbackAction->IsOnGround() && fHuBrain->fCallbackAction->GetImpactTime() > kMinAirTime) + { + if (fHuBrain->fCallbackAction->GetImpactVelocity().fZ < -kMinImpactVel) + { + if (fHuBrain->fCallbackAction->GetImpactVelocity().fY < kRunningImpactThresh) + { + fMaxBlend = 0.5f + (0.5f * (-fHuBrain->fCallbackAction->GetImpactVelocity().fZ / (kFullImpactVel - kMinImpactVel))); + if (fMaxBlend > 1) + fMaxBlend = 1; + fDuration = 1.0f / fFadeIn; + } + } + } + return(fDuration > 0.0f); +} + +void RunningImpact::IStop() +{ + fDuration = 0.0f; + plHBehavior::IStop(); +} + +GroundImpact::GroundImpact() : fDuration(0.0f) {} + +hsBool GroundImpact::PreCondition(double time, float elapsed) +{ + + bool result = false; + if (fDuration > 0.0f) + fDuration = fDuration - elapsed; + else if (fHuBrain->fCallbackAction->IsOnGround() && fHuBrain->fCallbackAction->GetImpactTime() > kMinAirTime) + { + if (fHuBrain->fCallbackAction->GetImpactVelocity().fZ < -kMinImpactVel) + { + if (fHuBrain->fCallbackAction->GetImpactVelocity().fY >= kRunningImpactThresh) + { + fMaxBlend = 0.5f + (0.5f * (-fHuBrain->fCallbackAction->GetImpactVelocity().fZ / (kFullImpactVel - kMinImpactVel))); + if (fMaxBlend > 1) + fMaxBlend = 1; + fDuration = 1.0f / fFadeIn; + } + } + } + + return(fDuration > 0.0f); +} + +void GroundImpact::IStop() +{ + fDuration = 0.0f; + plHBehavior::IStop(); +} + +hsBool Fall::PreCondition(double time, float elapsed) +{ + return !fHuBrain->fCallbackAction->IsOnGround() && fHuBrain->fCallbackAction->HitGroundInThisAge(); +} + +void Fall::Process(double time, float elapsed) +{ + // We don't see remote players panic link (from timeouts) because we don't know if they're + // really falling, or if our understanding of their physical location is just not up-to-date. + if (plAvatarMgr::GetInstance()->GetLocalAvatar() == fAvMod) + { + if (fAnim && fAnim->GetBlend() > 0.8) + { + float panicThresh = plAvBrainHuman::kAirTimePanicThreshold; + if (panicThresh > 0.0f && fHuBrain->fCallbackAction->GetAirTime() > panicThresh) + { + fHuBrain->IdleOnly(); // clear the fall state; we're going somewhere new + fAvMod->PanicLink(); + } + } + } +} + +extern float QuatAngleDiff(const hsQuat &a, const hsQuat &b); +void Push::Process(double time, float elapsed) +{ + hsQuat rot; + hsPoint3 pos; + fAvMod->GetPositionAndRotationSim(&pos, &rot); + + hsPoint3 lookAt; + fHuBrain->fCallbackAction->GetPushingPhysical()->GetPositionSim(lookAt); + hsVector3 up(0.f, 0.f, 1.f); + hsScalar angle = hsATan2(lookAt.fY - pos.fY, lookAt.fX - pos.fX) + hsScalarPI / 2; + hsQuat targRot(angle, &up); + + const hsScalar kTurnSpeed = 3.f; + hsScalar angDiff = QuatAngleDiff(rot, targRot); + hsScalar turnSpeed = (angDiff > elapsed * kTurnSpeed ? kTurnSpeed : angDiff / elapsed); + + hsQuat invRot = targRot.Conjugate(); + hsPoint3 globFwd = invRot.Rotate(&kAvatarForward); + globFwd = rot.Rotate(&globFwd); + + if (globFwd.fX < 0) + fHuBrain->fCallbackAction->SetTurnStrength(-turnSpeed); + else + fHuBrain->fCallbackAction->SetTurnStrength(turnSpeed); +} + +//hsBool PushIdle::PreCondition(double time, float elapsed) +//{ +// return (fHuBrain->fCallbackAction->GetPushingPhysical() && +// fHuBrain->fCallbackAction->IsOnGround() && +// !fAvMod->TurnLeftKeyDown() && !fAvMod->TurnRightKeyDown() +// && fAvMod->GetTurnStrength() == 0); +//} + +hsBool PushWalk::PreCondition(double time, float elapsed) +{ + return (fHuBrain->fCallbackAction->GetPushingPhysical() && fHuBrain->fCallbackAction->GetFacingPushingPhysical() && + fHuBrain->fCallbackAction->IsOnGround() && + fAvMod->ForwardKeyDown()); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// UTIL FUNCTIONS +// +///////////////////////////////////////////////////////////////////////////////////////// + +bool PushSimpleMultiStage(plArmatureMod *avatar, const char *enterAnim, const char *idleAnim, const char *exitAnim, + bool netPropagate, bool autoExit, plAGAnim::BodyUsage bodyUsage, plAvBrainGeneric::BrainType type /* = kGeneric */) +{ + plAvBrainHuman *huBrain = plAvBrainHuman::ConvertNoRef(avatar->FindBrainByClass(plAvBrainHuman::Index())); + const char *names[3] = {enterAnim, idleAnim, exitAnim}; + if (!huBrain || !huBrain->fCallbackAction->IsOnGround() || !huBrain->fCallbackAction->HitGroundInThisAge() || huBrain->IsRunningTask() || + !avatar->IsPhysicsEnabled() || avatar->FindMatchingGenericBrain(names, 3)) + return false; + + // XXX + if (type == plAvBrainGeneric::kSit || type == plAvBrainGeneric::kSitOnGround) + { + plAvBrainSwim *swimBrain = plAvBrainSwim::ConvertNoRef(avatar->GetCurrentBrain()); + if (swimBrain && !swimBrain->IsWalking()) + return false; + } + + // if autoExit is true, then we will immediately exit the idle loop when the user hits a move + // key. otherwise, we'll loop until someone sends a message telling us explicitly to advance + plAnimStage::AdvanceType idleAdvance = autoExit ? plAnimStage::kAdvanceOnMove : plAnimStage::kAdvanceNone; + + plAnimStageVec *v = TRACKED_NEW plAnimStageVec; + plAnimStage *s1 = TRACKED_NEW plAnimStage(enterAnim, + 0, + plAnimStage::kForwardAuto, plAnimStage::kBackNone, + plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + v->push_back(s1); + plAnimStage *s2 = TRACKED_NEW plAnimStage(idleAnim, 0, + plAnimStage::kForwardAuto, plAnimStage::kBackNone, + idleAdvance, plAnimStage::kRegressNone, + -1); + v->push_back(s2); + plAnimStage *s3 = TRACKED_NEW plAnimStage(exitAnim, 0, + plAnimStage::kForwardAuto, plAnimStage::kBackNone, + plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, + 0); + v->push_back(s3); + + plAvBrainGeneric *b = TRACKED_NEW plAvBrainGeneric(v, nil, nil, nil, plAvBrainGeneric::kExitAnyTask | plAvBrainGeneric::kExitNewBrain, + 2.0f, 2.0f, plAvBrainGeneric::kMoveStandstill); + + b->SetBodyUsage(bodyUsage); + b->SetType(type); + + plAvTaskBrain *bt = TRACKED_NEW plAvTaskBrain(b); + plAvTaskMsg *btm = TRACKED_NEW plAvTaskMsg(plAvatarMgr::GetInstance()->GetKey(), avatar->GetKey(), bt); + if(netPropagate) + btm->SetBCastFlag(plMessage::kNetPropagate); + btm->Send(); + + return true; +} + +bool AvatarEmote(plArmatureMod *avatar, const char *emoteName) +{ + bool result = false; + char *fullName = avatar->MakeAnimationName(emoteName); + plAGAnim *anim = plAGAnim::FindAnim(fullName); + plEmoteAnim *emote = plEmoteAnim::ConvertNoRef(anim); + hsBool alreadyActive = avatar->FindAnimInstance(fullName) != nil; + plAvBrainHuman *huBrain = plAvBrainHuman::ConvertNoRef(avatar->FindBrainByClass(plAvBrainHuman::Index())); + delete[] fullName; + + // XXX + plAvBrainSwim *swimBrain = plAvBrainSwim::ConvertNoRef(avatar->GetCurrentBrain()); + if (swimBrain && swimBrain->IsSwimming()) + return false; + + if (huBrain && huBrain->fCallbackAction->IsOnGround() && huBrain->fCallbackAction->HitGroundInThisAge() && !huBrain->IsRunningTask() && + emote && !alreadyActive && avatar->IsPhysicsEnabled()) + { + plKey avKey = avatar->GetKey(); + float fadeIn = emote->GetFadeIn(); + float fadeOut = emote->GetFadeOut(); + plAnimStage *s1 = TRACKED_NEW plAnimStage(emoteName, + 0, + plAnimStage::kForwardAuto, + plAnimStage::kBackNone, + plAnimStage::kAdvanceOnMove, + plAnimStage::kRegressNone, + 0); + plAnimStageVec *v = TRACKED_NEW plAnimStageVec; + v->push_back(s1); + + plAvBrainGeneric *b = TRACKED_NEW plAvBrainGeneric(v, nil, nil, nil, + plAvBrainGeneric::kExitAnyInput | plAvBrainGeneric::kExitNewBrain | plAvBrainGeneric::kExitAnyTask, + 2.0f, 2.0f, huBrain->IsActor() ? plAvBrainGeneric::kMoveRelative : plAvBrainGeneric::kMoveStandstill); + b->SetType(plAvBrainGeneric::kEmote); + b->SetBodyUsage(emote->GetBodyUsage()); + plAvTaskBrain *bt = TRACKED_NEW plAvTaskBrain(b); + plAvTaskMsg *btm = TRACKED_NEW plAvTaskMsg(plAvatarMgr::GetInstance()->GetKey(), avKey, bt); + btm->SetBCastFlag(plMessage::kNetPropagate); + btm->Send(); + + result = true; + } + return result; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h new file mode 100644 index 00000000..68621721 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.h @@ -0,0 +1,393 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLAVBRAINHUMAN_INC +#define PLAVBRAINHUMAN_INC + +#define HI_RES_MOVEMENT + +#include "plAvBrainGeneric.h" +#include "plAvBehaviors.h" +#include "plAGAnim.h" + +#pragma warning(disable: 4284) +#include +#include "hsTemplates.h" + +class plMatrixChannel; +class plMatrixMultiplyApplicator; +class plAGAnimInstance; +class plAvTask; +class plAvTaskMsg; +class plAvBrainHuman; +class plWalkingController; +class plArmatureUpdateMsg; +class plClimbMsg; +class plControlEventMsg; + +class plAvBrainHuman : public plArmatureBrain +{ +public: + plAvBrainHuman(bool isActor = false); + virtual ~plAvBrainHuman(); + + CLASSNAME_REGISTER( plAvBrainHuman ); + GETINTERFACE_ANY( plAvBrainHuman, plArmatureBrain ); + + virtual hsBool Apply(double timeNow, hsScalar elapsed); + virtual void Activate(plArmatureModBase *avMod); + virtual void Deactivate(); + virtual void Suspend(); + virtual void Resume(); + virtual void Spawn(double timeNow); + virtual hsBool LeaveAge(); + + bool IsActor() const {return fIsActor;} + void IsActor(bool isActor) {fIsActor = isActor;} + + bool IsMovementZeroBlend(); + void TurnToPoint(hsPoint3 point); + hsBool RunStandardBehaviors(double timeNow, float elapsed); + void SetStartedTurning(double when); + + virtual bool IsMovingForward(); // we're either walking or running. doesn't account for sliding. + void ResetIdle(); + void IdleOnly(bool instantOff = false); + + bool IsBehaviorPlaying(int behavior); // returns whether the specified behavior is playing at all (see list of behaviors below) + + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual hsBool MsgReceive(plMessage *msg); + virtual void DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); + + // Hardwired Identifiers for all the canonical bones. + enum HumanBoneID { + Pelvis, + // left leg + LThigh, LCalf, LFoot, LFootPrint, LToe0, + + // right leg + RThigh, RCalf, RFoot, RFootPrint, RToe0, + + // spine and head, starting at base of spine + Spine, TrunkPrint, Spine1, Spine2, Neck, Head, Jaw, LMouthLower, RMouthLower, + LBrowInner, LBrowOuter, LCheek, LEye, LEyeLid01, LEyeLid02, LMouthCorner, LMouthUpper, + RBrowInner, RBrowOuter, RCheek, REye, REyeLid01, REyeLid02, RMouthCorner, RMouthUpper, + + // Left Arm + LClavicle, LUpperArm, LForearm, LHand, LHandPrint, LMiddleFinger1, LMiddleFinger2, LMiddleFinger3, + LPinkyFinger1, LPinkyFinger2, LPinkyFinger3, LPointerFinger1, LPointerFinger2, LPointerFinger3, + LRingFinger1, LRingFinger2, LRingFinger3, LThumb1, LThumb2, LThumb3, + + // Right Arm + RClavicle, RUpperArm, RForearm, RHand, RHandPrint, RMiddleFinger1, RMiddleFinger2, RMiddleFinger3, + RPinkyFinger1, RPinkyFinger2, RPinkyFinger3, RPointerFinger1, RPointerFinger2, RPointerFinger3, + RRingFinger1, RRingFinger2, RRingFinger3, RThumb1, RThumb2, RThumb3 + }; + + enum TurnCurve + { + kTurnLinear, + kTurnExponential, + kTurnLogarithmic + }; + + enum // Indicies for the brains array of behaviors + { + kIdle, + kWalk, + kRun, + kWalkBack, + kStandingTurnLeft, + kStandingTurnRight, + kStepLeft, + kStepRight, + kFall, + kStandingJump, + kWalkingJump, + kRunningJump, + kGroundImpact, + kRunningImpact, + kMovingTurnLeft, + kMovingTurnRight, + kPushWalk, + //kPushIdle, + kHuBehaviorMax, + }; + + static void SetTimeToMaxTurn(float time, hsBool walk); + static float GetTimeToMaxTurn(hsBool walk); + static void SetMaxTurnSpeed(float radsPerSec, hsBool walk); + static float GetMaxTurnSpeed(hsBool walk); + static void SetTurnCurve(TurnCurve curve, hsBool walk); + static TurnCurve GetTurnCurve(hsBool walk); + + static const hsScalar kControlledFlightThreshold; + static const hsScalar kAirTimeThreshold; + static const hsScalar kAirTimePanicThreshold; + plWalkingController* fCallbackAction; + +protected: + plAGAnim *FindCustomAnim(const char *baseName); + virtual hsBool IHandleControlMsg(plControlEventMsg *ctrlMsg); + virtual hsBool IHandleClimbMsg(plClimbMsg *msg); + virtual hsBool IHandleTaskMsg(plAvTaskMsg *msg); + virtual hsBool IInitAnimations(); + virtual void IInitBoneMap(); + hsScalar IGetTurnStrength(double timeNow); + void IChatOn(); + void IChatOff(); + hsBool IValidateAnimations(); + + plAGModifier *fHandleAGMod; // the ag modifier that's attached to our top object + double fStartedTurning; // when did we start turning? + UInt32 fPreconditions; + bool fIsActor; // are we an actor with special privileges? + static float fWalkTimeToMaxTurn; + static float fRunTimeToMaxTurn; + static float fWalkMaxTurnSpeed; + static float fRunMaxTurnSpeed; + static TurnCurve fWalkTurnCurve; + static TurnCurve fRunTurnCurve; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +class plHBehavior : public plArmatureBehavior +{ + friend class plAvBrainHuman; + friend class plAvBrainCritter; + +public: + // ID flags for human behaviors. These are used by plBehaviorNotifyMsg + // to identify what type of behavior just triggered. + // This is an old way of doing things. I'd like to remove it, but a lot of + // python scripts use it as is. + enum BehaviorType + { + kBehaviorTypeStandingJump = 0x00000001, + kBehaviorTypeWalkingJump = 0x00000002, + kBehaviorTypeRunningJump = 0x00000004, + kBehaviorTypeAnyJump = kBehaviorTypeStandingJump | kBehaviorTypeWalkingJump | kBehaviorTypeRunningJump, + kBehaviorTypeRunningImpact = 0x00000008, + kBehaviorTypeGroundImpact = 0x00000010, + kBehaviorTypeAnyImpact = kBehaviorTypeRunningImpact | kBehaviorTypeGroundImpact, + kBehaviorTypeIdle = 0x00000020, + kBehaviorTypeWalk = 0x00000040, + kBehaviorTypeRun = 0x00000080, + kBehaviorTypeWalkBack = 0x00000100, + kBehaviorTypeTurnLeft = 0x00000200, + kBehaviorTypeTurnRight = 0x00000400, + kBehaviorTypeSidestepLeft = 0x00000800, + kBehaviorTypeSidestepRight = 0x00001000, + kBehaviorTypeFall = 0x00002000, + kBehaviorTypeMovingTurnLeft = 0x00004000, + kBehaviorTypeMovingTurnRight = 0x00008000, + kBehaviorTypeLinkIn = 0x00010000, + kBehaviorTypeLinkOut = 0x00020000, + kBehaviorTypeEmote = 0x00040000, + kBehaviorTypePushWalk = 0x00080000, + //kBehaviorTypePushIdle = 0x00100000, + kBehaviorTypeNeedsRecalcMask = kBehaviorTypeAnyJump | kBehaviorTypeRunningImpact | kBehaviorTypeWalk | + kBehaviorTypeRun | kBehaviorTypeWalkBack | kBehaviorTypeTurnLeft | + kBehaviorTypeTurnRight | kBehaviorTypeSidestepLeft | + kBehaviorTypeSidestepRight | kBehaviorTypePushWalk, + }; + + plHBehavior(); + ~plHBehavior(); + + void Init(plAGAnim *anim, hsBool loop, plAvBrainHuman *brain, plArmatureMod *body, + float fadeIn, float fadeOut, UInt8 index, UInt32 type = 0); + virtual hsBool PreCondition(double time, float elapsed) { return true; } + UInt32 GetType() const { return fType; } + +protected: + virtual void IStart(); + virtual void IStop(); + + plArmatureMod *fAvMod; + plAvBrainHuman *fHuBrain; + float fFadeIn; // speed at which the animation fades in, in blend units per second + float fFadeOut; // speed at which the animation fades out, in blend units per second + float fMaxBlend; // the maximum blend the animation should reach + UInt32 fType; // (see the behaviorType enum) + + bool fStartMsgSent; // flags to prevent multiple start and stop messages from being sent + bool fStopMsgSent; +}; + +class Idle : public plHBehavior +{ + virtual void IStart(); +}; + +class Run : public plHBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class Walk : public plHBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class WalkBack : public plHBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class StepLeft : public plHBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class StepRight : public plHBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class StandingTurnLeft : public plHBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class StandingTurnRight : public plHBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class MovingTurn : public plHBehavior +{ +public: + +protected: + void IStart(); +}; + +class MovingTurnLeft : public MovingTurn +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class MovingTurnRight : public MovingTurn +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class Jump : public plHBehavior +{ +protected: + hsBool fReleased; + virtual void IStart(); + virtual void IStop(); + +public: + Jump() : plHBehavior(), fReleased(true) {} +}; + +class StandingJump : public Jump +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class WalkingJump : public Jump +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class RunningJump : public Jump +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +class GroundImpact : public plHBehavior +{ +public: + GroundImpact(); + virtual hsBool PreCondition(double time, float elapsed); + +private: + virtual void IStop(); + float fDuration; +}; + +class RunningImpact : public plHBehavior +{ +public: + RunningImpact(); + virtual hsBool PreCondition(double time, float elapsed); + +private: + virtual void IStop(); + float fDuration; +}; + +class Fall : public plHBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed); + virtual void Process(double time, float elapsed); +}; + +class Push : public plHBehavior +{ +public: + virtual void Process(double time, float elapsed); +}; + +//class PushIdle : public Push +//{ +//public: +// virtual hsBool PreCondition(double time, float elapsed); +//}; + +class PushWalk : public Push +{ +public: + virtual hsBool PreCondition(double time, float elapsed); +}; + +bool PushSimpleMultiStage(plArmatureMod *avatar, const char *enterAnim, const char *idleAnim, + const char *exitAnim, bool netPropagate, bool autoExit, plAGAnim::BodyUsage bodyUsage, + plAvBrainGeneric::BrainType type = plAvBrainGeneric::kGeneric); + +bool AvatarEmote(plArmatureMod *avatar, const char *emoteName); + + +#endif // PLAVBRAINHUMAN_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainPuppet.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainPuppet.cpp new file mode 100644 index 00000000..66b27320 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainPuppet.cpp @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Removed + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainPuppet.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainPuppet.h new file mode 100644 index 00000000..36684654 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainPuppet.h @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Removed \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp new file mode 100644 index 00000000..f6ee7660 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.cpp @@ -0,0 +1,176 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAvBrainRideAnimatedPhysical.h" +#include "plArmatureMod.h" + +#include "plAvBrainHuman.h" +#include "plAvBrain.h" +#include "plAvCallbackAction.h" +#include "../plMessage/plRideAnimatedPhysMsg.h" + + +void plAvBrainRideAnimatedPhysical::Activate(plArmatureModBase *avMod) +{ + plArmatureBrain::Activate(avMod); + IInitAnimations(); + if (!fCallbackAction) + { + plSceneObject* avObj = fArmature->GetTarget(0); + plAGModifier* agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); + plPhysicalControllerCore* controller = avMod->GetController(); + fCallbackAction = TRACKED_NEW plRidingAnimatedPhysicalController(avObj, agMod->GetApplicator(kAGPinTransform), controller); + fCallbackAction->ActivateController(); + } +} +plAvBrainRideAnimatedPhysical::~plAvBrainRideAnimatedPhysical() +{ + delete fCallbackAction; + fCallbackAction=nil; + +} + +void plAvBrainRideAnimatedPhysical::Deactivate() +{ + plArmatureBrain::Deactivate(); +} +hsBool plAvBrainRideAnimatedPhysical::MsgReceive(plMessage *msg) +{ + plRideAnimatedPhysMsg *ride = plRideAnimatedPhysMsg::ConvertNoRef(msg); + if(ride) + { + if(!ride->Entering()) + { + /*this->fArmature->PopBrain(); + delete this; + return true; + */ + fMode=kAbort; + } + } + return plArmatureBrain::MsgReceive(msg); +} +hsBool plAvBrainRideAnimatedPhysical::IInitAnimations() +{ + hsBool result = false; + + plAGAnim *idle = fAvMod->FindCustomAnim("Idle"); + plAGAnim *walk = fAvMod->FindCustomAnim("Walk"); + plAGAnim *run = fAvMod->FindCustomAnim("Run"); + plAGAnim *walkBack = fAvMod->FindCustomAnim("WalkBack"); + plAGAnim *stepLeft = fAvMod->FindCustomAnim("StepLeft"); + plAGAnim *stepRight = fAvMod->FindCustomAnim("StepRight"); + plAGAnim *standingLeft = fAvMod->FindCustomAnim("TurnLeft"); + plAGAnim *standingRight = fAvMod->FindCustomAnim("TurnRight"); + plAGAnim *fall = fAvMod->FindCustomAnim("Fall"); + plAGAnim *standJump = fAvMod->FindCustomAnim("StandingJump"); + plAGAnim *walkJump = fAvMod->FindCustomAnim("WalkingJump"); + plAGAnim *runJump = fAvMod->FindCustomAnim("RunningJump"); + plAGAnim *groundImpact = fAvMod->FindCustomAnim("GroundImpact"); + plAGAnim *runningImpact = fAvMod->FindCustomAnim("RunningImpact"); + plAGAnim *movingLeft = nil; // fAvMod->FindCustomAnim("LeanLeft"); + plAGAnim *movingRight = nil; // fAvMod->FindCustomAnim("LeanRight"); + plAGAnim *pushWalk = fAvMod->FindCustomAnim("BallPushWalk"); + + //plAGAnim *pushIdle = fAvMod->FindCustomAnim("BallPushIdle"); + + const float kDefaultFade = 3.0; // most animations fade in and out in 1/4 of a second. + + if (idle && walk && run && walkBack && standingLeft && standingRight && stepLeft && stepRight) + { + plHBehavior *behavior; + fBehaviors.SetCountAndZero(kHuBehaviorMax); + fBehaviors[kIdle] = behavior = TRACKED_NEW Idle; + behavior->Init(idle, true, this, fAvMod, kDefaultFade, kDefaultFade, kIdle, plHBehavior::kBehaviorTypeIdle); + behavior->SetStrength(1.f, 0.f); + + fBehaviors[kWalk] = behavior = TRACKED_NEW Walk; + behavior->Init(walk, true, this, fAvMod, kDefaultFade, 5.f, kWalk, plHBehavior::kBehaviorTypeWalk); + + fBehaviors[kRun] = behavior = TRACKED_NEW Run; + behavior->Init(run, true, this, fAvMod, kDefaultFade, 2.0, kRun, plHBehavior::kBehaviorTypeRun); + + fBehaviors[kWalkBack] = behavior = TRACKED_NEW WalkBack; + behavior->Init(walkBack, true, this, fAvMod, kDefaultFade, kDefaultFade, kWalkBack, plHBehavior::kBehaviorTypeWalkBack); + + fBehaviors[kStandingTurnLeft] = behavior = TRACKED_NEW StandingTurnLeft; + behavior->Init(standingLeft, true, this, fAvMod, 3.0f, 6.0f, kStandingTurnLeft, plHBehavior::kBehaviorTypeTurnLeft); + + fBehaviors[kStandingTurnRight] = behavior = TRACKED_NEW StandingTurnRight; + behavior->Init(standingRight, true, this, fAvMod, 3.0f, 6.0f, kStandingTurnRight, plHBehavior::kBehaviorTypeTurnRight); + + fBehaviors[kStepLeft] = behavior = TRACKED_NEW StepLeft; + behavior->Init(stepLeft, true, this, fAvMod, kDefaultFade, kDefaultFade, kStepLeft, plHBehavior::kBehaviorTypeSidestepLeft); + + fBehaviors[kStepRight] = behavior = TRACKED_NEW StepRight; + behavior->Init(stepRight, true, this, fAvMod, kDefaultFade, kDefaultFade, kStepRight, plHBehavior::kBehaviorTypeSidestepRight); + + // Warning: Changing the blend times of the jump animations will affect the path you take, because until we're fully blended, + // we won't be using the full motion defined in the animation. This isn't an issue for standing jump, but you need to be + // aware of it for the walk/run jumps. + fBehaviors[kFall] = behavior = TRACKED_NEW Fall; + behavior->Init(fall, true, this, fAvMod, 1.0f, 10, kFall, plHBehavior::kBehaviorTypeFall); + + fBehaviors[kStandingJump] = behavior = TRACKED_NEW StandingJump; + behavior->Init(standJump, false, this, fAvMod, kDefaultFade, kDefaultFade, kStandingJump, plHBehavior::kBehaviorTypeStandingJump); + + fBehaviors[kWalkingJump] = behavior = TRACKED_NEW WalkingJump; + behavior->Init(walkJump, false, this, fAvMod, 10, 3.0, kWalkingJump, plHBehavior::kBehaviorTypeWalkingJump); + + fBehaviors[kRunningJump] = behavior = TRACKED_NEW RunningJump; + behavior->Init(runJump, false, this, fAvMod, 10, 2.0, kRunningJump, plHBehavior::kBehaviorTypeRunningJump); + + fBehaviors[kGroundImpact] = behavior = TRACKED_NEW GroundImpact; + behavior->Init(groundImpact, false, this, fAvMod, 6.0f, kDefaultFade, kGroundImpact, plHBehavior::kBehaviorTypeGroundImpact); + + fBehaviors[kRunningImpact] = behavior = TRACKED_NEW RunningImpact; + behavior->Init(runningImpact, false, this, fAvMod, 6.0f, kDefaultFade, kRunningImpact, plHBehavior::kBehaviorTypeRunningImpact); + + fBehaviors[kMovingTurnLeft] = behavior = TRACKED_NEW MovingTurnLeft; + behavior->Init(movingLeft, true, this, fAvMod, kDefaultFade, kDefaultFade, kMovingTurnLeft, plHBehavior::kBehaviorTypeMovingTurnLeft); + + fBehaviors[kMovingTurnRight] = behavior = TRACKED_NEW MovingTurnRight; + behavior->Init(movingRight, true, this, fAvMod, kDefaultFade, kDefaultFade, kMovingTurnRight, plHBehavior::kBehaviorTypeMovingTurnRight); + + fBehaviors[kPushWalk] = behavior = TRACKED_NEW PushWalk; + behavior->Init(pushWalk, true, this, fAvMod, kDefaultFade, kDefaultFade, kPushWalk, plHBehavior::kBehaviorTypePushWalk); + + //fBehaviors[kPushIdle] = behavior = TRACKED_NEW PushIdle; + //behavior->Init(pushIdle, true, this, fAvMod, kDefaultFade, kDefaultFade, kPushIdle, plHBehavior::kBehaviorTypePushIdle); + + result = true; + } + return result; +} +hsBool plAvBrainRideAnimatedPhysical::LeaveAge() +{ + return plArmatureBrain::LeaveAge(); +} +hsBool plAvBrainRideAnimatedPhysical::Apply(double timeNow, hsScalar elapsed) +{ + if(this->fMode==kAbort) return false; + else return plAvBrainHuman::Apply(timeNow, elapsed); + +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h new file mode 100644 index 00000000..d64948a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainRideAnimatedPhysical.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAvBrainHuman.h" + +class plRidingAnimatedPhysicalController; + +class plAvBrainRideAnimatedPhysical : public plAvBrainHuman +{ +public: + enum mode { + kWalking, + kAbort + }; + CLASSNAME_REGISTER( plAvBrainRideAnimatedPhysical ); + GETINTERFACE_ANY( plAvBrainRideAnimatedPhysical, plArmatureBrain ); + plAvBrainRideAnimatedPhysical() : plAvBrainHuman(false),fMode(kWalking){}; + ~plAvBrainRideAnimatedPhysical(); + virtual void Activate(plArmatureModBase *avMod); + virtual void Deactivate(); + virtual hsBool MsgReceive(plMessage *msg); + virtual hsBool LeaveAge(); + virtual hsBool Apply(double timeNow, hsScalar elapsed); +protected: + hsBool IInitAnimations(); + mode fMode; +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainStaticNPC.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainStaticNPC.cpp new file mode 100644 index 00000000..41caea93 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainStaticNPC.cpp @@ -0,0 +1,131 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +//class plAvBrainStaticNPC : public plAvBrain +//{ +// +//}; +// +///** \class plAvBrain +// Virtual base class for the modular avatar & creature brains. +// A brain is a modular object which can be installed into a plArmatureMod +// to drive it around in the scene. Some brains are for creature ai; +// others intepret user input. The most interesting brain for reference is +// probably plAvBrainHuman, which implements the control system for the +// user's avatar. +// /sa plAvBrainHuman, plArmatureMod +//*/ +//class plAvBrainStaticNPC : public plAvBrainStaticNPC +//{ +//public: +// /** Default constructor - constructs the brain but does not attach it. +// Brains are always constructed before being Pushed onto the armature +// they seek to control. */ +// plAvBrain(); +// +// /** Destructor. Automatically called on a brain when it is popped from the armature. */ +// virtual ~plAvBrain(); +// +// // BRAIN PROTOCOL +// /** This brain has just been freshly pushed onto an armature and is now +// in primary control. Note that brains beneath it on the stack may +// still have effect on the armature; any messages which the top +// brain doesn't handle will propagate down to the next brain in line. +// */ +// virtual hsBool Activate(plArmatureMod *avMod); +// +// /** Has the brain resolved all its load-time dependencies? This is a mechanism +// to allow newly loading creatures to reach a known state before they +// are asked to load secondary state or to interact with the environment. +// */ +// virtual hsBool IsReady(); +// +// /** This brain has just been removed from its armature and is about to be destructed. */ +// virtual hsBool Deactivate(); +// +// /** This is the top brain and it's time for it to evaluate. Called during eval +// time for the armature modifier. Only the topmost brain gets an apply +// call; others must do any necessary background work during MsgReceive. */ +// virtual hsBool Apply(double timeNow, hsScalar elapsed); +// +// /** Another brain has been pushed atop this brain. Drop into the background. +// We'll still receive any messages that the upper brain doesn't eat. */ +// virtual hsBool Suspend(); +// +// /** We were suspended, but now we're on top of the brain stack again. */ +// virtual hsBool Resume(); +// +// // \name Spawning \{ +// /** Do any necessary custom action upon spawning afresh in a new age. +// For the human brain, this simply consists of noting that we have +// spawned and we can stop trying to do so. */ +// virtual void Spawn(double timeNow) {}; +// +// /** Custom behavior for entering an age. Binding the camera, audio source, etc. */ +// virtual void EnterAge(hsBool reSpawn) {}; +// +// /** Custom behavior for leaving an age. Free any attachments to camera, audio, etc. */ +// virtual void LeaveAge() {}; +// +// // TASKS +// // tasks are operations that must be done in sequence. +// /** Push a new task onto the end of our FIFO task queue. Will not +// run until all the tasks ahead of it have run. +// \sa plAvTask +// */ +// virtual void QueueTask(plAvTask *task); +// +// +// virtual void DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); +// +// +// // PLASMOTICS +// CLASSNAME_REGISTER( plAvBrain ); +// GETINTERFACE_ANY( plAvBrain, plCreatable ); +// +// virtual void Write(hsStream *stream, hsResMgr *mgr); +// virtual void Read(hsStream *stream, hsResMgr *mgr); +// +// virtual void SaveAux(hsStream *stream, hsResMgr *mgr); +// virtual void LoadAux(hsStream *stream, hsResMgr *mgr, double time); +// +// virtual hsBool MsgReceive(plMessage *msg); +// +// +//protected: +// plArmatureMod *fAvMod; // the avatar we're controlling +// +// // TASKS +// // -- variables +// typedef std::deque plAvTaskQueue; +// plAvTaskQueue fTaskQueue; // FIFO queue of tasks we're working on +// plAvTask *fCurTask; // the task we're working on right now +// // -- methods +// virtual hsBool IHandleTaskMsg(plAvTaskMsg *msg); // respond to a task scheduling message +// void IProcessTasks(double time, hsScalar elapsed); // process current task and start new one if necessary +// +// hsBool fSuspended; +//}; +// diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp new file mode 100644 index 00000000..d31287e1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.cpp @@ -0,0 +1,682 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "plAntiGravAction.h" // descends from Havok class, so must be included first, like havok objects + +// singular +#include "plAvBrainSwim.h" + +// local +#include "plArmatureMod.h" +#include "plAvBehaviors.h" +#include "plAvBrainHuman.h" +#include "plAGAnim.h" +#include "plAvBrainDrive.h" +#include "plMatrixChannel.h" +#include "plSwimRegion.h" +#include "plAvatarTasks.h" +#include "plArmatureEffects.h" +#include "plAvTaskBrain.h" +// global +#include "hsQuat.h" +#include "hsTimer.h" +#include "plPhysical.h" +#include "plPhysicalControllerCore.h" +#include "plAvCallbackAction.h" +// other +#include "../plPhysical/plCollisionDetector.h" +#include "../plPipeline/plDebugText.h" + +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plSwimMsg.h" +#include "../plMessage/plLOSRequestMsg.h" +#include "../plMessage/plLOSHitMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plSimStateMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pfMessage/plArmatureEffectMsg.h" + +class plSwimBehavior : public plArmatureBehavior +{ + friend class plAvBrainSwim; + +public: + plSwimBehavior() : fAvMod(nil), fSwimBrain(nil) {} + virtual ~plSwimBehavior() {} + + void Init(plAGAnim *anim, hsBool loop, plAvBrainSwim *brain, plArmatureMod *body, UInt8 index) + { + plArmatureBehavior::Init(anim, loop, brain, body, index); + fAvMod = body; + fSwimBrain = brain; + } + + virtual hsBool PreCondition(double time, float elapsed) { return true; } + +protected: + virtual void IStart() + { + plArmatureBehavior::IStart(); + fAvMod->SynchIfLocal(hsTimer::GetSysSeconds(), false); + } + + virtual void IStop() + { + plArmatureBehavior::IStop(); + fAvMod->SynchIfLocal(hsTimer::GetSysSeconds(), false); + } + + plArmatureMod *fAvMod; + plAvBrainSwim *fSwimBrain; +}; + +class SwimForward: public plSwimBehavior +{ +public: + /** Walk key is down, fast key is not down */ + virtual hsBool PreCondition(double time, float elapsed) + { + return (fAvMod->ForwardKeyDown() && !fAvMod->FastKeyDown()); + } +}; + +class SwimForwardFast: public plSwimBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed) + { + return (fAvMod->ForwardKeyDown() && fAvMod->FastKeyDown()); + } +}; + +class SwimBack : public plSwimBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed) + { + return (fAvMod->BackwardKeyDown()); + } +}; + +class TreadWater: public plSwimBehavior +{ +}; + +class SwimLeft : public plSwimBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed) + { + return ((fAvMod->StrafeLeftKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnLeftKeyDown())) && + !(fAvMod->StrafeRightKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnRightKeyDown())) && + !(fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown())); + } +}; + +class SwimRight : public plSwimBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed) + { + return ((fAvMod->StrafeRightKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnRightKeyDown())) && + !(fAvMod->StrafeLeftKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnLeftKeyDown())) && + !(fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown())); + } +}; + +class SwimTurn: public plSwimBehavior +{ +public: + virtual void Process(double time, float elapsed) + { + static const float maxTurnSpeed = 1.0f; // radians per second; + static const float timeToMaxTurn = 0.5f; + static const float incPerSec = maxTurnSpeed / timeToMaxTurn; + +// hsAssert(0, "fixme physx"); + float oldSpeed = fabs(fSwimBrain->fCallbackAction->GetTurnStrength()); + float thisInc = elapsed * incPerSec; + float newSpeed = __min(oldSpeed + thisInc, maxTurnSpeed); + fSwimBrain->fCallbackAction->SetTurnStrength(newSpeed * fAvMod->GetKeyTurnStrength() + fAvMod->GetAnalogTurnStrength()); + // the turn is actually applied during PhysicsUpdate + } + virtual void IStop() + { +// hsAssert(0, "fixme physx"); + if (fSwimBrain->fCallbackAction) + fSwimBrain->fCallbackAction->SetTurnStrength(0.0f); + plSwimBehavior::IStop(); + } +}; + +class SwimTurnLeft : public SwimTurn +{ +public: + virtual hsBool PreCondition(double time, float elapsed) + { + return (fAvMod->GetTurnStrength() > 0 && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown())); + } +}; + +class SwimTurnRight : public SwimTurn +{ +public: + virtual hsBool PreCondition(double time, float elapsed) + { + return (fAvMod->GetTurnStrength() < 0 && (fAvMod->ForwardKeyDown() || fAvMod->BackwardKeyDown())); + } +}; + +class TreadTurnLeft : public plSwimBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed) + { + return (fAvMod->TurnLeftKeyDown() && !fAvMod->ForwardKeyDown() && !fAvMod->BackwardKeyDown()); + } +}; + +class TreadTurnRight : public plSwimBehavior +{ +public: + virtual hsBool PreCondition(double time, float elapsed) + { + return (fAvMod->TurnRightKeyDown() && !fAvMod->ForwardKeyDown() && !fAvMod->BackwardKeyDown()); + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////// + +const hsScalar plAvBrainSwim::kMinSwimDepth = 4.0f; + +plAvBrainSwim::plAvBrainSwim() : + fCallbackAction(nil), + fMode(kWalking), + fSurfaceDistance(0.f) +{ + fSurfaceProbeMsg = TRACKED_NEW plLOSRequestMsg(); + fSurfaceProbeMsg->SetReportType(plLOSRequestMsg::kReportHitOrMiss); + fSurfaceProbeMsg->SetRequestType(plSimDefs::kLOSDBSwimRegion); + fSurfaceProbeMsg->SetTestType(plLOSRequestMsg::kTestAny); + fSurfaceProbeMsg->SetRequestID(plArmatureMod::kAvatarLOSSwimSurface); +} + +plAvBrainSwim::~plAvBrainSwim() +{ + if(fCallbackAction) + { + IDetachAction(); + delete fCallbackAction; + fCallbackAction=nil; + } + fSurfaceProbeMsg->UnRef(); + + int i; + for (i = 0; i < fBehaviors.GetCount(); i++) + delete fBehaviors[i]; +} + +hsBool plAvBrainSwim::Apply(double time, hsScalar elapsed) +{ + IProbeSurface(); + if (fMode == kWalking) + { + if (fSurfaceDistance >= 0.f) + { + fMode = kWading; + + plAvBrainHuman *huBrain = plAvBrainHuman::ConvertNoRef(fAvMod->GetNextBrain(this)); +// hsAssert(0, "fixme physx"); + if (huBrain && !huBrain->fCallbackAction->IsOnGround()) + { + // We're jumping in! Trigger splash effect (sound) + plArmatureEffectMsg *msg = TRACKED_NEW plArmatureEffectMsg(fAvMod->GetArmatureEffects()->GetKey(), kTime); + msg->fEventTime = (hsScalar)time; + msg->fTriggerIdx = plArmatureMod::kImpact; + + plEventCallbackInterceptMsg *iMsg = TRACKED_NEW plEventCallbackInterceptMsg; + iMsg->AddReceiver(fAvMod->GetArmatureEffects()->GetKey()); + iMsg->fEventTime = (hsScalar)time; + iMsg->fEvent = kTime; + iMsg->SetMessage(msg); + iMsg->Send(); + } + } + } + + plArmatureBrain *nextBrain = fAvMod->GetNextBrain(this); + if (fMode == kWading) + { + if (fSurfaceDistance > kMinSwimDepth && fSurfaceDistance < 100.0f) + IStartSwimming(true); + else if (fSurfaceDistance < 0.f) + fMode = kWalking; + } + + int i; + if (fMode == kWalking || fMode == kWading || nextBrain->IsRunningTask()) + { + nextBrain->Apply(time, elapsed); // Let brain below process for us + + for (i = 0; i < kSwimBehaviorMax; i++) + fBehaviors[i]->SetStrength(0.f, 2.f); + } + else if (fMode == kAbort) + return false; + else + { + if (fMode == kSwimming2D) + { + IProcessSwimming2D(time, elapsed); + + // The contact check is so that if buoyancy bobs us a little too high, we don't + // switch to wading only to fall again. +// hsAssert(0, "fixme physx"); + if (fSurfaceDistance < kMinSwimDepth-.5 && fCallbackAction->HadContacts()) + IStartWading(); + } + else if (fMode == kSwimming3D) + IProcessSwimming3D(time, elapsed); + } + return plArmatureBrain::Apply(time, elapsed); +} + +hsBool plAvBrainSwim::MsgReceive(plMessage *msg) +{ + plLOSHitMsg *losHit = plLOSHitMsg::ConvertNoRef(msg); + if (losHit) + { + if (losHit->fRequestID == plArmatureMod::kAvatarLOSSwimSurface) + { + plSwimRegionInterface *region = nil; + if (!losHit->fNoHit) + { + plSceneObject *hitObj = plSceneObject::ConvertNoRef(losHit->fObj->ObjectIsLoaded()); + region = hitObj ? plSwimRegionInterface::ConvertNoRef(hitObj->GetGenericInterface(plSwimRegionInterface::Index())) : nil; + //100-fDistance because of casting the ray from above to get around physxs Raycast requirments + fSurfaceDistance = 100.f-losHit->fDistance; + } + else + fSurfaceDistance = -100.f; + +// hsAssert(0, "fixme physx"); + if (fCallbackAction) + { + if (region) + fCallbackAction->SetSurface(region, fArmature->GetTarget(0)->GetLocalToWorld().GetTranslate().fZ + fSurfaceDistance); + else + fCallbackAction->SetSurface(nil, 0.f); + } + return true; + } + } + + plSwimMsg *swimMsg = plSwimMsg::ConvertNoRef(msg); + if (swimMsg) + { + if (swimMsg->GetIsLeaving()) + fMode = kAbort; + + return true; + } + + plControlEventMsg *ctrlMsg = plControlEventMsg::ConvertNoRef(msg); + if (ctrlMsg) + return IHandleControlMsg(ctrlMsg); + + if (fMode == kWalking || fMode == kWading) + return fAvMod->GetNextBrain(this)->MsgReceive(msg); + + if (plAvSeekMsg *seekM = plAvSeekMsg::ConvertNoRef(msg)) + { + + // seek and subclasses always have a seek first + if (!seekM->fNoSeek) + { + // use dumb seek + plAvSeekTask *seek = TRACKED_NEW plAvSeekTask(seekM->fSeekPoint, seekM->fAlignType, seekM->fAnimName); + QueueTask(seek); + } + // else don't seek at all. + + plAvOneShotMsg * oneshotM = plAvOneShotMsg::ConvertNoRef(msg); + if(oneshotM) + { + // if it's a oneshot, add the oneshot task as well + plAvOneShotTask *oneshot = TRACKED_NEW plAvOneShotTask(oneshotM, fAvMod, this); + QueueTask(oneshot); + } + return true; + } + + if (plArmatureBrain::MsgReceive(msg)) + return true; + + if (fMode == kWading) // Things like LOS need to go to the human brain below us. + return fAvMod->GetNextBrain(this)->MsgReceive(msg); + + return false; +} + +void plAvBrainSwim::Activate(plArmatureModBase* avMod) +{ + plArmatureBrain::Activate(avMod); + + IInitAnimations(); + fSurfaceProbeMsg->SetSender(fAvMod->GetKey()); + + // turn our underlying brain back on until we're all the way in the water. + fAvMod->GetNextBrain(this)->Resume(); +} + +void plAvBrainSwim::Deactivate() +{ + plArmatureBrain::Deactivate(); + + IDetachAction(); +} + +void plAvBrainSwim::Suspend() +{ + if (fMode == kSwimming2D) + IDetachAction(); +} + +void plAvBrainSwim::Resume() +{ + if (fMode == kSwimming2D) + IAttachAction(); +} + +bool plAvBrainSwim::IsWalking() +{ + return fMode == kWalking; +} + +bool plAvBrainSwim::IsWading() +{ + return fMode == kWading; +} + +bool plAvBrainSwim::IsSwimming() +{ + return (fMode == kSwimming2D || fMode == kSwimming3D); +} + +void plAvBrainSwim::IStartWading() +{ + plArmatureBrain *nextBrain = fAvMod->GetNextBrain(this); + nextBrain->Resume(); + fMode = kWading; + + int i; + for (i = 0; i < fBehaviors.GetCount(); i++) + fBehaviors[i]->SetStrength(0.f, 2.f); + + IDetachAction(); + + if (fAvMod->IsLocalAvatar()) + { + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, false); + pMsg->SetCmd(plCameraMsg::kResponderUndoThirdPerson); + pMsg->Send(); + } +} + +void plAvBrainSwim::IStartSwimming(bool is2D) +{ + // if we *were* wading, the next brain will be running as well. turn it off + // if we weren't wading, there's no harm in suspending it redundantly. + plArmatureBrain *nextBrain = fAvMod->GetNextBrain(this); + nextBrain->Suspend(); + + IAttachAction(); + if (is2D) + fMode = kSwimming2D; + else + fMode = kSwimming3D; + + if (fAvMod->IsLocalAvatar()) + { + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, false); + pMsg->SetCmd(plCameraMsg::kResponderSetThirdPerson); + pMsg->Send(); + } +} + +hsBool plAvBrainSwim::IProcessSwimming2D(double time, float elapsed) +{ + int i; + for (i = 0; i < fBehaviors.GetCount(); i++) + { + plSwimBehavior *behavior = (plSwimBehavior*)fBehaviors[i]; + if (behavior->PreCondition(time, elapsed) && !IsRunningTask()) + { + behavior->SetStrength(1.f, 2.f); + behavior->Process(time, elapsed); + } + else + behavior->SetStrength(0.f, 2.f); + } +// hsAssert(0, "fixme physx"); + fCallbackAction->RecalcVelocity(time, time - elapsed); + + return true; +} + +hsBool plAvBrainSwim::IProcessSwimming3D(double time, float elapsed) +{ + fAvMod->ApplyAnimations(time, elapsed); + return true; +} + +hsBool plAvBrainSwim::IInitAnimations() +{ + plAGAnim *treadWater = fAvMod->FindCustomAnim("SwimIdle"); + plAGAnim *swimForward = fAvMod->FindCustomAnim("SwimSlow"); + plAGAnim *swimForwardFast = fAvMod->FindCustomAnim("SwimFast"); + plAGAnim *swimBack = fAvMod->FindCustomAnim("SwimBackward"); + plAGAnim *swimLeft = fAvMod->FindCustomAnim("SideSwimLeft"); + plAGAnim *swimRight = fAvMod->FindCustomAnim("SideSwimRight"); + plAGAnim *treadWaterLeft = fAvMod->FindCustomAnim("TreadWaterTurnLeft"); + plAGAnim *treadWaterRight = fAvMod->FindCustomAnim("TreadWaterTurnRight"); + + static const float defaultFade = 2.0f; + fBehaviors.SetCountAndZero(kSwimBehaviorMax); + plSwimBehavior *behavior; + fBehaviors[kTreadWater] = behavior = TRACKED_NEW TreadWater; + behavior->Init(treadWater, true, this, fAvMod, kTreadWater); + + fBehaviors[kSwimForward] = behavior = TRACKED_NEW SwimForward; + behavior->Init(swimForward, true, this, fAvMod, kSwimForward); + + fBehaviors[kSwimForwardFast] = behavior = TRACKED_NEW SwimForwardFast; + behavior->Init(swimForwardFast, true, this, fAvMod, kSwimForwardFast); + + fBehaviors[kSwimBack] = behavior = TRACKED_NEW SwimBack; + behavior->Init(swimBack, true, this, fAvMod, kSwimBack); + + fBehaviors[kSwimLeft] = behavior = TRACKED_NEW SwimLeft; + behavior->Init(swimLeft, true, this, fAvMod, kSwimLeft); + + fBehaviors[kSwimRight] = behavior = TRACKED_NEW SwimRight; + behavior->Init(swimRight, true, this, fAvMod, kSwimRight); + + fBehaviors[kSwimTurnLeft] = behavior = TRACKED_NEW SwimTurnLeft; + behavior->Init(nil, true, this, fAvMod, kSwimTurnLeft); + + fBehaviors[kSwimTurnRight] = behavior = TRACKED_NEW SwimTurnRight; + behavior->Init(nil, true, this, fAvMod, kSwimTurnRight); + + fBehaviors[kTreadTurnLeft] = behavior = TRACKED_NEW TreadTurnLeft; + behavior->Init(treadWaterLeft, true, this, fAvMod, kTreadTurnLeft); + + fBehaviors[kTreadTurnRight] = behavior = TRACKED_NEW TreadTurnRight; + behavior->Init(treadWaterRight, true, this, fAvMod, kTreadTurnRight); + + return true; +} + +bool plAvBrainSwim::IAttachAction() +{ + bool result = false; + if(fAvMod) + { +// hsAssert(0, "fixme physx"); + plPhysicalControllerCore *physical = fAvMod->GetController(); + + if (physical) + { + if (!fCallbackAction) + { + plSceneObject * avObj = fArmature->GetTarget(0); + plAGModifier *agMod = const_cast(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index()))); + fCallbackAction = new plSwimmingController(avObj, agMod->GetApplicator(kAGPinTransform),physical); +// physical->AttachAction(fCallbackAction, true, false); + result = true; + } + else + { + fCallbackAction->ActivateController(); + } + + } + } + return result; +} + +bool plAvBrainSwim::IDetachAction() +{ + bool result = false; + + if (fCallbackAction) + { +// hsAssert(0, "fixme physx"); +// plPhysical *physical = fAvMod->GetPhysical(); +// +// if(physical) +// { +// physical->RemoveAction(fCallbackAction); +// result = true; // there was an action and we removed it +// } + + // TODO: We get a compiler warning about deleting a pointer to an + // undefined class. So, who knows what the code is actually doing. + // Seems bad. Just putting a note in here for whoever fixes the + // physx issue. + //delete fCallbackAction; + //fCallbackAction = nil; + } + return result; +} + +void plAvBrainSwim::IProbeSurface() +{ + hsPoint3 ourPos = fAvMod->GetTarget(0)->GetLocalToWorld().GetTranslate(); + hsPoint3 up = ourPos; + up.fZ += 100; + fSurfaceProbeMsg->SetFrom(up); + fSurfaceProbeMsg->SetTo(ourPos); + fSurfaceProbeMsg->SendAndKeep(); +} + +hsBool plAvBrainSwim::IHandleControlMsg(plControlEventMsg* msg) +{ + ControlEventCode moveCode = msg->GetControlCode(); + if (msg->ControlActivated()) + { + switch (moveCode) + { + case B_CONTROL_TOGGLE_PHYSICAL: + { +#ifndef PLASMA_EXTERNAL_RELEASE // external clients can't go non-physical + plAvBrainDrive *driver = TRACKED_NEW plAvBrainDrive(20, 1); + fAvMod->PushBrain(driver); +#endif + return true; + } + break; + } + } + return false; +} + + +void plAvBrainSwim::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + sprintf(strBuf, "Brain type: Swim"); + debugTxt.DrawString(x, y, strBuf, 0, 255, 255); + y += lineHeight; + + switch(fMode) { + case kWading: + sprintf(strBuf, "Mode: Wading"); + break; + case kSwimming2D: + sprintf(strBuf, "Mode: Swimming2D"); + break; + case kSwimming3D: + sprintf(strBuf, "Mode: Swimming3D"); + break; + case kAbort: + sprintf(strBuf, "Mode: Abort (you should never see this)"); + break; + } + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + + +// hsAssert(0, "fixme physx"); +// float buoy = fCallbackAction? fCallbackAction->GetBuoyancy() : 0.0f; +// sprintf(strBuf, "Distance to surface: %f Buoyancy: %f", fSurfaceDistance, buoy); +// debugTxt.DrawString(x, y, strBuf); +// y += lineHeight; +// +// hsVector3 linV; +// fAvMod->GetPhysical()->GetLinearVelocitySim(linV); +// hsVector3 angV; +// fAvMod->GetPhysical()->GetAngularVelocitySim(angV); +// hsScalar angle = angV.fZ > 0 ? angV.Magnitude() : -angV.Magnitude(); +// sprintf(strBuf, "Velocity: Linear (%5.2f, %5.2f, %5.2f), Angular %5.2f", linV.fX, linV.fY, linV.fZ, angle); +// debugTxt.DrawString(x, y, strBuf); +// y += lineHeight; + + int i; + for (i = 0; i < fBehaviors.GetCount(); i++) + fBehaviors[i]->DumpDebug(x, y, lineHeight, strBuf, debugTxt); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h new file mode 100644 index 00000000..cfcb4d75 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainSwim.h @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_AV_BRAIN_SWIM_H +#define PL_AV_BRAIN_SWIM_H + +#include "plAvBrain.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" + +class plArmatureMod; +class plAntiGravAction; +class plControlEventMsg; +class plLOSRequestMsg; +class plSwimRegionInterface; +class plSwimmingController; +class plAvBrainSwim : public plArmatureBrain +{ +public: + plAvBrainSwim(); + virtual ~plAvBrainSwim(); + + CLASSNAME_REGISTER( plAvBrainSwim ); + GETINTERFACE_ANY( plAvBrainSwim, plArmatureBrain ); + + virtual void Activate(plArmatureModBase *avMod); + hsBool Apply(double time, hsScalar elapsed); + virtual void Deactivate(); + virtual void Suspend(); + virtual void Resume(); + virtual void DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt); + hsBool MsgReceive(plMessage *msg); + bool IsWalking(); + bool IsWading(); + bool IsSwimming(); + hsScalar GetSurfaceDistance() { return fSurfaceDistance; } + + plSwimmingController *fCallbackAction; + static const hsScalar kMinSwimDepth; + +protected: + void IStartWading(); + void IStartSwimming(bool is2D); + hsBool IProcessSwimming2D(double time, float elapsed); + hsBool IProcessSwimming3D(double time, float elapsed); + hsBool IProcessWading(double time, float elapsed); + hsBool IProcessClimbingOut(double time, float elapsed); + hsBool IProcessBehaviors(double time, float elapsed); + + virtual hsBool IInitAnimations(); + bool IAttachAction(); + bool IDetachAction(); + void IProbeSurface(); + hsBool IHandleControlMsg(plControlEventMsg* msg); + float IGetTargetZ(); + + float fSurfaceDistance; + plLOSRequestMsg *fSurfaceProbeMsg; + plSwimRegionInterface *fCurrentRegion; + + enum Mode { + kWading, + kSwimming2D, + kSwimming3D, + kClimbingOut, + kAbort, + kWalking, + } fMode; + + enum + { + kTreadWater, + kSwimForward, + kSwimForwardFast, + kSwimBack, + kSwimLeft, + kSwimRight, + kSwimTurnLeft, + kSwimTurnRight, + kTreadTurnLeft, + kTreadTurnRight, + kSwimBehaviorMax, + }; +}; + +#endif PL_AV_BRAIN_SWIM_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainUser.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainUser.cpp new file mode 100644 index 00000000..04cab7d8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainUser.cpp @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Obsolete \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainUser.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainUser.h new file mode 100644 index 00000000..04cab7d8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainUser.h @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Obsolete \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.cpp new file mode 100644 index 00000000..655f4e02 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.cpp @@ -0,0 +1,559 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAvCallbackAction.h" +#include "../plMessage/plLOSHitMsg.h" + +#include "plArmatureMod.h" // for LOS enum type +#include "plMatrixChannel.h" +#include "hsTimer.h" +#include "plPhysicalControllerCore.h" + +// Generic geom utils. +hsBool LinearVelocity(hsVector3 &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat); +void AngularVelocity(hsScalar &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat); +float AngleRad2d (float x1, float y1, float x3, float y3); +inline hsVector3 GetYAxis(hsMatrix44 &mat) +{ + return hsVector3(mat.fMap[1][0], mat.fMap[1][1], mat.fMap[1][2]); +} + +plAnimatedController::plAnimatedController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) + : fRootObject(rootObject) + , fRootApp(rootApp) + , fController(controller) + , fTurnStr(0.f) + , fAnimAngVel(0.f) + , fAnimPosVel(0.f, 0.f, 0.f) +{ +} + +void plAnimatedController::RecalcVelocity(double timeNow, double timePrev, hsBool useAnim /* = true */) +{ + if (useAnim) + { + // while you may think it would be correct to cache this, + // what we're actually asking is "what would the animation's + // position be at the previous time given its *current* + // parameters (particularly blends)" + hsMatrix44 prevMat = ((plMatrixChannel *)fRootApp->GetChannel())->Value(timePrev, true); + hsMatrix44 curMat = ((plMatrixChannel *)fRootApp->GetChannel())->Value(timeNow, true); + + // If we get a valid linear velocity (ie, we didn't wrap around in the anim), + // use it. Otherwise just reuse the previous frames velocity. + hsVector3 linearVel; + if (LinearVelocity(linearVel, (float)(timeNow - timePrev), prevMat, curMat)) + fAnimPosVel = linearVel; + + // Automatically sets fAnimAngVel + AngularVelocity(fAnimAngVel, (float)(timeNow - timePrev), prevMat, curMat); + } + else + { + fAnimPosVel.Set(0.f, 0.f, 0.f); + fAnimAngVel = 0.f; + } + + if (fController) + fController->SetVelocities(fAnimPosVel, fAnimAngVel + fTurnStr); +} + +/////////////////////////////////////////////////////////////////////////// + +const hsScalar plWalkingController::kControlledFlightThreshold = 1.f; // seconds + +plWalkingController::plWalkingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) + : plAnimatedController(rootObject, rootApp, controller) + , fHitGroundInThisAge(false) + , fWaitingForGround(false) + , fControlledFlightTime(0) + , fControlledFlight(0) + , fImpactTime(0.f) + , fImpactVelocity(0.f, 0.f, 0.f) + , fClearImpact(false) + , fGroundLastFrame(false) +{ + if (fController) + { + fWalkingStrategy= TRACKED_NEW plWalkingStrategy(fController); + fController->SetMovementSimulationInterface(fWalkingStrategy); + } + else + fWalkingStrategy = nil; +} + +void plWalkingController::RecalcVelocity(double timeNow, double timePrev, hsBool useAnim) +{ + if (!fHitGroundInThisAge && fController && fController->IsEnabled() && fWalkingStrategy->IsOnGround()) + fHitGroundInThisAge = true; // if we're not pinned and we're not in an age yet, we are now. + + if (fClearImpact) + { + fImpactTime = 0.f; + fImpactVelocity.Set(0.f, 0.f, 0.f); + } + + if (fController && !fWalkingStrategy->IsOnGround()) + { + fImpactTime = fWalkingStrategy->GetAirTime(); + fImpactVelocity = fController->GetLinearVelocity(); + fClearImpact = false; + } + else + fClearImpact = true; + + if (IsControlledFlight()) + { + if (fWalkingStrategy && fWalkingStrategy->IsOnGround()) + fControlledFlightTime = fWalkingStrategy->GetAirTime(); + if(fGroundLastFrame&&(fWalkingStrategy && !fWalkingStrategy->IsOnGround())) + { + //we have started to leave the ground tell the movement strategy in case it cares + fWalkingStrategy->StartJump(); + } + if (fControlledFlightTime > kControlledFlightThreshold) + EnableControlledFlight(false); + } + if (fWalkingStrategy) + fGroundLastFrame = fWalkingStrategy->IsOnGround(); + else + fGroundLastFrame=false; + plAnimatedController::RecalcVelocity(timeNow, timePrev, useAnim); +} + +void plWalkingController::Reset(bool newAge) +{ + + ActivateController(); + if (newAge) + { + if (fWalkingStrategy) + fWalkingStrategy->ResetAirTime(); + fHitGroundInThisAge = false; + } +} + void plWalkingController::ActivateController() +{ + if (fWalkingStrategy) + { + fWalkingStrategy->RefreshConnectionToControllerCore(); + } + else + { + fWalkingStrategy= TRACKED_NEW plWalkingStrategy(fController); + fWalkingStrategy->RefreshConnectionToControllerCore(); + + } +} + +bool plWalkingController::EnableControlledFlight(bool status) +{ + if (status) + { + if (fControlledFlight == 0) + fControlledFlightTime = 0.f; + + ++fControlledFlight; + fWaitingForGround = true; + } + else + fControlledFlight = __max(--fControlledFlight, 0); + + return status; +} +plWalkingController::~plWalkingController() +{ + delete fWalkingStrategy; + if (fController) + fController->SetMovementSimulationInterface(nil); +} +#if 0 +void plWalkingController::Update() +{ +// double elapsed = time.asDouble() - getRefresh().asDouble(); +// setRefresh(time); +// +// hsBool isPhysical = !fPhysical->GetProperty(plSimulationInterface::kPinned); +// const Havok::Vector3 straightUp(0.0f, 0.0f, 1.0f); +// hsBool alreadyInAge = fHitGroundInThisAge; +// +// int numContacts = fPhysical->GetNumContacts(); +// bool ground = false; +// fPushingPhysical = nil; +// int i, j; + +/* for(i = 0; i < numContacts; i++) + { + plHKPhysical *contactPhys = fPhysical->GetContactPhysical(i); + if (!contactPhys) + continue; // Physical no longer exists. Skip it. + + const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); + hsScalar dotUp = straightUp.dot(contact->m_normal); + if (dotUp > .5) + ground = true; + else if (contactPhys->GetProperty(plSimulationInterface::kAvAnimPushable)) + { + hsPoint3 position; + hsQuat rotation; + fPhysical->GetPositionAndRotationSim(&position, &rotation); + + hsQuat inverseRotation = rotation.Inverse(); + hsVector3 normal(contact->m_normal.x, contact->m_normal.y, contact->m_normal.z); + fFacingPushingPhysical = (inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false); + + fPushingPhysical = contactPhys; + } + } + + // We need to check for the case where the avatar hasn't collided with "ground", but is colliding + // with a few other objects so that he's not actually falling (wedged in between some slopes). + // We do this by answering the following question (in 2d top-down space): "If you sort the contact + // normals by angle, is there a large enough gap between normals?" + // + // If you think in terms of geometry, this means a collection of surfaces are all pushing on you. + // If they're pushing from all sides, you have nowhere to go, and you won't fall. There needs to be + // a gap, so that you're pushed out and have somewhere to fall. This is the same as finding a gap + // larger than 180 degrees between sorted normals. + // + // The problem is that on top of that, the avatar needs enough force to shove him out that gap (he + // has to overcome friction). I deal with that by making the threshold (360 - (180 - 60) = 240). I've + // seen up to 220 reached in actual gameplay in a situation where we'd want this to take effect. + // This is the same running into 2 walls where the angle between them is 60. + const hsScalar threshold = hsScalarDegToRad(240); + if (!ground && numContacts >= 2) + { + // Can probably do a special case for exactly 2 contacts. Not sure if it's worth it... + + fCollisionAngles.SetCount(numContacts); + for (i = 0; i < numContacts; i++) + { + const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); + fCollisionAngles[i] = hsATan2(contact->m_normal.y, contact->m_normal.x); + } + + // numContacts is rarely larger than 6, so let's do a simple bubble sort. + for (i = 0; i < numContacts; i++) + { + for (j = i + 1; j < numContacts; j++) + { + if (fCollisionAngles[i] > fCollisionAngles[j]) + { + hsScalar tempAngle = fCollisionAngles[i]; + fCollisionAngles[i] = fCollisionAngles[j]; + fCollisionAngles[j] = tempAngle; + } + } + } + + // sorted, now we check. + for (i = 1; i < numContacts; i++) + { + if (fCollisionAngles[i] - fCollisionAngles[i - 1] >= threshold) + break; + } + + if (i == numContacts) + { + // We got to the end. Check the last with the first and make your decision. + if (!(fCollisionAngles[0] - fCollisionAngles[numContacts - 1] >= (threshold - 2 * hsScalarPI))) + ground = true; + } + } +*/ + + bool ground = fController ? fController->GotGroundHit() : true; + bool isPhysical = true; + + if (!fHitGroundInThisAge && isPhysical) + fHitGroundInThisAge = true; // if we're not pinned and we're not in an age yet, we are now. + + if (IsControlledFlight()) + fControlledFlightTime += (hsScalar)elapsed; + if (fControlledFlightTime > kControlledFlightThreshold && numContacts > 0) + EnableControlledFlight(false); + + if (ground || !isPhysical) + { + if (!IsControlledFlight() && !IsOnGround()) + { + // The first ground contact in an age doesn't count. +// if (alreadyInAge) +// { +// hsVector3 vel; +// fPhysical->GetLinearVelocitySim(vel); +// fImpactVel = vel.fZ; +// fTimeInAirPeak = (hsScalar)(fTimeInAir + elapsed); +// } + + fWaitingForGround = false; + } + fTimeInAir = 0; + } + else if (elapsed < plSimulationMgr::GetInstance()->GetMaxDelta()) + { + // If the simultation skipped a huge chunk of time, we didn't process the + // collisions, which could trick us into thinking we've just gone a long + // time without hitting ground. So we only count the time if this wasn't + // the case. + fTimeInAir += (hsScalar)elapsed; + } + + + // Tweakage so that we still fall under the right conditions. + // If we're in controlled flight, or standing still with ground solidly under us (probe hit). We only use anim velocity. +// if (!IsControlledFlight() && !(ground && fProbeHitGround && fAnimPosVel.fX == 0 && fAnimPosVel.fY == 0)) +// { +// hsVector3 curV; +// fPhysical->GetLinearVelocitySim(curV); +// fAnimPosVel.fZ = curV.fZ; +// +// // Prevents us from going airborn from running up bumps/inclines. +// if (IsOnGround() && fAnimPosVel.fZ > 0.f) +// fAnimPosVel.fZ = 0.f; +// +// // Unless we're on the ground and moving, or standing still with a probe hit, we use the sim's other axes too. +// if (!(IsOnGround() && (fProbeHitGround || fAnimPosVel.fX != 0 || fAnimPosVel.fY != 0))) +// { +// fAnimPosVel.fX = curV.fX; +// fAnimPosVel.fY = curV.fY; +// } +// } +// +// fPhysical->SetLinearVelocitySim(fAnimPosVel); +// fPhysical->SetSpin(fAnimAngVel + fTurnStr, hsVector3(0.0f, 0.0f, 1.0f)); +} +#endif + + +#if 0 + +///////////////////////////////////////////////////////////////////////// + +plSimDefs::ActionType plHorizontalFreezeAction::GetType() +{ + return plSimDefs::kHorizontalFreeze; +} + +void plHorizontalFreezeAction::apply(Havok::Subspace &s, Havok::hkTime time) +{ + double elapsed = time.asDouble() - getRefresh().asDouble(); + setRefresh(time); + + int numContacts = fPhysical->GetNumContacts(); + bool ground = false; + const Havok::Vector3 straightUp(0.0f, 0.0f, 1.0f); + int i; + for(i = 0; i < numContacts; i++) + { + const Havok::ContactPoint *contact = fPhysical->GetContactPoint(i); + hsScalar dotUp = straightUp.dot(contact->m_normal); + if (dotUp > .5) + ground = true; + } + + hsVector3 vel; + fPhysical->GetLinearVelocitySim(vel); + vel.fX = 0.0; + vel.fY = 0.0; + if (ground) + vel.fZ = 0; + fPhysical->SetLinearVelocitySim(vel); + fPhysical->ClearContacts(); +} +#endif +plSwimmingController::plSwimmingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) +:plAnimatedController(rootObject,rootApp,controller) +{ + if (controller) + fSwimmingStrategy= TRACKED_NEW plSwimStrategy(controller); + else + fSwimmingStrategy = nil; +} +plSwimmingController::~plSwimmingController() +{ + delete fSwimmingStrategy; +} + +plRidingAnimatedPhysicalController::plRidingAnimatedPhysicalController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller) +: plWalkingController(rootObject, rootApp, controller) +{ + if(controller) + fWalkingStrategy = TRACKED_NEW plRidingAnimatedPhysicalStrategy(controller); + else + fWalkingStrategy = nil; +} +plRidingAnimatedPhysicalController::~plRidingAnimatedPhysicalController() +{ + delete fWalkingStrategy; + fWalkingStrategy=nil; +} + + +////////////////////////////////////////////////////////////////////////// + + +/* +Purpose: + +ANGLE_RAD_2D returns the angle in radians swept out between two rays in 2D. + +Discussion: + +Except for the zero angle case, it should be true that + +ANGLE_RAD_2D(X1,Y1,X2,Y2,X3,Y3) ++ ANGLE_RAD_2D(X3,Y3,X2,Y2,X1,Y1) = 2 * PI + +Modified: + +19 April 1999 + +Author: + +John Burkardt + +Parameters: + +Input, float X1, Y1, X2, Y2, X3, Y3, define the rays +( X1-X2, Y1-Y2 ) and ( X3-X2, Y3-Y2 ) which in turn define the +angle, counterclockwise from ( X1-X2, Y1-Y2 ). + +Output, float ANGLE_RAD_2D, the angle swept out by the rays, measured +in radians. 0 <= ANGLE_DEG_2D < 2 PI. If either ray has zero length, +then ANGLE_RAD_2D is set to 0. +*/ + +static float AngleRad2d ( float x1, float y1, float x3, float y3 ) +{ + float value; + float x; + float y; + + x = ( x1 ) * ( x3 ) + ( y1 ) * ( y3 ); + y = ( x1 ) * ( y3 ) - ( y1 ) * ( x3 ); + + if ( x == 0.0 && y == 0.0 ) { + value = 0.0; + } + else + { + value = atan2 ( y, x ); + + if ( value < 0.0 ) + { + value = (float)(value + TWO_PI); + } + } + return value; +} + +static hsBool LinearVelocity(hsVector3 &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat) +{ + bool result = false; + + hsPoint3 startPos(0.0f, 0.0f, 0.0f); // default position (at start of anim) + hsPoint3 prevPos = prevMat.GetTranslate(); // position previous frame + hsPoint3 nowPos = curMat.GetTranslate(); // position current frame + + hsVector3 prev2Now = (hsVector3)(nowPos - prevPos); // frame-to-frame delta + + if (fabs(prev2Now.fX) < 0.0001f && fabs(prev2Now.fY) < 0.0001f && fabs(prev2Now.fZ) < 0.0001f) + { + outputV.Set(0.f, 0.f, 0.f); + result = true; + } + else + { + hsVector3 start2Now = (hsVector3)(nowPos - startPos); // start-to-frame delta + + float prev2NowMagSqr = prev2Now.MagnitudeSquared(); + float start2NowMagSqr = start2Now.MagnitudeSquared(); + + float dot = prev2Now.InnerProduct(start2Now); + + // HANDLING ANIMATION WRAPPING: + // the vector from the animation origin to the current frame should point in roughly + // the same direction as the vector from the previous animation position to the + // current animation position. + // + // If they don't agree (dot < 0,) then we probably mpst wrapped around. + // The right answer would be to compare the current frame to the start of + // the anim loop, but it's cheaper to cheat and return false, + // telling the caller to use the previous frame's velocity. + if (dot > 0.0f) + { + prev2Now /= elapsed; + + float xfabs = fabs(prev2Now.fX); + float yfabs = fabs(prev2Now.fY); + float zfabs = fabs(prev2Now.fZ); + static const float maxVel = 20.0f; + hsBool valid = xfabs < maxVel && yfabs < maxVel && zfabs < maxVel; + + if (valid) + { + outputV = prev2Now; + result = true; + } + } + } + + return result; +} + +static void AngularVelocity(hsScalar &outputV, float elapsed, hsMatrix44 &prevMat, hsMatrix44 &curMat) +{ + outputV = 0.f; + hsScalar appliedVelocity = 0.0f; + hsVector3 prevForward = GetYAxis(prevMat); + hsVector3 curForward = GetYAxis(curMat); + + hsScalar angleSincePrev = AngleRad2d(curForward.fX, curForward.fY, prevForward.fX, prevForward.fY); + hsBool sincePrevSign = angleSincePrev > 0.0f; + if (angleSincePrev > hsScalarPI) + angleSincePrev = angleSincePrev - TWO_PI; + + const hsVector3 startForward = hsVector3(0, -1.0, 0); // the Y orientation of a "resting" armature.... + hsScalar angleSinceStart = AngleRad2d(curForward.fX, curForward.fY, startForward.fX, startForward.fY); + hsBool sinceStartSign = angleSinceStart > 0.0f; + if (angleSinceStart > hsScalarPI) + angleSinceStart = angleSinceStart - TWO_PI; + + // HANDLING ANIMATION WRAPPING: + // under normal conditions, the angle from rest to the current frame will have the same + // sign as the angle from the previous frame to the current frame. + // if it does not, we have (most likely) wrapped the motivating animation from frame n back + // to frame zero, creating a large angle from the previous frame to the current one + if (sincePrevSign == sinceStartSign) + { + // signs are the same; didn't wrap; use the frame-to-frame angle difference + appliedVelocity = angleSincePrev / elapsed; // rotation / time + if (fabs(appliedVelocity) < 3) + { + outputV = appliedVelocity; + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.h new file mode 100644 index 00000000..4daa441f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvCallbackAction.h @@ -0,0 +1,201 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_HK_CALLBACK_ACTION_H +#define PL_HK_CALLBACK_ACTION_H + +#include "hsGeometry3.h" +#include "hsMatrix44.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" +#include "../plPhysical/plSimDefs.h" +#include "../pnMessage/plMessage.h" +#include "plPhysicalControllerCore.h" +class plLOSHitMsg; +class plAGApplicator; +class plSceneObject; +class plPhysical; +class plAvatarController; +class plCoordinateInterface; +class plPhysicalControllerCore; +// Used by the other controllers to actually move the avatar. The actual +// implementation is in the physics system. +/*class plPhysicalController +{ +public: + // Implemented in the physics system. If you're linking this without that for + // some reason, just stub this function out. + // + // Pass in the key to the root sceneobject for the avatar + static plPhysicalController* Create(plKey ownerSO, hsScalar height, hsScalar width); + + virtual ~plPhysicalController() {} + + // A disabled avatar doesn't move or accumulate air time if he's off the ground. + virtual void Enable(bool enable) = 0; + virtual bool IsEnabled() const = 0; + + // Set the LOS DB this avatar will be in (only one) + virtual void SetLOSDB(plSimDefs::plLOSDB losDB) = 0; + + // Call this once per frame with the velocities of the avatar in avatar space. + virtual void SetVelocities(const hsVector3& linearVel, hsScalar angVel) = 0; + + // Gets the actual velocity we achieved in the last step (relative to our subworld) + virtual const hsVector3& GetLinearVelocity() const = 0; + virtual void ResetAchievedLinearVelocity() = 0; + + // Get and set the current subworld for the avatar. Use nil for the main world. + virtual plKey GetSubworld() const = 0; + virtual void SetSubworld(plKey world) = 0; + + // If IsOnGround returns false, GetAirTime will tell you how long the avatar + // has been airborne. Use ResetAirTime to reset the air time to zero, for + // cases like when the avatar spawns into a new age. + virtual bool IsOnGround() const = 0; + virtual bool IsOnFalseGround() const = 0; + virtual hsScalar GetAirTime() const = 0; + virtual void ResetAirTime() = 0; + + virtual plPhysical* GetPushingPhysical() const = 0; + virtual bool GetFacingPushingPhysical() const = 0; + + // A helper function to get the coordinate interface for the avatars current + // world. Handy if you need to convert points to and from that. This will + // return nil if the avatar is in the main world (ie, you don't need to do + // any translation). + virtual const plCoordinateInterface* GetSubworldCI() const = 0; + + // For the avatar SDL only + virtual void GetState(hsPoint3& pos, float& zRot) = 0; + virtual void SetState(const hsPoint3& pos, float zRot) = 0; + + // kinematic stuff .... should be just for when playing a behavior... + virtual void Kinematic(bool state) = 0; + virtual bool IsKinematic() = 0; + virtual void GetKinematicPosition(hsPoint3& pos) = 0; + + virtual const hsMatrix44& GetPrevSubworldW2L() = 0; + + //when seeking no longer want to interact with exclusion regions + virtual void SetSeek(bool seek)=0; + + +}; +*/ +class plAvatarController +{ +public: + virtual ~plAvatarController() {} +}; + +class plAnimatedController : public plAvatarController +{ +public: + plAnimatedController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); + + virtual void RecalcVelocity(double timeNow, double timePrev, hsBool useAnim = true); + void SetTurnStrength(hsScalar val) { fTurnStr = val; } + hsScalar GetTurnStrength() { return fTurnStr; } + virtual void ActivateController()=0; +protected: + plSceneObject* fRootObject; + plPhysicalControllerCore* fController; + plAGApplicator* fRootApp; + hsScalar fAnimAngVel; + hsVector3 fAnimPosVel; + hsScalar fTurnStr; // Explicit turning, separate from animations +}; + +class plWalkingController : public plAnimatedController +{ +public: + plWalkingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); + virtual ~plWalkingController(); + virtual void RecalcVelocity(double timeNow, double timePrev, hsBool useAnim = true); + + void Reset(bool newAge); + bool IsControlledFlight() const { return fControlledFlight != 0; } + bool IsOnGround() const { return fWalkingStrategy ? fWalkingStrategy->IsOnGround() : true; } + bool IsOnFalseGround() const { return fWalkingStrategy ? fWalkingStrategy->IsOnFalseGround() : true; } + bool HitGroundInThisAge() const { return fHitGroundInThisAge; } + bool EnableControlledFlight(bool status); + hsScalar GetAirTime() const { return fWalkingStrategy ? fWalkingStrategy->GetAirTime() : 0.f; } + void ResetAirTime() { if (fWalkingStrategy) fWalkingStrategy->ResetAirTime(); } + hsScalar GetForwardVelocity() const; + void ActivateController(); + // Check these after the avatar the avatar hits the ground for his total + // hangtime and impact velocity. + hsScalar GetImpactTime() const { return fImpactTime; } + const hsVector3& GetImpactVelocity() const { return fImpactVelocity; } + + plPhysical* GetPushingPhysical() const + { + if(fController)return fController->GetPushingPhysical(); + else return nil; + } + bool GetFacingPushingPhysical() const + { if(fController)return fController->GetFacingPushingPhysical(); + else return false; + } + + +protected: + bool fHitGroundInThisAge; + bool fWaitingForGround; // We've gone airborne. IsOnGround() returns false until we hit ground again. + hsScalar fControlledFlightTime; + int fControlledFlight; // Count of how many are currently forcing flight + plWalkingStrategy* fWalkingStrategy; + hsScalar fImpactTime; + hsVector3 fImpactVelocity; + bool fClearImpact; + bool fGroundLastFrame;//used for a test to pass the event of first getting air during a jump + static const hsScalar kControlledFlightThreshold; +}; +class plSwimmingController: public plAnimatedController +{ +public : + plSwimmingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); + virtual ~plSwimmingController(); + void SetSurface(plSwimRegionInterface *region, hsScalar surfaceHeight){ + fSwimmingStrategy->SetSurface(region,surfaceHeight); + } + hsScalar GetBuoyancy() { return fSwimmingStrategy->GetBuoyancy(); } + hsBool IsOnGround() { return fSwimmingStrategy->IsOnGround(); } + hsBool HadContacts() { return fSwimmingStrategy->HadContacts();} + void Enable(bool en){if (fController) fController->Enable(en);} + plPhysicalControllerCore* GetController(){return fController;} + virtual void ActivateController(){fSwimmingStrategy->RefreshConnectionToControllerCore();} +protected: + plSwimStrategy* fSwimmingStrategy; + +}; +class plRidingAnimatedPhysicalController: public plWalkingController +{ +public: + plRidingAnimatedPhysicalController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller); + virtual ~plRidingAnimatedPhysicalController(); +}; +#endif // PL_HK_CALLBACK_ACTION_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvDefs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvDefs.h new file mode 100644 index 00000000..e15b6815 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvDefs.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAvDefs_h +#define plAvDefs_h + +/** This is where we keep definitions that are used by different avatar classes. + Typically, these don't change very much, so there's a compile-time savings + if we don't keep them with class headers, which tend to be more volatile. +*/ + +#include "../CoreLib/hsGeometry3.h" // for the hsVector3, below + + +// animation alignment types (used by "calcanimalignment") +// when starting oneshots, we need to first get your handle into a position to play the oneshot properly +// there are different approaches to calculating this aligment +/** \enum Alignment + There are several different ways to get into position to play a detailed + interaction animation. Interactions always have an associated "seek point" + indicating an alignment point, but there are several different types of + alignment. */ +enum plAvAlignment { + kAlignHandle, /// align our handle with the seek point + kAlignHandleAnimEnd, /** align our handle at the seek point "minus" the animation + i.e. after seeking and then playing the animation, our + handle should wind up aligned with the seek point. */ + kAlignWorld, /** align our handle with the world origin; the animation + is defined in global space */ + kAlignBone, /// align a specific bone with the seek point + kAlignBoneAnimEnd, /** align our handle so that the bone is at the seek point + after the animation has finished playing. */ + kAlignEnsure16 = 0xffff +}; + +const hsVector3 kAvatarUp(0,0,1); +const hsVector3 kAvatarForward(0,-1,0); +const hsVector3 kAvatarRight(-1,0,0); + +/** \enum PinType + Tells us what category of animation this channel affects. + Primarly used to determine if two channels are competing, + which you can't tell strictly from the type of data the + channel handles. */ +enum plAGPinType +{ + kAGPinUnknown, // this applicator hasn't decided its pin type + kAGPinTransform, // this applicator munches the entire transform + kNumPinTypes +}; + +#define kAGMaxBlendPriority 0x0fffffff +#define kAGMinBlendPriority 0x00000000 +#define kAGMedBlendPriority 0x0000ffff + + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp new file mode 100644 index 00000000..18788ce8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.cpp @@ -0,0 +1,378 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../plAvatar/plAvCallbackAction.h" + +#include "hsTypes.h" + +// singular +#include "plAvLadderModifier.h" + +// local +#include "plArmatureMod.h" +#include "plAvatarMgr.h" +#include "plAvBrainGeneric.h" +#include "plAGAnim.h" +#include "plAnimStage.h" + +// global +#include "plCreatableIndex.h" +// #include "plgDispatch.h" // Message Dependencies +#include "hsStream.h" + +//other +#include "../plMessage/plCollideMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../plStatusLog/plStatusLog.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnMessage/plEnableMsg.h" + +#include "../pnMessage/plTimeMsg.h" +#include "plgDispatch.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plModifier/plDetectorLog.h" + +enum NotifyType +{ + kNotifyTrigger, + kNotifyAvatarOnLadder, +}; + +// CTOR default +plAvLadderMod::plAvLadderMod() +: fGoingUp(true), + fType(kBig), + fLoops(0), + fEnabled(true), + fAvatarInBox(false), + fLadderView(0,0,0), + fAvatarMounting(false) +{ + fTarget = nil; +} + +// CTOR goingUp, type, loops +plAvLadderMod::plAvLadderMod(bool goingUp, int type, int loops, bool enabled, hsVector3& ladderView) +: fGoingUp(goingUp), + fType(type), + fLoops(loops), + fEnabled(enabled), + fAvatarInBox(false), + fLadderView(ladderView) +{ + fTarget = nil; +} + +// Must be facing within 45 degrees of the ladder +static const hsScalar kTolerance = hsCosine(hsScalarDegToRad(45)); + +bool plAvLadderMod::IIsReadyToClimb() +{ + if (fAvatarMounting) + return false; + + plArmatureMod* armMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + plSceneObject* avatar = armMod->GetTarget(0); + if (avatar) + { + hsVector3 playerView = avatar->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + playerView.fZ = 0; + + // Are we facing towards the ladder? + hsScalar dot = playerView * fLadderView; + + bool movingForward = false; + + // And are we walking towards it? + hsAssert(armMod, "Avatar doesn't have an armature mod"); + if (armMod) + { + plAvBrainHuman* brain = plAvBrainHuman::ConvertNoRef(armMod->GetCurrentBrain()); + if (brain && brain->IsMovingForward() && brain->fCallbackAction->IsOnGround()) + movingForward = true; + } + + if (dot >= kTolerance && movingForward) + { + DetectorLogSpecial("%s: Ladder starting climb (%f)", GetKeyName(), hsScalarRadToDeg(hsACosine(dot))); + return true; + } + else if (movingForward) + { +// DetectorLog("%s: Ladder rejecting climb (%f)", GetKeyName(), hsScalarRadToDeg(hsACosine(dot))); + return false; + } + } + + return false; +} + +// use a plNotify (to ourself) to propagate across the network +void plAvLadderMod::ITriggerSelf(plKey avKey) +{ + if (fEnabled) + { + plKey avPhysKey = avKey; + // I'm going to lie and pretend it's from the avatar. the alternative is lengthy and unreadable. + plNotifyMsg *notifyMsg = TRACKED_NEW plNotifyMsg(avPhysKey, GetKey()); + notifyMsg->fID = kNotifyTrigger; + notifyMsg->Send(); + fAvatarMounting = true; + } +} + +// MSGRECEIVE +hsBool plAvLadderMod::MsgReceive(plMessage* msg) +{ + // Avatar is entering or exiting our detector box + plCollideMsg* collMsg = plCollideMsg::ConvertNoRef(msg); + if (collMsg) + { + // make sure this is the local player... the notify will be the thing that propagates over the network + if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != collMsg->fOtherKey) + return true; + + fAvatarInBox = (collMsg->fEntering != 0); + + // If entering, check if ready to climb. If not, register for eval so + // we can check every frame + if (fAvatarInBox) + { + DetectorLogSpecial("%s: Avatar entered ladder region", GetKeyName()); + + if (IIsReadyToClimb()) + ITriggerSelf(collMsg->fOtherKey); + else + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + } + else + { + DetectorLogSpecial("%s: Avatar exited ladder region", GetKeyName()); + + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + } + + return true; + } + + // Avatar is inside our detector box, so every frame we check if he's ready to climb + plEvalMsg* evalMsg = plEvalMsg::ConvertNoRef(msg); + if (evalMsg) + { + if (IIsReadyToClimb()) + ITriggerSelf(plNetClientApp::GetInstance()->GetLocalPlayerKey()); + return true; + } + + plNotifyMsg *notifyMsg = plNotifyMsg::ConvertNoRef(msg); + if (notifyMsg) + { + if (notifyMsg->fID == kNotifyTrigger && fEnabled) + { + const plKey avPhysKey = notifyMsg->GetSender(); + EmitCommand(avPhysKey); + } + else if (notifyMsg->fID == kNotifyAvatarOnLadder) + { + DetectorLogSpecial("%s: Avatar mounted ladder", GetKeyName()); + fAvatarMounting = false; + } + + return true; + } + + plEnableMsg *enableMsg = plEnableMsg::ConvertNoRef(msg); + if (enableMsg) + { + if (enableMsg->Cmd(plEnableMsg::kDisable)) + fEnabled = false; + else if (enableMsg->Cmd(plEnableMsg::kEnable)) + fEnabled = true; + } + + return plSingleModifier::MsgReceive(msg); +} + +// EMITCOMMAND +void plAvLadderMod::EmitCommand(const plKey receiver) +{ + hsKeyedObject *object = receiver->ObjectIsLoaded(); + plSceneObject *SO = plSceneObject::ConvertNoRef(object); + if(SO) + { + const plArmatureMod *constAvMod = (plArmatureMod*)SO->GetModifierByType(plArmatureMod::Index()); + if(constAvMod) + { + plAvBrainGeneric *curGenBrain = (plAvBrainGeneric *)constAvMod->FindBrainByClass(plAvBrainGeneric::Index()); + // *** warning; if there's more than one generic brain active, this will only look at the first + bool alreadyOnLadder = ( curGenBrain && curGenBrain->GetType() == plAvBrainGeneric::kLadder ); + + if( ! alreadyOnLadder) + { + plSceneObject *seekObj = GetTarget(); + plKey seekKey = seekObj->GetKey(); // this modifier's target is the seek object + + char *mountName, *dismountName, *traverseName; + + if(fGoingUp) + { + mountName = "LadderUpOn"; + dismountName = "LadderUpOff"; + traverseName = "LadderUp"; + } else { + mountName = "LadderDownOn"; + dismountName = "LadderDownOff"; + traverseName = "LadderDown"; + } + + plAnimStageVec *v = TRACKED_NEW plAnimStageVec; + + plAnimStage *s1 = TRACKED_NEW plAnimStage(mountName, + 0, + plAnimStage::kForwardAuto, + plAnimStage::kBackAuto, + plAnimStage::kAdvanceAuto, + plAnimStage::kRegressAuto, + 0); + if( ! fGoingUp) + s1->SetReverseOnIdle(true); + v->push_back(s1); + + // if loops is zero, we don't need the traverse animation at all. + if(fLoops) + { + plAnimStage *s2 = TRACKED_NEW plAnimStage(traverseName, + 0, + plAnimStage::kForwardKey, + plAnimStage::kBackKey, + plAnimStage::kAdvanceAuto, + plAnimStage::kRegressAuto, + fLoops - 1 // first loop is implied; zero loops means + // play this anim once, 1 loop means "loop + // once after reaching the end." + ); + if( ! fGoingUp) + s2->SetReverseOnIdle(true); + v->push_back(s2); + } + plAnimStage *s3 = TRACKED_NEW plAnimStage(dismountName, + 0, + plAnimStage::kForwardAuto, + plAnimStage::kBackAuto, + plAnimStage::kAdvanceAuto, + plAnimStage::kRegressAuto, + 0); + if( ! fGoingUp) + s3->SetReverseOnIdle(true); + v->push_back(s3); + + plNotifyMsg* enterNotify = TRACKED_NEW plNotifyMsg(GetKey(), GetKey()); + enterNotify->fID = kNotifyAvatarOnLadder; + + UInt32 exitFlags = plAvBrainGeneric::kExitNormal; + + plAvBrainGeneric *ladBrain = TRACKED_NEW plAvBrainGeneric(v, enterNotify, nil, nil, exitFlags, plAvBrainGeneric::kDefaultFadeIn, + plAvBrainGeneric::kDefaultFadeOut, plAvBrainGeneric::kMoveRelative); + ladBrain->SetType(plAvBrainGeneric::kLadder); + ladBrain->SetReverseFBControlsOnRelease(!fGoingUp); + + plKey avKey = constAvMod->GetKey(); + + // Very important that we dumb seek here. Otherwise you can run off the edge of a ladder, and seek will be helpless + // until you hit the ground, at which point you have no hope of successfully seeking. + plAvSeekMsg *seeker = TRACKED_NEW plAvSeekMsg(nil, avKey, seekKey, 1.0f, false); + seeker->Send(); + plAvPushBrainMsg *brainer = TRACKED_NEW plAvPushBrainMsg(nil, avKey, ladBrain); + brainer->Send(); + } + } + } +} + +void plAvLadderMod::Read(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Read(stream, mgr); + + fType = stream->ReadSwap32(); + fLoops = stream->ReadSwap32(); + fGoingUp = stream->Readbool(); + fEnabled = stream->Readbool(); + fLadderView.fX = stream->ReadSwapScalar(); + fLadderView.fY = stream->ReadSwapScalar(); + fLadderView.fZ = stream->ReadSwapScalar(); +} + +void plAvLadderMod::Write(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Write(stream, mgr); + + stream->WriteSwap32(fType); + stream->WriteSwap32(fLoops); + stream->Writebool(fGoingUp); + stream->WriteBool(fEnabled); + stream->WriteSwapScalar(fLadderView.fX); + stream->WriteSwapScalar(fLadderView.fY); + stream->WriteSwapScalar(fLadderView.fZ); +} + +// true is up; false is down +bool plAvLadderMod::GetGoingUp() const +{ + return fGoingUp; +} + +void plAvLadderMod::SetGoingUp(bool up) +{ + fGoingUp = up; +} + +int plAvLadderMod::GetLoops() const +{ + return fLoops; +} + +void plAvLadderMod::SetLoops(int loops) +{ + fLoops = loops; +} + +int plAvLadderMod::GetType() const +{ + return fType; +} + +void plAvLadderMod::SetType(int type) +{ + if(type >= kNumOfTypeFields || type < 0) + { + hsStatusMessage("Invalid param to plAvLadderMod::SetType: defaulting to kBig"); + fType = kBig; + } else { + fType = type; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.h new file mode 100644 index 00000000..ad209273 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvLadderModifier.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAvLadderMod_INC +#define plAvLadderMod_INC + +#include "../pnModifier/plSingleModifier.h" +#include "../pnMessage/plMessage.h" +#include "hsGeometry3.h" + +// has a detector region. when a local avatar enters that region, +// creates a ladder brain and applies it to the avatar with the relevant fields +class plAvLadderMod : public plSingleModifier +{ +public: + plAvLadderMod(); + plAvLadderMod(bool goingUp, int type, int loops, bool enabled, hsVector3& ladderView); + virtual ~plAvLadderMod() {}; + + void EmitCommand(const plKey receiver); + + CLASSNAME_REGISTER( plAvLadderMod ); + GETINTERFACE_ANY( plAvLadderMod, plSingleModifier ); + + // virtual void AddTarget(plSceneObject* so) { SetTarget(so); } + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + bool GetGoingUp() const; + void SetGoingUp(bool v); + + int GetLoops() const; + void SetLoops(int v); + + int GetType() const; + void SetType(int v); + + void SetEnabled(bool enabled) { fEnabled = enabled; } + +protected: + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) {return true;} + bool IIsReadyToClimb(); + void ITriggerSelf(plKey avKey); + + enum fTypeField + { + kBig, // big ladders are built from a mount, N traverse loops, and a dismount + kFourFeet, // four-foot ladders are a one-shot + kTwoFeet, // two-foot (step)ladders are a one-shot + kNumOfTypeFields, + }; + + bool fGoingUp; // true means heading up; false means down + int fType; // what type of ladder are we? + int fLoops; // if we're a big ladder, how many traverse loops do we need? + hsVector3 fLadderView; + bool fEnabled; + bool fAvatarInBox; + bool fAvatarMounting; // True if the avatar is in the process of mounting the ladder. + // Don't try to trigger during this time. +}; + +#endif plAvLadderMod_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvMotorHuman.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvMotorHuman.cpp new file mode 100644 index 00000000..e1078d86 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvMotorHuman.cpp @@ -0,0 +1,115 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#pragma once + +#ifndef PLAVMOTORHUMAN_INC + +/*** \file plAvMotorHuman.h +*/ + +#include + +class plAvBrainHuman; + +/** \class plAvMotorHuman + Applies avatar at simulation frequency rather than frame frequency. +*/ +class plAvMotorHuman : public Havok::Action +{ +public: + plAvMotorHuman(); + + /** Called from havok during integration; converts time to plasma format + and asks the brain to move. Brain will sample animation at current time + and apply velocity or forces to the body. + */ + virtual void apply(Havok::Subspace &subspace, Havok::hkTime time); + + /** Sets the motor to control a different human brain. + Returns the previously controlled brain, or nil. + */ + plAvBrainHuman * SetBrain(plAvBrainHuman *brain); + + /** Which brain is this motor currently controlling? + */ + plAvBrainHuman * GetBrain(); + +private: + /** The brain we get our movement data from */ + plAvBrainHuman *fBrain; + double fLastTime; +}; + + + +/*** \file plAvMotorHuman.cpp +*/ + + +#include "plAvBrainHuman.h" + +plAvMotorHuman::plAvMotorHuman() +: fBrain(0), + fLastTime(0.0f) +{ +} + +//plAvMotorHuman::plAvMotorHuman(plAvBrainHuman *brain) +//: fBrain(brain), +// fLastTime(0.0f); + +void plAvMotorHuman::apply(Havok::Subspace &subspace, Havok::hkTime time) +{ + double timeNow = time.asDouble(); + double elapsedD = timeNow - fLastTime; + float elapsed = elapsedD; + + if(elapsed > 1.0f) + { + elapsed = 0.1f; + hsStatusMessageF("Clamping plAvMotorHuman::apply interval to <%f>", elapsed); + + fLastTime = timeNow - elapsed; + } + +// fBrain->MoveViaAnimation(timeNow, elapsed); +} + +plAvBrainHuman * plAvMotorHuman::SetBrain(plAvBrainHuman *brain) +{ + plAvBrainHuman * oldBrain = fBrain; + + fBrain = brain; + + return oldBrain; +} + +plAvBrainHuman * plAvMotorHuman::GetBrain() +{ + return fBrain; +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvMotorHuman.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvMotorHuman.h new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvMotorHuman.h @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTask.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTask.cpp new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTask.cpp @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTask.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTask.h new file mode 100644 index 00000000..908af555 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTask.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAvTask_h +#define plAvTask_h + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// +// base class +#include "../pnFactory/plCreatable.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DECLARATIONS +// +///////////////////////////////////////////////////////////////////////////////////////// +class plArmatureMod; +class plArmatureBrain; +class plDebugText; + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEFINITIONS +// +///////////////////////////////////////////////////////////////////////////////////////// +/** \class plAvTask + Abstract class for avatar tasks + A task is an activity which completely occupies the avatar from beginning to end. + Only one task can run at a time. + Tasks are queued within a given avatar brain; you can queue several at once, and they'll + run in succession, with each waiting for the former to complete before running. + Some tasks are instantaneous (like attaching an animation,) and only use the task model for + sequencing effect. (i.e., walk here, attach this animation, etc.) + If your task requires user intervention or control, you probably should + be using an avatar brain instead. Tasks are meant to sort of run on their own. +*/ +class plAvTask : public plCreatable +{ +public: + plAvTask(); + + /** Start the task: set up initial conditions or wait for resources to become available. + Start will be called repeatedly until it returns true, indicating the task has begun. */ + virtual hsBool Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + + /** Run the task. Start is guaranteed to have returned true before Process() is called even once. + Returns false when the task has finished and epilogue code has been executed. */ + virtual hsBool Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + + /** Clean up the task. This is guaranteed to be called when Process returns false. */ + virtual void Finish(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + + virtual void LeaveAge(plArmatureMod *avatar) {} + + /** dump descriptive stuff to the given debug text */ + virtual void DumpDebug(const char *name, int &x, int&y, int lineHeight, char *strBuf, plDebugText &debugTxt); + + // plasma protocol + CLASSNAME_REGISTER( plAvTask ); + GETINTERFACE_ANY( plAvTask, plCreatable ); + + /** Read the task from a stream. Not all tasks need to read/write, so the base implementation + gives a warning to expose tasks that are being read/written unexpectedly. */ + virtual void Read(hsStream *stream, hsResMgr *mgr); + + /** Write the task to a stream. Not all tasks need to read/write, so the base implementation + gives a warning to expose tasks that are being read/written unexpectedly. */ + virtual void Write(hsStream *stream, hsResMgr *mgr); + +protected: + virtual void ILimitPlayersInput(plArmatureMod *avatar); + virtual void IUndoLimitPlayersInput(plArmatureMod *avatar); +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskBrain.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskBrain.cpp new file mode 100644 index 00000000..aaae0a65 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskBrain.cpp @@ -0,0 +1,115 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +// singular +#include "plAvTaskBrain.h" + +// local +#include "plArmatureMod.h" +#include "plAvBrain.h" + +// other +#include "../plPipeline/plDebugText.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// PLAVTASKBRAIN +// +///////////////////////////////////////////////////////////////////////////////////////// + +// CTOR default +plAvTaskBrain::plAvTaskBrain() +: fBrain(nil) +{ +} + +// CTOR brain +plAvTaskBrain::plAvTaskBrain(plArmatureBrain *brain) +: fBrain(brain) +{ +} + +plAvTaskBrain::~plAvTaskBrain() +{ + delete fBrain; +} + +// Start ------------------------------------------------------------------------------------------ +// ------ +hsBool plAvTaskBrain::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + if(fBrain) + avatar->PushBrain(fBrain); + else + avatar->PopBrain(); + + fBrain = nil; // We've passed it on the the avatar, set our pointer to nil so we don't destroy it + + return true; +} + +void plAvTaskBrain::Finish(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ +} + + +// DumpDebug ------------------------------------------------------------------------------------------------------ +// ---------- +void plAvTaskBrain::DumpDebug(const char *name, int &x, int&y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + if(fBrain) + { + debugTxt.DrawString(x, y, "Brain task -- Push New Brain."); + } else { + debugTxt.DrawString(x, y, "Brain task -- Pop Current Brain."); + } + y += lineHeight; +} + + +// GetBrain ------------------------ +// --------- +plArmatureBrain *plAvTaskBrain::GetBrain() +{ + return fBrain; +} + +// Read ------------------------------------------ +// ----- +void plAvTaskBrain::Read(hsStream *stream, hsResMgr *mgr) +{ + plAvTask::Read(stream, mgr); + fBrain = plArmatureBrain::ConvertNoRef(mgr->ReadCreatable(stream)); +} + +// Write ------------------------------------------ +// ------ +void plAvTaskBrain::Write(hsStream *stream, hsResMgr *mgr) +{ + plAvTask::Write(stream, mgr); + mgr->WriteCreatable(stream, fBrain); + +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskBrain.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskBrain.h new file mode 100644 index 00000000..bc1e6158 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskBrain.h @@ -0,0 +1,80 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAvTaskBrain_h +#define plAvTaskBrain_h + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// +#include "plAvTask.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEFINITIONS +// +///////////////////////////////////////////////////////////////////////////////////////// + +/** \class plAvTaskBrain + Push a new brain onto the avatar or pull an old brain off. + If a brain is supplied, it's a push. Otherwise it's a pull. +*/ +class plAvTaskBrain : public plAvTask +{ +public: + /** Default constructor. Used as is, will function as a pop brain message. */ + plAvTaskBrain(); + /** Constructor for a push configuration. If the brain is non, nil, it + will be pushed onto the receiving avatar when the task is dequeued. + If the brain is nil, the current brain will be popped. */ + plAvTaskBrain(plArmatureBrain *brain); + virtual ~plAvTaskBrain(); + + // task protocol + virtual hsBool Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + virtual void Finish(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + + /** dump descriptive stuff to the given debug text */ + virtual void DumpDebug(const char *name, int &x, int&y, int lineHeight, char *strBuf, plDebugText &debugTxt); + + plArmatureBrain *GetBrain(); + + CLASSNAME_REGISTER( plAvTaskBrain ); + GETINTERFACE_ANY( plAvTaskBrain, plAvTask ); + + /** Read the task from a stream. Not all tasks need to read/write, so the base implementation + gives a warning to expose tasks that are being read/written unexpectedly. */ + virtual void Read(hsStream *stream, hsResMgr *mgr); + + /** Write the task to a stream. Not all tasks need to read/write, so the base implementation + gives a warning to expose tasks that are being read/written unexpectedly. */ + virtual void Write(hsStream *stream, hsResMgr *mgr); +protected: + plArmatureBrain *fBrain; +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskOrient.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskOrient.cpp new file mode 100644 index 00000000..c417d4f5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskOrient.cpp @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// obsolete \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskOrient.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskOrient.h new file mode 100644 index 00000000..c417d4f5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskOrient.h @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// obsolete \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp new file mode 100644 index 00000000..4c1a851c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.cpp @@ -0,0 +1,623 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// + +// singular +#include "plAvTaskSeek.h" + +// local +#include "plAvBrainHuman.h" +#include "plAGAnim.h" +#include "plArmatureMod.h" +#include "plAvatarMgr.h" +#include "plAvCallbackAction.h" + +// other +#include "../plMessage/plAvatarMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pnInputCore/plControlEventCodes.h" +#include "../plPipeline/plDebugText.h" +#include "../plStatusLog/plStatusLog.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "hsTimer.h" +#include "plgDispatch.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// PROTOTYPES +// +///////////////////////////////////////////////////////////////////////////////////////// +float QuatAngleDiff(const hsQuat &a, const hsQuat &b); +void MakeMatrixUpright(hsMatrix44 &mat); + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEFINES +// +///////////////////////////////////////////////////////////////////////////////////////// + +#define kSeekTimeout 5.0f +#define kRotSpeed 1.0f // normal rotation speed is 1.0 radians per second +#define kFloatSpeed 3.0f +#define kMaxRadiansPerSecond 1.5 + +#define kDefaultShuffleRange 0.5f +#define kDefaultMaxSidleRange 4.0f +#define kDefaultMaxSidleAngle 0.2f + +hsBool plAvTaskSeek::fLogProcess = false; + +///////////////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +///////////////////////////////////////////////////////////////////////////////////////// + +void plAvTaskSeek::IInitDefaults() +{ + fSeekObject = nil; + fMovingTarget = false; + fAlign = kAlignHandle; + fAnimName = nil; + fPosGoalHit = false; + fRotGoalHit = false; + fStillPositioning = true; + fStillRotating = true; + fShuffleRange = kDefaultShuffleRange; + fMaxSidleRange = kDefaultMaxSidleRange; + fMaxSidleAngle = kDefaultMaxSidleAngle; + fFlags = kSeekFlagForce3rdPersonOnStart; + fState = kSeekRunNormal; + fNotifyFinishedKey = nil; +} +// plAvTaskSeek ------------ +// ------------- +plAvTaskSeek::plAvTaskSeek() {} + +plAvTaskSeek::plAvTaskSeek(plAvSeekMsg *msg) +{ + IInitDefaults(); + + fAlign = msg->fAlignType; + fAnimName = msg->fAnimName; + + plKey &target = msg->fSeekPoint; + if (target) + SetTarget(target); + else + SetTarget(msg->fTargetPos, msg->fTargetLookAt); + + if (msg->UnForce3rdPersonOnFinish()) + fFlags |= kSeekFlagUnForce3rdPersonOnFinish; + else + fFlags &= ~kSeekFlagUnForce3rdPersonOnFinish; + + if (msg->Force3rdPersonOnStart()) + fFlags |= kSeekFlagForce3rdPersonOnStart; + else + fFlags &= ~kSeekFlagForce3rdPersonOnStart; + + if (msg->NoWarpOnTimeout()) + fFlags |= kSeekFlagNoWarpOnTimeout; + else + fFlags &= ~kSeekFlagNoWarpOnTimeout; + + if (msg->RotationOnly()) + { + fFlags |= kSeekFlagRotationOnly; + fStillPositioning = false; + fPosGoalHit = true; + } + else + fFlags &= ~kSeekFlagRotationOnly; + + fNotifyFinishedKey = msg->fFinishKey; +} + +// plAvTaskSeek ------------------------ +// ------------- +plAvTaskSeek::plAvTaskSeek(plKey target) +{ + IInitDefaults(); + + SetTarget(target); +} + +// plAvTaskSeek ------------------------------------------------------------------------------------------- +// ------------- +plAvTaskSeek::plAvTaskSeek(plKey target, plAvAlignment align, const char *animName, bool moving) +{ + IInitDefaults(); + + fMovingTarget = moving; + fAlign = align; + fAnimName = animName; + + SetTarget(target); +} + +void plAvTaskSeek::SetTarget(plKey target) +{ + hsAssert(target, "Bad key to seek task"); + if(target) + { + fSeekObject = plSceneObject::ConvertNoRef(target->ObjectIsLoaded()); + } + else + { + fSeekObject = nil; + } +} + +void plAvTaskSeek::SetTarget(hsPoint3 &pos, hsPoint3 &lookAt) +{ + fSeekPos = pos; + hsVector3 up(0.f, 0.f, 1.f); + hsScalar angle = hsATan2(lookAt.fY - pos.fY, lookAt.fX - pos.fX) + hsScalarPI / 2; + fSeekRot.SetAngleAxis(angle, up); +} + +// Start ----------------------------------------------------------------------------------------- +// ------ +hsBool plAvTaskSeek::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + plAvBrainHuman *huBrain = plAvBrainHuman::ConvertNoRef(brain); + hsAssert(huBrain, "Seek task only works on human brains"); + + plAvatarMgr::GetInstance()->GetLog()->AddLine("Starting SMART SEEK"); + //controller needs to know we are seeking. prevents controller from interacting with exclusion regions + + if (avatar->GetController() ) + avatar->GetController()->SetSeek(true); + fStartTime = time; + if(huBrain) + { + avatar->SuspendInput(); // stop accepting input from the user, but queue any messages + // ...and save our current input state. + + ILimitPlayersInput(avatar); + + if (plAvOneShotTask::fForce3rdPerson && avatar->IsLocalAvatar() && (fFlags & plAvSeekMsg::kSeekFlagForce3rdPersonOnStart)) + { + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, false); + pMsg->SetCmd(plCameraMsg::kResponderSetThirdPerson); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + + huBrain->IdleOnly(); // Makes sure to kill jumps too. Just calling ClearInputFlags isn't enough + IUpdateObjective(avatar); + return true; + } + else + { + return false; + } +} + +// Process ------------------------------------------------------------------------------------------- +// -------- +hsBool plAvTaskSeek::Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + if (fState == kSeekAbort) + return false; + + plAvBrainHuman *uBrain = plAvBrainHuman::ConvertNoRef(brain); + if (uBrain) + { + if (fMovingTarget) + { + IUpdateObjective(avatar); + } + + IAnalyze(avatar); + hsBool result = IMoveTowardsGoal(avatar, uBrain, time, elapsed); + if (fLogProcess) + DumpToAvatarLog(avatar); + return result; + } + + return false; +} + +// Finish --------------------------------------------------------------------------------------- +// ------- +void plAvTaskSeek::Finish(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + plAvBrainHuman *huBrain = plAvBrainHuman::ConvertNoRef(brain); + + if(huBrain) + { + // this will process any queued input messages so if the user pressed or released a key while we were busy, we'll note it now. + avatar->ResumeInput(); + IUndoLimitPlayersInput(avatar); + + if (plAvOneShotTask::fForce3rdPerson && avatar->IsLocalAvatar() && (fFlags & plAvSeekMsg::kSeekFlagUnForce3rdPersonOnFinish)) + { + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, false); + pMsg->SetCmd(plCameraMsg::kResponderUndoThirdPerson); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + + avatar->SynchIfLocal(hsTimer::GetSysSeconds(), false); + } + + if (fNotifyFinishedKey) + { + plAvTaskSeekDoneMsg *msg = TRACKED_NEW plAvTaskSeekDoneMsg(avatar->GetKey(), fNotifyFinishedKey); + msg->fAborted = (fState == kSeekAbort); + msg->Send(); + } + plAvatarMgr::GetInstance()->GetLog()->AddLine("Finished SMART SEEK"); + //inform controller we are done seeking + if (avatar->GetController()) + avatar->GetController()->SetSeek(false); +} + +void plAvTaskSeek::LeaveAge(plArmatureMod *avatar) +{ + fSeekObject = nil; + fState = kSeekAbort; +} + +// IAnalyze ---------------------------------------- +// --------- +hsBool plAvTaskSeek::IAnalyze(plArmatureMod *avatar) +{ + avatar->GetPositionAndRotationSim(&fPosition, &fRotation); + fGoalVec.Set(&(hsScalarTriple)(fSeekPos - fPosition)); + hsVector3 normalizedGoalVec(fGoalVec); + normalizedGoalVec.Normalize(); + + fDistance = hsSquareRoot(fGoalVec.fX * fGoalVec.fX + fGoalVec.fY * fGoalVec.fY); + + if(fDistance < 3.0f) + { + // we're in "near target" mode + fMinFwdAngle = .5f; // walk forward if target's in 90' cone straight ahead + fMaxBackAngle = -.2f; // walk backward if target's in a 144' cone behind + } + else + { + // we're in "far target" mode + fMinFwdAngle = .2f; // walk forward if target's in a 144' cone ahead + fMaxBackAngle = -2.0; // disable backing up if goal is far out (-1 is the minimum usable value here) + } + + hsQuat invRot = fRotation.Conjugate(); + hsPoint3 globFwd = invRot.Rotate(&kAvatarForward); + hsPoint3 globRight = invRot.Rotate(&kAvatarRight); + + hsPoint3 locGoalVec = fRotation.Rotate(&fGoalVec); + + fDistForward = -(locGoalVec.fY); + fDistRight = -(locGoalVec.fX); + + fAngForward = globFwd.InnerProduct(normalizedGoalVec); + fAngRight = globRight.InnerProduct(normalizedGoalVec); + return true; +} + +// IMoveTowardsGoal -------------------------------------------------------------- +// ----------------- +hsBool plAvTaskSeek::IMoveTowardsGoal(plArmatureMod *avatar, plAvBrainHuman *brain, + double time, hsScalar elapsed) +{ + bool stillRunning = true; + avatar->ClearInputFlags(false, false); + + double duration = time - fStartTime; + + if(duration > kSeekTimeout) + { + if (fFlags & kSeekFlagNoWarpOnTimeout) + { + fState = kSeekAbort; + return false; + } + fSeekRot.Normalize(); + avatar->SetPositionAndRotationSim(&fSeekPos, &fSeekRot); + IAnalyze(avatar); // Recalcs fPosition, fDistance, etc. + hsStatusMessage("Timing out on smart seek - jumping to target."); + stillRunning = false; + + // We just set the pos/rot, so we know these are hit. + fPosGoalHit = true; + fRotGoalHit = true; + } + + if (!(fDistance > fShuffleRange)) + fPosGoalHit = true; + + if (!fPosGoalHit) + { + bool right = fAngRight > 0.0f; + bool inSidleRange = fDistance < fMaxSidleRange; + + bool sidling = fabs(fDistRight) > fabs(fDistForward) && inSidleRange; + + if(sidling) + { + if(right) + avatar->SetStrafeRightKeyDown(); + else + avatar->SetStrafeLeftKeyDown(); + } + else + { + if(fAngForward < fMaxBackAngle) + avatar->SetBackwardKeyDown(); + + else + { + if(fAngForward > fMinFwdAngle) + avatar->SetForwardKeyDown(); + + if(right) + avatar->SetTurnRightKeyDown(); + else + avatar->SetTurnLeftKeyDown(); + } + } + } + else + { + if (!(QuatAngleDiff(fRotation, fSeekRot) > .1)) + fRotGoalHit = true; + + if (!fRotGoalHit) + { + hsQuat invRot = fSeekRot.Conjugate(); + hsPoint3 globFwd = invRot.Rotate(&kAvatarForward); + globFwd = fRotation.Rotate(&globFwd); + + if (globFwd.fX < 0) + avatar->SetTurnRightKeyDown(); + else + avatar->SetTurnLeftKeyDown(); + } + } + + if (fPosGoalHit && fRotGoalHit) + stillRunning = ITryFinish(avatar, brain, time, elapsed); + + return stillRunning; +} + + +// ITRYFINISH +bool plAvTaskSeek::ITryFinish(plArmatureMod *avatar, plAvBrainHuman *brain, double time, hsScalar elapsed) +{ + hsBool animsDone = brain->IsMovementZeroBlend(); + + hsPoint3 newPosition = fPosition; + hsQuat newRotation = fRotation; + + if (!(fFlags & kSeekFlagRotationOnly) && (fStillPositioning || !animsDone)) + fStillPositioning = IFinishPosition(newPosition, avatar, brain, time, elapsed); + if (fStillRotating || !animsDone) + fStillRotating = IFinishRotation(newRotation, avatar, brain, time, elapsed); + + newRotation.Normalize(); + if (hsCheckBits(fFlags, kSeekFlagRotationOnly)) + avatar->SetPositionAndRotationSim(nil, &newRotation); + else + avatar->SetPositionAndRotationSim(&newPosition, &newRotation); + + return fStillPositioning || fStillRotating || !animsDone; +} + +hsBool plAvTaskSeek::IFinishPosition(hsPoint3 &newPosition, + plArmatureMod *avatar, plAvBrainHuman *brain, + double time, hsScalar elapsed) +{ + // While warping, we might be hovering just above the ground. Don't want that to + // trigger any falling behavior. + if(brain&&brain->fCallbackAction) + { + + brain->fCallbackAction->ResetAirTime(); + } + // how far will we translate this frame? + float thisDist = kFloatSpeed * elapsed; + // what percentage of the remaining distance will we cover? + float thisPct = (fDistance ? thisDist / fDistance : 1.f); + + if(thisPct > 0.9f) + { + // we're pretty much done; just hop the rest of the way + newPosition = fSeekPos; + return false; // we're done + } + else + { + // move incrementally toward the target position + hsVector3 thisMove = fGoalVec * thisPct; + newPosition = fPosition + thisMove; + return true; // we're still processing + } + return true; +} + + + +// IFinishRotation -------------------------------------- +// ---------------- +hsBool plAvTaskSeek::IFinishRotation(hsQuat &newRotation, + plArmatureMod *avatar, plAvBrainHuman *brain, + double time, hsScalar elapsed) +{ + // we're pretty much done; just hop the rest of the way + newRotation = fSeekRot; + return false; +} + +// IUpdateObjective ---------------------------------------- +// ----------------- +hsBool plAvTaskSeek::IUpdateObjective(plArmatureMod *avatar) +{ + // This is an entirely valid case. It just means our goal is fixed. + if (fSeekObject == nil) + return true; + + // goal here is to express the target matrix in the avatar's PHYSICAL space + hsMatrix44 targL2W = fSeekObject->GetLocalToWorld(); + const plCoordinateInterface* subworldCI = nil; + if (avatar->GetController()) + subworldCI = avatar->GetController()->GetSubworldCI(); + if (subworldCI) + targL2W = subworldCI->GetWorldToLocal() * targL2W; + + MakeMatrixUpright(targL2W); + + switch(fAlign) + { + // match our handle to the target matrix at the end of the given animation + // This case isn't currently used but will be important someday. The idea + // is that you have a target point and an animation, and you want to seek + // the avatar to a point where he can start playing the animation and wind + // up, after the animation completes, at the target location. + // Hence "AlignHandleAnimEnd" = "align the avatar so the animation will + // end on the target." + case kAlignHandleAnimEnd: + { + hsMatrix44 adjustment; + plAGAnim *anim = avatar->FindCustomAnim(fAnimName); + // don't need to do this every frame; the animation doesn't change. + // *** cache the adjustment; + GetStartToEndTransform(anim, nil, &adjustment, "Handle"); // actually getting end-to-start + // ... but we do still need to multiply by the (potentially changed) target + targL2W = targL2W * adjustment; + } + break; + case kAlignHandle: // targetMat is already correct + default: + break; + }; + + GetPositionAndRotation(targL2W, &fSeekPos, &fSeekRot); + + return true; +} + + + +// DumpDebug ----------------------------------------------------------------------------------------------------- +// ---------- +void plAvTaskSeek::DumpDebug(const char *name, int &x, int&y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + sprintf(strBuf, "duration: %.2f pos: (%.3f, %.3f, %.3f) goalPos: (%.3f, %.3f, %.3f) ", + hsTimer::GetSysSeconds() - fStartTime, + fPosition.fX, fPosition.fY, fPosition.fZ, fSeekPos.fX, fSeekPos.fY, fSeekPos.fZ); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + + sprintf(strBuf, "positioning: %d rotating %d goalVec: (%.3f, %.3f, %.3f) dist: %.3f angFwd: %.3f angRt: %.3f", + fStillPositioning, fStillRotating, fGoalVec.fX, fGoalVec.fY, fGoalVec.fZ, fDistance, fAngForward, fAngRight); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + + sprintf(strBuf, " distFwd: %.3f distRt: %.3f shufRange: %.3f sidAngle: %.3f sidRange: %.3f, fMinWalk: %.3f", + fDistForward, fDistRight, fShuffleRange, fMaxSidleAngle, fMaxSidleRange, fMinFwdAngle); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; +} + +void plAvTaskSeek::DumpToAvatarLog(plArmatureMod *avatar) +{ + plStatusLog *log = plAvatarMgr::GetInstance()->GetLog(); + char strBuf[256]; + avatar->GetMoveKeyString(strBuf); + log->AddLine(strBuf); + + sprintf(strBuf, " duration: %.2f pos: (%.3f, %.3f, %.3f) goalPos: (%.3f, %.3f, %.3f) ", + hsTimer::GetSysSeconds() - fStartTime, + fPosition.fX, fPosition.fY, fPosition.fZ, fSeekPos.fX, fSeekPos.fY, fSeekPos.fZ); + log->AddLine(strBuf); + + sprintf(strBuf, " positioning: %d rotating %d goalVec: (%.3f, %.3f, %.3f) dist: %.3f angFwd: %.3f angRt: %.3f", + fStillPositioning, fStillRotating, fGoalVec.fX, fGoalVec.fY, fGoalVec.fZ, fDistance, fAngForward, fAngRight); + log->AddLine(strBuf); + + sprintf(strBuf, " distFwd: %.3f distRt: %.3f shufRange: %.3f sidAngle: %.3f sidRange: %.3f, fMinWalk: %.3f", + fDistForward, fDistRight, fShuffleRange, fMaxSidleAngle, fMaxSidleRange, fMinFwdAngle); + log->AddLine(strBuf); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// LOCALS +// +///////////////////////////////////////////////////////////////////////////////////////// + +// QuatAngleDiff ------------------------------------ +// -------------- +float QuatAngleDiff(const hsQuat &a, const hsQuat &b) +{ + hsScalar theta; /* angle between A and B */ + hsScalar cos_t; /* sine, cosine of theta */ + + /* cosine theta = dot product of A and B */ + cos_t = a.Dot(b); + + /* if B is on opposite hemisphere from A, use -B instead */ + if (cos_t < 0.0) + { + cos_t = -cos_t; + } + + // Calling acos on 1.0 is returning an undefined value. Need to check for it. + hsScalar epsilon = 0.00001; + if (hsABS(cos_t - 1.f) < epsilon) + return 0; + + theta = hsACosine(cos_t); + return theta; +} + +// MakeMatrixUpright ------------------------------------------- +// ------------------ +// ensure that the z axis of the given matrix points at the sky. +// does not orthonormalize +// man, I could have sworn I did this somewhere else... +void MakeMatrixUpright(hsMatrix44 &mat) +{ + mat.fMap[0][2] = 0.0f; // eliminate any z in the x axis + mat.fMap[1][2] = 0.0f; // eliminate any z in the y axis + mat.fMap[2][0] = 0.0f; mat.fMap[2][1] = 0.0f; mat.fMap[2][2] = 1.0f; // z axis = pure sky +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.h new file mode 100644 index 00000000..5b6f5a0f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvTaskSeek.h @@ -0,0 +1,174 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLAVTASKSEEK_INC +#define PLAVTASKSEEK_INC +#pragma once + +#include "plAvatarTasks.h" +#include "hsQuat.h" +#include "..\CoreLib\hsGeometry3.h" + +class plArmatureMod; +class plArmatureBrain; +class plAvBrainHuman; +class plSceneObject; + +// PLAVTASKSEEK +class plAvTaskSeek : public plAvTask +{ +public: + static hsBool fLogProcess; + + enum State { + kSeekRunNormal, + kSeekAbort, + }; + UInt8 fState; + + enum + { + kSeekFlagUnForce3rdPersonOnFinish = 0x1, + kSeekFlagForce3rdPersonOnStart = 0x2, + kSeekFlagNoWarpOnTimeout = 0x4, + kSeekFlagRotationOnly = 0x8, + }; + + plAvTaskSeek(); + plAvTaskSeek(plKey target); + plAvTaskSeek(plAvSeekMsg *msg); + plAvTaskSeek(plKey target, plAvAlignment align, const char *animName, bool moving); + + void SetTarget(plKey target); + void SetTarget(hsPoint3 &pos, hsPoint3 &lookAt); + + /** Initiate the task; make sure we're running on the right type of brain, save off + user input state, and turn off any other running behaviors.*/ + virtual hsBool Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + + /** Progress towards the goal using a combination of walking and cheating-via-sliding. + Returns true if we're still working on it; false if we're done. */ + + virtual hsBool Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + + /** Restore user input state, etc. */ + virtual void Finish(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + + /** clear our target, and when we try to eval, we'll just finish */ + virtual void LeaveAge(plArmatureMod *avatar); + + /** Spew "useful" information to the game screen. Used when Avatar.Debug is active. */ + virtual void DumpDebug(const char *name, int &x, int&y, int lineHeight, char *strBuf, plDebugText &debugTxt); + + void DumpToAvatarLog(plArmatureMod *avatar); + + CLASSNAME_REGISTER( plAvTaskSeek ); + GETINTERFACE_ANY( plAvTaskSeek, plAvTask ); + +protected: + + ///////////////////////////////////////////////////////////////////////////////////// + // + // METHODS + // + ///////////////////////////////////////////////////////////////////////////////////// + + /** Called by constructors */ + void IInitDefaults(); + + /** Make some observations about our current relation to our target. + Done every frame. */ + hsBool IAnalyze(plArmatureMod *avatar); + + /** Progress towards the goal. We get as close as we can by just pushing the same + buttons as the user (forward, turn, etc.) when we're really close we slide + around a bit so we can wind up on the *exact* initial orientation. */ + hsBool IMoveTowardsGoal(plArmatureMod *avatar, plAvBrainHuman *brain, double time, hsScalar elapsed); + + /** Okay, we're in the pure cheating mode now. Try to wrap it up; + returns true when it's finally there. */ + bool ITryFinish(plArmatureMod *avatar, plAvBrainHuman *brain, double time, hsScalar elapsed); + + /** Final cheating for position */ + hsBool IFinishPosition(hsPoint3 &newPosition, plArmatureMod *avatar, plAvBrainHuman *brain, + double time, hsScalar elapsed); + + /** Final cheating for rotation */ + hsBool IFinishRotation(hsQuat &newRotation, plArmatureMod *avatar, plAvBrainHuman *brain, + double time, hsScalar elapsed); + + /** If our target's moving, cache its new position and orientation for later math */ + hsBool IUpdateObjective(plArmatureMod *avatar); + + ///////////////////////////////////////////////////////////////////////////////////// + // + // VARS + // + ///////////////////////////////////////////////////////////////////////////////////// + + plSceneObject * fSeekObject; // the seek target.... + hsQuat fSeekRot; // The (current) orientation of our objective + hsPoint3 fSeekPos; // The (current) position of our objective + bool fMovingTarget; // do we check our target's position each frame? + // animation alignment: + // sometimes your seek position depends on a particular animation + // for example, you can say "find a good start point so that you can play this animation + // and have your handle wind up here" i.e: aligntype = "kAlignHandleAnimEnd" + plAvAlignment fAlign; // how to line up with the seek point + const char * fAnimName; // an (optional) anim to use to line up our target + // so you can say "seek to a place where your hand + // will be here after you play animation foo" + + hsPoint3 fPosition; // our current position + hsQuat fRotation; // our current rotation + + // These are set to true once we EVER get close enough to the goal, so that if we fall out + // of range from the anim blend out, we don't later try and correct again, and get in a fun + // little back-and-forth loop. + hsBool fPosGoalHit; + hsBool fRotGoalHit; + + hsBool fStillPositioning; // haven't yet reached the final position + hsBool fStillRotating; // haven't yet reached the final orientation + + hsVector3 fGoalVec; // vec from us to the goal + hsScalar fDistance; // how far to the goal? + hsScalar fAngForward; // 1.0 = goal is forward; -1.0 = goal is backward + hsScalar fAngRight; // 1.0 = goal is directly right; -1.0 = goal is directly left + hsScalar fDistForward; // distance straight forward to goal plane + hsScalar fDistRight; // distance straight right to goal plane + + hsScalar fShuffleRange; + hsScalar fMaxSidleAngle; // in right . goal + hsScalar fMaxSidleRange; // in feet + hsScalar fMinFwdAngle; // in fwd . goal + hsScalar fMaxBackAngle; // in fwd . goal + + double fStartTime; + UInt8 fFlags; + plKey fNotifyFinishedKey; // Send a message to this key when we're done. +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp new file mode 100644 index 00000000..d85ab704 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp @@ -0,0 +1,1828 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsTemplates.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "plGDispatch.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plDrawable/plInstanceDrawInterface.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plPipeResMakeMsg.h" +#include "../pfMessage/plClothingMsg.h" +#include "../plMessage/plRenderMsg.h" +#include "../plGImage/plMipmap.h" +#include "../plPipeline/hsGDeviceRef.h" +#include "../plPipeline/plRenderTarget.h" +#include "plPipeline.h" +#include "plClothingLayout.h" +#include "plAvatarClothing.h" +#include "plClothingSDLModifier.h" +#include "../plGImage/hsCodecManager.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plArmatureEffects.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnMessage/plSDLModifierMsg.h" +#include "../plMessage/plReplaceGeometryMsg.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plSharedMesh.h" +#include "../plDrawable/plMorphSequence.h" +#include "../plDrawable/plMorphSequenceSDLMod.h" +#include "../plDrawable/plSpaceTree.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayer.h" +#include "../plMath/plRandom.h" +#include "../plSDL/plSDL.h" +#include "../plVault/plVault.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plNetClientComm/plNetClientComm.h" + + +plClothingItem::plClothingItem() : fName(nil), fGroup(0), fTileset(0), fType(0), fSortOrder(0), + fDescription(nil), fCustomText(nil), fThumbnail(nil), + fAccessory(nil), fAccessoryName(nil) +{ + int i; + fTextures.Reset(); + fElementNames.Reset(); + fElements.Reset(); + + for (i = 0; i < 3; i++) + { + fDefaultTint1[i] = fDefaultTint2[i] = 255; + } + for (i = 0; i < kMaxNumLODLevels; i++) + fMeshes[i] = nil; +} + +plClothingItem::~plClothingItem() +{ + while (fElementNames.GetCount() > 0) + delete [] fElementNames.Pop(); + + while (fTextures.GetCount() > 0) + delete [] fTextures.Pop(); + + delete [] fName; + delete [] fDescription; + delete [] fCustomText; + delete [] fAccessoryName; +} + +hsBool plClothingItem::CanWearWith(plClothingItem *item) +{ + // For now, you can only wear one shirt, one pair of pants, etc. + // Except accessories, of which you're allowed one per tileset. + return ((item->fType != fType) || + (item->fType == plClothingMgr::kTypeAccessory && + fType == plClothingMgr::kTypeAccessory)); + //&& item->fTileset != fTileset)); +} + +hsBool plClothingItem::WearBefore(plClothingItem *item) +{ + // Accessories come last + // Face comes first + // Other stuff is arbitrary. + + int myPri; + if (fType == plClothingMgr::kTypeFace) + myPri = 0; + else + myPri = (fType == plClothingMgr::kTypeAccessory ? 2 : 1); + + int otherPri; + if (item->fType == plClothingMgr::kTypeFace) + otherPri = 0; + else + otherPri = (item->fType == plClothingMgr::kTypeAccessory ? 2 : 1); + + return myPri < otherPri; +} + +hsBool plClothingItem::HasBaseAlpha() +{ + int i; + for (i = 0; i < fElements.GetCount(); i++) + { + plMipmap *tex = fTextures[i][plClothingElement::kLayerBase]; +// if (tex && (tex->GetFlags() & (plMipmap::kAlphaBitFlag | plMipmap::kAlphaChannelFlag))) + if (tex && (tex->GetFlags() & plMipmap::kAlphaChannelFlag)) + return true; + } + return false; +} + +hsBool plClothingItem::HasSameMeshes(plClothingItem *other) +{ + int i; + for (i = 0; i < kMaxNumLODLevels; i++) + if (fMeshes[i] != other->fMeshes[i]) + return false; + + return true; +} + +void plClothingItem::Read(hsStream *s, hsResMgr *mgr) +{ + hsKeyedObject::Read(s, mgr); + + fName = s->ReadSafeString(); + fGroup = s->ReadByte(); + fType = s->ReadByte(); + fTileset = s->ReadByte(); + fSortOrder = s->ReadByte(); + + fCustomText = s->ReadSafeString(); + fDescription = s->ReadSafeString(); + if (s->ReadBool()) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // thumbnail + + int tileCount = s->ReadSwap32(); + int i, j; + for (i = 0; i < tileCount; i++) + { + fElementNames.Append(s->ReadSafeString()); + + int layerCount = s->ReadByte(); + for (j = 0; j < layerCount; j++) + { + int layer = s->ReadByte(); + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plElementRefMsg(GetKey(), plRefMsg::kOnCreate, i, -1, nil, layer), plRefFlags::kActiveRef); // texture + } + } + + for (i = 0; i < kMaxNumLODLevels; i++) + { + if (s->ReadBool()) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, i, -1), plRefFlags::kActiveRef); // shared mesh + } + + fElements.SetCountAndZero(tileCount); + if (plClothingMgr::GetClothingMgr()) + { + plGenRefMsg *msg = TRACKED_NEW plGenRefMsg(plClothingMgr::GetClothingMgr()->GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(GetKey(), msg, plRefFlags::kActiveRef); + } + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // forced accessory + + for (i = 0; i < 3; i++) + { + fDefaultTint1[i] = s->ReadByte(); + fDefaultTint2[i] = s->ReadByte(); + } +} + +void plClothingItem::Write(hsStream *s, hsResMgr *mgr) +{ + hsKeyedObject::Write(s, mgr); + int i, j; + + s->WriteSafeString(fName); + s->WriteByte(fGroup); + s->WriteByte(fType); + s->WriteByte(fTileset); + s->WriteByte(fSortOrder); + + s->WriteSafeString(fCustomText); + s->WriteSafeString(fDescription); + s->WriteBool(fThumbnail != nil); + if (fThumbnail != nil) + mgr->WriteKey(s, fThumbnail->GetKey()); + + UInt32 texSkip = 0; + for (i = 0; i < fTextures.GetCount(); i++) + if (fTextures[i] == nil) + texSkip++; + + s->WriteSwap32(fTextures.GetCount() - texSkip); + for (i = 0; i < fTextures.GetCount(); i++) + { + if (fTextures[i] == nil) + continue; + + s->WriteSafeString(fElementNames.Get(i)); + + int layerCount = 0; + for (j = 0; j < plClothingElement::kLayerMax; j++) + { + // Run through once to get the count of valid layers + if (fTextures[i][j] != nil) + layerCount++; + } + + s->WriteByte(layerCount); + for (j = 0; j < plClothingElement::kLayerMax; j++) + { + if (fTextures[i][j] != nil) + { + s->WriteByte(j); + mgr->WriteKey(s, fTextures[i][j]->GetKey()); + } + } + + } + + for (i = 0; i < kMaxNumLODLevels; i++) + { + s->WriteBool(fMeshes[i] != nil); + if (fMeshes[i] != nil) + mgr->WriteKey(s, fMeshes[i]->GetKey()); + } + + // EXPORT ONLY + plKey accessoryKey = nil; + if (fAccessoryName) + { + char strBuf[512]; + sprintf(strBuf, "CItm_%s", fAccessoryName); + accessoryKey = plKeyFinder::Instance().StupidSearch("GlobalClothing", nil, plClothingItem::Index(), strBuf); + if (accessoryKey == nil) + { + sprintf(strBuf, "Couldn't find accessory \"%s\". It won't show at runtime.", fAccessoryName); + hsMessageBox(strBuf, GetKeyName(), hsMessageBoxNormal); + } + } + mgr->WriteKey(s, accessoryKey); + + for (i = 0; i < 3; i++) + { + s->WriteByte(fDefaultTint1[i]); + s->WriteByte(fDefaultTint2[i]); + } +} + +hsBool plClothingItem::MsgReceive(plMessage* msg) +{ + plElementRefMsg *eMsg = plElementRefMsg::ConvertNoRef(msg); + if (eMsg) + { + plMipmap *tex = plMipmap::ConvertNoRef(eMsg->GetRef()); + if (tex) + { + if( eMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + if (fTextures.GetCount() <= eMsg->fWhich) + fTextures.ExpandAndZero(eMsg->fWhich + 1); + if (fElementNames.GetCount() <= eMsg->fWhich) + fElementNames.ExpandAndZero(eMsg->fWhich + 1); + + if (fElementNames.Get(eMsg->fWhich) == nil) + fElementNames.Set(eMsg->fWhich, hsStrcpy(eMsg->fElementName)); + if (fTextures.Get(eMsg->fWhich) == nil) + { + plMipmap **layers = TRACKED_NEW plMipmap*[plClothingElement::kLayerMax]; + int i; + for (i = 0; i < plClothingElement::kLayerMax; i++) + layers[i] = nil; + fTextures.Set(eMsg->fWhich, layers); + } + + fTextures.Get(eMsg->fWhich)[eMsg->fLayer] = tex; + } + else if( eMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + fTextures.Get(eMsg->fWhich)[eMsg->fLayer] = nil; + } + return true; + } + } + + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + plSharedMesh *mesh = plSharedMesh::ConvertNoRef(refMsg->GetRef()); + if (mesh) + { + if (refMsg->fWhich >= 0 && refMsg->fWhich < kMaxNumLODLevels) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fMeshes[refMsg->fWhich] = mesh; + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + fMeshes[refMsg->fWhich] = nil; + } + return true; + } + + plMipmap *thumbnail = plMipmap::ConvertNoRef(refMsg->GetRef()); + if (thumbnail) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fThumbnail = thumbnail; + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + fThumbnail = nil; + return true; + } + + plClothingItem *accessory = plClothingItem::ConvertNoRef(refMsg->GetRef()); + if (accessory) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fAccessory = accessory; + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + fAccessory = nil; + return true; + } + } + + return hsKeyedObject::MsgReceive(msg); +} + +///////////////////////////////////////////////////////////////////////////// + +hsBool plClosetItem::IsMatch(plClosetItem *other) +{ + return (fItem == other->fItem && fOptions.IsMatch(&other->fOptions)); +} + +///////////////////////////////////////////////////////////////////////////// + +plClothingBase::plClothingBase() : fName(nil), fBaseTexture(nil), fLayoutName(nil) {} + +plClothingBase::~plClothingBase() +{ + delete [] fName; + delete [] fLayoutName; +} + +void plClothingBase::Read(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Read(s, mgr); + + fName = s->ReadSafeString(); + if (s->ReadBool()) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); + fLayoutName = s->ReadSafeString(); +} + +void plClothingBase::Write(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Write(s, mgr); + + s->WriteSafeString(fName); + s->WriteBool(fBaseTexture != nil); + if (fBaseTexture != nil) + mgr->WriteKey(s, fBaseTexture->GetKey()); + s->WriteSafeString(fLayoutName); +} + +hsBool plClothingBase::MsgReceive(plMessage* msg) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fBaseTexture = plMipmap::ConvertNoRef(refMsg->GetRef()); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + fBaseTexture = nil; + } + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + + +///////////////////////////////////////////////////////////////////////////// + +plClothingOutfit::plClothingOutfit() : + fTargetLayer(nil), fBase(nil), fGroup(0), fAvatar(nil), fSynchClients(false), fMaterial(nil), fVaultSaveEnabled(true), fMorphsInitDone(false) +{ + fSkinTint.Set(1.f, 0.84, 0.71, 1.f); + fItems.Reset(); + int i; + for (i = 0; i < plClothingLayout::kMaxTileset; i++) + fDirtyItems.SetBit(i); + + for (i = 0; i < plClothingElement::kLayerSkinLast - plClothingElement::kLayerSkinFirst; i++) + fSkinBlends[i] = 0.f; +} + +plClothingOutfit::~plClothingOutfit() +{ + fItems.Reset(); + while (fOptions.GetCount() > 0) + delete fOptions.Pop(); + plgDispatch::Dispatch()->UnRegisterForExactType(plPreResourceMsg::Index(), GetKey()); +} + +void plClothingOutfit::AddItem(plClothingItem *item, hsBool update /* = true */, hsBool broadcast /* = true */, hsBool netForce /* = false */) +{ + if (fItems.Find(item) != fItems.kMissingIndex) + return; + + plClothingMsg *msg = TRACKED_NEW plClothingMsg(); + msg->AddReceiver(GetKey()); + msg->AddCommand(plClothingMsg::kAddItem); + msg->fItemKey = item->GetKey(); + if (broadcast) + msg->SetBCastFlag(plMessage::kNetPropagate); + if ( netForce ) + { + // also doesn't make much sense to not net propagate netForced messages + msg->SetBCastFlag(plMessage::kNetPropagate); + msg->SetBCastFlag(plMessage::kNetForce); + } + + if (update) + msg->AddCommand(plClothingMsg::kUpdateTexture); + + plgDispatch::MsgSend(msg); +} + +void plClothingOutfit::ForceUpdate(bool retry) +{ + plClothingMsg *msg = TRACKED_NEW plClothingMsg(); + msg->AddCommand(plClothingMsg::kUpdateTexture); + if (retry) + msg->AddCommand(plClothingMsg::kRetry); // force a resend + msg->Send(GetKey()); +} + +void plClothingOutfit::RemoveItem(plClothingItem *item, hsBool update /* = true */, hsBool netForce /* = false */) +{ + if (fItems.Find(item) == fItems.kMissingIndex) + return; + + plClothingMsg *msg = TRACKED_NEW plClothingMsg(); + msg->AddReceiver(GetKey()); + msg->AddCommand(plClothingMsg::kRemoveItem); + msg->SetBCastFlag(plMessage::kNetPropagate); + if ( netForce ) + msg->SetBCastFlag(plMessage::kNetForce); + msg->fItemKey = item->GetKey(); + if (update) + msg->AddCommand(plClothingMsg::kUpdateTexture); + + plgDispatch::MsgSend(msg); +} + +void plClothingOutfit::TintItem(plClothingItem *item, hsScalar red, hsScalar green, hsScalar blue, + hsBool update /* = true */, hsBool broadcast /* = true */, hsBool netForce /* = false */, + hsBool retry /* = true */, UInt8 layer /* = kLayerTint1 */) +{ + plClothingMsg *msg = TRACKED_NEW plClothingMsg(); + msg->AddReceiver(GetKey()); + msg->AddCommand(plClothingMsg::kTintItem); + msg->fItemKey = item->GetKey(); + msg->fColor.Set(red, green, blue, 1.f); + msg->fLayer = layer; + if (broadcast) + msg->SetBCastFlag(plMessage::kNetPropagate); + if (netForce) + { + msg->SetBCastFlag(plMessage::kNetPropagate); + msg->SetBCastFlag(plMessage::kNetForce); + } + if (update) + msg->AddCommand(plClothingMsg::kUpdateTexture); + if (retry) + msg->AddCommand(plClothingMsg::kRetry); + plgDispatch::MsgSend(msg); +} + +void plClothingOutfit::TintSkin(hsScalar red, hsScalar green, hsScalar blue, + hsBool update /* = true */, hsBool broadcast /* = true */) +{ + plClothingMsg *msg = TRACKED_NEW plClothingMsg(); + msg->AddReceiver(GetKey()); + msg->AddCommand(plClothingMsg::kTintSkin); + msg->fColor.Set(red, green, blue, 1.f); + if (broadcast) + msg->SetBCastFlag(plMessage::kNetPropagate); + if (update) + msg->AddCommand(plClothingMsg::kUpdateTexture); + + plgDispatch::MsgSend(msg); +} + +void plClothingOutfit::MorphItem(plClothingItem *item, UInt8 layer, UInt8 delta, hsScalar weight, + hsBool retry /* = true */) +{ + plClothingMsg *msg = TRACKED_NEW plClothingMsg(); + msg->AddReceiver(GetKey()); + msg->AddCommand(plClothingMsg::kMorphItem); + msg->fItemKey = item->GetKey(); + msg->fLayer = layer; + msg->fDelta = delta; + msg->fWeight = weight; + if (retry) + msg->AddCommand(plClothingMsg::kRetry); + plgDispatch::MsgSend(msg); +} + +void plClothingOutfit::SetAge(hsScalar age, hsBool update /* = true */, hsBool broadcast /* = true */) +{ + SetSkinBlend(age, plClothingElement::kLayerSkinBlend1, update, broadcast); +} + +void plClothingOutfit::SetSkinBlend(hsScalar blend, UInt8 layer, hsBool update /* = true */, hsBool broadcast /* = true */) +{ + plClothingMsg *msg = TRACKED_NEW plClothingMsg(); + msg->AddReceiver(GetKey()); + msg->AddCommand(plClothingMsg::kBlendSkin); + msg->fLayer = layer; + msg->fColor.Set(1.f, 1.f, 1.f, blend); + if (broadcast) + msg->SetBCastFlag(plMessage::kNetPropagate); + if (update) + msg->AddCommand(plClothingMsg::kUpdateTexture); + + plgDispatch::MsgSend(msg); +} + +hsScalar plClothingOutfit::GetSkinBlend(UInt8 layer) +{ + if (layer >= plClothingElement::kLayerSkinBlend1 && layer <= plClothingElement::kLayerSkinLast) + return fSkinBlends[layer - plClothingElement::kLayerSkinBlend1]; + + return 0; +} + +void plClothingOutfit::IAddItem(plClothingItem *item) +{ + if (item->fGroup != fGroup) + { + // Trying to wear clothing from the wrong group, remove the ref. + GetKey()->Release(item->GetKey()); + return; + } + + if (fItems.Find(item) == fItems.kMissingIndex) + { + // Remove any other item we have that can't be worn with this + int i; + for (i = fItems.GetCount() - 1; i >= 0; i--) + { + if (!item->CanWearWith(fItems[i])) + { + plClothingItem *goner = fItems[i]; + if (goner->fAccessory) + { + GetKey()->Release(goner->fAccessory->GetKey()); + IRemoveItem(goner->fAccessory); + } + GetKey()->Release(goner->GetKey()); + IRemoveItem(goner); // Can't wait for the ref message to process + } + } + + for (i = 0; i < fItems.GetCount(); i++) + { + if (item->WearBefore(fItems[i])) + break; + } + fItems.Insert(i, item); + plClothingItemOptions *op = TRACKED_NEW plClothingItemOptions; + fOptions.Insert(i, op); + IInstanceSharedMeshes(item); + fDirtyItems.SetBit(item->fTileset); + + // A bit of hackage for bare feet sound effects. + if (item->fType == plClothingMgr::kTypeLeftFoot) + { + plArmatureEffectsMgr *mgr = fAvatar->GetArmatureEffects(); + plArmatureEffectFootSound *soundEffect = nil; + int num = mgr->GetNumEffects(); + int i; + + for (i = 0; i < num; i++) + { + if (soundEffect = plArmatureEffectFootSound::ConvertNoRef(mgr->GetEffect(i))) + break; + } + + if (soundEffect) + { + if (!strcmp(item->fName, "03_MLFoot04_01") || !strcmp(item->fName, "03_FLFoot04_01")) + soundEffect->SetFootType(plArmatureEffectFootSound::kFootTypeBare); + else + soundEffect->SetFootType(plArmatureEffectFootSound::kFootTypeShoe); + } + } + } +} + +void plClothingOutfit::IRemoveItem(plClothingItem *item) +{ + // We may just be removing the ref... + UInt32 index = fItems.Find(item); + if (index != fItems.kMissingIndex) + { + fItems.Remove(index); + delete fOptions.Get(index); + fOptions.Remove(index); + IRemoveSharedMeshes(item); + fDirtyItems.SetBit(item->fTileset); + } +} + +hsBool plClothingOutfit::ITintItem(plClothingItem *item, hsColorRGBA color, UInt8 layer) +{ + UInt32 index = fItems.Find(item); + if (index != fItems.kMissingIndex) + { + if (layer == plClothingElement::kLayerTint1) + fOptions[index]->fTint1 = color; + if (layer == plClothingElement::kLayerTint2) + fOptions[index]->fTint2 = color; + fDirtyItems.SetBit(item->fTileset); + + if (fItems[index]->fAccessory) + { + plClothingItem *acc = fItems[index]->fAccessory; + UInt32 accIndex = fItems.Find(acc); + if (accIndex != fItems.kMissingIndex) + { + if (layer == plClothingElement::kLayerTint1) + fOptions[accIndex]->fTint1 = color; + if (layer == plClothingElement::kLayerTint2) + fOptions[accIndex]->fTint2 = color; + fDirtyItems.SetBit(acc->fTileset); + } + } + return true; + } + + return false; +} + +hsColorRGBA plClothingOutfit::GetItemTint(plClothingItem *item, UInt8 layer /* = kLayerTint1 */) const +{ + if (layer >= plClothingElement::kLayerSkinFirst && + layer <= plClothingElement::kLayerSkinLast) + return fSkinTint; + + UInt32 index = fItems.Find(item); + if (index != fItems.kMissingIndex) + { + if (layer == plClothingElement::kLayerTint1) + return fOptions[index]->fTint1; + if (layer == plClothingElement::kLayerTint2) + return fOptions[index]->fTint2; + } + + hsColorRGBA color; + color.Set(1.f, 1.f, 1.f, 1.f); + return color; +} + +hsBool plClothingOutfit::IMorphItem(plClothingItem *item, UInt8 layer, UInt8 delta, hsScalar weight) +{ + UInt32 index = fItems.Find(item); + if (index != fItems.kMissingIndex) + { + int i; + for (i = 0; i < fAvatar->GetNumLOD(); i++) + { + if (item->fMeshes[i]->fMorphSet == nil) + continue; + + const plSceneObject *so = fAvatar->GetClothingSO(i); + if (!so) + continue; + + plMorphSequence *seq = const_cast(plMorphSequence::ConvertNoRef(so->GetModifierByType(plMorphSequence::Index()))); + plKey meshKey = item->fMeshes[i]->GetKey(); + + // Lower LOD objects don't have all the morphs, so check if this one is in range. + if (seq && layer < seq->GetNumLayers(meshKey) && delta < seq->GetNumDeltas(layer, meshKey)) + seq->SetWeight(layer, delta, weight, meshKey); + } + return true; + } + + return false; +} + +void plClothingOutfit::Read(hsStream* s, hsResMgr* mgr) +{ + plSynchedObject::Read(s, mgr); + + fGroup = s->ReadByte(); + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // plClothingBase + + if (fGroup != plClothingMgr::kClothingBaseNoOptions) + { + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // target layer + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // material + } + plgDispatch::Dispatch()->RegisterForExactType(plPreResourceMsg::Index(), GetKey()); + //ReadItems(s, mgr, false); +} + +void plClothingOutfit::Write(hsStream* s, hsResMgr* mgr) +{ + plSynchedObject::Write(s, mgr); + + s->WriteByte(fGroup); + mgr->WriteKey(s, fBase->GetKey()); + + if (fGroup != plClothingMgr::kClothingBaseNoOptions) + { + mgr->WriteKey(s, fTargetLayer->GetKey()); + mgr->WriteKey(s, fMaterial->GetKey()); + } + //WriteItems(s, mgr); +} + +void plClothingOutfit::StripAccessories() +{ + int i; + for (i = fItems.GetCount() - 1; i >= 0; i--) + { + if (fItems[i]->fType == plClothingMgr::kTypeAccessory) + { + GetKey()->Release(fItems[i]->GetKey()); + IRemoveItem(fItems[i]); + } + } +} + +void plClothingOutfit::IHandleMorphSDR(plStateDataRecord *sdr) +{ + plSimpleStateVariable *lodVar = sdr->FindVar(plMorphSequenceSDLMod::kStrTargetID); + if (!lodVar) + return; + + UInt8 lod; + lodVar->Get(&lod); + + const plSceneObject *so = fAvatar->GetClothingSO(lod); + if (!so) + return; + + plMorphSequenceSDLMod *morph = const_cast(plMorphSequenceSDLMod::ConvertNoRef(so->GetModifierByType(plMorphSequenceSDLMod::Index()))); + if (morph) + morph->SetCurrentStateFrom(sdr); +} + +void plClothingOutfit::ReadFromVault() +{ + SetupMorphSDL(); + + WearDefaultClothing(); + + RelVaultNode * rvn; + if (nil == (rvn = VaultGetAvatarOutfitFolderIncRef())) + return; + + ARRAY(RelVaultNode*) nodes; + rvn->GetChildNodesIncRef(plVault::kNodeType_SDL, 1, &nodes); + + for (unsigned i = 0; i < nodes.Count(); ++i) { + VaultSDLNode sdl(nodes[i]); + if (sdl.sdlDataLen) { + hsRAMStream ram; + ram.Write(sdl.sdlDataLen, sdl.sdlData); + ram.Rewind(); + + char * sdlRecName = nil; + int sdlRecVersion; + plStateDataRecord::ReadStreamHeader(&ram, &sdlRecName, &sdlRecVersion); + plStateDescriptor * desc = plSDLMgr::GetInstance()->FindDescriptor(sdlRecName, sdlRecVersion); + if (desc) { + plStateDataRecord * sdlDataRec = TRACKED_NEW plStateDataRecord(desc); + if (sdlDataRec->Read(&ram, 0)) { + if (!strcmp(sdlRecName, kSDLMorphSequence)) + IHandleMorphSDR(sdlDataRec); + else + plClothingSDLModifier::HandleSingleSDR(sdlDataRec, this); + } + delete sdlDataRec; + } + delete [] sdlRecName; + } + nodes[i]->DecRef(); + } + + fSynchClients = true; // set true if the next synch should be bcast + ForceUpdate(true); + + rvn->DecRef(); +} + +void plClothingOutfit::SaveCustomizations(hsBool retry /* = true */) +{ + plClothingMsg *msg = TRACKED_NEW plClothingMsg(); + msg->AddReceiver(GetKey()); + msg->AddCommand(plClothingMsg::kSaveCustomizations); + if (retry) + msg->AddCommand(plClothingMsg::kRetry); + + msg->Send(); +} + +void plClothingOutfit::WriteToVault() +{ + if (!fVaultSaveEnabled) + return; + + RelVaultNode * rvn; + if (nil == (rvn = VaultGetAvatarOutfitFolderIncRef())) + return; + + ARRAY(plStateDataRecord*) SDRs; + + plStateDataRecord clothingSDR(kSDLClothing); + fAvatar->GetClothingSDLMod()->PutCurrentStateIn(&clothingSDR); + plSDStateVariable * clothesStateDesc = clothingSDR.FindSDVar(plClothingSDLModifier::kStrWardrobe); + + for (unsigned i = 0; i < clothesStateDesc->GetCount(); ++i) + SDRs.Add(clothesStateDesc->GetStateDataRecord(i)); + + plSDStateVariable * appearanceStateDesc = clothingSDR.FindSDVar(plClothingSDLModifier::kStrAppearance); // for skin tint + SDRs.Add(appearanceStateDesc->GetStateDataRecord(0)); + + WriteToVault(SDRs); + rvn->DecRef(); +} + +void plClothingOutfit::WriteToVault(const ARRAY(plStateDataRecord*) & SDRs) +{ + // We'll hit this case when the server asks us to save state for NPCs. + if (fAvatar->GetTarget(0) != plNetClientApp::GetInstance()->GetLocalPlayer()) + return; + + RelVaultNode * rvn; + if (nil == (rvn = VaultGetAvatarOutfitFolderIncRef())) + return; + + ARRAY(plStateDataRecord*) morphs; + + // Gather morph SDRs + hsTArray morphsSDRs; + plMorphSequence::FindMorphMods(fAvatar->GetTarget(0), morphsSDRs); + for (unsigned i = 0; i < morphsSDRs.GetCount(); ++i) { + for (unsigned j = 0; j < fAvatar->GetNumLOD(); j++) { + if (fAvatar->GetClothingSO(j) == morphsSDRs[i]->GetTarget(0)) { + plStateDataRecord * morphSDR = TRACKED_NEW plStateDataRecord(kSDLMorphSequence); + plSimpleStateVariable * lodVar = morphSDR->FindVar(plMorphSequenceSDLMod::kStrTargetID); + if (lodVar) + lodVar->Set((int)j); + + morphsSDRs[i]->GetSDLMod()->PutCurrentStateIn(morphSDR); + morphs.Add(morphSDR); + } + } + } + + ARRAY(RelVaultNode*) templates; + ARRAY(RelVaultNode*) actuals; + ARRAY(RelVaultNode*) nodes; + + // Get all existing clothing SDRs + rvn->GetChildNodesIncRef(plVault::kNodeType_SDL, 1, &nodes); // REF: Find + + const ARRAY(plStateDataRecord*) * arrs[] = { + &SDRs, + &morphs, + }; + for (unsigned arrIdx = 0; arrIdx < arrsize(arrs); ++arrIdx) { + const ARRAY(plStateDataRecord*) * arr = arrs[arrIdx]; + + // Write all SDL to to the outfit folder, reusing existing nodes and creating new ones as necessary + for (unsigned i = 0; i < arr->Count(); ++i) { + RelVaultNode * node; + if (nodes.Count()) { + node = nodes[0]; + nodes.DeleteUnordered(0); + node->IncRef(); // REF: Work + node->DecRef(); // REF: Find + } + else { + RelVaultNode * templateNode = NEWZERO(RelVaultNode); + templateNode->SetNodeType(plVault::kNodeType_SDL); + templates.Add(templateNode); + node = templateNode; + node->IncRef(); // REF: Create + node->IncRef(); // REF: Work + } + + VaultSDLNode sdl(node); + sdl.SetStateDataRecord((*arr)[i], 0); + node->DecRef(); // REF: Work + } + } + + // Delete any leftover nodes + { for (unsigned i = 0; i < nodes.Count(); ++i) { + VaultDeleteNode(nodes[i]->nodeId); + nodes[i]->DecRef(); // REF: Array + }} + + // Create actual new nodes from their templates + { for (unsigned i = 0; i < templates.Count(); ++i) { + ENetError result; + if (RelVaultNode * actual = VaultCreateNodeAndWaitIncRef(templates[i], &result)) { + actuals.Add(actual); + } + templates[i]->DecRef(); // REF: Create + }} + + // Add new nodes to outfit folder + { for (unsigned i = 0; i < actuals.Count(); ++i) { + VaultAddChildNodeAndWait(rvn->nodeId, actuals[i]->nodeId, NetCommGetPlayer()->playerInt); + actuals[i]->DecRef(); // REF: Create + }} + + // Cleanup morph SDRs + {for (unsigned i = 0; i < morphs.Count(); ++i) { + DEL(morphs[i]); + }} + + rvn->DecRef(); +} + +// XXX HACK. DON'T USE (this function exists for the temp console command Clothing.SwapClothTexHACK) +void plClothingOutfit::DirtyTileset(int tileset) +{ + fDirtyItems.SetBit(tileset); + ForceUpdate(true); +} + +void plClothingOutfit::IUpdate() +{ + //GenerateTexture(); + fAvatar->RefreshTree(); + DirtySynchState(kSDLClothing, 0); + + plArmatureMod * avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + hsAssert(avMod,"plArmatureMod not found for local player"); + if (avMod->GetClothingOutfit()==this) + { + // Let the GUI know we changed clothes + plClothingUpdateBCMsg *BCMsg = TRACKED_NEW plClothingUpdateBCMsg(); + BCMsg->SetSender(GetKey()); + plgDispatch::MsgSend(BCMsg); + } +} + +void plClothingOutfit::WearDefaultClothing() +{ + StripAccessories(); + + plClothingMgr *cMgr = plClothingMgr::GetClothingMgr(); + hsTArrayitems; + cMgr->GetItemsByGroup(fGroup, items); + + // Wear one thing of each type + UInt32 i, j; + for (i = 0; i < plClothingMgr::kMaxType; i++) + { + if (i == plClothingMgr::kTypeAccessory) + continue; + + for (j = 0; j < items.GetCount(); j++) + { + if (items[j]->fType == i) + { + AddItem(items[j], false, false); + if (i == plClothingMgr::kTypeHair || i == plClothingMgr::kTypeFace) + { + // Hair tint color + TintItem(items[j], 0.5, 0.3, 0.2, false, false); + } + else + { + TintItem(items[j], items[j]->fDefaultTint1[0] / 255.f, items[j]->fDefaultTint1[1] / 255.f, + items[j]->fDefaultTint1[2] / 255.f, false, false); + } + + // Everyone can tint layer 2. Go nuts! + TintItem(items[j], items[j]->fDefaultTint2[0] / 255.f, items[j]->fDefaultTint2[1] / 255.f, + items[j]->fDefaultTint2[2] / 255.f, false, false, false, true, plClothingElement::kLayerTint2); + break; + } + } + } +} + +void plClothingOutfit::WearDefaultClothingType(UInt32 clothingType) +{ + plClothingMgr *cMgr = plClothingMgr::GetClothingMgr(); + hsTArray items; + cMgr->GetItemsByGroup(fGroup, items); + + UInt32 i; + for (i=0; ifType == clothingType) + { + AddItem(items[i], false, false); + if (clothingType == plClothingMgr::kTypeHair || clothingType == plClothingMgr::kTypeFace) + { + // Hair tint color + TintItem(items[i], 0.5, 0.3, 0.2, false, false); + } + else + { + TintItem(items[i], items[i]->fDefaultTint1[0] / 255.f, items[i]->fDefaultTint1[1] / 255.f, + items[i]->fDefaultTint1[2] / 255.f, false, false); + } + + // Everyone can tint layer 2. Go nuts! + TintItem(items[i], items[i]->fDefaultTint2[0] / 255.f, items[i]->fDefaultTint2[1] / 255.f, + items[i]->fDefaultTint2[2] / 255.f, false, false, false, true, plClothingElement::kLayerTint2); + break; + } + } +} + +void plClothingOutfit::WearMaintainerOutfit() +{ + fVaultSaveEnabled = false; + + WearDefaultClothing(); + plClothingItem *item; + + if (fGroup == plClothingMgr::kClothingBaseMale) + { + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_MHAcc_SuitHelmet"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_MLHand_Suit"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_MRHand_Suit"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_MTorso_Suit"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_MLegs_Suit"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_MLFoot_Suit"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_MRFoot_Suit"); + if (item) + AddItem(item, false, false); + } + else if (fGroup == plClothingMgr::kClothingBaseFemale) + { + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_FHair_SuitHelmet"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_FLHand_Suit"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_FRHand_Suit"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_FTorso_Suit"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_FLegs_Suit"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_FLFoot_Suit"); + if (item) + AddItem(item, false, false); + item = plClothingMgr::GetClothingMgr()->FindItemByName("03_FRFoot_Suit"); + if (item) + AddItem(item, false, false); + } + + fSynchClients = true; + ForceUpdate(true); +} + +void plClothingOutfit::RemoveMaintainerOutfit() +{ + ReadFromVault(); + + fVaultSaveEnabled = true; +} + +static plRandom sRandom; + +void plClothingOutfit::WearRandomOutfit() +{ + plClothingMgr *cMgr = plClothingMgr::GetClothingMgr(); + hsTArrayitems; + + // Wear one thing of each type + UInt32 i, j; + for (i = 0; i < plClothingMgr::kMaxType; i++) + { + if (i == plClothingMgr::kTypeAccessory) + continue; + + items.Reset(); + cMgr->GetItemsByGroupAndType(fGroup, (UInt8)i, items); + j = (UInt32)(sRandom.RandZeroToOne() * items.GetCount()); + + hsScalar r1 = sRandom.RandZeroToOne(); + hsScalar g1 = sRandom.RandZeroToOne(); + hsScalar b1 = sRandom.RandZeroToOne(); + hsScalar r2 = sRandom.RandZeroToOne(); + hsScalar g2 = sRandom.RandZeroToOne(); + hsScalar b2 = sRandom.RandZeroToOne(); + + AddItem(items[j], false, false); + TintItem(items[j], r1, g1, b1, false, false, false, true, 1); + TintItem(items[j], r2, g2, b2, false, false, false, true, 2); + + plClothingItem *match = cMgr->GetLRMatch(items[j]); + if (match) + { + AddItem(match, false, false); + TintItem(match, r1, g1, b1, false, false, false, true, 1); + TintItem(match, r2, g2, b2, false, false, false, true, 2); + } + } + TintSkin(sRandom.RandZeroToOne(), sRandom.RandZeroToOne(), sRandom.RandZeroToOne()); +} + +hsBool plClothingOutfit::ReadItems(hsStream* s, hsResMgr* mgr, hsBool broadcast /* = true */) +{ + hsBool result = true; + UInt32 numItems = s->ReadSwap32(); + int i; + for (i = 0; i < numItems; i++) + { + plKey key = mgr->ReadKey( s ); + hsColorRGBA color; + hsColorRGBA color2; + color.Read(s); + color2.Read(s); + + // Make sure to read everything in before hitting this and possibly skipping to + // the next item, lest we disrupt the stream. + if( key == nil ) + { + hsAssert( false, "Nil item in plClothingOutfit::ReadItems(). The vault probably contains a key with a plLocation that's moved since then. Tsk, tsk." ); + result = false; + continue; + } + + plClothingItem *item = plClothingItem::ConvertNoRef( key->GetObjectPtr() ); + AddItem(item, false, broadcast); + TintItem(item, color.r, color.g, color.b, false, broadcast, false, true, plClothingElement::kLayerTint1); + TintItem(item, color2.r, color2.g, color2.b, false, broadcast, false, true, plClothingElement::kLayerTint2); + } + + return result; +} + +void plClothingOutfit::WriteItems(hsStream *s, hsResMgr *mgr) +{ + s->WriteSwap32(fItems.GetCount()); + int i; + for (i = 0; i < fItems.GetCount(); i++) + { + mgr->WriteKey(s, fItems.Get(i)->GetKey()); + fOptions.Get(i)->fTint1.Write(s); + fOptions.Get(i)->fTint2.Write(s); + } +} + +hsBool plClothingOutfit::MsgReceive(plMessage* msg) +{ + plPreResourceMsg *preMsg = plPreResourceMsg::ConvertNoRef(msg); + if (preMsg) + { + if (fAvatar && fGroup != plClothingMgr::kClothingBaseNoOptions) + { + plDrawable *spans = fAvatar->FindDrawable(); + const hsBounds3Ext &bnds = spans->GetWorldBounds(); + if (bnds.GetType() == kBoundsNormal) + { + // This is a bit hacky... The drawable code has just run through and updated + // each span's bounds (see plDrawableSpans::IUpdateMatrixPaletteBoundsHack()) + // but not the world bounds for the entire drawable. So we tell the space tree + // to refresh. However, the pageTreeMgr would then get confused because the + // space tree is no longer dirty (see plPageTreeMgr::IRefreshTree()), + // causing the avatar to only draw if the origin is in view. + // So we just force it dirty, and everyone's happy. + spans->GetSpaceTree()->Refresh(); + spans->GetSpaceTree()->MakeDirty(); + + // Where were we? Oh yeah... if this avatar is in view it needs a texture. Tell + // the pipeline. + if (preMsg->Pipeline()->TestVisibleWorld(spans->GetSpaceTree()->GetWorldBounds())) + preMsg->Pipeline()->SubmitClothingOutfit(this); + } + } + } + + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + plLayer *layer = plLayer::ConvertNoRef(refMsg->GetRef()); + if (layer) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fTargetLayer = layer; + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + fTargetLayer = nil; + + return true; + } + plClothingItem *item = plClothingItem::ConvertNoRef(refMsg->GetRef()); + if (item) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest) ) + { + IAddItem(item); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + IRemoveItem(item); + } + return true; + } + plClothingBase *base = plClothingBase::ConvertNoRef(refMsg->GetRef()); + if (base) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fBase = base; + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + fBase = nil; + + return true; + } + + hsGMaterial *mat = hsGMaterial::ConvertNoRef(refMsg->GetRef()); + if (mat) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fMaterial = mat; + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + fMaterial = nil; + } + } + + // For now: Adding an item fires off a notify, and the item won't get added until the ref message + // is processed, BUT we need to make sure that task doesn't require any more messages (like the + // trickling of chains of AddViaNotifies), so that other messages received after it (like tint + // commands) just have to check a kRetry flag, resend the message, and be confident that the item + // will be present by the time their resent message is handled. + // + // Should we ever handle AddViaNotify in a separate thread, this will blow up. (Ok, we'll just have + // bugs about the characters not having the right clothings options visible. No explosions.) + + plClothingMsg *cMsg = plClothingMsg::ConvertNoRef(msg); + if (cMsg) + { + if (cMsg->GetCommand(plClothingMsg::kAddItem)) + { + plGenRefMsg *msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(cMsg->fItemKey, msg, plRefFlags::kActiveRef); + plClothingItem *accessory = plClothingItem::ConvertNoRef(cMsg->fItemKey->GetObjectPtr())->fAccessory; + if (accessory) + { + plGenRefMsg *msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(accessory->GetKey(), msg, plRefFlags::kActiveRef); + } + } + if (cMsg->GetCommand(plClothingMsg::kRemoveItem)) + { + plClothingItem *item = plClothingItem::ConvertNoRef(cMsg->fItemKey->GetObjectPtr()); + if (item && item->fType == plClothingMgr::kTypeAccessory) + GetKey()->Release(cMsg->fItemKey); + } + if (cMsg->GetCommand(plClothingMsg::kTintItem)) + { + if (!ITintItem(plClothingItem::ConvertNoRef(cMsg->fItemKey->GetObjectPtr()), cMsg->fColor, cMsg->fLayer) && + cMsg->GetCommand(plClothingMsg::kRetry)) + { + // We failed to tint because we're not yet wearing the item. + // However, the kRetry flag is set, so we fire another tint command off + + TintItem(plClothingItem::ConvertNoRef(cMsg->fItemKey->GetObjectPtr()), + cMsg->fColor.r, cMsg->fColor.g, cMsg->fColor.b, + cMsg->GetCommand(plClothingMsg::kUpdateTexture), false, false, false, cMsg->fLayer); + return true; + } + } + if (cMsg->GetCommand(plClothingMsg::kMorphItem)) + { + if (cMsg->GetCommand(plClothingMsg::kRetry)) + MorphItem(plClothingItem::ConvertNoRef(cMsg->fItemKey->GetObjectPtr()), cMsg->fLayer, cMsg->fDelta, cMsg->fWeight, false); + else + IMorphItem(plClothingItem::ConvertNoRef(cMsg->fItemKey->GetObjectPtr()), cMsg->fLayer, cMsg->fDelta, cMsg->fWeight); + } + + if (cMsg->GetCommand(plClothingMsg::kTintSkin)) + { + fSkinTint = cMsg->fColor; + int i, j; + for (i = 0; i < fItems.GetCount(); i++) + for (j = 0; j < fItems[i]->fElements.GetCount(); j++) + if (fItems[i]->fTextures[j][plClothingElement::kLayerSkin] != nil) + fDirtyItems.SetBit(fItems[i]->fTileset); + } + + if (cMsg->GetCommand(plClothingMsg::kBlendSkin)) + { + hsScalar blend = cMsg->fColor.a; + if (blend > 1.f) + blend = 1.f; + if (blend < 0.f) + blend = 0.f; + if (cMsg->fLayer >= plClothingElement::kLayerSkinBlend1 && + cMsg->fLayer <= plClothingElement::kLayerSkinBlend6) + { + fSkinBlends[cMsg->fLayer - plClothingElement::kLayerSkinBlend1] = blend; + + int i, j; + for (i = 0; i < fItems.GetCount(); i++) + for (j = 0; j < fItems[i]->fElements.GetCount(); j++) + if (fItems[i]->fTextures[j][cMsg->fLayer] != nil) + fDirtyItems.SetBit(fItems[i]->fTileset); + } + } + if (cMsg->GetCommand(plClothingMsg::kSaveCustomizations)) + { + if (cMsg->GetCommand(plClothingMsg::kRetry)) + SaveCustomizations(false); + else + WriteToVault(); + } + + // Make sure to check for an update last + if (cMsg->GetCommand(plClothingMsg::kUpdateTexture)) + { + // If kUpdateTexture was not the only command, we need to resend the update + // as a solo command, so that it happens after any other AddViaNotify messages + if (cMsg->ResendUpdate()) + { + plClothingMsg *update = TRACKED_NEW plClothingMsg(); + update->AddReceiver(GetKey()); + update->AddCommand(plClothingMsg::kUpdateTexture); + plgDispatch::MsgSend(update); + } + else + IUpdate(); + } + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + +// +// TESTING SDL +// Send clothing sendState msg to object's plClothingSDLModifier +// +hsBool plClothingOutfit::DirtySynchState(const char* SDLStateName, UInt32 synchFlags) +{ + plSynchEnabler ps(true); // make sure synching is enabled, since this happens during load + synchFlags |= plSynchedObject::kForceFullSend; // TEMP + if (fSynchClients) + synchFlags |= plSynchedObject::kBCastToClients; + fSynchClients=false; + hsAssert(fAvatar, "nil fAvatar"); + return fAvatar->GetTarget(0)->DirtySynchState(SDLStateName, synchFlags); +} + +// Note: Currently the word "instance" is a lie. We just copy. In the future +// we'll be good about this, but I wanted to get it working first. +void plClothingOutfit::IInstanceSharedMeshes(plClothingItem *item) +{ + if (fAvatar) + fAvatar->ValidateMesh(); + + hsBool partialSort = item->fCustomText && strstr(item->fCustomText, "NeedsSort"); + int i; + for (i = 0; i < plClothingItem::kMaxNumLODLevels; i++) + { + const plSceneObject *so = fAvatar->GetClothingSO(i); + if (so != nil && item->fMeshes[i] != nil) + { + plInstanceDrawInterface *idi = const_cast(plInstanceDrawInterface::ConvertNoRef(so->GetDrawInterface())); + if (idi) + idi->AddSharedMesh(item->fMeshes[i], fMaterial, !item->HasBaseAlpha(), i, partialSort); + } + } +} + +void plClothingOutfit::IRemoveSharedMeshes(plClothingItem *item) +{ + if (fAvatar == nil) + return; + + int i; + for (i = 0; i < plClothingItem::kMaxNumLODLevels; i++) + { + const plSceneObject *so = fAvatar->GetClothingSO(i); + if (so != nil && item->fMeshes[i] != nil) + { + plInstanceDrawInterface *idi = const_cast(plInstanceDrawInterface::ConvertNoRef(so->GetDrawInterface())); + if (idi) + idi->RemoveSharedMesh(item->fMeshes[i]); + } + } +} + +void plClothingOutfit::SetupMorphSDL() +{ + if (!fMorphsInitDone) + { + hsTArray morphs; + plMorphSequence::FindMorphMods(fAvatar->GetTarget(0), morphs); + for (unsigned i = 0; i < morphs.GetCount(); ++i) + { + for (unsigned j = 0; j < fAvatar->GetNumLOD(); j++) + { + if (fAvatar->GetClothingSO(j) == morphs[i]->GetTarget(0)) + { + plMorphSequenceSDLMod* morph = morphs[i]->GetSDLMod(); + if (morph) + morph->SetIsAvatar(true); + } + } + } + + fMorphsInitDone = true; + } +} + + +///////////////////////////////////////////////////////////////////////////// + +const char *plClothingMgr::GroupStrings[] = +{ + "Male Clothing", + "Female Clothing", + "(No Clothing Options)" +}; + +const char *plClothingMgr::TypeStrings[] = +{ + "Pants", + "Shirt", + "LeftHand", + "RightHand", + "Face", + "Hair", + "LeftFoot", + "RightFoot", + "Accessory" +}; + +plClothingMgr *plClothingMgr::fInstance = nil; + +plClothingMgr::plClothingMgr() +{ + fLayouts.Reset(); + fItems.Reset(); +} + +plClothingMgr::~plClothingMgr() +{ + while (fElements.GetCount() > 0) + delete fElements.Pop(); + while (fLayouts.GetCount() > 0) + delete fLayouts.Pop(); + while (fItems.GetCount() > 0) + delete fItems.Pop(); +} + +plClothingLayout *plClothingMgr::GetLayout(char *name) +{ + int i; + for (i = 0; i < fLayouts.GetCount(); i++) + { + if (!strcmp(fLayouts.Get(i)->fName, name)) + return fLayouts.Get(i); + } + return nil; +} + +plClothingElement *plClothingMgr::FindElementByName(char *name) +{ + int i; + for (i = 0; i < fElements.GetCount(); i++) + { + if (!strcmp(fElements.Get(i)->fName, name)) + return fElements.Get(i); + } + return nil; +} + +void plClothingMgr::AddItemsToCloset(hsTArray &items) +{ + RelVaultNode * rvn = VaultGetAvatarClosetFolderIncRef(); + if (!rvn) + return; + + hsTArray closet; + GetClosetItems(closet); + + ARRAY(RelVaultNode*) templates; + + for (unsigned i = 0; i < items.GetCount(); ++i) { + bool match = false; + for (unsigned j = 0; j < closet.GetCount(); ++j) { + if (closet[j].IsMatch(&items[i])) + { + match = true; + break; + } + } + + if (match) + continue; + + plStateDataRecord rec(plClothingSDLModifier::GetClothingItemSDRName()); + plClothingSDLModifier::PutSingleItemIntoSDR(&items[i], &rec); + + RelVaultNode * templateNode = NEWZERO(RelVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_SDL); + + VaultSDLNode sdl(templateNode); + sdl.SetStateDataRecord(&rec); + + templates.Add(templateNode); + } + + for (unsigned i = 0; i < templates.Count(); ++i) { + ENetError result; + if (RelVaultNode * actual = VaultCreateNodeAndWaitIncRef(templates[i], &result)) { + VaultAddChildNodeAndWait( + rvn->nodeId, + actual->nodeId, + NetCommGetPlayer()->playerInt + ); + actual->DecRef(); // REF: Create + } + templates[i]->DecRef(); // REF: Create + } + + rvn->DecRef(); +} + +void plClothingMgr::GetClosetItems(hsTArray &out) +{ + RelVaultNode * rvn = VaultGetAvatarClosetFolderIncRef(); + if (!rvn) + return; + + ARRAY(RelVaultNode*) nodes; + rvn->GetChildNodesIncRef(plVault::kNodeType_SDL, 1, &nodes); + out.SetCount(nodes.Count()); + + for (unsigned i = 0; i < nodes.Count(); ++i) { + VaultSDLNode sdl(nodes[i]); + plStateDataRecord * rec = NEWZERO(plStateDataRecord); + if (sdl.GetStateDataRecord(rec, 0)) + plClothingSDLModifier::HandleSingleSDR(rec, nil, &out[i]); + DEL(rec); + } + + if (out.GetCount()) { + for (int i = out.GetCount() - 1; i >= 0; i--) { + if (out[i].fItem == nil) + out.Remove(i); + } + } + + rvn->DecRef(); +} + +void plClothingMgr::GetAllWithSameMesh(plClothingItem *item, hsTArray &out) +{ + int i; + for (i = 0; i < fItems.GetCount(); i++) + { + if (item->HasSameMeshes(fItems[i])) + out.Append(fItems[i]); + } +} + +// Yes, it's a lame n^2 function. Show me that we have enough items for it +// to matter and I'll speed it up. +void plClothingMgr::FilterUniqueMeshes(hsTArray &items) +{ + int i, j; + for (i = items.GetCount() - 1; i >= 1; i--) + { + for (j = i - 1; j >= 0; j--) + { + if (items[i]->HasSameMeshes(items[j])) + { + items.Remove(i); + break; + } + } + } +} + +plClothingItem *plClothingMgr::FindItemByName(const char *name) +{ + if (!name) + return nil; + + int i; + for (i = 0; i < fItems.GetCount(); i++) + { + plClothingItem* item = fItems.Get(i); + if (!strcmp(item->fName, name)) + return item; + } + return nil; +} + +void plClothingMgr::GetItemsByGroup(UInt8 group, hsTArray &out) +{ + int i; + for (i = 0; i < fItems.GetCount(); i++) + { + if (fItems.Get(i)->fGroup == group) + out.Append(fItems.Get(i)); + } +} + +void plClothingMgr::GetItemsByGroupAndType(UInt8 group, UInt8 type, hsTArray &out) +{ + int i; + for (i = 0; i < fItems.GetCount(); i++) + { + if (fItems.Get(i)->fGroup == group && fItems.Get(i)->fType == type) + out.Append(fItems.Get(i)); + } +} + +plClothingItem *plClothingMgr::GetLRMatch(plClothingItem *item) +{ + int i; + for (i = 0; i < fItems.GetCount(); i++) + { + if (IsLRMatch(item, fItems[i])) + return fItems[i]; + } + + // Couldn't find one. + return nil; +} + +hsBool plClothingMgr::IsLRMatch(plClothingItem *item1, plClothingItem *item2) +{ + if (!item1 || !item2) + return false; + + switch (item1->fType) + { + case kTypeLeftHand: + if (item2->fType != kTypeRightHand) return false; + break; + case kTypeRightHand: + if (item2->fType != kTypeLeftHand) return false; + break; + case kTypeLeftFoot: + if (item2->fType != kTypeRightFoot) return false; + break; + case kTypeRightFoot: + if (item2->fType != kTypeLeftFoot) return false; + break; + default: + // if its not a matching kinda item, then there can be no match + return false; + } + + // Types check out fine, now compare textures + if (item1->fTextures.GetCount() != item2->fTextures.GetCount()) return false; + + int i, j; + for (i = 0; i < item1->fTextures.GetCount(); i++) + { + for (j = 0; j < plClothingElement::kLayerMax; j++) + if (item1->fTextures[i][j] != item2->fTextures[i][j]) + return false; + } + + // Finally... we're not our own match + return item1 != item2; +} + +void plClothingMgr::Init() +{ + fInstance = TRACKED_NEW plClothingMgr; + fInstance->RegisterAs(kClothingMgr_KEY); + fInstance->IInit(); +} + +void plClothingMgr::IInit() +{ + plClothingElement::GetElements(fElements); + plClothingLayout *layout = TRACKED_NEW plClothingLayout("BasicHuman", 1024); + layout->fElements.Append(FindElementByName("shirt-chest")); + layout->fElements.Append(FindElementByName("shirt-sleeve")); + layout->fElements.Append(FindElementByName("face")); + layout->fElements.Append(FindElementByName("eyeball")); + layout->fElements.Append(FindElementByName("shoe-top")); + layout->fElements.Append(FindElementByName("shoe-bottom")); + layout->fElements.Append(FindElementByName("pants")); + layout->fElements.Append(FindElementByName("hand-LOD")); + layout->fElements.Append(FindElementByName("hand-square")); + layout->fElements.Append(FindElementByName("hand-wide")); + + fLayouts.Append(layout); +} + +void plClothingMgr::DeInit() +{ + if (fInstance) + { + fInstance->UnRegisterAs(kClothingMgr_KEY); + fInstance = nil; + } +} + +hsBool plClothingMgr::MsgReceive(plMessage* msg) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + plClothingItem *item = plClothingItem::ConvertNoRef(refMsg->GetRef()); + if (item) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate) ) + { + IAddItem(item); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + fItems.RemoveItem(item); + } + return true; + } + } + + return hsKeyedObject::MsgReceive(msg); +} + +void plClothingMgr::IAddItem(plClothingItem *item) +{ + hsBool allFound = true; + int i, j; + for (i = 0; i < item->fElementNames.GetCount(); i++) + { + for (j = 0; j < fElements.GetCount(); j++) + { + if (!strcmp(item->fElementNames.Get(i), fElements.Get(j)->fName)) + { + item->fElements.Set(i, fElements.Get(j)); + break; + } + } + if (j >= fElements.GetCount()) + { + allFound = false; + break; + } + } + + if (allFound) + { + for (i = 0; i < fItems.GetCount(); i++) + { + if (fItems[i]->fSortOrder >= item->fSortOrder) + break; + } + fItems.InsertAtIndex(i, item); + } + else + hsAssert(false, "Couldn't match all elements of added clothing item."); +} + +void plClothingMgr::ChangeAvatar(char *name) +{ + plAvatarMgr::GetInstance()->UnLoadLocalPlayer(); + plAvatarMgr::GetInstance()->LoadPlayer(name, nil); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h new file mode 100644 index 00000000..6c9dcf7f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h @@ -0,0 +1,313 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLAVATARCLOTHING_INC +#define PLAVATARCLOTHING_INC + +#include "../pnUtils/pnUtils.h" +#include "../pnNetCommon/plSynchedObject.h" +#include "../CoreLib/hsColorRGBA.h" +#include "hsBitVector.h" +#include "plClothingLayout.h" + +class hsGMaterial; +class plMipmap; +class plLayer; +class hsStream; +class plSceneObject; +class plGeometrySpan; +class plClothingLayout; +class plClothingElement; +class plArmatureMod; +class plSharedMesh; +class plStateDataRecord; +class plDXPipeline; + +class plClothingItemOptions +{ +public: + hsColorRGBA fTint1; + hsColorRGBA fTint2; + + plClothingItemOptions() { fTint1.Set(1.f, 1.f, 1.f, 1.f); fTint2.Set(1.f, 1.f, 1.f, 1.f); } + + hsBool IsMatch(plClothingItemOptions *other) { return fTint1 == other->fTint1 && fTint2 == other->fTint2; } +}; + +class plClothingItem : public hsKeyedObject +{ +public: + enum + { + kLODHigh, + kLODMedium, + kLODLow, + kMaxNumLODLevels, + }; + + // If you change the format of a clothing item, chances are you need + // to change plClothingMgr::IsLRMatch() as well + char *fName; + plSharedMesh *fMeshes[kMaxNumLODLevels]; + hsTArray fTextures; + hsTArray fElementNames; + hsTArray fElements; + UInt8 fGroup; // Each avatar can wear one of the available groups + UInt8 fType; // Each group has multiple types of clothes (shirt/pants/etc) + UInt8 fTileset; + UInt8 fSortOrder; + char *fDescription; + char *fCustomText; + plMipmap *fThumbnail; + plClothingItem *fAccessory; // Forced accessory to always wear with this item. + UInt8 fDefaultTint1[3]; + UInt8 fDefaultTint2[3]; + + char *fAccessoryName; // Export only + + + + plClothingItem(); + ~plClothingItem(); + + CLASSNAME_REGISTER( plClothingItem ); + GETINTERFACE_ANY( plClothingItem, hsKeyedObject ); + + void SetName(char *name) { delete fName; fName = hsStrcpy(name); } + const char* GetName() { return fName; } + hsBool CanWearWith(plClothingItem *item); + hsBool WearBefore(plClothingItem *item); // Should we come before the arg item? (texture gen order) + hsBool HasBaseAlpha(); + hsBool HasSameMeshes(plClothingItem *other); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); +}; + +class plClosetItem +{ +public: + plClosetItem() : fItem(nil) {} + + plClothingItem *fItem; + plClothingItemOptions fOptions; + + hsBool IsMatch(plClosetItem *other); +}; + +class plClothingBase : public hsKeyedObject +{ +public: + char *fName; + plMipmap *fBaseTexture; + char *fLayoutName; + + plClothingBase(); + ~plClothingBase(); + + CLASSNAME_REGISTER( plClothingBase ); + GETINTERFACE_ANY( plClothingBase, hsKeyedObject ); + + void SetLayoutName(char *name) { delete fLayoutName; fLayoutName = hsStrcpy(name); } + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); +}; + +class plClothingOutfit : public plSynchedObject +{ + friend class plDXPipeline; + +public: + plArmatureMod *fAvatar; + plLayer *fTargetLayer; + hsGMaterial *fMaterial; // Needed to tell swapped geometry what material to use. + hsTArray fItems; + hsTArray fOptions; + plClothingBase *fBase; + UInt8 fGroup; + bool fSynchClients; // set true if the next synch should be bcast + hsColorRGBA fSkinTint; + hsScalar fSkinBlends[plClothingElement::kLayerSkinLast - plClothingElement::kLayerSkinFirst]; // Controls the opacity between skin textures. + + plClothingOutfit(); + ~plClothingOutfit(); + + CLASSNAME_REGISTER( plClothingOutfit ); + GETINTERFACE_ANY( plClothingOutfit, plSynchedObject ); + + void SaveCustomizations(hsBool retry = true); + void AddItem(plClothingItem *item, hsBool update = true, hsBool broadcast = true, hsBool netForce=false); + void RemoveItem(plClothingItem *item, hsBool update = true, hsBool netForce=false); + void TintItem(plClothingItem *item, hsScalar red, hsScalar green, hsScalar blue, hsBool update = true, hsBool broadcast = true, + hsBool netForce = false, hsBool retry = true, UInt8 fLayer = plClothingElement::kLayerTint1); + void TintSkin(hsScalar red, hsScalar green, hsScalar blue, + hsBool update = true, hsBool broadcast = true); + void MorphItem(plClothingItem *item, UInt8 layer, UInt8 delta, hsScalar weight, hsBool retry = true); + void SetAge(hsScalar age, hsBool update = true, hsBool broadcast = true); + void SetSkinBlend(hsScalar blend, UInt8 layer, hsBool update = true, hsBool broadcast = true); + hsScalar GetSkinBlend(UInt8 layer); + hsColorRGBA GetItemTint(plClothingItem *item, UInt8 layer = 2) const; + hsScalar GetAge() const { return fSkinBlends[0]; } + hsTArray &GetItemList() { return fItems; } + hsTArray &GetOptionList() { return fOptions; } + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + hsBool DirtySynchState(const char* SDLStateName, UInt32 synchFlags); + + void StripAccessories(); + void WearDefaultClothing(); + void WearDefaultClothingType(UInt32 clothingType); + void WearMaintainerOutfit(); + void WearRandomOutfit(); + void RemoveMaintainerOutfit(); + + hsBool ReadItems(hsStream* s, hsResMgr* mgr, hsBool broadcast = true); + void WriteItems(hsStream* s, hsResMgr* mgr); + + void ForceUpdate(bool retry); // send updateTexture msg + + virtual hsBool MsgReceive(plMessage* msg); + + void IInstanceSharedMeshes(plClothingItem *item); + void IRemoveSharedMeshes(plClothingItem *item); + + void ReadFromVault(); + void WriteToVault(); + void WriteToVault(const ARRAY(plStateDataRecord*) & SDRs); + + void SetupMorphSDL(); + + // XXX Don't use this. Temp function for a temp HACK console command. + void DirtyTileset(int tileset); + +protected: + hsBitVector fDirtyItems; + hsBool fVaultSaveEnabled; + bool fMorphsInitDone; + + void IAddItem(plClothingItem *item); + void IRemoveItem(plClothingItem *item); + hsBool ITintItem(plClothingItem *item, hsColorRGBA color, UInt8 layer); + hsBool IMorphItem(plClothingItem *item, UInt8 layer, UInt8 delta, hsScalar weight); + void IHandleMorphSDR(plStateDataRecord *sdr); + + void IUpdate(); + +}; + +class plClothingMgr : public hsKeyedObject +{ +protected: + static plClothingMgr *fInstance; + + hsTArray fElements; + hsTArray fItems; + hsTArray fLayouts; + + void IInit(); + void IAddItem(plClothingItem *item); + +public: + plClothingMgr(); + ~plClothingMgr(); + + CLASSNAME_REGISTER( plClothingMgr ); + GETINTERFACE_ANY( plClothingMgr, hsKeyedObject ); + + plClothingLayout *GetLayout(char *name); + plClothingElement *FindElementByName(char *name); + + + // Functions that just relate to the clothing you have permission to wear (closet) + void AddItemsToCloset(hsTArray &items); + void GetClosetItems(hsTArray &out); + + // Functions that relate to all existing clothing + plClothingItem *FindItemByName(const char *name); + hsTArray& GetItemList() { return fItems; } + void GetItemsByGroup(UInt8 group, hsTArray &out); + void GetItemsByGroupAndType(UInt8 group, UInt8 type, hsTArray &out); + void GetAllWithSameMesh(plClothingItem *item, hsTArray &out); + + // Give an array of items (from one of the above functions, for example) + // and this will yank out items so that only item is in the array for each mesh. + void FilterUniqueMeshes(hsTArray &items); + + // For a pair of items that go together (ie gloves) give us one, we'll give you the other + plClothingItem *GetLRMatch(plClothingItem *item); + hsBool IsLRMatch(plClothingItem *item1, plClothingItem *item2); + + static void ChangeAvatar(char *name); + + static plClothingMgr *GetClothingMgr() { return fInstance; } + static void Init(); + static void DeInit(); + + //virtual void Read(hsStream* s, hsResMgr* mgr); + //virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + + // NOTE: + // NOTE: + // NOTE: The following two enums are duplicated in Python, so... + // DON'T CHANGE THEIR CURRENT VALUES, ADD NEW ONES ON THE END OF THE LIST! + enum + { + kClothingBaseMale = 0, + kClothingBaseFemale, + kClothingBaseNoOptions, // Custom avatars + kMaxGroup + }; + enum + { + kTypePants = 0, + kTypeShirt, + kTypeLeftHand, + kTypeRightHand, + kTypeFace, + kTypeHair, + kTypeLeftFoot, + kTypeRightFoot, + kTypeAccessory, + kMaxType, + }; + + static const char *GroupStrings[]; + static const char *TypeStrings[]; +}; + + + + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarCreatable.h new file mode 100644 index 00000000..be70f260 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarCreatable.h @@ -0,0 +1,198 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAvatarCreatable_inc +#define plAvatarCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plAGAnim.h" + +REGISTER_CREATABLE( plAGAnim ); +REGISTER_CREATABLE( plATCAnim ); +REGISTER_CREATABLE( plEmoteAnim ); +REGISTER_CREATABLE( plAgeGlobalAnim ); + +#include "plAGChannel.h" + +REGISTER_NONCREATABLE( plAGChannel ); +REGISTER_NONCREATABLE( plAGApplicator ); + +#include "plMatrixChannel.h" + +REGISTER_CREATABLE( plMatrixChannel ); +REGISTER_CREATABLE( plMatrixConstant ); +REGISTER_CREATABLE( plMatrixTimeScale ); +REGISTER_CREATABLE( plMatrixBlend ); +REGISTER_CREATABLE( plMatrixControllerChannel ); +REGISTER_CREATABLE( plMatrixControllerCacheChannel ); +REGISTER_CREATABLE( plQuatPointCombine ); +REGISTER_CREATABLE( plMatrixChannelApplicator ); +REGISTER_CREATABLE( plMatrixDelayedCorrectionApplicator ); +REGISTER_CREATABLE( plMatrixDifferenceApp ); + +#include "plPointChannel.h" + +REGISTER_CREATABLE( plPointChannel ); +REGISTER_CREATABLE( plPointConstant ); +REGISTER_CREATABLE( plPointBlend ); +REGISTER_CREATABLE( plPointTimeScale ); +REGISTER_CREATABLE( plPointControllerChannel ); +REGISTER_CREATABLE( plPointControllerCacheChannel ); +REGISTER_CREATABLE( plPointChannelApplicator ); +REGISTER_CREATABLE( plLightDiffuseApplicator ); +REGISTER_CREATABLE( plLightAmbientApplicator ); +REGISTER_CREATABLE( plLightSpecularApplicator ); + +#include "plQuatChannel.h" + +REGISTER_CREATABLE( plQuatChannel ); +REGISTER_CREATABLE( plQuatConstant ); +REGISTER_CREATABLE( plQuatBlend ); +REGISTER_CREATABLE( plQuatTimeScale ); +REGISTER_CREATABLE( plQuatChannelApplicator ); + +#include "plScalarChannel.h" +REGISTER_CREATABLE( plScalarChannel ); +REGISTER_CREATABLE( plScalarConstant ); +REGISTER_CREATABLE( plScalarTimeScale ); +REGISTER_CREATABLE( plScalarBlend ); +REGISTER_CREATABLE( plScalarControllerChannel ); +REGISTER_CREATABLE( plScalarControllerCacheChannel ); +REGISTER_CREATABLE( plScalarChannelApplicator ); +REGISTER_CREATABLE( plSpotInnerApplicator ); +REGISTER_CREATABLE( plSpotOuterApplicator ); +REGISTER_CREATABLE( plATCChannel ); +REGISTER_CREATABLE( plScalarSDLChannel ); +REGISTER_CREATABLE( plOmniApplicator ); +REGISTER_CREATABLE( plOmniSqApplicator ); +REGISTER_CREATABLE( plOmniCutoffApplicator ); + +#include "plAGModifier.h" +REGISTER_CREATABLE( plAGModifier ); + +#include "plAGMasterMod.h" +REGISTER_CREATABLE( plAGMasterMod ); + +#include "plSeekPointMod.h" +REGISTER_CREATABLE( plSeekPointMod ); + +#include "plOneShotMod.h" +REGISTER_CREATABLE( plOneShotMod ); + +#include "plMultistageBehMod.h" +REGISTER_CREATABLE( plMultistageBehMod ); + +#include "plArmatureMod.h" +REGISTER_CREATABLE( plArmatureModBase ); +REGISTER_CREATABLE( plArmatureMod ); +REGISTER_CREATABLE( plArmatureLODMod ); + +#include "plArmatureEffects.h" +REGISTER_CREATABLE( plArmatureEffectsMgr ); +REGISTER_NONCREATABLE( plArmatureEffect ); +REGISTER_CREATABLE( plArmatureEffectFootSound ); + +#include "plAvBrain.h" +REGISTER_NONCREATABLE(plArmatureBrain); + +#include "plAvBrainHuman.h" +REGISTER_CREATABLE(plAvBrainHuman); + +#include "plAvBrainDrive.h" +REGISTER_CREATABLE(plAvBrainDrive); + +#include "plAvLadderModifier.h" +REGISTER_CREATABLE( plAvLadderMod ); + +#include "plAvatarClothing.h" +REGISTER_CREATABLE(plClothingItem); +REGISTER_CREATABLE(plClothingOutfit); +REGISTER_CREATABLE(plClothingBase); +REGISTER_CREATABLE(plClothingMgr); + +#include "plAvBrainGeneric.h" +REGISTER_CREATABLE(plAvBrainGeneric); + +#include "plAvatarTasks.h" +REGISTER_NONCREATABLE( plAvTask ); +REGISTER_CREATABLE( plAvAnimTask ); +REGISTER_CREATABLE( plAvSeekTask ) +REGISTER_CREATABLE( plAvOneShotTask ); +REGISTER_CREATABLE( plAvOneShotLinkTask ); + +#include "plAnimStage.h" +REGISTER_CREATABLE( plAnimStage ); + +#include "plAvTaskSeek.h" +REGISTER_CREATABLE( plAvTaskSeek ); + +#include "plAGMasterSDLModifier.h" +REGISTER_CREATABLE( plAGMasterSDLModifier ); + +#include "plAvatarSDLModifier.h" +REGISTER_CREATABLE( plAvatarSDLModifier ); +REGISTER_CREATABLE( plAvatarPhysicalSDLModifier ); + +#include "plClothingSDLModifier.h" +REGISTER_CREATABLE( plClothingSDLModifier ); + +#include "plAvatarMgr.h" +REGISTER_NONCREATABLE( plAvatarMgr ); + +#include "plNPCSpawnMod.h" +REGISTER_CREATABLE( plNPCSpawnMod ); + +#include "plAvBrainSwim.h" +REGISTER_CREATABLE( plAvBrainSwim ); + +#include "plAvBrainClimb.h" +REGISTER_CREATABLE( plAvBrainClimb ); + +#include "plAvBrainCoop.h" +REGISTER_CREATABLE( plAvBrainCoop ); + +#include "plCoopCoordinator.h" +REGISTER_CREATABLE( plCoopCoordinator ); + +#include "plAvTaskBrain.h" +REGISTER_CREATABLE( plAvTaskBrain ); + +#include "plSittingModifier.h" +REGISTER_CREATABLE( plSittingModifier ); + +#include "plSwimRegion.h" +REGISTER_CREATABLE( plSwimRegionInterface ); +REGISTER_CREATABLE( plSwimCircularCurrentRegion ); +REGISTER_CREATABLE( plSwimStraightCurrentRegion ); + +#include "plAvBrainCritter.h" +REGISTER_CREATABLE( plAvBrainCritter ); + +#include "plAvBrainRideAnimatedPhysical.h" +REGISTER_CREATABLE(plAvBrainRideAnimatedPhysical) +#endif // plAvatarCreatable_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp new file mode 100644 index 00000000..362f3c84 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp @@ -0,0 +1,1029 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#pragma warning(disable: 4503 4786) + +#include + +#include "plAvatarMgr.h" + +// local +#include "plArmatureMod.h" +#include "plSeekPointMod.h" +#include "plOneShotMod.h" +#include "plArmatureMod.h" +#include "plAGModifier.h" +#include "plAnimStage.h" +#include "plCoopCoordinator.h" +#include "plAvBrainCoop.h" + +// global +#include "hsResMgr.h" +#include "../pnNetCommon/plNetApp.h" +#include "plgDispatch.h" +#include "hsTimer.h" + +// other +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plResMgr/plKeyFinder.h" +#include "../pfCCR/plCCRMgr.h" // Only included for defined constants. +#include "../plNetTransport/plNetTransport.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../plModifier/plSpawnModifier.h" +#include "../plModifier/plMaintainersMarkerModifier.h" +#include "../plVault/plDniCoordinateInfo.h" +#include "../plMath/plRandom.h" + +#include "../pnMessage/plPlayerPageMsg.h" +#include "../pnMessage/plWarpMsg.h" +#include "../pnMessage/plNotifyMsg.h" + +#include "../plMessage/plMemberUpdateMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plAvCoopMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../plStatusLog/plStatusLog.h" + +// The static single instance, allocated on demand by GetInstance() +plAvatarMgr *plAvatarMgr::fInstance = nil; + +// CTOR +plAvatarMgr::plAvatarMgr() +{ + fLog = plStatusLogMgr::GetInstance().CreateStatusLog(40, "Avatar.log", plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kTimestamp); + fLog->AddLine("Initalized avatar mgr"); +} + +// DTOR +plAvatarMgr::~plAvatarMgr() +{ + IReset(); + + delete fLog; + fLog = nil; +} + +// GETINSTANCE +plAvatarMgr * plAvatarMgr::GetInstance() +{ + if(!fInstance) + { + fInstance = TRACKED_NEW plAvatarMgr; + fInstance->RegisterAs(kAvatarMgr_KEY); + fInstance->Ref(); + } + return fInstance; +} + +// SHUTDOWN +void plAvatarMgr::ShutDown() +{ + if(fInstance) + { + fInstance->UnRef(); + if(fInstance) + fInstance->UnRegister(); + fInstance = nil; + } +} + +// RESET +void plAvatarMgr::IReset() +{ + fSeekPoints.clear(); + + // Oneshots have copies of strings in their maps. I'm assuming the others should be the same, but until + // I hear otherwise... + for( plOneShotMap::iterator it = fOneShots.begin(); it != fOneShots.end(); it++ ) + delete [] (char *)it->first; + fOneShots.clear(); + fAvatars.clear(); + fSpawnPoints.clear(); + fMaintainersMarkers.SetCountAndZero(0); + + plCoopMap::iterator acIt = fActiveCoops.begin(); + while (acIt != fActiveCoops.end()) + { + plCoopCoordinator* deadCoop = acIt->second; + delete deadCoop; + acIt++; + } + fActiveCoops.clear(); +} + +plKey plAvatarMgr::LoadPlayer(const char *name, const char *account) +{ + return LoadAvatar(name, account, true, nil, nil); +} + +plKey plAvatarMgr::LoadPlayer(const char *name, const char *account, const char *linkInName) +{ + // what we'd like to do is turn the linkInName into a spawn point key and + // put that into the plLoadAvatarMsg, which is already set up to handle + // initial spawn points. + // however, that will require that we can handle waiting for our spawn point to load, + // so we're goin to do this the "old way" for now. + + plArmatureMod::SetSpawnPointOverride(linkInName); + return LoadAvatar(name, account, true, nil, nil); +} + + +plKey plAvatarMgr::LoadAvatar(const char *name, const char *accountName, bool isPlayer, plKey spawnPoint, plAvTask *initialTask, const char *userStr /*=nil*/) +{ + // *** account is currently unused. the idea is that eventually an NPC will + // *** be able to use a customization account + plKey result = nil; + plKey requestor = GetKey(); // avatar manager is always the requestor for avatar loads + plNetClientMgr *netMgr = plNetClientMgr::GetInstance(); + + if(netMgr) // can't clone without the net manager + { + hsAssert(name, "name required by LoadPlayer fxn"); + netMgr->DebugMsg("Local: Loading player %s", name); + + // look up player by key name provided by user. + // this string search should be replaced with some other method of + // avatar selection and key lookup. + + // Get the location for the player first + plKey playerKey = nil; + const plLocation& globalLoc = plKeyFinder::Instance().FindLocation("GlobalAvatars", name); + const plLocation& maleLoc = plKeyFinder::Instance().FindLocation("GlobalAvatars", "Male"); + const plLocation& custLoc = plKeyFinder::Instance().FindLocation("CustomAvatars", name); + + // Silliness to make the compiler happy with const references. + // and don't allow players to use custom avatars + const plLocation& loc = (globalLoc.IsValid() ? globalLoc : isPlayer ? maleLoc : custLoc); + + const char* theName = name; + if ( isPlayer && !globalLoc.IsValid() ) + theName = "Male"; + + if (loc.IsValid()) + { + plUoid uID(loc, plSceneObject::Index(), theName); + plLoadAvatarMsg *cloneMsg = TRACKED_NEW plLoadAvatarMsg (uID, requestor, 0, isPlayer, spawnPoint, initialTask, userStr); + result = cloneMsg->GetCloneKey(); + + // the clone message is automatically addressed to the net client manager + // we'll receive the message back (or a similar message) when the clone is loaded + cloneMsg->Send(); + } + } + return result; +} + +void plAvatarMgr::UnLoadAvatar(plKey avatarKey, bool isPlayer) +{ + hsBool isLoading = false; + plLoadAvatarMsg *msg = TRACKED_NEW plLoadAvatarMsg(avatarKey, GetKey(), 0, isPlayer, isLoading); + msg->Send(); +} + +// our player's already loaded locally, but we've just linked into an age and others there need to be +// told about us +void plAvatarMgr::PropagateLocalPlayer(int spawnPoint) +{ + plKey playerKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + if(playerKey) + { + plKey requestor = GetKey(); + bool isPlayer = true; + hsBool isLoading = true; + plLoadAvatarMsg *msg = TRACKED_NEW plLoadAvatarMsg(playerKey, requestor, 0, isPlayer, isLoading); + + if (spawnPoint >= 0) + { + const plSpawnModifier * spawn = GetSpawnPoint(spawnPoint); + if ( spawn ) + { + const plSceneObject * spawnObj = spawn->GetTarget(0); + msg->SetSpawnPoint(spawnObj->GetKey()); + } + } + + // don't propagate locally. this is only for our peers + msg->SetBCastFlag(plMessage::kLocalPropagate, false); + msg->Send(); + } else { + hsStatusMessage("Tried to propagate non-existent local player."); + } +} + +// UNLOADLOCALPLAYERREMOTELY +bool plAvatarMgr::UnPropagateLocalPlayer() +{ + plKey playerKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + if(playerKey) + { + plKey requestor = GetKey(); + bool isPlayer = true; + hsBool isLoading = false; + plLoadAvatarMsg *msg = TRACKED_NEW plLoadAvatarMsg(playerKey, requestor, 0, isPlayer, isLoading); + msg->SetBCastFlag(plMessage::kLocalPropagate, false); + msg->Send(); + return true; + } + return false; +} + +// UNLOADREMOTEPLAYER +void plAvatarMgr::UnLoadRemotePlayer(plKey remotePlayer) +{ + if(remotePlayer) + { + plKey requestor = GetKey(); + bool isPlayer = true; + hsBool isLoading = false; + plLoadAvatarMsg * msg = TRACKED_NEW plLoadAvatarMsg(remotePlayer, requestor, 0, isPlayer, isLoading); + + // don't propagate over the network. this is just for removing our local version + msg->SetBCastFlag(plMessage::kNetPropagate, false); + msg->Send(); + } +} + +// UNLOADLOCALPLAYER +void plAvatarMgr::UnLoadLocalPlayer() +{ + plKey playerKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + if(playerKey) + { + plKey mgrKey = GetKey(); + bool isPlayer = true; + hsBool isLoading = false; + plLoadAvatarMsg *msg = TRACKED_NEW plLoadAvatarMsg(playerKey, mgrKey, 0, isPlayer, isLoading); + msg->Send(); + } +} + +// MSGRECEIVE +hsBool plAvatarMgr::MsgReceive(plMessage *msg) +{ + plLoadAvatarMsg *cloneM = plLoadAvatarMsg::ConvertNoRef(msg); + if(cloneM) + { + // The only way we get clone messages is if we (or our remote counterparts) + // requested them. + if(cloneM->GetIsLoading()) + { + IFinishLoadingAvatar(cloneM); + } else { + IFinishUnloadingAvatar(cloneM); + } + return true; + } + + plLoadCloneMsg* pCloneMsg = plLoadCloneMsg::ConvertNoRef(msg); + if (pCloneMsg) + { + pCloneMsg->Ref(); + fCloneMsgQueue.Append(pCloneMsg); + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + return true; + } + + plEvalMsg* pEval = plEvalMsg::ConvertNoRef(msg); + if (pEval) + { + for (int i = fCloneMsgQueue.Count() - 1; i > -1; i--) + { + plArmatureMod* pAvatar = FindAvatarByPlayerID(fCloneMsgQueue[i]->GetUserData()); + if (pAvatar && pAvatar->GetKey()->ObjectIsLoaded()) + { + pAvatar->MsgReceive(fCloneMsgQueue[i]); + fCloneMsgQueue[i]->UnRef(); + fCloneMsgQueue.Remove(i); + } + } + if (fCloneMsgQueue.Count() == 0) + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + + return true; + } + plAvCoopMsg *coopM = plAvCoopMsg::ConvertNoRef(msg); + if(coopM) + { + return HandleCoopMsg(coopM); + } + + plNotifyMsg *notifyM = plNotifyMsg::ConvertNoRef(msg); + if(notifyM) + { + if(proEventData * evt = notifyM->FindEventRecord(proEventData::kCoop)) + { + proCoopEventData *coopE = static_cast(evt); + return IPassMessageToActiveCoop(msg, coopE->fID, coopE->fSerial); + } + } + + return false; +} + +hsBool plAvatarMgr::HandleCoopMsg(plAvCoopMsg *msg) +{ + plAvCoopMsg::Command cmd = msg->fCommand; + + UInt32 id = msg->fInitiatorID; + UInt16 serial = msg->fInitiatorSerial; + + if(cmd == plAvCoopMsg::kStartNew) + { + // Currently, there's nothing that removes these coop coordinators when + // they're done. Since I can't think of a good way to figure out when + // they're done, I'm just going to clear them every time a new one starts. + // With the current usage, you should only get one at a time anyway -Colin + plCoopMap::iterator it = fActiveCoops.begin(); + while (it != fActiveCoops.end()) + { + plCoopCoordinator* deadCoop = it->second; + delete deadCoop; + it++; + } + fActiveCoops.clear(); + + // start a new coop + plCoopCoordinator *coord = msg->fCoordinator; + plCoopMap::value_type newVal(id, coord); + fActiveCoops.insert(newVal); + coord->Run(); + return true; + } else { + // it's a message for an existing coop... + return IPassMessageToActiveCoop(msg, id, serial); + } +} + +hsBool plAvatarMgr::HandleNotifyMsg(plNotifyMsg *msg) +{ + proCoopEventData *ed = static_cast(msg->FindEventRecord(proEventData::kCoop)); + if(ed) + { + UInt32 id = ed->fID; + UInt16 serial = ed->fSerial; + return IPassMessageToActiveCoop(msg, id, serial); + } + return false; +} + +hsBool plAvatarMgr::IPassMessageToActiveCoop(plMessage *msg, UInt32 id, UInt16 serial) +{ + plCoopMap::iterator i = fActiveCoops.find(id); + while(i != fActiveCoops.end() && (*i).first == id) + { + plCoopCoordinator *coord = (*i).second; + if(coord->GetInitiatorSerial() == serial && coord->IsActiveForReal() ) + { + // this is the one + coord->MsgReceive(msg); + return true; + } + i++; + } + return false; +} + +bool plAvatarMgr::IsACoopRunning() +{ + bool isRunning = false; + plCoopMap::iterator it = fActiveCoops.begin(); + while (it != fActiveCoops.end()) + { + plCoopCoordinator* aCoop = it->second; + if (aCoop->IsActiveForReal()) + isRunning = true; + it++; + } + return isRunning; +} + + +void plAvatarMgr::IFinishLoadingAvatar(plLoadAvatarMsg *cloneMsg) +{ + plKey avatarKey = cloneMsg->GetCloneKey(); + plUoid playerUoid = avatarKey->GetUoid(); + const plArmatureMod *armature = FindAvatar(avatarKey); + + // we're going to re-send the clone message to the loaded avatar so he can get + // any necessary details from it. + cloneMsg->ClearReceivers(); // don't want it coming back to us + cloneMsg->Ref(); // or going away + + if(armature) + { + cloneMsg->AddReceiver(armature->GetKey()); + cloneMsg->Send(); + } else { + IDeferInit(avatarKey, cloneMsg); // we'll send this message when the armature mod loads. + } + + if( cloneMsg->GetIsPlayer() ) + { + // notify everyone who cares that a new player has arrived + // *** might want to move this to the human brain so we can make sure the + // *** avatar is sufficiently initialized before anyone accesses him + bool isLocal = cloneMsg->GetOriginatingPlayerID() == plNetClientMgr::GetInstance()->GetPlayerID(); + plPlayerPageMsg* pageM = TRACKED_NEW plPlayerPageMsg; + pageM->SetBCastFlag(plMessage::kBCastByExactType); + pageM->fLocallyOriginated = isLocal; + pageM->fPlayer = avatarKey; + pageM->fUnload = false; + pageM->fClientID = cloneMsg->GetOriginatingPlayerID(); + pageM->Send(); + } + + // This can probably be replaced by the plPlayerPageMsg: + // ...keeping for the moment for compatibility + plMemberUpdateMsg* mu = TRACKED_NEW plMemberUpdateMsg; + mu->Send(); +} + +// IFINISHUNLOADINGAVATAR +void plAvatarMgr::IFinishUnloadingAvatar(plLoadAvatarMsg *cloneMsg) +{ + // Note: in the corresponding FinishLoading, above, we give the incoming avatar + // a look at the message that spawned him. When unloading, however, he doesn't get + // that benefit because I don't think he'll actually be around to receive it. + // *** need to test that theory....but it's not a problem for now. + if( cloneMsg->GetIsPlayer() ) + { + plKey avatar = cloneMsg->GetCloneKey(); + + bool isLocal = cloneMsg->GetOriginatingPlayerID() == plNetClientMgr::GetInstance()->GetPlayerID(); + plPlayerPageMsg *pageM = TRACKED_NEW plPlayerPageMsg; + pageM->SetBCastFlag(plMessage::kBCastByExactType); + pageM->fLocallyOriginated = isLocal; + pageM->fPlayer = avatar; + pageM->fUnload = true; + pageM->fClientID = cloneMsg->GetOriginatingPlayerID(); + if (plNetClientMgr::GetInstance()->RemotePlayerKeys().size() == 0) + pageM->fLastOut = true; + pageM->Send(); + } + + // check on this...can it be subsumed by plPlayerPageMsg ? + plMemberUpdateMsg *mu = TRACKED_NEW plMemberUpdateMsg; + mu->Send(); +} + + +// IDEFERINIT +void plAvatarMgr::IDeferInit(plKey playerSOKey, plMessage *initMsg) +{ + plMessage *existing = fDeferredInits[playerSOKey]; // okay to use this form because we're going + // to do the add either way + if(existing) + { + hsStatusMessage("Avatar was registered twice for init. Discarding initial init message."); + existing->UnRef(); + } + + fDeferredInits[playerSOKey] = initMsg; + initMsg->Ref(); +} + +// ISENDDEFERREDINIT +void plAvatarMgr::ISendDeferredInit(plKey avatarSOKey) +{ + // get armaturemod + const plArmatureMod * armature = FindAvatar(avatarSOKey); + + if(armature) + { + DeferredInits::iterator i = fDeferredInits.find(avatarSOKey); + bool found = (i != fDeferredInits.end()); + + if(i != fDeferredInits.end()) + { + plMessage * initMsg = (*i).second; + hsAssert(initMsg, "Tried to init avatar, but found nil initialization message."); + + if(initMsg) + { + initMsg->AddReceiver(armature->GetKey()); + initMsg->Send(); + } + } + } +} + + +// ADDSEEKPOINT +void plAvatarMgr::AddSeekPoint(plSeekPointMod *seekPoint) +{ + if(seekPoint) + { + const char *name = seekPoint->GetTarget(0)->GetKey()->GetName(); + char *ourName = hsStrcpy(name); + plSeekPointMod *alreadyThere = FindSeekPoint(name); + + /// hsAssert( ! alreadyThere, "Tried to add a seek point with duplicate name. Ignoring second seek point."); + + if ( ! alreadyThere) + { + fSeekPoints[ourName] = seekPoint; + } + } +} + +// REMOVESEEKPOINT +void plAvatarMgr::RemoveSeekPoint(plSeekPointMod *seekPoint) +{ + if(seekPoint) + { + const char *name = seekPoint->GetTarget(0)->GetKey()->GetName(); + + plSeekPointMap::iterator found = fSeekPoints.find(name); + + if(found != fSeekPoints.end()) + { + const char *oldName = (*found).first; + fSeekPoints.erase(found); + delete[] const_cast(oldName); // retarded language, this is... + } + } +} + +// FINDSEEKPOINT +plSeekPointMod * plAvatarMgr::FindSeekPoint(const char *name) +{ + plSeekPointMap::iterator found = fSeekPoints.find(name); + + if (found == fSeekPoints.end()) + { + return nil; + } else { + return (*found).second; + } +} + +// ADDONESHOT +void plAvatarMgr::AddOneShot(plOneShotMod *oneshot) +{ + if(oneshot) + { + // allocate a copy of the target name to use as a key + char * name = hsStrcpy(oneshot->GetTarget(0)->GetKey()->GetName()); + plOneShotMod *alreadyThere = FindOneShot(name); + + + if ( ! alreadyThere) + { + fOneShots[name] = oneshot; + } + else + delete [] name; + } +} + +// REMOVEONESHOT +void plAvatarMgr::RemoveOneShot(plOneShotMod *oneshot) +{ + plOneShotMap::iterator i = fOneShots.begin(); + + while (i != fOneShots.end()) + { + char * name = (*i).first; + plOneShotMod *thisOneshot = (*i).second; + + if(oneshot == thisOneshot) + { + i = fOneShots.erase(i); + // destroy our copy of the target name + delete[] name; + } else { + i++; + } + } +} + +// FINDONESHOT +plOneShotMod *plAvatarMgr::FindOneShot(char *name) +{ + plOneShotMap::iterator found = fOneShots.find(name); + + if (found == fOneShots.end()) + { + return nil; + } else { + return (*found).second; + } +} + +// ADDAVATAR +void plAvatarMgr::AddAvatar(plArmatureMod *avatar) +{ + // we shouldn't really need to ref this, as every time we access this object we will be checking it, and we don't care too much if it gets + // pulled out from under us + fAvatars.push_back(avatar->GetKey()); + plSceneObject *avatarSO = avatar->GetTarget(0); + hsAssert(avatarSO, "Adding avatar, but it hasn't been attached to a scene object yet."); + if(avatarSO) + { + plKey soKey = avatarSO->GetKey(); + ISendDeferredInit(soKey); + } +} + +// REMOVEAVATAR +void plAvatarMgr::RemoveAvatar(plArmatureMod *avatar) +{ + if (avatar) + { + plAvatarVec::iterator tail = std::remove(fAvatars.begin(), fAvatars.end(), avatar->GetKey()); + if(tail != fAvatars.end()) + fAvatars.erase(tail); + } +} + +plArmatureMod* plAvatarMgr::GetLocalAvatar() +{ + plNetClientApp * app = plNetClientApp::GetInstance(); + if(app) + { + plKey key = app->GetLocalPlayerKey(); + if (key && key->ObjectIsLoaded()) + { + plSceneObject* so = plSceneObject::ConvertNoRef(key->GetObjectPtr()); + if (so) + return const_cast((plArmatureMod*)so->GetModifierByType(plArmatureMod::Index())); + } + } + + return nil; +} + +plKey plAvatarMgr::GetLocalAvatarKey() +{ + plArmatureMod *avatar = GetLocalAvatar(); + if (avatar) + return avatar->GetKey(); + + return nil; +} + +plArmatureMod *plAvatarMgr::GetFirstRemoteAvatar() +{ + plNetClientApp * app = plNetClientApp::GetInstance(); + if(app) + { + plArmatureMod *localAvatar = GetLocalAvatar(); + + plAvatarVec::iterator it; + for (it = fAvatars.begin(); it != fAvatars.end(); ++it) + { + plArmatureMod* armature = plArmatureMod::ConvertNoRef((*it)->ObjectIsLoaded()); + if(armature && (armature != localAvatar)) + return armature; + } + } + + return nil; +} + +plArmatureMod* plAvatarMgr::FindAvatar(plKey& avatarKey) +{ + plSceneObject *so = plSceneObject::ConvertNoRef(avatarKey->ObjectIsLoaded()); + if (so) + return const_cast((plArmatureMod*)so->GetModifierByType(plArmatureMod::Index())); + + return nil; +} + +plArmatureMod* plAvatarMgr::FindAvatarByPlayerID(UInt32 pid) +{ + plAvatarVec::iterator it; + for (it = fAvatars.begin(); it != fAvatars.end(); ++it) + { + plArmatureMod* armature = plArmatureMod::ConvertNoRef((*it)->ObjectIsLoaded()); + if (armature && (armature->GetKey()->GetUoid().GetClonePlayerID() == pid)) + return armature; + } + return nil; +} + +plArmatureMod *plAvatarMgr::FindAvatarByModelName(char *name) +{ + plAvatarVec::iterator it; + for (it = fAvatars.begin(); it != fAvatars.end(); ++it) + { + plArmatureMod* armature = plArmatureMod::ConvertNoRef((*it)->ObjectIsLoaded()); + if (armature && (!strcmp(armature->GetTarget(0)->GetKeyName(), name))) + return armature; + } + + return nil; +} + +void plAvatarMgr::FindAllAvatarsByModelName(const char* name, plArmatureModPtrVec& outVec) +{ + plAvatarVec::iterator it; + for (it = fAvatars.begin(); it != fAvatars.end(); ++it) + { + plArmatureMod* armature = plArmatureMod::ConvertNoRef((*it)->ObjectIsLoaded()); + if (armature && (!strcmp(armature->GetTarget(0)->GetKeyName(), name))) + outVec.push_back(armature); + } +} + +// ADDSPAWNPOINT +void plAvatarMgr::AddSpawnPoint(plSpawnModifier *spawn) +{ + fSpawnPoints.push_back(spawn); +} + +// REMOVESPAWNPOINT +void plAvatarMgr::RemoveSpawnPoint(plSpawnModifier *spawn) +{ + plSpawnVec::iterator found = std::find(fSpawnPoints.begin(), fSpawnPoints.end(), spawn); + + if(found != fSpawnPoints.end()) + { + fSpawnPoints.erase(found); + } +} + +// GETSPAWNPOINT +const plSpawnModifier * plAvatarMgr::GetSpawnPoint(int i) +{ + if(i < fSpawnPoints.size()) + { + return fSpawnPoints[i]; + } else return nil; +} + +int plAvatarMgr::FindSpawnPoint( const char *name ) const +{ + int i; + + for( i = 0; i < fSpawnPoints.size(); i++ ) + { + if( fSpawnPoints[ i ] != nil && + (strstr( fSpawnPoints[ i ]->GetKey()->GetUoid().GetObjectName(), name ) != nil || + strstr( fSpawnPoints[i]->GetTarget(0)->GetKeyName(), name) != nil)) + return i; + } + + return -1; +} + +int plAvatarMgr::WarpPlayerToAnother(hsBool iMove, UInt32 remoteID) +{ + plNetTransport &mgr = plNetClientMgr::GetInstance()->TransportMgr(); + plNetTransportMember *mbr = mgr.GetMember(mgr.FindMember(remoteID)); + + if (!mbr) + return plCCRError::kCantFindPlayer; + + if (!mbr->GetAvatarKey()) + return plCCRError::kPlayerNotInAge; + + plSceneObject *remoteSO = plSceneObject::ConvertNoRef(mbr->GetAvatarKey()->ObjectIsLoaded()); + plSceneObject *localSO = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); + + if (!remoteSO) + return plCCRError::kCantFindPlayer; + if (!localSO) + return plCCRError::kNilLocalAvatar; + + plWarpMsg *warp = TRACKED_NEW plWarpMsg(nil, (iMove ? localSO->GetKey() : remoteSO->GetKey()), + plWarpMsg::kFlushTransform, (iMove ? remoteSO->GetLocalToWorld() : localSO->GetLocalToWorld())); + + warp->SetBCastFlag(plMessage::kNetPropagate); + plgDispatch::MsgSend(warp); + + return hsOK; +} + +int plAvatarMgr::WarpPlayerToXYZ(hsScalar x, hsScalar y, hsScalar z) +{ + plSceneObject *localSO = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); + if (!localSO) + return plCCRError::kNilLocalAvatar; + + hsMatrix44 m = localSO->GetLocalToWorld(); + hsVector3 v(x, y, z); + m.SetTranslate(&v); + + plWarpMsg *warp = TRACKED_NEW plWarpMsg(nil, localSO->GetKey(), plWarpMsg::kFlushTransform, m); + warp->SetBCastFlag(plMessage::kNetPropagate); + plgDispatch::MsgSend(warp); + + return hsOK; +} + +int plAvatarMgr::WarpPlayerToXYZ(int pid, hsScalar x, hsScalar y, hsScalar z) +{ + plNetClientMgr* nc=plNetClientMgr::GetInstance(); + plNetTransportMember* mbr=nc->TransportMgr().GetMember(nc->TransportMgr().FindMember(pid)); + plSceneObject *player = plSceneObject::ConvertNoRef(mbr && mbr->GetAvatarKey() ? + mbr->GetAvatarKey()->ObjectIsLoaded() : nil); + if (!player) + return plCCRError::kNilLocalAvatar; + + hsMatrix44 m = player->GetLocalToWorld(); + hsVector3 v(x, y, z); + m.SetTranslate(&v); + + plWarpMsg *warp = TRACKED_NEW plWarpMsg(nil, player->GetKey(), 0, m); + warp->SetBCastFlag(plMessage::kNetPropagate); + plgDispatch::MsgSend(warp); + + return hsOK; +} + +// ADD maintainers marker +void plAvatarMgr::AddMaintainersMarker(plMaintainersMarkerModifier *mm) +{ + fMaintainersMarkers.Append(mm); +} + +// REMOVE maintainers marker +void plAvatarMgr::RemoveMaintainersMarker(plMaintainersMarkerModifier *mm) +{ + for (int i = 0; i < fMaintainersMarkers.Count(); i++) + { + if (fMaintainersMarkers[i] == mm) + fMaintainersMarkers.Remove(i); + } +} + +void plAvatarMgr::PointToDniCoordinate(hsPoint3 pt, plDniCoordinateInfo* ret) +{ + int count = fMaintainersMarkers.Count(); + // plDniCoordinateInfo ret = TRACKED_NEW plDniCoordinateInfo; + if (count > 0) + { + + // find the closest maintainers marker + int nearestIndex = 0; + if (count > 1) + { + for (int i = 0; i < fMaintainersMarkers.Count(); i++) + { + if (fMaintainersMarkers[i]->GetTarget(0)) + { + hsVector3 testDist(fMaintainersMarkers[i]->GetTarget(0)->GetCoordinateInterface()->GetLocalToWorld().GetTranslate() - pt); + hsVector3 baseDist(fMaintainersMarkers[nearestIndex]->GetTarget(0)->GetCoordinateInterface()->GetLocalToWorld().GetTranslate() - pt); + if (testDist.MagnitudeSquared() < baseDist.MagnitudeSquared()) + nearestIndex = i; + } + } + } + // convert the marker position to Dni coordinates + int status = fMaintainersMarkers[nearestIndex]->GetCalibrated(); + + switch (status) + { + case plMaintainersMarkerModifier::kBroken: + { + plRandom rnd; + rnd.SetSeed((int)(hsTimer::GetSeconds())); + rnd.RandRangeI(1,999); + ret->SetHSpans( rnd.RandRangeI(1,999) ); + ret->SetVSpans( rnd.RandRangeI(1,999) ); + ret->SetTorans( rnd.RandRangeI(1,62500) ); + } + break; + case plMaintainersMarkerModifier::kRepaired: + { + ret->SetHSpans(0); + ret->SetVSpans(0); + ret->SetTorans(0); + } + break; + case plMaintainersMarkerModifier::kCalibrated: + { + // this is the real deal here: + // vertical spans: + hsPoint3 retPoint = fMaintainersMarkers[nearestIndex]->GetTarget(0)->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + ret->SetVSpans( ((int)(pt.fZ - retPoint.fZ) / 16) ); + + // horizontal spans: + + // zero out the z axis... + retPoint.fZ = pt.fZ = 0.0f; + hsVector3 hSpanVec(retPoint - pt); + ret->SetHSpans( (int)hSpanVec.Magnitude() / 16) ; + + // torans + hsVector3 zeroVec = fMaintainersMarkers[nearestIndex]->GetTarget(0)->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + hsVector3 zeroRight = fMaintainersMarkers[nearestIndex]->GetTarget(0)->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kRight); + zeroVec *= -1; // match the zero vectors to the positive X & Y axes in 3DSMax + zeroRight *= -1; + hsVector3 retVec(pt - retPoint); + retVec.Normalize(); + + hsScalar dotView = retVec * zeroVec; + hsScalar dotRight = retVec * zeroRight; + + hsScalar deg = acosf(dotView); + deg*=(180/3.141592); + // account for being > 180 + if (dotRight < 0.0f) + { + deg = 360.f - deg; + } + // convert it to dni radians (torans) + deg*=173.61; + ret->SetTorans((int)deg); + } + break; + } + + } +} + +void plAvatarMgr::GetDniCoordinate(plDniCoordinateInfo* ret) +{ + plSceneObject* localSO = plSceneObject::ConvertNoRef(plNetClientMgr::GetInstance()->GetLocalPlayer()); + if (localSO) + { + hsPoint3 pos = localSO->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + PointToDniCoordinate(pos, ret); + } +} + +// OfferLinkingBook --------------------------------------------------------------- +// ---------- +void plAvatarMgr::OfferLinkingBook(plKey hostKey, plKey guestKey, plMessage *linkMsg, plKey replyKey) +{ + if(hostKey != nil && guestKey != nil) + { + const plArmatureMod *hostAv = FindAvatar(hostKey); + const plArmatureMod *guestAv = FindAvatar(guestKey); + + hsAssert(hostAv && guestAv, "Offering linking book: host or guest missing."); + + if(hostAv && guestAv) + { + + // make the host brain + plAvBrainCoop * brainH = TRACKED_NEW plAvBrainCoop(plAvBrainGeneric::kExitNormal, 3.0, 3.0, plAvBrainGeneric::kMoveRelative, guestKey); + + plAnimStage *hostOffer = TRACKED_NEW plAnimStage("BookOffer", plAnimStage::kNotifyAdvance); // autoforward, autoadvance + // repeats until the guest brain tells us that it's done + plAnimStage *hostIdle = TRACKED_NEW plAnimStage("BookOfferIdle", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, + plAnimStage::kAdvanceNone, plAnimStage::kRegressNone, -1); + + plAnimStage *hostFinish = TRACKED_NEW plAnimStage("BookOfferFinish", plAnimStage::kNotifyAdvance); // autoforward, autoadvance + + brainH->AddStage(hostOffer); + brainH->AddStage(hostIdle); + brainH->AddStage(hostFinish); + + UInt32 hostID = brainH->GetInitiatorID(); + UInt32 hostSerial = brainH->GetInitiatorSerial(); + + + // make the guest brain + plAvBrainCoop * brainG = TRACKED_NEW plAvBrainCoop(plAvBrainGeneric::kExitNormal, 3.0, 3.0, plAvBrainGeneric::kMoveRelative, + hostID, (UInt16)hostSerial, hostKey); + + plAnimStage *guestAccept = TRACKED_NEW plAnimStage("BookAccept", plAnimStage::kNotifyAdvance); + plAnimStage *guestAcceptIdle = TRACKED_NEW plAnimStage("BookAcceptIdle", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, + plAnimStage::kAdvanceNone, plAnimStage::kRegressNone, -1); + + brainG->AddStage(guestAccept); + brainG->AddStage(guestAcceptIdle); + plCoopCoordinator *coord = TRACKED_NEW plCoopCoordinator(hostKey, guestKey, brainH, brainG, "Convergence", 1, 1, linkMsg, true); + + + plAvCoopMsg *coMg = TRACKED_NEW plAvCoopMsg(hostKey, coord); + coMg->SetBCastFlag(plMessage::kNetPropagate); + coMg->SetBCastFlag(plMessage::kNetForce); + + coMg->Send(); + brainH->SetRecipient(replyKey); + brainG->SetRecipient(replyKey); + + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h new file mode 100644 index 00000000..b992003c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h @@ -0,0 +1,220 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/** \file plAvatarMgr.h + Gathering place for global animations and miscellaneous avatar data. + Ideally this stuff will all be migrated into the resource manager. */ +#ifndef PLAVATARMGR_INC +#define PLAVATARMGR_INC + +#include "hsStlUtils.h" +#include "hsStlSortUtils.h" +#include "hsGeometry3.h" + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../plMessage/plLoadAvatarMsg.h" + +// This is still pretty much a hack, but it's a compartmentalized hack instead of the previous +// interwoven spaghetti hack. + +class plSeekPointMod; +class plOneShotMod; +class plAGMasterMod; +class plArmatureMod; +class plSpawnModifier; +class plKey; +class plLoadAvatarMsg; +class plMaintainersMarkerModifier; +class plDniCoordinateInfo; +class plAvCoopMsg; +class plNotifyMsg; +class plCoopCoordinator; +class plLoadCloneMsg; +class plStatusLog; + +/** \class plAvatarMgr + Gathering place for global animations and miscellaneous avatar data. + Ideally this stuff will all be migrated into the resource manager. + This class is 100% static and must be explictly cleared when + resetting the scene on shutdown or when rebuilding the scene for export. +*/ +class plAvatarMgr : public hsKeyedObject +{ +public: + typedef std::vector plArmatureModPtrVec; + + enum AvatarTypeMask + { + Human = 1, + Player = 2 + }; + + plAvatarMgr(); // can only be constructed by itself (singleton) + virtual ~plAvatarMgr(); + + CLASSNAME_REGISTER( plAvatarMgr ); + GETINTERFACE_ANY( plAvatarMgr, hsKeyedObject ); + + // \{ + /** Seek points are alignment points used for aligning + the avatar before playing a detail interaction animation. + These are registered by name here primarily for debugging + and ad-hoc scripting. In final releases, we'll be able to + do away with this bookeeping entirely. */ + void AddSeekPoint(plSeekPointMod *seekpoint); + void RemoveSeekPoint(plSeekPointMod *seekpoint); + plSeekPointMod *FindSeekPoint(const char *name); + // \} + + // \{ + /** One shots are registered here for debugging and ad-hoc + scripting only. */ + void AddOneShot(plOneShotMod *oneshot); + void RemoveOneShot(plOneShotMod *oneshot); + plOneShotMod *FindOneShot(char *name); + // \} + + plKey LoadPlayer(const char* name, const char *account); + plKey LoadPlayer(const char* name, const char *account, const char *linkName); + plKey LoadAvatar(const char *name, const char *accountName, bool isPlayer, plKey spawnPoint, plAvTask *initialTask, const char *userStr = nil); + /** Unload an avatar - player or npc - both locally and remotely. */ + void UnLoadAvatar(plKey avKey, bool isPlayer); + /** send our (already loaded) local player to newly-associated clients - used when linking */ + void PropagateLocalPlayer(int spawnPoint = -1); + /** Unload our local player on other machines because we're leaving this age. + The player will stay around on our local machine, though. */ + bool UnPropagateLocalPlayer(); + + void UnLoadRemotePlayer(plKey playerKey); + void UnLoadLocalPlayer(); + + void AddAvatar(plArmatureMod *avatar); + void RemoveAvatar(plArmatureMod *instance); + + plArmatureMod *GetLocalAvatar(); + plKey GetLocalAvatarKey(); + static plArmatureMod *FindAvatar(plKey& avatarKey); // Key of the sceneObject + plArmatureMod *FindAvatarByPlayerID(UInt32 pid); + plArmatureMod *FindAvatarByModelName(char *name); // Probably only useful for custom NPCs. All players are + // either "Male" or "Female". + void FindAllAvatarsByModelName(const char* name, plArmatureModPtrVec& outVec); + plArmatureMod *GetFirstRemoteAvatar(); + + // \{ + /** Spawn points are potential entry points for the + avatar. They're selected pretty randomly right now; + eventually they'll be selected by script based + on the book used to enter the scene. */ + void AddSpawnPoint(plSpawnModifier *spawn); + void RemoveSpawnPoint(plSpawnModifier *spawn); + const plSpawnModifier *GetSpawnPoint(int index); + int NumSpawnPoints() { return fSpawnPoints.size(); } + int FindSpawnPoint( const char *name ) const; + // \} + static int WarpPlayerToAnother(hsBool iMove, UInt32 remoteID); + static int WarpPlayerToXYZ(hsScalar x, hsScalar y, hsScalar z); + static int WarpPlayerToXYZ(int pid, hsScalar x, hsScalar y, hsScalar z); + + static plAvatarMgr *GetInstance(); + static void ShutDown(); + + + hsBool MsgReceive(plMessage *msg); + hsBool HandleCoopMsg(plAvCoopMsg *msg); + hsBool HandleNotifyMsg(plNotifyMsg *msg); + hsBool IPassMessageToActiveCoop(plMessage *msg, UInt32 id, UInt16 serial); + + // similar to a spawn point, maintainers markers are used + // to generate your position in Dni coordinates + void AddMaintainersMarker(plMaintainersMarkerModifier *mm); + void RemoveMaintainersMarker(plMaintainersMarkerModifier *mm); + void PointToDniCoordinate(hsPoint3 pt, plDniCoordinateInfo* ret); + void GetDniCoordinate(plDniCoordinateInfo* ret); + + static void OfferLinkingBook(plKey hostKey, plKey guestKey, plMessage *linkMsg, plKey replyKey); + + bool IsACoopRunning(); + plStatusLog *GetLog() { return fLog; } + +protected: + /** Dump all internal data. */ + void IReset(); + + /** Handle an incoming clone message; do any necessary post-processing + on the avatar. */ + void plAvatarMgr::IFinishLoadingAvatar(plLoadAvatarMsg *cloneMsg); + + /** Handle an incoming clone message which holds an unload request. + */ + void plAvatarMgr::IFinishUnloadingAvatar(plLoadAvatarMsg *cloneMsg); + + /** When an armature modifier attached to the given scene object is loaded, + send it the given message. + We get notified when the avatar's scene object is loaded, but we also need to + set some information up for the avatar modifier when it comes in. + We'll get that notification via the AddAvatar call later. In this function + we're going to squirrel away an initialization message to pass to the armature + modifier when it arrives. */ + void plAvatarMgr::IDeferInit(plKey playerSOKey, plMessage *initMsg); + + /** See if we have an avatar type message saved for the given avatar and send them. */ + void plAvatarMgr::ISendDeferredInit(plKey playerSOKey); + + static plAvatarMgr* fInstance; // the single instance of the avatar manager + + typedef std::map plSeekPointMap; + plSeekPointMap fSeekPoints; + + typedef std::map plOneShotMap; + plOneShotMap fOneShots; + + typedef std::map DeferredInits; + DeferredInits fDeferredInits; + +// typedef std::map plAvatarMap; + typedef std::vector plAvatarVec; + plAvatarVec fAvatars; + + typedef std::vector plSpawnVec; + plSpawnVec fSpawnPoints; + + hsTArray fMaintainersMarkers; + + // we're using a multimap, which is a map which allows multiple entries to + // share the same key. the key we use is the initiator's player id; in the vast + // majority of cases, there will only be one coop running for a given initiator's + // ID. By using a multimap, however, we can still handle a few different coops + // for the same user by just iterating from the first match forward until + // we run out of matches. + typedef std::multimap plCoopMap; + plCoopMap fActiveCoops; + + hsTArray fCloneMsgQueue; + plStatusLog *fLog; +}; + + +#endif // PLAVATARMGR_INC + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp new file mode 100644 index 00000000..3db4cea8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.cpp @@ -0,0 +1,555 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAvatarSDLModifier.h" +#include "plArmatureMod.h" + +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvBrainGeneric.h" +#include "../plAvatar/plAvBrainClimb.h" +#include "../plAvatar/plAvBrainDrive.h" +#include "../plAvatar/plAnimStage.h" +#include "../plAvatar/plAvCallbackAction.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plSDLModifierMsg.h" +#include "../plSDL/plSDL.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../pnAsyncCore/pnAsyncCore.h" + +// static vars +char plAvatarPhysicalSDLModifier::kStrPosition[] = "position"; +char plAvatarPhysicalSDLModifier::kStrRotation[] = "rotation"; +char plAvatarPhysicalSDLModifier::kStrSubworld[] = "subworld"; + +char plAvatarSDLModifier::kStrBrainStack[] = "brainStack"; +char plAvatarSDLModifier::kStrInvisibilityLevel[] = "invisibilityLevel"; + +char plAvatarSDLModifier::StandardStageVarNames::kStrName[]="name"; +char plAvatarSDLModifier::StandardStageVarNames::kStrNumLoops[]="numLoops"; +char plAvatarSDLModifier::StandardStageVarNames::kStrForward[]="forward"; +char plAvatarSDLModifier::StandardStageVarNames::kStrBackward[]="backward"; +char plAvatarSDLModifier::StandardStageVarNames::kStrStageAdvance[]="stageAdvance"; +char plAvatarSDLModifier::StandardStageVarNames::kStrStageRegress[]="stageRegress"; +char plAvatarSDLModifier::StandardStageVarNames::kStrNotifyEnter[]="notifyEnter"; +char plAvatarSDLModifier::StandardStageVarNames::kStrNotifyLoop[]="notifyLoop"; +char plAvatarSDLModifier::StandardStageVarNames::kStrNotifyStageAdvance[]="notifyStageAdvance"; +char plAvatarSDLModifier::StandardStageVarNames::kStrNotifyStageRegress[]="notifyStageRegress"; +char plAvatarSDLModifier::StandardStageVarNames::kStrUseGlobalCoords[]="useGlobalCoords"; +char plAvatarSDLModifier::StandardStageVarNames::kStrLocalTime[]="localTime"; +char plAvatarSDLModifier::StandardStageVarNames::kStrCurrentLoop[]="currentLoop"; +char plAvatarSDLModifier::StandardStageVarNames::kStrIsAttached[]="isAttached"; + +char plAvatarSDLModifier::GenericBrainVarNames::kStrStages[]="stages"; +char plAvatarSDLModifier::GenericBrainVarNames::kStrCurrentStage[]="currentStage"; +char plAvatarSDLModifier::GenericBrainVarNames::kStrCallbackRcvr[]="callbackRcvr"; +char plAvatarSDLModifier::GenericBrainVarNames::kStrMovingForward[]="movingForward"; +char plAvatarSDLModifier::GenericBrainVarNames::kStrExitFlags[]="exitFlags"; +char plAvatarSDLModifier::GenericBrainVarNames::kStrType[]="type"; +char plAvatarSDLModifier::GenericBrainVarNames::kStrMode[]="mode"; +char plAvatarSDLModifier::GenericBrainVarNames::kStrFadeIn[]="fadeIn"; +char plAvatarSDLModifier::GenericBrainVarNames::kStrFadeOut[]="fadeOut"; +char plAvatarSDLModifier::GenericBrainVarNames::kStrMoveMode[]="moveMode"; +char plAvatarSDLModifier::GenericBrainVarNames::kStrBodyUsage[]="bodyUsage"; + +char plAvatarSDLModifier::ClimbBrainVarNames::kStrCurMode[]="curMode"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrNextMode[]="nextMode"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrAllowedDirections[]="allowedDirections"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrAllowedDismounts[]="allowedDismounts"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrVertProbeLength[]="vertProbeLength"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrHorizProbeLength[]="horizProbeLength"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageAttached[]="curStageAttached"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStage[]="curStage"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageTime[]="curStageTime"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageStrength[]="curStageStrength"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageAttached[]="exitStageAttached"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStage[]="exitStage"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageTime[]="exitStageTime"; +char plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageStrength[]="exitStageStrength"; + +char plAvatarSDLModifier::BrainUnionVarNames::kGenericBrain[]="fGenericBrain"; +char plAvatarSDLModifier::BrainUnionVarNames::kClimbBrain[]="fClimbBrain"; +char plAvatarSDLModifier::BrainUnionVarNames::kDriveBrain[]="fDriveBrain"; + +void plAvatarPhysicalSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + plSceneObject* sObj = GetTarget(); + hsAssert(sObj, "plAvatarPhysicalSDLModifier, nil target"); + if(sObj) + { + const plArmatureMod* kAvMod = (plArmatureMod*)sObj->GetModifierByType(plArmatureMod::Index()); + plArmatureMod * avMod = const_cast(kAvMod); + if(avMod && !avMod->GetCurrentBrain()->IsRunningTask()) + { + plAvBrainGeneric* genBrain = plAvBrainGeneric::ConvertNoRef(avMod->GetCurrentBrain()); + if (genBrain && (genBrain->GetType() == plAvBrainGeneric::kLadder || genBrain->GetType() == plAvBrainGeneric::kSit || genBrain->GetType() == plAvBrainGeneric::kSitOnGround)) + return; + + plSimpleStateVariable* worldVar = srcState->FindVar(kStrSubworld); + if (worldVar->IsDirty() && avMod->fController) + { + plKey worldKey; + worldVar->Get(&worldKey); + avMod->fController->SetSubworld(worldKey); + } + + plSimpleStateVariable* rotVar = srcState->FindVar(kStrRotation); + plSimpleStateVariable* posVar = srcState->FindVar(kStrPosition); + if ((rotVar->IsDirty() || posVar->IsDirty()) && avMod->GetController()) + { + hsPoint3 pos; + hsScalar zRot; + posVar->Get(&pos.fX); + rotVar->Get(&zRot); + avMod->GetController()->SetState(pos, zRot); + } + } + } +} + +void plAvatarPhysicalSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + plSceneObject* sObj = GetTarget(); + hsAssert(sObj, "plAvatarPhysicalSDLModifier, nil target"); + const plArmatureMod* kAvMod = (plArmatureMod*)sObj->GetModifierByType(plArmatureMod::Index()); + plArmatureMod * avMod = const_cast(kAvMod); + hsAssert(avMod, "nil avMod"); + + if(avMod && avMod->GetController()) + { + hsPoint3 pos; + hsScalar zRot; + avMod->GetController()->GetState(pos, zRot); + dstState->FindVar(kStrRotation)->Set(zRot); + dstState->FindVar(kStrPosition)->Set(&pos.fX); + dstState->FindVar(kStrSubworld)->Set(avMod->GetWorldKey()); + } +} + +// ISetCurrentStateFrom --------------------------------------------------------- +void plAvatarSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + plSceneObject* sObj = GetTarget(); + hsAssert(sObj, "plAvatarSDLModifier, nil target"); + if(sObj) + { + const plArmatureMod* kAvMod = (plArmatureMod*)sObj->GetModifierByType(plArmatureMod::Index()); + plArmatureMod * avMod = const_cast(kAvMod); + if(avMod) + { + ISetBaseAvatarStateFrom(avMod, srcState); + + // right now we're just going to destroy all the non-standard brains + // and rebuild them from the incoming synch state + // This is acceptable because we should only receive this state when + // we're loading another player for the first time after entering an age. + int brainCount = avMod->GetBrainCount(); + if(brainCount > 1) + { + // remove all non-default brains + for(int i = 0; i < brainCount - 1; i++) + { + plArmatureBrain* current = avMod->GetCurrentBrain(); + avMod->PopBrain(); + delete current; + } + } + + plSDStateVariable* brainsVar = srcState->FindSDVar(kStrBrainStack); + if (brainsVar->IsUsed()) + { + int nBrains = brainsVar->GetCount(); + for(int i = 0; i < nBrains; i++) + { + // get the record containing the brain union structure + plStateDataRecord* brainUnion = brainsVar->GetStateDataRecord(i); + + // get the (one element) generic brain list, if populated + plSDStateVariable *genBrainListVar = brainUnion->FindSDVar(BrainUnionVarNames::kGenericBrain); + if(genBrainListVar->IsUsed() && genBrainListVar->GetCount() > 0) + { + // get the state for the generic brain from the list (there's only allowed to be one per list + plStateDataRecord *genBrainVar = genBrainListVar->GetStateDataRecord(0); + ISetGenericBrainFrom(avMod, genBrainVar); + } else { + // get the (one element) climb brain list, if populated + plSDStateVariable *climbBrainListVar = brainUnion->FindSDVar(BrainUnionVarNames::kClimbBrain); + if(climbBrainListVar->IsUsed() && climbBrainListVar->GetCount() > 0) + { + // TEMP: Not read/writing the climb brain until it gets an overhaul + // get the state for the climb brain from the list (there's only allowed to be one per list + //plStateDataRecord *climbBrainVar = climbBrainListVar->GetStateDataRecord(0); + //ISetClimbBrainFrom(avMod, climbBrainVar); + } else { + // get the (one element) drive brain list, if populated + plSDStateVariable *driveBrainListVar = brainUnion->FindSDVar(BrainUnionVarNames::kDriveBrain); + if(driveBrainListVar->IsUsed() && driveBrainListVar->GetCount() > 0) + { + // get the state for the drive brain from the list (there's only allowed to be one per list + plStateDataRecord *driveBrainVar = driveBrainListVar->GetStateDataRecord(0); + ISetDriveBrainFrom(avMod, driveBrainVar); + } + } + } + } + } + } + } +} + +// IPutCurrentStateIn --------------------------------------------------- +// ------------------ +void plAvatarSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + plSceneObject* sObj = GetTarget(); + hsAssert(sObj, "plAvatarSDLModifier, nil target"); + const plArmatureMod* kAvMod = (plArmatureMod*)sObj->GetModifierByType(plArmatureMod::Index()); + plArmatureMod * avMod = const_cast(kAvMod); + hsAssert(avMod, "nil avMod"); + + if(avMod) + { + IPutBaseAvatarStateIn(avMod, dstState); + // create a brainUnion nested record + plSDStateVariable* brainUnionArray = dstState->FindSDVar(plAvatarSDLModifier::kStrBrainStack); + int nBrains = avMod->GetBrainCount(); + brainUnionArray->Resize(nBrains - 1); // we skip the base brain + + for (int i = 1; i < nBrains; i++) + { + plStateDataRecord *brainUnion = brainUnionArray->GetStateDataRecord(i - 1); + + // create (or get) a nested array of climbBrain records (we'll only populate the first) + plSDStateVariable* climbStateVar = brainUnion->FindSDVar(plAvatarSDLModifier::BrainUnionVarNames::kClimbBrain); + climbStateVar->Resize(0); + + // create (or get) a nested array of genbrain records (we'll only populate the first) + plSDStateVariable *genStateVar = brainUnion->FindSDVar(plAvatarSDLModifier::BrainUnionVarNames::kGenericBrain); + genStateVar->Resize(0); + + // create (or get) a nested array of driveBrain records (we'll only populate the first) + plSDStateVariable* driveStateVar = brainUnion->FindSDVar(plAvatarSDLModifier::BrainUnionVarNames::kDriveBrain); + driveStateVar->Resize(0); + + plArmatureBrain *brain = avMod->GetBrain(i); + if (plAvBrainClimb *climbBrain = plAvBrainClimb::ConvertNoRef(brain)) + { + // TEMP: Not read/writing the climb brain until it gets an overhaul + //climbStateVar->Resize(1); + //IPutClimbBrainIn(avMod, climbBrain, climbStateVar->GetStateDataRecord(0)); + } + else + if(plAvBrainGeneric *genBrain = plAvBrainGeneric::ConvertNoRef(brain)) + { + genStateVar->Resize(1); + // put the brain into the first slot in the array + IPutGenericBrainIn(avMod, genBrain, genStateVar->GetStateDataRecord(0)); + } + else + if (plAvBrainDrive *driveBrain = plAvBrainDrive::ConvertNoRef(brain)) + { + driveStateVar->Resize(1); + IPutDriveBrainIn(avMod, driveBrain, driveStateVar->GetStateDataRecord(0)); + } + } + } +} + +void plAvatarSDLModifier::IPutClimbBrainIn(plArmatureMod *avMod, plAvBrainClimb *brain, plStateDataRecord* dstState) +{ + brain->SaveToSDL(dstState); +} + +void plAvatarSDLModifier::ISetClimbBrainFrom(plArmatureMod *avMod, const plStateDataRecord* srcState) +{ + plAvBrainClimb *climbBrain = TRACKED_NEW plAvBrainClimb(); + avMod->PushBrain(climbBrain); + climbBrain->LoadFromSDL(srcState); +} + +void plAvatarSDLModifier::IPutDriveBrainIn(plArmatureMod *avMod, plAvBrainDrive *brain, plStateDataRecord* dstState) +{ + dstState->FindVar("unUsed")->Set(0); +} + +void plAvatarSDLModifier::ISetDriveBrainFrom(plArmatureMod *avMod, const plStateDataRecord* src) +{ + plAvBrainDrive *driveBrain = TRACKED_NEW plAvBrainDrive(); + avMod->PushBrain(driveBrain); +} + +// IPutGenericBrainIn -------------------------------------------------------------------------- +void plAvatarSDLModifier::IPutGenericBrainIn(plArmatureMod * avMod, plAvBrainGeneric *genBrain, plStateDataRecord* dstState) +{ + // get state of the brain: + dstState->FindVar(GenericBrainVarNames::kStrCurrentStage)->Set(genBrain->GetCurStageNum()); + dstState->FindVar(GenericBrainVarNames::kStrMovingForward)->Set(genBrain->GetForward()); + dstState->FindVar(GenericBrainVarNames::kStrCallbackRcvr)->Set(genBrain->GetRecipient()); + dstState->FindVar(GenericBrainVarNames::kStrExitFlags)->Set((int)genBrain->GetExitFlags()); + + dstState->FindVar(GenericBrainVarNames::kStrType)->Set((int)genBrain->GetType()); + dstState->FindVar(GenericBrainVarNames::kStrMode)->Set((int)genBrain->GetMode()); + dstState->FindVar(GenericBrainVarNames::kStrFadeIn)->Set(genBrain->GetFadeIn()); + dstState->FindVar(GenericBrainVarNames::kStrFadeOut)->Set(genBrain->GetFadeOut()); + dstState->FindVar(GenericBrainVarNames::kStrMoveMode)->Set((int)genBrain->GetMoveMode()); + dstState->FindVar(GenericBrainVarNames::kStrBodyUsage)->Set((int)genBrain->GetBodyUsage()); + + // let's fill in the stages + int stageCount = genBrain->GetStageCount(); + plSDStateVariable* stagesVar = dstState->FindSDVar(GenericBrainVarNames::kStrStages); + stagesVar->Resize(stageCount); + + for(int i = 0; i < stageCount; i++) + { + plAnimStage *stage = genBrain->GetStage(i); + IPutStageIn(avMod, stage, stagesVar->GetStateDataRecord(i)); + } +} + +// ISetGenericBrainFrom ------------------------------------------------------------------------------- +bool plAvatarSDLModifier::ISetGenericBrainFrom(plArmatureMod *avMod, const plStateDataRecord* srcState) +{ + int i; + hsBool success = true; + int numStages=0; + plSDStateVariable* stagesVar = srcState->FindSDVar(GenericBrainVarNames::kStrStages); + if (stagesVar->IsUsed()) + { + numStages = stagesVar->GetCount(); + if (!numStages) + return false; + } + + plAnimStageVec * stages = TRACKED_NEW plAnimStageVec(); + for (int j = 0; j < numStages; j++) + { + plStateDataRecord* stageState = stagesVar->GetStateDataRecord(j); + plAnimStage *newStage = IGetStageFrom(avMod, stageState); + if (!newStage) + success = false; + + stages->push_back(newStage); + } + + int curStage; + srcState->FindVar(GenericBrainVarNames::kStrCurrentStage)->Get(&curStage); + if (curStage >= numStages) + success = false; + + plKey callbackRcvr; + srcState->FindVar(GenericBrainVarNames::kStrCallbackRcvr)->Get(&callbackRcvr); + + int exitFlags; + srcState->FindVar(GenericBrainVarNames::kStrExitFlags)->Get(&exitFlags); + if (exitFlags >= plAvBrainGeneric::kExitMaxFlag) + success = false; + + int type; + srcState->FindVar(GenericBrainVarNames::kStrType)->Get(&type); + if (type >= plAvBrainGeneric::kNumBrainTypes) + success = false; + + int mode; + srcState->FindVar(GenericBrainVarNames::kStrMode)->Get(&mode); + if (mode >= plAvBrainGeneric::kMaxMode) + success = false; + + float fadeIn, fadeOut; + srcState->FindVar(GenericBrainVarNames::kStrFadeIn)->Get(&fadeIn); + srcState->FindVar(GenericBrainVarNames::kStrFadeOut)->Get(&fadeOut); + + int moveMode; + srcState->FindVar(GenericBrainVarNames::kStrMoveMode)->Get(&moveMode); + if (moveMode >= plAvBrainGeneric::kMaxMoveMode) + success = false; + + int bodyUsage; + srcState->FindVar(GenericBrainVarNames::kStrBodyUsage)->Get(&bodyUsage); + if (bodyUsage >= plAGAnim::kBodyMax) + success = false; + + if (!success) + { + for (i = 0; i < numStages; i++) + { + plAnimStage *s = (*stages)[i]; + (*stages)[i] = nil; + delete s; + } + delete stages; + return false; + } + + plAvBrainGeneric *newBrain = + TRACKED_NEW plAvBrainGeneric(stages, + nil, nil, + callbackRcvr, + exitFlags, + fadeIn, + fadeOut, + static_cast(moveMode)); + newBrain->SetType(static_cast(type)); + newBrain->SetBodyUsage(static_cast(bodyUsage)); + newBrain->SetCurStageNum(curStage); + + avMod->PushBrain(newBrain); + + return true; +} + +// IGetStageFrom ---------------------------------------------------------------------------------------- +plAnimStage * plAvatarSDLModifier::IGetStageFrom(plArmatureMod *avMod, const plStateDataRecord* srcState) +{ + UInt32 notifyFlags=0; + + bool notifyEnter, notifyLoop, notifyAdv, notifyRegress; + if (srcState->FindVar(StandardStageVarNames::kStrNotifyEnter)->Get(¬ifyEnter)) + if(notifyEnter) + notifyFlags &= plAnimStage::kNotifyEnter; + + if (srcState->FindVar(StandardStageVarNames::kStrNotifyLoop)->Get(¬ifyLoop)) + if(notifyLoop) + notifyFlags &= plAnimStage::kNotifyLoop; + + if (srcState->FindVar(StandardStageVarNames::kStrNotifyStageAdvance)->Get(¬ifyAdv)) + if(notifyAdv) + notifyFlags &= plAnimStage::kNotifyAdvance; + + if (srcState->FindVar(StandardStageVarNames::kStrNotifyStageRegress)->Get(¬ifyRegress)) + if(notifyRegress) + notifyFlags &= plAnimStage::kNotifyRegress; + + bool useLocal; + if (srcState->FindVar(StandardStageVarNames::kStrUseGlobalCoords)->Get(&useLocal)) + useLocal = !useLocal; + + char name[32]; + srcState->FindVar(StandardStageVarNames::kStrName)->Get(name); + plAGAnim *anim = avMod->FindCustomAnim(name); + if (!anim) + return nil; + + int fwd, bwd, adv, reg; + srcState->FindVar(StandardStageVarNames::kStrForward)->Get(&fwd); + if (fwd >= plAnimStage::kForwardMax) + return nil; + + srcState->FindVar(StandardStageVarNames::kStrBackward)->Get(&bwd); + if (bwd >= plAnimStage::kBackMax) + return nil; + + srcState->FindVar(StandardStageVarNames::kStrStageAdvance)->Get(&adv); + if (adv >= plAnimStage::kAdvanceMax) + return nil; + + srcState->FindVar(StandardStageVarNames::kStrStageRegress)->Get(®); + if (reg >= plAnimStage::kRegressMax) + return nil; + + int numLoops; + srcState->FindVar(StandardStageVarNames::kStrNumLoops)->Get(&numLoops); + + float localTime; + srcState->FindVar(StandardStageVarNames::kStrLocalTime)->Get(&localTime); + + int curLoop; + srcState->FindVar(StandardStageVarNames::kStrCurrentLoop)->Get(&curLoop); + if (curLoop > numLoops && numLoops > 0) // numLoops == -1 means infinite looping + return nil; + + bool isAttached; + srcState->FindVar(StandardStageVarNames::kStrIsAttached)->Get(&isAttached); + + // ***!!! need to capture "advanceTo" and "regressTo" values!!! + bool hackAdvanceToWrong = false, hackRegressToWrong = false; + plAnimStage *newStage = TRACKED_NEW plAnimStage(name, + (UInt8)notifyFlags, + static_cast(fwd), + static_cast(bwd), + static_cast(adv), + static_cast(reg), + numLoops, hackAdvanceToWrong, 0, hackRegressToWrong, 0); + + newStage->SetLocalTime(localTime); + newStage->SetLoopValue(curLoop); + newStage->SetIsAttached(isAttached); + + return newStage; +} + +// IPutStageIn --------------------------------------------------------------------------------------------- +bool plAvatarSDLModifier::IPutStageIn(plArmatureMod *avMod, plAnimStage *stage, plStateDataRecord* dstState) +{ + if(stage) + { + dstState->FindVar(StandardStageVarNames::kStrName)->Set(stage->GetAnimName()); + dstState->FindVar(StandardStageVarNames::kStrNumLoops)->Set(stage->GetNumLoops()); + dstState->FindVar(StandardStageVarNames::kStrForward)->Set((int)stage->GetForwardType()); + dstState->FindVar(StandardStageVarNames::kStrBackward)->Set((int)stage->GetBackType()); + dstState->FindVar(StandardStageVarNames::kStrStageAdvance)->Set((int)stage->GetAdvanceType()); + dstState->FindVar(StandardStageVarNames::kStrStageRegress)->Set((int)stage->GetRegressType()); + + UInt32 notifies = stage->GetNotifyFlags(); + dstState->FindVar(StandardStageVarNames::kStrNotifyEnter)->Set((notifies & plAnimStage::kNotifyEnter) ? true : false); + dstState->FindVar(StandardStageVarNames::kStrNotifyLoop)->Set((notifies & plAnimStage::kNotifyLoop) ? true : false); + dstState->FindVar(StandardStageVarNames::kStrNotifyStageAdvance)->Set((notifies & plAnimStage::kNotifyAdvance) ? true : false); + dstState->FindVar(StandardStageVarNames::kStrNotifyStageRegress)->Set((notifies & plAnimStage::kNotifyRegress) ? true : false); + +// XXX dstState->FindVar(StandardStageVarNames::kStrUseGlobalCoords)->Set(stage->GetIsLocal()); + dstState->FindVar(StandardStageVarNames::kStrLocalTime)->Set(stage->GetLocalTime()); + + //dstState->FindVar(StandardStageVarNames::kStrLength)->Set(0.0f); // NOT CURRENTLY USED + // Well, if we're not using it, why are we still dirtying it and sending it across? Commented out. + + dstState->FindVar(StandardStageVarNames::kStrCurrentLoop)->Set(stage->GetLoopValue()); + dstState->FindVar(StandardStageVarNames::kStrIsAttached)->Set(stage->GetIsAttached()); + return true; + } + + return false; +} + +// IPutBaseAvatarStateIn ------------------------------------------------------------------------- +void plAvatarSDLModifier::IPutBaseAvatarStateIn(plArmatureMod *avMod, plStateDataRecord* dstState) +{ + if (avMod->GetStealthLevel() > 0) + LogMsg(kLogDebug, "plAvatarSDLModifier::IPutBaseAvatarStateIn - Stealth level being set greater than zero"); + dstState->FindVar(kStrInvisibilityLevel)->Set(avMod->GetStealthLevel()); +} + +// ISetBaseAvatarStateFrom ------------------------------------------------------------------------------- +void plAvatarSDLModifier::ISetBaseAvatarStateFrom(plArmatureMod *avMod, const plStateDataRecord* srcState) +{ + // invisibility + if (!avMod->IsLocallyOwned()) + { + int invisLevel; + srcState->FindVar(kStrInvisibilityLevel)->Get(&invisLevel); + + if (invisLevel > 0) + LogMsg(kLogDebug, "plAvatarSDLModifier::ISetBaseAvatarStateFrom - Stealth level greater than zero"); + plNetClientMgr::GetInstance()->MakeCCRInvisible(avMod->GetTarget(0)->GetKey(), invisLevel); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.h new file mode 100644 index 00000000..9768ff46 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarSDLModifier.h @@ -0,0 +1,167 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAvatarSDLModifier_inc +#define plAvatarSDLModifier_inc + +#include "hsConfig.h" +#include "hsStlUtils.h" +#include "../plModifier/plSDLModifier.h" + + +// +// This modifier is responsible for sending and recving +// an avatar's state +// +class plStateDataRecord; +class plArmatureMod; +class plAnimStage; +class plAvBrainGeneric; +class plAvBrainClimb; +class plAvBrainDrive; + +class plAvatarPhysicalSDLModifier : public plSDLModifier +{ +protected: + static char kStrPosition[]; + static char kStrRotation[]; + static char kStrSubworld[]; + + void ISetCurrentStateFrom(const plStateDataRecord* srcState); + void IPutCurrentStateIn(plStateDataRecord* dstState); + + UInt32 IApplyModFlags(UInt32 sendFlags) { return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState); } + +public: + CLASSNAME_REGISTER( plAvatarPhysicalSDLModifier ); + GETINTERFACE_ANY( plAvatarPhysicalSDLModifier, plSDLModifier); + + const char* GetSDLName() const { return kSDLAvatarPhysical; } + +}; + +class plAvatarSDLModifier : public plSDLModifier +{ +protected: + static char kStrBrainStack[]; + static char kStrInvisibilityLevel[]; + + // var labels + struct StandardStageVarNames + { + static char kStrName[]; + static char kStrNumLoops[]; + static char kStrForward[]; + static char kStrBackward[]; + static char kStrStageAdvance[]; + static char kStrStageRegress[]; + static char kStrNotifyEnter[]; + static char kStrNotifyLoop[]; + static char kStrNotifyStageAdvance[]; + static char kStrNotifyStageRegress[]; + static char kStrUseGlobalCoords[]; + static char kStrLocalTime[]; + static char kStrCurrentLoop[]; + static char kStrIsAttached[]; + }; + + struct GenericBrainVarNames + { + static char kStrStages[]; + static char kStrCurrentStage[]; + static char kStrFreezePhysicalAtEnd[]; + static char kStrCallbackRcvr[]; + static char kStrMovingForward[]; + static char kStrExitFlags[]; + static char kStrType[]; + static char kStrMode[]; + static char kStrFadeIn[]; + static char kStrFadeOut[]; + static char kStrMoveMode[]; + static char kStrBodyUsage[]; + }; + + + struct BrainUnionVarNames + { + static char kGenericBrain[]; + static char kClimbBrain[]; + static char kDriveBrain[]; + }; + + /** Top level: gets the current avatar brain state for any number of brains + of varying type from the data record into the avatar. + Will destroy all optional brains and recreate them from the incoming SDL. */ + void ISetCurrentStateFrom(const plStateDataRecord* srcState); + /** Set the base state for the avatar. This primarily consists of the state of the + human brain, which is required for all avatars. */ + void ISetBaseAvatarStateFrom(plArmatureMod *avMod, const plStateDataRecord* src); + /** We found a generic brain in the SDL stream. Build the corresponding brain and attach it. */ + bool ISetGenericBrainFrom(plArmatureMod *avMod, const plStateDataRecord* srcState); + /** We found a climb brain in the SDL stream. Build the corresponding brain and attach it. */ + void ISetClimbBrainFrom(plArmatureMod *avMod, const plStateDataRecord* src); + /** We found a drive brain in the SDL stream. Build the corresponding brain and attach it. */ + void ISetDriveBrainFrom(plArmatureMod *avMod, const plStateDataRecord* src); + /** Generic brains have multiple "stages" parse *one* out of the SDL stream and return it. */ + plAnimStage * IGetStageFrom(plArmatureMod *avMod, const plStateDataRecord* srcState); + + void IPutCurrentStateIn(plStateDataRecord* dstState); + void IPutBaseAvatarStateIn(plArmatureMod *avMod, plStateDataRecord* dst); + void IPutGenericBrainIn(plArmatureMod *avMod, plAvBrainGeneric *brain, plStateDataRecord* dstState); + void IPutClimbBrainIn(plArmatureMod *avMod, plAvBrainClimb *brain, plStateDataRecord* dstState); + void IPutDriveBrainIn(plArmatureMod *avMod, plAvBrainDrive *brain, plStateDataRecord* dstState); + bool IPutStageIn(plArmatureMod *avMod, plAnimStage *stage, plStateDataRecord* dstState); + + UInt32 IApplyModFlags(UInt32 sendFlags) { return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState); } + +public: + CLASSNAME_REGISTER( plAvatarSDLModifier ); + GETINTERFACE_ANY( plAvatarSDLModifier, plSDLModifier); + + const char* GetSDLName() const { return kSDLAvatar; } + + // trying a new approach, which is to let the climb brain read its own SDL. + // this allows us to synch protected data members with either creating a friend + // relationship or punching a lot of hole's in the object's encapsulation + struct ClimbBrainVarNames + { + static char kStrCurMode[]; + static char kStrNextMode[]; + static char kStrAllowedDirections[]; + static char kStrAllowedDismounts[]; + static char kStrVertProbeLength[]; + static char kStrHorizProbeLength[]; + static char kStrCurStageAttached[]; + static char kStrCurStage[]; + static char kStrCurStageTime[]; + static char kStrCurStageStrength[]; + static char kStrExitStageAttached[]; + static char kStrExitStage[]; + static char kStrExitStageTime[]; + static char kStrExitStageStrength[]; + }; +}; + +#endif // plAvatarSDLModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp new file mode 100644 index 00000000..23b29a7b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.cpp @@ -0,0 +1,834 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" +#include "hsWindows.h" + +// singular +#include "plAvatarTasks.h" + +// local +#include "plArmatureMod.h" +#include "plSeekPointMod.h" +#include "plAvBrainHuman.h" +#include "plAGAnim.h" +#include "plAGAnimInstance.h" +#include "plAGModifier.h" +#include "plMatrixChannel.h" +#include "plAvCallbackAction.h" +#include "plAvatarMgr.h" + +// global +#include "hsUtils.h" + +// other +#include "plgDispatch.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plOneShotCallbacks.h" +#include "../plMessage/plConsoleMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plPipeline/plDebugText.h" +#include "../plInputCore/plInputInterfaceMgr.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetCommon/plNetCommon.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../pfMessage/pfKIMsg.h" + +// for console hack +hsBool plAvOneShotTask::fForce3rdPerson = true; +#include "../pnMessage/plCameraMsg.h" + +///////////// +// +// PLAVTASK +// Abstract definition for the avatar task class +// +///////////// +plAvTask::plAvTask() +{ +} + +// START +hsBool plAvTask::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + return true; // true indicates the task has started succesfully +} + +// PROCESS +hsBool plAvTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + return false; +} + +// Finish ----------------------------------------------------------------------------------- +// ------- +void plAvTask::Finish(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ +} + + +// DUMPDEBUG +void plAvTask::DumpDebug(const char *name, int &x, int&y, int lineHeight, char *strBuf, plDebugText &debugTxt) +{ + debugTxt.DrawString(x, y, ""); + y += lineHeight; +} + +// READ +void plAvTask::Read(hsStream *stream, hsResMgr *mgr) +{ + plCreatable::Read(stream, mgr); +} + +// WRITE +void plAvTask::Write(hsStream *stream, hsResMgr *mgr) +{ + plCreatable::Write(stream, mgr); +} + +void plAvTask::ILimitPlayersInput(plArmatureMod *avatar) +{ + // make sure this is the local avatar we are talking about + if (avatar == plAvatarMgr::GetInstance()->GetLocalAvatar()) + { + plInputInterfaceMgr::GetInstance()->ForceCursorHidden(true); + // tell the KI to be disabled while we are busy + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kTempDisableKIandBB); + plgDispatch::MsgSend( msg ); + } +} + +void plAvTask::IUndoLimitPlayersInput(plArmatureMod *avatar) +{ + // make sure this is the local avatar we are talking about + if (avatar == plAvatarMgr::GetInstance()->GetLocalAvatar()) + { + plInputInterfaceMgr::GetInstance()->ForceCursorHidden(false); + // tell the KI to be re-enabled + pfKIMsg* msg = TRACKED_NEW pfKIMsg(pfKIMsg::kTempEnableKIandBB); + plgDispatch::MsgSend( msg ); + } +} + +///////////// +// +// AVSEEKTASK +// +///////////// + +// CTOR default +plAvSeekTask::plAvSeekTask() +: fAnimName(nil), + fAlign(kAlignHandle), + fDuration(0.25f), + fTarget(nil), + fAnimInstance(nil), + fTargetTime(nil), + fPhysicalAtStart(false), + fCleanup(false) +{ +} + +// CTOR target, align, animName +plAvSeekTask::plAvSeekTask(plKey target, plAvAlignment align, const char *animName) +: fAlign(align), + fDuration(0.25f), + fTarget(target), + fAnimInstance(nil), + fTargetTime(nil), + fPhysicalAtStart(false), + fCleanup(false) +{ + fAnimName = hsStrcpy(animName); +} + +// CTOR target +plAvSeekTask::plAvSeekTask(plKey target) +: fAnimName(nil), +fAlign(kAlignHandle), +fDuration(0.25f), +fTarget(target), +fAnimInstance(nil), +fTargetTime(nil), +fPhysicalAtStart(false), +fCleanup(false) +{ +} + +void GetPositionAndRotation(hsMatrix44 transform, hsScalarTriple *position, hsQuat *rotation) +{ + hsPoint3 p = (hsPoint3)transform.GetTranslate(); + position->fX = p.fX; position->fY = p.fY; position->fZ = p.fZ; + + + transform.RemoveScale(); + + rotation->SetFromMatrix(&transform); + rotation->Normalize(); + + float angle; + hsVector3 axis; + + rotation->GetAngleAxis(&angle, &axis); +} + +// START +// Adjust our goal time based on our duration and the current time +hsBool plAvSeekTask::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + fTargetTime = time + fDuration; // clock starts now.... + fPhysicalAtStart = avatar->IsPhysicsEnabled(); + avatar->EnablePhysics(false); // always turn physics off for seek + plAvBrainHuman *huBrain = plAvBrainHuman::ConvertNoRef(brain); + if(huBrain) + huBrain->IdleOnly(); + + ILimitPlayersInput(avatar); + + if (!fTarget || !fTarget->ObjectIsLoaded()) + { + fCleanup = true; + return true; + } + + plSceneObject* seekTarget = plSceneObject::ConvertNoRef(fTarget->ObjectIsLoaded()); + hsMatrix44 targetL2W = seekTarget->GetLocalToWorld(); + const plCoordinateInterface* subworldCI = nil; + if (avatar->GetController()) + subworldCI = avatar->GetController()->GetSubworldCI(); + if (subworldCI) + targetL2W = subworldCI->GetWorldToLocal() * targetL2W; + + switch(fAlign) + { + // just match our handle to the target matrix + case kAlignHandle: + // targetL2Sim is already correct + break; + // match our handle to the target matrix at the end of the given animation + case kAlignHandleAnimEnd: + { + hsMatrix44 adjustment; + plAGAnim *anim = avatar->FindCustomAnim(fAnimName); + GetStartToEndTransform(anim, nil, &adjustment, "Handle"); // actually getting end-to-start + targetL2W = targetL2W * adjustment; + } + break; + default: + break; + }; + + GetPositionAndRotation(targetL2W, &fTargetPosition, &fTargetRotation); + Process(avatar, brain, time, elapsed); + return true; +} + +// CALCHANDLETARGETPOSITION +void CalcHandleTargetPosition(hsMatrix44 &result, plSceneObject *handle, plSceneObject *target, hsMatrix44 &bodyToHandle) +{ + hsMatrix44 targetToWorld = target->GetLocalToWorld(); + + result = bodyToHandle * targetToWorld; +} + +// CALCHANDLETARGETPOSITION +// where should I move my insertion point so that my bodyRoot lines up with the target? +void CalcHandleTargetPosition(hsMatrix44 &result, plSceneObject *insert, plSceneObject *target, plSceneObject *bodyRoot) +{ + hsMatrix44 bodyToHandle = bodyRoot->GetLocalToParent(); + CalcHandleTargetPosition(result, insert, target, bodyToHandle); +} + +// PROCESS +// Move closer to the goal position and orientation +hsBool plAvSeekTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + hsQuat rotation; + hsPoint3 position; + avatar->GetPositionAndRotationSim(&position, &rotation); + + // We've had a history of odd bugs caused by assuming a rotation quat is normalized. + // This line here seems to be fixing one of them. (Avatars scaling oddly when smart seeking.) + rotation.Normalize(); + + double timeToGo = fTargetTime - time - elapsed; // time from *beginning* of this interval to the goal + if (fCleanup) + { + avatar->EnablePhysics( fPhysicalAtStart ); + IUndoLimitPlayersInput(avatar); + + return false; // we're done processing + } + else if(timeToGo < .01) + { + fTargetRotation.Normalize(); + avatar->SetPositionAndRotationSim(&fTargetPosition, &fTargetRotation); + fCleanup = true; // we're going to wait one frame for the transform to propagate + return true; // still running until next frame/cleanup + } + else + { + hsPoint3 posToGo = fTargetPosition - position; // vec from here to the goal + float thisPercentage = (float)(elapsed / timeToGo); + + hsPoint3 newPosition = position + posToGo * thisPercentage; + hsQuat newRotation; + newRotation.SetFromSlerp(rotation, fTargetRotation, thisPercentage); + + newRotation.Normalize(); + avatar->SetPositionAndRotationSim(&newPosition, &newRotation); + return true; // we're still processing + } +} + +void plAvSeekTask::LeaveAge(plArmatureMod *avatar) +{ + fTarget = nil; + fCleanup = true; +} + +/////////////////// +// +// PLAVANIMTASK +// +/////////////////// + +// CTOR default +plAvAnimTask::plAvAnimTask() +: fAnimName(nil), + fInitialBlend(0.0f), + fTargetBlend(0.0f), + fFadeSpeed(0.0f), + fSetTime(0.0f), + fStart(false), + fLoop(false), + fAttach(false), + fAnimInstance(nil) +{ +} + +// CTOR animName, initialBlend, targetBlend, fadeSpeed, start, loop, attach +plAvAnimTask::plAvAnimTask(const char *animName, + hsScalar initialBlend, + hsScalar targetBlend, + hsScalar fadeSpeed, + hsScalar setTime, + hsBool start, + hsBool loop, + hsBool attach) +: fInitialBlend(initialBlend), + fTargetBlend(targetBlend), + fFadeSpeed(fadeSpeed), + fSetTime(setTime), + fStart(start), + fLoop(loop), + fAttach(attach), + fAnimInstance(nil) +{ + if(animName) + fAnimName = hsStrcpy(animName); +} + +// CTOR animName, fadeSpeed, attach +plAvAnimTask::plAvAnimTask(const char *animName, hsScalar fadeSpeed, hsBool attach) +: fInitialBlend(0.0f), + fTargetBlend(0.0f), + fFadeSpeed(fadeSpeed), + fSetTime(0.0f), + fStart(false), + fLoop(false), + fAttach(attach), + fAnimInstance(nil) +{ + if(animName) + fAnimName = hsStrcpy(animName); +} + + + + +// DTOR +plAvAnimTask::~plAvAnimTask() +{ + if(fAnimName) + { + delete[] fAnimName; + fAnimName = nil; + } +} + +// START +hsBool plAvAnimTask::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + hsBool result = false; + if(fAttach) + { + plAGAnimInstance * aInstance = avatar->FindOrAttachInstance(fAnimName, fInitialBlend); + + if(aInstance) + { + if(fStart) + aInstance->Start(fStart); + if(fSetTime > 0) + aInstance->SetCurrentTime(fSetTime, true); + if(fTargetBlend > fInitialBlend) + { + aInstance->Fade(fTargetBlend, fFadeSpeed); + } + aInstance->SetLoop(fLoop); + + result = true; + } + else + { + hsStatusMessageF("Couldn't find animation <%s> for plAvAnimTask: will try again", fAnimName); + } + } + else + { + fAnimInstance = avatar->FindAnimInstance(fAnimName); + if(fAnimInstance) + { + // start fading towards zero + fAnimInstance->Fade(0.0, fFadeSpeed); + } + // if we started the fade, we're done and ready to process + // if we couldn't find the animation, we're still done. + result = true; + } + return result; +} + +// PROCESS +plAvAnimTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + // the only reason we need this function is to watch the animation until it fades out + hsBool result = false; + if(fAttach) + { + // we finished in the Start() function + } + else + { + if(fAnimInstance) + { + if(fAnimInstance->GetBlend() < 0.1) + { + avatar->DetachAnimation(fAnimInstance); + } + else + { + // still waiting for the fadeout; keep the task alive + result = true; + } + } + } + return result; +} + +// LEAVEAGE +void plAvAnimTask::LeaveAge(plArmatureMod *avatar) +{ + // if we are supposed to be removing the animation anyway, kill it completely on link out + if (!fAttach) + { + fAnimInstance = avatar->FindAnimInstance(fAnimName); + if(fAnimInstance) + avatar->DetachAnimation(fAnimInstance); + } +} + +// READ +void plAvAnimTask::Read(hsStream *stream, hsResMgr *mgr) +{ + fAnimName = stream->ReadSafeString(); + fInitialBlend = stream->ReadSwapScalar(); + fTargetBlend = stream->ReadSwapScalar(); + fFadeSpeed = stream->ReadSwapScalar(); + fSetTime = stream->ReadSwapScalar(); + fStart = stream->ReadBool(); + fLoop = stream->ReadBool(); + fAttach = stream->ReadBool(); +} + +// WRITE +void plAvAnimTask::Write(hsStream *stream, hsResMgr *mgr) +{ + stream->WriteSafeString(fAnimName); + stream->WriteSwapScalar(fInitialBlend); + stream->WriteSwapScalar(fTargetBlend); + stream->WriteSwapScalar(fFadeSpeed); + stream->WriteSwapScalar(fSetTime); + stream->WriteBool(fStart); + stream->WriteBool(fLoop); + stream->WriteBool(fAttach); +} + +//////////////// +// +// AVONESHOTTASK +// OBSOLETE -- DEPRECATED +// +//////////////// + +void plAvOneShotTask::InitDefaults() +{ + fBackwards = false; + fDisableLooping = false; + fDisablePhysics = true; + fAnimName = nil; + fMoveHandle = false; + fAnimInstance = nil; + fDrivable = false; + fReversible = false; + fEnablePhysicsAtEnd = false; + fDetachAnimation = false; + fIgnore = false; + fCallbacks = nil; + fWaitFrames = 0; +} + +// CTOR default +plAvOneShotTask::plAvOneShotTask() +{ + InitDefaults(); +} + +// CTOR (animName, drivable, reversible) +// this construct is typically used when you want to create a one-shot task as part of a sequence +// of tasks +// it's different than the message-based constructor in that fDetachAnimation and fMoveHandle default to false +plAvOneShotTask::plAvOneShotTask(const char *animName, hsBool drivable, hsBool reversible, plOneShotCallbacks *callbacks) +{ + InitDefaults(); + + fDrivable = drivable; + fReversible = reversible; + fCallbacks = callbacks; + + // we're going to use this sometime in the future, better ref it so someone else doesn't release it + hsRefCnt_SafeRef(fCallbacks); + fAnimName = hsStrcpy(animName); +} + +// CTOR (plAvOneShotMsg, plArmatureMod) +// this constructor is typically used when we're doing a classic, isolated one-shot +// fDetachAnimation and fMoveHandle both default to *true* +plAvOneShotTask::plAvOneShotTask (plAvOneShotMsg *msg, plArmatureMod *avatar, plArmatureBrain *brain) +{ + InitDefaults(); + + fDrivable = msg->fDrivable; + fReversible = msg->fReversible; + fCallbacks = msg->fCallbacks; + fDetachAnimation = true; + fMoveHandle = true; + + // we're going to use this sometime in the future, better ref it so someone else doesn't release it + hsRefCnt_SafeRef(fCallbacks); + fAnimName = hsStrcpy(msg->fAnimName); +} + +// DTOR +plAvOneShotTask::~plAvOneShotTask() +{ + if(fAnimName) + delete[] fAnimName; + hsRefCnt_SafeUnRef(fCallbacks); +} + + +// START +hsBool plAvOneShotTask::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + hsBool result = false; + + if (fIgnore) + return true; + + plAGMasterMod * master = avatar; + + fAnimInstance = master->AttachAnimationBlended(fAnimName, 0); + fDetachAnimation = true; + + if(fAnimInstance) + { + fEnablePhysicsAtEnd = (avatar->IsPhysicsEnabled() && fDisablePhysics); + if (fEnablePhysicsAtEnd) + { + // Must do the physics re-enable through a callback so that it happens before the "done" callback and we don't + // step over some script's attempt to disable physics again. + plAvatarPhysicsEnableCallbackMsg *epMsg = TRACKED_NEW plAvatarPhysicsEnableCallbackMsg(avatar->GetKey(), kStop, 0, 0, 0, 0); + fAnimInstance->GetTimeConvert()->AddCallback(epMsg); + hsRefCnt_SafeUnRef(epMsg); + } + + if (fCallbacks) + { + fAnimInstance->AttachCallbacks(fCallbacks); + // ok, we're done with it, release it back to the river + hsRefCnt_SafeUnRef(fCallbacks); + fCallbacks = nil; + } + + fAnimInstance->SetBlend(1.0f); + fAnimInstance->SetSpeed(1.0f); + plAnimTimeConvert *atc = fAnimInstance->GetTimeConvert(); + if (fBackwards) + atc->Backwards(); + if (fDisableLooping) + atc->Loop(false); + + fAnimInstance->SetCurrentTime(fBackwards ? atc->GetEnd() : atc->GetBegin(), true); + fAnimInstance->Start(time); + + fWaitFrames = 2; // wait two frames after animation finishes before finalizing + + + if (fDisablePhysics) + avatar->EnablePhysics(false); + + ILimitPlayersInput(avatar); + + // this is for a console command hack + if (plAvOneShotTask::fForce3rdPerson && avatar->IsLocalAvatar()) + { + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, false); + pMsg->SetCmd(plCameraMsg::kResponderSetThirdPerson); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + + fMoveHandle = (fAnimInstance->GetAnimation()->GetChannel("Handle") != nil); + if(fMoveHandle) + { + plMatrixDifferenceApp *differ = avatar->GetRootAnimator(); + differ->Reset(time); // throw away any old state + differ->Enable(true); + } + + avatar->ApplyAnimations(time, elapsed); + + result = true; + } + else + { + char buf[256]; + sprintf(buf, "Oneshot: Can't find animation <%s>; all bets are off.", fAnimName); + hsAssert(false, buf); + result = true; + } + return result; +} + +// PROCESS +plAvOneShotTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + // *** if we are under mouse control, adjust it here + + avatar->ApplyAnimations(time, elapsed); + if(fAnimInstance) + { + if(fAnimInstance->IsFinished()) + { + const plAGAnim * animation = fAnimInstance->GetAnimation(); + double endTime = (fBackwards ? animation->GetStart() : animation->GetEnd()); + fAnimInstance->SetCurrentTime((hsScalar)endTime); + avatar->ApplyAnimations(time, elapsed); + + if(--fWaitFrames == 0) + { + + plSceneObject *handle = avatar->GetTarget(0); + + avatar->DetachAnimation(fAnimInstance); + avatar->GetRootAnimator()->Enable(false); + plAvBrainHuman *humanBrain = plAvBrainHuman::ConvertNoRef(brain); + if(fEnablePhysicsAtEnd) + { +#if 0//ndef PLASMA_EXTERNAL_RELEASE + if (!humanBrain || humanBrain->fCallbackAction->HitGroundInThisAge()) + { + // For some reason, calling CheckValidPosition at the beginning of + // an age can cause detectors to incorrectly report collisions. So + // we only call this if we're in the age. + // + // It's only debugging code anyway to help the artist check that + // their oneshot doesn't end while penetrating geometry. + char *overlaps = nil; + if (avatar->GetPhysical()) + avatar->GetPhysical()->CheckValidPosition(&overlaps); + if (overlaps) + { + char *buffy = TRACKED_NEW char[64 + strlen(overlaps)]; + sprintf(buffy, "Oneshot ends overlapping %s", overlaps); + plConsoleMsg *showLine = TRACKED_NEW plConsoleMsg( plConsoleMsg::kAddLine, buffy ); + showLine->Send(); + delete[] overlaps; + delete[] buffy; + } + } +#endif + } + if (humanBrain) + humanBrain->ResetIdle(); + + IUndoLimitPlayersInput(avatar); + // this is for a console command hack + if (plAvOneShotTask::fForce3rdPerson && avatar->IsLocalAvatar()) + { + // create message + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, false); + pMsg->SetCmd(plCameraMsg::kResponderUndoThirdPerson); + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + } + + return false; + } + else + return true; // still running; waiting for fWaitFrames == 0 + } + else + return true; + } + else + return false; +} + +void plAvOneShotTask::LeaveAge(plArmatureMod *avatar) +{ + if (fAnimInstance) + fAnimInstance->Stop(); + + if (fEnablePhysicsAtEnd) + avatar->EnablePhysics(true); + + IUndoLimitPlayersInput(avatar); + fIgnore = true; +} + +void plAvOneShotTask::SetAnimName(char *name) +{ + delete [] fAnimName; + fAnimName = hsStrcpy(name); +} + +////////////////////// +// +// PLAVONESHOTLINKTASK +// +////////////////////// + +plAvOneShotLinkTask::plAvOneShotLinkTask() : plAvOneShotTask(), +fMarkerName(nil), +fMarkerTime(-1), +fStartTime(0), +fLinkFired(false) +{ + fDisablePhysics = false; +} + +plAvOneShotLinkTask::~plAvOneShotLinkTask() +{ + delete [] fMarkerName; +} + +// task protocol +hsBool plAvOneShotLinkTask::Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + hsBool result = plAvOneShotTask::Start(avatar, brain, time, elapsed); + fStartTime = time; + + if (fAnimInstance && fMarkerName) + { + const plATCAnim *anim = plATCAnim::ConvertNoRef(fAnimInstance->GetAnimation()); + if (anim) + { + // GetMarker returns -1 if the marker isn't found + fMarkerTime = anim->GetMarker(fMarkerName); + } + } + return result; +} + +hsBool plAvOneShotLinkTask::Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed) +{ + hsBool result = plAvOneShotTask::Process(avatar, brain, time, elapsed); + if (fIgnore) + return result; + + if (avatar->GetTarget(0) == plNetClientApp::GetInstance()->GetLocalPlayer()) + { + if (!fLinkFired && (fStartTime + fMarkerTime < time)) + { + avatar->ILinkToPersonalAge(); + + avatar->EnablePhysics(false, plArmatureMod::kDisableReasonLinking); + fLinkFired = true; + } + } + + return result; +} + +void plAvOneShotLinkTask::Write(hsStream *stream, hsResMgr *mgr) +{ + plAvOneShotTask::Write(stream, mgr); + stream->WriteSafeString(fAnimName); + stream->WriteSafeString(fMarkerName); +} + +void plAvOneShotLinkTask::Read(hsStream *stream, hsResMgr *mgr) +{ + plAvOneShotTask::Read(stream, mgr); + fAnimName = stream->ReadSafeString(); + fMarkerName = stream->ReadSafeString(); +} + +void plAvOneShotLinkTask::SetMarkerName(char *name) +{ + delete [] fMarkerName; + fMarkerName = hsStrcpy(name); +} + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.h new file mode 100644 index 00000000..7e3e1b1f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plAvatarTasks.h @@ -0,0 +1,255 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/** \file plAvatarTasks.h +*/ +#ifndef PLAVATARTASKS_INC +#define PLAVATARTASKS_INC + +// base class +#include "plAvTask.h" + +#include "plAvDefs.h" + +#include "hsQuat.h" +#include "../CoreLib/hsGeometry3.h" +#include "../CoreLib/hsMatrix44.h" +#include "../pnKeyedObject/plKey.h" + +class plAGAnim; +class plAGAnimInstance; +class plAvSeekMsg; +class plAvOneShotMsg; +class plAvEnterModeMsg; +class plOneShotCallbacks; +class plMessage; +class plAvPushBrainMsg; + +/** \class plAvAnimTask + Attach or detach an animation from the avatar. + Optionally begin playing. + It's perfectly reasonable to attach an animation with a blend of zero and start of false. + This will have no effect visually until you start blending the animation in. */ +class plAvAnimTask : public plAvTask +{ +public: + /** Default constructor for the class factory and subclases */ + plAvAnimTask(); + + /** Canonical constructor form when attaching + \param animName The name of the animation we'll be attaching or removing + \param initialBlend Initial blend value; only relevant if we're attaching + \param targetBlend The blend value we're shooting for + \param fadeSpeed How fast (in fade units per second) to fade in + \param setTime Local time for the animation to start + \param start Start the animation, or just attach it? + \param loop Make the animation loop? + \param attach Are we attaching or detaching the animation? + */ + plAvAnimTask(const char *animName, hsScalar initialBlend, hsScalar targetBlend, hsScalar fadeSpeed, + hsScalar setTime, hsBool start, hsBool loop, hsBool attach); + + /** Canonical constructor form form for detaching + \param animName The name of the animation we're detaching + \param fadeSpeed How fast to fade it out. */ + plAvAnimTask(const char *animName, hsScalar fadeSpeed, hsBool attach = false); + + virtual ~plAvAnimTask(); + + // task protocol + virtual hsBool Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + virtual hsBool Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + virtual void LeaveAge(plArmatureMod *avatar); + + // plasma protocol + CLASSNAME_REGISTER( plAvAnimTask ); + GETINTERFACE_ANY( plAvAnimTask, plAvTask ); + + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *stream, hsResMgr *mgr); + +protected: + // public members + char* fAnimName; // the animation we're operating on + hsScalar fInitialBlend; // the blend to establish (attaching only) + hsScalar fTargetBlend; // the blend to achieve eventually (attaching only) + hsScalar fFadeSpeed; // how fast to achieve the blend + hsScalar fSetTime; // set the animation to this time + hsBool fStart; // start the animation playing? (attaching only) + hsBool fLoop; // turn on looping? (attaching only) + hsBool fAttach; // attach? (otherwise detach) + plAGAnimInstance *fAnimInstance; // the animation we're monitoring (detaching only) +}; + + +/** \task plAvSeekTask + Magically fly to a specific location and orientation. Not for use in final production scenes; mainly as a hack + to fly through incomplete areas of a level. + !!! Generally deprecated in favor of avBlendedSeekTask, a slightly less-gross-looking hack. + \deprecated */ +class plAvSeekTask : public plAvTask +{ +public: + /** Default constructor used by class factory and descendants. */ + plAvSeekTask(); + /** Common constructor. Seeks to the point at the normal speed. */ + plAvSeekTask(plKey target); + /** Constructor form for calculated seek points: uses aligment type and (optionally) animation + The animation is used if we're trying to align some future pose with a seek target. */ + plAvSeekTask(plKey target, plAvAlignment alignType, const char *animName); + + // task protocol + virtual hsBool Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + virtual hsBool Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + virtual void LeaveAge(plArmatureMod *avatar); + + // plasma protocol + CLASSNAME_REGISTER( plAvSeekTask ); + GETINTERFACE_ANY( plAvSeekTask, plAvTask ); + + // *** implement reader and writer if needed for network propagation +protected: + // -- specification members -- + char * fAnimName; // the animation we're using, if any; + plAvAlignment fAlign; // how to line up with the seek point + double fDuration; // the time we want the task to take + plKey fTarget; // the thing we're seeking towards + + // -- implementation members -- + plAGAnimInstance * fAnimInstance; // the animation we're using + hsPoint3 fTargetPosition; // the position we're seeking + hsQuat fTargetRotation; // the orientation we're seeking + double fTargetTime; // the time we want to be done with the task (start + duration) + hsBool fPhysicalAtStart; // was the avatar physical before we started? + hsBool fCleanup; // last frame of processing +}; + +// ---- THE CUTOFF ---- + +/** \class plAvOneShotTask + Makes the avatar play a single animation and then return to user control. + Typically used in combination with a seek task to position the avatar + at the proper start point first. + Deprecated. Will be replaced by single-stage generic brains. +*/ +class plAvOneShotTask : public plAvTask { +public: + /** Put default values for all member variables. */ + void InitDefaults(); + + /** Default constructor for the class factor and descendants. */ + plAvOneShotTask(); + + /** Canonical constructor + \param animName The name of the animation we're going to play + \param drivable Unused. Allows the oneshot to be controlled by keyboard input + \param reversable Unused. Allows the oneshot to be backed up by keyboard input + \param callbacks A vector of callback messages to be sent at specific times during the animation + */ + plAvOneShotTask(const char *animName, hsBool drivable, hsBool reversible, plOneShotCallbacks *callbacks); + /** Construct from a oneshot message. + \param msg The message to copy our parameters from + \param brain The brain to attach the task to. + */ + plAvOneShotTask (plAvOneShotMsg *msg, plArmatureMod *avatar, plArmatureBrain *brain); + virtual plAvOneShotTask::~plAvOneShotTask(); + + // task protocol + virtual hsBool Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + virtual hsBool Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + virtual void LeaveAge(plArmatureMod *avatar); + + void SetAnimName(char *name); + + static hsBool fForce3rdPerson; + + // plasma protocol + CLASSNAME_REGISTER( plAvOneShotTask ); + GETINTERFACE_ANY( plAvOneShotTask, plAvTask ); + + hsBool fBackwards; // play the anim backwards + hsBool fDisableLooping; // explicitly kill looping on this anim; + hsBool fDisablePhysics; // disable physics when we play this oneshot + + // *** implement reader and writer if needed for network propagation +protected: + char *fAnimName; // the name of the one-shot animation we want to use + hsBool fMoveHandle; // move the handle after the oneshot's done playing? + plAGAnimInstance *fAnimInstance; // the animation instance (available only after it starts playing) + hsBool fDrivable; // the user can control the animation with the mouse + hsBool fReversible; // the user can back up the animation with the mouse + hsBool fEnablePhysicsAtEnd; // was the avatar physical before we started (and did we disable physics?) + hsBool fDetachAnimation; // should we detach the animation when we're done? + hsBool fIgnore; // if this gets set before we start, we just finish without doing anything. + + plOneShotCallbacks *fCallbacks; // a set of callbacks to set up on our animation + + hsMatrix44 fPlayer2Anim; // matrix from player root space to animation root space + hsMatrix44 fAnim2Player; // matrix from animation root space to player root space + + hsMatrix44 fWorld2Anim; + hsMatrix44 fAnim2World; + + int fWaitFrames; // wait a couple frames before finalizing position to allow changes + // from any animation callbacks to propagate. based on weird interaction + // with Attach() command. sheeeit. +}; + +void GetPositionAndRotation(hsMatrix44 transform, hsScalarTriple *position, hsQuat *rotation); + + +// A quick task to play a oneshot, wait on a marker, and link to your personal age. +// Could be done with a responder, but this gives me a quick way to trigger it from code +// without the responder and callback messages flying around. +class plAvOneShotLinkTask : public plAvOneShotTask +{ +public: + plAvOneShotLinkTask(); + virtual ~plAvOneShotLinkTask(); + + // task protocol + virtual hsBool Start(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + virtual hsBool Process(plArmatureMod *avatar, plArmatureBrain *brain, double time, hsScalar elapsed); + + CLASSNAME_REGISTER( plAvOneShotLinkTask ); + GETINTERFACE_ANY( plAvOneShotLinkTask, plAvOneShotTask ); + + // only read/writes enough to send an unstarted task across the net. Not intended for + // use with a running task. + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *stream, hsResMgr *mgr); + + void SetMarkerName(char *name); + +protected: + char *fMarkerName; + double fStartTime; + hsScalar fMarkerTime; + hsBool fLinkFired; +}; + + +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingLayout.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingLayout.h new file mode 100644 index 00000000..996076da --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingLayout.h @@ -0,0 +1,153 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLCLOTHINGLAYOUT_INC +#define PLCLOTHINGLAYOUT_INC + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "hsUtils.h" + +// This file is intended to be an independent section so that plClothingMtl and plAvatarClothing +// can both use these structures, without all the plCreatable/hsKeyedObject/plSynchedObject stuff +// No virtual methods allowed, and define everything inside the class declaration. (So that we can +// #include the file where necessary and not get redundant method definition errors.) + +class plClothingElement +{ +public: + enum + { + kLayerBase, + kLayerSkin, + kLayerSkinBlend1, + kLayerSkinBlend2, + kLayerSkinBlend3, + kLayerSkinBlend4, + kLayerSkinBlend5, + kLayerSkinBlend6, + kLayerTint1, + kLayerTint2, + kLayerMax, + kLayerSkinFirst = kLayerSkin, + kLayerSkinLast = kLayerSkinBlend6, + }; + + char *fName; + UInt32 fXPos; + UInt32 fYPos; + UInt32 fWidth; + UInt32 fHeight; + + plClothingElement(char *name, UInt32 xPos, UInt32 yPos, UInt32 width, UInt32 height) + { + fName = hsStrcpy(name); + fXPos = xPos; + fYPos = yPos; + fWidth = width; + fHeight = height; + } + ~plClothingElement() { delete [] fName; } + + static void GetElements(hsTArray &out) + { + /* + out.Append(TRACKED_NEW plClothingElement("shirt-chest", 768, 0, 256, 512)); + out.Append(TRACKED_NEW plClothingElement("shirt-sleeve", 512, 192, 256, 128)); + out.Append(TRACKED_NEW plClothingElement("face", 0, 0, 512, 512)); + out.Append(TRACKED_NEW plClothingElement("eyeball", 64, 512, 64, 64)); + out.Append(TRACKED_NEW plClothingElement("shoe-top", 0, 832, 128, 128)); + out.Append(TRACKED_NEW plClothingElement("shoe-bottom", 0, 768, 128, 64)); + out.Append(TRACKED_NEW plClothingElement("pants", 512, 512, 512, 512)); + out.Append(TRACKED_NEW plClothingElement("hand-LOD", 64, 576, 64, 64)); + out.Append(TRACKED_NEW plClothingElement("hand-square", 128, 512, 128, 128)); + out.Append(TRACKED_NEW plClothingElement("hand-wide", 0, 640, 256, 128)); + out.Append(TRACKED_NEW plClothingElement("playerbook", 512, 0, 256, 128)); + out.Append(TRACKED_NEW plClothingElement("backpack", 512, 256, 256, 256)); + out.Append(TRACKED_NEW plClothingElement("glasses-front", 256, 512, 256, 64)); + out.Append(TRACKED_NEW plClothingElement("glasses-side", 256, 576, 256, 32)); + out.Append(TRACKED_NEW plClothingElement("KI", 256, 640, 256, 128)); + */ + out.Append(TRACKED_NEW plClothingElement("Chest", 768, 0, 256, 512)); + out.Append(TRACKED_NEW plClothingElement("Arm", 512, 192, 256, 128)); + out.Append(TRACKED_NEW plClothingElement("Face", 0, 256, 512, 256)); + out.Append(TRACKED_NEW plClothingElement("Eye", 64, 704, 64, 64)); + out.Append(TRACKED_NEW plClothingElement("Extra Hair", 256, 0, 256, 256)); + out.Append(TRACKED_NEW plClothingElement("Hat", 0, 0, 256, 256)); + out.Append(TRACKED_NEW plClothingElement("Foot", 0, 768, 256, 256)); + out.Append(TRACKED_NEW plClothingElement("Legs", 512, 512, 512, 512)); + out.Append(TRACKED_NEW plClothingElement("LOD", 64, 640, 64, 64)); + out.Append(TRACKED_NEW plClothingElement("Finger", 128, 640, 128, 128)); + out.Append(TRACKED_NEW plClothingElement("Palm", 0, 512, 256, 128)); + out.Append(TRACKED_NEW plClothingElement("Player Book", 256, 512, 256, 128)); + out.Append(TRACKED_NEW plClothingElement("Glasses", 384, 640, 128, 128)); + out.Append(TRACKED_NEW plClothingElement("KI", 256, 640, 128, 128)); + + } +}; + +class plClothingLayout +{ +public: + plClothingLayout(char *name, UInt32 origWidth) { fName = hsStrcpy(name); fOrigWidth = origWidth; } + ~plClothingLayout() { delete [] fName; } + + char *fName; + UInt32 fOrigWidth; + hsTArray fElements; +/* + enum + { + kSetShirt, + kSetFace, + kSetEye, + kSetShoe, + kSetPants, + kSetHand, + kSetPlayerBook, + kSetBackpack, + kSetGlasses, + kSetKI, + kMaxTileset, + }; +*/ + enum + { + kSetShirt, + kSetFace, + kSetShoe, + kSetPants, + kSetHand, + kSetPlayerBook, + kSetGlasses, + kSetKI, + kSetEye, // UNUSED + kSetBackpack, // UNUSED + kMaxTileset, + }; + +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingSDLModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingSDLModifier.cpp new file mode 100644 index 00000000..43cdc7e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingSDLModifier.cpp @@ -0,0 +1,308 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plClothingSDLModifier.h" +#include "plAvatarClothing.h" +#include "plClothingLayout.h" +#include "plArmatureMod.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plSDLModifierMsg.h" +#include "../plSDL/plSDL.h" +#include "../pnKeyedObject/plKeyImp.h" + +// static vars +char plClothingSDLModifier::kStrItem[]="item"; +char plClothingSDLModifier::kStrTint[]="tint"; +char plClothingSDLModifier::kStrTint2[]="tint2"; +char plClothingSDLModifier::kStrWardrobe[]="wardrobe"; +char plClothingSDLModifier::kStrSkinTint[]="skinTint"; +char plClothingSDLModifier::kStrFaceBlends[]="faceBlends"; +char plClothingSDLModifier::kStrAppearance[]="appearance"; +char plClothingSDLModifier::kStrClothingDescName[]="clothingItem"; +char plClothingSDLModifier::kStrAppearanceDescName[]="appearanceOptions"; +char plClothingSDLModifier::kStrLinkInAnim[] = "linkInAnim"; + +// +// init latest data +// +plClothingSDLModifier::plClothingSDLModifier() : + fClothingOutfit(nil) +{ +} + +// +// find armature mod and cache it's clothingOutfit +// +plClothingOutfit* plClothingSDLModifier::GetClothingOutfit() +{ + if (fClothingOutfit) + return fClothingOutfit; + + int i; + plSceneObject* target = GetTarget(); + hsAssert(target, "plClothingSDLModifier: nil target"); + for (i=0;iGetNumModifiers();i++) + { + const plArmatureMod* am = plArmatureMod::ConvertNoRef(target->GetModifier(i)); + if (am) + { + fClothingOutfit=am->GetClothingOutfit(); + break; + } + } + + return fClothingOutfit; +} + +void plClothingSDLModifier::PutCurrentStateIn(plStateDataRecord* dstState) +{ + IPutCurrentStateIn(dstState); +} + +// +// get current state from clothingOutfit and put it in dstState +// +void plClothingSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + plSceneObject* sobj=GetTarget(); + hsAssert(sobj, "plClothingSDLModifier, nil target"); + + plClothingOutfit* clothing = GetClothingOutfit(); + hsAssert(clothing, "nil clothingOutfit"); + + hsTArray SDRs; + hsTArray items=clothing->GetItemList(); + hsTArray options=clothing->GetOptionList(); + + plSDStateVariable* clothesStateDesc = dstState->FindSDVar(kStrWardrobe); // find clothes list + int itemCount=items.GetCount(); + int optionsCount = options.GetCount(); + hsAssert(optionsCount==itemCount, "number of items should match number of options according to clothing.sdl"); + + if (clothesStateDesc->GetCount() != itemCount) + clothesStateDesc->Alloc(itemCount); // set appropriate list size + + int lowerCount = (itemCount <= optionsCount ? itemCount : optionsCount); + int i; + for(i = 0; i < lowerCount; i++) + { + plClosetItem closetItem; + closetItem.fItem = items[i]; + closetItem.fOptions = *options[i]; + + PutSingleItemIntoSDR(&closetItem, clothesStateDesc->GetStateDataRecord(i)); + SDRs.Append(clothesStateDesc->GetStateDataRecord(i)); + } + + // skin tint + plSDStateVariable* appearanceStateDesc = dstState->FindSDVar(kStrAppearance); // for skin tint + UInt8 skinTint[3]; + skinTint[0] = (UInt8)(clothing->fSkinTint.r * 255); + skinTint[1] = (UInt8)(clothing->fSkinTint.g * 255); + skinTint[2] = (UInt8)(clothing->fSkinTint.b * 255); + appearanceStateDesc->GetStateDataRecord(0)->FindVar(kStrSkinTint)->Set(skinTint); + + plSimpleStateVariable* faceBlends = appearanceStateDesc->GetStateDataRecord(0)->FindVar(kStrFaceBlends); + int numBlends = plClothingElement::kLayerSkinLast - plClothingElement::kLayerSkinFirst; + if (faceBlends->GetCount() != numBlends) + faceBlends->Alloc(numBlends); + for(i = 0; i < numBlends; i++) + faceBlends->Set((UInt8)(clothing->fSkinBlends[i] * 255), i); + + SDRs.Append(appearanceStateDesc->GetStateDataRecord(0)); + + // This logically belongs in the avatar.sdl file, but clothing.sdl is already broadcast to + // other players when you join an age, and I don't want to broadcast all of avatar.sdl for + // just this one value. + plSimpleStateVariable *var = dstState->FindVar(kStrLinkInAnim); + if (var) + var->Set(clothing->fAvatar->fLinkInAnimKey); +} + +void plClothingSDLModifier::PutSingleItemIntoSDR(plClosetItem *item, plStateDataRecord *sdr) +{ + plKey key = item->fItem->GetKey(); + sdr->FindVar(kStrItem)->Set(key); + + //hsColorRGBA c = item->fOptions.fTint1; + //hsColorRGBA c2 = item->fOptions.fTint2; + UInt8 c[3]; + UInt8 c2[3]; + c[0] = (UInt8)(item->fOptions.fTint1.r * 255); + c[1] = (UInt8)(item->fOptions.fTint1.g * 255); + c[2] = (UInt8)(item->fOptions.fTint1.b * 255); + c2[0] = (UInt8)(item->fOptions.fTint2.r * 255); + c2[1] = (UInt8)(item->fOptions.fTint2.g * 255); + c2[2] = (UInt8)(item->fOptions.fTint2.b * 255); + + sdr->FindVar(kStrTint)->Set(c); + sdr->FindVar(kStrTint2)->Set(c2); +} + +// +// Apply the SDL state that is passed in to the current clothing state. +// +void plClothingSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + plSceneObject* sobj=GetTarget(); + hsAssert(sobj, "plClothingSDLModifier, nil target"); + + plClothingOutfit* clothing = GetClothingOutfit(); + if( clothing == nil ) + { + hsAssert(clothing, "nil clothingOutfit"); + return; + } + + plSDStateVariable* clothesStateDesc = srcState->FindSDVar(kStrWardrobe); // find clothes list + int num=clothesStateDesc->GetCount(); // size of clothes list + bool update=true; + + // We just remove the accessories. Any regular items will be replaced + // when we try to wear something else in the same category. (And in + // case the player lies, this guarantees they'll at least be wearing something.) + clothing->StripAccessories(); + + int i; + for(i=0;iGetStateDataRecord(i); + HandleSingleSDR(clothingItemState, clothing); + } + + plSDStateVariable* appearanceStateDesc = srcState->FindSDVar(kStrAppearance); // for skin tint + plStateDataRecord* appearanceState = appearanceStateDesc->GetStateDataRecord(0); + HandleSingleSDR(appearanceState, clothing); + + if (update) + clothing->ForceUpdate(true /* retry */); + + plSimpleStateVariable *var; + var = srcState->FindVar(kStrLinkInAnim); + if (var) + var->Get(&clothing->fAvatar->fLinkInAnimKey); +} + +void plClothingSDLModifier::HandleSingleSDR(const plStateDataRecord *sdr, plClothingOutfit *clothing /* = nil */, plClosetItem *closetItem /* = nil */) +{ + if (sdr == nil) + return; + + int i; + UInt8 tint[3]; + hsScalar tintScalar[3]; + if (!strcmp(sdr->GetDescriptor()->GetName(), kStrClothingDescName)) + { + // get item from clothesItem + plSimpleStateVariable* itemVar = sdr->FindVar(kStrItem); + plClothingItem* clothingItem = nil; // clothing->GetItemList()[i]; + if (itemVar->IsUsed()) + { + plKey key; + itemVar->Get(&key); + clothingItem = plClothingItem::ConvertNoRef(key ? key->GetObjectPtr() : nil); + if (clothingItem) + { + if (clothing) + clothing->AddItem(clothingItem, false, false /*bcast */); + if (closetItem) + closetItem->fItem = clothingItem; + } + } + + // tints + if (clothingItem) + { + // get item from clothesItem + plSimpleStateVariable* tintVar = sdr->FindVar(kStrTint); + if (tintVar->IsUsed()) + { + tintVar->Get(tint); + tintScalar[0] = tint[0] / 255.f; + tintScalar[1] = tint[1] / 255.f; + tintScalar[2] = tint[2] / 255.f; + if (clothing) + clothing->TintItem(clothingItem, tintScalar[0], tintScalar[1], tintScalar[2], + false /*update*/, false /*broadcast*/, false /*netForce*/, true, plClothingElement::kLayerTint1); + if (closetItem) + closetItem->fOptions.fTint1.Set(tintScalar[0], tintScalar[1], tintScalar[2], 1.f); + } + + tintVar=sdr->FindVar(kStrTint2); + if (tintVar->IsUsed()) + { + tintVar->Get(tint); + tintScalar[0] = tint[0] / 255.f; + tintScalar[1] = tint[1] / 255.f; + tintScalar[2] = tint[2] / 255.f; + + if (clothing) + clothing->TintItem(clothingItem, tintScalar[0], tintScalar[1], tintScalar[2], + false /*update*/, false /*broadcast*/, false /*netForce*/, true, plClothingElement::kLayerTint2); + if (closetItem) + closetItem->fOptions.fTint2.Set(tintScalar[0], tintScalar[1], tintScalar[2], 1.f); + } + } + } + else if (!strcmp(sdr->GetDescriptor()->GetName(), kStrAppearanceDescName)) + { + // skin tints + plSimpleStateVariable* skinVar = sdr->FindVar(kStrSkinTint); + if (skinVar->IsUsed()) + { + skinVar->Get(tint); + if (clothing) + clothing->TintSkin(tint[0] / 255.f, tint[1] / 255.f, tint[2] / 255.f, false /* update */, false /*broadcast*/); + } + plSimpleStateVariable* faceBlends = sdr->FindVar(kStrFaceBlends); + if (faceBlends->IsUsed()) + { + int numBlends = plClothingElement::kLayerSkinLast - plClothingElement::kLayerSkinFirst; + for(i = 0; i < numBlends && i < faceBlends->GetCount(); i++) + { + UInt8 blend; + faceBlends->Get(&blend, i); + clothing->fSkinBlends[i] = (hsScalar)blend / 255; + } + } + } +} + +// FINDARMATUREMOD +const plClothingSDLModifier *plClothingSDLModifier::FindClothingSDLModifier(const plSceneObject *obj) +{ + int count = obj->GetNumModifiers(); + + for (int i = 0; i < count; i++) + { + const plModifier *mod = obj->GetModifier(i); + const plClothingSDLModifier *sdlMod = plClothingSDLModifier::ConvertNoRef(mod); + if(sdlMod) + return sdlMod; + } + return nil; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingSDLModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingSDLModifier.h new file mode 100644 index 00000000..c4865012 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plClothingSDLModifier.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plClothingSDLModifier_inc +#define plClothingSDLModifier_inc + +#include "../plModifier/plSDLModifier.h" + +#include "hsColorRGBA.h" +#include "hsTemplates.h" +#include "hsTypes.h" + +// +// This modifier is responsible for sending and recving +// an avatar's clothing saveState. +// +class plClothingOutfit; +class plClothingItem; +class plClothingItemOptions; +class plClosetItem; +class plStateDataRecord; +class plKey; +class plClothingSDLModifier : public plSDLModifier +{ +protected: + plClothingOutfit* fClothingOutfit; + + void IPutCurrentStateIn(plStateDataRecord* dstState); + void ISetCurrentStateFrom(const plStateDataRecord* srcState); + + UInt32 IApplyModFlags(UInt32 sendFlags) { return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState); } + +public: + // var labels + static char kStrItem[]; + static char kStrTint[]; + static char kStrTint2[]; + static char kStrWardrobe[]; + static char kStrSkinTint[]; + static char kStrFaceBlends[]; + static char kStrAppearance[]; + static char kStrClothingDescName[]; + static char kStrAppearanceDescName[]; + static char kStrLinkInAnim[]; + + CLASSNAME_REGISTER( plClothingSDLModifier ); + GETINTERFACE_ANY( plClothingSDLModifier, plSDLModifier); + + plClothingSDLModifier(); + + plClothingOutfit* GetClothingOutfit(); + void SetClothingOutfit(plClothingOutfit* c) { fClothingOutfit=c; } + + void PutCurrentStateIn(plStateDataRecord* dstState); + const char* GetSDLName() const { return kSDLClothing; } + static const char* GetClothingItemSDRName() { return kStrClothingDescName; } + + // Pass in a clothing outfit if you want to apply the clothing item. Pass in a closet item if you're just + // looking to parse the SDL info. + // Aw heck. Go crazy if you want and pass them BOTH in! Muahahaha! + static void HandleSingleSDR(const plStateDataRecord *sdr, plClothingOutfit *clothing = nil, plClosetItem *closetItem = nil); + static void PutSingleItemIntoSDR(plClosetItem *item, plStateDataRecord *sdr); + + static const plClothingSDLModifier *plClothingSDLModifier::FindClothingSDLModifier(const plSceneObject *obj); +}; + +#endif // plClothingSDLModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCoopCoordinator.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCoopCoordinator.cpp new file mode 100644 index 00000000..a9f41923 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCoopCoordinator.cpp @@ -0,0 +1,427 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// + +// singular +#include "plCoopCoordinator.h" + +// local +#include "plAvBrainCoop.h" +#include "plAvatarMgr.h" +#include "plAvTaskBrain.h" +#include "plAvTaskSeek.h" + +// global +#include "hsUtils.h" + +// other +#include "../plMessage/plAvCoopMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClient/plNetClientMgr.h" +#include "plPhysical.h" +#include "../pnTimer/plTimerCallbackManager.h" +#include "../plMessage/plTimerCallbackMsg.h" + +const unsigned kAbortTimer = 1; +const float kAbortTimerDuration = 15; // 15 seconds + +///////////////////////////////////////////////////////////////////////////////////////// +// +// CONSTRUCTORS +// +///////////////////////////////////////////////////////////////////////////////////////// + +plCoopCoordinator::plCoopCoordinator() +: fHostBrain(nil), + fGuestBrain(nil), + fInitiatorID(0), + fInitiatorSerial(0), + fHostOfferStage(0), + fGuestAcceptStage(0), + fGuestAcceptMsg(nil), + fAutoStartGuest(nil), + fGuestAccepted(false) +{ +} + +// plCoopCoordinator ---------------------------------------- +// ------------------ +plCoopCoordinator::plCoopCoordinator(plKey host, plKey guest, + plAvBrainCoop *hostBrain, plAvBrainCoop *guestBrain, + const char *synchBone, + UInt32 hostOfferStage, UInt32 guestAcceptStage, + plMessage *guestAcceptMsg, + bool autoStartGuest) +: fHostKey(host), + fGuestKey(guest), + fHostBrain(hostBrain), + fGuestBrain(guestBrain), + fInitiatorID(hostBrain->GetInitiatorID()), + fInitiatorSerial(hostBrain->GetInitiatorSerial()), + fHostOfferStage(hostOfferStage), + fGuestAcceptStage(guestAcceptStage), + fGuestAcceptMsg(guestAcceptMsg), + fAutoStartGuest(autoStartGuest), + fGuestAccepted(false), + fGuestLinked(false) +{ + const char * hostName = host->GetName(); + const char * guestName = guest->GetName(); + static int serial = 0; + + int len = strlen(hostName) + strlen(guestName) + 3 /* serial num */ + 1; + + char *newName = TRACKED_NEW char[len]; + + serial = serial % 999; + + sprintf(newName, "%s%s%3i\x000", hostName, guestName, serial++); + + plKey newKey = hsgResMgr::ResMgr()->NewKey(newName, this, host->GetUoid().GetLocation()); + + delete[] newName; + + fSynchBone = hsStrcpy(synchBone); + + plKey avMgrKey = plAvatarMgr::GetInstance()->GetKey(); + + guestBrain->SetRecipient(avMgrKey); + hostBrain->SetRecipient(avMgrKey); + // disable our clickability here if we are the guest + if (plNetClientMgr::GetInstance()->GetLocalPlayerKey() == guest) + { + plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kGUIDisableAvatarClickable); + pMsg->SetAvKey(guest); + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + pMsg->Send(); + } +} + +// plCoopCoordinator ------------------ +// ------------------ +plCoopCoordinator::~plCoopCoordinator() +{ + delete[] fSynchBone; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// CONSTRUCTORS +// +///////////////////////////////////////////////////////////////////////////////////////// + +// MsgReceive -------------------------------------- +// ----------- +hsBool plCoopCoordinator::MsgReceive(plMessage *msg) +{ + plNotifyMsg *notify = plNotifyMsg::ConvertNoRef(msg); + if(notify) + { + proMultiStageEventData * mtevt = static_cast(notify->FindEventRecord(proEventData::kMultiStage)); + if(mtevt) + { + int stageNum = mtevt->fStage; + UInt32 stageState = mtevt->fEvent; + + plKey noteSender = notify->GetSender(); + bool isFromHost = (noteSender == fHostKey); + bool isFromGuest = (noteSender == fGuestKey); + + DebugMsg("COOP: Received multi-stage callback - stageNum = %d, stageState = %d, isFromHost = %d", stageNum, stageState, isFromHost ? 1 : 0); + + if(isFromHost) + { + if(!fGuestAccepted) + { + // we've just entered the host offer stage (i.e., the offer is ready) + if(stageNum == fHostOfferStage && stageState == proEventData::kEnterStage) + { + if(fAutoStartGuest) + { + IStartGuest(); + IStartTimeout(); + } else { + fHostBrain->EnableGuestClick(); + } + fGuestAccepted = true; + } + } + + } else if(isFromGuest) + { + if(stageNum == fGuestAcceptStage && stageState == proEventData::kEnterStage) + { + plKey localPlayer = plNetClientApp::GetInstance()->GetLocalPlayerKey(); + + // we only actually fire off the guest accept message if we're on the guest machine. + // if it needs to be netpropped, the client can set that up when they set up the coop. + if(fGuestAcceptMsg && localPlayer == fGuestKey) + { + fGuestAcceptMsg->Send(); + } + // kill the message (along with being active) + fGuestAcceptMsg = nil; + fGuestLinked = true; + IAdvanceParticipant(true); // advance the host +// IAdvanceParticipant(false); // advance the guest + } + } else { + // not from host; not from guest + // let's assume for the moment it's from a trigger. + IStartHost(); + } + } + } + + plAvCoopMsg *coop = plAvCoopMsg::ConvertNoRef(msg); + if(coop) + { + DebugMsg("COOP: Received coop message: %d", coop->fCommand); + switch(coop->fCommand) + { + case plAvCoopMsg::kGuestAccepted: + IStartGuest(); + IStartTimeout(); + break; + + case plAvCoopMsg::kGuestSeeked: + // if they did make it to their target, then continue + IContinueGuest(); + break; + + case plAvCoopMsg::kGuestSeekAbort: + // if they aborted then just advance the host + // kill the message (along with being active) + fGuestAcceptMsg = nil; + fGuestLinked = true; + IAdvanceParticipant(true); // advance the host + break; + + } + } + + plAvTaskSeekDoneMsg *seekDone = plAvTaskSeekDoneMsg::ConvertNoRef(msg); + if (seekDone) + { + DebugMsg("COOP: Received avatar seek finished msg: aborted = %d", seekDone->fAborted ? 1 : 0); + if ( seekDone->fAborted ) + { + plAvCoopMsg *coopM = TRACKED_NEW plAvCoopMsg(plAvCoopMsg::kGuestSeekAbort,fInitiatorID,(UInt16)fInitiatorSerial); + coopM->SetBCastFlag(plMessage::kNetPropagate); + coopM->SetBCastFlag(plMessage::kNetForce); + coopM->AddReceiver(GetKey()); + coopM->Send(); + + } + else + { + plAvCoopMsg *coopM = TRACKED_NEW plAvCoopMsg(plAvCoopMsg::kGuestSeeked,fInitiatorID,(UInt16)fInitiatorSerial); + coopM->SetBCastFlag(plMessage::kNetPropagate); + coopM->SetBCastFlag(plMessage::kNetForce); + coopM->AddReceiver(GetKey()); + coopM->Send(); + } + } + + plTimerCallbackMsg* timerMsg = plTimerCallbackMsg::ConvertNoRef(msg); + if (timerMsg) + { + if (timerMsg->fID == kAbortTimer && !fGuestLinked) + ITimeout(); + } + return false; +} + +// Run ---------------------- +// ---- +void plCoopCoordinator::Run() +{ + IStartHost(); +} + +bool plCoopCoordinator::IsActiveForReal() +{ + return fGuestAcceptMsg ? true : false; +} + +// GetInitiatorID ------------------------ +// --------------- +UInt32 plCoopCoordinator::GetInitiatorID() +{ + return fInitiatorID; +} + +// GetInitiatorSerial ------------------------ +UInt16 plCoopCoordinator::GetInitiatorSerial() +{ + return (UInt16)fInitiatorSerial; +} + +// IStartHost ---------------------- +// ----------- +void plCoopCoordinator::IStartHost() +{ + DebugMsg("COOP: IStartHost()"); + plArmatureMod *guestAv = plAvatarMgr::FindAvatar(fGuestKey); + plArmatureMod *hostAv = plAvatarMgr::FindAvatar(fHostKey); + if (guestAv && hostAv) + { + plAvSeekMsg *msg = TRACKED_NEW plAvSeekMsg(nil, hostAv->GetKey(), nil, 1.f, true); + hsClearBits(msg->fFlags, plAvSeekMsg::kSeekFlagForce3rdPersonOnStart); + guestAv->GetPositionAndRotationSim(&msg->fTargetLookAt, nil); + hostAv->GetPositionAndRotationSim(&msg->fTargetPos, nil); + msg->Send(); + } + + // now tell the host to initiate the thing. + plAvTaskBrain *brainT = TRACKED_NEW plAvTaskBrain(fHostBrain); + plAvTaskMsg *brainM = TRACKED_NEW plAvTaskMsg(GetKey(), fHostKey, brainT); + brainM->SetBCastFlag(plMessage::kPropagateToModifiers); + brainM->Send(); +} + +// IStartGuest ---------------------- +// ------------ +void plCoopCoordinator::IStartGuest() +{ + DebugMsg("COOP: IStartGuest()"); + plSceneObject *avSO = plSceneObject::ConvertNoRef(fHostKey->ObjectIsLoaded()); + if ( !avSO ) + return; + + const plArmatureMod *hostAv = (plArmatureMod*)avSO->GetModifierByType(plArmatureMod::Index()); + if ( hostAv ) + { + const plSceneObject *targetBone = hostAv->FindBone(fSynchBone); + if(targetBone) + { + plAvSeekMsg *seekMsg = TRACKED_NEW plAvSeekMsg( nil, nil,targetBone->GetKey(), 0, true, kAlignHandle, nil, false, plAvSeekMsg::kSeekFlagNoWarpOnTimeout, GetKey()); + plAvTaskSeek *seekT = TRACKED_NEW plAvTaskSeek(seekMsg); + plAvTaskMsg *seekM = TRACKED_NEW plAvTaskMsg(GetKey(), fGuestKey, seekT); + seekM->SetBCastFlag(plMessage::kPropagateToModifiers); + seekM->Send(); + } + } +} + +// IContinueGuest ---------------------- +// ------------ +void plCoopCoordinator::IContinueGuest() +{ + DebugMsg("COOP: IContinueGuest()"); + plAvTaskBrain *brainT = TRACKED_NEW plAvTaskBrain(fGuestBrain); + plAvTaskMsg *brainM = TRACKED_NEW plAvTaskMsg(GetKey(), fGuestKey, brainT); + brainM->SetBCastFlag(plMessage::kPropagateToModifiers); + brainM->Send(); + fGuestBrain = nil; // the armature will destroy the brain when done. +} + +// IContinueHost ---------------------- +// -------------- +void plCoopCoordinator::IAdvanceParticipant(bool host) +{ + DebugMsg("COOP: IAdvanceParticipant(%d)", host ? 1 : 0); + plKey &who = host ? fHostKey : fGuestKey; + + plAvBrainGenericMsg* pMsg = TRACKED_NEW plAvBrainGenericMsg(nil, who, + plAvBrainGenericMsg::kNextStage, 0, false, 0.0, + false, false, 0.0); + + pMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + + pMsg->Send(); +} + +// IStartTimeout ---------------------- +// -------------- +void plCoopCoordinator::IStartTimeout() +{ + plTimerCallbackMsg* timerMsg = TRACKED_NEW plTimerCallbackMsg(GetKey(), kAbortTimer); + plgTimerCallbackMgr::NewTimer(kAbortTimerDuration, timerMsg); +} + +// ITimeout --------------------------- +// -------------- +void plCoopCoordinator::ITimeout() +{ + fGuestAcceptMsg = nil; + IAdvanceParticipant(true); // advance the host +} + +// Read ------------------------------------------------------------- +// ----- +void plCoopCoordinator::Read(hsStream *stream, hsResMgr *mgr) +{ + fHostKey = mgr->ReadKey(stream); + fGuestKey = mgr->ReadKey(stream); + + fHostBrain = plAvBrainCoop::ConvertNoRef(mgr->ReadCreatable(stream)); + fGuestBrain = plAvBrainCoop::ConvertNoRef(mgr->ReadCreatable(stream)); + + fHostOfferStage = stream->ReadByte(); + fGuestAcceptStage = stream->ReadBool(); + + if(stream->Readbool()) + fGuestAcceptMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); + else + fGuestAcceptMsg = nil; + + fSynchBone = stream->ReadSafeString(); + fAutoStartGuest = stream->Readbool(); + + fInitiatorID = fHostBrain->GetInitiatorID(); + fInitiatorSerial = fHostBrain->GetInitiatorSerial(); +} + +// Write ------------------------------------------------------------- +// ------ +void plCoopCoordinator::Write(hsStream *stream, hsResMgr *mgr) +{ + mgr->WriteKey(stream, fHostKey); + mgr->WriteKey(stream, fGuestKey); + + mgr->WriteCreatable(stream, fHostBrain); + mgr->WriteCreatable(stream, fGuestBrain); + + stream->WriteByte((UInt8)fHostOfferStage); + stream->WriteByte((UInt8)fGuestAcceptStage); + + stream->Writebool(fGuestAcceptMsg != nil); + if(fGuestAcceptMsg) + mgr->WriteCreatable(stream, fGuestAcceptMsg); + + stream->WriteSafeString(fSynchBone); + stream->Writebool(fAutoStartGuest); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCoopCoordinator.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCoopCoordinator.h new file mode 100644 index 00000000..f7ae2dae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCoopCoordinator.h @@ -0,0 +1,118 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plCoopCoordinator_h +#define plCoopCoordinator_h + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// + +// global +#include "../pnKeyedObject/hsKeyedObject.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// FORWARDS +// +///////////////////////////////////////////////////////////////////////////////////////// + +class plAvBrainCoop; +class plMessage; + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEFINITIONS +// +///////////////////////////////////////////////////////////////////////////////////////// + +/** \class plCoopCoordinator + Manages cooperation betweeen several generic brains; primarily serves as a place + to send synchronization messages. Originally this was going to be the role of the + 'host' brain, but that didn't provide a good interface point for Python. + This object can either be a "helper" for Python, or its functionality can be + recoded in Python. + */ +class plCoopCoordinator : public hsKeyedObject +{ +public: + plCoopCoordinator(); + plCoopCoordinator(plKey host, plKey guest, + plAvBrainCoop *hostBrain, plAvBrainCoop *guestBrain, + const char *synchBone, UInt32 hostOfferStage, UInt32 guestAcceptStage, + plMessage *guestAcceptMsg, + bool autoStartGuest); + ~plCoopCoordinator(); + + virtual hsBool MsgReceive(plMessage *msg); + + void Run(); + + UInt32 GetInitiatorID(); + UInt16 GetInitiatorSerial(); + + bool IsActiveForReal(); + + // rtti + CLASSNAME_REGISTER( plCoopCoordinator ); + GETINTERFACE_ANY( plCoopCoordinator, hsKeyedObject); + + // i/o + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + +protected: + void IStartHost(); + void IStartGuest(); + void IContinueGuest(); + void IAdvanceParticipant(bool host); + + void IStartTimeout(); + void ITimeout(); + + plKey fHostKey; + plKey fGuestKey; + + plAvBrainCoop *fHostBrain; + plAvBrainCoop *fGuestBrain; + + UInt32 fInitiatorID; + UInt32 fInitiatorSerial; + + UInt32 fHostOfferStage; // when we enter this stage, the offer is ready + UInt32 fGuestAcceptStage; // when we enter this stage, the offer is accepted + + plMessage *fGuestAcceptMsg; // send this when the guest accepts + + char *fSynchBone; + bool fAutoStartGuest; + bool fGuestAccepted; + + bool fGuestLinked; // guest linked, so ignore the timeout timer +}; + +#endif // plCoopCoordinator_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCritterCommands.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCritterCommands.h new file mode 100644 index 00000000..24cf5aee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plCritterCommands.h @@ -0,0 +1,160 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef __PLCRITTERCMDS_H__ +#define __PLCRITTERCMDS_H__ + +#include "hsStlUtils.h" + + +typedef std::vector VCharArray; + +class plCritterCommands +{ +public: + // The order of these is significant; setting the blend on an animation in this list + // to maximum will prevent all animations above it from playing. + // I.E. if "turn left" is set to maximum, it will completely hide "idle" + enum stdAnim { + kIdle, + kTurnLeft, + kTurnRight, + kForwardMedium, + kForwardFast, + kReverse, + kBankLeft, + kBankRight, + kImpactDown, + kNumStdAnims + }; + + VCharArray fAnimNameTypes; + + //Vector is used because the []operator is handy for filling + //the dialog boxes for implementation Components. -RXA + // KEEP THIS SYNCHRONIZED with the enum above. + plCritterCommands() + { + fAnimNameTypes.push_back("Idle"); + fAnimNameTypes.push_back("TurnLeft"); + fAnimNameTypes.push_back("TurnRight"); + fAnimNameTypes.push_back("Forward Medium"); + fAnimNameTypes.push_back("Forward Fast"); + fAnimNameTypes.push_back("Reverse"); + fAnimNameTypes.push_back("Bank Left"); + fAnimNameTypes.push_back("Bank Right"); + fAnimNameTypes.push_back("Impact Down"); + } + + int GetNumElements() { return fAnimNameTypes.size(); } + +}; + +/* +class plCritterCommands +{ +public: + enum avStdAnim{ + kTorsoStraightenOut = 0, + kTorsoSpasticRotate, + kTorsoShakeHead, + kTorsoOpenMouth, + kTorsoHover, + kTorsoForwardFlightSpeedB, + kTorsoForwardFlightSpeedA, + kTorsoBankRightFlapping, + kTorsoBankRightCoasting, + kTorsoBankLeftFlapping, + kTorsoBankLeftCoasting, + kTorsoBackwardFlight, + kRightWingSpasticRotate, + kRightWingHover, + kRightWingForwardFlightSpeedB, + kRightWingForwardFlightSpeedA, + kRightWingBankRightFlapping, + kRightWingBankRightCoasting, + kRightWingBankLeftFlapping, + kRightWingBankLeftCoasting, + kRightWingBackwardFlight, + kLeftWingSpasticRotate, + kLeftWingHover, + kLeftWingForwardFlightSpeedA, + kLeftWingForwardFlightSpeedB, + kLeftWingBankRightFlapping, + kLeftWingBankRightCoasting, + kLeftWingBankLeftFlapping, + kLeftWingBankLeftCoasting, + kLeftWingBackwardFlight, + kNumStdAnims + } ; + + VCharArray fAnimNameTypes; + + //Vector is used because the []operator is handy for filling + //the dialog boxes for implementation Components. -RXA + // KEEP THIS SYNCHRONIZED with the enum above. + plCritterCommands() + { + fAnimNameTypes.push_back("Torso Straighten Out"); + fAnimNameTypes.push_back("Torso Spastic Rotate"); + fAnimNameTypes.push_back("Torso Shake Head"); + fAnimNameTypes.push_back("Torso Open Mouth"); + fAnimNameTypes.push_back("Torso Hover (Idle)"); + fAnimNameTypes.push_back("Torso Forward SpeedB"); + fAnimNameTypes.push_back("Torso Forward SpeedA"); + fAnimNameTypes.push_back("Torso RBank Flapping"); + fAnimNameTypes.push_back("Torso RBank Coasting"); + fAnimNameTypes.push_back("Torso LBank Flapping"); + fAnimNameTypes.push_back("Torso LBank Coasting"); + fAnimNameTypes.push_back("Torso Backward Flight"); + fAnimNameTypes.push_back("RWing Spastic Rotate"); + fAnimNameTypes.push_back("RWing Hover (Idle)"); + fAnimNameTypes.push_back("RWing Forward SpeedB"); + fAnimNameTypes.push_back("RWing Forward SpeedA"); + fAnimNameTypes.push_back("RWing RBank Flapping"); + fAnimNameTypes.push_back("RWing RBank Coasting"); + fAnimNameTypes.push_back("RWing LBank Flapping"); + fAnimNameTypes.push_back("RWing LBank Coasting"); + fAnimNameTypes.push_back("RWing Backward Flight"); + fAnimNameTypes.push_back("LWing Spastic Rotate"); + fAnimNameTypes.push_back("LWing Hover (Idle)"); + fAnimNameTypes.push_back("LWing Forward SpeedA"); + fAnimNameTypes.push_back("LWing Forward SpeedB"); + fAnimNameTypes.push_back("LWing RBank Flapping"); + fAnimNameTypes.push_back("LWing RBank Coasting"); + fAnimNameTypes.push_back("LWing LBank Flapping"); + fAnimNameTypes.push_back("LWing LBank Coasting"); + fAnimNameTypes.push_back("LWing Backward Flight"); + } + + int GetNumElements() { return fAnimNameTypes.size(); } + +}; + +*/ + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMatrixChannel.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMatrixChannel.cpp new file mode 100644 index 00000000..0c6d5abe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMatrixChannel.cpp @@ -0,0 +1,1002 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// + +// havok (must be first) +#include +#include + +// singular +#include "plMatrixChannel.h" + +// local +#include "plQuatChannel.h" +#include "plPointChannel.h" + +// global +#include "hsResMgr.h" +#include "plProfile.h" +#include "hsTimer.h" + +// other +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../plInterp/plController.h" +#include "../plInterp/plAnimTimeConvert.h" +#include "../plInterp/hsInterp.h" +#include "../plTransform/hsAffineParts.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// PROFILING GIBBLIES +// +///////////////////////////////////////////////////////////////////////////////////////// + +plProfile_Extern(AffineValue); +plProfile_Extern(AffineInterp); +plProfile_Extern(AffineBlend); +plProfile_Extern(AffineCompose); +plProfile_Extern(MatrixApplicator); + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plMatrixChannel +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor -------------------------- +// ----- +plMatrixChannel::plMatrixChannel() +: plAGChannel() +{ +} + +// dtor --------------------------- +// ----- +plMatrixChannel::~plMatrixChannel() +{ +} + +// Value -------------------------------------------------------- +// ------ +const hsMatrix44 & plMatrixChannel::Value(double time, bool peek) +{ + return fResult; +} + +// AffineValue ----------------------------------------------------------- +// ------------ +const hsAffineParts & plMatrixChannel::AffineValue(double time, bool peek) +{ + return fAP; +} + +// Value -------------------------------------------------------------- +// ------ +void plMatrixChannel::Value(hsMatrix44 &matrix, double time, bool peek) +{ + matrix = Value(time); +} + +// MakeCombine ----------------------------------------------- +// ------------ +plAGChannel * plMatrixChannel::MakeCombine(plAGChannel *other) +{ + return nil; +} + +// MakeBlend --------------------------------------------------- +// ---------- +plAGChannel * plMatrixChannel::MakeBlend(plAGChannel * channelB, + plScalarChannel * channelBias, + int blendPriority) +{ + plMatrixChannel * matChanB = plMatrixChannel::ConvertNoRef(channelB); + plAGChannel * result = this; // if the blend fails, we keep our position in the graph + + if (matChanB) + { + result = TRACKED_NEW plMatrixBlend(this, matChanB, channelBias, blendPriority); + } + return result; +} + +// MakeZeroState ----------------------------- +// -------------- +plAGChannel * plMatrixChannel::MakeZeroState() +{ + return TRACKED_NEW plMatrixConstant(Value(0)); +} + +// MakeTimeScale -------------------------------------------------------- +// -------------- +plAGChannel * plMatrixChannel::MakeTimeScale(plScalarChannel *timeSource) +{ + return TRACKED_NEW plMatrixTimeScale(this, timeSource); +} + +// Dump ------------------------------------------- +// ----- +void plMatrixChannel::Dump(int indent, bool optimized, double time) +{ + std::string indentStr; + for(int i = 0; i < indent; i++) + { + indentStr += "- "; + } + hsStatusMessageF("%s matChan<%s>", indentStr.c_str(), fName); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plMatrixConstant +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor ---------------------------- +// ----- +plMatrixConstant::plMatrixConstant() +: plMatrixChannel() +{ +} + +plMatrixConstant::~plMatrixConstant() +{ +} + +plMatrixConstant::plMatrixConstant(const hsMatrix44 &value) +{ + Set(value); +} + +void plMatrixConstant::Set(const hsMatrix44 &value) +{ + fResult = value; + + gemAffineParts gemParts1; + decomp_affine(value.fMap, &gemParts1); + AP_SET(fAP, gemParts1); +} + +void plMatrixConstant::Write(hsStream *stream, hsResMgr *mgr) +{ + plMatrixChannel::Write(stream, mgr); + fAP.Write(stream); +} + +void plMatrixConstant::Read(hsStream *stream, hsResMgr *mgr) +{ + plMatrixChannel::Read(stream, mgr); + fAP.Read(stream); + fAP.ComposeMatrix(&fResult); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plMatrixTimeScale +// +// Insert into the graph when you need to change the speed or direction of time +// Also serves as a handy instancing node, since it just passes its data through. +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor ------------------------------ +// ----- +plMatrixTimeScale::plMatrixTimeScale() +: plMatrixChannel(), fTimeSource(nil), fChannelIn(nil) +{ +} + +// ctor ------------------------------------------------------------------------ +// ----- +plMatrixTimeScale::plMatrixTimeScale(plMatrixChannel *channel, + plScalarChannel *timeSource) +: fChannelIn(channel), + fTimeSource(timeSource) +{ +} + +// dtor ------------------------------- +// ----- +plMatrixTimeScale::~plMatrixTimeScale() +{ +} + +// IsStoppedAt ---------------------------- +// ------------ +plMatrixTimeScale::IsStoppedAt(double time) +{ + return fTimeSource->IsStoppedAt(time); +} + +// Value ---------------------------------------------------------- +// ------ +const hsMatrix44 & plMatrixTimeScale::Value(double time, bool peek) +{ + fResult = fChannelIn->Value(fTimeSource->Value(time, peek), peek); + + return fResult; +} + +const hsAffineParts & plMatrixTimeScale::AffineValue(double time, bool peek) +{ + fAP = fChannelIn->AffineValue(fTimeSource->Value(time, peek), peek); + return fAP; +} + +// Detach ---------------------------------------------------- +// ------- +plAGChannel * plMatrixTimeScale::Detach(plAGChannel * detach) +{ + plAGChannel *result = this; + + // HAVE to recurse on the incoming channel in case there are cycles; + // even if we're detaching this node it might also be further upstream + fChannelIn = plMatrixChannel::ConvertNoRef(fChannelIn->Detach(detach)); + + // If you delete a timescale, it is not replaced with its upstream node; + // it's just gone. + if(!fChannelIn || detach == this) + result = nil; + + if(result != this) + delete this; + + return result; +} + +// Dump --------------------------------------------- +// ----- +void plMatrixTimeScale::Dump(int indent, bool optimized, double time) +{ + std::string indentStr; + for(int i = 0; i < indent; i++) + { + indentStr += "- "; + } + hsStatusMessageF("%s matTimeScale <%s> at time <%f>", indentStr.c_str(), fName, fTimeSource->Value(time, true)); + fChannelIn->Dump(indent + 1, optimized, time); + +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plMatrixBlend +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor ---------------------- +// ----- +plMatrixBlend::plMatrixBlend() +: fChannelA(nil), + fChannelB(nil), + fChannelBias(nil) +{ +} + +// ctor ---------------------------------------------------------------------------- +// ----- +plMatrixBlend::plMatrixBlend(plMatrixChannel * channelA, plMatrixChannel * channelB, + plScalarChannel * channelBias, int priority) +: fChannelA(channelA), + fOptimizedA(channelA), + fChannelB(channelB), + fOptimizedB(channelB), + fChannelBias(channelBias), + fPriority(priority) +{ +} + +// dtor ----------------------- +// ----- +plMatrixBlend::~plMatrixBlend() +{ + fChannelA = nil; + fChannelB = nil; + fChannelBias = nil; +} + +// MakeBlend -------------------------------------------------- +// ---------- +plAGChannel * plMatrixBlend::MakeBlend(plAGChannel *newChannel, + plScalarChannel *channelBias, + int blendPriority) +{ + plMatrixChannel * newMatChan = plMatrixChannel::ConvertNoRef(newChannel); + plAGChannel *result = this; + int effectiveBlendPriority = (blendPriority == -1 ? fPriority : blendPriority); + + if(newMatChan) + { + if(effectiveBlendPriority >= fPriority) + { + // if the new channel has higher priority, just do it. + result = plMatrixChannel::MakeBlend(newMatChan, channelBias, effectiveBlendPriority); + } else { + // we're higher priority: pass to our upstream channel + fChannelA = plMatrixChannel::ConvertNoRef(fChannelA->MakeBlend(newChannel, channelBias, blendPriority)); + hsAssert(fChannelA, "MakeBlend returned non-matrix channel."); + // ask our upstream channel to do the blend: it can't be atop us + // this request will get recursively delegated until the priorities work. + } + } + return result; +} + +UInt16 plMatrixBlend::GetPriority() { + return fPriority; +} + +hsBool plMatrixBlend::IsStoppedAt(double time) +{ + hsScalar blend = fChannelBias->Value(time); + if (blend == 0) + return fChannelA->IsStoppedAt(time); + if (blend == 1) + return fChannelB->IsStoppedAt(time); + + return (fChannelA->IsStoppedAt(time) && fChannelB->IsStoppedAt(time)); +} + +// Value ------------------------------------------------------ +// ------ +const hsMatrix44 & plMatrixBlend::Value(double time, bool peek) +{ + const hsAffineParts &parts = AffineValue(time, peek); + + plProfile_BeginTiming(AffineCompose); + parts.ComposeMatrix(&fResult); + plProfile_EndTiming(AffineCompose); + return fResult; +} + +// AffineValue --------------------------------------------------------- +// ------------ +const hsAffineParts & plMatrixBlend::AffineValue(double time, bool peek) +{ + const hsScalar &blend = fChannelBias->Value(time); + if(blend == 0) { + return fOptimizedA->AffineValue(time, peek); + } else { + if(blend == 1) { + return fOptimizedB->AffineValue(time, peek); + } else { + const hsAffineParts &apA = fChannelA->AffineValue(time, peek); + const hsAffineParts &apB = fChannelB->AffineValue(time, peek); + + plProfile_BeginTiming(AffineBlend); + hsInterp::LinInterp(&apA, &apB, blend, &fAP); + plProfile_EndTiming(AffineBlend); + } + } + return fAP; +} + +// Detach ---------------------------------------------- +// ------- +plAGChannel * plMatrixBlend::Detach(plAGChannel *remove) +{ + plAGChannel *result = this; + + // it's possible that the incoming channel could reside down *all* of our + // branches (it's a graph, not a tree,) so we always pass down all limbs + fChannelBias = plScalarChannel::ConvertNoRef(fChannelBias->Detach(remove)); + fChannelA = plMatrixChannel::ConvertNoRef(fChannelA->Detach(remove)); + fChannelB = plMatrixChannel::ConvertNoRef(fChannelB->Detach(remove)); + + if(!fChannelBias) + result = fChannelA; + else if(fChannelA && !fChannelB) + result = fChannelA; + else if(fChannelB && !fChannelA) + result = fChannelB; + else if(!fChannelA && !fChannelB) + result = nil; + + if(result != this) + delete this; + + return result; +} + +// Optimize ------------------------------------- +// --------- +plAGChannel *plMatrixBlend::Optimize(double time) +{ + fOptimizedA = (plMatrixChannel *)fChannelA->Optimize(time); + fOptimizedB = (plMatrixChannel *)fChannelB->Optimize(time); + hsScalar blend = fChannelBias->Value(time); + if(blend == 0.0f) + return fOptimizedA; + if(blend == 1.0f) + return fOptimizedB; + else + return this; +} + +// Dump ----------------------------------------- +// ----- +void plMatrixBlend::Dump(int indent, bool optimized, double time) +{ + std::string indentStr; + for(int i = 0; i < indent; i++) + { + indentStr += "- "; + } + hsStatusMessageF("%s matBlend<%s>, bias:<%f>", indentStr.c_str(), fName, fChannelBias->Value(time, true)); + if(optimized) + { + fOptimizedB->Dump(indent + 1, optimized, time); + fOptimizedA->Dump(indent + 1, optimized, time); + } else { + fChannelB->Dump(indent + 1, optimized, time); + fChannelA->Dump(indent + 1, optimized, time); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plMatrixControllerChannel +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor ---------------------------------------------- +// ----- +plMatrixControllerChannel::plMatrixControllerChannel() +: plMatrixChannel(), fController(nil) +{ +} + +// ctor --------------------------------------------------------------- +// ----- +plMatrixControllerChannel::plMatrixControllerChannel(plController *controller, + hsAffineParts *parts) +: fController(controller) +{ + fAP = *parts; +} + +// dtor ----------------------------------------------- +// ----- +plMatrixControllerChannel::~plMatrixControllerChannel() +{ + if(fController) { + delete fController; + fController = nil; + } +} + +// Value ------------------------------------------------------------------ +// ------ +const hsMatrix44 & plMatrixControllerChannel::Value(double time, bool peek) +{ + return Value(time, peek, nil); +} + +// Value ------------------------------------------------------------------ +// ------ +const hsMatrix44 & plMatrixControllerChannel::Value(double time, bool peek, + plControllerCacheInfo *cache) +{ + plProfile_BeginTiming(AffineInterp); + fController->Interp((hsScalar)time, &fAP, cache); + plProfile_EndTiming(AffineInterp); + + plProfile_BeginTiming(AffineCompose); + fAP.ComposeMatrix(&fResult); + plProfile_EndTiming(AffineCompose); + return fResult; +} + +// AffineValue --------------------------------------------------------------------- +// ------------ +const hsAffineParts & plMatrixControllerChannel::AffineValue(double time, bool peek) +{ + return AffineValue(time, peek, nil); +} + +// AffineValue --------------------------------------------------------------------- +// ------------ +const hsAffineParts & plMatrixControllerChannel::AffineValue(double time, bool peek, + plControllerCacheInfo *cache) +{ + plProfile_BeginTiming(AffineInterp); + fController->Interp((hsScalar)time, &fAP, cache); + plProfile_EndTiming(AffineInterp); + return fAP; +} + +// MakeCacheChannel ------------------------------------------------------------ +// ----------------- +plAGChannel *plMatrixControllerChannel::MakeCacheChannel(plAnimTimeConvert *atc) +{ + plControllerCacheInfo *cache = fController->CreateCache(); + cache->SetATC(atc); + return TRACKED_NEW plMatrixControllerCacheChannel(this, cache); +} + +void plMatrixControllerChannel::Dump(int indent, bool optimized, double time) +{ + std::string indentStr; + for(int i = 0; i < indent; i++) + { + indentStr += "- "; + } + hsStatusMessageF("%s MatController<%s>", indentStr.c_str(), fName); +} + +// Write ------------------------------------------------------------- +// ------ +void plMatrixControllerChannel::Write(hsStream *stream, hsResMgr *mgr) +{ + plMatrixChannel::Write(stream, mgr); + + hsAssert(fController, "Trying to write plMatrixControllerChannel with nil controller. File will not be importable."); + mgr->WriteCreatable(stream, fController); + + fAP.Write(stream); +} + +// Read ------------------------------------------------------------- +// ------ +void plMatrixControllerChannel::Read(hsStream *stream, hsResMgr *mgr) +{ + plMatrixChannel::Read(stream, mgr); + + fController = plController::ConvertNoRef(mgr->ReadCreatable(stream)); + + fAP.Read(stream); +} + +///////////////////////////////// +// PLMATRIXCONTROLLERCACHECHANNEL +///////////////////////////////// + +// CTOR +plMatrixControllerCacheChannel::plMatrixControllerCacheChannel() +: plMatrixChannel(), fControllerChannel(nil), fCache(nil) +{ +} + +// CTOR(name, controller) +plMatrixControllerCacheChannel::plMatrixControllerCacheChannel(plMatrixControllerChannel *controller, plControllerCacheInfo *cache) +: fControllerChannel(controller), fCache(cache) +{ +} + +// ~DTOR() +plMatrixControllerCacheChannel::~plMatrixControllerCacheChannel() +{ + delete fCache; + fControllerChannel = nil; +} + +// VALUE(time) +const hsMatrix44 & plMatrixControllerCacheChannel::Value(double time, bool peek) +{ + return fControllerChannel->Value(time, peek, fCache); +} + +const hsAffineParts & plMatrixControllerCacheChannel::AffineValue(double time, bool peek) +{ + return fControllerChannel->AffineValue(time, peek, fCache); +} + +// DETACH +plAGChannel * plMatrixControllerCacheChannel::Detach(plAGChannel * detach) +{ + plAGChannel *result = this; + + fControllerChannel = + plMatrixControllerChannel::ConvertNoRef(fControllerChannel->Detach(detach)); + + if(detach == this) + result = fControllerChannel; + + if(!fControllerChannel) + result = nil; + + if(result != this) + delete this; + + return result; +} + +///////////////////// +// PLQUATPOINTCOMBINE +///////////////////// + +// CTOR +plQuatPointCombine::plQuatPointCombine() +: fQuatChannel(nil), fPointChannel(nil) +{ +} + +// CTOR +plQuatPointCombine::plQuatPointCombine(plQuatChannel *quatChannel, plPointChannel *pointChannel) +: fQuatChannel(quatChannel), + fPointChannel(pointChannel) +{ +} + +// DTOR +plQuatPointCombine::~plQuatPointCombine() +{ + if(fQuatChannel) { + //XXX delete fQuatChannel; + fQuatChannel = nil; + } + if(fPointChannel) { + //XXX delete fPointChannel; + fPointChannel = nil; + } +} + +// VALUE(time) +const hsMatrix44 & plQuatPointCombine::Value(double time) +{ + if(fQuatChannel) + { + const hsQuat &quat = fQuatChannel->Value(time); + quat.MakeMatrix(&fResult); + } else { + fResult.Reset(); + } + if(fPointChannel) + { + const hsPoint3 &point = fPointChannel->Value(time); + fResult.SetTranslate(&point); + } + return fResult; +} + +const hsAffineParts & plQuatPointCombine::AffineValue(double time) +{ + // XXX Lame hack to get things to compile for now. + // Will fix when we actually start using this channel type. + gemAffineParts gemParts1; + decomp_affine(Value(time).fMap, &gemParts1); + AP_SET(fAP, gemParts1); + + return fAP; +} + +// DETACH +plAGChannel * plQuatPointCombine::Detach(plAGChannel *channel) +{ + hsAssert(this != channel, "Can't detach combiners or blenders directly. Detach sub-channels instead."); + if(this != channel) + { + // *** check the types on the replacement channels to make sure they're compatible + fQuatChannel = (plQuatChannel *)fQuatChannel->Detach(channel); + fPointChannel = (plPointChannel *)fPointChannel->Detach(channel); + } + return this; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// PLMATRIXCHANNELAPPLICATOR +// +/////////////////////////////////////////////////////////////////////////////////////////// + +// IAPPLY +void plMatrixChannelApplicator::IApply(const plAGModifier *mod, double time) +{ + if(fChannel) + { + plMatrixChannel *matChan = plMatrixChannel::ConvertNoRef(fChannel); + + if(matChan) + { + hsMatrix44 inverse; + + plProfile_BeginTiming(AffineValue); + const hsAffineParts &ap = matChan->AffineValue(time); + plProfile_EndTiming(AffineValue); + + hsMatrix44 result; + + plProfile_BeginTiming(AffineCompose); + ap.ComposeMatrix(&result); + ap.ComposeInverseMatrix(&inverse); + //result.GetInverse(&inverse); + plProfile_EndTiming(AffineCompose); + + plProfile_BeginTiming(MatrixApplicator); + plCoordinateInterface *CI = IGetCI(mod); + CI->SetLocalToParent(result, inverse); + plProfile_EndTiming(MatrixApplicator); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// plMatrixDelayedCorrectionApplicator +// +/////////////////////////////////////////////////////////////////////////////////////////// + +const hsScalar plMatrixDelayedCorrectionApplicator::fDelayLength = 1.f; // seconds + +void plMatrixDelayedCorrectionApplicator::SetCorrection(hsMatrix44 &cor) +{ + if (fIgnoreNextCorrection) + { + // We want the first correction we get from an avatar to be + // instantaneous, otherwise they float over from (0, 0, 0). + fIgnoreNextCorrection = false; + return; + } + + // decomp_affine seems to always give us the smaller angle quaternion, + // which looks right visually when we interp. If certain cases become + // visually annoying, we can check and adjust things here. + gemAffineParts gemParts1; + decomp_affine(cor.fMap, &gemParts1); + AP_SET(fCorAP, gemParts1); + + fDelayStart = hsTimer::GetSysSeconds(); +} + + +// CANBLEND +hsBool plMatrixDelayedCorrectionApplicator::CanBlend(plAGApplicator *app) +{ + plMatrixChannelApplicator *matChannelApp = plMatrixChannelApplicator::ConvertNoRef(app); + + if( plMatrixChannelApplicator::ConvertNoRef(app) ) + { + return true; + } + return false; +} + +// IAPPLY +void plMatrixDelayedCorrectionApplicator::IApply(const plAGModifier *mod, double time) +{ + if(fChannel) + { + if(fEnabled) + { + plMatrixChannel *matChan = plMatrixChannel::ConvertNoRef(fChannel); + hsAssert(matChan, "Invalid channel given to plMatrixChannelApplicator"); + + plProfile_BeginTiming(MatrixApplicator); + plCoordinateInterface *CI = IGetCI(mod); + const hsMatrix44 &animResult = matChan->Value(time); + hsMatrix44 localResult; + hsMatrix44 localInverse; + + if (time < fDelayStart + fDelayLength) + { + hsAffineParts identAP; + identAP.Reset(); + hsAffineParts interpAP; + hsMatrix44 interpResult; + + hsScalar blend = (hsScalar)((time - fDelayStart) / fDelayLength); + hsInterp::LinInterp(&fCorAP, &identAP, blend, &interpAP); + interpAP.ComposeMatrix(&interpResult); + + localResult = interpResult * animResult; + localResult.GetInverse(&localInverse); + CI->SetLocalToParent(localResult, localInverse); + } + else + { + animResult.GetInverse(&localInverse); + CI->SetLocalToParent(animResult, localInverse); + } + plProfile_EndTiming(MatrixApplicator); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// PLMATRIXDIFFERENCEAPPLICATOR +// +/////////////////////////////////////////////////////////////////////////////////////////// + +// Reset ------------------------------------- +// ------ +void plMatrixDifferenceApp::Reset(double time) +{ + hsAssert(fChannel,"Missing input channel when resetting."); + if(fChannel) + { + plMatrixChannel *matChan = plMatrixChannel::ConvertNoRef(fChannel); + hsAssert(matChan, "Invalid channel given to plMatrixChannelApplicator"); + if(matChan) + { + hsMatrix44 L2A, A2L; + const hsAffineParts &ap = matChan->AffineValue(time); + ap.ComposeMatrix(&L2A); // what comes out of AffineValue is a local-to-animation + ap.ComposeInverseMatrix(&A2L); + + fLastA2L = A2L; + fLastL2A = L2A; + } + } +} + +// CanBlend ----------------------------------------------- +// --------- +hsBool plMatrixDifferenceApp::CanBlend(plAGApplicator *app) +{ + plMatrixChannelApplicator *matChannelApp = plMatrixChannelApplicator::ConvertNoRef(app); + + if( plMatrixChannelApplicator::ConvertNoRef(app) ) + { + return true; + } + return false; +} + +// *** move this somewhere real +bool CompareMatrices2(const hsMatrix44 &matA, const hsMatrix44 &matB, float tolerance) +{ + bool c00 = fabs(matA.fMap[0][0] - matB.fMap[0][0]) < tolerance; + bool c01 = fabs(matA.fMap[0][1] - matB.fMap[0][1]) < tolerance; + bool c02 = fabs(matA.fMap[0][2] - matB.fMap[0][2]) < tolerance; + bool c03 = fabs(matA.fMap[0][3] - matB.fMap[0][3]) < tolerance; + + bool c10 = fabs(matA.fMap[1][0] - matB.fMap[1][0]) < tolerance; + bool c11 = fabs(matA.fMap[1][1] - matB.fMap[1][1]) < tolerance; + bool c12 = fabs(matA.fMap[1][2] - matB.fMap[1][2]) < tolerance; + bool c13 = fabs(matA.fMap[1][3] - matB.fMap[1][3]) < tolerance; + + bool c20 = fabs(matA.fMap[2][0] - matB.fMap[2][0]) < tolerance; + bool c21 = fabs(matA.fMap[2][1] - matB.fMap[2][1]) < tolerance; + bool c22 = fabs(matA.fMap[2][2] - matB.fMap[2][2]) < tolerance; + bool c23 = fabs(matA.fMap[2][3] - matB.fMap[2][3]) < tolerance; + + bool c30 = fabs(matA.fMap[3][0] - matB.fMap[3][0]) < tolerance; + bool c31 = fabs(matA.fMap[3][1] - matB.fMap[3][1]) < tolerance; + bool c32 = fabs(matA.fMap[3][2] - matB.fMap[3][2]) < tolerance; + bool c33 = fabs(matA.fMap[3][3] - matB.fMap[3][3]) < tolerance; + + return c00 && c01 && c02 && c03 && c11 && c12 && c13 && c20 && c21 && c22 && c23 && c30 && c31 && c32 && c33; +} + +// IAPPLY +void plMatrixDifferenceApp::IApply(const plAGModifier *mod, double time) +{ + plMatrixChannel *matChan = plMatrixChannel::ConvertNoRef(fChannel); + hsAssert(matChan, "Invalid channel given to plMatrixChannelApplicator"); + hsMatrix44 L2A, A2L; + + const hsAffineParts &ap = matChan->AffineValue(time); + + plProfile_BeginTiming(AffineCompose); + ap.ComposeMatrix(&L2A); // what comes out of AffineValue is a local-to-animation + ap.ComposeInverseMatrix(&A2L); + plProfile_EndTiming(AffineCompose); + + plProfile_BeginTiming(MatrixApplicator); + if(fNew) // if it's new, there's no previous frame to diff against; + { // cache the current and don't do anything + fLastA2L = A2L; + fLastL2A = L2A; + + fNew = false; + } else { + if( ! CompareMatrices2(fLastA2L, A2L, .0001f) && ! CompareMatrices2(fLastL2A, L2A, .0001f)) + { + plCoordinateInterface *CI = IGetCI(mod); + + hsMatrix44 l2p = CI->GetLocalToParent(); + hsMatrix44 p2l = CI->GetParentToLocal(); + + hsMatrix44 prev2Cur = fLastA2L * L2A; // previous to current in local space + hsMatrix44 cur2Prev = A2L * fLastL2A; // current to previous in local space + + hsMatrix44 newL2P = l2p * prev2Cur; + hsMatrix44 newP2L = cur2Prev * p2l; + + CI->SetLocalToParent(newL2P, newP2L); + CI->FlushTransform(); + + fLastL2A = L2A; + fLastA2L = A2L; + } + } + plProfile_EndTiming(MatrixApplicator); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// PLIK2APPLICATOR +// +/////////////////////////////////////////////////////////////////////////////////////////// + +/** A two-bone IK applicator. + */ +class plIK2Applicator : public plMatrixChannelApplicator +{ +public: + + // The latest time we were asked to evaluate. We won't actually do the evaluation + // until the other bone is asked as well. + double GetUpdateTime(); + + void SetIsEndEffector(bool status); + bool GetIsEndEffector(); + + void SetTarget(hsPoint3 &worldPoint); + +private: + virtual void IApply(const plAGModifier *mod, double time); + void ISolve(); + // The other bone involved in the IK solution + plIK2Applicator *fOtherBone; + // The latest time we were asked to evaluate. We won't actually run our + // process until the other guy is asked to evaluate the same time. + double fUpdateTime; + + hsPoint3 fTarget; + bool fIsEndEffector; +}; + +// GetUpdateTime --------------- +double plIK2Applicator::GetUpdateTime() +{ + return fUpdateTime; +} + +void plIK2Applicator::SetIsEndEffector(bool status) +{ + fIsEndEffector = status; +} + +bool plIK2Applicator::GetIsEndEffector() +{ + return fIsEndEffector; +} + +void plIK2Applicator::IApply(const plAGModifier *mod, double time) +{ + fUpdateTime = time; + if(time == fOtherBone->GetUpdateTime()) + { + // we're both up-to-date: go ahead and solve + ISolve(); + } +} + +void plIK2Applicator::ISolve() +{ + +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMatrixChannel.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMatrixChannel.h new file mode 100644 index 00000000..3de13fd8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMatrixChannel.h @@ -0,0 +1,368 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLMATRIXCHANNEL_INC +#define PLMATRIXCHANNEL_INC + +// local +#include "plScalarChannel.h" + +// global +#include "hsTypes.h" // you need types to include Matrix +#include "hsMatrix44.h" +#include "../plTransform/hsAffineParts.h" + +// local prototypes +class plQuatChannel; +class plPointChannel; + +// external prototypes +class plController; +class hsAffineParts; +class plAnimTimeConvert; +class plMatrixChannelApplicator; +class plControllerCacheInfo; + +////////////////// +// PLMATRIXCHANNEL +////////////////// +// an animation channel that outputs matrices +class plMatrixChannel : public plAGChannel +{ +protected: + hsMatrix44 fResult; + hsAffineParts fAP; + +public: + plMatrixChannel(); + virtual ~plMatrixChannel(); + + // AG PROTOCOL + virtual const hsMatrix44 & Value(double time, bool peek = false); + virtual void Value(hsMatrix44 &matrix, double time, bool peek = false); + virtual const hsAffineParts & AffineValue(double time, bool peek = false); + + // combine it (allocates combine object) + virtual plAGChannel * MakeCombine(plAGChannel * channelB); + + // blend it (allocates blend object) + virtual plAGChannel * MakeBlend(plAGChannel * channelB, plScalarChannel * channelBias, int blendPriority); + + // const eval at time zero + virtual plAGChannel * MakeZeroState(); + // make a timeScale instance + virtual plAGChannel * MakeTimeScale(plScalarChannel *timeSource); + + virtual plAGPinType GetPinType() { return kAGPinTransform; }; + + virtual void Dump(int indent, bool optimized, double time); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plMatrixChannel ); + GETINTERFACE_ANY( plMatrixChannel, plAGChannel ); +}; + +/////////////////// +// PLMATRIXCONSTANT +/////////////////// +// A matrix source that just keeps handing out the same value +class plMatrixConstant : public plMatrixChannel +{ +public: + plMatrixConstant(); + plMatrixConstant(const hsMatrix44 &value); + virtual ~plMatrixConstant(); + + void Set(const hsMatrix44 &value); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plMatrixConstant ); + GETINTERFACE_ANY( plMatrixConstant, plMatrixChannel ); + + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *s, hsResMgr *mgr); +}; + +//////////////////// +// PLMATRIXTIMESCALE +//////////////////// +// Adapts the time scale before passing it to the next channel in line. +// Use to instance animations while allowing each instance to run at different speeds. +class plMatrixTimeScale : public plMatrixChannel +{ +protected: + plScalarChannel *fTimeSource; + plMatrixChannel *fChannelIn; + +public: + plMatrixTimeScale(); + plMatrixTimeScale(plMatrixChannel *channel, plScalarChannel *timeSource); + virtual ~plMatrixTimeScale(); + + virtual hsBool IsStoppedAt(double time); + virtual const hsMatrix44 & Value(double time, bool peek = false); + virtual const hsAffineParts & AffineValue(double time, bool peek = false); + + virtual plAGChannel * Detach(plAGChannel * channel); + + virtual void Dump(int indent, bool optimized, double time); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plMatrixTimeScale ); + GETINTERFACE_ANY( plMatrixTimeScale, plMatrixChannel ); +}; + +//////////////// +// PLMATRIXBLEND +//////////////// +// blends two matrices into one with weighting +class plMatrixBlend : public plMatrixChannel +{ +protected: + plMatrixChannel * fChannelA; + plMatrixChannel * fOptimizedA; + plMatrixChannel * fChannelB; + plMatrixChannel * fOptimizedB; + plScalarChannel * fChannelBias; + int fPriority; + +public: + // xTORs + plMatrixBlend(); + plMatrixBlend(plMatrixChannel * channelA, plMatrixChannel * channelB, plScalarChannel * channelBias, int priority); + virtual ~plMatrixBlend(); + + virtual plAGChannel * plMatrixBlend::MakeBlend(plAGChannel *newChannel, plScalarChannel *channelBias, int blendPriority); + + // you cannot blend on top of a channel that has higher priority than you do. + virtual UInt16 GetPriority(); + + // SPECIFICS + const plMatrixChannel * GetChannelA() const { return fChannelA; } + void SetChannelA(plMatrixChannel * channel) { fChannelA = channel; } + + const plMatrixChannel * GetChannelB() const { return fChannelB; } + void SetChannelB(plMatrixChannel * channel) { fChannelB = channel; } + + const plScalarChannel * GetChannelBias() const { return fChannelBias; } + void SetChannelBias(plScalarChannel * channel) { fChannelBias = channel; } + + //virtual void SetBlend(float blend) { fBlend = blend; }; + //virtual float GetBlend() { return fBlend; }; + + virtual hsBool IsStoppedAt(double time); + + // AG PROTOCOL + virtual const hsMatrix44 & Value(double time, bool peek = false); + virtual const hsAffineParts & AffineValue(double time, bool peek = false); + + // remove the specified channel from our graph + virtual plAGChannel * Detach(plAGChannel * channel); + virtual void Dump(int indent, bool optimized, double time); + + plAGChannel *plMatrixBlend::Optimize(double time); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plMatrixBlend ); + GETINTERFACE_ANY( plMatrixBlend, plMatrixChannel ); +}; + +///////////////////// +// PLMATRIXCONTROLLER +///////////////////// +// converts a plController-style animation into a plMatrixChannel +class plMatrixControllerChannel : public plMatrixChannel +{ +protected: + plController *fController; + +public: + // xTORs + plMatrixControllerChannel(); + plMatrixControllerChannel(plController *controller, hsAffineParts *parts); + virtual ~plMatrixControllerChannel(); + + // AG PROTOCOL + virtual const hsAffineParts & AffineValue(double time, bool peek = false); + virtual const hsAffineParts & AffineValue(double time, bool peek, plControllerCacheInfo *cache); + virtual const hsMatrix44 & Value(double time, bool peek = false); + virtual const hsMatrix44 & Value(double time, bool peek, plControllerCacheInfo *cache); + + virtual plAGChannel * MakeCacheChannel(plAnimTimeConvert *atc); + + virtual void Dump(int indent, bool optimized, double time); + + // PLASMA PROTOCOL + // rtti + CLASSNAME_REGISTER( plMatrixControllerChannel ); + GETINTERFACE_ANY( plMatrixControllerChannel, plMatrixChannel ); + + // persistence + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *s, hsResMgr *mgr); +}; + +///////////////////////////////// +// PLMATRIXCONTROLLERCACHECHANNEL +///////////////////////////////// +// Same as plMatrixController, but with caching info +class plMatrixControllerCacheChannel : public plMatrixChannel +{ +protected: + plControllerCacheInfo *fCache; + plMatrixControllerChannel *fControllerChannel; + +public: + plMatrixControllerCacheChannel(); + plMatrixControllerCacheChannel(plMatrixControllerChannel *channel, plControllerCacheInfo *cache); + virtual ~plMatrixControllerCacheChannel(); + + virtual const hsMatrix44 & Value(double time, bool peek = false); + virtual const hsAffineParts & AffineValue(double time, bool peek = false); + + virtual plAGChannel * Detach(plAGChannel * channel); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plMatrixControllerCacheChannel ); + GETINTERFACE_ANY( plMatrixControllerCacheChannel, plMatrixChannel ); + + // Created at runtime only, so no Read/Write +}; + +///////////////////// +// PLQUATPOINTCOMBINE +///////////////////// +// combines a quaternion and a point into a matrix +class plQuatPointCombine : public plMatrixChannel +{ +protected: + plQuatChannel *fQuatChannel; + plPointChannel *fPointChannel; + +public: + // xTORs + plQuatPointCombine(); + plQuatPointCombine(plQuatChannel *quatChannel, plPointChannel *pointChannel); + virtual ~plQuatPointCombine(); + + // SPECIFICS + const plQuatChannel * GetQuatChannel() const { return fQuatChannel; } + void SetQuatChannel(plQuatChannel * channel) { fQuatChannel = channel; } + + plPointChannel * GetPointChannel() const { return fPointChannel; } + void SetPointChannel(plPointChannel * channel) { fPointChannel = channel; } + + // AG PROTOCOL + virtual const hsMatrix44 & Value(double time); + virtual const hsAffineParts & AffineValue(double time); + + // remove the specified channel from our graph + virtual plAGChannel * Detach(plAGChannel * channel); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plQuatPointCombine ); + GETINTERFACE_ANY( plQuatPointCombine, plMatrixChannel ); +}; + + +///////////////////// +// +// Applicator classes + +class plMatrixChannelApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plMatrixChannelApplicator ); + GETINTERFACE_ANY( plMatrixChannelApplicator, plAGApplicator ); + + virtual hsBool CanCombine(plAGApplicator *app) { return false; } + virtual plAGPinType GetPinType() { return kAGPinTransform; } +}; + +// PLMATRIXDELAYEDCORRECTIONAPPLICATOR +// Used for blending in sudden location changes due to synch messages. +// This app tacks on a correction to the l2p transform +// (so l2p is set as animL2P*correction) +// interpolating this to the identity matrix over time. +class plMatrixDelayedCorrectionApplicator : public plMatrixChannelApplicator +{ +protected: + hsAffineParts fCorAP; // AP of the correction. + double fDelayStart; // Start time of the delayed correction + + // apply our animation * our correction to the node + virtual void IApply(const plAGModifier *mod, double time); + +public: + plMatrixDelayedCorrectionApplicator() : fDelayStart(-1000.f), fIgnoreNextCorrection(true) { fCorAP.Reset(); } + + CLASSNAME_REGISTER( plMatrixDelayedCorrectionApplicator ); + GETINTERFACE_ANY( plMatrixDelayedCorrectionApplicator, plMatrixChannelApplicator ); + + void SetCorrection(hsMatrix44 &correction); + virtual hsBool AutoDelete() { return false; } // should we remove it when its input channel is gone? + + // applicator arbitration... + virtual plAGPinType GetPinType() { return kAGPinTransform; } + virtual hsBool CanBlend(plAGApplicator *app); + + hsBool fIgnoreNextCorrection; + static const hsScalar fDelayLength; // static var for now. +}; + +// PLMATRIXDIFFERENCEAPP +// Each frame, this guy takes the difference between his current value +// and his last value and applies that to the current world +// transform of the target. +// You could also call it the Temporal Matrix Difference Applicator, +// but that sucks to type. +class plMatrixDifferenceApp : public plMatrixChannelApplicator +{ +public: + /** Forget the previous cached transform and start again */ + void Reset(double time); + + /** Should this applicator be automatically removed when its channel goes away? */ + virtual hsBool AutoDelete() { return false; } + + // applicator arbitration + virtual plAGPinType GetPinType() { return kAGPinTransform; } + virtual hsBool CanBlend(plAGApplicator *app); + + CLASSNAME_REGISTER(plMatrixDifferenceApp); + GETINTERFACE_ANY(plMatrixDifferenceApp, plMatrixChannelApplicator); + +protected: + virtual void IApply(const plAGModifier *mod, double time); + hsMatrix44 fLastL2A; // local to animation space + hsMatrix44 fLastA2L; // animation space to local + bool fNew; // true if we haven't cached anything yet +}; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMultistageBehMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMultistageBehMod.cpp new file mode 100644 index 00000000..f1859327 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMultistageBehMod.cpp @@ -0,0 +1,245 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +// singular +#include "plMultistageBehMod.h" + +// local +#include "plAvBrainGeneric.h" +#include "plAnimStage.h" +#include "plArmatureMod.h" + +// global +#include "hsResMgr.h" + +//other +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plMultistageMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plInputCore/plAvatarInputInterface.h" + +#ifdef DEBUG_MULTISTAGE +#include "plAvatarMgr.h" +#include "../plStatusLog/plStatusLog.h" +#endif + +plMultistageBehMod::plMultistageBehMod() : fStages(nil), fFreezePhys(false), fSmartSeek(false), fReverseFBControlsOnRelease(false), fNetProp(true), fNetForce(false) +{ +} + +plMultistageBehMod::plMultistageBehMod(plAnimStageVec* stages, + bool freezePhys, + bool smartSeek, + bool reverseFBControlsOnRelease, + std::vector* receivers) + : fStages(stages), + fFreezePhys(freezePhys), + fSmartSeek(smartSeek), + fReverseFBControlsOnRelease(reverseFBControlsOnRelease), + fNetProp(true), + fNetForce(false) +{ + if (receivers) + fReceivers = *receivers; +} + +plMultistageBehMod::~plMultistageBehMod() +{ + IDeleteStageVec(); +} + +void plMultistageBehMod::Init(plAnimStageVec *stages, + bool freezePhys, + bool smartSeek, + bool reverseFBControlsOnRelease, + std::vector* receivers) +{ + fStages = stages; + fFreezePhys = freezePhys; + fSmartSeek = smartSeek; + fReverseFBControlsOnRelease = reverseFBControlsOnRelease; + if (receivers) + fReceivers = *receivers; +} + +void plMultistageBehMod::IDeleteStageVec() +{ + if (fStages) + { + int numStages = fStages->size(); + for (int i = 0; i < numStages; i++) + { + plAnimStage* stage = (*fStages)[i]; + delete stage; + } + + delete fStages; + fStages = nil; + } +} + +hsBool plMultistageBehMod::MsgReceive(plMessage* msg) +{ + plMultistageModMsg *multiMsg = nil; + plNotifyMsg* notifyMsg = plNotifyMsg::ConvertNoRef(msg); + if (notifyMsg) + { + hsAssert(fStages, "Trying to trigger multistage, but no stages are present."); + if(fStages) + { + plKey avKey = notifyMsg->GetAvatarKey(); + hsAssert(avKey, "Avatar key missing trying to trigger multistage."); + if(avKey) + { + plSceneObject *avObj = plSceneObject::ConvertNoRef(avKey->ObjectIsLoaded()); + hsAssert(avObj, "Avatar not loaded when trying to trigger multistage."); + if(avObj) + { + // Create a copy of our reference anim stages to give to the brain + plAnimStageVec* stages = TRACKED_NEW plAnimStageVec; + int numStages = fStages->size(); + stages->reserve(numStages); + // hack hack hack + hsBool ladder = false; + for (int i = 0; i < numStages; i++) + { + plAnimStage* stage = TRACKED_NEW plAnimStage; + *stage = *((*fStages)[i]); + stages->push_back(stage); + if (strstr(stage->GetAnimName(),"adder") != nil) + ladder = true; + } + + const plArmatureMod *avMod = (plArmatureMod*)avObj->GetModifierByType(plArmatureMod::Index()); + hsAssert(avMod, "Missing armature mod on avatar scene object."); + + if(avMod) + { + plKey sender = notifyMsg->GetSender(); + plKey avModKey = avMod->GetKey(); + plKey seekKey = GetTarget()->GetKey(); // our seek point + +#ifdef DEBUG_MULTISTAGE + char sbuf[256]; + sprintf(sbuf,"plMultistageModMsg - starting multistage from %s",sender->GetName()); + plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf); +#endif + plAvSeekMsg *seeker = TRACKED_NEW plAvSeekMsg(nil, avModKey, seekKey, 1.0f, fSmartSeek); + seeker->Send(); + + // these (currently unused) callbacks are for the brain itself, not any of the stages + plMessage *exitCallback = nil, *enterCallback = nil; + UInt32 exitFlags = plAvBrainGeneric::kExitNormal; + + plAvBrainGeneric *brain = TRACKED_NEW plAvBrainGeneric(stages, exitCallback, enterCallback, sender, exitFlags, + plAvBrainGeneric::kDefaultFadeIn, plAvBrainGeneric::kDefaultFadeOut, + plAvBrainGeneric::kMoveRelative); + if (ladder) + { + brain->SetType(plAvBrainGeneric::kLadder); + } + brain->SetReverseFBControlsOnRelease(fReverseFBControlsOnRelease); + plAvPushBrainMsg* pushBrain = TRACKED_NEW plAvPushBrainMsg(GetKey(), avModKey, brain); + pushBrain->Send(); + } + } + } + } + + return true; + } + else if (multiMsg = plMultistageModMsg::ConvertNoRef(msg)) + { + if (multiMsg->GetCommand(plMultistageModMsg::kSetLoopCount)) + { + ((*fStages)[multiMsg->fStageNum])->SetNumLoops(multiMsg->fNumLoops); + } + return true; + } + else { + return plSingleModifier::MsgReceive(msg); + } +} + +void plMultistageBehMod::Read(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Read(stream, mgr); + + fFreezePhys = stream->Readbool(); + fSmartSeek = stream->Readbool(); + fReverseFBControlsOnRelease = stream->Readbool(); + + IDeleteStageVec(); + fStages = TRACKED_NEW plAnimStageVec; + int numStages = stream->ReadSwap32(); + fStages->reserve(numStages); + + int i; + for (i = 0; i < numStages; i++) + { + plAnimStage* stage = TRACKED_NEW plAnimStage; + stage->Read(stream, mgr); + stage->SetMod(this); + fStages->push_back(stage); + } + + int numReceivers = stream->ReadSwap32(); + fReceivers.clear(); + fReceivers.reserve(numReceivers); + for (i = 0; i < numReceivers; i++) + { + plKey key = mgr->ReadKey(stream); + fReceivers.push_back(key); + } +} + +void plMultistageBehMod::Write(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Write(stream, mgr); + + stream->Writebool(fFreezePhys); + stream->Writebool(fSmartSeek); + stream->Writebool(fReverseFBControlsOnRelease); + + int numStages = fStages->size(); + stream->WriteSwap32(numStages); + + int i; + for (i = 0; i < numStages; i++) + { + plAnimStage* stage = (*fStages)[i]; + stage->Write(stream, mgr); + } + + int numReceivers = fReceivers.size(); + stream->WriteSwap32(numReceivers); + for (i = 0; i < numReceivers; i++) + { + plKey key = fReceivers[i]; + mgr->WriteKey(stream, key); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMultistageBehMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMultistageBehMod.h new file mode 100644 index 00000000..ced8e8c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plMultistageBehMod.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plMultistageBehMod_h_inc +#define plMultistageBehMod_h_inc + +#include "../pnModifier/plSingleModifier.h" +#include "hsStlUtils.h" + +class plAnimStageVec; + +class plMultistageBehMod : public plSingleModifier +{ +protected: + plAnimStageVec* fStages; + bool fFreezePhys; + bool fSmartSeek; + bool fReverseFBControlsOnRelease; + + bool fNetProp; + bool fNetForce; + + std::vector fReceivers; + + void IDeleteStageVec(); + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return true; } + +public: + plMultistageBehMod(); + plMultistageBehMod(plAnimStageVec* stages, bool freezePhys, bool smartSeek, bool reverseFBControlsOnRelease, std::vector* receivers); + virtual ~plMultistageBehMod(); + + CLASSNAME_REGISTER( plMultistageBehMod ); + GETINTERFACE_ANY( plMultistageBehMod, plSingleModifier ); + + hsBool NetProp() { return fNetProp; } + hsBool NetForce() { return fNetForce; } + + void SetNetProp(hsBool netProp) { fNetProp = netProp; } + void SetNetForce(hsBool netForce) { fNetForce = netForce; } + + hsBool MsgReceive(plMessage* msg); + + virtual void Init(plAnimStageVec *stages, bool freezePhys, bool smartSeek, bool reverseFBControlsOnRelease, std::vector* receivers); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + +#endif // plMultistageBehMod_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.cpp new file mode 100644 index 00000000..b716c9a8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.cpp @@ -0,0 +1,184 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +// singular +#include "plNPCSpawnMod.h" + +// local +#include "plAvatarMgr.h" + +// global +#include + +// other +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +//#include "../pnMessage/plWarpMsg.h" +#include "../pnMessage/plNotifyMsg.h" + + +// plNPCSpawnMod ctor +plNPCSpawnMod::plNPCSpawnMod() +: fModelName(nil), + fAccountName(nil), + fAutoSpawn(false), + fNotify(nil) +{ +} + +// plNPCSpawnMod ctor modelName accountName +plNPCSpawnMod::plNPCSpawnMod(const char * modelName, const char * accountName, bool autoSpawn) +: fAutoSpawn(autoSpawn), fNotify(nil) +{ + fModelName = hsStrcpy(modelName); + fAccountName = hsStrcpy(accountName); +} + +// plNPCSpawnMod dtor +plNPCSpawnMod::~plNPCSpawnMod() +{ + if(fModelName) + { + delete[] fModelName; + fModelName = nil; + } + if(fAccountName) + { + delete[] fAccountName; + fAccountName = nil; + } + if (fNotify) + fNotify->UnRef(); +} + +void plNPCSpawnMod::AddTarget(plSceneObject* so) +{ + plSingleModifier::AddTarget(so); + + if(fAutoSpawn) + Trigger(); +} + +void plNPCSpawnMod::RemoveTarget(plSceneObject *so) +{ + plSingleModifier::RemoveTarget(so); + + if(fSpawnedKey) + { + plAvatarMgr::GetInstance()->UnLoadAvatar(fSpawnedKey, false); + } +} + +// TRIGGER +bool plNPCSpawnMod::Trigger() +{ + bool result = false; + + // you can ONLY spawn if you are local. the spawn message + // will netpropagate + if(this->IsLocallyOwned()) + { + if(fModelName) + { + // spawn the NPC + plKey spawnPoint = GetTarget(0)->GetKey(); + + fSpawnedKey = plAvatarMgr::GetInstance()->LoadAvatar(fModelName, fAccountName, false, spawnPoint, nil); + + ISendNotify(fSpawnedKey); + } + } + + return result; +} + +// SETNOTIFY +void plNPCSpawnMod::SetNotify(plNotifyMsg *notify) +{ + fNotify = notify; +} + +// GETNOTIFY +plNotifyMsg * plNPCSpawnMod::GetNotify() +{ + return fNotify; +} + +// READ +void plNPCSpawnMod::Read(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Read(stream, mgr); + + fModelName = stream->ReadSafeString(); + fAccountName = stream->ReadSafeString(); + fAutoSpawn = stream->Readbool(); + if(stream->Readbool()) + fNotify = plNotifyMsg::ConvertNoRef(mgr->ReadCreatable(stream)); +} + +// WRITE +void plNPCSpawnMod::Write(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Write(stream, mgr); + + stream->WriteSafeString(fModelName); + stream->WriteSafeString(fAccountName); + stream->Writebool(fAutoSpawn); + if(fNotify) + { + stream->Writebool(true); + mgr->WriteCreatable(stream, fNotify); + } else { + stream->Writebool(false); + } +} + +// IEVAL +// attack of the bogons +hsBool plNPCSpawnMod::IEval(double secs, hsScalar del, UInt32 dirty) +{ + return true; +} + +// ISENDNOTIFY +void plNPCSpawnMod::ISendNotify(plKey &avatarKey) +{ + if(fNotify) + { + proSpawnedEventData * event = TRACKED_NEW proSpawnedEventData; + event->fSpawner = GetKey(); + event->fSpawnee = avatarKey; + fNotify->ClearEvents(); + fNotify->AddEvent(event); + delete event; // fNotify->AddEvent makes a copy + int i = fNotify->GetEventCount(); + fNotify->Ref(); // so we still hold onto it after it is delivered + fNotify->Send(); + } else { + hsStatusMessage("NPC Spawner is spawning but there is no notify message to send."); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.h new file mode 100644 index 00000000..927612a1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plNPCSpawnMod.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../pnModifier/plSingleModifier.h" + +class plNotifyMsg; + +class plNPCSpawnMod : public plSingleModifier +{ +public: + plNPCSpawnMod(); + plNPCSpawnMod(const char * modelName, const char *accountName, bool autoSpawn); + ~plNPCSpawnMod(); + + bool Trigger(); + void SetNotify(plNotifyMsg *notify); + plNotifyMsg * GetNotify(); + + CLASSNAME_REGISTER( plNPCSpawnMod ); + GETINTERFACE_ANY( plNPCSpawnMod, plSingleModifier ); + + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject *so); +// hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + +protected: + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + void ISendNotify(plKey &avatarKey); // send our notification message + +private: + char *fModelName; + char *fAccountName; + bool fAutoSpawn; // spawn immediately on loading + plKey fSpawnedKey; // if we want to be able to spawn many things, we should make this a vector + plNotifyMsg *fNotify; // notify message that we send when we spawn. +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plOneShotMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plOneShotMod.cpp new file mode 100644 index 00000000..07b9c92d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plOneShotMod.cpp @@ -0,0 +1,171 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +// singular +#include "plOneShotMod.h" + +// local +#include "plArmatureMod.h" +#include "plAvatarMgr.h" + +// global +#include "plgDispatch.h" + +// other +#include "../plMessage/plAvatarMsg.h" + +// CTOR() +plOneShotMod::plOneShotMod() +: fDrivable(false), + fReversable(false), + fSeekDuration(1.0f), + fSmartSeek(false), + fAnimName(nil), + fNoSeek(false) +{ + // this constructor is called from the loader. +} + +// CTOR(char *) +plOneShotMod::plOneShotMod(const char *animName, + hsBool drivable, + hsBool reversable, + float seekDuration, + hsBool smartSeek, + hsBool noSeek) +: fDrivable(drivable), + fReversable(reversable), + fSeekDuration(seekDuration), + fSmartSeek((float)smartSeek), + fNoSeek(noSeek) +{ + fAnimName = hsStrcpy(animName); +} + +// INIT +void plOneShotMod::Init(const char *animName, + hsBool drivable, + hsBool reversable, + float seekDuration, + hsBool smartSeek, + hsBool noSeek) +{ + fAnimName = hsStrcpy(animName); + fDrivable = drivable; + fReversable = reversable; + fSeekDuration = seekDuration; + fSmartSeek = (float)smartSeek; + fNoSeek = noSeek; +} + +// DTOR() +plOneShotMod::~plOneShotMod() +{ + if(fAnimName) { + delete[] fAnimName; + fAnimName = nil; + } +} + + +#include "../plMessage/plOneShotMsg.h" +#include "../plMessage/plOneShotCallbacks.h" + +// MSGRECEIVE +hsBool plOneShotMod::MsgReceive(plMessage* msg) +{ + plOneShotMsg *oneShotMsg = plOneShotMsg::ConvertNoRef(msg); + if (oneShotMsg) + { + // Send a one shot task request to the given target, which darn well better have an avatar modifier on it. + plKey myKey = GetKey(); + plKey objKey = GetTarget(0)->GetKey(); + plKey avKey = oneShotMsg->fPlayerKey; + hsAssert(avKey,"The avatar key is missing in the one shot!"); + + if ( avKey ) + { + plSceneObject *avObj = (plSceneObject *)avKey->ObjectIsLoaded(); + if(avObj) + { + const plArmatureMod *avMod = (plArmatureMod*)avObj->GetModifierByType(plArmatureMod::Index()); + + if(avMod) + { + char *animName = avMod->MakeAnimationName(fAnimName); + + plAvOneShotMsg *avOSmsg = TRACKED_NEW plAvOneShotMsg(myKey, oneShotMsg->fPlayerKey, objKey, + fSeekDuration, (hsBool)fSmartSeek, animName, fDrivable, + fReversable); + + delete [] animName; // AvOneShotMsg constructor copies. + avOSmsg->fNoSeek = fNoSeek; + avOSmsg->SetBCastFlag(plMessage::kPropagateToModifiers); + hsRefCnt_SafeRef(oneShotMsg->fCallbacks); + avOSmsg->fCallbacks = oneShotMsg->fCallbacks; + plgDispatch::MsgSend(avOSmsg); + } + } + + } + return true; + } + return plMultiModifier::MsgReceive(msg); +} + +// ADDTARGET +// Here I am. Announce presence to the avatar registry. +void plOneShotMod::AddTarget(plSceneObject* so) +{ + plMultiModifier::AddTarget(so); + + plAvatarMgr::GetInstance()->AddOneShot(this); +} + +void plOneShotMod::Read(hsStream *stream, hsResMgr *mgr) +{ + plMultiModifier::Read(stream, mgr); + + // read in the name of the animation itself + fAnimName = stream->ReadSafeString(); + fSeekDuration = stream->ReadSwapScalar(); + fDrivable = stream->ReadBool(); + fReversable = stream->ReadBool(); + fSmartSeek = (float)stream->ReadBool(); + fNoSeek = stream->ReadBool(); +} + +void plOneShotMod::Write(hsStream *stream, hsResMgr *mgr) +{ + plMultiModifier::Write(stream, mgr); + + stream->WriteSafeString(fAnimName); + stream->WriteSwapScalar(fSeekDuration); + stream->WriteBool(fDrivable); + stream->WriteBool(fReversable); + stream->WriteBool((hsBool)fSmartSeek); + stream->WriteBool(fNoSeek); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plOneShotMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plOneShotMod.h new file mode 100644 index 00000000..de593195 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plOneShotMod.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plOneShotMod_INC +#define plOneShotMod_INC + +#include "../pnModifier/plMultiModifier.h" +#include "../pnMessage/plMessage.h" + +// PLONESHOTMOD +// A one shot +// - suspends user input +// - moves the avatar to a specific position +// - plays a specific animation +// - returns control to the user from the final position of the animation +// This modifier holds the information that the avatar needs to do that. +class plOneShotMod : public plMultiModifier +{ +protected: + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) {return true;} + char * fAnimName; // the name of the animation associated with this one-shot + hsBool fDrivable; // whether the user can control the position of the animation + hsBool fReversable; // whether the user can back up the animation (fDrivable must be true as well) + float fSeekDuration; // how long to take to get to the seek point (??? should this be speed instead?) + float fSmartSeek; // use smart seek to walk to the seek point? + hsBool fNoSeek; +public: + plOneShotMod(); + plOneShotMod(const char *animName, hsBool drivable, hsBool reversable, float seekDuration, hsBool smartSeek,hsBool noSeek = false); + virtual ~plOneShotMod(); + + void Init(const char *animName, hsBool drivable, hsBool reversable, float seekDuration, hsBool smartSeek, hsBool noSeek = false); + + CLASSNAME_REGISTER( plOneShotMod ); + GETINTERFACE_ANY( plOneShotMod, plMultiModifier ); + + virtual void AddTarget(plSceneObject* so); + hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp new file mode 100644 index 00000000..3c032b41 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.cpp @@ -0,0 +1,965 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPhysicalControllerCore.h" +#include "../plMessage/plLOSHitMsg.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../../NucleusLib/inc/plPhysical.h" +#include "../../NucleusLib/pnMessage/plCorrectionMsg.h" +#include "plSwimRegion.h" +#include "plArmatureMod.h" // for LOS enum type +#include "plMatrixChannel.h" +#include "hsTimer.h" +#include "../plPhysx/plSimulationMgr.h" +#include "../plPhysx/plPXPhysical.h" +#include "../pnMessage/plSetNetGroupIDMsg.h" +#define kSWIMRADIUS 1.1f +#define kSWIMHEIGHT 2.8f +#define kGENERICCONTROLLERRADIUS 1.1f +#define kGENERICCONTROLLERHEIGHT 2.8f + +//#define kSWIMMINGCONTACTSLOPELIMIT (cosf(hsScalarDegToRad(80.f))) +const hsScalar plMovementStrategy::kAirTimeThreshold = .1f; // seconds +bool CompareMatrices(const hsMatrix44 &matA, const hsMatrix44 &matB, float tolerance); +bool operator<(const plControllerSweepRecord left, const plControllerSweepRecord right) +{ + if(left.TimeHit < right.TimeHit) return true; + else return false; +} +plMovementStrategy::plMovementStrategy(plPhysicalControllerCore* core) +{ + this->fTimeInAir=0.0f; + fCore=core; + fOwner=core->GetOwner(); + this->fPreferedControllerHeight=kGENERICCONTROLLERHEIGHT; + this->fPreferedControllerWidth=kGENERICCONTROLLERRADIUS; +} +void plMovementStrategy::IApplyKinematic() +{ + // first apply sceneobject update to the kinematic + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + // If we've been moved since the last physics update (somebody warped us), + // update the physics before we apply velocity. + const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); + if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) + { + fCore->SetKinematicLoc(l2w); + //fCore->SetGlobalLoc(l2w); + } + } +} +plPhysicalControllerCore::~plPhysicalControllerCore() +{ +} +void plPhysicalControllerCore::Apply(hsScalar delSecs) +{ + fSimLength=delSecs; + hsAssert(fMovementInterface, "plPhysicalControllerCore::Apply() missing a movement interface"); + if(fMovementInterface)fMovementInterface->Apply(delSecs); +} +void plPhysicalControllerCore::PostStep(hsScalar delSecs) +{ + hsAssert(fMovementInterface, "plPhysicalControllerCore::PostStep() missing a movement interface"); + if(fMovementInterface)fMovementInterface->PostStep(delSecs); +} +void plPhysicalControllerCore::Update(hsScalar delSecs) +{ + hsAssert(fMovementInterface, "plPhysicalControllerCore::Update() missing a movement interface"); + if(fMovementInterface)fMovementInterface->Update(delSecs); + +} +void plPhysicalControllerCore::SendCorrectionMessages() +{ + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + plCorrectionMsg* corrMsg = TRACKED_NEW plCorrectionMsg; + corrMsg->fLocalToWorld = fLastGlobalLoc; + corrMsg->fLocalToWorld.GetInverse(&corrMsg->fWorldToLocal); + corrMsg->fDirtySynch = true; + // Send the new position to the plArmatureMod and the scene object + const plArmatureMod* armMod = plArmatureMod::ConvertNoRef(so->GetModifierByType(plArmatureMod::Index())); + if (armMod) + corrMsg->AddReceiver(armMod->GetKey()); + corrMsg->AddReceiver(fOwner); + corrMsg->Send(); +} +plPhysicalControllerCore::plPhysicalControllerCore(plKey OwnerSceneObject, hsScalar height, hsScalar radius) +:fMovementInterface(nil) +,fOwner(OwnerSceneObject) +,fHeight(height) +,fRadius(radius) +,fWorldKey(nil) +,fLinearVelocity(0.f,0.f,0.f) +,fAngularVelocity(0.f) +,fAchievedLinearVelocity(0.0f,0.0f,0.0f) +,fLocalPosition(0.0f,0.0f,0.0f) +,fLocalRotation(0.0f,0.0f,0.0f,1.0f) +,fSeeking(false) +,fEnabled(true) +,fEnableChanged(false) +,fLOSDB(plSimDefs::kLOSDBNone) +,fDisplacementThisStep(0.f,0.f,0.f) +,fSimLength(0.f) +,fKinematic(false) +,fKinematicEnableNextUpdate(false) +,fNeedsResize(false) +,fPushingPhysical(nil) +{ +} + +void plPhysicalControllerCore::UpdateSubstepNonPhysical() +{ + // When we're in non-phys or a behavior we can't go through the rest of the function + // so we need to get out early, but we need to update the current position if we're + // in a subworld. + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + const plCoordinateInterface* ci = GetSubworldCI(); + if (ci && so) + { + const hsMatrix44& soL2W = so->GetCoordinateInterface()->GetLocalToWorld(); + const hsMatrix44& ciL2W = ci->GetLocalToWorld(); + hsMatrix44 l2w =GetPrevSubworldW2L()* soL2W; + l2w = ciL2W * l2w; + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + ((plCoordinateInterface*)so->GetCoordinateInterface())->SetTransform(l2w, w2l); + ((plCoordinateInterface*)so->GetCoordinateInterface())->FlushTransform(); + SetGlobalLoc(l2w); + } + + +} +void plPhysicalControllerCore::CheckAndHandleAnyStateChanges() +{ + if (IsEnabledChanged())HandleEnableChanged(); + if (IsKinematicChanged())HandleKinematicChanged(); + if (IsKinematicEnableNextUpdate())HandleKinematicEnableNextUpdate(); +} +void plPhysicalControllerCore::MoveActorToSim() +{ + // Get the current position of the physical + hsPoint3 curLocalPos; + hsPoint3 lastLocalPos; + GetPositionSim(curLocalPos); + MoveKinematicToController(curLocalPos); + lastLocalPos=GetLocalPosition(); + fDisplacementThisStep= hsVector3(curLocalPos - lastLocalPos); + fLocalPosition = curLocalPos; + if(fSimLength>0.0f) + fAchievedLinearVelocity=fDisplacementThisStep/fSimLength; + else fAchievedLinearVelocity.Set(0.0f,0.0f,0.0f); +} +void plPhysicalControllerCore::IncrementAngle(hsScalar deltaAngle) +{ + hsScalar angle; + hsVector3 axis; + fLocalRotation.NormalizeIfNeeded(); + fLocalRotation.GetAngleAxis(&angle, &axis); + // adjust it (quaternions are weird...) + if (axis.fZ < 0) + angle = (2*hsScalarPI) - angle; // axis is backwards, so reverse the angle too + angle += deltaAngle; + // make sure we wrap around + if (angle < 0) + angle = (2*hsScalarPI) + angle; // angle is -, so this works like a subtract + if (angle >= (2*hsScalarPI)) + angle = angle - (2*hsScalarPI); + // and set the new angle + fLocalRotation.SetAngleAxis(angle, hsVector3(0,0,1)); +} + +void plPhysicalControllerCore::UpdateWorldRelativePos() +{ + + // Apply rotation and translation + fLocalRotation.MakeMatrix(&fLastGlobalLoc); + fLastGlobalLoc.SetTranslate(&fLocalPosition); + // Localize to global coords if in a subworld + const plCoordinateInterface* ci = GetSubworldCI(); + if (ci) + { + const hsMatrix44& l2w = ci->GetLocalToWorld(); + fLastGlobalLoc = l2w * fLastGlobalLoc; + } +} +plPhysical* plPhysicalControllerCore::GetPushingPhysical() +{ + return fPushingPhysical; +} +const hsVector3& plPhysicalControllerCore::GetLinearVelocity() +{ + return fLinearVelocity; +} +bool plPhysicalControllerCore::GetFacingPushingPhysical() +{ + return fFacingPushingPhysical; +} +/////////////////////////// +//Walking Strategy +void plWalkingStrategy::Apply(hsScalar delSecs) +{ + //Apply Should Only be Called from a PhysicalControllerCore + hsAssert(fCore,"No Core shouldn't be Applying"); + UInt32 collideFlags = + 1<IsSeeking()) + { + collideFlags|=(1<GetLinearVelocity(); + hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); + hsPoint3 positionBegin; + fCore->GetPositionSim(positionBegin); + bool recovered=false; + if (fCore->IsKinematic()) + { + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + // If we've been moved since the last physics update (somebody warped us), + // update the physics before we apply velocity. + const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); + if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) + { + fCore->SetKinematicLoc(l2w); + fCore->SetGlobalLoc(l2w); + } + } + return; + } + + if (!fCore->IsEnabled()) + return; + + bool gotGroundHit = fGroundHit; + fGroundHit = false; + + fCore->SetPushingPhysical(nil); + fCore->SetFacingPushingPhysical( false); + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + static const float kGravity = -32.f; + // If we've been moved since the last physics update (somebody warped us), + // update the physics before we apply velocity. + const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); + if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) + fCore->SetGlobalLoc(l2w); + + // Convert our avatar relative velocity to subworld relative + if (!LinearVelocity.IsEmpty()) + { + LinearVelocity = l2w * LinearVelocity; + const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); + if (subworldCI) + LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; + } + + // Add in gravity if the avatar's z velocity isn't being set explicitly + // (Add in a little fudge factor, since the animations usually add a + // tiny bit of z.) + if (hsABS(LinearVelocity.fZ) < 0.001f) + { + // Get our previous z velocity. If we're on the ground, clamp it to zero at + // the largest, so we won't launch into the air if we're running uphill. + hsScalar prevZVel = AchievedLinearVelocity.fZ; + if (IsOnGround()) + prevZVel = hsMinimum(prevZVel, 0.f); + hsScalar grav = kGravity * delSecs; + // If our gravity contribution isn't high enough this frame, we won't + // report a collision even when standing on solid ground. + hsScalar maxGrav = -.001f / delSecs; + if (grav > maxGrav) + grav = maxGrav; + LinearVelocity.fZ = prevZVel + grav; + } + + // If we're airborne and the velocity isn't set, use the velocity from + // the last frame so we maintain momentum. + if (!IsOnGround() && LinearVelocity.fX == 0.f && LinearVelocity.fY == 0.f) + { + LinearVelocity.fX = AchievedLinearVelocity.fX; + LinearVelocity.fY = AchievedLinearVelocity.fY; + } + if (!IsOnGround() || IsOnFalseGround()) + { + // We're not on solid ground, so we should be sliding against whatever + // we're hitting (like a rock cliff). Each vector in fSlidingNormals is + // the surface normal of a collision that's too steep to be ground, so + // we project our current velocity onto that plane and slide along the + // wall. + // + // Also, sometimes PhysX reports a bunch of collisions from the wall, + // but nothing from underneath (when there should be). So if we're not + // touching ground, we offset the avatar in the direction of the + // surface normal(s). This doesn't fix the issue 100%, but it's a hell + // of a lot better than nothing, and suitable duct tape until a future + // PhysX revision fixes the issue. + // + // Yes, there's room for optimization here if we care. + hsVector3 offset(0.f, 0.f, 0.f); + for (int i = 0; i < fContactNormals.GetCount(); i++) + { + offset += fContactNormals[i]; + hsVector3 velNorm = LinearVelocity; + + if (velNorm.MagnitudeSquared() > 0) + velNorm.Normalize(); + + if (velNorm * fContactNormals[i] < 0) + { + hsVector3 proj = (velNorm % fContactNormals[i]) % fContactNormals[i]; + if (velNorm * proj < 0) + proj *= -1.f; + LinearVelocity = LinearVelocity.Magnitude() * proj; + } + } + if (offset.MagnitudeSquared() > 0) + { + // 5 ft/sec is roughly the speed we walk backwards. + // The higher the value, the less likely you'll trip + // the bug, and this seems reasonable. + offset.Normalize(); + LinearVelocity += offset * 5.0f; + } + } + //make terminal velocity equal to k. it is wrong but has been this way and + //don't want to break any puzzles. on top of that it will reduce tunneling behavior + if(LinearVelocity.fZSetLinearVelocity(LinearVelocity); + // Scale the velocity to our actual step size (by default it's feet/sec) + hsVector3 vel(LinearVelocity.fX * delSecs, LinearVelocity.fY * delSecs, LinearVelocity.fZ * delSecs); + unsigned int colFlags = 0; + fGroundHit = false; + fFalseGround = false; + fContactNormals.Swap(fPrevSlidingNormals); + fContactNormals.SetCount(0); + fCore->Move(vel, collideFlags, colFlags); + ICheckForFalseGround(); + //if(fReqMove2) fCore->Move2(vel); + /*If the Physx controller thinks we have a collision from below, need to make sure we + have at least have false ground, otherwise Autostepping can send us into the air, and we will some times + float/panic link. For some reason the NxControllerHitReport does not always send messages + regarding Controller contact with ground plane, but will (almost) always return NXCC_COLLISION_DOWN + with the move method. + */ + if((colFlags&kBottom ) &&(fGroundHit==false)) + { + fFalseGround=true; + } + + if(colFlags&kTop) + { + fHitHead=true; + //Did you hit your head on a dynamic? + //with Physx's wonderful controller hit report vs flags issues we need to actually sweep to see + std::multiset< plControllerSweepRecord > HitsDynamic; + UInt32 testFlag=1<GetPositionSim(startPos); + endPos= startPos + vel; + int NumObjsHit=fCore->SweepControllerPath(startPos, endPos, true, false, testFlag, HitsDynamic); + if(NumObjsHit>0) + { + for(std::multiset< plControllerSweepRecord >::iterator curObj= HitsDynamic.begin(); + curObj!=HitsDynamic.end(); curObj++) + { + + hsAssert(curObj->ObjHit,"We allegedly hit something, but there is no plasma physical associated with it"); + if(curObj->ObjHit) + {//really we shouldn't have to check hitObj should be nil only if we miss, or the physX object + //doesn't have a user data associated with this either way this just shouldn't happen + hsVector3 hitObjVel; + curObj->ObjHit->GetLinearVelocitySim(hitObjVel); + hsVector3 relativevel=LinearVelocity-hitObjVel; + curObj->ObjHit->SetHitForce(relativevel * 10.0f * (*curObj).ObjHit->GetMass(), (*curObj).locHit); + } + } + HitsDynamic.clear(); + } + } + } +} +void plWalkingStrategy::ICheckForFalseGround() +{ + if (fGroundHit) + return; // Already collided with "real" ground. + + // We need to check for the case where the avatar hasn't collided with "ground", but is colliding + // with a few other objects so that he's not actually falling (wedged in between some slopes). + // We do this by answering the following question (in 2d top-down space): "If you sort the contact + // normals by angle, is there a large enough gap between normals?" + // + // If you think in terms of geometry, this means a collection of surfaces are all pushing on you. + // If they're pushing from all sides, you have nowhere to go, and you won't fall. There needs to be + // a gap, so that you're pushed out and have somewhere to fall. This is the same as finding a gap + // larger than 180 degrees between sorted normals. + // + // The problem is that on top of that, the avatar needs enough force to shove him out that gap (he + // has to overcome friction). I deal with that by making the threshold (360 - (180 - 60) = 240). I've + // seen up to 220 reached in actual gameplay in a situation where we'd want this to take effect. + // This is the same running into 2 walls where the angle between them is 60. + int i, j; + const hsScalar threshold = hsScalarDegToRad(240.f); + int numContacts = fContactNormals.GetCount() + fPrevSlidingNormals.GetCount(); + if (numContacts >= 2) + { + // For extra fun... PhysX will actually report some collisions every other frame, as though + // we're bouncing back and forth between the two (or more) objects blocking us. So it's not + // enough to look at this frame's collisions, we have to check previous frames too. + hsTArray fCollisionAngles; + fCollisionAngles.SetCount(numContacts); + int angleIdx = 0; + for (i = 0; i < fContactNormals.GetCount(); i++, angleIdx++) + { + fCollisionAngles[angleIdx] = hsATan2(fContactNormals[i].fY, fContactNormals[i].fX); + } + for (i = 0; i < fPrevSlidingNormals.GetCount(); i++, angleIdx++) + { + fCollisionAngles[angleIdx] = hsATan2(fPrevSlidingNormals[i].fY, fPrevSlidingNormals[i].fX); + } + // numContacts is rarely larger than 6, so let's do a simple bubble sort. + for (i = 0; i < numContacts; i++) + { + for (j = i + 1; j < numContacts; j++) + { + if (fCollisionAngles[i] > fCollisionAngles[j]) + { + hsScalar tempAngle = fCollisionAngles[i]; + fCollisionAngles[i] = fCollisionAngles[j]; + fCollisionAngles[j] = tempAngle; + } + } + } + // sorted, now we check. + for (i = 1; i < numContacts; i++) + { + if (fCollisionAngles[i] - fCollisionAngles[i - 1] >= threshold) + break; + } + if (i == numContacts) + { + // We got to the end. Check the last with the first and make your decision. + if (!(fCollisionAngles[0] - fCollisionAngles[numContacts - 1] >= (threshold - 2 * hsScalarPI))) + fFalseGround = true; + } + } +} +void plWalkingStrategy::Update(hsScalar delSecs) +{ + //Update Should Only be Called from a PhysicalControllerCore + hsAssert(fCore,"Running Update: but have no Core"); + hsScalar AngularVelocity=fCore->GetAngularVelocity(); + hsVector3 LinearVelocity=fCore->GetLinearVelocity(); + + if (!fCore->IsEnabled() || fCore->IsKinematic()) + { + fCore->UpdateSubstepNonPhysical(); + return; + } + fCore->CheckAndHandleAnyStateChanges(); + if (!fGroundHit && !fFalseGround) + fTimeInAir += delSecs; + else + fTimeInAir = 0.f; + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + fCore->MoveActorToSim(); + if (AngularVelocity != 0.f) + { + hsScalar deltaAngle=AngularVelocity*delSecs; + fCore->IncrementAngle( deltaAngle); + } + // We can't only send updates when the physical position changes because the + // world relative position may be changing all the time if we're in a subworld. + fCore->UpdateWorldRelativePos(); + fCore->SendCorrectionMessages(); + bool headhit=fHitHead; + fHitHead=false; + hsVector3 AchievedLinearVelocity; + AchievedLinearVelocity = fCore->DisplacementLastStep(); + AchievedLinearVelocity=AchievedLinearVelocity/delSecs; + + /*if we hit our head the sweep api might try to + move us laterally to go as high as we requested kind of like autostep, to top it off the + way the NxCharacter and the sweep api work as a whole NxControllerHitReport::OnShapeHit + wont be called regarding the head blow. + if we are airborne: with out this we will gain large amounts of velocity in the x y plane + and on account of fAchievedLinearVelocity being used in the next step we will fly sideways + */ + if(headhit&&!(fGroundHit||fFalseGround)) + { + //we have hit our head and we don't have anything beneath our feet + //not really friction just a way to make it seem more realistic keep between 0 and 1 + hsScalar headFriction=0.0f; + AchievedLinearVelocity.fX=(1.0f-headFriction)*LinearVelocity.fX; + AchievedLinearVelocity.fY=(1.0f-headFriction)*LinearVelocity.fY; + //only clamping when hitting head and going upwards, if going down leave it be + // this should only occur when going down stairwells with low ceilings like in cleft + //kitchen area + if(AchievedLinearVelocity.fZ>0.0f) + { + AchievedLinearVelocity.fZ=0.0f; + } + fCore->OverrideAchievedVelocity(AchievedLinearVelocity); + } + fCore->OverrideAchievedVelocity(AchievedLinearVelocity); + // Apply angular velocity + } + + LinearVelocity.Set(0.f, 0.f, 0.f); + AngularVelocity = 0.f; + fCore->SetVelocities(LinearVelocity,AngularVelocity); + +} + + +void plWalkingStrategy::IAddContactNormals(hsVector3& vec) +{ + //TODO: ADD in functionality to Adjust walkable slope for controller, also apply that in here + hsScalar dot = vec * kAvatarUp; + if ( dot >= kSLOPELIMIT ) fGroundHit=true; + else plMovementStrategySimulationInterface::IAddContactNormals(vec); +} + +//swimming strategy +plSwimStrategy::plSwimStrategy(plPhysicalControllerCore* core) + :plMovementStrategy(core) + ,fOnGround(false) + ,fHadContacts(false) + ,fBuoyancy(0.f) + ,fSurfaceHeight(0.0f) + ,fCurrentRegion(nil) +{ + fPreferedControllerHeight=kSWIMHEIGHT; + fPreferedControllerWidth=kSWIMRADIUS; + fCore->SetMovementSimulationInterface(this); +} +void plSwimStrategy::IAdjustBuoyancy() +{ + // "surface depth" refers to the depth our handle object should be below + // the surface for the avatar to be "at the surface" + static const float surfaceDepth = 4.0f; + // 1.0 = neutral buoyancy + // 0 = no buoyancy (normal gravity) + // 2.0 = opposite of gravity, floating upwards + static const float buoyancyAtSurface = 1.0f; + + if (fCurrentRegion == nil) + { + fBuoyancy = 0.f; + return; + } + + hsMatrix44 l2w, w2l; + hsPoint3 posSim; + fCore->GetPositionSim(posSim); + float depth = fSurfaceHeight - posSim.fZ; + //this isn't a smooth transition but hopefully it won't be too obvious + if(depth<=0.0)//all the away above water + fBuoyancy = 0.f; // Same as being above ground. Plain old gravity. + else if(depth >= 5.0f) fBuoyancy=3.0f;//completely Submereged + else fBuoyancy =(depth/surfaceDepth ); + +} +void plSwimStrategy::Apply(hsScalar delSecs) +{ + hsAssert(fCore,"PlSwimStrategy::Apply No Core shouldn't be Applying"); + UInt32 collideFlags = + 1<IsSeeking()) + { + collideFlags|=(1<GetLinearVelocity(); + hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); + if (fCore->IsKinematic()) + { + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + // If we've been moved since the last physics update (somebody warped us), + // update the physics before we apply velocity. + const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); + if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) + { + fCore->SetKinematicLoc(l2w); + fCore->SetGlobalLoc(l2w); + } + } + return; + + } + if (!fCore->IsEnabled()) + return; + + fCore->SetPushingPhysical(nil); + fCore->SetFacingPushingPhysical( false); + fHadContacts=false; + fOnGround=false; + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + // If we've been moved since the last physics update (somebody warped us), + // update the physics before we apply velocity. + const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); + if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) + fCore->SetGlobalLoc(l2w); + + // Convert our avatar relative velocity to subworld relative + if (!LinearVelocity.IsEmpty()) + { + LinearVelocity = l2w * LinearVelocity; + const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); + if (subworldCI) + LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; + } + IAdjustBuoyancy(); + hsScalar zacc; + hsScalar retardent=0.0f; + static hsScalar FinalBobSpeed=0.5f; + //trying to dampen the oscillations + if((AchievedLinearVelocity.fZ>FinalBobSpeed)||(AchievedLinearVelocity.fZ<-FinalBobSpeed)) + retardent=AchievedLinearVelocity.fZ *-.90f; + zacc=(1-fBuoyancy)*-32.f + retardent; + + hsVector3 linCurrent(0.0f,0.0f,0.0f); + hsScalar angCurrent = 0.f; + if (fCurrentRegion != nil) + { + + fCurrentRegion->GetCurrent(fCore, linCurrent, angCurrent, delSecs); + //fAngularVelocity+= angCurrent; + } + hsVector3 vel(LinearVelocity.fX , LinearVelocity.fY , AchievedLinearVelocity.fZ+ LinearVelocity.fZ ); + vel.fZ= vel.fZ + zacc*delSecs; + if(fCurrentRegion!=nil){ + if (vel.fZ > fCurrentRegion->fMaxUpwardVel) + { + vel.fZ = fCurrentRegion->fMaxUpwardVel; + } + vel+= linCurrent; + } + static const float kGravity = -32.f; + if(vel.fZMove(displacement,collideFlags,colFlags); + if((colFlags&kBottom)||(colFlags&kSides))fHadContacts=true; + hsScalar angvel=fCore->GetAngularVelocity(); + fCore->SetAngularVelocity(angvel +angCurrent); + } +} +void plSwimStrategy::Update(hsScalar delSecs) +{ + hsAssert(fCore,"Running Update: but have no Core"); + hsScalar AngularVelocity=fCore->GetAngularVelocity(); + hsVector3 LinearVelocity=fCore->GetLinearVelocity(); + if (!fCore->IsEnabled() || fCore->IsKinematic()) + { + fCore->UpdateSubstepNonPhysical(); + return; + } + fCore->CheckAndHandleAnyStateChanges(); + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + fCore->MoveActorToSim(); + + if (AngularVelocity != 0.f) + { + hsScalar deltaAngle=AngularVelocity*delSecs; + fCore->IncrementAngle( deltaAngle); + } + fCore->UpdateWorldRelativePos(); + fCore->SendCorrectionMessages(); + } + LinearVelocity.Set(0.f, 0.f, 0.f); + AngularVelocity = 0.f; + fCore->SetVelocities(LinearVelocity,AngularVelocity); +} +void plSwimStrategy::IAddContactNormals(hsVector3& vec) +{ + //TODO: ADD in functionality to Adjust walkable slope for controller, also apply that in here + hsScalar dot = vec * kAvatarUp; + if ( dot >= kSLOPELIMIT ) + { + fOnGround=true; + // fHadContacts=true; + } + else plMovementStrategySimulationInterface::IAddContactNormals(vec); +} +void plSwimStrategy::SetSurface(plSwimRegionInterface *region, hsScalar surfaceHeight) +{ + fCurrentRegion=region; + fSurfaceHeight=surfaceHeight; +} +void plRidingAnimatedPhysicalStrategy::Apply(hsScalar delSecs) +{ + hsVector3 LinearVelocity=fCore->GetLinearVelocity(); + hsVector3 AchievedLinearVelocity=fCore->GetAchievedLinearVelocity(); + if (fCore->IsKinematic()) + { + //want to make sure nothing funky happens in the sim + IApplyKinematic(); + return; + } + if (!fCore->IsEnabled()) + return; + + //need to sweep ahead to see what we might hit. + // if we hit anything we should probably apply the force that would normally be applied in + + + fCore->SetPushingPhysical(nil); + fCore->SetFacingPushingPhysical( false); + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + hsPoint3 startPos, desiredDestination, endPos; + fCore->GetPositionSim(startPos); + UInt32 collideFlags = + 1< GroundHitRecords; + int possiblePlatformCount =fCore->SweepControllerPath(startPos, startPos + hsPoint3(0.f,0.f, -0.002f), true, true, collideFlags, GroundHitRecords); + float maxPlatformVel = - FLT_MAX; + int platformCount=0; + fGroundHit = false; + if(possiblePlatformCount) + { + + std::multiset::iterator curRecord; + + for(curRecord = GroundHitRecords.begin(); curRecord != GroundHitRecords.end(); curRecord++) + { + hsBool groundlike=false; + if((curRecord->locHit.fZ - startPos.fZ)<= .2) groundlike= true; + if(groundlike) + { + if(curRecord->ObjHit !=nil) + { + hsVector3 vel; + curRecord->ObjHit->GetLinearVelocitySim(vel); + if(vel.fZ > maxPlatformVel) + { + maxPlatformVel= vel.fZ; + } + } + platformCount ++; + fGroundHit = true; + } + } + } + + + + bool gotGroundHit = fGroundHit; + if (so) + { + + // If we've been moved since the last physics update (somebody warped us), + // update the physics before we apply velocity. + const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); + if (!CompareMatrices(l2w, fCore->GetLastGlobalLoc(), .0001f)) + fCore->SetGlobalLoc(l2w); + + // Convert our avatar relative velocity to subworld relative + if (!LinearVelocity.IsEmpty()) + { + LinearVelocity = l2w * LinearVelocity; + const plCoordinateInterface* subworldCI = fCore->GetSubworldCI(); + if (subworldCI) + LinearVelocity = subworldCI->GetWorldToLocal() * LinearVelocity; + } + + if(!IsOnGround()) + { + if(!fNeedVelocityOverride) + { + LinearVelocity.fZ= AchievedLinearVelocity.fZ; + } + else + { + LinearVelocity = fOverrideVelocity; + } + } + if(fStartJump) + { + LinearVelocity.fZ =12.0f; + } + if(platformCount) + { + LinearVelocity.fZ = LinearVelocity.fZ + maxPlatformVel; + } + + //probably neeed to do something with contact normals in here + //for false ground stuff + + fFalseGround = false; + hsVector3 testLength = LinearVelocity * delSecs + hsVector3(0.0, 0.0, -0.00f); + // + hsPoint3 desiredDestination= startPos + testLength; + if(!IsOnGround()) + { + if(ICheckMove(startPos, desiredDestination)) + {//we can get there soley by the LinearVelocity + + fNeedVelocityOverride =false; + } + else + { + + fNeedVelocityOverride =true; + fOverrideVelocity = LinearVelocity; + fOverrideVelocity.fZ -= delSecs * 32.f; + } + } + else + { + fNeedVelocityOverride =false; + } + + fCore->SetLinearVelocity(LinearVelocity); + + } +} +bool plRidingAnimatedPhysicalStrategy::ICheckMove(const hsPoint3& startPos, const hsPoint3& desiredPos) +{ + //returns false if it believes the end result can't be obtained by pure application of velocity (collides into somthing that it can't climb up) + //used as a way to check if it needs to hack getting there like in jumping + + UInt32 collideFlags = + 1<IsSeeking()) + { + collideFlags|=(1< DynamicHits; + int NumberOfHits=fCore->SweepControllerPath(startPos, desiredPos, true, true, collideFlags, DynamicHits); + + hsPoint3 stepFromPoint; + hsVector3 movementdir(&startPos, &desiredPos); + movementdir.Normalize(); + if(NumberOfHits) + { + hsPoint3 initBottomPos; + fCore->GetPositionSim(initBottomPos); + std::multiset< plControllerSweepRecord >::iterator cur; + hsVector3 testLength(desiredPos - startPos); + bool freeMove=true; + for(cur = DynamicHits.begin(); cur != DynamicHits.end(); cur++) + { + if(movementdir.InnerProduct(cur->Norm)>0.01f) + { + hsVector3 topOfBottomHemAtTimeT=hsVector3(initBottomPos + testLength * cur->TimeHit ); + topOfBottomHemAtTimeT.fZ = topOfBottomHemAtTimeT.fZ + fCore->GetControllerWidth(); + if(cur->locHit.fZ <= (topOfBottomHemAtTimeT.fZ -.5f)) + { + hitBottomOfCapsule=true; + hsVector3 norm= hsVector3(-1*(cur->locHit-topOfBottomHemAtTimeT)); + norm.Normalize(); + IAddContactNormals(norm); + } + else + { + return false; + } + } + + } + return true; + } + else + { + return true; + } + +} +void plRidingAnimatedPhysicalStrategy::Update(hsScalar delSecs) +{ + if (!fCore->IsEnabled() || fCore->IsKinematic()) + { + fCore->UpdateSubstepNonPhysical(); + return; + } + fCore->CheckAndHandleAnyStateChanges(); +} +void plRidingAnimatedPhysicalStrategy::PostStep(hsScalar delSecs) +{ + if(!(!fCore->IsEnabled() || fCore->IsKinematic())) + { + if (!fGroundHit && !fFalseGround) + fTimeInAir += delSecs; + else + fTimeInAir = 0.f; + hsVector3 AchievedLinearVelocity, LinearVelocity; + AchievedLinearVelocity = fCore->GetLinearVelocity(); + hsScalar AngularVelocity=fCore->GetAngularVelocity(); + fCore->OverrideAchievedVelocity(AchievedLinearVelocity); + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + fCore->UpdateControllerAndPhysicalRep(); + if (AngularVelocity != 0.f) + { + hsScalar deltaAngle=AngularVelocity*delSecs; + fCore->IncrementAngle( deltaAngle); + } + fCore->UpdateWorldRelativePos(); + fCore->SendCorrectionMessages(); + } + LinearVelocity.Set(0.f, 0.f, 0.f); + AngularVelocity = 0.f; + fCore->SetVelocities(LinearVelocity, AngularVelocity); + } + fStartJump = false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h new file mode 100644 index 00000000..5b6acb0f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPhysicalControllerCore.h @@ -0,0 +1,333 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLPHYSICALCONTROLLERCORE_H +#define PLPHYSICALCONTROLLERCORE_H +#include "hsGeometry3.h" +#include "hsMatrix44.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" +#include "../plPhysical/plSimDefs.h" +#include "../pnMessage/plMessage.h" + +#include "hsQuat.h" +#define PHYSX_ONLY_TRIGGER_FROM_KINEMATIC 1 +#define kSLOPELIMIT (cosf(hsScalarDegToRad(55.f))) + +class plCoordinateInterface; +class plPhysical; +class plPXPhysical; +class plSwimRegionInterface; +//Replacement for for plPhysicalController stripped out some walk specific code +//plPhysicalControllerCore needs to have movement strategies registered to it these will then +//be called by the controller during the simulation steps. The Strategies need to at least have an +// Apply and Update definition. Everything else should be movement specific. I hope to come back and +//and refactor when I have time this in the future. +enum plControllerCollisionFlags +{ + kSides=1, + kTop= (1<<1), + kBottom=(1<<2), +}; + +class plMovementStrategySimulationInterface +{ +public: + + virtual void Apply(hsScalar delSecs)=0; + virtual void Update(hsScalar delSecs)=0; + //most strategies don't require this. Only the ones that require behavior like a physical or need + //something after the sim step. this used to be taken care of by Update, but this was moved to take care of + //some of the frame lag + virtual void PostStep(hsScalar delSecs){}; + virtual void IAddContactNormals(hsVector3& vec){fContactNormals.Append(vec);} + virtual void AddOnTopOfObject(plPhysical* phys){ fOnTopOf.Append(phys);} + virtual void LeaveAge() + { + fContactNormals.SetCount(0); + fOnTopOf.SetCount(0); + } +protected: + hsTArray fContactNormals; + hsTArray fOnTopOf; +}; + +class plControllerSweepRecord +{ +public: + plPhysical *ObjHit; + hsPoint3 locHit;//World space + hsScalar TimeHit;//Normalized between 0 and 1 + hsVector3 Norm; +}; +bool operator<(const plControllerSweepRecord left, const plControllerSweepRecord right); +class plPhysicalControllerCore +{ +public: + virtual ~plPhysicalControllerCore(); + virtual void Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults)=0; + virtual void SetMovementSimulationInterface(plMovementStrategySimulationInterface* strat){fMovementInterface=strat;} + virtual void Apply(hsScalar delSecs); + virtual void Update(hsScalar delSecs); + virtual void PostStep(hsScalar delSecs); + // A disabled avatar doesn't move or accumulate air time if he's off the ground. + virtual void Enable(bool enable) = 0; + virtual bool IsEnabled() {return fEnabled;} + virtual plKey GetSubworld() {return fWorldKey;} + virtual void SetSubworld(plKey world) = 0; + virtual const plCoordinateInterface* GetSubworldCI() const = 0; + // For the avatar SDL only + virtual void GetState(hsPoint3& pos, float& zRot) = 0; + virtual void SetState(const hsPoint3& pos, float zRot) = 0; + // kinematic stuff .... should be just for when playing a behavior... + virtual void Kinematic(bool state) = 0; + virtual bool IsKinematic() = 0; + virtual void GetKinematicPosition(hsPoint3& pos) = 0; + virtual const hsMatrix44& GetPrevSubworldW2L() = 0; + //when seeking no longer want to interact with exclusion regions + virtual void SetSeek(bool seek){fSeeking=seek;} + virtual bool IsSeeking(){return fSeeking;} + static plPhysicalControllerCore* Create(plKey ownerSO, hsScalar height, hsScalar radius); + virtual plMovementStrategySimulationInterface* GetMovementInterface(){return fMovementInterface;} + plPhysicalControllerCore(plKey ownerSceneObject, hsScalar height, hsScalar radius); + virtual plKey GetOwner(){return fOwner;}; + // Set the LOS DB this avatar will be in (only one) + virtual void SetLOSDB(plSimDefs::plLOSDB losDB) { fLOSDB = losDB; } ; + virtual plSimDefs::plLOSDB GetLOSDB() {return fLOSDB ; } + virtual const hsMatrix44& GetLastGlobalLoc()=0; + virtual void SetKinematicLoc(const hsMatrix44& l2w)=0; + virtual void SetGlobalLoc(const hsMatrix44& l2w)=0; + virtual bool IsEnabledChanged(){return fEnableChanged;} + virtual void HandleEnableChanged()=0; + virtual bool IsKinematicChanged(){return fKinematicChanged;} + virtual void GetPositionSim(hsPoint3& pos)=0; + virtual void HandleKinematicChanged()=0; + virtual bool IsKinematicEnableNextUpdate(){return fKinematicEnableNextUpdate;} + virtual void HandleKinematicEnableNextUpdate()=0; + virtual void MoveKinematicToController(hsPoint3& pos)=0; + virtual void UpdateControllerAndPhysicalRep()=0; + virtual void CheckAndHandleAnyStateChanges(); + virtual void UpdateSubstepNonPhysical(); + virtual const hsPoint3& GetLocalPosition()=0; + virtual void MoveActorToSim(); + + virtual void OverrideAchievedVelocity(hsVector3 newAchievedVel) + {//because of things like superjumps this is needed I'd rather not, but can't help it + fAchievedLinearVelocity=newAchievedVel; + } + //any clean up for the controller should go here + virtual void LeaveAge()=0; + hsVector3 DisplacementLastStep(){return fDisplacementThisStep;} + hsVector3 MeanVelocityForLastStep() + { + hsVector3 vel=fDisplacementThisStep; + return vel/fSimLength; + } + void SendCorrectionMessages(); + void IncrementAngle(hsScalar deltaAngle); + void UpdateWorldRelativePos(); + virtual void SetLinearVelocity(const hsVector3& linearVel){fLinearVelocity=linearVel;} + //should actually be a 3 vector but everywhere else it is assumed to be just around Z + virtual void SetAngularVelocity(const hsScalar angvel){ fAngularVelocity=angvel;} + virtual void SetVelocities(const hsVector3& linearVel, hsScalar angVel) + { + fLinearVelocity=linearVel; + fAngularVelocity=angVel; + } + + virtual const hsVector3& GetLinearVelocity() ; + virtual hsScalar GetAngularVelocity(){return fAngularVelocity;} + virtual const hsVector3& GetAchievedLinearVelocity()const {return fAchievedLinearVelocity;} + plPhysical* GetPushingPhysical(); + bool GetFacingPushingPhysical(); + virtual void SetPushingPhysical(plPhysical* pl){fPushingPhysical=pl;} + virtual void SetFacingPushingPhysical(bool ans){fFacingPushingPhysical=ans;} + //To be Used during runtime conversions, say to switch a tall controller to a ball for swimming + virtual void SetControllerDimensions(hsScalar radius, hsScalar height)=0; + virtual hsScalar GetControllerWidth(){return fRadius;} + virtual hsScalar GetControllerHeight(){return fHeight;} + virtual void ResetAchievedLinearVelocity() + { + fAchievedLinearVelocity.Set(0.f,0.f,0.f); + } + virtual int SweepControllerPath(const hsPoint3& startPos,const hsPoint3& endPos, hsBool vsDynamics, hsBool vsStatics, UInt32& vsSimGroups, std::multiset< plControllerSweepRecord >& WhatWasHitOut)=0; + //this should only be used to force a move it could place your head into a wall and that would be good + virtual hsScalar GetHeight() {return fHeight;} + virtual hsScalar GetRadius() {return fRadius;} + //Wether the avatar thing has mass and forces things down or not, and changes the way things move + //This is an attempt fix things like riding on an animated physical + virtual void BehaveLikeAnimatedPhysical(hsBool actLikeAnAnimatedPhys)=0; + virtual hsBool BehavingLikeAnAnimatedPhysical()=0; +protected: + + plKey fOwner; + hsScalar fHeight; + hsScalar fRadius; + plKey fWorldKey; + plSimDefs::plLOSDB fLOSDB; + bool fSeeking; + bool fEnabled; + bool fEnableChanged; + bool fKinematic; + bool fKinematicEnableNextUpdate; + bool fKinematicChanged; + plMovementStrategySimulationInterface* fMovementInterface; + hsMatrix44 fLastGlobalLoc; + hsPoint3 fLocalPosition; + hsQuat fLocalRotation; + hsMatrix44 fPrevSubworldW2L; + hsVector3 fDisplacementThisStep; + hsScalar fSimLength; + + //physical properties + hsVector3 fLinearVelocity; + hsScalar fAngularVelocity; + hsVector3 fAchievedLinearVelocity; + plPhysical* fPushingPhysical; + bool fFacingPushingPhysical; + bool fNeedsResize; +}; + +class plMovementStrategy: public plMovementStrategySimulationInterface +{ +public: + virtual void SetControllerCore(plPhysicalControllerCore* core) + { + fCore=core; + fCore->SetMovementSimulationInterface(this); + } + virtual void RefreshConnectionToControllerCore() + { + fCore->SetMovementSimulationInterface(this); + //fCore->SetControllerDimensions(fPreferedControllerWidth,fPreferedControllerHeight); + fCore->BehaveLikeAnimatedPhysical(this->IRequireBehaviourLikeAnAnimatedPhysical()); + } + plMovementStrategy(plPhysicalControllerCore* core); + //should actually be a 3 vector but everywhere else it is assumed to be just around Z + virtual void SetLinearAcceleration(const hsVector3& accel){fLinearAcceleration=accel;} + virtual const hsVector3& GetLinearAcceleration()const{return fLinearAcceleration;} + //should actually be a 3 vector but everywhere else it is assumed to be just around Z + virtual void ResetAchievedLinearVelocity() + { + hsVector3 AchievedLinearVelocity(0.f,0.f,0.f); + if(fCore)fCore->OverrideAchievedVelocity(AchievedLinearVelocity); + } +//proxy functions for Controller Core + virtual hsScalar GetAirTime() const { return fTimeInAir; } + virtual void ResetAirTime() { fTimeInAir = 0.f; } + +protected: + virtual hsBool IRequireBehaviourLikeAnAnimatedPhysical()=0; + virtual void IApplyKinematic(); + plPhysicalControllerCore* fCore; + hsVector3 fLinearAcceleration; + hsScalar fAngularAcceleration; + plKey fOwner; + static const hsScalar kAirTimeThreshold; + hsScalar fTimeInAir; + hsScalar fPreferedControllerWidth; + hsScalar fPreferedControllerHeight; + + +}; + +class plWalkingStrategy: public plMovementStrategy +{ +public: + plWalkingStrategy(plPhysicalControllerCore* core):plMovementStrategy(core) + { + fGroundHit=false; + fFalseGround=false; + fHitHead=false; + fCore->SetMovementSimulationInterface(this); + fPreferedControllerWidth=core->GetControllerWidth(); + fPreferedControllerHeight=core->GetControllerHeight(); + fOnTopOfAnimatedPhysLastFrame=false; + } + virtual ~plWalkingStrategy(){}; + virtual void Apply(hsScalar delSecs); + virtual void Update(hsScalar delSecs); + + bool IsOnGround() const { return fTimeInAir < kAirTimeThreshold || fFalseGround; } + bool IsOnFalseGround() const { return fFalseGround && !fGroundHit; } + void GroundHit() { fGroundHit = true; } + virtual void IAddContactNormals(hsVector3& vec); + virtual void StartJump(){}; + + +protected: + + void ICheckForFalseGround(); + bool fGroundHit; + bool fFalseGround; + bool fHitHead; + bool fOnTopOfAnimatedPhysLastFrame; + hsTArray fPrevSlidingNormals; + virtual hsBool IRequireBehaviourLikeAnAnimatedPhysical(){return true;} + +}; +class plSwimStrategy: public plMovementStrategy +{ +public: + plSwimStrategy(plPhysicalControllerCore *core); + virtual ~plSwimStrategy(){}; + void SetSurface(plSwimRegionInterface *region, hsScalar surfaceHeight); + virtual void Apply(hsScalar delSecs); + virtual void Update(hsScalar delSecs); + hsScalar GetBuoyancy() { return fBuoyancy; } + hsBool IsOnGround() { return fOnGround; } + hsBool HadContacts() { return fHadContacts; } + virtual void IAddContactNormals(hsVector3& vec); +protected: + virtual hsBool IRequireBehaviourLikeAnAnimatedPhysical(){return true;} +private: + void IAdjustBuoyancy(); + hsScalar fBuoyancy; + hsBool fOnGround; + hsBool fHadContacts; + hsScalar fSurfaceHeight; + plSwimRegionInterface *fCurrentRegion; +}; +class plRidingAnimatedPhysicalStrategy : public plWalkingStrategy +{ +public: + plRidingAnimatedPhysicalStrategy(plPhysicalControllerCore *core ) : + fNeedVelocityOverride(false),fStartJump(false),plWalkingStrategy(core){}; + virtual ~plRidingAnimatedPhysicalStrategy(){}; + virtual void Apply(hsScalar delSecs); + virtual void Update(hsScalar delSecs); + virtual void PostStep(hsScalar delSecs); + bool IsOnGround() const { return fTimeInAir < kAirTimeThreshold || fFalseGround; } + bool IsOnFalseGround() const { return fFalseGround && !fGroundHit; } + void GroundHit() { fGroundHit = true; } + virtual void StartJump(){fStartJump = true;} +protected: + virtual hsBool IRequireBehaviourLikeAnAnimatedPhysical(){return false;} + bool ICheckMove(const hsPoint3& startPos, const hsPoint3& desiredPos); + hsBool fNeedVelocityOverride; + hsVector3 fOverrideVelocity; + bool fStartJump; +}; +#endif// PLPHYSICALCONTROLLERCORE_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPointChannel.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPointChannel.cpp new file mode 100644 index 00000000..58a20eb1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPointChannel.cpp @@ -0,0 +1,485 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPointChannel.h" +#include "plScalarChannel.h" +#include "hsResMgr.h" + +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../plInterp/plController.h" +#include "../plInterp/plAnimTimeConvert.h" +#include "../plGLight/plLightInfo.h" + +////////////// +// PLPOINTSRCE +////////////// + +// CTOR +plPointChannel::plPointChannel() +: plAGChannel(), fResult(0, 0, 0) +{ +} + +// DTOR +plPointChannel::~plPointChannel() +{ +} + +// VALUE +// default behaviour is just to return our result (constant value) +const hsPoint3 & plPointChannel::Value(double time) +{ + return fResult; +} + +// VALUE(point, time) +void plPointChannel::Value(hsPoint3 &point, double time) +{ + point = Value(time); +} + +// MAKECOMBINE +plAGChannel * plPointChannel::MakeCombine(plAGChannel *channelA) +{ + return nil; +} + +// MAKEBLEND +plAGChannel * plPointChannel::MakeBlend(plAGChannel * channelB, plScalarChannel * channelBias, int blendPriority) +{ + return TRACKED_NEW plPointBlend(this, (plPointChannel *)channelB, channelBias); +} + +// MAKEZEROSTATE +plAGChannel * plPointChannel::MakeZeroState() +{ + return TRACKED_NEW plPointConstant(Value(0)); +} + +// MAKETIMESCALE +plAGChannel * plPointChannel::MakeTimeScale(plScalarChannel *timeSource) +{ + return TRACKED_NEW plPointTimeScale(this, timeSource); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plPointConstant +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor -------------------------- +// ----- +plPointConstant::plPointConstant() +: plPointChannel() +{ +} + +// ctor ----------------------------------------------- +// ----- +plPointConstant::plPointConstant(const hsPoint3 &point) +{ + fResult = point; +} + +// dtor --------------------------- +// ----- +plPointConstant::~plPointConstant() +{ +} + +void plPointConstant::Read(hsStream *stream, hsResMgr *mgr) +{ + plPointChannel::Read(stream, mgr); + fResult.Read(stream); +} + +void plPointConstant::Write(hsStream *stream, hsResMgr *mgr) +{ + plPointChannel::Write(stream, mgr); + fResult.Write(stream); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// PLPOINTTIMESCALE +// +// Insert into the graph when you need to change the speed or direction of time +// Also serves as a handy instancing node, since it just passes its data through. +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor ---------------------------- +// ----- +plPointTimeScale::plPointTimeScale() +: fTimeSource(nil), fChannelIn(nil) +{ +} + +// ctor -------------------------------------------------------------------------------- +// ----- +plPointTimeScale::plPointTimeScale(plPointChannel *channel, plScalarChannel *timeSource) +: fChannelIn(channel), + fTimeSource(timeSource) +{ +} + +// dtor ----------------------------- +// ----- +plPointTimeScale::~plPointTimeScale() +{ +} + +// IsStoppedAt --------------------------- +// ------------ +plPointTimeScale::IsStoppedAt(double time) +{ + return fTimeSource->IsStoppedAt(time); +} + +// Value -------------------------------------------- +// ------ +const hsPoint3 & plPointTimeScale::Value(double time) +{ + fResult = fChannelIn->Value(fTimeSource->Value(time)); + + return fResult; +} + +// Detach --------------------------------------------------- +// ------- +plAGChannel * plPointTimeScale::Detach(plAGChannel * channel) +{ + plAGChannel *result = this; + + fChannelIn = plPointChannel::ConvertNoRef(fChannelIn->Detach(channel)); + + if(!fChannelIn || channel == this) + result = nil; + + if(result != this) + delete this; + + return result; + +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plPointBlend +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor -------------------- +// ----- +plPointBlend::plPointBlend() +: fPointA(nil), + fPointB(nil) +{ +} + +// ctor --------------------------------------------------------------------------------- +// ----- +plPointBlend::plPointBlend(plPointChannel *channelA, plPointChannel *channelB, + plScalarChannel *channelBias) +: fPointA(channelA), + fPointB(channelB), + fChannelBias(channelBias) +{ +} + +// dtor --------------------- +// ----- +plPointBlend::~plPointBlend() +{ + fPointA = nil; + fPointB = nil; + fChannelBias = nil; +} + +// IsStoppedAt ------------------------------ +// ------------ +hsBool plPointBlend::IsStoppedAt(double time) +{ + hsScalar blend = fChannelBias->Value(time); + if (blend == 0) + return fPointA->IsStoppedAt(time); + if (blend == 1) + return fPointB->IsStoppedAt(time); + + return (fPointA->IsStoppedAt(time) && fPointB->IsStoppedAt(time)); +} + +// Value --------------------------------------- +// ------ +const hsPoint3 &plPointBlend::Value(double time) +{ + if (fPointA && fPointB) + { + hsScalar curBlend = fChannelBias->Value(time); + if(curBlend == 0) { + fPointA->Value(fResult, time); + } else { + if(curBlend == 1) { + fPointB->Value(fResult, time); + } else { + const hsPoint3 &pointA = fPointA->Value(time); + const hsPoint3 &pointB = fPointB->Value(time); + hsPoint3 difference = pointB - pointA; + + difference *= curBlend; + + fResult = pointA + difference; + } + } + } else { + if (fPointA) + { + fResult = fPointA->Value(time); + } else { + if (fPointB) + { + fResult = fPointA->Value(time); + } + } + } + return fResult; +} + +// Detach ------------------------------------------------------ +// ------- +plAGChannel * plPointBlend::Detach(plAGChannel *remove) +{ + plAGChannel *result = this; + + if (remove == this) + { + result = nil; + } else { + // it's possible that the incoming channel could reside down *all* of our + // branches (it's a graph, not a tree,) so we always pass down all limbs + fChannelBias = plScalarChannel::ConvertNoRef(fChannelBias->Detach(remove)); + fPointA = plPointChannel::ConvertNoRef(fPointA->Detach(remove)); + fPointB = plPointChannel::ConvertNoRef(fPointB->Detach(remove)); + if (!fChannelBias) + { + // No more bias channel, assume it's zero from now on, (a.k.a. We just want channelA) + result = fPointA; + } + else + { + if(!fChannelBias) + result = fPointA; + else if(fPointA && !fPointB) + result = fPointA; + else if(fPointB && !fPointA) + result = fPointB; + else if(!fPointA && !fPointB) + result = nil; + if(result != this) + { + delete this; + } + } + } + return result; +} + +/////////////////////////// +// PLPOINTCONTROLLERCHANNEL +/////////////////////////// + +// CTOR +plPointControllerChannel::plPointControllerChannel() +: fController(nil) +{ +} + +// CTOR(name, controller) +plPointControllerChannel::plPointControllerChannel(plController *controller) +: fController(controller) +{ +} + +// ~DTOR() +plPointControllerChannel::~plPointControllerChannel() +{ + if(fController) { + delete fController; + fController = nil; + } +} + +// VALUE(time) +const hsPoint3 & plPointControllerChannel::Value(double time) +{ + return Value(time, nil); +} + +// VALUE(time) +const hsPoint3 & plPointControllerChannel::Value(double time, plControllerCacheInfo *cache) +{ + fController->Interp((hsScalar)time, &fResult, cache); + return fResult; +} + +plAGChannel *plPointControllerChannel::MakeCacheChannel(plAnimTimeConvert *atc) +{ + plControllerCacheInfo *cache = fController->CreateCache(); + cache->SetATC(atc); + return TRACKED_NEW plPointControllerCacheChannel(this, cache); +} + +// WRITE(stream, mgr) +void plPointControllerChannel::Write(hsStream *stream, hsResMgr *mgr) +{ + plPointChannel::Write(stream, mgr); + + hsAssert(fController, "Trying to write plPointControllerChannel with nil controller. File will not be importable."); + mgr->WriteCreatable(stream, fController); +} + +// READ(stream, mgr) +void plPointControllerChannel::Read(hsStream *stream, hsResMgr *mgr) +{ + plPointChannel::Read(stream, mgr); + + fController = plController::ConvertNoRef(mgr->ReadCreatable(stream)); +} + +///////////////////////////////// +// PLPOINTCONTROLLERCACHECHANNEL +///////////////////////////////// + +// CTOR +plPointControllerCacheChannel::plPointControllerCacheChannel() +: fControllerChannel(nil), + fCache(nil) +{ +} + +// CTOR(name, controller) +plPointControllerCacheChannel::plPointControllerCacheChannel(plPointControllerChannel *controller, plControllerCacheInfo *cache) +: fControllerChannel(controller), + fCache(cache) +{ +} + +// ~DTOR() +plPointControllerCacheChannel::~plPointControllerCacheChannel() +{ + delete fCache; + fControllerChannel = nil; +} + +// VALUE(time) +const hsPoint3 & plPointControllerCacheChannel::Value(double time, bool peek) +{ + return fControllerChannel->Value(time, fCache); +} + +// DETACH +plAGChannel * plPointControllerCacheChannel::Detach(plAGChannel * channel) +{ + if(channel == this) + { + return nil; + } else { + plAGChannel *result = fControllerChannel->Detach(channel); + + if(result == fControllerChannel) + { + return this; + } else { + // if our controller channel has been detached, then detach ourselves as well. + return result; + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Channel applicators + +void plPointChannelApplicator::IApply(const plAGModifier *modifier, double time) +{ + plPointChannel *pointChan = plPointChannel::ConvertNoRef(fChannel); + hsAssert(pointChan, "Invalid channel given to plPointChannelApplicator"); + + plCoordinateInterface *CI = IGetCI(modifier); + + hsMatrix44 l2p = CI->GetLocalToParent(); + const hsPoint3 &point = pointChan->Value(time); + + l2p.SetTranslate(&point); + + hsMatrix44 p2l; + l2p.GetInverse(&p2l); + CI->SetLocalToParent(l2p, p2l); +} + +void plLightDiffuseApplicator::IApply(const plAGModifier *modifier, double time) +{ + plPointChannel *pointChan = plPointChannel::ConvertNoRef(fChannel); + hsAssert(pointChan, "Invalid channel given to plLightDiffuseApplicator"); + + plLightInfo *li = plLightInfo::ConvertNoRef(IGetGI(modifier, plLightInfo::Index())); + + const hsPoint3 &point = pointChan->Value(time); + hsColorRGBA color; + color.Set(point.fX, point.fY, point.fZ, 1.0f); + li->SetDiffuse(color); +} + +void plLightAmbientApplicator::IApply(const plAGModifier *modifier, double time) +{ + plPointChannel *pointChan = plPointChannel::ConvertNoRef(fChannel); + hsAssert(pointChan, "Invalid channel given to plLightAmbientApplicator"); + + plLightInfo *li = plLightInfo::ConvertNoRef(IGetGI(modifier, plLightInfo::Index())); + + const hsPoint3 &point = pointChan->Value(time); + hsColorRGBA color; + color.Set(point.fX, point.fY, point.fZ, 1.0f); + li->SetAmbient(color); +} + +void plLightSpecularApplicator::IApply(const plAGModifier *modifier, double time) +{ + plPointChannel *pointChan = plPointChannel::ConvertNoRef(fChannel); + hsAssert(pointChan, "Invalid channel given to plLightSpecularApplicator"); + + plLightInfo *li = plLightInfo::ConvertNoRef(IGetGI(modifier, plLightInfo::Index())); + + const hsPoint3 &point = pointChan->Value(time); + hsColorRGBA color; + color.Set(point.fX, point.fY, point.fZ, 1.0f); + li->SetSpecular(color); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPointChannel.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPointChannel.h new file mode 100644 index 00000000..a44f741f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPointChannel.h @@ -0,0 +1,286 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLPOINTCHANNEL_H +#define PLPOINTCHANNEL_H + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// + +#include "plAGApplicator.h" +#include "plAGChannel.h" + +#include "hsGeometry3.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// FORWARDS +// +///////////////////////////////////////////////////////////////////////////////////////// + +class plController; +class plControllerCacheInfo; + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEFINITIONS +// +///////////////////////////////////////////////////////////////////////////////////////// + +// PLPOINTCHANNEL +///////////////// +// A source of animated hsPoint3 data +class plPointChannel : public plAGChannel +{ +protected: + hsPoint3 fResult; + +public: + plPointChannel(); + virtual ~plPointChannel(); + + // AG PROTOCOL + virtual const hsPoint3 & Value(double time); + virtual void Value(hsPoint3 &point, double time); + + // combine it (allocates combine object) + virtual plAGChannel * MakeCombine(plAGChannel * channelB); + + // blend it (allocates blend object) + virtual plAGChannel * MakeBlend(plAGChannel * channelB, plScalarChannel * channelBias, int blendPriority); + + // const eval at time zero + virtual plAGChannel * MakeZeroState(); + // make a timeScale instance + virtual plAGChannel * MakeTimeScale(plScalarChannel *timeSource); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plPointChannel ); + GETINTERFACE_ANY( plPointChannel, plAGChannel ); +}; + +////////////////// +// PLPOINTCONSTANT +////////////////// +// A point source that just keeps handing out the same point +class plPointConstant : public plPointChannel +{ +public: + plPointConstant(); + plPointConstant(const hsPoint3 &point); + virtual ~plPointConstant(); + + void Set(const hsPoint3 &the_Point) { fResult = the_Point; } + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plPointConstant ); + GETINTERFACE_ANY( plPointConstant, plPointChannel ); + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); +}; + +//////////////////// +// PLPOINTTIMESCALE +//////////////////// +// Adapts the time scale before passing it to the next channel in line. +// Use to instance animations while allowing each instance to run at different speeds. +class plPointTimeScale : public plPointChannel +{ +protected: + plScalarChannel *fTimeSource; + plPointChannel *fChannelIn; + +public: + plPointTimeScale(); + plPointTimeScale(plPointChannel *channel, plScalarChannel *timeSource); + virtual ~plPointTimeScale(); + + virtual hsBool IsStoppedAt(double time); + virtual const hsPoint3 & Value(double time); + + virtual plAGChannel * Detach(plAGChannel * channel); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plPointTimeScale ); + GETINTERFACE_ANY( plPointTimeScale, plPointChannel ); +}; + +// PLPOINTBLEND +/////////////// +// Combines two point sources into one +class plPointBlend : public plPointChannel +{ +public: + plPointBlend(); + plPointBlend(plPointChannel *channelA, plPointChannel *channelB, plScalarChannel *channelBias); + virtual ~plPointBlend(); + + plAGChannel * plPointBlend::Remove(plAGChannel *srceToRemove); + + const plPointChannel * GetPointChannelA() const { return fPointA; } + void SetPointChannelA(plPointChannel *the_PointA) { fPointA = the_PointA; } + + const plPointChannel * GetPointChannelB() const { return fPointB; } + void SetPointChannelB(plPointChannel *the_PointB) { fPointB = the_PointB; } + + const plScalarChannel * GetChannelBias() const { return fChannelBias; } + void SetChannelBias(plScalarChannel * channel) { fChannelBias = channel; } + + //float GetBlend() const { return fBlend; } + //void SetBlend(float the_blend) { fBlend = the_blend; } + virtual hsBool IsStoppedAt(double time); + + // AG PROTOCOL + virtual const hsPoint3 &Value(double time); + + // remove the specified channel from our graph + virtual plAGChannel * Detach(plAGChannel * channel); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plPointBlend ); + GETINTERFACE_ANY( plPointBlend, plPointChannel ); + +protected: + plPointChannel *fPointA; + plPointChannel *fPointB; + plScalarChannel *fChannelBias; +}; + +// BLENDPOINTS +// Handy little function to share with others. +hsPoint3 BlendPoints(hsPoint3 &pointA, hsPoint3 &pointB, float blend); + + +/////////////////////////// +// PLPOINTCONTROLLERCHANNEL +/////////////////////////// + +// converts a plController-style animation into a plPointChannel +class plPointControllerChannel : public plPointChannel +{ +protected: + plController *fController; + +public: + // xTORs + plPointControllerChannel(); + plPointControllerChannel(plController *controller); + virtual ~plPointControllerChannel(); + + // AG PROTOCOL + virtual const hsPoint3 & Value(double time); + virtual const hsPoint3 & Value(double time, plControllerCacheInfo *cache); + + virtual plAGChannel * MakeCacheChannel(plAnimTimeConvert *atc); + + // PLASMA PROTOCOL + // rtti + CLASSNAME_REGISTER( plPointControllerChannel ); + GETINTERFACE_ANY( plPointControllerChannel, plPointChannel ); + + // persistence + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *s, hsResMgr *mgr); +}; + +//////////////////////////////// +// PLPOINTCONTROLLERCACHECHANNEL +//////////////////////////////// +// Same as plPointController, but with caching info +class plPointControllerCacheChannel : public plPointChannel +{ +protected: + plControllerCacheInfo *fCache; + plPointControllerChannel *fControllerChannel; + +public: + plPointControllerCacheChannel(); + plPointControllerCacheChannel(plPointControllerChannel *channel, plControllerCacheInfo *cache); + virtual ~plPointControllerCacheChannel(); + + virtual const hsPoint3 & Value(double time, bool peek = false); + + virtual plAGChannel * Detach(plAGChannel * channel); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plPointControllerCacheChannel ); + GETINTERFACE_ANY( plPointControllerCacheChannel, plPointChannel ); + + // Created at runtime only, so no Read/Write +}; + +//////////////////////////// +// +// Channel Applicator classes + +class plPointChannelApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plPointChannelApplicator ); + GETINTERFACE_ANY( plPointChannelApplicator, plAGApplicator ); +}; + +class plLightDiffuseApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plLightDiffuseApplicator ); + GETINTERFACE_ANY( plLightDiffuseApplicator, plAGApplicator ); +}; + +class plLightAmbientApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plLightAmbientApplicator ); + GETINTERFACE_ANY( plLightAmbientApplicator, plAGApplicator ); +}; + +class plLightSpecularApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plLightSpecularApplicator ); + GETINTERFACE_ANY( plLightSpecularApplicator, plAGApplicator ); +}; + + + + +#endif // PLPOINTCHANNEL_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPuppetBrainMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPuppetBrainMsg.h new file mode 100644 index 00000000..6b7307e1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPuppetBrainMsg.h @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// removed \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPuppetCommands.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPuppetCommands.h new file mode 100644 index 00000000..36684654 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plPuppetCommands.h @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Removed \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plQuatChannel.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plQuatChannel.cpp new file mode 100644 index 00000000..a15990a4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plQuatChannel.cpp @@ -0,0 +1,314 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plQuatChannel.h" +#include "plPointChannel.h" +#include "plMatrixChannel.h" + +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../plInterp/plAnimTimeConvert.h" + +#include "hsMatrix44.h" + +//////////////// +// PLQUATCHANNEL +//////////////// + +// CTOR +plQuatChannel::plQuatChannel() +: plAGChannel() +{ + fResult.Identity(); +} + +// DTOR +plQuatChannel::~plQuatChannel() +{ +} + +// VALUE (time) +const hsQuat &plQuatChannel::Value(double time) +{ + return fResult; +} + +// VALUE (quaternion, time) +void plQuatChannel::Value(hsQuat &quat, double time) +{ + quat = Value(time); +} + +// CANCOMBINE +hsBool plQuatChannel::CanCombine(plAGChannel *channelA) +{ + return false; + if(plPointChannel::ConvertNoRef(channelA)) + { + return true; + } else { + return false; + } +} + +// MAKECOMBINE +plAGChannel * plQuatChannel::MakeCombine(plAGChannel *channelA) +{ + if(plPointChannel::ConvertNoRef(channelA)) + { + return TRACKED_NEW plQuatPointCombine(this, (plPointChannel *)channelA); + } else { + return nil; + } +} + +// MAKEBLEND +plAGChannel *plQuatChannel::MakeBlend(plAGChannel *channelB, plScalarChannel *channelBias, int blendPriority) +{ + plQuatChannel *chanB = plQuatChannel::ConvertNoRef(channelB); + plScalarChannel *chanBias = plScalarChannel::ConvertNoRef(channelBias); + if(chanB && chanBias) + { + return TRACKED_NEW plQuatBlend(this, chanB, chanBias); + } else { + hsStatusMessageF("Blend operation failed."); + return this; + } +} + +// MAKEZEROSTATE +plAGChannel * plQuatChannel::MakeZeroState() +{ + return TRACKED_NEW plQuatConstant(Value(0)); +} + +// MAKETIMESCALE +plAGChannel * plQuatChannel::MakeTimeScale(plScalarChannel *timeSource) +{ + return TRACKED_NEW plQuatTimeScale(this, timeSource); +} + +///////////////// +// PLQUATCONSTANT +///////////////// + +// CTOR +plQuatConstant::plQuatConstant() +: plQuatChannel() +{ +} + +// CTOR(name, quaternion) +plQuatConstant::plQuatConstant(const hsQuat &quaternion) +{ + fResult = quaternion; +} + +// DTOR +plQuatConstant::~plQuatConstant() +{ +} + +void plQuatConstant::Read(hsStream *stream, hsResMgr *mgr) +{ + plQuatChannel::Read(stream, mgr); + fResult.Read(stream); +} + +void plQuatConstant::Write(hsStream *stream, hsResMgr *mgr) +{ + plQuatChannel::Write(stream, mgr); + fResult.Write(stream); +} + +//////////////////// +// PLQUATTIMESCALE +//////////////////// +// Insert into the graph when you need to change the speed or direction of time +// Also serves as a handy instancing node, since it just passes its data through. + +// CTOR +plQuatTimeScale::plQuatTimeScale() +: fTimeSource(nil), + fChannelIn(nil) +{ +} + +// CTOR (channel, converter) +plQuatTimeScale::plQuatTimeScale(plQuatChannel *channel, plScalarChannel *timeSource) +: fChannelIn(channel), + fTimeSource(timeSource) +{ +} + +// DTOR +plQuatTimeScale::~plQuatTimeScale() +{ +} + +plQuatTimeScale::IsStoppedAt(double time) +{ + return fTimeSource->IsStoppedAt(time); +} + +// VALUE +const hsQuat & plQuatTimeScale::Value(double time) +{ + fResult = fChannelIn->Value(fTimeSource->Value(time)); + + return fResult; +} + +// DETACH +plAGChannel * plQuatTimeScale::Detach(plAGChannel * channel) +{ + plAGChannel *result = this; + + fChannelIn = plQuatChannel::ConvertNoRef(fChannelIn->Detach(channel)); + + if(!fChannelIn || channel == this) + result = nil; + + if (result != this) + delete this; + + return result; +} + +////////////// +// PLQUATBLEND +////////////// + +// CTOR +plQuatBlend::plQuatBlend() +: fQuatA(nil), + fQuatB(nil), + fChannelBias(nil) +{ +} + +// CTOR(channelA, channelB, blend) +plQuatBlend::plQuatBlend(plQuatChannel *channelA, plQuatChannel *channelB, plScalarChannel *channelBias) +: fQuatA(channelA), + fQuatB(channelB), + fChannelBias(channelBias) +{ +} + +// DTOR +plQuatBlend::~plQuatBlend() +{ + //if (fQuatA) delete fQuatA; + //if (fQuatB) delete fQuatB; + fQuatA = fQuatB = nil; + fChannelBias = nil; +} + +hsBool plQuatBlend::IsStoppedAt(double time) +{ + hsScalar blend = fChannelBias->Value(time); + if (blend == 0) + return fQuatA->IsStoppedAt(time); + if (blend == 1) + return fQuatB->IsStoppedAt(time); + + return (fQuatA->IsStoppedAt(time) && fQuatB->IsStoppedAt(time)); +} + +// VALUE(time) +const hsQuat &plQuatBlend::Value(double time) +{ + hsQuat quatA = fQuatA->Value(time); + hsQuat quatB = fQuatB->Value(time); + + fResult.SetFromSlerp(quatA, quatB, fChannelBias->Value(time)); + + return fResult; +} + + +// REMOVE +// Remove the given channel wherever it may be in the graph (including this node) +plAGChannel * plQuatBlend::Detach(plAGChannel *remove) +{ + plAGChannel *result = this; + + hsAssert(remove != this, "Cannot remove blenders explicitly. Remove blended source instead."); + + if (remove != this) + { + fChannelBias = plScalarChannel::ConvertNoRef(fChannelBias->Detach(remove)); + if (!fChannelBias) + { + // No more bias channel, assume it's zero from now on, (a.k.a. We just want channelA) + result = fQuatA; + } + else + { + fQuatA = (plQuatChannel *)fQuatA->Detach(remove); + if(fQuatA) + { + // channel a still here(although children may be gone); try channel b + fQuatB = (plQuatChannel *)fQuatB->Detach(remove); + + if(!fQuatB) + { + result = fQuatA; // channel b is gone: return channel a as blender's replacement + } + } else { + result = fQuatB; // channel a is gone: return channel b + } + + if (result != this) + { + delete this; // lost one of our channels: kill the blender. + } + } + } + return result; +} + +////////////////////////////////////////////////////////////////////////////////////// +// Applicators + +void plQuatChannelApplicator::IApply(const plAGModifier *mod, double time) +{ + plQuatChannel *quatChan = plQuatChannel::ConvertNoRef(fChannel); + hsAssert(quatChan, "Invalid channel in plQuatChannelApplicator"); + + const hsQuat &rotate = quatChan->Value(time); + + plCoordinateInterface *CI = IGetCI(mod); + + hsMatrix44 l2w; + hsMatrix44 w2l; + + rotate.MakeMatrix(&l2w); + l2w.GetInverse(&w2l); + + CI->SetLocalToParent(l2w, w2l); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plQuatChannel.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plQuatChannel.h new file mode 100644 index 00000000..d11b5824 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plQuatChannel.h @@ -0,0 +1,171 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLQUATCHANNEL_INC +#define PLQUATCHANNEL_INC + +#include "hsQuat.h" + +#include "plAGChannel.h" +#include "plAGApplicator.h" + +// PLQUATCHANNEL +///////////// +// A source of animated hsQuat data +class plQuatChannel : public plAGChannel +{ +protected: + hsQuat fResult; + +public: + plQuatChannel(); + virtual ~plQuatChannel(); + + // AG PROTOCOL + virtual const hsQuat & Value(double time); + virtual void Value(hsQuat &quaternion, double time); + + // can this channel combine with the given channel? + virtual hsBool CanCombine(plAGChannel * channelB); + // combine it (allocates combine object) + virtual plAGChannel * MakeCombine(plAGChannel * channelB); + + // const eval at time zero + virtual plAGChannel * MakeZeroState(); + // make a timeScale instance + virtual plAGChannel * MakeTimeScale(plScalarChannel *timeSource); + + // blend it (allocates blend object) + virtual plAGChannel * MakeBlend(plAGChannel * channelB, plScalarChannel * channelBias, int blendPriority); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plQuatChannel ); + GETINTERFACE_ANY( plQuatChannel, plAGChannel ); +}; + +// PLQUATCONSTANT +//////////// +// A quat channel that just keeps handing out the same quaternion +class plQuatConstant : public plQuatChannel +{ +public: + plQuatConstant(); + plQuatConstant(const hsQuat &quaternion); + virtual ~plQuatConstant(); + + void Set(const hsQuat &the_Quat) { fResult = the_Quat; } + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plQuatConstant ); + GETINTERFACE_ANY( plQuatConstant, plQuatChannel ); + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); +}; + +//////////////////// +// PLQUATTIMESCALE +//////////////////// +// Adapts the time scale before passing it to the next channel in line. +// Use to instance animations while allowing each instance to run at different speeds. +class plQuatTimeScale : public plQuatChannel +{ +protected: + plScalarChannel *fTimeSource; + plQuatChannel *fChannelIn; + +public: + plQuatTimeScale(); + plQuatTimeScale(plQuatChannel *channel, plScalarChannel *timeSource); + virtual ~plQuatTimeScale(); + + virtual hsBool IsStoppedAt(double time); + virtual const hsQuat & Value(double time); + + virtual plAGChannel * Detach(plAGChannel * channel); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plQuatTimeScale ); + GETINTERFACE_ANY( plQuatTimeScale, plQuatChannel ); +}; + +// PLQUATBLEND +////////////// +// Combines two quaternion sources into one +class plQuatBlend : public plQuatChannel +{ +public: + plQuatBlend(); + plQuatBlend(plQuatChannel *channelA, plQuatChannel *channelB, plScalarChannel *channelBias); + virtual ~plQuatBlend(); + + // GETTERS AND SETTERS FOR CHANNELS + const plQuatChannel * GetQuatA() const { return fQuatA; } + void SetQuatA(plQuatChannel *the_QuatA) { fQuatA = the_QuatA; } + + const plQuatChannel * GetQuatB() const { return fQuatB; } + void SetQuatB(plQuatChannel *the_QuatB) { fQuatB = the_QuatB; } + + const plScalarChannel * GetChannelBias() const { return fChannelBias; } + void SetChannelBias(plScalarChannel *channel) { fChannelBias = channel; } + + //float GetBlend() const { return fBlend; } + //void SetBlend(float the_blend) { fBlend = the_blend; } + virtual hsBool IsStoppedAt(double time); + + // AG PROTOCOL + virtual const hsQuat &Value(double time); + + // remove the specified channel from our graph + virtual plAGChannel * Detach(plAGChannel * channel); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plQuatBlend ); + GETINTERFACE_ANY( plQuatBlend, plQuatChannel ); + +protected: + plQuatChannel *fQuatA; + plQuatChannel *fQuatB; + plScalarChannel *fChannelBias; + +}; + + +//////////////////////////// +// +// Channel Applicator classes + +class plQuatChannelApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plQuatChannelApplicator ); + GETINTERFACE_ANY( plQuatChannelApplicator, plAGApplicator ); +}; + + +#endif // PLQUATCHANNEL_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plScalarChannel.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plScalarChannel.cpp new file mode 100644 index 00000000..63433b2d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plScalarChannel.cpp @@ -0,0 +1,590 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// + +// singular +#include "plScalarChannel.h" + +// global +#include "hsResMgr.h" + +// other +#include "../plGLight/plLightInfo.h" +#include "../plInterp/plController.h" +#include "../plInterp/plAnimTimeConvert.h" +#include "../plSDL/plSDL.h" + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plScalarChannel +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor -------------------------- +// ----- +plScalarChannel::plScalarChannel() +: plAGChannel() +{ +} + +// dtor --------------------------- +// ----- +plScalarChannel::~plScalarChannel() +{ +} + +// value -------------------------------------------------------- +// ------ +const hsScalar & plScalarChannel::Value(double time, hsBool peek) +{ + return fResult; +} + +// value -------------------------------------------------------------- +// ------ +void plScalarChannel::Value(hsScalar &scalar, double time, hsBool peek) +{ + scalar = Value(time, peek); +} + +// MakeCombine ----------------------------------------------- +// ------------ +plAGChannel * plScalarChannel::MakeCombine(plAGChannel *other) +{ + return nil; +} + +// MakeBlend --------------------------------------------------- +// ---------- +plAGChannel * plScalarChannel::MakeBlend(plAGChannel * channelB, + plScalarChannel * channelBias, + int blendPriority) +{ + plScalarChannel * chanB = plScalarChannel::ConvertNoRef(channelB); + plScalarChannel * chanBias = plScalarChannel::ConvertNoRef(channelBias); + plAGChannel * result = this; + + if (chanB) + { + result = TRACKED_NEW plScalarBlend(this, chanB, chanBias); + } else { + hsStatusMessage("Blend operation failed."); + } + return result; +} + +// MakeZeroState ----------------------------- +// -------------- +plAGChannel * plScalarChannel::MakeZeroState() +{ + return TRACKED_NEW plScalarConstant(Value(0)); +} + +// MakeTimeScale -------------------------------------------------------- +// -------------- +plAGChannel * plScalarChannel::MakeTimeScale(plScalarChannel *timeSource) +{ + return TRACKED_NEW plScalarTimeScale(this, timeSource); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plScalarConstant +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor ---------------------------- +// ----- +plScalarConstant::plScalarConstant() +{ +} + +// ctor ------------------------------------------ +// ----- +plScalarConstant::plScalarConstant(hsScalar value) +{ + fResult = value; +} + +// dtor ----------------------------- +// ----- +plScalarConstant::~plScalarConstant() +{ +} + +void plScalarConstant::Read(hsStream *stream, hsResMgr *mgr) +{ + plScalarChannel::Read(stream, mgr); + fResult = stream->ReadSwapScalar(); +} + +void plScalarConstant::Write(hsStream *stream, hsResMgr *mgr) +{ + plScalarChannel::Write(stream, mgr); + stream->WriteSwapScalar(fResult); +} + + + +//////////////////// +// PLSCALARTIMESCALE +//////////////////// +// Insert into the graph when you need to change the speed or direction of time +// Also serves as a handy instancing node, since it just passes its data through. + +// CTOR +plScalarTimeScale::plScalarTimeScale() +: fTimeSource(nil), + fChannelIn(nil) +{ +} + +// CTOR (channel, converter) +plScalarTimeScale::plScalarTimeScale(plScalarChannel *channel, plScalarChannel *timeSource) +: fChannelIn(channel), + fTimeSource(timeSource) +{ +} + +// DTOR +plScalarTimeScale::~plScalarTimeScale() +{ +} + +plScalarTimeScale::IsStoppedAt(double time) +{ + return fTimeSource->IsStoppedAt(time); +} + +// VALUE +const hsScalar & plScalarTimeScale::Value(double time, hsBool peek) +{ + fResult = fChannelIn->Value(fTimeSource->Value(time, peek)); + + return fResult; +} + +// DETACH +plAGChannel * plScalarTimeScale::Detach(plAGChannel * detach) +{ + plAGChannel *result = this; + + fChannelIn = plScalarChannel::ConvertNoRef(fChannelIn->Detach(detach)); + + if(!fChannelIn || detach == this) + result = nil; + + if(result != this) + delete this; + + return result; + +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plScalarBlend +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor ---------------------- +// ----- +plScalarBlend::plScalarBlend() +: fChannelA(nil), + fChannelB(nil), + fChannelBias(nil) +{ +} + +// ctor ---------------------------------------------------------------------------- +// ----- +plScalarBlend::plScalarBlend(plScalarChannel * channelA, plScalarChannel * channelB, + plScalarChannel * channelBias) +: fChannelA(channelA), + fChannelB(channelB), + fChannelBias(channelBias) +{ +} + +// dtor ----------------------- +// ----- +plScalarBlend::~plScalarBlend() +{ + fChannelA = nil; + fChannelB = nil; + fChannelBias = nil; +} + +// IsStoppedAt ------------------------------- +// ------------ +hsBool plScalarBlend::IsStoppedAt(double time) +{ + hsScalar blend = fChannelBias->Value(time); + if (blend == 0) + return fChannelA->IsStoppedAt(time); + if (blend == 1) + return fChannelB->IsStoppedAt(time); + + return (fChannelA->IsStoppedAt(time) && fChannelB->IsStoppedAt(time)); +} + +// Value ------------------------------------------------------ +// ------ +const hsScalar & plScalarBlend::Value(double time, hsBool peek) +{ + hsScalar curBlend = fChannelBias->Value(time, peek); + if(curBlend == 0) { + fChannelA->Value(fResult, time, peek); + } else { + if(curBlend == 1) { + fChannelB->Value(fResult, time, peek); + } else { + const hsScalar &scalarA = fChannelA->Value(time, peek); + const hsScalar &scalarB = fChannelB->Value(time, peek); + fResult = scalarA + curBlend * (scalarB - scalarA); + } + } + return fResult; +} + + +// Detach ---------------------------------------------- +// ------- +plAGChannel * plScalarBlend::Detach(plAGChannel *remove) +{ + plAGChannel *result = this; + + // it's possible that the incoming channel could reside down *all* of our + // branches (it's a graph, not a tree,) so we always pass down all limbs + fChannelBias = plScalarChannel::ConvertNoRef(fChannelBias->Detach(remove)); + fChannelA = plScalarChannel::ConvertNoRef(fChannelA->Detach(remove)); + fChannelB = plScalarChannel::ConvertNoRef(fChannelB->Detach(remove)); + + if(!fChannelBias) + result = fChannelA; + else if(fChannelA && !fChannelB) + result = fChannelA; + else if(fChannelB && !fChannelA) + result = fChannelB; + else if(!fChannelA && !fChannelB) + result = nil; + + if(result != this) + delete this; + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// PLSCALARCONTROLLERCHANNEL +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor ---------------------------------------------- +// ----- +plScalarControllerChannel::plScalarControllerChannel() +: fController(nil) +{ +} + +// ctor ---------------------------------------------------------------------------- +// ----- +plScalarControllerChannel::plScalarControllerChannel(plController *controller) +: fController(controller) +{ +} + +// dtor ----------------------------------------------- +// ----- +plScalarControllerChannel::~plScalarControllerChannel() +{ + if(fController) { + delete fController; + fController = nil; + } +} + +// Value ------------------------------------------------------------------ +// ------ +const hsScalar & plScalarControllerChannel::Value(double time, hsBool peek) +{ + return Value(time, peek, nil); +} + +// Value ------------------------------------------------------------------ +// ------ +const hsScalar & plScalarControllerChannel::Value(double time, hsBool peek, + plControllerCacheInfo *cache) +{ + fController->Interp((hsScalar)time, &fResult, cache); + return fResult; +} + +// MakeCacheChannel ------------------------------------------------------------ +// ----------------- +plAGChannel *plScalarControllerChannel::MakeCacheChannel(plAnimTimeConvert *atc) +{ + plControllerCacheInfo *cache = fController->CreateCache(); + cache->SetATC(atc); + return TRACKED_NEW plScalarControllerCacheChannel(this, cache); +} + +// Write ------------------------------------------------------------- +// ------ +void plScalarControllerChannel::Write(hsStream *stream, hsResMgr *mgr) +{ + plScalarChannel::Write(stream, mgr); + + hsAssert(fController, "Trying to write plScalarControllerChannel with nil controller. File will not be importable."); + mgr->WriteCreatable(stream, fController); +} + +// Read ------------------------------------------------------------- +// ----- +void plScalarControllerChannel::Read(hsStream *stream, hsResMgr *mgr) +{ + plScalarChannel::Read(stream, mgr); + + fController = plController::ConvertNoRef(mgr->ReadCreatable(stream)); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// PLSCALARCONTROLLERCACHECHANNEL +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor -------------------------------------------------------- +// ----- +plScalarControllerCacheChannel::plScalarControllerCacheChannel() +: fControllerChannel(nil), + fCache(nil) +{ +} + +// ctor --------------------------------------------------------------------------------------------- +// ----- +plScalarControllerCacheChannel::plScalarControllerCacheChannel(plScalarControllerChannel *controller, + plControllerCacheInfo *cache) +: fControllerChannel(controller), + fCache(cache) +{ +} + +// dtor --------------------------------------------------------- +// ----- +plScalarControllerCacheChannel::~plScalarControllerCacheChannel() +{ + delete fCache; + fControllerChannel = nil; +} + +// Value --------------------------------------------------------------------- +// ------ +const hsScalar & plScalarControllerCacheChannel::Value(double time, bool peek) +{ + return fControllerChannel->Value(time, peek, fCache); +} + +// Detach ----------------------------------------------------------------- +// ------- +plAGChannel * plScalarControllerCacheChannel::Detach(plAGChannel * channel) +{ + plAGChannel *result = this; + + if(channel == this) + { + result = nil; + } else { + fControllerChannel = plScalarControllerChannel::ConvertNoRef(fControllerChannel->Detach(channel)); + + if(!fControllerChannel) + result = nil; + } + if(result != this) + delete this; + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// PLATCCHANNEL +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor -------------------- +plATCChannel::plATCChannel() +: fConvert(nil) +{ +} + +// ctor ---------------------------------------------- +plATCChannel::plATCChannel(plAnimTimeConvert *convert) +: fConvert(convert) +{ +} + +// dtor --------------------- +plATCChannel::~plATCChannel() +{ +} + +// IsStoppedAt ------------------------------ +// ------------ +hsBool plATCChannel::IsStoppedAt(double time) +{ + return fConvert->IsStoppedAt(time); +} + +// Value ----------------------------------------------------- +// ------ +const hsScalar & plATCChannel::Value(double time, hsBool peek) +{ + fResult = (peek ? fConvert->WorldToAnimTimeNoUpdate(time) : fConvert->WorldToAnimTime(time)); + return fResult; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// PLSCALARSDLCHANNEL +// +///////////////////////////////////////////////////////////////////////////////////////// + +// ctor -------------------------------- +// ----- +plScalarSDLChannel::plScalarSDLChannel() +: fLength(1), fVar(nil) +{ + fResult = 0; +} + +plScalarSDLChannel::plScalarSDLChannel(hsScalar length) +: fLength(length), fVar(nil) +{ + fResult = 0; +} + +// dtor --------------------------------- +plScalarSDLChannel::~plScalarSDLChannel() +{ +} + +// IsStoppedAt ------------------------------------ +// ------------ +hsBool plScalarSDLChannel::IsStoppedAt(double time) +{ + return false; +} + +// Value ----------------------------------------------------------- +// ------ +const hsScalar & plScalarSDLChannel::Value(double time, hsBool peek) +{ + if (fVar) + fVar->Get(&fResult); + + // the var will return a 0-1 value, scale to match our anim length. + fResult *= fLength; + return fResult; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// APPLICATORS +// +///////////////////////////////////////////////////////////////////////////////////////// + +// IApply ------------------------------------------------------------------ +// ------- +void plScalarChannelApplicator::IApply(const plAGModifier *mod, double time) +{ +} + +// IApply -------------------------------------------------------------- +// ------- +void plSpotInnerApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *scalarChan = plScalarChannel::ConvertNoRef(fChannel); + hsAssert(scalarChan, "Invalid channel given to plSpotInnerApplicator"); + + plSpotLightInfo *sli = plSpotLightInfo::ConvertNoRef(IGetGI(mod, plSpotLightInfo::Index())); + + const hsScalar &scalar = scalarChan->Value(time); + sli->SetSpotInner(hsScalarDegToRad(scalar)*0.5f); +} + +// IApply -------------------------------------------------------------- +// ------- +void plSpotOuterApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *scalarChan = plScalarChannel::ConvertNoRef(fChannel); + hsAssert(scalarChan, "Invalid channel given to plSpotInnerApplicator"); + + plSpotLightInfo *sli = plSpotLightInfo::ConvertNoRef(IGetGI(mod, plSpotLightInfo::Index())); + + const hsScalar &scalar = scalarChan->Value(time); + sli->SetSpotOuter(hsScalarDegToRad(scalar)*0.5f); +} + + +void plOmniApplicator::IApply(const plAGModifier *modifier, double time) +{ + plScalarChannel *scalarChan = plScalarChannel::ConvertNoRef(fChannel); + hsAssert(scalarChan, "Invalid channel given to plLightOmniApplicator"); + + plOmniLightInfo *oli = plOmniLightInfo::ConvertNoRef(IGetGI(modifier, plOmniLightInfo::Index())); + + oli->SetLinearAttenuation(scalarChan->Value(time)); +} + +void plOmniSqApplicator::IApply(const plAGModifier *modifier, double time) +{ + plScalarChannel *scalarChan = plScalarChannel::ConvertNoRef(fChannel); + hsAssert(scalarChan, "Invalid channel given to plLightOmniApplicator"); + + plOmniLightInfo *oli = plOmniLightInfo::ConvertNoRef(IGetGI(modifier, plOmniLightInfo::Index())); + + oli->SetQuadraticAttenuation(scalarChan->Value(time)); +} + +void plOmniCutoffApplicator::IApply(const plAGModifier *modifier, double time) +{ + plScalarChannel *scalarChan = plScalarChannel::ConvertNoRef(fChannel); + hsAssert(scalarChan, "Invalid channel given to plOmniCutoffApplicator"); + + plOmniLightInfo *oli = plOmniLightInfo::ConvertNoRef(IGetGI(modifier, plOmniLightInfo::Index())); + + oli->SetCutoffAttenuation( scalarChan->Value( time ) ); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plScalarChannel.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plScalarChannel.h new file mode 100644 index 00000000..915267c6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plScalarChannel.h @@ -0,0 +1,346 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLSCALARCHANNEL_INC +#define PLSCALARCHANNEL_INC + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// +// base +#include "plAGChannel.h" +#include "plAGApplicator.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// FORWARDS +// +///////////////////////////////////////////////////////////////////////////////////////// +class plController; +class plAnimTimeConvert; +class plSimpleStateVariable; +class plControllerCacheInfo; + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEFINITIONS +// +///////////////////////////////////////////////////////////////////////////////////////// + +////////////////// +// PLSCALARCHANNEL +////////////////// +// an animation channel that outputs a scalar value +class plScalarChannel : public plAGChannel +{ +protected: + hsScalar fResult; + +public: + plScalarChannel(); + virtual ~plScalarChannel(); + + // AG PROTOCOL + virtual const hsScalar & Value(double time, hsBool peek = false); + virtual void Value(hsScalar &result, double time, hsBool peek = false); + + // combine it (allocates combine object) + virtual plAGChannel * MakeCombine(plAGChannel * channelB); + + // blend it (allocates blend object) + virtual plAGChannel * MakeBlend(plAGChannel * channelB, plScalarChannel * channelBias, int blendPriority); + + // const eval at time zero + virtual plAGChannel * MakeZeroState(); + + // make a timeScale instance + virtual plAGChannel * MakeTimeScale(plScalarChannel *timeSource); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plScalarChannel ); + GETINTERFACE_ANY( plScalarChannel, plAGChannel ); +}; + +/////////////////// +// PLSCALARCONSTANT +/////////////////// +// A scalar source that just keeps handing out the same value +class plScalarConstant : public plScalarChannel +{ +public: + plScalarConstant(); + plScalarConstant(hsScalar value); + virtual ~plScalarConstant(); + + void Set(hsScalar value) { fResult = value; } + hsScalar Get() { return fResult; } + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plScalarConstant ); + GETINTERFACE_ANY( plScalarConstant, plScalarChannel ); + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); +}; + + +//////////////////// +// PLSCALARTIMESCALE +//////////////////// +// Adapts the time scale before passing it to the next channel in line. +// Use to instance animations while allowing each instance to run at different speeds. +class plScalarTimeScale : public plScalarChannel +{ +protected: + plScalarChannel *fTimeSource; + plScalarChannel *fChannelIn; + +public: + plScalarTimeScale(); + plScalarTimeScale(plScalarChannel *channel, plScalarChannel *timeSource); + virtual ~plScalarTimeScale(); + + virtual hsBool IsStoppedAt(double time); + virtual const hsScalar & Value(double time, hsBool peek = false); + virtual plAGChannel * Detach(plAGChannel * channel); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plScalarTimeScale ); + GETINTERFACE_ANY( plScalarTimeScale, plScalarChannel ); +}; + +//////////////// +// PLSCALARBLEND +//////////////// +// blends two scalars into one with weighting +class plScalarBlend : public plScalarChannel +{ +protected: + plScalarChannel * fChannelA; + plScalarChannel * fChannelB; + plScalarChannel * fChannelBias; + +public: + // xTORs + plScalarBlend(); + plScalarBlend(plScalarChannel * channelA, plScalarChannel * channelB, plScalarChannel * channelBias); + virtual ~plScalarBlend(); + + // SPECIFICS + const plScalarChannel * GetChannelA() const { return fChannelA; } + void SetChannelA(plScalarChannel * channel) { fChannelA = channel; } + + const plScalarChannel * GetChannelB() const { return fChannelB; } + void SetChannelB(plScalarChannel * channel) { fChannelB = channel; } + + const plScalarChannel * GetChannelBias() const { return fChannelBias; } + void SetChannelBias(plScalarChannel * channel) { fChannelBias = channel; } + + virtual hsBool IsStoppedAt(double time); + + // AG PROTOCOL + virtual const hsScalar & Value(double time, hsBool peek = false); + + // remove the specified channel from our graph + virtual plAGChannel * Detach(plAGChannel * channel); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plScalarBlend ); + GETINTERFACE_ANY( plScalarBlend, plScalarChannel ); +}; + +//////////////////////////// +// PLSCALARCONTROLLERCHANNEL +//////////////////////////// +// converts a plController-style animation into a plScalarChannel +class plScalarControllerChannel : public plScalarChannel +{ +protected: + plController *fController; + +public: + // xTORs + plScalarControllerChannel(); + plScalarControllerChannel(plController *controller); + virtual ~plScalarControllerChannel(); + + // AG PROTOCOL + virtual const hsScalar & Value(double time, hsBool peek = false); + virtual const hsScalar & Value(double time, hsBool peek, plControllerCacheInfo *cache); + + virtual plAGChannel *plScalarControllerChannel::MakeCacheChannel(plAnimTimeConvert *atc); + + // PLASMA PROTOCOL + // rtti + CLASSNAME_REGISTER( plScalarControllerChannel ); + GETINTERFACE_ANY( plScalarControllerChannel, plScalarChannel ); + + // persistence + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *s, hsResMgr *mgr); +}; + +///////////////////////////////// +// PLSCALARCONTROLLERCACHECHANNEL +///////////////////////////////// +// Same as plScalarController, but with caching info +class plScalarControllerCacheChannel : public plScalarChannel +{ +protected: + plControllerCacheInfo *fCache; + plScalarControllerChannel *fControllerChannel; + +public: + plScalarControllerCacheChannel(); + plScalarControllerCacheChannel(plScalarControllerChannel *channel, plControllerCacheInfo *cache); + virtual ~plScalarControllerCacheChannel(); + + virtual const hsScalar & Value(double time, bool peek = false); + + virtual plAGChannel * Detach(plAGChannel * channel); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plScalarControllerCacheChannel ); + GETINTERFACE_ANY( plScalarControllerCacheChannel, plScalarChannel ); + + // Created at runtime only, so no Read/Write +}; + +//////////////////// +// PLATCChannel +//////////////////// +// Channel interface for a plAnimTimeConvert object +class plATCChannel : public plScalarChannel +{ +protected: + plAnimTimeConvert *fConvert; + +public: + plATCChannel(); + plATCChannel(plAnimTimeConvert *convert); + virtual ~plATCChannel(); + + virtual hsBool IsStoppedAt(double time); + virtual const hsScalar & Value(double time, hsBool peek = false); + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plATCChannel ); + GETINTERFACE_ANY( plATCChannel, plScalarChannel ); +}; + +//////////////////// +// PLSCALARSDLCHANNEL +//////////////////// +// Returns the value of an SDL scalar variable +class plScalarSDLChannel : public plScalarChannel +{ +protected: + plSimpleStateVariable *fVar; + hsScalar fLength; + +public: + plScalarSDLChannel(); + plScalarSDLChannel(hsScalar length); + virtual ~plScalarSDLChannel(); + + virtual hsBool IsStoppedAt(double time); + virtual const hsScalar & Value(double time, hsBool peek = false); + + void SetVar(plSimpleStateVariable *var) { fVar = var; } + + // PLASMA PROTOCOL + CLASSNAME_REGISTER( plScalarSDLChannel ); + GETINTERFACE_ANY( plScalarSDLChannel, plScalarChannel ); +}; + + +//////////////////////////// +// +// Channel Applicator classes + +class plScalarChannelApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plScalarChannelApplicator ); + GETINTERFACE_ANY( plScalarChannelApplicator, plAGApplicator ); +}; + +class plSpotInnerApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plSpotInnerApplicator ); + GETINTERFACE_ANY( plSpotInnerApplicator, plAGApplicator ); +}; + +class plSpotOuterApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plSpotOuterApplicator ); + GETINTERFACE_ANY( plSpotOuterApplicator, plAGApplicator ); +}; + +class plOmniApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plOmniApplicator ); + GETINTERFACE_ANY( plOmniApplicator, plAGApplicator ); +}; + +class plOmniSqApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plOmniSqApplicator ); + GETINTERFACE_ANY( plOmniSqApplicator, plAGApplicator ); +}; + +class plOmniCutoffApplicator : public plAGApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plOmniCutoffApplicator ); + GETINTERFACE_ANY( plOmniCutoffApplicator, plAGApplicator ); +}; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSeekPointMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSeekPointMod.cpp new file mode 100644 index 00000000..763f516b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSeekPointMod.cpp @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSeekPointMod.h" + +// local +#include "plAvatarMgr.h" + +// CTOR() +plSeekPointMod::plSeekPointMod() +: fName(nil), plMultiModifier() +{ + // this constructor is called from the loader. +} + +// CTOR(char *) +plSeekPointMod::plSeekPointMod(char * name) +: fName(name), plMultiModifier() +{ + // this constructor is called from the converter. it adds the seek point to the + // registry immediately because it has the name already +} + +// DTOR() +plSeekPointMod::~plSeekPointMod() +{ + if(fName) { + delete[] fName; + fName = nil; + } +} + +// MSGRECEIVE +hsBool plSeekPointMod::MsgReceive(plMessage* msg) +{ + return plMultiModifier::MsgReceive(msg); +} + +// ADDTARGET +// Here I am. Announce presence to the avatar registry. +void plSeekPointMod::AddTarget(plSceneObject* so) +{ + plMultiModifier::AddTarget(so); +// plAvatarMgr::GetInstance()->AddSeekPoint(this); +} + +void plSeekPointMod::Read(hsStream *stream, hsResMgr *mgr) +{ + plMultiModifier::Read(stream, mgr); + + // read in the name of the animation itself + int length = stream->ReadSwap32(); + if(length > 0) + { + fName = TRACKED_NEW char[length + 1]; + stream->Read(length, fName); + fName[length] = 0; + } + +} + +void plSeekPointMod::Write(hsStream *stream, hsResMgr *mgr) +{ + plMultiModifier::Write(stream, mgr); + + int length = strlen(fName); + stream->WriteSwap32(length); + if (length > 0) + { + stream->Write(length, fName); + } + +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSeekPointMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSeekPointMod.h new file mode 100644 index 00000000..cd5db0ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSeekPointMod.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLSEEKPOINTMOD_INC +#define PLSEEKPOINTMOD_INC + +#include "../pnModifier/plMultiModifier.h" +#include "../pnMessage/plMessage.h" + +// PLSEEKPOINTMOD +// This modifier is something the avatar knows how to go to. (you know, seek) +// It's kind of like a magnet that, when activated, draws the avatar... +// Seen another way, it's a point with a name and a type.... +class plSeekPointMod : public plMultiModifier +{ +protected: + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) {return true;} + char * fName; // public because you can't change it + +public: + + plSeekPointMod(); + plSeekPointMod(char *name); + virtual ~plSeekPointMod(); + + const char * GetName() { return fName; }; + void SetName(char * name) { fName = name; }; + + CLASSNAME_REGISTER( plSeekPointMod ); + GETINTERFACE_ANY( plSeekPointMod, plMultiModifier ); + + virtual void AddTarget(plSceneObject* so); + + hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSittingModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSittingModifier.cpp new file mode 100644 index 00000000..e48b8721 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSittingModifier.cpp @@ -0,0 +1,374 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// singular +#include "plSittingModifier.h" + +//other +#include "../plMessage/plAvatarMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAnimStage.h" +#include "../plAvatar/plAvTaskBrain.h" +#include "../plAvatar/plAvBrainGeneric.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plInputCore/plAvatarInputInterface.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +///////////////////////////////////////////////////////////////////////////////////////// + +// CTOR ------------------------------ +// ----- +plSittingModifier::plSittingModifier() +: fTriggeredAvatarKey(nil), + fMiscFlags(0) +{ +} + +// CTOR ------------------------------------------------------------------------ +// ----- +plSittingModifier::plSittingModifier(bool hasFront, bool hasLeft, bool hasRight) +: fTriggeredAvatarKey(nil), + fMiscFlags(0) +{ + if (hasFront) + fMiscFlags |= kApproachFront; + if (hasLeft) + fMiscFlags |= kApproachLeft; + if (hasRight) + fMiscFlags |= kApproachRight; +} + +// DTOR ------------------------------- +// ----- +plSittingModifier::~plSittingModifier() +{ +} + +// Read ----------------------------------------------------- +// ----- +void plSittingModifier::Read(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Read(stream, mgr); + + fMiscFlags = stream->ReadByte(); + + int keyCount = stream->ReadSwap32(); + for (int i = 0; i < keyCount; i++ ) + fNotifyKeys.Append(mgr->ReadKey(stream)); +} + +// Write ----------------------------------------------------- +// ------ +void plSittingModifier::Write(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Write(stream, mgr); + + stream->WriteByte(fMiscFlags); + + stream->WriteSwap32(fNotifyKeys.GetCount()); + for (int i = 0; i < fNotifyKeys.GetCount(); i++) + mgr->WriteKey(stream, fNotifyKeys[i]); +} + +// ISetupNotify ------------------------------------------------------------------------- +// ------------- +void plSittingModifier::ISetupNotify(plNotifyMsg *notifyMsg, plNotifyMsg *originalNotify) +{ + // Copy the original events to the new notify (some notify receivers need to have events) + int i; + for (i = 0; i < originalNotify->GetEventCount(); i++) + notifyMsg->AddEvent(originalNotify->GetEventRecord(i)); + for (i = 0; i < fNotifyKeys.Count(); i++) + { + plKey receiver = fNotifyKeys[i]; + notifyMsg->AddReceiver(receiver); + } + notifyMsg->SetSender(GetKey()); + plNetClientApp::InheritNetMsgFlags(originalNotify, notifyMsg, true); +} + +// MsgReceive -------------------------------------- +// ----------- +hsBool plSittingModifier::MsgReceive(plMessage *msg) +{ + hsBool result = false; + + plNotifyMsg* notifyMsg = plNotifyMsg::ConvertNoRef(msg); + if(notifyMsg) + { + if (notifyMsg->fState == 1.0) + { + // In case the previous occupant quit, lost-connection, etc. + if (fTriggeredAvatarKey && !fTriggeredAvatarKey->ObjectIsLoaded()) + UnTrigger(); + + // only fire if we're not already triggered + if (!fTriggeredAvatarKey) + { + // a notify message with a state of 1 tells us to fire. + // we'll copy any events from this notify and use them when we send our + // own notify messages -- + plKey avatarKey = notifyMsg->GetAvatarKey(); + plSceneObject * obj = plSceneObject::ConvertNoRef(avatarKey->ObjectIsLoaded()); + const plArmatureMod * avMod = (plArmatureMod*)obj->GetModifierByType(plArmatureMod::Index()); + plAvBrainHuman *brain = (avMod ? plAvBrainHuman::ConvertNoRef(avMod->GetCurrentBrain()) : nil); + if (brain && !brain->IsRunningTask()) + { + plNotifyMsg *notifyEnter = TRACKED_NEW plNotifyMsg(); // a message to send back when the brain starts + notifyEnter->fState = 1.0f; // it's an "on" event + ISetupNotify(notifyEnter, notifyMsg); // copy events and address to sender + + plNotifyMsg *notifyExit = nil; + if (avatarKey == plNetClientApp::GetInstance()->GetLocalPlayerKey()) + { + notifyExit = TRACKED_NEW plNotifyMsg(); // a new message to send back when the brain's done + notifyExit->fState = 0.0f; // it's an "off" event + ISetupNotify(notifyExit, notifyMsg); // copy events and address to sender + notifyExit->AddReceiver(GetKey()); // have this message come back to us as well + + // A player may have joined while we're sitting. We can't update them with the exit notify at + // that point (security hole), so instead the local avatar sends the message out to everybody + // when done. + notifyExit->SetBCastFlag(plMessage::kNetStartCascade, false); + notifyExit->SetBCastFlag(plMessage::kNetPropagate, true); + notifyExit->SetBCastFlag(plMessage::kNetForce, true); + } + Trigger(avMod, notifyEnter, notifyExit); + } + } + // eat the message either way + result = true; + } else if (notifyMsg->fState == 0.0f && msg->GetSender() == GetKey()) { + // it's a notify, but off; untrigger + UnTrigger(); + result = true; + } + else if (notifyMsg->GetEventCount() > 0 && notifyMsg->GetEventRecord(0)->fEventType == proEventData::kMultiStage && notifyMsg->GetAvatarKey() == plNetClientApp::GetInstance()->GetLocalPlayerKey()) + { + proMultiStageEventData* mStage = (proMultiStageEventData*)notifyMsg->GetEventRecord(0); + if (mStage->fEvent == proMultiStageEventData::kEnterStage && mStage->fStage == 1) + { + plKey avatarKey = notifyMsg->GetAvatarKey(); + plSceneObject * obj = plSceneObject::ConvertNoRef(avatarKey->ObjectIsLoaded()); + plArmatureMod * avMod = (plArmatureMod*)obj->GetModifierByType(plArmatureMod::Index()); + + UInt32 flags = kBCastToClients | kUseRelevanceRegions | kForceFullSend; + avMod->DirtyPhysicalSynchState(flags); + } + } + } + + return result || plSingleModifier::MsgReceive(msg); +} + +// Trigger ---------------------------------------------------------------------------------------- +// -------- +void plSittingModifier::Trigger(const plArmatureMod *avMod, plNotifyMsg *enterNotify, plNotifyMsg *exitNotify) +{ + if (avMod) + { + plKey avModKey = avMod->GetKey(); // key to the avatar MODIFIER + plKey ourKey = GetKey(); // key to this modifier + plKey seekKey = GetTarget()->GetKey(); // key to the scene object this modifier's on + + // send the SEEK message + + + char *animName = nil; // this will be the name of our sit animation, which we + // need to know so we can seek properly. + + plAvBrainGeneric *brain = IBuildSitBrain(avModKey, seekKey, &animName, enterNotify, exitNotify); + if(brain) + { + plAvSeekMsg *seekMsg = TRACKED_NEW plAvSeekMsg(ourKey, avModKey, seekKey, 0.0f, true, kAlignHandleAnimEnd, animName); + seekMsg->Send(); + plAvTaskBrain *brainTask = TRACKED_NEW plAvTaskBrain(brain); + plAvTaskMsg *brainMsg = TRACKED_NEW plAvTaskMsg(ourKey, avModKey, brainTask); + + brainMsg->Send(); + + if (avModKey == plAvatarMgr::GetInstance()->GetLocalAvatarKey()) + { + plCameraMsg* pCam = TRACKED_NEW plCameraMsg; + pCam->SetBCastFlag(plMessage::kBCastByExactType); + pCam->SetCmd(plCameraMsg::kResetPanning); + pCam->Send(); + + plCameraMsg* pCam2 = TRACKED_NEW plCameraMsg; + pCam2->SetBCastFlag(plMessage::kBCastByExactType); + pCam2->SetCmd(plCameraMsg::kPythonSetFirstPersonOverrideEnable); + pCam2->SetActivated(false); + pCam2->Send(); + + plCameraMsg* pCam3 = TRACKED_NEW plCameraMsg; + pCam3->SetBCastFlag(plMessage::kBCastByExactType); + pCam3->SetCmd(plCameraMsg::kPythonUndoFirstPerson); + pCam3->Send(); + } + fTriggeredAvatarKey = avModKey; + + if (fMiscFlags & kDisableForward) + { + if (fTriggeredAvatarKey == plAvatarMgr::GetInstance()->GetLocalAvatarKey()) + plAvatarInputInterface::GetInstance()->EnableControl(false, B_CONTROL_MOVE_FORWARD); + } + } + } +} + +// IIsClosestAnim ------------------------------------------------------------------- +// --------------- +bool IIsClosestAnim(const char *animName, hsMatrix44 &sitGoal, hsScalar &closestDist, + hsPoint3 curPosition, const plArmatureMod *avatar) +{ + plAGAnim *anim = avatar->FindCustomAnim(animName); + if(anim) + { + hsMatrix44 animEndToStart; + // The sit target is the position we want to be at the END of the sit animation. + // We have several animations to choose from, each starting from a different + // position. + // This will look at one of those animations and figure out how far we are from + // its starting position. + // The first step is to get the transform from the end to the beginning of the + // animation. That's what this next line is doing. It's a bit unintuitive + // until you look at the parameter definitions. + GetStartToEndTransform(anim, nil, &animEndToStart, "Handle"); + hsMatrix44 candidateGoal = sitGoal * animEndToStart; + hsPoint3 distP = candidateGoal.GetTranslate() - curPosition; + hsVector3 distV(distP.fX, distP.fY, distP.fZ); + hsScalar dist = distP.Magnitude(); + if(closestDist == 0.0 || dist < closestDist) + { + closestDist = dist; + return true; + } + } else { + char buffy[256]; + sprintf(buffy, "Missing sit animation: %s", animName); + hsAssert(false, buffy); + } + return false; +} + +// IBuildSitBrain --------------------------------------------------------------------- +// ---------------- +plAvBrainGeneric *plSittingModifier::IBuildSitBrain(plKey avModKey, plKey seekKey, + char **pAnimName, plNotifyMsg *enterNotify, plNotifyMsg *exitNotify) +{ + plArmatureMod *avatar = plArmatureMod::ConvertNoRef(avModKey->ObjectIsLoaded()); + plSceneObject *seekObj = plSceneObject::ConvertNoRef(seekKey->ObjectIsLoaded()); + hsMatrix44 animEndToStart; + hsMatrix44 sitGoal = seekObj->GetLocalToWorld(); + hsMatrix44 candidateGoal; + hsScalar closestDist = 0.0f; + UInt8 closestApproach = 0; + hsPoint3 curPosition = avatar->GetTarget(0)->GetLocalToWorld().GetTranslate(); + char * sitAnimName = nil; + char * standAnimName = "StandUpFront"; // always prefer to stand facing front + + bool frontClear = fMiscFlags & kApproachFront; + plAvBrainGeneric *brain = nil; + + if(avatar) + { + if(fMiscFlags & kApproachLeft && IIsClosestAnim("SitLeft", sitGoal, closestDist, curPosition, avatar)) + { + closestApproach = kApproachLeft; + sitAnimName = "SitLeft"; + if(!frontClear) + standAnimName = "StandUpLeft"; + } + if(fMiscFlags & kApproachRight && IIsClosestAnim("SitRight", sitGoal, closestDist, curPosition, avatar)) + { + closestApproach = kApproachRight; + sitAnimName = "SitRight"; + if(!frontClear) + standAnimName = "StandUpRight"; + } + if(frontClear && IIsClosestAnim("SitFront", sitGoal, closestDist, curPosition, avatar)) + { + sitAnimName = "SitFront"; + standAnimName = "StandUpFront"; + closestApproach = kApproachFront; + } + + if(sitAnimName) + { + UInt32 exitFlags = plAvBrainGeneric::kExitNormal; // SOME stages can be interrupted, but not the brain itself + brain = TRACKED_NEW plAvBrainGeneric(nil, enterNotify, exitNotify, nil, exitFlags, plAvBrainGeneric::kDefaultFadeIn, + plAvBrainGeneric::kDefaultFadeOut, plAvBrainGeneric::kMoveRelative); + + plAnimStage *sitStage = TRACKED_NEW plAnimStage(sitAnimName, 0, plAnimStage::kForwardAuto, plAnimStage::kBackNone, + plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); + plAnimStage *idleStage = TRACKED_NEW plAnimStage("SitIdle", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, + plAnimStage::kAdvanceOnMove, plAnimStage::kRegressNone, -1); + plAnimStage *standStage = TRACKED_NEW plAnimStage(standAnimName, 0, plAnimStage::kForwardAuto, plAnimStage::kBackNone, + plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); + + brain->AddStage(sitStage); + brain->AddStage(idleStage); + brain->AddStage(standStage); + *pAnimName = sitAnimName; + + brain->SetType(plAvBrainGeneric::kSit); + brain->SetRecipient(GetKey()); + } + } + return brain; +} + +// UnTrigger ---------------------- +// ---------- +void plSittingModifier::UnTrigger() +{ + if (fTriggeredAvatarKey == plAvatarMgr::GetInstance()->GetLocalAvatarKey()) + { + plCameraMsg* pCam = TRACKED_NEW plCameraMsg; + pCam->SetBCastFlag(plMessage::kBCastByExactType); + pCam->SetCmd(plCameraMsg::kResetPanning); + pCam->Send(); + + plCameraMsg* pCam2 = TRACKED_NEW plCameraMsg; + pCam2->SetBCastFlag(plMessage::kBCastByExactType); + pCam2->SetCmd(plCameraMsg::kPythonSetFirstPersonOverrideEnable); + pCam2->SetActivated(true); + pCam2->Send(); + + // The flag means we disabled it, so re-enable on UnTrigger. + if (fMiscFlags & kDisableForward) + plAvatarInputInterface::GetInstance()->EnableControl(true, B_CONTROL_MOVE_FORWARD); + } + + fTriggeredAvatarKey = nil; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSittingModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSittingModifier.h new file mode 100644 index 00000000..647eae0d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSittingModifier.h @@ -0,0 +1,112 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSittingModifier_inc +#define plSittingModifier_inc + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// + +#include "../pnModifier/plSingleModifier.h" // base class +#include "../pnKeyedobject/plKey.h" // for the notification keys +#include "hsTemplates.h" // for the array they're kept in + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DECLARATIONS +// +///////////////////////////////////////////////////////////////////////////////////////// +class plNotifyMsg; +class plAvBrainGeneric; +class plArmatureMod; + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEFINITIONS +// +///////////////////////////////////////////////////////////////////////////////////////// + +class plSittingModifier : public plSingleModifier +{ +public: + enum + { + kApproachFront = 0x01, + kApproachLeft = 0x02, + kApproachRight = 0x04, + kApproachRear = 0x08, + kApproachMask = kApproachFront | kApproachLeft | kApproachRight | kApproachRear, + kDisableForward = 0x10, + }; + + UInt8 fMiscFlags; + + plSittingModifier(); + plSittingModifier(bool hasFront, bool hasLeft, bool hasRight); + virtual ~plSittingModifier(); + + CLASSNAME_REGISTER( plSittingModifier ); + GETINTERFACE_ANY( plSittingModifier, plSingleModifier ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage *msg); + + void AddNotifyKey(plKey key) { fNotifyKeys.Append(key); } + + virtual void Trigger(const plArmatureMod *avMod, plNotifyMsg *enterNotify, plNotifyMsg *exitNotify); + virtual void UnTrigger(); + +protected: + /** We've been triggered: go ahead and send the seek and brain tasks to the + triggering avatar. */ + hsBool IEmitCommand(plKey playerKey, plMessage *enterCallback, plMessage *exitCallback); + + /** Set up generic notification messages which were passed in by the responder / + max authoring stuff. */ + void ISetupNotify(plNotifyMsg *notifyMsg, plNotifyMsg *originalNotify); + + /** Figure out which approach we should use to the sit target, and add the relevant + stages to the brain. */ + plAvBrainGeneric * IBuildSitBrain(plKey avModKey, plKey seekKey,char **pAnimName, plNotifyMsg *enterNotify, plNotifyMsg *exitNotify); + + /** Unused. */ + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return true; } + + /** An array of keys to objects that are interested in receiving our sit messages. */ + hsTArray fNotifyKeys; + + /** The chair in question is in use. It will untrigger when the avatar leaves it. */ + //hsBool fTriggered; + plKey fTriggeredAvatarKey; +}; + + + +#endif plSittingModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSwimRegion.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSwimRegion.cpp new file mode 100644 index 00000000..b1e0dd44 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSwimRegion.cpp @@ -0,0 +1,265 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsGeometry3.h" +#include "hsResMgr.h" + +#include "plPhysicalControllerCore.h" +#include "plSwimRegion.h" +#include "plArmatureMod.h" + +void plSwimRegionInterface::Read(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Read(s, mgr); + + fDownBuoyancy = s->ReadSwapScalar(); + fUpBuoyancy = s->ReadSwapScalar(); + fMaxUpwardVel = s->ReadSwapScalar(); +} + +void plSwimRegionInterface::Write(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Write(s, mgr); + + s->WriteSwapScalar(fDownBuoyancy); + s->WriteSwapScalar(fUpBuoyancy); + s->WriteSwapScalar(fMaxUpwardVel); +} + +void plSwimRegionInterface::GetCurrent(plPhysicalControllerCore *physical, hsVector3 &linearResult, hsScalar &angularResult, hsScalar elapsed) +{ + linearResult.Set(0.f, 0.f, 0.f); + angularResult = 0.f; +} + +///////////////////////////////////////////////////////////////////////// + +plSwimCircularCurrentRegion::plSwimCircularCurrentRegion() : + fCurrentSO(nil), + fRotation(0.f), + fPullNearDistSq(1.f), + fPullNearVel(0.f), + fPullFarDistSq(1.f), + fPullFarVel(0.f) +{ +} + +void plSwimCircularCurrentRegion::Read(hsStream* stream, hsResMgr* mgr) +{ + plSwimRegionInterface::Read(stream, mgr); + + fRotation = stream->ReadSwapScalar(); + fPullNearDistSq = stream->ReadSwapScalar(); + fPullNearVel = stream->ReadSwapScalar(); + fPullFarDistSq = stream->ReadSwapScalar(); + fPullFarVel = stream->ReadSwapScalar(); + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // currentSO +} + +void plSwimCircularCurrentRegion::Write(hsStream* stream, hsResMgr* mgr) +{ + plSwimRegionInterface::Write(stream, mgr); + + stream->WriteSwapScalar(fRotation); + stream->WriteSwapScalar(fPullNearDistSq); + stream->WriteSwapScalar(fPullNearVel); + stream->WriteSwapScalar(fPullFarDistSq); + stream->WriteSwapScalar(fPullFarVel); + mgr->WriteKey(stream, fCurrentSO); +} + +hsBool plSwimCircularCurrentRegion::MsgReceive(plMessage* msg) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + plSceneObject *so = plSceneObject::ConvertNoRef(refMsg->GetRef()); + if (so) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) + fCurrentSO = so; + else if (refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove)) + fCurrentSO = nil; + + return true; + } + } + + return plSwimRegionInterface::MsgReceive(msg); +} + +void plSwimCircularCurrentRegion::GetCurrent(plPhysicalControllerCore *physical, hsVector3 &linearResult, hsScalar &angularResult, hsScalar elapsed) +{ + if (elapsed <= 0.f || fCurrentSO == nil || GetProperty(kDisable)) + { + linearResult.Set(0.f, 0.f, 0.f); + angularResult = 0.f; + return; + } + + hsPoint3 center, pos; + center.Set(&fCurrentSO->GetLocalToWorld().GetTranslate()); + + plKey worldKey = physical->GetSubworld(); + if (worldKey) + { + plSceneObject* so = plSceneObject::ConvertNoRef(worldKey->ObjectIsLoaded()); + center = so->GetWorldToLocal() * center; + } + + center.fZ = 0.f; // Just doing 2D + + physical->GetPositionSim(pos); + + hsBool applyPull = true; + hsVector3 pos2Center(center.fX - pos.fX, center.fY - pos.fY, 0.f); + hsScalar pullVel; + hsScalar distSq = pos2Center.MagnitudeSquared(); + if (distSq < .5) + { + // Don't want to pull us too close to the center, or we + // get this annoying jitter. + pullVel = 0.f; + } + else if (distSq <= fPullNearDistSq) + pullVel = fPullNearVel; + else if (distSq >= fPullFarDistSq) + pullVel = fPullFarVel; + else + pullVel = fPullNearVel + (fPullFarVel - fPullNearVel) * (distSq - fPullNearDistSq) / (fPullFarDistSq - fPullNearDistSq); + + hsVector3 pull = pos2Center; + pull.Normalize(); + linearResult.Set(pull.fY, -pull.fX, pull.fZ); + + pull *= pullVel; + linearResult *= fRotation; + linearResult += pull; + + hsVector3 v1 = linearResult * elapsed - pos2Center; + hsVector3 v2 = -pos2Center; + hsScalar invCos = v1.InnerProduct(v2) / v1.Magnitude() / v2.Magnitude(); + if (invCos > 1) + invCos = 1; + if (invCos < -1) + invCos = -1; + angularResult = hsACosine(invCos) / elapsed; + +// hsAssert(real_finite(linearResult.fX) && +// real_finite(linearResult.fY) && +// real_finite(linearResult.fZ) && +// real_finite(angularResult), "Bad water current computation."); +} + +///////////////////////////////////////////////////////////////////////////////////// + +plSwimStraightCurrentRegion::plSwimStraightCurrentRegion() : + fCurrentSO(nil), + fNearDist(1.f), + fNearVel(0.f), + fFarDist(1.f), + fFarVel(0.f) +{ +} + +void plSwimStraightCurrentRegion::Read(hsStream* stream, hsResMgr* mgr) +{ + plSwimRegionInterface::Read(stream, mgr); + + fNearDist = stream->ReadSwapScalar(); + fNearVel = stream->ReadSwapScalar(); + fFarDist = stream->ReadSwapScalar(); + fFarVel = stream->ReadSwapScalar(); + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // currentSO +} + +void plSwimStraightCurrentRegion::Write(hsStream* stream, hsResMgr* mgr) +{ + plSwimRegionInterface::Write(stream, mgr); + + stream->WriteSwapScalar(fNearDist); + stream->WriteSwapScalar(fNearVel); + stream->WriteSwapScalar(fFarDist); + stream->WriteSwapScalar(fFarVel); + mgr->WriteKey(stream, fCurrentSO); +} + +hsBool plSwimStraightCurrentRegion::MsgReceive(plMessage* msg) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + plSceneObject *so = plSceneObject::ConvertNoRef(refMsg->GetRef()); + if (so) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) + fCurrentSO = so; + else if (refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove)) + fCurrentSO = nil; + + return true; + } + } + + return plSwimRegionInterface::MsgReceive(msg); +} + +void plSwimStraightCurrentRegion::GetCurrent(plPhysicalControllerCore *physical, hsVector3 &linearResult, hsScalar &angularResult, hsScalar elapsed) +{ + angularResult = 0.f; + + if (elapsed <= 0.f || GetProperty(kDisable)) + { + linearResult.Set(0.f, 0.f, 0.f); + return; + } + + hsPoint3 center, pos; + hsVector3 current = fCurrentSO->GetLocalToWorld() * hsVector3(0.f, 1.f, 0.f); + center.Set(&fCurrentSO->GetLocalToWorld().GetTranslate()); + physical->GetPositionSim(pos); + + plKey worldKey = physical->GetSubworld(); + if (worldKey) + { + plSceneObject* so = plSceneObject::ConvertNoRef(worldKey->ObjectIsLoaded()); + hsMatrix44 w2l = so->GetWorldToLocal(); + center = w2l * center; + current = w2l * current; + } + + hsVector3 pos2Center(center.fX - pos.fX, center.fY - pos.fY, 0.f); + hsScalar dist = current.InnerProduct(pos - center); + hsScalar pullVel; + + if (dist <= fNearDist) + pullVel = fNearVel; + else if (dist >= fFarDist) + pullVel = fFarVel; + else + pullVel = fNearVel + (fFarVel - fNearVel) * (dist - fNearDist) / (fFarDist - fNearDist); + + linearResult = current * pullVel; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSwimRegion.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSwimRegion.h new file mode 100644 index 00000000..8964404a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plAvatar/plSwimRegion.h @@ -0,0 +1,110 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSwimRegion_inc +#define plSwimRegion_inc + +#include "../pnSceneObject/plObjInterface.h" + +class plArmatureModBase; +class plPhysical; +struct hsVector3; +class plPhysicalControllerCore; +class plSwimRegionInterface : public plObjInterface +{ +public: + plSwimRegionInterface() : fDownBuoyancy(1.f), fUpBuoyancy(1.f), fMaxUpwardVel(1.f) {} + virtual ~plSwimRegionInterface() {} + + CLASSNAME_REGISTER( plSwimRegionInterface ); + GETINTERFACE_ANY( plSwimRegionInterface, plObjInterface ); + + enum { + kDisable = 0x0, + kNumProps // last + }; + + virtual Int32 GetNumProperties() const { return kNumProps; } + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) {} + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual void GetCurrent(plPhysicalControllerCore *physical, hsVector3 &linearResult, hsScalar &angularResult, hsScalar elapsed); + + hsScalar fDownBuoyancy; + hsScalar fUpBuoyancy; + hsScalar fMaxUpwardVel; +}; + +class plSwimCircularCurrentRegion : public plSwimRegionInterface +{ +public: + plSwimCircularCurrentRegion(); + virtual ~plSwimCircularCurrentRegion() {} + + CLASSNAME_REGISTER( plSwimCircularCurrentRegion ); + GETINTERFACE_ANY( plSwimCircularCurrentRegion, plSwimRegionInterface ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual void GetCurrent(plPhysicalControllerCore *physical, hsVector3 &linearResult, hsScalar &angularResult, hsScalar elapsed); + virtual hsBool MsgReceive(plMessage* msg); + + hsScalar fRotation; + hsScalar fPullNearDistSq; + hsScalar fPullNearVel; + hsScalar fPullFarDistSq; + hsScalar fPullFarVel; + +protected: + plSceneObject *fCurrentSO; +}; + +class plSwimStraightCurrentRegion : public plSwimRegionInterface +{ +public: + plSwimStraightCurrentRegion(); + virtual ~plSwimStraightCurrentRegion() {} + + CLASSNAME_REGISTER( plSwimStraightCurrentRegion ); + GETINTERFACE_ANY( plSwimStraightCurrentRegion, plSwimRegionInterface ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual void GetCurrent(plPhysicalControllerCore *physical, hsVector3 &linearResult, hsScalar &angularResult, hsScalar elapsed); + virtual hsBool MsgReceive(plMessage* msg); + + hsScalar fNearDist; + hsScalar fNearVel; + hsScalar fFarDist; + hsScalar fFarVel; + +protected: + plSceneObject *fCurrentSO; +}; + +#endif // plSwimRegion_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plCompress.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plCompress.h new file mode 100644 index 00000000..dc655402 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plCompress.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plCompress_h +#define plCompress_h + +#include "hsTypes.h" + +// +// Abstract base class for compression code +// +class plCompress +{ +public: + plCompress() {} + virtual ~plCompress() {} + + // return true if successful + virtual hsBool Uncompress(UInt8* bufOut, UInt32* bufLenOut, const UInt8* bufIn, UInt32 bufLenIn) = 0; + virtual hsBool Compress(UInt8* bufOut, UInt32* bufLenOut, const UInt8* bufIn, UInt32 bufLenIn) = 0; + + // in place versions + virtual hsBool Uncompress(UInt8** bufIn, UInt32* bufLenIn, UInt32 maxBufLenOut, int offset=0) = 0; + virtual hsBool Compress(UInt8** bufIn, UInt32* bufLenIn, int offset=0) = 0; +}; + + +#endif // plCompress_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibCompress.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibCompress.cpp new file mode 100644 index 00000000..9bd0ddee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibCompress.cpp @@ -0,0 +1,268 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plZlibCompress.h" +#include "zlib.h" +#include "hsMemory.h" +#include "hsStream.h" + +hsBool plZlibCompress::Uncompress(UInt8* bufOut, UInt32* bufLenOut, const UInt8* bufIn, UInt32 bufLenIn) +{ + return (uncompress(bufOut, bufLenOut, bufIn, bufLenIn) == Z_OK); +} + +hsBool plZlibCompress::Compress(UInt8* bufOut, UInt32* bufLenOut, const UInt8* bufIn, UInt32 bufLenIn) +{ + // according to compress doc, the bufOut buffer should be at least .1% larger than source buffer, plus 12 bytes. + hsAssert(*bufLenOut>=(int)(bufLenIn*1.1+12), "bufOut compress buffer is not large enough"); + return (compress(bufOut, bufLenOut, bufIn, bufLenIn) == Z_OK); +} + +// +// copy bufOut to bufIn, set bufLenIn=bufLenOut +// +hsBool plZlibCompress::ICopyBuffers(UInt8** bufIn, UInt32* bufLenIn, char* bufOut, UInt32 bufLenOut, int offset, bool ok) +{ + if (ok) + { + *bufLenIn = bufLenOut+offset; + UInt8* newBuf = TRACKED_NEW UInt8[*bufLenIn]; // alloc new buffer + HSMemory::BlockMove(*bufIn, newBuf, offset); // copy offset (uncompressed) part + delete [] *bufIn; // delete original buffer + + HSMemory::BlockMove(bufOut, newBuf+offset, bufLenOut); // copy compressed part + delete [] bufOut; + *bufIn = newBuf; + return true; + } + delete [] bufOut; + return false; +} + +// +// In place version +// offset is how much to skip over when compressing +// +hsBool plZlibCompress::Compress(UInt8** bufIn, UInt32* bufLenIn, int offset) +{ + UInt32 adjBufLenIn = *bufLenIn - offset; + UInt8* adjBufIn = *bufIn + offset; + + // according to compress doc, the bufOut buffer should be at least .1% larger than source buffer, plus 12 bytes. + UInt32 bufLenOut = (int)(adjBufLenIn*1.1+12); + char* bufOut = TRACKED_NEW char[bufLenOut]; + + bool ok=(Compress((UInt8*)bufOut, &bufLenOut, (UInt8*)adjBufIn, adjBufLenIn) && + bufLenOut < adjBufLenIn); + return ICopyBuffers(bufIn, bufLenIn, bufOut, bufLenOut, offset, ok); +} + +// +// In place version +// +hsBool plZlibCompress::Uncompress(UInt8** bufIn, UInt32* bufLenIn, UInt32 bufLenOut, int offset) +{ + UInt32 adjBufLenIn = *bufLenIn - offset; + UInt8* adjBufIn = *bufIn + offset; + + char* bufOut = TRACKED_NEW char[bufLenOut]; + + bool ok=Uncompress((UInt8*)bufOut, &bufLenOut, (UInt8*)adjBufIn, adjBufLenIn) ? true : false; + return ICopyBuffers(bufIn, bufLenIn, bufOut, bufLenOut, offset, ok); +} + +//// .gz File Versions /////////////////////////////////////////////////////// + +#define kGzBufferSize 64 * 1024 +#if 1 + +hsBool plZlibCompress::UncompressFile( const char *compressedPath, const char *destPath ) +{ + gzFile inFile; + FILE *outFile; + hsBool worked = false; + int length, err; + + UInt8 buffer[ kGzBufferSize ]; + + + outFile = fopen( destPath, "wb" ); + if( outFile != nil ) + { + inFile = gzopen( compressedPath, "rb" ); + if( inFile != nil ) + { + for( ;; ) + { + length = gzread( inFile, buffer, sizeof( buffer ) ); + if( length < 0 ) + { + gzerror( inFile, &err ); + break; + } + if( length == 0 ) + { + worked = true; + break; + } + if( fwrite( buffer, 1, length, outFile ) != length ) + break; + } + if( gzclose( inFile ) != Z_OK ) + worked = false; + } + fclose( outFile ); + } + + return worked; +} + + +hsBool plZlibCompress::CompressFile( const char *uncompressedPath, const char *destPath ) +{ + FILE *inFile; + gzFile outFile; + hsBool worked = false; + int length, err; + + UInt8 buffer[ kGzBufferSize ]; + + + inFile = fopen( uncompressedPath, "rb" ); + if( inFile != nil ) + { + outFile = gzopen( destPath, "wb" ); + if( outFile != nil ) + { + for( ;; ) + { + length = fread( buffer, 1, sizeof( buffer ), inFile ); + if( ferror( inFile ) ) + break; + + if( length == 0 ) + { + worked = true; + break; + } + if( gzwrite( outFile, buffer, (unsigned)length ) != length ) + { + gzerror( outFile, &err ); + break; + } + } + if( gzclose( outFile ) != Z_OK ) + worked = false; + } + fclose( inFile ); + } + + return worked; +} + + +//// file <-> stream /////////////////////////////////////////////////////// + +hsBool plZlibCompress::UncompressToStream( const char * filename, hsStream * s ) +{ + gzFile inFile; + hsBool worked = false; + int length, err; + + UInt8 buffer[ kGzBufferSize ]; + + + inFile = gzopen( filename, "rb" ); + if( inFile != nil ) + { + for( ;; ) + { + length = gzread( inFile, buffer, sizeof( buffer ) ); + if( length < 0 ) + { + gzerror( inFile, &err ); + break; + } + if( length == 0 ) + { + worked = true; + break; + } + s->Write( length, buffer ); + } + if( gzclose( inFile ) != Z_OK ) + worked = false; + } + + return worked; +} + + +hsBool plZlibCompress::CompressToFile( hsStream * s, const char * filename ) +{ + gzFile outFile; + hsBool worked = false; + int length, err; + + UInt8 buffer[ kGzBufferSize ]; + + + outFile = gzopen( filename, "wb" ); + if( outFile != nil ) + { + for( ;; ) + { + int avail = s->GetEOF()-s->GetPosition(); + int n = ( avail>sizeof( buffer ) ) ? sizeof( buffer ) : avail; + + if( n == 0 ) + { + worked = true; + break; + } + + length = s->Read( n, buffer ); + + if( length == 0 ) + { + worked = true; + break; + } + + if( gzwrite( outFile, buffer, (unsigned)length ) != length ) + { + gzerror( outFile, &err ); + break; + } + } + if( gzclose( outFile ) != Z_OK ) + worked = false; + } + + return worked; +} + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibCompress.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibCompress.h new file mode 100644 index 00000000..ce93b93e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibCompress.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plZlibCompress_h +#define plZlibCompress_h + +#include "plCompress.h" + +class hsStream; + +class plZlibCompress : public plCompress +{ +protected: + hsBool ICopyBuffers(UInt8** bufIn, UInt32* bufLenIn, char* bufOut, UInt32 bufLenOut, int offset, bool ok ); +public: + hsBool Uncompress(UInt8* bufOut, UInt32* bufLenOut, const UInt8* bufIn, UInt32 bufLenIn); + hsBool Compress(UInt8* bufOut, UInt32* bufLenOut, const UInt8* bufIn, UInt32 bufLenIn); + + // in place versions + hsBool Uncompress(UInt8** bufIn, UInt32* bufLenIn, UInt32 maxBufLenOut, int offset=0); + hsBool Compress(UInt8** bufIn, UInt32* bufLenIn, int offset=0); + + // .gz versions + static hsBool UncompressFile( const char *compressedPath, const char *destPath ); + static hsBool CompressFile( const char *uncompressedPath, const char *destPath ); + + // file <-> stream + static hsBool UncompressToStream( const char * filename, hsStream * s ); + static hsBool CompressToFile( hsStream * s, const char * filename ); +}; + +#endif // plZlibCompress_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibStream.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibStream.cpp new file mode 100644 index 00000000..26be9ae1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibStream.cpp @@ -0,0 +1,299 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plZlibStream.h" +#include "zlib.h" + +voidpf ZlibAlloc(voidpf opaque, uInt items, uInt size) +{ + return ALLOC(items*size); +} + +void ZlibFree(voidpf opaque, voidpf address) +{ + FREE(address); +} + +plZlibStream::plZlibStream() : fOutput(nil), fZStream(nil), fHeader(kNeedMoreData), fDecompressedOk(false) +{ +} + +plZlibStream::~plZlibStream() +{ + hsAssert(!fOutput && !fZStream, "plZlibStream not closed"); +} + +hsBool plZlibStream::Open(const char* filename, const char* mode) +{ + wchar* wFilename = hsStringToWString(filename); + wchar* wMode = hsStringToWString(mode); + hsBool ret = Open(wFilename, wMode); + delete [] wFilename; + delete [] wMode; + return ret; +} + +hsBool plZlibStream::Open(const wchar* filename, const wchar* mode) +{ + fFilename = filename; + fMode = mode; + + fOutput = NEW(hsUNIXStream); + return fOutput->Open(filename, L"wb"); +} + +hsBool plZlibStream::Close() +{ + if (fOutput) + { + fOutput->Close(); + DEL(fOutput); + fOutput = nil; + } + if (fZStream) + { + z_streamp zstream = (z_streamp)fZStream; + inflateEnd(zstream); + DEL(zstream); + fZStream = nil; + } + + return true; +} + +UInt32 plZlibStream::Write(UInt32 byteCount, const void* buffer) +{ + UInt8* byteBuf = (UInt8*)buffer; + + // Check if we've read in the full gzip header yet + if (fHeader == kNeedMoreData) + { + int cutAmt = IValidateGzHeader(byteCount, buffer); + + // Once we've read in the whole header, cut out whatever part of it is + // in this buffer, leaving raw compressed data + if (cutAmt != 0) + { + byteCount -= cutAmt; + byteBuf += cutAmt; + } + } + + if (fHeader == kInvalidHeader) + return 0; + + if (fHeader == kValidHeader) + { + ASSERT(fOutput); + ASSERT(fZStream); + z_streamp zstream = (z_streamp)fZStream; + zstream->avail_in = byteCount; + zstream->next_in = byteBuf; + + char outBuf[2048]; + while (zstream->avail_in != 0) + { + zstream->avail_out = sizeof(outBuf); + zstream->next_out = (UInt8*)outBuf; + + UInt32 amtWritten = zstream->total_out; + + int ret = inflate(zstream, Z_NO_FLUSH); + + bool inflateErr = (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || + ret == Z_STREAM_ERROR || ret == Z_MEM_ERROR || ret == Z_BUF_ERROR); + // If we have a decompression error, just fail + if (inflateErr) + { + hsAssert(!inflateErr, "Error in inflate"); + fHeader = kInvalidHeader; + break; + } + + amtWritten = zstream->total_out - amtWritten; + fOutput->Write(amtWritten, outBuf); + + // If zlib says we hit the end of the stream, ignore avail_in + if (ret == Z_STREAM_END) + { + fDecompressedOk = true; + break; + } + } + } + + return byteCount; +} + +int plZlibStream::IValidateGzHeader(UInt32 byteCount, const void* buffer) +{ + // Ripped from gzio.cpp + #define HEAD_CRC 0x02 + #define EXTRA_FIELD 0x04 + #define ORIG_NAME 0x08 + #define COMMENT 0x10 + #define RESERVED 0xE0 + static int gz_magic[2] = {0x1f, 0x8b}; // gzip magic header + + #define CheckForEnd() if (s.AtEnd()) { fHeader = kNeedMoreData; return 0; } + + int i; + + int initCacheSize = fHeaderCache.size(); + for (i = 0; i < byteCount; i++) + fHeaderCache.push_back(((UInt8*)buffer)[i]); + hsReadOnlyStream s(fHeaderCache.size(), &fHeaderCache[0]); + + // Check the gzip magic header + for (i = 0; i < 2; i++) + { + UInt8 c = s.ReadByte(); + if (c != gz_magic[i]) + { + CheckForEnd(); + fHeader = kInvalidHeader; + return 0; + } + } + + int method = s.ReadByte(); + int flags = s.ReadByte(); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) + { + CheckForEnd(); + fHeader = kInvalidHeader; + return 0; + } + + // Discard time, xflags and OS code: + for (i = 0; i < 6; i++) + s.ReadByte(); + CheckForEnd(); + + if ((flags & EXTRA_FIELD) != 0) + { + // skip the extra field + UInt16 len = s.ReadSwap16(); + while (len-- != 0 && s.ReadByte()) + { + CheckForEnd(); + } + } + + int c; + + // skip the original file name + if ((flags & ORIG_NAME) != 0) + { + while ((c = s.ReadByte()) != 0) + { + CheckForEnd(); + } + } + + // skip the .gz file comment + if ((flags & COMMENT) != 0) + { + while ((c = s.ReadByte()) != 0) + { + CheckForEnd(); + } + } + + // skip the header crc + if ((flags & HEAD_CRC) != 0) + { + s.ReadSwap16(); + CheckForEnd(); + } + + CheckForEnd(); + + UInt32 headerSize = s.GetPosition(); + UInt32 clipBuffer = headerSize - initCacheSize; + + // Initialize the zlib stream + z_streamp zstream = NEW(z_stream_s); + memset(zstream, 0, sizeof(z_stream_s)); + zstream->zalloc = ZlibAlloc; + zstream->zfree = ZlibFree; + zstream->opaque = nil; + zstream->avail_in = byteCount - clipBuffer; + zstream->next_in = (UInt8*)&fHeaderCache[clipBuffer]; + // Gotta use inflateInit2, because there's no header for zlib to look at. + // The -15 tells it to not try and find a header + bool initOk = (inflateInit2(zstream, -15) == Z_OK); + fZStream = zstream; + + fHeaderCache.clear(); + + if (!initOk) + { + hsAssert(0, "Zip init failed"); + fHeader = kInvalidHeader; + return 0; + } + ASSERT(fZStream); + + fHeader = kValidHeader; + return clipBuffer; +} + +hsBool plZlibStream::AtEnd() +{ + hsAssert(0, "AtEnd not supported"); + return true; +} + +UInt32 plZlibStream::Read(UInt32 byteCount, void* buffer) +{ + hsAssert(0, "Read not supported"); + return 0; +} + +void plZlibStream::Skip(UInt32 deltaByteCount) +{ + hsAssert(0, "Skip not supported"); +} + +void plZlibStream::Rewind() +{ + // hack so rewind will work (someone thought it would be funny to not implement base class functions) + Close(); + Open(fFilename.c_str(), fMode.c_str()); + fHeader = kNeedMoreData; + fDecompressedOk = false; +} + +void plZlibStream::FastFwd() +{ + hsAssert(0, "FastFwd not supported"); +} + +UInt32 plZlibStream::GetEOF() +{ + hsAssert(0, "GetEOF not supported"); + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibStream.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibStream.h new file mode 100644 index 00000000..020be6ba --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plCompression/plZlibStream.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plZlibStream_h_inc +#define plZlibStream_h_inc + +#include "hsStream.h" +#include "hsStlUtils.h" + +// +// This is for reading a .gz file from a buffer, and writing the uncompressed data to a file. +// Call open with the name of the uncompressed file, then call write with the compressed data. +// +class plZlibStream : public hsStream +{ +protected: + hsStream* fOutput; + void* fZStream; + bool fDecompressedOk; + + enum Validate { kNeedMoreData, kInvalidHeader, kValidHeader }; + Validate fHeader; + std::vector fHeaderCache; + + std::wstring fFilename, fMode; // needed for rewind function + + int IValidateGzHeader(UInt32 byteCount, const void* buffer); + +public: + plZlibStream(); + virtual ~plZlibStream(); + + virtual hsBool Open(const char* filename, const char* mode); + virtual hsBool Open(const wchar* filename, const wchar* mode); + virtual hsBool Close(); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + + // Since most functions don't check the return value from Write, you can + // call this after you've passed in all your data to determine if it + // decompressed ok + bool DecompressedOk() { return fDecompressedOk; } + + // You can't use these + virtual hsBool AtEnd(); + virtual UInt32 Read(UInt32 byteCount, void* buffer); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void FastFwd(); + virtual UInt32 GetEOF(); +}; + +#endif // plZlibStream_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/hsStringTable.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/hsStringTable.cpp new file mode 100644 index 00000000..4bd0c364 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/hsStringTable.cpp @@ -0,0 +1,223 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStringTable.h" +#include "stdlib.h" +#if HS_BUILD_FOR_PS2 || __MWERKS__ || HS_BUILD_FOR_UNIX +#include +#endif +// +// hsStringTable +// + +hsStringTable::Node::Node(char c) : sib(0), kid(0), chr(c), data(0) {}; +hsStringTable::Node::~Node() { }; +void* hsStringTable::Node::GetData() { return data; } +void hsStringTable::Node::SetData(void* v) { data = v; } + +hsStringTable::~hsStringTable() +{ + RemoveNode(root.kid); +} + +void hsStringTable::Reset() +{ + RemoveNode(root.kid); + root.kid = nil; +} + + +// +// Find a the Node associated with a given string +// +hsStringTable::Node* hsStringTable::Find(const char* str) +{ + return (str && *str) ? FindRecur(root.kid, str) : nil; +} + +// +// Find the unique node matching the given prefix. Returns the node +// at the first point where a unique path can not be determined +// char* str is filled out with the matching path +// +hsStringTable::Node* hsStringTable::FindPartial(char* str, Int32 len) const +{ + return (str && *str) ? FindPartialRecur(root.kid, str, len) : nil; +} + +// +// Find and Add if needs a node for str and register it with data +// +void hsStringTable::Register(const char* str, void* data) +{ + Node* node = FindRecur(root.kid, str, true); + if (!node) + { + node = AddRecur(&root, str); + } + node->SetData(data); +} + +// +// Iterate through the tree and call the callback function on each child node +// of the fromNode (or the root if fromNode is nil) +// +hsBool hsStringTable::Iterate(hsStringTableCallback* callback, Node* fromNode) +{ + if (!fromNode) + fromNode = &root; + return IterateRecur(fromNode->kid,callback); +} + +// +// Recursively find node, create if needed? +// +hsStringTable::Node* hsStringTable::FindRecur(Node* root, const char* str, hsBool createIfNeeded) +{ + if (!root || !str) return nil; + if (tolower(root->chr)==tolower(*str)) + { + if (*(str+1)) + { + Node* node = FindRecur(root->kid, str+1, createIfNeeded); + if (!node && createIfNeeded) node = AddRecur(root, str+1); + return node; + } + else + { + return root; + } + } + else + { + return FindRecur(root->sib,str,createIfNeeded); + } +} + +// +// Recursively find the unique partial match +// +hsStringTable::Node* hsStringTable::FindPartialRecur(Node* root, char* str, Int32 len) const +{ + if (!root || !str) + { + return nil; + } + + if (tolower(root->chr)==tolower(*str)) + { + if (len) + { + *str = root->chr; + } + + if (*(str+1)) + { + Node* node = FindPartialRecur(root->kid,str+1,len-1); + //if (!node) node = AddRecur(root,str+1); + return node; + } + else + { + return FindLeafRecur(root, str, len); + } + } + else + { + return FindPartialRecur(root->sib, str, len); + } +} + +// +// Follow the current node as far as possible towards a unique leaf +// +hsStringTable::Node* hsStringTable::FindLeafRecur(Node* root, char* str, Int32 len) const +{ + if (root->data || !root->kid || root->kid->sib) + { + if (len) + { + *(str+1) = 0; + } + return root; + } + else + { + if (len) + { + *(str+1) = len>1 ? root->kid->chr : 0; + } + return FindLeafRecur(root->kid,str+1,len-1); + } +} + + +// +// Add a string to the tree +// +hsStringTable::Node* hsStringTable::AddRecur(Node* root, const char* str) +{ + Node* node = TRACKED_NEW Node(*str); + node->sib = root->kid; + root->kid = node; + if (*(str+1)) return AddRecur(node,str+1); + else return node; +} + +// +// Remove a node recursive from the tree +// +void hsStringTable::RemoveNode(Node* root) +{ + if (!root) return; + RemoveNode(root->kid); + RemoveNode(root->sib); + delete root; +} + +// +// Recurse through tree and call callback on each node +// +hsBool hsStringTable::IterateRecur(Node* root, hsStringTableCallback* callback) +{ + if (!root) + return true; + if (root->data) + { + if (!callback(root)) + return false; + } + if (root->kid) + { + if (!IterateRecur(root->kid,callback)) + return false; + } + if (root->sib) + { + if (!IterateRecur(root->sib,callback)) + return false; + } + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/hsStringTable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/hsStringTable.h new file mode 100644 index 00000000..7c905585 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/hsStringTable.h @@ -0,0 +1,71 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef HS_STRING_TABLE_H +#define HS_STRING_TABLE_H + +#include "HeadSpin.h" + +class hsStringTable +{ +public: + class Node { + protected: + Node(char c=0); + ~Node(); + public: + void* GetData(); + void SetData(void* v); + friend class hsStringTable; + private: + Node* sib; + Node* kid; + char chr; + void* data; + }; + + hsStringTable() {} + ~hsStringTable(); + void Reset(); + Node* Find(const char* str); + Node* FindPartial(char* str, Int32 len=0) const; + void Register(const char* str, void* data); + + typedef hsBool (hsStringTableCallback)(Node*); + hsBool Iterate(hsStringTableCallback* callback, Node* fromNode=nil); +private: + Node* FindRecur(Node* root, const char* str, hsBool createIfNeeded=false); + Node* FindPartialRecur(Node* root, char* str, Int32 len) const; + Node* AddRecur(Node* root, const char* str); + Node* FindLeafRecur(Node* root, char* str, Int32 len) const; + void RemoveNode(Node* root); + hsBool IterateRecur(Node* root, hsStringTableCallback* callback); + + Node root; +}; + + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfo.cpp new file mode 100644 index 00000000..43905961 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfo.cpp @@ -0,0 +1,1127 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plConfigInfo.h" + +#include "hsStlUtils.h" +#include +#include +#include +#include + +const std::string& plConfigInfo::GlobalSection() +{ + static std::string section("global"); + return section; +} + +plConfigInfo::plConfigInfo() +{ +} + +plConfigInfo::plConfigInfo(const plConfigInfo & src) +: fSections(src.fSections) +{ +} + +plConfigInfo & plConfigInfo::operator =(const plConfigInfo & src) +{ + fSections = src.fSections; + return *this; +} + +void plConfigInfo::Clear() +{ + fSections.clear(); +} + +void plConfigInfo::RemoveSection(const std::string & section) +{ + fSections.erase(section.c_str()); +} + +void plConfigInfo::RemoveKey(const std::string & section, const std::string & key) +{ + Sections::iterator si = fSections.find(section.c_str()); + if (si != fSections.end()) + fSections[section.c_str()].RemoveKey(key); +} + +bool plConfigInfo::HasSection(const std::string & section) const +{ + return fSections.find(section.c_str())!=fSections.end(); +} + +bool plConfigInfo::HasKey(const std::string & section, const std::string & key) +{ + Sections::iterator si = fSections.find(section.c_str()); + if (si == fSections.end()) + return false; + return (si->second.HasKey(key)); +} + +bool plConfigInfo::HasKeyAny(const std::string & key) +{ + for (Sections::iterator si=fSections.begin(); si!=fSections.end(); ++si) + { + if (si->second.HasKey(key)) + return true; + } + return false; +} + +bool plConfigInfo::HasKeyIn(const std::string & key, const char * section1, ...) +{ + const char * section = section1; + va_list va; + va_start(va,section1); + std::vector sections; + while (section) + { + sections.push_back( section ); + section = va_arg(va,const char *); + } + va_end(va); + return HasKeyIn( key, sections ); +} + +bool plConfigInfo::HasKeyIn(const std::string & key, const std::vector & sections ) +{ + for ( int i=0; isecond.KeyHasValue(key,value); +} + +bool plConfigInfo::KeyHasValue(const std::string & section, const std::string & key, int value) +{ + Sections::iterator si = fSections.find(section.c_str()); + if (si == fSections.end()) + return false; + return si->second.KeyHasValue(key,value); +} + +bool plConfigInfo::KeyHasValue(const std::string & section, const std::string & key, double value) +{ + Sections::iterator si = fSections.find(section.c_str()); + if (si == fSections.end()) + return false; + return si->second.KeyHasValue(key,value); +} + +bool plConfigInfo::AddValue(const std::string & section, const std::string & key, const std::string & value, KAddValueMode mode) +{ + fSections[section.c_str()].AddValue(key,value,mode); + return true; +} + +bool plConfigInfo::AddValue(const std::string & section, const std::string & key, int value, KAddValueMode mode) +{ + char buf[20]; + sprintf(buf, "%d", value); + std::string v(buf); + return AddValue(section,key,v,mode); +} + +bool plConfigInfo::AddValue(const std::string & section, const std::string & key, double value, KAddValueMode mode) +{ + char buf[30]; + sprintf(buf, "%f", value); + std::string v(buf); + return AddValue(section,key,v,mode); +} + +bool plConfigInfo::AddValues(const std::string & section, const std::string & key, const std::vector & values, KAddValueMode mode) +{ + return fSections[section.c_str()].AddValues(key,values); +} + +plKeysAndValues plConfigInfo::GetSection(const std::string & section, bool & found) +{ + found = HasSection(section); + if (found) + return fSections[section.c_str()]; + else + return plKeysAndValues(); // empty +} + +std::vector plConfigInfo::GetSectionNames() +{ + std::vector results; + for (Sections::const_iterator ii=fSections.begin(); ii!=fSections.end(); ++ii) + results.push_back(ii->first.c_str()); + return results; +} + +std::string plConfigInfo::GetValue(const std::string & section, const std::string & key, const std::string & defval, bool * outFound) const +{ + return fSections[section.c_str()].GetValue(key,defval,outFound); +} + +int plConfigInfo::GetValue(const std::string & section, const std::string & key, int defval, bool * outFound) const +{ + return fSections[section.c_str()].GetValue(key,defval,outFound); +} + +double plConfigInfo::GetValue(const std::string & section, const std::string & key, double defval, bool * outFound) const +{ + return fSections[section.c_str()].GetValue(key,defval,outFound); +} + +std::vector plConfigInfo::GetAllValues(const std::string & section, const std::string & key) const +{ + Sections::iterator si = fSections.find(section.c_str()); + if (si != fSections.end()) + return si->second.GetAllValues(key); + std::vector empty; + return empty; +} + + +std::string plConfigInfo::GetValueAny(const std::string & key, const std::string & defval, bool * outFound) const +{ + if (outFound) *outFound=false; + for (Sections::iterator si=fSections.begin(); si!=fSections.end(); ++si) + if (si->second.HasKey(key)) + return si->second.GetValue(key,defval,outFound); + return defval; +} + +int plConfigInfo::GetValueAny(const std::string & key, int defval, bool * outFound) const +{ + if (outFound) *outFound=false; + for (Sections::iterator si=fSections.begin(); si!=fSections.end(); ++si) + if (si->second.HasKey(key)) + return si->second.GetValue(key,defval,outFound); + return defval; +} + +double plConfigInfo::GetValueAny(const std::string & key, double defval, bool * outFound) const +{ + if (outFound) *outFound=false; + for (Sections::iterator si=fSections.begin(); si!=fSections.end(); ++si) + if (si->second.HasKey(key)) + return si->second.GetValue(key,defval,outFound); + return defval; +} + +std::vector plConfigInfo::GetAllValuesAny(const std::string & key) const +{ + for (Sections::iterator si=fSections.begin(); si!=fSections.end(); ++si) + if (si->second.HasKey(key)) + return si->second.GetAllValues(key); + std::vector empty; + return empty; +} + + +std::string plConfigInfo::GetValueIn(const std::string & key, const std::string & defval, bool * outFound, const char * section1, ...) const +{ + if (outFound) *outFound=false; + const char * section = section1; + va_list sections; + va_start(sections,section1); + while (section) + { + if (HasSection(section)) + { + plKeysAndValues & kv = fSections[section]; + if (kv.HasKey(key)) + return kv.GetValue(key,defval,outFound); + } + section = va_arg(sections,const char *); + } + va_end(sections); + return defval; +} + +std::string plConfigInfo::GetValueIn(const std::string & key, const std::string & defval, bool * outFound, const std::vector & sections ) const +{ + if (outFound) *outFound=false; + for ( int i=0; i & sections ) const +{ + if (outFound) *outFound=false; + for ( int i=0; i & sections ) const +{ + if (outFound) *outFound=false; + for ( int i=0; i plConfigInfo::GetAllValuesIn(const std::string & key, const char * section1, ...) +{ + const char * section = section1; + va_list sections; + va_start(sections,section1); + std::vector result; + while (section) + { + if (HasSection(section)) + { + plKeysAndValues & kv = fSections[section]; + if (kv.HasKey(key)) + { + std::vector values = kv.GetAllValues(key); + result.insert(result.end(),values.begin(),values.end()); + } + } + section = va_arg(sections,const char *); + } + va_end(sections); + return result; +} + +bool plConfigInfo::GetSectionIterators(Sections::const_iterator & iter, Sections::const_iterator & end) const +{ + iter = fSections.begin(); + end = fSections.end(); + return true; +} + +bool plConfigInfo::GetKeyIterators(const xtl::istring & section, Keys::const_iterator & iter, Keys::const_iterator & end) const +{ + Sections::const_iterator si = fSections.find(section); + if (si==fSections.end()) + return false; + return fSections[section].GetKeyIterators(iter, end); +} + +bool plConfigInfo::GetValueIterators(const xtl::istring & section, const xtl::istring & key, Values::const_iterator & iter, Values::const_iterator & end) const +{ + Sections::const_iterator si = fSections.find(section); + if (si==fSections.end()) + return false; + return fSections[section].GetValueIterators(key, iter, end); +} + +bool plConfigInfo::ReadFrom(plConfigSource * src, KAddValueMode mode) +{ + return src->ReadInto(*this,mode); +} + +bool plConfigInfo::WriteTo(plConfigSource * src) +{ + return src->WriteOutOf(*this); +} + + +//////////////////////////////////////////////// + +void plConfigSource::SplitAt(std::string & key, std::string & value, char splitter, std::string & in) +{ + if(in.length() == 0) + return; + + int t = in.find(splitter); + if(t == std::string::npos) + { + key = in; + return; + } + + key.assign(in.substr(0,t)); + value.assign(in.substr(t+1,in.size()-t-1)); +} + + +bool plConfigSource::ReadString(const std::string & in) +{ + std::string work = in; + xtl::trim(work); + + // comment + if (work[0] == '#') + return true; + + // comment + if (work[0] == ';') + return true; + + // section + if (work[0] == '[') + { + int close = work.find_first_of("]"); + if(close == std::string::npos) + return false; + fCurrSection = work.substr(1,close-1); + fEffectiveSection = fCurrSection; + return true; + } + + // key=value + std::string key, value; + SplitAt(key, value, '=', work); + + // dot notation makes section change for this key=value only. + int t = key.find('.'); + if (t>0 && tAddValue(fEffectiveSection, key, value, fAddMode); +} + + +bool plConfigSource::ReadList(char ** l) +{ + while(*l != NULL) + { + ReadString(*l); + l++; + } + return true; +} + + +bool plConfigSource::ReadInto(plConfigInfo & configInfo, KAddValueMode mode) +{ + fConfigInfo = &configInfo; + fAddMode = mode; + return true; +} + + +bool plConfigSource::WriteOutOf(plConfigInfo & configInfo) +{ + fConfigInfo = &configInfo; + return true; +} + + +///////////////////////////////////////////////// + +plCmdLineConfigSource::plCmdLineConfigSource(int argc, char ** argv, const char * mySection) +: fArgc(argc) +, fArgv(argv) +, fMySection(mySection?mySection:"") +{} + + +bool plCmdLineConfigSource::ReadInto(plConfigInfo & configInfo, KAddValueMode mode) +{ + int argc = fArgc; + char ** argv = fArgv; + + if (!plConfigSource::ReadInto(configInfo, mode)) + return false; + + fCurrSection = fMySection; + fEffectiveSection = fCurrSection; + + if(argc < 1) + return true; + + fConfigInfo->AddValue(fEffectiveSection.c_str(), "ARGV0", *argv, fAddMode); + argc--; + argv++; + + while(argc > 0) + { + if(ReadString(*argv) != true) + { + // TODO: log error here + return false; + } + argv++; + argc--; + } + + return true; +} + +///////////////////////////////////////////////// + +plEnvConfigSource::plEnvConfigSource(char ** envp, const char * mySection) +: fEnvp(envp) +, fMySection(mySection?mySection:"") +{} + + +bool plEnvConfigSource::ReadInto(plConfigInfo & configInfo, KAddValueMode mode) +{ + if (!plConfigSource::ReadInto(configInfo, mode)) + return false; + + if (fEnvp != NULL) + { + fCurrSection = fMySection; + fEffectiveSection = fCurrSection; + return ReadList(fEnvp); + } + + return true; +} + + +///////////////////////////////////////////////// + +plIniConfigSource::plIniConfigSource(const char * iniFileName) +: fFileName(iniFileName) +{} + + +bool plIniConfigSource::ReadInto(plConfigInfo & configInfo, KAddValueMode mode) +{ + if (!plConfigSource::ReadInto(configInfo, mode)) + return false; + + fCurrSection = plConfigInfo::GlobalSection(); + fEffectiveSection = fCurrSection; + + if(fFileName.size() < 2) + return false; + + + std::ifstream file; + file.open(fFileName.c_str()); + + if(!file.is_open()) + { + // TODO log error here + return false; + } + + char buf[4096]; + + while (!file.eof()) + { + file.getline(buf, 4096); + + if(!ReadString(buf)) + { + // TODO log warning here + } + } + file.close(); + + return true; +} + +bool plIniConfigSource::WriteOutOf(plConfigInfo & configInfo) +{ + if (!plConfigSource::WriteOutOf(configInfo)) + return false; + + std::ofstream file; + file.open(fFileName.c_str()); + + if(!file.is_open()) + { + // TODO log error here + return false; + } + + file + << "# This is an auto-generated file." << std::endl + << std::endl + ; + + plConfigInfo::Sections::const_iterator si, se; + plConfigInfo::Keys::const_iterator ki, ke; + plConfigInfo::Values::const_iterator vi, ve; + + fConfigInfo->GetSectionIterators(si,se); + for (si; si!=se; ++si) + { + file << std::endl << "[" << si->first.c_str() << "]"<< std::endl; + if (fConfigInfo->GetKeyIterators(si->first, ki, ke)) + for (ki; ki!=ke; ++ki) + { + if (fConfigInfo->GetValueIterators(si->first, ki->first, vi, ve)) + for (vi; vi!=ve; ++vi) + { + file << ki->first.c_str() << "=" << vi->c_str() << std::endl; + } + } + } + + file.close(); + + return true; +} + + +///////////////////////////////////////////////// + +plIniStreamConfigSource::plIniStreamConfigSource(hsStream * stream) +: fStream(stream) +{} + + +bool plIniStreamConfigSource::ReadInto(plConfigInfo & configInfo, KAddValueMode mode) +{ + if (!plConfigSource::ReadInto(configInfo, mode)) + return false; + + fCurrSection = "global"; + fEffectiveSection = fCurrSection; + + if ( !fStream ) + return false; + + char buf[4096]; + + while (!fStream->AtEnd()) + { + fStream->ReadLn( buf, sizeof(buf) ); + + if(!ReadString(buf)) + { + // TODO log warning here + } + } + + return true; +} + +bool plIniStreamConfigSource::WriteOutOf(plConfigInfo & configInfo) +{ + if (!plConfigSource::WriteOutOf(configInfo)) + return false; + + if ( !fStream ) + return false; + + std::stringstream ss; + + plConfigInfo::Sections::const_iterator si, se; + plConfigInfo::Keys::const_iterator ki, ke; + plConfigInfo::Values::const_iterator vi, ve; + + fConfigInfo->GetSectionIterators(si,se); + for (si; si!=se; ++si) + { + ss << std::endl << "[" << si->first.c_str() << "]"<< std::endl; + if (fConfigInfo->GetKeyIterators(si->first, ki, ke)) + for (ki; ki!=ke; ++ki) + { + if (fConfigInfo->GetValueIterators(si->first, ki->first, vi, ve)) + for (vi; vi!=ve; ++vi) + { + ss << ki->first.c_str() << "=" << vi->c_str() << std::endl; + } + } + } + + fStream->WriteString( ss.str().c_str() ); + + return true; +} + + +///////////////////////////////////////////////// + +plIniSectionConfigSource::plIniSectionConfigSource(const char * iniFileName, std::vector & sections) +: plIniConfigSource(iniFileName) +{ + for (int i=0; i::iterator ii = std::find(fSections.begin(), fSections.end(), fCurrSection.c_str()); + + if (ii==fSections.end()) + return true; + + xtl::trim(key); + xtl::trim(value); + xtl::trim(value,"\"'"); + + if (key.size() == 0) + return true; + + if (key == "section") + fSections.push_back(value.c_str()); + + return fConfigInfo->AddValue(fEffectiveSection, key, value, fAddMode); +} + + +bool plIniSectionConfigSource::ReadSubSource( const char * name ) +{ + std::vector sections; + for ( int i=0; iReadFrom(&plIniSectionConfigSource( name, sections )); +} + +///////////////////////////////////////////////// + +plIniNoSectionsConfigSource::plIniNoSectionsConfigSource(const char * filename) +: fFileName(filename) +{ + fEffectiveSection = fCurrSection = ""; +} + +bool plIniNoSectionsConfigSource::ReadString(const std::string & in) +{ + std::string work = in; + xtl::trim(work); + + // ignore comments + if (work[0]=='#' || work[0]==';') + return true; + + // ignore sections + if (work[0] == '[') + return true; + + // parse key value + std::string key, value; + SplitAt(key, value, '=', work); + + return ReadPair(key, value); +} + +bool plIniNoSectionsConfigSource::ReadInto(plConfigInfo & configInfo, KAddValueMode mode) +{ + if (!plConfigSource::ReadInto(configInfo, mode)) + return false; + + if(fFileName.size() < 2) + return false; + + std::ifstream file; + file.open(fFileName.c_str()); + + if(!file.is_open()) + { + // TODO log error here + return false; + } + + char buf[4096]; + + while (!file.eof()) + { + file.getline(buf, 4096); + + if(!ReadString(buf)) + { + // TODO log warning here + } + } + file.close(); + + return true; +} + + +bool plIniNoSectionsConfigSource::WriteOutOf(plConfigInfo & configInfo) +{ + if (!plConfigSource::WriteOutOf(configInfo)) + return false; + + std::ofstream file; + file.open(fFileName.c_str()); + + if(!file.is_open()) + { + // TODO log error here + return false; + } + + file + << "# This is an auto-generated file." << std::endl + << std::endl + ; + + plConfigInfo::Sections::const_iterator si, se; + plConfigInfo::Keys::const_iterator ki, ke; + plConfigInfo::Values::const_iterator vi, ve; + + fConfigInfo->GetSectionIterators(si,se); + for (si; si!=se; ++si) + { + if (fConfigInfo->GetKeyIterators(si->first, ki, ke)) + for (ki; ki!=ke; ++ki) + { + if (fConfigInfo->GetValueIterators(si->first, ki->first, vi, ve)) + for (vi; vi!=ve; ++vi) + { + file << ki->first.c_str() << "=" << vi->c_str() << std::endl; + } + } + } + + file.close(); + + return true; +} + + +///////////////////////////////////////////////// + +bool plDebugConfigSource::WriteOutOf(plConfigInfo & configInfo) +{ + if (!plConfigSource::WriteOutOf(configInfo)) + return false; + + plConfigInfo::Sections::const_iterator si, se; + plConfigInfo::Keys::const_iterator ki, ke; + plConfigInfo::Values::const_iterator vi, ve; + + char buf[1024]; + fConfigInfo->GetSectionIterators(si,se); + for (si; si!=se; ++si) + { + sprintf(buf,"\n[%s]\n",si->first.c_str()); + hsStatusMessage(buf); + if (fConfigInfo->GetKeyIterators(si->first, ki, ke)) + for (ki; ki!=ke; ++ki) + { + if (fConfigInfo->GetValueIterators(si->first, ki->first, vi, ve)) + for (vi; vi!=ve; ++vi) + { + sprintf(buf,"%s=%s\n",ki->first.c_str(),vi->c_str()); + hsStatusMessage(buf); + } + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// + +void plConfigValueBase::ConfigRead(plConfigInfo * opts) +{ + if (fReadEvaluate()) + { + std::string value; + bool found; + value = opts->GetValue(GetConfigGroup(),GetConfigName(),"",&found); + if (found) + SetValue(fReadModify(value).c_str()); + } +} + +void plConfigValueBase::ConfigWrite(plConfigInfo * opts) +{ + if (fWriteEvaluate()) + { + opts->AddValue(GetConfigGroup(),GetConfigName(),fWriteModify(GetValue()),kAlwaysAdd); + } +} + +void plConfigValueBase::SetValue(const char * value) +{ + ISetValue(fSetModify(value).c_str()); +} + +std::string plConfigValueBase::GetValue() const +{ + return fGetModify(IGetValue()); +} + +void plConfigValueBase::SetReadEvaluate(plClass * targetObj, TEvaluate evalFunc) +{ + fReadEvaluate = plEvaluate(targetObj,evalFunc); +} + +void plConfigValueBase::SetWriteEvaluate(plClass * targetObj, TEvaluate evalFunc) +{ + fWriteEvaluate = plEvaluate(targetObj,evalFunc); +} + +void plConfigValueBase::SetWriteEvaluate(plClass * targetObj, TEvaluateConst evalFunc) +{ + fWriteEvaluate = plEvaluate(targetObj,evalFunc); +} + +void plConfigValueBase::SetReadModify(plClass * targetObj, TModify modifyFunc) +{ + fReadModify = plModify(targetObj,modifyFunc); +} + +void plConfigValueBase::SetWriteModify(plClass * targetObj, TModify modifyFunc) +{ + fWriteModify = plModify(targetObj,modifyFunc); +} + +void plConfigValueBase::SetGetModify(plClass * targetObj, TModify modifyFunc) +{ + fGetModify = plModify(targetObj,modifyFunc); +} + +void plConfigValueBase::SetSetModify(plClass * targetObj, TModify modifyFunc) +{ + fSetModify = plModify(targetObj,modifyFunc); +} + + +//////////////////////////////////////////////////////////////////// + +plConfigGroup::plConfigGroup(const char * groupName) +: fGroupName(groupName) +{} + +bool plConfigGroup::Read(plConfigSource * src) +{ + if (!fOpts.ReadFrom(src)) + return false; + for (int i=0; iConfigRead(&fOpts); + return true; +} + +bool plConfigGroup::Write(plConfigSource * src) +{ + for (int i=0; iConfigWrite(&fOpts); + return fOpts.WriteTo(src); +} + +void plConfigGroup::AddItem(plConfigValueBase * item, const char * name) +{ + item->SetConfigGroup(fGroupName.c_str()); + if (name) + item->SetConfigName(name); + fItems.push_back(item); +} + +//////////////////////////////////////////////////////////////////// + +plConfigAggregateValue::plConfigAggregateValue( + const char * name, + plConfigValueBase * item1, + plConfigValueBase * item2, + plConfigValueBase * item3, + plConfigValueBase * item4, + plConfigValueBase * item5, + plConfigValueBase * item6, + plConfigValueBase * item7) +{ + SetConfigName(name); + AddItems(item1,item2,item3,item4,item5,item6,item7); +} + +void plConfigAggregateValue::AddItems( + plConfigValueBase * item1, + plConfigValueBase * item2, + plConfigValueBase * item3, + plConfigValueBase * item4, + plConfigValueBase * item5, + plConfigValueBase * item6, + plConfigValueBase * item7) +{ + fItems.clear(); + if (item1) AddItem(item1); + if (item2) AddItem(item2); + if (item3) AddItem(item3); + if (item4) AddItem(item4); + if (item5) AddItem(item5); + if (item6) AddItem(item6); + if (item7) AddItem(item7); +} + +void plConfigAggregateValue::ISetValue(const char * value) +{ + std::string work = value; + int p=0,i=0; + do + { + xtl::trim(work); + p = work.find(" "); + fItems[i]->SetValue(work.substr(0,p).c_str()); + work.erase(0,p); + i++; + } while (iGetValue()); + value.append(" "); + } + return xtl::trim(value); +} + +void plConfigAggregateValue::AddItem(plConfigValueBase * item) +{ + fItems.push_back(item); +} + +//////////////////////////////////////////////////////////////////// + +plWWWAuthenticateConfigSource::plWWWAuthenticateConfigSource(const std::string& auth) +: fAuth(auth) +{ + fEffectiveSection = fCurrSection = ""; +} + +bool plWWWAuthenticateConfigSource::ReadInto(plConfigInfo & configInfo, KAddValueMode mode) +{ + if (!plConfigSource::ReadInto(configInfo, mode)) + return false; + + fCurrSection = "global"; + fEffectiveSection = fCurrSection; + + unsigned int i = 0; + + while (i < fAuth.size()) + { + bool inQuote = false; + unsigned int begin = i,end; + while (i < fAuth.size() + && ((fAuth[i] != ',' && !inQuote) || inQuote)) + { + if (fAuth[i] == '"') + inQuote = ! inQuote; + i++; + } + end = i; + + std::string buf; + buf.assign(fAuth,begin,end-begin); + if(!ReadString(buf.c_str())) + { + // TODO log warning here + } + i++; + } + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfo.h new file mode 100644 index 00000000..9a693b7a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfo.h @@ -0,0 +1,481 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plConfigInfo_h_inc +#define plConfigInfo_h_inc + +#include "plKeysAndValues.h" +#include "hsStlUtils.h" +#include + +///////////////////////////////////////////////// + +typedef std::vector plStringList; +typedef std::vector plWStringList; + +///////////////////////////////////////////////// + +class plConfigSource; +class plConfigInfo +{ +public: + typedef plKeysAndValues::Keys + Keys; + typedef plKeysAndValues::Values + Values; + typedef std::map + Sections; + + static const std::string& GlobalSection(); + +private: + mutable Sections fSections; + +public: + plConfigInfo(); + plConfigInfo(const plConfigInfo & src); + virtual ~plConfigInfo() {} + plConfigInfo & operator =(const plConfigInfo & src); + + // REMOVE + // remove all sections + void Clear(); + // remove section + void RemoveSection(const std::string & section); + // remove key from section + void RemoveKey(const std::string & section, const std::string & key); + // QUERY + // does this section exist? + bool HasSection(const std::string & section) const; + // does the given section contain this key? + bool HasKey(const std::string & section, const std::string & key); + // does any section contain this key? + bool HasKeyAny(const std::string & key); + // does any of the given sections contain this key? + bool HasKeyIn(const std::string & key, const char * section1, ... /*, nil*/); + bool HasKeyIn(const std::string & key, const std::vector & sections ); + // does key in section have this value? + bool KeyHasValue(const std::string & section, const std::string & key, const std::string & value); + bool KeyHasValue(const std::string & section, const std::string & key, int value); + bool KeyHasValue(const std::string & section, const std::string & key, double value); + // ADD + // add key=value to the section + bool AddValue(const std::string & section, const std::string & key, const std::string & value, KAddValueMode mode=kAlwaysAdd); + bool AddValue(const std::string & section, const std::string & key, int value, KAddValueMode mode=kAlwaysAdd); + bool AddValue(const std::string & section, const std::string & key, double value, KAddValueMode mode=kAlwaysAdd); + bool AddValues(const std::string & section, const std::string & key, const std::vector & values, KAddValueMode mode=kAlwaysAdd); + // GET + plKeysAndValues GetSection(const std::string & section, bool & found); + std::vector GetSectionNames(); + // get value for key from given section + std::string GetValue(const std::string & section, const std::string & key, const std::string & defval="", bool * outFound=nil) const; + int GetValue(const std::string & section, const std::string & key, int defval, bool * outFound=nil) const; + double GetValue(const std::string & section, const std::string & key, double defval, bool * outFound=nil) const; + std::vector GetAllValues(const std::string & section, const std::string & key) const; + // get value for key from any section + std::string GetValueAny(const std::string & key, const std::string & defval="", bool * outFound=nil) const; + int GetValueAny(const std::string & key, int defval, bool * outFound=nil) const; + double GetValueAny(const std::string & key, double defval, bool * outFound=nil) const; + std::vector GetAllValuesAny(const std::string & key) const; + // get value for key from one of the given sections + std::string GetValueIn(const std::string & key, const std::string & defval, bool * outFound, const char * section1, ... /*, nil*/) const; + std::string GetValueIn(const std::string & key, const std::string & defval, bool * outFound, const std::vector & sections ) const; + int GetValueIn(const std::string & key, int defval, bool * outFound, const char * section1, ... /*, nil*/) const; + int GetValueIn(const std::string & key, int defval, bool * outFound, const std::vector & sections ) const; + double GetValueIn(const std::string & key, double defval, bool * outFound, const char * section1, ... /*, nil*/) const; + double GetValueIn(const std::string & key, double defval, bool * outFound, const std::vector & sections ) const; + std::vector GetAllValuesIn(const std::string & key, const char * section1, ... /*, nil*/); + // ITERATORS + bool GetSectionIterators(Sections::const_iterator & iter, Sections::const_iterator & end) const; + bool GetKeyIterators(const xtl::istring & section, Keys::const_iterator & iter, Keys::const_iterator & end) const; + bool GetValueIterators(const xtl::istring & section, const xtl::istring & key, Values::const_iterator & iter, Values::const_iterator & end) const; + // CONFIG SOURCE + virtual bool ReadFrom(plConfigSource * src, KAddValueMode mode=kAlwaysAdd); + virtual bool WriteTo(plConfigSource * src); +}; + +class plConfigInfoLogging +{ +private: + plConfigInfo fConfigInfo; + + plConfigInfo fLog; +public: + plConfigInfoLogging(); + ~plConfigInfoLogging(); + + plConfigInfo* GetConfigInfo() { return &fConfigInfo; } + plConfigInfo* GetConfigInfoLog() { return &fLog; } + + bool GetValue(std::string& retval, const std::string & section, const std::string & key, const std::string & desc, const std::string& defval = ""); + bool GetValue(int& retval, const std::string & section, const std::string & key, const std::string & desc, int defval); + bool GetValue(bool& retval, const std::string & section, const std::string & key, const std::string & desc, bool defval); + bool GetValue(float& retval, const std::string & section, const std::string & key, const std::string & desc, float defval); + bool GetValue(double& retval, const std::string & section, const std::string & key, const std::string & desc, double defval); + bool GetAllValues(std::vector& values, const std::string & section, const std::string & key, const std::string & desc); + +#if USE_MULT_SECTIONS + // get value for key from any section + bool GetValueAny(std::string& retval, const std::string & key, const std::string & desc, const std::string & defval); + bool GetValueAny(int &retval, const std::string & key, const std::string & desc, int defval); + bool GetValueAny(bool& retval, const std::string & key, const std::string & desc, bool defval); + bool GetValueAny(float& retval, const std::string & key, const std::string & desc, float defval); + bool GetValueAny(double& retval, const std::string & key, const std::string & desc, double defval); + bool GetAllValuesAny(std::vector& values, const std::string & key, const std::string & desc); + + // get value for key from one of the given sections + bool GetValueIn(std::string& retval, const std::string & key, const std::string & desc, const std::string & defval, const char * section1, ... /*, nil*/); + bool GetValueIn(std::string& retval, const std::string & key, const std::string & desc, const std::string & defval, std::vector & sections ); + bool GetValueIn(int& retval, const std::string & key, const std::string & desc, int defval, const char * section1, ... /*, nil*/); + bool GetValueIn(int& retval, const std::string & key, const std::string & desc, int defval, std::vector & sections ); + bool GetValueIn(bool& retval, const std::string & key, const std::string & desc, bool defval, const char * section1, ... /*, nil*/); + bool GetValueIn(bool& retval, const std::string & key, const std::string & desc, bool defval, std::vector & sections ); + bool GetValueIn(float& retval, const std::string & key, const std::string & desc, double defval, const char * section1, ... /*, nil*/); + bool GetValueIn(float& retval, const std::string & key, const std::string & desc, double defval, std::vector & sections ); + bool GetValueIn(double& retval, const std::string & key, const std::string & desc, double defval, const char * section1, ... /*, nil*/); + bool GetValueIn(double& retval, const std::string & key, const std::string & desc, double defval, std::vector & sections ); +#endif +}; + +///////////////////////////////////////////////// + +class plConfigSource +{ +protected: + std::string fCurrSection; // used in parsing + std::string fEffectiveSection; // used in parsing + KAddValueMode fAddMode; // used in parsing + plConfigInfo * fConfigInfo; + + void SplitAt(std::string & key, std::string & value, char splitter, std::string & in); + virtual bool ReadString(const std::string & in); + virtual bool ReadPair(std::string & key, std::string & value); + virtual bool ReadList(char ** list); + virtual bool ReadSubSource( const char * name ) { return true; } + +protected: + virtual bool ReadInto(plConfigInfo & configInfo, KAddValueMode mode=kAlwaysAdd); + virtual bool WriteOutOf(plConfigInfo & configInfo); + +public: + plConfigSource() {} + virtual ~plConfigSource() {} + + friend class plConfigInfo; +}; + + +///////////////////////////////////////////////// + +class plCmdLineConfigSource : public plConfigSource +{ + int fArgc; + char ** fArgv; + std::string fMySection; +protected: + bool ReadInto(plConfigInfo & configInfo, KAddValueMode mode=kAlwaysAdd); +public: + plCmdLineConfigSource(int argc, char ** argv, const char * mySection="CmdLine"); +}; + + +///////////////////////////////////////////////// + +class plEnvConfigSource : public plConfigSource +{ + char ** fEnvp; + std::string fMySection; +protected: + bool ReadInto(plConfigInfo & configInfo, KAddValueMode mode=kAlwaysAdd); +public: + plEnvConfigSource(char ** envp, const char * mySection="Environment"); +}; + + +///////////////////////////////////////////////// + +class plIniConfigSource : public plConfigSource +{ +protected: + std::string fFileName; + bool ReadInto(plConfigInfo & configInfo, KAddValueMode mode=kAlwaysAdd); + bool WriteOutOf(plConfigInfo & configInfo); +public: + plIniConfigSource(const char * iniFileName); +}; + +///////////////////////////////////////////////// +// just like above, but works with hsStream-derived classes + +class hsStream; +class plIniStreamConfigSource : public plConfigSource +{ +protected: + hsStream * fStream; + bool ReadInto(plConfigInfo & configInfo, KAddValueMode mode=kAlwaysAdd); + bool WriteOutOf(plConfigInfo & configInfo); +public: + plIniStreamConfigSource(hsStream * stream); +}; + +///////////////////////////////////////////////// + +// an ini file reader/writer that ignores section headers and puts all +// data in an unnamed section, or more accurately, in a section named "". +class plIniNoSectionsConfigSource : public plConfigSource +{ + std::string fFileName; +protected: + bool ReadString(const std::string & in); + bool ReadInto(plConfigInfo & configInfo, KAddValueMode mode=kAlwaysAdd); + bool WriteOutOf(plConfigInfo & configInfo); +public: + plIniNoSectionsConfigSource(const char * iniFileName); +}; + +///////////////////////////////////////////////// + +// an ini file reader that only reads specified sections +class plIniSectionConfigSource : public plIniConfigSource +{ + typedef std::vector + Sections; +protected: + Sections fSections; + bool ReadPair(std::string & key, std::string & value); + bool ReadSubSource( const char * name ); +public: + plIniSectionConfigSource(const char * iniFileName, std::vector & sections); +}; + +///////////////////////////////////////////////// + +// TODO: This would be a cool thing. not needed right now though. +class plDatabaseConfigSource : public plConfigSource +{ +protected: + bool ReadInto(plConfigInfo & configInfo, KAddValueMode mode=kAlwaysAdd); + bool WriteOutOf(plConfigInfo & configInfo); +public: + plDatabaseConfigSource(const char * connectString); +}; + + +///////////////////////////////////////////////// + +class plDebugConfigSource : public plConfigSource +{ +protected: + std::string fFileName; + bool WriteOutOf(plConfigInfo & configInfo); +public: + plDebugConfigSource(){} +}; + +///////////////////////////////////////////////// + +class plWWWAuthenticateConfigSource : public plConfigSource +{ + const std::string& fAuth; +protected: + bool ReadInto(plConfigInfo & configInfo, KAddValueMode mode=kAlwaysAdd); +public: + plWWWAuthenticateConfigSource(const std::string& auth); +}; + +//////////////////////////////////////////////////////////////////// + +// NOTE: plClass _must_ appear first in a multiple-inheritance list. +class plClass +{ +public: + virtual void Unused(){} +}; + +//////////////////////////////////////////////////////////////////// + +typedef bool (plClass::*TEvaluate)(); +typedef bool (plClass::*TEvaluateConst)() const; +struct plEvaluate +{ + plClass * fTarget; + bool (plClass::*fEvaluate)(); + bool (plClass::*fEvaluateConst)() const; + plEvaluate( plClass * target=nil, TEvaluate evaluate=nil ) + : fTarget(target) + , fEvaluate(evaluate) + , fEvaluateConst(nil) + {} + plEvaluate( plClass * target, TEvaluateConst evaluate ) + : fTarget(target) + , fEvaluateConst(evaluate) + , fEvaluate(nil) + {} + bool operator()() + { + if (fEvaluate) + return (fTarget)?(fTarget->*fEvaluate)():true; + else + if (fEvaluateConst) + return (fTarget)?(fTarget->*fEvaluateConst)():true; + else + return true; + } +}; + +//////////////////////////////////////////////////////////////////// + +typedef std::string (plClass::*TModify)(const std::string & value); + +struct plModify +{ + plClass * fTarget; + std::string (plClass::*fModify)(const std::string & value); + plModify( plClass * target=nil, TModify modify=nil ) + : fTarget(target) + , fModify(modify) + {} + std::string operator()(const std::string & value) { return (fTarget)?(fTarget->*fModify)(value):value;} + std::string operator()(const std::string & value) const { return (fTarget)?(fTarget->*fModify)(value):value;} +}; + +//////////////////////////////////////////////////////////////////// + +class plConfigValueBase +{ +public: + std::string fConfigName; + std::string fConfigGroup; + plEvaluate fReadEvaluate; // returns true if we want to read this value from options + plEvaluate fWriteEvaluate; // returns true if we want to write this value to options + plModify fReadModify; // may modify the value being read from options + plModify fWriteModify; // may modify the value being written to options + plModify fGetModify; // may modify the value when being assigned + plModify fSetModify; // may modify the value when being accessed + plConfigValueBase( const char * configName="", const char * configGroup="" ) + : fConfigName(configName) + , fConfigGroup(configGroup) + {} + void SetConfigName(const char * name) { fConfigName=(name)?name:"";} + std::string GetConfigName() const { return fConfigName;} + void SetConfigGroup(const char * group) { fConfigGroup=group;} + std::string GetConfigGroup() const { return fConfigGroup;} + bool HasConfigName() { return fConfigName.length()>0;} + bool HasConfigGroup() { return fConfigGroup.length()>0;} + virtual void ConfigRead(plConfigInfo * opts); + virtual void ConfigWrite(plConfigInfo * opts); + void SetValue(const char * value); + std::string GetValue() const; + virtual void ISetValue(const char * value) = 0; + virtual std::string IGetValue() const = 0; + + void SetReadEvaluate(plClass * targetObj, TEvaluate evalFunc); + void SetWriteEvaluate(plClass * targetObj, TEvaluate evalFunc); + void SetWriteEvaluate(plClass * targetObj, TEvaluateConst evalFunc); + void SetReadModify(plClass * targetObj, TModify modifyFunc); + void SetWriteModify(plClass * targetObj, TModify modifyFunc); + void SetGetModify(plClass * targetObj, TModify modifyFunc); + void SetSetModify(plClass * targetObj, TModify modifyFunc); +}; + +//////////////////////////////////////////////////////////////////// + +class plConfigValue : public plConfigValueBase +{ +public: + plConfigValue( const char * configName="", const char * configGroup="" ) + : plConfigValueBase(configName, configGroup) + {} + std::string fConfigValue; + void ISetValue(const char * value) { fConfigValue=value;} + std::string IGetValue() const { return fConfigValue;} +}; + +//////////////////////////////////////////////////////////////////// + +class plConfigAggregateValue : public plConfigValueBase +{ +public: + std::vector fItems; + plConfigAggregateValue( + const char * name=nil, + // A vararg here would not work because of a + // multiple inheritance issue. Classes that are + // plConfigValueBase are likely to be derived from + // plClass also. Since plClass must be first in + // the inheritance list, the vararg would not + // point to the plConfigValueBase vtable that we + // need to access. + plConfigValueBase * item1=nil, + plConfigValueBase * item2=nil, + plConfigValueBase * item3=nil, + plConfigValueBase * item4=nil, + plConfigValueBase * item5=nil, + plConfigValueBase * item6=nil, + plConfigValueBase * item7=nil); + void ISetValue(const char * value); + std::string IGetValue() const; + void AddItem(plConfigValueBase * item); + void AddItems( + plConfigValueBase * item1=nil, + plConfigValueBase * item2=nil, + plConfigValueBase * item3=nil, + plConfigValueBase * item4=nil, + plConfigValueBase * item5=nil, + plConfigValueBase * item6=nil, + plConfigValueBase * item7=nil); +}; + +//////////////////////////////////////////////////////////////////// + +class plConfigValueProxy : public plConfigValueBase +{ + plConfigValueBase * fConfigurable; +public: + plConfigValueProxy(plConfigValueBase * item=nil) + : fConfigurable(item) + {} + void Set(plConfigValueBase * item) { fConfigurable=item;} + void ISetValue(const char * value) { fConfigurable->ISetValue(value);} + std::string IGetValue() const { return fConfigurable->IGetValue();} +}; + +//////////////////////////////////////////////////////////////////// + +class plConfigGroup +{ +public: + plConfigInfo fOpts; + std::string fGroupName; + std::vector fItems; + plConfigGroup(const char * groupName=""); + bool Read(plConfigSource * src); + bool Write(plConfigSource * src); + void AddItem(plConfigValueBase * item, const char * name=nil); +}; + +//////////////////////////////////////////////////////////////////// + +#endif // plConfigInfo_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfoLogging.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfoLogging.cpp new file mode 100644 index 00000000..651bb7ab --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plConfigInfoLogging.cpp @@ -0,0 +1,307 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plConfigInfo.h" + +plConfigInfoLogging::plConfigInfoLogging() +{ +} + +plConfigInfoLogging::~plConfigInfoLogging() +{ +} + +bool plConfigInfoLogging::GetValue(std::string& retval, const std::string & section, const std::string & key, const std::string & desc, const std::string& defval) +{ + std::string descwdef; + xtl::format(descwdef,"%s # %s",defval.c_str(),desc.c_str()); + fLog.AddValue(section,key,descwdef,kReplaceIfExists); + + bool found; + retval = fConfigInfo.GetValue(section,key,defval,&found); + return found; +} + +bool plConfigInfoLogging::GetValue(int& retval, const std::string & section, const std::string & key, const std::string & desc, int defval) +{ + std::string descwdef; + xtl::format(descwdef,"%d # %s",defval,desc.c_str()); + fLog.AddValue(section,key,descwdef,kReplaceIfExists); + + bool found; + retval = fConfigInfo.GetValue(section,key,defval,&found); + return found; +} + +bool plConfigInfoLogging::GetValue(bool& retval, const std::string & section, const std::string & key, const std::string & desc, bool defval) +{ + std::string descwdef; + xtl::format(descwdef,"%d # %s",defval,desc.c_str()); + fLog.AddValue(section,key,descwdef,kReplaceIfExists); + + bool found; + retval = ( fConfigInfo.GetValue(section,key,(int)defval,&found)!=0 ); + return found; +} + +bool plConfigInfoLogging::GetValue(float& retval, const std::string & section, const std::string & key, const std::string & desc, float defval) +{ + std::string descwdef; + xtl::format(descwdef,"%f # %s",defval,desc.c_str()); + fLog.AddValue(section,key,descwdef,kReplaceIfExists); + + bool found; + double retvald = fConfigInfo.GetValue(section,key,defval,&found); + retval = (float)retvald; + return found; +} + +bool plConfigInfoLogging::GetValue(double& retval, const std::string & section, const std::string & key, const std::string & desc, double defval) +{ + std::string descwdef; + xtl::format(descwdef,"%f # %s",defval,desc.c_str()); + fLog.AddValue(section,key,descwdef,kReplaceIfExists); + + bool found; + retval = fConfigInfo.GetValue(section,key,defval,&found); + return found; +} + +bool plConfigInfoLogging::GetAllValues(std::vector& values, const std::string & section, const std::string & key, const std::string & desc) +{ + std::string descwdef; + xtl::format(descwdef,"%s # %s","\"Multiple Entries\"",desc.c_str()); + fLog.AddValue(section,key,descwdef,kReplaceIfExists); + + values = fConfigInfo.GetAllValues(section,key); + return values.size() != 0; +} + +#if USE_MULT_SECTIONS + +bool plConfigInfoLogging::GetValueAny(std::string& retval, const std::string & key, const std::string & desc, const std::string & defval) +{ + fLog.AddValue("ANY SECTION",key,desc,kReplaceIfExists); + + bool found; + retval = fConfigInfo.GetValueAny(key,defval,&found); + return found; +} + +bool plConfigInfoLogging::GetValueAny(int &retval, const std::string & key, const std::string & desc, int defval) +{ + fLog.AddValue("ANY SECTION",key,desc,kReplaceIfExists); + + bool found; + retval = fConfigInfo.GetValueAny(key,defval,&found); + return found; +} + +bool plConfigInfoLogging::GetValueAny(bool &retval, const std::string & key, const std::string & desc, bool defval) +{ + fLog.AddValue("ANY SECTION",key,desc,kReplaceIfExists); + + bool found; + retval = ( fConfigInfo.GetValueAny(key,(int)defval,&found)!=0 ); + return found; +} + +bool plConfigInfoLogging::GetValueAny(float& retval, const std::string & key, const std::string & desc, float defval) +{ + fLog.AddValue("ANY SECTION",key,desc,kReplaceIfExists); + + bool found; + retval = fConfigInfo.GetValueAny(key,defval,&found); + return found; +} + +bool plConfigInfoLogging::GetValueAny(double& retval, const std::string & key, const std::string & desc, double defval) +{ + fLog.AddValue("ANY SECTION",key,desc,kReplaceIfExists); + + bool found; + retval = fConfigInfo.GetValueAny(key,defval,&found); + return found; +} + +bool plConfigInfoLogging::GetAllValuesAny(std::vector& values, const std::string & key, const std::string & desc) +{ + fLog.AddValue("ANY SECTION",key,desc,kReplaceIfExists); + + values = fConfigInfo.GetAllValuesAny(key); + return values.size() != 0; +} + +bool plConfigInfoLogging::GetValueIn(std::string& retval, const std::string & key, const std::string & desc, const std::string & defval, const char * section1, ... /*, nil*/) +{ + const char * section = section1; + va_list va; + va_start(va,section1); + std::vector sections; + while (section) + { + sections.push_back( section ); + section = va_arg(va,const char *); + } + va_end(va); + + return GetValueIn(retval,key,desc,defval,sections); +} + +bool plConfigInfoLogging::GetValueIn(std::string& retval, const std::string & key, const std::string & desc, const std::string & defval, std::vector & sections ) +{ + std::vector::iterator si = sections.begin(); + while (si != sections.end()) + { + fLog.AddValue(*si,key,desc,kReplaceIfExists); + si++; + } + + bool found; + retval = fConfigInfo.GetValueIn(key,defval,&found,sections); + return found; +} + +bool plConfigInfoLogging::GetValueIn(int& retval, const std::string & key, const std::string & desc, int defval, const char * section1, ... /*, nil*/) +{ + const char * section = section1; + va_list va; + va_start(va,section1); + std::vector sections; + while (section) + { + sections.push_back( section ); + section = va_arg(va,const char *); + } + va_end(va); + + return GetValueIn(retval,key,desc,defval,sections); +} + +bool plConfigInfoLogging::GetValueIn(int& retval, const std::string & key, const std::string & desc, int defval, std::vector & sections ) +{ + std::vector::iterator si = sections.begin(); + while (si != sections.end()) + { + fLog.AddValue(*si,key,desc,kReplaceIfExists); + si++; + } + + bool found; + retval = fConfigInfo.GetValueIn(key,defval,&found,sections); + return found; +} + +bool plConfigInfoLogging::GetValueIn(bool& retval, const std::string & key, const std::string & desc, bool defval, const char * section1, ... /*, nil*/) +{ + const char * section = section1; + va_list va; + va_start(va,section1); + std::vector sections; + while (section) + { + sections.push_back( section ); + section = va_arg(va,const char *); + } + va_end(va); + + return GetValueIn(retval,key,desc,defval,sections); +} + +bool plConfigInfoLogging::GetValueIn(bool& retval, const std::string & key, const std::string & desc, bool defval, std::vector & sections ) +{ + std::vector::iterator si = sections.begin(); + while (si != sections.end()) + { + fLog.AddValue(*si,key,desc,kReplaceIfExists); + si++; + } + + bool found; + retval = ( fConfigInfo.GetValueIn(key,(int)defval,&found,sections)!=0 ); + return found; +} + +bool plConfigInfoLogging::GetValueIn(float& retval, const std::string & key, const std::string & desc, double defval, const char * section1, ... /*, nil*/) +{ + const char * section = section1; + va_list va; + va_start(va,section1); + std::vector sections; + while (section) + { + sections.push_back( section ); + section = va_arg(va,const char *); + } + va_end(va); + + return GetValueIn(retval,key,desc,defval,sections); +} + +bool plConfigInfoLogging::GetValueIn(float& retval, const std::string & key, const std::string & desc, double defval, std::vector & sections ) +{ + std::vector::iterator si = sections.begin(); + while (si != sections.end()) + { + fLog.AddValue(*si,key,desc,kReplaceIfExists); + si++; + } + + bool found; + retval = fConfigInfo.GetValueIn(key,defval,&found,sections); + return found; +} + +bool plConfigInfoLogging::GetValueIn(double& retval, const std::string & key, const std::string & desc, double defval, const char * section1, ... /*, nil*/) +{ + const char * section = section1; + va_list va; + va_start(va,section1); + std::vector sections; + while (section) + { + sections.push_back( section ); + section = va_arg(va,const char *); + } + va_end(va); + + return GetValueIn(retval,key,desc,defval,sections); +} + +bool plConfigInfoLogging::GetValueIn(double& retval, const std::string & key, const std::string & desc, double defval, std::vector & sections ) +{ + std::vector::iterator si = sections.begin(); + while (si != sections.end()) + { + fLog.AddValue(*si,key,desc,kReplaceIfExists); + si++; + } + + bool found; + retval = fConfigInfo.GetValueIn(key,defval,&found,sections); + return found; +} + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plContainer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plContainer.h new file mode 100644 index 00000000..86940a42 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plContainer.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plContainer_h_inc +#define plContainer_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" + +template < class T > +class plDataContainerT +{ + typedef std::map Items; + Items fItems; + UInt32 fNextKey; +public: + plDataContainerT() + : fNextKey( 1 ) + {} + UInt32 Add( T* data ) + { + UInt32 key = fNextKey; + fItems[ key ] = data; + fNextKey++; + return key; + } + bool Get( UInt32 key, T*& outItem, bool remove=true ) + { + outItem = nil; + Items::iterator ii = fItems.find( key ); + if ( ii==fItems.end() ) + return false; + outItem = ii->second; + if ( remove ) + fItems.erase( ii ); + return true; + } +}; + + +#endif // plContainer_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plKeysAndValues.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plKeysAndValues.cpp new file mode 100644 index 00000000..0fbb01a2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plKeysAndValues.cpp @@ -0,0 +1,263 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plKeysAndValues.h" +#include "hsStream.h" +#include + + +plKeysAndValues::plKeysAndValues() +{ +} + +plKeysAndValues::plKeysAndValues(const plKeysAndValues & src) +: fKeys(src.fKeys) +{ +} + +plKeysAndValues & plKeysAndValues::operator =(const plKeysAndValues & src) +{ + fKeys = src.fKeys; + return *this; +} + + +void plKeysAndValues::Clear() +{ + fKeys.clear(); +} + +void plKeysAndValues::RemoveKey(const std::string & key) +{ + fKeys.erase(key.c_str()); +} + +bool plKeysAndValues::HasKey(const std::string & key) const +{ + return (fKeys.find(key.c_str()) != fKeys.end()); +} + +bool plKeysAndValues::KeyHasValue(const std::string & key, const std::string & value) +{ + Keys::const_iterator ki = fKeys.find(key.c_str()); + if (ki==fKeys.end()) + return false; + return std::find(ki->second.begin(),ki->second.end(), value.c_str()) != ki->second.end(); +} + +bool plKeysAndValues::KeyHasValue(const std::string & key, int value) +{ + char buf[20]; + sprintf(buf, "%d", value); + std::string v(buf); + return KeyHasValue(key, v); +} + +bool plKeysAndValues::KeyHasValue(const std::string & key, double value) +{ + char buf[30]; + sprintf(buf, "%f", value); + std::string v(buf); + return KeyHasValue(key, v); +} + +bool plKeysAndValues::AddValue(const std::string & key, const std::string & value, KAddValueMode mode) +{ + switch (mode) + { + case kFailIfExists: + if (HasKey(key)) + return false; + break; + case kReplaceIfExists: + if (HasKey(key)) + RemoveKey(key); + break; + } + fKeys[key.c_str()].push_front(value.c_str()); + return true; +} + +bool plKeysAndValues::AddValue(const std::string & key, int value, KAddValueMode mode) +{ + char buf[20]; + sprintf(buf, "%d", value); + std::string v(buf); + return AddValue(key,v,mode); +} + +bool plKeysAndValues::AddValue(const std::string & key, double value, KAddValueMode mode) +{ + char buf[30]; + sprintf(buf, "%f", value); + std::string v(buf); + return AddValue(key,v,mode); +} + +bool plKeysAndValues::AddValues(const std::string & key, const std::vector & values, KAddValueMode mode) +{ + for (int i=0; isecond.front().c_str(); +// fKeys[key.c_str()].push_front(defval.c_str()); + return defval; +} + +UInt32 plKeysAndValues::GetValue(const std::string & key, UInt32 defval, bool * outFound) const +{ + char buf[20]; + sprintf(buf, "%ul", defval); + std::string v(buf); + return strtoul(GetValue(key,v,outFound).c_str(), nil, 0); +} + +int plKeysAndValues::GetValue(const std::string & key, int defval, bool * outFound) const +{ + char buf[20]; + sprintf(buf, "%d", defval); + std::string v(buf); + return atol(GetValue(key,v,outFound).c_str()); +} + +double plKeysAndValues::GetValue(const std::string & key, double defval, bool * outFound) const +{ + char buf[30]; + sprintf(buf, "%f", defval); + std::string v(buf); + return atof(GetValue(key,v,outFound).c_str()); +} + +std::vector plKeysAndValues::GetAllValues(const std::string & key) +{ + std::vector result; + xtl::istring xkey = key.c_str(); + if (HasKey(key)) + for (Values::const_iterator vi=fKeys[xkey].begin(); vi!=fKeys[xkey].end(); ++vi) + result.push_back(vi->c_str()); + return result; +} + +bool plKeysAndValues::GetKeyIterators(Keys::const_iterator & iter, Keys::const_iterator & end) const +{ + iter = fKeys.begin(); + end = fKeys.end(); + return true; +} + +bool plKeysAndValues::GetValueIterators(const xtl::istring & key, Values::const_iterator & iter, Values::const_iterator & end) const +{ + Keys::const_iterator ki = fKeys.find(key); + if(ki != fKeys.end()) + { + iter = ki->second.begin(); + end = ki->second.end(); + return true; + } + return false; +} + +void plKeysAndValues::Read(hsStream * s) +{ + UInt16 nkeys; + s->ReadSwap(&nkeys); + for (int ki=0; kiReadSwap(&strlen); + std::string key; + key.assign(strlen+1,'\0'); + s->Read(strlen,(void*)key.data()); + key.resize(strlen); + UInt16 nvalues; + s->ReadSwap(&nvalues); + for (int vi=0; viReadSwap(&strlen); + std::string value; + value.assign(strlen+1,'\0'); + s->Read(strlen,(void*)value.data()); + value.resize(strlen); + // for now, only single value for key on stream is allowed. + SetValue(key,value); + } + } +} + +void plKeysAndValues::Write(hsStream * s) +{ + // write nkeys + s->WriteSwap((UInt16)fKeys.size()); + // iterate through keys + Keys::const_iterator ki,ke; + GetKeyIterators(ki,ke); + for (ki;ki!=ke;++ki) + { + // write key string + s->WriteSwap((UInt16)ki->first.size()); + s->Write(ki->first.size(),ki->first.c_str()); + // write nvalues for this key + s->WriteSwap((UInt16)ki->second.size()); + // iterate through values for this key + Values::const_iterator vi,ve; + GetValueIterators(ki->first,vi,ve); + for (vi;vi!=ve;++vi) + { + // write value string + s->WriteSwap((UInt16)vi->size()); + s->Write(vi->size(),vi->c_str()); + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plKeysAndValues.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plKeysAndValues.h new file mode 100644 index 00000000..e6106f38 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plContainer/plKeysAndValues.h @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plKeysAndValues_h_inc +#define plKeysAndValues_h_inc + +#include "hsConfig.h" +#include "hsUtils.h" +#include "hsStlUtils.h" +#include "hsStream.h" + +#pragma warning(disable:4284) + +enum KAddValueMode +{ + kAlwaysAdd, // Add another value if key already exists + kReplaceIfExists, // Replace any existing key with new value. + kFailIfExists, // Do not add if key already exists. +}; + + +// class plKeysAndValues +// A multimap class. Stores multiple values per key. +class plKeysAndValues : public hsStreamable +{ +public: + typedef std::list + Values; + typedef std::map + Keys; + +private: + mutable Keys fKeys; + +public: + // ctor + plKeysAndValues(); + plKeysAndValues(const plKeysAndValues & src); + virtual ~plKeysAndValues(){} + // assign + plKeysAndValues & operator =(const plKeysAndValues & src); + // clear + void Clear(); + void RemoveKey(const std::string & key); + // query + bool HasKey(const std::string & key) const; + bool KeyHasValue(const std::string & key, const std::string & value); + bool KeyHasValue(const std::string & key, int value); + bool KeyHasValue(const std::string & key, double value); + // add + bool AddValue(const std::string & key, const std::string & value, KAddValueMode mode=kAlwaysAdd); + bool AddValue(const std::string & key, int value, KAddValueMode mode=kAlwaysAdd); + bool AddValue(const std::string & key, double value, KAddValueMode mode=kAlwaysAdd); + bool AddValues(const std::string & key, const std::vector & values, KAddValueMode mode=kAlwaysAdd); + // set (clear and add) + bool SetValue(const std::string & key, const std::string & value); + bool SetValue(const std::string & key, int value); + bool SetValue(const std::string & key, double value); + // get single value + std::string GetValue(const std::string & key, const std::string & defval="", bool * outFound=nil) const; + UInt32 GetValue(const std::string & key, UInt32 defval, bool * outFound=nil) const; + int GetValue(const std::string & key, int defval, bool * outFound=nil) const; + double GetValue(const std::string & key, double defval, bool * outFound=nil) const; + std::vector GetAllValues(const std::string & key); + // key iterator + bool GetKeyIterators(Keys::const_iterator & iter, Keys::const_iterator & end) const; + // value iterator (use for getting all values for key) + bool GetValueIterators(const xtl::istring & key, Values::const_iterator & iter, Values::const_iterator & end) const; + // streamable + void Read(hsStream * s); + void Write(hsStream * s); + // TODO: + UInt32 GetStreamSize() { return 0;} +}; + + +///////////////////////////////////////////////////////////// + + + +#endif // plKeysAndValues_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.cpp new file mode 100644 index 00000000..478dc3b4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.cpp @@ -0,0 +1,571 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plDeviceSelector.h" +#include "hsStream.h" +#include "hsUtils.h" + +#include + +DeviceSelector::DeviceSelector() : + fSelDevType(hsG3DDeviceSelector::kDevTypeUnknown), + fSelDev(0), + fSelMode(0), + fDevDesc(0), + fModeDesc(0), + fPerformance(0), + fFilterBPP(0), + fFilterWidth(0), + fFilterHeight(0), + fWindowed(false) +{ + memset(fStr, 0x00, sizeof(fStr)); +} + +const char *DeviceSelector::GetErrorString( void ) +{ + return fSelector.GetErrorString(); +} + +hsBool DeviceSelector::Enumerate(HWND hWnd, hsBool expertMode ) +{ + plDemoDebugFile::Enable( true ); /// ALWAYS enable (well, for now at least) + + if( !fSelector.Init() ) + return false; + + fSelector.Enumerate(hWnd); + + // 11.25.2000 mcn - Now we are tough if we're not in expert mode + fSelector.RemoveUnusableDevModes( !expertMode ); + + // Sort the modes + hsTArray &recs = fSelector.GetDeviceRecords(); + for (Int32 i = 0; i < recs.Count(); i++) + { + hsTArray &modes = recs[i].GetModes(); + std::sort(modes.FirstIter(), modes.StopIter()); + } + + IRefreshFilter(); + + return true; +} + +void DeviceSelector::SetModeFilter( int bitDepth, int minWidth, int minHeight ) +{ + fFilterBPP = bitDepth; + fFilterWidth = minWidth; + fFilterHeight = minHeight; + + IRefreshFilter(); +} + +void DeviceSelector::IRefreshFilter( void ) +{ + if (fSelDev >= fRecords.Count() ) + return; + + // Make sure to preserve fSelMode if possible + const hsG3DDeviceMode *oldMode = nil; + if( fSelMode < fFilteredModes.GetCount() && fFilteredModes[ fSelMode ]GetColorDepth() ) + continue; + + if( mode->GetWidth() < fFilterWidth || mode->GetHeight() < fFilterHeight ) + continue; + + // Remove any non 4:3 modes + bool goodAspectRatio = (mode->GetWidth() / 4 == mode->GetHeight() / 3) && + (mode->GetWidth() % 4 == 0) && + (mode->GetHeight() % 3 == 0); + + if (!goodAspectRatio && !(mode->GetWidth() == 1280 && mode->GetHeight() == 1024)) + { + continue; + } + + // Add the remaining to our filter index + fFilteredModes.Append( i ); + } + + if( oldMode != nil ) + { + fSelMode = IFindFiltered( GetModeNum( oldMode ) ); + if( fSelMode == -1 ) + { + // Try w/o bpp + fSelMode = IFindFiltered( IGetModeNumNoBPP( oldMode ) ); + if( fSelMode == -1 ) + fSelMode = 0; + } + } + else + fSelMode = 0; + +} + +int DeviceSelector::IFindFiltered( int realIndex ) +{ + int idx = fFilteredModes.Find( realIndex ); + if( idx == fFilteredModes.kMissingIndex ) + return -1; + + return idx; +} + +hsBool DeviceSelector::CheckDeviceType(UInt32 type) +{ + hsTArray& records = fSelector.GetDeviceRecords(); + + for (Int32 i = 0; i < records.Count(); i++) + { + if (type == records[i].GetG3DDeviceType()) + return true; + } + + return false; +} + +hsBool DeviceSelector::IsDirect3DAvailable() +{ + return CheckDeviceType(hsG3DDeviceSelector::kDevTypeDirect3D); +} + +hsBool DeviceSelector::IsDirect3DTnLAvailable() +{ + return CheckDeviceType(hsG3DDeviceSelector::kDevTypeDirect3DTnL); +} + +hsBool DeviceSelector::IsGlideAvailable() +{ + return CheckDeviceType(hsG3DDeviceSelector::kDevTypeGlide); +} + +hsBool DeviceSelector::IsOpenGLAvailable() +{ + return CheckDeviceType(hsG3DDeviceSelector::kDevTypeOpenGL); +} + +void DeviceSelector::SetDirect3D() +{ + SetDeviceType(hsG3DDeviceSelector::kDevTypeDirect3D); +} + +void DeviceSelector::SetDirect3DTnL() +{ + SetDeviceType(hsG3DDeviceSelector::kDevTypeDirect3DTnL); +} + +void DeviceSelector::SetGlide() +{ + SetDeviceType(hsG3DDeviceSelector::kDevTypeGlide); +} + +void DeviceSelector::SetOpenGL() +{ + SetDeviceType(hsG3DDeviceSelector::kDevTypeOpenGL); +} + +void DeviceSelector::SetDeviceType (UInt32 type) +{ + Int32 i; + for(i = 0; i < fRecords.GetCount(); i++) + fRecords[i].Clear(); + fRecords.Reset(); + + hsTArray& records = fSelector.GetDeviceRecords(); + for (i = 0; i < records.Count(); i++) + { + if (records[i].GetG3DDeviceType() == type) + fRecords.Push(records[i]); + } + + fSelDevType = type; + fSelDev = 0; + fDevDesc = 0; + fModeDesc = 0; + + IRefreshFilter(); +} + +hsBool DeviceSelector::IsDirect3D() +{ + if (fSelDevType == hsG3DDeviceSelector::kDevTypeDirect3D) + return true; + else + return false; +} + +hsBool DeviceSelector::IsDirect3DTnL() +{ + return ( fSelDevType == hsG3DDeviceSelector::kDevTypeDirect3DTnL ) ? true : false; +} + +hsBool DeviceSelector::IsGlide() +{ + if (fSelDevType == hsG3DDeviceSelector::kDevTypeGlide) + return true; + else + return false; +} + +hsBool DeviceSelector::IsOpenGL() +{ + if (fSelDevType == hsG3DDeviceSelector::kDevTypeOpenGL) + return true; + else + return false; +} + +hsBool DeviceSelector::SetDevice(UInt32 index) +{ + if (index < fRecords.Count()) + { + fSelDev = index; + fSelMode = 0; + fSelRec = fRecords[index]; + fSelRec.SetMaxAnisotropicSamples(0); + + IRefreshFilter(); + return true; + } + + return false; +} + +hsBool DeviceSelector::SetMode(UInt32 index) +{ + if (fSelDev >= fRecords.Count()) + return false; + + if (index < fFilteredModes.GetCount()) + { + fSelMode = index; + return true; + } + + return false; +} + +char* DeviceSelector::GetDeviceDescription() +{ + if (fDevDesc == fRecords.Count()) + { + fDevDesc = 0; + return nil; + } + + sprintf(fStr, "%s [%s]", fRecords[fDevDesc].GetDriverDesc(), fRecords[fDevDesc].GetDeviceDesc()); + fDevDesc++; + return fStr; +} + +char* DeviceSelector::GetModeDescription( void ) +{ + if (fSelDev >= fRecords.Count() ) + return nil; + + if (fModeDesc == fFilteredModes.GetCount()) + { + fModeDesc = 0; + return nil; + } + + hsG3DDeviceMode* mode = fRecords[fSelDev].GetMode( fFilteredModes[ fModeDesc ] ); + fModeDesc++; + + if( fFilterBPP != 0 ) + sprintf( fStr, "%ux%u", mode->GetWidth(), mode->GetHeight() ); + else + sprintf(fStr, "%ux%u %u bit", mode->GetWidth(), mode->GetHeight(), mode->GetColorDepth()); + + return fStr; +} + +UInt32 DeviceSelector::GetNumModes() +{ + return fFilteredModes.GetCount(); +} + +void DeviceSelector::GetMode(UInt32 i, int& width, int& height, int& depth) +{ + if (i >= fFilteredModes.GetCount()) + return; + + hsG3DDeviceMode* mode = fRecords[fSelDev].GetMode(fFilteredModes[i]); + + width = mode->GetWidth(); + height = mode->GetHeight(); + depth = mode->GetColorDepth(); +} + +hsBool DeviceSelector::SetDefault() +{ + hsG3DDeviceModeRecord dmr; + if (fSelector.GetDefault(&dmr)) + { + SetDeviceType(dmr.GetDevice()->GetG3DDeviceType()); + fSelDev = GetDeviceNum(dmr.GetDevice()); + fSelMode = IFindFiltered( GetModeNum(dmr.GetMode()) ); + fSelRec = fRecords[fSelDev]; + fSelRec.SetMaxAnisotropicSamples( 0 ); // Also off unless explicitly requested + + // Set a default detail level based on the available memory + if (hsMemorySpec() == kBlows) + fPerformance = 25; + else + fPerformance = 100; + + IRefreshFilter(); + + return true; + } + + return false; +} + +hsBool DeviceSelector::Save() +{ + hsUNIXStream stream; + if (!stream.Open(DEV_MODE_DAT, "wb")) + return false; + + hsG3DDeviceRecord selRec = fSelRec; + hsG3DDeviceMode selMode = *(selRec.GetMode( fFilteredModes[ fSelMode ] )); + selRec.ClearModes(); + + selRec.Write(&stream); + + if (fWindowed) + selMode.SetColorDepth(0); + selMode.Write(&stream); + + stream.WriteSwap16(fPerformance); + + stream.Close(); + + return true; +} + +hsBool DeviceSelector::Load() +{ + hsUNIXStream stream; + if (!stream.Open(DEV_MODE_DAT, "rb")) + return false; + + hsG3DDeviceRecord LoadRec; // Device copy for reading/writing + hsG3DDeviceMode LoadMode; // Modes copy for reading/writing + + LoadRec.Read(&stream); + if (LoadRec.IsInvalid()) + { + stream.Close(); + return false; + } + + LoadMode.Read(&stream); + + fPerformance = stream.ReadSwap16(); + + stream.Close(); + + // If selected device is available use it, otherwise return false + if ((LoadRec.GetG3DDeviceType() == hsG3DDeviceSelector::kDevTypeDirect3D) && IsDirect3DAvailable()) + SetDirect3D(); + else if ((LoadRec.GetG3DDeviceType() == hsG3DDeviceSelector::kDevTypeDirect3DTnL) && IsDirect3DTnLAvailable()) + SetDirect3DTnL(); + else if ((LoadRec.GetG3DDeviceType() == hsG3DDeviceSelector::kDevTypeGlide) && IsGlideAvailable()) + SetGlide(); + else + return false; + + //////////////////////////////////////////////////////////////////////////// + // Attempt to match the saved device and mode to the ones that are currently + // available. + //////////////////////////////////////////////////////////////////////////// + int num = GetDeviceNum(&LoadRec); + if (num == -1) + return false; + SetDevice(num); + + // Copy the flags + fSelRec.SetCap(hsG3DDeviceSelector::kCapsCompressTextures, + LoadRec.GetCap(hsG3DDeviceSelector::kCapsCompressTextures)); + fSelRec.SetAASetting( LoadRec.GetAASetting() ); + fSelRec.SetMaxAnisotropicSamples( LoadRec.GetMaxAnisotropicSamples() ); + + if (LoadMode.GetColorDepth() == 0) + { + fWindowed = true; + LoadMode.SetColorDepth(32); + } + num = GetModeNum(&LoadMode); + if (num == -1) + return false; + + SetMode(IFindFiltered(num)); + + return true; +} + +int DeviceSelector::GetDeviceNum(const hsG3DDeviceRecord *pLoadRec) +{ + hsTArray& records = fRecords; + + for (int i = 0; i < records.Count(); i++) + { + if (!strcmp(records[i].GetDriverDesc(), pLoadRec->GetDriverDesc()) && + !strcmp(records[i].GetDriverName(), pLoadRec->GetDriverName()) && + !strcmp(records[i].GetDriverVersion(), pLoadRec->GetDriverVersion()) && + !strcmp(records[i].GetDeviceDesc(), pLoadRec->GetDeviceDesc())) + return i; + } + + return -1; +} + +int DeviceSelector::IGetModeNumNoBPP( const hsG3DDeviceMode *pLoadMode ) +{ + hsTArray& modes = fRecords[fSelDev].GetModes(); + + for (int i = 0; i < modes.Count(); i++) + { + if ((modes[i].GetWidth() == pLoadMode->GetWidth()) && + (modes[i].GetHeight() == pLoadMode->GetHeight()) + ) + { + if( fFilteredModes.Find( i ) != fFilteredModes.kMissingIndex ) + { +#ifndef M3DRELEASE + if (pLoadMode->GetColorDepth() == 0) + fSelRec.GetMode( i )->SetColorDepth(0); +#endif + return i; + } + } + } + + return -1; +} + +int DeviceSelector::GetModeNum(const hsG3DDeviceMode *pLoadMode) +{ + hsTArray& modes = fRecords[fSelDev].GetModes(); + + for (int i = 0; i < modes.Count(); i++) + { + if ((modes[i].GetWidth() == pLoadMode->GetWidth()) && + (modes[i].GetHeight() == pLoadMode->GetHeight()) && + (modes[i].GetColorDepth() == pLoadMode->GetColorDepth())) + { + return i; + } + } + + return -1; +} + +UInt8 DeviceSelector::CanAntiAlias() +{ + hsG3DDeviceMode *mode = fRecords[ fSelDev ].GetMode( fFilteredModes[ fSelMode ] ); + + return mode->GetNumFSAATypes(); +} + +UInt8 DeviceSelector::IsAntiAliased() +{ + return fSelRec.GetAASetting(); +} + +void DeviceSelector::SetAntiAlias(UInt8 numSamples) +{ + fSelRec.SetAASetting( numSamples ); +} + +UInt8 DeviceSelector::CanAnisotropicFilter() +{ + UInt8 hi = fRecords[ fSelDev ].GetMaxAnisotropicSamples(); + if( hi > 1 ) + return hi; + + return 0; +} + +UInt8 DeviceSelector::GetAnisotropicLevel() +{ + return fSelRec.GetMaxAnisotropicSamples(); +} + +void DeviceSelector::SetAnisotropicLevel( UInt8 level ) +{ + fSelRec.SetMaxAnisotropicSamples( level ); +} + +bool DeviceSelector::CanWindow () +{ + return !fSelRec.GetCap(hsG3DDeviceSelector::kCapsNoWindow); +} + +bool DeviceSelector::IsWindowed() +{ + return fWindowed; +} + +void DeviceSelector::SetWindowed(bool state) +{ + fWindowed = state; +} + +hsBool DeviceSelector::CanCompress () +{ + return fRecords[fSelDev].GetCap(hsG3DDeviceSelector::kCapsCompressTextures); +} + +hsBool DeviceSelector::IsCompressed() +{ + return fSelRec.GetCap(hsG3DDeviceSelector::kCapsCompressTextures); +} + +void DeviceSelector::SetCompressed(hsBool state) +{ + fSelRec.SetCap(hsG3DDeviceSelector::kCapsCompressTextures, state); +} + +bool DeviceSelector::GetCap(UInt32 cap) +{ + return fSelRec.GetCap(cap) != 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.h new file mode 100644 index 00000000..366169ca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDeviceSelector/plDeviceSelector.h @@ -0,0 +1,144 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef DEVICESELECTOR_H +#define DEVICESELECTOR_H + +#include "hsTypes.h" +//#include "plRender.h" +#include "../../PubUtilLib/plPipeline/hsG3DDeviceSelector.h" + +#define DEV_MODE_DAT "dev_mode.dat" + +// +// A wrapper class to simplify hsG3DDeviceSelector. +// Make sure to call Enumerate before doing anything else. +// +class DeviceSelector +{ +protected: + UInt32 fSelDevType; // Current type of driver. Set by the SetDirect3D/Glide/OpenGL functions + UInt32 fSelDev; // Index of selected device. Set by SetDevice() or + UInt32 fSelMode; // Index of selected mode for current device + + int fDevDesc; // Used by GetDeviceDescription() to store index of current device + int fModeDesc; // Used by GetModeDescription() to store index of current mode + + char fStr[1024]; // Used to return text + + UInt16 fPerformance; // Performance level (0-100) + + int fFilterBPP, fFilterWidth, fFilterHeight; + + bool fWindowed; + + hsG3DDeviceSelector fSelector; + hsTArray fRecords; // Copy of all records for the current device type + hsG3DDeviceRecord fSelRec; // Device copy for reading/writing + + hsTArray fFilteredModes; + + void IRefreshFilter( void ); + int IFindFiltered( int realIndex ); + int IGetModeNumNoBPP( const hsG3DDeviceMode *pLoadMode ); // Returns index of passed in mode + +public: + DeviceSelector(); + + hsBool Enumerate(HWND hWnd, hsBool expertMode); // Enumerates all devices + const char *GetErrorString( void ); + + // Determines if any devices of the specified type are available + hsBool IsDirect3DAvailable(); + hsBool IsGlideAvailable(); + hsBool IsOpenGLAvailable(); + hsBool IsDirect3DTnLAvailable(); + + // Set current device type + void SetDirect3D(); + void SetGlide(); + void SetOpenGL(); + void SetDirect3DTnL(); + + // Returns true if current device is of the specified type + hsBool IsDirect3D(); + hsBool IsDirect3DTnL(); + hsBool IsGlide(); + hsBool IsOpenGL(); + + // Gets and sets the current device or mode. + UInt32 GetSelectedDevice() { return fSelDev; } + UInt32 GetSelectedMode() { return fSelMode; } + hsBool SetDevice(UInt32 index); + hsBool SetMode(UInt32 index); + + // Returns the device or mode descriptions. Call repeatedly until nil is returned. + char* GetDeviceDescription(); + char* GetModeDescription( void ); + + UInt32 GetNumModes(); + void GetMode(UInt32 i, int& width, int& height, int& depth); + + void SetModeFilter( int bitDepth = 0, int minWidth = 0, int minHeight = 0 ); + + void SetPerformance (UInt16 value) { fPerformance = value; } + UInt16 GetPerformance () { return fPerformance; } + + // Returns max number of samples allowed for AA + UInt8 CanAntiAlias (); + // Returns current # of samples selected for AA, 0 if none + UInt8 IsAntiAliased (); + void SetAntiAlias (UInt8 numSamples); + + UInt8 CanAnisotropicFilter(); + UInt8 GetAnisotropicLevel(); + void SetAnisotropicLevel( UInt8 level ); + + bool CanWindow(); + bool IsWindowed(); + void SetWindowed(bool state); + + hsBool CanCompress (); + hsBool IsCompressed (); + void SetCompressed (hsBool state); + + // Caps from hsG3DDeviceSelector + bool GetCap(UInt32 cap); + + // Save and load + hsBool Save(); // Returns false if output file can't be opened + hsBool Load(); // Returns false if input file can't be opened + hsBool SetDefault(); // Returns false if no suitable renderers are found + +protected: + hsBool CheckDeviceType(UInt32 type); // Used by the Is*Available() functions + void SetDeviceType(UInt32 type); // Used by SetDirect3D/Glide/OpenGL + + // Helpers for LoadDeviceMode() + int GetDeviceNum(const hsG3DDeviceRecord *pLoadRec); // Returns index of passed in device + int GetModeNum(const hsG3DDeviceMode *pLoadMode); // Returns index of passed in mode +}; + +#endif //DEVICESELECTOR_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccMeshSmooth.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccMeshSmooth.cpp new file mode 100644 index 00000000..9e9976b8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccMeshSmooth.cpp @@ -0,0 +1,341 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plAccMeshSmooth.h" + +#include "plGeometrySpan.h" +#include "plAccessGeometry.h" +#include "plAccessTriSpan.h" + +#include "hsFastMath.h" + +class EdgeBin +{ +public: + UInt16 fVtx; + UInt16 fCount; + + EdgeBin() : fVtx(0), fCount(0) {} +}; + +void plAccMeshSmooth::FindEdges(UInt32 maxVtxIdx, UInt32 nTris, UInt16* idxList, hsTArray& edgeVerts) +{ + hsTArray* bins = TRACKED_NEW hsTArray[maxVtxIdx+1]; + + hsBitVector edgeVertBits; + // For each vert pair (edge) in idxList + int i; + for( i = 0; i < nTris; i++ ) + { + int j; + for( j = 0; j < 3; j++ ) + { + int jPlus = j < 2 ? j+1 : 0; + int idx0 = idxList[i*3 + j]; + int idx1 = idxList[i*3 + jPlus]; + + int lo, hi; + + // Look in the LUT for the lower index. + if( idx0 < idx1 ) + { + lo = idx0; + hi = idx1; + } + else + { + lo = idx1; + hi = idx0; + } + + hsTArray& loBin = bins[lo]; + // In that bucket, look for the higher index. + int k; + for( k = 0; k < loBin.GetCount(); k++ ) + { + if( loBin[k].fVtx == hi ) + break; + } + + // If we find it, increment it's count, + // else add it. + if( k < loBin.GetCount() ) + { + loBin[k].fCount++; + } + else + { + EdgeBin* b = loBin.Push(); + b->fVtx = hi; + b->fCount = 1; + } + } + } + + // For each bucket in the LUT, + for( i = 0; i < maxVtxIdx+1; i++ ) + { + hsTArray& loBin = bins[i]; + // For each higher index + int j; + for( j = 0; j < loBin.GetCount(); j++ ) + { + // If the count is one, it's an edge, so set the edge bit for both indices (hi and lo) + if( 1 == loBin[j].fCount ) + { + edgeVertBits.SetBit(i); + edgeVertBits.SetBit(loBin[j].fVtx); + } + } + } + + // Now translate the bitvector to a list of indices. + for( i = 0; i < maxVtxIdx+1; i++ ) + { + if( edgeVertBits.IsBitSet(i) ) + edgeVerts.Append(i); + } + delete [] bins; +} + +void plAccMeshSmooth::FindEdges(hsTArray& spans, hsTArray* edgeVerts) +{ + fSpans.SetCount(spans.GetCount()); + + int i; + for( i = 0; i < spans.GetCount(); i++ ) + { + fAccGeom.AccessSpanFromGeometrySpan(fSpans[i], spans[i]); + if( !fSpans[i].HasAccessTri() ) + continue; + + plAccessTriSpan& triSpan = fSpans[i].AccessTri(); + + UInt32 nTris = triSpan.TriCount(); + UInt16* idxList = triSpan.fTris; + UInt32 maxVertIdx = triSpan.VertCount()-1; + + FindEdges(maxVertIdx, nTris, idxList, edgeVerts[i]); + } +} + +void plAccMeshSmooth::Smooth(hsTArray& spans) +{ + hsTArray* shareVtx = TRACKED_NEW hsTArray[spans.GetCount()]; + hsTArray* edgeVerts = TRACKED_NEW hsTArray[spans.GetCount()]; + FindEdges(spans, edgeVerts); + + int i; + for( i = 0; i < spans.GetCount(); i++ ) + { + while( edgeVerts[i].GetCount() ) + { + int j = edgeVerts[i].GetCount()-1; + + plAccessTriSpan& triSpan = fSpans[i].AccessTri(); + + VtxAccum accum; + accum.fPos = IPositionToWorld(fSpans[i], edgeVerts[i][j]); + accum.fNorm = INormalToWorld(fSpans[i], edgeVerts[i][j]); + if( triSpan.HasDiffuse() ) + accum.fDiffuse = triSpan.DiffuseRGBA(edgeVerts[i][j]); + else + accum.fDiffuse.Set(1.f, 1.f, 1.f, 1.f); + + shareVtx[i].Append(edgeVerts[i][j]); + + // Find shared verts on this same span + FindSharedVerts(fSpans[i], j, edgeVerts[i], shareVtx[i], accum); + + // Now look through the rest of the spans + int k; + for( k = i+1; k < spans.GetCount(); k++ ) + { + FindSharedVerts(fSpans[k], edgeVerts[k].GetCount(), edgeVerts[k], shareVtx[k], accum); + } + + accum.fNorm.Normalize(); + + if( fFlags & kSmoothNorm ) + { + for( k = i; k < spans.GetCount(); k++ ) + { + SetNormals(fSpans[k], shareVtx[k], accum.fNorm); + } + } + if( fFlags & kSmoothPos ) + { + for( k = i; k < spans.GetCount(); k++ ) + { + SetPositions(fSpans[k], shareVtx[k], accum.fPos); + } + } + if( fFlags & kSmoothDiffuse ) + { + for( k = i; k < spans.GetCount(); k++ ) + { + SetDiffuse(fSpans[k], shareVtx[k], accum.fDiffuse); + } + } + + // Now remove all the shared verts (which we just processed) + // from edgeVerts so we don't process them again. + for( k = i; k < spans.GetCount(); k++ ) + { + int m; + for( m = 0; m < shareVtx[k].GetCount(); m++ ) + { + int idx = edgeVerts[k].Find(shareVtx[k][m]); + hsAssert(idx != edgeVerts[k].kMissingIndex, "Lost vertex between find and remove"); + edgeVerts[k].Remove(idx); + } + shareVtx[k].SetCount(0); + } + } + } + + delete [] shareVtx; + delete [] edgeVerts; +} + +hsPoint3 plAccMeshSmooth::IPositionToWorld(plAccessSpan& span, int i) const +{ + return span.GetLocalToWorld() * span.AccessTri().Position(i); +} + +hsVector3 plAccMeshSmooth::INormalToWorld(plAccessSpan& span, int i) const +{ + if( span.GetWorldToLocal().fFlags & hsMatrix44::kIsIdent ) + { + return span.AccessTri().Normal(i); + } + + hsMatrix44 l2wInvTransp; + span.GetWorldToLocal().GetTranspose(&l2wInvTransp); + + hsVector3 ret = l2wInvTransp * span.AccessTri().Normal(i); + hsFastMath::NormalizeAppr(ret); + return ret; +} + +hsPoint3 plAccMeshSmooth::IPositionToLocal(plAccessSpan& span, const hsPoint3& wPos) const +{ + return span.GetWorldToLocal() * wPos; +} + +hsVector3 plAccMeshSmooth::INormalToLocal(plAccessSpan& span, const hsVector3& wNorm) const +{ + if( span.GetLocalToWorld().fFlags & hsMatrix44::kIsIdent ) + { + return wNorm; + } + + hsMatrix44 w2lInvTransp; + span.GetLocalToWorld().GetTranspose(&w2lInvTransp); + + hsVector3 ret = w2lInvTransp * wNorm; + hsFastMath::NormalizeAppr(ret); + return ret; +} + +void plAccMeshSmooth::FindSharedVerts(plAccessSpan& span, int numEdgeVerts, hsTArray& edgeVerts, hsTArray& shareVtx, VtxAccum& accum) +{ + plAccessTriSpan& triSpan = span.AccessTri(); + int i; + for( i = 0; i < numEdgeVerts; i++ ) + { + hsPoint3 pos = IPositionToWorld(span, edgeVerts[i]); + hsVector3 diff(&accum.fPos, &pos); + if( diff.MagnitudeSquared() < fDistTolSq ) + { + hsVector3 norm = INormalToWorld(span, edgeVerts[i]); + if( norm.InnerProduct(accum.fNorm) > fMinNormDot ) + { + shareVtx.Append(edgeVerts[i]); + + accum.fPos += pos; + accum.fPos *= 0.5f; + + accum.fNorm += norm; + hsFastMath::NormalizeAppr(accum.fNorm); + + hsColorRGBA diff; + if( triSpan.HasDiffuse() ) + diff = triSpan.DiffuseRGBA(edgeVerts[i]); + else + diff.Set(1.f, 1.f, 1.f, 1.f); + accum.fDiffuse += diff; + accum.fDiffuse *= 0.5f; + } + } + } +} + +void plAccMeshSmooth::SetPositions(plAccessSpan& span, hsTArray& shareVtx, const hsPoint3& pos) const +{ + plAccessTriSpan& triSpan = span.AccessTri(); + int i; + for( i = 0; i < shareVtx.GetCount(); i++ ) + triSpan.Position(shareVtx[i]) = IPositionToLocal(span, pos); +} + +void plAccMeshSmooth::SetNormals(plAccessSpan& span, hsTArray& shareVtx, const hsVector3& norm) const +{ + plAccessTriSpan& triSpan = span.AccessTri(); + int i; + for( i = 0; i < shareVtx.GetCount(); i++ ) + triSpan.Normal(shareVtx[i]) = INormalToLocal(span, norm); +} + +void plAccMeshSmooth::SetDiffuse(plAccessSpan& span, hsTArray& shareVtx, const hsColorRGBA& diff) const +{ + plAccessTriSpan& triSpan = span.AccessTri(); + hsAssert(triSpan.HasDiffuse(), "Calling SetColors on data with no color"); + int i; + for( i = 0; i < shareVtx.GetCount(); i++ ) + triSpan.Diffuse32(shareVtx[i]) = diff.ToARGB32(); +} + +void plAccMeshSmooth::SetAngle(hsScalar degs) +{ + fMinNormDot = hsCosine(hsScalarDegToRad(degs)); +} + +hsScalar plAccMeshSmooth::GetAngle() const +{ + return hsScalarRadToDeg(hsACosine(fMinNormDot)); +} + +void plAccMeshSmooth::SetDistTol(hsScalar dist) +{ + fDistTolSq = dist * dist; +} + +hsScalar plAccMeshSmooth::GetDistTol() const +{ + return hsSquareRoot(fDistTolSq); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccMeshSmooth.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccMeshSmooth.h new file mode 100644 index 00000000..b8b57dea --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccMeshSmooth.h @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAccMeshSmooth_inc +#define plAccMeshSmooth_inc + +#include "hsTemplates.h" +#include "plAccessGeometry.h" +#include "plAccessSpan.h" + +struct hsPoint3; +struct hsVector3; +class plGeometrySpan; + +class plAccMeshSmooth +{ +public: + enum { + kNone = 0x0, + kSmoothNorm = 0x1, + kSmoothPos = 0x2, + kSmoothDiffuse = 0x4 + }; + +protected: + struct VtxAccum + { + hsPoint3 fPos; + hsVector3 fNorm; + hsColorRGBA fDiffuse; + }; + + UInt32 fFlags; + + hsScalar fMinNormDot; + hsScalar fDistTolSq; + + plAccessGeometry fAccGeom; + hsTArray fSpans; + + hsPoint3 IPositionToWorld(plAccessSpan& span, int i) const; + hsVector3 INormalToWorld(plAccessSpan& span, int i) const; + hsPoint3 IPositionToLocal(plAccessSpan& span, const hsPoint3& wPos) const; + hsVector3 INormalToLocal(plAccessSpan& span, const hsVector3& wNorm) const; + + void FindEdges(UInt32 maxVtxIdx, UInt32 nTris, UInt16* idxList, hsTArray& edgeVerts); + void FindEdges(hsTArray& sets, hsTArray* edgeVerts); + void FindSharedVerts(plAccessSpan& span, int numEdgeVerts, hsTArray& edgeVerts, hsTArray& shareVtx, VtxAccum& accum); + void SetNormals(plAccessSpan& span, hsTArray& shareVtx, const hsVector3& norm) const; + void SetPositions(plAccessSpan& span, hsTArray& shareVtx, const hsPoint3& pos) const; + void SetDiffuse(plAccessSpan& span, hsTArray& shareVtx, const hsColorRGBA& diff) const; + +public: + plAccMeshSmooth() : fFlags(kSmoothNorm), fMinNormDot(0.25f), fDistTolSq(1.e-4f), fAccGeom() {} + + void SetAngle(hsScalar degs); + hsScalar GetAngle() const; // returns degrees + + void SetDistTol(hsScalar dist); + hsScalar GetDistTol() const; + + void Smooth(hsTArray& sets); + + void SetFlags(UInt32 f) { fFlags = f; } + UInt32 GetFlags() const { return fFlags; } +}; + +#endif // plAccMeshSmooth_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessGeometry.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessGeometry.cpp new file mode 100644 index 00000000..547ebaea --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessGeometry.cpp @@ -0,0 +1,583 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#include "hsTypes.h" + +#include "plAccessGeometry.h" + +#include "../pnSceneObject/plDrawInterface.h" + +#include "plDrawableSpans.h" +#include "plGeometrySpan.h" + +#include "plAccessSpan.h" +#include "plAccessPartySpan.h" +#include "plAccessTriSpan.h" +#include "plAccessVtxSpan.h" +#include "plAccessSnapShot.h" + +// For dipping directly into device buffers. +#include "../plPipeline/plGBufferGroup.h" +#include "../plPipeline/hsGDeviceRef.h" +#include "plPipeline.h" + +#include "plTweak.h" + +////////////////////////////////////////////////////////////////////////////////// +// Dropping these here, because they have no place else to go except a header. +void plAccessSpan::SetSource(plSpan* s) +{ + fLocalToWorld = &s->fLocalToWorld; + fWorldToLocal = &s->fWorldToLocal; + fLocalBounds = &s->fLocalBounds; + fWorldBounds = &s->fWorldBounds; + + fWaterHeight = s->fProps & plSpan::kWaterHeight ? &s->fWaterHeight : nil; +} +void plAccessSpan::SetSource(plGeometrySpan* s) +{ + fLocalToWorld = &s->fLocalToWorld; + fWorldToLocal = &s->fWorldToLocal; + fLocalBounds = &s->fLocalBounds; + fWorldBounds = &s->fWorldBounds; + + fWaterHeight = s->fProps & plGeometrySpan::kWaterHeight ? &s->fWaterHeight : nil; +} + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +// Simple constructor + +plAccessGeometry::plAccessGeometry(plPipeline* pipe) +: fPipe(pipe) +{ +} + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +// Global access stuff. +plAccessGeometry* plAccessGeometry::fInstance = nil; + +void plAccessGeometry::Init(plPipeline* pipe) +{ + plAccessGeometry* oldAcc = fInstance; + + fInstance = NEW(plAccessGeometry)(pipe); + + hsRefCnt_SafeUnRef(oldAcc); +} + +void plAccessGeometry::DeInit() +{ + if( fInstance ) + fInstance->Nilify(); + hsRefCnt_SafeUnRef(fInstance); +} + +void plAccessGeometry::SetTheIntance(plAccessGeometry* i) +{ + hsRefCnt_SafeAssign(fInstance, i); +} + + + + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +// The first couple of these just interpret between the SceneObjects we like to +// think about and the clumps of geometry that comprise each one. + +void plAccessGeometry::OpenRO(const plDrawInterface* di, hsTArray& accs, hsBool useSnap) const +{ + int numGot = 0; + accs.SetCount(di->GetNumDrawables()); + accs.SetCount(0); + int j; + for( j = 0; j < di->GetNumDrawables(); j++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(j)); + // Nil dr - it hasn't loaded yet or something. + if( dr ) + { + plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex(j)); + if( !diIndex.IsMatrixOnly() ) + { + int k; + for( k = 0; k < diIndex.GetCount(); k++ ) + { + accs.Expand(numGot+1); + accs.SetCount(numGot+1); + OpenRO(dr, diIndex[k], accs[numGot++]); + } + } + } + } + +} + +void plAccessGeometry::OpenRW(const plDrawInterface* di, hsTArray& accs, hsBool idxToo) const +{ + int numGot = 0; + accs.Expand(di->GetNumDrawables()); + accs.SetCount(0); + int j; + for( j = 0; j < di->GetNumDrawables(); j++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(j)); + // Nil dr - it hasn't loaded yet or something. + if( dr ) + { + plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex(j)); + if( !diIndex.IsMatrixOnly() ) + { + int k; + for( k = 0; k < diIndex.GetCount(); k++ ) + { + accs.Expand(numGot+1); + accs.SetCount(numGot+1); + OpenRW(dr, diIndex[k], accs[numGot++], idxToo); + } + } + } + } +} + +void plAccessGeometry::Close(hsTArray& accs) const +{ + int i; + for( i = 0; i < accs.GetCount(); i++ ) + Close(accs[i]); +} + +void plAccessGeometry::TakeSnapShot(const plDrawInterface* di, UInt32 channels) const +{ + int j; + for( j = 0; j < di->GetNumDrawables(); j++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(j)); + // Nil dr - it hasn't loaded yet or something. + if( dr ) + { + plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex(j)); + if( !diIndex.IsMatrixOnly() ) + { + int k; + for( k = 0; k < diIndex.GetCount(); k++ ) + { + TakeSnapShot(dr, diIndex[k], channels); + } + } + } + } +} + +void plAccessGeometry::RestoreSnapShot(const plDrawInterface* di, UInt32 channels) const +{ + int j; + for( j = 0; j < di->GetNumDrawables(); j++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(j)); + // Nil dr - it hasn't loaded yet or something. + if( dr ) + { + plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex(j)); + if( !diIndex.IsMatrixOnly() ) + { + int k; + for( k = 0; k < diIndex.GetCount(); k++ ) + { + RestoreSnapShot(dr, diIndex[k], channels); + } + } + } + } +} + +void plAccessGeometry::ReleaseSnapShot(const plDrawInterface* di) const +{ + int j; + for( j = 0; j < di->GetNumDrawables(); j++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(j)); + // Nil dr - it hasn't loaded yet or something. + if( dr ) + { + plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex(j)); + if( !diIndex.IsMatrixOnly() ) + { + int k; + for( k = 0; k < diIndex.GetCount(); k++ ) + { + ReleaseSnapShot(dr, diIndex[k]); + } + } + } + } +} + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +void plAccessGeometry::Close(plAccessSpan& acc) const +{ + if( !fPipe ) + return; + + fPipe->CloseAccess(acc); +} + +void plAccessGeometry::IOpen(plDrawable* d, UInt32 spanIdx, plAccessSpan& acc, hsBool useSnap, hsBool readOnly, hsBool idxToo) const +{ + acc.SetType(plAccessSpan::kUndefined); + + plDrawableSpans* drawable = plDrawableSpans::ConvertNoRef(d); + if( !drawable ) + return; + + if( drawable->GetSourceSpans().GetCount() && !drawable->GetNumSpans() ) + IAccessSpanFromSourceSpan(acc, drawable->GetSourceSpans()[spanIdx]); + else + IAccessSpanFromSpan(acc, drawable, drawable->GetSpan(spanIdx), useSnap, readOnly); + + if( !readOnly ) + { + // Need to mark the drawable's data as dirty. + plDrawableSpans* ds = plDrawableSpans::ConvertNoRef(drawable); + if( !ds ) + return; + if( acc.HasAccessVtx() ) + { + plVertexSpan* vtx = (plVertexSpan*)ds->GetSpan(spanIdx); + ds->DirtyVertexBuffer(vtx->fGroupIdx, vtx->fVBufferIdx); + } + + if( idxToo && acc.HasAccessTri() ) + { + plIcicle* ice = (plIcicle*)ds->GetSpan(spanIdx); + ds->DirtyIndexBuffer(ice->fGroupIdx, ice->fIBufferIdx); + } + } +} + +void plAccessGeometry::OpenRO(plDrawable* d, UInt32 spanIdx, plAccessSpan& acc, hsBool useSnap) const +{ + IOpen(d, spanIdx, acc, useSnap, true); +} + +void plAccessGeometry::OpenRW(plDrawable* drawable, UInt32 spanIdx, plAccessSpan& acc, hsBool idxToo) const +{ + IOpen(drawable, spanIdx, acc, false, false, idxToo); + +} + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +void plAccessGeometry::TakeSnapShot(plDrawable* drawable, UInt32 spanIdx, UInt32 channels) const +{ + plDrawableSpans* ds = plDrawableSpans::ConvertNoRef(drawable); + if( !ds ) + return; + const plSpan* span = ds->GetSpan(spanIdx); + + if( !span->fSnapShot ) + span->fSnapShot = NEW(plAccessSnapShot); + + plAccessSpan tmp; + OpenRO(drawable, spanIdx, tmp, false); + + if( tmp.HasAccessVtx() ) + { + span->fSnapShot->IncRef(); + span->fSnapShot->CopyFrom(tmp.AccessVtx(), channels); + } +} + +void plAccessGeometry::RestoreSnapShot(plDrawable* drawable, UInt32 spanIdx, UInt32 channels) const +{ + plDrawableSpans* ds = plDrawableSpans::ConvertNoRef(drawable); + if( !ds ) + return; + const plSpan* span = ds->GetSpan(spanIdx); + + if( !span->fSnapShot ) + return; + + plAccessSpan tmp; + OpenRW(drawable, spanIdx, tmp); + + if( tmp.HasAccessVtx() ) + span->fSnapShot->CopyTo(tmp.AccessVtx(), channels); +} + +void plAccessGeometry::ReleaseSnapShot(plDrawable* drawable, UInt32 spanIdx) const +{ + plDrawableSpans* ds = plDrawableSpans::ConvertNoRef(drawable); + if( !ds ) + return; + const plSpan* span = ds->GetSpan(spanIdx); + + if( !span->fSnapShot ) + return; + + span->fSnapShot->Release(); +} + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +void plAccessGeometry::IAccessSpanFromSourceSpan(plAccessSpan& dst, const plGeometrySpan* src) const +{ + // Get the connectivity info + dst.SetType(plAccessSpan::kTri); + plAccessTriSpan& tri = dst.AccessTri(); + tri.fNumTris = src->fNumIndices / 3; + tri.fTris = src->fIndexData; + tri.fIdxDeviceRef = nil; + + dst.SetSource(const_cast(src)); + dst.SetMaterial(src->fMaterial); + + plAccessVtxSpan& acc = dst.AccessVtx(); + acc.fNumVerts = (UInt16)(src->fNumVerts); + UInt32 sz = src->GetVertexSize(src->fFormat); + UInt8* ptr = src->fVertexData; + acc.PositionStream(ptr, (UInt16)sz, 0); + ptr += sizeof(hsPoint3); + acc.NormalStream(ptr, (UInt16)sz, 0); + ptr += sizeof(hsVector3); + acc.DiffuseStream(src->fDiffuseRGBA, sizeof(UInt32), 0); + acc.SpecularStream(src->fSpecularRGBA, sizeof(UInt32), 0); + acc.UVWStream(ptr, (UInt16)sz, 0); + acc.SetNumUVWs(src->CalcNumUVs(src->fFormat)); + + acc.fVtxDeviceRef = nil; +} + +void plAccessGeometry::IAccessSpanFromSpan(plAccessSpan& dst, plDrawableSpans* drawable, const plSpan* span, hsBool useSnap, hsBool readOnly) const +{ + dst.SetType(plAccessSpan::kUndefined); + dst.SetSource(const_cast (span)); + if( span->fTypeMask & plSpan::kIcicleSpan ) + { + IAccessSpanFromIcicle(dst, drawable, (const plIcicle*)span, readOnly); + } + else if( span->fTypeMask & plSpan::kParticleSpan ) + { + IAccessSpanFromParticle(dst, drawable, (const plParticleSpan*)span, readOnly); + } + if( useSnap ) + { + IAccessSpanFromSnap(dst, drawable, span); + } +} + +void plAccessGeometry::IAccessSpanFromSnap(plAccessSpan& dst, plDrawableSpans* drawable, const plSpan* span) const +{ + plAccessVtxSpan& acc = dst.AccessVtx(); + if( span->fSnapShot ) + { + span->fSnapShot->SetupChannels(acc); + } +} + +void plAccessGeometry::IAccessSpanFromVertexSpan(plAccessSpan& dst, plDrawableSpans* drawable, const plVertexSpan* span, hsBool readOnly) const +{ + dst.SetMaterial(drawable->GetMaterial(span->fMaterialIdx)); + + plAccessVtxSpan& acc = dst.AccessVtx(); + + plGBufferGroup* grp = drawable->GetBufferGroup(span->fGroupIdx); + +//#define MF_TOSSER +#ifndef MF_TOSSER + plConst(hsBool) useDev(false); +#else // MF_TOSSER + plConst(hsBool) useDev(true); +#endif // MF_TOSSER + if( useDev && !drawable->GetNativeProperty(plDrawable::kPropVolatile) && grp->GetVertexBufferRef(span->fVBufferIdx) ) + { + fPipe->OpenAccess(dst, drawable, span, readOnly); + return; + } + + acc.fNumVerts = (UInt16)(span->fVLength); + + plGBufferCell* cell = grp->GetCell(span->fVBufferIdx, span->fCellIdx); + + UInt8* ptr = grp->GetVertBufferData(span->fVBufferIdx); + + // Interleaved + if( cell->fColorStart == UInt32(-1) ) + { + UInt32 stride = grp->GetVertexSize(); + + ptr += cell->fVtxStart + span->fCellOffset * stride; + Int32 offset = (-(Int32)(span->fVStartIdx)) * (Int32)stride; + + acc.PositionStream(ptr, (UInt16)stride, offset); + ptr += sizeof(hsPoint3); + + int numWgts = grp->GetNumWeights(); + if( numWgts ) + { + acc.SetNumWeights(numWgts); + acc.WeightStream(ptr, (UInt16)stride, offset); + ptr += numWgts * sizeof(hsScalar); + if( grp->GetVertexFormat() & plGBufferGroup::kSkinIndices ) + { + acc.WgtIndexStream(ptr, (UInt16)stride, offset); + ptr += sizeof(UInt32); + } + else + { + acc.WgtIndexStream(nil, 0, offset); + } + } + else + { + acc.SetNumWeights(0); + } + + acc.NormalStream(ptr, (UInt16)stride, offset); + ptr += sizeof(hsVector3); + + acc.DiffuseStream(ptr, (UInt16)stride, offset); + ptr += sizeof(UInt32); + + acc.SpecularStream(ptr, (UInt16)stride, offset); + ptr += sizeof(UInt32); + + acc.UVWStream(ptr, (UInt16)stride, offset); + + acc.SetNumUVWs(grp->GetNumUVs()); + + } + else + { + UInt32 stride = grp->GetVertexLiteStride(); + + ptr += cell->fVtxStart + span->fCellOffset * stride; + Int32 posOffset = (-(Int32)(span->fVStartIdx)) * (Int32)stride; + + acc.PositionStream(ptr, (UInt16)stride, posOffset); + ptr += sizeof(hsPoint3); + + int numWgts = grp->GetNumWeights(); + if( numWgts ) + { + acc.SetNumWeights(numWgts); + acc.WeightStream(ptr, (UInt16)stride, posOffset); + ptr += numWgts * sizeof(hsScalar); + if( grp->GetVertexFormat() & plGBufferGroup::kSkinIndices ) + { + acc.WgtIndexStream(ptr, (UInt16)stride, posOffset); + ptr += sizeof(UInt32); + } + else + { + acc.WgtIndexStream(nil, 0, 0); + } + } + else + { + acc.SetNumWeights(0); + } + + acc.NormalStream(ptr, (UInt16)stride, posOffset); + ptr += sizeof(hsVector3); + + plGBufferColor* col = grp->GetColorBufferData(span->fVBufferIdx) + cell->fColorStart; + + Int16 colOffset = (Int16)((-(Int32)(span->fVStartIdx)) * (Int32)stride); + colOffset = (Int16)((-(Int32)(span->fVStartIdx)) * sizeof(*col)); + + acc.DiffuseStream(&col->fDiffuse, sizeof(*col), colOffset); + + acc.SpecularStream(&col->fSpecular, sizeof(*col), colOffset); + + acc.UVWStream(ptr, (UInt16)stride, posOffset); + + acc.SetNumUVWs(grp->GetNumUVs()); + } + + acc.fVtxDeviceRef = nil; +} + +void plAccessGeometry::IAccessConnectivity(plAccessSpan& dst, plDrawableSpans* drawable, const plSpan* src) const +{ + if( src->fTypeMask & plSpan::kIcicleSpan ) + { + const plIcicle* span = (const plIcicle*)src; + + dst.SetType(plAccessSpan::kTri); + + plAccessTriSpan& acc = dst.AccessTri(); + + acc.fNumTris = span->fILength / 3; + plGBufferGroup* grp = drawable->GetBufferGroup(span->fGroupIdx); + acc.fTris = grp->GetIndexBufferData(span->fIBufferIdx) + span->fIStartIdx; + + acc.fIdxDeviceRef = nil; + } + // Hmm, particle should probably go here... + else + { + dst.SetType(plAccessSpan::kVtx); + } + +} + +void plAccessGeometry::IAccessSpanFromIcicle(plAccessSpan& dst, plDrawableSpans* drawable, const plIcicle* span, hsBool readOnly) const +{ + dst.SetType(plAccessSpan::kTri); + + plAccessTriSpan& acc = dst.AccessTri(); + + IAccessSpanFromVertexSpan(dst, drawable, span, readOnly); + + acc.fNumTris = span->fILength / 3; + plGBufferGroup* grp = drawable->GetBufferGroup(span->fGroupIdx); + acc.fTris = grp->GetIndexBufferData(span->fIBufferIdx) + span->fIStartIdx; + + acc.fIdxDeviceRef = nil; +} + +void plAccessGeometry::IAccessSpanFromParticle(plAccessSpan& dst, plDrawableSpans* drawable, const plParticleSpan* span, hsBool readOnly) const +{ + hsAssert(false, "Aint got to it yet"); + // dst.SetType(plAccessSpan::kParty); +} + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessGeometry.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessGeometry.h new file mode 100644 index 00000000..c1c00a34 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessGeometry.h @@ -0,0 +1,161 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAccessGeometry_inc +#define plAccessGeometry_inc + +#include "hsTemplates.h" + +class plDrawable; +class plDrawableSpans; +class plGeometrySpan; +class plDrawInterface; +class plAccessSpan; +class plSpan; +class plParticleSpan; +class plIcicle; +class plVertexSpan; + +class plPipeline; + +class plAccessGeometry : public hsRefCnt +{ +protected: + void Nilify() { fPipe = nil; } + + friend class plAccessGeometry; + + plPipeline* fPipe; + + static plAccessGeometry* fInstance; +public: + // You're welcome to make your own, + // but this is normally just called by the global plAccessGeometry's Init() function. + // You should normally just use the instance supplied by Instance(); + plAccessGeometry(plPipeline* pipe=nil); + + static plAccessGeometry* Instance() { return fInstance; } + + // App will initialize, which will create the global instance. + // DeInit will nil the global instance. + static void Init(plPipeline* pipe); + static void DeInit(); + + // External DLL's will share the same plAccessGeometry. After the main App has + // DeInited the AccessGeometry, + // all calls to Instance()->Function() will return nil in one form or another (e.g. + // empty triangle lists). The external DLL needs to either not try to use these + // accessor functions after PythonInterface::WeAreInShutdown() (it won't do any good + // anyway as any work done will be thrown away), or else be prepared for receiving + // empty data where there was data before. + static void SetTheIntance(plAccessGeometry* i); + + // You have 2 options in opening the data. + // RO - Read Only. + // If you specify useSnapShot=true, then for channels which have had a snapshot + // taken, you will get pointers to this constant original snapshot form. For + // channels which have no snapshot data, or if useSnapShot=false, you will get + // pointers to the current (possibly modified since load) data. See SnapShot functions + // below. + // RW - Read/Write access to the source data. After closing, this modified source data + // will be used to update the buffer data used for rendering. + // In the normal case of reading the original (disk image) data, performing some operation + // on it and updating the renderable data, you need to open the same data twice, once + // RO(useSnapShot=true) (to get the constant source data) and once RW (for destination data). Note that + // the memory returned by RO will may be the same as returned by RW if there has been no snapshot + // taken. + // The RW permutation by itself is useful when performing a one-time operation on the data (e.g. + // loadtime), so the modified source data is, to everyone else, what was read from disk. + // The only way to retrieve the original source data is to read it from disk again, + // unless you've made a snapshot. Normally you would do a RW modify the original data, + // then take the snapshot if you are going to be performing more modifications. + // In ALL MODIFICATION CASES, if the modified data is paged out, and the original paged back in, you will + // need to perform your operation again - your modifications aren't saved anywhere. + void OpenRO(plDrawable* drawable, UInt32 spanIdx, plAccessSpan& acc, hsBool useSnapShot=true) const; + void OpenRW(plDrawable* drawable, UInt32 spanIdx, plAccessSpan& acc, hsBool idxToo=false) const; + + // What do we need to close up here? + void Close(plAccessSpan& acc) const; + + // Second set. You have a SceneObject's DrawInterface. This can reference into + // multiple drawables, and multiple spans within each drawable. You would rather + // not deal with it. So you can open by passing in a DrawInterface, and get back + // a list of geometry corresponding to that SceneObject/DrawInterface. + // NOTE: the list is in no way suggested to be homogenous. In fact, it's guaranteed + // not to be, because the reason the single object resolved into multiple geometry spans + // (possibly across multiple drawables) is that the conceptual single object is composed + // of multiple types of data that can't be batched into a single drawprimitive call. + // At the least, the different AccessSpans will have different materials. But it's just + // as likely that they will have different underlying formats (number of UVs, etc.). + // Again, if you are using the iterators supplied, you probably don't care, but sometimes + // you will (like if you are messing with the UVs). + void OpenRO(const plDrawInterface* di, hsTArray& accs, hsBool useSnapShot=true) const; + void OpenRW(const plDrawInterface* di, hsTArray& accs, hsBool idxToo=false) const; + + void Close(hsTArray& accs) const; + + // SnapShot functions. + // If you need to generate channel values based on the original values (e.g. normal perterbation) + // you need to reserve a copy of the original data. Only the channels specified will be copied. + // Only one snapshot is ever taken, and it is the union of all channels requested. For example, + // taking a snapshot of positions AFTER taking a snapshot of positions/normals is a no-op, + // but taking a snapshot of positions/normals AFTER a snapshot of just positions will result in + // a copy of positions from the old snapshot, then a copy of normals from the buffergroup + // into the new snapshot, then freeing of the old snapshot. + // Still, you should only snapshot the minimum set of channels you will need to be reading in their + // original form later. + // The snapshot data is refcounted. You need to match your TakeSnapShots with FreeSnapShots. + // SnapShot data is stored interleaved for efficiency, but don't count on it. Use an iterator. + // RestoreSnapShot will copy the stored channels back into the buffer group, resetting those channels to + // the state when the snapshot was taken. Note that channels not SnapShotted might have been modified + // via OpenRW. + // + void TakeSnapShot(plDrawable* drawable, UInt32 spanIdx, UInt32 channels) const; + void RestoreSnapShot(plDrawable* drawable, UInt32 spanIdx, UInt32 channels) const; + void ReleaseSnapShot(plDrawable* drawable, UInt32 spanIdx) const; + + void TakeSnapShot(const plDrawInterface* di, UInt32 channels) const; + void RestoreSnapShot(const plDrawInterface* di, UInt32 channels) const; + void ReleaseSnapShot(const plDrawInterface* di) const; + + // We often have geometry spans just sitting around devoid of any DI's, drawables or sceneobjects. + // They aren't too bad to access directly (not like diving through the drawable into buffergroups), + // but this let's them be accessed in a manner consistent with other geometry manipulations. + void AccessSpanFromGeometrySpan(plAccessSpan& dst, const plGeometrySpan* src) const { IAccessSpanFromSourceSpan(dst, src); } + +protected: + void IAccessSpanFromSourceSpan(plAccessSpan& dst, const plGeometrySpan* src) const; + void IAccessSpanFromSpan(plAccessSpan& dst, plDrawableSpans* drawable, const plSpan* span, hsBool useSnap, hsBool readOnly) const; + void IAccessSpanFromVertexSpan(plAccessSpan& dst, plDrawableSpans* drawable, const plVertexSpan* span, hsBool readOnly) const; + void IAccessConnectivity(plAccessSpan& dst, plDrawableSpans* drawable, const plSpan* src) const; + void IAccessSpanFromIcicle(plAccessSpan& dst, plDrawableSpans* drawable, const plIcicle* span, hsBool readOnly) const; + void IAccessSpanFromParticle(plAccessSpan& dst, plDrawableSpans* drawable, const plParticleSpan* span, hsBool readOnly) const; + void IAccessSpanFromSnap(plAccessSpan& dst, plDrawableSpans* drawable, const plSpan* src) const; + + void IOpen(plDrawable* d, UInt32 spanIdx, plAccessSpan& acc, hsBool useSnap, hsBool readOnly, hsBool idxToo=true) const; +}; + +#endif // plAccessGeometry_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessPartySpan.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessPartySpan.h new file mode 100644 index 00000000..238bc94b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessPartySpan.h @@ -0,0 +1,39 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAccessPartySpan_inc +#define plAccessPartySpan_inc + +#include "plAccessVtxSpan.h" + +class plAccessPartySpan : public plAccessVtxSpan +{ +public: + + +}; + +#endif // plAccessPartySpan_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSnapShot.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSnapShot.cpp new file mode 100644 index 00000000..f7cbc3cf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSnapShot.cpp @@ -0,0 +1,270 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plAccessSnapShot.h" + +#include "hsGeometry3.h" + +#include + +void plAccessSnapShot::Clear() +{ + ClearVerts(); + + int i; + for( i = 0; i < kNumValidChans; i++ ) + fChanSize[i] = 0; +} + +void plAccessSnapShot::Destroy() +{ + Clear(); + + delete [] fData; + + fRefCnt = 0; +} + +void plAccessSnapShot::Release() +{ + hsAssert(fRefCnt, "Releasing a snapshot with no refs. Check matching TakeSnapShot/ReleaseSnapShot calls."); + if( !--fRefCnt ) + { + Destroy(); + } +} + +UInt32 plAccessSnapShot::ICheckAlloc(const plAccessVtxSpan& src, UInt32 chanMask, UInt32 chan, UInt16 chanSize) +{ + if( ((1 << chan) & chanMask) && src.fStrides[chan] ) + { + if( fChanSize[chan] ) + { + // We already have this one + chanMask &= ~(1 << chan); + } + else + { + // We'll get this one + fChanSize[chan] = chanSize; + } + } + else + { + // either we haven't been asked for this or src doesn't have it. + // either way, we're never going to get it. + chanMask &= ~(1 << chan); + } + return chanMask; +} + +void plAccessSnapShot::IRecordSizes(UInt16 sizes[]) const +{ + int chan; + for( chan = 0; chan < kNumValidChans; chan++ ) + sizes[chan] = fChanSize[chan]; +} + +UInt16 plAccessSnapShot::IComputeStride() const +{ + UInt16 stride = 0; + int chan; + for( chan = 0; chan < kNumValidChans; chan++ ) + stride += fChanSize[chan]; + + return stride; +} + +void plAccessSnapShot::ICopyOldData(UInt8* data, const UInt16* const oldSizes, UInt16 oldStride, UInt16 newStride) +{ + UInt32 oldOffset = 0; + UInt32 newOffset = 0; + UInt8* srcChannels[kNumValidChans]; + UInt8* dstChannels[kNumValidChans]; + int chan; + for( chan = 0; chan < kNumValidChans; chan++ ) + { + if( oldSizes[chan] ) + { + hsAssert(fChanSize[chan], "Copying a channel we don't have"); + srcChannels[chan] = data + oldOffset; + oldOffset += oldSizes[chan]; + } + + if( fChanSize[chan] ) + { + dstChannels[chan] = fData + newOffset; + newOffset += fChanSize[chan]; + } + } + int i; + for( i = 0; i < fNumVerts; i++ ) + { + for( chan = 0; chan < kNumValidChans; chan++ ) + { + if( oldSizes[chan] ) + { + memcpy(dstChannels[chan], srcChannels[chan], oldSizes[chan]); + dstChannels[chan] += newStride; + srcChannels[chan] += oldStride; + } + } + } +} + +void plAccessSnapShot::ISetupPointers(UInt16 newStride) +{ + fData = TRACKED_NEW UInt8[fNumVerts * newStride]; + + int size = 0; + int chan; + for( chan = 0; chan < kNumValidChans; chan++ ) + { + if( fChanSize[chan] ) + { + fStrides[chan] = newStride; + fChannels[chan] = fData + size; + size += fChanSize[chan]; + } + } +} + +UInt32 plAccessSnapShot::CopyFrom(const plAccessVtxSpan& src, UInt32 chanMask) +{ + hsAssert(!fNumVerts || (fNumVerts == src.fNumVerts), "Copying from a different sized span"); + + fNumVerts = src.fNumVerts; + + UInt16 oldSize[kNumValidChans]; + UInt8* oldData = fData; + IRecordSizes(oldSize); + + UInt16 oldStride = IComputeStride(); + + // First, allocate any storage we need. Kill any requested channels out of the + // mask that we already have. + chanMask = ICheckAlloc(src, chanMask, kPosition, sizeof(hsPoint3)); + + chanMask = ICheckAlloc(src, chanMask, kWeight, sizeof(hsScalar) * src.fNumWeights); + if( fChanSize[kWeight] ) + fNumWeights = src.fNumWeights; + + chanMask = ICheckAlloc(src, chanMask, kWgtIndex, sizeof(UInt32)); + + chanMask = ICheckAlloc(src, chanMask, kNormal, sizeof(hsVector3)); + + chanMask = ICheckAlloc(src, chanMask, kDiffuse, sizeof(UInt32)); + + chanMask = ICheckAlloc(src, chanMask, kSpecular, sizeof(UInt32)); + + chanMask = ICheckAlloc(src, chanMask, kUVW, sizeof(hsPoint3) * src.fNumUVWsPerVert); + if( fChanSize[kUVW] ) + fNumUVWsPerVert = src.fNumUVWsPerVert; + + // If our chanMask has gone to zero, we've only been asked to record + // channels we already have, so there's nothing to do. + if( !chanMask ) + return 0; + + UInt16 newStride = IComputeStride(); + + ISetupPointers(newStride); + + ICopyOldData(oldData, oldSize, oldStride, newStride); + + UInt8* srcChannels[kNumValidChans]; + UInt8* dstChannels[kNumValidChans]; + int chan; + for( chan = 0; chan < kNumValidChans; chan++ ) + { + srcChannels[chan] = src.fChannels[chan]; + dstChannels[chan] = fChannels[chan]; + } + + int i; + for( i = 0; i < src.VertCount(); i++ ) + { + for( chan = 0; chan < kNumValidChans; chan++ ) + { + if( (1<< chan) & chanMask ) + { + memcpy(dstChannels[chan], srcChannels[chan], fChanSize[chan]); + dstChannels[chan] += fStrides[chan]; + srcChannels[chan] += src.fStrides[chan]; + } + } + } + + return chanMask; +} + +UInt32 plAccessSnapShot::CopyTo(const plAccessVtxSpan& dst, UInt32 chanMask) +{ + hsAssert(fNumVerts == dst.fNumVerts, "Vertex count mismatch, is this our real source?"); + + int chan; + for( chan = 0; chan < kNumValidChans; chan++ ) + { + if( !(fChanSize[chan] && dst.fStrides[chan]) ) + chanMask &= ~(1 << chan); + } + // If chanMask has gone to zero, either we don't have any of the requested channels + // recorded, or dst doesn't have them. Both being true is valid, but + // us having a channel recorded that's not in dst is probably an error. + if( !chanMask ) + return 0; + + int i; + for( i = 0; i < fNumVerts; i++ ) + { + for( chan = 0; chan < kNumValidChans; chan++ ) + { + if( (1 << chan) & chanMask ) + { + memcpy( + dst.fChannels[chan] + dst.fStrides[chan] * i, + fChannels[chan] + fStrides[chan] * i, + fChanSize[chan]); + } + } + } + return chanMask; +} + +void plAccessSnapShot::SetupChannels(plAccessVtxSpan& dst) const +{ + int chan; + for( chan = 0; chan < kNumValidChans; chan++ ) + { + if( fChanSize[chan] ) + { + dst.fChannels[chan] = fChannels[chan]; + dst.fStrides[chan] = fStrides[chan]; + dst.fOffsets[chan] = 0; + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSnapShot.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSnapShot.h new file mode 100644 index 00000000..fee8937d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSnapShot.h @@ -0,0 +1,71 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAccessSnapShot_inc +#define plAccessSnapShot_inc + +#include "plAccessVtxSpan.h" + +// All functions and fields here (including constructor) are private, +// because the only valid user of this is the friend class plAccessGeometry. +// Use this only via plAccessGeometry. +class plAccessSnapShot : public plAccessVtxSpan +{ +public: + void Destroy(); // Free up and reset to zero. + +protected: + + UInt16 fRefCnt; + + UInt8* fData; + + UInt16 fChanSize[kNumValidChans]; + + void ICopyOldData(UInt8* data, const UInt16* const oldSizes, UInt16 oldStride, UInt16 newStride); + UInt16 IComputeStride() const; + void IRecordSizes(UInt16 sizes[]) const; + UInt32 ICheckAlloc(const plAccessVtxSpan& src, UInt32 chanMask, UInt32 chan, UInt16 chanSize); + + void ISetupPointers(UInt16 newStride); + + void SetupChannels(plAccessVtxSpan& dst) const; + + UInt32 CopyTo(const plAccessVtxSpan& dst, UInt32 chanMask); + UInt32 CopyFrom(const plAccessVtxSpan& src, UInt32 chanMask); + void Release(); // Decrements refcnt, calls Destroy on zero + void Clear(); // Initialize to zeros + + void IncRef() { fRefCnt++; } + void DecRef() { Release(); } + + plAccessSnapShot() : fRefCnt(0), fData(nil) { Clear(); } + + friend class plAccessGeometry; +}; + + +#endif // plAccessSnapShot_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSpan.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSpan.h new file mode 100644 index 00000000..3a05df07 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessSpan.h @@ -0,0 +1,130 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAccessSpan_inc +#define plAccessSpan_inc + +#include "plAccessVtxSpan.h" +#include "plAccessTriSpan.h" +#include "plAccessPartySpan.h" + +#include "plSpanTypes.h" +#include "plGeometrySpan.h" + +class plGeometrySpan; +class plSpan; + +class hsGMaterial; + +class plAccessSpan +{ +public: + enum AccessType + { + kTri = 0, + kParty, + kVtx, + kUndefined + }; + +private: + union + { + plAccessTriSpan fAccessTri; + plAccessPartySpan fAccessParty; + plAccessVtxSpan fAccessVtx; + } fAccess; + AccessType fType; + + const hsMatrix44* fLocalToWorld; + const hsMatrix44* fWorldToLocal; + hsBounds3Ext* fLocalBounds; + hsBounds3Ext* fWorldBounds; + + hsScalar* fWaterHeight; + + hsGMaterial* fMaterial; + + void SetSource(plSpan* s); + void SetSource(plGeometrySpan* s); + void SetMaterial(hsGMaterial* m) { fMaterial = m; } + + friend class plAccessGeometry; +public: + + plAccessSpan() : fType(kUndefined), fLocalToWorld(nil), fWorldToLocal(nil), fLocalBounds(nil), fWorldBounds(nil), fWaterHeight(nil), fMaterial(nil) {} + plAccessSpan(AccessType t) : fType(t), fLocalToWorld(nil), fWorldToLocal(nil), fLocalBounds(nil), fWorldBounds(nil), fWaterHeight(nil), fMaterial(nil) {} + + void SetType(AccessType t) { fType = t; } + AccessType GetType() const { return fType; } + + hsBool HasAccessTri() const { return fType == kTri; } + hsBool HasAccessParty() const { return fType == kParty; } + hsBool HasAccessVtx() const { return fType != kUndefined; } + + plAccessTriSpan& AccessTri() { hsAssert(fType == kTri, "Cross type access"); return fAccess.fAccessTri; } + plAccessPartySpan& AccessParty() { hsAssert(fType == kParty, "Cross type access"); return fAccess.fAccessParty; } + + inline plAccessVtxSpan& AccessVtx(); + + + const hsMatrix44& GetLocalToWorld() const { return *fLocalToWorld; } + const hsMatrix44& GetWorldToLocal() const { return *fWorldToLocal; } + + hsGMaterial* GetMaterial() const { return fMaterial; } + + const hsBounds3Ext& GetLocalBounds() const { return *fLocalBounds; } + const hsBounds3Ext& GetWorldBounds() const { return *fWorldBounds; } + + void SetLocalBounds(const hsBounds3Ext& bnd) { *fWorldBounds = *fLocalBounds = bnd; fWorldBounds->Transform(fLocalToWorld); } + void SetWorldBounds(const hsBounds3Ext& wBnd) { *fWorldBounds = wBnd; } + + hsBool HasWaterHeight() const { return nil != fWaterHeight; } + hsScalar GetWaterHeight() const { hsAssert(HasWaterHeight(), "Check before asking"); return *fWaterHeight; } +}; + +inline plAccessVtxSpan& plAccessSpan::AccessVtx() +{ + switch( fType ) + { + case kTri: + return fAccess.fAccessTri; + case kParty: + return fAccess.fAccessParty; + case kVtx: + return fAccess.fAccessVtx; + + case kUndefined: + default: + break; + } + hsAssert(false, "Undefined type"); + return fAccess.fAccessVtx; +} + + + +#endif // plAccessSpan_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessTriSpan.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessTriSpan.h new file mode 100644 index 00000000..440869c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessTriSpan.h @@ -0,0 +1,158 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAccessTriSpan_inc +#define plAccessTriSpan_inc + +#include "plAccessVtxSpan.h" + +class plAccessTriSpan : public plAccessVtxSpan +{ +public: + hsGDeviceRef* fIdxDeviceRef; + + UInt16* fTris; // array length fNumTris*3 + + UInt32 fNumTris; + + void ClearTris() { fTris = nil; fNumTris = 0; } + + UInt32 TriCount() const { return fNumTris; } + + void SetIdxDeviceRef(hsGDeviceRef* ref) { fIdxDeviceRef = ref; } + hsGDeviceRef* GetIdxDeviceRef() const { return fIdxDeviceRef; } +}; + +class plAccTriIterator +{ +protected: + + UInt16* fCurrIdx; + UInt16* fEndIdx; + + plAccessTriSpan* fAccess; +public: + plAccTriIterator() { Set(nil); } + plAccTriIterator(plAccessTriSpan* acc) { Set(acc); } + + void Set(plAccessTriSpan* acc) { fAccess = acc; fCurrIdx = nil; } + + void Begin(); + void Advance(); + void SetTri(int i); + hsBool More() const; + + UInt32 NumTris() const { return fAccess->fNumTris; } + + UInt16& RawIndex(int iVtx) const { return fCurrIdx[iVtx]; } + + hsPoint3& Position(int iVtx) const { return fAccess->PositionOff(fCurrIdx[iVtx]); } + hsVector3& Normal(int iVtx) const { return fAccess->NormalOff(fCurrIdx[iVtx]); } + UInt32 Diffuse32(int iVtx) const { return fAccess->Diffuse32Off(fCurrIdx[iVtx]); } + UInt32 Specular32(int iVtx) const { return fAccess->Specular32Off(fCurrIdx[iVtx]); } + hsColorRGBA DiffuseRGBA(int iVtx) const { return fAccess->DiffuseRGBAOff(fCurrIdx[iVtx]); } + hsColorRGBA SpecularRGBA(int iVtx) const { return fAccess->SpecularRGBAOff(fCurrIdx[iVtx]); } + hsPoint3* UVWs(int iVtx) const { return fAccess->UVWsOff(fCurrIdx[iVtx]); } + hsPoint3& UVW(int iVtx, int iUVW) const { return fAccess->UVWOff(fCurrIdx[iVtx], iUVW); } + + hsPoint3 InterpPosition(const hsPoint3& bary) const; + hsVector3 InterpNormal(const hsPoint3& bary) const; + UInt32 InterpDiffuse32(const hsPoint3& bary) const; + UInt32 InterpSpecular32(const hsPoint3& bary) const; + hsColorRGBA InterpDiffuseRGBA(const hsPoint3& bary) const; + hsColorRGBA InterpSpecularRGBA(const hsPoint3& bary) const; + hsPoint3 InterpUVW(const hsPoint3& bary, int i) const; + + +}; + +inline void plAccTriIterator::Begin() +{ + fCurrIdx = fAccess->fTris; + fEndIdx = fCurrIdx + fAccess->fNumTris*3; +} + +inline void plAccTriIterator::Advance() +{ + fCurrIdx += 3; +} + +inline void plAccTriIterator::SetTri(int i) +{ + fCurrIdx = fAccess->fTris + i * 3; +} + +inline hsBool plAccTriIterator::More() const +{ + return fCurrIdx < fEndIdx; +} + +inline hsPoint3 plAccTriIterator::InterpPosition(const hsPoint3& bary) const +{ + return Position(0) * bary[0] + + Position(1) * bary[1] + + Position(2) * bary[2]; +} + +inline hsVector3 plAccTriIterator::InterpNormal(const hsPoint3& bary) const +{ + return Normal(0) * bary[0] + + Normal(1) * bary[1] + + Normal(2) * bary[2]; +} + +inline hsColorRGBA plAccTriIterator::InterpDiffuseRGBA(const hsPoint3& bary) const +{ + return DiffuseRGBA(0) * bary[0] + + DiffuseRGBA(1) * bary[1] + + DiffuseRGBA(2) * bary[2]; +} + +inline hsColorRGBA plAccTriIterator::InterpSpecularRGBA(const hsPoint3& bary) const +{ + return SpecularRGBA(0) * bary[0] + + SpecularRGBA(1) * bary[1] + + SpecularRGBA(2) * bary[2]; +} + +inline UInt32 plAccTriIterator::InterpDiffuse32(const hsPoint3& bary) const +{ + return InterpDiffuseRGBA(bary).ToARGB32(); +} + +inline UInt32 plAccTriIterator::InterpSpecular32(const hsPoint3& bary) const +{ + return InterpSpecularRGBA(bary).ToARGB32(); +} + +inline hsPoint3 plAccTriIterator::InterpUVW(const hsPoint3& bary, int i) const +{ + return UVW(0, i) * bary[0] + + UVW(1, i) * bary[1] + + UVW(2, i) * bary[2]; +} + +#endif // plAccessTriSpan_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessVtxSpan.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessVtxSpan.h new file mode 100644 index 00000000..e9cbcea2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAccessVtxSpan.h @@ -0,0 +1,447 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAccessVtxSpan_inc +#define plAccessVtxSpan_inc + +#include "hsGeometry3.h" +#include "hsColorRGBA.h" + +class hsGMaterial; +struct hsMatrix44; +class hsGDeviceRef; + +// AccessSpan - a generic "vertex array". It'll at least act like one, +// whatever the underlying storage mechanism. For random access, just +// use the accessor functions. For faster sequential iteration, use +// the iterator classes defined further down. I'm leaving the actual +// data (fChannels & fStrides) public, but I'll probably regret it. +// I have to dig pretty deep to come up with a reason not to use +// either the indexed accessors or the iterator classes. +// +// You may be wondering why have separate strides for each of the data channels, +// especially since this means doing nChan pointer adds to advance rather than +// one. The answer is two part. First, the different channels may be in different +// buffers, either because we're on the export side and haven't glommed them together +// into a single stream yet, or because we finally got around to using multiple +// streams on the runtime side (say to support instancing). The point is for you +// to be able to write your code without knowing or caring. The second part is that +// those nChan adds aren't really costing you anything. Say the data is stored contiguously, +// and we keep a base pointer and an advance just adds the (single) stride to the base +// pointer. That's probably your position, and we've just saved a bunch of adds in advancing +// it. Now you want to access your normal, well, you still have to do an add on the base pointer +// to get to your normal, and another to get to your color, etc. So if you really want to +// eliminate those extra adds, go ahead and use the iterator, just make sure you use +// the most focused iterator available. Like if you're just looking at position and +// normal, use the plAccPosNormIterator. +class plAccessVtxSpan +{ +public: + enum Channel + { + kPosition = 0, + kWeight, + kWgtIndex, + kNormal, + kDiffuse, + kSpecular, + kUVW, + kNumValidChans, + kInvalid = 0xffff + }; + // Mask versions, to be or'ed together and passed in when using plAccessGeometry::SnapShotStuff. + enum + { + kPositionMask = (1 << kPosition), + kWeightMask = (1 << kWeight), + kWgtIndexMask = (1 << kWgtIndex), + kNormalMask = (1 << kNormal), + kDiffuseMask = (1 << kDiffuse), + kSpecularMask = (1 << kSpecular), + kUVWMask = (1 << kUVW) + }; + + hsGDeviceRef* fVtxDeviceRef; + + UInt8* fChannels[kNumValidChans]; + UInt16 fStrides[kNumValidChans]; + Int32 fOffsets[kNumValidChans]; + + UInt16 fNumWeights; + UInt16 fNumUVWsPerVert; + UInt16 fNumVerts; + + ////////////////////////////////// + // QUERY SECTION + // Queries on how much of what we got. + UInt32 VertCount() const { return fNumVerts; } + hsBool HasChannel(Channel chan) const { return fStrides[chan] > 0; } + hsBool HasPositions() const { return HasChannel(kPosition); } + hsBool HasWeights() const { return HasChannel(kWeight); } + int NumWeights() const { return fNumWeights; } + hsBool HasWgtIndex() const { return HasChannel(kWgtIndex); } + hsBool HasNormals() const { return HasChannel(kNormal); } + hsBool HasDiffuse() const { return HasChannel(kDiffuse); } + hsBool HasSpecular() const { return HasChannel(kSpecular); } + hsBool HasUVWs() const { return HasChannel(kUVW); } + hsBool HasUVWs(int n) { return HasChannel(kUVW) && (n <= fNumUVWsPerVert); } + int NumUVWs() const { return fNumUVWsPerVert; } + + ////////////////////////////////// + // ACCESSOR SECTION + hsPoint3& Position(int i) const { return *(hsPoint3*)(fChannels[kPosition] + i*fStrides[kPosition]); } + hsVector3& Normal(int i) const { return *(hsVector3*)(fChannels[kNormal] + i*fStrides[kNormal]); } + + hsScalar& Weight(int iVtx, int iWgt) const { return *(hsScalar*)(fChannels[kWeight] + iVtx*fStrides[kWeight] + iWgt*sizeof(hsScalar)); } + hsScalar* Weights(int i) const { return (hsScalar*)(fChannels[kWeight] + i*fStrides[kWeight]); } + UInt32& WgtIndices(int i) const { return *(UInt32*)(fChannels[kWgtIndex] + i * fStrides[kWgtIndex]); } + UInt8& WgtIndex(int iVtx, int iWgt) const { return *(fChannels[kWgtIndex] + iVtx*fStrides[kWgtIndex] + iWgt); } + + UInt32& Diffuse32(int i) const { return *(UInt32*)(fChannels[kDiffuse] + i*fStrides[kDiffuse]); } + UInt32& Specular32(int i) const { return *(UInt32*)(fChannels[kSpecular] + i*fStrides[kSpecular]); } + + hsColorRGBA DiffuseRGBA(int i) const { return hsColorRGBA().FromARGB32(Diffuse32(i)); } + hsColorRGBA SpecularRGBA(int i) const { return hsColorRGBA().FromARGB32(Specular32(i)); } + + // Two versions of UVW, first to get the iVtx'th vertex's iUVW'th uvw. + // Second just gets the vertex's uvw array, which is hopefully stored contiguously or we're screwed. + hsPoint3& UVW(int iVtx, int iUVW) const { return *(hsPoint3*)(fChannels[kUVW] + iVtx*fStrides[kUVW] + iUVW*sizeof(hsPoint3)); } + hsPoint3* UVWs(int i) const { return (hsPoint3*)(fChannels[kUVW] + i*fStrides[kUVW]); } + + // OFFSET VERSIONS + // Tri and particle accessors call the offset versions because they have indices into the original + // buffers, but our pointers are based at the beginning of our data. + hsPoint3& PositionOff(int i) const { return *(hsPoint3*)(fChannels[kPosition] + i*fStrides[kPosition] + fOffsets[kPosition]); } + hsVector3& NormalOff(int i) const { return *(hsVector3*)(fChannels[kNormal] + i*fStrides[kNormal] + fOffsets[kNormal]); } + + hsScalar& WeightOff(int iVtx, int iWgt) const { return *(hsScalar*)(fChannels[kWeight] + iVtx*fStrides[kWeight] + fOffsets[kWeight] + iWgt*sizeof(hsScalar)); } + hsScalar* WeightsOff(int i) const { return (hsScalar*)(fChannels[kWeight] + i*fStrides[kWeight] + fOffsets[kWeight]); } + UInt32& WgtIndicesOff(int i) const { return *(UInt32*)(fChannels[kWgtIndex] + i * fStrides[kWgtIndex] + fOffsets[kWgtIndex]); } + UInt8& WgtIndexOff(int iVtx, int iWgt) const { return *(fChannels[kWgtIndex] + iVtx*fStrides[kWgtIndex] + fOffsets[kWgtIndex] + iWgt); } + + UInt32& Diffuse32Off(int i) const { return *(UInt32*)(fChannels[kDiffuse] + i*fStrides[kDiffuse] + fOffsets[kDiffuse]); } + UInt32& Specular32Off(int i) const { return *(UInt32*)(fChannels[kSpecular] + i*fStrides[kSpecular] + fOffsets[kSpecular]); } + + hsColorRGBA DiffuseRGBAOff(int i) const { return hsColorRGBA().FromARGB32(Diffuse32Off(i)); } + hsColorRGBA SpecularRGBAOff(int i) const { return hsColorRGBA().FromARGB32(Specular32Off(i)); } + + // Two versions of UVW, first to get the iVtx'th vertex's iUVW'th uvw. + // Second just gets the vertex's uvw array, which is hopefully stored contiguously or we're screwed. + hsPoint3& UVWOff(int iVtx, int iUVW) const { return *(hsPoint3*)(fChannels[kUVW] + iVtx*fStrides[kUVW] + fOffsets[kUVW] + iUVW*sizeof(hsPoint3)); } + hsPoint3* UVWsOff(int i) const { return (hsPoint3*)(fChannels[kUVW] + i*fStrides[kUVW] + fOffsets[kUVW]); } + + ////////////////////////////////// + // SETUP SECTION. + ////////////////////////////////// + + ////////////////////////////////// + // Call Clear to initialize. + void ClearVerts(); + + // Must at least set a count and the valid channels. Note below on setting up for UVW access. + plAccessVtxSpan& SetVertCount(UInt16 c) { fNumVerts = c; return *this; } + plAccessVtxSpan& SetStream(void* p, UInt16 stride, Int32 offset, Channel chan) { fChannels[chan] = (UInt8*)p; fStrides[chan] = stride; fOffsets[chan] = offset; return *this; } + + ////////////////////////////////// + // Convenience versions. You get the idea. + plAccessVtxSpan& PositionStream(void* p, UInt16 stride, Int32 offset) { return SetStream(p, stride, offset, kPosition); } + plAccessVtxSpan& NormalStream(void* p, UInt16 stride, Int32 offset) { return SetStream(p, stride, offset, kNormal); } + plAccessVtxSpan& DiffuseStream(void* p, UInt16 stride, Int32 offset) { return SetStream(p, stride, offset, kDiffuse); } + plAccessVtxSpan& SpecularStream(void* p, UInt16 stride, Int32 offset) { return SetStream(p, stride, offset, kSpecular); } + plAccessVtxSpan& WeightStream(void* p, UInt16 stride, Int32 offset) { return SetStream(p, stride, offset, kWeight); } + plAccessVtxSpan& WgtIndexStream(void* p, UInt16 stride, Int32 offset) { return SetStream(p, stride, offset, kWgtIndex); } + plAccessVtxSpan& SetNumWeights(int n) { if( !(fNumWeights = n) ) SetStream(nil, 0, 0, kWeight).SetStream(nil, 0, 0, kWgtIndex); return *this; } + // Note on UVW access setup, you don't actually "have" to set the number of uvws per vertex, + // but one way or another you'll need to know to access the uvws. If you're going to be setting + // up the AccessSpan and passing it off for processing, you definitely better set it. But the + // accessor and iterators don't ever use it. + // Note that this means the UVWStream stride is from uvw[0][0] to uvw[1][0] in bytes. + plAccessVtxSpan& UVWStream(void* p, UInt16 stride, Int32 offset) { return SetStream(p, stride, offset, kUVW); } + plAccessVtxSpan& SetNumUVWs(int n) { if( !(fNumUVWsPerVert = n) )SetStream(nil, 0, 0, kUVW); return *this; } + ////////////////////////////////// + + // Cache away any device stuff that needed to be opened (locked) to get this data, so + // we can close (unlock) it later. + void SetVtxDeviceRef(hsGDeviceRef* ref) { fVtxDeviceRef = ref; } + hsGDeviceRef* GetVtxDeviceRef() const { return fVtxDeviceRef; } +}; + +inline void plAccessVtxSpan::ClearVerts() +{ + int i; + for( i = 0; i < kNumValidChans; i++ ) + { + fChannels[i] = nil; + fStrides[i] = 0; + } + fNumVerts = 0; + fNumUVWsPerVert = 0; + fNumWeights = 0; +} + +template class plAccIterator +{ +private: + union + { + T* fValue; + UInt8* fValueByte; + }; + UInt8* fValueEnd; + plAccessVtxSpan* fAccess; + plAccessVtxSpan::Channel fChan; + + void ISetEnd() { fValueEnd = fAccess->fChannels[fChan] + fAccess->fNumVerts * fAccess->fStrides[fChan]; } + +public: + plAccIterator(plAccessVtxSpan* acc, plAccessVtxSpan::Channel chan) { Set(acc, chan); } + plAccIterator(const plAccIterator& accIt) { *this = accIt; } + plAccIterator() : fValueByte(nil), fValueEnd(nil), fAccess(nil), fChan(plAccessVtxSpan::kInvalid) {} + + void Set(plAccessVtxSpan* acc, plAccessVtxSpan::Channel chan) { fAccess = acc; fChan = chan; ISetEnd(); } + + T* Value() const { return fValue; } + + int Count() const { return fAccess->VertCount(); } + int GetCount() const { return Count(); } + + void Begin() { fValueByte = fAccess->fChannels[fChan]; } + void Advance() { fValueByte += fAccess->fStrides[fChan]; } + hsBool More() const { return fValueByte < fValueEnd; } +}; + +class plAccPositionIterator +{ +protected: + plAccIterator fPosition; + +public: + plAccPositionIterator(plAccessVtxSpan* acc) + : fPosition(acc, plAccessVtxSpan::kPosition) {} + + plAccPositionIterator() {} + + plAccPositionIterator& Set(plAccessVtxSpan* acc) + { + fPosition.Set(acc, plAccessVtxSpan::kPosition); + return *this; + } + + hsPoint3* Position() const { return fPosition.Value(); } + + void Begin() { fPosition.Begin(); } + void Advance() { fPosition.Advance(); } + hsBool More() const { return fPosition.More(); } +}; + +class plAccPosNormIterator +{ +protected: + plAccIterator fPosition; + plAccIterator fNormal; +public: + plAccPosNormIterator(plAccessVtxSpan* acc) + : fPosition(acc, plAccessVtxSpan::kPosition), + fNormal(acc, plAccessVtxSpan::kNormal) {} + + plAccPosNormIterator() {} + + plAccPosNormIterator& Set(plAccessVtxSpan* acc) + { + fPosition.Set(acc, plAccessVtxSpan::kPosition); + fNormal.Set(acc, plAccessVtxSpan::kNormal); + return *this; + } + + hsPoint3* Position() const { return fPosition.Value(); } + hsVector3* Normal() const { return fNormal.Value(); } + + void Begin() { fPosition.Begin(); fNormal.Begin(); } + void Advance() { fPosition.Advance(); fNormal.Advance(); } + hsBool More() const { return fPosition.More(); } +}; + +class plAccPosNormUVWIterator +{ +protected: + plAccIterator fPosition; + plAccIterator fNormal; + plAccIterator fUVW; +public: + plAccPosNormUVWIterator(plAccessVtxSpan* acc) + : fPosition(acc, plAccessVtxSpan::kPosition), + fNormal(acc, plAccessVtxSpan::kNormal), + fUVW(acc, plAccessVtxSpan::kUVW) {} + plAccPosNormUVWIterator() {} + + plAccPosNormUVWIterator& Set(plAccessVtxSpan* acc) + { + fPosition.Set(acc, plAccessVtxSpan::kPosition); + fNormal.Set(acc, plAccessVtxSpan::kNormal); + fUVW.Set(acc, plAccessVtxSpan::kUVW); + return *this; + } + + hsPoint3* Position() const { return fPosition.Value(); } + hsVector3* Normal() const { return fNormal.Value(); } + hsPoint3* UVWs() const { return fUVW.Value(); } + hsPoint3* UVW(int i) const { return fUVW.Value() + i; } + + void Begin() { fPosition.Begin(); fNormal.Begin(); fUVW.Begin(); } + void Advance() { fPosition.Advance(); fNormal.Advance(); fUVW.Advance(); } + hsBool More() const { return fPosition.More(); } +}; + +class plAccUVWIterator +{ + plAccIterator fUVW; +public: + plAccUVWIterator(plAccessVtxSpan* acc) + : fUVW(acc, plAccessVtxSpan::kUVW) {} + plAccUVWIterator() {} + + plAccUVWIterator& Set(plAccessVtxSpan* acc) + { + fUVW.Set(acc, plAccessVtxSpan::kUVW); + return *this; + } + + hsPoint3* UVWs() const { return fUVW.Value(); } + hsPoint3* UVW(int i) const { return fUVW.Value() + i; } + + void Begin() { fUVW.Begin(); } + void Advance() { fUVW.Advance(); } + hsBool More() const { return fUVW.More(); } +}; + +class plAccDiffuseIterator +{ + plAccIterator fDiffuse; +public: + plAccDiffuseIterator(plAccessVtxSpan* acc) + : fDiffuse(acc, plAccessVtxSpan::kDiffuse) {} + plAccDiffuseIterator() {} + + plAccDiffuseIterator& Set(plAccessVtxSpan* acc) + { + fDiffuse.Set(acc, plAccessVtxSpan::kDiffuse); + return *this; + } + + UInt32* Diffuse32() const { return fDiffuse.Value(); } + + hsColorRGBA DiffuseRGBA() const { return hsColorRGBA().FromARGB32(*Diffuse32()); } + + void Begin() { fDiffuse.Begin(); } + void Advance() { fDiffuse.Advance(); } + hsBool More() const { return fDiffuse.More(); } +}; + +class plAccDiffSpecIterator +{ +protected: + plAccIterator fDiffuse; + plAccIterator fSpecular; +public: + plAccDiffSpecIterator(plAccessVtxSpan* acc) + : fDiffuse(acc, plAccessVtxSpan::kDiffuse), + fSpecular(acc, plAccessVtxSpan::kSpecular) {} + plAccDiffSpecIterator() {} + + plAccDiffSpecIterator& Set(plAccessVtxSpan* acc) + { + fDiffuse.Set(acc, plAccessVtxSpan::kDiffuse); + fSpecular.Set(acc, plAccessVtxSpan::kSpecular); + return *this; + } + + UInt32* Diffuse32() const { return fDiffuse.Value(); } + UInt32* Specular32() const { return fSpecular.Value(); } + + hsColorRGBA DiffuseRGBA() const { return hsColorRGBA().FromARGB32(*Diffuse32()); } + hsColorRGBA SpecularRGBA() const { return hsColorRGBA().FromARGB32(*Specular32()); } + + void Begin() { fDiffuse.Begin(); fSpecular.Begin(); } + void Advance() { fDiffuse.Advance(); fSpecular.Advance(); } + hsBool More() const { return fDiffuse.More(); } +}; + + +class plAccVertexIterator +{ +protected: + plAccIterator fPosition; + plAccIterator fWeight; + plAccIterator fWgtIndex; + plAccIterator fNormal; + plAccIterator fDiffuse; + plAccIterator fSpecular; + plAccIterator fUVW; +public: + plAccVertexIterator(plAccessVtxSpan* acc) + : fPosition(acc, plAccessVtxSpan::kPosition), + fWeight(acc, plAccessVtxSpan::kWeight), + fWgtIndex(acc, plAccessVtxSpan::kWgtIndex), + fNormal(acc, plAccessVtxSpan::kNormal), + fDiffuse(acc, plAccessVtxSpan::kDiffuse), + fSpecular(acc, plAccessVtxSpan::kSpecular), + fUVW(acc, plAccessVtxSpan::kUVW) {} + plAccVertexIterator() {} + + plAccVertexIterator& Set(plAccessVtxSpan* acc) + { + fPosition.Set(acc, plAccessVtxSpan::kPosition); + fWeight.Set(acc, plAccessVtxSpan::kWeight); + fWgtIndex.Set(acc, plAccessVtxSpan::kWgtIndex); + fNormal.Set(acc, plAccessVtxSpan::kNormal); + fDiffuse.Set(acc, plAccessVtxSpan::kDiffuse); + fSpecular.Set(acc, plAccessVtxSpan::kSpecular); + fUVW.Set(acc, plAccessVtxSpan::kUVW); + return *this; + } + + hsPoint3* Position() const { return fPosition.Value(); } + hsScalar* Weights() const { return fWeight.Value(); } + hsScalar* Weight(int i) const { return fWeight.Value() + i; } + UInt32* WgtIndices() const { return (UInt32*)(fWgtIndex.Value()); } + UInt8* WgtIndex(int i) const { return fWgtIndex.Value() + i; } + hsVector3* Normal() const { return fNormal.Value(); } + hsPoint3* UVWs() const { return fUVW.Value(); } + hsPoint3* UVW(int i) const { return fUVW.Value() + i; } + + UInt32* Diffuse32() const { return fDiffuse.Value(); } + UInt32* Specular32() const { return fSpecular.Value(); } + + hsColorRGBA DiffuseRGBA() const { return hsColorRGBA().FromARGB32(*Diffuse32()); } + hsColorRGBA SpecularRGBA() const { return hsColorRGBA().FromARGB32(*Specular32()); } + + void Begin() { fPosition.Begin(); fWeight.Begin(); fNormal.Begin(); fDiffuse.Begin(); fSpecular.Begin(); fUVW.Begin(); } + void Advance() { fPosition.Advance(); fWeight.Advance(); fNormal.Advance(); fDiffuse.Begin(); fSpecular.Begin(); fUVW.Advance(); } + hsBool More() const { return fPosition.More(); } +}; + + +#endif // plAccessVtxSpan_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plActivePrintShape.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plActivePrintShape.cpp new file mode 100644 index 00000000..c11061b4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plActivePrintShape.cpp @@ -0,0 +1,114 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plActivePrintShape.h" + +#include "../plMessage/plRippleShapeMsg.h" + +#include "hsStream.h" +#include "hsResMgr.h" + +#include "plgDispatch.h" +#include "../pnMessage/plTimeMsg.h" + +plActivePrintShape::plActivePrintShape() +: fShapeMsg(nil) +{ +} + +plActivePrintShape::~plActivePrintShape() +{ + delete fShapeMsg; +} + +void plActivePrintShape::Read(hsStream* stream, hsResMgr* mgr) +{ + plPrintShape::Read(stream, mgr); + + UInt32 n = stream->ReadSwap32(); + fDecalMgrs.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + fDecalMgrs[i] = mgr->ReadKey(stream); + + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +void plActivePrintShape::Write(hsStream* stream, hsResMgr* mgr) +{ + plPrintShape::Write(stream, mgr); + + stream->WriteSwap32(fDecalMgrs.GetCount()); + int i; + for( i = 0; i < fDecalMgrs.GetCount(); i++ ) + mgr->WriteKey(stream, fDecalMgrs[i]); +} + + // Export construction +void plActivePrintShape::AddDecalKey(const plKey& k) +{ + fDecalMgrs.Append(k); +} + +hsBool plActivePrintShape::MsgReceive(plMessage* msg) +{ + plEvalMsg* eval = plEvalMsg::ConvertNoRef(msg); + if( eval ) + { + return INotify(); + } + + return plPrintShape::MsgReceive(msg); +} + +hsBool plActivePrintShape::INotify() +{ + if( !fShapeMsg ) + ISetupShapeMsg(); + + if( fDecalMgrs.GetCount() ) + { + fShapeMsg->SetBCastFlag(plMessage::kBCastByExactType, false); + int i; + for( i = 0; i < fDecalMgrs.GetCount(); i++ ) + { + fShapeMsg->ClearReceivers().SendAndKeep(fDecalMgrs[i]); + } + } + else + { + fShapeMsg->SetBCastFlag(plMessage::kBCastByExactType, true); + fShapeMsg->SendAndKeep(); + } + return true; +} + +plRippleShapeMsg* plActivePrintShape::ISetupShapeMsg() +{ + fShapeMsg = TRACKED_NEW plRippleShapeMsg(nil, this); + return fShapeMsg; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plActivePrintShape.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plActivePrintShape.h new file mode 100644 index 00000000..6ea3a0d3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plActivePrintShape.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plActivePrintShape_inc +#define plActivePrintShape_inc + +#include "plPrintShape.h" + +class plRippleShapeMsg; + +class plActivePrintShape : public plPrintShape +{ +protected: + hsTArray fDecalMgrs; + + plRippleShapeMsg* fShapeMsg; + + plRippleShapeMsg* ISetupShapeMsg(); + hsBool INotify(); + +public: + plActivePrintShape(); + virtual ~plActivePrintShape(); + + CLASSNAME_REGISTER( plActivePrintShape ); + GETINTERFACE_ANY( plActivePrintShape, plPrintShape ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + // Export construction + void AddDecalKey(const plKey& k); + +}; + +#endif // plActivePrintShape_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAuxSpan.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAuxSpan.h new file mode 100644 index 00000000..0a4d79d2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAuxSpan.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAuxSpan_inc +#define plAuxSpan_inc + + +#include "hsTemplates.h" +#include "plSpanTypes.h" +#include "../plPipeline/plGBufferGroup.h" + +class plDrawableSpans; +class hsGMaterial; +class plGBufferGroup; +class plDecalVtxFormat; + +class plAuxSpan +{ +public: + enum + { + kRTLit = 0x1, + kOverrideLiteModel = 0x2, + kAttenColor = 0x4, + kWorldSpace = 0x8, + kVertexShader = 0x10 + }; + + void* fOwner; + + plDrawableSpans* fDrawable; + UInt32 fBaseSpanIdx; + hsGMaterial* fMaterial; + UInt32 fFlags; + + hsTArray fOrigPos; + hsTArray fOrigUVW; + + plGBufferGroup* fGroup; // Which buffer group, i.e. which vertex format + + UInt32 fVBufferIdx; // Which vertex buffer in group + UInt32 fCellIdx; // Cell index inside the vertex buffer + UInt32 fCellOffset; // Offset inside the cell + UInt32 fVStartIdx; // Start vertex # in the actual interlaced buffer + UInt32 fVLength; // Length of this span in the buffer + + UInt32 fVBufferInit; + UInt32 fVBufferLimit; + + UInt32 fIBufferIdx; // Which index buffer in group + UInt32 fIStartIdx; // Redundant, since all spans are contiguous. Here for debugging + UInt32 fILength; // Length of this span in the buffer + + UInt32 fIBufferInit; + UInt32 fIBufferLimit; + + plDecalVtxFormat* GetBaseVtxPtr() const + { + plGBufferGroup* grp = fGroup; + plGBufferCell* cell = grp->GetCell(fVBufferIdx, fCellIdx); + + UInt8* ptr = grp->GetVertBufferData(fVBufferIdx); + + ptr += cell->fVtxStart + fCellOffset; + + return (plDecalVtxFormat*)ptr; + } + UInt16* GetBaseIdxPtr() const + { + plGBufferGroup* grp = fGroup; + + return grp->GetIndexBufferData(fIBufferIdx) + fIBufferInit; + } + +}; + +#endif // plAuxSpan_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAvMeshSmooth.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAvMeshSmooth.cpp new file mode 100644 index 00000000..12c2e90c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAvMeshSmooth.cpp @@ -0,0 +1,270 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plAvMeshSmooth.h" + +#include "plGeometrySpan.h" +#include "plAccessGeometry.h" +#include "plAccessTriSpan.h" + +#include "hsFastMath.h" + +class EdgeBin +{ +public: + UInt16 fVtx; + UInt16 fCount; + + EdgeBin() : fVtx(0), fCount(0) {} +}; + +void plAvMeshSmooth::FindEdges(UInt32 maxVtxIdx, UInt32 nTris, UInt16* idxList, hsTArray& edgeVerts) +{ + hsTArray* bins = TRACKED_NEW hsTArray[maxVtxIdx+1]; + + hsBitVector edgeVertBits; + // For each vert pair (edge) in idxList + int i; + for( i = 0; i < nTris; i++ ) + { + int j; + for( j = 0; j < 3; j++ ) + { + int jPlus = j < 2 ? j+1 : 0; + int idx0 = idxList[i*3 + j]; + int idx1 = idxList[i*3 + jPlus]; + + int lo, hi; + + // Look in the LUT for the lower index. + if( idx0 < idx1 ) + { + lo = idx0; + hi = idx1; + } + else + { + lo = idx1; + hi = idx0; + } + + hsTArray& loBin = bins[lo]; + // In that bucket, look for the higher index. + int k; + for( k = 0; k < loBin.GetCount(); k++ ) + { + if( loBin[k].fVtx == hi ) + break; + } + + // If we find it, increment it's count, + // else add it. + if( k < loBin.GetCount() ) + { + loBin[k].fCount++; + } + else + { + EdgeBin* b = loBin.Push(); + b->fVtx = hi; + b->fCount = 1; + } + } + } + + // For each bucket in the LUT, + for( i = 0; i < maxVtxIdx+1; i++ ) + { + hsTArray& loBin = bins[i]; + // For each higher index + int j; + for( j = 0; j < loBin.GetCount(); j++ ) + { + // If the count is one, it's an edge, so set the edge bit for both indices (hi and lo) + if( 1 == loBin[j].fCount ) + { + edgeVertBits.SetBit(i); + edgeVertBits.SetBit(loBin[j].fVtx); + } + } + } + + // Now translate the bitvector to a list of indices. + for( i = 0; i < maxVtxIdx+1; i++ ) + { + if( edgeVertBits.IsBitSet(i) ) + edgeVerts.Append(i); + } + delete [] bins; +} + +void plAvMeshSmooth::FindEdges(hsTArray& spans, hsTArray* edgeVerts) +{ + int i; + for( i = 0; i < spans.GetCount(); i++ ) + { + fAccGeom.AccessSpanFromGeometrySpan(spans[i].fAccSpan, spans[i].fSpan); + if( !spans[i].fAccSpan.HasAccessTri() ) + continue; + + plAccessTriSpan& triSpan = spans[i].fAccSpan.AccessTri(); + + UInt32 nTris = triSpan.TriCount(); + UInt16* idxList = triSpan.fTris; + UInt32 maxVertIdx = triSpan.VertCount()-1; + + FindEdges(maxVertIdx, nTris, idxList, edgeVerts[i]); + } +} + +// A little note about why we need to pass in so much to do this. +// If the input geometryspans were in local space (ForceLocal), then +// all we would need to do is ignore any transforms they might have, +// and life is grand. +// But for reasons I don't pretend to understand, we can't do that, so +// here, to smooth the delta meshes, we transform both them and the base "snap-to" +// meshes into their respective local spaces, and then look for matches. This works +// because the base and delta meshes are constrained, not to be coincident in world space, +// but to be coincident in the local space relative to Max pivot. +// The funny painful thing is that later, when we go to use these smoothed delta meshes, +// again we need to coerce them into a neutral space. At that time, we'll use the +// morph target mesh's local space. Whatever. +void plAvMeshSmooth::Smooth(hsTArray& srcSpans, hsTArray& dstSpans) +{ + hsTArray* dstEdgeVerts = TRACKED_NEW hsTArray[dstSpans.GetCount()]; + FindEdges(dstSpans, dstEdgeVerts); + + hsTArray* srcEdgeVerts = TRACKED_NEW hsTArray[srcSpans.GetCount()]; + FindEdges(srcSpans, srcEdgeVerts); + + int i; + for( i = 0; i < dstSpans.GetCount(); i++ ) + { + plAccessTriSpan& dstTriSpan = dstSpans[i].fAccSpan.AccessTri(); + + int j; + for( j = 0; j < dstEdgeVerts[i].GetCount(); j++ ) + { + + hsPoint3 dstPos = IPositionToNeutral(dstSpans[i], dstEdgeVerts[i][j]); + hsVector3 dstNorm = INormalToNeutral(dstSpans[i], dstEdgeVerts[i][j]); + hsColorRGBA dstDiff; + if( dstTriSpan.HasDiffuse() ) + dstDiff = dstTriSpan.DiffuseRGBA(dstEdgeVerts[i][j]); + else + dstDiff.Set(1.f, 1.f, 1.f, 1.f); + + hsScalar maxDot = fMinNormDot; + + hsPoint3 smoothPos = dstPos; + hsVector3 smoothNorm = dstNorm; + hsColorRGBA smoothDiff = dstDiff; + + int k; + for( k = 0; k < srcSpans.GetCount(); k++ ) + { + int m; + for( m = 0; m < srcEdgeVerts[k].GetCount(); m++ ) + { + hsPoint3 srcPos = IPositionToNeutral(srcSpans[k], srcEdgeVerts[k][m]); + hsVector3 srcNorm = INormalToNeutral(srcSpans[k], srcEdgeVerts[k][m]); + + hsScalar dist = hsVector3(&dstPos, &srcPos).MagnitudeSquared(); + if( dist <= fDistTolSq ) + { + smoothPos = srcPos; + + hsScalar currDot = srcNorm.InnerProduct(dstNorm); + if( currDot > maxDot ) + { + maxDot = currDot; + smoothNorm = srcNorm; + if( srcSpans[k].fAccSpan.AccessTri().HasDiffuse() ) + smoothDiff = srcSpans[k].fAccSpan.AccessTri().DiffuseRGBA(srcEdgeVerts[k][m]); + else + smoothDiff = dstDiff; + } + } + } + } + if( fFlags & kSmoothPos ) + dstTriSpan.Position(dstEdgeVerts[i][j]) = IPositionToSpan(dstSpans[i], smoothPos); + if( fFlags & kSmoothNorm ) + dstTriSpan.Normal(dstEdgeVerts[i][j]) = INormalToSpan(dstSpans[i], smoothNorm); + if( (fFlags & kSmoothDiffuse) && dstTriSpan.HasDiffuse() ) + dstTriSpan.Diffuse32(dstEdgeVerts[i][j]) = smoothDiff.ToARGB32(); + } + + } + + delete [] srcEdgeVerts; + delete [] dstEdgeVerts; +} + +hsPoint3 plAvMeshSmooth::IPositionToNeutral(XfmSpan& span, int i) const +{ + return span.fSpanToNeutral * span.fAccSpan.AccessTri().Position(i); +} + +hsVector3 plAvMeshSmooth::INormalToNeutral(XfmSpan& span, int i) const +{ + hsVector3 ret = span.fNormSpanToNeutral * span.fAccSpan.AccessTri().Normal(i); + hsFastMath::Normalize(ret); + return ret; +} + +hsPoint3 plAvMeshSmooth::IPositionToSpan(XfmSpan& span, const hsPoint3& wPos) const +{ + return span.fNeutralToSpan * wPos; +} + +hsVector3 plAvMeshSmooth::INormalToSpan(XfmSpan& span, const hsVector3& wNorm) const +{ + hsVector3 ret = span.fNormNeutralToSpan * wNorm; + hsFastMath::Normalize(ret); + return ret; +} + +void plAvMeshSmooth::SetAngle(hsScalar degs) +{ + fMinNormDot = hsCosine(hsScalarDegToRad(degs)); +} + +hsScalar plAvMeshSmooth::GetAngle() const +{ + return hsScalarRadToDeg(hsACosine(fMinNormDot)); +} + +void plAvMeshSmooth::SetDistTol(hsScalar dist) +{ + fDistTolSq = dist * dist; +} + +hsScalar plAvMeshSmooth::GetDistTol() const +{ + return hsSquareRoot(fDistTolSq); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAvMeshSmooth.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAvMeshSmooth.h new file mode 100644 index 00000000..04e3b0b0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plAvMeshSmooth.h @@ -0,0 +1,92 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAvMeshSmooth_inc +#define plAvMeshSmooth_inc + +#include "hsTemplates.h" +#include "plAccessGeometry.h" +#include "plAccessSpan.h" + +struct hsPoint3; +struct hsVector3; +class plGeometrySpan; + +class plAvMeshSmooth +{ +public: + enum { + kNone = 0x0, + kSmoothNorm = 0x1, + kSmoothPos = 0x2, + kSmoothDiffuse = 0x4 + }; + + class XfmSpan + { + public: + plGeometrySpan* fSpan; + hsMatrix44 fSpanToNeutral; + hsMatrix44 fNormSpanToNeutral; // == Transpose(Inverse(fSpanToNeutral)) == Transpose(fNeutralToSpan) + + hsMatrix44 fNeutralToSpan; + hsMatrix44 fNormNeutralToSpan; // == Transpose(Inverse(fNeutralToSpan)) == Transpose(fSpanToNeutral) + + plAccessSpan fAccSpan; + }; + +protected: + UInt32 fFlags; + + hsScalar fMinNormDot; + hsScalar fDistTolSq; + + plAccessGeometry fAccGeom; + + hsPoint3 IPositionToNeutral(XfmSpan& span, int i) const; + hsVector3 INormalToNeutral(XfmSpan& span, int i) const; + hsPoint3 IPositionToSpan(XfmSpan& span, const hsPoint3& wPos) const; + hsVector3 INormalToSpan(XfmSpan& span, const hsVector3& wNorm) const; + + void FindEdges(UInt32 maxVtxIdx, UInt32 nTris, UInt16* idxList, hsTArray& edgeVerts); + void FindEdges(hsTArray& spans, hsTArray* edgeVerts); + +public: + plAvMeshSmooth() : fFlags(kSmoothNorm), fMinNormDot(0.25f), fDistTolSq(1.e-4f), fAccGeom() {} + + void SetAngle(hsScalar degs); + hsScalar GetAngle() const; // returns degrees + + void SetDistTol(hsScalar dist); + hsScalar GetDistTol() const; + + void Smooth(hsTArray& srcSpans, hsTArray& dstSpans); + + void SetFlags(UInt32 f) { fFlags = f; } + UInt32 GetFlags() const { return fFlags; } +}; + +#endif // plAvMeshSmooth_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCluster.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCluster.cpp new file mode 100644 index 00000000..ecc8318d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCluster.cpp @@ -0,0 +1,196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsStream.h" +#include "plCluster.h" + +#include "plSpanTemplate.h" +#include "plSpanInstance.h" + +#include "hsFastMath.h" + +plCluster::plCluster() +: fGroup(nil) +{ +} + +plCluster::~plCluster() +{ + int i; + for( i = 0; i < fInsts.GetCount(); i++ ) + delete fInsts[i]; +} + +void plCluster::Read(hsStream* s, plClusterGroup* grp) +{ + fGroup = grp; + + fEncoding.Read(s); + + hsAssert(fGroup->GetTemplate(), "Template should have been loaded by now"); + const int numVerts = fGroup->GetTemplate()->NumVerts(); + const int numInst = s->ReadSwap32(); + fInsts.SetCount(numInst); + int i; + for( i = 0; i < numInst; i++ ) + { + fInsts[i] = TRACKED_NEW plSpanInstance; + fInsts[i]->Read(s, fEncoding, numVerts); + } + +} + +void plCluster::Write(hsStream* s) const +{ + fEncoding.Write(s); + + const int numVerts = fGroup->GetTemplate()->NumVerts(); + s->WriteSwap32(fInsts.GetCount()); + int i; + for( i = 0; i < fInsts.GetCount(); i++ ) + { + fInsts[i]->Write(s, fEncoding, numVerts); + } +} + +inline void inlTESTPOINT(const hsPoint3& destP, + hsScalar& minX, hsScalar& minY, hsScalar& minZ, + hsScalar& maxX, hsScalar& maxY, hsScalar& maxZ) +{ + if( destP.fX < minX ) + minX = destP.fX; + else if( destP.fX > maxX ) + maxX = destP.fX; + + if( destP.fY < minY ) + minY = destP.fY; + else if( destP.fY > maxY ) + maxY = destP.fY; + + if( destP.fZ < minZ ) + minZ = destP.fZ; + else if( destP.fZ > maxZ ) + maxZ = destP.fZ; +} + +void plCluster::UnPack(UInt8* vDst, UInt16* iDst, int idxOffset, hsBounds3Ext& wBnd) const +{ + hsScalar minX = 1.e33f; + hsScalar minY = 1.e33f; + hsScalar minZ = 1.e33f; + + hsScalar maxX = -1.e33f; + hsScalar maxY = -1.e33f; + hsScalar maxZ = -1.e33f; + + hsAssert(fGroup->GetTemplate(), "Can't unpack without a template"); + const plSpanTemplate& templ = *fGroup->GetTemplate(); + int i; + for( i = 0; i < fInsts.GetCount(); i++ ) + { + // First, just copy our template, offsetting by prescribed amount. + const UInt16* iSrc = templ.IndexData(); + int n = templ.NumIndices(); + while( n-- ) + { + *iDst = *iSrc + idxOffset; + iDst++; + iSrc++; + } + idxOffset += templ.NumVerts(); + + memcpy(vDst, templ.VertData(), templ.VertSize()); + + // Now we need to fix it up. That means, + // a) Possibly adding a delta to the position. + // b) Transforming the position and normal. + // c) Possibly overwriting some (or all) of the color. + + // If we have individual position and/or color info, apply + // it, along with the transform. + if( GetInst(i).HasPosDelta() || GetInst(i).HasColor() ) + { + const int posOff = templ.PositionOffset(); + const int normOff = templ.NormalOffset(); + const int colOff = templ.ColorOffset(); + const int stride = templ.Stride(); + + const hsMatrix44 l2w = GetInst(i).LocalToWorld(); + hsMatrix44 w2l; + GetInst(i).WorldToLocal().GetTranspose(&w2l); + + plSpanInstanceIter iter(fInsts[i], fEncoding, templ.NumVerts()); + const int numVerts = templ.NumVerts(); + int iVert; + for( iVert = 0, iter.Begin(); iVert < numVerts; iVert++, iter.Advance() ) + { + hsPoint3* pos = (hsPoint3*)(vDst + posOff); + *pos = iter.Position(*pos); + *pos = l2w * *pos; + inlTESTPOINT(*pos, minX, minY, minZ, maxX, maxY, maxZ); + + hsVector3* norm = (hsVector3*)(vDst + normOff); + *norm = w2l * *norm; + hsFastMath::NormalizeAppr(*norm); + + UInt32* color = (UInt32*)(vDst + colOff); + *color = iter.Color(*color); + + vDst += stride; + } + } + else + { + // Just transform the position and normal. + const int posOff = templ.PositionOffset(); + const int normOff = templ.NormalOffset(); + const int stride = templ.Stride(); + + const hsMatrix44 l2w = GetInst(i).LocalToWorld(); + hsMatrix44 w2l; + GetInst(i).WorldToLocal().GetTranspose(&w2l); + + const int numVerts = templ.NumVerts(); + int iVert; + for( iVert = 0; iVert < numVerts; iVert++ ) + { + hsPoint3* pos = (hsPoint3*)(vDst + posOff); + *pos = l2w * *pos; + inlTESTPOINT(*pos, minX, minY, minZ, maxX, maxY, maxZ); + + hsVector3* norm = (hsVector3*)(vDst + normOff); + *norm = w2l * *norm; + + vDst += stride; + } + } + } + wBnd.Reset(&hsPoint3(minX, minY, minZ)); + wBnd.Union(&hsPoint3(maxX, maxY, maxZ)); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCluster.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCluster.h new file mode 100644 index 00000000..4c88af95 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCluster.h @@ -0,0 +1,94 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#ifndef plCluster_inc +#define plCluster_inc + +#include "hsTemplates.h" + +#include "plClusterGroup.h" +#include "plSpanInstance.h" + +class hsGMaterial; +class hsStream; +class plSpanInstance; +class hsKeyedObject; +class plLightInfo; +class plSpanTemplate; +class plVisRegion; +class hsBounds3Ext; + +class plCluster +{ +public: + enum + { + kNoIdx = UInt8(-1) + }; +protected: + plClusterGroup* fGroup; + + hsTArray fInsts; + + plSpanEncoding fEncoding; + + friend class plClusterUtil; + plSpanInstance* IGetInst(int i) const { return fInsts[i]; } + void IAddInst(plSpanInstance* inst) { fInsts.Append(inst); } +public: + + plCluster(); + ~plCluster(); + + void Read(hsStream* s, plClusterGroup* grp); + void Write(hsStream* s) const; + + UInt32 NumInsts() const { return fInsts.GetCount(); } + const plSpanInstance& GetInst(int i) const { return *fInsts[i]; } + + void UnPack(UInt8* vDst, UInt16* iDst, int idxOffset, hsBounds3Ext& wBnd) const; + + // Getters and setters, mostly for export construction. + const plSpanTemplate* GetTemplate() const { return fGroup->GetTemplate(); } + + void SetEncoding(const plSpanEncoding& c) { fEncoding = c; } + plSpanEncoding GetEncoding() const { return fEncoding; } + + plClusterGroup* GetGroup() const { return fGroup; } + void SetGroup(plClusterGroup* g) { fGroup = g; } + + hsGMaterial* GetMaterial() const { return fGroup->GetMaterial(); } + + const hsBitVector& GetVisSet() const { return fGroup->GetVisSet(); } + const hsBitVector& GetVisNot() const { return fGroup->GetVisNot(); } + + const hsTArray& GetLights() const { fGroup->GetLights(); } + + const plLODDist& GetLOD() const { return fGroup->GetLOD(); } +}; + +#endif // plCluster_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plClusterGroup.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plClusterGroup.cpp new file mode 100644 index 00000000..a22166e6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plClusterGroup.cpp @@ -0,0 +1,327 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plClusterGroup.h" + +#include "plSpanTemplate.h" +#include "plCluster.h" + +#include "../pnMessage/plTimeMsg.h" + +#include "../plScene/plVisMgr.h" +#include "../plScene/plVisRegion.h" + +#include "../plSurface/hsGMaterial.h" + +#include "../plGLight/plLightInfo.h" + +#include "plDrawableSpans.h" + +#include "hsBitVector.h" +#include "hsStream.h" +#include "hsResMgr.h" + +//STUB +#include "plgDispatch.h" +#include "../plMessage/plAgeLoadedMsg.h" + +plClusterGroup::plClusterGroup() +: fSceneNode(nil), + fDrawable(nil), + fTemplate(nil), + fMaterial(nil), + fUnPacked(0) +{ + fVisSet.SetBit(plVisMgr::kNormal); +} + +plClusterGroup::~plClusterGroup() +{ + int i; + for( i = 0; i < fClusters.GetCount(); i++ ) + delete fClusters[i]; + + delete fTemplate; +} + +plCluster* plClusterGroup::IAddCluster() +{ + plCluster* cluster = TRACKED_NEW plCluster; + // Set the cluster's group. + cluster->SetGroup(this); + fClusters.Append(cluster); + return cluster; +} + +plCluster* plClusterGroup::IGetCluster(int i) const +{ + return fClusters[i]; +} + +const plCluster* plClusterGroup::GetCluster(int i) const +{ + return fClusters[i]; +} + +void plClusterGroup::Read(hsStream* stream, hsResMgr* mgr) +{ + hsKeyedObject::Read(stream, mgr); + + int i; + + fTemplate = TRACKED_NEW plSpanTemplate; + fTemplate->Read(stream); + + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefMaterial), plRefFlags::kActiveRef); + + const int numClust = stream->ReadSwap32(); + fClusters.SetCount(numClust); + for( i = 0; i < numClust; i++ ) + { + fClusters[i] = TRACKED_NEW plCluster; + fClusters[i]->Read(stream, this); + } + + const int numRegions = stream->ReadSwap32(); + for( i = 0; i < numRegions; i++ ) + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefRegion), plRefFlags::kActiveRef); + + const int numLights = stream->ReadSwap32(); + for( i = 0; i < numLights; i++ ) + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefLight), plRefFlags::kActiveRef); + + fLOD.Read(stream); + + fRenderLevel.Set(stream->ReadSwap32()); + + fSceneNode = mgr->ReadKey(stream); + + //STUB + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +void plClusterGroup::Write(hsStream* stream, hsResMgr* mgr) +{ + hsKeyedObject::Write(stream, mgr); + + int i; + + fTemplate->Write(stream); + + mgr->WriteKey(stream, fMaterial); + + stream->WriteSwap32(fClusters.GetCount()); + for( i = 0; i < fClusters.GetCount(); i++ ) + fClusters[i]->Write(stream); + + stream->WriteSwap32(fRegions.GetCount()); + for( i = 0; i < fRegions.GetCount(); i++ ) + mgr->WriteKey(stream, fRegions[i]); + + stream->WriteSwap32(fLights.GetCount()); + for( i = 0; i < fLights.GetCount(); i++ ) + mgr->WriteKey(stream, fLights[i]); + + fLOD.Write(stream); + + stream->WriteSwap32(fRenderLevel.Level()); + + mgr->WriteKey(stream, fSceneNode); +} + +void plClusterGroup::ISendToSelf(RefType t, hsKeyedObject* ref) +{ + hsAssert(ref, "Sending self a nil object"); + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, t); + hsgResMgr::ResMgr()->SendRef(ref->GetKey(), refMsg, plRefFlags::kActiveRef); +} + +hsBool plClusterGroup::IAddVisRegion(plVisRegion* reg) +{ + if( reg ) + { + int idx = fRegions.Find(reg); + if( idx == fRegions.kMissingIndex ) + { + fRegions.Append(reg); + if( reg->GetProperty(plVisRegion::kIsNot) ) + fVisNot.SetBit(reg->GetIndex()); + else + { + fVisSet.SetBit(reg->GetIndex()); + if( reg->ReplaceNormal() ) + fVisSet.ClearBit(plVisMgr::kNormal); + } + } + } + return true; +} + +hsBool plClusterGroup::IRemoveVisRegion(plVisRegion* reg) +{ + if( reg ) + { + int idx = fRegions.Find(reg); + if( fRegions.kMissingIndex != idx ) + { + fRegions.Remove(idx); + if( reg->GetProperty(plVisRegion::kIsNot) ) + fVisNot.ClearBit(reg->GetIndex()); + else + fVisSet.ClearBit(reg->GetIndex()); + } + } + return true; +} + +hsBool plClusterGroup::IAddLight(plLightInfo* li) +{ + int idx = fLights.Find(li); + if( fLights.kMissingIndex == idx ) + { + fLights.Append(li); + } + return true; +} + +hsBool plClusterGroup::IRemoveLight(plLightInfo* li) +{ + int idx = fLights.Find(li); + if( fLights.kMissingIndex != idx ) + { + fLights.Remove(idx); + } + return true; +} + +hsBool plClusterGroup::IOnReceive(plGenRefMsg* ref) +{ + switch( ref->fType ) + { + case kRefMaterial: + fMaterial = hsGMaterial::ConvertNoRef(ref->GetRef()); + return true; + case kRefRegion: + return IAddVisRegion(plVisRegion::ConvertNoRef(ref->GetRef())); + case kRefLight: + return IAddLight(plLightInfo::ConvertNoRef(ref->GetRef())); + } + return false; +} + +hsBool plClusterGroup::IOnRemove(plGenRefMsg* ref) +{ + int idx = -1; + switch( ref->fType ) + { + case kRefMaterial: + fMaterial = nil; + return true; + case kRefRegion: + return IRemoveVisRegion(plVisRegion::ConvertNoRef(ref->GetRef())); + case kRefLight: + return IRemoveLight(plLightInfo::ConvertNoRef(ref->GetRef())); + } + return false; +} + +hsBool plClusterGroup::IOnRef(plGenRefMsg* ref) +{ + if( ref->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + return IOnReceive(ref); + } + + return IOnRemove(ref); +} + +hsBool plClusterGroup::MsgReceive(plMessage* msg) +{ + plGenRefMsg* ref = plGenRefMsg::ConvertNoRef(msg); + if( ref ) + { + if( IOnRef(ref) ) + return true; + } + + // STUB + plEvalMsg* evalMsg = plEvalMsg::ConvertNoRef(msg); + if (evalMsg) + { + UnPack(); + fUnPacked = true; + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + +void plClusterGroup::UnPack() +{ + plDrawableSpans* drawable = TRACKED_NEW plDrawableSpans; + fDrawable = hsgResMgr::ResMgr()->NewKey(GetKey()->GetName(), drawable, GetKey()->GetUoid().GetLocation()); + drawable->UnPackCluster(this); + + drawable->SetSceneNode(fSceneNode); +} + +void plClusterGroup::SetVisible(bool visible) +{ + if (fDrawable) + { + plDrawableSpans *drawable = plDrawableSpans::ConvertNoRef(fDrawable->ObjectIsLoaded()); + if (drawable) + drawable->SetProperty(0,!visible); // property 0 is the disable drawing property + } +} + +UInt32 plClusterGroup::NumInst() const +{ + UInt32 numInst = 0; + int i; + for( i = 0; i < fClusters.GetCount(); i++ ) + numInst += fClusters[i]->NumInsts(); + + return numInst; +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +void plLODDist::Read(hsStream* s) +{ + fMinDist = s->ReadSwapScalar(); + fMaxDist = s->ReadSwapScalar(); +} + +void plLODDist::Write(hsStream* s) const +{ + s->WriteSwapScalar(fMinDist); + s->WriteSwapScalar(fMaxDist); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plClusterGroup.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plClusterGroup.h new file mode 100644 index 00000000..797f2f55 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plClusterGroup.h @@ -0,0 +1,147 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plClusterGroup_inc +#define plClusterGroup_inc + +#include "hsTemplates.h" +#include "hsBitVector.h" +#include "plRenderLevel.h" +#include "../pnKeyedObject/hsKeyedObject.h" + +class hsStream; +class hsResMgr; +class plSpanTemplate; +class plCluster; +class hsGMaterial; +class plVisRegion; +class plLightInfo; +class plMessage; +class plGenRefMsg; +class plDrawableSpans; + +class plLODDist +{ +public: + hsScalar fMinDist; + hsScalar fMaxDist; + + plLODDist(hsScalar minDist, hsScalar maxDist) : fMinDist(minDist), fMaxDist(maxDist) {} + plLODDist() : fMinDist(0), fMaxDist(0) {} + + plLODDist& Set(float minDist, float maxDist) { fMinDist = minDist; fMaxDist = maxDist; return *this; } + + plLODDist& operator=(int d) { fMinDist = hsScalar(d); fMaxDist = hsScalar(d); return *this; } + + int operator==(const plLODDist& d) const { return (fMinDist == d.fMinDist)&&(fMaxDist == d.fMaxDist); } + + void Read(hsStream* s); + void Write(hsStream* s) const; + +}; + +class plClusterGroup : public hsKeyedObject +{ +public: + enum RefType { + kRefMaterial, + kRefRegion, + kRefLight + }; +protected: + plSpanTemplate* fTemplate; + + hsGMaterial* fMaterial; + + hsTArray fRegions; + hsBitVector fVisSet; + hsBitVector fVisNot; + + hsTArray fLights; + + plLODDist fLOD; + + hsTArray fClusters; + UInt32 fUnPacked; + + plKey fSceneNode; + plKey fDrawable; + + plRenderLevel fRenderLevel; + + hsBool IAddVisRegion(plVisRegion* reg); + hsBool IRemoveVisRegion(plVisRegion* reg); + hsBool IAddLight(plLightInfo* li); + hsBool IRemoveLight(plLightInfo* li); + hsBool IOnRef(plGenRefMsg* ref); + hsBool IOnRemove(plGenRefMsg* ref); + hsBool IOnReceive(plGenRefMsg* ref); + void ISetVisBits(); + void ISendToSelf(RefType t, hsKeyedObject* ref); + + plCluster* IAddCluster(); + plCluster* IGetCluster(int i) const; + + friend class plClusterUtil; +public: + plClusterGroup(); + ~plClusterGroup(); + + CLASSNAME_REGISTER( plClusterGroup ); + GETINTERFACE_ANY( plClusterGroup, hsKeyedObject ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + hsGMaterial* GetMaterial() const { return fMaterial; } + const hsBitVector& GetVisSet() const { return fVisSet; } + const hsBitVector& GetVisNot() const { return fVisNot; } + const hsTArray& GetLights() const { return fLights; } + const plLODDist& GetLOD() const { return fLOD; } + + const plSpanTemplate* GetTemplate() const { return fTemplate; } + + const plCluster* GetCluster(int i) const; + int GetNumClusters() const { return fClusters.GetCount(); } + UInt32 NumInst() const; + + // The drawable needs us to be able to convert our data + // into, well, drawable stuff. + void UnPack(); + + void SetVisible(bool visible=true); + + void SetSceneNode(const plKey& key) { fSceneNode = key; } + plKey GetSceneNode() const { return fSceneNode; } + + plKey GetDrawable() const { return fDrawable; } + + plRenderLevel GetRenderLevel() const { return fRenderLevel; } +}; + +#endif // plClusterGroup_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCutter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCutter.cpp new file mode 100644 index 00000000..8923996b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCutter.cpp @@ -0,0 +1,1371 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plCutter.h" +#include "plAccessSpan.h" +#include "hsFastMath.h" +#include "plAccessGeometry.h" + +#include "hsStream.h" + +// Test hack +#include "plDrawableSpans.h" +#include "plDrawableGenerator.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../plScene/plSceneNode.h" +#include "../plScene/plPageTreeMgr.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" + +void plCutter::Read(hsStream* stream, hsResMgr* mgr) +{ + plCreatable::Read(stream, mgr); + + fLengthU = stream->ReadSwapScalar(); + fLengthV = stream->ReadSwapScalar(); + fLengthW = stream->ReadSwapScalar(); +} + +void plCutter::Write(hsStream* stream, hsResMgr* mgr) +{ + plCreatable::Write(stream, mgr); + + stream->WriteSwapScalar(fLengthU); + stream->WriteSwapScalar(fLengthV); + stream->WriteSwapScalar(fLengthW); +} + + +void plCutter::Set(const hsPoint3& pos, const hsVector3& dir, const hsVector3& out, hsBool flip) +{ + hsVector3 du = dir % out; + hsVector3 dv = out % du; + hsVector3 dw = out; + + hsFastMath::NormalizeAppr(du); + hsFastMath::NormalizeAppr(dv); + hsFastMath::NormalizeAppr(dw); + + fBackDir = dw; + + if( flip ) + du = -du; + + fDirU = du / fLengthU; + fDirV = dv / -fLengthV; + fDirW = dw / fLengthW; + + du *= fLengthU * 0.5f; + dv *= fLengthV * 0.5f; + dw *= fLengthW * 0.5f; + + hsPoint3 corner = pos; + corner += -du; + corner += dv; + corner += -dw; + + fDistU = corner.InnerProduct(fDirU); + fDistV = corner.InnerProduct(fDirV); + fDistW = corner.InnerProduct(fDirW); + + hsMatrix44 l2w; + l2w.NotIdentity(); + int i; + for( i = 0; i < 3; i++ ) + { + l2w.fMap[i][0] = du[i]; + l2w.fMap[i][1] = dv[i]; + l2w.fMap[i][2] = dw[i]; + l2w.fMap[i][3] = pos[i]; + } + l2w.fMap[3][0] = l2w.fMap[3][1] = l2w.fMap[3][2] = 0; + l2w.fMap[3][3] = 1.f; + + + hsPoint3 p; + p.Set(1.f, 1.f, 1.f); + fWorldBounds.Reset(&p); + p.Set(-1.f, -1.f, -1.f); + fWorldBounds.Union(&p); + fWorldBounds.Transform(&l2w); + + fIsect.SetBounds(fWorldBounds); +} + + +inline void plCutter::ISetPosNorm(hsScalar parm, const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const +{ + dst.fPos = outVtx.fPos; + dst.fPos += parm * (inVtx.fPos - outVtx.fPos); + + dst.fNorm = outVtx.fNorm; + dst.fNorm += parm * (inVtx.fNorm - outVtx.fNorm); + + dst.fColor = outVtx.fColor; + dst.fColor += parm * (inVtx.fColor - outVtx.fColor); +} + +// A note on where the interpolation parameter is coming from. +// +// For the lower cases, we're looking for the point where Dot(pos, fDir) - fDist = 0. +// Starting with p = outVtx + parm * (inVtx - outVtx) and Dot(p, fDir) == fDist, we get: +// parm = (fDist - Dot(fDir,outVtx.fPos)) / (Dot(fDir, invVtx.fPos) - Dot(fDir, outVtx.fPos)) +// +// UVW = Dot(fDir, fPos) - fDist +// Dot(fDir, fPos) = UVW + fDist +// So: +// parm = (fDist - (outVtx.fUVW - fDist)) / ((invVtx.fUVW - fDist) - (outVtx.fUVW - fDist)) +// = -outVtx.fUVW / (inVtx.fUVW - outVtx.fUVW) +// = outVtx.fUVW / (outVtx.fUVW - inVtx.fUVW) + +inline void plCutter::ICutoutVtxLoU(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const +{ + hsScalar parm = outVtx.fUVW.fX / (outVtx.fUVW.fX - inVtx.fUVW.fX); + + ISetPosNorm(parm, inVtx, outVtx, dst); + + dst.fUVW.fX = 0; + dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY); + dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ); +} + +inline void plCutter::ICutoutVtxLoV(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const +{ + hsScalar parm = outVtx.fUVW.fY / (outVtx.fUVW.fY - inVtx.fUVW.fY); + + ISetPosNorm(parm, inVtx, outVtx, dst); + + dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX); + dst.fUVW.fY = 0; + dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ); +} + +inline void plCutter::ICutoutVtxLoW(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const +{ + hsScalar parm = outVtx.fUVW.fZ / (outVtx.fUVW.fZ - inVtx.fUVW.fZ); + + ISetPosNorm(parm, inVtx, outVtx, dst); + + dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX); + dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY); + dst.fUVW.fZ = 0; +} + +// Now for the upper cases, we start with Dot(pos, fDir) - fDist = 1.f +// So parm = (fDist + 1.f - Dot(fDir, outVtx.fPos) / (Dot(fDir, invVtx.fPos) - Dot(fDir, outVtx.fPos)) +// and doing the same substitution gets +// parm = (outVtx.fUVW - 1.f) / (outVtx.fUVW - inVtx.fUVW) +inline void plCutter::ICutoutVtxHiU(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const +{ + hsScalar parm = (outVtx.fUVW.fX - 1.f) / (outVtx.fUVW.fX - inVtx.fUVW.fX); + + ISetPosNorm(parm, inVtx, outVtx, dst); + + dst.fUVW.fX = 1.f; + dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY); + dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ); +} + +inline void plCutter::ICutoutVtxHiV(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const +{ + hsScalar parm = (outVtx.fUVW.fY - 1.f) / (outVtx.fUVW.fY - inVtx.fUVW.fY); + + ISetPosNorm(parm, inVtx, outVtx, dst); + + dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX); + dst.fUVW.fY = 1.f; + dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ); +} + +inline void plCutter::ICutoutVtxHiW(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const +{ + hsScalar parm = (outVtx.fUVW.fZ - 1.f) / (outVtx.fUVW.fZ - inVtx.fUVW.fZ); + + ISetPosNorm(parm, inVtx, outVtx, dst); + + dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX); + dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY); + dst.fUVW.fZ = 1.f; +} + + +// Now for the split down the middle cases, we start with Dot(pos, fDir) - fDist = 0.5f +// So parm = (fDist + 0.5f - Dot(fDir, outVtx.fPos) / (Dot(fDir, invVtx.fPos) - Dot(fDir, outVtx.fPos)) +// and doing the same substitution gets +// parm = (outVtx.fUVW - 0.5f) / (outVtx.fUVW - inVtx.fUVW) +inline void plCutter::ICutoutVtxMidU(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const +{ + hsScalar parm = (outVtx.fUVW.fX - 0.5f) / (outVtx.fUVW.fX - inVtx.fUVW.fX); + + ISetPosNorm(parm, inVtx, outVtx, dst); + + dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX); // TEST + + dst.fUVW.fX = 0.5f; + dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY); + dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ); +} + +inline void plCutter::ICutoutVtxMidV(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const +{ + hsScalar parm = (outVtx.fUVW.fY - 0.5f) / (outVtx.fUVW.fY - inVtx.fUVW.fY); + + ISetPosNorm(parm, inVtx, outVtx, dst); + + dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY); // TEST + + dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX); + dst.fUVW.fY = 0.5f; + dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ); +} + +inline void plCutter::ICutoutVtxMidW(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const +{ + hsScalar parm = (outVtx.fUVW.fZ - 0.5f) / (outVtx.fUVW.fZ - inVtx.fUVW.fZ); + + ISetPosNorm(parm, inVtx, outVtx, dst); + + dst.fUVW.fZ = outVtx.fUVW.fZ + parm * (inVtx.fUVW.fZ - outVtx.fUVW.fZ); // TEST + + dst.fUVW.fX = outVtx.fUVW.fX + parm * (inVtx.fUVW.fX - outVtx.fUVW.fX); + dst.fUVW.fY = outVtx.fUVW.fY + parm * (inVtx.fUVW.fY - outVtx.fUVW.fY); + dst.fUVW.fZ = 0.5f; +} + +// IPolyClip +hsBool plCutter::IPolyClip(hsTArray& poly, const hsPoint3 vPos[]) const +{ + static hsTArray accum; + accum.SetCount(0); + + poly[0].fUVW.fX = vPos[0].InnerProduct(fDirU) - fDistU; + poly[0].fUVW.fY = vPos[0].InnerProduct(fDirV) - fDistV; + poly[0].fUVW.fZ = vPos[0].InnerProduct(fDirW) - fDistW; + + poly[1].fUVW.fX = vPos[1].InnerProduct(fDirU) - fDistU; + poly[1].fUVW.fY = vPos[1].InnerProduct(fDirV) - fDistV; + poly[1].fUVW.fZ = vPos[1].InnerProduct(fDirW) - fDistW; + + poly[2].fUVW.fX = vPos[2].InnerProduct(fDirU) - fDistU; + poly[2].fUVW.fY = vPos[2].InnerProduct(fDirV) - fDistV; + poly[2].fUVW.fZ = vPos[2].InnerProduct(fDirW) - fDistW; + + // Try an early out test. + int i; + for( i = 0; i < 3; i++ ) + { + int lo = 1; + int hi = 1; + int j; + for( j = 0; j < 3; j++ ) + { + lo &= poly[j].fUVW[i] <= 0; + hi &= poly[j].fUVW[i] >= 1.f; + } + if( lo || hi ) + { + poly.SetCount(0); + return false; + } + } + + + // First trim to lower bounds. + for( i = 0; i < poly.GetCount(); i++ ) + { + int j = i ? i-1 : poly.GetCount()-1; + + int test = ((poly[i].fUVW.fX < 0) << 1) | (poly[j].fUVW.fX < 0); + switch(test) + { + case 0: + // Both in + // Add this vert to outList + accum.Append(poly[i]); + break; + case 1: + // This in, last out + // Add ClipVert(j, j-1) to outList + // Add this vert to outList + accum.Push(); + ICutoutVtxLoU(poly[i], poly[j], accum[accum.GetCount()-1]); + accum.Append(poly[i]); + break; + case 2: + // This out, last in + // Add ClipVert(j-1, j) to outList + accum.Push(); + ICutoutVtxLoU(poly[j], poly[i], accum[accum.GetCount()-1]); + break; + case 3: + // Both out + break; + } + } + poly.Swap(accum); + accum.SetCount(0); + + for( i = 0; i < poly.GetCount(); i++ ) + { + int j = i ? i-1 : poly.GetCount()-1; + + int test = ((poly[i].fUVW.fY < 0) << 1) | (poly[j].fUVW.fY < 0); + switch(test) + { + case 0: + // Both in + // Add this vert to outList + accum.Append(poly[i]); + break; + case 1: + // This in, last out + // Add ClipVert(j, j-1) to outList + // Add this vert to outList + accum.Push(); + ICutoutVtxLoV(poly[i], poly[j], accum[accum.GetCount()-1]); + accum.Append(poly[i]); + break; + case 2: + // This out, last in + // Add ClipVert(j-1, j) to outList + accum.Push(); + ICutoutVtxLoV(poly[j], poly[i], accum[accum.GetCount()-1]); + break; + case 3: + // Both out + break; + } + } + poly.Swap(accum); + accum.SetCount(0); + + for( i = 0; i < poly.GetCount(); i++ ) + { + int j = i ? i-1 : poly.GetCount()-1; + + int test = ((poly[i].fUVW.fZ < 0) << 1) | (poly[j].fUVW.fZ < 0); + switch(test) + { + case 0: + // Both in + // Add this vert to outList + accum.Append(poly[i]); + break; + case 1: + // This in, last out + // Add ClipVert(j, j-1) to outList + // Add this vert to outList + accum.Push(); + ICutoutVtxLoW(poly[i], poly[j], accum[accum.GetCount()-1]); + accum.Append(poly[i]); + break; + case 2: + // This out, last in + // Add ClipVert(j-1, j) to outList + accum.Push(); + ICutoutVtxLoW(poly[j], poly[i], accum[accum.GetCount()-1]); + break; + case 3: + // Both out + break; + } + } + poly.Swap(accum); + accum.SetCount(0); + + // Now upper bounds + for( i = 0; i < poly.GetCount(); i++ ) + { + int j = i ? i-1 : poly.GetCount()-1; + + int test = ((poly[i].fUVW.fX > 1.f) << 1) | (poly[j].fUVW.fX > 1.f); + switch(test) + { + case 0: + // Both in + // Add this vert to outList + accum.Append(poly[i]); + break; + case 1: + // This in, last out + // Add ClipVert(j, j-1) to outList + // Add this vert to outList + accum.Push(); + ICutoutVtxHiU(poly[i], poly[j], accum[accum.GetCount()-1]); + accum.Append(poly[i]); + break; + case 2: + // This out, last in + // Add ClipVert(j-1, j) to outList + accum.Push(); + ICutoutVtxHiU(poly[j], poly[i], accum[accum.GetCount()-1]); + break; + case 3: + // Both out + break; + } + } + poly.Swap(accum); + accum.SetCount(0); + + for( i = 0; i < poly.GetCount(); i++ ) + { + int j = i ? i-1 : poly.GetCount()-1; + + int test = ((poly[i].fUVW.fY > 1.f) << 1) | (poly[j].fUVW.fY > 1.f); + switch(test) + { + case 0: + // Both in + // Add this vert to outList + accum.Append(poly[i]); + break; + case 1: + // This in, last out + // Add ClipVert(j, j-1) to outList + // Add this vert to outList + accum.Push(); + ICutoutVtxHiV(poly[i], poly[j], accum[accum.GetCount()-1]); + accum.Append(poly[i]); + break; + case 2: + // This out, last in + // Add ClipVert(j-1, j) to outList + accum.Push(); + ICutoutVtxHiV(poly[j], poly[i], accum[accum.GetCount()-1]); + break; + case 3: + // Both out + break; + } + } + poly.Swap(accum); + accum.SetCount(0); + + for( i = 0; i < poly.GetCount(); i++ ) + { + int j = i ? i-1 : poly.GetCount()-1; + + int test = ((poly[i].fUVW.fZ > 1.f) << 1) | (poly[j].fUVW.fZ > 1.f); + switch(test) + { + case 0: + // Both in + // Add this vert to outList + accum.Append(poly[i]); + break; + case 1: + // This in, last out + // Add ClipVert(j, j-1) to outList + // Add this vert to outList + accum.Push(); + ICutoutVtxHiW(poly[i], poly[j], accum[accum.GetCount()-1]); + accum.Append(poly[i]); + break; + case 2: + // This out, last in + // Add ClipVert(j-1, j) to outList + accum.Push(); + ICutoutVtxHiW(poly[j], poly[i], accum[accum.GetCount()-1]); + break; + case 3: + // Both out + break; + } + } + poly.Swap(accum); + accum.SetCount(0); + + return poly.GetCount() > 2; +} + +// IPolyClip +hsBool plCutter::IFindHitPoint(const hsTArray& inPoly, plCutoutHit& hit) const +{ + static hsTArray accum; + static hsTArray poly; + accum.SetCount(0); + + poly = inPoly; + + // First trim to lower bounds. + int i; + for( i = 0; i < poly.GetCount(); i++ ) + { + int j = i ? i-1 : poly.GetCount()-1; + + int test = ((poly[i].fUVW.fX < 0.5f) << 1) | (poly[j].fUVW.fX < 0.5f); + switch(test) + { + case 0: + // Both in + // Add this vert to outList + accum.Append(poly[i]); + break; + case 1: + // This in, last out + // Add ClipVert(j, j-1) to outList + // Add this vert to outList + accum.Push(); + ICutoutVtxMidU(poly[i], poly[j], accum[accum.GetCount()-1]); + accum.Append(poly[i]); + break; + case 2: + // This out, last in + // Add ClipVert(j-1, j) to outList + accum.Push(); + ICutoutVtxMidU(poly[j], poly[i], accum[accum.GetCount()-1]); + break; + case 3: + // Both out + break; + } + } + poly.Swap(accum); + accum.SetCount(0); + + for( i = 0; i < poly.GetCount(); i++ ) + { + int j = i ? i-1 : poly.GetCount()-1; + + int test = ((poly[i].fUVW.fY < 0.5f) << 1) | (poly[j].fUVW.fY < 0.5f); + switch(test) + { + case 0: + // Both in + // Add this vert to outList + accum.Append(poly[i]); + break; + case 1: + // This in, last out + // Add ClipVert(j, j-1) to outList + // Add this vert to outList + accum.Push(); + ICutoutVtxMidV(poly[i], poly[j], accum[accum.GetCount()-1]); + accum.Append(poly[i]); + break; + case 2: + // This out, last in + // Add ClipVert(j-1, j) to outList + accum.Push(); + ICutoutVtxMidV(poly[j], poly[i], accum[accum.GetCount()-1]); + break; + case 3: + // Both out + break; + } + } + poly.Swap(accum); + accum.SetCount(0); + + // Now upper bounds + for( i = 0; i < poly.GetCount(); i++ ) + { + int j = i ? i-1 : poly.GetCount()-1; + + int test = ((poly[i].fUVW.fX > 0.5f) << 1) | (poly[j].fUVW.fX > 0.5f); + switch(test) + { + case 0: + // Both in + // Add this vert to outList + accum.Append(poly[i]); + break; + case 1: + // This in, last out + // Add ClipVert(j, j-1) to outList + // Add this vert to outList + accum.Push(); + ICutoutVtxMidU(poly[i], poly[j], accum[accum.GetCount()-1]); + accum.Append(poly[i]); + break; + case 2: + // This out, last in + // Add ClipVert(j-1, j) to outList + accum.Push(); + ICutoutVtxMidU(poly[j], poly[i], accum[accum.GetCount()-1]); + break; + case 3: + // Both out + break; + } + } + poly.Swap(accum); + accum.SetCount(0); + + for( i = 0; i < poly.GetCount(); i++ ) + { + int j = i ? i-1 : poly.GetCount()-1; + + int test = ((poly[i].fUVW.fY > 0.5f) << 1) | (poly[j].fUVW.fY > 0.5f); + switch(test) + { + case 0: + // Both in + // Add this vert to outList + accum.Append(poly[i]); + break; + case 1: + // This in, last out + // Add ClipVert(j, j-1) to outList + // Add this vert to outList + accum.Push(); + ICutoutVtxMidV(poly[i], poly[j], accum[accum.GetCount()-1]); + accum.Append(poly[i]); + break; + case 2: + // This out, last in + // Add ClipVert(j-1, j) to outList + accum.Push(); + ICutoutVtxMidV(poly[j], poly[i], accum[accum.GetCount()-1]); + break; + case 3: + // Both out + break; + } + } + + // At this point, if we hit, all verts should be identical, interpolated + // into the center of the cutter. + // No verts means no hit. + if( !accum.GetCount() ) + return false; + + if( accum[0].fNorm.InnerProduct(fDirW) < 0 ) + return false; + + hit.fPos = accum[0].fPos; + hit.fNorm = accum[0].fNorm; + + return true; +} + + +hsBool plCutter::FindHitPoints(const hsTArray& src, hsTArray& hits) const +{ + hits.SetCount(0); + + int iPoly; + for( iPoly = 0; iPoly < src.GetCount(); iPoly++ ) + { + hsBool loU = false; + hsBool hiU = false; + hsBool loV = false; + hsBool hiV = false; + + const plCutoutPoly& poly = src[iPoly]; + int iv; + for( iv = 0; iv < poly.fVerts.GetCount(); iv++ ) + { + const hsPoint3& uvw = poly.fVerts[iv].fUVW; + if( uvw.fX < 0.5f ) + loU = true; + else + hiU = true; + if( uvw.fY < 0.5f ) + loV = true; + else + hiV = true; + + if( loU && hiU && loV && hiV ) + { + plCutoutHit hit; + if( IFindHitPoint(poly.fVerts, hit) ) + hits.Append(hit); + break; + } + } + } + + return hits.GetCount() > 0; +} + +hsBool plCutter::FindHitPointsConstHeight(const hsTArray& src, hsTArray& hits, hsScalar height) const +{ + if( FindHitPoints(src, hits) ) + { + int i; + for( i = 0; i < hits.GetCount(); i++ ) + hits[i].fPos.fZ = height; + + return true; + } + + return false; +} + +void plCutter::ICutoutTransformedConstHeight(plAccessSpan& src, hsTArray& dst) const +{ + const hsMatrix44& l2w = src.GetLocalToWorld(); + hsMatrix44 l2wNorm; + src.GetWorldToLocal().GetTranspose(&l2wNorm); + + hsBool baseHasAlpha = 0 != (src.GetMaterial()->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendAlpha); + + plAccTriIterator tri(&src.AccessTri()); + // For each tri + for( tri.Begin(); tri.More(); tri.Advance() ) + { + // Do a polygon clip of tri to box + static hsTArray poly; + poly.SetCount(3); + + // Not sure about this, whether the constant water height should be world space or local. + // We'll leave it in local for now. + const hsVector3 up(0, 0, 1.f); + hsPoint3 vPos[3]; + vPos[0] = l2w * hsPoint3(tri.Position(0).fX, tri.Position(0).fY, src.GetWaterHeight()); + vPos[1] = l2w * hsPoint3(tri.Position(1).fX, tri.Position(1).fY, src.GetWaterHeight()); + vPos[2] = l2w * hsPoint3(tri.Position(2).fX, tri.Position(2).fY, src.GetWaterHeight()); + + poly[0].Init(l2w * hsPoint3(tri.Position(0).fX, tri.Position(0).fY, tri.Position(0).fZ), l2wNorm * up, tri.DiffuseRGBA(0)); + poly[1].Init(l2w * hsPoint3(tri.Position(1).fX, tri.Position(1).fY, tri.Position(1).fZ), l2wNorm * up, tri.DiffuseRGBA(1)); + poly[2].Init(l2w * hsPoint3(tri.Position(2).fX, tri.Position(2).fY, tri.Position(2).fZ), l2wNorm * up, tri.DiffuseRGBA(2)); + + // If we got a polygon + if( IPolyClip(poly, vPos) ) + { + // tessalate the polygon into dst + IConstruct(dst, poly, baseHasAlpha); + } + } +} + +// We usually don't need to do any transform, because the kind of surface you +// would leave prints on tends to be static, with the transform folded into the +// verts. So it's worth having 2 separate versions of the function. +void plCutter::ICutoutTransformed(plAccessSpan& src, hsTArray& dst) const +{ + const hsMatrix44& l2w = src.GetLocalToWorld(); + hsMatrix44 l2wNorm; + src.GetWorldToLocal().GetTranspose(&l2wNorm); + + hsBool baseHasAlpha = 0 != (src.GetMaterial()->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendAlpha); + + plAccTriIterator tri(&src.AccessTri()); + // For each tri + for( tri.Begin(); tri.More(); tri.Advance() ) + { + // Do a polygon clip of tri to box + static hsTArray poly; + poly.SetCount(3); + + hsPoint3 vPos[3]; + vPos[0] = l2w * tri.Position(0); + vPos[1] = l2w * tri.Position(1); + vPos[2] = l2w * tri.Position(2); + + poly[0].Init(vPos[0], l2wNorm * tri.Normal(0), tri.DiffuseRGBA(0)); + poly[1].Init(vPos[1], l2wNorm * tri.Normal(1), tri.DiffuseRGBA(1)); + poly[2].Init(vPos[2], l2wNorm * tri.Normal(2), tri.DiffuseRGBA(2)); + + // If we got a polygon + if( IPolyClip(poly, vPos) ) + { + // tessalate the polygon into dst + IConstruct(dst, poly, baseHasAlpha); + } + } +} + +void plCutter::ICutoutConstHeight(plAccessSpan& src, hsTArray& dst) const +{ + if( !(src.GetLocalToWorld().fFlags & hsMatrix44::kIsIdent) ) + { + ICutoutTransformedConstHeight(src, dst); + return; + } + + hsBool baseHasAlpha = 0 != (src.GetMaterial()->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendAlpha); + + plAccTriIterator tri(&src.AccessTri()); + // For each tri + for( tri.Begin(); tri.More(); tri.Advance() ) + { + // Do a polygon clip of tri to box + static hsTArray poly; + poly.SetCount(3); + + const hsVector3 up(0, 0, 1.f); + + hsPoint3 vPos[3]; + vPos[0].Set(tri.Position(0).fX, tri.Position(0).fY, src.GetWaterHeight()); + vPos[1].Set(tri.Position(1).fX, tri.Position(1).fY, src.GetWaterHeight()); + vPos[2].Set(tri.Position(2).fX, tri.Position(2).fY, src.GetWaterHeight()); + + poly[0].Init(hsPoint3(tri.Position(0).fX, tri.Position(0).fY, tri.Position(0).fZ), up, tri.DiffuseRGBA(0)); + poly[1].Init(hsPoint3(tri.Position(1).fX, tri.Position(1).fY, tri.Position(1).fZ), up, tri.DiffuseRGBA(1)); + poly[2].Init(hsPoint3(tri.Position(2).fX, tri.Position(2).fY, tri.Position(2).fZ), up, tri.DiffuseRGBA(2)); + + // If we got a polygon + if( IPolyClip(poly, vPos) ) + { + // tessalate the polygon into dst + IConstruct(dst, poly, baseHasAlpha); + } + } +} + +// Cutout +void plCutter::Cutout(plAccessSpan& src, hsTArray& dst) const +{ + if( !src.HasAccessTri() ) + return; + + if( src.HasWaterHeight() ) + { + ICutoutConstHeight(src, dst); + return; + } + + if( !(src.GetLocalToWorld().fFlags & hsMatrix44::kIsIdent) ) + { + ICutoutTransformed(src, dst); + return; + } + + hsBool baseHasAlpha = 0 != (src.GetMaterial()->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendAlpha); + + plAccTriIterator tri(&src.AccessTri()); + // For each tri + for( tri.Begin(); tri.More(); tri.Advance() ) + { + // Do a polygon clip of tri to box + static hsTArray poly; + poly.SetCount(3); + + hsPoint3 vPos[3]; + vPos[0] = tri.Position(0); + vPos[1] = tri.Position(1); + vPos[2] = tri.Position(2); + + poly[0].Init(vPos[0], tri.Normal(0), tri.DiffuseRGBA(0)); + poly[1].Init(vPos[1], tri.Normal(1), tri.DiffuseRGBA(1)); + poly[2].Init(vPos[2], tri.Normal(2), tri.DiffuseRGBA(2)); + + // If we got a polygon + if( IPolyClip(poly, vPos) ) + { + // tessalate the polygon into dst + IConstruct(dst, poly, baseHasAlpha); + } + } +} + +void plCutter::IConstruct(hsTArray& dst, hsTArray& poly, hsBool baseHasAlpha) const +{ + int iDst = dst.GetCount(); + dst.Push(); + dst[iDst].fVerts.Swap(poly); + dst[iDst].fBaseHasAlpha = baseHasAlpha; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// + +hsBool plCutter::CutoutGrid(int nWid, int nLen, plFlatGridMesh& grid) const +{ + hsVector3 halfU = fDirU * (fLengthU * fLengthU * 0.5f); + hsVector3 halfV = fDirV * (fLengthV * fLengthV * 0.5f); + return MakeGrid(nWid, nLen, fWorldBounds.GetCenter(), halfU, halfV, grid); +} + +hsBool plCutter::MakeGrid(int nWid, int nLen, const hsPoint3& center, const hsVector3& halfU, const hsVector3& halfV, plFlatGridMesh& grid) +{ + if( nWid < 3 ) + nWid = 3; + if( !(nWid & 0x1) ) + nWid++; + if( nLen < 3 ) + nLen = 3; + if( !(nLen & 0x1) ) + nLen++; + + grid.fVerts.SetCount(nWid * nLen); + + hsVector3 dux = halfU; + hsVector3 dvx = halfV; + dux.fZ = 0; + dvx.fZ = 0; + + hsPoint3 corner = center; + corner.fZ = 0; + corner += -dux; + corner += -dvx; + + hsScalar sWid = 1.f / hsScalar(nWid-1); + hsScalar sLen = 1.f / hsScalar(nLen-1); + + dux *= 2.f * sWid; + dvx *= 2.f * sLen; + + hsScalar du = sWid; + hsScalar dv = sLen; + int j; + for( j = 0; j < nLen; j++ ) + { + int i; + for( i = 0; i < nWid; i++ ) + { + plCutoutMiniVtx& vtx = grid.fVerts[j * nWid + i]; + + vtx.fPos = corner; + vtx.fPos += dux * (hsScalar)i; + vtx.fPos += dvx * (hsScalar)j; + + vtx.fUVW.fX = du * i; + vtx.fUVW.fY = dv * j; + vtx.fUVW.fZ = 0.5f; + } + } + + int idx = 0; + grid.fIdx.SetCount(2 * (nWid-1) * (nLen-1) * 3); + for( j = 1; j < nLen; ) + { + int i; + for( i = 1; i < nWid; ) + { + grid.fIdx[idx++] = j * nWid + (i-1); + grid.fIdx[idx++] = j * nWid + i; + grid.fIdx[idx++] = (j-1) * nWid + (i-1); + + grid.fIdx[idx++] = (j-1) * nWid + (i-1); + grid.fIdx[idx++] = j * nWid + i; + grid.fIdx[idx++] = (j-1) * nWid + i; + + i++; + + grid.fIdx[idx++] = (j-1) * nWid + (i-1); + grid.fIdx[idx++] = j * nWid + (i-1); + grid.fIdx[idx++] = (j-1) * nWid + i; + + grid.fIdx[idx++] = (j-1) * nWid + i; + grid.fIdx[idx++] = j * nWid + (i-1); + grid.fIdx[idx++] = j * nWid + i; + + i++; + } + + j++; + + for( i = 1; i < nWid; ) + { + grid.fIdx[idx++] = (j-1) * nWid + (i-1); + grid.fIdx[idx++] = j * nWid + (i-1); + grid.fIdx[idx++] = (j-1) * nWid + i; + + grid.fIdx[idx++] = (j-1) * nWid + i; + grid.fIdx[idx++] = j * nWid + (i-1); + grid.fIdx[idx++] = j * nWid + i; + + i++; + + grid.fIdx[idx++] = j * nWid + (i-1); + grid.fIdx[idx++] = j * nWid + i; + grid.fIdx[idx++] = (j-1) * nWid + (i-1); + + grid.fIdx[idx++] = (j-1) * nWid + (i-1); + grid.fIdx[idx++] = j * nWid + i; + grid.fIdx[idx++] = (j-1) * nWid + i; + + i++; + } + + j++; + } + + return grid.fIdx.GetCount() > 0; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// Test hackage ensues +///////////////////////////////////////////////////////////////////////////////////////////////////// +void TestCutter(const plKey& key, const hsVector3& size, const hsPoint3& pos) +{ + plCutter cutter; + + cutter.SetLength(size); + + static hsVector3 dir(0, 1.f, 0); + static hsVector3 up(0, 0, 1.f); + + cutter.Set(pos, dir, up); + + plSceneObject* so = plSceneObject::ConvertNoRef(key->ObjectIsLoaded()); + if( !so ) + return; + const plDrawInterface* di = so->GetDrawInterface(); + if( !di ) + return; + + static plDrawableSpans* drawable = nil; + hsBool newDrawable = !drawable; + hsBool haveNormal = true; + + hsTArray retIndex; + + hsTArray src; + plAccessGeometry::Instance()->OpenRO(di, src); + + if( !src.GetCount() ) + return; + + int i; + for( i = 0; i < src.GetCount(); i++ ) + { + + static hsTArray dst; + dst.SetCount(0); +#if 1 + cutter.Cutout(src[i], dst); +#else + hsPoint3 corner; + hsVector3 ax[3]; + cutter.GetWorldBounds().GetCorner(&corner); + cutter.GetWorldBounds().GetAxes(ax+0, ax+1, ax+2); + int iAx = 0; + int jAx = 1; + dst.SetCount(6); + int xx; + for( xx = 0; xx < 3; xx++ ) + { + dst[xx].fVerts.SetCount(4); + + dst[xx].fVerts[0].fPos = corner; + dst[xx].fVerts[0].fNorm.Set(0,0,1.f); + dst[xx].fVerts[0].fUVW.Set(0,0,0); + + dst[xx].fVerts[1].fPos = corner; + dst[xx].fVerts[1].fPos += ax[iAx]; + dst[xx].fVerts[1].fNorm.Set(0,0,1.f); + dst[xx].fVerts[1].fUVW.Set(1,0,0); + + dst[xx].fVerts[2].fPos = corner; + dst[xx].fVerts[2].fPos += ax[iAx]; + dst[xx].fVerts[2].fPos += ax[jAx]; + dst[xx].fVerts[2].fNorm.Set(0,0,1.f); + dst[xx].fVerts[2].fUVW.Set(1.f,1.f,0); + + dst[xx].fVerts[3].fPos = corner; + dst[xx].fVerts[3].fPos += ax[jAx]; + dst[xx].fVerts[3].fNorm.Set(0,0,1.f); + dst[xx].fVerts[3].fUVW.Set(0,1.f,0); + + iAx++; + jAx = iAx > 1 ? 0 : iAx+1; + } + corner += ax[0]; + corner += ax[1]; + corner += ax[2]; + ax[0] = -ax[0]; + ax[1] = -ax[1]; + ax[2] = -ax[2]; + iAx = 0; + jAx = 1; + for( xx = 3; xx < 6; xx++ ) + { + dst[xx].fVerts.SetCount(4); + + dst[xx].fVerts[0].fPos = corner; + dst[xx].fVerts[0].fNorm.Set(0,0,1.f); + dst[xx].fVerts[0].fUVW.Set(0,0,0); + + dst[xx].fVerts[3].fPos = corner; + dst[xx].fVerts[3].fPos += ax[iAx]; + dst[xx].fVerts[3].fNorm.Set(0,0,1.f); + dst[xx].fVerts[3].fUVW.Set(1.f,0,0); + + dst[xx].fVerts[2].fPos = corner; + dst[xx].fVerts[2].fPos += ax[iAx]; + dst[xx].fVerts[2].fPos += ax[jAx]; + dst[xx].fVerts[2].fNorm.Set(0,0,1.f); + dst[xx].fVerts[2].fUVW.Set(1.f,1.f,0); + + dst[xx].fVerts[1].fPos = corner; + dst[xx].fVerts[1].fPos += ax[jAx]; + dst[xx].fVerts[1].fNorm.Set(0,0,1.f); + dst[xx].fVerts[1].fUVW.Set(0,1.f,0); + + iAx++; + jAx = iAx > 1 ? 0 : iAx+1; + } + haveNormal = false; +#endif + + // What's our total number of verts? + // Total number of tris? + int numVerts = 0; + int numTris = 0; + int j; + for( j = 0; j < dst.GetCount(); j++ ) + { + if( dst[j].fVerts.GetCount() ) + { + numVerts += dst[j].fVerts.GetCount(); + numTris += dst[j].fVerts.GetCount()-2; + } + } + if( !numTris ) + continue; + + hsTArray pos; + pos.SetCount(numVerts); + hsTArray norm; + norm.SetCount(numVerts); + hsTArray uvw; + uvw.SetCount(numVerts); + hsTArray col; + col.SetCount(numVerts); + + int iPoly = 0; + int iVert = 0; + int iv; + for( iv = 0; iv < numVerts; iv++ ) + { + pos[iv] = dst[iPoly].fVerts[iVert].fPos; + norm[iv] = dst[iPoly].fVerts[iVert].fNorm; + uvw[iv] = dst[iPoly].fVerts[iVert].fUVW; + col[iv] = dst[iPoly].fVerts[iVert].fColor; + + hsScalar opac = uvw[iv].fZ < 0.25f + ? uvw[iv].fZ * 4.f + : uvw[iv].fZ > 0.75f + ? (1.f - uvw[iv].fZ) * 4.f + : 1.f; + + opac *= norm[iv].fZ; + if( opac < 0 ) + opac = 0; + + if( dst[iPoly].fBaseHasAlpha ) + col[iv].a *= opac; + else + col[iv].a = opac; + + if( ++iVert >= dst[iPoly].fVerts.GetCount() ) + { + iVert = 0; + iPoly++; + } + } + + hsTArray idx; + + UInt16 base = 0; + for( j = 0; j < dst.GetCount(); j++ ) + { + UInt16 next = base+1; + int k; + for( k = 2; k < dst[j].fVerts.GetCount(); k++ ) + { + idx.Append(base); + idx.Append(next++); + idx.Append(next); + } + base = ++next; + } + + drawable = plDrawableGenerator::GenerateDrawable( numVerts, pos.AcquireArray(), + haveNormal ? norm.AcquireArray() : nil, + uvw.AcquireArray(), 1, + col.AcquireArray(), + true, + nil, + idx.GetCount(), idx.AcquireArray(), + src[i].GetMaterial(), + hsMatrix44::IdentityMatrix(), + true, + &retIndex, + drawable); + + } + + if( drawable && newDrawable ) + drawable->SetSceneNode(so->GetSceneNode()); + +} + +void TestCutter2(const plKey& key, const hsVector3& size, const hsPoint3& pos, hsBool flip) +{ + plCutter cutter; + + cutter.SetLength(size); + + static hsVector3 dir(0, 1.f, 0); + static hsVector3 up(0, 0, 1.f); + + cutter.Set(pos, dir, up, flip); + + plSceneObject* so = plSceneObject::ConvertNoRef(key->ObjectIsLoaded()); + if( !so ) + return; + + plSceneNode* node = plSceneNode::ConvertNoRef(so->GetSceneNode()->ObjectIsLoaded()); + if( !node ) + return; + + static plDrawableSpans* drawable = nil; + hsBool newDrawable = !drawable; + hsBool haveNormal = true; + + hsTArray retIndex; + + hsTArray drawVis; + node->Harvest(&cutter.GetIsect(), drawVis); + if( !drawVis.GetCount() ) + return; + + hsTArray src; + + int numSpan = 0; + int iDraw; + for( iDraw = 0; iDraw < drawVis.GetCount(); iDraw++ ) + numSpan += drawVis[iDraw].fVisList.GetCount(); + + src.SetCount(numSpan); + + int i; + + iDraw = 0; + int iSpan = 0; + for( i = 0; i < numSpan; i++ ) + { + plAccessGeometry::Instance()->OpenRO(drawVis[iDraw].fDrawable, drawVis[iDraw].fVisList[iSpan], src[i]); + + if( ++iSpan >= drawVis[iDraw].fVisList.GetCount() ) + { + iDraw++; + iSpan = 0; + } + } + + + for( i = 0; i < src.GetCount(); i++ ) + { + static hsTArray dst; + dst.SetCount(0); + cutter.Cutout(src[i], dst); + + // What's our total number of verts? + // Total number of tris? + int numVerts = 0; + int numTris = 0; + int j; + for( j = 0; j < dst.GetCount(); j++ ) + { + if( dst[j].fVerts.GetCount() ) + { + numVerts += dst[j].fVerts.GetCount(); + numTris += dst[j].fVerts.GetCount()-2; + } + } + if( !numTris ) + continue; + + hsTArray pos; + pos.SetCount(numVerts); + hsTArray norm; + norm.SetCount(numVerts); + hsTArray uvw; + uvw.SetCount(numVerts); + hsTArray col; + col.SetCount(numVerts); + + int iPoly = 0; + int iVert = 0; + int iv; + for( iv = 0; iv < numVerts; iv++ ) + { + pos[iv] = dst[iPoly].fVerts[iVert].fPos; + norm[iv] = dst[iPoly].fVerts[iVert].fNorm; + uvw[iv] = dst[iPoly].fVerts[iVert].fUVW; + col[iv] = dst[iPoly].fVerts[iVert].fColor; + + hsScalar opac = uvw[iv].fZ < 0.25f + ? uvw[iv].fZ * 4.f + : uvw[iv].fZ > 0.75f + ? (1.f - uvw[iv].fZ) * 4.f + : 1.f; + + opac *= norm[iv].fZ; + if( opac < 0 ) + opac = 0; + + if( dst[iPoly].fBaseHasAlpha ) + col[iv].a *= opac; + else + col[iv].a = opac; + + + if( ++iVert >= dst[iPoly].fVerts.GetCount() ) + { + iVert = 0; + iPoly++; + } + } + + hsTArray idx; + + UInt16 base = 0; + for( j = 0; j < dst.GetCount(); j++ ) + { + UInt16 next = base+1; + int k; + for( k = 2; k < dst[j].fVerts.GetCount(); k++ ) + { + idx.Append(base); + idx.Append(next++); + idx.Append(next); + } + base = ++next; + } + + drawable = plDrawableGenerator::GenerateDrawable( numVerts, pos.AcquireArray(), + haveNormal ? norm.AcquireArray() : nil, + uvw.AcquireArray(), 1, + col.AcquireArray(), + false, + nil, + idx.GetCount(), idx.AcquireArray(), + src[i].GetMaterial(), + hsMatrix44::IdentityMatrix(), + true, + &retIndex, + drawable); + + } + + for( i = 0; i < numSpan; i++ ) + { + plAccessGeometry::Instance()->Close(src[i]); + } + + if( drawable && newDrawable ) + drawable->SetSceneNode(so->GetSceneNode()); + +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCutter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCutter.h new file mode 100644 index 00000000..ac7eedc8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plCutter.h @@ -0,0 +1,162 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCutter_inc +#define plCutter_inc + +#include "../pnFactory/plCreatable.h" + +#include "hsGeometry3.h" +#include "hsTemplates.h" +#include "hsBounds.h" +#include "../plIntersect/plVolumeIsect.h" +#include "hsColorRGBA.h" + +struct hsPoint3; +struct hsVector3; + +class plPrintCollect; +class plAccTriIterator; +class plAccessSpan; + +class plCutoutHit +{ +public: + hsPoint3 fPos; + hsVector3 fNorm; +}; + +class plCutoutVtx +{ +public: + plCutoutVtx& Init(const hsPoint3& p, const hsVector3& n, const hsColorRGBA& c) { fPos = p; fNorm = n; fColor = c; return *this; } + + hsPoint3 fPos; + hsVector3 fNorm; + hsColorRGBA fColor; + hsPoint3 fUVW; +}; + +class plCutoutPoly +{ +public: + hsTArray fVerts; + + hsBool fBaseHasAlpha; +}; + +class plCutoutMiniVtx +{ +public: + hsPoint3 fPos; + hsPoint3 fUVW; +}; + +class plFlatGridMesh +{ +public: + hsTArray fVerts; + hsTArray fIdx; + + void Reset() { fVerts.SetCount(0); fIdx.SetCount(0); } +}; + +class plCutter : public plCreatable +{ +protected: + + // Permanent attributes + hsScalar fLengthU; + hsScalar fLengthV; + hsScalar fLengthW; + + // Internal cached stuff + hsScalar fDistU; + hsScalar fDistV; + hsScalar fDistW; + hsVector3 fDirU; + hsVector3 fDirV; + hsVector3 fDirW; + hsVector3 fBackDir; + + hsBounds3Ext fWorldBounds; + plBoundsIsect fIsect; + + void IConstruct(hsTArray& dst, hsTArray& poly, hsBool baseHasAlpha) const; + hsBool IPolyClip(hsTArray& poly, const hsPoint3 vPos[]) const; + + inline void ICutoutVtxHiU(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const; + inline void ICutoutVtxHiV(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const; + inline void ICutoutVtxHiW(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const; + inline void ICutoutVtxLoU(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const; + inline void ICutoutVtxLoV(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const; + inline void ICutoutVtxLoW(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const; + inline void ICutoutVtxMidV(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const; + inline void ICutoutVtxMidU(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const; + inline void ICutoutVtxMidW(const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const; + + hsBool IFindHitPoint(const hsTArray& inPoly, plCutoutHit& hit) const; + + inline void ISetPosNorm(hsScalar parm, const plCutoutVtx& inVtx, const plCutoutVtx& outVtx, plCutoutVtx& dst) const; + + void ICutoutTransformed(plAccessSpan& src, hsTArray& dst) const; + void ICutoutConstHeight(plAccessSpan& src, hsTArray& dst) const; + void ICutoutTransformedConstHeight(plAccessSpan& src, hsTArray& dst) const; + + +public: + plCutter() {} + virtual ~plCutter() {} + + CLASSNAME_REGISTER( plCutter ); + GETINTERFACE_ANY( plCutter, plCreatable ); + + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + hsBool FindHitPoints(const hsTArray& src, hsTArray& hits) const; + hsBool FindHitPointsConstHeight(const hsTArray& src, hsTArray& hits, hsScalar height) const; + + void Set(const hsPoint3& pos, const hsVector3& dir, const hsVector3& out, hsBool flip=false); + + void Cutout(plAccessSpan& src, hsTArray& dst) const; + hsBool CutoutGrid(int nWid, int nLen, plFlatGridMesh& dst) const; + + void SetLength(const hsVector3& s) { fLengthU = s.fX; fLengthV = s.fY; fLengthW = s.fZ; } + hsScalar GetLengthU() const { return fLengthU; } + hsScalar GetLengthV() const { return fLengthV; } + hsScalar GetLengthW() const { return fLengthW; } + + const hsBounds3Ext& GetWorldBounds() const { return fWorldBounds; } + plBoundsIsect& GetIsect() { return fIsect; } + hsVector3 GetBackDir() const { return fBackDir; } + + static hsBool MakeGrid(int nWid, int nLen, const hsPoint3& center, const hsVector3& halfU, const hsVector3& halfV, plFlatGridMesh& grid); + +}; + +#endif // plCutter_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableCreatable.h new file mode 100644 index 00000000..22272ee0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableCreatable.h @@ -0,0 +1,122 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDrawableCreatable_inc +#define plDrawableCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plDrawableSpans.h" + +REGISTER_CREATABLE( plDrawableSpans ); + +#include "plSpaceTree.h" + +REGISTER_CREATABLE( plSpaceTree ); + +#include "plSharedMesh.h" + +REGISTER_CREATABLE( plSharedMesh ); +REGISTER_CREATABLE( plSharedMeshBCMsg ); + +#include "plInstanceDrawInterface.h" + +REGISTER_CREATABLE( plInstanceDrawInterface ); + +#include "plDynaDecalMgr.h" + +REGISTER_NONCREATABLE( plDynaDecalMgr ); + +#include "plDynaFootMgr.h" + +REGISTER_CREATABLE( plDynaFootMgr ); + +#include "plDynaRippleMgr.h" + +REGISTER_CREATABLE( plDynaRippleMgr ); + +#include "plDynaBulletMgr.h" + +REGISTER_CREATABLE( plDynaBulletMgr ); + +#include "plDynaPuddleMgr.h" + +REGISTER_CREATABLE( plDynaPuddleMgr ); + +#include "plDynaTorpedoMgr.h" + +REGISTER_CREATABLE( plDynaTorpedoMgr ); + +#include "plDynaTorpedoVSMgr.h" + +REGISTER_CREATABLE( plDynaTorpedoVSMgr ); + +#include "plDynaWakeMgr.h" + +REGISTER_CREATABLE( plDynaWakeMgr ); + +#include "plCutter.h" + +REGISTER_CREATABLE( plCutter ); + +#include "plPrintShape.h" + +REGISTER_CREATABLE( plPrintShape ); + +#include "plActivePrintShape.h" + +REGISTER_CREATABLE( plActivePrintShape ); + +#include "plWaveSetBase.h" + +REGISTER_NONCREATABLE( plWaveSetBase ); + +#include "plWaveSet7.h" + +REGISTER_CREATABLE( plWaveSet7 ); + +#include "plMorphSequence.h" + +REGISTER_CREATABLE( plMorphSequence ); +REGISTER_CREATABLE( plMorphDataSet ); + +#include "plMorphSequenceSDLMod.h" + +REGISTER_CREATABLE( plMorphSequenceSDLMod ); + +#include "plMorphDelta.h" + +REGISTER_CREATABLE( plMorphDelta ); + +#include "plDynaRippleVSMgr.h" + +REGISTER_CREATABLE( plDynaRippleVSMgr ); + +#include "plClusterGroup.h" + +REGISTER_CREATABLE( plClusterGroup ); + +#endif // plDrawableCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableGenerator.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableGenerator.cpp new file mode 100644 index 00000000..33d337c4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableGenerator.cpp @@ -0,0 +1,749 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDrawableGenerator Class Functions // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// 5.15.2001 mcn - Created. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plDrawableGenerator.h" +#include "plDrawableSpans.h" +#include "plGeometrySpan.h" +#include "hsFastMath.h" +#include "plRenderLevel.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plUoid.h" + +// Making light white and dark black by default, because this is really +// redundant. The handling of what color unlit and fully lit map to is +// encapsulated in the material used to draw the mesh. The caller +// wants illumination values, and can handle on screen contrast +// through the material. mf +hsColorRGBA plDrawableGenerator::fLiteColor = { 1, 1, 1, 1 }; +hsColorRGBA plDrawableGenerator::fDarkColor = { 0.0, 0.0, 0.0, 1 }; + + +//// SetFauxLightColors ////////////////////////////////////////////////////// +// Set the colors for the foux lighting on generated drawables + +void plDrawableGenerator::SetFauxLightColors( hsColorRGBA &lite, hsColorRGBA &dark ) +{ + fLiteColor = lite; + fDarkColor = dark; +} + +//// IQuickShadeVerts //////////////////////////////////////////////////////// +// Quickly shades vertices based on a fake directional light. Good for doing +// faux shadings on proxy objects. + +void plDrawableGenerator::IQuickShadeVerts( UInt32 count, hsVector3 *normals, hsColorRGBA *colors, hsColorRGBA* origColors, const hsColorRGBA* multColor ) +{ + hsVector3 lightDir; + float scale; + + + lightDir.Set( 1, 1, 1 ); + lightDir.Normalize(); + + while( count-- ) + { + scale = ( normals[ count ] * lightDir ); + // pretend there are two opposing directional lights, but the + // one pointing downish is a little stronger. + const hsScalar kReverseLight = -0.8f; + if( scale < 0 ) + scale = kReverseLight * scale; + colors[ count ] = fLiteColor * scale + fDarkColor * ( 1.f - scale ); + if( origColors ) + colors[ count ] *= origColors[ count ]; + if( multColor ) + colors[ count ] *= *multColor; + } +} + +void plDrawableGenerator::IFillSpan( UInt32 vertCount, hsPoint3 *positions, hsVector3 *normals, + hsPoint3 *uvws, UInt32 uvwsPerVtx, + hsColorRGBA *origColors, hsBool fauxShade, const hsColorRGBA* multColor, + UInt32 numIndices, UInt16 *indices, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, + plGeometrySpan* span ) +{ + hsTArray myNormals; + + + /// Calculate normals if we don't have them + if( normals == nil ) + { + int i; + hsVector3 normal, v1, v2; + + myNormals.SetCount( vertCount ); + for( i = 0; i < vertCount; i++ ) + myNormals[ i ].Set( 0, 0, 0 ); + + for( i = 0; i < numIndices; i += 3 ) + { + v1.Set( &positions[ indices[ i + 1 ] ], &positions[ indices[ i ] ] ); + v2.Set( &positions[ indices[ i + 2 ] ], &positions[ indices[ i ] ] ); + + normal = v1 % v2; + myNormals[ indices[ i ] ] += normal; + myNormals[ indices[ i + 1 ] ] += normal; + myNormals[ indices[ i + 2 ] ] += normal; + } + + for( i = 0; i < vertCount; i++ ) + myNormals[ i ].Normalize(); + + normals = myNormals.AcquireArray(); + } + + if( uvws == nil ) + uvwsPerVtx = 0; + span->BeginCreate( material, localToWorld, plGeometrySpan::UVCountToFormat( (UInt8)uvwsPerVtx ) ); + + if( !origColors && !fauxShade ) + span->AddVertexArray( vertCount, positions, normals, nil, uvws, uvwsPerVtx ); + else + { + hsTArray colArray; + + hsColorRGBA* colors; + if( fauxShade ) + { + colArray.SetCount(vertCount); + IQuickShadeVerts( vertCount, normals, colArray.AcquireArray(), origColors, multColor ); + colors = colArray.AcquireArray(); + } + else // just use the origColors + { + colors = origColors; + } + + + hsTArray tempColors; + int i; + UInt8 a, r, g, b; + + + tempColors.SetCount( vertCount ); + for( i = 0; i < vertCount; i++ ) + { + hsColorRGBA *color = &colors[ i ]; + a = (UInt8)( color->a >= 1 ? 255 : color->a <= 0 ? 0 : color->a * 255.0 ); + r = (UInt8)( color->r >= 1 ? 255 : color->r <= 0 ? 0 : color->r * 255.0 ); + g = (UInt8)( color->g >= 1 ? 255 : color->g <= 0 ? 0 : color->g * 255.0 ); + b = (UInt8)( color->b >= 1 ? 255 : color->b <= 0 ? 0 : color->b * 255.0 ); + + tempColors[ i ] = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | ( b ); + } + + span->AddVertexArray( vertCount, positions, normals, tempColors.AcquireArray(), uvws, uvwsPerVtx ); + } + + span->AddIndexArray( numIndices, indices ); + span->EndCreate(); + + +} + +//// RegenerateDrawable //////////////////////////////////////////////////////// +// Static function that refills an existing drawable based on the vertex/index +// data given. That data had better match the data the drawable was first filled +// with (i.e. vertex/index count + +hsBool plDrawableGenerator::RegenerateDrawable( UInt32 vertCount, hsPoint3 *positions, hsVector3 *normals, + hsPoint3 *uvws, UInt32 uvwsPerVtx, + hsColorRGBA *origColors, hsBool fauxShade, const hsColorRGBA* multColor, + UInt32 numIndices, UInt16 *indices, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, + UInt32 diIndex, plDrawableSpans *destDraw ) +{ + plDISpanIndex spanList = destDraw->GetDISpans( diIndex ); + if( spanList.GetCount() != 1 ) + { + hsAssert(false, "Don't know how to distribute this geometry over multiple spans"); + return false; + } + plGeometrySpan* span = destDraw->GetGeometrySpan(spanList[0]); + + if( (span->fNumVerts != vertCount) + ||(span->fNumIndices != numIndices) ) + { + hsAssert(false, "Mismatched data coming in for a refill"); + return false; + } + IFillSpan( vertCount, positions, normals, + uvws, uvwsPerVtx, + origColors, fauxShade, multColor, + numIndices, indices, + material, localToWorld, blended, + span ); + + destDraw->RefreshDISpans( diIndex ); + + return true; +} + + +//// GenerateDrawable //////////////////////////////////////////////////////// +// Static function that creates a new drawable based on the vertex/index +// data given. + +plDrawableSpans *plDrawableGenerator::GenerateDrawable( UInt32 vertCount, hsPoint3 *positions, hsVector3 *normals, + hsPoint3 *uvws, UInt32 uvwsPerVtx, + hsColorRGBA *origColors, hsBool fauxShade, const hsColorRGBA* multColor, + UInt32 numIndices, UInt16 *indices, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, + hsTArray *retIndex, plDrawableSpans *toAddTo ) +{ + plDrawableSpans *newDraw; + hsTArray spanArray; + plGeometrySpan *span; + + // Set up props on the new drawable + if( toAddTo != nil ) + newDraw = toAddTo; + else + { + newDraw = TRACKED_NEW plDrawableSpans; +// newDraw->SetNativeProperty( plDrawable::kPropVolatile, true ); + if( blended ) + { + newDraw->SetRenderLevel(plRenderLevel(plRenderLevel::kBlendRendMajorLevel, plRenderLevel::kDefRendMinorLevel)); + newDraw->SetNativeProperty( plDrawable::kPropSortSpans | plDrawable::kPropSortFaces, true ); + } + + static int nameIdx = 0; + char buff[256]; + sprintf(buff, "%s_%d", "GenDrawable", nameIdx++); + hsgResMgr::ResMgr()->NewKey( buff, newDraw, plLocation::kGlobalFixedLoc ); + } + + // Create a temp plGeometrySpan + spanArray.SetCount( 1 ); + span = spanArray[ 0 ] = TRACKED_NEW plGeometrySpan; + + IFillSpan( vertCount, positions, normals, + uvws, uvwsPerVtx, + origColors, fauxShade, multColor, + numIndices, indices, + material, localToWorld, blended, + span ); + + /// Now add the span to the new drawable, clear up the span's buffers and return! + UInt32 trash = UInt32(-1); + UInt32 idx = newDraw->AppendDISpans( spanArray, trash, false ); + if( retIndex != nil ) + retIndex->Append(idx); + + return newDraw; +} + +//// GenerateSphericalDrawable /////////////////////////////////////////////// + +plDrawableSpans *plDrawableGenerator::GenerateSphericalDrawable( const hsPoint3& pos, hsScalar radius, hsGMaterial *material, + const hsMatrix44 &localToWorld, hsBool blended, + const hsColorRGBA* multColor, + hsTArray *retIndex, plDrawableSpans *toAddTo, + hsScalar qualityScalar ) +{ + hsTArray points; + hsTArray normals; + hsTArray indices; + hsTArray colors; + hsPoint3 point; + hsVector3 normal; + + int i, j, numDivisions, start; + float angle, z, x, y, internRad; + plDrawableSpans *drawable; + + + numDivisions = (int)( radius * qualityScalar / 10.f ); + if( numDivisions < 5 ) + numDivisions = 5; + else if( numDivisions > 30 ) + numDivisions = 30; + + + /// Generate points + for( i = 0; i <= numDivisions; i++ ) + { + angle = (float)i * ( hsScalarPI ) / (float)numDivisions; + hsFastMath::SinCosInRange( angle, internRad, z ); + internRad *= radius; + + for( j = 0; j < numDivisions; j++ ) + { + angle = (float)j * ( 2 * hsScalarPI ) / (float)numDivisions; + hsFastMath::SinCosInRange( angle, x, y ); + + point.Set( pos.fX + x * internRad, pos.fY + y * internRad, pos.fZ + z * radius ); + normal.Set( x * internRad, y * internRad, z * radius ); + normal.Normalize(); + + points.Append( point ); + normals.Append( normal ); + } + } + + /// Generate indices + for( i = 0, start = 0; i < numDivisions; i++, start += numDivisions ) + { + for( j = 0; j < numDivisions - 1; j++ ) + { + indices.Append( start + j ); + indices.Append( start + j + 1 ); + indices.Append( start + j + numDivisions + 1 ); + + indices.Append( start + j ); + indices.Append( start + j + numDivisions + 1 ); + indices.Append( start + j + numDivisions ); + } + + indices.Append( start + j ); + indices.Append( start ); + indices.Append( start + numDivisions ); + + indices.Append( start + j ); + indices.Append( start + numDivisions ); + indices.Append( start + j + numDivisions ); + } + + /// Create a drawable for it + drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), + nil, 0, + nil, true, multColor, + indices.GetCount(), indices.AcquireArray(), + material, localToWorld, blended, retIndex, toAddTo ); + + return drawable; +} + +//// GenerateBoxDrawable ///////////////////////////////////////////////////// + +plDrawableSpans *plDrawableGenerator::GenerateBoxDrawable( hsScalar width, hsScalar height, hsScalar depth, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, + const hsColorRGBA* multColor, + hsTArray *retIndex, plDrawableSpans *toAddTo ) +{ + hsVector3 xVec, yVec, zVec; + hsPoint3 pt; + + + xVec.Set( width, 0, 0 ); + yVec.Set( 0, height, 0 ); + zVec.Set( 0, 0, depth ); + + pt.Set( -width / 2.f, -height / 2.f, -depth / 2.f ); + + return GenerateBoxDrawable( pt, xVec, yVec, zVec, material, localToWorld, blended, multColor, retIndex, toAddTo ); +} + +//// GenerateBoxDrawable ///////////////////////////////////////////////////// +// Version that takes a corner and three vectors, for x, y and z edges. + +#define CALC_NORMAL( nA, xVec, yVec, zVec ) { hsVector3 n = (xVec) + (yVec) + (zVec); n = -n; n.Normalize(); nA.Append( n ); } + +plDrawableSpans *plDrawableGenerator::GenerateBoxDrawable( const hsPoint3 &corner, const hsVector3 &xVec, const hsVector3 &yVec, const hsVector3 &zVec, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, + const hsColorRGBA* multColor, + hsTArray *retIndex, plDrawableSpans *toAddTo ) +{ + hsTArray points; + hsTArray normals; + hsTArray indices; + hsTArray colors; + hsTArray uvws; + hsPoint3 point; + + plDrawableSpans *drawable; + float mults[ 8 ][ 3 ] = { { -1, -1, -1 }, { 1, -1, -1 }, { 1, 1, -1 }, { -1, 1, -1 }, + { -1, -1, 1 }, { 1, -1, 1 }, { 1, 1, 1 }, { -1, 1, 1 } }; + + + + /// Generate points and normals + points.Expand( 8 ); + normals.Expand( 8 ); + + point = corner; points.Append( point ); + point += xVec; points.Append( point ); + point += yVec; points.Append( point ); + point = corner + yVec; points.Append( point ); + point = corner + zVec; points.Append( point ); + point += xVec; points.Append( point ); + point += yVec; points.Append( point ); + point = corner + zVec + yVec; points.Append( point ); + + CALC_NORMAL( normals, xVec, yVec, zVec ); + CALC_NORMAL( normals, -xVec, yVec, zVec ); + CALC_NORMAL( normals, -xVec, -yVec, zVec ); + CALC_NORMAL( normals, xVec, -yVec, zVec ); + CALC_NORMAL( normals, xVec, yVec, -zVec ); + CALC_NORMAL( normals, -xVec, yVec, -zVec ); + CALC_NORMAL( normals, -xVec, -yVec, -zVec ); + CALC_NORMAL( normals, xVec, -yVec, -zVec ); + + uvws.Expand( 8 ); + uvws.Append( hsPoint3( 0.f, 1.f, 0.f ) ); + uvws.Append( hsPoint3( 1.f, 1.f, 0.f ) ); + uvws.Append( hsPoint3( 1.f, 0.f, 0.f ) ); + uvws.Append( hsPoint3( 0.f, 0.f, 0.f ) ); + uvws.Append( hsPoint3( 1.f, 1.f, 0.f ) ); + uvws.Append( hsPoint3( 1.f, 0.f, 0.f ) ); + uvws.Append( hsPoint3( 0.f, 0.f, 0.f ) ); + uvws.Append( hsPoint3( 0.f, 1.f, 0.f ) ); + + /// Generate indices + indices.Expand( 36 ); + indices.Append( 0 ); indices.Append( 1 ); indices.Append( 2 ); + indices.Append( 0 ); indices.Append( 2 ); indices.Append( 3 ); + + indices.Append( 1 ); indices.Append( 0 ); indices.Append( 4 ); + indices.Append( 1 ); indices.Append( 4 ); indices.Append( 5 ); + + indices.Append( 2 ); indices.Append( 1 ); indices.Append( 5 ); + indices.Append( 2 ); indices.Append( 5 ); indices.Append( 6 ); + + indices.Append( 3 ); indices.Append( 2 ); indices.Append( 6 ); + indices.Append( 3 ); indices.Append( 6 ); indices.Append( 7 ); + + indices.Append( 0 ); indices.Append( 3 ); indices.Append( 7 ); + indices.Append( 0 ); indices.Append( 7 ); indices.Append( 4 ); + + indices.Append( 7 ); indices.Append( 6 ); indices.Append( 5 ); + indices.Append( 7 ); indices.Append( 5 ); indices.Append( 4 ); + + /// Create a drawable for it + drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), + uvws.AcquireArray(), 1, + nil, true, multColor, + indices.GetCount(), indices.AcquireArray(), + material, localToWorld, blended, retIndex, toAddTo ); + + return drawable; +} + + +//// GenerateBoundsDrawable ////////////////////////////////////////////////// + +plDrawableSpans *plDrawableGenerator::GenerateBoundsDrawable( hsBounds3Ext *bounds, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, + const hsColorRGBA* multColor, + hsTArray *retIndex, plDrawableSpans *toAddTo ) +{ + hsTArray points; + hsTArray normals; + hsTArray indices; + hsTArray colors; + hsPoint3 point; + hsVector3 normal; + + int i; + plDrawableSpans *drawable; + float mults[ 8 ][ 3 ] = { { -1, -1, -1 }, { 1, -1, -1 }, { 1, 1, -1 }, { -1, 1, -1 }, + { -1, -1, 1 }, { 1, -1, 1 }, { 1, 1, 1 }, { -1, 1, 1 } }; + + + /// Generate points and normals + points.Expand( 8 ); + normals.Expand( 8 ); + hsPoint3 min = bounds->GetMins(); + hsPoint3 max = bounds->GetMaxs(); + + for( i = 0; i < 8; i++ ) + { + points.Append( hsPoint3( mults[ i ][ 0 ] > 0 ? max.fX : min.fX, + mults[ i ][ 1 ] > 0 ? max.fY : min.fY, + mults[ i ][ 2 ] > 0 ? max.fZ : min.fZ ) ); + normals.Append( hsVector3( mults[ i ][ 0 ], mults[ i ][ 1 ], mults[ i ][ 2 ] ) ); + } + + /// Generate indices + indices.Expand( 36 ); + indices.Append( 0 ); indices.Append( 1 ); indices.Append( 2 ); + indices.Append( 0 ); indices.Append( 2 ); indices.Append( 3 ); + + indices.Append( 1 ); indices.Append( 0 ); indices.Append( 4 ); + indices.Append( 1 ); indices.Append( 4 ); indices.Append( 5 ); + + indices.Append( 2 ); indices.Append( 1 ); indices.Append( 5 ); + indices.Append( 2 ); indices.Append( 5 ); indices.Append( 6 ); + + indices.Append( 3 ); indices.Append( 2 ); indices.Append( 6 ); + indices.Append( 3 ); indices.Append( 6 ); indices.Append( 7 ); + + indices.Append( 0 ); indices.Append( 3 ); indices.Append( 7 ); + indices.Append( 0 ); indices.Append( 7 ); indices.Append( 4 ); + + indices.Append( 7 ); indices.Append( 6 ); indices.Append( 5 ); + indices.Append( 7 ); indices.Append( 5 ); indices.Append( 4 ); + + /// Create a drawable for it + drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), + nil, 0, + nil, true, multColor, + indices.GetCount(), indices.AcquireArray(), + material, localToWorld, blended, retIndex, toAddTo ); + + return drawable; +} + +//// GenerateConicalDrawable ///////////////////////////////////////////////// + +plDrawableSpans *plDrawableGenerator::GenerateConicalDrawable( hsScalar radius, hsScalar height, hsGMaterial *material, + const hsMatrix44 &localToWorld, hsBool blended, + const hsColorRGBA* multColor, + hsTArray *retIndex, plDrawableSpans *toAddTo ) +{ + hsVector3 direction; + + + direction.Set( 0, 0, height ); + + return GenerateConicalDrawable( hsPoint3( 0, 0, 0 ), direction, radius, material, localToWorld, blended, + multColor, retIndex, toAddTo ); +} + + +//// GenerateConicalDrawable ///////////////////////////////////////////////// + +plDrawableSpans *plDrawableGenerator::GenerateConicalDrawable( hsPoint3 &apex, hsVector3 &direction, hsScalar radius, hsGMaterial *material, + const hsMatrix44 &localToWorld, hsBool blended, + const hsColorRGBA* multColor, + hsTArray *retIndex, plDrawableSpans *toAddTo ) +{ + hsTArray points; + hsTArray normals; + hsTArray indices; + hsTArray colors; + hsPoint3 point; + hsVector3 normal; + + int i, numDivisions; + float angle, x, y; + plDrawableSpans *drawable; + + + numDivisions = (int)( radius / 4.f ); + if( numDivisions < 6 ) + numDivisions = 6; + else if( numDivisions > 20 ) + numDivisions = 20; + + + /// First, we need a few more vectors--specifically, the x and y vectors for the cone's base + hsPoint3 baseCenter = apex + direction; + hsVector3 xVec, yVec; + + xVec.Set( 1, 0, 0 ); + yVec = xVec % direction; + if( yVec.MagnitudeSquared() == 0 ) + { + xVec.Set( 0, 1, 0 ); + yVec = xVec % direction; + hsAssert( yVec.MagnitudeSquared() != 0, "Weird funkiness when doing this!!!" ); + } + + xVec = yVec % direction; + xVec.Normalize(); + yVec.Normalize(); + + /// Now generate points based on those + points.Expand( numDivisions + 2 ); + normals.Expand( numDivisions + 2 ); + + points.Append( apex ); + normals.Append( -direction ); + for( i = 0; i < numDivisions; i++ ) + { + angle = (float)i * ( hsScalarPI * 2.f ) / (float)numDivisions; + hsFastMath::SinCosInRange( angle, x, y ); + + points.Append( baseCenter + ( xVec * x * radius ) + ( yVec * y * radius ) ); + normals.Append( ( xVec * x ) + ( yVec * y ) ); + } + + /// Generate indices + indices.Expand( ( numDivisions + 1 + numDivisions - 2 ) * 3 ); + for( i = 0; i < numDivisions - 1; i++ ) + { + indices.Append( 0 ); + indices.Append( i + 2 ); + indices.Append( i + 1 ); + } + indices.Append( 0 ); + indices.Append( 1 ); + indices.Append( numDivisions ); + // Bottom cap + for( i = 3; i < numDivisions + 1; i++ ) + { + indices.Append( i - 1 ); + indices.Append( i ); + indices.Append( 1 ); + } + + /// Create a drawable for it + drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), + nil, 0, + nil, true, multColor, + indices.GetCount(), indices.AcquireArray(), + material, localToWorld, blended, retIndex, toAddTo ); + + return drawable; +} + +//// GenerateAxesDrawable //////////////////////////////////////////////////// + +plDrawableSpans *plDrawableGenerator::GenerateAxesDrawable( hsGMaterial *material, + const hsMatrix44 &localToWorld, hsBool blended, + const hsColorRGBA* multColor, + hsTArray *retIndex, plDrawableSpans *toAddTo ) +{ + hsTArray points; + hsTArray normals; + hsTArray colors; + hsTArray indices; + + int i; + float size = 15; + plDrawableSpans *drawable; + + + /// Generate points + points.SetCount( 6 * 3 ); + normals.SetCount( 6 * 3 ); + colors.SetCount( 6 * 3 ); + + points[ 0 ].Set( 0, 0, 0 ); + points[ 1 ].Set( size, -size * 0.1f, 0 ); + points[ 2 ].Set( size, -size * 0.3f, 0 ); + points[ 3 ].Set( size * 1.3f, 0, 0 ); + points[ 4 ].Set( size, size * 0.3f, 0 ); + points[ 5 ].Set( size, size * 0.1f, 0 ); + for( i = 0; i < 6; i++ ) + { + points[ i + 6 ].fX = - points[ i ].fY; + points[ i + 6 ].fY = points[ i ].fX; + points[ i + 6 ].fZ = 0; + + points[ i + 12 ].fX = points[ i ].fY; + points[ i + 12 ].fZ = points[ i ].fX; + points[ i + 12 ].fY = 0; + + colors[ i ].Set( 1, 0, 0, 1 ); + colors[ i + 6 ].Set( 0, 1, 0, 1 ); + colors[ i + 12 ].Set( 0, 0, 1, 1 ); + + if( multColor ) + colors[ i ] *= *multColor; + } + + /// Generate indices + indices.SetCount( 6 * 3 ); + for( i = 0; i < 18; i += 6 ) + { + indices[ i ] = i + 0; + indices[ i + 1 ] = i + 1; + indices[ i + 2 ] = i + 5; + indices[ i + 3 ] = i + 2; + indices[ i + 4 ] = i + 3; + indices[ i + 5 ] = i + 4; + } + + /// Create a drawable for it + drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), + nil, 0, + nil, true, multColor, + indices.GetCount(), indices.AcquireArray(), + material, localToWorld, blended, retIndex, toAddTo ); + + return drawable; +} + +//// GeneratePlanarDrawable ////////////////////////////////////////////////// +// Version that takes a corner and two vectors, for x and y edges. + +#define CALC_PNORMAL( nA, xVec, yVec ) { hsVector3 n = (xVec) % (yVec); n.Normalize(); nA.Append( n ); } + +plDrawableSpans *plDrawableGenerator::GeneratePlanarDrawable( const hsPoint3 &corner, const hsVector3 &xVec, const hsVector3 &yVec, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, + const hsColorRGBA* multColor, + hsTArray *retIndex, plDrawableSpans *toAddTo ) +{ + hsTArray points; + hsTArray normals; + hsTArray indices; + hsTArray colors; + hsTArray uvws; + hsPoint3 point; + + plDrawableSpans *drawable; + + + /// Generate points and normals + points.Expand( 4 ); + normals.Expand( 4 ); + + point = corner; points.Append( point ); + point += xVec; points.Append( point ); + point += yVec; points.Append( point ); + point = corner + yVec; points.Append( point ); + + CALC_PNORMAL( normals, xVec, yVec ); + CALC_PNORMAL( normals, xVec, yVec ); + CALC_PNORMAL( normals, xVec, yVec ); + CALC_PNORMAL( normals, xVec, yVec ); + + uvws.Expand( 4 ); + uvws.Append( hsPoint3( 0.f, 1.f, 0.f ) ); + uvws.Append( hsPoint3( 1.f, 1.f, 0.f ) ); + uvws.Append( hsPoint3( 1.f, 0.f, 0.f ) ); + uvws.Append( hsPoint3( 0.f, 0.f, 0.f ) ); + + /// Generate indices + indices.Expand( 6 ); + indices.Append( 0 ); indices.Append( 1 ); indices.Append( 2 ); + indices.Append( 0 ); indices.Append( 2 ); indices.Append( 3 ); + + /// Create a drawable for it + drawable = plDrawableGenerator::GenerateDrawable( points.GetCount(), points.AcquireArray(), normals.AcquireArray(), + uvws.AcquireArray(), 1, + nil, true, multColor, + indices.GetCount(), indices.AcquireArray(), + material, localToWorld, blended, retIndex, toAddTo ); + + return drawable; +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableGenerator.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableGenerator.h new file mode 100644 index 00000000..a4801168 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableGenerator.h @@ -0,0 +1,152 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDrawableGenerator Header // +// // +// Static helper class for creating various kind of drawable primitives. // +// Can be very useful for visualization stuff ;) // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// 5.15.2001 mcn - Created. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDrawableGenerator_h +#define _plDrawableGenerator_h + +#include "hsTemplates.h" +#include "hsBounds.h" +#include "hsMatrix44.h" + +class hsGMaterial; +class plDrawableSpans; +class plGeometrySpan; +struct hsColorRGBA; + + +//// Class Definition //////////////////////////////////////////////////////// + +class plDrawableGenerator +{ + public: + + // Set the colors for the faux lighting on generated drawables + static void SetFauxLightColors( hsColorRGBA &lite, hsColorRGBA &dark ); + + // Refills a drawable previously created with GenerateDrawable with the new data. New data + // must match previous data in counts. + hsBool RegenerateDrawable( UInt32 vertCount, hsPoint3 *positions, hsVector3 *normals, + hsPoint3 *uvws, UInt32 uvwsPerVtx, + hsColorRGBA *origColors, hsBool fauxShade, const hsColorRGBA* multColor, + UInt32 numIndices, UInt16 *indices, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, + UInt32 diIndex, plDrawableSpans *destDraw ); + + // Generates a drawable based on the vertex/index data given + // uvws is an array vertCount*uvwsPerVtx long in order [uvw(s) for vtx0, uvw(s) for vtx1, ...], or is nil + static plDrawableSpans *GenerateDrawable( UInt32 vertCount, hsPoint3 *positions, hsVector3 *normals, + hsPoint3 *uvws, UInt32 uvwsPerVtx, + hsColorRGBA *origColors, hsBool fauxShade, const hsColorRGBA* multColor, + UInt32 numIndices, UInt16 *indices, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended = false, + hsTArray *retIndex = nil, plDrawableSpans *toAddTo = nil ); + + // Generates a spherical drawable + static plDrawableSpans *GenerateSphericalDrawable( const hsPoint3& localPos, hsScalar radius, hsGMaterial *material, + const hsMatrix44 &localToWorld, hsBool blended = false, + const hsColorRGBA* multColor = nil, + hsTArray *retIndex = nil, plDrawableSpans *toAddTo = nil, + hsScalar qualityScalar = 1.f ); + + // Generates a rectangular drawable + static plDrawableSpans *GenerateBoxDrawable( hsScalar width, hsScalar height, hsScalar depth, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended = false, + const hsColorRGBA* multColor = nil, + hsTArray *retIndex = nil, plDrawableSpans *toAddTo = nil ); + + // Generate a rectangular drawable based on a corner and three vectors + static plDrawableSpans *GenerateBoxDrawable( const hsPoint3 &corner, const hsVector3 &xVec, const hsVector3 &yVec, const hsVector3 &zVec, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended = false, + const hsColorRGBA* multColor = nil, + hsTArray *retIndex = nil, plDrawableSpans *toAddTo = nil ); + // Generates a bounds-based drawable + static plDrawableSpans *GenerateBoundsDrawable( hsBounds3Ext *bounds, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended = false, + const hsColorRGBA* multColor = nil, + hsTArray *retIndex = nil, plDrawableSpans *toAddTo = nil ); + + // Generates a conical drawable + static plDrawableSpans *GenerateConicalDrawable( hsScalar radius, hsScalar height, hsGMaterial *material, + const hsMatrix44 &localToWorld, hsBool blended = false, + const hsColorRGBA* multColor = nil, + hsTArray *retIndex = nil, plDrawableSpans *toAddTo = nil ); + + // Generates a general conical drawable based on a center and direction + static plDrawableSpans *GenerateConicalDrawable( hsPoint3 &apex, hsVector3 &direction, hsScalar radius, hsGMaterial *material, + const hsMatrix44 &localToWorld, hsBool blended = false, + const hsColorRGBA* multColor = nil, + hsTArray *retIndex = nil, plDrawableSpans *toAddTo = nil ); + + // Generates a drawable representing 3 axes + static plDrawableSpans *GenerateAxesDrawable( hsGMaterial *material, + const hsMatrix44 &localToWorld, hsBool blended = false, + const hsColorRGBA* multColor = nil, + hsTArray *retIndex = nil, plDrawableSpans *toAddTo = nil ); + + // Generate a planar drawable based on a corner and two vectors + static plDrawableSpans *GeneratePlanarDrawable( const hsPoint3 &corner, const hsVector3 &xVec, const hsVector3 &yVec, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended = false, + const hsColorRGBA* multColor = nil, + hsTArray *retIndex = nil, plDrawableSpans *toAddTo = nil ); + + protected: + + // Shade the vertices given based on a quick fake directional light. + // If origColors is non-nil, it must be an array of length count. Each outColor[i] *= origColor[i]. + // If multColor is non-nil, modulate the output by multColor. + static void IQuickShadeVerts( UInt32 count, hsVector3 *normals, + hsColorRGBA *colors, + hsColorRGBA* origColors = nil, + const hsColorRGBA* multColor = nil ); + + // Take the vertex and connectivity info supplied and fill out a geometry span with it. + // Output span is ready to be added to a Drawable, or refreshed in a Drawable if it's + // already in the SourceSpans. + static void IFillSpan( UInt32 vertCount, hsPoint3 *positions, hsVector3 *normals, + hsPoint3 *uvws, UInt32 uvwsPerVtx, + hsColorRGBA *origColors, hsBool fauxShade, const hsColorRGBA* multColor, + UInt32 numIndices, UInt16 *indices, + hsGMaterial *material, const hsMatrix44 &localToWorld, hsBool blended, + plGeometrySpan* span ); + + static hsColorRGBA fLiteColor; + static hsColorRGBA fDarkColor; + +}; + +#endif // _plDrawableGenerator_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpans.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpans.cpp new file mode 100644 index 00000000..980350ab --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpans.cpp @@ -0,0 +1,3945 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDrawableSpans Class Functions // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// 4.3.2001 mcn - Created. // +// 5.3.2001 mcn - Completely revamped. Now plDrawableSpans *IS* the group // +// of spans, and each span is either an icicle or patch. // +// This eliminates the need entirely for separate drawables,// +// at the cost of having to do a bit of extra work to // +// maintain different types of spans in the same drawable. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" + +#include "plAccessSpan.h" +#include "plAccessTriSpan.h" + +#include "plDrawableSpans.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "plPipeline.h" +#include "plGeometrySpan.h" +#include "plSpaceTree.h" +#include "plParticleFiller.h" +#include "plSpaceTreeMaker.h" + +#include "plClusterGroup.h" +#include "plCluster.h" +#include "plSpanTemplate.h" + +#include "../plMath/hsRadixSort.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" +#include "../plPipeline/plFogEnvironment.h" +#include "../plPipeline/hsGDeviceRef.h" +#include "../plPipeline/plPipeDebugFlags.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plDISpansMsg.h" +#include "../plMessage/plDeviceRecreateMsg.h" +#include "../plMessage/plRenderMsg.h" +#include "../plPipeline/plGBufferGroup.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnKeyedObject/plKey.h" +#include "../plParticleSystem/plParticleEmitter.h" +#include "../plParticleSystem/plParticle.h" +#include "../plGLight/plLightInfo.h" +#include "plgDispatch.h" +#include "plProfile.h" + +#include "../plMath/plTriUtils.h" + +#include "../pnMessage/plPipeResMakeMsg.h" + +#include "../plScene/plVisMgr.h" +#include "../plScene/plVisRegion.h" + +#include + +//// Local Konstants ///////////////////////////////////////////////////////// + +const UInt32 plDrawableSpans::kSpanTypeMask = 0xc0000000; +const UInt32 plDrawableSpans::kSpanIDMask = ~kSpanTypeMask; +const UInt32 plDrawableSpans::kSpanTypeIcicle = 0x00000000; +const UInt32 plDrawableSpans::kSpanTypeParticleSpan = 0xc0000000; + +//// Constructor & Destructor //////////////////////////////////////////////// + +plDrawableSpans::plDrawableSpans() : + fSceneNode(nil), + fSpaceTree(nil) +{ + fReadyToRender = false; + fProps = 0; + fCriteria = 0; + fRegisteredForRecreate = false; + fRegisteredForRender = false; + fNeedCleanup = false; + + fOptimized = true; + + fSettingMatIdxLock = false; + + fSkinTime = 0; + + fType = kNormal; + fMaterials.Reset(); + + fLocalToWorld.Reset(); + fWorldToLocal.Reset(); + + fVisSet.SetBit(0); +} + +plDrawableSpans::~plDrawableSpans() +{ + int i; + + + for( i = 0; i < fGroups.GetCount(); i++ ) + { + delete fGroups[ i ]; + } + fGroups.Reset(); + + /// Loop and delete both our types of spans + for( i = 0; i < fSpans.GetCount(); i++ ) + fSpans[ i ]->Destroy(); + fSpans.Reset(); + fIcicles.Reset(); + fParticleSpans.Reset(); + + for( i = 0; i < fSourceSpans.GetCount(); i++ ) + delete fSourceSpans[ i ]; + fSourceSpans.Reset(); + + /// Loop and unref our materials + if( GetKey() != nil ) + { + for( i = 0; i < fMaterials.GetCount(); i++ ) + { + if( fMaterials[ i ] != nil && fMaterials[ i ]->GetKey() != nil ) + GetKey()->Release( fMaterials[ i ]->GetKey() ); + } + } + fMaterials.Reset(); + + delete fSpaceTree; + + for( i = 0; i < fDIIndices.GetCount(); i++ ) + delete fDIIndices[ i ]; + fDIIndices.Reset(); + + if( fRegisteredForRecreate ) + plgDispatch::Dispatch()->UnRegisterForExactType( plDeviceRecreateMsg::Index(), GetKey() ); + + if( fRegisteredForRender ) + plgDispatch::Dispatch()->UnRegisterForExactType( plRenderMsg::Index(), GetKey() ); +} + +void plDrawableSpans::SetKey(plKey k) +{ + hsKeyedObject::SetKey(k); + if( k ) + { + fRegisteredForRecreate = true; + plgDispatch::Dispatch()->RegisterForExactType(plDeviceRecreateMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plPipeGeoMakeMsg::Index(), GetKey()); + } +} + +//// ChangeSceneNode ///////////////////////////////////////////////////////// + +void plDrawableSpans::SetSceneNode( plKey newNode ) +{ + plKey curNode=GetSceneNode(); + if( curNode == newNode ) + return; + if( newNode ) + { + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(newNode, plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kDrawable); + hsgResMgr::ResMgr()->SendRef(GetKey(), refMsg, plRefFlags::kActiveRef); + } + if( curNode ) + { + curNode->Release(GetKey()); + } + fSceneNode = newNode; +} + +//// PrepForRender /////////////////////////////////////////////////////////// + +void plDrawableSpans::PrepForRender( plPipeline *p ) +{ + /// If we're not registered for this message, register for it so we know when + /// we need to refresh the buffers + if( !fRegisteredForRecreate && GetKey() != nil ) + { + fRegisteredForRecreate = true; + plgDispatch::Dispatch()->RegisterForExactType( plDeviceRecreateMsg::Index(), GetKey() ); + } + + if( !fReadyToRender ) + { + UInt32 i; + + for( i = 0; i < fGroups.GetCount(); i++ ) + { + // Each group will decide whether it needs to be prepped + fGroups[ i ]->PrepForRendering( p, false ); + } + + fReadyToRender = true; + } + + if( fParticleSpans.GetCount() ) + { + UInt32 i; +#if 0 + for( i = 0; i < fSpans.GetCount(); i++ ) + { + if( fSpans[ i ]->fTypeMask & plSpan::kParticleSpan ) + { + plParticleSpan* span = (plParticleSpan*)fSpans[i]; + + plParticleFiller::FillParticles(p, this, span); + } + } +#else + for( i = 0; i < fParticleSpans.GetCount(); i++ ) + { + plParticleFiller::FillParticles(p, this, &fParticleSpans[i]); + } +#endif + } +} + +void plDrawableSpans::SetDISpanVisSet(UInt32 diIndex, hsKeyedObject* ref, hsBool on) +{ + // Could actually do something here to neutralize bones, but we're not. + // Main thing is that if it's Matrix Only, then the indices are into + // the LocalToWorlds, not into fSpans + if( fDIIndices[diIndex]->IsMatrixOnly() ) + return; + + plVisRegion* reg = plVisRegion::ConvertNoRef(ref); + if( !reg ) + return; + hsBool isNot = reg->GetProperty(plVisRegion::kIsNot); + UInt32 visRegIndex = reg->GetIndex(); + + if( isNot ) + { + fVisNot.SetBit(visRegIndex, on); + int i; + for( i = 0; i < fDIIndices[ diIndex ]->GetCount(); i++ ) + { + int spanIndex = (*fDIIndices[diIndex])[i]; + fSpans[spanIndex]->SetVisNot(visRegIndex, on); + } + } + else + { + fVisSet.SetBit(visRegIndex, on); + int i; + for( i = 0; i < fDIIndices[ diIndex ]->GetCount(); i++ ) + { + int spanIndex = (*fDIIndices[diIndex])[i]; + fSpans[spanIndex]->SetVisBit(visRegIndex, on); + + // HACKAGE + // We need to be more careful about when we set and clear this bit, + // but not today. + // Okay, today. mf + if( reg->ReplaceNormal() ) + fSpans[spanIndex]->SetVisBit(plVisMgr::kNormal, false); + } + } +} + +void plDrawableSpans::SetVisSet(plVisMgr* visMgr) +{ + plProfile_Extern(VisSelect); + plProfile_BeginTiming(VisSelect); + if( visMgr ) + { + const hsBitVector& visSet = visMgr->GetVisSet(); + const hsBitVector& visNot = visMgr->GetVisNot(); + + // Go through some contortions to not be new[]'ing and delete[]'ing. + static hsBitVector myVis; + static hsBitVector myNot; + + // myVis = (visSet ^ fLastVisSet) & fVisSet; + myVis = visSet; + myVis ^= fLastVisSet; + myVis &= fVisSet; + + // myNot = (visNot ^ fLastVisNot) & fVisNot; + myNot = visNot; + myNot ^= fLastVisNot; + myNot &= fVisNot; + + // myVis = myVis | myNot + myVis |= myNot; + + GetSpaceTree()->SetCache(&fVisCache); + if( !myVis.Empty() ) + { + fVisCache.Clear(); + + int i; + for( i = 0; i < fSpans.GetCount(); i++ ) + { + if( !fSpans[i]->GetVisNot().Overlap(visNot) && fSpans[i]->GetVisSet().Overlap(visSet) ) + { + GetSpaceTree()->EnableLeaf(i, fVisCache); + } + } + fLastVisSet = visSet; + fLastVisNot = visNot; + } + } + else + { + GetSpaceTree()->SetCache(nil); + } + plProfile_EndTiming(VisSelect); +} + +// Here's the problem. When we update one of the matrices in the matrix palette, +// we've invalidated the current bounds of all the spans that use it. The skinning +// matrix may have moved the spans, or may just have stretched them, but either +// way, they aren't right any more. Wait, it gets worse. We're going to have a bunch +// of SetTransform calls come in, and then some time later, without our knowledge, +// our bounds (via the space tree and PageTreeMgr) will be used to evaluate whether +// to bother drawing us. Clearly, if we're going to keep our bounds up to date, we +// want to wait until all the SetTransforms have come in, and then update our bounds +// all at once, but before the visibility determination phase kicks in. +// The alternative is to make the local bounds conservative. We bloat out the local +// bounds on export so that whereever the skinning matrices push us around, we're +// still inside the local bounds. This is certainly cheaper to compute (all computation +// is offline), but gives a less tight bounds. Whether the conservative bounds is +// tight enough will depend on how the skinning is actually being used. +// +// Therefore I'm deferring decision on which way to go, and putting in this temp hack +// which does it the dumbest way possible while still being correct. +// +// So recap: +// If we need tight up-to-date bounds, we'll probably send a system message between +// the update phase (Eval Msg) and the render phase, only marking as dirty the changed +// palette matrices. +// If conservative bounds are good enough, we'll need to compute those from the skinning +// animations offline. +void plDrawableSpans::IUpdateMatrixPaletteBoundsHack() +{ + int i; + for( i = 0; i < fSpans.GetCount(); i++ ) + { + if( fSpans[i]->fNumMatrices && !(fSpans[i]->fProps & plSpan::kPropNoDraw) ) + { + hsBounds3Ext bnd = fSpans[i]->fLocalBounds; + const hsMatrix44& xfm0 = fSpans[i]->fMaxBoneIdx ? fLocalToWorlds[fSpans[i]->fBaseMatrix + fSpans[i]->fMaxBoneIdx] : fSpans[i]->fLocalToWorld; + bnd.Transform(&xfm0); + fSpans[i]->fWorldBounds = bnd; + + bnd = fSpans[i]->fLocalBounds; + const hsMatrix44& xfm1 = fSpans[i]->fPenBoneIdx ? fLocalToWorlds[fSpans[i]->fBaseMatrix + fSpans[i]->fPenBoneIdx] : fSpans[i]->fLocalToWorld; + bnd.Transform(&xfm1); + fSpans[i]->fWorldBounds.Union(&bnd); + + GetSpaceTree()->MoveLeaf(i, fSpans[i]->fWorldBounds); + } + } +} + +hsBool plDrawableSpans::IBoundsInvalid(const hsBounds3Ext& bnd) const +{ + int i; + for( i = 0; i < 3; i++ ) + { + const hsScalar kLimit(1.e5f); + + if( bnd.GetMaxs()[i] > kLimit ) + return true; + if( bnd.GetMins()[i] < -kLimit ) + return true; + } + return false; +} + +//// SetTransform //////////////////////////////////////////////////////////// + +static inline hsMatrix44 IMatrixMul34(const hsMatrix44& lhs, const hsMatrix44& rhs) +{ + hsMatrix44 ret; + ret.NotIdentity(); + ret.fMap[3][0] = ret.fMap[3][1] = ret.fMap[3][2] = 0; + ret.fMap[3][3] = 1.f; + + ret.fMap[0][0] = lhs.fMap[0][0] * rhs.fMap[0][0] + + lhs.fMap[0][1] * rhs.fMap[1][0] + + lhs.fMap[0][2] * rhs.fMap[2][0]; + + ret.fMap[0][1] = lhs.fMap[0][0] * rhs.fMap[0][1] + + lhs.fMap[0][1] * rhs.fMap[1][1] + + lhs.fMap[0][2] * rhs.fMap[2][1]; + + ret.fMap[0][2] = lhs.fMap[0][0] * rhs.fMap[0][2] + + lhs.fMap[0][1] * rhs.fMap[1][2] + + lhs.fMap[0][2] * rhs.fMap[2][2]; + + ret.fMap[0][3] = lhs.fMap[0][0] * rhs.fMap[0][3] + + lhs.fMap[0][1] * rhs.fMap[1][3] + + lhs.fMap[0][2] * rhs.fMap[2][3] + + lhs.fMap[0][3]; + + ret.fMap[1][0] = lhs.fMap[1][0] * rhs.fMap[0][0] + + lhs.fMap[1][1] * rhs.fMap[1][0] + + lhs.fMap[1][2] * rhs.fMap[2][0]; + + ret.fMap[1][1] = lhs.fMap[1][0] * rhs.fMap[0][1] + + lhs.fMap[1][1] * rhs.fMap[1][1] + + lhs.fMap[1][2] * rhs.fMap[2][1]; + + ret.fMap[1][2] = lhs.fMap[1][0] * rhs.fMap[0][2] + + lhs.fMap[1][1] * rhs.fMap[1][2] + + lhs.fMap[1][2] * rhs.fMap[2][2]; + + ret.fMap[1][3] = lhs.fMap[1][0] * rhs.fMap[0][3] + + lhs.fMap[1][1] * rhs.fMap[1][3] + + lhs.fMap[1][2] * rhs.fMap[2][3] + + lhs.fMap[1][3]; + + ret.fMap[2][0] = lhs.fMap[2][0] * rhs.fMap[0][0] + + lhs.fMap[2][1] * rhs.fMap[1][0] + + lhs.fMap[2][2] * rhs.fMap[2][0]; + + ret.fMap[2][1] = lhs.fMap[2][0] * rhs.fMap[0][1] + + lhs.fMap[2][1] * rhs.fMap[1][1] + + lhs.fMap[2][2] * rhs.fMap[2][1]; + + ret.fMap[2][2] = lhs.fMap[2][0] * rhs.fMap[0][2] + + lhs.fMap[2][1] * rhs.fMap[1][2] + + lhs.fMap[2][2] * rhs.fMap[2][2]; + + ret.fMap[2][3] = lhs.fMap[2][0] * rhs.fMap[0][3] + + lhs.fMap[2][1] * rhs.fMap[1][3] + + lhs.fMap[2][2] * rhs.fMap[2][3] + + lhs.fMap[2][3]; + + return ret; +} + +#include "../plStatusLog/plStatusLog.h" + +#ifdef MF_TEST_UPDATE +plProfile_CreateCounter("DSSetTrans", "Update", DSSetTrans); +plProfile_CreateCounter("DSMatSpans", "Update", DSMatSpans); +plProfile_CreateCounter("DSRegSpans", "Update", DSRegSpans); + +plProfile_CreateTimer("DSSetTransT", "Update", DSSetTransT); +plProfile_CreateTimer("DSMatTransT", "Update", DSMatTransT); +plProfile_CreateTimer("DSRegTransT", "Update", DSRegTransT); +plProfile_CreateTimer("DSBndTransT", "Update", DSBndTransT); +#endif // MF_TEST_UPDATE + + +plDrawable& plDrawableSpans::SetTransform( UInt32 index, const hsMatrix44& l2w, const hsMatrix44& w2l ) +{ +#ifdef MF_TEST_UPDATE + plProfile_IncCount(DSSetTrans, 1); + plProfile_BeginTiming(DSSetTransT); +#endif // MF_TEST_UPDATE + + if( index == (UInt32)-1 ) + { + fLocalToWorld = l2w; + fWorldToLocal = w2l; + + fWorldBounds = fLocalBounds; + fWorldBounds.Transform( &l2w ); + } + else + { + int i; + UInt32 idx; + plDISpanIndex *spans = fDIIndices[ index ]; + + + if( spans->IsMatrixOnly() ) + { +#ifdef MF_TEST_UPDATE + plProfile_IncCount(DSMatSpans, spans->GetCount()); + plProfile_BeginTiming(DSMatTransT); +#endif // MF_TEST_UPDATE + for( i = 0; i < spans->GetCount(); i++ ) + { +#if 0 + fLocalToWorlds[ (*spans)[ i ] ] = l2w * fLocalToBones[ (*spans)[ i ] ]; + fWorldToLocals[ (*spans)[ i ] ] = fBoneToLocals[ (*spans)[ i ] ] * w2l; +#else + fLocalToWorlds[ (*spans)[ i ] ] = IMatrixMul34(l2w, fLocalToBones[ (*spans)[ i ] ]); + fWorldToLocals[ (*spans)[ i ] ] = IMatrixMul34(fBoneToLocals[ (*spans)[ i ] ], w2l); +#endif + } +#ifdef MF_TEST_UPDATE + plProfile_EndTiming(DSMatTransT); +#endif // MF_TEST_UPDATE + } + else if( !spans->DontTransform() ) + { +#ifdef MF_TEST_UPDATE + plProfile_IncCount(DSRegSpans, spans->GetCount()); +#endif // MF_TEST_UPDATE + for( i = 0; i < spans->GetCount(); i++ ) + { +#ifdef MF_TEST_UPDATE + plProfile_BeginTiming(DSRegTransT); +#endif // MF_TEST_UPDATE + + idx = (*spans)[ i ]; + plSpan *mSpan = fSpans[ idx ]; + mSpan->fLocalToWorld = l2w; + mSpan->fWorldToLocal = w2l; + + mSpan->fWorldBounds = mSpan->fLocalBounds; + mSpan->fWorldBounds.Transform( &l2w ); + + if( fSourceSpans.GetCount() > idx ) + { + /// If we have a geoSpan for this, update its transform as well, + /// just in case we need to use it later ( SceneViewer reshade ) + if( fSourceSpans[ idx ] == nil ) + { + plStatusLog::AddLineS( "pipeline.log", 0xffffffff, "Nil source spans found in SetTransform()" ); + } + + fSourceSpans[ idx ]->fLocalToWorld = l2w; + fSourceSpans[ idx ]->fWorldToLocal = w2l; + } +#ifdef MF_TEST_UPDATE + plProfile_EndTiming(DSRegTransT); + + plProfile_BeginTiming(DSBndTransT); +#endif // MF_TEST_UPDATE + if( IBoundsInvalid(mSpan->fWorldBounds) ) + { + mSpan->fProps |= kPropNoDraw; + GetSpaceTree()->SetLeafFlag((Int16)idx, plSpaceTreeNode::kDisabled, true); + } + else + { + GetSpaceTree()->MoveLeaf((Int16)((*spans)[i]), mSpan->fWorldBounds); + } +#ifdef MF_TEST_UPDATE + plProfile_EndTiming(DSBndTransT); +#endif // MF_TEST_UPDATE + } + } + +#ifdef MF_TEST_UPDATE + plProfile_BeginTiming(DSBndTransT); +#endif // MF_TEST_UPDATE + fWorldBounds = GetSpaceTree()->GetNode(GetSpaceTree()->GetRoot()).GetWorldBounds(); +#ifdef MF_TEST_UPDATE + plProfile_EndTiming(DSBndTransT); +#endif // MF_TEST_UPDATE + } + +#ifdef MF_TEST_UPDATE + plProfile_EndTiming(DSSetTransT); +#endif // MF_TEST_UPDATE + // Might want to assert that MaxWorldBounds still contains WorldBounds. + + return *this; +} + +void plDrawableSpans::SetNativeTransform(UInt32 idx, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + if( idx == UInt32(-1) ) + { + hsAssert(false, "Invalid index to SetNativeTransform"); + } + else + { + plSpan* span = fSpans[idx]; + span->fLocalToWorld = l2w; + span->fWorldToLocal = w2l; + + span->fWorldBounds = span->fLocalBounds; + span->fWorldBounds.Transform(&l2w); + + if( fSourceSpans.GetCount() > idx ) + { + fSourceSpans[idx]->fLocalToWorld = l2w; + fSourceSpans[idx]->fWorldToLocal = w2l; + } + + if( IBoundsInvalid(span->fWorldBounds) ) + { + SetNativeProperty( idx, kPropNoDraw, true); + } + else + { + GetSpaceTree()->MoveLeaf((Int16)idx, span->fWorldBounds); + } + } +} + +//// GetLocalToWorld & GetWorldToLocal /////////////////////////////////////// + +const hsMatrix44& plDrawableSpans::GetLocalToWorld( UInt32 span ) const +{ + if( span == (UInt32)-1 ) + return fLocalToWorld; + + return fSpans[ span ]->fLocalToWorld; +} + +const hsMatrix44& plDrawableSpans::GetWorldToLocal( UInt32 span ) const +{ + if( span == (UInt32)-1 ) + return fWorldToLocal; + + return fSpans[ span ]->fWorldToLocal; +} + + +//// Set/GetNativeProperty /////////////////////////////////////////////////// + +plDrawable& plDrawableSpans::SetNativeProperty( UInt32 index, int prop, hsBool on) +{ + int i; + + + if( index == (UInt32)-1 ) + { + hsAssert(false, "Invalid index to SetNativeProperty"); + } + else + { + plDISpanIndex *spans = fDIIndices[ index ]; + + if( !spans->IsMatrixOnly() ) + { + if( on ) + { + for( i = 0; i < spans->GetCount(); i++ ) + fSpans[ (*spans)[ i ] ]->fProps |= prop; + } + else + { + for( i = 0; i < spans->GetCount(); i++ ) + fSpans[ (*spans)[ i ] ]->fProps &= ~prop; + } + if( (prop & kPropNoDraw) ) + { + for( i = 0; i < spans->GetCount(); i++ ) + GetSpaceTree()->SetLeafFlag((Int16)((*spans)[ i ]), plSpaceTreeNode::kDisabled, on); + } + } + } + + return *this; +} + +hsBool plDrawableSpans::GetNativeProperty( UInt32 index, int prop ) const +{ + int i; + UInt32 ret = false; + + + if( index == (UInt32)-1 ) + { + for( i = 0; i < fSpans.GetCount(); i++ ) + ret |= ( fSpans[ i ]->fProps & prop ); + } + else + { + plDISpanIndex& spans = *fDIIndices[ index ]; + + if( !spans.IsMatrixOnly() ) + { + for( i = 0; i < spans.GetCount(); i++ ) + ret |= ( fSpans[ spans[ i ] ]->fProps & prop ); + } + } + + return ret != 0; +} + +plDrawable& plDrawableSpans::SetSubType(UInt32 index, plSubDrawableType t, hsBool on) +{ + if( UInt32(-1) == index ) + { + if( on ) + { + int i; + for( i = 0; i < fSpans.GetCount(); i++ ) + fSpans[i]->fSubType |= t; + } + else + { + int i; + for( i = 0; i < fSpans.GetCount(); i++ ) + fSpans[i]->fSubType &= ~t; + } + } + else + { + plDISpanIndex& spans = *fDIIndices[ index ]; + + if( on ) + { + int i; + for( i = 0; i < spans.GetCount(); i++ ) + fSpans[ spans[i] ]->fSubType |= t; + } + else + { + int i; + for( i = 0; i < spans.GetCount(); i++ ) + fSpans[ spans[i] ]->fSubType &= ~t; + } + } + return *this; +} + +UInt32 plDrawableSpans::GetSubType(UInt32 index) const +{ + UInt32 retVal = 0; + + if( UInt32(-1) == index ) + { + int i; + for( i = 0; i < fSpans.GetCount(); i++ ) + retVal |= fSpans[i]->fSubType; + } + else + { + plDISpanIndex& spans = *fDIIndices[ index ]; + + int i; + for( i = 0; i < spans.GetCount(); i++ ) + retVal |= fSpans[ spans[i] ]->fSubType; + } + return retVal; +} + +//// IXlateSpanProps ///////////////////////////////////////////////////////// +// Never used yet--just here in case we ever need it + +UInt32 plDrawableSpans::IXlateSpanProps( UInt32 props, hsBool xlateToSpan ) +{ + UInt32 retProps = 0; + + + if( xlateToSpan ) + { + /// Drawable props to plSpan props + if( props & kPropNoDraw ) retProps |= plSpan::kPropNoDraw; + if( props & kPropSortFaces ) retProps |= plSpan::kPropFacesSortable; + } + else + { + /// plSpan props to Drawable props + if( props & plSpan::kPropNoDraw ) retProps |= kPropNoDraw; + if( props & plSpan::kPropFacesSortable ) retProps |= kPropSortFaces; + } + + return retProps; +} + +//// Set/GetProperty ///////////////////////////////////////////////////////// +// Sets/gets a property just like the normal Set/GetNativeProperty, but the +// flag taken in is from plDrawInterface, not our props flags. So we have to +// translate... + +plDrawable& plDrawableSpans::SetProperty( UInt32 index, int diProp, hsBool on ) +{ + switch( diProp ) + { + case plDrawInterface::kDisable: + return SetNativeProperty( index, kPropNoDraw, on ); + default: + hsAssert( false, "Bad property passed to SetProperty" ); + } + + return *this; +} + +hsBool plDrawableSpans::GetProperty( UInt32 index, int diProp ) const +{ + switch( diProp ) + { + case plDrawInterface::kDisable: + return GetNativeProperty( index, kPropNoDraw ); + default: + hsAssert( false, "Bad property passed to SetProperty" ); + } + + return false; +} + +plDrawable& plDrawableSpans::SetProperty( int prop, hsBool on ) +{ + switch( prop ) + { + case plDrawInterface::kDisable: + { + int i; + for (i=0; iIsMatrixOnly() ) + { + for( i = 0; i < spans->GetCount(); i++ ) + { + bnd.Union( &fSpans[ (*spans)[ i ] ]->fLocalBounds ); + } + } + + return bnd; +} + +const hsBounds3Ext& plDrawableSpans::GetWorldBounds( UInt32 index ) const +{ + int i; + static hsBounds3Ext bnd; + + + if( index == (UInt32)-1 ) + return fWorldBounds; + + plDISpanIndex *spans = fDIIndices[ index ]; + + bnd.MakeEmpty(); + if( !spans->IsMatrixOnly() ) + { + for( i = 0; i < spans->GetCount(); i++ ) + { + bnd.Union( &fSpans[ (*spans)[ i ] ]->fWorldBounds ); + } + } + + return bnd; +} + +const hsBounds3Ext& plDrawableSpans::GetMaxWorldBounds( UInt32 index ) const +{ + return GetWorldBounds( index ); +} + +//// Read //////////////////////////////////////////////////////////////////// +// We read each in the array of icicles, +// then we read in an array of indices that we translate into +// pointers. Note: since materials and fog environments are shared, +// we read those keys last, so we don't have to have separate messages for +// each. + +void plDrawableSpans::Read( hsStream* s, hsResMgr* mgr ) +{ + UInt32 i, j, count, count2; + hsBool gotSkin = false; + plGBufferGroup *group; + plRefMsg *refMsg; + + + plDrawable::Read(s, mgr); + + fProps = s->ReadSwap32(); + fCriteria = s->ReadSwap32(); + fRenderLevel.fLevel = s->ReadSwap32(); + + /// Read in the material keys + count = s->ReadSwap32(); + fMaterials.SetCountAndZero( count ); + for( i = 0; i < count; i++ ) + { + refMsg = TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kMsgMaterial ); + mgr->ReadKeyNotifyMe( s, refMsg, plRefFlags::kActiveRef ); + } + + /// Read the icicles in + count = s->ReadSwap32(); + fIcicles.SetCount( count ); + for( i = 0; i < count; i++ ) + { + fIcicles[ i ].Read( s ); + if( fIcicles[ i ].fNumMatrices ) + gotSkin = true; + } + + /// Read the patches in + // FIXME MAJOR VERSION + // no more patches, remove this line + count = s->ReadSwap32(); + + /// Now read the index array in and use it to create a pointer table + fSpanSourceIndices.Reset(); + fSpans.Reset(); + count = s->ReadSwap32(); + for( i = 0; i < count; i++ ) + { + j = s->ReadSwap32(); + switch( j & kSpanTypeMask ) + { + case kSpanTypeIcicle: fSpans.Append( (plSpan *)&fIcicles[ j & kSpanIDMask ] ); break; + case kSpanTypeParticleSpan: fSpans.Append( (plSpan *)&fParticleSpans[ j & kSpanIDMask ] ); break; + } + + fSpanSourceIndices.Append( j ); + + if( fSpans[ fSpans.GetCount() - 1 ]->fTypeMask & plSpan::kParticleSpan ) + { + plParticleSpan *span = (plParticleSpan *)fSpans[ fSpans.GetCount() - 1 ]; + span->fSrcSpanIdx = fSpans.GetCount() - 1; + } + } + + // Rebuild bit vectors for various span types + IBuildVectors(); + + /// Now that we have our pointer array, read in the common keys (fog environs, etc) + for( i = 0; i < count; i++ ) + { + // Ref message for the fog environment + refMsg = TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kMsgFogEnviron ); + mgr->ReadKeyNotifyMe( s, refMsg, plRefFlags::kActiveRef ); + } + + /// Read in bounds and stuff + if( count > 0 ) + { + fLocalBounds.Read(s); + fWorldBounds.Read(s); + fMaxWorldBounds.Read(s); + } + else + { + fLocalBounds.MakeEmpty(); + fWorldBounds.MakeEmpty(); + fMaxWorldBounds.MakeEmpty(); + } + + for( i = 0; i < count; i++ ) + { + if( fSpans[i]->fProps & plSpan::kPropHasPermaLights ) + { + UInt32 lcnt = s->ReadSwap32(); + int j; + for( j = 0; j < lcnt; j++ ) + { + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, i, kMsgPermaLight), plRefFlags::kPassiveRef); + } + } + if( fSpans[i]->fProps & plSpan::kPropHasPermaProjs ) + { + UInt32 lcnt = s->ReadSwap32(); + int j; + for( j = 0; j < lcnt; j++ ) + { + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, i, kMsgPermaProj), plRefFlags::kPassiveRef); + } + } + } + + /// Read in the source spans if necessary + count = s->ReadSwap32(); + if( count > 0 ) + { + fSourceSpans.SetCount( count ); + for( i = 0; i < count; i++ ) + { + fSourceSpans[ i ] = TRACKED_NEW plGeometrySpan; + fSourceSpans[ i ]->Read( s ); + fSourceSpans[ i ]->fMaterial = GetMaterial( fSpans[ i ]->fMaterialIdx ); + fSourceSpans[ i ]->fFogEnviron = fSpans[ i ]->fFogEnvironment; + fSourceSpans[ i ]->fSpanRefIndex = i; + } + } + else + fSourceSpans.Reset(); + + /// Read in the matrix palette (if any) + count = s->ReadSwap32(); + fLocalToWorlds.SetCount(count); + fWorldToLocals.SetCount(count); + fLocalToBones.SetCount(count); + fBoneToLocals.SetCount(count); + for( i = 0; i < count; i++ ) + { + fLocalToWorlds[i].Read(s); + fWorldToLocals[i].Read(s); + + fLocalToBones[i].Read(s); + fBoneToLocals[i].Read(s); + } + + /// Read in the drawInterface index arrays + count = s->ReadSwap32(); + fDIIndices.SetCountAndZero( count ); + for( i = 0; i < count; i++ ) + { + fDIIndices[ i ] = TRACKED_NEW plDISpanIndex; + + fDIIndices[ i ]->fFlags = (UInt8)(s->ReadSwap32()); + count2 = s->ReadSwap32(); + fDIIndices[ i ]->SetCountAndZero( count2 ); + for( j = 0; j < count2; j++ ) + (*fDIIndices[ i ])[ j ] = s->ReadSwap32(); + } + + /// Read the groups in + count = s->ReadSwap32(); + while( count-- ) + { + group = TRACKED_NEW plGBufferGroup(0, fProps & kPropVolatile, fProps & kPropSortFaces); + group->Read( s ); + + fGroups.Append( group ); + + } + + if( fProps & kPropSortFaces ) + { + for( i = 0; i < fSpans.GetCount(); i++ ) + IMakeSpanSortable(i); + } + + /// Other stuff now + fSpaceTree = plSpaceTree::ConvertNoRef(mgr->ReadCreatable(s)); + + fSceneNode = mgr->ReadKey(s); + plNodeRefMsg* nRefMsg = TRACKED_NEW plNodeRefMsg(fSceneNode, plRefMsg::kOnCreate, -1, plNodeRefMsg::kDrawable); + mgr->AddViaNotify(GetKey(), nRefMsg, plRefFlags::kActiveRef); + + if( GetNativeProperty(plDrawable::kPropCharacter) ) + { + fVisSet.SetBit(plVisMgr::kCharacter, true); + for( i = 0; i < fSpans.GetCount(); i++ ) + fSpans[i]->SetVisBit(plVisMgr::kCharacter, true); + } + + // Placeholder hack - see IUpdateMatrixPaletteBoundsHack() for comments + if( gotSkin ) + { + fRegisteredForRender = true; + plgDispatch::Dispatch()->RegisterForExactType( plRenderMsg::Index(), GetKey() ); + } + + fReadyToRender = false; +} + +//// ITestMatForSpecularity ////////////////////////////////////////////////// + +hsBool plDrawableSpans::ITestMatForSpecularity( hsGMaterial *mat ) +{ + int i; + + + for( i = 0; i < mat->GetNumLayers(); i++ ) + { + if( mat->GetLayer( i )->GetShadeFlags() && hsGMatState::kShadeSpecular ) + return true; + } + + return false; +} + + +#include "plProfile.h" +plProfile_CreateTimer("MatrixPalleteHack", "RenderSetup", PalletteHack); +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool plDrawableSpans::MsgReceive( plMessage* msg ) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); + int i; + hsBool hasSpec; + + + if( refMsg ) + { + if( refMsg->fType == kMsgMaterial ) + { + /// Material add/remove on this drawable + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + hsAssert( refMsg->fWhich < fMaterials.GetCount(), "Invalid material index" ); + + fMaterials[ refMsg->fWhich ] = hsGMaterial::ConvertNoRef( refMsg->GetRef() ); + + if( !fSettingMatIdxLock ) + { + // Now find all spans with this material and mark them as using or not using specular + hasSpec = ITestMatForSpecularity( fMaterials[ refMsg->fWhich ] ); + + for( i = 0; i < fSpans.GetCount(); i++ ) + { + if( fSpans[ i ] != nil && fSpans[ i ]->fMaterialIdx == refMsg->fWhich ) + { + if( hasSpec ) + fSpans[ i ]->fProps |= plSpan::kPropMatHasSpecular; + else + fSpans[ i ]->fProps &= ~plSpan::kPropMatHasSpecular; + } + } + } + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + hsAssert( refMsg->fWhich < fMaterials.GetCount(), "Invalid material index" ); + + fMaterials[ refMsg->fWhich ] = nil; + } + return true; + } + else if( refMsg->fType == kMsgFogEnviron ) + { + /// Fog environment add/remove on this drawable + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + hsAssert( refMsg->fWhich < fSpans.GetCount(), "Shesh, send us valid data, will ya??" ); + + fSpans[ refMsg->fWhich ]->fFogEnvironment = plFogEnvironment::ConvertNoRef( refMsg->GetRef() ); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + UInt32 i; + plFogEnvironment *fog = plFogEnvironment::ConvertNoRef( refMsg->GetRef() ); + + for( i = 0; i < GetNumSpans(); i++ ) + { + if( fSpans[ i ]->fFogEnvironment == fog ) + { + fSpans[ i ]->fFogEnvironment = nil; + break; + } + } + } + return true; + } + else if( refMsg->fType == kMsgPermaLight ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + hsAssert( refMsg->fWhich < fSpans.GetCount(), "Shesh, send us valid data, will ya??" ); + plLightInfo* li = plLightInfo::ConvertNoRef(refMsg->GetRef()); + fSpans[refMsg->fWhich]->AddPermaLight(li, false); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + hsAssert( refMsg->fWhich < fSpans.GetCount(), "Shesh, send us valid data, will ya??" ); + plLightInfo* li = (plLightInfo*)refMsg->GetRef(); + fSpans[refMsg->fWhich]->RemovePermaLight(li, false); + } + return true; + } + else if( refMsg->fType == kMsgPermaProj ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + hsAssert( refMsg->fWhich < fSpans.GetCount(), "Shesh, send us valid data, will ya??" ); + plLightInfo* li = plLightInfo::ConvertNoRef(refMsg->GetRef()); + fSpans[refMsg->fWhich]->AddPermaLight(li, true); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + hsAssert( refMsg->fWhich < fSpans.GetCount(), "Shesh, send us valid data, will ya??" ); + plLightInfo* li = (plLightInfo*)refMsg->GetRef(); + fSpans[refMsg->fWhich]->RemovePermaLight(li, true); + } + return true; + } + else if( refMsg->fType == kMsgPermaLightDI ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + hsAssert( refMsg->fWhich < fDIIndices.GetCount(), "Shesh, send us valid data, will ya??" ); + plLightInfo* li = plLightInfo::ConvertNoRef(refMsg->GetRef()); + + int diIndex = int(refMsg->fWhich); + if( (diIndex >= 0) + && !fDIIndices[ diIndex ]->IsMatrixOnly() ) + { + for( i = 0; i < fDIIndices[ diIndex ]->GetCount(); i++ ) + { + int spanIndex = (*fDIIndices[diIndex])[i]; + fSpans[spanIndex]->AddPermaLight(li, false); + } + } + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + hsAssert( refMsg->fWhich < fDIIndices.GetCount(), "Shesh, send us valid data, will ya??" ); + plLightInfo* li = plLightInfo::ConvertNoRef(refMsg->GetRef()); + + int diIndex = int(refMsg->fWhich); + if( (diIndex >= 0) + && !fDIIndices[ diIndex ]->IsMatrixOnly() ) + { + for( i = 0; i < fDIIndices[ diIndex ]->GetCount(); i++ ) + { + int spanIndex = (*fDIIndices[diIndex])[i]; + fSpans[spanIndex]->RemovePermaLight(li, false); + } + } + } + return true; + } + else if( refMsg->fType == kMsgPermaProjDI ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + hsAssert( refMsg->fWhich < fDIIndices.GetCount(), "Shesh, send us valid data, will ya??" ); + plLightInfo* li = plLightInfo::ConvertNoRef(refMsg->GetRef()); + + int diIndex = int(refMsg->fWhich); + if( (diIndex >= 0) + && !fDIIndices[ diIndex ]->IsMatrixOnly() ) + { + for( i = 0; i < fDIIndices[ diIndex ]->GetCount(); i++ ) + { + int spanIndex = (*fDIIndices[diIndex])[i]; + fSpans[spanIndex]->AddPermaLight(li, true); + } + } + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + hsAssert( refMsg->fWhich < fDIIndices.GetCount(), "Shesh, send us valid data, will ya??" ); + plLightInfo* li = plLightInfo::ConvertNoRef(refMsg->GetRef()); + + int diIndex = int(refMsg->fWhich); + if( (diIndex >= 0) + && !fDIIndices[ diIndex ]->IsMatrixOnly() ) + { + for( i = 0; i < fDIIndices[ diIndex ]->GetCount(); i++ ) + { + int spanIndex = (*fDIIndices[diIndex])[i]; + fSpans[spanIndex]->RemovePermaLight(li, true); + } + } + } + return true; + } + } + else if( plDeviceRecreateMsg::ConvertNoRef( msg ) != nil ) + { + /// Device recreation message--just reset our flag so we refresh buffer groups + fReadyToRender = false; + return true; + } + else if( plRenderMsg::ConvertNoRef( msg ) ) + { + plProfile_BeginLap(PalletteHack, this->GetKey()->GetUoid().GetObjectName()); + + IUpdateMatrixPaletteBoundsHack(); + + // Last thing here. We have a bit list of which of our spans are skinned and + // thus need to be re-blended each frame. However, we don't want to blend them + // multiple times per frame if at all possible. Since the pipeline already checks + // our bitfield, what we *really* do is make a copy and give the pipeline our copy. + // The pipeline will then clear out those bits as it blends them, and then we simply + // re-set them here, since plRenderMsg is sent once before all the rendering is done :) + fFakeBlendingSpanVector = fBlendingSpanVector; + plProfile_EndLap(PalletteHack, this->GetKey()->GetUoid().GetObjectName()); + + return true; + } + + plDISpansMsg* diMsg = plDISpansMsg::ConvertNoRef(msg); + if( diMsg ) + { + if( diMsg->fType == plDISpansMsg::kRemovingSpan ) + { + // If the only set of spans we've got is about to be removed, + // (and we're not flagged to stick around) then just + // kill ourselves entirely. + if( fDIIndices.GetCount() < 2 && !(diMsg->fFlags & plDISpansMsg::kLeaveEmptyDrawable) ) + { + hsAssert(diMsg->fIndex + 1 == fDIIndices.GetCount(), "Deleting the an unknown set of indices"); + if( GetSceneNode() ) + { + GetSceneNode()->Release(GetKey()); + } + } + else /// plDrawInterface telling us to remove some spans + { + RemoveDISpans( (Int32)diMsg->fIndex ); + } + } +#ifdef HS_DEBUGGING + else if( diMsg->fType == plDISpansMsg::kAddingSpan ) + { + /// plDrawInterface telling us which spans it owns + Int32 i, spanIndex = (Int32)diMsg->fIndex; + + if( spanIndex == -1 ) + return true; + + for( i = 0; i < fDIIndices[ spanIndex ]->GetCount(); i++ ) + { + if( !fDIIndices[ spanIndex ]->IsMatrixOnly() ) + fSpans[ (*fDIIndices[ spanIndex])[ i ] ]->fOwnerKey = diMsg->GetSender(); + } + } +#endif + return true; + } + + plPipeGeoMakeMsg* make = plPipeGeoMakeMsg::ConvertNoRef(msg); + if( make ) + { + fReadyToRender = false; + PrepForRender(make->Pipeline()); + return true; + } + + return plDrawable::MsgReceive(msg); +} + +void plDrawableSpans::SetRenderLevel(const plRenderLevel& l) +{ + fRenderLevel = l; +} + +const plRenderLevel& plDrawableSpans::GetRenderLevel() const +{ + return fRenderLevel; +} + + +//// DoIMatch //////////////////////////////////////////////////////////////// +// Called by the sceneNode to determine if we match the criteria + +hsBool plDrawableSpans::DoIMatch( const plDrawableCriteria& crit ) +{ + if( crit.fCriteria ^ fCriteria ) + return false; + + if( crit.fLevel != fRenderLevel ) + return false; + + if( crit.fType != fType ) + return false; + + if( crit.fLoadMask != fLoadMask ) + return false; + + return true; +} + +//// SetCriteria ///////////////////////////////////////////////////////////// +// Sets the criteria that this ice matches to. Needed since some ice will +// be static per sceneNode while others wont, etc. + +void plDrawableSpans::SetCriteria( const plDrawableCriteria& crit ) +{ + // Very simple right now. May be more complicated later... + fCriteria = crit.fCriteria; + fRenderLevel = crit.fLevel; + fType = crit.fType; + fLoadMask = crit.fLoadMask; + + if( fCriteria & kCritSortSpans ) + fProps |= kPropSortSpans; + if( fCriteria & kCritSortFaces ) + fProps |= kPropSortFaces; + if( fCriteria & kCritCharacter ) + fProps |= kPropCharacter; +} + +//// IQuickSpaceTree /////////////////////////////////////////////////// +// Creates a fast, space tree for use at run-time (like in the +// SceneViewer). Any time a space tree is requested but there isn't +// one available, this will be supplied. This is a full featured +// hierarchical bounds which is pretty fast to compute. A more highly +// optimized version may be plugged into the Optimize function at a later +// date if this one doesn't perform enough (it does so far). + +void plDrawableSpans::IQuickSpaceTree( void ) const +{ + int i; + + // Make the space tree (hierarchical bounds). + plSpaceTreeMaker maker; + maker.Reset(); + for( i = 0; i < fSpans.GetCount(); i++ ) + { + maker.AddLeaf( fSpans[ i ]->fWorldBounds, fSpans[ i ]->fProps & plSpan::kPropNoDraw ); + } + plSpaceTree* tree = maker.MakeTree(); + SetSpaceTree(tree); +} + +//// SetSpaceTree //////////////////////////////////////////////////////////// + +void plDrawableSpans::SetSpaceTree( plSpaceTree *st ) const +{ + delete fSpaceTree; + fSpaceTree = st; + fLastVisSet.Clear(); + fLastVisNot.Clear(); +} + +//// GetDISpans ////////////////////////////////////////////////////////////// + +plDISpanIndex& plDrawableSpans::GetDISpans( UInt32 index ) const +{ + return *fDIIndices[index]; +} + +//// GetVertex/IndexRef ////////////////////////////////////////////////////// + +hsGDeviceRef *plDrawableSpans::GetVertexRef( UInt32 group, UInt32 idx ) +{ + return fGroups[ group ]->GetVertexBufferRef( idx ); +} + +hsGDeviceRef *plDrawableSpans::GetIndexRef( UInt32 group, UInt32 idx ) +{ + return fGroups[ group ]->GetIndexBufferRef( idx ); +} + +void plDrawableSpans::DirtyVertexBuffer(UInt32 group, UInt32 idx) +{ + hsAssert(group < fGroups.GetCount(), "Dirtying vtx buffer I don't have"); + GetBufferGroup(group)->DirtyVertexBuffer(idx); + + SetNotReadyToRender(); +} + +void plDrawableSpans::DirtyIndexBuffer(UInt32 group, UInt32 idx) +{ + hsAssert(group < fGroups.GetCount(), "Dirtying index buffer I don't have"); + GetBufferGroup(group)->DirtyIndexBuffer(idx); + + SetNotReadyToRender(); +} + +hsGMaterial* plDrawableSpans::GetSubMaterial(int index) const +{ + return GetMaterial(fSpans[index]->fMaterialIdx); +} + +// return true if span invisible before minDist and/or after maxDist +hsBool plDrawableSpans::GetSubVisDists(int index, hsScalar& minDist, hsScalar& maxDist) const +{ + return (minDist = fSpans[index]->GetMinDist()) < (maxDist = fSpans[index]->GetMaxDist()); +} + +////////////////////////////////////////////////////////////////////////////// +//// Runtime Dynamics //////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// SortSpan //////////////////////////////////////////////////////////////// +// Given the index of the span to sort, sorts the triangles of that span +// based on the sorting data. Note: the span MUST be of type plIcicle. + +plProfile_CreateTimer("Face Sort", "Draw", FaceSort); +plProfile_CreateCounter("Face Sort Calls", "Draw", FaceSortCalls); +plProfile_CreateCounter("Faces Sorted", "Draw", FacesSorted); + +void plDrawableSpans::SortSpan( UInt32 index, plPipeline *pipe ) +{ + plProfile_Inc(FaceSortCalls); + + plProfile_BeginLap(FaceSort, "0"); + + plIcicle *span = (plIcicle *)fSpans[ index ]; + plGBufferTriangle *list, temp; + UInt32 numTris; + int i; + hsMatrix44 w2cMatrix = pipe->GetWorldToCamera() * pipe->GetLocalToWorld(); + hsScalar dist; + + ICheckSpanForSortable(index); + + static hsTArray sortList; + static hsTArray tempTriList; + hsRadixSort::Elem *elem; + + + /// Get some stuff + list = span->fSortData; + numTris = span->fILength / 3; + + plProfile_IncCount(FacesSorted, numTris); + + hsAssert( numTris > 0, "How could we start sorting no triangles??" ); + + /// Sort the triangles in "list" + sortList.SetCount( numTris ); + tempTriList.SetCount( numTris * 3 ); + elem = sortList.AcquireArray(); + + plProfile_EndLap(FaceSort, "0"); + plProfile_BeginLap(FaceSort, "1"); + + hsVector3 vec(w2cMatrix.fMap[2][0], w2cMatrix.fMap[2][1], w2cMatrix.fMap[2][2]); + hsScalar trans = w2cMatrix.fMap[2][3]; + + // Fill out the radix sort elements with our data + for( i = 0; i < numTris; i++ ) + { + dist = vec.InnerProduct(list[ i ].fCenter) + trans; + elem[ i ].fKey.fFloat = dist; + elem[ i ].fBody = &list[ i ]; + elem[ i ].fNext = elem + i + 1; + } + elem[ i - 1 ].fNext = nil; + + plProfile_EndLap(FaceSort, "1"); + plProfile_BeginLap(FaceSort, "2"); + + // Do da sort thingy + hsRadixSort rad; + hsRadixSort::Elem *sortedList = rad.Sort( elem, 0 ); + + plProfile_EndLap(FaceSort, "2"); + plProfile_BeginLap(FaceSort, "3"); + + UInt16* indices = tempTriList.AcquireArray(); + // Stuff into the temp array + for( i = 0, elem = sortedList; i < numTris; i++ ) + { + *indices++ = ((plGBufferTriangle *)elem->fBody)->fIndex1; + *indices++ = ((plGBufferTriangle *)elem->fBody)->fIndex2; + *indices++ = ((plGBufferTriangle *)elem->fBody)->fIndex3; + elem = elem->fNext; + } + + plProfile_EndLap(FaceSort, "3"); + plProfile_BeginLap(FaceSort, "4"); + + /// Now send them on to the buffer group + fGroups[ span->fGroupIdx ]->StuffFromTriList( span->fIBufferIdx, span->fIStartIdx, + numTris, tempTriList.AcquireArray() ); + + /// Optional step in a way: copy back our new, sorted list to our original + /// array. This lets us do less sorting next call, since the order should + /// remain largely unchanged from the last call. If this turns out NOT to + /// be the case, or if the sorting step takes less time than this copy, + /// take this out! +// memcpy( list, tempTriList.AcquireArray(), numTris * sizeof( plGBufferTriangle ) ); + + /// All done! (force buffer groups to refresh during next render call) + fReadyToRender = false; + + plProfile_EndLap(FaceSort, "4"); +} + +//// SortVisibleSpans //////////////////////////////////////////////////////// +// Sorts the visible spans's triangles in one big lump, for proper back-to- +// front display. +// Updated 5.14.2001 mcn - Fixed so loops don't assume spans are icicles + +void plDrawableSpans::SortVisibleSpans(const hsTArray& visList, plPipeline* pipe) +{ +#define MF_CHUNKSORT +#ifndef MF_CHUNKSORT + + plProfile_Inc(FaceSortCalls); + + if( !visList.GetCount() ) + return; + + + static hsLargeArray sortScratch; + static hsLargeArray triList; + static hsTArray counters; + static hsTArray startIndex; + + int i; + + plProfile_BeginTiming(FaceSort); + if( pipe->IsDebugFlagSet( plPipeDbg::kFlagDontSortFaces ) ) + { + /// Don't sort, just send unchanged + int j, idx; + + for( i = 0; i < visList.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + ICheckSpanForSortable(visList[i]); + + /// Build a fake list of indices.... + plGBufferTriangle* list = span->fSortData; + triList.SetCount( span->fILength ); + for( j = 0, idx = 0; j < span->fILength / 3; j++, idx += 3 ) + { + triList[ idx ] = list[ j ].fIndex1; + triList[ idx + 1 ] = list[ j ].fIndex2; + triList[ idx + 2 ] = list[ j ].fIndex3; + } + + /// Now send them on to the buffer group + fGroups[ span->fGroupIdx ]->StuffFromTriList( span->fIBufferIdx, span->fIStartIdx, + span->fILength / 3, triList.AcquireArray() ); + } + fReadyToRender = false; + return; + } + plProfile_EndTiming(FaceSort); + + plProfile_BeginLap(FaceSort, "0"); + + startIndex.SetCount(fSpans.GetCount()); + + // First figure out the total number of tris to deal with. + int totTris = 0; + for( i = 0; i < visList.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + + startIndex[visList[i]] = totTris * 3; + if( span->fProps & plSpan::kPropReverseSort ) + startIndex[visList[i]] += span->fILength - 3; + + totTris += span->fILength / 3; + } + if( totTris == 0 ) + { + plProfile_EndLap(FaceSort, "0"); + return; + } + + plProfile_IncCount(FacesSorted, totTris); + + sortScratch.SetCount(totTris); + triList.SetCount(3 * totTris); + + hsRadixSort::Elem* elem = sortScratch.AcquireArray(); + + plProfile_EndLap(FaceSort, "0"); + plProfile_BeginLap(FaceSort, "1"); + + // Pack them into the sort structure. We probably want to make the + // plGBufferTriangle look like plTriSortData (just add span index) + // which would get rid of this copy and help the data alignment. + // Oops, I already did. + int cnt = 0; + for( i = 0; i < visList.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + + int nTris = span->fILength / 3; + + hsPoint3 viewPos = span->fWorldToLocal * pipe->GetViewPositionWorld(); + + plGBufferTriangle* list = span->fSortData; + int j; + for( j = 0; j < nTris; j++ ) + { + hsScalar dist = -(viewPos - list[j].fCenter).MagnitudeSquared(); + elem[cnt].fKey.fFloat = dist; + elem[cnt].fBody = &list[j]; + elem[cnt].fNext = elem + cnt + 1; + + cnt++; + } + } + elem[cnt-1].fNext = nil; + + plProfile_EndLap(FaceSort, "1"); + plProfile_BeginLap(FaceSort, "2"); + + // Actual sort + hsRadixSort rad; + hsRadixSort::Elem* sortedList = rad.Sort( elem, 0 ); + + plProfile_EndLap(FaceSort, "2"); + plProfile_BeginLap(FaceSort, "3"); + + counters.SetCountAndZero(fSpans.GetCount()); + + while( sortedList ) + { + plGBufferTriangle* data = (plGBufferTriangle*)sortedList->fBody; + plIcicle* span = (plIcicle*)fSpans[data->fSpanIndex]; + + UInt16* idx = &triList[startIndex[data->fSpanIndex] + counters[data->fSpanIndex]]; + *idx++ = data->fIndex1; + *idx++ = data->fIndex2; + *idx++ = data->fIndex3; + if( span->fProps & plSpan::kPropReverseSort ) + counters[data->fSpanIndex] -= 3; + else + counters[data->fSpanIndex] += 3; + + sortedList = sortedList->fNext; + } + + plProfile_EndLap(FaceSort, "3"); + plProfile_BeginLap(FaceSort, "4"); + + const int kMaxBufferGroups = 20; + const int kMaxIndexBuffers = 20; + static Int16 newStarts[kMaxBufferGroups][kMaxIndexBuffers]; + + // Temp hack stuff so we can switch back and forth (to see if it really does any good). + static int oldWay = false; + static int lastOldWay = oldWay; + if( oldWay != lastOldWay ) + { + memset(newStarts, 0, kMaxBufferGroups * kMaxIndexBuffers * sizeof(Int16)); + + for( i = 0; i < fSpans.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[i]; + + span->fPackedIdx = span->fIStartIdx = newStarts[span->fGroupIdx][span->fIBufferIdx]; + newStarts[span->fGroupIdx][span->fIBufferIdx] += span->fILength; + } + lastOldWay = oldWay; + } + + if( oldWay ) + { + for( i = 0; i < visList.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + + /// Now send them on to the buffer group + fGroups[ span->fGroupIdx ]->StuffFromTriList( span->fIBufferIdx, span->fIStartIdx, + span->fILength / 3, triList.AcquireArray() + startIndex[visList[i]]); + } + } + else + { + memset(newStarts, 0, kMaxBufferGroups * kMaxIndexBuffers * sizeof(Int16)); + + UInt32 start = 0; + for( i = 0; i < visList.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + + /// Now send them on to the buffer group + span->fPackedIdx = span->fIStartIdx = newStarts[span->fGroupIdx][span->fIBufferIdx]; + newStarts[span->fGroupIdx][span->fIBufferIdx] += span->fILength; + fGroups[ span->fGroupIdx ]->StuffFromTriList( span->fIBufferIdx, span->fIStartIdx, + span->fILength / 3, triList.AcquireArray() + startIndex[visList[i]]); + } + } + + plProfile_EndLap(FaceSort, "4"); + + fReadyToRender = false; + + return; + +#else // MF_CHUNKSORT + + plProfile_Inc(FaceSortCalls); + + if( !visList.GetCount() ) + return; + + plProfile_BeginTiming(FaceSort); + + static hsLargeArray sortScratch; + static hsLargeArray triList; + static hsTArray counters; + static hsTArray startIndex; + + int i; + + if( pipe->IsDebugFlagSet( plPipeDbg::kFlagDontSortFaces ) ) + { + /// Don't sort, just send unchanged + int j, idx; + + for( i = 0; i < visList.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + ICheckSpanForSortable(visList[i]); + + /// Build a fake list of indices.... + plGBufferTriangle* list = span->fSortData; + triList.SetCount( span->fILength ); + for( j = 0, idx = 0; j < span->fILength / 3; j++, idx += 3 ) + { + triList[ idx ] = list[ j ].fIndex1; + triList[ idx + 1 ] = list[ j ].fIndex2; + triList[ idx + 2 ] = list[ j ].fIndex3; + } + + /// Now send them on to the buffer group + fGroups[ span->fGroupIdx ]->StuffFromTriList( span->fIBufferIdx, span->fIStartIdx, + span->fILength / 3, triList.AcquireArray() ); + } + fReadyToRender = false; + return; + } + + plProfile_EndTiming(FaceSort); + + plProfile_BeginLap(FaceSort, "0"); + + startIndex.SetCount(fSpans.GetCount()); + + // First figure out the total number of tris to deal with. + int totTris = 0; + for( i = 0; i < visList.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + ICheckSpanForSortable(visList[i]); + + startIndex[visList[i]] = totTris * 3; + if( span->fProps & plSpan::kPropReverseSort ) + startIndex[visList[i]] += span->fILength - 3; + + + totTris += span->fILength / 3; + } + if( totTris == 0 ) + { + plProfile_EndLap(FaceSort, "0"); + return; + } + + plProfile_IncCount(FacesSorted, totTris); + + sortScratch.SetCount(totTris); + triList.SetCount(3 * totTris); + + hsRadixSort::Elem* elem = sortScratch.AcquireArray(); + + plProfile_EndLap(FaceSort, "0"); + + int iVis = 0; + while( iVis < visList.GetCount() ) + { + plProfile_BeginLap(FaceSort, "1"); + + // Pack them into the sort structure. We probably want to make the + // plGBufferTriangle look like plTriSortData (just add span index) + // which would get rid of this copy and help the data alignment. + // Oops, I already did. + const int kTriCutoff = 4000; + int cnt = 0; + while( (iVis < visList.GetCount()) && (cnt < kTriCutoff) ) + { + plIcicle* span = (plIcicle*)fSpans[visList[iVis]]; + + int nTris = span->fILength / 3; + + hsPoint3 viewPos = span->fWorldToLocal * pipe->GetViewPositionWorld(); + + plGBufferTriangle* list = span->fSortData; + int j; + for( j = 0; j < nTris; j++ ) + { + hsScalar dist = -(viewPos - list[j].fCenter).MagnitudeSquared(); + elem[cnt].fKey.fFloat = dist; + elem[cnt].fBody = &list[j]; + elem[cnt].fNext = elem + cnt + 1; + + cnt++; + } + iVis++; + } + elem[cnt-1].fNext = nil; + + plProfile_EndLap(FaceSort, "1"); + plProfile_BeginLap(FaceSort, "2"); + + // Actual sort + hsRadixSort rad; + hsRadixSort::Elem* sortedList = rad.Sort( elem, 0 ); + + plProfile_EndLap(FaceSort, "2"); + plProfile_BeginLap(FaceSort, "3"); + + counters.SetCountAndZero(fSpans.GetCount()); + + while( sortedList ) + { + plGBufferTriangle* data = (plGBufferTriangle*)sortedList->fBody; + plIcicle* span = (plIcicle*)fSpans[data->fSpanIndex]; + + UInt16* idx = &triList[startIndex[data->fSpanIndex] + counters[data->fSpanIndex]]; + *idx++ = data->fIndex1; + *idx++ = data->fIndex2; + *idx++ = data->fIndex3; + if( span->fProps & plSpan::kPropReverseSort ) + counters[data->fSpanIndex] -= 3; + else + counters[data->fSpanIndex] += 3; + + sortedList = sortedList->fNext; + } + + plProfile_EndLap(FaceSort, "3"); + } + + plProfile_BeginLap(FaceSort, "4"); + + const int kMaxBufferGroups = 20; + const int kMaxIndexBuffers = 20; + static Int16 newStarts[kMaxBufferGroups][kMaxIndexBuffers]; + + hsAssert(kMaxBufferGroups >= GetNumBufferGroups(), "Bigger than we counted on num groups sort."); + + memset(newStarts, 0, kMaxBufferGroups * kMaxIndexBuffers * sizeof(Int16)); + + UInt32 start = 0; + for( i = 0; i < visList.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + + hsAssert(kMaxIndexBuffers > span->fIBufferIdx, "Bigger than we counted on num buffers sort."); + + if( span->fProps & plSpan::kPropReverseSort ) + startIndex[visList[i]] -= span->fILength - 3; + + /// Now send them on to the buffer group + span->fIPackedIdx = span->fIStartIdx = newStarts[span->fGroupIdx][span->fIBufferIdx]; + newStarts[span->fGroupIdx][span->fIBufferIdx] += (Int16)(span->fILength); + fGroups[ span->fGroupIdx ]->StuffFromTriList( span->fIBufferIdx, span->fIStartIdx, + span->fILength / 3, triList.AcquireArray() + startIndex[visList[i]]); + } + + plProfile_EndLap(FaceSort, "4"); + + fReadyToRender = false; + +#endif // MF_CHUNKSORT +} + +struct buffTriCmpBackToFront : public std::binary_function +{ + hsPoint3 fViewPos; + buffTriCmpBackToFront(const hsPoint3& p) : fViewPos(p) {} + + bool operator()( const plGBufferTriangle& lhs, const plGBufferTriangle& rhs) const + { + return hsVector3(&fViewPos, &lhs.fCenter).MagnitudeSquared() > hsVector3(&fViewPos, &rhs.fCenter).MagnitudeSquared(); + } +}; + +struct buffTriCmpFrontToBack : public std::binary_function +{ + hsPoint3 fViewPos; + buffTriCmpFrontToBack(const hsPoint3& p) : fViewPos(p) {} + + bool operator()( const plGBufferTriangle& lhs, const plGBufferTriangle& rhs) const + { + return hsVector3(&fViewPos, &lhs.fCenter).MagnitudeSquared() < hsVector3(&fViewPos, &rhs.fCenter).MagnitudeSquared(); + } +}; + +void plDrawableSpans::SortVisibleSpansPartial(const hsTArray& visList, plPipeline* pipe) +{ + plProfile_Inc(FaceSortCalls); + + plProfile_BeginTiming(FaceSort); + + int i; + for( i = 0; i < visList.GetCount(); i++ ) + { + hsAssert(fSpans[visList[i]]->fTypeMask & plSpan::kIcicleSpan, "Unknown type for sorting faces"); + + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + + if( span->fProps & plSpan::kPartialSort ) + { + hsAssert(fGroups[span->fGroupIdx]->AreIdxVolatile(), "Badly setup buffer group - set PartialSort too late?"); + + ICheckSpanForSortable(visList[i]); + + const hsPoint3 viewPos = span->fWorldToLocal * pipe->GetViewPositionWorld(); + + const int numTris = span->fILength/3; + std::sort(span->fSortData, span->fSortData+numTris, buffTriCmpBackToFront(viewPos)); + + UInt16* idx = fGroups[span->fGroupIdx]->GetIndexBufferData(span->fIBufferIdx) + span->fIStartIdx; + plGBufferTriangle* iter = span->fSortData; + int j; + for( j = 0; j < numTris; j++ ) + { + *idx++ = iter->fIndex1; + *idx++ = iter->fIndex2; + *idx++ = iter->fIndex3; + iter++; + } + + fGroups[span->fGroupIdx]->DirtyIndexBuffer(span->fIBufferIdx); + fReadyToRender = false; + } + } + + plProfile_EndTiming(FaceSort); +} + +#if 0 + +void plDrawableSpans::SortVisibleSpansUnit(const hsTArray& visList, plPipeline* pipe) +{ + plProfile_Inc(FaceSortCalls); + + if( !visList.GetCount() ) + return; + + plProfile_BeginTiming(FaceSort); + + static hsLargeArray triList; + + int i; + + if( pipe->IsDebugFlagSet( plPipeDbg::kFlagDontSortFaces ) ) + { + /// Don't sort, just send unchanged + int j, idx; + + for( i = 0; i < visList.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + ICheckSpanForSortable(visList[i]); + + /// Build a fake list of indices.... + plGBufferTriangle* list = span->fSortData; + triList.SetCount( span->fILength ); + for( j = 0, idx = 0; j < span->fILength / 3; j++, idx += 3 ) + { + triList[ idx ] = list[ j ].fIndex1; + triList[ idx + 1 ] = list[ j ].fIndex2; + triList[ idx + 2 ] = list[ j ].fIndex3; + } + + /// Now send them on to the buffer group + fGroups[ span->fGroupIdx ]->StuffFromTriList( span->fIBufferIdx, span->fIStartIdx, + span->fILength / 3, triList.AcquireArray() ); + } + fReadyToRender = false; + return; + } + + plProfile_EndTiming(FaceSort); + + plProfile_BeginLap(FaceSort, "0"); + + struct sortFace + { + UInt16 fIndex0; + UInt16 fIndex1; + UInt16 fIndex2; + hsScalar fDist; + }; + static hsLargeArray sortList; + + struct SelectCloserFace + { + bool operator()(const sortFace* face0, const sortFace* face1) const + { + return face0->fDist < face1->fDist; + } + }; + hsPoint3 viewPos = fSpans[visList[0]]->fWorldToLocal * pipe->GetViewPositionWorld(); + + hsScalar dist1 = (fViewPos - face1->fCenter).MagnitudeSquared(); + + // First figure out the total number of tris to deal with. + sortList.SetCount(0); + int totTris = 0; + for( i = 0; i < visList.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + + int nTris = span->fILength / 3; + sortList.Expand(sortList.GetCount() + nTris); + + plGBufferTriangle* sortData = span->fSortData; + for( j = 0; j < nTris; j++ ) + { + sortList.Append(*sortData++); + } + + totTris += nTris; + } + if( totTris == 0 ) + { + plProfile_EndLap(FaceSort, "0"); + return; + } + + plProfile_IncCount(FacesSorted, totTris); + + sortFace* pBegin = sortList.AcquireArray(); + sortFace* pEnd = pBegin + totTris; + + stl::sort(pBegin, pEnd, SelectCloserFace); + + triList.SetCount(sortList.GetCount()); + + UInt16* pTri = triList.AcquireArray(); + + while( pBegin < pEnd ) + { + *pTri = pBegin->fIndex1; + pTri++; + *pTri = pBegin->fIndex2; + pTri++; + *pTri = pBegin->fIndex3; + pTri++; + } + + + plProfile_EndLap(FaceSort, "0"); + + UInt32 start = 0; + for( i = 0; i < visList.GetCount(); i++ ) + { + plIcicle* span = (plIcicle*)fSpans[visList[i]]; + + /// Now send them on to the buffer group + span->fIPackedIdx = span->fIStartIdx = newStarts[span->fGroupIdx][span->fIBufferIdx]; + newStarts[span->fGroupIdx][span->fIBufferIdx] += span->fILength; + fGroups[ span->fGroupIdx ]->StuffFromTriList( span->fIBufferIdx, span->fIStartIdx, + span->fILength / 3, triList.AcquireArray() + startIndex[visList[i]]); + } + + plProfile_EndLap(FaceSort, "4"); + + fReadyToRender = false; +} +#endif + +void plDrawableSpans::SetInitialBone(int i, const hsMatrix44& l2b, const hsMatrix44& b2l) +{ + fLocalToBones[ i ] = l2b; + fBoneToLocals[ i ] = b2l; +} + +//// AppendDIMatrixSpans /////////////////////////////////////////////////////////// +// Adds a di span which will only reference into the matrix palette. That is, +// these indices won't index into any drawable data, they will only index into +// the same matrix list that the drawable data itself can index into. When +// an object (through its DrawInterface) sets a transform, it doesn't know +// whether it's setting the transform for some drawable data it owns, or just +// setting one of the matrices which influence the drawable data someone else +// owns. +UInt32 plDrawableSpans::AppendDIMatrixSpans(int n) +{ + /// Do garbage cleanup first + if( fNeedCleanup ) + IRemoveGarbage(); + + UInt32 baseIdx = fLocalToWorlds.GetCount(); + fLocalToWorlds.Expand(baseIdx + n); + fLocalToWorlds.SetCount(baseIdx + n); + fWorldToLocals.Expand(baseIdx + n); + fWorldToLocals.SetCount(baseIdx + n); + + fLocalToBones.Expand(baseIdx + n); + fLocalToBones.SetCount(baseIdx + n); + fBoneToLocals.Expand(baseIdx + n); + fBoneToLocals.SetCount(baseIdx + n); + + int i; + for( i = baseIdx; i < baseIdx + n; i++ ) + { + fLocalToWorlds[i].Reset(); + fWorldToLocals[i].Reset(); + + fLocalToBones[i].Reset(); + fBoneToLocals[i].Reset(); + } + + return baseIdx; +} + +// Look for a compatible set of palette matrices. Compatible means: +// a) Same number bones +// b) Same LocalToBones matrices (which implies same BoneToLocal matrices. +// Note that the LocalToBone transform is dependent on both the bone's transform +// and the transform of the object being skinned. In general, objects can only +// share a palette set if they have been flattened into world space (the object's +// transform is identity). Fortunately, this is a common case. +UInt32 plDrawableSpans::FindBoneBaseMatrix(const hsTArray& initL2B, hsBool searchAll) const +{ + if (!searchAll) + { + // Just look amongst the added spans for a matching set + int i; + for( i = 0; i < fSpans.GetCount(); i++ ) + { + if( fSpans[i] && (initL2B.GetCount() == fSpans[i]->fNumMatrices) ) + { + int j; + for( j = 0; j < initL2B.GetCount(); j++ ) + { + if( initL2B[j] != fLocalToBones[j + fSpans[i]->fBaseMatrix] ) + break; + } + if( initL2B.GetCount() == j ) + return fSpans[i]->fBaseMatrix; + } + } + } + else + { + // Since swappable geometry spans aren't added to the drawable until + // runtime, a sharable bone pallete won't be found by scanning fSpans. + // We have to do a larger search through all bone matrices. + int i; + for( i = 0; i + initL2B.GetCount() < fLocalToBones.GetCount(); i++ ) + { + int j; + for( j = 0; j < initL2B.GetCount(); j++ ) + { + if( initL2B[j] != fLocalToBones[j + i] ) + break; + } + if( initL2B.GetCount() == j ) + return i; + } + } + return UInt32(-1); +} + +UInt32 plDrawableSpans::NewDIMatrixIndex() +{ + int index; + /// Do we have a free lookup entry? + for( index = 0; index < fDIIndices.GetCount(); index++ ) + { + if( !fDIIndices[ index ]->GetCount() ) + break; + } + if( index == fDIIndices.GetCount() ) + fDIIndices.Append( TRACKED_NEW plDISpanIndex ); + + fDIIndices[index]->Reset(); + fDIIndices[index]->fFlags = plDISpanIndex::kMatrixOnly; + + return index; +} + +//// IFindDIIndices ////////////////////////////////////////////////////////// +// Finds the given DIIndices array and returns a pointer to it, or creates +// a new one if requested + +plDISpanIndex *plDrawableSpans::IFindDIIndices( UInt32 &index ) +{ + plDISpanIndex *spanLookup; + + + /// Do we have a free lookup entry? + if( index == (UInt32)-1 ) // new index + { + for( index = 0; index < fDIIndices.GetCount(); index++ ) + { + if( fDIIndices[ index ]->GetCount() == 0 ) + break; + } + if( index == fDIIndices.GetCount() ) + fDIIndices.Append( TRACKED_NEW plDISpanIndex ); + + spanLookup = fDIIndices[ index ]; + spanLookup->fFlags = plDISpanIndex::kNone; + } + else + spanLookup = fDIIndices[ index ]; // Just grab the one we requested + + return spanLookup; +} + +//// AppendDISpans /////////////////////////////////////////////////////////// +// Given a set of DI spans, appends them to the current storage buffers. +// Note: AddDISpans() adds the spans to a list to be sorted, THEN put into +// the buffers; this shoves them right in, bypassing the sorting altogether. + +UInt32 plDrawableSpans::AppendDISpans( hsTArray &spans, UInt32 index, hsBool clearSpansAfterAdd, + hsBool doNotAddToSource, hsBool addToFront, int lod) +{ + hsAssert(spans.GetCount(), "Adding no spans? Blow me."); + + int i, j; + UInt32 spanIdx; + plSpan *span; + hsBounds3Ext bounds; + plDISpanIndex *spanLookup; + UInt32 numAddedIcicle = 0; + +// hsAssert( fProps & kPropVolatile, "Trying to add spans on a non-volatile drawable" ); + + /// Do garbage cleanup first + if( fNeedCleanup ) + IRemoveGarbage(); + + spanLookup = IFindDIIndices( index ); + + if( GetNativeProperty(plDrawable::kPropCharacter) ) + fVisSet.SetBit(plVisMgr::kCharacter, true); + + int insertionPoint = 0; + if( spans[0]->fProps & plGeometrySpan::kPartialSort ) + { + insertionPoint = fSpans.GetCount(); + } + else if( !addToFront ) + { + int idx; + for( idx = 0; (idx < fSpans.GetCount()) && !(fSpans[idx]->fProps & plSpan::kPartialSort); idx++ ) + { + } + insertionPoint = idx; + } + hsBool inserted = insertionPoint < fSpans.GetCount(); + + /// Add the geometry spans to our list. Also add our internal span + /// copies + for( i = 0; i < spans.GetCount(); i++ ) + { + spanIdx = fIcicles.GetCount(); + fIcicles.Append( plIcicle() ); + plIcicle *icicle = &fIcicles[ spanIdx ]; + IConvertGeoSpanToIcicle( spans[ i ], icicle, lod ); + span = (plSpan *)icicle; + numAddedIcicle++; + + /// Add to our source indices + spans[ i ]->fSpanRefIndex = insertionPoint+i; + spanLookup->Append( spans[ i ]->fSpanRefIndex ); + fSpans.Insert( spans[ i ]->fSpanRefIndex, span ); + fSpanSourceIndices.Insert( spans[ i ]->fSpanRefIndex, spanIdx ); + + if( GetNativeProperty(plDrawable::kPropCharacter) ) + span->SetVisBit(plVisMgr::kCharacter, true); + + /// Add the material to our list if necessary + IAssignMatIdxToSpan( span, spans[ i ]->fMaterial ); + + if( clearSpansAfterAdd ) + { + delete spans[ i ]; + spans[ i ] = nil; + } + else if( !doNotAddToSource ) + { + if( fSourceSpans.GetCount() < fSpans.GetCount() ) + { + fSourceSpans.Expand( fSpans.GetCount() ); + // Since that does not change the use count, we still have to do that ourselves. ARGH! + fSourceSpans.SetCount( fSpans.GetCount() ); + } + + fSourceSpans[ spans[ i ]->fSpanRefIndex ] = spans[ i ]; + } + + if( fProps & kPropSortFaces ) + { + // Should add sort data too... + IMakeSpanSortable( fSpans.GetCount() - 1 ); + } + } + + if (inserted) + { + /// Go adjusting indices in the DI index list + for( i = 0; i < fDIIndices.GetCount(); i++ ) + { + if( !fDIIndices[ i ]->IsMatrixOnly() ) + { + if (fDIIndices[ i ] == spanLookup) + continue; + + for( j = 0; j < fDIIndices[ i ]->GetCount(); j++ ) + { + if( (*fDIIndices[i])[j] >= insertionPoint ) + { + (*fDIIndices[ i ])[ j ] += spans.GetCount(); + hsAssert((*fDIIndices[ i ])[ j ] < fSpans.GetCount(), "Span index snafu"); + } + } + } + } + } + + // Update fLocalBounds, since we were updating the world bounds + fLocalBounds = fWorldBounds; + fLocalBounds.Transform( &fWorldToLocal ); + fMaxWorldBounds = fWorldBounds; + + /// Rebuild the pointer array + IRebuildSpanArray(); + + fReadyToRender = false; + + return index; +} + +//// IAddAMaterial /////////////////////////////////////////////////////////// + +UInt32 plDrawableSpans::IAddAMaterial( hsGMaterial *material ) +{ + UInt32 i; + + + // Scan for if we already have it + for( i = 0; i < fMaterials.GetCount(); i++ ) + { + if( fMaterials[ i ] == material ) + return i; + } + + // Scan again for a blank space to add into, if possible + for( i = 0; i < fMaterials.GetCount(); i++ ) + { + if( fMaterials[ i ] == nil ) + { + fMaterials[ i ] = material; + IRefMaterial( i ); + return i; + } + } + + // Add in to the end + i = fMaterials.GetCount(); + fMaterials.Append( material ); + + // Plus ref it + IRefMaterial( i ); + + return i; +} + +//// IRefMaterial //////////////////////////////////////////////////////////// +// Called if we already have a material index that we just want to use +// again... + +UInt32 plDrawableSpans::IRefMaterial( UInt32 index ) +{ + hsGMaterial *material = fMaterials[ index ]; + + if( GetKey() && material != nil && material->GetKey() != nil ) + hsgResMgr::ResMgr()->AddViaNotify( material->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, index, 0 ), plRefFlags::kActiveRef ); + + return index; +} + +//// ICheckToRemoveMaterial ////////////////////////////////////////////////// +// Runs through the span array to see if the given material index is still +// used. If not, Release()s it and nil's it. + +void plDrawableSpans::ICheckToRemoveMaterial( UInt32 materialIdx ) +{ + int j; + hsGMaterial *mat; + + + for( j = 0; j < fSpans.GetCount(); j++ ) + { + if( fSpans[ j ]->fMaterialIdx == materialIdx ) + break; + } + if( j == fSpans.GetCount() ) + { + /// No longer used--Release() it + mat = fMaterials[ materialIdx ]; + if( GetKey() && mat != nil && mat->GetKey() != nil ) + GetKey()->Release( mat->GetKey() ); + fMaterials[ materialIdx ] = nil; + } +} + +//// IConvertGeoSpanToVertexSpan ///////////////////////////////////////////// +// Helper function for the two vertex-based convert functions. + +hsBool plDrawableSpans::IConvertGeoSpanToVertexSpan( plGeometrySpan *geoSpan, plVertexSpan *span, int lod, plVertexSpan *instancedParent) +{ + hsBounds3Ext bounds; + UInt8 groupIdx; + UInt32 vbIndex, cellIdx, cellOffset; + + + span->fLocalToWorld = geoSpan->fLocalToWorld; + span->fWorldToLocal = geoSpan->fWorldToLocal; + span->fProps |= ( geoSpan->fProps & plGeometrySpan::kPropRunTimeLight ) ? plSpan::kPropRunTimeLight : 0; + if( geoSpan->fProps & plGeometrySpan::kPropNoShadowCast ) + span->fProps |= plSpan::kPropNoShadowCast; + if( geoSpan->fProps & plGeometrySpan::kPropNoShadow ) + span->fProps |= plSpan::kPropNoShadow; + if( geoSpan->fProps & plGeometrySpan::kPropForceShadow ) + span->fProps |= plSpan::kPropForceShadow; + if( geoSpan->fProps & plGeometrySpan::kPropReverseSort ) + span->fProps |= plSpan::kPropReverseSort; + if( geoSpan->fProps & plGeometrySpan::kPartialSort ) + span->fProps |= plSpan::kPartialSort; + switch( geoSpan->fProps & plGeometrySpan::kLiteMask ) + { + case plGeometrySpan::kLiteMaterial: span->fProps |= plSpan::kLiteMaterial; break; + case plGeometrySpan::kLiteVtxPreshaded: span->fProps |= plSpan::kLiteVtxPreshaded; break; + case plGeometrySpan::kLiteVtxNonPreshaded: span->fProps |= plSpan::kLiteVtxNonPreshaded; break; + } + if( geoSpan->fProps & plGeometrySpan::kWaterHeight ) + { + span->fProps |= plSpan::kWaterHeight; + span->fWaterHeight = geoSpan->fWaterHeight; + } + if( geoSpan->fProps & plGeometrySpan::kVisLOS ) + { + span->fProps |= plSpan::kVisLOS; + fProps |= plDrawable::kPropHasVisLOS; + } + + span->fNumMatrices = geoSpan->fNumMatrices; + span->fBaseMatrix = geoSpan->fBaseMatrix; + span->fLocalUVWChans = geoSpan->fLocalUVWChans; + span->fMaxBoneIdx = geoSpan->fMaxBoneIdx; + span->fPenBoneIdx = (UInt16)(geoSpan->fPenBoneIdx); + span->fMinDist = geoSpan->fMinDist; + span->fMaxDist = geoSpan->fMaxDist; + + bounds = geoSpan->fLocalBounds; + span->fLocalBounds = bounds; + bounds.Transform( &span->fLocalToWorld ); + span->fWorldBounds = bounds; + fWorldBounds.Union( &bounds ); + span->fFogEnvironment = geoSpan->fFogEnviron; + + hsBool vertsVol = false; + if( fProps & kPropVolatile ) + vertsVol = true; + + hsBool idxVol = false; + if( fProps & kPropSortFaces ) + idxVol = true; + if( geoSpan->fProps & plGeometrySpan::kPartialSort ) + idxVol = true; + + // Are we instanced? + if( instancedParent != nil /*&& !( fProps & kPropVolatile )*/ ) + { + /// We can instance w/o vert data IF 1) we're not the first span, 2) we can fit into the same buffer group, and + /// 3) we can fit into the same vertex buffer in the buffer group + if( instancedParent != span && ( geoSpan->fProps & plGeometrySpan::kPropNoPreShade ) ) + { + /// WOW! We can actually share the *exact* same data, since we don't do any preshading. Coooooool! + span->fGroupIdx = instancedParent->fGroupIdx; + span->fVBufferIdx = instancedParent->fVBufferIdx; + span->fVStartIdx = instancedParent->fVStartIdx; + cellIdx = instancedParent->fCellIdx; + cellOffset = instancedParent->fCellOffset; + } + else if( instancedParent != span && + ( IFindBufferGroup( geoSpan->fFormat, geoSpan->fNumVerts, lod, vertsVol, idxVol ) == instancedParent->fGroupIdx ) && + fGroups[ instancedParent->fGroupIdx ]->GetNumVertsLeft( instancedParent->fVBufferIdx ) >= geoSpan->fNumVerts ) + { + /// Boooring.... + /// Append the colors, but the vertices themselves we don't use, rather we point + /// to our parent's verts (or rather, the cell will take care of that for us) + groupIdx = (UInt8)(instancedParent->fGroupIdx); + fGroups[ groupIdx ]->AppendToColorStorage( geoSpan, &vbIndex, &cellIdx, &cellOffset, instancedParent->fCellIdx ); + + span->fGroupIdx = groupIdx; + span->fVBufferIdx = instancedParent->fVBufferIdx; + // Do a new vStart here, since it's really the colorStart, but oh well + span->fVStartIdx = fGroups[ groupIdx ]->GetVertStartFromCell( vbIndex, cellIdx, cellOffset ); + } + else + { + /// WE'RE the parent?? This means we're the first to get converted + groupIdx = IFindBufferGroup( geoSpan->fFormat, geoSpan->fNumVerts, lod, vertsVol, idxVol ); + fGroups[ groupIdx ]->AppendToVertAndColorStorage( geoSpan, &vbIndex, &cellIdx, &cellOffset ); + + span->fGroupIdx = groupIdx; + span->fVBufferIdx = vbIndex; + span->fVStartIdx = fGroups[ groupIdx ]->GetVertStartFromCell( vbIndex, cellIdx, cellOffset ); + geoSpan->fProps |= plGeometrySpan::kFirstInstance; + } + } + else + { + // Pack the vertices in + groupIdx = IFindBufferGroup( geoSpan->fFormat, geoSpan->fNumVerts, lod, vertsVol, idxVol ); + fGroups[ groupIdx ]->AppendToVertStorage( geoSpan, &vbIndex, &cellIdx, &cellOffset ); + + span->fGroupIdx = groupIdx; + span->fVBufferIdx = vbIndex; + span->fVStartIdx = fGroups[ groupIdx ]->GetVertStartFromCell( vbIndex, cellIdx, cellOffset ); + geoSpan->fProps |= plGeometrySpan::kFirstInstance; + } + + span->fCellIdx = cellIdx; + span->fCellOffset = cellOffset; + span->fVLength = geoSpan->fNumVerts; + + /// Add the material to our list if necessary + span->fMaterialIdx = IAddAMaterial( geoSpan->fMaterial ); + + return true; +} + +//// IConvertGeoSpanToIcicle ///////////////////////////////////////////////// + +hsBool plDrawableSpans::IConvertGeoSpanToIcicle(plGeometrySpan *geoSpan, plIcicle *icicle, int lod, plIcicle *instancedParent) +{ + UInt32 ibIndex, ibStart; + + + IConvertGeoSpanToVertexSpan(geoSpan, icicle, lod, instancedParent); + +/* + Disabling this 8.16.2001. Works great until you have to SORT the faces, then things blow up. We could + do this only when we won't sort faces, but it's easier right now to just never do this ever. + + if( instancedParent != nil && instancedParent != icicle && ( icicle->fProps & plSpan::kPropRunTimeLight ) ) + { + /// WOW! We can actually share the *exact* same data, since we don't do any preshading. Coooooool! + icicle->fIBufferIdx = instancedParent->fIBufferIdx; + icicle->fIStartIdx = instancedParent->fIStartIdx; + icicle->fILength = instancedParent->fILength; + icicle->fSortData = nil; + } + else +*/ + { + // Pack the indices in (automagically offsets by the start we give it) + fGroups[ icicle->fGroupIdx ]->AppendToIndexStorage( geoSpan->fNumIndices, geoSpan->fIndexData, + icicle->fVStartIdx, &ibIndex, &ibStart ); + + icicle->fIBufferIdx = ibIndex; + icicle->fIPackedIdx = icicle->fIStartIdx = ibStart; + icicle->fILength = geoSpan->fNumIndices; + icicle->fSortData = nil; + } + + return true; +} + +//// RemoveDIMatrixSpans +// Nuke out a matrix only DI index set someone doesn't need anymore (cuz they're dead). +// Since there's no data associated, there's not much to do, except mark the slot as +// free and that some cleanup is in order some time. +void plDrawableSpans::RemoveDIMatrixSpans( UInt32 index ) +{ + plDISpanIndex* spanIndices = fDIIndices[ index ]; + if( !spanIndices->IsMatrixOnly() ) + return; + + /// Flag that we need garbage cleanup now + fNeedCleanup = true; + fReadyToRender = false; + + spanIndices->Reset(); +} + +//// RemoveDISpans /////////////////////////////////////////////////////////// +// Given a drawInterface index (i.e. the index into fDIIndices), deletes +// the spans associated with that entry and clears the entry. Also packs +// the spans, deletes the verts/indices associated with the deleted spans +// and packs the vertex/index buffers as well. + +void plDrawableSpans::RemoveDISpans( UInt32 index ) +{ + plDISpanIndex *spanIndices = fDIIndices[ index ]; + + if( spanIndices->IsMatrixOnly() ) + { + RemoveDIMatrixSpans( index ); + } + + UInt32 i, j, k, idxRemoving, materialIdx; + + +// #define MF_RENDDEP +#ifdef MF_RENDDEP + hsTArray spanInverseTable; + spanInverseTable.SetCount(fSpans.GetCount()); + for( i = 0; i < fSpans.GetCount(); i++ ) + spanInverseTable[i] = i; +#endif // MF_RENDDEP + +// hsAssert( fProps & kPropVolatile, "Trying to remove spans on a non-volatile drawable" ); + hsAssert( spanIndices->GetCount() > 0, "If there are no DI spans, why were we called?" ); + + /// Delete the actual spans themselves + for( i = 0; i < spanIndices->GetCount(); i++ ) + { + /// If this is the last use of this material, Release() it + materialIdx = fSpans[ (*spanIndices)[ i ] ]->fMaterialIdx; + + /// Remove the source index and the object itself + if( fSourceSpans.GetCount() && !( fSpans[ (*spanIndices)[ i ] ]->fTypeMask & plSpan::kParticleSpan ) ) + { + delete fSourceSpans[ (*spanIndices)[i] ]; + fSourceSpans.Remove( (*spanIndices)[i] ); + for( j = (*spanIndices)[ i ]; j < fSourceSpans.GetCount(); j++ ) + fSourceSpans[ j ]->fSpanRefIndex = j; + } + fSpans[ (*spanIndices)[ i ] ]->Destroy(); + fSpans.Remove( (*spanIndices)[ i ] ); + idxRemoving = fSpanSourceIndices[ (*spanIndices)[ i ] ]; + fSpanSourceIndices.Remove( (*spanIndices)[ i ] ); + + switch( idxRemoving & kSpanTypeMask ) + { + case kSpanTypeIcicle: fIcicles.Remove( idxRemoving & kSpanIDMask ); break; + case kSpanTypeParticleSpan: fParticleSpans.Remove( idxRemoving & kSpanIDMask ); break; + } + + // Do this AFTER the span array has been updated + ICheckToRemoveMaterial( materialIdx ); + + /// Go adjusting the source indices + for( j = 0; j < fSpanSourceIndices.GetCount(); j++ ) + { + if( ( ( fSpanSourceIndices[ j ] ^ idxRemoving ) & kSpanTypeMask ) == 0 ) + { + /// Same type + k = fSpanSourceIndices[ j ] & kSpanIDMask; + if( k > ( idxRemoving & kSpanIDMask ) ) + { + k--; + fSpanSourceIndices[ j ] &= kSpanTypeMask; + fSpanSourceIndices[ j ] |= k; + } + } + } + +#ifndef MF_RENDDEP + /// Go adjusting indices in the DI index list + for( j = 0; j < fDIIndices.GetCount(); j++ ) + { + if( !fDIIndices[ j ]->IsMatrixOnly() ) + { + for( k = 0; k < fDIIndices[ j ]->GetCount(); k++ ) + { + if( (*fDIIndices[ j ])[ k ] > (*spanIndices)[ i ] ) + (*fDIIndices[ j ])[ k ]--; + } + } + } +#else MF_RENDDEP + spanInverseTable[(*spanIndices)[i]] = -1; + for( j = (*spanIndices)[i]; j < fSpans.GetCount(); j++ ) + spanInverseTable[j]--; +#endif // MF_RENDDEP + + } +#ifdef MF_RENDDEP + for( j = 0; j < fDIIndices.GetCount(); j++ ) + { + if( !fDIIndices[j]->IsMatrixOnly() ) + { + for( k = 0; k < fDIIndices[j]->GetCount(); k++ ) + { + int idx = (*fDIIndices[j])[k]; + hsAssert(idx >= 0, "Just deleted a span another DI was pointing at"); + (*fDIIndices[j])[k] = spanInverseTable[idx]; + } + } + } +#endif // MF_RENDDEP + + /// Rebuild the pointer array, since it's now invalid + IRebuildSpanArray(); + + /// Flag that we need garbage cleanup now + fNeedCleanup = true; + fReadyToRender = false; + + /// Clear this entry. Note: DON'T DELETE IT! Otherwise they'll be hell + /// to pay. Just clear it, so if we go looking for a free index, we just + /// look for one with zero spans. + spanIndices->Reset(); + + /// All done! +} + +//// IRebuildSpanArray /////////////////////////////////////////////////////// + +void plDrawableSpans::IRebuildSpanArray( void ) +{ + UInt32 j, i; + plIcicle *icicle = nil; + + + for( j = 0; j < fSpans.GetCount(); j++ ) + { + switch( fSpanSourceIndices[ j ] & kSpanTypeMask ) + { + case kSpanTypeIcicle: + icicle = &fIcicles[ fSpanSourceIndices[ j ] & kSpanIDMask ]; + fSpans[ j ] = (plSpan *)icicle; + if( icicle->fSortData != nil ) + { + // GOTTA RESET THE SPAN INDICES TOO!!! + for( i = 0; i < icicle->fILength / 3; i++ ) + icicle->fSortData[ i ].fSpanIndex = (UInt16)j; + } + break; + case kSpanTypeParticleSpan: + fSpans[ j ] = (plSpan *)&fParticleSpans[ fSpanSourceIndices[ j ] & kSpanIDMask ]; + break; + } + } + + // Redid the span array, so cause the space tree to rebuild to match + SetSpaceTree( nil ); + + // Rebuild bit vectors for various span types + IBuildVectors(); +} + + +//// ICleanupMatrices +// Find all the matrix slots in the palette that aren't being used, collapse +// them out and adjust the indices into the matrix palette (the MatrixOnly DIIndices) +void plDrawableSpans::ICleanupMatrices() +{ + // We really don't want to do this at runtime. The spans as read in have their bone + // matrices fixed (via plSpan::fBaseMatrix && plSpan::fNumMatrices). We can't just + // change where in our palette (fLocalToWorlds) bones will point, because the skinned spans + // will still be looking in the same place. I'm not sure why we'd want to either. + // Commenting out for the moment (so things will work), will look into why this was + // ever here, and more mysterious, how it ever worked. mf +#if 0 + hsBitVector usedMatrices; + + int i, j, k; + for( i = 0; i < fDIIndices.GetCount(); i++ ) + { + plDISpanIndex& indices = *fDIIndices[i]; + if( indices.IsMatrixOnly() ) + { + for( j = 0; j < indices.GetCount(); j++ ) + usedMatrices.SetBit(indices[j]); + } + } + + for( j = 0; j < fLocalToWorlds.GetCount(); j++ ) + { + if( !usedMatrices.IsBitSet(j) ) + { + for( i = 0; i < fDIIndices.GetCount(); i++ ) + { + plDISpanIndex& indices = *fDIIndices[i]; + if( indices.IsMatrixOnly() ) + { + for( k = 0; k < indices.GetCount(); k++ ) + { + if( indices[k] > j ) + indices[k]--; + } + } + } + for( i = j+1; i < fLocalToWorlds.GetCount(); i++ ) + { + fLocalToWorlds[i] = fLocalToWorlds[i-1]; + fWorldToLocals[i] = fWorldToLocals[i-1]; + fLocalToBones[i] = fLocalToBones[i-1]; + fBoneToLocals[i] = fBoneToLocals[i-1]; + } + } + } +#endif +} + +//// IRemoveGarbage ////////////////////////////////////////////////////////// +// Cleans out all the unused spans. Oh, joy. + +void plDrawableSpans::IRemoveGarbage( void ) +{ + int groupIdx, ibIdx, i, j, k, count, offset; + + hsTArray *> usedFlags; + hsTArray *> *> usedIdxFlags; + plGBufferGroup *group; + + + ICleanupMatrices(); + + /// Getting rid of verts + usedFlags.SetCount( fGroups.GetCount() ); + usedIdxFlags.SetCount( fGroups.GetCount() ); + for( i = 0; i < fGroups.GetCount(); i++ ) + { + hsAssert( fGroups[ i ]->GetNumVertexBuffers() == 1, "Cannot clean garbage on a non-volatile buffer group!" ); + usedFlags[ i ] = TRACKED_NEW hsTArray; + usedFlags[ i ]->SetCountAndZero( fGroups[ i ]->GetVertBufferCount( 0 ) ); + + usedIdxFlags[ i ] = TRACKED_NEW hsTArray *>; + usedIdxFlags[ i ]->SetCount( fGroups[ i ]->GetNumIndexBuffers() ); + for( j = 0; j < fGroups[ i ]->GetNumIndexBuffers(); j++ ) + { + (*usedIdxFlags[ i ])[ j ] = TRACKED_NEW hsTArray; + (*usedIdxFlags[ i ])[ j ]->SetCountAndZero( fGroups[ i ]->GetIndexBufferCount( j ) ); + } + } + + for( i = 0; i < fIcicles.GetCount(); i++ ) + { + // Set the flags so we know these verts are used + groupIdx = fIcicles[ i ].fGroupIdx; + + for( j = fIcicles[ i ].fCellOffset; j < fIcicles[ i ].fCellOffset + fIcicles[ i ].fVLength; j++ ) + (*usedFlags[ groupIdx ])[ j ] = true; + + for( j = fIcicles[ i ].fIStartIdx; j < fIcicles[ i ].fIStartIdx + fIcicles[ i ].fILength; j++ ) + (*((*usedIdxFlags[ groupIdx ])[ fIcicles[ i ].fIBufferIdx ]))[ j ] = true; + } + for( i = 0; i < fParticleSpans.GetCount(); i++ ) + { + // Set the flags so we know these verts are used + groupIdx = fParticleSpans[ i ].fGroupIdx; + + for( j = fParticleSpans[ i ].fCellOffset; j < fParticleSpans[ i ].fCellOffset + fParticleSpans[ i ].fVLength; j++ ) + (*usedFlags[ groupIdx ])[ j ] = true; + + for( j = fParticleSpans[ i ].fIStartIdx; j < fParticleSpans[ i ].fIStartIdx + fParticleSpans[ i ].fILength; j++ ) + (*((*usedIdxFlags[ groupIdx ])[ fParticleSpans[ i ].fIBufferIdx ]))[ j ] = true; + } + + /// Now loop through and delete any unused + for( groupIdx = 0; groupIdx < fGroups.GetCount(); groupIdx++ ) + { + group = fGroups[ groupIdx ]; + hsTArray &uFlags = *usedFlags[ groupIdx ]; + + // Find groups of verts to delete! + count = uFlags.GetCount(); + for( i = 0; i < count; ) + { + // Skip through used verts + for( ; i < count && uFlags[ i ] == true; i++ ); + + // Find span of unused verts + for( j = i; j < count && uFlags[ j ] == false; j++ ); + + if( j > i ) + { + // Delete this span of vertices + group->DeleteVertsFromStorage( 0, i, j - i ); + + // Adjust indices in this group + for( ibIdx = 0; ibIdx < group->GetNumIndexBuffers(); ibIdx++ ) + group->AdjustIndicesInStorage( ibIdx, i, -(Int16)( j - i ) ); + + // Adjust spans that use this vertex buffer + for( k = 0; k < fIcicles.GetCount(); k++ ) + { + if( fIcicles[ k ].fGroupIdx == groupIdx && fIcicles[ k ].fVBufferIdx == 0 && + fIcicles[ k ].fCellOffset >= i ) + fIcicles[ k ].fCellOffset -= j - i; + + // Adjust sorting data, if necessary + if( fIcicles[ k ].fSortData != nil ) + IAdjustSortData( fIcicles[ k ].fSortData, fIcicles[ k ].fILength / 3, i, -(Int16)( j - i ) ); + } + for( k = 0; k < fParticleSpans.GetCount(); k++ ) + { + if( fParticleSpans[ k ].fGroupIdx == groupIdx && fParticleSpans[ k ].fVBufferIdx == 0 && + fParticleSpans[ k ].fCellOffset >= i ) + fParticleSpans[ k ].fCellOffset -= j - i; + + // Adjust sorting data, if necessary + if( fParticleSpans[ k ].fSortData != nil ) + IAdjustSortData( fParticleSpans[ k ].fSortData, fParticleSpans[ k ].fILength / 3, i, -(Int16)( j - i ) ); + } + + // Move the flags too, lest we start deleting the wrong vertices + count -= j - i; + for( offset = j - i; i < count; i++ ) + uFlags[ i ] = uFlags[ i + offset ]; + } + + // Keep going + i = j; + } + + /// Getting rid of indices now + /// IF WE ARE SORTING, then the fIStartIdx positions of the spans are not necessarily + /// valid (since they're refreshed every frame, and not all are guaranteed to be refreshed). + /// However, since we will be refilling the buffer next frame anyway, it doesn't really + /// matter WHAT indices we delete, so long as the total length of the + /// buffer is correct. + if( fProps & kPropSortFaces ) + { + for( ibIdx = 0; ibIdx < group->GetNumIndexBuffers(); ibIdx++ ) + { + // For this index buffer, find the total length we want + i = 0; + for( j = 0; j < fIcicles.GetCount(); j++ ) + { + if( fIcicles[ j ].fGroupIdx == groupIdx && fIcicles[ j ].fIBufferIdx == ibIdx ) + i += fIcicles[ j ].fILength; + } + for( j = 0; j < fParticleSpans.GetCount(); j++ ) + { + if( fParticleSpans[ j ].fGroupIdx == groupIdx && fParticleSpans[ j ].fIBufferIdx == ibIdx ) + i += fParticleSpans[ j ].fILength; + } + + /// i is the total length + group->DeleteIndicesFromStorage( ibIdx, i, group->GetIndexBufferCount( ibIdx ) - i ); + } + } + else + { + /// Non-sorting, we have to figure out exactly which indices we aren't using + + for( ibIdx = 0; ibIdx < group->GetNumIndexBuffers(); ibIdx++ ) + { + hsTArray &uiFlags = *((*usedIdxFlags[ groupIdx ])[ ibIdx ]); + + // Find groups of indices to delete! + count = uiFlags.GetCount(); + for( i = 0; i < count; ) + { + // Skip through used verts + for( ; i < count && uiFlags[ i ] == true; i++ ); + + // Find span of unused verts + for( j = i; j < count && uiFlags[ j ] == false; j++ ); + + if( j > i ) + { + // Delete this span of indices + group->DeleteIndicesFromStorage( ibIdx, i, j - i ); + + // Adjust spans that use this index buffer (only icicles use them) + for( k = 0; k < fIcicles.GetCount(); k++ ) + { + if( fIcicles[ k ].fGroupIdx == groupIdx && fIcicles[ k ].fIBufferIdx == ibIdx && + fIcicles[ k ].fIStartIdx >= i ) + { + fIcicles[k].fIPackedIdx = (fIcicles[ k ].fIStartIdx -= j - i); + } + } + // and particle spans :) + for( k = 0; k < fParticleSpans.GetCount(); k++ ) + { + if( fParticleSpans[ k ].fGroupIdx == groupIdx && fParticleSpans[ k ].fIBufferIdx == ibIdx && + fParticleSpans[ k ].fIStartIdx >= i ) + { + fParticleSpans[k].fIPackedIdx = (fParticleSpans[ k ].fIStartIdx -= j - i); + } + } + + // Move the flags too, lest we start deleting the wrong vertices + count -= j - i; + for( offset = j - i; i < count; i++ ) + uiFlags[ i ] = uiFlags[ i + offset ]; + } + + // Keep going + i = j; + } + } + } + } + + /// Refresh all vStartIdx entries + for( i = 0; i < fSpans.GetCount(); i++ ) + { + if( fSpans[ i ]->fTypeMask & plSpan::kVertexSpan ) + { + plVertexSpan *vtx = (plVertexSpan *)fSpans[ i ]; + vtx->fVStartIdx = fGroups[ vtx->fGroupIdx ]->GetVertStartFromCell( vtx->fVBufferIdx, vtx->fCellIdx, vtx->fCellOffset ); + } + } + + /// Destroy!!! + for( i = 0; i < usedFlags.GetCount(); i++ ) + delete usedFlags[ i ]; + usedFlags.Reset(); + + for( i = 0; i < usedIdxFlags.GetCount(); i++ ) + { + for( j = 0; j < usedIdxFlags[ i ]->GetCount(); j++ ) + delete ((*usedIdxFlags[ i ])[ j ]); + delete usedIdxFlags[ i ]; + } + usedIdxFlags.Reset(); + + /// This will let us query the buffer group as to whether its ready to render or not. + /// If not, we'll force it then to reload from its storage + fReadyToRender = false; + + // Whew! + fNeedCleanup = false; +} + +//// IAdjustSortData ///////////////////////////////////////////////////////// +// Adjusts the indices in the given sort data, using the delta and threshhold +// given. + +void plDrawableSpans::IAdjustSortData( plGBufferTriangle *triList, UInt32 count, UInt32 threshhold, Int32 delta ) +{ + UInt32 i; + + + for( i = 0; i < count; i++ ) + { + if( triList[ i ].fIndex1 >= threshhold ) + triList[ i ].fIndex1 = (UInt16)( triList[ i ].fIndex1 + delta ); + if( triList[ i ].fIndex2 >= threshhold ) + triList[ i ].fIndex2 = (UInt16)( triList[ i ].fIndex2 + delta ); + if( triList[ i ].fIndex3 >= threshhold ) + triList[ i ].fIndex3 = (UInt16)( triList[ i ].fIndex3 + delta ); + } +} + +//// IMakeSpanSortable //////////////////////////////////////////////////////// +// Given an index of a span, flags it sortable and creates the sorting data +// for that span. + +void plDrawableSpans::IMakeSpanSortable( UInt32 index ) +{ + plIcicle *span = (plIcicle *)fSpans[ index ]; + plGBufferTriangle *list; + + + if( span->fProps & plSpan::kPropFacesSortable ) + return; + + /// Create data for it + list = fGroups[ span->fGroupIdx ]->ConvertToTriList( (Int16)index, span->fIBufferIdx, span->fVBufferIdx, span->fCellIdx, + span->fIStartIdx, span->fILength / 3 ); + if( list == nil ) + return; + + span->fSortData = list; + + /// Mark as sortable + span->fProps |= plSpan::kPropFacesSortable; +} + +void plDrawableSpans::UnPackCluster(plClusterGroup* cluster) +{ + const UInt32 vertsPerInst = cluster->GetTemplate()->NumVerts(); + const UInt32 idxPerInst = cluster->GetTemplate()->NumIndices(); + + const UInt32 numClust = cluster->GetNumClusters(); + + fVisSet |= cluster->GetVisSet(); + fVisNot |= cluster->GetVisNot(); + + fIcicles.SetCount(numClust); + fSpans.SetCount(numClust); + int iSpan; + for( iSpan = 0; iSpan < numClust; iSpan++ ) + fSpans[iSpan] = &fIcicles[iSpan]; + iSpan = 0; + + UInt32 vtxFormat = + cluster->GetTemplate()->NumUVWs() + | cluster->GetTemplate()->NumWeights() << 4; + if( cluster->GetTemplate()->NumWgtIdx() ) + vtxFormat |= plGBufferGroup::kSkinIndices; + + const hsTArray& lights = cluster->GetLights(); + + int iStart; + for( iStart = 0; iStart < cluster->GetNumClusters(); ) + { + int numVerts = 0; + int numIdx = 0; + int iEnd; + for( iEnd = iStart; iEnd < cluster->GetNumClusters(); iEnd++ ) + { + numVerts += vertsPerInst * cluster->GetCluster(iEnd)->NumInsts(); + numIdx += idxPerInst * cluster->GetCluster(iEnd)->NumInsts(); + + if( (numVerts > plGBufferGroup::kMaxNumVertsPerBuffer) + ||(numIdx > plGBufferGroup::kMaxNumIndicesPerBuffer) ) + { + // Oops, too much. + numVerts -= vertsPerInst * cluster->GetCluster(iEnd)->NumInsts(); + numIdx -= idxPerInst * cluster->GetCluster(iEnd)->NumInsts(); + + iEnd--; + + break; + } + } + + // Still in trouble here. We need to fake up that cell crap for each of + // our clusters to make a span for it. Whoo-hoo. + UInt8 grpIdx = IFindBufferGroup((UInt8)vtxFormat, numVerts, 0, false, false); + + UInt32 vbufferIdx; + UInt32 cellIdx; + UInt32 cellOffset; + fGroups[grpIdx]->ReserveVertStorage(numVerts, &vbufferIdx, &cellIdx, &cellOffset, plGBufferGroup::kReserveInterleaved | plGBufferGroup::kReserveIsolate); + hsAssert(!cellOffset, "This should be our own personal group"); + hsAssert(fGroups[grpIdx]->GetVertexSize() == cluster->GetTemplate()->Stride(), "Mismatch on src and dst sizes"); + + UInt32 ibufferIdx; + UInt32 istartIdx; + fGroups[grpIdx]->ReserveIndexStorage(numIdx, &ibufferIdx, &istartIdx); + UInt32 iOffset = 0; + + UInt8* vData = fGroups[grpIdx]->GetVertBufferData(vbufferIdx); + UInt16* iData = fGroups[grpIdx]->GetIndexBufferData(ibufferIdx); + UInt8* pvData = vData; + UInt16* piData = iData; + int i; + for( i = iStart; i < iEnd; i++ ) + { + hsBounds3Ext bnd; + cluster->GetCluster(i)->UnPack(pvData, piData, cellOffset, bnd); + fIcicles[iSpan].fLocalBounds = bnd; + fIcicles[iSpan].fWorldBounds = bnd; + + fIcicles[iSpan].fTypeMask = plSpan::kSpan | plSpan::kVertexSpan | plSpan::kIcicleSpan; + // STUB - need to set whether strictly runtime lit or preshaded based on cluster. +// fIcicles[iSpan].fProps = plSpan::kLiteMaterial; + fIcicles[iSpan].fProps = plSpan::kLiteVtxNonPreshaded | plSpan::kPropRunTimeLight; + if (fProps & plSpan::kPropNoDraw) + fIcicles[iSpan].fProps |= plSpan::kPropNoDraw; + fIcicles[iSpan].fMaterialIdx = 0; + fIcicles[iSpan].fLocalToWorld.Reset(); + fIcicles[iSpan].fWorldToLocal.Reset(); + fIcicles[iSpan].fBaseMatrix = 0; + fIcicles[iSpan].fNumMatrices = 0; + fIcicles[iSpan].fLocalUVWChans = 0; + fIcicles[iSpan].fMaxBoneIdx = 0; + fIcicles[iSpan].fPenBoneIdx = 0; + fIcicles[iSpan].fFogEnvironment = nil; + fIcicles[iSpan].fMaxDist = cluster->GetLOD().fMaxDist; + fIcicles[iSpan].fMinDist = cluster->GetLOD().fMinDist; + fIcicles[iSpan].fWaterHeight = 0; + fIcicles[iSpan].fVisSet = cluster->GetVisSet(); + fIcicles[iSpan].fVisNot = cluster->GetVisNot(); + + if( lights.GetCount() ) + { + int iLight; + for( iLight = 0; iLight < lights.GetCount(); iLight++ ) + fIcicles[iSpan].AddPermaLight(lights[iLight], lights[iLight]->GetProjection() != nil); + } + + fIcicles[iSpan].fGroupIdx = grpIdx; + fIcicles[iSpan].fVBufferIdx = vbufferIdx; + fIcicles[iSpan].fCellIdx = cellIdx; + fIcicles[iSpan].fCellOffset = cellOffset; + fIcicles[iSpan].fVStartIdx = cellOffset; + const UInt32 numVerts = cluster->GetCluster(i)->NumInsts() * vertsPerInst; + fIcicles[iSpan].fVLength = numVerts; + cellOffset += numVerts; + + fIcicles[iSpan].fIBufferIdx = ibufferIdx; + fIcicles[iSpan].fIPackedIdx = fIcicles[iSpan].fIStartIdx = iOffset; + const UInt32 iLength = cluster->GetCluster(i)->NumInsts() * cluster->GetTemplate()->NumIndices(); + fIcicles[iSpan].fILength = iLength; + iOffset += iLength; + + iSpan++; + + const UInt32 vSize = cluster->GetCluster(i)->NumInsts() * cluster->GetTemplate()->VertSize(); + pvData += vSize; + piData += iLength; + } + + iStart = iEnd; + } + fMaterials.SetCountAndZero(1); + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kMsgMaterial); + hsgResMgr::ResMgr()->SendRef(cluster->GetMaterial()->GetKey(), refMsg, plRefFlags::kActiveRef); + + fRenderLevel = cluster->GetRenderLevel(); + GetSpaceTree(); +} + +//// IFindBufferGroup //////////////////////////////////////////////////////// + +UInt8 plDrawableSpans::IFindBufferGroup(UInt8 vtxFormat, UInt32 numVertsNeeded, int lod, hsBool vertVolatile, hsBool idxVolatile) +{ + int i; + + + // Scan through the buffer groups, looking for a good format. If there isn't + // one, add it + if( fProps & kPropVolatile ) + vertVolatile = true; + if( fProps & kPropSortFaces ) + idxVolatile = true; + + for( i = 0; i < fGroups.GetCount(); i++ ) + { + if( (fGroups[ i ]->GetVertexFormat() == vtxFormat) + && (!vertVolatile == !fGroups[i]->AreVertsVolatile()) + && (!idxVolatile == !fGroups[i]->AreIdxVolatile()) + && (lod == fGroups[i]->GetLOD()) ) + { + if( fGroups[ i ]->GetNumPrimaryVertsLeft() >= numVertsNeeded ) + return i; + } + } + + // Add a new one of the right format + fGroups.Append( TRACKED_NEW plGBufferGroup(vtxFormat, vertVolatile, idxVolatile, lod) ); + return i; +} + +//// GetParticleSpanVector /////////////////////////////////////////////////// +// Get a bitVector of the spans that are particle spans + +hsBitVector const &plDrawableSpans::GetParticleSpanVector( void ) const +{ + return fParticleSpanVector; +} + +//// GetBlendingSpanVector /////////////////////////////////////////////////// +// Get a bitVector of the spans that are blending (i.e. numMatrices > 0) + +hsBitVector const &plDrawableSpans::GetBlendingSpanVector( void ) const +{ + /// See the plRenderMsg handler for why we do this + return fFakeBlendingSpanVector; +} + +//// SetBlendingSpanVectorBit //////////////////////////////////////////////// +// Could just make GetBlendingSpanVector() non-const, but this way it's harder +// for the end user, thus I'm hoping to discourage them from doing it too much. + +void plDrawableSpans::SetBlendingSpanVectorBit( UInt32 bitNumber, hsBool on ) +{ + fFakeBlendingSpanVector.SetBit( bitNumber, on ); +} + +//// IBuildVectors /////////////////////////////////////////////////////////// + +void plDrawableSpans::IBuildVectors( void ) +{ + int i; + bool needRenderMsg = false; + + + fParticleSpanVector.Clear(); + fBlendingSpanVector.Clear(); + for( i = 0; i < fSpans.GetCount(); i++ ) + { + if( fSpans[ i ]->fTypeMask & plSpan::kParticleSpan ) + fParticleSpanVector.SetBit( i ); + // BoneUpdate + if( ( fSpans[ i ]->fTypeMask & plSpan::kVertexSpan ) && (fSpans[ i ]->fNumMatrices > 2) ) +// if( ( fSpans[ i ]->fTypeMask & plSpan::kVertexSpan ) && (fSpans[ i ]->fNumMatrices > 1) ) + { + fBlendingSpanVector.SetBit( i ); + needRenderMsg = true; + } + } + // Reset this sucker too + fFakeBlendingSpanVector = fBlendingSpanVector; + + if( needRenderMsg && !fRegisteredForRender ) + { + // Placeholder hack - see IUpdateMatrixPaletteBoundsHack() for comments + fRegisteredForRender = true; + plgDispatch::Dispatch()->RegisterForExactType( plRenderMsg::Index(), GetKey() ); + } + else if( !needRenderMsg && fRegisteredForRender ) + { + plgDispatch::Dispatch()->UnRegisterForExactType( plRenderMsg::Index(), GetKey() ); + fRegisteredForRender = false; + } +} + + +////////////////////////////////////////////////////////////////////////////// +//// Particle Sets /////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// CreateParticleSystem //////////////////////////////////////////////////// + +UInt32 plDrawableSpans::CreateParticleSystem( UInt32 maxNumSpans, UInt32 maxNumParticles, hsGMaterial *material ) +{ + UInt32 i, numVerts, numIndices; + plParticleSet *set; + plDISpanIndex *spanLookup; + + + // Make a shiny new set + set = TRACKED_NEW plParticleSet; + set->fRefCount = 0; + + /// Fill out info + numVerts = maxNumParticles * 4; // 4 verts per particle + numIndices = maxNumParticles * 6; // 6 indices per particle + + if( material != nil && material->GetLayer( 0 ) != nil && material->GetLayer( 0 )->GetTexture() != nil ) + set->fFormat = plGeometrySpan::UVCountToFormat( 1 ); + else + set->fFormat = plGeometrySpan::UVCountToFormat( 0 ); + + // Reserve space + set->fGroupIdx = IFindBufferGroup( set->fFormat, numVerts, 0, true, false ); // Force them to volatile even if the drawable isn't + fGroups[ set->fGroupIdx ]->ReserveVertStorage( numVerts, &set->fVBufferIdx, &set->fCellIdx, &set->fCellOffset, plGBufferGroup::kReserveInterleaved | plGBufferGroup::kReserveIsolate ); + fGroups[ set->fGroupIdx ]->ReserveIndexStorage( numIndices, &set->fIBufferIdx, &set->fIStartIdx ); + + set->fVStartIdx = fGroups[ set->fGroupIdx ]->GetVertStartFromCell( set->fVBufferIdx, set->fCellIdx, set->fCellOffset ); + set->fVLength = numVerts; + set->fILength = numIndices; + + UInt32 vIdx = set->fVStartIdx; + UInt16* idx = fGroups[set->fGroupIdx]->GetIndexBufferData(set->fIBufferIdx) + set->fIStartIdx; + for( i = 0; i < maxNumParticles; i++ ) + { + *idx++ = (UInt16)vIdx; + *idx++ = (UInt16)(vIdx + 1); + *idx++ = (UInt16)(vIdx + 2); + + *idx++ = (UInt16)vIdx; + *idx++ = (UInt16)(vIdx + 2); + *idx++ = (UInt16)(vIdx + 3); + + vIdx += 4; + } + + // Create us some spans + set->fDIEntry = -1; + spanLookup = IFindDIIndices( set->fDIEntry ); + spanLookup->fFlags |= plDISpanIndex::kDontTransformSpans; + + set->fNumSpans = maxNumSpans; + for( i = 0; i < maxNumSpans; i++ ) + ICreateParticleIcicle( material, set ); + + set->fNextIStartIdx = set->fIStartIdx; + set->fNextVStartIdx = set->fVStartIdx; + set->fNextCellOffset = set->fCellOffset; + + + /// Rebuild the pointer array + IRebuildSpanArray(); + + fReadyToRender = false; + + return set->fDIEntry; +} + +//// IAssignMatIdxToSpan ///////////////////////////////////////////////////// + +void plDrawableSpans::IAssignMatIdxToSpan( plSpan *span, hsGMaterial *mtl ) +{ + fSettingMatIdxLock = true; + + if( mtl != nil ) + span->fMaterialIdx = IAddAMaterial( mtl ); + + if( fMaterials[ span->fMaterialIdx ] != nil ) + { + if( ITestMatForSpecularity( fMaterials[ span->fMaterialIdx ] ) ) + span->fProps |= plSpan::kPropMatHasSpecular; + else + span->fProps &= ~plSpan::kPropMatHasSpecular; + } + + fSettingMatIdxLock = false; +} + +//// ICreateParticleIcicle /////////////////////////////////////////////////// +// Creates a shiny new plIcicle for use with particle sets. + +plParticleSpan *plDrawableSpans::ICreateParticleIcicle( hsGMaterial *material, plParticleSet *set ) +{ + UInt32 spanIdx; + hsMatrix44 ident; + plDISpanIndex *spanLookup; + + + /// Ahh, an icicle span + spanIdx = fParticleSpans.GetCount(); + fParticleSpans.Append( plParticleSpan() ); + plParticleSpan *icicle = &fParticleSpans[ spanIdx ]; + + ident.Reset(); + IAssignMatIdxToSpan( icicle, material ); + icicle->fMaterialIdx = IAddAMaterial( material ); + icicle->fLocalToWorld = ident; + icicle->fWorldToLocal = ident; + icicle->fProps |= plSpan::kPropRunTimeLight | plSpan::kPropNoDraw | plSpan::kLiteVtxPreshaded; + if( fProps & kPropSortSpans ) + icicle->fProps |= plSpan::kPropFacesSortable; + + icicle->fLocalBounds.MakeEmpty(); + icicle->fWorldBounds.MakeEmpty(); + + icicle->fLocalBounds.Union( &hsPoint3(0,0,0) ); + icicle->fWorldBounds.Union( &hsPoint3(0,0,0) ); + + icicle->fGroupIdx = set->fGroupIdx; + icicle->fVBufferIdx = set->fVBufferIdx; + icicle->fCellIdx = set->fCellIdx; + icicle->fCellOffset = set->fCellOffset; + icicle->fVStartIdx = set->fVStartIdx; + icicle->fVLength = 0; + + icicle->fIBufferIdx = set->fIBufferIdx; + icicle->fIPackedIdx = icicle->fIStartIdx = set->fIStartIdx; + icicle->fILength = 0; + icicle->fFogEnvironment = nil; + icicle->fSortData = nil; + + icicle->fSrcSpanIdx = fSpans.GetCount(); + + spanLookup = IFindDIIndices( set->fDIEntry ); + spanLookup->Append( fSpans.GetCount() ); + fSpans.Append( (plSpan *)icicle ); + fSpanSourceIndices.Append( spanIdx | kSpanTypeParticleSpan ); + + /// Set our pointer to the set and inc it's refCount + icicle->fParentSet = set; + set->fRefCount++; + + /// Flag that we need garbage cleanup now + fNeedCleanup = true; + fReadyToRender = false; + + // Cause us to rebuild the space tree when needed + SetSpaceTree( nil ); + + return icicle; +} + +//// ResetParticleSystem ///////////////////////////////////////////////////// + +void plDrawableSpans::ResetParticleSystem( UInt32 setIndex ) +{ + UInt32 i; + plDISpanIndex *indices = IFindDIIndices( setIndex ); + plParticleSet *set; + plParticleSpan *span; + + for( i = 0; i < indices->GetCount(); i++ ) + { + span = (plParticleSpan *)fSpans[ (*indices)[ i ] ]; + span->fVStartIdx = 0; + span->fCellIdx = 0; + span->fCellOffset = 0; + span->fVLength = 0; + span->fIPackedIdx = span->fIStartIdx = 0; + span->fILength = 0; + span->fSource = nil; + span->fProps |= plSpan::kPropNoDraw; + GetSpaceTree()->SetLeafFlag( (Int16)(span->fSrcSpanIdx), plSpaceTreeNode::kDisabled ); + + set = span->fParentSet; // To use at the end of the loop + } + + set->fNextIStartIdx = set->fIStartIdx; + set->fNextVStartIdx = set->fVStartIdx; + set->fNextCellOffset = set->fCellOffset; + + fGroups[set->fGroupIdx]->SetVertBufferEnd(set->fVBufferIdx, set->fNextVStartIdx); + if( fProps & kPropSortFaces ) + fGroups[set->fGroupIdx]->SetIndexBufferEnd(set->fIBufferIdx, set->fNextIStartIdx); +} + + +//// AssignEmitterToParticleSystem /////////////////////////////////////////// + +void plDrawableSpans::AssignEmitterToParticleSystem( UInt32 setIndex, plParticleEmitter *emitter ) +{ + plDISpanIndex *indices = IFindDIIndices( setIndex ); + plParticleSet *set; + plParticleSpan *icicle; + UInt32 i, index, numParticles = emitter->GetParticleCount(); + plParticleCore *array = emitter->GetParticleArray(); + hsMatrix44 ident; + + plGBufferTriangle *sortArray; + + + icicle = (plParticleSpan *)fSpans[ (*indices)[ emitter->GetSpanIndex() ] ]; + set = icicle->fParentSet; + + icicle->fCellIdx = set->fCellIdx; + icicle->fCellOffset = set->fNextCellOffset; + icicle->fVStartIdx = set->fNextVStartIdx; + icicle->fIPackedIdx = icicle->fIStartIdx = set->fNextIStartIdx; + icicle->fVLength = numParticles * 4; + icicle->fILength = numParticles * 6; + icicle->fSource = emitter; + icicle->fProps &= ~plSpan::kPropNoDraw; + GetSpaceTree()->ClearLeafFlag( (Int16)(icicle->fSrcSpanIdx), plSpaceTreeNode::kDisabled ); + icicle->fNumParticles = numParticles; + set->fNextVStartIdx += numParticles * 4; + set->fNextIStartIdx += numParticles * 6; + set->fNextCellOffset += numParticles * 4; + + fGroups[set->fGroupIdx]->SetVertBufferEnd(set->fVBufferIdx, set->fNextVStartIdx); + if( fProps & kPropSortFaces ) + fGroups[set->fGroupIdx]->SetIndexBufferEnd(set->fIBufferIdx, set->fNextIStartIdx); + + hsAssert( set->fNextVStartIdx <= set->fVStartIdx + set->fVLength, "Buffer overflow in AddParticlesToSet()" ); + hsAssert( set->fNextIStartIdx <= set->fIStartIdx + set->fILength, "Buffer overflow in AddParticlesToSet()" ); + + if( fProps & kPropSortFaces ) + { + // Prep sorting data + if( icicle->fSortData == nil || icicle->fSortCount < ( numParticles << 1 ) ) + { + delete [] icicle->fSortData; + icicle->fSortData = sortArray = TRACKED_NEW plGBufferTriangle[ numParticles << 1 ]; + icicle->fSortCount = numParticles << 1; + } + else + sortArray = icicle->fSortData; + + // Find our bounds and fill out sorting data + for( i = 0, index = icicle->fVStartIdx; i < numParticles; i++ ) + { + sortArray->fCenter = array[ i ].fPos; + sortArray->fIndex1 = (UInt16)index; + sortArray->fIndex2 = (UInt16)(index + 1); + sortArray->fIndex3 = (UInt16)(index + 2); + sortArray->fSpanIndex = (UInt16)(icicle->fSrcSpanIdx); + sortArray++; + + sortArray->fCenter = array[ i ].fPos; + sortArray->fIndex1 = (UInt16)index; + sortArray->fIndex2 = (UInt16)(index + 2); + sortArray->fIndex3 = (UInt16)(index + 3); + sortArray->fSpanIndex = (UInt16)(icicle->fSrcSpanIdx); + sortArray++; + + index += 4; + } + } + + icicle->fLocalBounds = emitter->GetBoundingBox(); + icicle->fWorldBounds = icicle->fLocalBounds; + + GetSpaceTree()->MoveLeaf( (Int16)(icicle->fSrcSpanIdx), icicle->fWorldBounds ); + // Done for now, we'll fill on the pipeline side + + return; +} + +////////////////////////////////////////////////////////////////////////////// +//// Dynamic Functions for SceneViewer /////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// RefreshDISpans ////////////////////////////////////////////////////////// +// Given a set of plGeometrySpans, replaces the data in the given DI span set +// with the vertices in the plGeometrySpans. Does NOT replace the indices, +// and the count must be EXACT, or this function gets pyst. +//MFREPACK +// don't need to pass in spans, they're already in fSourceSpans. +// clearSpansAfterRefresh is ignored anyway, nuke it. +UInt32 plDrawableSpans::RefreshDISpans( UInt32 index ) +{ + int i; + UInt32 spanIdx; + plSpan *span; + hsBounds3Ext bounds; + plDISpanIndex *spanLookup; + +// hsAssert( fProps & kPropVolatile, "Trying to add spans on a non-volatile drawable" ); + hsAssert( index != (UInt32)-1, "Invalid DI span index" ); + + /// Do garbage cleanup first + if( fNeedCleanup ) + IRemoveGarbage(); + + spanLookup = IFindDIIndices( index ); + + /// Loop through the spans and copy the vertex data over + for( i = 0; i < spanLookup->GetCount(); i++ ) + { + // Main info + plGeometrySpan *geoSpan = fSourceSpans[ (*spanLookup)[i] ]; + spanIdx = fSpanSourceIndices[ (*spanLookup)[ i ] ]; + hsAssert( ( spanIdx & kSpanTypeMask ) == kSpanTypeIcicle, "Mismatch in span formats" ); + plIcicle *icicle = &fIcicles[ spanIdx & kSpanIDMask ]; + IUpdateIcicleFromGeoSpan( geoSpan, icicle ); + span = (plSpan *)icicle; + if( fProps & kPropSortFaces ) + { + // Should add sort data too... + IMakeSpanSortable( (*spanLookup)[ i ] ); + } + + } + + // Update fLocalBounds, since we were updating the world bounds + fLocalBounds = fWorldBounds; + fLocalBounds.Transform( &fWorldToLocal ); + fMaxWorldBounds = fWorldBounds; + + // Cause us to rebuild the space tree when needed + SetSpaceTree( nil ); + + fReadyToRender = false; + + return index; +} + +// Same as above, but takes an actual span index (not a DI Index). +UInt32 plDrawableSpans::RefreshSpan( UInt32 index ) +{ + UInt32 spanIdx; + plSpan *span; + hsBounds3Ext bounds; + +// hsAssert( fProps & kPropVolatile, "Trying to add spans on a non-volatile drawable" ); + hsAssert( index < fSourceSpans.GetCount(), "Invalid span index" ); + + /// Do garbage cleanup first + if( fNeedCleanup ) + IRemoveGarbage(); + + // Main info + plGeometrySpan *geoSpan = fSourceSpans[ index ]; + spanIdx = fSpanSourceIndices[ index ]; + hsAssert( ( spanIdx & kSpanTypeMask ) == kSpanTypeIcicle, "Mismatch in span formats" ); + plIcicle *icicle = &fIcicles[ spanIdx & kSpanIDMask ]; + IUpdateIcicleFromGeoSpan( geoSpan, icicle ); + span = (plSpan *)icicle; + if( fProps & kPropSortFaces ) + { + // Should add sort data too... + IMakeSpanSortable( index ); + } + + // Update fLocalBounds, since we were updating the world bounds + fLocalBounds = fWorldBounds; + fLocalBounds.Transform( &fWorldToLocal ); + fMaxWorldBounds = fWorldBounds; + + // Cause us to rebuild the space tree when needed + SetSpaceTree( nil ); + + fReadyToRender = false; + + return index; +} + +//// IUpdateVertexSpanFromGeoSpan //////////////////////////////////////////// +// Common function for the two functions that follow. + +void plDrawableSpans::IUpdateVertexSpanFromGeoSpan( plGeometrySpan *geoSpan, plVertexSpan *span ) +{ + hsBounds3Ext bounds; + + // This function looks pretty dubious to me. It or's in properties instead of setting them, and + // it doesn't set all the available properties (like the skinning matrix indices). It clearly should + // be doing more or doing less, but it's unclear which. I'm guessing the latter, but until there's + // a reason to dig through this trash, here it stays. mf + + hsAssert( geoSpan->fNumVerts == span->fVLength, "Vertex count mismatch in IUpdateVertexSpanFromGeoSpan()" ); + + span->fLocalToWorld = geoSpan->fLocalToWorld; + span->fWorldToLocal = geoSpan->fWorldToLocal; + span->fProps |= ( geoSpan->fProps & plGeometrySpan::kPropRunTimeLight ) ? plSpan::kPropRunTimeLight : 0; + if( geoSpan->fProps & plGeometrySpan::kPropNoShadowCast ) + span->fProps |= plSpan::kPropNoShadowCast; + if( geoSpan->fProps & plGeometrySpan::kPropNoShadow ) + span->fProps |= plSpan::kPropNoShadow; + if( geoSpan->fProps & plGeometrySpan::kPropForceShadow ) + span->fProps |= plSpan::kPropForceShadow; + if( geoSpan->fProps & plGeometrySpan::kPropReverseSort ) + span->fProps |= plSpan::kPropReverseSort; + if( geoSpan->fProps & plGeometrySpan::kPartialSort ) + span->fProps |= plSpan::kPartialSort; + switch( geoSpan->fProps & plGeometrySpan::kLiteMask ) + { + case plGeometrySpan::kLiteMaterial: span->fProps |= plSpan::kLiteMaterial; break; + case plGeometrySpan::kLiteVtxPreshaded: span->fProps |= plSpan::kLiteVtxPreshaded; break; + case plGeometrySpan::kLiteVtxNonPreshaded: span->fProps |= plSpan::kLiteVtxNonPreshaded; break; + } + if( geoSpan->fProps & plGeometrySpan::kWaterHeight ) + { + span->fProps |= plSpan::kWaterHeight; + span->fWaterHeight = geoSpan->fWaterHeight; + } + if( geoSpan->fProps & plGeometrySpan::kVisLOS ) + { + span->fProps |= plSpan::kVisLOS; + fProps |= plDrawable::kPropHasVisLOS; + } + + bounds = geoSpan->fLocalBounds; + span->fLocalBounds = bounds; + bounds.Transform( &span->fLocalToWorld ); + span->fWorldBounds = bounds; + fWorldBounds.Union( &bounds ); + + // Pack the vertices in + fGroups[ span->fGroupIdx ]->StuffToVertStorage( geoSpan, span->fVBufferIdx, span->fCellIdx, span->fCellOffset, + plGBufferGroup::kReserveInterleaved ); + + span->fFogEnvironment = geoSpan->fFogEnviron; + + /// Add the material to our list if necessary + IAssignMatIdxToSpan( span, geoSpan->fMaterial ); +} + +//// IUpdateIcicleFromGeoSpan //////////////////////////////////////////////// + +void plDrawableSpans::IUpdateIcicleFromGeoSpan( plGeometrySpan *geoSpan, plIcicle *icicle ) +{ + IUpdateVertexSpanFromGeoSpan( geoSpan, icicle ); +} + +hsPoint3& plDrawableSpans::GetPosition(int spanIdx, int vtxIdx) +{ + hsAssert(fSpans[spanIdx]->fTypeMask & plSpan::kVertexSpan, "Getting vertex info on non-vertex based span"); + plVertexSpan* span = (plVertexSpan*)fSpans[spanIdx]; + + return fGroups[span->fGroupIdx]->Position(span->fVBufferIdx, span->fCellIdx, vtxIdx); +} +hsVector3& plDrawableSpans::GetNormal(int spanIdx, int vtxIdx) +{ + hsAssert(fSpans[spanIdx]->fTypeMask & plSpan::kVertexSpan, "Getting vertex info on non-vertex based span"); + plVertexSpan* span = (plVertexSpan*)fSpans[spanIdx]; + + return fGroups[span->fGroupIdx]->Normal(span->fVBufferIdx, span->fCellIdx, vtxIdx); +} + +UInt32 plDrawableSpans::GetNumTris(int spanIdx) +{ + hsAssert(fSpans[spanIdx]->fTypeMask & plSpan::kIcicleSpan, "Asking for index info on non-triangle based span"); + plIcicle* span = (plIcicle*)fSpans[spanIdx]; + return fGroups[span->fGroupIdx]->GetIndexBufferCount(span->fIBufferIdx) / 3; +} + +UInt16* plDrawableSpans::GetIndexList(int spanIdx) +{ + hsAssert(fSpans[spanIdx]->fTypeMask & plSpan::kIcicleSpan, "Asking for index info on non-triangle based span"); + plIcicle* span = (plIcicle*)fSpans[spanIdx]; + return fGroups[span->fGroupIdx]->GetIndexBufferData(span->fIBufferIdx); +} + +hsPoint3& plDrawableSpans::CvtGetPosition(int spanIdx, int vtxIdx) +{ + return *(hsPoint3*)(fSourceSpans[spanIdx]->fVertexData + vtxIdx * plGeometrySpan::GetVertexSize(fSourceSpans[spanIdx]->fFormat)); +} + +hsVector3& plDrawableSpans::CvtGetNormal(int spanIdx, int vtxIdx) +{ + // Normal follows the position (+ 12bytes) + return *(hsVector3*)(fSourceSpans[spanIdx]->fVertexData + vtxIdx * plGeometrySpan::GetVertexSize(fSourceSpans[spanIdx]->fFormat) + 12); +} + +UInt32 plDrawableSpans::CvtGetNumTris(int spanIdx) +{ + return fSourceSpans[spanIdx]->fNumIndices / 3; +} + +UInt16* plDrawableSpans::CvtGetIndexList(int spanIdx) +{ + return fSourceSpans[spanIdx]->fIndexData; +} + +UInt32 plDrawableSpans::CvtGetNumVerts(int spanIdx) const +{ + return fSourceSpans[spanIdx]->fNumVerts; +} + +plGeometrySpan* plDrawableSpans::GetGeometrySpan(int spanIdx) +{ + return fSourceSpans[spanIdx]; +} + +void plDrawableSpans::GetOrigGeometrySpans( UInt32 diIndex, hsTArray &arrayToFill ) +{ + if( diIndex >= fDIIndices.GetCount() ) + { + hsAssert( false, "Invalid diIndex to GetOrigGeometrySpans()" ); + return; + } + + plDISpanIndex *indices = fDIIndices[ diIndex ]; + UInt32 i; + + + arrayToFill.Reset(); + for( i = 0; i < indices->GetCount(); i++ ) + arrayToFill.Append( fSourceSpans[ (*indices)[ i ] ] ); + + // HA! + return; +} + +void plDrawableSpans::ClearAndSetMaterialCount(UInt32 count) +{ + // Release the old materials + for (int i = 0; i < fMaterials.GetCount(); i++) + { + if ( fMaterials[ i ] != nil && fMaterials[ i ]->GetKey() != nil ) + GetKey()->Release( fMaterials[ i ]->GetKey() ); + } + + fMaterials.SetCountAndZero(count); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpans.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpans.h new file mode 100644 index 00000000..66994aa6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpans.h @@ -0,0 +1,374 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDrawableSpans Header // +// // +// The base plDrawable derivative for ice (triangle list) // +// drawables. Contains the basic structure for spans and the functions // +// to deal with them. // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// 4.3.2001 mcn - Created. // +// 5.3.2001 mcn - Completely revamped. Now plDrawableSpans *IS* the group // +// of spans, and each span is either an icicle or patch. // +// This eliminates the need entirely for separate drawables,// +// at the cost of having to do a bit of extra work to // +// maintain different types of spans in the same drawable. // +// 4.20.2006 bob - Patches are long gone. Only icicle (or particle) spans // +// now. +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDrawableSpans_h +#define _plDrawableSpans_h + + +#include "hsBitVector.h" +#include "hsTemplates.h" +#include "plDrawable.h" +#include "hsBounds.h" +#include "hsMatrix44.h" +#include "plSpanTypes.h" + +class plPipeline; +class plMessage; +class hsGMaterial; +class plGeometrySpan; +class plSpaceTree; +class plFogEnvironment; +class plLightInfo; +class plGBufferGroup; +class plParticleCore; +class plAccessSpan; +class plAccessVtxSpan; +class plAccessTriSpan; +class plVisMgr; +class plVisRegion; +class plClusterGroup; + +//// Class Definition //////////////////////////////////////////////////////// + +class plDISpanIndex +{ +public: + enum { + kNone = 0x0, + kMatrixOnly = 0x1, + kDontTransformSpans = 0x2 // Only used for particle systems right now + }; + UInt8 fFlags; + hsTArray fIndices; + + hsBool IsMatrixOnly() const { return 0 != (fFlags & kMatrixOnly); } + hsBool DontTransform() const { return 0 != ( fFlags & kDontTransformSpans ); } + void Append(UInt32 i) { fIndices.Append(i); } + void Reset() { fFlags = kNone; fIndices.Reset(); } + void SetCountAndZero(int c) { fIndices.SetCountAndZero(c); } + UInt32 GetCount() const { return fIndices.GetCount(); } + UInt32& operator[](int i) const { return fIndices[i]; } +}; + +struct hsColorRGBA; + +class plDrawableSpans : public plDrawable +{ + protected: + + static const UInt32 kSpanTypeMask; + static const UInt32 kSpanIDMask; + static const UInt32 kSpanTypeIcicle; + static const UInt32 kSpanTypeParticleSpan; + + UInt32 fType; + + hsBool fReadyToRender; + + hsBounds3Ext fLocalBounds; + hsBounds3Ext fWorldBounds; + hsBounds3Ext fMaxWorldBounds; + + hsMatrix44 fLocalToWorld; + hsMatrix44 fWorldToLocal; + + hsTArray fLocalToWorlds; + hsTArray fWorldToLocals; + + hsTArray fLocalToBones; + hsTArray fBoneToLocals; + + hsTArray fMaterials; + + mutable plSpaceTree* fSpaceTree; + + hsBitVector fVisSet; // the or of all our spans visset's. Doesn't have to be exact, just conservative. + hsBitVector fVisNot; // same, but for visregions that exclude us. + mutable hsBitVector fLastVisSet; // Last vis set we were evaluated against. + mutable hsBitVector fLastVisNot; // Last exclusion set we were evaluated agains. + hsBitVector fVisCache; // the enabled section of the space tree + + hsTArray fIcicles; + hsTArray fParticleSpans; + + hsTArray fSpans; // Pointers into the above two arrays + hsTArray fSpanSourceIndices; // For volatile drawables only + hsTArray fGroups; + hsTArray fDIIndices; + + UInt32 fProps; + UInt32 fCriteria; + plRenderLevel fRenderLevel; + plLoadMask fLoadMask; + + hsBool fRegisteredForRecreate, fNeedCleanup; + hsBool fRegisteredForRender; + + hsBitVector fParticleSpanVector; + hsBitVector fBlendingSpanVector; + hsBitVector fFakeBlendingSpanVector; + + plKey fSceneNode; + + hsBool fSettingMatIdxLock; + + UInt32 fSkinTime; + + /// Export-only members + hsTArray fSourceSpans; + hsBool fOptimized; + + virtual void IQuickSpaceTree( void ) const; + + // Temp placeholder function. See code for comments. + void IUpdateMatrixPaletteBoundsHack( ); + + void ICleanupMatrices(); + void IRemoveGarbage( void ); + void IAdjustSortData( plGBufferTriangle *triList, UInt32 count, UInt32 threshhold, Int32 delta ); + + // The following two functions return true if they create a new span, false if it's just an instance + hsBool IConvertGeoSpanToVertexSpan( plGeometrySpan *geoSpan, plVertexSpan *span, int lod, plVertexSpan *instancedParent ); + hsBool IConvertGeoSpanToIcicle( plGeometrySpan *geoSpan, plIcicle *icicle, int lod, plIcicle *instancedParent = nil ); + + void IUpdateIcicleFromGeoSpan( plGeometrySpan *geoSpan, plIcicle *icicle ); + void IUpdateVertexSpanFromGeoSpan( plGeometrySpan *geoSpan, plVertexSpan *span ); + + UInt32 IXlateSpanProps( UInt32 props, hsBool xlateToSpan ); + + UInt32 IAddAMaterial( hsGMaterial *material ); + UInt32 IRefMaterial( UInt32 index ); + void ICheckToRemoveMaterial( UInt32 materialIdx ); + + // Annoying to need this, but necessary until materials can test for properties on any of their layers (might add in the future) + hsBool ITestMatForSpecularity( hsGMaterial *mat ); + + void IAssignMatIdxToSpan( plSpan *span, hsGMaterial *mtl ); + + // Create the sorting data for a given span and flag it as sortable + void ICheckSpanForSortable( UInt32 idx ) { if( !(fSpans[idx]->fProps & plSpan::kPropFacesSortable) )IMakeSpanSortable(idx); } + void IMakeSpanSortable( UInt32 index ); + + /// Bit vector build thingies + virtual void IBuildVectors( void ); + + hsBool IBoundsInvalid(const hsBounds3Ext& bnd) const; + + /// EXPORT-ONLY FUNCTIONS + // Packs the span indices + void IPackSourceSpans( void ); + // Sort the spans + void ISortSourceSpans( void ); + // Compare two spans for sorting + short ICompareSpans( plGeometrySpan *span1, plGeometrySpan *span2 ); + // Find a buffer group of the given format (returns its index into fGroups) + UInt8 IFindBufferGroup( UInt8 vtxFormat, UInt32 numVertsNeeded, int lod, hsBool vertVolatile, hsBool idxVolatile); + // Write a span to a stream + void IWriteSpan( hsStream *s, plSpan *span ); + /// EXPORT-ONLY FUNCTIONS + + /// DYNAMIC FUNCTIONS + plDISpanIndex *IFindDIIndices( UInt32 &index ); + void IRebuildSpanArray( void ); + plParticleSpan *ICreateParticleIcicle( hsGMaterial *material, plParticleSet *set ); + + virtual void SetKey(plKey k); + + public: + plDrawableSpans(); + virtual ~plDrawableSpans(); + + CLASSNAME_REGISTER( plDrawableSpans ); + GETINTERFACE_ANY( plDrawableSpans, plDrawable ); + + virtual void SetNativeTransform(UInt32 idx, const hsMatrix44& l2w, const hsMatrix44& w2l); + virtual plDrawable& SetTransform( UInt32 index, const hsMatrix44& l2w, const hsMatrix44& w2l); + virtual const hsMatrix44& GetLocalToWorld( UInt32 index = (UInt32)-1 ) const; + virtual const hsMatrix44& GetWorldToLocal( UInt32 index = (UInt32)-1 ) const; + + virtual plDrawable& SetProperty( UInt32 index, int prop, hsBool on ); + virtual hsBool GetProperty( UInt32 index, int prop ) const; + + virtual plDrawable& SetProperty( int prop, hsBool on ); + virtual hsBool GetProperty( int prop ) const; + + virtual plDrawable& SetNativeProperty( int prop, hsBool on ) { if( on ) fProps |= prop; else fProps &= ~prop; return *this; } + virtual hsBool GetNativeProperty( int prop ) const { return ( fProps & prop ) ? true : false; } + + virtual plDrawable& SetNativeProperty( UInt32 index, int prop, hsBool on ); + virtual hsBool GetNativeProperty( UInt32 index, int prop ) const; + + virtual plDrawable& SetSubType( UInt32 index, plSubDrawableType t, hsBool on ); + virtual UInt32 GetSubType( UInt32 index ) const; // returns or of all spans with this index (index==-1 is all spans). + + virtual UInt32 GetType( void ) const { return fType; } + virtual void SetType( UInt32 type ) { fType = type; } + + virtual void SetRenderLevel(const plRenderLevel& l); + virtual const plRenderLevel& GetRenderLevel() const; + + const hsBounds3Ext& GetLocalBounds( UInt32 index = (UInt32)-1 ) const; + const hsBounds3Ext& GetWorldBounds( UInt32 index = (UInt32)-1 ) const; + const hsBounds3Ext& GetMaxWorldBounds( UInt32 index = (UInt32)-1 ) const; + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual plSpaceTree* GetSpaceTree() const { if(!fSpaceTree)IQuickSpaceTree(); return fSpaceTree; } + virtual void SetSpaceTree(plSpaceTree* st) const; + virtual void SetVisSet(plVisMgr* visMgr); + virtual void SetDISpanVisSet(UInt32 diIndex, hsKeyedObject* reg, hsBool on); + + virtual const plSpan *GetSpan( UInt32 index ) const { return fSpans[ index ]; } + virtual const plSpan *GetSpan( UInt32 diIndex, UInt32 index ) const { return fSpans[ (*fDIIndices[ diIndex ])[ index ] ]; } + virtual const UInt32 GetNumSpans( void ) const { return fSpans.GetCount(); } + virtual const hsTArray &GetSpanArray( void ) const { return fSpans; } + + hsMatrix44* GetMatrixPalette(int baseMatrix) const { return &fLocalToWorlds[baseMatrix]; } + const hsMatrix44& GetPaletteMatrix(int i) const { return fLocalToWorlds[i]; } + void SetInitialBone(int i, const hsMatrix44& l2b, const hsMatrix44& b2l); + + // Get the vertex buffer ref of a given group + hsGDeviceRef *GetVertexRef( UInt32 group, UInt32 idx ); + // Get the index buffer ref of a given group + hsGDeviceRef *GetIndexRef( UInt32 group, UInt32 idx ); + + // BufferGroups accessed only by Pipeline and it's close personal acquaintances. + plGBufferGroup* GetBufferGroup(UInt32 i) const { return fGroups[i]; } + UInt32 GetNumBufferGroups() const { return fGroups.GetCount(); } + const hsTArray& GetSourceSpans() const { return fSourceSpans; } + + void DirtyVertexBuffer(UInt32 group, UInt32 idx); + void DirtyIndexBuffer(UInt32 group, UInt32 idx); + + // Prepare all internal data structures for rendering + virtual void PrepForRender( plPipeline *p ); + void SetNotReadyToRender() { fReadyToRender = false; } + + virtual hsBool MsgReceive( plMessage* msg ); + + // These two should only be called by the SceneNode + virtual plKey GetSceneNode() const { return fSceneNode; } + virtual void SetSceneNode(plKey newNode); + + // Lookup a material in the material array + hsGMaterial *GetMaterial( UInt32 index ) const { return ( ( index == (UInt32)-1 ) ? nil : fMaterials[ index ] ); } + UInt32 GetNumMaterials( void ) const { return fMaterials.GetCount(); } + + // Convert intermediate data into export/run-time-ready data + virtual void Optimize( void ); + // Called by the sceneNode to determine if we match the criteria + virtual hsBool DoIMatch( const plDrawableCriteria& crit ); + // To set the criteria that this ice fits + void SetCriteria( const plDrawableCriteria& crit ); + + // Get a bitVector of the spans that are particle spans + virtual hsBitVector const &GetParticleSpanVector( void ) const; + + // Get a bitVector of the spans that are blending (i.e. numMatrices > 0) + virtual hsBitVector const &GetBlendingSpanVector( void ) const; + + // Set a single bit in the bitVector of spans that are blending + virtual void SetBlendingSpanVectorBit( UInt32 bitNumber, hsBool on ); + + // Taking span index. DI Index doesn't make sense here, because one object's DI can dereference into many materials etc. + virtual hsGMaterial* GetSubMaterial(int index) const; + virtual hsBool GetSubVisDists(int index, hsScalar& minDist, hsScalar& maxDist) const; // return true if span invisible before minDist and/or after maxDist + + // Used by the pipeline to keep from reskinning on multiple renders per frame. + UInt32 GetSkinTime() const { return fSkinTime; } + void SetSkinTime(UInt32 t) { fSkinTime = t; } + + void UnPackCluster(plClusterGroup* cluster); + + /// EXPORT-ONLY FUNCTIONS + virtual UInt32 AddDISpans( hsTArray &spans, UInt32 index = (UInt32)-1); + virtual plDISpanIndex& GetDISpans( UInt32 index ) const; + + // Data Access functions + // Runtime + hsPoint3& GetPosition(int spanIdx, int vtxIdx); + hsVector3& GetNormal(int spanIdx, int vtxIdx); + + UInt32 GetNumTris(int spanIdx); + UInt16* GetIndexList(int spanIdx); + + // Conversion (before geometryspans get tossed (at write)). + UInt32 CvtGetNumVerts(int spanIdx) const; + hsPoint3& CvtGetPosition(int spanIdx, int vtxIdx); + hsVector3& CvtGetNormal(int spanIdx, int vtxIdx); + + UInt32 CvtGetNumTris(int spanIdx); + UInt16* CvtGetIndexList(int spanIdx); + + plGeometrySpan* GetGeometrySpan(int spanIdx); + + /// DYNAMIC FUNCTIONS + virtual void RemoveDISpans( UInt32 index ); + virtual UInt32 AppendDISpans( hsTArray &spans, UInt32 index = (UInt32)-1, hsBool clearSpansAfterAdd = true, hsBool doNotAddToSource = false, hsBool addToFront = false, int lod = 0 ); + virtual UInt32 RefreshDISpans( UInt32 diIndex ); + virtual UInt32 RefreshSpan( UInt32 srcSpanIndex ); + virtual void RemoveDIMatrixSpans(UInt32 index); + virtual UInt32 AppendDIMatrixSpans(int n); + virtual UInt32 FindBoneBaseMatrix(const hsTArray& initL2B, hsBool searchAll) const; + virtual UInt32 NewDIMatrixIndex(); + void SortSpan( UInt32 index, plPipeline *pipe ); + void SortVisibleSpans(const hsTArray& visList, plPipeline* pipe); + void SortVisibleSpansPartial(const hsTArray& visList, plPipeline* pipe); + void CleanUpGarbage( void ) { IRemoveGarbage(); } + + /// Funky particle system functions + virtual UInt32 CreateParticleSystem( UInt32 maxNumEmitters, UInt32 maxNumParticles, hsGMaterial *material ); + virtual void ResetParticleSystem( UInt32 index ); + virtual void AssignEmitterToParticleSystem( UInt32 index, plParticleEmitter *emitter ); + + /// SceneViewer only! + void GetOrigGeometrySpans( UInt32 diIndex, hsTArray &arrayToFill ); + void ClearAndSetMaterialCount(UInt32 count); +}; + + +#endif // _plDrawableSpans_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpansExport.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpansExport.cpp new file mode 100644 index 00000000..719642b1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDrawableSpansExport.cpp @@ -0,0 +1,768 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDrawableSpans Class Export-only Functions // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// Created 4.3.2001 mcn // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plDrawableSpans.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "plPipeline.h" +#include "plGeometrySpan.h" + +#include "plSpaceTree.h" +#include "plSpaceTreeMaker.h" // This is fun and amusing and wonderful to have here. + // Keep it here forever. +#include "../plSurface/hsGMaterial.h" +#include "../plPipeline/plFogEnvironment.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" // for NodeRefMsg +#include "../plMessage/plDeviceRecreateMsg.h" +#include "../plPipeline/plGBufferGroup.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" +#include "../plGImage/plBitmap.h" +#include "../plGLight/plLightInfo.h" +#include "plgDispatch.h" + +#include "../plStatusLog/plStatusLog.h" + +//#define VERT_LOG + +//// Write /////////////////////////////////////////////////////////////////// + +void plDrawableSpans::Write( hsStream* s, hsResMgr* mgr ) +{ + UInt32 i, j, count; + + // Make sure we're optimized before we write (should be tho) +// Optimize(); + + // Make sure all the garbage is cleaned up + if( fNeedCleanup ) + IRemoveGarbage(); + + // Parent write + plDrawable::Write(s, mgr); + + s->WriteSwap32( fProps ); + s->WriteSwap32( fCriteria ); + s->WriteSwap32( fRenderLevel.fLevel ); + + /// Write out the material keys + s->WriteSwap32( fMaterials.GetCount() ); + for( i = 0; i < fMaterials.GetCount(); i++ ) + mgr->WriteKey( s, fMaterials[ i ] ); + + /// Write out the icicles + s->WriteSwap32( fIcicles.GetCount() ); + for( i = 0; i < fIcicles.GetCount(); i++ ) + fIcicles[ i ].Write( s ); + + /// Write out the patches + // FIXME MAJOR VERSION + // no more patches, remove this line + s->WriteSwap32(0); + + /// Write out the index table based on the pointer array + count = fSpans.GetCount(); + s->WriteSwap32( count ); + for( i = 0; i < count; i++ ) + { + UInt8 *icicle = (UInt8 *)fSpans[ i ], *base = (UInt8 *)fIcicles.AcquireArray(); + j = (UInt32)( icicle - base ) / sizeof( plIcicle ); + s->WriteSwap32( j ); + } + + /// Write out the common keys + for( i = 0; i < count; i++ ) + { + // The fog environ key + mgr->WriteKey( s, fSpans[ i ]->fFogEnvironment ); + } + + /// Write out the bounds and stuff + if( count > 0 ) + { + fLocalBounds.Write(s); + fWorldBounds.Write(s); + fMaxWorldBounds.Write(s); + } + + for( i = 0; i < count; i++ ) + { + if( fSpans[i]->fProps & plSpan::kPropHasPermaLights ) + { + UInt32 lcnt = fSpans[i]->fPermaLights.GetCount(); + s->WriteSwap32(lcnt); + int j; + for( j = 0; j < lcnt; j++ ) + mgr->WriteKey( s, fSpans[i]->fPermaLights[j]); + } + if( fSpans[i]->fProps & plSpan::kPropHasPermaProjs ) + { + UInt32 lcnt = fSpans[i]->fPermaProjs.GetCount(); + s->WriteSwap32(lcnt); + int j; + for( j = 0; j < lcnt; j++ ) + mgr->WriteKey( s, fSpans[i]->fPermaProjs[j]); + } + } + + /// Write out the source spans if necessary + s->WriteSwap32( fSourceSpans.GetCount() ); + if( fSourceSpans.GetCount() > 0 ) + { + for( i = 0; i < fSourceSpans.GetCount(); i++ ) + fSourceSpans[ i ]->Write( s ); + } + + count = fLocalToWorlds.GetCount(); + s->WriteSwap32(count); + for( i = 0; i < count; i++ ) + { + fLocalToWorlds[i].Write(s); + fWorldToLocals[i].Write(s); + + fLocalToBones[i].Write(s); + fBoneToLocals[i].Write(s); + } + + // Write out the drawInterface index arrays + s->WriteSwap32( fDIIndices.GetCount() ); + for( i = 0; i < fDIIndices.GetCount(); i++ ) + { + plDISpanIndex *array = fDIIndices[ i ]; + + s->WriteSwap32( array->fFlags ); + s->WriteSwap32( array->GetCount() ); + for( j = 0; j < array->GetCount(); j++ ) + s->WriteSwap32( (*array)[ j ] ); + } + + // Write the groups out + count = fGroups.GetCount(); + + s->WriteSwap( count ); + for( i = 0; i < count; i++ ) + { +#ifdef VERT_LOG + + hsUNIXStream log; + log.Open("log\\GBuf.log", "ab"); + char buf[256]; + sprintf(buf, "Drawable Span: %s, GroupNum: %u\r\n", GetKeyName(), i); + log.WriteString(buf); + log.Close(); +#endif + fGroups[ i ]->Write( s ); + } + + /// Other stuff + mgr->WriteCreatable(s, fSpaceTree); + + mgr->WriteKey(s, fSceneNode); + + /// All done! +} + +//// AddDISpans ////////////////////////////////////////////////////////////// +// Adds a drawInterface's geometry spans to the list to be collapsed into +// buffers. + +UInt32 plDrawableSpans::AddDISpans( hsTArray &spans, UInt32 index ) +{ + int i; + UInt32 spanIdx; + plSpan *span; + hsBounds3Ext bounds; + + + /// Do garbage cleanup first + if( fNeedCleanup ) + IRemoveGarbage(); + + if (index == (UInt32)-1) // need a new one + { + /// Create a lookup entry + index = fDIIndices.GetCount(); + fDIIndices.Append( TRACKED_NEW plDISpanIndex ); + fDIIndices[ index ]->fFlags = plDISpanIndex::kNone; + } + plDISpanIndex *spanLookup = fDIIndices[ index ]; + + + /// Add the geometry spans to our list. Also add our internal span + /// copies + for( i = 0; i < spans.GetCount(); i++ ) + { + spanLookup->Append( fSourceSpans.GetCount() ); + spans[ i ]->fSpanRefIndex = fSourceSpans.GetCount(); + fSourceSpans.Append( spans[ i ] ); + + spanIdx = fIcicles.GetCount(); + fIcicles.Append( plIcicle() ); + plIcicle *icicle = &fIcicles[ spanIdx ]; + span = (plSpan *)icicle; + + /// Set common stuff + IAssignMatIdxToSpan( span, spans[ i ]->fMaterial ); + span->fLocalToWorld = spans[ i ]->fLocalToWorld; + span->fWorldToLocal = spans[ i ]->fWorldToLocal; + span->fProps |= ( spans[ i ]->fProps & plGeometrySpan::kPropRunTimeLight ) ? plSpan::kPropRunTimeLight : 0; + if( spans[i]->fProps & plGeometrySpan::kPropNoShadowCast ) + span->fProps |= plSpan::kPropNoShadowCast; + if( spans[i]->fProps & plGeometrySpan::kPropNoShadow ) + span->fProps |= plSpan::kPropNoShadow; + if( spans[i]->fProps & plGeometrySpan::kPropForceShadow ) + span->fProps |= plSpan::kPropForceShadow; + if( spans[i]->fProps & plGeometrySpan::kPropReverseSort ) + span->fProps |= plSpan::kPropReverseSort; + if( spans[i]->fProps & plGeometrySpan::kPartialSort ) + span->fProps |= plSpan::kPartialSort; + if( spans[i]->fProps & plGeometrySpan::kVisLOS ) + { + span->fProps |= plSpan::kVisLOS; + fProps |= plDrawable::kPropHasVisLOS; + } + + span->fNumMatrices = spans[ i ]->fNumMatrices; + span->fBaseMatrix = spans[ i ]->fBaseMatrix; + span->fLocalUVWChans = spans[i]->fLocalUVWChans; + span->fMaxBoneIdx = spans[i]->fMaxBoneIdx; + span->fPenBoneIdx = (UInt16)(spans[i]->fPenBoneIdx); + + bounds = spans[ i ]->fLocalBounds; + span->fLocalBounds = bounds; + bounds.Transform( &span->fLocalToWorld ); + span->fWorldBounds = bounds; + span->fFogEnvironment = spans[ i ]->fFogEnviron; + + /// Add to our source indices + fSpans.Append( span ); + fSpanSourceIndices.Append( spanIdx ); + } + + /// Rebuild the pointer array + IRebuildSpanArray(); + + SetSpaceTree(nil); + + fOptimized = false; + + return index; +} + +//// Optimize //////////////////////////////////////////////////////////////// + +void plDrawableSpans::Optimize( void ) +{ + int i; + + if( fOptimized ) + return; + + /// Sort all the source spans + if( !(fProps & kPropNoReSort) ) + ISortSourceSpans(); + + /// Pack the source spans into spans and indices + IPackSourceSpans(); + + /// Now that we're done with the source spans, get rid of them! (BLEAH!) + for( i = 0; i < fSourceSpans.GetCount(); i++ ) + delete fSourceSpans[ i ]; + fSourceSpans.Reset(); + + if( fCriteria & kCritSortSpans ) + { + if( !(fProps & kPropNoReSort) ) + fProps |= kPropSortSpans; + } + if( fCriteria & kCritSortFaces ) + { + fProps |= kPropSortFaces; + } + + /// Now we do a pass at the buffer groups, asking them to tidy up + for( i = 0; i < fGroups.GetCount(); i++ ) + fGroups[ i ]->TidyUp(); + + // Look to see if we have any materials we aren't using anymore. + for( i = 0; i < fMaterials.GetCount(); i++ ) + { + ICheckToRemoveMaterial(i); + } + + fReadyToRender = false; + + // Make the space tree (hierarchical bounds). + plSpaceTreeMaker maker; + maker.Reset(); + for( i = 0; i < GetNumSpans(); i++ ) + { + maker.AddLeaf( fSpans[ i ]->fWorldBounds, fSpans[ i ]->fProps & plSpan::kPropNoDraw ); + } + plSpaceTree* tree = maker.MakeTree(); + SetSpaceTree(tree); + + /// All done! + fOptimized = true; +} + +static plStatusLog* IStartLog(const char* name, int numSpans) +{ + static char buff[256]; + sprintf(buff, "x%s.log", name); + + plStatusLog* statusLog = plStatusLogMgr::GetInstance().CreateStatusLog( + plStatusLogMgr::kDefaultNumLines, + buff, + plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe ); + return statusLog; +} + +static plStatusLog* IEndLog(plStatusLog* statusLog) +{ + delete statusLog; + return nil; +} + +static void ILogSpan(plStatusLog* statusLog, plGeometrySpan* geo, plVertexSpan* span, plGBufferGroup* group) +{ + if( span->fTypeMask & plSpan::kIcicleSpan ) + { + plIcicle* ice = (plIcicle*)span; + if( geo->fProps & plGeometrySpan::kFirstInstance ) + { + plGBufferCell* cell = group->GetCell(span->fVBufferIdx, span->fCellIdx); + UInt32 stride = group->GetVertexSize(); + UInt32 ptr = cell->fVtxStart + span->fCellOffset * stride; + + statusLog->AddLineF("From obj <%s> mat <%s> size %d bytes grp=%d (%d offset)", + geo->fMaxOwner ? geo->fMaxOwner : "", + geo->fMaterial ? geo->fMaterial->GetKey()->GetName() : "", + geo->GetVertexSize(geo->fFormat) * geo->fNumVerts + sizeof(UInt16) * geo->fNumIndices, + span->fGroupIdx, + ptr + ); +// span->fVBufferIdx, +// span->fCellIdx, +// span->fCellOffset, +// span->fVStartIdx, +// span->fVLength, +// ice->fIBufferIdx, +// ice->fIStartIdx, +// ice->fILength + } + else + { + statusLog->AddLineF("Instanced obj <%s> mat <%s> grp=%d (%d/%d/%d/%d/%d/%d/%d/%d)", + geo->fMaxOwner ? geo->fMaxOwner : "", + geo->fMaterial ? geo->fMaterial->GetKey()->GetName() : "", + span->fGroupIdx, + span->fVBufferIdx, + span->fCellIdx, + span->fCellOffset, + span->fVStartIdx, + span->fVLength, + ice->fIBufferIdx, + ice->fIStartIdx, + ice->fILength + ); + } + } + else + { + if( geo->fProps & plGeometrySpan::kFirstInstance ) + { + statusLog->AddLineF("From obj <%s> mat <%s> size %d bytes grp=%d (%d/%d/%d/%d/%d)", + geo->fMaxOwner ? geo->fMaxOwner : "", + geo->fMaterial ? geo->fMaterial->GetKey()->GetName() : "", + geo->GetVertexSize(geo->fFormat) * geo->fNumVerts + sizeof(UInt16) * geo->fNumIndices, + span->fGroupIdx, + span->fVBufferIdx, + span->fCellIdx, + span->fCellOffset, + span->fVStartIdx, + span->fVLength + ); + } + else + { + statusLog->AddLineF("Instanced obj <%s> mat <%s> grp=%d (%d/%d/%d/%d/%d)", + geo->fMaxOwner ? geo->fMaxOwner : "", + geo->fMaterial ? geo->fMaterial->GetKey()->GetName() : "", + span->fGroupIdx, + span->fVBufferIdx, + span->fCellIdx, + span->fCellOffset, + span->fVStartIdx, + span->fVLength + ); + } + } +} + +//// IPackSourceSpans //////////////////////////////////////////////////////// +// Takes the array of source spans and converts them to our internal icicle +// spans, vertex buffers and index buffers. + +void plDrawableSpans::IPackSourceSpans( void ) +{ + int i, j; + hsBounds3Ext bounds; + hsBitVector doneSpans; + + + /// Calc bounds + fLocalBounds.MakeEmpty(); + fWorldBounds.MakeEmpty(); + + for( i = 0; i < fSourceSpans.GetCount(); i++ ) + { + hsBounds3Ext bnd = fSourceSpans[ i ]->fLocalBounds; + bnd.Transform( &fSourceSpans[ i ]->fLocalToWorld ); + fWorldBounds.Union( &bnd ); + } + + fLocalBounds = fWorldBounds; + fLocalBounds.Transform( &fWorldToLocal ); + fMaxWorldBounds = fWorldBounds; + + // It could be that instance refs in spans that we (the drawable) own + // are actually spans in some other drawable. That's not currently handled. + // (Making that case handled would involve rewriting the instancing implementation + // to not suck ass). + // So for each instance set, we make two lists of instance refs, the ones also + // in this drawable (refsHere), and ones not (refsThere). If there are refsThere, + // we split out the refsHere into a new instance group, removing them from the + // refsThere list. If refsThere still contains spans from separate drawables, + // that will be dealt with in those drawables' Optimize calls. + doneSpans.Clear(); + for( i = 0; i < fSourceSpans.GetCount(); i++ ) + { + if( !doneSpans.IsBitSet(i) ) + { + plGeometrySpan* span = fSourceSpans[i]; + if( span->fInstanceRefs ) + { + hsTArray& refs = *(span->fInstanceRefs); + hsTArray refsHere; + hsTArray refsThere; + + int k; + for( k = 0; k < refs.GetCount(); k++ ) + { + plGeometrySpan* other = refs[k]; + if( other != span ) + { + int idx = fSourceSpans.Find(other); + if( fSourceSpans.kMissingIndex == idx ) + { + refsThere.Append(other); + } + else + { + refsHere.Append(other); + } + } + } + if( refsThere.GetCount() ) + { + if( refsHere.GetCount() ) + { + span->BreakInstance(); + + // Okay, got to form a new instance group out of refsHere. + for( k = 0; k < refsHere.GetCount(); k++ ) + { + plGeometrySpan* other = refsHere[k]; + + other->ChangeInstance(span); + + doneSpans.SetBit(other->fSpanRefIndex); + } + } + else + { + span->UnInstance(); + } + if( refsThere.GetCount() == 1 ) + { + refsThere[0]->UnInstance(); + } + } + } + doneSpans.SetBit(i); + } + } + + /// Now pack the spans + doneSpans.Clear(); + for( i = 0; i < fSourceSpans.GetCount(); i++ ) + { + // Now we fill the rest of the data in for our span + if( !doneSpans.IsBitSet( i ) ) + { + if( fSourceSpans[ i ]->fProps & plGeometrySpan::kInstanced ) + { + // Instanced spans--convert the first as normal, then convert the rest + // using the first as reference + doneSpans.SetBit( i, true ); + + plIcicle *baseIcicle = (plIcicle *)fSpans[ i ]; + IConvertGeoSpanToIcicle( fSourceSpans[ i ], baseIcicle, 0, baseIcicle ); + + // Loop through the rest + for( j = 0; j < fSourceSpans[ i ]->fInstanceRefs->GetCount(); j++ ) + { + plGeometrySpan *other = (*fSourceSpans[ i ]->fInstanceRefs)[ j ]; + if( other == fSourceSpans[ i ] ) + continue; + +#if 0 // What exactly is this supposed to be doing? My guess is, NADA. + if( IConvertGeoSpanToIcicle( other, (plIcicle *)fSpans[ other->fSpanRefIndex ], 0, baseIcicle ) ) + baseIcicle = (plIcicle *)fSpans[ other->fSpanRefIndex ]; +#else // What exactly is this supposed to be doing? My guess is, NADA. + IConvertGeoSpanToIcicle( other, (plIcicle *)fSpans[ other->fSpanRefIndex ], 0, baseIcicle ); +#endif // What exactly is this supposed to be doing? My guess is, NADA. + doneSpans.SetBit( other->fSpanRefIndex, true ); + } + + } + else + { + // Do normal, uninstanced conversion + IConvertGeoSpanToIcicle( fSourceSpans[ i ], (plIcicle *)fSpans[ i ], 0 ); + doneSpans.SetBit( i, true ); + } + } + } + +#ifdef VERT_LOG + hsTArray order; + order.SetCount(fSourceSpans.GetCount()); + for( i = 0; i < fSourceSpans.GetCount(); i++ ) + { + order[fSourceSpans[i]->fSpanRefIndex] = fSourceSpans[i]; + } + + plStatusLog* statusLog = IStartLog(GetKey()->GetName(), fSourceSpans.GetCount()); + for( i = 0; i < order.GetCount(); i++ ) + { + plVertexSpan* vSpan = (plVertexSpan*)fSpans[i]; + ILogSpan(statusLog, order[i], vSpan, fGroups[vSpan->fGroupIdx]); + } + statusLog = IEndLog(statusLog); +#endif +} + +//// ISortSourceSpans //////////////////////////////////////////////////////// +// Does our actual optimization path by resorting all the spans into the +// most efficient order possible. Also has to re-order the span lookup +// table. + +void plDrawableSpans::ISortSourceSpans( void ) +{ + hsTArray spanReorderTable, spanInverseTable; + int i, j, idx; + plGeometrySpan *tmpSpan; + UInt32 tmpIdx; + plSpan *tmpSpanPtr; + + + // Init the reorder table + for( i = 0; i < fSourceSpans.GetCount(); i++ ) + spanReorderTable.Append( i ); + + // Do a nice, if naiive, sort by material (hehe by the pointers, no less) + for( i = 0; i < fSourceSpans.GetCount() - 1; i++ ) + { + for( j = i + 1, idx = i; j < fSourceSpans.GetCount(); j++ ) + { + if( ICompareSpans( fSourceSpans[ j ], fSourceSpans[ idx ] ) < 0 ) + idx = j; + } + + // Swap idx with i, so we get the smallest pointer on top + if( i != idx ) + { + // Swap both the source span and our internal span + tmpSpan = fSourceSpans[ i ]; + fSourceSpans[ i ] = fSourceSpans[ idx ]; + fSourceSpans[ idx ] = tmpSpan; + + fSourceSpans[ i ]->fSpanRefIndex = i; + fSourceSpans[ idx ]->fSpanRefIndex = idx; + + tmpSpanPtr = fSpans[ i ]; + fSpans[ i ] = fSpans[ idx ]; + fSpans[ idx ] = tmpSpanPtr; + + // Also swap the entries in the reorder table + tmpIdx = spanReorderTable[ i ]; + spanReorderTable[ i ] = spanReorderTable[ idx ]; + spanReorderTable[ idx ] = tmpIdx; + } + + // Next! + } + + + /// Problem: our reorder table is inversed(y->x instead of x->y). Either we search for numbers, + /// or we just flip it first... + spanInverseTable.SetCountAndZero( spanReorderTable.GetCount() ); + for( i = 0; i < spanReorderTable.GetCount(); i++ ) + spanInverseTable[ spanReorderTable[ i ] ] = i; + + /// Now update our span xlate table + for( i = 0; i < fDIIndices.GetCount(); i++ ) + { + if( !fDIIndices[ i ]->IsMatrixOnly() ) + { + for( j = 0; j < fDIIndices[ i ]->GetCount(); j++ ) + { + idx = (*fDIIndices[ i ])[ j ]; + + (*fDIIndices[ i ])[ j ] = spanInverseTable[ idx ]; + } + } + } + + /// Use our pointer array to rebuild the icicle array (UUUUGLY) + hsTArray tempIcicles; + plIcicle *newIcicle; + + tempIcicles.SetCount( fIcicles.GetCount() ); + + for( i = 0, newIcicle = tempIcicles.AcquireArray(); + i < fSpans.GetCount(); i++ ) + { + *newIcicle = *( (plIcicle *)fSpans[ i ] ); + fSpans[ i ] = newIcicle; + newIcicle++; + } + + /// Swap the two arrays out. This will basically swap the actual memory blocks, so be careful... + fIcicles.Swap( tempIcicles ); + tempIcicles.Reset(); +} + +//// ICompareSpans /////////////////////////////////////////////////////////// +// Sorting function for ISortSpans(). Kinda like strcmp(): returns -1 if +// span1 < span2, 1 if span1 > span2, 0 if "equal". + +short plDrawableSpans::ICompareSpans( plGeometrySpan *span1, plGeometrySpan *span2 ) +{ + hsBool b1, b2; + int i, j, numLayers; + plBitmap *t1, *t2; + + + /// Quick check--identical materials are easy to compare :) + if( span1->fMaterial == span2->fMaterial ) + return 0; + + /// Compare features from most to least important... + + // Any decal span should come after a non-decal + if( span1->fDecalLevel < span2->fDecalLevel ) + return -1; + if( span1->fDecalLevel > span2->fDecalLevel ) + return 1; + // Ok, they're equal decal-wise, so find something else to judge on + + // Most important: is one of the materials an alpha blend? (if so, gotta + // put at end, so it's "bigger") + if( span1->fMaterial->GetNumLayers() > 0 && + ( span1->fMaterial->GetLayer( 0 )->GetState().fBlendFlags & hsGMatState::kBlendMask ) != 0 ) + b1 = true; + else + b1 = false; + + if( span2->fMaterial->GetNumLayers() > 0 && + ( span2->fMaterial->GetLayer( 0 )->GetState().fBlendFlags & hsGMatState::kBlendMask ) != 0 ) + b2 = true; + else + b2 = false; + + if( b1 != b2 ) + return( b1 ? 1 : -1 ); + + // Next is texture (name). We do this kinda like strings: compare the first layer's + // textures and go upwards, so that we group materials together starting with the + // base layer's texture and going upwards + numLayers = span1->fMaterial->GetNumLayers(); + if( span2->fMaterial->GetNumLayers() < numLayers ) + numLayers = span2->fMaterial->GetNumLayers(); + + for( i = 0; i < numLayers; i++ ) + { + t1 = span1->fMaterial->GetLayer( i )->GetTexture(); + t2 = span2->fMaterial->GetLayer( i )->GetTexture(); + + if( t1 != nil && t2 == nil ) + return 1; + else if( t1 == nil && t2 != nil ) + return -1; + else if( t1 == nil && t2 == nil ) + break; // Textures equal up to here--keep going with rest of tests + + if( t1->GetKeyName() != nil && t2->GetKeyName() != nil ) + { + j = stricmp( t1->GetKeyName(), t2->GetKeyName() ); + if( j != 0 ) + return (short)j; + } + } + + // Finally, by material itself. + if( span1->fMaterial->GetKeyName() != nil && span2->fMaterial->GetKeyName() != nil ) + { + j = stricmp( span1->fMaterial->GetKeyName(), span2->fMaterial->GetKeyName() ); + if( j != 0 ) + return (short)j; + } + + if( span1->fLocalToWorld.fFlags != span2->fLocalToWorld.fFlags ) + { + if( span1->fLocalToWorld.fFlags ) + return -1; + else + return 1; + } + + /// Equal in our book... + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaBulletMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaBulletMgr.cpp new file mode 100644 index 00000000..2ba8b39d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaBulletMgr.cpp @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plDynaBulletMgr.h" +#include "plDynaDecal.h" + +#include "../plMessage/plBulletMsg.h" + +#include "plCutter.h" + +#include "plgDispatch.h" + + +#include "hsStream.h" +#include "hsResMgr.h" +#include "hsTimer.h" +#include "../pnMessage/plRefMsg.h" + + +#include "hsFastMath.h" +#include "../plMath/plRandom.h" +static plRandom sRand; + + +int plDynaBulletMgr::INewDecal() +{ + int idx = fDecals.GetCount(); + fDecals.Append(TRACKED_NEW plDynaSplot()); + + return idx; +} + +plDynaBulletMgr::plDynaBulletMgr() +{ +} + +plDynaBulletMgr::~plDynaBulletMgr() +{ +} + +void plDynaBulletMgr::Read(hsStream* stream, hsResMgr* mgr) +{ + plDynaDecalMgr::Read(stream, mgr); + + plgDispatch::Dispatch()->RegisterForExactType(plBulletMsg::Index(), GetKey()); +} + +void plDynaBulletMgr::Write(hsStream* stream, hsResMgr* mgr) +{ + plDynaDecalMgr::Write(stream, mgr); +} + +hsBool plDynaBulletMgr::IHandleEnableMsg(const plDynaDecalEnableMsg* enaMsg) +{ + return true; +} + +hsBool plDynaBulletMgr::IHandleShot(plBulletMsg* bull) +{ + hsVector3 up = IRandomUp(bull->Dir()); + + hsPoint3 pos = bull->From() + bull->Dir() * (bull->Range() * 0.5f); + fCutter->SetLength(hsVector3(bull->Radius() * fScale.fX, bull->Radius() * fScale.fY, bull->Range())); + fCutter->Set(pos, up, -bull->Dir()); + + plDynaDecalInfo& info = IGetDecalInfo(UInt32(this), GetKey()); + + if( bull->PartyTime() > 0 ) + fPartyTime = bull->PartyTime(); + + double secs = hsTimer::GetSysSeconds(); + + if( ICutoutTargets(secs) ) + info.fLastTime = secs; + + return true; +} + +hsBool plDynaBulletMgr::MsgReceive(plMessage* msg) +{ + plBulletMsg* bullMsg = plBulletMsg::ConvertNoRef(msg); + if( bullMsg ) + { + if( bullMsg->Shot() ) + { + return IHandleShot(bullMsg); + } + return true; + } + return plDynaDecalMgr::MsgReceive(msg); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaBulletMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaBulletMgr.h new file mode 100644 index 00000000..c495485e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaBulletMgr.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynaBulletMgr_inc +#define plDynaBulletMgr_inc + +#include "plDynaDecalMgr.h" + +class plBulletMsg; + +class plDynaBulletMgr : public plDynaDecalMgr +{ +public: +protected: + + virtual hsBool IHandleEnableMsg(const plDynaDecalEnableMsg* enaMsg); + virtual int INewDecal(); + + virtual hsBool IHandleShot(plBulletMsg* bull); + +public: + plDynaBulletMgr(); + virtual ~plDynaBulletMgr(); + + CLASSNAME_REGISTER( plDynaBulletMgr ); + GETINTERFACE_ANY( plDynaBulletMgr, plDynaDecalMgr ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); +}; + +#endif // plDynaBulletMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecal.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecal.cpp new file mode 100644 index 00000000..caa28f2b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecal.cpp @@ -0,0 +1,370 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" + +#include "plDynaDecal.h" +#include "plAuxSpan.h" + +hsBool plDynaSplot::Age(double t, hsScalar ramp, hsScalar decay, hsScalar life) +{ + hsScalar age = hsScalar(t - fBirth); + if( age >= life ) + return true; + + int n = fNumVerts; + if( !n ) + return true; + + hsScalar atten = fInitAtten; + if( age < ramp ) + { + atten *= age / ramp; + fFlags |= kFresh; + } + else if( age > decay ) + { + atten *= (life - age) / (life - decay); + fFlags |= kFresh; + } + else if( fFlags & kFresh ) + { + fFlags &= ~kFresh; + } + else + { + return false; + } + + hsPoint3* origUVW = &fAuxSpan->fOrigUVW[fStartVtx]; + + if( fFlags & kAttenColor ) + { + plDecalVtxFormat* vtx = fVtxBase; + + while( n-- ) + { + UInt32 diff = UInt32(origUVW->fZ * atten * 255.99f); + vtx->fDiffuse = 0xff000000 + | (diff << 16) + | (diff << 8) + | diff; + + vtx++; + origUVW++; + } + } + else + if( fAuxSpan->fFlags & plAuxSpan::kRTLit ) + { + const int stride = sizeof(plDecalVtxFormat); + + hsScalar* sPtr = &origUVW->fZ; + + unsigned char* alpha = (unsigned char*)&fVtxBase->fDiffuse; + alpha += 3; + while( n-- ) + { + hsScalar initOpac = *sPtr; + *alpha = unsigned char(initOpac * atten * 255.99f); + + alpha += stride; + sPtr += 3; + } + } + else + { + hsScalar* sPtr = &origUVW->fZ; + + char* oPtr = (char*)&fVtxBase->fUVW[1].fX; + + const int stride = sizeof(plDecalVtxFormat); + + while( n-- ) + { + (*(hsScalar*)oPtr) = *sPtr * atten; + + oPtr += stride; + sPtr += 3; + } + } + return false; +} + +hsBool plDynaRipple::Age(double t, hsScalar ramp, hsScalar decay, hsScalar life) +{ + hsScalar age = hsScalar(t - fBirth); + if( age >= life ) + return true; + + int n = fNumVerts; + if( !n ) + return true; + + hsScalar atten = fInitAtten; + if( age < ramp ) + { + atten *= age / ramp; + } + else if( age > decay ) + { + atten *= (life - age) / (life - decay); + } + + hsScalar scaleU = fC1U / (age*fC2U + 1.f); + hsScalar scaleV = fC1V / (age*fC2V + 1.f); + + hsPoint3* origUVW = &fAuxSpan->fOrigUVW[fStartVtx]; + + if( fFlags & kAttenColor ) + { + plDecalVtxFormat* vtx = fVtxBase; + + while( n-- ) + { + UInt32 diff = UInt32(origUVW->fZ * atten * 255.99f); + vtx->fDiffuse = 0xff000000 + | (diff << 16) + | (diff << 8) + | diff; + + vtx->fUVW[0].fX = (origUVW->fX - 0.5f) * scaleU + 0.5f; + vtx->fUVW[0].fY = (origUVW->fY - 0.5f) * scaleV + 0.5f; + + vtx++; + origUVW++; + } + } + else + if( fAuxSpan->fFlags & plAuxSpan::kRTLit ) + { + plDecalVtxFormat* vtx = fVtxBase; + + while( n-- ) + { + unsigned char* alpha = ((unsigned char*)&vtx->fDiffuse) + 3; + *alpha = unsigned char(origUVW->fZ * atten * 255.99f); + + vtx->fUVW[0].fX = (origUVW->fX - 0.5f) * scaleU + 0.5f; + vtx->fUVW[0].fY = (origUVW->fY - 0.5f) * scaleV + 0.5f; + + vtx++; + origUVW++; + } + } + else + { + plDecalVtxFormat* vtx = fVtxBase; + + while( n-- ) + { + vtx->fUVW[0].fX = (origUVW->fX - 0.5f) * scaleU + 0.5f; + vtx->fUVW[0].fY = (origUVW->fY - 0.5f) * scaleV + 0.5f; + + vtx->fUVW[1].fX = origUVW->fZ * atten; + + vtx++; + origUVW++; + } + } + fFlags &= ~kFresh; + return false; +} + +hsBool plDynaWake::Age(double t, hsScalar ramp, hsScalar decay, hsScalar life) +{ + hsScalar age = hsScalar(t - fBirth); + if( age >= life ) + return true; + + int n = fNumVerts; + if( !n ) + return true; + + hsScalar atten = fInitAtten; + if( age < ramp ) + { + atten *= age / ramp; + } + else if( age > decay ) + { + atten *= (life - age) / (life - decay); + } + + hsScalar scaleU = fC1U / (age*fC2U + 1.f); + hsScalar scaleV = fC1V / (age*fC2V + 1.f); + + hsPoint3* origUVW = &fAuxSpan->fOrigUVW[fStartVtx]; + + if( fFlags & kAttenColor ) + { + plDecalVtxFormat* vtx = fVtxBase; + + while( n-- ) + { + UInt32 diff = UInt32(origUVW->fZ * atten * 255.99f); + vtx->fDiffuse = 0xff000000 + | (diff << 16) + | (diff << 8) + | diff; + + vtx->fUVW[0].fX = (origUVW->fX - 0.5f) * scaleU + 0.5f; + vtx->fUVW[0].fY = origUVW->fY * scaleV; + + vtx++; + origUVW++; + } + } + else + if( fAuxSpan->fFlags & plAuxSpan::kRTLit ) + { + plDecalVtxFormat* vtx = fVtxBase; + + while( n-- ) + { + unsigned char* alpha = ((unsigned char*)&vtx->fDiffuse) + 3; + *alpha = unsigned char(origUVW->fZ * atten * 255.99f); + + vtx->fUVW[0].fX = (origUVW->fX - 0.5f) * scaleU + 0.5f; + vtx->fUVW[0].fY = origUVW->fY * scaleV; + + vtx++; + origUVW++; + } + } + else + { + plDecalVtxFormat* vtx = fVtxBase; + + while( n-- ) + { + vtx->fUVW[0].fX = (origUVW->fX - 0.5f) * scaleU + 0.5f; + vtx->fUVW[0].fY = origUVW->fY * scaleV; + + vtx->fUVW[1].fX = origUVW->fZ * atten; + + vtx++; + origUVW++; + } + } + fFlags &= ~kFresh; + return false; +} + +hsBool plDynaWave::Age(double t, hsScalar ramp, hsScalar decay, hsScalar life) +{ + hsScalar age = hsScalar(t - fBirth); + if( age >= life ) + return true; + + int n = fNumVerts; + if( !n ) + return true; + + hsScalar atten = fInitAtten; + if( age < ramp ) + { + atten *= age / ramp; + } + else if( age > decay ) + { + atten *= (life - age) / (life - decay); + } + + hsScalar scale = 1.f + life * fScrollRate; + hsScalar scroll = -fScrollRate * age; + + hsPoint3* origUVW = &fAuxSpan->fOrigUVW[fStartVtx]; + + if( fFlags & kAttenColor ) + { + plDecalVtxFormat* vtx = fVtxBase; + + while( n-- ) + { + UInt32 diff = UInt32(origUVW->fZ * atten * 255.99f); + vtx->fDiffuse = 0xff000000 + | (diff << 16) + | (diff << 8) + | diff; + + vtx->fUVW[0].fX = origUVW->fX; + vtx->fUVW[0].fY = origUVW->fY * scale + scroll; + + vtx++; + origUVW++; + } + } + else + if( fAuxSpan->fFlags & plAuxSpan::kRTLit ) + { + plDecalVtxFormat* vtx = fVtxBase; + + while( n-- ) + { + unsigned char* alpha = ((unsigned char*)&vtx->fDiffuse) + 3; + *alpha = unsigned char(origUVW->fZ * atten * 255.99f); + + vtx->fUVW[0].fX = origUVW->fX; + vtx->fUVW[0].fY = origUVW->fY * scale + scroll; + + vtx++; + origUVW++; + } + } + else + { + plDecalVtxFormat* vtx = fVtxBase; + + while( n-- ) + { + vtx->fUVW[0].fX = origUVW->fX; + vtx->fUVW[0].fY = origUVW->fY * scale + scroll; + + vtx->fUVW[1].fX = origUVW->fZ * atten; + + vtx++; + origUVW++; + } + } + fFlags &= ~kFresh; + return false; +} + +hsBool plDynaRippleVS::Age(double t, hsScalar ramp, hsScalar decay, hsScalar life) +{ + hsScalar age = hsScalar(t - fBirth); + if( age >= life ) + return true; + + int n = fNumVerts; + if( !n ) + return true; + + fFlags &= ~kFresh; + return false; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecal.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecal.h new file mode 100644 index 00000000..987dc6b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecal.h @@ -0,0 +1,145 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynaDecal_inc +#define plDynaDecal_inc + +#include "hsGeometry3.h" + +class plAuxSpan; + +class plDynaDecalBin; + +class plDecalVtxFormat +{ +public: + hsPoint3 fPos; + hsVector3 fNorm; + UInt32 fDiffuse; + UInt32 fSpecular; // Not used anywhere, carried around everywhere. + hsPoint3 fUVW[2]; +}; + +// UVW[0] is the currently used UVW. +// UVW[1] is for the alpha hack (when necessary). +const UInt8 kDecalVtxFormat = 0x2; // Two UVW's, otherwise vanilla. + + +class plDynaDecal +{ +public: + enum + { + kFresh = 0x1, + kAttenColor = 0x2, + kVertexShader = 0x4 + }; +protected: + + // StartVtx and StartIdx are relative to the start of the data + // owned by this decal's span, not relative to the start of the + // underlying buffers. + UInt16 fStartVtx; + UInt16 fNumVerts; + + UInt16 fStartIdx; + UInt16 fNumIdx; + + double fBirth; + hsScalar fInitAtten; + hsBool fFlags; + + plDecalVtxFormat* fVtxBase; // Safe pointer, the buffer data will outlive this decal + + plAuxSpan* fAuxSpan; + + friend class plDynaDecalMgr; +public: + + virtual hsBool Age(double t, hsScalar ramp, hsScalar decay, hsScalar life) = 0; +}; + +// No expansion +class plDynaSplot : public plDynaDecal +{ +protected: + +public: + + virtual hsBool Age(double t, hsScalar ramp, hsScalar decay, hsScalar life); +}; + +// Expands radially from center +class plDynaRipple : public plDynaDecal +{ +public: + + virtual hsBool Age(double t, hsScalar ramp, hsScalar decay, hsScalar life); + + hsScalar fC1U; + hsScalar fC2U; + + hsScalar fC1V; + hsScalar fC2V; + +}; + +// Expands in V from top (V=0), expands in U from center (U=0.5) +class plDynaWake : public plDynaDecal +{ +public: + + virtual hsBool Age(double t, hsScalar ramp, hsScalar decay, hsScalar life); + + hsScalar fC1U; + hsScalar fC2U; + + hsScalar fC1V; + hsScalar fC2V; + +}; + +// Scrolls in V, no change in U +class plDynaWave : public plDynaDecal +{ +public: + + virtual hsBool Age(double t, hsScalar ramp, hsScalar decay, hsScalar life); + + hsScalar fScrollRate; +}; + +// About the same as a DynaRipple, but implemented with vertex/pixel shaders. +// Only useful with plWaveSet. +class plDynaRippleVS : public plDynaRipple +{ +public: + + virtual hsBool Age(double t, hsScalar ramp, hsScalar decay, hsScalar life); +}; + +#endif // plDynaDecal_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecalMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecalMgr.cpp new file mode 100644 index 00000000..43e06d8b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecalMgr.cpp @@ -0,0 +1,1787 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plDynaDecalMgr.h" +#include "plDynaDecal.h" + +#include "plCutter.h" + +#include "plAccessGeometry.h" +#include "plAccessSpan.h" + +#include "plDrawableSpans.h" +#include "plAuxSpan.h" +#include "plSpaceTree.h" + +#include "plPrintShape.h" + +#include "../plAvatar/plArmatureMod.h" + +#include "../plParticleSystem/plParticleSystem.h" +#include "../plParticleSystem/plParticleEmitter.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" +#include "../plScene/plPageTreeMgr.h" + +#include "../plPipeline/plGBufferGroup.h" +#include "../plPipeline/hsGDeviceRef.h" + +#include "../plMessage/plAgeLoadedMsg.h" +#include "../plMessage/plDynaDecalEnableMsg.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "plgDispatch.h" + +#include "../plMath/plRandom.h" +#include "hsFastMath.h" + +#include "hsStream.h" +#include "hsResMgr.h" +#include "hsTimer.h" + +#include "../pnMessage/plPipeResMakeMsg.h" + +// Stuff for creating a bumpenv decal on demand. +#include "../plGImage/plMipmap.h" +#include "../plSurface/plLayer.h" +#include "../plMessage/plLayRefMsg.h" + +//### Hackage +#include "../plMessage/plRenderMsg.h" +#include "../plMessage/plListenerMsg.h" +#include "plPipeline.h" + +#include "plTweak.h" + +#include "plProfile.h" + +plProfile_CreateTimerNoReset("Total", "DynaDecal", Total); +plProfile_CreateTimerNoReset("Cutter", "DynaDecal", Cutter); +plProfile_CreateTimerNoReset("Process", "DynaDecal", Process); +plProfile_CreateTimerNoReset("Callback", "DynaDecal", Callback); + +static plRandom sRand; +static const int kBinBlockSize = 20; +static const UInt16 kDefMaxNumVerts = 1000; +static const UInt16 kDefMaxNumIdx = kDefMaxNumVerts; + +static const hsScalar kDefLifeSpan = 30.f; +static const hsScalar kDefDecayStart = kDefLifeSpan * 0.5f; +static const hsScalar kDefRampEnd = kDefLifeSpan * 0.1f; + +static const hsScalar kInitAuxSpans = 5; + +#define MF_NO_INIT_ALLOC +#define MF_NEVER_RUN_OUT + +// If we aren't doing an initial alloc, we MUST have NEVER_RUN_OUT +// It's also useful to not have initial alloc but do have NEVER_RUN_OUT +// So: +// MF_NO_INIT_ALLOC && MF_NEVER_RUN_OUT - Okay +// !MF_NO_INIT_ALLOC && !MF_NEVER_RUN_OUT - Okay +// !MF_NO_INIT_ALLOC && MF_NEVER_RUN_OUT - Okay +// MF_NO_INIT_ALLOC && !MF_NEVER_RUN_OUT - Bad (you'll never get any decals) +#if defined(MF_NO_INIT_ALLOC) && !defined(MF_NEVER_RUN_OUT) +#define MF_NEVER_RUN_OUT +#endif // defined(MF_NO_INIT_ALLOC) && !defined(MF_NEVER_RUN_OUT) + +using namespace std; + +hsBool plDynaDecalMgr::fDisableAccumulate = false; +hsBool plDynaDecalMgr::fDisableUpdate = false; + +plDynaDecalMgr::plDynaDecalMgr() +: + fMatPreShade(nil), + fMatRTShade(nil), + fMaxNumVerts(kDefMaxNumVerts), + fMaxNumIdx(kDefMaxNumIdx), + fWetLength(0), + fRampEnd(kDefRampEnd), + fDecayStart(kDefDecayStart), + fLifeSpan(kDefLifeSpan), + fInitAtten(1.f), + fIntensity(1.f), + fWaitOnEnable(false), + fGridSizeU(2.5f), + fGridSizeV(2.5f), + fScale(1.f, 1.f, 1.f), + fPartyTime(1.f) +{ + fCutter = TRACKED_NEW plCutter; +} + +plDynaDecalMgr::~plDynaDecalMgr() +{ + int i; + for( i = 0; i < fDecals.GetCount(); i++ ) + delete fDecals[i]; + + for( i = 0; i < fAuxSpans.GetCount(); i++ ) + { + if( fAuxSpans[i]->fDrawable ) + { + plSpan* span = const_cast(fAuxSpans[i]->fDrawable->GetSpan(fAuxSpans[i]->fBaseSpanIdx)); + + span->RemoveAuxSpan(fAuxSpans[i]); + } + + delete fAuxSpans[i]->fGroup; + + delete fAuxSpans[i]; + } + + delete fCutter; +} + +void plDynaDecalMgr::SetKey(plKey k) +{ + hsKeyedObject::SetKey(k); + if( k ) + { + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plPipeGeoMakeMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); + } +} + +void plDynaDecalMgr::Read(hsStream* stream, hsResMgr* mgr) +{ + plSynchedObject::Read(stream, mgr); + + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefMatPreShade), plRefFlags::kActiveRef); + + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefMatRTShade), plRefFlags::kActiveRef); + + int n = stream->ReadSwap32(); + int i; + for( i = 0; i < n; i++ ) + { + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefTarget), plRefFlags::kPassiveRef); + } + // Associated slave particle systems. We read in the scene objects now, and find the associated systems on loaded message. + n = stream->ReadSwap32(); + for( i = 0; i < n; i++ ) + { + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefPartyObject), plRefFlags::kPassiveRef); + } + + fMaxNumVerts = (UInt16)(stream->ReadSwap32()); + fMaxNumIdx = (UInt16)(stream->ReadSwap32()); + + fWaitOnEnable = stream->ReadSwap32(); + + fIntensity = stream->ReadSwapScalar(); + fInitAtten = fIntensity; + + fWetLength = stream->ReadSwapScalar(); + fRampEnd = stream->ReadSwapScalar(); + fDecayStart = stream->ReadSwapScalar(); + fLifeSpan = stream->ReadSwapScalar(); + + fGridSizeU = stream->ReadSwapScalar(); + fGridSizeV = stream->ReadSwapScalar(); + + fScale.Read(stream); + + fPartyTime = stream->ReadSwapScalar(); + + n = stream->ReadSwap32(); + fNotifies.SetCount(n); + for( i = 0; i < n; i++ ) + fNotifies[i] = mgr->ReadKey(stream); + + // If we need to be creating DynaDecalMgrs on the fly, this should go in the + // constructor, or we should call it explicitly on the DynaDecalMgr we create. + // But putting it here makes it automatic for normal scene loading, without + // popping up during the export conversion process. +#ifndef MF_NO_INIT_ALLOC + InitAuxSpans(); +#endif // MF_NO_INIT_ALLOC + + ///////////////////////////////////////////////////// + // ###Things that should be in derived classes follow. + +} + +void plDynaDecalMgr::Write(hsStream* stream, hsResMgr* mgr) +{ + plSynchedObject::Write(stream, mgr); + + mgr->WriteKey(stream, fMatPreShade); + mgr->WriteKey(stream, fMatRTShade); + + stream->WriteSwap32(fTargets.GetCount()); + + int i; + for( i = 0; i < fTargets.GetCount(); i++ ) + { + mgr->WriteKey(stream, fTargets[i]); + } + + // Particle systems (really their associated sceneobjects). + stream->WriteSwap32(fPartyObjects.GetCount()); + for( i = 0; i < fPartyObjects.GetCount(); i++ ) + { + mgr->WriteKey(stream, fPartyObjects[i]); + } + + stream->WriteSwap32(fMaxNumVerts); + stream->WriteSwap32(fMaxNumIdx); + + stream->WriteSwap32(fWaitOnEnable); + + stream->WriteSwapScalar(fIntensity); + + stream->WriteSwapScalar(fWetLength); + stream->WriteSwapScalar(fRampEnd); + stream->WriteSwapScalar(fDecayStart); + stream->WriteSwapScalar(fLifeSpan); + + stream->WriteSwapScalar(fGridSizeU); + stream->WriteSwapScalar(fGridSizeV); + + fScale.Write(stream); + + stream->WriteSwapScalar(fPartyTime); + + stream->WriteSwap32(fNotifies.GetCount()); + for( i = 0; i < fNotifies.GetCount(); i++ ) + mgr->WriteKey(stream, fNotifies[i]); + + ///////////////////////////////////////////////////// + // ###Things that should be in derived classes follow. + +} + +hsBool plDynaDecalMgr::IMakeAuxRefs(plPipeline* pipe) +{ + int i; + for( i = 0; i < fGroups.GetCount(); i++ ) + fGroups[i]->PrepForRendering(pipe, false); + + return true; +} + +const plPrintShape* plDynaDecalMgr::IGetPrintShape(const plKey& objKey) const +{ + const plPrintShape* shape = nil; + plSceneObject* part = plSceneObject::ConvertNoRef(objKey->ObjectIsLoaded()); + if( part ) + { + // This is a safe cast, because GetGenericInterface(type) will only return + // either a valid object of that type, or nil. + shape = static_cast(part->GetGenericInterface(plPrintShape::Index())); + } + return shape; +} + +const plPrintShape* plDynaDecalMgr::IGetPrintShape(plArmatureMod* avMod, UInt32 id) const +{ + const plPrintShape* shape = nil; + const plSceneObject* part = avMod->FindBone(id); + if( part ) + { + // This is a safe cast, because GetGenericInterface(type) will only return + // either a valid object of that type, or nil. + shape = static_cast(part->GetGenericInterface(plPrintShape::Index())); + } + return shape; +} + +hsBool plDynaDecalMgr::IHandleEnableMsg(const plDynaDecalEnableMsg* enaMsg) +{ + IWetParts(enaMsg); + + return true; +} + +hsBool plDynaDecalMgr::IWetParts(const plDynaDecalEnableMsg* enaMsg) +{ + if( !enaMsg->IsArmature() ) + { + const plPrintShape* shape = IGetPrintShape(enaMsg->GetShapeKey()); + if( shape ) + { + plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey()); + IWetInfo(info, enaMsg); + } + } + else + if( enaMsg->GetID() == UInt32(-1) ) + { + plArmatureMod* avMod = plArmatureMod::ConvertNoRef(enaMsg->GetArmKey()->ObjectIsLoaded()); + int i; + for( i = 0; i < fPartIDs.GetCount(); i++ ) + { + const plPrintShape* shape = IGetPrintShape(avMod, fPartIDs[i]); + if( shape ) + { + plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey()); + IWetInfo(info, enaMsg); + } + } + } + else + { + IWetPart(enaMsg->GetID(), enaMsg); + } + return true; +} + +hsBool plDynaDecalMgr::IWetPart(UInt32 id, const plDynaDecalEnableMsg* enaMsg) +{ + plArmatureMod* avMod = plArmatureMod::ConvertNoRef(enaMsg->GetArmKey()->ObjectIsLoaded()); + + const plPrintShape* shape = IGetPrintShape(avMod, id); + if( shape ) + { + plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey()); + IWetInfo(info, enaMsg); + } + return true; +} + +void plDynaDecalMgr::IWetInfo(plDynaDecalInfo& info, const plDynaDecalEnableMsg* enaMsg) const +{ + info.fWetTime = enaMsg->GetContactTime(); + info.fWetLength = enaMsg->GetWetLength(); + if( !enaMsg->AtEnd() ) + info.fFlags |= plDynaDecalInfo::kImmersed; + else + info.fFlags &= ~plDynaDecalInfo::kImmersed; +} + +hsBool plDynaDecalMgr::MsgReceive(plMessage* msg) +{ + // On eval pulse, update all our active decals, letting old ones die off. + plEvalMsg* eval = plEvalMsg::ConvertNoRef(msg); + if( eval ) + { + IUpdateDecals(hsTimer::GetSysSeconds()); + return true; + } + + plDynaDecalEnableMsg* enaMsg = plDynaDecalEnableMsg::ConvertNoRef(msg); + if( enaMsg ) + { + IHandleEnableMsg(enaMsg); + + return true; + } + + plPipeGeoMakeMsg* make = plPipeGeoMakeMsg::ConvertNoRef(msg); + if( make ) + { + return IMakeAuxRefs(make->Pipeline()); + } + + plAgeLoadedMsg* ageLoadMsg = plAgeLoadedMsg::ConvertNoRef(msg); + if( ageLoadMsg && ageLoadMsg->fLoaded ) + { + IGetParticles(); + return true; + } + + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + switch( refMsg->fType ) + { + case kRefMatPreShade: + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fMatPreShade = hsGMaterial::ConvertNoRef(refMsg->GetRef()); + else + fMatPreShade = nil; + return true; + case kRefMatRTShade: + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fMatRTShade = hsGMaterial::ConvertNoRef(refMsg->GetRef()); + else + fMatRTShade = nil; + return true; + case kRefTarget: + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fTargets.Append(plSceneObject::ConvertNoRef(refMsg->GetRef())); + } + else + { + int idx = fTargets.Find((plSceneObject*)refMsg->GetRef()); + if( idx != fTargets.kMissingIndex ) + fTargets.Remove(idx); + } + return true; + case kRefPartyObject: + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fPartyObjects.Append(plSceneObject::ConvertNoRef(refMsg->GetRef())); + } + else + { + int idx = fPartyObjects.Find((plSceneObject*)refMsg->GetRef()); + if( idx != fPartyObjects.kMissingIndex ) + fPartyObjects.Remove(idx); + } + return true; + case kRefParticles: + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fParticles.Append(plParticleSystem::ConvertNoRef(refMsg->GetRef())); + fParticles[fParticles.GetCount()-1]->fMiscFlags |= plParticleSystem::kParticleSystemAlwaysUpdate; + } + else + { + int idx = fParticles.Find((plParticleSystem*)refMsg->GetRef()); + if( idx != fParticles.kMissingIndex ) + fParticles.Remove(idx); + } + return true; + case kRefAvatar: + if( refMsg->GetContext() & (plRefMsg::kOnRemove|plRefMsg::kOnDestroy) ) + IRemoveDecalInfo(UInt32(refMsg->GetRef())); + return true; + } + } + + return plSynchedObject::MsgReceive(msg); +} + +////////////////////////////////////////////////////////////////////////////////// +// +void plDynaDecalMgr::INotifyActive(plDynaDecalInfo& info, const plKey& armKey, UInt32 id) const +{ + if( !(info.fFlags & plDynaDecalInfo::kActive) ) + { + double secs = hsTimer::GetSysSeconds(); + int i; + for( i = 0; i < fNotifies.GetCount(); i++ ) + { + plDynaDecalEnableMsg* enaMsg = TRACKED_NEW plDynaDecalEnableMsg(fNotifies[i], armKey, secs, fWetLength, false, id); + enaMsg->Send(); + } + info.fFlags |= plDynaDecalInfo::kActive; + } +} + +void plDynaDecalMgr::INotifyInactive(plDynaDecalInfo& info, const plKey& armKey, UInt32 id) const +{ + if( info.fFlags & plDynaDecalInfo::kActive ) + { + double secs = hsTimer::GetSysSeconds(); + int i; + for( i = 0; i < fNotifies.GetCount(); i++ ) + { + plDynaDecalEnableMsg* enaMsg = TRACKED_NEW plDynaDecalEnableMsg(fNotifies[i], armKey, secs, fWetLength, true, id); + enaMsg->Send(); + } + info.fFlags &= ~plDynaDecalInfo::kActive; + } +} + +plDynaDecalInfo& plDynaDecalInfo::Init(const plKey& key) +{ + fKey = key; + + fLastTime = -1.e33f; + fLastPos.Set(-1.e33f, -1.e33f, -1.e33f); + fWetTime = -1.e33f; + fWetLength = 0; + fFlags = kNone; + + return *this; +} + +plDynaDecalInfo& plDynaDecalMgr::IGetDecalInfo(UInt32 id, const plKey& key) +{ + plDynaDecalMap::iterator iter = fDecalMap.find(id); + if( iter == fDecalMap.end() ) + { + plDynaDecalInfo decalInfo; + decalInfo.Init(key); + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefAvatar); + hsgResMgr::ResMgr()->AddViaNotify(plKey(key), refMsg, plRefFlags::kPassiveRef); + + pair iterPair; + iterPair = fDecalMap.insert(plDynaDecalMap::value_type(id, decalInfo)); + iter = iterPair.first; + } + + return iter->second; +} + +void plDynaDecalMgr::IRemoveDecalInfo(UInt32 id) +{ + plDynaDecalMap::iterator iter = fDecalMap.find(id); + if( iter != fDecalMap.end() ) + fDecalMap.erase(iter, iter); +} + +void plDynaDecalMgr::IRemoveDecalInfos(const plKey& key) +{ + plDynaDecalMap::iterator iter = fDecalMap.begin(); + while( iter != fDecalMap.end() ) + { + if( iter->second.fKey == key ) + { + plDynaDecalMap::iterator nuke0 = iter; + plDynaDecalMap::iterator nuke1 = iter; + iter++; + while( (iter != fDecalMap.end()) && (iter->second.fKey == key) ) + { + nuke1 = iter; + iter++; + } + fDecalMap.erase(nuke0, nuke1); + } + } +} + +hsScalar plDynaDecalMgr::IHowWet(plDynaDecalInfo& info, double t) const +{ + // We aren't playing this wet/dry/enable/disable thing. + if( !fWaitOnEnable ) + return fIntensity; + + // We've been notified we've entered the pool, + // and haven't been notified we've left it. + if( info.fFlags & plDynaDecalInfo::kImmersed ) + { + info.fWetTime = t; + return fIntensity; + } + + // We've never been enabled. + if( info.fWetLength <= 0 ) + return 0; + + // We're wet, let's see how wet. + hsScalar wet = (hsScalar)(1.f - (t - info.fWetTime) / info.fWetLength); + if( wet > 1.f ) // This should never happen. It means t < info.fWetTime (we get wet in the future). + return fIntensity; + if( wet < 0 ) + return 0; + return wet * fIntensity; +} +// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// + +plAuxSpan* plDynaDecalMgr::IGetAuxSpan(plDrawableSpans* targ, int iSpan, hsGMaterial* mat, UInt16 numVerts, UInt16 numIdx) +{ + // Some of this code just assumes you get the number of verts you ask for. + // Which was causing errors when you asked for more than the max and didn't + // get it. So now if you ask for too many verts, you just lose. + if (numVerts > fMaxNumVerts || numIdx > fMaxNumIdx) + return nil; + + plSpan* span = const_cast(targ->GetSpan(iSpan)); + + int i; + + // First, see if we've got an aux span already sitting on this span. + // We can use an existing aux span iff + // a) we own it + // b) there's enough room to append our stuff. + // + // This is kind of overkill in both respects, because: + // a) we don't care if we own it, it just really needs the same material + // b) there might be room at the beginning or middle of the span + // + // Relaxing those brings on the additional bookkeeping with having a span + // that requires multiple drawprimitive calls. + // Case a) can cause it if we share with a DynaDecalMgr with a different + // lifespan than ours. That would allow decals in the middle of the span + // to die out. + // Case b) won't have random bits dropping out of the middle, but will + // create a gap in the middle (and probably end) when we wrap around. + // + // The simplicity in bookkeeping should make up for any space/speed advantages + // in packing more into a single AuxSpan. + for( i = 0; i < span->GetNumAuxSpans(); i++ ) + { + plAuxSpan* aux = span->GetAuxSpan(i); + if( (aux->fOwner == (void*)this) + &&(aux->fVStartIdx + aux->fVLength + numVerts < aux->fVBufferLimit) + &&(aux->fIStartIdx + aux->fILength + numIdx < aux->fIBufferLimit) ) + return aux; + } + + hsBool rtLit = span->fProps & plSpan::kLiteVtxNonPreshaded; + + // Now look to see if we've got one sitting around unused that's suitable. + // Here the suitable criteria is a little different. We know we are the owner, + // and we know there's enough room (because it's sitting idle). + for( i = 0; i < fAuxSpans.GetCount(); i++ ) + { + plAuxSpan* aux = fAuxSpans[i]; + if( !aux->fDrawable + &&(aux->fVStartIdx + aux->fVLength + numVerts < aux->fVBufferLimit) + &&(aux->fIStartIdx + aux->fILength + numIdx < aux->fIBufferLimit) ) + { + aux->fDrawable = targ; + aux->fBaseSpanIdx = iSpan; + + ISetAuxMaterial(aux, mat, rtLit); + + span->AddAuxSpan(aux); + + return aux; + } + } + + // Ain't got one. We could allocate another one, or we can just say too bad doo-dad. + // If we allocate a new one: + // A) we'll need to flush managed memory to load it in. + // B) we'll be stuck with that memory (video and system) used up until this age is paged out and reloaded + // C) we've got a whole bunch of extra faces to draw. + // If we just return nil: + // A) opposite of above + // B) we stop leaving footprints/ripples for a while. + // I'm going to try the latter for a bit. + +#ifdef MF_NEVER_RUN_OUT + // Okay, nothing there. Let's get a new one. + plAuxSpan* aux = TRACKED_NEW plAuxSpan; + fAuxSpans.Append(aux); + + IAllocAuxSpan(aux, numVerts, numIdx); + + aux->fOwner = (void*)this; + aux->fDrawable = targ; + aux->fBaseSpanIdx = iSpan; + + ISetAuxMaterial(aux, mat, rtLit); + + span->AddAuxSpan(aux); + + return aux; +#else // MF_NEVER_RUN_OUT + return nil; +#endif // MF_NEVER_RUN_OUT +} + +void plDynaDecalMgr::InitAuxSpans() +{ + int i; + for( i = 0; i < kInitAuxSpans; i++ ) + { + plAuxSpan* aux = TRACKED_NEW plAuxSpan; + fAuxSpans.Append(aux); + IAllocAuxSpan(aux, fMaxNumVerts, fMaxNumIdx); + } +} + +void plDynaDecalMgr::IAllocAuxSpan(plAuxSpan* aux, UInt32 maxNumVerts, UInt32 maxNumIdx) +{ + int iGrp = fGroups.GetCount(); + plGBufferGroup* grp = TRACKED_NEW plGBufferGroup(kDecalVtxFormat, true, false); + fGroups.Append(grp); + + grp->ReserveVertStorage(maxNumVerts, + &aux->fVBufferIdx, + &aux->fCellIdx, + &aux->fCellOffset, + plGBufferGroup::kReserveInterleaved); + + aux->fFlags = 0; + + aux->fVStartIdx = grp->GetVertStartFromCell(aux->fVBufferIdx, aux->fCellIdx, aux->fCellOffset); + aux->fVLength = 0; + + UInt16* dataPtr = nil; + grp->ReserveIndexStorage(maxNumIdx, &aux->fIBufferIdx, &aux->fIStartIdx, &dataPtr); + aux->fIStartIdx; + + aux->fILength = 0; + + aux->fGroup = grp; + + aux->fVBufferInit = aux->fVStartIdx; + aux->fVBufferLimit = aux->fVBufferInit + maxNumVerts; + + aux->fIBufferInit = aux->fIStartIdx; + aux->fIBufferLimit = aux->fIBufferInit + maxNumIdx; + + aux->fOrigPos.SetCount(maxNumVerts); + aux->fOrigUVW.SetCount(maxNumVerts); + + aux->fOwner = (void*)this; + aux->fDrawable = nil; + aux->fBaseSpanIdx = 0; + + grp->SetVertBufferStart(aux->fVBufferIdx, aux->fVStartIdx); + grp->SetIndexBufferStart(aux->fIBufferIdx, aux->fIStartIdx); + grp->SetVertBufferEnd(aux->fVBufferIdx, aux->fVStartIdx); + grp->SetIndexBufferEnd(aux->fIBufferIdx, aux->fIStartIdx); +} + +hsGMaterial* plDynaDecalMgr::ISetAuxMaterial(plAuxSpan* aux, hsGMaterial* mat, hsBool rtLit) +{ + if( !mat ) + mat = fMatRTShade; + + hsBool attenColor = 0 != (mat->GetLayer(0)->GetBlendFlags() + & (hsGMatState::kBlendAdd + | hsGMatState::kBlendMult + | hsGMatState::kBlendMADD)); + hsBool bump = 0 != (mat->GetLayer(0)->GetMiscFlags() & hsGMatState::kMiscBumpChans); + hsBool hasVS = nil != mat->GetLayer(0)->GetVertexShader(); + + if( hasVS ) + { + aux->fFlags |= plAuxSpan::kVertexShader; + aux->fFlags &= ~plAuxSpan::kAttenColor; + aux->fFlags &= ~(plAuxSpan::kOverrideLiteModel | plAuxSpan::kRTLit); + aux->fMaterial = mat; + } + else + if( bump ) + { + aux->fFlags &= ~plAuxSpan::kVertexShader; + aux->fFlags |= plAuxSpan::kAttenColor; + aux->fFlags &= ~(plAuxSpan::kOverrideLiteModel | plAuxSpan::kRTLit); + aux->fMaterial = mat; + } + else + if( attenColor ) + { + aux->fFlags &= ~plAuxSpan::kVertexShader; + aux->fFlags |= plAuxSpan::kOverrideLiteModel | plAuxSpan::kAttenColor; + aux->fFlags &= ~plAuxSpan::kRTLit; + aux->fMaterial = mat; + } + else + if( rtLit ) + { + aux->fFlags &= ~plAuxSpan::kVertexShader; + aux->fFlags &= ~(plAuxSpan::kOverrideLiteModel | plAuxSpan::kAttenColor); + aux->fFlags |= plAuxSpan::kRTLit; + aux->fMaterial = mat; + } + else + { + aux->fFlags &= ~(plAuxSpan::kOverrideLiteModel | plAuxSpan::kAttenColor); + aux->fFlags &= ~plAuxSpan::kRTLit; + aux->fMaterial = fMatPreShade; + } + return aux->fMaterial; +} + +////////////////////////////////////////////////////////////////////////////////// + +plDynaDecal* plDynaDecalMgr::IInitDecal(plAuxSpan* aux, double t, UInt16 numVerts, UInt16 numIdx) +{ + int idx = INewDecal(); + + fDecals[idx]->fStartVtx = (UInt16)(aux->fVStartIdx + aux->fVLength); + fDecals[idx]->fNumVerts = numVerts; + fDecals[idx]->fStartIdx = (UInt16)(aux->fIStartIdx + aux->fILength); + fDecals[idx]->fNumIdx = numIdx; + + fDecals[idx]->fBirth = t; + fDecals[idx]->fFlags = plDynaDecal::kFresh; + fDecals[idx]->fInitAtten = fInitAtten; + + fDecals[idx]->fAuxSpan = aux; + + aux->fVLength += numVerts; + aux->fGroup->SetVertBufferEnd(aux->fVBufferIdx, aux->fVStartIdx + aux->fVLength); + aux->fGroup->DirtyVertexBuffer(aux->fVBufferIdx); + + aux->fILength += numIdx; + aux->fGroup->SetIndexBufferEnd(aux->fIBufferIdx, aux->fIStartIdx + aux->fILength); + aux->fGroup->DirtyIndexBuffer(aux->fIBufferIdx); + + if( aux->fFlags & plAuxSpan::kVertexShader ) + fDecals[idx]->fFlags |= plDynaDecal::kVertexShader; + else + if( aux->fFlags & plAuxSpan::kAttenColor ) + fDecals[idx]->fFlags |= plDynaDecal::kAttenColor; + + // We should probably assert here that our span hasn't just overrun buffergroup storage. + hsAssert(aux->fVStartIdx + aux->fVLength <= aux->fVBufferLimit, "Overrunning allocated storage"); + hsAssert(aux->fIStartIdx + aux->fILength <= aux->fIBufferLimit, "Overrunning allocated storage"); + + hsAssert(aux->fGroup->GetVertBufferEnd(aux->fVBufferIdx) >= aux->fGroup->GetVertBufferStart(aux->fVBufferIdx), "Going out of range on verts"); + hsAssert(aux->fGroup->GetIndexBufferEnd(aux->fIBufferIdx) >= aux->fGroup->GetIndexBufferStart(aux->fIBufferIdx), "Going out of range on verts"); + + return fDecals[idx]; +} + +void plDynaDecalMgr::IKillDecal(int i) +{ + // Update this decal's span. + // Since decals die off in the same order they are created, and we always + // append a decal to a span, we only need to advance the span's start indices, + // and decrement the lengths. + plAuxSpan* aux = fDecals[i]->fAuxSpan; + aux->fVStartIdx += fDecals[i]->fNumVerts; + aux->fGroup->SetVertBufferStart(aux->fVBufferIdx, aux->fVStartIdx); + aux->fVLength -= fDecals[i]->fNumVerts; + + aux->fIStartIdx += fDecals[i]->fNumIdx; + aux->fGroup->SetIndexBufferStart(aux->fIBufferIdx, aux->fIStartIdx); + aux->fILength -= fDecals[i]->fNumIdx; + + hsAssert(aux->fGroup->GetVertBufferEnd(aux->fVBufferIdx) >= aux->fGroup->GetVertBufferStart(aux->fVBufferIdx), "Going out of range on verts"); + hsAssert(aux->fGroup->GetIndexBufferEnd(aux->fIBufferIdx) >= aux->fGroup->GetIndexBufferStart(aux->fIBufferIdx), "Going out of range on verts"); + + if( !aux->fVLength ) + { + hsAssert(!aux->fILength, "Ran out of verts before indices"); + aux->fVStartIdx = aux->fVBufferInit; + aux->fIStartIdx = aux->fIBufferInit; + + aux->fGroup->SetVertBufferStart(aux->fVBufferIdx, aux->fVStartIdx); + aux->fGroup->SetIndexBufferStart(aux->fIBufferIdx, aux->fIStartIdx); + aux->fGroup->SetVertBufferEnd(aux->fVBufferIdx, aux->fVStartIdx); + aux->fGroup->SetIndexBufferEnd(aux->fIBufferIdx, aux->fIStartIdx); + + hsAssert(aux->fGroup->GetVertBufferEnd(aux->fVBufferIdx) >= aux->fGroup->GetVertBufferStart(aux->fVBufferIdx), "Going out of range on verts"); + hsAssert(aux->fGroup->GetIndexBufferEnd(aux->fIBufferIdx) >= aux->fGroup->GetIndexBufferStart(aux->fIBufferIdx), "Going out of range on verts"); + + if( aux->fDrawable ) + const_cast(aux->fDrawable->GetSpan(aux->fBaseSpanIdx))->RemoveAuxSpan(aux); + + aux->fDrawable = nil; + aux->fBaseSpanIdx = 0; + } + + delete fDecals[i]; + int newCount = fDecals.GetCount()-1; + if( i < newCount ) + { + memmove(&fDecals[i], &fDecals[i+1], (newCount-i) * sizeof(fDecals[i])); + } + fDecals.SetCount(newCount); +} + +void plDynaDecalMgr::IUpdateDecals(double t) +{ + if( fDisableUpdate ) + return; + + int i; + + for( i = 0; i < fDecals.GetCount(); i++ ) + { + if( fDecals[i]->Age(t, fRampEnd, fDecayStart, fLifeSpan) ) + { + IKillDecal(i); + i--; + } + } + + for( i = 0; i < fAuxSpans.GetCount(); i++ ) + { + if( fAuxSpans[i]->fVLength ) + { + plAuxSpan* aux = fAuxSpans[i]; + aux->fGroup->DirtyVertexBuffer(aux->fVBufferIdx); + } + } +} + +////////////////////////////////////////////////////////////////////////////////// + +void plDynaDecalMgr::ICountIncoming(hsTArray& src, UInt16& numVerts, UInt16& numIdx) const +{ + numVerts = 0; + numIdx = 0; + int j; + for( j = 0; j < src.GetCount(); j++ ) + { + if( src[j].fVerts.GetCount() ) + { + numVerts += src[j].fVerts.GetCount(); + numIdx += src[j].fVerts.GetCount()-2; + } + } + numIdx *= 3; +} + +plDecalVtxFormat* plDynaDecalMgr::IGetBaseVtxPtr(const plAuxSpan* auxSpan) const +{ + plGBufferGroup* grp = auxSpan->fGroup; + plGBufferCell* cell = grp->GetCell(auxSpan->fVBufferIdx, auxSpan->fCellIdx); + + UInt8* ptr = grp->GetVertBufferData(auxSpan->fVBufferIdx); + + ptr += cell->fVtxStart + auxSpan->fCellOffset; + + return (plDecalVtxFormat*)ptr; + +} + +UInt16* plDynaDecalMgr::IGetBaseIdxPtr(const plAuxSpan* auxSpan) const +{ + plGBufferGroup* grp = auxSpan->fGroup; + + return grp->GetIndexBufferData(auxSpan->fIBufferIdx) + auxSpan->fIBufferInit; +} + +hsBool plDynaDecalMgr::IConvertFlatGrid(plAuxSpan* auxSpan, + plDynaDecal* decal, + const plFlatGridMesh& grid) const +{ + plDecalVtxFormat* vtx = IGetBaseVtxPtr(auxSpan); + vtx += decal->fStartVtx; + decal->fVtxBase = vtx; + + hsPoint3* origPos = &auxSpan->fOrigPos[decal->fStartVtx]; + hsPoint3* origUVW = &auxSpan->fOrigUVW[decal->fStartVtx]; + + UInt32 initColor = decal->fFlags & plDynaDecal::kAttenColor + ? 0xff000000 + : 0x00ffffff; + int iv; + for( iv = 0; iv < decal->fNumVerts; iv++ ) + { + *origPos = vtx->fPos = grid.fVerts[iv].fPos; + + vtx->fNorm.Set(0.f, 0.f, 1.f); + + vtx->fDiffuse = initColor; + vtx->fSpecular = 0; + + vtx->fUVW[0] = grid.fVerts[iv].fUVW; + vtx->fUVW[1].Set(0.f, 0.f, 0.f); + *origUVW = grid.fVerts[iv].fUVW; + origUVW->fZ = 1.f; + + vtx++; + origPos++; + origUVW++; + } + + UInt16* idx = IGetBaseIdxPtr(auxSpan); + idx += decal->fStartIdx; + + hsAssert(grid.fIdx.GetCount() == decal->fNumIdx, "Mismatch on dynamic indices"); + + UInt16 base = decal->fStartVtx; + int ii; + for( ii = 0; ii < grid.fIdx.GetCount(); ii++ ) + { + hsAssert(grid.fIdx[ii] + base - decal->fStartVtx < decal->fNumVerts, "Index going out of range"); + hsAssert(grid.fIdx[ii] + base < auxSpan->fIStartIdx + auxSpan->fILength, "Index going out of range."); + + *idx++ = grid.fIdx[ii] + base; + } + + auxSpan->fGroup->DirtyVertexBuffer(auxSpan->fVBufferIdx); + auxSpan->fGroup->DirtyIndexBuffer(auxSpan->fIBufferIdx); + + return true; +} + +void plDynaDecalMgr::ISetDepthFalloff() +{ + const hsScalar totalDepth = fCutter->GetLengthW(); + + // Currently all constants, but these could be set per DecalMgr. + plConst(hsScalar) kMinFeet(3.f); + plConst(hsScalar) kMaxFeet(10.f); + + plConst(hsScalar) kMinDepth(0.25f); + plConst(hsScalar) kMaxDepth(0.75f); + + fMinDepth = kMinFeet / totalDepth; + if( fMinDepth > kMinDepth ) + fMinDepth = kMinDepth; + + fMinDepthRange = 1.f / fMinDepth; + + fMaxDepth = 1.f - (kMaxFeet / totalDepth); + if( fMaxDepth < kMaxDepth ) + fMaxDepth = kMaxDepth; + + fMaxDepthRange = 1.f / (1.f - fMaxDepth); +} + +hsBool plDynaDecalMgr::IConvertPolys(plAuxSpan* auxSpan, + plDynaDecal* decal, + hsTArray& src) +{ + ISetDepthFalloff(); + + if( decal->fFlags & plDynaDecal::kVertexShader ) + return IConvertPolysVS(auxSpan, decal, src); + + if( decal->fFlags & plDynaDecal::kAttenColor ) + return IConvertPolysColor(auxSpan, decal, src); + + return IConvertPolysAlpha(auxSpan, decal, src); +} + +hsBool plDynaDecalMgr::IConvertPolysAlpha(plAuxSpan* auxSpan, + plDynaDecal* decal, + hsTArray& src) +{ + hsBool loU = false; + hsBool hiU = false; + hsBool loV = false; + hsBool hiV = false; + plDecalVtxFormat* vtx = IGetBaseVtxPtr(auxSpan); + vtx += decal->fStartVtx; + decal->fVtxBase = vtx; + + hsPoint3* origPos = &auxSpan->fOrigPos[decal->fStartVtx]; + hsPoint3* origUVW = &auxSpan->fOrigUVW[decal->fStartVtx]; + + const hsVector3 backDir = fCutter->GetBackDir(); + + int iPoly = 0; + int iVert = 0; + int iv; + for( iv = 0; iv < decal->fNumVerts; iv++ ) + { + *origPos = vtx->fPos = src[iPoly].fVerts[iVert].fPos; + + vtx->fNorm = src[iPoly].fVerts[iVert].fNorm; + vtx->fUVW[0] = src[iPoly].fVerts[iVert].fUVW; + + if( vtx->fUVW[0].fX < 0.5f ) + loU = true; + else + hiU = true; + if( vtx->fUVW[0].fY < 0.5f ) + loV = true; + else + hiV = true; + + hsColorRGBA col = src[iPoly].fVerts[iVert].fColor; + + hsScalar depth = vtx->fUVW[0].fZ; + + hsScalar opac = depth < fMinDepth + ? depth * fMinDepthRange + : depth > fMaxDepth + ? (1.f - depth) * fMaxDepthRange + : 1.f; + + hsScalar normOpac = 1.f - vtx->fNorm.InnerProduct(backDir); + opac *= 1.f - normOpac * normOpac; + if( opac < 0 ) + opac = 0; + + if( src[iPoly].fBaseHasAlpha ) + opac *= col.a; + col.a = 0; + + origUVW->fX = vtx->fUVW[0].fX; + origUVW->fY = vtx->fUVW[0].fY; + + origUVW->fZ = opac; + vtx->fUVW[1].Set(0, 0, 0); + + vtx->fDiffuse = col.ToARGB32(); + vtx->fSpecular = 0; + + + if( ++iVert >= src[iPoly].fVerts.GetCount() ) + { + iVert = 0; + iPoly++; + } + vtx++; + origPos++; + origUVW++; + } + hsAssert(vtx <= IGetBaseVtxPtr(auxSpan) + auxSpan->fVBufferLimit, "Vtx pointer gone wild"); + + UInt16* idx = IGetBaseIdxPtr(auxSpan); + idx += decal->fStartIdx; + + UInt16 base = decal->fStartVtx; + int j; + for( j = 0; j < src.GetCount(); j++ ) + { + UInt16 next = base+1; + int k; + for( k = 2; k < src[j].fVerts.GetCount(); k++ ) + { + *idx++ = base; + *idx++ = next++; + *idx++ = next; + } + base = ++next; + } + hsAssert(idx <= auxSpan->fGroup->GetIndexBufferData(auxSpan->fIBufferIdx) + auxSpan->fIBufferLimit, "Index ptr gone wild"); + + auxSpan->fGroup->DirtyVertexBuffer(auxSpan->fVBufferIdx); + auxSpan->fGroup->DirtyIndexBuffer(auxSpan->fIBufferIdx); + + return loU & hiU & loV & hiV; +} + +hsBool plDynaDecalMgr::IConvertPolysColor(plAuxSpan* auxSpan, + plDynaDecal* decal, + hsTArray& src) +{ + hsBool loU = false; + hsBool hiU = false; + hsBool loV = false; + hsBool hiV = false; + plDecalVtxFormat* vtx = IGetBaseVtxPtr(auxSpan); + vtx += decal->fStartVtx; + decal->fVtxBase = vtx; + + hsPoint3* origPos = &auxSpan->fOrigPos[decal->fStartVtx]; + hsPoint3* origUVW = &auxSpan->fOrigUVW[decal->fStartVtx]; + + const hsVector3 backDir = fCutter->GetBackDir(); + int iPoly = 0; + int iVert = 0; + int iv; + for( iv = 0; iv < decal->fNumVerts; iv++ ) + { + *origPos = vtx->fPos = src[iPoly].fVerts[iVert].fPos; + + vtx->fNorm = src[iPoly].fVerts[iVert].fNorm; + vtx->fUVW[0] = src[iPoly].fVerts[iVert].fUVW; + + if( vtx->fUVW[0].fX < 0.5f ) + loU = true; + else + hiU = true; + if( vtx->fUVW[0].fY < 0.5f ) + loV = true; + else + hiV = true; + + hsScalar depth = vtx->fUVW[0].fZ; + hsScalar opac = depth < fMinDepth + ? depth * fMinDepthRange + : depth > fMaxDepth + ? (1.f - depth) * fMaxDepthRange + : 1.f; + + hsScalar normOpac = 1.f - vtx->fNorm.InnerProduct(backDir); + opac *= 1.f - normOpac * normOpac; + if( opac < 0 ) + opac = 0; + + origUVW->fX = vtx->fUVW[0].fX; + origUVW->fY = vtx->fUVW[0].fY; + + origUVW->fZ = opac; + vtx->fUVW[1].Set(0, 0, 0); + + vtx->fDiffuse = 0xff000000; + vtx->fSpecular = 0; + + + if( ++iVert >= src[iPoly].fVerts.GetCount() ) + { + iVert = 0; + iPoly++; + } + vtx++; + origPos++; + origUVW++; + } + hsAssert(vtx <= IGetBaseVtxPtr(auxSpan) + auxSpan->fVBufferLimit, "Vtx pointer gone wild"); + + UInt16* idx = IGetBaseIdxPtr(auxSpan); + idx += decal->fStartIdx; + + UInt16 base = decal->fStartVtx; + int j; + for( j = 0; j < src.GetCount(); j++ ) + { + UInt16 next = base+1; + int k; + for( k = 2; k < src[j].fVerts.GetCount(); k++ ) + { + *idx++ = base; + *idx++ = next++; + *idx++ = next; + } + base = ++next; + } + hsAssert(idx <= auxSpan->fGroup->GetIndexBufferData(auxSpan->fIBufferIdx) + auxSpan->fIBufferLimit, "Index ptr gone wild"); + + auxSpan->fGroup->DirtyVertexBuffer(auxSpan->fVBufferIdx); + auxSpan->fGroup->DirtyIndexBuffer(auxSpan->fIBufferIdx); + + return loU & hiU & loV & hiV; +} + +hsBool plDynaDecalMgr::IConvertPolysVS(plAuxSpan* auxSpan, + plDynaDecal* decal, + hsTArray& src) +{ + hsBool loU = false; + hsBool hiU = false; + hsBool loV = false; + hsBool hiV = false; + plDecalVtxFormat* vtx = IGetBaseVtxPtr(auxSpan); + vtx += decal->fStartVtx; + decal->fVtxBase = vtx; + + hsPoint3* origPos = &auxSpan->fOrigPos[decal->fStartVtx]; + hsPoint3* origUVW = &auxSpan->fOrigUVW[decal->fStartVtx]; + + int iPoly = 0; + int iVert = 0; + int iv; + for( iv = 0; iv < decal->fNumVerts; iv++ ) + { + *origPos = vtx->fPos = src[iPoly].fVerts[iVert].fPos; + + vtx->fNorm = src[iPoly].fVerts[iVert].fNorm; + vtx->fUVW[0] = src[iPoly].fVerts[iVert].fUVW; + + if( vtx->fUVW[0].fX < 0.5f ) + loU = true; + else + hiU = true; + if( vtx->fUVW[0].fY < 0.5f ) + loV = true; + else + hiV = true; + + origUVW->fX = vtx->fUVW[0].fX; + origUVW->fY = vtx->fUVW[0].fY; + + origUVW->fZ = vtx->fUVW[0].fZ = (hsScalar)decal->fBirth; + + vtx->fUVW[1].Set(0, 0, 0); + + const hsColorRGBA& col = src[iPoly].fVerts[iVert].fColor; + vtx->fDiffuse = col.ToARGB32(); + vtx->fSpecular = 0; + + + if( ++iVert >= src[iPoly].fVerts.GetCount() ) + { + iVert = 0; + iPoly++; + } + vtx++; + origPos++; + origUVW++; + } + hsAssert(vtx <= IGetBaseVtxPtr(auxSpan) + auxSpan->fVBufferLimit, "Vtx pointer gone wild"); + + UInt16* idx = IGetBaseIdxPtr(auxSpan); + idx += decal->fStartIdx; + + UInt16 base = decal->fStartVtx; + int j; + for( j = 0; j < src.GetCount(); j++ ) + { + UInt16 next = base+1; + int k; + for( k = 2; k < src[j].fVerts.GetCount(); k++ ) + { + *idx++ = base; + *idx++ = next++; + *idx++ = next; + } + base = ++next; + } + hsAssert(idx <= auxSpan->fGroup->GetIndexBufferData(auxSpan->fIBufferIdx) + auxSpan->fIBufferLimit, "Index ptr gone wild"); + + auxSpan->fGroup->DirtyVertexBuffer(auxSpan->fVBufferIdx); + auxSpan->fGroup->DirtyIndexBuffer(auxSpan->fIBufferIdx); + + return loU & hiU & loV & hiV; +} + +hsBool plDynaDecalMgr::IHitTestPolys(hsTArray& src) const +{ + hsBool loU = false; + hsBool hiU = false; + hsBool loV = false; + hsBool hiV = false; + int iPoly = 0; + int iVert = 0; + while( iPoly < src.GetCount() ) + { + const hsPoint3& uvw = src[iPoly].fVerts[iVert].fUVW; + + if( uvw.fX < 0.5f ) + loU = true; + else + hiU = true; + if( uvw.fY < 0.5f ) + loV = true; + else + hiV = true; + + if( ++iVert >= src[iPoly].fVerts.GetCount() ) + { + iVert = 0; + iPoly++; + } + } + + return loU & hiU & loV & hiV; +} + +hsBool plDynaDecalMgr::IProcessPolys(plDrawableSpans* targ, int iSpan, double t, hsTArray& src) +{ + // Figure out how many verts and idxs are coming in. + UInt16 numVerts, numIdx; + ICountIncoming(src, numVerts, numIdx); + if( !numVerts ) + return false; + + // Find a span to put them in. Either the current span, or a new + // one if it's full up. + plAuxSpan* auxSpan = IGetAuxSpan(targ, iSpan, nil, numVerts, numIdx); + + // If we're full up, just see if we hit anything, but don't + // make any more decals. Might be nice to accelerate decay + // here, since we definitely aren't keeping up. + if( !auxSpan ) + return IHitTestPolys(src); + + // Get a decal to manage this group's aging. + // Update the span to point to enough room. + plDynaDecal* decal = IInitDecal(auxSpan, t, numVerts, numIdx); + + // Convert the polys from src into the accessor tris + return IConvertPolys(auxSpan, decal, src); +} + +hsBool plDynaDecalMgr::IProcessGrid(plDrawableSpans* targ, int iSpan, hsGMaterial* mat, double t, const plFlatGridMesh& grid) +{ + // Find a span to put them in. Either the current span, or a new + // one if it's full up. + plAuxSpan* auxSpan = IGetAuxSpan(targ, iSpan, mat, grid.fVerts.GetCount(), grid.fIdx.GetCount()); + + // If we're full up, just see if we hit anything, but don't + // make any more decals. + if( !auxSpan ) + return IHitTestFlatGrid(grid); + + auxSpan->fFlags |= plAuxSpan::kWorldSpace; + + // Get a decal to manage this group's aging. + // Update the span to point to enough room. + plDynaDecal* decal = IInitDecal(auxSpan, t, grid.fVerts.GetCount(), grid.fIdx.GetCount()); + + // Convert the grid from src into the accessor tris + return IConvertFlatGrid(auxSpan, decal, grid); +} + +hsBool plDynaDecalMgr::IHitTestFlatGrid(const plFlatGridMesh& grid) const +{ + return true; +} + +////////////////////////////////////////////////////////////////////////////////// + +hsBool plDynaDecalMgr::ICutoutGrid(plDrawableSpans* drawable, int iSpan, hsGMaterial* mat, double secs) +{ + static plFlatGridMesh grid; + grid.Reset(); + + int nWid = int(fCutter->GetLengthU() / fGridSizeU); + int nLen = int(fCutter->GetLengthV() / fGridSizeV); + + fCutter->CutoutGrid(nWid, nLen, grid); + + return IProcessGrid(drawable, iSpan, mat, secs, grid); +} + +hsBool plDynaDecalMgr::ICutoutObject(plSceneObject* so, double secs) +{ + if( fDisableAccumulate ) + return false; + + hsBool retVal = false; + + if( !so ) + return retVal; + + const plDrawInterface* di = so->GetDrawInterface(); + if( !di ) + return retVal; + + plProfile_BeginTiming(Total); + int numGot = 0; + int j; + for( j = 0; j < di->GetNumDrawables(); j++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(j)); + // Nil dr - it hasn't loaded yet or something. + if( dr ) + { + plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex(j)); + if( !diIndex.IsMatrixOnly() ) + { + int k; + for( k = 0; k < diIndex.GetCount(); k++ ) + { + const plSpan* span = dr->GetSpan(diIndex[k]); + if( kVolumeCulled != fCutter->GetIsect().Test(span->fWorldBounds) ) + { + plAccessSpan src; + plAccessGeometry::Instance()->OpenRO(dr, diIndex[k], src); + + static hsTArray dst; + dst.SetCount(0); + + plProfile_BeginTiming(Cutter); + fCutter->Cutout(src, dst); + plProfile_EndTiming(Cutter); + + plProfile_BeginTiming(Process); + if( IProcessPolys(dr, diIndex[k], secs, dst) ) + { + plProfile_BeginTiming(Callback); + if( src.HasWaterHeight() ) + ICutoutCallback(dst, true, src.GetWaterHeight()); + else + ICutoutCallback(dst); + plProfile_EndTiming(Callback); + + retVal = true; + } + plProfile_EndTiming(Process); + + plAccessGeometry::Instance()->Close(src); + } + } + } + } + } + plProfile_EndTiming(Total); + return retVal; +} + +hsBool plDynaDecalMgr::ICutoutList(hsTArray& drawVis, double secs) +{ + if( fDisableAccumulate ) + return false; + + hsBool retVal = false; + + if( !drawVis.GetCount() ) + return retVal; + + hsTArray src; + + int numSpan = 0; + int iDraw; + for( iDraw = 0; iDraw < drawVis.GetCount(); iDraw++ ) + numSpan += drawVis[iDraw].fVisList.GetCount(); + + src.SetCount(numSpan); + + int i; + + iDraw = 0; + int iSpan = 0; + for( i = 0; i < numSpan; i++ ) + { + static hsTArray dst; + dst.SetCount(0); + + plAccessGeometry::Instance()->OpenRO(drawVis[iDraw].fDrawable, drawVis[iDraw].fVisList[iSpan], src[i]); + + fCutter->Cutout(src[i], dst); + + if( IProcessPolys((plDrawableSpans*)drawVis[iDraw].fDrawable, drawVis[iDraw].fVisList[iSpan], secs, dst) ) + retVal = true; + + plAccessGeometry::Instance()->Close(src[i]); + + if( ++iSpan >= drawVis[iDraw].fVisList.GetCount() ) + { + iDraw++; + iSpan = 0; + } + } + return retVal; +} + +hsBool plDynaDecalMgr::ICutoutTargets(double secs) +{ + if( fDisableAccumulate ) + return false; + + hsBool retVal = false; + + int i; + for( i = 0; i < fTargets.GetCount(); i++ ) + { + if( fTargets[i] ) + retVal |= ICutoutObject(fTargets[i], secs); + } + return retVal; +} + +////////////////////////////////////////////////////////////////////////////////// + +#include "../plGImage/plBumpMapGen.h" + +hsGMaterial* plDynaDecalMgr::IConvertToEnvMap(hsGMaterial* mat, plBitmap* envMap) +{ + if( !mat || !envMap ) + return nil; + + plLayerInterface* oldLay = mat->GetLayer(0); + + plMipmap* oldMip = plMipmap::ConvertNoRef(oldLay->GetTexture()); + if( !oldMip ) + return mat; + oldMip->SetCurrLevel(0); + + hsGMaterial* newMat = TRACKED_NEW hsGMaterial; + char buff[256]; + sprintf(buff, "%s_%s", GetKey()->GetName(), "EnvMat"); + hsgResMgr::ResMgr()->NewKey(buff, newMat, GetKey()->GetUoid().GetLocation()); + + static plTweak kSmooth(1.f); + plMipmap* bumpMap = plBumpMapGen::QikNormalMap(nil, oldMip, 0xffffffff, plBumpMapGen::kBubbleTest, kSmooth); +// plMipmap* bumpMap = plBumpMapGen::QikNormalMap(nil, oldMip, 0xffffffff, plBumpMapGen::kNormalize, kSmooth); +// plMipmap* bumpMap = plBumpMapGen::QikNormalMap(nil, oldMip, 0xffffffff, 0, 0); + sprintf(buff, "%s_%s", GetKey()->GetName(), "BumpMap"); + hsgResMgr::ResMgr()->NewKey(buff, bumpMap, GetKey()->GetUoid().GetLocation()); + + bumpMap->SetFlags(bumpMap->GetFlags() | plMipmap::kBumpEnvMap | plMipmap::kForceNonCompressed); + + plLayer* bumpLay = TRACKED_NEW plLayer; + sprintf(buff, "%s_%s_%d", GetKey()->GetName(), "BumpMap", 0); + hsgResMgr::ResMgr()->NewKey(buff, bumpLay, GetKey()->GetUoid().GetLocation()); + + bumpLay->SetState(oldLay->GetState()); + bumpLay->SetBlendFlags(hsGMatState::kBlendAdd | hsGMatState::kBlendEnvBumpNext); + bumpLay->SetTransform(hsMatrix44::IdentityMatrix()); + bumpLay->SetUVWSrc(0); + + hsMatrix44 bumpEnvXfm; + bumpEnvXfm.Reset(); + bumpLay->SetBumpEnvMatrix(bumpEnvXfm); + + bumpLay->SetAmbientColor(oldLay->GetAmbientColor()); + bumpLay->SetRuntimeColor(oldLay->GetRuntimeColor()); + bumpLay->SetOpacity(1.f); + + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(bumpLay->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture); + hsgResMgr::ResMgr()->SendRef(bumpMap->GetKey(), refMsg, plRefFlags::kActiveRef); + + newMat->AddLayerViaNotify(bumpLay); + + plLayer* envLay = TRACKED_NEW plLayer; + sprintf(buff, "%s_%s_%d", GetKey()->GetName(), "EnvMap", 0); + hsgResMgr::ResMgr()->NewKey(buff, envLay, GetKey()->GetUoid().GetLocation()); + + envLay->SetBlendFlags(hsGMatState::kBlendMult); + envLay->SetClampFlags(0); + envLay->SetShadeFlags(bumpLay->GetShadeFlags()); + envLay->SetZFlags(hsGMatState::kZNoZWrite); + envLay->SetMiscFlags(hsGMatState::kMiscUseReflectionXform); + envLay->SetUVWSrc(plLayer::kUVWReflect); + + envLay->SetAmbientColor(oldLay->GetAmbientColor()); + envLay->SetRuntimeColor(oldLay->GetRuntimeColor()); + envLay->SetOpacity(1.f); + + refMsg = TRACKED_NEW plLayRefMsg(envLay->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture); + hsgResMgr::ResMgr()->SendRef(envMap->GetKey(), refMsg, plRefFlags::kActiveRef); + + newMat->AddLayerViaNotify(envLay); + + return newMat; +} + +void plDynaDecalMgr::ConvertToEnvMap(plBitmap* envMap) +{ + hsGMaterial* newPreShade = IConvertToEnvMap(fMatPreShade, envMap); + if( newPreShade && (newPreShade != fMatPreShade) ) + hsgResMgr::ResMgr()->SendRef(newPreShade->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefMatPreShade), plRefFlags::kActiveRef); + + hsGMaterial* newRTShade = IConvertToEnvMap(fMatRTShade, envMap); + if( newRTShade && (newRTShade != fMatRTShade) ) + hsgResMgr::ResMgr()->SendRef(newRTShade->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefMatRTShade), plRefFlags::kActiveRef); +} + +const plMipmap* plDynaDecalMgr::GetMipmap() const +{ + plMipmap* mip = nil; + if( fMatRTShade ) + mip = plMipmap::ConvertNoRef(fMatRTShade->GetLayer(0)->GetTexture()); + + if( !mip && fMatPreShade ) + mip = plMipmap::ConvertNoRef(fMatPreShade->GetLayer(0)->GetTexture()); + + if( mip ) + mip->SetCurrLevel(0); + + return mip; +} + +hsVector3 plDynaDecalMgr::IRandomUp(hsVector3 dir) const +{ + hsVector3 retVal; + + // Okay, we want a pretty random vector perpindicular to the + // direction of fire. So we take that direction and cross product + // it with the 3 world axes. Now we have 3 random vectors perpindicular + // to the dir (but not necessarily each other). Note that some (but not all) + // of these vectors may be zero length, it the direction happens to line up + // with an axis. We scale each by a random amount, sum them up, and since + // they are all perpindicular to dir, the weighted sum is too. + // Only problem here is that our random scalings might wind us up with + // a zero vector. Unlikely, which means almost certain to happen. So + // we keep trying till we get a non-zero vector. + hsScalar lenSq(-1.f); + do { + hsScalar ranXx = sRand.RandMinusOneToOne(); + hsScalar ranXy = sRand.RandMinusOneToOne(); + hsScalar ranXz = sRand.RandMinusOneToOne(); + retVal.fX = -dir.fZ * ranXy + dir.fY * ranXz; + retVal.fY = dir.fZ * ranXx + -dir.fX * ranXz; + retVal.fZ = -dir.fY * ranXx + dir.fX * ranXy; + + lenSq = retVal.MagnitudeSquared(); + + } while( lenSq <= 0 ); + + retVal *= hsFastMath::InvSqrtAppr(lenSq); + + return retVal; +} + +hsVector3 plDynaDecalMgr::IReflectDir(hsVector3 dir) const +{ + hsFastMath::NormalizeAppr(dir); // it's been interpolated. + + // parm of zero returns unaffected dir, parm of one returns cutter direction reflected about dir. + // Here's the original math. + // Here N is dir, B is -cutter back direction (incoming), k is parm. + // Reflection R of B is 2*(N dot B)*N + B + // Interpolating gives K*R + (1-K)*N + // Simplifying gives (2*K*(N dot B) + (1-K)) * N + K*B + // Or something. + + plConst(hsScalar) parm(0.5f); + + hsVector3 b = -fCutter->GetBackDir(); + + hsScalar t = dir.InnerProduct(b); + t *= -2.f * parm; + t += (1.f - parm); + + hsVector3 ret = dir * t; + + ret += b * parm; + + return ret; +} + +hsMatrix44 plDynaDecalMgr::IL2WFromHit(hsPoint3 pos, hsVector3 dir) const +{ + dir = IReflectDir(dir); + + // Negate the firing direction before constructing our psys l2w, because + // particles fire in the negative Z direction. + dir = -dir; + + hsVector3 up = IRandomUp(dir); + hsVector3 acc = up % dir; + + hsMatrix44 l2w; + l2w.Reset(); + l2w.fMap[0][0] = acc[0]; + l2w.fMap[1][0] = acc[1]; + l2w.fMap[2][0] = acc[2]; + + l2w.fMap[0][1] = up[0]; + l2w.fMap[1][1] = up[1]; + l2w.fMap[2][1] = up[2]; + + l2w.fMap[0][2] = dir[0]; + l2w.fMap[1][2] = dir[1]; + l2w.fMap[2][2] = dir[2]; + + l2w.fMap[0][3] = pos[0]; + l2w.fMap[1][3] = pos[1]; + l2w.fMap[2][3] = pos[2]; + + l2w.NotIdentity(); + + return l2w; +} + +void plDynaDecalMgr::ICutoutCallback(const hsTArray& cutouts, hsBool hasWaterHeight, hsScalar waterHeight) +{ + hsTArray hits; + + if( (fPartyTime > 0) && fParticles.GetCount() ) + { + if( hasWaterHeight ) + fCutter->FindHitPointsConstHeight(cutouts, hits, waterHeight); + else + fCutter->FindHitPoints(cutouts, hits); + + int i; + for( i = 0; i < hits.GetCount(); i++ ) + { + int j; + for( j = 0; j < fParticles.GetCount(); j++ ) + { + plParticleEmitter* emit = fParticles[j]->GetAvailEmitter(); + if( emit ) + { + hsMatrix44 l2w = IL2WFromHit(hits[i].fPos, hits[i].fNorm); + + emit->OverrideLocalToWorld(l2w); + emit->SetTimeToLive(fPartyTime); + } + } + } + } +} + +void plDynaDecalMgr::IGetParticles() +{ + if( fParticles.GetCount() != fPartyObjects.GetCount() ) + { + int i; + for( i = 0; i < fPartyObjects.GetCount(); i++ ) + { + const plParticleSystem *sys = plParticleSystem::ConvertNoRef(fPartyObjects[i]->GetModifierByType(plParticleSystem::Index())); + // const_cast here is just to see if it's in our list, make Find happy. + if( sys && (fParticles.kMissingIndex == fParticles.Find(const_cast(sys))) ) + { + hsgResMgr::ResMgr()->AddViaNotify(sys->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefParticles), plRefFlags::kPassiveRef); + } + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecalMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecalMgr.h new file mode 100644 index 00000000..e68a0f78 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaDecalMgr.h @@ -0,0 +1,276 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynaDecalMgr_inc +#define plDynaDecalMgr_inc + +#include "../pnNetCommon/plSynchedObject.h" +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "hsMatrix44.h" +#include "hsStlUtils.h" + + +class plParticleSystem; + +class plPrintShape; +class plDynaDecalEnableMsg; +class plDynaDecal; +class plDrawableSpans; +class plGBufferGroup; +class plIcicle; +class hsGMaterial; +class plBitmap; +class plMipmap; +class plSceneObject; +class plArmatureMod; + +class hsStream; +class hsResMgr; +class plMessage; + +class plCutter; +class plCutoutPoly; +class plFlatGridMesh; + +class plDrawVisList; +class plRenderLevel; + +class plAccessSpan; +class plAuxSpan; +class plDecalVtxFormat; + +class plPipeline; + +// plDynaDecalInfo - information we store specific to what we've +// done about a specific avatar part or scene object. +class plDynaDecalInfo +{ +public: + enum + { + kNone = 0x0, + kImmersed = 0x1, + kActive = 0x2 + }; + + plKey fKey; + + double fLastTime; + hsPoint3 fLastPos; + double fWetTime; + hsScalar fWetLength; + UInt32 fFlags; + + plDynaDecalInfo& Init(const plKey& key); +}; + +typedef std::map< UInt32, plDynaDecalInfo, std::less > plDynaDecalMap; + +// plDynaDecalMgr +// Primary responsibilities: +// Allocation of adequate buffer space in plGBufferGroup +// Setup of decal materials +// Allocation of auxSpans +// Receive lists of polys, translate into drawable tris +// Create DynaDecals and destroy them when they expire. +// Assign vertex and index subsets to DynaDecals +// Call Update on DynaDecals +class plDynaDecalMgr : public plSynchedObject +{ +public: + enum DynaRefType + { + kRefMatPreShade, + kRefMatRTShade, + kRefTarget, + kRefAvatar, + kRefPartyObject, + kRefParticles, + kRefNextAvailable = 10 + }; +protected: + static hsBool fDisableAccumulate; + static hsBool fDisableUpdate; + + plDynaDecalMap fDecalMap; + + hsTArray fDecals; + + hsTArray fGroups; + + plCutter* fCutter; + + hsTArray fAuxSpans; + + hsGMaterial* fMatPreShade; + hsGMaterial* fMatRTShade; + + hsTArray fTargets; + + hsTArray fPartyObjects; + hsTArray fParticles; + + hsScalar fPartyTime; + + UInt16 fMaxNumVerts; + UInt16 fMaxNumIdx; + + UInt32 fWaitOnEnable; + + hsScalar fWetLength; + hsScalar fRampEnd; + hsScalar fDecayStart; + hsScalar fLifeSpan; + hsScalar fIntensity; + + hsScalar fGridSizeU; + hsScalar fGridSizeV; + + hsVector3 fScale; + + // some temp calculated stuff + hsScalar fInitAtten; + // These 4 are in normalized units [0..1], not feet. + hsScalar fMinDepth; + hsScalar fMinDepthRange; + hsScalar fMaxDepth; + hsScalar fMaxDepthRange; + + hsTArray fPartIDs; + hsTArray fNotifies; + + const plPrintShape* IGetPrintShape(const plKey& objKey) const; + const plPrintShape* IGetPrintShape(plArmatureMod* avMod, UInt32 id) const; + + virtual hsBool IHandleEnableMsg(const plDynaDecalEnableMsg* enaMsg); + void INotifyActive(plDynaDecalInfo& info, const plKey& armKey, UInt32 id) const; + void INotifyInactive(plDynaDecalInfo& info, const plKey& armKey, UInt32 id) const; + hsBool IWetParts(const plDynaDecalEnableMsg* enaMsg); + hsBool IWetPart(UInt32 id, const plDynaDecalEnableMsg* enaMsg); + void IWetInfo(plDynaDecalInfo& info, const plDynaDecalEnableMsg* enaMsg) const; + hsScalar IHowWet(plDynaDecalInfo& info, double t) const; + plDynaDecalInfo& IGetDecalInfo(UInt32 id, const plKey& key); + void IRemoveDecalInfo(UInt32 id); + void IRemoveDecalInfos(const plKey& key); + + hsGMaterial* ISetAuxMaterial(plAuxSpan* aux, hsGMaterial* mat, hsBool rtLit); + void IAllocAuxSpan(plAuxSpan* aux, UInt32 maxNumVerts, UInt32 maxNumIdx); + plAuxSpan* IGetAuxSpan(plDrawableSpans* targ, int iSpan, hsGMaterial* mat, UInt16 numVerts, UInt16 numIdx); + hsBool IMakeAuxRefs(plPipeline* pipe); + + UInt16* IGetBaseIdxPtr(const plAuxSpan* auxSpan) const; + plDecalVtxFormat* IGetBaseVtxPtr(const plAuxSpan* auxSpan) const; + + virtual int INewDecal() = 0; + plDynaDecal* IInitDecal(plAuxSpan* aux, double t, UInt16 numVerts, UInt16 numIdx); + void IKillDecal(int i); + void IUpdateDecals(double t); + + void ICountIncoming(hsTArray& src, UInt16& numVerts, UInt16& numIdx) const; + hsBool IConvertPolysColor(plAuxSpan* auxSpan, plDynaDecal* decal, hsTArray& src); + hsBool IConvertPolysAlpha(plAuxSpan* auxSpan, plDynaDecal* decal, hsTArray& src); + hsBool IConvertPolysVS(plAuxSpan* auxSpan, plDynaDecal* decal, hsTArray& src); + hsBool IConvertPolys(plAuxSpan* auxSpan, plDynaDecal* decal, hsTArray& src); + hsBool IProcessPolys(plDrawableSpans* targ, int iSpan, double t, hsTArray& src); + hsBool IHitTestPolys(hsTArray& src) const; + + hsBool IProcessGrid(plDrawableSpans* targ, int iSpan, hsGMaterial* mat, double t, const plFlatGridMesh& grid); + hsBool IConvertFlatGrid(plAuxSpan* auxSpan, plDynaDecal* decal, const plFlatGridMesh& grid) const; + hsBool ICutoutGrid(plDrawableSpans* drawable, int iSpan, hsGMaterial* mat, double secs); + hsBool IHitTestFlatGrid(const plFlatGridMesh& grid) const; + + hsBool ICutoutList(hsTArray& drawVis, double secs); + hsBool ICutoutObject(plSceneObject* so, double secs); + hsBool ICutoutTargets(double secs); + + void ISetDepthFalloff(); // Sets from current cutter settings. + + virtual void ICutoutCallback(const hsTArray& cutouts, hsBool hasWaterHeight=false, hsScalar waterHeight=0.f); + + hsGMaterial* IConvertToEnvMap(hsGMaterial* mat, plBitmap* envMap); + + virtual void SetKey(plKey k); + + hsVector3 IReflectDir(hsVector3 dir) const; + hsMatrix44 IL2WFromHit(hsPoint3 pos, hsVector3 dir) const; + hsVector3 IRandomUp(hsVector3 dir) const; + void IGetParticles(); + +public: + + plDynaDecalMgr(); + virtual ~plDynaDecalMgr(); + + CLASSNAME_REGISTER( plDynaDecalMgr ); + GETINTERFACE_ANY( plDynaDecalMgr, plSynchedObject ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + // This is public, because you need to call it after creating + // a DynaDecalMgr on the fly. It's normally called on Read(). + void InitAuxSpans(); + + void SetScale(const hsVector3& v) { fScale = v; } + const hsVector3& GetScale() const { return fScale; } + + void SetWaitOnEnable(hsBool on) { fWaitOnEnable = on; } + hsBool GetWaitOnEnable() const { return fWaitOnEnable; } + + void SetWetLength(hsScalar f) { fWetLength = f; } + void SetRampEnd(hsScalar f) { fRampEnd = f; } + void SetDecayStart(hsScalar f) { fDecayStart = f; } + void SetLifeSpan(hsScalar f) { fLifeSpan = f; } + void SetIntensity(hsScalar f) { fIntensity = f; } + hsScalar GetWetLength() const { return fWetLength; } + hsScalar GetRampEnd() const { return fRampEnd; } + hsScalar GetDecayStart() const { return fDecayStart; } + hsScalar GetLifeSpan() const { return fLifeSpan; } + hsScalar GetIntensity() const { return fIntensity; } + + void SetPartyTime(hsScalar secs) { fPartyTime = secs; } // Duration of particle spewage + hsScalar GetPartyTime() const { return fPartyTime; } + + void ConvertToEnvMap(plBitmap* envMap); + const plMipmap* GetMipmap() const; + + void AddNotify(const plKey& k) { fNotifies.Append(k); } + UInt32 GetNumNotifies() const { return fNotifies.GetCount(); } + const plKey& GetNotify(int i) const { return fNotifies[i]; } + + static void SetDisableAccumulate(hsBool on) { fDisableAccumulate = on; } + static void ToggleDisableAccumulate() { fDisableAccumulate = !fDisableAccumulate; } + static hsBool GetDisableAccumulate() { return fDisableAccumulate; } + + static void SetDisableUpdate(hsBool on) { fDisableUpdate = on; } + static void ToggleDisableUpdate() { fDisableUpdate = !fDisableUpdate; } + static hsBool GetDisableUpdate() { return fDisableUpdate; } +}; + +#endif // plDynaDecalMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaFootMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaFootMgr.cpp new file mode 100644 index 00000000..4e9943e0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaFootMgr.cpp @@ -0,0 +1,155 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plDynaFootMgr.h" +#include "plDynaDecal.h" + +#include "plCutter.h" + +#include "plPrintShape.h" + +#include "plgDispatch.h" + +#include "hsStream.h" +#include "hsResMgr.h" +#include "hsTimer.h" + +#include "../plMessage/plDynaDecalEnableMsg.h" + +#include "../plMessage/plAvatarFootMsg.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvBrainHuman.h" + +#include "../plMath/plRandom.h" +static plRandom sRand; + +static const UInt32 kNumPrintIDs = 2; +static const UInt32 kPrintIDs[kNumPrintIDs] = +{ + plAvBrainHuman::RFootPrint, + plAvBrainHuman::LFootPrint +}; + + +int plDynaFootMgr::INewDecal() +{ + int idx = fDecals.GetCount(); + fDecals.Append(TRACKED_NEW plDynaSplot()); + + return idx; +} + +plDynaFootMgr::plDynaFootMgr() +{ + fPartIDs.SetCount(kNumPrintIDs); + int i; + for( i = 0; i < kNumPrintIDs; i++ ) + fPartIDs[i] = kPrintIDs[i]; +} + +plDynaFootMgr::~plDynaFootMgr() +{ +} + +void plDynaFootMgr::Read(hsStream* stream, hsResMgr* mgr) +{ + plDynaDecalMgr::Read(stream, mgr); + + plgDispatch::Dispatch()->RegisterForExactType(plAvatarFootMsg::Index(), GetKey()); +} + +void plDynaFootMgr::Write(hsStream* stream, hsResMgr* mgr) +{ + plDynaDecalMgr::Write(stream, mgr); +} + + +hsBool plDynaFootMgr::MsgReceive(plMessage* msg) +{ + plAvatarFootMsg* footMsg = plAvatarFootMsg::ConvertNoRef(msg); + if( footMsg ) + { + UInt32 id = footMsg->IsLeft() ? plAvBrainHuman::LFootPrint : plAvBrainHuman::RFootPrint; + + plArmatureMod* armMod = footMsg->GetArmature(); + const plPrintShape* shape = IGetPrintShape(armMod, id); + if( shape ) + { + plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey()); + if( IPrintFromShape(shape, footMsg->IsLeft()) ) + { + INotifyActive(info, armMod->GetKey(), id); + } + else + { + INotifyInactive(info, armMod->GetKey(), id); + } + } + + return true; + } + + return plDynaDecalMgr::MsgReceive(msg); +} + +hsBool plDynaFootMgr::IPrintFromShape(const plPrintShape* shape, hsBool flip) +{ + hsBool retVal = false; + + if( shape ) + { + plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey()); + + double secs = hsTimer::GetSysSeconds(); + hsScalar wetness = IHowWet(info, secs); + fInitAtten = wetness; + + if( wetness <= 0 ) + return true; + + hsMatrix44 shapeL2W = shape->GetOwner()->GetLocalToWorld(); + hsPoint3 newPos = shapeL2W.GetTranslate(); + hsVector3 newDir = shapeL2W.GetAxis(hsMatrix44::kView); + hsVector3 newUp(0.f, 0.f, 1.f); + + hsVector3 size(shape->GetWidth() * fScale.fX, shape->GetLength() * fScale.fY, shape->GetHeight() * fScale.fZ * 2.f); + fCutter->SetLength(size); + fCutter->Set(newPos, newDir, newUp, flip); + + // Should this be moved inside the if( ICutout() ) clause? I think so. Probably doesn't + // matter for foot prints, but it seems more correct, since fLastPos/fLastTime is the + // last time and position we actually dropped a print, not tried to. + info.fLastPos = newPos; + info.fLastTime = secs; + + if( ICutoutTargets(secs) ) + { + retVal = true; + } + } + return retVal; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaFootMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaFootMgr.h new file mode 100644 index 00000000..9d6fa8a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaFootMgr.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynaFootMgr_inc +#define plDynaFootMgr_inc + +#include "plDynaDecalMgr.h" + +class plAvatarFootMsg; + +class plDynaFootMgr : public plDynaDecalMgr +{ +protected: + virtual hsBool IPrintFromShape(const plPrintShape* shape, hsBool flip); + + virtual int INewDecal(); +public: + plDynaFootMgr(); + virtual ~plDynaFootMgr(); + + CLASSNAME_REGISTER( plDynaFootMgr ); + GETINTERFACE_ANY( plDynaFootMgr, plDynaDecalMgr ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + +}; + +#endif // plDynaFootMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaPuddleMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaPuddleMgr.cpp new file mode 100644 index 00000000..fb43749d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaPuddleMgr.cpp @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plDynaPuddleMgr.h" + +#include "plPrintShape.h" + +#include "plgDispatch.h" + +#include "hsStream.h" +#include "hsResMgr.h" + +#include "../plMessage/plAvatarFootMsg.h" + +#include "../plAvatar/plAvBrainHuman.h" +#include "../plAvatar/plArmatureMod.h" + +static const UInt32 kNumPrintIDs = 2; +static const UInt32 kPrintIDs[kNumPrintIDs] = +{ + plAvBrainHuman::RFootPrint, + plAvBrainHuman::LFootPrint +}; + + +plDynaPuddleMgr::plDynaPuddleMgr() +{ + fPartIDs.SetCount(kNumPrintIDs); + int i; + for( i = 0; i < kNumPrintIDs; i++ ) + fPartIDs[i] = kPrintIDs[i]; +} + +plDynaPuddleMgr::~plDynaPuddleMgr() +{ +} + +void plDynaPuddleMgr::Read(hsStream* stream, hsResMgr* mgr) +{ + plDynaRippleMgr::Read(stream, mgr); + + plgDispatch::Dispatch()->RegisterForExactType(plAvatarFootMsg::Index(), GetKey()); +} + +hsBool plDynaPuddleMgr::MsgReceive(plMessage* msg) +{ + plAvatarFootMsg* footMsg = plAvatarFootMsg::ConvertNoRef(msg); + if( footMsg ) + { + int i; + for( i = 0; i < fPartIDs.GetCount(); i++ ) + { + plArmatureMod* armMod = footMsg->GetArmature(); + const plPrintShape* shape = IGetPrintShape(armMod, fPartIDs[i]); + if( shape ) + { + plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey()); + if( IRippleFromShape(shape, true) ) + { + INotifyActive(info, armMod->GetKey(), fPartIDs[i]); + } + else + { + INotifyInactive(info, armMod->GetKey(), fPartIDs[i]); + } + } + } + return true; + } + + return plDynaRippleMgr::MsgReceive(msg); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaPuddleMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaPuddleMgr.h new file mode 100644 index 00000000..6b26d4da --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaPuddleMgr.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynaPuddleMgr_inc +#define plDynaPuddleMgr_inc + +#include "plDynaRippleMgr.h" + +class plKey; + +class plDynaPuddleMgr : public plDynaRippleMgr +{ +protected: + +public: + plDynaPuddleMgr(); + virtual ~plDynaPuddleMgr(); + + CLASSNAME_REGISTER( plDynaPuddleMgr ); + GETINTERFACE_ANY( plDynaPuddleMgr, plDynaRippleMgr ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + +}; + +#endif // plDynaPuddleMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgr.cpp new file mode 100644 index 00000000..8e2c761f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgr.cpp @@ -0,0 +1,233 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plDynaRippleMgr.h" +#include "plDynaDecal.h" + +#include "plPrintShape.h" + +#include "plCutter.h" + +#include "plgDispatch.h" + +#include "hsStream.h" +#include "hsResMgr.h" +#include "hsTimer.h" + +#include "../plMessage/plDynaDecalEnableMsg.h" +#include "../plMessage/plRippleShapeMsg.h" + +#include "../plMessage/plAvatarMsg.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plAvatar/plArmatureMod.h" + +#include "../plMath/plRandom.h" +static plRandom sRand; + +#include "plTweak.h" + +static const UInt32 kNumPrintIDs = 5; +static const UInt32 kPrintIDs[kNumPrintIDs] = +{ + plAvBrainHuman::TrunkPrint, + plAvBrainHuman::LHandPrint, + plAvBrainHuman::RHandPrint, + plAvBrainHuman::LFootPrint, + plAvBrainHuman::RFootPrint +}; + + +int plDynaRippleMgr::INewDecal() +{ + int idx = fDecals.GetCount(); + +#if 1 + plDynaRipple* rip = TRACKED_NEW plDynaRipple(); + rip->fC1U = fInitUVW.fX; + rip->fC2U = (fInitUVW.fX - fFinalUVW.fX) / (fLifeSpan * fFinalUVW.fX); + + rip->fC1V = fInitUVW.fY; + rip->fC2V = (fInitUVW.fY - fFinalUVW.fY) / (fLifeSpan * fFinalUVW.fY); + + fDecals.Append(rip); +#else + plDynaWave* wave = TRACKED_NEW plDynaWave(); + static hsScalar kDefScrollRate = 0.1f; + wave->fScrollRate = kDefScrollRate; + fDecals.Append(wave); +#endif + + return idx; +} + +plDynaRippleMgr::plDynaRippleMgr() +: + fInitUVW(1.f,1.f,1.f), + fFinalUVW(1.f,1.f,1.f) +{ + fPartIDs.SetCount(kNumPrintIDs); + int i; + for( i = 0; i < kNumPrintIDs; i++ ) + fPartIDs[i] = kPrintIDs[i]; +} + +plDynaRippleMgr::~plDynaRippleMgr() +{ +} + +void plDynaRippleMgr::Read(hsStream* stream, hsResMgr* mgr) +{ + plDynaDecalMgr::Read(stream, mgr); + + fInitUVW.Read(stream); + fFinalUVW.Read(stream); + +// plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); // ###HACKTEST +// plgDispatch::Dispatch()->RegisterForExactType(plListenerMsg::Index(), GetKey()); // ###HACKTEST + plgDispatch::Dispatch()->RegisterForExactType(plArmatureUpdateMsg::Index(), GetKey()); +} + +void plDynaRippleMgr::Write(hsStream* stream, hsResMgr* mgr) +{ + plDynaDecalMgr::Write(stream, mgr); + + fInitUVW.Write(stream); + fFinalUVW.Write(stream); +} + +hsBool plDynaRippleMgr::MsgReceive(plMessage* msg) +{ + plArmatureUpdateMsg* armMsg = plArmatureUpdateMsg::ConvertNoRef(msg); + if( armMsg && !armMsg->IsInvis()) + { + int i; + for( i = 0; i < fPartIDs.GetCount(); i++ ) + { + const plPrintShape* shape = IGetPrintShape(armMsg->fArmature, fPartIDs[i]); + if( shape ) + { + plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey()); + if( IRippleFromShape(shape, false) ) + { + INotifyActive(info, armMsg->fArmature->GetKey(), fPartIDs[i]); + } + else + { + INotifyInactive(info, armMsg->fArmature->GetKey(), fPartIDs[i]); + } + } + } + return true; + } + plRippleShapeMsg* shapeMsg = plRippleShapeMsg::ConvertNoRef(msg); + if( shapeMsg ) + { + const plPrintShape* shape = shapeMsg->GetShape(); + if( shape ) + { + // Note we don't care about the return value here, because we only send notifies + // for avatar based ripples. + IRippleFromShape(shape); + } + return true; + } + + return plDynaDecalMgr::MsgReceive(msg); +} + +hsBool plDynaRippleMgr::IRippleFromShape(const plPrintShape* shape, hsBool force) +{ + if( !shape ) + return false; + + hsBool retVal = false; + + plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey()); + + const hsMatrix44& shapeL2W = shape->GetOwner()->GetLocalToWorld(); + + plConst(hsScalar) kMinDist(2.0f); + plConst(hsScalar) kMinTime(1.5f); + double t = hsTimer::GetSysSeconds(); + hsScalar dt = hsScalar(t - info.fLastTime) * sRand.RandZeroToOne(); + hsBool longEnough = (dt >= kMinTime); + hsBool farEnough = (hsVector3(&info.fLastPos, &shapeL2W.GetTranslate()).Magnitude() > kMinDist); + if( force || longEnough || farEnough ) + { + hsPoint3 pos = shapeL2W.GetTranslate(); + + // We'll perturb the position a little so it doesn't look quite so regular, + // but we perturb it more if we're just standing still + hsVector3 randPert(sRand.RandMinusOneToOne(), sRand.RandMinusOneToOne(), 0); + randPert.Normalize(); + if( !farEnough ) + { + plConst(hsScalar) kRandPert = 0.5f; + randPert *= kRandPert; + } + else + { + plConst(hsScalar) kRandPert = 0.15f; + randPert *= kRandPert; + } + pos += randPert; + + hsVector3 dir(0.f, 1.f, 0.f); + hsVector3 up(0.f, 0.f, 1.f); + + plConst(hsScalar) kHeightScale = 1.f; + pos.fZ += (shape->GetHeight() * fScale.fZ * kHeightScale) * 0.25f; + + hsScalar wid = hsMaximum(shape->GetWidth(), shape->GetLength()); + hsVector3 size(wid * fScale.fX, wid * fScale.fY, shape->GetHeight() * fScale.fZ * kHeightScale); + fCutter->SetLength(size); + fCutter->Set(pos, dir, up); + + + hsBool hit = ICutoutTargets(t); + if( hit ) + { + retVal = true; + } + else + { + retVal = false; // No-effect else just for break points. + } + // This isn't ideal, but it's a quick fix. ICutoutTargets returns true if the center + // of our cutter hit a face, which is what we want for notifies. But here we want to + // know whether any decal faces were generated at all. At some point, I'll have ICutoutTargets + // return a bit field, with separate bits for different interesting cases. But for now, we'll + // just keep track of when we last TRIED to throw down a decal. That'll get rid of the + // current problem of tons of decals piling up when you are standing next to a puddle, + // so the center isn't covered, so last time doesn't get set, and there's no throttle + // on how frequently we throw down decals. + info.fLastTime = t; + info.fLastPos = shapeL2W.GetTranslate(); + } + return retVal; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgr.h new file mode 100644 index 00000000..3bbe5d15 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgr.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynaRippleMgr_inc +#define plDynaRippleMgr_inc + +#include "plDynaDecalMgr.h" + +class plArmatureUpdateMsg; + +class plDynaRippleMgr : public plDynaDecalMgr +{ +protected: + hsVector3 fInitUVW; + hsVector3 fFinalUVW; + + virtual hsBool IRippleFromShape(const plPrintShape* shape, hsBool force=false); + + virtual int INewDecal(); +public: + plDynaRippleMgr(); + virtual ~plDynaRippleMgr(); + + CLASSNAME_REGISTER( plDynaRippleMgr ); + GETINTERFACE_ANY( plDynaRippleMgr, plDynaDecalMgr ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + void SetUVWAnim(const hsVector3& init, const hsVector3& final) { fInitUVW = init; fFinalUVW = final; } + const hsVector3& GetInitUVW() const { return fInitUVW; } + const hsVector3& GetFinalUVW() const { return fFinalUVW; } +}; + + +#endif // plDynaRippleMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgrVS.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgrVS.cpp new file mode 100644 index 00000000..e1bb7798 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleMgrVS.cpp @@ -0,0 +1,222 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#include "hsTypes.h" +#include "plDynaRippleVSMgr.h" +#include "plDynaDecal.h" + +#include "plPrintShape.h" + +#include "plCutter.h" + +#include "plgDispatch.h" + +#include "hsStream.h" +#include "hsResMgr.h" +#include "hsTimer.h" + +#include "plWaveSetBase.h" +#include "plRipVSConsts.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" + +#include "../plMessage/plDynaDecalEnableMsg.h" +#include "../plMessage/plRippleShapeMsg.h" + +#include "../plMessage/plAvatarMsg.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plAvatar/plArmatureMod.h" + +#include "../plMath/plRandom.h" +static plRandom sRand; + +#include "plTweak.h" + +int plDynaRippleVSMgr::INewDecal() +{ + int idx = fDecals.GetCount(); + + plDynaRippleVS* rip = TRACKED_NEW plDynaRippleVS(); + rip->fC1U = fInitUVW.fX; + rip->fC2U = (fInitUVW.fX - fFinalUVW.fX) / (fLifeSpan * fFinalUVW.fX); + + rip->fC1V = fInitUVW.fY; + rip->fC2V = (fInitUVW.fY - fFinalUVW.fY) / (fLifeSpan * fFinalUVW.fY); + + fDecals.Append(rip); + + return idx; +} + + +plDynaRippleVSMgr::plDynaRippleVSMgr() +: fWaveSetBase(nil) +{ +} + +plDynaRippleVSMgr::~plDynaRippleVSMgr() +{ +} + +void plDynaRippleVSMgr::Read(hsStream* stream, hsResMgr* mgr) +{ + plDynaRippleMgr::Read(stream, mgr); + + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefWaveSetBase), plRefFlags::kPassiveRef); +} + +void plDynaRippleVSMgr::Write(hsStream* stream, hsResMgr* mgr) +{ + plDynaRippleMgr::Write(stream, mgr); + + mgr->WriteKey(stream, fWaveSetBase); +} + +hsBool plDynaRippleVSMgr::MsgReceive(plMessage* msg) +{ + hsBool retVal = plDynaRippleMgr::MsgReceive(msg); + if( retVal ) + return true; + + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + switch( refMsg->fType ) + { + case kRefWaveSetBase: + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fWaveSetBase = plWaveSetBase::ConvertNoRef(refMsg->GetRef()); + else + fWaveSetBase = nil; + return true; + } + } + return false; +} + +hsBool plDynaRippleVSMgr::ICheckRTMat() +{ + if( !fMatRTShade ) + return false; + + if( !fWaveSetBase ) + return false; + + if( fMatRTShade->GetLayer(0)->GetVertexShader() ) + return true; + + plRipVSConsts ripConsts; + + ripConsts.fC1U = fInitUVW.fX; + ripConsts.fC2U = (fInitUVW.fX - fFinalUVW.fX) / (fLifeSpan * fFinalUVW.fX); + + ripConsts.fC1V = fInitUVW.fY; + ripConsts.fC2V = (fInitUVW.fY - fFinalUVW.fY) / (fLifeSpan * fFinalUVW.fY); + + ripConsts.fInitAtten = fInitAtten; + ripConsts.fLife = fLifeSpan; + ripConsts.fDecay = fDecayStart; + ripConsts.fRamp = fRampEnd; + + return fWaveSetBase->SetupRippleMat(fMatRTShade, ripConsts); +} + +hsBool plDynaRippleVSMgr::IRippleFromShape(const plPrintShape* shape, hsBool force) +{ + if( !ICheckRTMat() ) + return false; + + if( !shape ) + return false; + + hsBool retVal = false; + + plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey()); + + const hsMatrix44& shapeL2W = shape->GetOwner()->GetLocalToWorld(); + + plConst(hsScalar) kMinDist(2.0f); + plConst(hsScalar) kMinTime(1.5f); + double t = hsTimer::GetSysSeconds(); + hsScalar dt = hsScalar(t - info.fLastTime) * sRand.RandZeroToOne(); + hsBool longEnough = (dt >= kMinTime); + hsBool farEnough = (hsVector3(&info.fLastPos, &shapeL2W.GetTranslate()).Magnitude() > kMinDist); + if( force || longEnough || farEnough ) + { + hsPoint3 pos = shapeL2W.GetTranslate(); + + // We'll perturb the position a little so it doesn't look quite so regular, + // but we perturb it more if we're just standing still + hsVector3 randPert(sRand.RandMinusOneToOne(), sRand.RandMinusOneToOne(), 0); + randPert.Normalize(); + if( !farEnough ) + { + plConst(hsScalar) kRandPert = 0.5f; + randPert *= kRandPert; + } + else + { + plConst(hsScalar) kRandPert = 0.15f; + randPert *= kRandPert; + } + pos += randPert; + + // Are we potentially touching the water? + hsScalar waterHeight = fWaveSetBase->GetHeight(); + if( (pos.fZ - fScale.fZ * shape->GetHeight() < waterHeight) + &&(pos.fZ + fScale.fZ * shape->GetHeight() > waterHeight) ) + { + + hsVector3 dir(fWaveSetBase->GetWindDir()); + hsVector3 up(0.f, 0.f, 1.f); + + hsScalar wid = hsMaximum(shape->GetWidth(), shape->GetLength()); + + plConst(hsScalar) kMaxWaterDepth(1000.f); + + hsVector3 size(wid * fScale.fX, wid * fScale.fY, kMaxWaterDepth); + fCutter->SetLength(size); + fCutter->Set(pos, dir, up); + + + hsBool hit = ICutoutTargets(t); + if( hit ) + { + info.fLastTime = t; + info.fLastPos = shapeL2W.GetTranslate(); + retVal = true; + } + else + { + retVal = false; // No-effect else just for break points. + } + } + } + return retVal; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleVSMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleVSMgr.h new file mode 100644 index 00000000..4687f169 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaRippleVSMgr.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynaRippleVSMgr_inc +#define plDynaRippleVSMgr_inc + +#include "plDynaRippleMgr.h" + +class plWaveSetBase; + +class plDynaRippleVSMgr : public plDynaRippleMgr +{ +public: + enum { + kRefWaveSetBase = kRefNextAvailable + }; +protected: + + plWaveSetBase* fWaveSetBase; + + virtual hsBool IRippleFromShape(const plPrintShape* shape, hsBool force=false); + + virtual int INewDecal(); + + virtual hsBool ICheckRTMat(); + +public: + plDynaRippleVSMgr(); + virtual ~plDynaRippleVSMgr(); + + CLASSNAME_REGISTER( plDynaRippleVSMgr ); + GETINTERFACE_ANY( plDynaRippleVSMgr, plDynaRippleMgr ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); +}; + +#endif // plDynaRippleVSMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoMgr.cpp new file mode 100644 index 00000000..6d09e70a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoMgr.cpp @@ -0,0 +1,143 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plDynaTorpedoMgr.h" + +#include "../plMessage/plBulletMsg.h" + +#include "plCutter.h" + +#include "plgDispatch.h" + +#include "hsStream.h" +#include "hsResMgr.h" +#include "hsTimer.h" +#include "plTweak.h" + +#include "../plMath/plRandom.h" + +static const UInt32 kNumPrintIDs = 0; + +static plRandom sRand; + +plDynaTorpedoMgr::plDynaTorpedoMgr() +{ + fPartIDs.SetCount(kNumPrintIDs); +} + +plDynaTorpedoMgr::~plDynaTorpedoMgr() +{ +} + +void plDynaTorpedoMgr::Read(hsStream* stream, hsResMgr* mgr) +{ + plDynaRippleMgr::Read(stream, mgr); + + plgDispatch::Dispatch()->RegisterForExactType(plBulletMsg::Index(), GetKey()); +} + +hsBool plDynaTorpedoMgr::IHandleShot(plBulletMsg* bull) +{ + hsScalar partyTime = fPartyTime; + + plConst(int) kNumShots(3); + int i; + for( i = 0; i < kNumShots; i++ ) + { + hsVector3 up = IRandomUp(bull->Dir()); + hsVector3 pert = bull->Dir() % up; + + plConst(hsScalar) kMaxPert(1.f); + hsScalar maxPert = i ? kMaxPert * bull->Radius() : 0; + pert *= sRand.RandMinusOneToOne() * maxPert * fScale.fX; + + pert += up * (sRand.RandMinusOneToOne() * maxPert * fScale.fY); + + hsPoint3 pos = bull->From() + bull->Dir() * (bull->Range() * 0.5f); + pos += pert; + + hsScalar scaleX = bull->Radius() * fScale.fX * fInitUVW.fX; + hsScalar scaleY = bull->Radius() * fScale.fY * fInitUVW.fY; + +#if 0 + plConst(hsScalar) kMinScale(0.5f); + if( i ) + { + scaleX *= sRand.RandRangeF(kMinScale, 1.f); + scaleY *= sRand.RandRangeF(kMinScale, 1.f); + } +#elif 0 + hsScalar div = 1.f / (1.f + hsScalar(i)); + scaleX *= div; + scaleY *= div; +#else + plConst(hsScalar) kMinScale(0.25f); + plConst(hsScalar) kMaxScale(0.75f); + if( i ) + { + hsScalar scale = sRand.RandRangeF(kMinScale, kMaxScale); + scaleX *= scale; + scaleY *= scale; + } +#endif + + fCutter->SetLength(hsVector3(scaleX, scaleY, bull->Range())); + fCutter->Set(pos, up, -bull->Dir()); + + plDynaDecalInfo& info = IGetDecalInfo(UInt32(this), GetKey()); + + if( bull->PartyTime() > 0 ) + fPartyTime = bull->PartyTime(); + + double secs = hsTimer::GetSysSeconds(); + + if( ICutoutTargets(secs) ) + info.fLastTime = secs; + + fPartyTime = 0; + + } + fPartyTime = partyTime; + + return true; +} + +hsBool plDynaTorpedoMgr::MsgReceive(plMessage* msg) +{ + plBulletMsg* bullMsg = plBulletMsg::ConvertNoRef(msg); + if( bullMsg ) + { + if( bullMsg->Shot() ) + { + return IHandleShot(bullMsg); + } + return true; + } + + return plDynaRippleMgr::MsgReceive(msg); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoMgr.h new file mode 100644 index 00000000..4a30261e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoMgr.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynaTorpedoMgr_inc +#define plDynaTorpedoMgr_inc + +#include "plDynaRippleMgr.h" + +class plKey; +class plBulletMsg; + +class plDynaTorpedoMgr : public plDynaRippleMgr +{ +protected: + + virtual hsBool IHandleShot(plBulletMsg* bull); + +public: + plDynaTorpedoMgr(); + virtual ~plDynaTorpedoMgr(); + + CLASSNAME_REGISTER( plDynaTorpedoMgr ); + GETINTERFACE_ANY( plDynaTorpedoMgr, plDynaRippleMgr ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + +}; + +#endif // plDynaTorpedoMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoVSMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoVSMgr.cpp new file mode 100644 index 00000000..88070dac --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoVSMgr.cpp @@ -0,0 +1,143 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plDynaTorpedoVSMgr.h" +#include "plDynaDecal.h" + +#include "plWaveSetBase.h" +#include "plRipVSConsts.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" + +#include "hsStream.h" +#include "hsResMgr.h" + +#include "../pnMessage/plRefMsg.h" + +plDynaTorpedoVSMgr::plDynaTorpedoVSMgr() +: fWaveSetBase(nil) +{ +} + +plDynaTorpedoVSMgr::~plDynaTorpedoVSMgr() +{ +} + +int plDynaTorpedoVSMgr::INewDecal() +{ + int idx = fDecals.GetCount(); + + plDynaRippleVS* rip = TRACKED_NEW plDynaRippleVS(); + rip->fC1U = fInitUVW.fX; + rip->fC2U = (fInitUVW.fX - fFinalUVW.fX) / (fLifeSpan * fFinalUVW.fX); + + rip->fC1V = fInitUVW.fY; + rip->fC2V = (fInitUVW.fY - fFinalUVW.fY) / (fLifeSpan * fFinalUVW.fY); + + fDecals.Append(rip); + + return idx; +} + +hsBool plDynaTorpedoVSMgr::IHandleShot(plBulletMsg* bull) +{ + if( !ICheckRTMat() ) + return false; + + return plDynaTorpedoMgr::IHandleShot(bull); +} + +hsBool plDynaTorpedoVSMgr::ICheckRTMat() +{ + if( !fMatRTShade ) + return false; + + if( !fWaveSetBase ) + return false; + + if( fMatRTShade->GetLayer(0)->GetVertexShader() ) + return true; + + plRipVSConsts ripConsts = IGetRippleConsts(); + + return fWaveSetBase->SetupRippleMat(fMatRTShade, ripConsts); +} + +plRipVSConsts plDynaTorpedoVSMgr::IGetRippleConsts() const +{ + plRipVSConsts ripConsts; + + ripConsts.fC1U = fInitUVW.fX; + ripConsts.fC2U = (fInitUVW.fX - fFinalUVW.fX) / (fLifeSpan * fFinalUVW.fX); + + ripConsts.fC1V = fInitUVW.fY; + ripConsts.fC2V = (fInitUVW.fY - fFinalUVW.fY) / (fLifeSpan * fFinalUVW.fY); + + ripConsts.fInitAtten = fInitAtten; + ripConsts.fLife = fLifeSpan; + ripConsts.fDecay = fDecayStart; + ripConsts.fRamp = fRampEnd; + + return ripConsts; +} + +hsBool plDynaTorpedoVSMgr::MsgReceive(plMessage* msg) +{ + hsBool retVal = plDynaTorpedoMgr::MsgReceive(msg); + if( retVal ) + return true; + + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + switch( refMsg->fType ) + { + case kRefWaveSetBase: + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fWaveSetBase = plWaveSetBase::ConvertNoRef(refMsg->GetRef()); + else + fWaveSetBase = nil; + return true; + } + } + return false; +} + +void plDynaTorpedoVSMgr::Read(hsStream* stream, hsResMgr* mgr) +{ + plDynaTorpedoMgr::Read(stream, mgr); + + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefWaveSetBase), plRefFlags::kPassiveRef); +} + +void plDynaTorpedoVSMgr::Write(hsStream* stream, hsResMgr* mgr) +{ + plDynaTorpedoMgr::Write(stream, mgr); + + mgr->WriteKey(stream, fWaveSetBase); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoVSMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoVSMgr.h new file mode 100644 index 00000000..a4f61aea --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaTorpedoVSMgr.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynaTorpedoMgrVS_inc +#define plDynaTorpedoMgrVS_inc + +#include "plDynaTorpedoMgr.h" +#include "plRipVSConsts.h" + +class plWaveSetBase; + +class plDynaTorpedoVSMgr : public plDynaTorpedoMgr +{ +public: + enum { + kRefWaveSetBase = kRefNextAvailable + }; +protected: + plWaveSetBase* fWaveSetBase; + + virtual int INewDecal(); + + virtual hsBool ICheckRTMat(); + + plRipVSConsts IGetRippleConsts() const; + virtual hsBool IHandleShot(plBulletMsg* bull); +public: + plDynaTorpedoVSMgr(); + virtual ~plDynaTorpedoVSMgr(); + + CLASSNAME_REGISTER( plDynaTorpedoVSMgr ); + GETINTERFACE_ANY( plDynaTorpedoVSMgr, plDynaTorpedoMgr ); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + + +#endif // plDynaTorpedoMgrVS_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaWakeMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaWakeMgr.cpp new file mode 100644 index 00000000..1fd46a9b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaWakeMgr.cpp @@ -0,0 +1,212 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plDynaWakeMgr.h" +#include "plDynaDecal.h" + +#include "plPrintShape.h" + +#include "plCutter.h" + +#include "plgDispatch.h" + +#include "hsStream.h" +#include "hsResMgr.h" +#include "hsTimer.h" + +#include "../plMessage/plDynaDecalEnableMsg.h" +#include "../plMessage/plRippleShapeMsg.h" + +#include "../plMessage/plAvatarMsg.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plAvatar/plArmatureMod.h" + +#include "../plInterp/plAnimPath.h" + +#include "hsFastMath.h" +#include "../plMath/plRandom.h" +static plRandom sRand; + + +int plDynaWakeMgr::INewDecal() +{ + int idx = fDecals.GetCount(); + + plDynaWake* wake = TRACKED_NEW plDynaWake(); + wake->fC1U = fInitUVW.fX; + wake->fC2U = (fInitUVW.fX - fFinalUVW.fX) / (fLifeSpan * fFinalUVW.fX); + + wake->fC1V = fInitUVW.fY; + wake->fC2V = (fInitUVW.fY - fFinalUVW.fY) / (fLifeSpan * fFinalUVW.fY); + + fDecals.Append(wake); + + return idx; +} + +plDynaWakeMgr::plDynaWakeMgr() +: + fAnimPath(nil), + fDefaultDir(0.f, 1.f, 0.f), + fAnimWgt(0), + fVelWgt(1.f) +{ +} + +plDynaWakeMgr::~plDynaWakeMgr() +{ + delete fAnimPath; +} + +void plDynaWakeMgr::SetAnimPath(plAnimPath* a) +{ + delete fAnimPath; + fAnimPath = a; +} + +void plDynaWakeMgr::SetDefaultDir(const hsVector3& v) +{ + fDefaultDir = v; + hsFastMath::Normalize(fDefaultDir); +} + +void plDynaWakeMgr::Read(hsStream* stream, hsResMgr* mgr) +{ + plDynaRippleMgr::Read(stream, mgr); + + fDefaultDir.Read(stream); + fAnimPath = plAnimPath::ConvertNoRef(mgr->ReadCreatable(stream)); + + fAnimWgt = stream->ReadSwapScalar(); + fVelWgt = stream->ReadSwapScalar(); +} + +void plDynaWakeMgr::Write(hsStream* stream, hsResMgr* mgr) +{ + plDynaRippleMgr::Write(stream, mgr); + + fDefaultDir.Write(stream); + mgr->WriteCreatable(stream, fAnimPath); + + stream->WriteSwapScalar(fAnimWgt); + stream->WriteSwapScalar(fVelWgt); +} + +hsVector3 plDynaWakeMgr::IGetDirection(const plDynaDecalInfo& info, const hsPoint3& pos) const +{ + hsVector3 dir = fDefaultDir; + // If we have an animpath, figure a direction based on position here + if( fAnimPath ) + { + hsVector3 animDir; + hsPoint3 p = pos; + hsScalar t = fAnimPath->GetExtremePoint(p); + fAnimPath->SetCurTime(t, plAnimPath::kNone); + + fAnimPath->GetVelocity(&animDir); + + animDir *= fAnimWgt; + + dir += animDir; + } + + // Now if we want to factor in velocity, we can use (pos - info.fLastPos) / (hsTimer::GetSysSeconds() - info.fLastTime) + hsScalar dt = hsScalar(hsTimer::GetSysSeconds() - info.fLastTime); + const hsScalar kMinDt = 1.e-3f; + if( (info.fFlags & plDynaDecalInfo::kImmersed) && (dt > kMinDt) ) + { + hsVector3 velDir(&pos, &info.fLastPos); + + velDir *= 1.f / dt * fVelWgt; + + dir += velDir; + } + hsFastMath::Normalize(dir); + + return dir; +} + +hsBool plDynaWakeMgr::IRippleFromShape(const plPrintShape* shape, hsBool force) +{ + if( !shape ) + return false; + + hsBool retVal = false; + + plDynaDecalInfo& info = IGetDecalInfo(UInt32(shape), shape->GetKey()); + + const hsMatrix44& shapeL2W = shape->GetOwner()->GetLocalToWorld(); + + static hsScalar kMinDist = 1.0f; + static hsScalar kMinTime = 0.25f; + double t = hsTimer::GetSysSeconds(); + hsScalar dt = hsScalar(t - info.fLastTime) * sRand.RandZeroToOne(); + hsBool longEnough = (dt >= kMinTime); + hsBool farEnough = (hsVector3(&info.fLastPos, &shapeL2W.GetTranslate()).Magnitude() > kMinDist); + if( force || longEnough || farEnough ) + { + hsPoint3 pos = shapeL2W.GetTranslate(); + + // Base the direction on the unperturbed pos. + hsVector3 dir = IGetDirection(info, pos); + + // We'll perturb the position a little so it doesn't look quite so regular, + // but we perturb it more if we're just standing still + hsVector3 randPert(sRand.RandMinusOneToOne(), sRand.RandMinusOneToOne(), 0); + randPert.Normalize(); + if( !farEnough ) + { + static hsScalar kRandPert = 0.05f; + randPert *= kRandPert * shape->GetWidth(); + } + else + { + static hsScalar kRandPert = 0.05f; + randPert *= kRandPert * shape->GetWidth(); + } + pos += randPert; + + hsVector3 up(0.f, 0.f, 1.f); + + static hsScalar kHeightScale = 1.f; + pos.fZ += (shape->GetHeight() * fScale.fZ * kHeightScale) * 0.25f; + + pos += dir * shape->GetLength() * 0.5f * (1.f - fScale.fY); + + hsVector3 size(shape->GetWidth() * fScale.fX, shape->GetLength() * fScale.fY, shape->GetHeight() * fScale.fZ * kHeightScale); + fCutter->SetLength(size); + fCutter->Set(pos, dir, up); + + info.fLastTime = t; + info.fLastPos = shapeL2W.GetTranslate(); + + hsBool hit = ICutoutTargets(t); + if( hit ) + retVal = true; + } + return retVal; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaWakeMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaWakeMgr.h new file mode 100644 index 00000000..9aa326f7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plDynaWakeMgr.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynaWakeMgr_inc +#define plDynaWakeMgr_inc + +#include "plDynaRippleMgr.h" + +class plArmatureUpdateMsg; +class plAnimPath; + +class plDynaWakeMgr : public plDynaRippleMgr +{ +protected: + hsVector3 fDefaultDir; + plAnimPath* fAnimPath; + + hsScalar fAnimWgt; + hsScalar fVelWgt; + + virtual hsVector3 IGetDirection(const plDynaDecalInfo& info, const hsPoint3& pos) const; + + virtual hsBool IRippleFromShape(const plPrintShape* shape, hsBool force=false); + + virtual int INewDecal(); +public: + plDynaWakeMgr(); + virtual ~plDynaWakeMgr(); + + CLASSNAME_REGISTER( plDynaWakeMgr ); + GETINTERFACE_ANY( plDynaWakeMgr, plDynaRippleMgr ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void SetAnimPath(plAnimPath* a); + plAnimPath* GetAnimPath() const { return fAnimPath; } + + void SetDefaultDir(const hsVector3& v); + const hsVector3& GetDefaultDir() const { return fDefaultDir; } + + void SetAnimWeight(hsScalar f) { fAnimWgt = f; } + hsScalar GetAnimWeight() const { return fAnimWgt; } + + void SetVelocityWeight(hsScalar f) { fVelWgt = f; } + hsScalar GetVelocityWeight() const { return fVelWgt; } +}; + + +#endif // plDynaWakeMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plFixedWaterState7.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plFixedWaterState7.cpp new file mode 100644 index 00000000..4cdd1f41 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plFixedWaterState7.cpp @@ -0,0 +1,193 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plFixedWaterState7.h" + +#include "hsStream.h" + +void plFixedWaterState7::WaveState::Set(const plFixedWaterState7::WaveState& w, hsScalar secs) +{ + fMaxLength.Set(w.fMaxLength, secs); + fMinLength.Set(w.fMinLength, secs); + fAmpOverLen.Set(w.fAmpOverLen, secs); + fChop.Set(w.fChop, secs); + fAngleDev.Set(w.fAngleDev, secs); +} + +void plFixedWaterState7::Set(const plFixedWaterState7& t, hsScalar secs) +{ + fWindDir.Set(t.fWindDir, secs); + + fGeoState.Set(t.fGeoState, secs); + + fTexState.Set(t.fTexState, secs); + fRippleScale.Set(t.fRippleScale, secs); + + fSpecVec.Set(t.fSpecVec, secs); + + fWaterHeight.Set(t.fWaterHeight, secs); + fWaterOffset.Set(t.fWaterOffset, secs); + fMaxAtten.Set(t.fMaxAtten, secs); + fMinAtten.Set(t.fMinAtten, secs); + fDepthFalloff.Set(t.fDepthFalloff, secs); + + fWispiness.Set(t.fWispiness, secs); + fShoreTint.Set(t.fShoreTint, secs); + fMaxColor.Set(t.fMaxColor, secs); + fMinColor.Set(t.fMinColor, secs); + fEdgeOpac.Set(t.fEdgeOpac, secs); + fEdgeRadius.Set(t.fEdgeRadius, secs); + + fPeriod.Set(t.fPeriod, secs); + fFingerLength.Set(t.fFingerLength, secs); + + + fWaterTint.Set(t.fWaterTint, secs); + fSpecularTint.Set(t.fSpecularTint, secs); + + fEnvCenter.Set(t.fEnvCenter, secs); + fEnvRefresh.Set(t.fEnvRefresh, secs); + fEnvRadius.Set(t.fEnvRadius, secs); + + +} + +void plFixedWaterState7::WaveState::Read(hsStream* s) +{ + fMaxLength.Read(s); + fMinLength.Read(s); + fAmpOverLen.Read(s); + fChop.Read(s); + fAngleDev.Read(s); +} + +void plFixedWaterState7::WaveState::Write(hsStream* s) const +{ + fMaxLength.Write(s); + fMinLength.Write(s); + fAmpOverLen.Write(s); + fChop.Write(s); + fAngleDev.Write(s); +} + +void plFixedWaterState7::Read(hsStream* s) +{ + // Geometric waves + fGeoState.Read(s); + + // Texture waves + fTexState.Read(s); + fRippleScale.Read(s); + + // Geometric and Texture share wind direction + fWindDir.Read(s); + + // Level of noise added during summation of texture waves + fSpecVec.Read(s); + + // Depth parameters. Affect how the depth of + // the water vertex is interpreted into water + // surface properties. + fWaterHeight.Read(s); + fWaterOffset.Read(s); + fMaxAtten.Read(s); + fMinAtten.Read(s); + fDepthFalloff.Read(s); + + // Shore parameters + + // Appearance + fWispiness.Read(s); + fShoreTint.Read(s); + // Next two only used in generation of bubble layer + fMaxColor.Read(s); + fMinColor.Read(s); + fEdgeOpac.Read(s); + fEdgeRadius.Read(s); + + // Simulation + fPeriod.Read(s); + fFingerLength.Read(s); + + + // Water appearance. + fWaterTint.Read(s); + fSpecularTint.Read(s); + + fEnvCenter.Read(s); + fEnvRefresh.Read(s); + fEnvRadius.Read(s); +} + +void plFixedWaterState7::Write(hsStream* s) const +{ + // Geometric waves + fGeoState.Write(s); + + // Texture waves + fTexState.Write(s); + fRippleScale.Write(s); + + // Geometric and Texture share wind direction + fWindDir.Write(s); + + // Level of noise added during summation of texture waves + fSpecVec.Write(s); + + // Depth parameters. Affect how the depth of + // the water vertex is interpreted into water + // surface properties. + fWaterHeight.Write(s); + fWaterOffset.Write(s); + fMaxAtten.Write(s); + fMinAtten.Write(s); + fDepthFalloff.Write(s); + + // Shore parameters + + // Appearance + fWispiness.Write(s); + fShoreTint.Write(s); + // Next two only used in generation of bubble layer + fMaxColor.Write(s); + fMinColor.Write(s); + fEdgeOpac.Write(s); + fEdgeRadius.Write(s); + + // Simulation + fPeriod.Write(s); + fFingerLength.Write(s); + + + // Water appearance. + fWaterTint.Write(s); + fSpecularTint.Write(s); + + fEnvCenter.Write(s); + fEnvRefresh.Write(s); + fEnvRadius.Write(s); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plFixedWaterState7.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plFixedWaterState7.h new file mode 100644 index 00000000..14bf044e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plFixedWaterState7.h @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plFixedWaterState7_inc +#define plFixedWaterState7_inc + +#include "hsGeometry3.h" +#include "hsColorRGBA.h" +#include "../pnTimer/plTimedValue.h" + +class hsStream; + +class plFixedWaterState7 +{ +public: + class WaveState + { + public: + plTimedSimple fMaxLength; + plTimedSimple fMinLength; + plTimedSimple fAmpOverLen; + plTimedSimple fChop; + plTimedSimple fAngleDev; + + void Set(const WaveState& w, hsScalar secs); + + void Read(hsStream* s); + void Write(hsStream* s) const; + }; + // Main body of water state + + // Geometric waves + WaveState fGeoState; + + // Texture waves + WaveState fTexState; + plTimedSimple fRippleScale; + + // Geometric and Texture share wind direction + plTimedCompound fWindDir; + + // Level of noise added during summation of texture waves + enum { + kNoise = 0, + kSpecStart = 1, + kSpecEnd = 2 + }; + plTimedCompound fSpecVec; // (Noise, SpecStart, SpecEnd); + + // Depth parameters. Affect how the depth of + // the water vertex is interpreted into water + // surface properties. + plTimedSimple fWaterHeight; + plTimedCompound fWaterOffset; + plTimedCompound fMaxAtten; + plTimedCompound fMinAtten; + plTimedCompound fDepthFalloff; + + // Shore parameters + + // Appearance + plTimedSimple fWispiness; + plTimedCompound fShoreTint; + // Next two only used in generation of bubble layer + plTimedCompound fMaxColor; + plTimedCompound fMinColor; + plTimedSimple fEdgeOpac; + plTimedSimple fEdgeRadius; + + // Simulation + plTimedSimple fPeriod; + plTimedSimple fFingerLength; + + // The rest aren't really part of the state, that is they are normally controlled + // by something exterior to the state. They are included here to allow a convenient + // override during development. + + // Water appearance. + plTimedCompound fWaterTint; + plTimedCompound fSpecularTint; + + plTimedCompound fEnvCenter; + plTimedSimple fEnvRefresh; + plTimedSimple fEnvRadius; + + void Set(const plFixedWaterState7& s, hsScalar secs); + + void Read(hsStream* s); + void Write(hsStream* s) const; +}; + + +#endif // plFixedWaterState7_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeoSpanDice.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeoSpanDice.cpp new file mode 100644 index 00000000..86b242a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeoSpanDice.cpp @@ -0,0 +1,318 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "memory.h" + +#include "hsTypes.h" +#include "plGeoSpanDice.h" +#include "plGeometrySpan.h" + +plGeoSpanDice::plGeoSpanDice() +: fMinFaces(0), + fMaxFaces(0) +{ + fMaxSize.Set(0,0,0); +} + +plGeoSpanDice::~plGeoSpanDice() +{ +} + +hsBool plGeoSpanDice::Dice(hsTArray& spans) const +{ + int startingCount = spans.GetCount(); + + hsTArray out; + hsTArray next; + + while(spans.GetCount()) + { + int i; + for( i = 0; i < spans.GetCount(); i++ ) + { + if( !IHalf(spans[i], next) ) + { + out.Append(spans[i]); + } + } + spans.Swap(next); + next.SetCount(0); + } + spans.Swap(out); + + return spans.GetCount() != startingCount; +} + +hsBool plGeoSpanDice::INeedSplitting(plGeometrySpan* src) const +{ + // Do we have enough faces to bother? + if( fMinFaces ) + { + if( src->fNumIndices < fMinFaces * 3 ) + return false; + } + // Do we have enough faces to bother no matter how small we are? + if( fMaxFaces ) + { + if( src->fNumIndices > fMaxFaces * 3 ) + return true; + } + // Are we big enough to bother? + if( (fMaxSize.fX > 0) || (fMaxSize.fY > 0) || (fMaxSize.fZ > 0) ) + { + hsPoint3 size = src->fLocalBounds.GetMaxs() - src->fLocalBounds.GetMins(); + if( size.fX > fMaxSize.fX ) + return true; + if( size.fY > fMaxSize.fY ) + return true; + if( size.fZ > fMaxSize.fZ ) + return true; + } + + return false; +} + +hsBool plGeoSpanDice::IHalf(plGeometrySpan* src, hsTArray& out, int exclAxis) const +{ + if( !INeedSplitting(src) ) + return false; + + int iAxis = ISelectAxis(exclAxis, src); + // Ran out of axes to try. + if( iAxis < 0 ) + return false; + + hsScalar midPoint = src->fLocalBounds.GetCenter()[iAxis]; + + hsTArray loTris; + hsTArray hiTris; + + UInt16* indexData = src->fIndexData; + + int numTris = src->fNumIndices / 3; + + int stride = src->GetVertexSize(src->fFormat); + + int i; + for( i = 0; i < numTris; i++ ) + { + hsPoint3& pos0 = *(hsPoint3*)(src->fVertexData + *indexData++ * stride); + hsPoint3& pos1 = *(hsPoint3*)(src->fVertexData + *indexData++ * stride); + hsPoint3& pos2 = *(hsPoint3*)(src->fVertexData + *indexData++ * stride); + + if( (pos0[iAxis] >= midPoint) + &&(pos1[iAxis] >= midPoint) + &&(pos2[iAxis] >= midPoint) ) + { + hiTris.Append(i); + } + else + { + loTris.Append(i); + } + } + + // This axis isn't working out, try another. + if( !hiTris.GetCount() || !loTris.GetCount() ) + return IHalf(src, out, exclAxis | (1 << iAxis)); + + + plGeometrySpan* loDst = IExtractTris(src, loTris); + plGeometrySpan* hiDst = IExtractTris(src, hiTris); + + delete src; + + out.Append(loDst); + out.Append(hiDst); + + return true; +} + +int plGeoSpanDice::ISelectAxis(int exclAxis, plGeometrySpan* src) const +{ + int iAxis = -1; + hsScalar maxDim = 0; + + int i; + for( i = 0; i < 3; i++ ) + { + // Check to see if we've already tried this one. + if( exclAxis & (1 << i) ) + continue; + + hsScalar dim = src->fLocalBounds.GetMaxs()[i] - src->fLocalBounds.GetMins()[i]; + if( dim > maxDim ) + { + maxDim = dim; + iAxis = i; + } + } + return iAxis; +} + +plGeometrySpan* plGeoSpanDice::IExtractTris(plGeometrySpan* src, hsTArray& tris) const +{ + // First off, find out how many and which vers we're talking here. + // Easiest way is while we're building the LUTs we'll want later anyway. + hsTArray fwdLUT; + fwdLUT.SetCount(src->fNumVerts); + memset(fwdLUT.AcquireArray(), -1, src->fNumVerts * sizeof(*fwdLUT.AcquireArray())); + + hsTArray bckLUT; + bckLUT.SetCount(0); + + int i; + for( i = 0; i < tris.GetCount(); i++ ) + { + UInt16* idx = src->fIndexData + tris[i] * 3; + + if( fwdLUT[*idx] < 0 ) + { + fwdLUT[*idx] = bckLUT.GetCount(); + bckLUT.Append(*idx); + } + + idx++; + + if( fwdLUT[*idx] < 0 ) + { + fwdLUT[*idx] = bckLUT.GetCount(); + bckLUT.Append(*idx); + } + + idx++; + + if( fwdLUT[*idx] < 0 ) + { + fwdLUT[*idx] = bckLUT.GetCount(); + bckLUT.Append(*idx); + } + } + + int numVerts = bckLUT.GetCount(); + int numTris = tris.GetCount(); + + plGeometrySpan* dst = IAllocSpace(src, numVerts, numTris); + + // Okay, set the index data. + UInt16* idxTrav = dst->fIndexData; + for( i = 0; i < tris.GetCount(); i++ ) + { + *idxTrav++ = fwdLUT[src->fIndexData[ tris[i] * 3 + 0] ]; + *idxTrav++ = fwdLUT[src->fIndexData[ tris[i] * 3 + 1] ]; + *idxTrav++ = fwdLUT[src->fIndexData[ tris[i] * 3 + 2] ]; + } + + // Copy over the basic vertex data + // We'll update the bounds as we go. + int stride = src->GetVertexSize(src->fFormat); + + hsBounds3Ext localBnd; + localBnd.MakeEmpty(); + UInt8* vtxTrav = dst->fVertexData; + for( i = 0; i < numVerts; i++ ) + { + memcpy(vtxTrav, src->fVertexData + bckLUT[i] * stride, stride); + + hsPoint3* pos = (hsPoint3*)vtxTrav; + localBnd.Union(pos); + vtxTrav += stride; + } + if( src->fProps & plGeometrySpan::kWaterHeight ) + { + src->AdjustBounds(localBnd); + } + dst->fLocalBounds = localBnd; + + // Now the rest of this optional garbage. + if( src->fMultColor ) + { + for( i = 0; i < numVerts; i++ ) + { + dst->fMultColor[i] = src->fMultColor[bckLUT[i]]; + } + } + if( src->fAddColor ) + { + for( i = 0; i < numVerts; i++ ) + { + dst->fAddColor[i] = src->fAddColor[bckLUT[i]]; + } + } + if( src->fDiffuseRGBA ) + { + for( i = 0; i < numVerts; i++ ) + { + dst->fDiffuseRGBA[i] = src->fDiffuseRGBA[bckLUT[i]]; + } + } + if( src->fSpecularRGBA ) + { + for( i = 0; i < numVerts; i++ ) + { + dst->fSpecularRGBA[i] = src->fSpecularRGBA[bckLUT[i]]; + } + } + dst->fMaxOwner = src->fMaxOwner; + + return dst; +} + +plGeometrySpan* plGeoSpanDice::IAllocSpace(plGeometrySpan* src, int numVerts, int numTris) const +{ + plGeometrySpan* dst = TRACKED_NEW plGeometrySpan; + // Do a structure copy here. That's okay, because we're going to immediately + // fix up the pointers and counters that shouldn't have been copied. If + // plGeometrySpan ever gets a copy constructor, this'll blow wide open. + *dst = *src; + + int stride = src->GetVertexSize(src->fFormat); + + dst->fNumIndices = numTris * 3; + dst->fIndexData = TRACKED_NEW UInt16[dst->fNumIndices]; + + dst->fNumVerts = numVerts; + dst->fVertexData = TRACKED_NEW UInt8[numVerts * stride]; + + if( src->fMultColor ) + { + dst->fMultColor = TRACKED_NEW hsColorRGBA[numVerts]; + } + if( src->fAddColor ) + { + dst->fAddColor = TRACKED_NEW hsColorRGBA[numVerts]; + } + if( src->fDiffuseRGBA ) + { + dst->fDiffuseRGBA = TRACKED_NEW UInt32[numVerts]; + } + if( src->fSpecularRGBA ) + { + dst->fSpecularRGBA = TRACKED_NEW UInt32[numVerts]; + } + + return dst; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeoSpanDice.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeoSpanDice.h new file mode 100644 index 00000000..02ed2210 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeoSpanDice.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plGeoSpanDice_inc +#define plGeoSpanDice_inc + +#include "hsGeometry3.h" +#include "hsTemplates.h" + +class plGeometrySpan; + +class plGeoSpanDice +{ +protected: + UInt32 fMinFaces; + UInt32 fMaxFaces; + hsPoint3 fMaxSize; + + hsBool INeedSplitting(plGeometrySpan* src) const; + plGeometrySpan* IAllocSpace(plGeometrySpan* src, int numVerts, int numTris) const; + plGeometrySpan* IExtractTris(plGeometrySpan* src, hsTArray& tris) const; + int ISelectAxis(int exclAxis, plGeometrySpan* src) const; + hsBool IHalf(plGeometrySpan* src, hsTArray& out, int exclAxis=0) const; + +public: + plGeoSpanDice(); + virtual ~plGeoSpanDice(); + + hsBool Dice(hsTArray& spans) const; + + void SetMaxSize(const hsPoint3& size) { fMaxSize = size; } + hsPoint3 GetMaxSize() const { return fMaxSize; } + + void SetMinFaces(int n) { fMinFaces = n; } + int GetMinFaces() const { return fMinFaces; } + + void SetMaxFaces(int n) { fMaxFaces = n; } + int GetMaxFaces() const { return fMaxFaces; } +}; + +#endif // plGeoSpanDice_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeometrySpan.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeometrySpan.cpp new file mode 100644 index 00000000..24ad7795 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeometrySpan.cpp @@ -0,0 +1,1194 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plGeometrySpan Class Functions // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// Created 3.8.2001 mcn // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsWindows.h" +#include "hsTypes.h" +#include "plGeometrySpan.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" +#include "hsBitVector.h" + + +//// Static Data ///////////////////////////////////////////////////////////// + +hsBitVector plGeometrySpan::fInstanceGroupIDFlags; +UInt32 plGeometrySpan::fHighestReadInstanceGroup = 0; + +hsTArray *> plGeometrySpan::fInstanceGroups; + + +//// Constructor and Destructor ////////////////////////////////////////////// + +plGeometrySpan::plGeometrySpan() +{ + IClearMembers(); +} + +plGeometrySpan::plGeometrySpan( const plGeometrySpan *instance ) +{ + IClearMembers(); + MakeInstanceOf( instance ); +} + +plGeometrySpan::~plGeometrySpan() +{ + ClearBuffers(); +} + +void plGeometrySpan::IClearMembers( void ) +{ + fVertexData = nil; + fIndexData = nil; + fMaterial = nil; + fNumVerts = fNumIndices = 0; + fBaseMatrix = fNumMatrices = 0; + fLocalUVWChans = 0; + fMaxBoneIdx = 0; + fPenBoneIdx = 0; + fCreating = false; + fFogEnviron = nil; + fProps = 0; + + fMinDist = fMaxDist = -1.f; + + fWaterHeight = 0; + + fMultColor = nil; + fAddColor = nil; + + fDiffuseRGBA = nil; + fSpecularRGBA = nil; + fInstanceRefs = nil; + fInstanceGroupID = kNoGroupID; + fSpanRefIndex = (UInt32)-1; + + fLocalToOBB.Reset(); + fOBBToLocal.Reset(); + + fDecalLevel = 0; + + fMaxOwner = nil; +} + +//// ClearBuffers //////////////////////////////////////////////////////////// + +void plGeometrySpan::ClearBuffers( void ) +{ + // If UserOwned, the actual buffer data belongs to someone else (like a BufferGroup). + // Just erase our knowledge of it and move on. + if( fProps & kUserOwned ) + { + IClearMembers(); + return; + } + + bool removeData = true; + + + // If we have an instanceRefs array, remove ourselves from + // the array. If we are the last in the array, remove the array itself + if( fInstanceRefs != nil ) + { + if( fInstanceRefs->GetCount() == 1 ) + { + delete fInstanceRefs; + + // Remove the group ID flag as well + IClearGroupID( fInstanceGroupID ); + } + else + { + int idx = fInstanceRefs->Find( this ); + hsAssert( idx != fInstanceRefs->kMissingIndex, "Invalid instance ref data in plGeometrySpan::ClearBuffers()" ); + + fInstanceRefs->Remove( idx ); + removeData = false; // Don't remove data until we're the last one + } + fInstanceRefs = nil; + fInstanceGroupID = kNoGroupID; + } + + if( removeData ) + { + delete [] fVertexData; + fVertexData = nil; + + delete [] fMultColor; + delete [] fAddColor; + fMultColor = nil; + fAddColor = nil; + } + + delete [] fIndexData; + delete [] fDiffuseRGBA; + delete [] fSpecularRGBA; + fIndexData = nil; + fDiffuseRGBA = fSpecularRGBA = nil; +} + +//// MakeInstanceOf ////////////////////////////////////////////////////////// +// Note: instancing copies the index buffer but just copies the POINTERS +// for the vertex buffers and source colors. This is because the indices +// will eventually change but the vertices won't. + +void plGeometrySpan::MakeInstanceOf( const plGeometrySpan *instance ) +{ + hsTArray *array; + + + ClearBuffers(); + + /// Adjust the instanceRefs array + array = instance->fInstanceRefs; + if( array == nil ) + { + // Go find a new groupID + instance->fInstanceGroupID = IAllocateNewGroupID(); + + instance->fInstanceRefs = array = TRACKED_NEW hsTArray; + // Go figure, it won't append the const version to the array...this is a cheat, + // but then, so is making fInstanceRefs mutable :) + array->Append( (plGeometrySpan *)instance ); + } + + fInstanceGroupID = instance->fInstanceGroupID; + array->Append( this ); + fInstanceRefs = array; + + /// Copy over the data + IDuplicateUniqueData( instance ); + fVertexData = instance->fVertexData; + fMultColor = instance->fMultColor; + fAddColor = instance->fAddColor; + + /// All done! +} + +void plGeometrySpan::IUnShareData() +{ + if( fVertexData ) + { + UInt8* oldVtxData = fVertexData; + + UInt32 size = GetVertexSize( fFormat ); + + fVertexData = TRACKED_NEW UInt8[ size * fNumVerts ]; + memcpy( fVertexData, oldVtxData, size * fNumVerts ); + } + + if( fMultColor ) + { + hsColorRGBA* oldMult = fMultColor; + + fMultColor = TRACKED_NEW hsColorRGBA[ fNumVerts ]; + memcpy( fMultColor, oldMult, sizeof(hsColorRGBA) * fNumVerts ); + } + + if( fAddColor ) + { + hsColorRGBA* oldAdd = fAddColor; + + fAddColor = TRACKED_NEW hsColorRGBA[ fNumVerts ]; + memcpy( fAddColor, oldAdd, sizeof(hsColorRGBA) * fNumVerts ); + } +} + +void plGeometrySpan::BreakInstance() +{ + hsAssert(fInstanceRefs, "Breaking instancing when I'm not instanced"); + int idx = fInstanceRefs->Find(this); + hsAssert(idx != fInstanceRefs->kMissingIndex, "I'm not in my own instance refs list"); + fInstanceRefs->Remove(idx); + hsAssert(fInstanceRefs->GetCount(), "Don't BreakInstance if I'm the last one, use UnInstance instead"); + + fInstanceGroupID = IAllocateNewGroupID(); + fInstanceRefs = TRACKED_NEW hsTArray; + fInstanceRefs->Append(this); + + IUnShareData(); + + fProps |= plGeometrySpan::kInstanced; +} + +void plGeometrySpan::ChangeInstance(plGeometrySpan* newInstance) +{ + hsAssert(fInstanceRefs, "Changing instancing when I'm not instanced"); + int idx = fInstanceRefs->Find(this); + hsAssert(idx != fInstanceRefs->kMissingIndex, "I'm not in my own instance refs list"); + fInstanceRefs->Remove(idx); + + fInstanceGroupID = newInstance->fInstanceGroupID; + fInstanceRefs = newInstance->fInstanceRefs; + fInstanceRefs->Append(this); + + fVertexData = newInstance->fVertexData; + fMultColor = newInstance->fMultColor; + fAddColor = newInstance->fAddColor; + + fProps |= plGeometrySpan::kInstanced; +} + +void plGeometrySpan::UnInstance() +{ + hsAssert(fInstanceRefs, "UnInstancing a non-instance"); + + int idx = fInstanceRefs->Find(this); + hsAssert(idx != fInstanceRefs->kMissingIndex, "I'm not in my own instance refs list"); + fInstanceRefs->Remove(idx); + + // If we're the last one, we just take ownership of the shared data, + // else we make our own copy of it. + if( !fInstanceRefs->GetCount() ) + { + delete fInstanceRefs; + } + else + { + IUnShareData(); + } + fInstanceRefs = nil; + fInstanceGroupID = kNoGroupID; + fProps &= ~plGeometrySpan::kInstanced; +} + +//// IAllocateNewGroupID ///////////////////////////////////////////////////// +// Static function that allocates a new groupID by finding an empty slot in +// the bitVector, then marking it as used and returning that bit # + +UInt32 plGeometrySpan::IAllocateNewGroupID( void ) +{ + UInt32 id; + + + for( id = 0; id < fInstanceGroupIDFlags.GetSize(); id++ ) + { + if( !fInstanceGroupIDFlags.IsBitSet( id ) ) + break; + } + + fInstanceGroupIDFlags.SetBit( id, true ); + return id + 1; +} + +//// IClearGroupID /////////////////////////////////////////////////////////// +// Done with this groupID, so clear the entry in the bit table. + +void plGeometrySpan::IClearGroupID( UInt32 groupID ) +{ + fInstanceGroupIDFlags.ClearBit( groupID - 1 ); +} + +//// IGetInstanceGroup /////////////////////////////////////////////////////// +// This does the whole hash table thing lookup during read. If: +// - The group ID does not yet exist: +// - Allocates a new hsTArray, puts it into the hash table if expectedCount > 1, +// sets the groupID flag, and returns a pointer to the array. +// - The group ID does exist: +// - If the array count is less than expectedCount - 1, returns the array +// - else sets the hash entry to nil and returns the array. +// Since we want to clear the hash table as soon as possible, but don't want +// to search the entire hash table every time to make sure its empty, we +// keep an ID of the highest element in the hash table that's set; every time +// we remove an entry, we decrement this ID until we hit a used pointer again; +// if we don't find one, we reset the array. + +hsTArray *plGeometrySpan::IGetInstanceGroup( UInt32 groupID, UInt32 expectedCount ) +{ + hsTArray *array; + + + groupID--; // Make it our array index + + if( fInstanceGroups.GetCount() <= groupID || fInstanceGroups[ groupID ] == nil ) + { + // Not yet in the list--make a new hsTArray + array = TRACKED_NEW hsTArray; + fInstanceGroupIDFlags.SetBit( groupID, true ); + + if( expectedCount > 1 ) + { + if( fInstanceGroups.GetCount() <= groupID ) + fInstanceGroups.ExpandAndZero( groupID + 1 ); + + fInstanceGroups[ groupID ] = array; + if( fHighestReadInstanceGroup < groupID + 1 ) + fHighestReadInstanceGroup = groupID + 1; + } + + return array; + } + else + { + // In the list...get it, but are we done with it? + array = fInstanceGroups[ groupID ]; + if( expectedCount == array->GetCount() + 1 ) // I.E. next Append() will make it == + { + // Done with it, remove from hash table + fInstanceGroups[ groupID ] = nil; + + // Find new fHighestReadInstanceGroup + for( ; fHighestReadInstanceGroup > 0 && fInstanceGroups[ fHighestReadInstanceGroup - 1 ] == nil; + fHighestReadInstanceGroup-- ); + + if( fHighestReadInstanceGroup == 0 ) + fInstanceGroups.Reset(); + } + + // Either way, return the array + return array; + } +} + +//// IDuplicateUniqueData //////////////////////////////////////////////////// +// Does the copy on all unique data--i.e. data copied for both clones and +// instances. + +void plGeometrySpan::IDuplicateUniqueData( const plGeometrySpan *source ) +{ + fCreating = source->fCreating; + fVertAccum = source->fVertAccum; + fIndexAccum = source->fIndexAccum; + + fMaterial = source->fMaterial; + fLocalToWorld = source->fLocalToWorld; + fWorldToLocal = source->fWorldToLocal; + fLocalBounds = source->fLocalBounds; + fWorldBounds = source->fWorldBounds; + fFogEnviron = source->fFogEnviron; + + fBaseMatrix = source->fBaseMatrix; + fNumMatrices = source->fNumMatrices; + fLocalUVWChans = source->fLocalUVWChans; + + fFormat = source->fFormat; + fProps = source->fProps; + fNumVerts = source->fNumVerts; + fNumIndices = source->fNumIndices; + fDecalLevel = source->fDecalLevel; + + if( source->fIndexData != nil ) + { + fIndexData = TRACKED_NEW UInt16[ fNumIndices ]; + memcpy( fIndexData, source->fIndexData, sizeof( UInt16 ) * fNumIndices ); + } + else + fIndexData = nil; + + if( source->fDiffuseRGBA ) + { + fDiffuseRGBA = TRACKED_NEW UInt32[ fNumVerts ]; + memcpy( fDiffuseRGBA, source->fDiffuseRGBA, sizeof( UInt32 ) * fNumVerts ); + } + else + fDiffuseRGBA = nil; + + if( source->fSpecularRGBA ) + { + fSpecularRGBA = TRACKED_NEW UInt32[ fNumVerts ]; + memcpy( fSpecularRGBA, source->fSpecularRGBA, sizeof( UInt32 ) * fNumVerts ); + } + else + fSpecularRGBA = nil; + + fLocalToOBB = source->fLocalToOBB; + fOBBToLocal = source->fOBBToLocal; +} + +//// CopyFrom //////////////////////////////////////////////////////////////// +// Duplicate this span from a given span. + +void plGeometrySpan::CopyFrom( const plGeometrySpan *source ) +{ + UInt32 size; + + + // Just to make sure + ClearBuffers(); + + IDuplicateUniqueData( source ); + + fInstanceRefs = nil; + fInstanceGroupID = kNoGroupID; + + if( source->fVertexData != nil ) + { + size = GetVertexSize( fFormat ); + + fVertexData = TRACKED_NEW UInt8[ size * fNumVerts ]; + memcpy( fVertexData, source->fVertexData, size * fNumVerts ); + } + else + fVertexData = nil; + + if( source->fMultColor ) + { + fMultColor = TRACKED_NEW hsColorRGBA[ fNumVerts ]; + memcpy( fMultColor, source->fMultColor, sizeof(hsColorRGBA) * fNumVerts ); + } + else + { + fMultColor = nil; + } + + if( source->fAddColor ) + { + fAddColor = TRACKED_NEW hsColorRGBA[ fNumVerts ]; + memcpy( fAddColor, source->fAddColor, sizeof(hsColorRGBA) * fNumVerts ); + } + else + { + fAddColor = nil; + } +} + +//// Read //////////////////////////////////////////////////////////////////// + +void plGeometrySpan::Read( hsStream *stream ) +{ + UInt32 size, i; + + + hsAssert( !fCreating, "Cannot read geometry span while creating" ); + + // Just to make sure + ClearBuffers(); + + fCreating = false; + + // WARNING: can't read in the material here, so hopefully the owner will set it for us... + // Same with the fog environ + + fLocalToWorld.Read( stream ); + fWorldToLocal.Read( stream ); + fLocalBounds.Read( stream ); + fWorldBounds = fLocalBounds; + fWorldBounds.Transform(&fLocalToWorld); + + fOBBToLocal.Read(stream); + fLocalToOBB.Read(stream); + + fBaseMatrix = stream->ReadSwap32(); + fNumMatrices = stream->ReadByte(); + fLocalUVWChans = stream->ReadSwap16(); + fMaxBoneIdx = stream->ReadSwap16(); + fPenBoneIdx = stream->ReadSwap16(); + + fMinDist = stream->ReadSwapScalar(); + fMaxDist = stream->ReadSwapScalar(); + + fFormat = stream->ReadByte(); + fProps = stream->ReadSwap32(); + fNumVerts = stream->ReadSwap32(); + fNumIndices = stream->ReadSwap32(); + + // FIXME MAJOR VERSION + // remove these two lines. No more patches. + stream->ReadSwap32(); + stream->ReadByte(); + + fDecalLevel = stream->ReadSwap32(); + + if( fProps & kWaterHeight ) + fWaterHeight = stream->ReadSwapScalar(); + + if( fNumVerts > 0 ) + { + size = GetVertexSize( fFormat ); + + fVertexData = TRACKED_NEW UInt8[ size * fNumVerts ]; + stream->Read( size * fNumVerts, fVertexData ); + + fMultColor = TRACKED_NEW hsColorRGBA[ fNumVerts ]; + fAddColor = TRACKED_NEW hsColorRGBA[ fNumVerts ]; + for( i = 0; i < fNumVerts; i++ ) + { + fMultColor[ i ].Read( stream ); + fAddColor[ i ].Read( stream ); + } + + fDiffuseRGBA = TRACKED_NEW UInt32[ fNumVerts ]; + fSpecularRGBA = TRACKED_NEW UInt32[ fNumVerts ]; + stream->ReadSwap32( fNumVerts, fDiffuseRGBA ); + stream->ReadSwap32( fNumVerts, fSpecularRGBA ); + } + else + { + fVertexData = nil; + fMultColor = nil; + fAddColor = nil; + fDiffuseRGBA = nil; + fSpecularRGBA = nil; + } + + if( fNumIndices > 0 ) + { + fIndexData = TRACKED_NEW UInt16[ fNumIndices ]; + stream->ReadSwap16( fNumIndices, fIndexData ); + } + else + fIndexData = nil; + + // Read the group ID, then look up our instanceRef array from it + fInstanceGroupID = stream->ReadSwap32(); + if( fInstanceGroupID != kNoGroupID ) + { + UInt32 count = stream->ReadSwap32(); + + fInstanceRefs = IGetInstanceGroup( fInstanceGroupID, count ); + fInstanceRefs->Append( this ); + hsAssert( fInstanceRefs != nil, "Cannot locate fInstanceRefs on plGeometrySpan::Read()" ); + } +} + +//// Write /////////////////////////////////////////////////////////////////// + +void plGeometrySpan::Write( hsStream *stream ) +{ + UInt32 size, i; + + + hsAssert( !fCreating, "Cannot write geometry span while creating" ); + + // WARNING: can't write out the material here, so hopefully the owner will do it for us... + // Same with the fog environ + + fLocalToWorld.Write( stream ); + fWorldToLocal.Write( stream ); + fLocalBounds.Write( stream ); + + fOBBToLocal.Write(stream); + fLocalToOBB.Write(stream); + + stream->WriteSwap32( fBaseMatrix ); + stream->WriteByte( fNumMatrices ); + stream->WriteSwap16(fLocalUVWChans); + stream->WriteSwap16(fMaxBoneIdx); + stream->WriteSwap16((UInt16)fPenBoneIdx); + + stream->WriteSwapScalar(fMinDist); + stream->WriteSwapScalar(fMaxDist); + + stream->WriteByte( fFormat ); + stream->WriteSwap32( fProps ); + stream->WriteSwap32( fNumVerts ); + stream->WriteSwap32( fNumIndices ); + + // FIXME MAJOR VERSION + // Remove these two lines. + stream->WriteSwap32(0); + stream->WriteByte(0); + + stream->WriteSwap32( fDecalLevel ); + + if( fProps & kWaterHeight ) + stream->WriteSwapScalar(fWaterHeight); + + if( fNumVerts > 0 ) + { + size = GetVertexSize( fFormat ); + + stream->Write( size * fNumVerts, fVertexData ); + + for( i = 0; i < fNumVerts; i++ ) + { + fMultColor[ i ].Write( stream ); + fAddColor[ i ].Write( stream ); + } + stream->WriteSwap32( fNumVerts, fDiffuseRGBA ); + stream->WriteSwap32( fNumVerts, fSpecularRGBA ); + } + + if( fNumIndices > 0 ) + { + stream->WriteSwap16( fNumIndices, fIndexData ); + } + + // Write the groupID as well as the count for instanceRefs. This way + stream->WriteSwap32( fInstanceGroupID ); + if( fInstanceGroupID != kNoGroupID ) + stream->WriteSwap32( fInstanceRefs->GetCount() ); + else + { + hsAssert( fInstanceRefs == nil, "Nil instanceRefs array but no group ID, non sequitur" ); + } +} + +//// GetVertexSize /////////////////////////////////////////////////////////// + +UInt32 plGeometrySpan::GetVertexSize( UInt8 format ) +{ + UInt32 size; + + + size = sizeof( float ) * ( 3 + 3 ); // pos + normal + // Taken out 8.6.2001 mcn - Diffuse and specular are now separate arrays +// size += sizeof( DWORD ) * 2; // diffuse + specular + + size += sizeof( float ) * 3 * CalcNumUVs( format ); + + switch( format & kSkinWeightMask ) + { + case kSkinNoWeights: break; + case kSkin1Weight: size += sizeof( float ) * 1; break; + case kSkin2Weights: size += sizeof( float ) * 2; break; + case kSkin3Weights: size += sizeof( float ) * 3; break; + default: hsAssert( false, "Bad weight count in GetVertexSize()" ); + } + if( format & kSkinIndices ) + size += sizeof(UInt32); + + return size; +} + +//// BeginCreate ////////////////////////////////////////////////////////////// + +void plGeometrySpan::BeginCreate( hsGMaterial *material, const hsMatrix44 &l2wMatrix, UInt8 format ) +{ + fCreating = true; + + fMaterial = material; + fLocalToWorld = l2wMatrix; + fLocalToWorld.GetInverse( &fWorldToLocal ); + fFormat = format; +} + +//// AddVertex //////////////////////////////////////////////////////////////// +// Note: uvPtrArray is an array of pointers to hsPoint3s. If a pointer is nil, +// that UV channel (and all above them) are not used. The array of pointers +// MUST be of size kMaxNumUVChannels. + +UInt16 plGeometrySpan::AddVertex( hsPoint3 *position, hsPoint3 *normal, hsColorRGBA& multColor, hsColorRGBA& addColor, + hsPoint3 **uvPtrArray, float weight1, float weight2, float weight3, UInt32 indices ) +{ + AddVertex( position, normal, 0, 0, uvPtrArray, weight1, weight2, weight3, indices ); + + int idx = fVertAccum.GetCount() - 1; + + TempVertex& vert = fVertAccum[idx]; + vert.fMultColor = multColor; + vert.fAddColor = addColor; + + + return idx; +} + +UInt16 plGeometrySpan::AddVertex( hsPoint3 *position, hsPoint3 *normal, UInt32 hexColor, UInt32 specularColor, + hsPoint3 **uvPtrArray, float weight1, float weight2, float weight3, UInt32 indices ) +{ + TempVertex vert; + int i, numWeights; + + + hsAssert( fCreating, "Calling AddVertex() on a non-creating plGeometrySpan!" ); + + // UV channels + if( uvPtrArray != nil ) + { + for( i = 0; i < kMaxNumUVChannels; i++ ) + { + if( uvPtrArray[ i ] == nil ) + break; + vert.fUVs[ i ] = *(uvPtrArray[ i ]); + } + } + else + i = 0; + hsAssert( GetNumUVs() == i, "Incorrect number of UVs passed to AddVertex()" ); + + switch( fFormat & kSkinWeightMask ) + { + case kSkin3Weights: + numWeights = 3; + vert.fWeights[ 0 ] = weight1; + vert.fWeights[ 1 ] = weight2; + vert.fWeights[ 2 ] = weight3; + vert.fIndices = indices; + break; + case kSkin2Weights: + numWeights = 2; + vert.fWeights[ 0 ] = weight1; + vert.fWeights[ 1 ] = weight2; + vert.fIndices = indices; + break; + case kSkin1Weight: + numWeights = 1; + vert.fWeights[ 0 ] = weight1; + vert.fIndices = indices; + break; + default: + case kSkinNoWeights: + numWeights = 0; + break; + } + + vert.fPosition = *position; + vert.fNormal = *normal; + vert.fColor = hexColor; + vert.fSpecularColor = specularColor; + vert.fMultColor.Set( 1.f, 1.f, 1.f, 1.f ); + vert.fAddColor.Set( 0, 0, 0, 1.f ); + + fVertAccum.Append( vert ); + return fVertAccum.GetCount() - 1; +} + +//// AddIndex Variations ////////////////////////////////////////////////////// + +void plGeometrySpan::AddIndex( UInt16 index ) +{ + hsAssert( fCreating, "Calling AddIndex() on a non-creating plGeometrySpan!" ); + + fIndexAccum.Append( index ); +} + +void plGeometrySpan::AddTriIndices( UInt16 index1, UInt16 index2, UInt16 index3 ) +{ + hsAssert( fCreating, "Calling AddTriIndices() on a non-creating plGeometrySpan!" ); + + fIndexAccum.Append( index1 ); + fIndexAccum.Append( index2 ); + fIndexAccum.Append( index3 ); +} + +//// AddTriangle ////////////////////////////////////////////////////////////// + +void plGeometrySpan::AddTriangle( hsPoint3 *vert1, hsPoint3 *vert2, hsPoint3 *vert3, UInt32 color ) +{ + hsVector3 twoTo1, twoTo3, normal; + hsPoint3 normalPt; + + + hsAssert( fCreating, "Calling AddTriangle() on a non-creating plGeometrySpan!" ); + + twoTo1.Set( vert1, vert2 ); + twoTo3.Set( vert3, vert2 ); + + normal = twoTo1 % twoTo3; + normalPt.fX = normal.fX; + normalPt.fY = normal.fY; + normalPt.fZ = normal.fZ; + + AddIndex( AddVertex( vert1, &normalPt, color, 0 ) ); + AddIndex( AddVertex( vert2, &normalPt, color, 0 ) ); + AddIndex( AddVertex( vert3, &normalPt, color, 0 ) ); +} + +//// AddVertexArray /////////////////////////////////////////////////////////// + +void plGeometrySpan::AddVertexArray( UInt32 count, hsPoint3 *positions, hsVector3 *normals, UInt32 *colors, hsPoint3* uvws, int uvwsPerVtx ) +{ + hsAssert( fCreating, "Calling AddTriIndices() on a non-creating plGeometrySpan!" ); + + + UInt32 i, dest; + + // This test actually does work, even if it's bad form... + hsAssert( GetNumUVs() == uvwsPerVtx, "Calling wrong AddVertex() for plGeometrySpan format" ); + + + dest = fVertAccum.GetCount(); + fVertAccum.SetCount( dest + count ); + for( i = 0; i < count; i++, dest++ ) + { + fVertAccum[ dest ].fPosition = positions[ i ]; + if( normals != nil ) + { + // Stupid hsPoint3...I COULD change it, but why? + fVertAccum[ dest ].fNormal.fX = normals[ i ].fX; + fVertAccum[ dest ].fNormal.fY = normals[ i ].fY; + fVertAccum[ dest ].fNormal.fZ = normals[ i ].fZ; + } + else + fVertAccum[ dest ].fNormal.Set( 0, 0, 0 ); + + if( colors != nil ) + fVertAccum[ dest ].fColor = colors[ i ]; + else + fVertAccum[ dest ].fColor = 0xffffffff; + + if( uvws && uvwsPerVtx ) + { + int j; + for( j = 0; j < uvwsPerVtx; j++ ) + fVertAccum[ dest ].fUVs[ j ] = uvws[j]; + uvws += uvwsPerVtx; + } + } +} + +//// AddIndexArray //////////////////////////////////////////////////////////// + +void plGeometrySpan::AddIndexArray( UInt32 count, UInt16 *indices ) +{ + hsAssert( fCreating, "Calling AddTriIndices() on a non-creating plGeometrySpan!" ); + + + UInt32 i, dest; + + + dest = fIndexAccum.GetCount(); + fIndexAccum.SetCount( dest + count ); + for( i = 0; i < count; i++, dest++ ) + fIndexAccum[ dest ] = indices[ i ]; +} + +//// EndCreate //////////////////////////////////////////////////////////////// + +void plGeometrySpan::EndCreate( void ) +{ + hsBounds3Ext bounds; + UInt32 i, size; + UInt8 *tempPtr; + + + hsAssert( fCreating, "Calling EndCreate() on a non-creating plGeometrySpan!" ); + + /// If we're empty, just clean up and return + if( fVertAccum.GetCount() <= 0 ) + { + delete [] fVertexData; + fVertexData = nil; + fNumVerts = 0; + + delete [] fIndexData; + fIndexData = nil; + fNumIndices = 0; + fVertAccum.Reset(); + fIndexAccum.Reset(); + + fCreating = false; + return; + } + + /// Convert vertices + bounds.MakeEmpty(); + size = GetVertexSize( fFormat ); + + if( fVertexData == nil || fNumVerts < fVertAccum.GetCount() ) + { + if( fVertexData != nil ) + delete [] fVertexData; + + fNumVerts = fVertAccum.GetCount(); + fVertexData = TRACKED_NEW UInt8[ size * fNumVerts ]; + + delete [] fMultColor; + fMultColor = TRACKED_NEW hsColorRGBA[ fNumVerts ]; + + delete [] fAddColor; + fAddColor = TRACKED_NEW hsColorRGBA[ fNumVerts ]; + + delete [] fDiffuseRGBA; + delete [] fSpecularRGBA; + fDiffuseRGBA = TRACKED_NEW UInt32[ fNumVerts ]; + fSpecularRGBA = TRACKED_NEW UInt32[ fNumVerts ]; + } + else + fNumVerts = fVertAccum.GetCount(); + + for( i = 0, tempPtr = fVertexData; i < fNumVerts; i++ ) + { + // Get position, normal, color + memcpy( tempPtr, &fVertAccum[ i ], 6 * sizeof(float) ); + tempPtr += 6 * sizeof(float); + + // Get Uvs + int numUvs = GetNumUVs(); + if( numUvs > 0 ) + { + memcpy( tempPtr, &fVertAccum[ i ].fUVs[ 0 ], numUvs * 3 * sizeof(float) ); + tempPtr += numUvs * 3 * sizeof(float); + } + + // Get Weights + if( fFormat & kSkinWeightMask ) + { + int numWeights = 0; + switch( fFormat & kSkinWeightMask ) + { + case kSkin1Weight: numWeights = 1; break; + case kSkin2Weights: numWeights = 2; break; + case kSkin3Weights: numWeights = 3; break; + default: hsAssert(false, "Garbage for weight format"); break; + } + memcpy( tempPtr, &fVertAccum[ i ].fWeights[ 0 ], numWeights * sizeof(float) ); + tempPtr += numWeights * sizeof(float); + if( fFormat & kSkinIndices ) + { + memcpy( tempPtr, &fVertAccum[ i ].fIndices, sizeof(UInt32) ); + tempPtr += sizeof(UInt32); + } + } + + fMultColor[i] = fVertAccum[i].fMultColor; + fAddColor[i] = fVertAccum[i].fAddColor; + fDiffuseRGBA[ i ] = fVertAccum[ i ].fColor; + fSpecularRGBA[ i ] = fVertAccum[ i ].fSpecularColor; + + hsPoint3 pBnd = fLocalToOBB * fVertAccum[i].fPosition; + bounds.Union(&pBnd); + } + bounds.Transform(&fOBBToLocal); + + if( fProps & kWaterHeight ) + { + AdjustBounds(bounds); + } + + fLocalBounds = bounds; + fWorldBounds = fLocalBounds; + fWorldBounds.Transform(&fLocalToWorld); + + /// Convert indices + if( fIndexAccum.GetCount() == 0 ) // Allowed for patches + { + delete [] fIndexData; + fIndexData = nil; + fNumIndices = 0; + } + else if( fIndexData == nil || fNumIndices < fIndexAccum.GetCount() ) + { + if( fIndexData != nil ) + delete [] fIndexData; + + fNumIndices = fIndexAccum.GetCount(); + fIndexData = TRACKED_NEW UInt16[ fNumIndices ]; + } + else + fNumIndices = fIndexAccum.GetCount(); + + for( i = 0; i < fNumIndices; i++ ) + fIndexData[ i ] = fIndexAccum[ i ]; + + /// Cleanup + fVertAccum.Reset(); + fIndexAccum.Reset(); + fCreating = false; +} + +void plGeometrySpan::AdjustBounds(hsBounds3Ext& bnd) const +{ + hsBounds3Ext wBnd = bnd; + wBnd.Transform(&fLocalToWorld); + + const hsScalar kMaxWaveHeight(5.f); + hsBounds3Ext rebound; + rebound.MakeEmpty(); + hsPoint3 pos = wBnd.GetMins(); + pos.fZ = fWaterHeight - kMaxWaveHeight; + rebound.Union(&pos); + pos = wBnd.GetMaxs(); + pos.fZ = fWaterHeight + kMaxWaveHeight; + rebound.Union(&pos); + bnd = rebound; + bnd.Transform(&fWorldToLocal); +} + +//// ExtractVertex //////////////////////////////////////////////////////////// +void plGeometrySpan::ExtractInitColor( UInt32 index, hsColorRGBA *multColor, hsColorRGBA *addColor) const +{ + if( multColor ) + *multColor = fMultColor[index]; + if( addColor ) + *addColor = fAddColor[index]; +} + +// Extracts the given vertex out of the vertex buffer and into the pointers +// given. + +void plGeometrySpan::ExtractVertex( UInt32 index, hsPoint3 *pos, hsVector3 *normal, hsColorRGBA *color, hsColorRGBA *specColor ) +{ + UInt32 hex, spec; + + + ExtractVertex( index, pos, normal, &hex, &spec ); + color->a = ( ( hex >> 24 ) & 0xff ) / 255.0f; + color->r = ( ( hex >> 16 ) & 0xff ) / 255.0f; + color->g = ( ( hex >> 8 ) & 0xff ) / 255.0f; + color->b = ( ( hex >> 0 ) & 0xff ) / 255.0f; + + if( specColor != nil ) + { + specColor->a = ( ( spec >> 24 ) & 0xff ) / 255.0f; + specColor->r = ( ( spec >> 16 ) & 0xff ) / 255.0f; + specColor->g = ( ( spec >> 8 ) & 0xff ) / 255.0f; + specColor->b = ( ( spec >> 0 ) & 0xff ) / 255.0f; + } +} + +//// ExtractVertex //////////////////////////////////////////////////////////// +// Hex-color version of ExtractVertex. + +void plGeometrySpan::ExtractVertex( UInt32 index, hsPoint3 *pos, hsVector3 *normal, UInt32 *color, UInt32 *specColor ) +{ + UInt8 *basePtr; + float *fPtr; + + + /// Where? + hsAssert( index < fNumVerts, "Invalid index passed to ExtractVertex()" ); + basePtr = fVertexData + index * GetVertexSize( fFormat ); + + /// Copy over point and normal + fPtr = (float *)basePtr; + pos->fX = fPtr[ 0 ]; + pos->fY = fPtr[ 1 ]; + pos->fZ = fPtr[ 2 ]; + normal->fX = fPtr[ 3 ]; + normal->fY = fPtr[ 4 ]; + normal->fZ = fPtr[ 5 ]; + fPtr += 6; + + /// Diffuse color + *color = fDiffuseRGBA[ index ]; + if( specColor != nil ) + *specColor = fSpecularRGBA[ index ]; +} + +//// ExtractUv //////////////////////////////////////////////////////////////// + +void plGeometrySpan::ExtractUv( UInt32 vIdx, UInt8 uvIdx, hsPoint3 *uv ) +{ + UInt8 *basePtr; + float *fPtr; + + + /// Where? + hsAssert( vIdx < fNumVerts, "Invalid index passed to ExtractVertex()" ); + hsAssert( uvIdx < GetNumUVs(), "Invalid UV index passed to ExtractVertex()" ); + basePtr = fVertexData + vIdx * GetVertexSize( fFormat ); + + /// Skip over point, normal, and color and specular color + basePtr += 12 + 12; + + fPtr = (float *)basePtr; + fPtr += 3 * uvIdx; + uv->fX = *fPtr++; + uv->fY = *fPtr++; + uv->fZ = *fPtr; +} + +//// ExtractWeights /////////////////////////////////////////////////////////// +// Gets the weights out of the vertex data. + +void plGeometrySpan::ExtractWeights( UInt32 vIdx, float *weightArray, UInt32 *indices ) +{ + UInt8 *basePtr; + float *fPtr; + UInt32 *dPtr; + int numWeights; + + + /// Where? + hsAssert( vIdx < fNumVerts, "Invalid index passed to ExtractVertex()" ); + basePtr = fVertexData + vIdx * GetVertexSize( fFormat ); + + /// Copy over weights + basePtr += sizeof( float ) * ( 6 + 3 * GetNumUVs() ); + fPtr = (float *)basePtr; + switch( fFormat & kSkinWeightMask ) + { + case kSkinNoWeights: hsAssert( false, "ExtractWeights() called on a span with no weights" ); break; + case kSkin1Weight: numWeights = 1; break; + case kSkin2Weights: numWeights = 2; break; + case kSkin3Weights: numWeights = 3; break; + default: hsAssert( false, "Bad number of weights in ExtractWeights()" ); + } + + memcpy( weightArray, fPtr, sizeof( float ) * numWeights ); + + if( fFormat & kSkinIndices ) + { + fPtr += numWeights; + + dPtr = (UInt32 *)fPtr; + *indices = *dPtr; + } +} + +//// StuffVertex ////////////////////////////////////////////////////////////// +// Stuffs the given vertex data into the vertex buffer. Vertex must already +// exist! + +void plGeometrySpan::StuffVertex( UInt32 index, hsPoint3 *pos, hsPoint3 *normal, hsColorRGBA *color, hsColorRGBA *specColor ) +{ + UInt8 *basePtr; + float *fPtr; + + + /// Where? + hsAssert( index < fNumVerts, "Invalid index passed to StuffVertex()" ); + basePtr = fVertexData + index * GetVertexSize( fFormat ); + + /// Copy over point and normal + fPtr = (float *)basePtr; + fPtr[ 0 ] = pos->fX; + fPtr[ 1 ] = pos->fY; + fPtr[ 2 ] = pos->fZ; + + fPtr[ 3 ] = normal->fX; + fPtr[ 4 ] = normal->fY; + fPtr[ 5 ] = normal->fZ; + fPtr += 6; + + /// Diffuse color + StuffVertex( index, color, specColor ); +} + +void plGeometrySpan::StuffVertex( UInt32 index, hsColorRGBA *color, hsColorRGBA *specColor ) +{ + UInt8 r, g, b, a; + + + /// Where? + hsAssert( index < fNumVerts, "Invalid index passed to StuffVertex()" ); + + a = (UInt8)( color->a >= 1 ? 255 : color->a <= 0 ? 0 : color->a * 255.0 ); + r = (UInt8)( color->r >= 1 ? 255 : color->r <= 0 ? 0 : color->r * 255.0 ); + g = (UInt8)( color->g >= 1 ? 255 : color->g <= 0 ? 0 : color->g * 255.0 ); + b = (UInt8)( color->b >= 1 ? 255 : color->b <= 0 ? 0 : color->b * 255.0 ); + + fDiffuseRGBA[ index ] = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | ( b ); + + if( specColor != nil ) + { + a = (UInt8)( specColor->a >= 1 ? 255 : specColor->a <= 0 ? 0 : specColor->a * 255.0 ); + r = (UInt8)( specColor->r >= 1 ? 255 : specColor->r <= 0 ? 0 : specColor->r * 255.0 ); + g = (UInt8)( specColor->g >= 1 ? 255 : specColor->g <= 0 ? 0 : specColor->g * 255.0 ); + b = (UInt8)( specColor->b >= 1 ? 255 : specColor->b <= 0 ? 0 : specColor->b * 255.0 ); + + fSpecularRGBA[ index ] = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | ( b ); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeometrySpan.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeometrySpan.h new file mode 100644 index 00000000..c5f39053 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plGeometrySpan.h @@ -0,0 +1,296 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plGeometrySpan Header // +// // +// plGeometrySpans are abstract reprentations of Plasma 2.0 geometry data. // +// They consist of a material, a transform, bounds and an abstract vertex // +// and index buffer pair. plGeometrySpans is what is fed to plDrawableIce // +// to convert into its own internal data structures; the format for the // +// vertex and index data is (or should be) identical. More or less, they // +// are identical to Ice's plIcicle, but this is more abstract (read: not // +// internal to Ice). // +// // +// Also included is a temporary hacked triMesh-to-geometrySpan[] converter // +// for everyone's convenience until triMeshes disappear. // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// Created 3.8.2001 mcn // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plGeometrySpan_h +#define _plGeometrySpan_h + + +#include "hsTemplates.h" +#include "hsBounds.h" +#include "hsMatrix44.h" +#include "hsColorRGBA.h" +#include "hsBitVector.h" + +class hsGMaterial; +class plFogEnvironment; + + +//// plGeometrySpan Class Definition ///////////////////////////////////////// + +class plGeometrySpan +{ + public: + enum + { + kMaxNumUVChannels = 8 + }; + + /// Duplication of the formats from plGBufferGroup; theoretically, they + /// could be different, but they're identical for now + enum Formats + { + kUVCountMask = 0x0f, // Problem is, we need enough bits to store the max #, which means + // we really want ( max # << 1 ) - 1 + + kSkinNoWeights = 0x00, // 0000000 + kSkin1Weight = 0x10, // 0010000 + kSkin2Weights = 0x20, // 0100000 + kSkin3Weights = 0x30, // 0110000 + kSkinWeightMask = 0x30, // 0110000 + + kSkinIndices = 0x40, // 1000000 + }; + + enum Properties + { + kPropRunTimeLight = 0x01, + kPropNoPreShade = 0x02, + kLiteMaterial = 0x00, + kLiteVtxPreshaded = 0x04, + kLiteVtxNonPreshaded = 0x08, + kLiteMask = 0x0c, + kRequiresBlending = 0x10, + kInstanced = 0x20, + kUserOwned = 0x40, + kPropNoShadow = 0x80, + kPropForceShadow = 0x100, + kDiffuseFoldedIn = 0x200, // Sometimes we want to fold the diffuse color of the material into the vertex color (but only once). + kPropReverseSort = 0x400, + kWaterHeight = 0x800, + kFirstInstance = 0x1000, + kPartialSort = 0x2000, + kVisLOS = 0x4000, + kPropNoShadowCast = 0x8000 + }; + + enum + { + kNoGroupID = 0 + }; + + // Note: these are public because this is really just a glorified + // struct; no data hiding here + hsGMaterial *fMaterial; + hsMatrix44 fLocalToWorld; + hsMatrix44 fWorldToLocal; + hsBounds3Ext fLocalBounds; + hsBounds3Ext fWorldBounds; + plFogEnvironment *fFogEnviron; + + UInt32 fBaseMatrix; + UInt8 fNumMatrices; + UInt16 fLocalUVWChans; + UInt16 fMaxBoneIdx; + UInt32 fPenBoneIdx; + + hsScalar fMinDist; + hsScalar fMaxDist; + + hsScalar fWaterHeight; + + UInt8 fFormat; + UInt32 fProps; + UInt32 fNumVerts, fNumIndices; + + /// Current vertex format: + /// float position[ 3 ]; + /// float normal[ 3 ]; + /// float uvCoords[ ][ 3 ]; + /// float weights[]; // 0-3 blending weights + /// UInt32 weightIndices; // Only if there are >= 1 blending weights + + UInt8* fVertexData; + UInt16* fIndexData; + UInt32 fDecalLevel; + + hsColorRGBA* fMultColor; + hsColorRGBA* fAddColor; + + UInt32* fDiffuseRGBA; + UInt32* fSpecularRGBA; + + mutable hsTArray* fInstanceRefs; + mutable UInt32 fInstanceGroupID; // For writing out/reading in instance refs + + // The following is only used for logging during export. It is never set + // at runtime. Don't even think about using it for anything. + const char* fMaxOwner; + + // The following is ONLY used during pack; it's so we can do a reverse lookup + // from the instanceRefs list to the correct span in the drawable + UInt32 fSpanRefIndex; + + // These two matrices are inverses of each other (duh). They are only used on computing the local + // bounds. fLocalBounds is always the bounds in the space defined by fWorldToLocal, but the bounds + // are an OBB, and the orientation of the OBB isn't necessarily the same as fLocalToWorld's axes. + // For now, it is the orientation of the pivot point in max (but might be further optimized). + hsMatrix44 fLocalToOBB; + hsMatrix44 fOBBToLocal; + + plGeometrySpan(); + plGeometrySpan( const plGeometrySpan *instance ); + ~plGeometrySpan(); + + /// UV stuff + UInt8 GetNumUVs( void ) const { return ( fFormat & kUVCountMask ); } + void SetNumUVs( UInt8 numUVs ) + { + hsAssert( numUVs < kMaxNumUVChannels, "Invalid UV count to plGeometrySpan" ); + fFormat = ( fFormat & ~kUVCountMask ) | numUVs; + } + + static UInt8 CalcNumUVs( UInt8 format ) { return ( format & kUVCountMask ); } + static UInt8 UVCountToFormat( UInt8 numUVs ) { return numUVs & kUVCountMask; } + + /// Creation functions + void BeginCreate( hsGMaterial *material, const hsMatrix44 &l2wMatrix, UInt8 format ); + + // Phasing these in... + // Note: uvArray should be a fixed array with enough pointers for the max # of uv channels. + // Any unused UVs should be nil + UInt16 AddVertex( hsPoint3 *position, hsPoint3 *normal, hsColorRGBA& multColor, hsColorRGBA& addColor, + hsPoint3 **uvPtrArray, float weight1 = -1.0f, float weight2 = -1.0f, float weight3 = -1.0f, UInt32 weightIndices = 0 ); + UInt16 AddVertex( hsPoint3 *position, hsPoint3 *normal, UInt32 hexColor, UInt32 specularColor = 0, + hsPoint3 **uvPtrArray = nil, float weight1 = -1.0f, float weight2 = -1.0f, float weight3 = -1.0f, UInt32 weightIndices = 0 ); + + void AddIndex( UInt16 index ); + void AddTriIndices( UInt16 index1, UInt16 index2, UInt16 index3 ); + void AddTriangle( hsPoint3 *vert1, hsPoint3 *vert2, hsPoint3 *vert3, UInt32 color ); + + // uvws is an array count*uvwsPerVtx long in order [uvw(s) for vtx0, uvw(s) for vtx1, ...], or is nil + void AddVertexArray( UInt32 count, hsPoint3 *positions, hsVector3 *normals, UInt32 *colors, hsPoint3 *uvws=nil, int uvwsPerVtx=0 ); + void AddIndexArray( UInt32 count, UInt16 *indices ); + + void EndCreate( void ); + + + /// Manipulation--currently only used for applying static lighting, which of course needs individual vertices + // Wrong. Also used for the interleaving of the multiple vertex data streams here into single vertex + // stream within the plGBufferGroups. mf. + void ExtractInitColor( UInt32 index, hsColorRGBA *multColor, hsColorRGBA *addColor) const; + void ExtractVertex( UInt32 index, hsPoint3 *pos, hsVector3 *normal, hsColorRGBA *color, hsColorRGBA *specColor = nil ); + void ExtractVertex( UInt32 index, hsPoint3 *pos, hsVector3 *normal, UInt32 *color, UInt32 *specColor = nil ); + void ExtractUv( UInt32 vIdx, UInt8 uvIdx, hsPoint3* uv ); + void ExtractWeights( UInt32 vIdx, float *weightArray, UInt32 *indices ); + void StuffVertex( UInt32 index, hsPoint3 *pos, hsPoint3 *normal, hsColorRGBA *color, hsColorRGBA *specColor = nil ); + void StuffVertex( UInt32 index, hsColorRGBA *color, hsColorRGBA *specColor = nil ); + + // Clear out the buffers + void ClearBuffers( void ); + + // Duplicate this span from a given span + void CopyFrom( const plGeometrySpan *source ); + + // Make this span an instance of the given span. Handles the instance ref array as well as copying over pointers + void MakeInstanceOf( const plGeometrySpan *instance ); + + // Get the size of one vertex in a span, based on a format + static UInt32 GetVertexSize( UInt8 format ); + + void Read( hsStream *stream ); + void Write( hsStream *stream ); + + static UInt32 AllocateNewGroupID() { return IAllocateNewGroupID(); } + + void BreakInstance(); + void ChangeInstance(plGeometrySpan* newInstance); + void UnInstance(); + + void AdjustBounds(hsBounds3Ext& bnd) const; + + protected: + + struct TempVertex + { + hsPoint3 fPosition; + hsPoint3 fNormal; + UInt32 fColor, fSpecularColor; + hsColorRGBA fMultColor, fAddColor; + hsPoint3 fUVs[ kMaxNumUVChannels ]; + float fWeights[ 3 ]; + UInt32 fIndices; + }; + + hsBool fCreating; + hsTArray fVertAccum; + hsTArray fIndexAccum; + + void IUnShareData(); + void IDuplicateUniqueData( const plGeometrySpan *source ); + void IClearMembers( void ); + + // Please don't yell at me. We can't write out the instanceRef pointers, and we can't write + // out keys because we're not keyed objects, and we can't be keyed objects because we need + // to be deleted eventually. So instead, we assign each geoSpan a instanceGroupID, unique + // for each instance group but identical among all geoSpans in a given group (i.e. all + // members of the instanceRef list). We write these IDs out, then on read, we rebuild the + // instanceRef arrays by using a hash table to find insert new hsTArrays at the given groupID, + // and looking up in that hash table to get pointers for each geoSpan's instanceRef array. + // THIS is because we need a way of assigning unique, unused groupIDs to each geoSpan instance + // group, and since we only need to know if the ID has been used yet, we can just use a bitVector. + // NOTE: Group IDs start at 1, not 0, because 0 is reserved for "no instance group". So subtract + // 1 from the group ID when accessing this array... + // ....Please don't yell at me :( + static hsBitVector fInstanceGroupIDFlags; + + // The following is for rebuilding the said groups on read. The sad thing is that we also + // have to write out the instanceRef array count for each geoSpan, so that when we read in + // to do the lookup here, we know that we've read everything and can dump the entry in this + // table. + static hsTArray *> fInstanceGroups; + + // THIS is so we can clear fInstanceGroups as early and as efficiently as possible; see + // the notes on IGetInstanceGroup(). + static UInt32 fHighestReadInstanceGroup; + + static UInt32 IAllocateNewGroupID( void ); + static void IClearGroupID( UInt32 groupID ); + + static hsTArray *IGetInstanceGroup( UInt32 groupID, UInt32 expectedCount ); +}; + + +#endif // _plGeometrySpan_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInstanceDrawInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInstanceDrawInterface.cpp new file mode 100644 index 00000000..0366dcfd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInstanceDrawInterface.cpp @@ -0,0 +1,232 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plInstanceDrawInterface.h" +#include "plSharedMesh.h" +#include "plMorphSequence.h" +#include "../plMessage/plReplaceGeometryMsg.h" +#include "plDrawableSpans.h" +#include "plGeometrySpan.h" +#include "../plScene/plSceneNode.h" +#include "../pnMessage/plDISpansMsg.h" +#include "hsResMgr.h" + +plInstanceDrawInterface::plInstanceDrawInterface() : plDrawInterface(), fTargetID(-1) {} + +plInstanceDrawInterface::~plInstanceDrawInterface() {} + +void plInstanceDrawInterface::Read(hsStream* stream, hsResMgr* mgr) +{ + plDrawInterface::Read(stream, mgr); + + fTargetID = stream->ReadSwap32(); + plSwapSpansRefMsg *sMsg = TRACKED_NEW plSwapSpansRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1); + mgr->ReadKeyNotifyMe(stream, sMsg, plRefFlags::kActiveRef); +} + +void plInstanceDrawInterface::Write(hsStream* stream, hsResMgr* mgr) +{ + plDrawInterface::Write(stream, mgr); + + stream->WriteSwap32(fTargetID); + mgr->WriteKey(stream, fDrawable->GetKey()); +} + +hsBool plInstanceDrawInterface::MsgReceive(plMessage* msg) +{ +#if 0 // UNUSED + // This currently isn't being used, so I'm commenting it out at + // Bob's preference. If it does come back into play, the plReplaceGeometryMsg should also + // contain an LOD field, saying which avatar LOD this is targetted for. + // Just always specifying 0 won't break anything, it just circumvents some + // optimizations the pipeline can make, so it'll look right, just slower. + plReplaceGeometryMsg *rMsg = plReplaceGeometryMsg::ConvertNoRef(msg); + if (rMsg) + { + if (rMsg->fFlags & rMsg->kAddingGeom) + AddSharedMesh(rMsg->fMesh, rMsg->fMaterial, rMsg->fFlags & rMsg->kAddToFront); + else + RemoveSharedMesh(rMsg->fMesh); + + return true; + } +#endif // UNUSED + + plSwapSpansRefMsg* refMsg = plSwapSpansRefMsg::ConvertNoRef(msg); + if (refMsg) + { + if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + fDrawable = nil; + else + fDrawable = plDrawableSpans::ConvertNoRef(refMsg->GetRef()); + return true; + } + + return plDrawInterface::MsgReceive(msg); +} + +void plInstanceDrawInterface::AddSharedMesh(plSharedMesh *mesh, hsGMaterial *mat, hsBool addToFront, int lod, hsBool partialSort) +{ + if (fDrawable == nil) + { + hsAssert(false, "Missing drawable when instancing a shared mesh. Ignoring instance."); + return; + } + +#ifdef MF_NOSHADOW_ACC + // TESTHACKERY FOLLOWS - GlassesNoShadow + UInt32 noShadHack = 0; + if( mesh->GetKey() && (strstr(mesh->GetKey()->GetName(), "lasses") || strstr(mesh->GetKey()->GetName(), "oggles")) ) + noShadHack = plGeometrySpan::kPropNoShadowCast; +#endif // MF_NOSHADOW_ACC + + int i; + for (i = 0; i < mesh->fSpans.GetCount(); i++) + { + mesh->fSpans[i]->fMaterial = mat; + + if( partialSort ) + { + mesh->fSpans[i]->fProps |= plGeometrySpan::kPartialSort; + } + else + mesh->fSpans[i]->fProps &= ~plGeometrySpan::kPartialSort; + +#ifdef MF_NOSHADOW_ACC + mesh->fSpans[i]->fProps |= noShadHack; +#endif // MF_NOSHADOW_ACC + } + + // Add the spans to the drawable + UInt32 index = (UInt32)-1; + index = fDrawable->AppendDISpans(mesh->fSpans, index, false, true, addToFront, lod); + + // Tell the drawInterface what drawable and index it wants. + UInt8 iDraw = (UInt8)GetNumDrawables(); + ISetDrawable(iDraw, fDrawable); + SetDrawableMeshIndex(iDraw, index); + SetSharedMesh(iDraw, mesh); + if (GetProperty(kDisable)) + fDrawables[iDraw]->SetProperty(fDrawableIndices[iDraw], kDisable, true); + +#ifdef HS_DEBUGGING + plDISpansMsg* diMsg = TRACKED_NEW plDISpansMsg(fDrawable->GetKey(), plDISpansMsg::kAddingSpan, index, plDISpansMsg::kLeaveEmptyDrawable); + diMsg->SetSender(GetKey()); + diMsg->Send(); +#endif + + plSharedMeshBCMsg *smMsg = TRACKED_NEW plSharedMeshBCMsg; + smMsg->SetSender(GetKey()); + smMsg->fDraw = fDrawable; + smMsg->fMesh = mesh; + smMsg->fIsAdding = true; + smMsg->Send(); + + if (mesh->fMorphSet) + { + plMorphSequence *morph = const_cast(plMorphSequence::ConvertNoRef(fOwner->GetModifierByType(plMorphSequence::Index()))); + if (morph) + { + //hsgResMgr::ResMgr()->AddViaNotify(mesh->GetKey(), TRACKED_NEW plGenRefMsg(morph->GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kPassiveRef); + morph->AddSharedMesh(mesh); + } + } +} + +void plInstanceDrawInterface::RemoveSharedMesh(plSharedMesh *mesh) +{ + UInt32 geoIndex = fMeshes.Find(mesh); + if (geoIndex != fMeshes.kMissingIndex) + { + IClearIndex((UInt8)geoIndex); + + plSharedMeshBCMsg *smMsg = TRACKED_NEW plSharedMeshBCMsg; + smMsg->SetSender(GetKey()); + smMsg->fDraw = fDrawable; + smMsg->fMesh = mesh; + smMsg->fIsAdding = false; + smMsg->Send(); + + if (mesh->fMorphSet) + { + plMorphSequence *morph = const_cast(plMorphSequence::ConvertNoRef(fOwner->GetModifierByType(plMorphSequence::Index()))); + if (morph) + { + //morph->GetKey()->Release(mesh->GetKey()); + morph->RemoveSharedMesh(mesh); + } + } + } +} + +void plInstanceDrawInterface::ICheckDrawableIndex(UInt8 which) +{ + if( which >= fMeshes.GetCount() ) + { + fMeshes.ExpandAndZero(which+1); + } + + plDrawInterface::ICheckDrawableIndex(which); +} + +void plInstanceDrawInterface::ReleaseData() +{ + fMeshes.Reset(); + + plDrawInterface::ReleaseData(); +} + +void plInstanceDrawInterface::SetSharedMesh(UInt8 which, plSharedMesh *mesh) +{ + ICheckDrawableIndex(which); + fMeshes[which] = mesh; +} + +void plInstanceDrawInterface::IClearIndex(UInt8 which) +{ + plDrawableSpans *drawable = plDrawableSpans::ConvertNoRef(fDrawables[which]); + if (drawable != nil) + { + plDISpansMsg* diMsg = TRACKED_NEW plDISpansMsg(fDrawable->GetKey(), plDISpansMsg::kRemovingSpan, fDrawableIndices[which], plDISpansMsg::kLeaveEmptyDrawable); + diMsg->SetSender(GetKey()); + diMsg->Send(); + } + + fDrawables.Remove(which); + fDrawableIndices.Remove(which); + fMeshes.Remove(which); +} + +// Temp testing - not really ideal. Check with Bob for real soln. +Int32 plInstanceDrawInterface::GetSharedMeshIndex(const plSharedMesh *mesh) const +{ + int i; + for( i = 0; i < fMeshes.GetCount(); i++ ) + { + if( fMeshes[i] == mesh ) + return i; + } + return -1; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInstanceDrawInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInstanceDrawInterface.h new file mode 100644 index 00000000..9d2753ba --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInstanceDrawInterface.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plInstanceDrawInterface_inc +#define plInstanceDrawInterface_inc + +#include "../pnSceneObject/plDrawInterface.h" + +class plDrawableSpans; +class plSharedMesh; + +class plInstanceDrawInterface : public plDrawInterface +{ +protected: + plDrawableSpans *fDrawable; + hsTArray fMeshes; + + virtual void ICheckDrawableIndex(UInt8 which); + +public: + UInt32 fTargetID; + + plInstanceDrawInterface(); + virtual ~plInstanceDrawInterface(); + + CLASSNAME_REGISTER( plInstanceDrawInterface ); + GETINTERFACE_ANY( plInstanceDrawInterface, plDrawInterface ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + void AddSharedMesh(plSharedMesh *mesh, hsGMaterial *mat, hsBool addToFront, int LOD, hsBool partialSort); + void RemoveSharedMesh(plSharedMesh *mesh); + + virtual void ReleaseData(); + virtual void SetSharedMesh(UInt8 which, plSharedMesh *mesh); + virtual void IClearIndex(UInt8 which); + plDrawableSpans *GetInstanceDrawable() const { return fDrawable; } + + Int32 GetSharedMeshIndex(const plSharedMesh *mesh) const; +}; + + +#endif // plInstanceDrawInterface_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInterMeshSmooth.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInterMeshSmooth.cpp new file mode 100644 index 00000000..91213426 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInterMeshSmooth.cpp @@ -0,0 +1,232 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plInterMeshSmooth.h" + +#include "plDrawableSpans.h" + +class EdgeBin +{ +public: + UInt16 fVtx; + UInt16 fCount; + + EdgeBin() : fVtx(0), fCount(0) {} +}; + +void plInterMeshSmooth::FindEdges(UInt32 maxVtxIdx, UInt32 nTris, UInt16* idxList, hsTArray& edgeVerts) +{ + hsTArray* bins = TRACKED_NEW hsTArray[maxVtxIdx+1]; + + hsBitVector edgeVertBits; + // For each vert pair (edge) in idxList + int i; + for( i = 0; i < nTris; i++ ) + { + int j; + for( j = 0; j < 3; j++ ) + { + int jPlus = j < 2 ? j+1 : 0; + int idx0 = idxList[i*3 + j]; + int idx1 = idxList[i*3 + jPlus]; + + int lo, hi; + + // Look in the LUT for the lower index. + if( idx0 < idx1 ) + { + lo = idx0; + hi = idx1; + } + else + { + lo = idx1; + hi = idx0; + } + + hsTArray& loBin = bins[lo]; + // In that bucket, look for the higher index. + int k; + for( k = 0; k < loBin.GetCount(); k++ ) + { + if( loBin[k].fVtx == hi ) + break; + } + + // If we find it, increment it's count, + // else add it. + if( k < loBin.GetCount() ) + { + loBin[k].fCount++; + } + else + { + EdgeBin* b = loBin.Push(); + b->fVtx = hi; + b->fCount = 1; + } + } + } + + // For each bucket in the LUT, + for( i = 0; i < maxVtxIdx+1; i++ ) + { + hsTArray& loBin = bins[i]; + // For each higher index + int j; + for( j = 0; j < loBin.GetCount(); j++ ) + { + // If the count is one, it's an edge, so set the edge bit for both indices (hi and lo) + if( 1 == loBin[j].fCount ) + { + edgeVertBits.SetBit(i); + edgeVertBits.SetBit(loBin[j].fVtx); + } + } + } + + // Now translate the bitvector to a list of indices. + for( i = 0; i < maxVtxIdx+1; i++ ) + { + if( edgeVertBits.IsBitSet(i) ) + edgeVerts.Append(i); + } + delete [] bins; +} + +void plInterMeshSmooth::FindEdges(hsTArray& sets, hsTArray* edgeVerts) +{ + int i; + for( i = 0; i < sets.GetCount(); i++ ) + { + const plSpan* span = sets[i].fDrawable->GetSpan(sets[i].fSpanIdx); + if( !(span->fTypeMask & plSpan::kIcicleSpan) ) + continue; + + UInt32 nTris = sets[i].fDrawable->CvtGetNumTris(sets[i].fSpanIdx); + UInt16* idxList = sets[i].fDrawable->CvtGetIndexList(sets[i].fSpanIdx); + UInt32 maxVertIdx = sets[i].fDrawable->CvtGetNumVerts(sets[i].fSpanIdx)-1; + + FindEdges(maxVertIdx, nTris, idxList, edgeVerts[i]); + } +} + +void plInterMeshSmooth::SmoothNormals(hsTArray& sets) +{ + hsTArray* shareVtx = TRACKED_NEW hsTArray[sets.GetCount()]; + hsTArray* edgeVerts = TRACKED_NEW hsTArray[sets.GetCount()]; + FindEdges(sets, edgeVerts); + + int i; + for( i = 0; i < sets.GetCount()-1; i++ ) + { + int j; + for( j = edgeVerts[i].GetCount()-1; j >= 0; --j ) + { + hsPoint3 pos = GetPosition(sets[i], edgeVerts[i][j]); + hsVector3 normAccum = GetNormal(sets[i], edgeVerts[i][j]);; + + shareVtx[i].Append(edgeVerts[i][j]); + + int k; + for( k = i+1; k < sets.GetCount(); k++ ) + { + FindSharedVerts(pos, sets[k], edgeVerts[k], shareVtx[k], normAccum); + } + + normAccum.Normalize(); + GetNormal(sets[i], edgeVerts[i][j]) = normAccum; + + for( k = i+1; k < sets.GetCount(); k++ ) + { + SetNormals(sets[k], shareVtx[k], normAccum); + } + + // Now remove all the shared verts (which we just processed) + // from edgeVerts so we don't process them again. + for( k = i; k < sets.GetCount(); k++ ) + { + int m; + for( m = 0; m < shareVtx[k].GetCount(); m++ ) + { + int idx = edgeVerts[k].Find(shareVtx[k][m]); + hsAssert(idx != edgeVerts[k].kMissingIndex, "Lost vertex between find and remove"); + edgeVerts[k].Remove(idx); + } + shareVtx[k].SetCount(0); + } + } + } + + delete [] shareVtx; + delete [] edgeVerts; +} + +void plInterMeshSmooth::FindSharedVerts(hsPoint3& searchPos, plSpanHandle& set, hsTArray& edgeVerts, hsTArray& shareVtx, hsVector3& normAccum) +{ + int i; + for( i = 0; i < edgeVerts.GetCount(); i++ ) + { + hsPoint3 pos = GetPosition(set, edgeVerts[i]); + hsVector3 norm = GetNormal(set, edgeVerts[i]); + if( searchPos == pos ) + { + if( norm.InnerProduct(normAccum) > fMinNormDot ) + { + shareVtx.Append(edgeVerts[i]); + normAccum += norm; + } + } + } +} + +void plInterMeshSmooth::SetNormals(plSpanHandle& set, hsTArray& shareVtx, hsVector3& norm) +{ + int i; + for( i = 0; i < shareVtx.GetCount(); i++ ) + GetNormal(set, shareVtx[i]) = norm; +} + +hsPoint3& plInterMeshSmooth::GetPosition(plSpanHandle& set, UInt16 idx) +{ + return set.fDrawable->CvtGetPosition(set.fSpanIdx, idx); +} + +hsVector3& plInterMeshSmooth::GetNormal(plSpanHandle& set, UInt16 idx) +{ + return set.fDrawable->CvtGetNormal(set.fSpanIdx, idx); +} + +void plInterMeshSmooth::SetAngle(hsScalar degs) +{ + fMinNormDot = hsCosine(hsScalarDegToRad(degs)); +} + +hsScalar plInterMeshSmooth::GetAngle() const +{ + return hsScalarRadToDeg(hsACosine(fMinNormDot)); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInterMeshSmooth.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInterMeshSmooth.h new file mode 100644 index 00000000..c1aff0ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plInterMeshSmooth.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plInterMeshSmooth_inc +#define plInterMeshSmooth_inc + +#include "hsTemplates.h" + +class plDrawableSpans; +struct hsPoint3; +struct hsVector3; + +class plSpanHandle +{ +public: + plDrawableSpans* fDrawable; + UInt32 fSpanIdx; +}; + +class plInterMeshSmooth +{ +protected: + hsScalar fMinNormDot; + + void FindEdges(UInt32 maxVtxIdx, UInt32 nTris, UInt16* idxList, hsTArray& edgeVerts); + void FindEdges(hsTArray& sets, hsTArray* edgeVerts); + void FindSharedVerts(hsPoint3& searchPos, plSpanHandle& set, hsTArray& edgeVerts, hsTArray& shareVtx, hsVector3& normAccum); + void SetNormals(plSpanHandle& set, hsTArray& shareVtx, hsVector3& norm); + hsPoint3& GetPosition(plSpanHandle& set, UInt16 idx); + hsVector3& GetNormal(plSpanHandle& set, UInt16 idx); + +public: + plInterMeshSmooth() : fMinNormDot(0.25f) {} + + void SetAngle(hsScalar degs); + hsScalar GetAngle() const; // returns degrees + + void SmoothNormals(hsTArray& sets); +}; + +#endif // plInterMeshSmooth_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphArray.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphArray.cpp new file mode 100644 index 00000000..775d3331 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphArray.cpp @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plMorphArray.h" + +plMorphArray::plMorphArray() +{ +} + +plMorphArray::~plMorphArray() +{ +} + + +// MorphArray - Apply +void plMorphArray::Apply(hsTArray& dst, hsTArray* weights /* = nil */) const +{ + // Have our choice of cache thrashing here + // We can for each delta/for each vert + // or for each vert/for each delta + // I'll go for the former, though I can't say why at the moment. + // A nice thing about the outer loop being for each delta is + // that a delta with a weight of zero can just bag it with one if. + // + // For each delta + int i; + for( i = 0; i < fDeltas.GetCount(); i++ ) + { + // delta->Apply + fDeltas[i].Apply(dst, (weights ? weights->Get(i) : -1.f)); + } +} + +void plMorphArray::Read(hsStream* s, hsResMgr* mgr) +{ + int n = s->ReadSwap32(); + fDeltas.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + fDeltas[i].Read(s, mgr); +} + +void plMorphArray::Write(hsStream* s, hsResMgr* mgr) +{ + s->WriteSwap32(fDeltas.GetCount()); + + int i; + for( i = 0; i < fDeltas.GetCount(); i++ ) + fDeltas[i].Write(s, mgr); +} + +void plMorphArray::Reset() +{ + fDeltas.Reset(); +} + +void plMorphArray::AddDelta(const plMorphDelta& delta) +{ + fDeltas.Append(delta); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphArray.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphArray.h new file mode 100644 index 00000000..8994aba3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphArray.h @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMorphArray_inc +#define plMorphArray_inc + +#include "plMorphDelta.h" + +class plMorphArray +{ +protected: + hsTArray fDeltas; +public: + plMorphArray(); + virtual ~plMorphArray(); + + void Apply(hsTArray& dst, hsTArray* weights = nil) const; + + void Read(hsStream* s, hsResMgr* mgr); + void Write(hsStream* s, hsResMgr* mgr); + + void Reset(); + void AddDelta(const plMorphDelta& delta); + + int GetNumDeltas() const { return fDeltas.GetCount(); } + hsScalar GetWeight(int iDel) { return fDeltas[iDel].GetWeight(); } + void SetWeight(int iDel, hsScalar w) { if( iDel < fDeltas.GetCount() )fDeltas[iDel].SetWeight(w); } +}; + +#endif // plMorphArray_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphDelta.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphDelta.cpp new file mode 100644 index 00000000..53eadb70 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphDelta.cpp @@ -0,0 +1,337 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plMorphDelta.h" + +#include "hsStream.h" +#include "hsMemory.h" + +#include "plAccessGeometry.h" +#include "plAccessSpan.h" +#include "plAccessVtxSpan.h" +#include "plGeometrySpan.h" + +#include "plTweak.h" + +static const hsScalar kMinWeight = 1.e-2f; + +plMorphSpan::plMorphSpan() +: fUVWs(nil), + fNumUVWChans(0) +{ +} + +plMorphSpan::~plMorphSpan() +{ + delete [] fUVWs; +} + +plMorphDelta::plMorphDelta() +: fWeight(0) +{ +} + +plMorphDelta::~plMorphDelta() +{ +} + +plMorphDelta::plMorphDelta(const plMorphDelta& src) +{ + *this = src; +} + +plMorphDelta& plMorphDelta::operator=(const plMorphDelta& src) +{ + SetNumSpans(src.GetNumSpans()); + int i; + for( i = 0; i < fSpans.GetCount(); i++ ) + { + SetDeltas(i, src.fSpans[i].fDeltas, src.fSpans[i].fNumUVWChans, src.fSpans[i].fUVWs); + } + return *this; +} + +void plMorphDelta::Apply(hsTArray& dst, hsScalar weight /* = -1.f */) const +{ + if( weight == -1.f) + weight = fWeight; // None passed in, use our stored value + + if( weight <= kMinWeight ) + return; + + // Easy + // For each span + int iSpan; + for( iSpan = 0; iSpan < fSpans.GetCount(); iSpan++ ) + { + plAccessVtxSpan& vtxDst = dst[iSpan].AccessVtx(); + + plMorphSpan& span = fSpans[iSpan]; + + // For each vertDelta + const hsPoint3* uvwDel = span.fUVWs; + int iDelta; + for( iDelta = 0; iDelta < span.fDeltas.GetCount(); iDelta++ ) + { + const plVertDelta& delta = span.fDeltas[iDelta]; + // Add delPos * wgt to position + // Add delNorm * wgt to normal + vtxDst.Position(delta.fIdx) += delta.fPos * weight; + vtxDst.Normal(delta.fIdx) += delta.fNorm * weight; + + // Leave skin weights and indices alone? + + // Skip color for now, since diffuse and specular are + // ignored on the avatar? + // // Add delDiff * wgt to diffuse + // // Add delSpec * wgt to specular + + // For each UVW + hsPoint3* uvws = vtxDst.UVWs(delta.fIdx); + int iUVW; + for( iUVW = 0; iUVW < span.fNumUVWChans; iUVW++ ) + { + // Add delUVW * wgt to uvw + *uvws += *uvwDel * weight; + uvws++; + uvwDel++; + } + } + } +} + +// MorphDelta - ComputeDeltas +void plMorphDelta::ComputeDeltas(const hsTArray& base, const hsTArray& moved) +{ + SetNumSpans(base.GetCount()); + + // For each span + { + // for( i = 0; i < numVerts; i++ ) + { + // NOTE: we want to discard zero deltas, but a + // delta in any channel forces us to save the whole thing. + // But we don't want to compare to zero (because we'll end + // up with a lot of near zero deltas), but the epsilon we + // compare to needs to be different for comparing something + // like a normal delta and a position delta. + // + // For position, normal, color and all uvws + // Calc del and delLenSq + // If any delLenSq big enough, set nonZero to true + // If nonZero + { + // Append to deltas (i, del's) + } + } + } +} + +// MorphDelta - ComputeDeltas +void plMorphDelta::ComputeDeltas(const hsTArray& base, const hsTArray& moved, const hsMatrix44& d2b, const hsMatrix44& d2bTInv) +{ + SetNumSpans(base.GetCount()); + + hsPoint3 delUVWs[8]; + + // For each span + int iSpan; + for( iSpan = 0; iSpan < base.GetCount(); iSpan++ ) + { + plAccessSpan baseAcc; + plAccessGeometry::Instance()->AccessSpanFromGeometrySpan(baseAcc, base[iSpan]); + plAccessSpan movedAcc; + plAccessGeometry::Instance()->AccessSpanFromGeometrySpan(movedAcc, moved[iSpan]); + + plAccPosNormUVWIterator baseIter(&baseAcc.AccessVtx()); + plAccPosNormUVWIterator movedIter(&movedAcc.AccessVtx()); + + + plMorphSpan& dst = fSpans[iSpan]; + + const UInt16 numUVWs = baseAcc.AccessVtx().NumUVWs(); + + hsTArray deltas; + hsTArray uvws; + deltas.SetCount(0); + uvws.SetCount(0); + + + int iVert = 0;; + for( baseIter.Begin(), movedIter.Begin(); baseIter.More(); baseIter.Advance(), movedIter.Advance() ) + { + // NOTE: we want to discard zero deltas, but a + // delta in any channel forces us to save the whole thing. + // But we don't want to compare to zero (because we'll end + // up with a lot of near zero deltas), but the epsilon we + // compare to needs to be different for comparing something + // like a normal delta and a position delta. + // + // For position, normal, color and all uvws + // Calc del and delLenSq + // If any delLenSq big enough, set nonZero to true + hsBool nonZero = false; + + // These are actually min del SQUARED. + plConst(hsScalar) kMinDelPos(1.e-4f); // From Budtpueller's Handbook of Constants + plConst(hsScalar) kMinDelNorm(3.e-2f); // About 10 degrees + plConst(hsScalar) kMinDelUVW(1.e-4f); // From BHC + hsPoint3 mPos = d2b * *movedIter.Position(); + hsVector3 delPos( &mPos, baseIter.Position()); + hsScalar delPosSq = delPos.MagnitudeSquared(); + if( delPosSq > kMinDelPos ) + nonZero = true; + else + delPos.Set(0,0,0); + + + hsVector3 delNorm = (d2bTInv * *movedIter.Normal()) - *baseIter.Normal(); + hsScalar delNormSq = delNorm.MagnitudeSquared(); + if( delNormSq > kMinDelNorm ) + nonZero = true; + else + delNorm.Set(0,0,0); + + int i; + for( i = 0; i < numUVWs; i++ ) + { + delUVWs[i] = *movedIter.UVW(i) - *baseIter.UVW(i); + hsScalar delUVWSq = delUVWs[i].MagnitudeSquared(); + if( delUVWSq > kMinDelUVW ) + nonZero = true; + else + delUVWs[i].Set(0,0,0); + } + + if( nonZero ) + { + // Append to deltas (i, del's) + plVertDelta del; + del.fIdx = iVert; + del.fPos = delPos; + del.fNorm = delNorm; + deltas.Append(del); + + for( i = 0; i < numUVWs; i++ ) + uvws.Append(delUVWs[i]); + } + else + { + nonZero = false; // Breakpoint. + } + + iVert++; + } + SetDeltas(iSpan, deltas, numUVWs, uvws.AcquireArray()); + } +} + +void plMorphDelta::SetNumSpans(int n) +{ + fSpans.Reset(); + fSpans.SetCount(n); +} + + +void plMorphDelta::AllocDeltas(int iSpan, int nDel, int nUVW) +{ + fSpans[iSpan].fDeltas.SetCount(nDel); + fSpans[iSpan].fNumUVWChans = nUVW; + + delete [] fSpans[iSpan].fUVWs; + + int uvwCnt = nDel * nUVW; + if( uvwCnt ) + fSpans[iSpan].fUVWs = TRACKED_NEW hsPoint3[uvwCnt]; + else + fSpans[iSpan].fUVWs = nil; +} + +void plMorphDelta::SetDeltas(int iSpan, const hsTArray& deltas, int numUVWChans, const hsPoint3* uvws) +{ + AllocDeltas(iSpan, deltas.GetCount(), numUVWChans); + if( deltas.GetCount() ) + { + HSMemory::BlockMove(&deltas[0], fSpans[iSpan].fDeltas.AcquireArray(), deltas.GetCount() * sizeof(plVertDelta)); + + if( numUVWChans ) + HSMemory::BlockMove(uvws, fSpans[iSpan].fUVWs, deltas.GetCount() * numUVWChans * sizeof(*uvws)); + } +} + +void plMorphDelta::Read(hsStream* s, hsResMgr* mgr) +{ + fWeight = s->ReadSwapScalar(); + + int n = s->ReadSwap32(); + SetNumSpans(n); + int iSpan; + for( iSpan = 0; iSpan < n; iSpan++ ) + { + int nDel = s->ReadSwap32(); + int nUVW = s->ReadSwap32(); + AllocDeltas(iSpan, nDel, nUVW); + if( nDel ) + { + s->Read(nDel * sizeof(plVertDelta), fSpans[iSpan].fDeltas.AcquireArray()); + if( nUVW ) + s->Read(nDel * nUVW * sizeof(hsPoint3), fSpans[iSpan].fUVWs); + } + } + +} + +void plMorphDelta::Write(hsStream* s, hsResMgr* mgr) +{ + s->WriteSwapScalar(fWeight); + + s->WriteSwap32(fSpans.GetCount()); + + int iSpan; + for( iSpan = 0; iSpan < fSpans.GetCount(); iSpan++ ) + { + int nDel = fSpans[iSpan].fDeltas.GetCount(); + int nUVW = fSpans[iSpan].fNumUVWChans; + s->WriteSwap32(nDel); + s->WriteSwap32(nUVW); + + if( nDel ) + { + // Initialize our padding here, so we don't write random data + for (int i = 0; i < nDel; i++) + { + plVertDelta& delta = fSpans[iSpan].fDeltas[i]; + delta.fPadding = 0; + } + + s->Write(nDel * sizeof(plVertDelta), fSpans[iSpan].fDeltas.AcquireArray()); + + if( nUVW ) + s->Write(nDel * nUVW * sizeof(hsPoint3), fSpans[iSpan].fUVWs); + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphDelta.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphDelta.h new file mode 100644 index 00000000..01f7973d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphDelta.h @@ -0,0 +1,92 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMorphDelta_inc +#define plMorphDelta_inc + +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "../pnFactory/plCreatable.h" + +#include "plAccessSpan.h" + +class plVertDelta +{ +public: + UInt16 fIdx; + UInt16 fPadding; + hsVector3 fPos; + hsVector3 fNorm; +}; + +class plMorphSpan +{ +public: + plMorphSpan(); + virtual ~plMorphSpan(); + + hsTArray fDeltas; + + UInt16 fNumUVWChans; + hsPoint3* fUVWs; // Length is fUVWChans*fDeltas.GetCount() (*sizeof(hsPoint3) in bytes). +}; + +class plMorphDelta : public plCreatable +{ +protected: + hsTArray fSpans; + + hsScalar fWeight; +public: + plMorphDelta(); + virtual ~plMorphDelta(); + + plMorphDelta(const plMorphDelta& src); + plMorphDelta& operator=(const plMorphDelta& src); + + CLASSNAME_REGISTER( plMorphDelta ); + GETINTERFACE_ANY( plMorphDelta, plCreatable ); + + void SetWeight(hsScalar w) { fWeight = w; } + hsScalar GetWeight() const { return fWeight; } + + void Apply(hsTArray& dst, hsScalar weight = -1.f) const; + + void ComputeDeltas(const hsTArray& base, const hsTArray& moved); + void ComputeDeltas(const hsTArray& base, const hsTArray& moved, const hsMatrix44& d2b, const hsMatrix44& d2bTInv); + + UInt32 GetNumSpans() const { return fSpans.GetCount(); } + void SetNumSpans(int n); + void SetDeltas(int iSpan, const hsTArray& deltas, int numUVWChans, const hsPoint3* uvws); // len uvws is deltas.GetCount() * numUVWChans + + void AllocDeltas(int iSpan, int nDel, int nUVW); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + +}; + +#endif // plMorphDelta_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequence.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequence.cpp new file mode 100644 index 00000000..faed4932 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequence.cpp @@ -0,0 +1,807 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plMorphSequence.h" +#include "plMorphSequenceSDLMod.h" + +#include "plAccessGeometry.h" +#include "plAccessVtxSpan.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +#include "plDrawableSpans.h" +#include "plInstanceDrawInterface.h" + +#include "hsResMgr.h" +#include "plgDispatch.h" +#include "../plMessage/plRenderMsg.h" + +#include "plSharedMesh.h" + +#include "plTweak.h" +#include "hsTimer.h" +#include "hsFastMath.h" + +/////////////////////////////////////////////////////////////////////////// + +void plMorphDataSet::Read(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Read(s, mgr); + + int n = s->ReadSwap32(); + fMorphs.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + fMorphs[i].Read(s, mgr); +} + +void plMorphDataSet::Write(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Write(s, mgr); + + s->WriteSwap32(fMorphs.GetCount()); + int i; + for( i = 0; i < fMorphs.GetCount(); i++ ) + fMorphs[i].Write(s, mgr); +} + +/////////////////////////////////////////////////////////////////////////// + +const UInt32 kChanMask = plAccessVtxSpan::kPositionMask + | plAccessVtxSpan::kNormalMask + | plAccessVtxSpan::kUVWMask; + +// Probably want another type which is just initialize/override +// so we can set the mesh to whatever base set we want, as if +// that's what got loaded from disk. +// Use Access RW. That might already be handled in the customization stuff. + +plConst(hsScalar) kMorphTime(0.5); + +class plMorphTarget +{ +public: + UInt16 fLayer; + UInt16 fDelta; + hsScalar fWeight; +}; + +hsTArray fTgtWgts; + +plMorphSequence::plMorphSequence() +: fMorphFlags(0), + fMorphSDLMod(nil), + fGlobalLayerRef(-1) +{ +} + +plMorphSequence::~plMorphSequence() +{ + DeInit(); +} + +hsBool plMorphSequence::MsgReceive(plMessage* msg) +{ + plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg); + if( rend ) + { + // For now, I'm ignoring the target weight stuff for shared meshes. + // Can always add it in later if desired. + if( fTgtWgts.GetCount() ) + { + hsScalar delWgt = hsTimer::GetDelSysSeconds() / (kMorphTime > 0 ? kMorphTime : 1.e-3f); + int i; + for( i = 0; i < fTgtWgts.GetCount(); i++ ) + { + hsScalar currWgt = GetWeight(fTgtWgts[i].fLayer, fTgtWgts[i].fDelta); + if( fTgtWgts[i].fWeight < currWgt ) + { + if( fTgtWgts[i].fWeight >= (currWgt -= delWgt) ) + currWgt = fTgtWgts[i].fWeight; + } + else if( fTgtWgts[i].fWeight > currWgt ) + { + if( fTgtWgts[i].fWeight <= (currWgt += delWgt) ) + currWgt = fTgtWgts[i].fWeight; + } + + fMorphs[fTgtWgts[i].fLayer].SetWeight(fTgtWgts[i].fDelta, currWgt); + + if( fTgtWgts[i].fWeight == currWgt ) + { + fTgtWgts.Remove(i); + i--; + } + } + ISetDirty(true); + } + + if( !(fMorphFlags & kDirty) ) + { + // We went a whole frame without getting dirty, + // we can stop refreshing now. + plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); + + return true; + } + ISetDirty(false); + if( fMorphFlags & kDirtyIndices ) + IFindIndices(); + + if( fMorphFlags & kHaveShared ) + { + IApplyShared(); + } + else + { + Apply(); + } + return true; + } + + plSharedMeshBCMsg *smMsg = plSharedMeshBCMsg::ConvertNoRef(msg); + if (smMsg) + { + if (IGetDrawInterface()->GetKey() == smMsg->GetSender() || IIsUsingDrawable(smMsg->fDraw)) + fMorphFlags |= kDirtyIndices; + } + + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + plSharedMesh *mesh = plSharedMesh::ConvertNoRef(refMsg->GetRef()); + if (mesh) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest) ) + { + AddSharedMesh(mesh); + } + else if( refMsg->GetContext() & plRefMsg::kOnReplace) + { + plSharedMesh *oldMesh = plSharedMesh::ConvertNoRef(refMsg->GetOldRef()); + if (oldMesh) + RemoveSharedMesh(oldMesh); + AddSharedMesh(mesh); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + RemoveSharedMesh(mesh); + + return true; + } + } + + return plSingleModifier::MsgReceive(msg); +} + +int plMorphSequence::GetNumLayers(plKey meshKey /* = nil */) const +{ + int index = IFindSharedMeshIndex(meshKey); + if (index < 0) + return fMorphs.GetCount(); + else + return fSharedMeshes[index].fMesh->fMorphSet->fMorphs.GetCount(); +} + +int plMorphSequence::GetNumDeltas(int iLay, plKey meshKey /* = nil */) const +{ + int index = IFindSharedMeshIndex(meshKey); + if (index < 0) + return fMorphs[iLay].GetNumDeltas(); + else + return fSharedMeshes[index].fMesh->fMorphSet->fMorphs[iLay].GetNumDeltas(); +} + +hsScalar plMorphSequence::GetWeight(int iLay, int iDel, plKey meshKey /* = nil */) const +{ + int index = IFindSharedMeshIndex(meshKey); + if (index == -1) + return fMorphs[iLay].GetWeight(iDel); + else + return fSharedMeshes[index].fArrayWeights[iLay].fDeltaWeights[iDel]; +} + +void plMorphSequence::SetWeight(int iLay, int iDel, hsScalar w, plKey meshKey /* = nil */) +{ + int index = IFindSharedMeshIndex(meshKey); + + // Only dirty if the weight isn't for a pending mesh + if(meshKey == nil || index >= 0) + ISetDirty(true); + + if (meshKey == nil) + { + if( iLay < fMorphs.GetCount() ) + { + if( kMorphTime > 0 ) + { + plMorphTarget tgt; + tgt.fLayer = iLay; + tgt.fDelta = iDel; + tgt.fWeight = w; + + fTgtWgts.Append(tgt); + } + else + { + fMorphs[iLay].SetWeight(iDel, w); + } + } + } + else if (index >= 0) + { + fSharedMeshes[index].fArrayWeights[iLay].fDeltaWeights[iDel] = w; + fSharedMeshes[index].fFlags |= plSharedMeshInfo::kInfoDirtyMesh; + if (index == fGlobalLayerRef) + ISetAllSharedToGlobal(); + } + else + { + // Setting weight for a mesh we haven't added yet (loading state) + index = IFindPendingStateIndex(meshKey); + if (index < 0) + { + fPendingStates.Push(); + index = fPendingStates.GetCount() - 1; + fPendingStates[index].fSharedMeshKey = meshKey; + } + if (fPendingStates[index].fArrayWeights.GetCount() <= iLay) + { + int had = fPendingStates[index].fArrayWeights.GetCount(); + hsTArray temp(iLay + 1); + temp = fPendingStates[index].fArrayWeights; + temp.SetCount(iLay + 1); + + fPendingStates[index].fArrayWeights.Swap(temp); + int i; + for( i = had; i < iLay+1; i++ ) + fPendingStates[index].fArrayWeights[i].fDeltaWeights.Reset(); + } + if (fPendingStates[index].fArrayWeights[iLay].fDeltaWeights.GetCount() <= iDel) + fPendingStates[index].fArrayWeights[iLay].fDeltaWeights.ExpandAndZero(iDel + 1); + + fPendingStates[index].fArrayWeights[iLay].fDeltaWeights[iDel] = w; + } +} + +void plMorphSequence::ISetDirty(hsBool on) +{ + if( on ) + { + if( !(fMorphFlags & kDirty) ) + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + fMorphFlags |= kDirty; + + // Even if we know we're already dirty, there could be new info this frame. + // Need to tell our scene object + // + // Actually, in the case of the avatar, this sends an insane flurry of messages to the server + // when we drag (or even just hold the mouse button down on) the morph scrollbars. + // These messages are completely unneccessary. We broadcast our state when we join a new age, and that's enough. + // Non-avatar related morphs (if they ever exist) will need to call DirtySynchState some special + // way. Not here. + // + //if (GetTarget(0)) + // GetTarget(0)->DirtySynchState(kSDLMorphSequence, 0); + } + else + { + fMorphFlags &= ~kDirty; + } +} + +// MorphSequence - Init +void plMorphSequence::Init() +{ + if( fMorphFlags & kHaveShared ) + { + IFindIndices(); + } + else + { + if( !GetHaveSnap() ) + { + // Take an access snapshot of all our spans + const plDrawInterface* di = IGetDrawInterface(); + + plAccessGeometry::Instance()->TakeSnapShot(di, kChanMask); + + ISetHaveSnap(true); + } + } +} + +void plMorphSequence::Activate() +{ + Init(); + ISetDirty(true); +} + +void plMorphSequence::DeActivate() +{ + if( fMorphFlags & kHaveShared ) + { + IReleaseIndices(); + } + plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); +} + +// MorphSequence - DeInit +void plMorphSequence::DeInit() +{ + if( fMorphFlags & kHaveShared ) + { + IReleaseIndices(); + } + else + { + if( GetHaveSnap() ) + { + // Release all our snapshot data + const plDrawInterface* di = IGetDrawInterface(); + + if( di ) + plAccessGeometry::Instance()->ReleaseSnapShot(di); + + ISetHaveSnap(false); + } + } +} + +void plMorphSequence::IRenormalize(hsTArray& dst) const +{ + int i; + for( i = 0; i < dst.GetCount(); i++ ) + { + hsAssert(dst[i].HasAccessVtx(), "Come on, everyone has vertices"); + plAccessVtxSpan& accVtx = dst[i].AccessVtx(); + int j; + for( j = 0; j < accVtx.VertCount(); j++ ) + { + hsFastMath::Normalize(accVtx.Normal(j)); + } + } +} + +// MorphSequence - Apply +void plMorphSequence::Apply() const +{ + const plDrawInterface* di = IGetDrawInterface(); + if( !di ) + return; + + // Reset to initial. + Reset(di); + + // We'll be accumulating into the buffer, so open RW + hsTArray dst; + plAccessGeometry::Instance()->OpenRW(di, dst); + + // For each MorphArray + int i; + for( i = 0; i < fMorphs.GetCount(); i++ ) + { + // Apply Delta + fMorphs[i].Apply(dst); + } + + IRenormalize(dst); + + // Close up the access spans + plAccessGeometry::Instance()->Close(dst); +} + +// MorphSequence - Reset to initial +void plMorphSequence::Reset(const plDrawInterface* di) const +{ + if( !di ) + { + di = IGetDrawInterface(); + } + + // Use access span RestoreSnapshot + if( di ) + plAccessGeometry::Instance()->RestoreSnapShot(di, kChanMask); +} + +const plDrawInterface* plMorphSequence::IGetDrawInterface() const +{ + plSceneObject* so = GetTarget(); + if( !so ) + return nil; + + const plDrawInterface* di = so->GetDrawInterface(); + + return di; +} + +void plMorphSequence::AddTarget(plSceneObject* so) +{ + plSingleModifier::AddTarget(so); + + if (!fMorphSDLMod) + { + fMorphSDLMod = TRACKED_NEW plMorphSequenceSDLMod; + so->AddModifier(fMorphSDLMod); + } +} + +void plMorphSequence::RemoveTarget(plSceneObject *so) +{ + plSingleModifier::RemoveTarget(so); + + if (so) + if (fMorphSDLMod) + so->RemoveModifier(fMorphSDLMod); + + delete fMorphSDLMod; + fMorphSDLMod = nil; +} + +void plMorphSequence::Read(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Read(s, mgr); + + fMorphFlags = 0; + + int n = s->ReadSwap32(); + fMorphs.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + fMorphs[i].Read(s, mgr); + + n = s->ReadSwap32(); + for( i = 0; i < n; i++) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); +} + +void plMorphSequence::Write(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Write(s, mgr); + + s->WriteSwap32(fMorphs.GetCount()); + int i; + for( i = 0; i < fMorphs.GetCount(); i++ ) + fMorphs[i].Write(s, mgr); + + s->WriteSwap32(fSharedMeshes.GetCount()); + for( i = 0; i < fSharedMeshes.GetCount(); i++ ) + mgr->WriteKey(s, fSharedMeshes[i].fMesh); +} + +// Normal sequence of calls: +// 1) on notification that meshes have changed (or activate) +// IFindIndices() - Sets up indices +// IApplyShared() - Find which mesh is active and +// IResetShared(iActive); +// IApplyShared(iActive); +// go dormant +// 2) on weight change +// SetWeight() - Register for render message and set the new weight +// 3) on render msg: +// if dirty +// IApplyShared() - Find which mesh is active and +// IResetShared(iActive); +// IApplyShared(iActive); +// else +// Unregister for render message. +// 4) on deinit +// IReleaseIndices() - Just let's us know there's nothing much to be done. +// + +void plMorphSequence::IResetShared() +{ + int i; + for (i = 0; i < fSharedMeshes.GetCount(); i++) + IResetShared(i); +} + +void plMorphSequence::IApplyShared() +{ + int i; + for (i = 0; i < fSharedMeshes.GetCount(); i++) + { + IResetShared(i); + IApplyShared(i); + } +} + +void plMorphSequence::IFindIndices() +{ + fMorphFlags &= ~kDirtyIndices; + + int i; + for( i = 0; i < fSharedMeshes.GetCount(); i++ ) + IFindIndices(i); +} + +void plMorphSequence::IReleaseIndices() +{ + int i; + for( i = 0; i < fSharedMeshes.GetCount(); i++ ) + IReleaseIndices(i); +} + + +void plMorphSequence::IApplyShared(int iShare) +{ + if( iShare >= fSharedMeshes.GetCount() || fSharedMeshes[iShare].fCurrDraw == nil) + return; + + plSharedMeshInfo& mInfo = fSharedMeshes[iShare]; + + hsTArray dst; + // Now copy each shared mesh geometryspan into the drawable + // to get it back to it's pristine condition. + int i; + for( i = 0; i < mInfo.fMesh->fSpans.GetCount(); i++ ) + { + plAccessSpan dstAcc; + plAccessGeometry::Instance()->OpenRW(mInfo.fCurrDraw, mInfo.fCurrIdx[i], dstAcc); + + dst.Append(dstAcc); + } + + // For each MorphArray + for( i = 0; i < mInfo.fMesh->fMorphSet->fMorphs.GetCount(); i++ ) + { + // Apply Delta + mInfo.fMesh->fMorphSet->fMorphs[i].Apply(dst, &mInfo.fArrayWeights[i].fDeltaWeights); + } + + IRenormalize(dst); + + // Close up the access spans + plAccessGeometry::Instance()->Close(dst); + mInfo.fFlags &= ~plSharedMeshInfo::kInfoDirtyMesh; +} + +hsBool plMorphSequence::IResetShared(int iShare) +{ + if( iShare >= fSharedMeshes.GetCount() || fSharedMeshes[iShare].fCurrDraw == nil) + return false; + + plSharedMeshInfo& mInfo = fSharedMeshes[iShare]; + + // Now copy each shared mesh geometryspan into the drawable + // to get it back to it's pristine condition. + int i; + for( i = 0; i < mInfo.fMesh->fSpans.GetCount(); i++ ) + { + plAccessSpan srcAcc; + plAccessGeometry::Instance()->AccessSpanFromGeometrySpan(srcAcc, mInfo.fMesh->fSpans[i]); + plAccessSpan dstAcc; + plAccessGeometry::Instance()->OpenRW(mInfo.fCurrDraw, mInfo.fCurrIdx[i], dstAcc); + + plAccPosNormUVWIterator srcIter(&srcAcc.AccessVtx()); + plAccPosNormUVWIterator dstIter(&dstAcc.AccessVtx()); + + const int numUVWs = srcAcc.AccessVtx().NumUVWs(); + + for( srcIter.Begin(), dstIter.Begin(); srcIter.More(); srcIter.Advance(), dstIter.Advance() ) + { + *dstIter.Position() = *srcIter.Position(); + *dstIter.Normal() = *srcIter.Normal(); + + int j; + for( j = 0; j < numUVWs; j++ ) + *dstIter.UVW(j) = *srcIter.UVW(j); + } + } + + return true; +} + +hsBool plMorphSequence::IFindIndices(int iShare) +{ + plSharedMeshInfo& mInfo = fSharedMeshes[iShare]; + mInfo.fCurrDraw = nil; // In case we fail. + + const plInstanceDrawInterface* di = plInstanceDrawInterface::ConvertNoRef(IGetDrawInterface()); + if( !di ) + return false; + + Int32 meshIdx = di->GetSharedMeshIndex(mInfo.fMesh); + if( meshIdx < 0 ) + return false; + + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable((UInt8)meshIdx)); + if( !dr ) + return false; + + mInfo.fCurrDraw = dr; + + plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex((UInt8)meshIdx)); + + hsAssert(mInfo.fMesh->fSpans.GetCount() == diIndex.GetCount(), "Mismatch between geometry and indices"); + + mInfo.fCurrIdx.SetCount(diIndex.GetCount()); + + int i; + for( i = 0; i < diIndex.GetCount(); i++ ) + mInfo.fCurrIdx[i] = diIndex[i]; + + return true; +} + +void plMorphSequence::IReleaseIndices(int iShare) +{ + plSharedMeshInfo& mInfo = fSharedMeshes[iShare]; + mInfo.fCurrDraw = nil; +} + +Int32 plMorphSequence::IFindSharedMeshIndex(plKey meshKey) const +{ + int i; + for( i = 0; i < fSharedMeshes.GetCount(); i++ ) + { + if (fSharedMeshes[i].fMesh->GetKey() == meshKey) + return i; + } + + return -1; +} + +Int32 plMorphSequence::IFindPendingStateIndex(plKey meshKey) const +{ + int i; + for( i = 0; i < fPendingStates.GetCount(); i++ ) + { + if (fPendingStates[i].fSharedMeshKey == meshKey) + return i; + } + + return -1; +} + +hsBool plMorphSequence::IIsUsingDrawable(plDrawable *draw) +{ + int i; + for (i = 0; i < fSharedMeshes.GetCount(); i++) + { + if (fSharedMeshes[i].fCurrDraw == draw) + return true; + } + + return false; +} + +void plMorphSequence::AddSharedMesh(plSharedMesh* mesh) +{ + // This happens if SDL state tells us to use a mesh without morph info + // (Either because the object used to but no longer does, or because + // the SDL is giving us the wrong key.) + if (!mesh->fMorphSet) + return; + + if (fSharedMeshes.GetCount() == 0) + plgDispatch::Dispatch()->RegisterForExactType(plSharedMeshBCMsg::Index(), GetKey()); + + if (IFindSharedMeshIndex(mesh->GetKey()) >= 0) + return; // We already have it. + + hsAssert(fSharedMeshes.GetCount() < 127, "Too many meshes for one morph sequence."); + SetUseSharedMesh(true); + int pendingIndex = IFindPendingStateIndex(mesh->GetKey()); + + plSharedMeshInfo mInfo; + mInfo.fMesh = mesh; + mInfo.fCurrDraw = nil; + + // Intialize our weights to zero. + mInfo.fArrayWeights.Reset(); + mInfo.fArrayWeights.SetCount(mesh->fMorphSet->fMorphs.GetCount()); + int i, j; + for (i = 0; i < mesh->fMorphSet->fMorphs.GetCount(); i++) + mInfo.fArrayWeights[i].fDeltaWeights.ExpandAndZero(mesh->fMorphSet->fMorphs[i].GetNumDeltas()); + + // Aha, we have some pending weights. Copy them in! + if (pendingIndex >= 0) + { + // Filter in any data that's valid + for (i = 0; i < mInfo.fArrayWeights.GetCount() && i < fPendingStates[pendingIndex].fArrayWeights.GetCount(); i++) + { + for (j = 0; j < mInfo.fArrayWeights[i].fDeltaWeights.GetCount() && + j < fPendingStates[pendingIndex].fArrayWeights[i].fDeltaWeights.GetCount(); j++) + { + mInfo.fArrayWeights[i].fDeltaWeights[j] = fPendingStates[pendingIndex].fArrayWeights[i].fDeltaWeights[j]; + } + } + + fPendingStates.Remove(pendingIndex); + + mInfo.fFlags |= plSharedMeshInfo::kInfoDirtyMesh; + ISetDirty(true); + } + + fSharedMeshes.Append(mInfo); + IFindIndices(fSharedMeshes.GetCount() - 1); + + if (mesh->fFlags & plSharedMesh::kLayer0GlobalToMod) + { + fGlobalLayerRef = fSharedMeshes.GetCount() - 1; + ISetAllSharedToGlobal(); + } + else + ISetSingleSharedToGlobal(fSharedMeshes.GetCount() - 1); +} + +void plMorphSequence::RemoveSharedMesh(plSharedMesh* mesh) +{ + int idx = IFindSharedMeshIndex(mesh->GetKey()); + if (idx < 0) + return; // Don't have it... can't remove it can we? + + fSharedMeshes.Remove(idx); + + fGlobalLayerRef = -1; + int i; + for (i = 0; i < fSharedMeshes.GetCount(); i++) + { + if (fSharedMeshes[i].fMesh->fFlags & plSharedMesh::kLayer0GlobalToMod) + { + fGlobalLayerRef = i; + break; + } + } + + if (fSharedMeshes.GetCount() == 0) + plgDispatch::Dispatch()->UnRegisterForExactType(plSharedMeshBCMsg::Index(), GetKey()); +} + +void plMorphSequence::FindMorphMods(const plSceneObject *so, hsTArray &mods) +{ + const plMorphSequence *morph = plMorphSequence::ConvertNoRef(so->GetModifierByType(plMorphSequence::Index())); + if (morph) + mods.Append(morph); + + const plCoordinateInterface *ci = so->GetCoordinateInterface(); + int i; + for (i = 0; i < ci->GetNumChildren(); i++) + FindMorphMods(ci->GetChild(i)->GetOwner(), mods); +} + +void plMorphSequence::ISetAllSharedToGlobal() +{ + int i; + for (i = 0; i < fSharedMeshes.GetCount(); i++) + { + if (i != fGlobalLayerRef) + ISetSingleSharedToGlobal(i); + } +} + +void plMorphSequence::ISetSingleSharedToGlobal(int idx) +{ + if (fGlobalLayerRef < 0) + return; + + int i; + for (i = 0; i < fSharedMeshes[fGlobalLayerRef].fArrayWeights[0].fDeltaWeights.GetCount(); i++) + SetWeight(0, i, fSharedMeshes[fGlobalLayerRef].fArrayWeights[0].fDeltaWeights[i], fSharedMeshes[idx].fMesh->GetKey()); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequence.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequence.h new file mode 100644 index 00000000..5e05f089 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequence.h @@ -0,0 +1,174 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMorphSequence_inc +#define plMorphSequence_inc + +#include "../pnModifier/plSingleModifier.h" +#include "plMorphArray.h" + +class plDrawable; +class plDrawInterface; +class plSharedMesh; +class plMorphSequenceSDLMod; + +class plMorphArrayWeights +{ +public: + hsTArray fDeltaWeights; +}; + +class plSharedMeshInfo +{ +public: + enum + { + kInfoDirtyMesh = 0x1 + }; + + plSharedMesh* fMesh; + hsTArray fCurrIdx; + plDrawable* fCurrDraw; + hsTArray fArrayWeights; + UInt8 fFlags; + + plSharedMeshInfo() : fMesh(nil), fCurrDraw(nil), fFlags(0) {} +}; + +// Keyed storage class for morph arrays/deltas +// supply your own weights. +class plMorphDataSet : public hsKeyedObject +{ +public: + hsTArray fMorphs; + + CLASSNAME_REGISTER( plMorphDataSet ); + GETINTERFACE_ANY( plMorphDataSet, hsKeyedObject ); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + +// A place to hold incoming state while we're still waiting for the +// mesh and morph data to load. +class plMorphState +{ +public: + plKey fSharedMeshKey; + hsTArray fArrayWeights; +}; + +class plMorphSequence : public plSingleModifier +{ + friend class plMorphSequenceSDLMod; + +protected: + enum + { + kDirty = 0x1, + kHaveSnap = 0x2, + kHaveShared = 0x4, + kDirtyIndices = 0x8 + }; + UInt32 fMorphFlags; + + hsTArray fMorphs; + + //Int32 fActiveMesh; // Doesn't appear to be used. + hsTArray fSharedMeshes; + hsTArray fPendingStates; + plMorphSequenceSDLMod* fMorphSDLMod; + Int8 fGlobalLayerRef; + + const plDrawInterface* IGetDrawInterface() const; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return false; } + + void ISetHaveSnap(hsBool on) { if(on)fMorphFlags |= kHaveSnap; else fMorphFlags &= ~kHaveSnap; } + void ISetDirty(hsBool on); + + hsBool IResetShared(int iShare); + void IApplyShared(int iShare); + hsBool IFindIndices(int iShare); + void IReleaseIndices(int iShare); + + void IRenormalize(hsTArray& dst) const; + + void IResetShared(); + void IReleaseIndices(); // Puts everyone inactive + void IFindIndices(); // Refresh Indicies + void IApplyShared(); // Apply whatever morphs are active + + Int32 IFindPendingStateIndex(plKey meshKey) const; // Do we have pending state for this mesh? + Int32 IFindSharedMeshIndex(plKey meshKey) const; // What's this mesh's index in our array? + hsBool IIsUsingDrawable(plDrawable *draw); // Are we actively looking at spans in this drawable? + + // Internal functions for maintaining that all meshes share the same global weight(s) (fGlobalLayerRef) + void ISetAllSharedToGlobal(); + void ISetSingleSharedToGlobal(int idx); + + +public: + plMorphSequence(); + virtual ~plMorphSequence(); + + CLASSNAME_REGISTER( plMorphSequence ); + GETINTERFACE_ANY( plMorphSequence, plSingleModifier ); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + void Init(); + void Activate(); + void DeInit(); + void DeActivate(); + + void Apply() const; + void Reset(const plDrawInterface* di=nil) const; + + int GetNumLayers(plKey meshKey = nil) const; + void AddLayer(const plMorphArray& ma) { fMorphs.Append(ma); } + + int GetNumDeltas(int iLay, plKey meshKey = nil) const; + hsScalar GetWeight(int iLay, int iDel, plKey meshKey = nil) const; + void SetWeight(int iLay, int iDel, hsScalar w, plKey meshKey = nil); + + hsBool GetHaveSnap() const { return 0 != (fMorphFlags & kHaveSnap); } + hsBool GetDirty() const { return 0 != (fMorphFlags & kDirty); } + hsBool GetUseSharedMesh() const { return 0 != (fMorphFlags & kHaveShared); } + + void SetUseSharedMesh(hsBool on) { if(on)fMorphFlags |= kHaveShared; else fMorphFlags &= ~kHaveShared; } + void AddSharedMesh(plSharedMesh* mesh); + void RemoveSharedMesh(plSharedMesh* mesh); + static void FindMorphMods(const plSceneObject *so, hsTArray &mods); + plMorphSequenceSDLMod *GetSDLMod() const { return fMorphSDLMod; } +}; + +#endif // plMorphSequence_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequenceSDLMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequenceSDLMod.cpp new file mode 100644 index 00000000..eb97c162 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequenceSDLMod.cpp @@ -0,0 +1,168 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plMorphSequenceSDLMod.h" +#include "plMorphSequence.h" +#include "plSharedMesh.h" +#include "hsResMgr.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plSDLModifierMsg.h" +#include "../plSDL/plSDL.h" + +// static vars +char plMorphSequenceSDLMod::kStrMorphArrayDescName[]="MorphArray"; +char plMorphSequenceSDLMod::kStrWeights[]="weights"; + +char plMorphSequenceSDLMod::kStrMorphSetDescName[]="MorphSet"; +char plMorphSequenceSDLMod::kStrMesh[]="mesh"; +char plMorphSequenceSDLMod::kStrArrays[]="arrays"; + +char plMorphSequenceSDLMod::kStrTargetID[]="targetID"; +char plMorphSequenceSDLMod::kStrMorphs[]="morphs"; + +void plMorphSequenceSDLMod::PutCurrentStateIn(plStateDataRecord* dstState) +{ + IPutCurrentStateIn(dstState); +} + +void plMorphSequenceSDLMod::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + plSceneObject* sobj=GetTarget(); + hsAssert(sobj, "plMorphSequenceSDLMod, nil target"); + + const plMorphSequence *kMorphMod = nil; + int i, j; + kMorphMod = plMorphSequence::ConvertNoRef(sobj->GetModifierByType(plMorphSequence::Index())); + if (!kMorphMod) + { + hsAssert(false, "Couldn't find a morph sequence."); + return; + } + plMorphSequence *morphMod = const_cast(kMorphMod); + + //dstState->FindVar(kStrTarget)->Set(morphMod->GetKey()); + + plSDStateVariable *morphSD = dstState->FindSDVar(kStrMorphs); + int numMorphs = morphMod->fSharedMeshes.GetCount() + 1; // 1 for the non-sharedMesh morph + hsTArray keys; + for (i = 0; i < numMorphs; i++) + { + if (i == morphMod->fSharedMeshes.GetCount()) + { + // the non-sharedMesh morph + if (morphMod->GetNumLayers(nil) != 0) + keys.Append(nil); + } + else + { + if (!(morphMod->fSharedMeshes[i].fMesh->fFlags & plSharedMesh::kDontSaveMorphState)) + keys.Append(morphMod->fSharedMeshes[i].fMesh->GetKey()); + } + } + if (morphSD->GetCount() != keys.GetCount()) + morphSD->Alloc(keys.GetCount()); + for (i = 0; i < keys.GetCount(); i++) + { + plKey meshKey = keys[i]; + morphSD->GetStateDataRecord(i)->FindVar(kStrMesh)->Set(meshKey); + + plSimpleStateVariable *weights = morphSD->GetStateDataRecord(i)->FindVar(kStrWeights); + int numLayers = morphMod->GetNumLayers(meshKey); + if (weights->GetCount() != numLayers) + weights->Alloc(numLayers); + + for (j = 0; j < numLayers; j++) + { + int numDeltas = morphMod->GetNumDeltas(j, meshKey); + if (numDeltas != 2) + continue; // plMorphSequenceSDLMod assumes 2 deltas (pos/neg) per layer, so that we can + // store both in a single byte + + // Translate the range [-1.0, 1.0] into a 0-255 byte + UInt8 weight = (UInt8)((1.f + morphMod->GetWeight(j, 0, meshKey) - morphMod->GetWeight(j, 1, meshKey)) * 255 / 2); + + weights->Set(&weight, j); + } + } +} + +void plMorphSequenceSDLMod::SetCurrentStateFrom(const plStateDataRecord* srcState) +{ + ISetCurrentStateFrom(srcState); +} + +void plMorphSequenceSDLMod::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + plSceneObject* sobj=GetTarget(); + hsAssert(sobj, "plMorphSequenceSDLMod, nil target"); + + if (strcmp(srcState->GetDescriptor()->GetName(), kSDLMorphSequence)) + { + hsAssert(false, "Wrong type of state data record passed into plMorphSequenceSDLMod."); + return; + } + + int i, j; + const plMorphSequence *kMorphMod = plMorphSequence::ConvertNoRef(sobj->GetModifierByType(plMorphSequence::Index())); + if (!kMorphMod) + { + hsAssert(false, "Couldn't find a morph sequence."); + return; + } + plMorphSequence *morphMod = const_cast(kMorphMod); + + plSDStateVariable *morphSD = srcState->FindSDVar(kStrMorphs); + for (i = 0; i < morphSD->GetCount(); i++) + { + plKey meshKey; + morphSD->GetStateDataRecord(i)->FindVar(kStrMesh)->Get(&meshKey); + if (meshKey && !meshKey->GetUoid().GetClassType() == plSharedMesh::Index()) + continue; + + // meshKey will be nil when dealing with non-sharedMesh data + if (meshKey) + hsgResMgr::ResMgr()->AddViaNotify(meshKey, TRACKED_NEW plGenRefMsg(morphMod->GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kPassiveRef); + + plSimpleStateVariable *weights = morphSD->GetStateDataRecord(i)->FindVar(kStrWeights); + + // Count down so that we do the high index first and the pending state struct + // of plMorphSequence only has to resize the array once. + for (j = weights->GetCount() - 1; j >= 0; j--) + { + UInt8 weight; + weights->Get(&weight, j); + hsScalar posWeight = weight * 2.f / 255.f - 1.f; + hsScalar negWeight = 0; + + if (posWeight < 0) + { + negWeight = -posWeight; + posWeight = 0; + } + morphMod->SetWeight(j, 1, negWeight, meshKey); + morphMod->SetWeight(j, 0, posWeight, meshKey); + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequenceSDLMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequenceSDLMod.h new file mode 100644 index 00000000..11b9f1e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plMorphSequenceSDLMod.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plMorphSequenceSDLMod_inc +#define plMorphSequenceSDLMod_inc + +#include "../plModifier/plSDLModifier.h" + +#include "hsTypes.h" + +// +// This modifier is responsible for sending and recving +// state for morphed objects. +// + +class plMorphSequenceSDLMod : public plSDLModifier +{ +protected: + void IPutCurrentStateIn(plStateDataRecord* dstState); + void ISetCurrentStateFrom(const plStateDataRecord* srcState); + + UInt32 IApplyModFlags(UInt32 sendFlags) + { + if (fIsAvatar) + return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState); + else + return sendFlags; + } + + bool fIsAvatar; + +public: + // var labels + static char kStrMorphArrayDescName[]; + static char kStrWeights[]; + + static char kStrMorphSetDescName[]; + static char kStrMesh[]; + static char kStrArrays[]; + + static char kStrTargetID[]; + static char kStrMorphs[]; + + + CLASSNAME_REGISTER( plMorphSequenceSDLMod ); + GETINTERFACE_ANY( plMorphSequenceSDLMod, plSDLModifier); + + plMorphSequenceSDLMod() : fIsAvatar(false) {} + + void SetCurrentStateFrom(const plStateDataRecord* srcState); + void PutCurrentStateIn(plStateDataRecord* dstState); + const char* GetSDLName() const { return kSDLMorphSequence; } + + void SetIsAvatar(bool avatar) { fIsAvatar = avatar; } +}; + +#endif // plMorphSequenceSDLMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plParticleFiller.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plParticleFiller.cpp new file mode 100644 index 00000000..9bc37947 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plParticleFiller.cpp @@ -0,0 +1,800 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" + +#include "plParticleFiller.h" + +// Core background +#include "hsTemplates.h" +#include "hsFastMath.h" +#include "plPipeline.h" +#include "plViewTransform.h" + +// Getting at the destination data +#include "../pnSceneObject/plDrawInterface.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plPipeline/plGBufferGroup.h" + +// For shading +#include "../plGLight/plLightInfo.h" + +// Getting at the source data +#include "../plParticleSystem/plParticleEmitter.h" +#include "../plParticleSystem/plParticle.h" + +static hsScalar sInvDelSecs; + +//// Local Static Stuff /////////////////////////////////////////////////////// + +/// Macros for getting/setting data in a D3D vertex buffer +#define STUFF_POINT( ptr, point ) { float *fPtr = (float *)ptr; \ + fPtr[ 0 ] = point.fX; fPtr[ 1 ] = point.fY; fPtr[ 2 ] = point.fZ; \ + ptr += sizeof( float ) * 3; } + +#define STUFF_UINT32( ptr, uint ) { UInt32 *dPtr = (UInt32 *)ptr; \ + dPtr[ 0 ] = uint; ptr += sizeof( UInt32 ); } + +#define EXTRACT_POINT( ptr, pt ) { float *fPtr = (float *)ptr; \ + pt.fX = fPtr[ 0 ]; pt.fY = fPtr[ 1 ]; pt.fZ = fPtr[ 2 ]; \ + ptr += sizeof( float ) * 3; } +#define EXTRACT_FLOAT( ptr, f ) { float *fPtr = (float *)ptr; \ + f = fPtr[ 0 ]; \ + ptr += sizeof( float ); } + +#define EXTRACT_UINT32( ptr, uint ) { UInt32 *dPtr = (UInt32 *)ptr; \ + uint = dPtr[ 0 ]; ptr += sizeof( UInt32 ); } + + +static hsScalar sCurrMinWidth = 0; + +/////////////////////////////////////////////////////////////////////////////// +//// Particles //////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// Particle Processing Inlines ////////////////////////////////////////////// +// Thanks to the beauty of C++, the internal loop of +// IFillParticlePolys would be horrendous without these inlines (with them, +// it's just slightly annoying). The goal is to make the code easier to +// maintain without loosing speed (hence the inlines, which will *hopefully* +// remove the function call overhead....) + +void inline IInlSetParticlePathFollow( const plParticleCore &particle, const hsMatrix44& viewToWorld, + hsVector3 &xVec, hsVector3 &yVec, hsVector3 &zVec ) +{ + + /// Follow path specified by interpreting orientation as a velocity vector. + + hsVector3 viewDir(&particle.fPos, &viewToWorld.GetTranslate()); + hsFastMath::NormalizeAppr(viewDir); + + zVec = viewDir; + + const hsVector3& orientation = (const hsVector3)(particle.fOrientation); + yVec = orientation - orientation.InnerProduct(viewDir) * viewDir; + hsFastMath::NormalizeAppr(yVec); + + xVec = yVec % viewDir; + hsFastMath::NormalizeAppr(xVec); + xVec *= particle.fHSize; + + yVec *= particle.fVSize; +} + +void inline IInlSetParticlePathStretch( const plParticleCore &particle, const hsMatrix44& viewToWorld, + hsVector3 &xVec, hsVector3 &yVec, hsVector3 &zVec ) +{ + + /// Follow path specified by interpreting orientation as a velocity vector. + + // Well, that's one way to do it, but it has a bit of a flaw. If the length of the + // particle doesn't match the distance it's moved each frame, you get a nasty strobing + // effect, particularly as particles move faster. One of those things they don't teach + // you in the math class you slept through. + // So what we want is for the tail of the particle this frame to be where the head of + // the particle was last frame. + // First thing changed was the orientation passed in is now the change in position for + // this frame (was the last frame's velocity). + // zVec will still be the normalized vector from the eye to the particle (in world space). + // Does zVec ever get used? Hmm, only gets used for the normal facing the camera. + // This thing gets a lot faster and cleaner when we just use the view axes to compute. + // So zVec is just -viewDir. + // yVec = orientation - orientation.InnerProduct(zVec) * zVec + // xVec = yVec % zVec + // To be really correct, you can normalize xVec, because the particle doesn't get fatter + // as it goes faster, just longer. Costs a little more, but... + // And it also raises the question of what to do with the user supplied sizes. Again, + // for correctness, we want to add the size to the length of the displacement. + // We could afford that (maybe) by doing a fast normalize on yVec, then multiplying + // by orientation.InnerProduct(yVec) + fVSize. + // So the new stuff we need here are: + // The particle (to get the size out of). + // The viewToWorld transform (we can pull the eyePt and viewDir out of that. + // Note that we could probably slim away a normalize or two, but the actual number + // of normalizes we're doing hasn't gone up, I've just moved them up from IInlSetParticlePoints(). + + hsVector3 viewDir(&particle.fPos, &viewToWorld.GetTranslate()); + hsScalar invD = hsFastMath::InvSqrtAppr(viewDir.MagnitudeSquared()); + viewDir *= invD; + + zVec = viewDir; + + const hsVector3& orientation = (const hsVector3)(particle.fOrientation); + // We don't want the projection of orientation orthogonal to viewDir here, + // it's okay (and looks better) if yVec isn't in the image plane, we just + // want to make sure that xVec is in the image plane. Might want to make + // the same change to IInlSetParticlePathFollow(), but I haven't checked + // that yet. Artifact to look for is particles starting to look goofy + // as their orientation gets close in direction to viewDir. mf + yVec = orientation; + hsFastMath::NormalizeAppr(yVec); + + xVec = yVec % viewDir; // cross product of two orthonormal vectors, no need to normalize. + + hsScalar xLen = particle.fHSize; + if( xLen * invD < sCurrMinWidth ) + xLen = sCurrMinWidth / invD; + xVec *= xLen; + + hsScalar len = yVec.InnerProduct(orientation); + // Might want to give it a little boost to overlap itself (and compensate for the massive + // transparent border the artists love). But they can do that themselves with the VSize. +// len *= 1.5f; + len += particle.fVSize; + yVec *= len * -1.f; +} + +void inline IInlSetParticlePathFlow( const plParticleCore &particle, const hsMatrix44& viewToWorld, + hsVector3 &xVec, hsVector3 &yVec, hsVector3 &zVec ) +{ + + // Okay, all the notes for SetParticlePathStretch apply here too. The only + // difference is that we're going to keep the area of the particle constant, + // so the longer it stretches, the narrower it gets orthogonal to the velocity. + + hsVector3 viewDir(&particle.fPos, &viewToWorld.GetTranslate()); + hsScalar invD = hsFastMath::InvSqrtAppr(viewDir.MagnitudeSquared()); + viewDir *= invD; + + zVec = viewDir; + + const hsVector3& orientation = (const hsVector3)(particle.fOrientation); + // We don't want the projection of orientation orthogonal to viewDir here, + // it's okay (and looks better) if yVec isn't in the image plane, we just + // want to make sure that xVec is in the image plane. Might want to make + // the same change to IInlSetParticlePathFollow(), but I haven't checked + // that yet. Artifact to look for is particles starting to look goofy + // as their orientation gets close in direction to viewDir. mf + yVec = orientation; + hsFastMath::NormalizeAppr(yVec); + + xVec = yVec % viewDir; // cross product of two orthonormal vectors, no need to normalize. + + hsScalar len = yVec.InnerProduct(orientation); + + hsScalar xLen = particle.fHSize * hsFastMath::InvSqrtAppr(1.f + len * sInvDelSecs); + if( xLen * invD < sCurrMinWidth ) + xLen = sCurrMinWidth / invD; + xVec *= xLen; + + // Might want to give it a little boost to overlap itself (and compensate for the massive + // transparent border the artists love). But they can do that themselves with the VSize. + len += particle.fVSize; + yVec *= len * -2.f; + +} + +void inline IInlSetParticleExplicit( const hsMatrix44 &viewToWorld, const plParticleCore &particle, + hsVector3 &xVec, hsVector3 &yVec, hsVector3 &zVec ) +{ + const hsVector3& orientation = (const hsVector3)(particle.fOrientation); +#if 0 // See notes below - mf + zVec.Set( 0, 0, -1 ); + yVec.Set( &orientation ); + zVec = viewToWorld * zVec; + yVec = viewToWorld * yVec; + xVec = yVec % zVec; +#else // See notes below - mf + // The above has a bit of a problem with wide field of view. All of the + // particles are facing the same direction with respect to lighting, + // even though some are to the left and some are to the right of the camera + // (when facing the center of the system). We'll also start seeing them side on + // as they get to the edge of the screen. Fortunately, it's actually faster + // to calculate the vector from camera to particle and normalize it than + // to transform the vector (0,0,-1) (though not as fast as pulling the + // camera direction directly from the viewToWorld). + hsVector3 del(&particle.fPos, &viewToWorld.GetTranslate()); + hsFastMath::NormalizeAppr(del); + zVec = del; + yVec.Set(&(viewToWorld * orientation)); + xVec = yVec % zVec; +#endif // See notes below - mf + + xVec = hsFastMath::NormalizeAppr( xVec ) * particle.fHSize; + yVec = hsFastMath::NormalizeAppr( yVec ) * particle.fVSize; +} + +void inline IInlSetParticlePoints( const hsVector3 &xVec, const hsVector3 &yVec, const plParticleCore &particle, + hsPoint3 *partPts, UInt32 &partColor ) +{ + /// Do the 4 verts for this particle + partPts[ 0 ] = partPts[ 1 ] = partPts[ 2 ] = partPts[ 3 ] = particle.fPos; + partPts[ 0 ] += xVec - yVec; + partPts[ 1 ] += -xVec - yVec; + partPts[ 2 ] += -xVec + yVec; + partPts[ 3 ] += xVec + yVec; + + partColor = particle.fColor; +} + +void inline IInlSetParticlePointsStretch( const hsVector3 &xVec, const hsVector3 &yVec, const plParticleCore &particle, + hsPoint3 *partPts, UInt32 &partColor ) +{ + /// Do the 4 verts for this particle + partPts[ 0 ] = partPts[ 1 ] = partPts[ 2 ] = partPts[ 3 ] = particle.fPos; + partPts[ 0 ] += xVec + yVec; + partPts[ 1 ] += -xVec + yVec; + partPts[ 2 ] += -xVec; + partPts[ 3 ] += xVec; + + partColor = particle.fColor; +} + +void inline IInlStuffParticle1UV( UInt8 *&destPtr, const hsPoint3 *partPts, const hsVector3 &partNorm, + const UInt32 &partColor, const plParticleCore &particle ) +{ + UInt8 j; + + + for( j = 0; j < 4; j++ ) + { + STUFF_POINT( destPtr, partPts[ j ] ); + STUFF_POINT( destPtr, partNorm ); + STUFF_UINT32( destPtr, partColor ); + STUFF_UINT32( destPtr, 0 ); + STUFF_POINT( destPtr, particle.fUVCoords[ j ] ); + } +} + +void inline IInlStuffParticleNoUVs( UInt8 *&destPtr, const hsPoint3 *partPts, const hsVector3 &partNorm, + const UInt32 &partColor ) +{ + UInt8 j; + + + for( j = 0; j < 4; j++ ) + { + STUFF_POINT( destPtr, partPts[ j ] ); + STUFF_POINT( destPtr, partNorm ); + STUFF_UINT32( destPtr, partColor ); + STUFF_UINT32( destPtr, 0 ); + } +} + +void inline IInlSetNormalViewFace( hsVector3 &partNorm, const hsVector3 &zVec ) +{ + partNorm = -zVec; +} + +void inline IInlSetNormalStrongestLight( hsVector3 &partNorm, const plParticleCore &particle, + const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight, const hsVector3 &zVec ) +{ + if( omniLight != nil ) + { + partNorm.Set( &particle.fPos, &omniLight->GetWorldPosition() ); + partNorm = -partNorm; + } + else if( directionLight != nil ) + { + partNorm = -directionLight->GetWorldDirection(); + } + else + partNorm = -zVec; + partNorm = hsFastMath::NormalizeAppr( partNorm ); +} + +void inline IInlSetNormalExplicit( hsVector3 &partNorm, const plParticleCore &particle ) +{ + partNorm = particle.fNormal; +} + +//// IIPL Functions (Internal-Inline-Particle-Loop) /////////////////////////// +// Function names go as follows: +// IIPL_u_o_n() +// where u is 1UV or 0UV (for 1 UV channel or no UV channels), +// o is OVel (for orientation-follows-velocity) or OExp (for orientation-is-explicit) +// and n is NViewFace (for normals facing view), NLite (for facing strongest light) +// or NExp (for explicit) + +#define IIPL_PROLOG \ + UInt32 i, partColor; \ + hsVector3 xVec, yVec, zVec, partNorm; \ + hsPoint3 partPts[ 4 ]; \ + for( i = 0; i < numParticles; i++ ) + +void inline IIPL_1UV_OVel_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalViewFace( partNorm, zVec ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_1UV_OVel_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, + const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_1UV_OVel_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalExplicit( partNorm, particles[ i ] ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_1UV_OStr_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalViewFace( partNorm, zVec ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_1UV_OStr_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, + const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) +{ + IIPL_PROLOG + { + IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_1UV_OStr_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalExplicit( partNorm, particles[ i ] ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_1UV_OFlo_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalViewFace( partNorm, zVec ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_1UV_OFlo_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, + const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_1UV_OFlo_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalExplicit( partNorm, particles[ i ] ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_1UV_OExp_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalViewFace( partNorm, zVec ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_1UV_OExp_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, + const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) +{ + IIPL_PROLOG + { + IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_1UV_OExp_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalExplicit( partNorm, particles[ i ] ); + IInlStuffParticle1UV( destPtr, partPts, partNorm, partColor, particles[ i ] ); + } +} + +void inline IIPL_0UV_OVel_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalViewFace( partNorm, zVec ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +void inline IIPL_0UV_OVel_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, + const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +void inline IIPL_0UV_OVel_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFollow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalExplicit( partNorm, particles[ i ] ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +void inline IIPL_0UV_OStr_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalViewFace( partNorm, zVec ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +void inline IIPL_0UV_OStr_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, + const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) +{ + IIPL_PROLOG + { + IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +void inline IIPL_0UV_OStr_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathStretch( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalExplicit( partNorm, particles[ i ] ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +void inline IIPL_0UV_OFlo_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalViewFace( partNorm, zVec ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +void inline IIPL_0UV_OFlo_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, + const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +void inline IIPL_0UV_OFlo_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticlePathFlow( particles[ i ], viewToWorld, xVec, yVec, zVec ); + IInlSetParticlePointsStretch( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalExplicit( partNorm, particles[ i ] ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +void inline IIPL_0UV_OExp_NViewFace( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalViewFace( partNorm, zVec ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +void inline IIPL_0UV_OExp_NLite( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr, + const plOmniLightInfo *omniLight, const plDirectionalLightInfo *directionLight ) +{ + IIPL_PROLOG + { + IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalStrongestLight( partNorm, particles[ i ], omniLight, directionLight, zVec ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +void inline IIPL_0UV_OExp_NExp( const UInt32 &numParticles, const plParticleCore *particles, const hsMatrix44& viewToWorld, UInt8 *&destPtr ) +{ + IIPL_PROLOG + { + IInlSetParticleExplicit( viewToWorld, particles[ i ], xVec, yVec, zVec ); + IInlSetParticlePoints( xVec, yVec, particles[ i ], partPts, partColor ); + IInlSetNormalExplicit( partNorm, particles[ i ] ); + IInlStuffParticleNoUVs( destPtr, partPts, partNorm, partColor ); + } +} + +//// IFillParticlePolys /////////////////////////////////////////////////////// +// Takes a list of particles and makes the polys for them. +#include "hsTimer.h" +#include "plProfile.h" +plProfile_CreateTimer("Fill Polys", "Particles", ParticleFillPoly); + +void plParticleFiller::FillParticles(plPipeline* pipe, plDrawableSpans* drawable, plParticleSpan* span) +{ + if (!span->fSource || span->fNumParticles <= 0) + return; + + plProfile_BeginTiming(ParticleFillPoly); + + sInvDelSecs = hsTimer::GetDelSysSeconds(); + if( sInvDelSecs > 0 ) + sInvDelSecs = 1.f / sInvDelSecs; + + const plParticleCore* particles = span->fSource->GetParticleArray(); + const UInt32 numParticles = span->fNumParticles; + + plGBufferGroup* group = drawable->GetBufferGroup(span->fGroupIdx); + + UInt8* destPtr = group->GetVertBufferData(span->fVBufferIdx); + + destPtr += span->fVStartIdx * group->GetVertexSize(); + + /// Get the z vector (pointing away from the camera) in worldspace + hsMatrix44 viewToWorld = pipe->GetCameraToWorld(); + + plOmniLightInfo* omniLight = nil; + plDirectionalLightInfo* directionLight = nil; + + /// Get strongest light, if there is one, for normal generation + if( span->GetNumLights( false ) > 0 ) + { + omniLight = plOmniLightInfo::ConvertNoRef( span->GetLight( 0, false ) ); + directionLight = plDirectionalLightInfo::ConvertNoRef( span->GetLight( 0, false ) ); + } + + /// Fill with 1 UV channel + if( group->GetNumUVs() == 1 ) + { + /// Switch on orientation + if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityBased ) + { + /// Switch on normal generation + if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) + IIPL_1UV_OVel_NViewFace( numParticles, particles, viewToWorld, destPtr ); + + else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) + IIPL_1UV_OVel_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); + + else + IIPL_1UV_OVel_NExp( numParticles, particles, viewToWorld, destPtr ); + } + else if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityStretch ) + { + sCurrMinWidth = pipe->GetViewTransform().GetOrthoWidth() / pipe->GetViewTransform().GetScreenWidth() * 0.75f; + + /// Switch on normal generation + if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) + IIPL_1UV_OStr_NViewFace( numParticles, particles, viewToWorld, destPtr ); + + else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) + IIPL_1UV_OStr_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); + + else + IIPL_1UV_OStr_NExp( numParticles, particles, viewToWorld, destPtr ); + } + else if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityFlow ) + { + sCurrMinWidth = pipe->GetViewTransform().GetOrthoWidth() / pipe->GetViewTransform().GetScreenWidth() * 0.75f; + + /// Switch on normal generation + if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) + IIPL_1UV_OFlo_NViewFace( numParticles, particles, viewToWorld, destPtr ); + + else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) + IIPL_1UV_OFlo_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); + + else + IIPL_1UV_OFlo_NExp( numParticles, particles, viewToWorld, destPtr ); + } + else // Orientation explicit + { + /// Switch on normal generation + if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) + IIPL_1UV_OExp_NViewFace( numParticles, particles, viewToWorld, destPtr ); + + else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) + IIPL_1UV_OExp_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); + + else + IIPL_1UV_OExp_NExp( numParticles, particles, viewToWorld, destPtr ); + } + } + else + /// Fill with no UV channels + { + /// Switch on orientation + if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityBased ) + { + /// Switch on normal generation + if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) + IIPL_0UV_OVel_NViewFace( numParticles, particles, viewToWorld, destPtr ); + + else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) + IIPL_0UV_OVel_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); + + else + IIPL_0UV_OVel_NExp( numParticles, particles, viewToWorld, destPtr ); + } + else if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityStretch ) + { + sCurrMinWidth = pipe->GetViewTransform().GetOrthoWidth() / pipe->GetViewTransform().GetScreenWidth() * 0.75f; + + /// Switch on normal generation + if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) + IIPL_0UV_OStr_NViewFace( numParticles, particles, viewToWorld, destPtr ); + + else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) + IIPL_0UV_OStr_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); + + else + IIPL_0UV_OStr_NExp( numParticles, particles, viewToWorld, destPtr ); + } + else if( span->fSource->fMiscFlags & plParticleEmitter::kOrientationVelocityFlow ) + { + sCurrMinWidth = pipe->GetViewTransform().GetOrthoWidth() / pipe->GetViewTransform().GetScreenWidth() * 0.75f; + + /// Switch on normal generation + if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) + IIPL_0UV_OFlo_NViewFace( numParticles, particles, viewToWorld, destPtr ); + + else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) + IIPL_0UV_OFlo_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); + + else + IIPL_0UV_OFlo_NExp( numParticles, particles, viewToWorld, destPtr ); + } + else // Orientation explicit + { + /// Switch on normal generation + if( span->fSource->fMiscFlags & plParticleEmitter::kNormalViewFacing ) + IIPL_0UV_OExp_NViewFace( numParticles, particles, viewToWorld, destPtr ); + + else if( span->fSource->fMiscFlags & plParticleEmitter::kNormalNearestLight ) + IIPL_0UV_OExp_NLite( numParticles, particles, viewToWorld, destPtr, omniLight, directionLight ); + + else + IIPL_0UV_OExp_NExp( numParticles, particles, viewToWorld, destPtr ); + } + } + + /// All done! + plProfile_EndTiming(ParticleFillPoly); +} + +void plParticleFiller::FillParticlePolys(plPipeline* pipe, plDrawInterface* di) +{ + if( !di ) + return; // should probably be an asserted error. + + plDrawableSpans* drawable = plDrawableSpans::ConvertNoRef(di->GetDrawable(0)); + if( !drawable ) + return; + + // Currently, the di always points to exactly 1 drawable with 1 span index. If + // that changes, this would just become a loop. + UInt32 diIndex = di->GetDrawableMeshIndex(0); + hsAssert(diIndex >= 0, "Bogus input to fill particles"); + + const plDISpanIndex& diSpans = drawable->GetDISpans(diIndex); + int i; + for( i = 0; i < diSpans.GetCount(); i++ ) + { + UInt32 spanIdx = diSpans[i]; + hsAssert(drawable->GetSpan(spanIdx), "Bogus input to fill particles"); + hsAssert(drawable->GetSpan(spanIdx)->fTypeMask & plSpan::kParticleSpan, "Bogus input to fill particles"); + + // Safe cast, since we just checked the type mask. + plParticleSpan* span = (plParticleSpan*)drawable->GetSpan(spanIdx); + + if( !span->fSource ) + return; // Nothing to do, it's idle. + + FillParticles(pipe, drawable, span); + } + +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plParticleFiller.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plParticleFiller.h new file mode 100644 index 00000000..afcb47e8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plParticleFiller.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plParticleFiller_inc +#define plParticleFiller_inc + +class plDrawInterface; +class plPipeline; +class plParticleSpan; +class plDrawableSpans; + +namespace plParticleFiller +{ + void FillParticles(plPipeline* pipe, plDrawableSpans* drawable, plParticleSpan* span); + void FillParticlePolys(plPipeline* pipe, plDrawInterface* di); +}; + +#endif // plParticleFiller_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plPrintShape.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plPrintShape.cpp new file mode 100644 index 00000000..7046721f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plPrintShape.cpp @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plPrintShape.h" + +#include "hsStream.h" +#include "hsResMgr.h" + +plPrintShape::plPrintShape() +: fWidth(0.5f), + fLength(1.f), + fHeight(1.f) +{ +} + +plPrintShape::~plPrintShape() +{ +} + +void plPrintShape::Read(hsStream* stream, hsResMgr* mgr) +{ + plObjInterface::Read(stream, mgr); + + fWidth = stream->ReadSwapScalar(); + fLength = stream->ReadSwapScalar(); + fHeight = stream->ReadSwapScalar(); +} + +void plPrintShape::Write(hsStream* stream, hsResMgr* mgr) +{ + plObjInterface::Write(stream, mgr); + + stream->WriteSwapScalar(fWidth); + stream->WriteSwapScalar(fLength); + stream->WriteSwapScalar(fHeight); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plPrintShape.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plPrintShape.h new file mode 100644 index 00000000..4f4846f8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plPrintShape.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPrintShape_inc +#define plPrintShape_inc + +#include "../pnSceneObject/plObjInterface.h" + +class plPrintShape : public plObjInterface +{ +protected: + hsScalar fWidth; + hsScalar fLength; + hsScalar fHeight; +public: + plPrintShape(); + virtual ~plPrintShape(); + + CLASSNAME_REGISTER( plPrintShape ); + GETINTERFACE_ANY( plPrintShape, plObjInterface ); + + + virtual Int32 GetNumProperties() const { return 1; } + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) {} + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + hsScalar GetWidth() const { return fWidth; } + hsScalar GetLength() const { return fLength; } + hsScalar GetHeight() const { return fHeight; } + + void SetWidth(hsScalar f) { fWidth = f; } + void SetLength(hsScalar f) { fLength = f; } + void SetHeight(hsScalar f) { fHeight = f; } +}; + +#endif // plPrintShape_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plProxyGen.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plProxyGen.cpp new file mode 100644 index 00000000..e191e915 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plProxyGen.cpp @@ -0,0 +1,352 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plProxyGen.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayer.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../pnMessage/plProxyDrawMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + +static hsTArray fProxyDrawables; +static hsTArray fProxyMaterials; +static UInt32 fProxyKeyCounter = 0; + +plProxyGen::plProxyGen(const hsColorRGBA& amb, const hsColorRGBA& dif, hsScalar opac) +: fProxyMsgType(0), + fProxyDraw(nil), + fProxyMat(nil) +{ + fAmbient = amb; + fColor = dif; + fAmbient.a = opac; +} + +plProxyGen::~plProxyGen() +{ + if( fProxyDraw ) + GetKey()->Release(fProxyDraw->GetKey()); + if( fProxyMat ) + GetKey()->Release(fProxyMat->GetKey()); +} + +void plProxyGen::Init(const hsKeyedObject* owner) +{ + if( !GetKey() ) + { + char buff[256]; + plLocation loc; + if( owner->GetKey() ) + { + sprintf(buff, "%s_%s_%d_%d", owner->GetKey()->GetName(), "ProxyGen", owner->GetKey()->GetUoid().GetClonePlayerID(), fProxyKeyCounter++); + loc = owner->GetKey()->GetUoid().GetLocation(); + } + else + { + sprintf( buff, "ProxyGen%d", fProxyKeyCounter++ ); + loc = plLocation::kGlobalFixedLoc; + } + + hsgResMgr::ResMgr()->NewKey( buff, this, loc ); + + plgDispatch::Dispatch()->RegisterForExactType(plProxyDrawMsg::Index(), GetKey()); + } + +} + +UInt32 plProxyGen::IGetDrawableType() const +{ + switch( fProxyMsgType & plProxyDrawMsg::kAllTypes ) + { + case plProxyDrawMsg::kLight: + return plDrawable::kLightProxy; + case plProxyDrawMsg::kPhysical: + return plDrawable::kPhysicalProxy; + case plProxyDrawMsg::kOccluder: + return plDrawable::kOccluderProxy; + case plProxyDrawMsg::kAudible: + return plDrawable::kAudibleProxy; + case plProxyDrawMsg::kCoordinate: + return plDrawable::kCoordinateProxy; + case plProxyDrawMsg::kCamera: + return plDrawable::kCameraProxy; + } + hsAssert(false, "Unknown proxy type"); + return plDrawable::kGenericProxy; +} + +UInt32 plProxyGen::IGetProxyIndex() const +{ + plKey sceneNode = IGetNode(); + UInt32 drawType = IGetDrawableType(); + + int firstNil = -1; + int firstMatch = -1; + int i; + for( i = 0; i < fProxyDrawables.GetCount(); i++ ) + { + if( fProxyDrawables[i] ) + { + if( (fProxyDrawables[i]->GetType() & drawType) + &&(fProxyDrawables[i]->GetSceneNode() == sceneNode) ) + { + return i; + } + } + else if( firstNil < 0 ) + firstNil = i; + } + if( firstNil < 0 ) + firstNil = fProxyDrawables.GetCount(); + fProxyDrawables.ExpandAndZero(firstNil+1); + + return firstNil; +} + +hsGMaterial* plProxyGen::IMakeProxyMaterial() const +{ + const hsColorRGBA& amb = fAmbient; + const hsColorRGBA& dif = fColor; + hsScalar opac = fAmbient.a; + + hsGMaterial* retVal = TRACKED_NEW hsGMaterial(); + + char buff[256]; + if( GetKey()->GetName() ) + sprintf(buff, "%s_%s", GetKey()->GetName(), "Material"); + else + strcpy(buff, "ProxyMaterial"); + hsgResMgr::ResMgr()->NewKey( buff, retVal, GetKey() ? GetKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc ); + + plLayer *lay = retVal->MakeBaseLayer(); + lay->SetRuntimeColor(dif); + lay->SetPreshadeColor(dif); + lay->SetAmbientColor(amb); + lay->SetOpacity(opac); + if( opac < 1.f ) + { + lay->SetBlendFlags(lay->GetBlendFlags() | hsGMatState::kBlendAlpha); + lay->SetMiscFlags(hsGMatState::kMiscTwoSided); + lay->SetZFlags(hsGMatState::kZNoZWrite); + } + + return retVal; +} + +hsGMaterial* plProxyGen::IFindProxyMaterial() const +{ + for (int i = 0; i < fProxyMaterials.GetCount(); i++) + { + hsGMaterial* mat = fProxyMaterials[i]; + if (!mat) + continue; + + plLayer* lay = plLayer::ConvertNoRef(mat->GetLayer(0)); + if (lay && + lay->GetAmbientColor() == fAmbient && + lay->GetRuntimeColor() == fColor && + lay->GetOpacity() == fAmbient.a) + return mat; + } + + return nil; +} + +hsGMaterial* plProxyGen::IGetProxyMaterial() const +{ + // If we already have a material, return that + if (fProxyMat) + return fProxyMat; + + // If there's one existing (for another proxy) that is setup the same as ours, use that + hsGMaterial* mat = IFindProxyMaterial(); + if (mat) + return mat; + + // Have to make a new one + mat = IMakeProxyMaterial(); + fProxyMaterials.Append(mat); + return mat; +} + +void plProxyGen::IGenerateProxy() +{ + if( !IGetNode() ) + return; + + UInt32 idx = IGetProxyIndex(); + + hsGMaterial* mat = IGetProxyMaterial(); + hsAssert(mat, "Failed to create proxy material"); + + hsBool onCreate = !fProxyDrawables[idx]; + + fProxyIndex.SetCount(0); + fProxyDrawables[idx] = ICreateProxy(mat, fProxyIndex, fProxyDrawables[idx]); + + if( fProxyDrawables[idx] && !fProxyDrawables[idx]->GetKey() ) + { + char buff[256]; + if( GetKey()->GetName() ) + sprintf(buff, "%s_%s", GetKey()->GetName(), "ProxyDrawable"); + else + strcpy(buff, "ProxyDrawable"); + + hsgResMgr::ResMgr()->NewKey( buff, fProxyDrawables[ idx ], GetKey() ? GetKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc ); + } + + if( fProxyDrawables[idx] ) + { + fProxyDrawables[idx]->SetType(IGetDrawableType()); + + IApplyProxy(idx); + + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, idx, 0); + hsgResMgr::ResMgr()->AddViaNotify(mat->GetKey(), msg, plRefFlags::kActiveRef); + fProxyMat = mat; + + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg( GetKey(), plNodeRefMsg::kOnRequest, (Int8)idx, plNodeRefMsg::kDrawable ); + hsgResMgr::ResMgr()->AddViaNotify(fProxyDrawables[idx]->GetKey(), refMsg, plRefFlags::kActiveRef); + fProxyDraw = fProxyDrawables[idx]; + } +} + +//// IApplyProxy +// Add our proxy to our scenenode for rendering. +void plProxyGen::IApplyProxy(UInt32 idx) const +{ + if( fProxyDrawables[idx] && IGetNode() && (fProxyDrawables[idx]->GetSceneNode() != IGetNode()) ) + { + fProxyDrawables[idx]->SetSceneNode(IGetNode()); + } +} + +//// IRemoveProxy +// Remove our proxy from our scenenode. +void plProxyGen::IRemoveProxy(UInt32 idx) const +{ + if( fProxyDrawables[idx] ) + { + fProxyDrawables[idx]->SetSceneNode(nil); + } +} + +/// Destroy the proxy. Duh. +void plProxyGen::IDestroyProxy() +{ + if( fProxyDraw ) + { + if( fProxyDraw->GetSceneNode() ) + fProxyDraw->SetSceneNode(nil); + GetKey()->Release(fProxyDraw->GetKey()); + fProxyDraw = nil; + } + if( fProxyMat ) + { + GetKey()->Release(fProxyMat->GetKey()); + fProxyMat = nil; + } + fProxyDrawables.Reset(); + fProxyMaterials.Reset(); +} + +hsBool plProxyGen::MsgReceive(plMessage* msg) +{ + plProxyDrawMsg* pDraw = plProxyDrawMsg::ConvertNoRef(msg); + if( pDraw && (pDraw->GetProxyFlags() & IGetProxyMsgType()) ) + { + if( pDraw->GetProxyFlags() & plProxyDrawMsg::kCreate ) + { + if( fProxyDraw == nil ) + IGenerateProxy(); + } + else if( pDraw->GetProxyFlags() & plProxyDrawMsg::kDestroy ) + { + if( fProxyDraw != nil ) + IDestroyProxy(); + } + else if( pDraw->GetProxyFlags() & plProxyDrawMsg::kToggle ) + { + if( fProxyDraw == nil ) + IGenerateProxy(); + else + IDestroyProxy(); + } + return true; + } + plNodeRefMsg* nodeRef = plNodeRefMsg::ConvertNoRef(msg); + if( nodeRef ) + { + if( nodeRef->GetContext() & (plRefMsg::kOnDestroy | plRefMsg::kOnRemove) ) + { + if( nodeRef->fWhich < fProxyDrawables.GetCount() ) + fProxyDrawables[nodeRef->fWhich] = nil; + } + return true; + } + plGenRefMsg* genMsg = plGenRefMsg::ConvertNoRef(msg); + if( genMsg ) + { + if( genMsg->GetContext() & (plRefMsg::kOnDestroy | plRefMsg::kOnRemove) ) + { + if( genMsg->fWhich < fProxyMaterials.GetCount() ) + fProxyMaterials[genMsg->fWhich] = nil; + } + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + + +void plProxyGen::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + UInt32 idx = IGetProxyIndex(); + if( fProxyDrawables[idx] ) + { + int i; + for( i = 0; i < fProxyIndex.GetCount(); i++ ) + fProxyDrawables[idx]->SetTransform(fProxyIndex[i], l2w, w2l); + } +} + +void plProxyGen::SetDisable(hsBool on) +{ + UInt32 idx = IGetProxyIndex(); + if( fProxyDrawables[idx] ) + { + int i; + for( i = 0; i < fProxyIndex.GetCount(); i++ ) + fProxyDrawables[idx]->SetNativeProperty(fProxyIndex[i], plDrawable::kPropNoDraw, on); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plProxyGen.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plProxyGen.h new file mode 100644 index 00000000..eed352f8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plProxyGen.h @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plProxyGen_inc +#define plProxyGen_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsColorRGBA.h" +#include "hsTemplates.h" + +class plDrawableSpans; +class hsGMaterial; +struct hsMatrix44; + +// +// Making your own ProxyGen suitable for visualizing class YourObject. +// +// Derive YourObjectProxy() from ProxyGen. +// YourObjectProxy() should set fProxyMsgType and the colors fAmbient,fColor, either +// in the constructor or in Init(). +// Implement IGetNode() (see below). +// Implement ICreateProxy() (see below). +// Implement a CreateProxy() member to YourObject (see below). +// Add DrawableType kYourObjectProxy to plDrawable.h +// Add console commands under Graphics.Show.YourObjectProxy. +// +// See plLightSpace and plLightProxy for examples. +// +// More details. +// ============ +// To make your own ProxyGen, you should subclass plProxyGen and override Init() +// because you'll probably want to keep a pointer to your owner (of type only you +// need to understand). +// +// The IGetNode() function lets your owner specify which plSceneNode to place the +// drawable in. That will normally just return your owner's GetSceneNode() call. +// +// ICreateProxy() let's your ProxyGen do any tweaking before calling CreateProxy() +// on your object. Your ProxyGen::ICreateProxy() could just do the work itself, but +// normally doesn't have enough protected information to really do the job. +// +// Typically your object's CreateProxy() function will just translate its internal +// data to a form suitable for the plDrawableGenerator suite of helper functions +// and return the result of a call into one of them. +// +class plProxyGen : public hsKeyedObject +{ +protected: + + hsColorRGBA fAmbient; // opacity in ambient.a + hsColorRGBA fColor; + + plDrawableSpans* fProxyDraw; + hsGMaterial* fProxyMat; + + UInt32 fProxyMsgType; + + hsTArray fProxyIndex; + + // These must be implemented by the specific type, so we know what to draw. + virtual plDrawableSpans* ICreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo=nil) = 0; // called by IGenerate + virtual plKey IGetNode() const = 0; + + // Derived type should set fProxyMsgType as one of plProxyDrawMsg::types + UInt32 IGetProxyMsgType() const { return fProxyMsgType; } + + // These are all fine by default. + UInt32 IGetProxyIndex() const; + UInt32 IGetDrawableType() const; + + virtual hsGMaterial* IMakeProxyMaterial() const; + virtual hsGMaterial* IGetProxyMaterial() const; // will make material if needed. + hsGMaterial* IFindProxyMaterial() const; + + virtual void IGenerateProxy(); + virtual void IApplyProxy(UInt32 drawIdx) const; // called by IGenerate + virtual void IRemoveProxy(UInt32 drawIdx) const; + virtual void IDestroyProxy(); +public: + plProxyGen(const hsColorRGBA& amb, const hsColorRGBA& dif, hsScalar opac); + virtual ~plProxyGen(); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + virtual void SetDisable(hsBool on); + + virtual void Init(const hsKeyedObject* owner); + + virtual hsBool MsgReceive(plMessage* msg); + +}; + + +#endif // plProxyGen_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plRipVSConsts.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plRipVSConsts.h new file mode 100644 index 00000000..1050e095 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plRipVSConsts.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRipVSConsts_inc +#define plRipVSConsts_inc + +class plRipVSConsts +{ +public: + hsScalar fC1U; + hsScalar fC2U; + hsScalar fC1V; + hsScalar fC2V; + + hsScalar fInitAtten; + hsScalar fLife; + hsScalar fDecay; + + hsScalar fRamp; +}; + + +#endif // plRipVSConsts_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSharedMesh.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSharedMesh.cpp new file mode 100644 index 00000000..96ed402b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSharedMesh.cpp @@ -0,0 +1,121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsResMgr.h" +#include "hsTemplates.h" +#include "plSharedMesh.h" +#include "plGeometrySpan.h" +#include "plInstanceDrawInterface.h" +#include "plDrawableSpans.h" +#include "plMorphSequence.h" + +plSharedMesh::plSharedMesh() : fMorphSet(nil), fFlags(0) +{ +} + +plSharedMesh::~plSharedMesh() +{ + hsAssert(fActiveInstances.GetCount() == 0, "Tried to delete a shared mesh that has active instances."); + + while (fSpans.GetCount() > 0) + delete fSpans.Pop(); +} +/* +void plSharedMesh::CreateInstance(plSceneObject *so, UInt8 boneIndex) +{ +plDrawInterface *di = so->GetVolatileDrawInterface(); + + // hsAssert((fActiveInstances.GetCount == 0) || + // (di->GetDrawable() == fActiveInstances[0]->GetDrawInterface()->GetDrawable()), + // "Trying to share a mesh between two seperate drawables. No can do."); + + + fActiveInstances.Append(so); + } + + void plSharedMesh::RemoveInstance(plSceneObject *so) + { + so->GetVolatileDrawInterface()->ReleaseData(); + + fActiveInstances.RemoveItem(so); + } +*/ + +hsBool plSharedMesh::MsgReceive(plMessage* msg) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + plMorphDataSet *set = plMorphDataSet::ConvertNoRef(refMsg->GetRef()); + if (set) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fMorphSet = plMorphDataSet::ConvertNoRef(refMsg->GetRef()); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + fMorphSet = nil; + } + return true; + } + } + + return hsKeyedObject::MsgReceive(msg); +} + +// Currently, active instances are not meant to be created at export and written to disk. +void plSharedMesh::Read(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Read(s, mgr); + + int i; + fSpans.SetCount(s->ReadSwap32()); + for (i = 0; i < fSpans.GetCount(); i++) + { + fSpans[i] = TRACKED_NEW plGeometrySpan; + fSpans[i]->Read(s); + } + + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); + fFlags = s->ReadByte(); +} + +void plSharedMesh::Write(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Write(s, mgr); + + int i; + s->WriteSwap32(fSpans.GetCount()); + for (i = 0; i < fSpans.GetCount(); i++) + fSpans[i]->Write(s); + + mgr->WriteKey(s, (fMorphSet ? fMorphSet->GetKey() : nil)); + s->WriteByte(fFlags); +} + +////////////////////////////////////////////////////////////////////////////////////// + +plSharedMeshBCMsg::plSharedMeshBCMsg() : plMessage(), fMesh(nil), fIsAdding(true) { SetBCastFlag(plMessage::kBCastByExactType); } diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSharedMesh.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSharedMesh.h new file mode 100644 index 00000000..2fd828cd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSharedMesh.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLSHAREDMESH_INC +#define PLSHAREDMESH_INC + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnMessage/plMessage.h" + +class plGeometrySpan; +class plDrawableSpans; +class plSceneObject; +class hsStream; +class hsResMgr; +class plMorphDataSet; + +class plSharedMesh : public hsKeyedObject +{ +public: + enum + { + kDontSaveMorphState = 0x1, // Don't save state (duh). Used for a morph that only has global layers + kLayer0GlobalToMod = 0x2 // This mesh's weight for layer 0 should be applied to all meshes on + // the same morph mod. + }; + + hsTArrayfSpans; + hsTArray fActiveInstances; + plMorphDataSet *fMorphSet; + UInt8 fFlags; + + plSharedMesh(); + ~plSharedMesh(); + + void CreateInstance(plSceneObject *so, UInt8 boneIndex); + void RemoveInstance(plSceneObject *so); + + CLASSNAME_REGISTER( plSharedMesh ); + GETINTERFACE_ANY( plSharedMesh, hsKeyedObject ); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + +class plSharedMeshBCMsg : public plMessage +{ +public: + plDrawableSpans *fDraw; + plSharedMesh *fMesh; + hsBool fIsAdding; + + plSharedMeshBCMsg(); + ~plSharedMeshBCMsg() {} + + CLASSNAME_REGISTER( plSharedMeshBCMsg ); + GETINTERFACE_ANY( plSharedMeshBCMsg, plMessage ); + + virtual void Read(hsStream* s, hsResMgr* mgr) {} + virtual void Write(hsStream* s, hsResMgr* mgr) {} +}; + + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTree.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTree.cpp new file mode 100644 index 00000000..f0eb51c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTree.cpp @@ -0,0 +1,582 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plSpaceTree.h" +#include "hsStream.h" +#include "hsBitVector.h" +#include "plProfile.h" + +#include "../plIntersect/plVolumeIsect.h" +#include "../plMath/hsRadixSort.h" + +static hsBitVector scratchTotVec; +static hsBitVector scratchBitVec; +static hsTArray scratchList; +static hsTArray scratchSort; + +plProfile_CreateCounter("Harvest Leaves", "Draw", HarvestLeaves); + +void plSpaceTreeNode::Read(hsStream* s) +{ + fWorldBounds.Read(s); + + fFlags = s->ReadSwap16(); + fParent = s->ReadSwap16(); + fChildren[0] = s->ReadSwap16(); + fChildren[1] = s->ReadSwap16(); + +} + +void plSpaceTreeNode::Write(hsStream* s) +{ + fWorldBounds.Write(s); + + s->WriteSwap16(fFlags); + s->WriteSwap16(fParent); + s->WriteSwap16(fChildren[0]); + if( fFlags & kIsLeaf ) + // Temp for now to play nice with binary patches + s->WriteSwap16( 0 ); + else + s->WriteSwap16(fChildren[1]); +} + +plSpaceTree::plSpaceTree() +: fCullFunc(nil), + fNumLeaves(0), + fCache(nil) +{ +} + +plSpaceTree::~plSpaceTree() +{ +} + +void plSpaceTree::IRefreshRecur(Int16 which) +{ + plSpaceTreeNode& sub = fTree[which]; + + if( sub.fFlags & plSpaceTreeNode::kIsLeaf ) + { + sub.fFlags &= ~plSpaceTreeNode::kDirty; + return; + } + + if( sub.fFlags & plSpaceTreeNode::kDirty ) + { + IRefreshRecur(sub.fChildren[0]); + IRefreshRecur(sub.fChildren[1]); + + sub.fWorldBounds.MakeEmpty(); + if( !(fTree[sub.fChildren[0]].fFlags & plSpaceTreeNode::kDisabled) ) + sub.fWorldBounds.Union(&fTree[sub.fChildren[0]].fWorldBounds); + if( !(fTree[sub.fChildren[1]].fFlags & plSpaceTreeNode::kDisabled) ) + sub.fWorldBounds.Union(&fTree[sub.fChildren[1]].fWorldBounds); + + sub.fFlags &= ~plSpaceTreeNode::kDirty; + } +} + +void plSpaceTree::Refresh() +{ + if( !IsEmpty() ) + IRefreshRecur(fRoot); +} + +void plSpaceTree::SetTreeFlag(UInt16 f, hsBool on) +{ + if( IsEmpty() ) + return; + + if( !on ) + { + ClearTreeFlag(f); + return; + } + + int i; + for( i = 0; i < fTree.GetCount(); i++ ) + fTree[i].fFlags |= f; +} + +void plSpaceTree::ClearTreeFlag(UInt16 f) +{ + if( IsEmpty() ) + return; + + int i; + for( i = 0; i < fTree.GetCount(); i++ ) + fTree[i].fFlags &= ~f; +} + +void plSpaceTree::SetLeafFlag(Int16 idx, UInt16 f, hsBool on) +{ + if( IsEmpty() ) + return; + + hsAssert(idx == fTree[idx].fLeafIndex, "Some scrambling of indices"); + + if( !on ) + { + ClearLeafFlag(idx, f); + return; + } + + fTree[idx].fFlags |= f; + + idx = fTree[idx].fParent; + + while( idx != kRootParent ) + { + if( (fTree[idx].fFlags & f) + || !(fTree[fTree[idx].fChildren[0]].fFlags & fTree[fTree[idx].fChildren[1]].fFlags & f) ) + { + idx = kRootParent; + } + else + { + fTree[idx].fFlags |= f; + idx = fTree[idx].fParent; + } + } +} + +void plSpaceTree::ClearLeafFlag(Int16 idx, UInt16 f) +{ + hsAssert(idx == fTree[idx].fLeafIndex, "Some scrambling of indices"); + + while( idx != kRootParent ) + { + if( !(fTree[idx].fFlags & f) ) + { + return; + } + else + { + fTree[idx].fFlags &= ~f; + idx = fTree[idx].fParent; + } + } + +} + +inline void plSpaceTree::IEnableLeaf(Int16 idx, hsBitVector& cache) const +{ + + cache.SetBit(idx); + + idx = fTree[idx].fParent; + + while( idx != kRootParent ) + { + if( cache.IsBitSet(idx) ) + { + return; + } + else + { + cache.SetBit(idx); + idx = fTree[idx].fParent; + } + } +} + +void plSpaceTree::EnableLeaf(Int16 idx, hsBitVector& cache) const +{ + IEnableLeaf(idx, cache); +} + +void plSpaceTree::EnableLeaves(const hsTArray& list, hsBitVector& cache) const +{ + if( IsEmpty() ) + return; + int i; + for( i = 0; i < list.GetCount(); i++ ) + { + IEnableLeaf(list[i], cache); + } +} + +void plSpaceTree::IHarvestAndCullEnabledLeaves(Int16 subIdx, const hsBitVector& cache, hsTArray& list) const +{ + if( !cache.IsBitSet(subIdx) ) + return; + + const plSpaceTreeNode& subRoot = fTree[subIdx]; + + plVolumeCullResult res = fCullFunc->Test(subRoot.fWorldBounds); + if( res == kVolumeCulled ) + return; + + if( subRoot.fFlags & plSpaceTreeNode::kIsLeaf ) + { + list.Append(subIdx); + } + else + { + if( res == kVolumeClear ) + { + IHarvestEnabledLeaves(subRoot.fChildren[0], cache, list); + IHarvestEnabledLeaves(subRoot.fChildren[1], cache, list); + } + else + { + IHarvestAndCullEnabledLeaves(subRoot.fChildren[0], cache, list); + IHarvestAndCullEnabledLeaves(subRoot.fChildren[1], cache, list); + } + } +} + +void plSpaceTree::IHarvestEnabledLeaves(Int16 subIdx, const hsBitVector& cache, hsTArray& list) const +{ + if( !cache.IsBitSet(subIdx) ) + return; + + const plSpaceTreeNode& subRoot = fTree[subIdx]; + + if( subRoot.fFlags & plSpaceTreeNode::kIsLeaf ) + { + plProfile_Inc(HarvestLeaves); + list.Append(subIdx); + } + else + { + IHarvestEnabledLeaves(subRoot.fChildren[0], cache, list); + IHarvestEnabledLeaves(subRoot.fChildren[1], cache, list); + } +} + +void plSpaceTree::HarvestEnabledLeaves(plVolumeIsect* cull, const hsBitVector& cache, hsTArray& list) const +{ + if( IsEmpty() ) + return; + + if( fCullFunc = cull ) + IHarvestAndCullEnabledLeaves(fRoot, cache, list); + else + IHarvestEnabledLeaves(fRoot, cache, list); +} + +void plSpaceTree::IHarvestEnabledLeaves(Int16 subIdx, const hsBitVector& cache, hsBitVector& totList, hsBitVector& list) const +{ + if( IsDisabled(subIdx) ) + return; + + if( totList.IsBitSet(subIdx) ) + return; + + totList.SetBit(subIdx); + + const plSpaceTreeNode& subRoot = fTree[subIdx]; + if( subRoot.fFlags & plSpaceTreeNode::kIsLeaf ) + { + plProfile_Inc(HarvestLeaves); + list.SetBit(subIdx); + } + else + { + IHarvestEnabledLeaves(subRoot.fChildren[0], cache, totList, list); + IHarvestEnabledLeaves(subRoot.fChildren[1], cache, totList, list); + } +} + +void plSpaceTree::MoveLeaf(Int16 idx, const hsBounds3Ext& bnd) +{ + hsAssert(idx == fTree[idx].fLeafIndex, "Some scrambling of indices"); + + fTree[idx].fWorldBounds = bnd; + + while( idx != kRootParent ) + { + if( fTree[idx].fFlags & plSpaceTreeNode::kDirty ) + { + idx = kRootParent; + } + else + { + fTree[idx].fFlags |= plSpaceTreeNode::kDirty; + idx = fTree[idx].fParent; + } + } +} + +void plSpaceTree::HarvestLeaves(Int16 subRoot, hsBitVector& totList, hsBitVector& list) const +{ + if( !IsEmpty() ) + { + if( fCache ) + { + IHarvestEnabledLeaves(subRoot, *fCache, totList, list); + } + else + { + IHarvestLeaves(fTree[subRoot], totList, list); + } + } +} + +void plSpaceTree::HarvestLeaves(hsBitVector& totList, hsBitVector& list) const +{ + if( !IsEmpty() ) + IHarvestLeaves(fTree[fRoot], totList, list); +} + +void plSpaceTree::HarvestLeaves(hsBitVector& list) const +{ + if( !IsEmpty() ) + IHarvestLeaves(fTree[fRoot], scratchTotVec, list); + scratchTotVec.Clear(); +} + +void plSpaceTree::HarvestLeaves(plVolumeIsect* cull, hsBitVector& list) const +{ + if( !IsEmpty() ) + { + if( fCullFunc = cull ) + IHarvestAndCullLeaves(fTree[fRoot], scratchTotVec, list); + else + IHarvestLeaves(fTree[fRoot], scratchTotVec, list); + } + scratchTotVec.Clear(); +} + +void plSpaceTree::HarvestLeaves(Int16 subRoot, hsBitVector& list) const +{ + IHarvestLeaves(GetNode(subRoot), scratchTotVec, list); + scratchTotVec.Clear(); +} + +void plSpaceTree::HarvestLeaves(plVolumeIsect* cull, hsTArray& list) const +{ + if( !IsEmpty() ) + { + scratchBitVec.Clear(); + HarvestLeaves(cull, scratchBitVec); + + BitVectorToList(list, scratchBitVec); + } +} + +void plSpaceTree::HarvestLeaves(hsTArray& list) const +{ + if( !IsEmpty() ) + { + scratchBitVec.Clear(); + HarvestLeaves(scratchBitVec); + + BitVectorToList(list, scratchBitVec); + } +} + +void plSpaceTree::HarvestLeaves(Int16 subRoot, hsTArray& list) const +{ + if( !IsEmpty() ) + { + scratchBitVec.Clear(); + + HarvestLeaves(subRoot, scratchBitVec); + + BitVectorToList(list, scratchBitVec); + } +} + +void plSpaceTree::BitVectorToList(hsTArray& list, const hsBitVector& bitVec) const +{ +#if 0 // added func to bitvector + int i; + for( i = 0; i < fNumLeaves; i++ ) + { + if( bitVec.IsBitSet(i) ) + list.Append(i); + } +#else // added func to bitvector + bitVec.Enumerate(list); +#endif // added func to bitvector +} + +void plSpaceTree::IHarvestAndCullLeaves(const plSpaceTreeNode& subRoot, hsBitVector& totList, hsBitVector& list) const +{ + if( subRoot.fFlags & plSpaceTreeNode::kDisabled ) + return; + + int idx = &subRoot - &fTree[0]; + if( totList.IsBitSet(idx) ) + return; + + hsAssert(fCullFunc, "Oops"); + plVolumeCullResult res = fCullFunc->Test(subRoot.fWorldBounds); + if( res == kVolumeCulled ) + return; + + if( subRoot.fFlags & plSpaceTreeNode::kIsLeaf ) + { + totList.SetBit(idx); + + plProfile_Inc(HarvestLeaves); + list.SetBit(subRoot.fLeafIndex); + } + else + { + if( res == kVolumeClear ) + { + totList.SetBit(idx); + + IHarvestLeaves(fTree[subRoot.fChildren[0]], totList, list); + IHarvestLeaves(fTree[subRoot.fChildren[1]], totList, list); + } + else + { + IHarvestAndCullLeaves(fTree[subRoot.fChildren[0]], totList, list); + IHarvestAndCullLeaves(fTree[subRoot.fChildren[1]], totList, list); + } + } +} + +void plSpaceTree::IHarvestAndCullLeaves(const plSpaceTreeNode& subRoot, hsTArray& list) const +{ + if( subRoot.fFlags & plSpaceTreeNode::kDisabled ) + return; + + hsAssert(fCullFunc, "Oops"); + plVolumeCullResult res = fCullFunc->Test(subRoot.fWorldBounds); + if( res == kVolumeCulled ) + return; + + if( subRoot.fFlags & plSpaceTreeNode::kIsLeaf ) + { + plProfile_Inc(HarvestLeaves); + list.Append(subRoot.fLeafIndex); + } + else + { + if( res == kVolumeClear ) + { + IHarvestLeaves(fTree[subRoot.fChildren[0]], list); + IHarvestLeaves(fTree[subRoot.fChildren[1]], list); + } + else + { + IHarvestAndCullLeaves(fTree[subRoot.fChildren[0]], list); + IHarvestAndCullLeaves(fTree[subRoot.fChildren[1]], list); + } + } +} + +void plSpaceTree::IHarvestLeaves(const plSpaceTreeNode& subRoot, hsBitVector& totList, hsBitVector& list) const +{ + if( subRoot.fFlags & plSpaceTreeNode::kDisabled ) + return; + + int idx = &subRoot - &fTree[0]; + if( totList.IsBitSet(idx) ) + return; + + totList.SetBit(idx); + + if( subRoot.fFlags & plSpaceTreeNode::kIsLeaf ) + { + plProfile_Inc(HarvestLeaves); + list.SetBit(subRoot.fLeafIndex); + } + else + { + IHarvestLeaves(fTree[subRoot.fChildren[0]], totList, list); + IHarvestLeaves(fTree[subRoot.fChildren[1]], totList, list); + } +} + +void plSpaceTree::IHarvestLeaves(const plSpaceTreeNode& subRoot, hsTArray& list) const +{ + if( subRoot.fFlags & plSpaceTreeNode::kDisabled ) + return; + if( subRoot.fFlags & plSpaceTreeNode::kIsLeaf ) + { + plProfile_Inc(HarvestLeaves); + list.Append(subRoot.fLeafIndex); + } + else + { + IHarvestLeaves(fTree[subRoot.fChildren[0]], list); + IHarvestLeaves(fTree[subRoot.fChildren[1]], list); + } +} + +void plSpaceTree::Read(hsStream* s, hsResMgr* mgr) +{ + plCreatable::Read(s, mgr); + + fRoot = s->ReadSwap16(); + + fNumLeaves = UInt16(s->ReadSwap32()); + + UInt32 n = s->ReadSwap32(); + fTree.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + fTree[i].Read(s); +} + +void plSpaceTree::Write(hsStream* s, hsResMgr* mgr) +{ + plCreatable::Write(s, mgr); + + s->WriteSwap16(fRoot); + + s->WriteSwap32(fNumLeaves); + + s->WriteSwap32(fTree.GetCount()); + int i; + for( i = 0; i < fTree.GetCount(); i++ ) + { + fTree[i].Write(s); + } +} + +// Some debug only stuff + +void plSpaceTree::HarvestLevel(int level, hsTArray& list) const +{ + if( !IsEmpty() ) + { + IHarvestLevel(fRoot, level, 0, list); + } +} + +void plSpaceTree::IHarvestLevel(Int16 subRoot, int level, int currLevel, hsTArray& list) const +{ + if( level == currLevel ) + { + list.Append(subRoot); + return; + } + if( IsLeaf(subRoot) ) + return; + + IHarvestLevel(GetNode(subRoot).GetChild(0), level, currLevel+1, list); + IHarvestLevel(GetNode(subRoot).GetChild(1), level, currLevel+1, list); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTree.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTree.h new file mode 100644 index 00000000..906bb3b9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTree.h @@ -0,0 +1,175 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSpaceTree_inc +#define plSpaceTree_inc + +#include "hsTemplates.h" +#include "hsBounds.h" +#include "../pnFactory/plCreatable.h" +#include "hsBitVector.h" + +class hsStream; +class hsResMgr; +class plVolumeIsect; + +class plSpaceTreeNode +{ +public: + enum { + kNone = 0x0, + kIsLeaf = 0x1, + kDirty = 0x2, + kDisabled = 0x4, + kEmpty = 0x8, + kEnabledTemp = 0x10 + }; + + hsBounds3Ext fWorldBounds; + + UInt16 fFlags; // mostly for alignment; + Int16 fParent; + union { + Int16 fChildren[2]; // Children are actually in same array as parent + Int16 fLeafIndex; + }; + + Int16 GetChild(int w) const { hsAssert(!(fFlags & kIsLeaf), "Getting Child of leaf node"); return fChildren[w]; } + Int16 GetParent() const { return fParent; } + Int16 GetLeaf() const { hsAssert(fFlags & kIsLeaf, "Getting leaf idx off interior node"); return fLeafIndex; } + hsBool IsLeaf() const { return 0 != (fFlags & kIsLeaf); } + const hsBounds3Ext& GetWorldBounds() const { return fWorldBounds; } + + // Kind of hate this. Would like to blast the whole thing in, but + // the bounds are a real class. + void Read(hsStream* s); + void Write(hsStream* s); +}; + + +class plSpaceTree : public plCreatable +{ +public: + enum plHarvestFlags { + kNone = 0x0, + kSortBackToFront = 0x1, + kSortFrontToBack = 0x2 + }; + enum { + kRootParent = -1 + }; +private: + hsTArray fTree; + const hsBitVector* fCache; + + Int16 fRoot; + Int16 fNumLeaves; + + UInt16 fHarvestFlags; + + mutable plVolumeIsect* fCullFunc; + + hsPoint3 fViewPos; + + void IRefreshRecur(Int16 which); + + void IHarvestAndCullLeaves(const plSpaceTreeNode& subRoot, hsTArray& list) const; + void IHarvestLeaves(const plSpaceTreeNode& subRoot, hsTArray& list) const; + + void IHarvestAndCullLeaves(const plSpaceTreeNode& subRoot, hsBitVector& totList, hsBitVector& list) const; + void IHarvestLeaves(const plSpaceTreeNode& subRoot, hsBitVector& totList, hsBitVector& list) const; + + void IHarvestLevel(Int16 subRoot, int level, int currLevel, hsTArray& list) const; + + void IHarvestAndCullEnabledLeaves(Int16 subRoot, const hsBitVector& cache, hsTArray& list) const; + void IHarvestEnabledLeaves(Int16 subRoot, const hsBitVector& cache, hsTArray& list) const; + void IHarvestEnabledLeaves(Int16 subIdx, const hsBitVector& cache, hsBitVector& totList, hsBitVector& list) const; + + void IEnableLeaf(Int16 idx, hsBitVector& cache) const; + +public: + plSpaceTree(); + virtual ~plSpaceTree(); + + CLASSNAME_REGISTER( plSpaceTree ); + GETINTERFACE_ANY( plSpaceTree, plCreatable ); + + void SetViewPos(const hsPoint3& p) { fViewPos = p; } + const hsPoint3& GetViewPos() const { return fViewPos; } + + const plSpaceTreeNode& GetNode(Int16 w) const { return fTree[w]; } + Int16 GetRoot() const { return fRoot; } + hsBool IsRoot(Int16 w) const { return fRoot == w; } + hsBool IsLeaf(Int16 w) const { return GetNode(w).IsLeaf(); } + + void HarvestLeaves(hsBitVector& totList, hsBitVector& list) const; + void HarvestLeaves(hsBitVector& list) const; + void HarvestLeaves(plVolumeIsect* cullFunc, hsBitVector& list) const; + void HarvestLeaves(Int16 subRoot, hsBitVector& list) const; + void HarvestLeaves(Int16 subRoot, hsBitVector& totList, hsBitVector& list) const; + + void HarvestLeaves(hsTArray& list) const; + void HarvestLeaves(plVolumeIsect* cullFunc, hsTArray& list) const; + void HarvestLeaves(Int16 subRoot, hsTArray& list) const; + + void EnableLeaf(Int16 idx, hsBitVector& cache) const; + void EnableLeaves(const hsTArray& list, hsBitVector& cache) const; + void HarvestEnabledLeaves(plVolumeIsect* cullFunc, const hsBitVector& cache, hsTArray& list) const; + void SetCache(const hsBitVector* cache) { fCache = cache; } + + void BitVectorToList(hsTArray& list, const hsBitVector& bitVec) const; + + void SetHarvestFlags(plHarvestFlags f) { fHarvestFlags = f; } + UInt16 GetHarvestFlags() const { return fHarvestFlags; } + + UInt16 HasLeafFlag(Int16 w, UInt16 f) const { return GetNode(w).fFlags & f; } + void SetLeafFlag(Int16 w, UInt16 f, hsBool on=true); + void ClearLeafFlag(Int16 w, UInt16 f); + void ClearTreeFlag(UInt16 f); + void SetTreeFlag(UInt16 f, hsBool on=true); + + hsBool IsDisabled(UInt16 w) const { return (GetNode(w).fFlags & plSpaceTreeNode::kDisabled) || (fCache && !fCache->IsBitSet(w)); } + + // Should GetWorldBounds check and refresh if needed? + const hsBounds3Ext& GetWorldBounds() const { return GetNode(GetRoot()).fWorldBounds; } + + void MoveLeaf(Int16 idx, const hsBounds3Ext& newWorldBnd); + void Refresh(); + hsBool IsEmpty() const { return 0 != (GetNode(GetRoot()).fFlags & plSpaceTreeNode::kEmpty); } + hsBool IsDirty() const { return 0 != (GetNode(GetRoot()).fFlags & plSpaceTreeNode::kDirty); } + void MakeDirty() { fTree[GetRoot()].fFlags |= plSpaceTreeNode::kDirty; } + + Int16 GetNumLeaves() const { return fNumLeaves; } + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + void HarvestLevel(int level, hsTArray& list) const; + + friend class plSpaceTreeMaker; +}; + +#endif // plSpaceTree_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTreeMaker.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTreeMaker.cpp new file mode 100644 index 00000000..862a3893 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTreeMaker.cpp @@ -0,0 +1,721 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plSpaceTreeMaker.h" +#include "../plMath/hsRadixSort.h" +#include "../plDrawable/plSpaceTree.h" + +#include "hsUtils.h" // for testing, get hsRand() +#include "hsTimer.h" +#include "../plIntersect/plVolumeIsect.h" + +//#define MF_DO_TIMES + +enum mfTimeTypes +{ + kMakeTree = 0, + kMakeFatTree, + kSortList, + kHarvest, + kMakeSpaceTree, + kMakeTreeAll, + kHarvestSphere, + kHarvestCone, + kHarvestCapped, + + kNumTimes +}; + +#ifdef MF_DO_TIMES + +double times[kNumTimes]; +#define StartTimer(i) { times[(i)] -= hsTimer::GetSeconds(); } +#define StopTimer(i) { times[(i)] += hsTimer::GetSeconds(); } + +#define InitTimers() { for( int i = 0; i < kNumTimes; i++ )times[i] = 0; } + +#else // MF_DO_TIMES + +#define StartTimer(i) +#define StopTimer(i) +#define InitTimers() + +#endif // MF_DO_TIMES + +// Create the tree + +// more temp testing garbage +#if 0 +plSpaceCullResult mySpaceCullFunction(const hsBounds3Ext& bnd) +{ + const hsPoint3& maxs = bnd.GetMaxs(); + const hsPoint3& mins = bnd.GetMins(); + + if( maxs.fX < 0.25f ) + return kSpaceCulled; + if( maxs.fY < 0.25f ) + return kSpaceCulled; + if( maxs.fZ < 0.25f ) + return kSpaceCulled; + + if( mins.fX > 0.75f ) + return kSpaceCulled; + if( mins.fY > 0.75f ) + return kSpaceCulled; + if( mins.fZ > 0.75f ) + return kSpaceCulled; + + if( maxs.fX > 0.75f ) + return kSpaceSplit; + if( maxs.fY > 0.75f ) + return kSpaceSplit; + if( maxs.fZ > 0.75f ) + return kSpaceSplit; + + if( mins.fX < 0.25f ) + return kSpaceSplit; + if( mins.fY < 0.25f ) + return kSpaceSplit; + if( mins.fZ < 0.25f ) + return kSpaceSplit; + + return kSpaceClear; +} +#endif + +void plSpaceTreeMaker::ISortList(hsTArray& nodes, const hsVector3& axis) +{ + StartTimer(kSortList); + + hsRadixSort::Elem* list = fSortScratch; + hsRadixSort::Elem* listTrav = list; + Int32 n = nodes.GetCount(); + while( n-- ) + { + listTrav->fKey.fFloat = axis.InnerProduct(nodes[n]->fWorldBounds.GetCenter()); + listTrav->fBody = (void*)nodes[n]; + listTrav->fNext = listTrav+1; + listTrav++; + } + list[nodes.GetCount()-1].fNext = nil; + UInt32 sortFlags = 0; + hsRadixSort rad; + hsRadixSort::Elem* sortedList = rad.Sort(list, sortFlags); + listTrav = sortedList; + + int i; + for( i = 0; i < nodes.GetCount(); i++ ) + { + nodes[i] = (plSpacePrepNode*)(listTrav->fBody); + listTrav = listTrav->fNext; + } + + StopTimer(kSortList); + + return; +} + +void plSpaceTreeMaker::ISplitList(hsTArray& nodes, const hsVector3& axis, hsTArray& lower, hsTArray& upper) +{ + + ISortList(nodes, axis); + + int lowerCount = nodes.GetCount() / 2; + int upperCount = nodes.GetCount() - lowerCount; + lower.SetCount(lowerCount); + upper.SetCount(upperCount); + + int i; + for( i = 0; i < lowerCount; i++ ) + lower[i] = nodes[i]; + for( i = 0; i < upperCount; i++ ) + upper[i] = nodes[i + lowerCount]; +} + +hsBounds3Ext plSpaceTreeMaker::IFindDistToCenterAxis(hsTArray& nodes, hsScalar& length, hsVector3& axis) +{ + hsBounds3Ext bnd; + bnd.MakeEmpty(); + + hsAssert(nodes.GetCount() > 1, "Degenerate case"); + + int i; + for( i = 0; i < nodes.GetCount(); i++ ) + { + bnd.Union(&nodes[i]->fWorldBounds); + } + length = 0; + for( i = 0; i < nodes.GetCount(); i++ ) + { + hsVector3 sep; + sep.Set(&bnd.GetCenter(), &nodes[i]->fWorldBounds.GetCenter()); + hsScalar len = sep.MagnitudeSquared(); + if( len > length ) + { + axis = sep; + length = len; + } + } + length = hsSquareRoot(length); + if( length > 1.e-3f ) + axis /= length; + else + return IFindSplitAxis(nodes, length, axis); + + return bnd; +} + +plSpacePrepNode* plSpaceTreeMaker::IMakeFatTreeRecur(hsTArray& nodes) +{ + if( !nodes.GetCount() ) + return nil; + + StartTimer(kMakeFatTree); + + plSpacePrepNode* subRoot = TRACKED_NEW plSpacePrepNode; + fTreeSize++; + + if( nodes.GetCount() == 1 ) + { + *subRoot = *nodes[0]; + + subRoot->fChildren[0] = nil; + subRoot->fChildren[1] = nil; + + StopTimer(kMakeFatTree); + + return subRoot; + } + + // Find the overall bounds of the list. + + // Find the maximum length vector from nodes[i] center to list center. + // If that length is zero, just use the maximum dimension of overall bounds. + hsScalar length; + hsVector3 axis; + hsBounds3Ext bnd = IFindDistToCenterAxis(nodes, length, axis); + + hsTArray list0; + hsTArray list1; + ISplitList(nodes, axis, list0, list1); + + subRoot->fChildren[0] = IMakeTreeRecur(list0); + subRoot->fChildren[1] = IMakeTreeRecur(list1); + + subRoot->fWorldBounds = bnd; + + StopTimer(kMakeFatTree); + + return subRoot; +} + +hsBounds3Ext plSpaceTreeMaker::IFindSplitAxis(hsTArray& nodes, hsScalar& length, hsVector3& axis) +{ + hsBounds3Ext bnd; + bnd.MakeEmpty(); + int i; + for( i = 0; i < nodes.GetCount(); i++ ) + { + bnd.Union(&nodes[i]->fWorldBounds); + } + hsScalar maxLen = bnd.GetMaxs()[0] - bnd.GetMins()[0]; + int maxAxis = 0; + + if( bnd.GetMaxs()[1] - bnd.GetMins()[1] > maxLen ) + { + maxLen = bnd.GetMaxs()[1] - bnd.GetMins()[1]; + maxAxis = 1; + } + + if( bnd.GetMaxs()[2] - bnd.GetMins()[2] > maxLen ) + { + maxLen = bnd.GetMaxs()[2] - bnd.GetMins()[2]; + maxAxis = 2; + } + + length = maxLen; + switch( maxAxis ) + { + case 0: + axis.Set(1.f, 0, 0); + break; + case 1: + axis.Set(0, 1.f, 0); + break; + case 2: + axis.Set(0, 0, 1.f); + break; + } + + return bnd; +} + +void plSpaceTreeMaker::IFindBigList(hsTArray& nodes, hsScalar length, const hsVector3& axis, hsTArray& giants, hsTArray& strimps) +{ + const hsScalar kCutoffFrac = 0.5f; + + giants.SetCount(0); + strimps.SetCount(0); + int i; + for( i = 0; i < nodes.GetCount(); i++ ) + { + hsPoint2 depth; + nodes[i]->fWorldBounds.TestPlane(axis, depth); + if( depth.fY - depth.fX > length * kCutoffFrac ) + giants.Append(nodes[i]); + else + strimps.Append(nodes[i]); + } +} + +plSpacePrepNode* plSpaceTreeMaker::INewSubRoot(const hsBounds3Ext& bnd) +{ + plSpacePrepNode* subRoot = TRACKED_NEW plSpacePrepNode; + subRoot->fDataIndex = Int16(-1); + fTreeSize++; + + subRoot->fWorldBounds = bnd; + + return subRoot; +} + +plSpacePrepNode* plSpaceTreeMaker::IMakeTreeRecur(hsTArray& nodes) +{ + if( !nodes.GetCount() ) + return nil; + + if( nodes.GetCount() == 1 ) + { + return IMakeFatTreeRecur(nodes); + } + + StartTimer(kMakeTree); + + // Find the maximum bounds dimension + hsScalar length; + hsVector3 axis; + hsBounds3Ext bnd = IFindSplitAxis(nodes, length, axis); + + // Find everyone with bounds over half that size in the same dimension as list0. + hsTArray list0; + hsTArray list1; + IFindBigList(nodes, length, axis, list0, list1); + + plSpacePrepNode* subRoot = nil; + + // If list0 not empty, put them in first child, recur on remainder, + if( list0.GetCount() && list1.GetCount() ) + { + subRoot = INewSubRoot(bnd); + subRoot->fChildren[0] = IMakeFatTreeRecur(list0); // too big + subRoot->fChildren[1] = IMakeTreeRecur(list1); // remainder + } + else if( list0.GetCount() ) + { + subRoot = IMakeFatTreeRecur(list0); + } + // Else sort along axis by bounds center, recur separately on lower and upper halves. + else + { + ISplitList(nodes, axis, list0, list1); + + subRoot = INewSubRoot(bnd); + subRoot->fChildren[0] = IMakeTreeRecur(list0); + subRoot->fChildren[1] = IMakeTreeRecur(list1); + + } + + StopTimer(kMakeTree); + + return subRoot; +} + +void plSpaceTreeMaker::IMakeTree() +{ + fSortScratch = TRACKED_NEW hsRadixSort::Elem[fLeaves.GetCount()]; + + fPrepTree = IMakeTreeRecur(fLeaves); + + delete [] fSortScratch; + fSortScratch = nil; +} + +void plSpaceTreeMaker::Reset() +{ + fLeaves.Reset(); + fPrepTree = nil; + fTreeSize = 0; + fSortScratch = nil; +} + +void plSpaceTreeMaker::IDeleteTreeRecur(plSpacePrepNode* node) +{ + if( node ) + { + IDeleteTreeRecur(node->fChildren[0]); + IDeleteTreeRecur(node->fChildren[1]); + + delete node; + } +} + +void plSpaceTreeMaker::Cleanup() +{ + IDeleteTreeRecur(fPrepTree); + fPrepTree = nil; + + int i; + for( i = 0; i < fLeaves.GetCount(); i++ ) + delete fLeaves[i]; + fLeaves.Reset(); + fDisabled.Reset(); +} + +Int32 plSpaceTreeMaker::AddLeaf(const hsBounds3Ext& worldBnd, hsBool disable) +{ + plSpacePrepNode* leaf = TRACKED_NEW plSpacePrepNode; + fLeaves.Append(leaf); + leaf->fDataIndex = fLeaves.GetCount()-1; + leaf->fChildren[0] = nil; + leaf->fChildren[1] = nil; + + leaf->fWorldBounds = worldBnd; + if( leaf->fWorldBounds.GetType() != kBoundsNormal ) + { + static const hsPoint3 zero(0.f, 0.f, 0.f); + leaf->fWorldBounds.Reset(&zero); + } + + fDisabled.SetBit(leaf->fDataIndex, disable); + + return leaf->fDataIndex; +} + +//#define MF_DO_RAND +#define MF_DO_3D + +#ifdef MF_DO_RAND +#define MF_SETPOINT(pt,a,b,c) pt.Set(hsRand()/32767.f, hsRand()/32767.f, hsRand()/32767.f) +#else // MF_DO_RAND +#define MF_SETPOINT(pt,a,b,c) pt.Set(a,b,c) +#endif // MF_DO_RAND +void plSpaceTreeMaker::TestTree() +{ + Reset(); + + const int kTestSize = 10; + int i; + for( i = 0; i < kTestSize; i++ ) + { + int j; + for( j = 0; j < kTestSize; j++ ) + { + int k; +#ifdef MF_DO_3D + for( k = 0; k < kTestSize; k++ ) +#else // MF_DO_3D + k = 0; +#endif // MF_DO_3D + { + hsBounds3Ext bnd; + hsPoint3 pt; + MF_SETPOINT(pt, float(i-1)/kTestSize, float(j-1)/kTestSize, float(k-1)/kTestSize); + bnd.Reset(&pt); + MF_SETPOINT(pt, float(i)/kTestSize, float(j)/kTestSize, float(k)/kTestSize); + bnd.Union(&pt); + + AddLeaf(bnd); + } + } + } + + hsBitVector list; + + plSpaceTree* tree = MakeTree(); + +#if 0 // HACK TESTING MOVE TO VOLUMECULL + + hsMatrix44 liX; + hsMatrix44 invLiX; + liX.MakeTranslateMat(&hsVector3(0.5f, 0.5f, 0)); + liX.GetInverse(&invLiX); + + plSphereIsect sphere; + sphere.SetRadius(0.2); + sphere.SetTransform(liX, invLiX); + + tree->SetViewPos(*hsPoint3().Set(0,0,0)); + + plConeIsect cone; + cone.SetAngle(hsScalarPI*0.25f); + cone.SetTransform(liX, invLiX); + + StartTimer(kHarvestCone); + + list.Clear(); + tree->HarvestLeaves(&cone, list); + + StopTimer(kHarvestCone); + + plConeIsect capped; + capped.SetAngle(hsScalarPI*0.25f); + capped.SetLength(0.5f); + capped.SetTransform(liX, invLiX); + + StartTimer(kHarvestCapped); + + list.Clear(); + tree->HarvestLeaves(&capped, list); + + StopTimer(kHarvestCapped); + + StartTimer(kHarvestSphere); + + list.Clear(); + tree->HarvestLeaves(&sphere, list); + + StopTimer(kHarvestSphere); + +#endif // HACK TESTING MOVE TO VOLUMECULL + + delete tree; +} + +plSpaceTree* plSpaceTreeMaker::MakeTree() +{ + // DEBUG FISH + InitTimers(); + // DEBUG FISH + + StartTimer(kMakeTreeAll); + + if( !fLeaves.GetCount() ) + return IMakeEmptyTree(); + + if( fLeaves.GetCount() < 2 ) + return IMakeDegenerateTree(); + + IMakeTree(); + + plSpaceTree* retVal = IMakeSpaceTree(); + + Cleanup(); + + StopTimer(kMakeTreeAll); + + return retVal; +} + +plSpaceTree* plSpaceTreeMaker::IMakeEmptyTree() +{ + plSpaceTree* tree = TRACKED_NEW plSpaceTree; + + tree->fTree.SetCount(1); + tree->fTree[0].fWorldBounds.Reset(&hsPoint3(0,0,0)); + tree->fTree[0].fFlags = plSpaceTreeNode::kEmpty; + tree->fRoot = 0; + tree->fNumLeaves = 0; + + Cleanup(); + + return tree; +} + +plSpaceTree* plSpaceTreeMaker::IMakeDegenerateTree() +{ + plSpaceTree* tree = TRACKED_NEW plSpaceTree; + + tree->fTree.Push(); + + tree->fRoot = 0; + tree->fTree[0].fWorldBounds = fLeaves[0]->fWorldBounds; + tree->fTree[0].fFlags = plSpaceTreeNode::kIsLeaf; + tree->fTree[0].fLeafIndex = 0; + tree->fTree[0].fParent = plSpaceTree::kRootParent; + tree->fNumLeaves = 1; + + if( fDisabled.IsBitSet(0) ) + tree->SetLeafFlag(0, plSpaceTreeNode::kDisabled, true); + + Cleanup(); + + return tree; +} + +int plSpaceTreeMaker::ITreeDepth(plSpacePrepNode* subRoot) +{ + if( !subRoot ) + return 0; + + int dep0 = ITreeDepth(subRoot->fChildren[0]); + int dep1 = ITreeDepth(subRoot->fChildren[1]); + + int dep = hsMaximum(dep0, dep1); + + return dep+1; +} + +plSpaceTree* plSpaceTreeMaker::IMakeSpaceTree() +{ + StartTimer(kMakeSpaceTree); + + plSpaceTree* tree = TRACKED_NEW plSpaceTree; + + plSpacePrepNode* head = fPrepTree; + + tree->fTree.SetCount(fLeaves.GetCount()); + + IGatherLeavesRecur(head, tree); + + int level = ITreeDepth(head); + while( level > 0 ) + IMakeSpaceTreeRecur(head, tree, --level, 0); + + tree->fRoot = tree->fTree.GetCount()-1; + tree->fTree[tree->fRoot].fParent = plSpaceTree::kRootParent; + tree->fNumLeaves = fLeaves.GetCount(); + + int i; + for( i = 0; i < fLeaves.GetCount(); i++ ) + { + if( fDisabled.IsBitSet(i) ) + tree->SetLeafFlag(i, plSpaceTreeNode::kDisabled, true); + } + + StopTimer(kMakeSpaceTree); + + return tree; +} + +// The following goofy cache-friendly tree set up slows down the tree build by 10%, but speeds up the runtime by 9%. +// Sounds fair. +#if 0 // Leaves first +Int16 plSpaceTreeMaker::IMakeSpaceTreeRecur(plSpacePrepNode* sub, plSpaceTree* tree, const int targetLevel, int currLevel) +{ + if( currLevel == targetLevel ) + { + Int16 nodeIdx = tree->fTree.GetCount(); + tree->fTree.Push(); + + tree->fTree[nodeIdx].fWorldBounds = sub->fWorldBounds; + + sub->fIndex = nodeIdx; + + if( !sub->fChildren[0] ) + { + hsAssert(!sub->fChildren[1], "Unsupported unbalance of tree"); + + tree->fTree[nodeIdx].fFlags = plSpaceTreeNode::kIsLeaf; + tree->fTree[nodeIdx].fLeafIndex = sub->fDataIndex; + + return nodeIdx; + } + hsAssert(sub->fChildren[1], "Unsupported unbalance of tree"); + + tree->fTree[nodeIdx].fFlags = plSpaceTreeNode::kNone; + + + return nodeIdx; + } + + Int16 nodeIdx = sub->fIndex; + + if( !sub->fChildren[0] ) + { + hsAssert(!sub->fChildren[1] , "Unsupported unbalance of tree"); + return nodeIdx; + } + hsAssert(sub->fChildren[1] , "Unsupported unbalance of tree"); + + tree->fTree[nodeIdx].fChildren[0] = IMakeSpaceTreeRecur(sub->fChildren[0], tree, targetLevel, currLevel+1); + tree->fTree[tree->fTree[nodeIdx].fChildren[0]].fParent = nodeIdx; + + tree->fTree[nodeIdx].fChildren[1] = IMakeSpaceTreeRecur(sub->fChildren[1], tree, targetLevel, currLevel+1); + tree->fTree[tree->fTree[nodeIdx].fChildren[1]].fParent = nodeIdx; + + return nodeIdx; +} + +#else // Leaves first +void plSpaceTreeMaker::IGatherLeavesRecur(plSpacePrepNode* sub, plSpaceTree* tree) +{ + // if it's a leaf, stuff it in the right slot, else recur + if( !sub->fChildren[0] ) + { + hsAssert(!sub->fChildren[1], "Unsupported unbalance of tree"); + + plSpaceTreeNode& leaf = tree->fTree[sub->fDataIndex]; + Int16 nodeIdx = sub->fDataIndex; + leaf.fWorldBounds = sub->fWorldBounds; + sub->fIndex = nodeIdx; + leaf.fFlags = plSpaceTreeNode::kIsLeaf; + leaf.fLeafIndex = nodeIdx; + + return; + } + hsAssert(sub->fChildren[1], "Unsupported unbalance of tree"); + + IGatherLeavesRecur(sub->fChildren[0], tree); + IGatherLeavesRecur(sub->fChildren[1], tree); +} + +void plSpaceTreeMaker::IMakeSpaceTreeRecur(plSpacePrepNode* sub, plSpaceTree* tree, const int targetLevel, int currLevel) +{ + // If it's a leaf, we've already done it. + if( !sub->fChildren[0] ) + { + hsAssert(!sub->fChildren[1], "Unsupported unbalance of tree"); + return; + } + + hsAssert(sub->fChildren[0] && sub->fChildren[1], "Shouldn't get this deep, already got the leaves"); + + if( currLevel == targetLevel ) + { + Int16 nodeIdx = tree->fTree.GetCount(); + tree->fTree.Push(); + + tree->fTree[nodeIdx].fWorldBounds = sub->fWorldBounds; + + sub->fIndex = nodeIdx; + + tree->fTree[nodeIdx].fFlags = plSpaceTreeNode::kNone; + + tree->fTree[nodeIdx].fChildren[0] = sub->fChildren[0]->fIndex; + tree->fTree[sub->fChildren[0]->fIndex].fParent = nodeIdx; + + tree->fTree[nodeIdx].fChildren[1] = sub->fChildren[1]->fIndex; + tree->fTree[sub->fChildren[1]->fIndex].fParent = nodeIdx; + + return; + } + + IMakeSpaceTreeRecur(sub->fChildren[0], tree, targetLevel, currLevel+1); + IMakeSpaceTreeRecur(sub->fChildren[1], tree, targetLevel, currLevel+1); +} + +#endif // Leaves first \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTreeMaker.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTreeMaker.h new file mode 100644 index 00000000..b27c730f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpaceTreeMaker.h @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSpaceTreeMaker_inc +#define plSpaceTreeMaker_inc + +#include "hsBounds.h" +#include "hsTemplates.h" +#include "hsBitVector.h" + +class hsRadixSortElem; +class plSpaceTree; + +class plSpacePrepNode +{ +public: + hsBounds3Ext fWorldBounds; + + Int16 fIndex; + Int16 fDataIndex; + + plSpacePrepNode* fChildren[2]; +}; + +class plSpaceTreeMaker +{ +protected: + hsTArray fLeaves; // input + + hsRadixSortElem* fSortScratch; + + hsBitVector fDisabled; + + plSpacePrepNode* fPrepTree; + Int16 fTreeSize; + + plSpacePrepNode* INewSubRoot(const hsBounds3Ext& bnd); + void IFindBigList(hsTArray& nodes, hsScalar length, const hsVector3& axis, hsTArray& giants, hsTArray& strimp); + void ISortList(hsTArray& nodes, const hsVector3& axis); + void ISplitList(hsTArray& nodes, const hsVector3& axis, hsTArray& lower, hsTArray& upper); + hsBounds3Ext IFindDistToCenterAxis(hsTArray& nodes, hsScalar& length, hsVector3& axis); + plSpacePrepNode* IMakeFatTreeRecur(hsTArray& nodes); + hsBounds3Ext IFindSplitAxis(hsTArray& nodes, hsScalar& length, hsVector3& axis); + plSpacePrepNode* IMakeTreeRecur(hsTArray& nodes); + + void IMakeTree(); + + plSpaceTree* IMakeEmptyTree(); + plSpaceTree* IMakeDegenerateTree(); + void IGatherLeavesRecur(plSpacePrepNode* sub, plSpaceTree* tree); + void IMakeSpaceTreeRecur(plSpacePrepNode* sub, plSpaceTree* tree, const int targetLevel, int currLevel); + plSpaceTree* IMakeSpaceTree(); + int ITreeDepth(plSpacePrepNode* subRoot); + + void IDeleteTreeRecur(plSpacePrepNode* node); + +public: + + void Cleanup(); + + void Reset(); + Int32 AddLeaf(const hsBounds3Ext& worldBnd, hsBool disable=false); + plSpaceTree* MakeTree(); + + void TestTree(); // development only - NUKE ME mf horse +}; + +#endif // plSpaceTreeMaker_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanInstance.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanInstance.cpp new file mode 100644 index 00000000..219bf7c8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanInstance.cpp @@ -0,0 +1,238 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plSpanInstance.h" + +#include "hsGeometry3.h" +#include "hsStream.h" + +void plSpanEncoding::Read(hsStream* s) +{ + fCode = s->ReadByte(); + fPosScale = s->ReadSwapScalar(); +} + +void plSpanEncoding::Write(hsStream* s) const +{ + s->WriteByte((UInt8)fCode); + s->WriteSwapScalar(fPosScale); +} + + +plSpanInstance::plSpanInstance() +: fPosDelta(nil), + fCol(nil) +{ + fL2W[0][1] = fL2W[0][2] = 0.f; + fL2W[1][0] = fL2W[1][2] = 0.f; + fL2W[2][0] = fL2W[2][1] = 0.f; + fL2W[0][0] = fL2W[1][1] = fL2W[2][2] = 1.f; +} + +plSpanInstance::~plSpanInstance() +{ + DeAlloc(); +} + +void plSpanInstance::DeAlloc() +{ + delete [] fPosDelta; + fPosDelta = nil; + delete [] fCol; + fCol = nil; +} + +void plSpanInstance::Alloc(const plSpanEncoding& encoding, UInt32 numVerts) +{ + DeAlloc(); + UInt32 posStride = PosStrideFromEncoding(encoding); + if( posStride ) + fPosDelta = TRACKED_NEW UInt8[numVerts * posStride]; + + UInt32 colStride = ColStrideFromEncoding(encoding); + if( colStride ) + fCol = TRACKED_NEW UInt8[numVerts * colStride]; +} + +void plSpanInstance::Read(hsStream* stream, const plSpanEncoding& encoding, UInt32 numVerts) +{ + Alloc(encoding, numVerts); + + stream->Read(12 * sizeof(hsScalar), fL2W[0]); + if( fPosDelta ) + { + stream->Read(numVerts * PosStrideFromEncoding(encoding), fPosDelta); + } + + if( fCol ) + { + stream->Read(numVerts * ColStrideFromEncoding(encoding), fCol); + } +} + +void plSpanInstance::Write(hsStream* stream, const plSpanEncoding& encoding, UInt32 numVerts) const +{ + stream->Write(12 * sizeof(hsScalar), fL2W[0]); + if( fPosDelta ) + { + stream->Write(numVerts * PosStrideFromEncoding(encoding), fPosDelta); + } + if( fCol ) + { + stream->Write(numVerts * ColStrideFromEncoding(encoding), fCol); + } +} + +hsMatrix44 plSpanInstance::LocalToWorld() const +{ + hsMatrix44 retVal; + retVal.NotIdentity(); + int i; + for( i = 0; i < 3; i++ ) + { + int j; + for( j = 0; j < 4; j++ ) + { + retVal.fMap[i][j] = fL2W[i][j]; + } + } + retVal.fMap[3][0] = + retVal.fMap[3][1] = + retVal.fMap[3][2] = 0.f; + retVal.fMap[3][3] = 1.f; + + return retVal; +} + +hsMatrix44 plSpanInstance::WorldToLocal() const +{ + hsMatrix44 l2w = LocalToWorld(); + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + return w2l; +} + +void plSpanInstance::SetLocalToWorld(const hsMatrix44& l2w) +{ + int i; + for( i = 0; i < 3; i++ ) + { + int j; + for( j = 0; j < 4; j++ ) + { + fL2W[i][j] = l2w.fMap[i][j]; + } + } +} + +void plSpanInstance::Encode(const plSpanEncoding& encoding, UInt32 numVerts, const hsVector3* delPos, const UInt32* color) +{ + Alloc(encoding, numVerts); + + hsAssert(!(encoding.fCode & plSpanEncoding::kPosMask) == !delPos, "Position encoding mismatch"); + hsAssert(!(encoding.fCode & plSpanEncoding::kColMask) == !color, "Color encoding mismatch"); + + // Check that there's anything to encode. + if( !(fPosDelta || fCol) ) + return; + + Int8* pos888 = (Int8*)fPosDelta; + Int16* pos161616 = (Int16*)fPosDelta; + UInt32* pos101010 = (UInt32*)fPosDelta; + + UInt8* col8 = (UInt8*)fCol; + UInt16* col16 = (UInt16*)fCol; + UInt32* col32 = (UInt32*)fCol; + int i; + for( i = 0; i < numVerts; i++ ) + { + switch(encoding.fCode & plSpanEncoding::kPosMask) + { + case plSpanEncoding::kPos888: + pos888[0] = Int8(delPos->fX / encoding.fPosScale); + pos888[1] = Int8(delPos->fY / encoding.fPosScale); + pos888[2] = Int8(delPos->fZ / encoding.fPosScale); + pos888 += 3; + delPos++; + break; + case plSpanEncoding::kPos161616: + pos161616[0] = Int16(delPos->fX / encoding.fPosScale); + pos161616[1] = Int16(delPos->fY / encoding.fPosScale); + pos161616[2] = Int16(delPos->fZ / encoding.fPosScale); + pos161616 += 3; + delPos++; + break; + case plSpanEncoding::kPos101010: + *pos101010 = + ((UInt32(delPos->fX / encoding.fPosScale) & 0x3f) << 0) + | ((UInt32(delPos->fY / encoding.fPosScale) & 0x3f) << 10) + | ((UInt32(delPos->fZ / encoding.fPosScale) & 0x3f) << 20); + pos101010++; + delPos++; + break; + case plSpanEncoding::kPos008: + *pos888 = Int8(delPos->fZ / encoding.fPosScale); + pos888++; + delPos++; + break; + case plSpanEncoding::kPosNone: + default: + break; + } + switch(encoding.fCode & plSpanEncoding::kColMask) + { + case plSpanEncoding::kColA8: + *col8 = (UInt8)((*color) >> 24); + col8++; + color++; + break; + case plSpanEncoding::kColI8: + *col8 = (UInt8)((*color) & 0xff); + col8++; + color++; + break; + case plSpanEncoding::kColAI88: + *col16 = (UInt16)(((*color) >> 16) & 0xffff); + col16++; + color++; + break; + case plSpanEncoding::kColRGB888: + *col8++ = (UInt8)((*color >> 16) & 0xff); + *col8++ = (UInt8)((*color >> 8) & 0xff); + *col8++ = (UInt8)((*color >> 0) & 0xff); + color++; + break; + case plSpanEncoding::kColARGB8888: + *col32++ = *color; + color++; + break; + case plSpanEncoding::kColNone: + default: + break; + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanInstance.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanInstance.h new file mode 100644 index 00000000..04d9c465 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanInstance.h @@ -0,0 +1,288 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSpanInstance_inc +#define plSpanInstance_inc + +#include "hsGeometry3.h" +#include "hsMatrix44.h" + +// plClusterGroup +// array of templates +// array of materials (indexed by templates) +// array of clusters +// array of LOD blend +// array of vis sets + +// plCluster +// array of span instances that are combinable +// LOD blend start and end index +// vis set index +// Combinable means: +// same material +// same format +// same LOD blend +// same vis set +// same lights +// same encoding? + +// plSpanInstance +// template idx +// 3x4 transform +// encoding flags +// (what components does it include, delPos? color?) +// (what's the encoding? 32bit RGBA? 16Bit/Chan? 10Bit/Chan? 8Bit/Chan?) +// (need an encoding per channel) + +// plSpanTemplate +// numVerts +// Format (assume pos & norm?) +// kUVWMask +// kColMask +// kWeightMask +// fPos +// fNorm +// fCol +// fUVWs +// fWeights + + +class plSpanEncoding +{ +public: + enum + { + kPosNone = 0x0, + kPos888 = 0x1, + kPos161616 = 0x2, + kPos101010 = 0x4, + kPos008 = 0x8, + kPosMask = kPos888 | kPos161616 | kPos101010 | kPos008, + + kColNone = 0x0, + kColA8 = 0x10, + kColI8 = 0x20, + kColAI88 = 0x40, + kColRGB888 = 0x80, + kColARGB8888 = 0x100, + kColMask = kColNone + | kColA8 + | kColI8 + | kColAI88 + | kColRGB888 + | kColARGB8888, + }; + + UInt32 fCode; + hsScalar fPosScale; + + plSpanEncoding() : fCode(kPosNone|kColNone), fPosScale(0) {} + plSpanEncoding(UInt32 c, hsScalar s) : fCode(c), fPosScale(s) {} + + UInt32 Code() const { return fCode; } + hsScalar Scale() const { return fPosScale; } + + void Read(hsStream* s); + void Write(hsStream* s) const; +}; + +class plSpanInstance +{ +protected: + UInt8* fPosDelta; + UInt8* fCol; + + hsScalar fL2W[3][4]; + + friend class plSpanInstanceIter; +public: + plSpanInstance(); + ~plSpanInstance(); + + void Read(hsStream* s, const plSpanEncoding& encoding, UInt32 numVerts); + void Write(hsStream* s, const plSpanEncoding& encoding, UInt32 numVerts) const; + + void Encode(const plSpanEncoding& encoding, UInt32 numVerts, const hsVector3* delPos, const UInt32* color); + + void Alloc(const plSpanEncoding& encoding, UInt32 numVerts); + void DeAlloc(); + + hsMatrix44 LocalToWorld() const; + hsMatrix44 WorldToLocal() const; + + void SetLocalToWorld(const hsMatrix44& l2w); + + hsBool HasPosDelta() const { return fPosDelta != nil; } + hsBool HasColor() const { return fCol != nil; } + + static UInt16 PosStrideFromEncoding(const plSpanEncoding& encoding) + { + switch(encoding.fCode & plSpanEncoding::kPosMask) + { + case plSpanEncoding::kPos888: + return 3; + case plSpanEncoding::kPos161616: + return 6; + case plSpanEncoding::kPos101010: + return 4; + case plSpanEncoding::kPos008: + return 1; + } + return 0; + } + static UInt16 ColStrideFromEncoding(const plSpanEncoding& encoding) + { + switch(encoding.fCode & plSpanEncoding::kPosMask) + { + case plSpanEncoding::kColNone: + return 0; + case plSpanEncoding::kColA8: + return 1; + case plSpanEncoding::kColI8: + return 1; + case plSpanEncoding::kColAI88: + return 2; + case plSpanEncoding::kColRGB888: + return 3; + case plSpanEncoding::kColARGB8888: + return 4; + } + return 0; + } +}; + +class plSpanInstanceIter +{ +protected: + plSpanInstance* fInst; + plSpanEncoding fEncoding; + UInt32 fNumVerts; + Int32 fNumVertsLeft; + UInt16 fPosStride; + UInt16 fColStride; + + union { + Int8* fPos888; + Int16* fPos161616; + UInt32* fPos101010; + }; + union { + UInt8* fA8; + UInt8* fI8; + UInt16* fAI88; + UInt8* fRGB888; + UInt32* fARGB8888; + }; + +public: + plSpanInstanceIter(); + plSpanInstanceIter(plSpanInstance* inst, const plSpanEncoding& encoding, UInt32 numVerts) { Init(inst, encoding, numVerts); } + + void Init(plSpanInstance* inst, const plSpanEncoding& encoding, UInt32 numVerts) + { + fInst = inst; + fEncoding = encoding; + fNumVerts = numVerts; + fPosStride = inst->PosStrideFromEncoding(fEncoding); + fColStride = inst->ColStrideFromEncoding(fEncoding); + fNumVertsLeft = 0; + } + + void Begin() + { + fPos888 = (Int8*)fInst->fPosDelta; + fA8 = fInst->fCol; + + fNumVertsLeft = fNumVerts; + } + void Advance() + { + fPos888 += fPosStride; + fA8 += fColStride; + + fNumVertsLeft--; + } + hsBool More() const { return fNumVertsLeft > 0; } + + hsVector3 DelPos() const + { + switch(fEncoding.fCode & plSpanEncoding::kPosMask) + { + case plSpanEncoding::kPos888: + return hsVector3(fPos888[0] * fEncoding.fPosScale, + fPos888[1] * fEncoding.fPosScale, + fPos888[2] * fEncoding.fPosScale); + case plSpanEncoding::kPos161616: + return hsVector3(fPos161616[0] * fEncoding.fPosScale, + fPos161616[1] * fEncoding.fPosScale, + fPos161616[2] * fEncoding.fPosScale); + case plSpanEncoding::kPos101010: + return hsVector3(int(*fPos101010 & 0x3f) * fEncoding.fPosScale, + int((*fPos101010 >> 10) & 0x3f) * fEncoding.fPosScale, + int((*fPos101010 >> 20) & 0x3f) * fEncoding.fPosScale); + case plSpanEncoding::kPos008: + return hsVector3(0,0, *fPos888 * fEncoding.fPosScale); + } + return hsVector3(0,0,0); + }; + hsPoint3 Position(const hsPoint3& p) const + { + hsPoint3 pos(p); + pos += DelPos(); + return pos; + }; + UInt32 Color(UInt32 c) const + { + switch(fEncoding.fCode & plSpanEncoding::kColMask) + { + case plSpanEncoding::kColA8: + return (c & 0x00ffffff) | *fA8; + case plSpanEncoding::kColI8: + return (c & 0xff000000) + | (*fI8 << 16) + | (*fI8 << 8) + | (*fI8 << 0); + case plSpanEncoding::kColAI88: + { + const UInt32 col = *fAI88 & 0xff; + return ((*fAI88 & 0xff00) << 24) + | (col << 16) + | (col << 8) + | (col << 0); + } + case plSpanEncoding::kColRGB888: + return (c & 0xff000000) + | (fRGB888[0] << 16) + | (fRGB888[1] << 8) + | (fRGB888[2] << 0); + case plSpanEncoding::kColARGB8888: + return *fARGB8888; + } + return c; + }; +}; + +#endif // plSpanInstance_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTemplate.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTemplate.cpp new file mode 100644 index 00000000..aeee9f87 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTemplate.cpp @@ -0,0 +1,137 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plSpanTemplate.h" +#include "plSpanInstance.h" + +#include "hsStream.h" +#include "hsGeometry3.h" + + + +plSpanTemplate::plSpanTemplate() +: fNumVerts(0), + fFormat(0), + fData(nil), + fNumTris(0), + fIndices(nil) +{ +} + +UInt32 plSpanTemplate::CalcStride() +{ + fStride = 0; + if( NumPos() ) + fStride += sizeof(hsPoint3); + if( NumNorm() ) + fStride += sizeof(hsVector3); + if( NumColor() ) + fStride += sizeof(UInt32); + if( NumColor2() ) + fStride += sizeof(UInt32); + if( NumWgtIdx() ) + fStride += sizeof(UInt32); + if( NumUVWs() ) + fStride += (UInt8)(sizeof(hsPoint3) * NumUVWs()); + if( NumWeights() ) + fStride += (UInt8)(sizeof(hsScalar) * NumWeights()); + + return UInt32(fStride); +} + +void plSpanTemplate::Alloc(UInt16 format, UInt32 numVerts, UInt32 numTris) +{ + DeAlloc(); + fNumVerts = (UInt16)numVerts; + fFormat = format; + CalcStride(); + + fNumTris = (UInt16)numTris; + + fData = TRACKED_NEW UInt8[VertSize()]; + + fIndices = TRACKED_NEW UInt16[NumIndices()]; +} + +void plSpanTemplate::DeAlloc() +{ + delete [] fData; + delete [] fIndices; + + fNumTris = 0; + fNumVerts = 0; + fFormat = 0; + + fData = nil; + fIndices = nil; +} + +void plSpanTemplate::Read(hsStream* stream) +{ + fNumVerts = stream->ReadSwap16(); + fFormat = stream->ReadSwap16(); + fNumTris = stream->ReadSwap16(); + + Alloc(fFormat, fNumVerts, fNumTris); + + stream->Read(VertSize(), fData); + stream->Read(IndexSize(), fIndices); +} + +void plSpanTemplate::Write(hsStream* stream) const +{ + stream->WriteSwap16(fNumVerts); + stream->WriteSwap16(fFormat); + stream->WriteSwap16(fNumTris); + + stream->Write(VertSize(), fData); + stream->Write(IndexSize(), fIndices); +} + + +void plSpanTemplateB::ComputeBounds() +{ + fLocalBounds.MakeEmpty(); + int i; + for( i = 0; i < NumVerts(); i++ ) + fLocalBounds.Union(Position(i)); +} + +void plSpanTemplateB::AllocColors() +{ + fMultColors = TRACKED_NEW hsColorRGBA[NumVerts()]; + fAddColors = TRACKED_NEW hsColorRGBA[NumVerts()]; +} + +void plSpanTemplateB::DeAllocColors() +{ + delete [] fMultColors; + delete [] fAddColors; + + fMultColors = nil; + fAddColors = nil; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTemplate.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTemplate.h new file mode 100644 index 00000000..fb04013b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTemplate.h @@ -0,0 +1,264 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSpanTemplate_inc +#define plSpanTemplate_inc + +#include "hsGeometry3.h" +#include "hsBounds.h" +#include "hsColorRGBA.h" +#include "plRenderLevel.h" + +class hsStream; + +class INode; +class hsGMaterial; + +class plSpanTemplate +{ +public: + // 99% of the time, the defaults are fine. Just tell me + // how many UVWs, and whether you've got color. + static UInt16 MakeFormat(hsBool hasColor, int numUVWs, + hsBool hasWgtIdx = false, + int numWgts = 0, + hsBool hasNorm = true, + hsBool hasPos = true, + hsBool hasColor2 = true) + { + return (hasPos ? kPosMask : 0) + | (hasNorm ? kNormMask : 0) + | (hasColor ? kColorMask : 0) + | (hasWgtIdx ? kWgtIdxMask : 0) + | ((numUVWs << 4) & kUVWMask) + | ((numWgts << 8) & kWeightMask) + | (hasColor2 ? kColor2Mask : 0); // Till we can get this out of here. + }; + enum + { + kPosMask = 0x1, + + kNormMask = 0x2, + + kColorMask = 0x4, + + kWgtIdxMask = 0x8, + + kUVWMask = 0xf0, + + kWeightMask = 0x300, + + kColor2Mask = 0x400 + }; + enum Channel + { + kPosition, + kWeight, + kWgtIdx, + kNormal, + kColor, + kColor2, + kUVW + }; +protected: + + UInt16 fNumVerts; + UInt16 fFormat; + UInt8 fStride; + UInt16 fNumTris; + + // Data stored interleaved. Any channels may be omitted, but + // existing channels are in exactly the following order: + // Position + // Weights + // Indices + // Normal + // Color + // Color2 + // UVWs + UInt8* fData; + + UInt16* fIndices; + + friend class plClusterUtil; +public: + plSpanTemplate(); + virtual ~plSpanTemplate() { DeAlloc(); } + + const UInt8* VertData() const { return fData; } + + const UInt16* IndexData() const { return fIndices; } + + UInt32 NumVerts() const { return fNumVerts; } + UInt32 Stride() const { return UInt32(fStride); } + UInt32 CalcStride(); + UInt32 VertSize() const { return NumVerts() * Stride(); } + + UInt32 NumTris() const { return fNumTris; } + UInt32 NumIndices() const { return NumTris() * 3; } + UInt32 IndexSize() const { return NumIndices() * sizeof(UInt16); } + + UInt8 PositionOffset() const { return UInt8(0); } + UInt8 WgtIdxOffset() const { return UInt8(PositionOffset() + NumPos() * sizeof(hsPoint3)); } + UInt8 WeightOffset() const { return UInt8(WgtIdxOffset() + NumWgtIdx() * sizeof(UInt32)); } + UInt8 NormalOffset() const { return UInt8(WeightOffset() + NumWeights() * sizeof(hsScalar)); } + UInt8 ColorOffset() const { return UInt8(NormalOffset() + NumNorm() * sizeof(hsVector3)); } + UInt8 Color2Offset() const { return UInt8(ColorOffset() + NumColor() * sizeof(UInt32)); } + UInt8 UVWOffset() const { return UInt8(Color2Offset() + NumColor2() * sizeof(UInt32)); } + + UInt32 NumUVWs() const { return (fFormat & kUVWMask) >> 4; } + UInt32 NumWeights() const { return (fFormat & kWeightMask) >> 8; } + + UInt32 NumPos() const { return (fFormat & kPosMask) >> 0; } + UInt32 NumNorm() const { return (fFormat & kNormMask) >> 1; } + UInt32 NumColor() const { return (fFormat & kColorMask) >> 2; } + UInt32 NumColor2() const { return (fFormat & kColor2Mask) >> 10; } + UInt32 NumWgtIdx() const { return (fFormat & kWgtIdxMask) >> 3; } + + hsPoint3* Position(int i) const { return (hsPoint3*)GetData(kPosition, i); } + hsVector3* Normal(int i) const { return (hsVector3*)GetData(kNormal, i); } + UInt32* Color(int i) const { return (UInt32*)GetData(kColor, i); } + UInt32* Color2(int i) const { return (UInt32*)GetData(kColor2, i); } + UInt32* WgtIdx(int i) const { return (UInt32*)GetData(kWgtIdx, i); } + hsPoint3* UVWs(int iv, int iuv) const { return (hsPoint3*)GetData(kUVW, iv, iuv); } + hsScalar* Weight(int iv, int iw) const { return (hsScalar*)GetData(kWeight, iv, iw); } + + UInt8* GetData(Channel chan, int i, int j=0) const + { + ValidateInput(chan, i, j); + UInt8* base = fData + i * fStride; + switch(chan) + { + case kPosition: + return base; + case kWeight: + return base + + NumPos() * sizeof(hsPoint3) + + j * sizeof(hsScalar); + case kWgtIdx: + return base + + NumPos() * sizeof(hsPoint3) + + NumWeights() * sizeof(hsScalar); + case kNormal: + return base + + NumPos() * sizeof(hsPoint3) + + NumWeights() * sizeof(hsScalar) + + NumWgtIdx() * sizeof(UInt32); + case kColor: + return base + + NumPos() * sizeof(hsPoint3) + + NumWeights() * sizeof(hsScalar) + + NumWgtIdx() * sizeof(UInt32) + + NumNorm() * sizeof(hsVector3); + case kColor2: + return base + + NumPos() * sizeof(hsPoint3) + + NumWeights() * sizeof(hsScalar) + + NumWgtIdx() * sizeof(UInt32) + + NumNorm() * sizeof(hsVector3) + + NumColor() * sizeof(UInt32); + case kUVW: + return base + + NumPos() * sizeof(hsPoint3) + + NumWeights() * sizeof(hsScalar) + + NumWgtIdx() * sizeof(UInt32) + + NumNorm() * sizeof(hsVector3) + + NumColor() * sizeof(UInt32) + + NumColor2() * sizeof(UInt32) + + j * sizeof(hsPoint3); + } + hsAssert(false, "Unrecognized vertex channel"); + return nil; + } + + hsBool ValidateInput(Channel chan, int i, int j) const + { + switch(chan) + { + case kPosition: + hsAssert(NumPos(), "Invalid data request"); + return NumPos() > 0; + case kWeight: + hsAssert(NumWeights(), "Invalid data request"); + return NumWeights() > 0; + case kWgtIdx: + hsAssert(NumWgtIdx() > j, "Invalid data request"); + return NumWgtIdx() > j; + case kNormal: + hsAssert(NumNorm(), "Invalid data request"); + return NumNorm() > 0; + case kColor: + hsAssert(NumColor(), "Invalid data request"); + return NumColor() > 0; + case kColor2: + hsAssert(NumColor2(), "Invalid data request"); + return NumColor2() > 0; + case kUVW: + hsAssert(NumUVWs() > j, "Invalid data request"); + return NumUVWs() > j; + } + hsAssert(false, "Unrecognized vertex channel"); + return false; + } + + void Alloc(UInt16 format, UInt32 numVerts, UInt32 numTris); + void DeAlloc(); + + void Read(hsStream* s); + void Write(hsStream* s) const; +}; + +// An export only version +class plSpanTemplateB : public plSpanTemplate +{ + INode* fSrc; + hsBounds3Ext fLocalBounds; + + hsColorRGBA* fMultColors; + hsColorRGBA* fAddColors; + +public: + plSpanTemplateB(INode* src) : plSpanTemplate(), fSrc(src), fMaterial(nil) {} + virtual ~plSpanTemplateB() { DeAllocColors(); } + + void ComputeBounds(); + + const hsBounds3Ext& GetLocalBounds() const { return fLocalBounds; } + + INode* GetSrcNode() const { return fSrc; } + + hsGMaterial* fMaterial; + + plRenderLevel fRenderLevel; + + hsColorRGBA* MultColor(int i) const { return &fMultColors[i]; } + hsColorRGBA* AddColor(int i) const { return &fAddColors[i]; } + + void AllocColors(); + void DeAllocColors(); +}; + +#endif // plSpanTemplate_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTypes.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTypes.cpp new file mode 100644 index 00000000..6d2ad324 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTypes.cpp @@ -0,0 +1,562 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSpanTypes Class Functions // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// 5.3.2001 mcn - Created. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plSpanTypes.h" +#include "hsStream.h" +#include "../pnKeyedObject/plKey.h" +#include "../plPipeline/plGBufferGroup.h" +#include "../plPipeline/hsGDeviceRef.h" +#include "../plGLight/plLightInfo.h" +#include "plDrawable.h" +#include "plAuxSpan.h" +#include "plAccessSnapShot.h" + +///////////////////////////////////////////////////////////////////////////// +//// plSpan Functions //////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Read //////////////////////////////////////////////////////////////////// + +void plSpan::Read( hsStream *stream ) +{ + fSubType = (UInt16)(stream->ReadSwap32()); + fFogEnvironment = nil; + + fMaterialIdx = stream->ReadSwap32(); + fLocalToWorld.Read( stream ); + fWorldToLocal.Read( stream ); + fProps = stream->ReadSwap32(); + fLocalBounds.Read( stream ); + fWorldBounds.Read( stream ); + + fNumMatrices = (UInt8)(stream->ReadSwap32()); + fBaseMatrix = stream->ReadSwap32(); + + fLocalUVWChans = stream->ReadSwap16(); + fMaxBoneIdx = stream->ReadSwap16(); + fPenBoneIdx = stream->ReadSwap16(); + + fMinDist = stream->ReadSwapScalar(); + fMaxDist = stream->ReadSwapScalar(); + + if( fProps & kWaterHeight ) + fWaterHeight = stream->ReadSwapScalar(); + +#ifdef HS_DEBUGGING + fOwnerKey = nil; +#endif +} + +//// Write /////////////////////////////////////////////////////////////////// + +void plSpan::Write( hsStream *stream ) +{ + stream->WriteSwap32(fSubType); + + stream->WriteSwap32( fMaterialIdx ); + + fLocalToWorld.Write( stream ); + fWorldToLocal.Write( stream ); + stream->WriteSwap32( fProps ); + fLocalBounds.Write( stream ); + fWorldBounds.Write( stream ); + + stream->WriteSwap32( fNumMatrices ); + stream->WriteSwap32( fBaseMatrix ); + + stream->WriteSwap16( fLocalUVWChans ); + stream->WriteSwap16( fMaxBoneIdx ); + stream->WriteSwap16( fPenBoneIdx ); + + stream->WriteSwapScalar( fMinDist ); + stream->WriteSwapScalar( fMaxDist ); + + if( fProps & kWaterHeight ) + stream->WriteSwapScalar(fWaterHeight); +} + +void plSpan::RemoveAuxSpan(plAuxSpan* aux) +{ + int i = fAuxSpans.Find(aux); + int newCount = fAuxSpans.GetCount()-1; + if( i < newCount ) + { + fAuxSpans[i] = fAuxSpans[fAuxSpans.GetCount()-1]; + } + fAuxSpans.SetCount(newCount); +} + +void plSpan::AddAuxSpan(plAuxSpan* aux) +{ + fAuxSpans.Append(aux); +} + +// AddPermaLight //////////////////////////////////////////////////////////////// +// PermaLights are permanently assigned to a set of spans. There's no checking +// for in range or anything, they are just on. + +void plSpan::AddPermaLight(plLightInfo* li, hsBool proj) +{ + if( li ) + { + hsTArray& lights = proj ? fPermaProjs : fPermaLights; + int idx = lights.Find(li); + if( lights.kMissingIndex == idx ) + lights.Append(li); + if( lights.GetCount() ) + fProps |= proj ? kPropHasPermaProjs : kPropHasPermaLights; + } +} + +void plSpan::RemovePermaLight(plLightInfo* li, hsBool proj) +{ + hsTArray& lights = proj ? fPermaProjs : fPermaLights; + int idx = lights.Find(li); + if( lights.kMissingIndex != idx ) + { + lights.Remove(idx); + if( !lights.GetCount() ) + fProps &= ~(proj ? kPropHasPermaProjs : kPropHasPermaLights); + } +} + +//// AddLight //////////////////////////////////////////////////////////////// +// Smart function for maintaining the sorted list of lights for a plSpan. + +void plSpan::AddLight( plLightInfo *li, hsScalar strength, hsScalar scale, hsBool proj ) const +{ + hsTArray& lights = proj ? fProjectors : fLights; + hsTArray& strengths = proj ? fProjStrengths : fLightStrengths; + hsTArray& scales = proj ? fProjScales : fLightScales; + + int i; + + for( i = 0; i < lights.GetCount(); i++ ) + { + if( strengths[ i ] < strength ) + break; + } + lights.Insert(i, li); + strengths.Insert(i, strength); + scales.Insert(i, hsScalar(UInt32(scale * 127.9f)) / 127.f); +} + +void plSpan::ClearLights() const +{ + fLights.SetCount(0); + fLightStrengths.SetCount(0); + fLightScales.SetCount(0); + fProjectors.SetCount(0); + fProjStrengths.SetCount(0); + fProjScales.SetCount(0); + + int i; + for( i = 0; i < fPermaLights.GetCount(); i++ ) + { + if( !(fPermaLights[i]->IsIdle() || fPermaLights[i]->GetProperty(plLightInfo::kLPShadowOnly)) ) + { + fLights.Append(fPermaLights[i]); + fLightStrengths.Append(2.f); + fLightScales.Append(1.f); + } + } + for( i = 0; i < fPermaProjs.GetCount(); i++ ) + { + if( !(fPermaProjs[i]->IsIdle() || fPermaProjs[i]->GetProperty(plLightInfo::kLPShadowOnly)) ) + { + fProjectors.Append(fPermaProjs[i]); + fProjStrengths.Append(2.f); + fProjScales.Append(1.f); + } + } + + fShadowSlaveBits.Clear(); +} + +//// CanMergeInto //////////////////////////////////////////////////////////// + +hsBool plSpan::CanMergeInto( plSpan *other ) +{ + if( fTypeMask ^ other->fTypeMask ) + { + return false; + } + + // Make sure lighting equations match + if( ( ( fProps ^ other->fProps ) & kLiteMask ) != 0 ) + { + return false; + } + + if( fNumMatrices != other->fNumMatrices ) + return false; + if( fBaseMatrix != other->fBaseMatrix ) + return false; + + if( fMaterialIdx != other->fMaterialIdx ) + { + return false; + } + if( fFogEnvironment != other->fFogEnvironment ) + return false; + + // Don't bother checking for having exactly the same matrix elements. + // Either they are both ident, or they are inequal. + if( fLocalToWorld != other->fLocalToWorld ) +// if( !(fLocalToWorld.fFlags & other->fLocalToWorld.fFlags & hsMatrix44::kIsIdent) ) + { + return false; + } + + if( fLights.GetCount() != other->fLights.GetCount() ) + return false; + if( fProjectors.GetCount() != other->fProjectors.GetCount() ) + return false; + + if( fLights.GetCount() ) + { + if( !HSMemory::EqualBlocks(fLights.AcquireArray(), other->fLights.AcquireArray(), fLights.GetCount() * sizeof(plLightInfo*)) ) + return false; + if( !HSMemory::EqualBlocks(fLightScales.AcquireArray(), other->fLightScales.AcquireArray(), fLights.GetCount() * sizeof(hsScalar)) ) + return false; + } + if( fProjectors.GetCount() ) + { + if( !HSMemory::EqualBlocks(fProjectors.AcquireArray(), other->fProjectors.AcquireArray(), fProjectors.GetCount() * sizeof(plLightInfo*)) ) + return false; + if( !HSMemory::EqualBlocks(fProjScales.AcquireArray(), other->fProjScales.AcquireArray(), fProjectors.GetCount() * sizeof(hsScalar)) ) + return false; + } + + if( fShadowSlaveBits != other->fShadowSlaveBits ) + return false; + + if( fSubType ^ other->fSubType ) + return false; + + return true; +} + +//// MergeInto /////////////////////////////////////////////////////////////// + +void plSpan::MergeInto( plSpan *other ) +{ + int i; + for( i = 0; i < GetNumAuxSpans(); i++ ) + other->fAuxSpans.Append(GetAuxSpan(i)); +} + +//// Constructor & Destructor //////////////////////////////////////////////// + +plSpan::plSpan() +{ + fTypeMask = kSpan; + fSubType = plDrawable::kSubNormal; + fMaterialIdx = (UInt32)-1; + fFogEnvironment = nil; + fProps = 0; + + fSnapShot = nil; + + ClearLights(); + fVisSet.SetBit(0); + + fNumMatrices = 0; + fBaseMatrix = 0; + fLocalUVWChans = 0; + fMaxBoneIdx = 0; + fPenBoneIdx = 0; + + fMinDist = fMaxDist = -1.f; + + fWaterHeight = 0; + +#ifdef HS_DEBUGGING + fOwnerKey = nil; +#endif +} + +void plSpan::Destroy() +{ + if( fSnapShot ) + { + fSnapShot->Destroy(); + delete fSnapShot; + } + + int i; + for( i = 0; i < fAuxSpans.GetCount(); i++ ) + fAuxSpans[i]->fDrawable = nil; +} + +////////////////////////////////////////////////////////////////////////////// +//// plVertexSpan Functions ////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +plVertexSpan::plVertexSpan() +{ + fTypeMask |= kVertexSpan; + fGroupIdx = (UInt32)-1; + fVBufferIdx = (UInt32)-1; + fCellIdx = (UInt32)-1; + fCellOffset = (UInt32)-1; +} + +void plVertexSpan::Read( hsStream* stream ) +{ + plSpan:: Read( stream ); + + fGroupIdx = stream->ReadSwap32(); + fVBufferIdx = stream->ReadSwap32(); + fCellIdx = stream->ReadSwap32(); + fCellOffset = stream->ReadSwap32(); + fVStartIdx = stream->ReadSwap32(); + fVLength = stream->ReadSwap32(); +} + +void plVertexSpan::Write( hsStream* stream ) +{ + plSpan::Write( stream ); + + stream->WriteSwap32( fGroupIdx ); + stream->WriteSwap32( fVBufferIdx ); + stream->WriteSwap32( fCellIdx ); + stream->WriteSwap32( fCellOffset ); + stream->WriteSwap32( fVStartIdx ); + stream->WriteSwap32( fVLength ); +} + +hsBool plVertexSpan::CanMergeInto( plSpan *other ) +{ + if( !plSpan::CanMergeInto( other ) ) + return false; + + plVertexSpan *otherSpan = (plVertexSpan*)other; + + if( fGroupIdx != otherSpan->fGroupIdx || + fVBufferIdx != otherSpan->fVBufferIdx || + fCellIdx != otherSpan->fCellIdx ) + { + return false; + } + + return true; +} + +void plVertexSpan::MergeInto( plSpan *other ) +{ + plSpan::MergeInto( other ); + + plVertexSpan* otherIce = (plVertexSpan*)other; + int min, max; + + + min = fVStartIdx; + max = min + fVLength - 1; + if( otherIce->fVStartIdx < min ) + min = otherIce->fVStartIdx; + if( otherIce->fVStartIdx + otherIce->fVLength - 1 > max ) + max = otherIce->fVStartIdx + otherIce->fVLength - 1; + + otherIce->fVStartIdx = min; + otherIce->fVLength = max - min + 1; +} + + +////////////////////////////////////////////////////////////////////////////// +//// plIcicle Functions ////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Read //////////////////////////////////////////////////////////////////// + +void plIcicle::Read( hsStream *stream ) +{ + plVertexSpan::Read( stream ); + + fIBufferIdx = stream->ReadSwap32(); + fIPackedIdx = fIStartIdx = stream->ReadSwap32(); + fILength = stream->ReadSwap32(); + + if( fProps & kPropFacesSortable ) + { + /// Read in sorting data + int i; + + + fSortData = TRACKED_NEW plGBufferTriangle[ fILength / 3 ]; + for( i = 0; i < fILength / 3; i++ ) + fSortData[ i ].Read( stream ); + } + else + fSortData = nil; +} + +//// Write /////////////////////////////////////////////////////////////////// + +void plIcicle::Write( hsStream *stream ) +{ + plVertexSpan::Write( stream ); + + stream->WriteSwap32( fIBufferIdx ); + stream->WriteSwap32( fIStartIdx ); + stream->WriteSwap32( fILength ); + + if( fProps & kPropFacesSortable ) + { + /// Write out sorting data + int i; + for( i = 0; i < fILength / 3; i++ ) + fSortData[ i ].Write( stream ); + } +} + +//// Destroy ///////////////////////////////////////////////////////////////// + +void plIcicle::Destroy( void ) +{ + plSpan::Destroy(); + delete [] fSortData; + fSortData = nil; +} + +//// CanMergeInto //////////////////////////////////////////////////////////// + +hsBool plIcicle::CanMergeInto( plSpan *other ) +{ + if( !plVertexSpan::CanMergeInto( other ) ) + return false; + + plIcicle *otherIce = (plIcicle *)other; + + if( fIBufferIdx != otherIce->fIBufferIdx ) + return false; + + if( (fNumMatrices != otherIce->fNumMatrices) + ||(fBaseMatrix != otherIce->fBaseMatrix) ) + return false; + + if( fIPackedIdx != otherIce->fIPackedIdx + otherIce->fILength ) + { + return false; + } + + return true; +} + +//// MergeInto /////////////////////////////////////////////////////////////// + +void plIcicle::MergeInto( plSpan *other ) +{ + plVertexSpan::MergeInto( other ); + + plIcicle *otherIce = (plIcicle *)other; + + otherIce->fILength += fILength; +} + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plIcicle::plIcicle() +{ + fTypeMask |= kIcicleSpan; + + fSortData = nil; +} + +////////////////////////////////////////////////////////////////////////////// +//// plParticleSpan Functions //////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Constructor ///////////////////////////////////////////////////////////// + +plParticleSpan::plParticleSpan() : plIcicle() +{ + fTypeMask |= kParticleSpan; + + fProps |= kPropProjAsVtx; + + fSource = nil; + fSortCount = 0; +} + +//// CanMergeInto //////////////////////////////////////////////////////////// + +hsBool plParticleSpan::CanMergeInto( plSpan *other ) +{ + return plIcicle::CanMergeInto( other ); +} + +//// MergeInto /////////////////////////////////////////////////////////////// + +void plParticleSpan::MergeInto( plSpan *other ) +{ + plIcicle::MergeInto( other ); +} + +//// Destroy ///////////////////////////////////////////////////////////////// + +void plParticleSpan::Destroy( void ) +{ + plIcicle::Destroy(); + fSource = nil; + if( fParentSet != nil ) + { + fParentSet->fRefCount--; + if( fParentSet->fRefCount == 0 ) + delete fParentSet; + } +} + + +////////////////////////////////////////////////////////////////////////////// +//// plParticleSet Functions ///////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Constructor ///////////////////////////////////////////////////////////// + +plParticleSet::plParticleSet() +{ +} + +//// Destructor ////////////////////////////////////////////////////////////// + +plParticleSet::~plParticleSet() +{ + fVLength = 0; // Flag as empty +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTypes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTypes.h new file mode 100644 index 00000000..3524d7ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plSpanTypes.h @@ -0,0 +1,314 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSpanTypes Header - Class Definitions for all the span types // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// 5.3.2001 mcn - Created. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plSpans_h +#define _plSpans_h + + +#include "hsBitVector.h" +#include "hsTemplates.h" +#include "hsBounds.h" +#include "hsMatrix44.h" + +#include "../pnKeyedObject/plKey.h" + +class hsGMaterial; +class plGeometrySpan; +class plFogEnvironment; +class plLightInfo; +class plGBufferTriangle; +class hsGDeviceRef; +class plShadowSlave; +class plAuxSpan; +class plAccessSnapShot; +class plDrawableSpans; + +//// plSpan Class Definition ///////////////////////////////////////////////// +// Represents the generic span for any kind of drawableMatter derivative. + +class plSpan +{ + public: + enum { + // Renumber these the next time we bump major version #s (no reason to do it now) + kPropNoDraw = 0x001, + kPropNoShadowCast = 0x002, + kPropFacesSortable = 0x004, + kPropVolatile = 0x008, // Means that the vertices for this span are volatile + kWaterHeight = 0x010, // Vtx Z value doesn't mean vtx pos, use fWaterHeight + kPropRunTimeLight = 0x020, // Enable dynamic lighting + kPropReverseSort = 0x040, // Sort front to back instead of back to front + kPropHasPermaLights = 0x080, // Has a list of permanently assigned lights. + kPropHasPermaProjs = 0x100, // Has a list of permanently assigned projectors. + // Lighting types + kLiteMaterial = 0x000, // Default + kLiteVtxPreshaded = 0x200, // For particle systems, gives us vtx alpha + kLiteVtxNonPreshaded = 0x400, // Runtime lit, gives us vtx alpha + kLiteProjection = 0x800, + kLiteShadowErase = 0x1000, + kLiteShadow = 0x2000, + kLiteMask = kLiteMaterial + | kLiteVtxPreshaded + | kLiteVtxNonPreshaded + | kLiteProjection + | kLiteShadowErase + | kLiteShadow, // Mask for all types + + kPropMatHasSpecular = 0x10000, // Shortcut for efficiency; means the span's material has specular checked on it + kPropProjAsVtx = 0x20000, // Force projected lights to act as vertex lights + kPropSkipProjection = 0x40000, // Just skip projected lights entirely + kPropNoShadow = 0x80000, // Never cast a shadow on this. + kPropForceShadow = 0x100000, // Force casting a shadow, even if the pipeline thinks it'll look bad. + kPropDisableNormal = 0x200000, // Not a member of the normal vis-set + kPropCharacter = 0x400000, // Is a member of the character vis-set + kPartialSort = 0x800000, + kVisLOS = 0x1000000 + + }; + enum plSpanType { + kSpan = 0x0, + kVertexSpan = 0x1, + kIcicleSpan = 0x2, + kParticleSpan = 0x8, + kParticleSet = 0x10 + }; + + UInt16 fTypeMask; // For safe casting. Or it with the type you want to cast to, don't check equality + UInt16 fSubType; // Or'ed from plDrawable::plDrawableSubType + UInt32 fMaterialIdx; // Index into drawable's material array + hsMatrix44 fLocalToWorld; + hsMatrix44 fWorldToLocal; + UInt32 fBaseMatrix; + UInt8 fNumMatrices; + UInt16 fLocalUVWChans; + UInt16 fMaxBoneIdx; + UInt16 fPenBoneIdx; + UInt32 fProps; + hsBounds3Ext fLocalBounds; + hsBounds3Ext fWorldBounds; + plFogEnvironment *fFogEnvironment; + + // Use setter/getters below. + hsScalar fMinDist; + hsScalar fMaxDist; + + hsScalar fWaterHeight; + + hsBitVector fVisSet; + hsBitVector fVisNot; + + mutable plAccessSnapShot* fSnapShot; + +// mutable hsBitVector fLightBits; + mutable hsTArray fLights; + mutable hsTArray fLightStrengths; + mutable hsTArray fLightScales; + mutable hsTArray fProjectors; + mutable hsTArray fProjStrengths; + mutable hsTArray fProjScales; + + mutable hsBitVector fShadowBits; + mutable hsBitVector fShadowSlaveBits; + + mutable hsTArray fAuxSpans; + + hsTArray fPermaLights; + hsTArray fPermaProjs; + +#ifdef HS_DEBUGGING + plKey fOwnerKey; // DEBUG ONLY--drawInterface owner key +#endif + + plSpan(); + + const hsBitVector& GetShadowSlaves() const { return fShadowSlaveBits; } + void AddShadowSlave(int iSlave) const { fShadowSlaveBits.SetBit(iSlave); } + + void SetShadowBit(UInt32 idx) const { fShadowBits.SetBit(idx); } + void ClearShadowBits() const { fShadowBits.Clear(); } + hsBool IsShadowBitSet(UInt32 idx) const { return fShadowBits.IsBitSet(idx); } + void ClearLights() const; + + void AddLight( plLightInfo* li, hsScalar strength, hsScalar scale, hsBool proj ) const; + + hsTArray& GetLightList(hsBool proj) const { return proj ? fProjectors : fLights; } + + UInt32 GetNumLights(hsBool proj) const { return proj ? fProjectors.GetCount() : fLights.GetCount(); } + plLightInfo* GetLight(int i, hsBool proj) const { return proj ? fProjectors[i] : fLights[i]; } + hsScalar GetLightStrength(int i, hsBool proj) const { return proj ? fProjStrengths[i] : fLightStrengths[i]; } + hsScalar GetLightScale(int i, hsBool proj) const { return proj ? fProjScales[i] : fLightScales[i]; } + + void AddPermaLight(plLightInfo* li, hsBool proj); + void RemovePermaLight(plLightInfo* li, hsBool proj); + + const hsBitVector& GetVisSet() const { return fVisSet; } + const hsBitVector& GetVisNot() const { return fVisNot; } + void SetVisBit(UInt32 w, hsBool on) { fVisSet.SetBit(w, on); } + void SetVisNot(UInt32 w, hsBool on) { fVisNot.SetBit(w, on); } + + void RemoveAuxSpan(plAuxSpan* aux); + void AddAuxSpan(plAuxSpan* aux); + int GetNumAuxSpans() const { return fAuxSpans.GetCount(); } + plAuxSpan* GetAuxSpan(int i) const { return fAuxSpans[i]; } + + virtual void Read( hsStream* stream ); + virtual void Write( hsStream* stream ); + + virtual hsBool CanMergeInto( plSpan* other ); + virtual void MergeInto( plSpan* other ); + virtual void Destroy( void ); + + void SetMinDist(hsScalar minDist) { fMinDist = minDist; } + void SetMaxDist(hsScalar maxDist) { fMaxDist = maxDist; } + hsScalar GetMinDist() const { return fMinDist; } + hsScalar GetMaxDist() const { return fMaxDist; } +}; + +//// plVertexSpan Definition +// All span types which are based on vertices derive from this. That's +// currently all span types. +class plVertexSpan : public plSpan +{ +public: + + // Stuff internal + UInt32 fGroupIdx; // Which buffer group, i.e. which vertex format + + UInt32 fVBufferIdx; // Which vertex buffer in group + UInt32 fCellIdx; // Cell index inside the vertex buffer + UInt32 fCellOffset; // Offset inside the cell + UInt32 fVStartIdx; // Start vertex # in the actual interlaced buffer + UInt32 fVLength; // Length of this span in the buffer + + plVertexSpan(); + + virtual void Read( hsStream* stream ); + virtual void Write( hsStream* stream ); + + virtual hsBool CanMergeInto( plSpan* other ); + virtual void MergeInto( plSpan* other ); +}; + +//// plIcicle Class Definition /////////////////////////////////////////////// +// Represents the generic span for a set of triangles to be drawn. + +class plIcicle : public plVertexSpan +{ + public: + + UInt32 fIBufferIdx; // Which index buffer in group + UInt32 fIStartIdx; // Redundant, since all spans are contiguous. Here for debugging + UInt32 fILength; // Length of this span in the buffer + // The index into the indexbuffer ref. This can be different from fIStartIdx if spans get + // culled, then we pack the non-culled index spans into the beginning of the index buffer ref, + // so we can still put them all out with a single DIP call. + mutable UInt32 fIPackedIdx; + + // Run-time-only stuff + plGBufferTriangle *fSortData; // Indices & center points for sorting tris in this span (optional) + + plIcicle(); + + virtual void Read( hsStream* stream ); + virtual void Write( hsStream* stream ); + + virtual hsBool CanMergeInto( plSpan* other ); + virtual void MergeInto( plSpan* other ); + virtual void Destroy( void ); +}; + +//// plParticleSpan Class Definition ///////////////////////////////////////// +// A single span that contains triangles for drawing particle-like things. +// Since we want to draw these just like icicles, we'll just derive from +// it... + +class plParticleEmitter; +class plParticleSet; +class plParticleSpan : public plIcicle +{ + public: + + plParticleEmitter* fSource; // Source emitter, used to get array of plParticleCores + UInt32 fNumParticles; + UInt32 fSortCount; + UInt32 fSrcSpanIdx; + + plParticleSet* fParentSet; + + plParticleSpan(); + + virtual void Read( hsStream* stream ) { /*plParticleSpans don't read in!*/ } + virtual void Write( hsStream* stream ) { /*plParticleSpans don't write out!*/ } + + virtual hsBool CanMergeInto( plSpan* other ); + virtual void MergeInto( plSpan* other ); + virtual void Destroy( void ); +}; + +//// plParticleSet Class Definition ////////////////////////////////////////// +// Represents a collection of dynamic plParticleSpans collected into one +// space, for rendering batches of particles. + +class plParticleSet +{ + public: + + UInt32 fRefCount; // Delete if this gets to 0 + UInt32 fDIEntry; // Our false DIIndices entry index + + UInt32 fGroupIdx; // Which buffer group, i.e. which vertex format + UInt8 fFormat; + + UInt32 fVBufferIdx; + UInt32 fCellIdx; + UInt32 fCellOffset; + UInt32 fVStartIdx; + UInt32 fVLength; // Total v.b. length that all the icicles can take up UInt32 fIBufferIdx; + UInt32 fIBufferIdx; + UInt32 fIStartIdx; // Start index buffer position + UInt32 fILength; // Total i.b. length that all the icicles can take up + + UInt32 fNumSpans; + hsGMaterial* fMaterial; + + UInt32 fNextVStartIdx; + UInt32 fNextCellOffset; + UInt32 fNextIStartIdx; + + plParticleSet(); + ~plParticleSet(); +}; + +#endif // _plSpans_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plTimedInterp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plTimedInterp.h new file mode 100644 index 00000000..76332065 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plTimedInterp.h @@ -0,0 +1,148 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plTimedInterp_inc +#define plTimedInterp_inc + +// There's no auto update on this, the owner is +// required to call update periodically. +// +// This isn't optimized in any way, and is still +// fairly general. Adding features would most likely +// be best implemented in a derived type. +// Notably lacking features are: +// ability to read and write, +// auto-eval on significant time change +// +// +// Requires class T to have the following members +// T& operator+=(const class T& t); +// T& operator-=(const class T& t); +// T& operator*=(hsScalar scale); +// T& operator=(const class T&); // unless builtin = is adequate. +// +template class plTimedInterp +{ +protected: + T fInit; + T fCurr; + T fTarg; + + double fDuration; + double fEnd; + + const T& IEnd(); + const T& IBegin(); + const T& IEval(hsScalar parm); +public: + plTimedInterp(); + plTimedInterp(const T& t); + + const T& Set(const T& val) { return SetTarget(val, 0, 0); } + const T& SetTarget(const T& targ, double start, double duration); + + const T& Update(double t); + + operator T() const { return fCurr; } + const T& Value() const { return fCurr; } +}; + +template +plTimedInterp::plTimedInterp() +: fDuration(0), + fEnd(0) +{ +} + +template +plTimedInterp::plTimedInterp(const T& t) +: fInit(t), + fCurr(t), + fTarg(t), + fDuration(0), + fEnd(0) +{ +} + +template +const T& +plTimedInterp::Update(double t) +{ + if( fDuration <= 0 ) + return IEnd(); + + hsScalar parm = hsScalar((fEnd - t) / fDuration); + if( parm <= 0 ) + return IEnd(); + else if( parm >= 1.f ) + return IBegin(); + + return IEval(parm); +} + +template +const T& +plTimedInterp::SetTarget(const T& targ, double start, double dur) +{ + fEnd = start + dur; + fDuration = dur; + fTarg = targ; + if( dur <= 0 ) + fCurr = targ; + fInit = fCurr; + return fCurr; +} + +template +const T& +plTimedInterp::IEnd() +{ + fDuration = 0; + fCurr = fTarg; + return fCurr; +} + +template +const T& +plTimedInterp::IBegin() +{ + return fCurr; +} + +template +const T& +plTimedInterp::IEval(hsScalar parm) +{ + fCurr = fInit; + fCurr -= fTarg; + fCurr *= parm; + fCurr += fTarg; + return fCurr; +} + + +#endif // plTimedInterp_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plVisLOSMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plVisLOSMgr.cpp new file mode 100644 index 00000000..04e4e0aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plVisLOSMgr.cpp @@ -0,0 +1,447 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsBounds.h" +#include "hsFastMath.h" + +#include "plVisLOSMgr.h" + +#include "plSpaceTree.h" +#include "plDrawableSpans.h" +#include "plAccessGeometry.h" +#include "plAccessSpan.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" + +#include "../plScene/plSceneNode.h" +#include "../plScene/plPageTreeMgr.h" + +// Stuff for cursor los +#include "../plInputCore/plInputDevice.h" +#include "plPipeline.h" + + +#include "plTweak.h" + +#include +#include + +plVisLOSMgr* plVisLOSMgr::Instance() +{ + static plVisLOSMgr inst; + return &inst; +} + +hsBool plVisLOSMgr::ICheckSpaceTreeRecur(plSpaceTree* space, int which, hsTArray& hits) +{ + const plSpaceTreeNode& node = space->GetNode(which); + + if( node.fFlags & plSpaceTreeNode::kDisabled ) + return false; + + hsScalar closest; + // If it's a hit + if( ICheckBound(node.fWorldBounds, closest) ) + { + // If it's a leaf, + if( node.IsLeaf() ) + { + // add it to the list with the closest intersection point, + plSpaceHit* hit = hits.Push(); + hit->fIdx = which; + hit->fClosest = closest; + + return true; + } + // else recurse on its children + else + { + hsBool retVal = false; + if( ICheckSpaceTreeRecur(space, node.GetChild(0), hits) ) + retVal = true; + + if( ICheckSpaceTreeRecur(space, node.GetChild(1), hits) ) + retVal = true; + + return retVal; + } + } + return false; +} + +struct plCompSpaceHit : public std::binary_function +{ + bool operator()( const plSpaceHit& lhs, const plSpaceHit& rhs) const + { + return lhs.fClosest < rhs.fClosest; + } +}; + + +hsBool plVisLOSMgr::ICheckSpaceTree(plSpaceTree* space, hsTArray& hits) +{ + hits.SetCount(0); + + if( space->IsEmpty() ) + return false; + + // Hierarchical search down the tree for bounds intersecting the current ray. + hsBool retVal = ICheckSpaceTreeRecur(space, space->GetRoot(), hits); + + // Now sort them front to back. + plSpaceHit* begin = hits.AcquireArray(); + plSpaceHit* end = begin + hits.GetCount(); + + std::sort(begin, end, plCompSpaceHit()); + + return retVal; +} + +hsBool plVisLOSMgr::ISetup(const hsPoint3& pStart, const hsPoint3& pEnd) +{ + fCurrFrom = pStart; + fCurrTarg = pEnd; + + fMaxDist = hsVector3(&fCurrTarg, &fCurrFrom).Magnitude(); + + const hsScalar kMinMaxDist(0); + return fMaxDist > kMinMaxDist; +} + +hsBool plVisLOSMgr::Check(const hsPoint3& pStart, const hsPoint3& pEnd, plVisHit& hit) +{ + if( !fPageMgr ) + return false; + + // Setup any internals, like fMaxDist + if( !ISetup(pStart, pEnd) ) + return false; + + // Go through the nodes in the PageMgr and find the closest + // point of intersection for each scene node. If none are before + // pEnd, return false. + // Node come out sorted by closest point, front to back + static hsTArray hits; + if( !ICheckSpaceTree(fPageMgr->GetSpaceTree(), hits) ) + return false; + + // In front to back order, check inside each node. + // Our max distance can be changing as we do this, because a + // face hit will limit how far we need to look. When we hit the + // first node with a closest distance < fMaxDist, we're done. + hsBool retVal = false; + + int i; + for( i = 0; i < hits.GetCount(); i++ ) + { + if( hits[i].fClosest > fMaxDist ) + break; + + if( ICheckSceneNode(fPageMgr->GetNodes()[hits[i].fIdx], hit) ) + retVal = true; + } + + return retVal; +} + +hsBool plVisLOSMgr::ICheckSceneNode(plSceneNode* node, plVisHit& hit) +{ + static hsTArray hits; + if( !ICheckSpaceTree(node->GetSpaceTree(), hits) ) + return false; + + hsBool retVal = false; + int i; + for( i = 0; i < hits.GetCount(); i++ ) + { + if( hits[i].fClosest > fMaxDist ) + break; + + if( (node->GetDrawPool()[hits[i].fIdx]->GetRenderLevel().Level() > 0) + && !node->GetDrawPool()[hits[i].fIdx]->GetNativeProperty(plDrawable::kPropHasVisLOS) ) + continue; + + if( ICheckDrawable(node->GetDrawPool()[hits[i].fIdx], hit) ) + retVal = true; + } + + return retVal; +} + + +hsBool plVisLOSMgr::ICheckDrawable(plDrawable* d, plVisHit& hit) +{ + plDrawableSpans* ds = plDrawableSpans::ConvertNoRef(d); + if( !ds ) + return false; + + static hsTArray hits; + if( !ICheckSpaceTree(ds->GetSpaceTree(), hits) ) + return false; + + const hsBool isOpaque = !ds->GetRenderLevel().Level(); + + const hsTArray spans = ds->GetSpanArray(); + + hsBool retVal = false; + int i; + for( i = 0; i < hits.GetCount(); i++ ) + { + if( hits[i].fClosest > fMaxDist ) + break; + + if( isOpaque || (spans[hits[i].fIdx]->fProps & plSpan::kVisLOS) ) + { + if( ICheckSpan(ds, hits[i].fIdx, hit) ) + retVal = true; + } + } + + return retVal; +} + +hsBool plVisLOSMgr::ICheckSpan(plDrawableSpans* dr, UInt32 spanIdx, plVisHit& hit) +{ + if( !(dr->GetSpan(spanIdx)->fTypeMask & plSpan::kIcicleSpan) ) + return false; + + plAccessSpan src; + plAccessGeometry::Instance()->OpenRO(dr, spanIdx, src); + + const hsBool twoSided = !!(src.GetMaterial()->GetLayer(0)->GetMiscFlags() & hsGMatState::kMiscTwoSided); + + hsBool retVal = false; + + // We move into local space, look for hits, and convert the closest we find + // (if any) back into world space at the end. + hsPoint3 currFrom = src.GetWorldToLocal() * fCurrFrom; + hsPoint3 currTarg = src.GetWorldToLocal() * fCurrTarg; + + hsVector3 currDir(&currTarg, &currFrom); + hsScalar maxDist = currDir.Magnitude(); + + currDir /= maxDist; + + plAccTriIterator tri(&src.AccessTri()); + for( tri.Begin(); tri.More(); tri.Advance() ) + { + // Project the current ray onto the tri plane + hsVector3 norm = hsVector3(&tri.Position(1), &tri.Position(0)) % hsVector3(&tri.Position(2), &tri.Position(0)); + hsScalar dotNorm = norm.InnerProduct(currDir); + + const hsScalar kMinDotNorm = 1.e-3f; + if( dotNorm >= -kMinDotNorm ) + { + if( !twoSided ) + continue; + if( dotNorm <= kMinDotNorm ) + continue; + } + hsScalar dist = hsVector3(&tri.Position(0), &currFrom).InnerProduct(norm); + if( dist > 0 ) + continue; + dist /= dotNorm; + hsPoint3 projPt = currFrom; + projPt += currDir * dist; + + // If the distance from source point to projected point is too long, skip + if( dist > maxDist ) + continue; + + // Find the 3 cross products (v[i+1]-v[i]) X (proj - v[i]) dotted with current ray + hsVector3 cross0 = hsVector3(&tri.Position(1), &tri.Position(0)) % hsVector3(&projPt, &tri.Position(0)); + hsScalar dot0 = cross0.InnerProduct(currDir); + + hsVector3 cross1 = hsVector3(&tri.Position(2), &tri.Position(1)) % hsVector3(&projPt, &tri.Position(1)); + hsScalar dot1 = cross1.InnerProduct(currDir); + + hsVector3 cross2 = hsVector3(&tri.Position(0), &tri.Position(2)) % hsVector3(&projPt, &tri.Position(2)); + hsScalar dot2 = cross2.InnerProduct(currDir); + + // If all 3 are negative, projPt is a hit + // If all 3 are positive and we're two sided, projPt is a hit + // We've already checked for back facing (when we checked for edge on in projection), + // so we'll accept either case here. + if( ((dot0 <= 0) && (dot1 <= 0) && (dot2 <= 0)) + ||((dot0 >= 0) && (dot1 >= 0) && (dot2 >= 0)) ) + { + if( dist < maxDist ) + { + maxDist = dist; + hit.fPos = projPt; + retVal = true; + } + } + } + plAccessGeometry::Instance()->Close(src); + + if( retVal ) + { + hit.fPos = src.GetLocalToWorld() * hit.fPos; + fCurrTarg = hit.fPos; + fMaxDist = hsVector3(&fCurrTarg, &fCurrFrom).Magnitude(); + } + + return retVal; +} + +hsBool plVisLOSMgr::ICheckBound(const hsBounds3Ext& bnd, hsScalar& closest) +{ + if( bnd.GetType() != kBoundsNormal ) + return false; + + if( bnd.IsInside(&fCurrFrom) || bnd.IsInside(&fCurrTarg) ) + { + closest = 0; + return true; + } + + const int face[6][4] = + { + {0,1,3,2}, + {1,5,7,3}, + {2,3,7,6}, + {5,4,6,7}, + {0,4,5,1}, + {0,2,6,4} + }; + + hsPoint3 corn[8]; + bnd.GetCorners(corn); + + hsBool retVal = false; + + const hsPoint3& currFrom = fCurrFrom; + const hsPoint3& currTarg = fCurrTarg; + + hsVector3 currDir(&currTarg, &currFrom); + const hsScalar maxDistSq = currDir.MagnitudeSquared(); + + currDir *= hsFastMath::InvSqrt(maxDistSq); + + int i; + for( i = 0; i < 6; i++ ) + { + const hsPoint3& p0 = corn[face[i][0]]; + const hsPoint3& p1 = corn[face[i][1]]; + const hsPoint3& p2 = corn[face[i][2]]; + const hsPoint3& p3 = corn[face[i][3]]; + + // Project the current ray onto the tri plane + hsVector3 norm = hsVector3(&p1, &p0) % hsVector3(&p2, &p0); + hsScalar dotNorm = norm.InnerProduct(currDir); + + const hsScalar kMinDotNorm = 1.e-3f; + if( dotNorm >= -kMinDotNorm ) + { + continue; + } + hsScalar dist = hsVector3(&p0, &currFrom).InnerProduct(norm); + if( dist >= 0 ) + continue; + dist /= dotNorm; + + // If the distance from source point to projected point is too long, skip + if( dist > fMaxDist ) + continue; + + hsPoint3 projPt = currFrom; + projPt += currDir * dist; + + // Find the 3 cross products (v[i+1]-v[i]) X (proj - v[i]) dotted with current ray + hsVector3 cross0 = hsVector3(&p1, &p0) % hsVector3(&projPt, &p0); + hsScalar dot0 = cross0.InnerProduct(currDir); + + hsVector3 cross1 = hsVector3(&p2, &p1) % hsVector3(&projPt, &p1); + hsScalar dot1 = cross1.InnerProduct(currDir); + + hsVector3 cross2 = hsVector3(&p3, &p2) % hsVector3(&projPt, &p2); + hsScalar dot2 = cross2.InnerProduct(currDir); + + hsVector3 cross3 = hsVector3(&p0, &p3) % hsVector3(&projPt, &p3); + hsScalar dot3 = cross3.InnerProduct(currDir); + + // If all 4 are negative, projPt is a hit + if( (dot0 <= 0) && (dot1 <= 0) && (dot2 <= 0) && (dot3 <= 0) ) + { + closest = dist; + return true; + } + } + return false; +} + +hsBool plVisLOSMgr::CursorCheck(plVisHit& hit) +{ + Int32 sx= Int32(plMouseDevice::Instance()->GetCursorX() * fPipe->Width()); + Int32 sy= Int32(plMouseDevice::Instance()->GetCursorY() * fPipe->Height()); + + hsPoint3 from = fPipe->GetViewPositionWorld(); + plConst(hsScalar) dist(1.e5f); + + hsPoint3 targ; + fPipe->ScreenToWorldPoint(1, 0, &sx, &sy, dist, 0, &targ); + return Check(from, targ, hit); +} + +///////////////////////////////////////////////////////////////// + +#include "plPipeline.h" +#include "../pnSceneObject/plSceneObject.h" + +static plSceneObject* marker = nil; +static plPipeline* pipe = nil; + + +void VisLOSHackBegin(plPipeline* p, plSceneObject* m) +{ + marker = m; + pipe = p; +} + +void VisLOSHackPulse() +{ + if( !pipe ) + return; + + plVisHit hit; + if( plVisLOSMgr::Instance()->CursorCheck(hit) ) + { + if( marker ) + { + hsMatrix44 l2w = marker->GetLocalToWorld(); + l2w.fMap[0][3] = hit.fPos.fX; + l2w.fMap[1][3] = hit.fPos.fY; + l2w.fMap[2][3] = hit.fPos.fZ; + l2w.NotIdentity(); + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + marker->SetTransform(l2w, w2l); + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plVisLOSMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plVisLOSMgr.h new file mode 100644 index 00000000..d7acd16a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plVisLOSMgr.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plVisLOSMgr_inc +#define plVisLOSMgr_inc + +#include "hsGeometry3.h" +#include "hsTemplates.h" + +class plSpaceTreeNode; +class plSpaceTree; +class plDrawableSpans; +class plDrawable; +class plSceneNode; +class plPageTreeMgr; +class plPipeline; +class hsBounds3Ext; + +class plVisHit +{ +public: + hsPoint3 fPos; +}; + +class plSpaceHit +{ +public: + int fIdx; + hsScalar fClosest; +}; + +class plVisLOSMgr +{ +protected: + plPageTreeMgr* fPageMgr; + plPipeline* fPipe; + + hsScalar fMaxDist; + + hsPoint3 fCurrFrom; + hsPoint3 fCurrTarg; + + hsBool ISetup(const hsPoint3& pStart, const hsPoint3& pEnd); + hsBool ICheckBound(const hsBounds3Ext& bnd, hsScalar& closest); + hsBool ICheckSpaceTreeRecur(plSpaceTree* space, int which, hsTArray& hits); + hsBool ICheckSpaceTree(plSpaceTree* space, hsTArray& hits); + hsBool ICheckSceneNode(plSceneNode* node, plVisHit& hit); + hsBool ICheckDrawable(plDrawable* d, plVisHit& hit); + hsBool ICheckSpan(plDrawableSpans* dr, UInt32 spanIdx, plVisHit& hit); + +public: + hsBool Check(const hsPoint3& pStart, const hsPoint3& pEnd, plVisHit& hit); + hsBool CursorCheck(plVisHit& hit); + + static plVisLOSMgr* Instance(); + + static void Init(plPipeline* pipe, plPageTreeMgr* mgr) { Instance()->fPipe = pipe; Instance()->fPageMgr = mgr; } + static void DeInit() { Instance()->fPipe = nil; Instance()->fPageMgr = nil; } +}; + +#endif // plVisLOSMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSet7.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSet7.cpp new file mode 100644 index 00000000..413917e8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSet7.cpp @@ -0,0 +1,4453 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsFastMath.h" +#include "hsTimer.h" + +#include "plWaveSet7.h" +#include "plWaveSetShaderConsts.h" +#include "plRipVSConsts.h" + +#include "plAccessGeometry.h" +#include "plAccessSpan.h" +#include "plAccessVtxSpan.h" + +#include "plAuxSpan.h" +#include "plDynaDecal.h" +#include "plDynaRippleVSMgr.h" + +#include "../plMessage/plRenderMsg.h" +#include "../pnMessage/plTimeMsg.h" + +#include "../pnMessage/plObjRefMsg.h" + +#include "plgDispatch.h" +#include "plPipeline.h" + +#include "hsResMgr.h" + +#include "../pnSceneObject/plDrawInterface.h" + +#include "plPhysical.h" +#include "../plMessage/plSimInfluenceMsg.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plDrawableGenerator.h" + +#include "../plMessage/plAvatarMsg.h" +#include "../plAvatar/plArmatureMod.h" + +#include "../plGImage/plMipmap.h" +#include "../plGImage/plCubicEnvironmap.h" +#include "../plSurface/plLayer.h" +#include "../plMessage/plLayRefMsg.h" + +#include "../plSurface/plShader.h" + +#include "../plPipeline/plRenderTarget.h" +#include "../plScene/plRenderRequest.h" +#include "../plMessage/plRenderRequestMsg.h" +#include "../plScene/plPageTreeMgr.h" + +#include "../plPipeline/plDynamicEnvMap.h" + +#include "../plGImage/plBumpMapGen.h" + +#include "../plMessage/plMatRefMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" + +#include "plTweak.h" + +#ifndef PLASMA_EXTERNAL_RELEASE +#include "../plStatusLog/plStatusLog.h" +#include "../plPipeline/plPlates.h" +#endif // PLASMA_EXTERNAL_RELEASE + +using namespace plShaderID; + +/////////////////////////////////////////////////////////////////////// +#include "plProfile.h" +/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + +#define TEST_ENVSPH + +// #define TEST_UVWS + +static const hsScalar kPiOverTwo = hsScalarPI * 0.5f; + +static const hsScalar kGravConst = 30.f; + +static const hsScalar kOOEightNsqPI = 1.f / (8.f * hsScalarPI * 4.f * 4.f); +static hsScalar currOOEightNsqPI = kOOEightNsqPI; + +static inline hsScalar FreqToLen(hsScalar f) { return 2.f * hsScalarPI / f; } +static inline hsScalar LenToFreq(hsScalar l) { return 2.f * hsScalarPI / l; } + +static inline hsScalar MPH2FPS(hsScalar f) { return f * 5280.f / 3600.f; } +static inline hsScalar FPS2MPH(hsScalar f) { return f / 5280.f * 3600.f; } + +plCONST(hsScalar) kTimeClamp(0.3f); + +inline void plWorldWave7::Accumulate(hsPoint3& accumPos, hsVector3& accumNorm) const +{ + hsScalar dist = accumPos.fX * fDir.fX + accumPos.fY * fDir.fY; + + dist *= fFreq; + dist += fPhase; + + hsScalar s, c; + hsFastMath::SinCosAppr(dist, s, c); +// s += 1.f; + + // Add scaled local Z instead? So decals can move up and down non-vertically? + // Thing is, for the height is still based on the unperterbed position, so when + // we move it laterally, the height is wrong. Maybe not too wrong, but still wrong. + // Same for normal. + accumPos.fZ += s * fAmplitude; + + c *= -fFreq * fAmplitude; + accumNorm.fX += fDir.fX * c; + accumNorm.fY += fDir.fY * c; +} + +#ifndef PLASMA_EXTERNAL_RELEASE +inline void plWaveSet7::GraphLen(hsScalar len) const +{ + if( fStatusGraph ) + { + hsScalar maxLen = TexState().fMaxLength * kCompositeSize / State().fRippleScale; + Int32 val = Int32(len / maxLen * 100.f); + fStatusGraph->AddData(val); + } +} + +inline void plWaveSet7::IRestartGraph() const +{ + if( fStatusGraph ) + fStatusGraph->ClearData(); +} + +void plWaveSet7::StartGraph() +{ + delete fStatusGraph; + plPlateManager::Instance().CreateGraphPlate(&fStatusGraph); + fStatusGraph->SetSize(0.25f, 0.25f); + fStatusGraph->SetDataRange(0, 100, kNumTexWaves); + fStatusGraph->SetVisible(true); + fStatusGraph->SetPosition(0.75f, -0.75f); +} + +void plWaveSet7::StopGraph() +{ + delete fStatusGraph; + fStatusGraph = nil; +} + +inline void plWaveSet7::LogF(const char *format, ...) const +{ + if( fStatusLog ) + { + va_list args; + va_start(args,format); + fStatusLog->AddLineV(format, args); + va_end(args); + } +} + +inline void plWaveSet7::LogF(UInt32 color, const char *format, ...) const +{ + if( fStatusLog ) + { + va_list args; + va_start(args,format); + fStatusLog->AddLineV(color, format, args); + va_end(args); + } +} + +inline void plWaveSet7::IRestartLog() const +{ + if( fStatusLog ) + fStatusLog->Clear(); +} + +void plWaveSet7::StartLog() +{ + delete fStatusLog; + fStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog(kNumTexWaves, "TexWaves", + plStatusLog::kDontWriteFile | plStatusLog::kDeleteForMe | plStatusLog::kFilledBackground); +} + +void plWaveSet7::StopLog() +{ + delete fStatusLog; + fStatusLog = nil; +} +#else // PLASMA_EXTERNAL_RELEASE +inline void plWaveSet7::GraphLen(hsScalar len) const +{ +} + +inline void plWaveSet7::IRestartGraph() const +{ +} + +void plWaveSet7::StartGraph() +{ +} + +void plWaveSet7::StopGraph() +{ +} + +inline void plWaveSet7::LogF(const char *format, ...) const +{ +} + +inline void plWaveSet7::LogF(UInt32 color, const char *format, ...) const +{ +} + +inline void plWaveSet7::IRestartLog() const +{ +} + +void plWaveSet7::StartLog() +{ +} + +void plWaveSet7::StopLog() +{ +} +#endif // PLASMA_EXTERNAL_RELEASE + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Enough of that, WaveSet (system manager) follows +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +plWaveSet7::plWaveSet7() +: fLastTime(0), + fCurrTime(0), + fScrunchLen(8.f), + fScrunchScale(2.f), + fFreqScale(1.f), + fRipVShader(nil), + fRipPShader(nil), + fShoreVShader(nil), + fShorePShader(nil), + fFixedVShader(nil), + fFixedPShader(nil), + fBiasVShader(nil), + fBiasPShader(nil), + fBumpMat(nil), + fBumpDraw(nil), + fBumpReq(nil), + fBumpReqMsg(nil), + fEnvMap(nil), + fRefObj(nil), + fCosineLUT(nil), + fGraphShoreTex(nil), + fBubbleShoreTex(nil), + fEdgeShoreTex(nil), + fMaxLen(0), + + fTrialUpdate(0), + fTransistor(-1), + fTransCountDown(30.f), + fTransDel(0), + + fTexTrans(0), + fTexTransCountDown(0), + fTexTransDel(0), + + fStatusLog(nil), + fStatusGraph(nil) +{ + IInitState(); + IInitWaveConsts(); + + int i; + for( i = 0; i < kNumWaves; i++ ) + { + fFreqMod[i] = 1.f; + IInitWave(i); + } + for( i = 0; i < 4; i++ ) + { + fFixedLayers[i] = nil; + } + for( i = 0; i < kNumBumpShaders; i++ ) + { + fBumpVShader[i] = nil; + fBumpPShader[i] = nil; + } + fBiasLayer[0] = nil; + fBiasLayer[1] = nil; + for( i = 0; i < kNumTexWaves; i++ ) + { + fBumpLayers[i] = nil; + fTexWaveFade[i] = 1.f; + } + for( i = 0; i < kGraphShorePasses; i++ ) + { + fGraphVShader[i] = nil; + fGraphPShader[i] = nil; + + fGraphShoreMat[i] = nil; + fGraphShoreRT[i] = nil; + fGraphShoreDraw[i] = nil; + fGraphReq[i] = nil; + fGraphReqMsg[i] = nil; + } + for( i = 0; i < kNumDecalVShaders; i++ ) + fDecalVShaders[i] = nil; + for( i = 0; i < kNumDecalPShaders; i++ ) + fDecalPShaders[i] = nil; +} + +plWaveSet7::~plWaveSet7() +{ + delete fStatusLog; + + delete fBumpReqMsg; + delete fBumpReq; + + int i; + for( i = 0; i < kGraphShorePasses; i++ ) + { + delete fGraphReqMsg[i]; + delete fGraphReq[i]; + } +} + +void plWaveSet7::Read(hsStream* stream, hsResMgr* mgr) +{ + plMultiModifier::Read(stream, mgr); + + fMaxLen = stream->ReadSwapScalar(); + + fState.Read(stream); + IUpdateWindDir(0); + + int n = stream->ReadSwap32(); + int i; + for( i = 0; i < n; i++ ) + { + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefShore), plRefFlags::kPassiveRef); + } + n = stream->ReadSwap32(); + for( i = 0; i < n; i++ ) + { + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefDecal), plRefFlags::kPassiveRef); + } + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefEnvMap), plRefFlags::kActiveRef); + + if( HasFlag(kHasRefObject) ) + { + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefRefObj), plRefFlags::kPassiveRef); + } + + ISetupTextureWaves(); + + for( i = 0; i < kNumWaves; i++ ) + { + fFreqMod[i] = 1.f; + IInitWave(i); + } + + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); +} + +void plWaveSet7::Write(hsStream* stream, hsResMgr* mgr) +{ + plMultiModifier::Write(stream, mgr); + + stream->WriteSwapScalar(fMaxLen); + + fState.Write(stream); + + stream->WriteSwap32(fShores.GetCount()); + int i; + for( i = 0; i < fShores.GetCount(); i++ ) + { + mgr->WriteKey(stream, fShores[i]); + } + stream->WriteSwap32(fDecals.GetCount()); + for( i = 0; i < fDecals.GetCount(); i++ ) + { + mgr->WriteKey(stream, fDecals[i]); + } + mgr->WriteKey(stream, fEnvMap); + + if( HasFlag(kHasRefObject) ) + { + mgr->WriteKey(stream, fRefObj); + } +} + +hsBool plWaveSet7::MsgReceive(plMessage* msg) +{ + plEvalMsg* update = plEvalMsg::ConvertNoRef(msg); + if( update ) + { + if (fFixedVShader == nil) + { + ICheckTargetMaterials(); + ICheckShoreMaterials(); + ICheckDecalMaterials(); + } + IRestartLog(); + IRestartGraph(); + + plCONST(hsBool) reRender(false); + if( reRender || (fTrialUpdate & kReRenderEnvMap) ) + { + plDynamicEnvMap* envMap = plDynamicEnvMap::ConvertNoRef(fEnvMap); + if( envMap ) + { + envMap->ReRender(); + + fTrialUpdate &= ~kReRenderEnvMap; + } + } + + hsScalar dt = update->DelSeconds(); + if( dt > kTimeClamp ) + dt = kTimeClamp; + + IUpdateWindDir(dt); + + IFloatBuoys(dt); + return true; + } + + plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg); + if( rend ) + { + if( !IAnyBoundsVisible(rend->Pipeline()) ) + return true; + + fCurrTime = hsTimer::GetSysSeconds(); + // Can't just use GetDelSysSeconds() or else we lose time if we skip a frame render because of high FPS. + hsScalar dt = fLastTime > 0 ? hsScalar(fCurrTime - fLastTime) : hsTimer::GetDelSysSeconds(); + if( dt > kTimeClamp ) + dt = kTimeClamp; + + IUpdateRefObject(); + + IUpdateLayers(dt); + + IUpdateWaves(dt); + + hsMatrix44 l2w; + hsMatrix44 w2l; + IUpdateShaders(rend->Pipeline(), l2w, w2l); + IUpdateGraphShaders(rend->Pipeline(), dt); + + fLastTime = fCurrTime; + return true; + } + + plAgeLoadedMsg* ageLoaded = plAgeLoadedMsg::ConvertNoRef(msg); + if( (ageLoaded && ageLoaded->fLoaded) || plInitialAgeStateLoadedMsg::ConvertNoRef(msg) ) + { + ICheckTargetMaterials(); + ICheckShoreMaterials(); + ICheckDecalMaterials(); + return true; + } + + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace) ) + { + IOnReceive(refMsg); + } + else + { + IOnRemove(refMsg); + } + return true; + } + + return plMultiModifier::MsgReceive(msg); +} + +hsBool plWaveSet7::IAnyBoundsVisible(plPipeline* pipe) const +{ + int i; + for( i = 0; i < fTargBnds.GetCount(); i++ ) + { + if( pipe->TestVisibleWorld(fTargBnds[i]) ) + return true; + } + return false; +} + +hsBool plWaveSet7::IOnReceive(plGenRefMsg* refMsg) +{ + switch( refMsg->fType ) + { + case kRefRefObj: + fRefObj = plSceneObject::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefCosineLUT: + fCosineLUT = plMipmap::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefEnvMap: + fEnvMap = plBitmap::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefShore: + fShores.Append(plSceneObject::ConvertNoRef(refMsg->GetRef())); + return true; + case kRefDecal: + fDecals.Append(plSceneObject::ConvertNoRef(refMsg->GetRef())); + return true; + case kRefDecVShader: + fDecalVShaders[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefDecPShader: + fDecalPShaders[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefGraphShoreRT: + fGraphShoreRT[refMsg->fWhich] = plRenderTarget::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefGraphVShader: + fGraphVShader[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefGraphPShader: + fGraphPShader[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefGraphShoreTex: + fGraphShoreTex = plMipmap::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefBubbleShoreTex: + fBubbleShoreTex = plMipmap::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefEdgeShoreTex: + fEdgeShoreTex = plMipmap::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefGraphShoreMat: + fGraphShoreMat[refMsg->fWhich] = hsGMaterial::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefGraphShoreDraw: + fGraphShoreDraw[refMsg->fWhich] = plDrawableSpans::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefBiasVShader: + fBiasVShader = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefBiasPShader: + fBiasPShader = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefBumpVShader: + fBumpVShader[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefBumpPShader: + fBumpPShader[refMsg->fWhich] = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefRipVShader: + fRipVShader = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefRipPShader: + fRipPShader = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefShoreVShader: + fShoreVShader = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefShorePShader: + fShorePShader = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefFixedVShader: + fFixedVShader = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefFixedPShader: + fFixedPShader = plShader::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefBumpDraw: + fBumpDraw = plDrawableSpans::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefBumpMat: + fBumpMat = hsGMaterial::ConvertNoRef(refMsg->GetRef()); + return true; + case kRefDynaDecalMgr: + { + plDynaDecalMgr* dyna = plDynaDecalMgr::ConvertNoRef(refMsg->GetRef()); + if( fDecalMgrs.Find(dyna) == fDecalMgrs.kMissingIndex ) + { + fDecalMgrs.Append(dyna); + } + } + return true; + case kRefBuoy: + { + plSceneObject* so = plSceneObject::ConvertNoRef(refMsg->GetRef()); + if( fBuoys.Find(so) == fBuoys.kMissingIndex ) + { + IShiftCenter(so); + fBuoys.Append(so); + } + } + return true; + } + return false; +} + +hsBool plWaveSet7::IOnRemove(plGenRefMsg* refMsg) +{ + switch( refMsg->fType ) + { + case kRefRefObj: + fRefObj = nil; + return true; + case kRefCosineLUT: + fCosineLUT = nil; + return true; + case kRefEnvMap: + fEnvMap = nil; + return true; + case kRefShore: + { + plSceneObject* shore = (plSceneObject*)refMsg->GetRef(); + int idx = fShores.Find(shore); + if( idx != fShores.kMissingIndex ) + fShores.Remove(idx); + } + return true; + case kRefDecal: + { + plSceneObject* decal = (plSceneObject*)refMsg->GetRef(); + int idx = fDecals.Find(decal); + if( idx != fDecals.kMissingIndex ) + fDecals.Remove(idx); + } + return true; + case kRefDecVShader: + fDecalVShaders[refMsg->fWhich] = nil; + return true; + case kRefDecPShader: + fDecalPShaders[refMsg->fWhich] = nil; + return true; + case kRefGraphShoreRT: + fGraphShoreRT[refMsg->fWhich] = nil; + return true; + case kRefGraphVShader: + fGraphVShader[refMsg->fWhich] = nil; + return true; + case kRefGraphPShader: + fGraphPShader[refMsg->fWhich] = nil; + return true; + case kRefGraphShoreTex: + fGraphShoreTex = nil; + return true; + case kRefBubbleShoreTex: + fBubbleShoreTex = nil; + return true; + case kRefEdgeShoreTex: + fEdgeShoreTex = nil; + return true; + case kRefGraphShoreMat: + fGraphShoreMat[refMsg->fWhich] = nil; + return true; + case kRefGraphShoreDraw: + fGraphShoreDraw[refMsg->fWhich] = nil; + return true; + case kRefBiasVShader: + fBiasVShader = nil; + return true; + case kRefBiasPShader: + fBiasPShader = nil; + return true; + case kRefBumpVShader: + fBumpVShader[refMsg->fWhich] = nil; + return true; + case kRefBumpPShader: + fBumpPShader[refMsg->fWhich] = nil; + return true; + case kRefRipVShader: + fRipVShader = nil; + return true; + case kRefRipPShader: + fRipPShader = nil; + return true; + case kRefShoreVShader: + fShoreVShader = nil; + return true; + case kRefShorePShader: + fShorePShader = nil; + return true; + case kRefFixedVShader: + fFixedVShader = nil; + return true; + case kRefFixedPShader: + fFixedPShader = nil; + return true; + case kRefBumpDraw: + fBumpDraw = nil; + return true; + case kRefBumpMat: + fBumpMat = nil; + return true; + case kRefDynaDecalMgr: + { + plDynaDecalMgr* dyna = (plDynaDecalMgr*)refMsg->GetRef(); + int idx = fDecalMgrs.Find(dyna); + if( fDecalMgrs.kMissingIndex != idx ) + fDecalMgrs.Remove(idx); + } + return true; + case kRefBuoy: + { + plSceneObject* so = (plSceneObject*)refMsg->GetRef(); + int idx = fBuoys.Find(so); + if( fBuoys.kMissingIndex != idx ) + fBuoys.Remove(idx); + } + return true; + } + return false; +} + +void plWaveSet7::SetState(const plFixedWaterState7& state, hsScalar dur) +{ + fState.Set(state, dur); + + if( fFixedLayers[0] ) + { + plLayer* lay = plLayer::ConvertNoRef(fFixedLayers[0]->BottomOfStack()); + lay->SetRuntimeColor(state.fWaterTint); + } +} + +void plWaveSet7::IUpdateWaves(hsScalar dt) +{ + ITransition(dt); + ITransTex(dt); + ICalcScale(); + fScrunchLen = 1.e33f; + + if( fTrialUpdate & kReInitWaves ) + { + IReInitWaves(); + ISetupTextureWaves(); + ICreateBumpMipmapPS(); + } + + int i; + for( i = 0; i < kNumWaves; i++ ) + { + IUpdateWave(dt, i); + } +} + +// return true if we've finished this transition. +hsBool plWaveSet7::ITransContinue(hsScalar dt) +{ + hsScalar currFade = (fFreqMod[fTransistor] += fTransDel * dt); + + if( currFade <= 0 ) + { + // Done fading down, time to fade back up. + fTransDel = -fTransDel; + fFreqMod[fTransistor] = fTransDel * dt; + + // Reinit the wave. This gives a chance to introduce more randomization, + // as well as react to changing weather conditions. + IInitWave(fTransistor); + + return false; + } + + if( currFade >= 1.f ) + { + fFreqMod[fTransistor] = 1.f; + fTransDel = 0; + + return true; + } + + return false; +} + +void plWaveSet7::IStartTransition(hsScalar dt) +{ + // select the next wave for transitioning + if( ++fTransistor >= kNumWaves ) + fTransistor = 0; + + // set the transFade to be fading down. + plCONST(hsScalar) kTransDel(0.5f); + fTransDel = -kTransDel; +} + +hsScalar plWaveSet7::ITransitionDelay() const +{ + plCONST(hsScalar) kTransDelay(2.f); + return kTransDelay; +} + +void plWaveSet7::ITransition(hsScalar dt) +{ + // If we're in a transition, keep transitioning till it's done. + if( fTransDel != 0 ) + { + if( ITransContinue(dt) ) + fTransCountDown = ITransitionDelay(); + } + // else if our transition countdown has gone to zero, start a new one. + else if( (fTransCountDown -= dt) <= 0 ) + { + IStartTransition(dt); + } + +} + +hsBool plWaveSet7::ITransTexContinue(hsScalar dt) +{ + hsScalar currFade = (fTexWaveFade[fTexTrans] += fTexTransDel * dt); + + if( currFade <= 0 ) + { + // Done fading down, time to fade back up. + fTexTransDel = -fTexTransDel; + fTexWaveFade[fTexTrans] = fTexTransDel * dt; + + // ReInit the wave + IInitTexWave(fTexTrans); + + return false; + } + + if( currFade >= 1.f ) + { + // This one is back to full on, time to pick on another one. + fTexWaveFade[fTexTrans] = 1.f; + fTexTransDel = 0; + + return true; + } + + return false; +} + +void plWaveSet7::IStartTexTransition(hsScalar dt) +{ + if( ++fTexTrans >= kNumTexWaves ) + fTexTrans = 0; + + plConst(hsScalar) kTexTransDel(4.f); + fTexTransDel = -kTexTransDel; +} + +void plWaveSet7::ITransTex(hsScalar dt) +{ + + // If we're in a transition, keep with it. + if( fTexTransDel != 0 ) + { + plConst(hsScalar) kTexTransDelay(0); + if( ITransTexContinue(dt) ) + fTexTransCountDown = kTexTransDelay; + } + // else if it's time to start another... + else if( (fTexTransCountDown -= dt) <= 0 ) + { + IStartTexTransition(dt); + } +} + +void plWaveSet7::ICalcScale() +{ +} + +void plWaveSet7::IUpdateWave(hsScalar dt, int i) +{ + plWorldWave7& wave = fWorldWaves[i]; + + hsScalar len = FreqToLen(wave.fFreq); + + hsScalar speed = hsFastMath::InvSqrtAppr(len / (2.f * hsScalarPI * kGravConst)); + + static hsScalar speedHack = 1.f; + speed *= speedHack; + wave.fPhase += speed * dt; +// wave.fPhase = fmod( speed * t, 2.f * hsScalarPI); + + hsScalar amp = GeoState().fAmpOverLen * len / hsScalar(kNumWaves); + + amp *= fFreqMod[i] * fFreqScale; + + wave.fAmplitude = amp; + +} + +void plWaveSet7::IReInitWaves() +{ + int i; + for( i = 0; i < kNumWaves; i++ ) + { + IInitWave(i); + } + fTransDel = 0; + fTransCountDown = ITransitionDelay(); + fTrialUpdate &= ~kReInitWaves; +} + + +void plWaveSet7::IInitWave(int i) +{ + plWorldWave7& wave = fWorldWaves[i]; + + wave.fLength = GeoState().fMinLength + fRand.RandZeroToOne() * (GeoState().fMaxLength - GeoState().fMinLength); + + hsScalar len = wave.fLength; + + wave.fFreq = LenToFreq(len); + + wave.fPhase = 0; + + wave.fAmplitude = 0; + + // Figure out the direction based on wind direction. + // Even waves go in the wind direction, + // odd waves go opposite direction + plConst(hsScalar) kMinRotDeg(15.f); + plConst(hsScalar) kMaxRotDeg(180.f); + hsVector3 dir = fWindDir; + + hsScalar rotBase = GeoState().fAngleDev; + + hsScalar rads = rotBase * fRand.RandMinusOneToOne(); + hsScalar rx = hsScalar(cosf(rads)); + hsScalar ry = hsScalar(sinf(rads)); + + hsScalar x = dir.fX; + hsScalar y = dir.fY; + dir.fX = x * rx + y * ry; + dir.fY = x * -ry + y * rx; + + wave.fDir.Set(dir.fX, dir.fY, 0); +} + +inline void plWaveSet7::IScrunch(hsPoint3& pos, hsVector3& norm) const +{ + pos.fX += -norm.fX * fScrunchLen; + pos.fY += -norm.fY * fScrunchLen; + + norm.fX *= fScrunchScale; + norm.fY *= fScrunchScale; + + hsFastMath::NormalizeAppr(norm); +} + +hsScalar plWaveSet7::EvalPoint(hsPoint3& pos, hsVector3& norm) +{ + hsPoint3 accumPos; + hsVector3 accumNorm; + accumPos.Set(pos.fX, pos.fY, State().fWaterHeight); + accumNorm.Set(0,0,0); + + int i; + for( i = 0; i < kNumWaves; i++ ) + fWorldWaves[i].Accumulate(accumPos, accumNorm); + + accumNorm.fZ = 1.f; + + hsFastMath::NormalizeAppr(accumNorm); + + IScrunch(accumPos, accumNorm); + + // Project original pos along Z onto the plane tangent at accumPos with norm accumNorm + hsScalar t = hsVector3(&accumPos, &pos).InnerProduct(accumNorm); + t /= accumNorm.fZ; + + pos.fZ += t; + + norm = accumNorm; + + return pos.fZ; +} + +void plWaveSet7::IUpdateWindDir(hsScalar dt) +{ + fWindDir = -State().fWindDir; + hsFastMath::NormalizeAppr(fWindDir); +} + +void plWaveSet7::IUpdateRefObject() +{ + if( fRefObj ) + { + hsMatrix44 l2w = fRefObj->GetLocalToWorld(); + + hsScalar h = l2w.fMap[2][3]; + + hsScalar x = -l2w.fMap[0][1]; + hsScalar y = -l2w.fMap[1][1]; + + fState.fWaterHeight = h; + + fState.fWindDir = hsVector3(x, y, 0.f); + } +} + +void plWaveSet7::IFloatBuoy(hsScalar dt, plSceneObject* so) +{ + // Compute force based on world bounds + hsBounds3Ext wBnd = so->GetDrawInterface()->GetWorldBounds(); + hsBounds3Ext lBnd = so->GetDrawInterface()->GetLocalBounds(); + + hsPoint3 pos(wBnd.GetCenter()); + hsPoint3 surfPos(pos); + hsVector3 surfNorm; + + EvalPoint(surfPos, surfNorm); + + // Direction of impulse is surfNorm. Magnitude is proportional to depth + // (in an approximation lazy hackish way). + hsPoint2 boxDepth; + wBnd.TestPlane(surfNorm, boxDepth); + hsScalar surfDepth = surfNorm.InnerProduct(surfPos); + hsScalar depth = surfDepth - boxDepth.fX; + + if( depth < 0 ) + return; + + if( depth > wBnd.GetMaxs().fZ - wBnd.GetMins().fZ ) + depth = wBnd.GetMaxs().fZ - wBnd.GetMins().fZ; + + // We really want the cross section area as facing into the water, + // but life is full of little disappointments. + hsScalar area = (wBnd.GetMaxs().fX - wBnd.GetMins().fX) * (wBnd.GetMaxs().fY - wBnd.GetMins().fY); + + hsScalar volume = area * depth; + + plCONST(hsScalar) kWaterDensity(1.0f); + hsScalar forceMag = volume * kWaterDensity; + + // surfNorm is now the impulse vector. But where to apply it. + // Don't currently have anything informative from the physical to use. + // So, let's fake something for the moment. + + plKey physKey = so->GetSimulationInterface()->GetPhysical()->GetKey(); + +// plImpulseMsg* iMsg = TRACKED_NEW plImpulseMsg(GetKey(), physKey, hsVector3(0, 0, 1.f) * forceMag * dt); +// iMsg->Send(); + +#if 0 + plCONST(hsScalar) kRotScale(1.f); + hsVector3 rotAx = hsVector3(0, 0, 1.f) % surfNorm; + rotAx *= kRotScale * dt * volume; + + plAngularImpulseMsg* aMsg = TRACKED_NEW plAngularImpulseMsg(GetKey(), physKey, rotAx); + aMsg->Send(); +#endif + + plCONST(hsScalar) kDampener(0.1f); + plCONST(hsScalar) kBaseDamp(0.1f); + if( wBnd.GetMaxs().fZ > wBnd.GetMins().fZ ) + { + // Remember, we've already limited depth to be <= Max.fZ - Min.fZ; + hsScalar damp = depth / (wBnd.GetMaxs().fZ - wBnd.GetMins().fZ); + damp *= kDampener; + damp += kBaseDamp; + +// plDampMsg* dMsg = TRACKED_NEW plDampMsg(GetKey(), physKey, damp); +// dMsg->Send(); + } +} + +void plWaveSet7::IFloatBuoys(hsScalar dt) +{ + int i; + for( i = 0; i < fBuoys.GetCount(); i++ ) + { + if( fBuoys[i] && fBuoys[i]->GetSimulationInterface() && fBuoys[i]->GetSimulationInterface()->GetPhysical() && fBuoys[i]->GetDrawInterface() ) + { + IFloatBuoy(dt, fBuoys[i]); + } + } +} + +void plWaveSet7::IShiftCenter(plSceneObject* so) const +{ + // HACKAGE + if( 0 && so->GetSimulationInterface() && so->GetSimulationInterface()->GetPhysical() && so->GetDrawInterface() ) + { + hsPoint3 center = so->GetDrawInterface()->GetWorldBounds().GetCenter(); + hsPoint3 pos = so->GetLocalToWorld().GetTranslate(); + hsVector3 offset(&pos, ¢er); +// plShiftMassMsg* msg = TRACKED_NEW plShiftMassMsg(GetKey(), so->GetSimulationInterface()->GetPhysical()->GetKey(), offset); +// msg->Send(); + } +} + +void plWaveSet7::ICheckTargetMaterials() +{ + hsBounds3Ext targBnd; + targBnd.MakeEmpty(); + int i; + for( i = 0; i < GetNumTargets(); i++ ) + { + plSceneObject* so = GetTarget(i); + if( !so ) + continue; + + const plDrawInterface* di = so->GetDrawInterface(); + if( !di ) + continue; + + hsTArray src; + plAccessGeometry::Instance()->OpenRO(di, src, false); + + const int numUVWs = src.GetCount() && src[0].AccessVtx().HasUVWs() ? src[0].AccessVtx().NumUVWs() : 0; + int j; + for( j = 0; j < src.GetCount(); j++ ) + { + hsAssert(src[j].AccessVtx().NumUVWs() == numUVWs, "Must have same number uvws on each water mesh"); + ICreateFixedMat(src[j].GetMaterial(), numUVWs); // no-op if it's already setup. + + targBnd.Union(&src[j].GetWorldBounds()); + } + + plAccessGeometry::Instance()->Close(src); + + for( j = 0; j < di->GetNumDrawables(); j++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(j)); + if( dr ) + { + dr->SetNativeProperty(di->GetDrawableMeshIndex(j), plSpan::kPropRunTimeLight | plSpan::kPropSkipProjection | plSpan::kPropNoShadow | plSpan::kLiteVtxNonPreshaded | plSpan::kPropReverseSort, true); + } + } + } + if( targBnd.GetType() == kBoundsNormal ) + { + if( !fTargBnds.GetCount() ) + fTargBnds.SetCount(1); + + plConst(hsScalar) kMaxWaveHeight(5.f); + + hsPoint3 p; + p = targBnd.GetMins(); + p.fZ = GetHeight() - kMaxWaveHeight; + fTargBnds[0].Reset(&p); + p = targBnd.GetMaxs(); + p.fZ = GetHeight() + kMaxWaveHeight; + fTargBnds[0].Union(&p); + } +} + + +void plWaveSet7::IAddTarget(const plKey& key) +{ + plObjRefMsg* refMsg = TRACKED_NEW plObjRefMsg(key, plRefMsg::kOnRequest, -1, plObjRefMsg::kModifier); + hsgResMgr::ResMgr()->AddViaNotify( GetKey(), refMsg, plRefFlags::kActiveRef); +} + +void plWaveSet7::IRemoveTarget(const plKey& key) +{ + plObjRefMsg* refMsg = TRACKED_NEW plObjRefMsg(key, plRefMsg::kOnRemove, -1, plObjRefMsg::kModifier); + refMsg->SetRef(this); + refMsg->Send(); +} + +void plWaveSet7::AddTarget(const plKey& key) +{ + IAddTarget(key); +} + +void plWaveSet7::RemoveTarget(const plKey& key) +{ + IRemoveTarget(key); +} + +void plWaveSet7::SetRefObject(plSceneObject* refObj) +{ + fFlags.SetBit(kHasRefObject, refObj != nil); + + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefRefObj); + hsgResMgr::ResMgr()->SendRef(refObj, msg, plRefFlags::kPassiveRef); +} + +void plWaveSet7::AddBuoy(plKey soKey) +{ + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefBuoy); + hsgResMgr::ResMgr()->AddViaNotify(soKey, msg, plRefFlags::kPassiveRef); +} + +void plWaveSet7::RemoveBuoy(plKey soKey) +{ + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRemove, 0, kRefBuoy); + msg->SetRef(soKey->ObjectIsLoaded()); + msg->Send(); +} + + + +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// + +// This is very evil. I can't believe I'm doing this. Stop me, please. +class plFilterMask +{ + protected: + int fExt; + hsScalar **fMask; + + public: + + plFilterMask( hsScalar sig ); + virtual ~plFilterMask(); + + int Begin() const { return -fExt; } + int End() const { return fExt; } + + hsScalar Mask( int i, int j ) const { return fMask[ i ][ j ]; } +}; +// End evil. + +void plWaveSet7::IInitState() +{ + plConst(hsScalar) kWaterTable(-10.f); + plConst(hsScalar) kWaterOffset[3] = { 3.f, 3.f, 0.f }; + plConst(hsScalar) kMaxAtten[3] = { 1.f, 1.f, 1.f }; + plConst(hsScalar) kMinAtten[3] = { 0.f, 0.f, 0.f }; + plConst(hsScalar) kDepthFalloff[3] = { 4.f, 4.f, 6.f }; + + plConst(hsScalar) kGeoMinLen(3.f); + plConst(hsScalar) kGeoMaxLen(8.f); + plConst(hsScalar) kGeoAmpOverLen(0.1f); + plConst(hsScalar) kGeoAngleDev(30.f * hsScalarPI / 180.f); + plConst(hsScalar) kGeoChop(1.f); + + plConst(hsScalar) kTexMinLen(4.f); + plConst(hsScalar) kTexMaxLen(30.f); + plConst(hsScalar) kTexAmpOverLen(0.1f); + plConst(hsScalar) kTexAngleDev(30.f * hsScalarPI / 180.f); + plConst(hsScalar) kTexChop(1.f); + + plFixedWaterState7 state; + + state.fWindDir = hsVector3(0, 1.f, 0); + + state.fGeoState.fMaxLength = kGeoMaxLen; + state.fGeoState.fMinLength = kGeoMinLen; + state.fGeoState.fAmpOverLen = kGeoAmpOverLen; + state.fGeoState.fChop = kGeoChop; + state.fGeoState.fAngleDev = kGeoAngleDev; + + state.fTexState.fMaxLength = kTexMaxLen; + state.fTexState.fMinLength = kTexMinLen; + state.fTexState.fAmpOverLen = kTexAmpOverLen; + state.fTexState.fChop = kTexChop; + state.fTexState.fAngleDev = kTexAngleDev; + + state.fRippleScale = 25.f; + + plConst(hsScalar) kNoise(1.f); + plConst(hsScalar) kSpecStart(50.f); + plConst(hsScalar) kSpecEnd(10000.f); + + hsVector3 spec; + spec[state.kNoise] = kNoise; + spec[state.kSpecStart] = kSpecStart; + spec[state.kSpecEnd] = kSpecEnd; + state.fSpecVec = spec; + + state.fWaterHeight = kWaterTable; + state.fWaterOffset = hsVector3(kWaterOffset[0], kWaterOffset[1], kWaterOffset[2]); + state.fMaxAtten = hsVector3(kMaxAtten[0], kMaxAtten[1], kMaxAtten[2]); + state.fMinAtten = hsVector3(kMinAtten[0], kMinAtten[1], kMinAtten[2]); + state.fDepthFalloff = hsVector3(kDepthFalloff[0], kDepthFalloff[1], kDepthFalloff[2]); + + state.fWispiness = 0; + state.fShoreTint = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f); + state.fMaxColor = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f); + state.fMinColor = hsColorRGBA().Set(0.2f, 0.4f, 0.4f, 0.6f); + state.fEdgeOpac = 1.f; + state.fEdgeRadius = 1.f; + + state.fPeriod = 3.f; + state.fFingerLength = 1.f; + + state.fWaterTint = hsColorRGBA().Set(0.1f, 0.2f, 0.2f, 1.f); + state.fSpecularTint = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f); + + state.fEnvCenter = hsPoint3(0,0,0); + state.fEnvRadius = 500.f; + state.fEnvRefresh = 0.f; + + fState = state; + + IUpdateWindDir(0); +} + + +void plWaveSet7::IInitWaveConsts() +{ + // See header for what fS, fK and fD actually mean. + WaveK waveKs[] = { + // fS fK fD + { 1.f, 5.f, 0.f }, + { 1.f, 10.f, 2.f }, + { -1.f, 20.f, -9.f }, + { 1.f, 30.f, 16.f } + }; + int i; + for( i = 0; i < kNumTexWaves; i++ ) + fWaveKs[i] = waveKs[i]; + + TexWaveWindDep windDeps[] = { + // WindSpeed, Height, Specular + { 0.f, 0.01f, 1.f }, + { 5.f, 0.05f, 0.8f }, + { 10.f, 0.05f, 0.7f }, + { 15.f, 0.1f, 0.6f }, + { 20.f, 0.1f, 0.5f }, + { 25.f, 0.1f, 0.3f } + }; + + for( i = 0; i < kNumWindDep; i++ ) + fWindDeps[i] = windDeps[i]; + + ISetupTextureWaves(); +} + +void plWaveSet7::ISetupTextureWaves() +{ + // Fill in with sine waves. + // Start with 1/4 length of map = size / 4 + // Go to 4 times length of pixel = 4 + // Dir = U +- iLen/(numLen/2) * V + // + // For each mip level + // For each pixel + // For each wavelen + // Add height (sine) to alpha, normal (cosine) to color + // Scrunch + // For each wavelen + // // Half the wavelen to match higher mip level + // // If a wavelen falls below min (4), discard it and smaller lens + // len /= 2 + // if( len < 4 ) + // { + // numLen = iLen + // break; + // } + // + + // Set up our table of amplitudes, wavelengths and wave directions + + // our rotation/scale in our texture transform will be + // scale = waveKs.fK + // dir.x * scale, dir.y * scale + // -dir.y * scale, dir.x * scale + // So to tile, dir.x * scale and dir.y * scale must be integers. + // We select a randomized direction and a wavelength, then + // round the scaled direction components to be integral, then + // figure out what wavelength we actually came up with. + int i; + for( i = 0; i < kNumTexWaves; i++ ) + { + IInitTexWave(i); + } + fTrialUpdate &= ~kReInitWaves; + + return; +} + +void plWaveSet7::IInitTexWave(int i) +{ + hsScalar rads = fRand.RandMinusOneToOne() * TexState().fAngleDev; + hsScalar dx = sin(rads); + hsScalar dy = cos(rads); + + + hsScalar tx = dx; + dx = fWindDir.fY * dx - fWindDir.fX * dy; + dy = fWindDir.fX * tx + fWindDir.fY * dy; + + hsScalar maxLen = TexState().fMaxLength * kCompositeSize / State().fRippleScale; + hsScalar minLen = TexState().fMinLength * kCompositeSize / State().fRippleScale; + hsScalar len = hsScalar(i) / hsScalar(kNumTexWaves-1) * (maxLen - minLen) + minLen; + + hsScalar reps = hsScalar(kCompositeSize) / len; + + dx *= reps; + dy *= reps; + dx = float(int(dx >= 0 ? dx + 0.5f : dx - 0.5f)); + dy = float(int(dy >= 0 ? dy + 0.5f : dy - 0.5f)); + + fTexWaves[i].fRotScale00 = dx; + fTexWaves[i].fRotScale01 = dy; + + hsScalar effK = hsFastMath::InvSqrt(dx*dx + dy*dy); + fTexWaves[i].fLen = hsScalar(kCompositeSize) * effK; + fTexWaves[i].fFreq = hsScalarPI * 2.f / fTexWaves[i].fLen; + fTexWaves[i].fAmp = fTexWaves[i].fLen * TexState().fAmpOverLen; + fTexWaves[i].fPhase = fRand.RandZeroToOne(); + + fTexWaves[i].fDirX = dx * effK; + fTexWaves[i].fDirY = dy * effK; +} + +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +void plWaveSet7::SetSceneNode(const plKey& key) +{ + fSceneNode = key; +} + +void plWaveSet7::AddDynaDecalMgr(plKey& key) +{ + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefDynaDecalMgr); + hsgResMgr::ResMgr()->AddViaNotify(key, msg, plRefFlags::kPassiveRef); + + msg = TRACKED_NEW plGenRefMsg(key, plRefMsg::kOnRequest, 0, plDynaRippleVSMgr::kRefWaveSetBase); + hsgResMgr::ResMgr()->AddViaNotify(GetKey(), msg, plRefFlags::kPassiveRef); +} + +void plWaveSet7::RemoveDynaDecalMgr(plKey& key) +{ + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRemove, 0, kRefDynaDecalMgr); + msg->SetRef(key->ObjectIsLoaded()); + msg->Send(); + + msg = TRACKED_NEW plGenRefMsg(key, plRefMsg::kOnRemove, 0, plDynaRippleVSMgr::kRefWaveSetBase); + msg->SetRef(this); + msg->Send(); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////// + +void plWaveSet7::IUpdateLayers(hsScalar dt) +{ + IUpdateBumpLayers(dt); + + ISubmitRenderRequests(); +} + +void plWaveSet7::IUpdateBumpLayers(hsScalar dt) +{ + plCONST(hsScalar) speedHack(1.f / 3.f); + int i; + for( i = 0; i < kNumTexWaves; i++ ) + { + hsScalar speed = hsFastMath::InvSqrtAppr(fTexWaves[i].fLen / (2.f * hsScalarPI * kGravConst)) * speedHack; + fTexWaves[i].fPhase -= dt * speed; + fTexWaves[i].fPhase -= int(fTexWaves[i].fPhase); + + if( fBumpLayers[i] ) + { + hsMatrix44 xfm = fBumpLayers[i]->GetTransform(); + + xfm.fMap[0][0] = fTexWaves[i].fRotScale00; + xfm.fMap[0][1] = fTexWaves[i].fRotScale01; + xfm.fMap[1][0] = -fTexWaves[i].fRotScale01; + xfm.fMap[1][1] = fTexWaves[i].fRotScale00; + + xfm.fMap[0][3] = fTexWaves[i].fPhase; + fBumpLayers[i]->SetTransform(xfm); + } + + LogF("%.4d - L%8.4f R(%5.3f, %5.3f)", i, fTexWaves[i].fLen, fTexWaves[i].fRotScale00, fTexWaves[i].fRotScale01); + GraphLen(fTexWaves[i].fLen); + } +} + +void plWaveSet7::ISubmitRenderRequests() +{ + if( fBumpReqMsg ) + fBumpReqMsg->SendAndKeep(); + + int i; + for( i = 0; i < kGraphShorePasses; i++ ) + { + if( fGraphReqMsg[i] ) + fGraphReqMsg[i]->SendAndKeep(); + } +} + +plMipmap* plWaveSet7::ICreateBumpBitmapFFP(hsScalar amp, hsScalar dx, hsScalar dy) const +{ + return nil; +} + +hsGMaterial* plWaveSet7::ICreateBumpLayersFFP() +{ + // Create kNumTexWaves bitmaps, each containing a single cycle of values: + // mag = 0.25 * -Amplitude * cos(i/sz*2*Pi) + // r = mag * dx * 0.5 + 0.5 + // g = mag * dy * 0.5 + 0.5 + // b = 0.7 * 0.25 * 0.5 + 0.5 + // a = 1.f + // + // Where: + // Amplitude == wave.amp * wave.freq * kNormHack(3.0) + // dx, dy == wave.fDir + // + + // Create a blank material + + // Create base layer + // Blend mode is none + // Transform scales to wave[0].freq + // No rotation, translation + // Add bitmap 0. + + // For layers 1..kNumTexWaves + // Create blank layer + // Blend mode is ADDSIGNED + // Transform scales to wave[i].freq + // No rotation or translation. + // Add bitmap i; + + // NOTE: if we use the fixed function pipeline to composite + // these into the CompositeMaterial, then they all need to + // be scaled down to avoid saturation on summation. + // Doing the composite with a pixel shader would allow the + // textures to be stored in full 256 range, and then scaled + // down during the summation, after interpolation. Would + // that be better? Not sure. Pixel shader registers only have + // a range of [-1..1], and it's hinted to be 8-bit fixed point. + // So we'd still have to scale down texture value (losing precision), + // then add it in. + // + // Also, for pixel shaded compositing, we can just store the cosine + // values in each layer, with no rotation in the bitmap values, just + // in the layer transform. No rotation would make for a cleaner + // interpolation (not interpolating diagonally across the waves). + // How much difference is that? Dunno. But it would also allow the + // bitmap to hold the full range [-1..1]=>[0..255] of cosine values, + // scaling them down with constants. The pixel shader would look like: + // + // tex t0; + // tex t1; + // tex t2; + // tex t3; + // + // mul r0, t0_bx2, c0; + // mad r0, t1_bx2, c1, r0; + // mad r0, t2_bx2, c2, r0; + // mad r0, t3_bx2, c3, r0; + // // Now bias it back into range [0..1] for output. + // mad r0, r0, c4, c5; // c4 = (0.5, 0.5, 0.5, 1), c5 = (0.5, 0.5, 0.5, 0) + // // or add_d2 r0, r0, c0.b; // except this is likely to saturate before the divide. Dunno. + // + // where the c[i] constants would be: + // c[i] = -wave[i].amp * wave[i].freq * kSomeNormHack * (wave[i].fDir.x, wave[i].fDir.y, 0, 1) + // except c[0].b = 0.7; + // In fact, we could cut it down to 2 textures using alpha replicate, except we need different + // transforms for each (for animation). So screw that. + // + // Note that the above won't run on a ps.1.0 pixel shader, because the combined number of tex and + // arith instructions is 9. Could make a version that was okay for ps.1.0 by doing: + // + // mad r0, t0_bias, c0, c4; // t0[-0.5..0.5]*c0 + 0.5, which is [0..1] + // mad r0, t1_bias, c1, r0; // + t1[-0.5..0.5]*c1 + // mad r0, t2_bias, c2, r0; // + t2[-0.5..0.5]*c2 + // mad r0, t3_bias, c3, r0; // + t3[-0.5..0.5]*c3 + // + // which doesn't need a final bias instruction because it remains biased all the way through + // at the cost of 1 bit of precision. + // Whatever. + // + + // return material; + return nil; +} + +plMipmap* plWaveSet7::ICreateBiasNoiseMap() +{ + const int size = kCompositeSize >> 2; + plMipmap* mipMap = TRACKED_NEW plMipmap( + size, size, + plMipmap::kARGB32Config, + 1, + plMipmap::kUncompressed, + plMipmap::UncompressedInfo::kRGB8888); + + char buff[256]; + sprintf(buff, "%s_%s", GetKey()->GetName(), "BiasBitPS"); + hsgResMgr::ResMgr()->NewKey(buff, mipMap, GetKey()->GetUoid().GetLocation()); + + int i; + for( i = 0; i < size; i++ ) + { + int j; + for( j = 0; j < size; j++ ) + { + hsScalar x = fRand.RandMinusOneToOne(); + hsScalar y = fRand.RandMinusOneToOne(); + + UInt8 r = UInt8((x * 0.5f + 0.5f) * 255.999f); + UInt8 g = UInt8((y * 0.5f + 0.5f) * 255.999f); + +// r = g = 0xff; // SATURATE + + UInt32* val = mipMap->GetAddr32(i, j); + *val = (0xff << 24) + | (r << 16) + | (g << 8) + | 0xff; + } + } + // For bonus points, modulate by a VERY coherent noise function. + return mipMap; +} + +plMipmap* plWaveSet7::ICreateBumpMipmapPS() +{ +// const int sizeV = kCompositeSize; + const int sizeV = 1; + + const kNumLevels = 8; // must be log2(kCompositeSize) + hsAssert(kCompositeSize == (1 << kNumLevels), "Mismatch on size and num mip levels"); + + if( !fCosineLUT ) + { + plMipmap* mipMap = TRACKED_NEW plMipmap( + kCompositeSize, sizeV, + plMipmap::kARGB32Config, + kNumLevels, + plMipmap::kUncompressed, + plMipmap::UncompressedInfo::kRGB8888); + + char buff[256]; + sprintf(buff, "%s_%s", GetKey()->GetName(), "BumpBitPS"); + hsgResMgr::ResMgr()->NewKey(buff, mipMap, GetKey()->GetUoid().GetLocation()); + + hsgResMgr::ResMgr()->SendRef(mipMap->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefCosineLUT), plRefFlags::kActiveRef); + } + hsAssert(fCosineLUT, "Failed to make cosine lookup table"); + + int k; + for( k = 0; k < kNumLevels; k++ ) + { + fCosineLUT->SetCurrLevel(k); + + const int sizeU = kCompositeSize >> k; + + int i; + for( i = 0; i < sizeU; i++ ) + { + hsScalar y = hsScalar(i); + hsScalar dist = hsScalar(i) / hsScalar(sizeU-1) * 2.f * hsScalarPI; + hsScalar c = cos(dist); + hsScalar s = sin(dist); + s *= 0.5f; + s += 0.5f; + s = hsScalar(pow(s, TexState().fChop)); + c *= s; + UInt8 cosDist = UInt8((c * 0.5 + 0.5) * 255.999f); + int j; + for( j = 0; j < sizeV; j++ ) + { + UInt32* val = fCosineLUT->GetAddr32(i, j); + *val = (0xff << 24) + | (cosDist << 16) + | (cosDist << 8) + | 0xff; + } + } + } + fCosineLUT->MakeDirty(); + + return fCosineLUT; +} + +void plWaveSet7::IAddBumpBiasLayer(hsGMaterial* mat) +{ + if( !fBiasLayer[0] ) + { + plMipmap* mipMap = ICreateBiasNoiseMap(); + + int i; + for( i = 0; i < 2; i++ ) + { + plLayer* layer = TRACKED_NEW plLayer; + char buff[256]; + sprintf(buff, "%s_%s_%d", GetKey()->GetName(), "Bias", i); + hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation()); + + layer->SetBlendFlags(hsGMatState::kBlendAdd); + layer->SetZFlags(hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite); + layer->SetShadeFlags(hsGMatState::kShadeReallyNoFog + | hsGMatState::kShadeNoProjectors + | hsGMatState::kShadeNoShade); + layer->SetClampFlags(0); + layer->SetMiscFlags(0); + layer->SetMiscFlags(i ? 0 : hsGMatState::kMiscRestartPassHere); + + layer->SetAmbientColor(hsColorRGBA().Set(0.25f, 0.25f, 0.f, 0.f)); + layer->SetRuntimeColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 0.f)); + layer->SetOpacity(0.f); + + layer->SetUVWSrc(0); + + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture); + hsgResMgr::ResMgr()->SendRef(mipMap->GetKey(), refMsg, plRefFlags::kActiveRef); + + IAddBumpBiasShaders(layer); + + fBiasLayer[i] = layer; // SHOULD BE REFFED (SO SHOULD fBumpLayers) + } + } + + mat->AddLayerViaNotify(fBiasLayer[0]); + mat->AddLayerViaNotify(fBiasLayer[1]); + +} + +plLayer* plWaveSet7::ICreateBumpLayerPS(plMipmap* mipMap, hsGMaterial* bumpMat, int which) +{ + plLayer* layer = TRACKED_NEW plLayer; + char buff[256]; + sprintf(buff, "%s_%s_%d", GetKey()->GetName(), "BumpLayerPS", which); + hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation()); + + layer->SetBlendFlags(which ? hsGMatState::kBlendAdd : 0); + layer->SetZFlags(hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite); + layer->SetShadeFlags(hsGMatState::kShadeReallyNoFog + | hsGMatState::kShadeNoProjectors + | hsGMatState::kShadeNoShade + | hsGMatState::kShadeWhite); + layer->SetClampFlags(0); + layer->SetMiscFlags(0); + if( (which / kBumpPerPass) * kBumpPerPass == which ) + layer->SetMiscFlags(hsGMatState::kMiscRestartPassHere); + + layer->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + layer->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + layer->SetOpacity(1.f); + + layer->SetUVWSrc(0); + + // Set up the transform. + hsMatrix44 xfm; + xfm.Reset(); + xfm.NotIdentity(); + + xfm.fMap[0][0] = fTexWaves[which].fRotScale00; + xfm.fMap[0][1] = fTexWaves[which].fRotScale01; + xfm.fMap[1][0] = -fTexWaves[which].fRotScale01; + xfm.fMap[1][1] = fTexWaves[which].fRotScale00; + layer->SetTransform(xfm); + + bumpMat->AddLayerViaNotify(layer); + + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture); + hsgResMgr::ResMgr()->SendRef(mipMap->GetKey(), refMsg, plRefFlags::kActiveRef); + + return layer; +} + +hsGMaterial* plWaveSet7::ICreateBumpLayersPS() +{ + if( fBumpMat ) + return fBumpMat; + + // Create four bitmaps, each containing a single cycle of values: + // mag = cos(i/sz*2*Pi) + // r = 0 + // g = mag* 0.5 + 0.5 + // b = 1.f + // a = 1.f + // + // Where: + // Amplitude == wave.amp * wave.freq * kNormHack(3.0) + // + + // Create a blank material + hsGMaterial* bumpMat = TRACKED_NEW hsGMaterial; + char buff[256]; + sprintf(buff, "%s_%s", GetKey()->GetName(), "BumpMatPS"); + hsgResMgr::ResMgr()->NewKey(buff, bumpMat, GetKey()->GetUoid().GetLocation()); + + plMipmap* mipMap = ICreateBumpMipmapPS(); + + // Create base layer + // Blend mode is none + // Transform scales to wave[0].freq + // No translation + // Rotation is by wave[0].fDir + // Add bitmap 0. + int i; + for( i = 0; i < kNumBumpShaders; i++ ) + { + int nBegin = bumpMat->GetNumLayers(); + + int j; + for( j = 0; j < kBumpPerPass; j++ ) + fBumpLayers[i*kBumpPerPass + j] = ICreateBumpLayerPS(mipMap, bumpMat, i*kBumpPerPass + j); + + IAddBumpVertexShader(bumpMat, i, nBegin, nBegin + kBumpPerPass-1); + IAddBumpPixelShader(bumpMat, i, nBegin, nBegin + kBumpPerPass-1); + + } + IAddBumpBiasLayer(bumpMat); + + + // Need to add this via notify to ourselves. + hsgResMgr::ResMgr()->SendRef(bumpMat->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefBumpMat), plRefFlags::kActiveRef); + + ICreateBumpDrawable(); + + return fBumpMat = bumpMat; +} + +void plWaveSet7::IAddBumpBiasShaders(plLayer* layer) +{ + if( !fBiasVShader ) + { + plShader* vShader = TRACKED_NEW plShader; + + char buff[256]; + sprintf(buff, "%s_BiasVS", GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation()); + vShader->SetIsPixelShader(false); + + vShader->SetNumConsts(plBiasVS::kNumConsts); + + vShader->SetVector(plBiasVS::kTexU0, + 1.f, + 0.f, + 0.f, + 0.f); + vShader->SetVector(plBiasVS::kTexV0, + 0.f, + 1.f, + 0.f, + 0.f); + + vShader->SetVector(plBiasVS::kTexU1, + 1.f, + 0.f, + 0.f, + 0.f); + vShader->SetVector(plBiasVS::kTexV1, + 0.f, + 1.f, + 0.f, + 0.f); + + vShader->SetVector(plBiasVS::kNumbers, + 0.f, + 0.5f, + 1.f, + 2.f); + + hsVector3 specVec = State().fSpecVec; + hsScalar biasScale = 0.5f * specVec[State().kNoise] / (hsScalar(kNumBumpShaders) + specVec[State().kNoise]); + vShader->SetVector(plBiasVS::kScaleBias, + biasScale, + biasScale, + 0.f, + 1.f); + + vShader->SetInputFormat(1); + vShader->SetOutputFormat(0); + +// static const plShaderDecl vDecl("sha/vs_BiasNormals.inl"); +// vShader->SetDecl(&vDecl); + vShader->SetDecl(plShaderTable::Decl(vs_BiasNormals)); + + hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefBiasVShader), plRefFlags::kActiveRef); + + + fBiasVShader = vShader; + } + + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kVertexShader); + hsgResMgr::ResMgr()->SendRef(fBiasVShader->GetKey(), refMsg, plRefFlags::kActiveRef); + + + if( !fBiasPShader ) + { + plShader* pShader = TRACKED_NEW plShader; + + char buff[256]; + sprintf(buff, "%s_BiasPS", GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation()); + pShader->SetIsPixelShader(true); + + pShader->SetNumConsts(plBiasPS::kNumConsts); + + pShader->SetInputFormat(0); + pShader->SetOutputFormat(0); + +// static const plShaderDecl pDecl("sha/ps_BiasNormals.inl"); +// pShader->SetDecl(&pDecl); + pShader->SetDecl(plShaderTable::Decl(ps_BiasNormals)); + + hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefBiasPShader), plRefFlags::kActiveRef); + + fBiasPShader = pShader; + } + + refMsg = TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kPixelShader); + hsgResMgr::ResMgr()->SendRef(fBiasPShader->GetKey(), refMsg, plRefFlags::kActiveRef); + +} + +void plWaveSet7::IAddBumpVertexShader(hsGMaterial* mat, int iShader, int iFirst, int iLast) +{ + if( !fBumpVShader[0] ) + { + int iBase = 0; + while( iBase < kNumTexWaves ) + { + int iShader = iBase / kBumpPerPass; + + plShader* vShader = TRACKED_NEW plShader; + char buff[256]; + sprintf(buff, "%s_BumpVS_%d", GetKey()->GetName(), iShader); + hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation()); + vShader->SetIsPixelShader(false); + + vShader->SetNumConsts(plBumpVS::kNumConsts); + + vShader->SetVector(plBumpVS::kNumbers, + 0.f, + 0.5f, + 1.f, + 2.f); + + vShader->SetInputFormat(1); + vShader->SetOutputFormat(0); + + vShader->SetNumPipeConsts(kBumpPerPass); + int i; + for( i = 0; i < kBumpPerPass; i++ ) + { + vShader->SetPipeConst(i, static_cast(plPipeConst::kTex1x4_0 + i), plBumpVS::kUXform0 + i); + } + + vShader->SetDecl(plShaderTable::Decl(vs_CompCosines)); + + hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, iShader, kRefBumpVShader), plRefFlags::kActiveRef); + + fBumpVShader[iShader] = vShader; + + iBase += kBumpPerPass; + } + } + + IAddShaderToLayers(mat, iFirst, iLast, plLayRefMsg::kVertexShader, fBumpVShader[iShader]); +} + +void plWaveSet7::IAddBumpPixelShader(hsGMaterial* mat, int iShader, int iFirst, int iLast) +{ + if( !fBumpPShader[0] ) + { + int iBase = 0; + while( iBase < kNumTexWaves ) + { + int iShader = iBase / kBumpPerPass; + + plShader* pShader = TRACKED_NEW plShader; + char buff[256]; + sprintf(buff, "%s_BumpPS_%d", GetKey()->GetName(), iShader); + hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation()); + pShader->SetIsPixelShader(true); + + pShader->SetNumConsts(plBumpPS::kNumConsts); + + int iLay; + for( iLay = 0; iLay < kBumpPerPass; iLay++ ) + { + pShader->SetVector(plBumpPS::kWave0, + -fTexWaves[iBase + iLay].fDirX * (1.f / hsScalar(kBumpPerPass)), + -fTexWaves[iBase + iLay].fDirY * (1.f / hsScalar(kBumpPerPass)), + 1.f, + 1.f); + } + + pShader->SetInputFormat(0); + pShader->SetOutputFormat(0); + + // pShader->SetShaderFileName("sha/ps_CompCosines.inl"); + // pShader->SetShaderFileName("sha/ps_TestPos.inl"); +// static const plShaderDecl moreDecl("sha/ps_MoreCosines.inl"); +// pShader->SetDecl(&moreDecl); + + pShader->SetDecl(plShaderTable::Decl(ps_MoreCosines)); + + pShader->SetVector(plBumpPS::kHalfOne, 0.25f, 0.25f, 0.25f, 1.f); + + hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, iShader, kRefBumpPShader), plRefFlags::kActiveRef); + + fBumpPShader[iShader] = pShader; + + iBase += kBumpPerPass; + } + } + + IAddShaderToLayers(mat, iFirst, iLast, plLayRefMsg::kPixelShader, fBumpPShader[iShader]); +} + +plDrawableSpans* plWaveSet7::ICreateBumpDrawable() +{ + fBumpDraw = TRACKED_NEW plDrawableSpans; + char buff[256]; + sprintf(buff, "%s_BumpDraw", GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(buff, fBumpDraw, GetKey()->GetUoid().GetLocation()); + + ICreateClearDrawable(fBumpDraw, fBumpMat); + + hsgResMgr::ResMgr()->SendRef(fBumpDraw->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefBumpDraw), plRefFlags::kActiveRef); + + return fBumpDraw; +} + +plDrawableSpans* plWaveSet7::ICreateClearDrawable(plDrawableSpans* drawable, hsGMaterial* mat) +{ +// drawable->SetNativeProperty(plDrawable::kPropVolatile, true); + + hsPoint3 pos[4]; + hsVector3 norm[4]; + hsPoint3 uvw[4]; + + pos[0].fX = -1.f; + pos[0].fY = -1.f; + pos[0].fZ = 0.5f; + + norm[0].Set(0.f, 0.f, -1.f); + + uvw[0].fX = 0.5f / kCompositeSize; + uvw[0].fY = 0.5f / kCompositeSize; + uvw[0].fZ = 0; + + // P1 + pos[1] = pos[0]; + pos[1].fY += 2.f; + + norm[1] = norm[0]; + + uvw[1] = uvw[0]; + uvw[1].fY += 1.f; + + // P2 + pos[2] = pos[0]; + pos[2].fX += 2.f; + pos[2].fY += 2.f; + + norm[2] = norm[0]; + + uvw[2] = uvw[0]; + uvw[2].fX += 1.f; + uvw[2].fY += 1.f; + + // P3 + pos[3] = pos[0]; + pos[3].fX += 2.f; + + norm[3] = norm[0]; + + uvw[3] = uvw[0]; + uvw[3].fX += 1.f; + + UInt16 idx[6]; + idx[0] = 1; + idx[1] = 0; + idx[2] = 2; + + idx[3] = 2; + idx[4] = 0; + idx[5] = 3; + + + plDrawableGenerator::GenerateDrawable( 4, pos, norm, + uvw, 1, + nil, false, nil, + 6, idx, + mat, + hsMatrix44::IdentityMatrix(), + false, + nil, + drawable); + + return drawable; +} + +plRenderRequest* plWaveSet7::ICreateRenderRequest(plRenderTarget* rt, plDrawableSpans* draw, hsScalar pri) +{ + plRenderRequest* req = TRACKED_NEW plRenderRequest; + + static plPageTreeMgr emptyMgr; + req->SetPageTreeMgr(&emptyMgr); + + req->SetClearDrawable(draw); + + req->SetOrthogonal(true); + + req->SetLocalTransform(hsMatrix44::IdentityMatrix(), hsMatrix44::IdentityMatrix()); + req->SetCameraTransform(hsMatrix44::IdentityMatrix(), hsMatrix44::IdentityMatrix()); + + req->SetHither(0); + req->SetYon(2.f); + req->SetSizeX(2.f); + req->SetSizeY(2.f); + + req->SetPriority(pri); + + req->SetRenderTarget(rt); + + return req; +} + +plRenderTarget* plWaveSet7::ICreateTransferRenderTarget(const char* name, int size) +{ + UInt16 flags = plRenderTarget::kIsTexture | plRenderTarget::kIsOrtho; + UInt8 bitDepth = 32; + UInt8 zDepth = 0; + UInt8 stencilDepth = 0; + + plRenderTarget* rt = TRACKED_NEW plRenderTarget(flags, size, size, bitDepth, zDepth, stencilDepth); + + char buff[256]; + sprintf(buff, "%s_%s", GetKey()->GetName(), name); + hsgResMgr::ResMgr()->NewKey(buff, rt, GetKey()->GetUoid().GetLocation()); + + return rt; +} + +plLayer* plWaveSet7::ICreateTotalLayer(plBitmap* bm, hsGMaterial* mat, int which, const char* suff) +{ + plLayer* layer = mat->GetNumLayers() > which ? plLayer::ConvertNoRef(mat->GetLayer(which)->BottomOfStack()) : nil; + if( !layer ) + { + layer = TRACKED_NEW plLayer; + char buff[256]; + sprintf(buff, "%s_%sLayerPS_%d", GetKey()->GetName(), suff, which); + hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation()); + + layer->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + layer->SetRuntimeColor(State().fWaterTint); + layer->SetOpacity(1.f); + + mat->AddLayerViaNotify(layer); + } + + layer->SetBlendFlags(which ? hsGMatState::kBlendAddSigned : hsGMatState::kBlendAlpha); + layer->SetZFlags(which ? hsGMatState::kZNoZWrite : 0); + layer->SetShadeFlags(hsGMatState::kShadeNoProjectors + | hsGMatState::kShadeNoShade); + layer->SetClampFlags(0); + layer->SetMiscFlags(hsGMatState::kMiscTwoSided); + + layer->SetUVWSrc(which); + + // Set up the transform. + hsMatrix44 xfm; + xfm.Reset(); + + layer->SetTransform(xfm); + + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture); + hsgResMgr::ResMgr()->SendRef(bm->GetKey(), refMsg, plRefFlags::kActiveRef); + + return layer; +} + +plLayer* plWaveSet7::ICreateTotalEnvLayer(plBitmap* envMap, hsGMaterial* mat, int which, const char* pref) +{ + plLayer* layer = TRACKED_NEW plLayer; + char buff[256]; + sprintf(buff, "%s_%s_%s_%d", GetKey()->GetName(), pref, "EnvLayerPS", which); + hsgResMgr::ResMgr()->NewKey(buff, layer, GetKey()->GetUoid().GetLocation()); + + layer->SetBlendFlags(which ? hsGMatState::kBlendAddSigned : 0); + layer->SetZFlags(which ? hsGMatState::kZNoZWrite : 0); + layer->SetShadeFlags(hsGMatState::kShadeNoProjectors + | hsGMatState::kShadeNoShade); + layer->SetClampFlags(0); + layer->SetMiscFlags(0); + + layer->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + layer->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + layer->SetOpacity(1.f); + + layer->SetUVWSrc(which); + + // Set up the transform. + hsMatrix44 xfm; + xfm.Reset(); + + layer->SetTransform(xfm); + + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture); + hsgResMgr::ResMgr()->SendRef(envMap->GetKey(), refMsg, plRefFlags::kActiveRef); + + mat->AddLayerViaNotify(layer); + + return layer; +} + +hsGMaterial* plWaveSet7::ICreateFixedMatPS(hsGMaterial* mat, const int numUVWs) +{ + hsAssert(mat, "Nil mat should have been filtered out before calling this"); + + // Check if we've already done this one. + if( (mat->GetNumLayers() == 4) + &&(mat->GetLayer(3) == fFixedLayers[3]) ) + return mat; + + // First, strip off whatever's on there now. + // If this is the + int i; + for( i = mat->GetNumLayers()-1; i > 0; i-- ) + { + plMatRefMsg* refMsg = TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnRemove, i, plMatRefMsg::kLayer); + hsgResMgr::ResMgr()->SendRef(mat->GetLayer(i)->GetKey(), refMsg, plRefFlags::kActiveRef); + } + + plRenderTarget* rt = ICreateTransferRenderTarget("CompRT", kCompositeSize); + + if( mat->GetNumLayers() && mat->GetLayer(0) ) + { + fState.fWaterTint = mat->GetLayer(0)->GetRuntimeColor(); + } + + fFixedLayers[0] = ICreateTotalLayer(rt, mat, 0, "Fix"); + fFixedLayers[1] = ICreateTotalLayer(rt, mat, 1, "Fix"); + fFixedLayers[2] = ICreateTotalLayer(rt, mat, 2, "Fix"); + fFixedLayers[3] = ICreateTotalEnvLayer(fEnvMap, mat, 3, "Fix"); + + + fBumpReq = ICreateRenderRequest(rt, fBumpDraw, -100.f); + fBumpReqMsg = TRACKED_NEW plRenderRequestMsg(GetKey(), fBumpReq); + + IAddFixedVertexShader(mat, numUVWs); + IAddFixedPixelShader(mat); + + return mat; +} + +void plWaveSet7::ICreateFixedMat(hsGMaterial* mat, const int numUVWs) +{ + if( !mat ) + return; + + if( !fEnvMap ) + { + plDynamicEnvMap* env = TRACKED_NEW plDynamicEnvMap((UInt16)fEnvSize, (UInt16)fEnvSize, 32); + hsgResMgr::ResMgr()->NewKey(GetKey()->GetName(), env, GetKey()->GetUoid().GetLocation()); + fEnvMap = env; + env->SetPosition(hsPoint3(0, 0, 50.f)); + env->SetPosition(State().fEnvCenter); + env->SetYon(10000.f); + env->SetRefreshRate(State().fEnvRefresh); + env->Init(); + env->ReRender(); + } + + ICreateBumpLayersPS(); + ICreateFixedMatPS(mat, numUVWs); +} + + +void plWaveSet7::IAddShoreVertexShader(hsGMaterial* mat) +{ + if( !fShoreVShader ) + { + + plShader* vShader = TRACKED_NEW plShader; + + char buff[256]; + sprintf(buff, "%s_ShoreVS", GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation()); + vShader->SetIsPixelShader(false); + + vShader->SetNumConsts(plShoreVS::kNumConsts); + + vShader->SetInputFormat(1); // This should really be one!!! + vShader->SetOutputFormat(0); + + vShader->SetVector(plShoreVS::kSinConsts, 1.f, -1.f/6.f, 1.f/120.f, -1.f/5040.f); + vShader->SetVector(plShoreVS::kCosConsts, 1.f, -1.f/2.f, 1.f/24.f, -1.f/720.f); + vShader->SetVector(plShoreVS::kPiConsts, 1.f / (8.f*hsScalarPI*4.f*4.f), hsScalarPI/2.f, hsScalarPI, hsScalarPI*2.f); + vShader->SetVector(plShoreVS::kNumericConsts, 0, 0.5f, 1.f, 2.f); + + plConst(hsScalar) kK1(0.5f); + plConst(hsScalar) kK2(1.5f); + hsScalar negK1OverK2Sq = -kK1 / (kK2 * kK2); + vShader->SetVector(plShoreVS::kIncline, negK1OverK2Sq, kK1, 0.f, 0.f); + + vShader->SetNumPipeConsts(5); + vShader->SetPipeConst(0, plPipeConst::kWorldToNDC, plShoreVS::kWorldToNDC); + vShader->SetPipeConst(1, plPipeConst::kTex3x4_0, plShoreVS::kTex0Transform); + vShader->SetPipeConst(2, plPipeConst::kLocalToWorld, plShoreVS::kLocalToWorld); + vShader->SetPipeConst(3, plPipeConst::kLayRuntime, plShoreVS::kShoreTint); + vShader->SetPipeConst(4, plPipeConst::kFogSet, plShoreVS::kFogSet); + +// vShader->SetShaderFileName("sha/vs_Shore.inl"); +// vShader->SetShaderFileName("sha/vs_ShoreSteep.inl"); +// vShader->SetShaderFileName("sha/vs_ShoreSucks.inl"); +// vShader->SetShaderFileName("sha/vs_ShoreLeave.inl"); +// vShader->SetShaderFileName("sha/vs_ShoreLeave6.inl"); +// vShader->SetDecl(plShaderTable::Decl(vs_ShoreLeave6)); + +// static const plShaderDecl decl("sha/vs_ShoreLeave7.inl"); +// vShader->SetDecl(&decl); + vShader->SetDecl(plShaderTable::Decl(vs_ShoreLeave7)); + + hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefShoreVShader), plRefFlags::kActiveRef); + + fShoreVShader = vShader; + + } + + IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kVertexShader, fShoreVShader); + +} + +void plWaveSet7::IAddShorePixelShader(hsGMaterial* mat) +{ + if( !fShorePShader ) + { + plShader* pShader = TRACKED_NEW plShader; + + char buff[256]; + sprintf(buff, "%s_ShorePS", GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation()); + pShader->SetIsPixelShader(true); + +// pShader->SetShaderFileName("sha/ps_ShoreSucks.inl"); +// pShader->SetShaderFileName("sha/ps_ShoreLeave6.inl"); + pShader->SetDecl(plShaderTable::Decl(ps_ShoreLeave6)); + + hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefShorePShader), plRefFlags::kActiveRef); + + fShorePShader = pShader; + } + + IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kPixelShader, fShorePShader); +} + +void plWaveSet7::IAddFixedVertexShader(hsGMaterial* mat, const int numUVWs) +{ + if( !fFixedVShader ) + { + + plShader* vShader = TRACKED_NEW plShader; + + char buff[256]; + sprintf(buff, "%s_FixedVS", GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation()); + vShader->SetIsPixelShader(false); + + vShader->SetNumConsts(plFixedVS7::kNumConsts); + + vShader->SetInputFormat(numUVWs); + vShader->SetOutputFormat(0); + + vShader->SetVector(plFixedVS7::kSinConsts, 1.f, -1.f/6.f, 1.f/120.f, -1.f/5040.f); + vShader->SetVector(plFixedVS7::kCosConsts, 1.f, -1.f/2.f, 1.f/24.f, -1.f/720.f); + vShader->SetVector(plFixedVS7::kPiConsts, 1.f / (8.f*hsScalarPI*4.f*4.f), hsScalarPI/2.f, hsScalarPI, hsScalarPI*2.f); + vShader->SetVector(plFixedVS7::kNumericConsts, 0, 0.5f, 1.f, 2.f); + + vShader->SetNumPipeConsts(5); + vShader->SetPipeConst(0, plPipeConst::kWorldToNDC, plFixedVS7::kWorldToNDC); + vShader->SetPipeConst(1, plPipeConst::kCamPosWorld, plFixedVS7::kCameraPos); + vShader->SetPipeConst(2, plPipeConst::kLocalToWorld, plFixedVS7::kLocalToWorld); + vShader->SetPipeConst(3, plPipeConst::kLayRuntime, plFixedVS7::kWaterTint); + vShader->SetPipeConst(4, plPipeConst::kFogSet, plFixedVS7::kFogSet); + + +// static const plShaderDecl decl("sha/vs_WaveFixedFin7.inl"); +// vShader->SetDecl(&decl); + vShader->SetDecl(plShaderTable::Decl(vs_WaveFixedFin7)); + + +// vShader->SetShaderFileName("sha/vs_WaveFixedFin6.inl"); +// vShader->SetShaderFileName("sha/vs_WaveFixedFin.inl"); +// vShader->SetShaderFileName("sha/vs_TestPos.inl"); + + hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefFixedVShader), plRefFlags::kActiveRef); + + fFixedVShader = vShader; + + } + + IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kVertexShader, fFixedVShader); + +} + +// type is either plLayRefMsg::kVertexShader or plLayRefMsg::kPixelShader. +void plWaveSet7::IAddShaderToLayers(hsGMaterial* mat, int iFirst, int iLast, UInt8 type, plShader* shader) +{ + if( iFirst < 0 ) + iFirst = 0; + if( UInt32(iLast) >= mat->GetNumLayers() ) + iLast = mat->GetNumLayers()-1; + int i; + for( i = iFirst; i <= iLast; i++ ) + { + plLayer* layer = plLayer::ConvertNoRef(mat->GetLayer(i)->BottomOfStack()); + if( layer + && (layer->GetVertexShader() != shader) + && (layer->GetPixelShader() != shader) ) + { + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, type); + hsgResMgr::ResMgr()->SendRef(shader->GetKey(), refMsg, plRefFlags::kActiveRef); + +// layer->SetShadeFlags(layer->GetShadeFlags() | hsGMatState::kShadeReallyNoFog); + } + } +} + +void plWaveSet7::IAddFixedPixelShader(hsGMaterial* mat) +{ + if( !fFixedPShader ) + { + plShader* pShader = TRACKED_NEW plShader; + char buff[256]; + sprintf(buff, "%s_FixedPS", GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation()); + pShader->SetIsPixelShader(true); + +// pShader->SetNumConsts(plFixedPS::kNumConsts); + pShader->SetNumConsts(0); + + pShader->SetInputFormat(0); + pShader->SetOutputFormat(0); + +// pShader->SetShaderFileName("sha/ps_WaveFixed.inl"); +// pShader->SetShaderFileName("sha/ps_TestPos.inl"); + pShader->SetDecl(plShaderTable::Decl(ps_WaveFixed)); + + hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefFixedPShader), plRefFlags::kActiveRef); + + fFixedPShader = pShader; + } + + IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kPixelShader, fFixedPShader); + +} + +void plWaveSet7::IAddRipVertexShader(hsGMaterial* mat, const plRipVSConsts& ripConsts) +{ + if( !fRipVShader ) + { + plShader* vShader = TRACKED_NEW plShader; + char buff[256]; + sprintf(buff, "%s_RipVS", GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation()); + vShader->SetIsPixelShader(false); + + vShader->SetInputFormat(1); // This should really be one!!! + vShader->SetOutputFormat(0); + + vShader->SetNumConsts(plRipVS::kNumConsts); + + vShader->SetVector(plRipVS::kSinConsts, 1.f, -1.f/6.f, 1.f/120.f, -1.f/5040.f); + vShader->SetVector(plRipVS::kCosConsts, 1.f, -1.f/2.f, 1.f/24.f, -1.f/720.f); + vShader->SetVector(plRipVS::kPiConsts, 1.f / (8.f*hsScalarPI*4.f*4.f), hsScalarPI/2.f, hsScalarPI, hsScalarPI*2.f); + vShader->SetVector(plRipVS::kNumericConsts, 0, 0.5f, 1.f, 2.f); + + hsVector3 waterOffset = State().fWaterOffset; + vShader->SetVector(plRipVS::kWaterLevel, + State().fWaterHeight + waterOffset.fX, + State().fWaterHeight + waterOffset.fY, + State().fWaterHeight + waterOffset.fZ, + State().fWaterHeight); + + hsVector3 maxAtten = State().fMaxAtten; + hsVector3 minAtten = State().fMinAtten; + hsVector3 depthFalloff = State().fDepthFalloff; + vShader->SetVector(plRipVS::kDepthFalloff, + (maxAtten.fX - minAtten.fX) / depthFalloff.fX, + (maxAtten.fY - minAtten.fY) / depthFalloff.fY, + (maxAtten.fZ - minAtten.fZ) / depthFalloff.fZ, + 1.f + ); + vShader->SetVector(plRipVS::kMinAtten, + minAtten.fX, + minAtten.fY, + minAtten.fZ, + 0.f + ); + + + // Set up the ones passed in from the dynarippleVSmgr. + vShader->SetVector(plRipVS::kTexConsts, + ripConsts.fC1U, + ripConsts.fC2U, + ripConsts.fC1V, + ripConsts.fC2V); + + vShader->SetVector(plRipVS::kLifeConsts, + ripConsts.fInitAtten, + 0, // current time, we'll fill that in later + ripConsts.fLife, + 1.f / (ripConsts.fLife - ripConsts.fDecay)); + + plConst(hsScalar) kRipBias(0.1); + vShader->SetVector(plRipVS::kRampBias, + ripConsts.fRamp, + 1.f / ripConsts.fRamp, + kRipBias, + 0); // Last one still unused. + + vShader->SetNumPipeConsts(4); + vShader->SetPipeConst(0, plPipeConst::kWorldToNDC, plRipVS::kWorldToNDC); + vShader->SetPipeConst(1, plPipeConst::kCamPosWorld, plRipVS::kCameraPos); + vShader->SetPipeConst(2, plPipeConst::kLocalToWorld, plRipVS::kLocalToWorld); + vShader->SetPipeConst(3, plPipeConst::kFogSet, plRipVS::kFogSet); + +// vShader->SetShaderFileName("sha/vs_WaveRip.inl"); +// vShader->SetDecl(plShaderTable::Decl(vs_WaveRip)); + +// static const plShaderDecl decl("sha/vs_WaveRip7.inl"); +// vShader->SetDecl(&decl); + vShader->SetDecl(plShaderTable::Decl(vs_WaveRip7)); + + hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefRipVShader), plRefFlags::kActiveRef); + + hsAssert(vShader == fRipVShader, "Should have been set by SendRef"); + } + + IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kVertexShader, fRipVShader); + +} + +void plWaveSet7::IAddRipPixelShader(hsGMaterial* mat, const plRipVSConsts& ripConsts) +{ + if( !fRipPShader ) + { + plShader* pShader = TRACKED_NEW plShader; + char buff[256]; + sprintf(buff, "%s_RipPS", GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation()); + pShader->SetIsPixelShader(true); + + pShader->SetNumConsts(0); + + pShader->SetInputFormat(0); + pShader->SetOutputFormat(0); + +// pShader->SetShaderFileName("sha/ps_WaveRip.inl"); + pShader->SetDecl(plShaderTable::Decl(ps_WaveRip)); + + hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefRipPShader), plRefFlags::kActiveRef); + + hsAssert(pShader == fRipPShader, "Should have been set by SendRef"); + } + + IAddShaderToLayers(mat, 0, mat->GetNumLayers()-1, plLayRefMsg::kPixelShader, fRipPShader); + +} + +plShader* plWaveSet7::ICreateDecalVShader(DecalVType t) +{ + if( !fDecalVShaders[t] ) + { + static const char* fname[kNumDecalVShaders] = { + "vs_WaveDec1Lay_7", + "vs_WaveDec2Lay11_7", + "vs_WaveDec2Lay12_7", + "vs_WaveDecEnv_7" + }; + static const plShaderDecl shaderDecls[kNumDecalVShaders] = { + plShaderDecl("sha/vs_WaveDec1Lay_7.inl"), + plShaderDecl("sha/vs_WaveDec2Lay11_7.inl"), + plShaderDecl("sha/vs_WaveDec2Lay12_7.inl"), + plShaderDecl("sha/vs_WaveDecEnv_7.inl") + }; + + static const plShaderID::ID shaderIDs[kNumDecalVShaders] = { + vs_WaveDec1Lay_7, + vs_WaveDec2Lay11_7, + vs_WaveDec2Lay12_7, + vs_WaveDecEnv_7 + }; + static const int numUVWs[kNumDecalVShaders] = { + 1, + 1, + 2, + 3 + }; + static const int numLayXfms[kNumDecalVShaders] = { + 1, + 2, + 2, + 1 + }; + + + plShader* vShader = TRACKED_NEW plShader; + char buff[256]; + sprintf(buff, "%s_%s", GetKey()->GetName(), fname[t]); + hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation()); + vShader->SetIsPixelShader(false); + + vShader->SetInputFormat(numUVWs[t]); // This should really be one!!! + vShader->SetOutputFormat(0); + + vShader->SetNumConsts(plWaveDecVS::kNumConsts); + + vShader->SetVector(plWaveDecVS::kSinConsts, 1.f, -1.f/6.f, 1.f/120.f, -1.f/5040.f); + vShader->SetVector(plWaveDecVS::kCosConsts, 1.f, -1.f/2.f, 1.f/24.f, -1.f/720.f); + vShader->SetVector(plWaveDecVS::kPiConsts, 1.f / (8.f*hsScalarPI*4.f*4.f), hsScalarPI/2.f, hsScalarPI, hsScalarPI*2.f); + vShader->SetVector(plWaveDecVS::kNumericConsts, 0, 0.5f, 1.f, 2.f); + + hsVector3 waterOffset = State().fWaterOffset; + vShader->SetVector(plWaveDecVS::kWaterLevel, + State().fWaterHeight + waterOffset.fX, + State().fWaterHeight + waterOffset.fY, + State().fWaterHeight + waterOffset.fZ, + State().fWaterHeight); + + hsVector3 maxAtten = State().fMaxAtten; + hsVector3 minAtten = State().fMinAtten; + hsVector3 depthFalloff = State().fDepthFalloff; + vShader->SetVector(plWaveDecVS::kDepthFalloff, + (maxAtten.fX - minAtten.fX) / depthFalloff.fX, + (maxAtten.fY - minAtten.fY) / depthFalloff.fY, + (maxAtten.fZ - minAtten.fZ) / depthFalloff.fZ, + 1.f + ); + vShader->SetVector(plWaveDecVS::kMinAtten, + minAtten.fX, + minAtten.fY, + minAtten.fZ, + 0.f + ); + + plConst(hsScalar) kBias(0.1); + vShader->SetVector(plWaveDecVS::kBias, + kBias, + 0, + 0, + 0); // Last one still unused. + + const int kNumPipe = 5; + vShader->SetNumPipeConsts(kNumPipe + numLayXfms[t]); + vShader->SetPipeConst(0, plPipeConst::kWorldToNDC, plWaveDecVS::kWorldToNDC); + vShader->SetPipeConst(1, plPipeConst::kLocalToWorld, plWaveDecVS::kLocalToWorld); + vShader->SetPipeConst(2, plPipeConst::kLayRuntime, plWaveDecVS::kMatColor); + vShader->SetPipeConst(3, plPipeConst::kCamPosWorld, plWaveDecVS::kCameraPos); + vShader->SetPipeConst(4, plPipeConst::kFogSet, plWaveDecVS::kFogSet); + int i; + for( i = 0; i < numLayXfms[t]; i++ ) + { + vShader->SetPipeConst(kNumPipe + i, plPipeConst::Type(plPipeConst::kTex2x4_0+i), plWaveDecVS::kTex0Transform + i); + } + +// vShader->SetDecl(&shaderDecls[t]); + vShader->SetDecl(plShaderTable::Decl(shaderIDs[t])); + + hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, t, kRefDecVShader), plRefFlags::kActiveRef); + + hsAssert(vShader == fDecalVShaders[t], "Should have been set by SendRef"); + } + return fDecalVShaders[t]; +} + +plShader* plWaveSet7::IGetDecalVShader(hsGMaterial* mat) +{ + if( mat->GetLayer(0)->GetShadeFlags() & hsGMatState::kShadeEnvironMap ) + return ICreateDecalVShader(kDecalVEnv); + + switch( mat->GetNumLayers() ) + { + case 1: + return ICreateDecalVShader(kDecalV1Lay); + case 2: + hsAssert(mat->GetLayer(0)->GetUVWSrc() == 0, "First layer must use uvw channel 1"); + hsAssert(mat->GetLayer(1)->GetUVWSrc() < 2, "Second layer must use uvw channel 1 or 2"); + switch( mat->GetLayer(1)->GetUVWSrc() ) + { + case 0: + return ICreateDecalVShader(kDecalV2Lay11); + case 1: + return ICreateDecalVShader(kDecalV2Lay12); + default: + return nil; + } + break; + default: + hsAssert(false, "Only 1 or 2 layers currently supported"); + } + return nil; +} + +plShader* plWaveSet7::ICreateDecalPShader(DecalPType t) +{ + if( !fDecalPShaders[t] ) + { + static const char* fname[kNumDecalPShaders] = { + "ps_CbaseAbase", + "ps_CalphaAbase", + "ps_CalphaAMult", + "ps_CalphaAadd", + "ps_CaddAbase", + "ps_CaddAMult", + "ps_CaddAAdd", + "ps_CmultAbase", + "ps_CmultAMult", + "ps_CmultAAdd", + "ps_WaveDecEnv" + }; + static const plShaderID::ID shaderIDs[kNumDecalPShaders] = { + ps_CbaseAbase, + ps_CalphaAbase, + ps_CalphaAMult, + ps_CalphaAadd, + ps_CaddAbase, + ps_CaddAMult, + ps_CaddAAdd, + ps_CmultAbase, + ps_CmultAMult, + ps_CmultAAdd, + ps_WaveDecEnv + }; + + plShader* pShader = TRACKED_NEW plShader; + + char buff[256]; + sprintf(buff, "%s_%s", GetKey()->GetName(), fname[t]); + hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation()); + pShader->SetIsPixelShader(true); + +// sprintf(buff, "sha/%s.inl", fname[t]); +// pShader->SetShaderFileName(buff); + pShader->SetDecl(plShaderTable::Decl(shaderIDs[t])); + + hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, t, kRefDecPShader), plRefFlags::kActiveRef); + + hsAssert(fDecalPShaders[t] == pShader, "Should have been set by SendRef"); + } + return fDecalPShaders[t]; + +} + +plShader* plWaveSet7::IGetDecalPShader(hsGMaterial* mat) +{ + if( mat->GetLayer(0)->GetShadeFlags() & hsGMatState::kShadeEnvironMap ) + return ICreateDecalPShader(kDecalPEnv); + + hsAssert(mat->GetNumLayers() < 3, "Only 2 layers supported on water decal"); + if( mat->GetNumLayers() >= 3 ) + return nil; + + if( mat->GetNumLayers() == 1 ) + return ICreateDecalPShader(kDecalPBB); + + DecalPType t = kNumDecalPShaders; // Initialize to illegal value. + + switch( mat->GetLayer(1)->GetBlendFlags() & (hsGMatState::kBlendMask | hsGMatState::kBlendAlphaMult | hsGMatState::kBlendAlphaAdd) ) + { + case 0: + case hsGMatState::kBlendTest: + case hsGMatState::kBlendAlpha: + t = kDecalPaB; + break; + case hsGMatState::kBlendAdd: + t = kDecalPAB; + break; + case hsGMatState::kBlendMult: + t = kDecalPMB; + break; + case hsGMatState::kBlendAlpha | hsGMatState::kBlendAlphaAdd: + t = kDecalPaA; + break; + case hsGMatState::kBlendAdd | hsGMatState::kBlendAlphaAdd: + t = kDecalPAA; + break; + case hsGMatState::kBlendMult | hsGMatState::kBlendAlphaAdd: + t = kDecalPMA; + break; + case hsGMatState::kBlendAlpha | hsGMatState::kBlendAlphaMult: + t = kDecalPaM; + break; + case hsGMatState::kBlendAdd | hsGMatState::kBlendAlphaMult: + t = kDecalPAM; + break; + case hsGMatState::kBlendMult | hsGMatState::kBlendAlphaMult: + t = kDecalPMM; + break; + + default: + hsAssert(false, "Unsupported layer blend mode"); + return nil; + } + + return ICreateDecalPShader(t); +} + +void plWaveSet7::IUpdateShaders(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + IUpdateBiasVShader(); + + IUpdateBumpVShader(pipe, l2w, w2l); + + IUpdateBumpPShader(pipe, l2w, w2l); + + IUpdateRipPShader(pipe, l2w, w2l); + + IUpdateRipVShader(pipe, l2w, w2l); + + IUpdateShoreVShader(pipe, l2w, w2l); + + IUpdateFixedVShader(pipe, l2w, w2l); + + IUpdateFixedPShader(pipe, l2w, w2l); + + IUpdateDecVShaders(pipe, l2w, w2l); +} + +void plWaveSet7::IUpdateBumpPShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + plCONST(Int32) skip(0); + int i; + for( i = 0; i < kNumBumpShaders; i++ ) + { + + int j; + for( j = 0; j < kBumpPerPass; j++ ) + { + int iTex = i*kBumpPerPass + j; + + hsVector3 specVec = State().fSpecVec; + hsScalar scale = 1.f / (hsScalar(kNumBumpShaders) + specVec[State().kNoise]); + + hsScalar maxLen = TexState().fMaxLength * kCompositeSize / State().fRippleScale; + hsScalar rescale = fTexWaves[iTex].fLen / maxLen; + + hsScalar bias = 0.5f * scale; + fBumpPShader[i]->SetVector(plBumpPS::kHalfOne, scale, scale, 1.f, 1.f); + fBumpPShader[i]->SetVector(plBumpPS::kBias, bias, bias, 1.f, 1.f); + + hsScalar layScale = skip & (1 << iTex) ? 0.f : (1.f / hsScalar(kBumpPerPass)); + layScale *= fTexWaveFade[iTex]; + + fBumpPShader[i]->SetVector(plBumpPS::kWave0 + j, + -fTexWaves[iTex].fDirX * layScale, + -fTexWaves[iTex].fDirY * layScale, + 1.f, + 1.f); + } + } + +} + +void plWaveSet7::IUpdateBumpVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ +} + +static inline hsScalar IRound(hsScalar f) +{ + return hsScalar(int(f + (f > 0 ? 0.5f : -0.5f))); +} + +void plWaveSet7::IUpdateBiasVShader() +{ + + if( fBiasVShader ) + { + // Can't just use GetDelSysSeconds() or else we lose time if we skip a frame render because of high FPS. + hsScalar dt = fLastTime > 0 ? hsScalar(fCurrTime - fLastTime) : hsTimer::GetDelSysSeconds(); + plConst(hsScalar) kRate(-0.1f); + hsScalar tx = kRate * dt; + hsScalar ty = kRate * dt; + plConst(hsScalar) kScaleU(4.f); + plConst(hsScalar) kScaleV(1.f); + tx += fBiasVShader->GetFloat(plBiasVS::kTexU0, 3); + tx -= hsScalar(int(tx)); + + ty += fBiasVShader->GetFloat(plBiasVS::kTexV0, 3); + ty -= hsScalar(int(ty)); + + hsScalar scale = 1.f + (4.f - 1.f) * TexState().fAngleDev/hsScalarPI; + + hsScalar m00 = IRound(fWindDir.fY * scale); + hsScalar m01 = IRound(fWindDir.fX * scale); + hsScalar m10 = IRound(-fWindDir.fX * 4.f); + hsScalar m11 = IRound(fWindDir.fY * 4.f); + + fBiasVShader->SetVector(plBiasVS::kTexU0, + m00, + m01, + 0, + tx); + fBiasVShader->SetVector(plBiasVS::kTexV0, + m10, + m11, + 0, + ty); + + plConst(hsScalar) kUpperNoiseOffU(0.f); + plConst(hsScalar) kUpperNoiseOffV(0.3f); + fBiasVShader->SetVector(plBiasVS::kTexU1, + m00, + m01, + 0, + -tx + kUpperNoiseOffU); + fBiasVShader->SetVector(plBiasVS::kTexV1, + m10, + m11, + 0, + ty + kUpperNoiseOffV); + + hsVector3 specVec = State().fSpecVec; + hsScalar biasScale = 0.5f * specVec[State().kNoise] / (hsScalar(kNumBumpShaders) + specVec[State().kNoise]); + fBiasVShader->SetVector(plBiasVS::kScaleBias, + biasScale, + biasScale, + 0.f, + 1.f); + } +} + +void plWaveSet7::IUpdateFixedPShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ +} + +void plWaveSet7::IUpdateRipPShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + // Nothing to do +} + +void plWaveSet7::IUpdateRipVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + if( fRipVShader ) + { + fRipVShader->SetVector(plRipVS::kFrequency, + fWorldWaves[0].fFreq, + fWorldWaves[1].fFreq, + fWorldWaves[2].fFreq, + fWorldWaves[3].fFreq); + + fRipVShader->SetVector(plRipVS::kPhase, + fWorldWaves[0].fPhase, + fWorldWaves[1].fPhase, + fWorldWaves[2].fPhase, + fWorldWaves[3].fPhase); + + fRipVShader->SetVector(plRipVS::kAmplitude, + fWorldWaves[0].fAmplitude, + fWorldWaves[1].fAmplitude, + fWorldWaves[2].fAmplitude, + fWorldWaves[3].fAmplitude); + + fRipVShader->SetVector(plRipVS::kDirectionX, + fWorldWaves[0].fDir.fX, + fWorldWaves[1].fDir.fX, + fWorldWaves[2].fDir.fX, + fWorldWaves[3].fDir.fX); + + fRipVShader->SetVector(plRipVS::kDirectionY, + fWorldWaves[0].fDir.fY, + fWorldWaves[1].fDir.fY, + fWorldWaves[2].fDir.fY, + fWorldWaves[3].fDir.fY); + + fRipVShader->SetVector(plRipVS::kWindRot, + fWindDir.fY, + -fWindDir.fX, + fWindDir.fX, + 0); + + fRipVShader->SetVector(plRipVS::kLengths, + fWorldWaves[0].fLength, + fWorldWaves[1].fLength, + fWorldWaves[2].fLength, + fWorldWaves[3].fLength); + + fRipVShader->SetFloat(plRipVS::kLifeConsts, 1, float(hsTimer::GetSysSeconds())); + float normQ[kNumWaves]; + int i; + for( i = 0; i < kNumWaves; i++ ) + { + normQ[i] = GeoState().fChop / (2.f*hsScalarPI * GeoState().fAmpOverLen * kNumWaves); + } + + fRipVShader->SetVector(plRipVS::kQADirX, + fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fX * normQ[0], + fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fX * normQ[1], + fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fX * normQ[2], + fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fX * normQ[3]); + + fRipVShader->SetVector(plRipVS::kQADirY, + fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fY * normQ[0], + fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fY * normQ[1], + fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fY * normQ[2], + fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fY * normQ[3]); + + } +} + +void plWaveSet7::IUpdateDecVShaders(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + int i; + for( i = 0; i < kNumDecalVShaders; i++ ) + IUpdateDecVShader(i, pipe); +} + +void plWaveSet7::IUpdateDecVShader(int t, plPipeline* pipe) +{ + plShader* shader = fDecalVShaders[t]; + if( shader ) + { + shader->SetVector(plWaveDecVS::kFrequency, + fWorldWaves[0].fFreq, + fWorldWaves[1].fFreq, + fWorldWaves[2].fFreq, + fWorldWaves[3].fFreq); + + shader->SetVector(plWaveDecVS::kPhase, + fWorldWaves[0].fPhase, + fWorldWaves[1].fPhase, + fWorldWaves[2].fPhase, + fWorldWaves[3].fPhase); + + shader->SetVector(plWaveDecVS::kAmplitude, + fWorldWaves[0].fAmplitude, + fWorldWaves[1].fAmplitude, + fWorldWaves[2].fAmplitude, + fWorldWaves[3].fAmplitude); + + shader->SetVector(plWaveDecVS::kDirectionX, + fWorldWaves[0].fDir.fX, + fWorldWaves[1].fDir.fX, + fWorldWaves[2].fDir.fX, + fWorldWaves[3].fDir.fX); + + shader->SetVector(plWaveDecVS::kDirectionY, + fWorldWaves[0].fDir.fY, + fWorldWaves[1].fDir.fY, + fWorldWaves[2].fDir.fY, + fWorldWaves[3].fDir.fY); + + shader->SetVector(plWaveDecVS::kLengths, + fWorldWaves[0].fLength, + fWorldWaves[1].fLength, + fWorldWaves[2].fLength, + fWorldWaves[3].fLength); + + if( t == kDecalVEnv ) + { + hsPoint3 worldCam = pipe->GetViewTransform().GetCameraToWorld().GetTranslate(); + + hsPoint3 envCenter(State().fEnvCenter); + + hsScalar envRadius = State().fEnvRadius; + + hsVector3 camToCen(&envCenter, &worldCam); + hsScalar G = camToCen.MagnitudeSquared() - envRadius * envRadius; + shader->SetVectorW(plWaveDecVS::kEnvAdjust, camToCen, G); + + } + + float normQ[kNumWaves]; + int i; + for( i = 0; i < kNumWaves; i++ ) + { + normQ[i] = GeoState().fChop / (2.f*hsScalarPI * GeoState().fAmpOverLen * kNumWaves); + } + + shader->SetVector(plWaveDecVS::kQADirX, + fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fX * normQ[0], + fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fX * normQ[1], + fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fX * normQ[2], + fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fX * normQ[3]); + + shader->SetVector(plWaveDecVS::kQADirY, + fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fY * normQ[0], + fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fY * normQ[1], + fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fY * normQ[2], + fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fY * normQ[3]); + + shader->SetVector(plWaveDecVS::kDirXW, + fWorldWaves[0].fDir.fX * fWorldWaves[0].fFreq, + fWorldWaves[1].fDir.fX * fWorldWaves[1].fFreq, + fWorldWaves[2].fDir.fX * fWorldWaves[2].fFreq, + fWorldWaves[3].fDir.fX * fWorldWaves[3].fFreq); + + shader->SetVector(plWaveDecVS::kDirYW, + fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq, + fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq, + fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq, + fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq); + + shader->SetVector(plWaveDecVS::kWK, + normQ[0] * fWorldWaves[0].fFreq, + normQ[1] * fWorldWaves[1].fFreq, + normQ[2] * fWorldWaves[2].fFreq, + normQ[3] * fWorldWaves[3].fFreq); + + shader->SetVector(plWaveDecVS::kDirXSqKW, + fWorldWaves[0].fDir.fX * fWorldWaves[0].fDir.fX * fWorldWaves[0].fFreq * normQ[0], + fWorldWaves[1].fDir.fX * fWorldWaves[1].fDir.fX * fWorldWaves[1].fFreq * normQ[1], + fWorldWaves[2].fDir.fX * fWorldWaves[2].fDir.fX * fWorldWaves[2].fFreq * normQ[2], + fWorldWaves[3].fDir.fX * fWorldWaves[3].fDir.fX * fWorldWaves[3].fFreq * normQ[3]); + + shader->SetVector(plWaveDecVS::kDirXDirYKW, + fWorldWaves[0].fDir.fX * fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq * normQ[0], + fWorldWaves[1].fDir.fX * fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq * normQ[1], + fWorldWaves[2].fDir.fX * fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq * normQ[2], + fWorldWaves[3].fDir.fX * fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq * normQ[3]); + + shader->SetVector(plWaveDecVS::kDirYSqKW, + fWorldWaves[0].fDir.fY * fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq * normQ[0], + fWorldWaves[1].fDir.fY * fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq * normQ[1], + fWorldWaves[2].fDir.fY * fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq * normQ[2], + fWorldWaves[3].fDir.fY * fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq * normQ[3]); + + } +} + +void plWaveSet7::IUpdateShoreVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + if( fShoreVShader ) + { + + fShoreVShader->SetVector(plShoreVS::kFrequency, + fWorldWaves[0].fFreq, + fWorldWaves[1].fFreq, + fWorldWaves[2].fFreq, + fWorldWaves[3].fFreq); + + fShoreVShader->SetVector(plShoreVS::kPhase, + fWorldWaves[0].fPhase, + fWorldWaves[1].fPhase, + fWorldWaves[2].fPhase, + fWorldWaves[3].fPhase); + + fShoreVShader->SetVector(plShoreVS::kAmplitude, + fWorldWaves[0].fAmplitude, + fWorldWaves[1].fAmplitude, + fWorldWaves[2].fAmplitude, + fWorldWaves[3].fAmplitude); + + fShoreVShader->SetVector(plShoreVS::kDirectionX, + fWorldWaves[0].fDir.fX, + fWorldWaves[1].fDir.fX, + fWorldWaves[2].fDir.fX, + fWorldWaves[3].fDir.fX); + + fShoreVShader->SetVector(plShoreVS::kDirectionY, + fWorldWaves[0].fDir.fY, + fWorldWaves[1].fDir.fY, + fWorldWaves[2].fDir.fY, + fWorldWaves[3].fDir.fY); + + hsVector3 waterOffset = State().fWaterOffset; + fShoreVShader->SetVector(plShoreVS::kWaterLevel, + State().fWaterHeight + waterOffset.fX, + State().fWaterHeight + waterOffset.fY, + State().fWaterHeight + waterOffset.fZ, + State().fWaterHeight); + + hsVector3 maxAtten = State().fMaxAtten; + hsVector3 minAtten = State().fMinAtten; + hsVector3 depthFalloff = State().fDepthFalloff; + fShoreVShader->SetVector(plShoreVS::kDepthFalloff, + (maxAtten.fX - minAtten.fX) / depthFalloff.fX, + (maxAtten.fY - minAtten.fY) / depthFalloff.fY, + (maxAtten.fZ - minAtten.fZ) / depthFalloff.fZ, + 1.f + ); + fShoreVShader->SetVector(plShoreVS::kMinAtten, + minAtten.fX, + minAtten.fY, + minAtten.fZ, + 0.f + ); + + fShoreVShader->SetVector(plShoreVS::kLengths, + fWorldWaves[0].fLength, + fWorldWaves[1].fLength, + fWorldWaves[2].fLength, + fWorldWaves[3].fLength); + + plConst(hsScalar) kK1(2.f); + plConst(hsScalar) kK2(5.f); + hsScalar negK1OverK2Sq = -kK1 / (kK2 * kK2); + fShoreVShader->SetVector(plShoreVS::kIncline, negK1OverK2Sq, kK1, 0.f, 0.f); + + float normQ[kNumWaves]; + int i; + for( i = 0; i < kNumWaves; i++ ) + { + normQ[i] = GeoState().fChop / (2.f*hsScalarPI * GeoState().fAmpOverLen * kNumWaves); + } + + fShoreVShader->SetVector(plShoreVS::kQADirX, + fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fX * normQ[0], + fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fX * normQ[1], + fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fX * normQ[2], + fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fX * normQ[3]); + + fShoreVShader->SetVector(plShoreVS::kQADirY, + fWorldWaves[0].fAmplitude * fWorldWaves[0].fDir.fY * normQ[0], + fWorldWaves[1].fAmplitude * fWorldWaves[1].fDir.fY * normQ[1], + fWorldWaves[2].fAmplitude * fWorldWaves[2].fDir.fY * normQ[2], + fWorldWaves[3].fAmplitude * fWorldWaves[3].fDir.fY * normQ[3]); + + + if( fTrialUpdate & kRemakeBubble ) + IRefillBubbleShoreTex(); + if( fTrialUpdate & kRemakeEdge ) + IRefillEdgeShoreTex(); + } +} + +void plWaveSet7::IUpdateFixedVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + if( fFixedVShader ) + { + fFixedVShader->SetVector(plFixedVS7::kFrequency, + fWorldWaves[0].fFreq, + fWorldWaves[1].fFreq, + fWorldWaves[2].fFreq, + fWorldWaves[3].fFreq); + + fFixedVShader->SetVector(plFixedVS7::kPhase, + fWorldWaves[0].fPhase, + fWorldWaves[1].fPhase, + fWorldWaves[2].fPhase, + fWorldWaves[3].fPhase); + + fFixedVShader->SetVector(plFixedVS7::kAmplitude, + fWorldWaves[0].fAmplitude, + fWorldWaves[1].fAmplitude, + fWorldWaves[2].fAmplitude, + fWorldWaves[3].fAmplitude); + + fFixedVShader->SetVector(plFixedVS7::kDirectionX, + fWorldWaves[0].fDir.fX, + fWorldWaves[1].fDir.fX, + fWorldWaves[2].fDir.fX, + fWorldWaves[3].fDir.fX); + + fFixedVShader->SetVector(plFixedVS7::kDirectionY, + fWorldWaves[0].fDir.fY, + fWorldWaves[1].fDir.fY, + fWorldWaves[2].fDir.fY, + fWorldWaves[3].fDir.fY); + + plCONST(hsScalar) kEnvRadius(500.f); + hsScalar envRadius = State().fEnvRadius; + + hsPoint3 worldCam = pipe->GetViewTransform().GetCameraToWorld().GetTranslate(); + + hsPoint3 envCenter(State().fEnvCenter); + + hsVector3 camToCen(&envCenter, &worldCam); + hsScalar G = camToCen.MagnitudeSquared() - envRadius * envRadius; + fFixedVShader->SetVectorW(plFixedVS7::kEnvAdjust, camToCen, G); + + hsScalar texScale = 1.f / State().fRippleScale; + + fFixedVShader->SetVector(plFixedVS7::kUVScale, + texScale, + 0, + 0, + 0); + + hsScalar specAtten = State().fTexState.fAmpOverLen * hsScalarPI * 2.f; + + plCONST(hsScalar) kScaleHack(0.1f); + hsScalar baseScale = kScaleHack; +// baseScale *= hsScalar(kBumpPerPass) * (hsScalar(kNumBumpShaders) + State().fSpecVec[State().kNoise]); + // Not sure what's right here. but we are currently scaling down by 1/(numBumpShaders + noise), + // so I guess we want to scale up by that amount here. Not sure we shouldn't figuring in bumpperpass + // on both, but at least now we're consistent. + hsVector3 specVec = State().fSpecVec; + baseScale *= (hsScalar(kNumBumpShaders) + specVec[State().kNoise]); + baseScale *= (TexState().fChop + 1.f); + + + hsScalar specStart = specVec[State().kSpecStart]; + hsScalar specEnd = specVec[State().kSpecEnd]; + if( specStart > specEnd ) + specEnd = specStart + 1.f; + fFixedVShader->SetVector(plFixedVS7::kSpecAtten, + -specEnd, + 1.f / (specStart - specEnd), + baseScale * specAtten, + 0.f); + + hsColorRGBA envTint = State().fSpecularTint; + if( fFixedLayers[0] && (fFixedLayers[0]->GetShadeFlags() & hsGMatState::kShadeSpecular) ) + { + envTint *= fFixedLayers[0]->GetSpecularColor(); + } + fFixedVShader->SetColor(plFixedVS::kEnvTint, envTint); + + fFixedVShader->SetVector(plFixedVS7::kWindRot, + fWindDir.fY, + -fWindDir.fX, + fWindDir.fX, + 0); + + fFixedVShader->SetVector(plFixedVS7::kLengths, + fWorldWaves[0].fLength, + fWorldWaves[1].fLength, + fWorldWaves[2].fLength, + fWorldWaves[3].fLength); + + // These don't change often, but they can change. We're going + // to be uploading them to the card anyway, might as well refresh + // the sysmem shader to be safe. + hsVector3 waterOffset = State().fWaterOffset; + fFixedVShader->SetVector(plFixedVS7::kWaterLevel, + State().fWaterHeight + waterOffset.fX, + State().fWaterHeight + waterOffset.fY, + State().fWaterHeight + waterOffset.fZ, + State().fWaterHeight); + + hsVector3 maxAtten = State().fMaxAtten; + hsVector3 minAtten = State().fMinAtten; + hsVector3 depthFalloff = State().fDepthFalloff; + fFixedVShader->SetVector(plFixedVS7::kDepthFalloff, + (maxAtten.fX - minAtten.fX) / depthFalloff.fX, + (maxAtten.fY - minAtten.fY) / depthFalloff.fY, + (maxAtten.fZ - minAtten.fZ) / depthFalloff.fZ, + 1.f + ); + fFixedVShader->SetVector(plFixedVS7::kMinAtten, + minAtten.fX, + minAtten.fY, + minAtten.fZ, + 0.f + ); + + float normQ[kNumWaves]; + int i; + for( i = 0; i < kNumWaves; i++ ) + { + normQ[i] = GeoState().fChop / (2.f*hsScalarPI * GeoState().fAmpOverLen * kNumWaves); + } + + fFixedVShader->SetVector(plFixedVS7::kDirXK, + fWorldWaves[0].fDir.fX * normQ[0], + fWorldWaves[1].fDir.fX * normQ[1], + fWorldWaves[2].fDir.fX * normQ[2], + fWorldWaves[3].fDir.fX * normQ[3]); + + fFixedVShader->SetVector(plFixedVS7::kDirYK, + fWorldWaves[0].fDir.fY * normQ[0], + fWorldWaves[1].fDir.fY * normQ[1], + fWorldWaves[2].fDir.fY * normQ[2], + fWorldWaves[3].fDir.fY * normQ[3]); + + fFixedVShader->SetVector(plFixedVS7::kDirXW, + fWorldWaves[0].fDir.fX * fWorldWaves[0].fFreq, + fWorldWaves[1].fDir.fX * fWorldWaves[1].fFreq, + fWorldWaves[2].fDir.fX * fWorldWaves[2].fFreq, + fWorldWaves[3].fDir.fX * fWorldWaves[3].fFreq); + + fFixedVShader->SetVector(plFixedVS7::kDirYW, + fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq, + fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq, + fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq, + fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq); + + fFixedVShader->SetVector(plFixedVS7::kWK, + normQ[0] * fWorldWaves[0].fFreq, + normQ[1] * fWorldWaves[1].fFreq, + normQ[2] * fWorldWaves[2].fFreq, + normQ[3] * fWorldWaves[3].fFreq); + + fFixedVShader->SetVector(plFixedVS7::kDirXSqKW, + fWorldWaves[0].fDir.fX * fWorldWaves[0].fDir.fX * fWorldWaves[0].fFreq * normQ[0], + fWorldWaves[1].fDir.fX * fWorldWaves[1].fDir.fX * fWorldWaves[1].fFreq * normQ[1], + fWorldWaves[2].fDir.fX * fWorldWaves[2].fDir.fX * fWorldWaves[2].fFreq * normQ[2], + fWorldWaves[3].fDir.fX * fWorldWaves[3].fDir.fX * fWorldWaves[3].fFreq * normQ[3]); + + fFixedVShader->SetVector(plFixedVS7::kDirXDirYKW, + fWorldWaves[0].fDir.fX * fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq * normQ[0], + fWorldWaves[1].fDir.fX * fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq * normQ[1], + fWorldWaves[2].fDir.fX * fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq * normQ[2], + fWorldWaves[3].fDir.fX * fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq * normQ[3]); + + fFixedVShader->SetVector(plFixedVS7::kDirYSqKW, + fWorldWaves[0].fDir.fY * fWorldWaves[0].fDir.fY * fWorldWaves[0].fFreq * normQ[0], + fWorldWaves[1].fDir.fY * fWorldWaves[1].fDir.fY * fWorldWaves[1].fFreq * normQ[1], + fWorldWaves[2].fDir.fY * fWorldWaves[2].fDir.fY * fWorldWaves[2].fFreq * normQ[2], + fWorldWaves[3].fDir.fY * fWorldWaves[3].fDir.fY * fWorldWaves[3].fFreq * normQ[3]); + + + } +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +void plWaveSet7::ICheckShoreMaterials() +{ + int i; + for( i = 0; i < fShores.GetCount(); i++ ) + ICheckShoreMaterial(fShores[i]); +} + +void plWaveSet7::ICheckShoreMaterial(plSceneObject* so) +{ + if( !so ) + return; + + const plDrawInterface* di = so->GetDrawInterface(); + if( !di ) + return; + + hsTArray src; + plAccessGeometry::Instance()->OpenRO(di, src); + + if( !src.GetCount() ) + return; + + int i; + for( i = 0; i < src.GetCount(); i++ ) + { + ISetupGraphShore(src[i].GetMaterial()); + } + + plAccessGeometry::Instance()->Close(src); + +} + +void plWaveSet7::ICheckDecalMaterials() +{ + int i; + for( i = 0; i < fDecals.GetCount(); i++ ) + ICheckDecalMaterial(fDecals[i]); +} + +void plWaveSet7::ICheckDecalMaterial(plSceneObject* so) +{ + if( !so ) + return; + + const plDrawInterface* di = so->GetDrawInterface(); + if( !di ) + return; + + hsTArray src; + plAccessGeometry::Instance()->OpenRO(di, src); + + if( !src.GetCount() ) + return; + + int i; + for( i = 0; i < src.GetCount(); i++ ) + { + ISetupDecal(src[i].GetMaterial()); + } + + plAccessGeometry::Instance()->Close(src); + +} + +void plWaveSet7::ICheckDecalEnvLayers(hsGMaterial* mat) +{ + // Mat needs to be set up as: + // Lay0 = bumpmap - uv is lookup into bumpmap + // Lay1 = bumpmap - uv is col0 of tangent2local + // Lay2 = bumpmap - uv is col1 of tangent2local + // Lay3 = fEnvMap - uv is col2 of tangent2local, except we'll ignore it and use the normal instead + // Note we'll have to transpose that as part of creating tangent2world + // Easiest way to check whether we've done this one already is checking whether + // Lay3 == fEnvMap. + + // If we haven't done this already + if( !fDecalVShaders[kDecalVEnv] || (mat->GetLayer(0)->GetVertexShader() != fDecalVShaders[kDecalVEnv]) ) + { + plLayer* lay3 = nil; + + plMatRefMsg* refMsg; + + const int numLayers = mat->GetNumLayers(); + int i; + for( i = numLayers-1; i >= 0; i-- ) + { + plLayer* lay0 = plLayer::ConvertNoRef(mat->GetLayer(i)->BottomOfStack()); + lay0->SetBlendFlags(hsGMatState::kBlendAddColorTimesAlpha); +// lay0->SetBlendFlags(hsGMatState::kBlendAlpha); + lay0->SetMiscFlags(lay0->GetMiscFlags() | hsGMatState::kMiscRestartPassHere); + + // Repeat lay3 as i+1, i+2 and i+3 + // set base blend to timealphaadd + + // If we are just creating lay3, then by creating it we've added (appended) it to mat. + // Otherwise, we need to add it as i+1. + if( !lay3 ) + { + lay3 = ICreateTotalEnvLayer(fEnvMap, mat, 3, "Dec"); + lay3->SetBlendFlags(hsGMatState::kBlendAlpha); + } + else + { + refMsg = TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnRequest, i+1, plMatRefMsg::kLayer | plMatRefMsg::kInsert); + hsgResMgr::ResMgr()->SendRef(lay3->GetKey(), refMsg, plRefFlags::kActiveRef); + } + + refMsg = TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnRequest, i+2, plMatRefMsg::kLayer | plMatRefMsg::kInsert); + hsgResMgr::ResMgr()->SendRef(lay3->GetKey(), refMsg, plRefFlags::kActiveRef); + + refMsg = TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnRequest, i+3, plMatRefMsg::kLayer | plMatRefMsg::kInsert); + hsgResMgr::ResMgr()->SendRef(lay3->GetKey(), refMsg, plRefFlags::kActiveRef); + } + } +} + +void plWaveSet7::ISetupDecal(hsGMaterial* mat) +{ + if( mat->GetLayer(0)->GetShadeFlags() & hsGMatState::kShadeEnvironMap ) + { + ICheckDecalEnvLayers(mat); + } + + plShader* vShader = IGetDecalVShader(mat); + if( mat->GetLayer(0)->GetVertexShader() != vShader ) + IAddShaderToLayers(mat, 0, -1, plLayRefMsg::kVertexShader, vShader); + + int i; + for( i = 0; i < mat->GetNumLayers(); i++ ) + { + plLayer* lay = plLayer::ConvertNoRef(mat->GetLayer(i)->BottomOfStack()); + if( lay ) + lay->SetZFlags(lay->GetZFlags() | hsGMatState::kZNoZWrite | hsGMatState::kZIncLayer); + } + + // The FFP is actually quite adequate for the required blends, + // but may not stay that way (if we allow fancier blends and what not). + // So we can either use the Pixel Shaders or FFP, depending on this define. +// #define MF_USE_FFP +#ifndef MF_USE_FFP + plShader* pShader = IGetDecalPShader(mat); + if( mat->GetLayer(0)->GetPixelShader() != pShader ) + IAddShaderToLayers(mat, 0, -1, plLayRefMsg::kPixelShader, pShader); +#endif // MF_USE_FFP +} + +void plWaveSet7::AddShoreTest(plKey& key) +{ + hsgResMgr::ResMgr()->SendRef(key, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefShore), plRefFlags::kPassiveRef); + + plSceneObject* so = plSceneObject::ConvertNoRef(key->ObjectIsLoaded()); + ICheckShoreMaterial(so); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////// +hsBool plWaveSet7::SetupRippleMat(hsGMaterial* mat, const plRipVSConsts& ripConsts) +{ + // We'll assume that if we set the vertexshader, we set the pixelshader too. + if( fRipVShader && (mat->GetLayer(0)->GetVertexShader() == fRipVShader) ) + return true; + + int i; + for( i = 0; i < mat->GetNumLayers(); i++ ) + { + plLayer* lay = plLayer::ConvertNoRef(mat->GetLayer(i)->BottomOfStack()); + if( lay ) + lay->SetZFlags(lay->GetZFlags() | hsGMatState::kZNoZWrite | hsGMatState::kZIncLayer); + } + + IAddRipVertexShader(mat, ripConsts); + IAddRipPixelShader(mat, ripConsts); + + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////// + +plDrawableSpans* plWaveSet7::ICreateGraphDrawable(plDrawableSpans* drawable, hsGMaterial* mat, int nWid) +{ +// drawable->SetNativeProperty(plDrawable::kPropVolatile, true); + + const int nVerts = nWid * 2; + + hsTArray pos; + hsTArray norm; + + pos.SetCount(nVerts); + norm.SetCount(nVerts); + +#ifdef TEST_UVWS + hsTArray uvw; // Are we actually ever going to use these uvws? + uvw.SetCount(nVerts); +#endif // TEST_UVWS + + int i; + for( i = 0; i < nWid; i++ ) + { + int iDn = i << 1; + int iUp = iDn + 1; + + hsScalar delX = hsScalar(i) / hsScalar(nWid-1); + + pos[iDn].fX = delX * 2.f - 1.f; + pos[iDn].fY = -1.f; + pos[iDn].fZ = 0.5f; + + norm[iDn].Set(0.f, 0.f, 1.f); + +#ifdef TEST_UVWS + uvw[iDn].fX = delX; + uvw[iDn].fY = 0.f; + uvw[iDn].fZ = 0.f; +#endif // TEST_UVWS + + pos[iUp].fX = delX * 2.f - 1.f; + pos[iUp].fY = 1.f; + pos[iUp].fZ = 0.5f; + + norm[iUp].Set(1.f, 0.f, 1.f); + +#ifdef TEST_UVWS + uvw[iUp].fX = delX; + uvw[iUp].fY = 1.f; + uvw[iUp].fZ = 0.f; +#endif // TEST_UVWS + } + + const int nTris = (nWid-1) * 2; + + hsTArray idxArr; + idxArr.SetCount(nTris * 3); + + UInt16* idx = idxArr.AcquireArray(); + + int iBase = 0; + for( i = 0; i < nTris; i += 2 ) + { + *idx++ = i; + *idx++ = i + 2; + *idx++ = i + 1; + + *idx++ = i + 1; + *idx++ = i + 2; + *idx++ = i + 3; + } + + + plDrawableGenerator::GenerateDrawable( nVerts, pos.AcquireArray(), norm.AcquireArray(), +#ifndef TEST_UVWS + nil, 0, +#else // TEST_UVWS + uvw.AcquireArray(), 1, +#endif // TEST_UVWS + nil, false, nil, + nTris * 3, idxArr.AcquireArray(), + mat, + hsMatrix44::IdentityMatrix(), + false, + nil, + drawable); + + return drawable; +} + +plDrawableSpans* plWaveSet7::ICreateEmptyGraphDrawable(const char* name, UInt32 ref, int which) +{ + plDrawableSpans* drawable = TRACKED_NEW plDrawableSpans; + char buff[256]; + sprintf(buff, "%s_%s_%d", GetKey()->GetName(), name, which); + hsgResMgr::ResMgr()->NewKey(buff, drawable, GetKey()->GetUoid().GetLocation()); + + hsgResMgr::ResMgr()->SendRef(drawable->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, which, (Int8)ref), plRefFlags::kActiveRef); + + return drawable; +} + +hsGMaterial* plWaveSet7::ICreateEmptyMaterial(const char* name, UInt32 ref, int which) +{ + hsGMaterial* mat = TRACKED_NEW hsGMaterial; + + char buff[256]; + sprintf(buff, "%s_%s_%d", GetKey()->GetName(), name, which); + hsgResMgr::ResMgr()->NewKey(buff, mat, GetKey()->GetUoid().GetLocation()); + + hsgResMgr::ResMgr()->SendRef(mat->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, which, (Int8)ref), plRefFlags::kActiveRef); + + return mat; +} + +plLayer* plWaveSet7::ICreateBlankLayer(const char* name, int suff) +{ + plLayer* lay = TRACKED_NEW plLayer; + char buff[256]; + sprintf(buff, "%s_%s_%d", GetKey()->GetName(), name, suff); + hsgResMgr::ResMgr()->NewKey(buff, lay, GetKey()->GetUoid().GetLocation()); + + return lay; +} + +plMipmap* plWaveSet7::ICreateBlankTex(const char* name, int width, int height, UInt32 ref) +{ + plMipmap* mipMap = TRACKED_NEW plMipmap( + width, height, + plMipmap::kARGB32Config, + 1, + plMipmap::kUncompressed, + plMipmap::UncompressedInfo::kRGB8888); + + char buff[256]; + sprintf(buff, "%s_%s", GetKey()->GetName(), name); + hsgResMgr::ResMgr()->NewKey(buff, mipMap, GetKey()->GetUoid().GetLocation()); + + hsgResMgr::ResMgr()->SendRef(mipMap->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, (Int8)ref), plRefFlags::kActiveRef); + + return mipMap; +} + +plMipmap* plWaveSet7::ICreateGraphShoreTex(int width, int height) +{ + // GraphShoreLayer has a texture with white color (possibly noised), + // and a solid alpha. + // Alpha is constant along U (width). + // Alpha is 0 at base, + // ramps up to opaque + // then drops off sharply to transparent just before the top. + + // If we haven't already made one... + if( !fGraphShoreTex ) + { + plMipmap* mipMap = ICreateBlankTex("Graph", width, height, kRefGraphShoreTex); + + plConst(hsScalar) kRampFrac(0.4f); + plConst(hsScalar) kTruncFrac(0.8f); + const int rampEnd = int(kRampFrac * height + 0.5f); + int truncEnd = int(kTruncFrac * height); + if( truncEnd >= (height-1) ) + truncEnd = height-2; + int j; + for( j = 0; j < height; j++ ) + { + UInt32 alpha = 255; + plConst(int) kRampStart(4); + if( j <= kRampStart ) + { + alpha = 0; + } + else + if( j - kRampStart < rampEnd ) + { + alpha = ((j-kRampStart) * 255) / rampEnd; + } + else if( j > truncEnd ) + { + alpha = 0; + } + UInt32 color = (alpha << 24) + | (0xff << 16) + | (0xff << 8) + | 0xff; + + int i; + for( i = 0; i < width; i++ ) + { + UInt32* val = mipMap->GetAddr32(i, j); + *val = color; + } + } + } + + return fGraphShoreTex; +} + +void plWaveSet7::IRefillBubbleShoreTex() +{ + plMipmap* mipMap = fBubbleShoreTex; + hsAssert(mipMap, "Refilling a non-existent bubble texture"); + + const int width = mipMap->GetWidth(); + const int height = mipMap->GetHeight(); + + // Initialize to white opaque. + memset(mipMap->GetAddr32(0,0), 0xff, width*height*sizeof(UInt32)); + + plConst(int) kMinNumBub(1024); + plConst(int) kMaxNumBub(6000); + const int kNumBub = (int)(kMinNumBub + State().fWispiness * (kMaxNumBub - kMinNumBub)); + int k; + for( k = 0; k < kNumBub; k++ ) + { + // Select a random location. + int iLoc = (int)(fRand.RandZeroToOne() * width); + int jLoc = (int)(fRand.RandZeroToOne() * height); + + // Select a random radius + plConst(hsScalar) kMinRad(2.f); + plConst(hsScalar) kMaxRad(5.0f); + int radius = int(kMinRad + fRand.RandZeroToOne() * (kMaxRad - kMinRad)); + hsScalar invRadiusSq = 1.f / hsScalar(radius*radius); + + // Carve out a hole. + int j; + for( j = -radius; j < radius; j++ ) + { + int jj = jLoc + j; + if( jj < 0 ) + jj += height; + else if( jj >= height ) + jj -= height; + + int i; + for( i = -radius; i < radius; i++ ) + { + int ii = iLoc + i; + if( ii < 0 ) + ii += width; + else if( ii >= width ) + ii -= width; + + hsScalar f = hsScalar(i*i + j*j) * invRadiusSq; + if( f > 1.f ) + f = 1.f; + plConst(hsScalar) kMinAlpha(0.8f); + plConst(hsScalar) kMaxAlpha(1.f); + f *= (kMaxAlpha - kMinAlpha); + f += kMinAlpha; + + UInt32* val = mipMap->GetAddr32(ii, jj); + UInt32 alpha = (*val) >> 24; + alpha = UInt32(hsScalar(alpha) * f); + *val &= 0x00ffffff; + *val |= (alpha << 24); + } + } + } + + const hsColorRGBA maxColor = State().fMaxColor; + const hsColorRGBA minColor = State().fMinColor; + int j; + for( j = 0; j < height; j++ ) + { + int i; + for( i = 0; i < width; i++ ) + { + UInt32* val = mipMap->GetAddr32(i, j); + hsColorRGBA col; + col.FromARGB32(*val); + hsScalar alpha = col.a; + col = maxColor - minColor; + col *= alpha; + col += minColor; + + *val = col.ToARGB32(); + + } + } + mipMap->MakeDirty(); + + fTrialUpdate &= ~kRemakeBubble; +} + +plMipmap* plWaveSet7::ICreateBubbleShoreTex(int width, int height) +{ + // Bubble layer is white in color (or noised). + // Alpha is just, well, random bubbles. + // Tile in U and V + + // If we haven't already made one... + if( !fBubbleShoreTex ) + { + plMipmap* mipMap = ICreateBlankTex("Bubble", width, height, kRefBubbleShoreTex); + + IRefillBubbleShoreTex(); + } + + return fBubbleShoreTex; +} + +void plWaveSet7::IRefillEdgeShoreTex() +{ + plMipmap* mipMap = fEdgeShoreTex; + + const int width = mipMap->GetWidth(); + const int height = mipMap->GetHeight(); + + plConst(hsScalar) kCenter(0.8f); + plConst(hsScalar) kRadius(0.025f); + + const int center = int(kCenter * height); + + const int radius = int(kRadius * height * State().fEdgeRadius); + + const int top = center + radius; + const int bot = center - radius; + const hsScalar invRadiusSq = 1.f / hsScalar(radius*radius); + + hsAssert(top < height-1, "Center too high or radius too big"); + + const hsScalar maxAlpha = State().fEdgeOpac * 255.9f; + int j; + for( j = 0; j < height; j++ ) + { + UInt32 alpha = 0; + if( (j > bot)&&(j < top) ) + { +#if 0 // like x^2 + hsScalar a = hsScalar(j-center); + a *= a; + a *= invRadiusSq; + a = 1.f - a; +#elif 1 // like 1/x^2 + hsScalar a = hsScalar(j-center); + if( a < 0 ) + a = -a; + a /= hsScalar(radius); + a = 1.f - a; + a *= a; +#else // like cos + hsScalar a = hsScalar(j - center); + a /= hsScalar(radius); + a *= hsScalarPI; + a = hsFastMath::CosInRange(a); + a += 1.f; + a *= 0.5f; +#endif + + alpha = UInt32(a * maxAlpha); + } + + int i; + for( i = 0; i < width; i++ ) + { + UInt32* val = mipMap->GetAddr32(i, j); + *val = (alpha << 24) + | (alpha << 16) + | (alpha << 8) + | (alpha << 0); + } + } + mipMap->MakeDirty(); + + fTrialUpdate &= ~kRemakeEdge; +} + +plMipmap* plWaveSet7::ICreateEdgeShoreTex(int width, int height) +{ + // Edge layer is solid white color. + // Alpha is 0 from base almost up to where graph shore texture drops off + // sharply. There, alpha ramps up and back down. Probably get a better look + // from sqrt(k - d^2) (elliptical) than a linear ramp up and down. + // Tile in U, clamp in V. + + // If we haven't already made one... + if( !fEdgeShoreTex ) + { + plMipmap* mipMap = ICreateBlankTex("Edge", width, height, kRefEdgeShoreTex); + + IRefillEdgeShoreTex(); + } + + return fEdgeShoreTex; +} + +void plWaveSet7::ISetAsTexture(plLayer* lay, plBitmap* tex) +{ + hsAssert(lay && tex, "Trying to set nil texture or nil layer"); + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(lay->GetKey(), plRefMsg::kOnRequest, 0, plLayRefMsg::kTexture); + hsgResMgr::ResMgr()->SendRef(tex->GetKey(), refMsg, plRefFlags::kActiveRef); +} + +void plWaveSet7::ICreateGraphShoreLayer(hsGMaterial* mat, int iPass) +{ + // GraphShoreLayer has a texture with white color (possibly noised), + // and a solid alpha. + // Alpha is constant along U (width). + // Alpha is 0 at base, + // ramps up to opaque + // then drops off sharply to transparent just before the top. + // Might as well make it pretty much 1 dimensional. + // + // Tile in U, clamp in V + + plLayer* lay = ICreateBlankLayer("Graph", iPass); + + // Set up it's state. + // First pass just overwrites, from then on alpha blend onto. + lay->SetBlendFlags(0); + lay->SetZFlags(hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite); + lay->SetShadeFlags(hsGMatState::kShadeReallyNoFog + | hsGMatState::kShadeNoProjectors + | hsGMatState::kShadeNoShade + | hsGMatState::kShadeWhite); + lay->SetClampFlags(hsGMatState::kClampTextureV); + lay->SetMiscFlags(hsGMatState::kMiscRestartPassHere); + + lay->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + lay->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + lay->SetOpacity(1.f); + + lay->SetUVWSrc(0); + + // Now set the texture. + plConst(int) kGraphWidth(1); + plConst(int) kGraphHeight(512); + plMipmap* tex = ICreateGraphShoreTex(kGraphWidth, kGraphHeight); + ISetAsTexture(lay, tex); + + mat->AddLayerViaNotify(lay); + +} + +// Second layer is the bubbles +void plWaveSet7::ICreateGraphBubbleLayer(hsGMaterial* mat, int iPass) +{ + // Bubble layer is white in color (or noised). + // Alpha is just, well, random bubbles. + // Tile in U and V + plLayer* lay = ICreateBlankLayer("Bubble", iPass); + + // Set up it's state. + lay->SetBlendFlags(hsGMatState::kBlendAlpha); + lay->SetZFlags(hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite); + lay->SetShadeFlags(hsGMatState::kShadeReallyNoFog + | hsGMatState::kShadeNoProjectors + | hsGMatState::kShadeNoShade + | hsGMatState::kShadeWhite); + lay->SetClampFlags(0); + lay->SetMiscFlags(0); + + lay->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + lay->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + lay->SetOpacity(1.f); + + lay->SetUVWSrc(0); + + // Now set the texture + plConst(int) kBubWidth(128); + plConst(int) kBubHeight(128); + plMipmap* tex = ICreateBubbleShoreTex(kBubWidth, kBubHeight); + ISetAsTexture(lay, tex); + + mat->AddLayerViaNotify(lay); +} + +// Third layer is the alpha leading edge +void plWaveSet7::ICreateGraphEdgeLayer(hsGMaterial* mat, int iPass) +{ + // Edge layer is solid white color. + // Alpha is 0 from base almost up to where graph shore texture drops off + // sharply. There, alpha ramps up and back down. Probably get a better look + // from sqrt(k - d^2) (elliptical) than a linear ramp up and down. + // Tile in U, clamp in V. + plLayer* lay = ICreateBlankLayer("Edge", iPass); + + // Set up it's state. + lay->SetBlendFlags(hsGMatState::kBlendAlpha); + lay->SetZFlags(hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite); + lay->SetShadeFlags(hsGMatState::kShadeReallyNoFog + | hsGMatState::kShadeNoProjectors + | hsGMatState::kShadeNoShade + | hsGMatState::kShadeWhite); + lay->SetClampFlags(hsGMatState::kClampTextureV); + lay->SetMiscFlags(0); + + lay->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + lay->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + lay->SetOpacity(1.f); + + lay->SetUVWSrc(0); + + // Now set the texture. + plConst(int) kEdgeWidth(1); + plConst(int) kEdgeHeight(512); + plMipmap* tex = ICreateEdgeShoreTex(kEdgeWidth, kEdgeHeight); + ISetAsTexture(lay, tex); + + mat->AddLayerViaNotify(lay); +} + +void plWaveSet7::ICreateGraphShoreMaterials() +{ + int i; + for( i = 0; i < kGraphShorePasses; i++ ) + { + // Create our material + // and send ourselves a ref. + hsGMaterial* mat = ICreateEmptyMaterial("GraphShoreMat", kRefGraphShoreMat, i); + + // GraphShoreMat's are the materials used to generate the shore texture layers + // which are then used on rendering the shore to the screen. + + // Create 3 layers + // First layer is the graph shore + ICreateGraphShoreLayer(mat, i); + + // Second layer is the bubbles + ICreateGraphBubbleLayer(mat, i); + + // Third layer is the alpha leading edge + ICreateGraphEdgeLayer(mat, i); + + IAddGraphVShader(mat, i); + IAddGraphPShader(mat, i); + + IInitGraph(i); + + hsAssert(fGraphShoreMat[i] == mat, "Should have been processed in ICreateEmptyMaterial()"); + } + +} + +void plWaveSet7::IAddGraphVShader(hsGMaterial* mat, int iPass) +{ + if( !fGraphVShader[iPass] ) + { + plShader* vShader = TRACKED_NEW plShader; + char buff[256]; + sprintf(buff, "%s_GraphVS_%d", GetKey()->GetName(), iPass); + hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation()); + vShader->SetIsPixelShader(false); + + vShader->SetNumConsts(plGraphVS::kNumConsts); + + vShader->SetVector(plGraphVS::kNumericConsts, 0, 0.5f, 1.f, 2.f); + vShader->SetVector(plGraphVS::kPiConsts, 1.f / (2.f*hsScalarPI), hsScalarPI/2.f, hsScalarPI, hsScalarPI*2.f); + vShader->SetVector(plGraphVS::kCosConsts, 1.f, -1.f/2.f, 1.f/24.f, -1.f/720.f); + +#ifndef TEST_UVWS + vShader->SetInputFormat(0); +#else // TEST_UVWS + vShader->SetInputFormat(1); +#endif // TEST_UVWS + vShader->SetOutputFormat(0); + +// vShader->SetShaderFileName("sha/vs_WaveGraph.inl"); +// vShader->SetShaderFileName("sha/vs_WaveGraph2.inl"); + vShader->SetDecl(plShaderTable::Decl(vs_WaveGraph2)); + + hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, iPass, kRefGraphVShader), plRefFlags::kActiveRef); + + hsAssert(fGraphVShader[iPass] == vShader, "SendRef should have set shader"); + } + + IAddShaderToLayers(mat, 0, 2, plLayRefMsg::kVertexShader, fGraphVShader[iPass]); +} + +void plWaveSet7::IAddGraphPShader(hsGMaterial* mat, int iPass) +{ + if( !fGraphPShader[iPass] ) + { + plShader* pShader = TRACKED_NEW plShader; + char buff[256]; + sprintf(buff, "%s_GraphPS_%d", GetKey()->GetName(), iPass); + hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation()); + pShader->SetIsPixelShader(true); + + pShader->SetNumConsts(plGraphPS::kNumConsts); + + pShader->SetInputFormat(0); + pShader->SetOutputFormat(0); + +// pShader->SetShaderFileName("sha/ps_WaveGraph.inl"); + pShader->SetDecl(plShaderTable::Decl(ps_WaveGraph)); + + hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, iPass, kRefGraphPShader), plRefFlags::kActiveRef); + + hsAssert(fGraphPShader[iPass] == pShader, "SendRef should have set shader"); + } + + IAddShaderToLayers(mat, 0, 2, plLayRefMsg::kPixelShader, fGraphPShader[iPass]); +} + +plRenderTarget* plWaveSet7::ISetupGraphShoreRenderReq(int which) +{ + plConst(int) kGraphSize(256); + char name[256]; + sprintf(name, "Graph_%d", which); + plRenderTarget* rt = ICreateTransferRenderTarget(name, kGraphSize); + + hsgResMgr::ResMgr()->SendRef(rt->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, which, kRefGraphShoreRT), plRefFlags::kActiveRef); + + fGraphReq[which] = ICreateRenderRequest(rt, fGraphShoreDraw[which], -100.f); + fGraphReqMsg[which] = TRACKED_NEW plRenderRequestMsg(GetKey(), fGraphReq[which]); + + return rt; +} + +void plWaveSet7::ISetupGraphShore(hsGMaterial* mat) +{ + if( !fGraphShoreRT[0] ) + { + // Create the material + ICreateGraphShoreMaterials(); + + int i; + for( i = 0; i < kGraphShorePasses; i++ ) + { + // Create the mesh + plDrawableSpans* drawable = ICreateEmptyGraphDrawable("GraphShore", kRefGraphShoreDraw, i); + plConst(int) kGraphWidth(256); + fGraphShoreDraw[i] = ICreateGraphDrawable(drawable, fGraphShoreMat[i], kGraphWidth); + + // Setup render requests + // Return value is the texture we want to use for the shore line. + fGraphShoreRT[i] = ISetupGraphShoreRenderReq(i); + } + + } + + // If we've already done this material, we're done (it may be shared + // with another shore mesh). + if( fShoreVShader && mat->GetLayer(0) && (fShoreVShader == mat->GetLayer(0)->GetVertexShader()) ) + return; + + // We now have all our render target textures. Set them up as + // our kGraphShorePasses layers on this material. + ISetupShoreLayers(mat); + +} + +void plWaveSet7::IMakeShoreLayer(hsGMaterial* mat, int which) +{ + char name[512]; + if( which >= mat->GetNumLayers() ) + { + plLayer* lay = TRACKED_NEW plLayer; + sprintf(name, "%s_lay_%d", mat->GetKey()->GetName(), which); + hsgResMgr::ResMgr()->NewKey(name, lay, GetKey()->GetUoid().GetLocation()); + + lay->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + lay->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + lay->SetOpacity(1.f); + + mat->AddLayerViaNotify(lay); + } +} + +void plWaveSet7::ISetupShoreLayers(hsGMaterial* mat) +{ + // Make sure we have exactly kGraphShorePasses layers on this mat. + IMakeShoreLayer(mat, 0); + IMakeShoreLayer(mat, 1); + IMakeShoreLayer(mat, 2); + + // Make sure the state is correct for each layer. + // And set the textures up to point at the render targets + plLayer* lay = plLayer::ConvertNoRef(mat->GetLayer(0)->BottomOfStack()); + hsAssert(lay, "Bad first layer on material %s. Animated?"); +// lay->SetBlendFlags(hsGMatState::kBlendAlpha | hsGMatState::kBlendInvertFinalAlpha | hsGMatState::kBlendAlphaAlways); + lay->SetBlendFlags(hsGMatState::kBlendAlpha); + lay->SetClampFlags(hsGMatState::kClampTextureV); + lay->SetShadeFlags(hsGMatState::kShadeNoProjectors + | hsGMatState::kShadeNoShade + | hsGMatState::kShadeWhite); + lay->SetZFlags(hsGMatState::kZNoZWrite | hsGMatState::kZIncLayer); + lay->SetMiscFlags(hsGMatState::kMiscTwoSided); + ISetAsTexture(lay, fGraphShoreRT[0]); + + lay->SetUVWSrc(0); + + lay = plLayer::ConvertNoRef(mat->GetLayer(1)); + hsAssert(lay, "Bad second layer on material %s. Animated?"); + lay->SetBlendFlags(hsGMatState::kBlendAlpha); + lay->SetClampFlags(hsGMatState::kClampTextureV); + lay->SetShadeFlags(hsGMatState::kShadeNoProjectors + | hsGMatState::kShadeNoShade + | hsGMatState::kShadeWhite); + lay->SetZFlags(hsGMatState::kZNoZWrite); + lay->SetMiscFlags(hsGMatState::kMiscTwoSided); + ISetAsTexture(lay, fGraphShoreRT[1]); + + lay->SetUVWSrc(1); + + lay = plLayer::ConvertNoRef(mat->GetLayer(2)); + hsAssert(lay, "Bad third layer on material %s. Animated?"); + lay->SetBlendFlags(hsGMatState::kBlendAlpha); + lay->SetClampFlags(hsGMatState::kClampTextureV); + lay->SetShadeFlags(hsGMatState::kShadeNoProjectors + | hsGMatState::kShadeNoShade + | hsGMatState::kShadeWhite); + lay->SetZFlags(hsGMatState::kZNoZWrite); + lay->SetMiscFlags(hsGMatState::kMiscTwoSided); + ISetAsTexture(lay, fGraphShoreRT[2]); + + lay->SetUVWSrc(2); + + // Add the vertex and pixel shaders. + IAddShoreVertexShader(mat); + IAddShorePixelShader(mat); +} + +void plWaveSet7::IInitGraph(int iPass) +{ + GraphState& gs = fGraphState[iPass]; + plShader* shader = fGraphVShader[iPass]; + + // First the easy stuff. + + // Age starts off 0. + gs.fAge = 0; + + static int lastOne = 0; + + plConst(hsScalar) kBasePeriod(3.f); + hsScalar life = State().fPeriod * kBasePeriod * (1.f + fRand.RandZeroToOne()); + gs.fInvLife = (1.f + hsScalar(lastOne)/hsScalar(kGraphShorePasses-1)) / life; + + lastOne = !lastOne; + + gs.fUOff = fRand.RandZeroToOne(); + + // Now the rest we have to think about a little, and + // think about four times. + int i; + for( i = 0; i < 4; i++ ) + { + // Okay, phase we don't have to think too hard about, + // it doesn't matter as long as it's random. + gs.fPhase[i] = fRand.RandZeroToOne() * 2.f * hsScalarPI; + + // Next up is frequency, but frequency is the hard one. + // Remember frequency has to preserve tiling, so freq = k * 2 * PI. + // We'd like to keep at least one big one around all the time, + // so we'll always put a big one in first, and then a bunch of + // smaller ones to noise it up nice. + int k; + if( !i ) + { + k = fRand.RandZeroToOne() > 0.5 ? 1 : 2; + } + else + { + plConst(int) kMinFreq(3); + plConst(int) kMaxFreq(10); + + k = kMinFreq + int((kMaxFreq-kMinFreq) * fRand.RandZeroToOne()); + } + + // Input will be in range [0..2], so we'll omit the customary 2*PI here. + gs.fFreq[i] = k * hsScalarPI; + + // Amplitude depends on freqency, or roughly inversely proportional + // to frequency (randomized about linear on period). + // Divide by 4 because that's how many oscillators we have, and they + // are summed. + hsScalar period = 1.f / hsScalar(k); + plConst(hsScalar) kAmpScale(1.f / 4.f / 2.f); + plConst(hsScalar) kMinPeriodFrac(1.f); + plConst(hsScalar) kMaxPeriodFrac(2.f); + period *= kMinPeriodFrac + fRand.RandZeroToOne() * (kMaxPeriodFrac - kMinPeriodFrac); + period *= kAmpScale; + gs.fAmp[i] = period; + } + // Go ahead and set the ones on the shader that won't be updated. + shader->SetVector(plGraphVS::kPhase, + gs.fPhase[0], + gs.fPhase[1], + gs.fPhase[2], + gs.fPhase[3]); + shader->SetVector(plGraphVS::kFrequency, + gs.fFreq[0], + gs.fFreq[1], + gs.fFreq[2], + gs.fFreq[3]); + + // Propagate all this to the shader. + IUpdateGraphShader(0, iPass); +} + +void plWaveSet7::IShuffleDownGraphs(int iPass) +{ + int i; + for( i = iPass+1; i < kGraphShorePasses; i++ ) + { + fGraphState[i-1] = fGraphState[i]; + fGraphVShader[i-1]->CopyConsts(fGraphVShader[i]); + } + IInitGraph(kGraphShorePasses-1); +} + +void plWaveSet7::IUpdateGraphShader(hsScalar dt, int iPass) +{ + if( fGraphShoreDraw[iPass] ) + { + GraphState& gs = fGraphState[iPass]; + plShader* shader = fGraphVShader[iPass]; + + gs.fAge += dt; + hsScalar rads = gs.fAge * gs.fInvLife; + if( rads >= hsScalarPI ) + { + // Recycle this one and restart the upper. + IShuffleDownGraphs(iPass); + } + else + { + hsScalar sinAge = hsFastMath::SinInRange(rads); + + shader->SetVector(plGraphVS::kAmplitude, + gs.fAmp[0] * sinAge, + gs.fAmp[1] * sinAge, + gs.fAmp[2] * sinAge, + gs.fAmp[3] * sinAge); + + // Might want to tint this sometime. + plConst(hsColorRGBA) kTint(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + hsColorRGBA tint = kTint; + tint.a *= sinAge; + shader->SetColor(plGraphVS::kColor, tint); + + plConst(hsScalar) kCMax(1.f); + plConst(hsScalar) kCMin(3.f); + hsScalar cMin = kCMax + (kCMin - kCMax) * State().fFingerLength; + plConst(hsScalar) k2ndLayerScale(2.f); + plConst(hsScalar) k2ndLayerVoff(1.5f); + shader->SetVector(plGraphVS::kUVWConsts, + (kCMax - cMin) * sinAge + cMin, + gs.fUOff, + k2ndLayerVoff, + k2ndLayerScale); + } + } +} + +void plWaveSet7::IUpdateGraphShaders(plPipeline* pipe, hsScalar dt) +{ + if( fGraphShoreDraw[0] ) + { + int i; + for( i = kGraphShorePasses-1; i >= 0; i-- ) + { + IUpdateGraphShader(dt, i); + } + } +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSet7.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSet7.h new file mode 100644 index 00000000..19f0451f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSet7.h @@ -0,0 +1,642 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plWaveSet7_inc +#define plWaveSet7_inc + + +#include "hsGeometry3.h" +#include "hsTemplates.h" +#include "../plMath/plRandom.h" +#include "hsBounds.h" + +#include "plFixedWaterState7.h" + +#include "plWaveSetBase.h" + +class hsStream; +class hsResMgr; +class plAccessVtxSpan; +class plMessage; +class hsGMaterial; +class plDrawableSpans; +class plRenderMsg; +class plArmatureUpdateMsg; +class plGenRefMsg; +class plAuxSpan; +class plDynaDecalMgr; +class plGBufferGroup; +class plBitmap; +class plMipmap; +class plLayer; +class plRenderRequest; +class plRenderRequestMsg; +class plRenderTarget; +class plShader; +class plPipeline; +class plRipVSConsts; +class plStatusLog; +class plGraphPlate; + +class plWorldWaveData7 +{ +public: + hsPoint3 fDir; + hsScalar fLength; + + hsScalar fFreq; + hsScalar fPhase; + hsScalar fAmplitude; +}; + +class plWorldWave7 : public plWorldWaveData7 +{ +public: + + inline void Accumulate(hsPoint3& accumPos, hsVector3& accumNorm) const; +}; + + +class plWaveSet7 : public plWaveSetBase +{ +public: + // Props inc by 1 (bit shift in bitvector). + enum plDrawProperties { + kDisable = 0, + + kNumProps // last in the list + }; + // Flags, also in a bitvector, so far unused in the multimodifier + enum { + kHasRefObject = 16 + }; + enum { + kNumWaves = 4 + }; + enum { + kRefDynaDecalMgr, + kRefBuoy, + kRefBumpMat, + kRefBumpDraw, + kRefBumpVShader, + kRefBumpPShader, + kRefBiasVShader, + kRefBiasPShader, + kRefRipVShader, + kRefRipPShader, + kRefShoreVShader, + kRefShorePShader, + kRefFixedVShader, + kRefFixedPShader, + kRefGraphShoreTex, + kRefBubbleShoreTex, + kRefEdgeShoreTex, + kRefGraphShoreMat, + kRefGraphShoreDraw, + kRefGraphVShader, + kRefGraphPShader, + kRefGraphShoreRT, + kRefShore, + kRefDecal, + kRefDecVShader, + kRefDecPShader, + kRefEnvMap, + kRefCosineLUT, + kRefRefObj + }; + + enum { + kGraphShorePasses = 3 + }; + + enum { + kNumWindDep = 5 + }; + enum { + kNumTexWaves = 16 + }; + enum { + kBumpPerPass = 4 + }; + enum { + kNumBumpShaders = kNumTexWaves / kBumpPerPass + }; + enum { + kCompositeSize = 256 + }; + + enum { + kUpdateWaveKs = 0x1, + kRemakeBubble = 0x2, + kRemakeEdge = 0x4, + kReRenderEnvMap = 0x8, + kReInitWaves = 0x10 + }; + +protected: + + double fCurrTime; + double fLastTime; + + plStatusLog* fStatusLog; + plGraphPlate* fStatusGraph; + + UInt32 fTrialUpdate; + + plFixedWaterState7 fState; + + hsScalar fScrunchLen; + hsScalar fScrunchScale; + + hsVector3 fWindDir; + + hsScalar fMinLen; + hsScalar fMaxLen; + hsScalar fFreqScale; + + hsScalar fTransCountDown; + int fTransistor; + hsScalar fTransDel; + + hsScalar fTexTransCountDown; + int fTexTrans; + hsScalar fTexTransDel; + hsScalar fTexWaveFade[kNumTexWaves]; + + plWorldWave7 fWorldWaves[kNumWaves]; + hsScalar fFreqMod[kNumWaves]; + + plRandom fRand; + + plKey fSceneNode; + + hsTArray fDecalMgrs; + + hsTArray fBuoys; + hsTArray fShores; + hsTArray fDecals; + plSceneObject* fRefObj; + + hsTArray fTargBnds; + + plLayer* fBiasLayer[2]; + plLayer* fBumpLayers[kNumTexWaves]; + hsGMaterial* fBumpMat; + plDrawableSpans* fBumpDraw; + plRenderRequest* fBumpReq; + plRenderRequestMsg* fBumpReqMsg; + plMipmap* fCosineLUT; + + plShader* fBumpVShader[kNumBumpShaders]; + plShader* fBumpPShader[kNumBumpShaders]; + + plShader* fBiasVShader; + plShader* fBiasPShader; + + plBitmap* fEnvMap; + UInt32 fEnvSize; + hsScalar fEnvRefresh; + + plLayer* fFixedLayers[4]; + + plShader* fRipVShader; + plShader* fRipPShader; + + plShader* fShoreVShader; + plShader* fShorePShader; + + plShader* fFixedVShader; + plShader* fFixedPShader; + + enum DecalVType { + kDecalV1Lay, + kDecalV2Lay11, + kDecalV2Lay12, + kDecalVEnv, + + kNumDecalVShaders + }; + enum DecalPType { + kDecalPBB, + kDecalPaB, + kDecalPaM, + kDecalPaA, + kDecalPAB, + kDecalPAM, + kDecalPAA, + kDecalPMB, + kDecalPMM, + kDecalPMA, + kDecalPEnv, + + kNumDecalPShaders + }; + plShader* fDecalVShaders[kNumDecalVShaders]; + plShader* fDecalPShaders[kNumDecalPShaders]; + + // Graph shore stuff + plMipmap* fGraphShoreTex; + plMipmap* fBubbleShoreTex; + plMipmap* fEdgeShoreTex; + + hsGMaterial* fGraphShoreMat[kGraphShorePasses]; + plDrawableSpans* fGraphShoreDraw[kGraphShorePasses]; + plRenderTarget* fGraphShoreRT[kGraphShorePasses]; + + plRenderRequest* fGraphReq[kGraphShorePasses]; + plRenderRequestMsg* fGraphReqMsg[kGraphShorePasses]; + + plShader* fGraphVShader[kGraphShorePasses]; + plShader* fGraphPShader[kGraphShorePasses]; + + class GraphState + { + public: + float fAge; + float fInvLife; + float fUOff; + + float fFreq[4]; + float fPhase[4]; + float fAmp[4]; + }; + + GraphState fGraphState[kGraphShorePasses]; + + class WaveK + { + public: + // fK is the number of times the sine wave repeats across the texture. Must be an integer + // fS/fK is the base X component of the direction of the wave, with Y = 1.f - X. Note that X^2 + Y^2 != 1. + // fD allows the wave to get more off the Y direction + // So the X component will be Int(fS + fD*dispersion) / fK, because it must be an integer ratio to + // preserve tiling. Also, (fS + fD) must be <= fK (for the Y normalization). + // See the notes. + float fS; + float fK; + float fD; + }; + + WaveK fWaveKs[kNumTexWaves]; + + class TexWaveDesc + { + public: + hsScalar fPhase; + hsScalar fAmp; + hsScalar fLen; + hsScalar fFreq; + hsScalar fDirX; + hsScalar fDirY; + hsScalar fRotScale00; + hsScalar fRotScale01; + }; + TexWaveDesc fTexWaves[kNumTexWaves]; + + class TexWaveWindDep + { + public: + hsScalar fWindSpeed; + + hsScalar fHeight; + hsScalar fSpecular; + }; + + TexWaveWindDep fWindDeps[kNumWindDep]; + + void IInitWaveConsts(); + void IInitState(); + + inline void IScrunch(hsPoint3& pos, hsVector3& norm) const; + + void ICalcWindow(hsScalar dt); + void ICalcScale(); + void IUpdateWaves(hsScalar dt); + void IUpdateWave(hsScalar dt, int i); + hsBool IAnyBoundsVisible(plPipeline* pipe) const; + + void IInitWave(int i); + void IReInitWaves(); + + void IUpdateRefObject(); + void IUpdateWindDir(hsScalar dt); + + void IShiftCenter(plSceneObject* so) const; + void IFloatBuoys(hsScalar dt); + void IFloatBuoy(hsScalar dt, plSceneObject* so); + + // Bookkeeping + void IAddTarget(const plKey& key); + void IRemoveTarget(const plKey& key); + + void ISetWindSpeed(hsScalar s); + + hsBool IOnReceive(plGenRefMsg* refMsg); + hsBool IOnRemove(plGenRefMsg* refMsg); + + hsBool ITransContinue(hsScalar dt); + void IStartTransition(hsScalar dt); + hsScalar ITransitionDelay() const; + void ITransition(hsScalar dt); + + hsBool ITransTexContinue(hsScalar dt); + void IStartTexTransition(hsScalar dt); + void ITransTex(hsScalar dt); + + void IInitTexWave(int i); + void ISetupTextureWaves(); + + void IUpdateLayers(hsScalar dt); + void IUpdateBumpLayers(hsScalar dt); + + plRenderRequest* ICreateRenderRequest(plRenderTarget* rt, plDrawableSpans* draw, hsScalar pri); + void ISubmitRenderRequests(); + + plRenderTarget* ICreateTransferRenderTarget(const char* name, int size); + plDrawableSpans* ICreateClearDrawable(plDrawableSpans* drawable, hsGMaterial* mat); + + void IAddBumpBiasShaders(plLayer* layer); + plMipmap* ICreateBiasNoiseMap(); + void IAddBumpBiasLayer(hsGMaterial* mat); + + plMipmap* ICreateBumpBitmapFFP(hsScalar amp, hsScalar dx, hsScalar dy) const; + hsGMaterial* ICreateBumpLayersFFP(); + plMipmap* ICreateBumpMipmapPS(); + plLayer* ICreateBumpLayerPS(plMipmap* mipMap, hsGMaterial* bumpMat, int which); + hsGMaterial* ICreateBumpLayersPS(); + plDrawableSpans* ICreateBumpDrawable(); + + plLayer* ICreateTotalEnvLayer(plBitmap* envMap, hsGMaterial* mat, int which, const char* pref); + plLayer* ICreateTotalLayer(plBitmap* bm, hsGMaterial* mat, int which, const char* suff); + + hsGMaterial* ICreateFixedMatPS(hsGMaterial* mat, const int numUVWs); + void ICreateFixedMat(hsGMaterial* mat, const int numUVWs); + void ICheckTargetMaterials(); + + plDrawableSpans* ICreateGraphDrawable(plDrawableSpans* drawable, hsGMaterial* mat, int nWid); + plDrawableSpans* ICreateEmptyGraphDrawable(const char* name, UInt32 ref, int wich); + hsGMaterial* ICreateEmptyMaterial(const char* name, UInt32 ref, int which); + plLayer* ICreateBlankLayer(const char* name, int suff); + plMipmap* ICreateBlankTex(const char* name, int width, int height, UInt32 ref); + plMipmap* ICreateGraphShoreTex(int width, int height); + plMipmap* ICreateBubbleShoreTex(int width, int height); + void IRefillBubbleShoreTex(); + plMipmap* ICreateEdgeShoreTex(int width, int height); + void IRefillEdgeShoreTex(); + void ISetAsTexture(plLayer* lay, plBitmap* tex); + void ICreateGraphShoreLayer(hsGMaterial* mat, int iPass); + void ICreateGraphBubbleLayer(hsGMaterial* mat, int iPass); + void ICreateGraphEdgeLayer(hsGMaterial* mat, int iPass); + void ICreateGraphShoreMaterials(); + plRenderTarget* ISetupGraphShoreRenderReq(int which); + void IMakeShoreLayer(hsGMaterial* mat, int which); + void ISetupShoreLayers(hsGMaterial* mat); + void ISetupGraphShore(hsGMaterial* mat); + void ICheckShoreMaterial(plSceneObject* so); + void ICheckShoreMaterials(); + void ICheckDecalMaterial(plSceneObject* so); + void ICheckDecalMaterials(); + void ISetupDecal(hsGMaterial* mat); + void ICheckDecalEnvLayers(hsGMaterial* mat); + + void IAddGraphPShader(hsGMaterial* mat, int iPass); + void IAddGraphVShader(hsGMaterial* mat, int iPass); + void IUpdateGraphShader(hsScalar dt, int iPass); + void IInitGraph(int iPass); + void IShuffleDownGraphs(int iPass); + + // type is either plLayRefMsg::kVertexShader or plLayRefMsg::kPixelShader. + void IAddShaderToLayers(hsGMaterial* mat, int iFirst, int iLast, UInt8 type, plShader* shader); + + void IAddBumpPixelShader(hsGMaterial* mat, int iShader, int iFirst, int iLast); + void IAddBumpVertexShader(hsGMaterial* mat, int iShader, int iFirst, int iLast); + + void IAddRipVertexShader(hsGMaterial* mat, const plRipVSConsts& ripConsts); + void IAddRipPixelShader(hsGMaterial* mat, const plRipVSConsts& ripConsts); + + void IAddShoreVertexShader(hsGMaterial* mat); + void IAddShorePixelShader(hsGMaterial* mat); + + void IAddFixedVertexShader(hsGMaterial* mat, const int numUVWs); + void IAddFixedPixelShader(hsGMaterial* mat); + + plShader* IGetDecalPShader(hsGMaterial* mat); + plShader* ICreateDecalPShader(DecalPType t); + plShader* IGetDecalVShader(hsGMaterial* mat); + plShader* ICreateDecalVShader(DecalVType t); + + void IUpdateShaders(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l); + + void IUpdateBiasVShader(); + void IUpdateBumpPShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l); + void IUpdateBumpVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l); + void IUpdateRipPShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l); + void IUpdateRipVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l); + void IUpdateShoreVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l); + void IUpdateFixedVShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l); + void IUpdateFixedPShader(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l); + void IUpdateGraphShaders(plPipeline* pipe, hsScalar dt); + void IUpdateDecVShader(int t, plPipeline* pipe); + void IUpdateDecVShaders(plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual int IShoreRef() const { return kRefShore; } + virtual int IDecalRef() const { return kRefDecal; } + + inline void LogF(const char *format, ...) const; + inline void LogF(UInt32 color, const char *format, ...) const; + inline void IRestartLog() const; + inline void GraphLen(hsScalar len) const; + inline void IRestartGraph() const; + +public: + plWaveSet7(); + virtual ~plWaveSet7(); + + CLASSNAME_REGISTER( plWaveSet7 ); + GETINTERFACE_ANY( plWaveSet7, plWaveSetBase ); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return false; } + + Int32 GetNumProperties() const { return kNumProps; } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + hsScalar EvalPoint(hsPoint3& pos, hsVector3& norm); + + // Getters and Setters for Python twiddling + // + // First a way to set new values. The secs parameter says how long to take + // blending to the new value from the current value. + // + // Geometric wave parameters. These are all safe to twiddle at any time or speed. + // The new settings take effect as new waves are spawned. + void SetGeoMaxLength(hsScalar s, hsScalar secs=0) { fState.fGeoState.fMaxLength.Set(s, secs); } + void SetGeoMinLength(hsScalar s, hsScalar secs=0) { fState.fGeoState.fMinLength.Set(s, secs); } + void SetGeoAmpOverLen(hsScalar s, hsScalar secs=0) { fState.fGeoState.fAmpOverLen.Set(s, secs); } + void SetGeoChop(hsScalar s, hsScalar secs=0) { fState.fGeoState.fChop.Set(s, secs); } + void SetGeoAngleDev(hsScalar s, hsScalar secs=0) { fState.fGeoState.fAngleDev.Set(s, secs); } + + // Texture wave parameters. Safe to twiddle any time or speed. + // The new settings take effect as new waves are spawned. + void SetTexMaxLength(hsScalar s, hsScalar secs=0) { fState.fTexState.fMaxLength.Set(s, secs); } + void SetTexMinLength(hsScalar s, hsScalar secs=0) { fState.fTexState.fMinLength.Set(s, secs); } + void SetTexAmpOverLen(hsScalar s, hsScalar secs=0) { fState.fTexState.fAmpOverLen.Set(s, secs); } + void SetTexChop(hsScalar s, hsScalar secs=0) { fState.fTexState.fChop.Set(s, secs); } + void SetTexAngleDev(hsScalar s, hsScalar secs=0) { fState.fTexState.fAngleDev.Set(s, secs); } + + // The size in feet of one tile of the ripple texture. If you change this (I don't + // recommend it), you need to change it very slowly or it will look very stupid. + void SetRippleScale(hsScalar s, hsScalar secs=0) { fState.fRippleScale.Set(s, secs); } + + // The direction the wind is blowing (waves will be more or less perpindicular to wind dir). + // Change somewhat slowly, like over 30 seconds. + void SetWindDir(const hsVector3& s, hsScalar secs=0) { fState.fWindDir.Set(s, secs); } + + // Change these gently, effect is immediate. + void SetSpecularNoise(hsScalar s, hsScalar secs=0) { hsVector3 spec = fState.fSpecVec; spec[plFixedWaterState7::kNoise] = s; fState.fSpecVec.Set(spec, secs); } + void SetSpecularStart(hsScalar s, hsScalar secs=0) { hsVector3 spec = fState.fSpecVec; spec[plFixedWaterState7::kSpecStart] = s; fState.fSpecVec.Set(spec, secs); } + void SetSpecularEnd(hsScalar s, hsScalar secs=0) { hsVector3 spec = fState.fSpecVec; spec[plFixedWaterState7::kSpecEnd] = s; fState.fSpecVec.Set(spec, secs); } + + // Water Height is overriden if the ref object is animated. + void SetWaterHeight(hsScalar s, hsScalar secs=0) { fState.fWaterHeight.Set(s, secs); } + + // Water Offset and DepthFalloff are complicated, and not immediately interesting to animate. + void SetWaterOffset(const hsVector3& s, hsScalar secs=0) { fState.fWaterOffset.Set(s, secs); } + void SetOpacOffset(hsScalar s, hsScalar secs=0) { hsVector3 off = fState.fWaterOffset; off.fX = s; fState.fWaterOffset.Set(off, secs); } + void SetReflOffset(hsScalar s, hsScalar secs=0) { hsVector3 off = fState.fWaterOffset; off.fY = s; fState.fWaterOffset.Set(off, secs); } + void SetWaveOffset(hsScalar s, hsScalar secs=0) { hsVector3 off = fState.fWaterOffset; off.fZ = s; fState.fWaterOffset.Set(off, secs); } + void SetDepthFalloff(const hsVector3& s, hsScalar secs=0) { fState.fDepthFalloff.Set(s, secs); } + void SetOpacFalloff(hsScalar s, hsScalar secs=0) { hsVector3 off = fState.fDepthFalloff; off.fX = s; fState.fDepthFalloff.Set(off, secs); } + void SetReflFalloff(hsScalar s, hsScalar secs=0) { hsVector3 off = fState.fDepthFalloff; off.fY = s; fState.fDepthFalloff.Set(off, secs); } + void SetWaveFalloff(hsScalar s, hsScalar secs=0) { hsVector3 off = fState.fDepthFalloff; off.fZ = s; fState.fDepthFalloff.Set(off, secs); } + + // Max and Min Atten aren't very interesting, and will probably go away. + void SetMaxAtten(const hsVector3& s, hsScalar secs=0) { fState.fMaxAtten.Set(s, secs); } + void SetMinAtten(const hsVector3& s, hsScalar secs=0) { fState.fMinAtten.Set(s, secs); } + + // Skipping the shore parameters, because they are never used. + + // Water colors, adjust slowly, effect is immediate. + void SetWaterTint(const hsColorRGBA& s, hsScalar secs=0) { fState.fWaterTint.Set(s, secs); } + void SetWaterRGB(const hsVector3& col, hsScalar secs=0) { hsColorRGBA rgb; rgb.Set(col.fX, col.fY, col.fZ, GetWaterOpacity()); SetWaterTint(rgb, secs); } + void SetWaterOpacity(hsScalar s, hsScalar secs=0) { hsColorRGBA col = GetWaterTint(); col.a = s; SetWaterTint(col, secs); } + void SetSpecularTint(const hsColorRGBA& s, hsScalar secs=0) { fState.fSpecularTint.Set(s, secs); } + void SetSpecularRGB(const hsVector3& col, hsScalar secs=0) { hsColorRGBA rgb; rgb.Set(col.fX, col.fY, col.fZ, GetSpecularMute()); SetSpecularTint(rgb, secs); } + void SetSpecularMute(hsScalar s, hsScalar secs=0) { hsColorRGBA col = GetSpecularTint(); col.a = s; SetSpecularTint(col, secs); } + + // The environment map is essentially projected onto a sphere. Moving the center of + // the sphere north will move the reflections north, changing the radius of the + // sphere effects parallax in the obvious way. + void SetEnvCenter(const hsPoint3& s, hsScalar secs=0) { fState.fEnvCenter.Set(s, secs); } + void SetEnvRadius(hsScalar s, hsScalar secs=0) { fState.fEnvRadius.Set(s, secs); } + + // Now a way to get current values. See the accompanying Setter for notes on + // what the parameter means. + // + hsScalar GetGeoMaxLength() const { return fState.fGeoState.fMaxLength; } + hsScalar GetGeoMinLength() const { return fState.fGeoState.fMinLength; } + hsScalar GetGeoAmpOverLen() const { return fState.fGeoState.fAmpOverLen; } + hsScalar GetGeoChop() const { return fState.fGeoState.fChop; } + hsScalar GetGeoAngleDev() const { return fState.fGeoState.fAngleDev; } + + hsScalar GetTexMaxLength() const { return fState.fTexState.fMaxLength; } + hsScalar GetTexMinLength() const { return fState.fTexState.fMinLength; } + hsScalar GetTexAmpOverLen() const { return fState.fTexState.fAmpOverLen; } + hsScalar GetTexChop() const { return fState.fTexState.fChop; } + hsScalar GetTexAngleDev() const { return fState.fTexState.fAngleDev; } + + hsScalar GetRippleScale() const { return fState.fRippleScale; } + + hsVector3 GetWindDir() const { return fState.fWindDir; } + + hsScalar GetSpecularNoise() const { hsVector3 spec = fState.fSpecVec; return spec[plFixedWaterState7::kNoise]; } + hsScalar GetSpecularStart() const { hsVector3 spec = fState.fSpecVec; return spec[plFixedWaterState7::kSpecStart]; } + hsScalar GetSpecularEnd() const { hsVector3 spec = fState.fSpecVec; return spec[plFixedWaterState7::kSpecEnd]; } + + hsScalar GetWaterHeight() const { return fState.fWaterHeight; } + + hsVector3 GetWaterOffset() const { return fState.fWaterOffset; } + hsScalar GetOpacOffset() const { hsVector3 off = fState.fWaterOffset; return off.fX; } + hsScalar GetReflOffset() const { hsVector3 off = fState.fWaterOffset; return off.fY; } + hsScalar GetWaveOffset() const { hsVector3 off = fState.fWaterOffset; return off.fZ; } + hsVector3 GetDepthFalloff() const { return fState.fDepthFalloff; } + hsScalar GetOpacFalloff() const { hsVector3 off = fState.fDepthFalloff; return off.fX; } + hsScalar GetReflFalloff() const { hsVector3 off = fState.fDepthFalloff; return off.fY; } + hsScalar GetWaveFalloff() const { hsVector3 off = fState.fDepthFalloff; return off.fZ; } + + hsVector3 GetMaxAtten() const { return fState.fMaxAtten; } + hsVector3 GetMinAtten() const { return fState.fMinAtten; } + + hsColorRGBA GetWaterTint() const { return fState.fWaterTint; } + hsVector3 GetWaterRGB() const { hsColorRGBA col = GetWaterTint(); return hsVector3(col.r, col.g, col.b); } + hsScalar GetWaterOpacity() const { return GetWaterTint().a; } + hsColorRGBA GetSpecularTint() const { return fState.fSpecularTint; } + hsVector3 GetSpecularRGB() const { hsColorRGBA col = GetSpecularTint(); return hsVector3(col.r, col.g, col.b); } + hsScalar GetSpecularMute() const { return GetSpecularTint().a; } + + hsPoint3 GetEnvCenter() const { return fState.fEnvCenter; } + hsScalar GetEnvRadius() const { return fState.fEnvRadius; } + + // Export/debugging functions. For runtime, use message interface (plGenRefMsg, plWaveMsg). + void AddTarget(const plKey& key); + void RemoveTarget(const plKey& key); + void AddShoreTest(plKey& key); + + void SetRefObject(plSceneObject* refObj); + + void SetSceneNode(const plKey& key); + plKey GetSceneNode() const { return fSceneNode; } + + void AddDynaDecalMgr(plKey& key); + void RemoveDynaDecalMgr(plKey& key); + + void AddBuoy(plKey soKey); + void RemoveBuoy(plKey soKey); + + virtual hsBool SetupRippleMat(hsGMaterial* mat, const plRipVSConsts& ripConsts); + + virtual hsScalar GetHeight() const { return State().fWaterHeight; } + + const plFixedWaterState7::WaveState& GeoState() const { return State().fGeoState; } + const plFixedWaterState7::WaveState& TexState() const { return State().fTexState; } + const plFixedWaterState7& State() const { return fState; } + void SetState(const plFixedWaterState7& state, hsScalar dur); + + void SetEnvSize(UInt32 s) { fEnvSize = s; } + UInt32 GetEnvSize() const { return fEnvSize; } + + void StopLog(); + void StartLog(); + hsBool Logging() const { return fStatusLog != nil; } + void StartGraph(); + void StopGraph(); + hsBool Graphing() const { return fStatusGraph != nil; } +}; + +#endif // plWaveSet7_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetBase.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetBase.cpp new file mode 100644 index 00000000..f528aec1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetBase.cpp @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plWaveSetBase.h" + +#include "hsResMgr.h" +#include "../pnMessage/plRefMsg.h" + +plWaveSetBase::plWaveSetBase() +{ +} + +plWaveSetBase::~plWaveSetBase() +{ +} + +void plWaveSetBase::AddShore(plKey key) +{ + hsgResMgr::ResMgr()->SendRef(key, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, IShoreRef()), plRefFlags::kPassiveRef); +} + +void plWaveSetBase::AddDecal(plKey key) +{ + hsgResMgr::ResMgr()->SendRef(key, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, IDecalRef()), plRefFlags::kPassiveRef); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetBase.h new file mode 100644 index 00000000..77694ef9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetBase.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plWaveSetBase_inc +#define plWaveSetBase_inc + +#include "../pnModifier/plMultiModifier.h" +#include "hsGeometry3.h" + +class hsGMaterial; +class plRipVSConsts; + +class plWaveSetBase : public plMultiModifier +{ + virtual int IShoreRef() const = 0; + virtual int IDecalRef() const = 0; + +public: + plWaveSetBase(); + virtual ~plWaveSetBase(); + + CLASSNAME_REGISTER( plWaveSetBase ); + GETINTERFACE_ANY( plWaveSetBase, plMultiModifier ); + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return false; } + + Int32 GetNumProperties() const { return 0; } + + virtual hsBool SetupRippleMat(hsGMaterial* mat, const plRipVSConsts& ripConsts) = 0; + virtual hsScalar GetHeight() const = 0; + virtual hsVector3 GetWindDir() const = 0; + + + void AddShore(plKey soKey); + void AddDecal(plKey key); + +}; + +#endif // plWaveSetBase_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetShaderConsts.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetShaderConsts.h new file mode 100644 index 00000000..aaa9e8ab --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plDrawable/plWaveSetShaderConsts.h @@ -0,0 +1,410 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plWaveSetShaderConsts_inc +#define plWaveSetShaderConsts_inc + +// Notice there can be skips for multi-vector consts (e.g. matrices). +namespace plGridVS +{ + enum { + kLocalToNDC = 0, + kProjRow0 = 0, + kProjRow1 = 1, + kProjRow2 = 2, + kProjRow3 = 3, + kWaterTint = 4, + kFrequency = 5, + kPhase = 6, + kAmplitude = 7, + kDirectionX = 8, + kDirectionY = 9, + kUTransform = 10, + kVTransform = 11, + kScrunch = 12, + kSinConsts = 13, + kCosConsts = 14, + kPiConsts = 15, + kNumericConsts = 16, + kCameraPos = 17, + kWindRot = 18, + kEnvAdjust = 19, + kEnvTint = 20, + + kNumConsts + }; +}; + +namespace plFixedVS +{ + enum { + kWorldToNDC = 0, + kProjRow0 = 0, + kProjRow1 = 1, + kProjRow2 = 2, + kProjRow3 = 3, + kWaterTint = 4, + kFrequency = 5, + kPhase = 6, + kAmplitude = 7, + kDirectionX = 8, + kDirectionY = 9, + kUTransform = 10, + kVTransform = 11, + kScrunch = 12, + kSinConsts = 13, + kCosConsts = 14, + kPiConsts = 15, + kNumericConsts = 16, + kCameraPos = 17, + kWindRot = 18, + kEnvAdjust = 19, + kEnvTint = 20, + kLocalToWorld = 21, + kL2WRow0 = 21, + kL2WRow1 = 22, + kL2WRow2 = 23, + kLengths = 24, + kWaterLevel = 25, + kDepthFalloff = 26, + kMinAtten = 27, + kFogSet = 28, + + kNumConsts + }; +}; + +namespace plFixedVS7 +{ + enum { + kWorldToNDC = 0, + kProjRow0 = 0, + kProjRow1 = 1, + kProjRow2 = 2, + kProjRow3 = 3, + kWaterTint = 4, + kFrequency = 5, + kPhase = 6, + kAmplitude = 7, + kDirectionX = 8, + kDirectionY = 9, + kUVScale = 10, + kSpecAtten = 11, + kScrunch = 12, // UNUSED + kSinConsts = 13, + kCosConsts = 14, + kPiConsts = 15, + kNumericConsts = 16, + kCameraPos = 17, + kWindRot = 18, + kEnvAdjust = 19, + kEnvTint = 20, + kLocalToWorld = 21, + kL2WRow0 = 21, + kL2WRow1 = 22, + kL2WRow2 = 23, + kLengths = 24, + kWaterLevel = 25, + kDepthFalloff = 26, + kMinAtten = 27, + kFogSet = 28, + kDirXK = 29, + kDirYK = 30, + kDirXW = 31, + kDirYW = 32, + kWK = 33, + kDirXSqKW = 34, + kDirXDirYKW = 35, + kDirYSqKW = 36, + + kNumConsts + }; +}; + +namespace plShoreVS +{ + enum { + kWorldToNDC = 0, + kProjRow0 = 0, + kProjRow1 = 1, + kProjRow2 = 2, + kProjRow3 = 3, + kShoreTint = 4, + kFrequency = 5, + kPhase = 6, + kAmplitude = 7, + kDirectionX = 8, + kDirectionY = 9, + kIncline = 10, + kFogSet = 11, + kScrunch = 12, // UNUSED + kSinConsts = 13, + kCosConsts = 14, + kPiConsts = 15, + kNumericConsts = 16, + kQADirX = 17, // Q * Dir.x * A + kQADirY = 18, // Q * Dir.y * A + kTex0Transform = 19, + kTex0_Row0 = 19, + kTex0_Row1 = 20, + kTex0_Row2 = 21, + kTex1Transform = 22, // UNUSED + kTex1_Row0 = 22, + kTex1_Row1 = 23, + kTex1_Row2 = 24, + kLocalToWorld = 25, + kL2WRow0 = 26, + kL2WRow1 = 27, + kL2WRow2 = 28, + kLengths = 29, + kWaterLevel = 30, + kDepthFalloff = 31, + kMinAtten = 32, + + kNumConsts + }; +}; + +namespace plGraphVS +{ + enum { + kNumericConsts = 0, + kFrequency = 1, + kPhase = 2, + kAmplitude = 3, + kPiConsts = 4, + kCosConsts = 5, + kUVWConsts = 6, + kColor = 7, + + kNumConsts + }; +}; + +namespace plGraphPS +{ + enum { + + kNumConsts + }; +}; + +namespace plFixedPS +{ + enum { + kDir0 = 0, + kDir1 = 1, + kDir2 = 2, + kDir3 = 3, + + kNumConsts + }; +}; + +namespace plBumpPS +{ + enum { + kWave0 = 0, + kWave1 = 1, + kWave2 = 2, + kWave3 = 3, + kHalfOne = 4, + kBias = 5, + + kNumConsts + }; +}; + +namespace plBumpVS +{ + enum { + kUXform0 = 0, + kUXform1 = 1, + kUXform2 = 2, + kUXform3 = 3, + + kNumbers = 4, + + kNumConsts + }; +}; + +namespace plBiasPS +{ + enum { + + kNumConsts + }; +}; + +namespace plBiasVS +{ + enum { + kTexU0 = 0, + kTexV0 = 1, + + kTexU1 = 2, + kTexV1 = 3, + + kNumbers = 4, + + kScaleBias = 5, + + kNumConsts + }; +}; + +namespace plCompPS +{ + enum { + + + kNumConsts + }; +}; + +namespace plCompVS +{ + enum { + kTex0Transform = 0, + kTex0_Row0 = 0, + kTex0_Row1 = 1, + kTex1Transform = 2, + kTex1_Row0 = 2, + kTex1_Row1 = 3, + kTex2Transform = 4, + kTex2_Row0 = 4, + kTex2_Row1 = 5, + kTex3Transform = 6, + kTex3_Row0 = 6, + kTex3_Row1 = 7, + + kNumbers = 8, + + kNumConsts + }; +}; + +namespace plRipVS // closely related to plFixedVS and plShoreVS +{ + enum { + kWorldToNDC = 0, + kProjRow0 = 0, + kProjRow1 = 1, + kProjRow2 = 2, + kProjRow3 = 3, + kFogSet = 4, + kFrequency = 5, + kPhase = 6, + kAmplitude = 7, + kDirectionX = 8, + kDirectionY = 9, + kQADirX = 10, + kQADirY = 11, + kScrunch = 12, // UNUSED + kSinConsts = 13, + kCosConsts = 14, + kPiConsts = 15, + kNumericConsts = 16, + kCameraPos = 17, + kWindRot = 18, // UNUSED + kTex0Transform = 19, // UNUSED + kTex0_Row0 = 19, + kTex0_Row1 = 20, + kTex0_Row2 = 21, + kTex1Transform = 22, // UNUSED + kTex1_Row0 = 22, + kTex1_Row1 = 23, + kTex1_Row2 = 24, + kLocalToWorld = 25, + kL2WRow0 = 26, + kL2WRow1 = 27, + kL2WRow2 = 28, + kLengths = 29, + kWaterLevel = 30, + kDepthFalloff = 31, + kMinAtten = 32, + kTexConsts = 33, + kLifeConsts = 34, + kRampBias = 35, + + kNumConsts + }; +}; + +namespace plWaveDecVS // closely related to plFixedVS and plShoreVS +{ + enum { + kWorldToNDC = 0, + kProjRow0 = 0, + kProjRow1 = 1, + kProjRow2 = 2, + kProjRow3 = 3, + kFrequency = 4, + kPhase = 5, + kAmplitude = 6, + kDirectionX = 7, + kDirectionY = 8, + kScrunch = 9, // UNUSED + kSinConsts = 10, + kCosConsts = 11, + kPiConsts = 12, + kNumericConsts = 13, + kTex0Transform = 14, + kTex0_Row0 = 14, + kTex0_Row1 = 15, + kTex1Transform = 16, + kTex1_Row0 = 16, + kTex1_Row1 = 17, + kLocalToWorld = 18, + kL2WRow0 = 18, + kL2WRow1 = 19, + kL2WRow2 = 20, + kLengths = 21, + kWaterLevel = 22, + kDepthFalloff = 23, + kMinAtten = 24, + kBias = 25, // Only using one slot + kMatColor = 26, + kCameraPos = 27, // Only used by DecalEnv + kEnvAdjust = 28, // Only used by DecalEnv + kFogSet = 29, + kQADirX = 30, + kQADirY = 31, + + kDirXW = 32, // Only used by DecalEnv + kDirYW = 33, // Only used by DecalEnv + kWK = 34, // Only used by DecalEnv + kDirXSqKW = 35, // Only used by DecalEnv + kDirXDirYKW = 36, // Only used by DecalEnv + kDirYSqKW = 37, // Only used by DecalEnv + + kNumConsts + }; +}; + + +#endif // plWaveSetShaderConsts_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plEncryption/plChecksum.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plEncryption/plChecksum.cpp new file mode 100644 index 00000000..4d1d8080 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plEncryption/plChecksum.cpp @@ -0,0 +1,197 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plChecksum.h" +#include "hsStream.h" + + +plChecksum::plChecksum(unsigned int bufsize, const char* buffer) +{ + unsigned int wndsz = GetWindowSize(),i = 0; + fSum = 0; + + const char* bufferAbsEnd = buffer + bufsize; + const char* bufferEnvenEnd = buffer + bufsize - (bufsize % wndsz); + + while (buffer < bufferEnvenEnd) + { + fSum += hsSWAP32(*((SumStorage*)buffer)); + buffer += wndsz; + } + + SumStorage last = 0; + while (buffer < bufferAbsEnd) + { + ((char*)&last)[i % wndsz] = *buffer; + buffer++; + } + fSum+= hsSWAP32(last); +} + +plMD5Checksum::plMD5Checksum( UInt32 size, UInt8 *buffer ) +{ + fValid = false; + Start(); + AddTo( size, buffer ); + Finish(); +} + +plMD5Checksum::plMD5Checksum() +{ + Clear(); +} + +plMD5Checksum::plMD5Checksum( const plMD5Checksum &rhs ) +{ + memcpy( fChecksum, rhs.fChecksum, sizeof( fChecksum ) ); + fValid = rhs.fValid; +} + +plMD5Checksum::plMD5Checksum( const char *fileName ) +{ + CalcFromFile( fileName ); +} + +void plMD5Checksum::Clear() +{ + memset( fChecksum, 0, sizeof( fChecksum ) ); + fValid = false; +} + +void plMD5Checksum::CalcFromFile( const char *fileName) +{ + FILE *fp; + fValid = false; + + if( fp = fopen(fileName, "rb" ) ) + { + unsigned loadLen = 1024 * 1024; + Start(); + + UInt8 *buf = TRACKED_NEW UInt8[loadLen]; + + while(int read = fread(buf, sizeof(UInt8), loadLen, fp)) + AddTo( read, buf ); + delete[] buf; + + Finish(); + fclose(fp); + } +} + +void plMD5Checksum::Start( void ) +{ + MD5_Init( &fContext ); + fValid = false; +} + +void plMD5Checksum::AddTo( UInt32 size, const UInt8 *buffer ) +{ + MD5_Update( &fContext, buffer, size ); +} + +void plMD5Checksum::Finish( void ) +{ + MD5_Final( fChecksum, &fContext ); + fValid = true; +} + +const char *plMD5Checksum::GetAsHexString( void ) const +{ + const int kHexStringSize = ( 2 * MD5_DIGEST_LENGTH ) + 1; + static char tempString[ kHexStringSize ]; + + int i; + char *ptr; + + + hsAssert( fValid, "Trying to get string version of invalid checksum" ); + + for( i = 0, ptr = tempString; i < sizeof( fChecksum ); i++, ptr += 2 ) + sprintf( ptr, "%02x", fChecksum[ i ] ); + + *ptr = 0; + + return tempString; +} + +UInt8 plMD5Checksum::IHexCharToInt( char c ) const +{ + switch( c ) + { + // yes, it's ugly, but it'll be fast :) + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + + case 'a': return 10; + case 'b': return 11; + case 'c': return 12; + case 'd': return 13; + case 'e': return 14; + case 'f': return 15; + + case 'A': return 10; + case 'B': return 11; + case 'C': return 12; + case 'D': return 13; + case 'E': return 14; + case 'F': return 15; + } + + return 0xff; +} + +void plMD5Checksum::SetFromHexString( const char *string ) +{ + const char *ptr; + int i; + + + hsAssert( strlen( string ) == 2 * MD5_DIGEST_LENGTH, "Invalid string in MD5Checksum Set()" ); + + for( i = 0, ptr = string; i < sizeof( fChecksum ); i++, ptr += 2 ) + fChecksum[ i ] = ( IHexCharToInt( ptr[ 0 ] ) << 4 ) | IHexCharToInt( ptr[ 1 ] ); + + fValid = true; +} + +void plMD5Checksum::SetValue(UInt8* checksum) +{ + fValid = true; + memcpy(fChecksum, checksum, sizeof(fChecksum)); +} + +bool plMD5Checksum::operator==( const plMD5Checksum &rhs ) const +{ + return (fValid && rhs.fValid && memcmp(fChecksum, rhs.fChecksum, sizeof(fChecksum)) == 0); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plEncryption/plChecksum.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plEncryption/plChecksum.h new file mode 100644 index 00000000..8cb01c96 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plEncryption/plChecksum.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_CHECKSUM_H +#define PL_CHECKSUM_H + +#include "hsTypes.h" +#include "../../../../../StaticSDKs/Win32/OpenSSL/include/openssl/md5.h" + +class plChecksum +{ +public: + typedef UInt32 SumStorage; +private: + SumStorage fSum; +public: + plChecksum(unsigned int bufsize, const char* buffer); + static int GetChecksumSize() { return sizeof(SumStorage); } + static int GetWindowSize() { return sizeof(SumStorage); } + SumStorage GetChecksum() { return fSum; } +}; + +class plMD5Checksum +{ + protected: + + hsBool fValid; + MD5_CTX fContext; + UInt8 fChecksum[ MD5_DIGEST_LENGTH ]; + + UInt8 IHexCharToInt( char c ) const; + + public: + + plMD5Checksum( UInt32 size, UInt8 *buffer ); + plMD5Checksum(); + plMD5Checksum( const plMD5Checksum &rhs ); + plMD5Checksum( const char *fileName ); + + hsBool IsValid( void ) const { return fValid; } + void Clear(); + + void CalcFromFile( const char *fileName ); + + void Start( void ); + void AddTo( UInt32 size, const UInt8 *buffer ); + void Finish( void ); + + const UInt8 *GetValue( void ) const { return fChecksum; } + UInt32 GetSize( void ) const { return sizeof( fChecksum ); } + + // Backdoor for cached checksums (ie, if you loaded it off disk) + void SetValue(UInt8* checksum); + + // Note: GetAsHexString() returns a pointer to a static string; do not rely on the contents of this string between calls! + const char *GetAsHexString( void ) const; + void SetFromHexString( const char *string ); + + bool operator==( const plMD5Checksum &rhs ) const; + bool operator!=( const plMD5Checksum &rhs ) const { return !operator==( rhs ); } +}; +#endif // PL_CHECKSUM_H + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles.cpp new file mode 100644 index 00000000..29469acf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles.cpp @@ -0,0 +1,168 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsFiles.h" +#include +#include "hsUtils.h" + +#include "hsExceptions.h" + +#if HS_BUILD_FOR_MAC + #define kDirChar ':' +#elif HS_BUILD_FOR_WIN32 + #define kDirChar '\\' +#else + #define kDirChar '/' +#endif + + +static const char* FindNameInPath(const char path[]) +{ + const char* name = ::strrchr(path, kDirChar); + + if (name == nil) + name = path; + return name; +} + +/////////////////////////////////////////////////////////////////////// +#if !HS_BUILD_FOR_PS2 + +hsFile::hsFile() : fPathAndName(nil), fFILE(nil) +{ +} + +hsFile::hsFile(const char pathAndName[]) : fPathAndName(nil), fFILE(nil) +{ + if (pathAndName) + fPathAndName = hsStrcpy(pathAndName); +} + +hsFile::~hsFile() +{ + this->SetPathAndName(nil); +} + +const char* hsFile::GetPathAndName() +{ + return fPathAndName; +} + +void hsFile::SetPathAndName(const char pathAndName[]) +{ + this->Close(); + + if (fPathAndName) + { delete[] fPathAndName; + fPathAndName = nil; + } + if (pathAndName) + fPathAndName = hsStrcpy(pathAndName); +} + +const char* hsFile::GetName() +{ + return FindNameInPath(this->GetPathAndName()); +} + +FILE* hsFile::OpenFILE(const char mode[], hsBool throwIfFailure) +{ + this->Close(); + + // We call the virtual method here rather than using + // fPathAndName directly, allowing a subclass to construct + // the name if necessary + // + const char* name = this->GetPathAndName(); + if (name) + fFILE = ::fopen(name, mode); + + hsThrowIfTrue(throwIfFailure && fFILE == nil); + return fFILE; +} + +hsStream* hsFile::OpenStream(const char mode[], hsBool throwIfFailure) +{ + FILE* file = this->OpenFILE(mode, throwIfFailure); + + if (file) + { hsUNIXStream* stream = TRACKED_NEW hsUNIXStream; + stream->SetFILE(file); + return stream; + } + return nil; +} + +void hsFile::Close() +{ + if (fFILE) + { int err = ::fflush(fFILE); + hsIfDebugMessage(err != 0, "fflush failed", err); + err = ::fclose(fFILE); + hsIfDebugMessage(err != 0, "fclose failed", err); + fFILE = nil; + } +} +#endif + +/////////////////////////////////////////////////////////////////////// + +hsBool hsFolderIterator::NextFileSuffix(const char suffix[]) +{ + while (this->NextFile()) + { const char* fileSuffix = ::strrchr(this->GetFileName(), '.'); + if (fileSuffix != nil && ::_stricmp(fileSuffix, suffix) == 0) + return true; + } + return false; +} + +int hsFolderIterator::GetPathAndName(char pathandname[]) +{ + const char* name = this->GetFileName(); + int pathLen = hsStrlen(fPath); + + // add 1 for null terminator + int totalLen = pathLen + sizeof(kDirChar) + hsStrlen(name) + 1; + hsAssert(totalLen <= kFolderIterator_MaxPath, "Overrun kFolderIterator_MaxPath"); + + if (pathandname) + { hsStrcpy(pathandname, fPath); + if (pathLen > 0 && pathandname[pathLen - 1] != kDirChar) + pathandname[pathLen++] = kDirChar; + hsStrcpy(pathandname + pathLen, name); + } + return totalLen; +} + +FILE* hsFolderIterator::OpenFILE(const char mode[]) +{ + char fileName[kFolderIterator_MaxPath]; + + (void)this->GetPathAndName(fileName); + + return ::fopen(fileName, mode); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles.h new file mode 100644 index 00000000..f701d65f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles.h @@ -0,0 +1,188 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsFiles_Defined +#define hsFiles_Defined + +#include "hsStream.h" +#include + +#if HS_BUILD_FOR_UNIX + #include + #define kFolderIterator_MaxPath PATH_MAX + #include + #define SetCurrentDirectory chdir +#elif !HS_BUILD_FOR_PS2 + #define kFolderIterator_MaxPath _MAX_PATH +#else + #define kFolderIterator_MaxPath 255 +#endif + +#if HS_BUILD_FOR_MAC + #include + #include +#endif + + +#if HS_BUILD_FOR_WIN32 +# define PATH_SEPARATOR '\\' +# define WPATH_SEPARATOR L'\\' +# define PATH_SEPARATOR_STR "\\" +# define WPATH_SEPARATOR_STR L"\\" +#elif HS_BUILD_FOR_UNIX +# define PATH_SEPARATOR '/' +# define WPATH_SEPARATOR L'/' +# define PATH_SEPARATOR_STR "/" +# define WPATH_SEPARATOR_STR L"/" +#endif + + +/////////////////////////////////////////////////////////////////////// +#if !HS_BUILD_FOR_PS2 + +class hsFile { + hsFile& operator=(const hsFile&); // disallow assignment +protected: + char* fPathAndName; + FILE* fFILE; +public: + hsFile(); + hsFile(const char pathAndName[]); + virtual ~hsFile(); + + const char* GetName(); + virtual const char* GetPathAndName(); + virtual void SetPathAndName(const char pathAndName[]); + + virtual FILE* OpenFILE(const char mode[], hsBool throwIfFailure = false); + virtual hsStream* OpenStream(const char mode[], hsBool throwIfFailure = false); + + virtual void Close(); // called automatically in the destructor +}; +typedef hsFile hsUnixFile; // for compatibility + +#if HS_BUILD_FOR_MAC + class hsMacFile : public hsFile { + enum { + kRefNum_Dirty, + kPathName_Dirty + }; + FSSpec fSpec; + Int16 fRefNum; + UInt16 fFlags; + + void SetSpecFromName(); + void SetNameFromSpec(); + public: + hsMacFile(); + hsMacFile(const FSSpec* spec); + hsMacFile(const char pathAndName[]); + virtual ~hsMacFile(); + + const FSSpec* GetSpec() const { return &fSpec; } + void SetSpec(const FSSpec* spec); + hsBool Create(OSType creator, OSType fileType, ScriptCode scriptCode = smSystemScript); + hsBool OpenDataFork(SInt8 permission, Int16* refnum); + + // Overrides + virtual const char* GetPathAndName(); + virtual void SetPathAndName(const char pathAndName[]); + virtual hsStream* OpenStream(const char mode[], hsBool throwIfFailure = false); + virtual void Close(); + }; + typedef hsMacFile hsOSFile; +#else + typedef hsFile hsOSFile; +#endif +#endif // HS_BUILD_FOR_PS2 +/////////////////////////////////////////////////////////////////////// + +class hsFolderIterator { + char fPath[kFolderIterator_MaxPath]; + struct hsFolderIterator_Data* fData; + bool fCustomFilter; +public: +#ifdef HS_BUILD_FOR_WIN32 + hsFolderIterator(const char path[] = nil, bool useCustomFilter=false); +#else + hsFolderIterator(const char path[] = nil, bool unused=true); + hsFolderIterator(const struct FSSpec* spec); // Alt constructor +#endif + virtual ~hsFolderIterator(); + + const char* GetPath() const { return fPath; } + void SetPath(const char path[]); + + void Reset(); + hsBool NextFile(); + hsBool NextFileSuffix(const char suffix[]); + const char* GetFileName() const; + int GetPathAndName(char pathandname[] = nil); + hsBool IsDirectory( void ) const; + + FILE* OpenFILE(const char mode[]); + +#if HS_BUILD_FOR_MAC + void SetMacFolder(const char path[]); + void SetMacFolder(OSType folderType); + void SetMacFolder(Int16 vRefNum, Int32 dirID); + hsBool NextMacFile(OSType targetFileType, OSType targetCreator); + const struct FSSpec* GetMacSpec() const; + OSType GetMacFileType() const; + OSType GetMacCreator() const; +#elif HS_BUILD_FOR_WIN32 + void SetWinSystemDir(const char subdir[]); // e.g. "Fonts" + void SetFileFilterStr(const char filterStr[]); // e.g. "*.*" +#endif +}; + +#ifdef HS_BUILD_FOR_WIN32 +// only implemented on Win32 for now +class hsWFolderIterator { + wchar fPath[kFolderIterator_MaxPath]; + struct hsWFolderIterator_Data* fData; + bool fCustomFilter; +public: + hsWFolderIterator(const wchar path[] = nil, bool useCustomFilter=false); + virtual ~hsWFolderIterator(); + + const wchar* GetPath() const { return fPath; } + void SetPath(const wchar path[]); + + void Reset(); + hsBool NextFile(); + hsBool NextFileSuffix(const wchar suffix[]); + const wchar* GetFileName() const; + int GetPathAndName(wchar pathandname[] = nil); + hsBool IsDirectory( void ) const; + + FILE* OpenFILE(const wchar mode[]); + + void SetWinSystemDir(const wchar subdir[]); // e.g. "Fonts" + void SetFileFilterStr(const wchar filterStr[]); // e.g. "*.*" +}; +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Mac.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Mac.cpp new file mode 100644 index 00000000..b6246393 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Mac.cpp @@ -0,0 +1,393 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsFiles.h" +#include "hsUtils.h" +#include "hsMemory.h" + +#if HS_BUILD_FOR_MAC + +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + +hsMacFile::hsMacFile() : fFlags(kRefNum_Dirty) +{ + fSpec.name[0] = 0; +} + +hsMacFile::hsMacFile(const char pathAndName[]) : hsFile(pathAndName), fFlags(kRefNum_Dirty) +{ + this->SetSpecFromName(); +} + +hsMacFile::hsMacFile(const FSSpec* spec) : fFlags(kRefNum_Dirty) +{ + this->SetSpec(spec); +} + +hsMacFile::~hsMacFile() +{ + this->Close(); +} + +void hsMacFile::SetSpec(const FSSpec* spec) +{ + if (spec) + fSpec = *spec; + else + fSpec.name[0] = 0; + fFlags |= kPathName_Dirty; +} + +void hsMacFile::SetSpecFromName() +{ + Str255 pstr; + + if (fPathAndName == nil) + fSpec.name[0] = 0; + else + { hsC2PString(fPathAndName, pstr); + ::FSMakeFSSpec(0, 0, pstr, &fSpec); + } +} + +void hsMacFile::SetNameFromSpec() +{ + CInfoPBRec pb; + Str255 dirNameP; + char dirName[256], temp[256]; + int err; + + hsP2CString(fSpec.name, temp); + + pb.dirInfo.ioNamePtr = dirNameP; + pb.dirInfo.ioVRefNum = fSpec.vRefNum; + pb.dirInfo.ioDrParID = fSpec.parID; + pb.dirInfo.ioFDirIndex = -1; + + do { + pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID; + err = PBGetCatInfoSync(&pb); + hsThrowIfOSErr(err); + + hsP2CString(dirNameP, dirName); + strcat(dirName,":"); + strcat(dirName, temp); + strcpy(temp, dirName); + } while( pb.dirInfo.ioDrDirID != fsRtDirID); + + hsAssert(fPathAndName == nil, "pathname should be nil"); + fPathAndName = hsStrcpy(temp); +} + +hsBool hsMacFile::Create(OSType creator, OSType fileType, ScriptCode scriptCode) +{ + this->Close(); + + OSErr err; + + (void)::FSpDelete(&fSpec); + err = ::FSpCreate(&fSpec, creator, fileType, scriptCode); + hsIfDebugMessage(err != 0, "FSpCreate failed", err); + + return err == 0; +} + +#define kFileNotFound_Err -43 + +hsBool hsMacFile::OpenDataFork(SInt8 perm, Int16* refnum) +{ + this->Close(); + + OSErr err; + + err = ::FSpOpenDF(&fSpec, perm, &fRefNum); + if (err == kFileNotFound_Err && (perm & fsWrPerm) && (perm & fsRdPerm) == 0) + { if (this->Create('HdSp', '????')) + err = ::FSpOpenDF(&fSpec, perm, &fRefNum); + } + + if (err == 0) + { fFlags &= ~kRefNum_Dirty; + if (refnum) + *refnum = fRefNum; + return true; + } + return false; +} + +const char* hsMacFile::GetPathAndName() +{ + if (fFlags & kPathName_Dirty) + { this->SetNameFromSpec(); + fFlags &= ~kPathName_Dirty; + } + return fPathAndName; +} + +void hsMacFile::SetPathAndName(const char pathAndName[]) +{ + this->hsFile::SetPathAndName(pathAndName); + this->SetSpecFromName(); +} + +hsStream* hsMacFile::OpenStream(const char mode[], hsBool throwIfFailure) +{ + hsThrowIfNilParam(mode); + + short refnum; + SInt8 perm = 0; + + if (::strchr(mode, 'r')) + perm |= fsRdPerm; + if (::strchr(mode, 'w')) + perm |= fsWrPerm; + + if (this->OpenDataFork(perm, &refnum)) + { hsFileStream* stream = TRACKED_NEW hsFileStream; + stream->SetFileRef(refnum); + return stream; + } + + hsThrowIfTrue(throwIfFailure); + return nil; +} + +void hsMacFile::Close() +{ + if (fFlags & kRefNum_Dirty) + { OSErr err = ::FSClose(fRefNum); + hsIfDebugMessage(err != 0, "FSClose failed", err); + fFlags &= ~kRefNum_Dirty; + } + this->hsFile::Close(); +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +struct hsFolderIterator_Data { + FSSpec fSpec; + OSType fFileType; + OSType fCreator; + char fCName[_MAX_PATH]; + Int16 fCurrIndex; + hsBool fValid; +}; + +hsFolderIterator::hsFolderIterator(const char path[]) +{ + fData = TRACKED_NEW hsFolderIterator_Data; + + fData->fCurrIndex = 0; + fData->fValid = false; +#if HS_BUILD_FOR_WIN32 + this->SetPath(path); +#else + this->SetMacFolder(path); +#endif +} + +hsFolderIterator::hsFolderIterator(const struct FSSpec* spec) // Alt Constructor - pass in FSSpec from OpenDlg() +{ + fData = TRACKED_NEW hsFolderIterator_Data; + + fData->fCurrIndex = 0; + fData->fValid = false; + + SetMacFolder(spec->vRefNum, spec->parID); +} + +hsFolderIterator::~hsFolderIterator() +{ + delete fData; +} + +void hsFolderIterator::SetPath(const char path[]) +{ + fPath[0] = 0; + fData->fValid = false; + fData->fCurrIndex = 0; + + if (path) + { + ::strcpy(fPath, path); + } +} + +/////////////////////////////////////////////////////////////////////////// + +void hsFolderIterator::SetMacFolder(OSType folderType) +{ + fData->fCurrIndex = 0; + fData->fValid = ::FindFolder(kOnSystemDisk, folderType, false, + &fData->fSpec.vRefNum, &fData->fSpec.parID) == 0; + this->Reset(); +} + +void hsFolderIterator::SetMacFolder(Int16 vRefNum, Int32 dirID) +{ + fData->fSpec.vRefNum = vRefNum; + fData->fSpec.parID = dirID; + fData->fCurrIndex = 0; + fData->fValid = true; + this->Reset(); +} + +void hsFolderIterator::SetMacFolder(const char path[]) +{ + char tmp[255]; + FSSpec fileSpec; + OSErr err; + + hsCPathToMacPath(&tmp[1], (char*)path); + tmp[0] = hsStrlen(&tmp[1]); + SetPath((char*)&tmp[1]); + + err = FSMakeFSSpec(0, 0, (const unsigned char*)tmp, &fileSpec); + if(err == fnfErr) + { + HSDebugProc("XCmd directory does not exist."); + return; + } + hsAssert(err == noErr, "Error making file spec."); + + // by now we should have the file spec for the given + // directory, however, the DirID is the PARENT directory, + // not the child directory. The following steps should + // give us the items we want. + + CInfoPBRec pb; + pb.hFileInfo.ioVRefNum = fileSpec.vRefNum; + pb.hFileInfo.ioNamePtr = (StringPtr)fileSpec.name; // The name of the child directory. + pb.hFileInfo.ioDirID = fileSpec.parID; // The ID of the parent directory. + pb.hFileInfo.ioFDirIndex = 0; + pb.hFileInfo.ioCompletion = 0; + + err = ::PBGetCatInfoSync(&pb); + hsAssert(err == noErr, "PBGetCatInfoSync() failure."); + + fData->fSpec.vRefNum = fileSpec.vRefNum; // Volume reference + fData->fSpec.parID = pb.dirInfo.ioDrDirID; // child directory ID (Finally!) + fData->fCurrIndex = 0; + fData->fValid = true; + + this->Reset(); +} + +/////////////////////////////////////////////////////////////////////////// + +void hsFolderIterator::Reset() +{ + if (fData->fValid) + fData->fCurrIndex = 1; +#ifdef HS_DEBUGGING + else + hsAssert(fData->fCurrIndex == 0, "bad currindex"); +#endif +} + +hsBool hsFolderIterator::NextFile() +{ + if (fData->fCurrIndex == 0) + return false; + + CInfoPBRec pb; + + do { + pb.hFileInfo.ioVRefNum = fData->fSpec.vRefNum; + pb.hFileInfo.ioNamePtr = (StringPtr)fData->fSpec.name; + pb.hFileInfo.ioDirID = fData->fSpec.parID; + pb.hFileInfo.ioFDirIndex = fData->fCurrIndex++; + + OSErr err = ::PBGetCatInfoSync(&pb); + if (err) + { + fData->fCurrIndex = 0; + return false; + } + } while (pb.hFileInfo.ioFlAttrib & ioDirMask); + + fData->fFileType = pb.hFileInfo.ioFlFndrInfo.fdType; + fData->fCreator = pb.hFileInfo.ioFlFndrInfo.fdCreator; + return true; +} + +const char* hsFolderIterator::GetFileName() const +{ + if (fData->fCurrIndex == 0) + throw "end of folder"; + + // Copy our filename (in pascal format) into a cstring and then return + HSMemory::BlockMove(&fData->fSpec.name[1], fData->fCName, fData->fSpec.name[0]); + fData->fCName[fData->fSpec.name[0]] = 0; + return fData->fCName; +} + +//////////////////////////////////////////////////////////////////////////// + +hsBool hsFolderIterator::NextMacFile(OSType targetFileType, OSType targetCreator) +{ + for (;;) + { if (this->NextFile() == false) + return false; + if ((targetFileType == 0 || targetFileType == this->GetMacFileType()) && + (targetCreator == 0 || targetCreator == this->GetMacCreator())) + return true; + } +} + +const FSSpec* hsFolderIterator::GetMacSpec() const +{ + if (fData->fCurrIndex == 0) + throw "end of folder"; + return &fData->fSpec; +} + + +OSType hsFolderIterator::GetMacFileType() const +{ + if (fData->fCurrIndex == 0) + throw "end of folder"; + return fData->fFileType; +} + +OSType hsFolderIterator::GetMacCreator() const +{ + if (fData->fCurrIndex == 0) + throw "end of folder"; + return fData->fCreator; +} + +hsBool hsFolderIterator::IsDirectory( void ) const +{ + hsAssert( false, "hsFolderIterator::IsDirectory() not defined on this platform!!!" ); + return false; +} + +#endif // HS_BUILD_FOR_MAC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_PS2.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_PS2.cpp new file mode 100644 index 00000000..e6258dc9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_PS2.cpp @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsFiles.h" + +#if HS_BUILD_FOR_PS2 + + + +hsFolderIterator::hsFolderIterator(const char path[]) +{ + hsAssert(0,"No folder Interator defined for PS2 -- yet"); +} + +hsFolderIterator::~hsFolderIterator() +{ + +} + +void hsFolderIterator::SetPath(const char path[]) +{ +} + + + +/////////////////////////////////////////////////////////////////////////////// + +void hsFolderIterator::Reset() +{ +} + +hsBool hsFolderIterator::NextFile() +{ +} + +const char* hsFolderIterator::GetFileName() const +{ +} + +hsBool hsFolderIterator::IsDirectory( void ) const +{ + hsAssert( false, "hsFolderIterator::IsDirectory() not defined on this platform!!!" ); + return false; +} + +#endif // HS_BUILD_FOR_PS2 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Unix.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Unix.cpp new file mode 100644 index 00000000..c85a5ec0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Unix.cpp @@ -0,0 +1,130 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsFiles.h" + +#if HS_BUILD_FOR_UNIX + +#include +#include +#include +#include +#include +#include +#include +#include "hsTemplates.h" +#include "plFileUtils.h" +#include "hsStlUtils.h" + +struct hsFolderIterator_Data { + glob_t fGlobBuf; + bool fInited; + int fCnt; + hsFolderIterator_Data() : fInited(false), fCnt(0) {} + // ~hsFolderIterator_Data() { fInited=false; globfree(&fData->fGlobBuf); } +}; + +hsFolderIterator::hsFolderIterator(const char path[], bool) +{ + fData = TRACKED_NEW hsFolderIterator_Data; + + this->SetPath(path); +} + +hsFolderIterator::~hsFolderIterator() +{ + this->Reset(); + delete fData; +} + +void hsFolderIterator::SetPath(const char path[]) +{ + fPath[0] = 0; + if (path) + { + ::strcpy(fPath, path); + } + this->Reset(); +} + +void hsFolderIterator::Reset() +{ + if (fData->fInited) + { + globfree(&fData->fGlobBuf); + fData->fCnt = 0; + fData->fInited=false; + } +} +hsBool hsFolderIterator::NextFile() +{ + if (fData->fInited == false) + { + std::string path=fPath; + if(!(strchr(fPath,'*') || strchr(fPath,'?') || strchr(fPath,'['))) + { + if (fPath[strlen(fPath)-1] != PATH_SEPARATOR) + path = path + PATH_SEPARATOR_STR + "*"; + else + path = path + "*"; + } + + if(glob(path.c_str(), 0, NULL, &fData->fGlobBuf) != 0 ) { + return false; + } + fData->fInited=true; + fData->fCnt = 0; + } + + return fData->fCnt++ < fData->fGlobBuf.gl_pathc; +} + +const char* hsFolderIterator::GetFileName() const +{ + if (!fData->fInited || fData->fCnt > fData->fGlobBuf.gl_pathc) + throw "end of folder"; + + const char* fn=fData->fGlobBuf.gl_pathv[fData->fCnt-1]; + return plFileUtils::GetFileName(fn); +} + +hsBool hsFolderIterator::IsDirectory( void ) const +{ + // rob, please forgive me, this is my best attempt... + if(fData->fCnt > fData->fGlobBuf.gl_pathc ) + return false; + + struct stat info; + const char* fn=fData->fGlobBuf.gl_pathv[fData->fCnt-1]; + if( stat( fn, &info ) ) + { + printf("Error calling stat(): %s errno=%d\n", strerror(errno), errno); + return false; + } + return ( info.st_mode & S_IFDIR ) ? true : false; +} + +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Win.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Win.cpp new file mode 100644 index 00000000..1e034747 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/hsFiles_Win.cpp @@ -0,0 +1,304 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsFiles.h" + +#if HS_BUILD_FOR_WIN32 + +#include + +#include "hsExceptions.h" + +struct hsFolderIterator_Data { + HANDLE fSearchHandle; + WIN32_FIND_DATA fFindData; + Boolean fValid; +}; + +hsFolderIterator::hsFolderIterator(const char path[], bool useCustomFilter) +{ + fCustomFilter = useCustomFilter; + + fData = TRACKED_NEW hsFolderIterator_Data; + fData->fSearchHandle = nil; + fData->fValid = true; + + if(useCustomFilter) + { + this->SetFileFilterStr(path); + } + else + { + this->SetPath(path); + } +} + +hsFolderIterator::~hsFolderIterator() +{ + delete fData; +} + +void hsFolderIterator::SetPath(const char path[]) +{ + fCustomFilter = false; + fPath[0] = 0; + if (path) + { + ::strcpy(fPath, path); + + // Make sure the dir ends with a slash + char lastchar = fPath[strlen(fPath)-1]; + if (lastchar != '\\' && lastchar != '/') + strcat(fPath, "\\"); + } + + this->Reset(); +} + +void hsFolderIterator::SetWinSystemDir(const char subdir[]) +{ + int ret = GetWindowsDirectory(fPath, _MAX_PATH); + hsAssert(ret != 0, "Error getting windows directory in UseWindowsFontsPath"); + + if (subdir) + { ::strcat(fPath, "\\"); + ::strcat(fPath, subdir); + ::strcat(fPath, "\\"); + } + this->Reset(); +} + +void hsFolderIterator::SetFileFilterStr(const char filterStr[]) +{ + fPath[0] = 0; + if (filterStr) + { + fCustomFilter = true; + ::strcpy(fPath, filterStr); + } + + this->Reset(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void hsFolderIterator::Reset() +{ + if (fData->fSearchHandle) + { FindClose(fData->fSearchHandle); + fData->fSearchHandle = nil; + } + fData->fValid = true; +} + +hsBool hsFolderIterator::NextFile() +{ + if (fData->fValid == false) + return false; + + if (fData->fSearchHandle == nil) + { int len = ::strlen(fPath); + + if(fCustomFilter == false) + { + fPath[len] = '*'; + fPath[len+1] = 0; + } + + fData->fSearchHandle = FindFirstFile(fPath, &fData->fFindData); + fPath[len] = 0; + + if (fData->fSearchHandle == INVALID_HANDLE_VALUE) + { fData->fSearchHandle = nil; + fData->fValid = false; + return false; + } + } + else + { if (FindNextFile(fData->fSearchHandle, &fData->fFindData) == false) + { FindClose(fData->fSearchHandle); + fData->fSearchHandle = nil; + fData->fValid = false; + return false; + } + } + + return true; +} + +hsBool hsFolderIterator::IsDirectory( void ) const +{ + if( fData->fValid && ( fData->fFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) + return true; + + return false; +} + +const char* hsFolderIterator::GetFileName() const +{ + if (fData->fValid == false) + hsThrow( "end of folder"); + + return fData->fFindData.cFileName; +} + +/////////////////////////////////////////////////////////////////////////////// + +struct hsWFolderIterator_Data { + HANDLE fSearchHandle; + WIN32_FIND_DATAW fFindData; + Boolean fValid; +}; + +hsWFolderIterator::hsWFolderIterator(const wchar path[], bool useCustomFilter) +{ + fCustomFilter = useCustomFilter; + + fData = TRACKED_NEW hsWFolderIterator_Data; + fData->fSearchHandle = nil; + fData->fValid = true; + + if(useCustomFilter) + SetFileFilterStr(path); + else + SetPath(path); +} + +hsWFolderIterator::~hsWFolderIterator() +{ + delete fData; +} + +void hsWFolderIterator::SetPath(const wchar path[]) +{ + fCustomFilter = false; + fPath[0] = 0; + if (path) + { + wcscpy(fPath, path); + + // Make sure the dir ends with a slash + wchar lastchar = fPath[wcslen(fPath)-1]; + if (lastchar != L'\\' && lastchar != L'/') + wcscat(fPath, L"\\"); + } + + Reset(); +} + +void hsWFolderIterator::SetWinSystemDir(const wchar subdir[]) +{ + int ret = GetWindowsDirectoryW(fPath, _MAX_PATH); + hsAssert(ret != 0, "Error getting windows directory in UseWindowsFontsPath"); + + if (subdir) + { + wcscat(fPath, L"\\"); + wcscat(fPath, subdir); + wcscat(fPath, L"\\"); + } + Reset(); +} + +void hsWFolderIterator::SetFileFilterStr(const wchar filterStr[]) +{ + fPath[0] = 0; + if (filterStr) + { + fCustomFilter = true; + wcscpy(fPath, filterStr); + } + + Reset(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void hsWFolderIterator::Reset() +{ + if (fData->fSearchHandle) + { + FindClose(fData->fSearchHandle); + fData->fSearchHandle = nil; + } + fData->fValid = true; +} + +hsBool hsWFolderIterator::NextFile() +{ + if (fData->fValid == false) + return false; + + if (fData->fSearchHandle == nil) + { + int len = wcslen(fPath); + + if(fCustomFilter == false) + { + fPath[len] = L'*'; + fPath[len+1] = L'\0'; + } + + fData->fSearchHandle = FindFirstFileW(fPath, &fData->fFindData); + fPath[len] = 0; + + if (fData->fSearchHandle == INVALID_HANDLE_VALUE) + { + fData->fSearchHandle = nil; + fData->fValid = false; + return false; + } + } + else + { + if (FindNextFileW(fData->fSearchHandle, &fData->fFindData) == false) + { + FindClose(fData->fSearchHandle); + fData->fSearchHandle = nil; + fData->fValid = false; + return false; + } + } + + return true; +} + +hsBool hsWFolderIterator::IsDirectory( void ) const +{ + if( fData->fValid && ( fData->fFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) + return true; + + return false; +} + +const wchar* hsWFolderIterator::GetFileName() const +{ + if (fData->fValid == false) + hsThrow( "end of folder"); + + return fData->fFindData.cFileName; +} + +#endif // HS_BUILD_FOR_WIN32 \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plBrowseFolder.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plBrowseFolder.cpp new file mode 100644 index 00000000..ae3e4dbd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plBrowseFolder.cpp @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plBrowseFolder.h" + +#ifdef HS_BUILD_FOR_WIN32 + +#include + +bool plBrowseFolder::GetFolder(char *path, const char *startPath, const char *title, HWND hwndOwner) +{ + BROWSEINFO bi; + memset(&bi, 0, sizeof(bi)); + bi.hwndOwner = hwndOwner; + bi.lpszTitle = title; + bi.lpfn = BrowseCallbackProc; + bi.lParam = (LPARAM) startPath; + + ITEMIDLIST *iil = SHBrowseForFolder(&bi); + // Browse failed, or cancel was selected + if (!iil) + return false; + // Browse succeded. Get the path. + else + SHGetPathFromIDList(iil, path); + + // Free the memory allocated by SHBrowseForFolder + LPMALLOC pMalloc; + SHGetMalloc(&pMalloc); + pMalloc->Free(iil); + pMalloc->Release(); + + return true; +} + +int CALLBACK plBrowseFolder::BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + switch (uMsg) + { + case BFFM_INITIALIZED: + // lpData should be the lParam passed to SHBrowseForFolder, which is the start path. + if (lpData) + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData); + break; + } + + return 0; +} + +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plBrowseFolder.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plBrowseFolder.h new file mode 100644 index 00000000..a8791efe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plBrowseFolder.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plBrowseFolder_h_inc +#define plBrowseFolder_h_inc + +#include "hsConfig.h" + +#ifdef HS_BUILD_FOR_WIN32 +#include "hsWindows.h" + +// +// Gets a directory using the "Browse for Folder" dialog. +// +// path: Buffer to recieve the path. Should be MAX_PATH characters. +// startPath: Initial path. +// title: Not really the title of the dialog, but it's displayed above the +// folder list. Could be used to give instructions. +// hwndOwner: Owner window for dialog box. +// +// Returns true if path contains a valid path, false otherwise (error or user +// clicked cancel. +// + +class plBrowseFolder +{ +public: + static bool GetFolder(char *path, const char *startPath = NULL, const char *title = NULL, HWND hwndOwner = NULL); + +protected: + static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData); +}; + +#endif // HS_BUILD_FOR_WIN32 + +#endif // plBrowseFolder_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plEncryptedStream.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plEncryptedStream.cpp new file mode 100644 index 00000000..23366dcc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plEncryptedStream.cpp @@ -0,0 +1,577 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plEncryptedStream.h" +#include "hsUtils.h" +#include "plFileUtils.h" +#include "hsSTLStream.h" + +#include + +static const UInt32 kDefaultKey[4] = { 0x6c0a5452, 0x3827d0f, 0x3a170b92, 0x16db7fc2 }; +static const int kEncryptChunkSize = 8; + +static const char* kOldMagicString = "BriceIsSmart"; +static const char* kMagicString = "whatdoyousee"; +static const int kMagicStringLen = 12; + +static const int kFileStartOffset = kMagicStringLen + sizeof(UInt32); + +static const int kMaxBufferedFileSize = 10*1024; + +plEncryptedStream::plEncryptedStream(UInt32* key) : + fRef(nil), + fActualFileSize(0), + fBufferedStream(false), + fRAMStream(nil), + fWriteFileName(nil), + fOpenMode(kOpenFail) +{ + if (key) + memcpy(&fKey, key, sizeof(kDefaultKey)); + else + memcpy(&fKey, &kDefaultKey, sizeof(kDefaultKey)); +} + +plEncryptedStream::~plEncryptedStream() +{ +} + +// +// Tiny Encryption Algorithm +// http://vader.brad.ac.uk/tea/tea.shtml +// +// A potential weakness in this implementation is the fact that a known value +// (length of the original file) is written at the start of the encrypted file. -Colin +// +// Oh, and also there's some kind of potential weakness in TEA that they fixed with XTEA, +// but frankly, who cares. No one is going to break the encryption, they'll just get the +// key out of the exe or memory. +// +void plEncryptedStream::IEncipher(UInt32* const v) +{ + register unsigned long y=v[0], z=v[1], sum=0, delta=0x9E3779B9, n=32; + + while (n-- > 0) + { + y += (z << 4 ^ z >> 5) + z ^ sum + fKey[sum&3]; + sum += delta; + z += (y << 4 ^ y >> 5) + y ^ sum + fKey[sum>>11 & 3]; + } + + v[0]=y; v[1]=z; +} + +void plEncryptedStream::IDecipher(UInt32* const v) +{ + register unsigned long y=v[0], z=v[1], sum=0xC6EF3720, delta=0x9E3779B9, n=32; + + // sum = delta<<5, in general sum = delta * n + + while (n-- > 0) + { + z -= (y << 4 ^ y >> 5) + y ^ sum + fKey[sum>>11 & 3]; + sum -= delta; + y -= (z << 4 ^ z >> 5) + z ^ sum + fKey[sum&3]; + } + + v[0]=y; v[1]=z; +} + +hsBool plEncryptedStream::Open(const char* name, const char* mode) +{ + wchar* wName = hsStringToWString(name); + wchar* wMode = hsStringToWString(mode); + hsBool ret = Open(wName, wMode); + delete [] wName; + delete [] wMode; + return ret; +} + +hsBool plEncryptedStream::Open(const wchar* name, const wchar* mode) +{ + if (wcscmp(mode, L"rb") == 0) + { + fRef = _wfopen(name, mode); + fPosition = 0; + + if (!fRef) + return false; + + // Make sure our special magic string is there + if (!ICheckMagicString(fRef)) + { + fclose(fRef); + return false; + } + + fread(&fActualFileSize, sizeof(UInt32), 1, fRef); + + // The encrypted stream is inefficient if you do reads smaller than + // 8 bytes. Since we do a lot of those, any file under a size threshold + // is buffered in memory + if (fActualFileSize <= kMaxBufferedFileSize) + IBufferFile(); + + fOpenMode = kOpenRead; + + return true; + } + else if (wcscmp(mode, L"wb") == 0) + { + fRAMStream = TRACKED_NEW hsVectorStream; + fWriteFileName = TRACKED_NEW wchar[wcslen(name) + 1]; + wcscpy(fWriteFileName, name); + fPosition = 0; + + fOpenMode = kOpenWrite; + fBufferedStream = true; + + return true; + } + else + { + hsAssert(0, "Unsupported open mode"); + fOpenMode = kOpenFail; + return false; + } +} + +hsBool plEncryptedStream::Close() +{ + int rtn = false; + + if (fOpenMode == kOpenWrite) + { + fRAMStream->Rewind(); + rtn = IWriteEncypted(fRAMStream, fWriteFileName); + } + if (fRef) + { + rtn = (fclose(fRef) == 0); + fRef = nil; + } + + if (fRAMStream) + { + delete fRAMStream; + fRAMStream = nil; + } + + if (fWriteFileName) + { + delete [] fWriteFileName; + fWriteFileName = nil; + } + + fActualFileSize = 0; + fBufferedStream = false; + fOpenMode = kOpenFail; + + return rtn; +} + +UInt32 plEncryptedStream::IRead(UInt32 bytes, void* buffer) +{ + if (!fRef) + return 0; + int numItems = (int)(::fread(buffer, 1 /*size*/, bytes /*count*/, fRef)); + fBytesRead += numItems; + fPosition += numItems; + if ((unsigned)numItems < bytes) { + if (feof(fRef)) { + // EOF ocurred + char str[128]; + sprintf(str, "Hit EOF on UNIX Read, only read %d out of requested %d bytes\n", numItems, bytes); + hsDebugMessage(str, 0); + } + else { + hsDebugMessage("Error on UNIX Read", ferror(fRef)); + } + } + return numItems; +} + +void plEncryptedStream::IBufferFile() +{ + fRAMStream = TRACKED_NEW hsVectorStream; + char buf[1024]; + while (!AtEnd()) + { + UInt32 numRead = Read(1024, buf); + fRAMStream->Write(numRead, buf); + } + fRAMStream->Rewind(); + + fBufferedStream = true; + fclose(fRef); + fRef = nil; + fPosition = 0; +} + +hsBool plEncryptedStream::AtEnd() +{ + if (fBufferedStream) + return fRAMStream->AtEnd(); + else + return (GetPosition() == fActualFileSize); +} + +void plEncryptedStream::Skip(UInt32 delta) +{ + if (fBufferedStream) + { + fRAMStream->Skip(delta); + fPosition = fRAMStream->GetPosition(); + } + else if (fRef) + { + fBytesRead += delta; + fPosition += delta; + fseek(fRef, delta, SEEK_CUR); + } +} + +void plEncryptedStream::Rewind() +{ + if (fBufferedStream) + { + fRAMStream->Rewind(); + fPosition = fRAMStream->GetPosition(); + } + else if (fRef) + { + fBytesRead = 0; + fPosition = 0; + fseek(fRef, kFileStartOffset, SEEK_SET); + } +} + +void plEncryptedStream::FastFwd() +{ + if (fBufferedStream) + { + fRAMStream->FastFwd(); + fPosition = fRAMStream->GetPosition(); + } + else if (fRef) + { + fseek(fRef, kFileStartOffset+fActualFileSize, SEEK_SET); + fBytesRead = fPosition = ftell(fRef); + } +} + +UInt32 plEncryptedStream::GetEOF() +{ + return fActualFileSize; +} + +UInt32 plEncryptedStream::Read(UInt32 bytes, void* buffer) +{ + if (fBufferedStream) + { + UInt32 numRead = fRAMStream->Read(bytes, buffer); + fPosition = fRAMStream->GetPosition(); + return numRead; + } + + UInt32 startPos = fPosition; + + // Offset into the first buffer (0 if we are aligned on a chunk, which means no extra block read) + UInt32 startChunkPos = startPos % kEncryptChunkSize; + // Amount of data in the partial first chunk (0 if we're aligned) + UInt32 startAmt = (startChunkPos != 0) ? hsMinimum(kEncryptChunkSize - startChunkPos, bytes) : 0; + + UInt32 totalNumRead = IRead(bytes, buffer); + + UInt32 numMidChunks = (totalNumRead - startAmt) / kEncryptChunkSize; + UInt32 endAmt = (totalNumRead - startAmt) % kEncryptChunkSize; + + // If the start position is in the middle of a chunk we need to rewind and + // read that whole chunk in and decrypt it. + if (startChunkPos != 0) + { + // Move to the start of this chunk + SetPosition(startPos-startChunkPos); + + // Read in the chunk and decrypt it + char buf[kEncryptChunkSize]; + UInt32 numRead = IRead(kEncryptChunkSize, &buf); + IDecipher((UInt32*)&buf); + + // Copy the relevant portion to the output buffer + memcpy(buffer, &buf[startChunkPos], startAmt); + + SetPosition(startPos+totalNumRead); + } + + if (numMidChunks != 0) + { + UInt32* bufferPos = (UInt32*)(((char*)buffer)+startAmt); + for (int i = 0; i < numMidChunks; i++) + { + // Decrypt chunk + IDecipher(bufferPos); + bufferPos += (kEncryptChunkSize / sizeof(UInt32)); + } + } + + if (endAmt != 0) + { + // Read in the final chunk and decrypt it + char buf[kEncryptChunkSize]; + SetPosition(startPos + startAmt + numMidChunks*kEncryptChunkSize); + UInt32 numRead = IRead(kEncryptChunkSize, &buf); + IDecipher((UInt32*)&buf); + + memcpy(((char*)buffer)+totalNumRead-endAmt, &buf, endAmt); + + SetPosition(startPos+totalNumRead); + } + + // If we read into the padding at the end, update the total read to not include that + if (totalNumRead > 0 && startPos + totalNumRead > fActualFileSize) + { + totalNumRead -= (startPos + totalNumRead) - fActualFileSize; + SetPosition(fActualFileSize); + } + + return totalNumRead; +} + +UInt32 plEncryptedStream::Write(UInt32 bytes, const void* buffer) +{ + if (fOpenMode != kOpenWrite) + { + hsAssert(0, "Trying to write to a read stream"); + return 0; + } + + return fRAMStream->Write(bytes, buffer); +} + +bool plEncryptedStream::IWriteEncypted(hsStream* sourceStream, const wchar* outputFile) +{ + hsUNIXStream outputStream; + + if (!outputStream.Open(outputFile, L"wb")) + return false; + + outputStream.Write(kMagicStringLen, kMagicString); + + // Save some space to write the file size at the end + outputStream.WriteSwap32(0); + + // Write out all the full size encrypted blocks we can + char buf[kEncryptChunkSize]; + UInt32 amtRead; + while ((amtRead = sourceStream->Read(kEncryptChunkSize, &buf)) == kEncryptChunkSize) + { + IEncipher((UInt32*)&buf); + outputStream.Write(kEncryptChunkSize, &buf); + } + + // Pad with random data and write out the final partial block, if there is one + if (amtRead > 0) + { + static bool seededRand = false; + if (!seededRand) + { + seededRand = true; + srand((unsigned int)time(nil)); + } + + for (int i = amtRead; i < kEncryptChunkSize; i++) + buf[i] = rand(); + + IEncipher((UInt32*)&buf); + + outputStream.Write(kEncryptChunkSize, &buf); + } + + // Write the original file size at the start + UInt32 actualSize = sourceStream->GetPosition(); + outputStream.Rewind(); + outputStream.Skip(kMagicStringLen); + outputStream.WriteSwap32(actualSize); + + outputStream.Close(); + + return true; +} + +bool plEncryptedStream::FileEncrypt(const char* fileName) +{ + wchar* wFilename = hsStringToWString(fileName); + bool ret = FileEncrypt(wFilename); + delete [] wFilename; + return ret; +} + +bool plEncryptedStream::FileEncrypt(const wchar* fileName) +{ + hsUNIXStream sIn; + if (!sIn.Open(fileName)) + return false; + + // Don't double encrypt any files + if (ICheckMagicString(sIn.GetFILE())) + { + sIn.Close(); + return true; + } + sIn.Rewind(); + + plEncryptedStream sOut; + bool wroteEncrypted = sOut.IWriteEncypted(&sIn, L"crypt.dat"); + + sIn.Close(); + sOut.Close(); + + if (wroteEncrypted) + { + plFileUtils::RemoveFile(fileName); + plFileUtils::FileMove(L"crypt.dat", fileName); + } + + return true; +} + +bool plEncryptedStream::FileDecrypt(const char* fileName) +{ + wchar* wFilename = hsStringToWString(fileName); + bool ret = FileDecrypt(wFilename); + delete [] wFilename; + return ret; +} + +bool plEncryptedStream::FileDecrypt(const wchar* fileName) +{ + plEncryptedStream sIn; + if (!sIn.Open(fileName)) + return false; + + hsUNIXStream sOut; + if (!sOut.Open(L"crypt.dat", L"wb")) + { + sIn.Close(); + return false; + } + + char buf[1024]; + + while (!sIn.AtEnd()) + { + UInt32 numRead = sIn.Read(sizeof(buf), buf); + sOut.Write(numRead, buf); + } + + sIn.Close(); + sOut.Close(); + + plFileUtils::RemoveFile(fileName); + plFileUtils::FileMove(L"crypt.dat", fileName); + + return true; +} + +bool plEncryptedStream::ICheckMagicString(FILE* fp) +{ + char magicString[kMagicStringLen+1]; + fread(&magicString, kMagicStringLen, 1, fp); + magicString[kMagicStringLen] = '\0'; + return (hsStrEQ(magicString, kMagicString) || hsStrEQ(magicString, kOldMagicString)); +} + +bool plEncryptedStream::IsEncryptedFile(const char* fileName) +{ + wchar* wFilename = hsStringToWString(fileName); + bool ret = IsEncryptedFile(wFilename); + delete [] wFilename; + return ret; +} + +bool plEncryptedStream::IsEncryptedFile(const wchar* fileName) +{ + FILE* fp = _wfopen(fileName, L"rb"); + if (!fp) + return false; + + bool isEncrypted = ICheckMagicString(fp); + + fclose(fp); + + return isEncrypted; +} + +hsStream* plEncryptedStream::OpenEncryptedFile(const char* fileName, bool requireEncrypted, UInt32* cryptKey) +{ + wchar* wFilename = hsStringToWString(fileName); + hsStream* ret = OpenEncryptedFile(wFilename, requireEncrypted, cryptKey); + delete [] wFilename; + return ret; +} + +hsStream* plEncryptedStream::OpenEncryptedFile(const wchar* fileName, bool requireEncrypted, UInt32* cryptKey) +{ +#ifndef PLASMA_EXTERNAL_RELEASE + requireEncrypted = false; +#endif + + bool isEncrypted = IsEncryptedFile(fileName); + + hsStream* s = nil; + if (isEncrypted) + s = TRACKED_NEW plEncryptedStream(cryptKey); + // If this isn't an external release, let them use unencrypted data + else + if (!requireEncrypted) + s = TRACKED_NEW hsUNIXStream; + + if (s) + s->Open(fileName, L"rb"); + return s; +} + +hsStream* plEncryptedStream::OpenEncryptedFileWrite(const char* fileName, UInt32* cryptKey) +{ + wchar* wFilename = hsStringToWString(fileName); + hsStream* ret = OpenEncryptedFileWrite(wFilename, cryptKey); + delete [] wFilename; + return ret; +} + +hsStream* plEncryptedStream::OpenEncryptedFileWrite(const wchar* fileName, UInt32* cryptKey) +{ + hsStream* s = nil; +#ifdef PLASMA_EXTERNAL_RELEASE + s = TRACKED_NEW plEncryptedStream(cryptKey); +#else + s = TRACKED_NEW hsUNIXStream; +#endif + + s->Open(fileName, L"wb"); + return s; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plEncryptedStream.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plEncryptedStream.h new file mode 100644 index 00000000..348c6347 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plEncryptedStream.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plEncryptedStream_h_inc +#define plEncryptedStream_h_inc + +#include "hsStream.h" + +// +// Encrypt a large file by running FileEncrypt on it. Small files can be done +// in the usual way, but they will be in memory until Close is called. Files +// will be decrypted on the fly during read.operations +// +class plEncryptedStream : public hsStream +{ +protected: + FILE* fRef; + UInt32 fKey[4]; + + UInt32 fActualFileSize; + + bool fBufferedStream; + + hsStream* fRAMStream; + + wchar* fWriteFileName; + + enum OpenMode { kOpenRead, kOpenWrite, kOpenFail }; + OpenMode fOpenMode; + + void IBufferFile(); + + UInt32 IRead(UInt32 bytes, void* buffer); + + void IEncipher(UInt32* const v); + void IDecipher(UInt32* const v); + + bool IWriteEncypted(hsStream* sourceStream, const wchar* outputFile); + + static bool ICheckMagicString(FILE* fp); + +public: + // If you don't pass in a key (4 UInt32's), the default one will be used + plEncryptedStream(UInt32* key=nil); + ~plEncryptedStream(); + + virtual hsBool Open(const char* name, const char* mode = "rb"); + virtual hsBool Open(const wchar* name, const wchar* mode = L"rb"); + virtual hsBool Close(); + + virtual UInt32 Read(UInt32 byteCount, void* buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual hsBool AtEnd(); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void FastFwd(); + virtual UInt32 GetEOF(); + + UInt32 GetActualFileSize() const { return fActualFileSize;} + + static bool FileEncrypt(const char* fileName); + static bool FileEncrypt(const wchar* fileName); + static bool FileDecrypt(const char* fileName); + static bool FileDecrypt(const wchar* fileName); + + static bool IsEncryptedFile(const char* fileName); + static bool IsEncryptedFile(const wchar* fileName); + + // Attempts to create a read-binary stream for the requested file. If it's + // encrypted, you'll get a plEncryptedStream, otherwise just a standard + // hsUNIXStream. Remember to delete the stream when you're done with it. + static hsStream* OpenEncryptedFile(const char* fileName, bool requireEncrypted = true, UInt32* cryptKey = nil); + static hsStream* OpenEncryptedFile(const wchar* fileName, bool requireEncrypted = true, UInt32* cryptKey = nil); + static hsStream* OpenEncryptedFileWrite(const char* fileName, UInt32* cryptKey = nil); + static hsStream* OpenEncryptedFileWrite(const wchar* fileName, UInt32* cryptKey = nil); +}; + +#endif // plEncryptedStream_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plFileUtils.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plFileUtils.cpp new file mode 100644 index 00000000..b72615df --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plFileUtils.cpp @@ -0,0 +1,500 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +///////////////////////////////////////////////////////////////////////////// +// +// plFileUtils - Namespace of fun file utilities +// +//// History ///////////////////////////////////////////////////////////////// +// +// 5.7.2002 mcn - Created +// 4.8.2003 chip - added FileCopy and FileMove for Unix +// +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "plFileUtils.h" +#include "hsFiles.h" +#include "hsStringTokenizer.h" +#include "hsWindows.h" + +#include "../plUnifiedTime/plUnifiedTime.h" + +#include "plSecureStream.h" // for the default key + +#include +#include +#include + + +#if HS_BUILD_FOR_WIN32 +#include +#include +#endif + +#if HS_BUILD_FOR_UNIX +#include +#include +#include +#endif + + +//// CreateDir /////////////////////////////////////////////////////////////// +// Creates the directory specified. Returns false if unsuccessful or +// directory already exists + +hsBool plFileUtils::CreateDir( const char *path ) +{ + // Create our directory +#if HS_BUILD_FOR_WIN32 + return ( mkdir( path ) == 0 ) ? true : ( errno==EEXIST ); +#elif HS_BUILD_FOR_UNIX + return ( mkdir( path, 0777 ) == 0 ) ? true : ( errno==EEXIST ); +#endif +} + +hsBool plFileUtils::CreateDir( const wchar *path ) +{ + // Create our directory +#if HS_BUILD_FOR_WIN32 + return ( _wmkdir( path ) == 0 ) ? true : ( errno==EEXIST ); +#elif HS_BUILD_FOR_UNIX + return ( mkdir( path, 0777 ) == 0 ) ? true : ( errno==EEXIST ); +#endif +} + +hsBool plFileUtils::RemoveDir(const char* path) +{ + return (rmdir(path) == 0); +} + +hsBool plFileUtils::RemoveDirTree(const char * path) +{ + hsFolderIterator it(path); + while (it.NextFile()) + { + const char * fname = it.GetFileName(); + if ( fname[0]=='.' ) + continue; + char pathAndName[128]; + it.GetPathAndName(pathAndName); + if ( it.IsDirectory() ) + { + RemoveDirTree( pathAndName ); + RemoveDir( pathAndName ); + } + else + { + RemoveFile( pathAndName ); + } + } + RemoveDir( path ); + + return 1; +} + +//// RemoveFile //////////////////////////////////////////////////////////// + +bool plFileUtils::RemoveFile(const char* filename, bool delReadOnly) +{ + if (delReadOnly) + chmod(filename, S_IWRITE); + return (unlink(filename) == 0); +} + +bool plFileUtils::RemoveFile(const wchar* filename, bool delReadOnly) +{ + if (delReadOnly) + _wchmod(filename, S_IWRITE); + return (_wunlink(filename) == 0); +} + +bool plFileUtils::FileCopy(const char* existingFile, const char* newFile) +{ + wchar* wExisting = hsStringToWString(existingFile); + wchar* wNew = hsStringToWString(newFile); + bool ret = FileCopy(wExisting, wNew); + delete [] wExisting; + delete [] wNew; + return ret; +} + +bool plFileUtils::FileCopy(const wchar* existingFile, const wchar* newFile) +{ +#if HS_BUILD_FOR_WIN32 + return (::CopyFileW(existingFile, newFile, FALSE) != 0); +#elif HS_BUILD_FOR_UNIX + char data[1500]; + FILE* fp = fopen(existingFile, "rb"); + FILE* fw = fopen(newFile, "w"); + int num = 0; + bool retVal = true; + if (fp && fw){ + while(!feof(fp)){ + num = fread(data, sizeof( char ), 1500, fp); + if( ferror( fp ) ) { + retVal = false; + break; + } + fwrite(data, sizeof( char ), num, fw); + } + fclose(fp); + fclose(fw); + } else { + retVal = false; + } + return retVal; +#else + hsAssert(0, "Not implemented"); + return false; +#endif +} + +bool plFileUtils::FileMove(const char* existingFile, const char* newFile) +{ +#if HS_BUILD_FOR_WIN32 + return (::MoveFile(existingFile, newFile) != 0); +#elif HS_BUILD_FOR_UNIX + FileCopy(existingFile,newFile); + return( RemoveFile( existingFile )==0); +#else + hsAssert(0, "Not implemented"); + return false; +#endif +} + +bool plFileUtils::FileMove(const wchar* existingFile, const wchar* newFile) +{ +#if HS_BUILD_FOR_WIN32 + return (::MoveFileW(existingFile, newFile) != 0); +#elif HS_BUILD_FOR_UNIX + FileCopy(existingFile,newFile); + return( RemoveFile( existingFile )==0); +#else + hsAssert(0, "Not implemented"); + return false; +#endif +} + +bool plFileUtils::FileExists(const wchar* file) +{ + FILE* fp = _wfopen(file, L"rb"); + bool retVal = (fp != nil); + if (fp) + fclose(fp); + return retVal; +} + +bool plFileUtils::FileExists(const char* file) +{ + FILE* fp = fopen(file, "rb"); + bool retVal = (fp != nil); + if (fp) + fclose(fp); + return retVal; +} + +//// EnsureFilePathExists //////////////////////////////////////////////////// +// Given a filename with path, makes sure the file's path exists + +hsBool plFileUtils::EnsureFilePathExists( const char *filename ) +{ + wchar* wFilename = hsStringToWString(filename); + hsBool ret = EnsureFilePathExists(wFilename); + delete [] wFilename; + return ret; +} + +hsBool plFileUtils::EnsureFilePathExists( const wchar *filename ) +{ + hsWStringTokenizer izer( filename, L"\\/" ); + + hsBool lastWorked = false; + wchar token[ kFolderIterator_MaxPath ]; + + + while( izer.Next( token, arrsize( token ) ) && izer.HasMoreTokens() ) + { + // Want the full path from the start of the string + lastWorked = CreateDir( izer.fString ); + izer.RestoreLastTerminator(); + } + + return lastWorked; +} + +//// GetFileTimes //////////////////////////////////////////////////////////// +// Gets the creation and modification dates of the file specified. Returns +// false if unsuccessful + +hsBool plFileUtils::GetFileTimes( const char *path, plUnifiedTime *createTimeOut, plUnifiedTime *modifyTimeOut ) +{ + struct stat fileInfo; + + int result = stat( path, &fileInfo ); + if( result != 0 ) + return false; + + if( createTimeOut != nil ) + *createTimeOut = plUnifiedTime( fileInfo.st_ctime ); + if( modifyTimeOut != nil ) + *modifyTimeOut = plUnifiedTime( fileInfo.st_mtime ); + + return true; +} + +plFileUtils::Modify plFileUtils::CompareModifyTimes(const char* file1, const char* file2) +{ + plUnifiedTime modTime1, modTime2; + if (GetFileTimes(file1, nil, &modTime1) && + GetFileTimes(file2, nil, &modTime2)) + { + double diff = plUnifiedTime::GetTimeDifference(modTime1, modTime2); + + if (hsABS(diff) <= 2) + return kFilesEqual; + else if (diff > 0) + return kFile1Newer; + else + return kFile2Newer; + } + + return kFileError; +} + +bool plFileUtils::SetModifyTime( const char * filename, const plUnifiedTime & timestamp ) +{ +#ifdef HS_BUILD_FOR_WIN32 + HANDLE hFile = CreateFile(filename, + GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,nil); + if (hFile==INVALID_HANDLE_VALUE) + return false; + SYSTEMTIME systime; + systime.wDay = timestamp.GetDay(); + systime.wDayOfWeek = timestamp.GetDayOfWeek(); + systime.wHour = timestamp.GetHour(); + systime.wMilliseconds = 0; + systime.wMinute = timestamp.GetMinute(); + systime.wMonth = timestamp.GetMonth(); + systime.wSecond = timestamp.GetSecond(); + systime.wYear = timestamp.GetYear(); + FILETIME localFileTime, filetime; + SystemTimeToFileTime(&systime,&localFileTime); + LocalFileTimeToFileTime(&localFileTime,&filetime); + SetFileTime(hFile,nil,nil,&filetime); + CloseHandle(hFile); + return true; + +#elif HS_BUILD_FOR_UNIX + struct stat sbuf; + int result = stat( filename, &sbuf ); + if( result ) + return false; + struct utimbuf utb; + utb.actime = sbuf.st_atime; + utb.modtime = timestamp.GetSecs(); + result = utime( filename, &utb ); + if( result ) + return false; + return true; + +#endif +} + +//// StripPath /////////////////////////////////////////////////////////////// + +const char* plFileUtils::GetFileName(const char* path) +{ + const char* c = strrchr(path, '/'); + if (c == nil) + c = strrchr(path, '\\'); + + if (c == nil) + c = path; + else + c++; + + return c; +} + +const wchar* plFileUtils::GetFileName(const wchar* path) +{ + const wchar* c = wcsrchr(path, L'/'); + if (c == nil) + c = wcsrchr(path, L'\\'); + + if (c == nil) + c = path; + else + c++; + + return c; +} + +void plFileUtils::StripFile(char* pathAndName) +{ + char* fileName = (char*)GetFileName(pathAndName); + if (fileName != pathAndName) + *fileName = '\0'; +} + +void plFileUtils::StripFile(wchar* pathAndName) +{ + wchar* fileName = (wchar*)GetFileName(pathAndName); + if (fileName != pathAndName) + *fileName = L'\0'; +} + +void plFileUtils::StripExt(char* fileName) +{ + char* ext = (char*)GetFileExt(fileName); + if (ext) + *(ext-1) = '\0'; +} + +const char* plFileUtils::GetFileExt(const char* pathAndName) +{ + const char* fileName = GetFileName(pathAndName); + if (fileName) + { + const char* ext = strrchr(fileName, '.'); + if (ext) + return ext+1; + } + + return nil; +} + +const wchar* plFileUtils::GetFileExt(const wchar* pathAndName) +{ + const wchar* fileName = GetFileName(pathAndName); + if (fileName) + { + const wchar* ext = wcsrchr(fileName, L'.'); + if (ext) + return ext+1; + } + + return nil; +} + +void plFileUtils::AddSlash(char* path) +{ + char lastChar = path[strlen(path)-1]; + if (lastChar != '\\' && lastChar != '/') + strcat(path, "\\"); +} + +void plFileUtils::ConcatFileName(char* path, const char* fileName) +{ + AddSlash(path); + strcat(path, fileName); +} + +//// GetFileSize ///////////////////////////////////////////////////////////// + +UInt32 plFileUtils::GetFileSize( const char *path ) +{ + wchar* wPath = hsStringToWString(path); + UInt32 ret = GetFileSize(wPath); + delete [] wPath; + return ret; +} + +UInt32 plFileUtils::GetFileSize( const wchar *path ) +{ + UInt32 len = 0; + + hsUNIXStream str; + if (str.Open(path, L"rb")) + { + len = str.GetEOF(); + str.Close(); + } + + return len; +} + +//// GetSecureEncryptionKey ////////////////////////////////////////////////// + +bool plFileUtils::GetSecureEncryptionKey(const char* filename, UInt32* key, unsigned length) +{ + wchar* wFilename = hsStringToWString(filename); + bool ret = GetSecureEncryptionKey(wFilename, key, length); + delete [] wFilename; + return ret; +} + +bool plFileUtils::GetSecureEncryptionKey(const wchar* filename, UInt32* key, unsigned length) +{ + // looks for an encryption key file in the same directory, and reads it + std::wstring sFilename = filename; + + // grab parent directory + unsigned loc = sFilename.rfind(L"\\"); + if (loc == std::wstring::npos) + loc = sFilename.rfind(L"/"); + + std::wstring sDir; + if (loc != std::wstring::npos) + sDir = sFilename.substr(0, loc); + else // no directory + sDir = L"./"; + if ((sDir[sDir.length()-1] != L'/') && (sDir[sDir.length()-1] != L'\\')) + sDir += L'/'; // add the slash, if it doesn't has one + + // now add the key filename + std::wstring keyFile = sDir + kWKeyFilename; + + if (FileExists(keyFile.c_str())) + { + // file exists, read from it + hsUNIXStream file; + file.Open(keyFile.c_str(), L"rb"); + + unsigned bytesToRead = length * sizeof(UInt32); + byte* buffer = (byte*)ALLOC(bytesToRead); + unsigned bytesRead = file.Read(bytesToRead, buffer); + + file.Close(); + + unsigned memSize = min(bytesToRead, bytesRead); + memcpy(key, buffer, memSize); + FREE(buffer); + + return true; + } + else + { + // file doesn't exist, use default key + unsigned memSize = min(length, arrsize(plSecureStream::kDefaultKey)); + memSize *= sizeof(UInt32); + memcpy(key, plSecureStream::kDefaultKey, memSize); + + return false; + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plFileUtils.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plFileUtils.h new file mode 100644 index 00000000..0fed0b4d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plFileUtils.h @@ -0,0 +1,106 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plFileUtils - Namespace of fun file utilities +// +//// History ///////////////////////////////////////////////////////////////// +// +// 5.7.2002 mcn - Created +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plFileUtils_h +#define _plFileUtils_h + +class plUnifiedTime; + +namespace plFileUtils +{ + static const char kKeyFilename[] = "encryption.key"; + static const wchar kWKeyFilename[] = L"encryption.key"; + + // Creates the directory specified. Returns false if unsuccessful or directory already exists + hsBool CreateDir( const char *path ); + hsBool CreateDir( const wchar *path ); + hsBool RemoveDir(const char* path); + hsBool RemoveDirTree(const char * path); + + // delete file from disk + bool RemoveFile(const char* filename, bool delReadOnly=false); + bool RemoveFile(const wchar* filename, bool delReadOnly=false); + + bool FileCopy(const char* existingFile, const char* newFile); + bool FileCopy(const wchar* existingFile, const wchar* newFile); + bool FileMove(const char* existingFile, const char* newFile); + bool FileMove(const wchar* existingFile, const wchar* newFile); + + bool FileExists(const char* file); + bool FileExists(const wchar* file); + + // Given a filename with path, makes sure the file's path exists + hsBool EnsureFilePathExists( const char *filename ); + hsBool EnsureFilePathExists( const wchar *filename ); + + // Gets the creation and modification dates of the file specified. Returns false if unsuccessful + hsBool GetFileTimes( const char *path, plUnifiedTime *createTimeOut, plUnifiedTime *modifyTimeOut ); + // Compares file times, taking into account NTFS/FAT32 time issues + enum Modify { kFileError, kFilesEqual, kFile1Newer, kFile2Newer }; + Modify CompareModifyTimes(const char* file1, const char* file2); + // Set file modify time + bool SetModifyTime( const char * filename, const plUnifiedTime & time ); + + // Return a pointer into the given string at the start of the actual filename (i.e. past any path info) + const char* GetFileName(const char* pathAndName); + const wchar* GetFileName(const wchar* pathAndName); + // Get the file extension (without the .), or nil if it doesn't have one + const char* GetFileExt(const char* pathAndName); + const wchar* GetFileExt(const wchar* pathAndName); + + // Strips the filename off the given full path + void StripFile(char* pathAndName); + void StripFile(wchar* pathAndName); + void StripExt(char* fileName); + + // Get the size of the given file in bytes + UInt32 GetFileSize( const char *path ); + UInt32 GetFileSize( const wchar *path ); + + // Adds a slash to the end of a filename (or does nothing if it's already there) + void AddSlash(char* path); + + // Concatenates fileName onto path, making sure to add a slash if necessary + void ConcatFileName(char* path, const char* fileName); + + // searches the parent directory of filename for the encryption key file, and reads it + // into the key passed in. Returns false if the key file didn't exist (and sets key to + // the default key) + bool GetSecureEncryptionKey(const char* filename, UInt32* key, unsigned length); + bool GetSecureEncryptionKey(const wchar* filename, UInt32* key, unsigned length); +}; + + +#endif // _plFileUtils_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plInitFileReader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plInitFileReader.cpp new file mode 100644 index 00000000..33770f17 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plInitFileReader.cpp @@ -0,0 +1,202 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plInitFileReader - Helper class that parses a standard-format .ini file // +// and allows you to specify derived classes to handle // +// interpreting specific portions. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plInitFileReader.h" + +#include "hsStream.h" +#include "hsUtils.h" +#include "hsStringTokenizer.h" +#include "plEncryptedStream.h" + + +plInitSectionTokenReader::plInitSectionTokenReader( const char *separators ) : fSeparators( separators ) +{ +} + +hsBool plInitSectionTokenReader::ParseLine( const char *line, UInt32 userData ) +{ + hsStringTokenizer izer( line, fSeparators ); + + char *token = izer.next(); + return IParseToken( token, &izer, userData ); +} + +void plInitFileReader::IInitReaders( plInitSectionReader **readerArray ) +{ + UInt32 i; + + + for( i = 0; readerArray[ i ] != nil; i++ ) + fSections.Append( readerArray[ i ] ); + + hsAssert( fSections.GetCount() > 0, "No sections for initFileReader" ); + + fCurrSection = fSections[ 0 ]; +} + +plInitFileReader::plInitFileReader( plInitSectionReader **readerArray, UInt16 lineSize ) +{ + fRequireEncrypted = true; + fCurrLine = nil; + fLineSize = lineSize; + fStream = fOurStream = nil; + IInitReaders( readerArray ); + fUnhandledSection = nil; +} + +plInitFileReader::plInitFileReader( const char *fileName, plInitSectionReader **readerArray, UInt16 lineSize ) +{ + fRequireEncrypted = true; + fCurrLine = nil; + fLineSize = lineSize; + fStream = fOurStream = nil; + IInitReaders( readerArray ); + if( !Open( fileName ) ) + hsAssert( false, "Constructor open for plInitFileReader failed!" ); + fUnhandledSection = nil; +} + +plInitFileReader::plInitFileReader( hsStream *stream, plInitSectionReader **readerArray, UInt16 lineSize ) +{ + fRequireEncrypted = true; + fCurrLine = nil; + fLineSize = lineSize; + fStream = fOurStream = nil; + IInitReaders( readerArray ); + if( !Open( stream ) ) + hsAssert( false, "Constructor open for plInitFileReader failed!" ); + fUnhandledSection = nil; +} + +plInitFileReader::~plInitFileReader() +{ + Close(); + delete [] fCurrLine; +} + +hsBool plInitFileReader::Open( const char *fileName ) +{ + if( fStream != nil ) + { + hsAssert( false, "Unable to open initFileReader; already open" ); + return false; + } + + fOurStream = plEncryptedStream::OpenEncryptedFile( fileName, fRequireEncrypted ); + + if( fOurStream == nil ) + return false; + + fStream = fOurStream; + + return true; +} + +hsBool plInitFileReader::Open( hsStream *stream ) +{ + if( fStream != nil ) + { + hsAssert( false, "Unable to open initFileReader; already open" ); + return false; + } + + fStream = stream; + return true; +} + +hsBool plInitFileReader::Parse( UInt32 userData ) +{ + hsAssert( fStream != nil, "Nil stream in initFileReader::Parse(); file not yet open?" ); + + if( fCurrLine == nil ) + fCurrLine = TRACKED_NEW char[ fLineSize + 1 ]; + + // Start parsing lines + while( fStream->ReadLn( fCurrLine, fLineSize ) ) + { + // puts( fCurrLine ); + + // Is line a section header? + if( fCurrLine[ 0 ] == '[' ) + { + // Yes--match against our sections and switch to the given one + char *end = strchr( fCurrLine, ']' ); + if( end != nil ) + *end = 0; + + UInt32 i; + + bool foundSection = false; + for( i = 0; i < fSections.GetCount(); i++ ) + { + if( stricmp( fSections[ i ]->GetSectionName(), &fCurrLine[ 1 ] ) == 0 ) + { + fCurrSection = fSections[ i ]; + foundSection = true; + break; + } + } + + if (!foundSection && fUnhandledSection) + { + fCurrSection = fUnhandledSection; + fCurrSection->SetSectionName(&fCurrLine[1]); + } + } + else + { + // Nope, just a line, pass to our current section tokenizer + if( !fCurrSection->ParseLine( fCurrLine, userData ) ) + return false; + } + } + + return true; +} + +void plInitFileReader::Close( void ) +{ + if( fStream == nil ) + return; + + if( fStream == fOurStream ) + { + fStream->Close(); + delete fOurStream; + fOurStream = nil; + } + + fStream = nil; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plInitFileReader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plInitFileReader.h new file mode 100644 index 00000000..66c2783c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plInitFileReader.h @@ -0,0 +1,137 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plInitFileReader - Helper class that parses a standard-format .ini file // +// and allows you to specify derived classes to handle // +// interpreting specific portions. // +// // +//// Usage /////////////////////////////////////////////////////////////////// +// // +// First create a set of derived classes from plInitSectionReader // +// (or plInitSectionTokenReader, to be easier) that will parse the lines // +// for each section of your .ini file. Then create a C-style array of // +// pointers to instances of each reader, the first being the default // +// reader, and ending with a nil pointer (see below). Finally, create // +// a plInitFileReader with the array you created and it'll parse the // +// given file (or stream) and call your readers as needed. You can also // +// optionally pass in a UInt32 for userData that will be passed on to each // +// reader in turn. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plInitFileReader_h +#define _plInitFileReader_h + +#include "hsTypes.h" +#include "hsStream.h" +#include "hsTemplates.h" + +//// Base Section Class ////////////////////////////////////////////////////// +// Define a derived version of this for each section of your init file. + +class plInitSectionReader +{ + public: + + // Override this to define what [string] your section starts with + virtual const char *GetSectionName( void ) const = 0; + + // Override this to parse each line in your section. Return false to abort parsing + virtual hsBool ParseLine( const char *line, UInt32 userData ) = 0; + + // Override this if you're defining an unhandled section reader + virtual void SetSectionName(const char* section) {} +}; + +//// Semi-Derived Class ////////////////////////////////////////////////////// +// Half-way derived class for parsing lines by tokens rather than pure +// strings. + +class hsStringTokenizer; + +class plInitSectionTokenReader : public plInitSectionReader +{ + protected: + + const char *fSeparators; + + // Override this to parse each token in your section. Return false to abort parsing + virtual hsBool IParseToken( const char *token, hsStringTokenizer *tokenizer, UInt32 userData ) = 0; + + public: + + plInitSectionTokenReader( const char *separators = ",=\t" ); + + // Overridden for you. Override IParseToken() + virtual hsBool ParseLine( const char *line, UInt32 userData ); +}; + +//// Main Reader Class /////////////////////////////////////////////////////// +// Create one of these and add an array of derived versions of the above +// reader to parse your init file. + +class plInitFileReader +{ + protected: + + hsStream *fStream; + hsStream *fOurStream; + char *fCurrLine; + UInt32 fLineSize; + bool fRequireEncrypted; + + plInitSectionReader *fCurrSection; + hsTArray fSections; + plInitSectionReader* fUnhandledSection; + + void IInitReaders( plInitSectionReader **readerArray ); + + public: + + // The array passed in should be an array of pointers to plInitSectionReader, + // with the last pointer being nil (denoting the end of the array). The first + // element of the array will be the "default" section--i.e. if there is no section + // header at the top of the file, that reader will be used. + + plInitFileReader( plInitSectionReader **readerArray, UInt16 lineSize = 256 ); + plInitFileReader( const char *fileName, plInitSectionReader **readerArray, UInt16 lineSize = 256 ); + plInitFileReader( hsStream *stream, plInitSectionReader **readerArray, UInt16 lineSize = 256 ); + virtual ~plInitFileReader(); + + void SetRequireEncrypted(bool require) { fRequireEncrypted = require; } + bool GetRequireEncrypted() const { return fRequireEncrypted; } + void SetUnhandledSectionReader(plInitSectionReader* reader) { fUnhandledSection = reader; } + + hsBool Open( const char *fileName ); + hsBool Open( hsStream *stream ); + hsBool Parse( UInt32 userData = 0 ); + void Close( void ); + + hsBool IsOpen( void ) const { return fStream != nil; } +}; + +#endif //_plInitFileReader_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plSecureStream.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plSecureStream.cpp new file mode 100644 index 00000000..e7eb0717 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plSecureStream.cpp @@ -0,0 +1,624 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSecureStream.h" +#include "hsUtils.h" +#include "plFileUtils.h" +#include "hsSTLStream.h" + +#include + +// our default encryption key +const UInt32 plSecureStream::kDefaultKey[4] = { 0x6c0a5452, 0x3827d0f, 0x3a170b92, 0x16db7fc2 }; + +static const int kEncryptChunkSize = 8; + +static const char* kMagicString = "notthedroids"; +static const int kMagicStringLen = 12; + +static const int kFileStartOffset = kMagicStringLen + sizeof(UInt32); + +static const int kMaxBufferedFileSize = 10*1024; + +plSecureStream::plSecureStream(hsBool deleteOnExit, UInt32* key) : +fRef(INVALID_HANDLE_VALUE), +fActualFileSize(0), +fBufferedStream(false), +fRAMStream(nil), +fWriteFileName(nil), +fOpenMode(kOpenFail), +fDeleteOnExit(deleteOnExit) +{ + if (key) + memcpy(&fKey, key, sizeof(kDefaultKey)); + else + memcpy(&fKey, &kDefaultKey, sizeof(kDefaultKey)); +} + +plSecureStream::~plSecureStream() +{ +} + +// +// XXTEA +// http://www-users.cs.york.ac.uk/~matthew/TEA/ +// +// A potential weakness in this implementation is the fact that a known value +// (length of the original file) is written at the start of the encrypted file. -Colin +// + +#define MX (z>>5 ^ y<<2) + (y>>3 ^ z<<4) ^ (sum^y) + (fKey[p&3^e]^z) + +void plSecureStream::IEncipher(UInt32* const v, UInt32 n) +{ + register unsigned long y=v[0], z=v[n-1], e, delta=0x9E3779B9; + register unsigned long q = 6 + 52/n, p, sum = 0; + + while (q-- > 0) + { + sum += delta; + e = (sum >> 2) & 3; + for (p = 0; p < n - 1; p++) + { + y = v[p + 1]; + v[p] += MX; + z = v[p]; + } + y = v[0]; + v[n - 1] += MX; + z = v[n - 1]; + } +} + +void plSecureStream::IDecipher(UInt32* const v, UInt32 n) +{ + register unsigned long y=v[0], z=v[n-1], e, delta=0x9E3779B9; + register unsigned long q = 6 + 52/n, p, sum = q * delta; + + while (sum > 0) + { + e = (sum >> 2) & 3; + for (p = n - 1; p > 0; p--) + { + z = v[p - 1]; + v[p] -= MX; + y = v[p]; + } + z = v[n - 1]; + v[0] -= MX; + y = v[0]; + sum -= delta; + } +} + +hsBool plSecureStream::Open(const char* name, const char* mode) +{ + wchar* wName = hsStringToWString(name); + wchar* wMode = hsStringToWString(mode); + hsBool ret = Open(wName, wMode); + delete [] wName; + delete [] wMode; + return ret; +} + +hsBool plSecureStream::Open(const wchar* name, const wchar* mode) +{ + if (wcscmp(mode, L"rb") == 0) + { + if (fDeleteOnExit) + { + fRef = CreateFileW(name, + GENERIC_READ, // open for reading + 0, // no one can open the file until we're done + NULL, // default security + OPEN_EXISTING, // only open existing files (no creation) + FILE_FLAG_DELETE_ON_CLOSE, // delete the file from disk when we close the handle + NULL); // no template + } + else + { + fRef = CreateFileW(name, + GENERIC_READ, // open for reading + 0, // no one can open the file until we're done + NULL, // default security + OPEN_EXISTING, // only open existing files (no creation) + FILE_ATTRIBUTE_NORMAL, // normal file attributes + NULL); // no template + } + + fPosition = 0; + + if (fRef == INVALID_HANDLE_VALUE) + return false; + + // Make sure our special magic string is there + if (!ICheckMagicString(fRef)) + { + CloseHandle(fRef); + fRef = INVALID_HANDLE_VALUE; + return false; + } + + DWORD numBytesRead; + ReadFile(fRef, &fActualFileSize, sizeof(UInt32), &numBytesRead, NULL); + + // The encrypted stream is inefficient if you do reads smaller than + // 8 bytes. Since we do a lot of those, any file under a size threshold + // is buffered in memory + if (fActualFileSize <= kMaxBufferedFileSize) + IBufferFile(); + + fOpenMode = kOpenRead; + + return true; + } + else if (wcscmp(mode, L"wb") == 0) + { + fRAMStream = TRACKED_NEW hsVectorStream; + fWriteFileName = TRACKED_NEW wchar[wcslen(name) + 1]; + wcscpy(fWriteFileName, name); + fPosition = 0; + + fOpenMode = kOpenWrite; + fBufferedStream = true; + + return true; + } + else + { + hsAssert(0, "Unsupported open mode"); + fOpenMode = kOpenFail; + return false; + } +} + +hsBool plSecureStream::Close() +{ + int rtn = false; + + if (fOpenMode == kOpenWrite) + { + fRAMStream->Rewind(); + rtn = IWriteEncrypted(fRAMStream, fWriteFileName); + } + if (fRef != INVALID_HANDLE_VALUE) + { + rtn = CloseHandle(fRef); + fRef = INVALID_HANDLE_VALUE; + } + + if (fRAMStream) + { + delete fRAMStream; + fRAMStream = nil; + } + + if (fWriteFileName) + { + delete [] fWriteFileName; + fWriteFileName = nil; + } + + fActualFileSize = 0; + fBufferedStream = false; + fOpenMode = kOpenFail; + + return rtn; +} + +UInt32 plSecureStream::IRead(UInt32 bytes, void* buffer) +{ + if (fRef == INVALID_HANDLE_VALUE) + return 0; + DWORD numItems; + bool success = (ReadFile(fRef, buffer, bytes, &numItems, NULL) != 0); + fBytesRead += numItems; + fPosition += numItems; + if ((unsigned)numItems < bytes) + { + if (success) + { + // EOF ocurred + char str[128]; + sprintf(str, "Hit EOF on Windows read, only read %d out of requested %d bytes\n", numItems, bytes); + hsDebugMessage(str, 0); + } + else + { + hsDebugMessage("Error on Windows read", GetLastError()); + } + } + return numItems; +} + +void plSecureStream::IBufferFile() +{ + fRAMStream = TRACKED_NEW hsVectorStream; + char buf[1024]; + while (!AtEnd()) + { + UInt32 numRead = Read(1024, buf); + fRAMStream->Write(numRead, buf); + } + fRAMStream->Rewind(); + + fBufferedStream = true; + CloseHandle(fRef); + fRef = INVALID_HANDLE_VALUE; + fPosition = 0; +} + +hsBool plSecureStream::AtEnd() +{ + if (fBufferedStream) + return fRAMStream->AtEnd(); + else + return (GetPosition() == fActualFileSize); +} + +void plSecureStream::Skip(UInt32 delta) +{ + if (fBufferedStream) + { + fRAMStream->Skip(delta); + fPosition = fRAMStream->GetPosition(); + } + else if (fRef != INVALID_HANDLE_VALUE) + { + fBytesRead += delta; + fPosition += delta; + SetFilePointer(fRef, delta, 0, FILE_CURRENT); + } +} + +void plSecureStream::Rewind() +{ + if (fBufferedStream) + { + fRAMStream->Rewind(); + fPosition = fRAMStream->GetPosition(); + } + else if (fRef != INVALID_HANDLE_VALUE) + { + fBytesRead = 0; + fPosition = 0; + SetFilePointer(fRef, kFileStartOffset, 0, FILE_BEGIN); + } +} + +void plSecureStream::FastFwd() +{ + if (fBufferedStream) + { + fRAMStream->FastFwd(); + fPosition = fRAMStream->GetPosition(); + } + else if (fRef != INVALID_HANDLE_VALUE) + { + fBytesRead = fPosition = SetFilePointer(fRef, kFileStartOffset + fActualFileSize, 0, FILE_BEGIN); + } +} + +UInt32 plSecureStream::GetEOF() +{ + return fActualFileSize; +} + +UInt32 plSecureStream::Read(UInt32 bytes, void* buffer) +{ + if (fBufferedStream) + { + UInt32 numRead = fRAMStream->Read(bytes, buffer); + fPosition = fRAMStream->GetPosition(); + return numRead; + } + + UInt32 startPos = fPosition; + + // Offset into the first buffer (0 if we are aligned on a chunk, which means no extra block read) + UInt32 startChunkPos = startPos % kEncryptChunkSize; + // Amount of data in the partial first chunk (0 if we're aligned) + UInt32 startAmt = (startChunkPos != 0) ? hsMinimum(kEncryptChunkSize - startChunkPos, bytes) : 0; + + UInt32 totalNumRead = IRead(bytes, buffer); + + UInt32 numMidChunks = (totalNumRead - startAmt) / kEncryptChunkSize; + UInt32 endAmt = (totalNumRead - startAmt) % kEncryptChunkSize; + + // If the start position is in the middle of a chunk we need to rewind and + // read that whole chunk in and decrypt it. + if (startChunkPos != 0) + { + // Move to the start of this chunk + SetPosition(startPos-startChunkPos); + + // Read in the chunk and decrypt it + char buf[kEncryptChunkSize]; + UInt32 numRead = IRead(kEncryptChunkSize, &buf); + IDecipher((UInt32*)&buf, kEncryptChunkSize / sizeof(UInt32)); + + // Copy the relevant portion to the output buffer + memcpy(buffer, &buf[startChunkPos], startAmt); + + SetPosition(startPos+totalNumRead); + } + + if (numMidChunks != 0) + { + UInt32* bufferPos = (UInt32*)(((char*)buffer)+startAmt); + for (int i = 0; i < numMidChunks; i++) + { + // Decrypt chunk + IDecipher(bufferPos, kEncryptChunkSize / sizeof(UInt32)); + bufferPos += (kEncryptChunkSize / sizeof(UInt32)); + } + } + + if (endAmt != 0) + { + // Read in the final chunk and decrypt it + char buf[kEncryptChunkSize]; + SetPosition(startPos + startAmt + numMidChunks*kEncryptChunkSize); + UInt32 numRead = IRead(kEncryptChunkSize, &buf); + IDecipher((UInt32*)&buf, kEncryptChunkSize / sizeof(UInt32)); + + memcpy(((char*)buffer)+totalNumRead-endAmt, &buf, endAmt); + + SetPosition(startPos+totalNumRead); + } + + // If we read into the padding at the end, update the total read to not include that + if (totalNumRead > 0 && startPos + totalNumRead > fActualFileSize) + { + totalNumRead -= (startPos + totalNumRead) - fActualFileSize; + SetPosition(fActualFileSize); + } + + return totalNumRead; +} + +UInt32 plSecureStream::Write(UInt32 bytes, const void* buffer) +{ + if (fOpenMode != kOpenWrite) + { + hsAssert(0, "Trying to write to a read stream"); + return 0; + } + + return fRAMStream->Write(bytes, buffer); +} + +bool plSecureStream::IWriteEncrypted(hsStream* sourceStream, const wchar* outputFile) +{ + hsUNIXStream outputStream; + + if (!outputStream.Open(outputFile, L"wb")) + return false; + + outputStream.Write(kMagicStringLen, kMagicString); + + // Save some space to write the file size at the end + outputStream.WriteSwap32(0); + + // Write out all the full size encrypted blocks we can + char buf[kEncryptChunkSize]; + UInt32 amtRead; + while ((amtRead = sourceStream->Read(kEncryptChunkSize, &buf)) == kEncryptChunkSize) + { + IEncipher((UInt32*)&buf, kEncryptChunkSize / sizeof(UInt32)); + outputStream.Write(kEncryptChunkSize, &buf); + } + + // Pad with random data and write out the final partial block, if there is one + if (amtRead > 0) + { + static bool seededRand = false; + if (!seededRand) + { + seededRand = true; + srand((unsigned int)time(nil)); + } + + for (int i = amtRead; i < kEncryptChunkSize; i++) + buf[i] = rand(); + + IEncipher((UInt32*)&buf, kEncryptChunkSize / sizeof(UInt32)); + + outputStream.Write(kEncryptChunkSize, &buf); + } + + // Write the original file size at the start + UInt32 actualSize = sourceStream->GetPosition(); + outputStream.Rewind(); + outputStream.Skip(kMagicStringLen); + outputStream.WriteSwap32(actualSize); + + outputStream.Close(); + + return true; +} + +bool plSecureStream::FileEncrypt(const char* fileName, UInt32* key /* = nil */) +{ + wchar* wFilename = hsStringToWString(fileName); + bool ret = FileEncrypt(wFilename, key); + delete [] wFilename; + return ret; +} + +bool plSecureStream::FileEncrypt(const wchar* fileName, UInt32* key /* = nil */) +{ + hsUNIXStream sIn; + if (!sIn.Open(fileName)) + return false; + + // Don't double encrypt any files + if (ICheckMagicString(sIn.GetFILE())) + { + sIn.Close(); + return true; + } + sIn.Rewind(); + + plSecureStream sOut(false, key); + bool wroteEncrypted = sOut.IWriteEncrypted(&sIn, L"crypt.dat"); + + sIn.Close(); + sOut.Close(); + + if (wroteEncrypted) + { + plFileUtils::RemoveFile(fileName); + plFileUtils::FileMove(L"crypt.dat", fileName); + } + + return true; +} + +bool plSecureStream::FileDecrypt(const char* fileName, UInt32* key /* = nil */) +{ + wchar* wFilename = hsStringToWString(fileName); + bool ret = FileDecrypt(wFilename, key); + delete [] wFilename; + return ret; +} + +bool plSecureStream::FileDecrypt(const wchar* fileName, UInt32* key /* = nil */) +{ + plSecureStream sIn(false, key); + if (!sIn.Open(fileName)) + return false; + + hsUNIXStream sOut; + if (!sOut.Open(L"crypt.dat", L"wb")) + { + sIn.Close(); + return false; + } + + char buf[1024]; + + while (!sIn.AtEnd()) + { + UInt32 numRead = sIn.Read(sizeof(buf), buf); + sOut.Write(numRead, buf); + } + + sIn.Close(); + sOut.Close(); + + plFileUtils::RemoveFile(fileName); + plFileUtils::FileMove(L"crypt.dat", fileName); + + return true; +} + +bool plSecureStream::ICheckMagicString(HANDLE fp) +{ + char magicString[kMagicStringLen+1]; + DWORD numBytesRead; + ReadFile(fp, &magicString, kMagicStringLen, &numBytesRead, NULL); + magicString[kMagicStringLen] = '\0'; + return (hsStrEQ(magicString, kMagicString) != 0); +} + +bool plSecureStream::IsSecureFile(const char* fileName) +{ + wchar* wFilename = hsStringToWString(fileName); + bool ret = IsSecureFile(wFilename); + delete [] wFilename; + return ret; +} + +bool plSecureStream::IsSecureFile(const wchar* fileName) +{ + HANDLE fp = INVALID_HANDLE_VALUE; + fp = CreateFileW(fileName, + GENERIC_READ, // open for reading + 0, // no one can open the file until we're done + NULL, // default security + OPEN_EXISTING, // only open existing files (no creation) + FILE_ATTRIBUTE_NORMAL, // normal file attributes + NULL); // no template + + if (fp == INVALID_HANDLE_VALUE) + return false; + + bool isEncrypted = ICheckMagicString(fp); + + CloseHandle(fp); + + return isEncrypted; +} + +hsStream* plSecureStream::OpenSecureFile(const char* fileName, const UInt32 flags /* = kRequireEncryption */, UInt32* key /* = nil */) +{ + wchar* wFilename = hsStringToWString(fileName); + hsStream* ret = OpenSecureFile(wFilename, flags, key); + delete [] wFilename; + return ret; +} + +hsStream* plSecureStream::OpenSecureFile(const wchar* fileName, const UInt32 flags /* = kRequireEncryption */, UInt32* key /* = nil */) +{ + bool requireEncryption = flags & kRequireEncryption; +#ifndef PLASMA_EXTERNAL_RELEASE + requireEncryption = false; +#endif + + hsBool deleteOnExit = flags & kDeleteOnExit; + bool isEncrypted = IsSecureFile(fileName); + + hsStream* s = nil; + if (isEncrypted) + s = TRACKED_NEW plSecureStream(deleteOnExit, key); + else if (!requireEncryption) // If this isn't an external release, let them use unencrypted data + s = TRACKED_NEW hsUNIXStream; + + if (s) + s->Open(fileName, L"rb"); + return s; +} + +hsStream* plSecureStream::OpenSecureFileWrite(const char* fileName, UInt32* key /* = nil */) +{ + wchar* wFilename = hsStringToWString(fileName); + hsStream* ret = OpenSecureFileWrite(wFilename, key); + delete [] wFilename; + return ret; +} + +hsStream* plSecureStream::OpenSecureFileWrite(const wchar* fileName, UInt32* key /* = nil */) +{ + hsStream* s = nil; +#ifdef PLASMA_EXTERNAL_RELEASE + s = TRACKED_NEW plSecureStream(false, key); +#else + s = TRACKED_NEW hsUNIXStream; +#endif + + s->Open(fileName, L"wb"); + return s; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plSecureStream.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plSecureStream.h new file mode 100644 index 00000000..7eb7ccc8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plSecureStream.h @@ -0,0 +1,111 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSecureStream_h_inc +#define plSecureStream_h_inc + +#include "hsStream.h" +#include + +// A slightly more secure stream then plEncryptedStream in that it uses windows file functions +// to prevent other processes from accessing the file it is working with. It also can be set +// to download the file from a server into a temporary directory (with a mangled name) and +// delete that file on close, thereby minimizing the chance of having that file examined or +// edited. +class plSecureStream: public hsStream +{ +protected: + HANDLE fRef; + UInt32 fKey[4]; + + UInt32 fActualFileSize; + + bool fBufferedStream; + + hsStream* fRAMStream; + + wchar* fWriteFileName; + + enum OpenMode {kOpenRead, kOpenWrite, kOpenFail}; + OpenMode fOpenMode; + + hsBool fDeleteOnExit; + + void IBufferFile(); + + UInt32 IRead(UInt32 bytes, void* buffer); + + void IEncipher(UInt32* const v, UInt32 n); + void IDecipher(UInt32* const v, UInt32 n); + + bool IWriteEncrypted(hsStream* sourceStream, const wchar* outputFile); + + static bool ICheckMagicString(HANDLE fp); + +public: + plSecureStream(hsBool deleteOnExit = false, UInt32* key = nil); // uses default key if you don't pass one in + ~plSecureStream(); + + virtual hsBool Open(const char* name, const char* mode = "rb"); + virtual hsBool Open(const wchar* name, const wchar* mode = L"rb"); + virtual hsBool Close(); + + virtual UInt32 Read(UInt32 byteCount, void* buffer); + virtual UInt32 Write(UInt32 byteCount, const void* buffer); + virtual hsBool AtEnd(); + virtual void Skip(UInt32 deltaByteCount); + virtual void Rewind(); + virtual void FastFwd(); + virtual UInt32 GetEOF(); + + UInt32 GetActualFileSize() const {return fActualFileSize;} + + static bool FileEncrypt(const char* fileName, UInt32* key = nil); + static bool FileEncrypt(const wchar* fileName, UInt32* key = nil); + static bool FileDecrypt(const char* fileName, UInt32* key = nil); + static bool FileDecrypt(const wchar* fileName, UInt32* key = nil); + + enum OpenSecureFileFlags + { + kRequireEncryption = 0x01, + kDeleteOnExit = 0x02, + }; + + static bool IsSecureFile(const char* fileName); + static bool IsSecureFile(const wchar* fileName); + + // Attempts to create a read-binary stream for the requested file (delete the stream + // when you are done with it!) + static hsStream* OpenSecureFile(const char* fileName, const UInt32 flags = kRequireEncryption, UInt32* key = nil); + static hsStream* OpenSecureFile(const wchar* fileName, const UInt32 flags = kRequireEncryption, UInt32* key = nil); + // Attempts to create a write-binary stream for the requested file (delete the stream + // when you are done with it!) + static hsStream* OpenSecureFileWrite(const char* fileName, UInt32* key = nil); + static hsStream* OpenSecureFileWrite(const wchar* fileName, UInt32* key = nil); + + static const UInt32 kDefaultKey[4]; // our default encryption key +}; + +#endif // plSecureStream_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plStreamSource.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plStreamSource.cpp new file mode 100644 index 00000000..8d437baf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plStreamSource.cpp @@ -0,0 +1,196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsUtils.h" +#include "hsFiles.h" +#include "plStreamSource.h" +#include "plSecureStream.h" +#include "plEncryptedStream.h" +#include "plFileUtils.h" + +void ToLower(std::wstring& str) +{ + for (unsigned i = 0; i < str.length(); i++) + str[i] = towlower(str[i]); +} + +void ReplaceSlashes(std::wstring& path, wchar replaceWith) +{ + for (unsigned i = 0; i < path.length(); i++) + { + if ((path[i] == L'\\') || (path[i] == L'/')) + path[i] = replaceWith; + } +} + +void plStreamSource::ICleanup() +{ + // loop through all the file data records, and delete the streams + std::map::iterator curData; + for (curData = fFileData.begin(); curData != fFileData.end(); curData++) + { + curData->second.fStream->Close(); + delete curData->second.fStream; + curData->second.fStream = nil; + } + + fFileData.clear(); +} + +void plStreamSource::IBreakupFilename(std::wstring filename, std::wstring& dir, std::wstring& ext) +{ + // break the filename up into its parts + char* temp = hsWStringToString(filename.c_str()); + std::string sFilename = temp; + std::string sExt = plFileUtils::GetFileExt(temp); + plFileUtils::StripFile(temp); + std::string sDir = temp; + delete [] temp; + + if (sDir == sFilename) // no directory + sDir = ""; + if (sDir != "") + if ((sDir[sDir.length()-1] == '/') || (sDir[sDir.length()-1] == '\\')) + sDir = sDir.substr(0, sDir.length() - 1); // trim the slash, if it has one + + wchar_t* wTemp; + wTemp = hsStringToWString(sDir.c_str()); + dir = wTemp; + delete [] wTemp; + wTemp = hsStringToWString(sExt.c_str()); + ext = wTemp; + delete [] wTemp; +} + +hsStream* plStreamSource::GetFile(std::wstring filename) +{ + ToLower(filename); + ReplaceSlashes(filename, L'/'); + + if (fFileData.find(filename) == fFileData.end()) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // internal releases can pull from disk + char* temp = hsWStringToString(filename.c_str()); + std::string sFilename = temp; + delete [] temp; + + if (plFileUtils::FileExists(sFilename.c_str())) + { + // file exists on disk, cache it + std::wstring dir, ext; + IBreakupFilename(filename, dir, ext); + fFileData[filename].fFilename = filename; + fFileData[filename].fDir = dir; + fFileData[filename].fExt = ext; + if (plSecureStream::IsSecureFile(sFilename.c_str())) + { + UInt32 encryptionKey[4]; + plFileUtils::GetSecureEncryptionKey(sFilename.c_str(), encryptionKey, 4); + fFileData[filename].fStream = plSecureStream::OpenSecureFile(sFilename.c_str(), 0, encryptionKey); + } + else // otherwise it is an encrypted or plain stream, this call handles both + fFileData[filename].fStream = plEncryptedStream::OpenEncryptedFile(sFilename.c_str()); + + return fFileData[filename].fStream; + } +#endif // PLASMA_EXTERNAL_RELEASE + return nil; + } + return fFileData[filename].fStream; +} + +std::vector plStreamSource::GetListOfNames(std::wstring dir, std::wstring ext) +{ + ToLower(ext); + ToLower(dir); + ReplaceSlashes(dir, L'/'); + + if (ext[0] == L'.') + ext = ext.substr(1, ext.length()); // trim the dot, if it has one + if (dir != L"") + if ((dir[dir.length()-1] == L'/') || (dir[dir.length()-1] == L'\\')) + dir = dir.substr(0, dir.length() - 1); // trim the slash, if it has one + + // loop through all the file data records, and create the list + std::vector retVal; + std::map::iterator curData; + for (curData = fFileData.begin(); curData != fFileData.end(); curData++) + { + if ((curData->second.fDir == dir.c_str()) && (curData->second.fExt == ext)) + retVal.push_back(curData->second.fFilename); + } + +#ifndef PLASMA_EXTERNAL_RELEASE + // in internal releases, we can use on-disk files if they exist + // Build the search string as "dir/*.ext" + std::wstring wSearchStr = dir + L"/*." + ext; + char* temp = hsWStringToString(wSearchStr.c_str()); + std::string searchStr = temp; + delete [] temp; + + hsFolderIterator folderIter(searchStr.c_str(), true); + while (folderIter.NextFile()) + { + const char* filename = folderIter.GetFileName(); + wchar_t* wTemp = hsStringToWString(filename); + std::wstring wFilename = dir + L"/" + wTemp; + delete [] wTemp; + ToLower(wFilename); + + if (fFileData.find(wFilename) == fFileData.end()) // we haven't added it yet + retVal.push_back(wFilename); + } +#endif // PLASMA_EXTERNAL_RELEASE + + return retVal; +} + +bool plStreamSource::InsertFile(std::wstring filename, hsStream* stream) +{ + ToLower(filename); + ReplaceSlashes(filename, L'/'); + + if (fFileData.find(filename) != fFileData.end()) + return false; // duplicate entry, return failure + + // break the filename up into its parts + std::wstring dir, ext; + IBreakupFilename(filename, dir, ext); + + // copy the data over (takes ownership of the stream!) + fFileData[filename].fFilename = filename; + fFileData[filename].fDir = dir; + fFileData[filename].fExt = ext; + fFileData[filename].fStream = stream; + + return true; +} + +plStreamSource* plStreamSource::GetInstance() +{ + static plStreamSource source; + return &source; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plStreamSource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plStreamSource.h new file mode 100644 index 00000000..ee3b02b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plFile/plStreamSource.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plStreamSource_h_inc +#define plStreamSource_h_inc + +#include "hsStream.h" +#include "hsStlUtils.h" + +// A class for holding and accessing file streams. The preloader will insert +// files in here once they are loaded. In internal builds, if a requested file +// is not found, it will be retrieved from disk. +class plStreamSource +{ +private: + struct fileData + { + std::wstring fFilename; // includes path + std::wstring fDir; // parent directory + std::wstring fExt; + hsStream* fStream; // we own this pointer, so clean it up + }; + std::map fFileData; // key is filename + + void ICleanup(); // closes all file pointers and cleans up after itself + void IBreakupFilename(std::wstring filename, std::wstring& dir, std::wstring& ext); + + plStreamSource() {} +public: + ~plStreamSource() {ICleanup();} + + // Force a cleanup of all data (some apps need to get at those file again, and they can't while we have them open) + void Cleanup() {ICleanup();} + + // File access functions + hsStream* GetFile(std::wstring filename); // internal builds will read from disk if it doesn't exist + std::vector GetListOfNames(std::wstring dir, std::wstring ext); // internal builds merge from disk + + // For other classes to insert files (takes ownership of the stream if successful) + bool InsertFile(std::wstring filename, hsStream* stream); + + // Instance handling + static plStreamSource* GetInstance(); +}; + +#endif // plStreamSource_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/HS_RECT.inc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/HS_RECT.inc new file mode 100644 index 00000000..906e9105 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/HS_RECT.inc @@ -0,0 +1,235 @@ + +struct HS_RECT_NAME { + HS_RECT_TYPE fLeft, fTop, fRight, fBottom; + + HS_RECT_TYPE Width() const { return fRight - fLeft; } + HS_RECT_TYPE Height() const { return fBottom - fTop; } + hsBool IsEmpty() const { return fLeft >= fRight || fTop >= fBottom; } + + void SetEmpty() { fLeft = fTop = fRight = fBottom = 0; } + HS_RECT_NAME* Set(HS_RECT_TYPE left, HS_RECT_TYPE top, HS_RECT_TYPE right, HS_RECT_TYPE bottom) + { + fLeft = left; fTop = top; fRight = right; fBottom = bottom; + return this; + } + HS_RECT_NAME* Set(const HS_RECT_POINT* p1, const HS_RECT_POINT* p2) + { + if (p1->fX < p2->fX) + { fLeft = p1->fX; + fRight = p2->fX; + } else + { fLeft = p2->fX; + fRight = p1->fX; + } + + if (p1->fY < p2->fY) + { fTop = p1->fY; + fBottom = p2->fY; + } else + { fTop = p2->fY; + fBottom = p1->fY; + } + return this; + } + HS_RECT_NAME* Set(UInt32 count, const HS_RECT_POINT pts[]) + { + if (count > 0) + { fLeft = fRight = pts[0].fX; + fTop = fBottom = pts[0].fY; + (void)this->Union(count - 1, &pts[1]); + } + return this; + } + + hsBool Contains(HS_RECT_TYPE x, HS_RECT_TYPE y) const + { + return x >= fLeft && x < fRight && y >= fTop && y < fBottom; + } + hsBool Contains(const HS_RECT_POINT* p) const + { + return this->Contains(p->fX, p->fY); + } + hsBool Contains(const HS_RECT_NAME* r) const + { + return fLeft <= r->fLeft && fTop <= r->fTop && fRight >= r->fRight && fBottom >= r->fBottom; + } + hsBool Contains(HS_RECT_TYPE left, HS_RECT_TYPE top, HS_RECT_TYPE right, HS_RECT_TYPE bottom) const + { + return fLeft <= left && fTop <= top && fRight >= right && fBottom >= bottom; + } + HS_RECT_NAME* Offset(HS_RECT_TYPE dx, HS_RECT_TYPE dy) + { + fLeft += dx; fTop += dy; fRight += dx; fBottom += dy; + return this; + } + HS_RECT_NAME* MoveTo(HS_RECT_TYPE x, HS_RECT_TYPE y) + { + this->fRight += x - this->fLeft; + this->fBottom += y - this->fTop; + this->fLeft = x; + this->fTop = y; + return this; + } + HS_RECT_NAME* Inset(HS_RECT_TYPE dx, HS_RECT_TYPE dy) + { + fLeft += dx; fRight -= dx; + fTop += dy; fBottom -= dy; + return this; + } + + HS_RECT_NAME* UnionX(HS_RECT_TYPE x) + { + if (x < fLeft) fLeft = x; else + if (x > fRight) fRight = x; + return this; + } + HS_RECT_NAME* UnionY(HS_RECT_TYPE y) + { + if (y < fTop) fTop = y; else + if (y > fBottom) fBottom = y; + return this; + } + HS_RECT_NAME* Union(const HS_RECT_NAME* r) + { + if (r->fLeft < fLeft) fLeft = r->fLeft; + if (r->fTop < fTop) fTop = r->fTop; + if (r->fRight > fRight) fRight = r->fRight; + if (r->fBottom > fBottom) fBottom = r->fBottom; + return this; + } + HS_RECT_NAME* Union(const HS_RECT_POINT* p) + { + if (p->fX < fLeft) fLeft = p->fX; + if (p->fX > fRight) fRight = p->fX; + if (p->fY < fTop) fTop = p->fY; + if (p->fY> fBottom) fBottom = p->fY; + return this; + } + HS_RECT_NAME* Union(UInt32 count, const HS_RECT_POINT p[]) + { + HS_RECT_TYPE left = this->fLeft; + HS_RECT_TYPE top = this->fTop; + HS_RECT_TYPE right = this->fRight; + HS_RECT_TYPE bottom = this->fBottom; + + for (; count > 0; ++p, --count) + { HS_RECT_TYPE value = p->fX; + if (value < left) left = value; + else if (value > right) right = value; + + value = p->fY; + if (value < top) top = value; + else if (value > bottom) bottom = value; + } + return this->Set(left, top, right, bottom); + } + +#if 0 // Havok reeks + friend int operator==(const HS_RECT_NAME& a, const HS_RECT_NAME& b) + { + return a.fLeft == b.fLeft && a.fTop == b.fTop && + a.fRight == b.fRight && a.fBottom == b.fBottom; + } + friend int operator!=(const HS_RECT_NAME& a, const HS_RECT_NAME& b) + { + return !(a == b); + } +#else // Havok reeks + int operator==(const HS_RECT_NAME& aa) const + { + return aa.fLeft == fLeft && aa.fTop == fTop && + aa.fRight == fRight && aa.fBottom == fBottom; + } + int operator!=(const HS_RECT_NAME& aa) const + { + return !(aa == *this); + } +#endif // Havok reeks + + // Intersect Test + friend int operator&&(const HS_RECT_NAME& a, const HS_RECT_NAME& b) + { + return a.fLeft < b.fRight && a.fRight > b.fLeft && + a.fTop < b.fBottom && a.fBottom > b.fTop; + } + + hsBool Intersect(const HS_RECT_NAME* r) + { + return this->Intersect(r->fLeft, r->fTop, r->fRight, r->fBottom); + } + hsBool Intersect(HS_RECT_TYPE left, HS_RECT_TYPE top, HS_RECT_TYPE right, HS_RECT_TYPE bottom) + { + if (left < fRight && top < fBottom && fLeft < right && fTop < bottom) + { if (left > fLeft) fLeft = left; + if (top > fTop) fTop = top; + if (right < fRight) fRight = right; + if (bottom < fBottom) fBottom = bottom; + return true; + } + return false; + } + hsBool Intersect(const HS_RECT_NAME* a, const HS_RECT_NAME* b) + { + if (a->fLeft < b->fRight && a->fTop < b->fBottom && b->fLeft < a->fRight && b->fTop < a->fBottom) + { *this = *b; + if (a->fLeft > fLeft) fLeft = a->fLeft; + if (a->fTop > fTop) fTop = a->fTop; + if (a->fRight < fRight) fRight = a->fRight; + if (a->fBottom < fBottom) fBottom = a->fBottom; + return true; + } + return false; // "this" is not changed + } + + HS_RECT_POINT* ToQuad(HS_RECT_POINT quad[4]) const + { + quad[0].fX = fLeft; quad[0].fY = fTop; + quad[1].fX = fRight; quad[1].fY = fTop; + quad[2].fX = fRight; quad[2].fY = fBottom; + quad[3].fX = fLeft; quad[3].fY = fBottom; + return quad; + } + + hsBool CornerTest(const HS_RECT_NAME* area, + HS_RECT_POINT* hitPt = nil, HS_RECT_POINT* oppositePt = nil) const + { + if (area->Contains(fLeft, fTop)) + { if (hitPt) hitPt->Set(fLeft, fTop); + if (oppositePt) oppositePt->Set(fRight, fBottom); + return true; + } + if (area->Contains(fLeft, fBottom)) + { if (hitPt) hitPt->Set(fLeft, fBottom); + if (oppositePt) oppositePt->Set(fRight, fTop); + return true; + } + if (area->Contains(fRight, fTop)) + { if (hitPt) hitPt->Set(fRight, fTop); + if (oppositePt) oppositePt->Set(fLeft, fBottom); + return true; + } + if (area->Contains(fRight, fBottom)) + { if (hitPt) hitPt->Set(fRight, fBottom); + if (oppositePt) oppositePt->Set(fLeft, fTop); + return true; + } + return false; + } + hsBool CornerTest(HS_RECT_POINT* pt, HS_RECT_TYPE tolerance, + HS_RECT_POINT* hitPt = nil, HS_RECT_POINT* oppositePt = nil) const + { + HS_RECT_NAME area = { pt->fX - tolerance, pt->fY - tolerance, + pt->fX + tolerance, pt->fY + tolerance }; + + return this->CornerTest(&area, hitPt, oppositePt); + } + +#if !(HS_RECT_EXTEND) +}; +#endif + +#undef HS_RECT_NAME +#undef HS_RECT_POINT +#undef HS_RECT_TYPE +#undef HS_RECT_EXTEND + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodec.h new file mode 100644 index 00000000..2e6e0a8f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodec.h @@ -0,0 +1,39 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsCodec_inc +#define hsCodec_inc + +class plMipmap; + +class hsCodec +{ +public: + virtual plMipmap *CreateCompressedMipmap( plMipmap *uncompressed ) = 0; + virtual plMipmap *CreateUncompressedMipmap( plMipmap *compressed, UInt8 bitDepth = 0 ) = 0; + virtual hsBool ColorizeCompMipmap( plMipmap *bMap, const UInt8 *colorMask ) = 0; +}; + +#endif // hsCodec_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodecManager.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodecManager.cpp new file mode 100644 index 00000000..fbb83302 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodecManager.cpp @@ -0,0 +1,162 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsCodecManager.h" +#include "plMipmap.h" +#include "hsDXTSoftwareCodec.h" + +#if HS_BUILD_FOR_WIN32 +#include "hsDXTDirectXCodec.h" +#endif + +hsCodecManager& hsCodecManager::Instance() +{ + static hsCodecManager the_instance; + static hsBool initialized = false; + + if (!initialized) + { + initialized = true; + hsDXTSoftwareCodec::Init(); + +#if HS_BUILD_FOR_WIN32 + hsDXTDirectXCodec::Init(); +#endif + } + + return the_instance; +} + +hsCodecManager::hsCodecManager() +{ +} + +plMipmap *hsCodecManager::CreateCompressedMipmap(UInt32 compressionFormat, plMipmap *uncompressed) +{ + Int32 i, j; + for (i = 0; i < fCodecTable.Count(); i++) + { + if (fCodecTable[i].fCompressionFormat == compressionFormat) + { + for (j = 0; j < fCodecTable[i].fCodecList.Count(); j++) + { + hsAssert(fCodecTable[i].fCodecList[j].fCodec != 0, + "Nil codec in hsCodecManager::CreateCompressedMipmap."); + + plMipmap *bm = + fCodecTable[i].fCodecList[j].fCodec->CreateCompressedMipmap(uncompressed); + + if (bm) + { + return bm; + } + } + + return nil; + } + } + + return nil; +} + +plMipmap *hsCodecManager::CreateUncompressedMipmap(plMipmap *compressed, UInt8 bitDepth) +{ + Int32 i, j; + for (i = 0; i < fCodecTable.Count(); i++) + { + if( fCodecTable[i].fCompressionFormat == compressed->fCompressionType ) + { + for (j = 0; j < fCodecTable[i].fCodecList.Count(); j++) + { + hsAssert(fCodecTable[i].fCodecList[j].fCodec != 0, + "Nil codec in hsCodecManager::CreateUncompressedMipmap."); + + plMipmap *bm = + fCodecTable[i].fCodecList[j].fCodec->CreateUncompressedMipmap(compressed, bitDepth); + + if (bm) + { + return bm; + } + } + + return nil; + } + } + + return nil; +} + +hsBool hsCodecManager::ColorizeCompMipmap( plMipmap *bMap, const UInt8 *colorMask ) +{ + Int32 i, j; + + + for( i = 0; i < fCodecTable.Count(); i++ ) + { + if( fCodecTable[ i ].fCompressionFormat == bMap->fCompressionType ) + { + for( j = 0; j < fCodecTable[ i ].fCodecList.Count(); j++ ) + { + hsAssert( fCodecTable[ i ].fCodecList[ j ].fCodec != 0, + "Nil codec in hsCodecManager::CreateUncompressedMipmap." ); + + if( fCodecTable[ i ].fCodecList[ j ].fCodec->ColorizeCompMipmap( bMap, colorMask ) ) + return true; + } + return false; + } + } + return false; +} + +hsBool hsCodecManager::Register(hsCodec *codec, UInt32 compressionFormat, hsScalar priority) +{ + Int32 i, j; + for (i = 0; i < fCodecTable.Count(); i++) + { + if (fCodecTable[i].fCompressionFormat == compressionFormat) + { + j = 0; + while ((j < fCodecTable[i].fCodecList.Count()) && + fCodecTable[i].fCodecList[j].fPriority > priority) + ++j; + + hsCodecEntry tempCodecEntry(priority, codec); + fCodecTable[i].fCodecList.InsertAtIndex(j, tempCodecEntry); + + return true; + } + } + + hsCodecList tempCodecList(compressionFormat); + fCodecTable.Append(tempCodecList); + + hsCodecEntry tempCodecEntry(priority, codec); + fCodecTable[fCodecTable.Count() - 1].fCodecList.Append(tempCodecEntry); + + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodecManager.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodecManager.h new file mode 100644 index 00000000..05556d48 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsCodecManager.h @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// hsCodecManager Class Header // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 6.7.2001 mcn - Updated for new bitmap classes. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef hsCodecManager_inc +#define hsCodecManager_inc + +#include "HeadSpin.h" +#include "hsTemplates.h" + +class hsCodec; +class plMipmap; + +class hsCodecManager +{ +private: + hsCodecManager(); +public: + ~hsCodecManager() { } + static hsCodecManager& Instance(); + + plMipmap *CreateCompressedMipmap( UInt32 compressionFormat, plMipmap *uncompressed ); + plMipmap *CreateUncompressedMipmap( plMipmap *compressed, UInt8 bitDepth = 0 ); + hsBool ColorizeCompMipmap( plMipmap *bMap, const UInt8 *colorMask ); + + hsBool Register(hsCodec *codec, UInt32 compressionFormat, hsScalar priority); + + /// Decompression flags + enum { + kBitDepthMask = 0x0003, + kCompOrderMask = 0x0004 + }; + enum { /// Bit depths + kDontCareDepth = 0x0000, + k16BitDepth = 0x0001, + k32BitDepth = 0x0002 + }; + enum { /// Byte orders + kNormalCompOrder = 0x0000, // DirectX, Glide + kWeirdCompOrder = 0x0004 // OpenGL + }; + +private: + struct hsCodecEntry + { + hsCodecEntry() : fPriority(0), fCodec(nil) { } + hsCodecEntry(hsScalar p, hsCodec *c) : fPriority(p), fCodec(c) { } + + hsScalar fPriority; + hsCodec *fCodec; + }; + + struct hsCodecList + { + hsCodecList() : fCompressionFormat(0) { } + hsCodecList(UInt32 f) : fCompressionFormat(f) { } + + UInt32 fCompressionFormat; + hsTArray fCodecList; + }; + + hsTArray fCodecTable; +}; + +#endif // hsCodecManager_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTDirectXCodec.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTDirectXCodec.cpp new file mode 100644 index 00000000..3e1c5766 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTDirectXCodec.cpp @@ -0,0 +1,1226 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// hsDXTDirectXCodec Class Functions // +// DirectX-based codec functions // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 6.8.2001 mcn - Got a much-needed Plasma 2.0/DX8 update. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsConfig.h" +#include "hsWindows.h" + +#include +#include + +#include "hsTypes.h" +#include "hsDXTDirectXCodec.h" +#include "plMipmap.h" +#include "hsCodecManager.h" +#include "../plPipeline/hsGDDrawDllLoad.h" + +namespace { + typedef HRESULT(WINAPI * DIRECTDRAWCREATEEX)( GUID*, VOID**, REFIID, IUnknown* ); +} + +enum +{ + D3DTEXTURE_FMT_ARGB32_8888 = 0x00000002, // No. 2: 8888 ARGB (32-bit) format. + D3DTEXTURE_FMT_FOURCC_DXT1 = 0x00002000, // No.14: DXTn FourCC (DXT1), format. + D3DTEXTURE_FMT_FOURCC_DXT5 = 0x00020000, // No.18: DXTn FourCC (DXT5), format. +}; + +hsBool hsDXTDirectXCodec::fRegistered = false; + +//// Small Init Functions ///////////////////////////////////////////////////// + +hsDXTDirectXCodec& hsDXTDirectXCodec::Instance() +{ + static hsDXTDirectXCodec the_instance; + + return the_instance; +} + +hsDXTDirectXCodec::hsDXTDirectXCodec() : fDirectDraw( nil ), fDDLibraryInstance( nil ), fFlags( 0 ) +{ +} + +hsDXTDirectXCodec::~hsDXTDirectXCodec() +{ + if ((fFlags & kExternalInit) == 0) + { + if (fDirectDraw) + { + fDirectDraw->Release(); + } + + if (fDDLibraryInstance) + { + FreeLibrary(fDDLibraryInstance); + } + } + + fDirectDraw = nil; + fDDLibraryInstance = nil; +} + +hsBool hsDXTDirectXCodec::Register() +{ + return hsCodecManager::Instance().Register( &(Instance()), plMipmap::kDirectXCompression, 500 ); +} + +//// Initialize /////////////////////////////////////////////////////////////// + +void hsDXTDirectXCodec::Initialize( IDirect3DDevice8 *directDraw ) +{ +/* if (directDraw) + { + fFlags |= (kInitialized | kExternalInit); + } + else + { + fFlags &= ~(kInitialized | kExternalInit); + } + fDirectDraw = directDraw; +*/ + // Don't do anything--force IInitialize to run +} + +//// IInitialize ////////////////////////////////////////////////////////////// +// Gotta initialize D3D ourself and create a device and everything. This is +// a LOT messier than it was in DX7, since this time we got D3D to deal with + +hsBool hsDXTDirectXCodec::IInitialize() +{ + fFlags |= kInitialized; + +// if( hsGDDrawDllLoad::GetDDrawDll() == nil ) +// return false; + + DIRECTDRAWCREATEEX DirectDrawCreateEx = 0; + + // Initialize DirectDraw + HRESULT hr; + DirectDrawCreateEx = (DIRECTDRAWCREATEEX)GetProcAddress( hsGDDrawDllLoad::GetD3DDll(), "DirectDrawCreateEx" ); + if( DirectDrawCreateEx == nil ) + return false; + + /// Using EMULATIONONLY here usually fails--using NULL forces the + /// use of the standard display driver, which DOES work. + if (FAILED(hr = DirectDrawCreateEx((GUID FAR *)NULL/*DDCREATE_EMULATIONONLY*/, (VOID**)&fDirectDraw, IID_IDirectDraw7, NULL))) + return false; + + if (FAILED(hr = fDirectDraw->SetCooperativeLevel(NULL, DDSCL_NORMAL))) + return false; + + return true; +} + +//// CreateCompressedMipmap /////////////////////////////////////////////////// +// Updated 8.15.2000 mcn to generate uncompressed mipmaps down to 1x1 (the +// decompressor better know how to deal with this!) Also cleaned up so I can +// read it :) + +plMipmap *hsDXTDirectXCodec::CreateCompressedMipmap( plMipmap *uncompressed ) +{ + const plMipmap *b = uncompressed; + plMipmap *compressed = nil; + + UInt32 numLevels = 1, numCompLevels; + UInt32 compFormat, totalSize, compSize; + Int32 width, height, blockSize, i; + + + /// Sanity checks, initialization, etc. + if( !Initialized() ) + { + if( !IInitialize() ) + return nil; + } + + hsAssert( fRegistered, "Calling member of unregistered codec." ); + hsAssert( !uncompressed->IsCompressed(), "Trying to re-compress compressed bitmap." ); + + if( !fDirectDraw ) + return nil; + + /// Check width and height + if( ( uncompressed->GetWidth() | uncompressed->GetHeight() ) & 0x03 ) + return nil; /// Width and height must be multiple of 4 + + /// This used to be checked later--but WHY? We can check it now and + /// potentially avoid a lot of headache + compFormat = ICompressedFormat( b ); + if( !compFormat ) + return nil; + + /// Precalc this + blockSize = ( compFormat == D3DTEXTURE_FMT_FOURCC_DXT1 ) ? 8 : 16; + + + { + compSize = 0; + numCompLevels = 0; + + width = uncompressed->GetWidth(); + height = uncompressed->GetHeight(); + + /// Count the levels we're going to compress + for( i = 0; i < uncompressed->GetNumLevels(); i++ ) + { + if( ( width | height ) & 0x03 ) + break; + + numCompLevels++; + compSize += blockSize * width * height >> 4; + width >>= 1; + height >>= 1; + } + + /// NEW 8.15.2000 mcn - Now count up remaining levels down to 1x1 + totalSize = compSize; + numLevels = numCompLevels; + for( ; i < uncompressed->GetNumLevels(); i++ ) + { + totalSize += uncompressed->GetLevelSize( (UInt8)i ); + + width >>= 1; + height >>= 1; + numLevels++; + } + } + + + /// Create source DirectDraw surface + IDirectDrawSurface7 *srcSurface = IMakeDirect3DSurface( D3DTEXTURE_FMT_ARGB32_8888, + numCompLevels, uncompressed->GetWidth(), + uncompressed->GetHeight() ); + + IFillSurface( (hsRGBAColor32 *)uncompressed->GetImage(), numCompLevels, srcSurface ); + + /// Create destination DirectDraw surface + IDirectDrawSurface7 *destSurface = IMakeDirect3DSurface( compFormat, numCompLevels, + uncompressed->GetWidth(), + uncompressed->GetHeight() ); + ICopySurface( destSurface, srcSurface, numCompLevels ); + + + /// Now set up the data structures + compressed = TRACKED_NEW plMipmap( uncompressed->GetWidth(), uncompressed->GetHeight(), plMipmap::kARGB32Config, + uncompressed->GetNumLevels(), plMipmap::kDirectXCompression, + ( compFormat == D3DTEXTURE_FMT_FOURCC_DXT1 ) ? + plMipmap::DirectXInfo::kDXT1 : plMipmap::DirectXInfo::kDXT5 ); + + /// Copy compressed data back from the surface + IFillFromSurface( (hsRGBAColor32 *)compressed->GetImage(), numCompLevels, destSurface ); + + /// Finally, copy back any remaining data + if( numCompLevels < numLevels ) + { + /// Now copy the rest straight over + for( i = numCompLevels; i < numLevels; i++ ) + { + memcpy( compressed->GetLevelPtr( (UInt8)i ), uncompressed->GetLevelPtr( (UInt8)i ), + uncompressed->GetLevelSize( (UInt8)i ) ); + } + } + + /// All done! + return compressed; +} + +//// CreateUncompressedMipmap ///////////////////////////////////////////////// +// Updated 8.15.2000 mcn to support mipmaps down to 1x1. See +// CreateCompressedMipmap(). Also cleaned up the code a bit (too tired to +// clean it ALL up) + +plMipmap *hsDXTDirectXCodec::CreateUncompressedMipmap( plMipmap *compressed, + UInt8 bitDepth ) +{ + /// Use software decompression ALWAYS + return nil; +/* + plMipmap *uncompressed = nil; + + UInt32 mmlvs, formatType, numCompLevels; + Int32 totalSize, width, height, i; + + + /// Check bit depth--if it's 16 bit, we don't support it (for now) + if( ( bitDepth & hsCodecManager::kBitDepthMask ) != hsCodecManager::k16BitDepth ) + return nil; + + if( !Initialized() ) + { + if( !IInitialize() ) + return nil; + } + + /// Sanity checks + hsAssert( fRegistered, "Calling member of unregistered codec." ); + hsAssert( compressed->fFlags & hsGMipmap::kCompressed, "Trying to uncompress already uncompressed bitmap." ); + hsAssert( compressed->fCompressionFormat == hsGMipmap::kDirectXCompression, "Uncompressing wrong format." ); + hsAssert( ( compressed->fDirectXInfo.fCompressionType == hsGMipmap::DirectXInfo::kDXT1 ) || + ( compressed->fDirectXInfo.fCompressionType == hsGMipmap::DirectXInfo::kDXT5 ), + "Unsupported directX compression format." ); + + if( !fDirectDraw ) + return nil; + + if( compressed->fDirectXInfo.fCompressionType == hsGMipmap::DirectXInfo::kDXT5 ) + { + // Fall out since directX software decompressor doesn't work... + return nil; + } + + if( compressed->fFlags & hsGMipmap::kMipMap ) + { + mmCompressed->SetLevel( 0 ); + mmlvs = mmCompressed->GetNumLevels(); + + for( i = 0, numCompLevels = 0; i < mmlvs; i++ ) + { + mmCompressed->SetLevel( i ); + if( ( mmCompressed->fWidth | mmCompressed->fHeight ) & 0x03 ) + break; + numCompLevels++; + } + mmCompressed->SetLevel( 0 ); + } + else + { + mmlvs = numCompLevels = 1; + } + + /// Get format type + formatType = ( compressed->fDirectXInfo.fCompressionType == hsGMipmap::DirectXInfo::kDXT1 ) ? + D3DTEXTURE_FMT_FOURCC_DXT1 : D3DTEXTURE_FMT_FOURCC_DXT5; + + /// Make the surfaces (decompress in the process) + IDirectDrawSurface7 *srcSurface = IMakeDirect3DSurface( formatType, numCompLevels, + compressed->fWidth, compressed->fHeight ); + IFillSurface( (hsRGBAColor32 *)compressed->fImage, numCompLevels, srcSurface ); + + + IDirectDrawSurface7 *destSurface = IMakeDirect3DSurface( D3DTEXTURE_FMT_ARGB32_8888, numCompLevels, + compressed->fWidth, compressed->fHeight ); + ICopySurface( destSurface, srcSurface, numCompLevels ); + + + /// Set up the uncompressed data structure + if( compressed->fFlags & hsGMipmap::kMipMap ) + { + mmUncompressed = TRACKED_NEW hsGMipmapClass; + uncompressed = mmUncompressed; + } + else + { + uncompressed = TRACKED_NEW plMipmap; + mmUncompressed = nil; + } + + uncompressed->fWidth = compressed->fWidth; + uncompressed->fHeight = compressed->fHeight; + uncompressed->fPixelSize = 32; + uncompressed->fRowBytes = uncompressed->fWidth * uncompressed->fPixelSize >> 3; + uncompressed->fFlags = compressed->fFlags & ~hsGMipmap::kCompressed; + uncompressed->fCompressionFormat = 0; + uncompressed->fDirectXInfo.fBlockSize = 0; + uncompressed->fDirectXInfo.fCompressionType = hsGMipmap::DirectXInfo::kError; + + /// Handle mipmaps or single image? + if( mmUncompressed ) + { + totalSize = 0; + width = compressed->fWidth; + height = compressed->fHeight; + for( i = 0; i < mmlvs; i++ ) + { + totalSize += width * height * uncompressed->fPixelSize >> 3; + width >>= 1; + height >>= 1; + } + mmUncompressed->fImage = HSMemory::New( totalSize ); + mmUncompressed->SetData( mmUncompressed->fImage ); + mmUncompressed->SetNumLevels( mmlvs ); + } + else + { + uncompressed->fImage = HSMemory::New( uncompressed->fWidth * uncompressed->fHeight * + uncompressed->fPixelSize >> 3 ); + } + + /// Copy over compressed levels + IFillFromSurface( (hsRGBAColor32 *)uncompressed->fImage, numCompLevels, destSurface ); + + /// Now take care of the remainder levels + if( mmUncompressed ) + { + for( i = numCompLevels; i < mmlvs; i++ ) + { + mmUncompressed->SetLevel( i ); + mmCompressed->SetLevel( i ); + + memcpy( mmUncompressed->fImage, mmCompressed->fImage, mmCompressed->ImageSize() ); + } + mmUncompressed->SetLevel( 0 ); + mmCompressed->SetLevel( 0 ); + } + + /// All done! + return uncompressed; +*/ +} + +UInt32 hsDXTDirectXCodec::ICompressedFormat(const plMipmap *uncompressed) +{ + if( uncompressed->GetFlags() & plMipmap::kAlphaChannelFlag ) + return D3DTEXTURE_FMT_FOURCC_DXT5; + + return D3DTEXTURE_FMT_FOURCC_DXT1; +} + +//// IFindTextureFormat /////////////////////////////////////////////////////// +// Changed to a local function 6.8.2001 to avoid certain annoying problems +// with headers and the compiler. + +DDPIXELFORMAT IFindTextureFormat(UInt32 formatType) +{ + DDPIXELFORMAT ddPixelFormat; + memset( &ddPixelFormat, 0x00, sizeof(DDPIXELFORMAT) ); + ddPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + + switch (formatType) + { + case D3DTEXTURE_FMT_ARGB32_8888: + ddPixelFormat.dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS; + ddPixelFormat.dwFourCC = 0; + ddPixelFormat.dwRGBBitCount = 32; + ddPixelFormat.dwRBitMask = 0x00ff0000; + ddPixelFormat.dwGBitMask = 0x0000ff00; + ddPixelFormat.dwBBitMask = 0x000000ff; + ddPixelFormat.dwRGBAlphaBitMask = 0xff000000; + return ddPixelFormat; + case D3DTEXTURE_FMT_FOURCC_DXT1: + ddPixelFormat.dwFlags = DDPF_FOURCC; + ddPixelFormat.dwFourCC = FOURCC_DXT1; + return ddPixelFormat; + case D3DTEXTURE_FMT_FOURCC_DXT5: + ddPixelFormat.dwFlags = DDPF_FOURCC; + ddPixelFormat.dwFourCC = FOURCC_DXT5; + return ddPixelFormat; + default: + hsAssert(false, "Unknown texture format selected"); + return ddPixelFormat; + } +} + +IDirectDrawSurface7 *hsDXTDirectXCodec::IMakeDirect3DSurface(UInt32 formatType, UInt32 mipMapLevels, + UInt32 width, UInt32 height) +{ + DDSURFACEDESC2 ddsd2; + memset( &ddsd2, 0x00, sizeof(DDSURFACEDESC2) ); + ddsd2.dwSize = sizeof(DDSURFACEDESC2); + ddsd2.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT; + ddsd2.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + if(mipMapLevels > 1 ) + { + ddsd2.dwFlags |= DDSD_MIPMAPCOUNT; + ddsd2.ddsCaps.dwCaps |= DDSCAPS_MIPMAP|DDSCAPS_COMPLEX; + } + ddsd2.dwMipMapCount = mipMapLevels; + ddsd2.dwWidth = width; + ddsd2.dwHeight = height; + ddsd2.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + + ddsd2.ddpfPixelFormat = IFindTextureFormat(formatType); + + IDirectDrawSurface7 *lpDDsTex; + HRESULT res = fDirectDraw->CreateSurface( &ddsd2, &lpDDsTex, NULL ); + if( S_OK != res ) + CheckErrorCode(res); + return lpDDsTex; +} + +void hsDXTDirectXCodec::IFillSurface(hsRGBAColor32* src, UInt32 mmlvs, IDirectDrawSurface7 *pddsDest) +{ + UInt8 *pTexDat = (UInt8*)src; + UInt32 cap = 0; + + HRESULT hr; + DDSCAPS2 ddsCaps2; + + for( WORD wNum=0; wNum < mmlvs; wNum++ ) + { + DDSURFACEDESC2 ddsd2; + memset( &ddsd2, 0x00, sizeof(DDSURFACEDESC2) ); + ddsd2.dwSize = sizeof( DDSURFACEDESC2 ); + + hr = pddsDest->Lock( NULL, &ddsd2, DDLOCK_WAIT | DDLOCK_WRITEONLY | DDLOCK_DISCARDCONTENTS, NULL ); + if (ddsd2.ddpfPixelFormat.dwFlags == DDPF_FOURCC) + { + Int32 blockSize = (ddsd2.ddpfPixelFormat.dwFourCC == FOURCC_DXT1) ? 8 : 16; + cap = ddsd2.dwHeight * ddsd2.dwWidth * blockSize >> 4; + memcpy( (char*)ddsd2.lpSurface, pTexDat, cap ); + pTexDat += cap; + } + else + { + hsAssert(ddsd2.ddpfPixelFormat.dwRGBBitCount == 32, "Format not supported."); + + Int32* dest = (Int32*)ddsd2.lpSurface; + Int32 pixelCount = ddsd2.dwHeight * ddsd2.dwWidth; + Int32 j; + for (j = 0; j < pixelCount; ++j) + { + dest[j] = ( ( src->a << 24 ) | ( src->r << 16 ) | ( src->g << 8 ) | src->b ); + src++; + } + } + hr = pddsDest->Unlock( NULL ); + + memset( &ddsCaps2, 0x00, sizeof(DDSCAPS2) ); + ddsCaps2.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP; + if( SUCCEEDED( pddsDest->GetAttachedSurface( &ddsCaps2, &pddsDest ) ) ) + pddsDest->Release(); + } +} + +void hsDXTDirectXCodec::IFillFromSurface(hsRGBAColor32* dest, UInt32 mmlvs, IDirectDrawSurface7 *pddsSrc) +{ + UInt8 *pTexDat = (UInt8 *)dest; + UInt32 cap = 0; + + HRESULT hr; + DDSCAPS2 ddsCaps2; + DDSURFACEDESC2 ddsd2; + + + for( WORD wNum = 0; wNum < mmlvs; wNum++ ) + { + memset( &ddsd2, 0, sizeof( DDSURFACEDESC2 ) ); + ddsd2.dwSize = sizeof( DDSURFACEDESC2 ); + + hr = pddsSrc->Lock( NULL, &ddsd2, DDLOCK_WAIT | DDLOCK_READONLY | + DDLOCK_DISCARDCONTENTS, NULL ); + + if( ddsd2.ddpfPixelFormat.dwFlags == DDPF_FOURCC ) + { + Int32 blockSize = ( ddsd2.ddpfPixelFormat.dwFourCC == FOURCC_DXT1 ) ? 8 : 16; + + cap = ddsd2.dwHeight * ddsd2.dwWidth * blockSize >> 4; + memcpy( pTexDat, (char*)ddsd2.lpSurface, cap ); + pTexDat += cap; + } + else + { + hsAssert( ddsd2.ddpfPixelFormat.dwRGBBitCount == 32, "Format not supported." ); + + Int32* src = (Int32*)ddsd2.lpSurface; + Int32 pixelCount = ddsd2.dwHeight * ddsd2.dwWidth; + Int32 j; + for (j = 0; j < pixelCount; ++j) + { + dest->a = (UInt8)((src[j] >> 24) & 0xff); + dest->r = (UInt8)((src[j] >> 16) & 0xff); + dest->g = (UInt8)((src[j] >> 8) & 0xff); + dest->b = (UInt8)((src[j]) & 0xff); + dest++; + } + } + hr = pddsSrc->Unlock( NULL ); + + memset( &ddsCaps2, 0, sizeof( DDSCAPS2 ) ); + ddsCaps2.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP; + + if( SUCCEEDED( pddsSrc->GetAttachedSurface( &ddsCaps2, &pddsSrc ) ) ) + pddsSrc->Release(); + } +} + +void hsDXTDirectXCodec::ICopySurface(IDirectDrawSurface7 *dest, IDirectDrawSurface7 *src, Int32 mipMapLevels) +{ + DDSURFACEDESC2 ddsd2; + memset( &ddsd2, 0x00, sizeof(DDSURFACEDESC2) ); + ddsd2.dwSize = sizeof( DDSURFACEDESC2 ); + + DDSCAPS2 ddsCaps; + + ZeroMemory(&ddsCaps, sizeof(ddsCaps)); + ddsCaps.dwCaps = DDSCAPS_TEXTURE; + if (mipMapLevels > 1) + ddsCaps.dwCaps2 = DDSCAPS2_MIPMAPSUBLEVEL; + + IDirectDrawSurface7 *lpSrc = src; + IDirectDrawSurface7 *lpDst = dest; + + int mmlvs = mipMapLevels; + + HRESULT hr; + int i; + for( i = 0; i < mmlvs; i++ ) + { + if( FAILED(hr = lpDst->Blt(NULL, lpSrc, NULL, DDBLT_WAIT, NULL)) ) + { + hsAssert(false, "Uh oh!"); + } + + if (FAILED(hr = lpSrc->GetAttachedSurface(&ddsCaps, &lpSrc)) + ||FAILED(hr = lpDst->GetAttachedSurface(&ddsCaps, &lpDst))) + { + break; + } + lpSrc->Release(); + lpDst->Release(); + } +} + +void hsDXTDirectXCodec::CheckErrorCode(HRESULT res) +{ + switch( res ) + { + // This object is already initialized + case DDERR_ALREADYINITIALIZED: + hsAssert(false, "DDERR_ALREADYINITIALIZED."); + break; + + // This surface can not be attached to the requested surface. + case DDERR_CANNOTATTACHSURFACE: + hsAssert(false, "DDERR_CANNOTATTACHSURFACE."); + break; + + // This surface can not be detached from the requested surface. + case DDERR_CANNOTDETACHSURFACE: + hsAssert(false, "DDERR_CANNOTDETACHSURFACE."); + break; + + // Support is currently not available. + case DDERR_CURRENTLYNOTAVAIL: + hsAssert(false, "DDERR_CURRENTLYNOTAVAIL."); + break; + + // An exception was encountered while performing the requested operation + case DDERR_EXCEPTION: + hsAssert(false, "DDERR_EXCEPTION."); + break; + + // Generic failure. + case DDERR_GENERIC: + hsAssert(false, "DDERR_GENERIC."); + break; + + // Height of rectangle provided is not a multiple of reqd alignment + case DDERR_HEIGHTALIGN: + hsAssert(false, "DDERR_HEIGHTALIGN."); + break; + + // Unable to match primary surface creation request with existing + // primary surface. + case DDERR_INCOMPATIBLEPRIMARY: + hsAssert(false, "DDERR_INCOMPATIBLEPRIMARY."); + break; + + // One or more of the caps bits passed to the callback are incorrect. + case DDERR_INVALIDCAPS: + hsAssert(false, "DDERR_INVALIDCAPS."); + break; + + // DirectDraw does not support provided Cliplist. + case DDERR_INVALIDCLIPLIST: + hsAssert(false, "DDERR_INVALIDCLIPLIST."); + break; + + // DirectDraw does not support the requested mode + case DDERR_INVALIDMODE: + hsAssert(false, "DDERR_INVALIDMODE."); + break; + + // DirectDraw received a pointer that was an invalid DIRECTDRAW object. + case DDERR_INVALIDOBJECT: + hsAssert(false, "DDERR_INVALIDOBJECT."); + break; + + // One or more of the parameters passed to the callback function are + // incorrect. + case DDERR_INVALIDPARAMS: + hsAssert(false, "DDERR_INVALIDPARAMS."); + break; + + // pixel format was invalid as specified + case DDERR_INVALIDPIXELFORMAT: + hsAssert(false, "DDERR_INVALIDPIXELFORMAT."); + break; + + // Rectangle provided was invalid. + case DDERR_INVALIDRECT: + hsAssert(false, "DDERR_INVALIDRECT."); + break; + + // Operation could not be carried out because one or more surfaces are locked + case DDERR_LOCKEDSURFACES: + hsAssert(false, "DDERR_LOCKEDSURFACES."); + break; + + // There is no 3D present. + case DDERR_NO3D: + hsAssert(false, "DDERR_NO3D."); + break; + + // Operation could not be carried out because there is no alpha accleration + // hardware present or available. + case DDERR_NOALPHAHW: + hsAssert(false, "DDERR_NOALPHAHW."); + break; + + // no clip list available + case DDERR_NOCLIPLIST: + hsAssert(false, "DDERR_NOCLIPLIST."); + break; + + // Operation could not be carried out because there is no color conversion + // hardware present or available. + case DDERR_NOCOLORCONVHW: + hsAssert(false, "DDERR_NOCOLORCONVHW."); + break; + + // Create function called without DirectDraw object method SetCooperativeLevel + // being called. + case DDERR_NOCOOPERATIVELEVELSET: + hsAssert(false, "DDERR_NOCOOPERATIVELEVELSET."); + break; + + // Surface doesn't currently have a color key + case DDERR_NOCOLORKEY: + hsAssert(false, "DDERR_NOCOLORKEY."); + break; + + // Operation could not be carried out because there is no hardware support + // of the dest color key. + case DDERR_NOCOLORKEYHW: + hsAssert(false, "DDERR_NOCOLORKEYHW."); + break; + + // No DirectDraw support possible with current display driver + case DDERR_NODIRECTDRAWSUPPORT: + hsAssert(false, "DDERR_NODIRECTDRAWSUPPORT."); + break; + + // Operation requires the application to have exclusive mode but the + // application does not have exclusive mode. + case DDERR_NOEXCLUSIVEMODE: + hsAssert(false, "DDERR_NOEXCLUSIVEMODE."); + break; + + // Flipping visible surfaces is not supported. + case DDERR_NOFLIPHW: + hsAssert(false, "DDERR_NOFLIPHW."); + break; + + // There is no GDI present. + case DDERR_NOGDI: + hsAssert(false, "DDERR_NOGDI."); + break; + + // Operation could not be carried out because there is no hardware present + // or available. + case DDERR_NOMIRRORHW: + hsAssert(false, "DDERR_NOMIRRORHW."); + break; + + // Requested item was not found + case DDERR_NOTFOUND: + hsAssert(false, "DDERR_NOTFOUND."); + break; + + // Operation could not be carried out because there is no overlay hardware + // present or available. + case DDERR_NOOVERLAYHW: + hsAssert(false, "DDERR_NOOVERLAYHW."); + break; + + // Operation could not be carried out because the source and destination + // rectangles are on the same surface and overlap each other. + case DDERR_OVERLAPPINGRECTS: + hsAssert(false, "DDERR_OVERLAPPINGRECTS."); + break; + + // Operation could not be carried out because there is no appropriate raster + // op hardware present or available. + case DDERR_NORASTEROPHW: + hsAssert(false, "DDERR_NORASTEROPHW."); + break; + + // Operation could not be carried out because there is no rotation hardware + // present or available. + case DDERR_NOROTATIONHW: + hsAssert(false, "DDERR_NOROTATIONHW."); + break; + + // Operation could not be carried out because there is no hardware support + // for stretching + case DDERR_NOSTRETCHHW: + hsAssert(false, "DDERR_NOSTRETCHHW."); + break; + + // DirectDrawSurface is not in 4 bit color palette and the requested operation + // requires 4 bit color palette. + case DDERR_NOT4BITCOLOR: + hsAssert(false, "DDERR_NOT4BITCOLOR."); + break; + + // DirectDrawSurface is not in 4 bit color index palette and the requested + // operation requires 4 bit color index palette. + case DDERR_NOT4BITCOLORINDEX: + hsAssert(false, "DDERR_NOT4BITCOLORINDEX."); + break; + + // DirectDraw Surface is not in 8 bit color mode and the requested operation + // requires 8 bit color. + case DDERR_NOT8BITCOLOR: + hsAssert(false, "DDERR_NOT8BITCOLOR."); + break; + + // Operation could not be carried out because there is no texture mapping + // hardware present or available. + case DDERR_NOTEXTUREHW: + hsAssert(false, "DDERR_NOTEXTUREHW."); + break; + + // Operation could not be carried out because there is no hardware support + // for vertical blank synchronized operations. + case DDERR_NOVSYNCHW: + hsAssert(false, "DDERR_NOVSYNCHW."); + break; + + // Operation could not be carried out because there is no hardware support + // for zbuffer blting. + case DDERR_NOZBUFFERHW: + hsAssert(false, "DDERR_NOZBUFFERHW."); + break; + + // Overlay surfaces could not be z layered based on their BltOrder because + // the hardware does not support z layering of overlays. + case DDERR_NOZOVERLAYHW: + hsAssert(false, "DDERR_NOZOVERLAYHW."); + break; + + // The hardware needed for the requested operation has already been + // allocated. + case DDERR_OUTOFCAPS: + hsAssert(false, "DDERR_OUTOFCAPS."); + break; + + // DirectDraw does not have enough memory to perform the operation. + case DDERR_OUTOFMEMORY: + hsAssert(false, "DDERR_OUTOFMEMORY."); + break; + + // DirectDraw does not have enough memory to perform the operation. + case DDERR_OUTOFVIDEOMEMORY: + hsAssert(false, "DDERR_OUTOFVIDEOMEMORY."); + break; + + // hardware does not support clipped overlays + case DDERR_OVERLAYCANTCLIP: + hsAssert(false, "DDERR_OVERLAYCANTCLIP."); + break; + + // Can only have ony color key active at one time for overlays + case DDERR_OVERLAYCOLORKEYONLYONEACTIVE: + hsAssert(false, "DDERR_OVERLAYCOLORKEYONLYONEACTIVE."); + break; + + // Access to this palette is being refused because the palette is already + // locked by another thread. + case DDERR_PALETTEBUSY: + hsAssert(false, "DDERR_PALETTEBUSY."); + break; + + // No src color key specified for this operation. + case DDERR_COLORKEYNOTSET: + hsAssert(false, "DDERR_COLORKEYNOTSET."); + break; + + // This surface is already attached to the surface it is being attached to. + case DDERR_SURFACEALREADYATTACHED: + hsAssert(false, "DDERR_SURFACEALREADYATTACHED."); + break; + + // This surface is already a dependency of the surface it is being made a + // dependency of. + case DDERR_SURFACEALREADYDEPENDENT: + hsAssert(false, "DDERR_SURFACEALREADYDEPENDENT."); + break; + + // Access to this surface is being refused because the surface is already + // locked by another thread. + case DDERR_SURFACEBUSY: + hsAssert(false, "DDERR_SURFACEBUSY."); + break; + + // Access to this surface is being refused because no driver exists + // which can supply a pointer to the surface. + // This is most likely to happen when attempting to lock the primary + // surface when no DCI provider is present. + // Will also happen on attempts to lock an optimized surface. + case DDERR_CANTLOCKSURFACE: + hsAssert(false, "DDERR_CANTLOCKSURFACE."); + break; + + // Access to Surface refused because Surface is obscured. + case DDERR_SURFACEISOBSCURED: + hsAssert(false, "DDERR_SURFACEISOBSCURED."); + break; + + // Access to this surface is being refused because the surface is gone. + // The DIRECTDRAWSURFACE object representing this surface should + // have Restore called on it. + case DDERR_SURFACELOST: + hsAssert(false, "DDERR_SURFACELOST."); + break; + + // The requested surface is not attached. + case DDERR_SURFACENOTATTACHED: + hsAssert(false, "DDERR_SURFACENOTATTACHED."); + break; + + // Height requested by DirectDraw is too large. + case DDERR_TOOBIGHEIGHT: + hsAssert(false, "DDERR_TOOBIGHEIGHT."); + break; + + // Size requested by DirectDraw is too large -- The individual height and + // width are OK. + case DDERR_TOOBIGSIZE: + hsAssert(false, "DDERR_TOOBIGSIZE."); + break; + + // Width requested by DirectDraw is too large. + case DDERR_TOOBIGWIDTH: + hsAssert(false, "DDERR_TOOBIGWIDTH."); + break; + + // Action not supported. + case DDERR_UNSUPPORTED: + hsAssert(false, "DDERR_UNSUPPORTED."); + break; + + // FOURCC format requested is unsupported by DirectDraw + case DDERR_UNSUPPORTEDFORMAT: + hsAssert(false, "DDERR_UNSUPPORTEDFORMAT."); + break; + + // Bitmask in the pixel format requested is unsupported by DirectDraw + case DDERR_UNSUPPORTEDMASK: + hsAssert(false, "DDERR_UNSUPPORTEDMASK."); + break; + + // The specified stream contains invalid data + case DDERR_INVALIDSTREAM: + hsAssert(false, "DDERR_INVALIDSTREAM."); + break; + + // vertical blank is in progress + case DDERR_VERTICALBLANKINPROGRESS: + hsAssert(false, "DDERR_VERTICALBLANKINPROGRESS."); + break; + + // Informs DirectDraw that the previous Blt which is transfering information + // to or from this Surface is incomplete. + case DDERR_WASSTILLDRAWING: + hsAssert(false, "DDERR_WASSTILLDRAWING."); + break; + + // Rectangle provided was not horizontally aligned on reqd. boundary + case DDERR_XALIGN: + hsAssert(false, "DDERR_XALIGN."); + break; + + // The GUID passed to DirectDrawCreate is not a valid DirectDraw driver + // identifier. + case DDERR_INVALIDDIRECTDRAWGUID: + hsAssert(false, "DDERR_INVALIDDIRECTDRAWGUID."); + break; + + // A DirectDraw object representing this driver has already been created + // for this process. + case DDERR_DIRECTDRAWALREADYCREATED: + hsAssert(false, "DDERR_DIRECTDRAWALREADYCREATED."); + break; + + // A hardware only DirectDraw object creation was attempted but the driver + // did not support any hardware. + case DDERR_NODIRECTDRAWHW: + hsAssert(false, "DDERR_NODIRECTDRAWHW."); + break; + + // this process already has created a primary surface + case DDERR_PRIMARYSURFACEALREADYEXISTS: + hsAssert(false, "DDERR_PRIMARYSURFACEALREADYEXISTS."); + break; + + // software emulation not available. + case DDERR_NOEMULATION: + hsAssert(false, "DDERR_NOEMULATION."); + break; + + // region passed to Clipper::GetClipList is too small. + case DDERR_REGIONTOOSMALL: + hsAssert(false, "DDERR_REGIONTOOSMALL."); + break; + + // an attempt was made to set a clip list for a clipper objec that + // is already monitoring an hwnd. + case DDERR_CLIPPERISUSINGHWND: + hsAssert(false, "DDERR_CLIPPERISUSINGHWND."); + break; + + // No clipper object attached to surface object + case DDERR_NOCLIPPERATTACHED: + hsAssert(false, "DDERR_NOCLIPPERATTACHED."); + break; + + // Clipper notification requires an HWND or + // no HWND has previously been set as the CooperativeLevel HWND. + case DDERR_NOHWND: + hsAssert(false, "DDERR_NOHWND."); + break; + + // HWND used by DirectDraw CooperativeLevel has been subclassed, + // this prevents DirectDraw from restoring state. + case DDERR_HWNDSUBCLASSED: + hsAssert(false, "DDERR_HWNDSUBCLASSED."); + break; + + // The CooperativeLevel HWND has already been set. + // It can not be reset while the process has surfaces or palettes created. + case DDERR_HWNDALREADYSET: + hsAssert(false, "DDERR_HWNDALREADYSET."); + break; + + // No palette object attached to this surface. + case DDERR_NOPALETTEATTACHED: + hsAssert(false, "DDERR_NOPALETTEATTACHED."); + break; + + // No hardware support for 16 or 256 color palettes. + case DDERR_NOPALETTEHW: + hsAssert(false, "DDERR_NOPALETTEHW."); + break; + + // If a clipper object is attached to the source surface passed into a + // BltFast call. + case DDERR_BLTFASTCANTCLIP: + hsAssert(false, "DDERR_BLTFASTCANTCLIP."); + break; + + // No blter. + case DDERR_NOBLTHW: + hsAssert(false, "DDERR_NOBLTHW."); + break; + + // No DirectDraw ROP hardware. + case DDERR_NODDROPSHW: + hsAssert(false, "DDERR_NODDROPSHW."); + break; + + // returned when GetOverlayPosition is called on a hidden overlay + case DDERR_OVERLAYNOTVISIBLE: + hsAssert(false, "DDERR_OVERLAYNOTVISIBLE."); + break; + + // returned when GetOverlayPosition is called on a overlay that UpdateOverlay + // has never been called on to establish a destionation. + case DDERR_NOOVERLAYDEST: + hsAssert(false, "DDERR_NOOVERLAYDEST."); + break; + + // returned when the position of the overlay on the destionation is no longer + // legal for that destionation. + case DDERR_INVALIDPOSITION: + hsAssert(false, "DDERR_INVALIDPOSITION."); + break; + + // returned when an overlay member is called for a non-overlay surface + case DDERR_NOTAOVERLAYSURFACE: + hsAssert(false, "DDERR_NOTAOVERLAYSURFACE."); + break; + + // An attempt was made to set the cooperative level when it was already + // set to exclusive. + case DDERR_EXCLUSIVEMODEALREADYSET: + hsAssert(false, "DDERR_EXCLUSIVEMODEALREADYSET."); + break; + + // An attempt has been made to flip a surface that is not flippable. + case DDERR_NOTFLIPPABLE: + hsAssert(false, "DDERR_NOTFLIPPABLE."); + break; + + // Can't duplicate primary & 3D surfaces, or surfaces that are implicitly + // created. + case DDERR_CANTDUPLICATE: + hsAssert(false, "DDERR_CANTDUPLICATE."); + break; + + // Surface was not locked. An attempt to unlock a surface that was not + // locked at all, or by this process, has been attempted. + case DDERR_NOTLOCKED: + hsAssert(false, "DDERR_NOTLOCKED."); + break; + + // Windows can not create any more DCs, or a DC was requested for a paltte-indexed + // surface when the surface had no palette AND the display mode was not palette-indexed + // (in this case DirectDraw cannot select a proper palette into the DC) + case DDERR_CANTCREATEDC: + hsAssert(false, "DDERR_CANTCREATEDC."); + break; + + // No DC was ever created for this surface. + case DDERR_NODC: + hsAssert(false, "DDERR_NODC."); + break; + + // This surface can not be restored because it was created in a different + // mode. + case DDERR_WRONGMODE: + hsAssert(false, "DDERR_WRONGMODE."); + break; + + // This surface can not be restored because it is an implicitly created + // surface. + case DDERR_IMPLICITLYCREATED: + hsAssert(false, "DDERR_IMPLICITLYCREATED."); + break; + + // The surface being used is not a palette-based surface + case DDERR_NOTPALETTIZED: + hsAssert(false, "DDERR_NOTPALETTIZED."); + break; + + // The display is currently in an unsupported mode + case DDERR_UNSUPPORTEDMODE: + hsAssert(false, "DDERR_UNSUPPORTEDMODE."); + break; + + // Operation could not be carried out because there is no mip-map + // texture mapping hardware present or available. + case DDERR_NOMIPMAPHW: + hsAssert(false, "DDERR_NOMIPMAPHW."); + break; + + // The requested action could not be performed because the surface was of + // the wrong type. + case DDERR_INVALIDSURFACETYPE: + hsAssert(false, "DDERR_INVALIDSURFACETYPE."); + break; + + // Device does not support optimized surfaces, therefore no video memory optimized surfaces + case DDERR_NOOPTIMIZEHW: + hsAssert(false, "DDERR_NOOPTIMIZEHW."); + break; + + // Surface is an optimized surface, but has not yet been allocated any memory + case DDERR_NOTLOADED: + hsAssert(false, "DDERR_NOTLOADED."); + break; + + // Attempt was made to create or set a device window without first setting + // the focus window + case DDERR_NOFOCUSWINDOW: + hsAssert(false, "DDERR_NOFOCUSWINDOW."); + break; + + // A DC has already been returned for this surface. Only one DC can be + // retrieved per surface. + case DDERR_DCALREADYCREATED: + hsAssert(false, "DDERR_DCALREADYCREATED."); + break; + + // An attempt was made to allocate non-local video memory from a device + // that does not support non-local video memory. + case DDERR_NONONLOCALVIDMEM: + hsAssert(false, "DDERR_NONONLOCALVIDMEM."); + break; + + // The attempt to page lock a surface failed. + case DDERR_CANTPAGELOCK: + hsAssert(false, "DDERR_CANTPAGELOCK."); + break; + + // The attempt to page unlock a surface failed. + case DDERR_CANTPAGEUNLOCK: + hsAssert(false, "DDERR_CANTPAGEUNLOCK."); + break; + + // An attempt was made to page unlock a surface with no outstanding page locks. + case DDERR_NOTPAGELOCKED: + hsAssert(false, "DDERR_NOTPAGELOCKED."); + break; + + // There is more data available than the specified buffer size could hold + case DDERR_MOREDATA: + hsAssert(false, "DDERR_MOREDATA."); + break; + + // The data has expired and is therefore no longer valid. + case DDERR_EXPIRED: + hsAssert(false, "DDERR_EXPIRED."); + break; + + // The video port is not active + case DDERR_VIDEONOTACTIVE: + hsAssert(false, "DDERR_VIDEONOTACTIVE."); + break; + + // Surfaces created by one direct draw device cannot be used directly by + // another direct draw device. + case DDERR_DEVICEDOESNTOWNSURFACE: + hsAssert(false, "DDERR_DEVICEDOESNTOWNSURFACE."); + break; + + // An attempt was made to invoke an interface member of a DirectDraw object + // created by CoCreateInstance() before it was initialized. + case DDERR_NOTINITIALIZED: + hsAssert(false, "DDERR_NOTINITIALIZED."); + break; + + default: + hsAssert(false, "Unknown error."); + break; + } +} + +hsBool hsDXTDirectXCodec::ColorizeCompMipmap( plMipmap *bMap, const UInt8 *colorMask ) +{ + return false; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTDirectXCodec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTDirectXCodec.h new file mode 100644 index 00000000..34fac632 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTDirectXCodec.h @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// hsDXTDirectXCodec Class Functions // +// DirectX-based codec functions // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 6.8.2001 mcn - Got a much-needed Plasma 2.0/DX8 update. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef hsDXTDirectXCodec_inc +#define hsDXTDirectXCodec_inc + +#include "hsWindows.h" +#include "hsCodec.h" + +class plMipmap; +struct IDirect3DDevice8; +struct IDirectDrawSurface7; +struct IDirectDraw7; + +class hsDXTDirectXCodec : public hsCodec +{ +private: + hsDXTDirectXCodec(); +public: + ~hsDXTDirectXCodec(); + static hsDXTDirectXCodec& Instance(); + + static void Init() { fRegistered = Register(); } + + plMipmap *CreateCompressedMipmap(plMipmap *uncompressed); + plMipmap *CreateUncompressedMipmap(plMipmap *compressed, UInt8 bitDepth = 0); + + // Colorize a compressed mipmap + hsBool ColorizeCompMipmap( plMipmap *bMap, const UInt8 *colorMask ); + + void Initialize( IDirect3DDevice8 *directDraw ); + hsBool Initialized() { return (fFlags & kInitialized) != 0; } + +private: + UInt32 ICompressedFormat(const plMipmap *uncompressed); + IDirectDrawSurface7 *IMakeDirect3DSurface( UInt32 formatType, UInt32 mipMapLevels, UInt32 width, UInt32 height ); + void IFillSurface( hsRGBAColor32* src, UInt32 mmlvs, IDirectDrawSurface7 *pddsDest ); + void IFillFromSurface( hsRGBAColor32* dest, UInt32 mmlvs, IDirectDrawSurface7 *pddsSrc ); + void ICopySurface( IDirectDrawSurface7 *dest, IDirectDrawSurface7 *src, Int32 mipMapLevels ); + void CheckErrorCode(HRESULT res); + hsBool IInitialize(); + + IDirectDraw7 *fDirectDraw; + HINSTANCE fDDLibraryInstance; + UInt32 fFlags; + + enum + { + kInitialized = 0x1, + kExternalInit = 0x2 + }; + + static hsBool Register(); + static hsBool fRegistered; +}; + +#endif // hsDXTDirectXCodec_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTSoftwareCodec.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTSoftwareCodec.cpp new file mode 100644 index 00000000..f8dc6a90 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTSoftwareCodec.cpp @@ -0,0 +1,2483 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "hsTypes.h" +#include "hsDXTSoftwareCodec.h" +#include "plMipmap.h" +#include "hsCodecManager.h" + +#define SWAPVARS( x, y, t ) { t = x; x = y; y = t; } + +// This is the color depth that we decompress to by default if we're not told otherwise +#define kDefaultDepth 32 + + +hsBool hsDXTSoftwareCodec::fRegistered = false; + +hsDXTSoftwareCodec& hsDXTSoftwareCodec::Instance() +{ + static hsDXTSoftwareCodec the_instance; + + return the_instance; +} + +hsDXTSoftwareCodec::hsDXTSoftwareCodec() +{ +} + +hsDXTSoftwareCodec::~hsDXTSoftwareCodec() +{ +} + +//// CreateCompressedMipmap /////////////////////////////////////////////////// +// Updated 8.15.2000 mcn to generate uncompressed mipmaps down to 1x1 (the +// decompressor better know how to deal with this!) Also cleaned up so I can +// read it :) + +plMipmap *hsDXTSoftwareCodec::CreateCompressedMipmap( plMipmap *uncompressed ) +{ + UInt8 format; + plMipmap *compressed = nil; + UInt8 i; + + + /// Sanity checks + hsAssert( fRegistered, "Calling member of unregistered codec." ); + hsAssert( !uncompressed->IsCompressed(), "Trying to compress already compressed bitmap."); + + /// Check width and height + if( ( uncompressed->GetWidth() | uncompressed->GetHeight() ) & 0x03 ) + return nil; /// Width and height must be multiple of 4 + + format = ICalcCompressedFormat( uncompressed ); + if( format == plMipmap::DirectXInfo::kError ) + return nil; + + + /// Set up structure + compressed = TRACKED_NEW plMipmap( uncompressed->GetWidth(), uncompressed->GetHeight(), plMipmap::kARGB32Config, + uncompressed->GetNumLevels(), plMipmap::kDirectXCompression, format ); + + { + /// Now loop through and compress each one (that is a valid size!) + for( i = 0; i < compressed->GetNumLevels(); i++ ) + { + uncompressed->SetCurrLevel( i ); + compressed->SetCurrLevel( i ); + if( ( compressed->GetCurrWidth() | compressed->GetCurrHeight() ) & 0x03 ) + break; + + CompressMipmapLevel( uncompressed, compressed ); + } + + /// Now copy the rest straight over + for( ; i < compressed->GetNumLevels(); i++ ) + memcpy( compressed->GetLevelPtr( i ), uncompressed->GetLevelPtr( i ), uncompressed->GetLevelSize( i ) ); + + /// Reset + uncompressed->SetCurrLevel( 0 ); + compressed->SetCurrLevel( 0 ); + } + + return compressed; +} + +//// CreateUncompressedBitmap ///////////////////////////////////////////////// +// +// Takes a compressed bitmap and uncompresses it. Duh! +// +// 8.14.2000 mcn - Updated to decompress to 16-bit modes and set flags +// accordingly. +// 8.15.2000 mcn - Updated to handle compressed mipmaps with invalid levels +// (i.e. they're not compressed). See CreateCompressedMipmap(). +// 8.18.2000 mcn - Updated to handle new flags instead of just a bit depth + +plMipmap *hsDXTSoftwareCodec::CreateUncompressedMipmap( plMipmap *compressed, UInt8 flags ) +{ + plMipmap *uncompressed = nil; + Int32 totalSize; + Int32 width, height; + UInt8 i; + + + /// Sanity checks + hsAssert( fRegistered, "Calling member of unregistered codec." ); + hsAssert( compressed->fCompressionType == plMipmap::kDirectXCompression, "Uncompressing wrong format." ); + hsAssert( ( compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1 ) || + ( compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5 ), + "Unsupported directX compression format." ); + + + /// Set up the info + uncompressed = ICreateUncompressedMipmap( compressed, flags ); + + /// Handle mipmaps? + { + totalSize = 0; + width = compressed->GetWidth(); + height = compressed->GetHeight(); + + for( i = 0; i < compressed->GetNumLevels(); i++ ) + { + /// Note: THIS is valid no matter whether the compressed level is + /// really compressed or not + totalSize += width * height * uncompressed->GetPixelSize() >> 3; + width >>= 1; + height >>= 1; + } + + /// Loop through and decompress! + width = compressed->GetWidth(); + height = compressed->GetHeight(); + for( i = 0; i < uncompressed->GetNumLevels(); i++ ) + { + uncompressed->SetCurrLevel( i ); + compressed->SetCurrLevel( i ); + + if( ( width | height ) & 0x03 ) + break; + + UncompressMipmap( uncompressed, compressed, flags ); + + if( width > 1 ) + width >>= 1; + if( height > 1 ) + height >>= 1; + } + + /// Now loop through the *rest* and just copy (they won't be compressed) + for( ; i < uncompressed->GetNumLevels(); i++ ) + { + uncompressed->SetCurrLevel( i ); + compressed->SetCurrLevel( i ); + + IXlateColorBlock( (plMipmap *)uncompressed, (UInt32 *)compressed->GetLevelPtr( i ), flags ); + } + + /// Reset + uncompressed->SetCurrLevel( 0 ); + compressed->SetCurrLevel( 0 ); + } + + return uncompressed; +} + +//// IXlateColorBlock ///////////////////////////////////////////////////////// +// Converts a block from ARGB 8888 to the given format of the bitmap. + +void hsDXTSoftwareCodec::IXlateColorBlock( plMipmap *destBMap, UInt32 *srcBlock, UInt8 flags ) +{ + UInt8 *bytePtr; + UInt16 *wordPtr, tempVal; + UInt32 i; + + + if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB8888 ) + memcpy( destBMap->GetCurrLevelPtr(), srcBlock, destBMap->GetCurrLevelSize() ); + else + { + if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kAInten88 ) + { + /// Upper 8 bits from alpha, lower 8 bits from blue + wordPtr = (UInt16 *)destBMap->GetCurrLevelPtr(); + for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ ) + { + wordPtr[ i ] = (UInt16)( ( ( srcBlock[ i ] >> 16 ) & 0xff00 ) | ( srcBlock[ i ] & 0x00ff ) ); + } + } + else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kInten8 ) + { + /// 8 bits from blue + bytePtr = (UInt8 *)destBMap->GetCurrLevelPtr(); + for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ ) + { + bytePtr[ i ] = (UInt8)( srcBlock[ i ] & 0x00ff ); + } + } + else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB1555 ) + { + wordPtr = (UInt16 *)destBMap->GetCurrLevelPtr(); + + if( ( flags & hsCodecManager::kCompOrderMask ) == hsCodecManager::kWeirdCompOrder ) + { + /// Really do 5551 + for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ ) + { + tempVal = (UInt16)( ( ( srcBlock[ i ] & 0x000000f8 ) >> 2 ) | + ( ( ( srcBlock[ i ] & 0x0000f800 ) >> 5 ) ) | + ( ( ( srcBlock[ i ] & 0x00f80000 ) >> 8 ) ) | + ( ( srcBlock[ i ] & 0x80000000 ) >> 31 ) ); + + wordPtr[ i ] = tempVal; + } + } + else + { + /// Normal 1555 + for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ ) + { + tempVal = (UInt16)( ( ( srcBlock[ i ] & 0x000000f8 ) >> 3 ) | + ( ( ( srcBlock[ i ] & 0x0000f800 ) >> 6 ) ) | + ( ( ( srcBlock[ i ] & 0x00f80000 ) >> 9 ) ) | + ( ( srcBlock[ i ] & 0x80000000 ) >> 16 ) ); + + wordPtr[ i ] = tempVal; + } + } + } + else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB4444 ) + { + wordPtr = (UInt16 *)destBMap->GetCurrLevelPtr(); + + if( ( flags & hsCodecManager::kCompOrderMask ) == hsCodecManager::kWeirdCompOrder ) + { + /// Really do 4444 reversed (switch red and blue) + for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ ) + { + tempVal = (UInt16)( ( ( srcBlock[ i ] & 0x000000f0 ) << 4 ) | + ( ( ( srcBlock[ i ] & 0x0000f000 ) >> 8 ) ) | + ( ( ( srcBlock[ i ] & 0x00f00000 ) >> 20 ) ) | + ( ( ( srcBlock[ i ] & 0xf0000000 ) >> 16 ) ) ); + + wordPtr[ i ] = tempVal; + } + } + else + { + /// Normal 4444 + for( i = 0; i < destBMap->GetCurrWidth() * destBMap->GetCurrHeight(); i++ ) + { + tempVal = (UInt16)( ( ( srcBlock[ i ] & 0x000000f0 ) >> 4 ) | + ( ( ( srcBlock[ i ] & 0x0000f000 ) >> 8 ) ) | + ( ( ( srcBlock[ i ] & 0x00f00000 ) >> 12 ) ) | + ( ( ( srcBlock[ i ] & 0xf0000000 ) >> 16 ) ) ); + + wordPtr[ i ] = tempVal; + } + } + } + else + hsAssert( false, "Unsupported pixel format during color block translation" ); + } +} + +//// ICreateUncompressedMipmap //////////////////////////////////////////////// +// Sets the fields of an uncompressed bitmap according to the source +// (compressed) bitmap and the given bit depth. + +plMipmap *hsDXTSoftwareCodec::ICreateUncompressedMipmap( plMipmap *compressed, UInt8 flags ) +{ + UInt8 bitDepth, type; + plMipmap *newMap; + + + if( compressed->GetFlags() & plMipmap::kIntensityMap ) + { + /// Intensity map--alpha too? + if( compressed->GetFlags() & plMipmap::kAlphaChannelFlag ) + { + type = plMipmap::UncompressedInfo::kAInten88; + bitDepth = 16; + } + else + { + type = plMipmap::UncompressedInfo::kInten8; + bitDepth = 8; + } + } + else + { + /// Default to 32 bit depth + if( ( flags & hsCodecManager::kBitDepthMask ) == hsCodecManager::kDontCareDepth ) + bitDepth = kDefaultDepth; + else if( ( flags & hsCodecManager::kBitDepthMask ) == hsCodecManager::k32BitDepth ) + bitDepth = 32; + else + bitDepth = 16; + + if( bitDepth == 32 ) + type = plMipmap::UncompressedInfo::kRGB8888; + else if( compressed->GetFlags() & plMipmap::kAlphaChannelFlag ) + type = plMipmap::UncompressedInfo::kRGB4444; + else + type = plMipmap::UncompressedInfo::kRGB1555; + } + + newMap = TRACKED_NEW plMipmap( compressed->GetWidth(), compressed->GetHeight(), bitDepth, + compressed->GetNumLevels(), plMipmap::kUncompressed, type ); + newMap->SetFlags( compressed->GetFlags() ); + + return newMap; +} + + + +/* +----------------------------------------------------------- + OLD DECOMPRESSION FUNCTION--KEEPING HERE FOR LEGACY +----------------------------------------------------------- + +void hsDXTSoftwareCodec::UncompressMipmap(plMipmap *uncompressed, plMipmap *compressed) +{ + UInt32 *compressedImage = (UInt32 *)compressed->GetCurrLevelPtr(); + UInt32 *uncompressedImage = (UInt32 *)uncompressed->GetCurrLevelPtr(); + UInt32 blockSize = compressed->fDirectXInfo.fBlockSize; + Int32 x, y; + Int32 xMax = compressed->GetCurrWidth() >> 2; + Int32 yMax = compressed->GetCurrHeight() >> 2; + for (x = 0; x < xMax; ++x) + { + for (y = 0; y < yMax; ++y) + { + UInt8 alpha[8]; + UInt16 color[4]; + UInt32 *block = &compressedImage[(x + xMax * y) * (blockSize >> 2)]; + UInt8 *charBlock = (UInt8 *)block; + UInt8 *alphaBlock = nil; + UInt8 *colorBlock = nil; + + if (compressed->fDirectXInfo.fCompressionType == hsGBitmap::DirectXInfo::kDXT5) + { + alphaBlock = charBlock; + colorBlock = (charBlock + 8); + + alpha[0] = charBlock[0]; + alpha[1] = charBlock[1]; + + // 8-alpha or 6-alpha block? + if (alpha[0] > alpha[1]) { + // 8-alpha block: derive the other 6 alphas. + // 000 = alpha[0], 001 = alpha[1], others are interpolated + alpha[2] = (6 * alpha[0] + alpha[1]) / 7; // bit code 010 + alpha[3] = (5 * alpha[0] + 2 * alpha[1]) / 7; // Bit code 011 + alpha[4] = (4 * alpha[0] + 3 * alpha[1]) / 7; // Bit code 100 + alpha[5] = (3 * alpha[0] + 4 * alpha[1]) / 7; // Bit code 101 + alpha[6] = (2 * alpha[0] + 5 * alpha[1]) / 7; // Bit code 110 + alpha[7] = (alpha[0] + 6 * alpha[1]) / 7; // Bit code 111 + } + else { // 6-alpha block: derive the other alphas. + // 000 = alpha[0], 001 = alpha[1], others are interpolated + alpha[2] = (4 * alpha[0] + alpha[1]) / 5; // Bit code 010 + alpha[3] = (3 * alpha[0] + 2 * alpha[1]) / 5; // Bit code 011 + alpha[4] = (2 * alpha[0] + 3 * alpha[1]) / 5; // Bit code 100 + alpha[5] = (alpha[0] + 4 * alpha[1]) / 5; // Bit code 101 + alpha[6] = 0; // Bit code 110 + alpha[7] = 255; // Bit code 111 + } + } + else if (compressed->fDirectXInfo.fCompressionType == hsGBitmap::DirectXInfo::kDXT1) + { + alphaBlock = nil; + colorBlock = charBlock; + } + else + { + hsAssert(false, "Unrecognized compression scheme."); + } + + UInt32 encoding; + color[0] = (colorBlock[1] << 8) | colorBlock[0]; + color[1] = (colorBlock[3] << 8) | colorBlock[2]; + + if (color[0] > color[1]) + { + // Four-color block: derive the other two colors. + // 00 = color[0], 01 = color[1], 10 = color[2, 11 = color[3 + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + color[2] = BlendColors16(2, color[0], 1, color[1]); + color[3] = BlendColors16(1, color[0], 2, color[1]); + + encoding = kFourColorEncoding; + } + else + { + // Three-color block: derive the other color. + // 00 = color[0], 01 = color[1], 10 = color[2, + // 11 = transparent. + // These two bit codes correspond to the 2-bit fields + // stored in the 64-bit block. + color[2] = BlendColors16(1, color[0], 1, color[1]); + color[3] = 0; + + encoding = kThreeColorEncoding; + } + + UInt8 r, g, b, a; + Int32 xx, yy; + for (xx = 0; xx < 4; ++xx) + { + for (yy = 0; yy < 4; ++yy) + { + if (alphaBlock) + { + UInt32 alphaMask = 0x7; + UInt32 firstThreeBytes = (alphaBlock[4] << 16) + (alphaBlock[3] << 8) + alphaBlock[2]; + UInt32 secondThreeBytes = (alphaBlock[7] << 16) + (alphaBlock[6] << 8) + alphaBlock[5]; + UInt32 alphaIndex; + UInt32 alphaShift; + if (yy < 2) + { + alphaShift = 3 * (4 * yy + xx); + alphaIndex = (firstThreeBytes >> alphaShift) & alphaMask; + } + else + { + alphaShift = 3 * (4 * (yy - 2) + xx); + alphaIndex = (secondThreeBytes >> alphaShift) & alphaMask; + } + + a = alpha[alphaIndex]; + } + else + { + a = 255; + } + + UInt32 colorMask = 0x3; + UInt32 colorDWord = (colorBlock[7] << 24) | (colorBlock[6] << 16) | + (colorBlock[5] << 8) | colorBlock[4]; + UInt32 colorShift = 2 * (4 * yy + xx); + UInt32 colorIndex = (colorDWord >> colorShift) & colorMask; + + if ((encoding == kThreeColorEncoding) && (colorIndex == 3)) + { + r = g = b = a = 0; + } + else + { + r = (UInt8)((color[colorIndex] >> 8) & 0xf8); + g = (UInt8)((color[colorIndex] >> 3) & 0xfc); + b = (UInt8)((color[colorIndex] << 3) & 0xf8); + } + + hsRGBAColor32* pixel = (hsRGBAColor32*)uncompressed->GetAddr32(4 * x + xx, 4 * y + yy); + pixel->a = a; + pixel->r = r; + pixel->g = g; + pixel->b = b; + } + } + } + } +} +*/ + +//// UncompressBitmap ///////////////////////////////////////////////////////// +// +// Workhorse function distribution. Takes a compressed GBitmapClass as input +// and writes the uncompressed version to the destination bitmap class. +// (Doesn't actually do anything but call the appropriate function per format. +// Change this function to add support for more compression formats.) +// +// Note: Supports DXT1 and DXT5 compression formats. +// +// 7.31.2000 mcn - Created, based on old code (uncredited) +// 8.18.2000 mcn - Updated to handle 'weird' formats and other flags. + +void hsDXTSoftwareCodec::UncompressMipmap( plMipmap *destBMap, plMipmap *srcBMap, UInt8 flags ) +{ + if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB8888 ) + { + /// 32-bit ARGB - Can be either DXT5 or DXT1 + if( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5 ) + IUncompressMipmapDXT5To32( destBMap, srcBMap ); + else if( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1 ) + IUncompressMipmapDXT1To32( destBMap, srcBMap ); + } + else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB1555 ) + { + /// 16-bit ARGB 1555--can ONLY be DXT1 + hsAssert( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1, + "Only DXT1 bitmaps can decompress to ARGB1555 format!" ); + + if( ( flags & hsCodecManager::kCompOrderMask ) == hsCodecManager::kWeirdCompOrder ) + IUncompressMipmapDXT1To16Weird( destBMap, srcBMap ); + else + IUncompressMipmapDXT1To16( destBMap, srcBMap ); + } + else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kRGB4444 ) + { + /// 16-bit ARGB 4444--can ONLY be DXT5 + hsAssert( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5, + "Only DXT5 bitmaps can decompress to ARGB4444 format!" ); + + if( ( flags & hsCodecManager::kCompOrderMask ) == hsCodecManager::kWeirdCompOrder ) + IUncompressMipmapDXT5To16Weird( destBMap, srcBMap ); + else + IUncompressMipmapDXT5To16( destBMap, srcBMap ); + } + else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kInten8 ) + { + /// 8-bit intensity--can ONLY be DXT1 + hsAssert( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1, + "Only DXT1 bitmaps can decompress to 8-bit Intensity format!" ); + IUncompressMipmapDXT1ToInten( destBMap, srcBMap ); + } + else if( destBMap->fUncompressedInfo.fType == plMipmap::UncompressedInfo::kAInten88 ) + { + /// 16-bit alpha-intensity--can ONLY be DXT5 + hsAssert( srcBMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5, + "Only DXT5 bitmaps can decompress to 8-8 Alpha-Intensity format!" ); + IUncompressMipmapDXT5ToAInten( destBMap, srcBMap ); + } + else + hsAssert( false, "Unsupported target decompression format" ); +} + + +//// IUncompressMipmapDXT5To16 //////////////////////////////////////////////// +// +// UncompressBitmap internal call for DXT5 compression. DXT5 is 3-bit linear +// interpolated alpha channel compression. Output is a 16-bit RGB 4444 bitmap. + +void hsDXTSoftwareCodec::IUncompressMipmapDXT5To16( plMipmap *destBMap, plMipmap *srcBMap ) +{ + UInt16 *srcData; + UInt16 *destData, destBlock[ 16 ]; + UInt32 blockSize; + UInt32 x, y, bMapStride; + UInt16 colors[ 4 ]; + Int32 numBlocks, i, j; + UInt8 *bytePtr; + UInt16 alphas[ 8 ], aTemp, a0, a1; + + UInt32 aBitSrc1, aBitSrc2; + UInt16 cBitSrc1, cBitSrc2; + + + /// Setup some nifty stuff + hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" ); + hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" ); + numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4; + + blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words + srcData = (UInt16 *)srcBMap->GetCurrLevelPtr(); + // Note our trick here to make sure nothing breaks if GetAddr16's + // formula changes + bMapStride = (UInt32)( destBMap->GetAddr16( 0, 1 ) - destBMap->GetAddr16( 0, 0 ) ); + x = y = 0; + + + /// Loop through the # of blocks (width*height / 16-pixel-blocks) + for( i = 0; i < numBlocks; i++ ) + { + /// Per block--determine alpha compression type first + bytePtr = (UInt8 *)srcData; + alphas[ 0 ] = bytePtr[ 0 ]; + alphas[ 1 ] = bytePtr[ 1 ]; + + /// Note that we use the preshifted alphas really as fixed point. + /// The result: more accuracy, and no need to shift the alphas afterwards + if( alphas[ 0 ] > alphas[ 1 ] ) + { + /// 8-alpha block: interpolate 6 others + alphas[ 0 ] <<= 8; + alphas[ 1 ] <<= 8; + + /// Note that, unlike below, we can't combine a0 and a1 into + /// one value, because that would give us a negative value, + /// and we're using unsigned values here. (i.e. we need all the bits) + aTemp = alphas[ 0 ]; + a0 = ( aTemp / 7 ); + a1 = ( alphas[ 1 ] / 7 ); + for( j = 2; j < 8; j++ ) + { + aTemp += a1 - a0; + alphas[ j ] = aTemp & 0xf000; /// Mask done here to retain as + /// much accuracy as possible + } + } + else + { + /// 6-alpha block: interpolate 4 others, then assume last 2 are 0 and 255 + alphas[ 0 ] <<= 8; + alphas[ 1 ] <<= 8; + + aTemp = alphas[ 0 ]; + a0 = ( alphas[ 1 ] - aTemp ) / 5; + for( j = 2; j < 6; j++ ) + { + aTemp += a0; + alphas[ j ] = aTemp & 0xf000; /// Mask done here to retain as + /// much accuracy as possible + } + + alphas[ 6 ] = 0; + alphas[ 7 ] = 0xf000; + } + + /// Mask off the original two now + alphas[ 0 ] &= 0xf000; + alphas[ 1 ] &= 0xf000; + + /// Now do the 16 pixels in 2 blocks, decompressing 3-bit lookups + aBitSrc1 = ( (UInt32)bytePtr[ 4 ] << 16 ) + + ( (UInt32)bytePtr[ 3 ] << 8 ) + + ( (UInt32)bytePtr[ 2 ] ); + aBitSrc2 = ( (UInt32)bytePtr[ 7 ] << 16 ) + + ( (UInt32)bytePtr[ 6 ] << 8 ) + + ( (UInt32)bytePtr[ 5 ] ); + + /// Now decompress color data + srcData += 4; // Alpha was 4 16-bit words worth + //hsAssert( srcData[ 0 ] > srcData[ 1 ], "Invalid block compression for DX5 method" ); /// If not, then it's one-bit + /// alpha, but this is DX5! + colors[ 0 ] = IRGB565To4444( srcData[ 0 ] ); + colors[ 1 ] = IRGB565To4444( srcData[ 1 ] ); + colors[ 2 ] = IMixTwoThirdsRGB4444( colors[ 0 ], colors[ 1 ] ); + colors[ 3 ] = IMixTwoThirdsRGB4444( colors[ 1 ], colors[ 0 ] ); + + cBitSrc1 = srcData[ 2 ]; + cBitSrc2 = srcData[ 3 ]; + +#ifdef HS_BUILD_FOR_MAC + cBitSrc1 = ISwapWordOrder( cBitSrc1 ); + cBitSrc2 = ISwapWordOrder( cBitSrc2 ); +#endif + + for( j = 0; j < 8; j++ ) + { + destBlock[ j ] = alphas[ aBitSrc1 & 0x07 ] | colors[ cBitSrc1 & 0x03 ]; + aBitSrc1 >>= 3; + cBitSrc1 >>= 2; + destBlock[ j + 8 ] = alphas[ aBitSrc2 & 0x07 ] | colors[ cBitSrc2 & 0x03 ]; + aBitSrc2 >>= 3; + cBitSrc2 >>= 2; +#ifdef HS_BUILD_FOR_MAC + destBlock[ j ] = ISwapWordOrder( destBlock[ j ] ); + destBlock[ j + 8 ] = ISwapWordOrder( destBlock[ j + 8 ] ); +#endif + } + + /// Now copy the block to the destination bitmap + /// (Trust me, this is actually *faster* than memcpy for some reason + destData = destBMap->GetAddr16( x, y ); + destData[ 0 ] = destBlock[ 0 ]; + destData[ 1 ] = destBlock[ 1 ]; + destData[ 2 ] = destBlock[ 2 ]; + destData[ 3 ] = destBlock[ 3 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 4 ]; + destData[ 1 ] = destBlock[ 5 ]; + destData[ 2 ] = destBlock[ 6 ]; + destData[ 3 ] = destBlock[ 7 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 8 ]; + destData[ 1 ] = destBlock[ 9 ]; + destData[ 2 ] = destBlock[ 10 ]; + destData[ 3 ] = destBlock[ 11 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 12 ]; + destData[ 1 ] = destBlock[ 13 ]; + destData[ 2 ] = destBlock[ 14 ]; + destData[ 3 ] = destBlock[ 15 ]; + + /// Increment and loop! + srcData += blockSize - 4; /// JUUUST in case our block size is diff + x += 4; + if( x == srcBMap->GetCurrWidth() ) + { + x = 0; + y += 4; + } + } +} + +//// IUncompressMipmapDXT5To16Weird /////////////////////////////////////////// +// +// UncompressBitmap internal call for DXT5 compression. DXT5 is 3-bit linear +// interpolated alpha channel compression. Output is a 16-bit RGB 4444 +// reversed bitmap (Red and blue are swapped ala OpenGL). +// (Note: Annoyingly enough, this is exactly the same as the above function +// EXCEPT for two stupid lines. We can't use function pointers as the two +// function calls are inline. Wouldn't it be nice if we could somehow write +// the inline opcodes beforehand?) + +void hsDXTSoftwareCodec::IUncompressMipmapDXT5To16Weird( plMipmap *destBMap, plMipmap *srcBMap ) +{ + UInt16 *srcData; + UInt16 *destData, destBlock[ 16 ]; + UInt32 blockSize; + UInt32 x, y, bMapStride; + UInt16 colors[ 4 ]; + Int32 numBlocks, i, j; + UInt8 *bytePtr; + UInt16 alphas[ 8 ], aTemp, a0, a1; + + UInt32 aBitSrc1, aBitSrc2; + UInt16 cBitSrc1, cBitSrc2; + + + /// Setup some nifty stuff + hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" ); + hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" ); + numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4; + + blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words + srcData = (UInt16 *)srcBMap->GetCurrLevelPtr(); + // Note our trick here to make sure nothing breaks if GetAddr16's + // formula changes + bMapStride = (UInt32)( destBMap->GetAddr16( 0, 1 ) - destBMap->GetAddr16( 0, 0 ) ); + x = y = 0; + + + /// Loop through the # of blocks (width*height / 16-pixel-blocks) + for( i = 0; i < numBlocks; i++ ) + { + /// Per block--determine alpha compression type first + bytePtr = (UInt8 *)srcData; + alphas[ 0 ] = bytePtr[ 0 ]; + alphas[ 1 ] = bytePtr[ 1 ]; + + /// Note that we use the preshifted alphas really as fixed point. + /// The result: more accuracy, and no need to shift the alphas afterwards + if( alphas[ 0 ] > alphas[ 1 ] ) + { + /// 8-alpha block: interpolate 6 others + alphas[ 0 ] <<= 8; + alphas[ 1 ] <<= 8; + + /// Note that, unlike below, we can't combine a0 and a1 into + /// one value, because that would give us a negative value, + /// and we're using unsigned values here. (i.e. we need all the bits) + aTemp = alphas[ 0 ]; + a0 = ( aTemp / 7 ); + a1 = ( alphas[ 1 ] / 7 ); + for( j = 2; j < 8; j++ ) + { + aTemp += a1 - a0; + alphas[ j ] = aTemp & 0xf000; /// Mask done here to retain as + /// much accuracy as possible + } + } + else + { + /// 6-alpha block: interpolate 4 others, then assume last 2 are 0 and 255 + alphas[ 0 ] <<= 8; + alphas[ 1 ] <<= 8; + + aTemp = alphas[ 0 ]; + a0 = ( alphas[ 1 ] - aTemp ) / 5; + for( j = 2; j < 6; j++ ) + { + aTemp += a0; + alphas[ j ] = aTemp & 0xf000; /// Mask done here to retain as + /// much accuracy as possible + } + + alphas[ 6 ] = 0; + alphas[ 7 ] = 0xf000; + } + + /// Mask off the original two now + alphas[ 0 ] &= 0xf000; + alphas[ 1 ] &= 0xf000; + + /// Now do the 16 pixels in 2 blocks, decompressing 3-bit lookups + aBitSrc1 = ( (UInt32)bytePtr[ 4 ] << 16 ) + + ( (UInt32)bytePtr[ 3 ] << 8 ) + + ( (UInt32)bytePtr[ 2 ] ); + aBitSrc2 = ( (UInt32)bytePtr[ 7 ] << 16 ) + + ( (UInt32)bytePtr[ 6 ] << 8 ) + + ( (UInt32)bytePtr[ 5 ] ); + + /// Now decompress color data + srcData += 4; // Alpha was 4 16-bit words worth + colors[ 0 ] = IRGB565To4444Rev( srcData[ 0 ] ); + colors[ 1 ] = IRGB565To4444Rev( srcData[ 1 ] ); + colors[ 2 ] = IMixTwoThirdsRGB4444( colors[ 0 ], colors[ 1 ] ); + colors[ 3 ] = IMixTwoThirdsRGB4444( colors[ 1 ], colors[ 0 ] ); + + cBitSrc1 = srcData[ 2 ]; + cBitSrc2 = srcData[ 3 ]; + +#ifdef HS_BUILD_FOR_MAC + cBitSrc1 = ISwapWordOrder( cBitSrc1 ); + cBitSrc2 = ISwapWordOrder( cBitSrc2 ); +#endif + + for( j = 0; j < 8; j++ ) + { + destBlock[ j ] = alphas[ aBitSrc1 & 0x07 ] | colors[ cBitSrc1 & 0x03 ]; + aBitSrc1 >>= 3; + cBitSrc1 >>= 2; + destBlock[ j + 8 ] = alphas[ aBitSrc2 & 0x07 ] | colors[ cBitSrc2 & 0x03 ]; + aBitSrc2 >>= 3; + cBitSrc2 >>= 2; +#ifdef HS_BUILD_FOR_MAC + destBlock[ j ] = ISwapWordOrder( destBlock[ j ] ); + destBlock[ j + 8 ] = ISwapWordOrder( destBlock[ j + 8 ] ); +#endif + } + + /// Now copy the block to the destination bitmap + /// (Trust me, this is actually *faster* than memcpy for some reason + destData = destBMap->GetAddr16( x, y ); + destData[ 0 ] = destBlock[ 0 ]; + destData[ 1 ] = destBlock[ 1 ]; + destData[ 2 ] = destBlock[ 2 ]; + destData[ 3 ] = destBlock[ 3 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 4 ]; + destData[ 1 ] = destBlock[ 5 ]; + destData[ 2 ] = destBlock[ 6 ]; + destData[ 3 ] = destBlock[ 7 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 8 ]; + destData[ 1 ] = destBlock[ 9 ]; + destData[ 2 ] = destBlock[ 10 ]; + destData[ 3 ] = destBlock[ 11 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 12 ]; + destData[ 1 ] = destBlock[ 13 ]; + destData[ 2 ] = destBlock[ 14 ]; + destData[ 3 ] = destBlock[ 15 ]; + + /// Increment and loop! + srcData += blockSize - 4; /// JUUUST in case our block size is diff + x += 4; + if( x == srcBMap->GetCurrWidth() ) + { + x = 0; + y += 4; + } + } +} + +//// IUncompressMipmapDXT5To32 //////////////////////////////////////////////// +// +// UncompressBitmap internal call for DXT5 compression. DXT5 is 3-bit linear +// interpolated alpha channel compression. Output is a 32-bit ARGB 8888 bitmap. +// +// 7.31.2000 - M.Burrack - Created, based on old code (uncredited) +// +// 8.14.2000 - M.Burrack - Optimized on the alpha blending. Now we precalc +// the divided values and run a for loop. This gets +// us only about 10% :( + +void hsDXTSoftwareCodec::IUncompressMipmapDXT5To32( plMipmap *destBMap, plMipmap *srcBMap ) +{ + UInt16 *srcData; + UInt32 *destData, destBlock[ 16 ]; + UInt32 blockSize; + UInt32 x, y, bMapStride; + UInt32 colors[ 4 ]; + Int32 numBlocks, i, j; + UInt8 *bytePtr; + UInt32 alphas[ 8 ], aTemp, a0, a1; + + UInt32 aBitSrc1, aBitSrc2; + UInt16 cBitSrc1, cBitSrc2; + + + /// Setup some nifty stuff + hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" ); + hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" ); + numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4; + + blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words + srcData = (UInt16 *)srcBMap->GetCurrLevelPtr(); + // Note our trick here to make sure nothing breaks if GetAddr32's + // formula changes + bMapStride = (UInt32)( destBMap->GetAddr32( 0, 1 ) - destBMap->GetAddr32( 0, 0 ) ); + x = y = 0; + + + /// Loop through the # of blocks (width*height / 16-pixel-blocks) + for( i = 0; i < numBlocks; i++ ) + { + /// Per block--determine alpha compression type first + bytePtr = (UInt8 *)srcData; + alphas[ 0 ] = bytePtr[ 0 ]; + alphas[ 1 ] = bytePtr[ 1 ]; + + /// Note that we use the preshifted alphas really as fixed point. + /// The result: more accuracy, and no need to shift the alphas afterwards + if( alphas[ 0 ] > alphas[ 1 ] ) + { + /// 8-alpha block: interpolate 6 others +/* //// Here's the old code, for reference //// + alphas[ 2 ] = ( 6 * alphas[ 0 ] + alphas[ 1 ] ) / 7; + alphas[ 3 ] = ( 5 * alphas[ 0 ] + 2 * alphas[ 1 ] ) / 7; + alphas[ 4 ] = ( 4 * alphas[ 0 ] + 3 * alphas[ 1 ] ) / 7; + alphas[ 5 ] = ( 3 * alphas[ 0 ] + 4 * alphas[ 1 ] ) / 7; + alphas[ 6 ] = ( 2 * alphas[ 0 ] + 5 * alphas[ 1 ] ) / 7; + alphas[ 7 ] = ( alphas[ 0 ] + 6 * alphas[ 1 ] ) / 7; +*/ + alphas[ 0 ] <<= 24; + alphas[ 1 ] <<= 24; + + /// Note that, unlike below, we can't combine a0 and a1 into + /// one value, because that would give us a negative value, + /// and we're using unsigned values here. (i.e. we need all the bits) + aTemp = alphas[ 0 ]; + a0 = ( aTemp / 7 ) & 0xff000000; + a1 = ( alphas[ 1 ] / 7 ) & 0xff000000; + for( j = 2; j < 8; j++ ) + { + aTemp += a1 - a0; + alphas[ j ] = aTemp; + } + } + else + { + /// 6-alpha block: interpolate 4 others, then assume last 2 are 0 and 255 +/* //// Here's the old code, for reference //// + alphas[ 2 ] = ( 4 * alphas[ 0 ] + alphas[ 1 ] ) / 5; + alphas[ 3 ] = ( 3 * alphas[ 0 ] + 2 * alphas[ 1 ] ) / 5; + alphas[ 4 ] = ( 2 * alphas[ 0 ] + 3 * alphas[ 1 ] ) / 5; + alphas[ 5 ] = ( alphas[ 0 ] + 4 * alphas[ 1 ] ) / 5; +*/ + alphas[ 0 ] <<= 24; + alphas[ 1 ] <<= 24; + + aTemp = alphas[ 0 ]; + a0 = ( alphas[ 1 ] - aTemp ) / 5; + for( j = 2; j < 6; j++ ) + { + aTemp += a0; + alphas[ j ] = aTemp & 0xff000000; + } + + alphas[ 6 ] = 0; + alphas[ 7 ] = 255 << 24; + } + + /// Now do the 16 pixels in 2 blocks, decompressing 3-bit lookups + aBitSrc1 = ( (UInt32)bytePtr[ 4 ] << 16 ) + + ( (UInt32)bytePtr[ 3 ] << 8 ) + + ( (UInt32)bytePtr[ 2 ] ); + aBitSrc2 = ( (UInt32)bytePtr[ 7 ] << 16 ) + + ( (UInt32)bytePtr[ 6 ] << 8 ) + + ( (UInt32)bytePtr[ 5 ] ); + + /// Now decompress color data + srcData += 4; // Alpha was 4 16-bit words worth + //hsAssert( srcData[ 0 ] > srcData[ 1 ], "Invalid block compression for DX5 method" ); /// If not, then it's one-bit + /// alpha, but this is DX5! + colors[ 0 ] = IRGB16To32Bit( srcData[ 0 ] ); + colors[ 1 ] = IRGB16To32Bit( srcData[ 1 ] ); + colors[ 2 ] = IMixTwoThirdsRGB32( colors[ 0 ], colors[ 1 ] ); + colors[ 3 ] = IMixTwoThirdsRGB32( colors[ 1 ], colors[ 0 ] ); + + cBitSrc1 = srcData[ 2 ]; + cBitSrc2 = srcData[ 3 ]; + +#ifdef HS_BUILD_FOR_MAC + cBitSrc1 = ( cBitSrc1 >> 8 ) | ( ( cBitSrc1 & 0xff ) << 8 ); + cBitSrc2 = ( cBitSrc2 >> 8 ) | ( ( cBitSrc2 & 0xff ) << 8 ); +#endif + + for( j = 0; j < 8; j++ ) + { + destBlock[ j ] = alphas[ aBitSrc1 & 0x07 ] | colors[ cBitSrc1 & 0x03 ]; + aBitSrc1 >>= 3; + cBitSrc1 >>= 2; + destBlock[ j + 8 ] = alphas[ aBitSrc2 & 0x07 ] | colors[ cBitSrc2 & 0x03 ]; + aBitSrc2 >>= 3; + cBitSrc2 >>= 2; +#ifdef HS_BUILD_FOR_MAC + destBlock[ j ] = ISwapDwordOrder( destBlock[ j ] ); + destBlock[ j + 8 ] = ISwapDwordOrder( destBlock[ j + 8 ] ); +#endif + } + + /// Now copy the block to the destination bitmap + /// (Trust me, this is actually *faster* than memcpy for some reason + destData = destBMap->GetAddr32( x, y ); + destData[ 0 ] = destBlock[ 0 ]; + destData[ 1 ] = destBlock[ 1 ]; + destData[ 2 ] = destBlock[ 2 ]; + destData[ 3 ] = destBlock[ 3 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 4 ]; + destData[ 1 ] = destBlock[ 5 ]; + destData[ 2 ] = destBlock[ 6 ]; + destData[ 3 ] = destBlock[ 7 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 8 ]; + destData[ 1 ] = destBlock[ 9 ]; + destData[ 2 ] = destBlock[ 10 ]; + destData[ 3 ] = destBlock[ 11 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 12 ]; + destData[ 1 ] = destBlock[ 13 ]; + destData[ 2 ] = destBlock[ 14 ]; + destData[ 3 ] = destBlock[ 15 ]; + + /// Increment and loop! + srcData += blockSize - 4; /// JUUUST in case our block size is diff + x += 4; + if( x == srcBMap->GetCurrWidth() ) + { + x = 0; + y += 4; + } + } +} + +//// IUncompressMipmapDXT5ToAInten //////////////////////////////////////////// +// +// UncompressBitmap internal call for DXT5 compression. DXT5 is 3-bit linear +// interpolated alpha channel compression. Output is a 16-bit Alpha-intensity +// map. + +void hsDXTSoftwareCodec::IUncompressMipmapDXT5ToAInten( plMipmap *destBMap, plMipmap *srcBMap ) +{ + UInt16 *srcData; + UInt16 *destData, destBlock[ 16 ]; + UInt32 blockSize; + UInt32 x, y, bMapStride; + UInt8 colors[ 4 ]; + Int32 numBlocks, i, j; + UInt8 *bytePtr; + UInt16 alphas[ 8 ], aTemp, a0, a1; + + UInt32 aBitSrc1, aBitSrc2; + UInt16 cBitSrc1, cBitSrc2; + + + /// Setup some nifty stuff + hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" ); + hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" ); + numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4; + + blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words + srcData = (UInt16 *)srcBMap->GetCurrLevelPtr(); + // Note our trick here to make sure nothing breaks if GetAddr32's + // formula changes + bMapStride = (UInt32)( destBMap->GetAddr16( 0, 1 ) - destBMap->GetAddr16( 0, 0 ) ); + x = y = 0; + + + /// Loop through the # of blocks (width*height / 16-pixel-blocks) + for( i = 0; i < numBlocks; i++ ) + { + /// Per block--determine alpha compression type first + bytePtr = (UInt8 *)srcData; + alphas[ 0 ] = bytePtr[ 0 ]; + alphas[ 1 ] = bytePtr[ 1 ]; + + /// Note that we use the preshifted alphas really as fixed point. + /// The result: more accuracy, and no need to shift the alphas afterwards + if( alphas[ 0 ] > alphas[ 1 ] ) + { + /// 8-alpha block: interpolate 6 others + alphas[ 0 ] <<= 8; + alphas[ 1 ] <<= 8; + + /// Note that, unlike below, we can't combine a0 and a1 into + /// one value, because that would give us a negative value, + /// and we're using unsigned values here. (i.e. we need all the bits) + aTemp = alphas[ 0 ]; + a0 = ( aTemp / 7 ); + a1 = ( alphas[ 1 ] / 7 ); + for( j = 2; j < 8; j++ ) + { + aTemp += a1 - a0; + alphas[ j ] = aTemp & 0xff00; /// Mask done here to retain as + /// much accuracy as possible + } + } + else + { + /// 6-alpha block: interpolate 4 others, then assume last 2 are 0 and 255 + alphas[ 0 ] <<= 8; + alphas[ 1 ] <<= 8; + + aTemp = alphas[ 0 ]; + a0 = ( alphas[ 1 ] - aTemp ) / 5; + for( j = 2; j < 6; j++ ) + { + aTemp += a0; + alphas[ j ] = aTemp & 0xff00; /// Mask done here to retain as + /// much accuracy as possible + } + + alphas[ 6 ] = 0; + alphas[ 7 ] = 0xff00; + } + + /// Now do the 16 pixels in 2 blocks, decompressing 3-bit lookups + aBitSrc1 = ( (UInt32)bytePtr[ 4 ] << 16 ) + + ( (UInt32)bytePtr[ 3 ] << 8 ) + + ( (UInt32)bytePtr[ 2 ] ); + aBitSrc2 = ( (UInt32)bytePtr[ 7 ] << 16 ) + + ( (UInt32)bytePtr[ 6 ] << 8 ) + + ( (UInt32)bytePtr[ 5 ] ); + + /// Now decompress color data + srcData += 4; // Alpha was 4 16-bit words worth + colors[ 0 ] = (UInt8)IRGB16To32Bit( srcData[ 0 ] ); + colors[ 1 ] = (UInt8)IRGB16To32Bit( srcData[ 1 ] ); + colors[ 2 ] = IMixTwoThirdsInten( colors[ 0 ], colors[ 1 ] ); + colors[ 3 ] = IMixTwoThirdsInten( colors[ 1 ], colors[ 0 ] ); + + cBitSrc1 = srcData[ 2 ]; + cBitSrc2 = srcData[ 3 ]; + +#ifdef HS_BUILD_FOR_MAC + cBitSrc1 = ( cBitSrc1 >> 8 ) | ( ( cBitSrc1 & 0xff ) << 8 ); + cBitSrc2 = ( cBitSrc2 >> 8 ) | ( ( cBitSrc2 & 0xff ) << 8 ); +#endif + + for( j = 0; j < 8; j++ ) + { + destBlock[ j ] = alphas[ aBitSrc1 & 0x07 ] | (UInt16)colors[ cBitSrc1 & 0x03 ]; + aBitSrc1 >>= 3; + cBitSrc1 >>= 2; + destBlock[ j + 8 ] = alphas[ aBitSrc2 & 0x07 ] | (UInt16)colors[ cBitSrc2 & 0x03 ]; + aBitSrc2 >>= 3; + cBitSrc2 >>= 2; +#ifdef HS_BUILD_FOR_MAC + destBlock[ j ] = ISwapWordOrder( destBlock[ j ] ); + destBlock[ j + 8 ] = ISwapWordOrder( destBlock[ j + 8 ] ); +#endif + } + + /// Now copy the block to the destination bitmap + /// (Trust me, this is actually *faster* than memcpy for some reason + destData = destBMap->GetAddr16( x, y ); + destData[ 0 ] = destBlock[ 0 ]; + destData[ 1 ] = destBlock[ 1 ]; + destData[ 2 ] = destBlock[ 2 ]; + destData[ 3 ] = destBlock[ 3 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 4 ]; + destData[ 1 ] = destBlock[ 5 ]; + destData[ 2 ] = destBlock[ 6 ]; + destData[ 3 ] = destBlock[ 7 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 8 ]; + destData[ 1 ] = destBlock[ 9 ]; + destData[ 2 ] = destBlock[ 10 ]; + destData[ 3 ] = destBlock[ 11 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 12 ]; + destData[ 1 ] = destBlock[ 13 ]; + destData[ 2 ] = destBlock[ 14 ]; + destData[ 3 ] = destBlock[ 15 ]; + + /// Increment and loop! + srcData += blockSize - 4; /// JUUUST in case our block size is diff + x += 4; + if( x == srcBMap->GetCurrWidth() ) + { + x = 0; + y += 4; + } + } +} + +//// IUncompressMipmapDXT1To16 //////////////////////////////////////////////// +// +// UncompressBitmap internal call for DXT1 compression. DXT1 is a simple on/off +// or all-on alpha 'compression'. +// +// Note: this version decompresses to a 1-5-5-5 ARGB format. + +void hsDXTSoftwareCodec::IUncompressMipmapDXT1To16( plMipmap *destBMap, plMipmap *srcBMap ) +{ + UInt16 *srcData, tempW1, tempW2; + UInt16 *destData, destBlock[ 16 ]; + UInt32 blockSize; + UInt32 bitSource, bitSource2, x, y, bMapStride; + UInt16 colors[ 4 ]; + Int32 numBlocks, i, j; + + + /// Setup some nifty stuff + hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" ); + hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" ); + numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4; + + blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words + srcData = (UInt16 *)srcBMap->GetCurrLevelPtr(); + // Note our trick here to make sure nothing breaks if GetAddr32's + // formula changes + bMapStride = (UInt32)( destBMap->GetAddr16( 0, 1 ) - destBMap->GetAddr16( 0, 0 ) ); + x = y = 0; + + + /// Loop through the # of blocks (width*height / 16-pixel-blocks) + for( i = 0; i < numBlocks; i++ ) + { + /// Decompress color data block + colors[ 0 ] = IRGB565To1555( srcData[ 0 ] ) | 0x8000; // Make sure alpha is set + colors[ 1 ] = IRGB565To1555( srcData[ 1 ] ) | 0x8000; // Make sure alpha is set + +#ifdef HS_BUILD_FOR_MAC + tempW1 = ISwapWordOrder( srcData[ 0 ] ); + tempW2 = ISwapWordOrder( srcData[ 1 ] ); +#else + tempW1 = srcData[ 0 ]; + tempW2 = srcData[ 1 ]; +#endif + if( tempW1 > tempW2 ) + { + /// Four-color block--mix the other two + colors[ 2 ] = IMixTwoThirdsRGB1555( colors[ 0 ], colors[ 1 ] ) | 0x8000;//IMixTwoThirdsRGB32( colors[ 0 ], colors[ 1 ] ) | 0xff000000; + colors[ 3 ] = IMixTwoThirdsRGB1555( colors[ 1 ], colors[ 0 ] ) | 0x8000;//IMixTwoThirdsRGB32( colors[ 1 ], colors[ 0 ] ) | 0xff000000; + } + else + { + /// Three-color block and transparent + colors[ 2 ] = IMixEqualRGB1555( colors[ 0 ], colors[ 1 ] ) | 0x8000;//IMixEqualRGB32( colors[ 0 ], colors[ 1 ] ) | 0xff000000; + colors[ 3 ] = 0; + } + + bitSource = srcData[ 2 ]; + bitSource2 = srcData[ 3 ]; + +#ifdef HS_BUILD_FOR_MAC + bitSource = ( bitSource >> 8 ) | ( ( bitSource & 0xff ) << 8 ); + bitSource2 = ( bitSource2 >> 8 ) | ( ( bitSource2 & 0xff ) << 8 ); +#endif + + for( j = 0; j < 8; j++ ) + { + destBlock[ j ] = colors[ bitSource & 0x03 ]; + bitSource >>= 2; + destBlock[ j + 8 ] = colors[ bitSource2 & 0x03 ]; + bitSource2 >>= 2; +#ifdef HS_BUILD_FOR_MAC + destBlock[ j ] = ISwapWordOrder( destBlock[ j ] ); + destBlock[ j + 8 ] = ISwapWordOrder( destBlock[ j + 8 ] ); +#endif + } + + /// Now copy the block to the destination bitmap + /// (Trust me, this is actually *faster* than memcpy for some reason + destData = destBMap->GetAddr16( x, y ); + destData[ 0 ] = destBlock[ 0 ]; + destData[ 1 ] = destBlock[ 1 ]; + destData[ 2 ] = destBlock[ 2 ]; + destData[ 3 ] = destBlock[ 3 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 4 ]; + destData[ 1 ] = destBlock[ 5 ]; + destData[ 2 ] = destBlock[ 6 ]; + destData[ 3 ] = destBlock[ 7 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 8 ]; + destData[ 1 ] = destBlock[ 9 ]; + destData[ 2 ] = destBlock[ 10 ]; + destData[ 3 ] = destBlock[ 11 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 12 ]; + destData[ 1 ] = destBlock[ 13 ]; + destData[ 2 ] = destBlock[ 14 ]; + destData[ 3 ] = destBlock[ 15 ]; + + /// Increment and loop! + srcData += blockSize; + x += 4; + if( x == srcBMap->GetCurrWidth() ) + { + x = 0; + y += 4; + } + } +} + +//// IUncompressMipmapDXT1To16Weird /////////////////////////////////////////// +// +// UncompressBitmap internal call for DXT1 compression. DXT1 is a simple on/off +// or all-on alpha 'compression'. +// +// Note: this version decompresses to a 5-5-5-1 RGBA format. + +void hsDXTSoftwareCodec::IUncompressMipmapDXT1To16Weird( plMipmap *destBMap, + plMipmap *srcBMap ) +{ + UInt16 *srcData, tempW1, tempW2; + UInt16 *destData, destBlock[ 16 ]; + UInt32 blockSize; + UInt32 bitSource, bitSource2, x, y, bMapStride; + UInt16 colors[ 4 ]; + Int32 numBlocks, i, j; + + + /// Setup some nifty stuff + hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" ); + hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" ); + numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4; + + blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words + srcData = (UInt16 *)srcBMap->GetCurrLevelPtr(); + // Note our trick here to make sure nothing breaks if GetAddr32's + // formula changes + bMapStride = (UInt32)( destBMap->GetAddr16( 0, 1 ) - destBMap->GetAddr16( 0, 0 ) ); + x = y = 0; + + + /// Loop through the # of blocks (width*height / 16-pixel-blocks) + for( i = 0; i < numBlocks; i++ ) + { + /// Decompress color data block + colors[ 0 ] = IRGB565To5551( srcData[ 0 ] ) | 0x0001; // Make sure alpha is set + colors[ 1 ] = IRGB565To5551( srcData[ 1 ] ) | 0x0001; // Make sure alpha is set + +#ifdef HS_BUILD_FOR_MAC + tempW1 = ISwapWordOrder( srcData[ 0 ] ); + tempW2 = ISwapWordOrder( srcData[ 1 ] ); +#else + tempW1 = srcData[ 0 ]; + tempW2 = srcData[ 1 ]; +#endif + if( tempW1 > tempW2 ) + { + /// Four-color block--mix the other two + colors[ 2 ] = IMixTwoThirdsRGB5551( colors[ 0 ], colors[ 1 ] ) | 0x0001; + colors[ 3 ] = IMixTwoThirdsRGB5551( colors[ 1 ], colors[ 0 ] ) | 0x0001; + } + else + { + /// Three-color block and transparent + colors[ 2 ] = IMixEqualRGB5551( colors[ 0 ], colors[ 1 ] ) | 0x0001; + colors[ 3 ] = 0; + } + + bitSource = srcData[ 2 ]; + bitSource2 = srcData[ 3 ]; + +#ifdef HS_BUILD_FOR_MAC + bitSource = ( bitSource >> 8 ) | ( ( bitSource & 0xff ) << 8 ); + bitSource2 = ( bitSource2 >> 8 ) | ( ( bitSource2 & 0xff ) << 8 ); +#endif + + for( j = 0; j < 8; j++ ) + { + destBlock[ j ] = colors[ bitSource & 0x03 ]; + bitSource >>= 2; + destBlock[ j + 8 ] = colors[ bitSource2 & 0x03 ]; + bitSource2 >>= 2; +#ifdef HS_BUILD_FOR_MAC + destBlock[ j ] = ISwapWordOrder( destBlock[ j ] ); + destBlock[ j + 8 ] = ISwapWordOrder( destBlock[ j + 8 ] ); +#endif + } + + /// Now copy the block to the destination bitmap + /// (Trust me, this is actually *faster* than memcpy for some reason + destData = destBMap->GetAddr16( x, y ); + destData[ 0 ] = destBlock[ 0 ]; + destData[ 1 ] = destBlock[ 1 ]; + destData[ 2 ] = destBlock[ 2 ]; + destData[ 3 ] = destBlock[ 3 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 4 ]; + destData[ 1 ] = destBlock[ 5 ]; + destData[ 2 ] = destBlock[ 6 ]; + destData[ 3 ] = destBlock[ 7 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 8 ]; + destData[ 1 ] = destBlock[ 9 ]; + destData[ 2 ] = destBlock[ 10 ]; + destData[ 3 ] = destBlock[ 11 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 12 ]; + destData[ 1 ] = destBlock[ 13 ]; + destData[ 2 ] = destBlock[ 14 ]; + destData[ 3 ] = destBlock[ 15 ]; + + /// Increment and loop! + srcData += blockSize; + x += 4; + if( x == srcBMap->GetCurrWidth() ) + { + x = 0; + y += 4; + } + } +} + +//// IUncompressMipmapDXT1To32 //////////////////////////////////////////////// +// +// UncompressBitmap internal call for DXT1 compression. DXT1 is a simple on/off +// or all-on alpha 'compression'. Output is a 32-bit ARGB 8888 bitmap. +// +// 7.31.2000 - M.Burrack - Created, based on old code (uncredited) + +void hsDXTSoftwareCodec::IUncompressMipmapDXT1To32( plMipmap *destBMap, + plMipmap *srcBMap ) +{ + UInt16 *srcData, tempW1, tempW2; + UInt32 *destData, destBlock[ 16 ]; + UInt32 blockSize; + UInt32 bitSource, bitSource2, x, y, bMapStride; + UInt32 colors[ 4 ]; + Int32 numBlocks, i, j; + + + /// Setup some nifty stuff + hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" ); + hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" ); + numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4; + + blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words + srcData = (UInt16 *)srcBMap->GetCurrLevelPtr(); + // Note our trick here to make sure nothing breaks if GetAddr32's + // formula changes + bMapStride = (UInt32)( destBMap->GetAddr32( 0, 1 ) - destBMap->GetAddr32( 0, 0 ) ); + x = y = 0; + + + /// Loop through the # of blocks (width*height / 16-pixel-blocks) + for( i = 0; i < numBlocks; i++ ) + { + /// Decompress color data block + colors[ 0 ] = IRGB16To32Bit( srcData[ 0 ] ) | 0xff000000; + colors[ 1 ] = IRGB16To32Bit( srcData[ 1 ] ) | 0xff000000; + +#ifdef HS_BUILD_FOR_MAC + tempW1 = ISwapWordOrder( srcData[ 0 ] ); + tempW2 = ISwapWordOrder( srcData[ 1 ] ); +#else + tempW1 = srcData[ 0 ]; + tempW2 = srcData[ 1 ]; +#endif + if( tempW1 > tempW2 ) + { + /// Four-color block--mix the other two + colors[ 2 ] = IMixTwoThirdsRGB32( colors[ 0 ], colors[ 1 ] ) | 0xff000000; + colors[ 3 ] = IMixTwoThirdsRGB32( colors[ 1 ], colors[ 0 ] ) | 0xff000000; + } + else + { + /// Three-color block and transparent + colors[ 2 ] = IMixEqualRGB32( colors[ 0 ], colors[ 1 ] ) | 0xff000000; + colors[ 3 ] = 0; + } + + bitSource = srcData[ 2 ]; + bitSource2 = srcData[ 3 ]; + +#ifdef HS_BUILD_FOR_MAC + bitSource = ( bitSource >> 8 ) | ( ( bitSource & 0xff ) << 8 ); + bitSource2 = ( bitSource2 >> 8 ) | ( ( bitSource2 & 0xff ) << 8 ); +#endif + + for( j = 0; j < 8; j++ ) + { + destBlock[ j ] = colors[ bitSource & 0x03 ]; + bitSource >>= 2; + destBlock[ j + 8 ] = colors[ bitSource2 & 0x03 ]; + bitSource2 >>= 2; +#ifdef HS_BUILD_FOR_MAC + destBlock[ j ] = ISwapDwordOrder( destBlock[ j ] ); + destBlock[ j + 8 ] = ISwapDwordOrder( destBlock[ j + 8 ] ); +#endif + } + + /// Now copy the block to the destination bitmap + /// (Trust me, this is actually *faster* than memcpy for some reason + destData = destBMap->GetAddr32( x, y ); + destData[ 0 ] = destBlock[ 0 ]; + destData[ 1 ] = destBlock[ 1 ]; + destData[ 2 ] = destBlock[ 2 ]; + destData[ 3 ] = destBlock[ 3 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 4 ]; + destData[ 1 ] = destBlock[ 5 ]; + destData[ 2 ] = destBlock[ 6 ]; + destData[ 3 ] = destBlock[ 7 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 8 ]; + destData[ 1 ] = destBlock[ 9 ]; + destData[ 2 ] = destBlock[ 10 ]; + destData[ 3 ] = destBlock[ 11 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 12 ]; + destData[ 1 ] = destBlock[ 13 ]; + destData[ 2 ] = destBlock[ 14 ]; + destData[ 3 ] = destBlock[ 15 ]; + + /// Increment and loop! + srcData += blockSize; + x += 4; + if( x == srcBMap->GetCurrWidth() ) + { + x = 0; + y += 4; + } + } +} + +//// IUncompressMipmapDXT1ToInten ///////////////////////////////////////////// +// +// UncompressBitmap internal call for DXT1 compression. DXT1 is a simple on/off +// or all-on alpha 'compression'. Output is an 8-bit intensity bitmap, +// constructed from the blue-color channel of the DXT1 output. + +void hsDXTSoftwareCodec::IUncompressMipmapDXT1ToInten( plMipmap *destBMap, + plMipmap *srcBMap ) +{ + UInt16 *srcData, tempW1, tempW2; + UInt8 *destData, destBlock[ 16 ]; + UInt32 blockSize; + UInt32 bitSource, bitSource2, x, y, bMapStride; + UInt8 colors[ 4 ]; + Int32 numBlocks, i, j; + + + /// Setup some nifty stuff + hsAssert( ( srcBMap->GetCurrWidth() & 3 ) == 0, "Bitmap width must be multiple of 4" ); + hsAssert( ( srcBMap->GetCurrHeight() & 3 ) == 0, "Bitmap height must be multiple of 4" ); + numBlocks = ( srcBMap->GetCurrWidth() * srcBMap->GetCurrHeight() ) >> 4; + + blockSize = srcBMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words + srcData = (UInt16 *)srcBMap->GetCurrLevelPtr(); + // Note our trick here to make sure nothing breaks if GetAddr8's + // formula changes + bMapStride = (UInt32)( destBMap->GetAddr8( 0, 1 ) - destBMap->GetAddr8( 0, 0 ) ); + x = y = 0; + + /// Loop through the # of blocks (width*height / 16-pixel-blocks) + for( i = 0; i < numBlocks; i++ ) + { + /// Decompress color data block (cast will automatically truncate to blue) + colors[ 0 ] = (UInt8)IRGB16To32Bit( srcData[ 0 ] ); + colors[ 1 ] = (UInt8)IRGB16To32Bit( srcData[ 1 ] ); + +#ifdef HS_BUILD_FOR_MAC + tempW1 = ISwapWordOrder( srcData[ 0 ] ); + tempW2 = ISwapWordOrder( srcData[ 1 ] ); +#else + tempW1 = srcData[ 0 ]; + tempW2 = srcData[ 1 ]; +#endif + if( tempW1 > tempW2 ) + { + /// Four-color block--mix the other two + colors[ 2 ] = IMixTwoThirdsInten( colors[ 0 ], colors[ 1 ] ); + colors[ 3 ] = IMixTwoThirdsInten( colors[ 1 ], colors[ 0 ] ); + } + else + { + /// Three-color block and transparent + colors[ 2 ] = IMixEqualInten( colors[ 0 ], colors[ 1 ] ); + colors[ 3 ] = 0; + } + + bitSource = srcData[ 2 ]; + bitSource2 = srcData[ 3 ]; + +#ifdef HS_BUILD_FOR_MAC + bitSource = ( bitSource >> 8 ) | ( ( bitSource & 0xff ) << 8 ); + bitSource2 = ( bitSource2 >> 8 ) | ( ( bitSource2 & 0xff ) << 8 ); +#endif + + for( j = 0; j < 8; j++ ) + { + destBlock[ j ] = colors[ bitSource & 0x03 ]; + bitSource >>= 2; + destBlock[ j + 8 ] = colors[ bitSource2 & 0x03 ]; + bitSource2 >>= 2; + } + + /// Now copy the block to the destination bitmap + /// (Trust me, this is actually *faster* than memcpy for some reason + destData = destBMap->GetAddr8( x, y ); + destData[ 0 ] = destBlock[ 0 ]; + destData[ 1 ] = destBlock[ 1 ]; + destData[ 2 ] = destBlock[ 2 ]; + destData[ 3 ] = destBlock[ 3 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 4 ]; + destData[ 1 ] = destBlock[ 5 ]; + destData[ 2 ] = destBlock[ 6 ]; + destData[ 3 ] = destBlock[ 7 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 8 ]; + destData[ 1 ] = destBlock[ 9 ]; + destData[ 2 ] = destBlock[ 10 ]; + destData[ 3 ] = destBlock[ 11 ]; + destData += bMapStride; + destData[ 0 ] = destBlock[ 12 ]; + destData[ 1 ] = destBlock[ 13 ]; + destData[ 2 ] = destBlock[ 14 ]; + destData[ 3 ] = destBlock[ 15 ]; + + /// Increment and loop! + srcData += blockSize; + x += 4; + if( x == srcBMap->GetCurrWidth() ) + { + x = 0; + y += 4; + } + } +} + +//// ISwapDwordOrder ////////////////////////////////////////////////////////// + +UInt32 hsDXTSoftwareCodec::ISwapDwordOrder( UInt32 color ) +{ + UInt8 a, r, g, b; + + + a = (UInt8)( color >> 24 ); + r = (UInt8)( color >> 16 ) & 0xff; + g = (UInt8)( color >> 8 ) & 0xff; + b = (UInt8)( color & 0xff ); + return( ( b << 24 ) | ( g << 16 ) | ( r << 8 ) | a ); +} + +//// ISwapWordOrder /////////////////////////////////////////////////////////// + +UInt16 hsDXTSoftwareCodec::ISwapWordOrder( UInt16 color ) +{ + return( ( color >> 8 ) | ( color << 8 ) ); +} + +//// IRGB16To32Bit //////////////////////////////////////////////////////////// +// +// Converts a RGB565 16-bit color into a RGB888 32-bit color. Alpha (upper 8 +// bits) is 0. Will be optimized LATER. +// + +UInt32 hsDXTSoftwareCodec::IRGB16To32Bit( UInt16 color ) +{ + UInt32 r, g, b; + +#ifdef HS_BUILD_FOR_MAC + color = ( ( color >> 8 ) | ( ( color & 0xff ) << 8 ) ); +#endif + + b = ( color & 31 ) << 3; + color >>= 5; + + g = ( color & 63 ) << ( 2 + 8 ); + color >>= 6; + + r = ( color & 31 ) << ( 3 + 16 ); + + return( r + g + b ); +} + +//// IRGB565To4444 //////////////////////////////////////////////////////////// +// +// Converts a RGB565 16-bit color into a RGB4444 16-bit color. Alpha (upper 8 +// bits) is 0. +// + +UInt16 hsDXTSoftwareCodec::IRGB565To4444( UInt16 color ) +{ + UInt16 r, g, b; + +#ifdef HS_BUILD_FOR_MAC + color = ISwapWordOrder( color ); +#endif + + b = ( color & 31 ) >> 1; + color >>= 5; + + g = ( color & 63 ) >> 2; + color >>= 6; + g <<= 4; + + r = ( color & 31 ) >> 1; + r <<= 8; + + return( r + g + b ); +} + +//// IRGB565To4444Rev ///////////////////////////////////////////////////////// +// +// Converts a RGB565 16-bit color into a RGB4444 16-bit color. Alpha (upper 8 +// bits) is 0. This is the OpenGL-friendly version (B and R are swapped) +// + +UInt16 hsDXTSoftwareCodec::IRGB565To4444Rev( UInt16 color ) +{ + UInt16 r, g, b; + +#ifdef HS_BUILD_FOR_MAC + color = ISwapWordOrder( color ); +#endif + + r = ( color & 31 ) >> 1; + color >>= 5; + r <<= 8; + + g = ( color & 63 ) >> 2; + color >>= 6; + g <<= 4; + + b = ( color & 31 ) >> 1; + + return( r + g + b ); +} + +//// IRGB565To1555 //////////////////////////////////////////////////////////// +// +// Converts a RGB565 16-bit color into a RGB1555 16-bit color. Alpha (upper 1 +// bit) is 0. +// + +UInt16 hsDXTSoftwareCodec::IRGB565To1555( UInt16 color ) +{ + UInt16 r, g, b; + +#ifdef HS_BUILD_FOR_MAC + color = ISwapWordOrder( color ); +#endif + + b = ( color & 31 ); + color >>= 5; + + g = ( color & 63 ) >> 1; + g <<= 5; + color >>= 6; + + r = ( color & 31 ); + r <<= 10; + + return( r + g + b ); +} + +//// IRGB565To5551 //////////////////////////////////////////////////////////// +// +// Converts a RGB565 16-bit color into a RGB5551 16-bit color. Alpha (lowest 1 +// bit) is 0. +// + +UInt16 hsDXTSoftwareCodec::IRGB565To5551( UInt16 color ) +{ + UInt16 rg, b; + +#ifdef HS_BUILD_FOR_MAC + color = ISwapWordOrder( color ); +#endif + + rg = color & 0xffc0; /// Masks off red and green + b = ( color & 31 ); + b <<= 1; + + return( rg | b ); +} + +//// IMixTwoThirdsRGB32 /////////////////////////////////////////////////////// +// +// Returns a 32-bit RGB888 value resulting from the mixing of 2/3rds of the +// first parameter and 1/3rd of the second. Will be optimized LATER. +// + +UInt32 hsDXTSoftwareCodec::IMixTwoThirdsRGB32( UInt32 twoThirds, UInt32 oneThird ) +{ + UInt32 r, g, b; + + + r = ( ( twoThirds & 0x00ff0000 ) + ( twoThirds & 0x00ff0000 ) + + ( oneThird & 0x00ff0000 ) ) / 3; + r &= 0x00ff0000; + + g = ( ( twoThirds & 0x0000ff00 ) + ( twoThirds & 0x0000ff00 ) + + ( oneThird & 0x0000ff00 ) ) / 3; + g &= 0x0000ff00; + + b = ( ( twoThirds & 0x000000ff ) + ( twoThirds & 0x000000ff ) + + ( oneThird & 0x000000ff ) ) / 3; + b &= 0x000000ff; + + return( r + g + b ); +} + +//// IMixTwoThirdsRGB1555 ///////////////////////////////////////////////////// +// +// Returns a 16-bit RGB1555 value resulting from the mixing of 2/3rds of the +// first parameter and 1/3rd of the second. Alpha is ignored. +// + +UInt16 hsDXTSoftwareCodec::IMixTwoThirdsRGB1555( UInt16 twoThirds, UInt16 oneThird ) +{ + UInt16 r, g, b; + + + r = ( ( twoThirds & 0x7c00 ) + ( twoThirds & 0x7c00 ) + + ( oneThird & 0x7c00 ) ) / 3; + r &= 0x7c00; + + g = ( ( twoThirds & 0x03e0 ) + ( twoThirds & 0x03e0 ) + + ( oneThird & 0x03e0 ) ) / 3; + g &= 0x03e0; + + b = ( ( twoThirds & 0x001f ) + ( twoThirds & 0x001f ) + + ( oneThird & 0x001f ) ) / 3; + b &= 0x001f; + + return( r + g + b ); +} + +//// IMixTwoThirdsRGB5551 ///////////////////////////////////////////////////// +// +// Returns a 16-bit RGB5551 value resulting from the mixing of 2/3rds of the +// first parameter and 1/3rd of the second. Alpha is ignored. +// + +UInt16 hsDXTSoftwareCodec::IMixTwoThirdsRGB5551( UInt16 twoThirds, UInt16 oneThird ) +{ + UInt16 r, g, b; + + + r = ( ( twoThirds & 0xf800 ) + ( twoThirds & 0xf800 ) + + ( oneThird & 0xf800 ) ) / 3; + r &= 0xf800; + + g = ( ( twoThirds & 0x07c0 ) + ( twoThirds & 0x07c0 ) + + ( oneThird & 0x07c0 ) ) / 3; + g &= 0x07c0; + + b = ( ( twoThirds & 0x003e ) + ( twoThirds & 0x003e ) + + ( oneThird & 0x003e ) ) / 3; + b &= 0x003e; + + return( r + g + b ); +} + +//// IMixTwoThirdsRGB4444 ///////////////////////////////////////////////////// +// +// Returns a 16-bit RGB4444 value resulting from the mixing of 2/3rds of the +// first parameter and 1/3rd of the second. Alpha is ignored. +// + +UInt16 hsDXTSoftwareCodec::IMixTwoThirdsRGB4444( UInt16 twoThirds, UInt16 oneThird ) +{ + UInt16 r, g, b; + + + r = ( ( twoThirds & 0x0f00 ) + ( twoThirds & 0x0f00 ) + + ( oneThird & 0x0f00 ) ) / 3; + r &= 0x0f00; + + g = ( ( twoThirds & 0x00f0 ) + ( twoThirds & 0x00f0 ) + + ( oneThird & 0x00f0 ) ) / 3; + g &= 0x00f0; + + b = ( ( twoThirds & 0x000f ) + ( twoThirds & 0x000f ) + + ( oneThird & 0x000f ) ) / 3; + b &= 0x000f; + + return( r + g + b ); +} + +//// IMixTwoThirdsInten /////////////////////////////////////////////////////// +// +// Returns an 8-bit intensity value resulting from the mixing of 2/3rds of the +// first parameter and 1/3rd of the second. +// + +UInt8 hsDXTSoftwareCodec::IMixTwoThirdsInten( UInt8 twoThirds, UInt8 oneThird ) +{ + return( ( twoThirds + twoThirds + oneThird ) / 3 ); +} + +//// IMixEqualRGB32 /////////////////////////////////////////////////////////// +// +// Returns a 32-bit RGB888 value resulting from the mixing of equal parts of +// the two colors given. +// + +UInt32 hsDXTSoftwareCodec::IMixEqualRGB32( UInt32 color1, UInt32 color2 ) +{ + UInt32 r, g, b; + + + r = ( ( color1 & 0x00ff0000 ) + ( color2 & 0x00ff0000 ) ) >> 1; + r &= 0x00ff0000; + + g = ( ( color1 & 0x0000ff00 ) + ( color2 & 0x0000ff00 ) ) >> 1; + g &= 0x0000ff00; + + b = ( ( color1 & 0x000000ff ) + ( color2 & 0x000000ff ) ) >> 1; + b &= 0x000000ff; + + return( r + g + b ); +} + +//// IMixEqualRGB1555 ///////////////////////////////////////////////////////// +// +// Returns a 16-bit RGB1555 value resulting from the mixing of equal parts of +// the two colors given. +// + +UInt16 hsDXTSoftwareCodec::IMixEqualRGB1555( UInt16 color1, UInt16 color2 ) +{ + UInt16 r, g, b; + + + r = ( ( color1 & 0x7c00 ) + ( color2 & 0x7c00 ) ) >> 1; + r &= 0x7c00; + + g = ( ( color1 & 0x03e0 ) + ( color2 & 0x03e0 ) ) >> 1; + g &= 0x03e0; + + b = ( ( color1 & 0x001f ) + ( color2 & 0x001f ) ) >> 1; + b &= 0x001f; + + return( r + g + b ); +} + +//// IMixEqualRGB5551 ///////////////////////////////////////////////////////// +// +// Returns a 16-bit RGB5551 value resulting from the mixing of equal parts of +// the two colors given. +// + +UInt16 hsDXTSoftwareCodec::IMixEqualRGB5551( UInt16 color1, UInt16 color2 ) +{ + UInt16 r, g, b; + + + r = ( ( color1 & 0xf800 ) + ( color2 & 0xf800 ) ) >> 1; + r &= 0xf800; + + g = ( ( color1 & 0x07c0 ) + ( color2 & 0x07c0 ) ) >> 1; + g &= 0x07c0; + + b = ( ( color1 & 0x003e ) + ( color2 & 0x003e ) ) >> 1; + b &= 0x003e; + + return( r + g + b ); +} + +//// IMixEqualRGB4444 ///////////////////////////////////////////////////////// +// +// Returns a 16-bit RGB4444 value resulting from the mixing of equal parts of +// the two colors given. +// + +UInt16 hsDXTSoftwareCodec::IMixEqualRGB4444( UInt16 color1, UInt16 color2 ) +{ + UInt16 r, g, b; + + + r = ( ( color1 & 0x0f00 ) + ( color2 & 0x0f00 ) ) >> 1; + r &= 0x0f00; + + g = ( ( color1 & 0x00f0 ) + ( color2 & 0x00f0 ) ) >> 1; + g &= 0x00f0; + + b = ( ( color1 & 0x000f ) + ( color2 & 0x000f ) ) >> 1; + b &= 0x000f; + + return( r + g + b ); +} + +//// IMixEqualInten /////////////////////////////////////////////////////////// +// +// Returns an 8-bit intensity value resulting from the mixing of equal parts of +// the two colors given. +// + +UInt8 hsDXTSoftwareCodec::IMixEqualInten( UInt8 color1, UInt8 color2 ) +{ + return( ( color1 + color2 ) >> 1 ); +} + + + +void hsDXTSoftwareCodec::CompressMipmapLevel( plMipmap *uncompressed, plMipmap *compressed ) +{ + UInt32 *compressedImage = (UInt32 *)compressed->GetCurrLevelPtr(); + UInt32 *uncompressedImage = (UInt32 *)uncompressed->GetCurrLevelPtr(); + Int32 x, y; + Int32 xMax = uncompressed->GetCurrWidth() >> 2; + Int32 yMax = uncompressed->GetCurrHeight() >> 2; + for (x = 0; x < xMax; ++x) + { + for (y = 0; y < yMax; ++y) + { + UInt8 maxAlpha = 0; + UInt8 minAlpha = 255; + UInt8 oldMaxAlpha = 0; + UInt8 oldMinAlpha = 255; + UInt8 alpha[8]; + Int32 maxDistance = 0; + hsRGBAColor32 color[4]; + hsBool hasTransparency = false; + + Int32 xx, yy; + for (xx = 0; xx < 4; ++xx) + { + for (yy = 0; yy < 4; ++yy) + { + hsRGBAColor32* pixel = (hsRGBAColor32*)uncompressed->GetAddr32(4 * x + xx, 4 * y + yy); + UInt8 pixelAlpha = pixel->a; + if (pixelAlpha != 255) + { + hasTransparency = true; + } + + if (compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5) + { + if (pixelAlpha > maxAlpha) + { + maxAlpha = pixelAlpha; + } + + if ((pixelAlpha > oldMaxAlpha) && (pixelAlpha < 255)) + { + oldMaxAlpha = pixelAlpha; + } + + if (pixelAlpha < minAlpha) + { + minAlpha = pixelAlpha; + } + + if ((pixelAlpha < oldMinAlpha) && (pixelAlpha > 0)) + { + oldMinAlpha = minAlpha; + } + } + + Int32 xx2, yy2; + for (xx2 = 0; xx2 < 4; ++xx2) + { + for (yy2 = 0; yy2 < 4; ++yy2) + { + hsRGBAColor32* pixel1 = (hsRGBAColor32*)uncompressed->GetAddr32(4 * x + xx, 4 * y + yy); + hsRGBAColor32* pixel2 = (hsRGBAColor32*)uncompressed->GetAddr32(4 * x + xx2, 4 * y + yy2); + + Int32 distance = ColorDistanceARGBSquared(*pixel1, *pixel2); + if (distance >= maxDistance) + { + maxDistance = distance; + color[0] = *pixel1; + color[1] = *pixel2; + } + } // for yy2 + } // for xx2 + } // for yy + } // for xx + + if (oldMinAlpha == 255) + { + hsAssert(oldMaxAlpha == 0, "Weirdness in oldMaxAlpha hsDXTSoftwareCodec::CompressBitmap."); + oldMinAlpha = 0; + oldMaxAlpha = 255; + } + + if (compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5) + { + if ((maxAlpha == 255) && (minAlpha == 0)) + { + hsAssert(oldMinAlpha <= oldMaxAlpha, "Min > Max in hsDXTSoftwareCodec::CompressBitmap 1."); + alpha[0] = oldMinAlpha; + alpha[1] = oldMaxAlpha; + alpha[2] = (4 * alpha[0] + alpha[1]) / 5; // Bit code 010 + alpha[3] = (3 * alpha[0] + 2 * alpha[1]) / 5; // Bit code 011 + alpha[4] = (2 * alpha[0] + 3 * alpha[1]) / 5; // Bit code 100 + alpha[5] = (alpha[0] + 4 * alpha[1]) / 5; // Bit code 101 + alpha[6] = 0; // Bit code 110 + alpha[7] = 255; // Bit code 111 + } + else if (maxAlpha == minAlpha) + { + alpha[0] = minAlpha; + alpha[1] = maxAlpha; + alpha[2] = (4 * alpha[0] + alpha[1]) / 5; // Bit code 010 + alpha[3] = (3 * alpha[0] + 2 * alpha[1]) / 5; // Bit code 011 + alpha[4] = (2 * alpha[0] + 3 * alpha[1]) / 5; // Bit code 100 + alpha[5] = (alpha[0] + 4 * alpha[1]) / 5; // Bit code 101 + alpha[6] = 0; // Bit code 110 + alpha[7] = 255; // Bit code 111 + } + else + { + hsAssert(minAlpha < maxAlpha, "Min => Max in hsDXTSoftwareCodec::CompressBitmap 3."); + alpha[0] = maxAlpha; + alpha[1] = minAlpha; + alpha[2] = (6 * alpha[0] + alpha[1]) / 7; // bit code 010 + alpha[3] = (5 * alpha[0] + 2 * alpha[1]) / 7; // Bit code 011 + alpha[4] = (4 * alpha[0] + 3 * alpha[1]) / 7; // Bit code 100 + alpha[5] = (3 * alpha[0] + 4 * alpha[1]) / 7; // Bit code 101 + alpha[6] = (2 * alpha[0] + 5 * alpha[1]) / 7; // Bit code 110 + alpha[7] = (alpha[0] + 6 * alpha[1]) / 7; // Bit code 111 + } + } + + UInt32 encoding; + UInt16 shortColor[2]; + shortColor[0] = Color32To16(color[0]); + shortColor[1] = Color32To16(color[1]); + if ((shortColor[0] == shortColor[1]) || + ((compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1) && + hasTransparency)) + { + encoding = kThreeColorEncoding; + + if (shortColor[0] > shortColor[1]) + { + UInt16 temp = shortColor[1]; + shortColor[1] = shortColor[0]; + shortColor[0] = temp; + + hsRGBAColor32 temp32 = color[1]; + color[1] = color[0]; + color[0] = temp32; + } + + color[2] = BlendColors32(1, color[0], 1, color[1]); + + hsRGBAColor32 black; + black.Set(0, 0, 0, 0); + + color[3] = black; + } + else + { + encoding = kFourColorEncoding; + + if (shortColor[0] < shortColor[1]) + { + UInt16 temp = shortColor[1]; + shortColor[1] = shortColor[0]; + shortColor[0] = temp; + + hsRGBAColor32 temp32 = color[1]; + color[1] = color[0]; + color[0] = temp32; + } + + color[2] = BlendColors32(2, color[0], 1, color[1]); + color[3] = BlendColors32(1, color[0], 2, color[1]); + } + + // Process each pixel in block + UInt32 blockSize = compressed->fDirectXInfo.fBlockSize; + UInt32 *block = &compressedImage[(x + xMax * y) * (blockSize >> 2)]; + UInt8 *byteBlock = (UInt8 *)block; + UInt8 *alphaBlock = nil; + UInt16 *colorBlock = nil; + if (compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5) + { + alphaBlock = byteBlock; + colorBlock = (UInt16 *)(byteBlock + 8); + alphaBlock[0] = 0; + alphaBlock[1] = 0; + alphaBlock[2] = 0; + alphaBlock[3] = 0; + alphaBlock[4] = 0; + alphaBlock[5] = 0; + alphaBlock[6] = 0; + alphaBlock[7] = 0; + } + else if (compressed->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT1) + { + alphaBlock = nil; + colorBlock = (UInt16 *)(byteBlock); + } + else + { + hsAssert(false, "Unrecognized compression scheme."); + } + + colorBlock[0] = 0; + colorBlock[1] = 0; + colorBlock[2] = 0; + colorBlock[3] = 0; + for (xx = 0; xx < 4; ++xx) + { + for (yy = 0; yy < 4; ++yy) + { + hsRGBAColor32* pixel = (hsRGBAColor32*)uncompressed->GetAddr32(4 * x + xx, 4 * y + yy); + UInt8 pixelAlpha = pixel->a; + if (alphaBlock) + { + UInt32 alphaIndex = 0; + UInt32 alphaDistance = abs(pixelAlpha - alpha[0]); + + Int32 i; + for (i = 1; i < 8; i++) + { + UInt32 distance = abs(pixelAlpha - alpha[i]); + if (distance < alphaDistance) + { + alphaIndex = i; + alphaDistance = distance; + } + } + + if (yy < 2) + { + UInt32 alphaShift = 3 * (4 * yy + xx); + UInt32 threeAlphaBytes = alphaIndex << alphaShift; + alphaBlock[2] |= (threeAlphaBytes & 0xff); + alphaBlock[3] |= ((threeAlphaBytes >> 8) & 0xff); + alphaBlock[4] |= ((threeAlphaBytes >> 16) & 0xff); + } + else + { + UInt32 alphaShift = 3 * (4 * (yy - 2) + xx); + UInt32 threeAlphaBytes = alphaIndex << alphaShift; + alphaBlock[5] |= (threeAlphaBytes & 0xff); + alphaBlock[6] |= ((threeAlphaBytes >> 8) & 0xff); + alphaBlock[7] |= ((threeAlphaBytes >> 16) & 0xff); + } + } + + UInt32 colorShift = 2 * (4 * yy + xx); + UInt32 colorIndex = 0; + UInt32 colorDistance = ColorDistanceARGBSquared(*pixel, color[0]); + + if ((encoding == kThreeColorEncoding) && + (pixelAlpha == 0)) + { + colorIndex = 3; + } + else + { + Int32 i; + Int32 colorMax = (encoding == kThreeColorEncoding) ? 3 : 4; + for (i = 1; i < colorMax; i++) + { + UInt32 distance = ColorDistanceARGBSquared(*pixel, color[i]); + if (distance < colorDistance) + { + colorIndex = i; + colorDistance = distance; + } + } + } + + if (yy < 2) + { + UInt32 colorShift = 2 * (4 * yy + xx); + UInt16 colorWord = (UInt16)(colorIndex << colorShift); + colorBlock[2] |= colorWord; + } + else + { + UInt32 colorShift = 2 * (4 * (yy - 2) + xx); + UInt16 colorWord = (UInt16)(colorIndex << colorShift); + colorBlock[3] |= colorWord; + } + } // for yy + } // for xx + + if (alphaBlock) + { + alphaBlock[0] = alpha[0]; + alphaBlock[1] = alpha[1]; + } + + colorBlock[0] = shortColor[0]; + colorBlock[1] = shortColor[1]; + } // for y + } // for x +} + +UInt16 hsDXTSoftwareCodec::BlendColors16(UInt16 weight1, UInt16 color1, UInt16 weight2, UInt16 color2) +{ + UInt16 r1, r2, g1, g2, b1, b2; + UInt16 r, g, b; + + r1 = (color1 >> 8) & 0xf8; + g1 = (color1 >> 3) & 0xfc; + b1 = (color1 << 3) & 0xf8; + + r2 = (color2 >> 8) & 0xf8; + g2 = (color2 >> 3) & 0xfc; + b2 = (color2 << 3) & 0xf8; + + r = ((UInt16)r1 * weight1 + (UInt16)r2 * weight2)/(weight1 + weight2); + g = ((UInt16)g1 * weight1 + (UInt16)g2 * weight2)/(weight1 + weight2); + b = ((UInt16)b1 * weight1 + (UInt16)b2 * weight2)/(weight1 + weight2); + + return ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3); +} + + +hsRGBAColor32 hsDXTSoftwareCodec::BlendColors32(UInt32 weight1, hsRGBAColor32 color1, + UInt32 weight2, hsRGBAColor32 color2) +{ + hsRGBAColor32 result; + + result.r = static_cast((color1.r * weight1 + color2.r * weight2)/(weight1 + weight2)); + result.g = static_cast((color1.g * weight1 + color2.g * weight2)/(weight1 + weight2)); + result.b = static_cast((color1.b * weight1 + color2.b * weight2)/(weight1 + weight2)); + + return result; +} + + +Int32 hsDXTSoftwareCodec::ColorDistanceARGBSquared(hsRGBAColor32 color1, hsRGBAColor32 color2) +{ + Int32 r1, g1, b1; + Int32 r2, g2, b2; + + r1 = color1.r; + r2 = color2.r; + g1 = color1.g; + g2 = color2.g; + b1 = color1.b; + b2 = color2.b; + + return (r1 - r2) * (r1 - r2) + (g1 - g2) * (g1 - g2) + (b1 - b2) * (b1 - b2); +} + +UInt16 hsDXTSoftwareCodec::Color32To16(hsRGBAColor32 color) +{ + UInt8 r = (UInt8)(color.r & 0xf8); + UInt8 g = (UInt8)(color.g & 0xfc); + UInt8 b = (UInt8)(color.b & 0xf8); + + return (r << 8) | (g << 3) | (b >> 3); +} + +hsBool hsDXTSoftwareCodec::Register() +{ + return hsCodecManager::Instance().Register(&(Instance()), plMipmap::kDirectXCompression, 100); +} + +//// ICalcCompressedFormat //////////////////////////////////////////////////// +// Determine the DXT compression format based on a bitmap. + +UInt8 hsDXTSoftwareCodec::ICalcCompressedFormat( plMipmap *bMap ) +{ + if( bMap->GetFlags() & plMipmap::kAlphaChannelFlag ) + return plMipmap::DirectXInfo::kDXT5; + + return plMipmap::DirectXInfo::kDXT1; +} + + +//// ColorizeCompBitmap /////////////////////////////////////////////////////// +// Colorizes a compressed bitmap according to the color mask given. + +hsBool hsDXTSoftwareCodec::ColorizeCompMipmap( plMipmap *bMap, const UInt8 *colorMask ) +{ + UInt32 numBlocks, blockSize; + UInt16 *srcData, color1, color2, gray, grayDiv2, i; + UInt8 compMasks[ 3 ][ 2 ] = { { 0, 0 }, { 0, 0xff }, { 0xff, 0 } }; + + + /// Sanity checks + hsAssert( bMap != nil, "Nil bitmap passed to ColorizeCompMipmap()" ); + hsAssert( colorMask != nil, "Nil color mask passed to ColorizeCompMipmap()" ); + hsAssert( bMap->IsCompressed(), "Trying to colorize uncompressed bitmap" ); + + + /// Calc some stuff + numBlocks = ( bMap->GetCurrWidth() * bMap->GetCurrHeight() ) >> 4; + + blockSize = bMap->fDirectXInfo.fBlockSize >> 1; // In 16-bit words + srcData = (UInt16 *)bMap->GetCurrLevelPtr(); + + // If we're DXT5, we'll artificially advance srcData so it points to the start + // of the first *color* block, not the first compressed block + if( bMap->fDirectXInfo.fCompressionType == plMipmap::DirectXInfo::kDXT5 ) + srcData += 4; // Alpha was 4 16-bit words worth + + + /// Loop! + for( ; numBlocks > 0; numBlocks-- ) + { + /// Get the two colors to colorize (our decompression scheme will do the rest... + /// handy, eh? :) +#if HS_BUILD_FOR_MAC + color1 = ISwapWordOrder( srcData[ 0 ] ); + color2 = ISwapWordOrder( srcData[ 1 ] ); +#else + color1 = srcData[ 0 ]; + color2 = srcData[ 1 ]; +#endif + + /// Now colorize using our age-old formula (see hsGMipmap::ColorLevel for details) + gray = ( ( color1 >> 11 ) & 0x1f ) + ( ( color1 >> 6 ) & 0x1f ) + ( color1 & 0x1f ); + gray /= 3; + gray = 0x1f - ( ( 0x1f - gray ) >> 1 ); // Lighten it 50% + grayDiv2 = gray >> 1; + + color1 = ( ( gray & compMasks[ colorMask[ 0 ] ][ 0 ] ) | + ( grayDiv2 & compMasks[ colorMask[ 0 ] ][ 1 ] ) ) << 11; + color1 |= ( ( gray & compMasks[ colorMask[ 1 ] ][ 0 ] ) | + ( grayDiv2 & compMasks[ colorMask[ 1 ] ][ 1 ] ) ) << 6; + color1 |= ( ( gray & compMasks[ colorMask[ 2 ] ][ 0 ] ) | + ( grayDiv2 & compMasks[ colorMask[ 2 ] ][ 1 ] ) ); + + gray = ( ( color2 >> 11 ) & 0x1f ) + ( ( color2 >> 6 ) & 0x1f ) + ( color2 & 0x1f ); + gray /= 3; + gray = 0x1f - ( ( 0x1f - gray ) >> 1 ); // Lighten it 50% + grayDiv2 = gray >> 1; + + color2 = ( ( gray & compMasks[ colorMask[ 0 ] ][ 0 ] ) | + ( grayDiv2 & compMasks[ colorMask[ 0 ] ][ 1 ] ) ) << 11; + color2 |= ( ( gray & compMasks[ colorMask[ 1 ] ][ 0 ] ) | + ( grayDiv2 & compMasks[ colorMask[ 1 ] ][ 1 ] ) ) << 6; + color2 |= ( ( gray & compMasks[ colorMask[ 2 ] ][ 0 ] ) | + ( grayDiv2 & compMasks[ colorMask[ 2 ] ][ 1 ] ) ); + + /// IMPORTANT: Check to make sure we're preserving the block type + if( srcData[ 0 ] > srcData[ 1 ] && color1 <= color2 ) + { + /// NOPE! We better flip the colors and flip all the color refs + /// in this block, to preserve block type + if( color1 == color2 ) + color1++; // This is a hack--our rounding may cause this, + // in which case we need to make SURE c1 > c2 + else + SWAPVARS( color1, color2, i ); + + srcData[ 2 ] ^= 0x5555; + srcData[ 3 ] ^= 0x5555; + } + else if( srcData[ 0 ] <= srcData[ 1 ] && color1 > color2 ) + { + SWAPVARS( color1, color2, i ); + + /// 1/2 block w/ alpha -- switch only first two + /// (watch carefully :) + srcData[ 2 ] ^= ( ( ~( srcData[ i ] >> 1 ) ) & 0x5555 ); + srcData[ 3 ] ^= ( ( ~( srcData[ i ] >> 1 ) ) & 0x5555 ); + + /// Spoiler for above: we shift the word right one bit, then + /// not the bits, then mask off the lower bits of the pairs + /// (which of course used to be the upper bits). Now any upper + /// bits that were 0 are now lower bits of 1, and everything + /// else 0's. This we then xor with the original value to + /// flip the lower bits of those pairs whose upper bits are 0. + /// Nifty, eh? + } + + /// Write back and go! +#if HS_BUILD_FOR_MAC + srcData[ 0 ] = ISwapWordOrder( color1 ); + srcData[ 1 ] = ISwapWordOrder( color2 ); +#else + srcData[ 0 ] = color1; + srcData[ 1 ] = color2; +#endif + srcData += blockSize; + } + + return true; /// We handled this bitmap +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTSoftwareCodec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTSoftwareCodec.h new file mode 100644 index 00000000..d3edacd9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsDXTSoftwareCodec.h @@ -0,0 +1,142 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __HSDXTSOFTWARECODEC_H +#define __HSDXTSOFTWARECODEC_H + +#include "HeadSpin.h" +#include "hsCodec.h" + +class plMipmap; + +class hsDXTSoftwareCodec : public hsCodec +{ +private: + hsDXTSoftwareCodec(); +public: + ~hsDXTSoftwareCodec(); + static hsDXTSoftwareCodec& Instance(); + + static void Init() { fRegistered = Register(); } + + plMipmap *CreateCompressedMipmap(plMipmap *uncompressed); + + // Uncompresses the given source into a new destination mipmap + plMipmap *CreateUncompressedMipmap( plMipmap *compressed, UInt8 flags = 0 ); + + // Colorize a compressed mipmap + hsBool ColorizeCompMipmap( plMipmap *bMap, const UInt8 *colorMask ); + +private: + enum { + kFourColorEncoding, + kThreeColorEncoding + }; + + void CompressMipmapLevel( plMipmap *uncompressed, plMipmap *compressed ); + + UInt16 BlendColors16(UInt16 weight1, UInt16 color1, UInt16 weight2, UInt16 color2); + hsRGBAColor32 BlendColors32(UInt32 weight1, hsRGBAColor32 color1, UInt32 weight2, hsRGBAColor32 color2); + Int32 ColorDistanceARGBSquared(hsRGBAColor32 color1, hsRGBAColor32 color2); + UInt16 Color32To16(hsRGBAColor32 color); + + // Calculates the DXT format based on a mipmap + UInt8 ICalcCompressedFormat( plMipmap *bMap ); + + // Copy over a block from a mipmap level to a destination mipmap, converting if necessary + void IXlateColorBlock( plMipmap *destBMap, UInt32 *srcBlock, UInt8 flags = 0 ); + + // Creates an uncompressed mipmap with the settings to match + plMipmap *ICreateUncompressedMipmap( plMipmap *compressed, UInt8 flags ); + + // Dispatcher for all the decompression functions + void inline UncompressMipmap( plMipmap *uncompressed, plMipmap *compressed, + UInt8 flags = 0 ); + + // Decompresses a DXT5 compressed mipmap into a RGB4444 mipmap + void IUncompressMipmapDXT5To16( plMipmap *destBMap, plMipmap *srcBMap ); + // Decompresses a DXT5 compressed mipmap into a RGB4444 reversed mipmap + void IUncompressMipmapDXT5To16Weird( plMipmap *destBMap, plMipmap *srcBMap ); + // Decompresses a DXT5 compressed mipmap into a RGB8888 mipmap + void IUncompressMipmapDXT5To32( plMipmap *destBMap, plMipmap *srcBMap ); + + // Decompresses a DXT1 compressed mipmap into a RGB1555 mipmap + void IUncompressMipmapDXT1To16( plMipmap *destBMap, plMipmap *srcBMap ); + // Decompresses a DXT1 compressed mipmap into a RGB5551 mipmap + void IUncompressMipmapDXT1To16Weird( plMipmap *destBMap, plMipmap *srcBMap ); + // Decompresses a DXT1 compressed mipmap into a RGB8888 mipmap + void IUncompressMipmapDXT1To32( plMipmap *destBMap, plMipmap *srcBMap ); + + // Decompresses a DXT1 compressed mipmap into an intensity map + void IUncompressMipmapDXT1ToInten( plMipmap *destBMap, plMipmap *srcBMap ); + // Decompresses a DXT5 compressed mipmap into an alpha-intensity map + void IUncompressMipmapDXT5ToAInten( plMipmap *destBMap, plMipmap *srcBMap ); + + // Mixes two RGB8888 colors equally + UInt32 inline IMixEqualRGB32( UInt32 color1, UInt32 color2 ); + // Mixes two-thirds of the first RGB8888 color and one-third of the second + UInt32 inline IMixTwoThirdsRGB32( UInt32 twoThirds, UInt32 oneThird ); + + // Mixes two RGB1555 colors equally + UInt16 inline IMixEqualRGB1555( UInt16 color1, UInt16 color2 ); + // Mixes two-thirds of the first RGB1555 color and one-third of the second + UInt16 inline IMixTwoThirdsRGB1555( UInt16 twoThirds, UInt16 oneThird ); + + // Mixes two RGB5551 colors equally + UInt16 inline IMixEqualRGB5551( UInt16 color1, UInt16 color2 ); + // Mixes two-thirds of the first RGB5551 color and one-third of the second + UInt16 inline IMixTwoThirdsRGB5551( UInt16 twoThirds, UInt16 oneThird ); + + // Mixes two RGB4444 colors equally + UInt16 inline IMixEqualRGB4444( UInt16 color1, UInt16 color2 ); + // Mixes two-thirds of the first RGB4444 color and one-third of the second + UInt16 inline IMixTwoThirdsRGB4444( UInt16 twoThirds, UInt16 oneThird ); + + // Mixes two intensity values equally + UInt8 inline IMixEqualInten( UInt8 color1, UInt8 color2 ); + // Mixes two-thirds of the first intensity and one-third of the second + UInt8 inline IMixTwoThirdsInten( UInt8 twoThirds, UInt8 oneThird ); + + // Converts a color from RGB565 to RGB8888 format, with alpha=0 + UInt32 inline IRGB16To32Bit( UInt16 color ); + // Converts a color from RGB565 to RGB4444 format, with alpha=0 + UInt16 inline IRGB565To4444( UInt16 color ); + // Converts a color from RGB565 to RGB1555 format, with alpha=0 + UInt16 inline IRGB565To1555( UInt16 color ); + // Converts a color from RGB565 to RGB5551 format, with alpha=0 + UInt16 inline IRGB565To5551( UInt16 color ); + // Converts a color from RGB565 to RGB4444 reversed format, with alpha=0 + UInt16 inline IRGB565To4444Rev( UInt16 color ); + + // Swaps the bytes in a doubleword + UInt32 inline ISwapDwordOrder( UInt32 color ); + // Swaps the bytes in a word + UInt16 inline ISwapWordOrder( UInt16 color ); + + static hsBool Register(); + static hsBool fRegistered; +}; + +#endif // __HSDXTSOFTWARECODEC_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsRect.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsRect.h new file mode 100644 index 00000000..2d1eb47c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/hsRect.h @@ -0,0 +1,151 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsRect_Defined +#define hsRect_Defined + +#include "hsPoint2.h" + +#if HS_BUILD_FOR_MAC + // This guy disables MetroWerks' desire to only include a file once, which obviously gets + // in the way of our little HS_RECT.inc trick + #pragma once off +#endif + +#define HS_RECT_NAME hsIntRect +#define HS_RECT_POINT hsIntPoint2 +#define HS_RECT_TYPE Int32 +#define HS_RECT_EXTEND 1 +#include "HS_RECT.inc" + +#if HS_BUILD_FOR_MAC + Rect* ToRect(Rect* r) const + { + r->left = (Int16)this->fLeft; + r->top = (Int16)this->fTop; + r->right = (Int16)this->fRight; + r->bottom = (Int16)this->fBottom; + return r; + } + hsIntRect* Set(const Rect* r) + { + return this->Set(r->left, r->top, r->right, r->bottom); + } +#endif +#ifdef _WINDOWS_ + RECT* ToRECT(RECT* r) const + { + r->left = this->fLeft; + r->top = this->fTop; + r->right = this->fRight; + r->bottom = this->fBottom; + return r; + } + hsIntRect* Set(const RECT* r) + { + return this->Set(r->left, r->top, r->right, r->bottom); + } +#endif +}; + +#define HS_RECT_NAME hsFixedRect +#define HS_RECT_POINT hsFixedPoint2 +#define HS_RECT_TYPE hsFixed +#define HS_RECT_EXTEND 1 +#include "HS_RECT.inc" + + hsFixedRect* Set(const hsIntRect* src) + { + this->fLeft = hsIntToFixed(src->fLeft); + this->fTop = hsIntToFixed(src->fTop); + this->fRight = hsIntToFixed(src->fRight); + this->fBottom = hsIntToFixed(src->fBottom); + return this; + } + + hsFixed CenterX(void) const { return (fLeft + fRight) >> 1; } + hsFixed CenterY(void) const { return (fTop + fBottom) >> 1; } + hsFixedPoint2* Center(hsFixedPoint2* center) const + { + (void)center->Set(this->CenterX(), this->CenterY()); + return center; + } + hsIntRect* Truncate(hsIntRect* dst) const + { + return (hsIntRect*)dst->Set( hsFixedToInt(fLeft), hsFixedToInt(fTop), + hsFixedToInt(fRight), hsFixedToInt(fBottom)); + } + hsIntRect* Round(hsIntRect* dst) const + { + return (hsIntRect*)dst->Set( hsFixedRound(fLeft), hsFixedRound(fTop), + hsFixedRound(fRight), hsFixedRound(fBottom)); + } + hsIntRect* RoundOut(hsIntRect* dst) const + { + return (hsIntRect*)dst->Set( hsFixedToFloorInt(fLeft), + hsFixedToFloorInt(fTop), + hsFixedToCeilingInt(fRight), + hsFixedToCeilingInt(fBottom)); + } +}; + +#if HS_SCALAR_IS_FLOAT + #define HS_RECT_NAME hsFloatRect + #define HS_RECT_POINT hsFloatPoint2 + #define HS_RECT_TYPE float + #define HS_RECT_EXTEND 1 + #include "HS_RECT.inc" + + hsFloatRect* Set(const hsIntRect* src) + { + this->fLeft = float(src->fLeft); + this->fTop = float(src->fTop); + this->fRight = float(src->fRight); + this->fBottom = float(src->fBottom); + return this; + } + + float CenterX(void) const { return (fLeft + fRight) / float(2); } + float CenterY(void) const { return (fTop + fBottom) / float(2); } + hsFloatPoint2* Center(hsFloatPoint2* center) const + { + (void)center->Set(this->CenterX(), this->CenterY()); + return center; + } + float Area() const { return this->Width() * this->Height(); } + + hsIntRect* Round(hsIntRect* r) const; + hsIntRect* RoundOut(hsIntRect* r) const; + hsIntRect* Truncate(hsIntRect* r) const; + }; +#endif + +#if HS_SCALAR_IS_FIXED + typedef hsFixedRect hsRect; +#else + typedef hsFloatRect hsRect; +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/notes.txt b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/notes.txt new file mode 100644 index 00000000..ab6efca9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/notes.txt @@ -0,0 +1,2 @@ + +Moving hsRect here since it doesn't have a lot to do with transforms. mf \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plAVIWriter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plAVIWriter.cpp new file mode 100644 index 00000000..e0148f26 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plAVIWriter.cpp @@ -0,0 +1,294 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAVIWriter.h" + +#include "hsTypes.h" + +#include "hsWindows.h" +#include + +#include "hsTimer.h" +#include "plMipmap.h" +#include "../plMessage/plRenderMsg.h" +#include "plPipeline.h" +#include "../pnDispatch/plDispatch.h" +#include "../pnKeyedObject/plFixedKey.h" + +bool plAVIWriter::fInitialized = false; + +class plAVIWriterImp : public plAVIWriter +{ +protected: + PAVIFILE fFileHandle; + PAVISTREAM fStreamHandle; + PAVISTREAM fCompressedHandle; + BITMAPINFOHEADER fBitmapInfo; + + hsBool fOldRealTime; + hsScalar fOldFrameTimeInc; + + double fStartTime; + + void IFillStreamInfo(AVISTREAMINFO* inf, plPipeline* pipeline); + void IFillBitmapInfo(BITMAPINFOHEADER* inf, plPipeline* pipeline); + + bool ICaptureFrame(plPipeline* pipeline); + +public: + plAVIWriterImp(); + virtual ~plAVIWriterImp(); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Shutdown(); + + virtual bool Open(const char* fileName, plPipeline* pipeline); + virtual void Close(); +}; + +plAVIWriter::~plAVIWriter() +{ +} + +plAVIWriter& plAVIWriter::Instance() +{ + static plAVIWriterImp theInstance; + + if (!fInitialized) + { + theInstance.RegisterAs(kAVIWriter_KEY); + fInitialized = true; + } + + return theInstance; +} + +//////////////////////////////////////////////////////////////////////////////// + +plAVIWriterImp::plAVIWriterImp() : + fStartTime(0), + fOldRealTime(false), + fStreamHandle(nil), + fCompressedHandle(nil), + fFileHandle(nil) +{ + AVIFileInit(); +} + +plAVIWriterImp::~plAVIWriterImp() +{ +} + +void plAVIWriterImp::Shutdown() +{ + Close(); + UnRegisterAs(kAVIWriter_KEY); + SetKey(nil); +} + + + +#include "plProfile.h" +plProfile_CreateTimer("AviCapture", "RenderSetup", AviCapture); + +hsBool plAVIWriterImp::MsgReceive(plMessage* msg) +{ + + plRenderMsg* renderMsg = plRenderMsg::ConvertNoRef(msg); + if (renderMsg) + { + plProfile_BeginTiming(AviCapture); + + ICaptureFrame(renderMsg->Pipeline()); + plProfile_EndTiming(AviCapture); + + } + + return hsKeyedObject::MsgReceive(msg); +} + +static const int kFramesPerSec = 30; + +bool plAVIWriterImp::Open(const char* fileName, plPipeline* pipeline) +{ + // Already writing, fail + if (fStreamHandle) + return false; + + fStartTime = hsTimer::GetSysSeconds(); + + // If we're running in real time, set to frame time + fOldRealTime = hsTimer::IsRealTime(); + if (fOldRealTime) + { + hsTimer::SetRealTime(false); + hsTimer::SetFrameTimeInc(1.f / kFramesPerSec); + } + + // Open AVI file + HRESULT err; + err = AVIFileOpen( &fFileHandle, // returned file pointer + fileName, // file name + OF_WRITE | OF_CREATE, // mode to open file with + NULL); // use handler determined + hsAssert(err == AVIERR_OK, "Error creating AVI file in plAVIWriter::Open"); + if (err != AVIERR_OK) + { + Close(); + return false; + } + + AVISTREAMINFO streamInfo; + IFillStreamInfo(&streamInfo, pipeline); + + // Create a video stream in the file + err = AVIFileCreateStream( fFileHandle, // file pointer + &fStreamHandle, // returned stream pointer + &streamInfo ); // stream header + hsAssert(err == AVIERR_OK, "Error creating video stream in plAVIWriter::Open"); + if (err != AVIERR_OK) + { + Close(); + return false; + } + + do + { + AVICOMPRESSOPTIONS opts; + AVICOMPRESSOPTIONS FAR * aopts[1] = {&opts}; + memset(&opts, 0, sizeof(opts)); + + BOOL bErr = AVISaveOptions(NULL, ICMF_CHOOSE_DATARATE, 1, &fStreamHandle, (LPAVICOMPRESSOPTIONS FAR*)&aopts); + hsAssert(bErr, "Error saving stream options in plAVIWriter::Open"); + if (!bErr) + { + Close(); + return false; + } + + err = AVIMakeCompressedStream(&fCompressedHandle, fStreamHandle, &opts, NULL); + hsAssert(err == AVIERR_OK, "Error creating compressed stream in plAVIWriter::Open"); + if (err != AVIERR_OK) + { + Close(); + return false; + } + + IFillBitmapInfo(&fBitmapInfo, pipeline); + err = AVIStreamSetFormat( fCompressedHandle, 0, + &fBitmapInfo, // stream format + fBitmapInfo.biSize); + } while (err != AVIERR_OK && + hsMessageBox("Codec unavailable, try again?", "AVI Writer", hsMessageBoxYesNo) == hsMBoxYes); + + if (err != AVIERR_OK) + { + Close(); + return false; + } + + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + + return true; +} + +void plAVIWriterImp::Close() +{ + plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); + + hsTimer::SetRealTime(fOldRealTime); + + if (fStreamHandle) + { + AVIStreamClose(fStreamHandle); + fStreamHandle = nil; + } + + if (fCompressedHandle) + { + AVIStreamClose(fCompressedHandle); + fCompressedHandle = nil; + } + + if (fFileHandle) + { + AVIFileClose(fFileHandle); + fFileHandle = nil; + } + + AVIFileExit(); +} + +void plAVIWriterImp::IFillStreamInfo(AVISTREAMINFO* inf, plPipeline* pipeline) +{ + memset(inf, 0, sizeof(AVISTREAMINFO)); + inf->fccType = streamtypeVIDEO; + inf->fccHandler = 0; + inf->dwScale = 1; + inf->dwRate = kFramesPerSec; + + SetRect(&inf->rcFrame, + 0,0, + pipeline->Width(), + pipeline->Height()); +} + +void plAVIWriterImp::IFillBitmapInfo(BITMAPINFOHEADER* inf, plPipeline* pipeline) +{ + memset(inf,0,sizeof(BITMAPINFOHEADER)); + inf->biSize = sizeof(BITMAPINFOHEADER); + inf->biPlanes = 1; + inf->biBitCount = 32; + inf->biCompression = BI_RGB; + inf->biSizeImage = 0; + inf->biXPelsPerMeter = 0; + inf->biYPelsPerMeter = 0; + inf->biClrUsed = 0; + inf->biClrImportant = 0; + inf->biWidth = pipeline->Width(); + inf->biHeight = pipeline->Height(); +} + +bool plAVIWriterImp::ICaptureFrame(plPipeline* pipeline) +{ + plMipmap frame; + pipeline->CaptureScreen(&frame, true); + + double time = hsTimer::GetSysSeconds() - fStartTime; + time *= kFramesPerSec; + + HRESULT err; + err = AVIStreamWrite( fCompressedHandle, + int(time), + 1, + (LPBYTE)frame.GetAddr32(0,0), + frame.GetTotalSize(), + AVIIF_KEYFRAME, + NULL, + NULL); + + return (err == AVIERR_OK); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plAVIWriter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plAVIWriter.h new file mode 100644 index 00000000..95e21392 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plAVIWriter.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAVIWriter_h_inc +#define plAVIWriter_h_inc + +#include "../pnKeyedObject/hsKeyedObject.h" + +class plPipeline; + +class plAVIWriter : public hsKeyedObject +{ +protected: + static bool fInitialized; + + virtual ~plAVIWriter(); + +public: + static plAVIWriter& Instance(); + + // If IsInitialized returns true, you need to call Shutdown before clearing + // the registry (dang key). + static bool IsInitialized() { return fInitialized; } + virtual void Shutdown()=0; + + CLASSNAME_REGISTER(plAVIWriter); + GETINTERFACE_ANY(plAVIWriter, hsKeyedObject); + + virtual bool Open(const char* fileName, plPipeline* pipeline)=0; + virtual void Close()=0; +}; + +#endif // plAVIWriter_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBitmap.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBitmap.cpp new file mode 100644 index 00000000..b9dc29a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBitmap.cpp @@ -0,0 +1,164 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plBitmap Class Functions // +// Base bitmap class for all the types of bitmaps (mipmaps, cubic envmaps, // +// etc. // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 6.7.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plBitmap.h" + +#include "hsResMgr.h" +#include "hsStream.h" +#include "../pnKeyedObject/plKey.h" +#include "../plPipeline/hsGDeviceRef.h" + + +//// Static Members /////////////////////////////////////////////////////////// + +UInt8 plBitmap::fGlobalNumLevelsToChop = 0; + + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plBitmap::plBitmap() +{ + fPixelSize = 0; + fSpace = kNoSpace; + fFlags = 0; + fCompressionType = kUncompressed; + fUncompressedInfo.fType = UncompressedInfo::kRGB8888; + fDeviceRef = nil; + fLowModifiedTime = fHighModifiedTime = 0; +} + +plBitmap::~plBitmap() +{ + if( fDeviceRef != nil ) + hsRefCnt_SafeUnRef( fDeviceRef ); +} + +bool plBitmap::IsSameModifiedTime(UInt32 lowTime, UInt32 highTime) +{ + return (fLowModifiedTime == lowTime && fHighModifiedTime == highTime); +} + +void plBitmap::SetModifiedTime(UInt32 lowTime, UInt32 highTime) +{ + fLowModifiedTime = lowTime; + fHighModifiedTime = highTime; +} + +//// Read ///////////////////////////////////////////////////////////////////// + +static UInt8 sBitmapVersion = 2; + +UInt32 plBitmap::Read( hsStream *s ) +{ + UInt8 version = s->ReadByte(); + UInt32 read = 6; + + + hsAssert( version == sBitmapVersion, "Invalid bitamp version on Read()" ); + + fPixelSize = s->ReadByte(); + fSpace = s->ReadByte(); + fFlags = s->ReadSwap16(); + fCompressionType = s->ReadByte(); + + if(( fCompressionType == kUncompressed )||( fCompressionType == kJPEGCompression )) + { + fUncompressedInfo.fType = s->ReadByte(); + read++; + } + else + { + fDirectXInfo.fBlockSize = s->ReadByte(); + fDirectXInfo.fCompressionType = s->ReadByte(); + read += 2; + } + + fLowModifiedTime = s->ReadSwap32(); + fHighModifiedTime = s->ReadSwap32(); + + return read; +} + +//// Write //////////////////////////////////////////////////////////////////// + +UInt32 plBitmap::Write( hsStream *s ) +{ + UInt32 written = 6; + + + s->WriteByte( sBitmapVersion ); + + s->WriteByte( fPixelSize ); + s->WriteByte( fSpace ); + s->WriteSwap16( fFlags ); + s->WriteByte( fCompressionType ); + + if(( fCompressionType == kUncompressed )||(fCompressionType == kJPEGCompression )) + { + s->WriteByte( fUncompressedInfo.fType ); + written++; + } + else + { + s->WriteByte( fDirectXInfo.fBlockSize ); + s->WriteByte( fDirectXInfo.fCompressionType ); + written += 2; + } + + s->WriteSwap32(fLowModifiedTime); + s->WriteSwap32(fHighModifiedTime); + + return written; +} + +//// SetDeviceRef ///////////////////////////////////////////////////////////// + +void plBitmap::SetDeviceRef( hsGDeviceRef *const devRef ) +{ + if( fDeviceRef == devRef ) + return; + + hsRefCnt_SafeAssign( fDeviceRef, devRef ); +} + +void plBitmap::MakeDirty() +{ + if( fDeviceRef ) + fDeviceRef->SetDirty(true); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBitmap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBitmap.h new file mode 100644 index 00000000..17e8971a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBitmap.h @@ -0,0 +1,188 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plBitmap Class Header // +// Base bitmap class for all the types of bitmaps (mipmaps, cubic envmaps, // +// etc. // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 6.7.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plBitmap_h +#define _plBitmap_h + +#include "../pnKeyedObject/hsKeyedObject.h" + +class hsResMgr; +class plFilterMask; +class hsGDeviceRef; + +//// Class Definition ///////////////////////////////////////////////////////// + +class plBitmap : public hsKeyedObject +{ + public: + + //// Public Flags //// + + enum { + kNoSpace, + kDirectSpace, + kGraySpace, + kIndexSpace + }; + + enum Flags { + kNoFlag = 0x0000, + kAlphaChannelFlag = 0x0001, + kAlphaBitFlag = 0x0002, + kBumpEnvMap = 0x0004, + kForce32Bit = 0x0008, + kDontThrowAwayImage = 0x0010, // We can delete the image, but the pipeline can't + kForceOneMipLevel = 0x0020, + kNoMaxSize = 0x0040, + kIntensityMap = 0x0080, + kHalfSize = 0x0100, + kUserOwnsBitmap = 0x0200, + kForceRewrite = 0x0400, + kForceNonCompressed = 0x0800, + // For renderTargets: + kIsTexture = 0x1000, + kIsOffscreen = 0x2000, + kMainScreen = 0x0000, // Exclusive, i.e. no renderTarget flags + kIsProjected = 0x4000, + kIsOrtho = 0x8000 + }; + + //// Public Data ///// + + enum // Compression format + { + kUncompressed = 0x0, + kDirectXCompression = 0x1, + kJPEGCompression = 0x2 + }; + + struct DirectXInfo + { + enum // Compression type + { + kError = 0x0, + kDXT1 = 0x1, + //kDXT2 = 0x2, + //kDXT3 = 0x3, + //kDXT4 = 0x4, + kDXT5 = 0x5 + }; + + UInt8 fCompressionType; + UInt8 fBlockSize; // In bytes + }; + + struct UncompressedInfo + { + enum + { + kRGB8888 = 0x00, /// 32-bit 8888 ARGB format + kRGB4444 = 0x01, /// 16-bit 4444 ARGB format + kRGB1555 = 0x02, /// 16-bit 555 RGB format w/ alpha bit + kInten8 = 0x03, /// 8-bit intensity channel (monochrome) + kAInten88 = 0x04 /// 8-bit intensity channel w/ 8-bit alpha + }; + + UInt8 fType; + }; + + //// Public Data ///// + + UInt8 fCompressionType; + union + { + DirectXInfo fDirectXInfo; + UncompressedInfo fUncompressedInfo; + }; + + + //// Public Members //// + + plBitmap(); + virtual ~plBitmap(); + + CLASSNAME_REGISTER( plBitmap ); + GETINTERFACE_ANY( plBitmap, hsKeyedObject ); + + // Get the total size in bytes + virtual UInt32 GetTotalSize( void ) const = 0; + + // Read and write + virtual void Read( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Read( s, mgr ); this->Read( s ); } + virtual void Write( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Write( s, mgr ); this->Write( s ); } + + UInt16 GetFlags( void ) const { return fFlags; } + void SetFlags( UInt16 flags ) { fFlags = flags; } + + UInt8 GetPixelSize( void ) const { return fPixelSize; } + + hsBool IsCompressed( void ) const { return ( fCompressionType == kDirectXCompression ); } + + virtual void MakeDirty(); + virtual hsGDeviceRef *GetDeviceRef( void ) const { return fDeviceRef; } + virtual void SetDeviceRef( hsGDeviceRef *const devRef ); + + static void SetGlobalLevelChopCount( UInt8 count ) { fGlobalNumLevelsToChop = count; } + static UInt8 GetGlobalLevelChopCount() { return fGlobalNumLevelsToChop; } + + // Compares and sets the modified time for the source texture + bool IsSameModifiedTime(UInt32 lowTime, UInt32 highTime); + void SetModifiedTime(UInt32 lowTime, UInt32 highTime); + + protected: + + //// Protected Members //// + + UInt8 fPixelSize; // 1, 2, 4, 8, 16, (24), 32, 64 + UInt8 fSpace; // no, direct, gray, index + UInt16 fFlags; // alphachannel | alphabit + + mutable hsGDeviceRef *fDeviceRef; + + static UInt8 fGlobalNumLevelsToChop; + + // The modification time of the source texture. + // Used to determine if we can reuse an already converted copy. + UInt32 fLowModifiedTime; + UInt32 fHighModifiedTime; + + virtual UInt32 Read( hsStream *s ); + virtual UInt32 Write( hsStream *s ); +}; + +#endif // _plBitmap_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBumpMapGen.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBumpMapGen.cpp new file mode 100644 index 00000000..29133009 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBumpMapGen.cpp @@ -0,0 +1,298 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" + +#include "plBumpMapGen.h" + +#include "plMipmap.h" + +#include "hsFastMath.h" + +#include "hsCodecManager.h" + +plMipmap* plBumpMapGen::MakeCompatibleBlank(const plMipmap* src) +{ + return TRACKED_NEW plMipmap(src->GetWidth(), src->GetHeight(), plMipmap::kARGB32Config, 1, plMipmap::kUncompressed, plMipmap::UncompressedInfo::kRGB8888); +} + +plMipmap* plBumpMapGen::TwosCompToBias(plMipmap* dst) +{ + UInt8* pDst = (UInt8*)dst->GetAddr32(0, 0); + + const int width = dst->GetWidth(); + const int height = dst->GetHeight(); + int i; + int j; + for( j = 0; j < height; j++ ) + { + for( i = 0; i < width; i++ ) + { + *pDst++ += 128; + *pDst++ += 128; + *pDst++ += 128; + pDst++; + } + } + + return dst; +} + +plMipmap* plBumpMapGen::QikBumpMap(plMipmap* dst, const plMipmap* origSrc, UInt32 mask, UInt32 flags) +{ + const plMipmap* src = origSrc; + if( !dst ) + { + dst = MakeCompatibleBlank(src); + } + else if( (src->GetWidth() != dst->GetWidth()) || (src->GetHeight() != dst->GetHeight()) ) + { + plMipmap* newSrc = src->Clone(); + // Note here that ResizeNicely currently does a point sample if scaling up (and about + // as expensive a point sample as possible without using transcendental functions). + // This might be correctable by calling plMipmap::Filter after the upscale. Or I + // could just assert that dst dimensions match src dimensions. + newSrc->ResizeNicely((UInt16)(dst->GetWidth()), (UInt16)(dst->GetHeight()), plMipmap::kDefaultFilter); + + src = newSrc; + } + if( src->IsCompressed() ) + { + plMipmap* newSrc = hsCodecManager::Instance().CreateUncompressedMipmap(const_cast(src), hsCodecManager::k32BitDepth); + src = newSrc; + } + dst->SetCurrLevel(0); + + const Int32 divis = ((mask >> 0) & 0xff) + +((mask >> 8) & 0xff) + +((mask >> 16) & 0xff); + + const int width = src->GetWidth(); + const int height = src->GetHeight(); + const int stride = src->GetRowBytes(); // Should be width * 4; + + const UInt32 alphaOr = flags & kScaleHgtByAlpha ? 0 : 0xff; + + UInt32* pDst = dst->GetAddr32(0, 0); + UInt32* pBase = src->GetAddr32(0, 0); + UInt32* pSrc = pBase; + int i; + int j; + for( j = 0; j < height; j++ ) + { + UInt32* pUp = j ? pSrc - width : pBase; + UInt32* pDn = j < height-1 ? pSrc + width : pBase; + for( i = 0; i < width; i++ ) + { + UInt32* pLf = i ? pSrc - 1 : pSrc + width-1; + UInt32* pRt = i < width-1 ? pSrc + 1 : pSrc - width + 1; + + UInt32 up = (((*pUp & mask) >> 0) & 0xff) + + (((*pUp & mask) >> 8) & 0xff) + + (((*pUp & mask) >> 16) & 0xff); + UInt32 dn = (((*pDn & mask) >> 0) & 0xff) + + (((*pDn & mask) >> 8) & 0xff) + + (((*pDn & mask) >> 16) & 0xff); + + UInt32 rt = (((*pRt & mask) >> 0) & 0xff) + + (((*pRt & mask) >> 8) & 0xff) + + (((*pRt & mask) >> 16) & 0xff); + UInt32 lf = (((*pLf & mask) >> 0) & 0xff) + + (((*pLf & mask) >> 8) & 0xff) + + (((*pLf & mask) >> 16) & 0xff); + + UInt32 hgt = (((*pSrc & mask) >> 0) & 0xff) + + (((*pSrc & mask) >> 8) & 0xff) + + (((*pSrc & mask) >> 16) & 0xff); + + if( hgt ) + hgt *= 1; + + // Multiply by alpha, divide by 255 (so *= float(alpha/255)) + // If we aren't scaling by alpha, we just force alpha to be 255. + hgt *= ((*pSrc >> 24) & 0xff) | alphaOr; // scale by alpha + hgt /= 255; + + // divis has an implicit 255. For example, all three channels + // are on, so divis = 0xff+0xff+0xff = 3*255. + // So we muliply by 255 and divide by divis, so in this example, + // that means divide by 3. + Int32 delUpDn = dn - up; + delUpDn *= 255; + delUpDn /= divis; + + Int32 delRtLf = lf - rt; + delRtLf *= 255; + delRtLf /= divis; + + hgt *= 255; + hgt /= divis; + +// hgt = 0xff; + + *pDst = ((delRtLf & 0xff) << 16) + |((delUpDn & 0xff) << 8) + |((0xff) << 0) + |((hgt & 0xff) << 24); + + if( delRtLf ) + hgt *= 1; + if( delUpDn ) + hgt *= 1; + + + pUp++; + pDn++; + pSrc++; + pDst++; + } + } + + if( flags & kBias ) + TwosCompToBias(dst); + + if( origSrc != src ) + delete src; + + return dst; +} + +plMipmap* plBumpMapGen::QikNormalMap(plMipmap* dst, const plMipmap* src, UInt32 mask, UInt32 flags, hsScalar smooth) +{ + dst = QikBumpMap(dst, src, mask, flags & ~kBias); + + const int width = src->GetWidth(); + const int height = src->GetHeight(); + + if( flags & kBubbleTest ) + { + Int8* pDst = (Int8*)dst->GetAddr32(0, 0); + + Int32 nZ = Int32(smooth * 255.99f); + + int i; + int j; + for( j = 0; j < height; j++ ) + { + for( i = 0; i < width; i++ ) + { + hsScalar x = hsScalar(i) / hsScalar(width-1) * 2.f - 1.f; + hsScalar y = hsScalar(j) / hsScalar(height-1) * 2.f - 1.f; + + hsScalar z = 1.f - x*x - y*y; + if( z > 0 ) + z = hsSquareRoot(z); + else + { + x = 0; + y = 0; + z = 1.f; + } + z *= smooth; + hsScalar invLen = hsFastMath::InvSqrt(x*x + y*y + z*z) * 127.00f; + + + pDst[2] = Int8(x * invLen); + pDst[1] = Int8(y * invLen); + pDst[0] = Int8(z * invLen); + + pDst += 4; + } + } + } + else + if( flags & kNormalize ) + { + Int8* pDst = (Int8*)dst->GetAddr32(0, 0); + + Int32 nZ = Int32(smooth * 127.00f); + + int i; + int j; + for( j = 0; j < height; j++ ) + { + for( i = 0; i < width; i++ ) + { + Int32 x = pDst[2]; + Int32 y = pDst[1]; + + if( x ) + x *= 1; + if( y ) + y *= 1; + + hsScalar invLen = hsFastMath::InvSqrt((hsScalar)(x*x + y*y + nZ*nZ)) * 127.0f; + pDst[2] = Int8(x * invLen); + pDst[1] = Int8(y * invLen); + pDst[0] = Int8(nZ * invLen); + + pDst += 4; + } + } + } + else if( smooth != 1.f ) + { + Int32 divis = 127; + Int32 nZ = 127; + if( (smooth > 1.f) ) + { + divis = (Int32)(smooth * 127); + } + else + { + nZ = UInt32(smooth * 127.5f); + } + Int8* pDst = (Int8*)dst->GetAddr32(0, 0); + + int i; + int j; + for( j = 0; j < height; j++ ) + { + for( i = 0; i < width; i++ ) + { + Int32 v; + *pDst = (Int8)nZ; + pDst++; + + v = *pDst * 127; + v /= divis; + *pDst = (Int8)(v & 0xff); + pDst++; + + v = *pDst * 127; + v /= divis; + *pDst = (Int8)(v & 0xff); + + pDst += 2; + } + } + } + + if( flags & kBias ) + TwosCompToBias(dst); + + return dst; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBumpMapGen.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBumpMapGen.h new file mode 100644 index 00000000..a7116de5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plBumpMapGen.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plBumpMapGen_inc +#define plBumpMapGen_inc + +class plMipmap; + +class plBumpMapGen +{ +public: + enum { + // output signed values with 0=>-1 and 255=>1, 127=>0, default is 2's complement, 0=>0, 255=>-1, 127=>1 + kBias = 0x1, + kMaximize = 0x2, + kNormalize = 0x4, + kScaleHgtByAlpha = 0x8, + kBubbleTest = 0x10 + }; + static plMipmap* QikBumpMap(plMipmap* dst, const plMipmap* src, UInt32 mask, UInt32 flags); + + static plMipmap* QikNormalMap(plMipmap* dst, const plMipmap* src, UInt32 mask, UInt32 flags, hsScalar smooth=1.f); // higher smooth means less bumpy, valid range [0..inf]. + + static plMipmap* TwosCompToBias(plMipmap* dst); + static plMipmap* MakeCompatibleBlank(const plMipmap* src); +}; + +#endif // plBumpMapGen_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plCubicEnvironmap.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plCubicEnvironmap.cpp new file mode 100644 index 00000000..91b65f1d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plCubicEnvironmap.cpp @@ -0,0 +1,179 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plCubicEnvironmap Class Functions // +// Derived bitmap class representing a collection of mipmaps to be used for // +// cubic environment mapping. // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 6.7.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plCubicEnvironmap.h" +#include "plMipmap.h" + + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plCubicEnvironmap::plCubicEnvironmap() +{ + int i; + + for( i = 0; i < 6; i++ ) + fFaces[ i ] = TRACKED_NEW plMipmap; + + fInitialized = false; +} + +plCubicEnvironmap::~plCubicEnvironmap() +{ + int i; + + for( i = 0; i < 6; i++ ) + delete fFaces[ i ]; +} + +//// GetTotalSize ///////////////////////////////////////////////////////////// +// Get the total size in bytes + +UInt32 plCubicEnvironmap::GetTotalSize( void ) const +{ + UInt32 size, i; + + + for( size = 0, i = 0; i < 6; i++ ) + { + hsAssert( fFaces[ i ] != nil, "Nil face in GetTotalSize()" ); + size += fFaces[ i ]->GetTotalSize(); + } + + return size; +} + +//// Read ///////////////////////////////////////////////////////////////////// + +UInt32 plCubicEnvironmap::Read( hsStream *s ) +{ + UInt32 i, tr = plBitmap::Read( s ); + + + for( i = 0; i < 6; i++ ) + tr += fFaces[ i ]->Read( s ); + + fInitialized = true; + + return tr; +} + +//// Write //////////////////////////////////////////////////////////////////// + +UInt32 plCubicEnvironmap::Write( hsStream *s ) +{ + UInt32 i, tw = plBitmap::Write( s ); + + + for( i = 0; i < 6; i++ ) + tw += fFaces[ i ]->Write( s ); + + return tw; +} + +//// CopyToFace /////////////////////////////////////////////////////////////// +// Export-only: Copy the mipmap given into a face + +void plCubicEnvironmap::CopyToFace( plMipmap *mip, UInt8 face ) +{ + hsAssert( face < 6, "Invalid face index in CopyToFace()" ); + hsAssert( fFaces[ face ] != nil, "nil face in CopyToFace()" ); + hsAssert( mip != nil, "nil source in CopyToFace()" ); + + + if( !fInitialized ) + { + // Make sure our stuff matches + fCompressionType = mip->fCompressionType; + if( fCompressionType != kDirectXCompression ) + fUncompressedInfo.fType = mip->fUncompressedInfo.fType; + else + { + fDirectXInfo.fBlockSize = mip->fDirectXInfo.fBlockSize; + fDirectXInfo.fCompressionType = mip->fDirectXInfo.fCompressionType; + } + + fPixelSize = mip->GetPixelSize(); + fSpace = kDirectSpace; + fFlags = mip->GetFlags(); + + fInitialized = true; + } + else + { + // Check to make sure their stuff matches + if( IsCompressed() != mip->IsCompressed() ) + { + hsAssert( false, "Compression types do not match in CopyToFace()" ); + return; + } + + if( !IsCompressed() ) + { + if( fUncompressedInfo.fType != mip->fUncompressedInfo.fType ) + { + hsAssert( false, "Compression formats do not match in CopyToFace()" ); + return; + } + } + else + { + if( fDirectXInfo.fBlockSize != mip->fDirectXInfo.fBlockSize || + fDirectXInfo.fCompressionType != mip->fDirectXInfo.fCompressionType ) + { + hsAssert( false, "Compression formats do not match in CopyToFace()" ); + return; + } + } + + if( fPixelSize != mip->GetPixelSize() ) + { + hsAssert( false, "Bitdepths do not match in CopyToFace()" ); + return; + } + + if( fFlags != mip->GetFlags() ) + { + hsAssert( false, "Flags do not match in CopyToFace()" ); + } + } + + // Copy the mipmap data + fFaces[ face ]->CopyFrom( mip ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plCubicEnvironmap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plCubicEnvironmap.h new file mode 100644 index 00000000..ab156dfa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plCubicEnvironmap.h @@ -0,0 +1,98 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plCubicEnvironmap Class Header // +// Derived bitmap class representing a collection of mipmaps to be used for // +// cubic environment mapping. // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 6.7.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plCubicEnvironmap_h +#define _plCubicEnvironmap_h + + +#include "plBitmap.h" + +class plMipmap; + +//// Class Definition ///////////////////////////////////////////////////////// + +class plCubicEnvironmap : public plBitmap +{ + public: + + //// Public Data //// + + enum Faces + { + kLeftFace = 0, + kRightFace, + kFrontFace, + kBackFace, + kTopFace, + kBottomFace + }; + + //// Public Members //// + + plCubicEnvironmap(); + virtual ~plCubicEnvironmap(); + + CLASSNAME_REGISTER( plCubicEnvironmap ); + GETINTERFACE_ANY( plCubicEnvironmap, plBitmap ); + + + // Get the total size in bytes + virtual UInt32 GetTotalSize( void ) const; + + virtual void Read( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Read( s, mgr ); this->Read( s ); } + virtual void Write( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Write( s, mgr ); this->Write( s ); } + + plMipmap *GetFace( UInt8 face ) const { return fFaces[ face ]; } + + // Export-only: Copy the mipmap given into a face + void CopyToFace( plMipmap *mip, UInt8 face ); + + protected: + + //// Protected Members //// + + plMipmap *fFaces[ 6 ]; + hsBool fInitialized; + + virtual UInt32 Read( hsStream *s ); + virtual UInt32 Write( hsStream *s ); + +}; + + +#endif // plCubicEnvironmap_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynSurfaceWriter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynSurfaceWriter.cpp new file mode 100644 index 00000000..545b1708 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynSurfaceWriter.cpp @@ -0,0 +1,1109 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDynSurfaceWriter Class Header // +// Abstract class wrapping around Windows GDI functionality for writing to // +// a generic RGBA surface. Allows us to create one writer per DTMap or a // +// single shared writer to conserve OS resources on 98/ME. // +// // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 10.28.2002 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsWindows.h" +#include "plDynSurfaceWriter.h" + +#include "plDynamicTextMap.h" +#include "hsExceptions.h" +#include "hsUtils.h" +#include "hsMatrix44.h" +#include "../plMessage/plDynamicTextMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "plProfile.h" +#include "../plStatusLog/plStatusLog.h" +#include "plWinFontCache.h" + + +//// plWinSurface Helper Functions //////////////////////////////////////////// + +#if HS_BUILD_FOR_WIN32 + +static UInt32 sNumDCsAllocated; +static UInt32 sNumBitmapsAllocated; + +plDynSurfaceWriter::plWinSurface::plWinSurface() +{ + fDC = nil; + fBitmap = nil; + fFont = nil; + fBits = nil; + fTextColor = RGB( 255, 255, 255 ); + fWidth = fHeight = 0; + + fSaveNum = 0; + fFontFace = nil; + fFontSize = 0; + fFontFlags = 0; + fFontAntiAliasRGB = false; + fFontBlockedRGB = false; +} + +plDynSurfaceWriter::plWinSurface::~plWinSurface() +{ + Release(); +} + +void plDynSurfaceWriter::plWinSurface::Allocate( UInt16 w, UInt16 h ) +{ + int i; + BITMAPINFO *bmi; + + + Release(); + + fWidth = w; + fHeight = h; + + /// Initialize a bitmap info struct to describe our surface + if( IBitsPerPixel() == 8 ) + bmi = (BITMAPINFO *)( TRACKED_NEW UInt8[ sizeof( BITMAPINFOHEADER ) + sizeof( RGBQUAD ) * 256 ] ); + else + bmi = TRACKED_NEW BITMAPINFO; + + memset( &bmi->bmiHeader, 0, sizeof( BITMAPINFOHEADER ) ); + bmi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); + bmi->bmiHeader.biWidth = (int)fWidth; + bmi->bmiHeader.biHeight = -(int)fHeight; + bmi->bmiHeader.biPlanes = 1; + bmi->bmiHeader.biCompression = BI_RGB; + bmi->bmiHeader.biBitCount = IBitsPerPixel(); + if( IBitsPerPixel() == 8 ) + { + // Set up map for grayscale bitmap + for( i = 0; i < 256; i++ ) + { + bmi->bmiColors[ i ].rgbRed = i; + bmi->bmiColors[ i ].rgbGreen = i; + bmi->bmiColors[ i ].rgbBlue = i; + bmi->bmiColors[ i ].rgbReserved = i; + } + } + + /// Create a screen-compatible DC + fDC = CreateCompatibleDC( nil ); + if( fDC == nil ) + { + char msg[ 256 ]; + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nil, GetLastError(), MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), msg, sizeof( msg ), nil ); + char *ret = strrchr( msg, '\n' ); + if( ret != nil ) + *ret = 0; + + plStatusLog::AddLineS( "pipeline.log", 0xffff0000, "Unable to allocate DC for dynamic text map (%s, %d DCs allocated already)", msg, sNumDCsAllocated ); + if (IBitsPerPixel() == 8 ) + delete [] bmi; + else + delete bmi; + return; + } + sNumDCsAllocated++; + + /// Create a bitmap using the DC and the bitmapInfo struct we filled out + fBitmap = CreateDIBSection( fDC, bmi, DIB_RGB_COLORS, (void **)&fBits, nil, 0 ); + if( fBitmap == nil ) + { + char msg[ 256 ]; + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nil, GetLastError(), MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), msg, sizeof( msg ), nil ); + char *ret = strrchr( msg, '\n' ); + if( ret != nil ) + *ret = 0; + + plStatusLog::AddLineS( "pipeline.log", 0xffff0000, "Unable to allocate RGB DIB section for dynamic text map (%s, %d bitmaps allocated already)", msg, sNumBitmapsAllocated ); + if (IBitsPerPixel() == 8 ) + delete [] bmi; + else + delete bmi; + return; + } + sNumBitmapsAllocated++; + + /// Set up some basic props + SetMapMode( fDC, MM_TEXT ); + SetBkMode( fDC, TRANSPARENT ); + SetTextAlign( fDC, TA_TOP | TA_LEFT ); + + fSaveNum = SaveDC( fDC ); + + SelectObject( fDC, fBitmap ); + + if (IBitsPerPixel() == 8 ) + delete [] bmi; + else + delete bmi; +} + +void plDynSurfaceWriter::plWinSurface::Release( void ) +{ + if( fBitmap != nil ) + sNumBitmapsAllocated--; + if( fDC != nil ) + sNumDCsAllocated--; + + if( fSaveNum != 0 ) + RestoreDC( fDC, fSaveNum ); + fSaveNum = 0; + + DeleteObject( fBitmap ); + DeleteDC( fDC ); + + fDC = nil; + fBitmap = nil; + fFont = nil; + fBits = nil; + fWidth = fHeight = 0; + + delete [] fFontFace; + fFontFace = nil; + fFontSize = 0; + fFontFlags = 0; + fFontAntiAliasRGB = false; + fFontBlockedRGB = false; +} + +hsBool plDynSurfaceWriter::plWinSurface::WillFit( UInt16 w, UInt16 h ) +{ + if( fWidth >= w && fHeight >= h ) + return true; + return false; +} + +static int SafeStrCmp( const char *str1, const char *str2 ) +{ + if( str1 == nil && str2 == nil ) + return -1; + if( str1 != nil && str2 != nil ) + return strcmp( str1, str2 ); + return -1; +} + +hsBool plDynSurfaceWriter::plWinSurface::FontMatches( const char *face, UInt16 size, UInt8 flags, hsBool aaRGB ) +{ + if( SafeStrCmp( face, fFontFace ) == 0 && fFontSize == size && + fFontFlags == flags && fFontAntiAliasRGB == aaRGB ) + return true; + + return false; +} + +void plDynSurfaceWriter::plWinSurface::SetFont( const char *face, UInt16 size, UInt8 flags, hsBool aaRGB ) +{ + delete [] fFontFace; + fFontFace = ( face != nil ) ? hsStrcpy( face ) : nil; + fFontSize = size; + fFontFlags = flags; + fFontAntiAliasRGB = aaRGB; + + bool hadAFont = false; + if( fFont != nil ) + { + hadAFont = true; + plWinFontCache::GetInstance().FreeFont( fFont ); + fFont = nil; + } + + if( face == nil ) + return; + + bool bold = ( fFontFlags & plDynSurfaceWriter::kFontBold ) ? true : false; + bool italic = ( fFontFlags & plDynSurfaceWriter::kFontItalic ) ? true : false; + + int nHeight = -MulDiv( size, GetDeviceCaps( fDC, LOGPIXELSY ), 72 ); + fFont = plWinFontCache::GetInstance().GetMeAFont( face, nHeight, bold ? FW_BOLD : FW_NORMAL, italic, + fFontAntiAliasRGB ? ANTIALIASED_QUALITY : DEFAULT_QUALITY ); + if( fFont == nil && fFontAntiAliasRGB ) + { + static bool warnedCantAntiAlias = false; + + // Creation of font failed; could be that we can't do anti-aliasing? Try not doing it... + if( !warnedCantAntiAlias ) + { + plStatusLog::AddLineS( "pipeline.log", "WARNING: Cannot allocate anti-aliased font. Falling back to non-anti-aliased. This will be the only warning" ); + warnedCantAntiAlias = true; + } + + fFont = plWinFontCache::GetInstance().GetMeAFont( face, nHeight, bold ? FW_BOLD : FW_NORMAL, italic, + fFontAntiAliasRGB ? ANTIALIASED_QUALITY : DEFAULT_QUALITY ); + } + + if( fFont == nil ) + { + hsAssert( false, "Cannot create Windows font for plDynSurfaceWriter" ); + plStatusLog::AddLineS( "pipeline.log", "ERROR: Cannot allocate font for RGB surface! (face: %s, size: %d %s %s)", face, nHeight, bold ? "bold" : "", italic ? "italic" : "" ); + + delete [] fFontFace; + fFontFace = nil; + fFontSize = 0; + return; + } + + if( SelectObject( fDC, fFont ) == nil && hadAFont ) + { + char msg[ 256 ]; + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nil, GetLastError(), MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), msg, sizeof( msg ), nil ); + char *ret = strrchr( msg, '\n' ); + if( ret != nil ) + *ret = 0; + + plStatusLog::AddLineS( "pipeline.log", 0xffff0000, "SelectObject() FAILED (%s)", msg ); + } + +} + +#endif // BUILD_FOR_WIN32 + +//// StupidStatic ///////////////////////////////////////////////////////////// + +hsBool plDynSurfaceWriter::fForceSharedSurfaces = false; +hsBool plDynSurfaceWriter::fOSDetected = false; +hsBool plDynSurfaceWriter::fOSCanShareSurfaces = false; + +hsBool plDynSurfaceWriter::CanHandleLotsOfThem( void ) +{ + if( fOSDetected ) + return fOSCanShareSurfaces; + + fOSDetected = true; + +#if HS_BUILD_FOR_WIN32 + OSVERSIONINFO versionInfo; + memset( &versionInfo, 0, sizeof( versionInfo ) ); + versionInfo.dwOSVersionInfoSize = sizeof( versionInfo ); + + if( GetVersionEx( &versionInfo ) ) + { + plStatusLog::AddLineS( "pipeline.log", "OS version detection results:" ); + plStatusLog::AddLineS( "pipeline.log", " Version: %d.%d", versionInfo.dwMajorVersion, versionInfo.dwMinorVersion ); + plStatusLog::AddLineS( "pipeline.log", " Build #: %d", versionInfo.dwBuildNumber ); + plStatusLog::AddLineS( "pipeline.log", " Platform ID: %d", versionInfo.dwPlatformId ); + + if( versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) + { + if( fForceSharedSurfaces ) + { + plStatusLog::AddLineS( "pipeline.log", "Detected NT-based platform, but sharing surfaces due to override" ); + fOSCanShareSurfaces = false; + } + else + { + plStatusLog::AddLineS( "pipeline.log", "Detected NT-based platform, allowing separate surfaces" ); + fOSCanShareSurfaces = true; + } + } + else + { + plStatusLog::AddLineS( "pipeline.log", "Detected non-NT-based platform: sharing surfaces" ); + fOSCanShareSurfaces = false; + } + } + else + { + plStatusLog::AddLineS( "pipeline.log", "OS version detection failed" ); + fOSCanShareSurfaces = false; + } + +#endif + return fOSCanShareSurfaces; +} + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plDynSurfaceWriter::plDynSurfaceWriter() +{ + IInit(); +} + +plDynSurfaceWriter::~plDynSurfaceWriter() +{ + Reset(); +} + +plDynSurfaceWriter::plDynSurfaceWriter( plDynamicTextMap *target, UInt32 flags ) +{ + IInit(); + fFlags = flags; + SwitchTarget( target ); +} + +void plDynSurfaceWriter::IInit( void ) +{ + fCurrTarget = 0; + fJustify = kLeftJustify; + fFlags = 0; + fFlushed = true; + fFontFace = nil; + fFontSize = 0; + fFontBlockedRGB = false; +} + +//// Reset //////////////////////////////////////////////////////////////////// + +void plDynSurfaceWriter::Reset( void ) +{ + fRGBSurface.Release(); + fAlphaSurface.Release(); + fCurrTarget = nil; + fFlushed = true; + + delete [] fFontFace; + fFontFace = nil; + fFontSize = 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +//// Target Switching ///////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// FlushToTarget //////////////////////////////////////////////////////////// +// Flushes all ops to the target. + +void plDynSurfaceWriter::FlushToTarget( void ) +{ + int x, y; + + + if( fCurrTarget != nil && !fFlushed ) + { +#if HS_BUILD_FOR_WIN32 + // Flush the GDI so we can grab the bits + GdiFlush(); + + UInt32 *destBits = (UInt32 *)fCurrTarget->GetImage(); + + // Are we merging in the alpha bits? + if( fFlags & kSupportAlpha ) + { + // Yup, munge 'em + UInt32 *srcRGBBits = fRGBSurface.GetBits(); + UInt8 *srcAlphaBits = fAlphaSurface.GetBits(); + UInt32 destWidth = fCurrTarget->GetWidth(); + + for( y = 0; y < fCurrTarget->GetHeight(); y++ ) + { + for( x = 0; x < destWidth; x++ ) + destBits[ x ] = ( srcRGBBits[ x ] & 0x00ffffff ) | ( (UInt32)srcAlphaBits[ x ] << 24 ); + + destBits += destWidth; + srcRGBBits += fRGBSurface.fWidth; + srcAlphaBits += fAlphaSurface.fWidth; + } + } + else + { + // Nope, just a 24-bit copy and set alphas to ff + UInt32 *srcBits = fRGBSurface.GetBits(); + UInt32 destWidth = fCurrTarget->GetWidth(); + + for( y = 0; y < fCurrTarget->GetHeight(); y++ ) + { + memcpy( destBits, srcBits, destWidth * sizeof( UInt32 ) ); + + // Fill in 0xff + for( x = 0; x < destWidth; x++ ) + destBits[ x ] |= 0xff000000; + + destBits += destWidth; + srcBits += fRGBSurface.fWidth; + } + } +#endif + } + fFlushed = true; +} + +//// SwitchTarget ///////////////////////////////////////////////////////////// +// Switches targets. Will flush to old target before switching. Also, if +// kDiscard isn't specified, will copy contents of new target to working +// surface. + +void plDynSurfaceWriter::SwitchTarget( plDynamicTextMap *target ) +{ + if( target == fCurrTarget ) + return; + + if( !fFlushed ) + FlushToTarget(); + + fCurrTarget = target; + fFlushed = true; // Will force a copy next IEnsureSurfaceUpdated() + + // Make sure our surfaces fit + bool hadToAllocate = false; + if( target != nil ) + { + if( !fRGBSurface.WillFit( (UInt16)(target->GetWidth()), (UInt16)(target->GetHeight()) ) ) + { + fRGBSurface.Allocate( (UInt16)(target->GetWidth()), (UInt16)(target->GetHeight()) ); + hadToAllocate = true; + } + + if( fFlags & kSupportAlpha ) + { + if( !fAlphaSurface.WillFit( (UInt16)(target->GetWidth()), (UInt16)(target->GetHeight()) ) ) + { + fAlphaSurface.Allocate( (UInt16)(target->GetWidth()), (UInt16)(target->GetHeight()) ); + hadToAllocate = true; + } + } + } + else + { + fRGBSurface.Release(); + fAlphaSurface.Release(); + hadToAllocate = true; + } + + if( hadToAllocate ) + { + delete [] fFontFace; + fFontFace = nil; + fFontSize = 0; + fFontFlags = 0; + } +} + +//// IEnsureSurfaceUpdated //////////////////////////////////////////////////// +// Makes sure our surfaces are ready to write to. + +void plDynSurfaceWriter::IEnsureSurfaceUpdated( void ) +{ + UInt32 x, y; + + + // If we're flushed, then we haven't drawn since the last flush, + // which means we want to copy from our target before we start drawing. + // If we've already drawn, then we won't be flushed and we don't want + // to be copying over what we've already drawn + if( fCurrTarget != nil && fFlushed ) + { + UInt32 *srcBits = (UInt32 *)fCurrTarget->GetImage(); + + // Are we merging in the alpha bits? + if( fFlags & kSupportAlpha ) + { + // Yup, de-munge 'em + UInt32 *destRGBBits = fRGBSurface.GetBits(); + UInt8 *destAlphaBits = fAlphaSurface.GetBits(); + UInt32 srcWidth = fCurrTarget->GetWidth(); + + for( y = 0; y < fCurrTarget->GetHeight(); y++ ) + { + for( x = 0; x < srcWidth; x++ ) + { + destRGBBits[ x ] = srcBits[ x ]; // Windows GDI probably doesn't care about the alpha bits here. Hopefully... + destAlphaBits[ x ] = (UInt8)(srcBits[ x ] >> 24); + } + + srcBits += srcWidth; + destRGBBits += fRGBSurface.fWidth; + destAlphaBits += fAlphaSurface.fWidth; + } + } + else + { + // Nope, just do a straight memcopy + UInt32 *destBits = fRGBSurface.GetBits(); + UInt32 srcWidth = fCurrTarget->GetWidth(); + + for( y = 0; y < fCurrTarget->GetHeight(); y++ ) + { + memcpy( destBits, srcBits, srcWidth * sizeof( UInt32 ) ); + + srcBits += srcWidth; + destBits += fRGBSurface.fWidth; + } + } + + // ALSO, we need to re-update our settings, since different targets + // can have different fonts or justifications + ISetFont( fCurrTarget->GetFontFace(), fCurrTarget->GetFontSize(), 0/*fCurrTarget->GetWriterFontFlags()*/, fCurrTarget->GetFontAARGB() ); + SetJustify( (Justify)fCurrTarget->GetFontJustify() ); + ISetTextColor( fCurrTarget->GetFontColor(), fCurrTarget->GetFontBlockRGB() ); + + fFlushed = false; + } +} + +hsBool plDynSurfaceWriter::IsValid( void ) const +{ + if( fCurrTarget == nil ) + return false; + + if( fRGBSurface.fDC == nil || fRGBSurface.fBitmap == nil ) + return false; + + if( ( fFlags & kSupportAlpha ) && ( fAlphaSurface.fDC == nil || fAlphaSurface.fBitmap == nil ) ) + return false; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Rendering Functions ////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// SetBitsFromBuffer //////////////////////////////////////////////////////// + +/* +void plDynSurfaceWriter::SetBitsFromBuffer( UInt32 *clearBuffer, UInt16 width, UInt16 height ) +{ + int x, y; + UInt32 *data = (UInt32 *)fImage, *srcData = clearBuffer; + UInt8 *destAlpha = nil; + + + if( !IsValid() ) + return; + +#if HS_BUILD_FOR_WIN32 + GdiFlush(); +#endif + + // Clear *all* to zero + memset( data, 0, fWidth * fHeight * sizeof( UInt32 ) ); + if( fHasAlpha ) + { +#if HS_BUILD_FOR_WIN32 + memset( fWinAlphaBits, 0, fWidth * fHeight ); + destAlpha = fWinAlphaBits; +#endif + } + + // Buffer is of size fVisWidth x fVisHeight, so we need a bit of work to do this right + for( y = 0; y < fVisHeight; y++ ) + { + for( x = 0; x < fVisWidth; x++ ) + { + data[ x ] = srcData[ x ]; + } + + if( destAlpha != nil ) + { + for( x = 0; x < fVisWidth; x++ ) + destAlpha[ x ] = srcData[ x ] >> 24; + + destAlpha += fWidth; + } + + data += fWidth; + srcData += fVisWidth; + } +} +*/ + +//// ClearToColor ///////////////////////////////////////////////////////////// + +void plDynSurfaceWriter::ClearToColor( hsColorRGBA &color ) +{ + if( !IsValid() ) + return; + + IEnsureSurfaceUpdated(); + +#if HS_BUILD_FOR_WIN32 + + UInt32 i, hexColor = color.ToARGB32(); + + + // Flush the GDI first, so it doesn't decide to overwrite us later + GdiFlush(); + + UInt32 *rgbBits = fRGBSurface.GetBits(); + for( i = 0; i < fRGBSurface.fWidth * fRGBSurface.fHeight; i++ ) + rgbBits[ i ] = hexColor; + + if( fFlags & kSupportAlpha ) + { + UInt8 *alphaBits = fAlphaSurface.GetBits(), alpha = (UInt8)(hexColor >> 24); + + for( i = 0; i < fAlphaSurface.fWidth * fAlphaSurface.fHeight; i++ ) + alphaBits[ i ] = alpha; + } +#endif +} + +//// SetFont ////////////////////////////////////////////////////////////////// +// OS-specific. Load the given font for drawing the text with. + +void plDynSurfaceWriter::SetFont( const char *face, UInt16 size, UInt8 fontFlags, hsBool antiAliasRGB ) +{ + if( !IsValid() ) + return; + + IEnsureSurfaceUpdated(); + + ISetFont( face, size, fontFlags, antiAliasRGB ); +} + +//// ISetFont ///////////////////////////////////////////////////////////////// + +void plDynSurfaceWriter::ISetFont( const char *face, UInt16 size, UInt8 fontFlags, hsBool antiAliasRGB ) +{ + fFlags = ( fFlags & ~kFontShadowed ) | ( fontFlags & kFontShadowed ); + + if( !fRGBSurface.FontMatches( face, size, fontFlags, antiAliasRGB ) ) + fRGBSurface.SetFont( face, size, fontFlags, antiAliasRGB ); + + if( fFlags & kSupportAlpha ) + { + if( !fAlphaSurface.FontMatches( face, size, fontFlags, !antiAliasRGB ) ) + fAlphaSurface.SetFont( face, size, fontFlags, !antiAliasRGB ); + } +} + +//// SetTextColor ///////////////////////////////////////////////////////////// +// blockRGB basically forces the RGB channel to write in blocks instead of +// actual characters. This isn't useful unless you're relying on the alpha +// channel to do the text (opaque text and transparent background), in which +// case you want plenty of block color in your RGB channel because it'll get +// alpha-ed out by the alpha channel. + +void plDynSurfaceWriter::SetTextColor( hsColorRGBA &color, hsBool blockRGB ) +{ + if( !IsValid() ) + return; + + IEnsureSurfaceUpdated(); + ISetTextColor( color, blockRGB ); +} + +//// IRefreshTextColor //////////////////////////////////////////////////////// + +void plDynSurfaceWriter::ISetTextColor( hsColorRGBA &color, hsBool blockRGB ) +{ +#if HS_BUILD_FOR_WIN32 + + int r = (int)(color.r * 255.f); + int g = (int)(color.g * 255.f); + int b = (int)(color.b * 255.f); + + fRGBSurface.fTextColor = RGB( r, g, b ); + if( fFlags & kSupportAlpha ) + { + int a = (int)(color.a * 255.f); + fAlphaSurface.fTextColor = RGB( a, a, a ); + } + + fFontBlockedRGB = blockRGB; + + if( fFontBlockedRGB && !( fFlags & kFontShadowed ) ) + { + ::SetBkColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); + ::SetBkMode( fRGBSurface.fDC, OPAQUE ); + } + else + ::SetBkMode( fRGBSurface.fDC, TRANSPARENT ); + + ::SetTextColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); + + if( fFlags & kSupportAlpha ) + ::SetTextColor( fAlphaSurface.fDC, fAlphaSurface.fTextColor ); +#endif +} + +//// SetJustify /////////////////////////////////////////////////////////////// + +void plDynSurfaceWriter::SetJustify( Justify j ) +{ + fJustify = j; +} + +//// IRefreshOSJustify //////////////////////////////////////////////////////// + +void plDynSurfaceWriter::IRefreshOSJustify( void ) +{ + if( !IsValid() ) + return; + + UINT justMode; + switch( fJustify ) + { + case kLeftJustify: justMode = TA_LEFT; break; + case kCenter: justMode = TA_CENTER; break; + case kRightJustify: justMode = TA_RIGHT; break; + } + ::SetTextAlign( fRGBSurface.fDC, justMode ); + if( fFlags & kSupportAlpha ) + ::SetTextAlign( fAlphaSurface.fDC, justMode ); +} + +//// DrawString /////////////////////////////////////////////////////////////// + +void plDynSurfaceWriter::DrawString( UInt16 x, UInt16 y, const char *text ) +{ + if( !IsValid() ) + return; + + IEnsureSurfaceUpdated(); + + IRefreshOSJustify(); + +#if HS_BUILD_FOR_WIN32 + if( fFlags & kFontShadowed ) + { + ::SetTextColor( fRGBSurface.fDC, RGB( 0, 0, 0 ) ); + ::TextOut( fRGBSurface.fDC, x + 1, y + 1, text, strlen( text ) ); + + ::SetTextColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); + ::TextOut( fRGBSurface.fDC, x, y, text, strlen( text ) ); + + if( fFlags & kSupportAlpha ) + { + ::TextOut( fAlphaSurface.fDC, x + 1, y + 1, text, strlen( text ) ); + ::TextOut( fAlphaSurface.fDC, x, y, text, strlen( text ) ); + } + } + else + { + ::TextOut( fRGBSurface.fDC, x, y, text, strlen( text ) ); + if( fFlags & kSupportAlpha ) + ::TextOut( fAlphaSurface.fDC, x, y, text, strlen( text ) ); + } + +#endif +} + +//// DrawClippedString //////////////////////////////////////////////////////// + +void plDynSurfaceWriter::DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 width, UInt16 height ) +{ + if( !IsValid() ) + return; + + IEnsureSurfaceUpdated(); + + IRefreshOSJustify(); + +#if HS_BUILD_FOR_WIN32 + + RECT r; + ::SetRect( &r, x, y, x + width, y + height ); + + if( fJustify == kRightJustify ) + x += width - 1; + else if( fJustify == kCenter ) + x += width >> 1; + + if( fFlags & kFontShadowed ) + { + ::SetTextColor( fRGBSurface.fDC, RGB( 0, 0, 0 ) ); + + ::OffsetRect( &r, 1, 1 ); + ::ExtTextOut( fRGBSurface.fDC, x + 1, y + 1, ETO_CLIPPED, &r, text, strlen( text ), nil ); + if( fFlags & kSupportAlpha ) + ::ExtTextOut( fAlphaSurface.fDC, x + 1, y + 1, ETO_CLIPPED, &r, text, strlen( text ), nil ); + ::OffsetRect( &r, -1, -1 ); + + ::SetTextColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); + } + + ::ExtTextOut( fRGBSurface.fDC, x, y, ETO_CLIPPED, &r, text, strlen( text ), nil ); + if( fFlags & kSupportAlpha ) + ::ExtTextOut( fAlphaSurface.fDC, x, y, ETO_CLIPPED, &r, text, strlen( text ), nil ); + +#endif +} + +//// DrawClippedString //////////////////////////////////////////////////////// + +void plDynSurfaceWriter::DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height ) +{ + if( !IsValid() ) + return; + + IEnsureSurfaceUpdated(); + + IRefreshOSJustify(); + +#if HS_BUILD_FOR_WIN32 + + RECT r; + ::SetRect( &r, clipX, clipY, clipX + width, clipY + height ); + + if( fFlags & kFontShadowed ) + { + ::SetTextColor( fRGBSurface.fDC, RGB( 0, 0, 0 ) ); + + ::OffsetRect( &r, 1, 1 ); + ::ExtTextOut( fRGBSurface.fDC, x + 1, y + 1, ETO_CLIPPED, &r, text, strlen( text ), nil ); + if( fFlags & kSupportAlpha ) + ::ExtTextOut( fAlphaSurface.fDC, x + 1, y + 1, ETO_CLIPPED, &r, text, strlen( text ), nil ); + ::OffsetRect( &r, -1, -1 ); + + ::SetTextColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); + } + + ::ExtTextOut( fRGBSurface.fDC, x, y, ETO_CLIPPED, &r, text, strlen( text ), nil ); + if( fFlags & kSupportAlpha ) + ::ExtTextOut( fAlphaSurface.fDC, x, y, ETO_CLIPPED, &r, text, strlen( text ), nil ); + +#endif +} + +//// DrawWrappedString //////////////////////////////////////////////////////// + +void plDynSurfaceWriter::DrawWrappedString( UInt16 x, UInt16 y, const char *text, UInt16 width, UInt16 height ) +{ + if( !IsValid() ) + return; + + IEnsureSurfaceUpdated(); + +#if HS_BUILD_FOR_WIN32 + + RECT r; + ::SetRect( &r, x, y, x + width, y + height ); + + UINT format = DT_TOP | DT_NOPREFIX | DT_WORDBREAK; + switch( fJustify ) + { + case kLeftJustify: format |= DT_LEFT; break; + case kCenter: format |= DT_CENTER; break; + case kRightJustify: format |= DT_RIGHT; break; + } + + if( fFlags & kFontShadowed ) + { + ::SetTextColor( fRGBSurface.fDC, RGB( 0, 0, 0 ) ); + + ::OffsetRect( &r, 1, 1 ); + ::DrawText( fRGBSurface.fDC, text, strlen( text ), &r, format ); + if( fFlags & kSupportAlpha ) + ::DrawText( fAlphaSurface.fDC, text, strlen( text ), &r, format ); + ::OffsetRect( &r, -1, -1 ); + + ::SetTextColor( fRGBSurface.fDC, fRGBSurface.fTextColor ); + } + + ::DrawText( fRGBSurface.fDC, text, strlen( text ), &r, format ); + if( fFlags & kSupportAlpha ) + ::DrawText( fAlphaSurface.fDC, text, strlen( text ), &r, format ); + +#endif +} + +//// CalcStringWidth ////////////////////////////////////////////////////////// + +UInt16 plDynSurfaceWriter::CalcStringWidth( const char *text, UInt16 *height ) +{ + if( !IsValid() ) + return 0; + + IEnsureSurfaceUpdated(); + +#if HS_BUILD_FOR_WIN32 + + SIZE size; + ::GetTextExtentPoint32( fRGBSurface.fDC, text, strlen( text ), &size ); + + if( height != nil ) + *height = (UInt16)size.cy; + + return (UInt16)size.cx; +#endif +} + +//// CalcWrappedStringSize //////////////////////////////////////////////////// + +void plDynSurfaceWriter::CalcWrappedStringSize( const char *text, UInt16 *width, UInt16 *height ) +{ + if( !IsValid() ) + return; + + IEnsureSurfaceUpdated(); + +#if HS_BUILD_FOR_WIN32 + + RECT r; + ::SetRect( &r, 0, 0, *width, 0 ); + + UINT format = DT_TOP | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT; + switch( fJustify ) + { + case kLeftJustify: format |= DT_LEFT; break; + case kCenter: format |= DT_CENTER; break; + case kRightJustify: format |= DT_RIGHT; break; + } + + ::DrawText( fRGBSurface.fDC, text, strlen( text ), &r, format ); + + *width = (UInt16)(r.right); + if( height != nil ) + *height = (UInt16)r.bottom; +#endif +} + +//// FillRect ///////////////////////////////////////////////////////////////// + +void plDynSurfaceWriter::FillRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ) +{ + if( !IsValid() ) + return; + + IEnsureSurfaceUpdated(); + +#if HS_BUILD_FOR_WIN32 + + RECT rc; + ::SetRect( &rc, x, y, x + width, y + height ); + + int r = (int)(color.r * 255.f); + int g = (int)(color.g * 255.f); + int b = (int)(color.b * 255.f); + int a = (int)(color.a * 255.f); + + HBRUSH brush = ::CreateSolidBrush( RGB( r, g, b ) ); + ::FillRect( fRGBSurface.fDC, &rc, brush ); + ::DeleteObject( brush ); + + if( fFlags & kSupportAlpha ) + { + brush = ::CreateSolidBrush( RGB( a, a, a ) ); + ::FillRect( fAlphaSurface.fDC, &rc, brush ); + ::DeleteObject( brush ); + } + +#endif +} + +//// FrameRect //////////////////////////////////////////////////////////////// + +void plDynSurfaceWriter::FrameRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ) +{ + if( !IsValid() ) + return; + + IEnsureSurfaceUpdated(); + +#if HS_BUILD_FOR_WIN32 + + RECT rc; + ::SetRect( &rc, x, y, x + width, y + height ); + + int r = (int)(color.r * 255.f); + int g = (int)(color.g * 255.f); + int b = (int)(color.b * 255.f); + int a = (int)(color.a * 255.f); + + HBRUSH brush = ::CreateSolidBrush( RGB( r, g, b ) ); + ::FrameRect( fRGBSurface.fDC, &rc, brush ); + ::DeleteObject( brush ); + + if( fFlags & kSupportAlpha ) + { + brush = ::CreateSolidBrush( RGB( a, a, a ) ); + ::FrameRect( fAlphaSurface.fDC, &rc, brush ); + ::DeleteObject( brush ); + } + +#endif +} + +/* +//// DrawImage //////////////////////////////////////////////////////////////// + +void plDynSurfaceWriter::DrawImage( UInt16 x, UInt16 y, plMipmap *image, hsBool respectAlpha ) +{ + if( !IsValid() ) + return; + +#if HS_BUILD_FOR_WIN32 + // Flush the GDI first, to make sure it's done + GdiFlush(); +#endif + + plMipmap::CompositeOptions opts( respectAlpha ? 0 : plMipmap::kForceOpaque ); + if( !fHasAlpha ) + opts.fFlags = plMipmap::kCopySrcAlpha; // Don't care, this is fastest + + Composite( image, x, y, &opts ); + + /// HACK for now, since the alpha in the mipmap gets copied straight into the + /// 32-bit color buffer, but our separate hacked alpha buffer hasn't been updated + if( fHasAlpha && !respectAlpha ) + { + HBRUSH brush = ::CreateSolidBrush( RGB( 255, 255, 255 ) ); + RECT rc; + ::SetRect( &rc, x, y, x + image->GetWidth(), y + image->GetHeight() ); + ::FillRect( fWinAlphaDC, &rc, brush ); + ::DeleteObject( brush ); + } +} + +//// DrawClippedImage ///////////////////////////////////////////////////////// + +void plDynSurfaceWriter::DrawClippedImage( UInt16 x, UInt16 y, plMipmap *image, + UInt16 srcClipX, UInt16 srcClipY, + UInt16 srcClipWidth, UInt16 srcClipHeight, + hsBool respectAlpha ) +{ + if( !IsValid() ) + return; + +#if HS_BUILD_FOR_WIN32 + // Flush the GDI first, to make sure it's done + GdiFlush(); +#endif + + plMipmap::CompositeOptions opts( respectAlpha ? 0 : plMipmap::kForceOpaque ); + if( !fHasAlpha ) + opts.fFlags = plMipmap::kCopySrcAlpha; // Don't care, this is fastest + + opts.fSrcClipX = srcClipX; + opts.fSrcClipY = srcClipY; + opts.fSrcClipWidth = srcClipWidth; + opts.fSrcClipHeight = srcClipHeight; + Composite( image, x, y, &opts ); + + /// HACK for now, since the alpha in the mipmap gets copied straight into the + /// 32-bit color buffer, but our separate hacked alpha buffer hasn't been updated + if( fHasAlpha && !respectAlpha ) + { + HBRUSH brush = ::CreateSolidBrush( RGB( 255, 255, 255 ) ); + RECT rc; + ::SetRect( &rc, x, y, x + ( srcClipWidth > 0 ? srcClipWidth : image->GetWidth() ), + y + ( srcClipHeight > 0 ? srcClipHeight : image->GetHeight() ) ); + ::FillRect( fWinAlphaDC, &rc, brush ); + ::DeleteObject( brush ); + } +} +*/ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynSurfaceWriter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynSurfaceWriter.h new file mode 100644 index 00000000..413604de --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynSurfaceWriter.h @@ -0,0 +1,205 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDynSurfaceWriter Class Header // +// Abstract class wrapping around Windows GDI functionality for writing to // +// a generic RGBA surface. Allows us to create one writer per DTMap or a // +// single shared writer to conserve OS resources on 98/ME. // +// // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 10.28.2002 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDynSurfaceWriter_h +#define _plDynSurfaceWriter_h + +#include "hsColorRGBA.h" +#include "hsWindows.h" // EVIL + +struct hsMatrix44; + +class plDynamicTextMap; + +//// Class Definition ///////////////////////////////////////////////////////// + +class plDynSurfaceWriter +{ + public: + + //// Public Flags //// + enum Justify + { + kLeftJustify = 0, + kCenter, + kRightJustify + }; + + enum Flags + { + kSupportAlpha = 0x00000001, + + kFontBold = 0x00000002, + kFontItalic = 0x00000004, + kFontShadowed = 0x00000008, + kFontMask = 0x0000000e, + + kDiscardOnFlush = 0x00000010 + }; + + //// Public Data ///// + + + //// Public Members //// + + + plDynSurfaceWriter(); + plDynSurfaceWriter( plDynamicTextMap *target, UInt32 flags = 0 ); + virtual ~plDynSurfaceWriter(); + + + /// Operations to perform on the text block + + void ClearToColor( hsColorRGBA &color ); + void SetFont( const char *face, UInt16 size, UInt8 fontFlags = 0, hsBool antiAliasRGB = true ); + void SetTextColor( hsColorRGBA &color, hsBool blockRGB = false ); + void SetJustify( Justify j ); + + void DrawString( UInt16 x, UInt16 y, const char *text ); + void DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 width, UInt16 height ); + void DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height ); + void DrawWrappedString( UInt16 x, UInt16 y, const char *text, UInt16 width, UInt16 height ); + UInt16 CalcStringWidth( const char *text, UInt16 *height = nil ); + void CalcWrappedStringSize( const char *text, UInt16 *width, UInt16 *height ); + void FillRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ); + void FrameRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ); + +// void DrawImage( UInt16 x, UInt16 y, plMipmap *image, hsBool respectAlpha = false ); +// void DrawClippedImage( UInt16 x, UInt16 y, plMipmap *image, UInt16 srcClipX, UInt16 srcClipY, +// UInt16 srcClipWidth, UInt16 srcClipHeight, hsBool respectAlpha = false ); + + // Copy the raw data from the given buffer. +// void SetBitsFromBuffer( UInt32 *clearBuffer, UInt16 width, UInt16 height ) + + /// Target switching operations + + // Flushes all ops to the target. + void FlushToTarget( void ); + + // Switches targets. Will flush to old target before switching. Also, if kDiscard isn't specified, will copy contents of new target to working surface + void SwitchTarget( plDynamicTextMap *target ); // Will force a flush + + // Clears and resets everything. Does NOT flush. + void Reset( void ); + + hsBool IsValid( void ) const; + + static hsBool CanHandleLotsOfThem( void ); + + protected: + + //// Protected Members //// + + void IInit( void ); + void IEnsureSurfaceUpdated( void ); + void IRefreshOSJustify( void ); + void ISetTextColor( hsColorRGBA &color, hsBool blockRGB ); + + void ISetFont( const char *face, UInt16 size, UInt8 fontFlags = 0, hsBool antiAliasRGB = true ); + + plDynamicTextMap *fCurrTarget; + UInt32 fFlags; + Justify fJustify; + hsBool fFlushed; + + char *fFontFace; + UInt16 fFontSize; + UInt8 fFontFlags; + hsBool fFontAntiAliasRGB; + hsBool fFontBlockedRGB; + + static hsBool fForceSharedSurfaces; + static hsBool fOSDetected; + static hsBool fOSCanShareSurfaces; + +#if HS_BUILD_FOR_WIN32 + class plWinSurface + { + protected: + void *fBits; + + virtual UInt8 IBitsPerPixel( void ) const = 0; + + public: + HDC fDC; + HBITMAP fBitmap; + HFONT fFont; + COLORREF fTextColor; + int fSaveNum; + + UInt16 fWidth, fHeight; + + char *fFontFace; + UInt16 fFontSize; + UInt8 fFontFlags; + hsBool fFontAntiAliasRGB, fFontBlockedRGB; + + plWinSurface(); + ~plWinSurface(); + + void Allocate( UInt16 w, UInt16 h ); + void Release( void ); + + hsBool WillFit( UInt16 w, UInt16 h ); + hsBool FontMatches( const char *face, UInt16 size, UInt8 flags, hsBool aaRGB ); + void SetFont( const char *face, UInt16 size, UInt8 flags, hsBool aaRGB ); + }; + + class plWinRGBSurface : public plWinSurface + { + virtual UInt8 IBitsPerPixel( void ) const { return 32; } + public: + UInt32 *GetBits( void ) const { return (UInt32 *)fBits; } + }; + + class plWinAlphaSurface : public plWinSurface + { + virtual UInt8 IBitsPerPixel( void ) const { return 8; } + public: + UInt8 *GetBits( void ) const { return (UInt8 *)fBits; } + }; + + plWinRGBSurface fRGBSurface; + plWinAlphaSurface fAlphaSurface; +#endif +}; + + +#endif // _plDynSurfaceWriter_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynamicTextMap.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynamicTextMap.cpp new file mode 100644 index 00000000..09e3b79f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynamicTextMap.cpp @@ -0,0 +1,911 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDynamicTextMap Class Functions // +// Derived bitmap class representing a single mipmap. // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 6.7.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plDynamicTextMap.h" + +#include "hsStream.h" +#include "hsExceptions.h" +#include "hsUtils.h" +#include "hsMatrix44.h" +#include "../plPipeline/hsGDeviceRef.h" +#include "../plMessage/plDynamicTextMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "plProfile.h" +#include "../plStatusLog/plStatusLog.h" +#include "plFont.h" +#include "plFontCache.h" +#include "../plResMgr/plLocalization.h" + + +plProfile_CreateMemCounter("DynaTextMem", "PipeC", DynaTextMem); +plProfile_CreateCounterNoReset("DynaTexts", "PipeC", DynaTexts); +plProfile_Extern(MemMipmaps); + + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plDynamicTextMap::plDynamicTextMap() : plMipmap() +{ + fVisWidth = fVisHeight = 0; + fHasAlpha = false; + fJustify = kLeftJustify; + fInitBuffer = nil; + fFontFace = nil; + fFontSize = 0; + fFontFlags = 0; + fFontAntiAliasRGB = false; + fFontColor.Set( 0, 0, 0, 1 ); + fFontBlockRGB = false; + fHasCreateBeenCalled = false; + +} + +plDynamicTextMap::~plDynamicTextMap() +{ + Reset(); +} + +plDynamicTextMap::plDynamicTextMap( UInt32 width, UInt32 height, hsBool hasAlpha, UInt32 extraWidth, UInt32 extraHeight ) : plMipmap() +{ + fInitBuffer = nil; + fFontFace = nil; + Create( width, height, hasAlpha, extraWidth, extraHeight ); +} + +//// SetNoCreate ////////////////////////////////////////////////////////////// +// For export time, we want to set up the config to write to disk, but we +// don't want to actually be creating OS surfaces. So we call this function +// instead, which does just that. It basically does all the setup work that +// Create() does, or enough for us to write out later. + +void plDynamicTextMap::SetNoCreate( UInt32 width, UInt32 height, hsBool hasAlpha ) +{ + // OK, so it really isn't that much work... + fVisWidth = (UInt16)width; + fVisHeight = (UInt16)height; + fHasAlpha = hasAlpha; + fImage = nil; // So we know we haven't actually done anything yet + delete [] fInitBuffer; + fInitBuffer = nil; +} + +//// Create /////////////////////////////////////////////////////////////////// + +void plDynamicTextMap::Create( UInt32 width, UInt32 height, hsBool hasAlpha, UInt32 extraWidth, UInt32 extraHeight ) +{ + SetConfig( hasAlpha ? kARGB32Config : kRGB32Config ); + + + fVisWidth = (UInt16)width; + fVisHeight = (UInt16)height; + fHasAlpha = hasAlpha; + + for( fWidth = 1; fWidth < width + extraWidth; fWidth <<= 1 ); + for( fHeight = 1; fHeight < height + extraHeight; fHeight <<= 1 ); + + // instead of allocating the fImage here, we'll wait for the first draw operation to be called (in IIsValid) + fHasCreateBeenCalled = true; + + fRowBytes = fWidth << 2; + fNumLevels = 1; + fFlags |= plMipmap::kDontThrowAwayImage; + fCompressionType = plMipmap::kUncompressed; + fUncompressedInfo.fType = plMipmap::UncompressedInfo::kRGB8888; + + // Destroy the old texture ref, if we have one. This should force the + // pipeline to recreate one more suitable for our use + SetDeviceRef( nil ); + + // Some init color + SetFont( "Arial", 12 ); + hsColorRGBA color; + color.Set( 0,0,1,1); + SetTextColor( color ); + + SetCurrLevel( 0 ); + plProfile_Inc(DynaTexts); + +} + +//// Reset //////////////////////////////////////////////////////////////////// + +void plDynamicTextMap::Reset( void ) +{ + IDestroyOSSurface(); + + plMipmap::Reset(); + + // they need to call create again to undo the affects of call Reset() + fHasCreateBeenCalled = false; + + delete [] fInitBuffer; + fInitBuffer = nil; + + delete [] fFontFace; + fFontFace = nil; + + // Destroy the old texture ref, since we're no longer using it + SetDeviceRef( nil ); +} + +/////////////////////////////////////////////////////////////////////////////// +//// OS-Specific Functions //////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +hsBool plDynamicTextMap::IIsValid( void ) +{ + if( GetImage() == nil && fHasCreateBeenCalled ) + { + // we are going to allocate the fImage at this point... when someone is looking for it + fImage = (void *)IAllocateOSSurface( (UInt16)fWidth, (UInt16)fHeight ); + hsColorRGBA color; + if( fInitBuffer != nil ) + { + IClearFromBuffer( fInitBuffer ); + } + else + { + color.Set( 0.f, 0.f, 0.f, 1.f ); + ClearToColor( color ); + FlushToHost(); + } + IBuildLevelSizes(); + fTotalSize = GetLevelSize( 0 ); + SetCurrLevel( 0 ); + // Destroy the old texture ref, if we have one. This should force the + // pipeline to recreate one more suitable for our use + SetDeviceRef( nil ); + plProfile_NewMem(MemMipmaps, fTotalSize); + plProfile_NewMem(DynaTextMem, fTotalSize); +#ifdef MEMORY_LEAK_TRACER + IAddToMemRecord( this, plRecord::kViaCreate ); +#endif + } + + if( GetImage() == nil ) + return false; + + return true;//fWriter->IsValid(); +} + +// allow the user of the DynaTextMap that they are done with the image... for now +// ... the fImage will be re-created on the next operation that requires the image +void plDynamicTextMap::PurgeImage() +{ + IDestroyOSSurface(); + fTotalSize = 0; + SetCurrLevel( 0 ); + // Destroy the old texture ref, if we have one. This should force the + // pipeline to recreate one more suitable for our use + SetDeviceRef( nil ); +} + +//// IAllocateOSSurface /////////////////////////////////////////////////////// +// OS-specific. Allocates a rectangular bitmap of the given dimensions that +// the OS can draw text into. Returns a pointer to the pixels. + +UInt32* plDynamicTextMap::IAllocateOSSurface( UInt16 width, UInt16 height ) +{ + UInt32* pixels = TRACKED_NEW UInt32[ width * height ]; + return pixels; +} + +//// IDestroyOSSurface //////////////////////////////////////////////////////// +// Opposite of allocate. DUH! + +void plDynamicTextMap::IDestroyOSSurface( void ) +{ +#ifdef MEMORY_LEAK_TRACER + if( fImage != nil ) + IRemoveFromMemRecord( (UInt8 *)fImage ); +#endif + + delete [] fImage; + fImage = nil; + + plProfile_Dec(DynaTexts); + plProfile_DelMem(DynaTextMem, fTotalSize); + plProfile_DelMem(MemMipmaps, fTotalSize); +} + +/////////////////////////////////////////////////////////////////////////////// +//// Virtual Functions //////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// Read ///////////////////////////////////////////////////////////////////// + +UInt32 plDynamicTextMap::Read( hsStream *s ) +{ + UInt32 totalRead = plBitmap::Read( s ); + + // The funny thing is that we don't read anything like a mipmap; we just + // keep the width and height and call Create() after we read those in + + fVisWidth = (UInt16)(s->ReadSwap32()); + fVisHeight = (UInt16)(s->ReadSwap32()); + fHasAlpha = s->ReadBool(); + totalRead += 2 * 4; + + UInt32 initSize = s->ReadSwap32(); + totalRead += 4; + if( initSize > 0 ) + { + fInitBuffer = TRACKED_NEW UInt32[ initSize ]; + + s->ReadSwap32( initSize, fInitBuffer ); + totalRead += initSize * 4; + } + else + fInitBuffer = nil; + + Create( fVisWidth, fVisHeight, fHasAlpha ); + + delete [] fInitBuffer; + fInitBuffer = nil; + + return totalRead; +} + +//// Write //////////////////////////////////////////////////////////////////// + +UInt32 plDynamicTextMap::Write( hsStream *s ) +{ + UInt32 totalWritten = plBitmap::Write( s ); + + s->WriteSwap32( fVisWidth ); + s->WriteSwap32( fVisHeight ); + s->WriteBool( fHasAlpha ); + + s->WriteSwap32( fInitBuffer != nil ? fVisWidth * fVisHeight * sizeof( UInt32 ) : 0 ); + if( fInitBuffer != nil ) + { + s->WriteSwap32( fVisWidth * fVisHeight, fInitBuffer ); + } + + return totalWritten; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Some More Functions ////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// SetInitBuffer //////////////////////////////////////////////////////////// +// Sets an initial buffer, which is written to disk and read in to use for +// initializing the color buffer upon creation. If not specified, we init to +// black. ASSUMES the buffer is of dimensions fVisWidth x fVisHeight. + +void plDynamicTextMap::SetInitBuffer( UInt32 *buffer ) +{ + delete [] fInitBuffer; + if( buffer == nil ) + { + fInitBuffer = nil; + return; + } + + fInitBuffer = TRACKED_NEW UInt32[ fVisWidth * fVisHeight ]; + memcpy( fInitBuffer, buffer, fVisWidth * fVisHeight * sizeof( UInt32 ) ); +} + +//// CopyFrom ///////////////////////////////////////////////////////////////// + +void plDynamicTextMap::CopyFrom( plMipmap *source ) +{ + hsAssert( false, "Copying plDynamicTextMaps is not supported." ); +} + +//// Clone //////////////////////////////////////////////////////////////////// + +plMipmap *plDynamicTextMap::Clone( void ) +{ + static bool alreadyWarned = false; + + if( !alreadyWarned ) + { + hsAssert( false, "Cloning plDynamicTextMaps is not supported." ); + alreadyWarned = true; + } + + return nil; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Rendering Functions ////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// IClearFromBuffer ///////////////////////////////////////////////////////// + +void plDynamicTextMap::IClearFromBuffer( UInt32 *clearBuffer ) +{ + int y; + UInt32 *data = (UInt32 *)fImage, *srcData = clearBuffer; + UInt8 *destAlpha = nil; + + + if( !IIsValid() ) + return; + + // Clear *all* to zero + memset( data, 0, fWidth * fHeight * sizeof( UInt32 ) ); + + // Buffer is of size fVisWidth x fVisHeight, so we need a bit of work to do this right + for( y = 0; y < fVisHeight; y++ ) + { + memcpy( data, srcData, fVisWidth * sizeof( UInt32 ) ); + data += fWidth; + srcData += fVisWidth; + } +} + +//// ClearToColor ///////////////////////////////////////////////////////////// + +void plDynamicTextMap::ClearToColor( hsColorRGBA &color ) +{ + if( !IIsValid() ) + return; + + UInt32 i, hex = color.ToARGB32(); + UInt32 *data = (UInt32 *)fImage; + + // Buffer is of size fVisWidth x fVisHeight, so we need a bit of work to do this right + for( i = 0; i < fHeight * fWidth; i++ ) + data[ i ] = hex; +} + +//// SetJustify /////////////////////////////////////////////////////////////// + +void plDynamicTextMap::SetJustify( Justify j ) +{ +// ===> Don't need to validate creation +// if( !IIsValid() ) +// return; + + fJustify = j; + switch( fJustify ) + { + case kLeftJustify: fCurrFont->SetRenderXJustify( plFont::kRenderJustXForceLeft ); break; + case kCenter: fCurrFont->SetRenderXJustify( plFont::kRenderJustXCenter ); break; + case kRightJustify: fCurrFont->SetRenderXJustify( plFont::kRenderJustXRight ); break; + } +} + +//// SetFont ////////////////////////////////////////////////////////////////// + +void plDynamicTextMap::SetFont( const char *face, UInt16 size, UInt8 fontFlags, hsBool antiAliasRGB ) +{ +// ===> Don't need to validate creation +// if( !IIsValid() ) +// return; + + delete [] fFontFace; + if (plLocalization::UsingUnicode()) + { + // unicode has a bunch of chars that most fonts don't have, so we override the font choice with one + // that will work with the desired language + hsStatusMessageF("We are using a unicode language, overriding font choice of %s", face ? face : "nil"); + fFontFace = hsStrcpy( "Unicode" ); + } + else + fFontFace = ( face != nil ) ? hsStrcpy( face ) : nil; + fFontSize = size; + fFontFlags = fontFlags; + fFontAntiAliasRGB = antiAliasRGB; + + fCurrFont = plFontCache::GetInstance().GetFont( fFontFace, (UInt8)fFontSize, + ( ( fFontFlags & kFontBold ) ? plFont::kFlagBold : 0 ) | + ( ( fFontFlags & kFontItalic ) ? plFont::kFlagItalic : 0 ) ); + if ( fCurrFont == nil ) + { + if (!fCurrFont) + hsStatusMessageF("Font missing - %s. Using Arial", fFontFace ? fFontFace : "nil"); + + if ( fFontFace ) + delete [] fFontFace; + fFontFace = hsStrcpy( "Arial" ); + // lets try again with Arial + fCurrFont = plFontCache::GetInstance().GetFont( fFontFace, (UInt8)fFontSize, + ( ( fFontFlags & kFontBold ) ? plFont::kFlagBold : 0 ) | + ( ( fFontFlags & kFontItalic ) ? plFont::kFlagItalic : 0 ) ); + } + + // This will be nil if we're just running the page optimizer. + if (fCurrFont) + { + fCurrFont->SetRenderYJustify( plFont::kRenderJustYTop ); + SetJustify( fJustify ); + } +} + +void plDynamicTextMap::SetFont( const wchar_t *face, UInt16 size, UInt8 fontFlags , hsBool antiAliasRGB ) +{ + char *sFace = hsWStringToString(face); + SetFont(sFace,size,fontFlags,antiAliasRGB); + delete [] sFace; +} + +//// SetLineSpacing /////////////////////////////////////////////////////////// + +void plDynamicTextMap::SetLineSpacing( Int16 spacing ) +{ +// ===> Don't need to validate creation +// if( !IIsValid() ) +// return; + + fLineSpacing = spacing; + fCurrFont->SetRenderLineSpacing(spacing); +} + +//// SetTextColor ///////////////////////////////////////////////////////////// + +void plDynamicTextMap::SetTextColor( hsColorRGBA &color, hsBool blockRGB ) +{ +// ===> Don't need to validate creation +// if( !IIsValid() ) +// return; + + fFontColor = color; + fFontBlockRGB = blockRGB; + + if (fCurrFont) + fCurrFont->SetRenderColor( fFontColor.ToARGB32() ); +} + +//// DrawString /////////////////////////////////////////////////////////////// + +void plDynamicTextMap::DrawString( UInt16 x, UInt16 y, const char *text ) +{ + wchar_t *wText = hsStringToWString(text); + DrawString(x,y,wText); + delete [] wText; +} + +void plDynamicTextMap::DrawString( UInt16 x, UInt16 y, const wchar_t *text ) +{ + if( !IIsValid() ) + return; + + SetJustify( fJustify ); + fCurrFont->SetRenderFlag( plFont::kRenderWrap | plFont::kRenderClip, false ); + fCurrFont->SetRenderColor( fFontColor.ToARGB32() ); + fCurrFont->SetRenderFlag( plFont::kRenderIntoAlpha, fFontBlockRGB ); + fCurrFont->RenderString( this, x, y, text ); +} + +//// DrawClippedString //////////////////////////////////////////////////////// + +void plDynamicTextMap::DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 width, UInt16 height ) +{ + wchar_t *wText = hsStringToWString(text); + DrawClippedString(x,y,wText,width,height); + delete [] wText; +} + +void plDynamicTextMap::DrawClippedString( Int16 x, Int16 y, const wchar_t *text, UInt16 width, UInt16 height ) +{ + if( !IIsValid() ) + return; + + SetJustify( fJustify ); + fCurrFont->SetRenderClipping( x, y, width, height ); + fCurrFont->SetRenderColor( fFontColor.ToARGB32() ); + fCurrFont->SetRenderFlag( plFont::kRenderIntoAlpha, fFontBlockRGB ); + fCurrFont->RenderString( this, x, y, text ); +} + +//// DrawClippedString //////////////////////////////////////////////////////// + +void plDynamicTextMap::DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height ) +{ + wchar_t *wText = hsStringToWString(text); + DrawClippedString(x,y,wText,clipX,clipY,width,height); + delete [] wText; +} + +void plDynamicTextMap::DrawClippedString( Int16 x, Int16 y, const wchar_t *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height ) +{ + if( !IIsValid() ) + return; + + SetJustify( fJustify ); + fCurrFont->SetRenderClipping( clipX, clipY, width, height ); + fCurrFont->SetRenderColor( fFontColor.ToARGB32() ); + fCurrFont->RenderString( this, x, y, text ); +} + +//// DrawWrappedString //////////////////////////////////////////////////////// + +void plDynamicTextMap::DrawWrappedString( UInt16 x, UInt16 y, const char *text, UInt16 width, UInt16 height, UInt16 *lastX, UInt16 *lastY ) +{ + wchar_t *wText = hsStringToWString(text); + DrawWrappedString(x,y,wText,width,height,lastX,lastY); + delete [] wText; +} + +void plDynamicTextMap::DrawWrappedString( UInt16 x, UInt16 y, const wchar_t *text, UInt16 width, UInt16 height, UInt16 *lastX, UInt16 *lastY ) +{ + if( !IIsValid() ) + return; + + SetJustify( fJustify ); + fCurrFont->SetRenderWrapping( x, y, width, height ); + fCurrFont->SetRenderColor( fFontColor.ToARGB32() ); + fCurrFont->SetRenderFlag( plFont::kRenderIntoAlpha, fFontBlockRGB ); + fCurrFont->RenderString( this, x, y, text, lastX, lastY ); +} + +//// CalcStringWidth ////////////////////////////////////////////////////////// + +UInt16 plDynamicTextMap::CalcStringWidth( const char *text, UInt16 *height ) +{ + wchar_t *wText = hsStringToWString(text); + UInt16 w = CalcStringWidth(wText,height); + delete [] wText; + return w; +} + +UInt16 plDynamicTextMap::CalcStringWidth( const wchar_t *text, UInt16 *height ) +{ +// ===> Don't need to validate creation +// if( !IIsValid() ) +// return 0; + + SetJustify( fJustify ); + UInt16 w, h, a, lastX, lastY; + UInt32 firstClipped; + fCurrFont->SetRenderFlag( plFont::kRenderClip | plFont::kRenderWrap, false ); + fCurrFont->CalcStringExtents( text, w, h, a, firstClipped, lastX, lastY ); + if( height != nil ) + *height = h; + return w; +} + +//// SetFirstLineIndent /////////////////////////////////////////////////////// + +void plDynamicTextMap::SetFirstLineIndent( Int16 indent ) +{ +// ===> Don't need to validate creation +// if( !IIsValid() ) +// return; + + fCurrFont->SetRenderFirstLineIndent( indent ); +} + +//// CalcWrappedStringSize //////////////////////////////////////////////////// + +void plDynamicTextMap::CalcWrappedStringSize( const char *text, UInt16 *width, UInt16 *height, UInt32 *firstClippedChar, UInt16 *maxAscent, UInt16 *lastX, UInt16 *lastY ) +{ + wchar_t *wText = hsStringToWString(text); + CalcWrappedStringSize(wText,width,height,firstClippedChar,maxAscent,lastX,lastY); + delete [] wText; +} + +void plDynamicTextMap::CalcWrappedStringSize( const wchar_t *text, UInt16 *width, UInt16 *height, UInt32 *firstClippedChar, UInt16 *maxAscent, UInt16 *lastX, UInt16 *lastY ) +{ +// ===> Don't need to validate creation +// if( !IIsValid() ) +// return; + + SetJustify( fJustify ); + UInt16 w, h, a, lX, lY; + UInt32 firstClipped; + fCurrFont->SetRenderWrapping( 0, 0, *width, *height ); + fCurrFont->CalcStringExtents( text, w, h, a, firstClipped, lX, lY ); + *width = w; + *height = h; + if( firstClippedChar != nil ) + *firstClippedChar = firstClipped; + if( maxAscent != nil ) + *maxAscent = a; + if( lastX != nil ) + *lastX = lX; + if( lastY != nil ) + *lastY = lY; +} + +//// FillRect ///////////////////////////////////////////////////////////////// + +void plDynamicTextMap::FillRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ) +{ + if( !IIsValid() ) + return; + + if( x + width > fWidth ) + width = (UInt16)(fWidth - x); + + // Gee, how hard can it REALLY be? + UInt32 i, hex = color.ToARGB32(); + height += y; + if( height > fHeight ) + height = (UInt16)fHeight; + for( ; y < height; y++ ) + { + UInt32 *destPtr = GetAddr32( x, y ); + for( i = 0; i < width; i++ ) + destPtr[ i ] = hex; + } +} + +//// FrameRect //////////////////////////////////////////////////////////////// + +void plDynamicTextMap::FrameRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ) +{ + if( !IIsValid() ) + return; + + if( x + width > fWidth ) + width = (UInt16)(fWidth - x); + if( y + height > fHeight ) + height = (UInt16)(fHeight - y); + + // Shouldn't be much harder + UInt32 i, hex = color.ToARGB32(); + UInt32 *dest1, *dest2; + + dest1 = GetAddr32( x, y ); + dest2 = GetAddr32( x, y + height - 1 ); + for( i = 0; i < width; i++ ) + dest1[ i ] = dest2[ i ] = hex; + + for( i = 0; i < height; i++ ) + { + dest1[ 0 ] = dest1[ width - 1 ] = hex; + dest1 += fWidth; + } +} + +//// DrawImage //////////////////////////////////////////////////////////////// + +void plDynamicTextMap::DrawImage( UInt16 x, UInt16 y, plMipmap *image, DrawMethods method ) +{ + if( !IIsValid() ) + return; + + plMipmap::CompositeOptions opts; + if( method == kImgNoAlpha ) + { + if( fHasAlpha ) + opts.fFlags = plMipmap::kForceOpaque; + else + opts.fFlags = plMipmap::kCopySrcAlpha; // Don't care, this is fastest + } + else if( method == kImgBlend ) + opts.fFlags = 0; // Default opts + else if( method == kImgSprite ) + opts.fFlags = plMipmap::kCopySrcAlpha; + + Composite( image, x, y, &opts ); + + /// HACK for now, since the alpha in the mipmap gets copied straight into the + /// 32-bit color buffer, but our separate hacked alpha buffer hasn't been updated +/* if( fHasAlpha && !respectAlpha ) + { + HBRUSH brush = ::CreateSolidBrush( RGB( 255, 255, 255 ) ); + RECT rc; + ::SetRect( &rc, x, y, x + image->GetWidth(), y + image->GetHeight() ); + ::FillRect( fWinAlphaDC, &rc, brush ); + ::DeleteObject( brush ); + } +*/ +} + +//// DrawClippedImage ///////////////////////////////////////////////////////// + +void plDynamicTextMap::DrawClippedImage( UInt16 x, UInt16 y, plMipmap *image, + UInt16 srcClipX, UInt16 srcClipY, + UInt16 srcClipWidth, UInt16 srcClipHeight, + DrawMethods method ) +{ + if( !IIsValid() ) + return; + + plMipmap::CompositeOptions opts; + if( method == kImgNoAlpha ) + { + if( fHasAlpha ) + opts.fFlags = plMipmap::kForceOpaque; + else + opts.fFlags = plMipmap::kCopySrcAlpha; // Don't care, this is fastest + } + else if( method == kImgBlend ) + opts.fFlags = 0; // Default opts + else if( method == kImgSprite ) + opts.fFlags = plMipmap::kCopySrcAlpha; + + opts.fSrcClipX = srcClipX; + opts.fSrcClipY = srcClipY; + opts.fSrcClipWidth = srcClipWidth; + opts.fSrcClipHeight = srcClipHeight; + Composite( image, x, y, &opts ); + + /// HACK for now, since the alpha in the mipmap gets copied straight into the + /// 32-bit color buffer, but our separate hacked alpha buffer hasn't been updated +/* if( fHasAlpha && !respectAlpha ) + { + HBRUSH brush = ::CreateSolidBrush( RGB( 255, 255, 255 ) ); + RECT rc; + ::SetRect( &rc, x, y, x + ( srcClipWidth > 0 ? srcClipWidth : image->GetWidth() ), + y + ( srcClipHeight > 0 ? srcClipHeight : image->GetHeight() ) ); + ::FillRect( fWinAlphaDC, &rc, brush ); + ::DeleteObject( brush ); + } +*/ +} + +//// FlushToHost ////////////////////////////////////////////////////////////// + +void plDynamicTextMap::FlushToHost( void ) +{ + if( !IIsValid() ) + return; + + // Dirty the mipmap's deviceRef, if there is one + if( GetDeviceRef() != nil ) + GetDeviceRef()->SetDirty( true ); +} + +//// GetLayerTransform //////////////////////////////////////////////////////// +// Since the textGen can actually create a texture bigger than you were expecting, +// you want to be able to apply a layer texture transform that will compensate. This +// function will give you that transform. Just feed it into plLayer->SetTransform(). + +hsMatrix44 plDynamicTextMap::GetLayerTransform( void ) +{ + hsMatrix44 xform; + hsVector3 scale; + + scale.Set( (float)GetVisibleWidth() / (float)GetWidth(), + (float)GetVisibleHeight() / (float)GetHeight(), 1.f ); + + xform.MakeScaleMat( &scale ); + return xform; +} + +//// MsgReceive /////////////////////////////////////////////////////////////// + +hsBool plDynamicTextMap::MsgReceive( plMessage *msg ) +{ + plDynamicTextMsg *textMsg = plDynamicTextMsg::ConvertNoRef( msg ); + if( textMsg != nil ) + { + if( textMsg->fCmd & plDynamicTextMsg::kClear ) + ClearToColor( textMsg->fClearColor ); + + if( textMsg->fCmd & plDynamicTextMsg::kSetTextColor ) + SetTextColor( textMsg->fColor, textMsg->fBlockRGB ); + + if( (textMsg->fCmd & plDynamicTextMsg::kSetFont ) && textMsg->fString) + SetFont( textMsg->fString, textMsg->fX, (UInt8)(textMsg->fFlags) ); + + if( textMsg->fCmd & plDynamicTextMsg::kSetLineSpacing ) + SetLineSpacing( textMsg->fLineSpacing ); + + if( textMsg->fCmd & plDynamicTextMsg::kSetJustify ) + SetJustify( (Justify)textMsg->fFlags ); + + if( textMsg->fCmd & plDynamicTextMsg::kFillRect ) + FillRect( textMsg->fLeft, textMsg->fTop, textMsg->fRight - textMsg->fLeft + 1, + textMsg->fBottom - textMsg->fTop + 1, textMsg->fColor ); + + if( textMsg->fCmd & plDynamicTextMsg::kFrameRect ) + FrameRect( textMsg->fLeft, textMsg->fTop, textMsg->fRight - textMsg->fLeft + 1, + textMsg->fBottom - textMsg->fTop + 1, textMsg->fColor ); + + if( (textMsg->fCmd & plDynamicTextMsg::kDrawString ) && textMsg->fString) + DrawString( textMsg->fX, textMsg->fY, textMsg->fString ); + + if( (textMsg->fCmd & plDynamicTextMsg::kDrawClippedString ) && textMsg->fString) + DrawClippedString( textMsg->fX, textMsg->fY, textMsg->fString, + textMsg->fLeft, textMsg->fTop, textMsg->fRight - textMsg->fLeft + 1, + textMsg->fBottom - textMsg->fTop + 1 ); + + if( (textMsg->fCmd & plDynamicTextMsg::kDrawWrappedString ) && textMsg->fString) + DrawWrappedString( textMsg->fX, textMsg->fY, textMsg->fString, textMsg->fRight, textMsg->fBottom ); + + if( textMsg->fCmd & plDynamicTextMsg::kDrawImage ) + { + plMipmap *mip = plMipmap::ConvertNoRef( textMsg->fImageKey ? textMsg->fImageKey->ObjectIsLoaded() : nil); + if( mip != nil ) + DrawImage( textMsg->fX, textMsg->fY, mip, textMsg->fFlags ? kImgBlend : kImgNoAlpha ); + } + + if( textMsg->fCmd & plDynamicTextMsg::kDrawClippedImage ) + { + plMipmap *mip = plMipmap::ConvertNoRef( textMsg->fImageKey ? textMsg->fImageKey->ObjectIsLoaded() : nil); + if( mip != nil ) + DrawClippedImage( textMsg->fX, textMsg->fY, mip, textMsg->fLeft, textMsg->fTop, + textMsg->fRight, textMsg->fBottom, textMsg->fFlags ? kImgBlend : kImgNoAlpha ); + } + + if( textMsg->fCmd & plDynamicTextMsg::kFlush ) + FlushToHost(); + + if( textMsg->fCmd & plDynamicTextMsg::kPurgeImage ) + PurgeImage(); + + return true; + } + + return plMipmap::MsgReceive( msg ); +} + +//// Swap ///////////////////////////////////////////////////////////////////// +// Swapping is an evil little trick. It's also something that should probably +// be exposed at the mipmap level eventually, but there's no need for it yet. +// Basically, it lets you take the contents of two DTMaps and swap them, as +// if you had swapped pointers, but you didn't. This is so you can, well, swap +// DTMaps without swapping pointers! (Like, say, you don't have access to them) + +#define SWAP_ME( Type, a, b ) { Type temp; temp = a; a = b; b = temp; } + +void plDynamicTextMap::Swap( plDynamicTextMap *other ) +{ + // We only do this if the two are the same size, color depth, etc + if( other->GetWidth() != GetWidth() || other->GetHeight() != GetHeight() || + other->GetPixelSize() != GetPixelSize() ) + return; + + // Swap image pointers + void *ptr = other->fImage; + other->fImage = fImage; + fImage = ptr; + + // Invalidate both device refs (don't risk swapping THOSE) + if( GetDeviceRef() != nil ) + GetDeviceRef()->SetDirty( true ); + if( other->GetDeviceRef() != nil ) + other->GetDeviceRef()->SetDirty( true ); + + // Swap DTMap info + SWAP_ME( hsBool, fHasAlpha, other->fHasAlpha ); + SWAP_ME( hsBool, fShadowed, other->fShadowed ); + + SWAP_ME( Justify, fJustify, other->fJustify ); + SWAP_ME( char *, fFontFace, other->fFontFace ); + SWAP_ME( UInt16, fFontSize, other->fFontSize ); + SWAP_ME( UInt8, fFontFlags, other->fFontFlags ); + SWAP_ME( hsBool, fFontAntiAliasRGB, other->fFontAntiAliasRGB ); + SWAP_ME( hsColorRGBA, fFontColor, other->fFontColor ); + SWAP_ME( hsBool, fFontBlockRGB, other->fFontBlockRGB ); + + SWAP_ME( hsBool, fFontBlockRGB, other->fFontBlockRGB ); + SWAP_ME( hsBool, fFontBlockRGB, other->fFontBlockRGB ); + SWAP_ME( hsBool, fFontBlockRGB, other->fFontBlockRGB ); + + SWAP_ME( plFont *, fCurrFont, other->fCurrFont ); + SWAP_ME( UInt32 *, fInitBuffer, other->fInitBuffer ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynamicTextMap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynamicTextMap.h new file mode 100644 index 00000000..f833de4d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plDynamicTextMap.h @@ -0,0 +1,228 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDynamicTextMap Class Header // +// I derived from plMipmap not because I inherit a lot of the functionality,// +// but because this acts very very much like a mipmap with one mip level. // +// The only difference is that the actual data gets generated on the fly, // +// instead of converted at export time. However, to the outside world, // +// we're just a plain old mipmap, and I'd like to keep it that way. // +// // +// Note that we are also of a fixed format--ARGB8888. Keeps things nice and // +// simple that way. // +// // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 1.14.2002 mcn - Created. // +// 10.28.2002 mcn - Changing the arrangement a bit. Now we have a single // +// writable "creation" surface that actually generates // +// data, which then copies out on a flush to the actual // +// mipmap data. This is slower because it requires two // +// copies (second one when we prepare to write to a new // +// surface, unless kDiscard is specified as a flush option)// +// but it lets us allocate only one OS surface, which saves// +// us on Win98/ME where we're very limited in terms of // +// available OS surfaces. // +// To facilitate this, we create a new abstract class to // +// encapsulate the actual GDI functionality of Windows. // +// This way, we have two options when creating DTMaps: // +// allocate an OS writer per surface, which lets us avoid // +// the copy problem mentioned above, or allocate one to // +// share. This will also let us optimize by switching to // +// non-shared writers once we write a non-OS-reliant // +// writer/text renderer. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDynamicTextMap_h +#define _plDynamicTextMap_h + +#include "plMipmap.h" +#include "hsColorRGBA.h" + +struct hsMatrix44; + +//// Class Definition ///////////////////////////////////////////////////////// + +class plFont; +class plDynamicTextMap : public plMipmap +{ + protected: + + UInt16 fVisWidth, fVisHeight; + + virtual UInt32 Read( hsStream *s ); + virtual UInt32 Write( hsStream *s ); + + public: + //// Public Flags //// + + enum Justify + { + kLeftJustify = 0, + kCenter, + kRightJustify + }; + + //// Public Data ///// + + + //// Public Members //// + + + plDynamicTextMap(); + plDynamicTextMap( UInt32 width, UInt32 height, hsBool hasAlpha = false, UInt32 extraWidth = 0, UInt32 extraHeight = 0 ); + virtual ~plDynamicTextMap(); + + CLASSNAME_REGISTER( plDynamicTextMap ); + GETINTERFACE_ANY( plDynamicTextMap, plMipmap ); + + + void Create( UInt32 width, UInt32 height, hsBool hasAlpha, UInt32 extraWidth = 0, UInt32 extraHeight = 0 ); + void SetNoCreate( UInt32 width, UInt32 height, hsBool hasAlpha ); + + virtual void Reset( void ); + + virtual void Read( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Read( s, mgr ); this->Read( s ); } + virtual void Write( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Write( s, mgr ); this->Write( s ); } + + virtual UInt8 GetNumLevels( void ) const { return 1; } + + virtual void Colorize( void ) { ; } + virtual plMipmap *Clone( void ); + virtual void CopyFrom( plMipmap *source ); + + + /// Operations to perform on the text block + + hsBool IsValid() { return IIsValid(); } + + // allow the user of the DynaTextMap that they are done with the image... for now + // ... the fImage will be re-created on the next operation that requires the image + void PurgeImage(); + + void ClearToColor( hsColorRGBA &color ); + + enum FontFlags + { + kFontBold = 0x01, + kFontItalic = 0x02, + kFontShadowed = 0x04 + }; + + void SetFont( const char *face, UInt16 size, UInt8 fontFlags = 0, hsBool antiAliasRGB = true ); + void SetFont( const wchar_t *face, UInt16 size, UInt8 fontFlags = 0, hsBool antiAliasRGB = true ); + void SetLineSpacing( Int16 spacing ); + void SetTextColor( hsColorRGBA &color, hsBool blockRGB = false ); + void SetJustify( Justify j ); + + void DrawString( UInt16 x, UInt16 y, const char *text ); + void DrawString( UInt16 x, UInt16 y, const wchar_t *text ); + void DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 width, UInt16 height ); + void DrawClippedString( Int16 x, Int16 y, const wchar_t *text, UInt16 width, UInt16 height ); + void DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height ); + void DrawClippedString( Int16 x, Int16 y, const wchar_t *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height ); + void DrawWrappedString( UInt16 x, UInt16 y, const char *text, UInt16 width, UInt16 height, UInt16 *lastX = nil, UInt16 *lastY = nil ); + void DrawWrappedString( UInt16 x, UInt16 y, const wchar_t *text, UInt16 width, UInt16 height, UInt16 *lastX = nil, UInt16 *lastY = nil ); + UInt16 CalcStringWidth( const char *text, UInt16 *height = nil ); + UInt16 CalcStringWidth( const wchar_t *text, UInt16 *height = nil ); + void CalcWrappedStringSize( const char *text, UInt16 *width, UInt16 *height, UInt32 *firstClippedChar = nil, UInt16 *maxAscent = nil, UInt16 *lastX = nil, UInt16 *lastY = nil ); + void CalcWrappedStringSize( const wchar_t *text, UInt16 *width, UInt16 *height, UInt32 *firstClippedChar = nil, UInt16 *maxAscent = nil, UInt16 *lastX = nil, UInt16 *lastY = nil ); + void FillRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ); + void FrameRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ); + void SetFirstLineIndent( Int16 indent ); + + enum DrawMethods + { + kImgNoAlpha, // Just copy color data, force alpha to full if present + kImgBlend, // Blend color onto dest using src alpha, keep dest alpha + kImgSprite // Copy color data and alphas + }; + void DrawImage( UInt16 x, UInt16 y, plMipmap *image, DrawMethods method = kImgNoAlpha ); + void DrawClippedImage( UInt16 x, UInt16 y, plMipmap *image, UInt16 srcClipX, UInt16 srcClipY, + UInt16 srcClipWidth, UInt16 srcClipHeight, DrawMethods method = kImgNoAlpha ); + + void FlushToHost( void ); + + hsBool MsgReceive( plMessage *msg ); + + UInt16 GetVisibleWidth( void ) { return fVisWidth; } + UInt16 GetVisibleHeight( void ) { return fVisHeight; } + + // Since the dynamic text can actually create a texture bigger than you were expecting, + // you want to be able to apply a layer texture transform that will compensate. This + // function will give you that transform. Just feed it into plLayer->SetTransform(). + + hsMatrix44 GetLayerTransform( void ); + + void SetInitBuffer( UInt32 *buffer ); + + // Gets for font values + Justify GetFontJustify( void ) const { return fJustify; } + const char *GetFontFace( void ) const { return fFontFace; } + UInt16 GetFontSize( void ) const { return fFontSize; } + hsBool GetFontAARGB( void ) const { return fFontAntiAliasRGB; } + hsColorRGBA GetFontColor( void ) const { return fFontColor; } + hsBool GetFontBlockRGB( void ) const { return fFontBlockRGB; } + Int16 GetLineSpacing( void ) const { return fLineSpacing; } + + plFont *GetCurrFont( void ) const { return fCurrFont; } + + virtual void Swap( plDynamicTextMap *other ); + + protected: + + //// Protected Members //// + + hsBool IIsValid( void ); + void IClearFromBuffer( UInt32 *clearBuffer ); + + UInt32 *IAllocateOSSurface( UInt16 width, UInt16 height ); + void IDestroyOSSurface( void ); + + hsBool fHasAlpha, fShadowed; + + Justify fJustify; + char *fFontFace; + UInt16 fFontSize; + UInt8 fFontFlags; + hsBool fFontAntiAliasRGB; + hsColorRGBA fFontColor; + hsBool fFontBlockRGB; + Int16 fLineSpacing; + + plFont *fCurrFont; + + UInt32 *fInitBuffer; + + hsBool fHasCreateBeenCalled; +}; + + +#endif // _plDynamicTextMap_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFont.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFont.cpp new file mode 100644 index 00000000..a5e18a5e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFont.cpp @@ -0,0 +1,1966 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plFont Class Header // +// Seems like we've come full circle again. This is our generic Plasma // +// bitmap font class/format. Quick list of things it supports, or needs to: // +// - Antialiasing, either in the font def or at rendertime // +// - Doublebyte character sets // +// - Platform independence, of course // +// - Render to reasonably arbitrary mipmap // +// - Character-level kerning, both before and after, as well as // +// negative kerning (for ligatures) // +// // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 3.4.2003 mcn - Created. // +// 4.3.2003 mcn - Updated. Casting a char to a UInt16 sign-extends it if // +// the char is > 128, but casting it to an UInt8 first works.// +// Ugly as sin, but hey, so are you. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "plFont.h" + +#include "plMipmap.h" +#include "hsResMgr.h" + + +//// plCharacter Stuff //////////////////////////////////////////////////////// + +plFont::plCharacter::plCharacter() +{ + fBitmapOff = 0; + fHeight = 0; + fBaseline = 0; + fLeftKern = fRightKern = 0.f; +} + +void plFont::plCharacter::Read( hsStream *s ) +{ + // Rocket science here... + s->ReadSwap( &fBitmapOff ); + s->ReadSwap( &fHeight ); + s->ReadSwap( &fBaseline ); + s->ReadSwap( &fLeftKern ); + s->ReadSwap( &fRightKern ); +} + +void plFont::plCharacter::Write( hsStream *s ) +{ + s->WriteSwap( fBitmapOff ); + s->WriteSwap( fHeight ); + s->WriteSwap( fBaseline ); + s->WriteSwap( fLeftKern ); + s->WriteSwap( fRightKern ); +} + +//// Constructor/Read/Write/Destructor/etc //////////////////////////////////// + +plFont::plFont() +{ + IClear( true ); +} + +plFont::~plFont() +{ + IClear(); +} + +void plFont::IClear( bool onConstruct ) +{ + if( !onConstruct ) + delete [] fBMapData; + + memset( fFace, 0, sizeof( fFace ) ); + fSize = 0; + fFlags = 0; + + fWidth = fHeight = 0; + fBPP = 0; + fBMapData = nil; + fFirstChar = 0; + fMaxCharHeight = 0; + fCharacters.Reset(); + + fRenderInfo.fFlags = 0; + fRenderInfo.fX = fRenderInfo.fY = fRenderInfo.fNumCols = 0; + fRenderInfo.fMaxWidth = fRenderInfo.fMaxHeight = 0; + fRenderInfo.fDestPtr = nil; + fRenderInfo.fDestStride = 0; + fRenderInfo.fColor = 0; + fRenderInfo.fMipmap = nil; + fRenderInfo.fRenderFunc = nil; + fRenderInfo.fVolatileStringPtr = nil; + fRenderInfo.fFirstLineIndent = 0; + fRenderInfo.fLineSpacing = 0; +} + +void plFont::SetFace( const char *face ) +{ + strncpy( fFace, face, sizeof( fFace ) ); +} + +void plFont::SetSize( UInt8 size ) +{ + fSize = size; +} + +void plFont::Read( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Read( s, mgr ); + ReadRaw( s ); +} + +void plFont::Write( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Write( s, mgr ); + WriteRaw( s ); +} + +/////////////////////////////////////////////////////////////////////////////// +//// Rendering //////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void plFont::SetRenderColor( UInt32 color ) +{ + fRenderInfo.fColor = color; +} + +void plFont::SetRenderClipRect( Int16 x, Int16 y, Int16 width, Int16 height ) +{ + fRenderInfo.fClipRect.Set( x, y, width, height ); +} + +void plFont::SetRenderClipping( Int16 x, Int16 y, Int16 width, Int16 height ) +{ + SetRenderFlag( kRenderWrap, false ); + SetRenderFlag( kRenderClip, true ); + SetRenderClipRect( x, y, width, height ); +} + +void plFont::SetRenderWrapping( Int16 x, Int16 y, Int16 width, Int16 height ) +{ + SetRenderFlag( kRenderWrap, true ); + SetRenderFlag( kRenderClip, false ); + SetRenderClipRect( x, y, width, height ); +} + +void plFont::ICalcFontAscent( void ) +{ + UInt32 i; + + + // Hack for now, only calc the ascent for characters in the 127 character ASCII range + for( i = 0, fFontAscent = 0, fFontDescent = 0, fMaxCharHeight = 0; i < fCharacters.GetCount(); i++ ) + { + if( i + fFirstChar < 128 && fFontAscent < fCharacters[ i ].fBaseline ) + fFontAscent = fCharacters[ i ].fBaseline; + + Int32 descent = fCharacters[ i ].fHeight - fCharacters[ i ].fBaseline; + if( fFontDescent < descent ) + fFontDescent = descent; + + if( fMaxCharHeight < fCharacters[ i ].fHeight ) + fMaxCharHeight = fCharacters[ i ].fHeight; + } +} + +//// IIsWordBreaker ////////////////////////////////////////////////////////// +// Returns whether the given character is one that can break a line + +static inline bool IIsWordBreaker( const char c ) +{ + return ( strchr( " \t,.;\n", c ) != nil ) ? true : false; +} + +//// IIsDrawableWordBreak ///////////////////////////////////////////////////// +// Returns whether the given character is a line breaker that has to be drawn +// (non whitespace) + +static inline bool IIsDrawableWordBreak( const char c ) +{ + return ( strchr( ",.;", c ) != nil ) ? true : false; +} + +//// RenderString ///////////////////////////////////////////////////////////// +// The base render function. Additional options are specified externally, +// so that their effects can be cached for optimization + +void plFont::RenderString( plMipmap *mip, UInt16 x, UInt16 y, const char *string, UInt16 *lastX, UInt16 *lastY ) +{ + // convert the char string to a wchar_t string + wchar_t *wideString = hsStringToWString(string); + RenderString(mip,x,y,wideString,lastX,lastY); + delete [] wideString; +} + + +void plFont::RenderString( plMipmap *mip, UInt16 x, UInt16 y, const wchar_t *string, UInt16 *lastX, UInt16 *lastY ) +{ + if( mip->IsCompressed() ) + { + hsAssert( false, "Unable to render string to compressed mipmap" ); + return; + } + + IRenderString( mip, x, y, string, false ); + if( lastX != nil ) + *lastX = fRenderInfo.fLastX; + if( lastY != nil ) + *lastY = fRenderInfo.fLastY; +} + +void plFont::IRenderString( plMipmap *mip, UInt16 x, UInt16 y, const wchar_t *string, hsBool justCalc ) +{ + fRenderInfo.fMipmap = mip; + fRenderInfo.fX = x; + fRenderInfo.fY = y; + fRenderInfo.fNumCols = (Int16)(( fBPP <= 8 ) ? ( ( fWidth * fBPP ) >> 3 ) : 0); + fRenderInfo.fFloatWidth = (hsScalar)fWidth; + fRenderInfo.fFarthestX = x; + fRenderInfo.fMaxAscent = 0; + fRenderInfo.fVolatileStringPtr = string; + + switch( fRenderInfo.fFlags & kRenderJustYMask ) + { + case kRenderJustYTop: + fRenderInfo.fY += (Int16)fFontAscent; + break; + case kRenderJustYBottom: + if( fRenderInfo.fFlags & ( kRenderClip | kRenderWrap ) ) + fRenderInfo.fY = (Int16)(fRenderInfo.fClipRect.GetBottom() - 1 - fMaxCharHeight + fFontAscent); + else + fRenderInfo.fY = (Int16)(mip->GetHeight() - 1 - fMaxCharHeight + fFontAscent); + break; + case kRenderJustYCenter: + fRenderInfo.fY = (Int16)(( fRenderInfo.fFlags & ( kRenderClip | kRenderWrap ) ) ? fRenderInfo.fClipRect.GetBottom() - 1 : mip->GetHeight() - 1); + fRenderInfo.fY = (Int16)(( fRenderInfo.fY - fMaxCharHeight ) >> 1); + fRenderInfo.fY += (Int16)fFontAscent; + break; + default: // Just the baseline + ; + } + + if( justCalc ) + { + plCharacter &ch = fCharacters[ (UInt16)string[ 0 ] - fFirstChar ]; + fRenderInfo.fX = fRenderInfo.fFarthestX = x - (Int16)ch.fLeftKern; + if( fRenderInfo.fX < 0 ) + fRenderInfo.fX = 0; + } + else + { + switch( fRenderInfo.fFlags & kRenderJustXMask ) + { + case kRenderJustXLeft: + // Default + break; + case kRenderJustXForceLeft: + { +// plCharacter &ch = fCharacters[ (UInt16)(UInt8)string[ 0 ] - fFirstChar ]; +// Int32 newX = x - (Int16)ch.fLeftKern; +// if( newX < 0 ) +// newX = 0; +// fRenderInfo.fX = fRenderInfo.fFarthestX = newX; + } + break; + case kRenderJustXCenter: + { + UInt16 right = (UInt16)(( fRenderInfo.fFlags & ( kRenderClip | kRenderWrap ) ) ? fRenderInfo.fClipRect.GetRight() : mip->GetWidth()); +// UInt16 width = CalcStringWidth( string ); + fRenderInfo.fX = fRenderInfo.fFarthestX = ( ( x + right ) >> 1 );// - ( width >> 1 ); + } + break; + case kRenderJustXRight: + { + UInt16 width = 0, right = (UInt16)(( fRenderInfo.fFlags & ( kRenderClip | kRenderWrap ) ) ? fRenderInfo.fClipRect.GetRight() : mip->GetWidth()); +/* if( fRenderInfo.fFlags & kRenderClip ) + { + fRenderInfo.fFlags &= ~kRenderClip; + width = CalcStringWidth( string ); + fRenderInfo.fFlags |= kRenderClip; + } + else +*/ width = 0;//CalcStringWidth( string ); + fRenderInfo.fX = fRenderInfo.fFarthestX = right - width; + } + break; + } + } + + // Choose an optimal rendering function + fRenderInfo.fRenderFunc = nil; + if( justCalc ) + fRenderInfo.fRenderFunc = IRenderCharNull; + else if( mip->GetPixelSize() == 32 ) + { + if( fBPP == 1 ) + fRenderInfo.fRenderFunc = ( fRenderInfo.fFlags & kRenderScaleAA ) ? IRenderChar1To32AA : IRenderChar1To32; + else if( fBPP == 8 ) + { + if( fRenderInfo.fFlags & kRenderIntoAlpha ) + { + if( ( fRenderInfo.fColor & 0xff000000 ) != 0xff000000 ) + fRenderInfo.fRenderFunc = IRenderChar8To32Alpha; + else + fRenderInfo.fRenderFunc = IRenderChar8To32FullAlpha; + } + else + fRenderInfo.fRenderFunc = IRenderChar8To32; + } + } + + if( fRenderInfo.fRenderFunc == nil ) + { + hsAssert( false, "Invalid combination of source and destination formats in RenderString()" ); + return; + } + + // Init our other render values + if( !justCalc ) + { + fRenderInfo.fDestStride = mip->GetRowBytes(); + fRenderInfo.fDestBPP = mip->GetPixelSize() >> 3; + fRenderInfo.fDestPtr = (UInt8 *)mip->GetImage(); + fRenderInfo.fDestPtr += fRenderInfo.fY * fRenderInfo.fDestStride; + fRenderInfo.fDestPtr += fRenderInfo.fX * fRenderInfo.fDestBPP; + } + if( fRenderInfo.fFlags & ( kRenderWrap | kRenderClip ) ) + { + fRenderInfo.fMaxHeight = (Int16)( fRenderInfo.fClipRect.GetBottom() - fRenderInfo.fY ); + fRenderInfo.fMaxWidth = (Int16)( fRenderInfo.fClipRect.GetRight() - x ); + } + else if( justCalc ) + { + // Just calculating, no wrapping, so the max is as far as we can go + // Note: 32767 isn't quite right, since we'll be adding the left kern in before we + // calc the first character, so adjust so we make sure we don't underflow + plCharacter &ch = fCharacters[ (UInt16)(UInt8)string[ 0 ] - fFirstChar ]; + + fRenderInfo.fMaxHeight = (Int16)fMaxCharHeight; + fRenderInfo.fMaxWidth = (Int16)32767 + (Int16)ch.fLeftKern; + } + else + { + fRenderInfo.fMaxHeight = (Int16)( mip->GetHeight() - fRenderInfo.fY ); + fRenderInfo.fMaxWidth = (Int16)( mip->GetWidth() - x ); + } + + fRenderInfo.fMaxDescent = 0; + + if( fRenderInfo.fFlags & kRenderWrap ) + { + // Hell, gotta word wrap the text + // To avoid backtracking, we step forward in the string one word at a time until we hit a break, + // then render what we have and continue + Int32 lastWord = 0, i; + bool isFirstLine = true; + x = 0; + Int16 firstMaxAscent = 0; + UInt32 lineHt, lineDelta; + if( fRenderInfo.fFlags & kRenderScaleAA ) + lineHt = fMaxCharHeight >> 1; + else + lineHt = fMaxCharHeight; + + // adjust the line height for spacing + lineHt += fRenderInfo.fLineSpacing; + + lineDelta = lineHt * fRenderInfo.fDestStride; + + while( *string != 0 && fRenderInfo.fMaxHeight >= fFontDescent ) + { + UInt8 *destStartPtr = fRenderInfo.fDestPtr; + UInt32 destStartX = fRenderInfo.fX; + Int16 destMaxWidth = fRenderInfo.fMaxWidth, thisIndent = 0; + + if( isFirstLine ) + { + // First line, apply indent if applicable + fRenderInfo.fX += fRenderInfo.fFirstLineIndent; + fRenderInfo.fMaxWidth -= fRenderInfo.fFirstLineIndent; + fRenderInfo.fDestPtr += fRenderInfo.fFirstLineIndent * fRenderInfo.fDestBPP; + thisIndent = fRenderInfo.fFirstLineIndent; + isFirstLine = false; + } + + std::string ellipsisTracker = ""; // keeps track of ellipsis, since there are three word break chars that can't be split + bool possibleEllipsis = false; + int preEllipsisLastWord = 0; // where the word break was before we started tracking an ellipsis + + // Iterate through the string, looking for the next line break + for( lastWord = 0, i = 0; string[ i ] != 0; i++ ) + { + // If we're a carriage return, we go ahead and break anyway + if( string[ i ] == L'\n' ) + { + lastWord = i; + break; + } + + // handle invalid chars discretely + plCharacter* charToDraw = NULL; + if (fCharacters.Count() <= ((UInt16)string[i] - fFirstChar)) + charToDraw = &(fCharacters[(UInt16)L' ' - fFirstChar]); + else + charToDraw = &(fCharacters[(UInt16)string[i] - fFirstChar]); + + Int16 leftKern = (Int16)charToDraw->fLeftKern; + if( fRenderInfo.fFlags & kRenderScaleAA ) + x += leftKern / 2; + else + x += leftKern; + + // Update our position and see if we're over + // Note that our wrapping is slightly off, in that it doesn't take into account + // the left kerning of characters. Hopefully that won't matter much... + UInt16 charWidth = (UInt16)(fWidth + (Int16)charToDraw->fRightKern); + if( fRenderInfo.fFlags & kRenderScaleAA ) + charWidth >>= 1; + + UInt16 nonAdjustedX = (UInt16)(x + fWidth); // just in case the actual bitmap is too big to fit on page and we think the character can (because of right kern) + x += charWidth; + + if(( x >= fRenderInfo.fMaxWidth ) || (nonAdjustedX >= fRenderInfo.fMaxWidth)) + { + // we're over, but lastWord may not be correct (especially if we're in the middle of an ellipsis) + if (possibleEllipsis) + { + // ellipsisTracker will not be empty since possibleEllipsis is true (so there will be at least one period) + if (ellipsisTracker == ".") // only one period so far + { + if ((string[i] == '.') && (string[i+1] == '.')) // we have an ellipsis, so reset the lastWord back before we found it + lastWord = preEllipsisLastWord; + // otherwise, we don't have an ellipsis, so lastWord is correct (but the grammer might not be ;-) + } + else if (ellipsisTracker == "..") // only two periods so far + { + if (string[i] == '.') // we have an ellipsis, so reset the lastWord back before we found it + lastWord = preEllipsisLastWord; + // otherwise, we don't have an ellipsis, so lastWord is correct (but the grammer might not be ;-) + } + // if neither of the above are true, then the full ellipsis was encountered and the lastWord is correct + ellipsisTracker = ""; + possibleEllipsis = false; + } + // Over, so break + break; + } + + // Are we a word breaker? + if( IIsWordBreaker( (char)(string[ i ]) ) ) + { + if (string[i] == '.') // we might have an ellipsis here + { + if (ellipsisTracker == "...") // we already have a full ellipsis, so break between them + { + preEllipsisLastWord = i; + ellipsisTracker = ""; + } + else if (ellipsisTracker == "") // no ellipsis yet, so save the last word + preEllipsisLastWord = lastWord; + ellipsisTracker += '.'; + possibleEllipsis = true; + } + else + { + ellipsisTracker = ""; // no chance of an ellipsis, so kill it + possibleEllipsis = false; + } + // Yes, and we didn't go over, so store as the last successfully fit word and move on + lastWord = i; + } + } + + if( string[ i ] == 0 ) + lastWord = i; // Final catch for end-of-string + else if( lastWord == 0 && string[ i ] != L'\n' && thisIndent == 0 ) + lastWord = i; // Catch for a single word that didn't fit (just go up as many chars as we can) + // (but NOT if we have a first line indent, mind you :) + + // Got to the end of a line (somewhere), so render up to lastWord, then advance from that point + // to the first non-word-breaker and continue onward + if( lastWord > 0 ) + { + bool drawableWordBreak = IIsDrawableWordBreak((char)(string[lastWord])); + if (drawableWordBreak) + lastWord++; // make sure we draw it + if( ( fRenderInfo.fFlags & kRenderJustXMask ) == kRenderJustXRight ) + { + UInt16 baseX = fRenderInfo.fX, baseMaxW = fRenderInfo.fMaxWidth; + UInt8 *baseDestPtr = fRenderInfo.fDestPtr; + + fRenderInfo.fX = 0; + CharRenderFunc oldFunc = fRenderInfo.fRenderFunc; + fRenderInfo.fRenderFunc = IRenderCharNull; + + IRenderLoop( string, lastWord ); + + fRenderInfo.fRenderFunc = oldFunc; + fRenderInfo.fX = baseX - fRenderInfo.fX; + fRenderInfo.fMaxWidth = baseMaxW; + fRenderInfo.fDestPtr = baseDestPtr + ( fRenderInfo.fX - baseX ) * fRenderInfo.fDestBPP; + + IRenderLoop( string, lastWord ); + + fRenderInfo.fLastX = fRenderInfo.fX; + fRenderInfo.fLastY = fRenderInfo.fY; + fRenderInfo.fX = baseX; + } + else if( ( fRenderInfo.fFlags & kRenderJustXMask ) == kRenderJustXCenter ) + { + UInt16 baseX = fRenderInfo.fX, baseMaxW = fRenderInfo.fMaxWidth; + UInt8 *baseDestPtr = fRenderInfo.fDestPtr; + + fRenderInfo.fX = 0; + CharRenderFunc oldFunc = fRenderInfo.fRenderFunc; + fRenderInfo.fRenderFunc = IRenderCharNull; + + IRenderLoop( string, lastWord ); + + fRenderInfo.fRenderFunc = oldFunc; + fRenderInfo.fX = baseX - ( fRenderInfo.fX >> 1 ); + fRenderInfo.fMaxWidth = baseMaxW; + fRenderInfo.fDestPtr = baseDestPtr + ( fRenderInfo.fX - baseX ) * fRenderInfo.fDestBPP; + + IRenderLoop( string, lastWord ); + + fRenderInfo.fLastX = fRenderInfo.fX; + fRenderInfo.fLastY = fRenderInfo.fY; + fRenderInfo.fX = baseX; + } + else if( ( fRenderInfo.fFlags & kRenderJustXMask ) == kRenderJustXForceLeft ) + { + Int16 baseX = fRenderInfo.fX; + + plCharacter &ch = fCharacters[ (UInt16)string[ 0 ] - fFirstChar ]; + + fRenderInfo.fX -= (Int16)ch.fLeftKern; + fRenderInfo.fDestPtr -= (Int16)ch.fLeftKern * fRenderInfo.fDestBPP; + + IRenderLoop( string, lastWord ); + + fRenderInfo.fLastX = fRenderInfo.fX; + fRenderInfo.fLastY = fRenderInfo.fY; + fRenderInfo.fX = baseX; + } + else + { + IRenderLoop( string, lastWord ); + fRenderInfo.fLastX = fRenderInfo.fX; + fRenderInfo.fLastY = fRenderInfo.fY; + } + if (drawableWordBreak) + lastWord--; // reset it + } + + + // Store farthest X for later reference + if( lastWord > 0 && fRenderInfo.fX > fRenderInfo.fFarthestX ) + fRenderInfo.fFarthestX = fRenderInfo.fX; + + if( firstMaxAscent == 0 ) + firstMaxAscent = fRenderInfo.fMaxAscent; + + x = 0; + fRenderInfo.fX = (Int16)destStartX; + fRenderInfo.fDestPtr = destStartPtr; + fRenderInfo.fMaxWidth = destMaxWidth; + fRenderInfo.fMaxAscent = 0; + + // Look for the next non-word-breaker. Note that if we have any carriage returns hidden in here, + // we'll want to be advancing down even further + // Advance down + if( string[ i ] != 0 ) + { + fRenderInfo.fY += (Int16)lineHt; + fRenderInfo.fMaxHeight -= (Int16)lineHt; + fRenderInfo.fDestPtr += lineDelta; + fRenderInfo.fLastX = fRenderInfo.fX; + fRenderInfo.fLastY = fRenderInfo.fY; + } + for( i = lastWord; string[ i ] != 0 && IIsWordBreaker( (char)(string[ i ]) ) && string[ i ] != L'\n'; i++ ) + { + } + // Process any trailing carriage returns as a separate loop b/c we don't want to throw away white space + // after returns + for( ; string[ i ] == L'\n'; i++ ) + { + // Don't process if i==lastWord, since we already did that one + if( i > lastWord && string[ i ] == L'\n' ) + { + // Advance down + fRenderInfo.fY += (Int16)lineHt; + fRenderInfo.fMaxHeight -= (Int16)lineHt; + fRenderInfo.fDestPtr += lineDelta; + fRenderInfo.fLastY = fRenderInfo.fY; + } + } + + // Keep going from here! + string += i; + } + + fRenderInfo.fMaxAscent = firstMaxAscent; + // Final y offset from the last line + fRenderInfo.fY += (Int16)fFontDescent; + + fRenderInfo.fVolatileStringPtr = string; + } + else + { + if( fRenderInfo.fFlags & kRenderClip ) + { + // Advance left past any clipping area + CharRenderFunc oldFunc = fRenderInfo.fRenderFunc; + fRenderInfo.fRenderFunc = IRenderCharNull; + while( fRenderInfo.fX < fRenderInfo.fClipRect.fX && *string != 0 ) + { + IRenderLoop( string, 1 ); + string++; + } + fRenderInfo.fRenderFunc = oldFunc; + } + + // Adjust for left kern + if( ( fRenderInfo.fFlags & kRenderJustXMask ) == kRenderJustXForceLeft ) + { + // See note at top of file + plCharacter &ch = fCharacters[ (UInt16)string[ 0 ] - fFirstChar ]; + Int32 newX = x - (Int16)ch.fLeftKern; + if( newX < 0 ) + newX = 0; + fRenderInfo.fX = fRenderInfo.fFarthestX = (Int16)newX; + } + + fRenderInfo.fVolatileStringPtr = string; // Just so we can keep track of when we clip + IRenderLoop( string, -1 ); + fRenderInfo.fFarthestX = fRenderInfo.fX; + } +} + +void plFont::IRenderLoop( const wchar_t *string, Int32 maxCount ) +{ + // Render the string straight across, one char at a time + while( *string != 0 && maxCount != 0 ) // Note: if maxCount starts out -1, then it'll just keep + { // decrementing...well ok, not for forever, but pretty close + UInt16 c = (UInt16)*string; + if( c < fFirstChar ) + ; // Invalid char + else + { + c -= fFirstChar; + if( c >= fCharacters.GetCount() ) + ; // Invalid char + else + { + // First pass at supporting left kerning values, but only at the pixel level + Int16 leftKern = (Int16)fCharacters[ c ].fLeftKern; + if( leftKern != 0 ) + { + if( fRenderInfo.fFlags & kRenderScaleAA ) + leftKern /= 2; + + fRenderInfo.fX += leftKern; + fRenderInfo.fMaxWidth -= leftKern; + fRenderInfo.fDestPtr += leftKern * fRenderInfo.fDestBPP; + } + + UInt16 thisWidth = (UInt16)(fWidth + fCharacters[ c ].fRightKern); + if( fRenderInfo.fFlags & kRenderScaleAA ) + thisWidth >>= 1; + + if( thisWidth >= fRenderInfo.fMaxWidth ) + break; + + (this->*(fRenderInfo.fRenderFunc))( fCharacters[ c ] ); + + fRenderInfo.fX += thisWidth; + fRenderInfo.fMaxWidth -= thisWidth; + fRenderInfo.fDestPtr += thisWidth * fRenderInfo.fDestBPP; + + + Int16 baseline = (Int16)(fCharacters[ c ].fBaseline); + if( fRenderInfo.fFlags & kRenderScaleAA ) + baseline >>= 1; + + if( baseline > fRenderInfo.fMaxAscent ) + fRenderInfo.fMaxAscent = baseline; + + Int16 thisHt = (Int16)(fCharacters[ c ].fHeight - baseline); + if( fRenderInfo.fMaxDescent < thisHt ) + fRenderInfo.fMaxDescent = thisHt; + } + } + + string++; + fRenderInfo.fVolatileStringPtr++; + maxCount--; + } +} + +//// The Rendering Functions ////////////////////////////////////////////////// + +void plFont::IRenderChar1To32( const plFont::plCharacter &c ) +{ + UInt8 bitMask, *src = fBMapData + c.fBitmapOff; + UInt32 *destPtr, *destBasePtr = (UInt32 *)( fRenderInfo.fDestPtr - c.fBaseline * fRenderInfo.fDestStride ); + UInt16 x, y; + + + if( (Int32)c.fHeight - (Int32)c.fBaseline >= fRenderInfo.fMaxHeight || c.fBaseline > fRenderInfo.fY ) + return; + + for( y = 0; y < c.fHeight; y++ ) + { + destPtr = destBasePtr; + if( fRenderInfo.fFlags & kRenderItalic ) + { + // Faux italic + destPtr += ( c.fHeight - y ) >> 1; + } + + for( x = 0; x < fRenderInfo.fNumCols; x++ ) + { + for( bitMask = 0x80; bitMask != 0; bitMask >>= 1 ) + { + if( *src & bitMask ) + *destPtr = fRenderInfo.fColor; + destPtr++; + } + src++; + } + destBasePtr = (UInt32 *)( (UInt8 *)destBasePtr + fRenderInfo.fDestStride ); + } +} + +void plFont::IRenderChar1To32AA( const plFont::plCharacter &c ) +{ + UInt8 bitMask, *src = fBMapData + c.fBitmapOff; + UInt32 *destPtr, *destBasePtr = (UInt32 *)( fRenderInfo.fDestPtr - ( c.fBaseline >> 1 ) * fRenderInfo.fDestStride ); + UInt16 x, y; + + + if( ( ( (Int32)c.fHeight - (Int32)c.fBaseline ) >> 1 ) >= fRenderInfo.fMaxHeight || ( c.fBaseline >> 1 ) > fRenderInfo.fY ) + return; + + for( y = 0; y < c.fHeight; y += 2 ) + { + destPtr = destBasePtr; + if( fRenderInfo.fFlags & kRenderItalic ) + { + // Faux italic + destPtr += ( c.fHeight - y ) >> 2; + } + + for( x = 0; x < fRenderInfo.fNumCols; x++ ) + { + for( bitMask = 0x80; bitMask != 0; bitMask >>= 2 ) + { + // Grab 4 bits and do 4-to-1 AA + UInt8 value = ( *src & bitMask ) ? 1 : 0; + value += ( *src & ( bitMask >> 1 ) ) ? 1 : 0; + value += ( src[ fRenderInfo.fNumCols ] & bitMask ) ? 1 : 0; + value += ( src[ fRenderInfo.fNumCols ] & ( bitMask >> 1 ) ) ? 1 : 0; + + switch( value ) + { + case 1: + { + UInt32 src = ( fRenderInfo.fColor >> 2 ) & 0x3f3f3f3f; + UInt32 dst = ( (*destPtr) >> 2 ) & 0x3f3f3f3f; + *destPtr = src + dst + dst + dst; + break; + } + case 2: + { + UInt32 src = ( fRenderInfo.fColor >> 1 ) & 0x7f7f7f7f; + UInt32 dst = ( (*destPtr) >> 1 ) & 0x7f7f7f7f; + *destPtr = src + dst; + break; + } + case 3: + { + UInt32 src = ( fRenderInfo.fColor >> 2 ) & 0x3f3f3f3f; + UInt32 dst = ( (*destPtr) >> 2 ) & 0x3f3f3f3f; + *destPtr = src + src + src + dst; + break; + } + case 4: + *destPtr = fRenderInfo.fColor; + } + + destPtr++; + } + src++; + } + destBasePtr = (UInt32 *)( (UInt8 *)destBasePtr + fRenderInfo.fDestStride ); + src += fRenderInfo.fNumCols; + } +} + +void plFont::IRenderChar8To32( const plFont::plCharacter &c ) +{ + UInt8 *src = fBMapData + c.fBitmapOff; + UInt32 *destPtr, *destBasePtr = (UInt32 *)( fRenderInfo.fDestPtr - c.fBaseline * fRenderInfo.fDestStride ); + UInt16 x, y; + UInt32 srcAlpha, oneMinusAlpha, r, g, b, dR, dG, dB, destAlpha, thisWidth; + UInt8 srcR, srcG, srcB; + + + // Unfortunately for some fonts, their right kern value actually is + // farther left than the right edge of the bitmap (think of overlapping + // script fonts). Ideally, we should store the actual width of each char's + // bitmap and use that here. However, it really shouldn't make too big of a + // difference, especially since the dest pixels that we end up overlapping + // should already be in the cache. If it does, time to upgrade the font + // format (again) + thisWidth = fWidth;// + (Int32)c.fRightKern; + + if( (Int32)c.fHeight - (Int32)c.fBaseline >= fRenderInfo.fMaxHeight || thisWidth >= fRenderInfo.fMaxWidth || c.fBaseline > fRenderInfo.fY ) + return; + + srcR = (UInt8)(( fRenderInfo.fColor >> 16 ) & 0x000000ff); + srcG = (UInt8)(( fRenderInfo.fColor >> 8 ) & 0x000000ff); + srcB = (UInt8)(( fRenderInfo.fColor ) & 0x000000ff); + + for( y = 0; y < c.fHeight; y++ ) + { + destPtr = destBasePtr; + for( x = 0; x < thisWidth; x++ ) + { + if( src[ x ] == 255 ) + destPtr[ x ] = fRenderInfo.fColor; + else if( src[ x ] == 0 ) + ; // Empty + else + { + srcAlpha = ( src[ x ] * ( fRenderInfo.fColor >> 24 ) ) / 255; + oneMinusAlpha = 255 - srcAlpha; + + destAlpha = destPtr[ x ] & 0xff000000; + + dR = ( destPtr[ x ] >> 16 ) & 0x000000ff; + dG = ( destPtr[ x ] >> 8 ) & 0x000000ff; + dB = ( destPtr[ x ] ) & 0x000000ff; + r = ( srcR * srcAlpha ) >> 8; + g = ( srcG * srcAlpha ) >> 8; + b = ( srcB * srcAlpha ) >> 8; + dR = ( dR * oneMinusAlpha ) >> 8; + dG = ( dG * oneMinusAlpha ) >> 8; + dB = ( dB * oneMinusAlpha ) >> 8; + + destPtr[ x ] = ( ( r + dR ) << 16 ) | ( ( g + dG ) << 8 ) | ( b + dB ) | destAlpha; + } + } + destBasePtr = (UInt32 *)( (UInt8 *)destBasePtr + fRenderInfo.fDestStride ); + src += fWidth; + } +} + +void plFont::IRenderChar8To32FullAlpha( const plFont::plCharacter &c ) +{ + UInt8 *src = fBMapData + c.fBitmapOff; + UInt32 *destPtr, *destBasePtr = (UInt32 *)( fRenderInfo.fDestPtr - c.fBaseline * fRenderInfo.fDestStride ); + UInt16 x, y; + UInt32 destColorOnly, thisWidth; + + + // Unfortunately for some fonts, their right kern value actually is + // farther left than the right edge of the bitmap (think of overlapping + // script fonts). Ideally, we should store the actual width of each char's + // bitmap and use that here. However, it really shouldn't make too big of a + // difference, especially since the dest pixels that we end up overlapping + // should already be in the cache. If it does, time to upgrade the font + // format (again) + thisWidth = fWidth;// + (Int32)c.fRightKern; + + if( (Int32)c.fHeight - (Int32)c.fBaseline >= fRenderInfo.fMaxHeight || thisWidth >= fRenderInfo.fMaxWidth || c.fBaseline > fRenderInfo.fY ) + return; + + destColorOnly = fRenderInfo.fColor & 0x00ffffff; + + for( y = 0; y < c.fHeight; y++ ) + { + destPtr = destBasePtr; + for( x = 0; x < thisWidth; x++ ) + { + if( src[ x ] != 0 ) + destPtr[ x ] = ( src[ x ] << 24 ) | destColorOnly; + } + destBasePtr = (UInt32 *)( (UInt8 *)destBasePtr + fRenderInfo.fDestStride ); + src += fWidth; + } +} + +void plFont::IRenderChar8To32Alpha( const plFont::plCharacter &c ) +{ + UInt8 val, *src = fBMapData + c.fBitmapOff; + UInt32 *destPtr, *destBasePtr = (UInt32 *)( fRenderInfo.fDestPtr - c.fBaseline * fRenderInfo.fDestStride ); + UInt16 x, y; + UInt32 destColorOnly, alphaMult, fullAlpha, thisWidth; + + + // Unfortunately for some fonts, their right kern value actually is + // farther left than the right edge of the bitmap (think of overlapping + // script fonts). Ideally, we should store the actual width of each char's + // bitmap and use that here. However, it really shouldn't make too big of a + // difference, especially since the dest pixels that we end up overlapping + // should already be in the cache. If it does, time to upgrade the font + // format (again) + thisWidth = fWidth;// + (Int32)c.fRightKern; + + if( (Int32)c.fHeight - (Int32)c.fBaseline >= fRenderInfo.fMaxHeight || thisWidth >= fRenderInfo.fMaxWidth || c.fBaseline > fRenderInfo.fY ) + return; + + destColorOnly = fRenderInfo.fColor & 0x00ffffff; + // alphaMult should come out to be a value to satisfy (fontAlpha * alphaMult >> 8) as the right alpha, + // but then we want it so (fontAlpha * alphaMult) will be in the upper 8 bits + fullAlpha = fRenderInfo.fColor & 0xff000000; + alphaMult = fullAlpha / 255; + + for( y = 0; y < c.fHeight; y++ ) + { + destPtr = destBasePtr; + for( x = 0; x < thisWidth; x++ ) + { + val = src[ x ]; + if( val == 0xff ) + destPtr[ x ] = fullAlpha | destColorOnly; + else if( val != 0 ) + { + destPtr[ x ] = ( ( alphaMult * val ) & 0xff000000 ) | destColorOnly; + } + } + destBasePtr = (UInt32 *)( (UInt8 *)destBasePtr + fRenderInfo.fDestStride ); + src += fWidth; + } +} + + +void plFont::IRenderCharNull( const plCharacter &c ) +{ +} + +//// CalcString Variations //////////////////////////////////////////////////// + +UInt16 plFont::CalcStringWidth( const char *string ) +{ + UInt16 w, h, a, lX, lY; + UInt32 s; + CalcStringExtents( string, w, h, a, s, lX, lY ); + return w; +} + +UInt16 plFont::CalcStringWidth( const wchar_t *string ) +{ + UInt16 w, h, a, lX, lY; + UInt32 s; + CalcStringExtents( string, w, h, a, s, lX, lY ); + return w; +} + +void plFont::CalcStringExtents( const char *string, UInt16 &width, UInt16 &height, UInt16 &ascent, UInt32 &firstClippedChar, UInt16 &lastX, UInt16 &lastY ) +{ + // convert the char string to a wchar_t string + wchar_t *wideString = hsStringToWString(string); + CalcStringExtents(wideString,width,height,ascent,firstClippedChar,lastX,lastY); + delete [] wideString; +} + +void plFont::CalcStringExtents( const wchar_t *string, UInt16 &width, UInt16 &height, UInt16 &ascent, UInt32 &firstClippedChar, UInt16 &lastX, UInt16 &lastY ) +{ + IRenderString( nil, 0, 0, string, true ); + width = fRenderInfo.fFarthestX; + height = (UInt16)(fRenderInfo.fY + fFontDescent);//fRenderInfo.fMaxDescent; + ascent = fRenderInfo.fMaxAscent; + lastX = fRenderInfo.fLastX; + lastY = fRenderInfo.fLastY; + + // firstClippedChar is an index into the given string that points to the start of the part of the string + // that got clipped (i.e. not rendered). + firstClippedChar = (UInt32)fRenderInfo.fVolatileStringPtr - (UInt32)string; + firstClippedChar /= 2; // divide by 2 because a wchar_t is two bytes wide, instead of one (like a char) +} + +//// IGetFreeCharData ///////////////////////////////////////////////////////// +// Used for constructing fonts one character at a time; finds a pointer to +// the first free space in our allocated font bitmap, based on the heights +// and offsets of all the current characters + +UInt8 *plFont::IGetFreeCharData( UInt32 &newOffset ) +{ + Int32 i; + + + newOffset = 0; + for( i = fCharacters.GetCount() - 1; i >= 0; i-- ) + { + UInt32 thisOff = fCharacters[ i ].fBitmapOff + ( ( fCharacters[ i ].fHeight * fWidth * fBPP ) >> 3 ); + if( newOffset < thisOff ) + { + newOffset = thisOff; + break; + } + } + + if( newOffset >= ( ( fWidth * fHeight * fBPP ) >> 3 ) ) + { + hsAssert( false, "Invalid position found in IGetFreeCharData()" ); + return nil; + } + + // Return pointer to the new area + return fBMapData + newOffset; +} + +//// LoadFromP2FFile ////////////////////////////////////////////////////////// +// Handy quick wrapper + +hsBool plFont::LoadFromP2FFile( const char *path ) +{ + hsUNIXStream stream; + if( stream.Open( path, "rb" ) ) + { + ReadRaw( &stream ); + return true; + } + return false; +} + +//// LoadFromFNT ////////////////////////////////////////////////////////////// +// Load this font from the data found in the given Windows FNT file, +// using the format specified in the Windows 3 Developers Notes. + +hsBool plFont::LoadFromFNT( const char *path ) +{ + hsUNIXStream stream; // Ahh, irony + if( !stream.Open( path, "rb" ) ) + return false; + + return LoadFromFNTStream( &stream ); +} + +hsBool plFont::LoadFromFNTStream( hsStream *stream ) +{ + IClear(); + + try + { + + // Note: hsUNIXStreams just happen to store in the same endian as Windows... + struct FNTInfo + { + UInt16 version; + UInt32 size; + char copyright[ 60 ]; + UInt16 type; + UInt16 points; + UInt16 vertRes; + UInt16 horzRes; + UInt16 ascent; + UInt16 internalLeading; + UInt16 externalLeading; + UInt8 italic, underline, strikeout; + UInt16 weight; + UInt8 charSet; + UInt16 pixWidth; // 0 means variable width chars + UInt16 pixHeight; + UInt8 pitchFamily; + UInt16 avgWidth; + UInt16 maxWidth; + UInt8 firstChar, lastChar, defaultChar, breakChar; + UInt16 widthBytes; + UInt32 device, face; + UInt32 bitsPointer, bitsOffset; + UInt8 reserved; + UInt32 flags; + UInt16 aSpace, bSpace, cSpace; + UInt32 colorPointer; + UInt8 reserved1[ 16 ]; + + void Read( hsStream *s ) + { + version = s->ReadSwap16(); + size = s->ReadSwap32(); + + s->Read( sizeof( copyright ), copyright ); + + s->ReadSwap( &type ); + s->ReadSwap( &points ); + s->ReadSwap( &vertRes ); + s->ReadSwap( &horzRes ); + s->ReadSwap( &ascent ); + s->ReadSwap( &internalLeading ); + s->ReadSwap( &externalLeading ); + s->ReadSwap( &italic ); + s->ReadSwap( &underline ); + s->ReadSwap( &strikeout ); + s->ReadSwap( &weight ); + s->ReadSwap( &charSet ); + s->ReadSwap( &pixWidth ); + s->ReadSwap( &pixHeight ); + s->ReadSwap( &pitchFamily ); + s->ReadSwap( &avgWidth ); + s->ReadSwap( &maxWidth ); + s->ReadSwap( &firstChar ); + s->ReadSwap( &lastChar ); + s->ReadSwap( &defaultChar ); + s->ReadSwap( &breakChar ); + s->ReadSwap( &widthBytes ); + s->ReadSwap( &device ); + s->ReadSwap( &face ); + s->ReadSwap( &bitsPointer ); + s->ReadSwap( &bitsOffset ); + s->ReadSwap( &reserved ); + if( version == 0x0300 ) + { + s->ReadSwap( &flags ); + s->ReadSwap( &aSpace ); + s->ReadSwap( &bSpace ); + s->ReadSwap( &cSpace ); + s->ReadSwap( &colorPointer ); + s->Read( sizeof( reserved1 ), reserved1 ); + } + else + { + flags = 0; + aSpace = bSpace = cSpace = 0; + colorPointer = 0; + } + } + } fntInfo; + + fntInfo.Read( stream ); + + struct charEntry + { + UInt16 width; + UInt32 offset; + charEntry() { width = 0; offset = 0; } + } *charEntries; + + int i, count = fntInfo.lastChar - fntInfo.firstChar + 2; + charEntries = TRACKED_NEW charEntry[ count ]; + for( i = 0; i < count; i++ ) + { + charEntries[ i ].width = stream->ReadSwap16(); + if( fntInfo.version == 0x0200 ) + charEntries[ i ].offset = stream->ReadSwap16(); + else + charEntries[ i ].offset = stream->ReadSwap32(); + } + + char faceName[ 256 ], deviceName[ 256 ]; + if( fntInfo.face != 0 ) + { + stream->SetPosition( fntInfo.face ); + for( i = 0; i < 256; i++ ) + { + faceName[ i ] = stream->ReadByte(); + if( faceName[ i ] == 0 ) + break; + } + strncpy( fFace, faceName, sizeof( fFace ) ); + } + if( fntInfo.device != 0 ) + { + stream->SetPosition( fntInfo.device ); + for( i = 0; i < 256; i++ ) + { + deviceName[ i ] = stream->ReadByte(); + if( deviceName[ i ] == 0 ) + break; + } + } + fSize = (UInt8)(fntInfo.points); + + // Figure out what we need to allocate our bitmap as + fWidth = 0; + fHeight = 0; + for( i = 0; i < count; i++ ) + { + if( fWidth < charEntries[ i ].width ) + fWidth = charEntries[ i ].width; + if( charEntries[ i ].offset > 0 ) + fHeight += fntInfo.pixHeight; + } + fBPP = 1; + + // Since we're 1 bbp, make sure width is a multiple of 8 + fWidth = ( ( fWidth + 7 ) >> 3 ) << 3; + + UInt32 widthInBytes = ( fWidth * fBPP ) >> 3; + + // Allocate our bitmap now + UInt32 size = widthInBytes * fHeight; + fBMapData = TRACKED_NEW UInt8[ size ]; + memset( fBMapData, 0, size ); + + fFirstChar = fntInfo.firstChar; + fMaxCharHeight = 0; + + // Read the bitmap info + UInt32 destOff = 0; + for( i = 0; i < count; i++ ) + { + int numCols = ( charEntries[ i ].width + 7 ) >> 3; + int height = fntInfo.pixHeight; + + // Write a record for this char + plCharacter outChar; + outChar.fBitmapOff = destOff; + outChar.fHeight = ( charEntries[ i ].offset == 0 ) ? 0 : height; + outChar.fBaseline = fntInfo.ascent; + outChar.fLeftKern = 0.f; + outChar.fRightKern = (hsScalar)(charEntries[ i ].width - fWidth); + fCharacters.Append( outChar ); + + if( outChar.fHeight > fMaxCharHeight ) + fMaxCharHeight = outChar.fHeight; + + if( charEntries[ i ].offset == 0 ) + continue; + + // Seek to this char + stream->SetPosition( charEntries[ i ].offset ); + + // Write the actual bitmap data. Note: FNTs store the bits one column at a time, + // one col after another, whereas we want it plain row by row + int col, y; + UInt8 *basePtr = fBMapData + destOff; + for( col = 0; col < numCols; col++ ) + { + UInt8 *yPtr = basePtr; + for( y = 0; y < height; y++ ) + { + *yPtr = stream->ReadByte(); + yPtr += widthInBytes; + } + basePtr++; + } + + destOff += widthInBytes * outChar.fHeight; + } + + delete [] charEntries; + + ICalcFontAscent(); + return true; + } + catch( ... ) + { + // Somehow we crashed converting! + IClear(); + return false; + } +} + +//// LoadFromBDF ////////////////////////////////////////////////////////////// +// Load this font from the data found in the given Adobe Systems BDF file, +// using the format specified in the Glyph Bitmap Distribution Format (BDF) +// Specification Version 2.2 from Adobe Systems. + +/*hsBool plFont::LoadFromBDF( const char *path, plBDFConvertCallback *callback ) +{ + hsUNIXStream stream; // Ahh, irony + if( !stream.Open( path, "rb" ) ) + return false; + + return LoadFromBDFStream( &stream, callback ); +} +*/ +// Some parsing helpers +typedef int (*fnCharTester)( int ); + +static int sQuoteTester( int c ) +{ + return ( c == '\"' ); +} + +static int sDashTester( int c ) +{ + return isspace( c ) || ( c == '-' ); +} + +class plLineParser +{ + protected: + static char fLine[ 512 ]; + + char *fCursor, fRestore; + + void IAdvanceToNextToken( fnCharTester tester = isspace ) + { + // Last cursor + *fCursor = fRestore; + + // Scan for the start of the next token + while( *fCursor != 0 && (*tester)( *fCursor ) ) + fCursor++; + + if( *fCursor == 0 ) + return; + + // This is the start of our token + const char *start = fCursor; + + // Put a stopper here + fRestore = *fCursor; + *fCursor = 0; + + // And return! + return; + } + + const char *IGetNextToken( fnCharTester tester = isspace ) + { + // Last cursor + *fCursor = fRestore; + + // Scan for the start of the next token + while( *fCursor != 0 && (*tester)( *fCursor ) ) + fCursor++; + + if( *fCursor == 0 ) + return nil; + + // This is the start of our token; find the end + const char *start = fCursor; + while( *fCursor != 0 && !(*tester)( *fCursor ) ) + fCursor++; + + // Put a stopper here + fRestore = *fCursor; + *fCursor = 0; + + // And return! + return start; + } + + public: + + plLineParser( const char *line ) + { + strncpy( fLine, line, sizeof( fLine ) ); + fCursor = fLine; + fRestore = *fCursor; + } + + ~plLineParser() { } + + const char *GetKeyword( void ) + { + return IGetNextToken(); + } + + const char *GetString( void ) + { + IAdvanceToNextToken(); + return IGetNextToken( sQuoteTester ); + } + + const char *GetKeywordNoDashes( void ) + { + return IGetNextToken( sDashTester ); + } + + Int32 GetInt( void ) + { + return atoi( IGetNextToken() ); + } + + hsScalar GetFloat( void ) + { + return (hsScalar)atof( IGetNextToken() ); + } +}; + +char plLineParser::fLine[ 512 ]; + + +// Another helper--parser for various sections of the BDF format +class plBDFSectParser +{ + protected: + plFont &fFont; + plBDFConvertCallback *fCallback; + + public: + plBDFSectParser( plFont &myFont, plBDFConvertCallback *callback ) : fFont( myFont ), fCallback( callback ) {} + + virtual plBDFSectParser *ParseKeyword( const char *keyword, plLineParser &line ) + { + return nil; + } +}; + + +class plBDFLookForEndCharParser : public plBDFSectParser +{ + public: + plBDFLookForEndCharParser( plFont &myFont, plBDFConvertCallback *callback ) : plBDFSectParser( myFont, callback ) {} + + virtual plBDFSectParser *ParseKeyword( const char *keyword, plLineParser &line ); +}; + +inline UInt8 iHexCharToByte( char c ) +{ + switch( c ) + { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': case 'A': return 10; + case 'b': case 'B': return 11; + case 'c': case 'C': return 12; + case 'd': case 'D': return 13; + case 'e': case 'E': return 14; + case 'f': case 'F': return 15; + default: return 0; + } +} +inline UInt8 iHexStringToByte( const char *str ) +{ + return ( iHexCharToByte( str[ 0 ] ) << 4 ) | iHexCharToByte( str[ 1 ] ); +} + +class plBDFCharsParser : public plBDFSectParser +{ + protected: + + // Info about the current character we're translating + UInt16 fWhichChar, fRowsLeft; + plFont::plCharacter *fCharacter; + UInt8 *fBitmap; + hsBool fDoingData; + UInt32 fBytesWide, fBMapStride; + + inline void IReset( void ) + { + fBitmap = nil; + fCharacter = nil; + fWhichChar = 0; + fDoingData = false; + } + + public: + static UInt32 fResolution; + + plBDFCharsParser( plFont &myFont, plBDFConvertCallback *callback ) : plBDFSectParser( myFont, callback ), fDoingData( false ) {} + + virtual plBDFSectParser *ParseKeyword( const char *keyword, plLineParser &line ) + { + if( strcmp( keyword, "ENDFONT" ) == 0 ) + { + // All done! + return nil; + } + else if( strcmp( keyword, "ENDCHAR" ) == 0 ) + { + // End of the character, reset + IReset(); + if( fCallback != nil ) + fCallback->CharDone(); + } + else if( fDoingData ) + { + // If we're doing data, all lines are hex values until we hit "ENDCHAR" + if( fRowsLeft == 0 ) + throw; + + int hDigit; + for( hDigit = 0; *keyword != 0 && hDigit < fBytesWide; hDigit++, keyword += 2 ) + { + *fBitmap = iHexStringToByte( keyword ); + fBitmap++; + } + + fBitmap += fBMapStride - hDigit; + fRowsLeft--; + } + else if( strcmp( keyword, "STARTCHAR" ) == 0 ) + { + // Start of a new character--ignore the name, we'll just use the encoding + IReset(); + } + else if( strcmp( keyword, "ENCODING" ) == 0 ) + { + int ch = line.GetInt(); + if( ch == -1 ) + { + // Nonstandard encoding, skip the character entirely + return TRACKED_NEW plBDFLookForEndCharParser( fFont, fCallback ); + } + else + { + fWhichChar = (UInt16)(UInt8)ch; + + // Set up our pointer. Note that since BDFs don't tell us the starting character, + // we just make it 0 + if( fFont.fCharacters.GetCount() < fWhichChar + 1 ) + fFont.fCharacters.SetCount( fWhichChar + 1 ); + + fCharacter = &fFont.fCharacters[ fWhichChar ]; + } + } + // Horizontal mode offsets (writing mode 0) + else if( strcmp( keyword, "SWIDTH" ) == 0 ) + { + // In device units, unused +// float fWidth = (float)line.GetInt() * ( (float)fFont.GetSize() / 1000.f ) * ( fResolution / 72.f ); +// fCharacter->fRightKern = fWidth - (float)fFont.fWidth; +// fCharacter->fLeftKern = 0.f; + } + else if( strcmp( keyword, "DWIDTH" ) == 0 ) + { + // In pixels, basically the offset to the next char. Note that we only + // support the X direction (altho admittedly the idea of a font whose characters cause + // the next chars to go up or down is way nifty) + fCharacter->fRightKern = (float)line.GetInt() - (float)fFont.fWidth; + fCharacter->fLeftKern = 0.f; + } + // Vertical mode offsets (unsupported) + else if( strcmp( keyword, "SWIDTH1" ) == 0 ) + { + + } + else if( strcmp( keyword, "DWIDTH1" ) == 0 ) + { + + } + // Bitmap bounding box + else if( strcmp( keyword, "BBX" ) == 0 ) + { + int pixW = line.GetInt(); + int pixH = line.GetInt(); + int xOff = line.GetInt(); + int yOff = line.GetInt(); + + // Got enough info now to allocate us a bitmap for this char + fBitmap = fFont.IGetFreeCharData( fCharacter->fBitmapOff ); + if( fBitmap == nil ) + throw false; + + if( fCharacter->fBitmapOff > ( ( fFont.fWidth * ( fFont.fHeight - pixH ) * fFont.fBPP ) >> 3 ) ) + { + hsAssert( false, "Invalid position found in IGetFreeCharData()" ); + return nil; + } + + // Set these now, since setting them before would've changed the IGetFreeCharData results + fCharacter->fLeftKern = (hsScalar)xOff; + fCharacter->fBaseline = yOff + pixH; + fCharacter->fHeight = fRowsLeft = pixH; + + fBytesWide = ( pixW + 7 ) >> 3; + fBMapStride = fFont.fWidth >> 3; + } + // Bitmap data + else if( strcmp( keyword, "BITMAP" ) == 0 ) + { + fDoingData = true; + } + // This keyword is outputted from the "getbdf" utility on linux, for some reason... + else if( strcmp( keyword, "ATTRIBUTES" ) == 0 ) + { + + } + else + throw false; // Invalid keyword + + return this; + } +}; + +UInt32 plBDFCharsParser::fResolution = 72; + + +plBDFSectParser *plBDFLookForEndCharParser::ParseKeyword( const char *keyword, plLineParser &line ) +{ + if( strcmp( keyword, "ENDCHAR" ) == 0 ) + { + // Horray! + return TRACKED_NEW plBDFCharsParser( fFont, fCallback ); + } + // Just gobble lines until we find the endchar section + return this; +} + +class plBDFLookForCharParser : public plBDFSectParser +{ + public: + plBDFLookForCharParser( plFont &myFont, plBDFConvertCallback *callback ) : plBDFSectParser( myFont, callback ) {} + + virtual plBDFSectParser *ParseKeyword( const char *keyword, plLineParser &line ) + { + if( strcmp( keyword, "CHARS" ) == 0 ) + { + // Horray! + return TRACKED_NEW plBDFCharsParser( fFont, fCallback ); + } + // Just gobble lines until we find the chars section + return this; + } +}; + +class plBDFPropertiesParser : public plBDFSectParser +{ + public: + plBDFPropertiesParser( plFont &myFont, plBDFConvertCallback *callback ) : plBDFSectParser( myFont, callback ) {} + + virtual plBDFSectParser *ParseKeyword( const char *keyword, plLineParser &line ) + { + // Note: the properties section is entirely optional and arbitrary, but we + // parse it in case we can get more accurate info about the font name and props + if( strcmp( keyword, "FACE_NAME" ) == 0 ) + { + fFont.SetFace( line.GetString() ); + } + else if( strcmp( keyword, "WEIGHT_NAME" ) == 0 ) + { + if( stricmp( line.GetString(), "Bold" ) == 0 ) + fFont.SetFlag( plFont::kFlagBold, true ); + } + else if( strcmp( keyword, "ENDPROPERTIES" ) == 0 ) + { + // Switch to waiting for the chars section + return TRACKED_NEW plBDFLookForCharParser( fFont, fCallback ); + } + // All tokens are technically valid, even if we don't recognize them + return this; + } +}; + +class plBDFHeaderParser : public plBDFSectParser +{ + public: + plBDFHeaderParser( plFont &myFont, plBDFConvertCallback *callback ) : plBDFSectParser( myFont, callback ) {} + + virtual plBDFSectParser *ParseKeyword( const char *keyword, plLineParser &line ) + { + if( strcmp( keyword, "STARTFONT" ) == 0 ) + { + // Start of the font; check version # + float version = line.GetFloat(); + if( version < 2.1f ) + throw false; + + // Initial font values + fFont.fFirstChar = 0; + fFont.fMaxCharHeight = 0; + } + else if( strcmp( keyword, "FONT" ) == 0 ) + { + // Postscript-style font name: figure out our face name and hopefully attributes too + // Format is usually of the form: vendor-face-weight-otherStuff + const char *vendor = line.GetKeywordNoDashes(); + const char *face = line.GetKeywordNoDashes(); + + fFont.SetFace( face != nil ? face : vendor ); + + const char *weight = line.GetKeywordNoDashes(); + + if( weight != nil && stricmp( weight, "Bold" ) == 0 ) + fFont.SetFlag( plFont::kFlagBold, true ); + } + else if( strcmp( keyword, "COMMENT" ) == 0 ) + { + // Comment, ignore + } + else if( strcmp( keyword, "CONTENTVERSION" ) == 0 ) + { + // Content version, app specific, not used by us + } + else if( strcmp( keyword, "SIZE" ) == 0 ) + { + // Point size and target resolution of this font + fFont.SetSize( (UInt8)(line.GetInt()) ); + plBDFCharsParser::fResolution = line.GetInt(); + } + else if( strcmp( keyword, "FONTBOUNDINGBOX" ) == 0 ) + { + // Max dimensions and base offsets + // Note that we should've already grabbed the width from our + // prescan earlier, to guard against malformed BDFs + int thisWidth = line.GetInt(); + if( fFont.fWidth < thisWidth ) + fFont.fWidth = thisWidth; + + // Since we're 1 bbp, make sure width is a multiple of 8 + fFont.fWidth = ( ( fFont.fWidth + 7 ) >> 3 ) << 3; + + // Allocate our data now + fFont.fBMapData = TRACKED_NEW UInt8[ ( fFont.fWidth * fFont.fHeight * fFont.fBPP ) >> 3 ]; + memset( fFont.fBMapData, 0, ( fFont.fWidth * fFont.fHeight * fFont.fBPP ) >> 3 ); + + fFont.fMaxCharHeight = line.GetInt(); + } + else if( strcmp( keyword, "METRICSSET" ) == 0 ) + { + // Metrics set specification; unsupported + } + else if( strcmp( keyword, "STARTPROPERTIES" ) == 0 ) + { + // Start of the properties section, switch to that + return TRACKED_NEW plBDFPropertiesParser( fFont, fCallback ); + } + else if( strcmp( keyword, "CHARS" ) == 0 ) + { + // Allocate our bitmap if we haven't already + + // Since we're 1 bbp, make sure width is a multiple of 8 + if( fFont.fBMapData == nil ) + { + fFont.fWidth = ( ( fFont.fWidth + 7 ) >> 3 ) << 3; + + // Allocate our data now + fFont.fBMapData = TRACKED_NEW UInt8[ ( fFont.fWidth * fFont.fHeight * fFont.fBPP ) >> 3 ]; + memset( fFont.fBMapData, 0, ( fFont.fWidth * fFont.fHeight * fFont.fBPP ) >> 3 ); + } + + // Start of the char section + return TRACKED_NEW plBDFCharsParser( fFont, fCallback ); + } + else + { + // Unrecognized token, abort + throw false; + } + + // Default, keep going with our parser + return this; + } +}; + +class plBDFCheckDimsParser : public plBDFSectParser +{ + hsBool fSkipNext; + + public: + + UInt32 fMaxWidth, fMaxHeight, fNumChars, fTotalHeight; + UInt16 fMaxChar; + + plBDFCheckDimsParser( plFont &myFont ) : plBDFSectParser( myFont, nil ) { fMaxWidth = fMaxHeight = fNumChars = fTotalHeight = 0; fSkipNext = false; fMaxChar = 0; } + + virtual plBDFSectParser *ParseKeyword( const char *keyword, plLineParser &line ) + { + if( strcmp( keyword, "ENDFONT" ) == 0 ) + { + // All done! + return nil; + } + // Encoding + else if( strcmp( keyword, "ENCODING" ) == 0 ) + { + int ch = line.GetInt(); + if( ch == -1 ) + // We skip these + fSkipNext = true; + else + { + fSkipNext = false; + if( fMaxChar < ch ) + fMaxChar = ch; + } + } + // Bitmap bounding box + else if( strcmp( keyword, "BBX" ) == 0 ) + { + if( fSkipNext ) + return this; + + int pixW = line.GetInt(); + int pixH = line.GetInt(); + int xOff = line.GetInt(); + int yOff = line.GetInt(); + + if( fMaxWidth < pixW ) + fMaxWidth = pixW; + if( fMaxHeight < pixH ) + fMaxHeight = pixH; + + fNumChars++; + + fTotalHeight += pixH; + } + return this; + } +}; + +hsBool plFont::LoadFromBDFStream( hsStream *stream, plBDFConvertCallback *callback ) +{ + return false; +} + +hsBool plFont::LoadFromBDF( const char *path, plBDFConvertCallback *callback ) +{ + FILE *fp = fopen( path, "rt" ); + if( fp == nil ) + return false; + try + { + IClear(); + + char line[ 512 ]; + + // Run through the entire file first with a plBDFCheckDimsParser. This is because + // some BDFs are naughty and don't report the correct fontboundingbox (i.e. too small) + // (See the following loop below for details on the workings of this loop) + plBDFCheckDimsParser checkDims( *this ); + while( fgets( line, sizeof( line ), fp ) ) + { + plLineParser parser( line ); + const char *keyword = parser.GetKeyword(); + if( keyword != nil ) + { + if( checkDims.ParseKeyword( keyword, parser ) == nil ) + break; + } + } + + // Set up initial values + fWidth = checkDims.fMaxWidth; + fHeight = checkDims.fTotalHeight; + fBPP = 1; + + // Pre-expand our char list + fCharacters.ExpandAndZero( checkDims.fMaxChar + 1 ); + fCharacters.SetCount( 0 ); + + if( callback != nil ) + callback->NumChars( (UInt16)(checkDims.fNumChars) ); + + // Rewind and continue normally + fseek( fp, 0, SEEK_SET ); + + // Start with the header parser + plBDFSectParser *currParser = TRACKED_NEW plBDFHeaderParser( *this, callback ); + + // Read from the stream one line at a time (don't want comments, and char #1 should be invalid for a BDF) + while( fgets( line, sizeof( line ), fp ) && currParser != nil ) + { + // Parse this one + plLineParser parser( line ); + + // Get the keyword for this line + const char *keyword = parser.GetKeyword(); + if( keyword != nil ) + { + // Pass on to the current parser + plBDFSectParser *newParser = currParser->ParseKeyword( keyword, parser ); + if( newParser != currParser ) + { + delete currParser; + currParser = newParser; + } + } + } + } + catch( ... ) + { + IClear(); + return false; + } + + fclose( fp ); + + ICalcFontAscent(); + return true; +} + +hsBool plFont::ReadRaw( hsStream *s ) +{ + s->Read( sizeof( fFace ), fFace ); + fSize = s->ReadByte(); + s->ReadSwap( &fFlags ); + + s->ReadSwap( &fWidth ); + s->ReadSwap( &fHeight ); + s->ReadSwap( &fMaxCharHeight ); + + fBPP = s->ReadByte(); + + UInt32 size = ( fWidth * fHeight * fBPP ) >> 3; + if( size > 0 ) + { + fBMapData = TRACKED_NEW UInt8[ size ]; + s->Read( size, fBMapData ); + } + else + fBMapData = nil; + + s->ReadSwap( &fFirstChar ); + + UInt32 i; + fCharacters.SetCountAndZero( s->ReadSwap32() ); + for( i = 0; i < fCharacters.GetCount(); i++ ) + fCharacters[ i ].Read( s ); + + ICalcFontAscent(); + + return true; +} + +hsBool plFont::WriteRaw( hsStream *s ) +{ + s->Write( sizeof( fFace ), fFace ); + s->WriteByte( fSize ); + s->WriteSwap( fFlags ); + + s->WriteSwap( fWidth ); + s->WriteSwap( fHeight ); + s->WriteSwap( fMaxCharHeight ); + + s->WriteByte( fBPP ); + + UInt32 size = ( fWidth * fHeight * fBPP ) >> 3; + if( size > 0 ) + s->Write( size, fBMapData ); + + s->WriteSwap( fFirstChar ); + + UInt32 i; + s->WriteSwap32( fCharacters.GetCount() ); + for( i = 0; i < fCharacters.GetCount(); i++ ) + fCharacters[ i ].Write( s ); + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFont.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFont.h new file mode 100644 index 00000000..744d439f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFont.h @@ -0,0 +1,282 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plFont Class Header // +// Seems like we've come full circle again. This is our generic Plasma // +// bitmap font class/format. Quick list of things it supports, or needs to: // +// - Antialiasing, either in the font def or at rendertime // +// - Doublebyte character sets // +// - Platform independence, of course // +// - Render to reasonably arbitrary mipmap // +// - Character-level kerning, both before and after, as well as // +// negative kerning (for ligatures) // +// // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 5.4.2003 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plFont_h +#define _plFont_h + +#include "hsTypes.h" +#include "hsColorRGBA.h" +#include "hsTemplates.h" +#include "pcSmallRect.h" + +#include "../pnKeyedObject/hsKeyedObject.h" + +#include + +class plBDFConvertCallback +{ + public: + virtual void NumChars( UInt16 chars ) {} + virtual void CharDone( void ) {} +}; + +//// Class Definition ///////////////////////////////////////////////////////// + +class plMipmap; +class plBDFHeaderParser; +class plBDFPropertiesParser; +class plBDFCharsParser; +class plFont : public hsKeyedObject +{ + public: + + enum RenderFlags + { + kRenderItalic = 0x00000001, + kRenderBold = 0x00000002, + kRenderScaleAA = 0x00000004, + kRenderClip = 0x00000008, + kRenderWrap = 0x00000010, + + kRenderJustXLeft = 0, + kRenderJustXRight = 0x00000020, // X right justify + kRenderJustXCenter = 0x00000040, // X center justify (left default) + kRenderJustXForceLeft=0x00000080, // Force left (no kerning) + kRenderJustXMask = 0x000000e0, + + kRenderJustYBaseline = 0, + kRenderJustYTop = 0x00000100, // Y top justify + kRenderJustYCenter = 0x00000200, // Y center justify + kRenderJustYBottom = 0x00000400, // Y bottom justify (baseline is default) + kRenderJustYMask = 0x00000700, + + kRenderIntoAlpha = 0x00000800, // This option causes grayscale (AA) fonts to + // render into the alpha channel instead of the color + // channel, so that the resulting pixels touched always + // have the renderColor and the alpha = to the font pixel. + // By default, we use the font pixel as an alpha blending + // value between the renderColor and the destColor and + // leave the alpha as-is + // This flag has no effect on monochrome fonts + }; + + enum Flags + { + kFlagBold = 0x00000001, + kFlagItalic = 0x00000002 + }; + + protected: + + friend class plBDFHeaderParser; + friend class plBDFPropertiesParser; + friend class plBDFCharsParser; + + // Font face and size. This is just used for IDing purposes, not for rendering + char fFace[ 256 ]; + UInt8 fSize; + UInt32 fFlags; + + // Size of the whole font bitmap. Fonts are stored vertically, one + // character at a time, so fWidth is really the max width of any one + // character bitmap, with of course the proper padding + UInt32 fWidth, fHeight; + + // Bpp of our font bitmap. We're grayscale, remember... + UInt8 fBPP; + + // Bitmap data! + UInt8 *fBMapData; + + // Our character class, for per-char info + class plCharacter + { + public: + UInt32 fBitmapOff; // Offset in the font bitmap in bytes + // to the first byte of the character + UInt32 fHeight; // Height in pixels of this character + Int32 fBaseline; // Number of pixels down from the top of + // the char bitmap to the baseline. + + hsScalar fLeftKern; // Kerning values for this char, in pixels + hsScalar fRightKern; // Note that the right kern is relative to + // the right side of the bitmap area, which + // is the width of the font bitmap, so + // basically each character is the same width, + // just kerned back! + // (left kerning currently unsupported, just + // in here in case we need to eventually) + + plCharacter& operator=(const int zero) + { + fBitmapOff=0; + fHeight = 0; + fBaseline = 0; + fLeftKern = 0; + fRightKern = 0; + + return *this; + } + + plCharacter(); + void Read( hsStream *s ); + void Write( hsStream *s ); + }; + + // First character we encode--everything below this we don't render + UInt16 fFirstChar; + + // Our characters, stored in an hsTArray for easy construction + hsTArray fCharacters; + + // Max character bitmap height and max ascent for any character + UInt32 fMaxCharHeight; + Int32 fFontAscent, fFontDescent; + + typedef void (plFont::*CharRenderFunc)( const plCharacter &c ); + + // Render info + class plRenderInfo + { + public: + Int16 fFirstLineIndent; + Int16 fX, fY, fNumCols, fFarthestX, fLastX, fLastY; + Int16 fMaxWidth, fMaxHeight, fMaxAscent, fMaxDescent; + Int16 fLineSpacing; + UInt8 *fDestPtr; + UInt32 fDestStride; + UInt8 fDestBPP; + UInt32 fColor; + plMipmap *fMipmap; + UInt32 fFlags; + pcSmallRect fClipRect; + hsScalar fFloatWidth; + + const wchar_t *fVolatileStringPtr; // Just so we know where we clipped + + CharRenderFunc fRenderFunc; + }; + + plRenderInfo fRenderInfo; + + void IClear( bool onConstruct = false ); + void ICalcFontAscent( void ); + + UInt8 *IGetFreeCharData( UInt32 &newOffset ); + + void IRenderLoop( const wchar_t *string, Int32 maxCount ); + void IRenderString( plMipmap *mip, UInt16 x, UInt16 y, const wchar_t *string, hsBool justCalc ); + + // Various render functions + void IRenderChar1To32( const plCharacter &c ); + void IRenderChar1To32AA( const plCharacter &c ); + void IRenderChar8To32( const plCharacter &c ); + void IRenderChar8To32Alpha( const plCharacter &c ); + void IRenderChar8To32FullAlpha( const plCharacter &c ); + void IRenderCharNull( const plCharacter &c ); + + public: + + plFont(); + virtual ~plFont(); + + CLASSNAME_REGISTER( plFont ); + GETINTERFACE_ANY( plFont, hsKeyedObject ); + + virtual void Read( hsStream *s, hsResMgr *mgr ); + virtual void Write( hsStream *s, hsResMgr *mgr ); + + const char *GetFace( void ) const { return fFace; } + UInt8 GetSize( void ) const { return fSize; } + UInt16 GetFirstChar( void ) const { return fFirstChar; } + UInt16 GetNumChars( void ) const { return fCharacters.GetCount(); } + UInt32 GetFlags( void ) const { return fFlags; } + hsScalar GetDescent( void ) const { return (hsScalar)fFontDescent; } + hsScalar GetAscent( void ) const { return (hsScalar)fFontAscent; } + + UInt32 GetBitmapWidth( void ) const { return fWidth; } + UInt32 GetBitmapHeight( void ) const { return fHeight; } + UInt8 GetBitmapBPP( void ) const { return fBPP; } + + void SetFace( const char *face ); + void SetSize( UInt8 size ); + void SetFlags( UInt32 flags ) { fFlags = flags; } + void SetFlag( UInt32 flag, hsBool on ) { if( on ) fFlags |= flag; else fFlags &= ~flag; } + hsBool IsFlagSet( UInt32 flag ) { if( fFlags & flag ) return true; return false; } + + void SetRenderColor( UInt32 color ); + void SetRenderFlag( UInt32 flag, hsBool on ) { if( on ) fRenderInfo.fFlags |= flag; else fRenderInfo.fFlags &= ~flag; } + hsBool IsRenderFlagSet( UInt32 flag ) { return ( fRenderInfo.fFlags & flag ) ? true : false; } + void SetRenderClipRect( Int16 x, Int16 y, Int16 width, Int16 height ); + void SetRenderXJustify( UInt32 j ) { fRenderInfo.fFlags &= ~kRenderJustXMask; fRenderInfo.fFlags |= j; } + void SetRenderYJustify( UInt32 j ) { fRenderInfo.fFlags &= ~kRenderJustYMask; fRenderInfo.fFlags |= j; } + void SetRenderFirstLineIndent( Int16 indent ) { fRenderInfo.fFirstLineIndent = indent; } + void SetRenderLineSpacing( Int16 spacing ) { fRenderInfo.fLineSpacing = spacing; } + + // Sets flags too + void SetRenderClipping( Int16 x, Int16 y, Int16 width, Int16 height ); + void SetRenderWrapping( Int16 x, Int16 y, Int16 width, Int16 height ); + + void RenderString( plMipmap *mip, UInt16 x, UInt16 y, const char *string, UInt16 *lastX = nil, UInt16 *lastY = nil ); + void RenderString( plMipmap *mip, UInt16 x, UInt16 y, const wchar_t *string, UInt16 *lastX = nil, UInt16 *lastY = nil ); + + UInt16 CalcStringWidth( const char *string ); + UInt16 CalcStringWidth( const wchar_t *string ); + void CalcStringExtents( const char *string, UInt16 &width, UInt16 &height, UInt16 &ascent, UInt32 &firstClippedChar, UInt16 &lastX, UInt16 &lastY ); + void CalcStringExtents( const wchar_t *string, UInt16 &width, UInt16 &height, UInt16 &ascent, UInt32 &firstClippedChar, UInt16 &lastX, UInt16 &lastY ); + + hsBool LoadFromFNT( const char *path ); + hsBool LoadFromFNTStream( hsStream *stream ); + + hsBool LoadFromBDF( const char *path, plBDFConvertCallback *callback ); + hsBool LoadFromBDFStream( hsStream *stream, plBDFConvertCallback *callback ); + + hsBool LoadFromP2FFile( const char *path ); + + hsBool ReadRaw( hsStream *stream ); + hsBool WriteRaw( hsStream *stream ); +}; + +#endif // _plFont_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFontCache.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFontCache.cpp new file mode 100644 index 00000000..8708645a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFontCache.cpp @@ -0,0 +1,203 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plFontCache Class Header // +// Generic cache lib for our plFonts. Basically just a simple plFont // +// manager. // +// // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 3.12.2003 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "plFontCache.h" + +#include "plFont.h" +#include "../plStatusLog/plStatusLog.h" +#include "../plFile/hsFiles.h" +#include "../pnMessage/plRefMsg.h" + +#include "hsResMgr.h" +#include "../pnKeyedObject/plUoid.h" + + +char *plFontCache::kCustFontExtension = ".prf"; + + +plFontCache *plFontCache::fInstance = nil; + +plFontCache::plFontCache() +{ + fCustFontDir = nil; + RegisterAs( kFontCache_KEY ); + fInstance = this; +} + +plFontCache::~plFontCache() +{ + Clear(); + delete [] fCustFontDir; + fInstance = nil; +} + +plFontCache &plFontCache::GetInstance( void ) +{ + return *fInstance; +} + +void plFontCache::Clear( void ) +{ +} + +plFont *plFontCache::GetFont( const char *face, UInt8 size, UInt32 fontFlags ) +{ + UInt32 i, currIdx = (UInt32)-1; + int currDeltaSize = 100000; + char toFind[ 256 ]; + + + strcpy( toFind, face ); + strlwr( toFind ); + for( i = 0; i < fCache.GetCount(); i++ ) + { + char thisOne[ 256 ]; + strcpy( thisOne, fCache[ i ]->GetFace() ); + strlwr( thisOne ); + + if( strncmp( thisOne, toFind, strlen( toFind ) ) == 0 && + ( fCache[ i ]->GetFlags() == fontFlags ) ) + { + int delta = fCache[ i ]->GetSize() - size; + if( delta < 0 ) + delta = -delta; + if( delta < currDeltaSize ) + { + currDeltaSize = delta; + currIdx = i; + } + } + } + + if( currIdx != (UInt32)-1 ) + { + //if( currDeltaSize > 0 ) + // plStatusLog::AddLineS( "pipeline.log", "Warning: plFontCache is matching %s %d (requested %s %d)", fCache[ currIdx ]->GetFace(), fCache[ currIdx ]->GetSize(), face, size ); + return fCache[ currIdx ]; + } + + // If we failed, it's possible we have a face saved as "Times", for example, and someone's + // asking for "Times New Roman", so strip all but the first word from our font and try the search again + char *c = strchr( toFind, ' ' ); + if( c != nil ) + { + *c = 0; + return GetFont( toFind, size, fontFlags ); + } + else if( fontFlags != 0 ) + { + // Hmm, well ok, just to be nice, try without our flags + plFont *f = GetFont( toFind, size, 0 ); + if( f != nil ) + { + //plStatusLog::AddLineS( "pipeline.log", "Warning: plFontCache is substituting %s %d regular (flags 0x%x could not be matched)", f->GetFace(), f->GetSize(), fontFlags ); + return f; + } + } + + //plStatusLog::AddLineS( "pipeline.log", "Warning: plFontCache was unable to match %s %d (0x%x)", face, size, fontFlags ); + return nil; +} + +void plFontCache::LoadCustomFonts( const char *dir ) +{ + delete [] fCustFontDir; + fCustFontDir = ( dir != nil ) ? hsStrcpy( dir ) : nil; + + ILoadCustomFonts(); +} + +void plFontCache::ILoadCustomFonts( void ) +{ + if( fCustFontDir == nil ) + return; + + // Iterate through all the custom fonts in our dir + hsFolderIterator iter( fCustFontDir ); + char fileName[ kFolderIterator_MaxPath ]; + + + hsFolderIterator iter2( fCustFontDir ); + while( iter2.NextFileSuffix( ".p2f" ) ) + { + iter2.GetPathAndName( fileName ); + + plFont *font = TRACKED_NEW plFont; + if( !font->LoadFromP2FFile( fileName ) ) + delete font; + else + { + char keyName[ 512 ]; + if( font->GetKey() == nil ) + { + sprintf( keyName, "%s-%d", font->GetFace(), font->GetSize() ); + hsgResMgr::ResMgr()->NewKey( keyName, font, plLocation::kGlobalFixedLoc ); + } + + hsgResMgr::ResMgr()->AddViaNotify( font->GetKey(), + TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, 0, -1 ), + plRefFlags::kActiveRef ); + + //plStatusLog::AddLineS( "pipeline.log", "FontCache: Added custom font %s", keyName ); + + } + } +} + +hsBool plFontCache::MsgReceive( plMessage* pMsg ) +{ + plGenRefMsg *ref = plGenRefMsg::ConvertNoRef( pMsg ); + if( ref != nil ) + { + if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fCache.Append( plFont::ConvertNoRef( ref->GetRef() ) ); + } + else + { + plFont *font = plFont::ConvertNoRef( ref->GetRef() ); + UInt32 idx = fCache.Find( font ); + if( idx != fCache.kMissingIndex ) + fCache.Remove( idx ); + } + return true; + } + + return hsKeyedObject::MsgReceive( pMsg ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFontCache.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFontCache.h new file mode 100644 index 00000000..0e8743a6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plFontCache.h @@ -0,0 +1,90 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plFontCache Class Header // +// Generic cache lib for our plFonts. Basically just a simple plFont // +// manager. // +// // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 3.12.2003 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plFontCache_h +#define _plFontCache_h + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/hsKeyedObject.h" + + +//// Class Definition ///////////////////////////////////////////////////////// + +class plFont; +class plFontCache : public hsKeyedObject +{ + protected: + + hsTArray fCache; + char *fCustFontDir; + + static plFontCache *fInstance; + + void ILoadCustomFonts( void ); + + public: + + CLASSNAME_REGISTER( plFontCache ); + GETINTERFACE_ANY( plFontCache, hsKeyedObject ); + + plFontCache(); + virtual ~plFontCache(); + + virtual void Read( hsStream *s, hsResMgr *mgr ) {} + virtual void Write( hsStream *s, hsResMgr *mgr ) {} + + virtual hsBool MsgReceive( plMessage* pMsg ); + + static plFontCache &GetInstance( void ); + + plFont *GetFont( const char *face, UInt8 size, UInt32 fontFlags ); + +// HFONT GetMeAFont( const char *face, int height, int weight, hsBool italic, UInt32 quality ); +// void FreeFont( HFONT font ); + void Clear( void ); + + void LoadCustomFonts( const char *dir ); + + // Our custom font extension + static char *kCustFontExtension; +}; + + +#endif // _plFontCache_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plGImageCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plGImageCreatable.h new file mode 100644 index 00000000..008fb0e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plGImageCreatable.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plGImageCreatable_inc +#define plGImageCreatable_inc + +#include "../pnFactory/plCreator.h" + +/* +#include "hsGMipmap.h" + +REGISTER_CREATABLE( hsGBitmapClass ); + +REGISTER_CREATABLE( hsGMipmapClass ); +*/ + +#include "plBitmap.h" + +REGISTER_NONCREATABLE( plBitmap ); + +#include "plMipmap.h" + +REGISTER_CREATABLE( plMipmap ); + +#include "plCubicEnvironmap.h" + +REGISTER_CREATABLE( plCubicEnvironmap ); + +#include "plDynamicTextMap.h" + +REGISTER_CREATABLE( plDynamicTextMap ); + +#include "plAVIWriter.h" + +REGISTER_NONCREATABLE( plAVIWriter ); + +#include "plFont.h" + +REGISTER_CREATABLE( plFont ); + +#include "plFontCache.h" + +REGISTER_CREATABLE( plFontCache ); + +#include "plLODMipmap.h" + +REGISTER_CREATABLE( plLODMipmap ); + +#endif // plGImageCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plImageConvert.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plImageConvert.h new file mode 100644 index 00000000..6babe64f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plImageConvert.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +class plImageConvert : public hsRefCnt +{ +public: + + // Fill in dst according to src's image and dst's format/size + hsGBitmap* Convert(hsGBitmap* src, hsGBitmap* dst); + + hsGBitmap* Create( + + // Fill dst with normals from height map src. May take additional + // parameters, like scale and which channel of src to use as height + hsGBitmap* CreateBumpMap(hsGBitmap* src, hsGBitmap* dst); + + hsGBitmap* Filter(hsGBitmap* src, hsGBitmap* dst, plFilterFunc func); ? + + +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plLODMipmap.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plLODMipmap.cpp new file mode 100644 index 00000000..eafa9181 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plLODMipmap.cpp @@ -0,0 +1,284 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLODMipmap.h" + +#include "hsResMgr.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnMessage/plRefMsg.h" + +#include "../plPipeline/hsGDeviceRef.h" + + +plLODMipmap::plLODMipmap() +: fBase(nil), + fLOD(0) +{ + int i; + for( i = 0; i < kNumLODs; i++ ) + fDeviceRefs[i] = nil; +} + +plLODMipmap::plLODMipmap(plMipmap* mip) +: fBase(nil), + fLOD(0) +{ + int i; + for( i = 0; i < kNumLODs; i++ ) + fDeviceRefs[i] = nil; + + hsgResMgr::ResMgr()->NewKey(mip->GetKey()->GetName(), this, mip->GetKey()->GetUoid().GetLocation()); + + // Need some kind of reffing assignment for the mipmap here + fBase = mip; + fLevelSizes = TRACKED_NEW UInt32[fBase->GetNumLevels()]; + + ISetup(); + + hsgResMgr::ResMgr()->AddViaNotify(mip->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefBase), plRefFlags::kActiveRef); + +} + +plLODMipmap::~plLODMipmap() +{ + // And matching unreffing of the mipmap here. + fImage = nil; + + int i; + for( i = 0; i < kNumLODs; i++ ) + { + hsRefCnt_SafeUnRef(fDeviceRefs[i]); + fDeviceRefs[i] = nil; + } +} + +hsGDeviceRef* plLODMipmap::GetDeviceRef() const +{ + return fDeviceRefs[GetLOD()]; +} + +void plLODMipmap::SetDeviceRef( hsGDeviceRef *const devRef ) +{ + hsRefCnt_SafeAssign(fDeviceRefs[GetLOD()], devRef); +} + + +void plLODMipmap::SetLOD(int lod) +{ + hsAssert(fBase, "UnInitialized"); + + const kMaxLOD = 5; + if( lod > kMaxLOD ) + lod = kMaxLOD; + if( lod >= fBase->GetNumLevels() ) + lod = fBase->GetNumLevels() - 1; + + if( fLOD != lod ) + { + fLOD = lod; + ISetup(); + } +} + + +void plLODMipmap::ISetup() +{ + hsAssert(fBase, "UnInitialized"); + hsAssert(fBase->GetNumLevels() > GetLOD(), "Skipping all mip levels"); + + fBase->SetCurrLevel(GetLOD()); + + // plBitmap + fPixelSize = fBase->GetPixelSize(); +// fSpace = fBase->fSpace; + fFlags = fBase->GetFlags(); + + fCompressionType = fBase->fCompressionType; + if( !fBase->IsCompressed() ) + { + fUncompressedInfo = fBase->fUncompressedInfo; + } + else + { + fDirectXInfo = fBase->fDirectXInfo; + } + + // plMipmap + fImage = fBase->GetCurrLevelPtr(); + fWidth = fBase->GetCurrWidth(); + fHeight = fBase->GetCurrHeight(); + + fRowBytes = fBase->GetRowBytes(); + + if( !fLevelSizes ) + fLevelSizes = TRACKED_NEW UInt32[fBase->GetNumLevels()]; + + fNumLevels = fBase->GetNumLevels() - GetLOD(); + fNumLevels = 1; + + fTotalSize = 0; + int i; + for( i = 0; i < fNumLevels; i++ ) + { + fLevelSizes[i] = fBase->GetLevelSize(i + GetLOD()); + fTotalSize += fBase->GetLevelSize(i + GetLOD()); + } + + ISetupCurrLevel(); + + IMarkDirty(); +} + +void plLODMipmap::ISetupCurrLevel() +{ + fCurrLevelPtr = fBase->GetCurrLevelPtr(); + fCurrLevel = (UInt8)(fBase->GetCurrLevel()); + fCurrLevelWidth = fBase->GetCurrWidth(); + fCurrLevelHeight = fBase->GetCurrHeight(); + fCurrLevelRowBytes = fBase->GetRowBytes(); +} + +void plLODMipmap::INilify() +{ + fBase = nil; + + fPixelSize = 0; + fSpace = kNoSpace; + fFlags = 0; + + fCompressionType = kUncompressed; + fUncompressedInfo.fType = UncompressedInfo::kRGB8888; + + // plMipmap + fImage = nil; + fWidth = 0; + fHeight = 0; + + fRowBytes = 0; + + fNumLevels = 0; + fTotalSize = 0; + + fCurrLevelPtr = nil; + fCurrLevel = 0; + fCurrLevelWidth = 0; + fCurrLevelHeight = 0; + fCurrLevelRowBytes = 0; + + delete [] fLevelSizes; + fLevelSizes = nil; + + int i; + for( i = 0; i < kNumLODs; i++ ) + { + hsRefCnt_SafeUnRef(fDeviceRefs[i]); + fDeviceRefs[i] = nil; + } +} + +void plLODMipmap::IMarkDirty() +{ + int i; + for( i = 0; i < kNumLODs; i++ ) + { + if( fDeviceRefs[i] ) + fDeviceRefs[i]->SetDirty(true); + } +} + +void plLODMipmap::SetCurrLevel(UInt8 level) +{ + fBase->SetCurrLevel(level + GetLOD()); + + ISetupCurrLevel(); +} + +void plLODMipmap::Reset() +{ + fBase->Reset(); + ISetup(); +} + +void plLODMipmap::ScaleNicely(UInt32 *destPtr, UInt16 destWidth, UInt16 destHeight, + UInt16 destStride, plMipmap::ScaleFilter filter) const +{ + fBase->ScaleNicely(destPtr, destWidth, destHeight, destStride, filter); +} + +hsBool plLODMipmap::ResizeNicely(UInt16 newWidth, UInt16 newHeight, plMipmap::ScaleFilter filter) +{ + hsBool retVal = fBase->ResizeNicely(newWidth, newHeight, filter); + ISetup(); + return retVal; +} + +void plLODMipmap::CopyFrom(const plMipmap *source) +{ + fBase->CopyFrom(source); + ISetup(); +} + +void plLODMipmap::Composite(plMipmap *source, UInt16 x, UInt16 y, CompositeOptions *options) +{ + fBase->Composite(source, x, y, options); + IMarkDirty(); +} + +hsBool plLODMipmap::MsgReceive(plMessage *msg) +{ + plGenRefMsg* ref = plGenRefMsg::ConvertNoRef(msg); + if( ref ) + { + if( ref->fType == kRefBase ) + { + INilify(); + if( ref->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fBase = plMipmap::ConvertNoRef(ref->GetRef()); + ISetup(); + } + + return true; + } + } + + return plMipmap::MsgReceive(msg); +} + +void plLODMipmap::Read(hsStream *s, hsResMgr *mgr) +{ + INilify(); + hsKeyedObject::Read(s, mgr); + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefBase), plRefFlags::kActiveRef); // fBase +} + +void plLODMipmap::Write(hsStream *s, hsResMgr *mgr) +{ + hsKeyedObject::Write(s, mgr); + mgr->WriteKey(s, fBase); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plLODMipmap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plLODMipmap.h new file mode 100644 index 00000000..0329737d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plLODMipmap.h @@ -0,0 +1,92 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLODMipmap_inc +#define plLODMipmap_inc + +#include "plMipmap.h" + +class plLODMipmap : public plMipmap +{ +protected: + const enum + { + kRefBase = 0 + }; + const enum + { + kNumLODs = 5 + }; + + plMipmap* fBase; + int fLOD; + + hsGDeviceRef* fDeviceRefs[kNumLODs]; + + void ISetup(); + void ISetupCurrLevel(); + void IMarkDirty(); + void INilify(); + + +public: + plLODMipmap(); + plLODMipmap(plMipmap* mip); + virtual ~plLODMipmap(); + + CLASSNAME_REGISTER( plLODMipmap ); + GETINTERFACE_ANY( plLODMipmap, plMipmap ); + + virtual hsBool MsgReceive(plMessage *msg); + + void SetLOD(int lod); + int GetLOD() const { return fLOD; } + + virtual hsGDeviceRef* GetDeviceRef() const; + virtual void SetDeviceRef( hsGDeviceRef *const devRef ); + + virtual void Reset(); + + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); + + virtual plMipmap* Clone() const { return fBase->Clone(); } + virtual void CopyFrom(const plMipmap *source); + + virtual void Composite(plMipmap *source, UInt16 x, UInt16 y, CompositeOptions *options = nil); + + virtual void ScaleNicely(UInt32 *destPtr, UInt16 destWidth, UInt16 destHeight, + UInt16 destStride, plMipmap::ScaleFilter filter) const; + + virtual hsBool ResizeNicely(UInt16 newWidth, UInt16 newHeight, plMipmap::ScaleFilter filter); + + virtual void SetCurrLevel(UInt8 level); + + const plMipmap* GetBase() const { return fBase; } +}; + + +#endif // plLODMipmap_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plMipmap.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plMipmap.cpp new file mode 100644 index 00000000..166f0935 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plMipmap.cpp @@ -0,0 +1,2194 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plMipmap Class Functions // +// Derived bitmap class representing a single mipmap. // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 6.7.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plMipmap.h" +#include "hsStream.h" +#include "hsExceptions.h" +#include "hsUtils.h" +#include "hsColorRGBA.h" +#include "../plPipeline/hsGDeviceRef.h" +#include "plProfile.h" +#include "../plJPEG/plJPEG.h" + +plProfile_CreateMemCounter("Mipmaps", "Memory", MemMipmaps); + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plMipmap::plMipmap() : fImage( nil ), fLevelSizes( nil ), fCurrLevelPtr( nil ), fCurrLevel( 0 ), fTotalSize( 0 ) +{ + SetConfig( kARGB32Config ); + fCompressionType = kUncompressed; + fUncompressedInfo.fType = UncompressedInfo::kRGB8888; + +#ifdef MEMORY_LEAK_TRACER + fNumMipmaps++; +#endif +} + +plMipmap::~plMipmap() +{ + Reset(); + +#ifdef MEMORY_LEAK_TRACER + fNumMipmaps--; + if( fNumMipmaps == 0 ) + IReportLeaks(); +#endif +} + +plMipmap::plMipmap( UInt32 width, UInt32 height, unsigned config, UInt8 numLevels, UInt8 compType, UInt8 format ) +{ + Create( width, height, config, numLevels, compType, format ); + +#ifdef MEMORY_LEAK_TRACER + fNumMipmaps++; +#endif +} + +//// Create /////////////////////////////////////////////////////////////////// + +void plMipmap::Create( UInt32 width, UInt32 height, unsigned config, UInt8 numLevels, UInt8 compType, UInt8 format ) +{ + int i; + + + SetConfig( config ); + + fWidth = width; + fHeight = height; + fRowBytes = fWidth * fPixelSize >> 3; + if( numLevels > 0 ) + fNumLevels = numLevels; + else + { + for( fNumLevels = 1; width > 1 || height > 1; fNumLevels++ ) + { + if( width > 1 ) + width >>= 1; + if( height > 1 ) + height >>= 1; + } + } + + fCompressionType = compType; + if( compType == kUncompressed ) + { + fUncompressedInfo.fType = format; + } + else if( compType == kJPEGCompression ) + { + fUncompressedInfo.fType = format; + } + else + { + fDirectXInfo.fBlockSize = ( format == DirectXInfo::kDXT1 ) ? 8 : 16; + fDirectXInfo.fCompressionType = format; + + if( format == DirectXInfo::kDXT1 ) + { + // Has an alpha bit, but no channel + fFlags |= kAlphaBitFlag; + fFlags &= ~kAlphaChannelFlag; + } + else // All other formats have an actual alpha channel + { + fFlags &= ~kAlphaBitFlag; + fFlags |= kAlphaChannelFlag; + } + } + + fLevelSizes = nil; + IBuildLevelSizes(); + + fTotalSize = 0; + for( i = 0; i < fNumLevels; i++ ) + fTotalSize += fLevelSizes[ i ]; + + fImage = (void *)TRACKED_NEW UInt8[ fTotalSize ]; + memset(fImage, 0, fTotalSize); + + SetCurrLevel( 0 ); + plProfile_NewMem(MemMipmaps, fTotalSize); + +#ifdef MEMORY_LEAK_TRACER + IAddToMemRecord( this, plRecord::kViaCreate ); +#endif + +} + +//// Reset //////////////////////////////////////////////////////////////////// + +void plMipmap::Reset() +{ + delete [] fLevelSizes; + fLevelSizes = nil; + if( !( fFlags & kUserOwnsBitmap ) ) + { +#ifdef MEMORY_LEAK_TRACER + if( fImage != nil ) + IRemoveFromMemRecord( (UInt8 *)fImage ); +#endif + + delete [] fImage; + plProfile_DelMem(MemMipmaps, fTotalSize); + } + fImage = nil; +} + + +/////////////////////////////////////////////////////////////////////////////// +//// Virtual Functions //////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// GetTotalSize ///////////////////////////////////////////////////////////// +// Get the total size in bytes + +UInt32 plMipmap::GetTotalSize() const +{ + return fTotalSize; +} + +//// Read ///////////////////////////////////////////////////////////////////// + +UInt32 plMipmap::Read( hsStream *s ) +{ + UInt32 totalRead = plBitmap::Read( s ); + + // Decide to clamp if we were told to + int clampBy = fGlobalNumLevelsToChop; + const kMaxSkipLevels = 1; + if( clampBy > kMaxSkipLevels ) + clampBy = kMaxSkipLevels; + if( fFlags & kNoMaxSize ) + clampBy = 0; + else if( ( fFlags & kHalfSize ) && clampBy > 1 ) + clampBy = 1; + UInt32 amtToSkip = 0; + + fWidth = s->ReadSwap32(); + fHeight = s->ReadSwap32(); + fRowBytes = s->ReadSwap32(); + fTotalSize = s->ReadSwap32(); + fNumLevels = s->ReadByte(); + + totalRead += 4 * 4 + 1; + + if( fTotalSize == 0 ) + fImage = nil; + else + { + IBuildLevelSizes(); + + if (fCompressionType != kJPEGCompression) // JPEGs don't play nicely with quality settings the way they are written, so we ignore them + { + if( clampBy > 0 ) + { + int i; + for( i = 0; i < clampBy && fNumLevels > 1 && fWidth > 4 && fHeight > 4; i++ ) + { + amtToSkip += fLevelSizes[ i ]; + fWidth >>= 1; + fHeight >>= 1; + fRowBytes >>= 1; + fNumLevels--; + } + fTotalSize -= amtToSkip; + IBuildLevelSizes(); + } + } + + fImage = (void *)TRACKED_NEW UInt8[ fTotalSize ]; +#ifdef MEMORY_LEAK_TRACER + IAddToMemRecord( this, plRecord::kViaRead ); +#endif + plProfile_NewMem(MemMipmaps, fTotalSize); + + switch( fCompressionType ) + { + case kDirectXCompression: + s->Skip( amtToSkip ); + s->Read( fTotalSize, fImage ); + break; + + case kUncompressed: + s->Skip( amtToSkip ); + IReadRawImage( s ); + break; + + case kJPEGCompression: + IReadJPEGImage( s ); + break; + + default: + hsAssert( false, "Unknown compression type in plMipmap::Read()" ); + return totalRead; + } + totalRead += fTotalSize; + } + return totalRead; +} + +//// Write //////////////////////////////////////////////////////////////////// + +UInt32 plMipmap::Write( hsStream *s ) +{ + UInt32 totalWritten = plBitmap::Write( s ); + + s->WriteSwap32( fWidth ); + s->WriteSwap32( fHeight ); + s->WriteSwap32( fRowBytes ); + s->WriteSwap32( fTotalSize ); + s->WriteByte( fNumLevels ); + + totalWritten += 4 * 4 + 1; + + if( fTotalSize > 0 ) + { + switch( fCompressionType ) + { + case kDirectXCompression: + s->Write( fTotalSize, fImage ); + break; + + case kUncompressed: + IWriteRawImage( s ); + break; + + case kJPEGCompression: + IWriteJPEGImage( s ); + break; + + default: + hsAssert( false, "Unknown compression type in plMipmap::Read()" ); + return totalWritten; + } + totalWritten += fTotalSize; + } + + return totalWritten; +} + + +/////////////////////////////////////////////////////////////////////////////// +//// Utility Functions //////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// IReadRawImage //////////////////////////////////////////////////////////// + +void plMipmap::IReadRawImage( hsStream *stream ) +{ + UInt32 i; + UInt8 *data = (UInt8 *)fImage; + + + switch( fPixelSize ) + { + case 32: + for( i = 0; i < fNumLevels; i++ ) + { + stream->ReadSwap32( fLevelSizes[ i ] >> 2, (UInt32 *)data ); + data += fLevelSizes[ i ]; + } + break; + + case 16: + for( i = 0; i < fNumLevels; i++ ) + { + stream->ReadSwap16( fLevelSizes[ i ] >> 1, (UInt16 *)data ); + data += fLevelSizes[ i ]; + } + break; + + default: + hsThrow( hsBadParamException() ); + } +} + +//// IWriteRawImage /////////////////////////////////////////////////////////// + +void plMipmap::IWriteRawImage( hsStream *stream ) +{ + UInt32 i; + UInt8 *data = (UInt8 *)fImage; + + + switch( fPixelSize ) + { + case 32: + for( i = 0; i < fNumLevels; i++ ) + { + stream->WriteSwap32( fLevelSizes[ i ] >> 2, (UInt32 *)data ); + data += fLevelSizes[ i ]; + } + break; + + case 16: + for( i = 0; i < fNumLevels; i++ ) + { + stream->WriteSwap16( fLevelSizes[ i ] >> 1, (UInt16 *)data ); + data += fLevelSizes[ i ]; + } + break; + + default: + hsThrow( hsBadParamException() ); + } +} + +plMipmap *plMipmap::ISplitAlpha() +{ + plMipmap *retVal = TRACKED_NEW plMipmap(); + retVal->CopyFrom(this); + memset( retVal->fImage, 0, fTotalSize ); + + UInt8 *curLoc = (UInt8 *)fImage; + UInt8 *destLoc = (UInt8 *)retVal->fImage; + UInt32 numBytes = fTotalSize; + UInt32 curByte = 0; + + switch( fUncompressedInfo.fType ) + { + case fUncompressedInfo.kRGB8888: + // first byte is the alpha channel, we will drop this byte into the red channel for compression + while (curByte < numBytes) + { + curLoc += 3; + destLoc += 2; // make the destination pointer point at the red channel + *destLoc = *curLoc; // copy the information + destLoc += 2; // make the destination pointer point at the beginning of the next pixel + curLoc ++; // same here for source pointer + curByte += 4; + } + break; + default: + break; // not going to mess with other formats for now + } + return retVal; +} + +// alphaChannel must be in the format generated from ISplitAlpha, or strange things will happen +void plMipmap::IRecombineAlpha( plMipmap *alphaChannel ) +{ + UInt8 *curLoc = (UInt8 *)alphaChannel->fImage; + UInt8 *destLoc = (UInt8 *)fImage; + UInt32 numBytes = fTotalSize; + UInt32 curByte = 0; + + switch( fUncompressedInfo.fType ) + { + case fUncompressedInfo.kRGB8888: + // first byte is the alpha channel, we will grab this byte from the red channel for reconstitution + while (curByte < numBytes) + { + curLoc += 2; // pointer points at the red channel (where the alpha is stored) + destLoc += 3; + *destLoc = *curLoc; // copy the data + destLoc++; // move the pointer to the next pixel + curLoc += 2; + curByte += 4; + } + break; + default: + break; // not going to mess with other formats for now + } + fFlags |= plBitmap::kAlphaChannelFlag; +} + +plMipmap *plMipmap::IReadRLEImage( hsStream *stream ) +{ + UInt32 count,color; + bool done = false; + + plMipmap *retVal = TRACKED_NEW plMipmap(fWidth,fHeight,plMipmap::kARGB32Config,1); + + UInt32 *curPos = (UInt32*)retVal->fImage; + UInt32 curLoc = 0; + + while (!done) + { + count = stream->ReadSwap32(); + color = stream->ReadSwap32(); + if (count == 0) + done = true; + else + { + for (UInt32 i=0; ifCompressionType = kUncompressed; + return retVal; +} + +void plMipmap::IWriteRLEImage( hsStream *stream, plMipmap *mipmap ) +{ + UInt32 count=0,color=0,curColor=0; + UInt32 curPos = 0; + UInt32 totalSize = mipmap->fLevelSizes[0]/4; // we only save the first mipmap level + + UInt32 *src = (UInt32*)mipmap->fImage; + curColor = *src; + curColor &= 0x00FFFFFF; // strip the alpha (if there is any) + while (curPos < totalSize) + { + color = *src; + color &= 0x00FFFFFF; // strip the alpha (if there is any) + if (color != curColor) + { + stream->WriteSwap32(count); + stream->WriteSwap32(curColor); + count = 0; + curColor = color; + } + count++; + src++; + curPos++; + } + stream->WriteSwap32(count); + stream->WriteSwap32(color); + stream->WriteSwap32(0); // terminate with zero count + stream->WriteSwap32(0); +} + +void plMipmap::IReadJPEGImage( hsStream *stream ) +{ + UInt8 flags = 0; + flags = stream->ReadByte(); + + plMipmap *temp = nil; + plMipmap *alpha = nil; + + if (flags & kColorDataRLE) + temp = IReadRLEImage(stream); + else + temp = plJPEG::Instance().ReadFromStream(stream); + + if (temp) + { + // copy the data + CopyFrom(temp); + + if (flags & kAlphaDataRLE) + alpha = IReadRLEImage(stream); + else + alpha = plJPEG::Instance().ReadFromStream(stream); + + if (alpha) + { + IRecombineAlpha(alpha); + delete alpha; + } + delete temp; + } +} + +void plMipmap::IWriteJPEGImage( hsStream *stream ) +{ + plMipmap *alpha = ISplitAlpha(); + UInt8 flags = 0; + + hsNullStream *nullStream = TRACKED_NEW hsNullStream(); + IWriteRLEImage(nullStream,this); + if (nullStream->GetBytesWritten() < 5120) // we use RLE if it can get the image size under 5k, otherwise we use JPEG + flags |= kColorDataRLE; + delete nullStream; + nullStream = TRACKED_NEW hsNullStream(); + IWriteRLEImage(nullStream,alpha); + if (nullStream->GetBytesWritten() < 5120) + flags |= kAlphaDataRLE; + delete nullStream; + stream->WriteByte(flags); + + if (flags & kColorDataRLE) + IWriteRLEImage(stream,this); + else + { + plJPEG::Instance().SetWriteQuality(70); + plJPEG::Instance().WriteToStream(stream, this); + } + if (flags & kAlphaDataRLE) + IWriteRLEImage(stream,alpha); + else + { + plJPEG::Instance().SetWriteQuality(100); + plJPEG::Instance().WriteToStream(stream, alpha); + } + delete alpha; +} + +//// GetLevelSize ///////////////////////////////////////////////////////////// +// Get the size of a single mipmap level (0 is the largest) + +UInt32 plMipmap::GetLevelSize( UInt8 level ) +{ + if( fLevelSizes == nil ) + IBuildLevelSizes(); + + return fLevelSizes[ level ]; +} + +//// IBuildLevelSizes ///////////////////////////////////////////////////////// +// Creates the table of level sizes, for quick reference + +void plMipmap::IBuildLevelSizes() +{ + UInt8 level; + UInt32 width, height, rowBytes; + + + delete [] fLevelSizes; + fLevelSizes = TRACKED_NEW UInt32[ fNumLevels ]; + memset( fLevelSizes, 0, sizeof( UInt32 ) * fNumLevels ); + + for( level = 0, width = fWidth, height = fHeight, rowBytes = fRowBytes; level < fNumLevels; level++ ) + { + switch( fCompressionType ) + { + case kDirectXCompression: + if( ( width | height ) & 0x03 ) + fLevelSizes[ level ] = height * rowBytes; + else + fLevelSizes[ level ] = ( height * width * (UInt32)fDirectXInfo.fBlockSize ) >> 4; + break; + + case kUncompressed: + case kJPEGCompression: + fLevelSizes[ level ] = height * rowBytes; + break; + + default: + hsAssert( false, "Bad compression type." ); + return; + } + + // Scale down and go! + if( width > 1 ) + { + width >>= 1; + rowBytes >>= 1; + } + if( height > 1 ) + height >>= 1; + } +} + +//// GetLevelPtr ////////////////////////////////////////////////////////////// + +UInt8 *plMipmap::GetLevelPtr( UInt8 level, UInt32 *width, UInt32 *height, UInt32 *rowBytes ) +{ + UInt8 *data, i; + UInt32 w, h, r; + + + if( fLevelSizes == nil ) + IBuildLevelSizes(); + + for( i = 0, data = (UInt8 *)fImage, w = fWidth, h = fHeight, r = fRowBytes; i < level; i++ ) + { + data += fLevelSizes[ i ]; + if( w > 1 ) + { + w >>= 1; + r >>= 1; + } + if( h > 1 ) + h >>= 1; + } + + if( width != nil ) + *width = w; + if( height != nil ) + *height = h; + if( rowBytes != nil ) + *rowBytes = r; + + return data; +} + +//// SetCurrLevel ///////////////////////////////////////////////////////////// +// Sets the current level pointer for use with GetAddr* + +void plMipmap::SetCurrLevel( UInt8 level ) +{ + fCurrLevel = level; + fCurrLevelPtr = GetLevelPtr( level, &fCurrLevelWidth, &fCurrLevelHeight, &fCurrLevelRowBytes ); +} + +//// SetConfig //////////////////////////////////////////////////////////////// + +void plMipmap::SetConfig( unsigned config ) +{ + switch( config ) + { + case kRGB32Config: + fPixelSize = 32; + fSpace = kDirectSpace; + fFlags = kNoFlag; + break; + case kARGB32Config: + fPixelSize = 32; + fSpace = kDirectSpace; + fFlags = kAlphaChannelFlag; + break; + case kRGB16Config: + fPixelSize = 16; + fSpace = kDirectSpace; + fFlags = kAlphaBitFlag; + break; + case kColor8Config: + fPixelSize = 8; + fSpace = kIndexSpace; + fFlags = kNoFlag; + break; + case kGray8Config: + fPixelSize = 8; + fSpace = kDirectSpace; + fFlags = kNoFlag; + break; + case kGray44Config: + fPixelSize = 8; + fSpace = kGraySpace; + fFlags = kAlphaChannelFlag; + break; + case kGray4Config: + fPixelSize = 4; + fSpace = kGraySpace; + fFlags = kNoFlag; + break; + default: + hsDebugMessage( "unknown config", config ); + break; + } +} + +//// ClipToMaxSize //////////////////////////////////////////////////////////// +// Looks for mipmap levels above the given dimension and "clips" them out, +// i.e. deletes them. So if you have a 512x1024 mipmap and call this with +// a size of 256, when this function returns the mipmap will be 256x128 in +// size. + +void plMipmap::ClipToMaxSize( UInt32 maxDimension ) +{ + UInt8 *srcData, *destData, i; + UInt32 newSize; + + + for( i = 0, newSize = fTotalSize, srcData = (UInt8 *)fImage; fWidth > maxDimension || fHeight > maxDimension; i++ ) + { + srcData += fLevelSizes[ i ]; + newSize -= fLevelSizes[ i ]; + if( fWidth > 1 ) + { + fWidth >>= 1; + fRowBytes >>= 1; + } + if( fHeight > 1 ) + fHeight >>= 1; + } + + if( i == 0 ) + // No change + return; + + /// Create a new image pointer + destData = TRACKED_NEW UInt8[ newSize ]; + hsAssert( destData != nil, "Out of memory in ClipToMaxSize()" ); + memcpy( destData, srcData, newSize ); + +#ifdef MEMORY_LEAK_TRACER + IRemoveFromMemRecord( (UInt8 *)fImage ); +#endif + delete [] fImage; + plProfile_DelMem(MemMipmaps, fTotalSize); + + fImage = destData; + fTotalSize = newSize; + fNumLevels -= i; + IBuildLevelSizes(); + plProfile_NewMem(MemMipmaps, fTotalSize); + +#ifdef MEMORY_LEAK_TRACER + IAddToMemRecord( this, plRecord::kViaClipToMaxSize ); +#endif +} + +//// RemoveMipping //////////////////////////////////////////////////////////// +// Basically removes mipmap levels so that there is only one level remaining; +// i.e. a plain bitmap instead of a mipmap. + +void plMipmap::RemoveMipping() +{ + UInt8 *destData; + + + /// Create a new image pointer + destData = TRACKED_NEW UInt8[ fLevelSizes[ 0 ] ]; + hsAssert( destData != nil, "Out of memory in ClipToMaxSize()" ); + memcpy( destData, fImage, fLevelSizes[ 0 ] ); + +#ifdef MEMORY_LEAK_TRACER + IRemoveFromMemRecord( (UInt8 *)fImage ); +#endif + delete [] fImage; + plProfile_DelMem(MemMipmaps, fTotalSize); + + fImage = destData; + fTotalSize = fLevelSizes[ 0 ]; + fNumLevels = 1; + fFlags |= kForceOneMipLevel; + IBuildLevelSizes(); + plProfile_NewMem(MemMipmaps, fTotalSize); + +#ifdef MEMORY_LEAK_TRACER + IAddToMemRecord( this, plRecord::kViaClipToMaxSize ); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// + + + +#include "hsCodecManager.h" + + +namespace { + const UInt32 kVersion = 1; + const hsScalar kDefaultSigma = 1.f; + const UInt32 kDefaultDetailBias = 5; + + // Color masks (out of 0-2) + const UInt8 fColorMasks[ 10 ][ 3 ] = { { 2, 0, 0 }, { 0, 2, 2 }, { 2, 0, 2 }, { 0, 2, 0 }, + { 0, 0, 2 }, { 2, 2, 0 }, { 2, 2, 2 }, { 2, 0, 1 }, { 0, 2, 1 }, { 1, 0, 2 } }; + +} + +/////////////////////////////////////////////////////////////////////////////// +//// plFilterMask Helper Class //////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// This filter mask class actually introduces a half pixel per level shift +// artifact. It's done that since I wrote it a couple of years ago, and nobody's +// noticed but me, so it's probably okay. But fixing it would be easy. The +// mask needs to extend -n down and n+1 up. To see why, notice that the current +// upper level pixel (i,j) maps to four lower pixels (i<<1,j<<1) .. (i<<1+1,j<<1+1). +// So the mask value on pixel (i<<1,j<<1) should give equal weight to those other +// 3 pixels. +// The mask here is symmetric about the src pixel, which is right if you are +// just filtering the src bitmap. But we're filtering and resampling, so the +// mask needs to be symmetric about the dst pixel, which is off by a half +// dst pixel, or on whole src pixel. +class plFilterMask +{ + protected: + int fExt; + hsScalar **fMask; + + public: + + plFilterMask( hsScalar sig ); + virtual ~plFilterMask(); + + int Begin() const { return -fExt; } + int End() const { return fExt; } + + hsScalar Mask( int i, int j ) const { return fMask[ i ][ j ]; } +}; + +plFilterMask::plFilterMask( hsScalar sig ) +{ + fExt = (int)( sig * 2.f ); + if( fExt < 1 ) + fExt = 1; + + hsScalar **m = TRACKED_NEW hsScalar *[ ( fExt << 1 ) + 1 ]; + m += fExt; + int i, j; + hsScalar ooSigSq = 1.f / ( sig * sig ); + + for( i = -fExt; i <= fExt; i++ ) + { + m[ i ] = ( TRACKED_NEW hsScalar[ ( fExt << 1 ) + 1] ) + fExt; + for( j = -fExt; j <= fExt; j++ ) + { + m[ i ][ j ] = expf( -( i*i + j*j ) * ooSigSq ); + } + } + fMask = m; +} + +plFilterMask::~plFilterMask() +{ + int i; + for( i = -fExt; i <= fExt; i++ ) + delete [] ( fMask[ i ] - fExt ); + delete [] ( fMask - fExt ); +} + + +/////////////////////////////////////////////////////////////////////////////// +//// Some More Functions ////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +plMipmap::plMipmap( plMipmap *bm, hsScalar sig, UInt32 createFlags, + hsScalar detailDropoffStart, hsScalar detailDropoffStop, + hsScalar detailMax, hsScalar detailMin) +{ + int i; + + + hsAssert(bm->GetHeight() && bm->GetWidth(), "Degenerate Bitmap into Mipmap"); + + if( sig <= 0 ) + sig = kDefaultSigma; + + fHeight = bm->GetHeight(); + fWidth = bm->GetWidth(); + fRowBytes = bm->GetRowBytes(); + fPixelSize = bm->GetPixelSize(); + fImage = nil; + fFlags = bm->GetFlags(); + + UInt32 minDim = fHeight < fWidth ? fHeight : fWidth; + for( fNumLevels = 0; (minDim >> fNumLevels); fNumLevels++ ) /* empty */; + + fLevelSizes = nil; + fCompressionType = kUncompressed; + fUncompressedInfo.fType = bm->fUncompressedInfo.fType; + IBuildLevelSizes(); + + fTotalSize = 0; + for( i = 0; i < fNumLevels; i++ ) + fTotalSize += fLevelSizes[ i ]; + fCurrLevel = 0; + + fImage = (void *)TRACKED_NEW UInt8[ fTotalSize ]; + memset(fImage, 0, fTotalSize); + memcpy( fImage, bm->fImage, bm->GetLevelSize( 0 ) ); +#ifdef MEMORY_LEAK_TRACER + IAddToMemRecord( this, plRecord::kViaDetailMapConstructor ); +#endif + plProfile_NewMem(MemMipmaps, fTotalSize); + + /// Filter levels! + plFilterMask mask(sig); + + /// First, fill in all the mipmap levels sans detail levels + for( i = 1; i < fNumLevels; i++ ) + ICreateLevelNoDetail(i, mask); + + if (createFlags & kCreateDetailMask) + { + // Fill in the detail levels afterwards, so we can just grab the current level's texture + // alpha (old way did it at the same time, which accumulated the detail alpha each level down) + detailDropoffStart *= fNumLevels; + detailDropoffStop *= fNumLevels; + + switch( createFlags & kCreateDetailMask ) + { + case kCreateDetailAlpha: + fFlags |= kAlphaChannelFlag; + for( i = 0; i < fNumLevels; i++ ) + IBlendLevelDetailAlpha(i, mask, detailDropoffStart, detailDropoffStop, detailMax, detailMin); + break; + + case kCreateDetailAdd: + for( i = 0; i < fNumLevels; i++ ) + IBlendLevelDetailAdd( i, mask, detailDropoffStart, detailDropoffStop, detailMax, detailMin); + break; + + case kCreateDetailMult: + for( i = 0; i < fNumLevels; i++ ) + IBlendLevelDetailMult(i, mask, detailDropoffStart, detailDropoffStop, detailMax, detailMin); + break; + } + } + if( createFlags & kCreateCarryAlpha ) + { + for( i = 1; i < fNumLevels; i++ ) + ICarryZeroAlpha(i); + } + if( createFlags & (kCreateCarryWhite | kCreateCarryBlack) ) + { + UInt32 col = createFlags & kCreateCarryWhite ? 0x00ffffff : 0x00000000; + for( i = 1; i < fNumLevels; i++ ) + ICarryColor(i, col); + } + + +#ifdef MEMORY_LEAK_TRACER + fNumMipmaps++; +#endif +} + +//// IGetDetailLevelAlpha ///////////////////////////////////////////////////// +// Given the detail range and the current level, returns the detail's alpha +// value at that level. + +hsScalar plMipmap::IGetDetailLevelAlpha( UInt8 level, hsScalar dropStart, hsScalar dropStop, hsScalar min, hsScalar max ) +{ + hsScalar detailAlpha; + + + detailAlpha = ( level - dropStart ) * ( min - max ) / ( dropStop - dropStart ) + max; + + if( min < max ) + detailAlpha = hsMinimum( max, hsMaximum( min, detailAlpha ) ); + else + detailAlpha = hsMinimum( min, hsMaximum( max, detailAlpha ) ); + + return detailAlpha; +} + +void plMipmap::SetBitmapAsLevel(UInt8 iDst, plMipmap *bm, hsScalar sig, UInt32 createFlags, + hsScalar detailDropoffStart, hsScalar detailDropoffStop, + hsScalar detailMax, hsScalar detailMin) +{ + SetCurrLevel( iDst ); + + hsAssert((bm->fHeight == fCurrLevelHeight) && (bm->fWidth == fCurrLevelWidth), "Wrong size bitmap for Mipmap level."); + + memcpy( fCurrLevelPtr, bm->fImage, bm->GetLevelSize( 0 ) ); + plFilterMask mask(sig); + int i; + + // Fill in levels w/o detail first + for( i = iDst+1; i < fNumLevels; i++ ) + ICreateLevelNoDetail(i, mask); + + // Now fill in the detail alphas, if we have any + switch( createFlags & kCreateDetailMask ) + { + case 0: + break; + + case kCreateDetailAlpha: + for( i = iDst; i < fNumLevels; i++ ) + IBlendLevelDetailAlpha(i, mask, detailDropoffStart, detailDropoffStop, detailMax, detailMin); + break; + + case kCreateDetailAdd: + for( i = iDst; i < fNumLevels; i++ ) + IBlendLevelDetailAdd(i, mask, detailDropoffStart, detailDropoffStop, detailMax, detailMin); + break; + + case kCreateDetailMult: + for( i = iDst; i < fNumLevels; i++ ) + IBlendLevelDetailMult(i, mask, detailDropoffStart, detailDropoffStop, detailMax, detailMin); + break; + } + if( createFlags & kCreateCarryAlpha ) + { + for( i = iDst+1; i < fNumLevels; i++ ) + ICarryZeroAlpha(i); + } + if( createFlags & (kCreateCarryWhite | kCreateCarryBlack) ) + { + UInt32 col = createFlags & kCreateCarryWhite ? 0x00ffffff : 0x00000000; + for( i = iDst+1; i < fNumLevels; i++ ) + ICarryColor(i, col); + } + + SetCurrLevel(0); +} + +//// ICreateLevelNoDetail ///////////////////////////////////////////////////// +// Creates one level of a mipmap based on a filter on the previous layer. +// This version assumes no detail map fading. + +void plMipmap::ICreateLevelNoDetail( UInt8 iDst, const plFilterMask& mask ) +{ + hsAssert(fPixelSize == 32, "Only 32 bit implemented"); + ASSERT_UNCOMPRESSED(); + + int i, j, ii, jj; + + if( 32 == fPixelSize ) + { + SetCurrLevel(iDst); + + UInt8 *src = (UInt8 *)GetLevelPtr( iDst-1 ); + UInt8 *dst = (UInt8 *)GetLevelPtr(iDst); + + UInt32 srcRowBytes = fCurrLevelRowBytes << 1; + UInt32 srcHeight = fCurrLevelHeight << 1; + UInt32 srcWidth = fCurrLevelWidth << 1; + + for( i = 0; i < fCurrLevelHeight; i++ ) + { + for( j = 0; j < fCurrLevelWidth; j++ ) + { + UInt8 *center = src + (i << 1) * srcRowBytes + (j << 3); + + UInt32 chan; + for( chan = 0; chan < 4; chan++ ) + { + hsScalar w = 0; + hsScalar a = 0; + + for( ii = mask.Begin(); ii <= mask.End(); ii++ ) + { + for( jj = mask.Begin(); jj <= mask.End(); jj++ ) + { + if( (ii + (i << 1) >= 0)&&(ii + (i << 1) < srcHeight) + &&(jj + (j << 1) >= 0)&&(jj + (j << 1) < srcWidth) ) + { + w += mask.Mask(ii, jj); + a += (hsScalar(center[ii*srcRowBytes + (jj<<2) + chan]) + 0.5f) * mask.Mask(ii, jj); + } + } + } + a /= w; + + dst[i * fCurrLevelRowBytes + (j << 2) + chan] = (UInt8)a; + } + } + } + } +} + +void plMipmap::ICarryZeroAlpha(UInt8 iDst) +{ + hsAssert(fPixelSize == 32, "Only 32 bit implemented"); + ASSERT_UNCOMPRESSED(); + + int i, j; + + if( 32 == fPixelSize ) + { + SetCurrLevel(iDst); + + UInt8 *src = (UInt8 *)GetLevelPtr( iDst-1 ); + UInt8 *dst = (UInt8 *)GetLevelPtr(iDst); + + UInt32 srcRowBytes = fCurrLevelRowBytes << 1; + UInt32 srcHeight = fCurrLevelHeight << 1; + UInt32 srcWidth = fCurrLevelWidth << 1; + + const UInt8 alphaOff = 3; + for( i = 0; i < fCurrLevelHeight; i++ ) + { + for( j = 0; j < fCurrLevelWidth; j++ ) + { + UInt8 *center = src + (i << 1) * srcRowBytes + (j << 3) + alphaOff; + + if( !center[0] + || !center[4] + || !center[srcRowBytes + 0] + || !center[srcRowBytes + 4] ) + { + dst[i * fCurrLevelRowBytes + (j << 2) + alphaOff] = 0; + } + } + } + } +} + +void plMipmap::ICarryColor(UInt8 iDst, UInt32 col) +{ + hsAssert(fPixelSize == 32, "Only 32 bit implemented"); + ASSERT_UNCOMPRESSED(); + + int i, j; + + if( 32 == fPixelSize ) + { + SetCurrLevel(iDst); + + UInt32* src = (UInt32*)GetLevelPtr( iDst-1 ); + UInt32* dst = (UInt32*)GetLevelPtr(iDst); + + UInt32 srcHeight = fCurrLevelHeight << 1; + UInt32 srcWidth = fCurrLevelWidth << 1; + + const UInt8 alphaOff = 3; + for( i = 0; i < fCurrLevelHeight; i++ ) + { + for( j = 0; j < fCurrLevelWidth; j++ ) + { + UInt32* center = src + (i << 1) * srcWidth + (j << 1); + + if( ((center[0] & 0x00ffffff) == col) + ||((center[1] & 0x00ffffff) == col) + ||((center[srcHeight] & 0x00ffffff) == col) + ||((center[srcHeight+1] & 0x00ffffff) == col) ) + { + dst[i * fCurrLevelWidth + j] &= 0xff000000; + dst[i * fCurrLevelWidth + j] |= col; + } + } + } + } +} + +//// IBlendLevelDetailAlpha /////////////////////////////////////////////////// +// Blends in the detail alpha for a given level. This version assumes +// standard detail map blending. + +void plMipmap::IBlendLevelDetailAlpha( UInt8 iDst, const plFilterMask& mask, + hsScalar detailDropoffStart, hsScalar detailDropoffStop, + hsScalar detailMax, hsScalar detailMin ) +{ + hsAssert(fPixelSize == 32, "Only 32 bit implemented"); + ASSERT_UNCOMPRESSED(); + + int i, j; + UInt32 offset; + + + SetCurrLevel(iDst); + + UInt8 *dst = (UInt8 *)GetLevelPtr(iDst); + + hsScalar detailAlpha = IGetDetailLevelAlpha( iDst, detailDropoffStart, detailDropoffStop, detailMin, detailMax ); + + for( i = 0; i < fCurrLevelHeight; i++ ) + { + for( j = 0; j < fCurrLevelWidth; j++ ) + { + UInt32 chan = 3; // Alpha channel only + offset = i * fCurrLevelRowBytes + (j << 2) + chan; + + float a = (float)dst[ offset ] * detailAlpha; + dst[ offset ] = (UInt8)a; + } + } +} + +//// IBlendLevelDetailAdd ///////////////////////////////////////////////////// +// Blends in the detail alpha for a given level. This version assumes additive +// detail map blending. (Shesh, gotta hate C sometimes....) + +void plMipmap::IBlendLevelDetailAdd( UInt8 iDst, const plFilterMask& mask, + hsScalar detailDropoffStart, hsScalar detailDropoffStop, + hsScalar detailMax, hsScalar detailMin ) +{ + hsAssert(fPixelSize == 32, "Only 32 bit implemented"); + ASSERT_UNCOMPRESSED(); + + int i, j; + UInt32 offset; + + + SetCurrLevel(iDst); + + UInt8 *dst = (UInt8 *)GetLevelPtr(iDst); + + hsScalar detailAlpha = IGetDetailLevelAlpha( iDst, detailDropoffStart, detailDropoffStop, detailMin, detailMax ); + + for( i = 0; i < fCurrLevelHeight; i++ ) + { + for( j = 0; j < fCurrLevelWidth; j++ ) + { + UInt32 chan; + + /// Blend all but the alpha channel, since we're doing additive blending + for( chan = 0; chan < 3; chan++ ) + { + offset = i * fCurrLevelRowBytes + (j << 2) + chan; + + float a = (float)dst[ offset ] * detailAlpha; + dst[ offset ] = (UInt8)a; + } + } + } +} + +//// IBlendLevelDetailMult //////////////////////////////////////////////////// +// Blends in the detail alpha for a given level. This version assumes +// multiplicitive detail map blending. (Shesh, gotta hate C sometimes....) + +void plMipmap::IBlendLevelDetailMult( UInt8 iDst, const plFilterMask& mask, + hsScalar detailDropoffStart, hsScalar detailDropoffStop, + hsScalar detailMax, hsScalar detailMin ) +{ + hsAssert(fPixelSize == 32, "Only 32 bit implemented"); + ASSERT_UNCOMPRESSED(); + + int i, j; + UInt32 offset; + + + SetCurrLevel(iDst); + + UInt8 *dst = (UInt8 *)GetLevelPtr(iDst); + + hsScalar detailAlpha = IGetDetailLevelAlpha( iDst, detailDropoffStart, detailDropoffStop, detailMin, detailMax ); + hsScalar invDetailAlpha = ( 1.f - detailAlpha ) * 255.f; + + for( i = 0; i < fCurrLevelHeight; i++ ) + { + for( j = 0; j < fCurrLevelWidth; j++ ) + { + UInt32 chan; + + for( chan = 0; chan < 4; chan++ ) + { + offset = i * fCurrLevelRowBytes + (j << 2) + chan; + + float a = (float)dst[ offset ]; + + // Mult should fade to white, not black like with additive blending + a = invDetailAlpha + a * detailAlpha; + + dst[ offset ] = (UInt8)a; + } + } + } +} + +//// EnsureKonstantBorder ///////////////////////////////////////////////////// +// Checks a mipmap's levels and makes sure that if the top level has constant +// border color/alpha, the rest of the levels get that border, too, regardless +// of filtering. (This is us guessing that that border was apparently what +// was intented, thus removing ugly stretching problems on clamped textures). + +void plMipmap::EnsureKonstantBorder( hsBool clampU, hsBool clampV ) +{ + if( fPixelSize != 32 ) + { + hsAssert( false, "Only 32 bit color supported in EnsureKonstantBorder()" ); + return; + } + + if( !clampU && !clampV ) + return; // Um, exactly what are we supposed to do, again? + + UInt32 uColor, vColor; + int i; + + + if( clampU && !IGrabBorderColor( false, &uColor ) ) + return; + if( clampV && !IGrabBorderColor( true, &vColor ) ) + return; + + if( clampU && clampV && ( uColor != vColor ) ) + return; + + for( i = 1; i < fNumLevels; i++ ) + { + SetCurrLevel( i ); + if( clampU ) + ISetCurrLevelUBorder( uColor ); + if( clampV ) + ISetCurrLevelVBorder( vColor ); + } + + SetCurrLevel( 0 ); +} + +//// IGrabBorderColor ///////////////////////////////////////////////////////// +// Grabs the top level's border color, or returns false if not all pixels +// on the border are the same color/alpha. + +hsBool plMipmap::IGrabBorderColor( hsBool grabVNotU, UInt32 *color ) +{ + int i; + UInt32 *src1 = (UInt32 *)fImage, *src2, testColor; + + + if( !grabVNotU ) + { + src2 = (UInt32 *)( (UInt8 *)fImage + fRowBytes * ( fHeight - 1 ) ); + + testColor = *src1; + for( i = 0; i < fWidth; i++ ) + { + if( src1[ i ] != testColor || src2[ i ] != testColor ) + return false; + } + + *color = testColor; + return true; + } + else + { + src2 = src1 + ( fWidth - 1 ); + + testColor = *src1; + for( i = 0; i < fHeight; i++ ) + { + if( *src1 != testColor || *src2 != testColor ) + return false; + src1 += fWidth; + src2 += fWidth; + } + + *color = testColor; + return true; + } +} + +//// ISetCurrLevelUBorder ///////////////////////////////////////////////////// + +void plMipmap::ISetCurrLevelUBorder( UInt32 color ) +{ + int i; + UInt32 *src1 = (UInt32 *)fCurrLevelPtr, *src2; + + + src2 = (UInt32 *)( (UInt8 *)fCurrLevelPtr + fCurrLevelRowBytes * ( fCurrLevelHeight - 1 ) ); + + for( i = 0; i < fCurrLevelWidth; i++ ) + { + src1[ i ] = color; + src2[ i ] = color; + } +} + +//// ISetCurrLevelVBorder ///////////////////////////////////////////////////// + +void plMipmap::ISetCurrLevelVBorder( UInt32 color ) +{ + int i; + UInt32 *src1 = (UInt32 *)fCurrLevelPtr, *src2; + + + src2 = src1 + ( fCurrLevelWidth - 1 ); + + for( i = 0; i < fCurrLevelHeight; i++ ) + { + *src1 = color; + *src2 = color; + src1 += fCurrLevelWidth; + src2 += fCurrLevelWidth; + } +} + +void plMipmap::Filter(hsScalar sig) +{ + hsAssert(fPixelSize == 32, "Only 32 bit implemented"); + ASSERT_UNCOMPRESSED(); + + int i, j, ii, jj; + + if( 32 == fPixelSize ) + { + UInt8 *dst = (UInt8 *)(fImage); + + UInt8* src = (UInt8*)HSMemory::New(fRowBytes * fHeight); + HSMemory::BlockMove(dst, src, fRowBytes * fHeight); + + if( sig <= 0 ) + sig = kDefaultSigma; + + plFilterMask mask(sig); + + UInt32 srcRowBytes = fRowBytes; + UInt32 srcHeight = fHeight; + UInt32 srcWidth = fWidth; + + for( i = 0; i < fHeight; i++ ) + { + for( j = 0; j < fWidth; j++ ) + { + UInt8 *center = src + i * srcRowBytes + (j << 2); + + UInt32 chan; + for( chan = 0; chan < 4; chan++ ) + { + hsScalar w = 0; + hsScalar a = 0; + + for( ii = mask.Begin(); ii <= mask.End(); ii++ ) + { + for( jj = mask.Begin(); jj <= mask.End(); jj++ ) + { + if( (ii + i >= 0)&&(ii + i < srcHeight) + &&(jj + j >= 0)&&(jj + j < srcWidth) ) + { + w += mask.Mask(ii, jj); + a += (hsScalar(center[ii*srcRowBytes + (jj<<2) + chan]) + 0.5f) * mask.Mask(ii, jj); + } + } + } + a /= w; + + dst[i * fRowBytes + (j << 2) + chan] = (UInt8)(a); + } + } + } + + HSMemory::Delete(src); + } +} + +static void CopyPixels(UInt32 srcWidth, UInt32 srcHeight,void *srcPixels, + UInt32 skipX, UInt32 skipY, UInt32 dstFormat, + void * &destPixels, UInt32 copyOptions) +{ + int y; + int xInc = skipX + 1; + int yInc = skipY + 1; + int i = 0; + int firstX = 0; + + int rowSkip = yInc * srcWidth; // Number of pixels to skip for each line + + const hsRGBAColor32 *p =(const hsRGBAColor32 *)srcPixels; + UInt16 *pixels16 = (UInt16*)destPixels; + UInt8 *pixels8 = (UInt8*)destPixels; + + for(y = 0; y < srcHeight; y += yInc, firstX += rowSkip) + { + const hsRGBAColor32 *srcPix = &(p[firstX]); + int x; + switch (dstFormat) + { + case plMipmap::kPixelAI88: + for(x =0; x < srcWidth; x += xInc) + pixels16[i++]= ((srcPix[x].a & 0xff) << 8) | (srcPix[x].r & 0xff); + break; + case plMipmap::kPixelI8: + for(x =0; x < srcWidth; x += xInc) + pixels8[i++]= srcPix[x].r; + break; + case plMipmap::kPixelARGB4444: + for(x = 0; x < srcWidth; x += xInc) + pixels16[i++]= (((srcPix[x].r>>4) & 0xf) << 8) + | (((srcPix[x].g >> 4) & 0xf) << 4) + | (((srcPix[x].b >> 4) & 0xf) ) + | (((srcPix[x].a >> 4) & 0xf) << 12); + break; + case plMipmap::kPixelARGB1555: + for(x = 0; x < srcWidth; x += xInc) + pixels16[i++]= (((srcPix[x].r>>3) & 0x1f) << 10) | + (((srcPix[x].g >> 3) & 0x1f) << 5) | + ((srcPix[x].b >> 3) & 0x1f) | ((srcPix[x].a == 0) ? 0 : 0x8000); + break; + } + } + destPixels = (char *)destPixels + (i * ((dstFormat == plMipmap::kPixelI8 ) ? 1 : 2)); +} + + +UInt32 plMipmap::CopyOutPixels(UInt32 destXSize, UInt32 destYSize, + UInt32 dstFormat, void *destPixels, UInt32 copyOptions) +{ + + hsAssert(fPixelSize == 32, "Only 32 bit implemented"); + ASSERT_UNCOMPRESSED(); + + int i; + + int skipX = fWidth/destXSize - 1; + int skipY = fHeight/destYSize - 1; + + hsAssert(!fCurrLevel,"Mip Map not at level 0"); + + for(i = 0 ; i < (fNumLevels - fCurrLevel); i++) + { + CopyPixels(fWidth >> i , fHeight >> i, GetLevelPtr( i ), skipX, skipY, + dstFormat, destPixels, copyOptions); + } + + return 0; +} + +//// CopyFrom ///////////////////////////////////////////////////////////////// + +void plMipmap::CopyFrom( const plMipmap *source ) +{ + hsAssert( source != nil, "nil source in plMipmap::CopyFrom()" ); + + plProfile_DelMem(MemMipmaps, fTotalSize); +#ifdef MEMORY_LEAK_TRACER + IRemoveFromMemRecord( (UInt8 *)fImage ); +#endif + delete [] fImage; + + fWidth = source->fWidth; + fHeight = source->fHeight; + fRowBytes = source->fRowBytes; + fPixelSize = source->fPixelSize; + fFlags = source->fFlags; + fSpace = source->fSpace; + fCompressionType = source->fCompressionType; + fTotalSize = source->fTotalSize; + + fImage = (void *)TRACKED_NEW UInt8[ fTotalSize ]; + memcpy( fImage, source->fImage, fTotalSize ); +#ifdef MEMORY_LEAK_TRACER + IAddToMemRecord( this, plRecord::kViaCopyFrom ); +#endif + plProfile_NewMem(MemMipmaps, fTotalSize); + + fNumLevels = source->fNumLevels; + + switch( fCompressionType ) + { + case kDirectXCompression: + { + fDirectXInfo.fBlockSize = source->fDirectXInfo.fBlockSize; + fDirectXInfo.fCompressionType = source->fDirectXInfo.fCompressionType; + } + break; + case kUncompressed: + case kJPEGCompression: + fUncompressedInfo.fType = source->fUncompressedInfo.fType; + break; + default: + hsAssert(false, "Reading unknown compression format."); + break; + } + + // Gotta do this AFTER we set our block size, etc. + IBuildLevelSizes(); + + // We just changed our texture, so if we have a texture ref, we better dirty it + if( GetDeviceRef() != nil ) + GetDeviceRef()->SetDirty( true ); +} + +//// Clone //////////////////////////////////////////////////////////////////// + +plMipmap *plMipmap::Clone() const +{ + plMipmap *newMap = TRACKED_NEW plMipmap; + + newMap->CopyFrom( this ); + + return newMap; +} + +//// Composite //////////////////////////////////////////////////////////////// +// Compositing function. Take a (smaller) mipmap and composite it onto this one +// at the given location + +void plMipmap::Composite( plMipmap *source, UInt16 x, UInt16 y, plMipmap::CompositeOptions *options ) +{ + UInt8 level, numLevels, srcNumLevels, srcLevelOffset, levelsToSkip; + UInt16 pX, pY; + UInt32 *srcLevelPtr, *dstLevelPtr, *srcPtr, *dstPtr; + UInt32 srcRowBytes, dstRowBytes, srcRowBytesToCopy, r, g, b, dR, dG, dB, srcWidth, srcHeight; + UInt32 srcAlpha, oneMinusAlpha, destAlpha; + UInt16 srcClipX, srcClipY; + + + // Currently we only support 32 bit uncompressed mipmaps + if( fPixelSize != 32 || fCompressionType == kDirectXCompression ) + { + hsAssert( false, "Destination mipmap on composite has unsupported format" ); + return; + } + if( source->fPixelSize != 32 || source->fCompressionType == kDirectXCompression ) + { + hsAssert( false, "Source mipmap on composite has unsupported format" ); + return; + } + + // Grab the correct options pointer + if( options == nil ) + { + static CompositeOptions defaultOptions; + options = &defaultOptions; + } + + // Src level skipping + srcWidth = source->fWidth; + srcHeight = source->fHeight; + srcLevelPtr = (UInt32 *)source->fImage; + srcRowBytes = source->fRowBytes; + srcNumLevels = source->fNumLevels; + for( srcLevelOffset = 0, levelsToSkip = options->fSrcLevelsToSkip; levelsToSkip > 0; levelsToSkip--, srcLevelOffset++ ) + { + srcWidth >>= 1; + srcHeight >>= 1; + srcLevelPtr += source->fLevelSizes[ srcLevelOffset ] >> 2; + srcRowBytes >>= 1; + srcNumLevels--; + } + + // Src clipping setup + srcClipX = srcClipY = 0; + srcRowBytesToCopy = srcRowBytes; + if( options->fSrcClipY > 0 ) + { + srcHeight -= options->fSrcClipY; + srcClipY = options->fSrcClipY; + } + if( options->fSrcClipHeight > 0 ) + { + srcHeight = options->fSrcClipHeight; + } + if( options->fSrcClipX > 0 ) + { + srcWidth -= options->fSrcClipX; + srcClipX = options->fSrcClipX; + srcRowBytesToCopy -= options->fSrcClipX * ( source->fPixelSize >> 3 ); + } + if( options->fSrcClipWidth > 0 ) + { + srcWidth = options->fSrcClipWidth; + srcRowBytesToCopy = srcWidth * ( source->fPixelSize >> 3 ); + } + + // Position checks + if( x + srcWidth > fWidth || y + srcHeight > fHeight ) + { + hsAssert( false, "Illegal position on mipmap composite" ); + return; + } + + // Do the composite on each level + numLevels = fNumLevels; + if( numLevels > srcNumLevels ) + numLevels = srcNumLevels; + + dstLevelPtr = (UInt32 *)fImage; + dstRowBytes = fRowBytes; + + if( options->fFlags & kForceOpaque ) + { + for( level = 0; level < numLevels; level++, y >>= 1, x >>= 1 ) + { + srcPtr = srcLevelPtr; + dstPtr = dstLevelPtr + ( y * dstRowBytes >> 2 ) + x; + + // Clipping + srcPtr += srcClipY * ( srcRowBytes >> 2 ) + srcClipX; + + for( pY = (UInt16)srcHeight; pY > 0; pY-- ) + { + memcpy( dstPtr, srcPtr, srcRowBytesToCopy ); + for( pX = 0; pX < srcWidth; pX++ ) + { + // Force the alpha opaque + dstPtr[ pX ] |= 0xff000000; + } + dstPtr += dstRowBytes >> 2; + srcPtr += srcRowBytes >> 2; + } + + srcLevelPtr += source->fLevelSizes[ level + srcLevelOffset ] >> 2; + dstLevelPtr += fLevelSizes[ level ] >> 2; + srcRowBytes >>= 1; + dstRowBytes >>= 1; + srcRowBytesToCopy >>= 1; + if( srcHeight > 1 ) + srcHeight >>= 1; + srcClipX >>= 1; + srcClipY >>= 1; + } + } + else if( options->fFlags & kCopySrcAlpha ) + { + for( level = 0; level < numLevels; level++, y >>= 1, x >>= 1 ) + { + srcPtr = srcLevelPtr; + dstPtr = dstLevelPtr + ( y * dstRowBytes >> 2 ) + x; + + // Clipping + srcPtr += srcClipY * ( srcRowBytes >> 2 ) + srcClipX; + + for( pY = (UInt16)srcHeight; pY > 0; pY-- ) + { + memcpy( dstPtr, srcPtr, srcRowBytesToCopy ); + dstPtr += dstRowBytes >> 2; + srcPtr += srcRowBytes >> 2; + } + + srcLevelPtr += source->fLevelSizes[ level + srcLevelOffset ] >> 2; + dstLevelPtr += fLevelSizes[ level ] >> 2; + srcRowBytes >>= 1; + dstRowBytes >>= 1; + srcRowBytesToCopy >>= 1; + if( srcHeight > 1 ) + srcHeight >>= 1; + srcClipX >>= 1; + srcClipY >>= 1; + } + } + else if( options->fFlags & kMaskSrcAlpha ) + { + for( level = 0; level < numLevels; level++, y >>= 1, x >>= 1 ) + { + srcPtr = srcLevelPtr; + dstPtr = dstLevelPtr + ( y * dstRowBytes >> 2 ) + x; + + // Clipping + srcPtr += srcClipY * ( srcRowBytes >> 2 ) + srcClipX; + + for( pY = (UInt16)srcHeight; pY > 0; pY-- ) + { + for( pX = 0; pX < srcWidth; pX++ ) + { + srcAlpha = options->fOpacity * ( ( srcPtr[ pX ] >> 16 ) & 0x0000ff00 ) / 255 / 256; + if( srcAlpha != 0 ) + dstPtr[ pX ] = ( srcPtr[ pX ] & 0x00ffffff ) | ( srcAlpha << 24 ); + } + dstPtr += dstRowBytes >> 2; + srcPtr += srcRowBytes >> 2; + } + + srcLevelPtr += source->fLevelSizes[ level + srcLevelOffset ] >> 2; + dstLevelPtr += fLevelSizes[ level ] >> 2; + srcRowBytes >>= 1; + dstRowBytes >>= 1; + srcRowBytesToCopy >>= 1; + if( srcHeight > 1 ) + srcHeight >>= 1; + srcClipX >>= 1; + srcClipY >>= 1; + } + } + else + { + for( level = 0; level < numLevels; level++, y >>= 1, x >>= 1 ) + { + srcPtr = srcLevelPtr; + dstPtr = dstLevelPtr + ( y * dstRowBytes >> 2 ) + x; + + // Clipping + srcPtr += srcClipY * ( srcRowBytes >> 2 ) + srcClipX; + + for( pY = (UInt16)srcHeight; pY > 0; pY-- ) + { + // Reverse the loop so we can count downwards--slightly faster + pX = (UInt16)srcWidth; + do + { + pX--; + + // Wacko trick here. Alphas are 0-255, which means scaling by alpha would + // be a v' = v * alpha / 255 operation sequence. However, since we hate + // dividing by 255 all the time, we actually scale the alpha just ever so + // slightly so it's 0-256, which makes the divide a simple shift. Note + // that this will result in some tiny bit of aliasing, but it shouldn't be + // enough to notice + + if (!(srcPtr[pX] >> 24)) // Zero alpha. Skip this pixel + continue; + + srcAlpha = options->fOpacity * ( ( srcPtr[ pX ] >> 16 ) & 0x0000ff00 ) / 255 / 256; + oneMinusAlpha = 256 - srcAlpha; + destAlpha = dstPtr[ pX ] & 0xff000000; + + r = (UInt32)((( srcPtr[ pX ] >> 16 ) & 0x000000ff) * options->fRedTint); + g = (UInt32)((( srcPtr[ pX ] >> 8 ) & 0x000000ff) * options->fGreenTint); + b = (UInt32)((( srcPtr[ pX ] ) & 0x000000ff) * options->fBlueTint); + dR = ( dstPtr[ pX ] >> 16 ) & 0x000000ff; + dG = ( dstPtr[ pX ] >> 8 ) & 0x000000ff; + dB = ( dstPtr[ pX ] ) & 0x000000ff; + r = ( r * srcAlpha ) >> 8; + g = ( g * srcAlpha ) >> 8; + b = ( b * srcAlpha ) >> 8; + dR = ( dR * oneMinusAlpha ) >> 8; + dG = ( dG * oneMinusAlpha ) >> 8; + dB = ( dB * oneMinusAlpha ) >> 8; + + // Dest alpha for now is just our original dest alpha + dstPtr[ pX ] = ( ( r + dR ) << 16 ) | ( ( g + dG ) << 8 ) | ( b + dB ) | destAlpha; + + if( !( options->fFlags & kBlendWriteAlpha ) ) + continue; + + // Unless our blend option is set of course + dstPtr[ pX ] = ( dstPtr[ pX ] & 0x00ffffff ) | ( srcAlpha << 24 ); + + } while( pX > 0 ); + + dstPtr += dstRowBytes >> 2; + srcPtr += srcRowBytes >> 2; + } + + srcLevelPtr += source->fLevelSizes[ level + srcLevelOffset ] >> 2; + dstLevelPtr += fLevelSizes[ level ] >> 2; + srcRowBytes >>= 1; + dstRowBytes >>= 1; + if( srcWidth > 1 ) + srcWidth >>= 1; + if( srcHeight > 1 ) + srcHeight >>= 1; + srcClipX >>= 1; + srcClipY >>= 1; + } + } + + // All done! + if( GetDeviceRef() != nil ) + GetDeviceRef()->SetDirty( true ); +} + +//// Colorize ///////////////////////////////////////////////////////////////// +// Colorizes a mipmap so that each level is color-coded. Assume max 10 levels +// (coloring wraps after 10). + +void plMipmap::Colorize() +{ + UInt32 currColor, width, height; + UInt8 currLevel; + + + if( fPixelSize != 32 && fCompressionType != kDirectXCompression ) + { + /// Most likely this is a luminance or luminance/alpha map, + /// so we ignore it (it's up to the device to make sure we + /// only get 32-bit or compressed mipmaps) + return; + } + + if( fFlags & kForceOneMipLevel ) + { + // Don't colorize if it's not mipmapped... + return; + } + + /// First handle compressed levels, if any + currLevel = 0; + currColor = 0; + width = fWidth; + height = fHeight; + if( fCompressionType == kDirectXCompression ) + { + for( ; currLevel < fNumLevels; currLevel++ ) + { + /// Are we over the threshold? + SetCurrLevel( currLevel ); + if( ( fCurrLevelWidth | fCurrLevelHeight ) & 0x03 ) + break; + + /// Since this level is compressed, we have to use the codec... + hsCodecManager::Instance().ColorizeCompMipmap( this, fColorMasks[ currColor ] ); + + /// Increment the color + currColor = ( currColor >=9 ) ? 0 : currColor + 1; + } + } + + /// Now loop through the uncompressed levels + for( ; currLevel < fNumLevels; currLevel++ ) + { + /// Do this one + IColorLevel( currLevel, fColorMasks[ currColor ] ); + + /// Increment the color + currColor = ( currColor >=9 ) ? 0 : currColor + 1; + } + + SetCurrLevel( 0 ); +} + +//// IColorLevel ////////////////////////////////////////////////////////////// +// Colorizes the current level of a mipmap according to the color mask given +// (percentages of r, g & b in the range of 0-2). + +void plMipmap::IColorLevel( UInt8 level, const UInt8 *colorMask ) +{ + UInt32 index, max, color, gray, grayDiv2, *data, width, height; + UInt8 compMasks[ 3 ][ 2 ] = { { 0, 0 }, { 0, 0xff }, { 0xff, 0 } }; + + + data = (UInt32 *)GetLevelPtr( level, &width, &height ); + max = fLevelSizes[ level ] >> 2; + + for( index = 0; index < max; index++ ) + { + /// Get color and calculate gray (average of r, g & b) + color = data[ index ]; + gray = ( ( color >> 16 ) & 0xff ) + ( ( color >> 8 ) & 0xff ) + ( color & 0xff ); + gray /= 3; + gray = 0xff - ( ( 0xff - gray ) >> 1 ); // Lighten it 50% + grayDiv2 = gray >> 1; + + /// Preserve alpha + color &= 0xff000000; + + /// Now rewrite the components based on the color mask + color |= ( ( gray & compMasks[ colorMask[ 0 ] ][ 0 ] ) | + ( grayDiv2 & compMasks[ colorMask[ 0 ] ][ 1 ] ) ) << 16; + color |= ( ( gray & compMasks[ colorMask[ 1 ] ][ 0 ] ) | + ( grayDiv2 & compMasks[ colorMask[ 1 ] ][ 1 ] ) ) << 8; + color |= ( ( gray & compMasks[ colorMask[ 2 ] ][ 0 ] ) | + ( grayDiv2 & compMasks[ colorMask[ 2 ] ][ 1 ] ) ); + + data[ index ] = color; + } +} + +/////////////////////////////////////////////////////////////////////////////// +//// Scaling ////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// ScaleNicely ////////////////////////////////////////////////////////////// +// Does a nice (smoothed) scaling of a 1-level mipmap onto another 1-level +// mipmap. Works only for 32-bit mipmaps. + +void plMipmap::ScaleNicely( UInt32 *destPtr, UInt16 destWidth, UInt16 destHeight, + UInt16 destStride, plMipmap::ScaleFilter filter ) const +{ + UInt16 destX, destY, srcX, srcY; + Int16 srcStartX, srcEndX, srcStartY, srcEndY; + float srcPosX, srcPosY, destToSrcXScale, destToSrcYScale, filterWidth, filterHeight, weight; + float totalWeight; + hsColorRGBA color, accumColor; + float whyWaits[ 16 ], whyWait, xWeights[ 16 ]; + UInt32 *srcPtr; + + + // Init + destToSrcXScale = (float)fWidth / (float)destWidth; + destToSrcYScale = (float)fHeight / (float)destHeight; + + // Filter size is the radius of the area (or rather, half the box size) around the source position + // that we sample from. We calculate it so that a 1:1 scale would result in a filter size of 1 (thus + // making a box filter at 1:1 result in a straight copy of the original) + filterWidth = 1.f * destToSrcXScale; + filterHeight = 1.f * destToSrcYScale; + + // If we are upsampling, we still want a filter at least a pixel half-width/height, which will just do + // a bilerp up. That doesn't make this function correctly resample, or excuse the incredibly complicated + // code to do something incredibly simple, but at least it doesn't fail so obviously. + if( filterWidth < 1.f ) + filterWidth = 1.f; + if( filterHeight < 1.f ) + filterHeight = 1.f; + + // Process + for( destY = 0; destY < destHeight; destY++ ) + { + // Calculate the span across this row + srcPosY = destY * destToSrcYScale; + + srcStartY = (Int16)( srcPosY - filterHeight ); + if( srcStartY < 0 ) + srcStartY = 0; + + srcEndY = (Int16)( srcPosY + filterHeight ); + if( srcEndY >= fHeight ) + srcEndY = (Int16)(fHeight - 1); + + // Precalc the y weights + for( srcY = srcStartY; srcY <= srcEndY && ( srcY - srcStartY ) < 16; srcY++ ) + whyWaits[ srcY - srcStartY ] = 1.f - ( fabs( (float)srcY - srcPosY ) / filterHeight ); + + for( destX = 0; destX < destWidth; destX++ ) + { + // For this pixel in the destination, figure out where in the source image we virtually are + srcPosX = destX * destToSrcXScale; + + // Range of pixels that the filter covers + srcStartX = (Int16)( srcPosX - filterWidth ); + if( srcStartX < 0 ) + srcStartX = 0; + + srcEndX = (Int16)( srcPosX + filterWidth ); + if( srcEndX >= fWidth ) + srcEndX = (Int16)(fWidth - 1); + + // Precalc the x weights + for( srcX = srcStartX; srcX <= srcEndX && ( srcX - srcStartX ) < 16; srcX++ ) + xWeights[ srcX - srcStartX ] = 1.f - ( fabs( (float)srcX - srcPosX ) / filterWidth ); + + // Sum up all the weighted colors in the filter area + accumColor.Set( 0.f, 0.f, 0.f, 0.f ); + totalWeight = 0.f; + for( srcY = srcStartY; srcY <= srcEndY; srcY++ ) + { + if( srcY - srcStartY < 16 ) + whyWait = whyWaits[ srcY - srcStartY ]; + else + whyWait = 1.f - ( fabs( (float)srcY - srcPosY ) / filterHeight ); + + if( whyWait <= 0.f ) + continue; + + srcPtr = GetAddr32( srcStartX, srcY ); + for( srcX = srcStartX; srcX <= srcEndX; srcX++, srcPtr++ ) + { + // Our weight... + weight = ( srcX - srcStartX < 16 ) ? xWeights[ srcX - srcStartX ] : + ( 1.f - ( fabs( (float)srcX - srcPosX ) / filterWidth ) ); + weight *= whyWait; + + if( weight > 0.f ) + { + // Grab pixel values from us... + color.FromARGB32( *srcPtr ); + color *= weight; + accumColor += color; + totalWeight += weight; + } + } + } + accumColor *= 1.f / totalWeight; + + // Set the final value + *destPtr = accumColor.ToARGB32(); + destPtr++; + } + destPtr += destStride - destWidth; + } +} + +//// ResizeNicely ///////////////////////////////////////////////////////////// +// Resizes us using the ScaleNicely function. Only works for 1-level, 32bpp +// uncompressed mipmaps. + +hsBool plMipmap::ResizeNicely( UInt16 newWidth, UInt16 newHeight, plMipmap::ScaleFilter filter ) +{ + // Make a temp buffer + UInt32 *newData = TRACKED_NEW UInt32[ newWidth * newHeight ]; + if( newData == nil ) + return false; + + // Scale to it + ScaleNicely( newData, newWidth, newHeight, newWidth, filter ); + + // Reset us to that + Reset(); + fWidth = newWidth; + fHeight = newHeight; + fRowBytes = fWidth * fPixelSize >> 3; + fTotalSize = fRowBytes * fWidth; + fNumLevels = 1; + IBuildLevelSizes(); + fImage = newData; + SetCurrLevel( 0 ); + plProfile_NewMem(MemMipmaps, fTotalSize); + +#ifdef MEMORY_LEAK_TRACER + IAddToMemRecord( this, plRecord::kViaResize ); +#endif + + // All done! + return true; +} + +#ifdef MEMORY_LEAK_TRACER +//// Debug Mipmap Memory Leak Tracker ///////////////////////////////////////// + +plMipmap::plRecord *plMipmap::fRecords = nil; +UInt32 plMipmap::fNumMipmaps = 0; + +void plMipmap::IAddToMemRecord( plMipmap *mip, plRecord::Method method ) +{ + plRecord *newRecord = TRACKED_NEW plRecord; + + + newRecord->fCompressionType = mip->fCompressionType; + newRecord->fCreationMethod = method; + newRecord->fHeight = mip->fHeight; + newRecord->fWidth = mip->fWidth; + newRecord->fImage = mip->fImage; + newRecord->fNumLevels = mip->fNumLevels; + newRecord->fRowBytes = mip->fRowBytes; + if( mip->GetKey() ) + strcpy( newRecord->fKeyName, mip->GetKeyName() ); + else + strcpy( newRecord->fKeyName, "" ); + if( mip->fCompressionType != kDirectXCompression ) + newRecord->fUncompressedInfo.fType = mip->fUncompressedInfo.fType; + else + { + newRecord->fDirectXInfo.fBlockSize = mip->fDirectXInfo.fBlockSize; + newRecord->fDirectXInfo.fCompressionType = mip->fDirectXInfo.fCompressionType; + } + + newRecord->Link( &fRecords ); +} + +void plMipmap::IRemoveFromMemRecord( UInt8 *image ) +{ + plRecord *record; + + + for( record = fRecords; record != nil; record = record->fNext ) + { + if( record->fImage == image ) + { + record->Unlink(); + delete record; + return; + } + } +} + +void plMipmap::IReportLeaks() +{ + plRecord *record, *next; + static char msg[ 512 ], m2[ 128 ]; + UInt32 size; + + + hsStatusMessage( "--- plMipmap Leaks ---\n" ); + for( record = fRecords; record != nil; ) + { + size = record->fHeight * record->fRowBytes; + if( size >= 1024 ) + sprintf( msg, "%s, %4.1f kB: \t%dx%d, %d levels, %d bpr", record->fKeyName, size / 1024.f, record->fWidth, record->fHeight, record->fNumLevels, record->fRowBytes ); + else + sprintf( msg, "%s, %u bytes: \t%dx%d, %d levels, %d bpr", record->fKeyName, size, record->fWidth, record->fHeight, record->fNumLevels, record->fRowBytes ); + + if( record->fCompressionType != kDirectXCompression ) + sprintf( m2, " UType: %d", record->fUncompressedInfo.fType ); + else + sprintf( m2, " DXT%d BSz: %d", record->fDirectXInfo.fCompressionType, record->fDirectXInfo.fBlockSize ); + strcat( msg, m2 ); + + switch( record->fCreationMethod ) + { + case plRecord::kViaCreate: strcat( msg, " via Create\n" ); break; + case plRecord::kViaRead: strcat( msg, " via Read\n" ); break; + case plRecord::kViaClipToMaxSize: strcat( msg, " via ClipToMaxSize\n" ); break; + case plRecord::kViaDetailMapConstructor: strcat( msg, " via DetailMapConstructor\n" ); break; + case plRecord::kViaCopyFrom: strcat( msg, " via CopyFrom\n" ); break; + case plRecord::kViaResize: strcat( msg, " via Resize\n" ); break; + } + + hsStatusMessage( msg ); + + next = record->fNext; + record->Unlink(); + delete record; + record = next; + } + hsStatusMessage( "--- End of plMipmap Leaks ---\n" ); +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plMipmap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plMipmap.h new file mode 100644 index 00000000..1cc98869 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plMipmap.h @@ -0,0 +1,370 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plMipmap Class Header // +// Derived bitmap class representing a single mipmap. // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 6.7.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plMipmap_h +#define _plMipmap_h + +#include "plBitmap.h" + +#ifdef HS_DEBUGGING + #define ASSERT_PIXELSIZE(bitmap, pixelsize) hsAssert((bitmap)->fPixelSize == (pixelsize), "pixelSize mismatch") + #define ASSERT_XY(bitmap, x, y) hsAssert(x < (bitmap)->fWidth && y < (bitmap)->fHeight, "bad XY") + #define ASSERT_UNCOMPRESSED() hsAssert(fCompressionType!=kDirectXCompression, "Can't operate on compressed map.") + + // Define the following konstant to enable mipmap leak checking. This is because our normal + // memory manager sucks when trying to track down these problems + #define MEMORY_LEAK_TRACER +#else + #define ASSERT_PIXELSIZE(bitmap, pixelsize) + #define ASSERT_XY(bitmap, x, y) + #define ASSERT_UNCOMPRESSED() +#endif + +//// Class Definition ///////////////////////////////////////////////////////// + +class plBitmapCreator; +class plTextGenerator; + +class plMipmap : public plBitmap +{ + friend class plBitmapCreator; + friend class plTextGenerator; + + public: + //// Public Flags //// + + + //// Public Data ///// + + + //// Public Members //// + + + plMipmap(); + plMipmap( UInt32 width, UInt32 height, unsigned config, UInt8 numLevels = 0, UInt8 compType = kUncompressed, UInt8 format = UncompressedInfo::kRGB8888 ); + plMipmap( plMipmap *bm, hsScalar sig, UInt32 createFlags, + hsScalar detailDropoffStart, hsScalar detailDropoffStop, + hsScalar detailMax, hsScalar detailMin ); + virtual ~plMipmap(); + + CLASSNAME_REGISTER( plMipmap ); + GETINTERFACE_ANY( plMipmap, plBitmap ); + + + void Create( UInt32 width, UInt32 height, unsigned config, UInt8 numLevels, UInt8 compType = kUncompressed, UInt8 format = UncompressedInfo::kRGB8888 ); + + virtual void Reset(); + + // Get the total size in bytes + virtual UInt32 GetTotalSize() const; + + virtual void Read( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Read( s, mgr ); this->Read( s ); } + virtual void Write( hsStream *s, hsResMgr *mgr ) { hsKeyedObject::Write( s, mgr ); this->Write( s ); } + + virtual UInt8 GetNumLevels() const { return fNumLevels; } + virtual UInt32 GetLevelSize( UInt8 level ); // 0 is the largest + + virtual void Colorize(); + virtual plMipmap *Clone() const; + virtual void CopyFrom( const plMipmap *source ); + + inline UInt32 GetWidth() const { return fWidth; } + inline UInt32 GetHeight() const { return fHeight; } + inline UInt32 GetRowBytes() const { return fRowBytes; } + + void *GetImage() const { return fImage; } + void SetImagePtr( void *ptr ) { fImage = ptr; } + UInt8 *GetLevelPtr( UInt8 level, UInt32 *width = nil, UInt32 *height = nil, UInt32 *rowBytes = nil ); + + // Sets the current level pointer for use with GetAddr* + virtual void SetCurrLevel(UInt8 level); + void *GetCurrLevelPtr() const { return fCurrLevelPtr; } + UInt32 GetCurrWidth() const { return fCurrLevelWidth; } + UInt32 GetCurrHeight() const { return fCurrLevelHeight; } + UInt32 GetCurrLevelSize() const { return fLevelSizes[ fCurrLevel ]; } + UInt32 GetCurrLevel() const { return fCurrLevel; } + + // These methods return the address of the pixel specified by x and y + // They are meant to be fast, therefore they are inlined and do not check + // the fPixelSize field at runtime (except when debugging) + + UInt8* GetAddr8(unsigned x, unsigned y) const + { + ASSERT_PIXELSIZE(this, 8); + ASSERT_XY(this, x, y); + ASSERT_UNCOMPRESSED(); + return (UInt8*)((char*)fCurrLevelPtr + y * fCurrLevelRowBytes + x); + } + UInt16* GetAddr16(unsigned x, unsigned y) const + { + ASSERT_PIXELSIZE(this, 16); + ASSERT_XY(this, x, y); + ASSERT_UNCOMPRESSED(); + return (UInt16*)((char*)fCurrLevelPtr + y * fCurrLevelRowBytes + (x << 1)); + } + UInt32* GetAddr32(unsigned x, unsigned y) const + { + ASSERT_PIXELSIZE(this, 32); + ASSERT_XY(this, x, y); + ASSERT_UNCOMPRESSED(); + return (UInt32*)((char*)fCurrLevelPtr + y * fCurrLevelRowBytes + (x << 2)); + } + void* GetAddr64(unsigned x, unsigned y) const + { + ASSERT_PIXELSIZE(this, 64); + ASSERT_XY(this, x, y); + ASSERT_UNCOMPRESSED(); + return (void*)((char*)fCurrLevelPtr + y * fCurrLevelRowBytes + (x << 3)); + } + + // This sets fPixelSize, fSpace, fFlags, for you + // All you need to set is + // fWidth, fHeight, fRowBytes, fImage and fColorTable + enum { + kColor8Config = 0, + kGray44Config = 1, + kGray4Config = 2, + kGray8Config = 8, // So we can use bit depths instead + kRGB16Config = 16, + kRGB32Config = 24, + kARGB32Config = 32, + }; + + void SetConfig( unsigned config ); + + + //// Really complex creation stuff //// + + enum { + kCreateDetailAlpha = 0x1, + kCreateDetailAdd = 0x2, + kCreateDetailMult = 0x4, + kCreateDetailMask = kCreateDetailAlpha | kCreateDetailAdd | kCreateDetailMult, + kCreateCarryAlpha = 0x8, + kCreateCarryWhite = 0x10, + kCreateCarryBlack = 0x20, + kCreateCarryMask = kCreateCarryAlpha | kCreateCarryWhite | kCreateCarryBlack + }; + enum hsGPixelType { + kPixelARGB4444, + kPixelARGB1555, + kPixelAI88, + kPixelI8 + }; + enum hsGCopyOptions { + kCopyLODMask, + }; + + enum { + kColorDataRLE = 0x1, + kAlphaDataRLE = 0x2 + }; + + void SetBitmapAsLevel(UInt8 iDst, plMipmap *bm, hsScalar sig, UInt32 createFlags, + hsScalar detailDropoffStart, hsScalar detailDropoffStop, + hsScalar detailMax, hsScalar detailMin); + void ICreateLevelNoDetail(UInt8 iDst, const plFilterMask& mask); + void IBlendLevelDetailAlpha(UInt8 iDst, const plFilterMask& mask, + hsScalar detailDropoffStart, hsScalar detailDropoffStop, + hsScalar detailMax, hsScalar detailMin); + void IBlendLevelDetailAdd(UInt8 iDst, const plFilterMask& mask, + hsScalar detailDropoffStart, hsScalar detailDropoffStop, + hsScalar detailMax, hsScalar detailMin); + void IBlendLevelDetailMult(UInt8 iDst, const plFilterMask& mask, + hsScalar detailDropoffStart, hsScalar detailDropoffStop, + hsScalar detailMax, hsScalar detailMin); + void Filter(hsScalar sig); + UInt32 CopyOutPixels(UInt32 destXSize, UInt32 destYSize, UInt32 dstFormat, void *destPixels, UInt32 copyOptions); + + void ClipToMaxSize( UInt32 maxDimension ); + void RemoveMipping(); + + void EnsureKonstantBorder( hsBool clampU, hsBool clampV ); + + enum CompositeFlags + { + kForceOpaque = 0x0001, // Copy src pixels raw, force dest alphas to opaque + kCopySrcAlpha = 0x0002, // Copy the src pixels raw, including alphas, overwrite dest + kBlendSrcAlpha = 0x0004, // Blend src pixels onto dest using src alpha, dest alpha = src alpha + kMaskSrcAlpha = 0x0008, // Same as copySrcAlpha, but dest is untouched when src alpha = 0 + kBlendWriteAlpha= 0x0010 // Like default (0), but writes dest alpha values + }; + + class CompositeOptions + { + // Helper class for specifying options to Composite() + public: + UInt16 fFlags; + UInt8 fSrcLevelsToSkip; + UInt8 fOpacity; + hsScalar fRedTint, fGreenTint, fBlueTint; + UInt16 fSrcClipX, fSrcClipY; // Clipping is applied AFTER levelSkip + UInt16 fSrcClipWidth, fSrcClipHeight; // 0 means max width/height + + CompositeOptions() { fFlags = 0; fSrcLevelsToSkip = 0; fRedTint = fGreenTint = fBlueTint = 1.f; + fSrcClipX = fSrcClipY = fSrcClipWidth = fSrcClipHeight = 0; fOpacity = 255;} + + CompositeOptions( UInt16 flags, UInt8 srcLevelsToSkip = 0, hsScalar red = 1.f, hsScalar green = 1.f, + hsScalar blue = 1.f, UInt16 srcClipX = 0, UInt16 srcClipY = 0, + UInt16 srcClipWidth = 0, UInt16 srcClipHeight = 0, UInt8 opacity = 255 ) + { + fFlags = flags; + fSrcLevelsToSkip = srcLevelsToSkip; + fRedTint = red; + fGreenTint = green; + fBlueTint = blue; + fSrcClipX = srcClipX; + fSrcClipY = srcClipY; + fSrcClipWidth = srcClipWidth; + fSrcClipHeight = srcClipHeight; + fOpacity = opacity; + } + }; + + // Compositing function. Take a (smaller) mipmap and composite it onto this one at the given location. Nil options means use default + virtual void Composite( plMipmap *source, UInt16 x, UInt16 y, CompositeOptions *options = nil ); + + // Scaling function + enum ScaleFilter + { + kBoxFilter, + kDefaultFilter = kBoxFilter + }; + + virtual void ScaleNicely( UInt32 *destPtr, UInt16 destWidth, UInt16 destHeight, + UInt16 destStride, plMipmap::ScaleFilter filter ) const; + + virtual hsBool ResizeNicely( UInt16 newWidth, UInt16 newHeight, plMipmap::ScaleFilter filter ); + + protected: + + //// Protected Members //// + + void *fImage; + UInt32 fWidth, fHeight, fRowBytes, fTotalSize; + UInt8 fNumLevels; + UInt32 *fLevelSizes; + + void *fCurrLevelPtr; + UInt8 fCurrLevel; + UInt32 fCurrLevelWidth, fCurrLevelHeight, fCurrLevelRowBytes; + + void IReadRawImage( hsStream *stream ); + void IWriteRawImage( hsStream *stream ); + plMipmap *ISplitAlpha(); + void IRecombineAlpha( plMipmap *alphaChannel ); + plMipmap *IReadRLEImage( hsStream *stream ); + void IWriteRLEImage( hsStream *stream, plMipmap *mipmap ); + void IReadJPEGImage( hsStream *stream ); + void IWriteJPEGImage( hsStream *stream ); + void IBuildLevelSizes(); + + void IColorLevel( UInt8 level, const UInt8 *colorMask ); + + hsScalar IGetDetailLevelAlpha( UInt8 level, hsScalar dropStart, hsScalar dropStop, hsScalar min, hsScalar max ); + + void ICarryZeroAlpha(UInt8 iDst); + void ICarryColor(UInt8 iDst, UInt32 col); + + hsBool IGrabBorderColor( hsBool grabVNotU, UInt32 *color ); + void ISetCurrLevelUBorder( UInt32 color ); + void ISetCurrLevelVBorder( UInt32 color ); + + virtual UInt32 Read( hsStream *s ); + virtual UInt32 Write( hsStream *s ); + + friend class plCubicEnvironmap; + +#ifdef MEMORY_LEAK_TRACER + + protected: + + class plRecord + { + public: + plRecord *fNext; + plRecord **fBackPtr; + + char fKeyName[ 256 ]; + void *fImage; + UInt32 fWidth, fHeight, fRowBytes; + UInt8 fNumLevels; + UInt8 fCompressionType; + union + { + DirectXInfo fDirectXInfo; + UncompressedInfo fUncompressedInfo; + }; + enum Method + { + kViaCreate, + kViaRead, + kViaClipToMaxSize, + kViaDetailMapConstructor, + kViaCopyFrom, + kViaResize + } fCreationMethod; + + void Link( plRecord **backPtr ) + { + fBackPtr = backPtr; + fNext = *backPtr; + if( fNext != nil ) + fNext->fBackPtr = &fNext; + *backPtr = this; + } + + void Unlink() + { + *fBackPtr = fNext; + if( fNext != nil ) + fNext->fBackPtr = fBackPtr; + } + }; + + static plRecord *fRecords; + static UInt32 fNumMipmaps; + + static void IAddToMemRecord( plMipmap *mip, plRecord::Method method ); + static void IRemoveFromMemRecord( UInt8 *image ); + static void IReportLeaks(); + +#endif +}; + + +#endif // _plMipmap_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plTGAWriter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plTGAWriter.cpp new file mode 100644 index 00000000..a03a3bd4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plTGAWriter.cpp @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plTGAWriter Class Functions // +// Simple utility class for writing a plMipmap out as a TGA file // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.15.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plTGAWriter.h" +#include "plMipmap.h" +#include "hsStream.h" + + +//// Class Statics //////////////////////////////////////////////////////////// + +plTGAWriter plTGAWriter::fInstance; + + +//// WriteMipmap ////////////////////////////////////////////////////////////// + +void plTGAWriter::WriteMipmap( const char *fileName, plMipmap *mipmap ) +{ + hsUNIXStream stream; + int x, y; + hsRGBAColor32 pixel; + + + stream.Open( fileName, "wb" ); + + /// Write header + stream.WriteByte( 0 ); // Size of ID field + stream.WriteByte( 0 ); // Map type + stream.WriteByte( 2 ); // Type 2 image - Unmapped RGB + + stream.WriteByte( 0 ); // Color map spec + stream.WriteByte( 0 ); // Color map spec + stream.WriteByte( 0 ); // Color map spec + stream.WriteByte( 0 ); // Color map spec + stream.WriteByte( 0 ); // Color map spec + + stream.WriteSwap16( 0 ); // xOrigin + stream.WriteSwap16( 0 ); // yOrigin + + stream.WriteSwap16( (UInt16)mipmap->GetWidth() ); + stream.WriteSwap16( (UInt16)mipmap->GetHeight() ); + + stream.WriteByte( 24 ); + stream.WriteByte( 0 ); + + /// Write image data (gotta do inversed, stupid TGAs...) + for( y = mipmap->GetHeight() - 1; y >= 0; y-- ) + { + for( x = 0; x < mipmap->GetWidth(); x++ ) + { + pixel = *( (hsRGBAColor32 *)mipmap->GetAddr32( x, y ) ); + stream.WriteByte( pixel.b ); + stream.WriteByte( pixel.g ); + stream.WriteByte( pixel.r ); + } + } + + /// All done! + stream.Close(); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plTGAWriter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plTGAWriter.h new file mode 100644 index 00000000..43304e0c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plTGAWriter.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plTGAWriter Class Header // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.15.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plTGAWriter_h +#define _plTGAWriter_h + +#include "hsTypes.h" + +class plMipmap; + + +//// Class Definition ///////////////////////////////////////////////////////// + +class plTGAWriter +{ + private: + + static plTGAWriter fInstance; + + plTGAWriter() {} + + public: + + static plTGAWriter &Instance( void ) { return fInstance; } + + void WriteMipmap( const char *fileName, plMipmap *mipmap ); + +}; + +#endif // _plTGAWriter_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plWinFontCache.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plWinFontCache.cpp new file mode 100644 index 00000000..d51a30d7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plWinFontCache.cpp @@ -0,0 +1,283 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plWinFontCache Class Header // +// I've stopped keeping track, there are far too many reasons already to // +// hate Microsoft. Anyway, this class keeps track of various Win32 fonts we // +// allocate because Win98/ME seems to have problems re-allocating the exact // +// same freaking goddamn font over and over again. I mean, you'd think // +// there'd be a rule somewhere about deterministic behavior when calling // +// the exact same function with the exact same parameters over and over... // +// Oh, wait... // +// // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 11.25.2002 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsWindows.h" +#include "plWinFontCache.h" + +#include "../plStatusLog/plStatusLog.h" +#include "../plFile/hsFiles.h" +#include "../plGImage/plDynSurfaceWriter.h" + +#if HS_BUILD_FOR_WIN32 + +#include + + +char *plWinFontCache::kCustFontExtension = ".prf"; + + +plWinFontCache::plWinFontCache() +{ + fInShutdown = false; + fCustFontDir = nil; +} + +plWinFontCache::~plWinFontCache() +{ + fInShutdown = true; + Clear(); + delete [] fCustFontDir; +} + +plWinFontCache &plWinFontCache::GetInstance( void ) +{ + static plWinFontCache cache; + return cache; +} + +HFONT plWinFontCache::IFindFont( const char *face, int height, int weight, hsBool italic, UInt32 quality ) +{ + int i; + + + for( i = 0; i < fFontCache.GetCount(); i++ ) + { + // Do other tests first, since they're cheaper + if( fFontCache[ i ].fHeight == height && + fFontCache[ i ].fWeight == weight && + fFontCache[ i ].fItalic == italic && + fFontCache[ i ].fQuality == quality ) + { + if( strcmp( fFontCache[ i ].fFace, face ) == 0 ) + return fFontCache[ i ].fFont; + } + } + + return nil; +} + +HFONT plWinFontCache::IMakeFont( const char *face, int height, int weight, hsBool italic, UInt32 quality ) +{ + plFontRecord myRec; + int i; + + + // Find a cached name for us + for( i = 0; i < fFontNameCache.GetCount(); i++ ) + { + if( strcmp( face, fFontNameCache[ i ] ) == 0 ) + break; + } + + if( i == fFontNameCache.GetCount() ) + fFontNameCache.Append( hsStrcpy( face ) ); + + myRec.fFace = fFontNameCache[ i ]; + myRec.fHeight = height; + myRec.fWeight = weight; + myRec.fItalic = italic; + myRec.fQuality = quality; + + myRec.fFont = CreateFont( height, 0, 0, 0, weight, italic ? TRUE : FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, VARIABLE_PITCH, face ); + + if( myRec.fFont != nil ) + { +//#ifdef HS_DEBUGGING +#if 1 + LOGFONT fontInfo; + + if( GetObject( myRec.fFont, sizeof( fontInfo ), &fontInfo ) ) + { + const char *err = nil; + + if( fontInfo.lfQuality != quality ) + err = "Quality of created font does not match"; + if( fontInfo.lfHeight != height ) + err = "Height of created font does not match"; + if( fontInfo.lfWeight != weight ) + err = "Weight of created font does not match"; + if( fontInfo.lfItalic != italic ) + err = "Italic-ness of created font does not match"; + if( stricmp( fontInfo.lfFaceName, face ) != 0 ) + err = "Face of created font does not match"; + + if( err != nil ) + { + static bool triedClearing = false; + + if( fontInfo.lfQuality != quality ) + { + plStatusLog::AddLineS( "pipeline.log", "ERROR: CreateFont() failed to return proper font (%s). Using what was given...", err ); + } + else + { + plStatusLog::AddLineS( "pipeline.log", "ERROR: CreateFont() failed to return proper font (%s). %s", err, triedClearing ? "" : "Clearing cache and retrying..." ); + if( !triedClearing ) + { + triedClearing = true; + + // Didn't work, so get rid of it + DeleteObject( myRec.fFont ); + + // Clear all fonts and try again + Clear(); + + // Make sure we reload our custom fonts tho + ILoadCustomFonts(); + + // Try again + HFONT font = IMakeFont( face, height, weight, italic, quality ); + + triedClearing = false; + + return font; + } + } + } + } +#endif + + fFontCache.Append( myRec ); + } + else + { + plStatusLog::AddLineS( "pipeline.log", "ERROR: CreateFont() call FAILED (face: %s, size: %d %s %s)", face, -height, weight == FW_BOLD ? "bold" : "", italic ? "italic" : "" ); + } + + return myRec.fFont; +} + +HFONT plWinFontCache::GetMeAFont( const char *face, int height, int weight, hsBool italic, UInt32 quality ) +{ + HFONT font = IFindFont( face, height, weight, italic, quality ); + if( font == nil ) + font = IMakeFont( face, height, weight, italic, quality ); + + return font; +} + +void plWinFontCache::Clear( void ) +{ + int i; + + + if( !fInShutdown ) + plStatusLog::AddLineS( "pipeline.log", "** Clearing Win32 font cache **" ); + + for( i = 0; i < fFontCache.GetCount(); i++ ) + DeleteObject( fFontCache[ i ].fFont ); + fFontCache.Reset(); + + for( i = 0; i < fFontNameCache.GetCount(); i++ ) + delete [] fFontNameCache[ i ]; + fFontNameCache.Reset(); + + for( i = 0; i < fCustFonts.GetCount(); i++ ) + { +#if (_WIN32_WINNT >= 0x0500) + if( plDynSurfaceWriter::CanHandleLotsOfThem() ) + RemoveFontResourceEx( fCustFonts[ i ]->fFilename, FR_PRIVATE, 0 ); + else +#endif + if( RemoveFontResource( fCustFonts[ i ]->fFilename ) == 0 ) + { + int q= 0; + DWORD e = GetLastError(); + } + delete fCustFonts[ i ]; + } + fCustFonts.Reset(); +} + +void plWinFontCache::FreeFont( HFONT font ) +{ + // Currently a no-op, but should do some sort of ref-counting +} + +void plWinFontCache::LoadCustomFonts( const char *dir ) +{ + delete [] fCustFontDir; + fCustFontDir = ( dir != nil ) ? hsStrcpy( dir ) : nil; + + ILoadCustomFonts(); +} + +void plWinFontCache::ILoadCustomFonts( void ) +{ + if( fCustFontDir == nil ) + return; + + // Iterate through all the custom fonts in our dir + hsFolderIterator iter( fCustFontDir ); + char fileName[ kFolderIterator_MaxPath ]; + int numAdded; + + + while( iter.NextFileSuffix( kCustFontExtension ) ) + { + iter.GetPathAndName( fileName ); + + // Note that this call can be translated as "does my OS suck?" +#if (_WIN32_WINNT >= 0x0500) + if( plDynSurfaceWriter::CanHandleLotsOfThem() ) + numAdded = AddFontResourceEx( fileName, FR_PRIVATE, 0 ); + else +#endif + numAdded = AddFontResource( fileName ); + + if( numAdded > 0 ) + { + plStatusLog::AddLineS( "pipeline.log", "WinFontCache: Added custom font %s, %d fonts", fileName, numAdded ); + fCustFonts.Append( TRACKED_NEW plCustFont( fileName ) ); + } + else + { + plStatusLog::AddLineS( "pipeline.log", "WinFontCache: Unable to load custom font %s", fileName ); + } + } +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plWinFontCache.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plWinFontCache.h new file mode 100644 index 00000000..e9c35df8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGImage/plWinFontCache.h @@ -0,0 +1,115 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plWinFontCache Class Header // +// I've stopped keeping track, there are far too many reasons already to // +// hate Microsoft. Anyway, this class keeps track of various Win32 fonts we // +// allocate because Win98/ME seems to have problems re-allocating the exact // +// same freaking goddamn font over and over again. I mean, you'd think // +// there'd be a rule somewhere about deterministic behavior when calling // +// the exact same function with the exact same parameters over and over... // +// Oh, wait... // +// // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 11.25.2002 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plWinFontCache_h +#define _plWinFontCache_h + + +#include "hsColorRGBA.h" +#include "hsWindows.h" // EVIL +#include "hsTemplates.h" + +#if HS_BUILD_FOR_WIN32 + + +//// Class Definition ///////////////////////////////////////////////////////// + +class plWinFontCache +{ + protected: + + class plFontRecord + { + public: + HFONT fFont; + + char *fFace; // Pointer is owned by fFontNameCache + int fHeight; + int fWeight; + hsBool fItalic; + UInt32 fQuality; + }; + + class plCustFont + { + public: + char *fFilename; + + plCustFont( const char *c ) { fFilename = hsStrcpy( c ); } + ~plCustFont() { delete [] fFilename; } + }; + + hsBool fInShutdown; + + hsTArray fFontCache; + hsTArray fFontNameCache; + + char *fCustFontDir; + hsTArray fCustFonts; + + plWinFontCache(); + + HFONT IFindFont( const char *face, int height, int weight, hsBool italic, UInt32 quality ); + HFONT IMakeFont( const char *face, int height, int weight, hsBool italic, UInt32 quality ); + + void ILoadCustomFonts( void ); + + public: + + virtual ~plWinFontCache(); + static plWinFontCache &GetInstance( void ); + + HFONT GetMeAFont( const char *face, int height, int weight, hsBool italic, UInt32 quality ); + void FreeFont( HFONT font ); + void Clear( void ); + + void LoadCustomFonts( const char *dir ); + + // Our custom font extension + static char *kCustFontExtension; +}; + + +#endif // HS_BUILD_FOR_WIN32 +#endif // _plWinFontCache_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plDirectShadowMaster.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plDirectShadowMaster.cpp new file mode 100644 index 00000000..0e3d1261 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plDirectShadowMaster.cpp @@ -0,0 +1,189 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" + +#include "plDirectShadowMaster.h" +#include "plShadowSlave.h" +#include "plPerspDirSlave.h" +#include "plShadowCaster.h" +#include "../plMessage/plShadowCastMsg.h" + +#include "plLightInfo.h" + +#include "hsMatrix44.h" +#include "hsBounds.h" +#include "hsFastMath.h" + +//////////////////////////////////////////////////////////////////////////////////// +// Point first, directional lights follow +//////////////////////////////////////////////////////////////////////////////////// + +plDirectShadowMaster::plDirectShadowMaster() +{ +} + +plDirectShadowMaster::~plDirectShadowMaster() +{ + fIsectPool.SetCount(fIsectPool.GetNumAlloc()); + int i; + for( i = 0; i < fIsectPool.GetCount(); i++ ) + delete fIsectPool[i]; +} + +plShadowSlave* plDirectShadowMaster::INewSlave(const plShadowCaster* caster) +{ + if( caster->GetPerspective() ) + return TRACKED_NEW plPerspDirSlave; + + return TRACKED_NEW plDirectShadowSlave; +} + +plShadowSlave* plDirectShadowMaster::INextSlave(const plShadowCaster* caster) +{ + if( !caster->GetPerspective() ) + return plShadowMaster::INextSlave(caster); + + int iSlave = fPerspSlavePool.GetCount(); + fPerspSlavePool.ExpandAndZero(iSlave+1); + plShadowSlave* slave = fPerspSlavePool[iSlave]; + if( !slave ) + { + fPerspSlavePool[iSlave] = slave = INewSlave(caster); + } + return slave; +} + +plShadowSlave* plDirectShadowMaster::IRecycleSlave(plShadowSlave* slave) +{ + if( fSlavePool.GetCount() && (fSlavePool[fSlavePool.GetCount()-1] == slave) ) + fSlavePool.SetCount(fSlavePool.GetCount()-1); + else + if( fPerspSlavePool.GetCount() && (fPerspSlavePool[fPerspSlavePool.GetCount()-1] == slave) ) + fPerspSlavePool.SetCount(fPerspSlavePool.GetCount()-1); + + return nil; +} + +void plDirectShadowMaster::IBeginRender() +{ + plShadowMaster::IBeginRender(); + + fPerspSlavePool.SetCount(0); + fIsectPool.SetCount(0); +} + +void plDirectShadowMaster::IComputeWorldToLight(const hsBounds3Ext& wBnd, plShadowSlave* slave) const +{ + hsMatrix44 kFlipDir; + kFlipDir.Reset(); + kFlipDir.NotIdentity(); + kFlipDir.fMap[2][2] = -1.f; + + hsMatrix44 worldToLight = kFlipDir * fLightInfo->GetWorldToLight(); + hsMatrix44 lightToWorld = fLightInfo->GetLightToWorld() * kFlipDir; + + hsBounds3Ext bnd = wBnd; + bnd.Transform(&worldToLight); + + hsPoint3 pos = bnd.GetCenter(); + + pos.fZ = bnd.GetMins().fZ; + + hsPoint3 wPos = lightToWorld * pos; + + lightToWorld.NotIdentity(); + lightToWorld.fMap[0][3] = wPos[0]; + lightToWorld.fMap[1][3] = wPos[1]; + lightToWorld.fMap[2][3] = wPos[2]; + + // Need worldToLight and hate doing an inverse. + // worldToLight = pureTrans * pureRot; + // lightToWorld = Inv(pureRot) * Inv(pureTran); + // So Inv(pureTran) = pureRot * Inv(pureRot) * Inv(pureTran) = pureRot * lightToWorld + // Make worldToLight pure rotation inverse of lightToWorld + worldToLight.fMap[0][3] = 0; + worldToLight.fMap[1][3] = 0; + worldToLight.fMap[2][3] = 0; + + hsMatrix44 trans = worldToLight * lightToWorld; + worldToLight.fMap[0][3] = -trans.fMap[0][3]; + worldToLight.fMap[1][3] = -trans.fMap[1][3]; + worldToLight.fMap[2][3] = -trans.fMap[2][3]; + +//#define CHECK_INVERSE +#ifdef CHECK_INVERSE + hsMatrix44 inv; + lightToWorld.GetInverse(&inv); +#endif // CHECK_INVERSE + + slave->fWorldToLight = worldToLight; + slave->fLightToWorld = lightToWorld; +} + +void plDirectShadowMaster::IComputeProjections(plShadowCastMsg* castMsg, plShadowSlave* slave) const +{ + + slave->fView.SetPerspective(false); +} + +void plDirectShadowMaster::IComputeISect(const hsBounds3Ext& casterBnd, plShadowSlave* slave) const +{ + int iIsect = fIsectPool.GetCount(); + fIsectPool.ExpandAndZero(iIsect+1); + if( !fIsectPool[iIsect] ) + { + fIsectPool[iIsect] = TRACKED_NEW plBoundsIsect; + } + plBoundsIsect* isect = fIsectPool[iIsect]; + + const hsBounds3Ext& wBnd = slave->fWorldBounds; + + isect->SetBounds(wBnd); + + slave->fIsect = isect; +} + +void plDirectShadowMaster::IComputeBounds(const hsBounds3Ext& casterBnd, plShadowSlave* slave) const +{ + // Plan here is to look at the bounds in the slave's local space. + // Our slave's bounds will clearly contain the shadow caster's bounds. It will also + // contain the bnd's corners extended out in light space Z. + // They will extend fAttenDist farther than the center pointof the bound. + + hsBounds3Ext bnd = casterBnd; + bnd.Transform(&slave->fWorldToLight); + + hsPoint3 farPt = bnd.GetCenter(); + + farPt.fZ += slave->fAttenDist; + + bnd.Union(&farPt); + + bnd.Transform(&slave->fLightToWorld); + + slave->fWorldBounds = bnd; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plDirectShadowMaster.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plDirectShadowMaster.h new file mode 100644 index 00000000..eb6769d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plDirectShadowMaster.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDirectShdowMaster_inc +#define plDirectShdowMaster_inc + + +#include "plShadowMaster.h" +#include "hsGeometry3.h" + + +class plBoundsIsect; + +class plDirectShadowMaster : public plShadowMaster +{ +protected: + mutable hsTArray fIsectPool; + hsTArray fPerspSlavePool; + + virtual void IComputeWorldToLight(const hsBounds3Ext& bnd, plShadowSlave* slave) const; + virtual void IComputeProjections(plShadowCastMsg* castMsg, plShadowSlave* slave) const; + virtual void IComputeISect(const hsBounds3Ext& bnd, plShadowSlave* slave) const; + virtual void IComputeBounds(const hsBounds3Ext& bnd, plShadowSlave* slave) const; + + virtual plShadowSlave* INewSlave(const plShadowCaster* caster); + virtual plShadowSlave* INextSlave(const plShadowCaster* caster); + virtual plShadowSlave* IRecycleSlave(plShadowSlave* slave); + + virtual void IBeginRender(); + +public: + plDirectShadowMaster(); + virtual ~plDirectShadowMaster(); + + CLASSNAME_REGISTER( plDirectShadowMaster ); + GETINTERFACE_ANY( plDirectShadowMaster, plShadowMaster ); + +}; + +#endif // plDirectShdowMaster_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plGLightCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plGLightCreatable.h new file mode 100644 index 00000000..0a37b58c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plGLightCreatable.h @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plGLightCreatable_inc +#define plGLightCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plLightInfo.h" + +REGISTER_NONCREATABLE( plLightInfo ); +REGISTER_CREATABLE( plDirectionalLightInfo ); +REGISTER_CREATABLE( plLimitedDirLightInfo ); +REGISTER_CREATABLE( plOmniLightInfo ); +REGISTER_CREATABLE( plSpotLightInfo ); + +#include "plLightSpace.h" + +REGISTER_CREATABLE( plLightSpace ); + +#include "plShadowMaster.h" + +REGISTER_NONCREATABLE( plShadowMaster ); + +#include "plPointShadowMaster.h" + +REGISTER_CREATABLE( plPointShadowMaster ); + +#include "plDirectShadowMaster.h" + +REGISTER_CREATABLE( plDirectShadowMaster ); + +#include "plShadowCaster.h" + +REGISTER_CREATABLE( plShadowCaster ); + +#endif // plGLightCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightInfo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightInfo.cpp new file mode 100644 index 00000000..a44a3f1d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightInfo.cpp @@ -0,0 +1,1003 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLightInfo.h" +#include "plLightKonstants.h" +#include "hsBounds.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "plgDispatch.h" +#include "../plIntersect/plVolumeIsect.h" +#include "../plDrawable/plSpaceTree.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plPipeline/hsGDeviceRef.h" +#include "../plPipeline/plRenderTarget.h" +#include "hsFastMath.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../plSurface/plLayerInterface.h" +#include "../plSurface/plLayer.h" +#include "../plSurface/hsGMaterial.h" +#include "../plGImage/plMipmap.h" +#include "../plMessage/plRenderMsg.h" +#include "../plMessage/plRenderRequestMsg.h" +#include "../plScene/plRenderRequest.h" +#include "plPipeline.h" +#include "../plIntersect/plSoftVolume.h" +#include "../plPipeline/plPipeDebugFlags.h" +#include "../pnMessage/plPipeResMakeMsg.h" + +#include "../plScene/plVisRegion.h" +#include "../plScene/plVisMgr.h" + +// heinous +#include "../plNetClient/plNetClientMgr.h" +#include "../pnMessage/plEnableMsg.h" +static hsScalar kMaxYon = 1000.f; +static hsScalar kMinHither = 1.f; + +#include "plLightProxy.h" + +#include "../plDrawable/plDrawableGenerator.h" + +plLightInfo::plLightInfo() +: fSceneNode(nil), + fDeviceRef(nil), + fVolFlags(0), + fProjection(nil), + fSoftVolume(nil) +{ + fLightToWorld.Reset(); + fWorldToLight.Reset(); + + fLocalToWorld.Reset(); + fWorldToLocal.Reset(); + + fLightToLocal.Reset(); + fLocalToLight.Reset(); + + fWorldToProj.Reset(); + + fNextDevPtr = nil; + fPrevDevPtr = nil; + + fProxyGen = TRACKED_NEW plLightProxy; + fProxyGen->Init(this); + + fRegisteredForRenderMsg = false; + + fVisSet.SetBit(plVisMgr::kNormal); +} + +plLightInfo::~plLightInfo() +{ + if( fNextDevPtr != nil || fPrevDevPtr != nil ) + Unlink(); + + hsRefCnt_SafeUnRef( fDeviceRef ); + + if( fRegisteredForRenderMsg ) + { + plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); + plgDispatch::Dispatch()->UnRegisterForExactType(plPipeRTMakeMsg::Index(), GetKey()); + fRegisteredForRenderMsg = false; + } + + delete fProxyGen; +} + +void plLightInfo::SetDeviceRef( hsGDeviceRef *ref ) +{ + hsRefCnt_SafeAssign( fDeviceRef, ref ); +} + +void plLightInfo::IRefresh() +{ + ICheckMaxStrength(); + if( fDeviceRef ) + fDeviceRef->SetDirty( true ); +} + +void plLightInfo::ICheckMaxStrength() +{ + hsScalar r = GetDiffuse().r >= 0 ? GetDiffuse().r : -GetDiffuse().r; + hsScalar g = GetDiffuse().g >= 0 ? GetDiffuse().g : -GetDiffuse().g; + hsScalar b = GetDiffuse().b >= 0 ? GetDiffuse().b : -GetDiffuse().b; + fMaxStrength = + r > g + ? ( + r > b + ? r + : b + ) + : ( + g > b + ? g + : b + ); + + const hsScalar kMinMaxStrength = 1.e-2f; + SetZero(fMaxStrength < kMinMaxStrength); +} + +void plLightInfo::GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const +{ + if( IsIdle() ) + { + strength = scale = 0.f; + return; + } + + strength = fMaxStrength; + + scale = 1.f; + if( fSoftVolume ) + { + scale = fSoftVolume->GetStrength(bnd.GetCenter()); + strength *= scale; + + } + return; +} + +void plLightInfo::GetAffectedForced(const plSpaceTree* space, hsBitVector& list, hsBool charac) +{ + Refresh(); + + if( IGetIsect() ) + { + space->HarvestLeaves(IGetIsect(), list); + } + else + { + list.Set(space->GetNumLeaves()); + } +} + +void plLightInfo::GetAffected(const plSpaceTree* space, hsBitVector& list, hsBool charac) +{ + Refresh(); + + if( IsIdle() ) + return; + + if( !GetProperty(kLPHasIncludes) || (GetProperty(kLPIncludesChars) && charac) ) + { + if( IGetIsect() ) + { + space->HarvestLeaves(IGetIsect(), list); + } + else + { + list.Set(space->GetNumLeaves()); + } + } +} + +const hsTArray& plLightInfo::GetAffected(plSpaceTree* space, const hsTArray& visList, hsTArray& litList, hsBool charac) +{ + Refresh(); + + if( !IsIdle() ) + { + if( !GetProperty(kLPHasIncludes) || (GetProperty(kLPIncludesChars) && charac) ) + { + if( IGetIsect() ) + { + static hsBitVector cache; + cache.Clear(); + space->EnableLeaves(visList, cache); + + space->HarvestEnabledLeaves(IGetIsect(), cache, litList); + + return litList; + } + else + { + return visList; + } + } + } + + litList.SetCount(0); + return litList; +} + +//// Set/GetProperty ///////////////////////////////////////////////////////// +// Sets/gets a property just like the normal Set/GetNativeProperty, but the +// flag taken in is from plDrawInterface, not our props flags. So we have to +// translate... + +void plLightInfo::SetProperty( int prop, hsBool on ) +{ + plObjInterface::SetProperty(prop, on); + if( kDisable == prop ) + fProxyGen->SetDisable(on); +} + +//// SetSpecular ///////////////////////////////////////////////////////////// +// A bit more complicated here--make sure we set/clear the kLPHasSpecular +// flag so we can test more easily for such a condition. + +void plLightInfo::SetSpecular( const hsColorRGBA& c ) +{ + fSpecular = c; + + if( fSpecular.r == 0.f && fSpecular.g == 0.f && fSpecular.b == 0.f ) + SetProperty( kLPHasSpecular, false ); + else + SetProperty( kLPHasSpecular, true ); + + SetDirty(); +} + +void plLightInfo::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + fLocalToWorld = l2w; + fWorldToLocal = w2l; + + fLightToWorld = l2w * fLightToLocal; + fWorldToLight = fLocalToLight * w2l; + + if( IGetIsect() ) + IGetIsect()->SetTransform(fLightToWorld, fWorldToLight); + + if( fDeviceRef != nil ) + fDeviceRef->SetDirty( true ); + + fProxyGen->SetTransform(fLightToWorld, fWorldToLight); + + SetDirty(true); + + if( GetProjection() ) + { + Refresh(); + hsMatrix44 w2proj = IGetWorldToProj(); + plLayer* lay = plLayer::ConvertNoRef(GetProjection()->BottomOfStack()); + if( lay ) + { + lay->SetTransform(w2proj); + } + } +} + +void plLightInfo::SetLocalToLight(const hsMatrix44& l2lt, const hsMatrix44& lt2l) +{ + fLocalToLight = l2lt; + fLightToLocal = lt2l; +} + +const hsMatrix44& plLightInfo::GetLocalToWorld() const +{ + return fLocalToWorld; +} + +const hsMatrix44& plLightInfo::GetWorldToLocal() const +{ + return fWorldToLocal; +} + +const hsMatrix44& plLightInfo::GetLightToWorld() const +{ + return fLightToWorld; +} + +const hsMatrix44& plLightInfo::GetWorldToLight() const +{ + return fWorldToLight; +} + +#include "plProfile.h" +plProfile_CreateTimer("Light Info", "RenderSetup", LightInfo); + +void plLightInfo::IAddVisRegion(plVisRegion* reg) +{ + if( reg ) + { + int idx = fVisRegions.Find(reg); + if( fVisRegions.kMissingIndex == idx ) + { + fVisRegions.Append(reg); + if( reg->GetProperty(plVisRegion::kIsNot) ) + fVisNot.SetBit(reg->GetIndex()); + else + { + fVisSet.SetBit(reg->GetIndex()); + if( reg->ReplaceNormal() ) + fVisSet.ClearBit(plVisMgr::kNormal); + } + } + } +} + +void plLightInfo::IRemoveVisRegion(plVisRegion* reg) +{ + if( reg ) + { + int idx = fVisRegions.Find(reg); + if( fVisRegions.kMissingIndex != idx ) + { + fVisRegions.Remove(idx); + if( reg->GetProperty(plVisRegion::kIsNot) ) + fVisNot.ClearBit(reg->GetIndex()); + else + fVisSet.ClearBit(reg->GetIndex()); + } + } +} + + +hsBool plLightInfo::MsgReceive(plMessage* msg) +{ + plRenderMsg* rendMsg = plRenderMsg::ConvertNoRef(msg); + if( rendMsg ) + { + plProfile_BeginLap(LightInfo, this->GetKey()->GetUoid().GetObjectName()); + + if( !fDeviceRef && !GetProperty(kLPShadowOnly) ) + { + rendMsg->Pipeline()->RegisterLight( this ); + } + + ICheckMaxStrength(); + + plProfile_EndLap(LightInfo, this->GetKey()->GetUoid().GetObjectName()); + return true; + } + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + switch( refMsg->fType ) + { + case kProjection: + fProjection = plLayerInterface::ConvertNoRef(refMsg->GetRef()); + { + if( GetKey() && GetKey()->GetName() && !strncmp(GetKey()->GetName(), "RTPatternLight", strlen("RTPatternLight")) ) + SetProperty(kLPForceProj, true); + } + break; + case kSoftVolume: + fSoftVolume = plSoftVolume::ConvertNoRef(refMsg->GetRef()); + break; + case kVisRegion: + IAddVisRegion(plVisRegion::ConvertNoRef(refMsg->GetRef())); + break; + } + } + else if( refMsg->GetContext() & (plRefMsg::kOnRemove | plRefMsg::kOnDestroy) ) + { + switch( refMsg->fType ) + { + case kProjection: + fProjection = nil; + break; + case kSoftVolume: + fSoftVolume = nil; + break; + case kVisRegion: + IRemoveVisRegion(plVisRegion::ConvertNoRef(refMsg->GetRef())); + break; + } + } + return true; + } + plPipeRTMakeMsg* rtMake = plPipeRTMakeMsg::ConvertNoRef(msg); + if( rtMake ) + { + // Make sure we're registered with the pipeline + // If we're only here to cast shadows, just don't tell anyone + // about us. + if( !fDeviceRef && !GetProperty(kLPShadowOnly) ) + { + rtMake->Pipeline()->RegisterLight( this ); + } + return true; + } + plEnableMsg* enaMsg = plEnableMsg::ConvertNoRef(msg); + if( enaMsg ) + { + SetProperty(kDisable, enaMsg->Cmd(plEnableMsg::kDisable)); + return true; + } + + + + return plObjInterface::MsgReceive(msg); +} + + +void plLightInfo::Read(hsStream* s, hsResMgr* mgr) +{ + hsRefCnt_SafeUnRef( fDeviceRef ); + fDeviceRef = nil; + + plObjInterface::Read(s, mgr); + + fAmbient.Read(s); + fDiffuse.Read(s); + fSpecular.Read(s); + + fLightToLocal.Read(s); + fLocalToLight.Read(s); + + fLightToWorld.Read(s); + fWorldToLight.Read(s); + + fLocalToWorld = fLightToWorld * fLocalToLight; + fWorldToLocal = fLightToLocal * fWorldToLight; + + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kProjection), plRefFlags::kActiveRef); + + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kSoftVolume), plRefFlags::kActiveRef); + + // Let our sceneNode know we're here. + plKey nodeKey = mgr->ReadKey(s); + ISetSceneNode(nodeKey); + + int n = s->ReadSwap32(); + fVisRegions.SetCountAndZero(n); + int i; + for( i = 0; i < n; i++ ) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kVisRegion), plRefFlags::kActiveRef); + + SetDirty(true); +} + +void plLightInfo::Write(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Write(s, mgr); + + fAmbient.Write(s); + fDiffuse.Write(s); + fSpecular.Write(s); + + fLightToLocal.Write(s); + fLocalToLight.Write(s); + + fLightToWorld.Write(s); + fWorldToLight.Write(s); + + mgr->WriteKey(s, GetProjection()); + + mgr->WriteKey(s, fSoftVolume); + + mgr->WriteKey(s, fSceneNode); + + s->WriteSwap32(fVisRegions.GetCount()); + int i; + for( i = 0; i < fVisRegions.GetCount(); i++ ) + mgr->WriteKey(s, fVisRegions[i]); +} + +// These two should only be called by the SceneObject +void plLightInfo::ISetSceneNode(plKey node) +{ + if( node != fSceneNode ) + { + if( node ) + { + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(node, plRefMsg::kOnCreate, -1, plNodeRefMsg::kLight); + hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kPassiveRef); + } + if( fSceneNode ) + { + fSceneNode->Release(GetKey()); + } + } + fSceneNode = node; + + if( fSceneNode != nil ) + { + if( !fRegisteredForRenderMsg ) + { + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plPipeRTMakeMsg::Index(), GetKey()); + fRegisteredForRenderMsg = true; + } + } + else if( fRegisteredForRenderMsg ) + { + plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); + plgDispatch::Dispatch()->UnRegisterForExactType(plPipeRTMakeMsg::Index(), GetKey()); + fRegisteredForRenderMsg = false; + } +} + +plKey plLightInfo::GetSceneNode() const +{ + return fSceneNode; +} + +//// Link & Unlink /////////////////////////////////////////////////////// + +void plLightInfo::Unlink( void ) +{ + hsAssert( fPrevDevPtr, "Light info not in list" ); + if( fNextDevPtr ) + fNextDevPtr->fPrevDevPtr = fPrevDevPtr; + *fPrevDevPtr = fNextDevPtr; + + fNextDevPtr = nil; + fPrevDevPtr = nil; +} + +void plLightInfo::Link( plLightInfo **back ) +{ + hsAssert( fNextDevPtr == nil && fPrevDevPtr == nil, "Trying to link a lightInfo that's already linked" ); + + fNextDevPtr = *back; + if( *back ) + (*back)->fPrevDevPtr = &fNextDevPtr; + fPrevDevPtr = back; + *back = this; +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +///// Standard light types +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +// Directional + +plDirectionalLightInfo::plDirectionalLightInfo() +{ +} + +plDirectionalLightInfo::~plDirectionalLightInfo() +{ +} + +void plDirectionalLightInfo::GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const +{ + plLightInfo::GetStrengthAndScale(bnd, strength, scale); +} + +void plDirectionalLightInfo::Read(hsStream* s, hsResMgr* mgr) +{ + plLightInfo::Read(s, mgr); +} + +void plDirectionalLightInfo::Write(hsStream* s, hsResMgr* mgr) +{ + plLightInfo::Write(s, mgr); +} + + +hsVector3 plDirectionalLightInfo::GetWorldDirection() const +{ + return -fLightToWorld.GetAxis( hsMatrix44::kUp ); +} + +////////////////////////////////////////////////////////////////////////// +// Limited Directional +plLimitedDirLightInfo::plLimitedDirLightInfo() +: fParPlanes(nil) +{ +} + +plLimitedDirLightInfo::~plLimitedDirLightInfo() +{ + delete fParPlanes; +} + +void plLimitedDirLightInfo::IRefresh() +{ + plLightInfo::IRefresh(); + + if( !IGetIsect() ) + IMakeIsect(); + + if( GetProjection() ) + { + hsMatrix44 l2ndc; + l2ndc.Reset(); + + hsScalar width = fWidth; + hsScalar height = fHeight; + + l2ndc.fMap[0][0] = 1.f / width; + l2ndc.fMap[0][3] = 0.5f; + + l2ndc.fMap[1][1] = -1.f / height; + l2ndc.fMap[1][3] = 0.5f; + + // Map Screen Z to range 0 (at hither) to 1 (at yon) + // No, map z dead on to 1.f + l2ndc.fMap[2][2] = 1.f / fDepth; + l2ndc.fMap[2][3] = 0; + + l2ndc.fMap[3][3] = 1.f; + l2ndc.NotIdentity(); + + fWorldToProj = l2ndc * fWorldToLight; + } +} + +void plLimitedDirLightInfo::GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const +{ + // If we haven't culled the object, return that we're full strength. + plLightInfo::GetStrengthAndScale(bnd, strength, scale); +} + +void plLimitedDirLightInfo::Read(hsStream* s, hsResMgr* mgr) +{ + plDirectionalLightInfo::Read(s, mgr); + + fWidth = s->ReadSwapScalar(); + fHeight = s->ReadSwapScalar(); + fDepth = s->ReadSwapScalar(); +} + +void plLimitedDirLightInfo::Write(hsStream* s, hsResMgr* mgr) +{ + plDirectionalLightInfo::Write(s, mgr); + + s->WriteSwapScalar(fWidth); + s->WriteSwapScalar(fHeight); + s->WriteSwapScalar(fDepth); +} + +void plLimitedDirLightInfo::IMakeIsect() +{ + if( !fParPlanes ) + fParPlanes = TRACKED_NEW plParallelIsect; + + fParPlanes->SetNumPlanes(3); + + hsPoint3 p0, p1; + + hsScalar width = fWidth; + hsScalar height = fHeight; + p0.Set(-width*0.5f, 0, 0); + p1.Set(width*0.5f, 0, 0); + fParPlanes->SetPlane(0, p0, p1); + + p0.Set(0, -height * 0.5f, 0); + p1.Set(0, height * 0.5f, 0); + fParPlanes->SetPlane(1, p0, p1); + + p0.Set(0, 0, 0); + p1.Set(0, 0, -fDepth); + fParPlanes->SetPlane(2, p0, p1); + + fParPlanes->SetTransform(fLightToWorld, fWorldToLight); +} + +//// ICreateProxy ////////////////////////////////////////////////////// +// Creates a new box drawable for showing the light's +// influence. + +plDrawableSpans* plLimitedDirLightInfo::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + hsPoint3 corner; + corner.Set(-fWidth*0.5f, -fHeight*0.5f, -fDepth); + hsVector3 vecs[3]; + vecs[0].Set(fWidth, 0, 0); + vecs[1].Set(0, fHeight, 0); + vecs[2].Set(0, 0, fDepth); + // Generate a rectangular drawable based on a corner and three vectors + plDrawableSpans* draw = plDrawableGenerator::GenerateBoxDrawable( corner, vecs[0], vecs[1], vecs[2], + mat, + fLightToWorld, + true, + nil, + &idx, + addTo ); + + return draw; +} + + + +////////////////////////////////////////////////////////////////////////// +// Omni +plOmniLightInfo::plOmniLightInfo() +: fAttenConst(0), + fAttenLinear(1.f), + fAttenQuadratic(0), + fAttenCutoff(0), + fSphere(nil) +{ +} + +plOmniLightInfo::~plOmniLightInfo() +{ + delete fSphere; +} + +void plOmniLightInfo::IMakeIsect() +{ + fSphere = TRACKED_NEW plSphereIsect; + fSphere->SetTransform(fLightToWorld, fWorldToLight); +} + +void plOmniLightInfo::GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const +{ + plLightInfo::GetStrengthAndScale(bnd, strength, scale); + + // Volume - Want to base this on the closest point on the bounds, instead of just the center. + const hsPoint3& pos = bnd.GetCenter(); + + hsScalar dist = hsVector3(&pos, &GetWorldPosition()).MagnitudeSquared(); + dist = 1.f / hsFastMath::InvSqrtAppr(dist); + if( fAttenQuadratic > 0 ) + { + strength /= (fAttenConst + fAttenLinear * dist + fAttenQuadratic * dist * dist); + } + else if( fAttenLinear > 0 ) + { + strength /= (fAttenConst + fAttenLinear * dist); + } + else if( fAttenConst > 0 ) + { + strength /= fAttenConst; + } + else if( fAttenCutoff > 0 ) + { + if( dist > fAttenCutoff ) + strength = 0; + } +} + +hsScalar plOmniLightInfo::GetRadius() const +{ + hsScalar radius = 0; + if( fAttenQuadratic > 0 ) + { + hsScalar mult = fDiffuse.a >= 0 ? fDiffuse.a : -fDiffuse.a; + hsScalar det = fAttenLinear*fAttenLinear - 4.f * fAttenQuadratic * fAttenConst * (1.f - mult * plSillyLightKonstants::GetFarPowerKonst()); + if( det > 0 ) + { + det = hsSquareRoot(det); + + radius = -fAttenLinear + det; + radius /= fAttenQuadratic * 2.f; + if( radius < 0 ) + radius = 0; + } + } + else if( fAttenLinear > 0 ) + { + hsScalar mult = fDiffuse.a >= 0 ? fDiffuse.a : -fDiffuse.a; + radius = (mult * plSillyLightKonstants::GetFarPowerKonst() - 1.f ) * fAttenConst / fAttenLinear; + } + else if( fAttenCutoff > 0 ) + { + radius = fAttenCutoff; + } + + return radius; +} + +void plOmniLightInfo::IRefresh() +{ + plLightInfo::IRefresh(); + + if( IsAttenuated() ) + { + if( !fSphere ) + IMakeIsect(); + fSphere->SetRadius(GetRadius()); + } + else + { + delete fSphere; + fSphere = nil; + } +} + +hsVector3 plOmniLightInfo::GetNegativeWorldDirection(const hsPoint3& pos) const +{ + return hsFastMath::NormalizeAppr(hsVector3(&GetWorldPosition(), &pos)); +} + +void plOmniLightInfo::Read(hsStream* s, hsResMgr* mgr) +{ + plLightInfo::Read(s, mgr); + + fAttenConst = s->ReadSwapScalar(); + fAttenLinear = s->ReadSwapScalar(); + fAttenQuadratic = s->ReadSwapScalar(); + fAttenCutoff = s->ReadSwapScalar(); +} + +void plOmniLightInfo::Write(hsStream* s, hsResMgr* mgr) +{ + plLightInfo::Write(s, mgr); + + s->WriteSwapScalar(fAttenConst); + s->WriteSwapScalar(fAttenLinear); + s->WriteSwapScalar(fAttenQuadratic); + s->WriteSwapScalar( fAttenCutoff ); +} + + +//// ICreateProxy ////////////////////////////////////////////////////// +// Creates a new sphere drawable for showing the omnilight's +// sphere (haha) of influence. + +plDrawableSpans* plOmniLightInfo::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + float rad = GetRadius(); + if( rad == 0 ) + rad = 50; + + plDrawableSpans* draw = plDrawableGenerator::GenerateSphericalDrawable(hsPoint3(0,0,0), + rad, + mat, + fLightToWorld, + true, + nil, + &idx, + addTo); + + return draw; +} + + +////////////////////////////////////////////////////////////////////////// +// Spot +plSpotLightInfo::plSpotLightInfo() +: fFalloff(1.f), + fSpotInner(hsScalarPI * 0.125f), + fSpotOuter(hsScalarPI * 0.25f), + fCone(nil) +{ +} + +plSpotLightInfo::~plSpotLightInfo() +{ + delete fCone; +} + +void plSpotLightInfo::GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const +{ + plOmniLightInfo::GetStrengthAndScale(bnd, strength, scale); + + // Volume - Want to base this on the closest point on the bounds, instead of just the center. + const hsPoint3& pos = bnd.GetCenter(); + + hsVector3 del; + del.Set(&pos, &GetWorldPosition()); + hsScalar invDist = del.MagnitudeSquared(); + invDist = hsFastMath::InvSqrtAppr(invDist); + + hsScalar dot = del.InnerProduct(GetWorldDirection()); + dot *= invDist; + + hsScalar cosInner, cosOuter, t; + hsFastMath::SinCosInRangeAppr(fSpotInner, t, cosInner); + hsFastMath::SinCosInRangeAppr(fSpotOuter, t, cosOuter); + if( dot < cosOuter ) + strength = 0; + else if( dot < cosInner ) + strength *= (dot - cosOuter) / (cosInner - cosOuter); +} + +void plSpotLightInfo::IMakeIsect() +{ + fCone = TRACKED_NEW plConeIsect; + fCone->SetTransform(fLightToWorld, fWorldToLight); +} + +void plSpotLightInfo::IRefresh() +{ + plLightInfo::IRefresh(); + + if( !fCone ) + IMakeIsect(); + + hsScalar effFOV = fSpotOuter; + fCone->SetAngle(effFOV); + + + if( IsAttenuated() ) + { + fCone->SetLength(GetRadius()); + fCone->SetTransform(fLightToWorld, fWorldToLight); + } + + if( GetProjection() ) + { + hsScalar yon = GetRadius(); + if( yon < kMinHither ) + yon = kMaxYon; + hsScalar hither = hsMinimum(kMinHither, yon * 0.5f); + + hsScalar sinFOV, cosFOV; + hsFastMath::SinCos(effFOV, sinFOV, cosFOV); + + hsMatrix44 l2ndc; + l2ndc.Reset(); + l2ndc.fMap[0][0] = cosFOV / sinFOV * 0.5f; + l2ndc.fMap[0][2] = -0.5f; + l2ndc.fMap[1][1] = -cosFOV / sinFOV * 0.5f; + l2ndc.fMap[1][2] = -0.5f; + l2ndc.fMap[2][2] = -yon / (yon - hither); + l2ndc.fMap[3][3] = 0; + l2ndc.fMap[3][2] = -1.f; + + l2ndc.NotIdentity(); + + fWorldToProj = l2ndc * fWorldToLight; + } +} + +void plSpotLightInfo::Read(hsStream* s, hsResMgr* mgr) +{ + plOmniLightInfo::Read(s, mgr); + + fFalloff = s->ReadSwapScalar(); + fSpotInner = s->ReadSwapScalar(); + fSpotOuter = s->ReadSwapScalar(); +} + +void plSpotLightInfo::Write(hsStream* s, hsResMgr* mgr) +{ + plOmniLightInfo::Write(s, mgr); + + s->WriteSwapScalar(fFalloff); + s->WriteSwapScalar(fSpotInner); + s->WriteSwapScalar(fSpotOuter); +} + + +hsVector3 plSpotLightInfo::GetWorldDirection() const +{ + return -fLightToWorld.GetAxis( hsMatrix44::kUp ); +} + +//// ICreateProxy ////////////////////////////////////////////////////// +// Generates a new drawable for showing the spotlight's +// sphere of influence. + +plDrawableSpans* plSpotLightInfo::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + float rad = GetRadius(); + float x, y; + + + if( rad == 0 ) + rad = 80; + + hsFastMath::SinCosAppr( GetSpotOuter(), x, y ); + + plDrawableSpans* draw = plDrawableGenerator::GenerateConicalDrawable(rad * x / y, -rad, + mat, + fLightToWorld, + true, + nil, + &idx, + addTo); + return draw; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightInfo.h new file mode 100644 index 00000000..00b3803b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightInfo.h @@ -0,0 +1,403 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLightInfo_inc +#define plLightInfo_inc + +#include "../pnSceneObject/plObjInterface.h" +#include "hsMatrix44.h" +#include "hsColorRGBA.h" +#include "../plIntersect/plVolumeIsect.h" +#include "hsBitVector.h" + +class hsStream; +class hsResMgr; +class plSpaceTree; + +class hsGDeviceRef; +class hsGMaterial; +class plDrawableSpans; +class plLayerInterface; +class plPipeline; +class plDrawable; +class plSoftVolume; +class hsBounds3Ext; +class plVisRegion; + +class plRenderRequest; +class plRenderTarget; + +class plLightProxy; + +class plLightInfo : public plObjInterface +{ +public: + enum { + kDisable = 0x0, // prop 0 is always disable, declared in plObjInterface + kLPObsolete, + kLPCastShadows, + kLPMovable, + kLPHasIncludes, + kLPIncludesChars, + kLP_OBSOLECTE_0, // OLD + kLPOverAll, + kLPHasSpecular, // This is the same as a non-black specular color, but we use it here to make it faster to check + kLPShadowOnly, + kLPShadowLightGroup, + kLPForceProj, + + kNumProps + }; + enum LIRefType + { + kProjection = 0, + k_OBSOLECTE_REF_0, // OLD + k_OBSOLECTE_REF_1, // OLD + k_OBSOLECTE_REF_2, // OLD + kSoftVolume, + kVisRegion + }; +protected: + // Volatile properties that shouldn't be network propagated. + enum VolatileFlags { + kVolNone = 0x0, + kVolDirty = 0x1, + kVolEmpty = 0x2, + kVolZero = 0x4 + }; + UInt8 fVolFlags; + + hsBitVector fVisSet; + hsBitVector fVisNot; + hsTArray fVisRegions; + + plLightInfo** fPrevDevPtr; + plLightInfo* fNextDevPtr; + hsGDeviceRef* fDeviceRef; + + plLayerInterface* fProjection; + hsMatrix44 fWorldToProj; + + hsColorRGBA fAmbient; + hsColorRGBA fDiffuse; + hsColorRGBA fSpecular; + + hsMatrix44 fLightToLocal; + hsMatrix44 fLocalToLight; + + hsMatrix44 fLocalToWorld; + hsMatrix44 fWorldToLocal; + + hsMatrix44 fLightToWorld; + hsMatrix44 fWorldToLight; + + plKey fSceneNode; + + plLightProxy* fProxyGen; + + plSoftVolume* fSoftVolume; + + hsScalar fMaxStrength; + + hsBool fRegisteredForRenderMsg; + + // Small shadow section + hsBitVector fSlaveBits; + + virtual void IMakeIsect() = 0; + virtual plVolumeIsect* IGetIsect() = 0; + virtual void IRefresh(); + + virtual const hsMatrix44& IGetWorldToProj() const { return fWorldToProj; } + + void IAddVisRegion(plVisRegion* reg); + void IRemoveVisRegion(plVisRegion* reg); + + virtual void ISetSceneNode(plKey node); + + void ICheckMaxStrength(); +public: + plLightInfo(); + virtual ~plLightInfo(); + + CLASSNAME_REGISTER( plLightInfo ); + GETINTERFACE_ANY( plLightInfo, plObjInterface ); + + void SetDeviceRef(hsGDeviceRef* ref); + hsGDeviceRef* GetDeviceRef() const { return fDeviceRef; } + + // Dirty state is local to this machine, so shouldn't be in the network synchronized properties. + hsBool IsDirty() const { return 0 != (fVolFlags & kVolDirty); } + void SetDirty(hsBool on=true) { if(on)fVolFlags |= kVolDirty; else fVolFlags &= ~kVolDirty; } + + hsBool IsEmpty() const { return 0 != (fVolFlags & kVolEmpty); } + void SetEmpty(hsBool on=true) { if(on)fVolFlags |= kVolEmpty; else fVolFlags &= ~kVolEmpty; } + + hsBool IsZero() const { return 0 != (fVolFlags & kVolZero); } + void SetZero(hsBool on) { if(on)fVolFlags |= kVolZero; else fVolFlags &= ~kVolZero; } + + inline hsBool IsIdle() const; + + hsBool OverAll() const { return GetProperty(kLPOverAll); } + + hsBool IsShadowCaster() const { return GetProperty(kLPCastShadows); } + void SetShadowCaster(hsBool on) { SetProperty(kLPCastShadows, on); } + + void Refresh() { if( IsDirty() ) { IRefresh(); SetDirty(false); } } + virtual void GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const; + + hsBool AffectsBound(const hsBounds3Ext& bnd) { return IGetIsect() ? IGetIsect()->Test(bnd) != kVolumeCulled : true; } + void GetAffectedForced(const plSpaceTree* space, hsBitVector& list, hsBool charac); + void GetAffected(const plSpaceTree* space, hsBitVector& list, hsBool charac); + const hsTArray& GetAffected(plSpaceTree* space, const hsTArray& visList, hsTArray& litList, hsBool charac); + hsBool InVisSet(const hsBitVector& visSet) const { return fVisSet.Overlap(visSet); } + hsBool InVisNot(const hsBitVector& visNot) const { return fVisNot.Overlap(visNot); } + + void SetAmbient(const hsColorRGBA& c) { fAmbient = c; SetDirty(); } + void SetDiffuse(const hsColorRGBA& c) { fDiffuse = c; SetDirty(); } + void SetSpecular(const hsColorRGBA& c); + + const hsColorRGBA& GetAmbient() const { return fAmbient; } + const hsColorRGBA& GetDiffuse() const { return fDiffuse; } + const hsColorRGBA& GetSpecular() const { return fSpecular; } + + plLayerInterface* GetProjection() const { return fProjection; } + + virtual void SetProperty(int prop, hsBool on); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + virtual const hsMatrix44& GetLocalToWorld() const; + virtual const hsMatrix44& GetWorldToLocal() const; + virtual const hsMatrix44& GetLightToWorld() const; + virtual const hsMatrix44& GetWorldToLight() const; + + virtual Int32 GetNumProperties() const { return kNumProps; } + + const plSoftVolume* GetSoftVolume() const { return fSoftVolume; } + + virtual hsVector3 GetNegativeWorldDirection(const hsPoint3& pos) const = 0; + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Unlink( void ); + virtual void Link( plLightInfo **back ); + virtual plLightInfo *GetNext( void ) { return fNextDevPtr; } + virtual hsBool IsLinked( void ) { return ( fNextDevPtr != nil || fPrevDevPtr != nil ) ? true : false; } + + // New shadow + void ClearSlaveBits() { fSlaveBits.Clear(); } + void SetSlaveBit(int which) { fSlaveBits.SetBit(which); } + const hsBitVector& GetSlaveBits() const { return fSlaveBits; } + + // These two should only be called internally and on export/convert + virtual plKey GetSceneNode() const; + + // Export only. At runtime, the LocalToLight should be considered const. + void SetLocalToLight(const hsMatrix44& l2lt, const hsMatrix44& lt2l); + + // Visualization + virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) { return addTo; } + +}; + +class plDirectionalLightInfo : public plLightInfo +{ +protected: + + virtual void IMakeIsect() {} + virtual plVolumeIsect* IGetIsect() { return nil; } + +public: + plDirectionalLightInfo(); + virtual ~plDirectionalLightInfo(); + + CLASSNAME_REGISTER( plDirectionalLightInfo ); + GETINTERFACE_ANY( plDirectionalLightInfo, plLightInfo ); + + virtual void GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const; + + hsVector3 GetWorldDirection() const; + virtual hsVector3 GetNegativeWorldDirection(const hsPoint3& pos) const { return -GetWorldDirection(); } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + +}; + +class plLimitedDirLightInfo : public plDirectionalLightInfo +{ +protected: + + hsScalar fWidth; + hsScalar fHeight; + hsScalar fDepth; + + plParallelIsect* fParPlanes; + + virtual void IMakeIsect(); + virtual plVolumeIsect* IGetIsect() { return fParPlanes; } + + virtual void IRefresh(); + +public: + plLimitedDirLightInfo(); + virtual ~plLimitedDirLightInfo(); + + CLASSNAME_REGISTER( plLimitedDirLightInfo ); + GETINTERFACE_ANY( plLimitedDirLightInfo, plDirectionalLightInfo ); + + virtual void GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const; + + hsScalar GetWidth() const { return fWidth; } + hsScalar GetHeight() const { return fHeight; } + hsScalar GetDepth() const { return fDepth; } + + void SetWidth(hsScalar w) { fWidth = w; } + void SetHeight(hsScalar h) { fHeight = h; } + void SetDepth(hsScalar d) { fDepth = d; } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + // Visualization + virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); +}; + +class plOmniLightInfo : public plLightInfo +{ +protected: + // Omni and spot + hsScalar fAttenConst; + hsScalar fAttenLinear; + hsScalar fAttenQuadratic; + hsScalar fAttenCutoff; // Only for attenuate-but-not-really mode, 0 otherwise + + plSphereIsect* fSphere; + + virtual void IMakeIsect(); + virtual plVolumeIsect* IGetIsect() { return fSphere; } + + virtual void IRefresh(); + + +public: + plOmniLightInfo(); + virtual ~plOmniLightInfo(); + + CLASSNAME_REGISTER( plOmniLightInfo ); + GETINTERFACE_ANY( plOmniLightInfo, plLightInfo ); + + virtual void GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const; + + virtual hsVector3 GetNegativeWorldDirection(const hsPoint3& pos) const; + + hsBool IsAttenuated() const { return (fAttenLinear != 0)||(fAttenQuadratic != 0) || ( fAttenCutoff != 0 ); } + hsScalar GetRadius() const; + + hsScalar GetConstantAttenuation() const { return fAttenConst; } + hsScalar GetLinearAttenuation() const { return fAttenLinear; } + hsScalar GetQuadraticAttenuation() const { return fAttenQuadratic; } + hsScalar GetCutoffAttenuation() const { return fAttenCutoff; } + hsPoint3 GetWorldPosition() const { return fLightToWorld.GetTranslate(); } + + void SetConstantAttenuation(hsScalar a) { fAttenConst = a; SetDirty(true); } + void SetLinearAttenuation(hsScalar a) { fAttenLinear = a; SetDirty(true); } + void SetQuadraticAttenuation(hsScalar a) { fAttenQuadratic = a; SetDirty(true); } + void SetCutoffAttenuation( hsScalar a ) { fAttenCutoff = a; SetDirty( true ); } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + // Visualization + virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); + +}; + +class plSpotLightInfo : public plOmniLightInfo +{ +protected: + // Valid only for spot + hsScalar fFalloff; + + // Note - these are half angles, D3D wants whole angles, so fSpotInner*2 etc. + hsScalar fSpotInner; + hsScalar fSpotOuter; + + hsScalar fEffectiveFOV; + + plConeIsect* fCone; + + virtual void IMakeIsect(); + virtual plVolumeIsect* IGetIsect() { return fCone; } + + virtual void IRefresh(); + +public: + plSpotLightInfo(); + virtual ~plSpotLightInfo(); + + CLASSNAME_REGISTER( plSpotLightInfo ); + GETINTERFACE_ANY( plSpotLightInfo, plOmniLightInfo ); + + virtual void GetStrengthAndScale(const hsBounds3Ext& bnd, hsScalar& strength, hsScalar& scale) const; + + hsVector3 GetWorldDirection() const; + virtual hsVector3 GetNegativeWorldDirection(const hsPoint3& pos) const { return -GetWorldDirection(); } + + void SetFalloff(hsScalar f) { fFalloff = f; SetDirty(true); } + void SetSpotInner(hsScalar rads) { fSpotInner = rads; SetDirty(true); } + void SetSpotOuter(hsScalar rads) { fSpotOuter = rads; SetDirty(true); } + + hsScalar GetFalloff() const { return fFalloff; } + hsScalar GetSpotInner() const { return fSpotInner; } + hsScalar GetSpotOuter() const { return fSpotOuter; } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + // Visualization + virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); + +}; + +inline hsBool plLightInfo::IsIdle() const +{ + if( GetProperty(kDisable) ) + return true; + + if( IsZero() ) + return true; + + if( IsEmpty() ) + return true; + + return false; +} + +#endif // plLightInfo_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightKonstants.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightKonstants.h new file mode 100644 index 00000000..e9398a58 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightKonstants.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLightKonstants_inc +#define plLightKonstants_inc + +class plSillyLightKonstants +{ +public: + static const float GetFarPowerKonst( void ) + { + // arbitrary const, make light drop to 1/kFarPower at attenEnd. 15 just looked good to me. mf + // Done as a function so we don't have to worry about a separate .cpp file + return 15.f; + } +}; + +#endif // plLightKonstants_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightProxy.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightProxy.cpp new file mode 100644 index 00000000..eaffef1e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightProxy.cpp @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLightProxy.h" +#include "plLightInfo.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../pnMessage/plProxyDrawMsg.h" + +plLightProxy::plLightProxy() +: plProxyGen(hsColorRGBA().Set(0,0,0,1.f), hsColorRGBA().Set(0.5f,1.0,0.5f,1.f), 0.2f), + fOwner(nil) +{ +} + +plLightProxy::~plLightProxy() +{ +} + +hsBool plLightProxy::Init(plLightInfo* liInfo) +{ + plProxyGen::Init(liInfo); + + fOwner = liInfo; + fProxyMsgType = plProxyDrawMsg::kLight; + + return fOwner != nil; +} + +plKey plLightProxy::IGetNode() const +{ + return fOwner ? fOwner->GetSceneNode() : nil; +} + +plDrawableSpans* plLightProxy::ICreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + if( fOwner ) + { + return fOwner->CreateProxy(mat, idx, addTo); + } + return nil; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightProxy.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightProxy.h new file mode 100644 index 00000000..5c391e17 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightProxy.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLightProxy_inc +#define plLightProxy_inc + +#include "../plDrawable/plProxyGen.h" + +class plLightInfo; + +class plLightProxy : public plProxyGen +{ +protected: + plLightInfo* fOwner; + + + virtual plDrawableSpans* ICreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo=nil); + virtual plKey IGetNode() const; +public: + plLightProxy(); + virtual ~plLightProxy(); + + hsBool Init(plLightInfo* liInfo); +}; + +#endif // plLightProxy_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightSpace.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightSpace.cpp new file mode 100644 index 00000000..b2112a0c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightSpace.cpp @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLightSpace.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "../plMessage/plLightRefMsg.h" +#include "../plMessage/plCollideMsg.h" +#include "plgDispatch.h" + + +hsBool plLightSpace::MsgReceive(plMessage* msg) +{ + plCollideMsg* collMsg = plCollideMsg::ConvertNoRef(msg); + if( collMsg ) + { + // HACK - CollideMsg doesn't have sufficient info yet. Need at least object + // which is entering and leaving, and whether it is entering or leaving. + plKey otherKey = nil; + hsBool enter = true; + UInt8 ctx = enter ? plRefMsg::kOnRequest : plRefMsg::kOnRemove; + plLightRefMsg* liMsg = TRACKED_NEW plLightRefMsg(GetKey(), otherKey, IGetLightInfo(), ctx); + plgDispatch::MsgSend(liMsg); + return true; + } + plLightRefMsg* liMsg = plLightRefMsg::ConvertNoRef(msg); + if( liMsg ) + { + if( liMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fLightInfo = liMsg->GetRef(); + else if( liMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + fLightInfo = nil; + + return true; + } + return false; +} + +void plLightSpace::Read(hsStream* s, hsResMgr* mgr) +{ + plMultiModifier::Read(s, mgr); + + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plLightRefMsg(nil, GetKey(), nil, plRefMsg::kOnCreate), plRefFlags::kPassiveRef); +} + +void plLightSpace::Write(hsStream* s, hsResMgr* mgr) +{ + plMultiModifier::Write(s, mgr); + + mgr->WriteKey(s, fLightInfo); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightSpace.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightSpace.h new file mode 100644 index 00000000..5d9a0289 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plLightSpace.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLightSpace_inc +#define plLightSpace_inc + +class hsStream; +class hsResMgr; + +#include "../pnModifier/plMultiModifier.h" + +class plLightSpace : public plMultiModifier +{ +protected: + + hsKeyedObject* fLightInfo; + + hsKeyedObject* IGetLightInfo() const { return fLightInfo; } +public: + plLightSpace() : fLightInfo(nil) {} + virtual ~plLightSpace() {} + + CLASSNAME_REGISTER( plLightSpace ); + GETINTERFACE_ANY( plLightSpace, plMultiModifier ); + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return false; } + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + +}; + +#endif // plLightSpace_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPerspDirSlave.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPerspDirSlave.cpp new file mode 100644 index 00000000..ce683cfa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPerspDirSlave.cpp @@ -0,0 +1,372 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plPipeline.h" +#include "plTweak.h" +#include "hsFastMath.h" + +#include "plPerspDirSlave.h" + +#include + +void plPerspDirSlave::Init() +{ + plShadowSlave::Init(); + fFlags |= kCastInCameraSpace; +} + +hsPoint3 plPerspDirSlave::IProject(const hsMatrix44& world2NDC, const hsPoint3& pos, hsScalar w) const +{ + hsPoint3 retVal; + retVal.fX = world2NDC.fMap[0][0] * pos.fX + + world2NDC.fMap[0][1] * pos.fY + + world2NDC.fMap[0][2] * pos.fZ + + world2NDC.fMap[0][3] * w; + + retVal.fY = world2NDC.fMap[1][0] * pos.fX + + world2NDC.fMap[1][1] * pos.fY + + world2NDC.fMap[1][2] * pos.fZ + + world2NDC.fMap[1][3] * w; + + retVal.fZ = world2NDC.fMap[2][0] * pos.fX + + world2NDC.fMap[2][1] * pos.fY + + world2NDC.fMap[2][2] * pos.fZ + + world2NDC.fMap[2][3] * w; + + hsScalar invW = 1.f / ( + world2NDC.fMap[3][0] * pos.fX + + world2NDC.fMap[3][1] * pos.fY + + world2NDC.fMap[3][2] * pos.fZ + + world2NDC.fMap[3][3] * w); + + retVal *= invW; + + return retVal; +} + +hsBounds3Ext plPerspDirSlave::IGetPerspCasterBound(const hsMatrix44& world2NDC) const +{ + hsPoint3 corners[8]; + fCasterWorldBounds.GetCorners(corners); + hsPoint3 perspCorners[8]; + + int i; + for( i = 0; i < 8; i++ ) + { + perspCorners[i] = IProject(world2NDC, corners[i]); + + if( perspCorners[i].fX < -1.f ) + perspCorners[i].fX = -1.f; + else if( perspCorners[i].fX > 1.f ) + perspCorners[i].fX = 1.f; + + if( perspCorners[i].fY < -1.f ) + perspCorners[i].fY = -1.f; + else if( perspCorners[i].fY > 1.f ) + perspCorners[i].fY = 1.f; + + if( perspCorners[i].fZ < 0 ) + perspCorners[i].Set(0.f, 0.f, 0.f); + } + hsBounds3Ext bnd; + bnd.MakeEmpty(); + for( i = 0; i < 8; i++ ) + bnd.Union(&perspCorners[i]); + + return bnd; +} + +bool plPerspDirSlave::SetupViewTransform(plPipeline* pipe) +{ + plViewTransform pipeView = pipe->GetViewTransform(); + + plConst(hsScalar) kYon(100.f); + plConst(hsScalar) kHither(30.f); + pipeView.SetHither(kHither); + pipeView.SetYon(kYon); + + hsMatrix44 cam2NDC = pipeView.GetCameraToNDC(); + hsMatrix44 world2NDC = cam2NDC * pipeView.GetWorldToCamera(); + + fLightDir = fLightToWorld.GetAxis(hsMatrix44::kUp); + hsVector3 worldLiDir = fLightDir; + hsPoint3 pWorldLiDir(worldLiDir.fX, worldLiDir.fY, worldLiDir.fZ); + hsPoint3 perspLiPos = IProject(world2NDC, pWorldLiDir, 0); + + hsBool reverseZ = fLightDir.InnerProduct(pipe->GetViewDirWorld()) > 0; + SetFlag(kReverseZ, reverseZ); + SetFlag(kReverseCull, reverseZ); + + hsPoint3 lookAt; + plConst(hsBool) kUsePerspCenter(true); + plConst(hsBool) kUseFrustCenter(true); + if( kUsePerspCenter ) + { + hsPoint3 lookAtCam = pipeView.GetWorldToCamera() * fCasterWorldBounds.GetCenter(); + hsPoint3 lookAtNDC = IProject(pipeView.GetCameraToNDC(), lookAtCam); + lookAt = IProject(world2NDC, fCasterWorldBounds.GetCenter()); + } + else if( kUseFrustCenter ) + { + plConst(hsScalar) kDist(50.f); + hsPoint3 camFrustCenter(0.f, 0.f, kDist); + lookAt = IProject(cam2NDC, camFrustCenter); + } + else + { + hsBounds3Ext ndcBnd(IGetPerspCasterBound(world2NDC)); + lookAt = ndcBnd.GetCenter(); + } + + hsMatrix44 camNDC2Li; + hsMatrix44 li2CamNDC; + IComputeCamNDCToLight(perspLiPos, lookAt, camNDC2Li, li2CamNDC); + + hsScalar minZ, maxZ; + hsScalar cotX, cotY; + + plConst(hsBool) kFixedPersp(true); + if( !kFixedPersp ) + { + hsBounds3Ext bnd(IGetPerspCasterBound(camNDC2Li * world2NDC)); + hsBounds3Ext bnd2(IGetPerspCasterBound(world2NDC)); + bnd2.Transform(&camNDC2Li); + plConst(hsBool) kUseBnd2(false); + if( kUseBnd2 ) + bnd = bnd2; + + minZ = bnd.GetMins().fZ; + maxZ = bnd.GetMaxs().fZ; // THIS IS WRONG + + // EAP + // This is my hack to get the Nexus age working. The real problem + // is probably data-side. I take full responsibility for this + // hack-around breaking the entire system, loosing data, causing + // unauthorized credit card transactions, etc. + if (_isnan(bnd.GetMins().fX) || _isnan(bnd.GetMins().fY)) + return false; + if (_isnan(bnd.GetMaxs().fX) || _isnan(bnd.GetMaxs().fY)) + return false; + + // THIS IS EVEN MORE WRONG + plConst(hsBool) kFakeDepth(false); + if( kFakeDepth ) + { + plConst(hsScalar) kMin(1.f); + plConst(hsScalar) kMax(30.f); + minZ = kMin; + maxZ = kMax; + } + + plConst(hsScalar) kMinMinZ(1.f); + if( minZ < kMinMinZ ) + minZ = kMinMinZ; + + if( -bnd.GetMins().fX > bnd.GetMaxs().fX ) + { + hsAssert(bnd.GetMins().fX < 0, "Empty shadow caster bounds?"); + cotX = -minZ / bnd.GetMins().fX; + } + else + { + hsAssert(bnd.GetMaxs().fX > 0, "Empty shadow caster bounds?"); + cotX = minZ / bnd.GetMaxs().fX; + } + + if( -bnd.GetMins().fY > bnd.GetMaxs().fY ) + { + hsAssert(bnd.GetMins().fY < 0, "Empty shadow caster bounds?"); + cotY = -minZ / bnd.GetMins().fY; + } + else + { + hsAssert(bnd.GetMaxs().fY > 0, "Empty shadow caster bounds?"); + cotY = minZ / bnd.GetMaxs().fY; + } + } + else + { + plConst(hsScalar) kHi(1.f); + hsBounds3Ext bnd; + const hsPoint3 lo(-1.f, -1.f, 0.f); + const hsPoint3 hi(1.f, 1.f, kHi); + bnd.MakeEmpty(); + bnd.Union(&lo); + bnd.Union(&hi); + + bnd.Transform(&camNDC2Li); + + minZ = bnd.GetMins().fZ; + maxZ = bnd.GetMaxs().fZ; // THIS IS WRONG + + // EAP + // This is my hack to get the Nexus age working. The real problem + // is probably data-side. I take full responsibility for this + // hack-around breaking the entire system, loosing data, causing + // unauthorized credit card transactions, etc. + if (_isnan(bnd.GetMins().fX) || _isnan(bnd.GetMins().fY)) + return false; + if (_isnan(bnd.GetMaxs().fX) || _isnan(bnd.GetMaxs().fY)) + return false; + + plConst(hsScalar) kMinMinZ(1.f); + if( minZ < kMinMinZ ) + minZ = kMinMinZ; + + if( -bnd.GetMins().fX > bnd.GetMaxs().fX ) + { + hsAssert(bnd.GetMins().fX < 0, "Empty shadow caster bounds?"); + cotX = -minZ / bnd.GetMins().fX; + } + else + { + hsAssert(bnd.GetMaxs().fX > 0, "Empty shadow caster bounds?"); + cotX = minZ / bnd.GetMaxs().fX; + } + + if( -bnd.GetMins().fY > bnd.GetMaxs().fY ) + { + hsAssert(bnd.GetMins().fY < 0, "Empty shadow caster bounds?"); + cotY = -minZ / bnd.GetMins().fY; + } + else + { + hsAssert(bnd.GetMaxs().fY > 0, "Empty shadow caster bounds?"); + cotY = minZ / bnd.GetMaxs().fY; + } + } + + + hsMatrix44 proj; + proj.Reset(); + proj.NotIdentity(); + + // LightToTexture + + // First the LightToTexture, which uses the above pretty much as is. + // Note the remapping to range [0.5..width-0.5] etc. Also, the perspective + // divide is by the 3rd output (not the fourth), so we make the 3rd + // output be W (instead of Z). + // This also means that our translate goes into [i][2] instead of [i][3]. + proj.fMap[0][0] = cotX * 0.5f; + proj.fMap[0][2] = 0.5f * (1.f + 0.5f/fWidth); + proj.fMap[1][1] = -cotY * 0.5f; + proj.fMap[1][2] = 0.5f * (1.f + 0.5f/fHeight); +#if 1 // This computes correct Z, but we really just want W in 3rd component. HACKFISH + proj.fMap[2][2] = maxZ / (maxZ - minZ); + proj.fMap[2][3] = -minZ * maxZ / (maxZ - minZ); +#elif 1 + proj.fMap[2][2] = 1.f; + proj.fMap[2][3] = 0; +#endif + proj.fMap[3][2] = 1.f; + proj.fMap[3][3] = 0; + +// fWorldToTexture = proj * camNDC2Li * pipeView.GetWorldToNDC(); + fWorldToTexture = proj * camNDC2Li * world2NDC; + fWorldToTexture.fMap[2][0] = fWorldToTexture.fMap[3][0]; + fWorldToTexture.fMap[2][1] = fWorldToTexture.fMap[3][1]; + fWorldToTexture.fMap[2][2] = fWorldToTexture.fMap[3][2]; + fWorldToTexture.fMap[2][3] = fWorldToTexture.fMap[3][3]; + + // Now the LightToNDC. This one's a little trickier, because we want to compensate for + // having brought in the viewport to keep our border constant, so we can clamp the + // projected texture and not have the edges smear off to infinity. + cotX -= cotX / (fWidth * 0.5f); + cotY -= cotY / (fHeight * 0.5f); + + hsScalar tanX = 1.f / cotX; + hsScalar tanY = 1.f / cotY; + fView.SetScreenSize((UInt16)fWidth, (UInt16)fHeight); + fView.SetCameraTransform(pipe->GetViewTransform().GetWorldToCamera(), pipe->GetViewTransform().GetCameraToWorld()); + fView.SetPerspective(true); + fView.SetViewPort(0, 0, (float)fWidth, (float)fHeight, false); + fView.SetView(hsPoint3(-tanX, -tanY, minZ), hsPoint3(tanX, tanY, maxZ)); + + hsMatrix44 cam2Light = camNDC2Li * pipeView.GetCameraToNDC(); + fView.PostMultCameraToNDC(cam2Light); + + fLightPos = fLightToWorld.GetTranslate(); + SetFlag(kPositional, false); + + return true; +} + +static inline hsVector3 CrossProd(const hsVector3& a, const hsPoint3& b) +{ + return hsVector3(a.fY*b.fZ - a.fZ*b.fY, a.fZ*b.fX - a.fX*b.fZ, a.fX*b.fY - a.fY*b.fX); +} + +static inline void InverseOfPureRotTran(const hsMatrix44& src, hsMatrix44& inv) +{ + inv = src; + + // We know this is a pure rotation and translation matrix, so + // we won't have to do a full inverse. Okay kids, don't try this + // at home. + inv.fMap[0][1] = src.fMap[1][0]; + inv.fMap[0][2] = src.fMap[2][0]; + + inv.fMap[1][0] = src.fMap[0][1]; + inv.fMap[1][2] = src.fMap[2][1]; + + inv.fMap[2][0] = src.fMap[0][2]; + inv.fMap[2][1] = src.fMap[1][2]; + + hsPoint3 newTran(-src.fMap[0][3], -src.fMap[1][3], -src.fMap[2][3]); + inv.fMap[0][3] = newTran.InnerProduct((hsVector3*)&inv.fMap[0][0]); + inv.fMap[1][3] = newTran.InnerProduct((hsVector3*)&inv.fMap[1][0]); + inv.fMap[2][3] = newTran.InnerProduct((hsVector3*)&inv.fMap[2][0]); +} + +void plPerspDirSlave::IComputeCamNDCToLight(const hsPoint3& from, const hsPoint3& at, hsMatrix44& camNDC2Li, hsMatrix44& li2CamNDC) +{ + + hsVector3 atToFrom(&from, &at); + hsScalar distSq = atToFrom.MagnitudeSquared(); + atToFrom *= hsFastMath::InvSqrtAppr(distSq); + + const hsScalar kMinMag = 0.5f; + hsVector3 up(0,0,1.f); + if( CrossProd(up, (at - from)).MagnitudeSquared() < kMinMag ) + { + up.Set(0, 1.f, 0); + } + hsMatrix44 w2light; + w2light.MakeCamera(&from, &at, &up); + + hsMatrix44 light2w; + InverseOfPureRotTran(w2light, light2w); + +#ifdef CHECK_INVERSE + hsMatrix44 inv; + w2light.GetInverse(&inv); +#endif // CHECK_INVERSE + + camNDC2Li = w2light; + li2CamNDC = light2w; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPerspDirSlave.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPerspDirSlave.h new file mode 100644 index 00000000..af9dcb4e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPerspDirSlave.h @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPerspDirSlave_inc +#define plPerspDirSlave_inc + +#include "plShadowSlave.h" + +class plPerspDirSlave : public plShadowSlave +{ +protected: + hsBounds3Ext IGetPerspCasterBound(const hsMatrix44& world2NDC) const; + hsPoint3 IProject(const hsMatrix44& world2NDC, const hsPoint3& pos, hsScalar w=1.f) const; + void IComputeCamNDCToLight(const hsPoint3& from, const hsPoint3& at, hsMatrix44& camNDC2Li, hsMatrix44& li2CamNDC); + +public: + + virtual void Init(); + + virtual bool SetupViewTransform(plPipeline* pipe); +}; + + +#endif // plPerspDirSlave_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPointShadowMaster.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPointShadowMaster.cpp new file mode 100644 index 00000000..7dc3565d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPointShadowMaster.cpp @@ -0,0 +1,203 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" + +#include "plPointShadowMaster.h" +#include "plShadowSlave.h" +#include "plShadowCaster.h" +#include "../plMessage/plShadowCastMsg.h" + +#include "plLightInfo.h" + +#include "hsMatrix44.h" +#include "hsBounds.h" +#include "hsFastMath.h" + +static const hsScalar kMinMinZ = 1.f; // totally random arbitrary number (has to be > 0). + +static inline void QuickNorm( hsVector3& a, hsScalar& b ) +{ + hsScalar len = hsFastMath::InvSqrtAppr((a).MagnitudeSquared()); + a *= len; + b *= len; +} + +static inline hsVector3 CrossProd(const hsVector3& a, const hsPoint3& b) +{ + return hsVector3(a.fY*b.fZ - a.fZ*b.fY, a.fZ*b.fX - a.fX*b.fZ, a.fX*b.fY - a.fY*b.fX); +} + +static inline void InverseOfPureRotTran(const hsMatrix44& src, hsMatrix44& inv) +{ + inv = src; + + // We know this is a pure rotation and translation matrix, so + // we won't have to do a full inverse. Okay kids, don't try this + // at home. + inv.fMap[0][1] = src.fMap[1][0]; + inv.fMap[0][2] = src.fMap[2][0]; + + inv.fMap[1][0] = src.fMap[0][1]; + inv.fMap[1][2] = src.fMap[2][1]; + + inv.fMap[2][0] = src.fMap[0][2]; + inv.fMap[2][1] = src.fMap[1][2]; + + hsPoint3 newTran(-src.fMap[0][3], -src.fMap[1][3], -src.fMap[2][3]); + inv.fMap[0][3] = newTran.InnerProduct((hsVector3*)&inv.fMap[0][0]); + inv.fMap[1][3] = newTran.InnerProduct((hsVector3*)&inv.fMap[1][0]); + inv.fMap[2][3] = newTran.InnerProduct((hsVector3*)&inv.fMap[2][0]); +} + +//////////////////////////////////////////////////////////////////////////////////// +// Point first +//////////////////////////////////////////////////////////////////////////////////// +plPointShadowMaster::plPointShadowMaster() +{ + fLastUp.Set(0,0,0); +} + +plPointShadowMaster::~plPointShadowMaster() +{ + fIsectPool.SetCount(fIsectPool.GetNumAlloc()); + int i; + for( i = 0; i < fIsectPool.GetCount(); i++ ) + delete fIsectPool[i]; +} + +plShadowSlave* plPointShadowMaster::INewSlave(const plShadowCaster* caster) +{ + return TRACKED_NEW plPointShadowSlave; +} + +void plPointShadowMaster::IBeginRender() +{ + plShadowMaster::IBeginRender(); + + fIsectPool.SetCount(0); +} + +void plPointShadowMaster::IComputeWorldToLight(const hsBounds3Ext& bnd, plShadowSlave* slave) const +{ + const hsScalar kMinMag = 0.5f; + hsPoint3 from = fLightInfo->GetLightToWorld().GetTranslate(); + hsPoint3 at = bnd.GetCenter(); + + hsVector3 atToFrom(&from, &at); + hsScalar distSq = atToFrom.MagnitudeSquared(); + atToFrom *= hsFastMath::InvSqrtAppr(distSq); + + hsPoint2 depth; + bnd.TestPlane(atToFrom, depth); + + hsScalar fromDepth = atToFrom.InnerProduct(from); + + hsScalar dist = fromDepth - depth.fY; + + static hsScalar kMinDist = 3.f; + if( dist < kMinDist ) + { + atToFrom *= kMinDist - dist; + from += atToFrom; + } + + hsVector3 up(0,0,1.f); + if( CrossProd(up, (at - from)).MagnitudeSquared() < kMinMag ) + { + up.Set(0, 1.f, 0); + } + hsMatrix44 w2light; + w2light.MakeCamera(&from, &at, &up); // mf_flip_up - mf + + hsMatrix44 light2w; + InverseOfPureRotTran(w2light, light2w); + +#ifdef CHECK_INVERSE + hsMatrix44 inv; + w2light.GetInverse(&inv); +#endif // CHECK_INVERSE + + slave->fWorldToLight = w2light; + slave->fLightToWorld = light2w; +} + +void plPointShadowMaster::IComputeProjections(plShadowCastMsg* castMsg, plShadowSlave* slave) const +{ + + slave->fView.SetPerspective(true); + + +} + +void plPointShadowMaster::IComputeISect(const hsBounds3Ext& bnd, plShadowSlave* slave) const +{ + int iIsect = fIsectPool.GetCount(); + fIsectPool.ExpandAndZero(iIsect+1); + if( !fIsectPool[iIsect] ) + { + fIsectPool[iIsect] = TRACKED_NEW plBoundsIsect; + } + plBoundsIsect* isect = fIsectPool[iIsect]; + + const hsBounds3Ext& wBnd = slave->fWorldBounds; + + isect->SetBounds(wBnd); + + slave->fIsect = isect; +} + +void plPointShadowMaster::IComputeBounds(const hsBounds3Ext& wBnd, plShadowSlave* slave) const +{ + // Plan here is to look at the bounds in the slave's local space. + // Our slave's bounds will clearly contain the shadow caster's bounds. It will also + // contain the bnd's corners projected out along the ray from the slave's position + // through each corner. They will extend fAttenDist farther than the center point + // of the bound. + + hsBounds3Ext sBnd = wBnd; + sBnd.Transform(&slave->fWorldToLight); + + hsBounds3Ext bnd = sBnd; + + hsScalar dist = sBnd.GetCenter().fZ; + dist += slave->fAttenDist; + + hsScalar minZ = sBnd.GetMaxs().fZ; + + hsPoint3 p(sBnd.GetMins().fX * dist / minZ, sBnd.GetMins().fY * dist / minZ, dist); + bnd.Union(&p); + + p.Set(sBnd.GetMaxs().fX * dist / minZ, sBnd.GetMaxs().fY * dist / minZ, dist); + bnd.Union(&p); + + bnd.Transform(&slave->fLightToWorld); + + slave->fWorldBounds = bnd; + +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPointShadowMaster.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPointShadowMaster.h new file mode 100644 index 00000000..377f7427 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plPointShadowMaster.h @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPointShadowMaster_inc +#define plPointShadowMaster_inc + +#include "plShadowMaster.h" +#include "hsGeometry3.h" + +class plBoundsIsect; + +class plPointShadowMaster : public plShadowMaster +{ +protected: + mutable hsVector3 fLastUp; + + mutable hsTArray fIsectPool; + + virtual void IComputeWorldToLight(const hsBounds3Ext& bnd, plShadowSlave* slave) const; + virtual void IComputeProjections(plShadowCastMsg* castMsg, plShadowSlave* slave) const; + virtual void IComputeISect(const hsBounds3Ext& bnd, plShadowSlave* slave) const; + virtual void IComputeBounds(const hsBounds3Ext& bnd, plShadowSlave* slave) const; + + virtual plShadowSlave* INewSlave(const plShadowCaster* caster); + + virtual void IBeginRender(); + +public: + plPointShadowMaster(); + virtual ~plPointShadowMaster(); + + CLASSNAME_REGISTER( plPointShadowMaster ); + GETINTERFACE_ANY( plPointShadowMaster, plShadowMaster ); + +}; + +#endif // plPointShadowMaster_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowCaster.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowCaster.cpp new file mode 100644 index 00000000..9015e1c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowCaster.cpp @@ -0,0 +1,208 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plShadowCaster.h" +#include "../plMessage/plShadowCastMsg.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" + +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plSpanTypes.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" + +#include "../plMessage/plRenderMsg.h" + +#include "plgDispatch.h" + +#include "plShadowMaster.h" + +hsBool plShadowCaster::fShadowCastDisabled = false; +hsBool plShadowCaster::fCanShadowCast = true; + +plShadowCaster::plShadowCaster() +: fMaxOpacity(0), + fCastFlags(0), + fAttenScale(1.f), + fBlurScale(0.f), + fBoost(1.f) +{ +} + +plShadowCaster::~plShadowCaster() +{ + Deactivate(); +} + +void plShadowCaster::Read(hsStream* stream, hsResMgr* mgr) +{ + plMultiModifier::Read(stream, mgr); + + fCastFlags = stream->ReadByte(); + + // HACKTESTPERSP +// if( !(fCastFlags & kPerspective) ) +// fCastFlags |= kPerspective; +// else + fCastFlags &= ~kPerspective; + + fBoost = stream->ReadSwapScalar(); + fAttenScale = stream->ReadSwapScalar(); + fBlurScale = stream->ReadSwapScalar(); + + Activate(); +} + +void plShadowCaster::Write(hsStream* stream, hsResMgr* mgr) +{ + plMultiModifier::Write(stream, mgr); + + stream->WriteByte(fCastFlags); + + stream->WriteSwapScalar(fBoost); + stream->WriteSwapScalar(fAttenScale); + stream->WriteSwapScalar(fBlurScale); +} + +void plShadowCaster::Activate() const +{ + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); +} + +void plShadowCaster::Deactivate() const +{ + plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); +} + +void plShadowCaster::ICollectAllSpans() +{ + fSpans.SetCount(0); + int i; + for( i = 0; i < GetNumTargets(); i++ ) + { + plSceneObject* so = GetTarget(i); + // Nil target? Shouldn't happen. + if( so ) + { + const plDrawInterface* di = so->GetDrawInterface(); + // Nil di- either it hasn't loaded yet, or we've been applied to something that isn't visible (oops). + if( di && !di->GetProperty(plDrawInterface::kDisable) ) + { + int j; + for( j = 0; j < di->GetNumDrawables(); j++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(j)); + // Nil dr - it hasn't loaded yet. + if( dr ) + { + plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex(j)); + if( !diIndex.IsMatrixOnly() ) + { + int k; + for( k = 0; k < diIndex.GetCount(); k++ ) + { + const plSpan* span = dr->GetSpan(diIndex[k]); +// if( !(span->fProps & plSpan::kPropNoShadowCast) ) + { + fSpans.Append(DrawSpan().Set(dr, span, diIndex[k])); + } + } + } + } + } + } + } + } +} + +hsBool plShadowCaster::IOnRenderMsg(plRenderMsg* msg) +{ + if( ShadowCastDisabled() ) + return true; + + const UInt8 shadowQuality = UInt8(plShadowMaster::GetGlobalShadowQuality() * 3.9f); + if( !GetKey()->GetUoid().GetLoadMask().MatchesQuality(shadowQuality) ) + return true; + + // Don't really like having to gather these guys up every frame, + // but with the avatar customization, it's all pretty volatile, + // subject to infrequent change, but change without warning. + // The number of actual targets (and hence shadow casting spans) + // for any ShadowCasterModifier should always be on the order of + // 10, so chances are we can get away with this. If not, we can + // figure some way of caching, like a broadcast message warning us + // that an avatar customization event has occurred. + ICollectAllSpans(); + + // Max opacity used to fade out shadows during link + + //find max opacity of all spans + //clear shadowBits of all spans + fMaxOpacity = 0.f; + int i; + for( i = 0; i < fSpans.GetCount(); i++ ) + { + hsGMaterial* mat = fSpans[i].fDraw->GetSubMaterial(fSpans[i].fSpan->fMaterialIdx); + if( mat ) + { + plLayerInterface* baseLay = mat->GetLayer(0); + if( baseLay && (baseLay->GetOpacity() > fMaxOpacity) ) + fMaxOpacity = baseLay->GetOpacity(); + } + + fSpans[i].fSpan->ClearShadowBits(); + } + + + if( fMaxOpacity > 0 ) + { + plShadowCastMsg* cast = TRACKED_NEW plShadowCastMsg(GetKey(), this, msg->Pipeline()); + cast->Send(); + } + + return true; +} + + +#include "plProfile.h" +plProfile_CreateTimer("ShadowCaster", "RenderSetup", ShadowCaster); + +hsBool plShadowCaster::MsgReceive(plMessage* msg) +{ + plRenderMsg* rendMsg = plRenderMsg::ConvertNoRef(msg); + if( rendMsg ) + { + plProfile_BeginLap(ShadowCaster, this->GetKey()->GetUoid().GetObjectName()); + IOnRenderMsg(rendMsg); + plProfile_EndLap(ShadowCaster, this->GetKey()->GetUoid().GetObjectName()); + return true; + } + + return plMultiModifier::MsgReceive(msg); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowCaster.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowCaster.h new file mode 100644 index 00000000..06e73a9c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowCaster.h @@ -0,0 +1,141 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plShadowCaster_inc +#define plShadowCaster_inc + +#include "../pnModifier/plMultiModifier.h" +#include "hsBounds.h" +#include "hsTemplates.h" + +class plDrawableSpans; +class plSpan; +class plMessage; +class hsStream; +class hsResMgr; +class plShadowMaster; +class plRenderMsg; + +class plShadowCaster : public plMultiModifier +{ +public: + enum { + kNone = 0x0, + kSelfShadow = 0x1, + kPerspective = 0x2, + kLimitRes = 0x4 + }; + class DrawSpan + { + public: + DrawSpan& Set(plDrawableSpans* dr, const plSpan* sp, UInt32 idx) { fDraw = (dr); fSpan = (sp); fIndex = (idx); return *this; } + + plDrawableSpans* fDraw; + const plSpan* fSpan; + UInt32 fIndex; + }; +protected: + + // Global state to just turn off the whole gig. Not just + // debugging, we'll probably want a user option for this. + static hsBool fShadowCastDisabled; + static hsBool fCanShadowCast; + + + // Properties really just to be read and written, + // never expected to change. Anything that might be + // triggered should go into plMultiModifier::fProps, + // to be network synced. + UInt8 fCastFlags; + + hsScalar fBoost; + hsScalar fAttenScale; + hsScalar fBlurScale; + + // Casting attributes calculated each frame. + hsScalar fMaxOpacity; + hsTArray fSpans; + + friend plShadowMaster; + + void ICollectAllSpans(); + + hsBool IOnRenderMsg(plRenderMsg* msg); + + friend class plDXPipeline; + static void SetCanShadowCast(hsBool b) { fCanShadowCast = b; } +public: + plShadowCaster(); + virtual ~plShadowCaster(); + + CLASSNAME_REGISTER( plShadowCaster ); + GETINTERFACE_ANY( plShadowCaster, plMultiModifier ); + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return true; } + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + hsScalar MaxOpacity() const { return fMaxOpacity; } + const hsTArray& Spans() const { return fSpans; } + + hsBool GetSelfShadow() const { return 0 != (fCastFlags & kSelfShadow); } + void SetSelfShadow(hsBool on) { if(on) fCastFlags |= kSelfShadow; else fCastFlags &= ~kSelfShadow; } + + hsBool GetPerspective() const { return 0 != (fCastFlags & kPerspective); } + void SetPerspective(hsBool on) { if(on) fCastFlags |= kPerspective; else fCastFlags &= ~kPerspective; } + + hsBool GetLimitRes() const { return 0 != (fCastFlags & kLimitRes); } + void SetLimitRes(hsBool on) { if(on) fCastFlags |= kLimitRes; else fCastFlags &= ~kLimitRes; } + + hsScalar GetAttenScale() const { return fAttenScale; } + void SetAttenScale(hsScalar s) { fAttenScale = s; } + + hsScalar GetBlurScale() const { return fBlurScale; } + void SetBlurScale(hsScalar s) { fBlurScale = s; } + + hsScalar GetBoost() const { return fBoost; } + void SetBoost(hsScalar s) { fBoost = s; } + + // These are usually handled internally, activating on read and deactivating + // on destruct. Made public in case they need to be manually handled, like + // on dynamic construction and use. + void Deactivate() const; + void Activate() const; + + static void DisableShadowCast(hsBool on=true) { fShadowCastDisabled = on; } + static void EnableShadowCast(hsBool on=true) { fShadowCastDisabled = !on; } + static void ToggleShadowCast() { fShadowCastDisabled = !fShadowCastDisabled; } + static hsBool ShadowCastDisabled() { return !CanShadowCast() || fShadowCastDisabled; } + + static hsBool CanShadowCast() { return fCanShadowCast; } +}; + +typedef plShadowCaster::DrawSpan plShadowCastSpan; + +#endif // plShadowCaster_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowMaster.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowMaster.cpp new file mode 100644 index 00000000..c55b3f1a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowMaster.cpp @@ -0,0 +1,1288 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plShadowMaster.h" +#include "plShadowSlave.h" + +#include "plLightInfo.h" +#include "plShadowCaster.h" + +#include "../plIntersect/plVolumeIsect.h" +#include "../plMessage/plShadowCastMsg.h" +#include "../plMessage/plRenderMsg.h" + +#include "../plDrawable/plDrawableSpans.h" + +#include "../plScene/plVisMgr.h" + +#include "hsBounds.h" +#include "plgDispatch.h" +#include "plPipeline.h" +#include "hsFastMath.h" + +#include "plTweak.h" + +UInt32 plShadowMaster::fGlobalMaxSize = 512; +hsScalar plShadowMaster::fGlobalMaxDist = 160.f; // PERSPTEST +// hsScalar plShadowMaster::fGlobalMaxDist = 100000.f; // PERSPTEST +hsScalar plShadowMaster::fGlobalVisParm = 1.f; + +void plShadowMaster::SetGlobalShadowQuality(hsScalar s) +{ + if( s < 0 ) + s = 0; + else if( s > 1.f ) + s = 1.f; + fGlobalVisParm = s; +} + +void plShadowMaster::SetGlobalMaxSize(UInt32 s) +{ + const UInt32 kMaxMaxGlobalSize = 512; + const UInt32 kMinMaxGlobalSize = 32; + + // Make sure it's a power of two. + if( ((s-1) & ~s) != (s-1) ) + { + int i; + for( i = 31; i >= 0; i-- ) + { + if( (1 << i) & s ) + break; + } + s = 1 << i; + } + + if( s > kMaxMaxGlobalSize ) + s = kMaxMaxGlobalSize; + if( s < kMinMaxGlobalSize ) + s = kMinMaxGlobalSize; + + fGlobalMaxSize = s; +} + +plShadowMaster::plShadowMaster() +: fAttenDist(0), + fMaxDist(0), + fMinDist(0), + fMaxSize(256), + fMinSize(256), + fPower(1.f), + fLightInfo(nil) +{ +} + +plShadowMaster::~plShadowMaster() +{ + Deactivate(); + + fSlavePool.SetCount(fSlavePool.GetNumAlloc()); + int i; + for( i = 0; i < fSlavePool.GetCount(); i++ ) + delete fSlavePool[i]; +} + +void plShadowMaster::Read(hsStream* stream, hsResMgr* mgr) +{ + plObjInterface::Read(stream, mgr); + + fAttenDist = stream->ReadSwapScalar(); + + fMaxDist = stream->ReadSwapScalar(); + fMinDist = stream->ReadSwapScalar(); + + fMaxSize = stream->ReadSwap32(); + fMinSize = stream->ReadSwap32(); + + fPower = stream->ReadSwapScalar(); + + Activate(); +} + +void plShadowMaster::Write(hsStream* stream, hsResMgr* mgr) +{ + plObjInterface::Write(stream, mgr); + + stream->WriteSwapScalar(fAttenDist); + + stream->WriteSwapScalar(fMaxDist); + stream->WriteSwapScalar(fMinDist); + + stream->WriteSwap32(fMaxSize); + stream->WriteSwap32(fMinSize); + + stream->WriteSwapScalar(fPower); +} + +void plShadowMaster::Activate() const +{ + plgDispatch::Dispatch()->RegisterForExactType(plShadowCastMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); +} + +void plShadowMaster::Deactivate() const +{ + plgDispatch::Dispatch()->UnRegisterForExactType(plShadowCastMsg::Index(), GetKey()); + plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); +} + +void plShadowMaster::SetMaxDist(hsScalar f) +{ + fMaxDist = f; + fMinDist = f * 0.75f; +} + + +#include "plProfile.h" +plProfile_CreateTimer("ShadowMaster", "RenderSetup", ShadowMaster); +hsBool plShadowMaster::MsgReceive(plMessage* msg) +{ + plRenderMsg* rendMsg = plRenderMsg::ConvertNoRef(msg); + if( rendMsg ) + { + plProfile_BeginLap(ShadowMaster, this->GetKey()->GetUoid().GetObjectName()); + IBeginRender(); + plProfile_EndLap(ShadowMaster, this->GetKey()->GetUoid().GetObjectName()); + return true; + } + + plShadowCastMsg* castMsg = plShadowCastMsg::ConvertNoRef(msg); + if( castMsg ) + { + IOnCastMsg(castMsg); + return true; + } + + return plObjInterface::MsgReceive(msg); +} + +void plShadowMaster::IBeginRender() +{ + fSlavePool.SetCount(0); + if( ISetLightInfo() ) + fLightInfo->ClearSlaveBits(); +} + +hsBool plShadowMaster::IOnCastMsg(plShadowCastMsg* castMsg) +{ +// // HACKTEST +// return false; + + if( !fLightInfo ) + return false; + + if( !fLightInfo->InVisSet(plGlobalVisMgr::Instance()->GetVisSet()) + || fLightInfo->InVisNot(plGlobalVisMgr::Instance()->GetVisNot()) ) + return false; + + const UInt8 shadowQuality = UInt8(plShadowMaster::GetGlobalShadowQuality() * 3.9f); + if( !GetKey()->GetUoid().GetLoadMask().MatchesQuality(shadowQuality) ) + return false; + + plShadowCaster* caster = castMsg->Caster(); + + if( !caster->Spans().GetCount() ) + return false; + + hsBounds3Ext casterBnd; + IComputeCasterBounds(caster, casterBnd); + + hsScalar power = IComputePower(caster, casterBnd); + + static hsScalar kVisShadowPower = 1.e-1f; + static hsScalar kMinShadowPower = 2.e-1f; + static hsScalar kKneeShadowPower = 3.e-1f; + if( power < kMinShadowPower ) + return false; + if( power < kKneeShadowPower ) + { + power -= kMinShadowPower; + power /= kKneeShadowPower - kMinShadowPower; + power *= kKneeShadowPower - kVisShadowPower; + power += kVisShadowPower; + } + + // Create ShadowSlave focused on ShadowCaster + // ShadowSlave extent just enough to cover ShadowCaster (including nearplane) + plShadowSlave* slave = ICreateShadowSlave(castMsg, casterBnd, power); + if( !slave ) + return false; + + // !!!IMPORTANT + // ShadowMaster contains 2 values for yon. + // First value applies to ShadowMaster. Any ShadowCaster beyond this distance + // won't cast a shadow + // Second value applies to ShadowSlaves. This is the distance beyond the ShadowCaster + // (NOT FROM SHADOW SOURCE) over which the shadow attenuates to zero + // The effective yon for the ShadowSlave is ShadowSlaveYon + DistanceToFarthestPointOnShadowCasterBound + // That's the distance used for culling ShadowReceivers + // The ShadowSlaveYon is used directly in the + + slave->fIndex = UInt32(-1); + castMsg->Pipeline()->SubmitShadowSlave(slave); + + if( slave->fIndex == UInt32(-1) ) + { + IRecycleSlave(slave); + return false; + } + + fLightInfo->SetSlaveBit(slave->fIndex); + slave->SetFlag(plShadowSlave::kObeysLightGroups, fLightInfo->GetProperty(plLightInfo::kLPShadowLightGroup) && fLightInfo->GetProperty(plLightInfo::kLPHasIncludes)); + slave->SetFlag(plShadowSlave::kIncludesChars, fLightInfo->GetProperty(plLightInfo::kLPIncludesChars)); + + return true; +} + + +plLightInfo* plShadowMaster::ISetLightInfo() +{ + fLightInfo = nil; + plSceneObject* owner = IGetOwner(); + if( owner ) + { + fLightInfo = plLightInfo::ConvertNoRef(owner->GetGenericInterface(plLightInfo::Index())); + } + return fLightInfo; +} + +void plShadowMaster::IComputeCasterBounds(const plShadowCaster* caster, hsBounds3Ext& casterBnd) +{ + casterBnd.MakeEmpty(); + const hsTArray& castSpans = caster->Spans(); + int i; + for( i = 0; i < castSpans.GetCount(); i++ ) + { + plDrawableSpans* dr = castSpans[i].fDraw; + UInt32 index = castSpans[i].fIndex; + + // Right now, the generic world bounds seems close enough, even when skinned. + // It gets a little off on the lower LODs, but, hey, they're the lower LODs. + // Getting something more precise will probably involve finding a cagey place + // to stash the accurate world bounds, because we only compute them when the + // skinned objects are visible, and we need them here before the object is + // visibility tested. + casterBnd.Union(&dr->GetSpan(index)->fWorldBounds); + } +} + +plShadowSlave* plShadowMaster::INextSlave(const plShadowCaster* caster) +{ + int iSlave = fSlavePool.GetCount(); + fSlavePool.ExpandAndZero(iSlave+1); + plShadowSlave* slave = fSlavePool[iSlave]; + if( !slave ) + { + fSlavePool[iSlave] = slave = INewSlave(caster); + } + return slave; +} + +plShadowSlave* plShadowMaster::ICreateShadowSlave(plShadowCastMsg* castMsg, const hsBounds3Ext& casterBnd, hsScalar power) +{ + const plShadowCaster* caster = castMsg->Caster(); + + plShadowSlave* slave = INextSlave(caster); + + slave->Init(); + //HACKTEST +// slave->SetFlag(plShadowSlave::kReverseCull, true); + + slave->fPower = power; + slave->fCaster = caster; + slave->fCasterWorldBounds = casterBnd; + slave->fAttenDist = fAttenDist * caster->GetAttenScale(); + slave->fBlurScale = caster->GetBlurScale(); + + slave->SetFlag(plShadowSlave::kSelfShadow, GetProperty(kSelfShadow) || caster->GetSelfShadow()); + + // Order of these matters, since values calculated in one are + // used by later functions. Rearrange at your own risk. + IComputeWorldToLight(casterBnd, slave); + + IComputeBounds(casterBnd, slave); + + IComputeWidthAndHeight(castMsg, slave); + + IComputeProjections(castMsg, slave); + + IComputeLUT(castMsg, slave); + + IComputeISect(casterBnd, slave); + + // Make sure we really need this shadow. If we decide we + // don't, the returned slave will be nil; + slave = ILastChanceToBail(castMsg, slave); + + return slave; +} + +plShadowSlave* plShadowMaster::IRecycleSlave(plShadowSlave* slave) +{ + fSlavePool.SetCount(fSlavePool.GetCount()-1); + return nil; +} + +plShadowSlave* plShadowMaster::ILastChanceToBail(plShadowCastMsg* castMsg, plShadowSlave* slave) +{ + const hsBounds3Ext& wBnd = slave->fWorldBounds; + + // If the bounds of the cast shadow aren't visible, forget it. + if( !castMsg->Pipeline()->TestVisibleWorld(wBnd) ) + return IRecycleSlave(slave); + + hsScalar maxDist = fMaxDist > 0 + ? (fGlobalMaxDist > 0 + ? hsMinimum(fMaxDist, fGlobalMaxDist) + : fMaxDist) + : fGlobalMaxDist; + + plConst(hsScalar) kMinFrac(0.6f); + maxDist *= kMinFrac + GetGlobalShadowQuality() * (1.f - kMinFrac); + + // If we haven't got a max distance at which the shadow stays visible + // then we just need to go with it. + if( maxDist <= 0 ) + return slave; + + plConst(hsScalar) kMinFadeFrac(0.90f); + plConst(hsScalar) kMaxFadeFrac(0.75f); + const hsScalar fadeFrac = kMinFadeFrac + GetGlobalShadowQuality() * (kMaxFadeFrac - kMinFadeFrac); + hsScalar minDist = maxDist * fadeFrac; + + // So we want to fade out the shadow as it gets farther away, hopefully + // pitching it in the distance when we couldn't see it anyway. + hsPoint2 depth; + // We've been testing based on the view direction, which shows unfortunate + // camera facing dependency (because it is camera facing dependent) with + // large shadow casters and low shadow quality. Let's try using the vector + // connecting the caster center with the view position. It's just as wrong, + // but at least nothing will change when you swing the camera around. +#if 0 + wBnd.TestPlane(castMsg->Pipeline()->GetViewDirWorld(), depth); + hsScalar eyeDist = castMsg->Pipeline()->GetViewDirWorld().InnerProduct(castMsg->Pipeline()->GetViewPositionWorld()); +#else + hsVector3 dir(&wBnd.GetCenter(), &castMsg->Pipeline()->GetViewPositionWorld()); + hsFastMath::NormalizeAppr(dir); + wBnd.TestPlane(dir, depth); + hsScalar eyeDist = dir.InnerProduct(castMsg->Pipeline()->GetViewPositionWorld()); +#endif + hsScalar dist = depth.fX - eyeDist; + + // If it's not far enough to be fading, just go with it as is. + dist -= minDist; + if( dist < 0 ) + return slave; + + dist /= maxDist - minDist; + dist = 1.f - dist; + + // If it's totally faded out, recycle the slave and return nil; + if( dist <= 0 ) + return IRecycleSlave(slave); + + slave->fPower *= dist; + + return slave; +} + +// compute ShadowSlave power influenced by SoftRegion, current light intensity, and ShadowCaster.fMaxOpacity; +hsScalar plShadowMaster::IComputePower(const plShadowCaster* caster, const hsBounds3Ext& casterBnd) const +{ + hsScalar power = 0; + if( fLightInfo && !fLightInfo->IsIdle() ) + { + power = caster->fMaxOpacity; + hsScalar strength, scale; + fLightInfo->GetStrengthAndScale(casterBnd, strength, scale); + power *= strength; + } + power *= fPower; + power *= caster->GetBoost(); + + return power; +} + +void plShadowMaster::IComputeWidthAndHeight(plShadowCastMsg* castMsg, plShadowSlave* slave) const +{ + slave->fWidth = fMaxSize; + slave->fHeight = fMaxSize; + + if( GetGlobalShadowQuality() <= 0.5f ) + { + slave->fWidth >>= 1; + slave->fHeight >>= 1; + } + if( castMsg->Caster()->GetLimitRes() ) + { + slave->fWidth >>= 1; + slave->fHeight >>= 1; + } + + const hsBounds3Ext& wBnd = slave->fWorldBounds; + + hsPoint2 depth; + wBnd.TestPlane(castMsg->Pipeline()->GetViewDirWorld(), depth); + hsScalar eyeDist = castMsg->Pipeline()->GetViewDirWorld().InnerProduct(castMsg->Pipeline()->GetViewPositionWorld()); + hsScalar dist = depth.fX - eyeDist; + if( dist < 0 ) + dist = 0; + + slave->fPriority = dist; // Might want to boost the local players priority. + + plConst(hsScalar) kShiftDist = 50.f; // PERSPTEST +// plConst(hsScalar) kShiftDist = 5000.f; // PERSPTEST + int iShift = int(dist / kShiftDist); + slave->fWidth >>= iShift; + slave->fHeight >>= iShift; + + if( slave->fWidth > fGlobalMaxSize ) + slave->fWidth = fGlobalMaxSize; + if( slave->fHeight > fGlobalMaxSize ) + slave->fHeight = fGlobalMaxSize; + + const int kMinSize = 32; + if( slave->fWidth < kMinSize ) + slave->fWidth = kMinSize; + if( slave->fHeight < kMinSize ) + slave->fHeight = kMinSize; +} + +void plShadowMaster::IComputeLUT(plShadowCastMsg* castMsg, plShadowSlave* slave) const +{ + // This needs to go from camera space z to lightspace z, and then translate that + // into a lookup on U from our LUT. + // First to get into lightspace, we transform our point by + // worldToLight * cameraToWorld. + // Then we want to map like + // Map 0 => (closest = CasterBnd.Closest), 1 => (CasterBnd.Closest + FalloffDist = farthest) + // which means a matrix looking like: + // 0.0, 0.0, 1/(farthest - closest), -closest / (farthest - closest), + // 0.0, 0.0, 0.0, 0.0, + // 0.0, 0.0, 0.0, 0.0, + // 0.0, 0.0, 0.0, 0.0, + + + hsBounds3Ext bnd = slave->fCasterWorldBounds; + bnd.Transform(&slave->fWorldToLight); + hsScalar farthest = bnd.GetCenter().fZ + slave->fAttenDist; + hsScalar closest = bnd.GetMins().fZ; + + // Shouldn't this always be negated? + static hsMatrix44 lightToLut; // Static ensures initialized to all zeros. + lightToLut.fMap[0][2] = 1/(farthest - closest); + lightToLut.fMap[0][3] = -closest / (farthest - closest); + + // This full matrix multiply is a little overkill. Could simplify it quite a bit... + slave->fRcvLUT = lightToLut * slave->fWorldToLight; + + // For caster, we'll be rendering in light space, so we just need to lut off + // cameraspace z + // Can put bias here if needed (probably) by adding small + // bias to ShadowSlave.LUTXfm.fMap[0][3]. Bias magnitude would probably be at + // least 0.5f/256.f to compensate for quantization. + + plConst(hsScalar) kSelfBias = 2.f / 256.f; + plConst(hsScalar) kOtherBias = -0.5 / 256.f; +#if 0 // MF_NOSELF + lightToLut.fMap[0][3] += slave->HasFlag(plShadowSlave::kSelfShadow) ? kSelfBias : kOtherBias; +#else // MF_NOSELF + lightToLut.fMap[0][3] += kSelfBias; +#endif // MF_NOSELF + + if( slave->CastInCameraSpace() ) + { + slave->fCastLUT = lightToLut * slave->fWorldToLight; + } + else + { + slave->fCastLUT = lightToLut; + } + +#ifdef MF_HACK_SKIPLUT + hsMatrix44 hack; + hack.Reset(); + hack.NotIdentity(); + hack.fMap[0][0] = 0; + hack.fMap[0][3] = 1.f; + slave->fCastLUT = hack; +#endif // MF_HACK_SKIPLUT +} + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// SOURCE CODE ENDS HERE!!!! +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// Some notes from when I was working this out, in case I ever need to figure +// out what I was thinking when I set this up. +#if 0 // Notes + + + +MasterShadow + +On RenderMsg + + Harvest all shadow casters within influence from CullTree + + Assign Shadow (shadow == plLightInfo) to each shadow caster + + Ideally, we want a shadow caster to be a conceptual object + (like one Avatar), rather than individual spans. Shadow Group ID? + + Each shadow renders it's caster into rendertarget, with: + + ClearAlpha to 255 (alphatest will neutralize any texels not written to with shadowcaster) + ClearColor to 0 - then a blur will bleed black in, darkening edges, making for softer effect + around edge of image, which means a softer shadow. + + + Camera Matrix is from=lightPos, at=casterCenter, up=lightUp + + ViewTransform = framed around casterBnd + + color = (camZ - nearPointOfCasterBound) / (lightYon - nearPointOfCasterBnd) + alpha = color + + Add all active shadows to pipeline lights (or just enable them) + + During render, if a shadow is affecting the current object, as a final pass: + + T0 = texture from shadow renders caster (UV = projection of pos by shadow Xform + + T1 = LUT on vtxPos (same LUT as for color and alpha above). + + Color = T1 - T0 + Alpha = T1 - T0 + + Texture blend is Subtract (color and alpha) + + FB AlphaTest = Greater + FB Blend = Mult + + Gives a linear falloff of shadow intensity from nearPointOfCasterBnd to lightYon + + Can be softened (just blur T0) + + + Big problem? On a two TMU system, we're screwed on alpha textures. + + On a 3 (or greater) TMU system, we could: + + // Select first texture + Stage0 + Color/Alpha + Arg1 = T0 + Op = SelectArg1 + + // Subtract first texture from second (T1 - T0) + Stage1 + Color/Alpha + Arg1 = T1 + Arg2 = Current + Op = Subtract + + // Add the complement of the orig texture's alpha to the color, so where the texture + // is transparent, we add 255 and neutralize the shadow, where texture is opaque we + // add 0 and process normally, and stuff in between. + Stage2 + Color + Arg1 = Current + Arg2 = origTex | D3DTA_COMPLEMENT | D3DTA_ALPHAREPLICATE; + Op = Add + + + + Stage0 = T0 + Stage1 = T1 + Stage2 = Original texture + + ColorOp0_1 = Subtract (T0 - T1, inverse of above) + ColorOp1_2 = Select Current + + AlphaOp0_1 = Subtract (T0 - T1, inverse of above) + AlphaOp1_2 = Multiply (OrigTex.a * (T0 - T1)) + + + Okay, time for the bonus round: + + We have 4 cases; + + a) 4 TMU system, base layer opaque + b) 4 TMU system, base layer has alpha (god help us on base layer has add) + c) 2 TMU system, base layer opaque + d) 2 TMU system, base layer has alpha + + If the base layer is opaque, we can do Stage0 and Stage1 as above, whether + we have 2 or 4 TMU's at our disposal + If the base layer has alpha, and we have 4 TMU's, we can do the above + If the base layer has alpha and we have 2 TMU's, we skip it (early out in Apply) + + So, we have the following set up (from above): + + Stage0 = T0 + Stage1 = T1, subtract + [Stage2 = origTex, add] - only if 4 TMU and origTex has alpha + + In any case, we can add one more stage as long as it's just diffuse (we are + out of textures on 2 TMU system). So we use the diffuse to modulate the + effect as follows + + Stage3 + ColorArg1 = Diffuse + ColorArg2 = current | D3DTA_COMPLEMENT + ColorOp = Modulate + + AlphaOp = Disable + + The Diffuse contains the value by which to scale the effect, e.g. by SoftRegion + or artist input. + + Now the alpha coming out is still fine (make sure you set up the alphatest), + but the color the inverse of what we want. That's okay, our framebuffer + blend now becomes + + SrcBlend = ZERO + DstBlend = INVSRCCOLOR + + That means we need to be sure to set the fog color to black + + And that's that. Uh-huh. + + +Shadow Plan 9 + +classes: + +class plShadowCaster : public plMultiModifier +{ +protected: + class plDrawSpan + { + public: + plDrawableSpans* fDraw; + plSpan* fSpan; + UInt32 fIndex; + }; + + hsTArray fSpans; + + hsBounds3Ext fTotalWorldBounds; + hsScalar fMaxOpacity; + + + On RenderMsg + { + // Don't really like having to gather these guys up every frame, + // but with the avatar customization, it's all pretty volatile, + // subject to infrequent change, but change without warning. + // The number of actual targets (and hence shadow casting spans) + // for any ShadowCasterModifier should always be on the order of + // 10, so chances are we can get away with this. If not, we can + // figure some way of caching, like a broadcast message warning us + // that an avatar customization event has occurred. + ICollectSpans(); + + // Max opacity used to fade out shadows during link + + //find max opacity of all spans + //clear shadowBits of all spans + hsScalar fMaxOpacity = 0.f; + int i; + for( i = 0; i < fSpans.GetCount(); i++ ) + { + plLayer* baseLay = fSpans[i].fDraw->GetSubMaterial(fSpans[i].fSpan->fMaterialIdx)->GetLayer(0); + if( baseLay->GetOpacity() > maxOpacity ) + fMaxOpacity = baseLay->GetOpacity(); + + fSpans[i].fSpan->ClearShadowBits(); + } + + + if( fMaxOpacity > 0 ) + { + Broadcast ShadowCastMsg containing + this (ShadowCaster) + } + } + + void ICollectAllSpans() + { + fSpans.SetCount(0); + int i; + for( i = 0; i < GetNumTargets(); i++ ) + { + plSceneObject* so = GetTarget(i); + // Nil target? Shouldn't happen. + if( so ) + { + plDrawInterface* di = so->GetDrawInterface(); + // Nil di- either it hasn't loaded yet, or we've been applied to something that isn't visible (oops). + if( di ) + { + int j; + for( j = 0; j < di->GetNumDrawables(); j++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(j)); + // Nil dr - it hasn't loaded yet. + if( dr ) + { + plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex(j)); + if( !diIndex.IsMatrixOnly() ) + { + int k; + for( k = 0; k < diIndex.GetCount(); k++ ) + { + fSpans.Append(dr, dr->GetSpan(diIndex[k]), diIndex[k]); + } + } + } + } + } + } + } + } +public: + plShadowCaster(); + virtual ~plShadowCaster(); + + CLASSNAME_REGISTER( plShadowCaster ); + GETINTERFACE_ANY( plShadowCaster, plMultiModifier ); + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) {} + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +} + +class plShadowMaster +{ +protected: + hsTArray fSlavePool; + + plVolumeIsect* fIsect; + + hsScalar fAttenDist; + + plSoftVolume* fSoftVolume; + + virtual void IComputeWidthAndHeight(const hsBounds3Ext& bnd, plShadowSlave* slave) const = 0; + virtual void IComputeWorldToLight(const hsBounds3Ext& bnd, plShadowSlave* slave) const = 0; + virtual void IComputeProjections(const hsBounds3Ext& bnd, plShadowSlave* slave) const = 0; + virtual void IComputeLUT(const hsBounds3Ext& bnd, plShadowSlave* slave) const = 0; + virtual void IComputeISect(const hsBounds3Ext& bnd, plShadowSlave* slave) const = 0; + virtual void IComputeBounds(const hsBounds3Ext& bnd, plShadowSlave* slave) const = 0; + + // Override if you want to attenuate (e.g. dist for omni, cone angle for spot). + // But make sure you factor in base class power. + virtual hsScalar IComputePower(const plShadowCaster* caster); + +public: + plVolumeIsect* GetIsect() const { return fIsect; } + + hsBool CanSee(const hsBounds3Ext& bnd) + { + switch( fType ) + { + case kSpot: + return GetIsect().Test(bnd) != kVolumeCulled; + case kDirectional: + return true; + case kLtdDirection: + return GetIsect().Test(bnd) != kVolumeCulled; + case kOmni: + return GetIsect().Test(bnd) != kVolumeCulled; + default: + return false; + } + } + + virtual void CreateShadowSlave(const hsBounds3Ext& bnd, hsScalar power) + { + int iSlave = fSlavePool.GetCount(); + fSlavePool.ExpandAndZero(iSlave+1); + plShadowSlave* slave = fSlavePool[iSlave]; + if( !slave ) + { + fSlavePool[iSlave] = slave = TRACKED_NEW plShadowSlave; + fISectPool[iSlave] = INewISect(); + } + + slave.SetIndex(iSlave); // To be used in not shadowing our own casters + + slave->fPower = power; + + IComputeWidthAndHeight(bnd, slave); + + IComputeWorldToLight(bnd, slave); + + IComputeProjections(bnd, slave); + + IComputeLUT(bnd, slave); + + IComputeISect(bnd, slave, iSlave); + + IComputeBounds(bnd, slave); + } +}; + +// compute ShadowSlave power influenced by SoftRegion and ShadowCaster.fMaxOpacity; +hsScalar plShadowMaster::ComputePower(const plShadowCaster* caster) +{ + hsScalar power = caster->fMaxOpacity; + if( fSoftVolume ) + { + power *= fSoftVolume->GetStrength(caster->fTotalWorldBounds.GetCenter()); + } + return power; +} + +class OmniShadowMaster : public plShadowMaster +{ +protected: + hsTArray fIsectPool; + + virtual void IComputeWidthAndHeight(const hsBounds3Ext& bnd, plShadowSlave* slave) const; + virtual void IComputeWorldToLight(const hsBounds3Ext& bnd, plShadowSlave* slave) const; + virtual void IComputeProjections(const hsBounds3Ext& bnd, plShadowSlave* slave) const; + virtual void IComputeLUT(const hsBounds3Ext& bnd, plShadowSlave* slave) const; + virtual void IComputeISect(const hsBounds3Ext& bnd, plShadowSlave* slave) const; + virtual void IComputeBounds(const hsBounds3Ext& bnd, plShadowSlave* slave) const; +}; + +void plOmniShadowMaster::IComputeWorldToLight(const hsBounds3Ext& bnd, plShadowSlave* slave) const +{ + hsPoint3 from = fPosition; + hsPoint3 at = bnd.GetCenter(); + hsVector3 up = fLastUp; + if( (up % (at - from)).MagnitudeSqaured() < kMinMag ) + { + up.Set(0, 1.f, 0); + if( (up % (at - from)).MagnitudeSqaured() < kMinMag ) + { + up.Set(0, 0, 1.f); + } + fLastUp = up; + } + slave->fWorldToLight.MakeCamera(&from, &at, &up); +} + +void plOmniShadowMaster::IComputeProjections(const hsBounds3Ext& wBnd, plShadowSlave* slave) const +{ + hsBounds3Ext bnd = wBnd; + bnd.Transform(slave->fWorldToLight); + + hsScalar minZ = bnd.GetMins().fZ; + hsScalar maxZ = bnd.GetCenter().fZ + fAttenDist; + + if( minZ < kMinMinZ ) + minZ = kMinMinZ; + + hsScalar cotX, cotY; + if( -bnd.GetMins().fX > bnd.GetMaxs().fX ) + { + hsAssert(bnd.GetMins().fX < 0, "Empty shadow caster bounds?"); + cotX = -minZ / bnd.GetMins().fX; + } + else + { + hsAssert(bnd.GetMaxs().fX > 0, "Empty shadow caster bounds?"); + cotX = minZ / bnd.GetMaxs().fX; + } + + if( -bnd.GetMins().fY > bnd.GetMaxs().fY ) + { + hsAssert(bnd.GetMins().fY < 0, "Empty shadow caster bounds?"); + cotY = -minZ / bnd.GetMins().fY; + } + else + { + hsAssert(bnd.GetMaxs().fY > 0, "Empty shadow caster bounds?"); + cotY = minZ / bnd.GetMaxs().fY; + } + + + hsMatrix44 proj; + proj.Reset(); + proj.NotIdentity(); + + // First the LightToTexture, which uses the above pretty much as is. + // Note the remapping to range [0.5..width-0.5] etc. Also, the perspective + // divide is by the 3rd output (not the fourth), so we make the 3rd + // output be W (instead of Z). + proj.fMap[0][0] = cotX * 0.5f; + proj.fMap[0][3] = 0.5f * (1.f + 1.f/slave->fWidth); + proj.fMap[1][1] = -cotY * 0.5f; + proj.fMap[1][3] = 0.5f * (1.f + 1.f/slave->fHeight); +#if 0 // This computes correct Z, but we really just want W in 3rd component. + proj.fMap[2][2] = maxZ / (maxZ - minZ); + proj.fMap[2][3] = minZ * maxZ / (maxZ - minZ); +#else + proj.fMap[2][2] = 1.f; + proj.fMap[2][3] = 0; +#endif + proj.fMap[3][3] = 0; + proj.fMap[3][2] = 1.f; + + slave->fLightToTexture = proj; + slave->fCameraToTexture = slave->fLightToTexture * slave->fWorldToLight * pipe->GetCameraToWorld(); + + // Now the LightToNDC. This one's a little trickier, because we want to compensate for + // having brought in the viewport to keep our border constant, so we can clamp the + // projected texture and not have the edges smear off to infinity. + cotX -= cotX / (slave->fWidth * 0.5f); + cotY -= cotY / (slave->fHeight * 0.5f); + + proj.fMap[0][0] = cotX; + proj.fMap[0][3] = 0.f; + proj.fMap[1][1] = cotY; + proj.fMap[1][3] = 0.f; + proj.fMap[2][2] = maxZ / (maxZ - minZ); + proj.fMap[2][3] = minZ * maxZ / (maxZ - minZ); + proj.fMap[3][3] = 0; + proj.fMap[3][2] = 1.f; + + slave->fLightToNDC = proj; +} + +class plShadowSlave +{ +public: + + hsMatrix44 fWorldToLight; + hsMatrix44 fLightToNDC; + hsMatrix44 fLightToTexture; + hsMatrix44 fCastLUT; + hsMatrix44 fRcvLUT; + + hsScalar fPower; + + plVolumeIsect* fISect; + + UInt32 fWidth; + UInt32 fHeight; +}; + +BeginScene (on EvalMsg?) +{ + ShadowMasters ClearShadowSlaves(); // fSlavePool.SetCount(0); fISectPool.SetCount(0); +} + +EndScene +{ + pipeline->ClearShadowSlaves(); +} + +Harvest +{ + ShadowMasters wait for ShadowCastMsg broadcast + + On ShadowCastMsg + + if( !ShadowMaster.CanSee(ShadowCaster.fTotalWorldBounds) ) + forget it; + + hsScalar power = ComputePower(ShadowCaster); + + if( power == 0 ) + forget it; + + // Create ShadowSlave focused on ShadowCaster + // ShadowSlave extent just enough to cover ShadowCaster (including nearplane) + CreateShadowSlave(ShadowCaster.fTotalWorldBounds, power); + + // !!!IMPORTANT + // ShadowMaster contains 2 values for yon. + // First value applies to ShadowMaster. Any ShadowCaster beyond this distance + // won't cast a shadow + // Second value applies to ShadowSlaves. This is the distance beyond the ShadowCaster + // (NOT FROM SHADOW SOURCE) over which the shadow attenuates to zero + // The effective yon for the ShadowSlave is ShadowSlaveYon + DistanceToFarthestPointOnShadowCasterBound + // That's the distance used for culling ShadowReceivers + // The ShadowSlaveYon is used directly in the + + if ShadowSlave extent not visible to current camera + forget it; + + ShadowSlave.Generate + + Submit to pipeline + + endOnMsg + + endfor +} + +///////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////// +Pipeline functions +///////////////////////////////////////////////////////////////////////////////////////////// + +// Puts the slave in a list valid for this frame only. The list will +// be preprocessed at BeginRender. See IPreprocessShadows. +void SubmitShadowSlave(plShadowSlave* slave); + +// rendering all the associated spans into +// a rendertarget of the correct size + +// We have a (possibly empty) list of shadows submitted for this frame. +// At BeginRender, we need to accomplish: +// Find render targets for each shadow request of the requested size. +// Render the associated spans into the render targets. Something like the following: +void IPreprocessShadows() +{ + + SetupShadowCastTextureStages - see below + + for each shadowCaster.fSpans + { + render shadowcaster.fSpans[i] to rendertarget + + shadowCaster.fSpans[i]->SetShadowBit(shadowCaster.fIndex); //index set in CreateShadowSlave + } + + Blur rendertarget (optional); + + // Must ensure we have an alpha border of 255 (for clamping the effect) + //SetBorderTo255(); we don't have to do this if we can set the viewport + // to leave a border and compensate the fov so LightToNDC and LightToTexture match up. +} + +// After doing the usual render for a span (all passes), we call the following. +// If the span accepts shadows, this will loop over all the shadows active this +// frame, and apply the ones that intersect this spans bounds. See below for details. +void IRenderSpanShadows(); + +// At EndRender(), we need to clear our list of shadow slaves. They are only valid for one frame. +void IClearShadowSlaves(); + +// We don't have the depth resolution to even think about self shadowing, so we just don't +// let a slave shadow any of the spans that were rendered into it. +hsBool AcceptsShadow(plSpan* span, plShadowSlave* slave) +{ + return !span->IsShadowBitSet(slave->fIndex); +} + +// Want artists to be able to just disable shadows for spans where they'll either +// look goofy, or won't contribute. +// Also, if we have less than 3 simultaneous textures, we want to skip anything with +// an alpha'd base layer, unless it's been overriden. +hsBool ReceivesShadows(plSpan* span, hsGMaterial* mat) +{ + if( span.fProps & plSpan::kPropNoShadow ) + return false; + + if( span.Props & plSpan::kPropForceShadow ) + return true; + + if( (fMaxLayersAtOnce < 3) && (mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendAlpha) ) + return false; + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////// + +SetBorderTo255() +{ + Set FB blend to Add; + + render a texture the same size as the render target to the render target + as a single quad. Texture has black as color, 0 as alpha except the border + which is black with alpha=255. +} + +Apply +{ + render all passes of span + + if( ShadowSlaveListNotEmpty() ) + RenderSpanShadows +} + +RenderSpanShadows +{ + hsBool first = true; + if receivesShadows(span) + { + for each ShadowSlave + { + + if AcceptsShadow(span, ShadowSlave) && (ShadowSlave->fIsect->Test(span.fBounds) != kVolumeCulled) + { + if( first ) + { + SetupShadowRcvTextureStages(); + first = false; + } + + SetupShadowSlaveTextures() + + render span + + } + } + } +} + +SetupShadowSlaveTextures() +{ + // See below +} + +TRANSFORMS +========== + +Summary - + ShadowSlave.W2Light - world space to space of shadow slave + ShadowSlave.Light2W - unused + + ShadowSlave.LightToNDC - normal projection matrix, maps to + [-1,1], [-1,1], [0,1] (after divide) + AND + has fov decreased slightly to compensate for the viewport being + brought down to preserve the border + + ShadowSlave.LightToTexture - like LightToNDC, but maps to + [0.5 / width, 1 - 0.5/width], [0.5/height, 1 - 0.5/height, [0,1] + fov NOT brought down for border + + ShadowSlave.CameraToTexture = ShadowSlave.LightToTexture * ShadowSlave.W2Light * pipe->GetCameraToWorld(); + + ShadowSlave.CasterLUTXfm - see below + + ShadowSlave.ViewPort = {1, 1, width-2, height-2, 0, 1} + +To Compensate FOV for border +{ + if( perspective ) // spots and omnis + { + delX = fovX / (txtWidth/2); + delY = fovY / (txtHeight/2); + fovX -= delX; + fovY -= delY; + } + else // directional + { + delX = width / (txtWidth/2); + delY = height / (txtHeight/2); + + minX += delX; + minY += delY; + maxX -= delX; + maxY -= delY; + } +} + +To Render ShadowCaster +{ + render transform to ShadowSlave.W2Light * ShadowCaster.L2W + + projection transform to ShadowSlave.LightToNDC + + viewPort to ShadowSlave.ViewPort + + Stage0 - + UVWSrc = CameraSpacePos + + UVWXfm = ShadowSlave.CasterLUTXfm * ShadowSlave.W2L * CameraToWorld + + Texture = U_LUT + + Stage1 - + Disable +} + +ShadowSlave.LUTXfm +{ + // Map 0 => (closest = CasterBnd.Closest), 1 => (CasterBnd.Closest + FalloffDist = farthest) + 0.0, 0.0, 1/(farthest - closest), -closest / (farthest - closest), + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, + + // FOR CASTER ONLY + // Can put bias here if needed (probably) by adding small NEGATIVE + // bias to ShadowSlave.LUTXfm.fMap[0][3]. Bias magnitude would probably be at + // least 0.5f/256.f to compensate for quantization. +} + +To Project onto Shadow Receiver // SetupShadowSlaveTexture +{ + render transform = current; + project transform = current; + viewport = current; + + Stage0 - + UVWSrc = CameraSpacePos + + UVWXfm = ShadowSlave.LightToTexture * ShadowSlave.W2L * CameraToWorld + + Texture = ShadowMap + + Stage1 - + UVWSrc = CameraSpacePos + + UVWXfm = ShadowSlave.RcvLUTXfm * ShadowSlave.W2L * CameraToWorld + + Texture = U_LUT + + [ // Optional for when have > 2 TMUs AND base texture is alpha + Stage2 - + Process base texture normally normally + ] + Stage2/3 + No texture - setup as in ShadowNotes.h +} + +ShadowSlave.Offset +{ + Offset = + { + 0.5, 0.0, 0.0, 0.5 + 0.5 * ShadowSlave.Width, + 0.0, 0.5, 0.0, 0.5 + 0.5 * ShadowSlave.Height, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + } +} + +#endif // Notes diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowMaster.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowMaster.h new file mode 100644 index 00000000..497a2a1e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowMaster.h @@ -0,0 +1,154 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plShadowMaster_inc +#define plShadowMaster_inc + +#include "../pnSceneObject/plObjInterface.h" + +class plShadowCaster; +class plShadowSlave; + +struct hsMatrix44; +class hsBounds3Ext; +class hsStream; +class hsResMgr; +class plMessage; +class plLightInfo; +class plShadowCastMsg; + + +class plShadowMaster : public plObjInterface +{ +public: + // Props inc by 1 (bit shift in bitvector). + enum plDrawProperties { + kDisable = 0, + kSelfShadow, + + kNumProps // last in the list + }; + + +protected: + // Global clamp on shadow map size and stuff + static UInt32 fGlobalMaxSize; + static hsScalar fGlobalMaxDist; + static hsScalar fGlobalVisParm; + + // Constant parameter(s) for this master. + hsScalar fAttenDist; + hsScalar fMaxDist; + hsScalar fMinDist; + + UInt32 fMaxSize; + UInt32 fMinSize; + + hsScalar fPower; + + // Temp data used for one frame and recycled. + hsTArray fSlavePool; + plLightInfo* fLightInfo; + + // These are specific to the projection type (perspective or orthogonal), so have to + // be implemented by the derived class. + virtual void IComputeWorldToLight(const hsBounds3Ext& bnd, plShadowSlave* slave) const = 0; + virtual void IComputeProjections(plShadowCastMsg* castMsg, plShadowSlave* slave) const = 0; + virtual void IComputeISect(const hsBounds3Ext& bnd, plShadowSlave* slave) const = 0; + virtual void IComputeBounds(const hsBounds3Ext& bnd, plShadowSlave* slave) const = 0; + + // Base class implementations of the rest. These might need to be overridden later, especially + // on computing the width and height. That's really specific to the projection type, but + // to get started I'll probably just always return 256x256. + virtual void IComputeCasterBounds(const plShadowCaster* caster, hsBounds3Ext& casterBnd); + virtual void IComputeWidthAndHeight(plShadowCastMsg* castMsg, plShadowSlave* slave) const; + virtual void IComputeLUT(plShadowCastMsg* castMsg, plShadowSlave* slave) const; + virtual hsScalar IComputePower(const plShadowCaster* caster, const hsBounds3Ext& casterBnd) const; + + virtual plShadowSlave* ILastChanceToBail(plShadowCastMsg* castMsg, plShadowSlave* slave); + + virtual plShadowSlave* ICreateShadowSlave(plShadowCastMsg* castMsg, const hsBounds3Ext& casterBnd, hsScalar power); + + virtual plShadowSlave* INewSlave(const plShadowCaster* caster) = 0; + virtual plShadowSlave* INextSlave(const plShadowCaster* caster); + + virtual plShadowSlave* IRecycleSlave(plShadowSlave* slave); + plLightInfo* ISetLightInfo(); + + virtual void IBeginRender(); + virtual hsBool IOnCastMsg(plShadowCastMsg* castMsg); + +public: + plShadowMaster(); + virtual ~plShadowMaster(); + + CLASSNAME_REGISTER( plShadowMaster ); + GETINTERFACE_ANY( plShadowMaster, plObjInterface ); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) {} + + Int32 GetNumProperties() const { return kNumProps; } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + // These are usually handled internally, activating on read and deactivating + // on destruct. Made public in case they need to be manually handled, like + // on dynamic construction and use. + void Deactivate() const; + void Activate() const; + + // These should only be useful on scene conversion. + hsScalar GetAttenDist() const { return fAttenDist; } + void SetAttenDist(hsScalar d) { fAttenDist = d; } + + hsScalar GetMaxDist() const { return fMaxDist; } + hsScalar GetMinDist() const { return fMinDist; } + void SetMaxDist(hsScalar m); + + UInt32 GetMaxSize() const { return fMaxSize; } + UInt32 GetMinSize() const { return fMinSize; } + void SetMaxSize(UInt32 s) { fMaxSize = s; } + void SetMinSize(UInt32 s) { fMinSize = s; } + + hsScalar GetPower() const { return fPower; } + void SetPower(hsScalar f) { fPower = f; } + + static void SetGlobalMaxSize(UInt32 s) ; + static UInt32 GetGlobalMaxSize() { return fGlobalMaxSize; } + + static void SetGlobalMaxDist(hsScalar s) { fGlobalMaxDist = s; } + static hsScalar GetGlobalMaxDist() { return fGlobalMaxDist; } + + static void SetGlobalShadowQuality(hsScalar s); + static hsScalar GetGlobalShadowQuality() { return fGlobalVisParm; } +}; + + + +#endif // plShadowMaster_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowSlave.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowSlave.cpp new file mode 100644 index 00000000..d702a68a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowSlave.cpp @@ -0,0 +1,200 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" + +#include "plShadowSlave.h" +#include "plTweak.h" + +#include + +static const hsScalar kMinMinZ = 1.f; // totally random arbitrary number (has to be > 0). + +bool plShadowSlave::ISetupOrthoViewTransform() +{ + hsBounds3Ext bnd = fCasterWorldBounds; + + bnd.Transform(&fWorldToLight); + + hsScalar minZ = bnd.GetMins().fZ; + hsScalar maxZ = bnd.GetCenter().fZ + fAttenDist; + + hsScalar minX = bnd.GetMins().fX; + hsScalar maxX = bnd.GetMaxs().fX; + + hsScalar minY = bnd.GetMins().fY; + hsScalar maxY = bnd.GetMaxs().fY; + + + hsMatrix44 proj; + proj.Reset(); + proj.NotIdentity(); + + // First the LightToTexture, which uses the above pretty much as is. + // Note the remapping to range [0.5..width-0.5] etc. + // About this kAdjustBias. According to the docs, it should be 0.5, + // and on the perspective projection 0.5 works great. But on the + // directional (ortho) projection, it just makes the mapping wrong, + // and an offset of zero works great. This could be a driver bug, or + // hardware "dependency" (read IHV bug), but whatever, zero is working + // now. Might need to adjust for new drivers or other hardware. + const hsScalar kAdjustBias = 0.0f; + proj.fMap[0][0] = 1.f / (maxX - minX); + proj.fMap[0][3] = -minX / (maxX - minX) + kAdjustBias / fWidth; + proj.fMap[1][1] = -1.f / (maxY - minY); + proj.fMap[1][3] = -minY / (maxY - minY) + kAdjustBias / fHeight; + proj.fMap[2][2] = 1.f; + proj.fMap[3][3] = 1.f; + + fWorldToTexture = proj * fWorldToLight; + + // Now the LightToNDC. This one's a little trickier, because we want to compensate for + // having brought in the viewport to keep our border constant, so we can clamp the + // projected texture and not have the edges smear off to infinity. + // Like the adjust bias above, this part is correct in theory, but only + // screws things up (increases Z-acne). +#if 0 + hsScalar delX = maxX - minX; + minX += delX / (fWidth * 0.5f); + maxX -= delX / (fWidth * 0.5f); + hsScalar delY = maxY - minY; + minY += delY / (fHeight * 0.5f); + maxY -= delY / (fHeight * 0.5f); +#endif + + + fView.SetView(hsPoint3(minX, minY, minZ), hsPoint3(maxX, maxY, maxZ)); + fView.SetScreenSize((UInt16)fWidth, (UInt16)fHeight); + fView.SetCameraTransform(fWorldToLight, fLightToWorld); + fView.SetPerspective(false); + fView.SetViewPort(0, 0, hsScalar(fWidth), hsScalar(fHeight), false); + + fLightDir = fLightToWorld.GetAxis(hsMatrix44::kUp); + SetFlag(kPositional, false); + SetFlag(kReverseCull, true); + + return true; +} + +bool plShadowSlave::ISetupPerspViewTransform() +{ + hsBounds3Ext bnd = fCasterWorldBounds; + + bnd.Transform(&fWorldToLight); + + hsScalar minZ = bnd.GetMins().fZ; + hsScalar maxZ = bnd.GetCenter().fZ + fAttenDist; + + if( minZ < kMinMinZ ) + minZ = kMinMinZ; + + // EAP + // This is my hack to get the Nexus age working. The real problem + // is probably data-side. I take full responsibility for this + // hack-around breaking the entire system, loosing data, causing + // unauthorized credit card transactions, etc. + if (_isnan(bnd.GetMins().fX) || _isnan(bnd.GetMins().fY)) + return false; + if (_isnan(bnd.GetMaxs().fX) || _isnan(bnd.GetMaxs().fY)) + return false; + + hsScalar cotX, cotY; + if( -bnd.GetMins().fX > bnd.GetMaxs().fX ) + { + hsAssert(bnd.GetMins().fX < 0, "Empty shadow caster bounds?"); + cotX = -minZ / bnd.GetMins().fX; + } + else + { + hsAssert(bnd.GetMaxs().fX > 0, "Empty shadow caster bounds?"); + cotX = minZ / bnd.GetMaxs().fX; + } + + if( -bnd.GetMins().fY > bnd.GetMaxs().fY ) + { + hsAssert(bnd.GetMins().fY < 0, "Empty shadow caster bounds?"); + cotY = -minZ / bnd.GetMins().fY; + } + else + { + hsAssert(bnd.GetMaxs().fY > 0, "Empty shadow caster bounds?"); + cotY = minZ / bnd.GetMaxs().fY; + } + + hsMatrix44 proj; + proj.Reset(); + proj.NotIdentity(); + + // First the LightToTexture, which uses the above pretty much as is. + // Note the remapping to range [0.5..width-0.5] etc. Also, the perspective + // divide is by the 3rd output (not the fourth), so we make the 3rd + // output be W (instead of Z). + // This also means that our translate goes into [i][2] instead of [i][3]. +#if 0 + proj.fMap[0][0] = cotX * 0.5f; + proj.fMap[0][2] = -0.5f * (1.f + 0.5f/fWidth); + proj.fMap[1][1] = cotY * 0.5f; + proj.fMap[1][2] = -0.5f * (1.f + 0.5f/fHeight); +#else + plConst(hsScalar) kBiasScale(1.f); + plConst(hsScalar) kBiasTrans(1.f); + proj.fMap[0][0] = cotX * 0.5f * ( hsScalar(fWidth-2.f) / hsScalar(fWidth) ) * kBiasScale; + proj.fMap[0][2] = 0.5f * (1.f - kBiasTrans * 0.5f/fWidth); + proj.fMap[1][1] = -cotY * 0.5f * ( hsScalar(fHeight-2.f) / hsScalar(fHeight) ) * kBiasScale; + proj.fMap[1][2] = 0.5f * (1.f - kBiasTrans * 0.5f/fHeight); +#endif + +#if 0 // This computes correct Z, but we really just want W in 3rd component. HACKFISH + proj.fMap[2][2] = maxZ / (maxZ - minZ); + proj.fMap[2][3] = -minZ * maxZ / (maxZ - minZ); +#elif 1 + proj.fMap[2][2] = 1.f; + proj.fMap[2][3] = 0; +#endif + proj.fMap[3][2] = 1.f; + proj.fMap[3][3] = 0; + + fWorldToTexture = proj * fWorldToLight; + + // Now the LightToNDC. This one's a little trickier, because we want to compensate for + // having brought in the viewport to keep our border constant, so we can clamp the + // projected texture and not have the edges smear off to infinity. + cotX -= cotX / (fWidth * 0.5f); + cotY -= cotY / (fHeight * 0.5f); + + hsScalar tanX = 1.f / cotX; + hsScalar tanY = 1.f / cotY; + fView.SetView(hsPoint3(-tanX, -tanY, minZ), hsPoint3(tanX, tanY, maxZ)); + fView.SetScreenSize((UInt16)fWidth, (UInt16)fHeight); + fView.SetCameraTransform(fWorldToLight, fLightToWorld); + fView.SetPerspective(true); + fView.SetViewPort(0, 0, hsScalar(fWidth), hsScalar(fHeight), false); + + fLightPos = fLightToWorld.GetTranslate(); + SetFlag(kPositional, true); + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowSlave.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowSlave.h new file mode 100644 index 00000000..0b7b9fb5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGLight/plShadowSlave.h @@ -0,0 +1,124 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plShadowSlave_inc +#define plShadowSlave_inc + +#include "hsMatrix44.h" +#include "hsBounds.h" +#include "plViewTransform.h" +#include "hsGeometry3.h" + +class plShadowCaster; +class plVolumeIsect; +class plPipeline; + +class plShadowSlave +{ +public: + + UInt32 fIndex; + + hsMatrix44 fWorldToLight; + hsMatrix44 fLightToWorld; + + hsPoint3 fLightPos; + hsVector3 fLightDir; + + hsMatrix44 fWorldToTexture; + hsMatrix44 fCastLUT; + hsMatrix44 fRcvLUT; + + plViewTransform fView; + + hsScalar fPower; + hsScalar fBlurScale; + + hsBounds3Ext fCasterWorldBounds; + hsBounds3Ext fWorldBounds; + + plVolumeIsect* fIsect; + + UInt32 fWidth; + UInt32 fHeight; + + hsScalar fAttenDist; + + hsScalar fPriority; + + UInt32 fFlags; + + enum SlaveFlag + { + kObeysLightGroups = 0x1, + kIncludesChars = 0x2, + kSelfShadow = 0x4, + kCastInCameraSpace = 0x8, + kReverseZ = 0x10, + kTwoSided = 0x20, + kReverseCull = 0x40, + kPositional = 0x80 + }; + + void SetFlag(SlaveFlag f, hsBool on) { if(on) fFlags |= f; else fFlags &= ~f; } + hsBool HasFlag(SlaveFlag f) const { return 0 != (fFlags & f); } + + hsBool ObeysLightGroups() const { return HasFlag(kObeysLightGroups); } + hsBool IncludesChars() const { return HasFlag(kIncludesChars); } + hsBool SelfShadow() const { return HasFlag(kSelfShadow); } + hsBool CastInCameraSpace() const { return HasFlag(kCastInCameraSpace); } + hsBool ReverseZ() const { return HasFlag(kReverseZ); } + hsBool TwoSided() const { return HasFlag(kTwoSided); } + hsBool ReverseCull() const { return HasFlag(kReverseCull); } + hsBool Positional() const { return HasFlag(kPositional); } + + virtual void Init() { fFlags = 0; } + + const plShadowCaster* fCaster; + + // Internal to pipeline, no touch!!! + int fLightIndex; + int fLightRefIdx; + int fSelfShadowOn; + void* fPipeData; + + bool ISetupOrthoViewTransform(); + bool ISetupPerspViewTransform(); + + virtual bool SetupViewTransform(plPipeline* pipe) = 0; +}; + +class plDirectShadowSlave : public plShadowSlave +{ + virtual bool SetupViewTransform(plPipeline* pipe) { return ISetupOrthoViewTransform(); } +}; + +class plPointShadowSlave : public plShadowSlave +{ + virtual bool SetupViewTransform(plPipeline* pipe) { return ISetupPerspViewTransform(); } +}; + +#endif // plShadowSlave_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsGRenderProcs.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsGRenderProcs.cpp new file mode 100644 index 00000000..2d7312c8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsGRenderProcs.cpp @@ -0,0 +1,234 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsUtils.h" +#include "hsScalar.h" +#include "hsMemory.h" +#include "hsGRenderProcs.h" +#include "hsStream.h" +#include "../plResMgr/plKey.h" +#include "../plResMgr/hsResMgr.h" + +hsGRenderProcs::hsGRenderProcs() +: fNext(nil), + fBack(nil), + fPipeline(nil), + fFlags(kNone), + fLinkCount(0) +{ +} + +hsGRenderProcs::~hsGRenderProcs() +{ +} + +hsGRenderProcs** hsGRenderProcs::IOneBeforeMe(hsGRenderProcs** base) +{ + hsAssert(base, "Searching for place in baseless list"); + + if( !*base ||((*base)->GetPriority() > GetPriority()) ) + return base; + + hsGRenderProcs* trav = *base; + + while( trav->fNext && (trav->fNext->GetPriority() > GetPriority()) ) + trav = trav->fNext; + + hsAssert((trav != this)&&(trav->fNext != this), "Found self in bad place"); + return &trav->fNext; +} + +void hsGRenderProcs::IInsert(hsGRenderProcs** ptr) +{ + hsAssert(*ptr != this, "Re-Inserting self"); + hsAssert(ptr, "Inserting into nil list"); + if( *ptr ) + (*ptr)->fBack = &fNext; + fNext = *ptr; + fBack = ptr; + *ptr = this; +} + +void hsGRenderProcs::IDetach() +{ + if( fNext ) + fNext->fBack = fBack; + *fBack = fNext; + + fNext = nil; + fBack = nil; +} + +void hsGRenderProcs::Enqueue(hsGRenderProcs** base) +{ + // Already linked? Just note another link. + if( fLinkCount++ ) + return; + + IInsert(IOneBeforeMe(base)); + Ref(); +} + +void hsGRenderProcs::Dequeue() +{ + if( fBack && !--fLinkCount ) + { + IDetach(); + UnRef(); + } +} + + +void hsGRenderProcs::Read(hsStream* s, hsResMgr* mgr) +{ + SetFlags(s->ReadSwap32()); + ReadObjectRefs(s, mgr); + Read(s); +} + +void hsGRenderProcs::ReadObjectRefs(hsStream* s, hsResMgr* mgr) +{ + if( fFlags & kObjectRefs ) + { + int n = s->ReadSwap32(); + fObjectRefs.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + { + fObjectRefs[i] = mgr->ReadKey(s); + } + } +} + +void hsGRenderProcs::WriteObjectRefs(hsStream* s, hsResMgr* mgr) +{ + if( fFlags & kObjectRefs ) + { + s->WriteSwap32(fObjectRefs.GetCount()); + int i; + for( i = 0; i < fObjectRefs.GetCount(); i++ ) + { +// if( fObjectRefs[i] ) + { + mgr->WriteKey(s,fObjectRefs[i]); // writes nil any...right? + } +// else +// { +// mgr->WriteKey(s, nil); +// } + } + } +} + +void hsGRenderProcs::Write(hsStream* s, hsResMgr* mgr) +{ + s->WriteSwap32(fFlags); + + WriteObjectRefs(s, mgr); + Write(s); +} + +plDrawable* hsGRenderProcs::GetObjectRef(int i) +{ + return (plDrawable*)((i < fObjectRefs.GetCount()) && fObjectRefs[i] ? fObjectRefs[i]->GetObjectPtr() : nil); +} + +void hsGRenderProcs::SetNumObjectRefs(int n) +{ + if( n > fObjectRefs.GetCount() ) + { + int oldCnt = fObjectRefs.GetCount(); + fObjectRefs.SetCount(n); + int i; + for( i = oldCnt; i < n; i++ ) + fObjectRefs[i] = nil; + } +} + +void hsGRenderProcs::SetObjectRef(plKey* key, int i) +{ + if( i >= fObjectRefs.GetCount() ) + SetNumObjectRefs(i+1); + fObjectRefs[i] = key; + fFlags |= kObjectRefs; +} + +hsBool32 hsGRenderProcs::BeginTree(plPipeline* pipe, plDrawable* root) +{ + hsAssert(fFlags & kObjectRefsInit, "Should have had refs initialized on read"); + + fPipeline = pipe; + + if( Inclusive() ) + { + fColorizer.Init(pipe); + hsColorRGBA col = fColorizer.GetCurrentColor(); + if( !fColorizer.Colorizing() ) + { + col.r = col.g = col.b = 1.f; + } + if( !fColorizer.Alpharizing() ) + col.a = 0.999f; + fColorizer.PushColorize(col, !fColorizer.Colorizing() /* alpha only */); + } + return true; +} + +hsBool32 hsGRenderProcs::BeginObject(plPipeline* pipe, plDrawable* obj) +{ + hsAssert(fFlags & kObjectRefsInit, "Should have had refs initialized on read"); + + fPipeline = pipe; + + if( !Inclusive() ) + { + fColorizer.Init(pipe); + hsColorRGBA col = fColorizer.GetCurrentColor(); + if( !fColorizer.Colorizing() ) + { + col.r = col.g = col.b = 1.f; + } + if( !fColorizer.Alpharizing() ) + col.a = 0.999f; + fColorizer.PushColorize(col, !fColorizer.Colorizing() /* alpha only */); + } + return true; +} + +void hsGRenderProcs::EndObject() +{ + if( !Inclusive() ) + fColorizer.PopColorize(); +} + +void hsGRenderProcs::EndTree() +{ + if( Inclusive() ) + fColorizer.PopColorize(); + fPipeline = nil; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsGRenderProcs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsGRenderProcs.h new file mode 100644 index 00000000..2e506699 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsGRenderProcs.h @@ -0,0 +1,202 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsGRenderProcs_inc +#define hsGRenderProcs_inc + +#include "hsRefCnt.h" +#include "hsScalar.h" +#include "hsMemory.h" +#include "hsBiExpander.h" +#include "../plPipeline/hsGColorizer.h" +#include "../plResMgr/plCreatable.h" + +class plPipeline; +class plDrawable; +class hsTriangle3; +struct hsGTriVertex; +struct hsGVertex3; +struct hsGShadeVertex; +struct hsGSplat3; +class hsStream; +class plKey; +class hsBounds3Ext; +class hsResMgr; + +class hsGRenderProcs : public plCreatable { +public: + enum { + kMaxLabelLen = 128 + }; + enum ProcType { + kTypeAngleFade, + kTypeDistFade, + kTypeMotionBlur1, + kTypeMotionBlur2, + kTypeIntenseAlpha, + kTypeGlobalShade, + kTypeObjDistFade, + kTypeDistShade, + kTypeObjDistShade + }; + enum { + kNone = 0x0, + kInclusive = 0x1, // Affect children + kNOP = 0x2, // Turned off (till EndObject) + kOpaque = 0x4, + kCulled = 0x8, + kObjectRefs = 0x10, + kObjectRefsInit = 0x20 + }; +private: + // Base class private stuff for managing the Queue of Procs on the device. + + UInt32 fLinkCount; + hsGRenderProcs* fNext; + hsGRenderProcs** fBack; + + hsGRenderProcs** IOneBeforeMe(hsGRenderProcs** base); + void IInsert(hsGRenderProcs** beforeMe); + void IDetach(); + +protected: + + UInt32 fFlags; + + hsGColorizer fColorizer; + + plPipeline* fPipeline; + + hsDynamicArray fObjectRefs; +public: + hsGRenderProcs(); + virtual ~hsGRenderProcs(); + + // BeginTree returns false if entire subtree is don't bother to draw, + // else true. Mostly a culling tool. + virtual hsBool32 BeginTree(plPipeline* pipe, plDrawable* root); + + // BeginObject returns true if the object should be drawn, false if + // don't bother. Can also do any initialization it wants. Should this + // get something more innocuous like a bound instead of the SceneObject? + // Is there anything else it might need to know? + virtual hsBool32 BeginObject(plPipeline* pipe, plDrawable* obj); + + // ProcessVerts takes the list of TriVerts and does what it will. + // I'll outline the hsGTriVertex below. The difference between + // the BaseVertex list and the UsedVertex list is interpolation. + // Shade values and generated Texture Coords are computed for + // the Base Triangle only, and then interpolated for vertices + // generated by clipping. So Shade Values and Texture Coordinates + // should only be computed in ProcessBaseVerts(). On the other + // hand, only the vertices from actual drawn triangles are + // transformed, and hence have a screen position to mess with. + // So any wiggling of the screen position should happen in + // ProcessUsedVerts(). These functions might be better named + // ProcessShadeVerts() and ProcessXformVerts(), except that + // vertex illumination (shade) is interpolated, but then + // the interpolated shade is fed into the material to calculate + // color. So messing with final color would happen in ProcessUsedVerts(), + // whereas messing with illumination's in ProcessBaseVerts(). Messing + // with UV's is equally valid in either. In general though, the number + // of BaseVerts is less than or equal to the number of UsedVerts. Most + // shaders would have one or the other a no-op. + + // Process list of unique vertices (with unique hsGXformVerts) which will be drawn to screen + virtual void ProcessScreenVerts(hsExpander& vList) {} + + // Take a list of verts and modulate shades for them. Care should be taken to only bother with verts that + // are not (hsGVertex3::kCulled|hsGVertex3::kDisabled). Also, any verts that this RenderProc causes + // to go completely transparent should be flagged hsGVertex3::kCulled (NOT DISABLED). + // See hsSfxDistFade for example (not exemplary) code. + + // Process list of unique vertices (unique hsGShadeVerts) before interpolation + virtual void ProcessPreInterpShadeVerts(hsExpander& vList) {} + // Process list of unique vertices (unique hsGShadeVerts) after interpolation - these will be drawn to screen + virtual void ProcessPostInterpShadeVerts(hsExpander& vList) {} + + // Process list of unique Pre or Post Interpolation TriVerts (with hsGShade and hsGXformVerts) + // While the TriVerts are unique, there may be sharing among constituents, i.e. position and uv. + // Care must be taken when accumulating effects. + virtual void ProcessPreInterpVerts(hsExpander& vList) {} + virtual void ProcessPostInterpVerts(hsExpander& vList) {} + + // Process list of triangles which are headed for the screen. vList is the full list of unique TriVerts + // used by these triangles. If triangles are added, any generated verts MUST be added to vList. If + // Triangles are removed, verts may be removed from vList (keeping in mind that vList verts may be + // shared between triangles). + virtual void ProcessPreClipTris(hsExpander& tList, hsExpander& vList) {} + virtual void ProcessPreInterpTris(hsExpander& tList, hsExpander& vList) {} + virtual void ProcessPostInterpTris(hsExpander& tList, hsExpander& vList) {} + + // Any cleanup for this object + virtual void EndObject(); + + // Any cleanup for this subtree + virtual void EndTree(); + + // Shaders can set their priority to affect order in which they are called + // When pushed onto device, device uses this priority to sort into queue + virtual hsScalar GetPriority() { return 0; } + + // When a shader is pushed onto the device (by an object), the object + // will pop it back off either before or after drawing its children, + // depending on Inclusive(). Not meaningful for mate + virtual hsBool32 Inclusive() { return fFlags & kInclusive; } + + virtual void Enqueue(hsGRenderProcs** list); + virtual void Dequeue(); + hsGRenderProcs* GetNext() { return fNext; } + + // External object references. Individual RenderProc type responsible for what they're used for. + void SetNumObjectRefs(int n); + UInt32 GetNumObjectRefs() { return fObjectRefs.GetCount(); } + void AddObjectRef(plKey* key) { fObjectRefs.Append(key); fFlags |= kObjectRefs; } + void SetObjectRef(plKey* key, int i=0); + void InsertObjectRef(int i, plKey* key) { fObjectRefs.InsertAtIndex(i, key); fFlags |= kObjectRefs; } + plDrawable* GetObjectRef(int i); + plKey* GetObjectRefKey(int i) { return fObjectRefs[i]; } + void ReadObjectRefs(hsStream* s, hsResMgr* mgr); + void WriteObjectRefs(hsStream* s, hsResMgr* mgr); + + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual void Read(hsStream* s) = 0; + virtual void Write(hsStream* s) = 0; + + virtual const char* GetLabel() const = 0; + virtual ProcType GetType() const = 0; + + UInt32 GetFlags() { return fFlags; } + void SetFlags(UInt32 f) { fFlags = f; } + + CLASSNAME_REGISTER( hsGRenderProcs ); + GETINTERFACE_ANY( hsGRenderProcs, plCreatable ); +}; + +#endif // hsGRenderProcs_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxAngleFade.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxAngleFade.cpp new file mode 100644 index 00000000..b5a9baf6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxAngleFade.cpp @@ -0,0 +1,218 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsMemory.h" +#include "hsSfxAngleFade.h" +#include "hsStream.h" +#include "../plPipeline/plPipeline.h" +//#include "../plPipeline/hsG3DDevice.h" +#include "../plGeometry/hsTriangle3.h" +#include "../plMath/hsFastMath.h" + +hsSfxAngleFade::hsSfxAngleFade() +{ +} + +hsSfxAngleFade::~hsSfxAngleFade() +{ +} + +hsScalar hsSfxAngleFade::IOpacFromDot(hsScalar dot) +{ + if( (fFlags & kTwoSided) + &&(dot < 0) ) + dot = -dot; + + if( dot <= fTable[0].fCosineDel ) + return fTable[0].fOpacity; + + int i; + for( i = 0; (i < fTable.GetCount()) && (dot >= fTable[i].fCosineDel); i++ ) + dot -= fTable[i].fCosineDel; + + if( i >= fTable.GetCount() ) + return fTable[fTable.GetCount()-1].fOpacity; + + dot *= fTable[i-1].fCosineNorm; + hsScalar opac0 = fTable[i-1].fOpacity; + hsScalar opac1 = fTable[i].fOpacity; + + return opac0 + dot * (opac1 - opac0); +} + +void hsSfxAngleFade::ProcessPreInterpTris(hsExpander& tList, hsExpander& vList) +{ + if( !(fFlags & kFaceNormals) ) + return; + +#if 0 // Taken out 2.26.2001 mcn 'cause it accesses the (now defunct) 3DDevice directly + + hsPoint3 vPos = fPipeline->GetViewPositionLocal(); + hsG3DDevice* dev = fPipeline->Get3DDevice(); + + fSetVector.Clear(); + + for( tList.First(); tList.More(); tList.Plus() ) + { + hsTriangle3* tri = tList.Current(); + hsVector3& norm = tri->fNormal; + + + hsScalar dot, opac; + hsGVertex3* vtx; + hsGShadeVertex* shade; + hsVector3 vDir; + + vtx = tri->GetVertex(0); + if( !fSetVector.IsBitSet(vtx->fShadeIdx) ) + { + vDir.Set(&vPos, &vtx->fLocalPos); + dot = hsFastMath::InvSqrtAppr(vDir.MagnitudeSquared()); + dot *= norm.InnerProduct(vDir); + + shade = dev->GetShadeEntry(vtx); + opac = IOpacFromDot(dot); + shade->fColor.a *= opac; + + fSetVector.SetBit(vtx->fShadeIdx); + } + + vtx = tri->GetVertex(1); + if( !fSetVector.IsBitSet(vtx->fShadeIdx) ) + { + vDir.Set(&vPos, &vtx->fLocalPos); + dot = hsFastMath::InvSqrtAppr(vDir.MagnitudeSquared()); + dot *= norm.InnerProduct(vDir); + + shade = dev->GetShadeEntry(vtx); + opac = IOpacFromDot(dot); + shade->fColor.a *= opac; + + fSetVector.SetBit(vtx->fShadeIdx); + } + + vtx = tri->GetVertex(2); + if( !fSetVector.IsBitSet(vtx->fShadeIdx) ) + { + vDir.Set(&vPos, &vtx->fLocalPos); + dot = hsFastMath::InvSqrtAppr(vDir.MagnitudeSquared()); + dot *= norm.InnerProduct(vDir); + + shade = dev->GetShadeEntry(vtx); + opac = IOpacFromDot(dot); + shade->fColor.a *= opac; + + fSetVector.SetBit(vtx->fShadeIdx); + } + + } +#endif +} + +void hsSfxAngleFade::ProcessPreInterpShadeVerts(hsExpander& vList) +{ + if( fFlags & kFaceNormals ) + return; + + hsVector3 vDir =fPipeline->GetViewDirLocal(); + hsPoint3 vPos = fPipeline->GetViewPositionLocal(); + + for( vList.First(); vList.More(); vList.Plus() ) + { + hsGShadeVertex* shade = vList.Current(); + + hsScalar dot; + if( !(fFlags & kDirectional) ) + { + vDir.Set(&vPos, &shade->fLocalPos); + dot = hsFastMath::InvSqrtAppr(vDir.MagnitudeSquared()); + dot *= shade->fNormal.InnerProduct(vDir); + } + else + { + dot = shade->fNormal.InnerProduct(vDir); + } + + hsScalar opac = IOpacFromDot(dot); + + shade->fShade.a *= opac; + } +} + +void hsSfxAngleFade::MakeTable(float* cosList, float* opacList, int num) +{ + fTable.Reset(); + if( !num ) + return; + + int i; + for( i = 0; i < num; i++ ) + { + hsSfxAfTableEntry* t = fTable.Append(); + t->fCosineDel = cosList[i]; + t->fOpacity = opacList[i]; + } + for( i = num-1; i > 0; i-- ) + fTable[i].fCosineDel -= fTable[i-1].fCosineDel; + for( i = 0; i < num-1; i++ ) + fTable[i].fCosineNorm = hsScalarInvert(fTable[i+1].fCosineDel); + fTable[num-1].fCosineNorm = 0; + hsAssert(fTable.GetCount() == num, "Mismatch making table"); +} + +void hsSfxAngleFade::Read(hsStream* s) +{ + fTable.Reset(); + + Int32 cnt = s->ReadSwap32(); + + if( cnt ) + { + hsSfxAfTableEntry* arr = new hsSfxAfTableEntry[cnt]; + int i; + for( i = 0; i < cnt; i++ ) + { + arr[i].fCosineDel = s->ReadSwapScalar(); + arr[i].fCosineNorm = s->ReadSwapScalar(); + arr[i].fOpacity = s->ReadSwapScalar(); + } + + fTable.SetArray(arr, cnt); + } +} + +void hsSfxAngleFade::Write(hsStream* s) +{ + s->WriteSwap32(fTable.GetCount()); + + for( fTable.First(); fTable.More(); fTable.Plus() ) + { + s->WriteSwapScalar(fTable.Current().fCosineDel); + s->WriteSwapScalar(fTable.Current().fCosineNorm); + s->WriteSwapScalar(fTable.Current().fOpacity); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxAngleFade.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxAngleFade.h new file mode 100644 index 00000000..5aa29686 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxAngleFade.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsSfxAngleFade_inc +#define hsSfxAngleFade_inc + +#include "hsBiExpander.h" +#include "hsGRenderProcs.h" +#include "hsBitVector.h" + +class hsSfxAngleFade : public hsGRenderProcs { +public: + enum { + kDirectional = 0x10000, + kTargetRelative = 0x20000, + kTwoSided = 0x40000, + kFaceNormals = 0x80000 + }; + struct hsSfxAfTableEntry { + hsScalar fCosineDel; + hsScalar fCosineNorm; + hsScalar fOpacity; + }; +protected: + + hsBitVector fSetVector; + hsExpander fTable; + + hsScalar IOpacFromDot(hsScalar dot); +public: + hsSfxAngleFade(); + virtual ~hsSfxAngleFade(); + + virtual void ProcessPreInterpShadeVerts(hsExpander& vList); + virtual void ProcessPreInterpTris(hsExpander& tList, hsExpander& vList); + + void MakeTable(float* cosList, float* opacList, int num); // lists sorted from lowest cosine to highest + + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); + + virtual const char* GetLabel() const { return "hsSfxAngleFade"; } + + virtual ProcType GetType() const { return kTypeAngleFade; } + + CLASSNAME_REGISTER( hsSfxAngleFade ); + GETINTERFACE_ANY( hsSfxAngleFade, hsGRenderProcs ); + +}; + +#endif // hsSfxAngleFade_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistFade.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistFade.cpp new file mode 100644 index 00000000..4c3aa43f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistFade.cpp @@ -0,0 +1,315 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsMemory.h" +#include "hsSfxDistFade.h" +#include "hsStream.h" +#include "../plPipeline/plPipeline.h" +#include "../plGeometry/hsTriangle3.h" +#include "../plDrawable/plDrawable.h" + +#include "../plIntersect/hsBounds.h" + + +static hsScalar globalScale = 1.f; + +hsSfxDistFade::hsSfxDistFade() +: fMinDist(0), fMaxDist(0) +{ +} + +hsSfxDistFade::~hsSfxDistFade() +{ +} + +hsScalar hsSfxDistFade::IOpacFromDist(hsScalar dist) +{ + if( dist <= fTable[0].fDistDel ) + return fTable[0].fOpacity; + + int i; + for( i = 0; (i < fTable.GetCount()) && (dist >= fTable[i].fDistDel); i++ ) + dist -= fTable[i].fDistDel; + + if( i >= fTable.GetCount() ) + return fTable[fTable.GetCount()-1].fOpacity; + + dist *= fTable[i-1].fDistNorm; + hsScalar opac0 = fTable[i-1].fOpacity; + hsScalar opac1 = fTable[i].fOpacity; + + return opac0 + dist * (opac1 - opac0); +} + +hsBool32 hsSfxDistFade::BeginObject(plPipeline* pipe, plDrawable* obj) +{ + hsGRenderProcs::BeginObject(pipe, obj); + + fFlags &= ~(kCulled | kNOP); + + hsPoint3 vPos; + if( GetObjectRef(1) ) + { + hsPoint3 wPos; + GetObjectRef(1)->GetLocalToWorld().GetTranslate(&wPos); + hsMatrix44 w2l = fPipeline->GetWorldToLocal(); + vPos = w2l * wPos; + } + else + { + vPos = fPipeline->GetViewPositionLocal(); + } + + hsScalar scale = 1.f / fPipeline->GetLocalScale(); + scale *= globalScale; + + const hsBounds3Ext& bnd = obj->GetLocalBounds(); + + hsPoint3 inner, outer; + bnd.ClosestPoint(vPos, inner, outer); + + hsScalar minDist, maxDist; + + minDist = hsVector3(&vPos, &inner).Magnitude(); + maxDist = hsVector3(&vPos, &outer).Magnitude(); + + minDist *= scale; + maxDist *= scale; + + if( (fFlags & kCullsBefore) + &&(maxDist <= fMinDist) ) + { + fFlags |= kCulled; + return false; + } + + if( (fFlags & kCullsBeyond) + &&(minDist > fMaxDist) ) + { + fFlags |= kCulled; + return false; + } + + if( (fFlags & kIdleBefore) + &&(maxDist < fMinIdle) ) + fFlags |= kNOP; + + if( (fFlags & kIdleBeyond) + &&(minDist > fMaxIdle) ) + fFlags |= kNOP; + + return true; +} + +void hsSfxDistFade::ProcessPreInterpShadeVerts(hsExpander& vList) +{ + if( fFlags & (kPostInterp | kNOP) ) + return; + + hsPoint3 vPos; + if( GetObjectRef(1) ) + { + hsPoint3 wPos; + GetObjectRef(1)->GetLocalToWorld().GetTranslate(&wPos); + hsMatrix44 w2l = fPipeline->GetWorldToLocal(); + vPos = w2l * wPos; + } + else + { + vPos = fPipeline->GetViewPositionLocal(); + } + hsScalar scale = 1.f / fPipeline->GetLocalScale(); + + scale *= globalScale; + + for( vList.First(); vList.More(); vList.Plus() ) + { + hsGShadeVertex* shade = vList.Current(); + + hsScalar dist = hsVector3(&shade->fLocalPos, &vPos).Magnitude(); + dist *= scale; + + hsScalar opac = IOpacFromDist(dist); + + if( opac > 0 ) + shade->fShade.a *= opac; + else + { + shade->fShade.a = 0; + shade->fBaseVertex->fFlags |= hsGVertex3::kCulled; + } + } +} + +void hsSfxDistFade::ProcessPostInterpShadeVerts(hsExpander& vList) +{ + if( !(fFlags & kPostInterp) ) + return; + + if( fFlags & kNOP ) + return; + + hsPoint3 vPos; + if( GetObjectRef(1) ) + { + hsPoint3 wPos; + GetObjectRef(1)->GetLocalToWorld().GetTranslate(&wPos); + hsMatrix44 w2l = fPipeline->GetWorldToLocal(); + vPos = w2l * wPos; + } + else + { + vPos = fPipeline->GetViewPositionLocal(); + } + hsScalar scale = 1.f / fPipeline->GetLocalScale(); + + for( vList.First(); vList.More(); vList.Plus() ) + { + hsGShadeVertex* shade = vList.Current(); + + hsScalar dist = hsVector3(&shade->fLocalPos, &vPos).Magnitude(); + dist *= scale; + + hsScalar opac = IOpacFromDist(dist); + + if( opac > 0 ) + shade->fColor.a *= opac; + else + { + shade->fColor.a = 0; + shade->fBaseVertex->fFlags |= hsGVertex3::kCulled; + } + } +} + +void hsSfxDistFade::MakeTable(float* distList, float* opacList, int num) +{ + fTable.Reset(); + if( !num ) + return; + + int i; + for( i = 0; i < num; i++ ) + { + hsSfxDfTableEntry* t = fTable.Append(); + t->fDistDel = distList[i]; + t->fOpacity = opacList[i]; + } + for( i = num-1; i > 0; i-- ) + fTable[i].fDistDel -= fTable[i-1].fDistDel; + for( i = 0; i < num-1; i++ ) + fTable[i].fDistNorm = hsScalarInvert(fTable[i+1].fDistDel); + fTable[num-1].fDistNorm = 0; + hsAssert(fTable.GetCount() == num, "Mismatch making table"); + + if( fTable[0].fOpacity <= 0 ) + fFlags |= kCullsBefore; + if( fTable[num-1].fOpacity <= 0 ) + fFlags |= kCullsBeyond; + if( fTable[0].fOpacity >= 1.f ) + fFlags |= kIdleBefore; + if( fTable[num-1].fOpacity >= 1.f ) + fFlags |= kIdleBeyond; + + int iMin; + for( iMin = 0; (iMin < fTable.GetCount())&&(fTable[iMin].fOpacity <= 0); iMin++ ); + fMinDist = fTable[0].fDistDel; + for( i = 1; i < iMin; i++ ) + fMinDist += fTable[i].fDistDel; + + for( iMin = 0; (iMin < fTable.GetCount())&&(fTable[iMin].fOpacity >= 1.f); iMin++ ); + fMinIdle = fTable[0].fDistDel; + for( i = 1; i < iMin; i++ ) + fMinIdle += fTable[i].fDistDel; + + int iMax; + for( iMax = fTable.GetCount()-1; (iMax >= 0)&&(fTable[iMax].fOpacity <= 0); iMax-- ); + if( ++iMax >= fTable.GetCount() ) + iMax = fTable.GetCount()-1; + fMaxDist = fTable[0].fDistDel; + for( i = 1; i <= iMax; i++ ) + fMaxDist += fTable[i].fDistDel; + + for( iMax = fTable.GetCount()-1; (iMax >= 0)&&(fTable[iMax].fOpacity >= 1.f); iMax-- ); + if( ++iMax >= fTable.GetCount() ) + iMax = fTable.GetCount()-1; + fMaxIdle = fTable[0].fDistDel; + for( i = 1; i <= iMax; i++ ) + fMaxIdle += fTable[i].fDistDel; +} + +void hsSfxDistFade::Read(hsStream* s) +{ + fTable.Reset(); + + fMinDist = s->ReadSwapScalar(); + fMaxDist = s->ReadSwapScalar(); + + if( fFlags & (kIdleBefore | kIdleBeyond) ) + { + fMinIdle = s->ReadSwapScalar(); + fMaxIdle = s->ReadSwapScalar(); + } + + Int32 cnt = s->ReadSwap32(); + + if( cnt ) + { + hsSfxDfTableEntry* arr = new hsSfxDfTableEntry[cnt]; + int i; + for( i = 0; i < cnt; i++ ) + { + arr[i].fDistDel = s->ReadSwapScalar(); + arr[i].fDistNorm = s->ReadSwapScalar(); + arr[i].fOpacity = s->ReadSwapScalar(); + } + + fTable.SetArray(arr, cnt); + } +} + +void hsSfxDistFade::Write(hsStream* s) +{ + s->WriteSwapScalar(fMinDist); + s->WriteSwapScalar(fMaxDist); + + if( fFlags & (kIdleBefore | kIdleBeyond) ) + { + s->WriteSwapScalar(fMinIdle); + s->WriteSwapScalar(fMaxIdle); + } + + s->WriteSwap32(fTable.GetCount()); + + for( fTable.First(); fTable.More(); fTable.Plus() ) + { + s->WriteSwapScalar(fTable.Current().fDistDel); + s->WriteSwapScalar(fTable.Current().fDistNorm); + s->WriteSwapScalar(fTable.Current().fOpacity); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistFade.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistFade.h new file mode 100644 index 00000000..4a47cdae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistFade.h @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsSfxDistFade_inc +#define hsSfxDistFade_inc + +#include "hsBiExpander.h" +#include "hsGRenderProcs.h" + +class hsSfxDistFade : public hsGRenderProcs { +public: + enum { + kCullsBefore = 0x10000, + kCullsBeyond = 0x20000, + + kDistFromView = 0x40000, + kDistFromTarget = 0x80000, + kDistAlongX = 0x100000, + kDistAlongY = 0x200000, + kDistAlongZ = 0x400000, + kWorldDist = 0x800000, + + kPostInterp = 0x1000000, + + kIdleBefore = 0x2000000, + kIdleBeyond = 0x4000000 + }; + + struct hsSfxDfTableEntry { + hsScalar fDistDel; + hsScalar fDistNorm; + hsScalar fOpacity; + }; +protected: + + hsScalar fMinDist; + hsScalar fMaxDist; + + hsScalar fMinIdle; + hsScalar fMaxIdle; + + hsExpander fTable; + + hsScalar IOpacFromDist(hsScalar dist); +public: + hsSfxDistFade(); + virtual ~hsSfxDistFade(); + + virtual hsBool32 BeginObject(plPipeline* pipe, plDrawable* obj); + + virtual void ProcessPreInterpShadeVerts(hsExpander& vList); + virtual void ProcessPostInterpShadeVerts(hsExpander& vList); + + void MakeTable(float* distList, float* opacList, int num); // lists sorted from lowest cosine to highest + + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); + + virtual const char* GetLabel() const { return "hsSfxDistFade"; } + + virtual ProcType GetType() const { return kTypeDistFade; } + + CLASSNAME_REGISTER( hsSfxDistFade ); + GETINTERFACE_ANY( hsSfxDistFade, hsGRenderProcs ); + +}; + +#endif // hsSfxDistFade_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistShade.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistShade.cpp new file mode 100644 index 00000000..a981f797 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistShade.cpp @@ -0,0 +1,276 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsMemory.h" +#include "hsSfxDistShade.h" +#include "hsStream.h" +#include "../plGeometry/hsTriangle3.h" + +#include "../plIntersect/hsBounds.h" +#include "../plDrawable/plDrawable.h" +#include "../plPipeline/plPipeline.h" + +static hsScalar globalScale = 1.f; + +hsSfxDistShade::hsSfxDistShade() +: fMinDist(0), fMaxDist(0) +{ +} + +hsSfxDistShade::~hsSfxDistShade() +{ +} + +hsScalar hsSfxDistShade::IShadeFromDist(hsScalar dist) +{ + if( dist <= fTable[0].fDistDel ) + return fTable[0].fShade; + + int i; + for( i = 0; (i < fTable.GetCount()) && (dist >= fTable[i].fDistDel); i++ ) + dist -= fTable[i].fDistDel; + + if( i >= fTable.GetCount() ) + return fTable[fTable.GetCount()-1].fShade; + + dist *= fTable[i-1].fDistNorm; + hsScalar shade0 = fTable[i-1].fShade; + hsScalar shade1 = fTable[i].fShade; + + return shade0 + dist * (shade1 - shade0); +} + + + +hsBool32 hsSfxDistShade::BeginObject(plPipeline* pipe, plDrawable* obj) +{ + hsGRenderProcs::BeginObject(pipe, obj); + + fFlags &= ~(kCulled | kNOP); + + hsPoint3 vPos; + if( GetObjectRef(1) ) + { + hsPoint3 wPos; + GetObjectRef(1)->GetLocalToWorld().GetTranslate(&wPos); + hsMatrix44 w2l = pipe->GetWorldToLocal(); + vPos = w2l * wPos; + } + else + { + vPos = pipe->GetViewPositionLocal(); + } + hsVector3 vDir = -pipe->GetViewDirLocal(); + + hsScalar scale = 1.f / fPipeline->GetLocalScale(); + scale *= globalScale; + + hsScalar vD = -(vDir.InnerProduct(vPos)); + vD *= scale; + + const hsBounds3Ext& bnd = obj->GetLocalBounds(); + + hsPoint3 corner; + bnd.GetCorner(&corner); + hsVector3 axis[3]; + bnd.GetAxes(axis+0, axis+1, axis+2); + + hsScalar dist = vDir.InnerProduct(corner) + vD; + hsScalar minDist = dist; + hsScalar maxDist = dist; + + int i; + for( i = 0; i < 3; i++ ) + { + dist = vDir.InnerProduct(axis[i]); + if( dist < 0 ) + minDist += dist; + else + maxDist += dist; + } + + minDist *= scale; + maxDist *= scale; + + fFlags &= ~kShadeConstant; + if( maxDist < fMinDist ) + { + fFlags |= kShadeConstant; + fConstShade = fTable[0].fShade; + } + else if( minDist > fMaxDist ) + { + fFlags |= kShadeConstant; + fConstShade = fTable[fTable.GetCount()-1].fShade; + } + return true; +} + +void hsSfxDistShade::ProcessPreInterpShadeVerts(hsExpander& vList) +{ + if( fFlags & kShadeConstant ) + { + IConstShadeVerts(vList); + } + else + { + ICalcShadeVerts(vList); + } +} + +void hsSfxDistShade::IConstShadeVerts(hsExpander& vList) +{ + for( vList.First(); vList.More(); vList.Plus() ) + { + hsGShadeVertex* svtx = vList.Current(); + svtx->fShade.r *= fConstShade; + svtx->fShade.g *= fConstShade; + svtx->fShade.b *= fConstShade; + } +} + +void hsSfxDistShade::ICalcShadeVerts(hsExpander& vList) +{ + hsPoint3 vPos; + if( GetObjectRef(1) ) + { + hsPoint3 wPos; + GetObjectRef(1)->GetLocalToWorld().GetTranslate(&wPos); + hsMatrix44 w2l = fPipeline->GetWorldToLocal(); + vPos = w2l * wPos; + } + else + { + vPos = fPipeline->GetViewPositionLocal(); + } + hsVector3 vDir = fPipeline->GetViewDirLocal(); + + hsScalar vDist = vDir.InnerProduct(vPos); + + hsScalar scale = 1.f / fPipeline->GetLocalScale(); + scale *= globalScale; + + for( vList.First(); vList.More(); vList.Plus() ) + { + hsGShadeVertex* svtx = vList.Current(); + + hsScalar dist = -vDir.InnerProduct(svtx->fLocalPos); + dist += vDist; + dist *= scale; + + hsScalar shade = IShadeFromDist(dist); + + if( shade > 0 ) + { + svtx->fShade.r *= shade; + svtx->fShade.g *= shade; + svtx->fShade.b *= shade; + } + else + { + svtx->fShade.r = 0; + svtx->fShade.g = 0; + svtx->fShade.b = 0; + } + } +} + +void hsSfxDistShade::MakeTable(float* distList, float* shadeList, int num) +{ + fTable.Reset(); + if( !num ) + return; + + int i; + for( i = 0; i < num; i++ ) + { + hsSfxDfTableEntry* t = fTable.Append(); + t->fDistDel = distList[i]; + t->fShade = shadeList[i]; + } + for( i = num-1; i > 0; i-- ) + fTable[i].fDistDel -= fTable[i-1].fDistDel; + for( i = 0; i < num-1; i++ ) + fTable[i].fDistNorm = hsScalarInvert(fTable[i+1].fDistDel); + fTable[num-1].fDistNorm = 0; + hsAssert(fTable.GetCount() == num, "Mismatch making table"); + + int iMin; + for( iMin = 0; (iMin < fTable.GetCount())&&(fTable[iMin].fShade <= 0); iMin++ ); + fMinDist = fTable[0].fDistDel; + for( i = 1; i < iMin; i++ ) + fMinDist += fTable[i].fDistDel; + + int iMax; + for( iMax = fTable.GetCount()-1; (iMax >= 0)&&(fTable[iMax].fShade <= 0); iMax-- ); + if( ++iMax >= fTable.GetCount() ) + iMax = fTable.GetCount()-1; + fMaxDist = fTable[0].fDistDel; + for( i = 1; i <= iMax; i++ ) + fMaxDist += fTable[i].fDistDel; + +} + +void hsSfxDistShade::Read(hsStream* s) +{ + fTable.Reset(); + + fMinDist = s->ReadSwapScalar(); + fMaxDist = s->ReadSwapScalar(); + + Int32 cnt = s->ReadSwap32(); + + if( cnt ) + { + hsSfxDfTableEntry* arr = TRACKED_NEW hsSfxDfTableEntry[cnt]; + int i; + for( i = 0; i < cnt; i++ ) + { + arr[i].fDistDel = s->ReadSwapScalar(); + arr[i].fDistNorm = s->ReadSwapScalar(); + arr[i].fShade = s->ReadSwapScalar(); + } + + fTable.SetArray(arr, cnt); + } +} + +void hsSfxDistShade::Write(hsStream* s) +{ + s->WriteSwapScalar(fMinDist); + s->WriteSwapScalar(fMaxDist); + + s->WriteSwap32(fTable.GetCount()); + + for( fTable.First(); fTable.More(); fTable.Plus() ) + { + s->WriteSwapScalar(fTable.Current().fDistDel); + s->WriteSwapScalar(fTable.Current().fDistNorm); + s->WriteSwapScalar(fTable.Current().fShade); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistShade.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistShade.h new file mode 100644 index 00000000..2d92da23 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxDistShade.h @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsSfxDistShade_inc +#define hsSfxDistShade_inc + +#include "hsBiExpander.h" +#include "hsGRenderProcs.h" + +class hsSfxDistShade : public hsGRenderProcs { +public: + enum { + kShadeConstant = 0x10000 + }; + + struct hsSfxDfTableEntry { + hsScalar fDistDel; + hsScalar fDistNorm; + hsScalar fShade; + }; +protected: + + hsScalar fMinDist; + hsScalar fMaxDist; + + hsScalar fConstShade; + + hsScalar fMinIdle; + hsScalar fMaxIdle; + + hsExpander fTable; + + void IConstShadeVerts(hsExpander& vList); + void ICalcShadeVerts(hsExpander& vList); + hsScalar IShadeFromDist(hsScalar dist); +public: + hsSfxDistShade(); + virtual ~hsSfxDistShade(); + + virtual hsBool32 BeginObject(plPipeline* pipe, plDrawable* obj); + + virtual void ProcessPreInterpShadeVerts(hsExpander& vList); + + void MakeTable(float* distList, float* shadeList, int num); // lists sorted from lowest cosine to highest + + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); + + virtual const char* GetLabel() const { return "hsSfxDistShade"; } + + virtual ProcType GetType() const { return kTypeDistShade; } + + CLASSNAME_REGISTER( hsSfxDistShade ); + GETINTERFACE_ANY( hsSfxDistShade, hsGRenderProcs ); + +}; + +#endif // hsSfxDistShade_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxGlobalShade.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxGlobalShade.cpp new file mode 100644 index 00000000..67caff50 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxGlobalShade.cpp @@ -0,0 +1,226 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsMemory.h" +#include "hsSfxGlobalShade.h" +#include "hsStream.h" +//#include "../plPipeline/hsG3DDevice.h" +#include "../plPipeline/plPipeline.h" +#include "../plGLight/hsGProjector3.h" +#include "../plSurface/hsGLayer.h" +#include "../plSurface/hsGMaterial.h" +#include "../plDrawable/plDrawable.h" +#include "../plIntersect/hsBounds.h" + + +void hsSfxGlobalShade::ISetIntensity(hsPoint3& pos) +{ + if( fGSFlags & kFromFog ) + ISetFromFog(pos); + else + if( fGSFlags & kFromClear ) + ISetFromClear(pos); + else + if( fGSFlags & kFromLights ) + ISetFromLights(pos); + + fIntensity.a = hsMaximum(fIntensity.r, hsMaximum(fIntensity.g, fIntensity.b)); +} + +void hsSfxGlobalShade::ISetFromClear(hsPoint3& pos) +{ + fIntensity.Set(0,0,0,0); +#if 0 // Taken out 2.26.2001 mcn 'cause it accesses the (now defunct) 3DDevice directly + hsG3DDevice* dev = fPipeline->Get3DDevice(); + hsGEnvironment* env = dev->GetEnvironment(); + if( env && (env->GetFlags() & hsGEnvironment::kClearColorSet) ) + { + fIntensity = env->GetClearColor(); + } +#endif +} + +void hsSfxGlobalShade::ISetFromFog(hsPoint3& pos) +{ + fIntensity.Set(0,0,0,0); +#if 0 // Taken out 2.26.2001 mcn 'cause it accesses the (now defunct) 3DDevice directly + hsG3DDevice* dev = fPipeline->Get3DDevice(); + hsGEnvironment* env = dev->GetEnvironment(); + if( env && (env->GetFlags() & hsGEnvironment::kFogColorSet) ) + { + fIntensity = env->GetFogColor(); + } +#endif +} + +void hsSfxGlobalShade::ISetFromLights(hsPoint3& pos) +{ + fIntensity = ISumLights(pos); +} + +hsColorRGBA hsSfxGlobalShade::ISumLights(hsPoint3& pos) +{ + hsColorRGBA accum; + accum.Set(0,0,0,0); + +#if 0 // Taken out 2.26.2001 mcn 'cause it accesses the (now defunct) 3DDevice directly + hsG3DDevice* dev = fPipeline->Get3DDevice(); + for( dev->FirstProjector(); dev->MoreProjectors(); dev->IncProjector() ) + { + hsGProjector3* proj = dev->CurrProjector(); + + if( proj->IsOmni() ) + { + hsScalar intensity = proj->AttenuatePoint(&pos) * proj->GetIntensity(); + + if( intensity > 0.f ) + { + hsColorRGBA col = intensity * proj->GetLightColor(); + accum += col; + } + } + else + if( proj->IsPerspective() ) // spot + { + hsPoint4 ang; + UInt32 clips; + proj->GetNdcPoints(1, &pos, sizeof(pos), &ang, kClipAll, &clips); + + if( !clips + || !( proj->IsAttenuated() || proj->AttenuatesAlpha() || (clips & ~kClipYon) ) + ) + { + hsScalar intensity = proj->AttenuatePoint(&pos) * proj->GetIntensity(); + + if( intensity > 0.f ) + { + hsColorRGBA col = intensity * proj->GetLightColor(); + accum += col; + } + } + } + else // directional + { + hsColorRGBA col = proj->GetIntensity() * proj->GetLightColor(); + accum += col; + } + } +#endif + return accum; +} + +void hsSfxGlobalShade::ProcessPreInterpShadeVerts(hsExpander& vList) +{ + if( fCurrentLayer ) + { + if( fGSFlags & kAffectDiffuse ) + fCurrentLayer->SetColor(fRestoreColor.r, fRestoreColor.g, fRestoreColor.b, fRestoreColor.a); + else + fCurrentLayer->SetAmbientColor(fRestoreColor.r, fRestoreColor.g, fRestoreColor.b, fRestoreColor.a); + } + +#if 0 // Taken out 2.26.2001 mcn 'cause it accesses the (now defunct) 3DDevice directly + hsG3DDevice* dev = fPipeline->Get3DDevice(); + hsRefCnt_SafeAssign(fCurrentLayer, dev->GetCurrentLayer()); + if( fCurrentLayer ) + { + fRestoreColor = fGSFlags & kAffectDiffuse ? fCurrentLayer->GetColor() : fCurrentLayer->GetAmbientColor(); + hsColorRGBA col = fAmbient; + if( fGSFlags & kScalarIntensity ) + { + col.r += fDiffuse.r * fIntensity.a; + col.g += fDiffuse.g * fIntensity.a; + col.b += fDiffuse.b * fIntensity.a; + } + else + { + col.r += fDiffuse.r * fIntensity.r; + col.g += fDiffuse.g * fIntensity.g; + col.b += fDiffuse.b * fIntensity.b; + } + if( fGSFlags & kAffectDiffuse ) + fCurrentLayer->SetColor(col.r, col.g, col.b, fRestoreColor.a); + else + fCurrentLayer->SetAmbientColor(col.r, col.g, col.b, fRestoreColor.a); + } +#endif +} + +hsBool32 hsSfxGlobalShade::BeginObject(plPipeline* pipe, plDrawable* obj) +{ + hsBool32 retVal = hsGRenderProcs::BeginObject(pipe, obj); + + const hsBounds3Ext& bnd = obj->GetLocalBounds(); + hsPoint3 pos = bnd.GetCenter(); + ISetIntensity(pos); + + return retVal; +} + +void hsSfxGlobalShade::EndObject() +{ + hsGRenderProcs::EndObject(); + if( fCurrentLayer ) + { + if( fGSFlags & kAffectDiffuse ) + fCurrentLayer->SetColor(fRestoreColor.r, fRestoreColor.g, fRestoreColor.b, fRestoreColor.a); + else + fCurrentLayer->SetAmbientColor(fRestoreColor.r, fRestoreColor.g, fRestoreColor.b, fRestoreColor.a); + hsRefCnt_SafeUnRef(fCurrentLayer); + fCurrentLayer = nil; + } +} + +void hsSfxGlobalShade::Read(hsStream* s) +{ + fGSFlags = s->ReadSwap32(); + fAmbient.Read(s); + fDiffuse.Read(s); + if( fGSFlags & kFromLights ) + fGSFlags |= kAffectDiffuse; +} + +void hsSfxGlobalShade::Write(hsStream* s) +{ + s->WriteSwap32(fGSFlags); + fAmbient.Write(s); + fDiffuse.Write(s); +} + +hsSfxGlobalShade::hsSfxGlobalShade() +{ + fCurrentLayer = nil; + fGSFlags = 0; + fAmbient.Set(0,0,0,0); + fDiffuse.Set(1.f,1.f,1.f,1.f); +} + +hsSfxGlobalShade::~hsSfxGlobalShade() +{ + hsRefCnt_SafeUnRef(fCurrentLayer); // should be nil anyway unless we're destroyed during processing +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxGlobalShade.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxGlobalShade.h new file mode 100644 index 00000000..0a89b514 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxGlobalShade.h @@ -0,0 +1,104 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#ifndef hsSfxGlobalShade_inc +#define hsSfxGlobalShade_inc + +#include "hsBiExpander.h" +#include "hsGRenderProcs.h" +#include "hsColorRGBA.h" +#include "hsGeometry3.h" + +class hsGLayer; + +class hsSfxGlobalShade : public hsGRenderProcs { +public: + enum { + kNone = 0x0, + kFromFog = 0x1, + kFromClear = 0x2, + kFromLights = 0x4, + kSourceMask = kFromFog | kFromClear | kFromLights, + kScalarIntensity = 0x8, + kAffectDiffuse = 0x10 + }; +protected: + + // Constants from which to work. + UInt32 fGSFlags; + + hsColorRGBA fAmbient; + hsColorRGBA fDiffuse; + + // Calculated each invocation. + hsColorRGBA fIntensity; + + hsGLayer* fCurrentLayer; + hsColorRGBA fRestoreColor; + + void ISetIntensity(hsPoint3& pos); + void ISetFromFog(hsPoint3& pos); + void ISetFromClear(hsPoint3& pos); + void ISetFromLights(hsPoint3& pos); + hsColorRGBA ISumLights(hsPoint3& pos); +public: + hsSfxGlobalShade(); + virtual ~hsSfxGlobalShade(); + + virtual void ProcessPreInterpShadeVerts(hsExpander& vList); + + virtual hsBool32 BeginObject(plPipeline* pipe, plDrawable* obj); + virtual void EndObject(); + + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); + + virtual const char* GetLabel() const { return "hsSfxGlobalShade"; } + + virtual ProcType GetType() const { return kTypeGlobalShade; } + + void SetAmbient(const hsColorRGBA& col) { fAmbient = col; } + hsColorRGBA GetAmbient() const { return fAmbient; } + + void SetDiffuse(const hsColorRGBA& col) { fDiffuse = col; } + hsColorRGBA GetDiffuse() const { return fDiffuse; } + + void SetSource(UInt32 f) { fGSFlags &= ~kSourceMask; fGSFlags |= f; } + UInt32 GetSource() { return fGSFlags & kSourceMask; } + + void SetScalar(hsBool32 on) { if(on)fGSFlags |= kScalarIntensity; else fGSFlags &= ~kScalarIntensity; } + hsBool32 GetScalar() { return 0 != (fGSFlags & kScalarIntensity); } + + void SetAffectDiffuse(hsBool32 on) { if(on)fGSFlags |= kAffectDiffuse; else fGSFlags &= ~kAffectDiffuse; } + hsBool32 GetAffectDiffuse() { return 0 != (fGSFlags & kAffectDiffuse); } + + CLASSNAME_REGISTER( hsSfxGlobalShade ); + GETINTERFACE_ANY( hsSfxGlobalShade, hsGRenderProcs ); + +}; + +#endif // hsSfxGlobalShade_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxIntenseAlpha.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxIntenseAlpha.cpp new file mode 100644 index 00000000..92580867 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxIntenseAlpha.cpp @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#include "hsTypes.h" +#include "hsGeometry3.h" +#include "hsSfxIntenseAlpha.h" +#include "../plGeometry/hsTriangle3.h" + + +hsSfxIntenseAlpha::hsSfxIntenseAlpha() +: fMinAlpha(0) +{ + fFlags |= kInclusive; +} + +hsSfxIntenseAlpha::~hsSfxIntenseAlpha() +{ +} + +void hsSfxIntenseAlpha::ProcessPreInterpShadeVerts(hsExpander& vList) +{ + hsScalar oScale = 1.f - fMinAlpha; + for( vList.First(); vList.More(); vList.Plus() ) + { + hsGShadeVertex* s = vList.Current(); + hsScalar o = hsMaximum(hsMaximum(s->fShade.r, s->fShade.g), s->fShade.b); + o *= oScale; + o += fMinAlpha; + s->fShade.a *= o; + } +} + +void hsSfxIntenseAlpha::Read(hsStream* s) +{ + fMinAlpha = s->ReadSwapScalar(); +} + +void hsSfxIntenseAlpha::Write(hsStream* s) +{ + s->WriteSwapScalar(fMinAlpha); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxIntenseAlpha.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxIntenseAlpha.h new file mode 100644 index 00000000..5cfd25f0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxIntenseAlpha.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsSfxIntenseAlpha_inc +#define hsSfxIntenseAlpha_inc + +#include "hsGRenderProcs.h" +#include "hsGeometry3.h" + +class hsSfxIntenseAlpha : public hsGRenderProcs +{ +protected: + hsVector3 fDirViewPerp; + hsVector3 fDirScreen; + hsScalar fOpacityScale; + hsScalar fOpacityMax; + + hsScalar fMinAlpha; + +public: + hsSfxIntenseAlpha(); + virtual ~hsSfxIntenseAlpha(); + + void SetMinAlpha(hsScalar s) { fMinAlpha = s; } + hsScalar GetMinAlpha() { return fMinAlpha; } + + virtual void ProcessPreInterpShadeVerts(hsExpander& vList); + + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); + + virtual const char* GetLabel() const { return "hsSfxIntenseAlpha"; } + + virtual ProcType GetType() const { return kTypeIntenseAlpha; } + + CLASSNAME_REGISTER( hsSfxIntenseAlpha ); + GETINTERFACE_ANY( hsSfxIntenseAlpha, hsGRenderProcs ); + +}; + + + +#endif // hsSfxIntenseAlpha_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistFade.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistFade.cpp new file mode 100644 index 00000000..04424093 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistFade.cpp @@ -0,0 +1,304 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsMemory.h" +#include "hsSfxObjDistFade.h" +#include "hsStream.h" +//#include "hsG3DDevice.h" +#include "../plPipeline/plPipeline.h" +#include "../plGeometry/hsTriangle3.h" + +#include "../plIntersect/hsBounds.h" +#include "../plDrawable/plDrawable.h" + +static hsScalar globalScale = 1.f; + +hsSfxObjDistFade::hsSfxObjDistFade() +: fMinDist(0), fMaxDist(0), fTreeCnt(0) +{ +} + +hsSfxObjDistFade::~hsSfxObjDistFade() +{ +} + +hsScalar hsSfxObjDistFade::IOpacFromDist(hsScalar dist) +{ + if( dist <= fTable[0].fDistDel ) + return fTable[0].fOpacity; + + int i; + for( i = 0; (i < fTable.GetCount()) && (dist >= fTable[i].fDistDel); i++ ) + dist -= fTable[i].fDistDel; + + if( i >= fTable.GetCount() ) + return fTable[fTable.GetCount()-1].fOpacity; + + dist *= fTable[i-1].fDistNorm; + hsScalar opac0 = fTable[i-1].fOpacity; + hsScalar opac1 = fTable[i].fOpacity; + + return opac0 + dist * (opac1 - opac0); +} + +hsBool32 hsSfxObjDistFade::ISetOpac(plDrawable* refObj) +{ + hsPoint3 refPos; + if( fFlags & kByBoundsCenter ) + { + const hsBounds3Ext& bnd = refObj->GetWorldBounds(); + if( kBoundsNormal != bnd.GetType() ) + return true; + refPos = bnd.GetCenter(); + } + else + { + refObj->GetLocalToWorld().GetTranslate(&refPos); + } + + fFlags &= ~(kCulled | kNOP); + + hsPoint3 vPos; + if( GetObjectRef(1) ) + { + GetObjectRef(1)->GetLocalToWorld().GetTranslate(&vPos); + } + else + { + vPos = fPipeline->GetViewPositionWorld(); + } + + hsScalar dist = hsVector3(&vPos, &refPos).Magnitude(); + + if( (fFlags & kCullsBefore) + &&(dist <= fMinDist) ) + { + fFlags |= kCulled; + return false; + } + + if( (fFlags & kCullsBeyond) + &&(dist > fMaxDist) ) + { + fFlags |= kCulled; + return false; + } + + if( (fFlags & kIdleBefore) + &&(dist < fMinIdle) ) + fFlags |= kNOP; + else + if( (fFlags & kIdleBeyond) + &&(dist > fMaxIdle) ) + fFlags |= kNOP; + else + { + hsScalar opac = IOpacFromDist(dist); + hsColorRGBA col = fColorizer.GetCurrentColor(); + if( fColorizer.Alpharizing() ) + col.a *= opac; + else + col.a = opac; + fColorizer.PushColorize(col, fColorizer.Colorizing()); + + if( fFlags & kNoZTrans ) + { + if( !(fPipeline->GetMaterialOverrideOff(hsGMatState::kZ) & hsGMatState::kZNoZWrite) ) + { + fRestoreOver = fPipeline->PushMaterialOverride(hsGMatState::kZ, hsGMatState::kZNoZWrite, true); + } + } + } + + return true; +} + +hsBool32 hsSfxObjDistFade::BeginObject(plPipeline* pipe, plDrawable* obj) +{ + if( Inclusive() ) + return true; + + hsGRenderProcs::BeginObject(pipe, obj); + +#if 0 + // This is bogus. We may want to fade something, but not fade it out entirely. + if( !(fFlags & (kCullsBefore | kCullsBeyond | kIdleBefore | kIdleBeyond)) ) + return true; +#endif + + plDrawable* refObj = fFlags & kObjectRefs ? GetObjectRef(0) : nil; + if( !refObj ) + refObj = obj; + + return ISetOpac(refObj); +} + +hsBool32 hsSfxObjDistFade::BeginTree(plPipeline* pipe, plDrawable* obj) +{ + if( !Inclusive() ) + return true; + + if( fTreeCnt++ ) + return true; + + hsGRenderProcs::BeginTree(pipe, obj); + + plDrawable* refObj = fFlags & kObjectRefs ? GetObjectRef(0) : nil; + if( !refObj ) + refObj = obj; + + return ISetOpac(refObj); +} + +void hsSfxObjDistFade::EndObject() +{ + if( !Inclusive() ) + { + fPipeline->PopMaterialOverride(fRestoreOver, true); + } + hsGRenderProcs::EndObject(); +} + +void hsSfxObjDistFade::EndTree() +{ + if( Inclusive() ) + { + fPipeline->PopMaterialOverride(fRestoreOver, true); + + fTreeCnt--; + hsAssert(fTreeCnt >= 0, "Push/Pop tree problem"); + } + hsGRenderProcs::EndTree(); +} + +void hsSfxObjDistFade::MakeTable(float* distList, float* opacList, int num) +{ + fTable.Reset(); + if( !num ) + return; + + int i; + for( i = 0; i < num; i++ ) + { + hsSfxDfTableEntry* t = fTable.Append(); + t->fDistDel = distList[i]; + t->fOpacity = opacList[i]; + } + for( i = num-1; i > 0; i-- ) + fTable[i].fDistDel -= fTable[i-1].fDistDel; + for( i = 0; i < num-1; i++ ) + fTable[i].fDistNorm = hsScalarInvert(fTable[i+1].fDistDel); + fTable[num-1].fDistNorm = 0; + hsAssert(fTable.GetCount() == num, "Mismatch making table"); + + if( fTable[0].fOpacity <= 0 ) + fFlags |= kCullsBefore; + if( fTable[num-1].fOpacity <= 0 ) + fFlags |= kCullsBeyond; + if( fTable[0].fOpacity >= 1.f ) + fFlags |= kIdleBefore; + if( fTable[num-1].fOpacity >= 1.f ) + fFlags |= kIdleBeyond; + + int iMin; + for( iMin = 0; (iMin < fTable.GetCount())&&(fTable[iMin].fOpacity <= 0); iMin++ ); + fMinDist = fTable[0].fDistDel; + for( i = 1; i < iMin; i++ ) + fMinDist += fTable[i].fDistDel; + + for( iMin = 0; (iMin < fTable.GetCount())&&(fTable[iMin].fOpacity >= 1.f); iMin++ ); + fMinIdle = fTable[0].fDistDel; + for( i = 1; i < iMin; i++ ) + fMinIdle += fTable[i].fDistDel; + + int iMax; + for( iMax = fTable.GetCount()-1; (iMax >= 0)&&(fTable[iMax].fOpacity <= 0); iMax-- ); + if( ++iMax >= fTable.GetCount() ) + iMax = fTable.GetCount()-1; + fMaxDist = fTable[0].fDistDel; + for( i = 1; i <= iMax; i++ ) + fMaxDist += fTable[i].fDistDel; + + for( iMax = fTable.GetCount()-1; (iMax >= 0)&&(fTable[iMax].fOpacity >= 1.f); iMax-- ); + if( ++iMax >= fTable.GetCount() ) + iMax = fTable.GetCount()-1; + fMaxIdle = fTable[0].fDistDel; + for( i = 1; i <= iMax; i++ ) + fMaxIdle += fTable[i].fDistDel; +} + +void hsSfxObjDistFade::Read(hsStream* s) +{ + fTable.Reset(); + + fMinDist = s->ReadSwapScalar(); + fMaxDist = s->ReadSwapScalar(); + + if( fFlags & (kIdleBefore | kIdleBeyond) ) + { + fMinIdle = s->ReadSwapScalar(); + fMaxIdle = s->ReadSwapScalar(); + } + + Int32 cnt = s->ReadSwap32(); + + if( cnt ) + { + hsSfxDfTableEntry* arr = new hsSfxDfTableEntry[cnt]; + int i; + for( i = 0; i < cnt; i++ ) + { + arr[i].fDistDel = s->ReadSwapScalar(); + arr[i].fDistNorm = s->ReadSwapScalar(); + arr[i].fOpacity = s->ReadSwapScalar(); + } + + fTable.SetArray(arr, cnt); + } +} + +void hsSfxObjDistFade::Write(hsStream* s) +{ + s->WriteSwapScalar(fMinDist); + s->WriteSwapScalar(fMaxDist); + + if( fFlags & (kIdleBefore | kIdleBeyond) ) + { + s->WriteSwapScalar(fMinIdle); + s->WriteSwapScalar(fMaxIdle); + } + + s->WriteSwap32(fTable.GetCount()); + + for( fTable.First(); fTable.More(); fTable.Plus() ) + { + s->WriteSwapScalar(fTable.Current().fDistDel); + s->WriteSwapScalar(fTable.Current().fDistNorm); + s->WriteSwapScalar(fTable.Current().fOpacity); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistFade.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistFade.h new file mode 100644 index 00000000..eac23fd2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistFade.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsSfxObjDistFade_inc +#define hsSfxObjDistFade_inc + +#include "hsBiExpander.h" +#include "hsGRenderProcs.h" +#include "../plPipeline/hsGMatState.h" + +class hsSfxObjDistFade : public hsGRenderProcs { +public: + enum { + kCullsBefore = 0x10000, + kCullsBeyond = 0x20000, + + kDistFromView = 0x40000, + kDistFromTarget = 0x80000, + kDistAlongX = 0x100000, + kZOff = 0x200000, + kNoZTrans = 0x400000, + kByBoundsCenter = 0x800000, + + kPostInterp = 0x1000000, + + kIdleBefore = 0x2000000, + kIdleBeyond = 0x4000000, + + kZWasOff = 0x8000000 + }; + + struct hsSfxDfTableEntry { + hsScalar fDistDel; + hsScalar fDistNorm; + hsScalar fOpacity; + }; +protected: + + hsScalar fMinDist; + hsScalar fMaxDist; + + hsScalar fMinIdle; + hsScalar fMaxIdle; + + Int32 fTreeCnt; + + hsExpander fTable; + + hsGMatState fRestoreOver; + + hsBool32 ISetOpac(plDrawable* refObj); + hsScalar IOpacFromDist(hsScalar dist); +public: + hsSfxObjDistFade(); + virtual ~hsSfxObjDistFade(); + + virtual hsBool32 BeginTree(plPipeline* pipe, plDrawable* root); + virtual hsBool32 BeginObject(plPipeline* pipe, plDrawable* obj); + + virtual void EndObject(); + virtual void EndTree(); + + void MakeTable(float* distList, float* opacList, int num); // lists sorted from lowest cosine to highest + + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); + + virtual const char* GetLabel() const { return "hsSfxObjDistFade"; } + + virtual ProcType GetType() const { return kTypeObjDistFade; } + + CLASSNAME_REGISTER( hsSfxObjDistFade ); + GETINTERFACE_ANY( hsSfxObjDistFade, hsGRenderProcs ); + +}; + +#endif // hsSfxObjDistFade_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistShade.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistShade.cpp new file mode 100644 index 00000000..7417637b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistShade.cpp @@ -0,0 +1,201 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsMemory.h" +#include "hsSfxObjDistShade.h" +#include "hsStream.h" +#include "../plPipeline/plPipeline.h" +#include "../plGeometry/hsTriangle3.h" + +#include "../plIntersect/hsBounds.h" +#include "../plDrawable/plDrawable.h" + +static hsScalar globalScale = 1.f; + +hsSfxObjDistShade::hsSfxObjDistShade() +: fMinDist(0), fMaxDist(0), fTreeCnt(0) +{ +} + +hsSfxObjDistShade::~hsSfxObjDistShade() +{ +} + +hsScalar hsSfxObjDistShade::IShadeFromDist(hsScalar dist) +{ + if( dist <= fTable[0].fDistDel ) + return fTable[0].fShade; + + int i; + for( i = 0; (i < fTable.GetCount()) && (dist >= fTable[i].fDistDel); i++ ) + dist -= fTable[i].fDistDel; + + if( i >= fTable.GetCount() ) + return fTable[fTable.GetCount()-1].fShade; + + dist *= fTable[i-1].fDistNorm; + hsScalar shade0 = fTable[i-1].fShade; + hsScalar shade1 = fTable[i].fShade; + + return shade0 + dist * (shade1 - shade0); +} + +hsBool32 hsSfxObjDistShade::ISetShade(plDrawable* refObj) +{ + hsPoint3 refPos; + if( fFlags & kByBoundsCenter ) + { + const hsBounds3Ext& bnd = refObj->GetWorldBounds(); + if( kBoundsNormal != bnd.GetType() ) + return true; + refPos = bnd.GetCenter(); + } + else + { + refObj->GetLocalToWorld().GetTranslate(&refPos); + } + + hsPoint3 vPos; + if( GetObjectRef(1) ) + { + GetObjectRef(1)->GetLocalToWorld().GetTranslate(&vPos); + } + else + { + vPos = fPipeline->GetViewPositionWorld(); + } + + hsScalar dist = hsVector3(&vPos, &refPos).Magnitude(); + + hsScalar shade = IShadeFromDist(dist); + + hsColorRGBA col = fColorizer.GetCurrentColor(); + if( fColorizer.Colorizing() ) + { + col.r *= shade; + col.g *= shade; + col.b *= shade; + } + else + { + col.r = shade; + col.g = shade; + col.b = shade; + } + fColorizer.PushColorize(col, false); + + return true; +} + + + +hsBool32 hsSfxObjDistShade::BeginObject(plPipeline* pipe, plDrawable* obj) +{ + hsGRenderProcs::BeginObject(pipe, obj); + + plDrawable* refObj = fFlags & kObjectRefs ? GetObjectRef(0) : nil; + if( !refObj ) + refObj = obj; + + return ISetShade(refObj); +} + +void hsSfxObjDistShade::MakeTable(float* distList, float* shadeList, int num) +{ + fTable.Reset(); + if( !num ) + return; + + int i; + for( i = 0; i < num; i++ ) + { + hsSfxDfTableEntry* t = fTable.Append(); + t->fDistDel = distList[i]; + t->fShade = shadeList[i]; + } + for( i = num-1; i > 0; i-- ) + fTable[i].fDistDel -= fTable[i-1].fDistDel; + for( i = 0; i < num-1; i++ ) + fTable[i].fDistNorm = hsScalarInvert(fTable[i+1].fDistDel); + fTable[num-1].fDistNorm = 0; + hsAssert(fTable.GetCount() == num, "Mismatch making table"); + + int iMin; + for( iMin = 0; (iMin < fTable.GetCount())&&(fTable[iMin].fShade <= 0); iMin++ ); + fMinDist = fTable[0].fDistDel; + for( i = 1; i < iMin; i++ ) + fMinDist += fTable[i].fDistDel; + + int iMax; + for( iMax = fTable.GetCount()-1; (iMax >= 0)&&(fTable[iMax].fShade <= 0); iMax-- ); + if( ++iMax >= fTable.GetCount() ) + iMax = fTable.GetCount()-1; + fMaxDist = fTable[0].fDistDel; + for( i = 1; i <= iMax; i++ ) + fMaxDist += fTable[i].fDistDel; + +} + +void hsSfxObjDistShade::Read(hsStream* s) +{ + fTable.Reset(); + + fMinDist = s->ReadSwapScalar(); + fMaxDist = s->ReadSwapScalar(); + + Int32 cnt = s->ReadSwap32(); + + if( cnt ) + { + hsSfxDfTableEntry* arr = new hsSfxDfTableEntry[cnt]; + int i; + for( i = 0; i < cnt; i++ ) + { + arr[i].fDistDel = s->ReadSwapScalar(); + arr[i].fDistNorm = s->ReadSwapScalar(); + arr[i].fShade = s->ReadSwapScalar(); + } + + fTable.SetArray(arr, cnt); + } +} + +void hsSfxObjDistShade::Write(hsStream* s) +{ + s->WriteSwapScalar(fMinDist); + s->WriteSwapScalar(fMaxDist); + + s->WriteSwap32(fTable.GetCount()); + + for( fTable.First(); fTable.More(); fTable.Plus() ) + { + s->WriteSwapScalar(fTable.Current().fDistDel); + s->WriteSwapScalar(fTable.Current().fDistNorm); + s->WriteSwapScalar(fTable.Current().fShade); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistShade.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistShade.h new file mode 100644 index 00000000..65dcc763 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/hsSfxObjDistShade.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsSfxObjDistShade_inc +#define hsSfxObjDistShade_inc + +#include "hsBiExpander.h" +#include "hsGRenderProcs.h" + +class hsSfxObjDistShade : public hsGRenderProcs { +public: + enum { + kShadeConstant = 0x10000, + kByBoundsCenter = 0x800000, + + }; + + struct hsSfxDfTableEntry { + hsScalar fDistDel; + hsScalar fDistNorm; + hsScalar fShade; + }; +protected: + + hsScalar fMinDist; + hsScalar fMaxDist; + + hsScalar fConstShade; + + hsScalar fMinIdle; + hsScalar fMaxIdle; + + Int32 fTreeCnt; + + hsExpander fTable; + + hsBool32 ISetShade(plDrawable* refObj); + hsScalar IShadeFromDist(hsScalar dist); +public: + hsSfxObjDistShade(); + virtual ~hsSfxObjDistShade(); + + virtual hsBool32 BeginObject(plPipeline* pipe, plDrawable* obj); + + void MakeTable(float* distList, float* shadeList, int num); // lists sorted from lowest cosine to highest + + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); + + virtual const char* GetLabel() const { return "hsSfxObjDistShade"; } + + virtual ProcType GetType() const { return kTypeObjDistShade; } + + CLASSNAME_REGISTER( hsSfxObjDistShade ); + GETINTERFACE_ANY( hsSfxObjDistShade, hsGRenderProcs ); + +}; + +#endif // hsSfxObjDistShade_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/plGRenderProcsCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/plGRenderProcsCreatable.h new file mode 100644 index 00000000..61529e61 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGRenderProcs/plGRenderProcsCreatable.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plGRenderProcsCreatable_inc +#define plGRenderProcsCreatable_inc + +#include "../plResMgr/plCreator.h" + +#include "hsGRenderProcs.h" + +REGISTER_NONCREATABLE( hsGRenderProcs ); + +#include "hsSfxAngleFade.h" + +REGISTER_CREATABLE( hsSfxAngleFade ); + +#include "hsSfxDistFade.h" + +REGISTER_CREATABLE( hsSfxDistFade ); + +#include "hsSfxDistShade.h" + +REGISTER_CREATABLE( hsSfxDistShade ); + +#include "hsSfxGlobalShade.h" + +REGISTER_CREATABLE( hsSfxGlobalShade ); + +#include "hsSfxIntenseAlpha.h" + +REGISTER_CREATABLE( hsSfxIntenseAlpha ); + +#include "hsSfxObjDistFade.h" + +REGISTER_CREATABLE( hsSfxObjDistFade ); + +#include "hsSfxObjDistShade.h" + +REGISTER_CREATABLE( hsSfxObjDistShade ); + +#endif // plGRenderProcsCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsOscillator.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsOscillator.cpp new file mode 100644 index 00000000..dabca07e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsOscillator.cpp @@ -0,0 +1,747 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsStream.h" +#include "hsOscillator.h" +#include "../plMath/hsFastMath.h" +#include "hsGTriMesh.h" +#include "hsTriangle3.h" +#include "../plPipeline/plPipeline.h" + +#if defined(__MWERKS__) && !defined(HS_DEBUGGING) +#pragma optimization_level 0 +#endif + +static hsScalar rnd0_1() +{ + return hsScalar(rand()) / hsScalar(RAND_MAX); +} + +void hsWave::Save(hsStream* s, hsScalar secs) +{ + fWorldCenter.Write(s); + + s->WriteSwapScalar(fWorldFrequency); + + s->WriteSwapScalar(fWorldAmplitude); + + s->WriteSwapScalar(fPhase); + s->WriteSwapScalar(fRate); + + s->WriteSwapScalar(secs - fStartSecs); + + s->WriteSwapScalar(fSecsToLive); +} + +void hsWave::Load(hsStream* s, hsScalar secs) +{ + fWorldCenter.Read(s); + + fWorldFrequency = s->ReadSwapScalar(); + + fWorldAmplitude = s->ReadSwapScalar(); + + fPhase = s->ReadSwapScalar(); + fRate = s->ReadSwapScalar(); + + fStartSecs = s->ReadSwapScalar(); + fStartSecs = secs - fStartSecs; + + fSecsToLive = s->ReadSwapScalar(); +} + +void hsWave::Init(hsScalar secs, hsPoint3& center, hsScalar per, hsScalar amp, hsScalar rate, hsScalar life, hsBool32 attenOut) +{ + fStartSecs = secs; + fWorldCenter = center; + fWorldFrequency = hsScalarInvert(per); + fWorldAmplitude = amp; + fRate = rate; + fSecsToLive = life; + AttenuateOut(attenOut); +} + +hsBool32 hsWave::IsSpent(hsScalar secs) const +{ + return secs - fStartSecs > fSecsToLive; +} + +void hsWave::Accumulate(const hsPoint3& pos, const hsVector3& localZ, hsVector3& accum, hsVector3& accumNorm) const +{ + hsVector3 del(&pos, &fLocalCenter); + hsScalar dot = del.InnerProduct(localZ); + dot *= -2.f; + del += localZ * dot; + + hsScalar dist = del.MagnitudeSquared(); + dist = hsFastMath::InvSqrtAppr(dist); + del *= dist; + dist = hsScalarInvert(dist); + + hsScalar ampl = fLocalAmplitude; + if( fAttenuateOutScale > 0 ) + { + if( dist > fInnerRadius ) + { + if( dist > fOuterRadius ) + return; + ampl *= fOuterRadius - dist; + ampl *= fAttenuateOutScale; + } + } + + dist *= fLocalFrequency; + dist += fPhase; + + hsScalar s, c; + hsFastMath::SinCosAppr(dist, s, c); + + s *= ampl; + s += ampl; + c *= ampl * fLocalFrequency; + +// accum += s * localZ; + accum.fZ += s / localZ.fZ; + + hsVector3 norm; + norm = localZ; + norm += del * -c; + accumNorm += norm; + + return; +} + +void hsWave::Update(hsScalar secs, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + if( l2w.fFlags & hsMatrix44::kIsIdent ) + { + fLocalCenter = fWorldCenter; + fLocalFrequency = fWorldFrequency; + fLocalAmplitude = fWorldAmplitude; + } + else + { + hsVector3 ax; + ax.Set(w2l.fMap[0][2], w2l.fMap[1][2], w2l.fMap[2][2]); + hsScalar ooScale = ax.MagnitudeSquared(); + ooScale = hsFastMath::InvSqrtAppr(ooScale); + + fLocalCenter = w2l * fWorldCenter; + fLocalFrequency = fWorldFrequency * ooScale; + + hsScalar scale = 1.f / ooScale; + fLocalAmplitude = fWorldAmplitude * scale; + } + fLocalAmplitude *= AgeScale(secs); + + if( fAttenuateOutScale > 0 ) + { + fInnerRadius = fRate * (secs - fStartSecs) * hsScalarPI * 2.f; + fOuterRadius = fInnerRadius * (5.f/4.f); + fAttenuateOutScale = hsScalarInvert(fOuterRadius - fInnerRadius); + } + + fPhase = -(secs - fStartSecs) * fRate * hsScalarPI * 2.f; +} + +hsScalar hsWave::ScaledAmplitude(hsScalar secs) const +{ + return fWorldAmplitude * AgeScale(secs); +} + +hsScalar hsWave::AgeScale(hsScalar secs) const +{ + hsScalar age = secs - fStartSecs; + extern int dbgCurrentTest; + if( dbgCurrentTest ) + { + age *= 4.f; + age -= 2.f * fSecsToLive; + if( age < 0 ) + age = -age; + age -= fSecsToLive; + } + else + { + age *= 2.f; + age -= fSecsToLive; + if( age < 0 ) + age = -age; + } + hsScalar ageScale = 1.f - age / fSecsToLive; + if( ageScale < 0 ) + ageScale = 0; + else if( ageScale > 1.f ) + ageScale = 1.f; + return ageScale; +} + +hsOscillator::hsOscillator() +{ +} + +hsOscillator::~hsOscillator() +{ +} + +hsWave& hsOscillator::GetWeakestWave(hsScalar secs) +{ + hsAssert(!GetDisabled(), "Shouldn't be messing with disabled oscillator system"); + int weakest = 0; + hsScalar amp = fWaves[0].ScaledAmplitude(secs); + int i; + for( i = 0; i < fWaves.GetCount(); i++ ) + { + hsScalar tAmp = fWaves[i].ScaledAmplitude(secs); + if( tAmp < amp ) + { + weakest = i; + amp = tAmp; + } + } + return fWaves[weakest]; +} + +hsWave& hsOscillator::GetTempWave(hsScalar secs) +{ + int i; + for( i = 0; i < fTempWaves.GetCount(); i++ ) + { + if( fTempWaves[i].IsSpent(secs) ) + return fTempWaves[i]; + } + fTempWaves.Push(); + return fTempWaves[fTempWaves.GetCount()-1]; +} + +void hsOscillator::ISpawnWave(hsScalar secs, int i) +{ + hsPoint3 corner; + fWorldCenterBounds.GetCorner(&corner); + hsVector3 ax[3]; + fWorldCenterBounds.GetAxes(ax+0, ax+1, ax+2); + hsScalar r; + r = rnd0_1(); + ax[0] *= r; + corner += ax[0]; + r = rnd0_1(); + ax[1] *= r; + corner += ax[1]; + r = rnd0_1(); + ax[2] *= r; + corner += ax[2]; + + hsScalar per = fMinPeriod; + r = rnd0_1(); + hsScalar rr = r; + r *= fMaxPeriod - fMinPeriod; + per += r; + + hsScalar amp = fMinAmplitude; + r = rr * rnd0_1(); + r *= fMaxAmplitude - fMinAmplitude; + amp += r; + + hsScalar life = fMinLife; + r = rnd0_1(); + r *= fMaxLife - fMinLife; + life += r; + + hsScalar rate = fMinRate; + r = rnd0_1(); + r *= fMaxRate - fMinRate; + rate += r; + + fWaves[i].Init(secs, corner, per, amp, rate, life); + +} + +void hsOscillator::IUpdate(hsScalar secs, plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + if( GetDisabled() ) + return; + + fWorldCenter = pipe->GetViewPositionWorld(); + fWorldCenter.fZ = (fWorldCenterBounds.GetMins().fZ + fWorldCenterBounds.GetMaxs().fZ) * 0.5f; + fLocalCenter = w2l * fWorldCenter; + + fLocalToWorld = l2w; + fWorldToLocal = w2l; + + fLocalX.Set(w2l.fMap[0][0],w2l.fMap[1][0],w2l.fMap[2][0]); + fLocalX.Normalize(); + fLocalY.Set(w2l.fMap[0][1],w2l.fMap[1][1],w2l.fMap[2][1]); + fLocalY.Normalize(); + fLocalZ.Set(w2l.fMap[0][2],w2l.fMap[1][2],w2l.fMap[2][2]); + fLocalZ.Normalize(); + + hsVector3 ax; + hsScalar ooScale; + ax.Set(w2l.fMap[0][0], w2l.fMap[1][0], w2l.fMap[2][0]); + ooScale = ax.MagnitudeSquared(); + ooScale = hsFastMath::InvSqrtAppr(ooScale); + fLocalAttenScale.fX = fWorldAttenScale.fX * ooScale; + + ax.Set(w2l.fMap[0][1], w2l.fMap[1][1], w2l.fMap[2][1]); + ooScale = ax.MagnitudeSquared(); + ooScale = hsFastMath::InvSqrtAppr(ooScale); + fLocalAttenScale.fY = fWorldAttenScale.fY * ooScale; + + fLocalAttenScale.fZ = 0; + + int i; + for( i = 0; i < fWaves.GetCount(); i++ ) + { + if( fWaves[i].IsSpent(secs) ) + ISpawnWave(secs, i); + fWaves[i].Update(secs, l2w, w2l); + } + for( i = 0; i < fTempWaves.GetCount(); i++ ) + { + while( (i < fTempWaves.GetCount()) && fTempWaves[i].IsSpent(secs) ) + fTempWaves.Remove(i, 1); + if( i < fTempWaves.GetCount() ) + fTempWaves[i].Update(secs, l2w, w2l); + } +} + +hsScalar hsOscillator::IAttenuate(const hsPoint3& in) const +{ + const hsPoint3& cen = fLocalCenter; + hsVector3 del(&in, &cen); + + hsScalar atX = del.InnerProduct(fLocalX); + atX *= fLocalAttenScale.fX; + if( atX > 0 ) + atX = -atX; + atX += 1.f; + if( atX < 0 ) + atX = 0; + + hsScalar atY = del.InnerProduct(fLocalY); + atY *= fLocalAttenScale.fY; + if( atY > 0 ) + atY = -atY; + atY += 1.f; + if( atY < 0 ) + atY = 0; + + hsScalar at = atX * atY; + return at; +} + +void hsOscillator::AdjustWorldBounds(const hsMatrix44& l2w, const hsMatrix44& w2l, hsBounds3Ext& bnd) const +{ + if( GetDisabled() ) + return; + + hsVector3 adj; + adj.Set(0,1.f/fLocalZ.fZ,0); + adj = l2w * adj; + adj *= fMaxAmplitude * fWaves.GetCount(); + + bnd.Union(&adj); + adj = -adj; + bnd.Union(&adj); +} + +void hsOscillator::IPerterb(const hsPoint3& in, hsGVertex3& out) const +{ + hsPoint3 pos = in; + hsVector3 del(&pos, &fLocalCenter); + hsScalar dot = del.InnerProduct(fLocalZ); + pos += fLocalZ * -dot; + + hsVector3 accum; + hsVector3 accumNorm; + accum.Set(0,0,0); + accumNorm.Set(0,0,0); + int i; + for( i = 0; i < fWaves.GetCount(); i++ ) + { + fWaves[i].Accumulate(pos, fLocalZ, accum, accumNorm); + } + for( i = 0; i < fTempWaves.GetCount(); i++ ) + { + fTempWaves[i].Accumulate(pos, fLocalZ, accum, accumNorm); + } + hsScalar atten = IAttenuate(pos); + static int attenuating = 1; + if( attenuating ) // nuke me + accum *= atten; + out.fLocalPos = in + accum; + + hsScalar invNorm = hsFastMath::InvSqrtAppr(accumNorm.MagnitudeSquared()); + accumNorm *= invNorm; + out.fNormal = accumNorm; +} + +void hsOscillator::Read(hsStream* s) +{ + int n = s->ReadSwap32(); + SetNumWaves(n); + + fWorldAttenScale.Read(s); + fWorldCenterBounds.Read(s); + + fMinPeriod = s->ReadSwapScalar(); + fMaxPeriod = s->ReadSwapScalar(); + + fMinAmplitude = s->ReadSwapScalar(); + fMaxAmplitude = s->ReadSwapScalar(); + + fMinRate = s->ReadSwapScalar(); + fMaxRate = s->ReadSwapScalar(); + + fMinLife = s->ReadSwapScalar(); + fMaxLife = s->ReadSwapScalar(); + + int i; + for( i = 0; i < fWaves.GetCount(); i++ ) + fWaves[i].Kill(); + + fTempWaves.Reset(); +} + +void hsOscillator::Load(hsStream* s, hsScalar secs) +{ + Read(s); + + int i; + for( i = 0; i < fWaves.GetCount(); i++ ) + fWaves[i].Load(s, secs); + + fTempWaves.Reset(); +} + +void hsOscillator::Write(hsStream* s) +{ + s->WriteSwap32(fWaves.GetCount()); + + fWorldAttenScale.Write(s); + fWorldCenterBounds.Write(s); + + s->WriteSwapScalar(fMinPeriod); + s->WriteSwapScalar(fMaxPeriod); + + s->WriteSwapScalar(fMinAmplitude); + s->WriteSwapScalar(fMaxAmplitude); + + s->WriteSwapScalar(fMinRate); + s->WriteSwapScalar(fMaxRate); + + s->WriteSwapScalar(fMinLife); + s->WriteSwapScalar(fMaxLife); + +} + +void hsOscillator::Save(hsStream* s, hsScalar secs) +{ + Write(s); + + int i; + for( i = 0; i < fWaves.GetCount(); i++ ) + fWaves[i].Save(s, secs); +} + +void hsOscillator::SetNumWaves(int n) +{ + fWaves.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + fWaves[i].Kill(); +} + +void hsOscillator::Init(Int32 nParams, hsScalar* params) +{ +// NumWaves = 1 +// AttenScale = 2 +// WorldCenterBounds = 6 +// Period = 2 +// Amp = 2 +// Rate = 2 +// Life = 2 + + hsAssert(17 == nParams, "Parameter input mismatch"); + + SetNumWaves(int(*params++)); + + fWorldAttenScale.fX = *params++; + fWorldAttenScale.fY = *params++; + fWorldAttenScale.fZ = 0; + + hsPoint3 pt; + hsBounds3Ext bnd; + pt.fX = *params++; + pt.fY = *params++; + pt.fZ = *params++; + bnd.Reset(&pt); + pt.fX = *params++; + pt.fY = *params++; + pt.fZ = *params++; + bnd.Union(&pt); + SetWorldCenterBounds(bnd); + + SetPeriodRange(params[0], params[1]); + params += 2; + + SetAmplitudeRange(params[0], params[1]); + params += 2; + + SetRateRange(params[0], params[1]); + params += 2; + + SetLifeRange(params[0], params[1]); + + fTempWaves.Reset(); +} + + +#if 1 +hsGTriMesh* hsOscillator::MakeWaveMesh(int nSpokes, const hsPoint3& center, hsScalar minRad, hsScalar maxRad, hsScalar uRange, hsScalar vRange, hsScalar attenStartFrac, hsBool32 stitch) +{ + hsGTriMesh* triMesh = new hsGTriMesh; + + hsTArray radii; + hsScalar cRad = 0; + while( cRad < maxRad ) + { + // OOPS - for the half circle, this should be PI*R/n, not 2PI. Don't fix until we've corrected the callers. Or we might want to leave it like + // this anyway, since we're looking obliquely at these faces anyway, and this error stretches the side that perspective compresses. May + // want to make the unstitched version wrong in the same way. + hsScalar tRad = 2.f * hsScalarPI * cRad / nSpokes; + if( tRad < minRad ) + tRad = minRad; + cRad += tRad; + radii.Append(cRad); + } + + int nShell = radii.GetCount(); + + int nTris = stitch + ? 2 * nSpokes * (nShell-1) + nSpokes + : 2 * (nSpokes-1) * (nShell-1) + (nSpokes-1); + int nVerts = nSpokes * nShell + 1; + triMesh->AllocatePointers(nTris, nVerts, nVerts, nVerts); + triMesh->SetNumTriVertex(nVerts); + triMesh->SetNumPoints(nVerts); + triMesh->SetNumUvs(nVerts); + triMesh->SetHasColors(true); + + *triMesh->GetPoint(0) = center; + triMesh->GetNormal(0)->Set(0,1.f,0); + triMesh->GetColor(0)->Set(0,0,0,1.f); + triMesh->GetUvs(0)->fX = triMesh->GetUvs(0)->fY = triMesh->GetUvs(0)->fZ = 0; + + hsScalar iToRadians = stitch + ? 2.f * hsScalarPI / nSpokes + : hsScalarPI / nSpokes; + hsScalar attenStart = maxRad * attenStartFrac; + hsScalar attenEnd = maxRad; + hsScalar attenScale = hsScalarInvert(attenEnd - attenStart); + int i, j; + for( i = 0; i < nSpokes; i++ ) + { + hsScalar s = hsSine(i * iToRadians); + hsScalar c = hsCosine(i * iToRadians); + for( j = 0; j < nShell; j++ ) + { + hsAssert(1 + i*nShell + j < nVerts, "Going out of range on verts"); + hsGVertex3* vtx = triMesh->GetVertex(1 + i*nShell + j); + hsColorRGBA* col = triMesh->GetColor(1 + i*nShell + j); + hsGUv* uv = triMesh->GetUvs(1 + i*nShell + j); + + hsScalar x = c * radii[j]; + hsScalar y = s * radii[j]; + + hsScalar u = x / uRange; + hsScalar v = y / vRange; + + vtx->fLocalPos.fX = center.fX + x; + vtx->fLocalPos.fY = center.fY + y; + vtx->fLocalPos.fZ = 0.f; + + vtx->fNormal.Set(0,0,1.f); + + uv->fX = u; + uv->fY = v; + uv->fZ = 0.f; + + if( radii[j] > attenStart ) + { + hsScalar a = (attenEnd - radii[j]) * attenScale; + if( a < 0 ) + a = 0; + else if( a > 1.f ) + a = 1.f; + col->Set(0,0,0,a); + } + else + col->Set(0,0,0,1.f); + } + } + + int spokeEnd = stitch ? nSpokes : nSpokes-1; + int nextTri = 0; + for( i = 0; i < spokeEnd; i++ ) + { + hsTriangle3* tri = triMesh->GetTriFromPool(nextTri); + tri->Zero(); + tri->fOrigTri = tri; + triMesh->SetTriangle(nextTri++, tri); + + tri->fVert[0] = triMesh->GetTriVertex(0); + tri->fVert[0]->fVtx = triMesh->GetVertex(0); + tri->fVert[0]->SetNumUvChannels(1); + tri->fVert[0]->fUvChan[0] = triMesh->GetUvs(0); + tri->fVert[0]->fVtxColor = triMesh->GetColor(0); + + int iv0 = 1 + i * nShell; + int iv1 = i < nSpokes - 1 ? 1 + (i+1)*nShell : 1; + hsAssert((iv0 < nVerts)&&(iv1 < nVerts), "Out of range on triverts"); + + tri->fVert[1] = triMesh->GetTriVertex(iv0); + tri->fVert[1]->fVtx = triMesh->GetVertex(iv0); + tri->fVert[1]->SetNumUvChannels(1); + tri->fVert[1]->fUvChan[0] = triMesh->GetUvs(iv0); + tri->fVert[1]->fVtxColor = triMesh->GetColor(iv0); + + tri->fVert[2] = triMesh->GetTriVertex(iv1); + tri->fVert[2]->fVtx = triMesh->GetVertex(iv1); + tri->fVert[2]->SetNumUvChannels(1); + tri->fVert[2]->fUvChan[0] = triMesh->GetUvs(iv1); + tri->fVert[2]->fVtxColor = triMesh->GetColor(iv1); + + tri->fVert[0]->fFlags = hsGTriVertex::kHasPointers + | hsGTriVertex::kHasVertexUvs + | hsGTriVertex::kHasVertexColors; + tri->fVert[1]->fFlags = hsGTriVertex::kHasPointers + | hsGTriVertex::kHasVertexUvs + | hsGTriVertex::kHasVertexColors; + tri->fVert[2]->fFlags = hsGTriVertex::kHasPointers + | hsGTriVertex::kHasVertexUvs + | hsGTriVertex::kHasVertexColors; + + tri->fFlags |= hsTriangle3::kHasVertexPosNorms + | hsTriangle3::kHasVertexUvs + | hsTriangle3::kHasVertexColors + | hsTriangle3::kHasPointers; + + int iv2 = iv0 + 1; + int iv3 = iv1 + 1; + hsAssert((iv1 < nVerts)&&(iv2 < nVerts), "Out of range on triverts"); + for( j = 0; j < nShell-1; j++ ) + { + tri = triMesh->GetTriFromPool(nextTri); + tri->Zero(); + tri->fOrigTri = tri; + triMesh->SetTriangle(nextTri++, tri); + + tri->fVert[0] = triMesh->GetTriVertex(iv0); + tri->fVert[0]->fVtx = triMesh->GetVertex(iv0); + tri->fVert[0]->SetNumUvChannels(1); + tri->fVert[0]->fUvChan[0] = triMesh->GetUvs(iv0); + tri->fVert[0]->fVtxColor = triMesh->GetColor(iv0); + + tri->fVert[1] = triMesh->GetTriVertex(iv2); + tri->fVert[1]->fVtx = triMesh->GetVertex(iv2); + tri->fVert[1]->SetNumUvChannels(1); + tri->fVert[1]->fUvChan[1] = triMesh->GetUvs(iv2); + tri->fVert[1]->fVtxColor = triMesh->GetColor(iv2); + + tri->fVert[2] = triMesh->GetTriVertex(iv3); + tri->fVert[2]->fVtx = triMesh->GetVertex(iv3); + tri->fVert[2]->SetNumUvChannels(1); + tri->fVert[2]->fUvChan[0] = triMesh->GetUvs(iv3); + tri->fVert[2]->fVtxColor = triMesh->GetColor(iv3); + + tri->fVert[0]->fFlags = hsGTriVertex::kHasPointers + | hsGTriVertex::kHasVertexUvs + | hsGTriVertex::kHasVertexColors; + tri->fVert[1]->fFlags = hsGTriVertex::kHasPointers + | hsGTriVertex::kHasVertexUvs + | hsGTriVertex::kHasVertexColors; + tri->fVert[2]->fFlags = hsGTriVertex::kHasPointers + | hsGTriVertex::kHasVertexUvs + | hsGTriVertex::kHasVertexColors; + + tri->fFlags |= hsTriangle3::kHasVertexPosNorms + | hsTriangle3::kHasVertexUvs + | hsTriangle3::kHasVertexColors + | hsTriangle3::kHasPointers; + + tri = triMesh->GetTriFromPool(nextTri); + tri->Zero(); + tri->fOrigTri = tri; + triMesh->SetTriangle(nextTri++, tri); + + tri->fVert[0] = triMesh->GetTriVertex(iv0); + tri->fVert[0]->fVtx = triMesh->GetVertex(iv0); + tri->fVert[0]->SetNumUvChannels(1); + tri->fVert[0]->fUvChan[0] = triMesh->GetUvs(iv0); + tri->fVert[0]->fVtxColor = triMesh->GetColor(iv0); + + tri->fVert[1] = triMesh->GetTriVertex(iv3); + tri->fVert[1]->fVtx = triMesh->GetVertex(iv3); + tri->fVert[1]->SetNumUvChannels(1); + tri->fVert[1]->fUvChan[0] = triMesh->GetUvs(iv3); + tri->fVert[1]->fVtxColor = triMesh->GetColor(iv3); + + tri->fVert[2] = triMesh->GetTriVertex(iv1); + tri->fVert[2]->fVtx = triMesh->GetVertex(iv1); + tri->fVert[2]->SetNumUvChannels(1); + tri->fVert[2]->fUvChan[0] = triMesh->GetUvs(iv1); + tri->fVert[2]->fVtxColor = triMesh->GetColor(iv1); + + tri->fVert[0]->fFlags = hsGTriVertex::kHasPointers + | hsGTriVertex::kHasVertexUvs + | hsGTriVertex::kHasVertexColors; + tri->fVert[1]->fFlags = hsGTriVertex::kHasPointers + | hsGTriVertex::kHasVertexUvs + | hsGTriVertex::kHasVertexColors; + tri->fVert[2]->fFlags = hsGTriVertex::kHasPointers + | hsGTriVertex::kHasVertexUvs + | hsGTriVertex::kHasVertexColors; + + tri->fFlags |= hsTriangle3::kHasVertexPosNorms + | hsTriangle3::kHasVertexUvs + | hsTriangle3::kHasVertexColors + | hsTriangle3::kHasPointers; + + iv0++; + iv1++; + iv2++; + iv3++; + } + } + hsAssert(nextTri <= nTris, "Out of range on tris"); + + triMesh->StoreOrigPoints(); + + return triMesh; +} +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsOscillator.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsOscillator.h new file mode 100644 index 00000000..f67a84d8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsOscillator.h @@ -0,0 +1,164 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsOscillator_inc +#define hsOscillator_inc + +#include "hsPerterber.h" +#include "hsTemplates.h" + +#include "hsGeometry3.h" +#include "../plIntersect/hsBounds.h" + +class hsStream; +class plPipeline; + +class hsWave +{ +protected: + hsPoint3 fWorldCenter; + hsPoint3 fLocalCenter; + + hsScalar fWorldFrequency; // 1.0 / Period + hsScalar fLocalFrequency; + + hsScalar fWorldAmplitude; + hsScalar fLocalAmplitude; + + hsScalar fPhase; + hsScalar fRate; // how long a crest takes to reach next crest + + hsScalar fStartSecs; + hsScalar fSecsToLive; + + hsScalar fInnerRadius; + hsScalar fOuterRadius; + hsScalar fAttenuateOutScale; + + hsScalar AgeScale(hsScalar secs) const; + +public: + void Accumulate(const hsPoint3& pos, const hsVector3& localZ, hsVector3& accum, hsVector3& accumNorm) const; + hsScalar ScaledAmplitude(hsScalar secs) const; + + void Init(hsScalar secs, hsPoint3& center, hsScalar per, hsScalar amp, hsScalar rate, hsScalar life, hsBool32 attenOut=false); + + void Update(hsScalar secs, const hsMatrix44& l2w, const hsMatrix44& w2l); + hsBool32 IsSpent(hsScalar secs) const; + void Kill() { fStartSecs = fSecsToLive = 0; } + void AttenuateOut(hsBool32 on) { fAttenuateOutScale = (on ? 1.f : 0); } + hsBool32 GetAttenuateOut() { return fAttenuateOutScale > 0; } + + void Save(hsStream* s, hsScalar secs); + void Load(hsStream* s, hsScalar secs); +}; + +class hsOscillator : public hsPerterber +{ +protected: + hsTArray fWaves; + hsTArray fTempWaves; + + hsMatrix44 fLocalToWorld; + hsMatrix44 fWorldToLocal; + + hsPoint3 fWorldCenter; + hsPoint3 fLocalCenter; + + hsVector3 fWorldAttenScale; + hsVector3 fLocalAttenScale; + + hsBounds3Ext fWorldCenterBounds; + + hsScalar fMinPeriod; + hsScalar fMaxPeriod; + + hsScalar fMinAmplitude; + hsScalar fMaxAmplitude; + + hsScalar fMinRate; + hsScalar fMaxRate; + + hsScalar fMinLife; + hsScalar fMaxLife; + + hsVector3 fLocalX; + hsVector3 fLocalY; + hsVector3 fLocalZ; + + hsScalar IAttenuate(const hsPoint3& in) const; + void ISpawnWave(hsScalar secs, int i); + + virtual void IUpdate(hsScalar secs, plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual void IPerterb(const hsPoint3& in, hsGVertex3& out) const; +public: + hsOscillator(); + virtual ~hsOscillator(); + + virtual void AdjustWorldBounds(const hsMatrix44& l2w, const hsMatrix44& w2l, hsBounds3Ext& bnd) const; + + virtual UInt32 GetType() const { return kTypeOscillator; } + + // Don't call these, use base class LabelAndWrite() and CreateAndRead() + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); + + virtual void Load(hsStream* s, hsScalar secs); + virtual void Save(hsStream* s, hsScalar secs); + + void SetPeriodRange(hsScalar lo, hsScalar hi) { fMinPeriod = lo; fMaxPeriod = hi; } + void SetAmplitudeRange(hsScalar lo, hsScalar hi) { fMinAmplitude = lo; fMaxAmplitude = hi; } + void SetRateRange(hsScalar lo, hsScalar hi) { fMinRate = lo; fMaxRate = hi; } + void SetLifeRange(hsScalar lo, hsScalar hi) { fMinLife = lo; fMaxLife = hi; } + + hsScalar GetMinPeriod() const { return fMinPeriod; } + hsScalar GetMaxPeriod() const { return fMaxPeriod; } + hsScalar GetMinAmplitude() const { return fMinAmplitude; } + hsScalar GetMaxAmplitude() const { return fMaxAmplitude; } + hsScalar GetMinRate() const { return fMinRate; } + hsScalar GetMaxRate() const { return fMaxRate; } + hsScalar GetMinLife() const { return fMinLife; } + hsScalar GetMaxLife() const { return fMaxLife; } + + void SetWorldAttenScale(const hsVector3& s) { fWorldAttenScale = s; } + void SetWorldCenterBounds(const hsBounds3Ext& bnd) { fWorldCenterBounds = bnd; } + + const hsVector3& GetWorldAttenScale() const { return fWorldAttenScale; } + const hsBounds3Ext& GetWorldCenterBounds() const { return fWorldCenterBounds; } + + void SetNumWaves(int n); + UInt32 GetNumWaves() const { return fWaves.GetCount(); } + hsWave& GetWeakestWave(hsScalar secs); + hsWave& GetTempWave(hsScalar secs); + + virtual void Init(Int32 nParams, hsScalar* params); + + static hsGTriMesh* MakeWaveMesh(int nSpokes, const hsPoint3& center, hsScalar minRad, hsScalar maxRad, hsScalar uRange, hsScalar vRange, hsScalar attenStartFrac, hsBool32 stitch); + +}; + +#endif // hsOscillator_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsPerterber.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsPerterber.cpp new file mode 100644 index 00000000..212dac7f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsPerterber.cpp @@ -0,0 +1,334 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsStream.h" +#include "hsPerterber.h" +#include "hsOscillator.h" +#include "hsGMesh.h" +#if 0 // GET_RID_OF_SHAPE_LAYER_DEFER +#include "hsGShape3.h" +#include "hsGShape3MegaMesh.h" +#endif// GET_RID_OF_SHAPE_LAYER_DEFER +#include "../plResMgr/plKey.h" +#include "../plSurface/hsGMaterial.h" +#include "hsTimer.h" +#include "../plPipeline/plPipeline.h" + +hsBool32 hsPerterber::fDisabled = false; + +hsPerterber::hsPerterber() +{ +} + +hsPerterber::~hsPerterber() +{ +} + +void hsPerterber::IUpdate(hsScalar secs, plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l) +{ +} + +void hsPerterber::TimeStampAndSave(hsStream* s) +{ + hsScalar secs = hsTimer::GetSeconds(); + + hsKeyedObject::Save(s, nil); + + Save(s, secs); +} + +void hsPerterber::TimeStampAndLoad(hsStream* s) +{ + hsScalar secs = hsTimer::GetSeconds(); + + hsKeyedObject::Load(s, nil); + + Load(s, secs); +} + +void hsPerterber::LabelAndWrite(hsStream* s) +{ + s->WriteSwap32(GetType()); + Write(s); +} + +hsPerterber* hsPerterber::CreateAndRead(hsStream* s) +{ + hsPerterber* retVal = nil; + + UInt32 t = s->ReadSwap32(); + switch( t ) + { + case kTypeOscillator: + retVal = new hsOscillator; + break; + default: + hsAssert(false, "Unknown perterber type"); + return nil; + } + retVal->Read(s); + + return retVal; +} + +hsGMesh* hsPerterber::IGetMesh(hsGShape3* shape) +{ + hsGMesh* mesh = nil; +#if 0 // GET_RID_OF_SHAPE_LAYER_DEFER + if( shape->GetShapeType() == hsGShape3::kTypeTriMesh ) + { + hsGShape3TriMesh* shp = (hsGShape3TriMesh*)shape; + mesh = shp->GetMesh(); + +#if 0 // move to export + if( mesh->GetKey() && strstr(mesh->GetKey()->GetName(), "create") ) + { + + hsTArray matList; + shp->AppendMaterials(matList); + + hsGTriMesh* newMesh = hsOscillator::MakeWaveMesh(40, hsPoint3(0,0,0), 4.f, 75.f, 1200.f, 1200.f, 0.75f, false); + newMesh->SetMaterial(matList[0]); + hsRefCnt_SafeUnRef(matList[0]); + + shp->SetMesh(newMesh); + hsRefCnt_SafeUnRef(newMesh); + mesh = newMesh; + } + else if( mesh->GetKey() && strstr(mesh->GetKey()->GetName(), "destroy") ) + { + + hsTArray matList; + shp->AppendMaterials(matList); + + hsGTriMesh* newMesh = hsOscillator::MakeWaveMesh(50, hsPoint3(0,0,0), 1.5f, 30.f, 600.f, 600.f, 0.6f, true); + newMesh->SetMaterial(matList[0]); + hsRefCnt_SafeUnRef(matList[0]); + + shp->SetMesh(newMesh); + hsRefCnt_SafeUnRef(newMesh); + mesh = newMesh; + } + else +#endif // move to export + { + hsGTriMesh* triMesh = (hsGTriMesh*)shp->GetMesh(); + if( triMesh->GetTriangle(0)->fFlags & hsTriangle3::kHasFacePlane ) + triMesh->TrashPlanes(); + mesh = triMesh; + } + } + else if( shape->GetShapeType() == hsGShape3::kTypeMegaMesh ) + { + hsGShape3MegaMesh* mega = (hsGShape3MegaMesh*)shape; + hsGMegaMesh* megaMesh = (hsGMegaMesh*)mega->GetMegaMesh(); + hsGTriMesh* triMesh = (hsGTriMesh*)megaMesh->GetMesh(0); + if( triMesh->GetTriangle(0)->fFlags & hsTriangle3::kHasFacePlane ) + { + int iMesh; + for( iMesh = 0; iMesh < megaMesh->GetMeshCount(); iMesh++ ) + { + triMesh = (hsGTriMesh*)megaMesh->GetMesh(iMesh); + triMesh->TrashPlanes(); + } + } + mesh = mega->GetMegaMesh(); + } +#endif // GET_RID_OF_SHAPE_LAYER_DEFER + return mesh; +} + +void hsPerterber::Perterb(hsScalar secs, plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l, hsGShape3* shape) +{ + if( GetDisabled() ) + return; + + hsGMesh* mesh = IGetMesh(shape); + + IUpdate(secs, pipe, l2w, w2l); + + if( !mesh->HasOrigPoints() ) + mesh->StoreOrigPoints(); + int i; + for( i = 0; i < mesh->GetNumPoints(); i++ ) + { + IPerterb(*mesh->GetOrigPoint(i), *mesh->GetVertex(i)); + } +} + +const int kPertCount = 6; +const int kMaxPertParams = 32; + +struct hsPertDesc +{ + + char fName[256]; + UInt32 fType; + UInt32 fNumParams; + hsScalar fParams[kMaxPertParams]; +}; + +// NumWaves = 1 +// AttenScale = 2 +// WorldCenterBounds = 6 +// Period = 2 +// Amp = 2 +// Rate = 2 +// Life = 2 +// +hsPertDesc sPertTable[kPertCount] = +{ + { + "mystocean", + hsPerterber::kTypeOscillator, + 17, + { + 5.f, + 1.f/100.f, 1.f/100.f, + -100.f, 100.f, 0, 100.f, 150.f, 0, + 2.f, 5.f, + 0.5, 0.75f, + 0.68f, 0.68f, + 5.f, 10.f + } + }, + { + "stoneocean", + hsPerterber::kTypeOscillator, + 17, + { + 5.f, + 1.f/100.f, 1.f/100.f, + -100.f, 100.f, 0, 100.f, 150.f, 0, + 2.f, 5.f, + 0.5, 0.75f, + 0.68f, 0.68f, + 5.f, 10.f + } + }, + { + "seleniticocean", + hsPerterber::kTypeOscillator, + 17, + { + 5.f, + 1.f/100.f, 1.f/100.f, + -100.f, 100.f, 0, 100.f, 150.f, 0, + 2.f, 5.f, + 0.25, 0.45f, + 0.6f, 0.6f, + 5.f, 10.f + } + }, + { + "channelocean", + hsPerterber::kTypeOscillator, + 17, + { + 5.f, + 1.f/30.f, 1.f/30.f, + -100.f, -100.f, 0, 100.f, 100.f, 0, + 0.25f, 0.5f, + 0.1, 0.2f, + 0.4f, 0.8f, + 5.f, 10.f + } + }, + { + "mechocean", + hsPerterber::kTypeOscillator, + 17, + { + 5.f, + 1.f/100.f, 1.f/100.f, + -100.f, 100.f, 0, 100.f, 150.f, 0, + 2.f, 5.f, + 0.5, 0.4f, + 0.68f, 0.68f, + 5.f, 10.f + } + }, + { + "rimeocean", + hsPerterber::kTypeOscillator, + 17, + { + 5.f, + 1.f/100.f, 1.f/100.f, + -100.f, 100.f, 0, 100.f, 150.f, 0, + 2.f, 5.f, + 0.5, 0.75, + 0.68f, 0.68f, + 5.f, 10.f + } + } +}; + + + +#if 0 // Used Registry...need to change paulg +void hsPerterber::InitSystem(hsRegistry* reg) +{ + if( GetDisabled() ) + return; + + int i; + for( i = 0; i < kPertCount; i++ ) + { + switch( sPertTable[i].fType ) + { + case kTypeOscillator: + { + hsOscillator* oscar = new hsOscillator; + oscar->Init(sPertTable[i].fNumParams, sPertTable[i].fParams); + +#ifdef PAULFIX + oscar->Register(reg, sPertTable[i].fName, 0, true); +#endif + } + break; + default: + hsAssert(false, "Unknown perterber type"); + break; + } + } +} + +void hsPerterber::Shutdown(hsRegistry* reg) +{ +#ifdef PAULFIX + int i; + + for( i = 0; i < reg->GetNumKeys(); i++ ) + { + hsPerterber* pert = (hsPerterber*)(reg->GetKey(i)->GetObjectPtr()); + delete pert; + } +#endif +} + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsPerterber.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsPerterber.h new file mode 100644 index 00000000..806c42c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/hsPerterber.h @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsPerterber_inc +#define hsPerterber_inc + +#include "../plResMgr/hsKeyedObject.h" +#include "hsGeometry3.h" + +class hsStream; +class hsGShape3; +class hsBounds3Ext; +class hsGMesh; +class plPipeline; +struct hsMatrix44; +struct hsGVertex3; + +class hsPerterber : public hsKeyedObject +{ +public: + enum { + kTypeUndefined = 0x0, + kTypeOscillator = 0x1 + }; +protected: + static hsBool32 fDisabled; + + virtual void IUpdate(hsScalar secs, plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual void IPerterb(const hsPoint3& in, hsGVertex3& out) const = 0; + + hsGMesh* IGetMesh(hsGShape3* shape); +public: + hsPerterber(); + virtual ~hsPerterber(); + + static void SetDisabled(hsBool32 on) { fDisabled = on; } + static void ToggleDisabled() { fDisabled = !fDisabled; } + static hsBool32 GetDisabled() { return fDisabled; } + + virtual void Perterb(hsScalar secs, plPipeline* pipe, const hsMatrix44& l2w, const hsMatrix44& w2l, hsGShape3* shape); + + virtual void AdjustWorldBounds(const hsMatrix44& l2w, const hsMatrix44& w2l, hsBounds3Ext& bnd) const = 0; + + virtual UInt32 GetType() const = 0; + + virtual void Write(hsStream* s) = 0; + virtual void Read(hsStream* s) = 0; + + virtual void Save(hsStream* s, hsScalar secs) = 0; + virtual void Load(hsStream* s, hsScalar secs) = 0; + + void TimeStampAndSave(hsStream* s); + void TimeStampAndLoad(hsStream* s); + + void LabelAndWrite(hsStream* s); + static hsPerterber* CreateAndRead(hsStream* s); + + virtual void Init(Int32 nParams, hsScalar* params) = 0; + +#if 0 // Used Registry...need to change paulg + static void InitSystem(plResMgr* reg); + + static void Shutdown(plResMgr* reg); +#endif +}; + +#endif hsPerterber_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/plGeometryCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/plGeometryCreatable.h new file mode 100644 index 00000000..033b29d1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plGeometry/plGeometryCreatable.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plGeometryCreatable_inc +#define plGeometryCreatable_inc + +#include "../pnFactory/plCreator.h" + +/* Taken out 6.12.2001 mcn - You can remove this file entirely if you wish, I just left + it in in case we wanted to use it in the future... + +#include "hsGVertexPool.h" + +REGISTER_CREATABLE( hsGVertexPool ); + + +#include "hsGMesh.h" + +REGISTER_NONCREATABLE( hsGMesh ); + +#include "hsGTriMesh.h" + +REGISTER_CREATABLE( hsGTriMesh ); + + +#include "hsGMegaMesh.h" + +REGISTER_CREATABLE( hsGMegaMesh ); +*/ + +#endif plGeometryCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plAvatarInputInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plAvatarInputInterface.cpp new file mode 100644 index 00000000..78f05eb4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plAvatarInputInterface.cpp @@ -0,0 +1,1122 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plAvatarInputInterface // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifdef PLASMA_EXTERNAL_RELEASE +//#define LIMIT_VOICE_CHAT 1 +#endif + +#include "hsConfig.h" +#include "hsWindows.h" +#include "hsTypes.h" +#include "plAvatarInputInterface.h" + +#include "../pnInputCore/plKeyMap.h" +#include "../plMessage/plInputEventMsg.h" + +#include "plInputInterfaceMgr.h" +#include "plInputManager.h" +#include "plInputDevice.h" + +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plProxyDrawMsg.h" +#include "../pnMessage/plCmdIfaceModMsg.h" + +// DEHACK +// used to run debug drawing stuff only; should never be checked in with this enabled + #if 0 +#include "../FeatureLib/pfCamera/plVirtualCam.h" +#include "../plDrawable/plDrawableSpans.h" + #endif + +#include "../plAudio/plVoiceChat.h" +#include "plInputDevice.h" +#include "plInputManager.h" +#include "hsResMgr.h" +#include "plgDispatch.h" + +#include "hsConfig.h" +#include "hsMatrix44.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +#include "../pnNetCommon/plNetApp.h" + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plAvatarInputInterface *plAvatarInputInterface::fInstance = nil; + +plAvatarInputInterface::plAvatarInputInterface() +{ + fInstance = this; + fMouseDisabled = false; + fCurrentCursor = kCursorUp; + fCursorOpacity = 1.f; + fCursorTimeout = 0; + fCursorFadeDelay = 3.f; + f3rdPerson = true; + fInputMap = nil; + ISetBasicMode(); // Must be after 3rdPerson and fInputMap are set. + SetEnabled( true ); // Always enabled + + // Add our control codes to our control map. Do NOT add the key bindings yet. + // Note: HERE is where you specify the actions for each command, i.e. net propagate and so forth. + // This part basically declares us master of the bindings for these commands. + + // IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO + // RestoreDefaultKeyMappings()!!!! + +#ifndef LIMIT_VOICE_CHAT + // only allow mapping of 'PushToTalk in online versions' + fControlMap->AddCode( S_PUSH_TO_TALK, kControlFlagNormal | kControlFlagNoRepeat ); +#endif + fControlMap->AddCode( S_SET_FIRST_PERSON_MODE, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_EXIT_MODE, kControlFlagNormal | kControlFlagNoRepeat ); + + fControlMap->AddCode( B_CAMERA_ZOOM_IN, kControlFlagNormal ); + fControlMap->AddCode( B_CAMERA_ZOOM_OUT, kControlFlagNormal ); + + fControlMap->AddCode( B_CONTROL_MODIFIER_FAST, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_MODIFIER_STRAFE, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_MOVE_FORWARD, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_MOVE_BACKWARD, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_ROTATE_LEFT, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_ROTATE_RIGHT, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_STRAFE_LEFT, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_STRAFE_RIGHT, kControlFlagNormal | kControlFlagNoRepeat ); + + + fControlMap->AddCode( B_CONTROL_ALWAYS_RUN, kControlFlagToggle | kControlFlagUpEvent | kControlFlagNoRepeat ); + + + fControlMap->AddCode( B_CONTROL_JUMP, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_DIVE, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_IGNORE_AVATARS, kControlFlagNormal | kControlFlagNoRepeat ); + + fControlMap->AddConsoleCommand( "Game.EnterChatMode" ); + fControlMap->AddConsoleCommand( "Game.Emote.wave" ); + fControlMap->AddConsoleCommand( "Game.Emote.laugh" ); + fControlMap->AddConsoleCommand( "Game.Emote.clap" ); + fControlMap->AddConsoleCommand( "Game.Emote.dance" ); + fControlMap->AddConsoleCommand( "Game.Emote.talk" ); + fControlMap->AddConsoleCommand( "Game.Emote.sneeze" ); + fControlMap->AddConsoleCommand( "Game.Emote.sit" ); + fControlMap->AddConsoleCommand( "Keyboard.ResetBindings" ); + + fControlMap->AddConsoleCommand( "Game.KIOpenKI" ); + fControlMap->AddConsoleCommand( "Game.KIHelp" ); + fControlMap->AddConsoleCommand( "Game.KICreateMarker" ); + fControlMap->AddConsoleCommand( "Game.KICreateMarkerFolder" ); + fControlMap->AddConsoleCommand( "Game.KIOpenYeeshaBook" ); + fControlMap->AddConsoleCommand( "Game.KIToggleMini" ); + fControlMap->AddConsoleCommand( "Game.KIPutAway" ); + fControlMap->AddConsoleCommand( "Game.KIChatPageUp" ); + fControlMap->AddConsoleCommand( "Game.KIChatPageDown" ); + fControlMap->AddConsoleCommand( "Game.KIChatToStart" ); + fControlMap->AddConsoleCommand( "Game.KIChatToEnd" ); + fControlMap->AddConsoleCommand( "Game.KIUpSizeFont" ); + fControlMap->AddConsoleCommand( "Game.KIDownSizeFont" ); + fControlMap->AddConsoleCommand( "Game.KITakePicture" ); + fControlMap->AddConsoleCommand( "Game.KICreateJournal" ); + +#ifndef PLASMA_EXTERNAL_RELEASE + fControlMap->AddCode( B_CONTROL_TOGGLE_PHYSICAL, kControlFlagDownEvent | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_MOVE_UP, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CONTROL_MOVE_DOWN, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_TOGGLE_DRIVE_MODE, kControlFlagDownEvent | kControlFlagNoRepeat ); + fControlMap->AddConsoleCommand( "NextStatusLog" ); +#endif + + // IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO + // RestoreDefaultKeyMappings()!!!! +} + +plAvatarInputInterface::~plAvatarInputInterface() +{ + delete fInputMap; +} + +//// Init/Shutdown /////////////////////////////////////////////////////////// + +void plAvatarInputInterface::Init( plInputInterfaceMgr *manager ) +{ + plInputInterface::Init( manager ); +} + +void plAvatarInputInterface::Shutdown( void ) +{ +} + +void plAvatarInputInterface::CameraInThirdPerson(hsBool state) +{ + if (state != f3rdPerson) + { + f3rdPerson = state; + + if (fInputMap->IsBasic()) + { + ISetBasicMode(); + } + } +} + +void plAvatarInputInterface::IDeactivateCommand(plMouseInfo *info) +{ + if (IHasControlFlag(info->fCode) && !(info->fControlFlags & (kControlFlagNoDeactivate | kControlFlagToggle))) + { + // The mapping is currently on, it's ok to deactivate, and it's not a toggle command + plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fNetPropagateToPlayers = info->fControlFlags & kControlFlagNetPropagate; + pCmd->fControlActivated = false; + pCmd->fControlCode = info->fCode; + IClearControlFlag(pCmd->fControlCode); + fMessageQueue->Append(pCmd); + } +} + +//// IChangeInputMaps //////////////////////////////////////////////////////// + +void plAvatarInputInterface::IChangeInputMaps( plAvatarInputMap *newMap ) +{ + newMap->fButtonState = fInputMap ? fInputMap->fButtonState : 0; + + if (fInputMap) + { + int i; + for (i = 0; i < fInputMap->fMouseMap->fMap.GetCount(); i++) + { + plMouseInfo *info = fInputMap->fMouseMap->fMap[i]; + IDeactivateCommand(info); + } + delete fInputMap; + } + + fInputMap = newMap; +// fInputMap->fButtonState = 0; +// Reset(); +} + +void plAvatarInputInterface::ISetSuspendMovementMode() +{ + IChangeInputMaps(TRACKED_NEW plSuspendedMovementMap()); + fCurrentCursor = kCursorUp; +} + +void plAvatarInputInterface::ISetLadderMap() +{ + IChangeInputMaps(TRACKED_NEW plLadderControlMap()); + fCurrentCursor = kCursorUp; +} + + +void plAvatarInputInterface::ISetPreLadderMap() +{ + IChangeInputMaps(TRACKED_NEW plLadderMountMap()); + fCurrentCursor = kCursorUp; +} + +void plAvatarInputInterface::ISetPostLadderMap() +{ + IChangeInputMaps(TRACKED_NEW plLadderDismountMap()); + fCurrentCursor = kCursorUp; +} + +void plAvatarInputInterface::ISetBasicMode() +{ + plAvatarInputMap *map; + if (!f3rdPerson) + map = TRACKED_NEW plBasicFirstPersonControlMap(); + else + map = TRACKED_NEW plBasicThirdPersonControlMap(); + + IChangeInputMaps(map); + fCurrentCursor = kCursorUp; +} + +void plAvatarInputInterface::ISetMouseWalkMode(ControlEventCode code) +{ + if (code == S_SET_WALK_BACK_MODE) + IChangeInputMaps(TRACKED_NEW pl3rdWalkBackwardMap()); + else if (code == S_SET_WALK_BACK_LB_MODE) + IChangeInputMaps(TRACKED_NEW pl3rdWalkBackwardLBMap()); + else + IChangeInputMaps(TRACKED_NEW pl3rdWalkForwardMap()); + + fCurrentCursor = kCursorHidden; +} + +//// ClearKeyMap /////////////////////////////////////////////// +void plAvatarInputInterface::ClearKeyMap() +{ + // Note: we might be clearing our key bindings, but we still want to be owners of the commands, + if( fControlMap != nil ) + { + fControlMap->UnmapAllBindings(); + + // Still want this one tho + fControlMap->BindKeyToConsoleCmd( plCtrlShiftKeyCombo( KEY_0 ), "Keyboard.ResetBindings" ); + } +} + +//// RestoreDefaultKeyMappings /////////////////////////////////////////////// + +void plAvatarInputInterface::RestoreDefaultKeyMappings( void ) +{ + if( fControlMap == nil ) + return; + + fControlMap->UnmapAllBindings(); + +#ifndef LIMIT_VOICE_CHAT + fControlMap->BindKey( KEY_TAB, S_PUSH_TO_TALK ); +#endif + fControlMap->BindKey( KEY_F1, S_SET_FIRST_PERSON_MODE ); + fControlMap->BindKey( plCtrlKeyCombo( KEY_F ), S_SET_FIRST_PERSON_MODE ); + fControlMap->BindKey( KEY_BACKSPACE, B_CONTROL_EXIT_MODE ); + fControlMap->BindKey( KEY_ESCAPE, B_CONTROL_EXIT_MODE ); + + fControlMap->BindKey( KEY_NUMPAD_ADD, B_CAMERA_ZOOM_IN ); + fControlMap->BindKey( KEY_NUMPAD_SUBTRACT, B_CAMERA_ZOOM_OUT ); + + fControlMap->BindKey( KEY_SHIFT, B_CONTROL_MODIFIER_FAST ); + fControlMap->BindKey( KEY_Z, B_CONTROL_MODIFIER_STRAFE ); + fControlMap->BindKey( KEY_UP, B_CONTROL_MOVE_FORWARD ); + fControlMap->BindKey( KEY_DOWN, B_CONTROL_MOVE_BACKWARD ); + fControlMap->BindKey( KEY_LEFT, B_CONTROL_ROTATE_LEFT ); + fControlMap->BindKey( KEY_RIGHT, B_CONTROL_ROTATE_RIGHT ); + fControlMap->BindKey( KEY_COMMA, B_CONTROL_STRAFE_LEFT ); + fControlMap->BindKey( KEY_PERIOD, B_CONTROL_STRAFE_RIGHT ); + +// This is now hard-coded to capslock +// fControlMap->BindKey( KEY_CAPSLOCK, B_CONTROL_ALWAYS_RUN ); + + fControlMap->BindKey( KEY_SPACE, B_CONTROL_JUMP ); +// fControlMap->BindKey( KEY_D, B_CONTROL_DIVE ); + fControlMap->BindKey( KEY_DELETE, B_CONTROL_IGNORE_AVATARS ); + + fControlMap->BindKeyToConsoleCmd( plCtrlKeyCombo( KEY_1 ), "Game.Emote.wave" ); + fControlMap->BindKeyToConsoleCmd( plCtrlKeyCombo( KEY_2 ), "Game.Emote.laugh" ); + fControlMap->BindKeyToConsoleCmd( plCtrlKeyCombo( KEY_3 ), "Game.Emote.clap" ); + fControlMap->BindKeyToConsoleCmd( plCtrlKeyCombo( KEY_4 ), "Game.Emote.dance" ); + fControlMap->BindKeyToConsoleCmd( plCtrlKeyCombo( KEY_5 ), "Game.Emote.talk" ); + fControlMap->BindKeyToConsoleCmd( plCtrlKeyCombo( KEY_6 ), "Game.Emote.sneeze" ); + fControlMap->BindKeyToConsoleCmd( plCtrlKeyCombo( KEY_7 ), "Game.Emote.sit" ); + + fControlMap->BindKeyToConsoleCmd( plCtrlShiftKeyCombo( KEY_0 ), "Keyboard.ResetBindings" ); + + // KI shortcut keyboard commands + fControlMap->BindKeyToConsoleCmd( KEY_F2, "Game.KIOpenKI" ); + fControlMap->BindKeyToConsoleCmd( KEY_F3, "Game.KIOpenYeeshaBook" ); + fControlMap->BindKeyToConsoleCmd( KEY_F4, "Game.KIHelp" ); + fControlMap->BindKeyToConsoleCmd( plCtrlKeyCombo( KEY_HOME ), "Game.KIToggleMini" ); + fControlMap->BindKeyToConsoleCmd( plCtrlKeyCombo( KEY_END ), "Game.KIPutAway" ); + fControlMap->BindKeyToConsoleCmd( KEY_PAGEUP, "Game.KIChatPageUp" ); + fControlMap->BindKeyToConsoleCmd( KEY_PAGEDOWN, "Game.KIChatPageDown" ); + fControlMap->BindKeyToConsoleCmd( KEY_HOME, "Game.KIChatToStart" ); + fControlMap->BindKeyToConsoleCmd( KEY_END, "Game.KIChatToEnd" ); + fControlMap->BindKeyToConsoleCmd( plCtrlKeyCombo( KEY_NUMPAD_ADD ), "Game.KIUpSizeFont" ); + fControlMap->BindKeyToConsoleCmd( plCtrlKeyCombo( KEY_NUMPAD_SUBTRACT ), "Game.KIDownSizeFont" ); + fControlMap->BindKeyToConsoleCmd( KEY_F5, "Game.KITakePicture" ); + fControlMap->BindKeyToConsoleCmd( KEY_F6, "Game.KICreateJournal" ); + fControlMap->BindKeyToConsoleCmd( KEY_F7, "Game.KICreateMarker" ); + fControlMap->BindKeyToConsoleCmd( KEY_F8, "Game.KICreateMarkerFolder" ); + +#ifndef PLASMA_EXTERNAL_RELEASE + fControlMap->BindKey( plShiftKeyCombo( KEY_P ), B_CONTROL_TOGGLE_PHYSICAL ); + fControlMap->BindKey( KEY_U, B_CONTROL_MOVE_UP ); + fControlMap->BindKey( KEY_H, B_CONTROL_MOVE_DOWN ); + fControlMap->BindKey( plShiftKeyCombo( KEY_C ), B_TOGGLE_DRIVE_MODE ); + + fControlMap->BindKeyToConsoleCmd( KEY_L, "NextStatusLog" ); +#endif +} + +void plAvatarInputInterface::SetLadderMode() +{ + ISetPreLadderMap(); +} + +void plAvatarInputInterface::ClearLadderMode() +{ + ISetBasicMode(); +} + +void plAvatarInputInterface::SuspendMouseMovement() +{ + ISetSuspendMovementMode(); +} + +void plAvatarInputInterface::EnableMouseMovement() +{ + ISetBasicMode(); +} + +void plAvatarInputInterface::EnableJump(hsBool val) +{ + EnableControl(val, B_CONTROL_JUMP); +} + +void plAvatarInputInterface::EnableForwardMovement(hsBool val) +{ + EnableControl(val, B_CONTROL_MOVE_FORWARD); +} + +void plAvatarInputInterface::EnableControl(hsBool val, ControlEventCode code) +{ + if (val) + IEnableControl(code); + else + IDisableControl(code); +} + +void plAvatarInputInterface::ForceAlwaysRun(hsBool val) +{ + plCtrlCmd *pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fControlCode = B_CONTROL_ALWAYS_RUN; + pCmd->fControlActivated = val; + pCmd->fNetPropagateToPlayers = false; + + fMessageQueue->Append( pCmd ); +} + +//// IEval /////////////////////////////////////////////////////////////////// +// Gets called once per IUpdate(), just like normal IEval()s + +hsBool plAvatarInputInterface::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + fCursorTimeout += del; + if( fCursorTimeout > fCursorFadeDelay ) + { + if( fCursorTimeout > fCursorFadeDelay + 2.f ) + fCursorOpacity = 0.f; + else + fCursorOpacity = 1.f - ( ( fCursorTimeout - fCursorFadeDelay ) / 2.f ); + } + else + fCursorOpacity = 1.f; + + return true; +} + +//// IHandleCtrlCmd ////////////////////////////////////////////////////////// + +hsBool plAvatarInputInterface::IHandleCtrlCmd( plCtrlCmd *cmd ) +{ + switch( cmd->fControlCode ) + { + case S_SET_CURSOR_UPWARD: + if( cmd->fControlActivated ) + fCurrentCursor = kCursorUpward; + return true; + case S_SET_CURSOR_UP: + if( cmd->fControlActivated ) + fCurrentCursor = kCursorUp; + return true; + case S_SET_CURSOR_DOWN: + if( cmd->fControlActivated ) + fCurrentCursor = kCursorDown; + return true; + case S_SET_CURSOR_RIGHT: + if( cmd->fControlActivated ) + fCurrentCursor = kCursorRight; + return true; + case S_SET_CURSOR_LEFT: + if( cmd->fControlActivated ) + fCurrentCursor = kCursorLeft; + return true; + case S_SET_CURSOR_HIDDEN: + if( cmd->fControlActivated ) + fCurrentCursor = kCursorHidden; + else + fCurrentCursor = kCursorUp; + case S_SET_LADDER_CONTROL: + if( cmd->fControlActivated ) + ISetLadderMap(); + return true; +#if 0 + case S_SET_FIRST_PERSON_MODE: + if( cmd->fControlActivated ) + IChangeInputMaps( TRACKED_NEW plFirstPersonControlMap() ); + return true; +#endif + case S_SET_BASIC_MODE: + if( cmd->fControlActivated ) + { + ISetBasicMode(); +#if 0 +plProxyDrawMsg* Dmsg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kCamera | plProxyDrawMsg::kDestroy); +plgDispatch::MsgSend(Dmsg); +plVirtualCam::Instance()->GetPipeline()->SetDrawableTypeMask(plVirtualCam::Instance()->GetPipeline()->GetDrawableTypeMask() & ~plDrawableSpans::kCameraProxy); +#endif + } + return true; + + + case S_SET_WALK_MODE: + + if( cmd->fControlActivated ) + { + hsBool abort = false; + for (int i = 0; i < fMessageQueue->GetCount(); i++) + { + if ((*fMessageQueue)[i]->fControlCode == S_SET_WALK_MODE && !(*fMessageQueue)[i]->fControlActivated) + { + abort = true; +#if 0 + + plProxyDrawMsg* Dmsg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kCamera | plProxyDrawMsg::kDestroy); + plgDispatch::MsgSend(Dmsg); + plVirtualCam::Instance()->GetPipeline()->SetDrawableTypeMask(plVirtualCam::Instance()->GetPipeline()->GetDrawableTypeMask() & ~plDrawableSpans::kCameraProxy); +#endif + break; + } + } + if (abort) + return true; + ISetMouseWalkMode(S_SET_WALK_MODE); + } + return true; + + case S_SET_WALK_BACK_MODE: + + if( cmd->fControlActivated ) + { + hsBool abort = false; + for (int i = 0; i < fMessageQueue->GetCount(); i++) + { + if ((*fMessageQueue)[i]->fControlCode == S_SET_WALK_BACK_MODE && !(*fMessageQueue)[i]->fControlActivated) + { + abort = true; +#if 0 + + plProxyDrawMsg* Dmsg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kCamera | plProxyDrawMsg::kDestroy); + plgDispatch::MsgSend(Dmsg); + plVirtualCam::Instance()->GetPipeline()->SetDrawableTypeMask(plVirtualCam::Instance()->GetPipeline()->GetDrawableTypeMask() & ~plDrawableSpans::kCameraProxy); +#endif + break; + } + } + if (abort) + return true; + ISetMouseWalkMode(S_SET_WALK_BACK_MODE); + } + return true; + + case S_SET_WALK_BACK_LB_MODE: + + if( cmd->fControlActivated ) + { + hsBool abort = false; + for (int i = 0; i < fMessageQueue->GetCount(); i++) + { + if ((*fMessageQueue)[i]->fControlCode == S_SET_WALK_BACK_MODE && !(*fMessageQueue)[i]->fControlActivated) + { + abort = true; +#if 0 + + plProxyDrawMsg* Dmsg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kCamera | plProxyDrawMsg::kDestroy); + plgDispatch::MsgSend(Dmsg); + plVirtualCam::Instance()->GetPipeline()->SetDrawableTypeMask(plVirtualCam::Instance()->GetPipeline()->GetDrawableTypeMask() & ~plDrawableSpans::kCameraProxy); +#endif + break; + } + } + if (abort) + return true; + ISetMouseWalkMode(S_SET_WALK_BACK_LB_MODE); + } + return true; + + case S_INCREASE_MIC_VOL: + plVoiceRecorder::IncreaseRecordingThreshhold(); + return true; + + case S_DECREASE_MIC_VOL: + plVoiceRecorder::DecreaseRecordingThreshhold(); + return true; + + /* case B_CONTROL_ACTION: + { + if (fMessageQueue[i]->fControlActivated) + { + // send a 'picked' message to the picked object + plPickedMsg* pPickedMsg = TRACKED_NEW plPickedMsg; + pPickedMsg->AddReceiver(fCurrentClickable); + plgDispatch::MsgSend(pPickedMsg); + } + else + { + // send an 'unpicked message' + plPickedMsg* pPickedMsg = TRACKED_NEW plPickedMsg; + pPickedMsg->AddReceiver(fCurrentClickable); + pPickedMsg->fPicked = false; + plgDispatch::MsgSend(pPickedMsg); + } + } + break; + */ + } + + return false; +} + +hsBool plAvatarInputInterface::CursorInBox(plMouseEventMsg* pMsg, hsPoint4 box) +{ + return ( pMsg->GetXPos() >= box.fX && pMsg->GetXPos() <= box.fY && pMsg->GetYPos() >= box.fZ && pMsg->GetYPos() <= box.fW ); +} + +void plAvatarInputInterface::Reset() +{ + fControlFlags.Clear(); + fKeyControlFlags.Clear(); + fDisabledControls.Clear(); +} + +void plAvatarInputInterface::ClearMouseCursor() +{ + IClearControlFlag(S_SET_CURSOR_UPWARD); + IClearControlFlag(S_SET_CURSOR_UP); + IClearControlFlag(S_SET_CURSOR_DOWN); + IClearControlFlag(S_SET_CURSOR_LEFT); + IClearControlFlag(S_SET_CURSOR_RIGHT); +} + +hsBool plAvatarInputInterface::MsgReceive( plMessage *msg ) +{ + plCmdIfaceModMsg *pCMsg = plCmdIfaceModMsg::ConvertNoRef( msg ); + if( pCMsg ) + { + if (pCMsg->Cmd(plCmdIfaceModMsg::kDisableControlCode)) + { + IDisableControl(pCMsg->fControlCode); + return true; + } + else + if (pCMsg->Cmd(plCmdIfaceModMsg::kEnableControlCode)) + { + IEnableControl(pCMsg->fControlCode); + return true; + } + return false; + } + return false; +} + +//// MissedInputEvent //////////////////////////////////////////////////////// +// If we "missed" an input event, then somebody caught it above us, thus we +// have "lost focus" in a way. So we should stop walking/moving/whatever. +// Should this be in the base inputInterface, since it deals with key +// bindings? Perhaps, dunno yet. We'll see... + +void plAvatarInputInterface::MissedInputEvent( plInputEventMsg *pMsg ) +{ + int i; + + + if( plKeyEventMsg::ConvertNoRef( pMsg ) == nil ) + { + // We only "lose focus" if someone else grabbed a key message. Don't care about anything else. + return; + } + + // Disable all set control flags, EXCEPT autorun. Rrrgh. + for( i = 0; i < fControlMap->GetNumBindings(); i++ ) + { + const plKeyBinding &binding = fControlMap->GetBinding( i ); + + if( IHasKeyControlFlag( binding.GetCode() ) && binding.GetCode() != B_CONTROL_ALWAYS_RUN ) + { + plCtrlCmd *pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fControlCode = binding.GetCode(); + pCmd->fControlActivated = false; + pCmd->SetCmdString( binding.GetExtendedString() ); + + if( binding.GetCodeFlags() & kControlFlagNetPropagate ) + pCmd->fNetPropagateToPlayers = true; + else + pCmd->fNetPropagateToPlayers = false; + + fMessageQueue->Append( pCmd ); + IClearKeyControlFlag( binding.GetCode() ); + } + } +} + +hsBool plAvatarInputInterface::IsEnterChatModeBound() +{ + int i; + for ( i=0; i< fControlMap->GetNumBindings(); i++ ) + { + const plKeyBinding &binding = fControlMap->GetBinding( i ); + + const char* extString = binding.GetExtendedString(); + if ( extString && strcmp("Game.EnterChatMode",extString) == 0 ) + { + if (binding.GetKey1() != plKeyCombo::kUnmapped ) + return true; + } + } + return false; +} + + +//// InterpretInputEvent ///////////////////////////////////////////////////// + +hsBool plAvatarInputInterface::InterpretInputEvent( plInputEventMsg *pMsg ) +{ + if( fInputMap == nil ) + return false; + + plMouseMap *mouseMap = fInputMap->fMouseMap; + + + plKeyEventMsg* pKeyMsg = plKeyEventMsg::ConvertNoRef(pMsg); + if( pKeyMsg ) + { + // Handled by key bindings + } + + if (fMouseDisabled) + return false; + + plMouseEventMsg* pMouseMsg = plMouseEventMsg::ConvertNoRef(pMsg); + if (pMouseMsg) + { + UInt32 oldButtonState = fInputMap->fButtonState; + + // check for button presses... + if (fInputMap->fButtonState & kLeftButtonDown) + { + fInputMap->fButtonState |= kLeftButtonRepeat; + } + if (fInputMap->fButtonState & kRightButtonDown) + { + fInputMap->fButtonState |= kRightButtonRepeat; + } + if (fInputMap->fButtonState & kMiddleButtonDown) + { + fInputMap->fButtonState |= kMiddleButtonRepeat; + } + if (pMouseMsg->GetButton() == kLeftButtonDown) + { + fInputMap->fButtonState |= kLeftButtonDown; + } + if (pMouseMsg->GetButton() == kLeftButtonUp) + { + fInputMap->fButtonState &= ~kLeftButtonDown; + fInputMap->fButtonState &= ~kLeftButtonRepeat; + } + if (pMouseMsg->GetButton() == kRightButtonDown) + { + fInputMap->fButtonState |= kRightButtonDown; + } + if (pMouseMsg->GetButton() == kRightButtonUp) + { + fInputMap->fButtonState &= ~kRightButtonDown; + fInputMap->fButtonState &= ~kRightButtonRepeat; + } + if (pMouseMsg->GetButton() == kMiddleButtonDown) + { + fInputMap->fButtonState |= kMiddleButtonDown; + } + if (pMouseMsg->GetButton() == kMiddleButtonUp) + { + fInputMap->fButtonState &= ~kMiddleButtonDown; + fInputMap->fButtonState &= ~kMiddleButtonRepeat; + } + + if( oldButtonState != fInputMap->fButtonState || pMouseMsg->GetDX() != 0.f || pMouseMsg->GetDY() != 0.f ) + { + fCursorTimeout = 0.f; // Reset cursor opacity timeout thingy + } + + /* NOTE: I see that this interface always returns true for mouse + messages, even if it does nothing with them. It ends up working + because this interface is always last in the stack. Seems like + a bad idea, but it works so far and I'm not going to change it + unless it obviously breaks something. + + Still, since we say that we've handled any mouse message, I'm + taking the liberty of making things simple. If a button is down, + we reserve focus. If not, we release it. + + If things ever change so that an interface below us expects us + to return false for messages we don't care about, we'll have to + be more careful about reserving focus. + */ + if (fInputMap->fButtonState & kAnyButtonDown) + fManager->SetCurrentFocus(this); + else + fManager->ReleaseCurrentFocus(this); + + for (int i=0; i < mouseMap->fMap.Count(); i++) + { + // is this control already set? + if (IHasControlFlag(mouseMap->fMap[i]->fCode)) + { + // Control isn't enabled, ignore + if (!IControlCodeEnabled(mouseMap->fMap[i]->fCode)) + return true; + + // can we disable this control? + hsBool disable = false; + + // can we disable this control based on a button? + if (mouseMap->fMap[i]->fControlFlags & kControlFlagLeftButton && !(fInputMap->fButtonState & kLeftButtonDown)) + disable = true; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRightButton && !(fInputMap->fButtonState & kRightButtonDown)) + disable = true; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagMiddleButton && !(fInputMap->fButtonState & kMiddleButtonDown)) + disable = true; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagLeftButtonEx && (fInputMap->fButtonState & kLeftButtonRepeat)) + disable = true; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRightButtonEx && (fInputMap->fButtonState & kRightButtonRepeat)) + disable = true; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagMiddleButtonEx && (fInputMap->fButtonState & kMiddleButtonRepeat)) + disable = true; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagLeftButtonEx && !(fInputMap->fButtonState & kLeftButtonDown)) + disable = true; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRightButtonEx && !(fInputMap->fButtonState & kRightButtonDown)) + disable = true; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagMiddleButtonEx && !(fInputMap->fButtonState & kMiddleButtonDown)) + disable = true; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagLeftButtonRepeat && !(fInputMap->fButtonState & kLeftButtonDown)) + disable = true; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRightButtonRepeat && !(fInputMap->fButtonState & kRightButtonDown)) + disable = true; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagMiddleButtonRepeat && !(fInputMap->fButtonState & kMiddleButtonDown)) + disable = true; + + // can we disable this control based on the cursor position? + if (!CursorInBox(pMouseMsg, mouseMap->fMap[i]->fBox) && mouseMap->fMap[i]->fControlFlags & kControlFlagBoxDisable) + disable = true; + + if (disable) + { + IDeactivateCommand(mouseMap->fMap[i]); + continue; + } + // is it a range control? If so we need to re-send the command + + if ((mouseMap->fMap[i]->fControlFlags & kControlFlagRangePos) || (mouseMap->fMap[i]->fControlFlags & kControlFlagRangeNeg)) + { + plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fControlActivated = true; + pCmd->fControlCode = mouseMap->fMap[i]->fCode; + hsScalar pct = 0.0f; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRangePos) + { + if (mouseMap->fMap[i]->fControlFlags & kControlFlagXAxisEvent) + pct = hsABS((mouseMap->fMap[i]->fBox.fX - pMouseMsg->GetXPos()) / (mouseMap->fMap[i]->fBox.fY - mouseMap->fMap[i]->fBox.fX)); + else + pct = hsABS((mouseMap->fMap[i]->fBox.fZ - pMouseMsg->GetYPos()) / (mouseMap->fMap[i]->fBox.fW - mouseMap->fMap[i]->fBox.fZ)); + } + else + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRangeNeg) + { + if (mouseMap->fMap[i]->fControlFlags & kControlFlagXAxisEvent) + pct = hsABS((mouseMap->fMap[i]->fBox.fY - pMouseMsg->GetXPos()) / (mouseMap->fMap[i]->fBox.fY - mouseMap->fMap[i]->fBox.fX)); + else + pct = hsABS((mouseMap->fMap[i]->fBox.fW - pMouseMsg->GetYPos()) / (mouseMap->fMap[i]->fBox.fW - mouseMap->fMap[i]->fBox.fZ)); + } + pCmd->fPct = pct; + if (pct == 1.0f || pct == -1.0f) + { + delete pCmd; + break; + } + pCmd->fNetPropagateToPlayers = mouseMap->fMap[i]->fControlFlags & kControlFlagNetPropagate; + fMessageQueue->Append(pCmd); + } + if (mouseMap->fMap[i]->fControlFlags & kControlFlagDelta) + { + plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fControlActivated = true; + pCmd->fControlCode = mouseMap->fMap[i]->fCode; + hsScalar pct = 0.0f; + + if (mouseMap->fMap[i]->fControlFlags & kControlFlagXAxisEvent) + pct = pMouseMsg->GetDX(); + else + pct = pMouseMsg->GetDY(); + + if (pct == 0.f) + { + delete pCmd; + continue; + } + pCmd->fPct = pct; + pCmd->fNetPropagateToPlayers = mouseMap->fMap[i]->fControlFlags & kControlFlagNetPropagate; + fMessageQueue->Append(pCmd); + } + + } + else // if it is an 'always if in box' command see if it's not in the box + if ( (mouseMap->fMap[i]->fControlFlags & kControlFlagInBox) && (!CursorInBox(pMouseMsg, mouseMap->fMap[i]->fBox)) ) + { + plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fControlActivated = false; + pCmd->fControlCode = mouseMap->fMap[i]->fCode; + pCmd->fNetPropagateToPlayers = mouseMap->fMap[i]->fControlFlags & kControlFlagNetPropagate; + fMessageQueue->Append(pCmd); + continue; + } + else // the control is not set, see if we should set it. + { + // is the control disabled? + if (fDisabledControls.IsBitSet(mouseMap->fMap[i]->fCode)) + continue; + + // is the cursor in the appropriate box? + if (CursorInBox(pMouseMsg, mouseMap->fMap[i]->fBox)) + { + // do we require a button? + if (mouseMap->fMap[i]->fControlFlags & kControlFlagLeftButton && !(fInputMap->fButtonState & kLeftButtonDown)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRightButton && !(fInputMap->fButtonState & kRightButtonDown)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagMiddleButton && !(fInputMap->fButtonState & kMiddleButtonDown)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagLeftButtonEx && (fInputMap->fButtonState & kLeftButtonRepeat)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRightButtonEx && (fInputMap->fButtonState & kRightButtonRepeat)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagMiddleButtonEx && (fInputMap->fButtonState & kMiddleButtonRepeat)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagLeftButtonRepeat && !(fInputMap->fButtonState & kLeftButtonRepeat)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRightButtonRepeat && !(fInputMap->fButtonState & kRightButtonRepeat)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagMiddleButtonRepeat && !(fInputMap->fButtonState & kMiddleButtonRepeat)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagLeftButtonEx && !(fInputMap->fButtonState & kLeftButtonDown)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRightButtonEx && !(fInputMap->fButtonState & kLeftButtonDown)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagMiddleButtonEx && !(fInputMap->fButtonState & kMiddleButtonDown)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagLeftButtonUp && !(pMouseMsg->GetButton() == kLeftButtonUp)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRightButtonUp && !(pMouseMsg->GetButton() == kRightButtonUp)) + continue; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagMiddleButtonUp && !(pMouseMsg->GetButton() == kMiddleButtonUp)) + continue; + + // okay, we're in the box and either we don't require a button or our button is pressed. + // so set the command as 'enabled' + // UNLESS it has kControlFlagInBox, which means we want it sent every frame it is in the box + if (!(mouseMap->fMap[i]->fControlFlags & kControlFlagInBox)) + SetControlFlag(mouseMap->fMap[i]->fCode); + // issue the command + plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fControlActivated = true; + pCmd->fControlCode = mouseMap->fMap[i]->fCode; + pCmd->fNetPropagateToPlayers = mouseMap->fMap[i]->fControlFlags & kControlFlagNetPropagate; + + // figure out what percent (if any) + hsScalar pct = 0.0f; + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRangePos) + { + if (mouseMap->fMap[i]->fControlFlags & kControlFlagXAxisEvent) + pct = hsABS((mouseMap->fMap[i]->fBox.fX - pMouseMsg->GetXPos()) / (mouseMap->fMap[i]->fBox.fY - mouseMap->fMap[i]->fBox.fX)); + else + pct = hsABS((mouseMap->fMap[i]->fBox.fZ - pMouseMsg->GetYPos()) / (mouseMap->fMap[i]->fBox.fW - mouseMap->fMap[i]->fBox.fZ)); + } + else + if (mouseMap->fMap[i]->fControlFlags & kControlFlagRangeNeg) + { + if (mouseMap->fMap[i]->fControlFlags & kControlFlagXAxisEvent) + pct = hsABS((mouseMap->fMap[i]->fBox.fY - pMouseMsg->GetXPos()) / (mouseMap->fMap[i]->fBox.fY - mouseMap->fMap[i]->fBox.fX)); + else + pct = hsABS((mouseMap->fMap[i]->fBox.fW - pMouseMsg->GetYPos()) / (mouseMap->fMap[i]->fBox.fW - mouseMap->fMap[i]->fBox.fZ)); + } + pCmd->fPct = pct; + if (pct == 1.0f || pct == -1.0f) + { + delete pCmd; + break; + } + + if (mouseMap->fMap[i]->fControlFlags & kControlFlagDelta) + { + if (mouseMap->fMap[i]->fControlFlags & kControlFlagXAxisEvent) + pct = pMouseMsg->GetDX(); + else + pct = pMouseMsg->GetDY(); + + pCmd->fPct = pct; + } + + // and add it to the list + fMessageQueue->Append(pCmd); + continue; + } + } + } + return true; + } + + return false; +} + + +////////////////////////////////////////////////////////////////////////////// +//// plAvatarInputMap and derivations //////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +// You really want to think of input maps as various states in a state machine. +// (This is why there are 3 different maps for walking. It all depends on which +// "state" we want to jump to.) +// +// When you pop from one map to the next: +// - All controls in the old map are deactivated +// (except kControlFlagNoDeactivate and kControlFlagToggle) +// - Any controls in the new map that can be activated by the current state +// will be. (i.e. you press a mouse button to switch to walk mode, hold +// hold that button down, and the walk command in the new mode will activate) + +plAvatarInputMap::plAvatarInputMap() +{ + fMouseMap = TRACKED_NEW plMouseMap; + + fButtonState = 0; + fInterface = plAvatarInputInterface::GetInstance(); +} + +plAvatarInputMap::~plAvatarInputMap() +{ + delete fMouseMap; +} + +plSuspendedMovementMap::plSuspendedMovementMap() : plAvatarInputMap() +{ + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_ACTION_MOUSE, kControlFlagLeftButtonEx, 0.0f, 1.0f, 0.0f, 1.0f, "The Picked key") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_PICK, kControlFlagLeftButton, 0.0f, 1.0f, 0.0f, 1.0f, "The Picked key") ); +} + +plBasicControlMap::plBasicControlMap() : plSuspendedMovementMap() +{ + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_ROTATE_RIGHT, kControlFlagLeftButton | kControlFlagBoxDisable, 0.95f, 1.0f, 0.0f, 1.0f, "Rotate Player Right") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_ROTATE_LEFT, kControlFlagLeftButton | kControlFlagBoxDisable, 0.0f, 0.05f, 0.0f, 1.0f, "Rotate Player Left") ); + + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_TURN_TO, kControlFlagLeftButtonEx, 0.05f, 0.95f, 0.0f, 0.95f, "Turn to") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_WALK_MODE, kControlFlagLeftButton, 0.05f, 0.95f, 0.0f, 0.95f, "Set Walk Mode") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_WALK_BACK_LB_MODE, kControlFlagLeftButton, 0.05f, 0.95f, 0.95f, 1.0f, "Set Walk Back LB Mode") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_WALK_BACK_MODE, kControlFlagMiddleButton, 0.05f, 0.95f, 0.0f, 1.0f, "Set Walk Back Mode") ); + + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_CURSOR_UP, kControlFlagNormal | kControlFlagInBox, 0.05f, 0.95f, 0.0f, 0.95f, "set cursor up") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_CURSOR_DOWN, kControlFlagNormal | kControlFlagInBox, 0.05f, 0.95f, 0.95f, 1.0f, "set cursor down") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_CURSOR_RIGHT, kControlFlagNormal | kControlFlagInBox, 0.95f, 1.0f, 0.0f, 1.0f, "set cursor right") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_CURSOR_LEFT, kControlFlagNormal | kControlFlagInBox, 0.0f, 0.05f, 0.0f, 1.0f, "set cursor left") ); +} + +plLadderControlMap::plLadderControlMap() : plSuspendedMovementMap() +{ + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_MOVE_FORWARD, kControlFlagLeftButton | kControlFlagBoxDisable, 0.0f, 1.0f, 0.0f, 0.5f, "Set Walk Mode") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_MOVE_BACKWARD, kControlFlagLeftButton | kControlFlagBoxDisable, 0.0f, 1.0f, 0.5f, 1.0f, "Set Walk Back LB Mode") ); + + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_CURSOR_UPWARD, kControlFlagNormal | kControlFlagInBox, 0.0f, 1.0f, 0.0f, 0.5f, "set cursor up") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_CURSOR_DOWN, kControlFlagNormal | kControlFlagInBox, 0.0f, 1.0f, 0.5f, 1.0f, "set cursor down") ); + + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_FREELOOK, kControlFlagRightButton, 0.05f, 0.95f, 0.0f, 0.95f, "Set Camera first-person z-axis panning") ); +} + + +plLadderMountMap::plLadderMountMap() : plSuspendedMovementMap() +{ + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_LADDER_CONTROL, kControlFlagLeftButtonUp, 0.0f, 1.0f, 0.0f, 1.0f, "Set Ladder Mode") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_LADDER_CONTROL, kControlFlagRightButtonUp, 0.0f, 1.0f, 0.0f, 1.0f, "Set Ladder Mode") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_LADDER_CONTROL, kControlFlagMiddleButtonUp, 0.0f, 1.0f, 0.0f, 1.0f, "Set Ladder Mode") ); +} + +plLadderDismountMap::plLadderDismountMap() : plSuspendedMovementMap() +{ + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_BASIC_MODE, kControlFlagLeftButtonUp, 0.0f, 1.0f, 0.0f, 1.0f, "Set Basic Mode") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_BASIC_MODE, kControlFlagRightButtonUp, 0.0f, 1.0f, 0.0f, 1.0f, "Set Basic Mode") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_BASIC_MODE, kControlFlagMiddleButtonUp, 0.0f, 1.0f, 0.0f, 1.0f, "Set Basic Mode") ); +} + + + + +plBasicThirdPersonControlMap::plBasicThirdPersonControlMap() : plBasicControlMap() +{ + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_FREELOOK, kControlFlagRightButton, 0.0f, 1.0f, 0.0f, 1.0f, "Freelook Mode") ); +} + +plBasicFirstPersonControlMap::plBasicFirstPersonControlMap() : plBasicControlMap() +{ + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(A_CONTROL_TURN, kControlFlagRightButtonRepeat | kControlFlagXAxisEvent | kControlFlagDelta, 0.0f, 1.0f, 0.0f, 1.0f, "Rotate Player") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_FREELOOK, kControlFlagRightButton, 0.05f, 0.95f, 0.0f, 0.95f, "Set Camera first-person z-axis panning") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_CAMERA_WALK_PAN, kControlFlagRightButton, 0.05f, 0.95f, 0.0f, 0.95f, "Set Camera first-person z-axis panning") ); +} + +// also used in 1st person walk mode +pl3rdWalkMap::pl3rdWalkMap() : plAvatarInputMap() +{ + // control special to this mode. + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_MODIFIER_FAST, kControlFlagRightButton, 0.0f, 1.0f, 0.0f, 1.0f, "Run Modifier" ) ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(A_CONTROL_TURN, kControlFlagXAxisEvent | kControlFlagDelta, 0.0f, 1.0f, 0.0f, 1.0f, "Rotate Player") ); + + plInputManager::SetRecenterMouse(true); + plMouseDevice::HideCursor(); + plInputInterfaceMgr::GetInstance()->ForceCursorHidden(true); +} + +pl3rdWalkMap::~pl3rdWalkMap() +{ + plInputManager::SetRecenterMouse(false); + plMouseDevice::ShowCursor(); + plInputInterfaceMgr::GetInstance()->ForceCursorHidden(false); +} + +pl3rdWalkForwardMap::pl3rdWalkForwardMap() : pl3rdWalkMap() +{ + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_BASIC_MODE, kControlFlagLeftButtonUp, 0.0f, 1.0f, 0.0f, 1.0f, "Third Person") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_MOVE_FORWARD, kControlFlagLeftButton, 0.0f, 1.0f, 0.0f, 1.0f, "Walk forward") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_CAMERA_WALK_PAN, kControlFlagLeftButton, 0.0f, 1.0f, 0.0f, 1.0f, "Set Camera first-person z-axis panning") ); +} + +pl3rdWalkBackwardMap::pl3rdWalkBackwardMap() : pl3rdWalkMap() +{ + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_BASIC_MODE, kControlFlagMiddleButtonUp, 0.0f, 1.0f, 0.0f, 1.0f, "Third Person") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_MOVE_BACKWARD, kControlFlagMiddleButton, 0.0f, 1.0f, 0.0f, 1.0f, "Walk backward") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_CAMERA_WALK_PAN, kControlFlagMiddleButton, 0.0f, 1.0f, 0.0f, 1.0f, "Set Camera first-person z-axis panning") ); +} + +// same as the other backward walk map, but this one is triggered by the left mouse button. +pl3rdWalkBackwardLBMap::pl3rdWalkBackwardLBMap() : pl3rdWalkMap() +{ + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(S_SET_BASIC_MODE, kControlFlagLeftButtonUp, 0.0f, 1.0f, 0.0f, 1.0f, "Third Person") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_MOVE_BACKWARD, kControlFlagLeftButton, 0.0f, 1.0f, 0.0f, 1.0f, "Walk backward") ); + fMouseMap->AddMapping( TRACKED_NEW plMouseInfo(B_CONTROL_CAMERA_WALK_PAN, kControlFlagLeftButton, 0.0f, 1.0f, 0.0f, 1.0f, "Set Camera first-person z-axis panning") ); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plAvatarInputInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plAvatarInputInterface.h new file mode 100644 index 00000000..5dfd0d56 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plAvatarInputInterface.h @@ -0,0 +1,252 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plAvatarInputInterface // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef plAvatarInputInterface_inc +#define plAvatarInputInterface_inc + +#include "plInputInterface.h" +#include "../pnInputCore/plInputMap.h" + +#include "hsTemplates.h" + +#include "hsGeometry3.h" +#include "hsUtils.h" + + +//// Class Definition //////////////////////////////////////////////////////// + +class plInputEventMsg; +class plMouseEventMsg; +class plKeyMap; +class plMouseMap; +class plKey; +class hsStream; +class hsResMgr; +class plAvatarInputMap; +class plPipeline; +class plAvatarInputInterface; + +//// Little Input Map Helpers //////////////////////////////////////////////// + +class plAvatarInputMap +{ + protected: + + plAvatarInputInterface *fInterface; + + public: + + plAvatarInputMap(); + virtual ~plAvatarInputMap(); + virtual char *GetName() = 0; + virtual hsBool IsBasic() { return false; } + + plMouseMap *fMouseMap; + UInt32 fButtonState; +}; + +// Basic avatar mappings, for when the avatar is in "suspended input" mode. +class plSuspendedMovementMap : public plAvatarInputMap +{ +public: + plSuspendedMovementMap(); + virtual char *GetName() { return "Suspended Movement"; } +}; + +// The above, plus movement +class plBasicControlMap : public plSuspendedMovementMap +{ +public: + plBasicControlMap(); + virtual char *GetName() { return "Basic"; } + virtual hsBool IsBasic() { return true; } + +}; +// The above, plus movement +class plBasicThirdPersonControlMap : public plBasicControlMap +{ +public: + plBasicThirdPersonControlMap(); + virtual char *GetName() { return "Basic Third Person"; } +}; + +class plLadderControlMap : public plSuspendedMovementMap +{ +public: + plLadderControlMap(); + virtual char *GetName() { return "LadderClimb"; } +}; + +class plLadderMountMap : public plSuspendedMovementMap +{ +public: + plLadderMountMap(); + virtual char *GetName() { return "Ladder Mount"; } +}; + +class plLadderDismountMap : public plSuspendedMovementMap +{ +public: + plLadderDismountMap(); + virtual char *GetName() { return "Ladder Dismount"; } +}; + + +class plBasicFirstPersonControlMap : public plBasicControlMap +{ +public: + plBasicFirstPersonControlMap(); + virtual char *GetName() { return "Basic First Person"; } +}; + +// Mouse walk mode +class pl3rdWalkMap : public plAvatarInputMap +{ +public: + pl3rdWalkMap(); + virtual ~pl3rdWalkMap(); +}; + +class pl3rdWalkForwardMap : public pl3rdWalkMap +{ +public: + pl3rdWalkForwardMap(); + virtual char *GetName() { return "Walking Forward"; } +}; + +class pl3rdWalkBackwardMap : public pl3rdWalkMap +{ +public: + pl3rdWalkBackwardMap(); + virtual char *GetName() { return "Walking Backward"; } +}; + +class pl3rdWalkBackwardLBMap : public pl3rdWalkMap +{ +public: + pl3rdWalkBackwardLBMap(); + virtual char *GetName() { return "Walking Backward (LB)"; } +}; + +/////////////////////////////////////////////////////////////////////////////////// + +class plAvatarInputInterface : public plInputInterface +{ + protected: + + UInt32 fCurrentCursor; + hsScalar fCursorOpacity, fCursorTimeout, fCursorFadeDelay; + + plAvatarInputMap *fInputMap; + + static plAvatarInputInterface *fInstance; + + virtual hsBool IHandleCtrlCmd( plCtrlCmd *cmd ); + + // Gets called once per IUpdate(), just like normal IEval()s + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); + + void IDeactivateCommand(plMouseInfo *info); + void IChangeInputMaps(plAvatarInputMap *newMap); + void ISetSuspendMovementMode(); + void ISetBasicMode(); + void ISetMouseWalkMode(ControlEventCode code); + void ISetLadderMap(); + void ISetPreLadderMap(); + void ISetPostLadderMap(); + + hsBool IHasControlFlag(int f) const { return fControlFlags.IsBitSet(f); } + void IClearControlFlag(int which) { fControlFlags.ClearBit( which ); } + + hsBool CursorInBox(plMouseEventMsg* pMsg, hsPoint4 box); + void ClearMouseCursor(); + void DisableMouseInput() { fMouseDisabled = true; } + void EnableMouseInput() { fMouseDisabled = false; } + + void Reset(); + + void RequestCursorToWorldPos(hsScalar xPos, hsScalar yPos, int ID); + + hsBitVector fControlFlags; + hsBool fMouseDisabled; + + plPipeline* fPipe; + int fCursorState; + int fCursorPriority; + hsBool f3rdPerson; + + public: + + plAvatarInputInterface(); + virtual ~plAvatarInputInterface(); + + void CameraInThirdPerson(hsBool state); + + // Always return true, since the cursor should be representing how we control the avatar + virtual hsBool HasInterestingCursorID( void ) const { return true; } + virtual UInt32 GetPriorityLevel( void ) const { return kAvatarInputPriority; } + virtual UInt32 GetCurrentCursorID( void ) const { return fCurrentCursor; } + virtual hsScalar GetCurrentCursorOpacity( void ) const { return fCursorOpacity; } + char* GetInputMapName() { return fInputMap ? fInputMap->GetName() : ""; } + + virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg ); + virtual void MissedInputEvent( plInputEventMsg *pMsg ); + + virtual hsBool MsgReceive( plMessage *msg ); + + virtual void Init( plInputInterfaceMgr *manager ); + virtual void Shutdown( void ); + + virtual void RestoreDefaultKeyMappings( void ); + virtual void ClearKeyMap(); + + // [dis/en]able mouse commands for avatar movement + void SuspendMouseMovement(); + void EnableMouseMovement(); + void EnableJump(hsBool val); + void EnableForwardMovement(hsBool val); + void EnableControl(hsBool val, ControlEventCode code); + void ClearLadderMode(); + void SetLadderMode(); + void ForceAlwaysRun(hsBool val); + + void SetControlFlag(int f, hsBool val = true) { fControlFlags.SetBit(f, val); } + + void SetCursorFadeDelay( hsScalar delay ) { fCursorFadeDelay = delay; } + + hsBool IsEnterChatModeBound(); + + static plAvatarInputInterface *GetInstance( void ) { return fInstance; } +}; + + + +#endif plAvatarInputInterface_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDInputDevice.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDInputDevice.cpp new file mode 100644 index 00000000..fd58d099 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDInputDevice.cpp @@ -0,0 +1,277 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// plDInputDevice.cpp + +#include "hsConfig.h" +#include "hsWindows.h" + +#include "plDInputDevice.h" +#include "plgDispatch.h" +#include "../plMessage/plInputEventMsg.h" +#define DIRECTINPUT_VERSION 0x0800 +#include +// +// +// +// plDInputDevice +// +// + +plDInputDevice::plDInputDevice() : +fX(0.5), +fY(0.5) +{ +} + +plDInputDevice::~plDInputDevice() +{ +} + +void plDInputDevice::Update(DIDEVICEOBJECTDATA* js) +{ + switch(js->uAppData) + { + case A_CONTROL_MOVE: + { + int i = (int)(js->dwData); + if (i <= -1) + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_MOVE_FORWARD ); + pMsg->SetControlActivated( true ); + plgDispatch::MsgSend( pMsg ); + } + else + if (i >= 1) + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_MOVE_BACKWARD ); + pMsg->SetControlActivated( true ); + plgDispatch::MsgSend( pMsg ); + } + else + if (i == 0) + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_MOVE_BACKWARD ); + pMsg->SetControlActivated( false ); + plgDispatch::MsgSend( pMsg ); + plControlEventMsg* pMsg2 = TRACKED_NEW plControlEventMsg; + pMsg2->SetControlCode( B_CONTROL_MOVE_FORWARD ); + pMsg2->SetControlActivated( false ); + plgDispatch::MsgSend( pMsg2 ); + } + + } + break; + case A_CONTROL_TURN: + { + int i = (int)(js->dwData); + float f = ((float)i) * 0.001f; + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( A_CONTROL_TURN ); + if (f <= 0.02 && f >= -0.02) + pMsg->SetControlActivated( false ); + else + pMsg->SetControlActivated( true ); + pMsg->SetControlPct(f); + plgDispatch::MsgSend( pMsg ); + } + break; + case B_CONTROL_ACTION: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_ACTION ); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CONTROL_MODIFIER_FAST: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_MODIFIER_FAST ); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CONTROL_JUMP: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_JUMP ); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CONTROL_STRAFE_LEFT: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_STRAFE_LEFT ); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CONTROL_STRAFE_RIGHT: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_STRAFE_RIGHT); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CONTROL_EQUIP: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_EQUIP ); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CONTROL_DROP: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_DROP ); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CONTROL_MOVE_FORWARD: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_MOVE_FORWARD ); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CONTROL_MOVE_BACKWARD: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_MOVE_BACKWARD ); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CONTROL_ROTATE_RIGHT: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_ROTATE_RIGHT); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CONTROL_ROTATE_LEFT: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_ROTATE_LEFT ); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CONTROL_TURN_TO: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CONTROL_TURN_TO ); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + case B_CAMERA_RECENTER: + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlCode( B_CAMERA_RECENTER ); + pMsg->SetControlActivated(js->dwData & 0x80); + plgDispatch::MsgSend(pMsg); + } + break; + + case A_CONTROL_MOUSE_X: + { + int i = (int)(js->dwData); + float f = ((float)i) * 0.001f; + if (f <= 0.02 && f >= -0.02) + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlActivated( false ); + pMsg->SetControlCode(B_CAMERA_ROTATE_DOWN); + pMsg->SetControlPct(0); + plgDispatch::MsgSend( pMsg ); + plControlEventMsg* pMsg2 = TRACKED_NEW plControlEventMsg; + pMsg2->SetControlActivated( false ); + pMsg2->SetControlCode(B_CAMERA_ROTATE_UP); + pMsg2->SetControlPct(0); + plgDispatch::MsgSend( pMsg2 ); + } + else + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlActivated( true ); + if (f < 0) + pMsg->SetControlCode(B_CAMERA_ROTATE_DOWN); + else + pMsg->SetControlCode(B_CAMERA_ROTATE_UP); + + pMsg->SetControlPct(f); + plgDispatch::MsgSend( pMsg ); + } + } + break; + case A_CONTROL_MOUSE_Y: + { + int i = (int)(js->dwData); + float f = ((float)i) * 0.001f; + if (f <= 0.02 && f >= -0.02) + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlActivated( false ); + pMsg->SetControlCode(B_CAMERA_ROTATE_RIGHT); + pMsg->SetControlPct(0); + plgDispatch::MsgSend( pMsg ); + plControlEventMsg* pMsg2 = TRACKED_NEW plControlEventMsg; + pMsg2->SetControlActivated( false ); + pMsg2->SetControlCode(B_CAMERA_ROTATE_LEFT); + pMsg2->SetControlPct(0); + plgDispatch::MsgSend( pMsg2 ); + } + else + { + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + pMsg->SetControlActivated( true ); + if (f < 0) + pMsg->SetControlCode(B_CAMERA_ROTATE_RIGHT); + else + pMsg->SetControlCode(B_CAMERA_ROTATE_LEFT); + + pMsg->SetControlPct(f); + plgDispatch::MsgSend( pMsg ); + } + } + break; + + default: + break; + } + +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDInputDevice.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDInputDevice.h new file mode 100644 index 00000000..efb07a63 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDInputDevice.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// plDInputDevice.h + +#ifndef PL_DINPUT_DEVICE_H +#define PL_DINPUT_DEVICE_H + +#include "plInputDevice.h" + +struct DIDEVICEOBJECTDATA; + +class plDInputDevice : public plInputDevice +{ +public: + plDInputDevice(); + ~plDInputDevice(); + + const char* GetInputName() { return "DInput"; } + + virtual void Update(DIDEVICEOBJECTDATA* js); + +protected: + hsScalar fX,fY; +}; + + +#endif // PL_INPUT_DEVICE_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDebugInputInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDebugInputInterface.cpp new file mode 100644 index 00000000..edda6a9d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDebugInputInterface.cpp @@ -0,0 +1,341 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDebugInputInterface // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsConfig.h" +#include "hsWindows.h" +#include "hsTypes.h" +#include "plDebugInputInterface.h" + +#include "plInputInterfaceMgr.h" +#include "plInputManager.h" +#include "plInputDevice.h" + +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnInputCore/plKeyMap.h" + +#include "plgDispatch.h" +#include "plPipeline.h" +#include "hsConfig.h" + + +plDebugInputInterface *plDebugInputInterface::fInstance = nil; + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plDebugInputInterface::plDebugInputInterface() +{ + fInstance = this; + + // Add our control codes to our control map. Do NOT add the key bindings yet. + // Note: HERE is where you specify the actions for each command, i.e. net propagate and so forth. + // This part basically declares us master of the bindings for these commands. + + // IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO + // RestoreDefaultKeyMappings()!!!! + +#ifndef PLASMA_EXTERNAL_RELEASE +// fControlMap->AddCode( B_CONTROL_MODIFIER_FAST, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CAMERA_DRIVE_SPEED_UP, kControlFlagNormal ); + fControlMap->AddCode( B_CAMERA_DRIVE_SPEED_DOWN, kControlFlagNormal ); + fControlMap->AddCode( B_CAMERA_MOVE_FORWARD, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CAMERA_MOVE_BACKWARD, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CAMERA_MOVE_LEFT, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CAMERA_MOVE_RIGHT, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CAMERA_MOVE_UP, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CAMERA_MOVE_DOWN, kControlFlagNormal | kControlFlagNoRepeat ); +// fControlMap->AddCode( B_TOGGLE_DRIVE_MODE, kControlFlagNormal | kControlFlagNoRepeat | kControlFlagShift ); +#endif + + // IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO + // RestoreDefaultKeyMappings()!!!! +} + +plDebugInputInterface::~plDebugInputInterface() +{ + fInstance = nil; +} + +//// Init/Shutdown /////////////////////////////////////////////////////////// + +void plDebugInputInterface::Init( plInputInterfaceMgr *manager ) +{ + plInputInterface::Init( manager ); +} + +void plDebugInputInterface::Shutdown( void ) +{ +} + +//// RestoreDefaultKeyMappings /////////////////////////////////////////////// + +void plDebugInputInterface::RestoreDefaultKeyMappings( void ) +{ + if( fControlMap == nil ) + return; + + fControlMap->UnmapAllBindings(); + +#ifndef PLASMA_EXTERNAL_RELEASE +// fControlMap->BindKey( KEY_SHIFT, B_CONTROL_MODIFIER_FAST ); + fControlMap->BindKey( plShiftKeyCombo( KEY_EQUAL ), B_CAMERA_DRIVE_SPEED_UP ); + fControlMap->BindKey( plShiftKeyCombo( KEY_DASH ), B_CAMERA_DRIVE_SPEED_DOWN ); + fControlMap->BindKey( KEY_W, B_CAMERA_MOVE_FORWARD ); + fControlMap->BindKey( KEY_S, B_CAMERA_MOVE_BACKWARD ); + fControlMap->BindKey( KEY_A, B_CAMERA_MOVE_LEFT ); + fControlMap->BindKey( KEY_D, B_CAMERA_MOVE_RIGHT ); + fControlMap->BindKey( KEY_I, B_CAMERA_MOVE_UP ); + fControlMap->BindKey( KEY_K, B_CAMERA_MOVE_DOWN ); +// fControlMap->BindKey( KEY_C, B_TOGGLE_DRIVE_MODE ); +#endif +} + + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool plDebugInputInterface::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return true; +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool plDebugInputInterface::MsgReceive( plMessage *msg ) +{ + return plInputInterface::MsgReceive(msg); +} + +//// cursorinbox ///////////////////////////////////////////////////// +hsBool plDebugInputInterface::CursorInBox(plMouseEventMsg* pMsg, hsPoint4 box) +{ + return ( pMsg->GetXPos() >= box.fX && pMsg->GetXPos() <= box.fY && pMsg->GetYPos() >= box.fZ && pMsg->GetYPos() <= box.fW ); +} + + +//// InterpretInputEvent ///////////////////////////////////////////////////// + +hsBool plDebugInputInterface::InterpretInputEvent( plInputEventMsg *pMsg ) +{ + hsBool handled = false; + + plMouseEventMsg* pMouseMsg = plMouseEventMsg::ConvertNoRef(pMsg); + if (pMouseMsg) + { + // check for button presses... + if (fButtonState & kLeftButtonDown) + { + fButtonState |= kLeftButtonRepeat; + } + if (fButtonState & kRightButtonDown) + { + fButtonState |= kRightButtonRepeat; + } + if (pMouseMsg->GetButton() == kLeftButtonDown) + { + fButtonState |= kLeftButtonDown; + } + if (pMouseMsg->GetButton() == kLeftButtonUp) + { + fButtonState &= ~kLeftButtonDown; + fButtonState &= ~kLeftButtonRepeat; + } + if (pMouseMsg->GetButton() == kRightButtonDown) + { + fButtonState |= kRightButtonDown; + } + if (pMouseMsg->GetButton() == kRightButtonUp) + { + fButtonState &= ~kRightButtonDown; + fButtonState &= ~kRightButtonRepeat; + } + + + for (int i=0; i < fMouseMap.fMap.Count(); i++) + { + // is this control already set? + if (fControlFlags.IsBitSet(fMouseMap.fMap[i]->fCode)) + { + // can we disable this control? + hsBool disable = false; + + // can we disable this control based on a button? + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButton && !(fButtonState & kLeftButtonDown)) + disable = true; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButton && !(fButtonState & kRightButtonDown)) + disable = true; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonEx && (fButtonState & kLeftButtonRepeat)) + disable = true; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonEx && (fButtonState & kRightButtonRepeat)) + disable = true; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonEx && !(fButtonState & kLeftButtonDown)) + disable = true; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonEx && !(fButtonState & kRightButtonDown)) + disable = true; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonRepeat && !(fButtonState & kLeftButtonDown)) + disable = true; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonRepeat && !(fButtonState & kRightButtonDown)) + disable = true; + + // can we disable this control based on the cursor position? + if (!CursorInBox(pMouseMsg, fMouseMap.fMap[i]->fBox) && fMouseMap.fMap[i]->fControlFlags & kControlFlagBoxDisable) + disable = true; + + if (disable) + { + plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fNetPropagateToPlayers = fMouseMap.fMap[i]->fControlFlags & kControlFlagNetPropagate; + pCmd->fControlActivated = false; + pCmd->fControlCode = fMouseMap.fMap[i]->fCode; + fControlFlags.ClearBit(pCmd->fControlCode); + fMessageQueue->Append(pCmd); + handled = true; + continue; + } + // is it a range control? If so we need to re-send the command + + if ((fMouseMap.fMap[i]->fControlFlags & kControlFlagRangePos) || (fMouseMap.fMap[i]->fControlFlags & kControlFlagRangeNeg)) + { + plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fControlActivated = true; + pCmd->fControlCode = fMouseMap.fMap[i]->fCode; + hsScalar pct = 0.0f; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRangePos) + { + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagXAxisEvent) + pct = hsABS((fMouseMap.fMap[i]->fBox.fX - pMouseMsg->GetXPos()) / (fMouseMap.fMap[i]->fBox.fY - fMouseMap.fMap[i]->fBox.fX)); + else + pct = hsABS((fMouseMap.fMap[i]->fBox.fZ - pMouseMsg->GetYPos()) / (fMouseMap.fMap[i]->fBox.fW - fMouseMap.fMap[i]->fBox.fZ)); + } + else + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRangeNeg) + { + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagXAxisEvent) + pct = hsABS((fMouseMap.fMap[i]->fBox.fY - pMouseMsg->GetXPos()) / (fMouseMap.fMap[i]->fBox.fY - fMouseMap.fMap[i]->fBox.fX)); + else + pct = hsABS((fMouseMap.fMap[i]->fBox.fW - pMouseMsg->GetYPos()) / (fMouseMap.fMap[i]->fBox.fW - fMouseMap.fMap[i]->fBox.fZ)); + } + pCmd->fPct = pct; + if (pct == 1.0f || pct == -1.0f) + { + delete pCmd; + break; + } + pCmd->fNetPropagateToPlayers = fMouseMap.fMap[i]->fControlFlags & kControlFlagNetPropagate; + fMessageQueue->Append(pCmd); + } + } + else // if it is an 'always if in box' command see if it's not in the box + if ( (fMouseMap.fMap[i]->fControlFlags & kControlFlagInBox) && (!CursorInBox(pMouseMsg, fMouseMap.fMap[i]->fBox)) ) + { + plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fControlActivated = false; + pCmd->fControlCode = fMouseMap.fMap[i]->fCode; + pCmd->fNetPropagateToPlayers = fMouseMap.fMap[i]->fControlFlags & kControlFlagNetPropagate; + fMessageQueue->Append(pCmd); + continue; + } + else // the control is not set, see if we should set it. + { + // is the cursor in the appropriate box? + if (CursorInBox(pMouseMsg, fMouseMap.fMap[i]->fBox)) + { + // do we require a button? + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButton && !(fButtonState & kLeftButtonDown)) + continue; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButton && !(fButtonState & kRightButtonDown)) + continue; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonEx && (fButtonState & kLeftButtonRepeat)) + continue; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonEx && (fButtonState & kRightButtonRepeat)) + continue; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonRepeat && !(fButtonState & kLeftButtonRepeat)) + continue; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonRepeat && !(fButtonState & kRightButtonRepeat)) + continue; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonEx && !(fButtonState & kLeftButtonDown)) + continue; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonEx && !(fButtonState & kLeftButtonDown)) + continue; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagLeftButtonUp && !(pMouseMsg->GetButton() == kLeftButtonUp)) + continue; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRightButtonUp && !(pMouseMsg->GetButton() == kRightButtonUp)) + continue; + + // okay, we're in the box and either we don't require a button or our button is pressed. + // so set the command as 'enabled' + // UNLESS it has kControlFlagInBox, which means we want it sent every frame it is in the box + if (!(fMouseMap.fMap[i]->fControlFlags & kControlFlagInBox)) + fControlFlags.SetBit(fMouseMap.fMap[i]->fCode); + // issue the command + plCtrlCmd* pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fControlActivated = true; + pCmd->fControlCode = fMouseMap.fMap[i]->fCode; + pCmd->fNetPropagateToPlayers = fMouseMap.fMap[i]->fControlFlags & kControlFlagNetPropagate; + + // figure out what percent (if any) + hsScalar pct = 0.0f; + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRangePos) + { + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagXAxisEvent) + pct = hsABS((fMouseMap.fMap[i]->fBox.fX - pMouseMsg->GetXPos()) / (fMouseMap.fMap[i]->fBox.fY - fMouseMap.fMap[i]->fBox.fX)); + else + pct = hsABS((fMouseMap.fMap[i]->fBox.fZ - pMouseMsg->GetYPos()) / (fMouseMap.fMap[i]->fBox.fW - fMouseMap.fMap[i]->fBox.fZ)); + } + else + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagRangeNeg) + { + if (fMouseMap.fMap[i]->fControlFlags & kControlFlagXAxisEvent) + pct = hsABS((fMouseMap.fMap[i]->fBox.fY - pMouseMsg->GetXPos()) / (fMouseMap.fMap[i]->fBox.fY - fMouseMap.fMap[i]->fBox.fX)); + else + pct = hsABS((fMouseMap.fMap[i]->fBox.fW - pMouseMsg->GetYPos()) / (fMouseMap.fMap[i]->fBox.fW - fMouseMap.fMap[i]->fBox.fZ)); + } + pCmd->fPct = pct; + if (pct == 1.0f || pct == -1.0f) + { + delete pCmd; + break; + } + + // and add it to the list + fMessageQueue->Append(pCmd); + handled = true; + continue; + } + } + } + + return handled; + } + + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDebugInputInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDebugInputInterface.h new file mode 100644 index 00000000..47e4412f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plDebugInputInterface.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDebugInputInterface // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDebugInputInterface_h +#define _plDebugInputInterface_h + +#include "plInputInterface.h" +#include "../pnInputCore/plInputMap.h" + + +//// Class Definition //////////////////////////////////////////////////////// + +class plMouseEventMsg; + +class plDebugInputInterface : public plInputInterface +{ + protected: + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); + hsBool CursorInBox(plMouseEventMsg* pMsg, hsPoint4 box); + + plMouseMap fMouseMap; + UInt32 fButtonState; + hsBitVector fControlFlags; + + static plDebugInputInterface *fInstance; + + public: + + plDebugInputInterface(); + virtual ~plDebugInputInterface(); + + // Always return false, + virtual hsBool HasInterestingCursorID( void ) const { return false; } + virtual UInt32 GetPriorityLevel( void ) const { return kDebugCmdPrioity; } + virtual void RestoreDefaultKeyMappings( void ); + virtual UInt32 GetCurrentCursorID( void ) const { return 0; } + + virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg ); + + virtual hsBool MsgReceive( plMessage *msg ); + + virtual void Init( plInputInterfaceMgr *manager ); + virtual void Shutdown( void ); + + static plDebugInputInterface *GetInstance( void ) { return fInstance; } +}; + + +#endif //_plDebugInputInterface_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputCoreCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputCoreCreatable.h new file mode 100644 index 00000000..8488346c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputCoreCreatable.h @@ -0,0 +1,40 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plInputCoreCreatable_inc +#define plInputCoreCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plInputManager.h" + +REGISTER_CREATABLE(plInputManager); + +#include "plInputInterfaceMgr.h" + +REGISTER_CREATABLE(plInputInterfaceMgr); + +#endif // plInputCoreCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp new file mode 100644 index 00000000..bcc02193 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp @@ -0,0 +1,944 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// plInputDevice.cpp +//#include "STRING" + +#include "hsConfig.h" +#include "hsWindows.h" + +#include "plInputDevice.h" +#include "plInputManager.h" +#include "plAvatarInputInterface.h" +#include "../plMessage/plInputEventMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "hsUtils.h" +#include "plgDispatch.h" + +#include "../plPipeline/plPlates.h" +#include "../plPipeline/plDebugText.h" + +#include "hsWindows.h" +#include "../NucleusLib/inc/plPipeline.h" + +// base size of the cursor +#define CURSOR_SIZE_X 0.0675f +#define CURSOR_SIZE_Y 0.09f + +// The resolution that uses the base size of the cursor. +// All other resolutions will scale the cursor size to keep the same physical size. +#define BASE_WIDTH 1024 +#define BASE_HEIGHT 768 + +plKeyboardDevice* plKeyboardDevice::fInstance = nil; +bool plKeyboardDevice::fKeyboardState[256]; +hsBool plKeyboardDevice::fIgnoreCapsLock = false; +hsBool plKeyboardDevice::fKeyIsDeadKey = false; + +plKeyboardDevice::plKeyboardDevice() : +fShiftKeyDown(false), +fCapsLockKeyDown(false), +fAltKeyDown(false), +fCtrlKeyDown(false), +fCapsLockLock(false), +fPrevNumLockOn(false), +fControlMode(STANDARD_MODE) +{ + fInstance = this; + fStartedUpWithNumLockOn = ((GetKeyState(VK_NUMLOCK) & 1) != 0); + InitKeyboardState(); +} + +plKeyboardDevice::~plKeyboardDevice() +{ + if (fStartedUpWithNumLockOn) + ForceNumLock(true); +} + +void plKeyboardDevice::InitKeyboardState() +{ + static bool initialized = false; + if (!initialized) + { + for (unsigned int i = 0; i < 256; ++i) + plKeyboardDevice::fKeyboardState[i] = false; + initialized = true; + } +} + +void plKeyboardDevice::ReleaseAllKeys() +{ + // send a key-up message for all "normal" keys + for (unsigned int i = 0; i < 256; ++i) + { + if ((i == KEY_SHIFT) || (i == KEY_CTRL) || (i == KEY_CAPSLOCK)) + continue; // these are handled slightly differently + + if (fKeyboardState[i]) + { + fKeyboardState[i] = false; + + // fake a key-up command + plKeyEventMsg* pMsg = TRACKED_NEW plKeyEventMsg; + pMsg->SetKeyCode( (plKeyDef)i ); + pMsg->SetKeyDown( false ); + pMsg->SetShiftKeyDown( fShiftKeyDown ); + pMsg->SetCtrlKeyDown( fCtrlKeyDown ); + pMsg->SetCapsLockKeyDown( fCapsLockLock ); + pMsg->SetRepeat( false ); + plgDispatch::MsgSend( pMsg ); + } + } + + // send key messages for shift and ctrl if necessary because the keys above need to have + // the proper states of the shift and ctrl keys sent with their messages, and our internal + // flags for these keys need to be cleared. We don't send a key-up message for caps lock + // because it doesn't really operate like every other key on the keyboard + if (fKeyboardState[KEY_SHIFT]) + { + fKeyboardState[KEY_SHIFT] = false; + fShiftKeyDown = false; + + plKeyEventMsg* pMsg = TRACKED_NEW plKeyEventMsg; + pMsg->SetKeyCode( KEY_SHIFT ); + pMsg->SetKeyDown( false ); + pMsg->SetShiftKeyDown( false ); + pMsg->SetCtrlKeyDown( false ); + pMsg->SetCapsLockKeyDown( fCapsLockLock ); + pMsg->SetRepeat( false ); + plgDispatch::MsgSend( pMsg ); + } + if (fKeyboardState[KEY_CTRL]) + { + fKeyboardState[KEY_CTRL] = false; + fCtrlKeyDown = false; + + plKeyEventMsg* pMsg = TRACKED_NEW plKeyEventMsg; + pMsg->SetKeyCode( KEY_CTRL ); + pMsg->SetKeyDown( false ); + pMsg->SetShiftKeyDown( false ); + pMsg->SetCtrlKeyDown( false ); + pMsg->SetCapsLockKeyDown( fCapsLockLock ); + pMsg->SetRepeat( false ); + plgDispatch::MsgSend( pMsg ); + } +} + +hsBool plKeyboardDevice::IsCapsLockKeyOn() +{ + return fCapsLockLock; +} + +void plKeyboardDevice::Shutdown() +{ +} + +#if HS_BUILD_FOR_WIN32 +void plKeyboardDevice::ForceNumLock(hsBool on) +{ + if (on != ((GetKeyState(VK_NUMLOCK) & 1) != 0)) + { + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&info); + if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + keybd_event( VK_NUMLOCK, 0, KEYEVENTF_EXTENDEDKEY | 0, 0 ); + keybd_event( VK_NUMLOCK, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0 ); + } + else + { + UInt8 keyState[256]; + GetKeyboardState(keyState); + keyState[VK_NUMLOCK] = keyState[VK_NUMLOCK] ^ 1; + SetKeyboardState(keyState); + } + } +} +#endif //HS_BUILD_FOR_WIN32 + +void plKeyboardDevice::HandleKeyEvent(plOSMsg message, plKeyDef key, bool bKeyDown, hsBool bKeyRepeat) +{ + // update the internal keyboard state + unsigned int keyCode = (unsigned int)key; + if ((key >= 0) && (key < 256)) + fKeyboardState[key] = bKeyDown; + +#if HS_BUILD_FOR_WIN32 + if (key == VK_NUMLOCK && bKeyDown) + { + ForceNumLock(false); + } +#endif // HS_BUILD_FOR_WIN32 + + if (key == KEY_SHIFT) + { + fShiftKeyDown = bKeyDown; +// return; + } + if (key == KEY_CTRL) + { + fCtrlKeyDown = bKeyDown; +// return; + } + if (key == KEY_CAPSLOCK) + { + // Keyboards toggle the light on key-down, so I'm going with that. + if (bKeyDown && !bKeyRepeat) + { + fCapsLockLock = !fCapsLockLock; + plAvatarInputInterface::GetInstance()->ForceAlwaysRun(fCapsLockLock); + } + } + + // send a key event... + plKeyEventMsg* pMsg = TRACKED_NEW plKeyEventMsg; + pMsg->SetKeyCode( key ); + pMsg->SetKeyDown( bKeyDown ); + pMsg->SetShiftKeyDown( fShiftKeyDown ); + pMsg->SetCtrlKeyDown( fCtrlKeyDown ); + pMsg->SetCapsLockKeyDown( fCapsLockLock ); + pMsg->SetRepeat(bKeyRepeat); + plgDispatch::MsgSend( pMsg ); +} + +void plKeyboardDevice::HandleWindowActivate(bool bActive, HWND hWnd) +{ + if (bActive) + { + fCtrlKeyDown = false; + + #if HS_BUILD_FOR_WIN32 + { + fPrevNumLockOn = ((GetKeyState(VK_NUMLOCK) & 1) != 0); + ForceNumLock(false); + hsBool oldLock = fCapsLockLock; + fCapsLockLock = (GetKeyState(KEY_CAPSLOCK) & 1) != 0; + if (fCapsLockLock != oldLock) + plAvatarInputInterface::GetInstance()->ForceAlwaysRun(fCapsLockLock); + } + #endif + } + else + { + ReleaseAllKeys(); // send key-up events for everything since we're losing focus + #if HS_BUILD_FOR_WIN32 + { + if (fPrevNumLockOn) + ForceNumLock(true); + } + #endif + } + +} + +//// KeyEventToChar ////////////////////////////////////////////////////////// +// Translate a Plasma key event to an actual char +char plKeyboardDevice::KeyEventToChar( plKeyEventMsg *msg ) +{ + short code = msg->GetKeyCode(); + char c = 0; + unsigned char *kbState = TRACKED_NEW unsigned char[256]; + unsigned char *buffer = TRACKED_NEW unsigned char[256]; + UINT scanCode; + int retVal; + + buffer[0] = 0; + + switch( code ) + { + case KEY_A: + case KEY_B: + case KEY_C: + case KEY_D: + case KEY_E: + case KEY_F: + case KEY_G: + case KEY_H: + case KEY_I: + case KEY_J: + case KEY_K: + case KEY_L: + case KEY_M: + case KEY_N: + case KEY_O: + case KEY_P: + case KEY_Q: + case KEY_R: + case KEY_S: + case KEY_T: + case KEY_U: + case KEY_V: + case KEY_W: + case KEY_X: + case KEY_Y: + case KEY_Z: + case KEY_1: + case KEY_2: + case KEY_3: + case KEY_4: + case KEY_5: + case KEY_6: + case KEY_7: + case KEY_8: + case KEY_9: + case KEY_0: + case KEY_TILDE: + case KEY_COMMA: + case KEY_PERIOD: + case KEY_LBRACKET: + case KEY_RBRACKET: + case KEY_BACKSLASH: + case KEY_SLASH: + case KEY_DASH: + case KEY_EQUAL: + case KEY_SEMICOLON: + case KEY_QUOTE: + // let windows translate everything for us! + scanCode = MapVirtualKeyEx(code,0,GetKeyboardLayout(0)); + GetKeyboardState(kbState); + if (fIgnoreCapsLock) + kbState[KEY_CAPSLOCK] = 0; // clear the caps lock key + retVal = ToAsciiEx(code,scanCode,kbState,(unsigned short*)buffer,0,GetKeyboardLayout(0)); + if (retVal == 2) + { + if ((buffer[0] == buffer[1]) && (!fKeyIsDeadKey)) + { + // it's actually a dead key, since the previous key wasn't a dead key + c = (char)buffer[0]; + fKeyIsDeadKey = true; + } + else + { + c = (char)buffer[1]; // it was an untranslated dead key, so copy the unconverted key + fKeyIsDeadKey = false; + } + } + else if (retVal == 0) + c = 0; // it's invalid + else + { + c = (char)buffer[0]; + if (retVal < 0) // the key was a dead key + fKeyIsDeadKey = true; + else + fKeyIsDeadKey = false; + } + break; + + case KEY_ESCAPE: c = 27; break; + case KEY_TAB: c = '\t'; break; + case KEY_BACKSPACE: c = 8; break; + case KEY_ENTER: c = '\n'; break; + case KEY_SPACE: c = ' '; break; + + // numlock on numbers + case KEY_NUMPAD0: c = '0'; break; + case KEY_NUMPAD1: c = '1'; break; + case KEY_NUMPAD2: c = '2'; break; + case KEY_NUMPAD3: c = '3'; break; + case KEY_NUMPAD4: c = '4'; break; + case KEY_NUMPAD5: c = '5'; break; + case KEY_NUMPAD6: c = '6'; break; + case KEY_NUMPAD7: c = '7'; break; + case KEY_NUMPAD8: c = '8'; break; + case KEY_NUMPAD9: c = '9'; break; + + // everything else + default: + c = 0; + break; + } + + delete [] kbState; + delete [] buffer; + + return c; +} + + +// +// +// +// plMouseDevice +// +// + +bool plMouseDevice::bMsgAlways = true; +bool plMouseDevice::bCursorHidden = false; +bool plMouseDevice::bCursorOverride = false; +bool plMouseDevice::bInverted = false; +hsScalar plMouseDevice::fWidth = BASE_WIDTH; +hsScalar plMouseDevice::fHeight = BASE_HEIGHT; +plMouseDevice* plMouseDevice::fInstance = 0; + +plMouseDevice::plMouseDevice() +{ + fXPos = 0; + fYPos = 0; + fCursorID = CURSOR_UP; + fButtonState = 0; + fOpacity = 1.f; + + fCursor = nil; + CreateCursor( fCursorID ); + plMouseDevice::fInstance = this; + fXMsg = nil; + fYMsg = nil; + fB2Msg = nil; + + fLeftBMsg[0] = nil; + fLeftBMsg[1] = nil; + fRightBMsg[0] = nil; + fRightBMsg[1] = nil; + fMiddleBMsg[0] = nil; + fMiddleBMsg[1] = nil; + +} + +plMouseDevice::~plMouseDevice() +{ + plPlateManager::Instance().DestroyPlate( fCursor ); + fCursor = nil; + plMouseDevice::fInstance = nil; +} +void plMouseDevice::SetDisplayResolution(hsScalar Width, hsScalar Height) +{ + fWidth = Width; + fHeight = Height; + IUpdateCursorSize(); +} + +void plMouseDevice::CreateCursor( int cursor ) +{ + if( fCursor == nil ) + { + plPlateManager::Instance().CreatePlate( &fCursor ); + fCursor->CreateFromResource( MAKEINTRESOURCE( cursor ) ); + } + else + { + fCursor->ReloadFromResource( MAKEINTRESOURCE( cursor ) ); + } + fCursor->SetPosition( 0, 0, 0 ); + IUpdateCursorSize(); + + fCursor->SetVisible( true ); + fCursor->SetOpacity( fOpacity ); +} + +void plMouseDevice::IUpdateCursorSize() +{ + if(fCursor) + { + // set the size of the cursor based on resolution. + fCursor->SetSize( CURSOR_SIZE_X * BASE_WIDTH / fWidth, CURSOR_SIZE_Y * BASE_HEIGHT / fHeight ); + } +} + +void plMouseDevice::AddNameToCursor(const char* name) +{ + if (fInstance && name) + { + plDebugText &txt = plDebugText::Instance(); + txt.DrawString(fInstance->fWXPos + 12 ,fInstance->fWYPos - 7,name); + } +} +void plMouseDevice::AddCCRToCursor() +{ + if (fInstance) + { + plDebugText &txt = plDebugText::Instance(); + txt.DrawString(fInstance->fWXPos + 12, fInstance->fWYPos - 17, "CCR"); + } +} +void plMouseDevice::AddIDNumToCursor(UInt32 idNum) +{ + if (fInstance && idNum) + { + plDebugText &txt = plDebugText::Instance(); + char str[256]; + sprintf(str, "%d",idNum); + txt.DrawString(fInstance->fWXPos + 12 ,fInstance->fWYPos + 3,str); + } +} + + +void plMouseDevice::SetCursorX(hsScalar x) +{ + /// Set the cursor position + if( fCursor == nil && !plMouseDevice::bCursorHidden) + CreateCursor( fCursorID ); + + if (fCursor) + fCursor->SetPosition( ( x * 2.0f ) - 1.0f, + ( fYPos * 2.0f ) - 1.0f ); + +// plDebugText &txt = plDebugText::Instance(); +// txt.DrawString(fWXPos + 20,fWYPos - 5,"test"); + +} +void plMouseDevice::SetCursorY(hsScalar y) +{ + /// Set the cursor position + if( fCursor == nil && !plMouseDevice::bCursorHidden) + CreateCursor( fCursorID ); + + if (fCursor) + fCursor->SetPosition( ( fXPos * 2.0f ) - 1.0f, + ( y * 2.0f ) - 1.0f ); + +// plDebugText &txt = plDebugText::Instance(); +// txt.DrawString(fWXPos + 20,fWYPos - 10,"test"); + +} + + +void plMouseDevice::HideCursor(hsBool override) +{ + if( fInstance->fCursor != nil ) + fInstance->fCursor->SetVisible( false ); + + plMouseDevice::bCursorOverride = (override != 0); + plMouseDevice::bCursorHidden = true; + +} + +void plMouseDevice::ShowCursor(hsBool override) +{ + if( !plMouseDevice::bCursorHidden ) + return; + if (plMouseDevice::bCursorOverride && !override) + return; + + plMouseDevice::bCursorHidden = false; + plMouseDevice::bCursorOverride = false; + + if( fInstance->fCursor == nil ) + fInstance->CreateCursor( fInstance->fCursorID ); + fInstance->fCursor->SetVisible( true ); +} + +void plMouseDevice::NewCursor(int cursor) +{ + fInstance->fCursorID = cursor; + fInstance->CreateCursor(cursor); + fInstance->SetCursorX(fInstance->GetCursorX()); + fInstance->SetCursorY(fInstance->GetCursorY()); + + if (!plMouseDevice::bCursorHidden) + fInstance->fCursor->SetVisible( true ); +} + +void plMouseDevice::SetCursorOpacity( hsScalar opacity ) +{ + fInstance->fOpacity = opacity; + if( fInstance->fCursor != nil ) + fInstance->fCursor->SetOpacity( opacity ); +} + +hsBool plMouseDevice::MsgReceive(plMessage* msg) +{ + plEvalMsg* pEMsg = plEvalMsg::ConvertNoRef(msg); + if (pEMsg) + { + if (fXMsg) + { + plgDispatch::MsgSend(fXMsg); + fXMsg = nil; + } + else + { + plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg; + pMsg->SetXPos( fXPos ); + pMsg->SetYPos( fYPos ); + pMsg->SetDX(0); + pMsg->SetDY(0); + plgDispatch::MsgSend(pMsg); + } + + if (fYMsg) + { + plgDispatch::MsgSend(fYMsg); + fYMsg = nil; + } + else + { + plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg; + pMsg->SetXPos( fXPos ); + pMsg->SetYPos( fYPos ); + pMsg->SetDX(0); + pMsg->SetDY(0); + plgDispatch::MsgSend(pMsg); + } + + if( fB2Msg ) + { + fB2Msg->Send(); + fB2Msg = nil; + } + + // look for mouse button events in the queues to be sent now + // ...Left mouse button + if ( fLeftBMsg[0] != nil) + { + fLeftBMsg[0]->Send(); + // slide queue elements over... get 'em on the next eval + fLeftBMsg[0] = fLeftBMsg[1]; + fLeftBMsg[1] = nil; + } + // ...Right mouse button + if ( fRightBMsg[0] != nil) + { + fRightBMsg[0]->Send(); + // slide queue elements over... get 'em on the next eval + fRightBMsg[0] = fRightBMsg[1]; + fRightBMsg[1] = nil; + } + // ...middle mouse button + if ( fMiddleBMsg[0] != nil) + { + fMiddleBMsg[0]->Send(); + // slide queue elements over... get 'em on the next eval + fMiddleBMsg[0] = fMiddleBMsg[1]; + fMiddleBMsg[1] = nil; + } + + } + + plIMouseXEventMsg* pXMsg = plIMouseXEventMsg::ConvertNoRef(msg); + if (pXMsg) + { + // send a mouse event + plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg; + if (pXMsg->fX == 999) + pMsg->SetXPos( fXPos + 0.001f ); + else + if (pXMsg->fX == -999) + pMsg->SetXPos( fXPos - 0.001f ); + else + pMsg->SetXPos(pXMsg->fX); + pMsg->SetYPos( fYPos ); + pMsg->SetDX( ( fXPos - pMsg->GetXPos()) ); + pMsg->SetDY(0); + + if (pMsg->GetDX() == 0.0f && !plMouseDevice::bMsgAlways) + { + delete pMsg; + return true; + } + if (fXMsg) + delete fXMsg; + fXMsg = pMsg; + + if (pXMsg->fX == 999) + fXPos += 0.01; + else + if (pXMsg->fX == -999) + fXPos -= 0.01; + else + fXPos = pXMsg->fX; + + SetCursorX(fXPos); + fWXPos = pXMsg->fWx; + return true; + } + + plIMouseYEventMsg* pYMsg = plIMouseYEventMsg::ConvertNoRef(msg); + if (pYMsg) + { + // send a mouse event + plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg; + pMsg->SetXPos( fXPos ); + if (pYMsg->fY == 999) + pMsg->SetYPos( fYPos + 0.01f ); + else + if (pYMsg->fY == -999) + pMsg->SetYPos( fYPos - 0.01f ); + else + pMsg->SetYPos(pYMsg->fY); + pMsg->SetDX(0); + pMsg->SetDY(fYPos - pMsg->GetYPos()); + + if (pMsg->GetDY() == 0.0f && !plMouseDevice::bMsgAlways) + { + delete pMsg; + return true; + } + if (fYMsg) + delete fYMsg; + fYMsg = pMsg; + + if (pYMsg->fY == 999) + fYPos += 0.01; + else + if (pYMsg->fY == -999) + fYPos -= 0.01; + else + fYPos = pYMsg->fY; + + fWYPos = pYMsg->fWy; + SetCursorY(fYPos); + + return true; + } + plIMouseBEventMsg* pBMsg = plIMouseBEventMsg::ConvertNoRef(msg); + if (pBMsg) + { + + // send a mouse event + plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg; + pMsg->SetXPos( fXPos ); + pMsg->SetYPos( fYPos ); + pMsg->SetDX(0); + pMsg->SetDY(0); + + bool deleteMe = true; + + // which button is different? + if (pBMsg->fButton & kLeftButtonDown && !(fButtonState & kLeftButtonDown)) + { + // left button now down + fButtonState |= kLeftButtonDown; + pMsg->SetButton( kLeftButtonDown ); + deleteMe = false; + } + else + if (pBMsg->fButton & kLeftButtonUp && fButtonState & kLeftButtonDown) + { + // left button now up + fButtonState &= ~kLeftButtonDown; + pMsg->SetButton( kLeftButtonUp ); + deleteMe = false; + } + else + if (pBMsg->fButton & kRightButtonDown && !(fButtonState & kRightButtonDown)) + { + // right button now down + fButtonState |= kRightButtonDown; + pMsg->SetButton( kRightButtonDown ); + deleteMe = false; + } + else + if (pBMsg->fButton & kRightButtonUp && fButtonState & kRightButtonDown) + { + // right button now up + fButtonState &= ~kRightButtonDown; + pMsg->SetButton( kRightButtonUp ); + deleteMe = false; + } + else + if (pBMsg->fButton & kMiddleButtonDown && !(fButtonState & kMiddleButtonDown)) + { + // mouse wheel button now down + fButtonState |= kMiddleButtonDown; + pMsg->SetButton( kMiddleButtonDown ); + deleteMe = false; + } + else + if (pBMsg->fButton & kMiddleButtonUp && fButtonState & kMiddleButtonDown) + { + // right button now up + fButtonState &= ~kMiddleButtonDown; + pMsg->SetButton( kMiddleButtonUp ); + deleteMe = false; + } + + if (pBMsg->fButton & kRightButtonDblClk) + { + // right button dbl clicked, send TWO messages + plMouseEventMsg* pMsg2 = TRACKED_NEW plMouseEventMsg; + pMsg2->SetXPos( fXPos ); + pMsg2->SetYPos( fYPos ); + pMsg2->SetDX(0); + pMsg2->SetDY(0); + pMsg2->SetButton( kRightButtonDblClk ); + + if( fB2Msg != nil ) + delete fB2Msg; + fB2Msg = pMsg2; + + pMsg->SetButton( kRightButtonDown ); + deleteMe = false; + } + else + if (pBMsg->fButton & kLeftButtonDblClk) + { + // left button dbl clicked, send TWO messages + plMouseEventMsg* pMsg2 = TRACKED_NEW plMouseEventMsg; + pMsg2->SetXPos( fXPos ); + pMsg2->SetYPos( fYPos ); + pMsg2->SetDX(0); + pMsg2->SetDY(0); + pMsg2->SetButton( kLeftButtonDblClk ); + + if( fB2Msg != nil ) + delete fB2Msg; + fB2Msg = pMsg2; + + pMsg->SetButton( kLeftButtonDown ); + deleteMe = false; + } + + if( deleteMe ) + { + // mouse button state not changed + delete pMsg; + return true; + } + + // we are going to save up to two button mouse events per button (left and right) + // that will be dispatched on the next eval + + // which button is this for? + if ( pMsg->GetButton() == kLeftButtonDown || pMsg->GetButton() == kLeftButtonUp ) + { + // see if the queue is just empty + if ( fLeftBMsg[0] == nil) + { + // nothing to think about... goes in first slot + fLeftBMsg[0] = pMsg; + } + else if (fLeftBMsg[1] == nil) + { + // nothing to think about... goes in second slot + fLeftBMsg[1] = pMsg; + } + else + { + // else queue if full... need to make some decisions + plMouseEventMsg* lastMsg = plMouseEventMsg::ConvertNoRef(pMsg); + // ...if this is an up event and [1] is a down then we need to remove both + // ...because we can't lose the up event and the down will have no match + if ( pMsg->GetButton() == kLeftButtonUp && lastMsg && lastMsg->GetButton() == kLeftButtonDown) + { + delete pMsg; + delete fLeftBMsg[1]; + fLeftBMsg[1] = nil; + } + // ... otherwise ignore this event + else + { + delete pMsg; + } + } + } + else if ( pMsg->GetButton() == kRightButtonDown || pMsg->GetButton() == kRightButtonUp ) + { + // see if the queue is just empty + if ( fRightBMsg[0] == nil) + { + // nothing to think about... goes in first slot + fRightBMsg[0] = pMsg; + } + else if (fRightBMsg[1] == nil) + { + // nothing to think about... goes in second slot + fRightBMsg[1] = pMsg; + } + else + { + // else queue if full... need to make some decisions + plMouseEventMsg* lastMsg = plMouseEventMsg::ConvertNoRef(pMsg); + // ...if this is an up event and [1] is a down then we need to remove both + // ...because we can't lose the up event and the down will have no match + if ( pMsg->GetButton() == kRightButtonUp && lastMsg && lastMsg->GetButton() == kRightButtonDown) + { + delete pMsg; + delete fRightBMsg[1]; + fRightBMsg[1] = nil; + } + // ... otherwise ignore this event + else + { + delete pMsg; + } + } + } + else if ( pMsg->GetButton() == kMiddleButtonDown || pMsg->GetButton() == kMiddleButtonUp ) + { + // see if the queue is just empty + if ( fMiddleBMsg[0] == nil) + { + // nothing to think about... goes in first slot + fMiddleBMsg[0] = pMsg; + } + else if (fMiddleBMsg[1] == nil) + { + // nothing to think about... goes in second slot + fMiddleBMsg[1] = pMsg; + } + else + { + // else queue if full... need to make some decisions + plMouseEventMsg* lastMsg = plMouseEventMsg::ConvertNoRef(pMsg); + // ...if this is an up event and [1] is a down then we need to remove both + // ...because we can't lose the up event and the down will have no match + if ( pMsg->GetButton() == kMiddleButtonUp && lastMsg && lastMsg->GetButton() == kMiddleButtonDown) + { + delete pMsg; + delete fMiddleBMsg[1]; + fMiddleBMsg[1] = nil; + } + // ... otherwise ignore this event + else + { + delete pMsg; + } + } + } + // we are going to dispatch the mouse button events right away + // and not wait for the next eval, because we shouldn't miss one of these + + return true; + } + return false; +} + + + +void plMouseDevice::HandleWindowActivate(bool bActive, HWND hWnd) +{ + if ( bActive ) + { + RECT rect; + ::GetClientRect(hWnd,&rect); + +// rect.right /= plInputManager::GetInstance()->GetMouseScale(); +// rect.bottom /= plInputManager::GetInstance()->GetMouseScale(); + + ::MapWindowPoints( hWnd, NULL, (POINT *)&rect, 2 ); + ::ClipCursor(&rect); + ::ShowCursor( FALSE ); + SetCapture(hWnd); + + } + else + { + ReleaseCapture(); + ::ClipCursor(nil); + ::ShowCursor( TRUE ); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h new file mode 100644 index 00000000..5c4911bb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h @@ -0,0 +1,227 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// plInputDevice.h + +#ifndef PL_INPUT_DEVICE_H +#define PL_INPUT_DEVICE_H + +#include "HeadSpin.h" +#include "hsWindows.h" +//#include "../pnInputCore/plControlDefinition.h" +#include "../pnInputCore/plOSMsg.h" +#include "hsBitVector.h" +#include "hsTemplates.h" +#include "../../apps/plClient/res/resource.h" +class plMessage; +enum plKeyDef; +struct plMouseInfo; +class plPipeline; + +class plInputDevice +{ +public: + enum Flags + { + kDisabled = 0x1 + }; +protected: + UInt32 fFlags; +public: + + plInputDevice() {;} + virtual ~plInputDevice() {;} + + virtual const char* GetInputName() = 0; + + UInt32 GetFlags() { return fFlags; } + void SetFlags(UInt32 f) { fFlags = f; } + virtual void HandleKeyEvent(plOSMsg message, plKeyDef key, bool bKeyDown, hsBool bKeyRepeat) {;} + virtual void HandleMouseEvent(plOSMsg message, plMouseState state) {;} + virtual void HandleWindowActivate(bool bActive, HWND hWnd) {;} + virtual hsBool MsgReceive(plMessage* msg) {return false;} + virtual void Shutdown() {;} + + +}; + +class plKeyEventMsg; + +class plKeyboardDevice : public plInputDevice +{ + hsBool fAltKeyDown; + hsBool fShiftKeyDown; + hsBool fCtrlKeyDown; + hsBool fCapsLockKeyDown; + int fControlMode; + hsBool fCapsLockLock; + + static bool fKeyboardState[256]; // virtual key code is the index, bool is whether it is down or not + static hsBool fIgnoreCapsLock; // set if we want it to ignore this key when translating characters (i.e. for chatting) + static hsBool fKeyIsDeadKey; // the key we just got was a dead key, store the value if you're a text input object + + static plKeyboardDevice* fInstance; + void InitKeyboardMaps(); + void InitKeyboardState(); + + void ReleaseAllKeys(); + +public: + enum + { + CONSOLE_MODE = 0, + CONSOLE_FULL, + STANDARD_MODE, + }; + plKeyboardDevice(); + ~plKeyboardDevice(); + + void SetControlMode(int i) { fControlMode = i; } + + const char* GetInputName() { return "keyboard"; } + void HandleKeyEvent(plOSMsg message, plKeyDef key, bool bKeyDown, hsBool bKeyRepeat); + virtual void HandleWindowActivate(bool bActive, HWND hWnd); + virtual hsBool IsCapsLockKeyOn(); + virtual void Shutdown(); + +#if HS_BUILD_FOR_WIN32 + void ForceNumLock(hsBool on); +#endif + + static hsBool IgnoreCapsLock() { return fIgnoreCapsLock; } + static void IgnoreCapsLock(hsBool ignore) { fIgnoreCapsLock = ignore; } + + static hsBool KeyIsDeadKey() { return fKeyIsDeadKey; } + + static plKeyboardDevice* GetInstance() { return fInstance; } + + static char KeyEventToChar( plKeyEventMsg *msg ); + +protected: + hsBool fStartedUpWithNumLockOn; // maintaining a separate flag since apparently the other one can get confused + hsBool fPrevNumLockOn; +}; + +class plPlate; + +#define CURSOR_UP IDB_CURSOR_UP +#define CURSOR_DOWN IDB_CURSOR_DOWN +#define CURSOR_RIGHT IDB_CURSOR_RIGHT +#define CURSOR_LEFT IDB_CURSOR_LEFT +#define CURSOR_OPEN IDB_CURSOR_OPEN +#define CURSOR_GRAB IDB_CURSOR_GRAB +#define CURSOR_CLICKED IDB_CURSOR_CLICKED +#define CURSOR_POISED IDB_CURSOR_POISED +#define CURSOR_ARROW IDB_CURSOR_ARROW +#define CURSOR_4WAY_OPEN IDB_CURSOR_4WAYOPEN +#define CURSOR_4WAY_CLOSED IDB_CURSOR_4WAYCLOSED +#define CURSOR_UPDOWN_CLOSED IDB_CURSOR_UPDOWNCLOSED +#define CURSOR_UPDOWN_OPEN IDB_CURSOR_UPDOWNOPEN +#define CURSOR_LEFTRIGHT_CLOSED IDB_CURSOR_LEFTRIGHTCLOSED +#define CURSOR_LEFTRIGHT_OPEN IDB_CURSOR_LEFTRIGHTOPEN +#define CURSOR_OFFER_BOOK IDB_CURSOR_BOOK +#define CURSOR_OFFER_BOOK_HI IDB_CURSOR_BOOK_HIGHLIGHT +#define CURSOR_OFFER_BOOK_CLICKED IDB_CURSOR_BOOK_CLICKED +#define CURSOR_CLICK_DISABLED IDB_CURSOR_DISABLED +#define CURSOR_HAND IDB_CURSOR_HAND +#define CURSOR_UPWARD IDB_CURSOR_UPWARD + +class plInputEventMsg; + +class plMouseDevice : public plInputDevice +{ +public: + plMouseDevice(); + ~plMouseDevice(); + + const char* GetInputName() { return "mouse"; } + void HandleWindowActivate(bool bActive, HWND hWnd); + + hsBool HasControlFlag(int f) const { return fControlFlags.IsBitSet(f); } + void SetControlFlag(int f) + { + fControlFlags.SetBit(f); + } + void ClearControlFlag(int which) { fControlFlags.ClearBit( which ); } + void SetCursorX(hsScalar x); + void SetCursorY(hsScalar y); + hsScalar GetCursorX() { return fXPos; } + hsScalar GetCursorY() { return fYPos; } + UInt32 GetButtonState() { return fButtonState; } + hsScalar GetCursorOpacity() { return fOpacity; } + void SetDisplayResolution(hsScalar Width, hsScalar Height); + + virtual hsBool MsgReceive(plMessage* msg); + + static plMouseDevice* Instance() { return plMouseDevice::fInstance; } + + static void SetMsgAlways(bool b) { plMouseDevice::bMsgAlways = b; } + static void ShowCursor(hsBool override = false); + static void NewCursor(int cursor); + static void HideCursor(hsBool override = false); + static bool GetHideCursor() { return plMouseDevice::bCursorHidden; } + static void SetCursorOpacity( hsScalar opacity = 1.f ); + static bool GetInverted() { return plMouseDevice::bInverted; } + static void SetInverted(bool inverted) { plMouseDevice::bInverted = inverted; } + static void AddNameToCursor(const char* name); + static void AddIDNumToCursor(UInt32 idNum); + static void AddCCRToCursor(); + +protected: + plInputEventMsg* fXMsg; + plInputEventMsg* fYMsg; + plInputEventMsg* fB2Msg; + + // mouse button event queues (only hold 2) + plInputEventMsg* fLeftBMsg[2]; + plInputEventMsg* fRightBMsg[2]; + plInputEventMsg* fMiddleBMsg[2]; + + hsScalar fXPos; + hsScalar fYPos; + int fWXPos; // the windows coordinates of the cursor + int fWYPos; + UInt32 fButtonState; + hsScalar fOpacity; + hsBitVector fControlFlags; + + + plPlate *fCursor; + int fCursorID; + + static plMouseDevice* fInstance; + static plMouseInfo fDefaultMouseControlMap[]; + void CreateCursor( int cursor ); + void IUpdateCursorSize(); + static bool bMsgAlways; + static bool bCursorHidden; + static bool bCursorOverride; + static bool bInverted; + static hsScalar fWidth, fHeight; +}; + + + +#endif // PL_INPUT_DEVICE_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterface.cpp new file mode 100644 index 00000000..1f4f7b3a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterface.cpp @@ -0,0 +1,289 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plInputInterface.cpp - A single layer on the input interface stack // +// // +//// History ///////////////////////////////////////////////////////////////// +// // +// 2.20.02 mcn - Created. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsConfig.h" +#include "hsWindows.h" +#include "hsTypes.h" +#include "plInputInterface.h" +#include "plInputInterfaceMgr.h" + +#include "../pnInputCore/plKeyMap.h" +#include "../plMessage/plInputEventMsg.h" + +#include "hsResMgr.h" +#include "plgDispatch.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plInputInterface::plInputInterface() +{ + fEnabled = false; + fControlMap = TRACKED_NEW plKeyMap; +} + +plInputInterface::~plInputInterface() +{ + delete fControlMap; +} + +void plInputInterface::ClearKeyMap() +{ + if( fControlMap != nil ) + fControlMap->ClearAll(); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void plInputInterface::Read( hsStream* s, hsResMgr* mgr ) +{ +} + +void plInputInterface::Write( hsStream* s, hsResMgr* mgr ) +{ +} + +//// Helper Functions //////////////////////////////////////////////////////// + +hsBool plInputInterface::IOwnsControlCode( ControlEventCode code ) +{ + if( fControlMap->FindBinding( code ) != nil ) + return true; + + return false; +} + +//// IVerifyShiftKey ///////////////////////////////////////////////////////// +// special logic so the shift key can make everyone totally happy... + +hsBool plInputInterface::IVerifyShiftKey( plKeyDef key, int index ) +{ + // if we are mapped to the actual shift key, return true + if (key == KEY_SHIFT) + return true; + + // if anything else is mapped to this key + shift, return false + /* for (int i=0; i < fControlMap->GetNumBindings(); i++) + { + if (index == i) + continue; + if (fKeyMap->fMap[i]->fKeyDef == key && fKeyMap->fMap[i]->fKeyFlags & plKeyInfo::kKeyShift ) + return false; + } + */ return true; +} + +void plInputInterface::IDeactivateBinding(const plKeyBinding *binding) +{ + if( !(binding->GetCodeFlags() & kControlFlagNoDeactivate) && !(binding->GetCodeFlags() & kControlFlagToggle) ) + { + plCtrlCmd *pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fControlCode = binding->GetCode(); + pCmd->fControlActivated = false; + pCmd->SetCmdString( binding->GetExtendedString() ); + pCmd->fNetPropagateToPlayers = ( binding->GetCodeFlags() & kControlFlagNetPropagate ) ? true : false; + + fMessageQueue->Append( pCmd ); + } + IClearKeyControlFlag(binding->GetCode()); +} + +//// ProcessKeyBindings ////////////////////////////////////////////////////// +// Processes the given key event as a key binding, if one exists. If not, +// returns false. + +hsBool plInputInterface::ProcessKeyBindings( plInputEventMsg *msg ) +{ + int i; + hsBool activate; + + plKeyEventMsg *keyMsg = plKeyEventMsg::ConvertNoRef( msg ); + if( keyMsg == nil ) + return false; + + + /// We might have controls that are currently enabled that are triggered in part by + /// modifiers (ctrl or shift)...if that is true, then we want to disable them if either + /// of those modifiers are up, no matter what key this message is for + hsTArray enabledCtrls; + fKeyControlFlags.Enumerate( enabledCtrls ); + + for( i = 0; i < enabledCtrls.GetCount(); i++ ) + { + const plKeyBinding *binding = fControlMap->FindBinding( (ControlEventCode)enabledCtrls[ i ] ); + if( binding == nil ) + ; // Somehow we lost the binding?? + else + { + bool wantShift, wantCtrl; + if( fKeyControlsFrom2ndKeyFlags.IsBitSet( enabledCtrls[ i ] ) ) + { + wantShift = ( binding->GetKey2().fFlags & plKeyCombo::kShift ) || ( binding->GetKey2().fKey == KEY_SHIFT ); + wantCtrl = ( binding->GetKey2().fFlags & plKeyCombo::kCtrl ) || ( binding->GetKey2().fKey == KEY_CTRL ); + } + else + { + wantShift = ( binding->GetKey1().fFlags & plKeyCombo::kShift ) || ( binding->GetKey1().fKey == KEY_SHIFT ); + wantCtrl = ( binding->GetKey1().fFlags & plKeyCombo::kCtrl ) || ( binding->GetKey1().fKey == KEY_CTRL ); + } + + if( ( wantShift && !keyMsg->GetShiftKeyDown() ) || ( wantCtrl && !keyMsg->GetCtrlKeyDown() ) ) + { + IDeactivateBinding(binding); + fKeyControlsFrom2ndKeyFlags.SetBit(enabledCtrls[i], false); + } + } + } + + + /// Process any binding for this message's key code now + plKeyCombo combo( keyMsg->GetKeyCode(), ( keyMsg->GetShiftKeyDown() ? plKeyCombo::kShift : 0 ) | + ( keyMsg->GetCtrlKeyDown() ? plKeyCombo::kCtrl : 0 ) ); + + hsTArray bindings; + fControlMap->FindAllBindingsByKey(combo, bindings); + + // The first binding is the one we want. (FindAllBindingsByKey guarantees this) + const plKeyBinding *binding = (bindings.GetCount() ? bindings[0] : nil); + + // If other bindings were found, they lose out to the first one. + for (i = 1; i < bindings.GetCount(); i++) + IDeactivateBinding(bindings[i]); + + /* + const plKeyBinding *binding = fControlMap->FindBindingByKey( combo ); + if( binding == nil ) + { + // Don't panic just yet, there are some special cases with the shift key to check first + if( keyMsg->GetKeyCode() == KEY_SHIFT || keyMsg->GetShiftKeyDown() ) + { + // See, there are two other cases to consider: 1) we have a binding directly to the shift + // key, which wouldn't have the shift flag set (so the above search wouldn't have caught it). + + // The second case would be if we have a matching binding without shift... + // which is VALID so long as no other bindings respond to this key combo + shift, but of course, + // if there were, we'd have found them already! + + // Either way, we remove the shift flag and try again + combo.fFlags &= ~plKeyCombo::kShift; + + binding = fControlMap->FindBindingByKey( combo ); + } + } + */ + + if (!binding) + return false; + + UInt32 codeFlags = binding->GetCodeFlags(); + + // Filter out no-repeat messages + if( ( codeFlags & kControlFlagNoRepeat ) && keyMsg->GetRepeat() ) + return false; + + if( codeFlags & kControlFlagNormal ) + { + // "Normal" behavior--enable on key down, disable on key up + activate = keyMsg->GetKeyDown() ? true : false; + } + else if( codeFlags & kControlFlagToggle ) + { + // Toggle behavior + if( ( codeFlags & kControlFlagDownEvent ) && !keyMsg->GetKeyDown() ) + return false; + if( ( codeFlags & kControlFlagUpEvent ) && keyMsg->GetKeyDown() ) + return false; + + if( IHasKeyControlFlag( binding->GetCode() ) ) + activate = false; + else + activate = true; + } + else + { + // Remaining ones are triggered to activate on their flagged event and + // deactivate when that turns false + if( ( codeFlags & kControlFlagDownEvent ) && !keyMsg->GetKeyDown() ) + activate = false; + else if( ( codeFlags & kControlFlagUpEvent ) && keyMsg->GetKeyDown() ) + activate = false; + else + activate = true; + } + + hsBool wasActive = IHasKeyControlFlag(binding->GetCode()); + + // Set or clear our flags, since we do that even if we don't send a message + if( activate ) + { + ISetKeyControlFlag( binding->GetCode() ); + fKeyControlsFrom2ndKeyFlags.SetBit( binding->GetCode(), ( binding->GetKey2() == combo ) ? true : false ); + } + else + { + IClearKeyControlFlag( binding->GetCode() ); + fKeyControlsFrom2ndKeyFlags.SetBit( binding->GetCode(), 0 ); + } + + // Filter out codes that only want their activate messages sent (like console commands) + if( ( codeFlags & kControlFlagNoDeactivate ) && !activate ) + return false; + + if (!IControlCodeEnabled(binding->GetCode())) + { + if (activate || (codeFlags & kControlFlagToggle) || !wasActive) + { + // It's ok to deactivate a disabled control, but not activate it + return false; + } + } + + /// OK, generate the message to send + plCtrlCmd *pCmd = TRACKED_NEW plCtrlCmd( this ); + pCmd->fControlCode = binding->GetCode(); + pCmd->fControlActivated = activate; + + pCmd->SetCmdString( binding->GetExtendedString() ); + pCmd->fNetPropagateToPlayers = ( codeFlags & kControlFlagNetPropagate ) ? true : false; + + fMessageQueue->Append( pCmd ); + + return true; +} + +hsBool plInputInterface::IControlCodeEnabled(ControlEventCode code ) +{ + return (!fDisabledControls.IsBitSet(code)); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterface.h new file mode 100644 index 00000000..f6bedb8d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterface.h @@ -0,0 +1,206 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plInputInterface.cpp - A single layer on the input interface stack // +// // +//// History ///////////////////////////////////////////////////////////////// +// // +// 2.20.02 mcn - Created. // +// // +//// Note on GetPriorityLevel() ////////////////////////////////////////////// +// // +// The inputInterfaceMgr uses GetPriorityLevel() to place each interface // +// into the stack relative to the other interfaces. Current priority // +// levels are: // +// Console - 100 // +// GUI system - 75 // +// Avatar input - 50 // +// Scene interaction - 25 // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plInputInterface_h +#define _plInputInterface_h + +#include "hsRefCnt.h" +#include "hsTemplates.h" +#include "hsBitVector.h" +// Needed for UNIX Build +// only windows will let you predeclare an enum +#include "../../NucleusLib/pnInputCore/plKeyDef.h" +#include "../../NucleusLib/pnInputCore/plControlEventCodes.h" + + +//// Class Definition //////////////////////////////////////////////////////// + +class hsStream; +class hsResMgr; +class plInputEventMsg; +class plInputInterfaceMgr; +class plMessage; +class plKeyMap; +class plCtrlCmd; +class plKeyBinding; + +class plInputInterface : public hsRefCnt +{ + friend class plInputInterfaceMgr; + + protected: + + enum Priorities + { + kConsolePriority = 100, + kGUISystemPriority = 75, + kDebugCmdPrioity = 60, + kSceneInteractionPriority = 50, + kTelescopeInputPriority = 26, + kAvatarInputPriority = 25, + }; + + plInputInterfaceMgr *fManager; + + plKeyMap *fControlMap; + hsTArray *fMessageQueue; + + hsBitVector fKeyControlFlags; + hsBitVector fKeyControlsFrom2ndKeyFlags; + hsBitVector fDisabledControls; + hsBool fEnabled; + + void ISetMessageQueue( hsTArray *queue ) { fMessageQueue = queue; } + plKeyMap *IGetControlMap( void ) const { return fControlMap; } + hsBool IOwnsControlCode( ControlEventCode code ); + hsBool IVerifyShiftKey( plKeyDef key, int index ); + + hsBool IHasKeyControlFlag(int f) const { return fKeyControlFlags.IsBitSet(f); } + void ISetKeyControlFlag(int f) { fKeyControlFlags.SetBit(f); } + void IClearKeyControlFlag(int which) { fKeyControlFlags.ClearBit( which ); } + void IDisableControl(int which) { fDisabledControls.SetBit(which); } + void IEnableControl(int which) { fDisabledControls.ClearBit(which); } + + // The binding lost focus/priority. Behave as though they released the key and send + // a deactivate message for the control code. + void IDeactivateBinding(const plKeyBinding *binding); + + + // Gets called once per IUpdate(), just like normal IEval()s + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ) { return false; } + + // Override to handle special-cased control messages of your own (same as receiving them via a message, but if you process them, nobody else gets them). Return false if you don't handle it. + virtual hsBool IHandleCtrlCmd( plCtrlCmd *cmd ) { return false; } + + // Override to let the input interfaces control when a binding is truly active. If this function returns false, + // ProcessKeyBindings will ignore the keypress for the given control. This way, the interfaces can be selective + // about which bindings are active when. By default, always returns true, since there are very few and rare + // cases where you'd want to return false + virtual hsBool IControlCodeEnabled( ControlEventCode code ); + + // Some helpers for derived classes to avoid including the manager unnecessariliy + + public: + + plInputInterface(); + virtual ~plInputInterface(); + + enum Cursors + { + kNullCursor = 0, + kCursorUp, + kCursorLeft, + kCursorRight, + kCursorDown, + kCursorPoised, + kCursorClicked, + kCursorUnClicked, + kCursorHidden, + kCursorOpen, + kCursorGrab, + kCursorArrow, + kCursor4WayDraggable, + kCursor4WayDragging, + kCursorUpDownDraggable, + kCursorUpDownDragging, + kCursorLeftRightDraggable, + kCursorLeftRightDragging, + kCursorOfferBook, + kCursorOfferBookHilite, + kCursorOfferBookClicked, + kCursorClickDisabled, + kCursorHand, + kCursorUpward, + }; + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + // Returns the priority of this interface layer, based on the Priorities enum + virtual UInt32 GetPriorityLevel( void ) const = 0; + + // Returns true if the message was handled, false if not and we want to pass it on to others in the stack + virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg ) = 0; + + // Returns the currently active mouse cursor for this layer, as defined in pnMessage/plCursorChangeMsg.h + virtual UInt32 GetCurrentCursorID( void ) const = 0; + + // Returns the current opacity that this layer wants the cursor to be, from 0 (xparent) to 1 (opaque) + virtual hsScalar GetCurrentCursorOpacity( void ) const { return 1.f; } + + // Returns true if this layer is wanting to change the mouse, false if it isn't interested + virtual hsBool HasInterestingCursorID( void ) const = 0; + + // Gets called by the manager. If you want a message to come to you, set your manager as the destination + virtual hsBool MsgReceive( plMessage *msg ) { return false; } + + // Any initialization that requires a pointer to the manager needs to be done on Init()/Shutdown() + virtual void Init( plInputInterfaceMgr *manager ) { fManager = manager; RestoreDefaultKeyMappings(); } + virtual void Shutdown( void ) {} + + // Gets called when any of the key mappings are changed, so that the interface layer can refresh the ones its interested in + virtual void RefreshKeyMap( void ) {} + + // Called when the interface manager is setting all key mappings to default + virtual void RestoreDefaultKeyMappings( void ) {} + + // Called on each interface layer that gets missed when processing inputEvents in the manager (i.e. you either get this call or InterpretInputEvent) + virtual void MissedInputEvent( plInputEventMsg *pMsg ) {} + + // Non-virtual, can't override--processes an inputEventMsg to see if we can handle it via a key binding (if so, InterpretInputEvent won't be called) + hsBool ProcessKeyBindings( plInputEventMsg *keyMsg ); + + void SetEnabled( hsBool e ) { fEnabled = e; } + hsBool IsEnabled( void ) const { return fEnabled; } + + // clear all keys from map + virtual void ClearKeyMap(); + + // reset clickable state + virtual void ResetClickableState() {;} +}; + + +#endif // _plInputInterface_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.cpp new file mode 100644 index 00000000..3948b503 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.cpp @@ -0,0 +1,947 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plInputInterfaceMgr.cpp - The manager of all input interface layers // +// // +//// History ///////////////////////////////////////////////////////////////// +// // +// 2.20.02 mcn - Created. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsConfig.h" +#include "hsWindows.h" +#include "hsTypes.h" +#include "plInputInterfaceMgr.h" +#include "plInputInterface.h" +#include "plInputDevice.h" // For mouse device stuff + +#include "../pnInputCore/plKeyMap.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../pnMessage/plClientMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plCmdIfaceModMsg.h" +#include "../pnMessage/plPlayerPageMsg.h" + +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" + +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClient/plNetClientMgr.h" + +#include "hsResMgr.h" +#include "plgDispatch.h" +#include "plProfile.h" +#include "../plResMgr/plLocalization.h" + +plProfile_CreateTimer("Input", "Update", Input); + +//// plCtrlCmd /////////////////////////////////////////////////////////////// + +void plCtrlCmd::Write(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteSwap32( fControlCode ); + stream->WriteBool( fControlActivated ); + fPt.Write(stream); + + // write cmd/string + plMsgCStringHelper::Poke(fCmd, stream); +} + +void plCtrlCmd::Read(hsStream* stream, hsResMgr* mgr) +{ + fControlCode = (ControlEventCode)stream->ReadSwap32(); + fControlActivated = stream->ReadBool(); + fPt.Read(stream); + + // read cmd/string + plMsgCStringHelper::Peek(fCmd, stream); +} + +//// plDefaultKeyCatcher ///////////////////////////////////////////////////// + +plDefaultKeyCatcher::~plDefaultKeyCatcher() +{ + if( plInputInterfaceMgr::GetInstance() != nil ) + plInputInterfaceMgr::GetInstance()->SetDefaultKeyCatcher( nil ); +} + +//// Statics ///////////////////////////////////////////////////////////////// + +plInputInterfaceMgr *plInputInterfaceMgr::fInstance = nil; + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plInputInterfaceMgr::plInputInterfaceMgr() +{ + fClickEnabled = false; + fCurrentCursor = -1; + fCursorOpacity = 1.f; + fForceCursorHidden = false; + fForceCursorHiddenCount = 0; + InitDefaultKeyMap(); + +#if 0 + // make sure we don't miss control keys on remote players + SetSynchFlagsBit(plSynchedObject::kSendReliably); +#endif + hsAssert( fInstance == nil, "Attempting to create two input interface managers!" ); + fInstance = this; + + fCurrentFocus = nil; + fDefaultCatcher = nil; +} + +plInputInterfaceMgr::~plInputInterfaceMgr() +{ + Shutdown(); + fInstance = nil; +} + +//// Init //////////////////////////////////////////////////////////////////// + +#include "plAvatarInputInterface.h" +#include "plSceneInputInterface.h" +#include "plDebugInputInterface.h" +#include "plTelescopeInputInterface.h" + +void plInputInterfaceMgr::Init( void ) +{ + RegisterAs( kInputInterfaceMgr_KEY ); + + plgDispatch::Dispatch()->RegisterForType( plInputIfaceMgrMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->RegisterForType( plInputEventMsg::Index(), GetKey() ); + + plgDispatch::Dispatch()->RegisterForType( plEvalMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->RegisterForExactType( plPlayerPageMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->RegisterForExactType( plCmdIfaceModMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->RegisterForExactType( plClientMsg::Index(), GetKey() ); + + /// Hacks (?) for now + plAvatarInputInterface *avatar = TRACKED_NEW plAvatarInputInterface(); + IAddInterface( avatar ); + hsRefCnt_SafeUnRef( avatar ); + + plSceneInputInterface *scene = TRACKED_NEW plSceneInputInterface(); + IAddInterface( scene ); + hsRefCnt_SafeUnRef( scene ); + + plDebugInputInterface *camDrive = TRACKED_NEW plDebugInputInterface(); + IAddInterface( camDrive ); + hsRefCnt_SafeUnRef( camDrive ); + +} + +//// Shutdown //////////////////////////////////////////////////////////////// + +void plInputInterfaceMgr::Shutdown( void ) +{ + int i; + + +// WriteKeyMap(); + + for( i = 0; i < fInterfaces.GetCount(); i++ ) + { + fInterfaces[ i ]->Shutdown(); + hsRefCnt_SafeUnRef( fInterfaces[ i ] ); + } + fInterfaces.Reset(); + + for( i = 0; i < fMessageQueue.GetCount(); i++ ) + delete fMessageQueue[ i ]; + fMessageQueue.Reset(); + + plgDispatch::Dispatch()->UnRegisterForType( plInputIfaceMgrMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->UnRegisterForType( plInputEventMsg::Index(), GetKey() ); + + plgDispatch::Dispatch()->UnRegisterForType( plEvalMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->UnRegisterForExactType( plPlayerPageMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->UnRegisterForExactType( plCmdIfaceModMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->UnRegisterForExactType( plClientMsg::Index(), GetKey() ); + + UnRegisterAs( kInputInterfaceMgr_KEY ); +} + +//// IAdd/RemoveInterface //////////////////////////////////////////////////// +// Each interface has a "priority level", i.e. where in the list it should be. +// Doing it this way allows us to keep the manager unaware of the number or +// types of interfaces. + +void plInputInterfaceMgr::IAddInterface( plInputInterface *iface ) +{ + int i; + + + for( i = 0; i < fInterfaces.GetCount(); i++ ) + { + if( fInterfaces[ i ]->GetPriorityLevel() < iface->GetPriorityLevel() ) + break; + } + + fInterfaces.Insert( i, iface ); + hsRefCnt_SafeRef( iface ); + iface->Init( this ); + iface->ISetMessageQueue( &fMessageQueue ); +} + +void plInputInterfaceMgr::IRemoveInterface( plInputInterface *iface ) +{ + int idx = fInterfaces.Find( iface ); + if( idx != fInterfaces.kMissingIndex ) + { + fInterfaces[ idx ]->Shutdown(); + hsRefCnt_SafeUnRef( fInterfaces[ idx ] ); + fInterfaces.Remove( idx ); + } +} + +/// reset clickable state ////////////////////////////////////////////////////////////////////////// + +void plInputInterfaceMgr::ResetClickableState() +{ + // look for the scene input interface + for(int i = 0; i < fInterfaces.GetCount(); i++ ) + fInterfaces[i]->ResetClickableState(); +} + +//// IUpdateCursor /////////////////////////////////////////////////////////// + +void plInputInterfaceMgr::IUpdateCursor( Int32 newCursor ) +{ + int mouseCursorResID; + + + fCurrentCursor = newCursor; + if( fCurrentCursor == plInputInterface::kCursorHidden ) + plMouseDevice::HideCursor(); + else + { + plMouseDevice::ShowCursor(); + + switch( fCurrentCursor ) + { + case plInputInterface::kCursorUp: mouseCursorResID = CURSOR_UP; break; + case plInputInterface::kCursorLeft: mouseCursorResID = CURSOR_LEFT; break; + case plInputInterface::kCursorRight: mouseCursorResID = CURSOR_RIGHT; break; + case plInputInterface::kCursorDown: mouseCursorResID = CURSOR_DOWN; break; + case plInputInterface::kCursorPoised: mouseCursorResID = CURSOR_POISED; break; + case plInputInterface::kCursorClicked: mouseCursorResID = CURSOR_CLICKED; break; + case plInputInterface::kCursorUnClicked: mouseCursorResID = CURSOR_POISED; break; + case plInputInterface::kCursorOpen: mouseCursorResID = CURSOR_OPEN; break; + case plInputInterface::kCursorGrab: mouseCursorResID = CURSOR_GRAB; break; + case plInputInterface::kCursorArrow: mouseCursorResID = CURSOR_ARROW; break; + case plInputInterface::kCursor4WayDraggable: mouseCursorResID = CURSOR_4WAY_OPEN; break; + case plInputInterface::kCursor4WayDragging: mouseCursorResID = CURSOR_4WAY_CLOSED; break; + case plInputInterface::kCursorUpDownDraggable: mouseCursorResID = CURSOR_UPDOWN_OPEN; break; + case plInputInterface::kCursorUpDownDragging: mouseCursorResID = CURSOR_UPDOWN_CLOSED; break; + case plInputInterface::kCursorLeftRightDraggable: mouseCursorResID = CURSOR_LEFTRIGHT_OPEN; break; + case plInputInterface::kCursorLeftRightDragging: mouseCursorResID = CURSOR_LEFTRIGHT_CLOSED; break; + case plInputInterface::kCursorOfferBook: mouseCursorResID = CURSOR_OFFER_BOOK; break; + case plInputInterface::kCursorOfferBookHilite: mouseCursorResID = CURSOR_OFFER_BOOK_HI; break; + case plInputInterface::kCursorOfferBookClicked: mouseCursorResID = CURSOR_OFFER_BOOK_CLICKED; break; + case plInputInterface::kCursorClickDisabled: mouseCursorResID = CURSOR_CLICK_DISABLED; break; + case plInputInterface::kCursorHand: mouseCursorResID = CURSOR_HAND; break; + case plInputInterface::kCursorUpward: mouseCursorResID = CURSOR_UPWARD; break; + default: mouseCursorResID = CURSOR_OPEN; break; + + } + + + plMouseDevice::NewCursor( mouseCursorResID ); + } +} + +//// IEval /////////////////////////////////////////////////////////////////// +// Inherited from plSingleModifier, gets called once per IUpdate() loop. + +hsBool plInputInterfaceMgr::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + const char *inputEval = "Eval"; + plProfile_BeginLap(Input, inputEval); + int i; + + + // Let all our layers eval + for( i = 0; i < fInterfaces.GetCount(); i++ ) + fInterfaces[ i ]->IEval( secs, del, dirty ); + + // Handle our message queue now + for( i = 0; i < fMessageQueue.Count(); i++ ) + { + // Can its layer handle it? + if( !fMessageQueue[ i ]->GetSource()->IHandleCtrlCmd( fMessageQueue[ i ] ) ) + { + // Nope, just dispatch it like normal + plControlEventMsg* pMsg = TRACKED_NEW plControlEventMsg; + for (int j = 0; j < fReceivers.Count(); j++) + pMsg->AddReceiver( fReceivers[ j ] ); + pMsg->SetControlActivated( fMessageQueue[i]->fControlActivated ); + pMsg->SetControlCode( fMessageQueue[i]->fControlCode ); + pMsg->SetControlPct(fMessageQueue[i]->fPct); + pMsg->SetTurnToPt( fMessageQueue[i]->fPt ); + pMsg->SetCmdString(fMessageQueue[i]->GetCmdString()); + pMsg->SetSender( GetKey() ); + plgDispatch::MsgSend( pMsg ); + + /////////////////////////////////////////////////////// + // send same msg over network to players + /////////////////////////////////////////////////////// + + if (fMessageQueue[i]->fNetPropagateToPlayers) + { + pMsg = TRACKED_NEW plControlEventMsg; + for (int j = 0; j < fReceivers.Count(); j++) + if (fReceivers[j] == plNetClientApp::GetInstance()->GetLocalPlayerKey()) + pMsg->AddReceiver( fReceivers[j] ); + if (pMsg->GetNumReceivers()) + { + pMsg->SetControlActivated( fMessageQueue[i]->fControlActivated ); + pMsg->SetControlCode( fMessageQueue[i]->fControlCode ); + pMsg->SetControlPct(fMessageQueue[i]->fPct); + pMsg->SetTurnToPt( fMessageQueue[i]->fPt ); + pMsg->SetCmdString(fMessageQueue[i]->GetCmdString()); + pMsg->SetSender( GetKey() ); + pMsg->SetBCastFlag(plMessage::kNetPropagate | plMessage::kPropagateToModifiers | + plMessage::kNetUseRelevanceRegions); // bcast only to other players who care about the region I'm in + pMsg->SetBCastFlag(plMessage::kLocalPropagate, false); + + plgDispatch::MsgSend( pMsg ); + } + else + delete pMsg; + } + } + } + + // Clear the message queue + for( i = 0; i < fMessageQueue.Count(); i++ ) + delete fMessageQueue[ i ]; + fMessageQueue.SetCount( 0 ); + + plProfile_EndLap(Input, inputEval); + return true; +} + +void plInputInterfaceMgr::ForceCursorHidden( hsBool requestedState ) +{ + if ( requestedState ) + { + fForceCursorHiddenCount++; + fForceCursorHidden = requestedState; + } + else + { + fForceCursorHiddenCount--; + +// this happens way too often to leave in +// hsAssert(fForceCursorHiddenCount>=0,"ForceCursorHidded: unhiding more times than hidden" ); + +#define OnlyHideCursorOnLast +#ifdef OnlyHideCursorOnLast + // is this is the last person... then really unforce hidding the mouse cursor + if ( fForceCursorHiddenCount <= 0 ) + { +#endif //OnlyHideCursorOnLast + + fForceCursorHidden = requestedState; + fForceCursorHiddenCount = 0; + +#ifdef OnlyHideCursorOnLast + } +#endif //OnlyHideCursorOnLast + } + +} + +hsBool plInputInterfaceMgr::ICheckCursor(plInputInterface *iFace) +{ + if( iFace->IsEnabled() && iFace->HasInterestingCursorID() ) + { + if( iFace->GetCurrentCursorID() != fCurrentCursor ) + IUpdateCursor( iFace->GetCurrentCursorID() ); + if( iFace->GetCurrentCursorOpacity() != fCursorOpacity ) + { + fCursorOpacity = iFace->GetCurrentCursorOpacity(); + plMouseDevice::SetCursorOpacity( fCursorOpacity ); + } + return true; + } + return false; +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool plInputInterfaceMgr::MsgReceive( plMessage *msg ) +{ + int i; + + + plEvalMsg *pEvalMsg = plEvalMsg::ConvertNoRef( msg ); + if( pEvalMsg ) + { + IEval( pEvalMsg->GetTimeStamp(), pEvalMsg->DelSeconds(), false ); + return true; + } + + plInputEventMsg *ieMsg = plInputEventMsg::ConvertNoRef( msg ); + if( ieMsg != nil ) + { + const char *inputIEM = "InputEventMsg"; + plProfile_BeginLap(Input, inputIEM); + hsBool handled = false; + UInt32 missedInputStartIdx = 0; + plInputInterface *oldCurrentFocus = fCurrentFocus; + + // Current focus (if there is one) gets first crack + if( fCurrentFocus ) + { + if( fCurrentFocus->IsEnabled() ) + { + handled = (fCurrentFocus->ProcessKeyBindings(ieMsg) || fCurrentFocus->InterpretInputEvent(ieMsg)); + } + } + + if (!handled) + { + // Walk our stack + for( i = 0; i < fInterfaces.GetCount(); i++ ) + { + if( fInterfaces[ i ]->IsEnabled() && fInterfaces[ i ] != oldCurrentFocus) + { + // Try the key bindings first (common for all layers) + if( fInterfaces[ i ]->ProcessKeyBindings( ieMsg ) || fInterfaces[ i ]->InterpretInputEvent( ieMsg )) + { + handled = true; + break; + } + } + } + + if( !handled ) + { + // Fell all the way through the stack...must've been a very uninteresting message... + if( plKeyEventMsg::ConvertNoRef( ieMsg ) && fDefaultCatcher != nil ) + { + // But somebody loves those keys :) + fDefaultCatcher->HandleKeyEvent( plKeyEventMsg::ConvertNoRef( ieMsg ) ); + } + } + missedInputStartIdx = i + 1; + } + + // Notify the rest of the interfaces in the stack that they missed the event ("lost focus", as it were) + for (i = missedInputStartIdx; i < fInterfaces.GetCount(); i++) + if (fInterfaces[i] != oldCurrentFocus) + fInterfaces[i]->MissedInputEvent(ieMsg); + + // Now we re-walk to see who's the new interested party. Note that we have to re-walk + // because a key down may have changed some layer's interest in the cursor + if( !fForceCursorHidden ) + { + hsBool cursorHandled = false; + if (fCurrentFocus) + cursorHandled = ICheckCursor(fCurrentFocus); + + if (!cursorHandled) + { + for( i = 0; i < fInterfaces.GetCount(); i++ ) + { + if (ICheckCursor(fInterfaces[i])) + { + cursorHandled = true; + break; + } + } + if (!cursorHandled) + { + // NOBODY is interested in the mouse, so set to our default cursor + IUpdateCursor( plInputInterface::kCursorUp ); + fCursorOpacity = 1.f; + plMouseDevice::SetCursorOpacity( fCursorOpacity ); + } + } + } + else + { + // Special debug flag to force the cursor to be hidden + if( fCursorOpacity != 0.f ) + { + fCursorOpacity = 0.f; + plMouseDevice::SetCursorOpacity( fCursorOpacity ); + } + } + plProfile_EndLap(Input, inputIEM); + return true; + } + + plInputIfaceMgrMsg *mgrMsg = plInputIfaceMgrMsg::ConvertNoRef( msg ); + if( mgrMsg != nil ) + { + if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kAddInterface ) + { + IAddInterface( mgrMsg->GetIFace() ); + return true; + } + else if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kRemoveInterface ) + { + IRemoveInterface( mgrMsg->GetIFace() ); + return true; + } + else if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kEnableClickables ) + { + fClickEnabled = true; + } + else if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kDisableClickables ) + { + fClickEnabled = false; + } + } + + plPlayerPageMsg *pPMsg = plPlayerPageMsg::ConvertNoRef( msg ); + if( pPMsg != nil && !pPMsg->fUnload) + { + if( pPMsg->fPlayer == plNetClientMgr::GetInstance()->GetLocalPlayerKey() ) + fReceivers.Append( pPMsg->fPlayer ); + else + { + int idx = fReceivers.Find( pPMsg->fPlayer ); + if( idx != fReceivers.kMissingIndex ) + fReceivers.Remove( idx ); + } + } + + plCmdIfaceModMsg *pCMsg = plCmdIfaceModMsg::ConvertNoRef( msg ); + if( pCMsg ) + { + if( pCMsg->Cmd( plCmdIfaceModMsg::kAdd ) ) + { + for( int i = 0; i < fReceivers.Count(); i++ ) + { + if( fReceivers[i] == pCMsg->GetSender() ) + return true; + } + fReceivers.Append( pCMsg->GetSender() ); + return true; + } + else + if( pCMsg->Cmd( plCmdIfaceModMsg::kRemove ) ) + { + for( int i = 0; i < fReceivers.Count(); i++ ) + { + if( fReceivers[ i ] == pCMsg->GetSender() ) + { + fReceivers.Remove( i ); + break; + } + } + return true; + } + } + + plClientMsg *cMsg = plClientMsg::ConvertNoRef(msg); + if (cMsg && cMsg->GetClientMsgFlag() == plClientMsg::kInitComplete) + { + // Backwards compatability hack: + // We've loaded in the user prefs for input. If they bind movement + // to an arrow, or numpad, and the other binding is free, automatically + // bind the other one. + plKeyMap *map = plAvatarInputInterface::GetInstance()->fControlMap; + map->HandleAutoDualBinding(KEY_UP, KEY_NUMPAD8); + map->HandleAutoDualBinding(KEY_DOWN, KEY_NUMPAD2); + map->HandleAutoDualBinding(KEY_LEFT, KEY_NUMPAD4); + map->HandleAutoDualBinding(KEY_RIGHT, KEY_NUMPAD6); + + plgDispatch::Dispatch()->UnRegisterForExactType( plClientMsg::Index(), GetKey() ); + return true; + } + // Wasn't one we want. Was it one that one of our interfaces wanted? + for( i = 0; i < fInterfaces.GetCount(); i++ ) + { + if( fInterfaces[ i ]->MsgReceive( msg ) ) + return true; + } + + // Nothing, pass on... + return plSingleModifier::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void plInputInterfaceMgr::Read( hsStream* s, hsResMgr* mgr ) +{ + plSingleModifier::Read( s, mgr ); +} + +void plInputInterfaceMgr::Write( hsStream* s, hsResMgr* mgr ) +{ + plSingleModifier::Write( s, mgr ); +} + + +////////////////////////////////////////////////////////////////////////////// +//// Key Maps //////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// IGetRoutedKeyMap //////////////////////////////////////////////////////// + +plKeyMap *plInputInterfaceMgr::IGetRoutedKeyMap( ControlEventCode code ) +{ + int i; + + + for( i = 0; i < fInterfaces.GetCount(); i++ ) + { + if( fInterfaces[ i ]->IOwnsControlCode( code ) ) + return fInterfaces[ i ]->fControlMap; + } + + return nil; +} + +//// IUnbind ///////////////////////////////////////////////////////////////// +// Unmaps any mappings with the given key. This prevents you from mapping +// a single key to multiple commands. Currently inactive because some people +// think it's a good idea to be able to do that. + +#define ALLOW_MULTIPLE_CMDS_PER_KEY 1 + +void plInputInterfaceMgr::IUnbind( const plKeyCombo &key ) +{ +#if !(ALLOW_MULTIPLE_CMDS_PER_KEY) + int i; + + + for( i = 0; i < fInterfaces.GetCount(); i++ ) + fInterfaces[ i ]->fControlMap->UnmapKey( key ); +#endif +} + +void plInputInterfaceMgr::ClearAllKeyMaps() +{ + for (int i = 0; i < fInterfaces.Count(); i++) + fInterfaces[i]->ClearKeyMap(); +} + +//// Binding Routers ///////////////////////////////////////////////////////// + +void plInputInterfaceMgr::BindAction( const plKeyCombo &key, ControlEventCode code ) +{ + plKeyMap *map = IGetRoutedKeyMap( code ); + if( map != nil ) + { + // Use default prefs + map->EnsureKeysClear( key, plKeyCombo::kUnmapped ); + map->BindKey( key, code ); + } + RefreshInterfaceKeyMaps(); +} + +void plInputInterfaceMgr::BindAction( const plKeyCombo &key1, const plKeyCombo &key2, + ControlEventCode code ) +{ + plKeyMap *map = IGetRoutedKeyMap( code ); + if( map != nil ) + { + // Force the bindings to each key, since the user specified both + map->EnsureKeysClear( key1, key2 ); + map->BindKey( key1, code, plKeyMap::kFirstAlways ); + map->BindKey( key2, code, plKeyMap::kSecondAlways ); + } + RefreshInterfaceKeyMaps(); +} + +const plKeyBinding* plInputInterfaceMgr::FindBinding( ControlEventCode code )\ +{ + plKeyMap *map = IGetRoutedKeyMap( code ); + if( map != nil ) + { + // Use default prefs + return map->FindBinding(code); + } + + return nil; +} + +void plInputInterfaceMgr::BindConsoleCmd( const plKeyCombo &key, const char *cmd, plKeyMap::BindPref pref /*= kNoPreference*/ ) +{ +// not sure why this is not for external...since its done thru the different interfaces? +//#ifdef PLASMA_EXTERNAL_RELEASE +// return; +//#endif + + plKeyMap *map = IGetRoutedKeyMap( B_CONTROL_CONSOLE_COMMAND ); + if( map != nil ) + { + // Default prefs again + map->EnsureKeysClear( key, plKeyCombo::kUnmapped ); + // BindKeyToConsoleCmd only works if the console command in question has already been assigned + // to this map. Oftentimes, this isn't true when the user is binding console commands, so go ahead + // and add the command to this map. If it's already added, this call will just quietly fail and + // we're ok to continue + map->AddConsoleCommand( cmd ); + map->BindKeyToConsoleCmd( key, cmd, pref ); + } + RefreshInterfaceKeyMaps(); +} + +const plKeyBinding* plInputInterfaceMgr::FindBindingByConsoleCmd( const char *cmd ) +{ + plKeyMap *map = IGetRoutedKeyMap( B_CONTROL_CONSOLE_COMMAND ); + if( map != nil ) + { + return map->FindConsoleBinding(cmd); + } + return nil; +} + +//// InitDefaultKeyMap /////////////////////////////////////////////////////// + +void plInputInterfaceMgr::InitDefaultKeyMap( void ) +{ + int i; + + for( i = 0; i < fInterfaces.GetCount(); i++ ) + fInterfaces[ i ]->RestoreDefaultKeyMappings(); + + RefreshInterfaceKeyMaps(); +} + +//// RefreshInterfaceKeyMaps ///////////////////////////////////////////////// + +void plInputInterfaceMgr::RefreshInterfaceKeyMaps( void ) +{ + int i; + + + for( i = 0; i < fInterfaces.GetCount(); i++ ) + fInterfaces[ i ]->RefreshKeyMap(); +} + +//// WriteKeyMap ///////////////////////////////////////////////////////////// + +void plInputInterfaceMgr::WriteKeyMap( void ) +{ +#ifdef PLASMA_EXTERNAL_RELEASE + return; +#endif + FILE* gKeyFile = 0; + gKeyFile = hsFopen( "init\\keyboard.fni", "wt" ); + + if (gKeyFile) + { + fprintf(gKeyFile, "# To remap a control to a new key,\n"); + fprintf(gKeyFile, "# just change the key listed. \n"); + fprintf(gKeyFile, "# The list of available commands is at the bottom.\n"); + fprintf(gKeyFile, "# For keys with multi-character names \n"); + fprintf(gKeyFile, "# like F1 or Backspace, be sure to enter \n"); + fprintf(gKeyFile, "# the key name as it appears in the list \n"); + fprintf(gKeyFile, "# at the end of the file. \n"); + fprintf(gKeyFile, "#\n"); + fprintf(gKeyFile, "# Be sure to put quotes around the actual command\n"); + fprintf(gKeyFile, "#\n"); + fprintf(gKeyFile, "# To add modifiers to a key mapping (like ctrl or shift)\n"); + fprintf(gKeyFile, "# append _C for ctrl or _S for Shift (or both)\n"); + fprintf(gKeyFile, "# to the actual name of the key.\n"); + fprintf(gKeyFile, "#\n"); + fprintf(gKeyFile, "# For example, to map Control-Shift-W to Walk Forward\n"); + fprintf(gKeyFile, "# your key mapping entry would look like this:\n"); + fprintf(gKeyFile, "# Keyboard.BindAction W_C_S \"Walk Forward\"\n"); + fprintf(gKeyFile, "# This also works for console command bindings\n"); + + + fprintf(gKeyFile, "# Keyboard.BindAction \t\tKey1\tKey2\t\t\t\tControl\n"); + fprintf(gKeyFile, "#\n"); +// fprintf(gKeyFile, "Keyboard.ClearBindings\n"); + int i; + + for( i = 0; i < fInterfaces.GetCount(); i++ ) + IWriteNonConsoleCmdKeys( fInterfaces[ i ]->fControlMap, gKeyFile ); + + fprintf(gKeyFile, "#\n"); + fprintf(gKeyFile, "# Console command bindings:\n"); + fprintf(gKeyFile, "#\n"); + fprintf(gKeyFile, "# Keyboard.BindConsoleCmd \tKey\t\t\tCommand\n"); + fprintf(gKeyFile, "#\n"); + + for( i = 0; i < fInterfaces.GetCount(); i++ ) + IWriteConsoleCmdKeys( fInterfaces[ i ]->fControlMap, gKeyFile ); + + fprintf(gKeyFile, "#\n"); + fprintf(gKeyFile, "# Available game commands:\n"); + fprintf(gKeyFile, "#\n"); + + for( int j = 0; plKeyMap::fCmdConvert[ j ].fCode != END_CONTROLS; j++ ) + { + if( stricmp( plKeyMap::fCmdConvert[ j ].fDesc, "Run Modifier" ) == 0) + continue; + fprintf( gKeyFile, "# %s\n", plKeyMap::fCmdConvert[ j ].fDesc ); + } + + fprintf(gKeyFile, "#\n"); + fprintf(gKeyFile, "# Key name list (for a-z or 0-9 just use the character)\n"); + fprintf(gKeyFile, "#\n"); + + Win32keyConvert* keyConvert = &plKeyMap::fKeyConversionEnglish[0]; + switch (plLocalization::GetLanguage()) + { + case plLocalization::kFrench: + keyConvert = &plKeyMap::fKeyConversionFrench[0]; + break; + case plLocalization::kGerman: + keyConvert = &plKeyMap::fKeyConversionGerman[0]; + break; + //case plLocalization::kSpanish: + // keyConvert = &plKeyMap::fKeyConversionSpanish[0]; + // break; + //case plLocalization::kItalian: + // keyConvert = &plKeyMap::fKeyConversionItalian[0]; + // break; + // default is English + } + for (i = 0; keyConvert[i].fVKey != 0xffffffff; i++) + { +// if (stricmp(fKeyMap->fKeyConversion[i].fKeyName, "Shift") == 0) +// continue; + fprintf(gKeyFile, "# %s\n", keyConvert[i].fKeyName); + } + fclose(gKeyFile); + } +} + +void plInputInterfaceMgr::SetCurrentFocus(plInputInterface *focus) +{ + fCurrentFocus = focus; +} + +void plInputInterfaceMgr::ReleaseCurrentFocus(plInputInterface *focus) +{ + if (fCurrentFocus == focus) + fCurrentFocus = nil; +} + + +//// IKeyComboToString /////////////////////////////////////////////////////// +// Uses static string, so don't call twice and expect the first result to +// be still valid! + +const char *plInputInterfaceMgr::IKeyComboToString( const plKeyCombo &combo ) +{ + static char str[ 64 ]; + bool unmapped = false; + + + if( combo == plKeyCombo::kUnmapped ) + sprintf( str, "(unmapped)" ); + else + { + char *c = plKeyMap::ConvertVKeyToChar( combo.fKey ); + if( c != nil ) + strncpy( str, c, sizeof( str ) ); + else + { + if( isalnum( combo.fKey ) ) + { + str[ 0 ] = (char)combo.fKey; + str[ 1 ] = 0; + } + else + { + strcpy( str, "(unmapped)" ); + unmapped = true; + } + } + if( !unmapped ) + { + if( combo.fFlags & plKeyCombo::kCtrl ) + strcat( str, "_C" ); + if( combo.fFlags & plKeyCombo::kShift ) + strcat( str, "_S" ); + } + } + + return str; +} + +//// IWriteNonConsoleCmdKeys ///////////////////////////////////////////////// + +void plInputInterfaceMgr::IWriteNonConsoleCmdKeys( plKeyMap *keyMap, FILE *keyFile ) +{ + int i; + + + for( i = 0; i < keyMap->GetNumBindings(); i++ ) + { + const plKeyBinding &binding = keyMap->GetBinding( i ); + + if( binding.GetCode() == B_CONTROL_CONSOLE_COMMAND ) + continue; + + char key1[ 64 ]; + strcpy( key1, IKeyComboToString( binding.GetKey1() ) ); + + const char *key2 = IKeyComboToString( binding.GetKey2() ); + + const char *desc = plInputMap::ConvertControlCodeToString( binding.GetCode() ); + + fprintf( keyFile, "Keyboard.BindAction \t\t%s\t%s\t\t\t\t\"%s\"\n", key1, key2, desc ); + } + +} + +//// IWriteConsoleCmdKeys //////////////////////////////////////////////////// + +void plInputInterfaceMgr::IWriteConsoleCmdKeys( plKeyMap *keyMap, FILE *keyFile ) +{ + int i; + + + for( i = 0; i < keyMap->GetNumBindings(); i++ ) + { + const plKeyBinding &binding = keyMap->GetBinding( i ); + + if( binding.GetCode() != B_CONTROL_CONSOLE_COMMAND ) + continue; + + // Our bindConsoleCmd console command (echo echo) only takes 1 key combo, not 2, + // so as not to confuse people. Or something. So if we got two bindings, we print + // 2 commands, which is perfectly valid +// if( binding.GetKey1() != plKeyCombo::kUnmapped ) +// { + const char *key = IKeyComboToString( binding.GetKey1() ); + fprintf( keyFile, "Keyboard.BindConsoleCmd\t%s\t\t\t\"%s\"\n", key, binding.GetExtendedString() ); +// } + + if( binding.GetKey2() != plKeyCombo::kUnmapped ) + { + const char *key = IKeyComboToString( binding.GetKey2() ); + fprintf( keyFile, "Keyboard.BindConsoleCmd\t%s\t\t\t\"%s\"\n", key, binding.GetExtendedString() ); + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.h new file mode 100644 index 00000000..de40327b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputInterfaceMgr.h @@ -0,0 +1,186 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plInputInterfaceMgr.h - The manager of all input interface layers // +// // +//// History ///////////////////////////////////////////////////////////////// +// // +// 2.20.02 mcn - Created. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plInputInterfaceMgr_h +#define _plInputInterfaceMgr_h + +#include "../pnModifier/plSingleModifier.h" +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "../pnInputCore/plKeyMap.h" + +//// Class Definition //////////////////////////////////////////////////////// + +class hsStream; +class hsResMgr; +class plInputInterface; +//class plKeyMap; +enum plKeyDef; +enum ControlEventCode; +class plKey; +class plCtrlCmd; +class plKeyCombo; +class plDefaultKeyCatcher; +class plKeyBinding; + +class plInputInterfaceMgr : public plSingleModifier +{ + protected: + + static plInputInterfaceMgr *fInstance; + + hsTArray fInterfaces; + hsTArray fMessageQueue; + hsTArray fReceivers; + +#ifdef MCN_DISABLE_OLD_WITH_NEW_HACK + hsTArray fDisabledCodes; + hsTArray fDisabledKeys; +#endif + + hsBool fClickEnabled; + Int32 fCurrentCursor; + hsScalar fCursorOpacity; + hsBool fForceCursorHidden; + Int32 fForceCursorHiddenCount; + plInputInterface *fCurrentFocus; + plDefaultKeyCatcher *fDefaultCatcher; + + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); + + void IAddInterface( plInputInterface *iface ); + void IRemoveInterface( plInputInterface *iface ); + + void IUpdateCursor( Int32 newCursor ); + hsBool ICheckCursor(plInputInterface *iFace); // returns true if the iface changed cursor settings + + void IWriteConsoleCmdKeys( plKeyMap *keyMap, FILE *keyFile ); + void IWriteNonConsoleCmdKeys( plKeyMap *keyMap, FILE *keyFile ); + + plKeyMap *IGetRoutedKeyMap( ControlEventCode code ); // Null for console commands + void IUnbind( const plKeyCombo &key ); + + const char *IKeyComboToString( const plKeyCombo &combo ); + + public: + + plInputInterfaceMgr(); + virtual ~plInputInterfaceMgr(); + + CLASSNAME_REGISTER( plInputInterfaceMgr ); + GETINTERFACE_ANY( plInputInterfaceMgr, plSingleModifier ); + + virtual hsBool MsgReceive( plMessage *msg ); + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + + void Init( void ); + void Shutdown( void ); + + void InitDefaultKeyMap( void ); + void WriteKeyMap( void ); + void RefreshInterfaceKeyMaps( void ); + + void SetCurrentFocus(plInputInterface *focus); + void ReleaseCurrentFocus(plInputInterface *focus); + void SetDefaultKeyCatcher( plDefaultKeyCatcher *c ) { fDefaultCatcher = c; } + + hsBool IsClickEnabled() { return fClickEnabled; } + + void ForceCursorHidden( hsBool requestedState ); + + // Binding routers + void BindAction( const plKeyCombo &key, ControlEventCode code ); + void BindAction( const plKeyCombo &key1, const plKeyCombo &key2, ControlEventCode code ); + void BindConsoleCmd( const plKeyCombo &key, const char *cmd, plKeyMap::BindPref pref = plKeyMap::kNoPreference ); + + const plKeyBinding* FindBinding( ControlEventCode code ); + const plKeyBinding* FindBindingByConsoleCmd( const char *cmd ); + + void ClearAllKeyMaps(); + void ResetClickableState(); + static plInputInterfaceMgr *GetInstance( void ) { return fInstance; } +}; + +//// plCtrlCmd /////////////////////////////////////////////////////////////// +// Networkable helper class that represents a single control statement + +class plCtrlCmd +{ + private: + char* fCmd; + plInputInterface *fSource; + + public: + plCtrlCmd( plInputInterface *source ) : fCmd(nil),fPct(1.0f), fSource(source) {;} + ~plCtrlCmd() { delete [] fCmd; } + + const char* GetCmdString() { return fCmd; } + void SetCmdString(const char* cs) { delete [] fCmd; fCmd=hsStrcpy(cs); } + + ControlEventCode fControlCode; + hsBool fControlActivated; + hsPoint3 fPt; + hsScalar fPct; + + hsBool fNetPropagateToPlayers; + + void Read( hsStream* s, hsResMgr* mgr ); + void Write( hsStream* s, hsResMgr* mgr ); + + plInputInterface *GetSource( void ) const { return fSource; } +}; + +//// Tiny Virtual Class For The Default Key Processor //////////////////////// +// +// Basically, if you want to be the one to catch the leftover key events, +// derive from this class and pass yourself to inputIFaceMgr. +// (it'll auto-tell inputIFaceMgr when it goes away) +// +// Note: if you want to do more than just get the darned key event (like +// mouse events or key bindings or change the cursor or the like), don't do +// this; create your own plInputInterface instead. + +class plKeyEventMsg; +class plDefaultKeyCatcher +{ + public: + virtual ~plDefaultKeyCatcher(); + virtual void HandleKeyEvent( plKeyEventMsg *eventMsg ) = 0; +}; + + +#endif // _plInputInterfaceMgr_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputManager.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputManager.cpp new file mode 100644 index 00000000..d262b712 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputManager.cpp @@ -0,0 +1,693 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" +#include "hsWindows.h" + +// plInputManager.cpp +#define DIRECTINPUT_VERSION 0x0800 +#include + +#include "hsTypes.h" +#include "plInputManager.h" +#include "plPipeline.h" +#include "plInputDevice.h" +#include "plDInputDevice.h" +#include "../plMessage/plInputEventMsg.h" +#include "plInputInterfaceMgr.h" +#include "hsStream.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "hsResMgr.h" +#include "hsTimer.h" +#include "plgDispatch.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plCmdIfaceModMsg.h" +#include "../pnMessage/plPlayerPageMsg.h" + +hsBool plInputManager::fUseDInput = false; +UInt8 plInputManager::bRecenterMouse = 0; +HWND plInputManager::fhWnd = nil; +#define NUM_ACTIONS 17 + +struct plDIDevice +{ + plDIDevice() : fDevice(nil), fCaps(nil) {;} + plDIDevice(IDirectInputDevice8* _device) : fCaps(nil) { fDevice = _device;} + IDirectInputDevice8* fDevice; + DIDEVCAPS* fCaps; +}; + +struct plDInput +{ + plDInput() : + fDInput(nil), + fActionFormat(nil) + {;} + IDirectInput8* fDInput; + hsTArray fSticks; + DIACTIONFORMAT* fActionFormat; +}; + +class plDInputMgr +{ +public: + plDInputMgr(); + ~plDInputMgr(); + + void Init(HINSTANCE hInst, HWND hWnd); + void Update(); + void AddDevice(IDirectInputDevice8* device); + void ConfigureDevice(); + virtual hsBool MsgReceive(plMessage* msg); + + // dinput callback functions + static int __stdcall EnumGamepadCallback(const DIDEVICEINSTANCE* device, void* pRef); +// I should be using these but they don't work... +// static int __stdcall SetAxisRange(const DIDEVICEOBJECTINSTANCE* obj, void* pRef); +// static int __stdcall EnumSuitableDevices(const struct DIDEVICEINSTANCEA* devInst, struct IDirectInputDevice8* dev, unsigned long why, unsigned long devRemaining, void* pRef); + +protected: + plDInput* fDI; + hsTArray fInputDevice; + static DIACTION fActionMap[]; + HWND fhWnd; +}; +// function pointers to dinput callbacks +typedef int (__stdcall * Pfunc1) (const DIDEVICEINSTANCE* device, void* pRef); +// I should need these... +//typedef int (__stdcall * Pfunc2) (const DIDEVICEOBJECTINSTANCE* device, void* pRef); +//typedef int (__stdcall * Pfunc3) (const struct DIDEVICEINSTANCEA* devInst, struct IDirectInputDevice8* dev, unsigned long why, unsigned long devRemaining, void* pRef); + + +plInputManager* plInputManager::fInstance = nil; + +plInputManager::plInputManager( HWND hWnd ) : +fDInputMgr(nil), +fInterfaceMgr(nil) +{ + fhWnd = hWnd; + fInstance = this; + fActive = false; + fFirstActivated = false; + fMouseScale = 1.f; +} + +plInputManager::plInputManager() : +fDInputMgr(nil), +fInterfaceMgr(nil) +{ + fInstance = this; + fActive = false; + fFirstActivated = false; + fMouseScale = 1.f; +} + +plInputManager::~plInputManager() +{ + fInterfaceMgr->Shutdown(); + fInterfaceMgr = nil; + + for (int i = 0; i < fInputDevices.Count(); i++) + { + fInputDevices[i]->Shutdown(); + delete(fInputDevices[i]); + } + if (fDInputMgr) + delete fDInputMgr; +} + +//static +void plInputManager::SetRecenterMouse(hsBool b) +{ + if (b) + bRecenterMouse++; + else if (bRecenterMouse > 0) + bRecenterMouse--; +} + + +void plInputManager::RecenterCursor() +{ + RECT rect; + GetClientRect(fhWnd, &rect); + POINT pt; +// pt.y = ( (rect.bottom - rect.top) / 2 ) / fInstance->fMouseScale; +// pt.x = ( (rect.right - rect.left) / 2 ) / fInstance->fMouseScale; + ClientToScreen(fhWnd, &pt); + SetCursorPos( pt.x, pt.y ); +} +void plInputManager::CreateInterfaceMod(plPipeline* p) +{ + fInterfaceMgr = TRACKED_NEW plInputInterfaceMgr(); + fInterfaceMgr->Init(); +} + +void plInputManager::InitDInput(HINSTANCE hInst, HWND hWnd) +{ + if (fUseDInput) + { + fDInputMgr = TRACKED_NEW plDInputMgr; + fDInputMgr->Init(hInst, hWnd); + } +} + +hsBool plInputManager::MsgReceive(plMessage* msg) +{ + for (int i=0; iMsgReceive(msg)) + return true; + + if (fDInputMgr) + return fDInputMgr->MsgReceive(msg); + + return hsKeyedObject::MsgReceive(msg); +} + +void plInputManager::Update() +{ + if (fDInputMgr) + fDInputMgr->Update(); +} + +void plInputManager::SetMouseScale( hsScalar s ) +{ +/* RECT rect; + POINT currPos; + + + // Gotta make sure to move the mouse to the correct new position for the scale + GetClientRect( fhWnd, &rect ); + GetCursorPos( &currPos ); + ScreenToClient( fhWnd, &currPos ); + + float x = (float)currPos.x / rect.right; + float y = (float)currPos.y / rect.bottom; + + x *= fMouseScale; y *= fMouseScale; + + fMouseScale = s; + + // Refreshes all of the input devices so that they can reset mouse limits, etc + RECT rect2 = rect; + rect2.right /= fMouseScale; + rect2.bottom /= fMouseScale; + ::MapWindowPoints( fhWnd, NULL, (POINT *)&rect2, 2 ); + BOOL ret = ::ClipCursor( &rect ); + + // Now move the cursor to the right spot + + currPos.x = ( x / fMouseScale ) * rect.right; + currPos.y = ( y / fMouseScale ) * rect.bottom; + + ClientToScreen( fhWnd, &currPos ); + SetCursorPos( currPos.x, currPos.y ); +*/ +} + +// Sometimes the keyboard driver "helps" us translating a key involved in a key +// combo. For example pressing shif-numpad8 will actually generate a KEY_UP event, +// the same as the up arrow. This function undoes that translation. +plKeyDef plInputManager::UntranslateKey(plKeyDef key, hsBool extended) +{ + if (!extended) + { + if (key == KEY_UP) + return KEY_NUMPAD8; + if (key == KEY_DOWN) + return KEY_NUMPAD2; + if (key == KEY_LEFT) + return KEY_NUMPAD4; + if (key == KEY_RIGHT) + return KEY_NUMPAD6; + } + + return key; +} + +void plInputManager::HandleWin32ControlEvent(UINT message, WPARAM Wparam, LPARAM Lparam, HWND hWnd) +{ + if( !fhWnd ) + fhWnd = hWnd; + + hsBool bExtended; + + switch (message) + { + case SYSKEYDOWN: + case KEYDOWN: + { + bExtended = Lparam >> 24 & 1; + hsBool bRepeat = ((Lparam >> 29) & 0xf) != 0; + for (int i=0; iHandleKeyEvent( KEYDOWN, UntranslateKey((plKeyDef)Wparam, bExtended), true, bRepeat ); + } + break; + case SYSKEYUP: + case KEYUP: + { + bExtended = Lparam >> 24 & 1; + for (int i=0; iHandleKeyEvent( KEYUP, UntranslateKey((plKeyDef)Wparam, bExtended), false, false ); + } + break; + case MOUSEWHEEL: + { + plMouseEventMsg* pMsg = TRACKED_NEW plMouseEventMsg; + int zDelta = GET_WHEEL_DELTA_WPARAM(Wparam); + pMsg->SetWheelDelta((float)zDelta); + if (zDelta < 0) + pMsg->SetButton(kWheelNeg); + else + pMsg->SetButton(kWheelPos); + + RECT rect; + GetClientRect(hWnd, &rect); + pMsg->SetXPos(LOWORD(Lparam) / (float)rect.right); + pMsg->SetYPos(HIWORD(Lparam) / (float)rect.bottom); + + pMsg->Send(); + } + break; + case MOUSEMOVE: + case L_BUTTONDN: + case L_BUTTONUP: + case R_BUTTONDN: + case R_BUTTONUP: + case L_BUTTONDBLCLK: + case R_BUTTONDBLCLK: + case M_BUTTONDN: + case M_BUTTONUP: + { + + RECT rect; + GetClientRect(hWnd, &rect); + + plIMouseXEventMsg* pXMsg = TRACKED_NEW plIMouseXEventMsg; + plIMouseYEventMsg* pYMsg = TRACKED_NEW plIMouseYEventMsg; + plIMouseBEventMsg* pBMsg = TRACKED_NEW plIMouseBEventMsg; + + pXMsg->fWx = LOWORD(Lparam); + pXMsg->fX = (float)LOWORD(Lparam) / (float)rect.right; + pYMsg->fWy = HIWORD(Lparam); + pYMsg->fY = (float)HIWORD(Lparam) / (float)rect.bottom; + + // Apply mouse scale +// pXMsg->fX *= fMouseScale; +// pYMsg->fY *= fMouseScale; + + if (Wparam & MK_LBUTTON && message != L_BUTTONUP) + { + pBMsg->fButton |= kLeftButtonDown; + } + else + { + pBMsg->fButton |= kLeftButtonUp; + } + + if (Wparam & MK_RBUTTON && message != R_BUTTONUP) + { + pBMsg->fButton |= kRightButtonDown; + } + else + { + pBMsg->fButton |= kRightButtonUp; + } + + if (Wparam & MK_MBUTTON && message != M_BUTTONUP) + { + pBMsg->fButton |= kMiddleButtonDown; + } + else + { + pBMsg->fButton |= kMiddleButtonUp; + } + + if( message == L_BUTTONDBLCLK ) + pBMsg->fButton |= kLeftButtonDblClk; // We send the double clicks separately + if( message == R_BUTTONDBLCLK ) + pBMsg->fButton |= kRightButtonDblClk; + + for (int i=0; iMsgReceive(pXMsg); + fInputDevices[i]->MsgReceive(pYMsg); + fInputDevices[i]->MsgReceive(pBMsg); + } + POINT pt; + + if (RecenterMouse() && (pXMsg->fX <= 0.1 || pXMsg->fX >= 0.9) ) + { + pt.x = (rect.right - rect.left) / 2; + pt.y = HIWORD(Lparam); + ClientToScreen(hWnd, &pt); + SetCursorPos( pt.x, pt.y ); + } + else + if (RecenterMouse() && (pYMsg->fY <= 0.1 || pYMsg->fY >= 0.9) ) + { + pt.y = (rect.bottom - rect.top) / 2; + pt.x = LOWORD(Lparam); + ClientToScreen(hWnd, &pt); + SetCursorPos( pt.x, pt.y ); + } + if (RecenterMouse() && Lparam == 0) + { + pt.y = (rect.bottom - rect.top) / 2; + pt.x = (rect.right - rect.left) / 2; + ClientToScreen(hWnd, &pt); + SetCursorPos( pt.x, pt.y ); + } + delete(pXMsg); + delete(pYMsg); + delete(pBMsg); + + + + } + break; + case WM_ACTIVATE: + { + bool activated = ( LOWORD( Wparam ) == WA_INACTIVE ) ? false : true; + Activate( activated ); + } + break; + } + +} + +//// Activate //////////////////////////////////////////////////////////////// +// Handles what happens when the app (window) activates/deactivates + +void plInputManager::Activate( bool activating ) +{ + int i; + + + for( i = 0; i < fInputDevices.GetCount(); i++ ) + fInputDevices[ i ]->HandleWindowActivate( activating, fhWnd ); + + fActive = activating; + fFirstActivated = true; +} + +//// AddInputDevice ////////////////////////////////////////////////////////// + +void plInputManager::AddInputDevice( plInputDevice *pDev ) +{ + fInputDevices.Append( pDev ); + if( fFirstActivated ) + pDev->HandleWindowActivate( fActive, fhWnd ); +} + +// +// +// dinput manager +// +// + + +plDInputMgr::plDInputMgr() : +fDI(nil) +{ + fDI = TRACKED_NEW plDInput; +} + +plDInputMgr::~plDInputMgr() +{ + if (fDI) + { + for (int i = 0; i < fDI->fSticks.Count(); i++) + { + plDIDevice* pD = fDI->fSticks[i]; + pD->fDevice->Release(); + delete(pD->fCaps); + delete(pD); + } + fDI->fSticks.SetCountAndZero(0); + delete(fDI->fActionFormat); + fDI->fDInput->Release(); + for(int j = 0; j < fInputDevice.Count(); j++) + delete(fInputDevice[j]); + fInputDevice.SetCountAndZero(0); + delete fDI; + } +} + + +void plDInputMgr::Init(HINSTANCE hInst, HWND hWnd) +{ + + HRESULT hr; + hr = DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&fDI->fDInput, NULL); + hsAssert(!hr, "failed to initialize directInput!"); + // enumerate game controllers + Pfunc1 fPtr = &plDInputMgr::EnumGamepadCallback; + int i = 0; + + + // set up the action mapping + fDI->fActionFormat = TRACKED_NEW DIACTIONFORMAT; + fDI->fActionFormat->dwSize = sizeof(DIACTIONFORMAT); + fDI->fActionFormat->dwActionSize = sizeof(DIACTION); + fDI->fActionFormat->dwDataSize = NUM_ACTIONS * sizeof(DWORD); + fDI->fActionFormat->dwNumActions = NUM_ACTIONS; + fDI->fActionFormat->guidActionMap = PL_ACTION_GUID; + fDI->fActionFormat->dwGenre = DIVIRTUAL_FIGHTING_THIRDPERSON; + fDI->fActionFormat->rgoAction = fActionMap; + fDI->fActionFormat->dwBufferSize = 16; + fDI->fActionFormat->lAxisMin = -1000; + fDI->fActionFormat->lAxisMax = 1000; + sprintf( fDI->fActionFormat->tszActionMap, "Plasma 2.0 Game Actions" ); + + // this call should not work: + fDI->fDInput->EnumDevices(DI8DEVCLASS_GAMECTRL, fPtr, fDI, DIEDFL_ATTACHEDONLY); + + // apply the mapping to the game controller + // this is the correct way to apply the action map: +// Pfunc3 fPtr3 = &plDInputMgr::EnumSuitableDevices; +// hr = fDI->fDInput->EnumDevicesBySemantics(NULL, fDI->fActionFormat, EnumSuitableDevices, fDI, NULL); + + + for (i = 0; i < fDI->fSticks.Count(); i++) + { + fDI->fSticks[i]->fCaps = TRACKED_NEW DIDEVCAPS; + fDI->fSticks[i]->fCaps->dwSize = sizeof(DIDEVCAPS); + hr = fDI->fSticks[i]->fDevice->GetCapabilities(fDI->fSticks[i]->fCaps); + hsAssert(!hr, "Unable to acquire devcaps in DInput Device!"); + hr = fDI->fSticks[i]->fDevice->Acquire(); + hsAssert(!hr, "Unable to acquire DInput Device!"); + } + + fhWnd = hWnd; + + for (i = 0; i < fDI->fSticks.Count(); i++) + fInputDevice.Append( TRACKED_NEW plDInputDevice ); +} + +void plDInputMgr::Update() +{ + HRESULT hr; + + if (!fDI->fSticks.Count()) + return; + + // Poll the devices to read the current state + for (int i = 0; i < fDI->fSticks.Count(); i++) + { + hr = fDI->fSticks[i]->fDevice->Poll(); + if (FAILED(hr)) + { + // Attempt to reacquire joystick + while(hr == DIERR_INPUTLOST) + { + hr = fDI->fSticks[i]->fDevice->Acquire(); + char str[256]; + sprintf(str, "DInput Device # %d connection lost - press Ignore to attempt to reacquire!", i); + hsAssert(!hr, str); + } + } + + DIDEVICEOBJECTDATA data; + ULONG size = 1; + hr = fDI->fSticks[i]->fDevice->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),&data,&size,0); + + fInputDevice[i]->Update(&data); + } +} + +void plDInputMgr::AddDevice(IDirectInputDevice8* device) +{ + HRESULT hr = device->BuildActionMap(fDI->fActionFormat, NULL, NULL); + if (!FAILED(hr)) + device->SetActionMap( fDI->fActionFormat, NULL, NULL ); +} + +void plDInputMgr::ConfigureDevice() +{ + ::ClipCursor(nil); + ::ShowCursor( TRUE ); + ReleaseCapture(); + + + DICOLORSET dics; + ZeroMemory(&dics, sizeof(DICOLORSET)); + dics.dwSize = sizeof(DICOLORSET); + + DICONFIGUREDEVICESPARAMS dicdp; + ZeroMemory(&dicdp, sizeof(dicdp)); + dicdp.dwSize = sizeof(dicdp); + dicdp.dwcUsers = 1; + dicdp.lptszUserNames = NULL; + + dicdp.dwcFormats = 1; + dicdp.lprgFormats = fDI->fActionFormat; + dicdp.hwnd = fhWnd; + dicdp.lpUnkDDSTarget = NULL; + + fDI->fDInput->ConfigureDevices(NULL, &dicdp, DICD_EDIT, NULL); + for (int i = 0; i < fDI->fSticks.Count(); i++) + fDI->fSticks[i]->fDevice->SetActionMap( fDI->fActionFormat, NULL, DIDSAM_FORCESAVE ); + + RECT rect; + ::GetClientRect(fhWnd,&rect); + ::ClientToScreen(fhWnd,(LPPOINT)&rect); + ::ClipCursor(&rect); + ::ShowCursor( FALSE ); + SetCapture(fhWnd); + +} + +hsBool plDInputMgr::MsgReceive(plMessage* msg) +{ + plInputEventMsg* pMsg = plInputEventMsg::ConvertNoRef(msg); + if (pMsg && pMsg->fEvent == plInputEventMsg::kConfigure) + { + ConfigureDevice(); + } + return false; +} + +// dinput required callback functions: + +// enumerate the dinput devices +int __stdcall plDInputMgr::EnumGamepadCallback(const DIDEVICEINSTANCE* device, void* pRef) +{ + HRESULT hr; + + plDInput* pDI = (plDInput*)pRef; + IDirectInputDevice8* fStick = nil; + hr = pDI->fDInput->CreateDevice(device->guidInstance, &fStick, NULL); + + if(!FAILED(hr)) + { + pDI->fSticks.Append(TRACKED_NEW plDIDevice(fStick)); + + // the following code pertaining to the action map shouldn't be here. + // in fact this shouldn't work at all according to MS, but this is + // currently the only way this works. Whatever - the correct + // code is here and commented out in case this ever gets fixed by MS + // in a future release of dinput. + HRESULT hr = fStick->BuildActionMap(pDI->fActionFormat, NULL, NULL); + if (!FAILED(hr)) + { + hr = fStick->SetActionMap( pDI->fActionFormat, NULL, NULL ); + + DIPROPDWORD dipW; + dipW.diph.dwSize = sizeof(DIPROPDWORD); + dipW.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipW.diph.dwHow = DIPH_DEVICE; + dipW.diph.dwObj = 0; + dipW.dwData = 500; // 5% of axis range for deadzone + + hr = fStick->SetProperty(DIPROP_DEADZONE , &dipW.diph); + } + return DIENUM_CONTINUE; + } + return DIENUM_STOP; +} + +// look for axes on the controller and set the output range to +-100 +// apparently not needed with action mapping: +/* +int __stdcall plDInputMgr::SetAxisRange(const DIDEVICEOBJECTINSTANCE* obj, void* pRef) +{ + HRESULT hr; + DIPROPRANGE diprg; + + diprg.diph.dwSize = sizeof(DIPROPRANGE); + diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); + diprg.diph.dwHow = DIPH_BYID; + diprg.diph.dwObj = obj->dwType; + diprg.lMin = -100; + diprg.lMax = +100; + + plDInput* pDI = (plDInput*)pRef; + for (int i = 0; i < pDI->fSticks.Count(); i++) + hr = pDI->fSticks[i]->fDevice->SetProperty(DIPROP_RANGE, &diprg.diph); + + if(!FAILED(hr)) + return DIENUM_CONTINUE; + + return DIENUM_STOP; + +} +*/ + +// apply mapping to controller +// not used. why? no one really knows. +// leave this here in case dinput ever gets fixed... +/* +int __stdcall plDInputMgr::EnumSuitableDevices(const struct DIDEVICEINSTANCEA* devInst, struct IDirectInputDevice8* dev, unsigned long why, unsigned long devRemaining, void* pRef) +{ + plDInput* pDI = (plDInput*)pRef; + HRESULT hr = dev->BuildActionMap(pDI->fActionFormat, NULL, NULL); + if (!FAILED(hr)) + { + hr = dev->SetActionMap( pDI->fActionFormat, NULL, NULL ); + } + return DIENUM_STOP; +} +*/ +DIACTION plDInputMgr::fActionMap[NUM_ACTIONS] = +{ + {A_CONTROL_MOVE, DIAXIS_TPS_MOVE, 0, "Walk Forward-Backward" ,}, + {A_CONTROL_TURN, DIAXIS_TPS_TURN, 0, "Turn Left-Right" ,}, + {A_CONTROL_MOUSE_X, DIAXIS_ANY_1, 0, "Move Camera Left-Right",}, + {A_CONTROL_MOUSE_Y, DIAXIS_ANY_2, 0, "Move Camera Up-Down" ,}, + {B_CONTROL_ACTION, DIBUTTON_TPS_ACTION, 0, "Action" ,}, + {B_CONTROL_JUMP, DIBUTTON_TPS_JUMP, 0, "Jump" ,}, + {B_CONTROL_STRAFE_LEFT, DIBUTTON_TPS_STEPLEFT, 0, "Strafe Left" ,}, + {B_CONTROL_STRAFE_RIGHT, DIBUTTON_TPS_STEPRIGHT, 0, "Strafe Right" ,}, + {B_CONTROL_MODIFIER_FAST, DIBUTTON_TPS_RUN, 0, "Run" ,}, + {B_CONTROL_EQUIP, DIBUTTON_TPS_SELECT, 0, "Equip Item" ,}, + {B_CONTROL_DROP, DIBUTTON_TPS_USE, 0, "Drop Item" ,}, + {B_CONTROL_MOVE_FORWARD, DIBUTTON_ANY(0), 0, "Walk Forward" ,}, + {B_CONTROL_MOVE_BACKWARD, DIBUTTON_ANY(1), 0, "Walk Backward" ,}, + {B_CONTROL_ROTATE_LEFT, DIBUTTON_ANY(2), 0, "Turn Left" ,}, + {B_CONTROL_ROTATE_RIGHT, DIBUTTON_ANY(3), 0, "Turn Right" ,}, + {B_CONTROL_TURN_TO, DIBUTTON_ANY(4), 0, "Pick Item" ,}, + {B_CAMERA_RECENTER, DIBUTTON_ANY(5), 0, "Recenter Camera" ,}, +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputManager.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputManager.h new file mode 100644 index 00000000..39c0f9cf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plInputManager.h @@ -0,0 +1,96 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// plInputManager.h + +#ifndef PL_INPUT_MANAGER_H +#define PL_INPUT_MANAGER_H + +#include +#include "hsTypes.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnInputCore/plInputMap.h" + +class plDInputMgr; +class plInputDevice; +class plDInputDevice; +class plInputInterfaceMgr; +class plPipeline; + +class plInputManager :public hsKeyedObject +{ +private: + static hsBool fUseDInput; +public: + plInputManager(); + plInputManager( HWND hWnd ); + ~plInputManager(); + + CLASSNAME_REGISTER( plInputManager ); + GETINTERFACE_ANY( plInputManager, hsKeyedObject ); + + + void AddInputDevice(plInputDevice* pDev); + void InitDInput(HINSTANCE hInst, HWND hWnd); + + static void UseDInput(hsBool b) { fUseDInput = b; } + void Update(); + static plInputManager* GetInstance() { return fInstance; } + static plInputManager* fInstance; + virtual hsBool MsgReceive(plMessage* msg); + static hsBool RecenterMouse() { return bRecenterMouse > 0; } + static void SetRecenterMouse(hsBool b); + static void RecenterCursor(); + void CreateInterfaceMod(plPipeline* p); + + void Activate( bool activating ); + + hsScalar GetMouseScale( void ) const { return fMouseScale; } + void SetMouseScale( hsScalar s ); + + static plKeyDef UntranslateKey(plKeyDef key, hsBool extended); + +protected: + + hsTArray fInputDevices; + plDInputMgr* fDInputMgr; + plInputInterfaceMgr *fInterfaceMgr; + bool fActive, fFirstActivated; + + hsScalar fMouseScale; + static UInt8 bRecenterMouse; + static HWND fhWnd; + +public: + // event handlers + void HandleWin32ControlEvent(UINT message, WPARAM Wparam, LPARAM Lparam, HWND hWnd); +}; + + +// {049DE53E-23A2-4d43-BF68-36AC1B57E357} +static const GUID PL_ACTION_GUID = { 0x49de53e, 0x23a2, 0x4d43, { 0xbf, 0x68, 0x36, 0xac, 0x1b, 0x57, 0xe3, 0x57 } }; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp new file mode 100644 index 00000000..6003f03b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.cpp @@ -0,0 +1,1226 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSceneInputInterface // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsConfig.h" +#include "hsWindows.h" + +#include "hsTypes.h" +#include "plSceneInputInterface.h" + +#include "plInputInterfaceMgr.h" +#include "plInputManager.h" +#include "plInputDevice.h" + +#include "../plPhysical/plPickingDetector.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plLOSRequestMsg.h" +#include "../plMessage/plLOSHitMsg.h" +#include "../plMessage/plPickedMsg.h" +#include "../plMessage/plRenderMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../plMessage/plVaultNotifyMsg.h" +#include "../pnMessage/plFakeOutMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnMessage/plRemoteAvatarInfoMsg.h" +#include "../pnMessage/plCursorChangeMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pnMessage/plPlayerPageMsg.h" +#include "../pnMessage/plCmdIfaceModMsg.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvBrain.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plAvCallbackAction.h" +#include "../plModifier/plInterfaceInfoModifier.h" +#include "../pnModifier/plLogicModBase.h" +#include "../plVault/plVault.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetClient/plNetLinkingMgr.h" +#include "../plNetCommon/plNetServerSessionInfo.h" +#include "../plNetTransport/plNetTransport.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnInputCore/plKeyMap.h" +#include "plPhysical.h" + +#include "plgDispatch.h" +#include "plPipeline.h" + +#include "../plModifier/plDetectorLog.h" + + +#define ID_FIND_CLICKABLE 2 +#define ID_FIND_LOCALPLAYER 3 +#define ID_FIND_WALKABLE_GROUND 4 + +#define SHARE_FACING_TOLERANCE -0.70f // 45 degrees + +plSceneInputInterface *plSceneInputInterface::fInstance = nil; + +hsBool plSceneInputInterface::fShowLOS = false; + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plSceneInputInterface::plSceneInputInterface() +{ + fPipe = nil; + fSpawnPoint = nil; + GuidClear(&fAgeInstanceGuid); + fInstance = this; + SetEnabled( true ); // Always enabled +} + +plSceneInputInterface::~plSceneInputInterface() +{ + ClearClickableMap(); + fIgnoredAvatars.Reset(); + fLocalIgnoredAvatars.Reset(); + fGUIIgnoredAvatars.Reset(); + fInstance = nil; +} + + +//// Init/Shutdown /////////////////////////////////////////////////////////// + +void plSceneInputInterface::Init( plInputInterfaceMgr *manager ) +{ + plInputInterface::Init( manager ); + + // To get the pipeline + fPipe = nil; + plgDispatch::Dispatch()->RegisterForExactType( plRenderMsg::Index(), fManager->GetKey() ); + plgDispatch::Dispatch()->RegisterForExactType( plInputIfaceMgrMsg::Index(), fManager->GetKey() ); + plgDispatch::Dispatch()->RegisterForExactType( plPlayerPageMsg::Index(), fManager->GetKey() ); + + fCurrentClickable = nil; + fCurrentClickableLogicMod = nil; + fLastClicked = nil; + fButtonState = 0; + fClickability = 1; // Hack for clickable avatars, we need to always do the LOS check + fLastClickIsAvatar = false; + fCurrClickIsAvatar = false; + fFadedLocalAvatar = false; + fBookMode = kNotOffering; + fOffereeKey = nil; + fPendingLink = false; + + // register for control messages + plCmdIfaceModMsg* pModMsg = TRACKED_NEW plCmdIfaceModMsg; + pModMsg->SetBCastFlag(plMessage::kBCastByExactType); + pModMsg->SetSender(fManager->GetKey()); + pModMsg->SetCmd(plCmdIfaceModMsg::kAdd); + plgDispatch::MsgSend(pModMsg); + +} + +void plSceneInputInterface::Shutdown( void ) +{ + if( fPipe == nil ) + plgDispatch::Dispatch()->UnRegisterForExactType( plRenderMsg::Index(), fManager->GetKey() ); + else + fPipe = nil; +} + +void plSceneInputInterface::ClearClickableMap() +{ + for (int i = 0; i < fClickableMap.Count(); i++) + { + clickableTest* pTest = fClickableMap[i]; + delete(pTest); + } + fClickableMap.SetCountAndZero(0); +} + +//// IHalfFadeAvatar ///////////////////////////////////////////////////////// + +void plSceneInputInterface::IHalfFadeAvatar(hsBool out) +{ + plIfaceFadeAvatarMsg* pMsg = TRACKED_NEW plIfaceFadeAvatarMsg(); + pMsg->SetSubjectKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + pMsg->SetBCastFlag(plMessage::kBCastByExactType); + pMsg->SetBCastFlag(plMessage::kNetPropagate, FALSE); + pMsg->SetFadeOut(out); + pMsg->Send(); + fFadedLocalAvatar = out; + +} + + +void plSceneInputInterface::ResetClickableState() +{ + if( fLastClicked != nil ) + ISetLastClicked( nil, hsPoint3(0,0,0) ); + + ClearClickableMap(); + fCurrentClickable = nil; + fCurrentClickableLogicMod = nil; + fCurrentCursor = SetCurrentCursorID(kNullCursor); + fCurrClickIsAvatar = false; + +} +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool plSceneInputInterface::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + // this needs to always go no matter what... + // ...unless we have cliclability disabled (as in the case of certain multistage behaviors) + if (plMouseDevice::Instance()->GetCursorOpacity() > 0.f && !plMouseDevice::Instance()->GetHideCursor()) + { + IRequestLOSCheck( plMouseDevice::Instance()->GetCursorX(), plMouseDevice::Instance()->GetCursorY(), ID_FIND_LOCALPLAYER ); + if (fClickability) + IRequestLOSCheck( plMouseDevice::Instance()->GetCursorX(), plMouseDevice::Instance()->GetCursorY(), ID_FIND_CLICKABLE ); + } + else + if (fFadedLocalAvatar) + IHalfFadeAvatar(false); + +// if (!fCurrClickIsAvatar) +// fCurrentCursor = SetCurrentCursorID(kNullCursor); + // ping for possible cursor changes + int i; + for (i=0; i < fClickableMap.Count(); i++) + { + plFakeOutMsg *pMsg = TRACKED_NEW plFakeOutMsg; + pMsg->SetSender( fManager->GetKey() ); + pMsg->AddReceiver( fClickableMap[i]->key ); + plgDispatch::MsgSend( pMsg ); + } + // then see if we have any + hsBool change = false; + for (i=0; i < fClickableMap.Count(); i++) + { + if( fClickableMap[i]->val ) + { + change = true; + break; + } + } + if (change) + { + if( fLastClicked != nil ) + fCurrentCursor = SetCurrentCursorID(kCursorClicked); + else + fCurrentCursor = SetCurrentCursorID(kCursorPoised); + } + return true; +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool plSceneInputInterface::MsgReceive( plMessage *msg ) +{ + plLOSHitMsg *pLOSMsg = plLOSHitMsg::ConvertNoRef( msg ); + if( pLOSMsg ) + { + if( pLOSMsg->fRequestID == ID_FIND_CLICKABLE ) + { + hsBool clearCursor = false; + if (!fClickability) + return true; + if( pLOSMsg->fObj ) + { + // is this object clickable? + plSceneObject *pObj = plSceneObject::ConvertNoRef( pLOSMsg->fObj->ObjectIsLoaded() ); + if( pObj ) + { + if (fShowLOS) + { + if (pLOSMsg->fNoHit) + DetectorLogSpecial("%s: LOS miss", pObj->GetKeyName()); + else + DetectorLogSpecial("%s: LOS hit", pObj->GetKeyName()); + } + int i; + const plInterfaceInfoModifier* pMod = 0; + for( i = 0; i < pObj->GetNumModifiers(); i++ ) + { + if (fBookMode == kNotOffering) // when sharing a book we don't care about other clickables + { + pMod = plInterfaceInfoModifier::ConvertNoRef( pObj->GetModifier(i) ); + if (pMod) // we found our list, stop here + { + plLogicModBase* pLogicMod = (plLogicModBase*)pObj->GetModifierByType(plLogicModBase::Index()); + if (!pLogicMod) + return true; + + if (fCurrentClickable != pObj->GetKey()) + { // is it the current clickable already? + ClearClickableMap(); + fCurrentCursor = SetCurrentCursorID(kNullCursor); + fCurrentClickable = pObj->GetKey(); + fCurrentClickableLogicMod = pLogicMod->GetKey(); + fCurrentClickPoint = pLOSMsg->fHitPoint; + for (int x = 0; x < pMod->GetNumReferencedKeys(); x++) + fClickableMap.Append( TRACKED_NEW clickableTest(pMod->GetReferencedKey(x))); + } + else + { + // even if this is still the same clickable object, the cursor could be + // ...at a different spot on the clickable, so save that + fCurrentClickPoint = pLOSMsg->fHitPoint; + } + fCurrClickIsAvatar = false; + return true; + } + } + + // see if it is an avatar + plArmatureMod* armMod = (plArmatureMod*)plArmatureMod::ConvertNoRef( pObj->GetModifier(i)); + if (armMod) + { + if (armMod->IsMidLink()) + return true; + + // okay, are we a CCR? + hsBool amCCR = plNetClientMgr::GetInstance()->GetCCRLevel(); + + // is this person a NPC or CCR? + int mbrIdx=plNetClientMgr::GetInstance()->TransportMgr().FindMember(pObj->GetKey()); + plNetTransportMember* pMbr = plNetClientMgr::GetInstance()->TransportMgr().GetMember(mbrIdx); + if (!pMbr) // whoops - it's a freakin' NPC ! + return true; + + if (pMbr->IsCCR()) + { + if (amCCR) + { + // we can click on them + plMouseDevice::AddCCRToCursor(); + } + else + { + // nope + return true; + } + + } + // now, if I am a CCR, let me click on anyone at any time + if (amCCR) + { + ClearClickableMap(); + fCurrentClickable = pObj->GetKey(); + fCurrentClickableLogicMod = nil; + fCurrClickIsAvatar = true; + fCurrentCursor = SetCurrentCursorID(kCursorPoised); + // not sure why we need to point on the avatar... + // ...but maybe something in the future will need this + fCurrentClickPoint = pLOSMsg->fHitPoint; + plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(fCurrentClickable)); + // also add their player ID to the cursor + plMouseDevice::AddIDNumToCursor(pMbr->GetPlayerID()); + return true; + } + // otherwise, cull people as necessary + // also make sure that they are not in our ignore list + else if (VaultAmIgnoringPlayer( pMbr->GetPlayerID())) + return true; + // further, if we are offering a book, only allow clicks on the person + // whom we've already offered it to (to cancel it) + else if (fBookMode == kBookOffered && pObj->GetKey() != fOffereeKey) + return true; + // within distance + // also... make sure they aren't off climbing a ladder or looking at their KI + else if (fBookMode == kOfferBook) + { + plArmatureBrain* curBrain = armMod->GetCurrentBrain(); + if (curBrain) + { + if (curBrain->IsRunningTask()) + { + fCurrentCursor = SetCurrentCursorID(kCursorClickDisabled); + plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(pObj->GetKey())); + return true; + } + } + plAvatarMgr* aMgr = plAvatarMgr::GetInstance(); + if (aMgr) + { + if (aMgr->IsACoopRunning()) + { + fCurrentCursor = SetCurrentCursorID(kCursorClickDisabled); + plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(pObj->GetKey())); + return true; + } + } + plSceneObject* locPlayer = (plSceneObject*)plNetClientMgr::GetInstance()->GetLocalPlayer(); + // make sure that they are facing each other + if ( locPlayer ) + { + hsVector3 ourView = locPlayer->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + hsVector3 theirView = pObj->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + hsScalar viewdot = ourView * theirView; + hsVector3 towards(locPlayer->GetCoordinateInterface()->GetLocalToWorld().GetTranslate() - pObj->GetCoordinateInterface()->GetLocalToWorld().GetTranslate()); + towards.Normalize(); + hsScalar towardsdot = ourView * towards; + if (viewdot > SHARE_FACING_TOLERANCE || towardsdot > SHARE_FACING_TOLERANCE ) + { + ResetClickableState(); + return true; // not facing enough... reject + } + } + //otherwise make sure that they are close enough to click on + if (locPlayer) + { + hsPoint3 avPt = locPlayer->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + hsPoint3 objPt = pObj->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); + hsVector3 dist(avPt - objPt); + if ( dist.MagnitudeSquared() >= 16.0f ) // you are too far away + { + ResetClickableState(); + return true; + } + + if (hsABS(avPt.fZ - objPt.fZ) > 1.0f) // you need to also be in the same plane (some books are on top of rocks you need to jump onto) + { + ResetClickableState(); + return true; + } + } + } + // finally - make sure this guy is not in our ignore lists. + int x; + for (x = 0; x < fIgnoredAvatars.Count(); x++) + { + if (fIgnoredAvatars[x] == pObj->GetKey()) + { + fCurrentCursor = SetCurrentCursorID(kCursorClickDisabled); + plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(pObj->GetKey())); + return true; + } + } + for (x = 0; x < fGUIIgnoredAvatars.Count(); x++) + { + if (fGUIIgnoredAvatars[x] == pObj->GetKey()) + { + fCurrentCursor = SetCurrentCursorID(kCursorClickDisabled); + plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(pObj->GetKey())); + return true; + } + } + + ClearClickableMap(); + fCurrentClickable = pObj->GetKey(); + fCurrentClickableLogicMod = nil; + fCurrClickIsAvatar = true; + fCurrentCursor = SetCurrentCursorID(kCursorPoised); + // not sure why we need to point on the avatar... + // ...but maybe something in the future will need this + fCurrentClickPoint = pLOSMsg->fHitPoint; + plMouseDevice::AddNameToCursor(plNetClientMgr::GetInstance()->GetPlayerName(fCurrentClickable)); + return true; + } + } + // here! it's an object which is not clickable + // no object, or not clickable or avatar + + fCurrentClickPoint = pLOSMsg->fHitPoint; + ResetClickableState(); + return false; + } + } + // no object, or not clickable or avatar + ResetClickableState(); + } + else + if( pLOSMsg->fRequestID == ID_FIND_LOCALPLAYER ) + { + bool result = false; + if( pLOSMsg->fObj ) + { + // is this object clickable? + plSceneObject *pObj = plSceneObject::ConvertNoRef( pLOSMsg->fObj->ObjectIsLoaded() ); + if( pObj ) + { + if (pObj == plNetClientMgr::GetInstance()->GetLocalPlayer()) + result = true; + } + } + if (result && !fFadedLocalAvatar) + { + IHalfFadeAvatar(true); + return true; + } + else + if (!result && fFadedLocalAvatar) + { + IHalfFadeAvatar(false); + return true; + } + } + if( pLOSMsg->fRequestID == ID_FIND_WALKABLE_GROUND ) + { + if (!pLOSMsg->fNoHit) + { + plAvatarMgr::GetInstance()->GetLocalAvatar()->TurnToPoint(pLOSMsg->fHitPoint); + } + return true; + } + + return true; + } + + plCursorChangeMsg *fakeReplyMsg = plCursorChangeMsg::ConvertNoRef( msg ); + if( fakeReplyMsg != nil ) + { + hsBool deniedCurrent = false; + plKey key = fakeReplyMsg->GetSender(); + for (int i = 0; i < fClickableMap.Count(); i++) + { + if (fClickableMap[i]->key == key) + { + if( fakeReplyMsg->fType == plCursorChangeMsg::kNullCursor ) + { + // Means not clickable--gotta fix this someday + fClickableMap[i]->val = false; + if (fClickableMap[i]->key == fCurrentClickableLogicMod) + { + deniedCurrent = true; + break; + } + } + else + { + // And fix this... + fClickableMap[i]->val = true; + } + } + } + if (deniedCurrent) + ResetClickableState(); + return true; + } + + plRenderMsg *rMsg = plRenderMsg::ConvertNoRef( msg ); + if( rMsg != nil ) + { + fPipe = rMsg->Pipeline(); + plgDispatch::Dispatch()->UnRegisterForExactType( plRenderMsg::Index(), fManager->GetKey() ); + return true; + } + // reply from coop share book multistage + plNotifyMsg* pNMsg = plNotifyMsg::ConvertNoRef(msg); + if (pNMsg) + { + for(int x=0; x < pNMsg->GetEventCount();x++) + { + proEventData* pED = pNMsg->GetEventRecord(0); + if ( pED->fEventType == proEventData::kMultiStage ) + { + proMultiStageEventData* pMS = (proMultiStageEventData*)pED; + if (pMS->fAvatar == fOffereeKey) // mojo has linked + { + // do something - they linked out but we are still in the multistage + fOffereeKey = nil; + } + else + if (pMS->fAvatar == plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + { + // do something else + if (fBookMode = kNotOffering && fPendingLink == false) // we just linked out + { + // make me clickable again + ISendAvatarDisabledNotification(true); + } + else // we put the book back after our target linked out + { + fBookMode = kNotOffering; + fPendingLink = false; + // make ME clickable again + ISendAvatarDisabledNotification(true); + } + } + return true; + } + } + return false; + + } + // if someone pages out / in, remove them from our ignore list or notify them to ignore us + plPlayerPageMsg* pPlayerMsg = plPlayerPageMsg::ConvertNoRef(msg); + if (pPlayerMsg) + { + if (pPlayerMsg->fUnload) + { + int x; + // first, remove this avatar from my list of avatars I ingore for clickable griefing (when the 'ignore avatars' key is pressed) + for(x = 0; x < fLocalIgnoredAvatars.Count(); x++) + { + if (fLocalIgnoredAvatars[x] == pPlayerMsg->fPlayer) + fLocalIgnoredAvatars.RemoveItem(pPlayerMsg->fPlayer); + } + // now deal with avatars we are always ignoring because of their current activity + for(x = 0; x < fIgnoredAvatars.Count(); x++) + { + if (fIgnoredAvatars[x] == pPlayerMsg->fPlayer) + fIgnoredAvatars.RemoveItem(pPlayerMsg->fPlayer); + } + for(x = 0; x < fGUIIgnoredAvatars.Count(); x++) + { + if (fGUIIgnoredAvatars[x] == pPlayerMsg->fPlayer) + fGUIIgnoredAvatars.RemoveItem(pPlayerMsg->fPlayer); + } + if (fOffereeKey == pPlayerMsg->fPlayer) + { + if (fBookMode == kBookOffered) + { + // and put our own dialog back up... + ISendOfferNotification(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), 0, false); + //IManageIgnoredAvatars(fOffereeKey, false); + fOffereeKey = nil; + fBookMode = kNotOffering; + ISendAvatarDisabledNotification(true); + } + } + } + else + { + // add them to the list we keep of everyone here: + // but DO NOT add the local avatar + if (pPlayerMsg->fPlayer != plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + fLocalIgnoredAvatars.Append(pPlayerMsg->fPlayer); + if (fBookMode != kNotOffering) + { + // tell them to ignore us + + plInputIfaceMgrMsg* pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableAvatarClickable); + pMsg->SetAvKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + pMsg->SetBCastFlag(plMessage::kLocalPropagate, false); + pMsg->AddNetReceiver( pPlayerMsg->fClientID ); + pMsg->Send(); + + // and tell them to ignore our victim + + //plInputIfaceMgrMsg* pMsg2 = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableAvatarClickable); + //pMsg2->SetAvKey(fOffereeKey); + //pMsg2->SetBCastFlag(plMessage::kNetPropagate); + //pMsg2->SetBCastFlag(plMessage::kNetForce); + //pMsg2->SetBCastFlag(plMessage::kLocalPropagate, false); + //pMsg2->AddNetReceiver( pPlayerMsg->fClientID ); + //pMsg2->Send(); + + } + // tell them to ingore us if we are looking at a GUI + for(int x = 0; x < fGUIIgnoredAvatars.Count(); x++) + { + if (fGUIIgnoredAvatars[x] == plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + { + plInputIfaceMgrMsg* pMsg3 = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kGUIDisableAvatarClickable); + pMsg3->SetAvKey(fGUIIgnoredAvatars[x]); + pMsg3->SetBCastFlag(plMessage::kNetPropagate); + pMsg3->SetBCastFlag(plMessage::kNetForce); + pMsg3->SetBCastFlag(plMessage::kLocalPropagate, false); + pMsg3->AddNetReceiver( pPlayerMsg->fClientID ); + pMsg3->Send(); + return true; + } + } + } + } + + plInputIfaceMgrMsg *mgrMsg = plInputIfaceMgrMsg::ConvertNoRef( msg ); + if( mgrMsg != nil ) + { + if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kDisableAvatarClickable ) + { + // ignore if already in list or this is who WE are offering the book to... + if (mgrMsg->GetAvKey() == fOffereeKey) + return true; + for(int x = 0; x < fIgnoredAvatars.Count(); x++) + { + if (fIgnoredAvatars[x] == mgrMsg->GetAvKey()) + return true; + } + fIgnoredAvatars.Append(mgrMsg->GetAvKey()); + } + else + if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kEnableAvatarClickable ) + { + for(int x = 0; x < fIgnoredAvatars.Count(); x++) + { + if (fIgnoredAvatars[x] == mgrMsg->GetAvKey()) + fIgnoredAvatars.RemoveItem(mgrMsg->GetAvKey()); + } + } + else + if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kGUIDisableAvatarClickable ) + { + // ignore if already in list or this is who WE are offering the book to... + if (mgrMsg->GetAvKey() == fOffereeKey) + return true; + for(int x = 0; x < fGUIIgnoredAvatars.Count(); x++) + { + if (fGUIIgnoredAvatars[x] == mgrMsg->GetAvKey()) + return true; + } + fGUIIgnoredAvatars.Append(mgrMsg->GetAvKey()); + } + else + if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kGUIEnableAvatarClickable ) + { + for(int x = 0; x < fGUIIgnoredAvatars.Count(); x++) + { + if (fGUIIgnoredAvatars[x] == mgrMsg->GetAvKey()) + fGUIIgnoredAvatars.RemoveItem(mgrMsg->GetAvKey()); + } + } + else + if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kEnableClickables ) + { + fClickability = true; + return true; + } + else if( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kDisableClickables ) + { + fClickability = false; + ResetClickableState(); + return true; + } + else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kSetOfferBookMode ) + { + fBookMode = kOfferBook; + fOffereeKey = nil; + fBookKey = mgrMsg->GetSender(); + fOfferedAgeInstance = mgrMsg->GetAgeName(); + fOfferedAgeFile = mgrMsg->GetAgeFileName(); + ISendAvatarDisabledNotification(false); + + } + else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kClearOfferBookMode ) + { + if (fBookMode == kOfferAccepted || fBookMode == kOfferLinkPending) + { + fPendingLink = true; + } + else + if (fOffereeKey != nil) + { + // notify any offeree that the offer is rescinded + ISendOfferNotification(fOffereeKey, -999, true); + //IManageIgnoredAvatars(fOffereeKey, false); + fOffereeKey = nil; + } + // shut down offer book mode + fBookMode = kNotOffering; + ISendAvatarDisabledNotification(true); + } + else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kNotifyOfferRejected) + { + if (fBookMode == kBookOffered) + { + // and put our own dialog back up... + ISendOfferNotification(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), 0, false); + //IManageIgnoredAvatars(fOffereeKey, false); + fBookMode = kOfferBook; + fOffereeKey = nil; + } + else + if (mgrMsg->GetSender() == plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + { + fBookMode = kNotOffering; + ISendAvatarDisabledNotification(true); + } + } + else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kNotifyOfferAccepted && fBookMode == kBookOffered) + { + fBookMode = kOfferAccepted; + } + else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kNotifyOfferCompleted ) + { + // must have actually offered the book... + if (!fPendingLink) + { + if (fBookMode == kOfferBook || fBookMode == kBookOffered) + return true; + } + if (!plNetClientMgr::GetInstance()) + return true; + + fOffereeID = mgrMsg->GetPageID(); + ILinkOffereeToAge(); + } + else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kSetShareSpawnPoint ) + { + fSpawnPoint = mgrMsg->GetSpawnPoint(); + } + else if ( mgrMsg->GetCommand() == plInputIfaceMgrMsg::kSetShareAgeInstanceGuid ) + { + fAgeInstanceGuid = mgrMsg->GetAgeInstanceGuid(); + } + } + plVaultNotifyMsg* pVaultMsg = plVaultNotifyMsg::ConvertNoRef(msg); + if (pVaultMsg && pVaultMsg->GetType()==plNetCommon::VaultTasks::kRegisterOwnedAge ) + { + //sanity check - + if (fBookMode != kOfferLinkPending && fPendingLink == false) + return true; + // stop looking for this message and reset interface to 'offer book' mode again + plgDispatch::Dispatch()->UnRegisterForExactType(plVaultNotifyMsg::Index(), fManager->GetKey()); + ILinkOffereeToAge(); + return true; + } + return false; +} + + +///// ILinkOffereeToAge + +void plSceneInputInterface::ILinkOffereeToAge() +{ + // check vault to see if we've got an instance of the offered age now, if not create one and wait until we get a reply... + plAgeInfoStruct info; + info.SetAgeFilename(fOfferedAgeFile); + info.SetAgeInstanceName(fOfferedAgeInstance); + + bool isAgeInstanceGuidSet = !GuidIsNil(fAgeInstanceGuid); + + plAgeLinkStruct link; + + if (isAgeInstanceGuidSet) { + info.SetAgeInstanceGuid(&plUUID(fAgeInstanceGuid)); + link.GetAgeInfo()->CopyFrom(&info); + + GuidClear(&fAgeInstanceGuid); + } + else if (!VaultGetOwnedAgeLink(&info, &link)) { + + // We must have an owned copy of the age before we can offer it, so make one now + info.SetAgeInstanceGuid(&plUUID(GuidGenerate())); + std::string title; + std::string desc; + + unsigned nameLen = StrLen(plNetClientMgr::GetInstance()->GetPlayerName()); + if (plNetClientMgr::GetInstance()->GetPlayerName()[nameLen - 1] == 's' || plNetClientMgr::GetInstance()->GetPlayerName()[nameLen - 1] == 'S') { + xtl::format( title, "%s'", plNetClientMgr::GetInstance()->GetPlayerName() ); + xtl::format( desc, "%s' %s", plNetClientMgr::GetInstance()->GetPlayerName(), link.GetAgeInfo()->GetAgeInstanceName() ); + } + else { + xtl::format( title, "%s's", plNetClientMgr::GetInstance()->GetPlayerName() ); + xtl::format( desc, "%s's %s", plNetClientMgr::GetInstance()->GetPlayerName(), link.GetAgeInfo()->GetAgeInstanceName() ); + } + + info.SetAgeUserDefinedName( title.c_str() ); + info.SetAgeDescription( desc.c_str() ); + + link.GetAgeInfo()->CopyFrom(&info); + if (!VaultRegisterOwnedAgeAndWait(&link)) { + // failed to become an owner of the age for some reason, offer cannot continue + return; + } + } + else if (RelVaultNode * linkNode = VaultGetOwnedAgeLinkIncRef(&info)) { + // We have the age in our AgesIOwnFolder. If its volatile, dump it for the new one. + VaultAgeLinkNode linkAcc(linkNode); + if (linkAcc.volat) { + if (VaultUnregisterOwnedAgeAndWait(link.GetAgeInfo())) { + link.GetAgeInfo()->SetAgeInstanceGuid(&plUUID(GuidGenerate())); + VaultRegisterOwnedAgeAndWait(&link); + } + } + linkNode->DecRef(); + } + + if (fSpawnPoint) { + plSpawnPointInfo spawnPoint; + spawnPoint.SetName(fSpawnPoint); + link.SetSpawnPoint(spawnPoint); + } + + + // We now own the age, offer it + + if (0 == stricmp(fOfferedAgeFile, kPersonalAgeFilename)) + plNetLinkingMgr::GetInstance()->OfferLinkToPlayer(&link, fOffereeID, fManager->GetKey()); + else + plNetLinkingMgr::GetInstance()->LinkPlayerToAge(&link, fOffereeID); + + if (!fPendingLink && stricmp(fOfferedAgeFile, kPersonalAgeFilename)) + { + // tell our local dialog to pop up again... + ISendOfferNotification(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), 0, false); + // make them clickable again(in case they come back?) + //IManageIgnoredAvatars(fOffereeKey, false); + + fBookMode = kNotOffering; + fOffereeKey = nil; + fPendingLink = false; + } + else // this is a yeesha book link, must wait for multistage callbacks + { + // commented out until after 0.9 + fBookMode = kOfferLinkPending; + fPendingLink = true; +// fBookMode = kNotOffering; +// fOffereeKey = nil; +// fPendingLink = false; +// ISendAvatarDisabledNotification(true); + } +} + +//// ISetLastClicked ///////////////////////////////////////////////////////// + +#define MATT_WAS_HERE + +void plSceneInputInterface::ISetLastClicked( plKey obj, hsPoint3 hitPoint ) +{ + if (fBookMode != kNotOffering) + return; + + if( fLastClicked != nil ) + { + // Send an "un-picked" message to it + if( !fLastClickIsAvatar ) + { + plPickedMsg *pPickedMsg = TRACKED_NEW plPickedMsg; + pPickedMsg->AddReceiver( fLastClicked ); + pPickedMsg->fPicked = false; + plgDispatch::MsgSend( pPickedMsg ); + } + else + { + plRemoteAvatarInfoMsg *pMsg = TRACKED_NEW plRemoteAvatarInfoMsg; + pMsg->SetAvatarKey( nil ); + plgDispatch::MsgSend( pMsg ); + } + } + + fLastClicked = obj; + fLastClickIsAvatar = ( obj == nil ) ? false : fCurrClickIsAvatar; + + if( fLastClicked != nil ) + { +#ifdef MATT_WAS_HERE + // now we send pick messages to avatars as well... + plPickedMsg *pPickedMsg = TRACKED_NEW plPickedMsg; + pPickedMsg->AddReceiver( fLastClicked ); + pPickedMsg->fHitPoint = hitPoint; + plgDispatch::MsgSend( pPickedMsg ); + + // if it's an avatar, we also send this thing + if(fLastClickIsAvatar) + { + plRemoteAvatarInfoMsg *pMsg = TRACKED_NEW plRemoteAvatarInfoMsg; + pMsg->SetAvatarKey( fLastClicked ); + plgDispatch::MsgSend( pMsg ); + } +#else + // Send a "picked" message to it + if( !fLastClickIsAvatar ) + { + plPickedMsg *pPickedMsg = TRACKED_NEW plPickedMsg; + pPickedMsg->AddReceiver( fLastClicked ); + pPickedMsg->fHitPoint = hitPoint; + plgDispatch::MsgSend( pPickedMsg ); + } + else + { + plRemoteAvatarInfoMsg *pMsg = TRACKED_NEW plRemoteAvatarInfoMsg; + pMsg->SetAvatarKey( fLastClicked ); + plgDispatch::MsgSend( pMsg ); + } +#endif + } +} + +//// InterpretInputEvent ///////////////////////////////////////////////////// + +hsBool plSceneInputInterface::InterpretInputEvent( plInputEventMsg *pMsg ) +{ + plControlEventMsg* pControlEvent = plControlEventMsg::ConvertNoRef(pMsg); + if (pControlEvent) + { + if (pControlEvent->GetControlCode() == B_CONTROL_IGNORE_AVATARS) + { + for (int i = 0; i < fLocalIgnoredAvatars.Count(); i++) + { + plSceneObject* pObj = plSceneObject::ConvertNoRef(fLocalIgnoredAvatars[i]->ObjectIsLoaded()); + if (!pObj) + continue; + + const plArmatureMod* pArm = (const plArmatureMod*)pObj->GetModifierByType(plArmatureMod::Index()); + if (!pArm) + continue; + + plPhysicalControllerCore* controller = pArm->GetController(); + if (controller) + { + if (pControlEvent->ControlActivated()) + controller->SetLOSDB(plSimDefs::kLOSDBNone); + else + controller->SetLOSDB(plSimDefs::kLOSDBUIItems); + } + } + return true; + } + return false; + } + + plMouseEventMsg *mouseMsg = plMouseEventMsg::ConvertNoRef( pMsg ); + if( mouseMsg != nil ) + { + // you're suspended when in this mode... + if (fBookMode == kOfferLinkPending || fBookMode == kOfferAccepted) + return true; + + if( mouseMsg->GetButton() == kLeftButtonDown ) + { + if( fCurrentClickable != nil && fLastClicked == nil && fCurrentCursor != kNullCursor ) + { + fButtonState |= kLeftButtonDown; + ISetLastClicked( fCurrentClickable, fCurrentClickPoint ); + fCurrentCursor = SetCurrentCursorID(kCursorClicked); + return true; + } + // right here + if (fBookMode == kOfferBook) + { + fBookMode = kNotOffering; + fOffereeKey = nil; + ISendAvatarDisabledNotification(true); + } + } + else if( mouseMsg->GetButton() == kLeftButtonUp ) + { + if (fBookMode != kNotOffering) + { + if (fBookMode == kOfferBook && fCurrClickIsAvatar) + { + // send the avatar a message to put up his appropriate book + ISendOfferNotification(fCurrentClickable, 999, true); + //IManageIgnoredAvatars(fCurrentClickable, true); + fBookMode = kBookOffered; + fOffereeKey = fCurrentClickable; + } + else + if (fBookMode == kBookOffered && fCurrClickIsAvatar) + { + // and put our own dialog back up... + ISendOfferNotification(fOffereeKey, -999, true); + ISendOfferNotification(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), 0, false); + //IManageIgnoredAvatars(fOffereeKey, false); + fBookMode = kOfferBook; + fOffereeKey = nil; + } + else + if (fBookMode == kOfferBook) + { + fBookMode = kNotOffering; + fOffereeKey = nil; + ISendAvatarDisabledNotification(true); + } + } + if( fLastClicked != nil ) + { + fButtonState &= ~kLeftButtonDown; + ISetLastClicked( nil, hsPoint3(0,0,0) ); + + return true; + } + } + } + + return false; +} + + +//// ISendOfferNotification //////////////////////////////////////////////////////// +void plSceneInputInterface::IManageIgnoredAvatars(plKey& offeree, hsBool add) +{ + // tell everyone else to be able to / not to be able to select this avatar + plInputIfaceMgrMsg* pMsg = 0; + if (!add) + pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kEnableAvatarClickable); + else + pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableAvatarClickable); + pMsg->SetAvKey(offeree); + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + pMsg->SetBCastFlag(plMessage::kLocalPropagate, false); + pMsg->Send(); +} + +void plSceneInputInterface::ISendOfferNotification(plKey& offeree, int ID, hsBool net) +{ + int offereeID = -1; + if (offeree == plNetClientMgr::GetInstance()->GetLocalPlayerKey()) + { + offereeID = plNetClientMgr::GetInstance()->GetPlayerID(); + } + else + { + plNetTransportMember **members = nil; + plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members ); + if( members != nil) + { + for(int i = 0; i < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); i++ ) + { + plNetTransportMember *mbr = members[ i ]; + + if( mbr != nil && mbr->GetAvatarKey() == offeree) + { + offereeID = mbr->GetPlayerID(); + break; + } + } + } + + delete [] members; + + } + plNotifyMsg* pMsg = TRACKED_NEW plNotifyMsg; + pMsg->AddOfferBookEvent(plNetClientMgr::GetInstance()->GetLocalPlayerKey(), ID, offereeID); + pMsg->AddReceiver(fBookKey); + if (net) + { + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + pMsg->SetBCastFlag(plMessage::kLocalPropagate,false); + pMsg->AddNetReceiver( offereeID ); + pMsg->Send(); + } + else + { + pMsg->SetBCastFlag(plMessage::kNetPropagate, false); // don't deliver networked! + pMsg->Send(); + } + +} + +void plSceneInputInterface::ISendAvatarDisabledNotification(hsBool enabled) +{ + plInputIfaceMgrMsg* pMsg = 0; + if (enabled) + pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kEnableAvatarClickable); + else + pMsg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableAvatarClickable); + pMsg->SetAvKey(plNetClientMgr::GetInstance()->GetLocalPlayerKey()); + pMsg->SetBCastFlag(plMessage::kNetPropagate); + pMsg->SetBCastFlag(plMessage::kNetForce); + pMsg->SetBCastFlag(plMessage::kLocalPropagate, false); + pMsg->Send(); +} + + +//// IRequestLOSCheck //////////////////////////////////////////////////////// + +void plSceneInputInterface::IRequestLOSCheck( hsScalar xPos, hsScalar yPos, int ID ) +{ + if( fPipe == nil ) + return; + + + Int32 x=(Int32) ( xPos * fPipe->Width() ); + Int32 y=(Int32) ( yPos * fPipe->Height() ); + + hsPoint3 endPos, startPos; + + fPipe->ScreenToWorldPoint( 1,0, &x, &y, 10000, 0, &endPos ); + startPos = fPipe->GetViewPositionWorld(); + + // move the start pos out a little to avoid backing up against physical objects... + hsVector3 view(endPos - startPos); + view.Normalize(); + startPos = startPos + (view * 0.3f); + + plLOSRequestMsg* pMsg; + + if(ID == ID_FIND_CLICKABLE) { + pMsg = TRACKED_NEW plLOSRequestMsg( fManager->GetKey(), startPos, endPos, plSimDefs::kLOSDBUIItems, plLOSRequestMsg::kTestClosest ); + pMsg->SetCullDB(plSimDefs::kLOSDBUIBlockers); + } else if(ID == ID_FIND_WALKABLE_GROUND) { + pMsg = TRACKED_NEW plLOSRequestMsg( fManager->GetKey(), startPos, endPos, plSimDefs::kLOSDBAvatarWalkable, plLOSRequestMsg::kTestClosest); + } else + pMsg = TRACKED_NEW plLOSRequestMsg( fManager->GetKey(), startPos, endPos, plSimDefs::kLOSDBLocalAvatar, plLOSRequestMsg::kTestClosest); + + pMsg->SetReportType( plLOSRequestMsg::kReportHitOrMiss ); + + pMsg->SetRequestID( ID ); + + plgDispatch::MsgSend( pMsg ); + + fLastStartPt = startPos; + fLastEndPt = endPos; +} + +//// IWorldPosMovedSinceLastLOSCheck ///////////////////////////////////////// + +hsBool plSceneInputInterface::IWorldPosMovedSinceLastLOSCheck( void ) +{ + if( fPipe == nil ) + return false; + + Int32 x=(Int32) ( plMouseDevice::Instance()->GetCursorX() * fPipe->Width() ); + Int32 y=(Int32) ( plMouseDevice::Instance()->GetCursorY() * fPipe->Height() ); + + hsPoint3 endPos, startPos; + + startPos = fPipe->GetViewPositionWorld(); + if( !( startPos == fLastStartPt ) ) + return true; + + fPipe->ScreenToWorldPoint( 1,0, &x, &y, 10000, 0, &endPos ); + if( !( endPos == fLastEndPt ) ) + return true; + + return false; +} + +//// GetCurrentCursorID /////////////////////////////////////////////////////// + +UInt32 plSceneInputInterface::SetCurrentCursorID(UInt32 id) +{ + if (fBookMode == kOfferBook || fBookMode == kBookOffered) + { + switch(id) + { + case kCursorPoised: + return kCursorOfferBookHilite; + case kNullCursor: + return kCursorOfferBook; + case kCursorClicked: + return kCursorOfferBookClicked; + } + } + else + if (fBookMode == kOfferAccepted || fBookMode == kOfferLinkPending) + return kCursorOfferBook; + + return id; +} + +void plSceneInputInterface::RequestAvatarTurnToPointLOS() +{ + IRequestLOSCheck( plMouseDevice::Instance()->GetCursorX(), plMouseDevice::Instance()->GetCursorY(), ID_FIND_WALKABLE_GROUND ); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.h new file mode 100644 index 00000000..41ee7dd0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plSceneInputInterface.h @@ -0,0 +1,133 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSceneInputInterface // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plSceneInputInterface_h +#define _plSceneInputInterface_h + +#include "plInputInterface.h" +#include "hsGeometry3.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnUtils/pnUtils.h" + +//// Class Definition //////////////////////////////////////////////////////// + +class plPipeline; +class plSceneObject; + +class plSceneInputInterface : public plInputInterface +{ + enum + { + kNotOffering = 0, + kOfferBook, + kBookOffered, + kOfferAccepted, + kOfferLinkPending, + }; + protected: + static plSceneInputInterface *fInstance; + + UInt32 fCurrentCursor; + UInt8 fButtonState; + hsBool fClickability; + plKey fCurrentClickable, fLastClicked, fCurrentClickableLogicMod; + hsPoint3 fCurrentClickPoint; + hsBool fCurrClickIsAvatar, fLastClickIsAvatar,fFadedLocalAvatar; + hsBool fPendingLink; + + int fBookMode; // are we in offer book mode? + plKey fBookKey; // key for the python file modifier for the book we are offering + plKey fOffereeKey; + UInt32 fOffereeID; // ID for the guy who's accepted our link offer + const char* fOfferedAgeFile; + const char* fOfferedAgeInstance; + const char* fSpawnPoint; + Uuid fAgeInstanceGuid; + struct clickableTest + { + clickableTest(plKey k) + { + key = k; + val = false; + } + plKey key; + hsBool val; + }; + + hsTArray fClickableMap; + hsTArray fIgnoredAvatars; // these are ignored because they are engaged in avatar-avatar interactions which need to be left undisturbed + hsTArray fGUIIgnoredAvatars; // these are ignored because they have a GUI in their face right now + hsTArray fLocalIgnoredAvatars; // these are ALL avatars currently in your age. they are ignored when you press the 'ignore' key so you can + // select clickable non-avatar objects through them. + hsPoint3 fLastStartPt, fLastEndPt; + plPipeline *fPipe; + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); + + + void IRequestLOSCheck( hsScalar xPos, hsScalar yPos, int ID ); + void ISetLastClicked( plKey obj, hsPoint3 hitPoint ); + void IHalfFadeAvatar(hsBool out); + + hsBool IWorldPosMovedSinceLastLOSCheck( void ); + void ClearClickableMap(); + void ISendOfferNotification(plKey& offeree, int ID, hsBool net); + void IManageIgnoredAvatars(plKey& offeree, hsBool add); + void ISendAvatarDisabledNotification(hsBool enabled); + void ILinkOffereeToAge(); + public: + + plSceneInputInterface(); + virtual ~plSceneInputInterface(); + + static hsBool fShowLOS; + + // Always return true, since the cursor should be representing how we control the avatar + virtual hsBool HasInterestingCursorID( void ) const { return ( fCurrentCursor != kNullCursor ) ? true : false; } + virtual UInt32 GetPriorityLevel( void ) const { return kSceneInteractionPriority; } + virtual UInt32 GetCurrentCursorID( void ) const {return fCurrentCursor;} + UInt32 SetCurrentCursorID(UInt32 id); + virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg ); + void RequestAvatarTurnToPointLOS(); + + virtual hsBool MsgReceive( plMessage *msg ); + + virtual void Init( plInputInterfaceMgr *manager ); + virtual void Shutdown( void ); + + virtual void ResetClickableState(); + + plKey GetCurrMousedAvatar( void ) const { if( fCurrClickIsAvatar ) return fCurrentClickable; else return nil; } + static plSceneInputInterface *GetInstance( void ) { return fInstance; } +}; + + +#endif //_plSceneInputInterface_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plTelescopeInputInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plTelescopeInputInterface.cpp new file mode 100644 index 00000000..c0edc354 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plTelescopeInputInterface.cpp @@ -0,0 +1,125 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plTelescopeInputInterface // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsConfig.h" +#include "hsWindows.h" + +#include "hsTypes.h" +#include "plTelescopeInputInterface.h" + +#include "plInputInterfaceMgr.h" +#include "plInputManager.h" +#include "plInputDevice.h" +#include "../pnInputCore/plKeyMap.h" + +#include "plgDispatch.h" + + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plTelescopeInputInterface::plTelescopeInputInterface() +{ + SetEnabled( true ); // Always enabled + + + // Add our control codes to our control map. Do NOT add the key bindings yet. + // Note: HERE is where you specify the actions for each command, i.e. net propagate and so forth. + // This part basically declares us master of the bindings for these commands. + + // IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO + // RestoreDefaultKeyMappings()!!!! + + fControlMap->AddCode( B_CONTROL_EXIT_MODE, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CAMERA_PAN_LEFT, kControlFlagNormal ); + fControlMap->AddCode( B_CAMERA_PAN_RIGHT, kControlFlagNormal ); + fControlMap->AddCode( B_CAMERA_PAN_UP, kControlFlagNormal ); + fControlMap->AddCode( B_CAMERA_PAN_DOWN, kControlFlagNormal ); + fControlMap->AddCode( B_CAMERA_RECENTER, kControlFlagNormal | kControlFlagNoRepeat ); + fControlMap->AddCode( B_CAMERA_ZOOM_IN, kControlFlagNormal ); + fControlMap->AddCode( B_CAMERA_ZOOM_OUT, kControlFlagNormal ); + + // IF YOU ARE LOOKING TO CHANGE THE DEFAULT KEY BINDINGS, DO NOT LOOK HERE. GO TO + // RestoreDefaultKeyMappings()!!!! +} + +plTelescopeInputInterface::~plTelescopeInputInterface() +{ +} + +//// Init/Shutdown /////////////////////////////////////////////////////////// + +void plTelescopeInputInterface::Init( plInputInterfaceMgr *manager ) +{ + plInputInterface::Init( manager ); +} + +//// IEval /////////////////////////////////////////////////////////////////// + +hsBool plTelescopeInputInterface::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + return true; +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool plTelescopeInputInterface::MsgReceive( plMessage *msg ) +{ + return false; +} + +//// InterpretInputEvent ///////////////////////////////////////////////////// + +hsBool plTelescopeInputInterface::InterpretInputEvent( plInputEventMsg *pMsg ) +{ + return false; +} + +//// RestoreDefaultKeyMappings /////////////////////////////////////////////// + +void plTelescopeInputInterface::RestoreDefaultKeyMappings( void ) +{ + if( fControlMap == nil ) + return; + + fControlMap->UnmapAllBindings(); + + fControlMap->BindKey( KEY_BACKSPACE, B_CONTROL_EXIT_MODE ); + fControlMap->BindKey( KEY_NUMPAD5, B_CAMERA_RECENTER ); + fControlMap->BindKey( KEY_C, B_CAMERA_RECENTER ); + fControlMap->BindKey( KEY_NUMPAD_ADD, B_CAMERA_ZOOM_IN ); + fControlMap->BindKey( KEY_NUMPAD_SUBTRACT, B_CAMERA_ZOOM_OUT ); + fControlMap->BindKey( KEY_NUMPAD4, B_CAMERA_PAN_LEFT ); + fControlMap->BindKey( KEY_NUMPAD6, B_CAMERA_PAN_RIGHT ); + fControlMap->BindKey( KEY_NUMPAD8, B_CAMERA_PAN_UP ); + fControlMap->BindKey( KEY_NUMPAD2, B_CAMERA_PAN_DOWN ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plTelescopeInputInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plTelescopeInputInterface.h new file mode 100644 index 00000000..8e4ffb3f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInputCore/plTelescopeInputInterface.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plTelescopeInputInterface // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plTelescopeInputInterface_h +#define _plTelescopeInputInterface_h + +#include "plInputInterface.h" +#include "../pnKeyedObject/plKey.h" + +//// Class Definition //////////////////////////////////////////////////////// + +class plTelescopeInputInterface : public plInputInterface +{ + protected: + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); + + public: + + plTelescopeInputInterface(); + virtual ~plTelescopeInputInterface(); + + virtual void RestoreDefaultKeyMappings( void ); + + virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg ); + + virtual hsBool MsgReceive( plMessage *msg ); + + virtual void Init( plInputInterfaceMgr *manager ); + virtual void Shutdown( void ) {;} + + // Returns the priority of this interface layer, based on the Priorities enum + virtual UInt32 GetPriorityLevel( void ) const { return kTelescopeInputPriority; } + + // Returns the currently active mouse cursor for this layer, as defined in pnMessage/plCursorChangeMsg.h + virtual UInt32 GetCurrentCursorID( void ) const { return kCursorUp; } + + // Returns the current opacity that this layer wants the cursor to be, from 0 (xparent) to 1 (opaque) + virtual hsScalar GetCurrentCursorOpacity( void ) const { return 1.f; } + + // Returns true if this layer is wanting to change the mouse, false if it isn't interested + virtual hsBool HasInterestingCursorID( void ) const { return false; } +}; + + +#endif //_plTelescopeInputInterface_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsInterp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsInterp.cpp new file mode 100644 index 00000000..0f9e0126 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsInterp.cpp @@ -0,0 +1,494 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsInterp.h" +#include "../plTransform/hsAffineParts.h" +#include "hsColorRGBA.h" +#include "hsPoint2.h" + +// +/////////////////////////////////////////////////////// +// linear interpolation +/////////////////////////////////////////////////////// +// +void hsInterp::LinInterp(hsScalar k1, hsScalar k2, hsScalar t, hsScalar* result) +{ + *result = k1 + t * (k2 - k1); +} + +void hsInterp::LinInterp(const hsScalarTriple* k1, const hsScalarTriple* k2, hsScalar t, + hsScalarTriple* result) +{ + if (t==0.0) + *result = *k1; + else + if (t==1.0) + *result = *k2; + else + { + LinInterp(k1->fX, k2->fX, t, &result->fX); + LinInterp(k1->fY, k2->fY, t, &result->fY); + LinInterp(k1->fZ, k2->fZ, t, &result->fZ); + } +} + +void hsInterp::LinInterp(const hsColorRGBA* k1, const hsColorRGBA* k2, hsScalar t, + hsColorRGBA* result, UInt32 flags) +{ + if (t==0.0) + { + // copy + result->r = k1->r; + result->g = k1->g; + result->b = k1->b; + if (!(flags & kIgnoreAlpha)) + result->a = k1->a; + return; + } + + if (t==1.0) + { + result->r = k2->r; + result->g = k2->g; + result->b = k2->b; + if (!(flags & kIgnoreAlpha)) + result->a = k2->a; + return; + } + + LinInterp(k1->r, k2->r, t, &result->r); + LinInterp(k1->g, k2->g, t, &result->g); + LinInterp(k1->b, k2->b, t, &result->b); + if (!(flags & kIgnoreAlpha)) + LinInterp(k1->a, k2->a, t, &result->a); +} + +void hsInterp::LinInterp(const hsMatrix33* k1, const hsMatrix33* k2, hsScalar t, + hsMatrix33* result, UInt32 flags) +{ + if (t==0.0) + { + // copy + result->fMap[0][0] = k1->fMap[0][0]; + result->fMap[0][1] = k1->fMap[0][1]; + result->fMap[0][2] = k1->fMap[0][2]; + + result->fMap[1][0] = k1->fMap[1][0]; + result->fMap[1][1] = k1->fMap[1][1]; + result->fMap[1][2] = k1->fMap[1][2]; + + if (!(flags & kIgnoreLastMatRow)) + { + result->fMap[2][0] = k1->fMap[2][0]; + result->fMap[2][1] = k1->fMap[2][1]; + result->fMap[2][2] = k1->fMap[2][2]; + } + return; + } + if (t==1.0) + { + // copy + result->fMap[0][0] = k2->fMap[0][0]; + result->fMap[0][1] = k2->fMap[0][1]; + result->fMap[0][2] = k2->fMap[0][2]; + + result->fMap[1][0] = k2->fMap[1][0]; + result->fMap[1][1] = k2->fMap[1][1]; + result->fMap[1][2] = k2->fMap[1][2]; + + if (!(flags & kIgnoreLastMatRow)) + { + result->fMap[2][0] = k2->fMap[2][0]; + result->fMap[2][1] = k2->fMap[2][1]; + result->fMap[2][2] = k2->fMap[2][2]; + } + return; + } + + LinInterp(k1->fMap[0][0], k2->fMap[0][0], t, &result->fMap[0][0]); + LinInterp(k1->fMap[0][1], k2->fMap[0][1], t, &result->fMap[0][1]); + LinInterp(k1->fMap[0][2], k2->fMap[0][2], t, &result->fMap[0][2]); + + LinInterp(k1->fMap[1][0], k2->fMap[1][0], t, &result->fMap[1][0]); + LinInterp(k1->fMap[1][1], k2->fMap[1][1], t, &result->fMap[1][1]); + LinInterp(k1->fMap[1][2], k2->fMap[1][2], t, &result->fMap[1][2]); + + if (!(flags & kIgnoreLastMatRow)) + { + LinInterp(k1->fMap[2][0], k2->fMap[2][0], t, &result->fMap[2][0]); + LinInterp(k1->fMap[2][1], k2->fMap[2][1], t, &result->fMap[2][1]); + LinInterp(k1->fMap[2][2], k2->fMap[2][2], t, &result->fMap[2][2]); + } +} + +// +// +void hsInterp::LinInterp(const hsMatrix44* mat1, const hsMatrix44* mat2, hsScalar t, + hsMatrix44* out, UInt32 flags) +{ + if (flags == 0) + { + if( 0 == t ) + { + *out = *mat1; + return; + } + + if( hsScalar1 == t ) + { + *out = *mat2; + return; + } + } + + if( flags & kIgnorePartsScale ) + { + if (!(flags & kIgnorePartsRot)) + { + // interp rotation with quats + hsQuat q1, q2, qOut; + q1.SetFromMatrix(mat1); + q2.SetFromMatrix(mat2); + LinInterp(&q1, &q2, t, &qOut); + + qOut.Normalize(); + qOut.MakeMatrix(out); + } + else + out->Reset(); +#if 1 + hsAssert(mat2->fMap[3][0]==0 && mat2->fMap[3][1]==0 && mat2->fMap[3][2]==0 && mat2->fMap[3][3]==1, + "matrix prob?"); +#else + // copy + for(int i=0; i<3; i++) + out->fMap[3][i] = mat2->fMap[3][i]; +#endif + + if (!(flags & kIgnorePartsPos)) + { + // interp translation + hsPoint3 p1,p2,pOut; + mat1->GetTranslate(&p1); + mat2->GetTranslate(&p2); + LinInterp(&p1, &p2, t, &pOut); + out->SetTranslate(&pOut); + out->NotIdentity(); // in case no rot + } + } + else + { + // Complete decomp and parts interp + + gemAffineParts gemParts1, gemParts2; + hsAffineParts parts1, parts2, partsOut; + + decomp_affine(mat1->fMap, &gemParts1); + AP_SET(parts1, gemParts1); + + decomp_affine(mat2->fMap, &gemParts2); + AP_SET(parts2, gemParts2); + + LinInterp(&parts1, &parts2, t, &partsOut, flags); // flags will be parsed here + + partsOut.ComposeMatrix(out); + } +} + +void hsInterp::LinInterp(const hsQuat* k1, const hsQuat* k2, hsScalar t, hsQuat* result) +{ + if (t==0.0) + *result = *k1; + else + if (t==1.0) + *result = *k2; + else + { + result->SetFromSlerp(*k1, *k2, t); + } +} + +void hsInterp::LinInterp(const hsScaleValue* k1, const hsScaleValue* k2, hsScalar t, + hsScaleValue* result) +{ + LinInterp(&k1->fS, &k2->fS, t, &result->fS); // Stretch rotation + LinInterp(&k1->fQ, &k2->fQ, t, &result->fQ); // Stretch factor +} + +void hsInterp::LinInterp(const hsAffineParts* k1, const hsAffineParts* k2, hsScalar t, + hsAffineParts* result, UInt32 flags) +{ + if (t==0.0) + { + // copy + if (!(flags & kIgnorePartsPos)) + result->fT = k1->fT; + if (!(flags & kIgnorePartsRot)) + result->fQ = k1->fQ; + if (!(flags & kIgnorePartsScale)) + { + // same as preserveScale + result->fU = k1->fU; + result->fK = k1->fK; + } + result->fF = k1->fF; + + return; + } + + if (flags & kPreservePartsScale) + { + result->fU = k1->fU; // just copy scale from 1st key + result->fK = k1->fK; + } + + if (t==1.0) + { + // copy + if (!(flags & kIgnorePartsPos)) + result->fT = k2->fT; + if (!(flags & kIgnorePartsRot)) + result->fQ = k2->fQ; + if (!(flags & (kIgnorePartsScale | kPreservePartsScale))) + { + result->fU = k2->fU; + result->fK = k2->fK; + } + result->fF = k2->fF; + + return; + } + + if(k1->fF!=k2->fF) + hsStatusMessageF("WARNING: Inequality in affine parts flip value."); + +// hsAssert(k1->fF==k2->fF, "inequality in affine parts flip value"); + if (!(flags & kIgnorePartsPos)) + LinInterp(&k1->fT, &k2->fT, t, &result->fT); // Translation + if (!(flags & kIgnorePartsRot)) + { + LinInterp(&k1->fQ, &k2->fQ, t, &result->fQ); // Essential rotation + } + + if (!(flags & (kIgnorePartsScale | kPreservePartsScale))) + { + LinInterp(&k1->fU, &k2->fU, t, &result->fU); // Stretch rotation + LinInterp(&k1->fK, &k2->fK, t, &result->fK); // Stretch factor + } + +#if 0 + if (!(flags & kIgnorePartsDet)) + LinInterp(k1->fF, k2->fF, t, &result->fF); // Flip rot var +#else + result->fF = k1->fF; +#endif +} + +// +/////////////////////////////////////////////////////// +// Key interpolation +/////////////////////////////////////////////////////// +// + +void hsInterp::BezScalarEval(const hsScalar value1, const hsScalar outTan, + const hsScalar value2, const hsScalar inTan, + const hsScalar t, const hsScalar tanScale, hsScalar *result) +{ +#if 0 + // If the tangents were what you'd expect them to be... Hermite splines, than this code + // would make sense. But no, Max likes to store them in a scaled form based on the + // time of each frame. If we ever optimize this further, we could do the scaling on export, + // but I need this to work right now before all the artists hate me too much. + const hsScalar t2 = t * t; + const hsScalar t3 = t2 * t; + + const hsScalar term1 = 2 * t3 - 3 * t2; + + *result = ((term1 + 1) * value1) + + (-term1 * value2) + + ((t3 - 2 * t2 + 1) * outTan) + + ((t3 - t2) * inTan); +#else + const hsScalar oneMinusT = (1.0f - t); + const hsScalar tSq = t * t; + const hsScalar oneMinusTSq = oneMinusT * oneMinusT; + + *result = (oneMinusT * oneMinusTSq * value1) + + (3.f * t * oneMinusTSq * (value1 + outTan * tanScale)) + + (3.f * tSq * oneMinusT * (value2 + inTan * tanScale)) + + (tSq * t * value2); +#endif +} + +void hsInterp::BezInterp(const hsBezPoint3Key* k1, const hsBezPoint3Key* k2, const hsScalar t, hsScalarTriple* result) +{ + hsScalar scale = (k2->fFrame - k1->fFrame) * MAX_TICKS_PER_FRAME / 3.f; + BezScalarEval(k1->fValue.fX, k1->fOutTan.fX, k2->fValue.fX, k2->fInTan.fX, t, scale, &result->fX); + BezScalarEval(k1->fValue.fY, k1->fOutTan.fY, k2->fValue.fY, k2->fInTan.fY, t, scale, &result->fY); + BezScalarEval(k1->fValue.fZ, k1->fOutTan.fZ, k2->fValue.fZ, k2->fInTan.fZ, t, scale, &result->fZ); +} + +void hsInterp::BezInterp(const hsBezScalarKey* k1, const hsBezScalarKey* k2, const hsScalar t, hsScalar* result) +{ + hsScalar scale = (k2->fFrame - k1->fFrame) * MAX_TICKS_PER_FRAME / 3.f; + BezScalarEval(k1->fValue, k1->fOutTan, k2->fValue, k2->fInTan, t, scale, result); +} + +void hsInterp::BezInterp(const hsBezScaleKey* k1, const hsBezScaleKey* k2, const hsScalar t, hsScaleValue* result) +{ + hsScalar scale = (k2->fFrame - k1->fFrame) * MAX_TICKS_PER_FRAME / 3.f; + BezScalarEval(k1->fValue.fS.fX, k1->fOutTan.fX, k2->fValue.fS.fX, k2->fInTan.fX, t, scale, &result->fS.fX); + BezScalarEval(k1->fValue.fS.fY, k1->fOutTan.fY, k2->fValue.fS.fY, k2->fInTan.fY, t, scale, &result->fS.fY); + BezScalarEval(k1->fValue.fS.fZ, k1->fOutTan.fZ, k2->fValue.fS.fZ, k2->fInTan.fZ, t, scale, &result->fS.fZ); + + // Slerp scale axis + LinInterp(&k1->fValue.fQ, &k2->fValue.fQ, t, &result->fQ); +} + +// +// Get an element from an array of unknown type +// +static inline hsKeyFrame* GetKey(Int32 i, void *keys, Int32 size) +{ + return (hsKeyFrame*) ((char*)keys + size * i); +} + +// +// STATIC +// Given a list of keys, and a time, fills in the 2 boundary keys and +// a fraction (p=0-1) indicating where the time falls between them. +// Returns the index of the first key which can be passed in as a hint (lastKeyIdx) +// for the next search. +// +void hsInterp::GetBoundaryKeyFrames(hsScalar time, UInt32 numKeys, void *keys, UInt32 size, + hsKeyFrame **kF1, hsKeyFrame **kF2, UInt32 *lastKeyIdx, hsScalar *p, hsBool forwards) +{ + hsAssert(numKeys>1, "Must have more than 1 keyframe"); + int k1, k2; + UInt16 frame = (UInt16)(time * MAX_FRAMES_PER_SEC); + + // boundary case, past end + if (frame > GetKey(numKeys-1, keys, size)->fFrame) + { + k1=k2=numKeys-1; + (*kF2) = GetKey(k1, keys, size); + (*kF1) = (*kF2); + *p = 0.0; + goto ret; + } + + hsKeyFrame *key1, *key2; + // boundary case, before start + if (frame < (key1=GetKey(0, keys, size))->fFrame) + { + k1=k2=0; + (*kF1) = GetKey(k1, keys, size); + (*kF2) = (*kF1); + *p = 0.0; + goto ret; + } + + // prime loop + int i; + i = 1; + if (*lastKeyIdx > 0 && *lastKeyIdx < numKeys - 1) + { + // new starting point for search + if (forwards) + key1 = GetKey(*lastKeyIdx, keys, size); + else + key2 = GetKey(*lastKeyIdx + 1, keys, size); + + i = *lastKeyIdx + 1; + } + else if (!forwards) + { + key2 = GetKey(1, keys, size); + } + + // search pairs of keys + int count; + if (forwards) + { + for (count = 1; count <= numKeys; count++, i++) + { + if (i >= numKeys) + { + key1 = GetKey(0, keys, size); + i = 1; + count++; + } + + key2 = GetKey(i, keys, size); + if (frame <= key2->fFrame && frame >= key1->fFrame) + { + k2=i; + k1=i-1; + (*kF2) = key2; + (*kF1) = key1; + *p = (time - (*kF1)->fFrame / MAX_FRAMES_PER_SEC) / (((*kF2)->fFrame - (*kF1)->fFrame) / MAX_FRAMES_PER_SEC); + goto ret; + } + key1=key2; + } + } + else + { + for (count = 1; count <= numKeys; count++, i--) + { + if (i < 1) + { + i = numKeys - 1; + key2 = GetKey(i, keys, size); + count++; + } + + key1 = GetKey(i - 1, keys, size); + if (frame <= key2->fFrame && frame >= key1->fFrame) + { + k2 = i; + k1 = i - 1; + (*kF2) = key2; + (*kF1) = key1; + *p = (time - (*kF1)->fFrame / MAX_FRAMES_PER_SEC) / (((*kF2)->fFrame - (*kF1)->fFrame) / MAX_FRAMES_PER_SEC); + goto ret; + } + key2=key1; + } + } + +ret: +; + +#if 0 + char str[128]; + sprintf(str, "k1=%d, k2=%d, p=%f\n", k1, k2, *p); + OutputDebugString(str); +#endif + *lastKeyIdx = k1; +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsInterp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsInterp.h new file mode 100644 index 00000000..9780bff0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsInterp.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef HSINTERP_inc +#define HSINTERP_inc + +#include "HeadSpin.h" +#include "hsKeys.h" + +/////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// + +// +// Performs interpolation of keyframes & values. +// t param should be 0-1 +// +struct hsColorRGBA; +class hsAffineParts; +class hsInterp +{ +public: + enum IgnoreFlags + { + kIgnoreAlpha = 0x1, + kIgnoreLastMatRow = 0x2, + kIgnorePartsPos = 0x4, + kIgnorePartsRot = 0x8, + kIgnorePartsScale = 0x10, // result gets no scale + kIgnorePartsDet = 0x20, + kPreservePartsScale = 0x40 // result gets the scale of key1 + }; + + static void BezScalarEval(const hsScalar value1, const hsScalar outTan, + const hsScalar value2, const hsScalar inTan, + const hsScalar t, const hsScalar scale, hsScalar *result); + static void BezInterp(const hsBezPoint3Key *k1, const hsBezPoint3Key *k2, const hsScalar t, hsScalarTriple *result); + static void BezInterp(const hsBezScalarKey *k1, const hsBezScalarKey *k2, const hsScalar t, hsScalar *result); + static void BezInterp(const hsBezScaleKey *k1, const hsBezScaleKey *k2, const hsScalar t, hsScaleValue *result); + + // simple linear interpolation + static void LinInterp(const hsScalar k1, const hsScalar k2, const hsScalar t, hsScalar *result); + static void LinInterp(const hsScalarTriple *k1, const hsScalarTriple *k2, const hsScalar t, hsScalarTriple *result); + static void LinInterp(const hsColorRGBA *k1, const hsColorRGBA *k2, const hsScalar t, hsColorRGBA *result, UInt32 ignoreFlags=0); + static void LinInterp(const hsMatrix33 *k1, const hsMatrix33 *k2, const hsScalar t, hsMatrix33 *result, UInt32 ignoreFlags=0); + static void LinInterp(const hsMatrix44 *mat1, const hsMatrix44 *mat2, const hsScalar t, hsMatrix44 *out, UInt32 ignoreFlags=0); + static void LinInterp(const hsQuat *k1, const hsQuat *k2, const hsScalar t, hsQuat *result); + static void LinInterp(const hsScaleValue *k1, const hsScaleValue *k2, const hsScalar t, hsScaleValue *result); + static void LinInterp(const hsAffineParts *k1, const hsAffineParts *k2, const hsScalar t, hsAffineParts *result, UInt32 ignoreFlags=0); + + // Given a time value, find the enclosing keyframes and normalize time (0-1) + static void GetBoundaryKeyFrames(hsScalar time, UInt32 numKeys, void *keys, + UInt32 keySize, hsKeyFrame **kF1, hsKeyFrame **kF2, UInt32 *lastKeyIdx, hsScalar *p, hsBool forwards); + +}; + +#define MAX_FRAMES_PER_SEC 30.0f +#define MAX_TICKS_PER_FRAME 160.0f +#define MAX_TICKS_PER_SEC (MAX_TICKS_PER_FRAME*MAX_FRAMES_PER_SEC) + +#endif // #ifndef HSINTERP_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsKeys.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsKeys.cpp new file mode 100644 index 00000000..a69959cf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsKeys.cpp @@ -0,0 +1,554 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsKeys.h" +#include "hsStream.h" + +const int hsKeyFrame::kMaxFrameNumber = 65535; + +/////////////////////////////////////////////////////////////// + +void hsPoint3Key::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + fValue.Read(stream); +} + +void hsPoint3Key::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + fValue.Write(stream); +} + +hsBool hsPoint3Key::CompareValue(hsPoint3Key *key) +{ + return hsABS(fValue.fX - key->fValue.fX) < .01 && + hsABS(fValue.fY - key->fValue.fY) < .01 && + hsABS(fValue.fZ - key->fValue.fZ) < .01; +} + +void hsBezPoint3Key::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + fInTan.Read(stream); + fOutTan.Read(stream); + fValue.Read(stream); +} + +void hsBezPoint3Key::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + fInTan.Write(stream); + fOutTan.Write(stream); + fValue.Write(stream); +} + +hsBool hsBezPoint3Key::CompareValue(hsBezPoint3Key *key) +{ + return hsABS(fValue.fX - key->fValue.fX) < .01 && + hsABS(fValue.fY - key->fValue.fY) < .01 && + hsABS(fValue.fZ - key->fValue.fZ) < .01; +} + +///////////////////////////////////////// + +void hsScalarKey::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + fValue = stream->ReadSwapScalar(); +} + +void hsScalarKey::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + stream->WriteSwapScalar(fValue); +} + +hsBool hsScalarKey::CompareValue(hsScalarKey *key) +{ + return fValue == key->fValue; +} + +void hsBezScalarKey::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + fInTan = stream->ReadSwapScalar(); + fOutTan = stream->ReadSwapScalar(); + fValue = stream->ReadSwapScalar(); +} + +void hsBezScalarKey::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + stream->WriteSwapScalar(fInTan); + stream->WriteSwapScalar(fOutTan); + stream->WriteSwapScalar(fValue); +} + +hsBool hsBezScalarKey::CompareValue(hsBezScalarKey *key) +{ + return fValue == key->fValue; +} + +///////////////////////////////////////// + +void hsQuatKey::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + fValue.Read(stream); +} + +void hsQuatKey::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + fValue.Write(stream); +} + +hsBool hsQuatKey::CompareValue(hsQuatKey *key) +{ + return fValue == key->fValue; +} + +////////////////////////////////////////////////////////////////////////////// + +const hsScalar hsCompressedQuatKey32::kOneOverRootTwo = 0.70710678; +const hsScalar hsCompressedQuatKey32::k10BitScaleRange = 1023 / (2 * kOneOverRootTwo); + +void hsCompressedQuatKey32::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + fData = stream->ReadSwap32(); +} + +void hsCompressedQuatKey32::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + stream->WriteSwap32(fData); +} + +hsBool hsCompressedQuatKey32::CompareValue(hsCompressedQuatKey32 *key) +{ + return fData == key->fData; +} + +// To store a quat in 32 bits, we find which element is the largest and use 2 bits to +// store which one it is. We now know the other 3 elements fall in the range +// of [-kOneOverRootTwo, kOneOverRootTwo]. We scale that range across 10 bits +// and store each. When extracting, we use the fact that the quat was normalized +// to compute the 4th element. +void hsCompressedQuatKey32::SetQuat(hsQuat &q) +{ + q.Normalize(); + UInt32 maxElement = kCompQuatNukeX; + hsScalar maxVal = hsABS(q.fX); + if (hsABS(q.fY) > maxVal) + { + maxElement = kCompQuatNukeY; + maxVal = hsABS(q.fY); + } + if (hsABS(q.fZ) > maxVal) + { + maxElement = kCompQuatNukeZ; + maxVal = hsABS(q.fZ); + } + if (hsABS(q.fW) > maxVal) + { + maxElement = kCompQuatNukeW; + maxVal = hsABS(q.fW); + } + switch (maxElement) + { + case kCompQuatNukeX: + { + // Invert the quat so that the largest element is positive. + // We need to do this so that later we know to use the positive root. + if (q.fX < 0) + q = -q; + + fData = (maxElement << 30) | + (((UInt32)(k10BitScaleRange * (q.fY + kOneOverRootTwo))) << 20) | + (((UInt32)(k10BitScaleRange * (q.fZ + kOneOverRootTwo))) << 10) | + (((UInt32)(k10BitScaleRange * (q.fW + kOneOverRootTwo)))); + break; + } + case kCompQuatNukeY: + { + if (q.fY < 0) + q = -q; + + fData = (maxElement << 30) | + (((UInt32)(k10BitScaleRange * (q.fX + kOneOverRootTwo))) << 20) | + (((UInt32)(k10BitScaleRange * (q.fZ + kOneOverRootTwo))) << 10) | + (((UInt32)(k10BitScaleRange * (q.fW + kOneOverRootTwo)))); + break; + } + case kCompQuatNukeZ: + { + if (q.fZ < 0) + q = -q; + + fData = (maxElement << 30) | + (((UInt32)(k10BitScaleRange * (q.fX + kOneOverRootTwo))) << 20) | + (((UInt32)(k10BitScaleRange * (q.fY + kOneOverRootTwo))) << 10) | + (((UInt32)(k10BitScaleRange * (q.fW + kOneOverRootTwo)))); + break; + } + case kCompQuatNukeW: + default: + { + if (q.fW < 0) + q = -q; + + fData = (maxElement << 30) | + (((UInt32)(k10BitScaleRange * (q.fX + kOneOverRootTwo))) << 20) | + (((UInt32)(k10BitScaleRange * (q.fY + kOneOverRootTwo))) << 10) | + (((UInt32)(k10BitScaleRange * (q.fZ + kOneOverRootTwo)))); + break; + } + } +} + +void hsCompressedQuatKey32::GetQuat(hsQuat &q) +{ + UInt32 maxElement = fData >> 30; + switch (maxElement) + { + case kCompQuatNukeX: + { + q.fY = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fZ = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fW = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fX = hsSquareRoot(1 - q.fY * q.fY - q.fZ * q.fZ - q.fW *q.fW); + break; + } + case kCompQuatNukeY: + { + q.fX = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fZ = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fW = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fY = hsSquareRoot(1 - q.fX * q.fX - q.fZ * q.fZ - q.fW *q.fW); + break; + } + case kCompQuatNukeZ: + { + q.fX = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fY = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fW = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fZ = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fW *q.fW); + break; + } + case kCompQuatNukeW: + default: + { + q.fX = (fData >> 20 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fY = (fData >> 10 & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fZ = (fData & 0x000003ff) / k10BitScaleRange - kOneOverRootTwo; + q.fW = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fZ * q.fZ); + break; + } + } +} + +///////////////////////////////////////////////////////////////////////////// + +const hsScalar hsCompressedQuatKey64::kOneOverRootTwo = 0.70710678; +const hsScalar hsCompressedQuatKey64::k20BitScaleRange = 1048575 / (2 * kOneOverRootTwo); +const hsScalar hsCompressedQuatKey64::k21BitScaleRange = 2097151 / (2 * kOneOverRootTwo); + +void hsCompressedQuatKey64::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + fData[0] = stream->ReadSwap32(); + fData[1] = stream->ReadSwap32(); +} + +void hsCompressedQuatKey64::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + stream->WriteSwap32(fData[0]); + stream->WriteSwap32(fData[1]); +} + +hsBool hsCompressedQuatKey64::CompareValue(hsCompressedQuatKey64 *key) +{ + return (fData[0] == key->fData[0]) && (fData[1] == key->fData[1]); +} + +// To store a quat in 64 bits, we find which element is the largest and use 2 bits to +// store which one it is. We now know the other 3 elements fall in the range +// of [-kOneOverRootTwo, kOneOverRootTwo]. We scale that range across 20/21/21 bits +// and store each. When extracting, we use the fact that the quat was normalized +// to compute the 4th element. +void hsCompressedQuatKey64::SetQuat(hsQuat &q) +{ + q.Normalize(); + UInt32 maxElement = kCompQuatNukeX; + hsScalar maxVal = hsABS(q.fX); + if (hsABS(q.fY) > maxVal) + { + maxElement = kCompQuatNukeY; + maxVal = hsABS(q.fY); + } + if (hsABS(q.fZ) > maxVal) + { + maxElement = kCompQuatNukeZ; + maxVal = hsABS(q.fZ); + } + if (hsABS(q.fW) > maxVal) + { + maxElement = kCompQuatNukeW; + maxVal = hsABS(q.fW); + } + switch (maxElement) + { + case kCompQuatNukeX: + { + // Invert the quat so that the largest element is positive. + // We need to do this so that later we know to use the positive root. + if (q.fX < 0) + q = -q; + + fData[0] = (maxElement << 30) | + (((UInt32)(k20BitScaleRange * (q.fY + kOneOverRootTwo))) << 10) | + (((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) >> 11); + fData[1] = + (((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) << 21) | + (((UInt32)(k21BitScaleRange * (q.fW + kOneOverRootTwo)))); + break; + } + case kCompQuatNukeY: + { + if (q.fY < 0) + q = -q; + + fData[0] = (maxElement << 30) | + (((UInt32)(k20BitScaleRange * (q.fX + kOneOverRootTwo))) << 10) | + (((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) >> 11); + fData[1] = + (((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo))) << 21) | + (((UInt32)(k21BitScaleRange * (q.fW + kOneOverRootTwo)))); + break; + } + case kCompQuatNukeZ: + { + if (q.fZ < 0) + q = -q; + + fData[0] = (maxElement << 30) | + (((UInt32)(k20BitScaleRange * (q.fX + kOneOverRootTwo))) << 10) | + (((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) >> 11); + fData[1] = + (((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) << 21) | + (((UInt32)(k21BitScaleRange * (q.fW + kOneOverRootTwo)))); + break; + } + case kCompQuatNukeW: + default: + { + if (q.fW < 0) + q = -q; + + fData[0] = (maxElement << 30) | + (((UInt32)(k20BitScaleRange * (q.fX + kOneOverRootTwo))) << 10) | + (((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) >> 11); + fData[1] = + (((UInt32)(k21BitScaleRange * (q.fY + kOneOverRootTwo))) << 21) | + (((UInt32)(k21BitScaleRange * (q.fZ + kOneOverRootTwo)))); + break; + } + } +} + +void hsCompressedQuatKey64::GetQuat(hsQuat &q) +{ + UInt32 maxElement = fData[0] >> 30; + switch (maxElement) + { + case kCompQuatNukeX: + { + q.fY = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo; + q.fZ = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo; + q.fW = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo; + q.fX = hsSquareRoot(1 - q.fY * q.fY - q.fZ * q.fZ - q.fW *q.fW); + break; + } + case kCompQuatNukeY: + { + q.fX = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo; + q.fZ = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo; + q.fW = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo; + q.fY = hsSquareRoot(1 - q.fX * q.fX - q.fZ * q.fZ - q.fW *q.fW); + break; + } + case kCompQuatNukeZ: + { + q.fX = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo; + q.fY = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo; + q.fW = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo; + q.fZ = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fW *q.fW); + break; + } + case kCompQuatNukeW: + default: + { + q.fX = ((fData[0] >> 10) & 0x000fffff) / k20BitScaleRange - kOneOverRootTwo; + q.fY = (((fData[0] & 0x000003ff) << 11) | (fData[1] >> 21)) / k21BitScaleRange - kOneOverRootTwo; + q.fZ = (fData[1] & 0x001fffff) / k21BitScaleRange - kOneOverRootTwo; + q.fW = hsSquareRoot(1 - q.fX * q.fX - q.fY * q.fY - q.fZ * q.fZ); + break; + } + } +} + +///////////////////////////////////////// +// Not a key +// +void hsScaleValue::Read(hsStream *stream) +{ + fS.Read(stream); + fQ.Read(stream); +} + +void hsScaleValue::Write(hsStream *stream) +{ + fS.Write(stream); + fQ.Write(stream); +} + +///////////////////////////////////////// +void hsScaleKey::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + fValue.Read(stream); +} + +void hsScaleKey::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + fValue.Write(stream); +} + +hsBool hsScaleKey::CompareValue(hsScaleKey *key) +{ + return fValue == key->fValue; +} + +void hsBezScaleKey::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + fInTan.Read(stream); + fOutTan.Read(stream); + fValue.Read(stream); +} + +void hsBezScaleKey::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + fInTan.Write(stream); + fOutTan.Write(stream); + fValue.Write(stream); +} + +hsBool hsBezScaleKey::CompareValue(hsBezScaleKey *key) +{ + return fValue == key->fValue; +} + +////////////////////// + +void hsG3DSMaxKeyFrame::Set(hsMatrix44 *mat, UInt16 frame) +{ + fFrame = frame; + gemAffineParts parts; + decomp_affine(mat->fMap, &parts); + AP_SET(fParts, parts); +} + +void hsG3DSMaxKeyFrame::Set(const hsAffineParts &parts, UInt16 frame) +{ + fFrame = frame; + fParts = parts; +} + +void hsG3DSMaxKeyFrame::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + fParts.Read(stream); +} + +void hsG3DSMaxKeyFrame::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + fParts.Write(stream); +} + +hsBool hsG3DSMaxKeyFrame::CompareValue(hsG3DSMaxKeyFrame *key) +{ + return fParts == key->fParts; +} + +///////////////////////////////////////// + +void hsMatrix33Key::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + Int32 i,j; + for(i=0;i<3;i++) + for(j=0;j<3;j++) + fValue.fMap[j][i] = stream->ReadSwapScalar(); +} + +void hsMatrix33Key::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + Int32 i,j; + for(i=0;i<3;i++) + for(j=0;j<3;j++) + stream->WriteSwapScalar(fValue.fMap[j][i]); +} + +hsBool hsMatrix33Key::CompareValue(hsMatrix33Key *key) +{ + return fValue == key->fValue; +} + +///////////////////////////////////////// + +void hsMatrix44Key::Read(hsStream *stream) +{ + fFrame = stream->ReadSwap16(); + fValue.Read(stream); +} + +void hsMatrix44Key::Write(hsStream *stream) +{ + stream->WriteSwap16(fFrame); + fValue.Write(stream); +} + +hsBool hsMatrix44Key::CompareValue(hsMatrix44Key *key) +{ + return fValue == key->fValue; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsKeys.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsKeys.h new file mode 100644 index 00000000..632a64d1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsKeys.h @@ -0,0 +1,239 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef HSKEYS_inc +#define HSKEYS_inc + +#include "HeadSpin.h" +#include "hsGeometry3.h" +#include "hsQuat.h" +#include "../plTransform/hsAffineParts.h" +#include "hsMatrix33.h" +#include "hsMatrix44.h" + +// No virtuals. Keep these nice and lean. +struct hsKeyFrame +{ + // Used by plController to specify which keys it has. + enum + { + kUnknownKeyFrame, + kPoint3KeyFrame, + kBezPoint3KeyFrame, + kScalarKeyFrame, + kBezScalarKeyFrame, + kScaleKeyFrame, + kBezScaleKeyFrame, + kQuatKeyFrame, + kCompressedQuatKeyFrame32, + kCompressedQuatKeyFrame64, + k3dsMaxKeyFrame, + kMatrix33KeyFrame, + kMatrix44KeyFrame, + }; + + UInt16 fFrame; + static const int kMaxFrameNumber; +}; + +struct hsPoint3Key : public hsKeyFrame +{ + hsPoint3 fValue; + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsPoint3Key *key); +}; + +struct hsBezPoint3Key : public hsKeyFrame +{ + hsPoint3 fInTan; + hsPoint3 fOutTan; + hsPoint3 fValue; + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsBezPoint3Key *key); +}; + +struct hsScalarKey : public hsKeyFrame +{ + hsScalar fValue; + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsScalarKey *key); +}; + +struct hsBezScalarKey : public hsKeyFrame +{ + hsScalar fInTan; + hsScalar fOutTan; + hsScalar fValue; + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsBezScalarKey *key); +}; + +struct hsQuatKey : public hsKeyFrame +{ + hsQuat fValue; + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsQuatKey *key); +}; + +struct hsCompressedQuatKey32 : public hsKeyFrame +{ + enum + { + kCompQuatNukeX, + kCompQuatNukeY, + kCompQuatNukeZ, + kCompQuatNukeW, + }; + + static const hsScalar kOneOverRootTwo; + static const hsScalar k10BitScaleRange; + + void SetQuat(hsQuat &q); + void GetQuat(hsQuat &q); + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsCompressedQuatKey32 *key); + +protected: + UInt32 fData; +}; + +struct hsCompressedQuatKey64 : public hsKeyFrame +{ + enum + { + kCompQuatNukeX, + kCompQuatNukeY, + kCompQuatNukeZ, + kCompQuatNukeW, + }; + + static const hsScalar kOneOverRootTwo; + static const hsScalar k20BitScaleRange; + static const hsScalar k21BitScaleRange; + + void SetQuat(hsQuat &q); + void GetQuat(hsQuat &q); + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsCompressedQuatKey64 *key); + +protected: + UInt32 fData[2]; +}; + +struct hsScaleValue : public hsKeyFrame +{ + hsVector3 fS; /* Scale components for x,y,z */ + hsQuat fQ; /* The axis along which the scale is applied */ + + void Read(hsStream *stream); + void Write(hsStream *stream); + + int operator==(const hsScaleValue& a) const { return (fS == a.fS && fQ == a.fQ); } +}; + +// +// +// +struct hsScaleKey : public hsKeyFrame +{ + hsScaleValue fValue; + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsScaleKey *key); +}; + +struct hsBezScaleKey : public hsKeyFrame +{ + hsPoint3 fInTan; + hsPoint3 fOutTan; + hsScaleValue fValue; + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsBezScaleKey *key); +}; + +struct hsG3DSMaxKeyFrame : public hsKeyFrame +{ + hsAffineParts fParts; + + void Reset() { fParts.Reset(); } // Make parts identity + + void Set(hsMatrix44 *mat, UInt16 frame); + void Set(const hsAffineParts &parts, UInt16 frame); + + hsMatrix44* GetMatrix44(hsMatrix44 *mat) { fParts.ComposeMatrix(mat); return mat; } + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsG3DSMaxKeyFrame *key); +}; + +struct hsMatrix33Key : public hsKeyFrame +{ + hsMatrix33 fValue; + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsMatrix33Key *key); +}; + +struct hsMatrix44Key : public hsKeyFrame +{ + hsMatrix44 fValue; + + void Read(hsStream *stream); + void Write(hsStream *stream); + + hsBool CompareValue(hsMatrix44Key *key); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsTimedValue.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsTimedValue.h new file mode 100644 index 00000000..2475f60d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/hsTimedValue.h @@ -0,0 +1,220 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsTimedValue_inc +#define hsTimedValue_inc + +#include "hsStream.h" + +template +class hsTimedValue { +public: + enum { + kIdle = 0x1, + kInstant = 0x2 + }; +protected: + UInt32 fFlags; + hsScalar fDuration; + hsScalar fStartTime; + + T fValue; + T fGoal; + T fFrom; + +public: + hsTimedValue() : fFlags(kIdle|kInstant), fDuration(0) {} + hsTimedValue(const T& v) : fFlags(kIdle|kInstant), fDuration(0) { SetValue(v); } + + UInt32 GetFlags() { return fFlags; } + + void SetDuration(hsScalar duration); + hsScalar GetDuration() const { return fDuration; } + + hsBool32 operator==(const hsTimedValue& v); + hsTimedValue& operator=(const T& v) { SetValue(v); return *this; } + hsTimedValue& operator+=(const T& v) { SetValue(v + fValue); return *this; } + + void SetTempValue(const T& v) { fValue = v; } + void SetValue(const T& v) { fFrom = fGoal = fValue = v; fFlags |= kIdle; } + const T& GetValue() const { return fValue; } + + void SetGoal(const T& g) { fGoal = g; } + const T& GetGoal() const { return fGoal; } + + void Reset() { fFlags |= (kIdle | kInstant); } + + void StartClock(hsScalar s); + hsScalar GetStartTime() const { return fStartTime; } + + const T& GetFrom() const { return fFrom; } + + void Update(hsScalar s); + + void WriteScalar(hsStream* s, hsScalar currSecs); + void Write(hsStream* s, hsScalar currSecs); + + void ReadScalar(hsStream* s, hsScalar currSecs); + void Read(hsStream* s, hsScalar currSecs); +}; + +template +void hsTimedValue::WriteScalar(hsStream* s, hsScalar currSecs) +{ + s->WriteSwap32(fFlags); + + s->WriteSwapScalar(fValue); + + if( !(fFlags & kIdle) ) + { + s->WriteSwapScalar(fDuration); + s->WriteSwapScalar(currSecs - fStartTime); + + s->WriteSwapScalar(fGoal); + s->WriteSwapScalar(fFrom); + } +} + +template +void hsTimedValue::Write(hsStream* s, hsScalar currSecs) +{ + s->WriteSwap32(fFlags); + + fValue.Write(s); + + if( !(fFlags & kIdle) ) + { + s->WriteSwapScalar(fDuration); + s->WriteSwapScalar(currSecs - fStartTime); + + fGoal.Write(s); + fFrom.Write(s); + } +} + +template +void hsTimedValue::ReadScalar(hsStream* s, hsScalar currSecs) +{ + fFlags = s->ReadSwap32(); + + fValue = s->ReadSwapScalar(); + + if( !(fFlags & kIdle) ) + { + fDuration = s->ReadSwapScalar(); + fStartTime = currSecs - s->ReadSwapScalar(); + + fGoal = s->ReadSwapScalar(); + fFrom = s->ReadSwapScalar(); + } +} + +template +void hsTimedValue::Read(hsStream* s, hsScalar currSecs) +{ + fFlags = s->ReadSwap32(); + + fValue.Read(s); + + if( !(fFlags & kIdle) ) + { + fDuration = s->ReadSwapScalar(); + fStartTime = currSecs - s->ReadSwapScalar(); + + fGoal.Read(s); + fFrom.Read(s); + } +} + +template +void hsTimedValue::SetDuration(hsScalar duration) +{ + fDuration = duration; + if( fDuration > 0 ) + fFlags &= ~kInstant; + else + fFlags |= kInstant; +} + +template +hsBool32 hsTimedValue::operator==(const hsTimedValue& v) +{ + if ((fFlags == v.fFlags) && + (fDuration == v.fDuration) && + (fStartTime == v.fStartTime) && + (fValue == v.fValue) && + (fGoal == v.fGoal) && + (fFrom == v.fFrom)) + { + return true; + } + + return false; +} + +template +void hsTimedValue::StartClock(hsScalar s) +{ + fStartTime = s; + + if( fFlags & kInstant ) + { + fFlags |= kIdle; + fValue = fGoal; + return; + } + + fFlags &= ~kIdle; + + if( fValue == fGoal ) + fFlags |= kIdle; + + fFrom = fValue; +} + +template +void hsTimedValue::Update(hsScalar s) +{ + if( fFlags & kIdle ) + return; + + hsAssert(fDuration > 0, "Instant should always be idle"); + + hsScalar interp = (s - fStartTime) / fDuration; + + if( interp >= hsScalar1 ) + { + fValue = fGoal; + interp = hsScalar1; + fFlags |= kIdle; + } + else + fValue = fFrom + (fGoal - fFrom) * interp; +} + + + +#endif // hsTimedValue_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plATCEaseCurves.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plATCEaseCurves.cpp new file mode 100644 index 00000000..2d50894f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plATCEaseCurves.cpp @@ -0,0 +1,347 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAnimEaseTypes.h" +#include "plAnimTimeConvert.h" + +/////////////////////////////////////////////////////////////////////////////////////////////// + +plATCEaseCurve *plATCEaseCurve::CreateEaseCurve(UInt8 type, hsScalar minLength, hsScalar maxLength, hsScalar length, + hsScalar startSpeed, hsScalar goalSpeed) +{ + if (type == plAnimEaseTypes::kConstAccel) + return TRACKED_NEW plConstAccelEaseCurve(minLength, maxLength, length, startSpeed, goalSpeed); + if (type == plAnimEaseTypes::kSpline) + return TRACKED_NEW plSplineEaseCurve(minLength, maxLength, length, startSpeed, goalSpeed); + + return nil; +} + +void plATCEaseCurve::RecalcToSpeed(hsScalar startSpeed, hsScalar goalSpeed, hsBool preserveRate /* = false */) +{ + hsScalar rate = 1; + + if (fSpeed == goalSpeed && fStartSpeed == startSpeed) // already there, no need to do anything + return; + + if (preserveRate) + rate = (fSpeed - fStartSpeed) / fLength; + + fStartSpeed = startSpeed; + fSpeed = goalSpeed; + + if (preserveRate) + SetLengthOnRate(rate); +} + +void plATCEaseCurve::SetLengthOnRate(hsScalar rate) +{ + fLength = (fSpeed - fStartSpeed) / rate; + if (fLength < 0) + fLength = -fLength; +} + +hsScalar plATCEaseCurve::GetMinDistance() +{ + if (fMinLength == 0) + return 0; + + hsScalar oldLength = fLength; + fLength = fMinLength; + hsScalar result = PositionGivenTime(fMinLength); + fLength = oldLength; + return result; +} + +hsScalar plATCEaseCurve::GetMaxDistance() +{ + if (fMaxLength == 0) + return 0; + + hsScalar oldLength = fLength; + fLength = fMaxLength; + hsScalar result = PositionGivenTime(fMaxLength); + fLength = oldLength; + return result; +} + +hsScalar plATCEaseCurve::GetNormDistance() +{ + if (fNormLength == 0) + return 0; + + hsScalar oldLength = fLength; + fLength = fNormLength; + hsScalar result = PositionGivenTime(fNormLength); + fLength = oldLength; + return result; +} + +void plATCEaseCurve::Read(hsStream *s, hsResMgr *mgr) +{ + plCreatable::Read(s, mgr); + + fMinLength = s->ReadSwapScalar(); + fMaxLength = s->ReadSwapScalar(); + fNormLength = fLength = s->ReadSwapScalar(); + fStartSpeed = s->ReadSwapScalar(); + fSpeed = s->ReadSwapScalar(); + fBeginWorldTime = s->ReadSwapDouble(); +} + +void plATCEaseCurve::Write(hsStream *s, hsResMgr *mgr) +{ + plCreatable::Write(s, mgr); + + s->WriteSwapScalar(fMinLength); + s->WriteSwapScalar(fMaxLength); + s->WriteSwapScalar(fNormLength); + s->WriteSwapScalar(fStartSpeed); + s->WriteSwapScalar(fSpeed); + s->WriteSwapDouble(fBeginWorldTime); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +plConstAccelEaseCurve::plConstAccelEaseCurve() +{ + fMinLength = fMaxLength = fNormLength = fLength = 1; + fBeginWorldTime = 0; + + RecalcToSpeed(0, 1); +} + +plConstAccelEaseCurve::plConstAccelEaseCurve(hsScalar minLength, hsScalar maxLength, hsScalar length, + hsScalar startSpeed, hsScalar goalSpeed) +{ + fMinLength = minLength; + fMaxLength = maxLength; + fNormLength = fLength = length; + fBeginWorldTime = 0; + + RecalcToSpeed(startSpeed, goalSpeed); +} + +plATCEaseCurve *plConstAccelEaseCurve::Clone() const +{ + plConstAccelEaseCurve *curve = TRACKED_NEW plConstAccelEaseCurve; + curve->fStartSpeed = fStartSpeed; + curve->fMinLength = fMinLength; + curve->fMaxLength = fMaxLength; + curve->fNormLength = fNormLength; + curve->fBeginWorldTime = fBeginWorldTime; + curve->fLength = fLength; + curve->fSpeed = fSpeed; + + return curve; +} + +void plConstAccelEaseCurve::SetLengthOnDistance(hsScalar dist) +{ + fLength = 2 * dist / (fSpeed + fStartSpeed); +} + +hsScalar plConstAccelEaseCurve::PositionGivenTime(hsScalar time) const +{ + return (hsScalar)(fStartSpeed * time + (0.5 * (fSpeed - fStartSpeed) / fLength) * time * time); +} + +hsScalar plConstAccelEaseCurve::VelocityGivenTime(hsScalar time) const +{ + return fStartSpeed + ((fSpeed - fStartSpeed) / fLength) * time; +} + +hsScalar plConstAccelEaseCurve::TimeGivenVelocity(hsScalar velocity) const +{ + return (velocity - fStartSpeed) / ((fSpeed - fStartSpeed) / fLength); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +plSplineEaseCurve::plSplineEaseCurve() +{ + fMinLength = fMaxLength = fNormLength = fLength = 1; + fBeginWorldTime = 0; + + RecalcToSpeed(0, 1); +} + +plSplineEaseCurve::plSplineEaseCurve(hsScalar minLength, hsScalar maxLength, hsScalar length, + hsScalar startSpeed, hsScalar goalSpeed) +{ + fMinLength = minLength; + fMaxLength = maxLength; + fNormLength = fLength = length; + fBeginWorldTime = 0; + + RecalcToSpeed(startSpeed, goalSpeed); +} + +plATCEaseCurve *plSplineEaseCurve::Clone() const +{ + plSplineEaseCurve *curve = TRACKED_NEW plSplineEaseCurve; + curve->fStartSpeed = fStartSpeed; + curve->fMinLength = fMinLength; + curve->fMaxLength = fMaxLength; + curve->fNormLength = fNormLength; + curve->fBeginWorldTime = fBeginWorldTime; + curve->fLength = fLength; + curve->fSpeed = fSpeed; + + int i; + for (i = 0; i < 4; i++) + curve->fCoef[i] = fCoef[i]; + + return curve; +} + +void plSplineEaseCurve::RecalcToSpeed(hsScalar startSpeed, hsScalar goalSpeed, hsBool preserveRate /* = false */) +{ + plATCEaseCurve::RecalcToSpeed(startSpeed, goalSpeed, preserveRate); + + // These are greatly simplified because the in/out tangents are always zero + // Note: "b" is always zero for the ease splines we're currently doing (and will remain that way + // so long as the initial acceleration is zero. Can optimize a bit of the eval math to take + // advantage of this. + hsScalar a, b, c, d; + + a = fStartSpeed; + b = 0; + c = -3 * fStartSpeed + 3 * fSpeed; + d = 2 * fStartSpeed - 2 * fSpeed; + + fCoef[0] = a; + fCoef[1] = b; + fCoef[2] = c; + fCoef[3] = d; +} + +void plSplineEaseCurve::SetLengthOnDistance(hsScalar dist) +{ + hsScalar curDist = PositionGivenTime(fLength); + + fLength = fLength * dist / curDist; +} + +hsScalar plSplineEaseCurve::PositionGivenTime(hsScalar time) const +{ + hsScalar t1, t2, t3, t4; + t1 = time / fLength; + t2 = t1 * t1; + t3 = t2 * t1; + t4 = t3 * t1; + + return fLength * (fCoef[0] * t1 + fCoef[1] * t2 / 2 + fCoef[2] * t3 / 3 + fCoef[3] * t4 / 4); +} + +hsScalar plSplineEaseCurve::VelocityGivenTime(hsScalar time) const +{ + hsScalar t1, t2, t3; + t1 = time / fLength; + t2 = t1 * t1; + t3 = t2 * t1; + return fCoef[0] + fCoef[1] * t1 + fCoef[2] * t2 + fCoef[3] * t3; +} + +hsScalar plSplineEaseCurve::TimeGivenVelocity(hsScalar velocity) const +{ + // Code based off of Graphics Gems V, pp 11-12 and + // http://www.worldserver.com/turk/opensource/FindCubicRoots.c.txt + + // Solving the equation: fCoef[0] + fCoef[1] * t + fCoef[2] * t^2 + fCoef[3] * t^3 - velocity = 0 + + hsScalar root; + hsScalar a = (fCoef[0] - velocity) / fCoef[3]; + hsScalar b = fCoef[1] / fCoef[3]; + hsScalar c = fCoef[2] / fCoef[3]; + + hsScalar Q = (c * c - 3 * b) / 9; + hsScalar R = (2 * c * c * c - 9 * c * b + 27 * a) / 54; + hsScalar Q3 = Q * Q * Q; + hsScalar D = Q3 - R * R; + + if (D >= 0) + { + // 3 roots, find the one in the range [0, 1] + const hsScalar pi = 3.14159; + double theta = acos(R / sqrt(Q3)); + double sqrtQ = sqrt(Q); + + root = (hsScalar)(-2 * sqrtQ * cos((theta + 4 * pi) / 3) - c / 3); // Middle root, most likely to match + if (root < 0.f || root > 1.f) + { + root = (hsScalar)(-2 * sqrtQ * cos((theta + 2 * pi) / 3) - c / 3); // Lower root + if (root < 0.f || root > 1.f) + { + root = (hsScalar)(-2 * sqrtQ * cos(theta / 3) - c / 3); // Upper root + } + } + } + else // One root to the equation (I don't expect this to happen for ease splines, but JIC) + { + double E = sqrt(-D) + pow(fabs(R), 1.f / 3.f); + root = (hsScalar)((E + Q / E) - c / 3); + if (R > 0) + root = -root; + } + + if (root < 0.f || root > 1.f) + { + hsAssert(false, "No valid root found while solving animation spline"); + // Either a bug, or a rare case of floating-point inaccuracy. Either way, guess + // the proper root as either the start or end of the curve based on the velocity. + hsScalar dStart = velocity - fStartSpeed; + if (dStart < 0) + dStart = -dStart; + hsScalar dEnd = velocity - fSpeed; + if (dEnd < 0) + dEnd = -dEnd; + + root = (dStart < dEnd ? 0.f : 1.f); + } + + return root * fLength; +} + +void plSplineEaseCurve::Read(hsStream *s, hsResMgr *mgr) +{ + plATCEaseCurve::Read(s, mgr); + + fCoef[0] = s->ReadSwapScalar(); + fCoef[1] = s->ReadSwapScalar(); + fCoef[2] = s->ReadSwapScalar(); + fCoef[3] = s->ReadSwapScalar(); +} + +void plSplineEaseCurve::Write(hsStream *s, hsResMgr *mgr) +{ + plATCEaseCurve::Write(s, mgr); + + s->WriteSwapScalar(fCoef[0]); + s->WriteSwapScalar(fCoef[1]); + s->WriteSwapScalar(fCoef[2]); + s->WriteSwapScalar(fCoef[3]); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimEaseTypes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimEaseTypes.h new file mode 100644 index 00000000..be374ea8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimEaseTypes.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_ANIM_EASE_TYPES +#define PL_ANIM_EASE_TYPES + +// Silly little header so that both export and runtime code can use these +// constants without including tons of unneccessary stuff + +namespace plAnimEaseTypes +{ + enum { + kNoEase, + kConstAccel, + kSpline, + }; +}; + +#define ENTIRE_ANIMATION_NAME "(Entire Animation)" + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimPath.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimPath.cpp new file mode 100644 index 00000000..9213bee6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimPath.cpp @@ -0,0 +1,793 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plAnimPath.h" +#include "plController.h" +#include "hsFastMath.h" +#include "hsResMgr.h" + +const hsScalar kSmallDelTime = 1.e-2f; +const hsScalar kInvSmallDelTime = 1.f / kSmallDelTime; +const hsScalar kTerminateDelTime = 1.e-3f; +const hsScalar kTerminateDelDistSq = .1f; + +plAnimPath::plAnimPath() +: fController(nil), fLength(0), fMinDistSq(0), + fAnimPathFlags(0) +{ + fLocalToWorld.Reset(); + fWorldToLocal.Reset(); + Reset(); +} + +plAnimPath::~plAnimPath() +{ + delete fController; +} + +void plAnimPath::Reset() +{ + SetCurTime(0); +} + +void plAnimPath::SetCurTime(hsScalar t, UInt32 calcFlags) +{ + fTime = t; + if( !fController ) + { + fPos.Set(0,0,0); + fXform.Reset(); + fVel.Set(0,0,0); + fAccel.Set(0,0,0); + return; + } + + hsScalar t0, t1, t2; + + if( t < kSmallDelTime ) + { + t0 = t; + t1 = t + kSmallDelTime; + t2 = t + 2 * kSmallDelTime; + } + else if( t > fLength - kSmallDelTime ) + { + t0 = t - 2 * kSmallDelTime; + t1 = t - kSmallDelTime; + t2 = t; + } + else + { + t0 = t - kSmallDelTime; + t1 = t; + t2 = t + kSmallDelTime; + } + + + if (!(calcFlags & kCalcPosOnly)) + { + hsPoint3 pos[3]; + + fController->Interp(t0, &fParts); + pos[0].Set(fParts.fT.fX, fParts.fT.fY, fParts.fT.fZ); + + fController->Interp(t1, &fParts); + pos[1].Set(fParts.fT.fX, fParts.fT.fY, fParts.fT.fZ); + + fController->Interp(t2, &fParts); + pos[2].Set(fParts.fT.fX, fParts.fT.fY, fParts.fT.fZ); + + fVel.Set(pos+1, pos+0); + fVel *= kInvSmallDelTime; + fVel = fLocalToWorld * fVel; + fAccel.Set(&(pos[2] - pos[1]), &(pos[1] - pos[0])); + fAccel *= kInvSmallDelTime * kInvSmallDelTime; + fAccel = fLocalToWorld * fAccel; + } + + fController->Interp(t, &fParts); + fParts.ComposeMatrix(&fXform); + fXform = fLocalToWorld * fXform; + fXform.GetTranslate(&fPos); + +} + +void plAnimPath::ICalcBounds() +{ + if( !fController ) + return; + + plController* pc = fController->GetPosController(); + + int i; + hsPoint3 pos; + hsTArray keyTimes; + pc->GetKeyTimes(keyTimes); + fCenter.Set(0,0,0); + for( i = 0; i < keyTimes.GetCount() ; i++ ) + { + pc->Interp(keyTimes[i], &pos); + fCenter += pos; + } + fCenter *= hsScalarInvert((hsScalar)keyTimes.GetCount()); + + fRadius = 0; + for( i = 0; i < keyTimes.GetCount(); i++ ) + { + pc->Interp(keyTimes[i], &pos); + hsScalar rad = (pos - fCenter).Magnitude(); + if( rad > fRadius ) + fRadius = rad; + } +} + +hsScalar plAnimPath::ICalcTotalLength() +{ + if( !(fController && fController->GetPosController()) ) + return 0; + + fLength = fController->GetPosController()->GetLength(); + + return fLength; +} + +void plAnimPath::SetController(plCompoundController* tmc) +{ + hsAssert(tmc, "Bad (nil) controller for AnimPath"); + hsAssert(tmc->GetPosController(), "Bad controller for AnimPath"); + fController = tmc; + ICalcTotalLength(); + ICalcBounds(); +} + +void plAnimPath::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + fLocalToWorld = l2w; + fWorldToLocal = w2l; +} + +void plAnimPath::Read(hsStream* stream, hsResMgr* mgr) +{ + fAnimPathFlags=stream->ReadSwap32(); + + delete fController; + fController = plCompoundController::ConvertNoRef(mgr->ReadCreatable(stream)); + + ICalcBounds(); + + fParts.Read(stream); + + fLocalToWorld.Read(stream); + fWorldToLocal.Read(stream); + + fLength = stream->ReadSwapScalar(); + fMinDistSq = stream->ReadSwapScalar(); + + Reset(); +} + +void plAnimPath::Write(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteSwap32(fAnimPathFlags); + + mgr->WriteCreatable(stream, fController); + + fParts.Write(stream); + + fLocalToWorld.Write(stream); + fWorldToLocal.Write(stream); + + stream->WriteSwapScalar(fLength); + stream->WriteSwapScalar(fMinDistSq); +} + +hsBool plAnimPath::OutOfRange(hsPoint3 &worldPt, hsScalar range) const +{ + hsPoint3 pt = fWorldToLocal * worldPt; + + hsScalar radius = (pt - fCenter).Magnitude() - fRadius; + return( radius > range ); +} + +hsScalar plAnimPath::GetExtremePoint(hsPoint3 &worldPt) const +{ + if( !fController ) + return 0; + + hsPoint3 pt = fWorldToLocal * worldPt; + + plController *pc = fController->GetPosController(); + + hsScalar minDistSq = 1.e33f; + hsScalar minTime = 0, delTime = 0; + // start search by using the time of the closest ctrl point + int i; + hsTArray keyTimes; + pc->GetKeyTimes(keyTimes); + for( i = 0; i < keyTimes.GetCount(); i++ ) + { + hsScalar t = keyTimes[i]; + hsPoint3 pos; + pc->Interp(t, &pos); // handles easing + hsScalar distSq = (pt - pos).MagnitudeSquared(); + if( distSq < minDistSq ) + { + minDistSq = distSq; + minTime = t; + if( 0 == i ) + delTime = keyTimes[i+1] - t; + else if( keyTimes.GetCount() - 1 == i ) + delTime = t - keyTimes[i - 1]; + else + { + hsScalar fore = keyTimes[i + 1] - t; + hsScalar back = t - keyTimes[i - 1]; + delTime = hsMaximum(fore, back); + } + } + } + + return GetExtremePoint(minTime, delTime, worldPt); +} + +hsScalar plAnimPath::GetExtremePoint(hsScalar lastTime, hsScalar delTime, hsPoint3 &worldPt) const +{ + if( !fController ) + return 0; + + hsPoint3 pt = fWorldToLocal * worldPt; + + IInitInterval(lastTime, delTime, pt); + return ICheckInterval(pt); +} + +hsScalar plAnimPath::ICheckInterval(hsPoint3 &pt) const +{ + if( fDelTime <= kTerminateDelTime && + hsVector3(&fCurPos, &fPrevPos).MagnitudeSquared() < kTerminateDelDistSq) + { + return IBestTime(); + } + + if( fThisTime < 0 ) + return 0; + + if( fThisTime > fLength ) + return fLength; + + if( GetFarthest() ) + { + if( (fLastDistSq > fThisDistSq)&&(fLastDistSq >= fNextDistSq) ) + return IShiftBack(pt); + + if( (fNextDistSq > fThisDistSq)&&(fNextDistSq >= fLastDistSq) ) + return IShiftFore(pt); + + if( (fThisDistSq >= fLastDistSq)&&(fLastDistSq >= fNextDistSq) ) + return ISubDivBack(pt); + + if( (fThisDistSq >= fNextDistSq)&&(fNextDistSq >= fLastDistSq) ) + return ISubDivFore(pt); + } + else + { + if( (fLastDistSq < fThisDistSq)&&(fLastDistSq <= fNextDistSq) ) + return IShiftBack(pt); + + if( (fNextDistSq < fThisDistSq)&&(fNextDistSq <= fLastDistSq) ) + return IShiftFore(pt); + + if( (fThisDistSq <= fLastDistSq)&&(fLastDistSq <= fNextDistSq) ) + return ISubDivBack(pt); + + if( (fThisDistSq <= fNextDistSq)&&(fNextDistSq <= fLastDistSq) ) + return ISubDivFore(pt); + } + + hsAssert(false, "Shouldn't have gotten here"); + return 0; +} + +void plAnimPath::IInitInterval(hsScalar time, hsScalar delTime, hsPoint3 &pt) const +{ + plController* pc = fController->GetPosController(); + + hsPoint3 pos; + + fDelTime = delTime; + if( fDelTime <= kTerminateDelTime ) + fDelTime = kTerminateDelTime * 2; + else + if( fDelTime > fLength * 0.5f ) + fDelTime = fLength * 0.5f; + + fThisTime = time; + if( fThisTime < fDelTime ) + fThisTime = fDelTime; + else if( fThisTime > fLength - fDelTime ) + fThisTime = fLength - fDelTime; + pc->Interp(fThisTime, &pos); + fPrevPos=fCurPos=pos; + fThisDistSq = (pos - pt).MagnitudeSquared(); + + fNextTime = fThisTime + delTime; + if( fNextTime > fLength ) + fNextTime = fLength; + if (!(GetAnimPathFlags() & kFavorBwdSearch)) + { + pc->Interp(fNextTime, &pos); + fNextDistSq = (pos - pt).MagnitudeSquared(); + } + else + { + fNextDistSq = 1.e33f; + } + + fLastTime = fThisTime - delTime; + if( fLastTime < 0 ) + fLastTime = 0; + if (!(GetAnimPathFlags() & kFavorFwdSearch)) + { + pc->Interp(fLastTime, &pos); + fLastDistSq = (pos - pt).MagnitudeSquared(); + } + else + { + fLastDistSq = 1.e33f; + } + + if( fMinDistSq != 0 ) + { + fThisDistSq -= fMinDistSq; + if( fThisDistSq < 0 ) + fThisDistSq = -fThisDistSq; + + fNextDistSq -= fMinDistSq; + if( fNextDistSq < 0 ) + fNextDistSq = -fNextDistSq; + + fLastDistSq -= fMinDistSq; + if( fLastDistSq < 0 ) + fLastDistSq = -fLastDistSq; + } +} + +hsScalar plAnimPath::ISubDivBack(hsPoint3 &pt) const +{ + fNextTime = fThisTime; + fNextDistSq = fThisDistSq; + + fDelTime *= 0.5f; + + fThisTime -= fDelTime; + if (fThisTime<0) + fThisTime = GetWrap() ? fLength + fThisTime : 0; + + plController* pc = fController->GetPosController(); + hsPoint3 pos; + pc->Interp(fThisTime, &pos); + fThisDistSq = (pos - pt).MagnitudeSquared() - fMinDistSq; + if( fThisDistSq < 0 ) + fThisDistSq = -fThisDistSq; + + fPrevPos=fCurPos; + fCurPos=pos; + + return ICheckInterval(pt); +} + +hsScalar plAnimPath::ISubDivFore(hsPoint3 &pt) const +{ + fLastTime = fThisTime; + fLastDistSq = fThisDistSq; + + fDelTime *= 0.5f; + + fThisTime += fDelTime; + if (fThisTime>fLength) + fThisTime = GetWrap() ? fThisTime-fLength : fLength; + + plController* pc = fController->GetPosController(); + hsPoint3 pos; + pc->Interp(fThisTime, &pos); + fThisDistSq = (pos - pt).MagnitudeSquared() - fMinDistSq; + if( fThisDistSq < 0 ) + fThisDistSq = -fThisDistSq; + + fPrevPos=fCurPos; + fCurPos=pos; + + return ICheckInterval(pt); +} + +hsScalar plAnimPath::IShiftBack(hsPoint3 &pt) const +{ + if( !GetWrap() && (fLastTime <= 0) ) + return ISubDivBack(pt); + + fNextTime = fThisTime; + fNextDistSq = fThisDistSq; + + fThisTime = fLastTime; + fThisDistSq = fLastDistSq; + + fLastTime -= fDelTime; + if( fLastTime < 0 ) + fLastTime = GetWrap() ? fLength + fLastTime : 0; + plController* pc = fController->GetPosController(); + hsPoint3 pos; + pc->Interp(fLastTime, &pos); + fLastDistSq = (pos - pt).MagnitudeSquared() - fMinDistSq; + if( fLastDistSq < 0 ) + fLastDistSq = -fLastDistSq; + + fPrevPos=fCurPos; + fCurPos=pos; + + return ICheckInterval(pt); +} + +hsScalar plAnimPath::IShiftFore(hsPoint3 &pt) const +{ + if( !GetWrap() &&(fNextTime >= fLength) ) + return ISubDivFore(pt); + + fLastTime = fThisTime; + fLastDistSq = fThisDistSq; + + fThisTime = fNextTime; + fThisDistSq = fNextDistSq; + + fNextTime += fDelTime; + if( fNextTime > fLength ) + fNextTime = GetWrap() ? fNextTime - fLength : fLength; + plController* pc = fController->GetPosController(); + hsPoint3 pos; + pc->Interp(fNextTime, &pos); + fNextDistSq = (pos - pt).MagnitudeSquared() - fMinDistSq; + if( fNextDistSq < 0 ) + fNextDistSq = -fNextDistSq; + + fPrevPos=fCurPos; + fCurPos=pos; + + return ICheckInterval(pt); +} + +// +// wireframe debug draw method. +// doesn't use any fancy subdivision or curvature measure when drawing. +// Changes current time. +// +void plAnimPath::IMakeSegment(hsTArray& idx, hsTArray& pos, + hsPoint3& p1, hsPoint3& p2) +{ + hsVector3 del(&p2, &p1); + hsVector3 up; + up.Set(0,0,1.f); + + const hsScalar kOutLength = 0.25f; + + hsVector3 a = del % up; + hsScalar magSq = a.MagnitudeSquared(); + if( magSq < 1.e-3f ) + { + a.Set(kOutLength, 0, 0); + } + else + { + a *= hsFastMath::InvSqrtAppr(magSq); + a *= kOutLength; + } + + hsVector3 b = a % del; + hsFastMath::Normalize(b); + b *= kOutLength; + + hsPoint3 p1out, p2out; + + int baseIdx = pos.GetCount(); + + pos.Append(p1); + pos.Append(p2); + + p1out = p1; + p1out += a; + p2out = p2; + p2out += a; + + pos.Append(p1out); + pos.Append(p2out); + + p1out += -2.f * a; + p2out += -2.f * a; + + pos.Append(p1out); + pos.Append(p2out); + + p1out = p1; + p1out += b; + p2out = p2; + p2out += b; + + pos.Append(p1out); + pos.Append(p2out); + + p1out += -2.f * b; + p2out += -2.f * b; + + pos.Append(p1out); + pos.Append(p2out); + + int i; + for( i = 0; i < 4; i++ ) + { + int outIdx = baseIdx + 2 + i * 2; + idx.Append(baseIdx); + idx.Append(baseIdx + 1); + idx.Append(baseIdx + outIdx); + + idx.Append(baseIdx + outIdx); + idx.Append(baseIdx + 1); + idx.Append(baseIdx + outIdx + 1); + } +} + +void plAnimPath::MakeDrawList(hsTArray& idx, hsTArray& pos) +{ + hsMatrix44 resetL2W = GetLocalToWorld(); + hsMatrix44 resetW2L = GetWorldToLocal(); + + hsMatrix44 ident; + ident.Reset(); + SetTransform(ident, ident); + + hsScalar numSegs = fRadius; // crude estimate of arclength + if (numSegs>100) + numSegs=100; + hsScalar animLen = GetLength(); + hsScalar timeInc = animLen/numSegs; + hsScalar time=0; + hsPoint3 p1, p2; + + SetCurTime(0, kCalcPosOnly); + GetPosition(&p1); + + time += timeInc; + hsBool quit=false; + while(! quit && time < animLen+timeInc) + { + if (time > animLen) + { + time = animLen; + quit=true; + } + + SetCurTime(time, kCalcPosOnly); + GetPosition(&p2); + + IMakeSegment(idx, pos, p1, p2); + + time += timeInc; + + p1 = p2; + } + + SetTransform(resetL2W, resetW2L); +} + +// +// Precompute array of arclen deltas for lookahead ability. +// Changes current time! +// +void plAnimPath::ComputeArcLenDeltas(Int32 numSamples) +{ + if (fArcLenDeltas.GetCount() >= numSamples) + return; // already computed enough samples + + // compute arc len deltas + fArcLenDeltas.Reset(); + fArcLenDeltas.SetCount(numSamples); + hsScalar animLen = GetLength(); + hsScalar timeInc = animLen/(numSamples-1); // use num-1 since we'll create the zeroth entry by hand + hsScalar time=0; + hsPoint3 p1, p2; + + Int32 cnt=0; + + // prime initial point + SetCurTime(0, kCalcPosOnly); + GetPosition(&p1); + ArcLenDeltaInfo aldi(time, 0); + fArcLenDeltas[cnt++]=aldi; + time += timeInc; + + hsBool quit=false; + while(!quit && time animLen || cnt+1 == numSamples) + { + time = animLen; + quit=true; + } + + SetCurTime(time, kCalcPosOnly); + GetPosition(&p2); + + ArcLenDeltaInfo aldi(time, hsVector3(&p2, &p1).Magnitude()); + fArcLenDeltas[cnt++]=aldi; + + time += timeInc; + p1 = p2; + } + hsAssert(fArcLenDeltas.GetCount()==numSamples, "arcLenArray size wrong?"); + hsAssert(cnt==numSamples, "arcLenArray size wrong?"); +} + +// +// Returns time of point (at least) arcLength units away from point at startTime. +// Also sets strtSrchIdx for incremental searching. +// +hsScalar plAnimPath::GetLookAheadTime(hsScalar startTime, hsScalar arcLengthIn, hsBool bwd, + Int32* startSrchIdx) +{ + if (arcLengthIn==0) + return startTime; // default is no look ahead + + if (startTime==GetLength() && !bwd) + return GetLength(); + + if (startTime==0 && bwd) + return 0; + + hsAssert(startSrchIdx, "nil var for startSrchIdx"); + + hsScalar oldTime=fTime; + + ComputeArcLenDeltas(); // precompute first time only + + // save and change time if necessary + if (fTime!=startTime) + SetCurTime(startTime, kCalcPosOnly); + + // find nearest (forward) arcLen sample point, use starting srch index provided + hsBool found=false; + Int32 i; + for(i=(*startSrchIdx); i=0; i+=inc) + { + if (curArcLen+fArcLenDeltas[i].fArcLenDelta>arcLengthIn) + { + // gone tooFar + endTime = fArcLenDeltas[i].fT; + curTime = fArcLenDeltas[i-1].fT; + break; + } + curArcLen += fArcLenDeltas[i].fArcLenDelta; + } + + if ( (i==fArcLenDeltas.GetCount() && !bwd) || (i<0 && bwd) ) + { + quit=true; + timeOut = bwd ? 0 : GetLength(); + } + } + else + { + curArcLen = 0; + curTime = startTime; + } + + if (!quit) + { + // interp remaining interval + + // 1. compute necessary distToGoal + hsScalar distToGoal = arcLengthIn-curArcLen; + hsAssert(distToGoal, "0 length distToGoal?"); + + // 2. compute % of dist interval which gives distToGoal + SetCurTime(curTime, kCalcPosOnly); + GetPosition(&pos); + + SetCurTime(endTime, kCalcPosOnly); + GetPosition(&pos2); + + hsScalar distInterval = hsVector3(&pos2, &pos).Magnitude(); + hsScalar percent = distToGoal/distInterval; + hsAssert(percent>=0 && percent<=1, "illegal percent value"); + + // 3. compute interpolated time value using percent + if (!bwd) + timeOut = curTime + (endTime-curTime)*percent; + else + timeOut = endTime - (endTime-curTime)*percent; + hsAssert((timeOut>=curTime && timeOut<=endTime), "illegal interpolated time value"); + // hsAssert(!bwd || (timeOut<=curTime && timeOut>=endTime), "bwd: illegal interpolated time value"); + } + + // restore time + if (fTime != oldTime) + SetCurTime(oldTime); + + hsAssert(bwd || (timeOut>startTime && timeOut<=GetLength()), "fwd: illegal look ahead time"); + hsAssert(!bwd || (timeOut=0), "bwd: illegal look ahead time"); + + return timeOut; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimPath.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimPath.h new file mode 100644 index 00000000..06665540 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimPath.h @@ -0,0 +1,172 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAnimPath_inc +#define plAnimPath_inc + +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "hsMatrix44.h" +#include "../plTransform/hsAffineParts.h" +#include "../pnFactory/plCreatable.h" + +class plCompoundController; + +class plAnimPath : public plCreatable +{ +public: + enum Flags + { + kNone = 0x0, + kFavorFwdSearch = 0x1, // only move fwd on the curve when searching + kFavorBwdSearch = 0x2, // only move bwd on the curve when searching + kCalcPosOnly = 0x4, // only compute pos when calling SetCurTime() + kFarthest = 0x8, + kWrap = 0x10, + kIncrement = 0x20, // find the nearest / farthest point, but increment toward it + }; +protected: + // The final product info + hsMatrix44 fXform; + hsPoint3 fPos; + hsVector3 fVel; + hsVector3 fAccel; + hsScalar fTime; // presumably seconds + + // The paramters (and options) for this curve. + UInt32 fAnimPathFlags; // currently set at runtime only + hsScalar fMinDistSq; + hsScalar fLength; // presumably seconds + + // Controller stuff only works in local space. + hsMatrix44 fLocalToWorld; + hsMatrix44 fWorldToLocal; + + // Bounding sphere available for ignoring out of range + hsPoint3 fCenter; + hsScalar fRadius; + + plCompoundController* fController; + + hsAffineParts fParts; + + // These are temps during a search. They're here to avoid recalc. + mutable hsScalar fLastTime; + mutable hsScalar fLastDistSq; + mutable hsScalar fThisTime; + mutable hsScalar fThisDistSq; + mutable hsScalar fNextTime; + mutable hsScalar fNextDistSq; + mutable hsScalar fDelTime; + mutable hsPoint3 fPrevPos, fCurPos; + + void ICalcBounds(); + hsScalar ICalcTotalLength(); + hsScalar IShiftFore(hsPoint3 &pt) const; + hsScalar IShiftBack(hsPoint3 &pt) const; + hsScalar ISubDivFore(hsPoint3 &pt) const; + hsScalar ISubDivBack(hsPoint3 &pt) const; + void IInitInterval(hsScalar time, hsScalar delTime, hsPoint3 &pt) const; + hsScalar ICheckInterval(hsPoint3 &pt) const; + hsScalar IBestTime() const { return fLastDistSq < fThisDistSq + ? (fLastDistSq < fNextDistSq + ? fLastTime + : fNextTime) + : (fThisDistSq < fNextDistSq + ? fThisTime + : fNextTime); } + + // Visualization helper + void IMakeSegment(hsTArray& idx, hsTArray& pos, + hsPoint3& p1, hsPoint3& p2); + + // For computing arclen + struct ArcLenDeltaInfo + { + hsScalar fT; + hsScalar fArcLenDelta; // arc len distance from prev sample point (array entry) + ArcLenDeltaInfo(hsScalar t, hsScalar del) : fT(t),fArcLenDelta(del) {} + ArcLenDeltaInfo() : fT(0),fArcLenDelta(0) {} + }; + hsTArray fArcLenDeltas; +public: + plAnimPath(); + virtual ~plAnimPath(); + + CLASSNAME_REGISTER( plAnimPath ); + GETINTERFACE_ANY( plAnimPath, plCreatable ); + + void Reset(); + + void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + const hsMatrix44& GetLocalToWorld() const { return fLocalToWorld; } + const hsMatrix44& GetWorldToLocal() const { return fWorldToLocal; } + + // Visualization helper + void MakeDrawList(hsTArray& idx, hsTArray& pos); + + void SetAnimPathFlags(UInt32 f) { fAnimPathFlags=f; } + UInt32 GetAnimPathFlags() const { return fAnimPathFlags; } + + void SetWrap(hsBool on) { if(on)fAnimPathFlags |= kWrap; else fAnimPathFlags &= ~kWrap; } + hsBool GetWrap() const { return 0 != (fAnimPathFlags & kWrap); } + + void SetFarthest(hsBool on) { if(on)fAnimPathFlags |= kFarthest; else fAnimPathFlags &= ~kFarthest; } + hsBool GetFarthest() const { return 0 != (fAnimPathFlags & kFarthest); } + + void SetCurTime(hsScalar t, UInt32 calcFlags=0); + hsScalar GetCurTime() const { return fTime; } + + void SetController(plCompoundController* tmc); + plCompoundController* GetController() const { return fController; } + hsScalar GetLength() const { return fLength; } // seconds + + void SetMinDistance(hsScalar d) { fMinDistSq = d*d; } + hsScalar GetMinDistance() const { return hsSquareRoot(fMinDistSq); } + + hsMatrix44* GetMatrix44(hsMatrix44* xOut) const { *xOut = fXform; return xOut; } + hsPoint3* GetPosition(hsPoint3* pOut) const { *pOut = fPos; return pOut; } + hsVector3* GetVelocity(hsVector3* vOut) const { *vOut = fVel; return vOut; } + hsVector3* GetDirection(hsVector3* dOut) const { dOut->Set(fXform.fMap[0][2], fXform.fMap[1][2], fXform.fMap[2][2]); return dOut; } + hsVector3* GetUp(hsVector3* uOut) const { uOut->Set(fXform.fMap[0][1], fXform.fMap[1][1], fXform.fMap[2][1]); return uOut; } + hsVector3* GetAcceleration(hsVector3* aOut) const { *aOut = fAccel; return aOut; } + + hsBool OutOfRange(hsPoint3 &pt, hsScalar range) const; + const hsAffineParts* Parts() const { return &fParts; } + void InitParts(const hsAffineParts& p) { fParts = p; } + + hsScalar GetExtremePoint(hsPoint3 &worldPt) const; // Exhaustive search + hsScalar GetExtremePoint(hsScalar lastTime, hsScalar delTime, hsPoint3 &worldPt) const; // Incremental search + + // for arclen usage + void ComputeArcLenDeltas(Int32 numSamples=256); + hsScalar GetLookAheadTime(hsScalar startTime, hsScalar arcLength, hsBool bwd, Int32* startSrchIdx); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + +#endif plAnimPath_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp new file mode 100644 index 00000000..0ed75b9e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.cpp @@ -0,0 +1,1386 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plAnimEaseTypes.h" +#include "plAnimTimeConvert.h" +#include "../plAvatar/plAGAnim.h" + +#include "hsTimer.h" +#include "hsStream.h" + +#include "../pnMessage/plEventCallbackMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plAvatar/plAGMasterSDLModifier.h" +#include "../plAvatar/plAGMasterMod.h" +#include "../plModifier/plLayerSDLModifier.h" +#include "../plSurface/plLayerAnimation.h" + +#include "hsResMgr.h" +#include "plgDispatch.h" + + +plAnimTimeConvert::plAnimTimeConvert() +: fCurrentAnimTime(0), + fLastEvalWorldTime(0), + fBegin(0), + fEnd(0), + fLoopEnd(0), + fLoopBegin(0), + fSpeed(1.f), + fFlags(0), + fOwner(nil), + fEaseInCurve(nil), + fEaseOutCurve(nil), + fSpeedEaseCurve(nil), + fCurrentEaseCurve(nil), + fInitialBegin(0), + fInitialEnd(0), + fWrapTime(0) + //fDirtyNotifier(nil) +{ +} + +plAnimTimeConvert::~plAnimTimeConvert() +{ + int i; + for( i = 0; i < fCallbackMsgs.GetCount(); i++ ) + hsRefCnt_SafeUnRef(fCallbackMsgs[i]); + + delete fEaseInCurve; + delete fEaseOutCurve; + delete fSpeedEaseCurve; + //delete fDirtyNotifier; + + IClearAllStates(); +} + +void plAnimTimeConvert::Init(plATCAnim *anim, plAGAnimInstance *instance, plAGMasterMod *master) +{ + // Set up our eval callbacks + plAGInstanceCallbackMsg *instMsg; + instMsg = TRACKED_NEW plAGInstanceCallbackMsg(master->GetKey(), kStart); + instMsg->fInstance = instance; + AddCallback(instMsg); + hsRefCnt_SafeUnRef(instMsg); + instMsg = TRACKED_NEW plAGInstanceCallbackMsg(master->GetKey(), kStop); + instMsg->fInstance = instance; + AddCallback(instMsg); + hsRefCnt_SafeUnRef(instMsg); + instMsg = TRACKED_NEW plAGInstanceCallbackMsg(master->GetKey(), kSingleFrameAdjust); + instMsg->fInstance = instance; + AddCallback(instMsg); + hsRefCnt_SafeUnRef(instMsg); + + SetOwner(master); + ClearFlags(); + + for (int i = 0; i < anim->NumStopPoints(); i++) + GetStopPoints().Append(anim->GetStopPoint(i)); + + SetBegin(anim->GetStart()); + SetEnd(anim->GetEnd()); + fInitialBegin = fBegin; + fInitialEnd = fEnd; + + if (anim->GetInitial() != -1) + SetCurrentAnimTime(anim->GetInitial()); + else + SetCurrentAnimTime(anim->GetStart()); + SetLoopPoints(anim->GetLoopStart(), anim->GetLoopEnd()); + Loop(anim->GetLoop()); + SetSpeed(1.f); + SetEase(true, anim->GetEaseInType(), anim->GetEaseInMin(), + anim->GetEaseInMax(), anim->GetEaseInLength()); + SetEase(false, anim->GetEaseOutType(), anim->GetEaseOutMin(), + anim->GetEaseOutMax(), anim->GetEaseOutLength()); + + // set up our time converter based on the animation's specs... + // ... after we've set all of its other state values. + if (anim->GetAutoStart()) + { + plSynchEnabler ps(true); // enable dirty tracking so that autostart will send out a state update + Start(); + } + else + InitStop(); +} + +// +// 0=nil, 1=easeIn, 2=easeOut, 3=speed +// +void plAnimTimeConvert::SetCurrentEaseCurve(int x) +{ + switch(x) + { + default: + hsAssert(false, "invalid arg to SetCurrentEaseCurve"); + break; + case kEaseNone: + fCurrentEaseCurve=nil; + break; + case kEaseIn: + fCurrentEaseCurve=fEaseInCurve; + break; + case kEaseOut: + fCurrentEaseCurve=fEaseOutCurve; + break; + case kEaseSpeed: + fCurrentEaseCurve=fSpeedEaseCurve; + break; + } +} + +int plAnimTimeConvert::GetCurrentEaseCurve() const +{ + if (fCurrentEaseCurve==nil) + return kEaseNone; + if (fCurrentEaseCurve==fEaseInCurve) + return kEaseIn; + if (fCurrentEaseCurve==fEaseOutCurve) + return kEaseOut; + if (fCurrentEaseCurve==fSpeedEaseCurve) + return kEaseSpeed; + + hsAssert(false, "unknown ease curve"); + return 0; +} + +// +// set the number of ATCStates we have +// +void plAnimTimeConvert::ResizeStates(int cnt) +{ + while (fStates.size() > cnt) + { + delete fStates.back(); + fStates.pop_back(); + } + + while (cnt>fStates.size()) + { + fStates.push_back(TRACKED_NEW plATCState); + } + + hsAssert(fStates.size()==cnt, "state resize mismatch"); +} + +void plAnimTimeConvert::ResetWrap() +{ + fBegin = fInitialBegin; + fEnd = fInitialEnd; + Forewards(); + + fFlags &= (~kWrap & ~kNeedsReset); +} + +hsScalar plAnimTimeConvert::ICalcEaseTime(const plATCEaseCurve *curve, double start, double end) +{ + start -= curve->fBeginWorldTime; + end -= curve->fBeginWorldTime; + + // Clamp to the curve's range + if (start < 0) + start = 0; + if (end > curve->fLength) + end = curve->fLength; + + + hsScalar delSecs = 0; + + if (start < curve->fLength) + { + // Redundant eval... but only when easing. + delSecs = curve->PositionGivenTime((hsScalar)end) - curve->PositionGivenTime((hsScalar)start); + } + return delSecs; +} + +void plAnimTimeConvert::IClearSpeedEase() +{ + + if (fCurrentEaseCurve == fSpeedEaseCurve) + fCurrentEaseCurve = nil; + + delete fSpeedEaseCurve; + fSpeedEaseCurve = nil; +} + +void plAnimTimeConvert::ICheckTimeCallbacks(hsScalar frameStart, hsScalar frameStop) +{ + int i; + for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i ) + { + if (fCallbackMsgs[i]->fEvent == kTime) + { + if( ITimeInFrame(fCallbackMsgs[i]->fEventTime, frameStart, frameStop) ) + ISendCallback(i); + } + else if (fCallbackMsgs[i]->fEvent == kBegin && ITimeInFrame(fBegin, frameStart, frameStop) ) + ISendCallback(i); + else if (fCallbackMsgs[i]->fEvent == kEnd && ITimeInFrame(fEnd, frameStart, frameStop) ) + ISendCallback(i); + + } +} + +hsBool plAnimTimeConvert::ITimeInFrame(hsScalar secs, hsScalar start, hsScalar stop) +{ + if (secs == start && secs == stop) + return true; + if( IsBackwards() ) + { + if( start < stop ) + { + // We've just wrapped. Careful to exclude markers outside current loop. + if( ((secs <= start) && (secs >= fLoopBegin)) + || ((secs >= stop) && (secs <= fLoopEnd)) ) + return true; + } + else + { + if( (secs <= start) && (secs >= stop) ) + return true; + } + } + else + { + if( start > stop ) + { + // We've just wrapped. Careful to exclude markers outside current loop. + if( ((secs >= start) && (secs <= fLoopEnd)) + || ((secs <= stop) && (secs >= fLoopBegin)) ) + return true; + } + else + { + if( (secs >= start) && (secs <= stop) ) + return true; + } + } + return false; +} + +void plAnimTimeConvert::ISendCallback(int i) +{ + // Check if callbacks are disabled this frame (i.e. when we're loading in state) + if (fFlags & kNoCallbacks) + return; + + // send callback if msg is local or if we are the local master + if (!fCallbackMsgs[i]->HasBCastFlag(plMessage::kNetPropagate) || + !fOwner || fOwner->IsLocallyOwned()==plSynchedObject::kYes) + { + plEventCallbackMsg *temp = fCallbackMsgs[i]; + + fCallbackMsgs[i]->SetSender(fOwner ? fOwner->GetKey() : nil); + + hsRefCnt_SafeRef(fCallbackMsgs[i]); + plgDispatch::MsgSend(fCallbackMsgs[i]); + + // No more repeats, remove this callback from our list + if (fCallbackMsgs[i]->fRepeats == 0) + { + hsRefCnt_SafeUnRef(fCallbackMsgs[i]); + fCallbackMsgs.Remove(i); + } + // If this isn't infinite, decrement the number of repeats + else if (fCallbackMsgs[i]->fRepeats > 0) + fCallbackMsgs[i]->fRepeats--; + } +} + +plAnimTimeConvert& plAnimTimeConvert::IStop(double time, hsScalar animTime) +{ + if( IsStopped() ) + return *this; + + IClearSpeedEase(); // If we had one queued up, clear it. It will automatically take effect when we start + SetFlag(kEasingIn, false); + SetFlag(kStopped, true); + if (fFlags & kNeedsReset) + ResetWrap(); + + IProcessStateChange(time, animTime); + + int i; + for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i ) + { + if (fCallbackMsgs[i]->fEvent == kStop) + { + ISendCallback(i); + } + } + return *this; +} + +plAnimTimeConvert& plAnimTimeConvert::IProcessStateChange(double worldTime, hsScalar animTime /* = -1 */) +{ + if (fStates.size() > 0 && worldTime < fStates.front()->fStartWorldTime) + return *this; // Sorry... state saves only work in the forward direction + + fLastStateChange = worldTime; + plATCState *state = TRACKED_NEW plATCState; + + state->fStartWorldTime = fLastStateChange; + state->fStartAnimTime = (animTime < 0 ? WorldToAnimTimeNoUpdate(fLastStateChange) : animTime); + state->fFlags = (UInt8)fFlags; + state->fBegin = fBegin; + state->fEnd = fEnd; + state->fLoopBegin = fLoopBegin; + state->fLoopEnd = fLoopEnd; + state->fSpeed = fSpeed; + state->fWrapTime = fWrapTime; + state->fEaseCurve = (fCurrentEaseCurve == nil ? nil : fCurrentEaseCurve->Clone()); + + fStates.push_front(state); + IFlushOldStates(); + + const char* sdlName=nil; + if (plLayerAnimation::ConvertNoRef(fOwner)) + sdlName=kSDLLayer; + else + if (plAGMasterMod::ConvertNoRef(fOwner)) + sdlName=kSDLAGMaster; + else + { + hsAssert(false, "unknown sdl owner"); + } + fOwner->DirtySynchState(sdlName, 0); // Send SDL state update to server + + return *this; +} + +// Remove any out-of-date plATCStates +// Where "out-of-date" means, "More than 1 frame old" +void plAnimTimeConvert::IFlushOldStates() +{ + plATCState *state; + plATCStateList::const_iterator i = fStates.begin(); + UInt32 count = 0; + + for (i; i != fStates.end(); i++) + { + count++; + state = *i; + if (fLastEvalWorldTime - hsTimer::GetDelSysSeconds() >= state->fStartWorldTime) + break; + } + + while (fStates.size() > count) + { + delete fStates.back(); + fStates.pop_back(); + } +} + +void plAnimTimeConvert::IClearAllStates() +{ + while (fStates.size() > 0) + { + delete fStates.back(); + fStates.pop_back(); + } +} + +plATCState *plAnimTimeConvert::IGetState(double wSecs) const +{ + plATCState *state; + plATCStateList::const_iterator i = fStates.begin(); + + for (i; i != fStates.end(); i++) + { + state = *i; + if (wSecs >= state->fStartWorldTime) + return state; + } + + return nil; +} + +plATCState *plAnimTimeConvert::IGetLatestState() const +{ + return fStates.front(); +} + +void plAnimTimeConvert::SetOwner(plSynchedObject* o) +{ + fOwner = o; +} + +hsBool plAnimTimeConvert::IIsStoppedAt(const double &wSecs, const UInt32 &flags, + const plATCEaseCurve *curve) const +{ + if (flags & kStopped) + return !(flags & kForcedMove); // If someone called SetCurrentAnimTime(), we need to say we moved. + + return false; +} + +hsBool plAnimTimeConvert::IsStoppedAt(double wSecs) const +{ + if (wSecs > fLastStateChange) + return IIsStoppedAt(wSecs, fFlags, fCurrentEaseCurve); + + plATCState *state = IGetState(wSecs); + + if ( !state ) + return true; + + return IIsStoppedAt(wSecs, state->fFlags, state->fEaseCurve); +} + +hsScalar plAnimTimeConvert::WorldToAnimTime(double wSecs) +{ + //hsAssert(wSecs >= fLastEvalWorldTime, "Tried to eval a time that's earlier than the last eval time."); + double d = wSecs - fLastEvalWorldTime; + hsScalar f = fCurrentAnimTime; + + if (wSecs < fLastStateChange) + { + fCurrentAnimTime = IWorldToAnimTimeBeforeState(wSecs); + fLastEvalWorldTime = wSecs; + return fCurrentAnimTime; + } + + if (fLastEvalWorldTime <= fLastStateChange) // Crossing into the latest state + { + fLastEvalWorldTime = fLastStateChange; + fCurrentAnimTime = IGetLatestState()->fStartAnimTime; + } + + if( (fFlags & kStopped) || (wSecs == fLastEvalWorldTime) ) + { + if (fFlags & kForcedMove) + { + int i; + for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i ) + { + if (fCallbackMsgs[i]->fEvent == kSingleFrameEval) + { + ISendCallback(i); + } + } + } + fFlags &= ~kForcedMove; + fLastEvalWorldTime = wSecs; + + return fCurrentAnimTime; + } + hsScalar note = fCurrentAnimTime - f; + hsScalar secs = 0, delSecs = 0; + + if (fCurrentEaseCurve != nil) + { + delSecs += ICalcEaseTime(fCurrentEaseCurve, fLastEvalWorldTime, wSecs); + if (wSecs > fCurrentEaseCurve->GetEndWorldTime()) + { + if (fFlags & kEasingIn) + delSecs += hsScalar(wSecs - fCurrentEaseCurve->GetEndWorldTime()) * fSpeed; + + IClearSpeedEase(); + + fCurrentEaseCurve = nil; + } + } + else + { + // The easy case... playing the animation at a constant speed. + delSecs = hsScalar(wSecs - fLastEvalWorldTime) * fSpeed; + } + + if (fFlags & kBackwards) + delSecs = -delSecs; + + secs = fCurrentAnimTime + delSecs; + // At this point, "secs" is the pre-wrapped (before looping) anim time. + // "delSecs" is the change in anim time + + // if our speed is < 0, then checking for the kBackwards flag isn't enough + // so we base our decision on the direction of the actual change we've computed. + hsBool forewards = delSecs >= 0; + + if (fFlags & kLoop) + { + hsBool wrapped = false; + + if (forewards) + { + if (IGetLatestState()->fStartAnimTime > fLoopEnd) + { + // Our animation started past the loop. Play to the end. + if (secs > fEnd) + { + secs = fEnd; + IStop(wSecs, secs); + } + } + else + { + if (secs > fLoopEnd) + { + secs = fmodf(secs - fLoopBegin, fLoopEnd - fLoopBegin) + fLoopBegin; + wrapped = true; + } + } + } + else + { + if (IGetLatestState()->fStartAnimTime < fLoopBegin) + { + if (secs < fBegin) + { + secs = fBegin; + IStop(wSecs, secs); + } + } + else + { + if (secs < fLoopBegin) + { + secs = fLoopEnd - fmodf(fLoopEnd - secs, fLoopEnd - fLoopBegin); + wrapped = true; + } + } + } + + if (fFlags & kWrap) + { + // possible options, representing each line: + // 1. We wrapped around the the beginning of the anim, so stop at the wrap point if we're past it. + // 2. Same as #1, but in the backwards case. + // 3. We started before the wrap point, now we're after it. Stop. + // 4. Same as #3, backwards. + if ((wrapped && (forewards && secs >= fWrapTime) || + (!forewards && secs <= fWrapTime)) || + (forewards && fCurrentAnimTime < fWrapTime && secs >= fWrapTime) || + (!forewards && fCurrentAnimTime > fWrapTime && secs <= fWrapTime)) + { + secs = fWrapTime; + IStop(wSecs, secs); + } + } + } + else // Not looping + { + if ((secs < fBegin) || (secs > fEnd)) + { + secs = forewards ? fEnd : fBegin; + IStop(wSecs, secs); + } + } + + ICheckTimeCallbacks(fCurrentAnimTime, secs); + + fLastEvalWorldTime = wSecs; + if (fEaseOutCurve != nil && !(fFlags & kEasingIn) && wSecs >= fEaseOutCurve->GetEndWorldTime()) + IStop(wSecs, secs); + + return fCurrentAnimTime = secs; +} + +hsScalar plAnimTimeConvert::WorldToAnimTimeNoUpdate(double wSecs) const +{ + return IWorldToAnimTimeNoUpdate(wSecs, IGetState(wSecs)); +} + +hsScalar plAnimTimeConvert::IWorldToAnimTimeNoUpdate(double wSecs, plATCState *state) +{ + //hsAssert(wSecs >= fLastEvalWorldTime, "Tried to eval a time that's earlier than the last eval time."); + if (state == nil) + return 0; + + if (state->fFlags & kStopped) + return state->fStartAnimTime; + + hsScalar secs = 0, delSecs = 0; + + if (state->fEaseCurve != nil) + { + delSecs += ICalcEaseTime(state->fEaseCurve, state->fStartWorldTime, wSecs); + if (wSecs > state->fEaseCurve->GetEndWorldTime()) + { + if (state->fFlags & kEasingIn) + delSecs += hsScalar(wSecs - state->fEaseCurve->GetEndWorldTime()) * state->fSpeed; + } + } + else + { + // The easy case... playing the animation at a constant speed. + delSecs = hsScalar(wSecs - state->fStartWorldTime) * state->fSpeed; + } + + if (state->fFlags & kBackwards) + delSecs = -delSecs; + + secs = state->fStartAnimTime + delSecs; + // At this point, "secs" is the pre-wrapped (before looping) anim time. + // "delSecs" is the change in anim time + hsBool forewards = delSecs >= 0; + + if (state->fFlags & kLoop) + { + hsBool wrapped = false; + + if (forewards) + { + if (state->fStartAnimTime > state->fLoopEnd) + { + // Our animation started past the loop. Play to the end. + if (secs > state->fEnd) + { + secs = state->fEnd; + } + } + else + { + if (secs > state->fLoopEnd) + { + secs = fmodf(secs - state->fLoopBegin, state->fLoopEnd - state->fLoopBegin) + state->fLoopBegin; + wrapped = true; + } + } + } + else + { + if (state->fStartAnimTime < state->fLoopBegin) + { + if (secs < state->fBegin) + { + secs = state->fBegin; + } + } + else + { + if (secs < state->fLoopBegin) + { + secs = state->fLoopEnd - fmodf(state->fLoopEnd - secs, state->fLoopEnd - state->fLoopBegin); + wrapped = true; + } + } + } + + if (state->fFlags & kWrap) + { + if ((wrapped && (forewards && secs >= state->fWrapTime) || + (!forewards && secs <= state->fWrapTime)) || + (forewards && state->fStartAnimTime < state->fWrapTime && secs >= state->fWrapTime) || + (!forewards && state->fStartAnimTime > state->fWrapTime && secs <= state->fWrapTime)) + { + secs = state->fWrapTime; + } + } + } + else + { + if ((secs < state->fBegin) || (secs > state->fEnd)) + { + secs = forewards ? state->fEnd : state->fBegin; + } + } + + return secs; +} + +hsScalar plAnimTimeConvert::IWorldToAnimTimeBeforeState(double wSecs) const +{ + return IWorldToAnimTimeNoUpdate(wSecs, IGetState(wSecs)); +} + +void plAnimTimeConvert::SetCurrentAnimTime(hsScalar s, hsBool jump /* = false */) +{ + // We're setting the anim value for whenever we last evaluated. + fFlags |= kForcedMove; + if (!jump) + ICheckTimeCallbacks(fCurrentAnimTime, s); + fCurrentAnimTime = s; + int i; + for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i ) + { + if (fCallbackMsgs[i]->fEvent == kSingleFrameAdjust) + { + ISendCallback(i); + } + } + IProcessStateChange(hsTimer::GetSysSeconds(), fCurrentAnimTime); +} + +void plAnimTimeConvert::SetEase(hsBool easeIn, UInt8 type, hsScalar minLength, hsScalar maxLength, hsScalar normLength) +{ + if (easeIn) + { + delete fEaseInCurve; + fEaseInCurve = plATCEaseCurve::CreateEaseCurve(type, minLength, maxLength, normLength, 0, fSpeed); + } + else + { + delete fEaseOutCurve; + fEaseOutCurve = plATCEaseCurve::CreateEaseCurve(type, minLength, maxLength, normLength, fSpeed, 0); + } +} + + + +hsScalar plAnimTimeConvert::GetBestStopDist(hsScalar min, hsScalar max, hsScalar norm, hsScalar time) const +{ + hsScalar bestTime = -1; + hsScalar bestDist = -1; + if (fStopPoints.GetCount() == 0) + return norm; + + hsScalar curTime; + hsScalar curDist; + + int i; + for (i = 0; i < fStopPoints.GetCount(); i++) + { + hsScalar stop = fStopPoints.Get(i); + + if (IsLooped()) + { + hsScalar loopDist; + if (IsBackwards()) + { + if ((time >= fLoopBegin && stop < fLoopBegin) || + (time < fLoopBegin && stop > fLoopBegin)) + continue; + loopDist = -(fLoopEnd - fLoopBegin); + } + else + { + if ((time <= fLoopEnd && stop > fLoopEnd) || + (time > fLoopEnd && stop < fLoopEnd)) + continue; // we'll never reach it. + loopDist = fLoopEnd - fLoopBegin; + } + if (stop <= fLoopEnd && stop >= fLoopBegin) + { + while (true) + { + curTime = stop - time; + if (IsBackwards()) + curTime = -curTime; + + if (curTime > max) + break; + + curDist = curTime - norm; + if (curDist < 0) + curDist = -curDist; + + if (curTime >= min && curTime <= max && (bestDist == -1 || bestDist > curDist)) + { + bestDist = curDist; + bestTime = curTime; + } + stop += loopDist; + } + + continue; + } + } + + curTime = stop - time; + if (IsBackwards()) + curTime = -curTime; + + curDist = curTime - norm; + if (curDist < 0) + curDist = -curDist; + + if (curTime >= min && curTime <= max && (bestDist == -1 || bestDist > curDist)) + { + bestDist = curDist; + bestTime = curTime; + } + } + + hsStatusMessageF("found stop point %f\n", bestTime); + + if (bestTime == -1) + bestTime = norm; + return bestTime; +} + +// Passing in a rate of zero specifies an immediate change. +void plAnimTimeConvert::SetSpeed(hsScalar goal, hsScalar rate /* = 0 */) +{ + hsScalar curSpeed = fSpeed; + fSpeed = goal; + + + if (rate == 0) + { + IClearSpeedEase(); + fCurrentEaseCurve = nil; + + } + // Skip if we're either stopped or stopping. We'll take the new speed into account next time we start up. + else if ((fFlags & kEasingIn)) + { + double curTime = hsTimer::GetSysSeconds(); + if (fCurrentEaseCurve != nil) + { + double easeTime = curTime - fCurrentEaseCurve->fBeginWorldTime; + curSpeed = fCurrentEaseCurve->VelocityGivenTime((hsScalar)easeTime); + } + if (fSpeedEaseCurve != nil) + { + fSpeedEaseCurve->RecalcToSpeed(curSpeed, goal); + fSpeedEaseCurve->SetLengthOnRate(rate); + } + else + { + hsScalar length; + length = (goal - curSpeed) / rate; + if (length < 0) + length = -length; + + fSpeedEaseCurve = plATCEaseCurve::CreateEaseCurve(plAnimEaseTypes::kConstAccel, length, length, length, + curSpeed, goal); + } + + fSpeedEaseCurve->fBeginWorldTime = curTime; + fCurrentEaseCurve = fSpeedEaseCurve; + } + + IProcessStateChange(hsTimer::GetSysSeconds()); +} + +void plAnimTimeConvert::Read(hsStream* s, hsResMgr* mgr) +{ + plCreatable::Read(s, mgr); + + fFlags = (UInt16)(s->ReadSwap32()); + + fBegin = fInitialBegin = s->ReadSwapScalar(); + fEnd = fInitialEnd = s->ReadSwapScalar(); + fLoopEnd = s->ReadSwapScalar(); + fLoopBegin = s->ReadSwapScalar(); + fSpeed = s->ReadSwapScalar(); + + fEaseInCurve = plATCEaseCurve::ConvertNoRef(mgr->ReadCreatable(s)); + fEaseOutCurve = plATCEaseCurve::ConvertNoRef(mgr->ReadCreatable(s)); + fSpeedEaseCurve = plATCEaseCurve::ConvertNoRef(mgr->ReadCreatable(s)); + + fCurrentAnimTime = s->ReadSwapScalar(); + fLastEvalWorldTime = s->ReadSwapDouble(); + + // load other non-synched data; + int count = s->ReadSwap32(); + fCallbackMsgs.SetCountAndZero(count); + + int i; + for (i = 0; i < count; i++) + { + plEventCallbackMsg* msg = plEventCallbackMsg::ConvertNoRef(mgr->ReadCreatable(s)); + fCallbackMsgs[i] = msg; + } + + count = s->ReadSwap32(); + for (i = 0; i < count; i++) + { + fStopPoints.Append(s->ReadSwapScalar()); + } + IProcessStateChange(0, fBegin); +} + +void plAnimTimeConvert::Write(hsStream* s, hsResMgr* mgr) +{ + plCreatable::Write(s, mgr); + + s->WriteSwap32(fFlags); + + s->WriteSwapScalar(fBegin); + s->WriteSwapScalar(fEnd); + s->WriteSwapScalar(fLoopEnd); + s->WriteSwapScalar(fLoopBegin); + s->WriteSwapScalar(fSpeed); + + mgr->WriteCreatable(s, fEaseInCurve); + mgr->WriteCreatable(s, fEaseOutCurve); + mgr->WriteCreatable(s, fSpeedEaseCurve); + + s->WriteSwapScalar(fCurrentAnimTime); + s->WriteSwapDouble(fLastEvalWorldTime); + + // save out other non-synched important data + s->WriteSwap32(fCallbackMsgs.Count()); + int i; + for (i = 0; i < fCallbackMsgs.Count(); i++) + mgr->WriteCreatable(s, fCallbackMsgs[i]); + + s->WriteSwap32(fStopPoints.GetCount()); + for (i = 0; i < fStopPoints.GetCount(); i++) + { + s->WriteSwapScalar(fStopPoints.Get(i)); + } +} + +plAnimTimeConvert& plAnimTimeConvert::InitStop() +{ + return IStop(hsTimer::GetSysSeconds(), fCurrentAnimTime); +} + +plAnimTimeConvert& plAnimTimeConvert::Stop(hsBool on) +{ + if( on ) + return Stop(); + else + return Start(); +} + +plAnimTimeConvert& plAnimTimeConvert::Stop(double stopTime) +{ + if( IsStopped() || (fEaseOutCurve != nil && !(fFlags & kEasingIn)) ) + return *this; + + if (stopTime < 0) + stopTime = hsTimer::GetSysSeconds(); + hsScalar stopAnimTime = WorldToAnimTimeNoUpdate(stopTime); + + SetFlag(kEasingIn, false); + + if( fEaseOutCurve == nil ) + { + return IStop(stopTime, fCurrentAnimTime); + } + + hsScalar currSpeed; + if (fCurrentEaseCurve == nil || stopTime >= fCurrentEaseCurve->GetEndWorldTime()) + currSpeed = fSpeed; + else + currSpeed = fCurrentEaseCurve->VelocityGivenTime((hsScalar)(stopTime - fCurrentEaseCurve->fBeginWorldTime)); + + fEaseOutCurve->RecalcToSpeed(currSpeed > fSpeed ? currSpeed : fSpeed, 0); + fEaseOutCurve->SetLengthOnDistance(GetBestStopDist(fEaseOutCurve->GetMinDistance(), fEaseOutCurve->GetMaxDistance(), + fEaseOutCurve->GetNormDistance(), stopAnimTime)); + fEaseOutCurve->fBeginWorldTime = stopTime - fEaseOutCurve->TimeGivenVelocity(currSpeed); + + fCurrentEaseCurve = fEaseOutCurve; + + return IProcessStateChange(stopTime); +} + +plAnimTimeConvert& plAnimTimeConvert::Start(double startTime) +{ + // If start has not been called since the last stop, kEasingIn will not be set + if( (fFlags & kEasingIn) && (startTime == fLastStateChange) ) + return *this; + + SetFlag(kEasingIn, true); + + if (startTime < 0) + startTime = hsTimer::GetSysSeconds(); + + if (fEaseInCurve != nil) + { + hsScalar currSpeed; + if (fCurrentEaseCurve == nil || startTime >= fCurrentEaseCurve->GetEndWorldTime()) + currSpeed = 0; + else + currSpeed = fCurrentEaseCurve->VelocityGivenTime((hsScalar)(startTime - fCurrentEaseCurve->fBeginWorldTime)); + + if (currSpeed <= fSpeed) + { + fEaseInCurve->RecalcToSpeed(0, fSpeed); + fEaseInCurve->fBeginWorldTime = startTime - fEaseInCurve->TimeGivenVelocity(currSpeed); + + fCurrentEaseCurve = fEaseInCurve; + + } + else + { // We eased out in the middle of a speed change, but were told to start again before slowing past + // the target speed, so the "ease in" is really a slow down. + SetSpeed(fSpeed, fEaseInCurve->fSpeed / fEaseInCurve->fLength); + } + } + + // check for a start callback + int i; + for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i ) + { + if (fCallbackMsgs[i]->fEvent == kStart) + { + ISendCallback(i); + } + } + + SetFlag(kStopped, false); + if (fFlags & kBackwards) + { + if (fCurrentAnimTime == fBegin) + return IProcessStateChange(startTime, fEnd); + } + else + { + if (fCurrentAnimTime == fEnd) + return IProcessStateChange(startTime, fBegin); + } + return IProcessStateChange(startTime); +} + +plAnimTimeConvert& plAnimTimeConvert::Backwards(hsBool on) +{ + return on ? Backwards() : Forewards(); +} + +plAnimTimeConvert& plAnimTimeConvert::Backwards() +{ + if( IsBackwards() ) + return *this; + + // check for a reverse callback + int i; + for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i ) + { + if (fCallbackMsgs[i]->fEvent == kReverse) + { + ISendCallback(i); + } + } + + SetFlag(kBackwards, true); + + // Record state changes + IProcessStateChange(hsTimer::GetSysSeconds(), fCurrentAnimTime); + + return *this; +} + +plAnimTimeConvert& plAnimTimeConvert::Forewards() +{ + if( !IsBackwards() ) + return *this; + + // check for a reverse callback + int i; + for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i ) + { + if (fCallbackMsgs[i]->fEvent == kReverse) + { + ISendCallback(i); + } + } + + SetFlag(kBackwards, false); + + // Record state changes + IProcessStateChange(hsTimer::GetSysSeconds(), fCurrentAnimTime); + + return *this; +} + +plAnimTimeConvert& plAnimTimeConvert::Loop(hsBool on) +{ + SetFlag(kLoop, on); + + // Record state changes + IProcessStateChange(hsTimer::GetSysSeconds(), fCurrentAnimTime); + + return *this; +} + +plAnimTimeConvert& plAnimTimeConvert::PlayToTime(hsScalar time) +{ + fFlags |= kNeedsReset; + if (fCurrentAnimTime > time) + { + if (fFlags & kLoop) + { + fWrapTime = time; + fFlags |= kWrap; + } + else + { + fBegin = time; + Backwards(); + } + } + else + { + fEnd = time; + } + Start(); + + return *this; +} + +plAnimTimeConvert& plAnimTimeConvert::PlayToPercentage(hsScalar percent) +{ + return PlayToTime(fBegin + (fEnd - fBegin) * percent); +} + +void plAnimTimeConvert::RemoveCallback(plEventCallbackMsg* pMsg) +{ + int idx = fCallbackMsgs.Find(pMsg); + if( idx != fCallbackMsgs.kMissingIndex ) + { + hsRefCnt_SafeUnRef(fCallbackMsgs[idx]); + fCallbackMsgs.Remove(idx); + } +} + +hsBool plAnimTimeConvert::HandleCmd(plAnimCmdMsg* modMsg) +{ + if (fFlags & kNeedsReset) + ResetWrap(); + + // The net msg screener is already checking for callbacks, + // I'm just being extra safe. + if (!modMsg->HasBCastFlag(plMessage::kNetCreatedRemotely)) + { + if( modMsg->Cmd(plAnimCmdMsg::kAddCallbacks) ) + { + int i; + for( i = 0; i < modMsg->GetNumCallbacks(); i++ ) + { + AddCallback(plEventCallbackMsg::ConvertNoRef(modMsg->GetEventCallback(i))); + } + } + if( modMsg->Cmd(plAnimCmdMsg::kRemoveCallbacks) ) + { + int i; + for( i = 0; i < modMsg->GetNumCallbacks(); i++ ) + { + RemoveCallback(modMsg->GetEventCallback(i)); + } + } + } + + if( modMsg->Cmd(plAnimCmdMsg::kSetBackwards) ) + { + Backwards(); + } + if( modMsg->Cmd(plAnimCmdMsg::kSetForewards) ) + { + Forewards(); + } + + if( modMsg->Cmd(plAnimCmdMsg::kStop) ) + Stop(); + + if( modMsg->Cmd(plAnimCmdMsg::kSetLooping) ) + Loop(); + + if( modMsg->Cmd(plAnimCmdMsg::kUnSetLooping) ) + NoLoop(); + + if (modMsg->Cmd(plAnimCmdMsg::kSetBegin)) + { + if (modMsg->fBegin >= fInitialBegin) + SetBegin(modMsg->fBegin); + else + SetBegin(fInitialBegin); + } + + if (modMsg->Cmd(plAnimCmdMsg::kSetEnd)) + { + if (modMsg->fEnd <= fInitialEnd) + SetEnd(modMsg->fEnd); + else + SetEnd(fInitialEnd); + } + + if (fBegin > fEnd) + { + fBegin = fInitialBegin; + fEnd = fInitialEnd; + } + + if( modMsg->Cmd(plAnimCmdMsg::kSetLoopEnd) ) + SetLoopEnd(modMsg->fLoopEnd); + + if( modMsg->Cmd(plAnimCmdMsg::kSetLoopBegin) ) + SetLoopBegin(modMsg->fLoopBegin); + + if( modMsg->Cmd(plAnimCmdMsg::kSetSpeed) ) + SetSpeed(modMsg->fSpeed, modMsg->fSpeedChangeRate); + + if( modMsg->Cmd(plAnimCmdMsg::kGoToTime) ) + { + if (modMsg->fTime < fBegin) + SetCurrentAnimTime(fBegin, true); + else if (modMsg->fTime > fEnd) + SetCurrentAnimTime(fEnd, true); + else + SetCurrentAnimTime(modMsg->fTime, true); + } + + if ( modMsg->Cmd(plAnimCmdMsg::kGoToPercent) ) + SetCurrentAnimTime(fBegin + (fEnd - fBegin) * modMsg->fTime); + + if( modMsg->Cmd(plAnimCmdMsg::kGoToBegin) ) + SetCurrentAnimTime(fBegin, true); + + if( modMsg->Cmd(plAnimCmdMsg::kGoToEnd) ) + SetCurrentAnimTime(fEnd, true); + + if( modMsg->Cmd(plAnimCmdMsg::kGoToLoopBegin) ) + SetCurrentAnimTime(fLoopBegin, true); + + if( modMsg->Cmd(plAnimCmdMsg::kGoToLoopEnd) ) + SetCurrentAnimTime(fLoopEnd, true); + + if( modMsg->Cmd(plAnimCmdMsg::kToggleState) ) + { + if( IsStopped() ) + { + Start(); + } + else + { + Stop(); + } + } + if( modMsg->Cmd(plAnimCmdMsg::kContinue) ) + { + Start(); + } + if( modMsg->Cmd(plAnimCmdMsg::kIncrementForward) ) + { + if (fCurrentAnimTime == fEnd) + return true; + double currTime = hsTimer::GetSysSeconds(); + hsScalar newTime = fCurrentAnimTime + hsTimer::GetDelSysSeconds(); + if (newTime > fEnd) + { + newTime = fEnd; + } + Forewards(); + SetCurrentAnimTime(newTime); + } + if( modMsg->Cmd(plAnimCmdMsg::kIncrementBackward) ) + { + if (fCurrentAnimTime == fBegin) + return true; + double currTime = hsTimer::GetSysSeconds(); + hsScalar newTime = fCurrentAnimTime - hsTimer::GetDelSysSeconds(); + if (newTime < fBegin) + { + newTime = fBegin; + } + Backwards(); + SetCurrentAnimTime(newTime); + } + + if (modMsg->Cmd(plAnimCmdMsg::kPlayToTime)) + PlayToTime(modMsg->fTime); + + if (modMsg->Cmd(plAnimCmdMsg::kPlayToPercentage)) + PlayToPercentage(modMsg->fTime); + + // Basically, simulate what would happen if we played the animation + if (modMsg->Cmd(plAnimCmdMsg::kFastForward)) + { + if (IsForewards()) + SetCurrentAnimTime(fEnd, true); + else + SetCurrentAnimTime(fBegin, true); + // but if it should continue to play, statr it. + if (IsLooped()) + Start(); + } + + return true; +} + +void plAnimTimeConvert::AddCallback(plEventCallbackMsg* pMsg) +{ + hsRefCnt_SafeRef(pMsg); + fCallbackMsgs.Append(pMsg); +} + +void plAnimTimeConvert::ClearCallbacks() +{ + for (int i = 0; iReadSwapDouble(); + fStartAnimTime = s->ReadSwapScalar(); + + fFlags = (UInt8)(s->ReadSwap32()); + fEnd = s->ReadSwapScalar(); + fLoopBegin = s->ReadSwapScalar(); + fLoopEnd = s->ReadSwapScalar(); + fSpeed = s->ReadSwapScalar(); + fWrapTime = s->ReadSwapScalar(); + if (s->ReadBool()) + fEaseCurve = plATCEaseCurve::ConvertNoRef(mgr->ReadCreatable(s)); +} + +void plATCState::Write(hsStream *s, hsResMgr *mgr) +{ + s->WriteSwapDouble(fStartWorldTime); + s->WriteSwapScalar(fStartAnimTime); + + s->WriteSwap32(fFlags); + s->WriteSwapScalar(fEnd); + s->WriteSwapScalar(fLoopBegin); + s->WriteSwapScalar(fLoopEnd); + s->WriteSwapScalar(fSpeed); + s->WriteSwapScalar(fWrapTime); + if (fEaseCurve != nil) + { + s->WriteBool(true); + mgr->WriteCreatable(s, fEaseCurve); + } + else + s->WriteBool(false); +} + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.h new file mode 100644 index 00000000..a4ce84c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plAnimTimeConvert.h @@ -0,0 +1,306 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAnimTimeConvert_inc +#define plAnimTimeConvert_inc + +#include "../pnFactory/plCreatable.h" +#include "hsTemplates.h" +#include "../pnNetCommon/plSynchedValue.h" + +#pragma warning (disable: 4284) + +class plSynchedObject; +class plAnimCmdMsg; +class plEventCallbackMsg; +class plATCEaseCurve; +class plATCState; +class plATCAnim; +class plAGMasterMod; + +class plAnimTimeConvert : public plCreatable +{ + friend class plAnimTimeConvertSDLModifier; + friend class plAGAnimInstance; + +protected: + UInt16 fFlags; + hsScalar fBegin; + hsScalar fEnd; + hsScalar fLoopEnd; + hsScalar fLoopBegin; + hsScalar fSpeed; + hsScalar fCurrentAnimTime; + hsScalar fWrapTime; + double fLastEvalWorldTime; + // Do not change fLastEvalWorldTime anywhere except WorldToAnimTime() + + plSynchedObject* fOwner; + double fLastStateChange; + + typedef std::list plATCStateList; + plATCStateList fStates; + + hsTArray fStopPoints; + hsTArray fCallbackMsgs; + + ///////////////////////// + // Ease In/Out stuff + + plATCEaseCurve *fEaseInCurve; + plATCEaseCurve *fEaseOutCurve; + plATCEaseCurve *fSpeedEaseCurve; + plATCEaseCurve *fCurrentEaseCurve; // One of the above, or nil + + // + ///////////////////////// + + hsScalar fInitialBegin; + hsScalar fInitialEnd; + + static hsScalar ICalcEaseTime(const plATCEaseCurve *curve, double start, double end); + void IClearSpeedEase(); + + void ICheckTimeCallbacks(hsScalar frameStart, hsScalar frameStop); + hsBool ITimeInFrame(hsScalar secs, hsScalar start, hsScalar stop); + void ISendCallback(int i); + + plAnimTimeConvert& IStop(double time, hsScalar animTime); + hsBool IIsStoppedAt(const double &wSecs, const UInt32 &flags, const plATCEaseCurve *curve) const; + plAnimTimeConvert& IProcessStateChange(double worldTime, hsScalar animTime = -1); + void IFlushOldStates(); + void IClearAllStates(); + plATCState *IGetState(double wSecs) const; + plATCState *IGetLatestState() const; + + plAnimTimeConvert& SetFlag(UInt8 f, hsBool on) { if(on)fFlags |= f; else fFlags &= ~f; return *this; } + +public: + plAnimTimeConvert(); + virtual ~plAnimTimeConvert(); + + void Init(plATCAnim *anim, plAGAnimInstance *instance, plAGMasterMod *master); + + CLASSNAME_REGISTER( plAnimTimeConvert ); + GETINTERFACE_ANY( plAnimTimeConvert, plCreatable ); + + void SetOwner(plSynchedObject* o); + const plSynchedObject* GetOwner() const { return fOwner; } + + // ALL WorldToAnimTime functions are only valid if called with a time >= fLastEvalWorldTime. + hsBool IsStoppedAt(double wSecs) const; + hsScalar WorldToAnimTime(double wSecs); + hsScalar WorldToAnimTimeNoUpdate(double wSecs) const; // convert time but don't fire triggers or set state + +protected: + static hsScalar IWorldToAnimTimeNoUpdate(double wSecs, plATCState *state); + hsScalar IWorldToAnimTimeBeforeState(double wSecs) const; + +public: + void SetBegin(hsScalar s) { fBegin = s; } + void SetEnd(hsScalar s) { fEnd = s; } + void SetSpeed(hsScalar goal, hsScalar rate = 0); + void SetLoopPoints(hsScalar begin, hsScalar end) { SetLoopBegin(begin); SetLoopEnd(end); } + void SetLoopBegin(hsScalar s) { fLoopBegin = s; } + void SetLoopEnd(hsScalar s) { fLoopEnd = s; } + void SetEase(hsBool easeIn, UInt8 inType, hsScalar minLength, hsScalar maxLength, hsScalar inLength); + void SetCurrentEaseCurve(int x); // 0=nil, 1=easeIn, 2=easeOut, 3=speed + + hsScalar GetBegin() const { return fBegin; } + hsScalar GetEnd() const { return fEnd; } + hsScalar GetLoopBegin() const { return fLoopBegin; } + hsScalar GetLoopEnd() const { return fLoopEnd; } + hsScalar GetSpeed() const { return fSpeed; } + hsTArray &GetStopPoints() { return fStopPoints; } + hsScalar GetBestStopDist(hsScalar min, hsScalar max, hsScalar norm, hsScalar time) const; + int GetCurrentEaseCurve() const; // returns 0=nil, 1=easeIn, 2=easeOut, 3=speed + + void ResizeStates(int cnt); + void ResetWrap(); + + plAnimTimeConvert& ClearFlags() { fFlags = kNone; return *this; } + hsBool GetFlag(UInt8 f) const { return (fFlags & f) ? true : false; } + + plAnimTimeConvert& InitStop(); // Called when initializing an anim that doesn't autostart + plAnimTimeConvert& Stop(hsBool on); + plAnimTimeConvert& Stop(double s = -1.0); + plAnimTimeConvert& Start(double s = -1.0); + plAnimTimeConvert& PlayToTime(hsScalar time); + plAnimTimeConvert& PlayToPercentage(hsScalar percent); // zero to one. + + plAnimTimeConvert& Loop(hsBool on); + plAnimTimeConvert& Loop() { return Loop(true); } + plAnimTimeConvert& NoLoop() { return Loop(false); } + + plAnimTimeConvert& Backwards(hsBool on); + plAnimTimeConvert& Backwards(); + plAnimTimeConvert& Forewards(); + + hsBool IsStopped() const { return 0 != (fFlags & kStopped); } + hsBool IsLooped() const { return 0 != (fFlags & kLoop); } + hsBool IsBackwards() const { return 0 != (fFlags & kBackwards); } + hsBool IsForewards() const { return !(fFlags & kBackwards); } + + double LastEvalWorldTime() const { return fLastEvalWorldTime; } + hsScalar CurrentAnimTime() const { return fCurrentAnimTime; } + void SetCurrentAnimTime(hsScalar s, hsBool jump = false); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + hsBool HandleCmd(plAnimCmdMsg* msg); + void AddCallback(plEventCallbackMsg* pMsg); + void RemoveCallback(plEventCallbackMsg* pMsg); + void ClearCallbacks(); + void EnableCallbacks(hsBool val); + + enum plAnimTimeFlags { + kNone = 0x0, + kStopped = 0x1, + kLoop = 0x2, + kBackwards = 0x4, + kWrap = 0x8, + kNeedsReset = 0x10, + kEasingIn = 0x20, + kForcedMove = 0x40, + kNoCallbacks = 0x80, + + kFlagsMask = 0xff + }; + + enum plEaseCurveType { + kEaseNone, + kEaseIn, + kEaseOut, + kEaseSpeed, + }; + +}; + +// Rules for happy ease curves: +// 1. Any time value between 0 and fLength is kosher. +// 2. Velocity values accepted/returned are in the range [fStartSpeed, fSpeed] +// (some tolerance for values REALLY close to the limit, to account for floating-point inaccuracy) + +class plATCEaseCurve : public plCreatable +{ +protected: + hsScalar fStartSpeed; + hsScalar fMinLength; + hsScalar fMaxLength; + hsScalar fNormLength; + +public: + CLASSNAME_REGISTER( plATCEaseCurve ); + GETINTERFACE_ANY( plATCEaseCurve, plCreatable ); + + double fBeginWorldTime; + hsScalar fLength; + hsScalar fSpeed; // The anim's target ("full") speed. + + static plATCEaseCurve *CreateEaseCurve(UInt8 type, hsScalar minLength, hsScalar maxLength, hsScalar normLength, + hsScalar startSpeed, hsScalar goalSpeed); + + double GetEndWorldTime() const { return fBeginWorldTime + fLength; } + + virtual plATCEaseCurve *Clone() const = 0; + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); + + virtual void RecalcToSpeed(hsScalar startSpeed, hsScalar goalSpeed, hsBool preserveRate = false); + virtual void SetLengthOnRate(hsScalar rate); + virtual void SetLengthOnDistance(hsScalar dist) = 0; + virtual hsScalar PositionGivenTime(hsScalar time) const = 0; + virtual hsScalar VelocityGivenTime(hsScalar time) const = 0; + virtual hsScalar TimeGivenVelocity(hsScalar velocity) const = 0; + virtual hsScalar GetMinDistance(); + virtual hsScalar GetMaxDistance(); + virtual hsScalar GetNormDistance(); +}; + +class plConstAccelEaseCurve : public plATCEaseCurve +{ +public: + plConstAccelEaseCurve(); + plConstAccelEaseCurve(hsScalar minLength, hsScalar maxLength, hsScalar length, + hsScalar startSpeed, hsScalar goalSpeed); + + CLASSNAME_REGISTER( plConstAccelEaseCurve ); + GETINTERFACE_ANY( plConstAccelEaseCurve, plATCEaseCurve ); + + virtual plATCEaseCurve *Clone() const; + virtual void SetLengthOnDistance(hsScalar dist); + virtual hsScalar PositionGivenTime(hsScalar time) const; + virtual hsScalar VelocityGivenTime(hsScalar time) const; + virtual hsScalar TimeGivenVelocity(hsScalar velocity) const; +}; + +class plSplineEaseCurve : public plATCEaseCurve +{ +public: + plSplineEaseCurve(); + plSplineEaseCurve(hsScalar minLength, hsScalar maxLength, hsScalar length, + hsScalar startSpeed, hsScalar goalSpeed); + + CLASSNAME_REGISTER( plSplineEaseCurve ); + GETINTERFACE_ANY( plSplineEaseCurve, plATCEaseCurve ); + + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); + + virtual plATCEaseCurve *Clone() const; + virtual void RecalcToSpeed(hsScalar startSpeed, hsScalar goalSpeed, hsBool preserveRate = false); + virtual void SetLengthOnDistance(hsScalar dist); + virtual hsScalar PositionGivenTime(hsScalar time) const; + virtual hsScalar VelocityGivenTime(hsScalar time) const; + virtual hsScalar TimeGivenVelocity(hsScalar velocity) const; + + hsScalar fCoef[4]; +}; + +class plATCState +{ +public: + plATCState() : fEaseCurve(nil) {} + ~plATCState() { delete fEaseCurve; } + + void Read(hsStream *s, hsResMgr *mgr); + void Write(hsStream *s, hsResMgr *mgr); + + double fStartWorldTime; + hsScalar fStartAnimTime; + + UInt8 fFlags; + hsScalar fBegin; + hsScalar fEnd; + hsScalar fLoopBegin; + hsScalar fLoopEnd; + hsScalar fSpeed; + hsScalar fWrapTime; + plATCEaseCurve *fEaseCurve; +}; + + +#endif // plAnimTimeConvert_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plController.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plController.cpp new file mode 100644 index 00000000..7d845796 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plController.cpp @@ -0,0 +1,900 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plController.h" +#include "hsInterp.h" +#include "hsResMgr.h" + +#include "../plTransform/hsEuler.h" +#include "plAnimTimeConvert.h" + +///////////////////////////////////////////// +// Controller interp caching +///////////////////////////////////////////// + +static const char *kInvalidInterpString = "Invalid call to plController::Interp()"; + +plControllerCacheInfo::plControllerCacheInfo() : fNumSubControllers(0), fSubControllers(nil), fKeyIndex(0), fAtc(nil) {} + +plControllerCacheInfo::~plControllerCacheInfo() +{ + int i; + for (i = 0; i < fNumSubControllers; i++) + delete fSubControllers[i]; + + delete [] fSubControllers; +} + +void plControllerCacheInfo::SetATC(plAnimTimeConvert *atc) +{ + fAtc = atc; + int i; + for (i = 0; i < fNumSubControllers; i++) + if (fSubControllers[i]) + fSubControllers[i]->SetATC(atc); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +plLeafController::~plLeafController() +{ + delete [] fKeys; +} + +void plLeafController::Interp(hsScalar time, hsScalar* result, plControllerCacheInfo *cache) const +{ + hsAssert(fType == hsKeyFrame::kScalarKeyFrame || fType == hsKeyFrame::kBezScalarKeyFrame, kInvalidInterpString); + + hsBool tryForward = (cache? cache->fAtc->IsForewards() : true); + if (fType == hsKeyFrame::kScalarKeyFrame) + { + hsScalarKey *k1, *k2; + hsScalar t; + UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx); + hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsScalarKey), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward); + hsInterp::LinInterp(k1->fValue, k2->fValue, t, result); + } + else + { + hsBezScalarKey *k1, *k2; + hsScalar t; + UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx); + hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsBezScalarKey), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward); + hsInterp::BezInterp(k1, k2, t, result); + } +} + +void plLeafController::Interp(hsScalar time, hsScalarTriple* result, plControllerCacheInfo *cache) const +{ + hsAssert(fType == hsKeyFrame::kPoint3KeyFrame || fType == hsKeyFrame::kBezPoint3KeyFrame, kInvalidInterpString); + + hsBool tryForward = (cache? cache->fAtc->IsForewards() : true); + if (fType == hsKeyFrame::kPoint3KeyFrame) + { + hsPoint3Key *k1, *k2; + hsScalar t; + UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx); + hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsPoint3Key), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward); + hsInterp::LinInterp(&k1->fValue, &k2->fValue, t, result); + } + else + { + hsBezPoint3Key *k1, *k2; + hsScalar t; + UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx); + hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsBezPoint3Key), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward); + hsInterp::BezInterp(k1, k2, t, result); + } +} + +void plLeafController::Interp(hsScalar time, hsScaleValue* result, plControllerCacheInfo *cache) const +{ + hsAssert(fType == hsKeyFrame::kScaleKeyFrame || fType == hsKeyFrame::kBezScaleKeyFrame, kInvalidInterpString); + + hsBool tryForward = (cache? cache->fAtc->IsForewards() : true); + if (fType == hsKeyFrame::kScaleKeyFrame) + { + hsScaleKey *k1, *k2; + hsScalar t; + UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx); + hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsScaleKey), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward); + hsInterp::LinInterp(&k1->fValue, &k2->fValue, t, result); + } + else + { + hsBezScaleKey *k1, *k2; + hsScalar t; + UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx); + hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsBezScaleKey), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward); + hsInterp::BezInterp(k1, k2, t, result); + } +} + +void plLeafController::Interp(hsScalar time, hsQuat* result, plControllerCacheInfo *cache) const +{ + hsAssert(fType == hsKeyFrame::kQuatKeyFrame || + fType == hsKeyFrame::kCompressedQuatKeyFrame32 || + fType == hsKeyFrame::kCompressedQuatKeyFrame64, kInvalidInterpString); + + hsBool tryForward = (cache? cache->fAtc->IsForewards() : true); + if (fType == hsKeyFrame::kQuatKeyFrame) + { + hsQuatKey *k1, *k2; + hsScalar t; + UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx); + hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsQuatKey), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward); + hsInterp::LinInterp(&k1->fValue, &k2->fValue, t, result); + } + else if (fType == hsKeyFrame::kCompressedQuatKeyFrame32) + { + hsCompressedQuatKey32 *k1, *k2; + hsScalar t; + UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx); + hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsCompressedQuatKey32), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward); + + hsQuat q1, q2; + k1->GetQuat(q1); + k2->GetQuat(q2); + hsInterp::LinInterp(&q1, &q2, t, result); + } + else // (fType == hsKeyFrame::kCompressedQuatKeyFrame64) + { + hsCompressedQuatKey64 *k1, *k2; + hsScalar t; + UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx); + hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsCompressedQuatKey64), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward); + + hsQuat q1, q2; + k1->GetQuat(q1); + k2->GetQuat(q2); + hsInterp::LinInterp(&q1, &q2, t, result); + } +} + +void plLeafController::Interp(hsScalar time, hsMatrix33* result, plControllerCacheInfo *cache) const +{ + hsAssert(fType == hsKeyFrame::kMatrix33KeyFrame, kInvalidInterpString); + + hsBool tryForward = (cache? cache->fAtc->IsForewards() : true); + hsMatrix33Key *k1, *k2; + hsScalar t; + UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx); + hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsMatrix33Key), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward); + hsInterp::LinInterp(&k1->fValue, &k2->fValue, t, result); +} + +void plLeafController::Interp(hsScalar time, hsMatrix44* result, plControllerCacheInfo *cache) const +{ + hsAssert(fType == hsKeyFrame::kMatrix44KeyFrame, kInvalidInterpString); + + hsBool tryForward = (cache? cache->fAtc->IsForewards() : true); + hsMatrix44Key *k1, *k2; + hsScalar t; + UInt32 *idxStore = (cache ? &cache->fKeyIndex : &fLastKeyIdx); + hsInterp::GetBoundaryKeyFrames(time, fNumKeys, fKeys, sizeof(hsMatrix44Key), (hsKeyFrame**)&k1, (hsKeyFrame**)&k2, idxStore, &t, tryForward); + hsInterp::LinInterp(&k1->fValue, &k2->fValue, t, result); +} + +void plLeafController::Interp(hsScalar time, hsColorRGBA* result, plControllerCacheInfo *cache) const +{ + hsPoint3 value; + Interp(time, &value, cache); + result->r = value.fX; + result->g = value.fY; + result->b = value.fZ; +} + +plControllerCacheInfo *plLeafController::CreateCache() const +{ + plControllerCacheInfo *cache = TRACKED_NEW plControllerCacheInfo; + cache->fNumSubControllers = 0; + return cache; +} + +hsScalar plLeafController::GetLength() const +{ + UInt32 stride = GetStride(); + if (stride == 0 || fNumKeys == 0) + return 0; + + UInt8 *ptr = (UInt8 *)fKeys; + return ((hsKeyFrame *)(ptr + (fNumKeys - 1) * stride))->fFrame / MAX_FRAMES_PER_SEC; +} + +UInt32 plLeafController::GetStride() const +{ + switch (fType) + { + case hsKeyFrame::kPoint3KeyFrame: + return sizeof(hsPoint3Key); + case hsKeyFrame::kBezPoint3KeyFrame: + return sizeof(hsBezPoint3Key); + case hsKeyFrame::kScalarKeyFrame: + return sizeof(hsScalarKey); + case hsKeyFrame::kBezScalarKeyFrame: + return sizeof(hsBezScalarKey); + case hsKeyFrame::kScaleKeyFrame: + return sizeof(hsScaleKey); + case hsKeyFrame::kBezScaleKeyFrame: + return sizeof(hsBezScaleKey); + case hsKeyFrame::kQuatKeyFrame: + return sizeof(hsQuatKey); + case hsKeyFrame::kCompressedQuatKeyFrame32: + return sizeof(hsCompressedQuatKey32); + case hsKeyFrame::kCompressedQuatKeyFrame64: + return sizeof(hsCompressedQuatKey64); + case hsKeyFrame::k3dsMaxKeyFrame: + return sizeof(hsG3DSMaxKeyFrame); + case hsKeyFrame::kMatrix33KeyFrame: + return sizeof(hsMatrix33Key); + case hsKeyFrame::kMatrix44KeyFrame: + return sizeof(hsMatrix44Key); + case hsKeyFrame::kUnknownKeyFrame: + default: + return 0; + } +} + +hsPoint3Key *plLeafController::GetPoint3Key(UInt32 i) const +{ + if (fType != hsKeyFrame::kPoint3KeyFrame) + return nil; + + return (hsPoint3Key *)((UInt8 *)fKeys + i * sizeof(hsPoint3Key)); +} + +hsBezPoint3Key *plLeafController::GetBezPoint3Key(UInt32 i) const +{ + if (fType != hsKeyFrame::kBezPoint3KeyFrame) + return nil; + + return (hsBezPoint3Key *)((UInt8 *)fKeys + i * sizeof(hsBezPoint3Key)); +} + +hsScalarKey *plLeafController::GetScalarKey(UInt32 i) const +{ + if (fType != hsKeyFrame::kScalarKeyFrame) + return nil; + + return (hsScalarKey *)((UInt8 *)fKeys + i * sizeof(hsScalarKey)); +} + +hsBezScalarKey *plLeafController::GetBezScalarKey(UInt32 i) const +{ + if (fType != hsKeyFrame::kBezScalarKeyFrame) + return nil; + + return (hsBezScalarKey *)((UInt8 *)fKeys + i * sizeof(hsBezScalarKey)); +} + +hsScaleKey *plLeafController::GetScaleKey(UInt32 i) const +{ + if (fType != hsKeyFrame::kScaleKeyFrame) + return nil; + + return (hsScaleKey *)((UInt8 *)fKeys + i * sizeof(hsScaleKey)); +} + +hsBezScaleKey *plLeafController::GetBezScaleKey(UInt32 i) const +{ + if (fType != hsKeyFrame::kBezScaleKeyFrame) + return nil; + + return (hsBezScaleKey *)((UInt8 *)fKeys + i * sizeof(hsBezScaleKey)); +} + +hsQuatKey *plLeafController::GetQuatKey(UInt32 i) const +{ + if (fType != hsKeyFrame::kQuatKeyFrame) + return nil; + + return (hsQuatKey *)((UInt8 *)fKeys + i * sizeof(hsQuatKey)); +} + +hsCompressedQuatKey32 *plLeafController::GetCompressedQuatKey32(UInt32 i) const +{ + if (fType != hsKeyFrame::kCompressedQuatKeyFrame32) + return nil; + + return (hsCompressedQuatKey32 *)((UInt8 *)fKeys + i * sizeof(hsCompressedQuatKey32)); +} + +hsCompressedQuatKey64 *plLeafController::GetCompressedQuatKey64(UInt32 i) const +{ + if (fType != hsKeyFrame::kCompressedQuatKeyFrame64) + return nil; + + return (hsCompressedQuatKey64 *)((UInt8 *)fKeys + i * sizeof(hsCompressedQuatKey64)); +} + +hsG3DSMaxKeyFrame *plLeafController::Get3DSMaxKey(UInt32 i) const +{ + if (fType != hsKeyFrame::k3dsMaxKeyFrame) + return nil; + + return (hsG3DSMaxKeyFrame *)((UInt8 *)fKeys + i * sizeof(hsG3DSMaxKeyFrame)); +} + +hsMatrix33Key *plLeafController::GetMatrix33Key(UInt32 i) const +{ + if (fType != hsKeyFrame::kMatrix33KeyFrame) + return nil; + + return (hsMatrix33Key *)((UInt8 *)fKeys + i * sizeof(hsMatrix33Key)); +} + +hsMatrix44Key *plLeafController::GetMatrix44Key(UInt32 i) const +{ + if (fType != hsKeyFrame::kMatrix44KeyFrame) + return nil; + + return (hsMatrix44Key *)((UInt8 *)fKeys + i * sizeof(hsMatrix44Key)); +} + +void plLeafController::GetKeyTimes(hsTArray &keyTimes) const +{ + int cIdx; + int kIdx; + UInt32 stride = GetStride(); + UInt8 *keyPtr = (UInt8 *)fKeys; + for (cIdx = 0, kIdx = 0; cIdx < fNumKeys, kIdx < keyTimes.GetCount();) + { + hsScalar kTime = keyTimes[kIdx]; + hsScalar cTime = ((hsKeyFrame*)(keyPtr + cIdx * stride))->fFrame / MAX_FRAMES_PER_SEC; + if (cTime < kTime) + { + keyTimes.InsertAtIndex(kIdx, cTime); + cIdx++; + kIdx++; + } + else if (cTime > kTime) + { + kIdx++; + } + else + { + kIdx++; + cIdx++; + } + } + + // All remaining times in the controller are later than the original keyTimes set + for (; cIdx < fNumKeys; cIdx++) + { + hsScalar cTime = ((hsKeyFrame*)(keyPtr + cIdx * stride))->fFrame / MAX_FRAMES_PER_SEC; + keyTimes.Append(cTime); + } +} + +void plLeafController::AllocKeys(UInt32 numKeys, UInt8 type) +{ + delete fKeys; + fNumKeys = numKeys; + fType = type; + + switch (fType) + { + case hsKeyFrame::kPoint3KeyFrame: + fKeys = TRACKED_NEW hsPoint3Key[fNumKeys]; + break; + + case hsKeyFrame::kBezPoint3KeyFrame: + fKeys = TRACKED_NEW hsBezPoint3Key[fNumKeys]; + break; + + case hsKeyFrame::kScalarKeyFrame: + fKeys = TRACKED_NEW hsScalarKey[fNumKeys]; + break; + + case hsKeyFrame::kBezScalarKeyFrame: + fKeys = TRACKED_NEW hsBezScalarKey[fNumKeys]; + break; + + case hsKeyFrame::kScaleKeyFrame: + fKeys = TRACKED_NEW hsScaleKey[fNumKeys]; + break; + + case hsKeyFrame::kBezScaleKeyFrame: + fKeys = TRACKED_NEW hsBezScaleKey[fNumKeys]; + break; + + case hsKeyFrame::kQuatKeyFrame: + fKeys = TRACKED_NEW hsQuatKey[fNumKeys]; + break; + + case hsKeyFrame::kCompressedQuatKeyFrame32: + fKeys = TRACKED_NEW hsCompressedQuatKey32[fNumKeys]; + break; + + case hsKeyFrame::kCompressedQuatKeyFrame64: + fKeys = TRACKED_NEW hsCompressedQuatKey64[fNumKeys]; + break; + + case hsKeyFrame::k3dsMaxKeyFrame: + fKeys = TRACKED_NEW hsG3DSMaxKeyFrame[fNumKeys]; + break; + + case hsKeyFrame::kMatrix33KeyFrame: + fKeys = TRACKED_NEW hsMatrix33Key[fNumKeys]; + break; + + case hsKeyFrame::kMatrix44KeyFrame: + fKeys = TRACKED_NEW hsMatrix44Key[fNumKeys]; + break; + + case hsKeyFrame::kUnknownKeyFrame: + default: + hsAssert(false, "Trying to allocate unknown keyframe type"); + break; + } +} + +void plLeafController::QuickScalarController(int numKeys, hsScalar* times, hsScalar* values, UInt32 valueStrides) +{ + AllocKeys(numKeys, hsKeyFrame::kScalarKeyFrame); + int i; + for( i = 0; i < numKeys; i++ ) + { + ((hsScalarKey*)fKeys)[i].fFrame = (UInt16)(*times++ * MAX_FRAMES_PER_SEC); + ((hsScalarKey*)fKeys)[i].fValue = *values; + values = (hsScalar *)((UInt8 *)values + valueStrides); + } +} + +// If all the keys are the same, this controller is pretty useless. +// This situation actually comes up a lot because of the biped killer +// trying to convert character studio animations. +hsBool plLeafController::AllKeysMatch() const +{ + if (fNumKeys <= 1) + return true; + + int idx; + for (idx = 1; idx < fNumKeys; idx++) + { + switch (fType) + { + case hsKeyFrame::kPoint3KeyFrame: + { + hsPoint3Key *k1 = GetPoint3Key(idx - 1); + hsPoint3Key *k2 = GetPoint3Key(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::kBezPoint3KeyFrame: + { + hsBezPoint3Key *k1 = GetBezPoint3Key(idx - 1); + hsBezPoint3Key *k2 = GetBezPoint3Key(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::kScalarKeyFrame: + { + hsScalarKey *k1 = GetScalarKey(idx - 1); + hsScalarKey *k2 = GetScalarKey(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::kBezScalarKeyFrame: + { + hsBezScalarKey *k1 = GetBezScalarKey(idx - 1); + hsBezScalarKey *k2 = GetBezScalarKey(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::kScaleKeyFrame: + { + hsScaleKey *k1 = GetScaleKey(idx - 1); + hsScaleKey *k2 = GetScaleKey(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::kBezScaleKeyFrame: + { + hsBezScaleKey *k1 = GetBezScaleKey(idx - 1); + hsBezScaleKey *k2 = GetBezScaleKey(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::kQuatKeyFrame: + { + hsQuatKey *k1 = GetQuatKey(idx - 1); + hsQuatKey *k2 = GetQuatKey(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::kCompressedQuatKeyFrame32: + { + hsCompressedQuatKey32 *k1 = GetCompressedQuatKey32(idx - 1); + hsCompressedQuatKey32 *k2 = GetCompressedQuatKey32(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::kCompressedQuatKeyFrame64: + { + hsCompressedQuatKey64 *k1 = GetCompressedQuatKey64(idx - 1); + hsCompressedQuatKey64 *k2 = GetCompressedQuatKey64(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::k3dsMaxKeyFrame: + { + hsG3DSMaxKeyFrame *k1 = Get3DSMaxKey(idx - 1); + hsG3DSMaxKeyFrame *k2 = Get3DSMaxKey(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::kMatrix33KeyFrame: + { + hsMatrix33Key *k1 = GetMatrix33Key(idx - 1); + hsMatrix33Key *k2 = GetMatrix33Key(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::kMatrix44KeyFrame: + { + hsMatrix44Key *k1 = GetMatrix44Key(idx - 1); + hsMatrix44Key *k2 = GetMatrix44Key(idx); + if (!k1->CompareValue(k2)) + return false; + break; + } + case hsKeyFrame::kUnknownKeyFrame: + default: + hsAssert(false, "Trying to compare unknown keyframe type"); + return false; + } + } + return true; +} + +hsBool plLeafController::PurgeRedundantSubcontrollers() +{ + return AllKeysMatch(); +} + +void plLeafController::Read(hsStream* s, hsResMgr *mgr) +{ + UInt8 type = s->ReadByte(); + UInt32 numKeys = s->ReadSwap32(); + AllocKeys(numKeys, type); + + int i; + switch (fType) + { + case hsKeyFrame::kPoint3KeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsPoint3Key *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::kBezPoint3KeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsBezPoint3Key *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::kScalarKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsScalarKey *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::kBezScalarKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsBezScalarKey *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::kScaleKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsScaleKey *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::kBezScaleKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsBezScaleKey *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::kQuatKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsQuatKey *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::kCompressedQuatKeyFrame32: + for (i = 0; i < fNumKeys; i++) + ((hsCompressedQuatKey32 *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::kCompressedQuatKeyFrame64: + for (i = 0; i < fNumKeys; i++) + ((hsCompressedQuatKey64 *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::k3dsMaxKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsG3DSMaxKeyFrame *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::kMatrix33KeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsMatrix33Key *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::kMatrix44KeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsMatrix44Key *)fKeys)[i].Read(s); + break; + + case hsKeyFrame::kUnknownKeyFrame: + default: + hsAssert(false, "Reading in controller with unknown key data"); + break; + } +} + +void plLeafController::Write(hsStream* s, hsResMgr *mgr) +{ + s->WriteByte(fType); + s->WriteSwap32(fNumKeys); + + int i; + switch (fType) + { + case hsKeyFrame::kPoint3KeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsPoint3Key *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::kBezPoint3KeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsBezPoint3Key *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::kScalarKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsScalarKey *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::kBezScalarKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsBezScalarKey *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::kScaleKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsScaleKey *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::kBezScaleKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsBezScaleKey *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::kQuatKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsQuatKey *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::kCompressedQuatKeyFrame32: + for (i = 0; i < fNumKeys; i++) + ((hsCompressedQuatKey32 *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::kCompressedQuatKeyFrame64: + for (i = 0; i < fNumKeys; i++) + ((hsCompressedQuatKey64 *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::k3dsMaxKeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsG3DSMaxKeyFrame *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::kMatrix33KeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsMatrix33Key *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::kMatrix44KeyFrame: + for (i = 0; i < fNumKeys; i++) + ((hsMatrix44Key *)fKeys)[i].Write(s); + break; + + case hsKeyFrame::kUnknownKeyFrame: + default: + hsAssert(false, "Writing controller with unknown key data"); + break; + } +} + +///////////////////////////////////////////////////////////////////////////////// + +plCompoundController::plCompoundController() : fXController(nil), fYController(nil), fZController(nil) {} + +plCompoundController::~plCompoundController() +{ + delete fXController; + delete fYController; + delete fZController; +} + +void plCompoundController::Interp(hsScalar time, hsScalarTriple* result, plControllerCacheInfo *cache) const +{ + if (fXController) + fXController->Interp(time, &result->fX, (cache ? cache->fSubControllers[0] : nil)); + if (fYController) + fYController->Interp(time, &result->fY, (cache ? cache->fSubControllers[1] : nil)); + if (fZController) + fZController->Interp(time, &result->fZ, (cache ? cache->fSubControllers[2] : nil)); +} + +void plCompoundController::Interp(hsScalar time, hsQuat* result, plControllerCacheInfo *cache) const +{ + hsEuler eul(0,0,0,EulOrdXYZs); + + fXController->Interp(time, &eul.fX, (cache ? cache->fSubControllers[0] : nil)); + fYController->Interp(time, &eul.fY, (cache ? cache->fSubControllers[1] : nil)); + fZController->Interp(time, &eul.fZ, (cache ? cache->fSubControllers[2] : nil)); + + eul.GetQuat(result); +} + +void plCompoundController::Interp(hsScalar time, hsAffineParts* parts, plControllerCacheInfo *cache) const +{ + if (fXController) + fXController->Interp(time, &parts->fT, (cache ? cache->fSubControllers[0] : nil)); + + if (fYController) + fYController->Interp(time, &parts->fQ, (cache ? cache->fSubControllers[1] : nil)); + + hsScaleValue sv; + if (fZController) + { + fZController->Interp(time, &sv, (cache ? cache->fSubControllers[2] : nil)); + parts->fU = sv.fQ; + parts->fK = sv.fS; + } +} + +void plCompoundController::Interp(hsScalar time, hsColorRGBA* result, plControllerCacheInfo *cache) const +{ + fXController->Interp(time, &result->r, (cache ? cache->fSubControllers[0] : nil)); + fYController->Interp(time, &result->g, (cache ? cache->fSubControllers[1] : nil)); + fZController->Interp(time, &result->b, (cache ? cache->fSubControllers[2] : nil)); +} + +hsScalar plCompoundController::GetLength() const +{ + hsScalar len=0; + int i; + for(i=0; i<3; i++) + { + if (GetController(i)) + len = hsMaximum(len, GetController(i)->GetLength()); + } + return len; +} + +void plCompoundController::GetKeyTimes(hsTArray &keyTimes) const +{ + if (fXController) + fXController->GetKeyTimes(keyTimes); + if (fYController) + fYController->GetKeyTimes(keyTimes); + if (fZController) + fZController->GetKeyTimes(keyTimes); +} + +hsBool plCompoundController::AllKeysMatch() const +{ + return (!fXController || fXController->AllKeysMatch()) && + (!fYController || fYController->AllKeysMatch()) && + (!fZController || fZController->AllKeysMatch()); +} + +// Careful here... We might detect that one of our subcontrollers +// has animation keys that all have the same value. That doesn't +// mean they're all zero though. An avatar animation might have +// elbow bend a constant 90 degrees through the entire anim, but +// if we delete the controller and assume zero, we'll have problems. +// Transform controller channels get around this by sampling the source +// first and using that to fill in the missing subcontrollers. +// +// Note: that one of our subcontrollers could itself be a compound +// controller. An example would be a controller for XYZ Euler angles +// that's a sub of the pos/rot/scale transform controller. +// It's possible that some of these sub-sub controllers could be +// removed, but then we'd have to store the default values somewhere. +// At the moment, this doesn't seem likely to save us enough space +// to be worth the effort. (This is why this function doesn't +// recursively call purge on its subcontrollers.) +hsBool plCompoundController::PurgeRedundantSubcontrollers() +{ + if (fXController && fXController->AllKeysMatch()) + { + delete fXController; + fXController = nil; + } + + if (fYController && fYController->AllKeysMatch()) + { + delete fYController; + fYController = nil; + } + + if (fZController && fZController->AllKeysMatch()) + { + delete fZController; + fZController = nil; + } + + return (!fXController && !fYController && !fZController); +} + +plControllerCacheInfo* plCompoundController::CreateCache() const +{ + plControllerCacheInfo* cache = TRACKED_NEW plControllerCacheInfo; + cache->fNumSubControllers = 3; + cache->fSubControllers = TRACKED_NEW plControllerCacheInfo*[cache->fNumSubControllers]; + int i; + for (i = 0; i < cache->fNumSubControllers; i++) + cache->fSubControllers[i] = (GetController(i) ? GetController(i)->CreateCache() : nil); + + return cache; +} + +plController* plCompoundController::GetController(Int32 i) const +{ + return (i==0 ? fXController : (i==1 ? fYController : fZController)); +} + +void plCompoundController::SetController(Int32 i, plController* c) +{ + delete GetController(i); + (i==0 ? fXController : (i==1 ? fYController : fZController)) = c; +} + +void plCompoundController::Read(hsStream* stream, hsResMgr *mgr) +{ + fXController = plController::ConvertNoRef(mgr->ReadCreatable(stream)); + fYController = plController::ConvertNoRef(mgr->ReadCreatable(stream)); + fZController = plController::ConvertNoRef(mgr->ReadCreatable(stream)); +} + +void plCompoundController::Write(hsStream* stream, hsResMgr *mgr) +{ + mgr->WriteCreatable(stream, fXController); + mgr->WriteCreatable(stream, fYController); + mgr->WriteCreatable(stream, fZController); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plController.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plController.h new file mode 100644 index 00000000..ed424225 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plController.h @@ -0,0 +1,214 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef HSCONTROLLER_inc +#define HSCONTROLLER_inc + +#include "HeadSpin.h" +#include "../pnFactory/plCreatable.h" +#include "hsColorRGBA.h" +#include "hsKeys.h" +#include "hsTemplates.h" + +class hsResMgr; + +struct hsScaleValue; +struct hsScalarKey; +struct hsPoint3Key; +struct hsScalarTriple; +struct hsMatrix33; +struct hsMatrix44; +class hsQuat; +class hsAffineParts; +class plScalarCurve; +class plAnimTimeConvert; +class plCompoundController; + +// +////////////////////////////////////////////////////////////// +// base controller class. +// Controllers correspond to Max controllers. +// Some are leaf controllers which actually have keys, these can also have +// multiple ease and multiplier controllers. +// Some are compound controllers, which just contain other (leaf) controllers. +// Leaf controllers have lists of keys (or plCurves which are just wrappers for +// the lists of keys). +// + +class plControllerCacheInfo +{ +public: + UInt8 fNumSubControllers; + plControllerCacheInfo **fSubControllers; + + UInt32 fKeyIndex; + plAnimTimeConvert *fAtc; + + plControllerCacheInfo(); + ~plControllerCacheInfo(); + + void SetATC(plAnimTimeConvert *atc); +}; + +// +////////////////////////////////////////////////////////////// +// defines base methods +// +class plController : public plCreatable +{ +public: + CLASSNAME_REGISTER( plController ); + GETINTERFACE_ANY( plController, plCreatable ); + + virtual void Interp(hsScalar time, hsScalar* result, plControllerCacheInfo *cache = nil) const {} + virtual void Interp(hsScalar time, hsScalarTriple* result, plControllerCacheInfo *cache = nil) const {} + virtual void Interp(hsScalar time, hsScaleValue* result, plControllerCacheInfo *cache = nil) const {} + virtual void Interp(hsScalar time, hsQuat* result, plControllerCacheInfo *cache = nil) const {} + virtual void Interp(hsScalar time, hsMatrix33* result, plControllerCacheInfo *cache = nil) const {} + virtual void Interp(hsScalar time, hsMatrix44* result, plControllerCacheInfo *cache = nil) const {} + virtual void Interp(hsScalar time, hsColorRGBA* result, plControllerCacheInfo *cache = nil) const {} + virtual void Interp(hsScalar time, hsAffineParts* parts, plControllerCacheInfo *cache = nil) const {} + + virtual plControllerCacheInfo* CreateCache() const { return nil; } // Caller must handle deleting the pointer. + virtual hsScalar GetLength() const = 0; + virtual void GetKeyTimes(hsTArray &keyTimes) const = 0; + virtual hsBool AllKeysMatch() const = 0; + + // Checks each of our subcontrollers (if we have any) and deletes any that + // are nothing but matching keys. Returns true if this controller itself + // is redundant. + virtual hsBool PurgeRedundantSubcontrollers() = 0; +}; + +////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////// + +class plLeafController : public plController +{ + friend class plCompoundController; + +protected: + UInt8 fType; + void *fKeys; // Need to pay attend to fType to determine what these actually are + UInt32 fNumKeys; + mutable UInt32 fLastKeyIdx; + +public: + plLeafController() : fType(hsKeyFrame::kUnknownKeyFrame), fKeys(nil), fNumKeys(0), fLastKeyIdx(0) {} + virtual ~plLeafController(); + + CLASSNAME_REGISTER( plLeafController ); + GETINTERFACE_ANY( plLeafController, plController ); + + void Interp(hsScalar time, hsScalar* result, plControllerCacheInfo *cache = nil) const; + void Interp(hsScalar time, hsScalarTriple* result, plControllerCacheInfo *cache = nil) const; + void Interp(hsScalar time, hsScaleValue* result, plControllerCacheInfo *cache = nil) const; + void Interp(hsScalar time, hsQuat* result, plControllerCacheInfo *cache = nil) const; + void Interp(hsScalar time, hsMatrix33* result, plControllerCacheInfo *cache = nil) const; + void Interp(hsScalar time, hsMatrix44* result, plControllerCacheInfo *cache = nil) const; + void Interp(hsScalar time, hsColorRGBA* result, plControllerCacheInfo *cache = nil) const; + + virtual plControllerCacheInfo* CreateCache() const; + hsScalar GetLength() const; + UInt32 GetStride() const; + + hsPoint3Key *GetPoint3Key(UInt32 i) const; + hsBezPoint3Key *GetBezPoint3Key(UInt32 i) const; + hsScalarKey *GetScalarKey(UInt32 i) const; + hsBezScalarKey *GetBezScalarKey(UInt32 i) const; + hsScaleKey *GetScaleKey(UInt32 i) const; + hsBezScaleKey *GetBezScaleKey(UInt32 i) const; + hsQuatKey *GetQuatKey(UInt32 i) const; + hsCompressedQuatKey32 *GetCompressedQuatKey32(UInt32 i) const; + hsCompressedQuatKey64 *GetCompressedQuatKey64(UInt32 i) const; + hsG3DSMaxKeyFrame *Get3DSMaxKey(UInt32 i) const; + hsMatrix33Key *GetMatrix33Key(UInt32 i) const; + hsMatrix44Key *GetMatrix44Key(UInt32 i) const; + + UInt8 GetType() const { return fType; } + UInt32 GetNumKeys() const { return fNumKeys; } + void *GetKeyBuffer() const { return fKeys; } + void GetKeyTimes(hsTArray &keyTimes) const; + void AllocKeys(UInt32 n, UInt8 type); + void QuickScalarController(int numKeys, hsScalar* times, hsScalar* values, UInt32 valueStrides); + hsBool AllKeysMatch() const; + hsBool PurgeRedundantSubcontrollers(); + + void Read(hsStream* s, hsResMgr* mgr); + void Write(hsStream* s, hsResMgr* mgr); +}; + + +//////////////////////////////////////////////////////////////////////////////// +// NON-LEAF (container) CONTROLLERS +//////////////////////////////////////////////////////////////////////////////// +// + +class plCompoundController : public plController +{ +private: + plController* fXController; + plController* fYController; + plController* fZController; + +public: + plCompoundController(); // allocs leaf controllers + ~plCompoundController(); + + CLASSNAME_REGISTER( plCompoundController ); + GETINTERFACE_ANY( plCompoundController, plController ); + + void Interp(hsScalar time, hsQuat* result, plControllerCacheInfo *cache = nil) const; + void Interp(hsScalar time, hsScalarTriple* result, plControllerCacheInfo *cache = nil) const; + void Interp(hsScalar time, hsAffineParts* parts, plControllerCacheInfo *cache = nil) const; + void Interp(hsScalar time, hsColorRGBA* result, plControllerCacheInfo *cache = nil) const; + + plControllerCacheInfo* CreateCache() const; + plController *GetXController() const { return fXController; } + plController *GetYController() const { return fYController; } + plController *GetZController() const { return fZController; } + plController *GetPosController() const { return fXController; } + plController *GetRotController() const { return fYController; } + plController *GetScaleController() const { return fZController; } + plController *GetController(Int32 i) const; + hsScalar GetLength() const; + void GetKeyTimes(hsTArray &keyTimes) const; + hsBool AllKeysMatch() const; + hsBool PurgeRedundantSubcontrollers(); + + void SetXController(plController *c) { delete fXController; fXController = c; } + void SetYController(plController *c) { delete fYController; fYController = c; } + void SetZController(plController *c) { delete fZController; fZController = c; } + void SetPosController(plController *c) { delete fXController; fXController = c; } + void SetRotController(plController *c) { delete fYController; fYController = c; } + void SetScaleController(plController *c) { delete fZController; fZController = c; } + void SetController(Int32 i, plController* c); + + void Read(hsStream* s, hsResMgr* mgr); + void Write(hsStream* s, hsResMgr* mgr); +}; + +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plInterpCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plInterpCreatable.h new file mode 100644 index 00000000..3bad98dc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plInterpCreatable.h @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plInterpCreatable_inc +#define plInterpCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plController.h" + +REGISTER_NONCREATABLE( plController ); +REGISTER_CREATABLE( plLeafController ); +REGISTER_CREATABLE( plCompoundController ); + +#include "plAnimTimeConvert.h" + +REGISTER_CREATABLE( plAnimTimeConvert ); +REGISTER_NONCREATABLE( plATCEaseCurve ); +REGISTER_CREATABLE( plConstAccelEaseCurve ); +REGISTER_CREATABLE( plSplineEaseCurve ); + +#include "plAnimPath.h" + +REGISTER_CREATABLE( plAnimPath ); + +#include "plModulator.h" + +REGISTER_CREATABLE( plModulator ); + +#endif // plInterpCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plModulator.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plModulator.cpp new file mode 100644 index 00000000..f101163e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plModulator.cpp @@ -0,0 +1,107 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plModulator.h" +#include "hsResMgr.h" +#include "hsStream.h" +#include "hsGeometry3.h" +#include "hsBounds.h" + +#include "plController.h" + +#include "../plIntersect/plVolumeIsect.h" + +plModulator::plModulator() +: fVolume(nil), + fSoftDist(0) +{ +} + +plModulator::~plModulator() +{ + delete fVolume; +} + +void plModulator::SetVolume(plVolumeIsect* vol) +{ + delete fVolume; + fVolume = vol; +} + +void plModulator::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + hsAssert(fVolume, "Modulator with no Volume is pretty useless"); + + fVolume->SetTransform(l2w, w2l); +} + +// Volume - Want to base this on the closest point on the bounds, instead of just the center. +hsScalar plModulator::Modulation(const hsBounds3Ext& bnd) const +{ + return Modulation(bnd.GetCenter()); +} + +hsScalar plModulator::Modulation(const hsPoint3& pos) const +{ + hsAssert(fVolume, "Modulator with no Volume is pretty useless"); + + hsScalar dist = fVolume->Test(pos); + + hsScalar retVal; + if( dist > 0 ) + { + if( dist < fSoftDist ) + { + dist /= fSoftDist; + retVal = 1.f - dist; + } + else + { + retVal = 0; + } + } + else + { + retVal = 1.f; + } + + return retVal; +} + +void plModulator::Read(hsStream* s, hsResMgr* mgr) +{ + fVolume = plVolumeIsect::ConvertNoRef(mgr->ReadCreatable(s)); + fSoftDist = s->ReadSwapScalar(); +} + +void plModulator::Write(hsStream* s, hsResMgr* mgr) +{ + mgr->WriteCreatable(s, fVolume); + s->WriteSwapScalar(fSoftDist); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plModulator.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plModulator.h new file mode 100644 index 00000000..cc9eba0f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plInterp/plModulator.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plModulator_inc +#define plModulator_inc + +#include "../pnFactory/plCreatable.h" + +struct hsMatrix44; +struct hsPoint3; +class plVolumeIsect; +class hsBounds3Ext; + +class plModulator : public plCreatable +{ +protected: + plVolumeIsect* fVolume; + hsScalar fSoftDist; + +public: + plModulator(); + virtual ~plModulator(); + + CLASSNAME_REGISTER( plModulator ); + GETINTERFACE_ANY( plModulator, plCreatable ); + + const plVolumeIsect* GetVolume() const { return fVolume; } + void SetVolume(plVolumeIsect* vol); // Takes ownership, so don't delete after handing it in. + + hsScalar Modulation(const hsPoint3& pos) const; + hsScalar Modulation(const hsBounds3Ext& bnd) const; + + void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + hsScalar GetSoftDist() const { return fSoftDist; } + void SetSoftDist(hsScalar s) { fSoftDist = s; } + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + +#endif // plModulator_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/notes.txt b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/notes.txt new file mode 100644 index 00000000..b0a68729 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/notes.txt @@ -0,0 +1,4 @@ + +Collection of geometry intersection routines between various primitives, e.g. TriList/Box, Box/Box, Box/Sphere, etc. + +Mesh types will often convert themselves to appropriate type, and call from this lib when queried for intersection. \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plClosest.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plClosest.cpp new file mode 100644 index 00000000..ad6ab638 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plClosest.cpp @@ -0,0 +1,685 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsGeometry3.h" +#include "plClosest.h" +#include "hsFastMath.h" + + +static const hsScalar kRealSmall = 1.e-5f; + +// Find the closest point on a line (or segment) to a point. +UInt32 plClosest::PointOnLine(const hsPoint3& p0, + const hsPoint3& p1, const hsVector3& v1, + hsPoint3& cp, + UInt32 clamp) +{ + hsScalar invV1Sq = v1.MagnitudeSquared(); + // v1 is also zero length. The two input points are the only options for output. + if( invV1Sq < kRealSmall ) + { + cp = p1; + return kClamp; + } + hsScalar t = v1.InnerProduct(p0 - p1) / invV1Sq; + cp = p1; + // clamp to the ends of segment v1. + if( (clamp & kClampLower1) && (t < 0) ) + { + return kClampLower1; + } + if( (clamp & kClampUpper1) && (t > 1.f) ) + { + cp += v1; + return kClampUpper1; + } + + cp += v1 * t; + return 0; +} + +// Find closest points to each other from two lines (or segments). +UInt32 plClosest::PointsOnLines(const hsPoint3& p0, const hsVector3& v0, + const hsPoint3& p1, const hsVector3& v1, + hsPoint3& cp0, hsPoint3& cp1, + UInt32 clamp) +{ + hsScalar invV0Sq = v0.MagnitudeSquared(); + // First handle degenerate cases. + // v0 is zero length. Resolves to finding closest point on p1+v1 to p0 + if( invV0Sq < kRealSmall ) + { + cp0 = p0; + return kClamp0 | PointOnLine(p0, p1, v1, cp1, clamp); + } + invV0Sq = 1.f / invV0Sq; + + // The real thing here, two non-zero length segments. (v1 can + // be zero length, it doesn't affect the math like |v0|=0 does, + // so we don't even bother to check. Only means maybe doing extra + // work, since we're using segment-segment math when all we really + // need is point-segment.) + + // The parameterized points for along each of the segments are + // P(t0) = p0 + v0*t0 + // P(t1) = p1 + v1*t1 + // + // The closest point on p0+v0 to P(t1) is: + // cp0 = p0 + ((P(t1) - p0) dot v0) * v0 / ||v0|| ||x|| is mag squared here + // cp0 = p0 + v0*t0 => t0 = ((P(t1) - p0) dot v0 ) / ||v0|| + // t0 = ((p1 + v1*t1 - p0) dot v0) / ||v0|| + // + // The distance squared from P(t1) to cp0 is: + // (cp0 - P(t1)) dot (cp0 - P(t1)) + // + // This expands out to: + // + // CV0 dot CV0 + 2 CV0 dot DV0 * t1 + (DV0 dot DV0) * t1^2 + // + // where + // + // CV0 = p0 - p1 + ((p1 - p0) dot v0) / ||v0||) * v0 == vector from p1 to closest point on p0+v0 + // and + // DV0 = ((v1 dot v0) / ||v0||) * v0 - v1 == ortho divergence vector of v1 from v0 negated. + // + // Taking the first derivative to find the local minimum of the function gives + // + // t1 = - (CV0 dot DV0) / (DV0 dot DV0) + // and + // t0 = ((p1 - v1 * t1 - p0) dot v0) / ||v0|| + // + // which seems kind of obvious in retrospect. + + hsVector3 p0subp1(&p0, &p1); + + hsVector3 CV0 = p0subp1; + CV0 += v0 * p0subp1.InnerProduct(v0) * -invV0Sq; + + hsVector3 DV0 = v0 * (v1.InnerProduct(v0) * invV0Sq) - v1; + + // Check for the vectors v0 and v1 being parallel, in which case + // following the lines won't get us to any closer point. + hsScalar DV0dotDV0 = DV0.InnerProduct(DV0); + if( DV0dotDV0 < kRealSmall ) + { + // If neither is clamped, return any two corresponding points. + // If one is clamped, return closest points in its clamp range. + // If both are clamped, well, both are clamped. The distance between + // points will no longer be the distance between lines. + // In any case, the distance between the points should be correct. + UInt32 clamp1 = PointOnLine(p0, p1, v1, cp1, clamp); + UInt32 clamp0 = PointOnLine(cp1, p0, v0, cp0, clamp >> 1); + return clamp1 | (clamp0 << 1); + } + + UInt32 retVal = 0; + + hsScalar t1 = - (CV0.InnerProduct(DV0)) / DV0dotDV0; + if( (clamp & kClampLower1) && (t1 <= 0) ) + { + t1 = 0; + retVal |= kClampLower1; + } + else if( (clamp & kClampUpper1) && (t1 >= 1.f) ) + { + t1 = 1.f; + retVal |= kClampUpper1; + } + + hsScalar t0 = v0.InnerProduct(p0subp1 - v1 * t1) * -invV0Sq; + cp0 = p0; + if( (clamp & kClampUpper0) && (t0 >= 1.f) ) + { + cp0 += v0; + retVal |= kClampUpper0; + } + else if( !(clamp & kClampLower0) || (t0 > 0) ) + { + cp0 += v0 * t0; + } + else + { + retVal |= kClampLower0; + } + + // If we clamped t0, we need to recalc t1 because the original + // calculation of t1 was based on an infinite p0+v0. + if( retVal & kClamp0 ) + { + t1 = v1.InnerProduct(cp0 - p1) / v1.MagnitudeSquared(); + retVal &= ~kClamp1; + if( (clamp & kClampLower1) && (t1 <= 0) ) + { + t1 = 0; + retVal |= kClampLower1; + } + else if( (clamp & kClampUpper1) && (t1 >= 1.f) ) + { + t1 = 1.f; + retVal |= kClampUpper1; + } + } + + cp1 = p1; + cp1 += v1 * t1; + + return retVal;; +} + +hsBool plClosest::PointOnSphere(const hsPoint3& p0, + const hsPoint3& center, hsScalar rad, + hsPoint3& cp) +{ + hsVector3 del(&p0, ¢er); + hsScalar dist = hsFastMath::InvSqrtAppr(del.MagnitudeSquared()); + dist *= rad; + del *= dist; + cp = center; + cp += del; + return dist <= 1.f; +} + +hsBool plClosest::PointOnBox(const hsPoint3& p0, + const hsPoint3& corner, + const hsVector3& axis0, + const hsVector3& axis1, + const hsVector3& axis2, + hsPoint3& cp) +{ + UInt32 clamps = 0; + hsPoint3 currPt = corner; + clamps |= PointOnLine(p0, currPt, axis0, cp, kClamp); + currPt = cp; + clamps |= PointOnLine(p0, currPt, axis1, cp, kClamp); + currPt = cp; + clamps |= PointOnLine(p0, currPt, axis2, cp, kClamp); + + return !clamps; +} + +hsBool plClosest::PointOnSphere(const hsPoint3& p0, const hsVector3& v0, + const hsPoint3& center, hsScalar rad, + hsPoint3& cp, + UInt32 clamp) +{ + // Does the line hit the sphere? If it does, we return the entry point in cp, + // otherwise we find the closest point on the sphere to the line. + /* + ((p0 + v0*t) - center)^2 = rad + v0*v0 * t*t + 2 * v0*t * (p0-c) + (p0-c)^2 - rad = 0 + + t = (-2 * v0*(p0-c) +- sqrt(4 * (v0*(p0-c))^2 - 4 * v0*v0 * ((p0-c)^2 - rad) / 2 * v0 * v0 + + t = (-v0*(p0-c) +- sqrt((v0*(p0-c))^2 - v0*v0 * ((p0-c)^2 - rad) / v0 * v0 + + So, line hits the sphere if + (v0*(p0-c))^2 > v0*v0 * ((p0-c)^2 - rad) + + If clamped, need additional checks on t before returning true + + If line doesn't hit the sphere, we find the closest point on the line + to the center of the sphere, and return the intersection of the segment + connecting that point and the center with the sphere. + */ + hsScalar termA = v0.InnerProduct(v0); + if( termA < kRealSmall ) + { + return PointOnSphere(p0, center, rad, cp); + } + hsVector3 p0Subc(&p0, ¢er); + hsScalar termB = v0.InnerProduct(p0Subc); + hsScalar termC = p0Subc.InnerProduct(p0Subc) - rad; + hsScalar disc = termB * termB - 4 * termA * termC; + if( disc >= 0 ) + { + disc = hsSquareRoot(disc); + hsScalar t = (-termB - disc) / (2.f * termA); + if( (t < 0) && (clamp & kClampLower0) ) + { + hsScalar tOut = (-termB + disc) / (2.f * termA); + if( tOut < 0 ) + { + // Both isects are before beginning of clamped line. + cp = p0; + cp += v0 * tOut; + return false; + } + if( (tOut > 1.f) && (clamp & kClampUpper0) ) + { + // The segment is entirely within the sphere. Take the closer end. + if( -t < tOut - 1.f ) + { + cp = p0; + cp += v0 * t; + } + else + { + cp = p0; + cp += v0 * tOut; + } + return true; + } + // We pierce the sphere from inside. + cp = p0; + cp += v0 * tOut; + return true; + } + cp = p0; + cp += v0 * t; + if( (t > 1.f) && (clamp & kClampUpper0) ) + { + return false; + } + return true; + } + + // Okay, missed the sphere, find closest point. + hsPoint3 lp; + PointOnLine(center, p0, v0, lp, clamp); + PointOnSphere(lp, center, rad, cp); + + return false; +} + +hsBool plClosest::PointOnBox(const hsPoint3& p0, const hsVector3& v0, + const hsPoint3& corner, + const hsVector3& axis0, + const hsVector3& axis1, + const hsVector3& axis2, + hsPoint3& cp, + UInt32 clamp) +{ + UInt32 clampRes = 0; + + hsPoint3 cp0, cp1; + hsPoint3 currPt = corner; + + clampRes |= PointsOnLines(p0, v0, currPt, axis0, cp0, cp1, clamp); + currPt = cp1; + + clampRes |= PointsOnLines(p0, v0, currPt, axis1, cp0, cp1, clamp); + currPt = cp1; + + clampRes |= PointsOnLines(p0, v0, currPt, axis2, cp0, cp1, clamp); + currPt = cp1; + + return !clampRes; +} + +hsBool plClosest::PointOnPlane(const hsPoint3& p0, + const hsPoint3& pPln, const hsVector3& n, + hsPoint3& cp) +{ + /* + p' = p - ((p-pPln)*n)/|n| * n/|n| + p' = p + ((pPln-p)*n) * n / |n|^2 + */ + hsScalar invNLen = hsFastMath::InvSqrt(n.MagnitudeSquared()); + + hsScalar nDotp = n.InnerProduct(pPln - p0); + cp = p0 + n * (nDotp * invNLen); + + return nDotp >= 0; +} + +hsBool plClosest::PointOnPlane(const hsPoint3& p0, const hsVector3& v0, + const hsPoint3& pPln, const hsVector3& n, + hsPoint3& cp, + UInt32 clamp) +{ + /* + p0 + v0*t is on plane, i.e. + (p0 + v0*t) * n = pPln * n + + p0 * n + v0 * n * t = pPln * n + v0 * n * t = (pPln - p0) * n + t = (pPln - p0) * n / (v0 * n) + + Then clamp appropriately, garnish, and serve with wild rice. + */ + hsBool retVal = true; + hsScalar pDotn = n.InnerProduct(pPln - p0); + hsScalar v0Dotn = n.InnerProduct(v0); + if( (v0Dotn < -kRealSmall) || (v0Dotn > kRealSmall) ) + { + hsScalar t = pDotn / v0Dotn; + + if( (clamp & kClampLower) && (t < 0) ) + { + t = 0; + retVal = false; + } + else if( (clamp & kClampUpper) && (t > 1.f) ) + { + t = 1.f; + retVal = false; + } + cp = p0; + cp += v0 * t; + + } + else + { + cp = p0 + v0 * 0.5f; + retVal = (pDotn > -kRealSmall) && (pDotn < kRealSmall); + } + + return retVal; +} + +hsBool plClosest::PointBetweenBoxes(const hsPoint3& aCorner, + const hsVector3& aAxis0, + const hsVector3& aAxis1, + const hsVector3& aAxis2, + const hsPoint3& bCorner, + const hsVector3& bAxis0, + const hsVector3& bAxis1, + const hsVector3& bAxis2, + hsPoint3& cp0, hsPoint3& cp1) +{ + const hsVector3* aAxes[3] = { &aAxis0, &aAxis1, &aAxis2 }; + const hsVector3* bAxes[3] = { &bAxis0, &bAxis1, &bAxis2 }; + + return PointBetweenBoxes(aCorner, aAxes, bCorner, bAxes, cp0, cp1); +} + +#if 0 // TRASH THIS +hsBool plClosest::PointBetweenBoxes(const hsPoint3& aCorner, + const hsVector3* aAxes[3], + const hsPoint3& bCorner, + const hsVector3* bAxes[3], + hsPoint3& cp0, hsPoint3& cp1) +{ + hsPoint3 aCurrPt = aCorner; + hsPoint3 bCurrPt = bCorner; + + hsPoint3 bStartPt[3]; + bStartPt[0] = bStartPt[1] = bStartPt[2] = bCorner; + + hsBool retVal = true; + int i, j; + for( i = 0; i < 3; i++ ) + { + hsPoint3 aBestPt; + hsPoint3 bBestPt; + + hsScalar minDistSq = 1.e33f; + for( j = 0; j < 3; j++ ) + { + hsPoint3 aNextPt, bNextPt; + PointsOnLines(aCurrPt, *aAxes[i], + bStartPt[j], *bAxes[j], + aNextPt, bNextPt, + plClosest::kClamp); + + hsScalar distSq = hsVector3(&aNextPt, &bNextPt).MagnitudeSquared(); + if( distSq < minDistSq ) + { + aBestPt = aNextPt; + bBestPt = bNextPt; + + if( distSq < kRealSmall ) + retVal = true; + + minDistSq = distSq; + } + hsVector3 bMove(&bNextPt, &bStartPt[j]); + int k; + for( k = 0; k < 3; k++ ) + { + if( k != j ) + bStartPt[k] += bMove; + } + } + aCurrPt = aBestPt; + bCurrPt = bBestPt; + } + cp0 = aCurrPt; + cp1 = bCurrPt; + + return retVal; +} +#elif 0 // TRASH THIS + +hsBool plClosest::PointBetweenBoxes(const hsPoint3& aCorner, + const hsVector3* aAxes[3], + const hsPoint3& bCorner, + const hsVector3* bAxes[3], + hsPoint3& cp0, hsPoint3& cp1) +{ + /* + Six combinations to try to go through every possible + combination of axes from A and B + + 00 00 01 01 02 02 + 11 12 12 10 10 11 + 22 21 20 22 21 20 + */ + + int bIdx0 = 0; + int bIdx1 = 1; + int bIdx2 = 2; + + hsPoint3 aBestPt, bBestPt; + hsScalar minDistSq = 1.e33f; + + hsBool retVal = false; + + int i; + for( i = 0; i < 6; i++ ) + { + hsPoint3 aCurrPt = aCorner; + hsPoint3 bCurrPt = bCorner; + + hsPoint3 aNextPt, bNextPt; + PointsOnLines(aCurrPt, *aAxes[0], + bCurrPt, *bAxes[bIdx0], + aNextPt, bNextPt, + plClosest::kClamp); + + aCurrPt = aNextPt; + bCurrPt = bNextPt; + + PointsOnLines(aCurrPt, *aAxes[1], + bCurrPt, *bAxes[bIdx1], + aNextPt, bNextPt, + plClosest::kClamp); + + aCurrPt = aNextPt; + bCurrPt = bNextPt; + + PointsOnLines(aCurrPt, *aAxes[2], + bCurrPt, *bAxes[bIdx2], + aNextPt, bNextPt, + plClosest::kClamp); + + + hsScalar distSq = hsVector3(&aNextPt, &bNextPt).MagnitudeSquared(); + if( distSq < minDistSq ) + { + aBestPt = aNextPt; + bBestPt = bNextPt; + + if( distSq < kRealSmall ) + retVal = true; + + minDistSq = distSq; + } + + if( i & 0x1 ) + { + bIdx0++; + bIdx1 = bIdx0 < 2 ? bIdx0+1 : 0; + bIdx2 = bIdx1 < 2 ? bIdx1+1 : 0; + } + else + { + int t = bIdx1; + bIdx1 = bIdx2; + bIdx2 = t; + } + } + cp0 = aBestPt; + cp1 = bBestPt; + + return retVal; +} + +#else // TRASH THIS + +hsBool plClosest::PointBetweenBoxes(const hsPoint3& aCorner, + const hsVector3* aAxes[3], + const hsPoint3& bCorner, + const hsVector3* bAxes[3], + hsPoint3& cp0, hsPoint3& cp1) +{ + /* + Six combinations to try to go through every possible + combination of axes from A and B + + 00 00 01 01 02 02 + 11 12 12 10 10 11 + 22 21 20 22 21 20 + */ + + struct trial { + int aIdx[3]; + int bIdx[3]; + } trials[36]; + + + int tNext = 0; + int k,l; + for( k = 0; k < 3; k++ ) + { + for( l = 0; l < 3; l++ ) + { + int kPlus = k < 2 ? k+1 : 0; + int kPlusPlus = kPlus < 2 ? kPlus+1 : 0; + + int lPlus = l < 2 ? l+1 : 0; + int lPlusPlus = lPlus < 2 ? lPlus+1 : 0; + + trials[tNext].aIdx[0] = k; + trials[tNext].bIdx[0] = l; + + trials[tNext].aIdx[1] = kPlus; + trials[tNext].bIdx[1] = lPlus; + + trials[tNext].aIdx[2] = kPlusPlus; + trials[tNext].bIdx[2] = lPlusPlus; + + tNext++; + + trials[tNext].aIdx[0] = k; + trials[tNext].bIdx[0] = l; + + trials[tNext].aIdx[1] = kPlusPlus; + trials[tNext].bIdx[1] = lPlusPlus; + + trials[tNext].aIdx[2] = kPlus; + trials[tNext].bIdx[2] = lPlus; + + tNext++; + + trials[tNext].aIdx[0] = k; + trials[tNext].bIdx[0] = l; + + trials[tNext].aIdx[1] = kPlus; + trials[tNext].bIdx[1] = lPlusPlus; + + trials[tNext].aIdx[2] = kPlusPlus; + trials[tNext].bIdx[2] = lPlus; + + tNext++; + + trials[tNext].aIdx[0] = k; + trials[tNext].bIdx[0] = l; + + trials[tNext].aIdx[1] = kPlusPlus; + trials[tNext].bIdx[1] = lPlus; + + trials[tNext].aIdx[2] = kPlus; + trials[tNext].bIdx[2] = lPlusPlus; + + tNext++; + } + } + + hsPoint3 aBestPt, bBestPt; + hsScalar minDistSq = 1.e33f; + + hsBool retVal = false; + + int i; + for( i = 0; i < 36; i++ ) + { + hsPoint3 aCurrPt = aCorner; + hsPoint3 bCurrPt = bCorner; + + hsPoint3 aNextPt, bNextPt; + PointsOnLines(aCurrPt, *aAxes[trials[i].aIdx[0]], + bCurrPt, *bAxes[trials[i].bIdx[0]], + aNextPt, bNextPt, + plClosest::kClamp); + + aCurrPt = aNextPt; + bCurrPt = bNextPt; + + PointsOnLines(aCurrPt, *aAxes[trials[i].aIdx[1]], + bCurrPt, *bAxes[trials[i].bIdx[1]], + aNextPt, bNextPt, + plClosest::kClamp); + + aCurrPt = aNextPt; + bCurrPt = bNextPt; + + PointsOnLines(aCurrPt, *aAxes[trials[i].aIdx[2]], + bCurrPt, *bAxes[trials[i].bIdx[2]], + aNextPt, bNextPt, + plClosest::kClamp); + + + hsScalar distSq = hsVector3(&aNextPt, &bNextPt).MagnitudeSquared(); + if( distSq < minDistSq ) + { + aBestPt = aNextPt; + bBestPt = bNextPt; + + if( distSq < kRealSmall ) + retVal = true; + + minDistSq = distSq; + } + + } + cp0 = aBestPt; + cp1 = bBestPt; + + return retVal; +} +#endif // TRASH THIS \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plClosest.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plClosest.h new file mode 100644 index 00000000..a1f4bbeb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plClosest.h @@ -0,0 +1,122 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plIsect_inc +#define plIsect_inc + +struct hsPoint3; +struct hsVector3; + + +class plClosest +{ +public: +enum plClosestClampFlags +{ + kClampLower0 = 0x1, + kClampUpper0 = 0x2, + kClamp0 = kClampLower0 | kClampUpper0, + kClampLower1 = 0x4, + kClampLower = kClampLower0 | kClampLower1, + kClampUpper1 = 0x8, + kClampUpper = kClampUpper0 | kClampUpper1, + kClamp1 = kClampLower1 | kClampUpper1, + kClamp = kClamp0 | kClamp1 +}; + + + // Return clamp flags for where clamped + static UInt32 PointOnLine(const hsPoint3& p0, // Point + const hsPoint3& p1, const hsVector3& v1, // Line + hsPoint3& cp, // Output closest point on line to p0 + UInt32 clamp); // Clamp on ends of segment (use Lower1/Upper1) + + // Return clamp flags for where clamped + static UInt32 PointsOnLines(const hsPoint3& p0, const hsVector3& v0,// First line + const hsPoint3& p1, const hsVector3& v1, // Second line + hsPoint3& cp0, // Output closest on line0 to line1 + hsPoint3& cp1, // Output closest on line1 to line0 + UInt32 clamp); // Clamp on ends + + // Return true if p0 is inside or on sphere. + static hsBool PointOnSphere(const hsPoint3& p0, // Point + const hsPoint3& center, hsScalar rad, // Sphere + hsPoint3& cp); // Output closest on sphere to p0 + + // Return true if p0 is inside box. + static hsBool PointOnBox(const hsPoint3& p0, // Point + const hsPoint3& corner, // Box defined by corner point and 3 (presumably but + const hsVector3& axis0, // not required) ortho axes. + const hsVector3& axis1, + const hsVector3& axis2, + hsPoint3& cp); + + // Return true if line intersects or is inside sphere. + static hsBool PointOnSphere(const hsPoint3& p0, const hsVector3& v0, // Line + const hsPoint3& center, hsScalar rad, // Sphere + hsPoint3& cp, // Output closest on sphere to p0, or entry point if line hits sphere + UInt32 clamp); + + // Return true if line intersects or is inside box. + static hsBool PointOnBox(const hsPoint3& p0, const hsVector3& v0, // Line + const hsPoint3& corner, // Box defined by corner point and 3 (presumably but + const hsVector3& axis0, // not required) ortho axes. + const hsVector3& axis1, + const hsVector3& axis2, + hsPoint3& cp, + UInt32 clamp); + + // Return true if point inside (negative side) of plane + static hsBool PointOnPlane(const hsPoint3& p0, + const hsPoint3& pPln, const hsVector3& n, + hsPoint3& cp); + + // Return true if line passes through plane. + static hsBool PointOnPlane(const hsPoint3& p0, const hsVector3& v0, + const hsPoint3& pPln, const hsVector3& n, + hsPoint3& cp, + UInt32 clamp); + + // Two identical functions, just different wrapping. First version repacks + // parameters and calls second. + static hsBool PointBetweenBoxes(const hsPoint3& aCorner, + const hsVector3& aAxis0, + const hsVector3& aAxis1, + const hsVector3& aAxis2, + const hsPoint3& bCorner, + const hsVector3& bAxis0, + const hsVector3& bAxis1, + const hsVector3& bAxis2, + hsPoint3& cp0, hsPoint3& cp1); + + static hsBool PointBetweenBoxes(const hsPoint3& aCorner, + const hsVector3* aAxes[3], + const hsPoint3& bCorner, + const hsVector3* bAxes[3], + hsPoint3& cp0, hsPoint3& cp1); +}; + +#endif // plVolume_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegion.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegion.cpp new file mode 100644 index 00000000..1fcef617 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegion.cpp @@ -0,0 +1,96 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsStream.h" + +#include "plHardRegion.h" + +#include "plgDispatch.h" +#include "../plMessage/plRenderMsg.h" +#include "plPipeline.h" + +plHardRegion::plHardRegion() +: fState(kDirty) +{ +} + +plHardRegion::~plHardRegion() +{ +} + +void plHardRegion::SetKey(plKey k) +{ + plRegionBase::SetKey(k); + +#if 0 // The caching of this probably isn't worth it. + // We'll try evaluation every time first, and try + // this later as an optimization. + if( k ) + { + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + } +#endif // Caching +} + +void plHardRegion::Read(hsStream* s, hsResMgr* mgr) +{ + plRegionBase::Read(s, mgr); + + fState = kDirty; +} + +void plHardRegion::Write(hsStream* s, hsResMgr* mgr) +{ + plRegionBase::Write(s, mgr); +} + +hsBool plHardRegion::CameraInside() const +{ + if( fState & kDirty ) + { + if( ICameraInside() ) + fState |= kCamInside; + else + fState &= ~kCamInside; + fState &= ~kDirty; + } + return 0 != (fState & kCamInside); +} + +hsBool plHardRegion::MsgReceive(plMessage* msg) +{ + plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg); + if( rend ) + { + fState |= kDirty; + fCamPos = rend->Pipeline()->GetViewPositionWorld(); + return true; + } + + return plRegionBase::MsgReceive(msg); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegion.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegion.h new file mode 100644 index 00000000..cf7f4106 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegion.h @@ -0,0 +1,80 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plHardRegion_inc +#define plHardRegion_inc + +#include "plRegionBase.h" +#include "hsGeometry3.h" + +class hsStream; +class hsResMgr; +class plMessage; + +class plHardRegion : public plRegionBase +{ +public: + enum RefTypes { + kSubRegion + }; + +protected: + enum + { + kDirty, + kCamInside + }; + + mutable UInt32 fState; + hsPoint3 fCamPos; + + virtual void SetKey(plKey k); + +public: + plHardRegion(); + virtual ~plHardRegion(); + + CLASSNAME_REGISTER( plHardRegion ); + GETINTERFACE_ANY( plHardRegion, plRegionBase ); + + virtual hsBool IsInside(const hsPoint3& pos) const { return IIsInside(pos); } + virtual hsBool CameraInside() const; + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) = 0; + + virtual Int32 GetNumProperties() const { return 1; } // This is stupid. + + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool IIsInside(const hsPoint3& pos) const = 0; + virtual hsBool ICameraInside() const = 0; +}; + +#endif // plHardRegion_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionPlanes.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionPlanes.cpp new file mode 100644 index 00000000..8362c984 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionPlanes.cpp @@ -0,0 +1,152 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plHardRegionPlanes.h" + +#include "hsStream.h" +#include "hsGeometry3.h" +#include "hsFastMath.h" +#include "hsMatrix44.h" + + +plHardRegionPlanes::plHardRegionPlanes() +{ +} + +plHardRegionPlanes::~plHardRegionPlanes() +{ +} + +hsBool plHardRegionPlanes::IIsInside(const hsPoint3& pos) const +{ + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + if( fPlanes[i].fWorldNorm.InnerProduct(pos) > fPlanes[i].fWorldDist ) + return false; + } + return true; +} + +hsBool plHardRegionPlanes::ICameraInside() const +{ + return IIsInside(fCamPos); +} + +void plHardRegionPlanes::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + fPlanes[i].fWorldPos = l2w * fPlanes[i].fPos; + + // Normal gets transpose of inverse. + fPlanes[i].fWorldNorm.fX = w2l.fMap[0][0] * fPlanes[i].fNorm.fX + + w2l.fMap[1][0] * fPlanes[i].fNorm.fY + + w2l.fMap[2][0] * fPlanes[i].fNorm.fZ; + + fPlanes[i].fWorldNorm.fY = w2l.fMap[0][1] * fPlanes[i].fNorm.fX + + w2l.fMap[1][1] * fPlanes[i].fNorm.fY + + w2l.fMap[2][1] * fPlanes[i].fNorm.fZ; + + fPlanes[i].fWorldNorm.fZ = w2l.fMap[0][2] * fPlanes[i].fNorm.fX + + w2l.fMap[1][2] * fPlanes[i].fNorm.fY + + w2l.fMap[2][2] * fPlanes[i].fNorm.fZ; + + hsFastMath::NormalizeAppr(fPlanes[i].fWorldNorm); + + fPlanes[i].fWorldDist = fPlanes[i].fWorldNorm.InnerProduct(fPlanes[i].fWorldPos); + } +} + +void plHardRegionPlanes::Read(hsStream* s, hsResMgr* mgr) +{ + plHardRegion::Read(s, mgr); + + int n = s->ReadSwap32(); + fPlanes.SetCount(n); + + int i; + for( i = 0; i < n; i++ ) + { + fPlanes[i].fNorm.Read(s); + fPlanes[i].fPos.Read(s); + + fPlanes[i].fWorldNorm.Read(s); + fPlanes[i].fWorldPos.Read(s); + + fPlanes[i].fWorldDist = fPlanes[i].fWorldNorm.InnerProduct(fPlanes[i].fWorldPos); + } +} + +void plHardRegionPlanes::Write(hsStream* s, hsResMgr* mgr) +{ + plHardRegion::Write(s, mgr); + + s->WriteSwap32(fPlanes.GetCount()); + + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + fPlanes[i].fNorm.Write(s); + fPlanes[i].fPos.Write(s); + + fPlanes[i].fWorldNorm.Write(s); + fPlanes[i].fWorldPos.Write(s); + } +} + +void plHardRegionPlanes::AddPlane(const hsVector3& n, const hsPoint3& p) +{ + hsVector3 nNorm = n; + hsFastMath::Normalize(nNorm); + + // First, make sure some idiot isn't adding the same plane in twice. + // Also, look for the degenerate case of two parallel planes. In that + // case, take the outer. + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + const hsScalar kCloseToOne = 1.f - 1.e-4f; + if( fPlanes[i].fNorm.InnerProduct(nNorm) >= kCloseToOne ) + { + hsScalar newDist = nNorm.InnerProduct(p); + hsScalar oldDist = fPlanes[i].fNorm.InnerProduct(fPlanes[i].fPos); + if( newDist > oldDist ) + { + fPlanes[i].fPos = p; + } + return; + } + } + HardPlane plane; + plane.fWorldNorm = plane.fNorm = nNorm; + plane.fWorldPos = plane.fPos = p; + plane.fWorldDist = plane.fWorldNorm.InnerProduct(plane.fWorldPos); + + fPlanes.Append(plane); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionPlanes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionPlanes.h new file mode 100644 index 00000000..eab104cb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionPlanes.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plHardRegionPlanes_inc +#define plHardRegionPlanes_inc + +#include "plHardRegion.h" + +class plHardRegionPlanes : public plHardRegion +{ +protected: + class HardPlane + { + public: + hsVector3 fNorm; + hsPoint3 fPos; + + hsVector3 fWorldNorm; + hsPoint3 fWorldPos; + hsScalar fWorldDist; + }; + hsTArray fPlanes; + +protected: +public: + plHardRegionPlanes(); + virtual ~plHardRegionPlanes(); + + CLASSNAME_REGISTER( plHardRegionPlanes ); + GETINTERFACE_ANY( plHardRegionPlanes, plHardRegion ); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + // Now Planes specifics + void AddPlane(const hsVector3& n, const hsPoint3& p); + UInt32 GetNumPlanes() const { return fPlanes.GetCount(); } + void GetPlane(int i, hsVector3& n, hsPoint3& p) const { n = fPlanes[i].fNorm; p = fPlanes[i].fPos; } + void GetWorldPlane(int i, hsVector3& n, hsPoint3& p) const { n = fPlanes[i].fWorldNorm; p = fPlanes[i].fWorldPos; } + + virtual hsBool IIsInside(const hsPoint3& pos) const; + virtual hsBool ICameraInside() const; + +}; + + + +#endif // plHardRegionPlanes_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionTypes.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionTypes.cpp new file mode 100644 index 00000000..02896d99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionTypes.cpp @@ -0,0 +1,196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" + +#include "plHardRegionTypes.h" +#include "hsStream.h" +#include "hsResMgr.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// Base hard and complex +//////////////////////////////////////////////////////////////////////////////////////// +plHardRegionComplex::plHardRegionComplex() +{ +} + +plHardRegionComplex::~plHardRegionComplex() +{ +} + +void plHardRegionComplex::Read(hsStream* s, hsResMgr* mgr) +{ + plHardRegion::Read(s, mgr); + + int n = s->ReadSwap32(); + int i; + for( i = 0; i < n; i++ ) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kSubRegion), plRefFlags::kActiveRef); +} + +void plHardRegionComplex::Write(hsStream* s, hsResMgr* mgr) +{ + plHardRegion::Write(s, mgr); + + s->WriteSwap32(fSubRegions.GetCount()); + int i; + for( i = 0; i < fSubRegions.GetCount(); i++ ) + mgr->WriteKey(s, fSubRegions[i]); +} + +hsBool plHardRegionComplex::MsgReceive(plMessage* msg) +{ + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest) ) + { + plHardRegion* sub = plHardRegion::ConvertNoRef(refMsg->GetRef()); + hsAssert(fSubRegions.kMissingIndex == fSubRegions.Find(sub), "Adding subRegion I already have"); + fSubRegions.Append(sub); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + plHardRegion* sub = (plHardRegion*)refMsg->GetRef(); + int idx = fSubRegions.Find(sub); + if( idx != fSubRegions.kMissingIndex ) + fSubRegions.Remove(idx); + } + return true; + } + return plHardRegion::MsgReceive(msg); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Booleans follow +//////////////////////////////////////////////////////////////////////////////////////// + +// Union +//////////////////////////////////////////////////////////////////////////////////////// + +plHardRegionUnion::plHardRegionUnion() +{ +} + +plHardRegionUnion::~plHardRegionUnion() +{ +} + +hsBool plHardRegionUnion::IIsInside(const hsPoint3& pos) const +{ + int i; + for( i = 0; i < fSubRegions.GetCount(); i++ ) + { + if( fSubRegions[i]->IIsInside(pos) ) + return true; + } + return false; +} + +hsBool plHardRegionUnion::ICameraInside() const +{ + if( fState & kDirty ) + { + fState &= ~(kCamInside | kDirty); + int i; + for( i = 0; i < fSubRegions.GetCount(); i++ ) + { + if( fSubRegions[i]->ICameraInside() ) + { + fState |= kCamInside; + return true; + } + } + } + return false; +} + +// Intersection +//////////////////////////////////////////////////////////////////////////////////////// +plHardRegionIntersect::plHardRegionIntersect() +{ +} + +plHardRegionIntersect::~plHardRegionIntersect() +{ +} + +hsBool plHardRegionIntersect::IIsInside(const hsPoint3& pos) const +{ + int i; + for( i = 0; i < fSubRegions.GetCount(); i++ ) + { + if( !fSubRegions[i]->IIsInside(pos) ) + return false; + } + return true; +} + +hsBool plHardRegionIntersect::ICameraInside() const +{ + if( fState & kDirty ) + { + fState &= ~kDirty; + fState |= kCamInside; + int i; + for( i = 0; i < fSubRegions.GetCount(); i++ ) + { + if( !fSubRegions[i]->ICameraInside() ) + { + fState &= ~kCamInside; + return false; + } + } + } + return true; +} + + +// Invert +//////////////////////////////////////////////////////////////////////////////////////// + +plHardRegionInvert::plHardRegionInvert() +{ +} + +plHardRegionInvert::~plHardRegionInvert() +{ +} + +hsBool plHardRegionInvert::IIsInside(const hsPoint3& pos) const +{ + hsAssert(fSubRegions.GetCount() <= 1, "Too many subRegions on inverter"); + return !fSubRegions[0]->IIsInside(pos); +} + +hsBool plHardRegionInvert::ICameraInside() const +{ + hsAssert(fSubRegions.GetCount() <= 1, "Too many subRegions on inverter"); + return !fSubRegions[0]->ICameraInside(); +} + + /////////////////////////////////////////////////////////////////////////////// diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionTypes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionTypes.h new file mode 100644 index 00000000..cd2c2149 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plHardRegionTypes.h @@ -0,0 +1,104 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plHardRegionTypes_inc +#define plHardRegionTypes_inc + +#include "plHardRegion.h" +#include "hsTemplates.h" + +class plHardRegionComplex : public plHardRegion +{ +protected: + hsTArray fSubRegions; + +public: + plHardRegionComplex(); + virtual ~plHardRegionComplex(); + + CLASSNAME_REGISTER( plHardRegionComplex ); + GETINTERFACE_ANY( plHardRegionComplex, plHardRegion ); + + // Don't propagate the settransform to our children, they move independently + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) {} + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + // Now Complex specifics + virtual hsBool MsgReceive(plMessage* msg); + + UInt16 GetNumSubs() const { return fSubRegions.GetCount(); } + const plHardRegion* GetSub(int i) const { return fSubRegions[i]; } +}; + +class plHardRegionUnion : public plHardRegionComplex +{ +protected: +public: + plHardRegionUnion(); + virtual ~plHardRegionUnion(); + + CLASSNAME_REGISTER( plHardRegionUnion ); + GETINTERFACE_ANY( plHardRegionUnion, plHardRegionComplex ); + + virtual hsBool IIsInside(const hsPoint3& pos) const; + virtual hsBool ICameraInside() const; + +}; + +class plHardRegionIntersect : public plHardRegionComplex +{ +protected: +public: + plHardRegionIntersect(); + virtual ~plHardRegionIntersect(); + + CLASSNAME_REGISTER( plHardRegionIntersect ); + GETINTERFACE_ANY( plHardRegionIntersect, plHardRegionComplex ); + + virtual hsBool IIsInside(const hsPoint3& pos) const; + virtual hsBool ICameraInside() const; + +}; + +class plHardRegionInvert : public plHardRegionComplex +{ +protected: +public: + plHardRegionInvert(); + virtual ~plHardRegionInvert(); + + CLASSNAME_REGISTER( plHardRegionInvert ); + GETINTERFACE_ANY( plHardRegionInvert, plHardRegionComplex ); + + virtual hsBool IIsInside(const hsPoint3& pos) const; + virtual hsBool ICameraInside() const; + +}; + + +#endif // plHardRegionTypes_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plIntersectCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plIntersectCreatable.h new file mode 100644 index 00000000..60589484 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plIntersectCreatable.h @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plIntersectCreatable_inc +#define plIntersectCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plVolumeIsect.h" + +REGISTER_NONCREATABLE( plVolumeIsect ); + +REGISTER_CREATABLE( plSphereIsect ); +REGISTER_CREATABLE( plConeIsect ); +REGISTER_CREATABLE( plCylinderIsect ); +REGISTER_CREATABLE( plParallelIsect ); +REGISTER_CREATABLE( plConvexIsect ); + +REGISTER_CREATABLE( plBoundsIsect ); + +REGISTER_NONCREATABLE( plComplexIsect ); + +REGISTER_CREATABLE( plUnionIsect ); +REGISTER_CREATABLE( plIntersectionIsect ); + +#include "plRegionBase.h" + +REGISTER_NONCREATABLE( plRegionBase ); + +#include "plSoftVolume.h" + +REGISTER_NONCREATABLE( plSoftVolume ); + +#include "plSoftVolumeTypes.h" + +REGISTER_CREATABLE( plSoftVolumeSimple ); + +REGISTER_NONCREATABLE( plSoftVolumeComplex ); + +REGISTER_CREATABLE( plSoftVolumeUnion ); +REGISTER_CREATABLE( plSoftVolumeIntersect ); +REGISTER_CREATABLE( plSoftVolumeInvert ); + +#include "plHardRegion.h" + +REGISTER_NONCREATABLE( plHardRegion ); + +#include "plHardRegionPlanes.h" + +REGISTER_CREATABLE( plHardRegionPlanes ); + +#include "plHardRegionTypes.h" + +REGISTER_NONCREATABLE( plHardRegionComplex ); + +REGISTER_CREATABLE( plHardRegionUnion ); +REGISTER_CREATABLE( plHardRegionIntersect ); +REGISTER_CREATABLE( plHardRegionInvert ); + +#endif // plIntersectCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plRegionBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plRegionBase.h new file mode 100644 index 00000000..b8f27ee0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plRegionBase.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRegionBase_inc +#define plRegionBase_inc + +#include "../pnSceneObject/plObjInterface.h" + +struct hsPoint3; + +class plRegionBase : public plObjInterface +{ +public: + plRegionBase() {} + virtual ~plRegionBase() {} + + CLASSNAME_REGISTER( plRegionBase ); + GETINTERFACE_ANY( plRegionBase, plObjInterface ); + + virtual hsBool IsInside(const hsPoint3& pos) const = 0; +}; + +#endif // plRegionBase_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolume.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolume.cpp new file mode 100644 index 00000000..e47f693a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolume.cpp @@ -0,0 +1,143 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsStream.h" + +#include "plSoftVolume.h" + +#include "plgDispatch.h" +#include "../plMessage/plListenerMsg.h" + +plSoftVolume::plSoftVolume() +: fListenState(0), + fListenStrength(0), + fInsideStrength(1.f), + fOutsideStrength(0) +{ + fListenPos.Set(0,0,0); +} + +plSoftVolume::~plSoftVolume() +{ +} + +void plSoftVolume::Read(hsStream* s, hsResMgr* mgr) +{ + plRegionBase::Read(s, mgr); + + fListenState = s->ReadSwap32(); + + SetCheckListener(0 != (fListenState & kListenCheck)); + + fInsideStrength = s->ReadSwapScalar(); + fOutsideStrength = s->ReadSwapScalar(); +} + +void plSoftVolume::Write(hsStream* s, hsResMgr* mgr) +{ + plRegionBase::Write(s, mgr); + + s->WriteSwap32(fListenState); + + s->WriteSwapScalar(fInsideStrength); + s->WriteSwapScalar(fOutsideStrength); +} + +hsScalar plSoftVolume::GetStrength(const hsPoint3& pos) const +{ + return IRemapStrength(IGetStrength(pos)); +} + +hsScalar plSoftVolume::GetListenerStrength() const +{ + if( !(fListenState & kListenPosSet) ) + { + // Some screw-up, haven't received a pos yet. Turn it off till we do. + return fListenStrength = IRemapStrength(0); + } + if( fListenState & kListenDirty ) + { + fListenStrength = IUpdateListenerStrength(); + fListenState &= ~kListenDirty; + } + return fListenStrength; +} + +void plSoftVolume::UpdateListenerPosition(const hsPoint3& pos) +{ + fListenPos = pos; + fListenState |= kListenDirty | kListenPosSet; +} + +void plSoftVolume::SetCheckListener(hsBool on) +{ + if( on ) + { + plgDispatch::Dispatch()->RegisterForExactType(plListenerMsg::Index(), GetKey()); + fListenState |= kListenCheck | kListenDirty | kListenRegistered; + } + else + { + plgDispatch::Dispatch()->UnRegisterForExactType(plListenerMsg::Index(), GetKey()); + fListenState &= ~(kListenCheck | kListenDirty | kListenRegistered); + } +} + +hsBool plSoftVolume::MsgReceive(plMessage* msg) +{ + plListenerMsg* list = plListenerMsg::ConvertNoRef(msg); + if( list ) + { + UpdateListenerPosition(list->GetPosition()); + return true; + } + + return plRegionBase::MsgReceive(msg); +} + +hsScalar plSoftVolume::IUpdateListenerStrength() const +{ + return fListenStrength = GetStrength(fListenPos); +} + +void plSoftVolume::SetInsideStrength(hsScalar s) +{ + if( s < 0 ) + s = 0; + else if( s > 1.f ) + s = 1.f; + fInsideStrength = s; +} + +void plSoftVolume::SetOutsideStrength(hsScalar s) +{ + if( s < 0 ) + s = 0; + else if( s > 1.f ) + s = 1.f; + fOutsideStrength = s; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolume.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolume.h new file mode 100644 index 00000000..9a162097 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolume.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSoftVolume_inc +#define plSoftVolume_inc + +#include "plRegionBase.h" +#include "hsGeometry3.h" + +class hsStream; +class hsResMgr; +class plMessage; + +class plSoftVolume : public plRegionBase +{ +public: + enum RefTypes { + kSubVolume + }; + +protected: + enum { + kListenNone = 0x0, + kListenCheck = 0x1, + kListenPosSet = 0x2, + kListenDirty = 0x4, + kListenRegistered = 0x8 + }; + + hsPoint3 fListenPos; + mutable hsScalar fListenStrength; + mutable UInt32 fListenState; + + hsScalar fInsideStrength; + hsScalar fOutsideStrength; + + virtual hsScalar IUpdateListenerStrength() const; + + hsScalar IRemapStrength(hsScalar s) const { return fOutsideStrength + s * (fInsideStrength - fOutsideStrength); } + +private: + // Don't call this, use public GetStrength(). + virtual hsScalar IGetStrength(const hsPoint3& pos) const = 0; + +public: + plSoftVolume(); + virtual ~plSoftVolume(); + + CLASSNAME_REGISTER( plSoftVolume ); + GETINTERFACE_ANY( plSoftVolume, plRegionBase ); + + virtual hsScalar GetStrength(const hsPoint3& pos) const; + virtual hsBool IsInside(const hsPoint3& pos) const { return GetStrength(pos) >= 1.f; } + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) = 0; + + virtual Int32 GetNumProperties() const { return 1; } // This is stupid. + + virtual hsScalar GetListenerStrength() const; + virtual void UpdateListenerPosition(const hsPoint3& p); + virtual void SetCheckListener(hsBool on=true); + virtual hsBool GetCheckListener() const { return 0 != (fListenState & kListenCheck); } + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void SetInsideStrength(hsScalar s); + void SetOutsideStrength(hsScalar s); + + hsScalar GetInsideStrength() const { return fInsideStrength; } + hsScalar GetOutsideStrength() const { return fOutsideStrength; } +}; + +#endif // plSoftVolume_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolumeTypes.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolumeTypes.cpp new file mode 100644 index 00000000..79ac670b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolumeTypes.cpp @@ -0,0 +1,279 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsMatrix44.h" +#include "hsGeometry3.h" +#include "hsResMgr.h" +#include "hsStream.h" + +#include "plVolumeIsect.h" +#include "plSoftVolumeTypes.h" + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +plSoftVolumeSimple::plSoftVolumeSimple() +: fVolume(nil), + fSoftDist(0) +{ +} + +plSoftVolumeSimple::~plSoftVolumeSimple() +{ + delete fVolume; +} + +hsScalar plSoftVolumeSimple::IGetStrength(const hsPoint3& pos) const +{ + if( !fVolume || GetProperty(kDisable) ) + return 0; + + hsScalar dist = fVolume->Test(pos); + + if( dist <= 0 ) + return 1.f; + + if( dist >= fSoftDist ) + return 0; + + dist /= fSoftDist; + + return 1.f - dist; +} + +void plSoftVolumeSimple::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + if( fVolume ) + fVolume->SetTransform(l2w, w2l); +} + +void plSoftVolumeSimple::Read(hsStream* s, hsResMgr* mgr) +{ + plSoftVolume::Read(s, mgr); + + fSoftDist = s->ReadSwapScalar(); + + fVolume = plVolumeIsect::ConvertNoRef(mgr->ReadCreatable(s)); +} + +void plSoftVolumeSimple::Write(hsStream* s, hsResMgr* mgr) +{ + plSoftVolume::Write(s, mgr); + + s->WriteSwapScalar(fSoftDist); + + mgr->WriteCreatable(s, fVolume); +} + +void plSoftVolumeSimple::SetVolume(plVolumeIsect* v) +{ + delete fVolume; + fVolume = v; +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +plSoftVolumeComplex::plSoftVolumeComplex() +{ +} + +plSoftVolumeComplex::~plSoftVolumeComplex() +{ +} + +void plSoftVolumeComplex::Read(hsStream* s, hsResMgr* mgr) +{ + plSoftVolume::Read(s, mgr); + + int n = s->ReadSwap32(); + int i; + for( i = 0; i < n; i++ ) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kSubVolume), plRefFlags::kActiveRef); +} + +void plSoftVolumeComplex::Write(hsStream* s, hsResMgr* mgr) +{ + plSoftVolume::Write(s, mgr); + + s->WriteSwap32(fSubVolumes.GetCount()); + int i; + for( i = 0; i < fSubVolumes.GetCount(); i++ ) + mgr->WriteKey(s, fSubVolumes[i]); +} + +hsBool plSoftVolumeComplex::MsgReceive(plMessage* msg) +{ + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest) ) + { + plSoftVolume* sub = plSoftVolume::ConvertNoRef(refMsg->GetRef()); + hsAssert(fSubVolumes.kMissingIndex == fSubVolumes.Find(sub), "Adding subvolume I already have"); + fSubVolumes.Append(sub); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + plSoftVolume* sub = (plSoftVolume*)refMsg->GetRef(); + int idx = fSubVolumes.Find(sub); + if( idx != fSubVolumes.kMissingIndex ) + fSubVolumes.Remove(idx); + } + return true; + } + return plSoftVolume::MsgReceive(msg); +} + +void plSoftVolumeComplex::UpdateListenerPosition(const hsPoint3& pos) +{ + plSoftVolume::UpdateListenerPosition(pos); + int i; + for( i = 0; i < fSubVolumes.GetCount(); i++ ) + fSubVolumes[i]->UpdateListenerPosition(pos); +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +plSoftVolumeUnion::plSoftVolumeUnion() +{ +} + +plSoftVolumeUnion::~plSoftVolumeUnion() +{ +} + +hsScalar plSoftVolumeUnion::IGetStrength(const hsPoint3& pos) const +{ + hsScalar retVal = 0; + int i; + for( i = 0; i < fSubVolumes.GetCount(); i++ ) + { + hsScalar subRet = fSubVolumes[i]->GetStrength(pos); + if( subRet >= 1.f ) + return 1.f; + if( subRet > retVal ) + retVal = subRet; + } + return retVal; +} + +hsScalar plSoftVolumeUnion::IUpdateListenerStrength() const +{ + hsScalar retVal = 0; + int i; + for( i = 0; i < fSubVolumes.GetCount(); i++ ) + { + hsScalar subRet = fSubVolumes[i]->GetListenerStrength(); + if( subRet >= 1.f ) + { + retVal = 1.f; + break; + } + if( subRet > retVal ) + retVal = subRet; + } + return fListenStrength = IRemapStrength(retVal); +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +plSoftVolumeIntersect::plSoftVolumeIntersect() +{ +} + +plSoftVolumeIntersect::~plSoftVolumeIntersect() +{ +} + +hsScalar plSoftVolumeIntersect::IGetStrength(const hsPoint3& pos) const +{ + hsScalar retVal = 1.f; + int i; + for( i = 0; i < fSubVolumes.GetCount(); i++ ) + { + hsScalar subRet = fSubVolumes[i]->GetStrength(pos); + if( subRet <= 0 ) + return 0; + if( subRet < retVal ) + retVal = subRet; + } + return retVal; +} + +hsScalar plSoftVolumeIntersect::IUpdateListenerStrength() const +{ + hsScalar retVal = 1.f; + int i; + for( i = 0; i < fSubVolumes.GetCount(); i++ ) + { + hsScalar subRet = fSubVolumes[i]->GetListenerStrength(); + if( subRet <= 0 ) + { + retVal = 0.f; + break; + } + if( subRet < retVal ) + retVal = subRet; + } + return fListenStrength = IRemapStrength(retVal); +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +plSoftVolumeInvert::plSoftVolumeInvert() +{ +} + +plSoftVolumeInvert::~plSoftVolumeInvert() +{ +} + +hsScalar plSoftVolumeInvert::IGetStrength(const hsPoint3& pos) const +{ + hsAssert(fSubVolumes.GetCount() <= 1, "Too many subvolumes on inverter"); + if( fSubVolumes.GetCount() ) + return 1.f - fSubVolumes[0]->GetStrength(pos); + + return 1.f; +} + +hsScalar plSoftVolumeInvert::IUpdateListenerStrength() const +{ + hsAssert(fSubVolumes.GetCount() <= 1, "Too many subvolumes on inverter"); + hsScalar retVal = 1.f; + if( fSubVolumes.GetCount() ) + retVal = (1.f - fSubVolumes[0]->GetListenerStrength()); + + return fListenStrength = IRemapStrength(retVal); +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolumeTypes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolumeTypes.h new file mode 100644 index 00000000..47fe10f2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plSoftVolumeTypes.h @@ -0,0 +1,144 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSoftVolumeTypes_inc +#define plSoftVolumeTypes_inc + +#include "plSoftVolume.h" +#include "hsTemplates.h" + +class plVolumeIsect; + +class plSoftVolumeSimple : public plSoftVolume +{ +protected: + plVolumeIsect* fVolume; + hsScalar fSoftDist; + +private: + virtual hsScalar IGetStrength(const hsPoint3& pos) const; + +public: + plSoftVolumeSimple(); + virtual ~plSoftVolumeSimple(); + + CLASSNAME_REGISTER( plSoftVolumeSimple ); + GETINTERFACE_ANY( plSoftVolumeSimple, plSoftVolume ); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + // Now Simple specifics + plVolumeIsect* GetVolume() const { return fVolume; } + void SetVolume(plVolumeIsect* v); // Takes ownership, don't delete after giving to SoftVolume + + hsScalar GetDistance() const { return fSoftDist; } + void SetDistance(hsScalar d) { fSoftDist = d; } + +}; + +class plSoftVolumeComplex : public plSoftVolume +{ +protected: + hsTArray fSubVolumes; + +public: + plSoftVolumeComplex(); + virtual ~plSoftVolumeComplex(); + + CLASSNAME_REGISTER( plSoftVolumeComplex ); + GETINTERFACE_ANY( plSoftVolumeComplex, plSoftVolume ); + + // Don't propagate the settransform to our children, they move independently + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) {} + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual void UpdateListenerPosition(const hsPoint3& p); + + // Now Complex specifics + virtual hsBool MsgReceive(plMessage* msg); + + UInt16 GetNumSubs() const { return fSubVolumes.GetCount(); } + const plSoftVolume* GetSub(int i) const { return fSubVolumes[i]; } +}; + +class plSoftVolumeUnion : public plSoftVolumeComplex +{ +protected: + virtual hsScalar IUpdateListenerStrength() const; + +private: + virtual hsScalar IGetStrength(const hsPoint3& pos) const; + +public: + plSoftVolumeUnion(); + virtual ~plSoftVolumeUnion(); + + CLASSNAME_REGISTER( plSoftVolumeUnion ); + GETINTERFACE_ANY( plSoftVolumeUnion, plSoftVolumeComplex ); + +}; + +class plSoftVolumeIntersect : public plSoftVolumeComplex +{ +protected: + virtual hsScalar IUpdateListenerStrength() const; + +private: + virtual hsScalar IGetStrength(const hsPoint3& pos) const; + +public: + plSoftVolumeIntersect(); + virtual ~plSoftVolumeIntersect(); + + CLASSNAME_REGISTER( plSoftVolumeIntersect ); + GETINTERFACE_ANY( plSoftVolumeIntersect, plSoftVolumeComplex ); + +}; + +class plSoftVolumeInvert : public plSoftVolumeComplex +{ +protected: + virtual hsScalar IUpdateListenerStrength() const; + +private: + virtual hsScalar IGetStrength(const hsPoint3& pos) const; + +public: + plSoftVolumeInvert(); + virtual ~plSoftVolumeInvert(); + + CLASSNAME_REGISTER( plSoftVolumeInvert ); + GETINTERFACE_ANY( plSoftVolumeInvert, plSoftVolumeComplex ); + + +}; + +#endif // plSoftVolumeTypes_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plVolumeIsect.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plVolumeIsect.cpp new file mode 100644 index 00000000..1e1a959c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plVolumeIsect.cpp @@ -0,0 +1,989 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plVolumeIsect.h" +#include "hsBounds.h" +#include "hsFastMath.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "../plIntersect/plClosest.h" + +static const hsScalar kDefLength = 5.f; + +plSphereIsect::plSphereIsect() +: fRadius(1.f) +{ + fCenter.Set(0,0,0); + int i; + for( i = 0; i < 3; i++ ) + { + fMins[i] = -fRadius; + fMaxs[i] = fRadius; + } +} + +plSphereIsect::~plSphereIsect() +{ +} + +void plSphereIsect::SetCenter(const hsPoint3& c) +{ + fWorldCenter = fCenter = c; + int i; + for( i = 0; i < 3; i++ ) + { + fMins[i] += c[i]; + fMaxs[i] += c[i]; + } +} + +void plSphereIsect::SetRadius(hsScalar r) +{ + hsScalar del = r - fRadius; + int i; + for( i = 0; i < 3; i++ ) + { + fMins[i] -= del; + fMaxs[i] += del; + } + fRadius = r; +} + +void plSphereIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + fWorldCenter = l2w * fCenter; + fMaxs = fMins = fWorldCenter; + int i; + for( i = 0; i < 3; i++ ) + { + fMins[i] -= fRadius; + fMaxs[i] += fRadius; + } +} + +// Could use ClosestPoint to find the closest point on the bounds +// to our center, and do a distance test on that. Would be more +// accurate than this box test approx, but whatever. +plVolumeCullResult plSphereIsect::Test(const hsBounds3Ext& bnd) const +{ + const hsPoint3& maxs = bnd.GetMaxs(); + const hsPoint3& mins = bnd.GetMins(); + + if( (maxs.fX < fMins.fX) + || + (maxs.fY < fMins.fY) + || + (maxs.fZ < fMins.fZ) ) + return kVolumeCulled; + + if( (mins.fX > fMaxs.fX) + || + (mins.fY > fMaxs.fY) + || + (mins.fZ > fMaxs.fZ) ) + return kVolumeCulled; + + if( (maxs.fX > fMaxs.fX) + || + (maxs.fY > fMaxs.fY) + || + (maxs.fZ > fMaxs.fZ) ) + return kVolumeSplit; + + if( (mins.fX < fMins.fX) + || + (mins.fY < fMins.fY) + || + (mins.fZ < fMins.fZ) ) + return kVolumeSplit; + + return kVolumeClear; +} + +hsScalar plSphereIsect::Test(const hsPoint3& pos) const +{ + hsScalar dist = (pos - fWorldCenter).MagnitudeSquared(); + if( dist < fRadius*fRadius ) + return 0; + dist = hsSquareRoot(dist); + return dist - fRadius; +} + +void plSphereIsect::Read(hsStream* s, hsResMgr* mgr) +{ + fCenter.Read(s); + fWorldCenter.Read(s); + fRadius = s->ReadSwapScalar(); + fMins.Read(s); + fMaxs.Read(s); +} + +void plSphereIsect::Write(hsStream* s, hsResMgr* mgr) +{ + fCenter.Write(s); + fWorldCenter.Write(s); + s->WriteSwapScalar(fRadius); + fMins.Write(s); + fMaxs.Write(s); +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +plConeIsect::plConeIsect() +: fLength(kDefLength), fRadAngle(hsScalarPI*0.25f), fCapped(false) +{ + ISetup(); +} + +plConeIsect::~plConeIsect() +{ +} + +void plConeIsect::SetAngle(hsScalar rads) +{ + fRadAngle = rads; + ISetup(); +} + +void plConeIsect::ISetup() +{ + hsScalar sinAng, cosAng; + hsFastMath::SinCosInRangeAppr(fRadAngle, sinAng, cosAng); + + const hsScalar kHither = 0.1f; + fLightToNDC.Reset(); + fLightToNDC.fMap[0][0] = hsScalarDiv( cosAng, sinAng ); + fLightToNDC.fMap[1][1] = hsScalarDiv( cosAng, sinAng ); + fLightToNDC.fMap[2][2] = -hsScalarDiv( fLength, fLength - kHither ); + fLightToNDC.fMap[3][3] = hsIntToScalar( 0 ); + fLightToNDC.fMap[3][2] = hsIntToScalar( -1 ); + fLightToNDC.fMap[2][3] = -hsScalarMulDiv( fLength, kHither, fLength - kHither ); + fLightToNDC.NotIdentity(); +} + +plVolumeCullResult plConeIsect::Test(const hsBounds3Ext& bnd) const +{ + plVolumeCullResult retVal = kVolumeClear; + + hsPoint2 depth; + hsVector3 normDir = -fWorldNorm; + bnd.TestPlane(normDir, depth); + if( depth.fY < normDir.InnerProduct(fWorldTip) ) + return kVolumeCulled; + + int last = fCapped ? 5 : 4; + int i; + for( i = 0; i < last; i++ ) + { + bnd.TestPlane(fNorms[i], depth); + if( depth.fY + fDists[i] <= 0 ) + return kVolumeCulled; + if( depth.fX + fDists[i] <= 0 ) + retVal = kVolumeSplit; + } + if( retVal == kVolumeSplit ) + { + hsVector3 axis = normDir % hsVector3(&bnd.GetCenter(), &fWorldTip); + hsFastMath::NormalizeAppr(axis); + + hsVector3 perp = axis % normDir; + + hsScalar sinAng, cosAng; + hsFastMath::SinCosInRangeAppr(fRadAngle, sinAng, cosAng); + + hsVector3 tangent = normDir + sinAng * perp + (1-cosAng) * (axis % perp); + + hsVector3 normIn = tangent % axis; + + hsVector3 normIn2 = perp + sinAng * (perp % axis) + (1-cosAng) * (axis % (axis % perp)); + + bnd.TestPlane(normIn, depth); + hsScalar normInDotTip = normIn.InnerProduct(fWorldTip); + if( depth.fY < normInDotTip ) + return kVolumeCulled; + } + + return retVal; +} + +hsScalar plConeIsect::Test(const hsPoint3& pos) const +{ + UInt32 clampFlags = fCapped ? plClosest::kClamp : plClosest::kClampLower; + hsPoint3 cp; + + plClosest::PointOnLine(pos, + fWorldTip, fWorldNorm, + cp, + clampFlags); + + hsScalar radDist = (pos - cp).Magnitude(); + hsScalar axDist = fWorldNorm.InnerProduct(pos - fWorldTip) / fLength; + if( axDist < 0 ) + { + return radDist; + } + hsScalar sinAng, cosAng; + + hsFastMath::SinCosInRangeAppr(fRadAngle, sinAng, cosAng); + + hsScalar radius = axDist * sinAng / cosAng; + + radDist -= radius; + axDist -= fLength; + + if( fCapped && (axDist > 0) ) + { + return axDist > radDist ? axDist : radDist; + } + + return radDist > 0 ? radDist : 0; +} + +//#define MF_DEBUG_NORM +#ifdef MF_DEBUG_NORM +#define IDEBUG_NORMALIZE( a, b ) { hsScalar len = 1.f / a.Magnitude(); a *= len; b *= len; } +#else // MF_DEBUG_NORM +#define IDEBUG_NORMALIZE( a, b ) +#endif // MF_DEBUG_NORM + +void plConeIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + fWorldTip = l2w.GetTranslate(); + fWorldNorm.Set(l2w.fMap[0][2], l2w.fMap[1][2], l2w.fMap[2][2]); + + fWorldToNDC = fLightToNDC * w2l; + int i; + for( i = 0; i < 2; i++ ) + { + fNorms[i].Set(fWorldToNDC.fMap[3][0] - fWorldToNDC.fMap[i][0], fWorldToNDC.fMap[3][1] - fWorldToNDC.fMap[i][1], fWorldToNDC.fMap[3][2] - fWorldToNDC.fMap[i][2]); + fDists[i] = fWorldToNDC.fMap[3][3] - fWorldToNDC.fMap[i][3]; + + IDEBUG_NORMALIZE( fNorms[i], fDists[i] ); + + fNorms[i+2].Set(fWorldToNDC.fMap[3][0] + fWorldToNDC.fMap[i][0], fWorldToNDC.fMap[3][1] + fWorldToNDC.fMap[i][1], fWorldToNDC.fMap[3][2] + fWorldToNDC.fMap[i][2]); + fDists[i+2] = fWorldToNDC.fMap[3][3] + fWorldToNDC.fMap[i][3]; + + IDEBUG_NORMALIZE( fNorms[i+2], fDists[i+2] ); + } + + if( fCapped ) + { + fNorms[4].Set(fWorldToNDC.fMap[3][0] - fWorldToNDC.fMap[2][0], fWorldToNDC.fMap[3][1] - fWorldToNDC.fMap[2][1], fWorldToNDC.fMap[3][2] - fWorldToNDC.fMap[2][2]); + fDists[4] = fWorldToNDC.fMap[3][3] - fWorldToNDC.fMap[2][3]; + + IDEBUG_NORMALIZE( fNorms[4], fDists[4] ); + } +} + +void plConeIsect::SetLength(hsScalar d) +{ + if( d > 0 ) + { + fCapped = true; + fLength = d; + } + else + { + fCapped = false; + fLength = kDefLength; + } + ISetup(); +} + +void plConeIsect::Read(hsStream* s, hsResMgr* mgr) +{ + fCapped = s->ReadSwap32(); + + fRadAngle = s->ReadSwapScalar(); + fLength = s->ReadSwapScalar(); + + fWorldTip.Read(s); + fWorldNorm.Read(s); + + fWorldToNDC.Read(s); + fLightToNDC.Read(s); + + int n = fCapped ? 5 : 4; + int i; + for(i = 0; i < n; i++ ) + { + fNorms[i].Read(s); + fDists[i] = s->ReadSwapScalar(); + } +} + +void plConeIsect::Write(hsStream* s, hsResMgr* mgr) +{ + s->WriteSwap32(fCapped); + + s->WriteSwapScalar(fRadAngle); + s->WriteSwapScalar(fLength); + + fWorldTip.Write(s); + fWorldNorm.Write(s); + + fWorldToNDC.Write(s); + fLightToNDC.Write(s); + + int n = fCapped ? 5 : 4; + int i; + for(i = 0; i < n; i++ ) + { + fNorms[i].Write(s); + s->WriteSwapScalar(fDists[i]); + } +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +plCylinderIsect::plCylinderIsect() +{ +} + +plCylinderIsect::~plCylinderIsect() +{ +} + +void plCylinderIsect::ISetupCyl(const hsPoint3& wTop, const hsPoint3& wBot, hsScalar radius) +{ + fWorldNorm.Set(&wTop, &wBot); + fLength = fWorldNorm.Magnitude(); + fMin = fWorldNorm.InnerProduct(wBot); + fMax = fWorldNorm.InnerProduct(wTop); + if( fMin > fMax ) + { + hsScalar t = fMin; + fMin = fMax; + fMax = t; + } + fRadius = radius; +} + +void plCylinderIsect::SetCylinder(const hsPoint3& lTop, const hsPoint3& lBot, hsScalar radius) +{ + fTop = lTop; + fBot = lBot; + fRadius = radius; + + ISetupCyl(fTop, fBot, fRadius); +} + +void plCylinderIsect::SetCylinder(const hsPoint3& lBot, const hsVector3& axis, hsScalar radius) +{ + fBot = lBot; + fTop = fBot; + fTop += axis; + + ISetupCyl(fTop, fBot, radius); + +} + +void plCylinderIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + hsPoint3 wTop = l2w * fTop; + hsPoint3 wBot = l2w * fBot; + + ISetupCyl(wTop, wBot, fRadius); +} + +plVolumeCullResult plCylinderIsect::Test(const hsBounds3Ext& bnd) const +{ + plVolumeCullResult radVal = kVolumeClear; + + // Central axis test + hsPoint2 depth; + bnd.TestPlane(fWorldNorm, depth); + if( depth.fX > fMax ) + return kVolumeCulled; + if( depth.fY < fMin ) + return kVolumeCulled; + + if( (depth.fX < fMin) + ||(depth.fY > fMax) ) + { + radVal = kVolumeSplit; + } + + // Radial test + plVolumeCullResult retVal = kVolumeCulled; + + // Find the closest point on/in the bounds to our central axis. + // If that closest point is inside the cylinder, we have a hit. + hsPoint3 corner; + bnd.GetCorner(&corner); + hsVector3 axes[3]; + bnd.GetAxes(axes+0, axes+1, axes+2); + hsPoint3 cp = corner; + + hsScalar bndRadiusSq = bnd.GetRadius(); + bndRadiusSq *= bndRadiusSq; + + hsScalar radiusSq = fRadius*fRadius; + + hsScalar maxClearDistSq = fRadius - bnd.GetRadius(); + maxClearDistSq *= maxClearDistSq; + + int i; + for( i = 0; i < 3; i++ ) + { + hsPoint3 cp0; + hsPoint3 currPt; + plClosest::PointsOnLines(fWorldBot, fWorldNorm, + cp, axes[i], + cp0, currPt, + plClosest::kClamp); + hsScalar distSq = (cp0 - currPt).MagnitudeSquared(); + if( distSq < radiusSq ) + { + if( distSq < maxClearDistSq ) + { + return kVolumeClear == radVal ? kVolumeClear : kVolumeSplit; + } + retVal = kVolumeSplit; + } + cp = currPt; + } + + return retVal; +} + +hsScalar plCylinderIsect::Test(const hsPoint3& pos) const +{ + hsPoint3 cp; + + plClosest::PointOnLine(pos, + fWorldBot, fWorldNorm, + cp, + plClosest::kClamp); + + hsScalar radDist = (pos - cp).Magnitude() - fRadius; + hsScalar axDist = fWorldNorm.InnerProduct(pos - fWorldBot) / fLength; + + if( axDist < 0 ) + axDist = -axDist; + else + axDist -= fLength; + + hsScalar dist = axDist > radDist ? axDist : radDist; + + return dist > 0 ? dist : 0; +} + +void plCylinderIsect::Read(hsStream* s, hsResMgr* mgr) +{ + fTop.Read(s); + fBot.Read(s); + fRadius = s->ReadSwapScalar(); + + fWorldBot.Read(s); + fWorldNorm.Read(s); + fLength = s->ReadSwapScalar(); + fMin = s->ReadSwapScalar(); + fMax = s->ReadSwapScalar(); +} + +void plCylinderIsect::Write(hsStream* s, hsResMgr* mgr) +{ + fTop.Write(s); + fBot.Write(s); + s->WriteSwapScalar(fRadius); + + fWorldBot.Write(s); + fWorldNorm.Write(s); + s->WriteSwapScalar(fLength); + s->WriteSwapScalar(fMin); + s->WriteSwapScalar(fMax); +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +plParallelIsect::plParallelIsect() +{ +} + +plParallelIsect::~plParallelIsect() +{ +} + +void plParallelIsect::SetNumPlanes(int n) +{ + fPlanes.SetCount(n); +} + +void plParallelIsect::SetPlane(int which, const hsPoint3& locPosOne, const hsPoint3& locPosTwo) +{ + fPlanes[which].fPosOne = locPosOne; + fPlanes[which].fPosTwo = locPosTwo; +} + +void plParallelIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + hsPoint3 wPosOne = l2w * fPlanes[i].fPosOne; + hsPoint3 wPosTwo = l2w * fPlanes[i].fPosTwo; + hsVector3 norm; + norm.Set(&wPosOne, &wPosTwo); + fPlanes[i].fNorm = norm; + hsScalar t0 = norm.InnerProduct(wPosOne); + hsScalar t1 = norm.InnerProduct(wPosTwo); + + if( t0 > t1 ) + { + fPlanes[i].fMin = t1; + fPlanes[i].fMax = t0; + } + else + { + fPlanes[i].fMin = t0; + fPlanes[i].fMax = t1; + } + } +} + +plVolumeCullResult plParallelIsect::Test(const hsBounds3Ext& bnd) const +{ + plVolumeCullResult retVal = kVolumeClear; + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + hsPoint2 depth; + bnd.TestPlane(fPlanes[i].fNorm, depth); + if( depth.fY < fPlanes[i].fMin ) + return kVolumeCulled; + if( depth.fX > fPlanes[i].fMax ) + return kVolumeCulled; + if( depth.fX < fPlanes[i].fMin ) + retVal = kVolumeSplit; + if( depth.fY > fPlanes[i].fMax ) + retVal = kVolumeSplit; + } + return retVal; +} + +hsScalar plParallelIsect::Test(const hsPoint3& pos) const +{ + hsScalar maxDist = 0; + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + hsScalar dist = fPlanes[i].fNorm.InnerProduct(pos); + + if( dist > fPlanes[i].fMax ) + { + dist -= fPlanes[i].fMax; + dist *= hsFastMath::InvSqrtAppr(fPlanes[i].fNorm.MagnitudeSquared()); + if( dist > maxDist ) + maxDist = dist; + } + else if( dist < fPlanes[i].fMin ) + { + dist = fPlanes[i].fMin - dist; + dist *= hsFastMath::InvSqrtAppr(fPlanes[i].fNorm.MagnitudeSquared()); + if( dist > maxDist ) + maxDist = dist; + } + + } + return maxDist; +} + +void plParallelIsect::Read(hsStream* s, hsResMgr* mgr) +{ + int n = s->ReadSwap16(); + + fPlanes.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + { + fPlanes[i].fNorm.Read(s); + fPlanes[i].fMin = s->ReadSwapScalar(); + fPlanes[i].fMax = s->ReadSwapScalar(); + + fPlanes[i].fPosOne.Read(s); + fPlanes[i].fPosTwo.Read(s); + } +} + +void plParallelIsect::Write(hsStream* s, hsResMgr* mgr) +{ + s->WriteSwap16(fPlanes.GetCount()); + + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + fPlanes[i].fNorm.Write(s); + s->WriteSwapScalar(fPlanes[i].fMin); + s->WriteSwapScalar(fPlanes[i].fMax); + + fPlanes[i].fPosOne.Write(s); + fPlanes[i].fPosTwo.Write(s); + } +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +plConvexIsect::plConvexIsect() +{ +} + +plConvexIsect::~plConvexIsect() +{ +} + +void plConvexIsect::AddPlaneUnchecked(const hsVector3& n, hsScalar dist) +{ + SinglePlane plane; + plane.fNorm = n; + plane.fPos.Set(0,0,0); + plane.fDist = dist; + + fPlanes.Append(plane); +} + +void plConvexIsect::AddPlane(const hsVector3& n, const hsPoint3& p) +{ + hsVector3 nNorm = n; + hsFastMath::Normalize(nNorm); + + // First, make sure some idiot isn't adding the same plane in twice. + // Also, look for the degenerate case of two parallel planes. In that + // case, take the outer. + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + const hsScalar kCloseToOne = 1.f - 1.e-4f; + if( fPlanes[i].fNorm.InnerProduct(nNorm) >= kCloseToOne ) + { + hsScalar dist = nNorm.InnerProduct(p); + if( dist > fPlanes[i].fDist ) + { + fPlanes[i].fDist = dist; + fPlanes[i].fPos = p; + } + return; + } + } + SinglePlane plane; + plane.fNorm = nNorm; + plane.fPos = p; + plane.fDist = nNorm.InnerProduct(p); + + fPlanes.Append(plane); +} + +void plConvexIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + hsPoint3 wPos = l2w * fPlanes[i].fPos; + + // Normal gets transpose of inverse. + fPlanes[i].fWorldNorm.fX = w2l.fMap[0][0] * fPlanes[i].fNorm.fX + + w2l.fMap[1][0] * fPlanes[i].fNorm.fY + + w2l.fMap[2][0] * fPlanes[i].fNorm.fZ; + + fPlanes[i].fWorldNorm.fY = w2l.fMap[0][1] * fPlanes[i].fNorm.fX + + w2l.fMap[1][1] * fPlanes[i].fNorm.fY + + w2l.fMap[2][1] * fPlanes[i].fNorm.fZ; + + fPlanes[i].fWorldNorm.fZ = w2l.fMap[0][2] * fPlanes[i].fNorm.fX + + w2l.fMap[1][2] * fPlanes[i].fNorm.fY + + w2l.fMap[2][2] * fPlanes[i].fNorm.fZ; + + hsFastMath::NormalizeAppr(fPlanes[i].fWorldNorm); + + fPlanes[i].fWorldDist = fPlanes[i].fWorldNorm.InnerProduct(wPos); + } +} + +plVolumeCullResult plConvexIsect::Test(const hsBounds3Ext& bnd) const +{ + plVolumeCullResult retVal = kVolumeClear; + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + hsPoint2 depth; + bnd.TestPlane(fPlanes[i].fWorldNorm, depth); + + if( depth.fX > fPlanes[i].fWorldDist ) + return kVolumeCulled; + + if( depth.fY > fPlanes[i].fWorldDist ) + retVal = kVolumeSplit; + } + return retVal; +} + +hsScalar plConvexIsect::Test(const hsPoint3& pos) const +{ + hsScalar maxDist = 0; + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + hsScalar dist = fPlanes[i].fWorldNorm.InnerProduct(pos) - fPlanes[i].fWorldDist; + + if( dist > maxDist ) + maxDist = dist; + } + return maxDist; +} + +void plConvexIsect::Read(hsStream* s, hsResMgr* mgr) +{ + Int16 n = s->ReadSwap16(); + + fPlanes.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + { + fPlanes[i].fNorm.Read(s); + fPlanes[i].fPos.Read(s); + fPlanes[i].fDist = s->ReadSwapScalar(); + + fPlanes[i].fWorldNorm.Read(s); + fPlanes[i].fWorldDist = s->ReadSwapScalar(); + } +} + +void plConvexIsect::Write(hsStream* s, hsResMgr* mgr) +{ + s->WriteSwap16(fPlanes.GetCount()); + + int i; + for( i = 0; i < fPlanes.GetCount(); i++ ) + { + fPlanes[i].fNorm.Write(s); + fPlanes[i].fPos.Write(s); + s->WriteSwapScalar(fPlanes[i].fDist); + + fPlanes[i].fWorldNorm.Write(s); + s->WriteSwapScalar(fPlanes[i].fWorldDist); + } +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +plBoundsIsect::plBoundsIsect() +{ +} + +plBoundsIsect::~plBoundsIsect() +{ +} + +void plBoundsIsect::SetBounds(const hsBounds3Ext& bnd) +{ + fLocalBounds = bnd; + fWorldBounds = bnd; +} + +void plBoundsIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + fWorldBounds = fLocalBounds; + fWorldBounds.Transform(&l2w); +} + +plVolumeCullResult plBoundsIsect::Test(const hsBounds3Ext& bnd) const +{ + int retVal = fWorldBounds.TestBound(bnd); + if( retVal < 0 ) + return kVolumeCulled; + if( retVal > 0 ) + return kVolumeClear; + + retVal = bnd.TestBound(fWorldBounds); + + return retVal < 0 ? kVolumeCulled : kVolumeSplit; +} + +hsScalar plBoundsIsect::Test(const hsPoint3& pos) const +{ + hsAssert(false, "Unimplemented"); + return 0.f; +} + +void plBoundsIsect::Read(hsStream* s, hsResMgr* mgr) +{ + fLocalBounds.Read(s); + fWorldBounds.Read(s); +} + +void plBoundsIsect::Write(hsStream* s, hsResMgr* mgr) +{ + fLocalBounds.Write(s); + fWorldBounds.Write(s); +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +plComplexIsect::plComplexIsect() +{ +} + +plComplexIsect::~plComplexIsect() +{ + int i; + for( i = 0; i < fVolumes.GetCount(); i++ ) + delete fVolumes[i]; +} + +void plComplexIsect::AddVolume(plVolumeIsect* v) +{ + fVolumes.Append(v); +} + +void plComplexIsect::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + int i; + for( i = 0; i < fVolumes.GetCount(); i++ ) + fVolumes[i]->SetTransform(l2w, w2l); +} + +void plComplexIsect::Read(hsStream* s, hsResMgr* mgr) +{ + int n = s->ReadSwap16(); + fVolumes.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + { + fVolumes[i] = plVolumeIsect::ConvertNoRef(mgr->ReadCreatable(s)); + hsAssert(fVolumes[i], "Failure reading in a sub-volume"); + } +} + +void plComplexIsect::Write(hsStream* s, hsResMgr* mgr) +{ + s->WriteSwap16(fVolumes.GetCount()); + int i; + for( i = 0; i < fVolumes.GetCount(); i++ ) + mgr->WriteCreatable(s, fVolumes[i]); +} + + + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +plUnionIsect::plUnionIsect() +{ +} + +plUnionIsect::~plUnionIsect() +{ +} + +plVolumeCullResult plUnionIsect::Test(const hsBounds3Ext& bnd) const +{ + plVolumeCullResult retVal = kVolumeCulled; + int i; + for( i = 0; i < fVolumes.GetCount(); i++ ) + { + plVolumeCullResult ret = fVolumes[i]->Test(bnd); + + switch( ret ) + { + case kVolumeCulled: + break; + case kVolumeClear: + return kVolumeClear; + case kVolumeSplit: + retVal = kVolumeSplit; + break; + }; + } + return retVal; +} + +hsScalar plUnionIsect::Test(const hsPoint3& pos) const +{ + hsScalar retVal = 1.e33f; + int i; + for( i = 0; i < fVolumes.GetCount(); i++ ) + { + hsScalar ret = fVolumes[i]->Test(pos); + if( ret <= 0 ) + return 0; + if( ret < retVal ) + retVal = ret; + } + return retVal; +} + +/////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// + +plIntersectionIsect::plIntersectionIsect() +{ +} + +plIntersectionIsect::~plIntersectionIsect() +{ +} + +plVolumeCullResult plIntersectionIsect::Test(const hsBounds3Ext& bnd) const +{ + plVolumeCullResult retVal = kVolumeClear; + int i; + for( i = 0; i < fVolumes.GetCount(); i++ ) + { + plVolumeCullResult ret = fVolumes[i]->Test(bnd); + + switch( ret ) + { + case kVolumeCulled: + return kVolumeCulled; + case kVolumeClear: + break; + case kVolumeSplit: + retVal = kVolumeSplit; + break; + }; + } + return retVal; +} + +hsScalar plIntersectionIsect::Test(const hsPoint3& pos) const +{ + hsScalar retVal = -1.f; + int i; + for( i = 0; i < fVolumes.GetCount(); i++ ) + { + hsScalar ret = fVolumes[i]->Test(pos); + if( ret > retVal ) + retVal = ret; + } + return retVal; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plVolumeIsect.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plVolumeIsect.h new file mode 100644 index 00000000..58acb582 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plIntersect/plVolumeIsect.h @@ -0,0 +1,308 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plVolumeIsect_inc +#define plVolumeIsect_inc + +#include "hsGeometry3.h" +#include "hsMatrix44.h" +#include "hsTemplates.h" +#include "hsBounds.h" + +#include "../pnFactory/plCreatable.h" + +class hsBounds3Ext; + +enum plVolumeCullResult +{ + kVolumeClear = 0x0, + kVolumeCulled = 0x1, + kVolumeSplit = 0x2 +}; + + +class plVolumeIsect : public plCreatable +{ +public: + CLASSNAME_REGISTER( plVolumeIsect ); + GETINTERFACE_ANY( plVolumeIsect, plCreatable ); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) = 0; + virtual plVolumeCullResult Test(const hsBounds3Ext& bnd) const = 0; + virtual hsScalar Test(const hsPoint3& pos) const = 0; + + virtual void Read(hsStream* s, hsResMgr* mgr) = 0; + virtual void Write(hsStream* s, hsResMgr* mgr) = 0; +}; + +class plSphereIsect : public plVolumeIsect +{ +protected: + hsPoint3 fCenter; + hsPoint3 fWorldCenter; + hsScalar fRadius; + hsPoint3 fMins; + hsPoint3 fMaxs; +public: + plSphereIsect(); + virtual ~plSphereIsect(); + + CLASSNAME_REGISTER( plSphereIsect ); + GETINTERFACE_ANY( plSphereIsect, plVolumeIsect ); + + void SetCenter(const hsPoint3& c); + void SetRadius(hsScalar r); + + hsScalar GetRadius() const { return fRadius; } + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual plVolumeCullResult Test(const hsBounds3Ext& bnd) const; + virtual hsScalar Test(const hsPoint3& pos) const; // return 0 if point inside, else "distance" from pos to volume + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + +class plConeIsect : public plVolumeIsect +{ +protected: + hsBool fCapped; + + hsScalar fRadAngle; + hsScalar fLength; + + hsPoint3 fWorldTip; + hsVector3 fWorldNorm; + + hsMatrix44 fWorldToNDC; + hsMatrix44 fLightToNDC; + + hsVector3 fNorms[5]; + hsScalar fDists[5]; + + void ISetup(); +public: + + plConeIsect(); + virtual ~plConeIsect(); + + CLASSNAME_REGISTER( plConeIsect ); + GETINTERFACE_ANY( plConeIsect, plVolumeIsect ); + + void SetAngle(hsScalar rads); + void SetLength(hsScalar d); + + hsScalar GetLength() const { return fCapped ? fLength : 0; } + hsScalar GetAngle() const { return fRadAngle; } + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual plVolumeCullResult Test(const hsBounds3Ext& bnd) const; + virtual hsScalar Test(const hsPoint3& pos) const; + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + +class plCylinderIsect : public plVolumeIsect +{ +protected: + hsPoint3 fTop; + hsPoint3 fBot; + hsScalar fRadius; + + hsPoint3 fWorldBot; + hsVector3 fWorldNorm; + hsScalar fLength; + hsScalar fMin; + hsScalar fMax; + + void ISetupCyl(const hsPoint3& wTop, const hsPoint3& wBot, hsScalar radius); + +public: + plCylinderIsect(); + virtual ~plCylinderIsect(); + + CLASSNAME_REGISTER( plCylinderIsect ); + GETINTERFACE_ANY( plCylinderIsect, plVolumeIsect ); + + void SetCylinder(const hsPoint3& lTop, const hsPoint3& lBot, hsScalar radius); + void SetCylinder(const hsPoint3& lBot, const hsVector3& axis, hsScalar radius); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual plVolumeCullResult Test(const hsBounds3Ext& bnd) const; + virtual hsScalar Test(const hsPoint3& pos) const; + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + +class plParallelIsect : public plVolumeIsect +{ +protected: + class ParPlane + { + public: + hsVector3 fNorm; + hsScalar fMin; + hsScalar fMax; + + hsPoint3 fPosOne; + hsPoint3 fPosTwo; + }; + hsTArray fPlanes; + +public: + plParallelIsect(); + virtual ~plParallelIsect(); + + CLASSNAME_REGISTER( plParallelIsect ); + GETINTERFACE_ANY( plParallelIsect, plVolumeIsect ); + + void SetNumPlanes(int n); // each plane is really two parallel planes + UInt16 GetNumPlanes() const { return fPlanes.GetCount(); } + + void SetPlane(int which, const hsPoint3& locPosOne, const hsPoint3& locPosTwo); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual plVolumeCullResult Test(const hsBounds3Ext& bnd) const; + virtual hsScalar Test(const hsPoint3& pos) const; + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + +class plConvexIsect : public plVolumeIsect +{ +protected: + class SinglePlane + { + public: + hsVector3 fNorm; + hsScalar fDist; + hsPoint3 fPos; + + hsVector3 fWorldNorm; + hsScalar fWorldDist; + }; + + hsTArray fPlanes; + +public: + plConvexIsect(); + virtual ~plConvexIsect(); + + CLASSNAME_REGISTER( plConvexIsect ); + GETINTERFACE_ANY( plConvexIsect, plVolumeIsect ); + + void ClearPlanes() { fPlanes.SetCount(0); } + void AddPlaneUnchecked(const hsVector3& n, hsScalar dist); // no validation here + void AddPlane(const hsVector3& n, const hsPoint3& p); + UInt16 GetNumPlanes() const { return fPlanes.GetCount(); } + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual plVolumeCullResult Test(const hsBounds3Ext& bnd) const; + virtual hsScalar Test(const hsPoint3& pos) const; + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + +class plBoundsIsect : public plVolumeIsect +{ +protected: + hsBounds3Ext fLocalBounds; + hsBounds3Ext fWorldBounds; +public: + plBoundsIsect(); + virtual ~plBoundsIsect(); + + CLASSNAME_REGISTER( plBoundsIsect ); + GETINTERFACE_ANY( plBoundsIsect, plVolumeIsect ); + + void SetBounds(const hsBounds3Ext& bnd); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual plVolumeCullResult Test(const hsBounds3Ext& bnd) const; + virtual hsScalar Test(const hsPoint3& pos) const; + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + +class plComplexIsect : public plVolumeIsect +{ +protected: + hsTArray fVolumes; + +public: + plComplexIsect(); + virtual ~plComplexIsect(); + + CLASSNAME_REGISTER( plComplexIsect ); + GETINTERFACE_ANY( plComplexIsect, plVolumeIsect ); + + void AddVolume(plVolumeIsect* v); // Will capture pointer + UInt16 GetNumVolumes() const { return fVolumes.GetCount(); } + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + +class plUnionIsect : public plComplexIsect +{ +public: + plUnionIsect(); + ~plUnionIsect(); + + CLASSNAME_REGISTER( plUnionIsect ); + GETINTERFACE_ANY( plUnionIsect, plComplexIsect ); + + virtual plVolumeCullResult Test(const hsBounds3Ext& bnd) const; + virtual hsScalar Test(const hsPoint3& pos) const; +}; + +class plIntersectionIsect : public plComplexIsect +{ +public: + plIntersectionIsect(); + ~plIntersectionIsect(); + + CLASSNAME_REGISTER( plIntersectionIsect ); + GETINTERFACE_ANY( plIntersectionIsect, plComplexIsect ); + + virtual plVolumeCullResult Test(const hsBounds3Ext& bnd) const; + virtual hsScalar Test(const hsPoint3& pos) const; +}; + +#endif // plVolumeIsect_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plJPEG/plJPEG.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plJPEG/plJPEG.cpp new file mode 100644 index 00000000..a16485af --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plJPEG/plJPEG.cpp @@ -0,0 +1,328 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plJPEG - JPEG Codec Wrapper for Plasma // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 2.1.2002 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plJPEG.h" +#include "hsStream.h" +#include "hsExceptions.h" +#include "hsUtils.h" +#include "../plGImage/plMipmap.h" + +#ifndef HS_BUILD_FOR_WIN32 +#error Currently the JPEG libraries don't build for anything but Win32. If you're building this on a non-Win32 platform....WHY?? +#endif + +#include "../../../../../StaticSDKs/Win32/IJL/include/ijl.h" + +//// Local Statics //////////////////////////////////////////////////////////// +// Done this way so we don't have to declare them in the .h file and pull in +// the platform-specific library + +static IJLERR sLastErrCode = IJL_OK; + + +//// Instance ///////////////////////////////////////////////////////////////// + +plJPEG &plJPEG::Instance( void ) +{ + static plJPEG theInstance; + return theInstance; +} + +//// GetLastError ///////////////////////////////////////////////////////////// + +const char *plJPEG::GetLastError( void ) +{ + return ijlErrorStr( sLastErrCode ); +} + +//// IRead //////////////////////////////////////////////////////////////////// +// Given an open hsStream (or a filename), reads the JPEG data off of the +// stream and decodes it into a new plMipmap. The mipmap's buffer ends up +// being a packed RGBx buffer, where x is 8 bits of unused alpha (go figure +// that JPEG images can't store alpha, or even if they can, IJL certainly +// doesn't know about it). +// Returns a pointer to the new mipmap if successful, nil otherwise. +// Note: more or less lifted straight out of the IJL documentation, with +// some changes to fit Plasma coding style and formats. + +plMipmap *plJPEG::IRead( hsStream *inStream ) +{ + MemPushDisableTracking(); + + plMipmap *newMipmap = nil; + UInt8 *jpegSourceBuffer = nil; + UInt32 jpegSourceSize; + + JPEG_CORE_PROPERTIES jcProps; + + + sLastErrCode = IJL_OK; + + try + { + /// Init the IJL library + sLastErrCode = ijlInit( &jcProps ); + if( sLastErrCode != IJL_OK ) + throw( false ); + + /// Read in the JPEG header + if ( inStream->GetEOF() == 0 ) + throw( false ); + + /// Wonderful limitation of mixing our streams with IJL--it wants either a filename + /// or a memory buffer. Since we can't give it the former, we have to read the entire + /// JPEG stream into a separate buffer before we can decode it. Which means we ALSO + /// have to write/read a length of said buffer. Such is life, I guess... + jpegSourceSize = inStream->ReadSwap32(); + jpegSourceBuffer = TRACKED_NEW UInt8[ jpegSourceSize ]; + if( jpegSourceBuffer == nil ) + { + sLastErrCode = IJL_MEMORY_ERROR; + throw( false ); + } + + inStream->Read( jpegSourceSize, jpegSourceBuffer ); + + jcProps.JPGFile = nil; + jcProps.JPGBytes = jpegSourceBuffer; + jcProps.JPGSizeBytes = jpegSourceSize; + + sLastErrCode = ijlRead( &jcProps, IJL_JBUFF_READPARAMS ); + if( sLastErrCode != IJL_OK ) + throw( false ); + + /// So we got lots of data to play with now. First, set the JPEG color + /// space to read in from/as... + /// From the IJL code: + // "Set the JPG color space ... this will always be + // somewhat of an educated guess at best because JPEG + // is "color blind" (i.e., nothing in the bit stream + // tells you what color space the data was encoded from). + // However, in this example we assume that we are + // reading JFIF files which means that 3 channel images + // are in the YCbCr color space and 1 channel images are + // in the Y color space." + + switch( jcProps.JPGChannels ) + { + case 1: + jcProps.JPGColor = IJL_G; + jcProps.DIBColor = IJL_RGBA_FPX; + jcProps.DIBChannels = 4; // We ALWAYS try to decode to 4 channels + break; + + case 3: + jcProps.JPGColor = IJL_YCBCR; + jcProps.DIBColor = IJL_RGBA_FPX; + jcProps.DIBChannels = 4; + break; + + default: + // We should probably assert here, since we're pretty sure we WON'T get ARGB. + hsAssert( false, "Unknown JPEG stream format in ReadFromStream()" ); + jcProps.JPGColor = IJL_OTHER; + jcProps.DIBColor = IJL_OTHER; + jcProps.DIBChannels = jcProps.JPGChannels; + break; + } + + /// Construct a new mipmap to hold everything + newMipmap = TRACKED_NEW plMipmap( jcProps.JPGWidth, jcProps.JPGHeight, plMipmap::kRGB32Config, 1, plMipmap::kJPEGCompression ); + if( newMipmap == nil || newMipmap->GetImage() == nil ) + { + sLastErrCode = IJL_MEMORY_ERROR; + throw( false ); + } + + /// Set up to read in to that buffer we now have + jcProps.DIBWidth = newMipmap->GetWidth(); + jcProps.DIBHeight = newMipmap->GetHeight(); + jcProps.DIBPadBytes = 0; + jcProps.DIBBytes = (UInt8 *)newMipmap->GetImage(); + + sLastErrCode = ijlRead( &jcProps, IJL_JBUFF_READWHOLEIMAGE ); + if( sLastErrCode != IJL_OK ) + throw( false ); + + // Sometimes life just sucks + ISwapRGBAComponents( (UInt32 *)newMipmap->GetImage(), newMipmap->GetWidth() * newMipmap->GetHeight() ); + } + catch( ... ) + { + delete newMipmap; + newMipmap = nil; + } + + // Cleanup + delete [] jpegSourceBuffer; + + // Clean up the IJL Library + ijlFree( &jcProps ); + MemPopDisableTracking(); + + // All done! + return newMipmap; +} + +plMipmap* plJPEG::ReadFromFile( const char *fileName ) +{ + wchar* wFilename = hsStringToWString(fileName); + plMipmap* retVal = ReadFromFile(wFilename); + delete [] wFilename; + return retVal; +} + +plMipmap* plJPEG::ReadFromFile( const wchar *fileName ) +{ + // we use a stream because the IJL can't handle unicode + hsUNIXStream out; + if (!out.Open(fileName, L"rb")) + return false; + plMipmap* ret = IRead(&out); + out.Close(); + return ret; +} + +//// IWrite /////////////////////////////////////////////////////////////////// +// Oh, figure it out yourself. :P + +hsBool plJPEG::IWrite( plMipmap *source, hsStream *outStream ) +{ + hsBool result = true, swapped = false; + UInt8 *jpgBuffer = nil; + UInt32 jpgBufferSize = 0; + + JPEG_CORE_PROPERTIES jcProps; + + + try + { + // Init the IJL Library + sLastErrCode = ijlInit( &jcProps ); + if( sLastErrCode != IJL_OK ) + throw( false ); + + // Create a buffer to hold the data + jpgBufferSize = source->GetWidth() * source->GetHeight() * 3; + jpgBuffer = TRACKED_NEW UInt8[ jpgBufferSize ]; + if( jpgBuffer == nil ) + { + sLastErrCode = IJL_MEMORY_ERROR; + throw( false ); + } + + jcProps.JPGFile = nil; + jcProps.JPGBytes = jpgBuffer; + jcProps.JPGSizeBytes = jpgBufferSize; + + jcProps.DIBWidth = source->GetWidth(); + jcProps.DIBHeight = source->GetHeight(); + jcProps.DIBBytes = (UInt8 *)source->GetImage(); + jcProps.DIBPadBytes = 0; + jcProps.DIBChannels = 4; + jcProps.DIBColor = IJL_RGB; + + jcProps.JPGWidth = source->GetWidth(); + jcProps.JPGHeight = source->GetHeight(); + jcProps.JPGChannels = 3; + jcProps.JPGColor = IJL_YCBCR; + jcProps.JPGSubsampling = IJL_411; // 4:1:1 subsampling + jcProps.jquality = fWriteQuality; + + // Sometimes life just sucks + ISwapRGBAComponents( (UInt32 *)source->GetImage(), source->GetWidth() * source->GetHeight() ); + swapped = true; + + // Write it! + sLastErrCode = ijlWrite( &jcProps, IJL_JBUFF_WRITEWHOLEIMAGE ); + if( sLastErrCode != IJL_OK ) + throw( false ); + + // On output, the IJL fills our image buffer with the JPEG stream, plus changes JPGSizeBytes to + // the length of the stream + outStream->WriteSwap32( jcProps.JPGSizeBytes ); + outStream->Write( jcProps.JPGSizeBytes, jcProps.JPGBytes ); + } + catch( ... ) + { + result = false; + } + + // Cleanup + if ( jpgBuffer ) + delete [] jpgBuffer; + ijlFree( &jcProps ); + + if( swapped ) + ISwapRGBAComponents( (UInt32 *)source->GetImage(), source->GetWidth() * source->GetHeight() ); + + return result; +} + +hsBool plJPEG::WriteToFile( const char *fileName, plMipmap *sourceData ) +{ + wchar* wFilename = hsStringToWString(fileName); + hsBool retVal = WriteToFile(wFilename, sourceData); + delete [] wFilename; + return retVal; +} + +hsBool plJPEG::WriteToFile( const wchar *fileName, plMipmap *sourceData ) +{ + // we use a stream because the IJL can't handle unicode + hsUNIXStream out; + if (!out.Open(fileName, L"wb")) + return false; + hsBool ret = IWrite(sourceData, &out); + out.Close(); + return ret; +} + +//// ISwapRGBAComponents ////////////////////////////////////////////////////// + +void plJPEG::ISwapRGBAComponents( UInt32 *data, UInt32 count ) +{ + while( count-- ) + { + *data = ( ( ( *data ) & 0xff00ff00 ) ) | + ( ( ( *data ) & 0x00ff0000 ) >> 16 ) | +// ( ( ( *data ) & 0x0000ff00 ) << 8 ) | + ( ( ( *data ) & 0x000000ff ) << 16 ); + data++; + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plJPEG/plJPEG.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plJPEG/plJPEG.h new file mode 100644 index 00000000..bb5a0e83 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plJPEG/plJPEG.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plJPEG - JPEG Codec Wrapper for Plasma // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 2.1.2002 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plJPEG_h +#define _plJPEG_h + + +//// Class Definition ///////////////////////////////////////////////////////// + +class plMipmap; +class hsStream; + +class plJPEG +{ + protected: + + UInt8 fWriteQuality; + + // Pick one... + plMipmap *IRead( hsStream *inStream ); + hsBool IWrite( plMipmap *source, hsStream *outStream ); + + void ISwapRGBAComponents( UInt32 *data, UInt32 count ); + + public: + + plMipmap *ReadFromStream( hsStream *inStream ) { return IRead( inStream ); } + plMipmap *ReadFromFile( const char *fileName ); + plMipmap *ReadFromFile( const wchar *fileName ); + + hsBool WriteToStream( hsStream *outStream, plMipmap *sourceData ) { return IWrite( sourceData, outStream ); } + hsBool WriteToFile( const char *fileName, plMipmap *sourceData ); + hsBool WriteToFile( const wchar *fileName, plMipmap *sourceData ); + + // Range is 0 (worst) to 100 (best) + void SetWriteQuality( UInt8 q ) { fWriteQuality = q; } + + const char *GetLastError( void ); + + static plJPEG &Instance( void ); +}; + +#endif // _plJPEG_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsNoiseFunc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsNoiseFunc.cpp new file mode 100644 index 00000000..60c8d7da --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsNoiseFunc.cpp @@ -0,0 +1,140 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsNoiseFunc.h" +#include "hsTypes.h" +#include "hsScalar.h" +#include "hsGeometry3.h" + +hsNoiseFunc::hsNoiseFunc() +{ +} + +hsNoiseFunc::~hsNoiseFunc() +{ +} + +void hsNoiseFunc::Seed(UInt32 s) +{ + srand(s); +} + +hsTableNoise::hsTableNoise() +: fTable(nil), fTableLen(0) +{ +} + +hsTableNoise::~hsTableNoise() +{ + delete [] fTable; +} + +void hsTableNoise::SetTable(int len, hsScalar* arr) +{ + fTableLen = len; + + delete [] fTable; + if( !len ) + { + fTable = nil; + return; + } + + fTable = TRACKED_NEW hsScalar[len+2]; + + int i; + for( i = 0; i < len; i++ ) + fTable[i] = arr[i]; + fTable[i++] = fTable[i-1]; + fTable[i++] = fTable[i-1]; + +} + +hsScalar hsTableNoise::Noise(hsScalar lo, hsScalar hi, hsScalar t) +{ + hsAssert(fTableLen, "Badly initialized table noise function"); + + hsScalar r = hsScalar(rand()) / hsScalar(RAND_MAX); + r = lo + (hi - lo) * r; + + if( t < 0 ) + t = 0; + else if( t > hsScalar1 ) + t = hsScalar1; + + hsScalar tIdx = t * fTableLen; + UInt32 idx = UInt32(tIdx); + hsScalar frac = tIdx - hsScalar(idx); + hsAssert((idx >= 0)&&(idx <= fTableLen), "Noise parm t out of range [0..1]"); + + hsScalar scale = fTable[idx] + (fTable[idx+1] - fTable[idx]) * frac; + + r *= scale; + + return r; +} + +hsScalar hsTableNoise::NoisePoint(const hsPoint3& p, hsScalar lo, hsScalar hi, hsScalar t) +{ + hsAssert(fTableLen, "Badly initialized table noise function"); + + UInt32 sX = *((UInt32*)&p.fX); + UInt32 sY = *((UInt32*)&p.fY); + UInt32 sZ = *((UInt32*)&p.fZ); + + UInt32 sAll = ((((sX & 0x07800000) >> 16) | ((sX & 0x007fffff) >> 17)) << 20) + | ((((sY & 0x07800000) >> 16) | ((sY & 0x007fffff) >> 17)) << 10) + | ((((sZ & 0x07800000) >> 16) | ((sZ & 0x007fffff) >> 17)) ); + + const UInt32 kExp = 0x3f800000; + const UInt32 kMsk = 0x007fffff; + + const UInt32 kA = 1665636L; + const UInt32 kC = 1013904223L; + + UInt32 iR = kA * sAll + kC; + iR &= kMsk; + iR |= kExp; + + hsScalar r = (*(float*)&iR) - 1.f; + + r = lo + (hi - lo) * r; + + if( t < 0 ) + t = 0; + else if( t > hsScalar1 ) + t = hsScalar1; + + hsScalar tIdx = t * fTableLen; + UInt32 idx = UInt32(tIdx); + hsScalar frac = tIdx - hsScalar(idx); + hsAssert((idx >= 0)&&(idx <= fTableLen), "Noise parm t out of range [0..1]"); + + hsScalar scale = fTable[idx] + (fTable[idx+1] - fTable[idx]) * frac; + + r *= scale; + + return r; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsNoiseFunc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsNoiseFunc.h new file mode 100644 index 00000000..002e7fb9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsNoiseFunc.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsNoiseFunc_inc +#define hsNoiseFunc_inc + +#include "hsRefCnt.h" +#include "hsScalar.h" + +struct hsPoint3; + +class hsNoiseFunc : public hsRefCnt // should inherit from keyed object +{ +public: + hsNoiseFunc(); + virtual ~hsNoiseFunc(); + + virtual void Seed(UInt32 s); + virtual hsScalar Noise(hsScalar lo=0, hsScalar hi=hsScalar1, hsScalar t=0) = 0; // t = [0..1] - returns random num [lo..hi] scaled by fTable[t] + + virtual hsScalar NoisePoint(const hsPoint3& p, hsScalar lo=0, hsScalar hi=hsScalar1, hsScalar t=0) = 0; // t = [0..1] - returns random num [lo..hi] scaled by fTable[t] +}; + +class hsTableNoise : public hsNoiseFunc // should inherit from keyed object +{ +protected: + hsScalar* fTable; + UInt32 fTableLen; + + +public: + hsTableNoise(); + virtual ~hsTableNoise(); + + void SetTable(int len, hsScalar* arr); // copies. arr should be hsScalars in range [0..1] + hsScalar* GetTable(int& len) { len = fTableLen; return fTable; } // should be debug only, access through noise func + + virtual hsScalar Noise(hsScalar lo=0, hsScalar hi=hsScalar1, hsScalar t=0); // t = [0..1] - returns random num [lo..hi] scaled by fTable[t] + + virtual hsScalar NoisePoint(const hsPoint3& p, hsScalar lo=0, hsScalar hi=hsScalar1, hsScalar t=0); // t = [0..1] - returns random num [lo..hi] scaled by fTable[t] +}; + +#endif // hsNoiseFunc_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsRadixSort.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsRadixSort.cpp new file mode 100644 index 00000000..996182db --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsRadixSort.cpp @@ -0,0 +1,165 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsMemory.h" +#include "hsRadixSort.h" + +hsRadixSort::hsRadixSort() +{ + HSMemory::Clear(fHeads, 256*sizeof(Elem*)); + HSMemory::Clear(fTails, 256*sizeof(Elem*)); +} + +void hsRadixSort::ILink(Elem*& head, Elem*& tail, int i) +{ + if( fHeads[i] ) + { + if( !head ) + head = fHeads[i]; + else + tail->fNext = fHeads[i]; + tail = fTails[i]; + } + fHeads[i] = fTails[i] = nil; +} + +void hsRadixSort::ISlot(Elem* in, int i) +{ + if( !fTails[i] ) + fHeads[i] = in; + else + fTails[i]->fNext = in; + in->fNext = nil; + fTails[i] = in; +} + +void hsRadixSort::ICollapse() +{ + Elem* head = nil; + Elem* tail = nil; + + int i; + for( i = 0; i < 256; i++ ) + ILink(head, tail, i); + + fList = head; +} + +void hsRadixSort::IUnPackSignedInt() +{ + Elem* head = nil; + Elem* tail = nil; + + int i; + for( i = 128; i < 256; i++ ) + ILink(head, tail, i); + + for( i = 0; i < 128; i++ ) + ILink(head, tail, i); + + fList = head; +} + +void hsRadixSort::IUnPackFloat() +{ + Elem* head = nil; + Elem* tail = nil; + + int i; + for( i = 128; i < 256; i++ ) + ILink(head, tail, i); + fList = head; + head = tail; + tail = fList; + IReverse(); + + for( i = 0; i < 128; i++ ) + ILink(head, tail, i); + + fList = head; +} + +void hsRadixSort::IReverse() +{ + if( !(fList && fList->fNext) ) + return; + + Elem* p = fList->fNext; + fList->fNext = nil; + while( p ) + { + Elem* n = p->fNext; + p->fNext = fList; + fList = p; + p = n; + } +} + +hsRadixSort::Elem* hsRadixSort::Sort(Elem* inList, UInt32 flags) +{ + if( !(inList && inList->fNext) ) + return inList; + + fList = inList; + + Elem* p; + Elem* n; + + for( p = fList, n = p->fNext; n; p = n, n = p->fNext ) + ISlot(p, p->fKey.fLong & 0xff); + ISlot(p, p->fKey.fLong & 0xff); + + ICollapse(); + + for( p = fList, n = p->fNext; n; p = n, n = p->fNext ) + ISlot(p, (p->fKey.fLong >> 8) & 0xff); + ISlot(p, (p->fKey.fLong >> 8) & 0xff); + + ICollapse(); + + for( p = fList, n = p->fNext; n; p = n, n = p->fNext ) + ISlot(p, (p->fKey.fLong >> 16) & 0xff); + ISlot(p, (p->fKey.fLong >> 16) & 0xff); + + ICollapse(); + + for( p = fList, n = p->fNext; n; p = n, n = p->fNext ) + ISlot(p, (p->fKey.fLong >> 24) & 0xff); + ISlot(p, (p->fKey.fLong >> 24) & 0xff); + + if( flags & kSignedInt ) + IUnPackSignedInt(); + else if( flags & kUnsigned ) + ICollapse(); + else + IUnPackFloat(); + + if( flags & kReverse ) + IReverse(); + + return fList; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsRadixSort.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsRadixSort.h new file mode 100644 index 00000000..d4b5d1e9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsRadixSort.h @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsRadixSort_inc +#define hsRadixSort_inc + +class hsRadixSortElem +{ +public: + union { + float fFloat; + long fLong; + unsigned long fULong; + } fKey; + + void* fBody; + + hsRadixSortElem* fNext; +}; + +class hsRadixSort { +public: + enum { + kFloat = 0x0, + kSignedInt = 0x1, + kUnsigned = 0x2, + kReverse = 0x4 + }; + typedef hsRadixSortElem Elem; + +protected: + Elem* fList; + Elem* fHeads[256]; + Elem* fTails[256]; + + inline void ILink(Elem*& head, Elem*& tail, int i); // inline? + inline void ISlot(Elem* in, int i); // inline? + + void ICollapse(); + void IUnPackSignedInt(); + void IUnPackFloat(); + void IReverse(); + +public: + + hsRadixSort(); + + Elem* Sort(Elem* inList, UInt32 flags = 0); + +}; + +#endif // hsRadixSort_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsSearchVersion.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsSearchVersion.h new file mode 100644 index 00000000..86d8b71d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/hsSearchVersion.h @@ -0,0 +1,165 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsSearchVersion_inc +#define hsSearchVersion_inc + +#include "hsTypes.h" + +/* +do a template of lists to search for a matching entry. basic idea is that +you start off with an array of buckets and you know that you will get at +least one, possibly several, in each bucket. when you go to search, you already +know which bucket it will be in if it's there. even as the array is being filled, +each filled entry in each bucket has a valid forever index, as well as it's key +value. so array is fixed length, index into array has no bearing on forever index, +elements of array can grow, and at all times the used forever indices form a contiguous +set from 0 to max forever index. +*/ + +template class hsVersionNode { +protected: + T fData; + Int32 fIndex; + hsVersionNode* fNext; +public: + hsVersionNode(const UInt32 idx, const T &data) : fIndex(idx), fNext(nil) { fData = data; } + ~hsVersionNode() { delete fNext; } + + hsVersionNode* Next() const { return fNext; } + + Int32 Index() const { return fIndex; } + + inline void Append(hsVersionNode* next); + inline int operator==(const T& o) const; + + int operator!=(const T& o) const { return !(this->operator==(o)); } + + T& GetData() { return fData; } +}; + +template int hsVersionNode::operator==(const T& data) const +{ + return fData == data; +} + +template void hsVersionNode::Append(hsVersionNode* next) +{ + if( fNext ) + fNext->Append(next); + else + fNext = next; +} + +template class hsSearchVersion { +protected: + UInt32 fLength; + hsVersionNode** fArray; + UInt32 fNextIndex; + UInt32 fNumIndex; + UInt32 fIncIndex; + T** fBackArray; + + void ICheckBackArray(); +public: + hsSearchVersion(UInt32 len, UInt32 inc = 0); + ~hsSearchVersion(); + + T& operator[]( Int32 index ); + + Int32 Find(int where, const T& what, hsBool forceUnique=false); + + UInt32 GetCount() const { return fNextIndex; } +}; + +template T& hsSearchVersion::operator[]( Int32 index ) +{ + hsDebugCode(hsThrowIfBadParam((UInt32)index >= (UInt32)fNextIndex);) + + return *fBackArray[index]; +} + +template hsSearchVersion::hsSearchVersion(UInt32 len, UInt32 inc) + : fNextIndex(0) +{ + fIncIndex = inc ? inc : len; + fLength = len; + fArray = TRACKED_NEW hsVersionNode*[fLength]; + HSMemory::Clear(fArray, fLength*sizeof(*fArray)); + fBackArray = TRACKED_NEW T*[fNumIndex = fLength]; +} + +template hsSearchVersion::~hsSearchVersion() +{ + int i; + for( i = 0; i < fLength; i++ ) + delete fArray[i]; + delete [] fArray; + delete [] fBackArray; +} + +template void hsSearchVersion::ICheckBackArray() +{ + if( fNextIndex >= fNumIndex ) + { + T** newBackArray = TRACKED_NEW T*[fNumIndex + fIncIndex]; + HSMemory::BlockMove(fBackArray, newBackArray, fNextIndex*sizeof(T*)); + delete [] fBackArray; + fBackArray = newBackArray; + fNumIndex += fIncIndex; + } +} + +template Int32 hsSearchVersion::Find(int where, const T&what, hsBool forceUnique) +{ + hsVersionNode* curr = fArray[where]; + + ICheckBackArray(); + + if( !curr ) + { + hsVersionNode* next = TRACKED_NEW hsVersionNode(fNextIndex, what); + fArray[where] = next; + fBackArray[fNextIndex] = &next->GetData(); + return fNextIndex++; + } + if( *curr == what ) + return curr->Index(); + + while( curr->Next() + && (forceUnique || (*curr->Next() != what)) ) + curr = curr->Next(); + + if( curr->Next() ) + return curr->Next()->Index(); + + hsVersionNode* next = TRACKED_NEW hsVersionNode(fNextIndex, what); + curr->Append(next); + fBackArray[fNextIndex] = &next->GetData(); + return fNextIndex++; +} + +#endif // hsSearchVersion_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plAvg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plAvg.cpp new file mode 100644 index 00000000..57458627 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plAvg.cpp @@ -0,0 +1,143 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plAvg.h" +#include + +template class TimeBasedAvgRing; +template class TimeBasedAvgRing; + +#define PercisionRoundUp(x) (ceil(x / kPercision) * kPercision) + +template +const float TimeBasedAvgRing::kPercision = 0.001; + +template +void TimeBasedAvgRing::AddItem(T value, double time) +{ + hsTempMutexLock lock( fLock ); + + if ( fList.empty() ) + { + // initialize with the first time and zero the first value + fList.insert(fList.end(),Item(0.0,time)); + fRingStart = fRingEnd = fList.begin(); + fAvg = (float)value; + } + else + { + // if we're within the percision amount subtract the RingEnd value from total + // and update the RingEnd value by adding the current value to it + if (time - (*fRingEnd).GetTime() <= kPercision) + { + fTotal -= PercisionRoundUp((*fRingEnd).GetValue()); + (*fRingEnd).SetValue((*fRingEnd).GetValue() + value); + } + else + { + // clean up the begining of the ring + //// there can be some precision loss in the loop time calc + //// check to see if the difference is within 1 milli + while (time - (*fRingStart).GetTime() > fLen + kPercision + && fRingStart != fRingEnd) + { + // remove RingStart from the avg part of the average calc + fTotal -= (*fRingStart).GetValue(); + + TimeList::iterator prev = fRingStart++; + + // loop the ring if needed + if (fRingStart == fList.end()) + fRingStart = fList.begin(); + + + // if the new ring start is in the range, interpolate + // and reuse prev + if (time - (*fRingStart).GetTime() < fLen) + { + // remove RingStart from the avg part of the average calc + fTotal -= PercisionRoundUp((*fRingStart).GetValue()); + + // Set up the interp + double remainder = fLen - (time - (*fRingStart).GetTime()); + double timedelta = (*fRingStart).GetTime() - (*prev).GetTime(); + (*prev).SetTime((*fRingStart).GetTime() - remainder); + (*prev).SetValue(0); + // rounding loss occurs here if T is not floting point + double scale = remainder/timedelta; + hsAssert(scale < 1.0 && scale > 0.0,"Interp Scale Out of Bounds"); + (*fRingStart).SetValue((float)((*fRingStart).GetValue() * scale)); + + // add the new value in + fTotal += (*fRingStart).GetValue(); + + // put prev back as ring start + fRingStart = prev; + } + + } + + // zero total & fAvg if we looped or neg + if (fRingStart == fRingEnd || fTotal < 0.0) + { + fTotal = 0.0; + fAvg = 0.0; + } + + // put the new value in the ring by expanding the ring if needed + // or replacing an empty value + fRingEnd++; + if (fRingEnd == fList.end()) + fRingEnd = fList.begin(); + // Do we have free space? + if (fRingEnd == fRingStart) + { + // no free space + fList.insert(fRingEnd,Item(value,time)); + fRingEnd--; + } + else + { + // yes free space @ fRingEnd + (*fRingEnd) = Item(value,time); + } + } + + //update the avg + fTotal += (*fRingEnd).GetValue(); + double currentLen = (*fRingEnd).GetTime() - (*fRingStart).GetTime(); + if (currentLen < 1.0) + fAvg = (float)fTotal; + else + fAvg = (float)(fTotal / currentLen); + } + + // update the max avg + fMaxAvg = hsMaximum( fMaxAvg, fAvg ); + +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plAvg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plAvg.h new file mode 100644 index 00000000..3224122b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plAvg.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_AVG_H +#define PL_AVG_H + +#include "hsConfig.h" +#include "hsThread.h" +#include "hsStlUtils.h" + + +// A Time based Value Averaging class +// implemented in a ring buffer +// Values are averaged over the RingLen +// independant of frame time +// The ring buffer will grow to accomadate +// as many samples as added during the Len +// Only Accurate to kPercision (0.001) of whatever units +// of time are used + +template class TimeBasedAvgRing +{ +/* T must be a simple type + + If T is not a floating point type then rounding + loss may occur when samples are interpolated + at window boundries +*/ +private: + static const float kPercision; + + template class Item + { + private: + T fValue; + double fTime; + public: + Item() { Reset(); } + Item(const T val, const double time) : fValue(val), fTime(time) { } + void Reset() { fValue = 0; fTime = 0; } + void SetValue(const T val) { fValue = val; } + void SetTime(const double time) { fTime = time; } + T GetValue() const { return fValue; } + double GetTime() const { return fTime; } + }; + typedef std::list< Item > TimeList; + typedef typename TimeList::iterator TimeListIterator; // .NET added typename to be C++ ISO compliant - JL + + TimeList fList; + float fLen; // in time + float fAvg; + float fMaxAvg; + double fTotal; + TimeListIterator fRingStart, fRingEnd; + hsMutex fLock; +public: + TimeBasedAvgRing():fLen(0.f),fAvg(0.f),fMaxAvg(0.f),fTotal(0.0) {} + + void SetRingLen(const float len) { fLen = len; } + float GetRingLen() const { return fLen; } + void AddItem(T value, double time); + float GetAvg() const { return fAvg; } + double GetTotal() const { return fTotal; } + float GetMaxAvg() const { return fMaxAvg; } + void ResetMaxAvg() { fMaxAvg=fAvg; } + void Reset() { fRingStart=fRingEnd; ResetMaxAvg(); } +}; + + +#endif // PL_AVG_H + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plRandom.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plRandom.h new file mode 100644 index 00000000..3f449c04 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plRandom.h @@ -0,0 +1,121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRandom_inc +#define plRandom_inc + +// FAST_Q is basically lifted from Numerical Recipes. +// The RandZeroToOne and RandMinusOneToOne were "enhanced" +// a bit, but tested out nicely to generate sequences with +// appropriate maxs, mins, and means. +// FAST_Q depends on IEEE floating point format. Undefine +// it for any platform which doesn't satisfy this, or +// come up with the appropriate constants for that platform. +#define FAST_Q + + +class plRandom +{ +protected: + mutable UInt32 fSeed; +public: + inline float RandNorm() const; + inline int Rand() const; + inline int RandRangeI(int lo, int hi) const; + inline float RandRangeF(float lo, float hi) const; + inline float RandMinusOneToOne() const; + inline float RandZeroToOne() const; + + UInt32 GetSeed() const { return fSeed; } + void SetSeed(int seed) { fSeed = seed; } + + plRandom(int seed = 1) : fSeed(seed) {} +}; + +inline float plRandom::RandNorm() const +{ +#ifndef FAST_Q + return 1.f / 32767.f; +#else // FAST_Q + return (1.f / float(~0UL)); +#endif // FAST_Q +} + +inline int plRandom::Rand() const +{ +#ifndef FAST_Q + register int temp; + fSeed = fSeed * 1103515245 + 12345; + temp = (int)((fSeed/65536)&32767); + return (temp); +#else // FAST_Q + return fSeed = 1664525L * fSeed + 1013904223L; +#endif // FAST_Q +} + +// RandZeroToOne - take our usual random UInt32. +// We're going to mask in an exponent to make it +// a float in range [1..2). Then subtract 1.f to +// make it [0..1). We shift our random UInt32 down +// by 9 because the upper bits are the most random. +inline float plRandom::RandZeroToOne() const +{ +#ifndef FAST_Q + return Rand() * RandNorm(); +#else // FAST_Q + const UInt32 kOneExp = 0x3f800000; + register UInt32 temp = kOneExp | (UInt32(Rand()) >> 9); + return (*(float*)&temp) - 1.f; +#endif // FAST_Q +} + +// RandMinusOneToOne - same as RandZeroToOne, but we +// mask in an exponent putting it in range [2..4), +// then subtract 3 to give [-1..1). +inline float plRandom::RandMinusOneToOne() const +{ +#ifndef FAST_Q + return RandZeroToOne() * 2.f - 1.f; +#else // FAST_Q + const UInt32 kTwoExp = 0x40000000; + register UInt32 temp = kTwoExp | (UInt32(Rand()) >> 9); + return (*(float*)&temp) - 3.f; +#endif // FAST_Q +} + +inline float plRandom::RandRangeF(float lo, float hi) const +{ + return lo + RandZeroToOne() * (hi - lo); +} + +inline int plRandom::RandRangeI(int lo, int hi) const +{ + return lo + int(RandZeroToOne() * float(hi + 1 - lo)); +} + + +#endif // plRandom_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plTriUtils.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plTriUtils.cpp new file mode 100644 index 00000000..927920a6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plTriUtils.cpp @@ -0,0 +1,314 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsGeometry3.h" +#include "plTriUtils.h" + +static const hsScalar kAlmostZero = 1.e-5f; +static const hsScalar kPastZero = -kAlmostZero; +static const hsScalar kPastOne = 1.f + kAlmostZero; +static const hsScalar kAlmostOne = 1.f - kAlmostZero; +static const hsScalar kAlmostZeroSquared = kAlmostZero*kAlmostZero; + +static inline hsVector3 Cross(const hsScalarTriple& p0, const hsScalarTriple& p1) +{ + return hsVector3(p0.fY * p1.fZ - p0.fZ * p1.fY, + p0.fZ * p1.fX - p0.fX * p1.fZ, + p0.fX * p1.fY - p0.fY * p1.fX); +} + + +// There's actually a possibly faster way to do all this. +// The barycentric coordinate in 3-space is the same as the barycentric coordinate of the projection +// in 2-space, as long as the projection doesn't degenerate the triangle (i.e. project the tri onto +// a plane perpindicular to the tri). The tri can't be perpindicular to all three major axes, so by +// picking the right one (or just not picking the wrong one), the lengths of the cross products becomes +// just the z component (e.g. v0.x*v1.y - v0.y*v1.x), so all the square roots go away (not to mention all +// the vector math going from 3 component to 2). +plTriUtils::Bary plTriUtils::ComputeBarycentricProjection(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, hsPoint3&p, hsPoint3& out) +{ + hsVector3 v12(&p1, &p2); + hsVector3 v02(&p0, &p2); + hsVector3 norm = Cross(v12, v02); + + hsScalar invLenSq12 = norm.MagnitudeSquared(); + if( invLenSq12 < kAlmostZero ) + return kDegenerateTri; // degenerate triangle + + invLenSq12 = 1.f / invLenSq12; + + p += norm * (hsVector3(&p2, &p).InnerProduct(norm) * invLenSq12); + + hsVector3 vp2(&p, &p2); + hsVector3 v0 = Cross(v12, vp2); + hsVector3 v1 = Cross(vp2, v02); + + return IComputeBarycentric(norm, invLenSq12, v0, v1, out); +} + +plTriUtils::Bary plTriUtils::ComputeBarycentric(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsPoint3&p, hsPoint3& out) +{ + hsVector3 v12(&p1, &p2); + hsVector3 v02(&p0, &p2); + hsVector3 norm = Cross(v12, v02); + hsScalar invLenSq12 = norm.MagnitudeSquared(); + if( invLenSq12 < kAlmostZero ) + return kDegenerateTri; // degenerate triangle + + invLenSq12 = 1.f / invLenSq12; + + hsVector3 vp2(&p, &p2); + hsVector3 v0 = Cross(v12, vp2); + hsVector3 v1 = Cross(vp2, v02); + + return IComputeBarycentric(norm, invLenSq12, v0, v1, out); +} + + +plTriUtils::Bary plTriUtils::IComputeBarycentric(const hsVector3& v12, hsScalar invLenSq12, const hsVector3& v0, const hsVector3& v1, hsPoint3& out) +{ + UInt32 state = 0; + + hsScalar lenSq0 = v0.MagnitudeSquared(); + if( lenSq0 < kAlmostZeroSquared ) + { + // On edge p1-p2; + out[0] = 0; + state |= kOnEdge12; + } + else + { + out[0] = lenSq0 * invLenSq12; + out[0] = hsSquareRoot(out[0]); + // + if( v0.InnerProduct(v12) < 0 ) + { + out[0] = -out[0]; + state |= kOutsideTri; + } + else if( out[0] > kPastOne ) + state |= kOutsideTri; + else if( out[0] > kAlmostOne ) + state |= kOnVertex0; + } + + hsScalar lenSq1 = v1.MagnitudeSquared(); + if( lenSq1 < kAlmostZeroSquared ) + { + // On edge p0-p2 + out[1] = 0; + state |= kOnEdge02; + } + else + { + out[1] = lenSq1 * invLenSq12; + out[1] = hsSquareRoot(out[1]); + + if( v1.InnerProduct(v12) < 0 ) + { + out[1] = -out[1]; + state |= kOutsideTri; + } + else if( out[1] > kPastOne ) + state |= kOutsideTri; + else if( out[1] > kAlmostOne ) + state |= kOnVertex1; + } + + // Could make more robust against precision problems + // by repeating above for out[2], then normalizing + // so sum(out[i]) = 1.f + out[2] = 1.f - out[0] - out[1]; + + if( out[2] < kPastZero ) + state |= kOutsideTri; + else if( out[2] < kAlmostZero ) + state |= kOnEdge01; + else if( out[2] > kAlmostOne ) + state |= kOnVertex2; + + /* + if( a,b,c outside range [0..1] ) + p is outside tri; + else if( a,b,c == 1 ) + p is on vert; + else if( a,b,c == 0 ) + p is on edge; + */ + + if( state & kOutsideTri ) + return kOutsideTri; + + if( state & kOnVertex ) + return Bary(state & kOnVertex); + + if( state & kOnEdge ) + return Bary(state & kOnEdge); + + return kInsideTri; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +int plTriUtils::ISelectAxis(const hsVector3& norm) +{ + int retVal = -2; + hsScalar maxDim = 0; + int i; + for( i = 0; i < 3; i++ ) + { + if( norm[i] > maxDim ) + { + maxDim = norm[i]; + retVal = i; + } + else if( -norm[i] > maxDim ) + { + maxDim = -norm[i]; + retVal = i; + } + } + return retVal; +} + +hsBool plTriUtils::IFastBarycentric(int iAx, const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsPoint3&p, hsPoint3& out) +{ + if( --iAx < 0 ) + iAx = 2; + int jAx = iAx - 1; + if( jAx < 0 ) + jAx = 2; + + hsVector3 v02(&p0, &p2); + hsVector3 v12(&p1, &p2); + + hsScalar totArea = v02[iAx] * v12[jAx] - v02[jAx] * v12[iAx]; + hsAssert(totArea != 0, "Should have already filtered degerate tris and degenerate projection"); + + hsScalar invTotArea = 1.f / totArea; + + hsVector3 vp2(&p, &p2); + + hsScalar aArea = vp2[iAx] * v12[jAx] - vp2[jAx] * v12[iAx]; + + hsScalar bArea = v02[iAx] * vp2[jAx] - v02[jAx] * vp2[iAx]; + + out[0] = aArea * invTotArea; + out[1] = bArea * invTotArea; + out[2] = 1.f - out[0] - out[1]; + + return true; +} + +hsBool plTriUtils::FastBarycentricProjection(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, hsPoint3&p, hsPoint3& out) +{ + hsVector3 v02(&p0, &p2); + hsVector3 v12(&p1, &p2); + + hsVector3 norm = Cross(v12, v02); + hsScalar invLenSq12 = norm.MagnitudeSquared(); + if( invLenSq12 < kAlmostZero ) + return false; // degenerate triangle + + invLenSq12 = 1.f / invLenSq12; + + hsVector3 del(&p0, &p); + hsScalar delDotNormOverLenSq = del.InnerProduct(norm) * invLenSq12; + + p += norm * delDotNormOverLenSq; + + int iAx = ISelectAxis(norm); + hsAssert(iAx >= 0, "Should have already picked out degenerate tris"); + + return IFastBarycentric(iAx, p0, p1, p2, p, out); +} + +hsBool plTriUtils::FastBarycentric(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsPoint3&p, hsPoint3& out) +{ + hsVector3 v02(&p0, &p2); + hsVector3 v12(&p1, &p2); + int iAx = ISelectAxis(Cross(v12, v02)); + if( iAx < 0 ) + return false; + return IFastBarycentric(iAx, p0, p1, p2, p, out); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////// +hsBool plTriUtils::ProjectOntoPlane(const hsVector3& norm, hsScalar dist, hsPoint3& p) +{ + hsScalar normMagSq = norm.MagnitudeSquared(); + if( normMagSq > kAlmostZero ) + { + dist /= normMagSq; + + p += norm * dist; + + return true; + } + return false; +} + +hsBool plTriUtils::ProjectOntoPlane(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, hsPoint3& p) +{ + hsVector3 v02(&p0, &p2); + hsVector3 v12(&p1, &p2); + + hsVector3 norm = v12 % v02; + + hsScalar dist = norm.InnerProduct(p0 - p); + + return ProjectOntoPlane(norm, dist, p); +} + +hsBool plTriUtils::ProjectOntoPlaneAlongVector(const hsVector3& norm, hsScalar dist, const hsVector3& vec, hsPoint3& p) +{ + hsScalar s = norm.InnerProduct(vec); + const hsScalar kAlmostZero = 1.e-5f; + if( (s > kAlmostZero)||(s < kPastZero) ) + { + dist /= s; + + p += vec * dist; + + return true; + } + return false; +} + +hsBool plTriUtils::ProjectOntoPlaneAlongVector(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsVector3& vec, hsPoint3& p) +{ + hsVector3 v02(&p0, &p2); + hsVector3 v12(&p1, &p2); + + hsVector3 norm = v12 % v02; + hsScalar dist = norm.InnerProduct(p0 - p); + + return ProjectOntoPlaneAlongVector(norm, dist, vec, p); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plTriUtils.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plTriUtils.h new file mode 100644 index 00000000..fb229e6a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMath/plTriUtils.h @@ -0,0 +1,71 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plTriUtils_inc +#define plTriUtils_inc + +class plTriUtils +{ +public: + enum Bary { + kInsideTri = 0x0, + kOnVertex0 = 0x1, + kOnVertex1 = 0x2, + kOnVertex2 = 0x4, + kOnVertex = kOnVertex0 | kOnVertex1 | kOnVertex2, + kOnEdge01 = 0x8, + kOnEdge12 = 0x10, + kOnEdge02 = 0x20, + kOnEdge = kOnEdge01 | kOnEdge12 | kOnEdge02, + + kOutsideTri = 0x100, + + kDegenerateTri = 0x200, + }; + + +protected: + Bary IComputeBarycentric(const hsVector3& v12, hsScalar invLenSq12, const hsVector3& v0, const hsVector3& v1, hsPoint3& out); + + int ISelectAxis(const hsVector3& norm); + hsBool IFastBarycentric(int iAx, const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsPoint3&p, hsPoint3& out); +public: + + Bary ComputeBarycentricProjection(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, hsPoint3&p, hsPoint3& out); + Bary ComputeBarycentric(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsPoint3&p, hsPoint3& out); + + + hsBool FastBarycentricProjection(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, hsPoint3&p, hsPoint3& out); + hsBool FastBarycentric(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsPoint3&p, hsPoint3& out); + + hsBool ProjectOntoPlane(const hsVector3& norm, hsScalar dist, hsPoint3& p); + hsBool ProjectOntoPlane(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, hsPoint3& p); + hsBool ProjectOntoPlaneAlongVector(const hsVector3& norm, hsScalar dist, const hsVector3& vec, hsPoint3& p); + hsBool ProjectOntoPlaneAlongVector(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& p2, const hsVector3& vec, hsPoint3& p); + +}; + +#endif // plTriUtils_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAIMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAIMsg.cpp new file mode 100644 index 00000000..4ad3d501 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAIMsg.cpp @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef SERVER // we use stuff the server doesn't link with +#ifndef NO_AV_MSGS + +#include "plAIMsg.h" + +#include "hsResMgr.h" +#include "hsStream.h" + +#include "..\plAvatar\plArmatureMod.h" + +/////////////////////////////////////////////////////////////////////////////// + +plAIMsg::plAIMsg(): plMessage(nil, nil, nil), fBrainUserStr("") +{} + +plAIMsg::plAIMsg(const plKey& sender, const plKey& receiver): plMessage(sender, receiver, nil) +{ + // set up our user string from the sender, if it is the right type + plArmatureMod* armMod = plArmatureMod::ConvertNoRef(sender->ObjectIsLoaded()); + if (armMod) + fBrainUserStr = armMod->GetUserStr(); + else + fBrainUserStr = ""; +} + +void plAIMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + char* temp = stream->ReadSafeString(); + if (temp) + fBrainUserStr = temp; + delete [] temp; +} + +void plAIMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + stream->WriteSafeString(fBrainUserStr.c_str()); +} + +/////////////////////////////////////////////////////////////////////////////// + +void plAIArrivedAtGoalMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plAIMsg::Read(stream, mgr); + fGoal.Read(stream); +} + +void plAIArrivedAtGoalMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plAIMsg::Write(stream, mgr); + fGoal.Write(stream); +} + +#endif // NO_AV_MSGS +#endif // SERVER \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAIMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAIMsg.h new file mode 100644 index 00000000..e78817e4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAIMsg.h @@ -0,0 +1,104 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAIMsg_inc +#define plAIMsg_inc + +#ifndef SERVER // we use stuff the server doesn't link with +#ifndef NO_AV_MSGS + +#include "hsGeometry3.h" +#include "../pnMessage/plMessage.h" + +class plAvBrainCritter; + +// abstract base class for all AI-related messages +class plAIMsg : public plMessage +{ +public: + plAIMsg(); + plAIMsg(const plKey& sender, const plKey& receiver); + + CLASSNAME_REGISTER(plAIMsg); + GETINTERFACE_ANY(plAIMsg, plMessage); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void BrainUserString(const std::string& userStr) {fBrainUserStr = userStr;} + std::string BrainUserString() const {return fBrainUserStr;} + + // enum for all messages to make things easier for people that use us + enum + { + kAIMsg_Unknown, + kAIMsg_BrainCreated, + kAIMsg_ArrivedAtGoal, + }; + +private: + std::string fBrainUserStr; +}; + +// message spammed to anyone listening so they can grab the brain's key and talk to it +// does NOT get net-propped +class plAIBrainCreatedMsg : public plAIMsg +{ +public: + plAIBrainCreatedMsg(): plAIMsg() {SetBCastFlag(plMessage::kBCastByExactType);} + plAIBrainCreatedMsg(const plKey& sender): plAIMsg(sender, nil) {SetBCastFlag(plMessage::kBCastByExactType);} + + CLASSNAME_REGISTER(plAIBrainCreatedMsg); + GETINTERFACE_ANY(plAIBrainCreatedMsg, plAIMsg); + + virtual void Read(hsStream* stream, hsResMgr* mgr) {plAIMsg::Read(stream, mgr);} + virtual void Write(hsStream* stream, hsResMgr* mgr) {plAIMsg::Write(stream, mgr);} +}; + +// message sent when the brain arrives at it's specified goal +// does NOT get net-propped +class plAIArrivedAtGoalMsg : public plAIMsg +{ +public: + plAIArrivedAtGoalMsg(): plAIMsg(), fGoal(0, 0, 0) {} + plAIArrivedAtGoalMsg(const plKey& sender, const plKey& receiver): plAIMsg(sender, receiver), fGoal(0, 0, 0) {} + + CLASSNAME_REGISTER(plAIArrivedAtGoalMsg); + GETINTERFACE_ANY(plAIArrivedAtGoalMsg, plAIMsg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void Goal(hsPoint3 goal) {fGoal = goal;} + hsPoint3 Goal() const {return fGoal;} + +private: + hsPoint3 fGoal; +}; + +#endif // NO_AV_MSGS +#endif // SERVER + +#endif // plAIMsg_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAccountUpdateMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAccountUpdateMsg.cpp new file mode 100644 index 00000000..71602701 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAccountUpdateMsg.cpp @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAccountUpdateMsg.h" +#include "hsStream.h" + +plAccountUpdateMsg::plAccountUpdateMsg() +{ + fUpdateType = 0; +} + +plAccountUpdateMsg::plAccountUpdateMsg(unsigned updateType) +{ + fUpdateType = updateType; +} + +void plAccountUpdateMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + fUpdateType = stream->ReadSwap32(); + fResult = stream->ReadSwap32(); + fPlayerInt = stream->ReadSwap32(); +} + +void plAccountUpdateMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwap32(fUpdateType); + stream->WriteSwap32(fResult); + stream->WriteSwap32(fPlayerInt); +} + +unsigned plAccountUpdateMsg::GetUpdateType() +{ + return fUpdateType; +} + +void plAccountUpdateMsg::SetUpdateType(unsigned type) +{ + fUpdateType = type; +} + +unsigned plAccountUpdateMsg::GetResult() +{ + return fResult; +} + +void plAccountUpdateMsg::SetResult(unsigned result) +{ + fResult = result; +} + +unsigned plAccountUpdateMsg::GetPlayerInt() +{ + return fPlayerInt; +} + +void plAccountUpdateMsg::SetPlayerInt(unsigned playerInt) +{ + fPlayerInt = playerInt; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAccountUpdateMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAccountUpdateMsg.h new file mode 100644 index 00000000..172cd671 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAccountUpdateMsg.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAccountUpdateMsg_inc +#define plAccountUpdateMsg_inc + +#include "../pnMessage/plMessage.h" + +class hsStream; +class hsResMgr; + +class plAccountUpdateMsg : public plMessage +{ +public: + // If you update this enum, please update the python enum + // located at the bottom of cyAccountManagementGlue.cpp + enum + { + kCreatePlayer = 1, + kDeletePlayer, + kUpgradePlayer, + kActivePlayer, + kChangePassword, + }; + + plAccountUpdateMsg(); + plAccountUpdateMsg(unsigned updateType); + + CLASSNAME_REGISTER( plAccountUpdateMsg ); + GETINTERFACE_ANY( plAccountUpdateMsg, plMessage ); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + unsigned GetUpdateType(); + void SetUpdateType(unsigned type); + + unsigned GetResult(); + void SetResult(unsigned result); + + unsigned GetPlayerInt(); + void SetPlayerInt(unsigned playerInt); + +private: + unsigned fUpdateType; + unsigned fResult; + unsigned fPlayerInt; +}; + +#endif // plAccountUpdateMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plActivatorMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plActivatorMsg.h new file mode 100644 index 00000000..a52e3eb0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plActivatorMsg.h @@ -0,0 +1,96 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plActivatorMsg_inc +#define plActivatorMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsGeometry3.h" + +class hsStream; +class hsResMgr; + + +class plActivatorMsg : public plMessage +{ + + void IReset() { fPickedObj=fHiteeObj=fHitterObj=nil; fTriggerType=0; fHitPoint.Set(0,0,0); } +public: + plActivatorMsg() { IReset(); } + plActivatorMsg(const plKey &s, + const plKey &r, + const double* t) { IReset(); } + ~plActivatorMsg() { } + + CLASSNAME_REGISTER( plActivatorMsg ); + GETINTERFACE_ANY( plActivatorMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fTriggerType = stream->ReadSwap32(); + fHitPoint.Read(stream); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwap32( fTriggerType ); + fHitPoint.Write(stream); + } + + hsBool TriggerType() { return fTriggerType; } + void SetTriggerType(int n) { fTriggerType = n; } + + enum + { + kUndefined = 0, + kPickedTrigger, + kUnPickedTrigger, + kCollideEnter, + kCollideExit, + kCollideContact, + kCollideUnTrigger, + kRoomEntryTrigger, + kLogicMod, + kVolumeEnter, + kVolumeExit, + kEnterUnTrigger, + kExitUnTrigger, + }; + + UInt32 fTriggerType; + plKey fPickedObj; + plKey fHiteeObj; + plKey fHitterObj; + + hsPoint3 fHitPoint; // currently only useful for Picked events + + +}; + +#endif // plActivatorMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAgeLoadedMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAgeLoadedMsg.h new file mode 100644 index 00000000..758238b3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAgeLoadedMsg.h @@ -0,0 +1,102 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAgeLoadedMsg_INC +#define plAgeLoadedMsg_INC + +#include "../pnUtils/pnUtils.h" +#include "../pnMessage/plMessage.h" + +// +// A msg sent locally when pending pages are done loading or unloading. +// +class hsResMgr; +class hsStream; +class plAgeLoadedMsg : public plMessage +{ +public: + CLASSNAME_REGISTER( plAgeLoadedMsg ); + GETINTERFACE_ANY( plAgeLoadedMsg, plMessage ); + + plAgeLoadedMsg() : fLoaded(true) { SetBCastFlag(kBCastByType); } + + // True if the pages finished loading, false if they finished unloading + bool fLoaded; + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); fLoaded = stream->Readbool(); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); stream->Writebool(fLoaded); } +}; + +// A msg sent locally when panding pages are done loaded and it's now ok to join the game +class plAgeLoaded2Msg : public plMessage +{ +public: + CLASSNAME_REGISTER( plAgeLoaded2Msg ); + GETINTERFACE_ANY( plAgeLoaded2Msg, plMessage ); + + plAgeLoaded2Msg() { SetBCastFlag(kBCastByType); } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); } +}; + +// +// A msg sent locally when pending pages are beginning to load or unload. +// +class plAgeBeginLoadingMsg : public plMessage +{ +public: + CLASSNAME_REGISTER(plAgeBeginLoadingMsg); + GETINTERFACE_ANY(plAgeBeginLoadingMsg, plMessage); + + plAgeBeginLoadingMsg() : fLoading(true) { SetBCastFlag(kBCastByType); } + + // True if the pages are starting to load, false if they are starting to unload + bool fLoading; + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); fLoading = stream->Readbool(); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); stream->Writebool(fLoading); } +}; + +// +// A msg sent locally when initial age state has been loaded +// +class plInitialAgeStateLoadedMsg : public plMessage +{ +public: + CLASSNAME_REGISTER( plInitialAgeStateLoadedMsg ); + GETINTERFACE_ANY( plInitialAgeStateLoadedMsg, plMessage ); + + plInitialAgeStateLoadedMsg() { SetBCastFlag(kBCastByType); } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); } +}; + +#endif // plAgeLoadedMsg diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAngularVelocityMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAngularVelocityMsg.h new file mode 100644 index 00000000..925e90cc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAngularVelocityMsg.h @@ -0,0 +1,42 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../../NucleusLib/pnMessage/plSimulationMsg.h" +#include "../../CoreLib/hsGeometry3.h" +class plAngularVelocityMsg : public plSimulationMsg +{ +public: + // pass-through constructors + plAngularVelocityMsg() : plSimulationMsg() {}; + plAngularVelocityMsg (const plKey &sender, const plKey &receiver, const double *time) + : plSimulationMsg(sender,receiver, time), fAngularVelocity(0.0f,0.0f,0.0f){}; + CLASSNAME_REGISTER( plAngularVelocityMsg ); + GETINTERFACE_ANY( plAngularVelocityMsg , plSimulationMsg); + void AngularVelocity(hsVector3& vel){fAngularVelocity=vel;} + const hsVector3& AngularVelocity(){return fAngularVelocity;} +protected: + hsVector3 fAngularVelocity; + +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimCmdMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimCmdMsg.cpp new file mode 100644 index 00000000..8720ca93 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimCmdMsg.cpp @@ -0,0 +1,178 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plAnimCmdMsg.h" +#include "hsStream.h" + +plAnimCmdMsg::~plAnimCmdMsg() +{ + ClearCmd(); + delete [] fAnimName; + delete [] fLoopName; +} + + +void plAnimCmdMsg::ClearCmd() +{ + plMessageWithCallbacks::Clear(); + fCmd.Clear(); +} + +void plAnimCmdMsg::SetAnimName(const char *name) +{ + delete [] fAnimName; + fAnimName = hsStrcpy(name); +} + +const char *plAnimCmdMsg::GetAnimName() +{ + return fAnimName; +} + +void plAnimCmdMsg::SetLoopName(const char *name) +{ + delete [] fLoopName; + fLoopName = hsStrcpy(name); +} + +const char *plAnimCmdMsg::GetLoopName() +{ + return fLoopName; +} + +hsBool plAnimCmdMsg::CmdChangesAnimTime() +{ + return (Cmd(kContinue) || + Cmd(kStop) || + Cmd(kGoToTime) || + Cmd(kToggleState) || + Cmd(kGoToBegin) || + Cmd(kGoToEnd) || + Cmd(kGoToLoopBegin) || + Cmd(kGoToLoopEnd) || + Cmd(kIncrementForward) || + Cmd(kIncrementBackward) || + Cmd(kFastForward) || + Cmd(kPlayToTime) || + Cmd(kPlayToPercentage)); +} + +void plAnimCmdMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessageWithCallbacks::Read(stream, mgr); + + fCmd.Read(stream); + stream->ReadSwap(&fBegin); + stream->ReadSwap(&fEnd); + stream->ReadSwap(&fLoopEnd); + stream->ReadSwap(&fLoopBegin); + stream->ReadSwap(&fSpeed); + stream->ReadSwap(&fSpeedChangeRate); + stream->ReadSwap(&fTime); + + fAnimName = stream->ReadSafeString(); + fLoopName = stream->ReadSafeString(); +} + +void plAnimCmdMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessageWithCallbacks::Write(stream, mgr); + + fCmd.Write(stream); + stream->WriteSwap(fBegin); + stream->WriteSwap(fEnd); + stream->WriteSwap(fLoopEnd); + stream->WriteSwap(fLoopBegin); + stream->WriteSwap(fSpeed); + stream->WriteSwap(fSpeedChangeRate); + stream->WriteSwap(fTime); + + stream->WriteSafeString(fAnimName); + stream->WriteSafeString(fLoopName); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +plAGCmdMsg::~plAGCmdMsg() +{ + ClearCmd(); + delete [] fAnimName; +} + +void plAGCmdMsg::SetAnimName(const char *name) +{ + delete [] fAnimName; + fAnimName = hsStrcpy(name); +} + +const char *plAGCmdMsg::GetAnimName() +{ + return fAnimName; +} + +void plAGCmdMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + fCmd.Read(stream); + stream->ReadSwap(&fBlend); + stream->ReadSwap(&fBlendRate); + stream->ReadSwap(&fAmp); + stream->ReadSwap(&fAmpRate); + + fAnimName = stream->ReadSafeString(); +} + +void plAGCmdMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + fCmd.Write(stream); + stream->WriteSwap(fBlend); + stream->WriteSwap(fBlendRate); + stream->WriteSwap(fAmp); + stream->WriteSwap(fAmpRate); + + stream->WriteSafeString(fAnimName); +} + +///////////////////////////////////////////////////////////////////////////////////// + +plAGDetachCallbackMsg::~plAGDetachCallbackMsg() +{ + delete [] fAnimName; +} + +void plAGDetachCallbackMsg::SetAnimName(const char *name) +{ + fAnimName = hsStrcpy(name); +} + +char *plAGDetachCallbackMsg::GetAnimName() +{ + return fAnimName; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimCmdMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimCmdMsg.h new file mode 100644 index 00000000..601f43eb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimCmdMsg.h @@ -0,0 +1,206 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAnimCmdMsg_inc +#define plAnimCmdMsg_inc + +#include "../pnMessage/plMessageWithCallbacks.h" +#include "hsBitVector.h" +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "../plInterp/plAnimEaseTypes.h" +#include "../plInterp/plAnimTimeConvert.h" + +class plAGAnimInstance; + +class plAnimCmdMsg : public plMessageWithCallbacks +{ +protected: + char *fAnimName; + char *fLoopName; + +private: + void IInit() { fBegin=fEnd=fLoopBegin=fLoopEnd=fSpeed=fSpeedChangeRate=fTime=0; fAnimName=fLoopName=nil;} +public: + plAnimCmdMsg() + : plMessageWithCallbacks(nil, nil, nil) { IInit(); } + plAnimCmdMsg(const plKey &s, + const plKey &r, + const double* t) + : plMessageWithCallbacks(s, r, t) { IInit(); } + virtual ~plAnimCmdMsg(); + + CLASSNAME_REGISTER( plAnimCmdMsg ); + GETINTERFACE_ANY( plAnimCmdMsg, plMessageWithCallbacks ); + + // When adding a command, add a check for it in CmdChangesAnimTime if appropriate + enum ModCmds + { + kContinue=0, + kStop, + kSetLooping, + kUnSetLooping, + kSetBegin, + kSetEnd, + kSetLoopEnd, + kSetLoopBegin, + kSetSpeed, + kGoToTime, + kSetBackwards, + kSetForewards, + kToggleState, + kAddCallbacks, + kRemoveCallbacks, + kGoToBegin, + kGoToEnd, + kGoToLoopBegin, + kGoToLoopEnd, + kIncrementForward, + kIncrementBackward, + kRunForward, + kRunBackward, + kPlayToTime, + kPlayToPercentage, + kFastForward, + kGoToPercent, + kNumCmds, + }; + + hsBitVector fCmd; + + hsBool Cmd(int n) const { return fCmd.IsBitSet(n); } + void SetCmd(int n) { fCmd.SetBit(n); } + void ClearCmd(); + void SetAnimName(const char *name); + const char *GetAnimName(); + hsBool CmdChangesAnimTime(); // Will this command cause an update to the current anim time? + + void SetLoopName(const char *name); + const char *GetLoopName(); + + hsScalar fBegin; + hsScalar fEnd; + hsScalar fLoopEnd; + hsScalar fLoopBegin; + hsScalar fSpeed; + hsScalar fSpeedChangeRate; + hsScalar fTime; + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +// plAnimCmdMsg is intented for animation commands sent to a plAnimTimeConvert. Commands that only apply to the +// AG (Animation Graph) system go here. + +class plAGCmdMsg : public plMessage +{ +protected: + char *fAnimName; + +private: + void IInit() { fBlend = fAmp = 0; + fAnimName=nil;} +public: + plAGCmdMsg() + : plMessage(nil, nil, nil) { IInit(); } + plAGCmdMsg(const plKey &s, + const plKey &r, + const double* t) + : plMessage(s, r, t) { IInit(); } + virtual ~plAGCmdMsg(); + + CLASSNAME_REGISTER( plAGCmdMsg ); + GETINTERFACE_ANY( plAGCmdMsg, plMessage ); + + enum ModCmds + { + kSetBlend, + kSetAmp, + kSetAnimTime, + }; + + hsBitVector fCmd; + + hsBool Cmd(int n) const { return fCmd.IsBitSet(n); } + void SetCmd(int n) { fCmd.SetBit(n); } + void ClearCmd() { fCmd.Clear(); } + void SetAnimName(const char *name); + const char *GetAnimName(); + + hsScalar fBlend; + hsScalar fBlendRate; + hsScalar fAmp; + hsScalar fAmpRate; + hsScalar fAnimTime; + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +class plAGInstanceCallbackMsg : public plEventCallbackMsg +{ +public: + plAGInstanceCallbackMsg() : plEventCallbackMsg(), fInstance(nil) {} + plAGInstanceCallbackMsg(plKey receiver, CallbackEvent e, int idx=0, hsScalar t=0, Int16 repeats=-1, UInt16 user=0) : + plEventCallbackMsg(receiver, e, idx, t, repeats, user), fInstance(nil) {} + + CLASSNAME_REGISTER( plAGInstanceCallbackMsg ); + GETINTERFACE_ANY( plAGInstanceCallbackMsg, plEventCallbackMsg ); + + // These aren't meant to go across the net, so no IO necessary. + void Read(hsStream* stream, hsResMgr* mgr) {} + void Write(hsStream* stream, hsResMgr* mgr) {} + + plAGAnimInstance *fInstance; +}; + +class plAGDetachCallbackMsg : public plEventCallbackMsg +{ +protected: + char *fAnimName; + +public: + plAGDetachCallbackMsg() : plEventCallbackMsg(), fAnimName(nil) {} + plAGDetachCallbackMsg(plKey receiver, CallbackEvent e, int idx=0, hsScalar t=0, Int16 repeats=-1, UInt16 user=0) : + plEventCallbackMsg(receiver, e, idx, t, repeats, user), fAnimName(nil) {} + virtual ~plAGDetachCallbackMsg(); + + CLASSNAME_REGISTER( plAGDetachCallbackMsg ); + GETINTERFACE_ANY( plAGDetachCallbackMsg, plEventCallbackMsg ); + + // These aren't meant to go across the net, so no IO necessary. + void Read(hsStream* stream, hsResMgr* mgr) {} + void Write(hsStream* stream, hsResMgr* mgr) {} + + void SetAnimName(const char *name); + char *GetAnimName(); +}; + + +#endif // plAnimCmdMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimationEventCallbackMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimationEventCallbackMsg.h new file mode 100644 index 00000000..73304b9d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAnimationEventCallbackMsg.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAnimationEventCallbackMsg_inc +#define plAnimationEventCallbackMsg_inc + +#include "../pnMessage/plMessage.h" + +enum AnimationEvent +{ + kAnimStart = 0, + kAnimStop, + kAnimReverse, + kAnimTime, + kAnimEventEnd +}; + +class plAnimationEventCallbackMsg : public plMessage +{ +protected: + +public: + AnimationEvent fEvent; + hsScalar fEventTime; + + plAnimationEventCallbackMsg(){;} + plAnimationEventCallbackMsg(const plKey* s, + const plKey* r, + const double* t){;} + ~plAnimationEventCallbackMsg(){;} + + CLASSNAME_REGISTER( plAnimationEventCallbackMsg ); + GETINTERFACE_ANY( plAnimationEventCallbackMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); } +}; + + +#endif // plAnimationEventCallbackMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plApplyAvatarCustomizationsMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plApplyAvatarCustomizationsMsg.h new file mode 100644 index 00000000..d4778d78 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plApplyAvatarCustomizationsMsg.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plApplyAvatarCustomizationsMsg_h +#define plApplyAvatarCustomizationsMsg_h + +#error + +#include "../pnMessage/plMessage.h" +#include "../plNetCommon/plNetAvatarVault.h" +#include "hsResMgr.h" + + +class plApplyAvatarCustomizationsMsg : public plMessage +{ +public: + CLASSNAME_REGISTER( plApplyAvatarCustomizationsMsg ); + GETINTERFACE_ANY( plApplyAvatarCustomizationsMsg, plMessage ); + + void SetCustomizations(const plPlayerCustomizations * value) { fCustomizations=*value;} + const plPlayerCustomizations * GetCustomizations() const { return &fCustomizations;} + void SetAvatarKey(plKey * key) { fAvatarKey=key;} + const plKey * GetAvatarKey() const { return fAvatarKey;} + + void Read(hsStream * stream, hsResMgr * mgr) + { + plMessage::IMsgRead(stream, mgr); + fCustomizations.Read(stream); + fAvatarKey = mgr->ReadKey(stream); + } + + void Write(hsStream * stream, hsResMgr * mgr) + { + plMessage::IMsgWrite(stream, mgr); + fCustomizations.Write(stream); + mgr->WriteKey(stream,fAvatarKey); + } +private: + plPlayerCustomizations fCustomizations; + plKey * fAvatarKey; +}; + + +#endif //plApplyAvatarCustomizationsMsg_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plApplyStoredAvatarSettingsMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plApplyStoredAvatarSettingsMsg.h new file mode 100644 index 00000000..e754f7bd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plApplyStoredAvatarSettingsMsg.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plApplyStoredAvatarSettingsMsg_h +#define plApplyStoredAvatarSettingsMsg_h + +#include "../pnMessage/plMessage.h" +#include "../plNetCommon/plNetAvatarVault.h" +#include "hsResMgr.h" + + +class plApplyStoredAvatarSettingsMsg : public plMessage +{ +public: + CLASSNAME_REGISTER( plApplyStoredAvatarSettingsMsg ); + GETINTERFACE_ANY( plApplyStoredAvatarSettingsMsg, plMessage ); + + void SetSettings(const plStoredAvatarSettings & settings) { fSettings=settings;} + const plStoredAvatarSettings & GetSettings() const { return fSettings;} + const plKey * GetAvatarKey() const { return fAvatarKey;} + void SetAvatarKey(plKey * key) { fAvatarKey=key;} + + void Read(hsStream * stream, hsResMgr * mgr) + { + plMessage::IMsgRead(stream, mgr); + fSettings.Read(stream); + fAvatarKey = mgr->ReadKey(stream); + } + + void Write(hsStream * stream, hsResMgr * mgr) + { + plMessage::IMsgWrite(stream, mgr); + fSettings.Write(stream); + mgr->WriteKey(stream,fAvatarKey); + } +private: + plStoredAvatarSettings fSettings; + plKey * fAvatarKey; +}; + + +#endif //plApplyStoredAvatarSettingsMsg_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvCoopMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvCoopMsg.cpp new file mode 100644 index 00000000..ca8f64db --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvCoopMsg.cpp @@ -0,0 +1,112 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef SERVER +#ifndef NO_AV_MSGS + +// singular +#include "plAvCoopMsg.h" + +// global +#include "hsStream.h" +#include "hsResMgr.h" + +// other +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plCoopCoordinator.h" + +// plAvCoopMsg ----------- +// ------------ +plAvCoopMsg::plAvCoopMsg() +: plMessage(nil, nil, nil), + fInitiatorID(0), + fInitiatorSerial(0), + fCommand(kNone), + fCoordinator(nil) +{ +} + +// plAvCoopMsg ------------ +// ------------ +plAvCoopMsg::~plAvCoopMsg() +{ +} + +// plAvCoopMsg ----------------------------------- +// ------------ +plAvCoopMsg::plAvCoopMsg(Command cmd, UInt32 id, UInt16 serial) +: plMessage(nil, plAvatarMgr::GetInstance()->GetKey(), nil), + fInitiatorID(id), + fInitiatorSerial(serial), + fCommand(cmd), + fCoordinator(nil) +{ + +} + +// plAvCoopMsg ---------------------------------- +// ------------ +plAvCoopMsg::plAvCoopMsg(plKey sender, plCoopCoordinator *coordinator) +: plMessage(sender, plAvatarMgr::GetInstance()->GetKey(), nil), + fInitiatorID(coordinator->GetInitiatorID()), + fInitiatorSerial(coordinator->GetInitiatorSerial()), + fCommand(kStartNew), + fCoordinator(coordinator) +{ +} + +// Read ----------------------------------------------- +// ----- +void plAvCoopMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgRead(stream, mgr); + + if(stream->Readbool()) + fCoordinator = reinterpret_cast(mgr->ReadCreatable(stream)); + + fInitiatorID = stream->ReadSwap32(); + fInitiatorSerial = stream->ReadSwap16(); + + fCommand = static_cast(stream->ReadSwap16()); +} + +// Write ----------------------------------------------- +// ------ +void plAvCoopMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + stream->Writebool(fCoordinator != nil); + if(fCoordinator) + mgr->WriteCreatable(stream, fCoordinator); + + stream->WriteSwap32(fInitiatorID); + stream->WriteSwap16(fInitiatorSerial); + + stream->WriteSwap16(fCommand); +} + +#endif // ndef NO_AV_MSGS +#endif // ndef SERVER diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvCoopMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvCoopMsg.h new file mode 100644 index 00000000..07dfaca9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvCoopMsg.h @@ -0,0 +1,115 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef NO_AV_MSGS +#ifndef SERVER + + +#ifndef plAvCoopMsg_inc +#define plAvCoopMsg_inc + +///////////////////////////////////////////////////////////////////////////////////////// +// +// INCLUDES +// +///////////////////////////////////////////////////////////////////////////////////////// +#include "../pnMessage/plMessage.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// +// FORWARDS +// +///////////////////////////////////////////////////////////////////////////////////////// + +class plCoopCoordinator; + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEFINITIONS +// +///////////////////////////////////////////////////////////////////////////////////////// + +/** plAvCoopMsg ------------------------------------------------------------------------- + These are always sent to the avatar manager, which sends the appropriate things to + the other avatar. + A possible future optimization would be to send them only to the involved players. +*/ +class plAvCoopMsg : public plMessage +{ +public: + + ///////////////////////////////////////////////////////////////////////////////////// + // + // TYPES + // + ///////////////////////////////////////////////////////////////////////////////////// + + enum Command { + kNone, + kStartNew, + kGuestAccepted, + kGuestSeeked, + kGuestSeekAbort, + kCommandSize = 0xffff + }; + + ///////////////////////////////////////////////////////////////////////////////////// + // + // INTERFACE + // + ///////////////////////////////////////////////////////////////////////////////////// + + // constructors + plAvCoopMsg(); + plAvCoopMsg(Command cmd, UInt32 id, UInt16 serial); + plAvCoopMsg(plKey sender, plCoopCoordinator *coordinateur); + ~plAvCoopMsg(); + + // rtti + CLASSNAME_REGISTER( plAvCoopMsg ); + GETINTERFACE_ANY( plAvCoopMsg, plMessage); + + // i/o + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + ///////////////////////////////////////////////////////////////////////////////////// + // + // PUBLIC DATA MEMBERS + // + ///////////////////////////////////////////////////////////////////////////////////// + + UInt32 fInitiatorID; + UInt16 fInitiatorSerial; + + Command fCommand; + + plCoopCoordinator *fCoordinator; // optional +}; + +#endif + +#endif // ndef SERVER +#endif // ndef NO_AV_MSGS diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarFootMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarFootMsg.cpp new file mode 100644 index 00000000..c6aea661 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarFootMsg.cpp @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plAvatarFootMsg.h" + +#include "hsStream.h" + +plAvatarFootMsg::plAvatarFootMsg() +: fArmature(nil), + fIsLeft(false); +{ +} + +plAvatarFootMsg::plAvatarFootMsg(const plKey& s, plArmatureMod *armature, plAvBrain *brain, hsBool isLocal, hsBool isLeft) +: plArmatureUpdateMsg(s, isLocal, true, armature, brain), + fIsLeft(isLeft) +{ + SetBCastFlag(plMessage::kBCastByExactType); +} + +void plAvatarFootMsg::Read(hsStream* s, hsResMgr* mgr) +{ + hsAssert(false, "This message is not supposed to travel over the network or persist in a file."); +} + +void plAvatarFootMsg::Write(hsStream* s, hsResMgr* mgr) +{ + hsAssert(false, "This message is not supposed to travel over the network or persist in a file."); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarFootMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarFootMsg.h new file mode 100644 index 00000000..d4b0e78f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarFootMsg.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAvatarFootMsg_inc +#define plAvatarFootMsg_inc + +#include "../pnMessage/plEventCallbackMsg.h" + +class hsStream; +class hsResMgr; +class plArmatureMod; +class plAvBrain; + +class plAvatarFootMsg : public plEventCallbackMsg +{ +protected: + hsBool fIsLeft; + plArmatureMod* fArmature; +public: + plAvatarFootMsg() + { + fEvent = kTime; + SetBCastFlag(plMessage::kBCastByExactType); + } + plAvatarFootMsg(const plKey& s, plArmatureMod *armature, hsBool isLeft) : plEventCallbackMsg(s, nil, nil), fArmature(armature), fIsLeft(isLeft) + { + fEvent = kTime; + SetBCastFlag(plMessage::kBCastByExactType); + } + + CLASSNAME_REGISTER( plAvatarFootMsg ); + GETINTERFACE_ANY( plAvatarFootMsg, plEventCallbackMsg ); + + virtual void Read(hsStream *stream, hsResMgr *mgr) + { + hsAssert(false, "This message is not supposed to travel over the network or persist in a file."); + } + virtual void Write(hsStream *stream, hsResMgr *mgr) + { + hsAssert(false, "This message is not supposed to travel over the network or persist in a file."); + } + + hsBool IsLeft() const { return fIsLeft; } + void SetIsLeft(hsBool on) { fIsLeft = (0 != on); } + + plArmatureMod* GetArmature() const { return fArmature; } + void SetArmature(plArmatureMod* a) { fArmature = a; } + +}; + +#endif // plAvatarFootMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarMsg.cpp new file mode 100644 index 00000000..704a397d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarMsg.cpp @@ -0,0 +1,658 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef NO_AV_MSGS + +#include "plAvatarMsg.h" + +#include "hsResMgr.h" + +#include "../pnKeyedObject/plKey.h" +#include "../pnSceneObject/plSceneObject.h" + +#ifndef SERVER +#include "../plAvatar/plAvBrain.h" +#endif + + +////////////// +// PLAVATARMSG +////////////// + +// CTOR() +plAvatarMsg::plAvatarMsg() +: plMessage() +{ +} + +// CTOR(sender, receiver, time) +plAvatarMsg::plAvatarMsg(const plKey &sender, const plKey &receiver) +: plMessage(sender, receiver, nil) +{ +} + +// READ +void plAvatarMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgRead(stream, mgr); +} + +// WRITE +void plAvatarMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgWrite(stream, mgr); +} + +////////////////////// +// PLARMATUREUPDATEMSG +////////////////////// + +// CTOR() +plArmatureUpdateMsg::plArmatureUpdateMsg() +: fIsInvis(false) +{ + SetBCastFlag(plMessage::kBCastByExactType); +} + +// CTOR sender receiver islocal isplayercontrolled +plArmatureUpdateMsg::plArmatureUpdateMsg(const plKey &sender, + hsBool isLocal, hsBool isPlayerControlled, + plArmatureMod *armature) +: plAvatarMsg(sender, nil), + fIsLocal(isLocal), + fIsPlayerControlled(isPlayerControlled), + fArmature(armature), + fIsInvis(false) +{ + SetBCastFlag(plMessage::kBCastByExactType); +} + +// READ stream mgr +void plArmatureUpdateMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + hsAssert(false, "This message is not supposed to travel over the network or persist in a file."); +} + +// WRITE stream mgr +void plArmatureUpdateMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + hsAssert(false, "This message is not supposed to travel over the network or persist in a file."); +} + +// ISLOCAL +hsBool plArmatureUpdateMsg::IsLocal() const +{ + return fIsLocal; +} + +// ISPLAYERCONTROLLED +hsBool plArmatureUpdateMsg::IsPlayerControlled() const +{ + return fIsPlayerControlled; +} + +hsBool plArmatureUpdateMsg::IsInvis() const +{ + return fIsInvis; +} + +///////////////////// +// PLAVATARSETTYPEMSG +///////////////////// + +// ctor +plAvatarSetTypeMsg::plAvatarSetTypeMsg() +: fIsPlayer(false) +{ +} + +plAvatarSetTypeMsg::plAvatarSetTypeMsg(const plKey &sender, const plKey &receiver) +: plAvatarMsg(sender, receiver), + fIsPlayer(false) +{ +} + +// READ +void plAvatarSetTypeMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + fIsPlayer = stream->Readbool(); +} + +// WRITE +void plAvatarSetTypeMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + stream->Writebool(fIsPlayer); +} + +// SETISPLAYER +void plAvatarSetTypeMsg::SetIsPlayer(bool is) +{ + fIsPlayer = is; +} + +// ISPLAYER +bool plAvatarSetTypeMsg::IsPlayer() +{ + return fIsPlayer; +} + + + +////////////// +// PLAVTASKMSG +////////////// + +plAvTaskMsg::plAvTaskMsg() +: plAvatarMsg(), fTask(nil) +{ +} + +plAvTaskMsg::plAvTaskMsg(const plKey &sender, const plKey &receiver) +: plAvatarMsg(sender, receiver), fTask(nil) +{ +} + +plAvTaskMsg::plAvTaskMsg(const plKey &sender, const plKey &receiver, plAvTask *task) +: plAvatarMsg(sender, receiver), + fTask(task) +{ +} + +plAvTask *plAvTaskMsg::GetTask() +{ + return fTask; +} + +// READ +void plAvTaskMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plAvatarMsg::Read(stream, mgr); + if(stream->ReadBool()) + fTask = (plAvTask *)mgr->ReadCreatable(stream); + +} + +// WRITE +void plAvTaskMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plAvatarMsg::Write(stream, mgr); + if(fTask) + { + stream->WriteBool(true); + mgr->WriteCreatable(stream, (plCreatable *)fTask); + } else { + stream->WriteBool(false); + } +} + +////////////// +// +// PLAVSEEKMSG +// +////////////// +// Tell the avatar to go to a specific seekpoint. +// The given key (seekKey) must be to a plSeekPointMod + +// CTOR() +plAvSeekMsg::plAvSeekMsg() +: plAvTaskMsg(), + fSeekPoint(nil), + fDuration(0), + fSmartSeek(true), + fAlignType(kAlignHandle), + fAnimName(nil), + fNoSeek(false), + fFlags(kSeekFlagForce3rdPersonOnStart) +{ +} + +// CTOR(sender, receiver, seekKey, time) +plAvSeekMsg::plAvSeekMsg(const plKey& sender, const plKey& receiver, + const plKey &seekKey, float duration, hsBool smartSeek, + plAvAlignment alignType, char *animName, hsBool noSeek, + UInt8 flags, plKey finishKey) +: plAvTaskMsg(sender, receiver), + fSeekPoint(seekKey), + fTargetPos(0, 0, 0), + fTargetLookAt(0, 0, 0), + fDuration(duration), + fSmartSeek(smartSeek), + fAnimName(animName), + fAlignType(alignType), + fNoSeek(noSeek), + fFlags(flags), + fFinishKey(finishKey) +{ +} + +hsBool plAvSeekMsg::Force3rdPersonOnStart() +{ + return fFlags & kSeekFlagForce3rdPersonOnStart; +} + +hsBool plAvSeekMsg::UnForce3rdPersonOnFinish() +{ + return fFlags & kSeekFlagUnForce3rdPersonOnFinish; +} + +hsBool plAvSeekMsg::NoWarpOnTimeout() +{ + return fFlags & kSeekFlagNoWarpOnTimeout; +} + +hsBool plAvSeekMsg::RotationOnly() +{ + return fFlags & kSeekFlagRotationOnly; +} + +// READ +void plAvSeekMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plAvTaskMsg::Read(stream, mgr); + + fSeekPoint = mgr->ReadKey(stream); + if (!fSeekPoint) + { + fTargetPos.Read(stream); + fTargetLookAt.Read(stream); + } + + fDuration = stream->ReadSwapScalar(); + fSmartSeek = stream->ReadBool(); + fAnimName = stream->ReadSafeString(); + fAlignType = static_cast(stream->ReadSwap16()); + fNoSeek = stream->ReadBool(); + fFlags = stream->ReadByte(); + fFinishKey = mgr->ReadKey(stream); +} + +// WRITE +void plAvSeekMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plAvTaskMsg::Write(stream, mgr); + + mgr->WriteKey(stream, fSeekPoint); + if (!fSeekPoint) + { + fTargetPos.Write(stream); + fTargetLookAt.Write(stream); + } + + stream->WriteSwapScalar(fDuration); + stream->WriteBool(fSmartSeek); + stream->WriteSafeString(fAnimName); + stream->WriteSwap16(static_cast(fAlignType)); + stream->WriteBool(fNoSeek); + stream->WriteByte(fFlags); + mgr->WriteKey(stream, fFinishKey); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void plAvTaskSeekDoneMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plAvatarMsg::Read(stream, mgr); + + fAborted = stream->ReadBool(); +} + +void plAvTaskSeekDoneMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plAvatarMsg::Write(stream, mgr); + + stream->WriteBool(fAborted); +} + +///////////////// +// +// PLAVONESHOTMSG +// +///////////////// + +#include "../plMessage/plOneShotCallbacks.h" + +// CTOR() +plAvOneShotMsg::plAvOneShotMsg() +: plAvSeekMsg(), fAnimName(nil), fDrivable(false), fReversible(false), fCallbacks(nil) +{ +} + +// CTOR(sender, receiver, seekKey, time) +plAvOneShotMsg::plAvOneShotMsg(const plKey &sender, const plKey& receiver, + const plKey& seekKey, float duration, hsBool smartSeek, + const char *animName, hsBool drivable, hsBool reversible) +: plAvSeekMsg(sender, receiver, seekKey, duration, smartSeek), + fDrivable(drivable), fReversible(reversible), fCallbacks(nil) +{ + fAnimName = hsStrcpy(animName); +} + +// DTOR +plAvOneShotMsg::~plAvOneShotMsg() +{ + hsRefCnt_SafeUnRef(fCallbacks); + fCallbacks = nil; + + delete[] fAnimName; +} + +// READ +void plAvOneShotMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plAvSeekMsg::Read(stream, mgr); + + delete [] fAnimName; + fAnimName = stream->ReadSafeString(); + fDrivable = stream->ReadBool(); + fReversible = stream->ReadBool(); +} + +// WRITE +void plAvOneShotMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plAvSeekMsg::Write(stream, mgr); + + stream->WriteSafeString(fAnimName); + stream->WriteBool(fDrivable); + stream->WriteBool(fReversible); +} + + +//////////////// +// +// plAvBrainGenericMsg +// +//////////////// + +// default CTOR +plAvBrainGenericMsg::plAvBrainGenericMsg() +: fType(kNextStage), // default verb is goto next stage + fWhichStage(0), // default stage is 0 + fSetTime(false), // don't set the time of the target stage + fNewTime(0.0f), // if we do set, set it to zero + fSetDirection(false), // don't set the direction of the brain + fNewDirection(true), // if we do set the direction, set it to forward + fNewLoopCount(0) +{ +} + +// canonical CTOR sender receiver type stage rewind transitionTime +plAvBrainGenericMsg::plAvBrainGenericMsg(const plKey& sender, const plKey &receiver, + plAvBrainGenericMsg::Type type, int stage, hsBool rewind, hsScalar transitionTime) +: plAvatarMsg(sender, receiver), + fType(type), + fWhichStage(stage), + fSetTime(rewind), + fNewTime(0.0f), + fSetDirection(false), + fNewDirection(true), + fNewLoopCount(0) +{ + +} + +plAvBrainGenericMsg::plAvBrainGenericMsg(const plKey& sender, const plKey &receiver, + Type type, int stage, hsBool setTime, hsScalar newTime, + hsBool setDirection, bool isForward, hsScalar transitiontime) +: plAvatarMsg(sender, receiver), + fType(type), + fWhichStage(stage), + fSetTime(setTime), + fNewTime(newTime), + fSetDirection(setDirection), + fNewDirection(isForward), + fNewLoopCount(0) +{ +} + +plAvBrainGenericMsg::plAvBrainGenericMsg(plKey sender, plKey receiver, + Type type, int stage, int newLoopCount) +: plAvatarMsg(sender, receiver), + fType(type), + fWhichStage(stage), + fSetTime(false), // unused + fNewTime(0), // unused + fSetDirection(false), // unused + fNewDirection(false), // unused + fNewLoopCount(newLoopCount) +{ + hsAssert(type == kSetLoopCount, "This constructor form is only for the kSetLoopCount command."); +} + + +void plAvBrainGenericMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plAvatarMsg::Write(stream, mgr); + stream->WriteSwap32(fType); + stream->WriteSwap32(fWhichStage); + stream->WriteBool(fSetTime); + stream->WriteSwapScalar(fNewTime); + stream->WriteBool(fSetDirection); + stream->WriteBool(fNewDirection); + stream->WriteSwapScalar(fTransitionTime); +} + +void plAvBrainGenericMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plAvatarMsg::Read(stream, mgr); + fType = static_cast(stream->ReadSwap32()); + fWhichStage = stream->ReadSwap32(); + fSetTime = stream->ReadBool(); + fNewTime = stream->ReadSwapScalar(); + fSetDirection = stream->ReadBool(); + fNewDirection = stream->ReadBool(); + fTransitionTime = stream->ReadSwapScalar(); +} + +enum AvBrainGenericFlags +{ + kAvBrainGenericType, + kAvBrainGenericWhich, + kAvBrainGenericSetTime, + kAvBrainGenericNewTime, + kAvBrainGenericSetDir, + kAvBrainGenericNewDir, + kAvBrainGenericTransTime, +}; + +void plAvBrainGenericMsg::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kAvBrainGenericType); + contentFlags.SetBit(kAvBrainGenericWhich); + contentFlags.SetBit(kAvBrainGenericSetTime); + contentFlags.SetBit(kAvBrainGenericNewTime); + contentFlags.SetBit(kAvBrainGenericSetDir); + contentFlags.SetBit(kAvBrainGenericNewDir); + contentFlags.SetBit(kAvBrainGenericTransTime); + contentFlags.Write(s); + + // kAvBrainGenericType + s->WriteSwap32(fType); + // kAvBrainGenericWhich + s->WriteSwap32(fWhichStage); + // kAvBrainGenericSetTime + s->WriteBool(fSetTime); + // kAvBrainGenericNewTime + s->WriteSwapScalar(fNewTime); + // kAvBrainGenericSetDir + s->WriteBool(fSetDirection); + // kAvBrainGenericNewDir + s->WriteBool(fNewDirection); + // kAvBrainGenericTransTime + s->WriteSwapScalar(fTransitionTime); +} + +void plAvBrainGenericMsg::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kAvBrainGenericType)) + fType = static_cast(s->ReadSwap32()); + if (contentFlags.IsBitSet(kAvBrainGenericWhich)) + fWhichStage = s->ReadSwap32(); + if (contentFlags.IsBitSet(kAvBrainGenericSetTime)) + fSetTime = s->ReadBool(); + if (contentFlags.IsBitSet(kAvBrainGenericNewTime)) + fNewTime = s->ReadSwapScalar(); + if (contentFlags.IsBitSet(kAvBrainGenericSetDir)) + fSetDirection = s->ReadBool(); + if (contentFlags.IsBitSet(kAvBrainGenericNewDir)) + fNewDirection = s->ReadBool(); + if (contentFlags.IsBitSet(kAvBrainGenericTransTime)) + fTransitionTime = s->ReadSwapScalar(); +} + +/////////////////// +// +// PLAVPUSHBRAINMSG +// +/////////////////// + +#ifndef SERVER + +// default ctor +plAvPushBrainMsg::plAvPushBrainMsg() +: fBrain(nil) +{ +} + +// canonical ctor +plAvPushBrainMsg::plAvPushBrainMsg(const plKey& sender, const plKey &receiver, plArmatureBrain *brain) +: plAvTaskMsg(sender, receiver) +{ + fBrain = brain; +} + +// dtor +plAvPushBrainMsg::~plAvPushBrainMsg() +{ +} + +// READ +void plAvPushBrainMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plAvTaskMsg::Read(stream, mgr); + + fBrain = plArmatureBrain::ConvertNoRef(mgr->ReadCreatable(stream)); + hsAssert(fBrain, "PLAVPUSHBRAINMSG: Problem reading brain from stream."); +} + +// WRITE +void plAvPushBrainMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plAvTaskMsg::Write(stream, mgr); + mgr->WriteCreatable(stream, fBrain); +} + + + +////////////////// +// +// PLAVPOPBRAINMSG +// +////////////////// + +// default ctor +plAvPopBrainMsg::plAvPopBrainMsg() +{ +} + +// canonical ctor +plAvPopBrainMsg::plAvPopBrainMsg(const plKey &sender, const plKey &receiver) +: plAvTaskMsg(sender, receiver) +{ +} + +#endif // SERVER + + +///////////////// +//// +//// PLAVEMOTEMSG +//// +///////////////// +// +//// default ctor +//plAvEmoteMsg::plAvEmoteMsg() +//: fAnimName(nil) +//{ +//} +// +//// canonical ctor +//plAvEmoteMsg::plAvEmoteMsg(plKey sender, plKey receiver, char *name) +//: plAvTaskMsg(sender, receiver) +//{ +// fAnimName = hsStrcpy(name); +//} +// +//// READ +//void plAvEmoteMsg::Read(hsStream *stream, hsResMgr *mgr) +//{ +// plAvTaskMsg::Read(stream, mgr); +// fAnimName = stream->ReadSafeString(); +//} +// +//// WRITE +//void plAvEmoteMsg::Write(hsStream *stream, hsResMgr *mgr) +//{ +// plAvTaskMsg::Write(stream, mgr); +// stream->WriteSafeString(fAnimName); + + + + +/////////////////////////// +// +// PLAVATARSTEALTHMODEMSG +// +/////////////////////////// + +plAvatarStealthModeMsg::plAvatarStealthModeMsg() : plAvatarMsg(), fMode(kStealthVisible), fLevel(0) +{ + SetBCastFlag(plMessage::kBCastByExactType); +} + +plAvatarStealthModeMsg::~plAvatarStealthModeMsg() {} + +// READ stream mgr +void plAvatarStealthModeMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + hsAssert(false, "This message is not supposed to travel over the network or persist in a file."); +} + +// WRITE stream mgr +void plAvatarStealthModeMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + hsAssert(false, "This message is not supposed to travel over the network or persist in a file."); +} + + +#endif // ndef NO_AV_MSGS diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarMsg.h new file mode 100644 index 00000000..2956858c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plAvatarMsg.h @@ -0,0 +1,448 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef NO_AV_MSGS + +#ifndef plAvatarMsg_inc +#define plAvatarMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsBitVector.h" +#include "../plAvatar/plArmatureMod.h" +#include "../pnMessage/plEventCallbackMsg.h" + +class plSceneObject; +class hsStream; +class hsResMgr; +class plAvTask; +class plKey; +class plArmatureMod; +class plArmatureBrain; + +/** \Class plAvatarMsg + Abstract base class for messages to and from the avatar. + */ +class plAvatarMsg : public plMessage +{ +public: + // tors + plAvatarMsg(); + plAvatarMsg(const plKey &sender, const plKey &receiver); + + // plasma protocol + CLASSNAME_REGISTER( plAvatarMsg ); + GETINTERFACE_ANY( plAvatarMsg, plMessage ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + + +/** \Class plArmatureUpdateMsg + Sent every frame by all armatures. Currently only sent by humans, + mainly because currently that's all we have. Non-humans may send + less often. + */ +class plArmatureUpdateMsg : public plAvatarMsg +{ +public: + plArmatureUpdateMsg(); + plArmatureUpdateMsg(const plKey &sender, + hsBool isLocal, hsBool isPlayerControlled, + plArmatureMod *armature); + + /** The avatar that sent this message is the local avatar for this client. */ + hsBool IsLocal() const; + void SetIsLocal(hsBool on) { fIsLocal = on; } + /** The avatar that sent this message is controlled by a human being -- although + not necessarily a local human being. */ + hsBool IsPlayerControlled() const; + void SetIsPlayerControlled(hsBool on) { fIsPlayerControlled = on; } + hsBool IsInvis() const; + void SetInvis(hsBool val) { fIsInvis = val; } + + // plasma protocol + CLASSNAME_REGISTER( plArmatureUpdateMsg ); + GETINTERFACE_ANY( plArmatureUpdateMsg, plAvatarMsg ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + plArmatureMod * fArmature; // the armature that sent this message + // valid during the message's lifetime + +protected: + // these will probably change to enums + bitmasks .. don't count on the representation + hsBool fIsLocal; + hsBool fIsPlayerControlled; + hsBool fIsInvis; // Avatar is invis. Don't update visable effects. +}; + +// use this to turn an npc into a player and vice-versa +class plAvatarSetTypeMsg : public plAvatarMsg +{ +public: + plAvatarSetTypeMsg(); + plAvatarSetTypeMsg(const plKey &sender, const plKey &receiver); + + // theoretically we will someday achieve a broader taxonomy + void SetIsPlayer(bool is); + bool IsPlayer(); + + CLASSNAME_REGISTER(plAvatarSetTypeMsg); + GETINTERFACE_ANY(plAvatarSetTypeMsg, plAvatarMsg); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + +private: + bool fIsPlayer; +}; + +/** PLAVTASKMSG + An abstract message class for Avatar Tasks -- things which may have multiple steps + and which may be conducted over an extended period of time. + There is currently no provision for tasks which may be executed in parallel. + Activities which are parallelizable are handled by Behaviors + */ +class plAvTaskMsg : public plAvatarMsg +{ +public: + // tors + plAvTaskMsg(); + plAvTaskMsg(const plKey &sender, const plKey &receiver); + plAvTaskMsg(const plKey &sender, const plKey &receiver, plAvTask *task); + + plAvTask *GetTask(); + + // plasma protocol + CLASSNAME_REGISTER( plAvTaskMsg ); + GETINTERFACE_ANY( plAvTaskMsg, plAvatarMsg ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +private: + plAvTask *fTask; +}; + +/** \Class plAvSeekMsg + Tell the avatar to go to a specific location and assume + a specific orientation. + If smartSeek is true, the avatar will walk to the given spot. + otherwise they will float magically through all intervening + air and geometry. + Duration is only meaningful for the latter case, when the avatar + is floating - it determines the speed of float (i.e. reach the + particular destination in this amount of time, regardless of distance.) + Smartseek is definitely the preferred method; dumb seek is there + for when the level geometry hasn't been quite worked out. +*/ +class plAvSeekMsg : public plAvTaskMsg +{ +public: + enum + { + kSeekFlagUnForce3rdPersonOnFinish = 0x01, + kSeekFlagForce3rdPersonOnStart = 0x02, + kSeekFlagNoWarpOnTimeout = 0x04, + kSeekFlagRotationOnly = 0x08, + }; + + // tors + plAvSeekMsg(); + plAvSeekMsg(const plKey& sender, const plKey& receiver, const plKey &seekKey, float duration, hsBool smartSeek, + plAvAlignment align = kAlignHandle, char *animName = nil, hsBool noSeek = false, + UInt8 flags = kSeekFlagForce3rdPersonOnStart, plKey finishKey = nil); + + // plasma protocol + CLASSNAME_REGISTER( plAvSeekMsg ); + GETINTERFACE_ANY( plAvSeekMsg, plAvTaskMsg ); + + hsBool Force3rdPersonOnStart(); + hsBool UnForce3rdPersonOnFinish(); + hsBool NoWarpOnTimeout(); + hsBool RotationOnly(); + plKey GetFinishCallbackKey() { return fFinishKey; } + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + // public members + plKey fSeekPoint; // the key to the seekpoint we are going to find + hsPoint3 fTargetPos; // Or we specify the point/lookat explicitly + hsPoint3 fTargetLookAt; + float fDuration; // take this much time to do the move (only if smartSeek is false) + hsBool fSmartSeek; // seek by walking rather than floating + hsBool fNoSeek; + char *fAnimName; + plAvAlignment fAlignType; + UInt8 fFlags; + plKey fFinishKey; +}; + +class plAvTaskSeekDoneMsg : public plAvatarMsg +{ +public: + hsBool fAborted; + + plAvTaskSeekDoneMsg() : plAvatarMsg(), fAborted(false) {} + plAvTaskSeekDoneMsg(const plKey &sender, const plKey &receiver) : plAvatarMsg(sender, receiver), fAborted(false) {} + + // plasma protocol + CLASSNAME_REGISTER( plAvTaskSeekDoneMsg ); + GETINTERFACE_ANY( plAvTaskSeekDoneMsg, plAvatarMsg ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + +class plOneShotCallbacks; + +/** \Class plAvOneShotMsg + */ +class plAvOneShotMsg : public plAvSeekMsg +{ +public: + + // tors + plAvOneShotMsg(); + virtual ~plAvOneShotMsg(); + plAvOneShotMsg(const plKey &sender, const plKey& receiver, + const plKey& seekKey, float duration, hsBool fSmartSeek, + const char *animName, hsBool drivable, hsBool reversible); + + // plasma protocol + CLASSNAME_REGISTER( plAvOneShotMsg ); + GETINTERFACE_ANY( plAvOneShotMsg, plAvSeekMsg ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + // public members + char * fAnimName; // the name of the animation we're going to use + hsBool fDrivable; // are we animated by time or by mouse movement? + hsBool fReversible; // can we play backwards? + plOneShotCallbacks *fCallbacks; // Callbacks given to us by a one-shot modifier + // we share it, so release with UnRef +}; + +//////////////// +// +// PLAVBRAINGENERICMSG +// +//////////////// +class plAvBrainGenericMsg : public plAvatarMsg +{ +public: + + // have to do members first for recursive definition + // don't bother with encapsulation + enum Type { kNextStage, + kPrevStage, + kGotoStage, + kSetLoopCount + } + fType; + int fWhichStage; // used only by goto stage + hsScalar fTransitionTime; // for crossfade between stages + hsBool fSetTime; + hsScalar fNewTime; + hsBool fSetDirection; + hsBool fNewDirection; + int fNewLoopCount; + // tors + plAvBrainGenericMsg(); + + //! Older constructor version, allowing simple rewinding only + plAvBrainGenericMsg(const plKey& sender, const plKey &receiver, + Type type, int stage, hsBool rewind, hsScalar transitionTime); + + /** Canonical constructor, allowing full control over time and direction of new stage. + \param sender Message sender + \param reciever Message receiver + \param type The "verb" for the command - next stage, previous stage, goto stage + \param stage The stage we're going to, if this is a goto command + \param setTime Do we want to manually set the time on the target stage? + \param newTime If setTime is true, this is the TRACKED_NEW (local) time used in the target stage + \param setDirection Do we want to set the overall brain direction? + \param isForward If setDirection is true, then true = forward, false = backward + \param transitionTime Time in seconds to transition between stages. + */ + plAvBrainGenericMsg(const plKey& sender, const plKey &receiver, + Type type, int stage, hsBool setTime, hsScalar newTime, + hsBool setDirection, bool isForward, hsScalar transitiontime); + + /** Constructor for setting the loop count in a particular stage. + \param sender The sender of this message. + \param receiver. The (key to the) avatar brain this message is going to. + \param type Must be kSetLoopCount + \param stage Which stage are we setting the loop count for? + \param newLoopCount The loop count we are setting on the stage + */ + plAvBrainGenericMsg::plAvBrainGenericMsg(plKey sender, plKey receiver, + Type type, int stage, int newLoopCount); + // plasma protocol + CLASSNAME_REGISTER( plAvBrainGenericMsg ); + GETINTERFACE_ANY( plAvBrainGenericMsg, plAvatarMsg ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + // WriteVersion writes the current version of this creatable and ReadVersion will read in + // any previous version. + virtual void ReadVersion(hsStream* s, hsResMgr* mgr); + virtual void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +/////////////////// +// +// PLAVPUSHBRAINMSG +// +/////////////////// + +#ifndef SERVER +class plAvPushBrainMsg : public plAvTaskMsg +{ +public: + // tors + plAvPushBrainMsg(); + plAvPushBrainMsg(const plKey& sender, const plKey &receiver, plArmatureBrain *brain); + ~plAvPushBrainMsg(); + + CLASSNAME_REGISTER( plAvPushBrainMsg ); + GETINTERFACE_ANY( plAvPushBrainMsg, plAvTaskMsg); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + plArmatureBrain *fBrain; +}; + +////////////////// +// +// PLAVPOPBRAINMSG +// +////////////////// +class plAvPopBrainMsg : public plAvTaskMsg +{ +public: + // tors + plAvPopBrainMsg(); + plAvPopBrainMsg(const plKey &sender, const plKey &receiver); + + CLASSNAME_REGISTER( plAvPopBrainMsg ); + GETINTERFACE_ANY( plAvPopBrainMsg, plAvTaskMsg); +}; + +#endif // SERVER + + +// For entering/exiting "stealth mode" +class plAvatarStealthModeMsg : public plAvatarMsg +{ +public: + plAvatarStealthModeMsg(); + ~plAvatarStealthModeMsg(); + + // modes + enum + { + kStealthVisible, + kStealthCloaked, + kStealthCloakedButSeen, + }; + UInt8 fMode; + int fLevel; // you are invisible to other players/CCRs of lower level + + CLASSNAME_REGISTER(plAvatarStealthModeMsg); + GETINTERFACE_ANY(plAvatarStealthModeMsg, plAvatarMsg); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + +class plAvatarBehaviorNotifyMsg : public plMessage +{ +public: + UInt32 fType; + hsBool state; + + plAvatarBehaviorNotifyMsg() : fType(0),state(false) {} + + CLASSNAME_REGISTER( plAvatarBehaviorNotifyMsg ); + GETINTERFACE_ANY( plAvatarBehaviorNotifyMsg, plMessage ); + + // Local Only + virtual void Read(hsStream *stream, hsResMgr *mgr) {} + virtual void Write(hsStream *stream, hsResMgr *mgr) {} +}; + +class plAvatarOpacityCallbackMsg : public plEventCallbackMsg +{ +public: + plAvatarOpacityCallbackMsg() : plEventCallbackMsg() {} + plAvatarOpacityCallbackMsg(plKey receiver, CallbackEvent e, int idx=0, hsScalar t=0, Int16 repeats=-1, UInt16 user=0) : + plEventCallbackMsg(receiver, e, idx, t, repeats, user) {} + + CLASSNAME_REGISTER( plAvatarOpacityCallbackMsg ); + GETINTERFACE_ANY( plAvatarOpacityCallbackMsg, plEventCallbackMsg ); + + // These aren't meant to go across the net, so no IO necessary. + void Read(hsStream* stream, hsResMgr* mgr) {} + void Write(hsStream* stream, hsResMgr* mgr) {} +}; + +class plAvatarSpawnNotifyMsg : public plMessage +{ +public: + plArmatureMod *fAvMod; + + plAvatarSpawnNotifyMsg() : fAvMod(nil) {} + + CLASSNAME_REGISTER( plAvatarSpawnNotifyMsg ); + GETINTERFACE_ANY( plAvatarSpawnNotifyMsg, plMessage ); + + // Local Only + virtual void Read(hsStream *stream, hsResMgr *mgr) {} + virtual void Write(hsStream *stream, hsResMgr *mgr) {} +}; + +class plAvatarPhysicsEnableCallbackMsg : public plEventCallbackMsg +{ +public: + plAvatarPhysicsEnableCallbackMsg() : plEventCallbackMsg() {} + plAvatarPhysicsEnableCallbackMsg(plKey receiver, CallbackEvent e, int idx=0, hsScalar t=0, Int16 repeats=-1, UInt16 user=0) : + plEventCallbackMsg(receiver, e, idx, t, repeats, user) {} + + CLASSNAME_REGISTER( plAvatarPhysicsEnableCallbackMsg ); + GETINTERFACE_ANY( plAvatarPhysicsEnableCallbackMsg, plEventCallbackMsg ); + + // These aren't meant to go across the net, so no IO necessary. + void Read(hsStream* stream, hsResMgr* mgr) {} + void Write(hsStream* stream, hsResMgr* mgr) {} +}; + +#endif // plAvatarMsg_inc +#endif // ndef NO_AV_MSGS diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plBulletMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plBulletMsg.cpp new file mode 100644 index 00000000..4d7d6bf5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plBulletMsg.cpp @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" + +#include "hsStream.h" + +#include "plBulletMsg.h" +#include "hsFastMath.h" + +void plBulletMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + IMsgRead(stream, mgr); + + fCmd = Cmd(stream->ReadByte()); + + fFrom.Read(stream); + fDir.Read(stream); + fRange = stream->ReadSwapScalar(); + fRadius = stream->ReadSwapScalar(); + fPartyTime = stream->ReadSwapScalar(); +} + +void plBulletMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + IMsgWrite(stream, mgr); + + stream->WriteByte(UInt8(fCmd)); + + fFrom.Write(stream); + fDir.Write(stream); + stream->WriteSwapScalar(fRange); + stream->WriteSwapScalar(fRadius); + stream->WriteSwapScalar(fPartyTime); +} + +void plBulletMsg::FireShot(const hsPoint3& from, const hsVector3& dir, hsScalar radius, hsScalar range, hsScalar psecs) +{ + fFrom = from; + fDir = dir; + fRange = range; + fRadius = radius; + fPartyTime = psecs; + + fCmd = kShot; +} + +void plBulletMsg::FireShot(const hsPoint3& from, const hsPoint3& at, hsScalar radius, hsScalar psecs) +{ + hsVector3 dir(&at, &from); + hsScalar invLen = hsFastMath::InvSqrt(dir.MagnitudeSquared()); + hsAssert(invLen > 0, "degenerate from and at to fire bullet"); + dir *= invLen; + hsScalar range = 1.f / invLen; + + FireShot(from, dir, radius, range, psecs); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plBulletMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plBulletMsg.h new file mode 100644 index 00000000..d8cd4e82 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plBulletMsg.h @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plBulletMsg_inc +#define plBulletMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsGeometry3.h" + +class plBulletMsg : public plMessage +{ +public: + enum Cmd + { + kStop, + kShot, + kSpray + }; +protected: + Cmd fCmd; + + hsPoint3 fFrom; + hsVector3 fDir; + hsScalar fRange; + hsScalar fRadius; + hsScalar fPartyTime; +public: + plBulletMsg() { SetBCastFlag(kNetPropagate | kBCastByType, true); } + plBulletMsg(const plKey &s, + const plKey &r, + const double* t) : plMessage(s, r, t) { SetBCastFlag(kNetPropagate | kBCastByType, true); } + + ~plBulletMsg() {} + + CLASSNAME_REGISTER( plBulletMsg ); + GETINTERFACE_ANY( plBulletMsg, plMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + hsBool Shot() const { return fCmd == kShot; } + hsBool Spray() const { return fCmd == kSpray; } + hsBool Stop() const { return fCmd == kStop; } + + void FireShot(const hsPoint3& from, const hsVector3& dir, hsScalar radius, hsScalar range, hsScalar psecs=-1.f); + void FireShot(const hsPoint3& from, const hsPoint3& at, hsScalar radius, hsScalar psecs=-1.f); + + Cmd GetCmd() const { return fCmd; } + void SetCmd(Cmd c) { fCmd = c; } + + const hsPoint3& From() const { return fFrom; } + const hsVector3& Dir() const { return fDir; } + hsScalar Range() const { return fRange; } + hsScalar Radius() const { return fRadius; } + hsScalar PartyTime() const { return fPartyTime; } +}; + +#endif // plBulletMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMessageCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMessageCreatable.h new file mode 100644 index 00000000..da49a64c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMessageCreatable.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plCCRMessageCreatable_inc +#define plCCRMessageCreatable_inc + +// +// CCR message creatables +// Kept separately for selective server include +// + +#include "plCCRMsg.h" +REGISTER_NONCREATABLE(plCCRMessage); +REGISTER_CREATABLE(plCCRPetitionMsg); +REGISTER_CREATABLE(plCCRInvisibleMsg); +REGISTER_CREATABLE(plCCRCommunicationMsg); +REGISTER_CREATABLE(plCCRBanLinkingMsg); +REGISTER_CREATABLE(plCCRSilencePlayerMsg); + +#endif // plCCRMessageCreatable_inc + + \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMsg.cpp new file mode 100644 index 00000000..82f76ba5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMsg.cpp @@ -0,0 +1,135 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "plCCRMsg.h" + +#include "../pnNetCommon/plNetApp.h" +#include "../plResMgr/plResManager.h" +#include "../plNetCommon/plNetCommon.h" + +void plCCRPetitionMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + plMsgStdStringHelper::Peek(fNote, stream); + plMsgStdStringHelper::Peek(fTitle, stream); + stream->ReadSwap(&fPetitionType); +} + +void plCCRPetitionMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + plMsgStdStringHelper::Poke(fNote, stream); + plMsgStdStringHelper::Poke(fTitle, stream); + stream->WriteSwap(fPetitionType); +} + +/////////////////////////////////////////////////////////// + +plCCRInvisibleMsg::plCCRInvisibleMsg() : fInvisLevel(0) +{ + // send only to remote NetClientMgrs + SetBCastFlag(kNetPropagate, true); + SetBCastFlag(kLocalPropagate, false); + AddReceiver(plNetApp::GetInstance()->GetKey()); +} + +void plCCRInvisibleMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + fAvKey=mgr->ReadKey(stream); + fInvisLevel = stream->ReadByte(); +} + +void plCCRInvisibleMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + mgr->WriteKey(stream, fAvKey); + stream->WriteByte(fInvisLevel); +} + +/////////////////////////////////////////////////////////// + +plCCRCommunicationMsg::plCCRCommunicationMsg() : fType(kUnInit), fCCRPlayerID(kInvalidPlayerID) +{ + SetBCastFlag(kBCastByType); +} + +void plCCRCommunicationMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + plMsgStdStringHelper::Peek(fString, stream); + fType = (Type)stream->ReadSwap32(); + stream->ReadSwap(&fCCRPlayerID); +} + +void plCCRCommunicationMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + plMsgStdStringHelper::Poke(fString, stream); + stream->WriteSwap32((int)fType); + stream->WriteSwap(fCCRPlayerID); +} + +/////////////////////////////////////////////////////////// + +plCCRBanLinkingMsg::plCCRBanLinkingMsg() : fBan(true) +{ + AddReceiver(plNetApp::GetInstance()->GetKey()); +} + +void plCCRBanLinkingMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + fBan = stream->ReadBool(); +} + +void plCCRBanLinkingMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + stream->WriteBool(fBan); +} + +/////////////////////////////////////////////////////////// + +plCCRSilencePlayerMsg::plCCRSilencePlayerMsg() : fSilence(true) +{ + AddReceiver(plNetApp::GetInstance()->GetKey()); +} + +void plCCRSilencePlayerMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + fSilence = stream->ReadBool(); +} + +void plCCRSilencePlayerMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + stream->WriteBool(fSilence); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMsg.h new file mode 100644 index 00000000..77b754e4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCCRMsg.h @@ -0,0 +1,170 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plCCRMsg_h +#define plCCRMsg_h + +#include "../pnMessage/plMessage.h" +#include "../plNetCommon/plNetCommon.h" + +// +// Abstract Baseclass for CCR messages. +// All CCR messages must derive from this or the server won't be able to tell that's what they are. +// +class plCCRMessage : public plMessage +{ +public: + CLASSNAME_REGISTER( plCCRMessage); + GETINTERFACE_ANY( plCCRMessage, plMessage ); +}; + + +// +// Player wants to petition a CCR +// +class plCCRPetitionMsg : public plCCRMessage +{ +private: + UInt8 fPetitionType; + std::string fNote; + std::string fTitle; +public: + plCCRPetitionMsg() : fPetitionType(plNetCommon::PetitionTypes::kGeneralHelp) { fBCastFlags |= kBCastByType; } + ~plCCRPetitionMsg() {} + + CLASSNAME_REGISTER( plCCRPetitionMsg); + GETINTERFACE_ANY( plCCRPetitionMsg, plCCRMessage ); + + // petition text + void SetNote(const char* n) { fNote=n; } + const char* GetNote() const { return fNote.c_str(); } + + // title + void SetTitle(const char* n) { fTitle=n; } + const char* GetTitle() const { return fTitle.c_str(); } + + // petition type + void SetType(const UInt8 t) { fPetitionType=t; } + UInt8 GetType() const { return fPetitionType; } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +// +// A CCR has gone invisible +// +class plCCRInvisibleMsg : public plCCRMessage +{ +public: + plKey fAvKey; + UInt8 fInvisLevel; // 0 means visible + + plCCRInvisibleMsg(); + ~plCCRInvisibleMsg() {} + + CLASSNAME_REGISTER( plCCRInvisibleMsg); + GETINTERFACE_ANY( plCCRInvisibleMsg, plCCRMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +// +// For CCR-player communication +// +class plCCRCommunicationMsg : public plCCRMessage +{ +public: + enum Type + { + kUnInit = 0, + kBeginCommunication, + kChat, + kEndCommunication, + kReturnChatMsg + }; + std::string fString; + Type fType; + UInt32 fCCRPlayerID; + + plCCRCommunicationMsg(); + ~plCCRCommunicationMsg() {} + + CLASSNAME_REGISTER( plCCRCommunicationMsg); + GETINTERFACE_ANY( plCCRCommunicationMsg, plCCRMessage ); + + // getters and setters + void SetMessage(const char* n) { fString=n; } + const char* GetMessage() const { return fString.c_str(); } + + void SetType(Type t) { fType=t; } + Type GetType() const { return fType; } + + void SetCCRPlayerID(UInt32 t) { fCCRPlayerID=t; } + UInt32 GetCCRPlayerID() const { return fCCRPlayerID; } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +// +// A CCR has banned a player from leaving the age +// +class plCCRBanLinkingMsg : public plCCRMessage +{ +public: + hsBool fBan; // ban or un ban + + plCCRBanLinkingMsg() ; + ~plCCRBanLinkingMsg() {} + + CLASSNAME_REGISTER( plCCRBanLinkingMsg); + GETINTERFACE_ANY( plCCRBanLinkingMsg, plCCRMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +// +// A CCR has silenced a player +// +class plCCRSilencePlayerMsg : public plCCRMessage +{ +public: + hsBool fSilence; // ban or un ban + + plCCRSilencePlayerMsg() ; + ~plCCRSilencePlayerMsg() {} + + CLASSNAME_REGISTER( plCCRSilencePlayerMsg); + GETINTERFACE_ANY( plCCRSilencePlayerMsg, plCCRMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plCCRMsg_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCaptureRenderMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCaptureRenderMsg.cpp new file mode 100644 index 00000000..1b6b1bd3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCaptureRenderMsg.cpp @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plCaptureRenderMsg.h" +#include "../plGImage/plMipmap.h" + +plCaptureRenderMsg::~plCaptureRenderMsg() +{ + hsRefCnt_SafeUnRef(fMipmap); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCaptureRenderMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCaptureRenderMsg.h new file mode 100644 index 00000000..a77f91c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCaptureRenderMsg.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCaptureRenderMsg_inc +#define plCaptureRenderMsg_inc + +#include "../pnMessage/plMessage.h" + +class plMipmap; + +class plCaptureRenderMsg : public plMessage +{ +public: + plCaptureRenderMsg() : plMessage(), fMipmap(nil) {} + plCaptureRenderMsg(const plKey &r, plMipmap* mipmap) : plMessage(nil, r, nil), fMipmap(mipmap) {} + + virtual ~plCaptureRenderMsg(); + + CLASSNAME_REGISTER( plCaptureRenderMsg ); + GETINTERFACE_ANY( plCaptureRenderMsg, plMessage ); + + // Mipmap will be unref'd on destruction of message. If you plan to + // hang onto it, you need to ref it when you receive this message. + // You can either use hsRefCnt or use the ResMgr to give it a new key. + plMipmap* GetMipmap() const { return fMipmap; } + plMipmap* fMipmap; + + virtual void Read(hsStream* stream, hsResMgr* mgr) { hsAssert(false, "Transient message"); } + virtual void Write(hsStream* stream, hsResMgr* mgr) { hsAssert(false, "Transient message"); } +}; + +#endif // plCaptureRenderMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbEventMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbEventMsg.h new file mode 100644 index 00000000..ed2a4306 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbEventMsg.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plClimbEventMsg_h_inc +#define plClimbEventMsg_h_inc + +#include "../pnMessage/plMessage.h" + + + +class plClimbEventMsg : public plMessage +{ +public: + plClimbEventMsg(){;} + virtual ~plClimbEventMsg(){;} + CLASSNAME_REGISTER(plClimbEventMsg); + GETINTERFACE_ANY(plClimbEventMsg, plMessage); + virtual void Read(hsStream* stream, hsResMgr* mgr){plMessage::IMsgRead(stream,mgr);} + virtual void Write(hsStream* stream, hsResMgr* mgr){plMessage::IMsgWrite(stream,mgr);} +}; + +#endif // plClimbEventMsg_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbMsg.cpp new file mode 100644 index 00000000..d83250f5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbMsg.cpp @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// singular +#include "plClimbMsg.h" + +// global +#include "hsResMgr.h" +#include "hsStream.h" + +plClimbMsg::plClimbMsg() +: fCommand(kNoCommand), + fDirection(plClimbMsg::kCenter), + fStatus(false), + fTarget(nil) +{ + // nothing +} + +plClimbMsg::plClimbMsg(const plKey &sender, const plKey &receiver, Command command, Direction direction, hsBool status, plKey target) +: plMessage(sender, receiver, nil), + fCommand(command), fDirection(direction), + fStatus(status), + fTarget(target) +{ + // not here +} + +void plClimbMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgRead(stream, mgr); + + fCommand = static_cast(stream->ReadSwap32()); + fDirection = static_cast(stream->ReadSwap32()); + fStatus = stream->ReadBool(); + fTarget = mgr->ReadKey(stream); +} + +void plClimbMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwap32(static_cast(fCommand)); + stream->WriteSwap32(static_cast(fDirection)); + stream->WriteBool(fStatus); + mgr->WriteKey(stream, fTarget); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbMsg.h new file mode 100644 index 00000000..993ff4e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plClimbMsg.h @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plClimbMsg_h +#define plClimbMsg_h + +#include "../pnMessage/plMessage.h" + +/** \class plClimbMsg + Things you can say with a climb message: + "mount the climbing wall with an upward/downward/leftward/rightward mount" + "dis-/enable climbing in the up/down/left/right direction" + "dis-/enable dismounting in the up/down/left/right direction" +*/ +class plClimbMsg : public plMessage +{ +public: + enum Direction { + kUp = 0x01, + kDown = 0x02, + kLeft = 0x04, + kRight = 0x08, + kCenter = 0x10 + }; + + enum Command { + kNoCommand = 0x0, + kEnableClimb = 0x1, + kEnableDismount = 0x2, + kFallOff = 0x4, + kRelease = 0x8, + kStartClimbing = 0x8 + }; + + // tors + plClimbMsg(); + plClimbMsg(const plKey &sender, const plKey &receiver, Command command = kNoCommand, Direction direction = kCenter, hsBool status = false, plKey target = nil); + + // plasma protocol + CLASSNAME_REGISTER( plClimbMsg ); + GETINTERFACE_ANY( plClimbMsg, plMessage ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + Command fCommand; + Direction fDirection; + hsBool fStatus; + plKey fTarget; // used for seeking to mount points +private: +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCollideMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCollideMsg.cpp new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCollideMsg.cpp @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCollideMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCollideMsg.h new file mode 100644 index 00000000..2331c2fe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCollideMsg.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef PLCOLLIDEMSG_INC +#define PLCOLLIDEMSG_INC + +#include "../pnMessage/plMessage.h" + +class plCollideMsg : public plMessage +{ +protected: +public: + plKey fOtherKey; + hsBool fEntering; // otherwise it's leaving + + plCollideMsg() { SetBCastFlag(plMessage::kPropagateToModifiers); } + ~plCollideMsg() {} + + CLASSNAME_REGISTER( plCollideMsg ); + GETINTERFACE_ANY( plCollideMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); } +}; + +#endif // PLCOLLIDEMSG_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCondRefMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCondRefMsg.h new file mode 100644 index 00000000..cdff4de8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plCondRefMsg.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCondRefMsg_inc +#define plCondRefMsg_inc + +#include "../pnMessage/plRefMsg.h" +#include "hsStream.h" + +class hsResMgr; + +class plCondRefMsg : public plRefMsg +{ + +public: + + plCondRefMsg() { fWhich = -1; } + plCondRefMsg::plCondRefMsg(const plKey &s, int which) + : plRefMsg(s, plRefMsg::kOnCreate), fWhich(which) {} + + CLASSNAME_REGISTER( plCondRefMsg ); + GETINTERFACE_ANY( plCondRefMsg, plRefMsg ); + + Int8 fWhich; + + // IO - not really applicable to ref msgs, but anyway + void Read(hsStream* stream, hsResMgr* mgr) + { + plRefMsg::Read(stream, mgr); + stream->ReadSwap(&fWhich); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plRefMsg::Write(stream, mgr); + stream->WriteSwap(fWhich); + } +}; + +#endif // plCondRefMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plConnectedToVaultMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plConnectedToVaultMsg.h new file mode 100644 index 00000000..1f4588dc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plConnectedToVaultMsg.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plConnectedToVaultMsg_INC +#define plConnectedToVaultMsg_INC + +#include "../pnMessage/plMessage.h" + +// +// A msg sent locally (once) when the client has successfully connected to the vault. +// +class hsResMgr; +class hsStream; +class plConnectedToVaultMsg : public plMessage +{ +public: + CLASSNAME_REGISTER( plConnectedToVaultMsg ); + GETINTERFACE_ANY( plConnectedToVaultMsg, plMessage ); + + plConnectedToVaultMsg() { SetBCastFlag(kBCastByType); } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); } +}; + +#endif // plConnectedToVaultMsg diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plConsoleMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plConsoleMsg.h new file mode 100644 index 00000000..03eaa97a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plConsoleMsg.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +////// TEMP HACK TO GET CONSOLE INIT EXECUTION ON AGE LOAD WORKING + +#ifndef plConsoleMsg_inc +#define plConsoleMsg_inc + +#include "../pnMessage/plMessage.h" + +class plEventCallbackMsg; + +class plConsoleMsg : public plMessage +{ +protected: + + UInt32 fCmd; + char *fString; + +public: + + enum + { + kExecuteFile, + kAddLine, + kExecuteLine + }; + + plConsoleMsg() : plMessage(nil, nil, nil), fCmd( 0 ), fString( nil ) { SetBCastFlag(kBCastByExactType); } + plConsoleMsg( UInt32 cmd, const char *str ) : + plMessage(nil, nil, nil), fCmd( cmd ), fString(hsStrcpy(str)) + { SetBCastFlag( kBCastByExactType ); } + + ~plConsoleMsg() { FREE(fString); } + + CLASSNAME_REGISTER( plConsoleMsg ); + GETINTERFACE_ANY( plConsoleMsg, plMessage ); + + UInt32 GetCmd( void ) const { return fCmd; } + const char *GetString( void ) const { return fString; }; + + void SetCmd (UInt32 cmd) { fCmd = cmd; } + void SetString (const char str[]) { FREE(fString); fString = hsStrcpy(str); } + + virtual void Read(hsStream* s, hsResMgr* mgr) + { + plMessage::IMsgRead(s, mgr); + s->ReadSwap(&fCmd); + // read string + plMsgCStringHelper::Peek(fString, s); + } + + virtual void Write(hsStream* s, hsResMgr* mgr) + { + plMessage::IMsgWrite(s, mgr); + s->WriteSwap(fCmd); + // write cmd/string + plMsgCStringHelper::Poke(fString, s); + } +}; + +#endif // plConsole_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDeviceRecreateMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDeviceRecreateMsg.h new file mode 100644 index 00000000..4f882805 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDeviceRecreateMsg.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDeviceRecreateMsg Header // +// Tiny message to let sceneNodes know that they need to clean up. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDeviceRecreateMsg_h +#define _plDeviceRecreateMsg_h + +#include "../pnMessage/plMessage.h" + +class plDeviceRecreateMsg : public plMessage +{ +public: + plDeviceRecreateMsg() : plMessage( nil, nil, nil ) { SetBCastFlag( kBCastByExactType ); } + ~plDeviceRecreateMsg() {} + + CLASSNAME_REGISTER( plDeviceRecreateMsg ); + GETINTERFACE_ANY( plDeviceRecreateMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead( stream, mgr ); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite( stream, mgr ); } +}; + +#endif // _plDeviceRecreateMsg_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynaDecalEnableMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynaDecalEnableMsg.cpp new file mode 100644 index 00000000..a2c067d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynaDecalEnableMsg.cpp @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plDynaDecalEnableMsg.h" + +#include "hsResMgr.h" +#include "hsStream.h" + +plDynaDecalEnableMsg::plDynaDecalEnableMsg() +: plMessage(nil, nil, nil), + fKey(nil), + fFlags(0), + fConTime(0), + fID(UInt32(-1)) +{ +} + +plDynaDecalEnableMsg::plDynaDecalEnableMsg(const plKey& r, const plKey& a, double t, hsScalar w, hsBool end, UInt32 id, hsBool isArm) +: plMessage(nil, r, nil), + fKey(a), + fFlags(0), + fConTime(t), + fWetLength(w), + fID(id) +{ + if( end ) + fFlags |= kAtEnd; + if( isArm ) + fFlags |= kArmature; +} + +plDynaDecalEnableMsg::~plDynaDecalEnableMsg() +{ +} + +void plDynaDecalEnableMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + IMsgRead(stream, mgr); + + fKey = mgr->ReadKey(stream); + + fConTime = stream->ReadSwapDouble(); + + fFlags = stream->ReadSwap32(); + + fID = stream->ReadSwap32(); +} + +void plDynaDecalEnableMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + IMsgWrite(stream, mgr); + + mgr->WriteKey(stream, fKey); + + stream->WriteSwapDouble(fConTime); + + stream->WriteSwap32(fFlags); + + stream->WriteSwap32(fID); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynaDecalEnableMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynaDecalEnableMsg.h new file mode 100644 index 00000000..1210693e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynaDecalEnableMsg.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynaDecalEnableMsg_inc +#define plDynaDecalEnableMsg_inc + +#include "../pnMessage/plMessage.h" +#include "../pnKeyedObject/plKey.h" + +class hsStream; +class hsResMgr; + +class plDynaDecalEnableMsg : public plMessage +{ +protected: + enum { + kAtEnd = 0x1, + kArmature = 0x2 + }; + plKey fKey; + double fConTime; + hsScalar fWetLength; + UInt32 fFlags; + UInt32 fID; +public: + plDynaDecalEnableMsg(); + plDynaDecalEnableMsg(const plKey& r, const plKey& armOrShapeKey, double conTime, hsScalar wetLength, hsBool end, UInt32 id=UInt32(-1), hsBool isArm=true); + ~plDynaDecalEnableMsg(); + + CLASSNAME_REGISTER( plDynaDecalEnableMsg ); + GETINTERFACE_ANY( plDynaDecalEnableMsg, plMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + // ArmKey undefined unless kArmature flag is set. You have to check. + const plKey& GetArmKey() const { return fKey; } + void SetArmKey(const plKey& k) { fKey = k; SetArmature(true); } + + hsBool IsArmature() const { return 0 != (fFlags & kArmature); } + void SetArmature(hsBool b) { if(b)fFlags |= kArmature; else fFlags &= ~kArmature; } + + const plKey& GetShapeKey() const { return fKey; } + void SetShapeKey(const plKey& k) { fKey = k; SetArmature(false); } + + double GetContactTime() const { return fConTime; } + void SetContactTime(double t) { fConTime = t; } + + hsScalar GetWetLength() const { return fWetLength; } + void SetWetLength(hsScalar w) { fWetLength = w; } + + hsBool AtEnd() const { return 0 != (fFlags & kAtEnd); } + void SetAtEnd(hsBool b) { if(b)fFlags |= kAtEnd; else fFlags &= ~kAtEnd; } + + UInt32 GetID() const { return fID; } + void SetID(UInt32 n) { fID = n; } +}; + +#endif // plDynaDecalEnableMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicEnvMapMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicEnvMapMsg.cpp new file mode 100644 index 00000000..3fc796df --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicEnvMapMsg.cpp @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plDynamicEnvMapMsg.h" + +#include "hsStream.h" + +void plDynamicEnvMapMsg::Read(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgRead(s, mgr); + + fCmd = s->ReadSwap32(); + + fPos.Read(s); + fHither = s->ReadSwapScalar(); + fYon = s->ReadSwapScalar(); + fFogStart = s->ReadSwapScalar(); + fColor.Read(s); + fRefresh = s->ReadSwapScalar(); +} + +void plDynamicEnvMapMsg::Write(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWrite(s, mgr); + + s->WriteSwap32(fCmd); + + fPos.Write(s); + s->WriteSwapScalar(fHither); + s->WriteSwapScalar(fYon); + s->WriteSwapScalar(fFogStart); + fColor.Write(s); + s->WriteSwapScalar(fRefresh); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicEnvMapMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicEnvMapMsg.h new file mode 100644 index 00000000..0a3ae3c7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicEnvMapMsg.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynamicEnvMapMsg_inc +#define plDynamicEnvMapMsg_inc + +#include "../pnMessage/plMessage.h" + +#include "hsGeometry3.h" +#include "hsColorRGBA.h" + +class hsStream; +class hsResMgr; + +class plDynamicEnvMapMsg : public plMessage +{ +public: + enum { + kReRender = 0x1, + kSetPosition = 0x2, + kSetHither = 0x4, + kSetYon = 0x8, + kSetColor = 0x10, + kSetFogStart = 0x20, + kSetRefresh = 0x40 + }; + + UInt32 fCmd; + + hsPoint3 fPos; + hsScalar fHither; + hsScalar fYon; + hsScalar fFogStart; + hsColorRGBA fColor; + hsScalar fRefresh; + +public: + plDynamicEnvMapMsg() : plMessage(nil, nil, nil), fCmd(0) {} + plDynamicEnvMapMsg(const plKey& rcv) : plMessage(nil, rcv, nil), fCmd(0) {} + virtual ~plDynamicEnvMapMsg() {} + + CLASSNAME_REGISTER( plDynamicEnvMapMsg ); + GETINTERFACE_ANY( plDynamicEnvMapMsg, plMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plDynamicEnvMapMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicTextMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicTextMsg.cpp new file mode 100644 index 00000000..4f29a570 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicTextMsg.cpp @@ -0,0 +1,360 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDynamicTextMsg // +// Message wrapper for commands to plDynamicTextMap. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plDynamicTextMsg.h" +#include "hsResMgr.h" +#include "hsBitVector.h" + +void plDynamicTextMsg::SetTextColor( hsColorRGBA &c, hsBool blockRGB ) +{ + hsAssert( ( fCmd & kColorCmds ) == 0, "Attempting to issue conflicting drawText commands" ); + fCmd &= ~kColorCmds; + fCmd |= kSetTextColor; + fColor = c; + fBlockRGB = blockRGB; +} + +void plDynamicTextMsg::SetFont( const char *face, Int16 size, hsBool isBold ) +{ + hsAssert( ( fCmd & ( kPosCmds | kStringCmds | kFlagCmds ) ) == 0, "Attempting to issue conflicting drawText commands" ); + fCmd &= ~( kPosCmds | kStringCmds | kFlagCmds ); + fCmd |= kSetFont; + fString = hsStringToWString( face ); + fX = size; + fFlags = (UInt32)isBold; +} + +void plDynamicTextMsg::SetLineSpacing( Int16 spacing ) +{ + fCmd |= kSetLineSpacing; + fLineSpacing = spacing; +} + +void plDynamicTextMsg::SetJustify( UInt8 justifyFlags ) +{ + hsAssert( ( fCmd & ( kFlagCmds ) ) == 0, "Attempting to issue conflicting drawText commands" ); + fCmd &= ~( kFlagCmds ); + fCmd |= kSetJustify; + fFlags = (UInt32)justifyFlags; +} + +void plDynamicTextMsg::FillRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, hsColorRGBA &c ) +{ + hsAssert( ( fCmd & ( kRectCmds | kColorCmds ) ) == 0, "Attempting to issue conflicting drawText commands" ); + fCmd &= ~( kRectCmds | kColorCmds ); + fCmd |= kFillRect; + + fLeft = left; + fTop = top; + fRight = right; + fBottom = bottom; + fColor = c; +} + +void plDynamicTextMsg::FrameRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, hsColorRGBA &c ) +{ + hsAssert( ( fCmd & ( kRectCmds | kColorCmds ) ) == 0, "Attempting to issue conflicting drawText commands" ); + fCmd &= ~( kRectCmds | kColorCmds ); + fCmd |= kFrameRect; + + fLeft = left; + fTop = top; + fRight = right; + fBottom = bottom; + fColor = c; +} + +void plDynamicTextMsg::DrawString( Int16 x, Int16 y, const char *text ) +{ + wchar_t *wString = hsStringToWString(text); + DrawString(x,y,wString); + delete [] wString; +} + +void plDynamicTextMsg::DrawString( Int16 x, Int16 y, const wchar_t *text ) +{ + hsAssert( ( fCmd & ( kStringCmds | kPosCmds ) ) == 0, "Attempting to issue conflicting drawText commands" ); + fCmd &= ~( kStringCmds | kPosCmds ); + fCmd |= kDrawString; + + fString = TRACKED_NEW wchar_t[wcslen(text)+1]; + wcscpy( fString, text ); + fString[wcslen(text)] = L'\0'; + fX = x; + fY = y; +} + +void plDynamicTextMsg::DrawClippedString( Int16 x, Int16 y, UInt16 clipLeft, UInt16 clipTop, UInt16 clipRight, UInt16 clipBottom, const char *text ) +{ + wchar_t *wString = hsStringToWString(text); + DrawClippedString(x,y,clipLeft,clipTop,clipRight,clipBottom,wString); + delete [] wString; +} + +void plDynamicTextMsg::DrawClippedString( Int16 x, Int16 y, UInt16 clipLeft, UInt16 clipTop, UInt16 clipRight, UInt16 clipBottom, const wchar_t *text ) +{ + hsAssert( ( fCmd & ( kStringCmds | kPosCmds | kRectCmds ) ) == 0, "Attempting to issue conflicting drawText commands" ); + fCmd &= ~( kStringCmds | kPosCmds | kRectCmds ); + fCmd |= kDrawClippedString; + + fString = TRACKED_NEW wchar_t[wcslen(text)+1]; + wcscpy( fString, text ); + fString[wcslen(text)] = L'\0'; + fX = x; + fY = y; + + fLeft = clipLeft; + fTop = clipTop; + fRight = clipRight; + fBottom = clipBottom; +} + +void plDynamicTextMsg::DrawWrappedString( Int16 x, Int16 y, UInt16 wrapWidth, UInt16 wrapHeight, const char *text ) +{ + wchar_t *wString = hsStringToWString(text); + DrawWrappedString(x,y,wrapWidth,wrapHeight,wString); + delete [] wString; +} + +void plDynamicTextMsg::DrawWrappedString( Int16 x, Int16 y, UInt16 wrapWidth, UInt16 wrapHeight, const wchar_t *text ) +{ + hsAssert( ( fCmd & ( kStringCmds | kPosCmds | kRectCmds ) ) == 0, "Attempting to issue conflicting drawText commands" ); + fCmd &= ~( kStringCmds | kPosCmds | kRectCmds ); + fCmd |= kDrawWrappedString; + + fString = TRACKED_NEW wchar_t[wcslen(text)+1]; + wcscpy( fString, text ); + fString[wcslen(text)] = L'\0'; + fX = x; + fY = y; + + fRight = wrapWidth; + fBottom = wrapHeight; +} + +void plDynamicTextMsg::DrawImage( Int16 x, Int16 y, plKey &image, hsBool respectAlpha ) +{ + hsAssert( ( fCmd & ( kPosCmds | kFlagCmds ) ) == 0, "Attempting to issue conflicting drawText commands" ); + fCmd &= ~( kPosCmds | kFlagCmds ); + fCmd |= kDrawImage; + + fImageKey = image; + fX = x; + fY = y; + fFlags = (UInt32)respectAlpha; +} + +void plDynamicTextMsg::DrawClippedImage( Int16 x, Int16 y, plKey &image, UInt16 clipX, UInt16 clipY, UInt16 clipWidth, UInt16 clipHeight, hsBool respectAlpha ) +{ + hsAssert( ( fCmd & ( kPosCmds | kFlagCmds | kRectCmds ) ) == 0, "Attempting to issue conflicting drawText commands" ); + fCmd &= ~( kPosCmds | kFlagCmds | kRectCmds ); + fCmd |= kDrawClippedImage; + + fImageKey = image; + fX = x; + fY = y; + fLeft = clipX; + fTop = clipY; + fRight = clipWidth; + fBottom = clipHeight; + fFlags = (UInt32)respectAlpha; +} + +void plDynamicTextMsg::Read( hsStream *s, hsResMgr *mgr ) +{ + plMessage::IMsgRead( s, mgr ); + + s->ReadSwap( &fCmd ); + s->ReadSwap( &fX ); + s->ReadSwap( &fY ); + + s->ReadSwap( &fLeft ); + s->ReadSwap( &fTop ); + s->ReadSwap( &fRight ); + s->ReadSwap( &fBottom ); + + fClearColor.Read( s ); + fColor.Read( s ); + + fString = s->ReadSafeWString(); + fImageKey = mgr->ReadKey( s ); + + s->ReadSwap( &fFlags ); + + s->ReadSwap( &fBlockRGB ); + s->ReadSwap( &fLineSpacing ); +} +void plDynamicTextMsg::Write( hsStream *s, hsResMgr *mgr ) +{ + plMessage::IMsgWrite( s, mgr ); + +#ifdef HS_DEBUGGING + if (fCmd & (kDrawImage | kDrawClippedImage)) + { + hsAssert(fImageKey != nil, "plDynamicTextMsg::Write: Must set imageKey for draw operation"); + } +#endif + + s->WriteSwap( fCmd ); + s->WriteSwap( fX ); + s->WriteSwap( fY ); + + s->WriteSwap( fLeft ); + s->WriteSwap( fTop ); + s->WriteSwap( fRight ); + s->WriteSwap( fBottom ); + + fClearColor.Write( s ); + fColor.Write( s ); + + s->WriteSafeWString( fString ); + mgr->WriteKey( s, fImageKey ); + + s->WriteSwap( fFlags ); + + s->WriteSwap( fBlockRGB ); + s->WriteSwap( fLineSpacing ); +} + +enum DynamicTextMsgFlags +{ + kDynTextMsgCmd, + kDynTextMsgX, + kDynTextMsgY, + kDynTextMsgLeft, + kDynTextMsgTop, + kDynTextMsgRight, + kDynTextMsgBottom, + kDynTextMsgClearColor, + kDynTextMsgColor, + kDynTextMsgString, + kDynTextMsgImageKey, + kDynTextMsgFlags, + kDynTextMsgBlockRGB, + kDynTextMsgLineSpacing, +}; + +void plDynamicTextMsg::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kDynTextMsgCmd)) + s->ReadSwap( &fCmd ); + if (contentFlags.IsBitSet(kDynTextMsgX)) + s->ReadSwap( &fX ); + if (contentFlags.IsBitSet(kDynTextMsgY)) + s->ReadSwap( &fY ); + if (contentFlags.IsBitSet(kDynTextMsgLeft)) + s->ReadSwap( &fLeft ); + if (contentFlags.IsBitSet(kDynTextMsgTop)) + s->ReadSwap( &fTop ); + if (contentFlags.IsBitSet(kDynTextMsgRight)) + s->ReadSwap( &fRight ); + if (contentFlags.IsBitSet(kDynTextMsgBottom)) + s->ReadSwap( &fBottom ); + if (contentFlags.IsBitSet(kDynTextMsgClearColor)) + fClearColor.Read( s ); + if (contentFlags.IsBitSet(kDynTextMsgColor)) + fColor.Read( s ); + if (contentFlags.IsBitSet(kDynTextMsgString)) + fString = s->ReadSafeWString(); + if (contentFlags.IsBitSet(kDynTextMsgImageKey)) + fImageKey = mgr->ReadKey( s ); + if (contentFlags.IsBitSet(kDynTextMsgFlags)) + s->ReadSwap( &fFlags ); + if (contentFlags.IsBitSet(kDynTextMsgBlockRGB)) + s->ReadSwap( &fBlockRGB ); + if (contentFlags.IsBitSet(kDynTextMsgLineSpacing)) + s->ReadSwap( &fLineSpacing ); +} + +void plDynamicTextMsg::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kDynTextMsgCmd); + contentFlags.SetBit(kDynTextMsgX); + contentFlags.SetBit(kDynTextMsgY); + contentFlags.SetBit(kDynTextMsgLeft); + contentFlags.SetBit(kDynTextMsgTop); + contentFlags.SetBit(kDynTextMsgRight); + contentFlags.SetBit(kDynTextMsgBottom); + contentFlags.SetBit(kDynTextMsgClearColor); + contentFlags.SetBit(kDynTextMsgColor); + contentFlags.SetBit(kDynTextMsgString); + contentFlags.SetBit(kDynTextMsgImageKey); + contentFlags.SetBit(kDynTextMsgFlags); + contentFlags.SetBit(kDynTextMsgBlockRGB); + contentFlags.SetBit(kDynTextMsgLineSpacing); + contentFlags.Write(s); + + // kDynTextMsgCmd + s->WriteSwap( fCmd ); + // kDynTextMsgX + s->WriteSwap( fX ); + // kDynTextMsgY + s->WriteSwap( fY ); + + // kDynTextMsgLeft + s->WriteSwap( fLeft ); + // kDynTextMsgTop + s->WriteSwap( fTop ); + // kDynTextMsgRight + s->WriteSwap( fRight ); + // kDynTextMsgBottom + s->WriteSwap( fBottom ); + + // kDynTextMsgClearColor + fClearColor.Write( s ); + // kDynTextMsgColor + fColor.Write( s ); + + // kDynTextMsgString + s->WriteSafeWString( fString ); + // kDynTextMsgImageKey + mgr->WriteKey( s, fImageKey ); + + // kDynTextMsgFlags + s->WriteSwap( fFlags ); + + // kDynTextMsgBlockRGB + s->WriteSwap( fBlockRGB ); + // kDynTextMsgLineSpacing + s->WriteSwap( fLineSpacing ); + +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicTextMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicTextMsg.h new file mode 100644 index 00000000..c67385b8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plDynamicTextMsg.h @@ -0,0 +1,132 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDynamicTextMsg Header // +// Message wrapper for commands to plDynamicTextMap. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDynamicTextMsg_h +#define _plDynamicTextMsg_h + +#include "../pnMessage/plMessage.h" +#include "hsColorRGBA.h" + +class plDynamicTextMap; + +class plDynamicTextMsg : public plMessage +{ + friend class plDynamicTextMap; + +protected: + UInt16 fCmd; + + // Position (fX is also used for font size) + Int16 fX, fY; + + // A rectangle + UInt16 fLeft, fTop, fRight, fBottom; + + // Colors + hsColorRGBA fClearColor; + hsColorRGBA fColor; + + // String + wchar_t *fString; + + // Mipmap + plKey fImageKey; + + // Misc flags field + UInt32 fFlags; + + hsBool fBlockRGB; + Int16 fLineSpacing; + +public: + plDynamicTextMsg() : plMessage( nil, nil, nil ) { fCmd = 0; fString = nil; fImageKey = nil; fFlags = 0; fBlockRGB = false; } + ~plDynamicTextMsg() { delete [] fString; } + + CLASSNAME_REGISTER( plDynamicTextMsg ); + GETINTERFACE_ANY( plDynamicTextMsg, plMessage ); + + enum Commands + { + kClear = 0x0001, + kSetTextColor = 0x0002, + kSetFont = 0x0004, + kFillRect = 0x0008, + kFrameRect = 0x0010, + kDrawString = 0x0020, + kDrawClippedString = 0x0040, + kDrawWrappedString = 0x0080, + kFlush = 0x0100, + kDrawImage = 0x0200, + kSetJustify = 0x0400, + kDrawClippedImage = 0x0800, + kSetLineSpacing = 0x1000, + kPurgeImage = 0x2000, + + // Don't use these--just masks for internal use + kColorCmds = kSetTextColor | kFillRect | kFrameRect, + kStringCmds = kSetFont | kDrawString | kDrawClippedString | kDrawWrappedString, + kRectCmds = kFillRect | kFrameRect | kDrawClippedString | kDrawWrappedString | kDrawClippedImage, + kPosCmds = kSetFont | kDrawClippedString | kDrawWrappedString | kDrawImage | kDrawClippedImage, + kFlagCmds = kSetFont | kDrawImage | kSetJustify | kDrawClippedImage + }; + + // Commands + void ClearToColor( hsColorRGBA &c ) { fCmd |= kClear; fClearColor = c; } + void Flush( void ) { fCmd |= kFlush; } + void PurgeImage( void ) { fCmd |= kPurgeImage; } + + // The following are mutually exclusive commands 'cause they share some parameters + void SetTextColor( hsColorRGBA &c, hsBool blockRGB = false ); + void SetFont( const char *face, Int16 size, hsBool isBold = false ); + void SetLineSpacing( Int16 spacing ); + void FillRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, hsColorRGBA &c ); + void FrameRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, hsColorRGBA &c ); + void DrawString( Int16 x, Int16 y, const char *text ); + void DrawString( Int16 x, Int16 y, const wchar_t *text ); + void DrawClippedString( Int16 x, Int16 y, UInt16 clipLeft, UInt16 clipTop, UInt16 clipRight, UInt16 clipBottom, const char *text ); + void DrawClippedString( Int16 x, Int16 y, UInt16 clipLeft, UInt16 clipTop, UInt16 clipRight, UInt16 clipBottom, const wchar_t *text ); + void DrawWrappedString( Int16 x, Int16 y, UInt16 wrapWidth, UInt16 wrapHeight, const char *text ); + void DrawWrappedString( Int16 x, Int16 y, UInt16 wrapWidth, UInt16 wrapHeight, const wchar_t *text ); + void DrawImage( Int16 x, Int16 y, plKey &image, hsBool respectAlpha = false ); + void DrawClippedImage( Int16 x, Int16 y, plKey &image, UInt16 clipX, UInt16 clipY, UInt16 clipWidth, UInt16 clipHeight, hsBool respectAlpha = false ); + void SetJustify( UInt8 justifyFlags ); + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + // WriteVersion writes the current version of this creatable and ReadVersion will read in + // any previous version. + virtual void ReadVersion(hsStream* s, hsResMgr* mgr); + virtual void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +#endif // _plDynamicTextMsg_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plElementRefMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plElementRefMsg.h new file mode 100644 index 00000000..e8c1fa21 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plElementRefMsg.h @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Nuked file. Delete from your source file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plEnvEffectMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plEnvEffectMsg.h new file mode 100644 index 00000000..187463ea --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plEnvEffectMsg.h @@ -0,0 +1,102 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plEnvEffectMsg_inc +#define plEnvEffectMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsStream.h" + +class hsResMgr; + + +class plEnvEffectMsg : public plMessage +{ + + hsBool fEnable; + +public: + plEnvEffectMsg(){ SetBCastFlag(plMessage::kPropagateToModifiers); } + + plEnvEffectMsg(const plKey* s, + const plKey* r, + const double* t){SetBCastFlag(plMessage::kPropagateToModifiers);} + + ~plEnvEffectMsg(){;} + + CLASSNAME_REGISTER( plEnvEffectMsg ); + GETINTERFACE_ANY( plEnvEffectMsg, plMessage ); + + hsBool Enabled() { return fEnable; } + void Enable(hsBool b) { fEnable = b; } + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + stream->ReadSwap(&fEnable); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwap(fEnable); + } +}; + + +class plEnvAudioEffectMsg : public plEnvEffectMsg +{ + UInt32 fPreset; + +public: + plEnvAudioEffectMsg(){SetBCastFlag(plMessage::kPropagateToModifiers);} + plEnvAudioEffectMsg(const plKey* s, + const plKey* r, + const double* t){SetBCastFlag(plMessage::kPropagateToModifiers);} + + ~plEnvAudioEffectMsg(){;} + + CLASSNAME_REGISTER( plEnvAudioEffectMsg ); + GETINTERFACE_ANY( plEnvAudioEffectMsg, plEnvEffectMsg ); + + UInt32 GetEffect() { return fPreset; } + void SetEffect(UInt32 i) { fPreset = i; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plEnvEffectMsg::Read(stream, mgr); + stream->ReadSwap(&fPreset); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plEnvEffectMsg::Write(stream, mgr); + stream->WriteSwap(fPreset); + } +}; + +#endif // plEnvEffectMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plExcludeRegionMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plExcludeRegionMsg.h new file mode 100644 index 00000000..1910b7f5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plExcludeRegionMsg.h @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plExcludeRegionMsg_inc +#define plExcludeRegionMsg_inc + +#include "../pnMessage/plMessage.h" + +class hsStream; + +class plExcludeRegionMsg : public plMessage +{ +public: + enum CmdType + { + kClear, // Moves all avatars from the affected region. Once they are gone, It sends a + // callback message and turns the region into a solid object that avatars + // cannot penetrate. + kRelease // Makes the xRegion not solid anymore + }; + +protected: + UInt8 fCmd; + +public: + plExcludeRegionMsg() : fCmd(kClear), fSynchFlags(0) {} + plExcludeRegionMsg(const plKey &s, const plKey &r, const double* t) : fCmd(kClear), fSynchFlags(0) {} + ~plExcludeRegionMsg() {} + + CLASSNAME_REGISTER(plExcludeRegionMsg); + GETINTERFACE_ANY(plExcludeRegionMsg, plMessage); + + void SetCmd(CmdType cmd) { fCmd = cmd; } + UInt8 GetCmd() { return fCmd; } + + UInt32 fSynchFlags; + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fCmd = stream->ReadByte(); + fSynchFlags = stream->ReadSwap32(); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->WriteByte(fCmd); + stream->WriteSwap32(fSynchFlags); + } +}; + +#endif // plExcludeRegionMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputEventMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputEventMsg.cpp new file mode 100644 index 00000000..913f3467 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputEventMsg.cpp @@ -0,0 +1,316 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plInputEventMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "hsResMgr.h" +#include "hsBitVector.h" + +plInputEventMsg::plInputEventMsg() : +fEvent(-1) +{ + SetBCastFlag(plMessage::kBCastByType); +} + +plInputEventMsg::plInputEventMsg(const plKey &s, + const plKey &r, + const double* t) : +fEvent(-1) +{ + SetBCastFlag(plMessage::kBCastByType); +} + +plInputEventMsg::~plInputEventMsg() +{ +} + +void plInputEventMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + stream->ReadSwap(&fEvent); +} + +void plInputEventMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + stream->WriteSwap(fEvent); +} + +enum InputEventMsgFlags +{ + kInputEventMsgEvent, +}; + +void plInputEventMsg::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kInputEventMsgEvent)) + s->ReadSwap(&fEvent); +} + +void plInputEventMsg::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kInputEventMsgEvent); + contentFlags.Write(s); + + // kInputEventMsgEvent + s->WriteSwap(fEvent); +} + +plControlEventMsg::plControlEventMsg() : + fCmd(nil) +{ + fTurnToPt.Set(0,0,0); + fControlPct = 1.0f; + SetBCastFlag(plMessage::kPropagateToModifiers); + SetBCastFlag(plMessage::kBCastByType, false); +} + +plControlEventMsg::plControlEventMsg(const plKey &s, + const plKey &r, + const double* t) : + fCmd(nil) +{ + fTurnToPt.Set(0,0,0); + fControlPct = 1.0f; + SetBCastFlag(plMessage::kBCastByType, false); + SetBCastFlag(plMessage::kPropagateToModifiers); +} + +plControlEventMsg::~plControlEventMsg() +{ + delete [] fCmd; +} + +void plControlEventMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plInputEventMsg::Read(stream, mgr); + stream->ReadSwap((Int32*)&fControlCode); + stream->ReadSwap(&fControlActivated); + stream->ReadSwap(&fControlPct); + fTurnToPt.Read(stream); + + // read cmd/string + plMsgCStringHelper::Peek(fCmd, stream); +} + +void plControlEventMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plInputEventMsg::Write(stream, mgr); + stream->WriteSwap((Int32)fControlCode); + stream->WriteSwap(fControlActivated); + stream->WriteSwap(fControlPct); + fTurnToPt.Write(stream); + + // write cmd/string + plMsgCStringHelper::Poke(fCmd, stream); +} + +enum ControlEventMsgFlags +{ + kControlEventMsgCode, + kControlEventMsgActivated, + kControlEventMsgPct, + kControlEventMsgTurnToPt, + kControlEventMsgCmd, +}; + +void plControlEventMsg::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plInputEventMsg::ReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kControlEventMsgCode)) + s->ReadSwap((Int32*)&fControlCode); + + if (contentFlags.IsBitSet(kControlEventMsgActivated)) + s->ReadSwap(&fControlActivated); + + if (contentFlags.IsBitSet(kControlEventMsgPct)) + s->ReadSwap(&fControlPct); + + if (contentFlags.IsBitSet(kControlEventMsgTurnToPt)) + fTurnToPt.Read(s); + + // read cmd/string + if (contentFlags.IsBitSet(kControlEventMsgCmd)) + plMsgCStringHelper::Peek(fCmd, s); +} + +void plControlEventMsg::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plInputEventMsg::WriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kControlEventMsgCode); + contentFlags.SetBit(kControlEventMsgActivated); + contentFlags.SetBit(kControlEventMsgPct); + contentFlags.SetBit(kControlEventMsgTurnToPt); + contentFlags.SetBit(kControlEventMsgCmd); + contentFlags.Write(s); + + // kControlEventMsgCode, + s->WriteSwap((Int32)fControlCode); + + // kControlEventMsgActivated, + s->WriteSwap(fControlActivated); + + // kControlEventMsgPct, + s->WriteSwap(fControlPct); + + // kControlEventMsgTurnToPt, + fTurnToPt.Write(s); + + // kControlEventMsgCmd, + plMsgCStringHelper::Poke(fCmd, s); +} + +plKeyEventMsg::plKeyEventMsg() +{ +} + +plKeyEventMsg::plKeyEventMsg(const plKey &s, + const plKey &r, + const double* t) +{ +} + +plKeyEventMsg::~plKeyEventMsg() +{ +} + + +plDebugKeyEventMsg::plDebugKeyEventMsg() +{ +} + +plDebugKeyEventMsg::plDebugKeyEventMsg(const plKey &s, + const plKey &r, + const double* t) +{ +} + +plDebugKeyEventMsg::~plDebugKeyEventMsg() +{ +} + + +plMouseEventMsg::plMouseEventMsg() : fXPos(0.0f),fYPos(0.0f),fDX(0.0f),fDY(0.0f),fButton(0) +{ +} + +plMouseEventMsg::plMouseEventMsg(const plKey &s, + const plKey &r, + const double* t) +{ +} + +plMouseEventMsg::~plMouseEventMsg() +{ +} + +///////////////////////////////////////////////////////////////////////////// + +// Mapping of bits to the control events we care about +const ControlEventCode plAvatarInputStateMsg::fCodeMap[] = +{ + B_CONTROL_MOVE_FORWARD, + B_CONTROL_MOVE_BACKWARD, + B_CONTROL_ROTATE_LEFT, + B_CONTROL_ROTATE_RIGHT, + B_CONTROL_STRAFE_LEFT, + B_CONTROL_STRAFE_RIGHT, + B_CONTROL_ALWAYS_RUN, + B_CONTROL_JUMP, + B_CONTROL_CONSUMABLE_JUMP, + B_CONTROL_MODIFIER_FAST, + B_CONTROL_MODIFIER_STRAFE, + B_CONTROL_LADDER_INVERTED, +}; +const UInt8 plAvatarInputStateMsg::fMapSize = 12; + +void plAvatarInputStateMsg::Read(hsStream *s, hsResMgr *mgr) +{ + plMessage::IMsgRead(s, mgr); + fState = s->ReadSwap16(); +} + +void plAvatarInputStateMsg::Write(hsStream *s, hsResMgr *mgr) +{ + plMessage::IMsgWrite(s, mgr); + s->WriteSwap16(fState); +} + +enum AvatarInputStateMsgFlags +{ + kAvatarInputStateMsgState, +}; + +void plAvatarInputStateMsg::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kAvatarInputStateMsgState)) + fState = s->ReadSwap16(); +} + +void plAvatarInputStateMsg::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kAvatarInputStateMsgState); + contentFlags.Write(s); + + s->WriteSwap16(fState); +} + +hsBool plAvatarInputStateMsg::IsCodeInMap(ControlEventCode code) +{ + int i; + for (i = 0; i < fMapSize; i++) + { + if (fCodeMap[i] == code) + return true; + } + + return false; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputEventMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputEventMsg.h new file mode 100644 index 00000000..727d789f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputEventMsg.h @@ -0,0 +1,407 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plInputEventMsg_inc +#define plInputEventMsg_inc + +#include "../pnMessage/plMessage.h" +#include "../pnInputCore/plControlDefinition.h" +#include "hsGeometry3.h" +#include "hsStream.h" +#include "hsUtils.h" + +class plKeyEventMsg; +class plMouseEventMsg; + +class plInputEventMsg : public plMessage +{ +public: + enum + { + kConfigure = 0, + }; + plInputEventMsg(); + plInputEventMsg(const plKey &s, + const plKey &r, + const double* t); + + ~plInputEventMsg(); + + CLASSNAME_REGISTER( plInputEventMsg ); + GETINTERFACE_ANY( plInputEventMsg, plMessage ); + + int fEvent; + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + + +class plControlEventMsg : public plInputEventMsg +{ +private: + char* fCmd; +protected: + + ControlEventCode fControlCode; + hsBool fControlActivated; + hsPoint3 fTurnToPt; + hsScalar fControlPct; +public: + + plControlEventMsg(); + plControlEventMsg(const plKey &s, + const plKey &r, + const double* t); + ~plControlEventMsg(); + + CLASSNAME_REGISTER( plControlEventMsg ); + GETINTERFACE_ANY( plControlEventMsg, plInputEventMsg ); + + void SetCmdString(const char* cs) { delete [] fCmd; fCmd=hsStrcpy(cs); } + void SetControlCode(ControlEventCode c) { fControlCode = c; } + void SetControlActivated(hsBool b) { fControlActivated = b; } + void SetTurnToPt(hsPoint3 pt) { fTurnToPt = pt; } + void SetControlPct(hsScalar p) { fControlPct = p; } + + ControlEventCode GetControlCode() const { return fControlCode; } + hsBool ControlActivated() { return fControlActivated; } + hsPoint3 GetTurnToPt() { return fTurnToPt; } + hsScalar GetPct() { return fControlPct; } + char* GetCmdString() { return fCmd; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + + +class plKeyEventMsg : public plInputEventMsg +{ +protected: + plKeyDef fKeyCode; + hsBool fKeyDown; + hsBool fCapsLockKeyDown; + hsBool fShiftKeyDown; + hsBool fCtrlKeyDown; + hsBool fRepeat; + +public: + + + plKeyEventMsg(); + plKeyEventMsg(const plKey &s, + const plKey &r, + const double* t); + ~plKeyEventMsg(); + + CLASSNAME_REGISTER( plKeyEventMsg ); + GETINTERFACE_ANY( plKeyEventMsg, plInputEventMsg ); + + void SetKeyCode(plKeyDef w) { fKeyCode = w; } + void SetKeyDown(hsBool b) { fKeyDown = b; } + void SetShiftKeyDown(hsBool b) { fShiftKeyDown = b; } + void SetCtrlKeyDown(hsBool b) { fCtrlKeyDown = b; } + void SetCapsLockKeyDown(hsBool b) { fCapsLockKeyDown = b; } + void SetRepeat(hsBool b) { fRepeat = b; } + + plKeyDef GetKeyCode() { return fKeyCode; } + hsBool GetKeyDown() { return fKeyDown; } + hsBool GetShiftKeyDown() { return fShiftKeyDown; } + hsBool GetCtrlKeyDown() { return fCtrlKeyDown; } + hsBool GetCapsLockKeyDown() { return fCapsLockKeyDown; } + hsBool GetRepeat() { return fRepeat; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Read(stream, mgr); + stream->ReadSwap((Int32*)&fKeyCode); + stream->ReadSwap(&fKeyDown); + stream->ReadSwap(&fCapsLockKeyDown); + stream->ReadSwap(&fShiftKeyDown); + stream->ReadSwap(&fCtrlKeyDown); + stream->ReadSwap(&fRepeat); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Write(stream, mgr); + stream->WriteSwap((Int32)fKeyCode); + stream->WriteSwap(fKeyDown); + stream->WriteSwap(fCapsLockKeyDown); + stream->WriteSwap(fShiftKeyDown); + stream->WriteSwap(fCtrlKeyDown); + stream->WriteSwap(fRepeat); + } +}; + + +class plDebugKeyEventMsg : public plInputEventMsg +{ +protected: + ControlEventCode fKeyCode; + hsBool fKeyDown; + hsBool fCapsLockKeyDown; + hsBool fShiftKeyDown; + hsBool fCtrlKeyDown; + +public: + + + plDebugKeyEventMsg(); + plDebugKeyEventMsg(const plKey &s, + const plKey &r, + const double* t); + ~plDebugKeyEventMsg(); + + CLASSNAME_REGISTER( plDebugKeyEventMsg ); + GETINTERFACE_ANY( plDebugKeyEventMsg, plInputEventMsg ); + + void SetKeyCode(ControlEventCode w) { fKeyCode = w; } + void SetKeyDown(hsBool b) { fKeyDown = b; } + void SetShiftKeyDown(hsBool b) { fShiftKeyDown = b; } + void SetCtrlKeyDown(hsBool b) { fCtrlKeyDown = b; } + void SetCapsLockKeyDown(hsBool b) { fCapsLockKeyDown = b; } + + ControlEventCode GetKeyCode() { return fKeyCode; } + hsBool GetKeyDown() { return fKeyDown; } + hsBool GetShiftKeyDown() { return fShiftKeyDown; } + hsBool GetCtrlKeyDown() { return fCtrlKeyDown; } + hsBool GetCapsLockKeyDown() { return fCapsLockKeyDown; } + + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Read(stream, mgr); + stream->ReadSwap((Int32*)&fKeyCode); + stream->ReadSwap(&fKeyDown); + stream->ReadSwap(&fCapsLockKeyDown); + stream->ReadSwap(&fShiftKeyDown); + stream->ReadSwap(&fCtrlKeyDown); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Write(stream, mgr); + stream->WriteSwap((Int32)fKeyCode); + stream->WriteSwap(fKeyDown); + stream->WriteSwap(fCapsLockKeyDown); + stream->WriteSwap(fShiftKeyDown); + stream->WriteSwap(fCtrlKeyDown); + } +}; + +class plIMouseXEventMsg : public plInputEventMsg +{ +public: + float fX; + int fWx; + + plIMouseXEventMsg() : + fX(0),fWx(0) {} + plIMouseXEventMsg(const plKey &s, + const plKey &r, + const double* t) : + fX(0),fWx(0) {} + ~plIMouseXEventMsg(){} + + CLASSNAME_REGISTER( plIMouseXEventMsg ); + GETINTERFACE_ANY( plIMouseXEventMsg, plInputEventMsg ); + + void Read(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Read(stream, mgr); + stream->ReadSwap(&fX); + stream->ReadSwap(&fWx); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Write(stream, mgr); + stream->WriteSwap(fX); + stream->WriteSwap(fWx); + } + +}; + +class plIMouseYEventMsg : public plInputEventMsg +{ +public: + float fY; + int fWy; + + plIMouseYEventMsg() : + fY(0),fWy(0) {} + plIMouseYEventMsg(const plKey &s, + const plKey &r, + const double* t) : + fY(0),fWy(0) {} + ~plIMouseYEventMsg(){} + + CLASSNAME_REGISTER( plIMouseYEventMsg ); + GETINTERFACE_ANY( plIMouseYEventMsg, plInputEventMsg ); + + void Read(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Read(stream, mgr); + stream->ReadSwap(&fY); + stream->ReadSwap(&fWy); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Write(stream, mgr); + stream->WriteSwap(fY); + stream->WriteSwap(fWy); + } + +}; +class plIMouseBEventMsg : public plInputEventMsg +{ +public: + short fButton; + + plIMouseBEventMsg() : + fButton(0) {} + plIMouseBEventMsg(const plKey &s, + const plKey &r, + const double* t) : + fButton(0) {} + ~plIMouseBEventMsg(){} + + CLASSNAME_REGISTER( plIMouseBEventMsg ); + GETINTERFACE_ANY( plIMouseBEventMsg, plInputEventMsg ); + + void Read(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Read(stream, mgr); + stream->ReadSwap(&fButton); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Write(stream, mgr); + stream->WriteSwap(fButton); + } + +}; + +class plMouseEventMsg : public plInputEventMsg +{ + +protected: + + float fXPos; + float fYPos; + float fDX; + float fDY; + float fWheelDelta; + + short fButton; + + +public: + plMouseEventMsg(); + plMouseEventMsg(const plKey &s, + const plKey &r, + const double* t); + ~plMouseEventMsg(); + + CLASSNAME_REGISTER( plMouseEventMsg ); + GETINTERFACE_ANY( plMouseEventMsg, plInputEventMsg ); + + void SetXPos(float Xpos) { fXPos = Xpos; }; + void SetYPos(float Ypos) { fYPos = Ypos; }; + void SetDX(float dX) { fDX = dX; } + void SetDY(float dY) { fDY = dY; } + void SetButton(short _button) { fButton = _button; } + void SetWheelDelta(float d) { fWheelDelta = d; } + + float GetXPos() { return fXPos; } + float GetYPos() { return fYPos; } + float GetDX() { return fDX; } + float GetDY() { return fDY; } + float GetWheelDelta() { return fWheelDelta; } + short GetButton() { return fButton; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Read(stream, mgr); + stream->ReadSwap(&fXPos); + stream->ReadSwap(&fYPos); + stream->ReadSwap(&fDX); + stream->ReadSwap(&fDY); + stream->ReadSwap(&fButton); + stream->ReadSwap(&fWheelDelta); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plInputEventMsg::Write(stream, mgr); + stream->WriteSwap(fXPos); + stream->WriteSwap(fYPos); + stream->WriteSwap(fDX); + stream->WriteSwap(fDY); + stream->WriteSwap(fButton); + stream->WriteSwap(fWheelDelta); + } +}; + +class plAvatarInputStateMsg : public plMessage +{ +public: + UInt16 fState; + + plAvatarInputStateMsg() : plMessage(), fState(0) {} + ~plAvatarInputStateMsg() {} + + CLASSNAME_REGISTER( plAvatarInputStateMsg ); + GETINTERFACE_ANY( plAvatarInputStateMsg, plMessage ); + + void Read(hsStream *s, hsResMgr *mgr); + void Write(hsStream *s, hsResMgr *mgr); + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); + + // Mapping of bits to the control events we care about + static const ControlEventCode fCodeMap[]; + static const UInt8 fMapSize; + + static hsBool IsCodeInMap(ControlEventCode code); +}; + +#endif // plInputEventMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputIfaceMgrMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputIfaceMgrMsg.cpp new file mode 100644 index 00000000..1bd6b45a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputIfaceMgrMsg.cpp @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plInputIfaceMgrMsg // +// Message wrapper for commands to plDynamicTextMap. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plInputIfaceMgrMsg.h" +#include "../plInputCore/plInputInterface.h" +#include "hsResMgr.h" +#include "hsRefCnt.h" + + +plInputIfaceMgrMsg::~plInputIfaceMgrMsg() +{ + if( fInterface != nil ) + hsRefCnt_SafeUnRef( fInterface ); +} + +void plInputIfaceMgrMsg::SetIFace( plInputInterface *iface ) +{ + fInterface = iface; + hsRefCnt_SafeRef( fInterface ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputIfaceMgrMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputIfaceMgrMsg.h new file mode 100644 index 00000000..e0570936 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInputIfaceMgrMsg.h @@ -0,0 +1,123 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plInputIfaceMgrMsg Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plInputIfaceMgrMsg_h +#define _plInputIfaceMgrMsg_h + +#include "hsTypes.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "../pnMessage/plMessage.h" +#include "../pnUtils/pnUtils.h" + +class plInputInterface; +class plInputIfaceMgrMsg : public plMessage +{ + protected: + + UInt8 fCommand; + plInputInterface *fInterface; + UInt32 fPageID; + const char* ageName; + const char* ageFileName; + const char* spawnPoint; + Uuid ageInstanceGuid; + plKey fAvKey; + public: + + enum + { + kAddInterface, + kRemoveInterface, + kEnableClickables, /// YEEEEEECH!!!! + kDisableClickables, + kSetOfferBookMode, + kClearOfferBookMode, + kNotifyOfferAccepted, + kNotifyOfferRejected, + kNotifyOfferCompleted, + kDisableAvatarClickable, + kEnableAvatarClickable, + kGUIDisableAvatarClickable, + kGUIEnableAvatarClickable, + kSetShareSpawnPoint, + kSetShareAgeInstanceGuid, + }; + + plInputIfaceMgrMsg() : plMessage( nil, nil, nil ) { SetBCastFlag( kBCastByExactType ); fInterface = nil; ageName = ageFileName = spawnPoint = 0; fAvKey = nil; } + plInputIfaceMgrMsg( plKey &receiver, UInt8 command ) : plMessage( nil, nil, nil ) { AddReceiver( receiver ); fCommand = command; fInterface = nil; fAvKey = nil; ageName = ageFileName = spawnPoint = 0;} + plInputIfaceMgrMsg( UInt8 command ) : plMessage( nil, nil, nil ) { SetBCastFlag( kBCastByExactType ); fCommand = command; fInterface = nil; fAvKey = nil; ageName = ageFileName = spawnPoint = 0;} + plInputIfaceMgrMsg( UInt8 command, UInt32 pageID ) : plMessage( nil, nil, nil ) { SetBCastFlag( kBCastByExactType ); fCommand = command; fPageID = pageID; fInterface = nil; fAvKey = nil; ageName = ageFileName = spawnPoint = 0;} + ~plInputIfaceMgrMsg(); + + CLASSNAME_REGISTER( plInputIfaceMgrMsg ); + GETINTERFACE_ANY( plInputIfaceMgrMsg, plMessage ); + + virtual void Read(hsStream* s, hsResMgr* mgr) + { + plMessage::IMsgRead( s, mgr ); + s->ReadSwap( &fCommand ); + s->ReadSwap( &fPageID ); + ageName = s->ReadSafeString(); + ageFileName = s->ReadSafeString(); + spawnPoint = s->ReadSafeString(); + fAvKey = mgr->ReadKey(s); + } + + virtual void Write(hsStream* s, hsResMgr* mgr) + { + plMessage::IMsgWrite( s, mgr ); + s->WriteSwap( fCommand ); + s->WriteSwap( fPageID ); + s->WriteSafeString(ageName); + s->WriteSafeString(ageFileName); + s->WriteSafeString(spawnPoint); + mgr->WriteKey(s,fAvKey); + } + + void SetAgeName(const char* s) { ageName = s; } + const char* GetAgeName() { return ageName; } + void SetAgeFileName(const char* s) { ageFileName = s; } + const char* GetAgeFileName() { return ageFileName; } + void SetSpawnPoint(const char* s) { spawnPoint = s; } + const char* GetSpawnPoint() { return spawnPoint; } + void SetAgeInstanceGuid(const Uuid& guid) { ageInstanceGuid = guid; } + const Uuid& GetAgeInstanceGuid() { return ageInstanceGuid; } + UInt8 GetCommand( void ) { return fCommand; } + UInt32 GetPageID( void ) { return fPageID; } + void SetIFace( plInputInterface *iface ); + plInputInterface *GetIFace( void ) const { return fInterface; } + plKey& GetAvKey( void ) { return fAvKey; } + const plKey& GetAvKey( void ) const { return fAvKey; } + void SetAvKey( plKey& k ) { fAvKey = k; } +}; + +#endif // _plInputIfaceMgrMsg_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInterestingPing.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInterestingPing.h new file mode 100644 index 00000000..80d93172 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plInterestingPing.h @@ -0,0 +1,102 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plInterestingPing_inc +#define plInterestingPing_inc + +#include "../pnMessage/plMessage.h" +#include "hsGeometry3.h" +#include "hsResMgr.h" + +class plInterestingModMsg : public plMessage +{ + +public: + plInterestingModMsg(){} + plInterestingModMsg(const plKey &s, + const plKey &r, + const double* t){} + ~plInterestingModMsg(){;} + + CLASSNAME_REGISTER( plInterestingModMsg ); + GETINTERFACE_ANY( plInterestingModMsg, plMessage ); + + hsScalar fWeight; + hsScalar fRadius; + hsScalar fSize; + hsPoint3 fPos; + plKey fObj; + UInt8 fType; + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + stream->ReadSwap(&fWeight); + stream->ReadSwap(&fRadius); + stream->ReadSwap(&fSize); + fPos.Read(stream); + fObj = mgr->ReadKey(stream); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwap(fWeight); + stream->WriteSwap(fRadius); + stream->WriteSwap(fSize); + fPos.Write(stream); + mgr->WriteKey(stream, fObj); + } +}; + +class plInterestingPing : public plMessage +{ + +public: + plInterestingPing(){SetBCastFlag(plMessage::kBCastByExactType);} + plInterestingPing(const plKey &s) {SetBCastFlag(plMessage::kBCastByExactType);SetSender(s);} + plInterestingPing(const plKey &s, + const plKey &r, + const double* t){SetBCastFlag(plMessage::kBCastByExactType);} + ~plInterestingPing(){;} + + CLASSNAME_REGISTER( plInterestingPing ); + GETINTERFACE_ANY( plInterestingPing, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + } +}; + +#endif // plInterestingPing diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSHitMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSHitMsg.cpp new file mode 100644 index 00000000..34c67e84 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSHitMsg.cpp @@ -0,0 +1,42 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLOSHitMsg.h" + +plLOSHitMsg::plLOSHitMsg() +{ + SetBCastFlag(plMessage::kPropagateToModifiers); + fHitFlags = 0; +} +plLOSHitMsg::plLOSHitMsg(const plKey &s, + const plKey &r, + const double* t) +: plMessage(s, r, t) +{ + SetBCastFlag(plMessage::kPropagateToModifiers); + fHitFlags = 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSHitMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSHitMsg.h new file mode 100644 index 00000000..808f518b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSHitMsg.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLOSHitMsg_inc +#define plLOSHitMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "hsGeometry3.h" + + +class plLOSHitMsg : public plMessage +{ +protected: + +public: + + plKey fObj; + hsPoint3 fHitPoint; + hsBool fNoHit; + UInt32 fRequestID; + UInt32 fHitFlags; + hsVector3 fNormal; + float fDistance; + + plLOSHitMsg(); + plLOSHitMsg(const plKey &s, + const plKey &r, + const double* t); + ~plLOSHitMsg(){;} + + CLASSNAME_REGISTER( plLOSHitMsg ); + GETINTERFACE_ANY( plLOSHitMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fObj = mgr->ReadKey(stream); + fHitPoint.Read(stream); + fNoHit = stream->ReadBool(); + stream->ReadSwap(&fRequestID); + stream->ReadSwap(&fHitFlags); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + mgr->WriteKey(stream, fObj); + fHitPoint.Write(stream); + stream->WriteBool(fNoHit); + stream->WriteSwap(fRequestID); + stream->WriteSwap(fHitFlags); + } +}; + + +#endif // plLOSHitMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSRequestMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSRequestMsg.cpp new file mode 100644 index 00000000..1e3317fc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSRequestMsg.cpp @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plLOSRequestMsg.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plUoid.h" + +plLOSRequestMsg::plLOSRequestMsg() + : fRequestID(0), + fRequestType(plSimDefs::kLOSDBNone), + fTestType(kTestAny), + fReportType(kReportHit), + fCullDB(plSimDefs::kLOSDBNone), + fWorldKey(nil) +{ + AddReceiver(hsgResMgr::ResMgr()->FindKey(kLOSObject_KEY)); + SetBCastFlag(plMessage::kPropagateToModifiers); +} + +plLOSRequestMsg::plLOSRequestMsg(const plKey& sender, hsPoint3& fromPoint, hsPoint3& toPoint, plSimDefs::plLOSDB db, TestType test, ReportType report) + : plMessage(sender, hsgResMgr::ResMgr()->FindKey(kLOSObject_KEY), nil), + fFrom(fromPoint), + fTo(toPoint), + fRequestID(0), + fRequestType(db), + fTestType(test), + fReportType(report), + fCullDB(plSimDefs::kLOSDBNone), + fWorldKey(nil) +{ + SetBCastFlag(plMessage::kPropagateToModifiers); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSRequestMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSRequestMsg.h new file mode 100644 index 00000000..36915fc9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLOSRequestMsg.h @@ -0,0 +1,101 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plLOSRequestMsg_inc +#define plLOSRequestMsg_inc + +#include "../pnMessage/plMessage.h" +#include "../plPhysical/plSimDefs.h" +#include "hsGeometry3.h" + +class plLOSRequestMsg : public plMessage +{ +public: + enum TestType { + kTestClosest, // this test has to keep going after finding a hit until it's ruled out others + kTestAny // this is faster because it will stop as soon as it finds any hit + }; + + enum ReportType + { + kReportHit, + kReportMiss, + kReportHitOrMiss + }; + + plLOSRequestMsg(); + plLOSRequestMsg(const plKey& sender, hsPoint3& fromPoint, hsPoint3& toPoint, plSimDefs::plLOSDB db, + TestType test = kTestAny, ReportType report = kReportHit); + + void SetFrom(const hsPoint3& from) { fFrom = from; } + void SetTo(const hsPoint3& to) { fTo = to; } + + void SetWorldKey(plKey world) { fWorldKey = world; } + + /** Determines which database we're testing against. + See plSimDefs::plLOSDB for a list of valid databases. */ + void SetRequestType(plSimDefs::plLOSDB type) { fRequestType = type; } + plSimDefs::plLOSDB GetRequestType() const { return fRequestType; } + + /** Determine whether we'll return on the first hit we get (kTestAny) + or keep looking until we're sure we've found the closest hit (kTestClosest) + The latter is slower because it has to look at all possible hits. */ + void SetTestType(TestType type) { fTestType = type; } + TestType GetTestType() const { return fTestType; } + + /** Do we report when we find a hit, when we don't find a hit, or both? */ + void SetReportType(ReportType type) { fReportType = type; } + ReportType GetReportType() const { return fReportType; } + + /** An ID invented by the caller for their own bookkeeping. */ + void SetRequestID(UInt32 id) { fRequestID = id; } + UInt32 GetRequestID() { return fRequestID; } + + /** If we get a hit on the first pass, we'll then double-check the remaining + segment (start->first hit) against the "cull db". If *any* hit is found, + the entire test fails. */ + void SetCullDB(plSimDefs::plLOSDB db) { fCullDB = db; } + plSimDefs::plLOSDB GetCullDB() { return fCullDB; } + + CLASSNAME_REGISTER( plLOSRequestMsg ); + GETINTERFACE_ANY( plLOSRequestMsg, plMessage ); + + // Local only, runtime, no IO necessary + virtual void Read(hsStream* s, hsResMgr* mgr) {} + virtual void Write(hsStream* s, hsResMgr* mgr) {} + +private: + friend class plLOSDispatch; + hsPoint3 fFrom; + hsPoint3 fTo; + plKey fWorldKey; // Force the query to happen in a particular subworld. + plSimDefs::plLOSDB fRequestType; // which database are we probing? + plSimDefs::plLOSDB fCullDB; // if we find a hit, see if anything in this DB blocks it. + TestType fTestType; // testing closest hit or just any? + ReportType fReportType; // reporting hits, misses, or both? + UInt32 fRequestID; +}; + +#endif // plLOSRequestMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLayRefMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLayRefMsg.cpp new file mode 100644 index 00000000..f140904f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLayRefMsg.cpp @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLayRefMsg.h" +#include "hsStream.h" + +void plLayRefMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plRefMsg::Read(stream, mgr); + stream->ReadSwap(&fType); + stream->ReadSwap(&fWhich); +} + +void plLayRefMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plRefMsg::Write(stream, mgr); + stream->WriteSwap(fType); + stream->WriteSwap(fWhich); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLayRefMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLayRefMsg.h new file mode 100644 index 00000000..416f7d0f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLayRefMsg.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLayRefMsg_inc +#define plLayRefMsg_inc + +#include "../pnMessage/plRefMsg.h" + +class hsStream; +class hsResMgr; + +class plLayRefMsg : public plRefMsg +{ +public: + enum { + kTexture = 1, + kUnderLay = 2, + kVertexShader = 3, + kPixelShader = 4 + }; + + plLayRefMsg() : fType(-1), fWhich(-1) {} + plLayRefMsg(const plKey &r, UInt8 f, Int8 which, Int8 type) : plRefMsg(r, f), fWhich(which), fType(type) {} + + CLASSNAME_REGISTER( plLayRefMsg ); + GETINTERFACE_ANY( plLayRefMsg, plRefMsg ); + + Int8 fType; + Int8 fWhich; + + // IO - not really applicable to ref msgs, but anyway + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plLayRefMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLightRefMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLightRefMsg.h new file mode 100644 index 00000000..3af4d105 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLightRefMsg.h @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLightRefMsg_inc +#define plLightRefMsg_inc + +#include "../pnMessage/plRefMsg.h" + +class hsKeyedObject; + +class plLightRefMsg : public plRefMsg +{ +public: + plLightRefMsg() {} + plLightRefMsg(const plKey& s, const plKey &r, hsKeyedObject* l, UInt8 c) : plRefMsg(r, c) { SetRef(l); SetSender(s); } + + CLASSNAME_REGISTER( plLightRefMsg ); + GETINTERFACE_ANY( plLightRefMsg, plRefMsg ); + + virtual void Read(hsStream* s, hsResMgr* mgr) { plRefMsg::Read(s, mgr); } + virtual void Write(hsStream* s, hsResMgr* mgr) { plRefMsg::Write(s, mgr); } +}; + +#endif // plLightRefMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinearVelocityMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinearVelocityMsg.h new file mode 100644 index 00000000..c2471efb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinearVelocityMsg.h @@ -0,0 +1,42 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../../NucleusLib/pnMessage/plSimulationMsg.h" +#include "../../CoreLib/hsGeometry3.h" +class plLinearVelocityMsg : public plSimulationMsg +{ +public: + // pass-through constructors + plLinearVelocityMsg() : plSimulationMsg() {}; + plLinearVelocityMsg(const plKey &sender, const plKey &receiver, const double *time) + : plSimulationMsg(sender,receiver, time), fVelocity(0.0f,0.0f,0.0f) {}; + CLASSNAME_REGISTER( plLinearVelocityMsg ); + GETINTERFACE_ANY( plLinearVelocityMsg, plSimulationMsg); + void Velocity(hsVector3& vel){fVelocity=vel;} + const hsVector3& Velocity(){return fVelocity;} +protected: + hsVector3 fVelocity; + +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinkToAgeMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinkToAgeMsg.cpp new file mode 100644 index 00000000..fa7d3871 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinkToAgeMsg.cpp @@ -0,0 +1,408 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "plLinkToAgeMsg.h" +#include "hsResMgr.h" +#include "hsUtils.h" +#include "plgDispatch.h" +#include "../plNetCommon/plNetServerSessionInfo.h" +#include "../plNetCommon/plNetCommon.h" +#include "hsBitVector.h" + + +///////////////////////////////////////////////////////////////////////// +// +// plLinkToAgeMsg + +plLinkToAgeMsg::plLinkToAgeMsg() : fLinkInAnimName(nil) +{ +} + +plLinkToAgeMsg::plLinkToAgeMsg( const plAgeLinkStruct * link ) : fLinkInAnimName(nil) +{ + fAgeLink.CopyFrom( link ); +} + +plLinkToAgeMsg::~plLinkToAgeMsg() +{ + delete [] fLinkInAnimName; +} + +// StreamVersion needed for back compatibility. +UInt8 plLinkToAgeInfo_StreamVersion = 0; +void plLinkToAgeMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead( stream, mgr ); + UInt8 ltaVer = stream->ReadByte(); + fAgeLink.Read( stream, mgr ); + fLinkInAnimName = stream->ReadSafeString(); +} + +void plLinkToAgeMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite( stream, mgr ); + stream->WriteByte( plLinkToAgeInfo_StreamVersion ); + fAgeLink.Write( stream, mgr ); + stream->WriteSafeString(fLinkInAnimName); +} + +enum LinkToAgeFlags +{ + kLinkToAgeAgeLinkStruct, + kLinkToAgeLinkAnimName, +}; + +void plLinkToAgeMsg::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if ( contentFlags.IsBitSet( kLinkToAgeAgeLinkStruct ) ) + fAgeLink.Read( s, mgr ); + if ( contentFlags.IsBitSet( kLinkToAgeLinkAnimName ) ) + fLinkInAnimName = s->ReadSafeString(); +} + +void plLinkToAgeMsg::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kLinkToAgeAgeLinkStruct); + contentFlags.SetBit(kLinkToAgeLinkAnimName); + contentFlags.Write(s); + + // write kLinkToAgeAgeLinkStruct + fAgeLink.Write( s, mgr ); + s->WriteSafeString(fLinkInAnimName); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// plLinkingMgrMsg + +plLinkingMgrMsg::plLinkingMgrMsg() +: fLinkingMgrCmd( 0 /*plNetLinkingMgr::kNilCmd*/ ) +{ +} + +plLinkingMgrMsg::~plLinkingMgrMsg() +{ +} + +enum LinkingMgrMsgFlags +{ + kLinkingMgrCmd, + kLinkingMgrArgs, +}; + +void plLinkingMgrMsg::Read( hsStream* stream, hsResMgr* mgr ) +{ + plMessage::IMsgRead( stream, mgr ); + + hsBitVector contentFlags; + contentFlags.Read( stream ); + + if ( contentFlags.IsBitSet( kLinkingMgrCmd ) ) + stream->ReadSwap( &fLinkingMgrCmd ); + if ( contentFlags.IsBitSet( kLinkingMgrArgs ) ) + fArgs.Read( stream, mgr ); +} + +void plLinkingMgrMsg::Write( hsStream* stream, hsResMgr* mgr ) +{ + plMessage::IMsgWrite( stream, mgr ); + + hsBitVector contentFlags; + contentFlags.SetBit( kLinkingMgrCmd ); + contentFlags.SetBit( kLinkingMgrArgs ); + contentFlags.Write( stream ); + + stream->WriteSwap( fLinkingMgrCmd ); + fArgs.Write( stream, mgr ); +} + +void plLinkingMgrMsg::ReadVersion( hsStream* s, hsResMgr* mgr ) +{ + // Read() does the contentFlags thing. + Read( s, mgr ); +} + +void plLinkingMgrMsg::WriteVersion( hsStream* s, hsResMgr* mgr ) +{ + // Write() does the contentFlags thing. + Write( s, mgr ); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// plLinkEffectsTriggerMsg + +plLinkEffectsTriggerMsg::~plLinkEffectsTriggerMsg() +{ +} + +void plLinkEffectsTriggerMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead( stream, mgr ); + + fInvisLevel = stream->ReadSwap32(); + fLeavingAge = stream->ReadBool(); + fLinkKey = mgr->ReadKey(stream); + + // This variable is for internal use only. Still read/written for backwards compatability. + fEffects = stream->ReadSwap32(); + fEffects = 0; + + fLinkInAnimKey = mgr->ReadKey(stream); +} + +void plLinkEffectsTriggerMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite( stream, mgr ); + + stream->WriteSwap32(fInvisLevel); + stream->WriteBool(fLeavingAge); + mgr->WriteKey(stream, fLinkKey); + stream->WriteSwap32(fEffects); + mgr->WriteKey(stream, fLinkInAnimKey); +} + +enum LinkEffectsFlags +{ + kLinkEffectsLeavingAge, + kLinkEffectsLinkKey, + kLinkEffectsEffects, + kLinkEffectsLinkInAnimKey, +}; + +#include "../pnNetCommon/plNetApp.h" +void plLinkEffectsTriggerMsg::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kLinkEffectsLeavingAge)) + fLeavingAge = s->ReadBool(); + if (contentFlags.IsBitSet(kLinkEffectsLinkKey)) + fLinkKey = mgr->ReadKey(s); + if (contentFlags.IsBitSet(kLinkEffectsEffects)) + fEffects = s->ReadSwap32(); + if (contentFlags.IsBitSet(kLinkEffectsLinkInAnimKey)) + fLinkInAnimKey = mgr->ReadKey(s); + + if (plNetClientApp::GetInstance() && fLinkKey == plNetClientApp::GetInstance()->GetLocalPlayerKey()) + { + SetBCastFlag(plMessage::kNetStartCascade, true); + SetBCastFlag(plMessage::kNetNonLocal | plMessage::kNetPropagate, false); + } +} + +void plLinkEffectsTriggerMsg::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kLinkEffectsLeavingAge); + contentFlags.SetBit(kLinkEffectsLinkKey); + contentFlags.SetBit(kLinkEffectsEffects); + contentFlags.SetBit(kLinkEffectsLinkInAnimKey); + contentFlags.Write(s); + + // kLinkEffectsLeavingAge + s->WriteBool(fLeavingAge); + // kLinkEffectsLinkKey + mgr->WriteKey(s, fLinkKey); + // kLinkEffectsEffects + s->WriteSwap32(fEffects); + // kLinkEffectsLinkInAnimKey + mgr->WriteKey(s, fLinkInAnimKey); +} + +void plLinkEffectsTriggerMsg::SetLinkKey(plKey &key) +{ + fLinkKey = key; +} + +void plLinkEffectsTriggerMsg::SetLinkInAnimKey(plKey &key) +{ + fLinkInAnimKey = key; +} + +///////////////////////////////////////////////////////////////////////////// +// +// plLinkEffectsPrepMsg + +plLinkEffectsTriggerPrepMsg::~plLinkEffectsTriggerPrepMsg() +{ + if (fTrigger) + hsRefCnt_SafeUnRef(fTrigger); +} + +void plLinkEffectsTriggerPrepMsg::SetTrigger(plLinkEffectsTriggerMsg *msg) +{ + if (fTrigger) + hsRefCnt_SafeUnRef(fTrigger); + + hsRefCnt_SafeRef(msg); + fTrigger = msg; +} + +///////////////////////////////////////////////////////////////////////////// +// +// plLinkEffectBCMsg + +plLinkEffectBCMsg::plLinkEffectBCMsg() : fLinkKey(nil), fLinkFlags(0) { SetBCastFlag(plMessage::kBCastByExactType); } + +void plLinkEffectBCMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + fLinkKey = mgr->ReadKey(stream); + fLinkFlags = stream->ReadSwap32(); +} + +void plLinkEffectBCMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + mgr->WriteKey(stream, fLinkKey); + stream->WriteSwap32(fLinkFlags); +} + +void plLinkEffectBCMsg::SetLinkFlag(UInt32 flag, hsBool on /* = true */) +{ + if (on) + fLinkFlags |= flag; + else + fLinkFlags &= ~flag; +} + +hsBool plLinkEffectBCMsg::HasLinkFlag(UInt32 flag) +{ + return fLinkFlags & flag; +} + +///////////////////////////////////////////////////////////////////////////// +// +// plLinkEffectPrepBCMsg + +plLinkEffectPrepBCMsg::plLinkEffectPrepBCMsg() : fLinkKey(nil), fLeavingAge(false) { SetBCastFlag(plMessage::kBCastByExactType); } + +///////////////////////////////////////////////////////////////////////////// +// +// plLinkCallbacks + +void plLinkCallbackMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plEventCallbackMsg::Read(stream, mgr); + + fLinkKey = mgr->ReadKey(stream); +} + +void plLinkCallbackMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plEventCallbackMsg::Write(stream, mgr); + + mgr->WriteKey(stream, fLinkKey); +} + +///////////////////////////////////////////////////////////////////////////////////// +//// +//// plPseudoLinkEffectMsg + +plPseudoLinkEffectMsg::plPseudoLinkEffectMsg() : fLinkObjKey(nil), fAvatarKey(nil) +{ + SetBCastFlag(plMessage::kNetPropagate | plMessage::kBCastByExactType); +} + +void plPseudoLinkEffectMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + fLinkObjKey = mgr->ReadKey(stream); + fAvatarKey = mgr->ReadKey(stream); +} + +void plPseudoLinkEffectMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + mgr->WriteKey(stream, fLinkObjKey); + mgr->WriteKey(stream, fAvatarKey); +} + +///////////////////////////////////////////////////////////////////////////////////// +//// +//// plPseudoLinkAnimTriggerMsg + +plPseudoLinkAnimTriggerMsg::plPseudoLinkAnimTriggerMsg() : fForward(false) +{ + SetBCastFlag(plMessage::kBCastByExactType); +} + + +plPseudoLinkAnimTriggerMsg::plPseudoLinkAnimTriggerMsg(hsBool forward, plKey avatarKey) +{ + fForward = forward; + fAvatarKey = avatarKey; + SetBCastFlag(plMessage::kBCastByExactType); +} + +void plPseudoLinkAnimTriggerMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream,mgr); + fForward = stream->ReadBool(); + fAvatarKey = mgr->ReadKey(stream); +} + +void plPseudoLinkAnimTriggerMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream,mgr); + stream->WriteBool(fForward); + mgr->WriteKey(stream, fAvatarKey); +} + +///////////////////////////////////////////////////////////////////////////////////// +//// +//// plPseudoLinkAnimCallbackMsg + + +void plPseudoLinkAnimCallbackMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream,mgr); + mgr->WriteKey(stream, fAvatarKey); +} + +void plPseudoLinkAnimCallbackMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream,mgr); + mgr->WriteKey(stream, fAvatarKey); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinkToAgeMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinkToAgeMsg.h new file mode 100644 index 00000000..0cd0180b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLinkToAgeMsg.h @@ -0,0 +1,284 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plLinkToAgeMsg_INC +#define plLinkToAgeMsg_INC + +#include "../pnMessage/plMessageWithCallbacks.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../plNetCommon/plNetServerSessionInfo.h" +#include "../plNetCommon/plNetCommonHelpers.h" +#include "hsUtils.h" + +//////////////////////////////////////////////////////////////////// +// A msg which is sent to the networking system to cause the player to link +// to a new age. +// +class plKey; +class hsStream; +class hsResMgr; + +class plLinkToAgeMsg : public plMessage +{ + plAgeLinkStruct fAgeLink; + char* fLinkInAnimName; + +public: + plLinkToAgeMsg(); + plLinkToAgeMsg( const plAgeLinkStruct * link ); + ~plLinkToAgeMsg(); + + CLASSNAME_REGISTER( plLinkToAgeMsg ); + GETINTERFACE_ANY( plLinkToAgeMsg, plMessage ); + + plAgeLinkStruct * GetAgeLink() { return &fAgeLink; } + const plAgeLinkStruct * GetAgeLink() const { return &fAgeLink; } + + const char * GetLinkInAnimName() { return fLinkInAnimName; } + void SetLinkInAnimName(const char* name) { delete [] fLinkInAnimName; fLinkInAnimName = hsStrcpy(name); } + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + // WriteVersion writes the current version of this creatable and ReadVersion will read in + // any previous version. + virtual void ReadVersion(hsStream* s, hsResMgr* mgr); + virtual void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +//////////////////////////////////////////////////////////////////// +// For other things the linking mgr does besides linking players to ages. +// See plNetLinkingMgr::Cmds for a list of these. + +class plLinkingMgrMsg : public plMessage +{ + UInt8 fLinkingMgrCmd; + plCreatableListHelper fArgs; + +public: + plLinkingMgrMsg(); + ~plLinkingMgrMsg(); + + CLASSNAME_REGISTER( plLinkingMgrMsg ); + GETINTERFACE_ANY( plLinkingMgrMsg, plMessage ); + + UInt8 GetCmd() const { return fLinkingMgrCmd; } + void SetCmd( UInt8 v ) { fLinkingMgrCmd=v; } + plCreatableListHelper * GetArgs() { return &fArgs; } + + void Read( hsStream* stream, hsResMgr* mgr ); + void Write( hsStream* stream, hsResMgr* mgr ); + + void ReadVersion( hsStream* s, hsResMgr* mgr ); + void WriteVersion( hsStream* s, hsResMgr* mgr ); +}; + + +//////////////////////////////////////////////////////////////////// + +class plLinkEffectsTriggerMsg : public plMessage +{ +protected: + hsBool fLeavingAge; + plKey fLinkKey; + plKey fLinkInAnimKey; + int fInvisLevel; +public: + plLinkEffectsTriggerMsg() : fLeavingAge(true), fLinkKey(nil), fLinkInAnimKey(nil), fEffects(0), fInvisLevel(0) { } + ~plLinkEffectsTriggerMsg(); + + CLASSNAME_REGISTER( plLinkEffectsTriggerMsg ); + GETINTERFACE_ANY( plLinkEffectsTriggerMsg, plMessage ); + + void SetInvisLevel(int invisLevel) { fInvisLevel=invisLevel; } + int GetInvisLevel() { return fInvisLevel; } + + void SetLeavingAge(hsBool leaving) { fLeavingAge = leaving; } + hsBool IsLeavingAge() { return fLeavingAge; } + + void SetLinkKey(plKey &key); + const plKey GetLinkKey() const { return fLinkKey; } + + void SetLinkInAnimKey(plKey &key); + plKey GetLinkInAnimKey() { return fLinkInAnimKey; } + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); + + Int32 fEffects; +}; + +//////////////////////////////////////////////////////////////////// + +class plLinkEffectsTriggerPrepMsg : public plMessage +{ +protected: + plLinkEffectsTriggerMsg *fTrigger; + +public: + hsBool fLeavingAge; + plKey fLinkKey; + + plLinkEffectsTriggerPrepMsg() : fLeavingAge(false), fLinkKey(nil), fTrigger(nil) { } + ~plLinkEffectsTriggerPrepMsg(); + + CLASSNAME_REGISTER( plLinkEffectsTriggerPrepMsg ); + GETINTERFACE_ANY( plLinkEffectsTriggerPrepMsg, plMessage ); + + void SetTrigger(plLinkEffectsTriggerMsg *msg); + plLinkEffectsTriggerMsg *GetTrigger() { return fTrigger; } + + // runtime-local only, no I/O + void Read(hsStream* stream, hsResMgr* mgr) {} + void Write(hsStream* stream, hsResMgr* mgr) {} +}; + +//////////////////////////////////////////////////////////////////// + + +class plLinkEffectBCMsg : public plMessage +{ +protected: + UInt32 fLinkFlags; + +public: + enum // link flags + { + kLeavingAge = 0x1, + kSendCallback = 0x2, + kMute = 0x4 // don't play any sound + }; + + plLinkEffectBCMsg(); + ~plLinkEffectBCMsg() { } + + CLASSNAME_REGISTER( plLinkEffectBCMsg ); + GETINTERFACE_ANY( plLinkEffectBCMsg, plMessage ); + + // These messages should never be sent over the net, so I'm + // suspicious that R/W actually does something... but if + // it isn't broken, I'm not going to touch it. + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + void SetLinkFlag(UInt32 flag, hsBool on = true); + hsBool HasLinkFlag(UInt32 flag); + + plKey fLinkKey; +}; + +//////////////////////////////////////////////////////////////////// + +class plLinkEffectPrepBCMsg : public plMessage +{ +public: + hsBool fLeavingAge; + plKey fLinkKey; + + plLinkEffectPrepBCMsg(); + ~plLinkEffectPrepBCMsg() { } + + CLASSNAME_REGISTER( plLinkEffectPrepBCMsg ); + GETINTERFACE_ANY( plLinkEffectPrepBCMsg, plMessage ); + + // runtime-local only, no I/O + void Read(hsStream* stream, hsResMgr* mgr) {} + void Write(hsStream* stream, hsResMgr* mgr) {} +}; + +//////////////////////////////////////////////////////////////////// + +class plLinkCallbackMsg : public plEventCallbackMsg +{ +public: + plLinkCallbackMsg() { } + ~plLinkCallbackMsg() { } + + CLASSNAME_REGISTER( plLinkCallbackMsg ); + GETINTERFACE_ANY( plLinkCallbackMsg, plEventCallbackMsg ); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + plKey fLinkKey; +}; + +//////////////////////////////////////////////////////////////////// + +class plPseudoLinkEffectMsg : public plMessage +{ +public: + plKey fLinkObjKey; + plKey fAvatarKey; + + plPseudoLinkEffectMsg(); + ~plPseudoLinkEffectMsg() {} + + CLASSNAME_REGISTER(plPseudoLinkEffectMsg); + GETINTERFACE_ANY(plPseudoLinkEffectMsg, plMessage); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + +}; + +class plPseudoLinkAnimTriggerMsg : public plMessage +{ +public: + + plPseudoLinkAnimTriggerMsg(); + plPseudoLinkAnimTriggerMsg(hsBool forward, plKey avatarKey); + ~plPseudoLinkAnimTriggerMsg() {} + + CLASSNAME_REGISTER(plPseudoLinkAnimTriggerMsg); + GETINTERFACE_ANY(plPseudoLinkAnimTriggerMsg, plMessage); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + plKey fAvatarKey; + hsBool fForward; + +}; + +class plPseudoLinkAnimCallbackMsg : public plMessage +{ +public: + + plPseudoLinkAnimCallbackMsg() {;} + ~plPseudoLinkAnimCallbackMsg() {;} + + CLASSNAME_REGISTER(plPseudoLinkAnimCallbackMsg); + GETINTERFACE_ANY(plPseudoLinkAnimCallbackMsg, plMessage); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + plKey fAvatarKey; +}; +#endif // plLinkToAgeMsg_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plListenerMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plListenerMsg.cpp new file mode 100644 index 00000000..f90c027f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plListenerMsg.cpp @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsStream.h" +#include "plListenerMsg.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/plFixedKey.h" + + +void plListenerMsg::Read(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgRead(s, mgr); + fPos.Read(s); + fDir.Read(s); + fUp.Read(s); + fVel.Read(s); +} +void plListenerMsg::Write(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWrite(s, mgr); + fPos.Write(s); + fDir.Write(s); + fUp.Write(s); + fVel.Write(s); +} + + +plSetListenerMsg::plSetListenerMsg( UInt8 type, const plKey &srcKey, hsBool binding ) : plMessage( nil, nil, nil ) +{ + plUoid uoid( kListenerMod_KEY ); + plKey pLKey = hsgResMgr::ResMgr()->FindKey( uoid ); + AddReceiver( pLKey ); + Set( srcKey, type, binding ); +} + +plSetListenerMsg::~plSetListenerMsg() +{ +} + +void plSetListenerMsg::Read( hsStream *s, hsResMgr *mgr ) +{ + plMessage::IMsgRead(s, mgr); + fType = s->ReadByte(); + fSrcKey = mgr->ReadKey( s ); + fBinding = s->ReadBool(); +} + +void plSetListenerMsg::Write( hsStream *s, hsResMgr *mgr ) +{ + plMessage::IMsgWrite(s, mgr); + s->WriteByte( fType ); + mgr->WriteKey( s, fSrcKey ); + s->WriteBool( fBinding ); +} + +void plSetListenerMsg::Set( const plKey &key, UInt8 type, hsBool binding ) +{ + fSrcKey = key; + fType = (UInt8)type; + fBinding = binding; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plListenerMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plListenerMsg.h new file mode 100644 index 00000000..d5355562 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plListenerMsg.h @@ -0,0 +1,106 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plListenerMsg_inc +#define plListenerMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsGeometry3.h" + +class plListenerMsg : public plMessage +{ +protected: + + hsPoint3 fPos; + hsVector3 fDir; + hsVector3 fUp; + hsVector3 fVel; + +public: + plListenerMsg() : plMessage(nil, nil, nil), + fPos(0,0,0), + fDir(0,1.f,0), + fUp(0,0,1.f), + fVel(0,0,0) + { SetBCastFlag(kBCastByExactType); } + + ~plListenerMsg() {} + + CLASSNAME_REGISTER( plListenerMsg ); + GETINTERFACE_ANY( plListenerMsg, plMessage ); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + const hsPoint3& SetPosition(const hsPoint3& pos) { return fPos = pos; } + const hsVector3& SetDirection(const hsVector3& dir) { return fDir = dir; } + const hsVector3& SetUp(const hsVector3& up) { return fUp = up; } + const hsVector3& SetVelocity(const hsVector3& vel) { return fVel = vel; } + + const hsPoint3& GetPosition() const { return fPos; } + const hsVector3& GetDirection() const { return fDir; } + const hsVector3& GetUp() const { return fUp; } + const hsVector3& GetVelocity() const { return fVel; } +}; + +class plSetListenerMsg : public plMessage +{ +protected: + + UInt8 fType; + plKey fSrcKey; + hsBool fBinding; + +public: + + enum SrcType + { + kPosition = 0x01, + kVelocity = 0x02, + kFacing = 0x04, + kVCam = 0x08, + + kListener = kPosition | kVelocity | kFacing + }; + + plSetListenerMsg() : plMessage( nil, nil, nil ) { fType = 0; fBinding = false; } + plSetListenerMsg( UInt8 type, const plKey &srcKey, hsBool binding ); + ~plSetListenerMsg(); + + CLASSNAME_REGISTER( plSetListenerMsg ); + GETINTERFACE_ANY( plSetListenerMsg, plMessage ); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + void Set( const plKey &key, UInt8 type, hsBool binding ); + + plKey &GetSrcKey( void ) { return fSrcKey; } + UInt8 GetType( void ) const { return fType; } + hsBool IsBinding( void ) const { return fBinding; } +}; + +#endif // plListenerMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAgeMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAgeMsg.cpp new file mode 100644 index 00000000..f0603969 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAgeMsg.cpp @@ -0,0 +1,119 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "plLoadAgeMsg.h" +#include "hsResMgr.h" +#include "hsUtils.h" +#include "hsBitVector.h" + +void plLoadAgeMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + delete [] fAgeFilename; + + // read agename + UInt8 len; + stream->ReadSwap(&len); + if (len) + { + fAgeFilename=TRACKED_NEW char[len+1]; + stream->Read(len, fAgeFilename); + fAgeFilename[len]=0; + } + fUnload = stream->ReadBool(); + stream->ReadSwap(&fPlayerID); + fAgeGuid.Read(stream); +} + +void plLoadAgeMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + // write agename + UInt8 len=fAgeFilename?hsStrlen(fAgeFilename):0; + stream->WriteSwap(len); + if (len) + { + stream->Write(len, fAgeFilename); + } + stream->WriteBool(fUnload); + stream->WriteSwap(fPlayerID); + fAgeGuid.Write(stream); +} + +enum LoadAgeFlags +{ + kLoadAgeAgeName, + kLoadAgeUnload, + kLoadAgePlayerID, + kLoadAgeAgeGuid, +}; + +void plLoadAgeMsg::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kLoadAgeAgeName)) + { + // read agename + delete [] fAgeFilename; + fAgeFilename = s->ReadSafeString(); + } + + if (contentFlags.IsBitSet(kLoadAgeUnload)) + fUnload = s->ReadBool(); + + if (contentFlags.IsBitSet(kLoadAgePlayerID)) + s->ReadSwap(&fPlayerID); + + if (contentFlags.IsBitSet(kLoadAgeAgeGuid)) + fAgeGuid.Read(s); +} + +void plLoadAgeMsg::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kLoadAgeAgeName); + contentFlags.SetBit(kLoadAgeUnload); + contentFlags.SetBit(kLoadAgePlayerID); + contentFlags.SetBit(kLoadAgeAgeGuid); + contentFlags.Write(s); + + // kLoadAgeAgeName + s->WriteSafeString(fAgeFilename); + // kLoadAgeUnload + s->WriteBool(fUnload); + // kLoadAgePlayerID + s->WriteSwap(fPlayerID); + // kLoadAgeAgeGuid + fAgeGuid.Write(s); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAgeMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAgeMsg.h new file mode 100644 index 00000000..c9b8c2bc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAgeMsg.h @@ -0,0 +1,106 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plLoadAgeMsg_INC +#define plLoadAgeMsg_INC + +#include "../pnMessage/plMessage.h" +#include "../plUUID/plUUID.h" +#include "hsUtils.h" + +// +// A msg which is sent to the networking system to cause an age to be loaded or unloaded +// +class plKey; +class hsStream; +class hsResMgr; +class plLoadAgeMsg : public plMessage +{ +protected: + char* fAgeFilename; // the age to load/unload + plUUID fAgeGuid; + hsBool fUnload; // true if we want to unload the age + int fPlayerID; +public: + plLoadAgeMsg() : fAgeFilename(nil), fUnload(false), fPlayerID(-1){ } + virtual ~plLoadAgeMsg() { delete [] fAgeFilename; } + + CLASSNAME_REGISTER( plLoadAgeMsg ); + GETINTERFACE_ANY( plLoadAgeMsg, plMessage ); + + void SetAgeFilename(const char* a) { delete [] fAgeFilename; fAgeFilename=a?hsStrcpy(a):nil; } + char* GetAgeFilename() const { return fAgeFilename; } + + void SetAgeGuid( const plUUID * v ) { fAgeGuid.CopyFrom( v ); } + const plUUID * GetAgeGuid() const { return &fAgeGuid; } + + void SetLoading(hsBool l) { fUnload=!l; } + hsBool GetLoading() const { return !fUnload; } + + void SetPlayerID(int p) { fPlayerID=p; } + int GetPlayerID() const { return fPlayerID; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +// +// Internal msg, sent by NetClientMgr to unload an age when linking out. +// Should not be used for other purposes +// +class plLinkOutUnloadMsg : public plLoadAgeMsg +{ +public: + plLinkOutUnloadMsg() { fUnload=true; } + + CLASSNAME_REGISTER( plLinkOutUnloadMsg ); + GETINTERFACE_ANY( plLinkOutUnloadMsg, plLoadAgeMsg ); +}; + +// +// Internal msg, used by NetClientMgr. +// (we send another to the avatar that linked) +// Not meant to go over the wire. +// +class plLinkInDoneMsg : public plMessage +{ +public: + + CLASSNAME_REGISTER( plLinkInDoneMsg ); + GETINTERFACE_ANY( plLinkInDoneMsg, plMessage ); + + void Read(hsStream* stream, hsResMgr* mgr) { IMsgRead(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { IMsgWrite(stream, mgr); } + + void ReadVersion(hsStream* stream, hsResMgr* mgr) { IMsgRead(stream, mgr); }; + void WriteVersion(hsStream* stream, hsResMgr* mgr) { IMsgWrite(stream, mgr); }; + +}; + +#endif // plLoadAgeMsg diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.cpp new file mode 100644 index 00000000..b7d77fe5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.cpp @@ -0,0 +1,225 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef NO_AV_MSGS +#ifndef SERVER + +#include "hsStream.h" +#include "plLoadAvatarMsg.h" +#include "hsResMgr.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnNetCommon/plSynchedObject.h" + +#include "../plAvatar/plAvatarTasks.h" + + +////////////////// +// PLLOADAVATARMSG +////////////////// + +// CTOR (default) +plLoadAvatarMsg::plLoadAvatarMsg() +: fIsPlayer(false), + fSpawnPoint(nil), + fInitialTask(nil), + fUserStr(nil) +{ +} + +// CTOR uoidToClone, requestorKey, userData, isPlayer, spawnPOint, initialTask +plLoadAvatarMsg::plLoadAvatarMsg(const plUoid &uoidToClone, const plKey &requestorKey, UInt32 userData, + hsBool isPlayer, const plKey &spawnPoint, plAvTask *initialTask, const char* userStr /*= nil*/) +: plLoadCloneMsg(uoidToClone, requestorKey, userData), + fIsPlayer(isPlayer), + fSpawnPoint(spawnPoint), + fInitialTask(initialTask), + fUserStr(nil) // setting to nil so SetUserStr doesn't try to nuke garbage +{ + SetUserStr(userStr); +} + +plLoadAvatarMsg::plLoadAvatarMsg(const plKey &existing, const plKey &requestor, UInt32 userData, + hsBool isPlayer, hsBool isLoading, const char* userStr /*= nil*/) +: plLoadCloneMsg(existing, requestor, userData, isLoading), + fIsPlayer(isPlayer), + fSpawnPoint(nil), + fInitialTask(nil), + fUserStr(nil) // setting to nil so SetUserStr doesn't try to nuke garbage +{ + SetUserStr(userStr); +} + + +// DTOR +plLoadAvatarMsg::~plLoadAvatarMsg() +{ + if (fUserStr) + { + delete [] fUserStr; + fUserStr = nil; + } +} + +void plLoadAvatarMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plLoadCloneMsg::Read(stream, mgr); + fIsPlayer = stream->ReadBool(); + fSpawnPoint = mgr->ReadKey(stream); + if(stream->ReadBool()) + { + fInitialTask = plAvTask::ConvertNoRef(mgr->ReadCreatable(stream)); + } + if (fUserStr) + { + delete [] fUserStr; + fUserStr = nil; + } + fUserStr = stream->ReadSafeString(); +} + +void plLoadAvatarMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plLoadCloneMsg::Write(stream, mgr); + stream->WriteBool(fIsPlayer); + mgr->WriteKey(stream, fSpawnPoint); + if(fInitialTask) + { + stream->WriteBool(true); + mgr->WriteCreatable(stream, fInitialTask); + } else { + stream->WriteBool(false); + } + stream->WriteSafeString(fUserStr); +} + +enum LoadAvatarMsgFlags +{ + kLoadAvatarMsgIsPlayer, + kLoadAvatarMsgSpawnPoint, + kLoadAvatarMsgUserStr, +}; + +void plLoadAvatarMsg::ReadVersion(hsStream* stream, hsResMgr* mgr) +{ + plLoadCloneMsg::ReadVersion(stream, mgr); + + hsBitVector contentFlags; + contentFlags.Read(stream); + + if (contentFlags.IsBitSet(kLoadAvatarMsgIsPlayer)) + fIsPlayer = stream->ReadBool(); + + if (contentFlags.IsBitSet(kLoadAvatarMsgSpawnPoint)) + fSpawnPoint = mgr->ReadKey(stream); + + if (fUserStr) + { + delete [] fUserStr; + fUserStr = nil; + } + if (contentFlags.IsBitSet(kLoadAvatarMsgUserStr)) + fUserStr = stream->ReadSafeString(); +} + +void plLoadAvatarMsg::WriteVersion(hsStream* stream, hsResMgr* mgr) +{ + plLoadCloneMsg::WriteVersion(stream, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kLoadAvatarMsgIsPlayer); + contentFlags.SetBit(kLoadAvatarMsgSpawnPoint); + contentFlags.SetBit(kLoadAvatarMsgUserStr); + contentFlags.Write(stream); + + // kLoadAvatarMsgIsPlayer + stream->WriteBool(fIsPlayer); + + // kLoadAvatarMsgSpawnPoint + mgr->WriteKey(stream, fSpawnPoint); + + // kLoadAvatarMsgUserStr + stream->WriteSafeString(fUserStr); +} + +// SETISPLAYER +void plLoadAvatarMsg::SetIsPlayer(bool is) +{ + fIsPlayer = is; +} + +// GETISPLAYER +hsBool plLoadAvatarMsg::GetIsPlayer() +{ + return fIsPlayer; +} + +// SETSPAWNPOINT +void plLoadAvatarMsg::SetSpawnPoint(const plKey &spawnPoint) +{ + fSpawnPoint = spawnPoint; +} + +// GETSPAWNPOINT +plKey plLoadAvatarMsg::GetSpawnPoint() +{ + return fSpawnPoint; +} + +// SETINITIALTASK +void plLoadAvatarMsg::SetInitialTask(plAvTask *initialTask) +{ + fInitialTask = initialTask; +} + +// GETINITIALTASK +plAvTask * plLoadAvatarMsg::GetInitialTask() +{ + return fInitialTask; +} + +// SETUSERSTR +void plLoadAvatarMsg::SetUserStr(const char *userStr) +{ + if (fUserStr) + delete [] fUserStr; + if (!userStr) + { + fUserStr = nil; + return; + } + + fUserStr = TRACKED_NEW char[strlen(userStr) + 1]; + strcpy(fUserStr, userStr); + fUserStr[strlen(userStr)] = '\0'; +} + +// GETUSERSTR +const char* plLoadAvatarMsg::GetUserStr() +{ + return fUserStr; +} + +#endif // ndef SERVER +#endif // ndef NO_AV_MSGS diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.h new file mode 100644 index 00000000..e2f79394 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.h @@ -0,0 +1,121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef NO_AV_MSGS +#ifndef SERVER + +#ifndef plLoadAvatarMsg_INC +#define plLoadAvatarMsg_INC + +#include "plLoadCloneMsg.h" +#include "hsUtils.h" +#include "../pnKeyedObject/plUoid.h" + +class plAvTask; + +// +// A msg which is sent to the networking system +// to cause a player to be loaded or unloaded +// +class plKey; +class hsStream; +class hsResMgr; + +// not sure if we need this yet, but it's already in the index so here's just enough +// implementation to keep the compiler happy. +class plLoadAvatarMsg : public plLoadCloneMsg +{ +public: + plLoadAvatarMsg(); + /** Canonical constructor. If you're trying to initiate a clone, this is + the one you want to use. + These messages are *always* sent to the net client manager. You can't + address them. + After they are received on the remote machines, they are forwarded + the remote versions of the requestor. + + \param uoidToClone - Specifies the template that will be cloned. + \param requestorKey - The key of the object that is requesting the clone. It's + strongly recommended that this be a local object, so that we don't get the same + requestor creating multiple clones by starting the process on several machines. + \param userData - Whatever you want. Will be propagated to the requestor after cloning. + \param isPlayer - this is a player, not an NPC + \param spawnPoint - warp to this spot after loading + \param initialTask - queue up this task after loading (and spawning) + \param userStr - a string that the user can set + */ + plLoadAvatarMsg(const plUoid &uoidToClone, const plKey &requestorKey, UInt32 userData, + hsBool isPlayer, const plKey &spawnPoint, plAvTask *initialTask, const char *userStr = nil); + + /** Use this form if you're sending a message about an existing clone -- either + to propagate it to other machines or to tell them to unload it. + \param existing - The key to the clone you want to unload + \param requestorKey - The key of the object that is requesting the clone. It's + strongly recommended that this be a local object, so that we don't get the same + requestor creating multiple clones by starting the process on several machines. + \param userData - Whatever you want. Will be propagated to the requestor after cloning. + \param isPlayer - this is a player, not an NPC + \param isLoading - Are we loading or unloading? + \param userStr - a string that the user can set + */ + plLoadAvatarMsg(const plKey &existing, const plKey &requestorKey, UInt32 userData, + hsBool isPlayer, hsBool isLoading, const char *userStr = nil); + + virtual ~plLoadAvatarMsg(); + + void SetIsPlayer(bool is); + hsBool GetIsPlayer(); + + void SetSpawnPoint(const plKey &spawnSceneObjectKey); + plKey GetSpawnPoint(); + + void SetInitialTask(plAvTask *task); + plAvTask * GetInitialTask(); + + void SetUserStr(const char *userStr); + const char* GetUserStr(); + + CLASSNAME_REGISTER(plLoadAvatarMsg); + GETINTERFACE_ANY(plLoadAvatarMsg, plLoadCloneMsg); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + void ReadVersion(hsStream* stream, hsResMgr* mgr); + void WriteVersion(hsStream* stream, hsResMgr* mgr); + +protected: + hsBool fIsPlayer; + plKey fSpawnPoint; + plAvTask *fInitialTask; + char *fUserStr; +}; + + +#endif // plLoadAvatarMsg_INC + + +#endif // ndef SERVER +#endif // ndef NO_AV_MSGS diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadCloneMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadCloneMsg.cpp new file mode 100644 index 00000000..c3b3ced2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadCloneMsg.cpp @@ -0,0 +1,259 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef NO_AV_MSGS +#ifndef SERVER + +// singular +#include "plLoadAvatarMsg.h" + +// global +#include "hsResMgr.h" + +// other +#include "../pnNetCommon/plNetApp.h" + + + +// CTOR / default +plLoadCloneMsg::plLoadCloneMsg() +: fValidMsg(false), + fOriginatingPlayerID(0), + fTriggerMsg(nil) +{ + SetBCastFlag(plMessage::kNetPropagate); +}; + +// CTOR uoidToClone, requestorKey, userData, isLoading +// this form is for creating new clones +plLoadCloneMsg::plLoadCloneMsg(const plUoid &uoidToClone, const plKey &requestorKey, UInt32 userData) + : fRequestorKey(requestorKey), + fUserData(userData), + fValidMsg(false), + fTriggerMsg(nil), + fIsLoading(true) // this constructor form is only used for loading +{ + SetBCastFlag(plMessage::kNetPropagate); + + hsKeyedObject * koRequestor = fRequestorKey->ObjectIsLoaded(); + if(koRequestor) + { + plNetClientApp *app = plNetClientApp::GetInstance(); + plKey originalKey = hsgResMgr::ResMgr()->FindKey(uoidToClone); + + if(originalKey) + { + // all is well. finish it off. + fCloneKey = hsgResMgr::ResMgr()->CloneKey(originalKey); + fOriginatingPlayerID = app->GetPlayerID(); + fValidMsg = true; + this->AddReceiver(plNetApp::GetInstance()->GetKey()); + } else { + char buffer[128]; + sprintf(buffer, "Can't find key named %s", uoidToClone.GetObjectName()); + hsAssert(0, buffer); + } + } else { + hsStatusMessage("Clone requestor is not loaded."); + } +} + +// CTOR existing, requestor, userData, isLoading +// this form is for unloading or other operations on existing clones +plLoadCloneMsg::plLoadCloneMsg(const plKey &existing, const plKey &requestor, UInt32 userData, hsBool isLoading) +: fCloneKey(existing), + fRequestorKey(requestor), + fUserData(userData), + fValidMsg(true), + fTriggerMsg(nil), + fIsLoading(isLoading) +{ + if (plNetApp::GetInstance()) + { + SetBCastFlag(plMessage::kNetPropagate); + AddReceiver(plNetApp::GetInstance()->GetKey()); + } + if (plNetClientApp::GetInstance()) + fOriginatingPlayerID = plNetClientApp::GetInstance()->GetPlayerID(); + hsAssert(fRequestorKey->ObjectIsLoaded(), "Clone (unloading) requestor is not loaded."); +} + +plLoadCloneMsg::~plLoadCloneMsg() +{ + hsRefCnt_SafeUnRef(fTriggerMsg); +} + +// READ +void plLoadCloneMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + fCloneKey = mgr->ReadKey(stream); + fRequestorKey = mgr->ReadKey(stream); + fOriginatingPlayerID = stream->ReadSwap32(); + fUserData = stream->ReadSwap32(); + fValidMsg = stream->ReadBool(); + fIsLoading = stream->ReadBool(); + fTriggerMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); +} + +// WRITE +void plLoadCloneMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream,mgr); + mgr->WriteKey(stream, fCloneKey); + mgr->WriteKey(stream, fRequestorKey); + stream->WriteSwap32(fOriginatingPlayerID); + stream->WriteSwap32(fUserData); + stream->WriteBool(fValidMsg); + stream->WriteBool(fIsLoading); + mgr->WriteCreatable(stream, fTriggerMsg); +} + +enum LoadCloneMsgFlags +{ + kLoadCloneMsgCloneKey, + kLoadCloneMsgRequestorKey, + kLoadCloneMsgOrigPlayerID, + kLoadCloneMsgUserData, + kLoadCloneMsgValidMsg, + kLoadCloneMsgIsLoading, + kLoadCloneMsgTriggerMsg, +}; + +void plLoadCloneMsg::ReadVersion(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgReadVersion(stream, mgr); + + hsBitVector contentFlags; + contentFlags.Read(stream); + + if (contentFlags.IsBitSet(kLoadCloneMsgCloneKey)) + fCloneKey = mgr->ReadKey(stream); + + if (contentFlags.IsBitSet(kLoadCloneMsgRequestorKey)) + fRequestorKey = mgr->ReadKey(stream); + + if (contentFlags.IsBitSet(kLoadCloneMsgOrigPlayerID)) + fOriginatingPlayerID = stream->ReadSwap32(); + + if (contentFlags.IsBitSet(kLoadCloneMsgUserData)) + fUserData = stream->ReadSwap32(); + + if (contentFlags.IsBitSet(kLoadCloneMsgValidMsg)) + fValidMsg = stream->ReadBool(); + + if (contentFlags.IsBitSet(kLoadCloneMsgIsLoading)) + fIsLoading = stream->ReadBool(); + + if (contentFlags.IsBitSet(kLoadCloneMsgTriggerMsg)) + fTriggerMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); +} + +void plLoadCloneMsg::WriteVersion(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWriteVersion(stream,mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kLoadCloneMsgCloneKey); + contentFlags.SetBit(kLoadCloneMsgRequestorKey); + contentFlags.SetBit(kLoadCloneMsgOrigPlayerID); + contentFlags.SetBit(kLoadCloneMsgUserData); + contentFlags.SetBit(kLoadCloneMsgValidMsg); + contentFlags.SetBit(kLoadCloneMsgIsLoading); + contentFlags.SetBit(kLoadCloneMsgTriggerMsg); + contentFlags.Write(stream); + + // kLoadCloneMsgCloneKey + mgr->WriteKey(stream, fCloneKey); + // kLoadCloneMsgRequestorKey + mgr->WriteKey(stream, fRequestorKey); + // kLoadCloneMsgOrigPlayerID + stream->WriteSwap32(fOriginatingPlayerID); + // kLoadCloneMsgUserData + stream->WriteSwap32(fUserData); + // kLoadCloneMsgValidMsg + stream->WriteBool(fValidMsg); + // kLoadCloneMsgIsLoading + stream->WriteBool(fIsLoading); + // kLoadCloneMsgTriggerMSg + mgr->WriteCreatable(stream, fTriggerMsg); +} + +// GETCLONEKEY +plKey plLoadCloneMsg::GetCloneKey() +{ + return fCloneKey; +} + +// GETREQUESTORKEY +plKey plLoadCloneMsg::GetRequestorKey() +{ + return fRequestorKey; +} + +// ISVALIDMESSAGE +hsBool plLoadCloneMsg::IsValidMessage() +{ + return fValidMsg; +} + +// GETUSERDATA +UInt32 plLoadCloneMsg::GetUserData() +{ + return fUserData; +} + +// GETORIGINATINGPLAYERID +UInt32 plLoadCloneMsg::GetOriginatingPlayerID() +{ + return fOriginatingPlayerID; +} + +void plLoadCloneMsg::SetOriginatingPlayerID(UInt32 playerId) +{ + fOriginatingPlayerID = playerId; +} + +hsBool plLoadCloneMsg::GetIsLoading() +{ + return fIsLoading; +} + +void plLoadCloneMsg::SetTriggerMsg(plMessage *msg) +{ + if (fTriggerMsg != nil) + hsRefCnt_SafeUnRef(fTriggerMsg); + + hsRefCnt_SafeRef(msg); + fTriggerMsg = msg; +} + +plMessage *plLoadCloneMsg::GetTriggerMsg() +{ + return fTriggerMsg; +} + +#endif // ndef SERVER +#endif // ndef NO_AV_MSGS diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadCloneMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadCloneMsg.h new file mode 100644 index 00000000..63fc6449 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plLoadCloneMsg.h @@ -0,0 +1,119 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef NO_AV_MSGS +#ifndef SERVER + +#ifndef plLoadCloneMsgMsg_INC +#define plLoadCloneMsgMsg_INC + + +#include "../pnMessage/plMessage.h" +// #include "hsUtils.h" +#include "../pnKeyedObject/plUoid.h" + +/** \class plLoadCloneMsg + Tell the net client manager to allocate a new object based on copying an + existing, statically keyed object. + On the initiating machine, this will synthesize a new key and load the object. + On the remote machines, it will use the (enclosed) new key and load the object. + On both machines, the requestor will be notified after the object is loaded. + */ +class plLoadCloneMsg : public plMessage +{ +public: + /** Default constructor. only useful for sending over the network, prior + to reading and writing. The clone message needs to synthesize its key + to do anything, and this constructor doesn't do that, because it doesn't + have enough information. */ + plLoadCloneMsg(); + + /** Canonical constructor. If you're trying to initiate a clone, this is + the one you want to use. + These messages are *always* sent to the net client manager. You can't + address them. + After they are received on the remote machines, they are forwarded + the remote versions of the requestor. + + \param uoidToClone - Specifies the template that will be cloned. + \param requestorKey - The key of the object that is requesting the clone. It's + strongly recommended that this be a local object, so that we don't get the same + requestor creating multiple clones by starting the process on several machines. + \param userData - Whatever you want. Will be propagated to the requestor after cloning. + + */ + plLoadCloneMsg(const plUoid &uoidToClone, const plKey &requestorKey, UInt32 userData); + + /** This constructor form is for when you want to send a clone message based on + an existing cloned object. The two primary uses of this are: + - unload an existing clone + - replicate a locally existing clone to other machines. You'll need + to set up the network propagation parameters yourself. + \param existing - The key to the clone you want to work on + \param requestor - Will be notified when the (un)clone is processed) + \param userData - Whatever you want. Will be propagated to the requestor. + \param isLoading - Are we loading or unloading? + */ + plLoadCloneMsg(const plKey &existing, const plKey &requestor, UInt32 userData, hsBool isLoading); + + virtual ~plLoadCloneMsg(); + + CLASSNAME_REGISTER(plLoadCloneMsg); + GETINTERFACE_ANY(plLoadCloneMsg, plMessage); + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + void ReadVersion(hsStream* stream, hsResMgr* mgr); + void WriteVersion(hsStream* stream, hsResMgr* mgr); + + plKey GetCloneKey(); + plKey GetRequestorKey(); + hsBool IsValidMessage(); + UInt32 GetUserData(); + UInt32 GetOriginatingPlayerID(); + void SetOriginatingPlayerID(UInt32 playerId); + hsBool GetIsLoading(); + void SetTriggerMsg(plMessage *msg); + plMessage *GetTriggerMsg(); + + +protected: + plKey fCloneKey; // the key that will be loaded + plKey fRequestorKey; // forward the message to this guy after the clone is created + hsBool fValidMsg; // only gets set if the message built successfully + UInt32 fUserData; // let requestors send some data to their remote versions + UInt32 fOriginatingPlayerID; // network / player id of the client initiating the request + hsBool fIsLoading; // true if we're loading; false if we're unloading + plMessage *fTriggerMsg; // Handy place to store a message that caused you to request a clone, + // so you can see it and continue processing once your clone is loaded. +}; + +#endif + + +#endif // ndef SERVER +#endif // ndef NO_AV_MSGS diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatRefMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatRefMsg.h new file mode 100644 index 00000000..17fb1822 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatRefMsg.h @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMatRefMsg_inc +#define plMatRefMsg_inc + +#include "../pnMessage/plRefMsg.h" + +class plMatRefMsg : public plGenRefMsg +{ +public: + enum { + kLayer = 0x1, + kInsert = 0x2, + kPiggyBack = 0x4 + }; + + plMatRefMsg() {} + plMatRefMsg(const plKey &r, UInt8 flags, Int8 which, Int8 type) : plGenRefMsg(r, flags, which, type) {} + + CLASSNAME_REGISTER( plMatRefMsg ); + GETINTERFACE_ANY( plMatRefMsg, plGenRefMsg ); +}; + +#endif // plMatRefMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatrixUpdateMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatrixUpdateMsg.cpp new file mode 100644 index 00000000..5b3bf50a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatrixUpdateMsg.cpp @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plMatrixUpdateMsg.h" +#include "hsResMgr.h" +#include "hsStream.h" + +void plMatrixUpdateMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + fMatrix.Read(stream); +} + +void plMatrixUpdateMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + fMatrix.Write(stream); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatrixUpdateMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatrixUpdateMsg.h new file mode 100644 index 00000000..aa297740 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMatrixUpdateMsg.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMatrixUpdateMsg_inc +#define plMatrixUpdateMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsMatrix44.h" + +class plMatrixUpdateMsg : public plMessage +{ + +public: + plMatrixUpdateMsg(){SetBCastFlag(plMessage::kPropagateToModifiers);} + plMatrixUpdateMsg(const plKey &s, + const plKey &r, + const double* t){SetBCastFlag(plMessage::kPropagateToModifiers);} + ~plMatrixUpdateMsg(){;} + + CLASSNAME_REGISTER( plMatrixUpdateMsg ); + GETINTERFACE_ANY( plMatrixUpdateMsg, plMessage ); + + void Read( hsStream* s, hsResMgr* mgr ); + void Write( hsStream* s, hsResMgr* mgr ); + + + hsMatrix44 fMatrix; + +}; + +#endif plMatrixUpdateMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMemberUpdateMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMemberUpdateMsg.h new file mode 100644 index 00000000..e97fe4e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMemberUpdateMsg.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plMemberUpdateMsg_INC +#define plMemberUpdateMsg_INC + +#include "../pnMessage/plMessage.h" +#include "hsUtils.h" + +// +// A msg sent locally when remote players have joined or left the game +// (whenever the netClientMgr's remote player list changes). +// Also sent initially when we first join a game. +// +class plMemberUpdateMsg : public plMessage +{ +public: + CLASSNAME_REGISTER( plMemberUpdateMsg ); + GETINTERFACE_ANY( plMemberUpdateMsg, plMessage ); + + plMemberUpdateMsg() { SetBCastFlag( kBCastByExactType ); } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); } +}; + +#endif // plMemberUpdateMsg diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMeshRefMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMeshRefMsg.h new file mode 100644 index 00000000..f066251d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMeshRefMsg.h @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMeshRefMsg_inc +#define plMeshRefMsg_inc + +#include "../pnMessage/plRefMsg.h" +#include "hsStream.h" + +class hsResMgr; + +class plMeshRefMsg : public plRefMsg +{ +public: + enum { + kVertexPool = 1, + kMaterial = 2 + }; + + plMeshRefMsg() : fType(-1), fWhich(-1) {} + plMeshRefMsg(const plKey &r, int which, int type) : plRefMsg(r, kOnCreate), fWhich(which), fType(type) {} + + CLASSNAME_REGISTER( plMeshRefMsg ); + GETINTERFACE_ANY( plMeshRefMsg, plRefMsg ); + + UInt8 fType; + UInt8 fWhich; + + // IO - not really applicable to ref msgs, but anyway + virtual void Read(hsStream* stream, hsResMgr* mgr) + { + plRefMsg::Read(stream, mgr); + stream->ReadSwap(&fType); + stream->ReadSwap(&fWhich); + } + + virtual void Write(hsStream* stream, hsResMgr* mgr) + { + plRefMsg::Write(stream, mgr); + stream->WriteSwap(fType); + stream->WriteSwap(fWhich); + } + +}; + +#endif // plMeshRefMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h new file mode 100644 index 00000000..887fefc8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h @@ -0,0 +1,365 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMessageCreatable_inc +#define plMessageCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plInterestingPing.h" + +REGISTER_CREATABLE( plInterestingModMsg ); +REGISTER_CREATABLE( plInterestingPing ); + +#include "plLayRefMsg.h" + +REGISTER_CREATABLE( plLayRefMsg ); + +#include "plMatRefMsg.h" + +REGISTER_CREATABLE( plMatRefMsg ); + +#include "plMeshRefMsg.h" + +REGISTER_CREATABLE( plMeshRefMsg ); + +#include "plLOSRequestMsg.h" + +REGISTER_CREATABLE( plLOSRequestMsg ); + +#include "plLOSHitMsg.h" + +REGISTER_CREATABLE( plLOSHitMsg ); + +#include "plActivatorMsg.h" + +REGISTER_CREATABLE( plActivatorMsg ); + +#include "plCondRefMsg.h" + +REGISTER_CREATABLE( plCondRefMsg ); + +#include "plAnimCmdMsg.h" + +REGISTER_CREATABLE( plAnimCmdMsg ); +REGISTER_CREATABLE( plAGCmdMsg ); +REGISTER_CREATABLE( plAGInstanceCallbackMsg ); +REGISTER_CREATABLE( plAGDetachCallbackMsg ); + +#include "plParticleUpdateMsg.h" + +REGISTER_CREATABLE( plParticleUpdateMsg ); +REGISTER_CREATABLE( plParticleTransferMsg ); +REGISTER_CREATABLE( plParticleKillMsg ); +REGISTER_CREATABLE( plParticleFlockMsg ); + +#include "plInputEventMsg.h" + +REGISTER_CREATABLE( plInputEventMsg ); +REGISTER_CREATABLE( plControlEventMsg ); +REGISTER_CREATABLE( plKeyEventMsg ); +REGISTER_CREATABLE( plDebugKeyEventMsg ); +REGISTER_CREATABLE( plMouseEventMsg ); +REGISTER_CREATABLE( plIMouseXEventMsg ); +REGISTER_CREATABLE( plIMouseYEventMsg ); +REGISTER_CREATABLE( plIMouseBEventMsg ); +REGISTER_CREATABLE( plAvatarInputStateMsg ); + +#include "plPickedMsg.h" + +REGISTER_CREATABLE( plPickedMsg ); + +#include "plCollideMsg.h" + +REGISTER_CREATABLE( plCollideMsg ); + +#include "plMatrixUpdateMsg.h" + +REGISTER_CREATABLE( plMatrixUpdateMsg ); + +#include "plRenderMsg.h" + +REGISTER_CREATABLE( plRenderMsg ); +REGISTER_CREATABLE( plPreResourceMsg ); + +#include "plTimerCallbackMsg.h" + +REGISTER_CREATABLE( plTimerCallbackMsg ); + +#include "plSpawnModMsg.h" + +REGISTER_CREATABLE( plSpawnModMsg ); + +#include "plSpawnRequestMsg.h" + +REGISTER_CREATABLE( plSpawnRequestMsg ); + +#include "plNodeCleanupMsg.h" + +REGISTER_CREATABLE( plNodeCleanupMsg ); + +#include "plDeviceRecreateMsg.h" + +REGISTER_CREATABLE( plDeviceRecreateMsg ); + +#include "plLightRefMsg.h" + +REGISTER_CREATABLE( plLightRefMsg ); + +#include "plSimInfluenceMsg.h" + +// REGISTER_CREATABLE( plSimInfluenceMsg ); +// REGISTER_CREATABLE( plForceMsg ); +// REGISTER_CREATABLE( plOffsetForceMsg ); +// REGISTER_CREATABLE( plTorqueMsg ); +// REGISTER_CREATABLE( plImpulseMsg ); +// REGISTER_CREATABLE( plOffsetImpulseMsg ); +// REGISTER_CREATABLE( plAngularImpulseMsg ); +// REGISTER_CREATABLE( plDampMsg ); +// REGISTER_CREATABLE( plShiftMassMsg ); + +#include "plSimStateMsg.h" + +// REGISTER_CREATABLE( plSimStateMsg ); +// REGISTER_CREATABLE( plFreezeMsg ); +// REGISTER_CREATABLE( plEventGroupMsg ); +// REGISTER_CREATABLE( plEventGroupEnableMsg ); +// REGISTER_CREATABLE( plSuspendEventMsg ); +REGISTER_CREATABLE( plSubWorldMsg ); + +#include "plLinearVelocityMsg.h" +REGISTER_CREATABLE( plLinearVelocityMsg ); + +#include "plAngularVelocityMsg.h" +REGISTER_CREATABLE( plAngularVelocityMsg ); + +#include "plRenderRequestMsg.h" + +REGISTER_CREATABLE( plRenderRequestMsg ); +REGISTER_CREATABLE( plRenderRequestAck ); + +#include "plLinkToAgeMsg.h" +REGISTER_CREATABLE(plLinkToAgeMsg); +REGISTER_CREATABLE(plLinkingMgrMsg); +REGISTER_CREATABLE(plLinkCallbackMsg); +REGISTER_CREATABLE(plLinkEffectsTriggerMsg); +REGISTER_CREATABLE(plLinkEffectBCMsg); +REGISTER_CREATABLE(plLinkEffectsTriggerPrepMsg); +REGISTER_CREATABLE(plLinkEffectPrepBCMsg); +REGISTER_CREATABLE(plPseudoLinkEffectMsg); +REGISTER_CREATABLE(plPseudoLinkAnimTriggerMsg); +REGISTER_CREATABLE(plPseudoLinkAnimCallbackMsg); + +#include "plListenerMsg.h" +REGISTER_CREATABLE(plListenerMsg); +REGISTER_CREATABLE(plSetListenerMsg); + +#include "plTransitionMsg.h" +REGISTER_CREATABLE(plTransitionMsg); + +#include "plConsoleMsg.h" +REGISTER_CREATABLE(plConsoleMsg); + +#include "plLoadAgeMsg.h" +REGISTER_CREATABLE(plLoadAgeMsg); +REGISTER_CREATABLE(plLinkOutUnloadMsg); + +#include "plResponderMsg.h" +REGISTER_CREATABLE(plResponderMsg); + +#include "plOneShotMsg.h" +REGISTER_CREATABLE(plOneShotMsg); + +#include "plTriggerMsg.h" +REGISTER_CREATABLE( plTriggerMsg ); + +#ifndef NO_AV_MSGS +#include "plAvatarMsg.h" +REGISTER_CREATABLE( plAvatarMsg ); +REGISTER_CREATABLE( plArmatureUpdateMsg ); +REGISTER_CREATABLE( plAvatarSetTypeMsg ); +REGISTER_CREATABLE( plAvTaskMsg ); +REGISTER_CREATABLE( plAvSeekMsg ); +REGISTER_CREATABLE( plAvOneShotMsg ); +REGISTER_CREATABLE( plAvBrainGenericMsg ); + +#ifndef SERVER +REGISTER_CREATABLE( plAvPushBrainMsg ); +REGISTER_CREATABLE( plAvPopBrainMsg ); +#endif // ndef SERVER + +REGISTER_CREATABLE( plAvatarStealthModeMsg ); +REGISTER_CREATABLE( plAvatarBehaviorNotifyMsg ); +REGISTER_CREATABLE( plAvatarOpacityCallbackMsg ); +REGISTER_CREATABLE( plAvTaskSeekDoneMsg ); +REGISTER_CREATABLE( plAvatarSpawnNotifyMsg ); +REGISTER_CREATABLE( plAvatarPhysicsEnableCallbackMsg ); +#endif // ndef NO_AV_MSGS + +#include "plMultistageMsg.h" +REGISTER_CREATABLE( plMultistageModMsg ); + +#include "plExcludeRegionMsg.h" +REGISTER_CREATABLE(plExcludeRegionMsg); + +#include "plDynamicTextMsg.h" +REGISTER_CREATABLE(plDynamicTextMsg); + +#include "plInputIfaceMgrMsg.h" +REGISTER_CREATABLE(plInputIfaceMgrMsg); + +#include "plRoomLoadNotifyMsg.h" +REGISTER_CREATABLE(plRoomLoadNotifyMsg); + +#include "plMemberUpdateMsg.h" +REGISTER_CREATABLE(plMemberUpdateMsg); + +#include "plAgeLoadedMsg.h" +REGISTER_CREATABLE(plAgeLoadedMsg); +REGISTER_CREATABLE(plAgeLoaded2Msg); +REGISTER_CREATABLE(plAgeBeginLoadingMsg); +REGISTER_CREATABLE(plInitialAgeStateLoadedMsg); +REGISTER_CREATABLE(plLinkInDoneMsg) + +#include "plReplaceGeometryMsg.h" +REGISTER_CREATABLE(plReplaceGeometryMsg); +REGISTER_CREATABLE(plSwapSpansRefMsg); + +#include "plShadowCastMsg.h" +REGISTER_CREATABLE(plShadowCastMsg); + +#include "plResMgrHelperMsg.h" +REGISTER_CREATABLE(plResMgrHelperMsg); + +#include "plBulletMsg.h" +REGISTER_CREATABLE(plBulletMsg); + +#include "plDynaDecalEnableMsg.h" +REGISTER_CREATABLE(plDynaDecalEnableMsg); + +#include "plDynamicEnvMapMsg.h" +REGISTER_CREATABLE(plDynamicEnvMapMsg); + +#include "plAvatarFootMsg.h" +REGISTER_CREATABLE(plAvatarFootMsg); + +#include "plRippleShapeMsg.h" +REGISTER_CREATABLE(plRippleShapeMsg); + +#include "plNetOwnershipMsg.h" +REGISTER_CREATABLE(plNetOwnershipMsg); + +#include "plCCRMessageCreatable.h" // kept separately for selective server include + +#include "plConnectedToVaultMsg.h" +REGISTER_CREATABLE(plConnectedToVaultMsg); + +#include "plClimbMsg.h" +REGISTER_CREATABLE(plClimbMsg); + +#include "plNetVoiceListMsg.h" +REGISTER_CREATABLE(plNetVoiceListMsg); + +#include "plSwimMsg.h" +REGISTER_CREATABLE(plSwimMsg); + +#include "plVaultNotifyMsg.h" +REGISTER_CREATABLE(plVaultNotifyMsg); + +#include "plSynchEnableMsg.h" +REGISTER_CREATABLE(plSynchEnableMsg); + +#include "plMovieMsg.h" +REGISTER_CREATABLE(plMovieMsg); + +#include "plCaptureRenderMsg.h" +REGISTER_CREATABLE(plCaptureRenderMsg); + +#include "plClimbEventMsg.h" +REGISTER_CREATABLE(plClimbEventMsg); + +#include "plNetCommMsgs.h" +REGISTER_CREATABLE(plNetCommAuthConnectedMsg); +REGISTER_CREATABLE(plNetCommAuthMsg); +REGISTER_CREATABLE(plNetCommFileListMsg); +REGISTER_CREATABLE(plNetCommFileDownloadMsg); +REGISTER_CREATABLE(plNetCommLinkToAgeMsg); +REGISTER_CREATABLE(plNetCommPlayerListMsg); +REGISTER_CREATABLE(plNetCommActivePlayerMsg); +REGISTER_CREATABLE(plNetCommCreatePlayerMsg); +REGISTER_CREATABLE(plNetCommDeletePlayerMsg); +REGISTER_CREATABLE(plNetCommPublicAgeListMsg); +REGISTER_CREATABLE(plNetCommPublicAgeMsg); +REGISTER_CREATABLE(plNetCommRegisterAgeMsg); + +#include "plPreloaderMsg.h" +REGISTER_CREATABLE(plPreloaderMsg); + +#include "plNetClientMgrMsg.h" +REGISTER_CREATABLE(plNetClientMgrMsg); + +#include "plNCAgeJoinerMsg.h" +REGISTER_CREATABLE(plNCAgeJoinerMsg); + +#include "plAccountUpdateMsg.h" +REGISTER_CREATABLE(plAccountUpdateMsg); + +#include "plRideAnimatedPhysMsg.h" +REGISTER_CREATABLE(plRideAnimatedPhysMsg); + +#ifndef SERVER +#ifndef NO_AV_MSGS +#include "plAIMsg.h" +REGISTER_CREATABLE(plAIMsg); +REGISTER_CREATABLE(plAIBrainCreatedMsg); +REGISTER_CREATABLE(plAIArrivedAtGoalMsg); +#endif // NO_AV_MSGS +#endif // SERVER + +/***************************************************************************** +* +* Messages excluded from SERVER build, and the NoAvMsgs build configurations +* +***/ + +#ifndef NO_AV_MSGS +#ifndef SERVER + +# include "plLoadCloneMsg.h" +REGISTER_CREATABLE(plLoadCloneMsg); + +# include "plLoadAvatarMsg.h" +REGISTER_CREATABLE(plLoadAvatarMsg); + +# include "plAvCoopMsg.h" +REGISTER_CREATABLE(plAvCoopMsg); + +#endif // ndef SERVER +#endif // ndef NO_AV_MSGS + + +#endif // plMessageCreatable_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMovieMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMovieMsg.h new file mode 100644 index 00000000..bbafcbeb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMovieMsg.h @@ -0,0 +1,180 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMovieMsg_inc +#define plMovieMsg_inc + +#include "../pnMessage/plMessage.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "hsPoint2.h" +#include "hsTemplates.h" + +class plMovieMsg : public plMessage +{ +public: + enum + { + kIgnore = 0x0, + kStart = 0x1, + kPause = 0x2, + kResume = 0x4, + kStop = 0x8, + kMove = 0x10, // Call SetCenter() or default is 0,0 + kScale = 0x20, // Call SetScale() or default is 1,1 + kColor = 0x40, // Call SetColor() or default is 1,1,1 + kVolume = 0x80, // Call SetVolume() or default is 1 + kOpacity = 0x100, // Call SetOpacity() or default is 1 + kColorAndOpacity = 0x200, // Call SetColor() or default is 1,1,1,1 + kMake = 0x400, //Installs, but doesn't play until kStart + kAddCallbacks = 0x800, // Call AddCallback() for each callback message + kFadeIn = 0x1000, // Call SetFadeInSecs() and SetFadeInColor() or defs are 0 and 0,0,0,0 + kFadeOut = 0x2000 // Call SetFadeOutSecs() and SetFadeOutColor() or defs are 0 and 0,0,0,0 + }; +protected: + + hsPoint2 fCenter; + + hsPoint2 fScale; + + hsColorRGBA fColor; + + hsColorRGBA fFadeInColor; + hsScalar fFadeInSecs; + + hsColorRGBA fFadeOutColor; + hsScalar fFadeOutSecs; + + hsScalar fVolume; + + char* fFileName; + + UInt16 fCmd; + + hsTArray fCallbacks; + +public: + plMovieMsg(const char* n, UInt16 cmd) + : plMessage(nil, nil, nil) + { + fFileName = hsStrcpy(n); + SetCmd(cmd).MakeDefault(); + } + plMovieMsg() : fFileName(nil), fCmd(kIgnore) + { + MakeDefault(); + } + virtual ~plMovieMsg() + { + delete [] fFileName; + int i; + for( i = 0; i < fCallbacks.GetCount(); i++ ) + { + hsRefCnt_SafeUnRef(fCallbacks[i]); + } + } + + CLASSNAME_REGISTER( plMovieMsg ); + GETINTERFACE_ANY( plMovieMsg, plMessage ); + + plMovieMsg& MakeDefault() + { + SetCenter(0,0); + SetScale(1.f,1.f); + SetColor(1.f, 1.f, 1.f, 1.f); + SetFadeInSecs(0); + SetFadeInColor(0, 0, 0, 0); + SetFadeOutSecs(0); + SetFadeOutColor(0, 0, 0, 0); + SetVolume(1.f); + SetBCastFlag(kBCastByType); + return *this; + } + + // Make sure you set at least one command, and set appropriate params for the command + UInt16 GetCmd() const { return fCmd; } + plMovieMsg& SetCmd(UInt16 c) { fCmd = c; return *this; } + + // Center 0,0 is center of screen, 1,1 is Upper-Right, -1,-1 is Lower-Left, etc. + const hsPoint2& GetCenter() const { return fCenter; } + hsScalar GetCenterX() const { return fCenter.fX; } + hsScalar GetCenterY() const { return fCenter.fY; } + + plMovieMsg& SetCenter(const hsPoint2& p) { fCenter = p; return *this; } + plMovieMsg& SetCenter(hsScalar x, hsScalar y) { fCenter.Set(x, y); return *this; } + plMovieMsg& SetCenterX(hsScalar x) { fCenter.fX = x; return *this; } + plMovieMsg& SetCenterY(hsScalar y) { fCenter.fY = y; return *this; } + + // Scale of 1.0 matches movie pixel to screen pixel (whatever the resolution). + // Scale of 2.0 doubles each movie pixel across 2 screen pixels. + // Etc. + const hsPoint2& GetScale() const { return fScale; } + hsScalar GetScaleX() const { return fScale.fX; } + hsScalar GetScaleY() const { return fScale.fY; } + + plMovieMsg& SetScale(const hsPoint2& p) { fScale = p; return *this; } + plMovieMsg& SetScale(hsScalar x, hsScalar y) { fScale.Set(x, y); return *this; } + plMovieMsg& SetScaleX(hsScalar x) { fScale.fX = x; return *this; } + plMovieMsg& SetScaleY(hsScalar y) { fScale.fY = y; return *this; } + + // Include the movie folder, e.g. "avi/porno.bik" + // String is copied, not pointer copy. + const char* GetFileName() const { return fFileName; } + plMovieMsg& SetFileName(const char* n) { delete [] fFileName; fFileName = hsStrcpy(n); return *this; } + + // Color is mostly useful for alpha fade up and down. + const hsColorRGBA& GetColor() const { return fColor; } + plMovieMsg& SetColor(const hsColorRGBA& c) { fColor = c; return *this; } + plMovieMsg& SetColor(hsScalar r, hsScalar g, hsScalar b, hsScalar a) { fColor.Set(r,g,b,a); return *this; } + plMovieMsg& SetOpacity(hsScalar a) { return SetColor(1.f, 1.f, 1.f, a); } + + // Or the auto matic fades + const hsColorRGBA& GetFadeInColor() const { return fFadeInColor; } + plMovieMsg& SetFadeInColor(const hsColorRGBA& c) { fFadeInColor = c; return *this; } + plMovieMsg& SetFadeInColor(hsScalar r, hsScalar g, hsScalar b, hsScalar a) { fFadeInColor.Set(r,g,b,a); return *this; } + + hsScalar GetFadeInSecs() const { return fFadeInSecs; } + plMovieMsg& SetFadeInSecs(hsScalar s) { fFadeInSecs = s; return *this; } + + const hsColorRGBA& GetFadeOutColor() const { return fFadeOutColor; } + plMovieMsg& SetFadeOutColor(const hsColorRGBA& c) { fFadeOutColor = c; return *this; } + plMovieMsg& SetFadeOutColor(hsScalar r, hsScalar g, hsScalar b, hsScalar a) { fFadeOutColor.Set(r,g,b,a); return *this; } + + hsScalar GetFadeOutSecs() const { return fFadeOutSecs; } + plMovieMsg& SetFadeOutSecs(hsScalar s) { fFadeOutSecs = s; return *this; } + + // Volume is on scale of 0=muted to 1=full + hsScalar GetVolume() const { return fVolume; } + plMovieMsg& SetVolume(hsScalar v) { fVolume = v; return *this; } + + plMovieMsg& AddCallback(plMessage* msg) { hsRefCnt_SafeRef(msg); fCallbacks.Append(msg); return *this; } + UInt32 GetNumCallbacks() const { return fCallbacks.GetCount(); } + plMessage* GetCallback(int i) const { return fCallbacks[i]; } + + virtual void Read(hsStream* s, hsResMgr* mgr) { hsAssert(false, "Not for I/O"); plMessage::IMsgRead(s, mgr); } + virtual void Write(hsStream* s, hsResMgr* mgr) { hsAssert(false, "Not for I/O"); plMessage::IMsgWrite(s, mgr); } +}; + +#endif // plMovieMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMultistageMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMultistageMsg.cpp new file mode 100644 index 00000000..80412220 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMultistageMsg.cpp @@ -0,0 +1,45 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plMultistageMsg.h" +#include "hsStream.h" + +void plMultistageModMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgRead(stream, mgr); + + fCmds.Read(stream); + fStageNum = stream->ReadByte(); + fNumLoops = stream->ReadByte(); +} + +void plMultistageModMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + fCmds.Write(stream); + stream->WriteByte(fStageNum); + stream->WriteByte(fNumLoops); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMultistageMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMultistageMsg.h new file mode 100644 index 00000000..97e557a0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plMultistageMsg.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plMultistageMsg_inc +#define plMultistageMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsBitVector.h" + +// Messages sent to a MultistageModifier. + +class plMultistageModMsg : public plMessage +{ +protected: + hsBitVector fCmds; + +public: + enum // Commands + { + kSetLoopCount, + }; + + UInt8 fStageNum; + UInt8 fNumLoops; + + plMultistageModMsg() : fStageNum(0), fNumLoops(1) {} + plMultistageModMsg(const plKey &sender, const plKey &receiver) : plMessage(sender,receiver,nil),fStageNum(0),fNumLoops(1) {} + + hsBool GetCommand(UInt8 cmd) { return fCmds.IsBitSet(cmd); } + void SetCommand(UInt8 cmd, hsBool val = true) { fCmds.SetBit(cmd, val); } + + // plasma protocol + CLASSNAME_REGISTER( plMultistageModMsg ); + GETINTERFACE_ANY( plMultistageModMsg, plMessage ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNCAgeJoinerMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNCAgeJoinerMsg.cpp new file mode 100644 index 00000000..8aad3a21 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNCAgeJoinerMsg.cpp @@ -0,0 +1,32 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNCAgeJoinerMsg.cpp +* +***/ + +#include "plNCAgeJoinerMsg.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNCAgeJoinerMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNCAgeJoinerMsg.h new file mode 100644 index 00000000..5790db1b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNCAgeJoinerMsg.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNCAgeJoinerMsg.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLNCAGEJOINERMSG_H +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLNCAGEJOINERMSG_H + +#include "../pnMessage/plMessage.h" + + +class plNCAgeJoinerMsg : public plMessage { +public: + enum { + kHACK_NotifyRcvdAllSDLStates, // Just until the server is working again + }; + + unsigned type; + + CLASSNAME_REGISTER(plNCAgeJoinerMsg); + GETINTERFACE_ANY(plNCAgeJoinerMsg, plMessage); + + void Read (hsStream *, hsResMgr *) { FATAL("plNCAgeJoinerMsg::Read"); } + void Write (hsStream *, hsResMgr *) { FATAL("plNCAgeJoinerMsg::Write"); } +}; + + +#endif // PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLNCAGEJOINERMSG_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetClientMgrMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetClientMgrMsg.cpp new file mode 100644 index 00000000..b2a53e31 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetClientMgrMsg.cpp @@ -0,0 +1,32 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetClientMgrMsg.cpp +* +***/ + +#include "plNetClientMgrMsg.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetClientMgrMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetClientMgrMsg.h new file mode 100644 index 00000000..6e8313c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetClientMgrMsg.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetClientMgrMsg.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLNETCLIENTMGRMSG_H +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLNETCLIENTMGRMSG_H + +#include "../pnMessage/plMessage.h" + + +class plNetClientMgrMsg : public plMessage { +public: + enum { + kNotifyRcvdAllSDLStates, + kCmdDisableNet, + }; + + unsigned type; + char str[256]; + bool yes; + + CLASSNAME_REGISTER(plNetClientMgrMsg); + GETINTERFACE_ANY(plNetClientMgrMsg, plMessage); + + void Read (hsStream *, hsResMgr *) { FATAL("plNetClientMgrMsg::Read"); } + void Write (hsStream *, hsResMgr *) { FATAL("plNetClientMgrMsg::Write"); } +}; + + +#endif // PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLNETCLIENTMGRMSG_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetCommMsgs.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetCommMsgs.cpp new file mode 100644 index 00000000..6be4b274 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetCommMsgs.cpp @@ -0,0 +1,32 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetCommMsgs.cpp +* +***/ + +#include "plNetCommMsgs.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetCommMsgs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetCommMsgs.h new file mode 100644 index 00000000..36c97fb0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetCommMsgs.h @@ -0,0 +1,160 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetCommMsgs.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLNETCOMMMSGS_H +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLNETCOMMMSGS_H + + +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" +#include "../pnMessage/plMessage.h" +#include "../pnNetProtocol/pnNetProtocol.h" + + +class plNetCommReplyMsg : public plMessage { +public: + enum EParamType { + kParamTypeOther = 0, + kParamTypePython, + }; + + ENetError result; + void * param; + EParamType ptype; + + plNetCommReplyMsg () { SetBCastFlag(kBCastByExactType); } + + void Read (hsStream * s, hsResMgr * mgr) { plMessage::IMsgRead(s, mgr); } + void Write (hsStream * s, hsResMgr * mgr) { plMessage::IMsgWrite(s, mgr); } +}; + +class plNetCommAuthMsg : public plNetCommReplyMsg { +public: + CLASSNAME_REGISTER(plNetCommAuthMsg); + GETINTERFACE_ANY(plNetCommAuthMsg, plMessage); +}; + +class plNetCommAuthConnectedMsg : public plMessage { +public: + plNetCommAuthConnectedMsg () { SetBCastFlag(kBCastByExactType); } + + CLASSNAME_REGISTER(plNetCommAuthConnectedMsg); + GETINTERFACE_ANY(plNetCommAuthConnectedMsg, plMessage); + + void Read (hsStream * s, hsResMgr * mgr) { plMessage::IMsgRead(s, mgr); } + void Write (hsStream * s, hsResMgr * mgr) { plMessage::IMsgWrite(s, mgr); } +}; + +struct NetCliAuthFileInfo; +class plNetCommFileListMsg : public plNetCommReplyMsg { +public: + FARRAY(NetCliAuthFileInfo) fileInfoArr; + + CLASSNAME_REGISTER(plNetCommFileListMsg); + GETINTERFACE_ANY(plNetCommFileListMsg, plMessage); +}; + +class plNetCommFileDownloadMsg : public plNetCommReplyMsg { +public: + wchar filename[MAX_PATH]; + hsStream * writer; + + CLASSNAME_REGISTER(plNetCommFileDownloadMsg); + GETINTERFACE_ANY(plNetCommFileDownloadMsg, plMessage); +}; + +class plNetCommLinkToAgeMsg : public plNetCommReplyMsg { +public: + CLASSNAME_REGISTER(plNetCommLinkToAgeMsg); + GETINTERFACE_ANY(plNetCommLinkToAgeMsg, plMessage); +}; + +class plNetCommPlayerListMsg : public plNetCommReplyMsg { +public: + CLASSNAME_REGISTER(plNetCommPlayerListMsg); + GETINTERFACE_ANY(plNetCommPlayerListMsg, plMessage); +}; + +class plNetCommActivePlayerMsg : public plNetCommReplyMsg { +public: + CLASSNAME_REGISTER(plNetCommActivePlayerMsg); + GETINTERFACE_ANY(plNetCommActivePlayerMsg, plMessage); +}; + +class plNetCommCreatePlayerMsg : public plNetCommReplyMsg { +public: + CLASSNAME_REGISTER(plNetCommCreatePlayerMsg); + GETINTERFACE_ANY(plNetCommCreatePlayerMsg, plMessage); +}; + +class plNetCommDeletePlayerMsg : public plNetCommReplyMsg { +public: + CLASSNAME_REGISTER(plNetCommDeletePlayerMsg); + GETINTERFACE_ANY(plNetCommDeletePlayerMsg, plMessage); +}; + +class plNetCommPublicAgeListMsg : public plNetCommReplyMsg { +public: + CLASSNAME_REGISTER(plNetCommPublicAgeListMsg); + GETINTERFACE_ANY(plNetCommPublicAgeListMsg, plMessage); + + ARRAY(struct NetAgeInfo) ages; +}; + +class plNetCommPublicAgeMsg : public plNetCommReplyMsg { +public: + CLASSNAME_REGISTER(plNetCommPublicAgeMsg); + GETINTERFACE_ANY(plNetCommPublicAgeMsg, plMessage); +}; + +class plNetCommRegisterAgeMsg : public plNetCommReplyMsg { +public: + CLASSNAME_REGISTER(plNetCommRegisterAgeMsg); + GETINTERFACE_ANY(plNetCommRegisterAgeMsg, plMessage); +}; + +class plNetCommUnregisterAgeMsg : public plNetCommReplyMsg { +public: + CLASSNAME_REGISTER(plNetCommUnregisterAgeMsg); + GETINTERFACE_ANY(plNetCommUnregisterAgeMsg, plMessage); +}; + +class plNetCommDisconnectedMsg : public plMessage { +public: + ENetProtocol protocol; + + void Read (hsStream * s, hsResMgr * mgr) { plMessage::IMsgRead(s, mgr); } + void Write (hsStream * s, hsResMgr * mgr) { plMessage::IMsgWrite(s, mgr); } +}; + + + +#endif // PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLNETCOMMMSGS_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetOwnershipMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetOwnershipMsg.h new file mode 100644 index 00000000..bede735e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetOwnershipMsg.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetOwnershipMsg_INC +#define plNetOwnershipMsg_INC + +#include "hsStlUtils.h" +#include "../pnMessage/plMessage.h" +#include "../plNetMessage/plNetMessage.h" + +// +// A msg sent locally when this client changes ownership of a group of objects +// +class hsResMgr; +class hsStream; +class plNetOwnershipMsg : public plMessage +{ +protected: + std::vector fGroups; +public: + plNetOwnershipMsg() { SetBCastFlag(plMessage::kBCastByType); } + + CLASSNAME_REGISTER( plNetOwnershipMsg ); + GETINTERFACE_ANY( plNetOwnershipMsg, plMessage ); + + // getters + int GetNumGroups() const { return fGroups.size(); } + plNetMsgGroupOwner::GroupInfo GetGroupInfo(int i) const { return fGroups[i]; } + + // setters + void AddGroupInfo(plNetMsgGroupOwner::GroupInfo gi) { fGroups.push_back(gi); } + void ClearGroupInfo() { fGroups.clear(); } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { hsAssert(false, "NA: localOnly msg"); } + void Write(hsStream* stream, hsResMgr* mgr) { hsAssert(false, "NA: localOnly msg"); } +}; + +#endif // plNetOwnershipMsg diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetVoiceListMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetVoiceListMsg.cpp new file mode 100644 index 00000000..314e5269 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetVoiceListMsg.cpp @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plNetVoiceListMsg.h" +#include "hsStream.h" +#include "hsResMgr.h" + +void plNetVoiceListMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + + fRemoved = mgr->ReadKey(stream); + fCmd = stream->ReadSwap32(); + int n = stream->ReadSwap32(); + fClientIDs.SetCountAndZero(0); + for (int i = 0; i < n; i++) + fClientIDs.Append(stream->ReadSwap32()); + +} + +void plNetVoiceListMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + mgr->WriteKey(stream, fRemoved); + stream->WriteSwap32(fCmd); + stream->WriteSwap32(fClientIDs.Count()); + for (int i = 0; iWriteSwap32(fClientIDs[i]); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetVoiceListMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetVoiceListMsg.h new file mode 100644 index 00000000..ee8089cb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNetVoiceListMsg.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plNetVoiceListMsg_inc +#define plNetVoiceListMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsTemplates.h" + +class plNetVoiceListMsg : public plMessage +{ +protected: + + hsTArray fClientIDs; + int fCmd; + plKey fRemoved; + +public: + + enum + { + kForcedListenerMode, + kDistanceMode, + }; + + plNetVoiceListMsg() : plMessage(nil, nil, nil), fCmd( 0 ) { SetBCastFlag(kBCastByExactType); } + plNetVoiceListMsg( UInt32 cmd ) : + plMessage(nil, nil, nil), fCmd( cmd ) + { SetBCastFlag( kBCastByExactType ); } + + ~plNetVoiceListMsg() { ; } + + CLASSNAME_REGISTER( plNetVoiceListMsg ); + GETINTERFACE_ANY( plNetVoiceListMsg, plMessage ); + + UInt32 GetCmd( void ) { return fCmd; } + hsTArray* GetClientList( void ) { return &fClientIDs; }; + plKey GetRemovedKey() { return fRemoved; } + void SetRemovedKey(plKey& k) { fRemoved = k; } + virtual void Read(hsStream* s, hsResMgr* mgr); + + virtual void Write(hsStream* s, hsResMgr* mgr); +}; + + + +#endif // plNetVoiceListMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNodeCleanupMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNodeCleanupMsg.h new file mode 100644 index 00000000..311347ca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plNodeCleanupMsg.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plNodeCleanupMsg Header // +// Tiny message to let sceneNodes know that they need to clean up. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plNodeCleanupMsg_h +#define _plNodeCleanupMsg_h + +#include "../pnMessage/plMessage.h" + +class plNodeCleanupMsg : public plMessage +{ +public: + plNodeCleanupMsg() : plMessage( nil, nil, nil ) { SetBCastFlag( kBCastByExactType ); } + ~plNodeCleanupMsg() {} + + CLASSNAME_REGISTER( plNodeCleanupMsg ); + GETINTERFACE_ANY( plNodeCleanupMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead( stream, mgr ); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite( stream, mgr ); } +}; + +#endif // _plNodeCleanupMsg_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotCallbacks.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotCallbacks.cpp new file mode 100644 index 00000000..7456bcd4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotCallbacks.cpp @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plOneShotCallbacks.h" +#include "hsStream.h" +#include "hsUtils.h" +#include "hsResMgr.h" + +plOneShotCallbacks::plOneShotCallbacks() +{ +} + +plOneShotCallbacks::~plOneShotCallbacks() +{ + int size = fCallbacks.size(); + for (int i = 0; i < size; i++) + delete [] fCallbacks[i].fMarker; + fCallbacks.clear(); +} + +void plOneShotCallbacks::AddCallback(const char *marker, plKey &receiver, Int16 user) +{ + fCallbacks.push_back(plOneShotCallback(hsStrcpy(marker), receiver, user)); +} + +int plOneShotCallbacks::GetNumCallbacks() +{ + return fCallbacks.size(); +} + +plOneShotCallbacks::plOneShotCallback& plOneShotCallbacks::GetCallback(int i) +{ + return fCallbacks[i]; +} + +void plOneShotCallbacks::Read(hsStream* stream, hsResMgr* mgr) +{ + int size = stream->ReadSwap32(); + fCallbacks.reserve(size); + for (int i = 0; i < size; i++) + { + char *marker = stream->ReadSafeString(); + plKey receiver = mgr->ReadKey(stream); + Int16 user = stream->ReadSwap16(); + + fCallbacks.push_back(plOneShotCallback(marker, receiver, user)); + } +} + +void plOneShotCallbacks::Write(hsStream* stream, hsResMgr* mgr) +{ + int size = fCallbacks.size(); + stream->WriteSwap32(size); + for (int i = 0; i < size; i++) + { + stream->WriteSafeString(fCallbacks[i].fMarker); + mgr->WriteKey(stream, fCallbacks[i].fReceiver); + stream->WriteSwap16(fCallbacks[i].fUser); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotCallbacks.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotCallbacks.h new file mode 100644 index 00000000..70310aee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotCallbacks.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsRefCnt.h" + +#include "../pnKeyedObject/plKey.h" + +class hsStream; +class hsResMgr; + +// +// HACK +// Basically, I just made this class because I couldn't stand duplicating +// all the code and strings for plOneShotMsg and plAvOneShotMsg. - Colin +// +class plOneShotCallbacks : public hsRefCnt +{ +public: + class plOneShotCallback + { + public: + plOneShotCallback(char *marker, plKey &receiver, Int16 user) : + fMarker(marker), fReceiver(receiver) , fUser(user) {} + + char *fMarker; + plKey fReceiver; + Int16 fUser; + }; + +protected: + std::vector fCallbacks; + +public: + plOneShotCallbacks(); + ~plOneShotCallbacks(); + + void AddCallback(const char *marker, plKey &receiver, Int16 user=0); + int GetNumCallbacks(); + plOneShotCallback& GetCallback(int i); + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotMsg.cpp new file mode 100644 index 00000000..5cb967ec --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotMsg.cpp @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plOneShotMsg.h" +#include "plOneShotCallbacks.h" + +plOneShotMsg::plOneShotMsg() +{ + fCallbacks = TRACKED_NEW plOneShotCallbacks; +} + +plOneShotMsg::~plOneShotMsg() +{ + hsRefCnt_SafeUnRef(fCallbacks); + fCallbacks = nil; +} + +void plOneShotMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plResponderMsg::Read(stream, mgr); + fCallbacks->Read(stream, mgr); +} + +void plOneShotMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plResponderMsg::Write(stream, mgr); + fCallbacks->Write(stream, mgr); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotMsg.h new file mode 100644 index 00000000..e910187a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plOneShotMsg.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plOneShotMsg_inc +#define plOneShotMsg_inc + +#include "plResponderMsg.h" + +class plOneShotCallbacks; + +class plOneShotMsg : public plResponderMsg +{ +public: + // We can't use a plEventCallbackMsg since we don't know the actual times we + // want to be called back at. We need to use a string, then the plAGAnim + // will figure out the time internally and create a plEventCallbackMsg. + plOneShotCallbacks *fCallbacks; + + plOneShotMsg(); + ~plOneShotMsg(); + + CLASSNAME_REGISTER(plOneShotMsg); + GETINTERFACE_ANY(plOneShotMsg, plResponderMsg); + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plOneShotMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plParticleUpdateMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plParticleUpdateMsg.h new file mode 100644 index 00000000..56ad566a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plParticleUpdateMsg.h @@ -0,0 +1,207 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plParticleUpdateMsg_inc +#define plParticleUpdateMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsResMgr.h" +#include "hsStream.h" +#include "hsBitVector.h" + +////////////////////////////////////////////////////////////////////////////// +// plParticleUpdateMsg. Messages to change the parameters of a particle system +// and its generators. + +class plParticleUpdateMsg : public plMessage +{ +public: + plParticleUpdateMsg() + : plMessage(nil, nil, nil) {} + plParticleUpdateMsg(const plKey &s, const plKey &r, const double* t, UInt32 paramID, hsScalar paramValue ) + : plMessage(s, r, t) { fParamID = paramID; fParamValue = paramValue; } + virtual ~plParticleUpdateMsg() {} + + CLASSNAME_REGISTER( plParticleUpdateMsg ); + GETINTERFACE_ANY( plParticleUpdateMsg, plMessage ); + + enum paramIDs + { + kParamParticlesPerSecond, + kParamInitPitchRange, + kParamInitYawRange, +// kParamInitVel, +// kParamInitVelRange, + kParamVelMin, + kParamVelMax, + kParamXSize, + kParamYSize, +// kParamSizeRange, + kParamScaleMin, + kParamScaleMax, + kParamGenLife, +// kParamPartLife, +// kParamPartLifeRange, + kParamPartLifeMin, + kParamPartLifeMax, + kParamEnabled, + }; + + UInt32 fParamID; + hsScalar fParamValue; + + UInt32 GetParamID() { return fParamID; } + hsScalar GetParamValue() { return fParamValue; } + + // IO + virtual void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + + fParamID = stream->ReadSwap32(); + stream->ReadSwap(&fParamValue); + } + + virtual void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + + stream->WriteSwap32(fParamID); + stream->WriteSwap(fParamValue); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// plParticleTransferMsg. Currently intended for just the avatar, but amendable. (Talk to Bob) +// Takes one particle system, clones it, slaps the clone on the target +// sceneObject, and transfers some particles from the old system to the new one. + +class plParticleTransferMsg : public plMessage +{ +public: + plKey fSysSOKey; // sceneObject of the system we're snagging particles from + UInt16 fNumToTransfer; // number of particles to transfer + + plParticleTransferMsg() : plMessage(nil, nil, nil), fSysSOKey(nil), fNumToTransfer(0) {} + plParticleTransferMsg(const plKey &s, const plKey &r, const double* t, plKey sysSOKey, UInt16 numParticles ) + : plMessage(s, r, t) { fSysSOKey = sysSOKey; fNumToTransfer = numParticles; } + virtual ~plParticleTransferMsg() {} + + CLASSNAME_REGISTER( plParticleTransferMsg ); + GETINTERFACE_ANY( plParticleTransferMsg, plMessage ); + + // IO + virtual void Read(hsStream *stream, hsResMgr *mgr) + { + plMessage::IMsgRead(stream, mgr); + + fSysSOKey = mgr->ReadKey(stream); + fNumToTransfer = stream->ReadSwap16(); + } + + virtual void Write(hsStream *stream, hsResMgr *mgr) + { + plMessage::IMsgWrite(stream, mgr); + + mgr->WriteKey(stream, fSysSOKey); + stream->WriteSwap16(fNumToTransfer); + } + +}; + + +////////////////////////////////////////////////////////////////////////////// +// plParticleKillMsg. Tell a system that a number (or percentage) of its +// particles have to go. + +class plParticleKillMsg : public plMessage +{ +public: + hsScalar fNumToKill; + hsScalar fTimeLeft; + + UInt8 fFlags; + enum + { + kParticleKillImmortalOnly = 0x1, // Only slap a death sentence on "immortal" particles (the others are already dying) + kParticleKillPercentage = 0x2, // Tells us to interpret "fNumToKill" as a 0-1 percentage. + }; + + plParticleKillMsg() : plMessage(nil, nil, nil), fNumToKill(0.f), fTimeLeft(0.f), fFlags(kParticleKillImmortalOnly) {} + plParticleKillMsg(const plKey &s, const plKey &r, const double* t, hsScalar numToKill, hsScalar timeLeft, UInt8 flags = kParticleKillImmortalOnly ) + : plMessage(s, r, t) { fNumToKill = numToKill; fTimeLeft = timeLeft; fFlags = flags; } + virtual ~plParticleKillMsg() {} + + CLASSNAME_REGISTER( plParticleKillMsg ); + GETINTERFACE_ANY( plParticleKillMsg, plMessage ); + + // Local only + virtual void Read(hsStream *stream, hsResMgr *mgr) + { + plMessage::IMsgRead(stream,mgr); + fNumToKill = stream->ReadSwapScalar(); + fTimeLeft = stream->ReadSwapScalar(); + stream->ReadSwap(&fFlags); + } + virtual void Write(hsStream *stream, hsResMgr *mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwapScalar(fNumToKill); + stream->WriteSwapScalar(fTimeLeft); + stream->WriteSwap(fFlags); + } +}; + +////////////////////////////////////////////////////////////////////////////// +// plParticleFlockMsg. Commands for a flock effect + +class plParticleFlockMsg : public plMessage +{ +public: + hsScalar fX, fY, fZ; + UInt8 fCmd; + enum + { + kFlockCmdSetOffset, + kFlockCmdSetDissentPoint, + }; + + plParticleFlockMsg() : plMessage(nil, nil, nil), fCmd(0), fX(0.f), fY(0.f), fZ(0.f) {} + plParticleFlockMsg(const plKey &s, const plKey &r, const double* t, UInt8 cmd, hsScalar x, hsScalar y, hsScalar z) + : plMessage(s, r, t), fCmd(cmd), fX(x), fY(y), fZ(z) {} + virtual ~plParticleFlockMsg() {} + + CLASSNAME_REGISTER( plParticleFlockMsg ); + GETINTERFACE_ANY( plParticleFlockMsg, plMessage ); + + // Local only + virtual void Read(hsStream *stream, hsResMgr *mgr) {} + virtual void Write(hsStream *stream, hsResMgr *mgr) {} +}; + + + +#endif // plParticleUpdateMsg_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPickedMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPickedMsg.h new file mode 100644 index 00000000..3d115e2e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPickedMsg.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPickedMsg_inc +#define plPickedMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsGeometry3.h" + + +class plPickedMsg : public plMessage +{ +protected: + +public: + + hsBool fPicked; + hsPoint3 fHitPoint; // where in the world the object was picked on + + plPickedMsg() : fPicked(true),fHitPoint(0,0,0){SetBCastFlag(plMessage::kPropagateToModifiers);} + plPickedMsg(const plKey &s, + const plKey &r, + const double* t) : fPicked(true),fHitPoint(0,0,0) {SetBCastFlag(plMessage::kPropagateToModifiers);} + ~plPickedMsg(){;} + + CLASSNAME_REGISTER( plPickedMsg ); + GETINTERFACE_ANY( plPickedMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fPicked = stream->ReadBool(); + fHitPoint.Read(stream); + } + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->WriteBool(fPicked); + fHitPoint.Write(stream); + } + + +}; + + + + +#endif // PickedMsg diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPlayerMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPlayerMsg.h new file mode 100644 index 00000000..dd9dafbc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPlayerMsg.h @@ -0,0 +1,88 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPlayerMsg_inc +#define plPlayerMsg_inc + +// +// Player message class +// +#include "../pnMessage/plMessage.h" +#include "hsBitVector.h" +#include "hsGeometry3.h" + +class hsStream; +class hsResMgr; + +class plPlayerMsg : public plMessage +{ +protected: + hsPoint3 targPoint; + +public: + plPlayerMsg() { SetBCastFlag(plMessage::kBCastByExactType); } + plPlayerMsg(const plKey &s, + const plKey &r, + const double* t){ SetBCastFlag(plMessage::kBCastByExactType); } + ~plPlayerMsg(){;} + + CLASSNAME_REGISTER( plPlayerMsg ); + GETINTERFACE_ANY( plPlayerMsg, plMessage ); + + enum ModCmds + { + kMovementStarted = 0, + kMovementStopped, + kSetDesiredFacing, + kWarpToSpawnPoint, + + kNumCmds + }; + + hsBitVector fCmd; + + hsBool Cmd(int n) { return fCmd.IsBitSet(n); } + void SetCmd(int n) { fCmd.SetBit(n); } + void ClearCmd() { fCmd.Clear(); } + const hsPoint3 GetTargPoint() { return targPoint; } + void SetTargPoint(hsPoint3 pt) { targPoint = pt; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + targPoint.Read(stream); + fCmd.Read(stream); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + targPoint.Write(stream); + fCmd.Write(stream); + } +}; + +#endif // plPlayerMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPreloaderMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPreloaderMsg.h new file mode 100644 index 00000000..473471bf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPreloaderMsg.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plPreloaderMsg.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLPRELOADERMSG_H +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLPRELOADERMSG_H + +#include "../pnMessage/plMessage.h" + +class plPreloaderMsg : public plMessage { +public: + bool fSuccess; + + plPreloaderMsg () { SetBCastFlag(kBCastByExactType); } + + CLASSNAME_REGISTER(plPreloaderMsg); + GETINTERFACE_ANY(plPreloaderMsg, plMessage); + + void Read (hsStream* stream, hsResMgr* ) { FATAL("plPreloaderMsg::Read"); } + void Write (hsStream* stream, hsResMgr* ) { FATAL("plPreloaderMsg::Write"); } +}; + + +#endif // PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLMESSAGE_PLPRELOADERMSG_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderMsg.h new file mode 100644 index 00000000..73367880 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderMsg.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRenderMsg_inc +#define plRenderMsg_inc + +#include "../pnMessage/plMessage.h" + +class plPipeline; + +class plRenderMsg : public plMessage +{ +protected: + plPipeline* fPipe; + +public: + plRenderMsg() : plMessage(nil, nil, nil), fPipe(nil) { SetBCastFlag(kBCastByExactType); } + plRenderMsg(plPipeline* pipe) : plMessage(nil, nil, nil), fPipe(pipe) { SetBCastFlag(kBCastByExactType); } + + ~plRenderMsg() {} + + CLASSNAME_REGISTER( plRenderMsg ); + GETINTERFACE_ANY( plRenderMsg, plMessage ); + + plPipeline* Pipeline() const { return fPipe; } + + virtual void Read(hsStream* s, hsResMgr* mgr) { plMessage::IMsgRead(s, mgr); } + virtual void Write(hsStream* s, hsResMgr* mgr) { plMessage::IMsgWrite(s, mgr); } +}; + +class plPreResourceMsg : public plMessage +{ +protected: + plPipeline* fPipe; + +public: + plPreResourceMsg() : plMessage(nil, nil, nil), fPipe(nil) { SetBCastFlag(kBCastByExactType); } + plPreResourceMsg(plPipeline* pipe) : plMessage(nil, nil, nil), fPipe(pipe) { SetBCastFlag(kBCastByExactType); } + + ~plPreResourceMsg() {} + + CLASSNAME_REGISTER( plPreResourceMsg ); + GETINTERFACE_ANY( plPreResourceMsg, plMessage ); + + plPipeline* Pipeline() const { return fPipe; } + + virtual void Read(hsStream* s, hsResMgr* mgr) {} + virtual void Write(hsStream* s, hsResMgr* mgr) {} +}; + +#endif // plRenderMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderRequestMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderRequestMsg.cpp new file mode 100644 index 00000000..cbed4660 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderRequestMsg.cpp @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plRenderRequestMsg.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "hsResMgr.h" + + +plRenderRequestMsg::plRenderRequestMsg(plKey sender, plRenderRequestBase* req) +: plMessage(sender, nil, nil), + fReq(req) +{ + plUoid oid( kClient_KEY ); // from plFixedKey.h + plKey key = hsgResMgr::ResMgr()->FindKey(oid); + AddReceiver(key); + + hsRefCnt_SafeRef(fReq); +} + +plRenderRequestMsg::~plRenderRequestMsg() +{ + hsRefCnt_SafeUnRef(fReq); +} + + +plRenderRequestMsg::plRenderRequestMsg() +{ + hsAssert(false, "Improper usage, use argumented constructor"); +} + +void plRenderRequestMsg::Read(hsStream* s, hsResMgr* mgr) +{ + hsAssert(false, "Transmission/read/write of render requests not currently supported"); + plMessage::IMsgRead(s, mgr); + + fReq = nil; +} + +void plRenderRequestMsg::Write(hsStream* s, hsResMgr* mgr) +{ + hsAssert(false, "Transmission/read/write of render requests not currently supported"); + plMessage::IMsgWrite(s, mgr); +} + +plRenderRequestAck::plRenderRequestAck() +{ + hsAssert(false, "Improper usage, use argumented constructor"); +} + +plRenderRequestAck::plRenderRequestAck(plKey r, UInt32 userData) +: plMessage(nil, r, nil), + fUserData(userData) +{ +} + +void plRenderRequestAck::Read(hsStream* s, hsResMgr* mgr) +{ + hsAssert(false, "Transmission/read/write of render requests not currently supported"); + plMessage::IMsgRead(s, mgr); + +} + +void plRenderRequestAck::Write(hsStream* s, hsResMgr* mgr) +{ + hsAssert(false, "Transmission/read/write of render requests not currently supported"); + plMessage::IMsgWrite(s, mgr); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderRequestMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderRequestMsg.h new file mode 100644 index 00000000..7b851207 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRenderRequestMsg.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRenderRequestMsg_inc +#define plRenderRequestMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsRefCnt.h" + +class plRenderRequest; +class hsStream; +class hsResMgr; + +// This is a little StoneAge, using the old HeadSpin ref count here. +// It's a perfect spot for a smart pointer, but we ain't got none. +// +// Basic issue is that we hand a pointer to this request off to the +// client (via the plRenderRequestMsg), who will at some later point this frame +// hand it to the pipeline to be processed. So if I want to hand it off and +// forget about it, the client needs to delete it. If I want to reuse it every +// frame and destroy it when I'm done, I need to delete it. Sounds just like +// a smart pointer, or for the cro-magnons a RefCnt. + +class plRenderRequestBase : public hsRefCnt +{ +}; + +class plRenderRequestMsg : public plMessage +{ +protected: + + plRenderRequestBase* fReq; + +public: + // Argumentless constructor useless (except for compiling). + plRenderRequestMsg(); + // non-nil sender will get an ack when render is "done". + plRenderRequestMsg(plKey sender, plRenderRequestBase* req); + virtual ~plRenderRequestMsg(); + + CLASSNAME_REGISTER( plRenderRequestMsg ); + GETINTERFACE_ANY( plRenderRequestMsg, plMessage ); + + // These aren't really implemented. Read/Write/Transmission of + // these messages doesn't currently make sense. + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + plRenderRequest* Request() const { return (plRenderRequest*)fReq; } + +}; + +class plRenderRequestAck: public plMessage +{ +protected: + UInt32 fUserData; + UInt32 fNumDrawn; // number of objects drawn. +public: + // Argumentless constructor useless (except for compiling). + plRenderRequestAck(); + plRenderRequestAck(plKey r, UInt32 userData=0); + ~plRenderRequestAck() {} + + CLASSNAME_REGISTER( plRenderRequestAck ); + GETINTERFACE_ANY( plRenderRequestAck, plMessage ); + + void SetNumDrawn(UInt32 n) { fNumDrawn = n; } + UInt32 GetNumDrawn() const { return fNumDrawn; } + + // These aren't really implemented. Read/Write/Transmission of + // these messages doesn't currently make sense. + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + +}; + +#endif // plRenderRequestMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plReplaceGeometryMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plReplaceGeometryMsg.h new file mode 100644 index 00000000..3f9cddf2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plReplaceGeometryMsg.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plReplaceGeometryMsg_inc +#define plReplaceGeometryMsg_inc + +#include "../pnMessage/plMessage.h" + +class plSharedMesh; +class hsGMaterial; +class plDrawableSpans; + +class plReplaceGeometryMsg : public plMessage +{ +public: + plSharedMesh *fMesh; + hsGMaterial *fMaterial; + UInt32 fFlags; + + plReplaceGeometryMsg() : fMesh(nil), fMaterial(nil), fFlags(0) {} + ~plReplaceGeometryMsg() {} + + CLASSNAME_REGISTER( plReplaceGeometryMsg ); + GETINTERFACE_ANY( plReplaceGeometryMsg, plMessage ); + + // No R/W, these shouldn't be sent over the wire + virtual void Read(hsStream* stream, hsResMgr* mgr) {} + virtual void Write(hsStream* stream, hsResMgr* mgr) {} + + // flags + enum + { + kAddingGeom = 0x0001, + kAddToFront = 0x0002, + }; + +}; + +class plSwapSpansRefMsg : public plGenRefMsg +{ +public: + plDrawableSpans *fSpans; + + plSwapSpansRefMsg() : plGenRefMsg(), fSpans(nil) {} + plSwapSpansRefMsg(const plKey &r, UInt8 c, int which, int type) : plGenRefMsg(r, c, which, type) {} + ~plSwapSpansRefMsg() {} + + CLASSNAME_REGISTER( plSwapSpansRefMsg ); + GETINTERFACE_ANY( plSwapSpansRefMsg, plGenRefMsg ); +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plResMgrHelperMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plResMgrHelperMsg.h new file mode 100644 index 00000000..ecca2156 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plResMgrHelperMsg.h @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef _plResMgrHelperMsg_h +#define _plResMgrHelperMsg_h + +#include "hsTypes.h" +#include "hsStream.h" +#include "../pnMessage/plMessage.h" +#include "../plResMgr/plResManagerHelper.h" + +class plResManagerHelper; +class plResMgrHelperMsg : public plMessage +{ +protected: + + friend class plResManagerHelper; + + plResPageKeyRefList *fKeyList; + + UInt8 fCommand; + +public: + + enum Commands + { + kKeyRefList, + kUpdateDebugScreen, + kEnableDebugScreen, + kDisableDebugScreen + }; + + plResMgrHelperMsg( UInt8 command = 0 ) : plMessage(nil, nil, nil), fKeyList( nil ) { fCommand = command; } + ~plResMgrHelperMsg() { delete fKeyList; } + + CLASSNAME_REGISTER( plResMgrHelperMsg ); + GETINTERFACE_ANY( plResMgrHelperMsg, plMessage ); + + virtual void Read(hsStream* s, hsResMgr* mgr) + { + hsAssert( false, "This should never get read" ); + } + + virtual void Write(hsStream* s, hsResMgr* mgr) + { + hsAssert( false, "This should never get written" ); + } + + UInt8 GetCommand( void ) const { return fCommand; } +}; + +#endif // _plResMgrHelperMsg_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plResponderMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plResponderMsg.h new file mode 100644 index 00000000..620c2e98 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plResponderMsg.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plResponderMsg_inc +#define plResponderMsg_inc + +#include "../pnMessage/plMessage.h" + +// Derive your message from this class if you need to know the key of the avatar +// that activated your responder +class plResponderMsg : public plMessage +{ +public: + // Don't bother reading and writing this, it is set right before sending + plKey fPlayerKey; + + plResponderMsg() : fPlayerKey(nil) {} + ~plResponderMsg() {} + + CLASSNAME_REGISTER(plResponderMsg); + GETINTERFACE_ANY(plResponderMsg, plMessage); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); } +}; + +#endif // plResponderMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRideAnimatedPhysMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRideAnimatedPhysMsg.cpp new file mode 100644 index 00000000..dda2bcf5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRideAnimatedPhysMsg.cpp @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plRideAnimatedPhysMsg.h" +// global +#include "hsResMgr.h" +#include "hsStream.h" + +plRideAnimatedPhysMsg::plRideAnimatedPhysMsg() +:fRegion(nil) +,fEntering(false) +{ +} +plRideAnimatedPhysMsg::plRideAnimatedPhysMsg(const plKey &sender, const plKey &receiver, bool entering, plKey regionKey) +: plMessage(sender, receiver, nil) +,fRegion(regionKey) +,fEntering(entering) +{ +} + +void plRideAnimatedPhysMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgRead(stream, mgr); + fEntering = stream->Readbool(); + fRegion = mgr->ReadKey(stream); +} +void plRideAnimatedPhysMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgWrite(stream, mgr); + stream->Writebool(fEntering); + mgr->WriteKey(stream, fRegion); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRideAnimatedPhysMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRideAnimatedPhysMsg.h new file mode 100644 index 00000000..a739c5da --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRideAnimatedPhysMsg.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_RIDE_ANIMATED_PHYS_MSG +#define PL_RIDE_ANIMATED_PHYS_MSG + +#include "../pnMessage/plMessage.h" + +class plRideAnimatedPhysMsg : public plMessage +{ +public: + plRideAnimatedPhysMsg(); + plRideAnimatedPhysMsg(const plKey &sender, const plKey &receiver, bool entering, plKey regionKey); + bool Entering(){return fEntering;} +//PLASMA + CLASSNAME_REGISTER( plRideAnimatedPhysMsg ); + GETINTERFACE_ANY( plRideAnimatedPhysMsg, plMessage ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + +private: + bool fEntering; + plKey fRegion; +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRippleShapeMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRippleShapeMsg.cpp new file mode 100644 index 00000000..f6638f61 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRippleShapeMsg.cpp @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plRippleShapeMsg.h" + +#include "hsResMgr.h" +#include "hsStream.h" + + +plRippleShapeMsg::plRippleShapeMsg() +: fShape(nil) +{ +} + +plRippleShapeMsg::plRippleShapeMsg(const plKey& r, const plPrintShape* shape) +: plMessage(nil, r, nil), + fShape(shape) +{ +} + +plRippleShapeMsg::~plRippleShapeMsg() +{ +} + +void plRippleShapeMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + hsAssert(false, "RippleShapeMsg not meant to be read/written/networked"); +} + +void plRippleShapeMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + hsAssert(false, "RippleShapeMsg not meant to be read/written/networked"); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRippleShapeMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRippleShapeMsg.h new file mode 100644 index 00000000..859aa891 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRippleShapeMsg.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRippleShapeMsg_inc +#define plRippleShapeMsg_inc + +#include "../pnMessage/plMessage.h" + +class plPrintShape; + +class hsStream; +class hsResMgr; + + +class plRippleShapeMsg : public plMessage +{ +protected: + const plPrintShape* fShape; +public: + plRippleShapeMsg(); + plRippleShapeMsg(const plKey& r, const plPrintShape* shape); + ~plRippleShapeMsg(); + + CLASSNAME_REGISTER( plRippleShapeMsg ); + GETINTERFACE_ANY( plRippleShapeMsg, plMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + const plPrintShape* GetShape() const { return fShape; } + void SetShape(const plPrintShape* shape) { fShape = shape; } + +}; + +#endif // plRippleShapeMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRoomLoadNotifyMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRoomLoadNotifyMsg.h new file mode 100644 index 00000000..d9ec90ad --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plRoomLoadNotifyMsg.h @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plRoomLoadNotifyMsg_inc +#define plRoomLoadNotifyMsg_inc + +/////////////////////////////////////////////////////////////////// +// +// plRoomLoadNotifyMsg +// +// This message is used to notify all those who are registered for this message +// whenever the plClient has finished loading a Room. +// Its up to the receiver to decide if they are interested in that particular room +// + +#include "../pnMessage/plMessage.h" + +class plRoomLoadNotifyMsg : public plMessage +{ +protected: + plKey fRoomKey; + UInt8 fWhat; + +private: + void IInit() + { + fRoomKey = nil; + fWhat=kDontKnow; + // we will only send this message to those who have registered for it + SetBCastFlag(plMessage::kBCastByExactType); + } +public: + plRoomLoadNotifyMsg() + : plMessage(nil, nil, nil) { IInit(); } + plRoomLoadNotifyMsg(const plKey &s, + const plKey &r, + const double* t) + : plMessage(s, r, t) { IInit(); } + virtual ~plRoomLoadNotifyMsg() {;} + + CLASSNAME_REGISTER( plRoomLoadNotifyMsg ); + GETINTERFACE_ANY( plRoomLoadNotifyMsg, plMessage ); + + enum NotifyType + { + kDontKnow=0, + kLoaded, + kUnloaded, + }; + + virtual void SetRoom(plKey &rkey) { fRoomKey = rkey; } + virtual plKey GetRoom() { return fRoomKey; } + virtual void SetWhatHappen(UInt8 what) { fWhat = what; } + virtual UInt8 GetWhatHappen() { return fWhat; } + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fRoomKey = mgr->ReadKey(stream); + fWhat = stream->ReadByte(); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + mgr->WriteKey(stream, fRoomKey); + stream->WriteByte(fWhat); + } +}; + + +#endif // plRoomLoadNotifyMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plShadowCastMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plShadowCastMsg.h new file mode 100644 index 00000000..5b84e068 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plShadowCastMsg.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plShadowCastMsg_inc +#define plShadowCastMsg_inc + +#include "../pnMessage/plMessage.h" + +class plPipeline; +class plShadowCaster; + +class plShadowCastMsg : public plMessage +{ +protected: + plPipeline* fPipe; + plShadowCaster* fCaster; + +public: + plShadowCastMsg() : fPipe(nil), fCaster(nil) { SetBCastFlag(kBCastByType); } + plShadowCastMsg(plKey sender, plShadowCaster* cast, plPipeline* pipe) : plMessage(sender, nil, nil), fPipe(pipe), fCaster(cast) { SetBCastFlag(kBCastByType); } + ~plShadowCastMsg() {} + + CLASSNAME_REGISTER( plShadowCastMsg ); + GETINTERFACE_ANY( plShadowCastMsg, plMessage ); + + virtual void Read(hsStream* stream, hsResMgr* mgr) { hsAssert(false, "Non-networkable"); } + virtual void Write(hsStream* stream, hsResMgr* mgr) { hsAssert(false, "Non-networkable"); } + + plPipeline* Pipeline() const { return fPipe; } + plShadowCaster* Caster() const { return fCaster; } + + void SetPipeline(plPipeline* pipe) { fPipe = pipe; } + void SetCaster(plShadowCaster* c) { fCaster = c; } +}; + +#endif // plShadowCastMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimInfluenceMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimInfluenceMsg.cpp new file mode 100644 index 00000000..f34cffb7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimInfluenceMsg.cpp @@ -0,0 +1,166 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../plMessage/plSimInfluenceMsg.h" +#include "../../CoreLib/hsStream.h" +/* + +void plSimInfluenceMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plSimulationMsg::Read(stream, mgr); +} + +void plSimInfluenceMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plSimulationMsg::Write(stream, mgr); +} + + +// PLFORCEMSG +void plForceMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Read(stream, mgr); + + fForce.Read(stream); +} + +void plForceMsg::Write(hsStream * stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Write(stream, mgr); + + fForce.Write(stream); +} + +// PLOFFSETFORCEMSG +void plOffsetForceMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plForceMsg::Read(stream, mgr); + + fPoint.Read(stream); +} + +void plOffsetForceMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plForceMsg::Write(stream, mgr); + + fPoint.Write(stream); +} + + +// PLTORQUEMSG +void plTorqueMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Read(stream, mgr); + + fTorque.Read(stream); +} + +void plTorqueMsg::Write(hsStream * stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Write(stream, mgr); + + fTorque.Write(stream); +} + + +// PLIMPULSE +void plImpulseMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Read(stream, mgr); + + fImpulse.Read(stream); +} + +void plImpulseMsg::Write(hsStream * stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Write(stream, mgr); + + fImpulse.Write(stream); +} + + +// PLOFFSETIMPULSE +void plOffsetImpulseMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Read(stream, mgr); + + fPoint.Read(stream); +} + +void plOffsetImpulseMsg::Write(hsStream * stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Write(stream, mgr); + + fPoint.Write(stream); +} + + +// PLANGULARIMPULSE +void plAngularImpulseMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Read(stream, mgr); + + fImpulse.Read(stream); +} + +void plAngularImpulseMsg::Write(hsStream * stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Write(stream, mgr); + + fImpulse.Write(stream); +} + + +// PLDAMPMSG +void plDampMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Read(stream, mgr); + + stream->WriteSwapScalar(fDamp); +} + +void plDampMsg::Write(hsStream * stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Write(stream, mgr); + + fDamp = stream->ReadSwapScalar(); +} + + +// PLSHIFTCENTERMSG +void plShiftMassMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Read(stream, mgr); + + fOffset.Read(stream); +} + +void plShiftMassMsg::Write(hsStream * stream, hsResMgr *mgr) +{ + plSimInfluenceMsg::Write(stream, mgr); + + fOffset.Write(stream); +} +*/ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimInfluenceMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimInfluenceMsg.h new file mode 100644 index 00000000..555d8724 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimInfluenceMsg.h @@ -0,0 +1,282 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// SIMULATION MESSAGES +// Messages that influence the movement of a physically simulated object + +#ifndef PLSIMINFLUENCEMSG_H +#define PLSIMINFLUENCEMSG_H +/* +#include "../../NucleusLib/pnMessage/plSimulationMsg.h" +#include "../../CoreLib/hsGeometry3.h" + +//////////// +// +// INFLUENCE +// +//////////// + +// PLSIMULATIONINFLUENCEMSG (virtual) +// base for messages that are used to push physical objects around in one way or another +class plSimInfluenceMsg : public plSimulationMsg +{ +public: + plSimInfluenceMsg() : plSimulationMsg() {}; + plSimInfluenceMsg(const plKey& sender, const plKey &receiver, double *time) + :plSimulationMsg(sender, receiver, time){}; + + CLASSNAME_REGISTER( plSimInfluenceMsg ); + GETINTERFACE_ANY( plSimInfluenceMsg, plSimulationMsg); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + + +// FORCES +///////// + +// PLFORCEMSG +// apply a force to the center of mass of the receiver +class plForceMsg : public plSimInfluenceMsg +{ +public: + plForceMsg() : plSimInfluenceMsg() { }; + plForceMsg::plForceMsg(const plKey& sender, const plKey &receiver, hsVector3 &force) + :plSimInfluenceMsg(sender, receiver, nil), fForce(force) {}; + plForceMsg::plForceMsg(const plKey& sender, const plKey &receiver, double *time, hsVector3 &force) + :plSimInfluenceMsg(sender, receiver, time), fForce(force) {}; + + CLASSNAME_REGISTER( plForceMsg ); + GETINTERFACE_ANY( plForceMsg, plSimInfluenceMsg); + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); + + hsVector3 GetForce() const { return fForce; } + + void SetForce(const hsVector3 &the_Force) + { fForce = the_Force; } + +protected: + hsVector3 fForce; +}; + + +// PLOFFSETFORCE +// apply a force to the receiver as though it were being impacted at the +// given point in global space +class plOffsetForceMsg : public plForceMsg +{ +public: + plOffsetForceMsg() : plForceMsg() { }; + plOffsetForceMsg(const plKey& sender, const plKey &receiver, hsVector3 &force, hsPoint3 &point) + :plForceMsg(sender, receiver, nil, force), fPoint(point) {}; + plOffsetForceMsg(const plKey& sender, const plKey &receiver, double *time, hsVector3 &force, hsPoint3 &point) + :plForceMsg(sender, receiver, time, force), fPoint(point) {}; + + CLASSNAME_REGISTER( plOffsetForceMsg ); + GETINTERFACE_ANY( plOffsetForceMsg, plForceMsg); + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); + + hsPoint3 GetPoint() const { return fPoint; } + + void SetPoint(const hsPoint3 &the_Point) + { fPoint = the_Point; } + +protected: + hsPoint3 fPoint; +}; + +// PLTORQUEMSG +// Apply the given torque force to the body +// The vector indicates the axes, and the magnitude indicates the strength +class plTorqueMsg : public plSimInfluenceMsg +{ +public: + plTorqueMsg() : plSimInfluenceMsg() { }; + plTorqueMsg(const plKey& sender, const plKey &receiver, hsVector3 & torque) + :plSimInfluenceMsg(sender, receiver, nil), fTorque(torque) {}; + + CLASSNAME_REGISTER( plTorqueMsg ); + GETINTERFACE_ANY( plTorqueMsg, plSimInfluenceMsg); + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); + + hsVector3 GetTorque() const { return fTorque; } + void SetTorque(const hsVector3 &the_Torque) { fTorque = the_Torque; } + +protected: + hsVector3 fTorque; + +}; + +// IMPULSES +/////////// + +// PLIMPULSE +// Add the given vector to the objects velocity +class plImpulseMsg : public plSimInfluenceMsg +{ +public: + plImpulseMsg() : plSimInfluenceMsg() { }; + plImpulseMsg(const plKey& sender, const plKey &receiver, hsVector3 &impulse) + :plSimInfluenceMsg(sender, receiver, nil), fImpulse(impulse) {}; + plImpulseMsg(const plKey& sender, const plKey &receiver, double *time, hsVector3 &impulse) + :plSimInfluenceMsg(sender, receiver, time), fImpulse(impulse) {}; + + CLASSNAME_REGISTER( plImpulseMsg ); + GETINTERFACE_ANY( plImpulseMsg, plSimInfluenceMsg); + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); + + hsVector3 GetImpulse() const { return fImpulse; } + void SetImpulse(const hsVector3 &the_Impulse) { fImpulse = the_Impulse; } + +protected: + hsVector3 fImpulse; + +}; + +// PLOFFSETIMPULSE +// Apply the given impulse to the object at the given point in global space +// Will impart torque if not applied to center of mass +class plOffsetImpulseMsg : public plImpulseMsg +{ +public: + plOffsetImpulseMsg() : plImpulseMsg() { }; + plOffsetImpulseMsg(const plKey& sender, const plKey &receiver, hsVector3 & impulse, hsPoint3 &point) + : plImpulseMsg(sender, receiver, impulse), fPoint(point) {}; + plOffsetImpulseMsg(const plKey& sender, const plKey &receiver, double *time, hsVector3 & impulse, hsPoint3 &point) + : plImpulseMsg(sender, receiver, time, impulse), fPoint(point) {}; + + CLASSNAME_REGISTER( plOffsetImpulseMsg ); + GETINTERFACE_ANY( plOffsetImpulseMsg, plImpulseMsg); + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); + + hsPoint3 GetPoint() const { return fPoint; } + void SetPoint(const hsPoint3 &the_Point) { fPoint = the_Point; } + +protected: + hsPoint3 fPoint; + +}; + + +// PLANGULARIMPULSE +// Add the given vector (representing a rotation axis and magnitude) +// to the +class plAngularImpulseMsg : public plSimInfluenceMsg +{ +public: + plAngularImpulseMsg() : plSimInfluenceMsg() { }; + plAngularImpulseMsg(const plKey& sender, const plKey &receiver, hsVector3 &impulse) + : plSimInfluenceMsg(sender, receiver, nil), fImpulse(impulse) {}; + plAngularImpulseMsg(const plKey& sender, const plKey &receiver, double *time, hsVector3 &impulse) + : plSimInfluenceMsg(sender, receiver, time), fImpulse(impulse) {}; + + CLASSNAME_REGISTER( plAngularImpulseMsg ); + GETINTERFACE_ANY( plAngularImpulseMsg, plSimInfluenceMsg); + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); + + hsVector3 GetImpulse() const { return fImpulse; } + + void SetImpulse(const hsVector3 &the_Impulse) + { fImpulse = the_Impulse; } + +protected: + hsVector3 fImpulse; +}; + + +// MISCELLANEOUS INFLUENCES +/////////////////////////// + +// PLDAMPMSG +// Decrease all velocities on the given object. +// A damp factor of 0 nulls them all entirely; +// A damp factor of 1 leaves them alone. +class plDampMsg : public plSimInfluenceMsg +{ +public: + plDampMsg() : plSimInfluenceMsg() { }; + plDampMsg(const plKey& sender, const plKey &receiver, float dampFactor) + : plSimInfluenceMsg(sender, receiver, nil), fDamp(dampFactor) {}; + plDampMsg(const plKey& sender, const plKey &receiver, double *time, float dampFactor) + : plSimInfluenceMsg(sender, receiver, time), fDamp(dampFactor) {}; + + CLASSNAME_REGISTER( plDampMsg ); + GETINTERFACE_ANY( plDampMsg, plSimInfluenceMsg); + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); + + float GetDamp() const { return fDamp; } + + void SetDamp( float the_Damp ) + { fDamp = the_Damp; } + +protected: + float fDamp; +}; + +// PLSHIFTCENTERMSG +// Shift the center of mass of the given object by the given +// amount in the given direction. +class plShiftMassMsg : public plSimInfluenceMsg +{ +public: + plShiftMassMsg() : plSimInfluenceMsg() { }; + plShiftMassMsg(const plKey& sender, const plKey &receiver, hsVector3 &offset) + :plSimInfluenceMsg(sender, receiver, nil), fOffset(offset) {}; + plShiftMassMsg(const plKey& sender, const plKey &receiver, double *time, hsVector3 &offset) + :plSimInfluenceMsg(sender, receiver, time), fOffset(offset) {}; + + CLASSNAME_REGISTER( plShiftMassMsg ); + GETINTERFACE_ANY( plShiftMassMsg, plSimInfluenceMsg); + + void Read(hsStream *stream, hsResMgr *mgr); + void Write(hsStream *stream, hsResMgr *mgr); + + hsVector3 GetOffset() const { return fOffset; } + + void SetOffset(const hsVector3 &the_Offset) + { fOffset = the_Offset; } + +protected: + hsVector3 fOffset; +}; +*/ +#endif // PLSIMINFLUENCEMSG_H + + \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimStateMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimStateMsg.cpp new file mode 100644 index 00000000..a0118119 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimStateMsg.cpp @@ -0,0 +1,39 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../plMessage/plSimStateMsg.h" +#include "hsResMgr.h" + +void plSubWorldMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plSimulationMsg::Read(stream, mgr); + fWorldKey = mgr->ReadKey(stream); +} + +void plSubWorldMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plSimulationMsg::Write(stream, mgr); + mgr->WriteKey(stream, fWorldKey); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimStateMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimStateMsg.h new file mode 100644 index 00000000..03d66d11 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSimStateMsg.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Messages that change physics-specific state directly. +// Compare to plSimInfluenceMsg, which changes movement-related state indirectly, via forces. +#ifndef PLSIMSTATEMSG_INC +#define PLSIMSTATEMSG_INC + +#include "../pnMessage/plSimulationMsg.h" + +// use a nil key to return to main world +// otherwise pass in the key of the world you're going to. +class plSubWorldMsg : public plSimulationMsg +{ +public: + plSubWorldMsg() + { + fWorldKey = nil; + } + plSubWorldMsg(const plKey &sender, const plKey &receiver, const plKey &worldKey) + : plSimulationMsg(sender, receiver, 0) + { + fWorldKey = worldKey; + SetBCastFlag(plMessage::kNetPropagate); + } + + plKey fWorldKey; + + CLASSNAME_REGISTER(plSubWorldMsg); + GETINTERFACE_ANY( plSubWorldMsg, plSimulationMsg); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + +#endif // PLSIMSTATEMSG_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSpawnModMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSpawnModMsg.h new file mode 100644 index 00000000..aae978b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSpawnModMsg.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSpawnModMsg_inc +#define plSpawnModMsg_inc + +#include "../pnMessage/plMessage.h" +#include "../pnKeyedObject/plUoid.h" +#include "hsGeometry3.h" + +class hsStream; +class hsResMgr; + + +class plSpawnModMsg : public plMessage +{ + +public: + plSpawnModMsg(){;} + plSpawnModMsg(const plKey &s, + const plKey &r, + const double* t){;} + ~plSpawnModMsg(){;} + + CLASSNAME_REGISTER( plSpawnModMsg ); + GETINTERFACE_ANY( plSpawnModMsg, plMessage ); + + hsPoint3 fPos; + plUoid fObj; + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fPos.Read(stream); + fObj.Read(stream); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + fPos.Write(stream); + fObj.Write(stream); + } +}; + +#endif // plSpawnModMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSpawnRequestMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSpawnRequestMsg.h new file mode 100644 index 00000000..1d7afc27 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSpawnRequestMsg.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSpawnRequestMsg_inc +#define plSpawnRequestMsg_inc + +#include "../pnMessage/plMessage.h" + +class hsStream; +class hsResMgr; + + +class plSpawnRequestMsg : public plMessage +{ + +public: + plSpawnRequestMsg(){SetBCastFlag(plMessage::kBCastByExactType);} + plSpawnRequestMsg(const plKey &s, + const plKey &r, + const double* t){SetBCastFlag(plMessage::kBCastByExactType);} + ~plSpawnRequestMsg(){;} + + CLASSNAME_REGISTER( plSpawnRequestMsg ); + GETINTERFACE_ANY( plSpawnRequestMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + } +}; + +#endif // plSpawnRequestMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSwimMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSwimMsg.cpp new file mode 100644 index 00000000..96f15892 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSwimMsg.cpp @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// singular +#include "plSwimMsg.h" + +// global +#include "hsResMgr.h" +#include "hsStream.h" + +// ctor default ------ +// ------------- +plSwimMsg::plSwimMsg() +: fIsEntering(false), + fSwimRegionKey(nil) +{ +} + +// ctor sender receiver entering ---------------------------------------------- +// ------------------------------ +plSwimMsg::plSwimMsg(const plKey &sender, const plKey &receiver, bool entering, plKey regionKey) +: plMessage(sender, receiver, nil), + fIsEntering(entering) +{ + fSwimRegionKey = regionKey; +} + +// GetIsEntering -------------- +// -------------- +bool plSwimMsg::GetIsEntering() +{ + return fIsEntering; +} + +// GetIsLeaving -------------- +// ------------- +bool plSwimMsg::GetIsLeaving() +{ + return !fIsEntering; +} + +// Read --------------------------------------------- +// ----- +void plSwimMsg::Read(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgRead(stream, mgr); + + fIsEntering = stream->Readbool(); + fSwimRegionKey = mgr->ReadKey(stream); +} + +// Write --------------------------------------------- +// ------ +void plSwimMsg::Write(hsStream *stream, hsResMgr *mgr) +{ + plMessage::IMsgWrite(stream, mgr); + + stream->Writebool(fIsEntering); + mgr->WriteKey(stream, fSwimRegionKey); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSwimMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSwimMsg.h new file mode 100644 index 00000000..40ec2159 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSwimMsg.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSwimMsg_h +#define plSwimMsg_h + +#include "../pnMessage/plMessage.h" + +/** \class plSwimMsg + You're either entering the pool, or leaving. Those are the only swim messages right now. +*/ +class plSwimMsg : public plMessage +{ +public: + + // tors + plSwimMsg(); + plSwimMsg(const plKey &sender, const plKey &receiver, bool entering, plKey regionKey); + + + bool GetIsEntering(); + bool GetIsLeaving(); + + // plasma protocol + CLASSNAME_REGISTER( plSwimMsg ); + GETINTERFACE_ANY( plSwimMsg, plMessage ); + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); + + plKey fSwimRegionKey; + +private: + bool fIsEntering; // right now, if you're not entering, you're leaving + // that might not be so simple later, so we hide it + // behind a getter. +}; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSynchEnableMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSynchEnableMsg.cpp new file mode 100644 index 00000000..712fb8a6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSynchEnableMsg.cpp @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSynchEnableMsg.h" +#include "../pnNetCommon/plNetApp.h" + +plSynchEnableMsg::plSynchEnableMsg(bool push, bool enable) : fPush(push), fEnable(enable) +{ + AddReceiver(plNetApp::GetInstance()->GetKey()); +} + +void plSynchEnableMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead( stream, mgr ); + stream->WriteSwap(fEnable); + stream->WriteSwap(fPush); +} + +void plSynchEnableMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite( stream, mgr ); + stream->ReadSwap(&fEnable); + stream->ReadSwap(&fPush); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSynchEnableMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSynchEnableMsg.h new file mode 100644 index 00000000..e79bc702 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plSynchEnableMsg.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSynchEnableMsg_h +#define plSynchEnableMsg_h + +#include "../pnMessage/plMessage.h" + +// +// Push or pop a cmd to enable/disable dirty state tracking. +// Sends a local msg to the NetClientMgr which does the work. +// +class plSynchEnableMsg : public plMessage +{ +public: + bool fPush; // true to push or false to pop + bool fEnable; // or disable + + plSynchEnableMsg(bool push=true, bool enable=true); + ~plSynchEnableMsg() {} + + CLASSNAME_REGISTER( plSynchEnableMsg); + GETINTERFACE_ANY( plSynchEnableMsg, plMessage ); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plSynchEnableMsg_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTimerCallbackMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTimerCallbackMsg.h new file mode 100644 index 00000000..354e45da --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTimerCallbackMsg.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plTimerCallbackMsg_inc +#define plTimerCallbackMsg_inc + +#include "../pnMessage/plMessage.h" +#include "hsStream.h" + +class hsResMgr; + +class plTimerCallbackMsg : public plMessage +{ +public: + plTimerCallbackMsg(){;} + plTimerCallbackMsg(const plKey &s, const plKey &r, const double* t){;} + plTimerCallbackMsg(const plKey &r, UInt32 id = 0) { AddReceiver(r); fID = id;} + ~plTimerCallbackMsg(){;} + + CLASSNAME_REGISTER( plTimerCallbackMsg ); + GETINTERFACE_ANY( plTimerCallbackMsg, plMessage ); + + UInt32 fID; + hsScalar fTime; + + virtual void Read(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgRead(stream, mgr); + fID = stream->ReadSwap32(); + fTime = stream->ReadSwapScalar(); + } + + virtual void Write(hsStream* stream, hsResMgr* mgr) + { + plMessage::IMsgWrite(stream, mgr); + stream->WriteSwap32(fID); + stream->WriteSwapScalar(fTime); + } +}; + +#endif // plTimerCallbackMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTransitionMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTransitionMsg.cpp new file mode 100644 index 00000000..fd40fc55 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTransitionMsg.cpp @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plTransitionMsg.h" + +plTransitionMsg::~plTransitionMsg() +{ + Clear(); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTransitionMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTransitionMsg.h new file mode 100644 index 00000000..d9939390 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTransitionMsg.h @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plTransitionMsg_inc +#define plTransitionMsg_inc + +#include "hsTypes.h" +#include "hsStream.h" +#include "../pnMessage/plMessageWithCallbacks.h" + +class plTransitionMsg : public plMessageWithCallbacks +{ +protected: + + UInt32 fEffect; + hsScalar fLengthInSecs; + hsBool fHoldUntilNext; +public: + enum + { + kFadeIn, + kFadeOut, + kFadeInNoSound, + kFadeOutNoSound + }; + + plTransitionMsg() : plMessageWithCallbacks(nil, nil, nil), fEffect( 0 ) { SetBCastFlag(kBCastByExactType); } + plTransitionMsg( UInt32 type, hsScalar lengthInSecs, hsBool holdUntilNext = false ) : + plMessageWithCallbacks(nil, nil, nil), fEffect( type ), fLengthInSecs( lengthInSecs ), fHoldUntilNext( holdUntilNext ) + { SetBCastFlag( kBCastByExactType ); } + + ~plTransitionMsg(); + + CLASSNAME_REGISTER( plTransitionMsg ); + GETINTERFACE_ANY( plTransitionMsg, plMessageWithCallbacks ); + + UInt32 GetEffect( void ) const { return fEffect; } + hsScalar GetLengthInSecs( void ) const { return fLengthInSecs; } + hsBool GetHoldState( void ) const { return fHoldUntilNext; } + + virtual void Read(hsStream* s, hsResMgr* mgr) + { + plMessageWithCallbacks::Read(s, mgr); + s->ReadSwap(&fEffect); + s->ReadSwap(&fLengthInSecs); + s->ReadSwap(&fHoldUntilNext); + } + + virtual void Write(hsStream* s, hsResMgr* mgr) + { + plMessageWithCallbacks::Write(s, mgr); + s->WriteSwap(fEffect); + s->WriteSwap(fLengthInSecs); + s->WriteSwap(fHoldUntilNext); + } +}; + +#endif // plTransitionMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTriggerMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTriggerMsg.h new file mode 100644 index 00000000..2ba1ee24 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plTriggerMsg.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plTriggerMsg_inc +#define plTriggerMsg_inc + +#include "../pnMessage/plMessage.h" + + +class plTriggerMsg : public plMessage +{ +protected: + +public: + + plTriggerMsg(){SetBCastFlag(plMessage::kBCastByExactType | plMessage::kPropagateToModifiers);} + plTriggerMsg(const plKey &s, + const plKey &r, + const double* t){SetBCastFlag(plMessage::kBCastByExactType | plMessage::kPropagateToModifiers);} + ~plTriggerMsg(){;} + + CLASSNAME_REGISTER( plTriggerMsg ); + GETINTERFACE_ANY( plTriggerMsg, plMessage ); + + // IO + void Read(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgRead(stream, mgr); } + void Write(hsStream* stream, hsResMgr* mgr) { plMessage::IMsgWrite(stream, mgr); } +}; + + + + +#endif // TriggerMsg diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plUniqueIdsMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plUniqueIdsMsg.h new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plUniqueIdsMsg.h @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plVaultNotifyMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plVaultNotifyMsg.cpp new file mode 100644 index 00000000..8dc090aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plVaultNotifyMsg.cpp @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "plVaultNotifyMsg.h" + + +plVaultNotifyMsg::plVaultNotifyMsg() +: fType( kNothing ) +, fResultCode( hsOK ) +{ + SetBCastFlag( kBCastByType ); +} + +plVaultNotifyMsg::~plVaultNotifyMsg() +{ +} + +void plVaultNotifyMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + stream->ReadSwap( &fType ); + stream->ReadSwap( &fResultCode ); + fArgs.Read( stream, mgr ); +} + +void plVaultNotifyMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + stream->WriteSwap( fType ); + stream->WriteSwap( fResultCode ); + fArgs.Write( stream, mgr ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plVaultNotifyMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plVaultNotifyMsg.h new file mode 100644 index 00000000..6d6d36a5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plMessage/plVaultNotifyMsg.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plVaultNotifyMsg_h_inc +#define plVaultNotifyMsg_h_inc + +#include "../pnMessage/plMessage.h" +#include "../plNetCommon/plNetCommonHelpers.h" +#include "../plNetCommon/plNetCommon.h" + +class plVaultNotifyMsg : public plMessage +{ + UInt16 fType; + plCreatableListHelper fArgs; + Int8 fResultCode; + +public: + enum VaultNotifyTypes + { + kNothing, + kRegisteredOwnedAge = plNetCommon::VaultTasks::kRegisterOwnedAge, + kRegisteredVisitAge = plNetCommon::VaultTasks::kRegisterVisitAge, + kUnRegisteredOwnedAge = plNetCommon::VaultTasks::kUnRegisterOwnedAge, + kUnRegisteredVisitAge = plNetCommon::VaultTasks::kUnRegisterVisitAge, + kPublicAgeCreated, + kPublicAgeRemoved + }; + + plVaultNotifyMsg(); + ~plVaultNotifyMsg(); + + CLASSNAME_REGISTER( plVaultNotifyMsg ); + GETINTERFACE_ANY_AUX( plVaultNotifyMsg, plMessage, plCreatableListHelper, fArgs ); + + UInt16 GetType() const { return fType; } + void SetType( UInt16 v ) { fType=v; } + + Int8 GetResultCode() const { return fResultCode; } + void SetResultCode( Int8 v ) { fResultCode=v; } + + plCreatableListHelper * GetArgs() { return &fArgs; } + const plCreatableListHelper * GetArgs() const { return &fArgs; } + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plVaultNotifyMsg_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimEventModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimEventModifier.cpp new file mode 100644 index 00000000..16053a5f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimEventModifier.cpp @@ -0,0 +1,127 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAnimEventModifier.h" + +#include "hsResMgr.h" +#include "../pnMessage/plMessage.h" + +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plEventCallbackMsg.h" + +plAnimEventModifier::plAnimEventModifier() : fCallback(nil), fDisabled(false) +{ +} + +plAnimEventModifier::~plAnimEventModifier() +{ + hsRefCnt_SafeUnRef(fCallback); +} + +void plAnimEventModifier::Read(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Read(stream, mgr); + + int numReceivers = stream->ReadSwap32(); + fReceivers.Expand(numReceivers); + for (int i = 0; i < numReceivers; i++) + fReceivers.Push(mgr->ReadKey(stream)); + + fCallback = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); + + // + // Create a passive ref to the animation controller. Then, when we're + // notified that it's loaded we can send our callback setup message. + // + plKey animKey = fCallback->GetReceiver(0); + hsgResMgr::ResMgr()->AddViaNotify(animKey, + TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0), + plRefFlags::kPassiveRef); +} + +void plAnimEventModifier::Write(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Write(stream, mgr); + + int numReceivers = fReceivers.Count(); + stream->WriteSwap32(numReceivers); + for (int i = 0; i < numReceivers; i++) + mgr->WriteKey(stream, fReceivers[i]); + + mgr->WriteCreatable(stream, fCallback); +} + +hsBool plAnimEventModifier::MsgReceive(plMessage* msg) +{ + // Assuming we only have one ref, the anim time convert. When it loads, we + // send our callback setup message. + plGenRefMsg* genRefMsg = plGenRefMsg::ConvertNoRef(msg); + if (genRefMsg && (genRefMsg->GetContext() & plRefMsg::kOnCreate) && fCallback) + { + hsRefCnt_SafeRef(fCallback); + fCallback->Send(); + } + + plEventCallbackMsg* callbackMsg = plEventCallbackMsg::ConvertNoRef(msg); + if (callbackMsg) + { + ISendNotify(true); + ISendNotify(false); + } + + plEnableMsg* pEnable = plEnableMsg::ConvertNoRef(msg); + if (pEnable) + { + if (pEnable->Cmd(plEnableMsg::kDisable)) + fDisabled = true; + else + if (pEnable->Cmd(plEnableMsg::kEnable)) + fDisabled = false; + return true; + } + return plSingleModifier::MsgReceive(msg); +} + +#include "../pnNetCommon/plNetApp.h" +#include "../pnMessage/plNotifyMsg.h" + +void plAnimEventModifier::ISendNotify(bool triggered) +{ + if (fDisabled) + return; + plNotifyMsg* notify = TRACKED_NEW plNotifyMsg; + + // Setup the event data in case this is a OneShot responder that needs it + plKey playerKey = plNetClientApp::GetInstance()->GetLocalPlayerKey(); + notify->AddPickEvent(playerKey, nil, true, hsPoint3(0,0,0) ); + + notify->SetSender(GetKey()); + notify->AddReceivers(fReceivers); + notify->SetState(triggered ? 1.0f : 0.0f); + // these should never need to net propagate... + notify->SetBCastFlag(plMessage::kNetPropagate, false); + notify->Send(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimEventModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimEventModifier.h new file mode 100644 index 00000000..e0c85cec --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimEventModifier.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAnimEventModifier_h_inc +#define plAnimEventModifier_h_inc + +#include "../pnModifier/plSingleModifier.h" +#include "hsTemplates.h" + +// +// Detects an anim event (marker, begin, end) and sends out a notification. +// +class plAnimEventModifier : public plSingleModifier +{ +protected: + hsTArray fReceivers;// Keys to notify when the anim event happens + plMessage* fCallback; // The callback setup message we send when the anim loads + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return false; } + + void ISendNotify(bool triggered); + hsBool fDisabled; +public: + plAnimEventModifier(); + virtual ~plAnimEventModifier(); + + CLASSNAME_REGISTER(plAnimEventModifier); + GETINTERFACE_ANY(plAnimEventModifier, plSingleModifier); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + // Export only + void SetReceivers(hsTArray& receivers) { fReceivers = receivers; } + void SetCallback(plMessage* callback) { fCallback = callback; } +}; + +#endif // plAnimEventModifier_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimTimeConvertSDLModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimTimeConvertSDLModifier.cpp new file mode 100644 index 00000000..3526b43a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimTimeConvertSDLModifier.cpp @@ -0,0 +1,127 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTimer.h" +#include "plAnimTimeConvertSDLModifier.h" +#include "../plSDL/plSDL.h" +#include "../plInterp/plAnimTimeConvert.h" + +// static vars +char plAnimTimeConvertSDLModifier::AnimTimeConvertVarNames::kStrFlags[]="flags"; +char plAnimTimeConvertSDLModifier::AnimTimeConvertVarNames::kStrLastStateAnimTime[]="lastStateAnimTime"; +char plAnimTimeConvertSDLModifier::AnimTimeConvertVarNames::kStrLoopBegin[]="loopBegin"; +char plAnimTimeConvertSDLModifier::AnimTimeConvertVarNames::kStrLoopEnd[]="loopEnd"; +char plAnimTimeConvertSDLModifier::AnimTimeConvertVarNames::kStrSpeed[]="speed"; +char plAnimTimeConvertSDLModifier::AnimTimeConvertVarNames::kStrCurrentEaseCurve[]="currentEaseCurve"; +char plAnimTimeConvertSDLModifier::AnimTimeConvertVarNames::kStrCurrentEaseBeginWorldTime[]="currentEaseBeginWorldTime"; +char plAnimTimeConvertSDLModifier::AnimTimeConvertVarNames::kStrLastStateChange[]="lastStateChange"; + +// +// Copy atcs from current state into sdl +// +void plAnimTimeConvertSDLModifier::IPutATC(plStateDataRecord* atcStateDataRec, plAnimTimeConvert* animTimeConvert) +{ + plATCState *lastState = animTimeConvert->fStates.front(); + atcStateDataRec->FindVar(AnimTimeConvertVarNames::kStrFlags)->Set(animTimeConvert->fFlags); + atcStateDataRec->FindVar(AnimTimeConvertVarNames::kStrLastStateAnimTime)->Set(lastState->fStartAnimTime); + atcStateDataRec->FindVar(AnimTimeConvertVarNames::kStrLoopEnd)->Set(animTimeConvert->fLoopEnd); + atcStateDataRec->FindVar(AnimTimeConvertVarNames::kStrLoopBegin)->Set(animTimeConvert->fLoopBegin); + atcStateDataRec->FindVar(AnimTimeConvertVarNames::kStrSpeed)->Set(animTimeConvert->fSpeed); + atcStateDataRec->FindVar(AnimTimeConvertVarNames::kStrLastStateChange)->Set(lastState->fStartWorldTime); + + int curEaseCurve = animTimeConvert->GetCurrentEaseCurve(); + atcStateDataRec->FindVar(AnimTimeConvertVarNames::kStrCurrentEaseCurve)->Set(curEaseCurve); + + atcStateDataRec->FindVar(AnimTimeConvertVarNames::kStrCurrentEaseBeginWorldTime)->Set(curEaseCurve ? + animTimeConvert->fCurrentEaseCurve->fBeginWorldTime : 0); +} + +// +// Apply state in SDL record to current animation state +// +//#include "../pnSceneObject/plSceneObject.h" +void plAnimTimeConvertSDLModifier::ISetCurrentATC(const plStateDataRecord* atcStateDataRec, plAnimTimeConvert* objAtc) +{ +// if ( GetTarget(0)->GetKeyName() && stricmp( GetTarget(0)->GetKeyName(), "RTDirLight01" )==0 ) +// { +// int xx=0; +// } + if (atcStateDataRec->IsUsed()) + { + plStateDataRecord::SimpleVarsList vars; + int num=atcStateDataRec->GetUsedVars(&vars); + int j; + hsScalar lastStateAnimTime = 0; + double lastStateChange = 0; + for(j=0;jIsNamed(AnimTimeConvertVarNames::kStrFlags)) + { + int f; + vars[j]->Get(&f); + objAtc->fFlags=f; + } + else + if (vars[j]->IsNamed(AnimTimeConvertVarNames::kStrLastStateAnimTime)) + vars[j]->Get(&lastStateAnimTime); + else + if (vars[j]->IsNamed(AnimTimeConvertVarNames::kStrLoopBegin)) + vars[j]->Get(&objAtc->fLoopBegin); + else + if (vars[j]->IsNamed(AnimTimeConvertVarNames::kStrLoopEnd)) + vars[j]->Get(&objAtc->fLoopEnd); + else + if (vars[j]->IsNamed(AnimTimeConvertVarNames::kStrSpeed)) + vars[j]->Get(&objAtc->fSpeed); + else + if (vars[j]->IsNamed(AnimTimeConvertVarNames::kStrLastStateChange)) + vars[j]->Get(&lastStateChange); + else + if (vars[j]->IsNamed(AnimTimeConvertVarNames::kStrCurrentEaseCurve)) + { + int ces; + vars[j]->Get(&ces); + if (ces == plAnimTimeConvert::kEaseSpeed) + { + // I don't think this ever happens in practice. If it becomes necessary, + // we can work around it. But unless it's actually used, I don't want + // to waste the space storing a speed ease curve. + objAtc->SetCurrentEaseCurve(plAnimTimeConvert::kEaseNone); + } + else + objAtc->SetCurrentEaseCurve(ces); // The ATC will ignore an index out of range + } + else + if (vars[j]->IsNamed(AnimTimeConvertVarNames::kStrCurrentEaseBeginWorldTime) && objAtc->fCurrentEaseCurve) + vars[j]->Get(&objAtc->fCurrentEaseCurve->fBeginWorldTime); + } + objAtc->IClearAllStates(); + objAtc->IProcessStateChange(lastStateChange, lastStateAnimTime); + objAtc->fCurrentAnimTime = lastStateAnimTime; + objAtc->fLastEvalWorldTime = lastStateChange; + objAtc->SetFlag(plAnimTimeConvert::kForcedMove, true); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimTimeConvertSDLModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimTimeConvertSDLModifier.h new file mode 100644 index 00000000..63c11e78 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAnimTimeConvertSDLModifier.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAnimTimeConvertSDLModifier_inc +#define plAnimTimeConvertSDLModifier_inc + +#include "../plModifier/plSDLModifier.h" + +// +// This modifier (abstract baseclass) handles sending and recving +// the state for an animTimeConvert +// +class plStateDataRecord; +class plAnimTimeConvert; + +class plAnimTimeConvertSDLModifier : public plSDLModifier +{ +protected: + // var labels + struct AnimTimeConvertVarNames + { + static char kStrFlags[]; + static char kStrLastStateAnimTime[]; + static char kStrLoopBegin[]; + static char kStrLoopEnd[]; + static char kStrSpeed[]; + static char kStrCurrentEaseCurve[]; + static char kStrCurrentEaseBeginWorldTime[]; + static char kStrLastStateChange[]; + }; + + void IPutATC(plStateDataRecord* state, plAnimTimeConvert* curAnimTimeConvert); + void ISetCurrentATC(const plStateDataRecord* state, plAnimTimeConvert* curAnimTimeConvert); + +public: + CLASSNAME_REGISTER( plAnimTimeConvertSDLModifier); + GETINTERFACE_ANY( plAnimTimeConvertSDLModifier, plSDLModifier); +}; + +#endif // plAnimTimeConvertSDLModifier_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAxisAnimModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAxisAnimModifier.cpp new file mode 100644 index 00000000..f5878dfb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAxisAnimModifier.cpp @@ -0,0 +1,373 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsUtils.h" +#include "plAxisAnimModifier.h" +#include "hsResMgr.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnKeyedObject/plKey.h" +#include "../plNetMessage/plNetMsgHelpers.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plCmdIfaceModMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../plInputCore/plInputDevice.h" +#include "../plInputCore/plInputManager.h" +#include "../plInputCore/plInputInterface.h" +#include "../pnNetCommon/plNetApp.h" +#include "plgDispatch.h" + +//// plAxisInputInterface //////////////////////////////////////////////////// +// Small input interface to handle mouse imput while dragging. Supercedes +// the sceneInteraction Interface + +class plAxisInputInterface : public plInputInterface +{ + protected: + + plAxisAnimModifier *fOwner; + + virtual ControlEventCode *IGetOwnedCodeList( void ) const + { + static ControlEventCode codes[] = { END_CONTROLS }; + return codes; + } + + public: + + plAxisInputInterface( plAxisAnimModifier *owner ) { fOwner = owner; SetEnabled( true ); } + + virtual UInt32 GetPriorityLevel( void ) const { return kSceneInteractionPriority + 10; } + virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg ) + { + plMouseEventMsg* pMMsg = plMouseEventMsg::ConvertNoRef( pMsg ); + if (pMMsg ) + { + if( pMMsg->GetButton() == kLeftButtonUp ) + { + // Remove ourselves from the stack + plInputIfaceMgrMsg *msg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kRemoveInterface ); + msg->SetIFace( this ); + plgDispatch::MsgSend( msg ); + return true; + } + return fOwner->MsgReceive( pMMsg ); + } + + return false; + } + + virtual UInt32 GetCurrentCursorID( void ) const { return kCursorGrab; } + virtual hsBool HasInterestingCursorID( void ) const { return true; } +}; + +plAxisAnimModifier::plAxisAnimModifier() : +fXAnim(nil), +fYAnim(nil), +fActive(false), +fXPos(0.0f), +fYPos(0.0f), +fIface(0), +fAllOrNothing(false) +{ + fNotify = TRACKED_NEW plNotifyMsg; + fInputIface = TRACKED_NEW plAxisInputInterface( this ); +} +plAxisAnimModifier::~plAxisAnimModifier() +{ + hsRefCnt_SafeUnRef(fNotify); + hsRefCnt_SafeUnRef( fInputIface ); +} + + +hsBool plAxisAnimModifier::IEval(double secs, hsScalar del, UInt32 dirty) +{ + if (!fActive) + return true; + + return true; +} + +void plAxisAnimModifier::SetTarget(plSceneObject* so) +{ + plSingleModifier::SetTarget(so); + + if( so ) + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); +} + +hsBool plAxisAnimModifier::MsgReceive(plMessage* msg) +{ + plEventCallbackMsg* pCall = plEventCallbackMsg::ConvertNoRef(msg); + if (pCall) + { + // Send our notification to whomever cares; + hsScalar time = 0.0f; + if (pCall->fEvent == kEnd) + time = 1.0f; + fNotify->ClearEvents(); + fNotify->SetSender(fNotificationKey); // so python can handle it. + fNotify->AddCallbackEvent( pCall->fEvent ); + fNotify->SetState(1.0f); + fNotify->AddActivateEvent(true); + fNotify->AddClickDragEvent(GetTarget()->GetKey(), plNetClientApp::GetInstance()->GetLocalPlayerKey(), time); + hsRefCnt_SafeRef(fNotify); + plgDispatch::MsgSend( fNotify ); + return true; + } + + plNotifyMsg* pNMsg = plNotifyMsg::ConvertNoRef(msg); + if (pNMsg) + { + for (int i = 0; i < pNMsg->GetEventCount(); i++) + { + if (pNMsg->GetEventRecord(i)->fEventType == proEventData::kActivate) + { + if( ( (proActivateEventData *)pNMsg->GetEventRecord(i) )->fActivate ) + { + fActive = true; + fXPos = plMouseDevice::Instance()->GetCursorX(); + fYPos = plMouseDevice::Instance()->GetCursorY(); + + // Insert our input interface onto the input stack + plInputIfaceMgrMsg *msg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kAddInterface ); + msg->SetIFace( fInputIface ); + plgDispatch::MsgSend( msg ); + } + else + { +/* if (fActive) + { + fActive = false; + + // Remove our input interface from the input stack + plInputIfaceMgrMsg *msg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kRemoveInterface ); + msg->SetIFace( fInputIface ); + plgDispatch::MsgSend( msg ); + } +*/ } + break; + } + } + return true; + } + + plMouseEventMsg* pMMsg = plMouseEventMsg::ConvertNoRef(msg); + if (pMMsg) + { + if (fXAnim && pMMsg->GetDX() != 0.0f) + { + if (pMMsg->GetDX() > 0.05f) + return true; + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->AddReceiver(fXAnim); + pMsg->SetAnimName(fAnimLabel.c_str()); + // pMsg->SetAnimName() + if (fXPos < pMMsg->GetXPos()) + { + pMsg->SetCmd(plAnimCmdMsg::kIncrementForward); + } + else + { + pMsg->SetCmd(plAnimCmdMsg::kIncrementBackward); + } + plgDispatch::MsgSend(pMsg); + fXPos = pMMsg->GetXPos(); + } + if (fYAnim && pMMsg->GetDY() != 0.0f) + { + + if (pMMsg->GetDY() > 0.05f) + return true; + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->AddReceiver(fYAnim); + pMsg->SetAnimName(fAnimLabel.c_str()); + if (fYPos > pMMsg->GetYPos()) + { + if (fAllOrNothing) + { + pMsg->SetCmd(plAnimCmdMsg::kContinue); + pMsg->SetCmd(plAnimCmdMsg::kSetForewards); + fActive = false; + + // Remove our input interface from the input stack + plInputIfaceMgrMsg *msg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kRemoveInterface ); + msg->SetIFace( fInputIface ); + plgDispatch::MsgSend( msg ); + plInputManager::SetRecenterMouse(false); + } + else + { + pMsg->SetCmd(plAnimCmdMsg::kIncrementForward); + } + } + else + { + if (fAllOrNothing) + { + pMsg->SetCmd(plAnimCmdMsg::kContinue); + pMsg->SetCmd(plAnimCmdMsg::kSetBackwards); + fActive = false; + + // Remove our input interface from the input stack + plInputIfaceMgrMsg *msg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kRemoveInterface ); + msg->SetIFace( fInputIface ); + plgDispatch::MsgSend( msg ); + + plInputManager::SetRecenterMouse(false); + } + else + { + pMsg->SetCmd(plAnimCmdMsg::kIncrementBackward); + } + } + plgDispatch::MsgSend(pMsg); + fYPos = pMMsg->GetYPos(); + } + + return true; + } + + plCmdIfaceModMsg* pCMsg = plCmdIfaceModMsg::ConvertNoRef(msg); + if (pCMsg && pCMsg->Cmd(plCmdIfaceModMsg::kIndexCallback)) + { + fIface = pCMsg->fIndex; + return true; + } + + plGenRefMsg* pRefMsg = plGenRefMsg::ConvertNoRef(msg); + if (pRefMsg) + { + if (pRefMsg->GetContext() == plRefMsg::kOnCreate ) + { + if (pRefMsg->fType == kTypeX) + { + fXAnim = pRefMsg->GetRef()->GetKey(); + + // add callbacks for beginning and end of animation + plEventCallbackMsg* pCall1 = TRACKED_NEW plEventCallbackMsg; + pCall1->fEvent = kBegin; + pCall1->fRepeats = -1; + pCall1->AddReceiver(GetKey()); + + plEventCallbackMsg* pCall2 = TRACKED_NEW plEventCallbackMsg; + pCall2->fEvent = kEnd; + pCall2->fRepeats = -1; + pCall2->AddReceiver(GetKey()); + + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); + pMsg->AddCallback(pCall1); + pMsg->AddCallback(pCall2); + pMsg->SetAnimName(fAnimLabel.c_str()); + pMsg->AddReceiver( fXAnim ); + + hsRefCnt_SafeUnRef( pCall1 ); + hsRefCnt_SafeUnRef( pCall2 ); + + plgDispatch::MsgSend(pMsg); + } + else + if (pRefMsg->fType == kTypeY) + { + fYAnim = pRefMsg->GetRef()->GetKey(); + + // add callbacks for beginning and end of animation + plEventCallbackMsg* pCall1 = TRACKED_NEW plEventCallbackMsg; + pCall1->fEvent = kBegin; + pCall1->fRepeats = -1; + pCall1->AddReceiver(GetKey()); + + plEventCallbackMsg* pCall2 = TRACKED_NEW plEventCallbackMsg; + pCall2->fEvent = kEnd; + pCall2->fRepeats = -1; + pCall2->AddReceiver(GetKey()); + + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); + pMsg->AddCallback(pCall1); + pMsg->AddCallback(pCall2); + pMsg->AddReceiver( fYAnim ); + pMsg->SetAnimName(fAnimLabel.c_str()); + + hsRefCnt_SafeUnRef( pCall1 ); + hsRefCnt_SafeUnRef( pCall2 ); + + plgDispatch::MsgSend(pMsg); + } + else + if (pRefMsg->fType == kTypeLogic) + { + SetNotificationKey(pRefMsg->GetRef()); + } + } + else + if (pRefMsg->GetContext() == plRefMsg::kOnDestroy ) + { + if (pRefMsg->fType == kTypeX) + fXAnim = nil; + else + if (pRefMsg->fType == kTypeY) + fYAnim = nil; + else + if (pRefMsg->fType == kTypeLogic) + fNotificationKey = nil; + } + return true; + } + + return plSingleModifier::MsgReceive(msg); +} + +void plAxisAnimModifier::Read(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Read(s, mgr); + + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kTypeX), plRefFlags::kPassiveRef); + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kTypeY), plRefFlags::kPassiveRef); + mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kTypeLogic), plRefFlags::kPassiveRef); + + fAllOrNothing = s->ReadBool(); + plNotifyMsg* pMsg = plNotifyMsg::ConvertNoRef(mgr->ReadCreatable(s)); + if (fNotify) + delete(fNotify); + fNotify = pMsg; + plMsgStdStringHelper::Peek(fAnimLabel, s); +} + +void plAxisAnimModifier::Write(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Write(s, mgr); + mgr->WriteKey(s, fXAnim); + mgr->WriteKey(s, fYAnim); + mgr->WriteKey(s, fNotificationKey); + s->WriteBool(fAllOrNothing); + mgr->WriteCreatable(s, fNotify); + plMsgStdStringHelper::Poke(fAnimLabel, s); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAxisAnimModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAxisAnimModifier.h new file mode 100644 index 00000000..7a9894c2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plAxisAnimModifier.h @@ -0,0 +1,92 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAxisAnimMod_inc +#define plAxisAnimMod_inc + +#include "hsStlUtils.h" +#include "../pnModifier/plSingleModifier.h" + + +class plKey; +class plNotifyMsg; +class plAxisInputInterface; + +class plAxisAnimModifier : public plSingleModifier +{ +public: + enum + { + kTypeX, + kTypeY, + kTypeLogic, + }; +protected: + + plKey fXAnim; + plKey fYAnim; + plKey fNotificationKey; + + hsScalar fXPos; + hsScalar fYPos; + + hsBool fActive; + hsBool fAllOrNothing; + int fIface; + plNotifyMsg* fNotify; + + std::string fAnimLabel; + + plAxisInputInterface *fInputIface; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + +public: + plAxisAnimModifier(); + virtual ~plAxisAnimModifier(); + + CLASSNAME_REGISTER( plAxisAnimModifier ); + GETINTERFACE_ANY( plAxisAnimModifier, plSingleModifier ); + + virtual hsBool MsgReceive(plMessage* msg); + virtual void SetTarget(plSceneObject* so); + + void SetAllOrNothing(hsBool b) { fAllOrNothing = b; } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void SetXAnim(plKey c) { fXAnim = c; } + void SetYAnim(plKey c) { fYAnim = c; } + void SetNotificationKey(plKey k) { fNotificationKey = k; } + plNotifyMsg* GetNotify() { return fNotify; } + + const char* GetAnimLabel() const {return fAnimLabel.c_str();} + void SetAnimLabel(const char* a) {fAnimLabel = a; } + +}; + +#endif // plAxisAnimMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plCloneSpawnModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plCloneSpawnModifier.cpp new file mode 100644 index 00000000..954e7d2d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plCloneSpawnModifier.cpp @@ -0,0 +1,118 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plCloneSpawnModifier.h" + +#include "hsResMgr.h" +#include "../plResMgr/plResManager.h" +#include "../plResMgr/plKeyFinder.h" +#include "../pnSceneObject/plSceneObject.h" + +#include "../plScene/plSceneNode.h" +#include "../pnMessage/plClientMsg.h" +#include "plgDispatch.h" +#include "../pnMessage/plWarpMsg.h" +#include "../pnMessage/plNodeRefMsg.h" + +plCloneSpawnModifier::plCloneSpawnModifier() : fTemplateName(nil), fExportTime(false) +{ +} + +plCloneSpawnModifier::~plCloneSpawnModifier() +{ + delete [] fTemplateName; +} + +void plCloneSpawnModifier::Read(hsStream *s, hsResMgr *mgr) +{ + delete [] fTemplateName; + fTemplateName = s->ReadSafeString(); + plSingleModifier::Read(s, mgr); +} + +void plCloneSpawnModifier::Write(hsStream *s, hsResMgr *mgr) +{ + s->WriteSafeString(fTemplateName); + plSingleModifier::Write(s, mgr); +} + +void plCloneSpawnModifier::SetTemplateName(const char *templateName) +{ + delete [] fTemplateName; + fTemplateName = hsStrcpy(templateName); +} + +void plCloneSpawnModifier::SetTarget(plSceneObject* so) +{ + fTarget = so; + // Spawning the clone here since at Read time fTarget isn't set. Kind of a hack though. + if (fTarget && !fExportTime) + { + // Assume the clone template is in the same age we are + const plLocation& loc = GetKey()->GetUoid().GetLocation(); + char ageName[256]; + ((plResManager*)hsgResMgr::ResMgr())->GetLocationStrings(loc, ageName, nil); + + // Spawn the clone + plKey cloneKey = SpawnClone(fTemplateName, ageName, GetTarget()->GetLocalToWorld(), GetKey()); + } +} + +#include "../plMessage/plLoadCloneMsg.h" + +plKey plCloneSpawnModifier::SpawnClone(const char* cloneName, const char* cloneAge, const hsMatrix44& pos, plKey requestor) +{ + plResManager* resMgr = (plResManager*)hsgResMgr::ResMgr(); + + // Find the clone room for this age + const plLocation& loc = plKeyFinder::Instance().FindLocation(cloneAge, "BuiltIn"); + + // Find the clone template key + plUoid objUoid(loc, plSceneObject::Index(), cloneName); + plKey key = resMgr->FindKey(objUoid); + + if (key) + { + plLoadCloneMsg* cloneMsg = TRACKED_NEW plLoadCloneMsg(objUoid, requestor, 0); + cloneMsg->SetBCastFlag(plMessage::kMsgWatch); + plKey cloneKey = cloneMsg->GetCloneKey();//resMgr->CloneKey(key); + cloneMsg->Send(); + + // Put the clone into the clone room, which also forces it to load. + plKey cloneNodeKey = resMgr->FindKey(kNetClientCloneRoom_KEY); + plNodeRefMsg* nodeRefCloneMsg = TRACKED_NEW plNodeRefMsg(cloneNodeKey, plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kObject); + resMgr->AddViaNotify(cloneKey, nodeRefCloneMsg, plRefFlags::kActiveRef); + + // Warp it into position + plWarpMsg *warpMsg = TRACKED_NEW plWarpMsg; + warpMsg->AddReceiver(cloneKey); + warpMsg->SetTransform(pos); + plgDispatch::MsgSend(warpMsg); + + return cloneKey; + } + + return nil; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plCloneSpawnModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plCloneSpawnModifier.h new file mode 100644 index 00000000..d59df73b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plCloneSpawnModifier.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plCloneSpawnModifier_inc +#define plCloneSpawnModifier_inc + +#include "../pnModifier/plSingleModifier.h" + +class plCloneSpawnModifier : public plSingleModifier +{ +protected: + char* fTemplateName; + bool fExportTime; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return true; } + +public: + plCloneSpawnModifier(); + ~plCloneSpawnModifier(); + + CLASSNAME_REGISTER(plCloneSpawnModifier); + GETINTERFACE_ANY(plCloneSpawnModifier, plSingleModifier); + + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); + + virtual void SetTarget(plSceneObject* so); + + void SetTemplateName(const char *templateName); + + // Set this to true at export time so the clone mod won't try to make a + // clone when it's attached + void SetExportTime() { fExportTime = true; } + + // Console backdoor + static plKey SpawnClone(const char* cloneName, const char* cloneAge, const hsMatrix44& pos, plKey requestor); +}; + +#endif // plCloneSpawnModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDecalEnableMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDecalEnableMod.cpp new file mode 100644 index 00000000..d2fdff2f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDecalEnableMod.cpp @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#include "hsTypes.h" +#include "plDecalEnableMod.h" +#include "../plMessage/plDynaDecalEnableMsg.h" +#include "../plMessage/plCollideMsg.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../plAvatar/plArmatureMod.h" + +#include "hsTimer.h" +#include "hsStream.h" +#include "hsResMgr.h" + +plDecalEnableMod::plDecalEnableMod() +{ +} + +plDecalEnableMod::~plDecalEnableMod() +{ +} + +hsBool plDecalEnableMod::MsgReceive(plMessage* msg) +{ + plCollideMsg* coll = plCollideMsg::ConvertNoRef(msg); + if( coll ) + { + plSceneObject* obj = plSceneObject::ConvertNoRef(coll->fOtherKey->ObjectIsLoaded()); + if( !obj ) + return true; + + const plArmatureMod* arm = (const plArmatureMod*)obj->GetModifierByType(plArmatureMod::Index()); + if( !arm ) + return true; + + plKey armKey = arm->GetKey(); + + int i; + for( i = 0; i < fDecalMgrs.GetCount(); i++ ) + { + plDynaDecalEnableMsg* ena = TRACKED_NEW plDynaDecalEnableMsg(fDecalMgrs[i], armKey, hsTimer::GetSysSeconds(), fWetLength, !coll->fEntering); + + ena->Send(); + } + return true; + } + + return plSingleModifier::MsgReceive(msg); +} + +void plDecalEnableMod::Read(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Read(stream, mgr); + + int n = stream->ReadSwap32(); + fDecalMgrs.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + fDecalMgrs[i] = mgr->ReadKey(stream); + + fWetLength = stream->ReadSwapScalar(); +} + +void plDecalEnableMod::Write(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Write(stream, mgr); + + stream->WriteSwap32(fDecalMgrs.GetCount()); + + int i; + for( i = 0; i < fDecalMgrs.GetCount(); i++ ) + mgr->WriteKey(stream, fDecalMgrs[i]); + + stream->WriteSwapScalar(fWetLength); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDecalEnableMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDecalEnableMod.h new file mode 100644 index 00000000..50168443 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDecalEnableMod.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDecalEnableMod_inc +#define plDecalEnableMod_inc + +#include "hsTemplates.h" +#include "../pnModifier/plSingleModifier.h" +#include "../pnKeyedObject/plKey.h" + +class plDecalEnableMod : public plSingleModifier +{ +protected: + + hsTArray fDecalMgrs; + + hsScalar fWetLength; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return false; } + +public: + plDecalEnableMod(); + virtual ~plDecalEnableMod(); + + CLASSNAME_REGISTER( plDecalEnableMod ); + GETINTERFACE_ANY( plDecalEnableMod, plSingleModifier ); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void SetWetLength(hsScalar t) { fWetLength = t; } + hsScalar GetWetLength() const { return fWetLength; } + + void AddDecalKey(const plKey& k) { fDecalMgrs.Append(k); } + UInt32 GetNumDecalKeys() const { return fDecalMgrs.GetCount(); } + const plKey& GetDecalKey(int i) const { return fDecalMgrs[i]; } +}; + +#endif // plDecalEnableMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDetectorLog.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDetectorLog.cpp new file mode 100644 index 00000000..39ff5a0b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDetectorLog.cpp @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plDetectorLog.h" +#include "../plStatusLog/plStatusLog.h" + +// Don't bother logging detectors in the external release, since it isn't written to disk +#ifdef PLASMA_EXTERNAL_RELEASE + +void DetectorLog(const char* format, ...) {} +void DetectorLogSpecial(const char* format, ...) {} +void DetectorLogRed(const char* format, ...) {} + +#else + +static plStatusLog* gLog = +plStatusLogMgr::GetInstance().CreateStatusLog( + 20, + "Detector", + plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | + plStatusLog::kDontWriteFile | plStatusLog::kAlignToTop); + + +void DetectorDoLogfile() +{ + delete gLog; + gLog = plStatusLogMgr::GetInstance().CreateStatusLog(20,"Detector.log",plStatusLog::kFilledBackground|plStatusLog::kDeleteForMe|plStatusLog::kAlignToTop); + +} + +void DetectorLog(const char* format, ...) +{ + va_list args; + va_start(args, format); + gLog->AddLineV(format, args); + va_end(args); +} + +void DetectorLogSpecial(const char* format, ...) +{ + va_list args; + va_start(args, format); + gLog->AddLineV(plStatusLog::kGreen, format, args); + va_end(args); +} + +void DetectorLogRed(const char* format, ...) +{ + va_list args; + va_start(args, format); + gLog->AddLineV(plStatusLog::kRed, format, args); + va_end(args); +} + + +#endif // PLASMA_EXTERNAL_RELEASE diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDetectorLog.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDetectorLog.h new file mode 100644 index 00000000..c07593aa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plDetectorLog.h @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plDetectorLog_h_inc +#define plDetectorLog_h_inc + +void DetectorLog(const char* format, ...); +void DetectorLogSpecial(const char* format, ...); +void DetectorLogRed(const char* format, ...); +void DetectorDoLogfile(); + +#endif // plDetectorLog_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp new file mode 100644 index 00000000..8071e66c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.cpp @@ -0,0 +1,387 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plExcludeRegionModifier.h" +#include "../plMessage/plExcludeRegionMsg.h" +#include "hsTemplates.h" +#include "hsResMgr.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "plDetectorLog.h" +// For MsgReceive +#include "../plMessage/plCollideMsg.h" +#include "../pnSceneObject/plSceneObject.h" + +// For IClear and IRelease +#include "../pnMessage/plWarpMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "plPhysical.h" +#include "../plPhysical/plSimDefs.h" +#include "../plAvatar/plAvCallbackAction.h" + +#include "../plAvatar/plAvBrainGeneric.h" + +#include "../plSDL/plSDL.h" +#include "../pnMessage/plSDLModifierMsg.h" +//for hack +#include "../plPhysX/plPXPhysical.h" +#include "../plPhysX/plPXPhysicalControllerCore.h" +#include "NxCapsule.h" +static plPhysical* GetPhysical(plSceneObject* obj) +{ + if (obj) + { + const plSimulationInterface* si = obj->GetSimulationInterface(); + if (si) + return si->GetPhysical(); + } + return nil; +} + +plExcludeRegionModifier::plExcludeRegionModifier() : + fSDLModifier(nil), + fSeek(false), + fSeekTime(10.0f) +{ +} + + +plExcludeRegionModifier::~plExcludeRegionModifier() +{ +} + +void plExcludeRegionModifier::Read(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Read(stream, mgr); + + int numPoints = stream->ReadSwap32(); + for (int i = 0; i < numPoints; i++) + { + fSafePoints.push_back(mgr->ReadKey(stream)); + } + fSeek = stream->ReadBool(); + fSeekTime = stream->ReadSwapScalar(); +} + +void plExcludeRegionModifier::Write(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Write(stream, mgr); + + int numPoints = fSafePoints.size(); + stream->WriteSwap32(numPoints); + for (int i = 0; i < numPoints; i++) + { + mgr->WriteKey(stream,fSafePoints[i]); + } + stream->WriteBool(fSeek); + stream->WriteSwapScalar(fSeekTime); +} + +void plExcludeRegionModifier::ISetPhysicalState(bool cleared) +{ + plPhysical* phys = GetPhysical(GetTarget()); + if (phys) + { + phys->ExcludeRegionHack(cleared); + + if (cleared) + { + phys->AddLOSDB(plSimDefs::kLOSDBUIBlockers); + if (HasFlag(kBlockCameras)) + phys->AddLOSDB(plSimDefs::kLOSDBCameraBlockers); + } + else + { + phys->RemoveLOSDB(plSimDefs::kLOSDBUIBlockers); + if (HasFlag(kBlockCameras)) + phys->RemoveLOSDB(plSimDefs::kLOSDBCameraBlockers); + } + } +} + +hsBool plExcludeRegionModifier::MsgReceive(plMessage* msg) +{ + plExcludeRegionMsg *exclMsg = plExcludeRegionMsg::ConvertNoRef(msg); + if (exclMsg) + { + if (exclMsg->GetCmd() == plExcludeRegionMsg::kClear) + { + DetectorLogSpecial("Clearing exclude region %s", GetKeyName()); + IMoveAvatars(); + fContainedAvatars.Reset(); + ISetPhysicalState(true); + } + else if (exclMsg->GetCmd() == plExcludeRegionMsg::kRelease) + { + DetectorLogSpecial("Releasing exclude region %s", GetKeyName()); + ISetPhysicalState(false); + } + + GetTarget()->DirtySynchState(kSDLXRegion, exclMsg->fSynchFlags); + + return true; + } + + // Avatar entering or exiting our volume. Only the owner of the SO logs this since + // it does all the moving at clear time. + plCollideMsg *collideMsg = plCollideMsg::ConvertNoRef(msg); + + if (collideMsg) + { + if (collideMsg->fEntering) + { + //DetectorLogSpecial("Avatar enter exclude region %s",GetKeyName()); + fContainedAvatars.Append(collideMsg->fOtherKey); + } + else + { + //DetectorLogSpecial("Avatar exit exclude region %s",GetKeyName()); + int idx = fContainedAvatars.Find(collideMsg->fOtherKey); + if (idx != fContainedAvatars.kMissingIndex) + fContainedAvatars.Remove(idx); + } + + return true; + } + + return plSingleModifier::MsgReceive(msg); +} + +void plExcludeRegionModifier::AddTarget(plSceneObject* so) +{ + if (so) + { + delete fSDLModifier; + fSDLModifier = TRACKED_NEW plExcludeRegionSDLModifier(this); + so->AddModifier(fSDLModifier); + } + plSingleModifier::SetTarget(so); +} + +void plExcludeRegionModifier::RemoveTarget( plSceneObject *so ) +{ + if (so && fSDLModifier) + { + so->RemoveModifier(fSDLModifier); + delete fSDLModifier; + fSDLModifier = nil; + } + plSingleModifier::RemoveTarget(so); +} + +void plExcludeRegionModifier::AddSafePoint(plKey& key) +{ + fSafePoints.push_back(key); +} + +int plExcludeRegionModifier::IFindClosestSafePoint(plKey avatar) +{ + float closestDist = 0.f; + int closestIdx = -1; + + plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->GetObjectPtr()); + if (!avObj) + return -1; + + hsVector3 avPos; + avObj->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(&avPos); + + for (int i = 0; i < fSafePoints.size(); i++) + { + plSceneObject* safeObj = plSceneObject::ConvertNoRef(fSafePoints[i]->GetObjectPtr()); + hsVector3 safePos; + safeObj->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(&safePos); + + float dist = (safePos - avPos).Magnitude(); + + if (dist < closestDist || closestIdx == -1) + { + closestDist = dist; + closestIdx = i; + } + } + + return closestIdx; +} + +// check to make sure that the avatar and the exclude region are in the same subworld +bool plExcludeRegionModifier::ICheckSubworlds(plKey avatar) +{ + plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->GetObjectPtr()); + if (avObj) + { + // get the avatar modifier + const plArmatureMod *avMod = (plArmatureMod*)avObj->GetModifierByType(plArmatureMod::Index()); + if (avMod) + { + // get the avatar controller + plPhysicalControllerCore* avController = avMod->GetController(); + if (avController) + { + // get physical of the detector region + plPhysical* phys = GetPhysical(GetTarget()); + if (phys) + { + // are they in the same subworld? + if ( phys->GetWorldKey() == avController->GetSubworld() ) + return true; + else + return false; + } + } + } + } + return false; +} + +// Move avatars out of volume +void plExcludeRegionModifier::IMoveAvatars() +{ + //some reason this is not always finding all avatars might have something to do with + //Physx trigger flutter. Adding in brute force method + /* + for (int i = 0; i < fContainedAvatars.Count(); i++) + { + if ( ICheckSubworlds(fContainedAvatars[i]) ) + { + int closestIdx = IFindClosestSafePoint(fContainedAvatars[i]); + + if (closestIdx != -1) + { + plAvSeekMsg* msg = TRACKED_NEW plAvSeekMsg; + msg->SetBCastFlag(plMessage::kPropagateToModifiers); + msg->AddReceiver(fContainedAvatars[i]); + msg->fSmartSeek = fSeek; + msg->fDuration = fSeekTime; + msg->fSeekPoint = fSafePoints[closestIdx]; + msg->fFlags |= plAvSeekMsg::kSeekFlagUnForce3rdPersonOnFinish; + msg->Send(); + } + } + } + */ + + plPXPhysical* phys =(plPXPhysical*) GetPhysical(GetTarget()); + if (phys) + { + plKey DetectorWorldKey = phys->GetWorldKey(); + int numControllers = plPXPhysicalControllerCore::GetNumberOfControllersInThisSubWorld(phys->GetWorldKey()); + if (numControllers > 0) + { + plPXPhysicalControllerCore** controllers = TRACKED_NEW plPXPhysicalControllerCore*[numControllers]; + + int actualCount = plPXPhysicalControllerCore::GetControllersInThisSubWorld(phys->GetWorldKey(), numControllers, controllers); + + for (int i=0;iGetWorldSpaceCapsule(cap); + if(phys->OverlapWithCapsule(cap)) + { + plSceneObject* so = plSceneObject::ConvertNoRef(controllers[i]->GetOwner()->ObjectIsLoaded()); + const plArmatureMod* constAvMod = (plArmatureMod*)so->GetModifierByType(plArmatureMod::Index()); + if(constAvMod) + { + plAvBrainGeneric *curGenBrain = (plAvBrainGeneric *)constAvMod->FindBrainByClass(plAvBrainGeneric::Index()); + // *** warning; if there's more than one generic brain active, this will only look at the first + if (curGenBrain && curGenBrain->GetType() == plAvBrainGeneric::kLadder) + { + plAvBrainGenericMsg* pMsg = TRACKED_NEW plAvBrainGenericMsg( + nil, + constAvMod->GetKey(), + plAvBrainGenericMsg::kGotoStage, + -1, + false, + 0.0, + false, + false, + 0.0 + ); + pMsg->Send(); + } + else + { + int closestIdx = IFindClosestSafePoint(controllers[i]->GetOwner()); + if (closestIdx != -1) + { + plAvSeekMsg* msg = TRACKED_NEW plAvSeekMsg; + msg->SetBCastFlag(plMessage::kPropagateToModifiers); + msg->AddReceiver(controllers[i]->GetOwner()); + msg->fSmartSeek = fSeek; + msg->fDuration = fSeekTime; + msg->fSeekPoint = fSafePoints[closestIdx]; + msg->fFlags |= plAvSeekMsg::kSeekFlagUnForce3rdPersonOnFinish; + msg->Send(); + } + } + } + } + } + + delete[] controllers; + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// + +plExcludeRegionSDLModifier::plExcludeRegionSDLModifier() : fXRegion(nil) +{ +} + +plExcludeRegionSDLModifier::plExcludeRegionSDLModifier(plExcludeRegionModifier* xregion) : + fXRegion(xregion) +{ +} + +static const char* kXRegionSDLCleared = "cleared"; + +void plExcludeRegionSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + plSimpleStateVariable* var = dstState->FindVar(kXRegionSDLCleared); + if (var) + { + plPhysical* phys = GetPhysical(GetTarget()); + if (phys) + { + //bool cleared = phys->GetGroup() == plSimDefs::kGroupStatic; + bool cleared = phys->GetGroup() == plSimDefs::kGroupExcludeRegion; + var->Set(cleared); + } + } +} + +void plExcludeRegionSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + plSimpleStateVariable* var = srcState->FindVar(kXRegionSDLCleared); + if (var) + { + bool cleared; + var->Get(&cleared); + + DetectorLogSpecial("SDL %s exclude region %s", cleared ? "clearing" : "releasing", fXRegion->GetKeyName()); + fXRegion->ISetPhysicalState(cleared); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.h new file mode 100644 index 00000000..e8411b69 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plExcludeRegionModifier.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plExcludeRegionModifier_inc +#define plExcludeRegionModifier_inc + +#include "../pnModifier/plSingleModifier.h" +#include "hsMatrix44.h" +#include "hsTemplates.h" +#include "../plModifier/plSDLModifier.h" + +// +// Moves all of the avatars out of the area it's SceneObject occupies and makes it +// physical on clear message. Makes the SO non-physical again on release message. +// +class plExcludeRegionSDLModifier; +class plExcludeRegionModifier : public plSingleModifier +{ +protected: + enum + { + kBlockCameras, + }; + std::vector fSafePoints; // Safe positions to move avatars to + hsTArray fContainedAvatars; // Avatars inside our volume + plExcludeRegionSDLModifier *fSDLModifier; + hsBool fSeek; // use smart seek or teleport? + hsScalar fSeekTime; // how long to seek for + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return true; } + + void ISetPhysicalState(bool cleared); + + int IFindClosestSafePoint(plKey avatar); + bool ICheckSubworlds(plKey avatar); + void IMoveAvatars(); + + friend class plExcludeRegionSDLModifier; + +public: + plExcludeRegionModifier(); + ~plExcludeRegionModifier(); + + CLASSNAME_REGISTER(plExcludeRegionModifier); + GETINTERFACE_ANY(plExcludeRegionModifier, plSingleModifier); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget( plSceneObject *so ); + + void AddSafePoint(plKey& key); + void UseSmartSeek() { fSeek = true; } + void SetSeekTime(hsScalar s) { fSeekTime = s; } + void SetBlockCameras(bool block) { fFlags.SetBit(kBlockCameras, block); } +}; + +class plExcludeRegionSDLModifier : public plSDLModifier +{ +protected: + plExcludeRegionModifier* fXRegion; + + void IPutCurrentStateIn(plStateDataRecord* dstState); + void ISetCurrentStateFrom(const plStateDataRecord* srcState); + +public: + plExcludeRegionSDLModifier(); + plExcludeRegionSDLModifier(plExcludeRegionModifier* xregion); + + CLASSNAME_REGISTER(plExcludeRegionSDLModifier); + GETINTERFACE_ANY(plExcludeRegionSDLModifier, plSDLModifier); + + const char* GetSDLName() const { return kSDLXRegion; } +}; + +#endif // plExcludeRegionModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plGameMarkerModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plGameMarkerModifier.cpp new file mode 100644 index 00000000..31c6f5c2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plGameMarkerModifier.cpp @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plGameMarkerModifier.h" +#include "../plMessage/plCollideMsg.h" + +#include "../pnMessage/plNotifyMsg.h" +#include "../pnSceneObject/plSceneObject.h" + +hsBool plGameMarkerModifier::MsgReceive(plMessage* msg) +{ + plCollideMsg *collideMsg = plCollideMsg::ConvertNoRef(msg); + if (collideMsg) + { + if (collideMsg->fEntering) + { + plNotifyMsg* notify = TRACKED_NEW plNotifyMsg; + notify->AddCollisionEvent(true, collideMsg->fOtherKey, GetTarget()->GetKey()); + notify->Send(hsgResMgr::ResMgr()->FindKey(kMarkerMgr_KEY)); + } + } + + return plSingleModifier::MsgReceive(msg); +} + +plKey plGameMarkerModifier::IFindCloneKey(plKey baseKey) +{ + const plUoid& myUoid = GetKey()->GetUoid(); + plUoid cloneUoid = baseKey->GetUoid(); + cloneUoid.SetClone(myUoid.GetClonePlayerID(), myUoid.GetCloneID()); + return hsgResMgr::ResMgr()->FindKey(cloneUoid); +} + +void plGameMarkerModifier::FixupAnimKeys() +{ + fGreenAnimKey = IFindCloneKey(fGreenAnimKey); + fRedAnimKey = IFindCloneKey(fRedAnimKey); + fOpenAnimKey = IFindCloneKey(fOpenAnimKey); + fBounceAnimKey = IFindCloneKey(fBounceAnimKey); +} + +void plGameMarkerModifier::Read(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Read(stream, mgr); + + fGreenAnimKey = mgr->ReadKey(stream); + fRedAnimKey = mgr->ReadKey(stream); + fOpenAnimKey = mgr->ReadKey(stream); + fBounceAnimKey = mgr->ReadKey(stream); + fPlaceSndIdx = stream->ReadSwap16(); + fHitSndIdx = stream->ReadSwap16(); +} + +void plGameMarkerModifier::Write(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Write(stream, mgr); + + mgr->WriteKey(stream, fGreenAnimKey); + mgr->WriteKey(stream, fRedAnimKey); + mgr->WriteKey(stream, fOpenAnimKey); + mgr->WriteKey(stream, fBounceAnimKey); + stream->WriteSwap16(fPlaceSndIdx); + stream->WriteSwap16(fHitSndIdx); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plGameMarkerModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plGameMarkerModifier.h new file mode 100644 index 00000000..f7d52336 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plGameMarkerModifier.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plGameMarkerModifier_h_inc +#define plGameMarkerModifier_h_inc + +#include "../pnModifier/plSingleModifier.h" + +class plGameMarkerModifier : public plSingleModifier +{ +protected: + plKey fGreenAnimKey; + plKey fRedAnimKey; + plKey fOpenAnimKey; + plKey fBounceAnimKey; + UInt16 fPlaceSndIdx; + UInt16 fHitSndIdx; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return true; } + + plKey IFindCloneKey(plKey baseKey); + + friend class pfMarkerInfo; + friend class pfMarkerInfoOwned; + +public: + plGameMarkerModifier() {} + + CLASSNAME_REGISTER(plGameMarkerModifier); + GETINTERFACE_ANY(plGameMarkerModifier, plSingleModifier); + + void ExportInit(plKey greenKey, plKey redKey, plKey openKey, plKey bounceAnimKey, + UInt16 placeSndIdx, UInt16 hitSndIdx) + { + fGreenAnimKey = greenKey; + fRedAnimKey = redKey; + fOpenAnimKey = openKey; + fBounceAnimKey = bounceAnimKey; + fPlaceSndIdx = placeSndIdx; + fHitSndIdx = hitSndIdx; + } + + void FixupAnimKeys(); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plGameMarkerModifier_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plImageLibMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plImageLibMod.cpp new file mode 100644 index 00000000..f7d266b0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plImageLibMod.cpp @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#include "hsTypes.h" +#include "plImageLibMod.h" + +#include "../plGImage/plBitmap.h" +#include "../pnMessage/plRefMsg.h" + +#include "hsTimer.h" +#include "hsStream.h" +#include "hsResMgr.h" + +plImageLibMod::plImageLibMod() +{ +} + +plImageLibMod::~plImageLibMod() +{ +} + +hsBool plImageLibMod::MsgReceive(plMessage* msg) +{ + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); + if( refMsg != nil ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + if( fImages.GetCount() <= refMsg->fWhich ) + fImages.ExpandAndZero( refMsg->fWhich + 1 ); + + fImages[ refMsg->fWhich ] = plBitmap::ConvertNoRef( refMsg->GetRef() ); + } + else if( refMsg->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) ) + { + fImages[ refMsg->fWhich ] = nil; + } + return true; + } + + return plSingleModifier::MsgReceive(msg); +} + +void plImageLibMod::Read(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Read(stream, mgr); + + UInt32 i, count = stream->ReadSwap32(); + fImages.SetCountAndZero( count ); + for( i = 0; i < count; i++ ) + mgr->ReadKeyNotifyMe( stream, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, i, kRefImage ), plRefFlags::kActiveRef ); +} + +void plImageLibMod::Write(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Write(stream, mgr); + + stream->WriteSwap32( fImages.GetCount() ); + UInt32 i; + for( i = 0; i < fImages.GetCount(); i++ ) + mgr->WriteKey( stream, fImages[ i ]->GetKey() ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plImageLibMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plImageLibMod.h new file mode 100644 index 00000000..49971054 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plImageLibMod.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plImageLibMod_inc +#define plImageLibMod_inc + +#include "hsTemplates.h" +#include "../pnModifier/plSingleModifier.h" +#include "../pnKeyedObject/plKey.h" + +class plBitmap; +class plImageLibMod : public plSingleModifier +{ +protected: + + hsTArray fImages; + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return false; } + +public: + plImageLibMod(); + virtual ~plImageLibMod(); + + CLASSNAME_REGISTER( plImageLibMod ); + GETINTERFACE_ANY( plImageLibMod, plSingleModifier ); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + enum Refs + { + kRefImage = 0 + }; + + UInt32 GetNumImages( void ) const { return fImages.GetCount(); } +}; + +#endif // plImageLibMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plInterfaceInfoModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plInterfaceInfoModifier.cpp new file mode 100644 index 00000000..e86a53a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plInterfaceInfoModifier.cpp @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plInterfaceInfoModifier.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plKey.h" + + +plInterfaceInfoModifier::plInterfaceInfoModifier() +{ +} +plInterfaceInfoModifier::~plInterfaceInfoModifier() +{ + fKeyList.Reset(); +} + + +void plInterfaceInfoModifier::Read(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Read(s, mgr); + int i = s->ReadSwap32(); + for (int x = 0; x < i; x++) + fKeyList.Append(mgr->ReadKey(s)); +} + +void plInterfaceInfoModifier::Write(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Write(s, mgr); + s->WriteSwap32(fKeyList.Count()); + for (int i = 0; i < fKeyList.Count(); i++) + mgr->WriteKey(s, fKeyList[i]); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plInterfaceInfoModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plInterfaceInfoModifier.h new file mode 100644 index 00000000..6c579899 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plInterfaceInfoModifier.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plInterfaceInfoMod_inc +#define plInterfaceInfoMod_inc + +#include "../pnModifier/plSingleModifier.h" +#include "../pnKeyedObject/plKey.h" +#include "hsTemplates.h" + +class plInterfaceInfoModifier : public plSingleModifier +{ +protected: + + hsTArray fKeyList; + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty){ return true; } +public: + + plInterfaceInfoModifier(); + ~plInterfaceInfoModifier(); + + CLASSNAME_REGISTER( plInterfaceInfoModifier ); + GETINTERFACE_ANY( plInterfaceInfoModifier, plSingleModifier ); + + void AddRefdKey(plKey &k) { fKeyList.Append(k); } + + int GetNumReferencedKeys() const { return fKeyList.Count(); } + plKey GetReferencedKey(int i) const { return fKeyList[i]; } + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plInterfaceInfoMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLayerSDLModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLayerSDLModifier.cpp new file mode 100644 index 00000000..10233690 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLayerSDLModifier.cpp @@ -0,0 +1,189 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsMatrix44.h" +#include "plLayerSDLModifier.h" +#include "../plSDL/plSDL.h" +#include "../plSurface/plLayerAnimation.h" + +// static vars +char plLayerSDLModifier::kStrAtc[]="atc"; +char plLayerSDLModifier::kStrPassThruChannels[]="passThruChannels"; +char plLayerSDLModifier::kStrTransform[]="transform"; +char plLayerSDLModifier::kStrChannelData[]="channelData"; +//char plLayerSDLModifier::kStrPreShadeColor[]="preshadeColor"; +//char plLayerSDLModifier::kStrRuntimeColor[]="runtimeColor"; +//char plLayerSDLModifier::kStrAmbientColor[]="ambientColor"; +//char plLayerSDLModifier::kStrOpacity[]="opacity"; + +plKey plLayerSDLModifier::GetStateOwnerKey() const +{ + return fLayerAnimation ? fLayerAnimation->GetKey() : nil; +} + +// +// Copy atc from current state into sdl +// +void plLayerSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + plLayerAnimation* layer=GetLayerAnimation(); + hsAssert(layer, "nil layer animation"); + + plSDStateVariable* atcVar = dstState->FindSDVar(kStrAtc); + plStateDataRecord* atcStateDataRec = atcVar->GetStateDataRecord(0); + IPutATC(atcStateDataRec, &layer->GetTimeConvert()); + + int passThru=layer->fPassThruChannels; + dstState->FindVar(kStrPassThruChannels)->Set(passThru); + + int transformSize = 0; + if (layer->fTransform && (layer->fOwnedChannels & plLayerInterface::kTransform)) + transformSize = 16; + + plSimpleStateVariable *transformVar = dstState->FindVar(kStrTransform); + if (transformVar->GetCount() != transformSize) + transformVar->Alloc(transformSize); + + if (transformSize > 0) + { + int i,j; + for(i=0;i<4;i++) + { + for(j=0;j<4;j++) + { + float f=layer->fTransform->fMap[i][j]; + transformVar->Set(&f, i*4+j); + } + } + } + + int channelDataSize = 0; + if (layer->fPreshadeColor && (layer->fOwnedChannels & plLayerInterface::kPreshadeColor)) + channelDataSize += 3; + if (layer->fRuntimeColor && (layer->fOwnedChannels & plLayerInterface::kRuntimeColor)) + channelDataSize += 3; + if (layer->fAmbientColor && (layer->fOwnedChannels & plLayerInterface::kAmbientColor)) + channelDataSize += 3; + if (layer->fOpacity && (layer->fOwnedChannels & plLayerInterface::kOpacity)) + channelDataSize += 1; + + plSimpleStateVariable *channelVar = dstState->FindVar(kStrChannelData); + if (channelVar->GetCount() != channelDataSize) + channelVar->Alloc(channelDataSize); + + int channelIdx = 0; + if (layer->fPreshadeColor && (layer->fOwnedChannels & plLayerInterface::kPreshadeColor)) + { + channelVar->Set((UInt8)(layer->fPreshadeColor->r * 255), channelIdx++); + channelVar->Set((UInt8)(layer->fPreshadeColor->g * 255), channelIdx++); + channelVar->Set((UInt8)(layer->fPreshadeColor->b * 255), channelIdx++); + } + if (layer->fRuntimeColor && (layer->fOwnedChannels & plLayerInterface::kRuntimeColor)) + { + channelVar->Set((UInt8)(layer->fRuntimeColor->r * 255), channelIdx++); + channelVar->Set((UInt8)(layer->fRuntimeColor->g * 255), channelIdx++); + channelVar->Set((UInt8)(layer->fRuntimeColor->b * 255), channelIdx++); + } + if (layer->fAmbientColor && (layer->fOwnedChannels & plLayerInterface::kAmbientColor)) + { + channelVar->Set((UInt8)(layer->fAmbientColor->r * 255), channelIdx++); + channelVar->Set((UInt8)(layer->fAmbientColor->g * 255), channelIdx++); + channelVar->Set((UInt8)(layer->fAmbientColor->b * 255), channelIdx++); + } + if (layer->fOpacity && (layer->fOwnedChannels & plLayerInterface::kOpacity)) + channelVar->Set((UInt8)(*layer->fOpacity * 255), channelIdx++); +} + +// +// Change the object's animation state to reflect what is specified in the +// stateDataRecord. +// +void plLayerSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + plLayerAnimation* layer=GetLayerAnimation(); + hsAssert(layer, "nil layer animation"); + + plSDStateVariable* atcVar = srcState->FindSDVar(kStrAtc); + plStateDataRecord* atcStateDataRec = atcVar->GetStateDataRecord(0); + ISetCurrentATC(atcStateDataRec, &layer->GetTimeConvert()); + + int pass; + if (srcState->FindVar(kStrPassThruChannels)->Get(&pass)) + layer->fPassThruChannels=pass; + + plSimpleStateVariable *transformVar = srcState->FindVar(kStrTransform); + if (transformVar->IsUsed() && transformVar->GetCount() == 16) + { + int i,j; + for(i=0;i<4;i++) + { + for(j=0;j<4;j++) + { + float f; + srcState->FindVar(kStrTransform)->Get(&f, i*4+j); + layer->fTransform->fMap[i][j]=f; + } + } + } + + plSimpleStateVariable *channelVar = srcState->FindVar(kStrChannelData); + int channelIdx = 0; + UInt8 val; + if (layer->fPreshadeColor && (layer->fOwnedChannels & plLayerInterface::kPreshadeColor)) + { + channelVar->Get(&val, channelIdx++); + layer->fPreshadeColor->r = val / 255.f; + channelVar->Get(&val, channelIdx++); + layer->fPreshadeColor->g = val / 255.f; + channelVar->Get(&val, channelIdx++); + layer->fPreshadeColor->b = val / 255.f; + } + if (layer->fRuntimeColor && (layer->fOwnedChannels & plLayerInterface::kRuntimeColor)) + { + channelVar->Get(&val, channelIdx++); + layer->fRuntimeColor->r = val / 255.f; + channelVar->Get(&val, channelIdx++); + layer->fRuntimeColor->g = val / 255.f; + channelVar->Get(&val, channelIdx++); + layer->fRuntimeColor->b = val / 255.f; + } + if (layer->fAmbientColor && (layer->fOwnedChannels & plLayerInterface::kAmbientColor)) + { + channelVar->Get(&val, channelIdx++); + layer->fAmbientColor->r = val / 255.f; + channelVar->Get(&val, channelIdx++); + layer->fAmbientColor->g = val / 255.f; + channelVar->Get(&val, channelIdx++); + layer->fAmbientColor->b = val / 255.f; + } + if (layer->fOpacity && (layer->fOwnedChannels & plLayerInterface::kOpacity)) + { + channelVar->Get(&val, channelIdx++); + *layer->fOpacity = val / 255.f; + } + + layer->fCurrentTime = -1.f; // force an eval +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLayerSDLModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLayerSDLModifier.h new file mode 100644 index 00000000..a3335e22 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLayerSDLModifier.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plLayerSDLModifier_inc +#define plLayerSDLModifier_inc + +#include "../plModifier/plAnimTimeConvertSDLModifier.h" + +// +// This modifier is responsible for sending and recving +// an layer's animation state (basically an animTimeConvert) +// +class plLayerAnimation; +class plStateDataRecord; + +class plLayerSDLModifier : public plAnimTimeConvertSDLModifier +{ +protected: + + static char kStrAtc[]; // animTimeConvert var name + static char kStrPassThruChannels[]; + static char kStrTransform[]; + static char kStrChannelData[]; + //static char kStrPreShadeColor[]; + //static char kStrRuntimeColor[]; + //static char kStrAmbientColor[]; + //static char kStrOpacity[]; + plLayerAnimation* fLayerAnimation; + + void IPutCurrentStateIn(plStateDataRecord* dstState); + void ISetCurrentStateFrom(const plStateDataRecord* srcState); +public: + CLASSNAME_REGISTER( plLayerSDLModifier); + GETINTERFACE_ANY( plLayerSDLModifier, plAnimTimeConvertSDLModifier); + + plLayerSDLModifier() : fLayerAnimation(nil) {} + + const char* GetSDLName() const { return kSDLLayer; } + + plLayerAnimation* GetLayerAnimation() const { return fLayerAnimation; } + void SetLayerAnimation(plLayerAnimation* l) { fLayerAnimation=l; AddTarget(nil); } + plKey GetStateOwnerKey() const; +}; + +#endif // plLayerSDLModifier_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp new file mode 100644 index 00000000..8516bfdb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.cpp @@ -0,0 +1,265 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plLogicModifier.h" +#include "plgDispatch.h" +#include "../pnTimer/plTimerCallbackManager.h" +#include "../pnModifier/plConditionalObject.h" +#include "../plPhysical/plDetectorModifier.h" +#include "../plMessage/plCondRefMsg.h" +#include "../plMessage/plTimerCallbackMsg.h" +#include "../plMessage/plActivatorMsg.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnMessage/plFakeOutMsg.h" +#include "../pnMessage/plCursorChangeMsg.h" +#include "../pnMessage/plNotifyMsg.h" + +#include "../plModifier/plDetectorLog.h" +#include "../plInputCore/plSceneInputInterface.h" +#include "../../FeatureLib/pfConditional/plFacingConditionalObject.h" +#include "../../FeatureLib/pfConditional/plObjectInBoxConditionalObject.h" + + +plLogicModifier::plLogicModifier() +{ + fMyCursor = plCursorChangeMsg::kCursorUp; +} + +plLogicModifier::~plLogicModifier() +{ +} + +// this is a special check that asks conditions +// not if they are satisfied, but if they are ready +// to be satisfied. Used by the activator condition +// to check that any special conditions (like players +// in boxes) are okay or not. +hsBool plLogicModifier::VerifyConditions(plMessage* msg) +{ + for (int i = 0; i < fConditionList.Count(); i++) + { + if (!fConditionList[i]->Verify(msg)) + return false; + } + return true; +} + +hsBool plLogicModifier::MsgReceive(plMessage* msg) +{ + hsBool retVal = false; + + // read messages: + plCondRefMsg* pCondMsg = plCondRefMsg::ConvertNoRef(msg); + if (pCondMsg) + { + plConditionalObject* pCond = plConditionalObject::ConvertNoRef( pCondMsg->GetRef() ); + if (pCond && (pCondMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace))) + { + if (fConditionList.Count() <= pCondMsg->fWhich) + fConditionList.ExpandAndZero(pCondMsg->fWhich + 1); + + fConditionList[pCondMsg->fWhich] = pCond; + pCond->SetLogicMod(this); + + if (pCond->HasFlag(plConditionalObject::kLocalElement)) + SetFlag(kLocalElement); + } + retVal = true; + } + plTimerCallbackMsg* pTMsg = plTimerCallbackMsg::ConvertNoRef(msg); + if (pTMsg) + { + hsBool netRequest = msg->HasBCastFlag(plMessage::kNetNonLocal); + Trigger(netRequest); + } + + plActivatorMsg* pActivateMsg = plActivatorMsg::ConvertNoRef(msg); + + if (pActivateMsg) + { +// if (pActivateMsg->fTriggerType == plActivatorMsg::kUnPickedTrigger) + { +// UnTrigger(); +// return true; + } +// else + { + hsBool ignore=false; + + // Ignore collision activations by remote players + if (pActivateMsg->fTriggerType==plActivatorMsg::kCollideEnter || + pActivateMsg->fTriggerType==plActivatorMsg::kCollideExit || + pActivateMsg->fTriggerType==plActivatorMsg::kCollideContact) + { + if (plNetClientApp::GetInstance()->IsRemotePlayerKey(pActivateMsg->fHitterObj)) + ignore=true; + } + + if (!ignore) + { + for (int i = 0; i < fConditionList.Count(); i++) + { + if (fConditionList[i]->MsgReceive(msg)) + return true; + } + } + } + } + + plNotifyMsg* pNotify = plNotifyMsg::ConvertNoRef(msg); + if (pNotify) + { + for (int i = 0; i < fConditionList.Count(); i++) + { + if (fConditionList[i]->MsgReceive(msg)) + return true; + } + } + + plFakeOutMsg* pFakeMsg = plFakeOutMsg::ConvertNoRef(msg); + if (pFakeMsg) + { + plCursorChangeMsg* pMsg = 0; + if ((VerifyConditions(pFakeMsg) && fMyCursor) && !Disabled()) + pMsg = TRACKED_NEW plCursorChangeMsg(fMyCursor, 1); + else + { +#ifndef PLASMA_EXTERNAL_RELEASE + // try to determine the reasons for not displaying cursor + if (plSceneInputInterface::fShowLOS) + { + if ( Disabled() ) + { + DetectorLogRed("%s: LogicMod is disabled", GetKeyName()); + } + else + { + for (int i = 0; i < fConditionList.Count(); i++) + { + if (!fConditionList[i]->Verify(msg)) + { + if ( plObjectInBoxConditionalObject::ConvertNoRef(fConditionList[i]) ) + DetectorLogRed("%s: LogicMod InRegion conditional not met", fConditionList[i]->GetKeyName()); + else if ( plFacingConditionalObject::ConvertNoRef(fConditionList[i]) ) + DetectorLogRed("%s: LogicMod Facing conditional not met", fConditionList[i]->GetKeyName()); + else + DetectorLogRed("%s: LogicMod conditional not met", fConditionList[i]->GetKeyName()); + } + } + } + } +#endif // PLASMA_EXTERNAL_RELEASE + pMsg = TRACKED_NEW plCursorChangeMsg(plCursorChangeMsg::kNullCursor, 1); + } + + pMsg->AddReceiver( pFakeMsg->GetSender() ); + pMsg->SetSender(GetKey()); + plgDispatch::MsgSend(pMsg); + + return true; + } + + return (plLogicModBase::MsgReceive(msg)); +} + +void plLogicModifier::RequestTrigger(hsBool netRequest) +{ + for (int i = 0; i < fConditionList.Count(); i++) + { + if (!fConditionList[i]->Satisfied()) + return; + } + + plLogicModBase::RequestTrigger(netRequest); +} + +void plLogicModifier::PreTrigger(hsBool netRequest) +{ + if (!IEvalCounter()) + return; + + if (fTimer) + { + plgTimerCallbackMgr::NewTimer( fTimer, TRACKED_NEW plTimerCallbackMsg( GetKey() ) ); + return; + } + plLogicModBase::PreTrigger(netRequest); +} + + +void plLogicModifier::Reset(bool bCounterReset) +{ + plLogicModBase::Reset(bCounterReset); + for (int i = 0; i < fConditionList.Count(); i++) + fConditionList[i]->Reset(); +} + +void plLogicModifier::Read(hsStream* stream, hsResMgr* mgr) +{ + plLogicModBase::Read(stream, mgr); + plCondRefMsg* refMsg; + int n = stream->ReadSwap32(); + fConditionList.SetCountAndZero(n); + int i; + for(i = 0; i < n; i++ ) + { + refMsg = TRACKED_NEW plCondRefMsg(GetKey(), i); + mgr->ReadKeyNotifyMe(stream,refMsg, plRefFlags::kActiveRef); + } + fMyCursor = stream->ReadSwap32(); +} + +void plLogicModifier::Write(hsStream* stream, hsResMgr* mgr) +{ + plLogicModBase::Write(stream, mgr); + stream->WriteSwap32(fConditionList.GetCount()); + for( int i = 0; i < fConditionList.GetCount(); i++ ) + mgr->WriteKey(stream, fConditionList[i]); + stream->WriteSwap32(fMyCursor); +} + +void plLogicModifier::AddCondition(plConditionalObject* c) +{ + plGenRefMsg *msg= TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(c->GetKey(), msg, plRefFlags::kActiveRef); + + fConditionList.Append(c); + c->SetLogicMod(this); + if (c->HasFlag(plConditionalObject::kLocalElement)) + SetFlag(kLocalElement); +} + +void plLogicModifier::VolumeIgnoreExtraEnters(bool ignore /* = true */) +{ + for (int curCondition = 0; curCondition < fConditionList.GetCount(); ++curCondition) + { + plVolumeSensorConditionalObject* condition = plVolumeSensorConditionalObject::ConvertNoRef(fConditionList[curCondition]); + if (condition) + condition->IgnoreExtraEnters(ignore); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h new file mode 100644 index 00000000..3bac1b18 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plLogicModifier.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLogicModifier_inc +#define plLogicModifier_inc + +#include "../pnModifier/plLogicModBase.h" + +class plLogicModifier : public plLogicModBase +{ +protected: + + hsTArray fConditionList; + + virtual void PreTrigger(hsBool netRequest); +public: + plLogicModifier(); + ~plLogicModifier(); + + CLASSNAME_REGISTER( plLogicModifier ); + GETINTERFACE_ANY( plLogicModifier, plLogicModBase ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void RequestTrigger(hsBool netRequest=false); + virtual hsBool VerifyConditions(plMessage* msg); + void AddCondition(plConditionalObject* c); + virtual void Reset(bool bCounterReset); + + void VolumeIgnoreExtraEnters(bool ignore = true); // hack for garrison + + int fMyCursor; +}; + +#endif // plLogicModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plMaintainersMarkerModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plMaintainersMarkerModifier.cpp new file mode 100644 index 00000000..072b08d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plMaintainersMarkerModifier.cpp @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plMaintainersMarkerModifier.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plAvatar/plAvatarMgr.h" + +void plMaintainersMarkerModifier::AddTarget(plSceneObject* so) +{ + plMultiModifier::AddTarget(so); + plAvatarMgr::GetInstance()->AddMaintainersMarker(this); +} + +void plMaintainersMarkerModifier::RemoveTarget(plSceneObject* so) +{ + plMultiModifier::RemoveTarget(so); + hsAssert(fTargets.GetCount() == 0, "Spawn modifier has multiple targets. Matt."); + + plAvatarMgr::GetInstance()->RemoveMaintainersMarker(this); +} + +void plMaintainersMarkerModifier::Read(hsStream *stream, hsResMgr *mgr) +{ + plMultiModifier::Read(stream, mgr); + stream->ReadSwap(&fCalibrated); +} + +void plMaintainersMarkerModifier::Write(hsStream *stream, hsResMgr *mgr) +{ + plMultiModifier::Write(stream, mgr); + stream->WriteSwap(fCalibrated); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plMaintainersMarkerModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plMaintainersMarkerModifier.h new file mode 100644 index 00000000..a9bc6250 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plMaintainersMarkerModifier.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMaintainersMarkerModifier_inc +#define plMaintainersMarkerModifier_inc + +#include "../pnModifier/plMultiModifier.h" +#include "../pnMessage/plMessage.h" + + +class plMaintainersMarkerModifier : public plMultiModifier +{ +public: + enum + { + kBroken = 0, + kRepaired, + kCalibrated, + }; +protected: + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) {return true;} + + int fCalibrated; +public: + plMaintainersMarkerModifier() : fCalibrated(0){;} + + CLASSNAME_REGISTER( plMaintainersMarkerModifier ); + GETINTERFACE_ANY( plMaintainersMarkerModifier, plMultiModifier ); + + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + void SetCalibrated(hsBool b) {fCalibrated = b;} + int GetCalibrated() { return fCalibrated; } + + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + + +#endif // plMaintainersMarkerModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plModifierCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plModifierCreatable.h new file mode 100644 index 00000000..b3c43771 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plModifierCreatable.h @@ -0,0 +1,92 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plModifierCreatable_inc +#define plModifierCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plSpawnModifier.h" + +REGISTER_CREATABLE( plSpawnModifier ); + +#include "plLogicModifier.h" + +REGISTER_CREATABLE( plLogicModifier ); + +#include "plResponderModifier.h" + +REGISTER_CREATABLE( plResponderModifier ); +REGISTER_CREATABLE( plResponderEnableMsg ); + +#include "plAxisAnimModifier.h" + +REGISTER_CREATABLE( plAxisAnimModifier ); + +#include "plExcludeRegionModifier.h" +REGISTER_CREATABLE(plExcludeRegionModifier); +REGISTER_CREATABLE(plExcludeRegionSDLModifier); + +#include "plSimpleModifier.h" +REGISTER_NONCREATABLE(plSimpleModifier); + +#include "plCloneSpawnModifier.h" +REGISTER_CREATABLE(plCloneSpawnModifier); + +#include "plAnimEventModifier.h" +REGISTER_CREATABLE(plAnimEventModifier); + +#include "plInterfaceInfoModifier.h" +REGISTER_CREATABLE(plInterfaceInfoModifier); + +#include "plSDLModifier.h" +REGISTER_NONCREATABLE(plSDLModifier); + +#include "plLayerSDLModifier.h" +REGISTER_CREATABLE(plLayerSDLModifier); + +#include "plAnimTimeConvertSDLModifier.h" +REGISTER_NONCREATABLE(plAnimTimeConvertSDLModifier); + +#include "plResponderSDLModifier.h" +REGISTER_CREATABLE(plResponderSDLModifier); + +#include "plSoundSDLModifier.h" +REGISTER_CREATABLE(plSoundSDLModifier); + +#include "plDecalEnableMod.h" +REGISTER_CREATABLE(plDecalEnableMod); + +#include "plMaintainersMarkerModifier.h" +REGISTER_CREATABLE(plMaintainersMarkerModifier); + +#include "plImageLibMod.h" +REGISTER_CREATABLE(plImageLibMod); + +#include "plGameMarkerModifier.h" +REGISTER_CREATABLE(plGameMarkerModifier); + +#endif // plModifierCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderModifier.cpp new file mode 100644 index 00000000..260844e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderModifier.cpp @@ -0,0 +1,791 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsTimer.h" +#include "plResponderModifier.h" +#include "plResponderSDLModifier.h" +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "plPhysical.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnNetCommon/plNetApp.h" + +// for localOnly cmd check +#include "../plMessage/plLinkToAgeMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pnMessage/plSoundMsg.h" + +#include "../plMessage/plResponderMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../pnMessage/plSDLModifierMsg.h" +#include "../../FeatureLib/pfMessage/plArmatureEffectMsg.h" + +#include "../plStatusLog/plStatusLog.h" + +#include "../plMessage/plTimerCallbackMsg.h" +#include "../pnTimer/plTimerCallbackManager.h" + +#include "../plMessage/plSimStateMsg.h" +//#include "../plHavok1\plHKPhysical.h" +//#include "../plHavok1\plHKSubWorld.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvatarMgr.h" + + +//#ifdef HS_DEBUGGING +#define STATUS_LOG +//#endif + +#ifdef STATUS_LOG +#define ResponderLog(x) x +#else +#define ResponderLog(x) +#endif + +void plResponderEnableMsg::Read(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgRead(stream, mgr); + fEnable = stream->Readbool(); +} + +void plResponderEnableMsg::Write(hsStream* stream, hsResMgr* mgr) +{ + plMessage::IMsgWrite(stream, mgr); + stream->Writebool(fEnable); +} + +///////////////////////////////////////////////////// +///////////////////////////////////////////////////// + +plResponderModifier::plResponderModifier() : + fCurState(0), + fCurCommand(-1), + fEnabled(true), + fFlags(0), + fEnter(false), + fResponderSDLMod(nil), + fGotFirstLoad(false), + fNotifyMsgFlags(0) +{ +} + +plResponderModifier::~plResponderModifier() +{ + delete fResponderSDLMod; + fResponderSDLMod=nil; + + for (int i = 0; i < fStates.Count(); i++) + { + for (int j = 0; j < fStates[i].fCmds.Count(); j++ ) + hsRefCnt_SafeUnRef(fStates[i].fCmds[j].fMsg); + } +} + +hsBool plResponderModifier::MsgReceive(plMessage* msg) +{ + plNotifyMsg* pNMsg = plNotifyMsg::ConvertNoRef(msg); + if (pNMsg) + { + if (pNMsg->fType == plNotifyMsg::kResponderFF) + { + ISetResponderStateFromNotify(pNMsg); + IFastForward(true); + } + else if (pNMsg->fType == plNotifyMsg::kResponderChangeState) + { + ISetResponderStateFromNotify(pNMsg); + DirtySynchState(kSDLResponder, 0); + } + else + { + // assumes state of 0 means untriggered and state of 1 is triggered + if ((pNMsg->fState != 0 && (fFlags & kDetectTrigger)) || + (pNMsg->fState == 0 && (fFlags & kDetectUnTrigger))) + { + Trigger(pNMsg); + DirtySynchState(kSDLResponder, 0); + } + } + + return true; + } + + plResponderEnableMsg *pEnableMsg = plResponderEnableMsg::ConvertNoRef(msg); + if (pEnableMsg) + { + fEnabled = pEnableMsg->fEnable; + DirtySynchState(kSDLResponder, 0); + return true; + } + + plEventCallbackMsg *pEventMsg = plEventCallbackMsg::ConvertNoRef(msg); + plTimerCallbackMsg *timerMsg = plTimerCallbackMsg::ConvertNoRef(msg); + if (pEventMsg || timerMsg) + { + UInt32 waitID = pEventMsg ? pEventMsg->fUser : timerMsg->fID; + + if (waitID != -1) + { + // Flag that this callback completed and try sending in case any commands were waiting on this + fCompletedEvents.SetBit(waitID); + + ResponderLog(ILog(plStatusLog::kWhite, "Got callback from command %d(id:%d)", ICmdFromWait((Int8)waitID)+1, waitID)); + + IContinueSending(); + DirtySynchState(kSDLResponder, 0); + } + // The is one of the stop callbacks we generated for debug mode + else if (fDebugAnimBox) + IDebugAnimBox(false); + + return true; + } + + // pass sdl msg to sdlMod + plSDLModifierMsg* sdlMsg = plSDLModifierMsg::ConvertNoRef(msg); + if (sdlMsg && fResponderSDLMod) + { + if (fResponderSDLMod->MsgReceive(sdlMsg)) + return true; // msg handled + } + + return plSingleModifier::MsgReceive(msg); +} + +void plResponderModifier::AddCommand(plMessage* pMsg, int state) +{ + fStates[state].fCmds.Append(plResponderCmd(pMsg, -1)); +} + +void plResponderModifier::AddCallback(Int8 state, Int8 cmd, Int8 callback) +{ + fStates[state].fWaitToCmd[callback] = cmd; +} + +// +// Decide if this cmd should only be run locally. +// If we are triggered remotely (netRequest==true), then +// we don't want to execute localOnly cmds (like cameraChanges) +// +bool plResponderModifier::IIsLocalOnlyCmd(plMessage* cmd) +{ + if (plLinkToAgeMsg::ConvertNoRef(cmd)) // don't want to page out any rooms + return true; + if (plCameraMsg::ConvertNoRef(cmd)) // don't want to change our camera + return true; + + plSoundMsg *snd = plSoundMsg::ConvertNoRef( cmd ); + if( snd != nil && snd->Cmd( plSoundMsg::kIsLocalOnly ) ) + return true; + + return false; +} + +void plResponderModifier::ISetResponderState(Int8 state) +{ + // make sure that it is a valid state to switch to + if (state >= 0 && state < fStates.Count()) + { + fCurState = state; + } + else + { + ResponderLog(ILog(plStatusLog::kRed, "Invalid state %d specified, will default to current state", state)); + } +} + +void plResponderModifier::ISetResponderStateFromNotify(plNotifyMsg* msg) +{ + // set the state of the responder IF they want it to be + proResponderStateEventData* event = (proResponderStateEventData*)msg->FindEventRecord(proEventData::kResponderState); + if (event != nil) + ISetResponderState((Int8)(event->fState)); +} + +void plResponderModifier::Trigger(plNotifyMsg *msg) +{ +#if 0 + char str[256]; + sprintf(str, "RM: Responder %s is triggering, num cmds=%d, enabled=%d, curCmd=%d, t=%f\n", + GetKeyName(), fStates[fCurState].fCmds.GetCount(), + ((int)fEnabled), ((int)fCurCommand), hsTimer::GetSysSeconds()); + plNetClientApp::GetInstance()->DebugMsg(str); +#endif + + // If we're not in the middle of sending, reset and start sending commands + if (fCurCommand == Int8(-1) && fEnabled) + { + ResponderLog(ILog(plStatusLog::kGreen, "Trigger")); + + fNotifyMsgFlags = msg->GetAllBCastFlags(); + fTriggerer = msg->GetSender(); + fPlayerKey = msg->GetAvatarKey(); + + ISetResponderStateFromNotify(msg); + + proCollisionEventData *cEvent = (proCollisionEventData *)msg->FindEventRecord(proEventData::kCollision); + fEnter = (cEvent ? cEvent->fEnter : false); + + fCompletedEvents.Reset(); + fCurCommand = 0; + + DirtySynchState(kSDLResponder, 0); + + IContinueSending(); + } + else + { + ResponderLog(ILog(plStatusLog::kRed, "Rejected Trigger, %s", !fEnabled ? "responder disabled" : "responder is running")); + } +} + +bool plResponderModifier::IContinueSending() +{ + // If we haven't been started, exit + if (fCurCommand == Int8(-1)) + return false; + + plResponderState& state = fStates[fCurState]; + + while (fCurCommand < state.fCmds.Count()) + { + plMessage *msg = state.fCmds[fCurCommand].fMsg; + if (msg) + { + // If this command needs to wait, and it's condition hasn't been met yet, exit + Int8 wait = state.fCmds[fCurCommand].fWaitOn; + if (wait != -1 && !fCompletedEvents.IsBitSet(wait)) + { + ResponderLog(ILog(plStatusLog::kWhite, "Command %d is waiting for command %d(id:%d)", Int8(fCurCommand)+1, ICmdFromWait(wait)+1, wait)); + return false; + } + + if (!(fNotifyMsgFlags & plMessage::kNetNonLocal)|| !IIsLocalOnlyCmd(msg)) + { + // make sure outgoing msgs inherit net flags as part of cascade + UInt32 msgFlags = msg->GetAllBCastFlags(); + plNetClientApp::InheritNetMsgFlags(fNotifyMsgFlags, &msgFlags, true); + msg->SetAllBCastFlags(msgFlags); + + // If this is a responder message, let it know which player triggered this + if (plResponderMsg* responderMsg = plResponderMsg::ConvertNoRef(msg)) + { + responderMsg->fPlayerKey = fPlayerKey; + } + else if (plNotifyMsg* notifyMsg = plNotifyMsg::ConvertNoRef(msg)) + { + bool foundCollision = false; + + // If we find a collision event, this message is meant to trigger a multistage + for (int i = 0; i < notifyMsg->GetEventCount(); i++) + { + proEventData* event = notifyMsg->GetEventRecord(i); + if (event->fEventType == proEventData::kCollision) + { + proCollisionEventData* collisionEvent = (proCollisionEventData*)event; + collisionEvent->fHitter = fPlayerKey; + foundCollision = true; + } + } + + // No collision event, this message is for notifying the triggerer + if (!foundCollision) + { + notifyMsg->ClearReceivers(); + notifyMsg->AddReceiver(fTriggerer); + } + + notifyMsg->SetSender(GetKey()); + } + else if (plLinkToAgeMsg* linkMsg = plLinkToAgeMsg::ConvertNoRef(msg)) + { + if (linkMsg->GetNumReceivers() == 0) + { + plUoid netUoid(kNetClientMgr_KEY); + plKey netKey = hsgResMgr::ResMgr()->FindKey(netUoid); + hsAssert(netKey,"NetClientMgr not found"); + linkMsg->AddReceiver(netKey); + } + } + else if (plArmatureEffectStateMsg* stateMsg = plArmatureEffectStateMsg::ConvertNoRef(msg)) + { + stateMsg->ClearReceivers(); + stateMsg->AddReceiver(fPlayerKey); + stateMsg->fAddSurface = fEnter; + } + else if (plSubWorldMsg* swMsg = plSubWorldMsg::ConvertNoRef(msg)) + { + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if(avatar) + { + swMsg->AddReceiver(avatar->GetKey()); + } + } + + // If we're in anim debug mode, check if this is an anim play + // message so we can put up the cue + if (fDebugAnimBox) + { + plAnimCmdMsg* animMsg = plAnimCmdMsg::ConvertNoRef(msg); + if (animMsg && animMsg->Cmd(plAnimCmdMsg::kContinue)) + IDebugPlayMsg(animMsg); + } + + + if (plTimerCallbackMsg *timerMsg = plTimerCallbackMsg::ConvertNoRef(msg)) + { + hsRefCnt_SafeRef(timerMsg); + plgTimerCallbackMgr::NewTimer(timerMsg->fTime, timerMsg); + } + else + { + hsRefCnt_SafeRef(msg); + plgDispatch::MsgSend(msg); + } + } + } + + fCurCommand++; + DirtySynchState(kSDLResponder, 0); + } + + // Make sure all callbacks we need to wait on are done before allowing a state switch or restart + for (int i = 0; i < state.fNumCallbacks; i++) + { + if (!fCompletedEvents.IsBitSet(i)) + { + ResponderLog(ILog(plStatusLog::kWhite, "Can't reset, waiting for command %d(id:%d)", ICmdFromWait(i)+1, i)); + return false; + } + } + + ResponderLog(ILog(plStatusLog::kGreen, "Reset")); + + fCurCommand = -1; + ISetResponderState(state.fSwitchToState); + DirtySynchState(kSDLResponder, 0); + + return true; +} + +Int8 plResponderModifier::ICmdFromWait(Int8 waitIdx) +{ + WaitToCmd& waitToCmd = fStates[fCurState].fWaitToCmd; + if (waitToCmd.find(waitIdx) != waitToCmd.end()) + return waitToCmd[waitIdx]; + return -1; +} + +void plResponderModifier::Restore() +{ + // If we're the first player in and we're loading old state where this responder + // was running, fast forward it + if (plNetClientApp::GetInstance()->GetJoinOrder() == 0 && fCurCommand != -1 && !fGotFirstLoad) + { + fGotFirstLoad = true; + IFastForward(false); + return; + } + + ResponderLog(ILog(plStatusLog::kGreen, "Load SDL State")); + + fGotFirstLoad = true; + + plResponderState& state = fStates[fCurState]; + for (int i = 0; i < state.fNumCallbacks; i++) + { + if (!fCompletedEvents[i]) + { + int cmdIdx = state.fWaitToCmd[i]; + + plResponderCmd& cmd = state.fCmds[cmdIdx]; + + // + // If it's a callback message (anim or sound), just send the callbacks again + // + plMessageWithCallbacks* callbackMsg = plMessageWithCallbacks::ConvertNoRef(cmd.fMsg); + if (callbackMsg) + { + // Create a new message for just the callbacks + plMessageWithCallbacks* newCallbackMsg = nil; + + if (plAnimCmdMsg* animMsg = plAnimCmdMsg::ConvertNoRef(callbackMsg)) + { + plAnimCmdMsg* newAnimMsg = TRACKED_NEW plAnimCmdMsg; + newAnimMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); + newCallbackMsg = newAnimMsg; + ResponderLog(ILog(plStatusLog::kGreen, "Restoring anim callback")); + } + else if (plSoundMsg* soundMsg = plSoundMsg::ConvertNoRef(callbackMsg)) + { + plSoundMsg* newSoundMsg = TRACKED_NEW plSoundMsg; + newSoundMsg->SetCmd(plSoundMsg::kAddCallbacks); + newCallbackMsg = newSoundMsg; + ResponderLog(ILog(plStatusLog::kGreen, "Restoring sound callback")); + } + + // Setup the sender and receiver + newCallbackMsg->SetSender(callbackMsg->GetSender()); + for (int iReceiver = 0; i < callbackMsg->GetNumReceivers(); i++) + newCallbackMsg->AddReceiver(callbackMsg->GetReceiver(iReceiver)); + + // Add the callbacks + int numCallbacks = callbackMsg->GetNumCallbacks(); + for (int iCallback = 0; iCallback < numCallbacks; iCallback++) + { + plMessage* callback = callbackMsg->GetCallback(iCallback); +// hsRefCnt_SafeRef(callback); AddCallback will ref this for us. + newCallbackMsg->AddCallback(callback); + } + + newCallbackMsg->Send(); + } + } + } +} + +#include "plCreatableIndex.h" + +plMessage* plResponderModifier::IGetFastForwardMsg(plMessage* msg, bool python) +{ + if (!msg) + return nil; + + if (plAnimCmdMsg* animMsg = plAnimCmdMsg::ConvertNoRef(msg)) + { + if (animMsg->Cmd(plAnimCmdMsg::kContinue) || + animMsg->Cmd(plAnimCmdMsg::kAddCallbacks)) + { + plAnimCmdMsg* newAnimMsg = TRACKED_NEW plAnimCmdMsg; + newAnimMsg->fCmd = animMsg->fCmd; + newAnimMsg->fBegin = animMsg->fBegin; + newAnimMsg->fEnd = animMsg->fEnd; + newAnimMsg->fLoopEnd = animMsg->fLoopEnd; + newAnimMsg->fLoopBegin = animMsg->fLoopBegin; + newAnimMsg->fSpeed = animMsg->fSpeed; + newAnimMsg->fSpeedChangeRate = animMsg->fSpeedChangeRate; + newAnimMsg->fTime = animMsg->fTime; + newAnimMsg->SetAnimName(animMsg->GetAnimName()); + newAnimMsg->SetLoopName(animMsg->GetLoopName()); + + // Remove the callbacks + newAnimMsg->fCmd.SetBit(plAnimCmdMsg::kAddCallbacks, false); + + if (newAnimMsg->Cmd(plAnimCmdMsg::kContinue)) + { + newAnimMsg->fCmd.SetBit(plAnimCmdMsg::kContinue, false); + newAnimMsg->fCmd.SetBit(plAnimCmdMsg::kFastForward, true); + } + + for (int i = 0; i < animMsg->GetNumReceivers(); i++) + newAnimMsg->AddReceiver(animMsg->GetReceiver(i)); + + ResponderLog(ILog(plStatusLog::kWhite, "FF Animation Play Msg")); + return newAnimMsg; + } + + ResponderLog(ILog(plStatusLog::kWhite, "FF Animation Non-Play Msg")); + hsRefCnt_SafeRef(msg); + return msg; + } + else if(plSoundMsg *soundMsg = plSoundMsg::ConvertNoRef(msg)) + { + if( fFlags & kSkipFFSound ) + { + return nil; + } + if(soundMsg->Cmd(plSoundMsg::kPlay) || + soundMsg->Cmd(plSoundMsg::kToggleState) || + soundMsg->Cmd(plAnimCmdMsg::kAddCallbacks)) + { + plSoundMsg *newSoundMsg = TRACKED_NEW plSoundMsg; + newSoundMsg->fCmd = soundMsg->fCmd; + newSoundMsg->fBegin = soundMsg->fBegin; + newSoundMsg->fEnd = soundMsg->fEnd; + newSoundMsg->fLoop = soundMsg->fLoop; + newSoundMsg->fSpeed = soundMsg->fSpeed; + newSoundMsg->fTime = soundMsg->fTime; + newSoundMsg->fIndex = soundMsg->fIndex; + newSoundMsg->fRepeats = soundMsg->fRepeats; + newSoundMsg->fPlaying = soundMsg->fPlaying; + newSoundMsg->fNameStr = soundMsg->fNameStr; + newSoundMsg->fVolume = soundMsg->fVolume; + + // Remove the callbacks + newSoundMsg->fCmd.SetBit(plSoundMsg::kAddCallbacks, false); + + if(newSoundMsg->Cmd(plSoundMsg::kPlay)) + { + newSoundMsg->fCmd.SetBit(plSoundMsg::kPlay, false); + newSoundMsg->fCmd.SetBit(plSoundMsg::kFastForwardPlay); + ResponderLog(ILog(plStatusLog::kWhite, "FF Sound Play Msg")); + } + else if(newSoundMsg->Cmd(plSoundMsg::kToggleState)) + { + newSoundMsg->fCmd.SetBit(plSoundMsg::kToggleState, false); + newSoundMsg->fCmd.SetBit(plSoundMsg::kFastForwardToggle); + ResponderLog(ILog(plStatusLog::kWhite, "FF Sound Toggle State Msg")); + } + for (int i = 0; i < soundMsg->GetNumReceivers(); i++) + newSoundMsg->AddReceiver(soundMsg->GetReceiver(i)); + + return newSoundMsg; + } + ResponderLog(ILog(plStatusLog::kWhite, "FF Sound Non-Play/Toggle Msg")); + hsRefCnt_SafeRef(msg); + return msg; + } + else if (msg->ClassIndex() == CLASS_INDEX_SCOPED(plExcludeRegionMsg)) + { + ResponderLog(ILog(plStatusLog::kWhite, "FF Exclude Region Msg")); + hsRefCnt_SafeRef(msg); + return msg; + } + else if (msg->ClassIndex() == CLASS_INDEX_SCOPED(plEnableMsg)) + { + ResponderLog(ILog(plStatusLog::kWhite, "FF Visibility/Detector Enable Msg")); + hsRefCnt_SafeRef(msg); + return msg; + } + else if (msg->ClassIndex() == CLASS_INDEX_SCOPED(plResponderEnableMsg)) + { + ResponderLog(ILog(plStatusLog::kWhite, "FF Responder Enable Msg")); + hsRefCnt_SafeRef(msg); + return msg; + } + else if (msg->ClassIndex() == CLASS_INDEX_SCOPED(plSimSuppressMsg)) + { + ResponderLog(ILog(plStatusLog::kWhite, "FF Physical Enable Msg")); + hsRefCnt_SafeRef(msg); + return msg; + } + + return nil; +} + +void plResponderModifier::IFastForward(bool python) +{ + ResponderLog(ILog(plStatusLog::kGreen, "Fast Forward")); + + fCurCommand = 0; + + plResponderState& state = fStates[fCurState]; + + while (fCurCommand < state.fCmds.Count()) + { + plMessage *msg = state.fCmds[fCurCommand].fMsg; + msg = IGetFastForwardMsg(msg, python); + if (msg) + plgDispatch::MsgSend(msg); + + fCurCommand++; + } + + ResponderLog(ILog(plStatusLog::kGreen, "Reset")); + + fCurCommand = -1; + ISetResponderState(state.fSwitchToState); + + plSynchEnabler enable(true); + DirtySynchState(kSDLResponder, 0); +} + +void plResponderModifier::Read(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Read(stream, mgr); + + Int8 numStates = stream->ReadByte(); + fStates.SetCount(numStates); + for (Int8 i = 0; i < numStates; i++) + { + plResponderState& state = fStates[i]; + state.fNumCallbacks = stream->ReadByte(); + state.fSwitchToState = stream->ReadByte(); + + Int8 j; + + Int8 numCmds = stream->ReadByte(); + state.fCmds.SetCount(numCmds); + for (j = 0; j < numCmds; j++) + { + plResponderCmd& cmd = state.fCmds[j]; + + plMessage* pMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); + cmd.fMsg = pMsg; + cmd.fWaitOn = stream->ReadByte(); + } + + state.fWaitToCmd.clear(); + Int8 mapSize = stream->ReadByte(); + for (j = 0; j < mapSize; j++) + { + Int8 wait = stream->ReadByte(); + Int8 cmd = stream->ReadByte(); + state.fWaitToCmd[wait] = cmd; + } + } + + ISetResponderState(stream->ReadByte()); + fEnabled = stream->Readbool(); + fFlags = stream->ReadByte(); + + // attach responderSDLMod + delete fResponderSDLMod; + fResponderSDLMod = TRACKED_NEW plResponderSDLModifier; + fResponderSDLMod->SetResponder(this); +} + +void plResponderModifier::Write(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Write(stream, mgr); + + Int8 numStates = fStates.GetCount(); + stream->WriteByte(numStates); + for (int i = 0; i < numStates; i++) + { + plResponderState& state = fStates[i]; + stream->WriteByte(state.fNumCallbacks); + stream->WriteByte(state.fSwitchToState); + + Int8 numCmds = state.fCmds.GetCount(); + stream->WriteByte(numCmds); + for (int j = 0; j < numCmds; j++) + { + plResponderCmd& cmd = state.fCmds[j]; + + mgr->WriteCreatable(stream, cmd.fMsg); + stream->WriteByte(cmd.fWaitOn); + } + + Int8 mapSize = state.fWaitToCmd.size(); + stream->WriteByte(mapSize); + for (WaitToCmd::iterator it = state.fWaitToCmd.begin(); it != state.fWaitToCmd.end(); it++) + { + stream->WriteByte(it->first); + stream->WriteByte(it->second); + } + } + + stream->WriteByte(fCurState); + stream->Writebool(fEnabled); + stream->WriteByte(fFlags); +} + +#include "../plPipeline/plDebugText.h" + +bool plResponderModifier::fDebugAnimBox = false; + +void plResponderModifier::IDebugAnimBox(bool start) +{ + plDebugText &debugTxt = plDebugText::Instance(); + + UInt32 scrnWidth, scrnHeight; + debugTxt.GetScreenSize(&scrnWidth, &scrnHeight); + + // Box size is 1/8 screen size + UInt32 boxSize = scrnHeight / 8; + + // Draw box in lower left corner + if (start) + debugTxt.DrawRect(0, (UInt16)(scrnHeight-boxSize), (UInt16)boxSize, (UInt16)scrnHeight, 0, 255, 0); + else + debugTxt.DrawRect((UInt16)boxSize, (UInt16)(scrnHeight-boxSize), (UInt16)(boxSize*2), (UInt16)scrnHeight, 255, 0, 0); +} + +void plResponderModifier::IDebugPlayMsg(plAnimCmdMsg* msg) +{ + // Create a stop callback so we can do a cue for that too + plEventCallbackMsg *eventMsg = TRACKED_NEW plEventCallbackMsg; + eventMsg->AddReceiver(GetKey()); + eventMsg->fRepeats = 0; + eventMsg->fUser = -1; + eventMsg->fEvent = kStop; + msg->SetCmd(plAnimCmdMsg::kAddCallbacks); + msg->AddCallback(eventMsg); + hsRefCnt_SafeUnRef(eventMsg); + + IDebugAnimBox(true); +} + +//////////////////////////////////////////////////////////////////////////////// + +#ifdef STATUS_LOG +static plStatusLog *gLog = nil; +static std::vector gNoLogStrings; +#endif // STATUS_LOG + +void plResponderModifier::NoLogString(const char* str) +{ +#ifdef STATUS_LOG + gNoLogStrings.push_back(str); +#endif // STATUS_LOG +} + +void plResponderModifier::ILog(UInt32 color, const char* format, ...) +{ +#ifdef STATUS_LOG + if (!gLog) + gLog = plStatusLogMgr::GetInstance().CreateStatusLog(15, "Responder", plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | plStatusLog::kDontWriteFile | plStatusLog::kAlignToTop); + + if (!format || *format == '\0') + return; + + const char* keyName = GetKeyName(); + + // Make sure this key isn't in our list of keys to deny + for (int i = 0; i < gNoLogStrings.size(); i++) + { + if (strncmp(gNoLogStrings[i].c_str(), keyName, gNoLogStrings[i].length()) == 0) + return; + } + + // Format the log text + char buf[256]; + va_list args; + va_start(args, format); + int numWritten = _vsnprintf(buf, sizeof(buf), format, args); + hsAssert(numWritten > 0, "Buffer too small"); + va_end(args); + + // Strip the redundant part off the key name + char logLine[512]; + const char* modPos = strstr("_ResponderModifier", keyName); + if (modPos) + strncpy(logLine, keyName, modPos - keyName); + else + strcpy(logLine, keyName); + + strcat(logLine, ": "); + strcat(logLine, buf); + + gLog->AddLine(logLine, color); +#endif // STATUS_LOG +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderModifier.h new file mode 100644 index 00000000..2bbf4920 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderModifier.h @@ -0,0 +1,155 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plResponderModifier_inc +#define plResponderModifier_inc + +#include "hsStlUtils.h" +#include "../pnModifier/plSingleModifier.h" +#include "../pnMessage/plMessage.h" + + +class plNotifyMsg; +class plAnimCmdMsg; +class plResponderSDLModifier; +class plResponderModifier : public plSingleModifier +{ + friend class plResponderSDLModifier; +protected: + typedef std::map WaitToCmd; + + class plResponderCmd + { + public: + plResponderCmd() : fMsg(nil), fWaitOn(-1) {} + plResponderCmd(plMessage *msg, Int8 waitOn) : fMsg(msg), fWaitOn(waitOn) {} + + plMessage *fMsg; + Int8 fWaitOn; // Index into fCompletedEvents of who we're waiting on + }; + class plResponderState + { + public: + hsTArray fCmds; + Int8 fNumCallbacks; // So we know how far to search into the bitvector to find out when we're done + Int8 fSwitchToState; // State to switch to when all commands complete + WaitToCmd fWaitToCmd; + }; + + hsTArray fStates; + + Int8 fCurState; // The current state (first index for fCommandList) + Int8 fCurCommand; // The command we are currently waiting to send (or -1 if we're not sending) + bool fNetRequest; // Was the last trigger a net request + hsBitVector fCompletedEvents; // Which events that commands are waiting on have completed + bool fEnabled; + plKey fPlayerKey; // The player who triggered this last + plKey fTriggerer; // Whoever triggered us (for sending notify callbacks) + hsBool fEnter; // Is our current trigger a volume enter? + bool fGotFirstLoad; // Have we gotten our first SDL load? + + plResponderSDLModifier* fResponderSDLMod; // handles saving and restoring state + + enum + { + kDetectTrigger = 0x1, + kDetectUnTrigger = 0x2, + kSkipFFSound = 0x4 + }; + UInt8 fFlags; + UInt32 fNotifyMsgFlags; // store the msg flags of the notify which triggered us + + void Trigger(plNotifyMsg *msg); + bool IIsLocalOnlyCmd(plMessage* cmd); + bool IContinueSending(); + + Int8 ICmdFromWait(Int8 waitIdx); + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return true; } + + static bool fDebugAnimBox; // Draws a box on screen when an animation is started + static void IDebugAnimBox(bool start); + void IDebugPlayMsg(plAnimCmdMsg* msg); + + // Trigger the responder (regardless of what it's doing) and "fast forward" it to the final state + // If python is true, only run animations + void IFastForward(bool python); + // If the message is FF-able, returns it (or a FF-able version) + plMessage* IGetFastForwardMsg(plMessage* msg, bool python); + + void ISetResponderStateFromNotify(plNotifyMsg* msg); + void ISetResponderState(Int8 state); + + void ILog(UInt32 color, const char* format, ...); + + friend class plResponderComponent; + friend class plResponderWait; + +public: + plResponderModifier(); + ~plResponderModifier(); + + CLASSNAME_REGISTER( plResponderModifier ); + GETINTERFACE_ANY( plResponderModifier, plSingleModifier ); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + const plResponderSDLModifier* GetSDLModifier() const { return fResponderSDLMod; } + + static bool ToggleDebugAnimBox() { return fDebugAnimBox = !fDebugAnimBox; } + static void NoLogString(const char* str); + + // Restore callback state after load + void Restore(); + + const Int8 GetState() const { return fCurState; } + // + // Export time only + // + void AddCommand(plMessage* pMsg, int state=0); + void AddCallback(Int8 state, Int8 cmd, Int8 callback); +}; + +// Message for changing the enable state in a responder modifier +class plResponderEnableMsg : public plMessage +{ +public: + bool fEnable; + + plResponderEnableMsg() : fEnable(true) {} + plResponderEnableMsg(bool enable) : fEnable(enable) {} + + CLASSNAME_REGISTER(plResponderEnableMsg); + GETINTERFACE_ANY(plResponderEnableMsg, plMessage); + + // IO + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +#endif // plResponderModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderSDLModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderSDLModifier.cpp new file mode 100644 index 00000000..2179b554 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderSDLModifier.cpp @@ -0,0 +1,160 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plResponderSDLModifier.h" +#include "../plSDL/plSDL.h" +#include "plResponderModifier.h" + +// static vars +char plResponderSDLModifier::kStrCurState[]="curState"; +char plResponderSDLModifier::kStrEnabled[]="enabled"; +char plResponderSDLModifier::kStrCurCommand[]="curCommand"; +char plResponderSDLModifier::kStrNetRequest[]="netRequest"; +char plResponderSDLModifier::kStrCompletedEvents[]="completedEvents"; +char plResponderSDLModifier::kStrPlayerKey[]="playerKey"; +char plResponderSDLModifier::kStrTriggerer[]="triggerer"; + +plKey plResponderSDLModifier::GetStateOwnerKey() const +{ + return fResponder ? fResponder->GetKey() : nil; +} + +// +// get current state from responder +// fill out state data rec +// +void plResponderSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + hsAssert(fResponder, "nil responder?"); + + dstState->FindVar(kStrCurState)->Set((int)fResponder->fCurState); + dstState->FindVar(kStrEnabled)->Set(fResponder->fEnabled); + + dstState->FindVar(kStrCurCommand)->Set((int)fResponder->fCurCommand); + dstState->FindVar(kStrNetRequest)->Set(fResponder->fNetRequest); + + int i; + int num=fResponder->fCompletedEvents.GetNumBitVectors(); + dstState->FindVar(kStrCompletedEvents)->Alloc(num); + for(i=0;ifCompletedEvents.GetBitVector(i); + dstState->FindVar(kStrCompletedEvents)->Set(ev, i); + } + + dstState->FindVar(kStrPlayerKey)->Set(fResponder->fPlayerKey); + dstState->FindVar(kStrTriggerer)->Set(fResponder->fTriggerer); +} + +// +// apply incoming state to responder +// +void plResponderSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + hsAssert(fResponder, "nil responder?"); + + int curState = fResponder->fCurState; + int curCommand = fResponder->fCurCommand; + bool enabled = fResponder->fEnabled; + bool netRequest = fResponder->fNetRequest; + hsBitVector completedEvents = fResponder->fCompletedEvents; + plKey playerKey = fResponder->fPlayerKey; + plKey triggerer = fResponder->fTriggerer; + + plStateDataRecord::SimpleVarsList vars; + int numVars = srcState->GetUsedVars(&vars); + + for (int i = 0; i < numVars; i++) + { + if (vars[i]->IsNamed(kStrCurState)) + { + vars[i]->Get(&curState); + } + else if (vars[i]->IsNamed(kStrEnabled)) + { + vars[i]->Get(&enabled); + } + else if (vars[i]->IsNamed(kStrCurCommand)) + { + vars[i]->Get(&curCommand); + } + else if (vars[i]->IsNamed(kStrNetRequest)) + { + vars[i]->Get(&netRequest); + } + else if (vars[i]->IsNamed(kStrCompletedEvents)) + { + int numEvents = vars[i]->GetCount(); + completedEvents.SetNumBitVectors(numEvents); + + for (int j = 0; j < numEvents; j++) + { + int bv; + vars[i]->Get(&bv, j); + completedEvents.SetBitVector(j, bv); + } + } + else if (vars[i]->IsNamed(kStrPlayerKey)) + { + vars[i]->Get(&playerKey); + } + else if (vars[i]->IsNamed(kStrTriggerer)) + { + vars[i]->Get(&triggerer); + } + else + { + hsAssert(false, "Unknown var name"); + } + } + + if (numVars) + { + bool stateValid = (curState >= 0 && curState < fResponder->fStates.Count()); + hsAssert(stateValid, "Received invalid responder state"); + if (!stateValid) + return; + + bool cmdValid = curCommand == -1 || (curCommand >= 0 && curCommand < fResponder->fStates[curState].fCmds.Count()); + hsAssert(stateValid, "Received invalid responder command"); + if (!cmdValid) + return; + + // Could try to validate the completed events, but if someone hacked that + // all they could do is set events that we don't look at + + fResponder->fCurState = curState; + fResponder->fCurCommand = curCommand; + fResponder->fEnabled = enabled; + fResponder->fNetRequest = netRequest; + fResponder->fCompletedEvents = completedEvents; + fResponder->fPlayerKey = playerKey; + fResponder->fTriggerer = triggerer; + + fResponder->Restore(); + } +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderSDLModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderSDLModifier.h new file mode 100644 index 00000000..22e3fb87 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plResponderSDLModifier.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plResponderSDLModifier_inc +#define plResponderSDLModifier_inc + +#include "../plModifier/plSDLModifier.h" + +// +// This modifier is responsible for sending and recving responder state +// +class plResponderModifier; +class plStateDataRecord; +class plResponderSDLModifier : public plSDLModifier +{ +protected: + // var labels + static char kStrCurState[]; + static char kStrCurCommand[]; + static char kStrNetRequest[]; + static char kStrCompletedEvents[]; + static char kStrEnabled[]; + static char kStrPlayerKey[]; + static char kStrTriggerer[]; + + plResponderModifier* fResponder; + + void IPutCurrentStateIn(plStateDataRecord* dstState); + void ISetCurrentStateFrom(const plStateDataRecord* srcState); +public: + CLASSNAME_REGISTER( plResponderSDLModifier ); + GETINTERFACE_ANY( plResponderSDLModifier, plSDLModifier); + + plResponderSDLModifier() : fResponder(nil) {} + + const char* GetSDLName() const { return kSDLResponder; } + plKey GetStateOwnerKey() const; + + plResponderModifier* GetResponder() const { return fResponder; } + void SetResponder(plResponderModifier* r) { fResponder=r; AddTarget(nil); } +}; + +#endif // plResponderSDLModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSDLModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSDLModifier.cpp new file mode 100644 index 00000000..628f4c87 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSDLModifier.cpp @@ -0,0 +1,229 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSDLModifier.h" + +#include "../pnNetCommon/plSynchedObject.h" +#include "../pnDispatch/plDispatch.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plSDLModifierMsg.h" + +#include "../plNetMessage/plNetMessage.h" +#include "../plSDL/plSDL.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetClient/plNetObjectDebugger.h" + +plSDLModifier::plSDLModifier() : fStateCache(nil), fSentOrRecvdState(false) +{ +} + +plSDLModifier::~plSDLModifier() +{ + delete fStateCache; +} + +plKey plSDLModifier::GetStateOwnerKey() const +{ + return GetTarget() ? GetTarget()->GetKey() : nil; +} + +void plSDLModifier::AddTarget(plSceneObject* so) +{ + if (so) + plSingleModifier::AddTarget(so); + if (!fStateCache) + fStateCache = TRACKED_NEW plStateDataRecord(GetSDLName()); +} + +UInt32 plSDLModifier::IApplyModFlags(UInt32 sendFlags) +{ + return sendFlags; +} + +// +// write to net msg and send to server +// +void plSDLModifier::ISendNetMsg(plStateDataRecord*& state, plKey senderKey, UInt32 sendFlags) +{ + hsAssert(senderKey, "nil senderKey?"); + + plSynchedObject* sobj = plSynchedObject::ConvertNoRef(senderKey->ObjectIsLoaded()); + if (sobj && (sobj->IsInSDLVolatileList(GetSDLName()))) + state->SetFlags(state->GetFlags() | plStateDataRecord::kVolatile); + + bool dirtyOnly = (sendFlags & plSynchedObject::kForceFullSend) == 0; + bool broadcast = (sendFlags & plSynchedObject::kBCastToClients) != 0; + int writeOptions=0; +// if (dirtyOnly) + writeOptions |= plSDL::kDirtyOnly; + if (broadcast) + writeOptions |= plSDL::kBroadcast; + + writeOptions |= plSDL::kTimeStampOnRead; + + plNetClientMgr::GetInstance()->StoreSDLState(state, senderKey->GetUoid(), sendFlags, writeOptions); + + fSentOrRecvdState = true; +} + +// +// Process SDL msgs to send and recv state +// +hsBool plSDLModifier::MsgReceive(plMessage* msg) +{ + plSDLModifierMsg* sdlMsg = plSDLModifierMsg::ConvertNoRef(msg); + if (sdlMsg && !stricmp(sdlMsg->GetSDLName(),GetSDLName())) + { + UInt32 sendFlags = IApplyModFlags(sdlMsg->GetFlags()); + + if (!fSentOrRecvdState) + sendFlags |= plSynchedObject::kNewState; + + if (sdlMsg->GetAction()==plSDLModifierMsg::kSendToServer) + { + // local player is changing the state and sending it out + plStateChangeNotifier::SetCurrentPlayerID(plNetClientApp::GetInstance()->GetPlayerID()); + + SendState(sendFlags); + } + else + if (sdlMsg->GetAction()==plSDLModifierMsg::kSendToServerAndClients) + { + // local player is changing the state and sending it out + plStateChangeNotifier::SetCurrentPlayerID(plNetClientApp::GetInstance()->GetPlayerID()); + + SendState(sendFlags | plSynchedObject::kBCastToClients); + } + else + if (sdlMsg->GetAction()==plSDLModifierMsg::kRecv) + { + plStateDataRecord* sdRec=sdlMsg->GetState(); + plStateChangeNotifier::SetCurrentPlayerID(sdlMsg->GetPlayerID()); // remote player changed the state + ReceiveState(sdRec); + } + + return true; // consumed + } + + return plSingleModifier::MsgReceive(msg); +} + +// +// send a state update +// +bool gMooseDump=false; +void plSDLModifier::SendState(UInt32 sendFlags) +{ + hsAssert(fStateCache, "nil stateCache"); + + bool debugObject = (plNetObjectDebugger::GetInstance() && + plNetObjectDebugger::GetInstance()->IsDebugObject(GetStateOwnerKey()->ObjectIsLoaded())); + + bool force = (sendFlags & plSynchedObject::kForceFullSend) != 0; + bool broadcast = (sendFlags & plSynchedObject::kBCastToClients) != 0; + + // record current state + plStateDataRecord* curState = TRACKED_NEW plStateDataRecord(GetSDLName()); + IPutCurrentStateIn(curState); // return sdl record which reflects current state of sceneObj, dirties curState + if (!force) + { + curState->FlagDifferentState(*fStateCache); // flag items which are different from localCopy as dirty + } + + if (curState->IsDirty()) + { + // send current state + bool dirtyOnly = force ? false : true; + ISendNetMsg(curState, GetStateOwnerKey(), sendFlags); // send the state + + if (debugObject) + { + gMooseDump=true; + plNetObjectDebugger::GetInstance()->SetDebugging(true); + curState->DumpToObjectDebugger(xtl::format("Object %s SENDS SDL state", + GetStateOwnerKey()->GetName(), dirtyOnly).c_str()); + gMooseDump=false; + } + + // cache current state, send notifications if necessary + fStateCache->UpdateFrom(*curState, dirtyOnly); // update local copy of state + + ISentState(curState); + } + delete curState; + + if (plNetObjectDebugger::GetInstance()) + plNetObjectDebugger::GetInstance()->SetDebugging(false); +} + +void plSDLModifier::ReceiveState(const plStateDataRecord* srcState) +{ + hsAssert(fStateCache, "nil stateCache"); + + if (plNetObjectDebugger::GetInstance() && + plNetObjectDebugger::GetInstance()->IsDebugObject(GetStateOwnerKey()->ObjectIsLoaded())) + { + gMooseDump=true; + plNetObjectDebugger::GetInstance()->SetDebugging(true); + srcState->DumpToObjectDebugger(xtl::format("Object %s RECVS SDL state", + GetStateOwnerKey()->GetName()).c_str()); + gMooseDump=false; + } + + if (srcState->IsUsed()) + { + plSynchEnabler ps(false); // disable dirty tracking while we are receiving/applying state + + // apply incoming state + ISetCurrentStateFrom(srcState); // apply incoming state to sceneObj + + // cache state, send notifications if necessary + fStateCache->UpdateFrom(*srcState, false); // update local copy of state + fSentOrRecvdState = true; + } + else + { + plNetClientApp::GetInstance()->DebugMsg("\tReceiving and ignoring unused SDL state msg: type %s, object %s", + GetSDLName(), GetStateOwnerKey()->GetName()); + } + + if (plNetObjectDebugger::GetInstance()) + plNetObjectDebugger::GetInstance()->SetDebugging(false); +} + +void plSDLModifier::AddNotifyForVar(plKey key, const char* varName, float tolerance) const +{ + // create a SDL notifier object + plStateChangeNotifier notifier(tolerance, key); + // set the notification + plStateDataRecord* rec = GetStateCache(); + if (rec) + { + plSimpleStateVariable* var = rec->FindVar(varName); + // was the variable found? + if (var) + var->AddStateChangeNotification(notifier); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSDLModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSDLModifier.h new file mode 100644 index 00000000..fc4857c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSDLModifier.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSDLModifier_inc +#define plSDLModifier_inc + +#include "../pnModifier/plSingleModifier.h" +#include "../pnNetCommon/plSDLTypes.h" + +// +// Base class for modifiers which send/recv State Desc Language (SDL) messages +// +class plStateDataRecord; +class plSimpleStateVariable; +class plSDLModifier : public plSingleModifier +{ +protected: + plStateDataRecord* fStateCache; + bool fSentOrRecvdState; + + void ISendNetMsg(plStateDataRecord*& state, plKey senderKey, UInt32 sendFlags); // transmit net msg + virtual void IPutCurrentStateIn(plStateDataRecord* dstState) = 0; + virtual void ISetCurrentStateFrom(const plStateDataRecord* srcState) = 0; + virtual void ISentState(const plStateDataRecord* sentState) {} + hsBool IEval(double secs, hsScalar del, UInt32 dirty) {return false;} + + virtual UInt32 IApplyModFlags(UInt32 sendFlags); + +public: + CLASSNAME_REGISTER( plSDLModifier ); + GETINTERFACE_ANY( plSDLModifier, plSingleModifier); + + plSDLModifier(); + virtual ~plSDLModifier(); + + hsBool MsgReceive(plMessage* msg); + void SendState(UInt32 sendFlags); // send a state update + void ReceiveState(const plStateDataRecord* srcState); // recv a state update + virtual const char* GetSDLName() const = 0; // return the string name of the type of state descriptor you handle + virtual plKey GetStateOwnerKey() const; + + plStateDataRecord* GetStateCache() const { return fStateCache; } + virtual void AddTarget(plSceneObject* so); + + void AddNotifyForVar(plKey key, const char* varName, float tolerance) const; +}; + +#endif // plSDLModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSimpleModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSimpleModifier.cpp new file mode 100644 index 00000000..99e98949 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSimpleModifier.cpp @@ -0,0 +1,185 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plSimpleModifier.h" +#include "plgDispatch.h" + +#include "hsStream.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plRefMsg.h" +#include "hsTimer.h" +// #include "../pfConditional/plAnimationEventConditionalObject.h" +#include "../plMessage/plAnimCmdMsg.h" + +plSimpleModifier::plSimpleModifier() +: + fTarget(nil) +{ + fTimeConvert.SetOwner(this); +} + +plSimpleModifier::~plSimpleModifier() +{ + if( !fTimeConvert.IsStopped() ) + IEnd(); +} + +void plSimpleModifier::Read(hsStream* s, hsResMgr* mgr) +{ + plModifier::Read(s, mgr); + + fTimeConvert.Read(s, mgr); + + if( !fTimeConvert.IsStopped() ) + IBegin(); // TEMP TILL Message causes IBEGIN +} + +void plSimpleModifier::Write(hsStream* s, hsResMgr* mgr) +{ + plModifier::Write(s, mgr); + + fTimeConvert.Write(s, mgr); +} + +void plSimpleModifier::AddTarget(plSceneObject* o) +{ + fTarget = o; + if( !fTimeConvert.IsStopped() ) + IBegin(); // TEMP TILL Message causes IBEGIN +} + +void plSimpleModifier::RemoveTarget(plSceneObject* o) +{ + hsAssert(o == fTarget, "Removing target I don't have"); + fTarget = nil; +} + +void plSimpleModifier::IBegin() +{ + if( fTarget ) + { + fTimeConvert.Start(); + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + + } +} + +void plSimpleModifier::IEnd() +{ + fTimeConvert.Stop(); + if( fTarget ) + { + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + } +} + +hsBool plSimpleModifier::IEval(double secs, hsScalar del, UInt32 dirty) +{ + return IHandleTime(secs, del); +} + +hsBool plSimpleModifier::MsgReceive(plMessage* msg) +{ + plRefMsg* refMsg = plRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + return IHandleRef(refMsg); + } + plAnimCmdMsg* modMsg = plAnimCmdMsg::ConvertNoRef(msg); + if( modMsg ) + { + return IHandleCmd(modMsg); + } + return plModifier::MsgReceive(msg); +} + +hsBool plSimpleModifier::IHandleRef(plRefMsg* refMsg) +{ + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + AddTarget(plSceneObject::ConvertNoRef(refMsg->GetRef())); + else + RemoveTarget(plSceneObject::ConvertNoRef(refMsg->GetRef())); + + return true; +} + +hsBool plSimpleModifier::IHandleCmd(plAnimCmdMsg* modMsg) +{ + hsBool wasStopped = fTimeConvert.IsStopped(); + + fTimeConvert.HandleCmd(modMsg); + + hsBool isStopped = fTimeConvert.IsStopped(); + + if( wasStopped != isStopped ) + { + if( isStopped ) + { + IEnd(); + } + else + { + IBegin(); + } + } + +#if 0 // debug + char str[256]; + sprintf(str, "ModHandleCmd: time=%f, ts=%f FWD=%d, BWD=%d, SpeedChange=%d sp=%f, CONT=%d, STOP=%d\n", + hsTimer::GetSysSeconds(), + modMsg->GetTimeStamp(), + modMsg->Cmd(plAnimCmdMsg::kSetForewards), + modMsg->Cmd(plAnimCmdMsg::kSetBackwards), + modMsg->Cmd(plAnimCmdMsg::kSetSpeed), + modMsg->fSpeed, + modMsg->Cmd(plAnimCmdMsg::kContinue), + modMsg->Cmd(plAnimCmdMsg::kStop)); + hsStatusMessage(str); +#endif + return true; +} + +hsBool plSimpleModifier::IHandleTime(double wSecs, hsScalar del) +{ + + if( !fTarget ) + return true; + + hsScalar secs = fTimeConvert.WorldToAnimTime(wSecs); + + if( secs != fCurrentTime ) + { + fCurrentTime = secs; + + IApplyDynamic(); + } + + return true; +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSimpleModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSimpleModifier.h new file mode 100644 index 00000000..940605a7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSimpleModifier.h @@ -0,0 +1,94 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSimpleModifier_inc +#define plSimpleModifier_inc + +#include "../pnModifier/plModifier.h" +#include "../pnNetCommon/plSynchedValue.h" +#include "../plInterp/plAnimTimeConvert.h" + +class plSceneObject; +class plRefMsg; +class plTimeMsg; +class plAnimCmdMsg; + +class plSimpleModifier : public plModifier +{ +public: + enum { + kStopped = 0, + kLoop, + kBackwards, + kWrapped, + + kNumFlags + }; +protected: + plSceneObject* fTarget; + + double fLastTime; // in world time + + plAnimTimeConvert fTimeConvert; + + // The following are in animation time. + hsScalar fCurrentTime; + + hsBool IHandleTime(double wSecs, hsScalar del); + + hsBool IHandleRef(plRefMsg* refMsg); + virtual hsBool IHandleCmd(plAnimCmdMsg* modMsg); + + virtual void IApplyDynamic() = 0; + + virtual void IBegin(); + virtual void IEnd(); + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + +public: + plSimpleModifier(); + virtual ~plSimpleModifier(); + + CLASSNAME_REGISTER( plSimpleModifier ); + GETINTERFACE_ANY( plSimpleModifier, plModifier); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual int GetNumTargets() const { return fTarget ? 1 : 0; } + virtual plSceneObject* GetTarget(int w) const { /* hsAssert(w < GetNumTargets(), "Bad target"); */ return fTarget; } + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + virtual plAnimTimeConvert& GetTimeConvert() { return fTimeConvert; } + + +}; + + +#endif // plSimpleModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSoundSDLModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSoundSDLModifier.cpp new file mode 100644 index 00000000..8fbe10af --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSoundSDLModifier.cpp @@ -0,0 +1,130 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSoundSDLModifier.h" +#include "../plSDL/plSDL.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../plAudio/plSound.h" + +// static vars +char plSoundSDLModifier::kStrVolume[]="desiredVolume"; +char plSoundSDLModifier::kStrTime[]="time"; +char plSoundSDLModifier::kStrPlaying[]="playing"; +char plSoundSDLModifier::kStrSounds[]="sounds"; + +// +// get current state from audio interface +// fill out state data rec +// +void plSoundSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + /*plSceneObject* sobj=GetTarget(); + hsAssert(sobj, "plSoundSDLModifier, nil target"); + + const plAudioInterface* ai=sobj->GetAudioInterface(); + hsAssert(ai, "nil audio interface"); + + plSDStateVariable* soundListVar=dstState->FindSDVar(kStrSounds); + int numSounds=ai->GetNumSounds(); + soundListVar->Resize(numSounds); + + int i; + for(i=0;iGetStateDataRecord(i); + plSound* sound=ai->GetSound(i); + + soundState->FindVar(kStrVolume)->Set(sound->fDesiredVol); + soundState->FindVar(kStrTime)->Set(sound->fVirtualStartTime); + soundState->FindVar(kStrPlaying)->Set(sound->IsPlaying()); + }*/ +} + +// +// apply incoming state to current audio interface +// +void plSoundSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + plSceneObject* sobj=GetTarget(); + hsAssert(sobj, "plSoundSDLModifier, nil target"); + + const plAudioInterface* ai=sobj->GetAudioInterface(); + hsAssert(ai, "nil audio interface"); + int numSounds=ai->GetNumSounds(); + + plSDStateVariable* soundListVar=srcState->FindSDVar(kStrSounds); + + if( soundListVar->GetCount() != numSounds ) + { + hsAssert( false, "number sounds sounds should not be changing"); + return; + } + + int i; + for(i=0;iGetStateDataRecord(i); + plSound* sound=ai->GetSound(i); + + float desiredVol; + soundState->FindVar(kStrVolume)->Get(&desiredVol); + //sound->ISetUnsynchedVolume(desiredVol); // MCN CHECK + + bool playing; + if (soundState->FindVar(kStrPlaying)->Get(&playing)) + { + if (playing) + { + //double timeStarted; + /*if (soundState->FindVar(kStrTime)->Get(&timeStarted)) + sound->SynchedPlay((hsScalar)timeStarted); + else + { + // Can't get the time we're supposed to start at, so we'll just try to play normally, + // which should be better than nothing... + hsAssert( false, "No timeStarted state in sound SDL. Bad state from server? Contact MCN *immediately*" ); + sound->Play(); + }*/ + } + else + { + if( sound->IsPropertySet( plSound::kPropAutoStart ) ) + { +#if 0 + // There is a sound in teledahn (swampAmb) which leggaly has this behavior + hsAssert( false, "Auto-start sound just got a state update telling it to stop. " + "This is technically legal, but so far there isn't any case where this should " + "happen. Further, it's very likely to be the cause of the very-intermittent " + "auto-start-sounds-not-playing bug. Leave this up and contact MCN *immediately*" ); +#endif + } + //sound->IStop(); + } + } + } +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSoundSDLModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSoundSDLModifier.h new file mode 100644 index 00000000..bf0a67ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSoundSDLModifier.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSoundSDLModifier_inc +#define plSoundSDLModifier_inc + +#include "../plModifier/plSDLModifier.h" + +// +// This modifier is responsible for sending and recving +// an object's audio state. +// +class plStateDataRecord; +class plSoundSDLModifier : public plSDLModifier +{ +protected: + // var labels + static char kStrVolume[]; + static char kStrTime[]; + static char kStrPlaying[]; + static char kStrSounds[]; + + void IPutCurrentStateIn(plStateDataRecord* dstState); + void ISetCurrentStateFrom(const plStateDataRecord* srcState); +public: + CLASSNAME_REGISTER( plSoundSDLModifier ); + GETINTERFACE_ANY( plSoundSDLModifier, plSDLModifier); + + const char* GetSDLName() const { return kSDLSound; } +}; + +#endif // plSoundSDLModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSpawnModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSpawnModifier.cpp new file mode 100644 index 00000000..9b5ad425 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSpawnModifier.cpp @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +//#include "hsTypes.h" +//#include "hsGeometry3.h" +//#include "plgDispatch.h" +//#include "../pnSceneObject/plDrawInterface.h" +//#include "../pnSceneObject/plCoordinateInterface.h" +//#include "hsBounds.h" +#include "plSpawnModifier.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plAvatar/plAvatarMgr.h" +//#include "../pnMessage/plTimeMsg.h" +//#include "../pnKeyedObject/plKey.h" + +//#include "../plMessage/plSpawnRequestMsg.h" +//#include "../plMessage/plSpawnModMsg.h" + +//hsBool plSpawnModifier::MsgReceive(plMessage* msg) +//{ +// plSpawnRequestMsg* pSpawnMsg = plSpawnRequestMsg::ConvertNoRef(msg); +// if (pSpawnMsg) +// { +// fTargets.GetCount(); +// for (int i=0; i < GetNumTargets(); i++) +// { +// plSpawnModMsg* pMsg = NEW(plSpawnModMsg); +// pMsg->AddReceiver( pSpawnMsg->GetSender() ); +// //pMsg->fPos= GetTarget(i)->GetDrawInterface()->GetWorldBounds().GetCenter(); +// pMsg->fPos= GetTarget(i)->GetCoordinateInterface()->GetLocalToWorld().GetTranslate(); +// pMsg->fObj = GetTarget(i)->GetKey()->GetUoid(); +// plgDispatch::MsgSend( pMsg ); +// } +// return true; +// } +// return plMultiModifier::MsgReceive(msg); +//} + +void plSpawnModifier::AddTarget(plSceneObject* so) +{ + plMultiModifier::AddTarget(so); + plAvatarMgr::GetInstance()->AddSpawnPoint(this); +// plgDispatch::Dispatch()->RegisterForExactType(plSpawnRequestMsg::Index(), GetKey()); +} + +void plSpawnModifier::RemoveTarget(plSceneObject* so) +{ + plMultiModifier::RemoveTarget(so); + hsAssert(fTargets.GetCount() == 0, "Spawn modifier has multiple targets. Matt."); + + plAvatarMgr::GetInstance()->RemoveSpawnPoint(this); +} + +void plSpawnModifier::Read(hsStream *stream, hsResMgr *mgr) +{ + plMultiModifier::Read(stream, mgr); +} + +void plSpawnModifier::Write(hsStream *stream, hsResMgr *mgr) +{ + plMultiModifier::Write(stream, mgr); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSpawnModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSpawnModifier.h new file mode 100644 index 00000000..229013d5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plSpawnModifier.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSpawnModifier_inc +#define plSpawnModifier_inc + +#include "../pnModifier/plMultiModifier.h" +#include "../pnMessage/plMessage.h" + +class plSpawnModifier : public plMultiModifier +{ +protected: + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) {return true;} + +public: + plSpawnModifier(){;} + + CLASSNAME_REGISTER( plSpawnModifier ); + GETINTERFACE_ANY( plSpawnModifier, plMultiModifier ); + + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + +// hsBool MsgReceive(plMessage* msg); +// + virtual void Read(hsStream *stream, hsResMgr *mgr); + virtual void Write(hsStream *stream, hsResMgr *mgr); +}; + + +#endif // plSpawnModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plTagModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plTagModifier.cpp new file mode 100644 index 00000000..5973d38f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plTagModifier.cpp @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plTagModifier.h" + +#include "hsResMgr.h" +#include "../pnMessage/plRemoteAvatarInfoMsg.h" +#include "../plMessage/plAvatarMsg.h" +//#include "../pfMessage/plClothingMsg.h" +#include "../plMessage/plCollideMsg.h" +#include "../plMessage/plSimInfluenceMsg.h" + +#include "../plStatusLog/plStatusLog.h" + +static plStatusLog* gLog = nil; + +plTagModifier::plTagModifier() +{ + if (!gLog) + gLog = plStatusLogMgr::GetInstance().CreateStatusLog(15, "Tag", plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | plStatusLog::kDontWriteFile | plStatusLog::kAlignToTop); +} + +plTagModifier::~plTagModifier() +{ +} + +hsBool plTagModifier::MsgReceive(plMessage* msg) +{ + plCollideMsg* collideMsg = plCollideMsg::ConvertNoRef(msg); + if (collideMsg) + { + gLog->AddLineF("Kicked by %s", collideMsg->fOtherKey->GetName()); + return true; + } + + plRemoteAvatarInfoMsg* avInfoMsg = plRemoteAvatarInfoMsg::ConvertNoRef(msg); + if (avInfoMsg) + { + // TODO + // Check if the local av is frozen +// plKey localAvKey = plNetClientMgr::GetInstance()->GetLocalPlayerKey(); + + // Freeze clicked av + plKey clickedAvKey = avInfoMsg->GetAvatarKey(); + if (clickedAvKey) + { + static hsBool tempHack = true; + tempHack = !tempHack; + + plAvEnableMsg* avEnableMsg = new plAvEnableMsg(GetKey(), clickedAvKey, tempHack); + avEnableMsg->SetBCastFlag(plMessage::kNetPropagate | plMessage::kPropagateToModifiers); + avEnableMsg->Send(); + + gLog->AddLineF("Tagged %s", clickedAvKey->GetName()); + } + + return true; + } + + return plSingleModifier::MsgReceive(msg); +} + +void plTagModifier::Read(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Read(stream, mgr); +} + +void plTagModifier::Write(hsStream* stream, hsResMgr* mgr) +{ + plSingleModifier::Write(stream, mgr); +} + +#include "plgDispatch.h" + +void plTagModifier::SetTarget(plSceneObject* so) +{ + if (so) + plgDispatch::Dispatch()->RegisterForExactType(plRemoteAvatarInfoMsg::Index(), GetKey()); + else + plgDispatch::Dispatch()->UnRegisterForExactType(plRemoteAvatarInfoMsg::Index(), GetKey()); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plTagModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plTagModifier.h new file mode 100644 index 00000000..5ad7c32f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plModifier/plTagModifier.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plTagModifier_inc +#define plTagModifier_inc + +#include "../pnModifier/plSingleModifier.h" + +class plTagModifier : public plSingleModifier +{ +protected: + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty) { return true; } + +public: + plTagModifier(); + virtual ~plTagModifier(); + + CLASSNAME_REGISTER(plTagModifier); + GETINTERFACE_ANY(plTagModifier, plSingleModifier); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual void SetTarget(plSceneObject* so); +}; + +#endif // plTagModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plLinkEffectsMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plLinkEffectsMgr.cpp new file mode 100644 index 00000000..1a849d7a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plLinkEffectsMgr.cpp @@ -0,0 +1,594 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "../pnKeyedObject/plKey.h" +#include "hsTemplates.h" +#include "hsStream.h" +#include "plLinkEffectsMgr.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plPlayerPageMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../plMessage/plTransitionMsg.h" +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "hsTimer.h" +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../plVault/plVault.h" +#include "../plNetClient/plNetLinkingMgr.h" +#include "../plAgeLoader/plAgeLoader.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnMessage/plWarpMsg.h" +#include "../pnKeyedObject/plFixedKey.h" + +// chronicle var +#define kCleftSolved L"CleftSolved" + +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvatarTasks.h" +#include "../plAvatar/plAGAnim.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plLoadAgeMsg.h" + +plLinkEffectsMgr::plLinkEffectsMgr() +{ +} + +plLinkEffectsMgr::~plLinkEffectsMgr() +{ + int i; + for( i = 0; i < fLinks.GetCount(); i++ ) + { + hsRefCnt_SafeUnRef(fLinks[i]); + } + for( i = 0; i < fWaitlist.GetCount(); i++ ) + { + hsRefCnt_SafeUnRef(fWaitlist[i]); + } + for( i = 0; i < fDeadlist.GetCount(); i++ ) + { + hsRefCnt_SafeUnRef(fDeadlist[i]); + } +} + +void plLinkEffectsMgr::Init() +{ + plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plPseudoLinkEffectMsg::Index(), GetKey()); +} + +plLinkEffectsTriggerMsg *plLinkEffectsMgr::IFindLinkTriggerMsg(plKey linkKey) +{ + int i; + for (i = 0; i < fLinks.GetCount(); i++) + { + if (fLinks[i]->GetLinkKey() == linkKey) + return fLinks[i]; + } + return nil; +} + +void plLinkEffectsMgr::IAddLink(plLinkEffectsTriggerMsg *msg) +{ + hsRefCnt_SafeRef(msg); + fLinks.Append(msg); +} + +void plLinkEffectsMgr::IAddWait(plLinkEffectsTriggerMsg *msg) +{ + hsRefCnt_SafeRef(msg); + fWaitlist.Append(msg); +} + +void plLinkEffectsMgr::IAddDead(plLinkEffectsTriggerMsg *msg) +{ + hsRefCnt_SafeRef(msg); + fDeadlist.Append(msg); +} + +void plLinkEffectsMgr::IAddPsuedo(plPseudoLinkEffectMsg *msg) +{ + hsRefCnt_SafeRef(msg); + fPseudolist.Append(msg); +} + +hsBool plLinkEffectsMgr::IHuntWaitlist(plLinkEffectsTriggerMsg *msg) +{ + int i; + hsBool found = false; + for (i = fWaitlist.GetCount() - 1; i >= 0; i--) + { + if (fWaitlist[i] == msg) + { + found = true; + hsRefCnt_SafeUnRef(fWaitlist[i]); + fWaitlist.Remove(i); + plNetApp::GetInstance()->DebugMsg("Received backup LinkEffectsTriggerMsg. Never got remote trigger!\n"); + } + } + + return found || IHuntWaitlist(msg->GetLinkKey()); +} + +hsBool plLinkEffectsMgr::IHuntWaitlist(plKey linkKey) +{ + int i; + hsBool found = false; + for (i = fWaitlist.GetCount() - 1; i >= 0; i--) + { + if (fWaitlist[i]->GetLinkKey() == linkKey) + { + found = true; + IAddDead(fWaitlist[i]); + + hsRefCnt_SafeUnRef(fWaitlist[i]); + fWaitlist.Remove(i); + plNetApp::GetInstance()->DebugMsg("Received remote LinkEffectsTriggerMsg. Awaiting backup.\n"); + } + } + + return found; +} + +hsBool plLinkEffectsMgr::IHuntDeadlist(plLinkEffectsTriggerMsg *msg) +{ + int i; + hsBool found = false; + for (i = fDeadlist.GetCount() - 1; i >= 0; i--) + { + if (fDeadlist[i] == msg) + { + found = true; + hsRefCnt_SafeUnRef(fDeadlist[i]); + fDeadlist.Remove(i); + plNetApp::GetInstance()->DebugMsg("Received backup LinkEffectsTriggerMsg. Cleanly ignoring since we received remote trigger.\n"); + } + } + + return found; +} + + + +void plLinkEffectsMgr::ISendAllReadyCallbacks() +{ + int i; + for (i = fLinks.GetCount() - 1; i >= 0; i--) + { + if (fLinks[i]->fEffects <= 0) + { + if (fLinks[i]->IsLeavingAge()) + { + if (fLinks[i]->GetLinkKey() == plNetClientApp::GetInstance()->GetLocalPlayerKey()) + { + plLinkOutUnloadMsg* lam = TRACKED_NEW plLinkOutUnloadMsg; // derived from LoadAgeMsg + lam->SetAgeFilename( NetCommGetAge()->ageDatasetName ); + lam->AddReceiver(plNetClientMgr::GetInstance()->GetKey()); + lam->SetPlayerID(plNetClientMgr::GetInstance()->GetPlayerID()); + lam->Send(); + } + } + else + { + plLinkInDoneMsg* lid = TRACKED_NEW plLinkInDoneMsg; + lid->AddReceiver(fLinks[i]->GetLinkKey()); + lid->SetBCastFlag(plMessage::kPropagateToModifiers); + lid->Send(); + + if (fLinks[i]->GetLinkKey() == plNetClientApp::GetInstance()->GetLocalPlayerKey()) + { + plLinkInDoneMsg* lid = TRACKED_NEW plLinkInDoneMsg; + lid->AddReceiver(plNetClientMgr::GetInstance()->GetKey()); + lid->Send(); + } + } + + hsRefCnt_SafeUnRef(fLinks[i]); + fLinks.Remove(i); + + hsStatusMessage("Done - removing link FX msg\n"); + } + } +} + +hsBool plLinkEffectsMgr::MsgReceive(plMessage *msg) +{ + plNetClientMgr* nc = plNetClientMgr::GetInstance(); + plNetLinkingMgr* lm = plNetLinkingMgr::GetInstance(); + + plPseudoLinkEffectMsg* pSeudoMsg = plPseudoLinkEffectMsg::ConvertNoRef(msg); + if (pSeudoMsg) + { + // verify valid avatar and "link" objects + if (!pSeudoMsg->fAvatarKey) + return true; + if (!pSeudoMsg->fLinkObjKey) + return true; + if (!pSeudoMsg->fLinkObjKey->ObjectIsLoaded()) + return true; + if (!plNetClientMgr::GetInstance()->IsAPlayerKey(pSeudoMsg->fAvatarKey)) + return true; + // send the trigger message to the avatar... + plPseudoLinkAnimTriggerMsg* pMsg = TRACKED_NEW plPseudoLinkAnimTriggerMsg(true, pSeudoMsg->fAvatarKey); + pMsg->SetSender(GetKey()); + pMsg->Send(); + IAddPsuedo(pSeudoMsg); + } + + plPseudoLinkAnimCallbackMsg* pSeudoCallback = plPseudoLinkAnimCallbackMsg::ConvertNoRef(msg); + if (pSeudoCallback) + { + // warp the avatar to his new position + plPseudoLinkEffectMsg* pMsg = IFindPseudo(pSeudoCallback->fAvatarKey); + if (pMsg) + { + plSceneObject* pObj = plSceneObject::ConvertNoRef(pMsg->fLinkObjKey->ObjectIsLoaded()); + if (pObj && pObj->GetCoordinateInterface()) + { + hsMatrix44 mat = pObj->GetCoordinateInterface()->GetLocalToWorld(); + // create message + plWarpMsg* pMsg = TRACKED_NEW plWarpMsg(mat); + pMsg->SetWarpFlags(plWarpMsg::kFlushTransform); + pMsg->AddReceiver(pSeudoCallback->fAvatarKey); + plUoid U(kVirtualCamera1_KEY); + plKey pCamKey = hsgResMgr::ResMgr()->FindKey(U); + if (pCamKey) + { + pMsg->AddReceiver(pCamKey); + } + plgDispatch::MsgSend( pMsg ); // whoosh... off it goes + // now make him re-appear + plPseudoLinkAnimTriggerMsg* pTrigMsg = TRACKED_NEW plPseudoLinkAnimTriggerMsg(false, pSeudoCallback->fAvatarKey); + pTrigMsg->SetSender(GetKey()); + pTrigMsg->Send(); + IRemovePseudo(pSeudoCallback->fAvatarKey); + + } + } + } + plLinkEffectsTriggerPrepMsg *tpMsg = plLinkEffectsTriggerPrepMsg::ConvertNoRef(msg); + if (tpMsg) + { + plNetApp::GetInstance()->DebugMsg("Received LinkEffectsTriggerPREPMsg\n"); + IAddWait(tpMsg->GetTrigger()); + plLinkEffectPrepBCMsg *bcpMsg = TRACKED_NEW plLinkEffectPrepBCMsg; + bcpMsg->fLeavingAge = tpMsg->fLeavingAge; + bcpMsg->fLinkKey = tpMsg->fLinkKey; + bcpMsg->Send(); + + return true; + } + + plLinkEffectsTriggerMsg* pTriggerMsg = plLinkEffectsTriggerMsg::ConvertNoRef(msg); + if (pTriggerMsg) + { + plNetApp::GetInstance()->DebugMsg("Received LinkEffectsTriggerMsg, local=%d, linkingIn=%d, stealth=%d", + !msg->HasBCastFlag(plMessage::kNetNonLocal), + !pTriggerMsg->IsLeavingAge(), pTriggerMsg->GetInvisLevel()); + + plKey linkKey = pTriggerMsg->GetLinkKey(); + if (linkKey == nil) + return true; + + if ((linkKey != nc->GetLocalPlayerKey()) && + (!pTriggerMsg->IsLeavingAge())) + { + if (IHuntDeadlist(pTriggerMsg)) // Just an obselete safety trigger + return true; + + if (!IHuntWaitlist(pTriggerMsg)) + { + plNetApp::GetInstance()->DebugMsg("Unexpected linkEffectsTriggerMsg. Ignoring\n"); + return true; + } + } + + plSceneObject *avatar = plSceneObject::ConvertNoRef(linkKey->ObjectIsLoaded()); + if (avatar == nil) + { + plNetApp::GetInstance()->DebugMsg("Can't find avatar, mod=%s\n", linkKey->GetName()); + return true; + } + + // This is not the right place to catch this problem. +// if (IFindLinkTriggerMsg(linkKey) != nil) +// { +// hsAssert(false, "Trying to link an Avatar already in the process of linking."); +// return true; +// } + + if (pTriggerMsg->GetInvisLevel() && linkKey != nc->GetLocalPlayerKey()) + { +#ifdef PLASMA_EXTERNAL_RELEASE + // Verify that the server told us that the invisible avatar is a CCR + plNetTransportMember* mbr=nc->TransportMgr().GetMember(nc->TransportMgr().FindMember(linkKey)); + if (!mbr || mbr->GetCCRLevel()GetInvisLevel()) + { + plNetApp::StaticErrorMsg("Remote Avatar trying to be stealthy - REJECTING since he's not a CCR"); + } + else +#endif + { + plNetApp::StaticDebugMsg("Remote Avatar is in stealth mode - making invisible"); + nc->MakeCCRInvisible(pTriggerMsg->GetLinkKey(), pTriggerMsg->GetInvisLevel()); + } + } + + if (pTriggerMsg->IsLeavingAge()) + hsStatusMessage("Starting LinkOut FX\n"); + else + hsStatusMessage("Starting LinkIn FX\n"); + + plLinkEffectBCMsg *BCMsg = TRACKED_NEW plLinkEffectBCMsg(); + BCMsg->fLinkKey = linkKey; + BCMsg->SetLinkFlag(plLinkEffectBCMsg::kLeavingAge, pTriggerMsg->IsLeavingAge()); + BCMsg->SetLinkFlag(plLinkEffectBCMsg::kSendCallback, true); + + // Check if you have a Yeesha book, and mute sound if you don't. + // 'CleftSolved' gets set when you click on the linking panel in the cleft, + // so we use that instead of checking KILevel. + // Also, check if you're going to/from the ACA, or through the fissure, and mute sound if you are. + if (linkKey == nc->GetLocalPlayerKey()) + { + if(lm) { + const char *ageName = lm->GetAgeLink()->GetAgeInfo()->GetAgeFilename(); + const char *prevAgeName = lm->GetPrevAgeLink()->GetAgeInfo()->GetAgeFilename(); + + bool linkToStartup = ageName && !stricmp(ageName, kStartUpAgeFilename ); // To Startup + bool linkFromStartup = prevAgeName && !stricmp(prevAgeName, kStartUpAgeFilename); // Leaving Startup + + bool cleftSolved = VaultHasChronicleEntry( kCleftSolved ); + + bool linkToACA = ageName && !stricmp(ageName, kAvCustomizationFilename); + bool linkFromACA = prevAgeName && !stricmp(prevAgeName, kAvCustomizationFilename); + + bool linkToFissureDrop = lm && + lm->GetAgeLink()->HasSpawnPt() && + lm->GetAgeLink()->SpawnPoint().GetName() && + !stricmp(lm->GetAgeLink()->SpawnPoint().GetName(), kCleftAgeLinkInPointFissureDrop); + bool linkToDsntFromShell = lm && + lm->GetAgeLink()->HasSpawnPt() && + lm->GetAgeLink()->SpawnPoint().GetTitle() && + !stricmp(lm->GetAgeLink()->SpawnPoint().GetTitle(), kDescentLinkFromShell); + if ( linkToACA || linkFromACA || linkToStartup || linkFromStartup || linkToFissureDrop || linkToDsntFromShell) + { + BCMsg->SetLinkFlag(plLinkEffectBCMsg::kMute); + } + } + } + + BCMsg->SetSender(GetKey()); + if (msg->HasBCastFlag(plMessage::kNetNonLocal)) + // terminate the remote cascade and start a new (local) cascade, since the rcvr is localOnly and will reject remote msgs + BCMsg->SetBCastFlag(plMessage::kNetStartCascade); + plgDispatch::MsgSend(BCMsg); + + if (!pTriggerMsg->IsLeavingAge()) // Avatar is currently entering a new age + { + plATCAnim *linkInAnim = nil; + plKey linkInAnimKey = nil; + const plArmatureMod *avMod = plArmatureMod::ConvertNoRef(avatar->GetModifierByType(plArmatureMod::Index())); + if (pTriggerMsg->HasBCastFlag(plMessage::kNetNonLocal)) + { + // Remote trigger, they should tell us how they linked in. + linkInAnimKey = pTriggerMsg->GetLinkInAnimKey(); + } + else + { + // this is our backup trigger we send ourselves. We've already received the remote player's SDL. + linkInAnimKey = avMod ? avMod->GetLinkInAnimKey() : nil; + } + linkInAnim = plATCAnim::ConvertNoRef(linkInAnimKey ? linkInAnimKey->ObjectIsLoaded() : nil); + + if (avMod && linkInAnim) + { + plAvOneShotTask *task = TRACKED_NEW plAvOneShotTask(linkInAnim->GetName(), false, false, nil); + task->fBackwards = true; + task->fDisableLooping = true; + task->fDisablePhysics = false; + (TRACKED_NEW plAvTaskMsg(GetKey(), avMod->GetKey(), task))->Send(); + } + + } + + IAddLink(pTriggerMsg); // refs the avatarMod + + // Dummy msg sent after the broadcast. This guarantees we have a callback to actually trigger the + // link, plus we know any effect broadcast messages will have processed before this (and therefore + // have told us to wait for them.) + pTriggerMsg->fEffects++; + plLinkCallbackMsg *dummyMsg = TRACKED_NEW plLinkCallbackMsg(); + dummyMsg->AddReceiver(GetKey()); + dummyMsg->fLinkKey = linkKey; + plgDispatch::MsgSend(dummyMsg); + + return true; + } + + // callbacks from linkout events + plLinkCallbackMsg* pLinkCallbackMsg = plLinkCallbackMsg::ConvertNoRef(msg); + if (pLinkCallbackMsg) + { + plNetApp::GetInstance()->DebugMsg("Received pLinkCallbackMsg, localmsg=%d\n", + !msg->HasBCastFlag(plMessage::kNetNonLocal)); + + static char str[ 128 ]; + plLinkEffectsTriggerMsg *pTriggerMsg = IFindLinkTriggerMsg(pLinkCallbackMsg->fLinkKey); + if (pTriggerMsg == nil) + { + hsAssert(true, "Received a callback for an avatar that isn't linking."); + return true; + } + + if (--pTriggerMsg->fEffects == 0) + { + plNetApp::GetInstance()->DebugMsg("All link callbacks received.\n" ); + plgDispatch::Dispatch()->RegisterForExactType(plTimeMsg::Index(), GetKey()); + } + else if (pTriggerMsg->fEffects < 0 ) + { + plNetApp::GetInstance()->DebugMsg("Too many link callbacks received for avatar %s. Ignoring extras.\n", + pTriggerMsg->GetLinkKey()->GetName()); + } + else + { + plNetApp::GetInstance()->DebugMsg("%d link callbacks left until avatar %s links...\n", + pTriggerMsg->fEffects, pTriggerMsg->GetLinkKey()->GetName()); + } + return true; + } + + plTimeMsg *time = plTimeMsg::ConvertNoRef(msg); + if (time) // This is how we know we're out of the render function, and it's safe to pageIn/Out nodes + { + plgDispatch::Dispatch()->UnRegisterForExactType(plTimeMsg::Index(), GetKey()); + ISendAllReadyCallbacks(); + + return true; + } + + plPlayerPageMsg *pageMsg = plPlayerPageMsg::ConvertNoRef(msg); + if (pageMsg) + { + if (pageMsg->fUnload) + { + IHuntWaitlist(pageMsg->fPlayer); + return true; + } + + const hsScalar kMaxTimeForLinkTrigger = 30.f; + + // If we're not loading state, we're in the age. So this avatar coming in must be linking in. + // If the player is us, no prep is necessary. + if (!plNetClientApp::GetInstance()->IsLoadingInitialAgeState() && + (pageMsg->fPlayer != nc->GetLocalPlayerKey())) + { + plLinkEffectsTriggerMsg *trigMsg = TRACKED_NEW plLinkEffectsTriggerMsg; + trigMsg->SetLeavingAge(false); + trigMsg->SetLinkKey(pageMsg->fPlayer); + + // Send off the prep message right away + plLinkEffectsTriggerPrepMsg *trigPrepMsg = TRACKED_NEW plLinkEffectsTriggerPrepMsg; + trigPrepMsg->fLinkKey = pageMsg->fPlayer; + trigPrepMsg->SetTrigger(trigMsg); + trigPrepMsg->Send(GetKey()); + + // Send off a delayed safety trigger. If things are going along properly, + // we'll get a trigger from the player linking in before this message is + // received, and we'll ignore it. + double timeToDeliver = hsTimer::GetSysSeconds() + kMaxTimeForLinkTrigger; + trigMsg->SetTimeStamp(timeToDeliver); + trigMsg->Send(GetKey()); + } + + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + +void plLinkEffectsMgr::WaitForEffect(plKey linkKey, hsScalar time) +{ + plLinkEffectsTriggerMsg *msg = IFindLinkTriggerMsg(linkKey); + if (msg == nil) + { + hsAssert(true, "Request to wait on an effect for an avatar that isn't linking."); + return; + } + + msg->fEffects++; + plLinkCallbackMsg *callback = TRACKED_NEW plLinkCallbackMsg(); + callback->fEvent = kStop; + callback->fRepeats = 0; + callback->fLinkKey = linkKey; + double timeToDeliver = hsTimer::GetSysSeconds() + time; + callback->SetTimeStamp( timeToDeliver ); + callback->Send( GetKey() ); +} + +plMessage *plLinkEffectsMgr::WaitForEffect(plKey linkKey) +{ + plLinkEffectsTriggerMsg *msg = IFindLinkTriggerMsg(linkKey); + if (msg == nil) + { + hsAssert(true, "Request to wait on an effect for an avatar that isn't linking."); + return nil; + } + + msg->fEffects++; + + plLinkCallbackMsg *callback = TRACKED_NEW plLinkCallbackMsg(); + callback->fEvent = kStop; + callback->fRepeats = 0; + callback->fLinkKey = linkKey; + callback->AddReceiver( GetKey() ); + return callback; +} + +void plLinkEffectsMgr::WaitForPseudoEffect(plKey linkKey, hsScalar time) +{ + plPseudoLinkEffectMsg* msg = IFindPseudo(linkKey); + if (msg == nil) + { + hsAssert(true, "Request to wait on an fake effect for an avatar that isn't fake linking."); + return; + } + + plPseudoLinkAnimCallbackMsg* callback = TRACKED_NEW plPseudoLinkAnimCallbackMsg(); + callback->fAvatarKey = linkKey; + double timeToDeliver = hsTimer::GetSysSeconds() + time; + callback->SetTimeStamp( timeToDeliver ); + callback->Send( GetKey() ); +} + +plPseudoLinkEffectMsg* plLinkEffectsMgr::IFindPseudo(plKey avatarKey) +{ + int i; + for (i = 0; i < fPseudolist.GetCount(); i++) + { + if (fPseudolist[i]->fAvatarKey == avatarKey) + return fPseudolist[i]; + } + return nil; + +} + +void plLinkEffectsMgr::IRemovePseudo(plKey avatarKey) +{ + int i; + for (i = 0; i < fPseudolist.GetCount(); i++) + { + if (fPseudolist[i]->fAvatarKey == avatarKey) + { + fPseudolist.Remove(i); + return; + } + } + +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plLinkEffectsMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plLinkEffectsMgr.h new file mode 100644 index 00000000..94a75ed7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plLinkEffectsMgr.h @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plLinkEffectsMgr_inc +#define plLinkEffectsMgr_inc + +#include "../pnKeyedObject/hsKeyedObject.h" + +class plLinkEffectsTriggerMsg; +class plPseudoLinkEffectMsg; + +class plLinkEffectsMgr : public hsKeyedObject +{ +protected: + // Collection of links in progress (in or out) + hsTArray fLinks; + + // Players we know exist, but aren't ready to link yet + hsTArray fWaitlist; + + // Queue of delayed messages from people linking in that + // we haven't received yet but are no longer necessary, + // because we either received the trigger from them, or + // they're no longer in the age. + hsTArray fDeadlist; + + // queue of pseudo link messages + hsTArray fPseudolist; + + plLinkEffectsTriggerMsg *IFindLinkTriggerMsg(plKey avatarKey); + void IAddLink(plLinkEffectsTriggerMsg *msg); + void IAddWait(plLinkEffectsTriggerMsg *msg); + void IAddDead(plLinkEffectsTriggerMsg *msg); + void IAddPsuedo(plPseudoLinkEffectMsg *msg); + void IRemovePseudo(plKey avatarKey); + plPseudoLinkEffectMsg* IFindPseudo(plKey avatarKey); + + hsBool IHuntWaitlist(plLinkEffectsTriggerMsg *msg); + hsBool IHuntWaitlist(plKey linkKey); + hsBool IHuntDeadlist(plLinkEffectsTriggerMsg *msg); + void ISendAllReadyCallbacks(); + +public: + plLinkEffectsMgr(); + ~plLinkEffectsMgr(); + void Init(); + + CLASSNAME_REGISTER( plLinkEffectsMgr ); + GETINTERFACE_ANY( plLinkEffectsMgr, hsKeyedObject ); + + void WaitForEffect(plKey linkKey, hsScalar time); + void WaitForPseudoEffect(plKey linkKey, hsScalar time); + + plMessage *WaitForEffect(plKey linkKey); + + virtual hsBool MsgReceive(plMessage *msg); +}; + +#endif // plLinkEffectsMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp new file mode 100644 index 00000000..ec076027 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp @@ -0,0 +1,476 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp +* +* Encapsulates all of the horrible ugliness that the age load process has become +* +***/ + +#include "plNetCliAgeJoiner.h" +#include "plNetClientMgr.h" +#include "plNetLinkingMgr.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +#include "../pnMessage/plPlayerPageMsg.h" +#include "../pnMessage/plTimeMsg.h" + +#include "../plNetClientComm/plNetClientComm.h" +#include "../plAgeLoader/plAgeLoader.h" +#include "../plAgeLoader/plBackgroundDownloader.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plVault/plVault.h" + +#include "../plNetMessage/plNetMessage.h" + +#include "../plMessage/plNetCommMsgs.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../plMessage/plNetClientMgrMsg.h" + +#include "../plProgressMgr/plProgressMgr.h" +#include "../pnDispatch/plDispatch.h" +#include "../plResMgr/plResManager.h" + + +/***************************************************************************** +* +* Private +* +***/ + +struct plNCAgeJoiner { + + enum NextOp { + kNoOp, + kLoadAge, + kLoadPlayer, + kRequestAgeState, + kPropagatePlayer, + kDestroyProgressBar, + kEnableClickables, + kSimStateRcvd, + kNotifyAgeLoaded, + }; + + NextOp nextOp; + NetCommAge age; + FNCAgeJoinerCallback callback; + void * userState; + bool complete; + + plOperationProgress* progressBar; + + plNCAgeJoiner ( + const NetCommAge & age, + FNCAgeJoinerCallback callback, + void * userState + ); + ~plNCAgeJoiner (); + + void Start (); + void Complete (bool success, const char msg[]); + bool MsgReceive (plMessage * msg); + void Update (); + void ExecNextOp (); + + static void IDispatchMsgReceiveCallback (); + static void IResMgrProgressBarCallback (plKey key); + + static plNCAgeJoiner* s_instance; +}; + +plNCAgeJoiner* plNCAgeJoiner::s_instance = nil; + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +void AgeVaultDownloadCallback ( + ENetError result, + void * param +) { + plNCAgeJoiner * joiner = (plNCAgeJoiner *)param; + if (IS_NET_ERROR(result)) { + joiner->Complete(false, "Failed to download age vault"); + } + else { + // vault downloaded. start loading age data + LogMsg(kLogPerf, L"AgeJoiner: Next:kLoadAge (vault downloaded)"); + joiner->nextOp = plNCAgeJoiner::kLoadAge; + } +} + + +/***************************************************************************** +* +* plNCAgeJoiner +* +***/ + +//============================================================================ +plNCAgeJoiner::plNCAgeJoiner ( + const NetCommAge & age, + FNCAgeJoinerCallback callback, + void * userState +) : nextOp(kNoOp) +, age(age) +, callback(callback) +, userState(userState) +, progressBar(nil) +{ +} + +//============================================================================ +plNCAgeJoiner::~plNCAgeJoiner () { +} + +//============================================================================ +void plNCAgeJoiner::IDispatchMsgReceiveCallback () { + if (s_instance) + s_instance->progressBar->Increment(1); +} + +//============================================================================ +void plNCAgeJoiner::IResMgrProgressBarCallback (plKey key) { +#ifndef PLASMA_EXTERNAL_RELEASE + if (s_instance) + s_instance->progressBar->SetStatusText(key->GetName()); +#endif +} + +//============================================================================ +void plNCAgeJoiner::Complete (bool success, const char msg[]) { + + if (!complete) { + complete = true; + + s_instance = nil; + + NCAgeJoinerCompleteNotify notify; + notify.success = success; + notify.msg = msg; + + callback(this, kAgeJoinerComplete, ¬ify, userState); + DEL(this); + } + + if (plBackgroundDownloader::GetInstance()) + plBackgroundDownloader::GetInstance()->UnPause(); +} + +//============================================================================ +void plNCAgeJoiner::Start () { + s_instance = this; + + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + nc->SetFlagsBit(plNetClientMgr::kPlayingGame, false); + nc->fServerTimeOffset = 0; // reset since we're connecting to a new server + nc->fRequiredNumInitialSDLStates = 0; + nc->fNumInitialSDLStates = 0; + nc->SetFlagsBit(plNetClientApp::kNeedInitialAgeStateCount); + nc->SetFlagsBit(plNetClientApp::kLoadingInitialAgeState); + + // if we're linking to startup then set the OfflineAge flag + // so we by-pass the game server + if (StrLen(age.ageDatasetName) == 0 || StrCmpI(age.ageDatasetName, "StartUp") == 0) + nc->SetFlagsBit(plNetClientApp::kLinkingToOfflineAge); + else + nc->SetFlagsBit(plNetClientApp::kLinkingToOfflineAge, false); + + plAgeLoader* al = plAgeLoader::GetInstance(); + al->UpdateAge(age.ageDatasetName); + + nc->ResetServerTimeOffset(); + + NetCommLinkToAge( + age, + this + ); +} + +//============================================================================ +void plNCAgeJoiner::ExecNextOp () { + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + plAvatarMgr * am = plAvatarMgr::GetInstance(); + plAgeLoader * al = plAgeLoader::GetInstance(); + + NextOp next = nextOp; + nextOp = kNoOp; + switch (next) { + //==================================================================== + case kNoOp: { + } + break; + + //==================================================================== + case kLoadAge: { + LogMsg(kLogPerf, L"AgeJoiner: Exec:kLoadAge"); + + // Start progress bar + char str[256]; + #ifdef PLASMA_EXTERNAL_RELEASE + StrCopy(str, "Loading age...", arrsize(str)); + #else + StrPrintf(str, arrsize(str), "Loading age %s...", age.ageDatasetName); + #endif + progressBar = plProgressMgr::GetInstance()->RegisterOperation(0, str, plProgressMgr::kNone, false, true); + plDispatch::SetMsgRecieveCallback(IDispatchMsgReceiveCallback); + ((plResManager*)hsgResMgr::ResMgr())->SetProgressBarProc(IResMgrProgressBarCallback); + + // Start loading age data + al->LoadAge(age.ageDatasetName); + } + break; + + //==================================================================== + case kLoadPlayer: { + LogMsg(kLogPerf, L"AgeJoiner: Exec:kLoadPlayer"); + // Start loading local player + const char * avatarName; + if (NetCommNeedToLoadAvatar()) { + if (nc->GetFlagsBit(plNetClientApp::kLinkingToOfflineAge)) + avatarName = "Male"; + else + avatarName = NetCommGetPlayer()->avatarDatasetName; + const char * linkInName = plNetLinkingMgr::GetInstance()->GetAgeLink()->SpawnPoint().GetName(); + am->LoadPlayer( avatarName, nil, linkInName ); + } + else { + LogMsg(kLogPerf, L"AgeJoiner: Next:kPropagatePlayer"); + nextOp = kPropagatePlayer; + } + } + break; + + //==================================================================== + case kPropagatePlayer: { + LogMsg(kLogPerf, L"AgeJoiner: Exec:kPropagatePlayer"); + // Add our avatar to the scene + int spawnPt = am->FindSpawnPoint(age.spawnPtName); + nc->IPlayerChangeAge(false /*not exiting*/, spawnPt); + + if (!nc->GetFlagsBit(plNetClientApp::kLinkingToOfflineAge)) + // Add our avatar to the game state + am->PropagateLocalPlayer(spawnPt); + + LogMsg(kLogPerf, L"AgeJoiner: Next:kRequestAgeState"); + nextOp = kRequestAgeState; + } + break; + + //============================================================================ + case kRequestAgeState: { + LogMsg(kLogPerf, L"AgeJoiner: Exec:kRequestAgeState"); + if (nc->GetFlagsBit(plNetClientApp::kLinkingToOfflineAge)) { + LogMsg(kLogPerf, L"AgeJoiner: Next:kSimStateRcvd"); + nextOp = kSimStateRcvd; + } + else { + // Request age player list + nc->ISendMembersListRequest(); + + // Request initial SDL state + plNetMsgGameStateRequest gsmsg; + gsmsg.SetNetProtocol(kNetProtocolCli2Game); + gsmsg.SetBit(plNetMessage::kInitialAgeStateRequest); + nc->SendMsg(&gsmsg); + + // Send our avatar settings + nc->SendLocalPlayerAvatarCustomizations(); + } + } + break; + + //==================================================================== + case kSimStateRcvd: { + nc->NotifyRcvdAllSDLStates(); + } + break; + + //============================================================================ + case kDestroyProgressBar: { + plDispatch::SetMsgRecieveCallback(nil); + ((plResManager*)hsgResMgr::ResMgr())->SetProgressBarProc(nil); + delete progressBar; + progressBar = nil; + + nextOp = kEnableClickables; + } + break; + + //==================================================================== + case kEnableClickables: { + LogMsg(kLogPerf, L"AgeJoiner: Exec:kEnableClickables"); + // Enable scene clickables + (void)(TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kEnableClickables))->Send(); + + LogMsg(kLogPerf, L"AgeJoiner: Next:kNotifyAgeLoaded"); + nextOp = kNotifyAgeLoaded; + } + break; + + //==================================================================== + case kNotifyAgeLoaded: { + LogMsg(kLogPerf, L"AgeJoiner: Exec:kNotifyAgeLoaded"); + nc->SetFlagsBit(plNetClientApp::kPlayingGame); + nc->SetFlagsBit(plNetClientApp::kNeedToSendInitialAgeStateLoadedMsg); + plAgeLoader::GetInstance()->NotifyAgeLoaded(true); + Complete(true, "Age joined"); + } + break; + + DEFAULT_FATAL(nextOp); + } +} + +//============================================================================ +bool plNCAgeJoiner::MsgReceive (plMessage * msg) { + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + plAvatarMgr * am = plAvatarMgr::GetInstance(); + plAgeLoader * al = plAgeLoader::GetInstance(); + + //======================================================================== + // Connected to age instance + //======================================================================== + if (plNetCommLinkToAgeMsg * linkToAgeMsg = plNetCommLinkToAgeMsg::ConvertNoRef(msg)) { + if (IS_NET_ERROR(linkToAgeMsg->result)) { + Complete(false, "LinkToAge failed"); + } + else if (unsigned ageVaultId = NetCommGetAge()->ageVaultId) { + // Download the age vault + VaultDownload( + L"AgeJoin", + ageVaultId, + AgeVaultDownloadCallback, + this, + nil, // FVaultDownloadProgressCallback + this + ); + } + else { + // not vault to downloaded, just start loading age data + LogMsg(kLogPerf, L"AgeJoiner: Next:kLoadAge (no vault)"); + nextOp = kLoadAge; + } + return true; + } + + //======================================================================== + // All age data paged in + //======================================================================== + if (plAgeLoaded2Msg * ageLoaded2Msg = plAgeLoaded2Msg::ConvertNoRef(msg)) { + // Exec custom age settings + al->ExecPendingAgeFniFiles(); + al->ExecPendingAgeCsvFiles(); + + LogMsg(kLogPerf, L"AgeJoiner: Next:kLoadPlayer"); + nextOp = kLoadPlayer; + return true; + } + + //======================================================================== + // Local avatar loaded + //======================================================================== + plPlayerPageMsg * playerPageMsg = plPlayerPageMsg::ConvertNoRef(msg); + if (playerPageMsg && !playerPageMsg->fUnload && playerPageMsg->fPlayer && playerPageMsg->fLocallyOriginated) { + + if (NetCommNeedToLoadAvatar()) + NetCommSetAvatarLoaded(); + + LogMsg(kLogPerf, L"AgeJoiner: Next:kPropagatePlayer"); + nextOp = kPropagatePlayer; + return false; // NetClientMgr must also handle this message + } + + //======================================================================== + // Received all SDL states + //======================================================================== + plNetClientMgrMsg * netClientMgrMsg = plNetClientMgrMsg::ConvertNoRef(msg); + if (netClientMgrMsg && netClientMgrMsg->type == plNetClientMgrMsg::kNotifyRcvdAllSDLStates) { + LogMsg(kLogPerf, L"AgeJoiner: Next:kEnableClickables"); + nextOp = kDestroyProgressBar; + return true; + } + + return false; +} + +//============================================================================ +void plNCAgeJoiner::Update () { + ExecNextOp(); +} + + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void NCAgeJoinerCreate ( + plNCAgeJoiner ** pjoiner, + const NetCommAge & age, + FNCAgeJoinerCallback callback, + void * userState +) { + ASSERT(pjoiner); + ASSERT(callback); + + plNCAgeJoiner * joiner; + *pjoiner = joiner = NEWZERO(plNCAgeJoiner)( + age, + callback, + userState + ); + joiner->Start(); +} + +//============================================================================ +bool NCAgeJoinerMsgReceive ( + plNCAgeJoiner * joiner, + plMessage * msg +) { + ASSERT(joiner); + return joiner->MsgReceive(msg); +} + +//============================================================================ +void NCAgeJoinerUpdate ( + plNCAgeJoiner * joiner +) { + ASSERT(joiner); + joiner->Update(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.h new file mode 100644 index 00000000..6c0a766a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETCLIENT_PLNETCLIAGEJOINER_H +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETCLIENT_PLNETCLIAGEJOINER_H + + +#include "HeadSpin.h" +#include "../pnUtils/pnUtils.h" +#include "../plNetClientComm/plNetClientComm.h" + + +/***************************************************************************** +* +* +* +***/ + +class plMessage; +struct plNCAgeJoiner; + +enum ENCAgeJoinerNotify { + kAgeJoinerComplete, // notify --> NCAgeJoinerCompleteNotify *, after callback, joiner is destroyed + kNumAgeJoinerNotifications +}; + +struct NCAgeJoinerCompleteNotify { + bool success; + const char * msg; +}; + + +typedef void (* FNCAgeJoinerCallback)( + plNCAgeJoiner * joiner, + unsigned type, // ENCAgeJoinerNotify + void * notify, + void * userState +); + + +void NCAgeJoinerCreate ( + plNCAgeJoiner ** joiner, + const NetCommAge & age, + FNCAgeJoinerCallback callback, + void * userState +); +bool NCAgeJoinerMsgReceive ( // returns true of message was processed + plNCAgeJoiner * joiner, + plMessage * msg +); +void NCAgeJoinerUpdate ( + plNCAgeJoiner * joiner +); + + + +#endif // PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETCLIENT_PLNETCLIAGEJOINER_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.cpp new file mode 100644 index 00000000..cf3fe27d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.cpp @@ -0,0 +1,284 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.cpp +* +***/ + +#include "plNetCliAgeLeaver.h" +#include "plNetClientMgr.h" +#include "plNetLinkingMgr.h" + +#include "../pnMessage/plTimeMsg.h" + +#include "../plNetClientComm/plNetClientComm.h" +#include "../plNetGameLib/plNetGameLib.h" +#include "../plAgeLoader/plAgeLoader.h" +#include "../plAgeLoader/plBackgroundDownloader.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plVault/plVault.h" + +#include "../plMessage/plLoadAgeMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" + + + +/***************************************************************************** +* +* Private +* +***/ + +struct plNCAgeLeaver { + + enum NextOp { + kNoOp, + kDisableClickables, + kLinkOutFX, + kUnloadAge, + kNotifyAgeUnloaded, + }; + + NextOp nextOp; + bool quitting; + bool complete; + FNCAgeLeaverCallback callback; + void * userState; + + plNCAgeLeaver ( + bool quitting, + FNCAgeLeaverCallback callback, + void * userState + ); + ~plNCAgeLeaver (); + + void Start (); + void Complete (bool success, const char msg[]); + bool MsgReceive (plMessage * msg); + void Update (); + void ExecNextOp (); +}; + + +/***************************************************************************** +* +* plNCAgeLeaver +* +***/ + +//============================================================================ +plNCAgeLeaver::plNCAgeLeaver ( + bool quitting, + FNCAgeLeaverCallback callback, + void * userState +) : nextOp(kNoOp) +, quitting(quitting) +, complete(false) +, callback(callback) +, userState(userState) +{ +} + +//============================================================================ +plNCAgeLeaver::~plNCAgeLeaver () { +} + +//============================================================================ +void plNCAgeLeaver::Start () { + if (plBackgroundDownloader::GetInstance()) + plBackgroundDownloader::GetInstance()->Pause(); + + nextOp = kLinkOutFX; +} + +//============================================================================ +void plNCAgeLeaver::Complete (bool success, const char msg[]) { + + if (!complete) { + complete = true; + + NCAgeLeaveCompleteNotify notify; + notify.success = success; + notify.msg = msg; + + callback(this, kAgeLeaveComplete, ¬ify, userState); + DEL(this); + } +} + +//============================================================================ +bool plNCAgeLeaver::MsgReceive (plMessage * msg) { + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + plAvatarMgr * am = plAvatarMgr::GetInstance(); + plAgeLoader * al = plAgeLoader::GetInstance(); + + //======================================================================== + // Done with link out effects + //======================================================================== + if (plLinkOutUnloadMsg * linkOutUnloadMsg = plLinkOutUnloadMsg::ConvertNoRef(msg)) + { + if (!linkOutUnloadMsg->HasBCastFlag(plMessage::kNetNonLocal) + && linkOutUnloadMsg->GetPlayerID() == NetCommGetPlayer()->playerInt + ) { + nextOp = kUnloadAge; + } + return true; + } + + //======================================================================== + // Age data unloaded + //======================================================================== + if (plAgeLoadedMsg * ageLoadedMsg = plAgeLoadedMsg::ConvertNoRef(msg)) { + if (!ageLoadedMsg->fLoaded) { + nextOp = kNotifyAgeUnloaded; + return true; + } + return false; + } + + + return false; +} + +//============================================================================ +void plNCAgeLeaver::ExecNextOp () { + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + plAvatarMgr * am = plAvatarMgr::GetInstance(); + plAgeLoader * al = plAgeLoader::GetInstance(); + + NextOp next = nextOp; + nextOp = kNoOp; + switch (next) { + //==================================================================== + case kNoOp: { + } + break; + + //==================================================================== + case kDisableClickables: { + (TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableClickables))->Send(); + nextOp = kLinkOutFX; + } + break; + + //==================================================================== + case kLinkOutFX: { + nc->StartLinkOutFX(); + } + break; + + //==================================================================== + case kUnloadAge: { + NetCliGameDisconnect(); + + // Cull nodes that were part of this age vault (but not shared by the player's vault) + VaultCull(NetCommGetAge()->ageVaultId); + + // remove the age device inbox mappings + VaultClearDeviceInboxMap(); + + // Tell our local player that he's unspawning (if that is indeed the case) + nc->IPlayerChangeAge(true /* exiting */, 0/* respawn */); + // disconnect age vault + + // @@@ TODO: Unload age vault here + + plAgeLoader::GetInstance()->UnloadAge(); // unload age + nc->ISendCameraReset(false/*leaving age*/); // reset camera + nc->IUnloadRemotePlayers(); // unload other players + + if (NetCommNeedToLoadAvatar()) + am->UnLoadLocalPlayer(); + } + break; + + //==================================================================== + case kNotifyAgeUnloaded: { + // Set "Playing Game" bit to false + nc->SetFlagsBit(plNetClientMgr::kPlayingGame, false); + + // Release AgeSDL object, if any + if (nc->fAgeSDLObjectKey) + nc->GetKey()->Release(nc->fAgeSDLObjectKey); + + // All done leaving age + Complete(true, "Age unloaded"); + } + break; + + DEFAULT_FATAL(nextOp); + } +} + +//============================================================================ +void plNCAgeLeaver::Update () { + ExecNextOp(); +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void NCAgeLeaverCreate ( + plNCAgeLeaver ** pleaver, + bool quitting, + FNCAgeLeaverCallback callback, + void * userState +) { + ASSERT(pleaver); + ASSERT(callback); + + plNCAgeLeaver * leaver; + *pleaver = leaver = NEWZERO(plNCAgeLeaver)( + quitting, + callback, + userState + ); + leaver->Start(); +} + +//============================================================================ +bool NCAgeLeaverMsgReceive ( + plNCAgeLeaver * leaver, + plMessage * msg +) { + ASSERT(leaver); + return leaver->MsgReceive(msg); +} + +//============================================================================ +void NCAgeLeaverUpdate ( + plNCAgeLeaver * leaver +) { + ASSERT(leaver); + leaver->Update(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.h new file mode 100644 index 00000000..a6b18ca0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeLeaver.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETCLIENT_PLNETCLIAGELEAVER_H +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETCLIENT_PLNETCLIAGELEAVER_H + + +#include "HeadSpin.h" +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" + + +/***************************************************************************** +* +* +* +***/ + +class plMessage; +struct plNCAgeLeaver; + +enum ENCAgeLeaverNotify { + kAgeLeaveComplete, // notify --> NCAgeLeaveCompleteNotify *, after callback, leaver is destroyed + kNumAgeLeaverNotifications +}; + +struct NCAgeLeaveCompleteNotify { + bool success; + const char * msg; +}; + + +typedef void (* FNCAgeLeaverCallback)( + plNCAgeLeaver * leaver, + unsigned type, // ENCAgeLeaverNotify + void * notify, + void * userState +); + + +void NCAgeLeaverCreate ( + plNCAgeLeaver ** leaver, + bool quitting, + FNCAgeLeaverCallback callback, + void * userState +); +bool NCAgeLeaverMsgReceive ( // returns true of message was processed + plNCAgeLeaver * leaver, + plMessage * msg +); +void NCAgeLeaverUpdate ( + plNCAgeLeaver * leaver +); + + +#endif // PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETCLIENT_PLNETCLIAGELEAVER_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientCommInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientCommInterface.cpp new file mode 100644 index 00000000..860c9a2f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientCommInterface.cpp @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTimer.h" +#include "plNetClientMgr.h" +#include "plNetLinkingMgr.h" + +#include "../plNetClientRecorder/plNetClientRecorder.h" +#include "../plNetMessage/plNetMessage.h" + +#include "plgDispatch.h" + +// +// Code for interfacing with plNetClientComm library, which in turn +// handles most client-server communication +// + + +int plNetClientCommMsgHandler::HandleMessage( plNetMessage* msg ) +{ + plNetClientMgr* nc=plNetClientMgr::GetInstance(); + int ret = nc->fMsgHandler.ReceiveMsg(msg); + + return ret; +} + +int plNetClientMgr::IInitNetClientComm() +{ + NetCommActivatePostInitErrorHandler(); + + ASSERT(!GetFlagsBit(kNetClientCommInited)); + fNetClientComm.SetDefaultHandler(&fNetClientCommMsgHandler); + + SetFlagsBit(kNetClientCommInited); + + return hsOK; +} + +// +// Cleanup netClientComm related stuff +// +int plNetClientMgr::IDeInitNetClientComm() +{ + SetFlagsBit(kNetClientCommInited, false); + return hsOK; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientCreatable.h new file mode 100644 index 00000000..69fa1afa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientCreatable.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetClientCreatable_inc +#define plNetClientCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plNetClientMgr.h" +REGISTER_CREATABLE( plNetClientMgr ); + +#include "../plNetTransport/plNetTransportMember.h" +REGISTER_CREATABLE( plNetTransportMember ); + +#include "plLinkEffectsMgr.h" +REGISTER_CREATABLE( plLinkEffectsMgr ); + + +#endif // plNetClientCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientGroup.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientGroup.cpp new file mode 100644 index 00000000..ad007669 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientGroup.cpp @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsResMgr.h" +#include "plNetClientGroup.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plResMgr/plPageInfo.h" + +// +// cache room desc string, from fID +// +void plNetClientGroups::ISetGroupDesc(plNetGroupId& grpId) +{ + if (grpId.Room() == plNetGroup::kNetGroupUnknown.Room()) + grpId.SetDesc("Unknown"); + else + if (grpId.Room()== plNetGroup::kNetGroupLocalPlayer.Room()) + grpId.SetDesc("LocalPlayer"); + else + if (grpId.Room()== plNetGroup::kNetGroupRemotePlayer.Room()) + grpId.SetDesc("RemotePlayer"); + else + if (grpId.Room()== plNetGroup::kNetGroupLocalPhysicals.Room()) + grpId.SetDesc("LocalPhysicals"); + else + if (grpId.Room()== plNetGroup::kNetGroupRemotePhysicals.Room()) + grpId.SetDesc("RemotePhysicals"); + else + { + const plPageInfo* pageInfo=plKeyFinder::Instance().GetLocationInfo(grpId.Room()); + grpId.SetDesc(pageInfo->GetPage()); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientGroup.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientGroup.h new file mode 100644 index 00000000..35e17c3a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientGroup.h @@ -0,0 +1,122 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetClientGroup_h +#define plNetClientGroup_h + +#include "hsStlUtils.h" +#include "../../NucleusLib/pnNetCommon/plNetGroup.h" + +// +// represents a collection of net groups. +// abstracted so that we can switch to a different container structure if necessary +// +class plNetClientGroups +{ + friend class plNetClientMgr; +private: + struct OwnedGroup + { + plNetGroupId fGroup; + bool fOwnIt; + + bool operator<(const OwnedGroup& other) const { return other.fGroup fGroups; + + std::set::iterator IFind(const plNetGroupId& grpId) + { + std::set::iterator it=fGroups.begin(); + for( ; it != fGroups.end(); it++) + if ((*it).fGroup==grpId) + break; + return it; + } + + // const version + std::set::const_iterator IFind(const plNetGroupId& grpId) const + { + std::set::const_iterator it=fGroups.begin(); + for( ; it != fGroups.end(); it++) + if ((*it).fGroup==grpId) + break; + return it; + } + + void ISetGroupDesc(plNetGroupId& grpId); +public: + void Reset() + { + ClearGroups(); + SetGroup(plNetGroup::kNetGroupLocalPlayer, true /*ownit*/); + SetGroup(plNetGroup::kNetGroupRemotePlayer, false /*ownit*/); + SetGroup(plNetGroup::kNetGroupLocalPhysicals, true /*ownit*/); + SetGroup(plNetGroup::kNetGroupRemotePhysicals, false /*ownit*/); + } + + void SetGroup(plNetGroupId& grpId, bool ownIt) + { + std::set::iterator it=IFind(grpId); + if (it != fGroups.end()) + (*it).fOwnIt=ownIt; + else + { + ISetGroupDesc(grpId); + fGroups.insert(OwnedGroup(grpId, ownIt)); + } + } + +#if 0 + void RemoveGroup(const plNetGroupId& grpId) + { + std::set::iterator it=IFind(grpId); + if (it != fGroups.end()) + fGroups.erase(it); + } +#else + void ClearGroups() + { + fGroups.clear(); + } +#endif + int IsGroupLocal(const plNetGroupId& grpId) const + { + std::set::const_iterator it=IFind(grpId); + if (it != fGroups.end()) + { + if ((*it).fOwnIt) + return 1; // yes + return 0; // no + } + return -1; // don't know about it + } +}; + + + +#endif // plNetClientGroup_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp new file mode 100644 index 00000000..6c314281 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp @@ -0,0 +1,1550 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plCreatableIndex.h" +#include "hsTimer.h" +#include "hsStream.h" +#include "plNetClientMgr.h" +#include "plgDispatch.h" +#include "plPhysical.h" +#include "plNetClientMsgHandler.h" +#include "plNetLinkingMgr.h" +#include "plNetObjectDebugger.h" + +#include "../pnUtils/pnUtils.h" +#include "../pnProduct/pnProduct.h" +#include "../pnNetCommon/plSynchedObject.h" +#include "../pnNetCommon/plNetServers.h" +#include "../pnNetCommon/plSDLTypes.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plObjInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnMessage/plClientMsg.h" +#include "../pnMessage/plSDLModifierMsg.h" +#include "../pnMessage/plPlayerPageMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnModifier/plModifier.h" +#include "../pnAsyncCore/pnAsyncCore.h" + +#include "../plAgeLoader/plAgeLoader.h" +#include "../plAgeLoader/plResPatcher.h" +#include "../plNetClientRecorder/plNetClientRecorder.h" +#include "../plScene/plSceneNode.h" +#include "../plNetCommon/plNetCommonConstants.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plMessage/plLoadAvatarMsg.h" +#include "../plMessage/plLoadCloneMsg.h" +#include "../plMessage/plSynchEnableMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../plMessage/plLoadAgeMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../plMessage/plCCRMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plNetVoiceListMsg.h" +#include "../plMessage/plNetCommMsgs.h" +#include "../plMessage/plNetClientMgrMsg.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plResMgr/plPageInfo.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../plAgeDescription/plAgeDescription.h" +#include "../plAvatar/plAvatarClothing.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plSurface/plLayerInterface.h" +#include "../plStatusLog/plStatusLog.h" +#include "../plSDL/plSDL.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "../plFile/plEncryptedStream.h" +#include "../plProgressMgr/plProgressMgr.h" +#include "../plVault/plVault.h" + +#include "../../FeatureLib/pfMessage/pfKIMsg.h" // Move this to PubUtil level + +#if 1 // for debugging +#include "plCreatableIndex.h" +#include "../plModifier/plResponderModifier.h" +#include "../plSurface/plLayerAnimation.h" +#endif + +#include +#include + + + +//////////////////////////////////////////////////////////////////// + +plNetClientMgr::PendingLoad::~PendingLoad() +{ + delete fSDRec; +} + +//////////////////////////////////////////////////////////////////// + +// +// CONSTRUCT +// +#pragma warning(disable:4355) // this used in initializer list +plNetClientMgr::plNetClientMgr() : + fLocalPlayerKey(nil), + fMsgHandler(this), + fJoinOrder(0), + // fProgressBar( nil ), + fTaskProgBar( nil ), + fMsgRecorder(nil), + fServerTimeOffset(0), + fTimeSamples(0), + fLastTimeUpdate(0), + fListenListMode(kListenList_Distance), + fAgeSDLObjectKey(nil), + fExperimentalLevel(0), + fOverrideAgeTimeOfDayPercent(-1.f), + fNumInitialSDLStates(0), + fRequiredNumInitialSDLStates(0), + fDisableMsg(nil), + fIsOwner(true) +{ +#ifndef HS_DEBUGGING + // release code will timeout inactive players on servers by default + SetFlagsBit(kAllowTimeOut); +#endif + SetFlagsBit(kAllowAuthTimeOut); + +// fPlayerVault.SetPlayerName("SinglePlayer"); // in a MP game, this will be replaced with a player name like 'Atrus' + fTransport.SetNumChannels(kNetNumChannels); +} +#pragma warning(default:4355) + +// +// DESTRUCT +// +plNetClientMgr::~plNetClientMgr() +{ + plSynchedObject::PushSynchDisabled(true); // disable dirty tracking + + IDeInitNetClientComm(); + + if (this==GetInstance()) + SetInstance(nil); // we're going down boys + + IClearPendingLoads(); +} + +// +// App is exiting +// +void plNetClientMgr::Shutdown() +{ + ISendDirtyState(hsTimer::GetSysSeconds()); + + plNetLinkingMgr::GetInstance()->LeaveAge(true); + + // release existing remote players + int i; + for (i=0;iUnLoadRemotePlayer(k); + } + + // Finally, pump the dispatch system so all the new refs get delivered. + plgDispatch::Dispatch()->MsgQueueProcess(); + + if (fMsgRecorder) + { + delete fMsgRecorder; + fMsgRecorder = nil; + } + for (i = 0; i < fMsgPlayers.size(); i++) + delete fMsgPlayers[i]; + fMsgPlayers.clear(); + + IRemoveCloneRoom(); + + // RATHER BAD DEBUG HACK: Clear the spawn override in armatureMod so there's no memory leak + plArmatureMod::SetSpawnPointOverride( nil ); + + VaultDestroy(); +} + +// +// Clear any pending state, probably joining a new age +// +void plNetClientMgr::IClearPendingLoads() +{ + std::for_each( fPendingLoads.begin(), fPendingLoads.end(), xtl::delete_ptr() ); + fPendingLoads.clear(); +} + +// +// manually add/remove the cloned object room in plClient +// +void plNetClientMgr::IAddCloneRoom() +{ + plSceneNode *cloneRoom = TRACKED_NEW plSceneNode(); + cloneRoom->RegisterAs(kNetClientCloneRoom_KEY); + plKey clientKey=hsgResMgr::ResMgr()->FindKey(kClient_KEY); + plClientRefMsg *pMsg1 = TRACKED_NEW plClientRefMsg(clientKey, plRefMsg::kOnCreate, -1, plClientRefMsg::kManualRoom); + hsgResMgr::ResMgr()->AddViaNotify(cloneRoom->GetKey(), pMsg1, plRefFlags::kPassiveRef); +} + +// +// manually add/remove the cloned object room in plClient +// +void plNetClientMgr::IRemoveCloneRoom() +{ + plKey cloneRoomKey = hsgResMgr::ResMgr()->FindKey(kNetClientCloneRoom_KEY); + plSceneNode *cloneRoom = cloneRoomKey ? plSceneNode::ConvertNoRef(cloneRoomKey->ObjectIsLoaded()) : nil; + if (cloneRoom) + { + cloneRoom->UnRegisterAs(kNetClientCloneRoom_KEY); + cloneRoom = nil; + } +} + +// +// turn null send on/off. Null send does everything except actually send the msg out on the socket +// +void plNetClientMgr::SetNullSend(hsBool on) +{ +} + +// +// returns server time in the form "[m/d/y h:m:s]" +// +const char* plNetClientMgr::GetServerLogTimeAsString(std::string& timestamp) const +{ + const plUnifiedTime st=GetServerTime(); + xtl::format(timestamp, "{%02d/%02d %02d:%02d:%02d}", + st.GetMonth(), st.GetDay(), st.GetHour(), st.GetMinute(), st.GetSecond()); + return timestamp.c_str(); +} + +// +// support for a single leading tab in statusLog strings +// +const char* ProcessTab(const char* fmt) +{ + static std::string s; + if (fmt && *fmt=='\t') + { + s = xtl::format(" %s", fmt); + return s.c_str(); + } + return fmt; +} + +// +// override for plLoggable +// +bool plNetClientMgr::Log(const char* str) const +{ + if (strlen(str)==nil) + return true; + + // prepend raw time + std::string buf2 = xtl::format("%.2f %s", hsTimer::GetSeconds(), ProcessTab(str)); + + if ( GetConsoleOutput() ) + hsStatusMessage(buf2.c_str()); + + // create status log if necessary + if(fStatusLog==nil) + { + fStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog(40, "network.log", + plStatusLog::kTimestamp | plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | + plStatusLog::kServerTimestamp); + fWeCreatedLog = true; + } + + plNetObjectDebugger::GetInstance()->LogMsgIfMatch(buf2.c_str()); + return fStatusLog->AddLine(buf2.c_str()); +} + +// +// Display OS version info for log +// +void plNetClientMgr::IDumpOSVersionInfo() const +{ + DebugMsg("*** OS Info"); + char** versionStrs = DisplaySystemVersion(); + int i=0; + while(versionStrs && versionStrs[i]) + { + DebugMsg(versionStrs[i]); + delete [] versionStrs[i]; + i++; + } + delete [] versionStrs; +} + +// +// initialize net client. returns hsFail on err. +// +int plNetClientMgr::Init() +{ + int ret=hsOK; + hsLogEntry( DebugMsg("*** plNetClientMgr::Init GMT:%s", plUnifiedTime::GetCurrentTime().Print()) ); + + IDumpOSVersionInfo(); + + if (GetFlagsBit(kNullSend)) + SetNullSend(true); + + VaultInitialize(); + + RegisterAs( kNetClientMgr_KEY ); + IAddCloneRoom(); + + fNetGroups.Reset(); + + plgDispatch::Dispatch()->RegisterForExactType(plNetClientMgrMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAgeLoaded2Msg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plCCRPetitionMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plNetVoiceListMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plClientMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + + plgDispatch::Dispatch()->RegisterForExactType(plNetCommAuthMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plNetCommActivePlayerMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plNetCommLinkToAgeMsg::Index(), GetKey()); + + IInitNetClientComm(); + + return ret; +} + +// +// Prepare to send. +// Update p2p transport groups and rcvrs list in some msgs +// Returns channel. +// +int plNetClientMgr::IPrepMsg(plNetMessage* msg) +{ + // pick channel, prepare msg + int channel=kNetChanDefault; + plNetMsgVoice* v=plNetMsgVoice::ConvertNoRef(msg); + if (v) + { // VOICE MSG + channel=kNetChanVoice; + + // compute new transport group (from talkList) if necessary + GetTalkList()->UpdateTransportGroup(this); + + // update receivers list in voice msg based on talk list + v->Receivers()->Clear(); + int i; + for(i=0;iGetNumMembers(); i++) + { + v->Receivers()->AddReceiverPlayerID(GetTalkList()->GetMember(i)->GetPlayerID()); + } + + if (msg->IsBitSet(plNetMessage::kEchoBackToSender)) + v->Receivers()->AddReceiverPlayerID(GetPlayerID()); + } + else if (plNetMsgListenListUpdate::ConvertNoRef(msg)) + { // LISTEN LIST UPDATE MSG + channel=kNetChanListenListUpdate; + + // update transport group from rcvrs list, add p2p mbrs to trasnport group + fTransport.ClearChannelGrp(kNetChanListenListUpdate); + if (IsPeerToPeer()) + { + plNetMsgReceiversListHelper* rl = plNetMsgReceiversListHelper::ConvertNoRef(msg); + hsAssert(rl, "error converting msg to rcvrs list?"); + int i; + for(i=0;iGetNumReceivers();i++) + { + int idx=fTransport.FindMember(rl->GetReceiverPlayerID(i)); + hsAssert(idx!=-1, "error finding transport mbr"); + plNetTransportMember* tm = fTransport.GetMember(idx); + if (tm->IsPeerToPeer()) + fTransport.SubscribeToChannelGrp(tm, kNetChanListenListUpdate); + } + } + } + else if( plNetMsgGameMessageDirected::ConvertNoRef( msg ) ) + { + channel = kNetChanDirectedMsg; + } + return channel; +} + +// +// unload other player since we're leaving the age +// +void plNetClientMgr::IUnloadRemotePlayers() +{ + for(int i=RemotePlayerKeys().size()-1;i>=0;i--) + plAvatarMgr::GetInstance()->UnLoadRemotePlayer(RemotePlayerKeys()[i]); + hsAssert(!RemotePlayerKeys().size(),"Still remote players left when linking out"); +} + +// +// begin linking out sounds and gfx +// +void plNetClientMgr::StartLinkOutFX() +{ + // send msg to trigger link out effect + if (fLocalPlayerKey) + { + plNetLinkingMgr * lm = plNetLinkingMgr::GetInstance(); + + plLinkEffectsTriggerMsg* lem = TRACKED_NEW plLinkEffectsTriggerMsg(); + lem->SetLeavingAge(true); + lem->SetLinkKey(fLocalPlayerKey); + lem->SetBCastFlag(plMessage::kNetPropagate); + lem->SetBCastFlag(plMessage::kNetForce); // Necessary? + lem->AddReceiver(hsgResMgr::ResMgr()->FindKey(plUoid(kLinkEffectsMgr_KEY))); + lem->Send(); + } +} + +// +// beging linking in sounds snd gfx +// +void plNetClientMgr::StartLinkInFX() +{ + if (fLocalPlayerKey) + { + const plArmatureMod *avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + plLinkEffectsTriggerMsg* lem = TRACKED_NEW plLinkEffectsTriggerMsg(); + lem->SetLeavingAge(false); // linking in + lem->SetLinkKey(fLocalPlayerKey); + lem->SetLinkInAnimKey(avMod->GetLinkInAnimKey()); + + // indicate if we are invisible + if (avMod && avMod->IsInStealthMode() && avMod->GetTarget(0)) + { + lem->SetInvisLevel(avMod->GetStealthLevel()); + } + + lem->SetBCastFlag(plMessage::kNetPropagate); + + lem->AddReceiver(hsgResMgr::ResMgr()->FindKey(plUoid(kLinkEffectsMgr_KEY))); + lem->AddReceiver(hsgResMgr::ResMgr()->FindKey(plUoid(kClient_KEY))); + plgDispatch::MsgSend(lem); + } +} + +// +// compute the difference between our clock and the server's in unified time +// +void plNetClientMgr::UpdateServerTimeOffset(plNetMessage* msg) +{ + if ((hsTimer::GetSysSeconds() - fLastTimeUpdate) > 5) + { + fLastTimeUpdate = hsTimer::GetSysSeconds(); + + const plUnifiedTime& msgSentUT = msg->GetTimeSent(); + if (!msgSentUT.AtEpoch()) + { + double diff = plUnifiedTime::GetTimeDifference(msgSentUT, plClientUnifiedTime::GetCurrentTime()); + + if (fServerTimeOffset == 0) + { + fServerTimeOffset = diff; + } + else + { + fServerTimeOffset = fServerTimeOffset + ((diff - fServerTimeOffset) / ++fTimeSamples); + } + + DebugMsg("Setting server time offset to %f", fServerTimeOffset); + } + } +} + +void plNetClientMgr::ResetServerTimeOffset() +{ + fServerTimeOffset = 0; + fTimeSamples = 0; + fLastTimeUpdate = 0; +} + +// +// return the gameservers time +// +plUnifiedTime plNetClientMgr::GetServerTime() const +{ + if ( fServerTimeOffset==0 ) // offline mode or before connecting/calibrating to a server + return plUnifiedTime::GetCurrentTime(); + + plUnifiedTime serverUT; + if (fServerTimeOffset<0) + return plUnifiedTime::GetCurrentTime() - plUnifiedTime(fabs(fServerTimeOffset)); + else + return plUnifiedTime::GetCurrentTime() + plUnifiedTime(fServerTimeOffset); +} + +// +// How old is the age +// +double plNetClientMgr::GetCurrentAgeElapsedSeconds() const +{ + return plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeElapsedSeconds(GetServerTime()); +} + +// +// Get the time in the current age (0-1), taking into account the age's day length +// +float plNetClientMgr::GetCurrentAgeTimeOfDayPercent() const +{ + if (fOverrideAgeTimeOfDayPercent>=0) + return fOverrideAgeTimeOfDayPercent; + + return plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeTimeOfDayPercent(GetServerTime()); +} + +// +// main update fxn for net client code, return hsFail on err +// +int plNetClientMgr::Update(double secs) +{ + int ret=hsOK; // ret code is unchecked, but what the hay + + if (GetFlagsBit(kDisableOnNextUpdate)) { + SetFlagsBit(kDisableOnNextUpdate, false); + IDisableNet(); + } + + // Pump net messages + NetCommUpdate(); + + static double lastUpdateTime=0; + double curTime=hsTimer::GetSeconds(); + if (curTime-lastUpdateTime > 1.f) + { + DebugMsg("NetClient hasn't updated for %f secs", curTime-lastUpdateTime); + } + lastUpdateTime=curTime; + + if (!GetFlagsBit(kDisabled)) + { + fNetClientStats.UpdateAgeStats(); + } + + if (GetFlagsBit(kPlayingGame) ) + { + MaybeSendPendingPagingRoomMsgs(); + ICheckPendingStateLoad(secs); + ISendDirtyState(secs); + IUpdateListenList(secs); + if (GetFlagsBit(plNetClientApp::kShowLists)) + IShowLists(); + if (GetFlagsBit(plNetClientApp::kShowRooms)) + IShowRooms(); + if (GetFlagsBit(plNetClientApp::kShowAvatars)) + IShowAvatars(); + if (GetFlagsBit(plNetClientApp::kShowRelevanceRegions)) + IShowRelevanceRegions(); + } + + // Send dirty nodes, deliver vault callbacks, etc. + VaultUpdate(); + + plNetLinkingMgr::GetInstance()->Update(); + + return ret; +} + +// +// See if there is state recvd from the network that needsto be delivered +// +void plNetClientMgr::ICheckPendingStateLoad(double secs) +{ + if (!fPendingLoads.empty() && GetFlagsBit( kPlayingGame ) || (GetFlagsBit(kLoadingInitialAgeState) && !GetFlagsBit(kNeedInitialAgeStateCount))) + { + PendingLoadsList::iterator it = fPendingLoads.begin(); + while ( it!=fPendingLoads.end() ) + { + PendingLoad * pl = (*it); + + // cache rcvr key + if (!pl->fKey) + { + // check for existence of key in dataset, excluding clone info. + plUoid tmpUoid = pl->fUoid; + tmpUoid.SetClone(0,0); + if ( !hsgResMgr::ResMgr()->FindKey( tmpUoid ) ) + { + // discard the state if object not found in dataset. + char tmp[256]; + hsLogEntry( DebugMsg( "Failed to find object %s in dataset. Discarding pending state '%s'", + tmpUoid.StringIze(tmp), + pl->fSDRec->GetDescriptor()->GetName() ) ); + delete pl; + it = fPendingLoads.erase(it); + continue; + } + // find and cache the real key. + pl->fKey = hsgResMgr::ResMgr()->FindKey(pl->fUoid); + } + + // deliver state if possible + plSynchedObject*so = pl->fKey ? plSynchedObject::ConvertNoRef(pl->fKey->ObjectIsLoaded()) : nil; + if (so && so->IsFinal()) + { + plSDLModifierMsg* sdlMsg = TRACKED_NEW plSDLModifierMsg(pl->fSDRec->GetDescriptor()->GetName(), + plSDLModifierMsg::kRecv); + sdlMsg->SetState( pl->fSDRec, true/*delete pl->fSDRec for us*/ ); + sdlMsg->SetPlayerID( pl->fPlayerID ); + +#ifdef HS_DEBUGGING + if (plNetObjectDebugger::GetInstance()->IsDebugObject(so)) + { + hsLogEntry( DebugMsg( "Delivering SDL state %s:%s", pl->fKey->GetName(), pl->fSDRec->GetDescriptor()->GetName() ) ); +// hsLogEntry(plNetObjectDebugger::GetInstance()->LogMsg(xtl::format("Dispatching SDL state, type %s to object:%s, locallyOwned=%d, st=%.3f rt=%.3f", +// pl->fSDRec->GetDescriptor()->GetName(), pl->fKey->GetName(), +// so->IsLocallyOwned()==plSynchedObject::kYes, secs, hsTimer::GetSeconds()).c_str())); +// hsLogEntry( pl->fSDRec->DumpToObjectDebugger( "Delivering SDL state", false, 0 ) ); + } +#endif + + sdlMsg->Send(pl->fKey); + + pl->fSDRec = nil; // so it won't be deleted in the PendingLoad dtor. + delete pl; + it = fPendingLoads.erase(it); + } + else + { + // report old pending state + double rawSecs = hsTimer::GetSeconds(); + if ((rawSecs - pl->fQueuedTime) > 60.f /*secs*/) + { + char tmp[256], tmp2[256]; + if (pl->fQueueTimeResets >= 5) + { + // if this is our fifth time in here then we've been queued + // for around 5 minutes and its time to go + + WarningMsg( "Pending state '%s' for object [uoid:%s,key:%s] has been queued for about %f secs. Removing...", + pl->fSDRec && pl->fSDRec->GetDescriptor() ? pl->fSDRec->GetDescriptor()->GetName() : "?", + pl->fUoid.StringIze(tmp2), pl->fKey ? pl->fKey->GetUoid().StringIze(tmp) : "?", + ( rawSecs - pl->fQueuedTime ) * pl->fQueueTimeResets); + + delete pl; + it = fPendingLoads.erase(it); + continue; + } + + WarningMsg( "Pending state '%s' for object [uoid:%s,key:%s] has been queued for about %f secs. %s", + pl->fSDRec && pl->fSDRec->GetDescriptor() ? pl->fSDRec->GetDescriptor()->GetName() : "?", + pl->fUoid.StringIze(tmp2), pl->fKey ? pl->fKey->GetUoid().StringIze(tmp) : "?", + ( rawSecs - pl->fQueuedTime ) * pl->fQueueTimeResets, + so ? "(not loaded)" : "(not final)" ); + // reset queue time so we don't spew too many log msgs. + pl->fQueuedTime = rawSecs; + pl->fQueueTimeResets += 1; + } + + it++; + } + } + } +} + +// +// Determine the net group for a given object (based on paging unit) +// +plNetGroupId plNetClientMgr::SelectNetGroup(plSynchedObject* objIn, plKey roomKey) +{ + if( objIn->GetSynchFlags() & plSynchedObject::kHasConstantNetGroup ) + return objIn->GetNetGroup(); + + return plNetGroupId(roomKey->GetUoid().GetLocation(), 0); +} + +// +// Get the net group that an object belongs to, taking into acct +// that objects may derive their net group from another object they belong to. +// +plNetGroupId plNetClientMgr::GetEffectiveNetGroup(const plSynchedObject*& obj) const +{ + plNetGroupId netGroup = plNetGroup::kNetGroupUnknown; + // the object is an interface, so consider the owner instead + const plObjInterface* oInt=plObjInterface::ConvertNoRef(obj); + if (oInt) + { + // the object is an interface, so consider the owner instead + if (oInt->GetOwner()) + obj = oInt->GetOwner(); + } + else + { + const plModifier* mod=plModifier::ConvertNoRef(obj); + if (mod) + { + // the object is a modifier, so consider the first target instead + if (mod->GetNumTargets() && mod->GetTarget(0)) + obj = mod->GetTarget(0); + } + else + { + const plLayerInterface* li = plLayerInterface::ConvertNoRef(obj); + const plClothingOutfit* cl = plClothingOutfit::ConvertNoRef(obj); + if (li || cl) + { + // the object is a layer interface or clothing object + plUoid u = obj->GetKey()->GetUoid(); + if (u.GetLocation().IsReserved()) + { + // layer interface is associated with an avatar, find which one + int cloneID = u.GetCloneID(); + int clonePlayerID = u.GetClonePlayerID(); + for(int i = -1; i < RemotePlayerKeys().size(); i++) + { + plKey pKey = (i==-1) ? GetLocalPlayerKey() : RemotePlayerKeys()[i]; + if (pKey && pKey->GetUoid().GetCloneID()==cloneID && pKey->GetUoid().GetClonePlayerID()==clonePlayerID) + { + obj = plSynchedObject::ConvertNoRef(pKey->ObjectIsLoaded()); + break; + } + } + } + else + { + // layer interface is on a non-avatar object + netGroup = u.GetLocation(); + } + } + else + { + const plPhysical* ph = plPhysical::ConvertNoRef(obj); + if (ph) + { + // the object is a physical + obj = plSynchedObject::ConvertNoRef(ph->GetObjectKey()->ObjectIsLoaded()); + } + } + } + } + + if (obj) + netGroup = obj->GetNetGroup(); + + return netGroup; +} + +// +// If we don't know for sure if something is locallyOwned, check if it' related to the remote or +// local avatar. if not, fallback to join order. +// +int plNetClientMgr::IDeduceLocallyOwned(const plUoid& uoid) const +{ + // check against local/remote avatars + if (fLocalPlayerKey) + if (uoid.GetLocation() == fLocalPlayerKey->GetUoid().GetLocation() && + uoid.GetClonePlayerID() == fLocalPlayerKey->GetUoid().GetClonePlayerID()) + return plSynchedObject::kYes; // this object's location is the same as the local player's + + if (!fRemotePlayerKeys.empty()) + if (uoid.GetLocation() == fRemotePlayerKeys.back()->GetUoid().GetLocation() && + uoid.GetClonePlayerID() == fRemotePlayerKeys.back()->GetUoid().GetClonePlayerID()) + return plSynchedObject::kNo; // this object's location is the same as the currently loading remote player's + + return fIsOwner; +} + + +// +// returns yes/no +// for special cases, like sceneNodes. +// +int plNetClientMgr::IsLocallyOwned(const plUoid& uoid) const +{ + // we're non-networked + if ( GetFlagsBit(kDisabled)) + return plSynchedObject::kYes; + + plNetGroupId netGroup = uoid.GetLocation(); + int answer=GetNetGroups()->IsGroupLocal(netGroup); + + if (answer==-1) + answer = IDeduceLocallyOwned(uoid); + + hsAssert(answer==plSynchedObject::kYes || answer==plSynchedObject::kNo, "invalid answer to IsLocallyOwned()"); + return answer; +} + + +// +// check if this object is in a group which is owned (mastered) by this client. +// returns yes/no +// +int plNetClientMgr::IsLocallyOwned(const plSynchedObject* obj) const +{ + // we're non-networked + if ( GetFlagsBit(kDisabled) ) + return plSynchedObject::kYes; + + if( !obj ) + return plSynchedObject::kNo; + + // object is flagged as local only + if (!obj->IsNetSynched()) + return plSynchedObject::kYes; + + plNetGroupId netGroup = GetEffectiveNetGroup(obj); + int answer=GetNetGroups()->IsGroupLocal(netGroup); + + if (answer==-1) // not sure + answer = IDeduceLocallyOwned(obj->GetKey()->GetUoid()); + + hsAssert(answer==plSynchedObject::kYes || answer==plSynchedObject::kNo, "invalid answer to IsLocallyOwned()"); + return answer; +} + +// +// return localPlayer ptr +// +plSynchedObject* plNetClientMgr::GetLocalPlayer(hsBool forceLoad) const +{ + if (forceLoad) + return fLocalPlayerKey ? plSynchedObject::ConvertNoRef(fLocalPlayerKey->GetObjectPtr()) : nil; + else + return fLocalPlayerKey ? + plSynchedObject::ConvertNoRef(fLocalPlayerKey->ObjectIsLoaded()) : nil; +} + +// +// return a ptr to a remote player +// +plSynchedObject* plNetClientMgr::GetRemotePlayer(int i) const +{ + return fRemotePlayerKeys[i] ? plSynchedObject::ConvertNoRef(fRemotePlayerKeys[i]->ObjectIsLoaded()) : nil; +} + +// +// check if a key si a remote player +// +hsBool plNetClientMgr::IsRemotePlayerKey(const plKey pKey, int *idx) +{ + if (pKey) + { + plKeyVec::iterator result=std::find(fRemotePlayerKeys.begin(), fRemotePlayerKeys.end(), pKey); + hsBool found = result!=fRemotePlayerKeys.end(); + if (idx) + *idx = found ? result-fRemotePlayerKeys.begin() : -1; + return found; + } + return false; +} + +// +// add remote char if not already there +// +void plNetClientMgr::AddRemotePlayerKey(plKey pKey) +{ + hsAssert(pKey, "adding nil remote player key"); + if (!IsRemotePlayerKey(pKey)) + { + fRemotePlayerKeys.push_back(pKey); + hsAssert(pKey!=fLocalPlayerKey, "setting remote player to the local player?"); + } +} + +// +// MsgReceive handler for plasma messages +// +hsBool plNetClientMgr::MsgReceive( plMessage* msg ) +{ + if (plNetLinkingMgr::GetInstance()->MsgReceive( msg )) + return true; + + plEvalMsg* evalMsg = plEvalMsg::ConvertNoRef(msg); + if (evalMsg) + { + IPlaybackMsgs(); + + if ( GetFlagsBit( kNeedToSendAgeLoadedMsg ) ) + { + SetFlagsBit( kNeedToSendAgeLoadedMsg, false ); + plAgeLoader::GetInstance()->NotifyAgeLoaded( true ); + } + + if ( GetFlagsBit( kNeedToSendInitialAgeStateLoadedMsg ) ) + { + SetFlagsBit(kNeedToSendInitialAgeStateLoadedMsg, false); + plInitialAgeStateLoadedMsg* m = TRACKED_NEW plInitialAgeStateLoadedMsg; + m->Send(); + } + + return true; + } + + plGenRefMsg* ref = plGenRefMsg::ConvertNoRef(msg); + if (ref) + { + if( ref->fType == kVaultImage ) + { + // Ignore, we just use it for reffing, don't care about the actual pointer + return true; + } + + hsAssert(ref->fType==kAgeSDLHook, "unknown ref msg context"); + if (ref->GetContext()==plRefMsg::kOnCreate) + { + hsAssert(fAgeSDLObjectKey==nil, "already have a ref to age sdl hook"); + fAgeSDLObjectKey = ref->GetRef()->GetKey(); + char tmp[256]; + DebugMsg("Age SDL hook object created, uoid=%s", fAgeSDLObjectKey->GetUoid().StringIze(tmp)); + } + else + { + fAgeSDLObjectKey=nil; + DebugMsg("Age SDL hook object destroyed"); + } + return true; + } + + if (plNetClientMgrMsg * ncmMsg = plNetClientMgrMsg::ConvertNoRef(msg)) { + if (ncmMsg->type == plNetClientMgrMsg::kCmdDisableNet) { + SetFlagsBit(kDisableOnNextUpdate); + hsRefCnt_SafeUnRef(fDisableMsg); + fDisableMsg = ncmMsg; + fDisableMsg->Ref(); + } + return true; + } + + if (plNetCommAuthMsg * authMsg = plNetCommAuthMsg::ConvertNoRef(msg)) { + if (IS_NET_ERROR(authMsg->result)) { + char str[256]; + StrPrintf(str, arrsize(str), "Authentication failed: %S", NetErrorToString(authMsg->result)); + QueueDisableNet(true, str); + return false; // @@@ TODO: Handle this failure better + } + + return true; + } + + if (plNetCommActivePlayerMsg * activePlrMsg = plNetCommActivePlayerMsg::ConvertNoRef(msg)) { + if (IS_NET_ERROR(activePlrMsg->result)) { + char str[256]; + StrPrintf(str, arrsize(str), "SetActivePlayer failed: %S", NetErrorToString(activePlrMsg->result)); + QueueDisableNet(true, str); + return false; // @@@ TODO: Handle this failure better. + } + + return true; + } + + plPlayerPageMsg *playerPageMsg = plPlayerPageMsg::ConvertNoRef(msg); + if(playerPageMsg) + { + IHandlePlayerPageMsg(playerPageMsg); + return true; // handled + } + + plLoadCloneMsg* pCloneMsg = plLoadCloneMsg::ConvertNoRef(msg); + if(pCloneMsg) + { + ILoadClone(pCloneMsg); + return true; // handled + } + + // player is petitioning a CCR + plCCRPetitionMsg* petMsg=plCCRPetitionMsg::ConvertNoRef(msg); + if (petMsg) + { + ISendCCRPetition(petMsg); + return true; + } + + // a remote CCR is turning invisible + plCCRInvisibleMsg* invisMsg=plCCRInvisibleMsg::ConvertNoRef(msg); + if (invisMsg) + { + LogMsg(kLogDebug, "plNetClientMgr::MsgReceive - Got plCCRInvisibleMsg"); + MakeCCRInvisible(invisMsg->fAvKey, invisMsg->fInvisLevel); + return true; + } + + plCCRBanLinkingMsg* banLinking = plCCRBanLinkingMsg::ConvertNoRef(msg); + if (banLinking) + { + DebugMsg("Setting BanLinking to %d", banLinking->fBan); + SetFlagsBit(kBanLinking, banLinking->fBan); + return true; + } + + plCCRSilencePlayerMsg* silence = plCCRSilencePlayerMsg::ConvertNoRef(msg); + if (silence) + { + DebugMsg("Setting Silence to %d", silence->fSilence); + SetFlagsBit(kSilencePlayer, silence->fSilence); + return true; + } + + + plInitialAgeStateLoadedMsg *stateMsg = plInitialAgeStateLoadedMsg::ConvertNoRef( msg ); + if( stateMsg != nil ) + { + // done receiving the initial state of the age from the server + plNetObjectDebugger::GetInstance()->LogMsg("OnServerInitComplete"); + + // delete fProgressBar; + // fProgressBar=nil; + + SetFlagsBit(kLoadingInitialAgeState, false); + StartLinkInFX(); + } + + plNetVoiceListMsg* voxList = plNetVoiceListMsg::ConvertNoRef(msg); + if (voxList) + { + IHandleNetVoiceListMsg(voxList); + return true; + } + + plSynchEnableMsg* synchEnable = plSynchEnableMsg::ConvertNoRef(msg); + if (synchEnable) + { + if (synchEnable->fPush) + { + plSynchedObject::PushSynchDisabled(!synchEnable->fEnable); + } + else + { + plSynchedObject::PopSynchDisabled(); + } + return true; + } + + plClientMsg* clientMsg = plClientMsg::ConvertNoRef(msg); + if (clientMsg && clientMsg->GetClientMsgFlag()==plClientMsg::kInitComplete) + { + // add 1 debug object for age sdl + if (plNetObjectDebugger::GetInstance()) + { + plNetObjectDebugger::GetInstance()->RemoveDebugObject("AgeSDLHook"); + plNetObjectDebugger::GetInstance()->AddDebugObject("AgeSDLHook"); + } + + // if we're linking to startup we don't need (or want) a player set + char ageName[kMaxAgeNameLength]; + StrCopy(ageName, NetCommGetStartupAge()->ageDatasetName, arrsize(ageName)); + if (!StrLen(ageName)) + StrCopy(ageName, "StartUp", arrsize(ageName)); + if (0 == StrCmpI(ageName, "StartUp")) + NetCommSetActivePlayer(0, nil); + + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename(NetCommGetStartupAge()->ageDatasetName); + link.SetLinkingRules(plNetCommon::LinkingRules::kOriginalBook); + plNetLinkingMgr::GetInstance()->LinkToAge(&link); + + return true; + } + + return plNetClientApp::MsgReceive(msg); +} + +void plNetClientMgr::IncNumInitialSDLStates() +{ + fNumInitialSDLStates++; + DebugMsg( "Received %d initial SDL states", fNumInitialSDLStates ); + if ( GetFlagsBit( plNetClientApp::kNeedInitialAgeStateCount ) ) + { + DebugMsg( "Need initial SDL state count" ); + return; + } + + if ( GetNumInitialSDLStates()>=GetRequiredNumInitialSDLStates() ) + { + ICheckPendingStateLoad(hsTimer::GetSysSeconds()); + NotifyRcvdAllSDLStates(); + } +} + +void plNetClientMgr::NotifyRcvdAllSDLStates() { + DebugMsg( "Got all initial SDL states" ); + plNetClientMgrMsg * msg = TRACKED_NEW plNetClientMgrMsg(); + msg->type = plNetClientMgrMsg::kNotifyRcvdAllSDLStates; + msg->SetBCastFlag(plMessage::kBCastByType); + msg->Send(); +} + +// +// return true if we should send this msg +// +bool plNetClientMgr::CanSendMsg(plNetMessage * msg) +{ + // TEMP + if (GetFlagsBit(kSilencePlayer)) + { + if (plNetMsgVoice::ConvertNoRef(msg)) + return false; + + plNetMsgGameMessage* gm=plNetMsgGameMessage::ConvertNoRef(msg); + if (gm && gm->StreamInfo()->GetStreamType()==pfKIMsg::Index()) + { + hsReadOnlyStream stream(gm->StreamInfo()->GetStreamLen(), gm->StreamInfo()->GetStreamBuf()); + pfKIMsg* kiMsg = pfKIMsg::ConvertNoRef(hsgResMgr::ResMgr()->ReadCreatable(&stream)); + if (kiMsg && kiMsg->GetCommand()==pfKIMsg::kHACKChatMsg) + { + delete kiMsg; + return false; + } + delete kiMsg; + } + } + + return true; +} + +// +// Return the net client (account) name of the player whose avatar +// key is provided. If avKey is nil, returns local client name. +// +const char* plNetClientMgr::GetPlayerName(const plKey avKey) const +{ + // local case + if (!avKey || avKey==GetLocalPlayerKey()) + return NetCommGetPlayer()->playerNameAnsi; + + plNetTransportMember* mbr=TransportMgr().GetMember(TransportMgr().FindMember(avKey)); + return mbr ? mbr->GetPlayerName() : nil; +} + +const char * plNetClientMgr::GetPlayerNameById (unsigned playerId) const { + // local case + if (NetCommGetPlayer()->playerInt == playerId) + return NetCommGetPlayer()->playerNameAnsi; + + plNetTransportMember * mbr = TransportMgr().GetMember(TransportMgr().FindMember(playerId)); + return mbr ? mbr->GetPlayerName() : nil; +} + +unsigned plNetClientMgr::GetPlayerIdByName (const char name[]) const { + // local case + if (0 == StrCmp(name, NetCommGetPlayer()->playerNameAnsi, (unsigned)-1)) + NetCommGetPlayer()->playerInt; + + unsigned n = TransportMgr().GetNumMembers(); + for (unsigned i = 0; i < n; ++i) + if (plNetTransportMember * member = TransportMgr().GetMember(i)) + if (0 == StrCmp(name, member->GetPlayerName(), (unsigned)-1)) + return member->GetPlayerID(); + return 0; +} + +UInt32 plNetClientMgr::GetPlayerID() const +{ + return NetCommGetPlayer()->playerInt; +} + +// +// return true if the age is the set of local ages +// +static const char* gLocalAges[]={"GUI", "AvatarCustomization", ""}; +static bool IsLocalAge(const char* ageName) +{ + int i=0; + while(strlen(gLocalAges[i])) + { + if (!stricmp(gLocalAges[i], ageName)) + return true; + i++; + } + return false; +} + +// +// is the object in a local age? +// +bool plNetClientMgr::ObjectInLocalAge(const plSynchedObject* obj) const +{ + plLocation loc = obj->GetKey()->GetUoid().GetLocation(); + const plPageInfo* pageInfo = plKeyFinder::Instance().GetLocationInfo(loc); + bool ret= IsLocalAge(pageInfo->GetAge()); + return ret; +} + +// +// the next age we are going to +// +const char* plNetClientMgr::GetNextAgeFilename() +{ + // set when we start linking to an age. + plNetLinkingMgr * lm = plNetLinkingMgr::GetInstance(); + return lm->GetAgeLink()->GetAgeInfo()->GetAgeFilename(); +} + +// +// a remote ccr is turning [in]visible +// +void plNetClientMgr::MakeCCRInvisible(plKey avKey, int level) +{ + if (!avKey) + { + ErrorMsg("Failed to make remote CCR Invisible, nil avatar key"); + return; + } + + if (level<0) + { + ErrorMsg("Failed to make remote CCR Invisible, negative level"); + return; + } + + if (!avKey->ObjectIsLoaded() || !avKey->ObjectIsLoaded()->IsFinal()) + { + hsAssert(false, "avatar not loaded or final"); + return; + } + + plAvatarStealthModeMsg *msg = TRACKED_NEW plAvatarStealthModeMsg(); + msg->SetSender(avKey); + msg->fLevel = level; + + if (GetCCRLevel()fMode = plAvatarStealthModeMsg::kStealthCloaked; + else + { + // he's visible to me + if (AmCCR() && level > 0) + msg->fMode = plAvatarStealthModeMsg::kStealthCloakedButSeen; // draw as semi-invis + else + msg->fMode = plAvatarStealthModeMsg::kStealthVisible; + } + + DebugMsg("Handled MakeCCRInvisible - sending stealth msg");; + + // This fxn is called when avatar SDL state is received from the server, + // so this msg will inherit the 'non-local' status of the parent sdl msg. + // That means that the avatar linkSound won't receive it, since the linkSound + // is set as localOnly. + // So, terminate the remote cascade and start a new (local) cascade. + msg->SetBCastFlag(plMessage::kNetStartCascade); + + msg->Send(); +} + +void plNetClientMgr::QueueDisableNet (bool showDlg, const char str[]) { + + plNetClientMgrMsg * msg = NEWZERO(plNetClientMgrMsg); + msg->type = plNetClientMgrMsg::kCmdDisableNet; + msg->yes = showDlg; + if (str) + StrCopy(msg->str, str, arrsize(msg->str)); + +#if 0 + msg->Send(GetKey()); +#else + msg->Ref(); + fDisableMsg = msg; + IDisableNet(); +#endif; +} + +void plNetClientMgr::IDisableNet () { + ASSERT(fDisableMsg); + + if (!GetFlagsBit(kDisabled)) { + SetFlagsBit(kDisabled); + // cause subsequent net operations to fail immediately, but don't block + // waiting for net subsystem to shutdown (we'll do that later) + NetCommEnableNet(false, false); + + // display a msg to the player + if ( fDisableMsg->yes ) + { + if (!GetFlagsBit(plNetClientApp::kPlayingGame)) + { + // KI may not be loaded + char title[256]; + StrPrintf(title, arrsize(title), "%S Error", ProductCoreName()); + hsMessageBox(fDisableMsg->str, title, hsMessageBoxNormal, hsMessageBoxIconError ); + plClientMsg *quitMsg = NEW(plClientMsg)(plClientMsg::kQuit); + quitMsg->Send(hsgResMgr::ResMgr()->FindKey(kClient_KEY)); + } + else + { + pfKIMsg *msg = TRACKED_NEW pfKIMsg(pfKIMsg::kKIOKDialog); + msg->SetString(fDisableMsg->str); + msg->Send(); + } + } + } + + hsRefCnt_SafeUnRef(fDisableMsg); + fDisableMsg = nil; +} + +bool plNetClientMgr::IHandlePlayerPageMsg(plPlayerPageMsg *playerMsg) +{ + bool result = false; + plKey playerKey = playerMsg->fPlayer; + int idx; + + if(playerMsg->fUnload) + { + if (GetLocalPlayerKey() == playerKey) + { + fLocalPlayerKey = nil; + DebugMsg("Net: Unloading local player %s", playerKey->GetName()); + + // notify server - NOTE: he might not still be around to get this... + plNetMsgPlayerPage npp (playerKey->GetUoid(), playerMsg->fUnload); + npp.SetNetProtocol(kNetProtocolCli2Game); + SendMsg(&npp); + } + else if (IsRemotePlayerKey(playerKey, &idx)) + { + fRemotePlayerKeys.erase(fRemotePlayerKeys.begin()+idx); // remove key from list + DebugMsg("Net: Unloading remote player %s", playerKey->GetName()); + } + } + else + { + plSceneObject *playerSO = plSceneObject::ConvertNoRef(playerKey->ObjectIsLoaded()); + if (!playerSO) + { + hsStatusMessageF("Ignoring player page message for non-existant player."); + } + else + if(playerMsg->fPlayer) + { + if (playerMsg->fLocallyOriginated) + { + hsAssert(!GetLocalPlayerKey() || + GetLocalPlayerKey() == playerKey, + "Different local player already loaded"); + + hsLogEntry(DebugMsg("Adding LOCAL player %s\n", playerKey->GetName())); + playerSO->SetNetGroupConstant(plNetGroup::kNetGroupLocalPlayer); + + // don't save avatar state permanently on server + playerSO->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile); + const plCoordinateInterface* co = playerSO->GetCoordinateInterface(); + if (co) + { + int i; + for(i=0;iGetNumChildren();i++) + { + if (co->GetChild(i) && co->GetChild(i)->GetOwner()) + const_cast(co->GetChild(i)->GetOwner())->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile); + } + } + + // notify server + plNetMsgPlayerPage npp (playerKey->GetUoid(), playerMsg->fUnload); + npp.SetNetProtocol(kNetProtocolCli2Game); + SendMsg(&npp); + } + else + { + hsLogEntry(DebugMsg("Adding REMOTE player %s\n", playerKey->GetName())); + playerSO->SetNetGroupConstant(plNetGroup::kNetGroupRemotePlayer); + idx=fTransport.FindMember(playerMsg->fClientID); + if( idx != -1 ) + { + hsAssert(playerKey, "NIL KEY?"); + hsAssert(playerKey->GetName(), "UNNAMED KEY"); + fTransport.GetMember(idx)->SetAvatarKey(playerKey); + } + else + { + hsLogEntry(DebugMsg("Ignoring player page msg (player not found in member list) : %s\n", playerKey->GetName())); + } + } + + hsAssert(IFindModifier(playerSO, CLASS_INDEX_SCOPED(plAvatarSDLModifier)), "avatar missing avatar SDL modifier"); + hsAssert(IFindModifier(playerSO, CLASS_INDEX_SCOPED(plClothingSDLModifier)), "avatar missing clothing SDL modifier"); + hsAssert(IFindModifier(playerSO, CLASS_INDEX_SCOPED(plAGMasterSDLModifier)), "avatar missing AGMaster SDL modifier"); + result = true; + } + } + return result; +} + +// for debugging purposes +bool plNetClientMgr::IFindModifier(plSynchedObject* obj, Int16 classIdx) +{ + plLayerAnimation* layer = plLayerAnimation::ConvertNoRef(obj); + if (layer) + return (layer->GetSDLModifier() != nil); + + plResponderModifier* resp = plResponderModifier::ConvertNoRef(obj); + if (resp) + return (resp->GetSDLModifier() != nil); + + int cnt=0; + plSceneObject* sceneObj=plSceneObject::ConvertNoRef(obj); + if (sceneObj) + { + int i; + for(i=0; iGetNumModifiers(); i++) + if (sceneObj->GetModifier(i) && sceneObj->GetModifier(i)->ClassIndex()==classIdx) + cnt++; + } + + hsAssert(cnt<2, xtl::format("Object %s has multiple SDL modifiers of the same kind (%s)?", + obj->GetKeyName(), plFactory::GetNameOfClass(classIdx)).c_str()); + return cnt==0 ? false : true; +} + +plUoid plNetClientMgr::GetAgeSDLObjectUoid(const char* ageName) const +{ + hsAssert(ageName, "nil ageName"); + + // if age sdl hook is loaded + if (fAgeSDLObjectKey) + return fAgeSDLObjectKey->GetUoid(); + + // if age is loaded + plLocation loc = plKeyFinder::Instance().FindLocation(ageName,plAgeDescription::GetCommonPage(plAgeDescription::kGlobal)); + if (!loc.IsValid()) + { + // check current age des + if (plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeName() && + !strcmp(plAgeLoader::GetInstance()->GetCurrAgeDesc().GetAgeName(), ageName)) + loc=plAgeLoader::GetInstance()->GetCurrAgeDesc().CalcPageLocation("BuiltIn"); + + if (!loc.IsValid()) + { + // try to load age desc + hsStream* stream=plAgeLoader::GetAgeDescFileStream(ageName); + if (stream) + { + plAgeDescription ad; + ad.Read(stream); + loc=ad.CalcPageLocation("BuiltIn"); + stream->Close(); + } + delete stream; + } + } + + return plUoid(loc, plSceneObject::Index(), plSDL::kAgeSDLObjectName); +} + +// +// Add a state update to the pending queue +// +void plNetClientMgr::AddPendingLoad(PendingLoad *pl) +{ + pl->fQueuedTime = hsTimer::GetSeconds(); // timestamp + + // find corresponding key + pl->fKey = hsgResMgr::ResMgr()->FindKey(pl->fUoid); + + // check for age SDL state + char tmp[256]; + if (pl->fUoid.GetObjectName() && !strcmp(pl->fUoid.GetObjectName(), plSDL::kAgeSDLObjectName)) + { + DebugMsg("Recv SDL state for age hook object, uoid=%s", pl->fUoid.StringIze(tmp)); + if (!pl->fKey) + WarningMsg("Can't find age hook object, nil key!"); + else + DebugMsg("Found age hook object"); + } + + // check if object is ready + if (pl->fKey) + { + if (!pl->fKey->ObjectIsLoaded()) + { + WarningMsg("Object %s not loaded, withholding SDL state", + pl->fKey->GetUoid().StringIze(tmp)); + } + else if (!pl->fKey->ObjectIsLoaded()->IsFinal()) + { + WarningMsg("Object %s is not FINAL, withholding SDL state", + pl->fKey->GetUoid().StringIze(tmp)); + } + } + + // add entry + fPendingLoads.push_back(pl); +} + +void plNetClientMgr::AddPendingPagingRoomMsg( plNetMsgPagingRoom * msg ) +{ + fPendingPagingRoomMsgs.push_back( msg ); +} + +void plNetClientMgr::MaybeSendPendingPagingRoomMsgs() +{ + if ( GetFlagsBit( kPlayingGame ) ) + SendPendingPagingRoomMsgs(); +} + +void plNetClientMgr::SendPendingPagingRoomMsgs() +{ + for ( int i=0; i. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_NET_CLIENT_inc +#define PL_NET_CLIENT_inc + +#include "hsConfig.h" +#include "hsUtils.h" +#include "hsStlUtils.h" + +#include "plNetClientGroup.h" +#include "plNetVoiceList.h" +#include "plNetClientMsgHandler.h" +#include "plNetClientStats.h" // STATS Counters + +#include "../pnNetCommon/plNetApp.h" + +#include "../plNetTransport/plNetTransport.h" +#include "../plEncryption/plChecksum.h" +#include "../plNetCommon/plNetServerSessionInfo.h" +#include "../plNetClientComm/plNetClientComm.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#pragma warning(disable: 4284) + +//////////////////////////////////////////////////////////////////// + +class plUoid; +class hsStream; +class plKey; +class plNetMessage; +class plSynchedObject; +struct DistSqInfo; +class plStatusLog; +class plOperationProgress; +class plIDataServer; +class plPlate; +class plLoadCloneMsg; +class plPlayerPageMsg; +class plNetClientRecorder; +class plVaultPlayerNode; +class plVaultAgeNode; +class plNetVoiceListMsg; +class plStateDataRecord; +class plCCRPetitionMsg; +class plNetMsgPagingRoom; + + +struct plNetClientCommMsgHandler : plNetClientComm::MsgHandler { + int HandleMessage( plNetMessage* msg ); +}; + +class plNetClientMgr : public plNetClientApp +{ +private: + typedef std::vector plKeyVec; +public: + + enum NetChannels + { + kNetChanDefault, + kNetChanVoice, + kNetChanListenListUpdate, + kNetChanDirectedMsg, + kNetNumChannels + }; + + enum DirectedSendFlags + { + kInterAgeMsg = 0x1 + }; + + enum ListenListMode + { + kListenList_Distance = 0, + kListenList_Forced, + kListenList_End, + }; + + enum RefContext + { + kVaultImage = 0, + kAgeSDLHook = 1 + }; + + struct PendingLoad + { + // must be set by user + plStateDataRecord* fSDRec; // the sdl data record + plUoid fUoid; // the object it's meant for + UInt32 fPlayerID; // the player that originally sent the state + + // set by NetClient + plKey fKey; // the key of the object it's meant for + double fQueuedTime; + int fQueueTimeResets; + + PendingLoad() : fSDRec(nil),fPlayerID(0),fKey(nil),fQueuedTime(0.0),fQueueTimeResets(0) {} + ~PendingLoad(); + }; + +private: + // plOperationProgress *fProgressBar; + plOperationProgress *fTaskProgBar; + + typedef std::list PendingLoadsList; + PendingLoadsList fPendingLoads; + + // pending room page msgs + std::vector fPendingPagingRoomMsgs; + + plNetTransport fTransport; + + // groups of objects in the game. Each group is mastered by a single client. + plNetClientGroups fNetGroups; + + // cached char info + plKey fLocalPlayerKey; + plKeyVec fRemotePlayerKeys; + // plKeyVec fNPCKeys; + + class plNetClientMgrMsg * fDisableMsg; + + // ini info + std::string fIniAccountName; + std::string fIniAccountPass; + std::string fIniAuthServer; + UInt32 fIniPlayerID; // the player we want to load from vault. + std::string fSPDesiredPlayerName; // SP: the player we want to load from vault. + + // server info + double fServerTimeOffset; // diff between our unified time and server's unified time + UInt32 fTimeSamples; + double fLastTimeUpdate; + + UInt8 fJoinOrder; // returned by the server + + // voice lists + int fListenListMode; // how we are generating our listen list + plNetListenList fListenList; // other players I'm listening to + plNetTalkList fTalkList; // other players I'm talking to + + plNetClientMsgHandler fMsgHandler; + + // recorder support + plNetClientRecorder* fMsgRecorder; + std::vector fMsgPlayers; + + plKey fAgeSDLObjectKey; + UInt8 fExperimentalLevel; + plNetClientStats fNetClientStats; + UInt8 fPingServerType; // non-zero if we're pinging someone + float fOverrideAgeTimeOfDayPercent; // for console debugging + + int fNumInitialSDLStates; + int fRequiredNumInitialSDLStates; + + // simplification of object ownership...one player owns all non-physical objects in the world + // physical objects are owned by whoever touched them most recently (or the "owner" if nobody + // has touched it yet) + bool fIsOwner; + + // + void ICheckPendingStateLoad(double secs); + int IDeduceLocallyOwned(const plUoid& loc) const; + bool IHandlePlayerPageMsg(plPlayerPageMsg *playerMsg); // *** + + void IShowLists(); + void IShowRooms(); + void IShowAvatars(); + void IShowRelevanceRegions(); + + int ISendDirtyState(double secs); + int ISendMembersListRequest(); + int ISendRoomsReset(); + void ISendCCRPetition(plCCRPetitionMsg* petMsg); + void ISendCameraReset(hsBool bEnteringAge); + + hsBool IUpdateListenList(double secs); + void IHandleNetVoiceListMsg(plNetVoiceListMsg* msg); + hsBool IApplyNewListenList(std::vector& newListenList, hsBool forceSynch); + int IPrepMsg(plNetMessage* msg); + void IPlayerChangeAge(hsBool exiting, Int32 spawnPt); + + void IAddCloneRoom(); + void IRemoveCloneRoom(); + + void IUnloadRemotePlayers(); + + plKey ILoadClone(plLoadCloneMsg *cloneMsg); + + bool IFindModifier(plSynchedObject* obj, Int16 classIdx); + void IClearPendingLoads(); + + // recorder + bool IIsRecordableMsg(plNetMessage* msg); + void IPlaybackMsgs(); + + void IRequestAgeState(); + + void IDumpOSVersionInfo() const; + + int ISendGameMessage(plMessage* msg); + void IDisableNet (); + +public: + plNetClientMgr(); + ~plNetClientMgr(); + + CLASSNAME_REGISTER( plNetClientMgr ); + GETINTERFACE_ANY( plNetClientMgr, plNetClientApp ); + + static plNetClientMgr* GetInstance() { return plNetClientMgr::ConvertNoRef(fInstance); } + + void StartLinkOutFX(); + void StartLinkInFX(); + + hsBool MsgReceive(plMessage* msg); + void Shutdown(); + int Init(); + + void QueueDisableNet (bool showDlg, const char msg[]); + + int SendMsg(plNetMessage* msg); + int Update(double secs); + int IsLocallyOwned(const plSynchedObject* obj) const; // returns yes/no/maybe + int IsLocallyOwned(const plUoid&) const; // for special cases, like sceneNodes. returns yes/no/maybe + plNetGroupId GetEffectiveNetGroup(const plSynchedObject*& obj) const; + plNetGroupId SelectNetGroup(plSynchedObject* objIn, plKey groupKey); + + void SendLocalPlayerAvatarCustomizations(); + void SendApplyAvatarCustomizationsMsg(const plKey msgReceiver, bool netPropagate=true, bool localPropagate=true); + + // plLoggable + bool Log(const char* str) const; + + // setters + void SetIniAuthServer(const char * value) { fIniAuthServer=value;} + void SetIniAccountName(const char * value) { fIniAccountName=value;} + void SetIniAccountPass(const char * value) { fIniAccountPass=value;} + void SetIniPlayerID(UInt32 value) { fIniPlayerID=value;} + + void SetSPDesiredPlayerName( const char * value ) { fSPDesiredPlayerName=value;} + const char * GetSPDesiredPlayerName() const { return fSPDesiredPlayerName.c_str(); } + + void SetLocalPlayerKey(plKey l, hsBool pageOut=false); + void SetNullSend(hsBool on); // turn null send on/off + void SetPingServer(UInt8 serverType) { fPingServerType = serverType; } + + // getters + UInt32 GetPlayerID( void ) const; + const char * GetPlayerName( const plKey avKey=nil ) const; + const char * GetPlayerNameById (unsigned playerId) const; + unsigned GetPlayerIdByName(const char name[]) const; + + UInt8 GetJoinOrder() const { return fJoinOrder; } // only valid at join time + + plKey GetLocalPlayerKey() const { return fLocalPlayerKey; } + plSynchedObject* GetLocalPlayer(hsBool forceLoad=false) const; + + hsBool IsPeerToPeer() const { return false; } + hsBool IsConnected() const { return true; } + + void IncNumInitialSDLStates(); + void ResetNumInitialSDLStates() { fNumInitialSDLStates=0; } + int GetNumInitialSDLStates() const { return fNumInitialSDLStates; } + void SetRequiredNumInitialSDLStates( int v ) { fRequiredNumInitialSDLStates=v; } + int GetRequiredNumInitialSDLStates() const { return fRequiredNumInitialSDLStates; } + + // Linking progress + void StartTaskProgress( const char *msg, int numSteps ); + void IncTaskProgress( const char *msg ); + + // avatar vault actions + int UploadPlayerVault(UInt32 vaultFlags); + + // remote players + const std::vector& RemotePlayerKeys() const { return fRemotePlayerKeys; } + plSynchedObject* GetRemotePlayer(int i) const; + void AddRemotePlayerKey(plKey p); + hsBool IsRemotePlayerKey(const plKey p, int* idx=nil); + bool IsAPlayerKey(const plKey pKey) { return (pKey==GetLocalPlayerKey() || IsRemotePlayerKey(pKey)); } + + void SetConsoleOutput( bool b ) { SetFlagsBit(kConsoleOutput, b); } + bool GetConsoleOutput() const { return GetFlagsBit(kConsoleOutput); } + + // Net groups + const plNetClientGroups* GetNetGroups() const { return &fNetGroups; } + plNetClientGroups* GetNetGroups() { return &fNetGroups; } + + // Voice Lists + plNetListenList* GetListenList() { return &fListenList; } + plNetTalkList* GetTalkList() { return &fTalkList; } + void SetListenListMode (int i); + void SynchTalkList(); + int GetListenListMode() { return fListenListMode; } + + // network activity-generated events, passed to current task + bool CanSendMsg(plNetMessage * msg); + + const plNetTransport& TransportMgr() const { return fTransport; } + plNetTransport& TransportMgr() { return fTransport; } + + bool ObjectInLocalAge(const plSynchedObject* obj) const; + + // time converters + plUnifiedTime GetServerTime() const; + const char* GetServerLogTimeAsString(std::string& ts) const; + double GetCurrentAgeElapsedSeconds() const; + float GetCurrentAgeTimeOfDayPercent() const; + + bool RecordMsgs(const char* recType, const char* recName); + bool PlaybackMsgs(const char* recName); + + void MakeCCRInvisible(plKey avKey, int level); + bool CCRVaultConnected() const { return GetFlagsBit(kCCRVaultConnected); } + + UInt8 GetExperimentalLevel() const { return fExperimentalLevel; } + + void AddPendingLoad(PendingLoad *pl); + const plKey& GetAgeSDLObjectKey() const { return fAgeSDLObjectKey; } + plUoid GetAgeSDLObjectUoid(const char* ageName) const; + plNetClientComm& GetNetClientComm() { return fNetClientComm; } + const char* plNetClientMgr::GetNextAgeFilename(); + plNetClientStats& GetNetClientStats() { return fNetClientStats; } + void SetOverrideAgeTimeOfDayPercent(float f) { fOverrideAgeTimeOfDayPercent=f; } + + void AddPendingPagingRoomMsg( plNetMsgPagingRoom * msg ); + void MaybeSendPendingPagingRoomMsgs(); + void SendPendingPagingRoomMsgs(); + void ClearPendingPagingRoomMsgs(); + + void NotifyRcvdAllSDLStates(); + + plOperationProgress* GetTaskProgBar() { return fTaskProgBar; } + + bool DebugMsgV(const char* fmt, va_list args) const; + bool ErrorMsgV(const char* fmt, va_list args) const; + bool WarningMsgV(const char* fmt, va_list args) const; + bool AppMsgV(const char* fmt, va_list args) const; + + bool IsObjectOwner(); + void SetObjectOwner(bool own); + + void StoreSDLState(const plStateDataRecord* sdRec, const plUoid& uoid, UInt32 sendFlags, UInt32 writeOptions); + + void UpdateServerTimeOffset(plNetMessage* msg); + void ResetServerTimeOffset(); + +private: + plNetClientComm fNetClientComm; + plNetClientCommMsgHandler fNetClientCommMsgHandler; + + int IInitNetClientComm(); + int IDeInitNetClientComm(); + void INetClientCommOpStarted(UInt32 context); + void INetClientCommOpComplete(UInt32 context, int resultCode); + + friend struct plNCAgeJoiner; + friend struct plNCAgeLeaver; + friend class plNetDniInfoSource; + friend class plNetTalkList; + friend class plNetClientMsgHandler; + friend struct plNetClientCommMsgHandler; +}; + +#define plCheckNetMgrResult_VoidReturn(r,s) if (hsFailed(r)) { ErrorMsg(s); hsAssert(false,s); return; } +// returns int +#define plCheckNetMgrResult_ValReturn(r,s) if (hsFailed(r)) { ErrorMsg(s); hsAssert(false,s); return r; } +// returns bool +#define plCheckNetMgrResult_BoolReturn(r,s) if (hsFailed(r)) { ErrorMsg(s); hsAssert(false,s); return false; } + +#endif // PL_NET_CLIENT_inc + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrLoad.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrLoad.cpp new file mode 100644 index 00000000..1d29c25c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrLoad.cpp @@ -0,0 +1,171 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#if 1 // for debugging +#include "plCreatableIndex.h" +#include "../plModifier/plResponderModifier.h" +#include "../plSurface/plLayerAnimation.h" +#endif + +#include "hsStream.h" +#include "plNetClientMgr.h" +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "hsTimer.h" + +#include "../plNetMessage/plNetMessage.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnModifier/plModifier.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plClientMsg.h" +#include "../pnMessage/plNodeChangeMsg.h" +#include "../pnMessage/plPlayerPageMsg.h" + +#include "../plScene/plSceneNode.h" +#include "../plScene/plRelevanceMgr.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plAgeDescription/plAgeDescription.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plSDL/plSDL.h" + +/// TEMP HACK TO LOAD CONSOLE INIT FILES ON AGE LOAD +#include "../plMessage/plConsoleMsg.h" +#include "../plMessage/plLoadAvatarMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" + +#include "../plAgeLoader/plResPatcher.h" +#include "../plProgressMgr/plProgressMgr.h" +#include "../plResMgr/plRegistryHelpers.h" +#include "../plResMgr/plRegistryNode.h" +#include "../plResMgr/plResManager.h" + +#include "process.h" // for getpid() + +extern hsBool gDataServerLocal; + + +// Load Player object +// a clone will be created if cloneNum>0 +// returns the playerKey if successful. +// +// Don't call this directly. Send a clone message to the NetClientManager instead. +// Load an object, optionally cloning if necessary. +plKey plNetClientMgr::ILoadClone(plLoadCloneMsg *pCloneMsg) +{ + plKey cloneKey = pCloneMsg->GetCloneKey(); + + if(pCloneMsg->GetIsLoading()) + { + if (cloneKey->ObjectIsLoaded()) + { + char tmp[256]; + DebugMsg("ILoadClone: object %s is already loaded, ignoring", cloneKey->GetUoid().StringIze(tmp)); + return cloneKey; + } + + // check if local or remote player before loading + plLoadAvatarMsg* loadAvMsg=plLoadAvatarMsg::ConvertNoRef(pCloneMsg); + if (loadAvMsg && loadAvMsg->GetIsPlayer()) + { + bool originating = ( pCloneMsg->GetOriginatingPlayerID() == this->GetPlayerID() ); + if (originating) + fLocalPlayerKey = cloneKey; + else + AddRemotePlayerKey(cloneKey); + } + + plKey cloneNodeKey = hsgResMgr::ResMgr()->FindKey(kNetClientCloneRoom_KEY); + + // Put the clone into the room, which also forces it to load. + plNodeRefMsg* nodeRefCloneMsg = TRACKED_NEW plNodeRefMsg(cloneNodeKey, plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kObject); + hsgResMgr::ResMgr()->AddViaNotify(cloneKey, nodeRefCloneMsg, plRefFlags::kActiveRef); + + // Finally, pump the dispatch system so all the new refs get delivered. ? + plgDispatch::Dispatch()->MsgQueueProcess(); + } + else // we're unloading a clone + { + if (!cloneKey->ObjectIsLoaded()) + { + DebugMsg("ILoadClone: object %s is already unloaded, ignoring", cloneKey->GetName()); + return cloneKey; + } + + ICheckPendingStateLoad(hsTimer::GetSysSeconds()); + plSynchEnabler p(false); // turn off dirty tracking while in this function + + GetKey()->Release(cloneKey); // undo the active ref we took in ILoadClone + + // send message to scene object to remove him from the room + plNodeChangeMsg* nodeChange = TRACKED_NEW plNodeChangeMsg(GetKey(), cloneKey, nil); + plgDispatch::MsgSend(nodeChange); + } + + plKey requestorKey = pCloneMsg->GetRequestorKey(); + + // Readdress the message to the requestor and send it again + plKey myKey = GetKey(); + pCloneMsg->SetBCastFlag(plMessage::kNetPropagate, false); + pCloneMsg->ClearReceivers(); + pCloneMsg->AddReceiver(requestorKey); + pCloneMsg->Ref(); // each message send unrefs once + pCloneMsg->Send(); + + return cloneKey; +} + +// +// Cause a player to respawn. This is typically called on the local player when he links to a new age. +// or for unspawn: +// +void plNetClientMgr::IPlayerChangeAge(hsBool exitAge, Int32 spawnPt) +{ + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + + if (avatar) + { + plSynchEnabler ps(false); // disable state change tracking while we change ages + if (exitAge) + avatar->LeaveAge(); + else + { + hsBool validSpawn = (spawnPt >= 0); + avatar->EnterAge(!validSpawn); + if (validSpawn) + avatar->SpawnAt(spawnPt, hsTimer::GetSysSeconds()); + } + } + else if (fLocalPlayerKey) + { + ErrorMsg("Can't find avatarMod %s", fLocalPlayerKey->GetName()); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrRecord.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrRecord.cpp new file mode 100644 index 00000000..d72ad6b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrRecord.cpp @@ -0,0 +1,117 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plgDispatch.h" +#include "plNetClientMgr.h" + +#include "../pnNetCommon/pnNetCommon.h" +#include "../pnMessage/plTimeMsg.h" + +#include "../plNetClientRecorder/plNetClientRecorder.h" + +// +// make a recording of current play +// +bool plNetClientMgr::RecordMsgs(const char* recType, const char* recName) +{ + if (!fMsgRecorder) + { + if (stricmp(recType,"stream") == 0) + fMsgRecorder = TRACKED_NEW plNetClientStreamRecorder; + if (stricmp(recType,"stressstream") == 0) + fMsgRecorder = TRACKED_NEW plNetClientStressStreamRecorder; + if (stricmp(recType,"stats") == 0) + fMsgRecorder = TRACKED_NEW plNetClientStatsRecorder; + if (stricmp(recType,"streamandstats") == 0) + fMsgRecorder = TRACKED_NEW plNetClientStreamAndStatsRecorder(TRACKED_NEW plNetClientStreamRecorder(), TRACKED_NEW plNetClientStatsRecorder()); + if (stricmp(recType,"stressstreamandstats") == 0) + fMsgRecorder = TRACKED_NEW plNetClientStreamAndStatsRecorder(TRACKED_NEW plNetClientStressStreamRecorder(), TRACKED_NEW plNetClientStatsRecorder()); + + if (!fMsgRecorder || !fMsgRecorder->BeginRecording(recName)) + { + delete fMsgRecorder; + fMsgRecorder = nil; + return false; + } + + return true; + } + + return false; +} + +// +// play a recording +// +bool plNetClientMgr::PlaybackMsgs(const char* recName) +{ + hsLogEntry(DebugMsg("DEMO: Beginning Playback")); + + plNetClientRecorder* player = TRACKED_NEW plNetClientStreamRecorder; + + if (player->BeginPlayback(recName)) + { + fMsgPlayers.push_back(player); +// plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + return true; + } + else + { + delete player; + return false; + } +} + +// +// +// +void plNetClientMgr::IPlaybackMsgs() +{ + for (int i = 0; i < fMsgPlayers.size(); i++) + { + plNetClientRecorder* recorder = fMsgPlayers[i]; + if (recorder->IsQueueEmpty()) + { + delete recorder; + fMsgPlayers.erase(fMsgPlayers.begin()+i); + i--; + + if (fMsgPlayers.empty()) + { +// plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + } + } + else + { + while (plNetMessage* msg = recorder->GetNextMessage()) + { + hsLogEntry(DebugMsg("")); + fMsgHandler.ReceiveMsg(msg); + } + } + } +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrSend.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrSend.cpp new file mode 100644 index 00000000..500dcaca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrSend.cpp @@ -0,0 +1,460 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTimer.h" +#include "hsResMgr.h" +#include "plNetClientMgr.h" +#include "plCreatableIndex.h" +#include "plNetObjectDebugger.h" +#include "plNetClientMsgScreener.h" + +#include "../pnNetCommon/plSynchedObject.h" +#include "../pnNetCommon/plSDLTypes.h" +#include "../pnMessage/plCameraMsg.h" + +#include "../plNetClientRecorder/plNetClientRecorder.h" +#include "../plMessage/plLoadCloneMsg.h" +#include "../plMessage/plLoadAvatarMsg.h" +#include "../plAvatar/plAvatarClothing.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plMessage/plCCRMsg.h" +#include "../plVault/plVault.h" +#include "../plContainer/plConfigInfo.h" +#include "../plDrawable/plMorphSequence.h" +#include "../plParticleSystem/plParticleSystem.h" +#include "../plParticleSystem/plParticleSDLMod.h" +#include "../plResMgr/plLocalization.h" + +#include "../../FeatureLib/pfMessage/pfKIMsg.h" // TMP + +#include "../plNetGameLib/plNetGameLib.h" +#include "../plSDL/plSDL.h" + +// +// request members list from server +// +int plNetClientMgr::ISendMembersListRequest() +{ + plNetMsgMembersListReq msg; + msg.SetNetProtocol(kNetProtocolCli2Game); + return SendMsg(&msg); +} + +// +// reset paged in rooms list on server +// +int plNetClientMgr::ISendRoomsReset() +{ + plNetMsgPagingRoom msg; + msg.SetPageFlags(plNetMsgPagingRoom::kResetList); + msg.SetNetProtocol(kNetProtocolCli2Game); + return SendMsg(&msg); +} + +// +// Make sure all dirty objects save their state. +// Mark those objects as clean and clear the dirty list. +// +int plNetClientMgr::ISendDirtyState(double secs) +{ + std::vector carryOvers; + + Int32 num=plSynchedObject::GetNumDirtyStates(); +#if 0 + if (num) + { + DebugMsg("%d dirty sdl state msgs queued, t=%f", num, secs); + } +#endif + Int32 i; + for(i=0;iGetObject(); + if (!obj) + continue; // could add to carryOvers + + if (!(state->fSendFlags & plSynchedObject::kSkipLocalOwnershipCheck)) + { + int localOwned=obj->IsLocallyOwned(); + if (localOwned==plSynchedObject::kNo) + { + DebugMsg("Late rejection of queued SDL state, obj %s, sdl %s", + state->fObjKey->GetName(), state->fSDLName.c_str()); + continue; + } + } + + obj->CallDirtyNotifiers(); + obj->SendSDLStateMsg(state->fSDLName.c_str(), state->fSendFlags); + } + + plSynchedObject::ClearDirtyState(carryOvers); + + return hsOK; +} + +// +// Given a plasma petition msg, send a petition text node to the vault +// vault will detect and fwd to CCR system. +// +void plNetClientMgr::ISendCCRPetition(plCCRPetitionMsg* petMsg) +{ + // petition msg info + UInt8 type = petMsg->GetType(); + const char* title = petMsg->GetTitle(); + const char* note = petMsg->GetNote(); + + std::string work = note; + std::replace( work.begin(), work.end(), '\n', '\t' ); + note = work.c_str(); + + // stuff petition info fields into a config info object + plConfigInfo info; + info.AddValue( "Petition", "Type", type ); + info.AddValue( "Petition", "Content", note ); + info.AddValue( "Petition", "Title", title ); + info.AddValue( "Petition", "Language", plLocalization::GetLanguageName( plLocalization::GetLanguage() ) ); + info.AddValue( "Petition", "AcctName", NetCommGetAccount()->accountNameAnsi ); + char buffy[20]; + sprintf( buffy, "%lu", GetPlayerID() ); + info.AddValue( "Petition", "PlayerID", buffy ); + info.AddValue( "Petition", "PlayerName", GetPlayerName() ); + + // write config info formatted like an ini file to a buffer + hsRAMStream ram; + info.WriteTo( &plIniStreamConfigSource( &ram ) ); + int size = ram.GetPosition(); + ram.Rewind(); + std::string buf; + buf.resize( size ); + ram.CopyToMem( (void*)buf.data() ); + + wchar * wStr = StrDupToUnicode(buf.c_str()); + NetCliAuthSendCCRPetition(wStr); + FREE(wStr); +} + +// +// send a msg to reset the camera in a new age +// +void plNetClientMgr::ISendCameraReset(hsBool bEnteringAge) +{ + plCameraMsg* pCamMsg = TRACKED_NEW plCameraMsg; + if (bEnteringAge) + pCamMsg->SetCmd(plCameraMsg::kResetOnEnter); + else + pCamMsg->SetCmd(plCameraMsg::kResetOnExit); + pCamMsg->SetBCastFlag(plMessage::kBCastByExactType, false); + plUoid U2(kVirtualCamera1_KEY); + plKey pCamKey = hsgResMgr::ResMgr()->FindKey(U2); + if (pCamKey) + pCamMsg->AddReceiver(pCamKey); + pCamMsg->Send(); +} + +// +// When we link in to a new age, we need to send our avatar state up to the gameserver +// +void plNetClientMgr::SendLocalPlayerAvatarCustomizations() +{ + plSynchEnabler ps(true); // make sure synching is enabled, since this happens during load + + const plArmatureMod * avMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + hsAssert(avMod,"Failed to get local avatar armature modifier."); + avMod->GetClothingOutfit()->DirtySynchState(kSDLClothing, plSynchedObject::kBCastToClients | plSynchedObject::kForceFullSend); + + plSceneObject* pObj = (const_cast(avMod))->GetFollowerParticleSystemSO(); + if (pObj) + { + const plParticleSystem* sys = plParticleSystem::ConvertNoRef(pObj->GetModifierByType(plParticleSystem::Index())); + if (sys) + (const_cast(sys))->GetSDLMod()->SendState(plSynchedObject::kBCastToClients | plSynchedObject::kForceFullSend); + + } + // may want to do this all the time, but for now stealthmode is the only extra avatar state we care about + // don't bcast this to other clients, the invis level is contained in the linkIn msg which will synch other clients + if (avMod->IsInStealthMode() && avMod->GetTarget(0)) + avMod->GetTarget(0)->DirtySynchState(kSDLAvatar, plSynchedObject::kForceFullSend); + + hsTArray morphs; + plMorphSequence::FindMorphMods(avMod->GetTarget(0), morphs); + int i; + for (i = 0; i < morphs.GetCount(); i++) + if (morphs[i]->GetTarget(0)) + morphs[i]->GetTarget(0)->DirtySynchState(kSDLMorphSequence, plSynchedObject::kBCastToClients); + +} + +// +// Called to send a plasma msg out over the network. Called by the dispatcher. +// return hsOK if ok +// +int plNetClientMgr::ISendGameMessage(plMessage* msg) +{ + if (GetFlagsBit(kDisabled)) + return hsOK; + + static plNetClientMsgScreener screener; // make static so that there's only 1 log per session + if (!screener.AllowMessage(msg)) + { + if (GetFlagsBit(kScreenMessages)) + return hsOK; // filter out illegal messages + } + + // TEMP + if (GetFlagsBit(kSilencePlayer)) + { + pfKIMsg* kiMsg = pfKIMsg::ConvertNoRef(msg); + if (kiMsg && kiMsg->GetCommand()==pfKIMsg::kHACKChatMsg) + return hsOK; + } + + plNetPlayerIDList* dstIDs = msg->GetNetReceivers(); + +#ifdef HS_DEBUGGING + if ( dstIDs ) + { + DebugMsg( "Preparing to send %s to specific players.", msg->ClassName() ); + } +#endif + + // get sender object + plSynchedObject* synchedObj = msg->GetSender() ? plSynchedObject::ConvertNoRef(msg->GetSender()->ObjectIsLoaded()) : nil; + + // if sender is flagged as localOnly, he shouldn't talk to the network + if (synchedObj && !synchedObj->IsNetSynched() ) + return hsOK; + + // choose appropriate type of net game msg wrapper + plNetMsgGameMessage* netMsgWrap=nil; + plLoadCloneMsg* loadClone = plLoadCloneMsg::ConvertNoRef(msg); + if (loadClone) + { + plLoadAvatarMsg* lam=plLoadAvatarMsg::ConvertNoRef(msg); + + netMsgWrap = TRACKED_NEW plNetMsgLoadClone; + plNetMsgLoadClone* netLoadClone=plNetMsgLoadClone::ConvertNoRef(netMsgWrap); + + netLoadClone->SetIsPlayer(lam && lam->GetIsPlayer()); + netLoadClone->SetIsLoading(loadClone->GetIsLoading()!=0); + netLoadClone->ObjectInfo()->SetFromKey(loadClone->GetCloneKey()); + } + else + if (dstIDs) + { + netMsgWrap = TRACKED_NEW plNetMsgGameMessageDirected; + int i; + for(i=0;isize();i++) + { + UInt32 playerID = (*dstIDs)[i]; + if (playerID == NetCommGetPlayer()->playerInt) + continue; + hsLogEntry( DebugMsg( "\tAdding receiver: %lu" , playerID ) ); + ((plNetMsgGameMessageDirected*)netMsgWrap)->Receivers()->AddReceiverPlayerID( playerID ); + } + } + else + netMsgWrap = TRACKED_NEW plNetMsgGameMessage; + + // check delivery timestamp + if (msg->GetTimeStamp()<=hsTimer::GetSysSeconds()) + msg->SetTimeStamp(0); + else + netMsgWrap->GetDeliveryTime().SetFromGameTime(msg->GetTimeStamp(), hsTimer::GetSysSeconds()); + + // write message (and label) to ram stream + hsRAMStream stream; + hsgResMgr::ResMgr()->WriteCreatable(&stream, msg); + + // put stream in net msg wrapper + netMsgWrap->StreamInfo()->CopyStream(&stream); + + // hsLogEntry( DebugMsg(plDispatchLog::GetInstance()->MakeMsgInfoString(msg, "\tActionMsg:",0)) ); + + // check if this msg uses direct communication (sent to specific rcvrs) + // if so the server can filter it + hsBool bCast = msg->HasBCastFlag(plMessage::kBCastByExactType) || + msg->HasBCastFlag(plMessage::kBCastByType); + hsBool directCom = msg->GetNumReceivers()>0; + if( directCom ) + { + // It's direct if we have receivers AND any of them are in non-virtual locations + int i; + for( i = 0, directCom = false; i < msg->GetNumReceivers(); i++ ) + { + if( !msg->GetReceiver( i )->GetUoid().GetLocation().IsVirtual() && + !msg->GetReceiver( i )->GetUoid().GetLocation().IsReserved() + // && !IsBuiltIn + ) + { + directCom = true; + break; + } + } + if (!directCom) + bCast = true; + } + if (!directCom && !bCast && !dstIDs) + WarningMsg("Msg %s has no rcvrs or bcast instructions?", msg->ClassName()); + + hsAssert(!(directCom && bCast), "msg has both rcvrs and bcast instructions, rcvrs ignored"); + if (directCom && !bCast) + { + netMsgWrap->SetBit(plNetMessage::kHasGameMsgRcvrs); // for quick server filtering + netMsgWrap->StreamInfo()->SetCompressionType(plNetMessage::kCompressionDont); + } + + // + // check for net propagated plasma msgs which should be filtered by relevance regions. + // currently only avatar control messages. + // + if (msg->HasBCastFlag(plMessage::kNetUseRelevanceRegions)) + { + netMsgWrap->SetBit(plNetMessage::kUseRelevanceRegions); + } + + // + // CCRs can route a plMessage to all online players. + // + hsBool ccrSendToAllPlayers = false; +#ifndef PLASMA_EXTERNAL_RELEASE + if ( AmCCR() ) + { + ccrSendToAllPlayers = msg->HasBCastFlag( plMessage::kCCRSendToAllPlayers ); + if ( ccrSendToAllPlayers ) + netMsgWrap->SetBit( plNetMessage::kRouteToAllPlayers ); + } +#endif + + // + // check for inter-age routing. if set, online rcvrs not in current age will receive + // this msg courtesy of pls routing. + // + if ( !ccrSendToAllPlayers ) + { + hsBool allowInterAge = msg->HasBCastFlag( plMessage::kNetAllowInterAge ); + if ( allowInterAge ) + netMsgWrap->SetBit(plNetMessage::kInterAgeRouting); + } + + // check for reliable send + if (msg->HasBCastFlag(plMessage::kNetSendUnreliable) && + !(synchedObj && (synchedObj->GetSynchFlags() & plSynchedObject::kSendReliably)) ) + netMsgWrap->SetBit(plNetMessage::kNeedsReliableSend, 0); // clear reliable net send bit + +#ifdef HS_DEBUGGING + Int16 type=*(Int16*)netMsgWrap->StreamInfo()->GetStreamBuf(); + hsAssert(type>=0 && typeSetPlayerID(GetPlayerID()); + netMsgWrap->SetNetProtocol(kNetProtocolCli2Game); + int ret = SendMsg(netMsgWrap); + + if (plNetObjectDebugger::GetInstance()->IsDebugObject(msg->GetSender() ? msg->GetSender()->ObjectIsLoaded() : nil)) + { + #if 0 + hsLogEntry(plNetObjectDebugger::GetInstance()->LogMsg( + xtl::format(" object:%s, rcvr %s %s", + msg->GetSender(), + msg->GetNumReceivers() ? msg->GetReceiver(0)->GetName() : "?", + netMsgWrap->AsStdString().c_str()).c_str())); + #endif + } + + delete netMsgWrap; + return ret; +} + +// +// Send a net msg. Delivers to transport mgr who sends p2p or to server +// +int plNetClientMgr::SendMsg(plNetMessage* msg) +{ + if (GetFlagsBit(kDisabled)) + return hsOK; + + if (!CanSendMsg(msg)) + return hsOK; + + // If we're recording messages, set an identifying flag and echo the message back to ourselves + if (fMsgRecorder && fMsgRecorder->IsRecordableMsg(msg)) + { + msg->SetBit(plNetMessage::kEchoBackToSender, true); + } + + msg->SetTimeSent(plUnifiedTime::GetCurrentTime()); + int channel = IPrepMsg(msg); + +// hsLogEntry( DebugMsg( " %s %s", msg->ClassName(), msg->AsStdString().c_str()) ); + + int ret=fTransport.SendMsg(channel, msg); + + // Debug + if (plNetMsgVoice::ConvertNoRef(msg)) + SetFlagsBit(kSendingVoice); + if (plNetMsgGameMessage::ConvertNoRef(msg)) + SetFlagsBit(kSendingActions); + + plCheckNetMgrResult_ValReturn(ret,(char*)xtl::format("Failed to send %s, NC ret=%d", + msg->ClassName(), ret).c_str()); + + return ret; +} + + +void plNetClientMgr::StoreSDLState(const plStateDataRecord* sdRec, const plUoid& uoid, + UInt32 sendFlags, UInt32 writeOptions) +{ + // send to server + plNetMsgSDLState* msg = sdRec->PrepNetMsg(0, writeOptions); + msg->SetNetProtocol(kNetProtocolCli2Game); + msg->ObjectInfo()->SetUoid(uoid); + + if (sendFlags & plSynchedObject::kNewState) + msg->SetBit(plNetMessage::kNewSDLState); + + if (sendFlags & plSynchedObject::kUseRelevanceRegions) + msg->SetBit(plNetMessage::kUseRelevanceRegions); + + if (sendFlags & plSynchedObject::kDontPersistOnServer) + msg->SetPersistOnServer(false); + + if (sendFlags & plSynchedObject::kIsAvatarState) + msg->SetIsAvatarState(true); + + bool broadcast = (sendFlags & plSynchedObject::kBCastToClients) != 0; + if (broadcast && plNetClientApp::GetInstance()) + { + msg->SetPlayerID(plNetClientApp::GetInstance()->GetPlayerID()); + } + + SendMsg(msg); + DEL(msg); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrShow.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrShow.cpp new file mode 100644 index 00000000..688cc229 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrShow.cpp @@ -0,0 +1,375 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetClientMgr.h" +#include "plNetLinkingMgr.h" + +#include "../pnSceneObject/plSceneObject.h" + +#include "../plPipeline/plPlates.h" +#include "../plPipeline/plDebugText.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plScene/plRelevanceMgr.h" + +// +// Code which displays stuff on the screen +// + +// +// show lists of members, listenList, and talkList +// +void plNetClientMgr::IShowLists() +{ + plNetLinkingMgr * lm = plNetLinkingMgr::GetInstance(); + plDebugText &txt = plDebugText::Instance(); + + int y,x,i; + const int yOff=10, xOff=300, startY=70, startX=10; + char str[256]; + + // My player info + x=startX; + y=startY; + plSceneObject *player = plSceneObject::ConvertNoRef(GetLocalPlayer()); + hsPoint3 pos = (player ? player->GetLocalToWorld() * hsPoint3(0, 0, 0) : hsPoint3(0, 0, 0)); + sprintf(str, "%s%s PlyrName=%s PlyrID=%d AcctID=%d P2P=%d Join#=%d Peers=%d %.1f,%.1f,%.1f", + GetFlagsBit(kSendingVoice) ? "V" : " ", + GetFlagsBit(kSendingActions) ? "A" : " ", + GetPlayerName(), GetPlayerID(), 0, + IsPeerToPeer(), GetJoinOrder(), 0, + pos.fX, pos.fY, pos.fZ); + txt.DrawString(x,y,str,255,255,255,255); + SetFlagsBit(kSendingVoice, 0); + SetFlagsBit(kSendingActions, 0); + + y+=yOff; + sprintf(str, " Server=%s ILIAS=%d", "foo", + IsLoadingInitialAgeState()); + txt.DrawString(x,y,str,255,255,255,255); + + // MEMBERS + y+=yOff; + int baseY=y; + txt.DrawString(x,y," Members",255,255,255,255,plDebugText::kStyleBold); + y+=yOff; + plNetTransportMember** members=nil; + fTransport.GetMemberListDistSorted(members); + for(i=0;iIsServer()) + continue; + player = (mbr->GetAvatarKey() ? plSceneObject::ConvertNoRef(mbr->GetAvatarKey()->ObjectIsLoaded()) : nil); + sprintf(str, "%s%s %s p2p=%d dist=%.1f", + mbr->GetTransportFlags() & plNetTransportMember::kSendingVoice ? "V" : " ", + mbr->GetTransportFlags() & plNetTransportMember::kSendingActions ? "A" : " ", + mbr->AsStdString().c_str(), + mbr->IsPeerToPeer(), + mbr->GetDistSq() != hsScalarMax ? hsSquareRoot(mbr->GetDistSq()) :-1.f); + txt.DrawString(x,y,str); + y+=yOff; + mbr->SetTransportFlags(mbr->GetTransportFlags() & + ~(plNetTransportMember::kSendingVoice|plNetTransportMember::kSendingActions)); + } + + delete [] members; + + // LISTENLIST + x+=xOff; + y=baseY; + txt.DrawString(x,y,"ListenList",255,255,255,255,plDebugText::kStyleBold); + y+=yOff; + for(i=0;iGetNumMembers();i++) + { + sprintf(str, "name=%s", GetListenList()->GetMember(i)->AsStdString().c_str()); + txt.DrawString(x,y,str); + y+=yOff; + } + + // TALKLIST + x+=xOff; + y=baseY; + txt.DrawString(x,y,"TalkList",255,255,255,255,plDebugText::kStyleBold); + y+=yOff; + for(i=0;iGetNumMembers();i++) + { + sprintf(str, "name=%s", GetTalkList()->GetMember(i)->AsStdString().c_str()); + txt.DrawString(x,y,str); + y+=yOff; + } +} + + +// +// show lists of members, listenList, and talkList +// +void plNetClientMgr::IShowRooms() +{ + plDebugText &txt = plDebugText::Instance(); + + int y,x; + const int yOff=10, xOff=300, startY=70, startX=10; + char str[256]; + + // OWNEDLIST + x=startX; + y=startY; + txt.DrawString(x,y,"RoomsOwned",255,255,255,255,plDebugText::kStyleBold); + y+=yOff; + std::set::iterator it=GetNetGroups()->fGroups.begin(); + for(;it != GetNetGroups()->fGroups.end(); it++) + { + if ((*it).fOwnIt) + { + sprintf(str, "%s", (*it).fGroup.GetDesc()); + txt.DrawString(x,y,str); + y+=yOff; + } + } + + // NOTOWNEDLIST + x+=xOff; + y=startY; + txt.DrawString(x,y,"RoomsNotOwned",255,255,255,255,plDebugText::kStyleBold); + y+=yOff; + it=GetNetGroups()->fGroups.begin(); + for(;it != GetNetGroups()->fGroups.end(); it++) + { + if (!(*it).fOwnIt) + { + sprintf(str, "%s", (*it).fGroup.GetDesc()); + txt.DrawString(x,y,str); + y+=yOff; + } + } +} + +UInt32 IPrintRelRegion(const hsBitVector& region, int x, int y, const hsBitVector* cmpRegion) +{ + char buf[256]; + int maxBits = 255; + + UInt32 num = plRelevanceMgr::Instance()->GetNumRegions(); + if (num > maxBits) + num = maxBits; + + bool orTrue = false; + + int i; + for (i = 0; i < num; i++) + { + buf[i] = (region.IsBitSet(i) ? '1' : '0'); + if (cmpRegion && cmpRegion->IsBitSet(i) && region.IsBitSet(i)) + orTrue = true; + } + buf[i] = '\0'; + + plDebugText& txt = plDebugText::Instance() ; + if (orTrue) + txt.DrawString(x, y, buf, 0, 255, 0); + else + txt.DrawString(x, y, buf); + + return txt.CalcStringWidth(buf); +} + +void plNetClientMgr::IShowRelevanceRegions() +{ + plDebugText& txt = plDebugText::Instance(); + + const int yOff = 12, xOff = 20, startY=70, startX=10; + int x = startX, y = startY; + + const char* title = "Name / In / Care"; + txt.DrawString(x, y - yOff, title, 255, 255, 255, 255, plDebugText::kStyleBold); + + plNetTransportMember** members = nil; + fTransport.GetMemberListDistSorted(members); + + // + // Print out the player names in the first column + // + UInt32 maxPlayerName = 0; + + txt.DrawString(x, y, GetPlayerName()); + maxPlayerName = hsMaximum(maxPlayerName, txt.CalcStringWidth(GetPlayerName())); + y += yOff; + + int i; + for (i = 0; i < fTransport.GetNumMembers(); i++) + { + plNetTransportMember* mbr = members[i]; + hsAssert(mbr, "ShowLists: nil member?"); + if (mbr->IsServer()) + continue; + + const char* name = mbr->GetPlayerName(); + txt.DrawString(x, y, name); + maxPlayerName = hsMaximum(maxPlayerName, txt.CalcStringWidth(name)); + y += yOff; + } + + x = startX + maxPlayerName + xOff; + y = startY; + + // + // Print out the regions + // + const hsBitVector* ourCare = nil; + const hsBitVector* ourIn = nil; + + plSceneObject* player = plSceneObject::ConvertNoRef(GetLocalPlayer()); + if (player) + { + const plArmatureMod *avMod = plArmatureMod::ConvertNoRef(player->GetModifierByType(plArmatureMod::Index())); + if (avMod) + { + ourIn = &avMod->GetRelRegionImIn(); + UInt32 width = IPrintRelRegion(*ourIn, x, y, nil); + + ourCare = &avMod->GetRelRegionCareAbout(); + IPrintRelRegion(*ourCare, x + width + xOff, y, nil); + + y += yOff; + } + } + + for (i = 0; i < fTransport.GetNumMembers(); i++) + { + plNetTransportMember* mbr = members[i]; + if (mbr->IsServer()) + continue; + + player = (mbr->GetAvatarKey() ? plSceneObject::ConvertNoRef(mbr->GetAvatarKey()->ObjectIsLoaded()) : nil); + if (player) + { + const plArmatureMod* avMod = plArmatureMod::ConvertNoRef(player->GetModifierByType(plArmatureMod::Index())); + if (avMod) + { + const hsBitVector& in = avMod->GetRelRegionImIn(); + UInt32 width = IPrintRelRegion(in, x, y, ourCare); + + const hsBitVector& care = avMod->GetRelRegionCareAbout(); + IPrintRelRegion(care, x + width + xOff, y, ourIn); + + y += yOff; + } + } + } + + delete [] members; +} + +void plNetClientMgr::IShowAvatars() +{ + plDebugText &txt = plDebugText::Instance(); + txt.SetFont( "Courier New", 6 ); + + int y,x,i; + const int yOff=10, xOff=285, startY=100, startX=10; + char str[256]; + + // Me + x=startX; + y=startY-yOff*3; + plSceneObject *player = plSceneObject::ConvertNoRef(GetLocalPlayer()); + hsPoint3 pos = (player ? player->GetLocalToWorld() * hsPoint3(0, 0, 0) : hsPoint3(0, 0, 0)); + hsVector3 ori = (player ? player->GetLocalToWorld() * hsVector3(0, -1, 0) : hsVector3(0, 0, 0)); + sprintf(str, "%s: pos(%.2f, %.2f, %.2f) ori(%.2f, %.2f, %.2f)", + GetPlayerName(), pos.fX, pos.fY, pos.fZ, ori.fX, ori.fY, ori.fZ); + txt.DrawString(x,y,str,255,255,255,255); + + + if (player) + { + const plArmatureMod *avMod = plArmatureMod::ConvertNoRef(player->GetModifierByType(plArmatureMod::Index())); + if (avMod) + { + plArmatureMod* pNonConstArm = const_cast(avMod); + plSceneObject* pObj = pNonConstArm->GetFollowerParticleSystemSO(); + if (pObj) + { + y+=yOff; + y+=yOff; + hsPoint3 pos = (pObj ? pObj->GetLocalToWorld() * hsPoint3(0, 0, 0) : hsPoint3(0, 0, 0)); + hsVector3 ori = (pObj ? pObj->GetLocalToWorld() * hsVector3(0, -1, 0) : hsVector3(0, 0, 0)); + sprintf(str, "%s: pos(%.2f, %.2f, %.2f) ori(%.2f, %.2f, %.2f)", + pObj->GetKeyName(), pos.fX, pos.fY, pos.fZ, ori.fX, ori.fY, ori.fZ); + txt.DrawString(x,y,str,255,255,255,255); + } + } + } + + + // Others + y=startY; + x=startX; + + plNetTransportMember** members=nil; + fTransport.GetMemberListDistSorted(members); + for(i=0;iIsServer()) + continue; + + player = (mbr->GetAvatarKey() ? plSceneObject::ConvertNoRef(mbr->GetAvatarKey()->ObjectIsLoaded()) : nil); + pos = (player ? player->GetLocalToWorld() * hsPoint3(0, 0, 0) : hsPoint3(0, 0, 0)); + ori = (player ? player->GetLocalToWorld() * hsVector3(0, -1, 0) : hsVector3(0, 0, 0)); + + sprintf(str, "%s: pos(%.2f, %.2f, %.2f) ori(%.2f, %.2f, %.2f)", + mbr->AsStdString().c_str(), pos.fX, pos.fY, pos.fZ, ori.fX, ori.fY, ori.fZ); + txt.DrawString(x,y,str); + y+=yOff; + + if (player) + { + const plArmatureMod *avMod = plArmatureMod::ConvertNoRef(player->GetModifierByType(plArmatureMod::Index())); + if (avMod) + { + plArmatureMod* pNonConstArm = const_cast(avMod); + plSceneObject* pObj = pNonConstArm->GetFollowerParticleSystemSO(); + if (pObj) + { + y+=yOff; + y+=yOff; + hsPoint3 pos = (pObj ? pObj->GetLocalToWorld() * hsPoint3(0, 0, 0) : hsPoint3(0, 0, 0)); + hsVector3 ori = (pObj ? pObj->GetLocalToWorld() * hsVector3(0, -1, 0) : hsVector3(0, 0, 0)); + sprintf(str, "%s: pos(%.2f, %.2f, %.2f) ori(%.2f, %.2f, %.2f)", + pObj->GetKeyName(), pos.fX, pos.fY, pos.fZ, ori.fX, ori.fY, ori.fZ); + txt.DrawString(x,y,str,255,255,255,255); + } + } + } + + } + + delete [] members; + + txt.SetFont( "Courier New", 8 ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrTask.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrTask.cpp new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrTask.cpp @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrVault.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrVault.cpp new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrVault.cpp @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrVoice.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrVoice.cpp new file mode 100644 index 00000000..b982c133 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgrVoice.cpp @@ -0,0 +1,394 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include +#include "hsMatrix44.h" +#include "hsGeometry3.h" +#include "plNetClientMgr.h" + +#include "../plNetMessage/plNetMessage.h" +#include "../pnNetCommon/plNetServers.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plNetTransport/plNetTransportMember.h" +#include "../plMessage/plMemberUpdateMsg.h" +#include "../plMessage/plNetVoiceListMsg.h" +#include "../plStatusLog/plStatusLog.h" +#include "../plVault/plVault.h" + + +#define SAME_TALK_AND_LISTEN + +struct DistSqInfo +{ + plNetTransportMember* fMbr; + float fDistSq; + DistSqInfo(plNetTransportMember* m, float d) : fMbr(m),fDistSq(d) {} +}; + +bool lessComp(DistSqInfo a, DistSqInfo b) +{ + return (a.fDistSq& newListenList, hsBool forceSynch) +{ + hsBool changed=false; + int i; + + // see if new listen list differs from current one + if (forceSynch || newListenList.size() != GetListenList()->GetNumMembers()) + changed=true; + else + { + for(i=0;iFindMember(newListenList[i].fMbr)==-1) + { + changed=true; + break; + } + } + } + + // set as new listen list + if (changed) + { + DebugMsg("ListenList changed, forceSynch=%d\n", forceSynch); + + plNetMsgListenListUpdate llu; + llu.SetPlayerID(GetPlayerID()); + llu.SetNetProtocol(kNetProtocolCli2Game); + + // + // for each client in the old list, if not in the new list, send a ListenList remove msg + // + llu.Receivers()->Clear(); + llu.SetAdding(false); + for(i=0;iGetNumMembers(); i++) + { + hsBool found=false; + if (!forceSynch) + { + int j; + for(j=0;jGetMember(i)) + { + found=true; + break; + } + } + } + if (found==false) + { + llu.Receivers()->AddReceiverPlayerID(GetListenList()->GetMember(i)->GetPlayerID()); + } + } + +#ifndef SAME_TALK_AND_LISTEN + if (llu.Receivers()->GetNumReceivers()) + { + // DEBUGGING + int i; + for(i=0;iGetNumReceivers(); i++) + { + int idx=fTransport.FindMember(llu.Receivers()->GetReceiverClientNum(i)); + plNetTransportMember* mbr=fTransport.GetMember(idx); + DebugMsg(" ListenListUpdate msg, adding=%d\n", + mbr->AsStdString().c_str(), llu.GetAdding()); + } + + SendMsg(&llu); + } +#endif + + // + // for each client in the new list, [if not in the old list,] send a ListenList add msg + // + llu.Receivers()->Clear(); + llu.SetAdding(true); + for(i=0;iFindMember(newListenList[i].fMbr)==-1) + { + // if not in the old list, send a ListenList add msg + llu.Receivers()->AddReceiverPlayerID(newListenList[i].fMbr->GetPlayerID()); + } + } + +#ifndef SAME_TALK_AND_LISTEN + if (llu.Receivers()->GetNumReceivers()) + { + // DEBUGGING + int i; + for(i=0;iGetNumReceivers(); i++) + { + int cNum=llu.Receivers()->GetReceiverClientNum(i); + int idx=fTransport.FindMember(cNum); + plNetTransportMember* mbr=fTransport.GetMember(idx); + DebugMsg(" ListenListUpdate msg, adding=%d, cNum=%d\n", + mbr->AsStdString().c_str(), llu.GetAdding(), cNum); + } + + SendMsg(&llu); + } +#endif + + // + // set as new listen list + // + GetListenList()->Clear(); +#ifdef HS_DEBUGGING + DebugMsg("New ListenList, size=%d\n", newListenList.size()); +#endif + for(i=0;iAddMember(newListenList[i].fMbr); +#ifdef HS_DEBUGGING + DebugMsg("\tLL Member %d, name=%s, cNum=%d, dist=%f\n", + i, newListenList[i].fMbr->AsStdString().c_str(), + newListenList[i].fMbr->GetPlayerID(), newListenList[i].fDistSq); +#endif + } + } + return changed; +} +// +// Periodically updates the list of what remote players I'm listening to. +// Used to filter voice streams. +// Returns true if the listenList was changed. +// Note: Updates distSq to each member. Other things rely on this so we must do it even if p2p is disabled. +// +hsBool plNetClientMgr::IUpdateListenList(double secs) +{ + if (GetFlagsBit(kDisabled)) + return false; + if (!fLocalPlayerKey || !fLocalPlayerKey->ObjectIsLoaded()) + return false; + + hsBool changed = false; + + if (secs - GetListenList()->GetLastUpdateTime()>plNetListenList::kUpdateInterval) + { + GetListenList()->SetLastUpdateTime(secs); + std::vector newListenList; + + switch (fListenListMode) + { + case kListenList_Forced: + { +#ifdef SAME_TALK_AND_LISTEN + SynchTalkList(); +#endif + } + return true; + case kListenList_Distance: + { + // Finds the 3 closest players to our local player + // Search is unoptimized for now... + + // compute our players pos + plSceneObject* locPlayer = plSceneObject::ConvertNoRef(fLocalPlayerKey->ObjectIsLoaded()); + hsAssert(locPlayer, "local player is not a sceneObject?"); + hsAssert(locPlayer->GetCoordinateInterface(), "locPlayer has no coordInterface"); + hsMatrix44 l2w=locPlayer->GetCoordinateInterface()->GetLocalToWorld(); + hsPoint3 locPlayerPos=l2w.GetTranslate(); + + int i; + for(i=0;iSetDistSq(hsScalarMax); + + if (fTransport.GetMember(i)->IsServer()) + continue; + if(VaultAmIgnoringPlayer(fTransport.GetMember(i)->GetPlayerID())) + { + continue; + } + plKey k=fTransport.GetMember(i)->GetAvatarKey(); +#if 0 + if (!k) + { + DebugMsg("UpdateListenList: Nil avatar key on member %s\n", + fTransport.GetMember(i)->AsStdString().c_str()); + } +#endif + plSceneObject* obj=plSceneObject::ConvertNoRef(k ? k->ObjectIsLoaded() : nil); + if (obj && obj->GetCoordinateInterface()) + { +#if 1 + // compute distSq to me + l2w=obj->GetCoordinateInterface()->GetLocalToWorld(); + hsPoint3 pos=l2w.GetTranslate(); + float distSq = hsVector3(&pos, &locPlayerPos).MagnitudeSquared(); + + fTransport.GetMember(i)->SetDistSq(distSq); + + // I can't listen to players that are more than 50 ft away + if (distSq>plNetListenList::kMaxListenDistSq) + continue; + // if we are p2p and member isn't, skip them. + if ( IsPeerToPeer() && !fTransport.GetMember(i)->IsPeerToPeer() ) + continue; + // otherwise, we aren't p2p so just update the listen list + // normally so it will update in the gui as distance changes. +#else + float distSq=1; +#endif + // if we have < 3 elements in the list, grow the list, or, + // if obj is closer than item 3, add it to the list. + // keep the list (3) elements sorted. + if (plNetListenList::kMaxListenListSize==-1 || + newListenList.size()CheckForceSynch() +#endif + ); + } + // update talkList based on listenList + if (changed) + { + +#ifdef SAME_TALK_AND_LISTEN + SynchTalkList(); +#endif + // notify KI, member distances have been updated + plMemberUpdateMsg* mu = TRACKED_NEW plMemberUpdateMsg; + mu->Send(); + } + + return changed; +} + + +void plNetClientMgr::SynchTalkList() +{ + GetTalkList()->Clear(); + int i; + for(i=0;iGetNumMembers(); i++) + GetTalkList()->AddMember(GetListenList()->GetMember(i)); +} + +void plNetClientMgr::SetListenListMode(int i) +{ + // set new mode, clear list and force update + fListenListMode = i; + GetListenList()->Clear(); + GetListenList()->SetLastUpdateTime(0.f); +} + +void plNetClientMgr::IHandleNetVoiceListMsg(plNetVoiceListMsg* msg) +{ + if (msg->GetCmd() == plNetVoiceListMsg::kForcedListenerMode) + { + // first make sure this message applies to us: + int i; + bool included = false; + for (i = 0; i < msg->GetClientList()->Count(); i++) + { + if (msg->GetClientList()->AcquireArray()[i] == NetCommGetPlayer()->playerInt) + { + included = true; + break; + } + } + if (!included) + return; + SetListenListMode(kListenList_Forced); + // add in the members we receive from python + for (i = 0; i < msg->GetClientList()->Count(); i++) + { + plNetTransportMember **members = nil; + plNetClientMgr::GetInstance()->TransportMgr().GetMemberListDistSorted( members ); + + if( members != nil) + { + for(int j= 0; j < plNetClientMgr::GetInstance()->TransportMgr().GetNumMembers(); j++ ) + { + plNetTransportMember *mbr = members[ j ]; + if( mbr != nil && mbr->GetAvatarKey() != nil && mbr->GetPlayerID() == msg->GetClientList()->AcquireArray()[i]) + { + plNetClientMgr::GetInstance()->GetListenList()->AddMember(mbr); + } + } + } + delete [] members; + } + } + else + if (msg->GetCmd() == plNetVoiceListMsg::kDistanceMode) + { + // again, see that it is us that we care about: + if (msg->GetRemovedKey() == GetLocalPlayerKey()) + SetListenListMode(kListenList_Distance); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.cpp new file mode 100644 index 00000000..03949fb1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.cpp @@ -0,0 +1,593 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTimer.h" +#include "plNetClientMgr.h" +#include "plNetClientMsgHandler.h" +#include "hsResMgr.h" +#include "plCreatableIndex.h" +#include "plgDispatch.h" +#include "plNetLinkingMgr.h" +#include "plCCRMgrBase.h" + +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plObjInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plClientMsg.h" +//#include "../pnMessage/plWarpMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pnMessage/plPlayerPageMsg.h" +#include "../pnFactory/plCreator.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../pnNetCommon/plSDLTypes.h" + +#include "../plAudible/plWinAudible.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../plMessage/plMemberUpdateMsg.h" +#include "../plMessage/plNetOwnershipMsg.h" +#include "../plMessage/plCCRMsg.h" +#include "../plVault/plVault.h" +#include "../plSDL/plSDL.h" +#include "../plNetCommon/plNetCommonConstants.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plNetMessage/plNetCommonMessage.h" + +#include "../../FeatureLib/pfMessage/pfKIMsg.h" // Should be moved to PubUtil level + +//////////////////////////////////////////////////////////////////////// + +plNetClientMsgHandler::plNetClientMsgHandler(plNetClientMgr * mgr) +{ + SetNetApp(mgr); +} + +plNetClientMsgHandler::~plNetClientMsgHandler() +{ +} + +plNetClientMgr * plNetClientMsgHandler::IGetNetClientMgr() +{ + return plNetClientMgr::ConvertNoRef(GetNetApp()); +} + +int plNetClientMsgHandler::PeekMsg(plNetMessage * netMsg) +{ + plNetClientMgr * nc = IGetNetClientMgr(); + int cnt = -1; + if (netMsg->GetNetCoreMsg()) // && !netMsg->Peeked()) // not needed + { + cnt = netMsg->PeekBuffer(netMsg->GetNetCoreMsg()->GetData(), netMsg->GetNetCoreMsg()->GetLen()); + hsAssert(cnt,"0 length message"); + } + return cnt; +} + +void plNetClientMsgHandler::IFillInTransportMember(const plNetMsgMemberInfoHelper* mbi, plNetTransportMember* mbr) +{ + const plNetClientMgr* nc=IGetNetClientMgr(); + UInt16 port = mbi->GetClientGuid()->GetSrcPort(); + UInt32 addr = mbi->GetClientGuid()->GetSrcAddr(); + UInt32 flags = mbi->GetFlags(); + UInt32 plrID = mbi->GetClientGuid()->GetPlayerID(); + plUoid avUoid = mbi->GetAvatarUoid(); + plKey avKey=hsgResMgr::ResMgr()->FindKey(avUoid); + + mbr->SetPlayerName(mbi->GetClientGuid()->GetPlayerName()); + mbr->SetFlags(flags); + mbr->SetPlayerID(plrID); + mbr->SetCCRLevel(mbi->GetClientGuid()->GetCCRLevel()); + if (avKey) + mbr->SetAvatarKey(avKey); +} + +int plNetClientMsgHandler::ReceiveMsg(plNetMessage *& netMsg) +{ +#ifdef HS_DEBUGGING + //plNetClientMgr::GetInstance()->DebugMsg(" %s", netMsg->ClassName()); +#endif + + plNetClientMgr::GetInstance()->UpdateServerTimeOffset(netMsg); + + switch(netMsg->ClassIndex()) + { + default: + plNetClientMgr::GetInstance()->ErrorMsg( "Unknown msg: %s", netMsg->ClassName() ); + return hsFail; + + MSG_HANDLER_CASE(plNetMsgTerminated) + MSG_HANDLER_CASE(plNetMsgGroupOwner) + + case CLASS_INDEX_SCOPED(plNetMsgSDLStateBCast): + MSG_HANDLER_CASE(plNetMsgSDLState) + + case CLASS_INDEX_SCOPED(plNetMsgGameMessageDirected): + case CLASS_INDEX_SCOPED(plNetMsgLoadClone): + MSG_HANDLER_CASE(plNetMsgGameMessage) + + MSG_HANDLER_CASE(plNetMsgVoice) + MSG_HANDLER_CASE(plNetMsgMembersList) + MSG_HANDLER_CASE(plNetMsgMemberUpdate) + MSG_HANDLER_CASE(plNetMsgListenListUpdate) + MSG_HANDLER_CASE(plNetMsgInitialAgeStateSent) + } +} + +//////////////////////////////////////////////////////////////////// + +MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgTerminated) +{ + return hsOK; +} + +MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgGroupOwner) +{ + plNetClientMgr* nc = IGetNetClientMgr(); + plNetMsgGroupOwner* m = plNetMsgGroupOwner::ConvertNoRef(netMsg); + PeekMsg(m); + +/* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap + hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", + m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); +*/ + + /* + plNetOwnershipMsg* netOwnMsg = TRACKED_NEW plNetOwnershipMsg; + + int i; + for(i=0;iGetNumGroups();i++) + { + plNetMsgGroupOwner::GroupInfo gr=m->GetGroupInfo(i); + netOwnMsg->AddGroupInfo(gr); + nc->GetNetGroups()->SetGroup(gr.fGroupID, gr.fOwnIt!=0 ? true : false); + hsLogEntry( nc->DebugMsg("\tGroup 0x%x, ownIt=%d\n", (const char*)gr.fGroupID.Room().GetSequenceNumber(), gr.fOwnIt) ); + } + + if (netOwnMsg->GetNumGroups()) + netOwnMsg->Send(); + else + delete netOwnMsg; + */ + + nc->SetObjectOwner(m->IsOwner()); + + return hsOK; +} + + + +MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgSDLState) +{ + plNetClientMgr* nc = IGetNetClientMgr(); + plNetMsgSDLState* m = plNetMsgSDLState::ConvertNoRef(netMsg); + PeekMsg(m); + +/* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap + hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", + m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); +*/ + + UInt32 rwFlags = 0; + + if ( m->IsInitialState() ) + { + nc->IncNumInitialSDLStates(); + rwFlags |= plSDL::kMakeDirty; // if initial state, we want all vars. + } + else if ( nc->GetFlagsBit( plNetClientApp::kLoadingInitialAgeState ) ) + { + if ( nc->GetFlagsBit( plNetClientApp::kNeedInitialAgeStateCount ) ) + { + hsLogEntry( nc->DebugMsg( "Ignoring SDL state because we are still joining age and don't have initial age state count yet." ) ); + return hsOK; + } + if ( nc->GetNumInitialSDLStates()GetRequiredNumInitialSDLStates() ) + { + hsLogEntry( nc->DebugMsg( "Ignoring SDL state because we are still joining age and have not received all initial state yet." ) ); + return hsOK; + } + hsLogEntry( nc->DebugMsg( "We are still joining age, but have all initial states. Accepting this state (risky?)." ) ); + } + + + // extract stateDataRecord from msg + hsReadOnlyStream stream(m->StreamInfo()->GetStreamLen(), m->StreamInfo()->GetStreamBuf()); + char* descName = nil; + int ver; + plStateDataRecord::ReadStreamHeader(&stream, &descName, &ver); + plStateDescriptor* des = plSDLMgr::GetInstance()->FindDescriptor(descName, ver); + + if (strcmpi(descName, kSDLAvatarPhysical) == 0) + rwFlags |= plSDL::kKeepDirty; + + // + // ERROR CHECK SDL FILE + // + plStateDataRecord* sdRec = des ? TRACKED_NEW plStateDataRecord(des) : nil; + if (!sdRec || sdRec->GetDescriptor()->GetVersion()!=ver) + { + std::string err; + if (!sdRec) + err = xtl::format( "SDL descriptor %s missing, v=%d", descName, ver); + else + err = xtl::format( "SDL descriptor %s, version mismatch, server v=%d, client v=%d", + descName, ver, sdRec->GetDescriptor()->GetVersion()); + + hsAssert(false, err.c_str()); + nc->ErrorMsg(const_cast(err.c_str())); + + // Post Quit message + nc->QueueDisableNet(true, "SDL Desc Problem"); + delete sdRec; + } + else if( sdRec->Read( &stream, 0, rwFlags ) ) + { + plStateDataRecord* stateRec = nil; + if (m->IsInitialState()) + { + stateRec = TRACKED_NEW plStateDataRecord(des); + stateRec->SetFromDefaults(false); + stateRec->UpdateFrom(*sdRec, rwFlags); + + delete sdRec; + } + else + stateRec = sdRec; + + plNetClientMgr::PendingLoad* pl = TRACKED_NEW plNetClientMgr::PendingLoad(); + pl->fSDRec = stateRec; // will be deleted when PendingLoad is processed + if (m->GetHasPlayerID()) + pl->fPlayerID = m->GetPlayerID(); // copy originating playerID if we have it + pl->fUoid = m->ObjectInfo()->GetUoid(); + + // queue up state + nc->fPendingLoads.push_back(pl); + hsLogEntry( nc->DebugMsg( "Added pending SDL delivery for %s:%s", m->ObjectInfo()->GetObjectName(), des->GetName() ) ); + } + else + delete sdRec; + + delete [] descName; // We've only used descName for a lookup (via SDR, and some error strings. Must delete now. + + return hsOK; +} + +MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgGameMessage) +{ + plNetClientMgr* nc = IGetNetClientMgr(); + plNetMsgGameMessage* m = plNetMsgGameMessage::ConvertNoRef(netMsg); + if (m) + { + PeekMsg(m); + + plNetMsgLoadClone * lcMsg = plNetMsgLoadClone::ConvertNoRef( m ); + if ( lcMsg ) + { + if ( lcMsg->GetIsInitialState() ) + { + nc->IncNumInitialSDLStates(); + } + } + + hsReadOnlyStream stream(m->StreamInfo()->GetStreamLen(), m->StreamInfo()->GetStreamBuf()); + plMessage* gameMsg = plMessage::ConvertNoRef(hsgResMgr::ResMgr()->ReadCreatable(&stream)); + hsAssert(gameMsg, "nil game msg?"); + + if (gameMsg) + { + /* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES!!! -eap + hsLogEntry( nc->DebugMsg(" %s, %s, sndr %s rcvr %s sz=%d", + m->ClassName(), m->AsStdString().c_str(), + gameMsg->GetSender() ? gameMsg->GetSender()->GetName() : "?", + gameMsg->GetNumReceivers() ? gameMsg->GetReceiver(0)->GetName() : "?", + m->GetNetCoreMsgLen()) ); + */ + + if (lcMsg) + { + if (!lcMsg->GetIsLoading()) + { + plLoadAvatarMsg* unloadClone = plLoadAvatarMsg::ConvertNoRef(gameMsg); + if (unloadClone) + { + plLoadAvatarMsg* unloadMsg = TRACKED_NEW plLoadAvatarMsg(unloadClone->GetCloneKey(), unloadClone->GetRequestorKey(), unloadClone->GetUserData(), unloadClone->GetIsPlayer(), false); + unloadMsg->SetOriginatingPlayerID(unloadClone->GetOriginatingPlayerID()); + gameMsg = unloadMsg; + } + } + else + { + plLoadCloneMsg* loadClone = plLoadCloneMsg::ConvertNoRef(gameMsg); + if (loadClone) + { + int idx = nc->fTransport.FindMember(loadClone->GetOriginatingPlayerID()); + if (idx == -1) + { + hsLogEntry( nc->DebugMsg( "Ignoring load clone because player isn't in our players list: %d", loadClone->GetOriginatingPlayerID()) ); + return hsOK; + } + } + } + } + + plNetClientApp::UnInheritNetMsgFlags(gameMsg); + gameMsg->SetBCastFlag(plMessage::kNetCreatedRemotely); + + if (!m->GetDeliveryTime().AtEpoch()) + { + double timeStamp; + double secs=hsTimer::GetSysSeconds(); + m->GetDeliveryTime().ConvertToGameTime(&timeStamp, secs); + hsAssert(timeStamp>=secs, "invalid future timeStamp"); + gameMsg->SetTimeStamp(timeStamp); + nc->DebugMsg("Converting game msg future timeStamp, curT=%f, futT=%f", secs, timeStamp); + } + + plgDispatch::Dispatch()->MsgSend(gameMsg); + + // Debug + if (m->GetHasPlayerID()) + { + int idx=nc->fTransport.FindMember(m->GetPlayerID()); + plNetTransportMember* mbr = idx != -1 ? nc->fTransport.GetMember(idx) : nil; + if (mbr) + mbr->SetTransportFlags(mbr->GetTransportFlags() | plNetTransportMember::kSendingActions); + } + return hsOK; + } + } + return hsFail; +} + +MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgVoice) +{ + plNetClientMgr* nc = IGetNetClientMgr(); + plNetMsgVoice* m = plNetMsgVoice::ConvertNoRef(netMsg); + PeekMsg(m); + +/* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap + hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", + m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); +*/ + + int bufLen = m->GetVoiceDataLen(); + const char* buf = m->GetVoiceData(); + BYTE flags = m->GetFlags(); + BYTE numFrames = m->GetNumFrames(); + plKey key = NULL; + + // plKey key=hsgResMgr::ResMgr()->FindKey(m->ObjectInfo()->GetUoid()); + + + + // Filter ignored sender + if ( VaultAmIgnoringPlayer( m->GetPlayerID() ) ) + { + hsLogEntry( nc->DebugMsg( "Ignoring voice chat from ignored player %lu", m->GetPlayerID() ) ); + return hsOK; + } + + int idx=nc->fTransport.FindMember(m->GetPlayerID()); + plNetTransportMember* mbr = idx != -1 ? nc->fTransport.GetMember(idx) : nil; + + if (mbr) + { + key = mbr->GetAvatarKey(); + // filter based on listen/talk list (for forced mode) + if (nc->GetListenListMode() == plNetClientMgr::kListenList_Forced) + { + if (nc->GetListenList()->FindMember( mbr )) + { + hsLogEntry( nc->DebugMsg( "Ignoring voice chat from ignored player %lu", m->GetPlayerID() ) ); + return hsOK; + } + } + mbr->SetTransportFlags(mbr->GetTransportFlags() | plNetTransportMember::kSendingVoice); + } + + +// hsKeyedObject* obj = key ? key->ObjectIsLoaded() : nil; + plSceneObject* avObj = key ? plSceneObject::ConvertNoRef( key->ObjectIsLoaded() ) : nil; + +// if (obj) + if (avObj) + { + plAudible * aud = avObj->GetAudioInterface()->GetAudible(); + pl2WayWinAudible* pAud = pl2WayWinAudible::ConvertNoRef(aud); + if (pAud) + pAud->PlayNetworkedSpeech(buf, bufLen, numFrames, flags); + else + { + nc->ErrorMsg("\tObject doesn't have audible"); + } + } + else + { + nc->DebugMsg("\tCan't find loaded object\n"); + } + return hsOK; +} + + +MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgMembersList) +{ + plNetClientMgr* nc = IGetNetClientMgr(); + plNetMsgMembersList* m = plNetMsgMembersList::ConvertNoRef(netMsg); + PeekMsg(m); + +/* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap + hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", + m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); +*/ + + int i; + + // remove existing members, except server + for( i=nc->fTransport.GetNumMembers()-1 ; i>=0; i-- ) + { + if (!nc->fTransport.GetMember(i)->IsServer()) + { + nc->fTransport.RemoveMember(i); + } + } // for + + // update the members list from the msg. + // this app is not one of the members in the msg + for( i=0 ;iMemberListInfo()->GetNumMembers() ;i++ ) + { + plNetTransportMember* mbr = TRACKED_NEW plNetTransportMember(nc); + IFillInTransportMember(m->MemberListInfo()->GetMember(i), mbr); + hsLogEntry(nc->DebugMsg("\tAdding transport member, name=%s, p2p=%d, plrID=%d\n", mbr->AsStdString().c_str(), mbr->IsPeerToPeer(), mbr->GetPlayerID())); + int idx=nc->fTransport.AddMember(mbr); + hsAssert(idx>=0, "Failed adding member?"); + + } // for + + // new player has been aded send local MembersUpdate msg + plMemberUpdateMsg* mu = TRACKED_NEW plMemberUpdateMsg; + mu->Send(); + + return hsOK; +} + +MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgMemberUpdate) +{ + plNetClientMgr* nc = IGetNetClientMgr(); + plNetMsgMemberUpdate* m = plNetMsgMemberUpdate::ConvertNoRef(netMsg); + PeekMsg(m); + +/* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap + hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", + m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); +*/ + + if (m->AddingMember()) + { + plNetTransportMember* mbr=nil; + int idx = nc->fTransport.FindMember(m->MemberInfo()->GetClientGuid()->GetPlayerID()); + if ( idx>=0 ) + mbr = nc->fTransport.GetMember(idx); + else + mbr = TRACKED_NEW plNetTransportMember(nc); + hsAssert(mbr, "nil xport member"); + IFillInTransportMember(m->MemberInfo(), mbr); + + if ( idx<0 ) + { // didn't find him + if ( nc->fTransport.AddMember(mbr)<0 ) + delete mbr; // delete newly created member + } + } + else + { + int idx=nc->fTransport.FindMember(m->MemberInfo()->GetClientGuid()->GetPlayerID()); + if (idx<0) + { + hsLogEntry( nc->DebugMsg("\tCan't find member to remove.") ); + } + else + { + nc->fTransport.RemoveMember(idx); + } + } + + // new player has been aded send local MembersUpdate msg + plMemberUpdateMsg* mu = TRACKED_NEW plMemberUpdateMsg; + mu->Send(); + + return hsOK; +} + +MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgListenListUpdate) +{ + plNetClientMgr* nc = IGetNetClientMgr(); + plNetMsgListenListUpdate* m = plNetMsgListenListUpdate::ConvertNoRef(netMsg); + PeekMsg(m); + +/* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap + hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", + m->ClassName(), m->AsStdString().c_str(), m->GetNetCoreMsgLen()) ); +*/ + + int idx=nc->fTransport.FindMember(m->GetPlayerID()); + plNetTransportMember* tm = (idx==-1 ? nil : nc->fTransport.GetMember(idx)); + if(!tm) + { +#if 0 + tm = TRACKED_NEW plNetTransportMember(nc); + tm->SetClientNum(m->GetSenderClientNum()); + int idx=nc->fTransport.AddMember(tm); + hsAssert(idx>=0, "Failed adding member?"); + nc->DebugMsg("ListenListUpdate msg: Adding member on the fly\n"); +#endif + return hsOK; + } + + if (m->GetAdding()) + { + // add the sender to my talk list + nc->GetTalkList()->AddMember(tm); + } + else + { + // remove the sender from my talk list + nc->GetTalkList()->RemoveMember(tm); + } + + return hsOK; +} + +MSG_HANDLER_DEFN(plNetClientMsgHandler,plNetMsgInitialAgeStateSent) +{ + plNetClientMgr * nc = IGetNetClientMgr(); + plNetMsgInitialAgeStateSent* msg = plNetMsgInitialAgeStateSent::ConvertNoRef(netMsg); + PeekMsg(msg); + +/* !!! THIS LOG MSG CRASHES THE CLIENT SOMETIMES! -eap + hsLogEntry( nc->DebugMsg(" %s, %s, sz=%d", + netMsg->ClassName(), netMsg->AsStdString().c_str(), netMsg->GetNetCoreMsgLen()) ); +*/ + + nc->DebugMsg( "Initial age SDL count: %d", msg->GetNumInitialSDLStates( ) ); + + nc->SetRequiredNumInitialSDLStates( msg->GetNumInitialSDLStates() ); + nc->SetFlagsBit( plNetClientApp::kNeedInitialAgeStateCount, false ); + + if (nc->GetNumInitialSDLStates() >= nc->GetRequiredNumInitialSDLStates()) { + nc->ICheckPendingStateLoad(hsTimer::GetSysSeconds()); + nc->NotifyRcvdAllSDLStates(); + } + + return hsOK; +} + +//////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.h new file mode 100644 index 00000000..86ed2d1d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgHandler.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetClientMsgHandler_inc +#define plNetClientMsgHandler_inc + +#include "../plNetCommon/plNetMsgHandler.h" +#include "hsStlUtils.h" + +/////////////////////////////////////////////////////////////////// + +// +// define msg handler fxn for netClient msgs +// +class plNetClientMgr; +class plNetMsgMemberInfoHelper; +class plNetTransportMember; + +/////////////////////////////////////////////////////////////////// + +class plNetClientMsgHandler : public plNetMsgHandler +{ +//protected: + enum // SendOrTimeOut return values and meanings + { + kTimedOutNoRetry = -1, + kDoNothing = 0, + kSend = 1, + }; + plNetClientMgr * IGetNetClientMgr(); + void IFillInTransportMember(const plNetMsgMemberInfoHelper* mbi, plNetTransportMember* mbr); +public: + plNetClientMsgHandler(plNetClientMgr * mgr); + ~plNetClientMsgHandler(); + + int ReceiveMsg(plNetMessage *& netMsg); + int PeekMsg(plNetMessage * netMsg); // return msgsize on success. -1 on error. + + MSG_HANDLER_DECL(plNetMsgTerminated) + MSG_HANDLER_DECL(plNetMsgGroupOwner) + MSG_HANDLER_DECL(plNetMsgSDLState) + MSG_HANDLER_DECL(plNetMsgGameMessage) + MSG_HANDLER_DECL(plNetMsgVoice) + MSG_HANDLER_DECL(plNetMsgMembersList) + MSG_HANDLER_DECL(plNetMsgMemberUpdate) + MSG_HANDLER_DECL(plNetMsgListenListUpdate) + MSG_HANDLER_DECL(plNetMsgInitialAgeStateSent) +}; + +#endif // plNetClientMsgHandler_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.cpp new file mode 100644 index 00000000..7d1abedb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.cpp @@ -0,0 +1,109 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetClientMsgScreener.h" +#include "plNetLinkingMgr.h" + +#include "../pnNetCommon/plNetApp.h" +#include "../pnMessage/plMessage.h" + +#include "../plStatusLog/plStatusLog.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plArmatureMod.h" + +/////////////////////////////////////////////////////////////// +// CLIENT Version +/////////////////////////////////////////////////////////////// + +plNetClientMsgScreener::plNetClientMsgScreener() +{ + DebugMsg("created"); +} + +// +// For plLoggable base +// +void plNetClientMsgScreener::ICreateStatusLog() const +{ + fStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog(40, "NetScreener.log", + plStatusLog::kTimestamp | plStatusLog::kFilledBackground | plStatusLog::kAlignToTop); +} + +// +// return cur age name +// +const char* plNetClientMsgScreener::IGetAgeName() const +{ + plNetLinkingMgr *lm = plNetLinkingMgr::GetInstance(); + return lm && lm->GetAgeLink()->GetAgeInfo() ? lm->GetAgeLink()->GetAgeInfo()->GetAgeFilename() : "?"; +} + +// +// Check if key is local avatar +// +bool plNetClientMsgScreener::IIsLocalAvatarKey(plKey key, const plNetGameMember* gm) const +{ + return (!key || key==plNetClientApp::GetInstance()->GetLocalPlayerKey()); +} + +bool plNetClientMsgScreener::IIsLocalArmatureModKey(plKey key, const plNetGameMember* gm) const +{ + plKey playerKey = plNetClientApp::GetInstance()->GetLocalPlayerKey(); + plArmatureMod* aMod = playerKey ? plAvatarMgr::GetInstance()->FindAvatar(playerKey) : nil; + return (!key || key==(aMod ? aMod->GetKey() : nil)); +} + +// +// Check if CCR +// +bool plNetClientMsgScreener::IIsSenderCCR(const plNetGameMember* gm) const +{ + return plNetClientApp::GetInstance()->AmCCR(); +} + +// +// return true if msg is allowed/accepted as a net msg +// +bool plNetClientMsgScreener::AllowMessage(const plMessage* msg) const +{ + if (!msg) + return false; + + Answer ans=IAllowMessageType(msg->ClassIndex()); + if (ans==kYes) + return true; + if (ans==kNo) + { + // WarningMsg("Quick-reject net propagated msg %s", msg->ClassName()); + return false; + } + + if (!IValidateMessage(msg)) + { + // WarningMsg("Validation failed. Blocking net propagated msg %s", msg->ClassName()); + return false; + } + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.h new file mode 100644 index 00000000..e61fd5fd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMsgScreener.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetClientMsgScreener_h +#define plNetClientMsgScreener_h + +#include "../plNetCommon/plNetMsgScreener.h" + +// +// Client-side version +// +class plNetClientMsgScreener : public plNetMsgScreener +{ +protected: + void ICreateStatusLog() const; + const char* IGetSenderName(const plNetGameMember* gm) const { return "local"; } + const char* IGetAgeName() const; + bool IIsLocalAvatarKey(plKey key, const plNetGameMember* gm) const; + bool IIsLocalArmatureModKey(plKey key, const plNetGameMember* gm) const; + bool IIsSenderCCR(const plNetGameMember* gm=nil) const; + bool IAmClient() const { return true; } +public: + + plNetClientMsgScreener(); + + bool AllowMessage(const plMessage* msg) const; +}; + + +#endif // plNetClientMsgScreener_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientStats.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientStats.cpp new file mode 100644 index 00000000..ec5f8567 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientStats.cpp @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetClientStats.h" +#include "plNetClientMgr.h" +#include "hsTimer.h" + + +plNetClientStats::plNetClientStats() : + fNumVaultMsgsSent(0), + fNumVaultMsgsRcvd(0), + fVaultMsgSentBytes(0), + fVaultMsgRcvdBytes(0), + fAgeStatsULBits(0), + fAgeStatsDLBits(0), + fAgeStatsLinkInTime(0) +{ + +} +// +// return the avg UL bps used so far in this age +// +float plNetClientStats::GetAgeStatsULBitsPerSec() const +{ + double elapsedAgeTime = hsTimer::GetSeconds() - fAgeStatsLinkInTime; + return (float)(fAgeStatsULBits/elapsedAgeTime); +} + +// +// return the acks that were conpressed so far +// +UInt32 plNetClientStats::GetRecvdMultipleAcks() const +{ + return fRecvdMultipleAcks; +} + +// +// return the avg DL bps used so far in this age +// +float plNetClientStats::GetAgeStatsDLBitsPerSec() const +{ + double elapsedAgeTime = hsTimer::GetSeconds() - fAgeStatsLinkInTime; + return (float)(fAgeStatsDLBits/elapsedAgeTime); +} + +// +// Accumulate the UL/DL bits & acks used from the netCore state +// +void plNetClientStats::UpdateAgeStats() +{ + plNetClientMgr* nc=plNetClientMgr::GetInstance(); + +#if 0 + fAgeStatsDLBits += nc->GetNetCore()->GetStats()->GetDLBits(); + fAgeStatsULBits += nc->GetNetCore()->GetStats()->GetULBits(); + fRecvdMultipleAcks += nc->GetNetCore()->GetStats()->GetRecvdMultipleAcks(); +#endif +} + + +// +// Call when you join an age +// +void plNetClientStats::ResetAgeStats() +{ + fAgeStatsDLBits = fAgeStatsULBits = 0; + fRecvdMultipleAcks = 0; + fAgeStatsLinkInTime = hsTimer::GetSeconds(); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientStats.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientStats.h new file mode 100644 index 00000000..a246abeb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientStats.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetClientStats_h +#define plNetClientStats_h + +#include "hsTypes.h" + +// +// Holds stats counters used by the Net Client +// + +class plNetClientStats +{ +public: + plNetClientStats(); + + // + // Vault Stats + // +private: + // Vault msg counters + UInt32 fNumVaultMsgsSent; + UInt32 fNumVaultMsgsRcvd; + UInt32 fVaultMsgSentBytes; + UInt32 fVaultMsgRcvdBytes; +public: + // msg counters + UInt32 GetNumVaultMsgsSent() const { return fNumVaultMsgsSent; } + UInt32 GetNumVaultMsgsRcvd() const { return fNumVaultMsgsRcvd; } + UInt32 GetVaultMsgSentBytes() const { return fVaultMsgSentBytes; } + UInt32 GetVaultMsgRcvdBytes() const { return fVaultMsgRcvdBytes; } + void TallyVaultMsgSent( int size ) { fNumVaultMsgsSent++; fVaultMsgSentBytes+=size; } + void TallyVaultMsgRcvd( int size ) { fNumVaultMsgsRcvd++; fVaultMsgRcvdBytes+=size; } + void ResetVaultMsgCounters() { fNumVaultMsgsSent=fNumVaultMsgsRcvd=fVaultMsgSentBytes=fVaultMsgRcvdBytes=0; } + + // + // NetClient BW Stats, kept per age + // +private: + int fAgeStatsULBits, fAgeStatsDLBits; + UInt32 fRecvdMultipleAcks; + double fAgeStatsLinkInTime; + +public: + void UpdateAgeStats(); + void ResetAgeStats(); + float GetAgeStatsULBitsPerSec() const; + float GetAgeStatsDLBitsPerSec() const; + UInt32 GetRecvdMultipleAcks() const; +}; + +#endif // plNetClientStats_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVNodeMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVNodeMgr.cpp new file mode 100644 index 00000000..194bc004 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVNodeMgr.cpp @@ -0,0 +1,164 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetClientMgr.h" +#include "plNetClientVNodeMgr.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plGImage/plMipmap.h" +#include "../plJPEG/plJPEG.h" +#include "../plVault/plVault.h" +#include "hsResMgr.h" +#include "../pnMessage/plRefMsg.h" +#include "../plVault/plDniCoordinateInfo.h" +#include "../plVault/plAgeInfoSource.h" +#include "plNetLinkingMgr.h" +#include "../plStatusLog/plStatusLog.h" +#include "../plClientState/plClientStateMgr.h" +#include "../plSDL/plSDL.h" +#include "../plAgeLoader/plAgeLoader.h" + +#include "../../FeatureLib/pfMessage/pfKIMsg.h" + + +//////////////////////////////////////////////////////////////////// + +class plNetClientAgeInfoSource : public plAgeInfoSource +{ + plDniCoordinateInfo fNilCoords; // TEMPORARY +public: + const plDniCoordinateInfo * GetAgeCoords( void ) const + { + return &fNilCoords; // tmp + } + const plUnifiedTime * GetAgeTime( void ) const + { + static plUnifiedTime ut; + ut.SetSecsDouble(plNetClientMgr::GetInstance()->GetCurrentAgeElapsedSeconds()); + return &ut; + } + const char * GetAgeName( void ) const + { + return plNetLinkingMgr::GetInstance()->GetAgeLink()->GetAgeInfo()->GetAgeInstanceName(); + } + const plUUID * GetAgeGuid( void ) const + { + return plNetLinkingMgr::GetInstance()->GetAgeLink()->GetAgeInfo()->GetAgeInstanceGuid(); + } + static plNetClientAgeInfoSource * GetInstance( void ) + { + static plNetClientAgeInfoSource Me; + return &Me; + } +}; + + +//// Image/Mipmap Conversion ////////////////////////////////////////////////// + +hsBool StuffImageIntoNode( plMipmap * src, RelVaultNode * dst ) +{ + VaultImageNode image(dst); + hsRAMStream ramStream; + + // Create our JPEG stream + plJPEG::Instance().SetWriteQuality( 30 ); // In percent quality + + if( !plJPEG::Instance().WriteToStream( &ramStream, src ) ) + return false; + + unsigned bytes = ramStream.GetEOF(); + byte * buffer = (byte *)ALLOC(bytes); + ramStream.CopyToMem(buffer); + + image.SetImageData(buffer, bytes); + + image.SetImageType( VaultImageNode::kJPEG ); + + // possibly make a plKey for the mipmap. + return ExtractImageFromNode( dst ); +} + +hsBool ExtractImageFromNode( RelVaultNode * src) +{ + // no id? exit now. we will be called again when element is given an id. + if ( src->nodeId == 0 ) + return false; + + VaultCliImageNode image(src); + + // already have a mipmap and it has a key? release it + if ( image.fMipmap && image.fMipmap->GetKey()!=nil ) + { + plNetClientMgr::GetInstance()->GetKey()->Release( image.fMipmap->GetKey() ); + image.fMipmap = nil; + } + + // convert image data to a plMipmap + switch( image.imgType ) + { + case VaultImageNode::kJPEG: + { + // Copy to a RAM stream so the JPEG class is happy + hsRAMStream ramStream; + ramStream.Write( image.imgDataLen, image.imgData ); + ramStream.Rewind(); + // create mipmap from image data + image.fMipmap = plJPEG::Instance().ReadFromStream( &ramStream ); + } + break; + + default: + { + hsAssert( false, "ExtractImageFromNode: Invalid image type" ); + return false; // Invalid image type + } + } + + if ( !image.fMipmap ) + { + hsAssert( false, "ExtractImageFromNode failed" ); + return false; + } + + // we now have a mipmap, but it doesn't have a key. make a key for it + static int UniqueIdentifier = 0; + + char keyName[512]; + sprintf( keyName, "VaultImage_%lu_%d", src->nodeId, UniqueIdentifier++ ); + + // create a key for the mipmap + plKey imageKey = hsgResMgr::ResMgr()->NewKey( keyName, image.fMipmap, plLocation::kGlobalFixedLoc ); + + // ref the image key + hsgResMgr::ResMgr()->AddViaNotify( imageKey, TRACKED_NEW plGenRefMsg( + plNetClientMgr::GetInstance()->GetKey(), plRefMsg::kOnCreate, 0, plNetClientMgr::kVaultImage ), + plRefFlags::kActiveRef ); + + return ( image.fMipmap->GetKey()!=nil ); +} + + +//////////////////////////////////////////////////////////////////// +// End. + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVault.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVault.cpp new file mode 100644 index 00000000..c8529235 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVault.cpp @@ -0,0 +1,234 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetClientMgr.h" +#include "plNetClientVault.h" +#include "../pnNetCommon/plNetMsg.h" +#include "../plGImage/plMipmap.h" +#include "../plJPEG/plJPEG.h" +#include "../plVault/plVaultTasks.h" +#include "hsResMgr.h" +#include "../pnMessage/plRefMsg.h" +#include "../plVault/plDniCoordinateInfo.h" +#include "../plVault/plAgeInfoSource.h" + +//////////////////////////////////////////////////////////////////// + +class plNetClientAgeInfoSource : public plAgeInfoSource +{ + plDniCoordinateInfo fNilCoords; // TEMPORARY +public: + const plDniCoordinateInfo * GetAgeCoords( void ) const + { + return &fNilCoords; // tmp + } + const plUnifiedTime * GetAgeTime( void ) const + { + static plUnifiedTime ut; + ut.SetSecsDouble(plNetClientMgr::GetInstance()->GetCurrentAgeElapsedSeconds()); + return &ut; + } + const char * GetAgeName( void ) const + { + return plNetClientMgr::GetInstance()->GetAgeName(); + } + const plServerGuid * GetAgeGuid( void ) const + { + return plNetClientMgr::GetInstance()->GetCurrSessionInfo()->GetServerGuid(); + } + static plNetClientAgeInfoSource * GetInstance( void ) + { + static plNetClientAgeInfoSource Me; + return &Me; + } +}; + +//////////////////////////////////////////////////////////////////// + +plNetClientVault::plNetClientVault() +{ +} + +plNetApp * plNetClientVault::GetNetApp( void ) const +{ + return plNetClientMgr::GetInstance(); +} + +plAgeInfoSource * plNetClientVault::GetAgeInfo( void ) const +{ + return plNetClientAgeInfoSource::GetInstance(); +} + + +void plNetClientVault::IInitNode( plVaultNode * node ) +{ + plVaultImageNode * IMG = plVaultImageNode::ConvertNoRef( node ); + if ( IMG ) + { + plNetClientVault::ExtractImageFromNode( IMG ); + } +} + +void plNetClientVault::IFiniNode( plVaultNode * node ) +{ + plVaultImageNode * IMG = plVaultImageNode::ConvertNoRef( node ); + if ( IMG && IMG->GetMipmap() ) + { + plNetClientMgr::GetInstance()->GetKey()->Release( IMG->GetMipmap()->GetKey() ); + IMG->ISetMipmap( nil ); + } +} + +int plNetClientVault::ISendNetMsg( plNetMsgVault * msg, UInt32 sendFlags ) +{ + return plNetClientMgr::GetInstance()->SendMsg( msg, sendFlags ); +} + +void plNetClientVault::IOnTaskTimedOut( plVaultTask * task ) +{ + std::string msg; + xtl::format( msg, "KI task timed out: %s", task->ClassName() ); + plNetClientMgr::GetInstance()->OnNetFailure( msg.c_str(), true ); +} + +bool plNetClientVault::IAmOnline( void ) const +{ + return plNetClientMgr::GetInstance()->IsEnabled()!=0; +} + +//// Image/Mipmap Conversion ////////////////////////////////////////////////// + +hsBool plNetClientVault::StuffImageIntoNode( plMipmap * src, plVaultImageNode * dst ) +{ + hsRAMStream ramStream; + + // Create our JPEG stream + plJPEG::Instance().SetWriteQuality( 50 ); // In percent quality + + if( !plJPEG::Instance().WriteToStream( &ramStream, src ) ) + return false; + + // Copy the stream to the image element now + void * buffer = dst->AllocBuffer( ramStream.GetEOF() ); + if( buffer == nil ) + return false; + ramStream.CopyToMem( buffer ); + dst->SetImageType( plVaultImageNode::kJPEG ); + + // possibly make a plKey for the mipmap. + return plNetClientVault::ExtractImageFromNode( dst ); +} + +hsBool plNetClientVault::ExtractImageFromNode( plVaultImageNode * src) +{ + // no id? exit now. we will be called again when element is given an id. + if ( src->GetID()==0 ) + return false; + + // already have a mipmap and it has a key? release it + if ( src->GetMipmap() && src->GetMipmap()->GetKey()!=nil ) + { + plNetClientMgr::GetInstance()->GetKey()->Release( src->GetMipmap()->GetKey() ); + src->ISetMipmap( nil ); + } + + // convert image data to a plMipmap + switch( src->GetImageType() ) + { + case plVaultImageNode::kJPEG: + { + // Copy to a RAM stream so the JPEG class is happy + hsRAMStream ramStream; + ramStream.Write( src->GetBufSize(), src->GetBuffer() ); + ramStream.Rewind(); + // create mipmap from image data + src->ISetMipmap( plJPEG::Instance().ReadFromStream( &ramStream ) ); + } + break; + + default: + { + hsAssert( false, "ExtractImageFromNode: Invalid image type" ); + return false; // Invalid image type + } + } + + if ( !src->GetMipmap() ) + { + hsAssert( false, "ExtractImageFromNode failed" ); + return false; + } + + // we now have a mipmap, but it doesn't have a key. make a key for it + static int UniqueIdentifier = 0; + + char keyName[512]; + sprintf( keyName, "VaultImage_%lu_%d", src->GetID(), UniqueIdentifier++ ); + + // create a key for the mipmap + plKey imageKey = hsgResMgr::ResMgr()->NewKey( keyName, src->IGetMipmap(), + plLocation::kGlobalFixedLoc ); + + // ref the image key + hsgResMgr::ResMgr()->AddViaNotify( imageKey, new plGenRefMsg( + plNetClientMgr::GetInstance()->GetKey(), plRefMsg::kOnCreate, 0, 0 ), + plRefFlags::kActiveRef ); + + return ( src->GetMipmap()->GetKey()!=nil ); +} + + +//////////////////////////////////////////////////////////////////// + +plNetPlayerVault::plNetPlayerVault() +{ +} + +bool plNetPlayerVault::IIsThisMe( plVaultPlayerInfoNode * node ) const +{ + return GetPlayer()->GetPlayerInfo()->GetID()==node->GetID(); +} + +plVaultPlayerNode * plNetPlayerVault::GetPlayer( void ) const +{ + return plVaultPlayerNode::ConvertNoRef( GetRootNode() ); +} + +void plNetPlayerVault::IFillOutConnectFields( plNetMsgVault * msg ) const +{ + msg->AddInt( plVault::kArg_VaultClientType, plVault::kNodeType_VaultPlayer ); + msg->AddInt( plVault::kArg_VaultClientID, plNetClientMgr::GetInstance()->GetDesiredPlayerID() ); +} + +bool plNetPlayerVault::IIsThisMsgMine( plNetMsgVault * msg ) const +{ + if ( plVaultClient::IIsThisMsgMine( msg ) ) + return true; + return ( msg->GetInt( plVault::kArg_VaultClientID )==plNetClientMgr::GetInstance()->GetDesiredPlayerID() ); +} + + +//////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVault.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVault.h new file mode 100644 index 00000000..f82c9a10 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetClientVault.h @@ -0,0 +1,71 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetClientVault_h_inc +#define plNetClientVault_h_inc + +#include "../plVault/plVaultClient.h" + +class plMipmap; +class plVaultImageNode; + + +//////////////////////////////////////////////////////////////////// + +class plNetClientVault : public plVaultClient +{ +protected: + void IInitNode( plVaultNode * node ); + void IFiniNode( plVaultNode * node ); + int ISendNetMsg( plNetMsgVault * msg, UInt32 sendFlags=0 ); + void IOnTaskTimedOut( plVaultTask * task ); + bool IAmOnline( void ) const; + +public: + plNetClientVault(); + + plNetApp * GetNetApp( void ) const; + plAgeInfoSource * GetAgeInfo( void ) const; + + // static helpers to convert between plMipmap and plVaultImageNode + static hsBool StuffImageIntoNode( plMipmap * src, plVaultImageNode * dst ); + static hsBool ExtractImageFromNode( plVaultImageNode * src ); +}; + +//////////////////////////////////////////////////////////////////// + +class plNetPlayerVault : public plNetClientVault +{ +protected: + bool IIsThisMe( plVaultPlayerInfoNode * node ) const; + void IFillOutConnectFields( plNetMsgVault * msg ) const; + bool IIsThisMsgMine( plNetMsgVault * msg ) const; + +public: + plNetPlayerVault(); + plVaultPlayerNode * GetPlayer( void ) const; +}; + +#endif // plNetClientVault_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp new file mode 100644 index 00000000..04d1eb6a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.cpp @@ -0,0 +1,1012 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetLinkingMgr.h" +#include "plNetClientMgr.h" +#include "plNetCliAgeJoiner.h" +#include "plNetCliAgeLeaver.h" + +#include "../plNetTransport/plNetTransportMember.h" // OfferLinkToPlayer() + +#include "plgDispatch.h" +#include "../pnMessage/plTimeMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plNetCommon/plNetCommon.h" +#include "../plVault/plVault.h" +#include "../pnNetCommon/pnNetCommon.h" +#include "../plMessage/plVaultNotifyMsg.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plFile/hsFiles.h" +#include "../plMessage/plNCAgeJoinerMsg.h" +#include "../plVault/plVault.h" + + +/***************************************************************************** +* +* Private +* +***/ + +struct NlmOpNode; + +enum ENlmOp { + kNlmOpNoOp, + kNlmOpWaitOp, + kNlmOpJoinAgeOp, + kNlmOpLeaveAgeOp, + kNumNlmOps +}; + +struct NlmOp { + ENlmOp opcode; + NlmOpNode * node; + NlmOp (const ENlmOp & op) + : opcode(op) + { } +}; + +struct NlmNoOpOp : NlmOp { + NlmNoOpOp () + : NlmOp(kNlmOpNoOp) + { } +}; + +struct NlmOpWaitOp : NlmOp { + NlmOpWaitOp () + : NlmOp(kNlmOpWaitOp) + { } +}; + +struct NlmJoinAgeOp : NlmOp { + NetCommAge age; + NlmJoinAgeOp () + : NlmOp(kNlmOpJoinAgeOp) + { } +}; + +struct NlmLeaveAgeOp : NlmOp { + bool quitting; + NlmLeaveAgeOp () + : NlmOp(kNlmOpLeaveAgeOp) + { } +}; + +struct NlmOpNode { + LINK(NlmOpNode) link; + NlmOp * op; + ~NlmOpNode () { DEL(op); } +}; + + +/***************************************************************************** +* +* Private data +* +***/ + +static plNCAgeJoiner * s_ageJoiner; +static plNCAgeLeaver * s_ageLeaver; +static LISTDECL(NlmOpNode, link) s_oplist; + + +/***************************************************************************** +* +* Local functions +* +***/ + +//============================================================================ +static void QueueOp (NlmOp * op, bool front = false) { + NlmOpNode * node = NEWZERO(NlmOpNode); + node->op = op; + op->node = node; + s_oplist.Link(node, front ? kListHead : kListTail); +} + + +/***************************************************************************** +* +* plNetLinkingMgr +* +***/ + +//============================================================================ +void plNetLinkingMgr::NCAgeJoinerCallback ( + plNCAgeJoiner * joiner, + unsigned type, + void * notify, + void * userState +) { + plNetLinkingMgr * lm = plNetLinkingMgr::GetInstance(); + + switch (type) { + case kAgeJoinerComplete: { + ASSERT(joiner == s_ageJoiner); + s_ageJoiner = nil; + + lm->IPostProcessLink(); + + lm->fLinkedIn = true; + lm->SetEnabled(true); + + // Pull our wait op off exec queue + if (NlmOpWaitOp * waitOp = (NlmOpWaitOp *) userState) + DEL(waitOp->node); + } + break; + + DEFAULT_FATAL(type); + } +} + +//============================================================================ +void plNetLinkingMgr::NCAgeLeaverCallback ( + plNCAgeLeaver * leaver, + unsigned type, + void * notify, + void * userState +) { + switch (type) { + case kAgeLeaveComplete: { + ASSERT(leaver == s_ageLeaver); + s_ageLeaver = nil; + + // Pull our wait op off exec queue + if (NlmOpWaitOp * waitOp = (NlmOpWaitOp *) userState) + DEL(waitOp->node); + } + break; + + DEFAULT_FATAL(type); + } +} + +//============================================================================ +void plNetLinkingMgr::ExecNextOp () { + plNetLinkingMgr * lm = plNetLinkingMgr::GetInstance(); + + NlmOpNode * opNode = s_oplist.Head(); + if (!opNode) + return; + + switch (opNode->op->opcode) { + case kNlmOpNoOp: { + } + break; + + case kNlmOpWaitOp: { + } + return; // don't allow wait op to be unlinked/deleted from list + + case kNlmOpJoinAgeOp: { + ASSERT(!s_ageJoiner); + ASSERT(!s_ageLeaver); + + // Insert a wait operation into the exec queue + NlmOpWaitOp * waitOp = NEWZERO(NlmOpWaitOp); + QueueOp(waitOp, true); + + NlmJoinAgeOp * joinAgeOp = (NlmJoinAgeOp *) opNode->op; + NCAgeJoinerCreate( + &s_ageJoiner, + joinAgeOp->age, + NCAgeJoinerCallback, + waitOp + ); + } + break; + + case kNlmOpLeaveAgeOp: { + ASSERT(!s_ageJoiner); + ASSERT(!s_ageLeaver); + + // Insert a wait operation into the exec queue + NlmOpWaitOp * waitOp = NEWZERO(NlmOpWaitOp); + QueueOp(waitOp, true); + + lm->SetEnabled(false); + lm->fLinkedIn = false; + + NlmLeaveAgeOp * leaveAgeOp = (NlmLeaveAgeOp *) opNode->op; + NCAgeLeaverCreate( + &s_ageLeaver, + leaveAgeOp->quitting, + NCAgeLeaverCallback, + waitOp + ); + } + break; + } + + DEL(opNode); +} + + +//////////////////////////////////////////////////////////////////// + +plNetLinkingMgr::plNetLinkingMgr() +: fLinkingEnabled(true) +, fLinkedIn (false) +{ +} + +plNetLinkingMgr * plNetLinkingMgr::GetInstance() +{ + static plNetLinkingMgr Instance; + return &Instance; +} + + +//////////////////////////////////////////////////////////////////// + +void plNetLinkingMgr::SetEnabled( bool b ) +{ + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + hsLogEntry( nc->DebugMsg( "plNetLinkingMgr: %s -> %s", fLinkingEnabled?"Enabled":"Disabled",b?"Enabled":"Disabled" ) ); + fLinkingEnabled = b; +} + +//////////////////////////////////////////////////////////////////// + +// static +std::string plNetLinkingMgr::GetProperAgeName( const char * ageName ) +{ + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + hsFolderIterator it("dat"PATH_SEPARATOR_STR"*.age", true); + while ( it.NextFile() ) + { + std::string work = it.GetFileName(); + work.erase( work.find( ".age" ) ); + if ( stricmp( ageName, work.c_str() )==0 ) + return work; + } + return ageName; +} + +//////////////////////////////////////////////////////////////////// + +hsBool plNetLinkingMgr::MsgReceive( plMessage *msg ) +{ + if (s_ageLeaver && NCAgeLeaverMsgReceive(s_ageLeaver, msg)) + return true; + + if (s_ageJoiner && NCAgeJoinerMsgReceive(s_ageJoiner, msg)) + return true; + + if (plLinkToAgeMsg * pLinkMsg = plLinkToAgeMsg::ConvertNoRef(msg)) { + if (!fLinkingEnabled) + hsLogEntry(plNetClientMgr::GetInstance()->DebugMsg("Not linking. Linking is disabled.")); + else + IProcessLinkToAgeMsg(pLinkMsg); + return true; + } + + if (plLinkingMgrMsg * pLinkingMgrMsg = plLinkingMgrMsg::ConvertNoRef(msg)) { + IProcessLinkingMgrMsg( pLinkingMgrMsg ); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// + +void plNetLinkingMgr::Update() +{ + if (s_ageLeaver) + NCAgeLeaverUpdate(s_ageLeaver); + if (s_ageJoiner) + NCAgeJoinerUpdate(s_ageJoiner); + + ExecNextOp(); +} + +//////////////////////////////////////////////////////////////////// + +bool plNetLinkingMgr::IProcessLinkToAgeMsg( plLinkToAgeMsg * msg ) +{ + if (!fLinkingEnabled) { + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); + return false; + } + + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + bool result = true; + + plAgeLinkStruct save; + save.CopyFrom( GetPrevAgeLink() ); + GetPrevAgeLink()->CopyFrom( GetAgeLink() ); + GetAgeLink()->CopyFrom( msg->GetAgeLink() ); + + if ( IPreProcessLink() ) + { + GetAgeLink()->SetSpawnPoint(msg->GetAgeLink()->SpawnPoint()); + + if (fLinkedIn) { + // Set the link out animation we should use + if (plSceneObject *localSO = plSceneObject::ConvertNoRef(nc->GetLocalPlayer())) { + plArmatureMod *avMod = const_cast(plArmatureMod::ConvertNoRef(localSO->GetModifierByType(plArmatureMod::Index()))); + avMod->SetLinkInAnim(msg->GetLinkInAnimName()); + } + // Queue leave op + NlmLeaveAgeOp * leaveAgeOp = NEWZERO(NlmLeaveAgeOp); + QueueOp(leaveAgeOp); + } + + // Queue join op + NlmJoinAgeOp * joinAgeOp = NEWZERO(NlmJoinAgeOp); + joinAgeOp->age.ageInstId = (Uuid) *GetAgeLink()->GetAgeInfo()->GetAgeInstanceGuid(); + StrCopy( + joinAgeOp->age.ageDatasetName, + GetAgeLink()->GetAgeInfo()->GetAgeFilename(), + arrsize(joinAgeOp->age.ageDatasetName) + ); + StrCopy( + joinAgeOp->age.spawnPtName, + GetAgeLink()->SpawnPoint().GetName(), + arrsize(joinAgeOp->age.spawnPtName) + ); + QueueOp(joinAgeOp); + } + else + { + hsLogEntry( nc->ErrorMsg( "IPreProcessLink failed. Not linking." ) ); + // Restore previous age info state. + GetAgeLink()->CopyFrom( GetPrevAgeLink() ); + GetPrevAgeLink()->CopyFrom( &save ); + result = false; + } + + return result; +} + +//////////////////////////////////////////////////////////////////// + +bool plNetLinkingMgr::IProcessLinkingMgrMsg( plLinkingMgrMsg * msg ) +{ + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + bool result = true; + + switch ( msg->GetCmd() ) + { + ///////////////////// + case kLinkPlayerHere: + { + // player wants to link to our age + UInt32 playerID = msg->GetArgs()->GetInt( 0 ); + hsLogEntry( nc->DebugMsg( "Linking player %lu to this age.", playerID ) ); + LinkPlayerHere( playerID ); + } + break; + + ///////////////////// + case kLinkPlayerToPrevAge: + { + // link myself back to my last age + hsLogEntry( nc->DebugMsg( "Linking back to my last age.") ); + LinkToPrevAge(); + } + break; + ///////////////////// + case kOfferLinkToPlayer: + { +// // Notify the KI that we received an offer. +// plVaultNotifyMsg * notify = TRACKED_NEW plVaultNotifyMsg(); +// notify->SetType( plVaultNotifyMsg::kPlayerOfferedLink ); +// notify->GetArgs()->AddItem( 0, msg->GetArgs()->GetItem( 0 ), true ); // add to notify and have notify take over memory management of the item. +// msg->GetArgs()->RemoveItem( 0, true ); // msg to stop memory managing item, notify msg will delete it. +// notify->Send(); + + plAgeLinkStruct *myLink = plAgeLinkStruct::ConvertNoRef(msg->GetArgs()->GetItem( 0 )); + LinkToAge(myLink); + } + break; + + ///////////////////// + default: + hsAssert( false, "IProcessLinkingMgrMsg: Unknown linking mgr cmd." ); + result = false; + break; + } + + return result; +} + + +//////////////////////////////////////////////////////////////////// + +bool plNetLinkingMgr::IDispatchMsg( plMessage * msg, UInt32 playerID ) +{ + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + msg->AddReceiver( plNetClientMgr::GetInstance()->GetKey() ); + + if ( playerID!=kInvalidPlayerID && playerID!=nc->GetPlayerID() ) + { + msg->SetBCastFlag( plMessage::kNetAllowInterAge ); + msg->SetBCastFlag( plMessage::kNetPropagate ); + msg->SetBCastFlag( plMessage::kNetForce ); + msg->SetBCastFlag( plMessage::kLocalPropagate, 0 ); + // send msg to other player (maybe in different age than us) + msg->AddNetReceiver( playerID ); + } + + return ( msg->Send()!=0 ); +} + +//////////////////////////////////////////////////////////////////// + +void plNetLinkingMgr::LinkToAge( plAgeLinkStruct * link, UInt32 playerID ) +{ + LinkToAge(link, nil, playerID); +} + +void plNetLinkingMgr::LinkToAge( plAgeLinkStruct * link, const char* linkAnim, UInt32 playerID ) +{ + if ( !fLinkingEnabled ) + { + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); + return; + } + + plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( link ); + if (linkAnim) + pMsg->SetLinkInAnimName(linkAnim); + IDispatchMsg( pMsg, playerID ); +} + +// link myself back to my last age +void plNetLinkingMgr::LinkToPrevAge( UInt32 playerID ) +{ + if ( !fLinkingEnabled ) + { + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); + return; + } + + if (GetPrevAgeLink()->GetAgeInfo()->HasAgeFilename()) + { + plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( GetPrevAgeLink() ); + IDispatchMsg( pMsg, playerID ); + } + else + { + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. No prev age fileName." ) ); + } +} + +void plNetLinkingMgr::LinkToMyPersonalAge( UInt32 playerID ) +{ + if ( !fLinkingEnabled ) + { + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); + return; + } + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename( kPersonalAgeFilename ); + link.SetLinkingRules( plNetCommon::LinkingRules::kOwnedBook ); + + plSpawnPointInfo hutSpawnPoint; + hutSpawnPoint.SetName(kPersonalAgeLinkInPointCloset); + link.SetSpawnPoint(hutSpawnPoint); + + plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( &link ); + IDispatchMsg( pMsg, playerID ); +} + +void plNetLinkingMgr::LinkToMyNeighborhoodAge( UInt32 playerID ) +{ + if ( !fLinkingEnabled ) + { + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); + return; + } + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + plAgeLinkStruct link; + if (!VaultGetLinkToMyNeighborhood(&link)) + return; + + link.SetLinkingRules( plNetCommon::LinkingRules::kOwnedBook ); + + plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( &link ); + IDispatchMsg( pMsg, playerID ); +} + +void plNetLinkingMgr::LinkPlayerHere( UInt32 playerID ) +{ + if ( !fLinkingEnabled ) + { + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); + return; + } + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + // send the player our current age info so they can link here. + plAgeLinkStruct link; + link.GetAgeInfo()->CopyFrom( GetAgeLink()->GetAgeInfo() ); + LinkPlayerToAge( &link, playerID ); +} + +void plNetLinkingMgr::LinkPlayerToAge( plAgeLinkStruct * link, UInt32 playerID ) +{ + if ( !fLinkingEnabled ) + { + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); + return; + } + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + // send the player the age link so they can link there. + link->SetLinkingRules( plNetCommon::LinkingRules::kBasicLink ); + plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( link ); + IDispatchMsg( pMsg, playerID ); +} + +// +// link the player back to his previous age +// +void plNetLinkingMgr::LinkPlayerToPrevAge( UInt32 playerID ) +{ + if ( !fLinkingEnabled ) + { + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); + return; + } + + // Send the player a msg telling them to link to their last age + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + plLinkingMgrMsg* pMsg = TRACKED_NEW plLinkingMgrMsg(); + pMsg->SetCmd( kLinkPlayerToPrevAge); + IDispatchMsg( pMsg, playerID ); +} + +void plNetLinkingMgr::LinkToPlayersAge( UInt32 playerID ) +{ + if ( !fLinkingEnabled ) + { + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg( "Not linking. Linking is disabled." ) ); + return; + } + // Send the player a msg telling them to send us a msg to link to them. isn't that fun? :) + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + plLinkingMgrMsg* pMsg = TRACKED_NEW plLinkingMgrMsg(); + pMsg->SetCmd( kLinkPlayerHere ); + pMsg->GetArgs()->AddInt( 0, NetCommGetPlayer()->playerInt ); // send them our id. + IDispatchMsg( pMsg, playerID ); +} + +//////////////////////////////////////////////////////////////////// + + +void plNetLinkingMgr::OfferLinkToPlayer( const plAgeLinkStruct * inInfo, UInt32 playerID ) +{ + + plNetClientMgr *mgr = plNetClientMgr::GetInstance(); + plLinkToAgeMsg * linkM = TRACKED_NEW plLinkToAgeMsg(inInfo); + linkM->AddReceiver(mgr->GetKey()); + + plKey host = mgr->GetLocalPlayerKey(); + + plNetTransport &transport = mgr->TransportMgr(); + int guestIdx = transport.FindMember(playerID); + plNetTransportMember *guestMem = transport.GetMember(guestIdx); // -1 ? + if(guestMem) + { + plKey guest = guestMem->GetAvatarKey(); + plAvatarMgr::OfferLinkingBook(host, guest, linkM, host); + } +} +// my special version - cjp +void plNetLinkingMgr::OfferLinkToPlayer( const plAgeLinkStruct * inInfo, UInt32 playerID, plKey replyKey ) +{ + + plNetClientMgr *mgr = plNetClientMgr::GetInstance(); + plLinkToAgeMsg * linkM = TRACKED_NEW plLinkToAgeMsg(inInfo); + linkM->AddReceiver(mgr->GetKey()); + + plKey host = mgr->GetLocalPlayerKey(); + + plNetTransport &transport = mgr->TransportMgr(); + int guestIdx = transport.FindMember(playerID); + plNetTransportMember *guestMem = transport.GetMember(guestIdx); // -1 ? + if(guestMem) + { + plKey guest = guestMem->GetAvatarKey(); + plAvatarMgr::OfferLinkingBook(host, guest, linkM, replyKey); + } +} + +// for backwards compatibility +void plNetLinkingMgr::OfferLinkToPlayer( const plAgeInfoStruct * inInfo, UInt32 playerID ) +{ + plAgeLinkStruct *ageLink = TRACKED_NEW plAgeLinkStruct; + + ageLink->GetAgeInfo()->CopyFrom(inInfo); + ageLink->SetLinkingRules(plNetCommon::LinkingRules::kBasicLink); + OfferLinkToPlayer(ageLink, playerID); +} + +//////////////////////////////////////////////////////////////////// + +void plNetLinkingMgr::IPostProcessLink( void ) +{ + bool city = (0 == stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kCityAgeFilename )); + bool hood = (0 == stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kNeighborhoodAgeFilename )); + bool psnl = (0 == stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kPersonalAgeFilename )); + + // Update our online status + if (RelVaultNode * rvnInfo = VaultGetPlayerInfoNodeIncRef()) { + VaultPlayerInfoNode accInfo(rvnInfo); + wchar ageInstName[MAX_PATH]; + Uuid ageInstGuid = *GetAgeLink()->GetAgeInfo()->GetAgeInstanceGuid(); + StrToUnicode(ageInstName, GetAgeLink()->GetAgeInfo()->GetAgeInstanceName(), arrsize(ageInstName)); + accInfo.SetAgeInstName(ageInstName); + accInfo.SetAgeInstUuid(ageInstGuid); + accInfo.SetOnline(true); + rvnInfo->DecRef(); + } + + switch (GetAgeLink()->GetLinkingRules()) { + + case plNetCommon::LinkingRules::kOwnedBook: { + // SPECIAL CASE: City: Every player ever created would be in the list; avoid that. + if (city) + break; + + { // Ensure we're in the AgeOwners folder + RelVaultNode * fldr = VaultGetAgeAgeOwnersFolderIncRef(); + RelVaultNode * info = VaultGetPlayerInfoNodeIncRef(); + + if (fldr && info) + if (!fldr->IsParentOf(info->nodeId, 1)) + VaultAddChildNodeAndWait( + fldr->nodeId, + info->nodeId, + NetCommGetPlayer()->playerInt + ); + + if (fldr) + fldr->DecRef(); + if (info) + info->DecRef(); + } + } + break; + + case plNetCommon::LinkingRules::kVisitBook: { + // SPECIAL CASE: City: Every player ever created would be in the list; avoid that. + if (city) + break; + + { // Ensure we're in the CanVisit folder + RelVaultNode * fldr = VaultGetAgeCanVisitFolderIncRef(); + RelVaultNode * info = VaultGetPlayerInfoNodeIncRef(); + + if (fldr && info) + if (!fldr->IsParentOf(info->nodeId, 1)) + VaultAddChildNodeAndWait( + fldr->nodeId, + info->nodeId, + NetCommGetPlayer()->playerInt + ); + + if (fldr) + fldr->DecRef(); + if (info) + info->DecRef(); + } + } + break; + + case plNetCommon::LinkingRules::kSubAgeBook: { + // Register the previous age as a sub age of the current one so that we can link back to that instance + plAgeLinkStruct subAgeLink; + VaultAgeFindOrCreateSubAgeLinkAndWait(GetPrevAgeLink()->GetAgeInfo(), &subAgeLink, NetCommGetAge()->ageInstId); + } + break; + } +} + +//////////////////////////////////////////////////////////////////// + +bool plNetLinkingMgr::IPreProcessLink( void ) +{ + plNetClientMgr * nc = plNetClientMgr::GetInstance(); + + bool success = true; + + if ( nc->GetFlagsBit( plNetClientMgr::kNullSend ) ) + { + hsLogEntry( nc->DebugMsg( "NetClientMgr nullsend. Not linking." ) ); + return false; + } + +#if 0 + // Appear offline until we're done linking + if (RelVaultNode * rvnInfo = VaultGetPlayerInfoNodeIncRef()) { + VaultPlayerInfoNode accInfo(rvnInfo); + accInfo.SetAgeInstName(nil); + accInfo.SetAgeInstUuid(kNilGuid); + accInfo.SetOnline(false); + rvnInfo->DecRef(); + } +#else + // Update our online status + if (RelVaultNode * rvnInfo = VaultGetPlayerInfoNodeIncRef()) { + VaultPlayerInfoNode accInfo(rvnInfo); + wchar ageInstName[MAX_PATH]; + Uuid ageInstGuid = *GetAgeLink()->GetAgeInfo()->GetAgeInstanceGuid(); + StrToUnicode(ageInstName, GetAgeLink()->GetAgeInfo()->GetAgeInstanceName(), arrsize(ageInstName)); + accInfo.SetAgeInstName(ageInstName); + accInfo.SetAgeInstUuid(ageInstGuid); + accInfo.SetOnline(true); + rvnInfo->DecRef(); + } +#endif + + //------------------------------------------------------------------------ + // Fixup empty fields + if ( GetAgeLink()->GetAgeInfo()->HasAgeFilename() ) + { + GetAgeLink()->GetAgeInfo()->SetAgeFilename( plNetLinkingMgr::GetProperAgeName( GetAgeLink()->GetAgeInfo()->GetAgeFilename() ).c_str() ); + + if ( !GetAgeLink()->GetAgeInfo()->HasAgeInstanceName() ) + { + GetAgeLink()->GetAgeInfo()->SetAgeInstanceName( GetAgeLink()->GetAgeInfo()->GetAgeFilename() ); + } + } + + hsLogEntry( nc->DebugMsg( "plNetLinkingMgr: Pre-Process: Linking with %s rules...", + plNetCommon::LinkingRules::LinkingRuleStr( GetAgeLink()->GetLinkingRules() ) ) ); + + //------------------------------------------------------------------------ + // SPECIAL CASE: StartUp: force basic link + if ( stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kStartUpAgeFilename )==0 ) + { + GetAgeLink()->SetLinkingRules( plNetCommon::LinkingRules::kBasicLink ); + } + + //------------------------------------------------------------------------ + // SPECIAL CASE: Nexus: force original link + if ( stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kNexusAgeFilename )==0 ) + { + GetAgeLink()->SetLinkingRules( plNetCommon::LinkingRules::kOriginalBook ); + } + + //------------------------------------------------------------------------ + // SPECIAL CASE: ACA: force original link + if ( stricmp( GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kAvCustomizationFilename )==0 ) + { + GetAgeLink()->SetLinkingRules( plNetCommon::LinkingRules::kOriginalBook ); + } + + hsLogEntry( nc->DebugMsg( "plNetLinkingMgr: Process: Linking with %s rules...", + plNetCommon::LinkingRules::LinkingRuleStr( GetAgeLink()->GetLinkingRules() ) ) ); + + switch ( GetAgeLink()->GetLinkingRules() ) + { + //-------------------------------------------------------------------- + // BASIC LINK. Link to a unique instance of the age, if no instance specified. + case plNetCommon::LinkingRules::kBasicLink: + if (!GetAgeLink()->GetAgeInfo()->HasAgeInstanceGuid()) + GetAgeLink()->GetAgeInfo()->SetAgeInstanceGuid(&plUUID(GuidGenerate())); + break; + + //-------------------------------------------------------------------- + // ORIGINAL BOOK. Become an owner of the age, if not already one. + case plNetCommon::LinkingRules::kOriginalBook: + { + // create a new ageinfo struct with the filename of the age because + // we just want to find out if we own *any* link to the age, not the specific + // link that we're linking through + plAgeInfoStruct ageInfo; + ageInfo.SetAgeFilename(GetAgeLink()->GetAgeInfo()->GetAgeFilename()); + + plAgeLinkStruct ownedLink; + if (!VaultGetOwnedAgeLink(&ageInfo, &ownedLink)) { + // Fill in fields for new age create. + if ( !GetAgeLink()->GetAgeInfo()->HasAgeUserDefinedName() ) + { + // set user-defined name + std::string title; + unsigned nameLen = StrLen(nc->GetPlayerName()); + if (nc->GetPlayerName()[nameLen - 1] == 's' || nc->GetPlayerName()[nameLen - 1] == 'S') + xtl::format( title, "%s'", nc->GetPlayerName() ); + else + xtl::format( title, "%s's", nc->GetPlayerName() ); + GetAgeLink()->GetAgeInfo()->SetAgeUserDefinedName( title.c_str() ); + } + if ( !GetAgeLink()->GetAgeInfo()->HasAgeDescription() ) + { + // set description + std::string desc; + unsigned nameLen = StrLen(nc->GetPlayerName()); + if (nc->GetPlayerName()[nameLen - 1] == 's' || nc->GetPlayerName()[nameLen - 1] == 'S') + xtl::format( desc, "%s' %s", nc->GetPlayerName(), GetAgeLink()->GetAgeInfo()->GetAgeInstanceName() ); + else + xtl::format( desc, "%s's %s", nc->GetPlayerName(), GetAgeLink()->GetAgeInfo()->GetAgeInstanceName() ); + GetAgeLink()->GetAgeInfo()->SetAgeDescription( desc.c_str() ); + } + if (!GetAgeLink()->GetAgeInfo()->HasAgeInstanceGuid()) { + GetAgeLink()->GetAgeInfo()->SetAgeInstanceGuid(&plUUID(GuidGenerate())); + } + + // register this as an owned age now before we link to it. + VaultRegisterOwnedAgeAndWait(GetAgeLink()); + } + else if (RelVaultNode * linkNode = VaultGetOwnedAgeLinkIncRef(&ageInfo)) { + // We have the age in our AgesIOwnFolder. If its volatile, dump it for the new one. + VaultAgeLinkNode linkAcc(linkNode); + if (linkAcc.volat) { + if (VaultUnregisterOwnedAgeAndWait(&ageInfo)) { + // Fill in fields for new age create. + if ( !GetAgeLink()->GetAgeInfo()->HasAgeUserDefinedName() ) + { + // set user-defined name + std::string title; + unsigned nameLen = StrLen(nc->GetPlayerName()); + if (nc->GetPlayerName()[nameLen - 1] == 's' || nc->GetPlayerName()[nameLen - 1] == 'S') + xtl::format( title, "%s'", nc->GetPlayerName() ); + else + xtl::format( title, "%s's", nc->GetPlayerName() ); + GetAgeLink()->GetAgeInfo()->SetAgeUserDefinedName( title.c_str() ); + } + if ( !GetAgeLink()->GetAgeInfo()->HasAgeDescription() ) + { + // set description + std::string desc; + unsigned nameLen = StrLen(nc->GetPlayerName()); + if (nc->GetPlayerName()[nameLen - 1] == 's' || nc->GetPlayerName()[nameLen - 1] == 'S') + xtl::format( desc, "%s' %s", nc->GetPlayerName(), GetAgeLink()->GetAgeInfo()->GetAgeInstanceName() ); + else + xtl::format( desc, "%s's %s", nc->GetPlayerName(), GetAgeLink()->GetAgeInfo()->GetAgeInstanceName() ); + GetAgeLink()->GetAgeInfo()->SetAgeDescription( desc.c_str() ); + } + if (!GetAgeLink()->GetAgeInfo()->HasAgeInstanceGuid()) { + GetAgeLink()->GetAgeInfo()->SetAgeInstanceGuid(&plUUID(GuidGenerate())); + } + + VaultRegisterOwnedAgeAndWait(GetAgeLink()); + } + } + else { + if (stricmp(GetAgeLink()->GetAgeInfo()->GetAgeFilename(), kNeighborhoodAgeFilename) == 0) { + // if we get here then its because we're linking to a neighborhood that we don't belong to + // and our own neighborhood book is not volatile, so really we want to basic link + GetAgeLink()->SetLinkingRules(plNetCommon::LinkingRules::kBasicLink); + success = true; + + break; + } + } + linkNode->DecRef(); + } + } + GetAgeLink()->SetLinkingRules( plNetCommon::LinkingRules::kOwnedBook ); + // falls thru to OWNED BOOK case... + + //-------------------------------------------------------------------- + // OWNED BOOK. Look for the book in our AgesIOwn folder + case plNetCommon::LinkingRules::kOwnedBook: + { + plAgeLinkStruct ownedLink; + if (VaultGetOwnedAgeLink(GetAgeLink()->GetAgeInfo(), &ownedLink)) { + GetAgeLink()->GetAgeInfo()->CopyFrom(ownedLink.GetAgeInfo()); + // Remember spawn point (treasure book support) + plSpawnPointInfo theSpawnPt = GetAgeLink()->SpawnPoint(); + VaultAddOwnedAgeSpawnPoint(*GetAgeLink()->GetAgeInfo()->GetAgeInstanceGuid(), theSpawnPt); + } + else { + success = false; + } + } + break; + + //-------------------------------------------------------------------- + // VISIT BOOK. Look for the book in our AgesICanVisit folder + case plNetCommon::LinkingRules::kVisitBook: + { + plAgeLinkStruct visitLink; + if (VaultGetVisitAgeLink(GetAgeLink()->GetAgeInfo(), &visitLink)) + GetAgeLink()->GetAgeInfo()->CopyFrom(visitLink.GetAgeInfo()); + else + success = false; + } + break; + + //-------------------------------------------------------------------- + // SUB-AGE BOOK: Look for an existing sub-age in this age's SubAges folder and use it if found. + // plNetClientTaskHandler will add a SubAge back link to our current age once we arrive there. + case plNetCommon::LinkingRules::kSubAgeBook: + { + plAgeLinkStruct subAgeLink; + if (VaultAgeFindOrCreateSubAgeLinkAndWait(GetAgeLink()->GetAgeInfo(), &subAgeLink, NetCommGetAge()->ageInstId)) + GetAgeLink()->GetAgeInfo()->CopyFrom(subAgeLink.GetAgeInfo()); + else + success = false; + } + break; + + //-------------------------------------------------------------------- + // CHILD-AGE BOOK: Look for an existing child-age in this parent ageinfo ChildAges folder and use it if found. + // plNetClientTaskHandler will add a ChildAge back link to our current age once we arrive there. + case plNetCommon::LinkingRules::kChildAgeBook: + { + plAgeLinkStruct childLink; + wchar parentAgeName[MAX_PATH]; + if (GetAgeLink()->HasParentAgeFilename()) { + StrToUnicode(parentAgeName, GetAgeLink()->GetParentAgeFilename(), arrsize(parentAgeName)); + success = VaultAgeFindOrCreateChildAgeLinkAndWait(parentAgeName, GetAgeLink()->GetAgeInfo(), &childLink); + } + else { + success = VaultAgeFindOrCreateChildAgeLinkAndWait(nil, GetAgeLink()->GetAgeInfo(), &childLink); + } + + if (success) + GetAgeLink()->GetAgeInfo()->CopyFrom(childLink.GetAgeInfo()); + } + break; + + //-------------------------------------------------------------------- + // ??? + DEFAULT_FATAL(GetAgeLink()->GetLinkingRules()); + } + + hsLogEntry( nc->DebugMsg( "plNetLinkingMgr: Post-Process: Linking with %s rules...", + plNetCommon::LinkingRules::LinkingRuleStr( GetAgeLink()->GetLinkingRules() ) ) ); + + hsAssert( GetAgeLink()->GetAgeInfo()->HasAgeFilename(), "AgeLink has no AgeFilename. Link will fail." ); + + return success; +} + + +//////////////////////////////////////////////////////////////////// +void plNetLinkingMgr::LeaveAge (bool quitting) { + // Queue leave op + NlmLeaveAgeOp * leaveAgeOp = NEWZERO(NlmLeaveAgeOp); + leaveAgeOp->quitting = quitting; + QueueOp(leaveAgeOp); + +} + + + + +//////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.h new file mode 100644 index 00000000..d254b51f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetLinkingMgr.h @@ -0,0 +1,148 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetLinkingMgr_h_inc +#define plNetLinkingMgr_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsBitVector.h" +#include "../plNetCommon/plNetServerSessionInfo.h" +#include "../plNetCommon/plNetCommon.h" +#include "../plMessage/plLinkToAgeMsg.h" + +class plMessage; +struct plNCAgeJoiner; +struct plNCAgeLeaver; + +class plNetLinkingMgr +{ + static void NCAgeJoinerCallback ( + plNCAgeJoiner * joiner, + unsigned type, + void * notify, + void * userState + ); + + static void NCAgeLeaverCallback ( + plNCAgeLeaver * leaver, + unsigned type, + void * notify, + void * userState + ); + + friend struct NCAgeJoinerCallback; + friend struct NCAgeLeaverCallback; + + static void ExecNextOp (); + + + plNetLinkingMgr(); + plNetLinkingMgr(const plNetLinkingMgr &); + + enum Cmds + { + kNilCmd, + // Sent to a player to have them call us back with info for linking to their age. + kLinkPlayerHere, + // Offer link to player. + kOfferLinkToPlayer, + // Offer link to another player from a public linking book (to my instance of the age w/out going through personal age) + kOfferLinkFromPublicBook, + // Sent to a player to have them link to their last age + kLinkPlayerToPrevAge + }; + + bool IPreProcessLink( void ); + void IPostProcessLink( void ); + bool IProcessLinkingMgrMsg( plLinkingMgrMsg * msg ); + bool IProcessLinkToAgeMsg( plLinkToAgeMsg * msg ); + + bool IDispatchMsg( plMessage * msg, UInt32 playerID ); + + +public: + static plNetLinkingMgr * GetInstance(); + hsBool MsgReceive( plMessage *msg ); // TODO: Make this a hsKeyedObject so we can really handle messages. + void Update(); + + bool IsEnabled( void ) const { return fLinkingEnabled;} + void SetEnabled( bool b ); + + bool LinkedIn () const { return fLinkedIn && fLinkingEnabled; } + bool Linking () const { return !fLinkedIn && !fLinkingEnabled; } + + // Link to an age. + void LinkToAge( plAgeLinkStruct * link, UInt32 playerID=kInvalidPlayerID ); + void LinkToAge( plAgeLinkStruct * link, const char* linkAnim, UInt32 playerID=kInvalidPlayerID ); + // Link to my last age. + void LinkToPrevAge( UInt32 playerID=kInvalidPlayerID ); + // Link to my Personal Age + void LinkToMyPersonalAge( UInt32 playerID=kInvalidPlayerID ); + // Link to my Neighborhood Age + void LinkToMyNeighborhoodAge( UInt32 playerID=kInvalidPlayerID ); + // Link a player here. + void LinkPlayerHere( UInt32 playerID ); + // Link player to specified age + void LinkPlayerToAge( plAgeLinkStruct * link, UInt32 playerID ); + // Link player back to his last age + void LinkPlayerToPrevAge( UInt32 playerID ); + // Link us to a players age. + void LinkToPlayersAge( UInt32 playerID ); + // Offer a link to player. + void OfferLinkToPlayer( const plAgeLinkStruct * info, UInt32 playerID, plKey replyKey ); + void OfferLinkToPlayer( const plAgeInfoStruct * info, UInt32 playerID ); + void OfferLinkToPlayer( const plAgeLinkStruct * info, UInt32 playerID ); + // Leave the current age + void LeaveAge (bool quitting); + + // link info + plAgeLinkStruct * GetAgeLink() { return &fAgeLink; } + plAgeLinkStruct * GetPrevAgeLink() { return &fPrevAgeLink; } + + // lobby info + void SetLobbyAddr( const char * ipaddr ) { fLobbyInfo.SetServerAddr( ipaddr );} + void SetLobbyPort( int port ) { fLobbyInfo.SetServerPort( port );} + const plNetServerSessionInfo * GetLobbyServerInfo( void ) const { return &fLobbyInfo;} + + // helpers + static std::string GetProperAgeName( const char * ageName ); // attempt to fix wrong case age name. + +private: + bool fLinkingEnabled; + bool fLinkedIn; + + // The age we are either joining or are joined with. + plAgeLinkStruct fAgeLink; + + // The age we just left. + plAgeLinkStruct fPrevAgeLink; + + // The lobby we want to talk to. + plNetServerSessionInfo fLobbyInfo; +}; + + +#endif // plNetLinkingMgr_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetObjectDebugger.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetObjectDebugger.cpp new file mode 100644 index 00000000..9e0d8b22 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetObjectDebugger.cpp @@ -0,0 +1,306 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetObjectDebugger.h" +#include "hsResMgr.h" +#include "hsTemplates.h" + +#include "../pnUtils/pnUtils.h" +#include "../pnKeyedObject/hsKeyedObject.h" + +#include "../plStatusLog/plStatusLog.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plAgeLoader/plAgeLoader.h" + +plNetObjectDebugger::DebugObject::DebugObject(const char* objName, plLocation& loc, UInt32 flags) : +fLoc(loc), +fFlags(flags) +{ + std::string tmp = objName; + hsStrLower((char*)tmp.c_str()); + fObjName = tmp; +} + +// +// return true if string matches objName according to flags +// +bool plNetObjectDebugger::DebugObject::StringMatches(const char* str) const +{ + if (!str) + return false; + + if (fFlags & kExactStringMatch) + return !stricmp(str, fObjName.c_str()); + + if (fFlags & kEndStringMatch) + { + int len=strlen(str); + if (len>fObjName.size()) + return false; + return !stricmp(str, fObjName.c_str()+fObjName.size()-len); + } + + if (fFlags & kStartStringMatch) + { + int len=strlen(str); + if (len>fObjName.size()) + return false; + return !strnicmp(str, fObjName.c_str(), strlen(str)); + } + + if (fFlags & kSubStringMatch) + { + std::string tmp = str; + hsStrLower((char*)tmp.c_str()); + return (strstr(tmp.c_str(), fObjName.c_str()) != nil); + } + + hsAssert(false, "missing flags"); + return false; +} + +// +// if both objName and pageName are provided, and this object has page info, +// return true if object matches both string and location. +// else just return true if object matches string +// +bool plNetObjectDebugger::DebugObject::ObjectMatches(const char* objName, const char* pageName) +{ + if (!objName) + return false; + + if (!pageName || (fFlags & kPageMatch)==0) + { + // only have enough info to match by objName + return StringMatches(objName); + } + + plLocation loc; + loc = plKeyFinder::Instance().FindLocation(NetCommGetAge()->ageDatasetName, pageName); + return (StringMatches(objName) && loc==fLoc); +} + +// +// try to match by plLocation +// +bool plNetObjectDebugger::DebugObject::ObjectMatches(const hsKeyedObject* obj) +{ + if (!obj || !obj->GetKey()) + return false; + + if ((fFlags & kPageMatch)==0) + { + // match based on object name only + return StringMatches(obj->GetKeyName()); + } + + return (obj->GetKey()->GetUoid().GetLocation()==fLoc); +} + +///////////////////////////////////////////////////////////////// +// plNetObjectDebugger +///////////////////////////////////////////////////////////////// +plNetObjectDebugger::plNetObjectDebugger() : fStatusLog(nil), fDebugging(false) +{ +} + +plNetObjectDebugger::~plNetObjectDebugger() +{ + ClearAllDebugObjects(); + delete fStatusLog; +} + +// +// STATIC +// +plNetObjectDebugger* plNetObjectDebugger::GetInstance() +{ + static plNetObjectDebugger gNetObjectDebugger; + + if (plNetObjectDebuggerBase::GetInstance()==nil) + plNetObjectDebuggerBase::SetInstance(&gNetObjectDebugger); + + return &gNetObjectDebugger; +} + +// +// create StatusLog if necessary +// +void plNetObjectDebugger::ICreateStatusLog() const +{ + if (!fStatusLog) + { + fStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog(40, "NetObject.log", + plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kTimestamp ); + } +} + +bool plNetObjectDebugger::AddDebugObject(const char* objName, const char* pageName) +{ + if (!objName) + return false; + + int size=strlen(objName)+1; + hsTempArray tmpObjName(size); + memset(tmpObjName, 0, size); + + // + // set string matching flags + // + int len = strlen(objName); + UInt32 flags=0; + if (objName[0]=='*') + { + if (objName[len-1]=='*') + { + flags = kSubStringMatch; // *foo* + strncpy(tmpObjName, objName+1, strlen(objName)-2); + } + else + { + flags = kEndStringMatch; // *foo + strncpy(tmpObjName, objName+1, strlen(objName)-1); + } + } + + if (!flags && objName[len-1]=='*') + { + flags = kStartStringMatch; // foo* + strncpy(tmpObjName, objName, strlen(objName)-1); + } + + if (!flags) + { + flags = kExactStringMatch; + strcpy(tmpObjName, objName); + } + + // + // set plLocation + // + plLocation loc; + if (pageName) + { + loc = plKeyFinder::Instance().FindLocation(NetCommGetAge()->ageDatasetName, pageName); + flags |= kPageMatch; + } + + fDebugObjects.push_back(TRACKED_NEW DebugObject(tmpObjName, loc, flags)); + + ICreateStatusLog(); + + return true; +} + +bool plNetObjectDebugger::RemoveDebugObject(const char* objName, const char* pageName) +{ + bool didIt=false; + if (!pageName) + { + DebugObjectList::iterator it =fDebugObjects.begin(); + for( ; it != fDebugObjects.end(); ) + { + if ( (*it) && (*it)->ObjectMatches(objName, pageName)) + { + delete *it; + it = fDebugObjects.erase(it); + didIt=true; + } + else + it++; + } + } + + return didIt; +} + +void plNetObjectDebugger::ClearAllDebugObjects() +{ + DebugObjectList::iterator it =fDebugObjects.begin(); + for( ; it != fDebugObjects.end(); it++) + { + delete *it; + } + fDebugObjects.clear(); +} + +// +// write to status log if there's a string match +// +void plNetObjectDebugger::LogMsgIfMatch(const char* msg) const +{ + if (GetNumDebugObjects()==0 || !msg) + return; + + // extract object name from msg, expects '...object:foo,...' + std::string tmp = msg; + hsStrLower((char*)tmp.c_str()); + std::string objTag="object"; + char* c=strstr(tmp.c_str(), objTag.c_str()); + if (c && c != tmp.c_str()) + { + c+=objTag.size(); + + // move past spaces + while ( *c || *c==' ' ) + c++; + + char objName[128]; + int i=0; + + // copy objName token + while(*c && *c != ',' && *c != ' ' && i<127) + objName[i++] = *c++; + objName[i]=0; + + DebugObjectList::const_iterator it = fDebugObjects.begin(); + for( objName[0]; it != fDebugObjects.end(); it++) + { + if ((*it) && (*it)->StringMatches(objName)) + { + LogMsg(msg); + break; + } + } + } +} + +void plNetObjectDebugger::LogMsg(const char* msg) const +{ + DEBUG_MSG(msg); +} + +bool plNetObjectDebugger::IsDebugObject(const hsKeyedObject* obj) const +{ + DebugObjectList::const_iterator it =fDebugObjects.begin(); + for( ; it != fDebugObjects.end(); it++) + if ((*it) && (*it)->ObjectMatches(obj)) + { + return true; + } + + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetObjectDebugger.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetObjectDebugger.h new file mode 100644 index 00000000..b9f0ca9c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetObjectDebugger.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetObjectDebugger_inc +#define plNetObjectDebugger_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnNetCommon/plNetApp.h" + +class hsKeyedObject; +class plStatusLog; +class plNetObjectDebugger : public plNetObjectDebuggerBase +{ +public: + enum Flags + { + kExactStringMatch = 0x1, + kEndStringMatch = 0x2, + kStartStringMatch = 0x4, + kSubStringMatch = 0x8, + kPageMatch = 0x10 // has page info specified + }; +private: + struct DebugObject + { + std::string fObjName; + plLocation fLoc; + UInt32 fFlags; + bool StringMatches(const char* str) const; // return true if string matches objName according to flags + bool ObjectMatches(const hsKeyedObject* obj); + bool ObjectMatches(const char* objName, const char* pageName); + DebugObject(const char* objName, plLocation& loc, UInt32 flags); + }; + typedef std::vector DebugObjectList; + DebugObjectList fDebugObjects; + mutable plStatusLog* fStatusLog; + bool fDebugging; + + void ICreateStatusLog() const; +public: + plNetObjectDebugger(); + ~plNetObjectDebugger(); + + static plNetObjectDebugger* GetInstance(); + + bool GetDebugging() const { return fDebugging; } + void SetDebugging(bool b) { fDebugging=b; } + + // object fxns + bool AddDebugObject(const char* objName, const char* pageName=nil); + bool RemoveDebugObject(const char* objName, const char* pageName=nil); + void ClearAllDebugObjects(); + int GetNumDebugObjects() const { return fDebugObjects.size(); } + bool IsDebugObject(const hsKeyedObject* obj) const; + + void LogMsgIfMatch(const char* msg) const; // write to status log if there's a string match + void LogMsg(const char* msg) const; +}; + +#endif // plNetObjectDebugger_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetVoiceList.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetVoiceList.cpp new file mode 100644 index 00000000..c984065b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetVoiceList.cpp @@ -0,0 +1,145 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "plgDispatch.h" +#include "plNetClientMgr.h" +#include "plNetVoiceList.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../pnMessage/plSoundMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "../plStatusLog/plStatusLog.h" + +// statics +float plNetListenList::kUpdateInterval=0.5f; +int plNetListenList::kMaxListenListSize=-1; // -1 is unlimited +float plNetListenList::kMaxListenDistSq=75.0f*75.0f; + +int plNetVoiceList::FindMember(plNetTransportMember* e) +{ + VoiceListType::iterator result = std::find(fMembers.begin(), fMembers.end(), e); + return result!=fMembers.end() ? result-fMembers.begin() : -1; +} + + +/***************************************************************************** +* +* plNetTalkList +* +***/ + +void plNetTalkList::UpdateTransportGroup(plNetClientMgr* nc) +{ + if (fFlags & kDirty) + { + nc->fTransport.ClearChannelGrp(plNetClientMgr::kNetChanVoice); + if (nc->IsPeerToPeer()) + { + int i; + for(i=0;iIsPeerToPeer()) + nc->fTransport.SubscribeToChannelGrp(GetMember(i), plNetClientMgr::kNetChanVoice); + } + } + fFlags &= ~kDirty; + } +} + +void plNetTalkList::AddMember(plNetTransportMember* e) +{ + if (FindMember(e)==-1) + { + plStatusLog::AddLineS("voice.log", "Adding %s to talk list", e->AsStdString().c_str()); + fMembers.push_back(e); + } + fFlags |= kDirty; +} + +void plNetTalkList::RemoveMember(plNetTransportMember* e) +{ + int idx=FindMember(e); + if (idx!=-1) + { + plStatusLog::AddLineS("voice.log", "Removing %s from talklist", e->AsStdString().c_str()); + fMembers.erase(fMembers.begin()+idx); + } + fFlags |= kDirty; +} + +void plNetTalkList::Clear() +{ + plNetVoiceList::Clear(); + fFlags |= kDirty; +} + + +/***************************************************************************** +* +* plNetListenList +* +***/ + +void plNetListenList::AddMember(plNetTransportMember* e) +{ + if (FindMember(e)==-1) + { + plStatusLog::AddLineS("voice.log", "Adding %s to listen list ", e->AsStdString().c_str()); + fMembers.push_back(e); + +#if 0 + // call the new member's win audible and set talk icon parameters + + plSoundMsg* pMsg = TRACKED_NEW plSoundMsg; + plArmatureMod* pMod = plArmatureMod::ConvertNoRef(e->GetAvatarKey()->GetObjectPtr()); + if (pMod) + pMsg->AddReceiver(pMod->GetTarget(0)->GetKey()); + pMsg->SetCmd(plSoundMsg::kSetTalkIcon); + pMsg->fIndex = GetNumMembers(); + pMsg->fNameStr = (UInt32)e->GetName(); + plgDispatch::MsgSend(pMsg); +#endif + } +} + +void plNetListenList::RemoveMember(plNetTransportMember* e) +{ + int idx=FindMember(e); + if (idx!=-1) + { + fMembers.erase(fMembers.begin()+idx); + plStatusLog::AddLineS("voice.log", "Removing %s from listen list", e->AsStdString().c_str()); +#if 0 + // call the new member's win audible and set talk icon parameters + + plSoundMsg* pMsg = TRACKED_NEW plSoundMsg; + plArmatureMod* pMod = plArmatureMod::ConvertNoRef(e->GetAvatarKey()->GetObjectPtr()); + if (pMod) + pMsg->AddReceiver(pMod->GetTarget(0)->GetKey()); + pMsg->SetCmd(plSoundMsg::kClearTalkIcon); + plgDispatch::MsgSend(pMsg); +#endif + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetVoiceList.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetVoiceList.h new file mode 100644 index 00000000..25079d29 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClient/plNetVoiceList.h @@ -0,0 +1,107 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetVoiceList_h +#define plNetVoiceList_h + +#include "hsTypes.h" +#include "hsStlUtils.h" + +// +// a simple class used by the net client code to hold listenLists and talkLists +// for voice filtering. +// +class plNetTransportMember; +class plNetVoiceList +{ +protected: + typedef std::vector VoiceListType; +protected: + VoiceListType fMembers; + +public: + plNetVoiceList() {} + virtual ~plNetVoiceList() {} + + int GetNumMembers() const { return fMembers.size(); } + plNetTransportMember* GetMember(int i) const { return fMembers[i]; } + virtual void AddMember(plNetTransportMember* e) = 0; + virtual void RemoveMember(plNetTransportMember* e) = 0; + virtual void Clear() { fMembers.clear(); } + int FindMember(plNetTransportMember* e); // return index or -1 +}; + +// +// Specialized version for listen list +// a list of other player I am listening to +// +class plNetListenList : public plNetVoiceList +{ +private: + double fLastUpdateTime; + int fNumUpdates; +public: + plNetListenList() : fNumUpdates(0) {} + ~plNetListenList() {} + + static float kUpdateInterval; + static int kMaxListenListSize; + static float kMaxListenDistSq; + + void SetLastUpdateTime(double t) { fLastUpdateTime=t; fNumUpdates++; } + double GetLastUpdateTime() { return fLastUpdateTime; } + + hsBool CheckForceSynch() { if (fNumUpdates>10) { fNumUpdates=0; return true;} return false; } + + virtual void AddMember(plNetTransportMember* e); + virtual void RemoveMember(plNetTransportMember* e); + +}; + +// +// Specialized version for talk list +// a list of other players I am talking to +// +class plNetClientMgr; +class plNetTalkList : public plNetVoiceList +{ +private: + enum + { + kDirty = 0x1 + }; + UInt32 fFlags; +public: + plNetTalkList() : fFlags(0) {} + ~plNetTalkList() {} + + void UpdateTransportGroup(plNetClientMgr* nc); + + void AddMember(plNetTransportMember* e); + void RemoveMember(plNetTransportMember* e); + void Clear(); +}; + +#endif // plNetVoiceList_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp new file mode 100644 index 00000000..5350f627 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp @@ -0,0 +1,1527 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.cpp +* +***/ + +#include "plNetClientComm.h" + +#include "../pnAsyncCore/pnAsyncCore.h" +#include "../pnProduct/pnProduct.h" +#include "../pnNetCli/pnNetCli.h" +#include "../plNetGameLib/plNetGameLib.h" +#include "../pnIni/pnIni.h" + +#include "../plMessage/plNetCommMsgs.h" +#include "../plMessage/plNetClientMgrMsg.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plNetCommon/plNetCommon.h" +#include "../plVault/plVault.h" +#include "../plMessage/plAccountUpdateMsg.h" +#include "../plNetClient/plNetClientMgr.h" + +#include "../../FeatureLib/pfMessage/pfKIMsg.h" + +#include "hsResMgr.h" + +#include + +extern hsBool gDataServerLocal; + +/***************************************************************************** +* +* Exported data +* +***/ + +const unsigned kNetCommAllMsgClasses = (unsigned)-1; +FNetCommMsgHandler * kNetCommAllMsgHandlers = (FNetCommMsgHandler*)-1; +const void * kNetCommAllUserStates = (void*)-1; + +struct NetCommParam { + void * param; + plNetCommReplyMsg::EParamType type; +}; + + +/***************************************************************************** +* +* Private +* +***/ + + +/***************************************************************************** +* +* Private data +* +***/ + +static bool s_shutdown; + +static NetCommAccount s_account; +static ARRAY(NetCommPlayer) s_players; +static NetCommPlayer * s_player; +static NetCommAge s_age; +static NetCommAge s_startupAge; +static bool s_needAvatarLoad = true; +static bool s_loginComplete = false; +static bool s_hasFileSrvIpAddress = false; +static ENetError s_authResult = kNetErrAuthenticationFailed; +static wchar s_fileSrvAddr[256]; + +static wchar s_iniServerAddr[256]; +static wchar s_iniFileServerAddr[256]; +static wchar s_iniAccountUsername[kMaxAccountNameLength]; +static ShaDigest s_namePassHash; +static wchar s_iniAuthToken[kMaxPublisherAuthKeyLength]; +static wchar s_iniOS[kMaxGTOSIdLength]; +static bool s_iniReadAccountInfo = true; +static wchar s_iniStartupAgeName[kMaxAgeNameLength]; +static Uuid s_iniStartupAgeInstId; +static wchar s_iniStartupPlayerName[kMaxPlayerNameLength]; +static bool s_netError = false; + + +struct NetCommMsgHandler : THashKeyVal { + HASHLINK(NetCommMsgHandler) link; + FNetCommMsgHandler * proc; + void * state; + + NetCommMsgHandler ( + unsigned msgId, + FNetCommMsgHandler * proc, + void * state + ) : THashKeyVal(msgId) + , proc(proc) + , state(state) + { } +}; + +static HASHTABLEDECL( + NetCommMsgHandler, + THashKeyVal, + link +) s_handlers; + +static NetCommMsgHandler s_defaultHandler(0, nil, nil); +static NetCommMsgHandler s_preHandler(0, nil, nil); + + +//============================================================================ +static void INetLogCallback ( + ELogSeverity severity, + const wchar msg[] +) { + // Use the async log facility + AsyncLogWriteMsg(ProductShortName(), severity, msg); +} + +//============================================================================ +static void INetErrorCallback ( + ENetProtocol protocol, + ENetError error +) { + NetClientDestroy(false); + + plNetClientMgrMsg * msg = NEWZERO(plNetClientMgrMsg); + msg->type = plNetClientMgrMsg::kCmdDisableNet; + msg->yes = true; + msg->AddReceiver(plNetClientApp::GetInstance()->GetKey()); + + switch (error) + { + case kNetErrKickedByCCR: + StrPrintf( + msg->str, + arrsize(msg->str), + "You have been kicked by a CCR." + ); + break; + + default: + // Until we get some real error handling, this'll ensure no errors + // fall thru the cracks and we hang forever wondering what's up. + StrPrintf( + // buf + msg->str, + arrsize(msg->str), + // fmt + "Network error %u, %S.\n" + "protocol: %S\n" + ,// values + error, + NetErrorToString(error), + NetProtocolToString(protocol) + ); + s_netError = true; + } + + msg->Send(); +} + +//============================================================================ +static void IPreInitNetErrorCallback ( + ENetProtocol protocol, + ENetError error +) { + s_authResult = error; + s_loginComplete = true; +} + +//============================================================================ +static void INetBufferCallback ( + unsigned type, + unsigned bytes, + const byte buffer[] +) { + if (!plFactory::IsValidClassIndex(type)) { + LogMsg(kLogError, "NetComm: received junk propagated buffer"); + return; + } + plNetMessage * msg = plNetMessage::ConvertNoRef(plFactory::Create(type)); + if (!msg) { + LogMsg(kLogError, "NetComm: could not convert plNetMessage to class %u", type); + return; + } + + if (!msg->PeekBuffer((const char *)buffer, bytes)) { + LogMsg(kLogError, "NetComm: plNetMessage %u failed to peek buffer", type); + return; + } + + NetCommRecvMsg(msg); + + msg->UnRef(); +} + +//============================================================================ +static void INotifyNewBuildCallback () { + + if (!hsgResMgr::ResMgr()) + return; + + if (!NetCommGetPlayer()) + return; + if (!NetCommGetPlayer()->playerInt) + return; + if (!NetCommGetAge()) + return; + if (!NetCommGetAge()->ageInstId) + return; + + pfKIMsg * msg = NEW(pfKIMsg)(pfKIMsg::kHACKChatMsg); + msg->SetString("Uru has been updated. Please quit the game and log back in."); + msg->SetUser("Updater Service", plNetClientApp::GetInstance()->GetPlayerID()); + msg->SetFlags(pfKIMsg::kAdminMsg); + msg->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce, 0); + msg->SetBCastFlag(plMessage::kLocalPropagate, 1); + msg->Send(); +} + +//============================================================================ +static void INotifyAuthConnectedCallback () { + + if (!hsgResMgr::ResMgr()) + return; + + plNetCommAuthConnectedMsg * msg = NEWZERO(plNetCommAuthConnectedMsg); + msg->Send(); +} + +//============================================================================ +static void PlayerInitCallback ( + ENetError result, + void * param +) { + if (IS_NET_ERROR(result) && (result != kNetErrVaultNodeNotFound)) { + s_player = nil; + } + else { + // Ensure the city link has the required spawn points + plAgeInfoStruct info; + info.SetAgeFilename(kCityAgeFilename); + if (RelVaultNode * rvn = VaultGetOwnedAgeLinkIncRef(&info)) { + VaultAgeLinkNode acc(rvn); + acc.AddSpawnPoint(plSpawnPointInfo(kCityFerryTerminalLinkTitle, kCityFerryTerminalLinkSpawnPtName)); + rvn->DecRef(); + } + + VaultProcessPlayerInbox(); + } + + plNetCommActivePlayerMsg * msg = NEW(plNetCommActivePlayerMsg); + msg->result = result; + msg->param = param; + msg->Send(); + + plAccountUpdateMsg * updateMsg = TRACKED_NEW plAccountUpdateMsg(plAccountUpdateMsg::kActivePlayer); + updateMsg->SetPlayerInt(NetCommGetPlayer()->playerInt); + updateMsg->SetResult((unsigned)result); + updateMsg->SetBCastFlag(plMessage::kBCastByExactType); + updateMsg->Send(); +} + +//============================================================================ +static void INetCliAuthSetPlayerRequestCallback ( + ENetError result, + void * param +) { + if (!s_player) { + PlayerInitCallback(result, param); + } + else if (IS_NET_ERROR(result) && (result != kNetErrVaultNodeNotFound)) { + s_player = nil; + PlayerInitCallback(result, param); + } + else { + s_needAvatarLoad = true; + + VaultDownload( + L"SetActivePlayer", + s_player->playerInt, + PlayerInitCallback, + param, + nil, + nil + ); + } +} + +//============================================================================ +static void LoginPlayerInitCallback ( + ENetError result, + void * param +) { + if (IS_NET_ERROR(result) && (result != kNetErrVaultNodeNotFound)) + s_player = nil; + else + VaultProcessPlayerInbox(); + + { + plNetCommAuthMsg * msg = NEW(plNetCommAuthMsg); + msg->result = result; + msg->param = param; + msg->Send(); + } + { + plNetCommActivePlayerMsg * msg = NEW(plNetCommActivePlayerMsg); + msg->result = result; + msg->param = param; + msg->Send(); + } + { + plAccountUpdateMsg * msg = TRACKED_NEW plAccountUpdateMsg(plAccountUpdateMsg::kActivePlayer); + msg->SetPlayerInt(NetCommGetPlayer()->playerInt); + msg->SetResult((unsigned)result); + msg->SetBCastFlag(plMessage::kBCastByExactType); + msg->Send(); + } +} + +//============================================================================ +static void INetCliAuthLoginSetPlayerRequestCallback ( + ENetError result, + void * param +) { + if (IS_NET_ERROR(result) && (result != kNetErrVaultNodeNotFound)) { + s_player = nil; + + plNetCommAuthMsg * msg = NEW(plNetCommAuthMsg); + msg->result = result; + msg->param = param; + msg->Send(); + } + else { + VaultDownload( + L"SetActivePlayer", + s_player->playerInt, + LoginPlayerInitCallback, + param, + nil, + nil + ); + } +} + +//============================================================================ +static void INetCliAuthLoginRequestCallback ( + ENetError result, + void * param, + const Uuid & accountUuid, + unsigned accountFlags, + unsigned billingType, + const NetCliAuthPlayerInfo playerInfoArr[], + unsigned playerCount +) { + s_authResult = result; + + s_player = nil; + s_players.Clear(); + + bool wantsStartUpAge = ( + !StrLen(s_startupAge.ageDatasetName) || + 0 == StrCmpI(s_startupAge.ageDatasetName, "StartUp") + ); + + s_loginComplete = true; + + if (!IS_NET_ERROR(result) || result == kNetErrVaultNodeNotFound) { + s_account.accountUuid = accountUuid; + s_account.accountFlags = accountFlags; + s_account.billingType = billingType; + s_players.GrowToCount(playerCount, true); + for (unsigned i = 0; i < playerCount; ++i) { + LogMsg(kLogDebug, L"Player %u: %s explorer: %u", playerInfoArr[i].playerInt, playerInfoArr[i].playerName, playerInfoArr[i].explorer); + s_players[i].playerInt = playerInfoArr[i].playerInt; + s_players[i].explorer = playerInfoArr[i].explorer; + StrCopy(s_players[i].playerName, playerInfoArr[i].playerName, arrsize(s_players[i].playerName)); + StrToAnsi(s_players[i].playerNameAnsi, playerInfoArr[i].playerName, arrsize(s_players[i].playerNameAnsi)); + StrToAnsi(s_players[i].avatarDatasetName, playerInfoArr[i].avatarShape, arrsize(s_players[i].avatarDatasetName)); + if (!wantsStartUpAge && 0 == StrCmpI(s_players[i].playerName, s_iniStartupPlayerName, (unsigned)-1)) + s_player = &s_players[i]; + } + } + else + s_account.accountUuid = kNilGuid; + + // If they specified an alternate age, but we couldn't find the player, force + // the StartUp age to load so that they may select/create a player first. + if (!wantsStartUpAge && !s_player) + StrCopy(s_startupAge.ageDatasetName, "StartUp", arrsize(s_startupAge.ageDatasetName)); + + // If they specified an alternate age, and we found the player, set the active player now + // so that the link operation will be successful once the client is finished initializing. + if (!wantsStartUpAge && s_player) { + NetCliAuthSetPlayerRequest( + s_player->playerInt, + INetCliAuthLoginSetPlayerRequestCallback, + param + ); + } +} + +//============================================================================ +static void INetCliAuthCreatePlayerRequestCallback ( + ENetError result, + void * param, + const NetCliAuthPlayerInfo & playerInfo +) { + ref(param); + + if (IS_NET_ERROR(result)) { + LogMsg(kLogDebug, L"Create player failed: %s", NetErrorToString(result)); + } + else { + LogMsg(kLogDebug, L"Created player %s: %u", playerInfo.playerName, playerInfo.playerInt); + + unsigned currPlayer = s_player ? s_player->playerInt : 0; + NetCommPlayer * newPlayer = s_players.New(); + + newPlayer->playerInt = playerInfo.playerInt; + newPlayer->explorer = playerInfo.explorer; + StrCopy(newPlayer->playerName, playerInfo.playerName, arrsize(newPlayer->playerName)); + StrToAnsi(newPlayer->playerNameAnsi, playerInfo.playerName, arrsize(newPlayer->playerNameAnsi)); + StrToAnsi(newPlayer->avatarDatasetName, playerInfo.avatarShape, arrsize(newPlayer->avatarDatasetName)); + + { for (unsigned i = 0; i < s_players.Count(); ++i) { + if (s_players[i].playerInt == currPlayer) { + s_player = &s_players[i]; + break; + } + }} + } + + plAccountUpdateMsg* updateMsg = TRACKED_NEW plAccountUpdateMsg(plAccountUpdateMsg::kCreatePlayer); + updateMsg->SetPlayerInt(playerInfo.playerInt); + updateMsg->SetResult((unsigned)result); + updateMsg->SetBCastFlag(plMessage::kBCastByExactType); + updateMsg->Send(); +} + +//============================================================================ +static void INetCliAuthDeletePlayerCallback ( + ENetError result, + void * param +) { + unsigned playerInt = (unsigned)param; + + if (IS_NET_ERROR(result)) { + LogMsg(kLogDebug, L"Delete player failed: %d %s", playerInt, NetErrorToString(result)); + } + else { + LogMsg(kLogDebug, L"Player deleted: %d", playerInt); + + unsigned currPlayer = s_player ? s_player->playerInt : 0; + + {for (unsigned i = 0; i < s_players.Count(); ++i) { + if (s_players[i].playerInt == playerInt) { + s_players.DeleteUnordered(i); + break; + } + }} + + {for (unsigned i = 0; i < s_players.Count(); ++i) { + if (s_players[i].playerInt == currPlayer) { + s_player = &s_players[i]; + break; + } + }} + } + + plAccountUpdateMsg* updateMsg = TRACKED_NEW plAccountUpdateMsg(plAccountUpdateMsg::kDeletePlayer); + updateMsg->SetPlayerInt(playerInt); + updateMsg->SetResult((unsigned)result); + updateMsg->SetBCastFlag(plMessage::kBCastByExactType); + updateMsg->Send(); +} + +//============================================================================ +static void INetCliAuthChangePasswordCallback ( + ENetError result, + void * param +) { + ref(param); + + if (IS_NET_ERROR(result)) { + LogMsg(kLogDebug, L"Change password failed: %s", NetErrorToString(result)); + } + else { + LogMsg(kLogDebug, L"Password changed!"); + } + + plAccountUpdateMsg* updateMsg = TRACKED_NEW plAccountUpdateMsg(plAccountUpdateMsg::kChangePassword); + updateMsg->SetPlayerInt(0); + updateMsg->SetResult((unsigned)result); + updateMsg->SetBCastFlag(plMessage::kBCastByExactType); + updateMsg->Send(); +} + +//============================================================================ +static void INetCliAuthGetPublicAgeListCallback ( + ENetError result, + void * param, + const ARRAY(NetAgeInfo) & ages +) { + NetCommParam * cp = (NetCommParam *) param; + + plNetCommPublicAgeListMsg * msg = NEWZERO(plNetCommPublicAgeListMsg); + msg->result = result; + msg->param = cp->param; + msg->ptype = cp->type; + msg->ages.Set(ages.Ptr(), ages.Count()); + msg->Send(); + + DEL(cp); +} + +//============================================================================ +static void INetAuthFileListRequestCallback ( + ENetError result, + void * param, + const NetCliAuthFileInfo infoArr[], + unsigned infoCount +) { + plNetCommFileListMsg * msg = NEW(plNetCommFileListMsg); + msg->result = result; + msg->param = param; + msg->fileInfoArr.Set(infoArr, infoCount); + msg->Send(); +} + +//============================================================================ +static void INetCliAuthFileRequestCallback ( + ENetError result, + void * param, + const wchar filename[], + hsStream * writer +) { + plNetCommFileDownloadMsg * msg = NEW(plNetCommFileDownloadMsg); + msg->result = result; + msg->writer = writer; + StrCopy(msg->filename, filename, arrsize(filename)); + msg->Send(); +} + +//============================================================================ +static void INetCliGameJoinAgeRequestCallback ( + ENetError result, + void * param +) { + plNetCommLinkToAgeMsg * msg = NEW(plNetCommLinkToAgeMsg); + msg->result = result; + msg->param = param; + msg->Send(); +} + +//============================================================================ +static void INetCliAuthAgeRequestCallback ( + ENetError result, + void * param, + unsigned ageMcpId, + unsigned ageVaultId, + const Uuid & ageInstId, + NetAddressNode gameAddr +) { + if (!IS_NET_ERROR(result) || result == kNetErrVaultNodeNotFound) { + s_age.ageInstId = ageInstId; + s_age.ageVaultId = ageVaultId; + + wchar gameAddrStr[64]; + wchar ageInstIdStr[64]; + NetAddressNodeToString(gameAddr, gameAddrStr, arrsize(gameAddrStr)); + LogMsg( + kLogPerf, + L"Connecting to game server %s, ageInstId %s", + gameAddrStr, + GuidToString(ageInstId, ageInstIdStr, arrsize(ageInstIdStr)) + ); + NetCliGameDisconnect(); + NetCliGameStartConnect(gameAddr); + NetCliGameJoinAgeRequest( + ageMcpId, + s_account.accountUuid, + s_player->playerInt, + INetCliGameJoinAgeRequestCallback, + param + ); + } + else { + INetCliGameJoinAgeRequestCallback( + result, + param + ); + } +} + +//============================================================================ +static void INetCliAuthUpgradeVisitorRequestCallback ( + ENetError result, + void * param +) { + unsigned playerInt = (unsigned)param; + + if (IS_NET_ERROR(result)) { + LogMsg(kLogDebug, L"Upgrade visitor failed: %d %s", playerInt, NetErrorToString(result)); + } + else { + LogMsg(kLogDebug, L"Upgrade visitor succeeded: %d", playerInt); + + {for (unsigned i = 0; i < s_players.Count(); ++i) { + if (s_players[i].playerInt == playerInt) { + s_players[i].explorer = true; + break; + } + }} + } + + plAccountUpdateMsg* updateMsg = TRACKED_NEW plAccountUpdateMsg(plAccountUpdateMsg::kUpgradePlayer); + updateMsg->SetPlayerInt(playerInt); + updateMsg->SetResult((unsigned)result); + updateMsg->SetBCastFlag(plMessage::kBCastByExactType); + updateMsg->Send(); +} + +//============================================================================ +static void INetCliAuthSendFriendInviteCallback ( + ENetError result, + void * param +) { + pfKIMsg* kiMsg = TRACKED_NEW pfKIMsg(pfKIMsg::kFriendInviteSent); + kiMsg->SetIntValue((Int32)result); + kiMsg->Send(); +} + + +//============================================================================ +static void IReadNetIni() { + wchar filename[MAX_PATH]; + StrPrintf(filename, arrsize(filename), L"%s.cfg", ProductCoreName()); + + wchar pathAndName[MAX_PATH]; + PathGetInitDirectory(pathAndName, arrsize(pathAndName)); + PathAddFilename(pathAndName, pathAndName, filename, arrsize(pathAndName)); + +#ifndef PLASMA_EXTERNAL_RELEASE + // internal dev build will override user-based setting with local folder if it's there + wchar localPathAndName[MAX_PATH]; + PathAddFilename(localPathAndName, L"init", filename, arrsize(localPathAndName)); + if (PathDoesFileExist(localPathAndName)) + StrCopy(pathAndName, localPathAndName, arrsize(pathAndName)); +#endif + + Ini * ini = IniOpen(pathAndName); + + wchar password[kMaxPasswordLength]; + + if (ini) { + // Read [Net.Server] section + IniGetString( + IniGetFirstValue( + ini, + L"Net.Server", + L"Addr", + nil + ), + s_iniServerAddr, + arrsize(s_iniServerAddr), + 0, + L"" + ); + + // Read [Net.FileServer] section + IniGetString( + IniGetFirstValue( + ini, + L"Net.FileServer", + L"Addr", + nil + ), + s_iniFileServerAddr, + arrsize(s_iniFileServerAddr), + 0, + s_iniServerAddr + ); + + if (s_iniReadAccountInfo) { + // Read [Net.Account] section + IniGetString( + IniGetFirstValue( + ini, + L"Net.Account", + L"Username", + nil + ), + s_iniAccountUsername, + arrsize(s_iniAccountUsername), + 0, + L"" + ); + IniGetString( + IniGetFirstValue( + ini, + L"Net.Account", + L"Password", + nil + ), + password, + arrsize(password), + 0, + L"" + ); + + // Read [Net.Startup] section + IniGetString( + IniGetFirstValue( + ini, + L"Net.Startup", + L"AgeName", + nil + ), + s_iniStartupAgeName, + arrsize(s_iniStartupAgeName), + 0, + L"StartUp" + ); + IniGetString( + IniGetFirstValue( + ini, + L"Net.Startup", + L"PlayerName", + nil + ), + s_iniStartupPlayerName, + arrsize(s_iniStartupPlayerName), + 0, + L"" + ); + + CryptHashPassword(s_iniAccountUsername, password, &s_namePassHash); + } + else { + StrCopy(s_iniStartupAgeName, L"StartUp", arrsize(s_iniStartupAgeName)); + } + } + +#ifndef PLASMA_EXTERNAL_RELEASE + // @@@: Internal dev build only: Drop a default version of the file if not found + if (!ini && BuildType() == BUILD_TYPE_DEV) { + EFileError fileError; + qword fileSize; + qword lastWrite; + AsyncFile file = AsyncFileOpen( + pathAndName, + nil, + &fileError, + kAsyncFileReadAccess|kAsyncFileWriteAccess, + kAsyncFileModeCreateNew, + 0, + nil, + &fileSize, + &lastWrite + ); + + if (file) { + char line[2048]; + StrPrintf( + line, + arrsize(line), + // format + "[Net.Server]\r\n" + "\tAddr=%S\r\n" + "\r\n" + "[Net.FileServer]\r\n" + "\tAddr=%S\r\n" + "\r\n" + "[Net.Account]\r\n" + "\tUsername=%S\r\n" + "\tPassword=AccountPassword\r\n" + "\r\n" + "[Net.Startup]\r\n" + "\tAgeName=%S\r\n" + "\tPlayerName=%S\r\n" + , // values + L"shard", + L"shard", + L"AccountUserName", + L"StartUp", + L"PlayerName", + nil + ); + AsyncFileWrite(file, 0, line, StrLen(line), kAsyncFileRwSync, nil); + AsyncFileClose(file, kAsyncFileDontTruncate); + } + } +#endif + + // Set startup age info + ZERO(s_startupAge); + + if (StrLen(s_iniStartupAgeName) == 0) + StrCopy(s_startupAge.ageDatasetName, "StartUp", arrsize(s_startupAge.ageDatasetName)); + else + StrToAnsi(s_startupAge.ageDatasetName, s_iniStartupAgeName, arrsize(s_startupAge.ageDatasetName)); + + s_startupAge.ageInstId = s_iniStartupAgeInstId; + StrCopy(s_startupAge.spawnPtName, "LinkInPointDefault", arrsize(s_startupAge.spawnPtName)); + + IniClose(ini); +} + +//============================================================================ +static void FileSrvIpAddressCallback ( + ENetError result, + void * param, + const wchar addr[] +) { + StrCopy(s_fileSrvAddr, addr, arrsize(s_fileSrvAddr)); + s_hasFileSrvIpAddress = true; +} + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +const NetCommPlayer * const NetCommGetPlayer () { + static NetCommPlayer s_nilPlayer; + return s_player ? s_player : &s_nilPlayer; +} + +//============================================================================ +const ARRAY(NetCommPlayer)& NetCommGetPlayerList () { + return s_players; +} + +//============================================================================ +unsigned NetCommGetPlayerCount () { + return s_players.Count(); +} + +//============================================================================ +const NetCommAccount * const NetCommGetAccount () { + return &s_account; +} + +//============================================================================ +bool NetCommIsLoginComplete() { + return s_loginComplete; +} + +//============================================================================ +const NetCommAge * const NetCommGetAge () { + return &s_age; +} + +//============================================================================ +const NetCommAge * const NetCommGetStartupAge () { + return &s_startupAge; +} + +//============================================================================ +bool NetCommNeedToLoadAvatar () { + return s_needAvatarLoad; +} + +//============================================================================ +void NetCommSetAvatarLoaded (bool loaded /* = true */) { + s_needAvatarLoad = !loaded; +} + +//============================================================================ +void NetCommChangeMyPassword ( + const wchar password[] +) { + NetCliAuthAccountChangePasswordRequest(s_account.accountName, password, INetCliAuthChangePasswordCallback, nil); +} + +//============================================================================ +void NetCommStartup () { + s_shutdown = false; + + LogRegisterHandler(INetLogCallback); + AsyncCoreInitialize(); + AsyncLogInitialize(L"Log", false); + wchar productString[256]; + ProductString(productString, arrsize(productString)); + LogMsg(kLogPerf, L"Client: %s", productString); + + NetClientInitialize(); + NetClientSetErrorHandler(IPreInitNetErrorCallback); + NetCliGameSetRecvBufferHandler(INetBufferCallback); +// NetCliAuthSetRecvBufferHandler(INetBufferCallback); + NetCliAuthSetNotifyNewBuildHandler(INotifyNewBuildCallback); + NetCliAuthSetConnectCallback(INotifyAuthConnectedCallback); + + IReadNetIni(); +} + +//============================================================================ +void NetCommShutdown () { + s_shutdown = true; + + NetCommSetDefaultMsgHandler(nil, nil); + NetCommSetMsgPreHandler(nil, nil); + NetCommRemoveMsgHandler( + kNetCommAllMsgClasses, + kNetCommAllMsgHandlers, + kNetCommAllUserStates + ); + + NetCliGameDisconnect(); + NetCliAuthDisconnect(); + if (!gDataServerLocal) + NetCliFileDisconnect(); + + NetClientDestroy(false); + AsyncLogDestroy(); + AsyncCoreDestroy(30 * 1000); + LogUnregisterHandler(INetLogCallback); +} + +//============================================================================ +void NetCommEnableNet ( + bool enabled, + bool wait +) { + if (enabled) { + NetClientInitialize(); + NetClientSetErrorHandler(INetErrorCallback); + NetCliGameSetRecvBufferHandler(INetBufferCallback); +// NetCliAuthSetRecvBufferHandler(INetBufferCallback); + } + else { + NetClientDestroy(wait); + } +} + +//============================================================================ +void NetCommActivatePostInitErrorHandler () { + NetClientSetErrorHandler(INetErrorCallback); +} + +//============================================================================ +void NetCommUpdate () { + // plClient likes to recursively call us on occasion; debounce that crap. + static long s_updating; + if (0 == AtomicSet(&s_updating, 1)) { + NetClientUpdate(); + AtomicSet(&s_updating, 0); + } +} + +//============================================================================ +void NetCommConnect () { + + const wchar ** addrs; + unsigned count; + + count = GetAuthSrvHostnames(&addrs); + NetCliAuthStartConnect(addrs, count); + + if (!gDataServerLocal) { + + // if a cmdline override was specified for a filesrv, connect directly to the fileserver rather than going through the gatekeeper + if(GetFileSrvHostnames(&addrs) && FileSrvHostnameOverride()) + { + NetCliFileStartConnect(addrs, count); + } + else + { + count = GetGateKeeperSrvHostnames(&addrs); + NetCliGateKeeperStartConnect(addrs, count); + + // request a file server ip address + NetCliGateKeeperFileSrvIpAddressRequest(FileSrvIpAddressCallback, nil, false); + + while(!s_hasFileSrvIpAddress && !s_netError) { + NetClientUpdate(); + AsyncSleep(10); + } + + const wchar * fileSrv[] = { + s_fileSrvAddr + }; + NetCliFileStartConnect(fileSrv, 1); + } + } +} + +//============================================================================ +void NetCommDisconnect () { + NetCliAuthDisconnect(); + + if (!gDataServerLocal) { + NetCliFileDisconnect(); + } +} + +//============================================================================ +void NetCommSendMsg ( + plNetMessage * msg +) { + msg->SetPlayerID(NetCommGetPlayer()->playerInt); + + unsigned msgSize = msg->GetPackSize(); + byte * buf = ALLOCA(byte, msgSize); + msg->PokeBuffer((char *)buf, msgSize); + + switch (msg->GetNetProtocol()) { + case kNetProtocolCli2Auth: + NetCliAuthPropagateBuffer( + msg->ClassIndex(), + msgSize, + buf + ); + break; + + case kNetProtocolCli2Game: + NetCliGamePropagateBuffer( + msg->ClassIndex(), + msgSize, + buf + ); + break; + + DEFAULT_FATAL(msg->GetNetProtocol()); + } +} + +//============================================================================ +void NetCommRecvMsg ( + plNetMessage * msg +) { + for (;;) { + if (s_preHandler.proc && kOK_MsgConsumed == s_preHandler.proc(msg, s_preHandler.state)) + break; + + unsigned msgClassIdx = msg->ClassIndex(); + NetCommMsgHandler * handler = s_handlers.Find(msgClassIdx); + + if (!handler && s_defaultHandler.proc) { + s_defaultHandler.proc(msg, s_defaultHandler.state); + break; + } + while (handler) { + if (kOK_MsgConsumed == handler->proc(msg, handler->state)) + break; + handler = s_handlers.FindNext(msgClassIdx, handler); + } + break; + } +} + +//============================================================================ +void NetCommAddMsgHandlerForType ( + unsigned msgClassIdx, + FNetCommMsgHandler * proc, + void * state +) { + for (unsigned i = 0; i < plFactory::GetNumClasses(); ++i) { + if (plFactory::DerivesFrom(msgClassIdx, i)) + NetCommAddMsgHandlerForExactType(i, proc, state); + } +} + +//============================================================================ +void NetCommAddMsgHandlerForExactType ( + unsigned msgClassIdx, + FNetCommMsgHandler * proc, + void * state +) { + ASSERT(msgClassIdx != kNetCommAllMsgClasses); + ASSERT(proc && proc != kNetCommAllMsgHandlers); + ASSERT(!state || (state && state != kNetCommAllUserStates)); + + NetCommRemoveMsgHandler(msgClassIdx, proc, state); + NetCommMsgHandler * handler = NEW(NetCommMsgHandler)(msgClassIdx, proc, state); + + s_handlers.Add(handler); +} + +//============================================================================ +void NetCommRemoveMsgHandler ( + unsigned msgClassIdx, + FNetCommMsgHandler * proc, + const void * state +) { + NetCommMsgHandler * next, * handler = s_handlers.Head(); + for (; handler; handler = next) { + next = handler->link.Next(); + if (handler->GetValue() != msgClassIdx) + if (msgClassIdx != kNetCommAllMsgClasses) + continue; + if (handler->proc != proc) + if (proc != kNetCommAllMsgHandlers) + continue; + if (handler->state != state) + if (state != kNetCommAllUserStates) + continue; + + // We found a matching handler, delete it + DEL(handler); + } +} + +//============================================================================ +void NetCommSetDefaultMsgHandler ( + FNetCommMsgHandler * proc, + void * state +) { + s_defaultHandler.proc = proc; + s_defaultHandler.state = state; +} + +//============================================================================ +void NetCommSetMsgPreHandler ( + FNetCommMsgHandler * proc, + void * state +) { + s_preHandler.proc = proc; + s_preHandler.state = state; +} + +//============================================================================ +void NetCommSetAccountUsernamePassword ( + wchar username[], + const ShaDigest & namePassHash +) { + StrCopy(s_iniAccountUsername, username, arrsize(s_iniAccountUsername)); + s_namePassHash = namePassHash; + + s_iniReadAccountInfo = false; +} + +//============================================================================ +void NetCommSetAuthTokenAndOS ( + wchar authToken[], + wchar os[] +) { + if (authToken) + StrCopy(s_iniAuthToken, authToken, arrsize(s_iniAuthToken)); + if (os) + StrCopy(s_iniOS, os, arrsize(s_iniOS)); +} + +//============================================================================ +ENetError NetCommGetAuthResult () { + return s_authResult; +} + +//============================================================================ +void NetCommSetReadIniAccountInfo(bool readFromIni) { + s_iniReadAccountInfo = readFromIni; +} + +//============================================================================ +void NetCommAuthenticate ( + void * param +) { + s_loginComplete = false; + + StrCopy( + s_account.accountName, + s_iniAccountUsername, + arrsize(s_account.accountName) + ); + StrToAnsi( + s_account.accountNameAnsi, + s_iniAccountUsername, + arrsize(s_account.accountNameAnsi) + ); + s_account.accountNamePassHash = s_namePassHash; + + NetCliAuthLoginRequest( + s_account.accountName, + &s_account.accountNamePassHash, + s_iniAuthToken, + s_iniOS, + INetCliAuthLoginRequestCallback, + nil + ); +} + +//============================================================================ +void NetCommLinkToAge ( // --> plNetCommLinkToAgeMsg + const NetCommAge & age, + void * param +) { + s_age = age; + + if (plNetClientMgr::GetInstance()->GetFlagsBit(plNetClientApp::kLinkingToOfflineAge)) { + plNetCommLinkToAgeMsg * msg = NEW(plNetCommLinkToAgeMsg); + msg->result = kNetSuccess; + msg->param = nil; + msg->Send(); + + return; + } + + wchar wAgeName[kMaxAgeNameLength]; + StrToUnicode(wAgeName, s_age.ageDatasetName, arrsize(wAgeName)); + + NetCliAuthAgeRequest( + wAgeName, + s_age.ageInstId, + INetCliAuthAgeRequestCallback, + param + ); +} + +//============================================================================ +void NetCommSetActivePlayer (//--> plNetCommActivePlayerMsg + unsigned desiredPlayerInt, + void * param +) { + unsigned playerInt = 0; + + if (s_player) { + if (RelVaultNode* rvn = VaultGetPlayerInfoNodeIncRef()) { + VaultPlayerInfoNode pInfo(rvn); + pInfo.SetAgeInstName(nil); + pInfo.SetAgeInstUuid(kNilGuid); + pInfo.SetOnline(false); + NetCliAuthVaultNodeSave(rvn, nil, nil); + + rvn->DecRef(); + } + + VaultCull(s_player->playerInt); + } + + if (desiredPlayerInt == 0) + s_player = nil; + else { + for (unsigned i = 0; i < s_players.Count(); ++i) { + if (s_players[i].playerInt == desiredPlayerInt) { + playerInt = desiredPlayerInt; + s_player = &s_players[i]; + break; + } + else if (0 == StrCmpI(s_players[i].playerName, s_iniStartupPlayerName, arrsize(s_players[i].playerName))) { + playerInt = s_players[i].playerInt; + s_player = &s_players[i]; + } + } + ASSERT(s_player); + } + + NetCliAuthSetPlayerRequest( + playerInt, + INetCliAuthSetPlayerRequestCallback, + param + ); +} + +//============================================================================ +void NetCommCreatePlayer ( // --> plNetCommCreatePlayerMsg + const char playerName[], + const char avatarShape[], + const char friendInvite[], + unsigned createFlags, + void * param +) { + wchar wplayerName[kMaxPlayerNameLength]; + wchar wavatarShape[MAX_PATH]; + wchar wfriendInvite[MAX_PATH]; + + StrToUnicode(wplayerName, playerName, arrsize(wplayerName)); + StrToUnicode(wavatarShape, avatarShape, arrsize(wavatarShape)); + StrToUnicode(wfriendInvite, friendInvite, arrsize(wfriendInvite)); + + NetCliAuthPlayerCreateRequest( + wplayerName, + wavatarShape, + (friendInvite != NULL) ? wfriendInvite : NULL, + INetCliAuthCreatePlayerRequestCallback, + param + ); +} + +//============================================================================ +void NetCommCreatePlayer ( // --> plNetCommCreatePlayerMsg + const wchar playerName[], + const wchar avatarShape[], + const wchar friendInvite[], + unsigned createFlags, + void * param +) { + NetCliAuthPlayerCreateRequest( + playerName, + avatarShape, + (friendInvite != NULL) ? friendInvite : NULL, + INetCliAuthCreatePlayerRequestCallback, + param + ); +} + +//============================================================================ +void NetCommDeletePlayer ( // --> plNetCommDeletePlayerMsg + unsigned playerInt, + void * param +) { + ASSERTMSG(!param, "'param' will not be propagated to your callback function, you may modify the code to support this"); + ASSERT(NetCommGetPlayer()->playerInt != playerInt); + + NetCliAuthPlayerDeleteRequest( + playerInt, + INetCliAuthDeletePlayerCallback, + (void*)playerInt + ); +} + +//============================================================================ +void NetCommGetPublicAgeList (//-> plNetCommPublicAgeListMsg + const char ageName[], + void * param, + plNetCommReplyMsg::EParamType ptype +) { + NetCommParam * cp = NEW(NetCommParam); + cp->param = param; + cp->type = ptype; + + wchar wStr[MAX_PATH]; + StrToUnicode(wStr, ageName, arrsize(wStr)); + NetCliAuthGetPublicAgeList( + wStr, + INetCliAuthGetPublicAgeListCallback, + cp + ); +} + +//============================================================================ +void NetCommSetAgePublic ( // --> no msg + unsigned ageInfoId, + bool makePublic +) { + NetCliAuthSetAgePublic( + ageInfoId, + makePublic + ); +} + +//============================================================================ +void NetCommCreatePublicAge (// --> plNetCommPublicAgeMsg + const char ageName[], + const Uuid & ageInstId, + void * param +) { +} + +//============================================================================ +void NetCommRemovePublicAge(// --> plNetCommPublicAgeMsg + const Uuid & ageInstId, + void * param +) { +} + +//============================================================================ +void NetCommRegisterOwnedAge ( + const NetCommAge & age, + const char ageInstDesc[], + unsigned playerInt, + void * param +) { +} + +//============================================================================ +void NetCommUnregisterOwnedAge ( + const char ageName[], + unsigned playerInt, + void * param +) { +} + +//============================================================================ +void NetCommRegisterVisitAge ( + const NetCommAge & age, + const char ageInstDesc[], + unsigned playerInt, + void * param +) { +} + +//============================================================================ +void NetCommUnregisterVisitAge ( + const Uuid & ageInstId, + unsigned playerInt, + void * param +) { +} + +//============================================================================ +void NetCommConnectPlayerVault ( + void * param +) { +} + +//============================================================================ +void NetCommConnectAgeVault ( + const Uuid & ageInstId, + void * param +) { +} + +//============================================================================ +void NetCommUpgradeVisitorToExplorer ( + unsigned playerInt, + void * param +) { + NetCliAuthUpgradeVisitorRequest( + playerInt, + INetCliAuthUpgradeVisitorRequestCallback, + (void*)playerInt + ); +} + +//============================================================================ +void NetCommSetCCRLevel ( + unsigned ccrLevel +) { + if (RelVaultNode * rvnInfo = VaultGetPlayerInfoNodeIncRef()) { + VaultPlayerInfoNode pInfo(rvnInfo); + pInfo.SetCCRLevel(ccrLevel); + rvnInfo->DecRef(); + } + + NetCliAuthSetCCRLevel(ccrLevel); +} + +//============================================================================ +void NetCommSendFriendInvite ( + const wchar emailAddress[], + const wchar toName[], + const Uuid& inviteUuid +) { + NetCliAuthSendFriendInvite( + emailAddress, + toName, + inviteUuid, + INetCliAuthSendFriendInviteCallback, + nil + ); +} + + +/***************************************************************************** +* +* Msg handler interface - compatibility layer with legacy code +* +***/ + + +//////////////////////////////////////////////////////////////////// + +// plNetClientComm ---------------------------------------------- +plNetClientComm::plNetClientComm() +{ +} + +// ~plNetClientComm ---------------------------------------------- +plNetClientComm::~plNetClientComm() +{ + NetCommSetMsgPreHandler(nil, nil); +} + +// AddMsgHandlerForType ---------------------------------------------- +void plNetClientComm::AddMsgHandlerForType( UInt16 msgClassIdx, MsgHandler* handler ) +{ + int i; + for( i = 0; i < plFactory::GetNumClasses(); i++ ) + { + if ( plFactory::DerivesFrom( msgClassIdx, i ) ) + AddMsgHandlerForExactType( i, handler ); + } +} + +// AddMsgHandlerForExactType ---------------------------------------------- +void plNetClientComm::AddMsgHandlerForExactType( UInt16 msgClassIdx, MsgHandler* handler ) +{ + NetCommAddMsgHandlerForExactType(msgClassIdx, MsgHandler::StaticMsgHandler, handler); +} + +// RemoveMsgHandler ---------------------------------------------- +bool plNetClientComm::RemoveMsgHandler( MsgHandler* handler ) +{ + NetCommRemoveMsgHandler(kNetCommAllMsgClasses, kNetCommAllMsgHandlers, handler); + return true; +} + +// SetDefaultHandler ---------------------------------------------- +void plNetClientComm::SetDefaultHandler( MsgHandler* handler) { + NetCommSetDefaultMsgHandler(MsgHandler::StaticMsgHandler, handler); +} + +// MsgHandler::StaticMsgHandler ---------------------------------------------- +int plNetClientComm::MsgHandler::StaticMsgHandler (plNetMessage * msg, void * userState) { + plNetClientComm::MsgHandler * handler = (plNetClientComm::MsgHandler *) userState; + return handler->HandleMessage(msg); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.h new file mode 100644 index 00000000..5f1ed53c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.h @@ -0,0 +1,376 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientComm.h +* +* This module is the translation layer between simple network types +* such as byte arrays, and higher-level Plasma-specific types such +* as the plFactory-managed types. +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETCLIENTCOMM_PLNETCLIENTCOMM_H +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETCLIENTCOMM_PLNETCLIENTCOMM_H + + +#include "hsTypes.h" +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" +#include "../plNetCommon/plNetServerSessionInfo.h" +#include "../plNetCommon/plNetCommonHelpers.h" +#include "../plMessage/plNetCommMsgs.h" + + +class plNetMessage; + + +/***************************************************************************** +* +* NetClientComm API +* +***/ + +struct NetCommPlayer { + unsigned playerInt; + wchar playerName[kMaxPlayerNameLength]; + char playerNameAnsi[kMaxPlayerNameLength]; + char avatarDatasetName[64]; + unsigned explorer; +}; + +struct NetCommAccount { + Uuid accountUuid; + wchar accountName[kMaxAccountNameLength]; + ShaDigest accountNamePassHash; + char accountNameAnsi[kMaxAccountNameLength]; + unsigned accountFlags; + unsigned billingType; +}; + +struct NetCommAge { + Uuid ageInstId; + unsigned ageVaultId; + char ageDatasetName[kMaxAgeNameLength]; + char spawnPtName[64]; +}; + +const NetCommAge * const NetCommGetAge (); +const NetCommAge * const NetCommGetStartupAge (); +const NetCommAccount * const NetCommGetAccount (); +const NetCommPlayer * const NetCommGetPlayer (); +const ARRAY(NetCommPlayer) & NetCommGetPlayerList (); +unsigned NetCommGetPlayerCount (); +bool NetCommIsLoginComplete (); +void NetCommSetReadIniAccountInfo (bool readFromIni); +void NetCommSetAccountUsernamePassword (wchar username[], const ShaDigest & namePassHash); +void NetCommSetAuthTokenAndOS (wchar authToken[], wchar os[]); +ENetError NetCommGetAuthResult (); + +bool NetCommNeedToLoadAvatar (); +void NetCommSetAvatarLoaded (bool loaded = true); +void NetCommChangeMyPassword (const wchar password[]); + +void NetCommStartup (); +void NetCommShutdown (); +void NetCommUpdate (); +void NetCommConnect (); +void NetCommDisconnect (); +void NetCommSendMsg ( + plNetMessage * msg +); +void NetCommRecvMsg ( + plNetMessage * msg +); +void NetCommEnableNet ( + bool enabled, + bool wait +); +void NetCommActivatePostInitErrorHandler(); + + +/***************************************************************************** +* +* Net message handlers +* +***/ + +// Return this value from your registered msg handler +// to stop further dispatching of incoming msg. +const unsigned kOK_MsgConsumed = hsOK + 1; + +typedef int (FNetCommMsgHandler)( + plNetMessage * msg, + void * userState +); + +// Adds a msg handler for a msg that is convertable to specified type. +void NetCommAddMsgHandlerForType ( + unsigned msgClassIdx, + FNetCommMsgHandler * handler, + void * userState +); +// Adds a msg handler for a specific msg type. +void NetCommAddMsgHandlerForExactType ( + unsigned msgClassIdx, + FNetCommMsgHandler * handler, + void * userState +); + +extern const unsigned kNetCommAllMsgClasses; +extern FNetCommMsgHandler * kNetCommAllMsgHandlers; +extern const void * kNetCommAllUserStates; + +void NetCommRemoveMsgHandler ( + unsigned msgClassIdx, + FNetCommMsgHandler * handler, + const void * userState +); + +void NetCommSetDefaultMsgHandler ( + FNetCommMsgHandler * handler, + void * userState +); +void NetCommSetMsgPreHandler ( + FNetCommMsgHandler * handler, + void * userState +); + +/***************************************************************************** +* +* Network requests +* Network replies are posted via plDispatch +* +***/ + +void NetCommAuthenticate ( // --> plNetCommAuthMsg + void * param +); +void NetCommGetFileList ( // --> plNetCommFileListMsg + const wchar dir[], + const wchar ext[], + void * param +); +void NetCommGetFile ( // --> plNetCommFileDownloadMsg + const wchar filename[], + hsStream * writer, + void * param +); +void NetCommLinkToAge ( // --> plNetCommLinkToAgeMsg + const NetCommAge & age, + void * param +); +void NetCommSetActivePlayer (//--> plNetCommActivePlayerMsg + unsigned desiredPlayerInt, + void * param +); +void NetCommCreatePlayer ( // --> plNetCommCreatePlayerMsg + const char playerName[], + const char avatarShape[], + const char friendInvite[], + unsigned createFlags, + void * param +); +void NetCommCreatePlayer ( // --> plNetCommCreatePlayerMsg + const wchar playerName[], + const wchar avatarShape[], + const wchar friendInvite[], + unsigned createFlags, + void * param +); +void NetCommDeletePlayer ( // --> plNetCommDeletePlayerMsg + unsigned playerInt, + void * param +); +void NetCommGetPublicAgeList (//-> plNetCommPublicAgeListMsg + const char ageName[], + void * param, + plNetCommReplyMsg::EParamType ptype = plNetCommReplyMsg::kParamTypeOther +); +void NetCommSetAgePublic ( // --> no msg + unsigned ageInfoId, + bool makePublic +); +void NetCommCreatePublicAge (// --> plNetCommPublicAgeMsg + const char ageName[], + const Uuid & ageInstId, + void * param +); +void NetCommRemovePublicAge(// --> plNetCommPublicAgeMsg + const Uuid & ageInstId, + void * param +); +void NetCommRegisterOwnedAge ( + const NetCommAge & age, + const char ageInstDesc[], + unsigned playerInt, + void * param +); +void NetCommUnregisterOwnedAge ( + const char ageName[], + unsigned playerInt, + void * param +); +void NetCommRegisterVisitAge ( + const NetCommAge & age, + const char ageInstDesc[], + unsigned playerInt, + void * param +); +void NetCommUnregisterVisitAge ( + const Uuid & ageInstId, + unsigned playerInt, + void * param +); +void NetCommConnectPlayerVault ( + void * param +); +void NetCommDisconnectPlayerVault (); +void NetCommConnectAgeVault ( + const Uuid & ageInstId, + void * param +); +void NetCommDisconnectAgeVault (); +void NetCommUpgradeVisitorToExplorer ( + unsigned playerInt, + void * param +); +void NetCommSetCCRLevel ( + unsigned ccrLevel +); +void NetCommSendFriendInvite ( + const wchar emailAddress[], + const wchar toName[], + const Uuid& inviteUuid +); + +#endif // PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETCLIENTCOMM_PLNETCLIENTCOMM_H + + + + +/***************************************************************************** +* +* Old code... +* +***/ +#ifndef plNetClientComm_h_inc +#define plNetClientComm_h_inc + + +//////////////////////////////////////////////////////////////////// + +class plCreatable; +class plStatusLog; +class plAgeLinkStruct; +class plNetClientCommTask; +class plNetMessage; +class plNetMsgTerminated; +class plPlayerMigrationPkg; + +//////////////////////////////////////////////////////////////////// +// plNetClientComm +// - Sends/Recvs messages with a server. +// - Performs common network tasks (eg. auth, ping, join, leave, +// create player, delete player, etc.). +// - Calls you back when network task is complete. +// - Fwds unsolicited messages to your supplied handler. +// - You can register a msg handler msg type family (i.e. msg +// type or base of type). +// - You can register a msg handler for an exact msg type. +// - Sends periodic alive msg to server when authenticated. +// - Tracks message receipts for message types you specify. +// - Checks for server silence. +// + +class plNetClientComm +{ +public: + //////////////////////////////////////////////////////////////// + // CALLBACK CLASSES + + // Callback object sent with calls to our API as optional arg. + class Callback + { + public: + // OPERATION-SPECIFIC RESULT ARGS. + //------------------------------- + // Auth: 0=guid of server that authenticated us, if success. + // Leave: None + // Alive: None + // Ping: 0=The plNetMsgPing rcvd. + // FindAge: 0=plNetServerSessionInfo for spawned game server. + // Join: 0=fireWalled bool, 1=joinOrder int, 2=experimentalLevel int, 3=initialAgeSDL rec + // GetPlayerList: 0=numPlayers int, 1=player1ID int, 2=player1Name string, 3=flags ... + // SetActivePlayer: None + // CreatePlayer: 0=newPlayerID int + // DeletePlayer: None + // GetPublicAgeList:0=plCreatableStream of plAgeInfoStruct, 1=plCreatableStream of nPlayers + // PublicAgeCreated:0=plAgeInfoStruct + plCreatableListHelper fCbArgs; + //------------------------------- + virtual ~Callback(){} + virtual void OperationStarted( UInt32 context ) = 0; + virtual void OperationComplete( UInt32 context, int resultCode ) = 0; + }; + class StubbedCallback : public Callback + { + public: + void OperationStarted( UInt32 context ) {} + void OperationComplete( UInt32 context, int resultCode ) {} + }; + + // Message handler for unsolicited msgs or registered for specific msg types. + class MsgHandler + { + public: + static int StaticMsgHandler(plNetMessage * msg, void * userState); + virtual int HandleMessage( plNetMessage* msg ) = 0; + }; + + //////////////////////////////////////////////////////////////// + + plNetClientComm(); + ~plNetClientComm(); + + //////////////////////////////////////////////////////////////// + + // Adds a msg handler for a msg that is convertable to specified type. + void AddMsgHandlerForType( UInt16 msgClassIdx, MsgHandler* handler ); + + // Adds a msg handler for a specific msg type. + void AddMsgHandlerForExactType( UInt16 msgClassIdx, MsgHandler* handler ); + + bool RemoveMsgHandler( MsgHandler* handler ); + + // Msgs not part of a task controlled by this + // object, and doesn't have a handler set for its type + // are sent to this handler (if set). + void SetDefaultHandler( MsgHandler* msgHandler ); +}; + +//////////////////////////////////////////////////////////////////// +#endif // plNetClientComm_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommCreatable.h new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommCreatable.h @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommTask.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommTask.cpp new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommTask.cpp @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommTask.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommTask.h new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientComm/plNetClientCommTask.h @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.cpp new file mode 100644 index 00000000..2835e57e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.cpp @@ -0,0 +1,160 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetClientRecorder.h" +#include "hsStream.h" +#include "hsTimer.h" + +#include "../plNetMessage/plNetMessage.h" +#include "plCreatableIndex.h" +#include "hsResMgr.h" +#include "plgDispatch.h" +#include "../plSDL/plSDL.h" +#include "../pnNetCommon/plNetApp.h" + +#include "../plMessage/plLinkToAgeMsg.h" +#include "../plMessage/plLoadAvatarMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" + +#include "../plStatusLog/plStatusLog.h" +#include "../plFile/hsFiles.h" + +plNetClientRecorder::plNetClientRecorder(TimeWrapper* timeWrapper) : +fTimeWrapper(timeWrapper) +{ +} + +plNetClientRecorder::~plNetClientRecorder() +{ +} + +double plNetClientRecorder::GetTime() +{ + if (fTimeWrapper) + return fTimeWrapper->GetWrappedTime(); + else + return hsTimer::GetSysSeconds(); +} + +void plNetClientRecorder::IMakeFilename(const char* recName, char* path) +{ + strcpy(path, "Recordings" PATH_SEPARATOR_STR); +#if HS_BUILD_FOR_WIN32 + CreateDirectory(path, NULL); +#endif + + char* lastDot = strrchr(recName, '.'); + if (lastDot) + strncat(path, recName, lastDot-recName); + else + strcat(path, recName); + + strcat(path, ".rec"); +} + +bool plNetClientRecorder::IsRecordableMsg(plNetMessage* msg) const +{ + UInt16 idx = msg->ClassIndex(); + + return ( + idx == CLASS_INDEX_SCOPED(plNetMsgLoadClone) || + idx == CLASS_INDEX_SCOPED(plNetMsgSDLStateBCast) || + idx == CLASS_INDEX_SCOPED(plNetMsgSDLState) || + idx == CLASS_INDEX_SCOPED(plNetMsgGameMessage) + ); +} + +plNetClientLoggingRecorder::plNetClientLoggingRecorder(TimeWrapper* timeWrapper) : + plNetClientRecorder(timeWrapper), + fPlaybackTimeOffset(0), + fNextPlaybackTime(0), + fLog(nil), + fBetweenAges(true) +{ +} + +plNetClientLoggingRecorder::~plNetClientLoggingRecorder() +{ + delete fLog; + fLog = nil; +} + +bool plNetClientLoggingRecorder::IProcessRecordMsg(plNetMessage* msg, double secs) +{ + if (msg->ClassIndex() == CLASS_INDEX_SCOPED(plNetMsgGameMessage)) + { + plNetMsgGameMessage* gameMsg = plNetMsgGameMessage::ConvertNoRef(msg); + UInt16 gameMsgIdx = gameMsg->StreamInfo()->GetStreamType(); + + if (gameMsgIdx == CLASS_INDEX_SCOPED(plServerReplyMsg)) + return false; + + // Throw out any notify messages that don't involve picking (running into + // detectors and that sort of thing should be recreated automatically + // during playback) + if (gameMsgIdx == CLASS_INDEX_SCOPED(plNotifyMsg)) + { + bool hasPick = false; + + plNotifyMsg* notifyMsg = plNotifyMsg::ConvertNoRef(gameMsg->GetContainedMsg()); + int numEvents = notifyMsg->GetEventCount(); + for (int i = 0; i < numEvents; i++) + { + proEventData* event = notifyMsg->GetEventRecord(i); + if (event->fEventType == proEventData::kPicked) + hasPick = true; + } + + hsRefCnt_SafeUnRef(notifyMsg); + + if (!hasPick) + return false; + } + } + + if (fPlaybackTimeOffset == 0) + fPlaybackTimeOffset = secs; + + return true; +} + +void plNetClientLoggingRecorder::RecordLinkMsg(plLinkToAgeMsg* linkMsg, double secs) +{ + if (!linkMsg->HasBCastFlag(plMessage::kNetSent)) + { + plNetMsgGameMessage netMsgWrap; + + // write message (and label) to ram stream + hsRAMStream stream; + hsgResMgr::ResMgr()->WriteCreatable(&stream, linkMsg); + + // put stream in net msg wrapper + netMsgWrap.StreamInfo()->CopyStream(&stream); + // netMsgWrap.StreamInfo()->SetStreamType(linkMsg->ClassIndex()); // type of game msg + + RecordMsg(&netMsgWrap, secs); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.h new file mode 100644 index 00000000..17debbb2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientRecorder.h @@ -0,0 +1,178 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetClientRecorder_h_inc +#define plNetClientRecorder_h_inc + +#include "hsTypes.h" + +class hsStream; +class plNetMessage; +class plStatusLog; +class plLinkToAgeMsg; +class plAgeLoadedMsg; +class hsResMgr; + +class plNetClientRecorder +{ +public: + class TimeWrapper + { + public: + virtual double GetWrappedTime() = 0; + }; +protected: + TimeWrapper *fTimeWrapper; + + // Puts the full path to recName in path + virtual void IMakeFilename(const char* recName, char* path); +public: + + plNetClientRecorder(TimeWrapper* timeWrapper); + virtual ~plNetClientRecorder(); + + virtual bool BeginRecording(const char* recName) = 0;; + virtual bool BeginPlayback(const char* recName) { hsAssert(false,"plNetClientRecording: Playback not supported"); return false; } + + // Recording functions + virtual bool IsRecordableMsg(plNetMessage* msg) const; + virtual void RecordMsg(plNetMessage* msg, double secs) = 0; + virtual void RecordLinkMsg(plLinkToAgeMsg* linkMsg, double secs) = 0; + virtual void RecordAgeLoadedMsg(plAgeLoadedMsg* ageLoadedMsg) = 0; + + // Playback functions + double GetTime(); + virtual bool IsQueueEmpty() { hsAssert(false,"plNetClientRecording: Playback not supported"); return true; } + virtual plNetMessage* GetNextMessage() { hsAssert(false,"plNetClientRecording: Playback not supported"); return nil; } + virtual double GetNextMessageTimeDelta() { hsAssert(false,"plNetClientRecording: Playback not supported"); return 0; } +}; + +class plNetClientLoggingRecorder : public plNetClientRecorder +{ +protected: + double fPlaybackTimeOffset; + double fNextPlaybackTime; + + plStatusLog* fLog; + + // We don't send messages when between ages + bool fBetweenAges; + + virtual void ILogMsg(plNetMessage* msg, const char* preText="") = 0; + + bool IProcessRecordMsg(plNetMessage* msg, double secs); +public: + plNetClientLoggingRecorder(TimeWrapper* timeWrapper); + ~plNetClientLoggingRecorder(); + + void RecordLinkMsg(plLinkToAgeMsg* linkMsg, double secs); + virtual void RecordAgeLoadedMsg(plAgeLoadedMsg* ageLoadedMsg) = 0; + +}; + +// +// Record the net client msgs to a file +// +class plNetClientStreamRecorder : public plNetClientLoggingRecorder +{ +protected: + hsStream* fRecordStream; + + hsResMgr* fResMgr; + + plNetMessage* IGetNextMessage(); + virtual bool IIsValidMsg(plNetMessage* msg); + + void ILogMsg(plNetMessage* msg, const char* preText=""); + +public: + plNetClientStreamRecorder(TimeWrapper* timeWrapper = nil); + ~plNetClientStreamRecorder(); + + bool BeginRecording(const char* recName); + bool BeginPlayback(const char* recName); + + // Recording functions + void RecordMsg(plNetMessage* msg, double secs); + void RecordAgeLoadedMsg(plAgeLoadedMsg* ageLoadedMsg); + + // Playback functions + void SetResMgr(hsResMgr* resmgr) { fResMgr = resmgr; } + hsResMgr* GetResMgr(); + bool IsQueueEmpty(); + plNetMessage* GetNextMessage(); + double GetNextMessageTimeDelta(); +}; + +class plNetClientStatsRecorder : public plNetClientLoggingRecorder +{ +protected: + void ILogMsg(plNetMessage* msg, const char* preText=""); + +public: + plNetClientStatsRecorder(TimeWrapper* timeWrapper = nil); + ~plNetClientStatsRecorder(); + + bool BeginRecording(const char* recName); + + // Recording functions + void RecordMsg(plNetMessage* msg, double secs); + void RecordAgeLoadedMsg(plAgeLoadedMsg* ageLoadedMsg) { }; +}; + +class plNetClientStreamAndStatsRecorder : public plNetClientRecorder +{ +protected: + plNetClientStreamRecorder* fStreamRecorder; + plNetClientStatsRecorder* fStatsRecorder; + +public: + plNetClientStreamAndStatsRecorder(plNetClientStreamRecorder* streamrec, plNetClientStatsRecorder* statrec) : + plNetClientRecorder(nil),fStreamRecorder(streamrec), fStatsRecorder(statrec) {} + ~plNetClientStreamAndStatsRecorder() { delete fStreamRecorder; delete fStatsRecorder; } + + bool BeginRecording(const char* recName) { return fStreamRecorder->BeginRecording(recName) && fStatsRecorder->BeginRecording(recName); } + bool BeginPlayback(const char* recName) { return fStreamRecorder->BeginPlayback(recName); } + + // Recording functions + bool IsRecordableMsg(plNetMessage* msg) const { return fStreamRecorder->IsRecordableMsg(msg) || fStatsRecorder->IsRecordableMsg(msg); } + void RecordMsg(plNetMessage* msg, double secs) { fStreamRecorder->RecordMsg(msg,secs); fStatsRecorder->RecordMsg(msg,secs); } + void RecordLinkMsg(plLinkToAgeMsg* linkMsg, double secs) { fStreamRecorder->RecordLinkMsg(linkMsg,secs); fStatsRecorder->RecordLinkMsg(linkMsg,secs); } + void RecordAgeLoadedMsg(plAgeLoadedMsg* ageLoadedMsg) { fStreamRecorder->RecordAgeLoadedMsg(ageLoadedMsg); fStatsRecorder->RecordAgeLoadedMsg(ageLoadedMsg); } + + // Playback functions + bool IsQueueEmpty() { return fStreamRecorder->IsQueueEmpty(); } + plNetMessage* GetNextMessage() { return fStreamRecorder->GetNextMessage(); } + double GetNextMessageTimeDelta() { return fStreamRecorder->GetNextMessageTimeDelta(); } +}; + +class plNetClientStressStreamRecorder : public plNetClientStreamRecorder +{ +public: + plNetClientStressStreamRecorder(TimeWrapper* timeWrapper = nil) : plNetClientStreamRecorder(timeWrapper) {} + bool IsRecordableMsg(plNetMessage* msg) const; +}; + +#endif // plNetClientRecorder_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientStatsRecorder.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientStatsRecorder.cpp new file mode 100644 index 00000000..ad0f09af --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientStatsRecorder.cpp @@ -0,0 +1,144 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetClientRecorder.h" +#include "hsStream.h" +#include "../plNetMessage/plNetMessage.h" +#include "plCreatableIndex.h" +#include "plgDispatch.h" +#include "../plSDL/plSDL.h" +#include "../pnNetCommon/plNetApp.h" + +#include "../plMessage/plLinkToAgeMsg.h" +#include "../plMessage/plLoadAvatarMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" + +#include "../plStatusLog/plStatusLog.h" + + +plNetClientStatsRecorder::plNetClientStatsRecorder(TimeWrapper* timeWrapper) : +plNetClientLoggingRecorder(timeWrapper) +{ + if (fLog) + delete fLog; + fLog = plStatusLogMgr::GetInstance().CreateStatusLog(30, "StatsRecorder.log", plStatusLog::kAlignToTop); +} + +plNetClientStatsRecorder::~plNetClientStatsRecorder() +{ +} + +bool plNetClientStatsRecorder::BeginRecording(const char* recName) +{ + return true; +} + +void plNetClientStatsRecorder::RecordMsg(plNetMessage* msg, double secs) +{ + if (IProcessRecordMsg(msg,secs)) + { + char stats[256]; + sprintf(stats,"tm:%4.2f;sz:%u,plrid:%s%u : ",secs,msg->GetPackSize(),msg->GetHasPlayerID()?"":"XX",msg->GetHasPlayerID()?msg->GetPlayerID():0); + // GetPackSize might compress the buffer on us, so uncompress it + plNetMsgStreamedObject* so = plNetMsgStreamedObject::ConvertNoRef(msg); + if (so) + so->StreamInfo()->Uncompress(); + ILogMsg(msg,stats); + } +} + + +void plNetClientStatsRecorder::ILogMsg(plNetMessage* msg, const char* preText) +{ + if (msg->ClassIndex() == CLASS_INDEX_SCOPED(plNetMsgGameMessage)) + { + plNetMsgGameMessage* gameMsg = plNetMsgGameMessage::ConvertNoRef(msg); + fLog->AddLineF("%s%s(%s)", preText, msg->ClassName(), plFactory::GetNameOfClass(gameMsg->StreamInfo()->GetStreamType())); + + if (gameMsg->StreamInfo()->GetStreamType() == CLASS_INDEX_SCOPED(plNotifyMsg)) + { + plNotifyMsg* notifyMsg = plNotifyMsg::ConvertNoRef(gameMsg->GetContainedMsg()); + int numEvents = notifyMsg->GetEventCount(); + for (int i = 0; i < numEvents; i++) + { + const char* eventName = ""; + + proEventData* event = notifyMsg->GetEventRecord(i); + switch (event->fEventType) + { + case proEventData::kCollision: eventName = "Collision"; break; + case proEventData::kPicked: eventName = "Picked"; break; + case proEventData::kControlKey: eventName = "ControlKey"; break; + case proEventData::kVariable: eventName = "Variable"; break; + case proEventData::kFacing: eventName = "Facing"; break; + case proEventData::kContained: eventName = "Contained"; break; + case proEventData::kActivate: eventName = "Activate"; break; + case proEventData::kCallback: eventName = "Callback"; break; + case proEventData::kResponderState: eventName = "ResponderState"; break; + case proEventData::kMultiStage: eventName = "MultiStage"; break; + case proEventData::kSpawned: eventName = "Spawned"; break; + case proEventData::kClickDrag: eventName = "ClickDrag"; break; + } + + fLog->AddLineF("\t%s", eventName); + } + + hsRefCnt_SafeUnRef(notifyMsg); + } + } + else if (plNetMsgSDLState* sdlMsg = plNetMsgSDLState::ConvertNoRef(msg)) + { + hsReadOnlyStream stream(sdlMsg->StreamInfo()->GetStreamLen(), sdlMsg->StreamInfo()->GetStreamBuf()); + char* descName=nil; + int ver; + if (plStateDataRecord::ReadStreamHeader(&stream, &descName, &ver)) + { + fLog->AddLineF("%s%s(%s)", preText, msg->ClassName(), descName); + + int i; + + plStateDataRecord sdRec(descName, ver); + sdRec.Read(&stream, 0); + plStateDataRecord::SimpleVarsList vars; + sdRec.GetDirtyVars(&vars); + for (i = 0; i < vars.size(); i++) + { + fLog->AddLineF("\t%s", vars[i]->GetVarDescriptor()->GetName()); + } + + plStateDataRecord::SDVarsList sdVars; + sdRec.GetDirtySDVars(&sdVars); + for (i = 0; i < sdVars.size(); i++) + { + fLog->AddLineF("\t%s", sdVars[i]->GetSDVarDescriptor()->GetName()); + } + } + delete [] descName; + } + else + fLog->AddLineF("%s%s", preText, msg->ClassName()); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientStreamRecorder.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientStreamRecorder.cpp new file mode 100644 index 00000000..be4213da --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetClientRecorder/plNetClientStreamRecorder.cpp @@ -0,0 +1,362 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetClientRecorder.h" +#include "hsStream.h" +#include "../plNetMessage/plNetMessage.h" +#include "plCreatableIndex.h" +#include "hsResMgr.h" +#include "plgDispatch.h" +#include "../plSDL/plSDL.h" +#include "../pnNetCommon/plNetApp.h" + +#include "../plMessage/plLinkToAgeMsg.h" +#include "../plMessage/plLoadAvatarMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" + +#include "../plStatusLog/plStatusLog.h" + +plNetClientStreamRecorder::plNetClientStreamRecorder(TimeWrapper* timeWrapper) : + plNetClientLoggingRecorder(timeWrapper), + fRecordStream(nil), + fResMgr(nil) +{ + if (fLog) + delete fLog; + fLog = plStatusLogMgr::GetInstance().CreateStatusLog(30, "StreamRecorder.log", plStatusLog::kAlignToTop); +} + + +plNetClientStreamRecorder::~plNetClientStreamRecorder() +{ + if (fRecordStream) + { + fRecordStream->Close(); + delete fRecordStream; + } +} + +hsResMgr* plNetClientStreamRecorder::GetResMgr() +{ + if (fResMgr) + return fResMgr; + else + return hsgResMgr::ResMgr(); +} + +double plNetClientStreamRecorder::GetNextMessageTimeDelta() +{ + return fNextPlaybackTime - (GetTime() - fPlaybackTimeOffset); +} + +enum NetClientRecFlags +{ + kNetClientRecSDLDesc, +}; + +bool plNetClientStreamRecorder::BeginRecording(const char* recName) +{ + if (!fRecordStream) + { + fRecordStream = TRACKED_NEW hsUNIXStream; + char path[256]; + IMakeFilename(recName, path); + + if (!fRecordStream->Open(path, "wb")) + { + delete fRecordStream; + return false; + } + + hsBitVector contentFlags; + contentFlags.SetBit(kNetClientRecSDLDesc); + contentFlags.Write(fRecordStream); + + // kNetClientRecSDLDesc + plSDLMgr::GetInstance()->Write(fRecordStream); + + return true; + } + + return false; +} + +bool plNetClientStreamRecorder::BeginPlayback(const char* recName) +{ + if (!fRecordStream) + { + fRecordStream = TRACKED_NEW hsUNIXStream; + char path[256]; + IMakeFilename(recName, path); + + if (fRecordStream->Open(path, "rb")) + { + hsBitVector contentFlags; + contentFlags.Read(fRecordStream); + + if (contentFlags.IsBitSet(kNetClientRecSDLDesc)) + plSDLMgr::GetInstance()->Read(fRecordStream); + + fPlaybackTimeOffset = GetTime(); + fNextPlaybackTime = fRecordStream->ReadSwapDouble(); + fBetweenAges = false; + } + else + { + delete fRecordStream; + fRecordStream = nil; + return false; + } + + return true; + } + + return false; +} + +void plNetClientStreamRecorder::RecordMsg(plNetMessage* msg, double secs) +{ + if (!fRecordStream) + return; + + if (IProcessRecordMsg(msg,secs)) + { + fRecordStream->WriteSwapDouble(secs - fPlaybackTimeOffset); + GetResMgr()->WriteCreatableVersion(fRecordStream, msg); + + ILogMsg(msg); + } +} + +void plNetClientStreamRecorder::RecordAgeLoadedMsg(plAgeLoadedMsg* ageLoadedMsg) +{ + fLog->AddLineF("Age %s", ageLoadedMsg->fLoaded ? "Loaded" : "Unloaded"); + + if (ageLoadedMsg->fLoaded) + { + fBetweenAges = false; + fPlaybackTimeOffset = GetTime(); + } + else + { + fBetweenAges = true; + } +} + +bool plNetClientStreamRecorder::IsQueueEmpty() +{ + return (fRecordStream == nil); +} + + +plNetMessage* plNetClientStreamRecorder::GetNextMessage() +{ + plNetMessage* msg = nil; + while (!fBetweenAges && (msg = IGetNextMessage())) + { + if (IIsValidMsg(msg)) + return msg; + else + hsRefCnt_SafeUnRef(msg); + } + + return nil; +} + +plNetMessage* plNetClientStreamRecorder::IGetNextMessage() +{ + plNetMessage* msg = nil; + + if (!IsQueueEmpty() && GetNextMessageTimeDelta() <= 0 ) + { + msg = plNetMessage::ConvertNoRef(GetResMgr()->ReadCreatableVersion(fRecordStream)); + // msg->SetPeeked(true); + + // Fix the flags on game messages, so we won't get an assert later + plNetMsgGameMessage* gameMsg = plNetMsgGameMessage::ConvertNoRef(msg); + if (gameMsg) + { + plMessage* plMsg = gameMsg->GetContainedMsg(GetResMgr()); + plNetClientApp::GetInstance()->UnInheritNetMsgFlags(plMsg); + + // write message (and label) to ram stream + hsRAMStream stream; + GetResMgr()->WriteCreatable(&stream, plMsg); + + // put stream in net msg wrapper + gameMsg->StreamInfo()->CopyStream(&stream); + // gameMsg->StreamInfo()->SetStreamType(plMsg->ClassIndex()); // type of game msg + } + + double nextPlaybackTime = fRecordStream->ReadSwapDouble(); + if (nextPlaybackTime < fNextPlaybackTime) + fBetweenAges = true; + + fNextPlaybackTime = nextPlaybackTime; + + // If this was the last message, stop playing back + if (fRecordStream->AtEnd()) + { + fRecordStream->Close(); + delete fRecordStream; + fRecordStream = nil; + } + } + + return msg; +} + +bool plNetClientStreamRecorder::IIsValidMsg(plNetMessage* msg) +{ + if (plNetMsgGameMessage* gameMsg = plNetMsgGameMessage::ConvertNoRef(msg)) + { + Int16 type = gameMsg->StreamInfo()->GetStreamType(); + + // + // These messages will be regenerated if they are for the local avatar, + // so don't dispatch them in that case. + // + + if (type == CLASS_INDEX_SCOPED(plLinkEffectsTriggerMsg)) + { + plLinkEffectsTriggerMsg* linkMsg = plLinkEffectsTriggerMsg::ConvertNoRef(gameMsg->GetContainedMsg(GetResMgr())); + if (plNetClientApp::GetInstance()) + { + bool isLocal = (linkMsg->GetLinkKey() == plNetClientApp::GetInstance()->GetLocalPlayerKey()); + hsRefCnt_SafeUnRef(linkMsg); + + if (isLocal) + { + ILogMsg(msg, "IGNORING "); + return false; + } + } + } + else if (type == CLASS_INDEX_SCOPED(plLoadAvatarMsg)) + { + plLoadAvatarMsg* loadAvMsg = plLoadAvatarMsg::ConvertNoRef(gameMsg->GetContainedMsg(GetResMgr())); + if (plNetClientApp::GetInstance()) + { + bool isLocal = (loadAvMsg->GetCloneKey() == plNetClientApp::GetInstance()->GetLocalPlayerKey()); + hsRefCnt_SafeUnRef(loadAvMsg); + + if (isLocal) + { + ILogMsg(msg, "IGNORING "); + return false; + } + } + } + } + + ILogMsg(msg); + return true; +} + +void plNetClientStreamRecorder::ILogMsg(plNetMessage* msg, const char* preText) +{ + if (msg->ClassIndex() == CLASS_INDEX_SCOPED(plNetMsgGameMessage)) + { + plNetMsgGameMessage* gameMsg = plNetMsgGameMessage::ConvertNoRef(msg); + fLog->AddLineF("%s%s(%s)", preText, msg->ClassName(), plFactory::GetNameOfClass(gameMsg->StreamInfo()->GetStreamType())); + + if (gameMsg->StreamInfo()->GetStreamType() == CLASS_INDEX_SCOPED(plNotifyMsg)) + { + plNotifyMsg* notifyMsg = plNotifyMsg::ConvertNoRef(gameMsg->GetContainedMsg(GetResMgr())); + int numEvents = notifyMsg->GetEventCount(); + for (int i = 0; i < numEvents; i++) + { + const char* eventName = ""; + + proEventData* event = notifyMsg->GetEventRecord(i); + switch (event->fEventType) + { + case proEventData::kCollision: eventName = "Collision"; break; + case proEventData::kPicked: eventName = "Picked"; break; + case proEventData::kControlKey: eventName = "ControlKey"; break; + case proEventData::kVariable: eventName = "Variable"; break; + case proEventData::kFacing: eventName = "Facing"; break; + case proEventData::kContained: eventName = "Contained"; break; + case proEventData::kActivate: eventName = "Activate"; break; + case proEventData::kCallback: eventName = "Callback"; break; + case proEventData::kResponderState: eventName = "ResponderState"; break; + case proEventData::kMultiStage: eventName = "MultiStage"; break; + case proEventData::kSpawned: eventName = "Spawned"; break; + case proEventData::kClickDrag: eventName = "ClickDrag"; break; + } + + fLog->AddLineF("\t%s", eventName); + } + + hsRefCnt_SafeUnRef(notifyMsg); + } + } + else if (plNetMsgSDLState* sdlMsg = plNetMsgSDLState::ConvertNoRef(msg)) + { + hsReadOnlyStream stream(sdlMsg->StreamInfo()->GetStreamLen(), sdlMsg->StreamInfo()->GetStreamBuf()); + char* descName=nil; + int ver; + if (plStateDataRecord::ReadStreamHeader(&stream, &descName, &ver)) + { + fLog->AddLineF("%s%s(%s)", preText, msg->ClassName(), descName); + + int i; + + plStateDataRecord sdRec(descName, ver); + sdRec.Read(&stream, 0); + plStateDataRecord::SimpleVarsList vars; + sdRec.GetDirtyVars(&vars); + for (i = 0; i < vars.size(); i++) + { + fLog->AddLineF("\t%s", vars[i]->GetVarDescriptor()->GetName()); + } + + plStateDataRecord::SDVarsList sdVars; + sdRec.GetDirtySDVars(&sdVars); + for (i = 0; i < sdVars.size(); i++) + { + fLog->AddLineF("\t%s", sdVars[i]->GetSDVarDescriptor()->GetName()); + } + } + delete [] descName; + } + else + fLog->AddLineF("%s%s", preText, msg->ClassName()); +} + + +bool plNetClientStressStreamRecorder::IsRecordableMsg(plNetMessage* msg) const +{ + UInt16 idx = msg->ClassIndex(); + + return ( + plNetClientStreamRecorder::IsRecordableMsg(msg) + || idx == CLASS_INDEX_SCOPED(plNetMsgTestAndSet) + || idx == CLASS_INDEX_SCOPED(plNetMsgGameMessageDirected) + || idx == CLASS_INDEX_SCOPED(plNetMsgVoice) + ); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plClientGuid.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plClientGuid.cpp new file mode 100644 index 00000000..82d3066c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plClientGuid.cpp @@ -0,0 +1,424 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plClientGuid.h" +#include "hsStream.h" +#include +#include "plNetCommon.h" +#include "../pnMessage/plMessage.h" +#include "../plSockets/plNet.h" + +plClientGuid::plClientGuid() +:fPlayerID(0) +,fCCRLevel(0) +,fFlags(0) +,fProtectedLogin(false) +,fBuildType(plNetCommon::BuildType::kUnknown) +,fSrcAddr(0) +,fSrcPort(0) +,fReserved(false) +{ + fAccountUUID.Clear(); +} + +void plClientGuid::SetAccountUUID(const plUUID * v ) +{ + fAccountUUID.CopyFrom( v ); + if ( !fAccountUUID.IsNull() ) + fFlags|=kAccountUUID; + else + fFlags&=~kAccountUUID; +} + +void plClientGuid::SetAccountUUID(const plUUID & v ) +{ + SetAccountUUID( &v ); +} + +void plClientGuid::SetBuildType(UInt8 type) +{ + fBuildType=type; + fFlags|=kBuildType; +} + +void plClientGuid::SetPlayerID(UInt32 id) +{ + fPlayerID=id; + if ( fPlayerID ) + { + fFlags|=kPlayerID; + fFlags&=~kTempPlayerID; + } + else + fFlags&=~kPlayerID; +} + +void plClientGuid::SetTempPlayerID(UInt32 id) +{ + fPlayerID=id; + if ( fPlayerID ) + { + fFlags&=~kPlayerID; + fFlags|=kTempPlayerID; + } + else + { + fFlags&=~kTempPlayerID; + } +} + +void plClientGuid::SetPlayerName( const char * v ) +{ + fPlayerName = v?v:""; + if ( fPlayerName.size() ) + fFlags|=kPlayerName; + else + fFlags&=~kPlayerName; +} + +void plClientGuid::SetCCRLevel(UInt8 v) +{ + fCCRLevel=v; + fFlags|=kCCRLevel; +} + +void plClientGuid::SetProtectedLogin(bool b) +{ + fProtectedLogin=b; + fFlags |= kProtectedLogin; +} + +void plClientGuid::SetSrcAddr( UInt32 v ) +{ + fSrcAddr = v; + if ( fSrcAddr ) + fFlags|=kSrcAddr; + else + fFlags&=~kSrcAddr; +} + +void plClientGuid::SetSrcAddrFromStr( const char * s ) +{ + hsAssert(false, "eric, port me"); +} + +void plClientGuid::SetSrcPort( UInt16 v ) +{ + fSrcPort = v; + if ( fSrcPort ) + fFlags|=kSrcPort; + else + fFlags&=~kSrcPort; +} + +void plClientGuid::SetReserved(bool b) +{ + fReserved=b; + fFlags |= kReserved; +} + +void plClientGuid::SetClientKey(const std::string& key) +{ + fClientKey = key; + if ( fClientKey.size() ) + fFlags|=kClientKey; + else + fFlags&=~kClientKey; +} + +const char * plClientGuid::GetSrcAddrStr() const +{ + hsAssert(false, "eric, port me"); + static const char foo[] = ""; + return foo; +} + +std::string plClientGuid::AsStdString() const +{ +#define kComma "," +#define kEmpty "" + const char * spacer = kEmpty; + + std::stringstream ss; + + ss << "["; + + if (IsFlagSet(kPlayerID)) + { + ss << spacer << "Pid:" << fPlayerID; + spacer = kComma; + } + else if (IsFlagSet(kTempPlayerID)) + { + ss << spacer << "tPd:" << fPlayerID; + spacer = kComma; + } + if (IsFlagSet(kPlayerName)) + { + ss << spacer << "Plr:" << fPlayerName; + } + if (IsFlagSet(kCCRLevel)) + { + ss << spacer << "CCR:" << (int)fCCRLevel; + spacer = kComma; + } + if (IsFlagSet(kProtectedLogin)) + { + ss << spacer << "Pro:" << (int)fProtectedLogin; + spacer = kComma; + } + if (IsFlagSet(kBuildType)) + { + ss << spacer << "Bld:" << plNetCommon::BuildType::BuildTypeStr(fBuildType); + spacer = kComma; + } + if ( IsFlagSet(kSrcAddr) ) + { + ss << spacer << "Addr:" << GetSrcAddrStr(); + spacer = kComma; + } + if ( IsFlagSet(kSrcPort) ) + { + ss << spacer << "Port:" << (int)fSrcPort; + spacer = kComma; + } + if (IsFlagSet(kAccountUUID)) + { + ss << spacer << "plUUID:" << fAccountUUID.AsStdString(); + spacer = kComma; + } + if ( IsFlagSet(kReserved)) + { + ss << spacer << "Res:" << (int)fReserved; + spacer = kComma; + } + if (IsFlagSet(kClientKey)) + { + ss << spacer << "ClientKey:" << fClientKey; + spacer = kComma; + } + ss << "]"; + + return ss.str().c_str(); +} + +std::string plClientGuid::AsLogString() const +{ +#define kSemicolon ";" + const char* spacer = kSemicolon; + + std::stringstream ss; + + if (IsFlagSet(kAccountUUID)) + { + ss << "AcctUUID=" << fAccountUUID.AsStdString(); + ss << spacer; + } + if (IsFlagSet(kPlayerID)) + { + ss << "PlayerID=" << fPlayerID; + ss << spacer; + } +// else if (IsFlagSet(kTempPlayerID)) +// { +// ss << "tempPlayerID:" << fPlayerID; +// ss << spacer; +// } + if ( IsFlagSet(kSrcAddr) ) + { + ss << "SrcAddr=" << GetSrcAddrStr(); + ss << spacer; + } + if ( IsFlagSet(kSrcPort) ) + { + ss << "SrcPort=" << (int)fSrcPort; + ss << spacer; + } + if (IsFlagSet(kCCRLevel)) + { + ss << "CCRLevel=" << (int)fCCRLevel; + ss << spacer; + } + if (IsFlagSet(kProtectedLogin)) + { + ss << "Protected=" << (int)fProtectedLogin; + ss << spacer; + } + if (IsFlagSet(kBuildType)) + { + ss << "Build=" << plNetCommon::BuildType::BuildTypeStr(fBuildType); + ss << spacer; + } + if (IsFlagSet(kReserved)) + { + ss << "Reserved=" << (int)fReserved; + ss << spacer; + } + if (IsFlagSet(kClientKey)) + { + ss << "ClientKey=" << fClientKey; + ss << spacer; + } + + return ss.str().c_str(); +} + +void plClientGuid::Read(hsStream * s, hsResMgr* mgr) +{ + s->LogSubStreamStart("push me"); + s->LogReadSwap(&fFlags,"Flags"); + if (IsFlagSet(kAccountUUID)) + { + s->LogSubStreamPushDesc("AcctUUID"); + fAccountUUID.Read( s ); + } + if (IsFlagSet(kPlayerID)) + s->LogReadSwap(&fPlayerID,"PlayerID"); + else if (IsFlagSet(kTempPlayerID)) + s->LogReadSwap(&fPlayerID,"TempPlayerID"); + if (IsFlagSet(kPlayerName)) + { + s->LogSubStreamPushDesc("PlayerName"); + plMsgStdStringHelper::Peek( fPlayerName, s ); + } + if (IsFlagSet(kCCRLevel)) + s->LogReadSwap(&fCCRLevel,"CCRLevel"); + if (IsFlagSet(kProtectedLogin)) + s->LogReadSwap(&fProtectedLogin,"ProtectedLogin"); + if (IsFlagSet(kBuildType)) + s->LogReadSwap(&fBuildType,"BuildType"); + if (IsFlagSet(kSrcAddr)) + s->LogReadSwap(&fSrcAddr,"SrcAddr"); + if (IsFlagSet(kSrcPort)) + s->LogReadSwap(&fSrcPort,"SrcPort"); + if (IsFlagSet(kReserved)) + s->LogReadSwap(&fReserved,"Reserved"); + if (IsFlagSet(kClientKey)) + { + s->LogSubStreamPushDesc("ClientKey"); + plMsgStdStringHelper::Peek( fClientKey, s ); + } + s->LogSubStreamEnd(); +} + +void plClientGuid::Write(hsStream * s, hsResMgr* mgr) +{ + s->WriteSwap(fFlags); + if (IsFlagSet(kAccountUUID)) + fAccountUUID.Write( s ); + if (IsFlagSet(kPlayerID)) + s->WriteSwap(fPlayerID); + else if (IsFlagSet(kTempPlayerID)) + s->WriteSwap(fPlayerID); + if (IsFlagSet(kPlayerName)) + plMsgStdStringHelper::Poke( fPlayerName, s ); + if (IsFlagSet(kCCRLevel)) + s->WriteSwap(fCCRLevel); + if (IsFlagSet(kProtectedLogin)) + s->WriteSwap(fProtectedLogin); + if (IsFlagSet(kBuildType)) + s->WriteSwap(fBuildType); + if (IsFlagSet(kSrcAddr)) + s->WriteSwap(fSrcAddr); + if (IsFlagSet(kSrcPort)) + s->WriteSwap(fSrcPort); + if (IsFlagSet(kReserved)) + s->WriteSwap(fReserved); + if (IsFlagSet(kClientKey)) + plMsgStdStringHelper::Poke( fClientKey, s ); +} + +void plClientGuid::CopyFrom(const plClientGuid * other) +{ + fFlags = other->fFlags; + fAccountUUID.CopyFrom( &other->fAccountUUID ); + fPlayerID = other->fPlayerID; + fPlayerName = other->fPlayerName; + fCCRLevel = other->fCCRLevel; + fProtectedLogin = other->fProtectedLogin; + fBuildType = other->fBuildType; + fSrcAddr = other->fSrcAddr; + fSrcPort = other->fSrcPort; + fReserved = other->fReserved; + fClientKey = other->fClientKey; +} + +void plClientGuid::UpdateFrom(const plClientGuid * other) +{ + if ( !HasAccountUUID() && other->HasAccountUUID() ) + SetAccountUUID( other->GetAccountUUID() ); + if ( !HasPlayerID() && other->HasPlayerID() ) + SetPlayerID( other->GetPlayerID() ); + if ( !HasPlayerName() && other->HasPlayerName() ) + SetPlayerName( other->GetPlayerName() ); + if ( !HasProtectedLogin() && other->HasProtectedLogin() ) + SetProtectedLogin( other->GetProtectedLogin() ); + if ( !HasCCRLevel() && other->HasCCRLevel() ) + SetCCRLevel( other->GetCCRLevel() ); + if ( !HasBuildType() && other->HasBuildType() ) + SetBuildType( other->GetBuildType() ); + if ( !HasSrcAddr() && other->HasSrcAddr() ) + SetSrcAddr( other->GetSrcAddr() ); + if ( !HasSrcPort() && other->HasSrcPort() ) + SetSrcPort( other->GetSrcPort() ); + if ( !HasReservedBit() && other->HasReservedBit() ) + SetReserved( other->IsReserved() ); + if ( !HasClientKey() && other->HasClientKey() ) + SetClientKey( other->GetClientKey() ); +} + +void plClientGuid::Clear() +{ + plClientGuid tmp; + CopyFrom( &tmp ); +} + +bool plClientGuid::IsEqualTo(const plClientGuid * other) const +{ + return + fFlags == other->fFlags && + fAccountUUID.IsEqualTo( &other->fAccountUUID ) && + fPlayerID == other->fPlayerID && + fPlayerName == other->fPlayerName && + fCCRLevel == other->fCCRLevel && + fProtectedLogin == other->fProtectedLogin && + fBuildType == other->fBuildType && + fReserved == other->fReserved && + fClientKey == other->fClientKey; +} + +bool operator==(const plClientGuid & X, const plClientGuid & Y) +{ + return ( X.fAccountUUID.IsEqualTo( &Y.fAccountUUID )&&X.fPlayerID==Y.fPlayerID&&X.fFlags==Y.fFlags); +} +bool operator!=(const plClientGuid & X, const plClientGuid & Y) +{ + return ( !X.fAccountUUID.IsEqualTo( &Y.fAccountUUID )||X.fPlayerID!=Y.fPlayerID||X.fFlags!=Y.fFlags); +} +bool operator<(const plClientGuid & X, const plClientGuid & Y) +{ + return ( X.fAccountUUID.CompareTo( &Y.fAccountUUID )<0||X.fPlayerID. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plClientGuid_h_inc +#define plClientGuid_h_inc + +#include "hsConfig.h" +#include "hsStlUtils.h" +#include "../pnFactory/plCreatable.h" +#include "../plUUID/plUUID.h" + +class plClientGuid : public plCreatable +{ + UInt16 fFlags; + plUUID fAccountUUID; + UInt32 fPlayerID; + UInt8 fCCRLevel; + bool fProtectedLogin; + UInt8 fBuildType; // see plNetCommon.h + std::string fPlayerName; + UInt32 fSrcAddr; + UInt16 fSrcPort; + bool fReserved; + std::string fClientKey; + +public: + enum Flags // 16 bits. + { + kAccountUUID = 1<<0, + kPlayerID = 1<<1, + kTempPlayerID = 1<<2, + kCCRLevel = 1<<3, + kProtectedLogin = 1<<4, + kBuildType = 1<<5, + kPlayerName = 1<<6, + kSrcAddr = 1<<7, + kSrcPort = 1<<8, + kReserved = 1<<9, + kClientKey = 1<<10, + }; + plClientGuid(); + CLASSNAME_REGISTER( plClientGuid ); + GETINTERFACE_ANY( plClientGuid, plCreatable ); + + std::string AsStdString() const; + std::string AsLogString() const; + void Clear(); + void CopyFrom(const plClientGuid * other); + void UpdateFrom(const plClientGuid * other); + bool IsEqualTo(const plClientGuid * other) const; + bool IsFlagSet( UInt16 flag ) const { return (fFlags&flag)!=0; } + bool IsFullyQualified() const { return HasAccountUUID()&&HasPlayerID();} + + void Read(hsStream * s, hsResMgr* =nil); + void Write(hsStream * s, hsResMgr* =nil); + + bool HasAccountUUID() const { return (fFlags&kAccountUUID&&!fAccountUUID.IsNull())?true:false;} + bool HasPlayerID() const { return (fFlags&kPlayerID&&fPlayerID>0)?true:false;} + bool HasPlayerName() const { return (fFlags&kPlayerName&&fPlayerName.size())?true:false; } + bool HasCCRLevel() const { return (fFlags&kCCRLevel)?true:false;} + bool HasProtectedLogin() const { return (fFlags&kProtectedLogin)?true:false;} + bool HasBuildType() const { return (fFlags&kBuildType)?true:false;} + bool HasSrcAddr() const { return (fFlags&kSrcAddr)!=0;} + bool HasSrcPort() const { return (fFlags&kSrcPort)!=0;} + bool HasReservedBit() const { return (fFlags&kReserved)!=0;} + bool HasClientKey() const { return (fFlags&kClientKey)!=0;} + + const plUUID * GetAccountUUID() const { return &fAccountUUID;} + UInt32 GetPlayerID() const { return fPlayerID;} + const char * GetPlayerName() const { return fPlayerName.c_str(); } + UInt8 GetCCRLevel() const { return fCCRLevel; } + bool GetProtectedLogin() const { return ( fProtectedLogin!=0 ); } + UInt8 GetFlags() const { return (UInt8)fFlags;} + UInt8 GetBuildType() const { return fBuildType;} + UInt32 GetSrcAddr() const { return fSrcAddr; } + const char * GetSrcAddrStr() const; + UInt16 GetSrcPort() const { return fSrcPort; } + bool IsReserved() const { return fReserved!=0; } + const std::string& GetClientKey() const { return fClientKey; } + + void SetAccountUUID(const plUUID * v); + void SetAccountUUID(const plUUID & v); + void SetPlayerID(UInt32 v); + void SetPlayerName( const char * v ); + void SetCCRLevel(UInt8 v); + void SetProtectedLogin(bool v); + void SetBuildType(UInt8 v); + void SetSrcAddr( UInt32 v ); + void SetSrcAddrFromStr( const char * s ); + void SetSrcPort( UInt16 v ); + void SetReserved( bool v ); + void SetClientKey( const std::string& key ); + // When a client hasn't selected a player yet, + // we need to uniquely identify them in the lobby server. + // We do this by stuffing a temp value into the fPlayerID + // while keeping the kPlayerID flag cleared. + void SetTempPlayerID(UInt32 id); + + friend bool operator==(const plClientGuid & X, const plClientGuid & Y); + friend bool operator!=(const plClientGuid & X, const plClientGuid & Y); + friend bool operator<(const plClientGuid & X, const plClientGuid & Y); +}; + + + + +#endif // plClientGuid_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommon.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommon.cpp new file mode 100644 index 00000000..f922534e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommon.cpp @@ -0,0 +1,114 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetCommon.h" +#include "hsStlUtils.h" +#include "../plUUID/plUUID.h" +#include + +namespace plNetCommon +{ + //////////////////////////////////////////////////////////////// + namespace VaultTasks + { + const char * VaultTaskStr( int taskID ) + { + switch ( taskID ) + { + case kCreatePlayer: return "CreatePlayer"; + case kDeletePlayer: return "DeletePlayer"; + case kGetPlayerList: return "GetPlayerList"; + case kCreateNeighborhood: return "CreateNeighborhood"; + case kJoinNeighborhood: return "JoinNeighborhood"; + case kSetAgePublic: return "SetAgePublic"; + case kIncPlayerOnlineTime: return "IncPlayerOnlineTime"; + case kEnablePlayer: return "EnablePlayer"; + case kRegisterOwnedAge: return "RegisterOwnedAge"; + case kUnRegisterOwnedAge: return "UnRegisterOwnedAge"; + case kRegisterVisitAge: return "RegisterVisitAge"; + case kUnRegisterVisitAge: return "UnRegisterVisitAge"; + case kFriendInvite: return "FriendInvite"; + default: return "UNKNOWN VAULT TASK"; + } + } + } + + //////////////////////////////////////////////////////////////// + namespace Accounts + { + //////////////////////////////////////////////////////////// + namespace Reserved + { +//////////////////////////////////////////// +// Adding a new reserved Avatar? Make sure you: +// 1) Add it to the switch statement in GetReservedAvatarShape() +// 2) Add the name to the list in GetReservedPlayerNames() +#define kPlayerNameDrWatson "Dr. Watson" +#define kAvatarShapeDrWatson "DrWatson" +#define kPlayerNameRand "Rand" +#define kAvatarShapeRand "RandMiller" +#define kPlayerNameSutherland "Marie Sutherland" +#define kAvatarShapeSutherland "Sutherland" +#define kPlayerNameLaxman "Victor Laxman" +#define kAvatarShapeLaxman "Victor" +#define kPlayerNameKodama "Dr. Kodama" +#define kAvatarShapeKodama "Kodama" +#define kPlayerNameEngberg "Michael Engberg" +#define kAvatarShapeEngberg "Engberg" +#define kPlayerNameZandi "Zandi" +#define kAvatarShapeZandi "Zandi" +#define kPlayerNameYeesha "Yeesha" +#define kAvatarShapeYeesha "Yeesha" +//////////////////////////////////////////// + + const char * GetReservedAvatarShape( const char * playerName, const char * currShapeName ) + { + if ( stricmp( playerName, kPlayerNameDrWatson )==0 ) + return kAvatarShapeDrWatson; + if ( stricmp( playerName, kPlayerNameRand )==0 ) + return kAvatarShapeRand; + if ( stricmp( playerName, kPlayerNameDrWatson )==0 ) + return kAvatarShapeDrWatson; + if ( stricmp( playerName, kPlayerNameRand )==0 ) + return kAvatarShapeRand; + if ( stricmp( playerName, kPlayerNameSutherland )==0 ) + return kAvatarShapeSutherland; + if ( stricmp( playerName, kPlayerNameLaxman )==0 ) + return kAvatarShapeLaxman; + if ( stricmp( playerName, kPlayerNameKodama )==0 ) + return kAvatarShapeKodama; + if ( stricmp( playerName, kPlayerNameEngberg )==0 ) + return kAvatarShapeEngberg; + if ( stricmp( playerName, kPlayerNameZandi )==0 ) + return kAvatarShapeZandi; + if ( stricmp( playerName, kPlayerNameYeesha )==0 ) + return kAvatarShapeYeesha; + // other reserved players go here. + return currShapeName; + } + + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommon.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommon.h new file mode 100644 index 00000000..dd3aaa1a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommon.h @@ -0,0 +1,242 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetCommon_h_inc +#define plNetCommon_h_inc + +#ifndef PLNETCOMMON_CONSTANTS_ONLY + +#include "plNetServerSessionInfo.h" +#include "hsStlUtils.h" + +#endif // PLNETCOMMON_CONSTANTS_ONLY + + +/////////////////////////////////////////////////////////////////// +// Default age info + +#define kStartUpAgeFilename "StartUp" + +#define kNeighborhoodAgeFilename "Neighborhood" +#define kNeighborhoodAgeFilenameW L"Neighborhood" +#define kNeighborhoodAgeInstanceName "Bevin" +#define kNeighborhoodAgeInstanceNameW L"Bevin" +#define kStartupNeighborhoodUserDefinedName "DRC" +#define kStartupNeighborhoodUserDefinedNameW L"DRC" + +#define kCityAgeFilename "city" +#define kCityAgeFilenameW L"city" +#define kCityAgeInstanceName "Ae'gura" +#define kCityAgeInstanceNameW L"Ae'gura" + +#define kAvCustomizationFilename "AvatarCustomization" +#define kAvCustomizationAgeInstanceName "AvatarCustomization" + +#define kNexusAgeFilename "Nexus" +#define kNexusAgeInstanceName "Nexus" + +#define kCleftAgeFilename "Cleft" +#define kCleftAgeInstanceName "Cleft" +#define kCleftAgeLinkInPointFissureDrop "LinkInPointFissureDrop" + +#define kDemoAgeFilename "Demo" +#define kDemoAgeInstanceName "Demo" + +#define kPersonalAgeFilename "Personal" +#define kPersonalAgeFilenameW L"Personal" +#define kPersonalAgeInstanceName "Relto" +#define kPersonalAgeInstanceNameW L"Relto" +#define kPersonalAgeLinkInPointCloset "LinkInPointCloset" + +#define kCityFerryTerminalLinkSpawnPtName "LinkInPointFerry" +#define kCityFerryTerminalLinkTitle "Ferry Terminal" + +#define kDescentLinkFromShell "dsntShaftFromShell" + +#define kWatchersPubAgeFilenameW L"GreatTreePub" +#define kWatchersPubAgeInstanceNameW L"The Watcher's Pub" + +#define kKirelFilenameW L"Neighborhood02" +#define kKirelInstanceNameW L"Kirel" + + +/////////////////////////////////////////////////////////////////// +// Chronicle Var Names + +#define kChronicle_CurCityInstance "CurCityInstance" +#define kChronicle_InitialAvCustomizationsDone "InitialAvCustomizationsDone" +#define kChronicle_CleftSolved "CleftSolved" +#define kChronicle_GiveYeeshaReward "GiveYeeshaReward" + +/////////////////////////////////////////////////////////////////// + +#define kInvalidVaultNodeID 0 +#define kInvalidPlayerID kInvalidVaultNodeID + + +/////////////////////////////////////////////////////////////////// +// Namespace for holding net-oriented shared enums, utils, etc. + +#ifndef PLNETCOMMON_CONSTANTS_ONLY + +namespace plNetCommon +{ + // Topics for plNetMsgVaultTask msg + namespace VaultTasks + { + enum + { + kInvalidLow, + kCreatePlayer, + kDeletePlayer, + kGetPlayerList, + kCreateNeighborhood, + kJoinNeighborhood, + kSetAgePublic, + kIncPlayerOnlineTime, + kEnablePlayer, + kRegisterOwnedAge, + kUnRegisterOwnedAge, + kRegisterVisitAge, + kUnRegisterVisitAge, + kFriendInvite, + kLastVaultTask, + }; + const char * VaultTaskStr( int taskID ); + } + + // Args for plNetMsgVaultTask msg + namespace VaultTaskArgs + { + enum + { + kInvalidLow, + kHoodTitle, + kHoodDesc, + kAgePublic, + kIntArg1, + kIntArg2, + kAgeInfoNodeID, + kAgeLinkNodeID, + kMTStationName, + kSpawnPointName, + kAgeInfoStruct, + kAgeLinkStruct, + kAgeFilename, + kAgeInstanceGuid, + kNodeID, + kFriendName, // Use with key + kInviteKey, // Use with friend + kAgeLinkNode, + }; + } + + //////////////////////////////////////////////////////////////// + namespace Accounts + { + namespace Reserved + { + const char * GetReservedAvatarShape( const char * playerName, const char * currShapeName ); + void GetReservedPlayerNames( std::vector & out ); + bool IsReservedPlayerName( const char * name ); + } + } + + //////////////////////////////////////////////////////////////// + namespace LinkingRules + { + enum Rules + { + // Link to public age: Use PLS-MCP load balancing rules. Don't remember this link in KI/vault. + kBasicLink, + // Link and create a book in the AgesIOwn folder + kOriginalBook, + // Link to a sub age of current age. + kSubAgeBook, + // Link using info from my AgesIOwn folder + kOwnedBook, + // Link using info from my AgesICanVisit folder + kVisitBook, + // Link to a child age of current age. + kChildAgeBook, + }; + + static const char * LinkingRuleStr( int rule ) + { + switch ( rule ) + { + case kBasicLink: return "kBasicLink"; + case kOriginalBook: return "kOriginalBook"; + case kSubAgeBook: return "kSubAgeBook"; + case kOwnedBook: return "kOwnedBook"; + case kVisitBook: return "kVisitBook"; + case kChildAgeBook: return "kChildAgeBook"; + default: return "UNKNOWN LINKING RULE"; + } + } + } + + namespace PetitionTypes + { + enum Types + { + kGeneralHelp = 0, + kBug, + kFeedback, + kExploit, + kHarass, + kStuck, + kTechnical + }; + } + + namespace BuildType + { + enum Types + { + kUnknown = 0, + kDebug, + kInternalRelease, + kExternalRelease + }; + + static const char * BuildTypeStr( int rule ) + { + switch ( rule ) + { + case kDebug: return "Dbg"; + case kInternalRelease: return "IntRel"; + case kExternalRelease: return "ExtRel"; + default: return "UNKNOWN"; + } + } + } +} + +#endif // PLNETCOMMON_CONSTANTS_ONLY + +/////////////////////////////////////////////////////////////////// + +#endif // plNetCommon_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonConstants.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonConstants.h new file mode 100644 index 00000000..85f7b107 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonConstants.h @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetCommonConstants_h_inc +#define plNetCommonConstants_h_inc + + + +#endif // plNetCommonConstants_h_inc + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonCreatable.h new file mode 100644 index 00000000..6b77ec5a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonCreatable.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetCommonCreatable_inc +#define plNetCommonCreatable_inc + +#include "../pnFactory/plCreator.h" + +#ifndef SERVER +#include "plNetMember.h" +REGISTER_NONCREATABLE( plNetMember ); +#endif // SERVER + +#include "plNetCommonHelpers.h" +#ifndef SERVER +REGISTER_CREATABLE( plNetCoreStatsSummary ); +#endif // SERVER +REGISTER_CREATABLE( plCreatableListHelper ); + +// HACK: plUUID should have it's own creatable include +#include "../plUUID/plUUID.h" +REGISTER_CREATABLE( plCreatableUuid ); + +#include "plClientGuid.h" +REGISTER_CREATABLE( plClientGuid ); +#include "plNetServerSessionInfo.h" +REGISTER_CREATABLE( plNetServerSessionInfo ); +REGISTER_CREATABLE( plAgeInfoStruct ); +REGISTER_CREATABLE( plAgeLinkStruct ); + +#endif // plNetCommonCreatable_inc + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonHelpers.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonHelpers.cpp new file mode 100644 index 00000000..474063f7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonHelpers.cpp @@ -0,0 +1,344 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsStream.h" +#include "hsStlUtils.h" +#include "plNetCommonHelpers.h" +#include "../pnNetCommon/plGenericVar.h" +#include "../plCompression/plZlibCompress.h" +#include + + +//////////////////////////////////////////////////////////////////// +#ifndef SERVER +const UInt8 plNetCoreStatsSummary::StreamVersion = 1; + +plNetCoreStatsSummary::plNetCoreStatsSummary() +:fULBitsPS(0), +fDLBitsPS(0), +fULPeakBitsPS(0), +fDLPeakBitsPS(0), +fULPeakPktsPS(0), +fDLPeakPktsPS(0), +fDLDroppedPackets(0) +{ +} + +void plNetCoreStatsSummary::Read(hsStream* s, hsResMgr*) +{ + UInt8 streamVer; + s->ReadSwap(&streamVer); + hsAssert(streamVer==StreamVersion,"plNetCoreStatsSummary invalid stream version."); + s->ReadSwap(&fULBitsPS); + s->ReadSwap(&fDLBitsPS); + s->ReadSwap(&fULPeakBitsPS); + s->ReadSwap(&fDLPeakBitsPS); + s->ReadSwap(&fULPeakPktsPS); + s->ReadSwap(&fDLPeakPktsPS); + s->ReadSwap(&fDLDroppedPackets); +} + +void plNetCoreStatsSummary::Write(hsStream* s, hsResMgr*) +{ + s->WriteSwap(StreamVersion); + s->WriteSwap(fULBitsPS); + s->WriteSwap(fDLBitsPS); + s->WriteSwap(fULPeakBitsPS); + s->WriteSwap(fDLPeakBitsPS); + s->WriteSwap(fULPeakPktsPS); + s->WriteSwap(fDLPeakPktsPS); + s->WriteSwap(fDLDroppedPackets); +} +#endif // SERVER + +//////////////////////////////////////////////////////////////////// + +plCreatableListHelper::plCreatableListHelper() +: fCompressionThreshold( kDefaultCompressionThreshold ) +, fFlags( kWantCompression ) +{ +} + +void plCreatableListHelper::IClearItems() +{ + std::for_each( fManagedItems.begin(), fManagedItems.end(), xtl::delete_ptr() ); + fManagedItems.clear(); + fItems.clear(); +} + +void plCreatableListHelper::CopyFrom( const plCreatableListHelper * other, bool manageItems ) +{ + IClearItems(); + fFlags = other->fFlags; + std::copy( other->fItems.begin(), other->fItems.end(), std::inserter(fItems, fItems.begin() ) ); + fCompressionThreshold = other->fCompressionThreshold; + fWritten = other->fWritten; + if ( manageItems ) + { + std::copy( other->fManagedItems.begin(), other->fManagedItems.end(), std::back_inserter( fManagedItems ) ); + other->fManagedItems.clear(); // we'll take responsibility for these. + } +} + + +void plCreatableListHelper::AddItem( UInt16 id, plCreatable * item, bool manageItem ) +{ + RemoveItem( id ); + fItems[id] = item; + if ( manageItem ) + fManagedItems.push_back( item ); +} + +void plCreatableListHelper::AddItem( UInt16 id, const plCreatable * item, bool manageItem ) +{ + AddItem( id, const_cast( item ), manageItem ); +} + +void plCreatableListHelper::RemoveItem( UInt16 id, bool unManageItem ) +{ + plCreatable * item = GetItem( id ); + if ( !item ) + return; + std::vector::iterator ii = std::find( fManagedItems.begin(),fManagedItems.end(), item ); + if ( ii!=fManagedItems.end() ) + { + if ( !unManageItem ) + delete ( *ii ); + fManagedItems.erase( ii ); + } + fItems.erase( id ); +} + +plCreatable * plCreatableListHelper::GetItem( UInt16 id, bool unManageItem/*=false */) const +{ + std::map::const_iterator it=fItems.find( id ); + if ( it!=fItems.end() ) + { + if ( unManageItem ) + { + std::vector::iterator ii = std::find( fManagedItems.begin(),fManagedItems.end(), it->second ); + if ( ii!=fManagedItems.end() ) + fManagedItems.erase( ii ); + } + return it->second; + } + return nil; +} + +bool plCreatableListHelper::ItemExists( UInt16 id ) const +{ + return ( fItems.find( id )!=fItems.end() ); +} + +void plCreatableListHelper::AddString(UInt16 id, const char * value) +{ + plCreatableGenericValue * V = TRACKED_NEW plCreatableGenericValue(); + V->Value().SetString( (char*)value ); + AddItem( id, V, true ); +} + +void plCreatableListHelper::AddString( UInt16 id, std::string & value ) +{ + AddString( id, value.c_str() ); +} + +void plCreatableListHelper::AddInt( UInt16 id, Int32 value ) +{ + plCreatableGenericValue * V = TRACKED_NEW plCreatableGenericValue(); + V->Value().SetInt(value); + AddItem( id, V, true ); +} + +void plCreatableListHelper::AddDouble( UInt16 id, double value ) +{ + plCreatableGenericValue * V = TRACKED_NEW plCreatableGenericValue(); + V->Value().SetDouble(value); + AddItem( id, V, true ); +} + +const char * plCreatableListHelper::GetString( UInt16 id ) +{ + plCreatableGenericValue * V = plCreatableGenericValue::ConvertNoRef( GetItem( id ) ); + if ( !V ) return nil; + return (const char *)V->Value(); +} + +Int32 plCreatableListHelper::GetInt( UInt16 id ) +{ + plCreatableGenericValue * V = plCreatableGenericValue::ConvertNoRef( GetItem( id ) ); + if ( !V ) return 0; + return (Int32)V->Value(); +} + +double plCreatableListHelper::GetDouble( UInt16 id ) +{ + plCreatableGenericValue * V = plCreatableGenericValue::ConvertNoRef( GetItem( id ) ); + if ( !V ) return 0; + return (double)V->Value(); +} + +void plCreatableListHelper::Read( hsStream* s, hsResMgr* mgr ) +{ + IClearItems(); + + s->LogSubStreamStart("CreatableListHelper"); + + s->LogReadSwap( &fFlags, "Flags" ); + + fFlags &= ~kWritten; + + UInt32 bufSz; + s->LogReadSwap( &bufSz, "BufSz" ); + std::string buf; + buf.resize( bufSz ); + + if ( fFlags&kCompressed ) + { + UInt32 zBufSz; + s->LogReadSwap( &zBufSz, "Compressed BufSz" ); + std::string zBuf; + zBuf.resize( zBufSz ); + s->LogSubStreamPushDesc("Compressed Data"); + s->Read( zBufSz, (void*)zBuf.data() ); + plZlibCompress compressor; + UInt32 tmp; + hsBool ans = compressor.Uncompress( (UInt8*)buf.data(), &tmp, (UInt8*)zBuf.data(), zBufSz ); + hsAssert( ans!=0, "plCreatableListHelper: Failed to uncompress buffer." ); + hsAssert( tmp==bufSz, "compression size mismatch" ); + fFlags&=~kCompressed; + hsLogEntry( plNetApp::StaticDebugMsg( "plCreatableListHelper: uncompressed from %lu to %lu", zBufSz, bufSz ) ); + } + else + { + s->LogSubStreamPushDesc("Uncompressed Data"); + s->Read( bufSz, (void*)buf.data() ); + } + + hsReadOnlyStream ram( bufSz, (void*)buf.data() ); + + UInt16 nItems; + ram.ReadSwap( &nItems ); + for ( int i=0; iRead( &ram, mgr ); + fItems[id] = object; + } + } +} + +void plCreatableListHelper::Write( hsStream* s, hsResMgr* mgr ) +{ + if ( !( fFlags&kWritten ) ) + { + // write items to ram stream + hsRAMStream ram; + UInt16 nItems = fItems.size(); + ram.WriteSwap( nItems ); + for ( std::map::iterator ii=fItems.begin(); ii!=fItems.end(); ++ii ) + { + UInt16 id = ii->first; + plCreatable * item = ii->second; + UInt16 classIdx = item->ClassIndex(); + ram.WriteSwap( id ); + ram.WriteSwap( classIdx ); + item->Write( &ram, mgr ); + } + + // read ram stream into a buffer + UInt32 bufSz = ram.GetPosition(); + ram.Rewind(); + std::string buf; + buf.resize( bufSz ); + ram.Read( bufSz, (void*)buf.data() ); + + // maybe compress the buffer + if ( fFlags&kWantCompression && bufSz>fCompressionThreshold ) + { + plZlibCompress compressor; + UInt32 zBufSz; + std::string zBuf; + zBuf.resize( bufSz ); + hsBool ans = compressor.Compress( (UInt8*)zBuf.data(), &zBufSz, (const UInt8*)buf.data(), bufSz ); + bool compressed = ( ans && zBufSz ); + hsAssert( compressed, "plCreatableListHelper: Failed to compress buffer." ); + if ( compressed ) + { + zBuf.resize( zBufSz ); + buf = zBuf; + fFlags |= kCompressed; + hsLogEntry( plNetApp::StaticDebugMsg( "plCreatableListHelper: compressed from %lu to %lu", bufSz, zBufSz ) ); + } + } + + ram.Truncate(); + + ram.WriteSwap( fFlags ); + ram.WriteSwap( bufSz ); + + if ( fFlags&kCompressed ) + { + UInt32 zBufSz = buf.size(); + ram.WriteSwap( zBufSz ); + } + + ram.Write( buf.size(), buf.data() ); + UInt32 sz = ram.GetPosition(); + ram.Rewind(); + + fWritten.resize( sz ); + ram.Read( sz, (void*)fWritten.data() ); + + fFlags |= kWritten; + } + + s->Write( fWritten.size(), fWritten.data() ); +} + +void plCreatableListHelper::GetItemsAsVec( std::vector& out ) +{ + for ( std::map::iterator ii=fItems.begin(); ii!=fItems.end(); ++ii ) + { + out.push_back( ii->second ); + } +} + +void plCreatableListHelper::GetItems( std::map& out ) +{ + for ( std::map::iterator ii=fItems.begin(); ii!=fItems.end(); ++ii ) + { + out[ii->first] = ii->second; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonHelpers.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonHelpers.h new file mode 100644 index 00000000..3f94012c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetCommonHelpers.h @@ -0,0 +1,164 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetCommonHelpers_h_inc +#define plNetCommonHelpers_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsTimer.h" +#include "../pnNetCommon/pnNetCommon.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnFactory/plCreatable.h" + +//////////////////////////////////////////////////////////////////// + +#ifndef SERVER +class plNetCoreStatsSummary : public plCreatable +{ + static const UInt8 plNetCoreStatsSummary::StreamVersion; + float fULBitsPS; + float fDLBitsPS; + float fULPeakBitsPS; + float fDLPeakBitsPS; + float fULPeakPktsPS; + float fDLPeakPktsPS; + UInt32 fDLDroppedPackets; +public: + plNetCoreStatsSummary(); + CLASSNAME_REGISTER( plNetCoreStatsSummary ); + GETINTERFACE_ANY( plNetCoreStatsSummary, plCreatable ); + void Read(hsStream* s, hsResMgr* mgr=nil); + void Write(hsStream* s, hsResMgr* mgr=nil); + float GetULBitsPS() const { return fULBitsPS; } + float GetDLBitsPS() const { return fDLBitsPS; } + float GetULPeakBitsPS() const { return fULPeakBitsPS; } + float GetDLPeakBitsPS() const { return fDLPeakBitsPS; } + float GetULPeakPktsPS() const { return fULPeakPktsPS; } + float GetDLPeakPktsPS() const { return fDLPeakPktsPS; } + UInt32 GetDLDroppedPackets() const { return fDLDroppedPackets; } +}; +#endif // SERVER + + +//////////////////////////////////////////////////////////////////// + +class plCreatableListHelper : public plCreatable +{ + enum { kDefaultCompressionThreshold = 255 }; // bytes + enum Flags + { + kWantCompression = 1<<0, + kCompressed = 1<<1, + kWritten = 1<<2, + }; + UInt8 fFlags; + std::map fItems; + mutable std::vector fManagedItems; + UInt32 fCompressionThreshold; // NOT WRITTEN + std::string fWritten; + void IClearItems(); + +public: + plCreatableListHelper(); + ~plCreatableListHelper() { IClearItems();} + + CLASSNAME_REGISTER( plCreatableListHelper ); + GETINTERFACE_ANY( plCreatableListHelper, plCreatable ); + + void Read( hsStream* s, hsResMgr* mgr ); + void Write( hsStream* s, hsResMgr* mgr ); + void Clear() { IClearItems(); } + void CopyFrom( const plCreatableListHelper * other, bool manageItems ); + + void SetWantCompression( bool v ) { if ( v ) fFlags|=kWantCompression; else fFlags&=~kWantCompression; } + bool WantCompression() const { return ( fFlags&kWantCompression )!=0; } + UInt32 GetCompressionThreshold() const { return fCompressionThreshold; } + void SetCompressionThreshold( UInt32 v ) { fCompressionThreshold=v; } + + // support for generic arguments + void AddItem( UInt16 id, plCreatable * item, bool manageItem=false ); + void AddItem( UInt16 id, const plCreatable * item, bool manageItem=false ); + plCreatable* GetItem( UInt16 id, bool unManageItem=false ) const; + void RemoveItem( UInt16 id, bool unManageItem=false ); + bool ItemExists( UInt16 id ) const; + int GetNumItems() const { return fItems.size();} + // helpers for typed arguments + void AddString( UInt16 id, const char * value ); + void AddString( UInt16 id, std::string & value ); + const char * GetString( UInt16 id ); + void AddInt( UInt16 id, Int32 value ); + Int32 GetInt( UInt16 id ); + void AddDouble( UInt16 id, double value ); + double GetDouble( UInt16 id ); + void GetItemsAsVec( std::vector& out ); + void GetItems( std::map& out ); +}; + +///////////////////////////////////////////////////////////////////// +struct plOperationTimer +{ + bool fRunning; + double fStartTime; + double fEndTime; + std::string fComment; + std::string fSpacer; + bool fPrintAtStart; + std::string fTag; + plOperationTimer( const char * tag="", bool printAtStart=false ) + : fRunning( false ) + , fTag( tag ) + , fStartTime( 0.0 ) + , fEndTime( 0.0 ) + , fPrintAtStart( printAtStart ) + {} + ~plOperationTimer() { Stop(); } + void Start( const char * comment, int level=0 ) + { + fSpacer = std::string( level, '\t' ); + Stop(); + fRunning = true; + fComment = comment; + fStartTime = hsTimer::GetSeconds(); + if ( fPrintAtStart ) + { + hsLogEntry( plNetApp::StaticDebugMsg( "%s%s Timing: %s", + fSpacer.c_str(), fTag.c_str(), fComment.c_str() ) ); + } + } + void Stop() + { + if ( !fRunning ) + return; + fRunning = false; + fEndTime = hsTimer::GetSeconds()-fStartTime; + hsLogEntry( plNetApp::StaticDebugMsg( "%s%s Timed: %f secs: %s", + fSpacer.c_str(), fTag.c_str(), fEndTime, fComment.c_str() ) ); + } + double GetTime() const { return fEndTime;} +}; + + +#endif // plNetCommonHelpers_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMember.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMember.cpp new file mode 100644 index 00000000..b025e3d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMember.cpp @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetMember.h" + + +//////////////////////////////////////////////////////// +// plNetMember +//////////////////////////////////////////////////////// +plNetMember::plNetMember(plNetApp* na) +{ + Reset(); + fNetApp=(na); +} + +plNetMember::plNetMember() +: fNetApp(nil) +{ + Reset(); +} + +// +// doesn't remove from session, just resets to initial state +// +void plNetMember::Reset() +{ + fFlags=(0); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMember.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMember.h new file mode 100644 index 00000000..b25d99ea --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMember.h @@ -0,0 +1,88 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLNETMEMBER_inc +#define PLNETMEMBER_inc + +#include "hsConfig.h" +#include "hsUtils.h" +#include "hsStlUtils.h" +#include "../pnFactory/plCreatable.h" + +class plNetApp; +class plNetGenericServer; +//////////////////////////////// +// A participant (peer) who we can send and recv messages from/to +//////////////////////////////// +class plNetMember : public plCreatable +{ +public: + enum Flags + { + kWaitingForLinkQuery = 1<<0, // only used server side + kIndirectMember = 1<<1, // this guy is behind a firewall of some sort + kRequestP2P = 1<<2, // wants to play peer to peer + kWaitingForChallengeResponse = 1<<3, // waiting for client response + kIsServer = 1<<4, // used by transport member + kAllowTimeOut = 1<<5, // used by gameserver + }; + +protected: + friend class plNetGenericServer; + friend class plNetClientMgr; + friend class plNetClientMsgHandler; + friend class plNetClientGamePlayMsgHandler; + + Int32 fPeerID; // low-level netPlayer object for msg send/recv + UInt32 fFlags; + plNetApp* fNetApp; + + // these calls should be made by the client/server app only, + // so they can keep the netCorePeer userData point to the right member + virtual ~plNetMember() {} + +public: + CLASSNAME_REGISTER( plNetMember ); + GETINTERFACE_ANY( plNetMember, plCreatable ); + + plNetMember(); + plNetMember(plNetApp* na); + + virtual void Reset(); // doesn't remove from session, just resets to initial state + virtual bool IsEqualTo(const plNetMember * other) const = 0; + + // getters + Int32 GetPeerID() const { return fPeerID; } + UInt32 GetFlags() const { return fFlags; } + plNetApp* GetNetApp() { return fNetApp; } + virtual std::string AsStdString() const = 0; + + // setters + void SetFlags(UInt32 f) { fFlags=f; } + void SetNetApp(plNetApp* n) { fNetApp=n; } + void SetPeerID(Int32 p) { fPeerID=p; } +}; + +#endif // PLNETMEMBER_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgHandler.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgHandler.h new file mode 100644 index 00000000..0fbcad66 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgHandler.h @@ -0,0 +1,80 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetMsgHandler_inc +#define plNetMsgHandler_inc + +#include "hsConfig.h" +#include "hsTypes.h" // for nil + +class plNetMessage; +class plNetApp; + +#define kMsgSendInterval 0.25f // time between message re-send +#define kNetOperationTimeout 10.f // amount of time we try a network operation before complaining + +// +// Base msg handler class +// +class plNetMsgHandler +{ +protected: + plNetApp* fNetApp; +public: + plNetMsgHandler() : fNetApp(nil) {} + virtual ~plNetMsgHandler() {} + + void SetNetApp(plNetApp* na) { fNetApp=na; } + plNetApp* GetNetApp() { return fNetApp; } + + // return -1 on error, 0 if ok. + virtual int ReceiveMsg(plNetMessage*& netMsg) = 0; +}; + +#define MSG_HANDLER(msgClassName) msgClassName##HandleMsg + +// +// Use to declare msg handler fxns in your MsgHandler .h class header +// +#define MSG_HANDLER_DECL(msgClassName) \ +virtual int MSG_HANDLER(msgClassName)(plNetMessage*& netMsg); + +// +// Use to define msg handler fxns in your MsgHandler .cpp file +// +#define MSG_HANDLER_DEFN(handlerClassName, msgClassName) \ +int handlerClassName::MSG_HANDLER(msgClassName)(plNetMessage*& netMsg) + +// +// Use in the switch statement in your ReceiveMsg function +// +#define MSG_HANDLER_CASE(msgClassName) \ +case CLASS_INDEX_SCOPED(msgClassName): \ + { \ + return MSG_HANDLER(msgClassName)(netMsg); \ + } + + +#endif // plNetMsgHandler_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp new file mode 100644 index 00000000..2266d130 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.cpp @@ -0,0 +1,248 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plNetMsgScreener.h" +#include "plCreatableIndex.h" + +#include "../pnNetCommon/plNetApp.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnMessage/plEnableMsg.h" +#include "../pnMessage/plSetNetGroupIDMsg.h" +#include "../pnInputCore/plControlEventCodes.h" + +#include "../plMessage/plCCRMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../plMessage/plAvatarMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plBulletMsg.h" +#include "../plMessage/plAvCoopMsg.h" +#include "../plMessage/plParticleUpdateMsg.h" + +#include "../../FeatureLib/pfMessage/pfKIMsg.h" +#include "../../FeatureLib/pfMessage/plClothingMsg.h" + +// +// say why the msg got rejected +// +void plNetMsgScreener::IRejectLogMsg(Int16 classIndex, const char* desc, const plNetGameMember* gm) const +{ + DebugMsg("Message %s was rejected, reason:%s, age:%s, client:%s", + plFactory::GetNameOfClass(classIndex), desc, IGetAgeName(), IGetSenderName(gm)); +} + +// +// say why the msg got rejected +// +void plNetMsgScreener::IRejectLogMsg(const plMessage* msg, const char* desc, const plNetGameMember* gm) const +{ + const char* senderName = msg->GetSender() ? msg->GetSender()->GetUoid().GetObjectName() : "?"; + const char* rcvrName = msg->GetNumReceivers() && msg->GetReceiver(0) ? msg->GetReceiver(0)->GetUoid().GetObjectName() : "?"; + + DebugMsg("Message %s was rejected, reason:%s, age:%s, client:%s, msgSndr:%s, msgRcvr:%s", + msg->ClassName(), desc, IGetAgeName(), IGetSenderName(gm), + senderName, rcvrName); +} + +// +// Try to accept/reject quickly +// the netMsg arg has been peeked except for the stream +// +plNetMsgScreener::Answer plNetMsgScreener::IAllowMessageType(Int16 classIndex, const plNetGameMember* gm) const +{ + // Check based on baseclass + if (plFactory::DerivesFrom(plCCRMessage::Index(), classIndex)) + { + ILogCCRMessage(classIndex, gm); + Answer ans=IIsSenderCCR(gm) ? kYes : kNo; + if (ans==kNo) + { + IRejectLogMsg(classIndex, "Not a CCR", gm); + } + return ans; + } + + // Check based on exact type + switch(classIndex) + { + // these are wrapped in their own net msg, so the client will see them this way, but not the server + // that's why they check IAmClient() - this is a special case + case CLASS_INDEX_SCOPED(plLoadAvatarMsg): + case CLASS_INDEX_SCOPED(plLoadCloneMsg): + { + Answer ans=IAmClient() ? kYes : kNo; + if (ans==kNo) + { + IRejectLogMsg(classIndex, "Only seen in native form on client", gm); + } + return ans; + } + + // definitely yes + case CLASS_INDEX_SCOPED(pfMarkerMsg): + case CLASS_INDEX_SCOPED(plBulletMsg): + case CLASS_INDEX_SCOPED(plNotifyMsg): + case CLASS_INDEX_SCOPED(plSetNetGroupIDMsg): + case CLASS_INDEX_SCOPED(plAvCoopMsg): + case CLASS_INDEX_SCOPED(plClothingMsg): + case CLASS_INDEX_SCOPED(plEnableMsg): + case CLASS_INDEX_SCOPED(plLinkToAgeMsg): + return kYes; + + // definitely yes or no (based on whether sender is a CCR) + case CLASS_INDEX_SCOPED(plWarpMsg): + { + Answer ans=IIsSenderCCR(gm) ? kYes : kNo; + if (ans==kNo) + { + IRejectLogMsg(classIndex, "Not a CCR", gm); + } + return ans; + } + + // conditionally yes, requires further validation of msg contents + case CLASS_INDEX_SCOPED(plAnimCmdMsg): + case CLASS_INDEX_SCOPED(pfKIMsg): + case CLASS_INDEX_SCOPED(plAvTaskMsg): + case CLASS_INDEX_SCOPED(plLinkEffectsTriggerMsg): + case CLASS_INDEX_SCOPED(plInputIfaceMgrMsg): + case CLASS_INDEX_SCOPED(plParticleKillMsg): + case CLASS_INDEX_SCOPED(plParticleTransferMsg): + case CLASS_INDEX_SCOPED(plAvatarInputStateMsg): + case CLASS_INDEX_SCOPED(plAvBrainGenericMsg): + case CLASS_INDEX_SCOPED(plMultistageModMsg): + return kMaybe; + + // definitely no + default: + IRejectLogMsg(classIndex, "Illegal msg class", gm); + return kNo; + } +} + +// +// Message may be allowed if contents or conditions are met +// +bool plNetMsgScreener::IValidateMessage(const plMessage* msg, const plNetGameMember* gm) const +{ + if (!msg) + return true; + + switch(msg->ClassIndex()) + { + // Only chat KI msgs are allowed. + // Admin/system-wide chat msgs are only allowed by CCRs + case CLASS_INDEX_SCOPED(pfKIMsg): + { + const pfKIMsg* km = pfKIMsg::ConvertNoRef(msg); + if (km->GetCommand() != pfKIMsg::kHACKChatMsg) + { + IRejectLogMsg(msg, "Non-chat KI msg", gm); + return false; + } + + ILogChatMessage(msg, gm); + if (km->GetFlags() & pfKIMsg::kAdminMsg) + { + if (!IIsSenderCCR(gm)) + { + IRejectLogMsg(msg, "Must be a CCR to send an Admin KI msg", gm); + return false; + } + } + return true; + } + break; + + // Allowed for local avatar + case CLASS_INDEX_SCOPED(plAvTaskMsg): + case CLASS_INDEX_SCOPED(plAvatarInputStateMsg): + case CLASS_INDEX_SCOPED(plAvBrainGenericMsg): + case CLASS_INDEX_SCOPED(plMultistageModMsg): + { + bool ret=IIsLocalArmatureModKey(msg->GetReceiver(0), gm); + if (!ret) + { + IRejectLogMsg(msg, "msg must refer to local avatar", gm); + } + return ret; + } + + // Allowed for local avatar + case CLASS_INDEX_SCOPED(plLinkEffectsTriggerMsg): + { + const plLinkEffectsTriggerMsg* linkMsg = plLinkEffectsTriggerMsg::ConvertNoRef(msg); + bool ret=IIsLocalAvatarKey(linkMsg->GetLinkKey(), gm); + if (!ret) + { + IRejectLogMsg(msg, "msg must refer to local avatar", gm); + } + return ret; + } + + // Allowed for local avatar + case CLASS_INDEX_SCOPED(plInputIfaceMgrMsg): + { + const plInputIfaceMgrMsg* iMsg = plInputIfaceMgrMsg::ConvertNoRef(msg); + bool ret=IIsLocalAvatarKey(iMsg->GetAvKey(), gm); + if (!ret) + { + IRejectLogMsg(msg, "msg must refer to local avatar", gm); + } + return ret; + } + break; + + case CLASS_INDEX_SCOPED(plParticleKillMsg): + case CLASS_INDEX_SCOPED(plParticleTransferMsg): + { + bool ret = IIsLocalAvatarKey(msg->GetReceiver(0), gm); + if (!ret) + { + IRejectLogMsg(msg, "msg must refer to local avatar", gm); + } + return ret; + } + break; + + case CLASS_INDEX_SCOPED(plAnimCmdMsg): + { + const plAnimCmdMsg *animMsg = plAnimCmdMsg::ConvertNoRef(msg); + bool ret = (animMsg->GetNumCallbacks() == 0); + if (!ret) + { + IRejectLogMsg(msg, "msg has callbacks", gm); + } + return ret; + } + break; + default: + return false; + } + +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.h new file mode 100644 index 00000000..bee16a25 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetMsgScreener.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetMsgScreener_h +#define plNetMsgScreener_h + +#include "../pnKeyedObject/plKey.h" +#include "../plStatusLog/plLoggable.h" + +// +// Class which decides what game messages are allowed to be sent to the server. +// Used both client and server-side. +// +class plNetGameMember; +class plMessage; +class plNetMessage; +class pfKIMsg; +class plNetMsgScreener : public plLoggable +{ +protected: + enum Answer + { + kMaybe = -1, + kNo, + kYes + }; + virtual const char* IGetSenderName(const plNetGameMember* gm) const = 0; + virtual const char* IGetAgeName() const = 0; + virtual bool IIsSenderCCR(const plNetGameMember* gm=nil) const = 0; + virtual bool IIsLocalAvatarKey(plKey key, const plNetGameMember* gm) const = 0; + virtual bool IIsLocalArmatureModKey(plKey key, const plNetGameMember* gm) const { return true; } + + virtual void ILogChatMessage(const plMessage* msg_, const plNetGameMember* gm) const {} + virtual void ILogCCRMessage(Int16 classIndex, const plNetGameMember* gm) const {} + + Answer IAllowMessageType(Int16 classIndex, const plNetGameMember* gm=nil) const; + bool IValidateMessage(const plMessage* msg, const plNetGameMember* gm=nil) const; + void IRejectLogMsg(Int16 classIndex, const char* desc, const plNetGameMember* gm) const; + void IRejectLogMsg(const plMessage* msg, const char* desc, const plNetGameMember* gm) const; + virtual bool IAmClient() const = 0; +}; + +#endif // plNetMsgScreener_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetResManager.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetResManager.cpp new file mode 100644 index 00000000..994ed3a5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetResManager.cpp @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plNetResManager +// +//// Philosophy ////////////////////////////////////////////////////////////// +// +// "Cannot say. Saying, I would know. Do not know, so cannot say." +// -- Zathras, "Babylon 5" +// +// Normally, plResManager would be plenty for the servers and then some. +// However, the normal resManager tries to do things smart, such as read in +// keys from disk if they don't already exist and so forth. However, all the +// servers care about is reading in enough of a key to be able to turn around +// and write it back out to a stream. So, we overload ReadKeyAndReg() to just +// read in a new key and return it. Our new key reffing system will guarantee +// that the key eventually gets freed once we're done with it, and we don't +// care about sharing keys because all we're interested in is the uoid anyway, +// so no need to store the keys in the registry or anything. +// +////////////////////////////////////////////////////////////////////////////// + +#include "plNetResManager.h" +#include "../pnKeyedObject/plKeyImp.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnFactory/plCreatable.h" +#include "../pnNetCommon/plNetApp.h" +#include "hsStream.h" + +plNetResManager::plNetResManager() +{ +} + +plNetResManager::~plNetResManager() +{ +} + +plCreatable* plNetResManager::IReadCreatable(hsStream* s) const +{ + UInt16 hClass = s->ReadSwap16(); + if (plFactory::CanCreate(hClass)) + { + plCreatable *pCre = plFactory::Create(hClass); + if (!pCre) + hsAssert( hClass == 0x8000, "Invalid creatable index" ); + + return pCre; + } + + plNetApp::StaticWarningMsg("NetResMgr::Can't create class %s", plFactory::GetNameOfClass(hClass)); + return nil; +} + +void plNetResManager::IKeyReffed(plKeyImp* key) +{ +} + +void plNetResManager::IKeyUnreffed(plKeyImp* key) +{ + delete key; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetResManager.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetResManager.h new file mode 100644 index 00000000..ab17dd78 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetResManager.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetResMgr_h +#define plNetResMgr_h + +#include "../plResMgr/plResManager.h" +#include "../pnKeyedObject/plKey.h" + +// +// plNetResMgr - Keys? Disk? What? Nah, we'll just create new ones and give them to ya +// + +class hsStream; +class plNetResManager : public plResManager +{ +public: + plNetResManager(); + ~plNetResManager(); + +protected: + plCreatable* IReadCreatable(hsStream* s) const; + virtual void IKeyReffed(plKeyImp* key); + virtual void IKeyUnreffed(plKeyImp* key); +}; + + +#endif // plNetResMgr_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetServerSessionInfo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetServerSessionInfo.cpp new file mode 100644 index 00000000..96b8aef7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetServerSessionInfo.cpp @@ -0,0 +1,792 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsStream.h" +#include "../pnMessage/plMessage.h" +#include "plNetServerSessionInfo.h" +#include "hsStlUtils.h" +#include "plNetCommon.h" +#include "../plVault/plVault.h" + +#include + +#define SAFE(s) ((s)?(s):"(nil)") +#define kComma "," +#define kEmpty "" +#define kSemicolon ";" + + +//////////////////////////////////////////////////////////////////// + +void plAgeInfoStruct::Read( hsStream * s, hsResMgr* ) +{ + s->LogSubStreamStart("push me"); + s->LogReadSwap( &fFlags ,"AgeInfoStruct Flags"); + if ( IsFlagSet( kHasAgeFilename ) ) { + s->LogSubStreamPushDesc("AgeFilename"); + plMsgStdStringHelper::Peek(fAgeFilename,s); + } + if ( IsFlagSet( kHasAgeInstanceName ) ) { + s->LogSubStreamPushDesc("AgeInstanceName"); + plMsgStdStringHelper::Peek(fAgeInstanceName,s); + } + if ( IsFlagSet( kHasAgeInstanceGuid ) ) { + s->LogSubStreamPushDesc("AgeInstanceGuid"); + fAgeInstanceGuid.Read( s ); + } + if ( IsFlagSet( kHasAgeUserDefinedName ) ){ + s->LogSubStreamPushDesc("UserDefinedName"); + plMsgStdStringHelper::Peek(fAgeUserDefinedName,s); + } + if ( IsFlagSet( kHasAgeSequenceNumber ) ) { + s->LogReadSwap( &fAgeSequenceNumber ,"AgeSequenceNumber"); + } + if ( IsFlagSet( kHasAgeDescription ) ) { + s->LogSubStreamPushDesc("AgeDescription"); + plMsgStdStringHelper::Peek(fAgeDescription,s); + } + if ( IsFlagSet( kHasAgeLanguage ) ) { + s->LogReadSwap( &fAgeLanguage ,"AgeLanguage"); + } + UpdateFlags(); + s->LogSubStreamEnd(); +} + +void plAgeInfoStruct::Write( hsStream * s, hsResMgr* ) +{ + UpdateFlags(); + s->WriteSwap( fFlags ); + if ( IsFlagSet( kHasAgeFilename ) ) + plMsgStdStringHelper::Poke(fAgeFilename,s); + if ( IsFlagSet( kHasAgeInstanceName ) ) + plMsgStdStringHelper::Poke(fAgeInstanceName,s); + if ( IsFlagSet( kHasAgeInstanceGuid ) ) + fAgeInstanceGuid.Write( s ); + if ( IsFlagSet( kHasAgeUserDefinedName ) ) + plMsgStdStringHelper::Poke(fAgeUserDefinedName,s); + if ( IsFlagSet( kHasAgeSequenceNumber ) ) + s->WriteSwap( fAgeSequenceNumber ); + if ( IsFlagSet( kHasAgeDescription ) ) + plMsgStdStringHelper::Poke(fAgeDescription,s); + if ( IsFlagSet( kHasAgeLanguage ) ) + s->WriteSwap( fAgeLanguage ); +} + +bool plAgeInfoStruct::IsEqualTo( const plAgeInfoStruct * other ) const +{ + UpdateFlags(); + other->UpdateFlags(); + + // if we both have guids, just compare them. + if ( HasAgeInstanceGuid() && other->HasAgeInstanceGuid() ) + return fAgeInstanceGuid.IsEqualTo( other->GetAgeInstanceGuid() ); + // otherwise compare everything. + bool match = true; + if (match && HasAgeFilename() && other->HasAgeFilename()) + match = match && ( stricmp( GetAgeFilename(), other->GetAgeFilename() )==0 ); + if (match && HasAgeInstanceName() && other->HasAgeInstanceName()) + match = match && ( stricmp( GetAgeInstanceName(), other->GetAgeInstanceName() )==0 ); + if (match && HasAgeUserDefinedName() && other->HasAgeUserDefinedName()) + match = match && ( stricmp( GetAgeUserDefinedName(), other->GetAgeUserDefinedName() )==0 ); + if (match && HasAgeSequenceNumber() && other->HasAgeSequenceNumber()) + match = match && fAgeSequenceNumber==other->GetAgeSequenceNumber(); + if (match && HasAgeLanguage() && other->HasAgeLanguage()) + match = match && fAgeLanguage==other->GetAgeLanguage(); + // don't compare description fields + return match; +} + +void plAgeInfoStruct::CopyFrom( const plVaultAgeInfoNode * node ) +{ + hsAssert(false, "eric, port me"); + Clear(); +/* + if ( node ) + { + SetAgeFilename( node->GetAgeFilename() ); + SetAgeInstanceName( node->GetAgeInstanceName() ); + SetAgeInstanceGuid( node->GetAgeInstanceGuid() ); + SetAgeUserDefinedName( node->GetAgeUserDefinedName() ); + SetAgeSequenceNumber( node->GetSequenceNumber() ); + SetAgeDescription( node->GetAgeDescription() ); + SetAgeLanguage( node->GetAgeLanguage() ); + UpdateFlags(); + } +*/ +} + +//============================================================================ +void plAgeInfoStruct::CopyFrom(const NetAgeInfo & info) { + char tmp[MAX_PATH]; + + // Filename + StrToAnsi(tmp, info.ageFilename, arrsize(tmp)); + SetAgeFilename(tmp); + // InstanceName + StrToAnsi(tmp, info.ageInstName, arrsize(tmp)); + SetAgeInstanceName(tmp); + // UserDefinedName + StrToAnsi(tmp, info.ageUserName, arrsize(tmp)); + SetAgeUserDefinedName(tmp); + // Description + StrToAnsi(tmp, info.ageDesc, arrsize(tmp)); + SetAgeDescription(tmp); + + SetAgeInstanceGuid(&plUUID(info.ageInstId)); + SetAgeSequenceNumber(info.ageSequenceNumber); + SetAgeLanguage(info.ageLanguage); +} + +//============================================================================ +std::string plAgeInfoStruct::AsStdString() const +{ + const char * spacer = kEmpty; + + std::stringstream ss; + + ss << "["; + + if (HasAgeFilename()) + { + ss << spacer + << "FName:" + << SAFE(GetAgeFilename()); + spacer = kComma; + } + if (HasAgeInstanceName()) + { + ss << spacer + << "IName:" + << SAFE(GetAgeInstanceName()); + spacer = kComma; + } + if (HasAgeInstanceGuid()) + { + ss << spacer + << "Guid:" + << fAgeInstanceGuid.AsString(); + spacer = kComma; + } + if (HasAgeUserDefinedName()) + { + ss << spacer + << "UName:" + << SAFE(GetAgeUserDefinedName()); + spacer = kComma; + } + if (HasAgeSequenceNumber()) + { + ss << spacer + << "Seq:" + << GetAgeSequenceNumber(); + spacer = kComma; + } + if (HasAgeDescription()) + { + ss << spacer + << "Desc:" + << SAFE(GetAgeDescription()); + spacer = kComma; + } + if (HasAgeLanguage()) + { + ss << spacer + << "Lang:" + << GetAgeLanguage(); + spacer = kComma; + } + ss << "]"; + + return ss.str().c_str(); +} + + +void plAgeInfoStruct::SetAgeFilename( const char * v ) +{ + if ( v && v[0]) + { + SetFlag( kHasAgeFilename ); + fAgeFilename=v; + } + else + { + ClearFlag( kHasAgeFilename ); + } +} + +void plAgeInfoStruct::SetAgeInstanceName( const char * v ) +{ + if ( v && v[0]) + { + SetFlag( kHasAgeInstanceName ); + fAgeInstanceName=v; + } + else + { + ClearFlag( kHasAgeInstanceName ); + } +} + +void plAgeInfoStruct::SetAgeInstanceGuid( const plUUID * v ) +{ + if ( v ) + { + SetFlag( kHasAgeInstanceGuid ); + fAgeInstanceGuid.CopyFrom( v ); + } + else + { + ClearFlag( kHasAgeInstanceGuid ); + fAgeInstanceGuid.Clear(); + } +} + +void plAgeInfoStruct::SetAgeUserDefinedName( const char * v ) +{ + if ( v && v[0]) + { + SetFlag( kHasAgeUserDefinedName ); + fAgeUserDefinedName=v; + } + else + { + ClearFlag( kHasAgeUserDefinedName ); + } +} + +void plAgeInfoStruct::SetAgeSequenceNumber( UInt32 v ) +{ + if ( v ) + { + SetFlag( kHasAgeSequenceNumber ); + fAgeSequenceNumber=v; + } + else + { + ClearFlag( kHasAgeSequenceNumber ); + } +} + +void plAgeInfoStruct::SetAgeDescription( const char * v ) +{ + if ( v && v[0]) + { + SetFlag( kHasAgeDescription ); + fAgeDescription=v; + } + else + { + ClearFlag( kHasAgeDescription ); + } +} + +void plAgeInfoStruct::SetAgeLanguage( UInt32 v ) +{ + if ( v >= 0 ) + { + SetFlag( kHasAgeLanguage ); + fAgeLanguage = v; + } + else + { + ClearFlag( kHasAgeLanguage ); + } +} + +void plAgeInfoStruct::UpdateFlags() const +{ + SetFlag( kHasAgeFilename, fAgeFilename.size()!=0 ); + SetFlag( kHasAgeInstanceName, fAgeInstanceName.size()!=0 ); + SetFlag( kHasAgeUserDefinedName, fAgeUserDefinedName.size()!=0 ); + SetFlag( kHasAgeInstanceGuid, fAgeInstanceGuid.IsSet() ); + SetFlag( kHasAgeSequenceNumber, fAgeSequenceNumber!=0 ); + SetFlag( kHasAgeDescription, fAgeDescription.size()!=0 ); + SetFlag( kHasAgeLanguage, fAgeLanguage>=0 ); +} + +void plAgeInfoStruct::Clear() +{ + fFlags = 0; + fAgeFilename = ""; + fAgeInstanceName = ""; + fAgeUserDefinedName = ""; + fAgeInstanceGuid.Clear(); + fAgeSequenceNumber = 0; + fAgeDescription = ""; + fAgeLanguage = -1; +} + +const char * plAgeInfoStruct::GetDisplayName() const +{ + int seq = GetAgeSequenceNumber(); + if ( seq>0 ) + xtl::format( fDisplayName, "%s(%d) %s", GetAgeUserDefinedName(), seq, GetAgeInstanceName() ); + else + xtl::format( fDisplayName, "%s %s", GetAgeUserDefinedName(), GetAgeInstanceName() ); + return fDisplayName.c_str(); +} + + +//////////////////////////////////////////////////////////////////// + +plAgeLinkStruct::plAgeLinkStruct() +: fFlags( kHasAgeInfo|kHasLinkingRules|kHasSpawnPt ) +, fLinkingRules( plNetCommon::LinkingRules::kBasicLink ) +, fSpawnPoint( kDefaultSpawnPoint ) +, fAmCCR( 0 ) +{ +} + +void plAgeLinkStruct::Read( hsStream * s, hsResMgr* m) +{ + s->LogSubStreamStart("push me"); + s->LogReadSwap( &fFlags ,"AgeLinkStruct Flags"); + if ( IsFlagSet( kHasAgeInfo ) ) { + s->LogSubStreamPushDesc("AgeInfo"); + fAgeInfo.Read( s,m ); + } + if ( IsFlagSet( kHasLinkingRules ) ) + s->LogReadSwap( &fLinkingRules ,"LinkingRules"); + if ( IsFlagSet( kHasSpawnPt_DEAD ) ) + { + std::string str; + s->LogSubStreamPushDesc("SpawnPt_DEAD"); + plMsgStdStringHelper::Peek(str,s); + fSpawnPoint.SetName( str.c_str() ); + if ( strcmp( fSpawnPoint.GetName(), kDefaultSpawnPtName )==0 ) + fSpawnPoint.SetTitle( kDefaultSpawnPtTitle ); + else + fSpawnPoint.SetTitle( str.c_str() ); + ClearFlag( kHasSpawnPt_DEAD ); + SetFlag( kHasSpawnPt ); + } + if ( IsFlagSet( kHasSpawnPt_DEAD2 ) ) + { + s->LogSubStreamPushDesc("SpawnPt_DEAD2"); + fSpawnPoint.ReadOld( s ); + ClearFlag( kHasSpawnPt_DEAD2 ); + SetFlag( kHasSpawnPt ); + } + else if ( IsFlagSet( kHasSpawnPt ) ) + { + s->LogSubStreamPushDesc("SpawnPt"); + fSpawnPoint.Read( s ); + } + if ( IsFlagSet( kHasAmCCR ) ) + s->LogReadSwap( &fAmCCR ,"AmCCR"); + + if ( IsFlagSet( kHasParentAgeFilename ) ) + { + s->LogSubStreamPushDesc("ParentAgeFilename"); + plMsgStdStringHelper::Peek(fParentAgeFilename,s); + } +} + +void plAgeLinkStruct::Write( hsStream * s, hsResMgr* m) +{ + s->WriteSwap( fFlags ); + if ( IsFlagSet( kHasAgeInfo ) ) + fAgeInfo.Write( s,m ); + if ( IsFlagSet( kHasLinkingRules ) ) + s->WriteSwap( fLinkingRules ); + if ( IsFlagSet( kHasSpawnPt ) ) + fSpawnPoint.Write( s ); + if ( IsFlagSet( kHasAmCCR ) ) + s->WriteSwap( fAmCCR ); + if ( IsFlagSet( kHasParentAgeFilename ) ) + plMsgStdStringHelper::Poke(fParentAgeFilename,s); +} + +void plAgeLinkStruct::SetParentAgeFilename( const char * v ) +{ + if ( v ) + { + SetFlag( kHasParentAgeFilename ); + fParentAgeFilename=v; + } + else + { + ClearFlag( kHasParentAgeFilename ); + } +} + +void plAgeLinkStruct::CopyFrom( const plAgeLinkStruct * other ) +{ + if ( other ) + { + *this=*other; + } + else + { + Clear(); + } +} + +void plAgeLinkStruct::CopyFrom( const plVaultAgeLinkNode * node ) +{ + // don't clear ourselves, copy age info from node. leave spawn point alone. + if ( node ) + { + hsAssert(false, "eric, port me"); +// fAgeInfo.CopyFrom( node->GetAgeInfo() ); + } + else + { + Clear(); + } +} + +bool plAgeLinkStruct::IsEqualTo( const plAgeLinkStruct * other ) const +{ + bool match = true; + if (match && IsFlagSet(kHasAgeInfo) && other->IsFlagSet(kHasAgeInfo)) + match = match && fAgeInfo.IsEqualTo( other->GetAgeInfo() ); + // don't compare linking rules. + // don't compare SpawnPt + // don't compare AmCCR + return match; +} + +void plAgeLinkStruct::Clear() +{ + fFlags = 0; + fAgeInfo.Clear(); + fLinkingRules = plNetCommon::LinkingRules::kBasicLink; + fSpawnPoint = kDefaultSpawnPoint; + fAmCCR = false; +} + +std::string plAgeLinkStruct::AsStdString() const +{ + const char * spacer = kEmpty; + + std::stringstream ss; + + ss << "["; + + if (HasAgeInfo()) + { + ss << spacer + << "Nfo:" + << fAgeInfo.AsStdString(); + spacer = kComma; + } + if (HasLinkingRules()) + { + ss << spacer + << "Rule:" + << plNetCommon::LinkingRules::LinkingRuleStr( GetLinkingRules() ); + spacer = kComma; + } + if (HasSpawnPt()) + { + ss << spacer + << "Spwn:" + << fSpawnPoint.AsStdString().c_str(); + spacer = kComma; + } + if (HasAmCCR()) + { + ss << spacer + << "CCR:" + << ( GetAmCCR()?"yes":"no" ); + spacer = kComma; + } + ss << "]"; + + return ss.str().c_str(); +} + + +//////////////////////////////////////////////////////////////////// + +void plNetServerSessionInfo::Read(hsStream* s, hsResMgr*) +{ + Clear(); + s->LogSubStreamStart("push me"); + s->LogReadSwap(&fFlags,"ServerSessionInfo Flags"); + if (IsFlagSet(kHasServerName)){ + s->LogSubStreamPushDesc("ServerName"); + plMsgStdStringHelper::Peek(fServerName,s); + } + if (IsFlagSet(kHasServerType)) + s->LogReadSwap(&fServerType,"ServerType"); + if (IsFlagSet(kHasServerAddr)){ + s->LogSubStreamPushDesc("ServerAddr"); + plMsgStdStringHelper::Peek(fServerAddr,s); + } + if (IsFlagSet(kHasServerPort)) + s->LogReadSwap(&fServerPort,"ServerPort"); + if (IsFlagSet(kHasServerGuid)){ + s->LogSubStreamPushDesc("ServerGuid"); + fServerGuid.Read(s); + } +} + +void plNetServerSessionInfo::Write(hsStream* s, hsResMgr*) +{ + s->WriteSwap(fFlags); + if (IsFlagSet(kHasServerName)) + plMsgStdStringHelper::Poke(fServerName,s); + if (IsFlagSet(kHasServerType)) + s->WriteSwap(fServerType); + if (IsFlagSet(kHasServerAddr)) + plMsgStdStringHelper::Poke(fServerAddr,s); + if (IsFlagSet(kHasServerPort)) + s->WriteSwap(fServerPort); + if (IsFlagSet(kHasServerGuid)) + fServerGuid.Write(s); +} + +// Read and Write already have their own flags, so I'll just leave those for now. -Colin +void plNetServerSessionInfo::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + Read(s, mgr); +} + +void plNetServerSessionInfo::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + Write(s, mgr); +} + +void plNetServerSessionInfo::Clear() +{ + fFlags = 0; + fServerName = ""; + fServerType = plNetServerConstants::kInvalidLo; + fServerAddr = ""; + fServerPort = 0; + fServerGuid = plUUID(); +} + +void plNetServerSessionInfo::CopyFrom(const plNetServerSessionInfo * other) +{ + if ( other ) + { + fFlags = other->fFlags; + fServerName = other->fServerName; + fServerType = other->fServerType; + fServerAddr = other->fServerAddr; + fServerPort = other->fServerPort; + fServerGuid.CopyFrom(other->fServerGuid); + } + else + { + Clear(); + } +} + +std::string plNetServerSessionInfo::AsStdString() const +{ + const char * spacer = kEmpty; + + std::stringstream ss; + + ss << "["; + + if (HasServerType()) + { + ss << spacer + << "T:" + << plNetServerConstants::GetServerTypeStr(fServerType); + spacer = kComma; + } + if (HasServerName()) + { + ss << spacer + << "N:" + << SAFE(fServerName.c_str()); + spacer = kComma; + } + if (HasServerGuid()) + { + ss << spacer + << "G:" + << fServerGuid.AsString(); + spacer = kComma; + } + if (HasServerAddr() || HasServerPort()) + { + ss << spacer + << "A:[" + << SAFE(fServerAddr.c_str()) + << ":" + << fServerPort + << "]"; + spacer = kComma; + } + ss << "]"; + + return ss.str().c_str(); +} + +std::string plNetServerSessionInfo::AsLogString() const +{ + const char* spacer = kSemicolon; + + std::stringstream ss; + std::string typeName = ""; + + if (HasServerType()) + { + typeName = plNetServerConstants::GetServerTypeStr(fServerType); + } + + if (HasServerName()) + { + ss << typeName << "Name" << "="; + ss << fServerName.c_str(); + ss << spacer; + } + + if (HasServerAddr()) + { + ss << typeName << "Addr" << "="; + ss << fServerAddr.c_str(); + ss << spacer; + } + + if (HasServerPort()) + { + ss << typeName << "Port" << "="; + ss << fServerPort; + ss << spacer; + } + + if (HasServerGuid()) + { + ss << typeName << "Guid" << "="; + ss << fServerGuid.AsString(); + ss << spacer; + } + + return ss.str().c_str(); +} + +bool plNetServerSessionInfo::IsEqualTo(const plNetServerSessionInfo * other) const +{ + bool match = true; + if (match && IsFlagSet(kHasServerGuid) && other->IsFlagSet(kHasServerGuid)) + match = match && fServerGuid.IsEqualTo(other->GetServerGuid()); + if (match && IsFlagSet(kHasServerName) && other->IsFlagSet(kHasServerName)) + match = match && (_stricmp(fServerName.c_str(),other->fServerName.c_str())==0); + if (match && IsFlagSet(kHasServerType) && other->IsFlagSet(kHasServerType)) + match = match && fServerType==other->fServerType; + if (match && IsFlagSet(kHasServerAddr) && other->IsFlagSet(kHasServerAddr)) + match = match && (_stricmp(fServerAddr.c_str(),other->fServerAddr.c_str())==0); + if (match && IsFlagSet(kHasServerPort) && other->IsFlagSet(kHasServerPort)) + match = match && fServerPort==other->fServerPort; + return match; +} + + +void plNetServerSessionInfo::SetServerName(const char * val) +{ + if (val) + { + fServerName=val; + SetFlag(kHasServerName); + } + else + { + fServerName=""; + ClearFlag(kHasServerName); + } +} + +void plNetServerSessionInfo::SetServerType(UInt8 val) +{ + if (val>0) + { + fServerType=val; + SetFlag(kHasServerType); + } + else + { + fServerType=0; + ClearFlag(kHasServerType); + } +} + +void plNetServerSessionInfo::SetServerAddr(const char * val) +{ + if (val) + { + fServerAddr = val; + SetFlag(kHasServerAddr); + } + else + { + fServerAddr = ""; + ClearFlag(kHasServerAddr); + } +} + +void plNetServerSessionInfo::SetServerPort(UInt16 val) +{ + if (val>0) + { + fServerPort=val; + SetFlag(kHasServerPort); + } + else + { + fServerPort=0; + ClearFlag(kHasServerPort); + } +} + +void plNetServerSessionInfo::SetServerGuid(const plUUID * val) +{ + if (val && val->IsSet()) + { + fServerGuid.CopyFrom(val); + SetFlag(kHasServerGuid); + } + else + { + fServerGuid.Clear(); + ClearFlag(kHasServerGuid); + } +} + +void plNetServerSessionInfo::CopyServerGuid(const plUUID & val) +{ + if (val.IsSet()) + { + fServerGuid.CopyFrom(val); + SetFlag(kHasServerGuid); + } + else + { + fServerGuid.Clear(); + ClearFlag(kHasServerGuid); + } +} + + +/////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetServerSessionInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetServerSessionInfo.h new file mode 100644 index 00000000..e915070d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plNetServerSessionInfo.h @@ -0,0 +1,334 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetServerSessionInfo_h_inc +#define plNetServerSessionInfo_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "../pnFactory/plCreatable.h" +#include "../pnNetCommon/plNetServers.h" +#include "../plNetCommon/plSpawnPointInfo.h" +#include "../plUUID/plUUID.h" + + +class hsStream; +class hsResMgr; +class plVaultAgeInfoNode; +class plVaultAgeLinkNode; + +/////////////////////////////////////////////////////////////////// +// Holds info that describes an age + +class plAgeInfoStruct : public plCreatable +{ + mutable UInt8 fFlags; + + // Age dataset name "Neighborhood" + std::string fAgeFilename; + + // Age string ID "Bevin" + std::string fAgeInstanceName; + + // Age guid. Same as game server guid. + plUUID fAgeInstanceGuid; + + // User-defined age name: "My Teledahn" + std::string fAgeUserDefinedName; + + // User-defined age description "This is Joe's Neighborhood" + std::string fAgeDescription; + + // A modifier to user-defined name to make it unique in gui lists. + // Assigned by vault server. + Int32 fAgeSequenceNumber; + + // The language of the client that created this age + Int32 fAgeLanguage; + + mutable std::string fDisplayName; + + enum + { + kHasAgeFilename = 1<<0, + kHasAgeInstanceName = 1<<1, + kHasAgeInstanceGuid = 1<<2, + kHasAgeUserDefinedName = 1<<3, + kHasAgeSequenceNumber = 1<<4, + kHasAgeDescription = 1<<5, + kHasAgeLanguage = 1<<6, + }; + + void SetFlag( UInt8 bit, bool on=true ) const { (on)?fFlags|=bit:fFlags&=~bit;} + void ClearFlag( UInt8 bit ) { fFlags&=~bit;} + bool IsFlagSet( UInt8 bit ) const { return (fFlags&bit)!=0;} + +public: + plAgeInfoStruct() + : fFlags( 0 ) + , fAgeSequenceNumber( 0 ) + , fAgeLanguage( -1 ) + {} + + CLASSNAME_REGISTER( plAgeInfoStruct ); + GETINTERFACE_ANY( plAgeInfoStruct, plCreatable ); + + void Clear(); + void UpdateFlags() const; + void CopyFrom( const plAgeInfoStruct * other ) { *this=*other; } + void CopyFrom( const plVaultAgeInfoNode * node ); + void CopyFrom(const struct NetAgeInfo & info); + bool IsEqualTo( const plAgeInfoStruct * other ) const; + + const char * GetAgeFilename() const { return fAgeFilename.c_str(); } + const char * GetAgeInstanceName() const { return fAgeInstanceName.c_str(); } + const plUUID * GetAgeInstanceGuid() const { return &fAgeInstanceGuid; } + const char * GetAgeUserDefinedName() const { return fAgeUserDefinedName.c_str(); } + const char * GetAgeDescription() const { return fAgeDescription.c_str(); } + UInt32 GetAgeSequenceNumber() const { return fAgeSequenceNumber; } + UInt32 GetAgeLanguage() const { return fAgeLanguage; } + + void SetAgeFilename( const char * v ); + void SetAgeInstanceName( const char * v ); + void SetAgeInstanceGuid( const plUUID * v ); + void SetAgeUserDefinedName( const char * v ); + void SetAgeDescription( const char * v ); + void SetAgeSequenceNumber( UInt32 v ); + void SetAgeLanguage( UInt32 v ); + + bool HasAgeFilename() const { return IsFlagSet( kHasAgeFilename ); } + bool HasAgeInstanceName() const { return IsFlagSet( kHasAgeInstanceName ); } + bool HasAgeInstanceGuid() const { return IsFlagSet( kHasAgeInstanceGuid ) && fAgeInstanceGuid.IsSet(); } + bool HasAgeUserDefinedName() const { return IsFlagSet( kHasAgeUserDefinedName ); } + bool HasAgeDescription() const { return IsFlagSet( kHasAgeDescription ); } + bool HasAgeSequenceNumber() const { return IsFlagSet( kHasAgeSequenceNumber ); } + bool HasAgeLanguage() const { return IsFlagSet( kHasAgeLanguage ); } + + void Read( hsStream * s, hsResMgr* ); + void Write( hsStream * s, hsResMgr* ); + + const char * GetDisplayName() const; + + std::string AsStdString() const; +}; + +//////////////////////////////////////////////////////////////////// + +class plAgeLinkStruct : public plCreatable +{ + UInt16 fFlags; + + // Where we want to link. + plAgeInfoStruct fAgeInfo; + + // The linking rule to use. See plNetCommon::LinkingRules + Int8 fLinkingRules; + + // Where to spawn avatar when we load the age specified in fAgeInfo + plSpawnPointInfo fSpawnPoint; + + // Override PLS/MCP load balancing rules for CCRs. + UInt8 fAmCCR; + + // If this is a child age link, who is the parent + // ...Age dataset name like "Neighborhood" + std::string fParentAgeFilename; + + enum + { + kHasAgeInfo = 1<<0, + kHasLinkingRules = 1<<1, + kHasSpawnPt_DEAD = 1<<2, + kHasSpawnPt_DEAD2 = 1<<3, + kHasAmCCR = 1<<4, + kHasSpawnPt = 1<<5, + kHasParentAgeFilename = 1<<6, + }; + + void SetFlag( UInt16 bit, bool on=true ) { (on)?fFlags|=bit:fFlags&=~bit;} + void ClearFlag( UInt16 bit ) { fFlags&=~bit;} + bool IsFlagSet( UInt16 bit ) const { return (fFlags&bit)!=0;} + +public: + plAgeLinkStruct(); + CLASSNAME_REGISTER( plAgeLinkStruct ); + GETINTERFACE_ANY( plAgeLinkStruct, plCreatable ); + + plAgeInfoStruct * GetAgeInfo() { return &fAgeInfo; } + const plAgeInfoStruct * GetAgeInfo() const { return &fAgeInfo; } + + const char * GetParentAgeFilename() const { return fParentAgeFilename.c_str(); } + void SetParentAgeFilename( const char * v ); + + void CopyFrom( const plAgeLinkStruct * other ); + void CopyFrom( const plVaultAgeLinkNode * node ); + bool IsEqualTo( const plAgeLinkStruct * other ) const; + void Clear(); + + bool HasAgeInfo() const { return IsFlagSet( kHasAgeInfo ); } + bool HasLinkingRules() const { return IsFlagSet( kHasLinkingRules ); } + bool HasSpawnPt() const { return IsFlagSet( kHasSpawnPt ); } + bool HasAmCCR() const { return IsFlagSet( kHasAmCCR ); } + bool HasParentAgeFilename() const { return IsFlagSet( kHasParentAgeFilename ); } + + void SetLinkingRules( int v ) { SetFlag( kHasLinkingRules ); fLinkingRules=v; } + int GetLinkingRules() const { return fLinkingRules; } + void SetSpawnPoint( const plSpawnPointInfo & point ) { SetFlag( kHasSpawnPt ); fSpawnPoint=point; } + plSpawnPointInfo & SpawnPoint() { return fSpawnPoint; } + const plSpawnPointInfo & SpawnPoint() const { return fSpawnPoint; } + void SetAmCCR( bool v ) { SetFlag( kHasAmCCR ); fAmCCR=v?1:0; } + bool GetAmCCR() const { return fAmCCR!=0; } + + void Read( hsStream * s, hsResMgr* ); + void Write( hsStream * s, hsResMgr* ); + + std::string AsStdString() const; +}; + + +//////////////////////////////////////////////////////////////////// +// Holds info that describes a server session +// + +class plNetServerSessionInfo : public plCreatable +{ + UInt8 fFlags; + std::string fServerName; + UInt8 fServerType; + std::string fServerAddr; + UInt16 fServerPort; + plUUID fServerGuid; + + enum + { + kHasServerName = 1<<0, + kHasServerType = 1<<1, + kHasServerAddr = 1<<2, + kHasServerPort = 1<<3, + kHasServerGuid = 1<<4, + }; + + void SetFlag( UInt8 bit ) { fFlags|=bit;} + void ClearFlag( UInt8 bit ) { fFlags&=~bit;} + bool IsFlagSet( UInt8 bit ) const { return (fFlags&bit)!=0;} + +public: + plNetServerSessionInfo() + : fServerType(plNetServerConstants::kInvalidLo) + , fServerPort(0) + , fFlags(0) + {} + CLASSNAME_REGISTER( plNetServerSessionInfo ); + GETINTERFACE_ANY( plNetServerSessionInfo, plCreatable ); + + void SetServerName(const char * val); + void SetServerType(UInt8 val); + void SetServerAddr(const char * val); + void SetServerPort(UInt16 val); + void SetServerGuid(const plUUID * val); + void CopyServerGuid(const plUUID & val); + + const char * GetServerName() const { return fServerName.c_str();} + UInt8 GetServerType() const { return fServerType;} + const char * GetServerAddr() const { return fServerAddr.c_str(); } + UInt16 GetServerPort() const { return fServerPort; } + const plUUID *GetServerGuid() const { return &fServerGuid; } + plUUID * GetServerGuid() { return &fServerGuid; } + + bool HasServerName() const { return IsFlagSet(kHasServerName);} + bool HasServerType() const { return IsFlagSet(kHasServerType);} + bool HasServerAddr() const { return IsFlagSet(kHasServerAddr);} + bool HasServerPort() const { return IsFlagSet(kHasServerPort);} + bool HasServerGuid() const { return IsFlagSet(kHasServerGuid);} + bool IsFullyQualified() const + { + return + IsFlagSet(kHasServerName)&& + IsFlagSet(kHasServerType)&& + IsFlagSet(kHasServerAddr)&& + IsFlagSet(kHasServerPort)&& + IsFlagSet(kHasServerGuid); + } + + void Clear(); + void CopyFrom(const plNetServerSessionInfo * other); + bool IsEqualTo(const plNetServerSessionInfo * other) const; + virtual std::string AsStdString() const; + virtual std::string AsLogString() const; + + void Read(hsStream* s, hsResMgr* mgr=nil); + void Write(hsStream* s, hsResMgr* mgr=nil); + + // WriteVersion writes the current version of this creatable and ReadVersion will read in + // any previous version. + virtual void ReadVersion(hsStream* s, hsResMgr* mgr); + virtual void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +//////////////////////////////////////////////////////////////////// + +//class plVaultAgeInfoNode; +//class plAgeLinkingInfo : public plCreatable +//{ +// int fLinkingRules; +// UInt32 fPlayerID; +// hsBool8 fSuperUser; +// mutable plAgeInfoStruct fAgeInfo; +// mutable plNetServerSessionInfo fServerInfo; +// +//public: +// plAgeLinkingInfo(); +// +// CLASSNAME_REGISTER( plAgeLinkingInfo ); +// GETINTERFACE_ANY( plAgeLinkingInfo, plCreatable ); +// +// int GetLinkingRules( void ) const { return fLinkingRules;} +// void SetLinkingRules( int v ) { fLinkingRules=v;} +// UInt32 GetPlayerID( void ) const { return fPlayerID;} +// void SetPlayerID( UInt32 v ) { fPlayerID=v;} +// void SetSuperUser(bool b) { fSuperUser=b; } +// bool GetSuperUser() const { return fSuperUser ? true : false; } +// +// plAgeInfoStruct * GetAgeInfo(); +// const plAgeInfoStruct * GetAgeInfo() const; +// +// // initializes info with age name and guid for you. +// plNetServerSessionInfo * GetServerInfo(); +// const plNetServerSessionInfo * GetServerInfo() const; +// const plNetServerSessionInfo * AsServerInfo() const; +// +// void Clear( void ); +// void CopyFrom( const plAgeLinkingInfo * other ); +// void CopyFrom( const plVaultAgeInfoNode * node ); +// void CopyFrom( const plNetServerSessionInfo * info ); +// void CopyFrom( const plAgeInfoStruct * info ); +// +// void Read(hsStream* s, hsResMgr* mgr=nil); +// void Write(hsStream* s, hsResMgr* mgr=nil); +// +// std::string AsStdString() const; +//}; + +#endif // plNetServerSessionInfo_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plServerGuid.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plServerGuid.cpp new file mode 100644 index 00000000..e65b1d9f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plServerGuid.cpp @@ -0,0 +1,275 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsWindows.h" +#include "plServerGuid.h" +#include "../pnMessage/plMessage.h" +#include "../PubUtilLib/plStreamLogger/plStreamLogger.h" +#if HS_BUILD_FOR_WIN32 +#include +#else +#include +#include +#endif + +//////////////////////////////////////////////////////////////////// + +#ifdef HS_BUILD_FOR_WIN32 + +// Taken from plUnifiedTime, in turn taken from python source. +// TODO: Move this down to CoreLib someday (and rename it maybe). +#define MAGICWINDOWSOFFSET ((__int64)11644473600) +static UInt32 SecsSinceUNIXEpoch() +{ + FILETIME ft; + GetSystemTimeAsFileTime(&ft); /* 100 ns blocks since 01-Jan-1641 */ + __int64 ff,ffsecs; + ff = *(__int64*)(&ft); + ffsecs = ff/(__int64)10000000; + return (UInt32)(ffsecs-MAGICWINDOWSOFFSET); +} + +#else + +static UInt32 SecsSinceUNIXEpoch() +{ + struct timeval tv; + gettimeofday(&tv, nil); + return tv.tv_sec; +} + +#endif + + +//////////////////////////////////////////////////////////////////// + +UInt32 plServerGuid::fGuidSeed = 0; + +plServerGuid::plServerGuid() +{ + Clear(); +} + +plServerGuid::plServerGuid( const plServerGuid & other ) +{ + Clear(); + CopyFrom( other ); +} + +plServerGuid::plServerGuid(const char * s) +{ + Clear(); + FromString(s); +} + +plServerGuid::plServerGuid(const hsWide & v) +{ + Clear(); + FromWide( v ); +} + +plServerGuid& plServerGuid::operator=( const plServerGuid & rhs ) +{ + CopyFrom(rhs); + return *this; +} + + +bool operator==(const plServerGuid & X, const plServerGuid & Y) +{ + return memcmp(X.N,Y.N,plServerGuid::kGuidBytes)==0; +} +bool operator!=(const plServerGuid & X, const plServerGuid & Y) +{ + return memcmp(X.N,Y.N,plServerGuid::kGuidBytes)!=0; +} +bool operator<(const plServerGuid & X, const plServerGuid & Y) +{ + return memcmp(X.N,Y.N,plServerGuid::kGuidBytes)<0; +} + + +hsWide plServerGuid::AsWide() const +{ + return fWide; +} + +void plServerGuid::FromWide( const hsWide & v ) +{ + fWide = v; +} + + +bool plServerGuid::IsSet() const +{ + return N[0]||N[1]||N[2]||N[3]||N[4]||N[5]||N[6]||N[7]; +} + +bool plServerGuid::IsEqualTo(const plServerGuid * other) const +{ + return (*this)==(*other); +} + +const char * plServerGuid::AsString() const +{ + static char str[kGuidBytes*2+1]; + sprintf(str,"%02X%02X%02X%02X%02X%02X%02X%02X",N[0],N[1],N[2],N[3],N[4],N[5],N[6],N[7]); + return str; +} + +std::string plServerGuid::AsStdString( void ) const +{ + std::string str; + str.resize(kGuidBytes*2+1); + int n = sprintf(const_cast(str.data()),"%02X%02X%02X%02X%02X%02X%02X%02X",N[0],N[1],N[2],N[3],N[4],N[5],N[6],N[7]); + str.resize(n); + return str; +} + + +static unsigned char hexValues[] = +{ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + + +bool plServerGuid::FromString(const char * s) +{ + if (!s || (s && strlen(s)!=kGuidBytes*2 ) ) + { + Clear(); + return false; + } + N[0] = hexValues[s[0]]<<4; + N[0] += hexValues[s[1]]; + N[1] = hexValues[s[2]]<<4; + N[1] += hexValues[s[3]]; + N[2] = hexValues[s[4]]<<4; + N[2] += hexValues[s[5]]; + N[3] = hexValues[s[6]]<<4; + N[3] += hexValues[s[7]]; + N[4] = hexValues[s[8]]<<4; + N[4] += hexValues[s[9]]; + N[5] = hexValues[s[10]]<<4; + N[5] += hexValues[s[11]]; + N[6] = hexValues[s[12]]<<4; + N[6] += hexValues[s[13]]; + N[7] = hexValues[s[14]]<<4; + N[7] += hexValues[s[15]]; + return true; +} + + +void plServerGuid::Read(hsStream * s, hsResMgr*) +{ + s->LogSubStreamStart("push me"); + s->LogSubStreamPushDesc("plServerGuid"); + plMsgCArrayHelper::Peek(N,kGuidBytes,s); + s->LogSubStreamEnd(); +} + +void plServerGuid::Write(hsStream * s, hsResMgr*) +{ + plMsgCArrayHelper::Poke(N,kGuidBytes,s); +} + +void plServerGuid::CopyFrom(const plServerGuid & other) +{ + memcpy(N,other.N,kGuidBytes); +} + +void plServerGuid::CopyFrom(const plServerGuid * other) +{ + if(other) + memcpy(N,other->N,kGuidBytes); + else + Clear(); +} + +void plServerGuid::Clear() +{ + memset(N,0,kGuidBytes); +} + +void plServerGuid::SetGuidSeed(UInt32 seed) +{ + fGuidSeed = seed; +} + +plServerGuid plServerGuid::GenerateGuid() +{ +// | N[0] | N[1] | N[2] | N[3] | N[4] | N[5] | N[6] | N[7] | +// 43210987654321098765432109876543210987654321098765432109876543210 +// 01234567890123456789012345678901234567890123456789012345678901234 +// 64 48 32 16 0 +// | fGuidSeed | Current Time | Counter | +// +// fGuidSeed: 24 bits (settable. default is getpid()) +// Current Time: 24 bits (seconds. ~8.5 year cycle) +// Counter: 16 bits (always increasing per process) + + static UInt16 StaticCounter = 0; + if (!fGuidSeed) + { + hsStatusMessage( "fGuidSeed not set yet. Cannot generate a reliable guid! Setting fGuidSeed=getpid()." ); +// hsAssert(fGuidSeed,"fGuidSeed not set yet. Cannot generate a reliable guid.\nIgnoring this assert will set fGuidSeed=getpid()."); + fGuidSeed = getpid(); + } + + UInt32 currTime = SecsSinceUNIXEpoch(); + + plServerGuid guid; + guid.N[0] = (UInt8)((fGuidSeed & 0x00FF0000)>>16); + guid.N[1] = (UInt8)((fGuidSeed & 0x0000FF00)>> 8); + guid.N[2] = (UInt8)(fGuidSeed & 0x000000FF); + guid.N[3] = (UInt8)((currTime & 0x00FF0000)>>16); + guid.N[4] = (UInt8)((currTime & 0x0000FF00)>> 8); + guid.N[5] = (UInt8)(currTime & 0x000000FF); + guid.N[6] = (StaticCounter & 0xFF00)>> 8; + guid.N[7] = (StaticCounter & 0x00FF); + + StaticCounter++; + + return guid; +} + + +//////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plServerGuid.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plServerGuid.h new file mode 100644 index 00000000..c6e97b04 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plServerGuid.h @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plServerGuid_h_inc +#define plServerGuid_h_inc + +#include "hsConfig.h" +#include "hsWide.h" +#include "../pnFactory/plCreatable.h" +#include + +//////////////////////////////////////////////////////////////////// +// plServerGuid + +class plServerGuid : public plCreatable +{ +public: + enum { kGuidBytes = 8 }; + struct Match + { + const plServerGuid * fGuid; + Match( const plServerGuid * guid ):fGuid( guid ){} + bool operator()( const plServerGuid * guid ) const { return guid->IsEqualTo( fGuid );} + }; + + union + { + UInt8 N[kGuidBytes]; + hsWide fWide; + }; + plServerGuid(); + plServerGuid( const plServerGuid & other ); + explicit plServerGuid( const char * s ); + explicit plServerGuid( const hsWide & v ); + + + plServerGuid& operator=( const plServerGuid & rhs ); + friend bool operator==( const plServerGuid & X, const plServerGuid & Y ); + friend bool operator!=( const plServerGuid & X, const plServerGuid & Y ); + friend bool operator<( const plServerGuid & X, const plServerGuid & Y) ; + + const char * AsString( void ) const; // returns static buffer. + std::string AsStdString( void ) const; + bool FromString( const char * s ); + + hsWide AsWide() const; + void FromWide( const hsWide & v ); + + bool IsSet( void ) const; + bool IsEqualTo( const plServerGuid * other ) const; + operator std::string ( void ) const { return AsString();} + + void Read( hsStream * s, hsResMgr* mgr=nil ); + void Write( hsStream * s, hsResMgr* mgr=nil ); + void CopyFrom( const plServerGuid & other ); + void CopyFrom( const plServerGuid * other ); + void Clear(); + + static void SetGuidSeed( UInt32 seed ); + static bool GuidSeedIsSet( void ) { return fGuidSeed!=0;} + static plServerGuid GenerateGuid( void ); + + CLASSNAME_REGISTER( plServerGuid ); + GETINTERFACE_ANY( plServerGuid, plCreatable ); + +private: + static UInt32 fGuidSeed; // only low 24 bits are used +}; + + +#endif // plServerGuid_h_inc +//////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plSpawnPointInfo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plSpawnPointInfo.cpp new file mode 100644 index 00000000..92f7b984 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plSpawnPointInfo.cpp @@ -0,0 +1,113 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSpawnPointInfo.h" +#include "../pnMessage/plMessage.h" +#include "hsStream.h" +#include "hsBitVector.h" + + +const plSpawnPointInfo kDefaultSpawnPoint( kDefaultSpawnPtTitle, kDefaultSpawnPtName ); + + +namespace SpawnPointInfoStreamFlags +{ + enum + { + kHasTitle, + kHasName, + kHasCameraStack, + }; +} + +void plSpawnPointInfo::ReadOld( hsStream * s ) +{ + s->LogSubStreamStart("push me"); + s->LogSubStreamPushDesc("Title"); + plMsgStdStringHelper::Peek( fTitle, s ); + s->LogSubStreamPushDesc("Name"); + plMsgStdStringHelper::Peek( fSpawnPt, s ); + fCameraStack = ""; + s->LogSubStreamEnd(); +} + +void plSpawnPointInfo::Read( hsStream * s ) +{ + hsBitVector flags; + flags.Read( s ); + + s->LogSubStreamStart("push me"); + if ( flags.IsBitSet( SpawnPointInfoStreamFlags::kHasTitle ) ) + { + s->LogSubStreamPushDesc("Title"); + plMsgStdStringHelper::Peek( fTitle, s ); + } + if ( flags.IsBitSet( SpawnPointInfoStreamFlags::kHasName ) ) + { + s->LogSubStreamPushDesc("Name"); + plMsgStdStringHelper::Peek( fSpawnPt, s ); + } + if ( flags.IsBitSet( SpawnPointInfoStreamFlags::kHasCameraStack ) ) + { + s->LogSubStreamPushDesc("CameraStack"); + plMsgStdStringHelper::Peek( fCameraStack, s ); + } + s->LogSubStreamEnd(); +} + +void plSpawnPointInfo::Write( hsStream * s ) const +{ + hsBitVector flags; + flags.SetBit( SpawnPointInfoStreamFlags::kHasTitle ); + flags.SetBit( SpawnPointInfoStreamFlags::kHasName ); + flags.SetBit( SpawnPointInfoStreamFlags::kHasCameraStack ); + flags.Write( s ); + + if ( flags.IsBitSet( SpawnPointInfoStreamFlags::kHasTitle ) ) + { + plMsgStdStringHelper::Poke( fTitle, s ); + } + if ( flags.IsBitSet( SpawnPointInfoStreamFlags::kHasName ) ) + { + plMsgStdStringHelper::Poke( fSpawnPt, s ); + } + if ( flags.IsBitSet( SpawnPointInfoStreamFlags::kHasCameraStack ) ) + { + plMsgStdStringHelper::Poke( fCameraStack, s ); + } +} + +void plSpawnPointInfo::Reset() +{ + (*this)=kDefaultSpawnPoint; +} + +std::string plSpawnPointInfo::AsStdString() const +{ + return xtl::format( "t:%s,n:%s,c:%s", + fTitle.size()?fTitle.c_str():"(nil)", + fSpawnPt.size()?fSpawnPt.c_str():"(nil)", + fCameraStack.size()?fCameraStack.c_str():"(nil)" ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plSpawnPointInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plSpawnPointInfo.h new file mode 100644 index 00000000..a1262037 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetCommon/plSpawnPointInfo.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSpawnPointInfo_h_inc +#define plSpawnPointInfo_h_inc + +#include "hsConfig.h" +#include "hsStlUtils.h" + +/////////////////////////////////////////////////////////////////// + +#define kDefaultSpawnPtTitle "Default" +#define kDefaultSpawnPtName "LinkInPointDefault" + +/////////////////////////////////////////////////////////////////// + +class hsStream; + +struct plSpawnPointInfo +{ + std::string fTitle; // friendly title for GUIs + std::string fSpawnPt; // name of spawn point in dataset + std::string fCameraStack; + plSpawnPointInfo(){} + plSpawnPointInfo( const plSpawnPointInfo & other ) { (*this)=other; } + plSpawnPointInfo( const char * title, const char * spawnPt ) + : fTitle( title ), fSpawnPt( spawnPt ) {} + const char * GetTitle() const { return fTitle.c_str(); } + void SetTitle( const char * v ) { fTitle=v; } + const char * GetName() const { return fSpawnPt.c_str(); } + void SetName( const char * v ) { fSpawnPt = v; } + const char * GetCameraStack() const { return fCameraStack.c_str(); } + void SetCameraStack( const char * v ) { fCameraStack=v; } + void Reset(); + void Read( hsStream * s ); + void ReadOld( hsStream * s ); + void Write( hsStream * s ) const; + std::string AsStdString() const; +}; +typedef std::vector plSpawnPointVec; + +extern const plSpawnPointInfo kDefaultSpawnPoint; + + + +#endif // plSpawnPointInfo_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Intern.h new file mode 100644 index 00000000..f4807e18 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Intern.h @@ -0,0 +1,407 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_INTERN_H + + +namespace Ngl { + + +/***************************************************************************** +* +* Module constants +* +***/ + +const unsigned kMaxReconnectIntervalMs = 5 * 1000; +const unsigned kMaxImmediateDisconnects = 5; +const unsigned kMaxFailedConnects = 5; +const unsigned kPingIntervalMs = 30 * 1000; +const unsigned kPingTimeoutMs = kPingIntervalMs * 10; +const unsigned kDisconnectedTimeoutMs = kPingIntervalMs; + + +/***************************************************************************** +* +* Core +* +***/ + +void ReportNetError (ENetProtocol protocol, ENetError error); + + +/***************************************************************************** +* +* Auth +* +***/ + +void AuthInitialize (); +void AuthDestroy (bool wait); + +bool AuthQueryConnected (); +unsigned AuthGetConnId (); +void AuthPingEnable (bool enable); + + +/***************************************************************************** +* +* Game +* +***/ + +void GameInitialize (); +void GameDestroy (bool wait); + +bool GameQueryConnected (); +unsigned GameGetConnId (); +void GamePingEnable (bool enable); + + +/***************************************************************************** +* +* File +* +***/ + +void FileInitialize (); +void FileDestroy (bool wait); + +bool FileQueryConnected (); +unsigned FileGetConnId (); + + +/***************************************************************************** +* +* Csr +* +***/ + +void CsrInitialize (); +void CsrDestroy (bool wait); + +bool CsrQueryConnected (); +unsigned CsrGetConnId (); + + +/***************************************************************************** +* +* GateKeeper +* +***/ + +void GateKeeperInitialize(); +void GateKeeperDestroy(bool wait); +bool GateKeeperQueryConnected (); +unsigned GateKeeperGetConnId(); + + +/***************************************************************************** +* +* Transactions +* +***/ + +enum ETransType { + //======================================================================== + // NglAuth.cpp transactions + kPingRequestTrans, + kLoginRequestTrans, + kAgeRequestTrans, + kAccountCreateRequestTrans, + kAccountCreateFromKeyRequestTrans, + kPlayerCreateRequestTrans, + kPlayerDeleteRequestTrans, + kUpgradeVisitorRequestTrans, + kSetPlayerRequestTrans, + kAccountChangePasswordRequestTrans, + kGetPublicAgeListTrans, + kAccountSetRolesRequestTrans, + kAccountSetBillingTypeRequestTrans, + kAccountActivateRequestTrans, + kFileListRequestTrans, + kFileDownloadRequestTrans, + kRcvdFileDownloadChunkTrans, + kRcvdPropagatedBufferTrans, + kVaultNodeChangedTrans, + kVaultNodeAddedTrans, + kVaultNodeRemovedTrans, + kVaultNodeDeletedTrans, + kVaultFetchNodeRefsTrans, + kVaultInitAgeTrans, + kVaultFetchNodeTrans, + kVaultFindNodeTrans, + kVaultCreateNodeTrans, + kVaultSaveNodeTrans, + kVaultAddNodeTrans, + kVaultRemoveNodeTrans, + kNotifyNewBuildTrans, + kSetPlayerBanStatusRequestTrans, + kChangePlayerNameRequestTrans, + kAuthConnectedNotifyTrans, + kScoreCreateTrans, + kScoreDeleteTrans, + kScoreGetScoresTrans, + kScoreAddPointsTrans, + kScoreTransferPointsTrans, + kScoreSetPointsTrans, + kScoreGetRanksTrans, + kSendFriendInviteTrans, + + //======================================================================== + // NglGame.cpp transactions + kJoinAgeRequestTrans, + kGmRcvdPropagatedBufferTrans, + kGmRcvdGameMgrMsgTrans, + + //======================================================================== + // NglFile.cpp transactions + kBuildIdRequestTrans, + kManifestRequestTrans, + kDownloadRequestTrans, + kFileRcvdFileDownloadChunkTrans, + + //======================================================================== + // NglCsr.cpp transactions + kCsrConnectedNotifyTrans, + kCsrLoginTrans, + + //======================================================================== + // NglCore.cpp transactions + kReportNetErrorTrans, + + //======================================================================== + // NglGateKeeper.cpp transactions + kGkFileSrvIpAddressRequestTrans, + kGkAuthSrvIpAddressRequestTrans, + + kNumTransTypes +}; + +static char * s_transTypes[] = { + // NglAuth.cpp + "PingRequestTrans", + "LoginRequestTrans", + "AgeRequestTrans", + "AccountCreateRequestTrans", + "AccountCreateFromKeyRequestTrans", + "PlayerCreateRequestTrans", + "PlayerDeleteRequestTrans", + "UpgradeVisitorRequestTrans", + "SetPlayerRequestTrans", + "AccountChangePasswordRequestTrans", + "GetPublicAgeListTrans", + "AccountSetRolesRequestTrans", + "AccountSetBillingTypeRequestTrans", + "AccountActivateRequestTrans", + "FileListRequestTrans", + "FileDownloadRequestTrans", + "RcvdFileDownloadChunkTrans", + "RcvdPropagatedBufferTrans", + "VaultNodeChangedTrans", + "VaultNodeAddedTrans", + "VaultNodeRemovedTrans", + "VaultNodeDeletedTrans", + "VaultFetchNodeRefsTrans", + "VaultInitAgeTrans", + "VaultFetchNodeTrans", + "VaultFindNodeTrans", + "VaultCreateNodeTrans", + "VaultSaveNodeTrans", + "VaultAddNodeTrans", + "VaultRemoveNodeTrans", + "NotifyNewBuildTrans", + "SetPlayerBanStatusfRequestTrans", + "ChangePlayerNameRequestTrans", + "AuthConnectedNotifyTrans", + "ScoreCreateTrans", + "ScoreDeleteTrans", + "ScoreGetScoresTrans", + "ScoreAddPointsTrans", + "ScoreTransferPointsTrans", + "ScoreSetPointsTrans", + "ScoreGetRanksTrans", + "SendFriendInviteTrans", + + // NglGame.cpp + "JoinAgeRequestTrans", + "GmRcvdPropagatedBufferTrans", + "GmRcvdGameMgrMsgTrans", + + // NglFile.cpp + "BuildIdRequestTrans", + "ManifestRequestTrans", + "DownloadRequestTrans", + "FileRcvdFileDownloadChunkTrans", + + // NglCsr.cpp + "CsrConnectedNotifyTrans", + "CsrLoginTrans", + + // NglCore.cpp + "ReportNetErrorTrans", + + // NglGateKeeper.cpp + "GkFileSrvIpAddress", + "GkAuthSrvIpAddress", + +}; +COMPILER_ASSERT(arrsize(s_transTypes) == kNumTransTypes); + +static long s_perfTransCount[kNumTransTypes]; + + +namespace Auth { struct CliAuConn; } +namespace Game { struct CliGmConn; } +namespace Csr { struct CliCsConn; } +namespace File { struct CliFileConn; } +namespace GateKeeper { struct CliGkConn; } + +enum ENetTransState { + kTransStateWaitServerConnect, + kTransStateWaitServerResponse, + kTransStateComplete, +}; + +struct NetTrans : AtomicRef { + LINK(NetTrans) m_link; + ENetTransState m_state; + ENetError m_result; + unsigned m_transId; + unsigned m_connId; + ENetProtocol m_protocol; + bool m_hasSubTrans; // set to specify this transaction should be completed after all others in the frame + unsigned m_timeoutAtMs; // curTime + s_timeoutMs (set upon send) + ETransType m_transType; + + NetTrans (ENetProtocol protocol, ETransType transType); + virtual ~NetTrans (); + + virtual bool CanStart () const; + virtual bool TimedOut () { return true; } // return true if we really did time out, false to reset the timeout timer + virtual bool Send () = 0; + virtual void Post () = 0; + virtual bool Recv ( // return false to disconnect from server + const byte msg[], + unsigned bytes + ) = 0; +}; + +struct NetAuthTrans : NetTrans { + Auth::CliAuConn * m_conn; + + NetAuthTrans (ETransType transType); + ~NetAuthTrans (); + + bool AcquireConn (); + void ReleaseConn (); +}; + +struct NetGameTrans : NetTrans { + Game::CliGmConn * m_conn; + + NetGameTrans (ETransType transType); + ~NetGameTrans (); + + bool AcquireConn (); + void ReleaseConn (); +}; + +struct NetFileTrans : NetTrans { + File::CliFileConn * m_conn; + + NetFileTrans (ETransType transType); + ~NetFileTrans (); + + bool AcquireConn (); + void ReleaseConn (); +}; + +struct NetCsrTrans : NetTrans { + Csr::CliCsConn * m_conn; + + NetCsrTrans (ETransType transType); + ~NetCsrTrans (); + + bool AcquireConn (); + void ReleaseConn (); +}; + + +struct NetGateKeeperTrans : NetTrans { + GateKeeper::CliGkConn * m_conn; + + NetGateKeeperTrans (ETransType transType); + ~NetGateKeeperTrans (); + + bool AcquireConn (); + void ReleaseConn (); +}; + + +struct NetNotifyTrans : NetTrans { + NetNotifyTrans (ETransType transType); + bool CanStart () const { return true; } + bool Send () { m_state = kTransStateComplete; return true; } + bool Recv ( + const byte [], + unsigned + ) { return true; } +}; + +void NetTransInitialize (); +void NetTransDestroy (bool wait); +void NetTransSetTimeoutMs (unsigned ms); +unsigned NetTransGetTimeoutMs (); + +void NetTransSend (NetTrans * trans); +bool NetTransRecv (unsigned transId, const byte msg[], unsigned bytes); +void NetTransCancel (unsigned transId, ENetError error); +void NetTransCancelByProtocol (ENetProtocol protocol, ENetError error); +void NetTransCancelByConnId (unsigned connId, ENetError error); +void NetTransCancelAll (ENetError error); +void NetTransUpdate (); + + +/***************************************************************************** +* +* Misc +* +***/ + +unsigned ConnNextSequence (); +unsigned ConnGetId (ENetProtocol protocol); + + +} using namespace Ngl; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Pch.h new file mode 100644 index 00000000..6b4f0a7f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Pch.h @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PCH_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PCH_H + +#include "pnUtils/pnUtils.h" +#include "pnNetBase/pnNetBase.h" +#include "pnAsyncCore/pnAsyncCore.h" +#include "pnNetCli/pnNetCli.h" +#include "pnProduct/pnProduct.h" + +#define USES_PROTOCOL_CLI2AUTH +#define USES_PROTOCOL_CLI2GAME +#define USES_PROTOCOL_CLI2FILE +#define USES_PROTOCOL_CLI2CSR +#define USES_PROTOCOL_CLI2GATEKEEPER +#include "pnNetProtocol/pnNetProtocol.h" + +#include "Private/plNglAllIncludes.h" +#include "Intern.h" + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAllIncludes.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAllIncludes.h new file mode 100644 index 00000000..846e2a5e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAllIncludes.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAllIncludes.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLALLINCLUDES_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAllIncludes.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLALLINCLUDES_H + + +// libs on which we rely +#include "../pnGameMgr/pnGameMgr.h" + +#include "plNglCore.h" +#include "plNglAuth.h" +#include "plNglGame.h" +#include "plNglFile.h" +#include "plNglCsr.h" +#include "plNglGateKeeper.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAuth.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAuth.cpp new file mode 100644 index 00000000..93e90c41 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAuth.cpp @@ -0,0 +1,6106 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAuth.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + +// This file excluded from pre-compiled header because it is auto-generated by the build server. +#include "pnNetBase/pnNbAuthKey.hpp" + +namespace Ngl { namespace Auth { +/***************************************************************************** +* +* Private +* +***/ + +struct CliAuConn : AtomicRef { + CliAuConn (); + ~CliAuConn (); + + // Reconnection + AsyncTimer * reconnectTimer; + unsigned reconnectStartMs; + + // Ping + AsyncTimer * pingTimer; + unsigned pingSendTimeMs; + unsigned lastHeardTimeMs; + + // This function should be called during object construction + // to initiate connection attempts to the remote host whenever + // the socket is disconnected. + void AutoReconnect (); + bool AutoReconnectEnabled (); + void StopAutoReconnect (); // call before destruction + void StartAutoReconnect (); + void TimerReconnect (); + + // ping + void AutoPing (); + void StopAutoPing (); + void TimerPing (); + + void Send (const unsigned_ptr fields[], unsigned count); + + CCritSect critsect; + LINK(CliAuConn) link; + AsyncSocket sock; + NetCli * cli; + wchar name[MAX_PATH]; + NetAddress addr; + Uuid token; + unsigned seq; + unsigned serverChallenge; + AsyncCancelId cancelId; + bool abandoned; +}; + +//============================================================================ +// PingRequestTrans +//============================================================================ +struct PingRequestTrans : NetAuthTrans { + FNetCliAuthPingRequestCallback m_callback; + void * m_param; + unsigned m_pingAtMs; + unsigned m_replyAtMs; + ARRAY(byte) m_payload; + + PingRequestTrans ( + FNetCliAuthPingRequestCallback callback, + void * param, + unsigned pingAtMs, + unsigned payloadBytes, + const void * payload + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// AccountExistsRequestTrans +//============================================================================ +struct AccountExistsRequestTrans : NetAuthTrans { + FNetCliAuthAccountExistsRequestCallback m_callback; + void * m_param; + + // send + wchar m_accountName[kMaxAccountNameLength]; + + // recv + byte m_exists; + + + + AccountExistsRequestTrans ( + FNetCliAuthAccountExistsRequestCallback callback, + void * param, + const wchar accountName[] + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// LoginRequestTrans +//============================================================================ +struct LoginRequestTrans : NetAuthTrans { + FNetCliAuthLoginRequestCallback m_callback; + void * m_param; + + Uuid m_accountId; + unsigned m_accountFlags; + unsigned m_billingType; + unsigned m_playerCount; + NetCliAuthPlayerInfo m_players[kMaxPlayersPerAccount]; + + LoginRequestTrans ( + FNetCliAuthLoginRequestCallback callback, + void * param + ); + + void AddPlayer ( + unsigned playerInt, + const wchar playerName[], + const wchar avatarShape[], + unsigned explorer + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// AgeRequestTrans +//============================================================================ +struct AgeRequestTrans : NetAuthTrans { + FNetCliAuthAgeRequestCallback m_callback; + void * m_param; + wchar m_ageName[kMaxAgeNameLength]; + unsigned m_ageMcpId; + Uuid m_ageInstId; + unsigned m_ageVaultId; + NetAddressNode m_gameSrvNode; + + AgeRequestTrans ( + const wchar ageName[], + const Uuid & ageInstId, + FNetCliAuthAgeRequestCallback callback, + void * param + ); + ~AgeRequestTrans (); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// AccountCreateRequestTrans +//============================================================================ +struct AccountCreateRequestTrans : NetAuthTrans { + FNetCliAuthAccountCreateRequestCallback m_callback; + void * m_param; + + // send + wchar m_accountName[kMaxAccountNameLength]; + ShaDigest m_namePassHash; + unsigned m_accountFlags; + unsigned m_billingType; + + // recv + Uuid m_accountId; + + AccountCreateRequestTrans ( + const wchar accountName[], + const wchar password[], + unsigned accountFlags, + unsigned billingType, + FNetCliAuthAccountCreateRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// AccountCreateFromKeyRequestTrans +//============================================================================ +struct AccountCreateFromKeyRequestTrans : NetAuthTrans { + FNetCliAuthAccountCreateFromKeyRequestCallback m_callback; + void * m_param; + + // send + wchar m_accountName[kMaxAccountNameLength]; + ShaDigest m_namePassHash; + Uuid m_key; + unsigned m_billingType; + + // recv + Uuid m_accountId; + Uuid m_activationKey; + + AccountCreateFromKeyRequestTrans ( + const wchar accountName[], + const wchar password[], + const Uuid & key, + unsigned billingType, + FNetCliAuthAccountCreateFromKeyRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// PlayerCreateRequestTrans +//============================================================================ +struct PlayerCreateRequestTrans : NetAuthTrans { + FNetCliAuthPlayerCreateRequestCallback m_callback; + void * m_param; + + // send + wchar m_playerName[kMaxPlayerNameLength]; + wchar m_avatarShape[MAX_PATH]; + wchar m_friendInvite[MAX_PATH]; + + // recv + NetCliAuthPlayerInfo m_playerInfo; + + + PlayerCreateRequestTrans ( + const wchar playerName[], + const wchar avatarShape[], + const wchar friendInvite[], + FNetCliAuthPlayerCreateRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// PlayerDeleteRequestTrans +//============================================================================ +struct PlayerDeleteRequestTrans : NetAuthTrans { + FNetCliAuthPlayerDeleteRequestCallback m_callback; + void * m_param; + + // send + unsigned m_playerId; + + // recv + NetCliAuthPlayerInfo m_playerInfo; + + + PlayerDeleteRequestTrans ( + unsigned playerId, + FNetCliAuthPlayerDeleteRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// UpgradeVisitorRequestTrans +//============================================================================ +struct UpgradeVisitorRequestTrans : NetAuthTrans { + FNetCliAuthUpgradeVisitorRequestCallback m_callback; + void * m_param; + + // send + unsigned m_playerId; + + UpgradeVisitorRequestTrans ( + unsigned playerId, + FNetCliAuthUpgradeVisitorRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// SetPlayerRequestTrans +//============================================================================ +struct SetPlayerRequestTrans : NetAuthTrans { + FNetCliAuthSetPlayerRequestCallback m_callback; + void * m_param; + unsigned m_playerInt; + + SetPlayerRequestTrans ( + unsigned playerInt, + FNetCliAuthSetPlayerRequestCallback callback, + void * param + ); + + // This transaction doesn't timeout since a client starting from a clean + // directory can take a long time between issuing this transaction and + // receiving a reply. + bool TimedOut () { return false; } + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// AccountChangePasswordRequestTrans +//============================================================================ +struct AccountChangePasswordRequestTrans : NetAuthTrans { + FNetCliAuthAccountChangePasswordRequestCallback m_callback; + void * m_param; + + // send + wchar m_accountName[kMaxAccountNameLength]; + ShaDigest m_namePassHash; + + AccountChangePasswordRequestTrans ( + const wchar accountName[], + const wchar password[], + FNetCliAuthAccountChangePasswordRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// GetPublicAgeListTrans +//============================================================================ +struct GetPublicAgeListTrans : NetAuthTrans { + FNetCliAuthGetPublicAgeListCallback m_callback; + void * m_param; + + // send + wchar m_ageName[MAX_PATH]; + + // recv + ARRAY(NetAgeInfo) m_ages; + + GetPublicAgeListTrans ( + const wchar ageName[], + FNetCliAuthGetPublicAgeListCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// AccountSetRolesRequestTrans +//============================================================================ +struct AccountSetRolesRequestTrans : NetAuthTrans { + FNetCliAuthAccountSetRolesRequestCallback m_callback; + void * m_param; + + // send + wchar m_accountName[kMaxAccountNameLength]; + unsigned m_accountFlags; + + AccountSetRolesRequestTrans ( + const wchar accountName[], + unsigned accountFlags, + FNetCliAuthAccountSetRolesRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// AccountSetBillingTypeRequestTrans +//============================================================================ +struct AccountSetBillingTypeRequestTrans : NetAuthTrans { + FNetCliAuthAccountSetBillingTypeRequestCallback m_callback; + void * m_param; + + // send + wchar m_accountName[kMaxAccountNameLength]; + unsigned m_billingType; + + AccountSetBillingTypeRequestTrans ( + const wchar accountName[], + unsigned billingType, + FNetCliAuthAccountSetBillingTypeRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// AccountActivateRequestTrans +//============================================================================ +struct AccountActivateRequestTrans : NetAuthTrans { + FNetCliAuthAccountActivateRequestCallback m_callback; + void * m_param; + + // send + Uuid m_activationKey; + + AccountActivateRequestTrans ( + const Uuid & activationKey, + FNetCliAuthAccountActivateRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// FileListRequestTrans +//============================================================================ +struct FileListRequestTrans : NetAuthTrans { + FNetCliAuthFileListRequestCallback m_callback; + void * m_param; + + wchar m_directory[MAX_PATH]; + wchar m_ext[MAX_EXT]; + + ARRAY(NetCliAuthFileInfo) m_fileInfoArray; + + FileListRequestTrans ( + FNetCliAuthFileListRequestCallback callback, + void * param, + const wchar directory[], + const wchar ext[] + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// FileDownloadRequestTrans +//============================================================================ +struct FileDownloadRequestTrans : NetAuthTrans { + FNetCliAuthFileRequestCallback m_callback; + void * m_param; + + wchar m_filename[MAX_PATH]; + hsStream * m_writer; + + FileDownloadRequestTrans ( + FNetCliAuthFileRequestCallback callback, + void * param, + const wchar filename[], + hsStream * writer + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// RcvdFileDownloadChunkTrans +//============================================================================ +struct RcvdFileDownloadChunkTrans : NetNotifyTrans { + + unsigned bytes; + unsigned offset; + byte * data; + hsStream * writer; + + RcvdFileDownloadChunkTrans () : NetNotifyTrans(kRcvdFileDownloadChunkTrans) {} + ~RcvdFileDownloadChunkTrans (); + void Post (); +}; + + +//============================================================================ +// RcvdPropagatedBufferTrans +//============================================================================ +struct RcvdPropagatedBufferTrans : NetNotifyTrans { + + unsigned bufferType; + unsigned bufferBytes; + byte * bufferData; + + RcvdPropagatedBufferTrans () : NetNotifyTrans(kRcvdPropagatedBufferTrans) {} + ~RcvdPropagatedBufferTrans (); + void Post (); +}; + +//============================================================================ +// VaultNodeChangedTrans +//============================================================================ +struct VaultNodeChangedTrans : NetNotifyTrans { + + unsigned m_nodeId; + Uuid m_revId; + + VaultNodeChangedTrans () : NetNotifyTrans(kVaultNodeChangedTrans) {} + void Post (); +}; + +//============================================================================ +// VaultNodeAddedTrans +//============================================================================ +struct VaultNodeAddedTrans : NetNotifyTrans { + + unsigned m_parentId; + unsigned m_childId; + unsigned m_ownerId; + + VaultNodeAddedTrans () : NetNotifyTrans(kVaultNodeAddedTrans) {} + void Post (); +}; + +//============================================================================ +// VaultNodeRemovedTrans +//============================================================================ +struct VaultNodeRemovedTrans : NetNotifyTrans { + + unsigned m_parentId; + unsigned m_childId; + + VaultNodeRemovedTrans () : NetNotifyTrans(kVaultNodeRemovedTrans) {} + void Post (); +}; + +//============================================================================ +// VaultNodeDeletedTrans +//============================================================================ +struct VaultNodeDeletedTrans : NetNotifyTrans { + + unsigned m_nodeId; + + VaultNodeDeletedTrans () : NetNotifyTrans(kVaultNodeDeletedTrans) {} + void Post (); +}; + +//============================================================================ +// VaultFetchNodeRefsTrans +//============================================================================ +struct VaultFetchNodeRefsTrans : NetAuthTrans { + + unsigned m_nodeId; + FNetCliAuthVaultNodeRefsFetched m_callback; + void * m_param; + + ARRAY(NetVaultNodeRef) m_refs; + + VaultFetchNodeRefsTrans ( + unsigned nodeId, + FNetCliAuthVaultNodeRefsFetched callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// VaultInitAgeTrans +//============================================================================ +struct VaultInitAgeTrans : NetAuthTrans { + FNetCliAuthAgeInitCallback m_callback; + void * m_param; + + Uuid m_ageInstId; + Uuid m_parentAgeInstId; + wchar * m_ageFilename; + wchar * m_ageInstName; + wchar * m_ageUserName; + wchar * m_ageDesc; + unsigned m_ageSequenceNumber; + unsigned m_ageLanguage; + + unsigned m_ageId; + unsigned m_ageInfoId; + + VaultInitAgeTrans ( + FNetCliAuthAgeInitCallback callback, // optional + void * param, // optional + const Uuid & ageInstId, // optional. is used in match + const Uuid & parentAgeInstId, // optional. is used in match + const wchar ageFilename[], // optional. is used in match + const wchar ageInstName[], // optional. not used in match + const wchar ageUserName[], // optional. not used in match + const wchar ageDesc[], // optional. not used in match + unsigned ageSequenceNumber, // optional. not used in match + unsigned ageLanguage // optional. not used in match + ); + ~VaultInitAgeTrans (); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// VaultFetchNodeTrans +//============================================================================ +struct VaultFetchNodeTrans : NetAuthTrans { + + unsigned m_nodeId; + FNetCliAuthVaultNodeFetched m_callback; + void * m_param; + + NetVaultNode * m_node; + + VaultFetchNodeTrans ( + unsigned nodeId, + FNetCliAuthVaultNodeFetched callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// VaultFindNodeTrans +//============================================================================ +struct VaultFindNodeTrans : NetAuthTrans { + + ARRAY(unsigned) m_nodeIds; + FNetCliAuthVaultNodeFind m_callback; + void * m_param; + + NetVaultNode * m_node; + + VaultFindNodeTrans ( + NetVaultNode * templateNode, + FNetCliAuthVaultNodeFind callback, + void * param + ); + ~VaultFindNodeTrans (); + + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// VaultCreateNodeTrans +//============================================================================ +struct VaultCreateNodeTrans : NetAuthTrans { + + NetVaultNode * m_templateNode; + FNetCliAuthVaultNodeCreated m_callback; + void * m_param; + + unsigned m_nodeId; + + VaultCreateNodeTrans ( + NetVaultNode * templateNode, + FNetCliAuthVaultNodeCreated callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// VaultSaveNodeTrans +//============================================================================ +struct VaultSaveNodeTrans : NetAuthTrans { + + unsigned m_nodeId; + Uuid m_revisionId; + ARRAY(byte) m_buffer; + FNetCliAuthVaultNodeSaveCallback m_callback; + void * m_param; + + VaultSaveNodeTrans ( + unsigned nodeId, + const Uuid & revisionId, + unsigned dataCount, + const void * data, + FNetCliAuthVaultNodeSaveCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// VaultAddNodeTrans +//============================================================================ +struct VaultAddNodeTrans : NetAuthTrans { + + unsigned m_parentId; + unsigned m_childId; + unsigned m_ownerId; + FNetCliAuthVaultNodeAddCallback m_callback; + void * m_param; + + VaultAddNodeTrans ( + unsigned parentId, + unsigned childId, + unsigned ownerId, + FNetCliAuthVaultNodeAddCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// VaultRemoveNodeTrans +//============================================================================ +struct VaultRemoveNodeTrans : NetAuthTrans { + + unsigned m_parentId; + unsigned m_childId; + FNetCliAuthVaultNodeRemoveCallback m_callback; + void * m_param; + + VaultRemoveNodeTrans ( + unsigned parentId, + unsigned childId, + FNetCliAuthVaultNodeRemoveCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// NotifyNewBuildTrans +//============================================================================ +struct NotifyNewBuildTrans : NetNotifyTrans { + + NotifyNewBuildTrans () : NetNotifyTrans(kNotifyNewBuildTrans) {} + void Post (); +}; + +//============================================================================ +// SetPlayerBanStatusRequestTrans +//============================================================================ +struct SetPlayerBanStatusRequestTrans : NetAuthTrans { + FNetCliAuthSetPlayerBanStatusRequestCallback m_callback; + void * m_param; + + // send + unsigned m_playerId; + unsigned m_banned; + + SetPlayerBanStatusRequestTrans ( + unsigned playerId, + unsigned banned, + FNetCliAuthSetPlayerBanStatusRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// ChangePlayerNameRequestTrans +//============================================================================ +struct ChangePlayerNameRequestTrans : NetAuthTrans { + FNetCliAuthChangePlayerNameRequestCallback m_callback; + void * m_param; + + // send + unsigned m_playerId; + wchar m_newName[kMaxPlayerNameLength]; + + ChangePlayerNameRequestTrans ( + unsigned playerId, + const wchar newName[], + FNetCliAuthChangePlayerNameRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// SendFriendInviteTrans +//============================================================================ +struct SendFriendInviteTrans : NetAuthTrans { + FNetCliAuthSendFriendInviteCallback m_callback; + void * m_param; + + // send + wchar m_emailAddress[kMaxEmailAddressLength]; + wchar m_toName[kMaxPlayerNameLength]; + Uuid m_inviteUuid; + + SendFriendInviteTrans ( + const wchar emailAddr[], + const wchar toName[], + const Uuid & inviteUuid, + FNetCliAuthSendFriendInviteCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// AuthConnectedNotifyTrans +//============================================================================ +struct AuthConnectedNotifyTrans : NetNotifyTrans { + + AuthConnectedNotifyTrans () : NetNotifyTrans(kAuthConnectedNotifyTrans) {} + void Post (); +}; + + +//============================================================================ +// ScoreCreateTrans +//============================================================================ +struct ScoreCreateTrans : NetAuthTrans { + FNetCliAuthCreateScoreCallback m_callback; + void * m_param; + + // send + unsigned m_ownerId; + char m_gameName[kMaxGameScoreNameLength]; + unsigned m_gameType; + int m_value; + + // recv + unsigned m_scoreId; + UInt32 m_createdTime; + + ScoreCreateTrans ( + unsigned ownerId, + const char* gameName, + unsigned gameType, + int value, + FNetCliAuthCreateScoreCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// ScoreDeleteTrans +//============================================================================ +struct ScoreDeleteTrans : NetAuthTrans { + FNetCliAuthScoreUpdateCallback m_callback; + void * m_param; + + // send + unsigned m_scoreId; + + ScoreDeleteTrans ( + unsigned scoreId, + FNetCliAuthScoreUpdateCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// ScoreGetScoresTrans +//============================================================================ +struct ScoreGetScoresTrans : NetAuthTrans { + FNetCliAuthGetScoresCallback m_callback; + void * m_param; + + // send + unsigned m_ownerId; + char m_gameName[kMaxGameScoreNameLength]; + + // recv + NetGameScore * m_scores; + unsigned m_scoreCount; + + ScoreGetScoresTrans ( + unsigned ownerId, + const char* gameName, + FNetCliAuthGetScoresCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// ScoreAddPointsTrans +//============================================================================ +struct ScoreAddPointsTrans : NetAuthTrans { + FNetCliAuthScoreUpdateCallback m_callback; + void * m_param; + + // send + unsigned m_scoreId; + int m_numPoints; + + ScoreAddPointsTrans ( + unsigned scoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// ScoreTransferPointsTrans +//============================================================================ +struct ScoreTransferPointsTrans : NetAuthTrans { + FNetCliAuthScoreUpdateCallback m_callback; + void * m_param; + + // send + unsigned m_srcScoreId; + unsigned m_destScoreId; + int m_numPoints; + + ScoreTransferPointsTrans ( + unsigned srcScoreId, + unsigned destScoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// ScoreSetPointsTrans +//============================================================================ +struct ScoreSetPointsTrans : NetAuthTrans { + FNetCliAuthScoreUpdateCallback m_callback; + void * m_param; + + // send + unsigned m_scoreId; + int m_numPoints; + + ScoreSetPointsTrans ( + unsigned scoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// ScoreGetRanksTrans +//============================================================================ +struct ScoreGetRanksTrans : NetAuthTrans { + FNetCliAuthGetRanksCallback m_callback; + void * m_param; + + // send + unsigned m_ownerId; + unsigned m_scoreGroup; + unsigned m_parentFolderId; + wchar m_gameName[kMaxGameScoreNameLength]; + unsigned m_timePeriod; + unsigned m_numResults; + unsigned m_pageNumber; + unsigned m_sortDesc; + + // recv + NetGameRank * m_ranks; + unsigned m_rankCount; + + ScoreGetRanksTrans ( + unsigned ownerId, + unsigned scoreGroup, + unsigned parentFolderId, + const char * cGameName, + unsigned timePeriod, + unsigned numResults, + unsigned pageNumber, + bool sortDesc, + FNetCliAuthGetRanksCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + + +/***************************************************************************** +* +* Private data +* +***/ + +enum { + kPerfConnCount, + kPingDisabled, + kAutoReconnectDisabled, + kNumPerf +}; + +static bool s_running; +static CCritSect s_critsect; +static LISTDECL(CliAuConn, link) s_conns; +static CliAuConn * s_active; +static wchar s_accountName[kMaxAccountNameLength]; +static ShaDigest s_accountNamePassHash; +static wchar s_authToken[kMaxPublisherAuthKeyLength]; +static wchar s_os[kMaxGTOSIdLength]; + +static long s_perf[kNumPerf]; + +static UInt32 s_encryptionKey[4]; + +static FNetCliAuthRecvBufferHandler s_bufRcvdCb; +static FNetCliAuthConnectCallback s_connectedCb; + +// Vault notification handlers +static FNetCliAuthVaultNodeChanged s_vaultNodeChangedHandler; +static FNetCliAuthVaultNodeAdded s_vaultNodeAddedHandler; +static FNetCliAuthVaultNodeRemoved s_vaultNodeRemovedHandler; +static FNetCliAuthVaultNodeDeleted s_vaultNodeDeletedHandler; + +static FNotifyNewBuildHandler s_notifyNewBuildHandler; + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +static inline bool ICharIsSpace (unsigned ch) { + return ch == ' '; +} + +//=========================================================================== +static ENetError FixupPlayerName (wchar * name) { + ASSERT(name); + + // Trim leading and trailing whitespace and convert + // multiple internal spaces into only one space + unsigned nonSpaceChars = 0; + for (wchar *src = name, *dst = name; *src; ) { + // Skip whitespace + while (*src && ICharIsSpace(*src)) + src++; + + // If the block skipped was not at the beginning + // of the string then add one space character + if (*src && (dst != name)) + *dst++ = ' '; + + // Copy characters until end-of-string or next whitespace + while (*src && !ICharIsSpace(*src)) { + ++nonSpaceChars; + *dst++ = *src++; + } + } + + // Ensure destination string is terminated + *dst = 0; + + // Check for minimum name length + if (nonSpaceChars < 3) + return kNetErrPlayerNameInvalid; + + return kNetSuccess; +} + +//=========================================================================== +static unsigned GetNonZeroTimeMs () { + if (unsigned ms = TimeGetMs()) + return ms; + return 1; +} + +//============================================================================ +static CliAuConn * GetConnIncRef_CS (const char tag[]) { + if (CliAuConn * conn = s_active) { + conn->IncRef(tag); + return conn; + } + return nil; +} + +//============================================================================ +static CliAuConn * GetConnIncRef (const char tag[]) { + CliAuConn * conn; + s_critsect.Enter(); + { + conn = GetConnIncRef_CS(tag); + } + s_critsect.Leave(); + return conn; +} + +//============================================================================ +static void UnlinkAndAbandonConn_CS (CliAuConn * conn) { + s_conns.Unlink(conn); + conn->abandoned = true; + + conn->StopAutoReconnect(); + + if (conn->cancelId) { + AsyncSocketConnectCancel(nil, conn->cancelId); + conn->cancelId = 0; + } + else if (conn->sock) { + AsyncSocketDisconnect(conn->sock, true); + } + else { + conn->DecRef("Lifetime"); + } +} + +//============================================================================ +static void SendClientRegisterRequest (CliAuConn * conn) { + const unsigned_ptr msg[] = { + kCli2Auth_ClientRegisterRequest, + BuildId(), + }; + + conn->Send(msg, arrsize(msg)); +} + +//============================================================================ +static bool ConnEncrypt (ENetError error, void * param) { + CliAuConn * conn = (CliAuConn *) param; + + if (IS_NET_SUCCESS(error)) { + + SendClientRegisterRequest(conn); + + if (!s_perf[kPingDisabled]) + conn->AutoPing(); + + AuthConnectedNotifyTrans * trans = NEW(AuthConnectedNotifyTrans); + NetTransSend(trans); + } + + return IS_NET_SUCCESS(error); +} + +//============================================================================ +static void NotifyConnSocketConnect (CliAuConn * conn) { + + conn->TransferRef("Connecting", "Connected"); + conn->cli = NetCliConnectAccept( + conn->sock, + kNetProtocolCli2Auth, + false, + ConnEncrypt, + 0, + nil, + conn + ); +} + +//============================================================================ +static void CheckedReconnect (CliAuConn * conn, ENetError error) { + + unsigned disconnectedMs = GetNonZeroTimeMs() - conn->lastHeardTimeMs; + + // no auto-reconnect or haven't heard from the server in a while? + if (!conn->AutoReconnectEnabled() || (int)disconnectedMs >= (int)kDisconnectedTimeoutMs) { + // Cancel all transactions in progress on this connection. + NetTransCancelByConnId(conn->seq, kNetErrTimeout); + // conn is dead. + conn->DecRef("Lifetime"); + ReportNetError(kNetProtocolCli2Auth, error); + } + else { + if (conn->token != kNilGuid) + // previously encrypted; reconnect quickly + conn->reconnectStartMs = GetNonZeroTimeMs() + 500; + else + // never encrypted; reconnect slowly + conn->reconnectStartMs = GetNonZeroTimeMs() + kMaxReconnectIntervalMs; + + // clean up the socket and start reconnect + if (conn->cli) + NetCliDelete(conn->cli, true); + conn->cli = nil; + conn->sock = nil; + + conn->StartAutoReconnect(); + } +} + +//============================================================================ +static void NotifyConnSocketConnectFailed (CliAuConn * conn) { + + s_critsect.Enter(); + { + conn->cancelId = 0; + s_conns.Unlink(conn); + + if (conn == s_active) + s_active = nil; + } + s_critsect.Leave(); + + CheckedReconnect(conn, kNetErrConnectFailed); + + conn->DecRef("Connecting"); +} + +//============================================================================ +static void NotifyConnSocketDisconnect (CliAuConn * conn) { + + conn->StopAutoPing(); + + s_critsect.Enter(); + { + conn->cancelId = 0; + s_conns.Unlink(conn); + + if (conn == s_active) + s_active = nil; + } + s_critsect.Leave(); + + + CheckedReconnect(conn, kNetErrDisconnected); + + conn->DecRef("Connected"); +} + +//============================================================================ +static bool NotifyConnSocketRead (CliAuConn * conn, AsyncNotifySocketRead * read) { + // TODO: Only dispatch messages from the active auth server + conn->lastHeardTimeMs = GetNonZeroTimeMs(); + bool result = NetCliDispatch(conn->cli, read->buffer, read->bytes, conn); + read->bytesProcessed += read->bytes; + return result; +} + +//============================================================================ +static bool SocketNotifyCallback ( + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +) { + bool result = true; + CliAuConn * conn; + + switch (code) { + case kNotifySocketConnectSuccess: + conn = (CliAuConn *) notify->param; + *userState = conn; + bool abandoned; + s_critsect.Enter(); + { + conn->sock = sock; + conn->cancelId = 0; + abandoned = conn->abandoned; + } + s_critsect.Leave(); + if (abandoned) + AsyncSocketDisconnect(sock, true); + else + NotifyConnSocketConnect(conn); + break; + + case kNotifySocketConnectFailed: + conn = (CliAuConn *) notify->param; + NotifyConnSocketConnectFailed(conn); + break; + + case kNotifySocketDisconnect: + conn = (CliAuConn *) *userState; + NotifyConnSocketDisconnect(conn); + break; + + case kNotifySocketRead: + conn = (CliAuConn *) *userState; + result = NotifyConnSocketRead(conn, (AsyncNotifySocketRead *) notify); + break; + } + + return result; +} + +//============================================================================ +static void Connect ( + CliAuConn * conn +) { + ASSERT(s_running); + + conn->pingSendTimeMs = 0; + + s_critsect.Enter(); + { + while (CliAuConn * oldConn = s_conns.Head()) { + if (oldConn != conn) + UnlinkAndAbandonConn_CS(oldConn); + else + s_conns.Unlink(oldConn); + } + s_conns.Link(conn); + } + s_critsect.Leave(); + + Cli2Auth_Connect connect; + connect.hdr.connType = kConnTypeCliToAuth; + connect.hdr.hdrBytes = sizeof(connect.hdr); + connect.hdr.buildId = BuildId(); + connect.hdr.buildType = BuildType(); + connect.hdr.branchId = BranchId(); + connect.hdr.productId = ProductId(); + connect.data.token = conn->token; + connect.data.dataBytes = sizeof(connect.data); + + AsyncSocketConnect( + &conn->cancelId, + conn->addr, + SocketNotifyCallback, + conn, + &connect, + sizeof(connect), + 0, + 0 + ); +} + +//============================================================================ +static void Connect ( + const wchar name[], + const NetAddress & addr +) { + ASSERT(s_running); + + CliAuConn * conn = NEWZERO(CliAuConn); + conn->addr = addr; + conn->seq = ConnNextSequence(); + conn->lastHeardTimeMs = GetNonZeroTimeMs(); // used in connect timeout, and ping timeout + StrCopy(conn->name, name, arrsize(conn->name)); + + conn->IncRef("Lifetime"); + conn->AutoReconnect(); +} + +//============================================================================ +static void AsyncLookupCallback ( + void * param, + const wchar name[], + unsigned addrCount, + const NetAddress addrs[] +) { + ref(param); + + if (!addrCount) { + ReportNetError(kNetProtocolCli2Auth, kNetErrNameLookupFailed); + return; + } + + for (unsigned i = 0; i < addrCount; ++i) { + Connect(name, addrs[i]); + } +} + + +/***************************************************************************** +* +* CliAuConn +* +***/ + +//=========================================================================== +static unsigned CliAuConnTimerDestroyed (void * param) { + CliAuConn * conn = (CliAuConn *) param; + conn->DecRef("TimerDestroyed"); + return kAsyncTimeInfinite; +} + +//=========================================================================== +static unsigned CliAuConnReconnectTimerProc (void * param) { + ((CliAuConn *) param)->TimerReconnect(); + return kAsyncTimeInfinite; +} + +//=========================================================================== +static unsigned CliAuConnPingTimerProc (void * param) { + ((CliAuConn *) param)->TimerPing(); + return kPingIntervalMs; +} + +//============================================================================ +CliAuConn::CliAuConn () { + AtomicAdd(&s_perf[kPerfConnCount], 1); +} + +//============================================================================ +CliAuConn::~CliAuConn () { + if (cli) + NetCliDelete(cli, true); + AtomicAdd(&s_perf[kPerfConnCount], -1); +} + +//=========================================================================== +void CliAuConn::TimerReconnect () { + ASSERT(!sock); + ASSERT(!cancelId); + + if (!s_running) { + s_critsect.Enter(); + UnlinkAndAbandonConn_CS(this); + s_critsect.Leave(); + } + else { + IncRef("Connecting"); + + // Remember the time we started the reconnect attempt, guarding against + // TimeGetMs() returning zero (unlikely), as a value of zero indicates + // a first-time connect condition to StartAutoReconnect() + reconnectStartMs = GetNonZeroTimeMs(); + + Connect(this); + } +} + +//=========================================================================== +// This function is called when after a disconnect to start a new connection +void CliAuConn::StartAutoReconnect () { + critsect.Enter(); + if (reconnectTimer && !s_perf[kAutoReconnectDisabled]) { + // Make reconnect attempts at regular intervals. If the last attempt + // took more than the specified max interval time then reconnect + // immediately; otherwise wait until the time interval is up again + // then reconnect. + unsigned remainingMs = 0; + if (reconnectStartMs) { + remainingMs = reconnectStartMs - GetNonZeroTimeMs(); + if ((signed)remainingMs < 0) + remainingMs = 0; + LogMsg(kLogPerf, L"Auth auto-reconnecting in %u ms", remainingMs); + } + AsyncTimerUpdate(reconnectTimer, remainingMs); + } + critsect.Leave(); +} + +//=========================================================================== +// This function should be called during object construction +// to initiate connection attempts to the remote host whenever +// the socket is disconnected. +void CliAuConn::AutoReconnect () { + + ASSERT(!reconnectTimer); + IncRef("ReconnectTimer"); + critsect.Enter(); + { + AsyncTimerCreate( + &reconnectTimer, + CliAuConnReconnectTimerProc, + 0, // immediate callback + this + ); + } + critsect.Leave(); +} + +//============================================================================ +void CliAuConn::StopAutoReconnect () { + critsect.Enter(); + { + if (AsyncTimer * timer = reconnectTimer) { + reconnectTimer = nil; + AsyncTimerDeleteCallback(timer, CliAuConnTimerDestroyed); + } + } + critsect.Leave(); +} + +//============================================================================ +bool CliAuConn::AutoReconnectEnabled () { + + return (reconnectTimer != nil) && !s_perf[kAutoReconnectDisabled]; +} + +//============================================================================ +void CliAuConn::AutoPing () { + ASSERT(!pingTimer); + IncRef("PingTimer"); + critsect.Enter(); + { + AsyncTimerCreate( + &pingTimer, + CliAuConnPingTimerProc, + sock ? 0 : kAsyncTimeInfinite, + this + ); + } + critsect.Leave(); +} + +//============================================================================ +void CliAuConn::StopAutoPing () { + critsect.Enter(); + { + if (AsyncTimer * timer = pingTimer) { + pingTimer = nil; + AsyncTimerDeleteCallback(timer, CliAuConnTimerDestroyed); + } + } + critsect.Leave(); +} + +//============================================================================ +void CliAuConn::TimerPing () { + +#if 0 + // if the time difference between when we last sent a ping and when we last + // heard from the server is >= 3x the ping interval, the socket is stale. + if (pingSendTimeMs && abs(int(pingSendTimeMs - lastHeardTimeMs)) >= kPingTimeoutMs) { + // ping timed out, disconnect the socket + AsyncSocketDisconnect(sock, true); + } + else +#endif + { + // Send a ping request + pingSendTimeMs = GetNonZeroTimeMs(); + + const unsigned_ptr msg[] = { + kCli2Auth_PingRequest, + pingSendTimeMs, + 0, // not a transaction + 0, // no payload + nil + }; + + Send(msg, arrsize(msg)); + } +} + +//============================================================================ +void CliAuConn::Send (const unsigned_ptr fields[], unsigned count) { + critsect.Enter(); + { + NetCliSend(cli, fields, count); + NetCliFlush(cli); + } + critsect.Leave(); +} + + +/***************************************************************************** +* +* Cli2Auth message handlers +* +***/ + +//============================================================================ +static bool Recv_PingReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_PingReply & reply = *(const Auth2Cli_PingReply *)msg; + + if (reply.transId) + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_AccountExistsReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_AccountExistsReply & reply = *(const Auth2Cli_AccountExistsReply *)msg; + + if (reply.transId) + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + + +//============================================================================ +static bool Recv_ClientRegisterReply ( + const byte msg[], + unsigned , + void * param +) { + const Auth2Cli_ClientRegisterReply & reply = *(const Auth2Cli_ClientRegisterReply *)msg; + + CliAuConn * conn = (CliAuConn *) param; + conn->serverChallenge = reply.serverChallenge; + + // Make this the active server + s_critsect.Enter(); + { + s_active = conn; + } + s_critsect.Leave(); + + return true; +} + +//============================================================================ +static bool Recv_AcctPlayerInfo ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_AcctPlayerInfo & reply = *(const Auth2Cli_AcctPlayerInfo *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_AcctLoginReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_AcctLoginReply & reply = *(const Auth2Cli_AcctLoginReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_AcctCreateReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_AcctCreateReply & reply = *(const Auth2Cli_AcctCreateReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_AcctCreateFromKeyReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_AcctCreateFromKeyReply & reply = *(const Auth2Cli_AcctCreateFromKeyReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_PlayerCreateReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_PlayerCreateReply & reply = *(const Auth2Cli_PlayerCreateReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_PlayerDeleteReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_PlayerDeleteReply & reply = *(const Auth2Cli_PlayerDeleteReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_UpgradeVisitorReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_UpgradeVisitorReply & reply = *(const Auth2Cli_UpgradeVisitorReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_AcctSetPlayerReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_AcctSetPlayerReply & reply = *(const Auth2Cli_AcctSetPlayerReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_AcctChangePasswordReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_AcctChangePasswordReply & reply = *(const Auth2Cli_AcctChangePasswordReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_AcctSetRolesReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_AcctSetRolesReply & reply = *(const Auth2Cli_AcctSetRolesReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_AcctSetBillingTypeReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_AcctSetBillingTypeReply & reply = *(const Auth2Cli_AcctSetBillingTypeReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_AcctActivateReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_AcctActivateReply & reply = *(const Auth2Cli_AcctActivateReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_AgeReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_AgeReply & reply = *(const Auth2Cli_AgeReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_FileListReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_FileListReply & reply = *(const Auth2Cli_FileListReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_FileDownloadChunk ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_FileDownloadChunk & reply = *(const Auth2Cli_FileDownloadChunk *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_KickedOff ( + const byte buffer[], + unsigned bytes, + void * param +) { + ref(bytes); + ref(param); + + const Auth2Cli_KickedOff & msg = *(const Auth2Cli_KickedOff *)buffer; + + ReportNetError(kNetProtocolCli2Auth, msg.reason); + NetCliAuthDisconnect(); + + return true; +} + +//============================================================================ +static bool Recv_VaultNodeRefsFetched ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_VaultNodeRefsFetched & reply = *(const Auth2Cli_VaultNodeRefsFetched *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_VaultNodeFetched ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_VaultNodeFetched & reply = *(const Auth2Cli_VaultNodeFetched *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_VaultNodeCreated ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_VaultNodeCreated & reply = *(const Auth2Cli_VaultNodeCreated *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_VaultNodeChanged ( + const byte msg[], + unsigned , + void * +) { + const Auth2Cli_VaultNodeChanged & notify = *(const Auth2Cli_VaultNodeChanged *)msg; + + VaultNodeChangedTrans * trans = NEW(VaultNodeChangedTrans); + trans->m_nodeId = notify.nodeId; + trans->m_revId = notify.revisionId; + NetTransSend(trans); + + return true; +} + +//============================================================================ +static bool Recv_VaultNodeAdded ( + const byte msg[], + unsigned , + void * +) { + const Auth2Cli_VaultNodeAdded & notify = *(const Auth2Cli_VaultNodeAdded *)msg; + + VaultNodeAddedTrans * trans = NEW(VaultNodeAddedTrans); + trans->m_parentId = notify.parentId; + trans->m_childId = notify.childId; + trans->m_ownerId = notify.ownerId; + NetTransSend(trans); + + return true; +} + +//============================================================================ +static bool Recv_VaultNodeRemoved ( + const byte msg[], + unsigned , + void * +) { + const Auth2Cli_VaultNodeRemoved & notify = *(const Auth2Cli_VaultNodeRemoved *)msg; + + VaultNodeRemovedTrans * trans = NEW(VaultNodeRemovedTrans); + trans->m_parentId = notify.parentId; + trans->m_childId = notify.childId; + NetTransSend(trans); + + return true; +} + +//============================================================================ +static bool Recv_VaultNodeDeleted ( + const byte msg[], + unsigned , + void * +) { + const Auth2Cli_VaultNodeDeleted & notify = *(const Auth2Cli_VaultNodeDeleted *)msg; + + VaultNodeDeletedTrans * trans = NEW(VaultNodeDeletedTrans); + trans->m_nodeId = notify.nodeId; + NetTransSend(trans); + + return true; +} + +//============================================================================ +static bool Recv_VaultSaveNodeReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_VaultSaveNodeReply & reply = *(const Auth2Cli_VaultSaveNodeReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_VaultAddNodeReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_VaultAddNodeReply & reply = *(const Auth2Cli_VaultAddNodeReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_VaultRemoveNodeReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_VaultRemoveNodeReply & reply = *(const Auth2Cli_VaultRemoveNodeReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_VaultInitAgeReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_VaultInitAgeReply & reply = *(const Auth2Cli_VaultInitAgeReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_VaultNodeFindReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_VaultNodeFindReply & reply = *(const Auth2Cli_VaultNodeFindReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_PublicAgeList ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_PublicAgeList & reply = *(const Auth2Cli_PublicAgeList *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_ServerAddr ( + const byte in[], + unsigned , + void * +) { + // the auth server has given us its actual address (behind any load-balancer) + // so that when we attempt a reconnect, we'll reconnect with our state on + // the auth (but only if we reconnect in a short period of time!) + const Auth2Cli_ServerAddr & msg = *(const Auth2Cli_ServerAddr *)in; + + s_critsect.Enter(); + { + if (s_active) { + s_active->token = msg.token; + NetAddressFromNode( + msg.srvAddr, + NetAddressGetPort(s_active->addr), + &s_active->addr + ); + wchar addrStr[64]; + NetAddressNodeToString( + msg.srvAddr, + addrStr, + arrsize(addrStr) + ); + LogMsg(kLogPerf, L"SrvAuth addr: %s", addrStr); + } + } + s_critsect.Leave(); + + return true; +} + +//============================================================================ +static bool Recv_NotifyNewBuild ( + const byte[], + unsigned , + void * +) { + NotifyNewBuildTrans * trans = NEW(NotifyNewBuildTrans); + NetTransSend(trans); + + return true; +} + +//============================================================================ +static bool Recv_SetPlayerBanStatusReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_SetPlayerBanStatusReply & reply = *(const Auth2Cli_SetPlayerBanStatusReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_ChangePlayerNameReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_ChangePlayerNameReply & reply = *(const Auth2Cli_ChangePlayerNameReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_SendFriendInviteReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_SendFriendInviteReply & reply = *(const Auth2Cli_SendFriendInviteReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_ScoreCreateReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_ScoreCreateReply & reply = *(const Auth2Cli_ScoreCreateReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_ScoreDeleteReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_ScoreDeleteReply & reply = *(const Auth2Cli_ScoreDeleteReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_ScoreGetScoresReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_ScoreGetScoresReply & reply = *(const Auth2Cli_ScoreGetScoresReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_ScoreAddPointsReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_ScoreAddPointsReply & reply = *(const Auth2Cli_ScoreAddPointsReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_ScoreTransferPointsReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_ScoreTransferPointsReply & reply = *(const Auth2Cli_ScoreTransferPointsReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_ScoreSetPointsReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_ScoreSetPointsReply & reply = *(const Auth2Cli_ScoreSetPointsReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_ScoreGetRanksReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Auth2Cli_ScoreGetRanksReply & reply = *(const Auth2Cli_ScoreGetRanksReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +/***************************************************************************** +* +* Cli2Auth protocol +* +***/ + +#define MSG(s) kNetMsg_Cli2Auth_##s +static NetMsgInitSend s_send[] = { + { MSG(PingRequest) }, + { MSG(ClientRegisterRequest) }, + { MSG(AcctLoginRequest) }, + { MSG(AcctCreateRequest) }, + { MSG(AcctCreateFromKeyRequest) }, + { MSG(PlayerCreateRequest) }, + { MSG(PlayerDeleteRequest) }, + { MSG(UpgradeVisitorRequest) }, + { MSG(AcctSetPlayerRequest) }, + { MSG(AcctChangePasswordRequest)}, + { MSG(AcctSetRolesRequest) }, + { MSG(AcctSetBillingTypeRequest)}, + { MSG(AcctActivateRequest) }, + { MSG(AgeRequest) }, + { MSG(FileListRequest) }, + { MSG(FileDownloadRequest) }, + { MSG(FileDownloadChunkAck) }, + { MSG(VaultFetchNodeRefs) }, + { MSG(VaultNodeAdd) }, + { MSG(VaultNodeRemove) }, + { MSG(VaultNodeFetch) }, + { MSG(VaultNodeCreate) }, + { MSG(VaultNodeSave) }, + { MSG(VaultInitAgeRequest) }, + { MSG(VaultNodeFind) }, + { MSG(VaultSetSeen) }, + { MSG(VaultSendNode) }, + { MSG(GetPublicAgeList) }, + { MSG(SetAgePublic) }, + { MSG(PropagateBuffer) }, + { MSG(ClientSetCCRLevel) }, + { MSG(LogPythonTraceback) }, + { MSG(LogStackDump) }, + { MSG(LogClientDebuggerConnect) }, + { MSG(SetPlayerBanStatusRequest)}, + { MSG(KickPlayer) }, + { MSG(ChangePlayerNameRequest) }, + { MSG(SendFriendInviteRequest) }, + { MSG(ScoreCreate) }, + { MSG(ScoreDelete) }, + { MSG(ScoreGetScores) }, + { MSG(ScoreAddPoints) }, + { MSG(ScoreTransferPoints) }, + { MSG(ScoreSetPoints) }, + { MSG(ScoreGetRanks) }, + { MSG(AccountExistsRequest) }, +}; +#undef MSG + +#define MSG(s) kNetMsg_Auth2Cli_##s, Recv_##s +static NetMsgInitRecv s_recv[] = { + { MSG(PingReply) }, + { MSG(ClientRegisterReply) }, + { MSG(AcctPlayerInfo) }, + { MSG(AcctLoginReply) }, + { MSG(AcctCreateReply) }, + { MSG(AcctCreateFromKeyReply) }, + { MSG(PlayerCreateReply) }, + { MSG(PlayerDeleteReply) }, + { MSG(UpgradeVisitorReply) }, + { MSG(AcctSetPlayerReply) }, + { MSG(AcctChangePasswordReply) }, + { MSG(AcctSetRolesReply) }, + { MSG(AcctSetBillingTypeReply) }, + { MSG(AcctActivateReply) }, + { MSG(AgeReply) }, + { MSG(FileListReply) }, + { MSG(FileDownloadChunk) }, + { MSG(KickedOff) }, + { MSG(VaultNodeRefsFetched) }, + { MSG(VaultNodeFetched) }, + { MSG(VaultNodeCreated) }, + { MSG(VaultNodeChanged) }, + { MSG(VaultNodeAdded) }, + { MSG(VaultNodeRemoved) }, + { MSG(VaultNodeDeleted) }, + { MSG(VaultSaveNodeReply) }, + { MSG(VaultAddNodeReply) }, + { MSG(VaultRemoveNodeReply) }, + { MSG(VaultInitAgeReply) }, + { MSG(VaultNodeFindReply) }, + { MSG(PublicAgeList) }, + { MSG(ServerAddr) }, + { MSG(NotifyNewBuild) }, + { MSG(SetPlayerBanStatusReply) }, + { MSG(ChangePlayerNameReply) }, + { MSG(SendFriendInviteReply) }, + { MSG(ScoreCreateReply) }, + { MSG(ScoreDeleteReply) }, + { MSG(ScoreGetScoresReply) }, + { MSG(ScoreAddPointsReply) }, + { MSG(ScoreTransferPointsReply) }, + { MSG(ScoreSetPointsReply) }, + { MSG(ScoreGetRanksReply) }, + { MSG(AccountExistsReply) }, +}; +#undef MSG + + +/***************************************************************************** +* +* PingRequestTrans +* +***/ + +//============================================================================ +PingRequestTrans::PingRequestTrans ( + FNetCliAuthPingRequestCallback callback, + void * param, + unsigned pingAtMs, + unsigned payloadBytes, + const void * payload +) : NetAuthTrans(kPingRequestTrans) +, m_callback(callback) +, m_param(param) +, m_pingAtMs(pingAtMs) +{ + m_payload.Set((const byte *)payload, payloadBytes); +} + +//============================================================================ +bool PingRequestTrans::Send () { + + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_PingRequest, + m_pingAtMs, + m_transId, + m_payload.Count(), + (unsigned_ptr) m_payload.Ptr(), + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void PingRequestTrans::Post () { + + m_callback( + m_result, + m_param, + m_pingAtMs, + m_replyAtMs, + m_payload.Count(), + m_payload.Ptr() + ); +} + +//============================================================================ +bool PingRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const Auth2Cli_PingReply & reply = *(const Auth2Cli_PingReply *)msg; + + m_payload.Set(reply.payload, reply.payloadBytes); + m_replyAtMs = TimeGetMs(); + m_result = kNetSuccess; + m_state = kTransStateComplete; + + return true; +} + + +/***************************************************************************** +* +* AccountExistsRequestTrans +* +***/ + +//============================================================================ +AccountExistsRequestTrans::AccountExistsRequestTrans ( + FNetCliAuthAccountExistsRequestCallback callback, + void * param, + const wchar accountName[] +) : NetAuthTrans(kPingRequestTrans) +, m_callback(callback) +, m_param(param) +{ + StrCopy(m_accountName, accountName, kMaxAccountNameLength); +} + +//============================================================================ +bool AccountExistsRequestTrans::Send () { + + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_AccountExistsRequest, + m_transId, + (unsigned_ptr)m_accountName + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void AccountExistsRequestTrans::Post () { + + m_callback( + m_result, + m_param, + m_exists + ); +} + +//============================================================================ +bool AccountExistsRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const Auth2Cli_AccountExistsReply & reply = *(const Auth2Cli_AccountExistsReply *)msg; + + m_exists = reply.exists; + m_result = reply.result; + m_state = kTransStateComplete; + + return true; +} + + +/***************************************************************************** +* +* LoginRequestTrans +* +***/ + +//============================================================================ +LoginRequestTrans::LoginRequestTrans ( + FNetCliAuthLoginRequestCallback callback, + void * param +) : NetAuthTrans(kLoginRequestTrans) +, m_callback(callback) +, m_param(param) +, m_accountId(kNilGuid) +, m_accountFlags(0) +, m_playerCount(0) +{ + ZERO(m_players); +} + +//============================================================================ +void LoginRequestTrans::AddPlayer ( + unsigned playerInt, + const wchar playerName[], + const wchar avatarShape[], + unsigned explorer +) { + unsigned index = m_playerCount++; + ASSERT(index < kMaxPlayersPerAccount); + m_players[index].playerInt = playerInt; + m_players[index].explorer = explorer; + StrCopy(m_players[index].playerName, playerName, arrsize(m_players[index].playerName)); + StrCopy(m_players[index].avatarShape, avatarShape, arrsize(m_players[index].avatarShape)); +} + +//============================================================================ +bool LoginRequestTrans::Send () { + + if (!AcquireConn()) + return false; + + ShaDigest challengeHash; + dword clientChallenge = 0; + + wchar domain[15]; + PathSplitEmail(s_accountName, nil, 0, domain, arrsize(domain), nil, 0, nil, 0, 0); + + if (StrLen(domain) == 0 || StrCmpI(domain, L"gametap") == 0) { + challengeHash = s_accountNamePassHash; + } + else { + CryptCreateRandomSeed( + sizeof(clientChallenge), + (byte *) &clientChallenge + ); + + CryptHashPasswordChallenge( + clientChallenge, + s_active->serverChallenge, + s_accountNamePassHash, + &challengeHash + ); + } + + const unsigned_ptr msg[] = { + kCli2Auth_AcctLoginRequest, + m_transId, + clientChallenge, + (unsigned_ptr) s_accountName, + (unsigned_ptr) &challengeHash, + (unsigned_ptr) s_authToken, + (unsigned_ptr) s_os, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void LoginRequestTrans::Post () { + m_callback( + m_result, + m_param, + m_accountId, + m_accountFlags, + m_billingType, + m_players, + m_playerCount + ); +} + +//============================================================================ +bool LoginRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + dword msgId = (dword)*msg; + switch (msgId) { + case kAuth2Cli_AcctPlayerInfo: { + const Auth2Cli_AcctPlayerInfo & reply = *(const Auth2Cli_AcctPlayerInfo *) msg; + AddPlayer(reply.playerInt, reply.playerName, reply.avatarShape, reply.explorer); + } + break; + + case kAuth2Cli_AcctLoginReply: { + const Auth2Cli_AcctLoginReply & reply = *(const Auth2Cli_AcctLoginReply *) msg; + m_result = reply.result; + m_state = kTransStateComplete; + m_accountId = reply.accountId; + m_accountFlags = reply.accountFlags; + m_billingType = reply.billingType; + + unsigned memSize = min(arrsize(s_encryptionKey), arrsize(reply.encryptionKey)); + memSize *= sizeof(UInt32); + MemCopy(s_encryptionKey, reply.encryptionKey, memSize); + } + break; + + DEFAULT_FATAL(msgId); + }; + + return true; +} + + +/***************************************************************************** +* +* AgeRequestTrans +* +***/ + +//============================================================================ +AgeRequestTrans::AgeRequestTrans ( + const wchar ageName[], + const Uuid & ageInstId, + FNetCliAuthAgeRequestCallback callback, + void * param +) : NetAuthTrans(kAgeRequestTrans) +, m_ageInstId(ageInstId) +, m_callback(callback) +, m_param(param) +{ + StrCopy(m_ageName, ageName, arrsize(m_ageName)); +} + +//============================================================================ +AgeRequestTrans::~AgeRequestTrans () { +} + +//============================================================================ +bool AgeRequestTrans::Send () { + if (!AcquireConn()) + return true; + + const unsigned_ptr msg[] = { + kCli2Auth_AgeRequest, + m_transId, + (unsigned_ptr) m_ageName, + (unsigned_ptr) &m_ageInstId, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void AgeRequestTrans::Post () { + m_callback( + m_result, + m_param, + m_ageMcpId, + m_ageVaultId, + m_ageInstId, + m_gameSrvNode + ); +} + +//============================================================================ +bool AgeRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_AgeReply & reply = *(const Auth2Cli_AgeReply *) msg; + m_gameSrvNode = reply.gameSrvNode; + m_ageMcpId = reply.ageMcpId; + m_ageVaultId = reply.ageVaultId; + m_ageInstId = reply.ageInstId; + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* AccountCreateRequestTrans +* +***/ + +//============================================================================ +AccountCreateRequestTrans::AccountCreateRequestTrans ( + const wchar accountName[], + const wchar password[], + unsigned accountFlags, + unsigned billingType, + FNetCliAuthAccountCreateRequestCallback callback, + void * param +) : NetAuthTrans(kAccountCreateRequestTrans) +, m_callback(callback) +, m_param(param) +, m_accountFlags(accountFlags) +, m_billingType(billingType) +{ + StrCopy(m_accountName, accountName, arrsize(m_accountName)); + + CryptHashPassword( + m_accountName, + password, + &m_namePassHash + ); +} + +//============================================================================ +bool AccountCreateRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_AcctCreateRequest, + m_transId, + (unsigned_ptr) m_accountName, + (unsigned_ptr) &m_namePassHash, + m_accountFlags, + m_billingType, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void AccountCreateRequestTrans::Post () { + m_callback( + m_result, + m_param, + m_accountId + ); +} + +//============================================================================ +bool AccountCreateRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_AcctCreateReply & reply = *(const Auth2Cli_AcctCreateReply *) msg; + + m_result = reply.result; + m_accountId = reply.accountId; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* AccountCreateFromKeyRequestTrans +* +***/ + +//============================================================================ +AccountCreateFromKeyRequestTrans::AccountCreateFromKeyRequestTrans ( + const wchar accountName[], + const wchar password[], + const Uuid & key, + unsigned billingType, + FNetCliAuthAccountCreateFromKeyRequestCallback callback, + void * param +) : NetAuthTrans(kAccountCreateFromKeyRequestTrans) +, m_callback(callback) +, m_param(param) +, m_billingType(billingType) +, m_key(key) +{ + StrCopy(m_accountName, accountName, arrsize(m_accountName)); + + CryptHashPassword( + m_accountName, + password, + &m_namePassHash + ); +} + +//============================================================================ +bool AccountCreateFromKeyRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_AcctCreateFromKeyRequest, + m_transId, + (unsigned_ptr) m_accountName, + (unsigned_ptr) &m_namePassHash, + (unsigned_ptr) &m_key, + m_billingType, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void AccountCreateFromKeyRequestTrans::Post () { + m_callback( + m_result, + m_param, + m_accountId, + m_activationKey + ); +} + +//============================================================================ +bool AccountCreateFromKeyRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_AcctCreateFromKeyReply & reply = *(const Auth2Cli_AcctCreateFromKeyReply *) msg; + + m_result = reply.result; + m_accountId = reply.accountId; + m_activationKey = reply.activationKey; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* PlayerCreateRequestTrans +* +***/ + +//============================================================================ +PlayerCreateRequestTrans::PlayerCreateRequestTrans ( + const wchar playerName[], + const wchar avatarShape[], + const wchar friendInvite[], + FNetCliAuthPlayerCreateRequestCallback callback, + void * param +) : NetAuthTrans(kPlayerCreateRequestTrans) +, m_callback(callback) +, m_param(param) +{ + StrCopy(m_playerName, playerName, arrsize(m_playerName)); + StrCopy(m_avatarShape, avatarShape, arrsize(m_avatarShape)); + if (friendInvite) + StrCopy(m_friendInvite, friendInvite, arrsize(m_friendInvite)); + else + m_friendInvite[0] = 0; + ZERO(m_playerInfo); +} + +//============================================================================ +bool PlayerCreateRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_PlayerCreateRequest, + m_transId, + (unsigned_ptr) m_playerName, + (unsigned_ptr) m_avatarShape, + (unsigned_ptr) m_friendInvite, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void PlayerCreateRequestTrans::Post () { + m_callback( + m_result, + m_param, + m_playerInfo + ); +} + +//============================================================================ +bool PlayerCreateRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_PlayerCreateReply & reply = *(const Auth2Cli_PlayerCreateReply *) msg; + if (!IS_NET_ERROR(reply.result)) { + m_playerInfo.playerInt = reply.playerInt; + m_playerInfo.explorer = reply.explorer; + StrCopy(m_playerInfo.playerName, reply.playerName, arrsize(m_playerInfo.playerName)); + StrCopy(m_playerInfo.avatarShape, reply.avatarShape, arrsize(m_playerInfo.avatarShape)); + } + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + + +/***************************************************************************** +* +* PlayerDeleteRequestTrans +* +***/ + +//============================================================================ +PlayerDeleteRequestTrans::PlayerDeleteRequestTrans ( + unsigned playerId, + FNetCliAuthPlayerDeleteRequestCallback callback, + void * param +) : NetAuthTrans(kPlayerDeleteRequestTrans) +, m_callback(callback) +, m_param(param) +, m_playerId(playerId) +{ +} + +//============================================================================ +bool PlayerDeleteRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_PlayerDeleteRequest, + m_transId, + m_playerId + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void PlayerDeleteRequestTrans::Post () { + m_callback( + m_result, + m_param + ); +} + +//============================================================================ +bool PlayerDeleteRequestTrans::Recv ( + const byte msg[], + unsigned +) { + const Auth2Cli_PlayerDeleteReply & reply = *(const Auth2Cli_PlayerDeleteReply *) msg; + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + + +/***************************************************************************** +* +* UpgradeVisitorRequestTrans +* +***/ + +//============================================================================ +UpgradeVisitorRequestTrans::UpgradeVisitorRequestTrans ( + unsigned playerId, + FNetCliAuthUpgradeVisitorRequestCallback callback, + void * param +) : NetAuthTrans(kUpgradeVisitorRequestTrans) +, m_callback(callback) +, m_param(param) +, m_playerId(playerId) +{ +} + +//============================================================================ +bool UpgradeVisitorRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_UpgradeVisitorRequest, + m_transId, + m_playerId + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void UpgradeVisitorRequestTrans::Post () { + m_callback( + m_result, + m_param + ); +} + +//============================================================================ +bool UpgradeVisitorRequestTrans::Recv ( + const byte msg[], + unsigned +) { + const Auth2Cli_UpgradeVisitorReply & reply = *(const Auth2Cli_UpgradeVisitorReply *) msg; + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + + +/***************************************************************************** +* +* SetPlayerRequestTrans +* +***/ + +//============================================================================ +SetPlayerRequestTrans::SetPlayerRequestTrans ( + unsigned playerInt, + FNetCliAuthSetPlayerRequestCallback callback, + void * param + +) : NetAuthTrans(kSetPlayerRequestTrans) +, m_playerInt(playerInt) +, m_callback(callback) +, m_param(param) +{ +} + +//============================================================================ +bool SetPlayerRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_AcctSetPlayerRequest, + m_transId, + m_playerInt, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void SetPlayerRequestTrans::Post () { + m_callback(m_result, m_param); +} + +//============================================================================ +bool SetPlayerRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_AcctSetPlayerReply & reply = *(const Auth2Cli_AcctSetPlayerReply *) msg; + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* AccountChangePasswordRequestTrans +* +***/ + +//============================================================================ +AccountChangePasswordRequestTrans::AccountChangePasswordRequestTrans ( + const wchar accountName[], + const wchar password[], + FNetCliAuthAccountChangePasswordRequestCallback callback, + void * param +) : NetAuthTrans(kAccountChangePasswordRequestTrans) +, m_callback(callback) +, m_param(param) +{ + StrCopy(m_accountName, accountName, arrsize(m_accountName)); + + CryptHashPassword( + m_accountName, + password, + &m_namePassHash + ); +} + +//============================================================================ +bool AccountChangePasswordRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_AcctChangePasswordRequest, + m_transId, + (unsigned_ptr) m_accountName, + (unsigned_ptr) &m_namePassHash, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void AccountChangePasswordRequestTrans::Post () { + m_callback( + m_result, + m_param + ); +} + +//============================================================================ +bool AccountChangePasswordRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_AcctChangePasswordReply & reply = *(const Auth2Cli_AcctChangePasswordReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* GetPublicAgeListTrans +* +***/ + +//============================================================================ +GetPublicAgeListTrans::GetPublicAgeListTrans ( + const wchar ageName[], + FNetCliAuthGetPublicAgeListCallback callback, + void * param +) : NetAuthTrans(kGetPublicAgeListTrans) +, m_callback(callback) +, m_param(param) +{ + StrCopy(m_ageName, ageName, arrsize(m_ageName)); +} + +//============================================================================ +bool GetPublicAgeListTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_GetPublicAgeList, + m_transId, + (unsigned_ptr) &m_ageName, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void GetPublicAgeListTrans::Post () { + m_callback( + m_result, + m_param, + m_ages + ); +} + +//============================================================================ +bool GetPublicAgeListTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_PublicAgeList & reply = *(const Auth2Cli_PublicAgeList *) msg; + + if (IS_NET_SUCCESS(reply.result)) + m_ages.Set(reply.ages, reply.ageCount); + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + + +/***************************************************************************** +* +* AccountSetRolesRequestTrans +* +***/ + +//============================================================================ +AccountSetRolesRequestTrans::AccountSetRolesRequestTrans ( + const wchar accountName[], + unsigned accountFlags, + FNetCliAuthAccountSetRolesRequestCallback callback, + void * param +) : NetAuthTrans(kAccountSetRolesRequestTrans) +, m_callback(callback) +, m_param(param) +, m_accountFlags(accountFlags) +{ + StrCopy(m_accountName, accountName, arrsize(m_accountName)); +} + +//============================================================================ +bool AccountSetRolesRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_AcctSetRolesRequest, + m_transId, + (unsigned_ptr) m_accountName, + m_accountFlags, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void AccountSetRolesRequestTrans::Post () { + m_callback( + m_result, + m_param + ); +} + +//============================================================================ +bool AccountSetRolesRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_AcctSetRolesReply & reply = *(const Auth2Cli_AcctSetRolesReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* AccountSetBillingTypeRequestTrans +* +***/ + +//============================================================================ +AccountSetBillingTypeRequestTrans::AccountSetBillingTypeRequestTrans ( + const wchar accountName[], + unsigned billingType, + FNetCliAuthAccountSetBillingTypeRequestCallback callback, + void * param +) : NetAuthTrans(kAccountSetBillingTypeRequestTrans) +, m_callback(callback) +, m_param(param) +, m_billingType(billingType) +{ + StrCopy(m_accountName, accountName, arrsize(m_accountName)); +} + +//============================================================================ +bool AccountSetBillingTypeRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_AcctSetBillingTypeRequest, + m_transId, + (unsigned_ptr) m_accountName, + m_billingType, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void AccountSetBillingTypeRequestTrans::Post () { + m_callback( + m_result, + m_param + ); +} + +//============================================================================ +bool AccountSetBillingTypeRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_AcctSetBillingTypeReply & reply = *(const Auth2Cli_AcctSetBillingTypeReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* AccountActivateRequestTrans +* +***/ + +//============================================================================ +AccountActivateRequestTrans::AccountActivateRequestTrans ( + const Uuid & activationKey, + FNetCliAuthAccountActivateRequestCallback callback, + void * param +) : NetAuthTrans(kAccountActivateRequestTrans) +, m_callback(callback) +, m_param(param) +{ + m_activationKey = activationKey; +} + +//============================================================================ +bool AccountActivateRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_AcctActivateRequest, + m_transId, + (unsigned_ptr) &m_activationKey, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void AccountActivateRequestTrans::Post () { + m_callback( + m_result, + m_param + ); +} + +//============================================================================ +bool AccountActivateRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_AcctActivateReply & reply = *(const Auth2Cli_AcctActivateReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* FileListRequestTrans +* +***/ + +//============================================================================ +FileListRequestTrans::FileListRequestTrans ( + FNetCliAuthFileListRequestCallback callback, + void * param, + const wchar directory[], + const wchar ext[] +) : NetAuthTrans(kFileListRequestTrans) +, m_callback(callback) +, m_param(param) +{ + StrCopy(m_directory, directory, arrsize(m_directory)); + StrCopy(m_ext, ext, arrsize(m_ext)); +} + +//============================================================================ +bool FileListRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_FileListRequest, + m_transId, + (unsigned_ptr) m_directory, + (unsigned_ptr) m_ext, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void FileListRequestTrans::Post () { + m_callback(m_result, m_param, m_fileInfoArray.Ptr(), m_fileInfoArray.Count()); +} + +//============================================================================ +bool FileListRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_FileListReply & reply = *(const Auth2Cli_FileListReply *) msg; + + dword wcharCount = reply.wcharCount; + const wchar* curChar = reply.fileData; + // if wcharCount is 2, the data only contains the terminator "\0\0" and we + // don't need to convert anything + if (wcharCount == 2) + m_fileInfoArray.Clear(); + else + { + // fileData format: "filename\0size\0filename\0size\0...\0\0" + bool done = false; + while (!done) { + if (wcharCount == 0) + { + done = true; + break; + } + + // read in the filename + wchar filename[MAX_PATH]; + StrCopy(filename, curChar, arrsize(filename)); + filename[arrsize(filename) - 1] = L'\0'; // make sure it's terminated + + unsigned filenameLen = StrLen(filename); + curChar += filenameLen; // advance the pointer + wcharCount -= filenameLen; // keep track of the amount remaining + if ((*curChar != L'\0') || (wcharCount <= 0)) + return false; // something is screwy, abort and disconnect + + curChar++; // point it at the first part of the size value (format: 0xHHHHLLLL) + wcharCount--; + if (wcharCount < 4) // we have to have 2 chars for the size, and 2 for terminator at least + return false; // screwy data + unsigned size = ((*curChar) << 16) + (*(curChar + 1)); + curChar += 2; + wcharCount -= 2; + if ((*curChar != L'\0') || (wcharCount <= 0)) + return false; // screwy data + + // save the data in our array + NetCliAuthFileInfo* info = m_fileInfoArray.New(); + StrCopy(info->filename, filename, arrsize(info->filename)); + info->filesize = size; + + // point it at either the second part of the terminator, or the next filename + curChar++; + wcharCount--; + if (*curChar == L'\0') + { + // we hit the terminator + if (wcharCount != 1) + return false; // invalid data, we shouldn't have any more + done = true; // we're done + } + else if (wcharCount < 6) // we must have at least a 1 char string, '\0', size, and "\0\0" terminator left + return false; // screwy data + } + } + + m_result = reply.result; + m_state = kTransStateComplete; + + return true; +} + +/***************************************************************************** +* +* FileDownloadRequestTrans +* +***/ + +//============================================================================ +FileDownloadRequestTrans::FileDownloadRequestTrans ( + FNetCliAuthFileRequestCallback callback, + void * param, + const wchar filename[], + hsStream * writer +) : NetAuthTrans(kFileDownloadRequestTrans) +, m_callback(callback) +, m_param(param) +, m_writer(writer) +{ + StrCopy(m_filename, filename, arrsize(m_filename)); + // This transaction issues "sub transactions" which must complete + // before this one even though they were issued after us. + m_hasSubTrans = true; +} + +//============================================================================ +bool FileDownloadRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_FileDownloadRequest, + m_transId, + (unsigned_ptr) m_filename, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void FileDownloadRequestTrans::Post () { + m_callback(m_result, m_param, m_filename, m_writer); +} + +//============================================================================ +bool FileDownloadRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_FileDownloadChunk & reply = *(const Auth2Cli_FileDownloadChunk *) msg; + + if (IS_NET_ERROR(reply.result)) { + // we have a problem... indicate we are done and abort + m_result = reply.result; + m_state = kTransStateComplete; + return true; + } + + // we have data to write, so queue it for write in the main thread (we're + // currently in a net recv thread) + if (reply.chunkSize) { + RcvdFileDownloadChunkTrans * writeTrans = NEW(RcvdFileDownloadChunkTrans); + writeTrans->writer = m_writer; + writeTrans->bytes = reply.chunkSize; + writeTrans->offset = reply.chunkOffset; + writeTrans->data = (byte *)ALLOC(reply.chunkSize); + MemCopy(writeTrans->data, reply.chunkData, reply.chunkSize); + NetTransSend(writeTrans); + } + + if (reply.chunkOffset + reply.chunkSize >= reply.fileSize) { + // all bytes received, mark as complete + m_result = reply.result; + m_state = kTransStateComplete; + } + + // Ack the data + const unsigned_ptr ack[] = { + kCli2Auth_FileDownloadChunkAck, + m_transId + }; + m_conn->Send(ack, arrsize(ack)); + + return true; +} + + +/***************************************************************************** +* +* RcvdFileDownloadChunkTrans +* +***/ + +//============================================================================ +RcvdFileDownloadChunkTrans::~RcvdFileDownloadChunkTrans () { + FREE(data); +} + +//============================================================================ +void RcvdFileDownloadChunkTrans::Post () { + + if (writer->GetPosition() != offset) + writer->SetPosition(offset); + + writer->Write(bytes, data); + m_result = kNetSuccess; + m_state = kTransStateComplete; +} + + +/***************************************************************************** +* +* RcvdPropagatedBufferTrans +* +***/ + +//============================================================================ +RcvdPropagatedBufferTrans::~RcvdPropagatedBufferTrans () { + FREE(bufferData); +} + +//============================================================================ +void RcvdPropagatedBufferTrans::Post () { + if (s_bufRcvdCb) + s_bufRcvdCb(bufferType, bufferBytes, bufferData); +} + + +/***************************************************************************** +* +* VaultNodeChangedTrans +* +***/ + +//============================================================================ +void VaultNodeChangedTrans::Post () { + if (s_vaultNodeChangedHandler) + s_vaultNodeChangedHandler(m_nodeId, m_revId); +} + + +/***************************************************************************** +* +* VaultNodeAddedTrans +* +***/ + +//============================================================================ +void VaultNodeAddedTrans::Post () { + if (s_vaultNodeAddedHandler) + s_vaultNodeAddedHandler(m_parentId, m_childId, m_ownerId); +} + + +/***************************************************************************** +* +* VaultNodeRemovedTrans +* +***/ + +//============================================================================ +void VaultNodeRemovedTrans::Post () { + if (s_vaultNodeRemovedHandler) + s_vaultNodeRemovedHandler(m_parentId, m_childId); +} + + +/***************************************************************************** +* +* VaultNodeDeletedTrans +* +***/ + +//============================================================================ +void VaultNodeDeletedTrans::Post () { + if (s_vaultNodeDeletedHandler) + s_vaultNodeDeletedHandler(m_nodeId); +} + + +/***************************************************************************** +* +* VaultFetchNodeRefsTrans +* +***/ + +//============================================================================ +VaultFetchNodeRefsTrans::VaultFetchNodeRefsTrans ( + unsigned nodeId, + FNetCliAuthVaultNodeRefsFetched callback, + void * param +) : NetAuthTrans(kVaultFetchNodeRefsTrans) +, m_nodeId(nodeId) +, m_callback(callback) +, m_param(param) +{ +} + +//============================================================================ +bool VaultFetchNodeRefsTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_VaultFetchNodeRefs, + m_transId, + m_nodeId, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void VaultFetchNodeRefsTrans::Post () { + if (m_callback) + m_callback( + m_result, + m_param, + m_refs.Ptr(), + m_refs.Count() + ); +} + +//============================================================================ +bool VaultFetchNodeRefsTrans::Recv ( + const byte msg[], + unsigned +) { + const Auth2Cli_VaultNodeRefsFetched & reply = *(const Auth2Cli_VaultNodeRefsFetched *) msg; + + if (IS_NET_SUCCESS(reply.result)) + m_refs.Set(reply.refs, reply.refCount); + + m_result = reply.result; + m_state = kTransStateComplete; + + return true; +} + + +/***************************************************************************** +* +* VaultInitAgeTrans +* +***/ + +//============================================================================ +VaultInitAgeTrans::VaultInitAgeTrans ( + FNetCliAuthAgeInitCallback callback, // optional + void * param, // optional + const Uuid & ageInstId, // optional. is used in match + const Uuid & parentAgeInstId, // optional. is used in match + const wchar ageFilename[], // optional. is used in match + const wchar ageInstName[], // optional. not used in match + const wchar ageUserName[], // optional. not used in match + const wchar ageDesc[], // optional. not used in match + unsigned ageSequenceNumber, // optional. not used in match + unsigned ageLanguage // optional. not used in match +) : NetAuthTrans(kVaultInitAgeTrans) +, m_callback(callback) +, m_param(param) +, m_ageInstId(ageInstId) +, m_parentAgeInstId(parentAgeInstId) +, m_ageFilename(StrDup(ageFilename ? ageFilename : L"")) +, m_ageInstName(StrDup(ageInstName ? ageInstName : L"")) +, m_ageUserName(StrDup(ageUserName ? ageUserName : L"")) +, m_ageDesc(StrDup(ageDesc ? ageDesc : L"")) +, m_ageSequenceNumber(ageSequenceNumber) +, m_ageLanguage(ageLanguage) +{ +} + +//============================================================================ +VaultInitAgeTrans::~VaultInitAgeTrans () { + FREE(m_ageFilename); + FREE(m_ageInstName); + FREE(m_ageUserName); + FREE(m_ageDesc); +} + +//============================================================================ +bool VaultInitAgeTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_VaultInitAgeRequest, + m_transId, + (unsigned_ptr) &m_ageInstId, + (unsigned_ptr) &m_parentAgeInstId, + (unsigned_ptr) m_ageFilename, + (unsigned_ptr) m_ageInstName, + (unsigned_ptr) m_ageUserName, + (unsigned_ptr) m_ageDesc, + m_ageSequenceNumber, + m_ageLanguage, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void VaultInitAgeTrans::Post () { + if (m_callback) + m_callback( + m_result, + m_param, + m_ageId, + m_ageInfoId + ); +} + +//============================================================================ +bool VaultInitAgeTrans::Recv ( + const byte msg[], + unsigned +) { + const Auth2Cli_VaultInitAgeReply & reply = *(const Auth2Cli_VaultInitAgeReply *) msg; + + m_ageId = reply.ageVaultId; + m_ageInfoId = reply.ageInfoVaultId; + + m_result = reply.result; + m_state = kTransStateComplete; + + return true; +} + + +/***************************************************************************** +* +* VaultFetchNodeTrans +* +***/ + +//============================================================================ +VaultFetchNodeTrans::VaultFetchNodeTrans ( + unsigned nodeId, + FNetCliAuthVaultNodeFetched callback, + void * param +) : NetAuthTrans(kVaultFetchNodeTrans) +, m_nodeId(nodeId) +, m_callback(callback) +, m_param(param) +{ +} + +//============================================================================ +bool VaultFetchNodeTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_VaultNodeFetch, + m_transId, + m_nodeId, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void VaultFetchNodeTrans::Post () { + m_callback( + m_result, + m_param, + m_node + ); + if (m_node) + m_node->DecRef("Recv"); +} + +//============================================================================ +bool VaultFetchNodeTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const Auth2Cli_VaultNodeFetched & reply = *(const Auth2Cli_VaultNodeFetched *) msg; + + if (IS_NET_SUCCESS(reply.result)) { + m_node = NEWZERO(NetVaultNode); + m_node->Read_LCS(reply.nodeBuffer, reply.nodeBytes, 0); + m_node->IncRef("Recv"); + } + + m_result = reply.result; + m_state = kTransStateComplete; + + return true; +} + + +/***************************************************************************** +* +* VaultFindNodeTrans +* +***/ + +//============================================================================ +VaultFindNodeTrans::VaultFindNodeTrans ( + NetVaultNode * templateNode, + FNetCliAuthVaultNodeFind callback, + void * param +) : NetAuthTrans(kVaultFindNodeTrans) +, m_node(templateNode) +, m_callback(callback) +, m_param(param) +{ + m_node->IncRef(); +} + +//============================================================================ +VaultFindNodeTrans::~VaultFindNodeTrans () { + m_node->DecRef(); +} + +//============================================================================ +bool VaultFindNodeTrans::Send () { + if (!AcquireConn()) + return false; + + ARRAY(byte) buffer; + m_node->critsect.Enter(); + m_node->Write_LCS(&buffer, 0); + m_node->critsect.Leave(); + + const unsigned_ptr msg[] = { + kCli2Auth_VaultNodeFind, + m_transId, + buffer.Count(), + (unsigned_ptr) buffer.Ptr(), + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void VaultFindNodeTrans::Post () { + m_callback( + m_result, + m_param, + m_nodeIds.Count(), + m_nodeIds.Ptr() + ); +} + +//============================================================================ +bool VaultFindNodeTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const Auth2Cli_VaultNodeFindReply & reply = *(const Auth2Cli_VaultNodeFindReply *) msg; + + if (IS_NET_SUCCESS(reply.result)) { + COMPILER_ASSERT(sizeof(unsigned) == sizeof(dword)); + m_nodeIds.Set((unsigned *)reply.nodeIds, reply.nodeIdCount); + } + + m_result = reply.result; + m_state = kTransStateComplete; + + return true; +} + + +/***************************************************************************** +* +* VaultCreateNodeTrans +* +***/ + +//============================================================================ +VaultCreateNodeTrans::VaultCreateNodeTrans ( + NetVaultNode * templateNode, + FNetCliAuthVaultNodeCreated callback, + void * param +) : NetAuthTrans(kVaultCreateNodeTrans) +, m_templateNode(templateNode) +, m_callback(callback) +, m_param(param) +{ + m_templateNode->IncRef(); +} + +//============================================================================ +bool VaultCreateNodeTrans::Send () { + if (!AcquireConn()) + return false; + + ARRAY(byte) buffer; + m_templateNode->Write_LCS(&buffer, 0); + + const unsigned_ptr msg[] = { + kCli2Auth_VaultNodeCreate, + m_transId, + buffer.Count(), + (unsigned_ptr) buffer.Ptr() + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void VaultCreateNodeTrans::Post () { + m_callback( + m_result, + m_param, + m_nodeId + ); + m_templateNode->DecRef(); +} + +//============================================================================ +bool VaultCreateNodeTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const Auth2Cli_VaultNodeCreated & reply = *(const Auth2Cli_VaultNodeCreated *) msg; + + if (IS_NET_SUCCESS(reply.result)) + m_nodeId = reply.nodeId; + + m_result = reply.result; + m_state = kTransStateComplete; + + return true; +} + +/***************************************************************************** +* +* VaultSaveNodeTrans +* +***/ + +//============================================================================ +VaultSaveNodeTrans::VaultSaveNodeTrans ( + unsigned nodeId, + const Uuid & revisionId, + unsigned dataCount, + const void * data, + FNetCliAuthVaultNodeSaveCallback callback, + void * param +) : NetAuthTrans(kVaultSaveNodeTrans) +, m_nodeId(nodeId) +, m_revisionId(revisionId) +, m_callback(callback) +, m_param(param) +{ + m_buffer.Set((const byte *)data, dataCount); +} + +//============================================================================ +bool VaultSaveNodeTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_VaultNodeSave, + m_transId, + m_nodeId, + (unsigned_ptr) &m_revisionId, + m_buffer.Count(), + (unsigned_ptr) m_buffer.Ptr(), + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void VaultSaveNodeTrans::Post () { + if (m_callback) { + m_callback( + m_result, + m_param + ); + } +} + +//============================================================================ +bool VaultSaveNodeTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const Auth2Cli_VaultSaveNodeReply & reply = *(const Auth2Cli_VaultSaveNodeReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + + return true; +} + +/***************************************************************************** +* +* VaultAddNodeTrans +* +***/ + +//============================================================================ +VaultAddNodeTrans::VaultAddNodeTrans ( + unsigned parentId, + unsigned childId, + unsigned ownerId, + FNetCliAuthVaultNodeAddCallback callback, + void * param +) : NetAuthTrans(kVaultAddNodeTrans) +, m_parentId(parentId) +, m_childId(childId) +, m_ownerId(ownerId) +, m_callback(callback) +, m_param(param) +{ +} + +//============================================================================ +bool VaultAddNodeTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_VaultNodeAdd, + m_transId, + m_parentId, + m_childId, + m_ownerId, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void VaultAddNodeTrans::Post () { + if (m_callback) { + m_callback( + m_result, + m_param + ); + } +} + +//============================================================================ +bool VaultAddNodeTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const Auth2Cli_VaultAddNodeReply & reply = *(const Auth2Cli_VaultAddNodeReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + + return true; +} + +/***************************************************************************** +* +* VaultRemoveNodeTrans +* +***/ + +//============================================================================ +VaultRemoveNodeTrans::VaultRemoveNodeTrans ( + unsigned parentId, + unsigned childId, + FNetCliAuthVaultNodeRemoveCallback callback, + void * param +) : NetAuthTrans(kVaultRemoveNodeTrans) +, m_parentId(parentId) +, m_childId(childId) +, m_callback(callback) +, m_param(param) +{ +} + +//============================================================================ +bool VaultRemoveNodeTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_VaultNodeRemove, + m_transId, + m_parentId, + m_childId, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void VaultRemoveNodeTrans::Post () { + if (m_callback) { + m_callback( + m_result, + m_param + ); + } +} + +//============================================================================ +bool VaultRemoveNodeTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const Auth2Cli_VaultRemoveNodeReply & reply = *(const Auth2Cli_VaultRemoveNodeReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + + return true; +} + +/***************************************************************************** +* +* NotifyNewBuildTrans +* +***/ + +//============================================================================ +void NotifyNewBuildTrans::Post () { + + if (s_notifyNewBuildHandler) + s_notifyNewBuildHandler(); +} + +/***************************************************************************** +* +* SetPlayerBanStatusRequestTrans +* +***/ + +//============================================================================ +SetPlayerBanStatusRequestTrans::SetPlayerBanStatusRequestTrans ( + unsigned playerId, + unsigned banned, + FNetCliAuthSetPlayerBanStatusRequestCallback callback, + void * param +) : NetAuthTrans(kSetPlayerBanStatusRequestTrans) +, m_callback(callback) +, m_param(param) +, m_playerId(playerId) +, m_banned(banned) +{ +} + +//============================================================================ +bool SetPlayerBanStatusRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_SetPlayerBanStatusRequest, + m_transId, + m_playerId, + m_banned, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void SetPlayerBanStatusRequestTrans::Post () { + m_callback( + m_result, + m_param + ); +} + +//============================================================================ +bool SetPlayerBanStatusRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_SetPlayerBanStatusReply & reply = *(const Auth2Cli_SetPlayerBanStatusReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* ChangePlayerNameRequestTrans +* +***/ + +//============================================================================ +ChangePlayerNameRequestTrans::ChangePlayerNameRequestTrans ( + unsigned playerId, + const wchar newName[], + FNetCliAuthChangePlayerNameRequestCallback callback, + void * param +) : NetAuthTrans(kChangePlayerNameRequestTrans) +, m_callback(callback) +, m_param(param) +, m_playerId(playerId) +{ + StrCopy(m_newName, newName, arrsize(m_newName)); +} + +//============================================================================ +bool ChangePlayerNameRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_ChangePlayerNameRequest, + m_transId, + m_playerId, + (unsigned_ptr) m_newName, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void ChangePlayerNameRequestTrans::Post () { + m_callback( + m_result, + m_param + ); +} + +//============================================================================ +bool ChangePlayerNameRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_ChangePlayerNameReply & reply = *(const Auth2Cli_ChangePlayerNameReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + + +/***************************************************************************** +* +* SendFriendInviteTrans +* +***/ + +//============================================================================ +SendFriendInviteTrans::SendFriendInviteTrans ( + const wchar emailAddr[], + const wchar toName[], + const Uuid & inviteUuid, + FNetCliAuthSendFriendInviteCallback callback, + void * param +) : NetAuthTrans(kSendFriendInviteTrans) +, m_callback(callback) +, m_param(param) +, m_inviteUuid(inviteUuid) +{ + StrCopy(m_emailAddress, emailAddr, arrsize(m_emailAddress)); + StrCopy(m_toName, toName, arrsize(m_toName)); +} + +//============================================================================ +bool SendFriendInviteTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_SendFriendInviteRequest, + m_transId, + (unsigned_ptr) &m_inviteUuid, + (unsigned_ptr) m_emailAddress, + (unsigned_ptr) m_toName, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void SendFriendInviteTrans::Post () { + m_callback( + m_result, + m_param + ); +} + +//============================================================================ +bool SendFriendInviteTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_SendFriendInviteReply & reply = *(const Auth2Cli_SendFriendInviteReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + + +/***************************************************************************** +* +* AuthConnectedNotifyTrans +* +***/ + +//============================================================================ +void AuthConnectedNotifyTrans::Post() { + if (s_connectedCb != nil) + s_connectedCb(); +} + + +/***************************************************************************** +* +* ScoreCreateTrans +* +***/ + +//============================================================================ +ScoreCreateTrans::ScoreCreateTrans ( + unsigned ownerId, + const char* gameName, + unsigned gameType, + int value, + FNetCliAuthCreateScoreCallback callback, + void * param +) : NetAuthTrans(kScoreCreateTrans) +, m_callback(callback) +, m_param(param) +, m_ownerId(ownerId) +, m_gameType(gameType) +, m_value(value) +, m_scoreId(0) +, m_createdTime(0) +{ + StrCopy(m_gameName, gameName, arrsize(m_gameName)); +} + +//============================================================================ +bool ScoreCreateTrans::Send () { + if (!AcquireConn()) + return false; + + wchar wgameName[kMaxGameScoreNameLength]; + StrToUnicode(wgameName, m_gameName, arrsize(wgameName)); + + const unsigned_ptr msg[] = { + kCli2Auth_ScoreCreate, + m_transId, + m_ownerId, + (unsigned_ptr) wgameName, + m_gameType, + m_value + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void ScoreCreateTrans::Post () { + if (m_callback) { + m_callback( + m_result, + m_param, + m_scoreId, + m_createdTime, + m_ownerId, + m_gameName, + m_gameType, + m_value + ); + } +} + +//============================================================================ +bool ScoreCreateTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_ScoreCreateReply & reply = *(const Auth2Cli_ScoreCreateReply *) msg; + + m_scoreId = reply.scoreId; + m_createdTime = reply.createdTime; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* ScoreDeleteTrans +* +***/ + +//============================================================================ +ScoreDeleteTrans::ScoreDeleteTrans ( + unsigned scoreId, + FNetCliAuthScoreUpdateCallback callback, + void * param +) : NetAuthTrans(kScoreDeleteTrans) +, m_callback(callback) +, m_param(param) +, m_scoreId(scoreId) +{ +} + +//============================================================================ +bool ScoreDeleteTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_ScoreDelete, + m_transId, + m_scoreId, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void ScoreDeleteTrans::Post () { + if (m_callback) { + m_callback( + m_result, + m_param + ); + } +} + +//============================================================================ +bool ScoreDeleteTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_ScoreDeleteReply & reply = *(const Auth2Cli_ScoreDeleteReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* ScoreGetScoresTrans +* +***/ + +//============================================================================ +ScoreGetScoresTrans::ScoreGetScoresTrans ( + unsigned ownerId, + const char* gameName, + FNetCliAuthGetScoresCallback callback, + void * param +) : NetAuthTrans(kScoreGetScoresTrans) +, m_callback(callback) +, m_param(param) +, m_ownerId(ownerId) +, m_scores(nil) +, m_scoreCount(0) +{ + StrCopy(m_gameName, gameName, arrsize(m_gameName)); +} + +//============================================================================ +bool ScoreGetScoresTrans::Send () { + if (!AcquireConn()) + return false; + + wchar wgameName[kMaxGameScoreNameLength]; + StrToUnicode(wgameName, m_gameName, arrsize(wgameName)); + + const unsigned_ptr msg[] = { + kCli2Auth_ScoreGetScores, + m_transId, + m_ownerId, + (unsigned_ptr) wgameName + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void ScoreGetScoresTrans::Post () { + if (m_callback) { + m_callback( + m_result, + m_param, + m_scores, + m_scoreCount + ); + } +} + +//============================================================================ +bool ScoreGetScoresTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_ScoreGetScoresReply & reply = *(const Auth2Cli_ScoreGetScoresReply *) msg; + + if (reply.scoreCount > 0) { + m_scoreCount = reply.scoreCount; + m_scores = TRACKED_NEW NetGameScore[m_scoreCount]; + + byte* bufferPos = const_cast(reply.buffer); + unsigned bufferLength = reply.byteCount; + + for (unsigned i = 0; i < m_scoreCount; ++i) { + bufferLength -= m_scores[i].Read(bufferPos, bufferLength, &bufferPos); + } + } + else { + m_scoreCount = 0; + m_scores = nil; + } + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* ScoreAddPointsTrans +* +***/ + +//============================================================================ +ScoreAddPointsTrans::ScoreAddPointsTrans ( + unsigned scoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param +) : NetAuthTrans(kScoreAddPointsTrans) +, m_callback(callback) +, m_param(param) +, m_scoreId(scoreId) +, m_numPoints(numPoints) +{ +} + +//============================================================================ +bool ScoreAddPointsTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_ScoreAddPoints, + m_transId, + m_scoreId, + m_numPoints, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void ScoreAddPointsTrans::Post () { + if (m_callback) { + m_callback( + m_result, + m_param + ); + } +} + +//============================================================================ +bool ScoreAddPointsTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_ScoreAddPointsReply & reply = *(const Auth2Cli_ScoreAddPointsReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* ScoreTransferPointsTrans +* +***/ + +//============================================================================ +ScoreTransferPointsTrans::ScoreTransferPointsTrans ( + unsigned srcScoreId, + unsigned destScoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param +) : NetAuthTrans(kScoreTransferPointsTrans) +, m_callback(callback) +, m_param(param) +, m_srcScoreId(srcScoreId) +, m_destScoreId(destScoreId) +, m_numPoints(numPoints) +{ +} + +//============================================================================ +bool ScoreTransferPointsTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_ScoreTransferPoints, + m_transId, + m_srcScoreId, + m_destScoreId, + m_numPoints, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void ScoreTransferPointsTrans::Post () { + if (m_callback) { + m_callback( + m_result, + m_param + ); + } +} + +//============================================================================ +bool ScoreTransferPointsTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_ScoreTransferPointsReply & reply = *(const Auth2Cli_ScoreTransferPointsReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* ScoreSetPointsTrans +* +***/ + +//============================================================================ +ScoreSetPointsTrans::ScoreSetPointsTrans ( + unsigned scoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param +) : NetAuthTrans(kScoreSetPointsTrans) +, m_callback(callback) +, m_param(param) +, m_scoreId(scoreId) +, m_numPoints(numPoints) +{ +} + +//============================================================================ +bool ScoreSetPointsTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_ScoreSetPoints, + m_transId, + m_scoreId, + m_numPoints, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void ScoreSetPointsTrans::Post () { + if (m_callback) { + m_callback( + m_result, + m_param + ); + } +} + +//============================================================================ +bool ScoreSetPointsTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_ScoreSetPointsReply & reply = *(const Auth2Cli_ScoreSetPointsReply *) msg; + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + + +/***************************************************************************** +* +* ScoreGetRanksTrans +* +***/ + +//============================================================================ +ScoreGetRanksTrans::ScoreGetRanksTrans ( + unsigned ownerId, + unsigned scoreGroup, + unsigned parentFolderId, + const char * cGameName, + unsigned timePeriod, + unsigned numResults, + unsigned pageNumber, + bool sortDesc, + FNetCliAuthGetRanksCallback callback, + void * param +) : NetAuthTrans(kScoreGetRanksTrans) +, m_callback(callback) +, m_param(param) +, m_ownerId(ownerId) +, m_scoreGroup(scoreGroup) +, m_parentFolderId(parentFolderId) +, m_timePeriod(timePeriod) +, m_numResults(numResults) +, m_pageNumber(pageNumber) +, m_sortDesc(sortDesc) +{ + StrToUnicode(m_gameName, cGameName, arrsize(m_gameName)); +} + +//============================================================================ +bool ScoreGetRanksTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Auth_ScoreGetRanks, + m_transId, + m_ownerId, + m_scoreGroup, + m_parentFolderId, + (unsigned_ptr) m_gameName, + m_timePeriod, + m_numResults, + m_pageNumber, + m_sortDesc, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void ScoreGetRanksTrans::Post () { + if (m_callback) { + m_callback( + m_result, + m_param, + m_ranks, + m_rankCount + ); + } +} + +//============================================================================ +bool ScoreGetRanksTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const Auth2Cli_ScoreGetRanksReply & reply = *(const Auth2Cli_ScoreGetRanksReply *) msg; + + if (reply.rankCount > 0) { + m_rankCount = reply.rankCount; + m_ranks = TRACKED_NEW NetGameRank[m_rankCount]; + + byte* bufferPos = const_cast(reply.buffer); + unsigned bufferLength = reply.byteCount; + + for (unsigned i = 0; i < m_rankCount; ++i) { + bufferLength -= m_ranks[i].Read(bufferPos, bufferLength, &bufferPos); + } + } + else { + m_rankCount = 0; + m_ranks = nil; + } + + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +} using namespace Auth; + + +/***************************************************************************** +* +* NetAuthTrans +* +***/ + +//============================================================================ +NetAuthTrans::NetAuthTrans (ETransType transType) +: NetTrans(kNetProtocolCli2Auth, transType) +, m_conn(nil) +{ +} + +//============================================================================ +NetAuthTrans::~NetAuthTrans () { + ReleaseConn(); +} + +//============================================================================ +bool NetAuthTrans::AcquireConn () { + if (!m_conn) + m_conn = GetConnIncRef("AcquireConn"); + return m_conn != nil; +} + +//============================================================================ +void NetAuthTrans::ReleaseConn () { + if (m_conn) { + m_conn->DecRef("AcquireConn"); + m_conn = nil; + } +} + + +/***************************************************************************** +* +* Protected functions +* +***/ + +//============================================================================ +void AuthInitialize () { + s_running = true; + NetMsgProtocolRegister( + kNetProtocolCli2Auth, + false, + s_send, arrsize(s_send), + s_recv, arrsize(s_recv), + kDhGValue, + BigNum(sizeof(kDhXData), kDhXData), + BigNum(sizeof(kDhNData), kDhNData) + ); +} + +//============================================================================ +void AuthDestroy (bool wait) { + s_running = false; + + s_bufRcvdCb = nil; + s_connectedCb = nil; + s_vaultNodeChangedHandler = nil; + s_vaultNodeAddedHandler = nil; + s_vaultNodeRemovedHandler = nil; + s_vaultNodeDeletedHandler = nil; + s_notifyNewBuildHandler = nil; + + + NetTransCancelByProtocol( + kNetProtocolCli2Auth, + kNetErrRemoteShutdown + ); + NetMsgProtocolDestroy( + kNetProtocolCli2Auth, + false + ); + + s_critsect.Enter(); + { + while (CliAuConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_active = nil; + } + s_critsect.Leave(); + + if (!wait) + return; + + while (s_perf[kPerfConnCount]) { + NetTransUpdate(); + AsyncSleep(10); + } +} + +//============================================================================ +bool AuthQueryConnected () { + bool result; + s_critsect.Enter(); + { + if (nil != (result = s_active)) + result &= (nil != s_active->cli); + } + s_critsect.Leave(); + return result; +} + +//============================================================================ +unsigned AuthGetConnId () { + unsigned connId; + s_critsect.Enter(); + connId = (s_active) ? s_active->seq : 0; + s_critsect.Leave(); + return connId; +} + +//============================================================================ +void AuthPingEnable (bool enable) { + s_perf[kPingDisabled] = !enable; + s_critsect.Enter(); + for (;;) { + if (!s_active) + break; + if (enable) + s_active->AutoPing(); + else + s_active->StopAutoPing(); + break; + } + s_critsect.Leave(); +} + + +} using namespace Ngl; + + +/***************************************************************************** +* +* Exported functions +* +***/ + +//============================================================================ +void NetCliAuthStartConnect ( + const wchar * authAddrList[], + unsigned authAddrCount +) { + // TEMP: Only connect to one auth server until we fill out this module + // to choose the "best" auth connection. + authAddrCount = min(authAddrCount, 1); + + for (unsigned i = 0; i < authAddrCount; ++i) { + // Do we need to lookup the address? + const wchar * name = authAddrList[i]; + while (unsigned ch = *name) { + ++name; + if (!(isdigit(ch) || ch == L'.' || ch == L':')) { + AsyncCancelId cancelId; + AsyncAddressLookupName( + &cancelId, + AsyncLookupCallback, + authAddrList[i], + kNetDefaultClientPort, + nil + ); + break; + } + } + if (!name[0]) { + NetAddress addr; + NetAddressFromString(&addr, authAddrList[i], kNetDefaultClientPort); + Connect(authAddrList[i], addr); + } + } +} + +//============================================================================ +bool NetCliAuthQueryConnected () { + + return AuthQueryConnected(); +} + +//============================================================================ +void NetCliAuthAutoReconnectEnable (bool enable) { + + s_perf[kAutoReconnectDisabled] = !enable; +} + +//============================================================================ +void NetCliAuthDisconnect () { + + s_critsect.Enter(); + { + while (CliAuConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_active = nil; + } + s_critsect.Leave(); +} + +//============================================================================ +void NetCliAuthUnexpectedDisconnect () { + s_critsect.Enter(); + { + if (s_active && s_active->sock) + AsyncSocketDisconnect(s_active->sock, true); + } + s_critsect.Leave(); +} + +//============================================================================ +void NetCliAuthSetConnectCallback ( + FNetCliAuthConnectCallback callback +) { + s_connectedCb = callback; +} + +//============================================================================ +void NetCliAuthPingRequest ( + unsigned pingTimeMs, + unsigned payloadBytes, + const void * payload, + FNetCliAuthPingRequestCallback callback, + void * param +) { + PingRequestTrans * trans = NEW(PingRequestTrans)( + callback, + param, + pingTimeMs, + payloadBytes, + payload + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthAccountExistsRequest ( + const wchar accountName[], + FNetCliAuthAccountExistsRequestCallback callback, + void * param +) { + AccountExistsRequestTrans * trans = NEW(AccountExistsRequestTrans)( + callback, + param, + accountName + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthLoginRequest ( + const wchar accountName[], + const ShaDigest * accountNamePassHash, + const wchar authToken[], + const wchar os[], + FNetCliAuthLoginRequestCallback callback, + void * param +) { + // Cache updated login info if provided. + if (accountName) + StrCopy(s_accountName, accountName, arrsize(s_accountName)); + if (accountNamePassHash) + s_accountNamePassHash = *accountNamePassHash; + if (authToken) + StrCopy(s_authToken, authToken, arrsize(s_authToken)); + if (os) + StrCopy(s_os, os, arrsize(s_os)); + + LoginRequestTrans * trans = NEW(LoginRequestTrans)(callback, param); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthAgeRequest ( + const wchar ageName[], + const Uuid & ageInstId, + FNetCliAuthAgeRequestCallback callback, + void * param +) { + AgeRequestTrans * trans = NEW(AgeRequestTrans)( + ageName, + ageInstId, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthGetEncryptionKey ( + UInt32 key[], + unsigned size +) { + unsigned memSize = min(arrsize(s_encryptionKey), size); + memSize *= sizeof(UInt32); + MemCopy(key, s_encryptionKey, memSize); +} + +//============================================================================ +void NetCliAuthAccountCreateRequest ( + const wchar accountName[], + const wchar password[], + unsigned accountFlags, + unsigned billingType, + FNetCliAuthAccountCreateRequestCallback callback, + void * param +) { + AccountCreateRequestTrans * trans = NEW(AccountCreateRequestTrans)( + accountName, + password, + accountFlags, + billingType, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthAccountCreateFromKeyRequest ( + const wchar accountName[], + const wchar password[], + Uuid key, + unsigned billingType, + FNetCliAuthAccountCreateFromKeyRequestCallback callback, + void * param +) { + AccountCreateFromKeyRequestTrans * trans = NEW(AccountCreateFromKeyRequestTrans)( + accountName, + password, + key, + billingType, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthPlayerCreateRequest ( + const wchar playerName[], + const wchar avatarShape[], + const wchar friendInvite[], + FNetCliAuthPlayerCreateRequestCallback callback, + void * param +) { + wchar name[kMaxPlayerNameLength]; + StrCopy(name, playerName, arrsize(name)); + ENetError error = FixupPlayerName(name); + if (IS_NET_ERROR(error)) { + NetCliAuthPlayerInfo playerInfo; + callback(error, param, playerInfo); + } + else { + PlayerCreateRequestTrans * trans = NEW(PlayerCreateRequestTrans)( + name, + avatarShape, + friendInvite, + callback, + param + ); + NetTransSend(trans); + } +} + +//============================================================================ +void NetCliAuthPlayerDeleteRequest ( + unsigned playerId, + FNetCliAuthPlayerDeleteRequestCallback callback, + void * param +) { + PlayerDeleteRequestTrans * trans = NEW(PlayerDeleteRequestTrans)( + playerId, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthUpgradeVisitorRequest ( + unsigned playerId, + FNetCliAuthUpgradeVisitorRequestCallback callback, + void * param +) { + UpgradeVisitorRequestTrans * trans = NEW(UpgradeVisitorRequestTrans)( + playerId, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthSetCCRLevel ( + unsigned ccrLevel +) { + CliAuConn * conn = GetConnIncRef("SetCCRLevel"); + if (!conn) + return; + + const unsigned_ptr msg[] = { + kCli2Auth_ClientSetCCRLevel, + ccrLevel, + }; + + conn->Send(msg, arrsize(msg)); + conn->DecRef("SetCCRLevel"); +} + +//============================================================================ +void NetCliAuthSetPlayerRequest ( + unsigned playerInt, + FNetCliAuthSetPlayerRequestCallback callback, + void * param +) { + SetPlayerRequestTrans * trans = NEW(SetPlayerRequestTrans)( + playerInt, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthSetAgePublic ( + unsigned ageInfoId, + bool publicOrNot +) { + CliAuConn * conn = GetConnIncRef("SetAgePublic"); + if (!conn) + return; + + const unsigned_ptr msg[] = { + kCli2Auth_SetAgePublic, + ageInfoId, + publicOrNot, + }; + + conn->Send(msg, arrsize(msg)); + + conn->DecRef("SetAgePublic"); +} + +//============================================================================ +void NetCliAuthGetPublicAgeList ( + const wchar ageName[], + FNetCliAuthGetPublicAgeListCallback callback, + void * param +) { + GetPublicAgeListTrans * trans = NEW(GetPublicAgeListTrans)( + ageName, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthAccountChangePasswordRequest ( + const wchar accountName[], + const wchar password[], + FNetCliAuthAccountChangePasswordRequestCallback callback, + void * param +) { + AccountChangePasswordRequestTrans * trans = NEW(AccountChangePasswordRequestTrans)( + accountName, + password, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthAccountSetRolesRequest ( + const wchar accountName[], + unsigned accountFlags, + FNetCliAuthAccountSetRolesRequestCallback callback, + void * param +) { + AccountSetRolesRequestTrans * trans = NEW(AccountSetRolesRequestTrans)( + accountName, + accountFlags, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthAccountSetBillingTypeRequest ( + const wchar accountName[], + unsigned billingType, + FNetCliAuthAccountSetBillingTypeRequestCallback callback, + void * param +) { + AccountSetBillingTypeRequestTrans * trans = NEW(AccountSetBillingTypeRequestTrans)( + accountName, + billingType, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthAccountActivateRequest ( + const Uuid & activationKey, + FNetCliAuthAccountActivateRequestCallback callback, + void * param +) { + AccountActivateRequestTrans * trans = NEW(AccountActivateRequestTrans)( + activationKey, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthFileListRequest ( + const wchar dir[], + const wchar ext[], + FNetCliAuthFileListRequestCallback callback, + void * param +) { + FileListRequestTrans * trans = NEW(FileListRequestTrans)( + callback, + param, + dir, + ext + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthFileRequest ( + const wchar filename[], + hsStream * writer, + FNetCliAuthFileRequestCallback callback, + void * param +) { + FileDownloadRequestTrans * trans = NEW(FileDownloadRequestTrans)( + callback, + param, + filename, + writer + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthVaultSetRecvNodeChangedHandler ( + FNetCliAuthVaultNodeChanged handler +) { + s_vaultNodeChangedHandler = handler; +} + +//============================================================================ +void NetCliAuthVaultSetRecvNodeAddedHandler ( + FNetCliAuthVaultNodeAdded handler +) { + s_vaultNodeAddedHandler = handler; +} + +//============================================================================ +void NetCliAuthVaultSetRecvNodeRemovedHandler ( + FNetCliAuthVaultNodeRemoved handler +) { + s_vaultNodeRemovedHandler = handler; +} + +//============================================================================ +void NetCliAuthVaultSetRecvNodeDeletedHandler ( + FNetCliAuthVaultNodeDeleted handler +) { + s_vaultNodeDeletedHandler = handler; +} + +//============================================================================ +void NetCliAuthVaultNodeCreate ( + NetVaultNode * templateNode, + FNetCliAuthVaultNodeCreated callback, + void * param +) { + VaultCreateNodeTrans * trans = NEWZERO(VaultCreateNodeTrans)( + templateNode, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthVaultNodeFetch ( + unsigned nodeId, + FNetCliAuthVaultNodeFetched callback, + void * param +) { + VaultFetchNodeTrans * trans = NEWZERO(VaultFetchNodeTrans)( + nodeId, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthVaultNodeFind ( + NetVaultNode * templateNode, + FNetCliAuthVaultNodeFind callback, + void * param +) { + VaultFindNodeTrans * trans = NEWZERO(VaultFindNodeTrans)( + templateNode, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +unsigned NetCliAuthVaultNodeSave ( + NetVaultNode * node, + FNetCliAuthVaultNodeSaveCallback callback, + void * param +) { + ASSERTMSG(!(node->dirtyFlags & NetVaultNode::kNodeType), "Node type may not be changed"); + + // Clear dirty bits of read-only fields before we write the node to the msg buffer + node->dirtyFlags &= ~( + NetVaultNode::kNodeId | + NetVaultNode::kNodeType | + NetVaultNode::kCreatorAcct | + NetVaultNode::kCreatorId | + NetVaultNode::kCreateTime + ); + + if (!node->dirtyFlags) + return 0; + + if (!node->nodeId) + return 0; + + // force sending of the nodeType value, since the auth needs it. + // auth will clear the field before sending it on to the vault. + node->dirtyFlags |= NetVaultNode::kNodeType; + + // We're definitely saving this node, so assign a revisionId + node->revisionId = GuidGenerate(); + + ARRAY(byte) buffer; + unsigned bytes = node->Write_LCS(&buffer, NetVaultNode::kRwDirtyOnly | NetVaultNode::kRwUpdateDirty); + + VaultSaveNodeTrans * trans = NEWZERO(VaultSaveNodeTrans)( + node->nodeId, + node->revisionId, + buffer.Count(), + buffer.Ptr(), + callback, + param + ); + NetTransSend(trans); + return bytes; +} + +//============================================================================ +void NetCliAuthVaultNodeDelete ( + unsigned nodeId +) { + ref(nodeId); + hsAssert(false, "eric, implement me"); +} + +//============================================================================ +void NetCliAuthVaultNodeAdd ( + unsigned parentId, + unsigned childId, + unsigned ownerId, + FNetCliAuthVaultNodeAddCallback callback, + void * param +) { + VaultAddNodeTrans * trans = NEWZERO(VaultAddNodeTrans)( + parentId, + childId, + ownerId, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthVaultNodeRemove ( + unsigned parentId, + unsigned childId, + FNetCliAuthVaultNodeRemoveCallback callback, + void * param +) { + VaultRemoveNodeTrans * trans = NEWZERO(VaultRemoveNodeTrans)( + parentId, + childId, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthVaultFetchNodeRefs ( + unsigned nodeId, + FNetCliAuthVaultNodeRefsFetched callback, + void * param +) { + VaultFetchNodeRefsTrans * trans = NEWZERO(VaultFetchNodeRefsTrans)( + nodeId, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthVaultSetSeen ( + unsigned parentId, + unsigned childId, + bool seen +) { + CliAuConn * conn = GetConnIncRef("SetSeen"); + if (!conn) + return; + + const unsigned_ptr msg[] = { + kCli2Auth_VaultSetSeen, + parentId, + childId, + seen, + }; + + conn->Send(msg, arrsize(msg)); + + conn->DecRef("SetSeen"); +} + +//============================================================================ +void NetCliAuthVaultSendNode ( + unsigned srcNodeId, + unsigned dstPlayerId +) { + CliAuConn * conn = GetConnIncRef("SendNode"); + if (!conn) + return; + + const unsigned_ptr msg[] = { + kCli2Auth_VaultSendNode, + srcNodeId, + dstPlayerId, + }; + + conn->Send(msg, arrsize(msg)); + + conn->DecRef("SendNode"); +} + +//============================================================================ +void NetCliAuthVaultInitAge ( + const Uuid & ageInstId, // optional. is used in match + const Uuid & parentAgeInstId, // optional. is used in match + const wchar ageFilename[], // optional. is used in match + const wchar ageInstName[], // optional. not used in match + const wchar ageUserName[], // optional. not used in match + const wchar ageDesc[], // optional. not used in match + unsigned ageSequenceNumber, // optional. not used in match + unsigned ageLanguage, // optional. not used in match + FNetCliAuthAgeInitCallback callback, // optional + void * param // optional +) { + VaultInitAgeTrans * trans = NEWZERO(VaultInitAgeTrans)( + callback, + param, + ageInstId, + parentAgeInstId, + ageFilename, + ageInstName, + ageUserName, + ageDesc, + ageSequenceNumber, + ageLanguage + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthSetRecvBufferHandler ( + FNetCliAuthRecvBufferHandler handler +) { + s_bufRcvdCb = handler; +} + +//============================================================================ +void NetCliAuthSendCCRPetition ( + const wchar * petitionText +) { + ref(petitionText); + hsAssert(false, "eric, implement me."); +} + +//============================================================================ +void NetCliAuthPropagateBuffer ( + unsigned type, + unsigned bytes, + const byte buffer[] +) { + CliAuConn * conn = GetConnIncRef("PropBuffer"); + if (!conn) + return; + + const unsigned_ptr msg[] = { + kCli2Auth_PropagateBuffer, + type, + bytes, + (unsigned_ptr) buffer, + }; + + conn->Send(msg, arrsize(msg)); + + conn->DecRef("PropBuffer"); + +} + +//============================================================================ +void NetCliAuthLogPythonTraceback (const wchar traceback[]) { + CliAuConn * conn = GetConnIncRef("LogTraceback"); + if (!conn) + return; + + const unsigned_ptr msg[] = { + kCli2Auth_LogPythonTraceback, + (unsigned_ptr) traceback + }; + + conn->Send(msg, arrsize(msg)); + + conn->DecRef("LogTraceback"); +} + + +//============================================================================ +void NetCliAuthLogStackDump (const wchar stackdump[]) { + CliAuConn * conn = GetConnIncRef("LogStackDump"); + if (!conn) + return; + + const unsigned_ptr msg[] = { + kCli2Auth_LogStackDump, + (unsigned_ptr) stackdump + }; + + conn->Send(msg, arrsize(msg)); + + conn->DecRef("LogStackDump"); +} + +//============================================================================ +void NetCliAuthLogClientDebuggerConnect () { + CliAuConn * conn = GetConnIncRef(""); + if (!conn) + return; + + unsigned nothing = 0; + + const unsigned_ptr msg[] = { + kCli2Auth_LogClientDebuggerConnect, + nothing + }; + + conn->Send(msg, arrsize(msg)); + + conn->DecRef(); +} + +//============================================================================ +void NetCliAuthSetNotifyNewBuildHandler (FNotifyNewBuildHandler handler) { + + s_notifyNewBuildHandler = handler; +} + +//============================================================================ +void NetCliAuthSetPlayerBanStatusRequest ( + unsigned playerId, + unsigned banned, + FNetCliAuthSetPlayerBanStatusRequestCallback callback, + void * param +) { + SetPlayerBanStatusRequestTrans * trans = NEW(SetPlayerBanStatusRequestTrans)( + playerId, + banned, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthKickPlayer ( + unsigned playerId +) { + CliAuConn * conn = GetConnIncRef("KickPlayer"); + if (!conn) + return; + + const unsigned_ptr msg[] = { + kCli2Auth_KickPlayer, + playerId + }; + + conn->Send(msg, arrsize(msg)); + conn->DecRef("KickPlayer"); +} + +//============================================================================ +void NetCliAuthChangePlayerNameRequest ( + unsigned playerId, + const wchar newName[], + FNetCliAuthChangePlayerNameRequestCallback callback, + void * param +) { + ChangePlayerNameRequestTrans * trans = NEW(ChangePlayerNameRequestTrans)( + playerId, + newName, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthSendFriendInvite ( + const wchar emailAddress[], + const wchar toName[], + const Uuid& inviteUuid, + FNetCliAuthSendFriendInviteCallback callback, + void * param +) { + SendFriendInviteTrans * trans = NEW(SendFriendInviteTrans)( + emailAddress, + toName, + inviteUuid, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthScoreCreate ( + unsigned ownerId, + const char* gameName, + unsigned gameType, + int value, + FNetCliAuthCreateScoreCallback callback, + void * param +) { + ScoreCreateTrans * trans = NEW(ScoreCreateTrans)( + ownerId, + gameName, + gameType, + value, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthScoreDelete( + unsigned scoreId, + FNetCliAuthScoreUpdateCallback callback, + void * param +) { + ScoreDeleteTrans * trans = NEW(ScoreDeleteTrans)( + scoreId, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthScoreGetScores( + unsigned ownerId, + const char* gameName, + FNetCliAuthGetScoresCallback callback, + void * param +) { + ScoreGetScoresTrans * trans = NEW(ScoreGetScoresTrans)( + ownerId, + gameName, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthScoreAddPoints( + unsigned scoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param +) { + ScoreAddPointsTrans * trans = NEW(ScoreAddPointsTrans)( + scoreId, + numPoints, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthScoreTransferPoints( + unsigned srcScoreId, + unsigned destScoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param +) { + ScoreTransferPointsTrans * trans = NEW(ScoreTransferPointsTrans)( + srcScoreId, + destScoreId, + numPoints, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthScoreSetPoints( + unsigned scoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param +) { + ScoreSetPointsTrans * trans = NEW(ScoreSetPointsTrans)( + scoreId, + numPoints, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliAuthScoreGetRankList( + unsigned ownerId, + unsigned scoreGroup, + unsigned parentFolderId, + const char * gameName, + unsigned timePeriod, + unsigned numResults, + unsigned pageNumber, + bool sortDesc, + FNetCliAuthGetRanksCallback callback, + void * param +) { + ScoreGetRanksTrans * trans = NEW(ScoreGetRanksTrans)( + ownerId, + scoreGroup, + parentFolderId, + gameName, + timePeriod, + numResults, + pageNumber, + sortDesc, + callback, + param + ); + NetTransSend(trans); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAuth.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAuth.h new file mode 100644 index 00000000..885c703b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAuth.h @@ -0,0 +1,700 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAuth.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLAUTH_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglAuth.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLAUTH_H + + +/***************************************************************************** +* +* Client auth functions +* +***/ + +//============================================================================ +// Connect +//============================================================================ +void NetCliAuthStartConnect ( + const wchar * authAddrList[], + unsigned authAddrCount +); +bool NetCliAuthQueryConnected (); +void NetCliAuthAutoReconnectEnable (bool enable); // is enabled by default + +// Called after the auth/client connection is encrypted +typedef void (*FNetCliAuthConnectCallback)(); +void NetCliAuthSetConnectCallback ( + FNetCliAuthConnectCallback callback +); + + +//============================================================================ +// Disconnect +//============================================================================ +void NetCliAuthDisconnect (); +void NetCliAuthUnexpectedDisconnect (); + +//============================================================================ +// Ping +//============================================================================ +typedef void (*FNetCliAuthPingRequestCallback)( + ENetError result, + void * param, + unsigned pingAtMs, + unsigned replyAtMs, + unsigned payloadbytes, + const byte payload[] +); +void NetCliAuthPingRequest ( + unsigned pingTimeMs, + unsigned payloadBytes, // max 64k (pnNetCli enforced upon send) + const void * payload, + FNetCliAuthPingRequestCallback callback, + void * param +); + +//============================================================================ +// AccountExists +//============================================================================ +typedef void (*FNetCliAuthAccountExistsRequestCallback)( + ENetError result, + void * param, + bool accountExists +); +void NetCliAuthAccountExistsRequest ( + const wchar accountName[], + FNetCliAuthAccountExistsRequestCallback callback, + void * param +); + +//============================================================================ +// Login +//============================================================================ +struct NetCliAuthPlayerInfo { + unsigned playerInt; + wchar playerName[kMaxPlayerNameLength]; + wchar avatarShape[kMaxVaultNodeStringLength]; + unsigned playerFlags; + unsigned explorer; +}; + +typedef void (*FNetCliAuthLoginRequestCallback)( + ENetError result, + void * param, + const Uuid & accountId, + unsigned accountFlags, + unsigned billingType, + const NetCliAuthPlayerInfo playerInfoArr[], + unsigned playerCount +); +void NetCliAuthLoginRequest ( + const wchar accountName[], // nil --> reuse previous acct name + const ShaDigest * accountNamePassHash, // nil --> reuse previous acct pass + const wchar authToken[], // nil --> reuse previous auth token + const wchar os[], // nil --> reuse previous os + FNetCliAuthLoginRequestCallback callback, + void * param +); + +//============================================================================ +// Set Player +//============================================================================ +typedef void (*FNetCliAuthSetPlayerRequestCallback)( + ENetError result, + void * param +); +void NetCliAuthSetPlayerRequest ( + unsigned playerInt, + FNetCliAuthSetPlayerRequestCallback callback, + void * param +); + +//============================================================================ +// Create Account +//============================================================================ +typedef void (*FNetCliAuthAccountCreateRequestCallback)( + ENetError result, + void * param, + const Uuid & accountId +); +void NetCliAuthAccountCreateRequest ( + const wchar accountName[], + const wchar accountPass[], + unsigned accountFlags, + unsigned billingType, + FNetCliAuthAccountCreateRequestCallback callback, + void * param +); + +//============================================================================ +// Create Account From Key +//============================================================================ +typedef void (*FNetCliAuthAccountCreateFromKeyRequestCallback)( + ENetError result, + void * param, + const Uuid & accountId, + const Uuid & activationKey +); +void NetCliAuthAccountCreateFromKeyRequest ( + const wchar accountName[], + const wchar accountPass[], + Uuid key, + unsigned billingType, + FNetCliAuthAccountCreateFromKeyRequestCallback callback, + void * param +); + +//============================================================================ +// Create Player +//============================================================================ +typedef void (*FNetCliAuthPlayerCreateRequestCallback)( + ENetError result, + void * param, + const NetCliAuthPlayerInfo & playerInfo +); +void NetCliAuthPlayerCreateRequest ( + const wchar playerName[], + const wchar avatarShape[], + const wchar friendInvite[], + FNetCliAuthPlayerCreateRequestCallback callback, + void * param +); + +//============================================================================ +// Delete Player +//============================================================================ +typedef void (*FNetCliAuthPlayerDeleteRequestCallback)( + ENetError result, + void * param +); +void NetCliAuthPlayerDeleteRequest ( + unsigned playerId, + FNetCliAuthPlayerDeleteRequestCallback callback, + void * param +); + +//============================================================================ +// Upgrade Visitor +//============================================================================ +typedef void (*FNetCliAuthUpgradeVisitorRequestCallback)( + ENetError result, + void * param +); +void NetCliAuthUpgradeVisitorRequest ( + unsigned playerId, + FNetCliAuthUpgradeVisitorRequestCallback callback, + void * param +); + +//============================================================================ +// SetCCRLevel +//============================================================================ +void NetCliAuthSetCCRLevel ( + unsigned ccrLevel +); + +//============================================================================ +// SetAgePublic +//============================================================================ +void NetCliAuthSetAgePublic ( + unsigned ageInfoId, + bool publicOrNot +); + +//============================================================================ +// GetPublicAgeList +//============================================================================ +struct NetAgeInfo; +typedef void (*FNetCliAuthGetPublicAgeListCallback)( + ENetError result, + void * param, + const ARRAY(NetAgeInfo) & ages +); +void NetCliAuthGetPublicAgeList ( + const wchar ageName[], + FNetCliAuthGetPublicAgeListCallback callback, + void * param +); + +//============================================================================ +// Change Password +//============================================================================ +typedef void (*FNetCliAuthAccountChangePasswordRequestCallback)( + ENetError result, + void * param +); +void NetCliAuthAccountChangePasswordRequest ( + const wchar accountName[], + const wchar accountPass[], + FNetCliAuthAccountChangePasswordRequestCallback callback, + void * param +); + +//============================================================================ +// Set Account Roles +//============================================================================ +typedef void (*FNetCliAuthAccountSetRolesRequestCallback)( + ENetError result, + void * param +); +void NetCliAuthAccountSetRolesRequest ( + const wchar accountName[], + unsigned accountFlags, + FNetCliAuthAccountSetRolesRequestCallback callback, + void * param +); + +//============================================================================ +// Set Billing Type +//============================================================================ +typedef void (*FNetCliAuthAccountSetBillingTypeRequestCallback)( + ENetError result, + void * param +); +void NetCliAuthAccountSetBillingTypeRequest ( + const wchar accountName[], + unsigned billingType, + FNetCliAuthAccountSetBillingTypeRequestCallback callback, + void * param +); + +//============================================================================ +// Account Activate +//============================================================================ +typedef void (*FNetCliAuthAccountActivateRequestCallback)( + ENetError result, + void * param +); +void NetCliAuthAccountActivateRequest ( + const Uuid & activationKey, + FNetCliAuthAccountActivateRequestCallback callback, + void * param +); + +//============================================================================ +// Age +//============================================================================ +typedef void (*FNetCliAuthAgeRequestCallback)( + ENetError result, + void * param, + unsigned ageMcpId, + unsigned ageVaultId, + const Uuid & ageInstId, + NetAddressNode gameAddr +); +void NetCliAuthAgeRequest ( + const wchar ageName[], // L"Teledahn" + const Uuid & ageInstId, + FNetCliAuthAgeRequestCallback callback, + void * param +); + +//============================================================================ +// Secure File Encryption Key +//============================================================================ +void NetCliAuthGetEncryptionKey ( + UInt32 key[], + unsigned size +); + +//============================================================================ +// File List +//============================================================================ +struct NetCliAuthFileInfo { + wchar filename[MAX_PATH]; + unsigned filesize; +}; +typedef void (*FNetCliAuthFileListRequestCallback)( + ENetError result, + void * param, + const NetCliAuthFileInfo infoArr[], + unsigned infoCount +); +void NetCliAuthFileListRequest ( + const wchar dir[], + const wchar ext[], + FNetCliAuthFileListRequestCallback callback, + void * param +); + +//============================================================================ +// File Download +//============================================================================ +typedef void (*FNetCliAuthFileRequestCallback)( + ENetError result, + void * param, + const wchar filename[], + hsStream * writer +); +void NetCliAuthFileRequest ( + const wchar filename[], + hsStream * writer, + FNetCliAuthFileRequestCallback callback, + void * param +); + +//============================================================================ +// Vault Operations +//============================================================================ +struct NetVaultNode; +struct NetVaultNodeRef; + +// VaultNodeChanged +typedef void (*FNetCliAuthVaultNodeChanged)( + unsigned nodeId, + const Uuid & revisionId +); +void NetCliAuthVaultSetRecvNodeChangedHandler ( + FNetCliAuthVaultNodeChanged handler +); +// VaultNodeAdded +typedef void (*FNetCliAuthVaultNodeAdded)( + unsigned parentId, + unsigned childId, + unsigned ownerId +); +void NetCliAuthVaultSetRecvNodeAddedHandler ( + FNetCliAuthVaultNodeAdded handler +); +// VaultNodeRemoved +typedef void (*FNetCliAuthVaultNodeRemoved)( + unsigned parentId, + unsigned childId +); +void NetCliAuthVaultSetRecvNodeRemovedHandler ( + FNetCliAuthVaultNodeRemoved handler +); +// VaultNodeDeleted +typedef void (*FNetCliAuthVaultNodeDeleted)( + unsigned nodeId +); +void NetCliAuthVaultSetRecvNodeDeletedHandler ( + FNetCliAuthVaultNodeDeleted handler +); +// VaultNodeAdd +typedef void (*FNetCliAuthVaultNodeAddCallback)( + ENetError result, + void * param +); +void NetCliAuthVaultNodeAdd ( + unsigned parentId, + unsigned childId, + unsigned ownerId, + FNetCliAuthVaultNodeAddCallback callback, + void * param +); +// VaultNodeRemove +typedef void (*FNetCliAuthVaultNodeRemoveCallback)( + ENetError result, + void * param +); +void NetCliAuthVaultNodeRemove ( + unsigned parentId, + unsigned childId, + FNetCliAuthVaultNodeRemoveCallback callback, + void * param +); +// VaultNodeCreate +typedef void (*FNetCliAuthVaultNodeCreated)( + ENetError result, + void * param, + unsigned nodeId +); +void NetCliAuthVaultNodeCreate ( + NetVaultNode * templateNode, + FNetCliAuthVaultNodeCreated callback, + void * param +); +// VaultNodeFetch +typedef void (*FNetCliAuthVaultNodeFetched)( + ENetError result, + void * param, + NetVaultNode * node +); +void NetCliAuthVaultNodeFetch ( + unsigned nodeId, + FNetCliAuthVaultNodeFetched callback, + void * param +); +// VaultNodeFind +typedef void (*FNetCliAuthVaultNodeFind)( + ENetError result, + void * param, + unsigned nodeIdCount, + const unsigned nodeIds[] +); +void NetCliAuthVaultNodeFind ( + NetVaultNode * templateNode, + FNetCliAuthVaultNodeFind callback, + void * param +); +// VaultNodeSave +typedef void (*FNetCliAuthVaultNodeSaveCallback)( + ENetError result, + void * param +); +unsigned NetCliAuthVaultNodeSave ( // returns number of bytes written + NetVaultNode * node, + FNetCliAuthVaultNodeSaveCallback callback, + void * param +); +void NetCliAuthVaultNodeDelete ( + unsigned nodeId +); +// FetchRefs (a vault structure only; no data) +typedef void (*FNetCliAuthVaultNodeRefsFetched)( + ENetError result, + void * param, + NetVaultNodeRef * refs, + unsigned refCount +); +void NetCliAuthVaultFetchNodeRefs ( + unsigned nodeId, + FNetCliAuthVaultNodeRefsFetched callback, + void * param +); +void NetCliAuthVaultSetSeen ( + unsigned parentId, + unsigned childId, + bool seen +); + +void NetCliAuthVaultSendNode ( + unsigned srcNodeId, + unsigned dstPlayerId +); + +// Initialize an age vault. Will find existing match in db, or create a new age vault structure. +typedef void (*FNetCliAuthAgeInitCallback) ( + ENetError result, + void * param, + unsigned ageVaultId, + unsigned ageInfoVaultId +); +void NetCliAuthVaultInitAge ( + const Uuid & ageInstId, // optional. is used in match + const Uuid & parentAgeInstId, // optional. is used in match + const wchar ageFilename[], // optional. is used in match + const wchar ageInstName[], // optional. not used in match + const wchar ageUserName[], // optional. not used in match + const wchar ageDesc[], // optional. not used in match + unsigned ageSequenceNumber, // optional. not used in match + unsigned ageLanguage, // optional. not used in match + FNetCliAuthAgeInitCallback callback, // optional + void * param // optional +); + +void NetCliAuthLogPythonTraceback (const wchar traceback[]); + +void NetCliAuthLogStackDump (const wchar stackdump[]); +void NetCliAuthLogClientDebuggerConnect (); + +//============================================================================ +// SetPlayerBanStatusRequest +//============================================================================ +typedef void (*FNetCliAuthSetPlayerBanStatusRequestCallback)( + ENetError result, + void * param +); +void NetCliAuthSetPlayerBanStatusRequest ( + unsigned playerId, + unsigned banned, + FNetCliAuthSetPlayerBanStatusRequestCallback callback, + void * param +); + +//============================================================================ +// KickPlayerRequest +//============================================================================ +void NetCliAuthKickPlayer ( + unsigned playerId +); + +//============================================================================ +// ChangePlayerNameRequest +//============================================================================ +typedef void (*FNetCliAuthChangePlayerNameRequestCallback)( + ENetError result, + void * param +); +void NetCliAuthChangePlayerNameRequest ( + unsigned playerId, + const wchar newName[], + FNetCliAuthChangePlayerNameRequestCallback callback, + void * param +); + +//============================================================================ +// CCRPetition +//============================================================================ +void NetCliAuthSendCCRPetition ( + const wchar * petitionText +); + +//============================================================================ +// SendFriendInvite +//============================================================================ +typedef void (*FNetCliAuthSendFriendInviteCallback)( + ENetError result, + void * param +); + +void NetCliAuthSendFriendInvite ( + const wchar emailAddress[], + const wchar toName[], + const Uuid& inviteUuid, + FNetCliAuthSendFriendInviteCallback callback, + void * param +); + +//============================================================================ +// Propagate app-specific data +//============================================================================ +typedef void (*FNetCliAuthRecvBufferHandler)( + unsigned type, + unsigned bytes, + const byte buffer[] +); +void NetCliAuthSetRecvBufferHandler ( + FNetCliAuthRecvBufferHandler handler +); +void NetCliAuthPropagateBuffer ( + unsigned type, + unsigned bytes, + const byte buffer[] +); + +//============================================================================ +// New build notifications +//============================================================================ +typedef void (*FNotifyNewBuildHandler)(); +void NetCliAuthSetNotifyNewBuildHandler (FNotifyNewBuildHandler handler); + +//============================================================================ +// Score handling +//============================================================================ +struct NetGameScore; + +typedef void (*FNetCliAuthScoreUpdateCallback)( + ENetError result, + void * param +); + +//============================================================================ +typedef void (*FNetCliAuthCreateScoreCallback)( + ENetError result, + void * param, + unsigned scoreId, + UInt32 createdTime, + unsigned ownerId, + const char* gameName, + unsigned gameType, + int value +); +void NetCliAuthScoreCreate( + unsigned ownerId, + const char* gameName, + unsigned gameType, + int value, + FNetCliAuthCreateScoreCallback callback, + void * param +); + +//============================================================================ +void NetCliAuthScoreDelete( + unsigned scoreId, + FNetCliAuthScoreUpdateCallback callback, + void * param +); + +//============================================================================ +typedef void (*FNetCliAuthGetScoresCallback)( + ENetError result, + void * param, + const NetGameScore scores[], + unsigned scoreCount +); + +void NetCliAuthScoreGetScores( + unsigned ownerId, + const char* gameName, + FNetCliAuthGetScoresCallback callback, + void * param +); + +//============================================================================ +void NetCliAuthScoreAddPoints( + unsigned scoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param +); + +//============================================================================ +void NetCliAuthScoreTransferPoints( + unsigned srcScoreId, + unsigned destScoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param +); + +//============================================================================ +void NetCliAuthScoreSetPoints( + unsigned scoreId, + int numPoints, + FNetCliAuthScoreUpdateCallback callback, + void * param +); + +//============================================================================ +struct NetGameRank; +typedef void (*FNetCliAuthGetRanksCallback)( + ENetError result, + void * param, + const NetGameRank ranks[], + unsigned rankCount +); + +void NetCliAuthScoreGetRankList( + unsigned ownerId, + unsigned scoreGroup, + unsigned parentFolderId, + const char * gameName, + unsigned timePeriod, + unsigned numResults, + unsigned pageNumber, + bool sortDesc, + FNetCliAuthGetRanksCallback callback, + void * param +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCore.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCore.cpp new file mode 100644 index 00000000..a3de65b1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCore.cpp @@ -0,0 +1,187 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCore.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +namespace Ngl { +/***************************************************************************** +* +* Private +* +***/ + + +struct ReportNetErrorTrans : NetNotifyTrans { + ENetProtocol m_errProtocol; + ENetError m_errError; + + ReportNetErrorTrans ( + ENetProtocol errProtocol, + ENetError errError + ); + + void Post (); +}; + + +/***************************************************************************** +* +* Private data +* +***/ + +static FNetClientErrorProc s_errorProc; +static long s_initCount; + + +/***************************************************************************** +* +* Local functions +* +***/ + + +/***************************************************************************** +* +* Transactions +* +***/ + +//============================================================================ +// NetNotifyTrans +//============================================================================ +NetNotifyTrans::NetNotifyTrans (ETransType transType) +: NetTrans(kNetProtocolNil, transType) +{ +} + +//============================================================================ +// ReportNetErrorTrans +//============================================================================ +ReportNetErrorTrans::ReportNetErrorTrans ( + ENetProtocol errProtocol, + ENetError errError +) : NetNotifyTrans(kReportNetErrorTrans) +, m_errProtocol(errProtocol) +, m_errError(errError) +{ } + +//============================================================================ +void ReportNetErrorTrans::Post () { + if (s_errorProc) + s_errorProc(m_errProtocol, m_errError); +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void ReportNetError (ENetProtocol protocol, ENetError error) { + ReportNetErrorTrans * trans = NEW(ReportNetErrorTrans)(protocol, error); + NetTransSend(trans); +} + +} using namespace Ngl; + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void NetClientInitialize () { + + if (0 == AtomicAdd(&s_initCount, 1)) { + NetTransInitialize(); + AuthInitialize(); + GameInitialize(); + FileInitialize(); + CsrInitialize(); + GateKeeperInitialize(); + } +} + +//============================================================================ +void NetClientCancelAllTrans () { + NetTransCancelAll(kNetErrTimeout); +} + +//============================================================================ +void NetClientDestroy (bool wait) { + + if (1 == AtomicAdd(&s_initCount, -1)) { + s_errorProc = nil; + + GateKeeperDestroy(false); + CsrDestroy(false); + FileDestroy(false); + GameDestroy(false); + AuthDestroy(false); + NetTransDestroy(false); + if (wait) { + GateKeeperDestroy(true); + CsrDestroy(true); + FileDestroy(true); + GameDestroy(true); + AuthDestroy(true); + NetTransDestroy(true); + } + } +} + +//============================================================================ +void NetClientUpdate () { + NetTransUpdate(); +} + +//============================================================================ +void NetClientSetTransTimeoutMs (unsigned ms) { + NetTransSetTimeoutMs(ms); +} + +//============================================================================ +void NetClientPingEnable (bool enable) { + AuthPingEnable(enable); + GamePingEnable(enable); +} + +//============================================================================ +void NetClientSetErrorHandler (FNetClientErrorProc errorProc) { + s_errorProc = errorProc; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCore.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCore.h new file mode 100644 index 00000000..9d62d21e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCore.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCore.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLCORE_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCore.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLCORE_H + + + +/***************************************************************************** +* +* Core functions +* +***/ + +void NetClientInitialize (); +// void NetClientCancelAllTrans (); +void NetClientDestroy (bool wait = true); + +void NetClientUpdate (); + +void NetClientSetTransTimeoutMs (unsigned ms); +void NetClientPingEnable (bool enable); + +typedef void (*FNetClientErrorProc)( + ENetProtocol protocol, + ENetError error +); +void NetClientSetErrorHandler (FNetClientErrorProc errorProc); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCsr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCsr.cpp new file mode 100644 index 00000000..4138c3c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCsr.cpp @@ -0,0 +1,892 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCsr.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + +// This file excluded from pre-compiled header because it is auto-generated by the build server. +#include "pnNetBase/pnNbCsrKey.hpp" + + +namespace Ngl { namespace Csr { +/***************************************************************************** +* +* Internal types +* +***/ + +struct ConnectParam { + FNetCliCsrConnectedCallback callback; + void * param; +}; + +//============================================================================ +// Connection record +//============================================================================ +struct CliCsConn : AtomicRef { + LINK(CliCsConn) link; + + CCritSect critsect; + AsyncSocket sock; + AsyncCancelId cancelId; + NetCli * cli; + NetAddress addr; + unsigned seq; + bool abandoned; + unsigned serverChallenge; + unsigned latestBuildId; + ConnectParam * connectParam; + + // ping + AsyncTimer * pingTimer; + unsigned pingSendTimeMs; + unsigned lastHeardTimeMs; + + CliCsConn (); + ~CliCsConn (); + + void AutoPing (); + void StopAutoPing (); + void TimerPing (); + + void Send (const unsigned_ptr fields[], unsigned count); +}; + + +//============================================================================ +// Transaction objects +//============================================================================ +struct ConnectedNotifyTrans : NetNotifyTrans { + + ConnectParam * m_connectParam; + unsigned m_latestBuildId; + + ConnectedNotifyTrans (ConnectParam * cp, unsigned lbi) + : NetNotifyTrans(kCsrConnectedNotifyTrans) + , m_connectParam(cp) + , m_latestBuildId(lbi) + { } + ~ConnectedNotifyTrans () { + DEL(m_connectParam); + } + void Post (); +}; + +struct LoginRequestTrans : NetCsrTrans { + + wchar m_csrName[kMaxAccountNameLength]; + ShaDigest m_namePassHash; + FNetCliCsrLoginCallback m_callback; + void * m_param; + + Uuid m_csrId; + unsigned m_csrFlags; + + LoginRequestTrans ( + const wchar csrName[], + const ShaDigest & namePassHash, + FNetCliCsrLoginCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + + +/***************************************************************************** +* +* Internal data +* +***/ + +enum { + kPerfConnCount, + kPingDisabled, + kNumPerf +}; + +static bool s_running; +static CCritSect s_critsect; +static LISTDECL(CliCsConn, link) s_conns; +static CliCsConn * s_active; +static long s_perf[kNumPerf]; + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +static unsigned GetNonZeroTimeMs () { + if (unsigned ms = TimeGetMs()) + return ms; + return 1; +} + +//============================================================================ +static CliCsConn * GetConnIncRef_CS (const char tag[]) { + + if (CliCsConn * conn = s_active) + if (conn->cli) { + conn->IncRef(tag); + return conn; + } + return nil; +} + +//============================================================================ +static CliCsConn * GetConnIncRef (const char tag[]) { + ref(GetConnIncRef); + + CliCsConn * conn; + s_critsect.Enter(); + { + conn = GetConnIncRef_CS(tag); + } + s_critsect.Leave(); + return conn; +} + +//============================================================================ +static void UnlinkAndAbandonConn_CS (CliCsConn * conn) { + + s_conns.Unlink(conn); + conn->abandoned = true; + if (conn->cancelId) { + AsyncSocketConnectCancel(nil, conn->cancelId); + conn->cancelId = 0; + } + else if (conn->sock) { + AsyncSocketDisconnect(conn->sock, true); + } + else { + conn->DecRef("Lifetime"); + } +} + +//============================================================================ +static void SendRegisterRequest (CliCsConn * conn) { + + const unsigned_ptr msg[] = { + kCli2Csr_RegisterRequest, + 0 + }; + + conn->Send(msg, arrsize(msg)); +} + +//============================================================================ +static bool ConnEncrypt (ENetError error, void * param) { + + CliCsConn * conn = (CliCsConn *) param; + + if (IS_NET_SUCCESS(error)) { + s_critsect.Enter(); + { + s_active = conn; + conn->AutoPing(); + conn->IncRef(); + } + s_critsect.Leave(); + + SendRegisterRequest(conn); + + conn->DecRef(); + } + + return IS_NET_SUCCESS(error); +} + +//============================================================================ +static void NotifyConnSocketConnect (CliCsConn * conn) { + + conn->cli = NetCliConnectAccept( + conn->sock, + kNetProtocolCli2Csr, + false, + ConnEncrypt, + 0, + nil, + conn + ); +} + +//============================================================================ +static void NotifyConnSocketConnectFailed (CliCsConn * conn) { + + bool notify; + s_critsect.Enter(); + { + conn->cancelId = 0; + s_conns.Unlink(conn); + + notify + = s_running + && !conn->abandoned + && (!s_active || conn == s_active); + + if (conn == s_active) + s_active = nil; + } + s_critsect.Leave(); + + NetTransCancelByConnId(conn->seq, kNetErrTimeout); + conn->DecRef("Connecting"); + conn->DecRef("Lifetime"); + + if (notify) + ReportNetError(kNetProtocolCli2Csr, kNetErrConnectFailed); +} + +//============================================================================ +static void NotifyConnSocketDisconnect (CliCsConn * conn) { + + conn->StopAutoPing(); + + bool notify; + s_critsect.Enter(); + { + s_conns.Unlink(conn); + + notify + = s_running + && !conn->abandoned + && (!s_active || conn == s_active); + + if (conn == s_active) + s_active = nil; + } + s_critsect.Leave(); + + // Cancel all transactions in process on this connection. + NetTransCancelByConnId(conn->seq, kNetErrTimeout); + conn->DecRef("Connected"); + conn->DecRef("Lifetime"); + + if (notify) + ReportNetError(kNetProtocolCli2Csr, kNetErrDisconnected); +} + +//============================================================================ +static bool NotifyConnSocketRead (CliCsConn * conn, AsyncNotifySocketRead * read) { + + conn->lastHeardTimeMs = GetNonZeroTimeMs(); + bool result = NetCliDispatch(conn->cli, read->buffer, read->bytes, conn); + read->bytesProcessed += read->bytes; + return result; +} + +//============================================================================ +static bool SocketNotifyCallback ( + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +) { + bool result = true; + CliCsConn * conn; + + switch (code) { + case kNotifySocketConnectSuccess: { + conn = (CliCsConn *) notify->param; + *userState = conn; + conn->TransferRef("Connecting", "Connected"); + bool abandoned = true; + + if (abandoned) + AsyncSocketDisconnect(sock, true); + else + NotifyConnSocketConnect(conn); + } + break; + + case kNotifySocketConnectFailed: + conn = (CliCsConn *) notify->param; + NotifyConnSocketConnectFailed(conn); + break; + + case kNotifySocketDisconnect: + conn = (CliCsConn *) *userState; + NotifyConnSocketDisconnect(conn); + break; + + case kNotifySocketRead: + conn = (CliCsConn *) *userState; + result = NotifyConnSocketRead(conn, (AsyncNotifySocketRead *) notify); + break; + } + + return result; +} + +//============================================================================ +static void Connect ( + const NetAddress & addr, + ConnectParam * cp +) { + CliCsConn * conn = NEWZERO(CliCsConn); + conn->addr = addr; + conn->seq = ConnNextSequence(); + conn->lastHeardTimeMs = GetNonZeroTimeMs(); + conn->connectParam = cp; + + conn->IncRef("Lifetime"); + conn->IncRef("Connecting"); + + s_critsect.Enter(); + { + while (CliCsConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_conns.Link(conn); + } + s_critsect.Leave(); + + Cli2Csr_Connect connect; + connect.hdr.connType = kConnTypeCliToCsr; + connect.hdr.hdrBytes = sizeof(connect.hdr); + connect.hdr.buildId = BuildId(); + connect.hdr.buildType = BuildType(); + connect.hdr.branchId = BranchId(); + connect.hdr.productId = ProductId(); + connect.data.dataBytes = sizeof(connect.data); + + AsyncSocketConnect( + &conn->cancelId, + addr, + SocketNotifyCallback, + conn, + &connect, + sizeof(connect), + 0, + 0 + ); +} + +//============================================================================ +static void AsyncLookupCallback ( + void * param, + const wchar name[], + unsigned addrCount, + const NetAddress addrs[] +) { + ref(name); + + if (!addrCount) { + ReportNetError(kNetProtocolCli2Auth, kNetErrNameLookupFailed); + return; + } + + // Only connect to one server + addrCount = MIN(addrCount, 1); + + for (unsigned i = 0; i < addrCount; ++i) { + Connect(addrs[i], (ConnectParam *)param); + } +} + + +/***************************************************************************** +* +* Message handlers +* +***/ + +//============================================================================ +static bool Recv_PingReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Csr2Cli_PingReply & reply = *(const Csr2Cli_PingReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_RegisterReply ( + const byte msg[], + unsigned , + void * param +) { + CliCsConn * conn = (CliCsConn *)param; + + const Csr2Cli_RegisterReply & reply = *(const Csr2Cli_RegisterReply *)msg; + + conn->serverChallenge = reply.serverChallenge; + conn->latestBuildId = reply.csrBuildId; + + ConnectedNotifyTrans * trans = NEW(ConnectedNotifyTrans)( + conn->connectParam, + conn->latestBuildId + ); + NetTransSend(trans); + + conn->connectParam = nil; + + return true; +} + +//============================================================================ +static bool Recv_LoginReply ( + const byte msg[], + unsigned bytes, + void * +) { + const Csr2Cli_LoginReply & reply = *(const Csr2Cli_LoginReply *)msg; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + + +/***************************************************************************** +* +* Protocol +* +***/ + +#define MSG(s) kNetMsg_Cli2Csr_##s +static NetMsgInitSend s_send[] = { + { MSG(PingRequest) }, + { MSG(RegisterRequest) }, + { MSG(LoginRequest) }, +}; +#undef MSG + +#define MSG(s) kNetMsg_Csr2Cli_##s, Recv_##s +static NetMsgInitRecv s_recv[] = { + { MSG(PingReply) }, + { MSG(RegisterReply) }, + { MSG(LoginReply) }, +}; +#undef MSG + + +/***************************************************************************** +* +* CliCsConn +* +***/ + +//=========================================================================== +static unsigned CliCsConnTimerDestroyed (void * param) { + + CliCsConn * conn = (CliCsConn *) param; + conn->DecRef("PingTimer"); + return kAsyncTimeInfinite; +} + +//=========================================================================== +static unsigned CliCsConnPingTimerProc (void * param) { + + ((CliCsConn *) param)->TimerPing(); + return kPingIntervalMs; +} + +//============================================================================ +CliCsConn::CliCsConn () { + + AtomicAdd(&s_perf[kPerfConnCount], 1); +} + +//============================================================================ +CliCsConn::~CliCsConn () { + + // Delete 'cli' after all refs have been removed + if (cli) + NetCliDelete(cli, true); + + DEL(connectParam); + + AtomicAdd(&s_perf[kPerfConnCount], -1); +} + +//============================================================================ +void CliCsConn::AutoPing () { + ASSERT(!pingTimer); + + IncRef("PingTimer"); + critsect.Enter(); + { + AsyncTimerCreate( + &pingTimer, + CliCsConnPingTimerProc, + sock ? 0 : kAsyncTimeInfinite, + this + ); + } + critsect.Leave(); +} + +//============================================================================ +void CliCsConn::StopAutoPing () { + critsect.Enter(); + { + if (AsyncTimer * timer = pingTimer) { + pingTimer = nil; + AsyncTimerDeleteCallback(timer, CliCsConnTimerDestroyed); + } + } + critsect.Leave(); +} + +//============================================================================ +void CliCsConn::TimerPing () { + + // Send a ping request + pingSendTimeMs = GetNonZeroTimeMs(); + + const unsigned_ptr msg[] = { + kCli2Auth_PingRequest, + 0, // not a transaction + pingSendTimeMs, + 0, // no payload + nil + }; + + Send(msg, arrsize(msg)); +} + +//============================================================================ +void CliCsConn::Send (const unsigned_ptr fields[], unsigned count) { + + critsect.Enter(); + { + NetCliSend(cli, fields, count); + NetCliFlush(cli); + } + critsect.Leave(); +} + + +/***************************************************************************** +* +* ConnectedNotifyTrans +* +***/ + +//============================================================================ +void ConnectedNotifyTrans::Post () { + + if (m_connectParam && m_connectParam->callback) + m_connectParam->callback(m_connectParam->param, m_latestBuildId); +} + + +/***************************************************************************** +* +* LoginRequestTrans +* +***/ + +//============================================================================ +LoginRequestTrans::LoginRequestTrans ( + const wchar csrName[], + const ShaDigest & namePassHash, + FNetCliCsrLoginCallback callback, + void * param +) : NetCsrTrans(kCsrLoginTrans) +, m_namePassHash(namePassHash) +, m_callback(callback) +, m_param(param) +{ + ASSERT(callback); + StrCopy(m_csrName, csrName, arrsize(m_csrName)); +} + +//============================================================================ +bool LoginRequestTrans::Send () { + + if (!AcquireConn()) + return false; + + ShaDigest challengeHash; + dword clientChallenge = 0; + + CryptCreateRandomSeed( + sizeof(clientChallenge), + (byte *) &clientChallenge + ); + + CryptHashPasswordChallenge( + clientChallenge, + s_active->serverChallenge, + m_namePassHash, + &challengeHash + ); + + const unsigned_ptr msg[] = { + kCli2Csr_LoginRequest, + m_transId, + clientChallenge, + (unsigned_ptr) m_csrName, + (unsigned_ptr) &challengeHash + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void LoginRequestTrans::Post () { + m_callback( + m_result, + m_param, + m_csrId, + m_csrFlags + ); +} + +//============================================================================ +bool LoginRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const Csr2Cli_LoginReply & reply = *(const Csr2Cli_LoginReply *) msg; + + m_result = reply.result; + m_csrId = reply.csrId; + m_csrFlags = reply.csrFlags; + + m_state = kTransStateComplete; + + return true; +} + + +} using namespace Csr; + + +/***************************************************************************** +* +* NetCsrTrans +* +***/ + +//============================================================================ +NetCsrTrans::NetCsrTrans (ETransType transType) +: NetTrans(kNetProtocolCli2Csr, transType) +, m_conn(nil) +{ +} + +//============================================================================ +NetCsrTrans::~NetCsrTrans () { + ReleaseConn(); +} + +//============================================================================ +bool NetCsrTrans::AcquireConn () { + if (!m_conn) + m_conn = GetConnIncRef("AcquireConn"); + return m_conn != nil; +} + +//============================================================================ +void NetCsrTrans::ReleaseConn () { + if (m_conn) { + m_conn->DecRef("AcquireConn"); + m_conn = nil; + } +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void CsrInitialize () { + + s_running = true; + + NetMsgProtocolRegister( + kNetProtocolCli2Csr, + false, + s_send, arrsize(s_send), + s_recv, arrsize(s_recv), + kDhGValue, + BigNum(sizeof(kDhXData), kDhXData), + BigNum(sizeof(kDhNData), kDhNData) + ); +} + +//============================================================================ +void CsrDestroy (bool wait) { + + s_running = false; + + NetTransCancelByProtocol( + kNetProtocolCli2Csr, + kNetErrRemoteShutdown + ); + NetMsgProtocolDestroy( + kNetProtocolCli2Csr, + false + ); + + s_critsect.Enter(); + { + while (CliCsConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_active = nil; + } + s_critsect.Leave(); + + if (!wait) + return; + + while (s_perf[kPerfConnCount]) { + NetTransUpdate(); + AsyncSleep(10); + } +} + +//============================================================================ +bool CsrQueryConnected () { + + bool result; + s_critsect.Enter(); + { + if (nil != (result = s_active)) + result &= (nil != s_active->cli); + } + s_critsect.Leave(); + return result; +} + +//============================================================================ +unsigned CsrGetConnId () { + + unsigned connId; + s_critsect.Enter(); + { + connId = (s_active) ? s_active->seq : 0; + } + s_critsect.Leave(); + return connId; +} + +} using namespace Ngl; + + +/***************************************************************************** +* +* Exports +* +***/ + +//============================================================================ +void NetCliCsrStartConnect ( + const wchar * addrList[], + unsigned addrCount, + FNetCliCsrConnectedCallback callback, + void * param +) { + // Only connect to one server + addrCount = min(addrCount, 1); + + for (unsigned i = 0; i < addrCount; ++i) { + // Do we need to lookup the address? + const wchar * name = addrList[i]; + while (unsigned ch = *name) { + ++name; + if (!(isdigit(ch) || ch == L'.' || ch == L':')) { + ConnectParam * cp = NEW(ConnectParam); + cp->callback = callback; + cp->param = param; + + AsyncCancelId cancelId; + AsyncAddressLookupName( + &cancelId, + AsyncLookupCallback, + addrList[i], + kNetDefaultClientPort, + cp + ); + break; + } + } + if (!name[0]) { + NetAddress addr; + NetAddressFromString(&addr, addrList[i], kNetDefaultClientPort); + + ConnectParam * cp = NEW(ConnectParam); + cp->callback = callback; + cp->param = param; + + Connect(addr, cp); + } + } +} + +//============================================================================ +void NetCliCsrDisconnect () { + + s_critsect.Enter(); + { + while (CliCsConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_active = nil; + } + s_critsect.Leave(); +} + +//============================================================================ +void NetCliCsrLoginRequest ( + const wchar csrName[], + const ShaDigest & namePassHash, + FNetCliCsrLoginCallback callback, + void * param +) { + LoginRequestTrans * trans = NEW(LoginRequestTrans)( + csrName, + namePassHash, + callback, + param + ); + NetTransSend(trans); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCsr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCsr.h new file mode 100644 index 00000000..0d4e4bde --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCsr.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCsr.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLCSR_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglCsr.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLCSR_H + + +/***************************************************************************** +* +* Client-side CSR functions +* +***/ + +typedef void (*FNetCliCsrConnectedCallback) ( + void * param, + unsigned latestBuildId +); +void NetCliCsrStartConnect ( + const wchar * addrList[], + unsigned addrCount, + FNetCliCsrConnectedCallback callback = nil, + void * param = nil +); +void NetCliCsrDisconnect (); + +typedef void (*FNetCliCsrLoginCallback)( + ENetError result, + void * param, + const Uuid & csrId, + unsigned csrFlags +); +void NetCliCsrLoginRequest ( + const wchar csrName[], + const ShaDigest & namePassHash, + FNetCliCsrLoginCallback callback, + void * param +); + +typedef void (*FNetCliCsrSetTicketFilterCallback)( + ENetError result, + void * param +); +void NetCliCsrSetTicketFilter ( + const wchar filterSpec[], + FNetCliCsrSetTicketFilterCallback callback, + void * param +); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglFile.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglFile.cpp new file mode 100644 index 00000000..14d8c8e7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglFile.cpp @@ -0,0 +1,1460 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglFile.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + +// Define this if the file servers are running behind load-balancing hardware. +// It changes the logic by which the decision to attempt a reconnect is made. +#define LOAD_BALANCER_HARDWARE + + +namespace Ngl { namespace File { +/***************************************************************************** +* +* Private +* +***/ + +struct CliFileConn : AtomicRef { + LINK(CliFileConn) link; + CLock sockLock; // to protect the socket pointer so we don't nuke it while using it + AsyncSocket sock; + wchar name[MAX_PATH]; + NetAddress addr; + unsigned seq; + ARRAY(byte) recvBuffer; + AsyncCancelId cancelId; + bool abandoned; + unsigned buildId; + unsigned serverType; + + CCritSect timerCritsect; // critsect for both timers + + // Reconnection + AsyncTimer * reconnectTimer; + unsigned reconnectStartMs; + unsigned connectStartMs; + unsigned numImmediateDisconnects; + unsigned numFailedConnects; + + // Ping + AsyncTimer * pingTimer; + unsigned pingSendTimeMs; + unsigned lastHeardTimeMs; + + CliFileConn (); + ~CliFileConn (); + + // This function should be called during object construction + // to initiate connection attempts to the remote host whenever + // the socket is disconnected. + void AutoReconnect (); + bool AutoReconnectEnabled () {return (reconnectTimer != nil);} + void StopAutoReconnect (); // call before destruction + void StartAutoReconnect (); + void TimerReconnect (); + + // ping + void AutoPing (); + void StopAutoPing (); + void TimerPing (); + + void Send (const void * data, unsigned bytes); + + void Destroy(); // cleans up the socket and buffer + + void Dispatch (const Cli2File_MsgHeader * msg); + bool Recv_PingReply (const File2Cli_PingReply * msg); + bool Recv_BuildIdReply (const File2Cli_BuildIdReply * msg); + bool Recv_BuildIdUpdate (const File2Cli_BuildIdUpdate * msg); + bool Recv_ManifestReply (const File2Cli_ManifestReply * msg); + bool Recv_FileDownloadReply (const File2Cli_FileDownloadReply * msg); +}; + + +//============================================================================ +// BuildIdRequestTrans +//============================================================================ +struct BuildIdRequestTrans : NetFileTrans { + FNetCliFileBuildIdRequestCallback m_callback; + void * m_param; + + unsigned m_buildId; + + BuildIdRequestTrans ( + FNetCliFileBuildIdRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// ManifestRequestTrans +//============================================================================ +struct ManifestRequestTrans : NetFileTrans { + FNetCliFileManifestRequestCallback m_callback; + void * m_param; + wchar m_group[MAX_PATH]; + unsigned m_buildId; + + ARRAY(NetCliFileManifestEntry) m_manifest; + unsigned m_numEntriesReceived; + + ManifestRequestTrans ( + FNetCliFileManifestRequestCallback callback, + void * param, + const wchar group[], + unsigned buildId + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// DownloadRequestTrans +//============================================================================ +struct DownloadRequestTrans : NetFileTrans { + FNetCliFileDownloadRequestCallback m_callback; + void * m_param; + + wchar m_filename[MAX_PATH]; + hsStream * m_writer; + unsigned m_buildId; + + unsigned m_totalBytesReceived; + + DownloadRequestTrans ( + FNetCliFileDownloadRequestCallback callback, + void * param, + const wchar filename[], + hsStream * writer, + unsigned buildId + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// RcvdFileDownloadChunkTrans +//============================================================================ +struct RcvdFileDownloadChunkTrans : NetNotifyTrans { + + unsigned bytes; + byte * data; + hsStream * writer; + + RcvdFileDownloadChunkTrans () : NetNotifyTrans (kFileRcvdFileDownloadChunkTrans) {} + ~RcvdFileDownloadChunkTrans (); + void Post (); +}; + + +/***************************************************************************** +* +* Private data +* +***/ + +enum { + kPerfConnCount, + kNumPerf +}; + +static bool s_running; +static CCritSect s_critsect; +static LISTDECL(CliFileConn, link) s_conns; +static CliFileConn * s_active; +static long s_perf[kNumPerf]; +static unsigned s_connectBuildId; +static unsigned s_serverType; + +static FNetCliFileBuildIdUpdateCallback s_buildIdCallback = nil; + +const unsigned kMinValidConnectionMs = 25 * 1000; + + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +static unsigned GetNonZeroTimeMs () { + if (unsigned ms = TimeGetMs()) + return ms; + return 1; +} + +//============================================================================ +static CliFileConn * GetConnIncRef_CS (const char tag[]) { + if (CliFileConn * conn = s_active) { + conn->IncRef(tag); + return conn; + } + return nil; +} + +//============================================================================ +static CliFileConn * GetConnIncRef (const char tag[]) { + CliFileConn * conn; + s_critsect.Enter(); + { + conn = GetConnIncRef_CS(tag); + } + s_critsect.Leave(); + return conn; +} + +//============================================================================ +static void UnlinkAndAbandonConn_CS (CliFileConn * conn) { + s_conns.Unlink(conn); + conn->abandoned = true; + + if (conn->AutoReconnectEnabled()) + conn->StopAutoReconnect(); + + bool needsDecref = true; + if (conn->cancelId) { + AsyncSocketConnectCancel(nil, conn->cancelId); + conn->cancelId = 0; + needsDecref = false; + } + else { + conn->sockLock.EnterRead(); + if (conn->sock) { + AsyncSocketDisconnect(conn->sock, true); + needsDecref = false; + } + conn->sockLock.LeaveRead(); + } + if (needsDecref) { + conn->DecRef("Lifetime"); + } +} + +//============================================================================ +static void NotifyConnSocketConnect (CliFileConn * conn) { + + conn->TransferRef("Connecting", "Connected"); + conn->connectStartMs = TimeGetMs(); + conn->numFailedConnects = 0; + + // Make this the active server + s_critsect.Enter(); + { + if (!conn->abandoned) { + conn->AutoPing(); + s_active = conn; + } + else + { + conn->sockLock.EnterRead(); + AsyncSocketDisconnect(conn->sock, true); + conn->sockLock.LeaveRead(); + } + } + s_critsect.Leave(); +} + +//============================================================================ +static void NotifyConnSocketConnectFailed (CliFileConn * conn) { + s_critsect.Enter(); + { + conn->cancelId = 0; + s_conns.Unlink(conn); + + if (conn == s_active) + s_active = nil; + } + s_critsect.Leave(); + + // Cancel all transactions in progress on this connection. + NetTransCancelByConnId(conn->seq, kNetErrTimeout); + +#ifndef SERVER + // Client apps fail if unable to connect for a time + if (++conn->numFailedConnects >= kMaxFailedConnects) { + ReportNetError(kNetProtocolCli2File, kNetErrConnectFailed); + } + else +#endif // ndef SERVER + { + // start reconnect, if we are doing that + if (s_running && conn->AutoReconnectEnabled()) + conn->StartAutoReconnect(); + else + conn->DecRef("Lifetime"); // if we are not reconnecting, this socket is done, so remove the lifetime ref + } + conn->DecRef("Connecting"); +} + +//============================================================================ +static void NotifyConnSocketDisconnect (CliFileConn * conn) { + conn->StopAutoPing(); + s_critsect.Enter(); + { + conn->cancelId = 0; + s_conns.Unlink(conn); + + if (conn == s_active) + s_active = nil; + } + s_critsect.Leave(); + + // Cancel all transactions in progress on this connection. + NetTransCancelByConnId(conn->seq, kNetErrTimeout); + + + bool notify = false; + +#ifdef SERVER + { + if (TimeGetMs() - conn->connectStartMs > kMinValidConnectionMs) + conn->reconnectStartMs = 0; + else + conn->reconnectStartMs = GetNonZeroTimeMs() + kMaxReconnectIntervalMs; + } +#else + { + #ifndef LOAD_BALANCER_HARDWARE + // If the connection to the remote server was open for longer than + // kMinValidConnectionMs then assume that the connection was to + // a valid server and try to perform reconnection immediately. If + // less time elapsed then the connection was likely to a server + // with an open port but with no notification procedure registered + // for this type of communication channel. + if (TimeGetMs() - conn->connectStartMs > kMinValidConnectionMs) { + conn->reconnectStartMs = 0; + } + else { + if (++conn->numImmediateDisconnects < kMaxImmediateDisconnects) + conn->reconnectStartMs = GetNonZeroTimeMs() + kMaxReconnectIntervalMs; + else + notify = true; + } + #else + // File server is running behind a load-balancer, so the next connection may + // send us to a new server, therefore attempt a reconnection to the same + // address even if the disconnect was immediate. This is safe because the + // file server is stateless with respect to clients. + if (TimeGetMs() - conn->connectStartMs <= kMinValidConnectionMs) { + if (++conn->numImmediateDisconnects < kMaxImmediateDisconnects) + conn->reconnectStartMs = GetNonZeroTimeMs() + kMaxReconnectIntervalMs; + else + notify = true; + } + else { + // disconnect was not immediate. attempt a reconnect unless we're shutting down + conn->numImmediateDisconnects = 0; + conn->reconnectStartMs = 0; + } + #endif // LOAD_BALANCER + } +#endif // ndef SERVER + + if (notify) { + ReportNetError(kNetProtocolCli2File, kNetErrDisconnected); + } + else { + // clean up the socket and start reconnect, if we are doing that + conn->Destroy(); + if (conn->AutoReconnectEnabled()) + conn->StartAutoReconnect(); + else + conn->DecRef("Lifetime"); // if we are not reconnecting, this socket is done, so remove the lifetime ref + } + + conn->DecRef("Connected"); +} + +//============================================================================ +static bool NotifyConnSocketRead (CliFileConn * conn, AsyncNotifySocketRead * read) { + conn->lastHeardTimeMs = GetNonZeroTimeMs(); + conn->recvBuffer.Add(read->buffer, read->bytes); + read->bytesProcessed += read->bytes; + + for (;;) { + if (conn->recvBuffer.Count() < sizeof(dword)) + return true; + + dword msgSize = *(dword *)conn->recvBuffer.Ptr(); + if (conn->recvBuffer.Count() < msgSize) + return true; + + const Cli2File_MsgHeader * msg = (const Cli2File_MsgHeader *) conn->recvBuffer.Ptr(); + conn->Dispatch(msg); + + conn->recvBuffer.Move(0, msgSize, conn->recvBuffer.Count() - msgSize); + conn->recvBuffer.ShrinkBy(msgSize); + } +} + +//============================================================================ +static bool SocketNotifyCallback ( + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +) { + bool result = true; + CliFileConn * conn; + + switch (code) { + case kNotifySocketConnectSuccess: + conn = (CliFileConn *) notify->param; + *userState = conn; + s_critsect.Enter(); + { + conn->sockLock.EnterWrite(); + conn->sock = sock; + conn->sockLock.LeaveWrite(); + conn->cancelId = 0; + } + s_critsect.Leave(); + NotifyConnSocketConnect(conn); + break; + + case kNotifySocketConnectFailed: + conn = (CliFileConn *) notify->param; + NotifyConnSocketConnectFailed(conn); + break; + + case kNotifySocketDisconnect: + conn = (CliFileConn *) *userState; + NotifyConnSocketDisconnect(conn); + break; + + case kNotifySocketRead: + conn = (CliFileConn *) *userState; + result = NotifyConnSocketRead(conn, (AsyncNotifySocketRead *) notify); + break; + } + + return result; +} + +//============================================================================ +static void Connect (CliFileConn * conn) { + ASSERT(s_running); + + conn->pingSendTimeMs = 0; + + s_critsect.Enter(); + { + while (CliFileConn * oldConn = s_conns.Head()) { + if (oldConn != conn) + UnlinkAndAbandonConn_CS(oldConn); + else + s_conns.Unlink(oldConn); + } + s_conns.Link(conn); + } + s_critsect.Leave(); + + Cli2File_Connect connect; + connect.hdr.connType = kConnTypeCliToFile; + connect.hdr.hdrBytes = sizeof(connect.hdr); + connect.hdr.buildId = kFileSrvBuildId; + connect.hdr.buildType = BuildType(); + connect.hdr.branchId = BranchId(); + connect.hdr.productId = ProductId(); + connect.data.buildId = conn->buildId; + connect.data.serverType = conn->serverType; + connect.data.dataBytes = sizeof(connect.data); + + AsyncSocketConnect( + &conn->cancelId, + conn->addr, + SocketNotifyCallback, + conn, + &connect, + sizeof(connect), + 0, + 0 + ); +} + +//============================================================================ +static void Connect ( + const wchar name[], + const NetAddress & addr +) { + ASSERT(s_running); + + CliFileConn * conn = NEWZERO(CliFileConn); + StrCopy(conn->name, name, arrsize(conn->name)); + conn->addr = addr; + conn->buildId = s_connectBuildId; + conn->serverType = s_serverType; + conn->seq = ConnNextSequence(); + conn->lastHeardTimeMs = GetNonZeroTimeMs(); // used in connect timeout, and ping timeout + + conn->IncRef("Lifetime"); + conn->AutoReconnect(); +} + +//============================================================================ +static void AsyncLookupCallback ( + void * param, + const wchar name[], + unsigned addrCount, + const NetAddress addrs[] +) { + ref(param); + + if (!addrCount) { + ReportNetError(kNetProtocolCli2File, kNetErrNameLookupFailed); + return; + } + + for (unsigned i = 0; i < addrCount; ++i) { + Connect(name, addrs[i]); + } +} + +/***************************************************************************** +* +* CliFileConn +* +***/ + +//============================================================================ +CliFileConn::CliFileConn () { + AtomicAdd(&s_perf[kPerfConnCount], 1); +} + +//============================================================================ +CliFileConn::~CliFileConn () { + ASSERT(!cancelId); + ASSERT(!reconnectTimer); + Destroy(); + AtomicAdd(&s_perf[kPerfConnCount], -1); +} + +//=========================================================================== +void CliFileConn::TimerReconnect () { + ASSERT(!sock); + ASSERT(!cancelId); + + if (!s_running) { + s_critsect.Enter(); + UnlinkAndAbandonConn_CS(this); + s_critsect.Leave(); + } + else { + IncRef("Connecting"); + + // Remember the time we started the reconnect attempt, guarding against + // TimeGetMs() returning zero (unlikely), as a value of zero indicates + // a first-time connect condition to StartAutoReconnect() + reconnectStartMs = GetNonZeroTimeMs(); + + Connect(this); + } +} + +//=========================================================================== +static unsigned CliFileConnTimerReconnectProc (void * param) { + ((CliFileConn *) param)->TimerReconnect(); + return kAsyncTimeInfinite; +} + +//=========================================================================== +// This function is called when after a disconnect to start a new connection +void CliFileConn::StartAutoReconnect () { + timerCritsect.Enter(); + if (reconnectTimer) { + // Make reconnect attempts at regular intervals. If the last attempt + // took more than the specified max interval time then reconnect + // immediately; otherwise wait until the time interval is up again + // then reconnect. + unsigned remainingMs = 0; + if (reconnectStartMs) { + remainingMs = reconnectStartMs - GetNonZeroTimeMs(); + if ((signed)remainingMs < 0) + remainingMs = 0; + } + AsyncTimerUpdate(reconnectTimer, remainingMs); + } + timerCritsect.Leave(); +} + +//=========================================================================== +// This function should be called during object construction +// to initiate connection attempts to the remote host whenever +// the socket is disconnected. +void CliFileConn::AutoReconnect () { + timerCritsect.Enter(); + { + ASSERT(!reconnectTimer); + IncRef("ReconnectTimer"); + AsyncTimerCreate( + &reconnectTimer, + CliFileConnTimerReconnectProc, + 0, // immediate callback + this + ); + } + timerCritsect.Leave(); +} + +//=========================================================================== +static unsigned CliFileConnTimerDestroyed (void * param) { + CliFileConn * sock = (CliFileConn *) param; + sock->DecRef("TimerDestroyed"); + return kAsyncTimeInfinite; +} + +//============================================================================ +void CliFileConn::StopAutoReconnect () { + timerCritsect.Enter(); + { + if (AsyncTimer * timer = reconnectTimer) { + reconnectTimer = nil; + AsyncTimerDeleteCallback(timer, CliFileConnTimerDestroyed); + } + } + timerCritsect.Leave(); +} + +//=========================================================================== +static unsigned CliFileConnPingTimerProc (void * param) { + ((CliFileConn *) param)->TimerPing(); + return kPingIntervalMs; +} + +//============================================================================ +void CliFileConn::AutoPing () { + ASSERT(!pingTimer); + IncRef("PingTimer"); + timerCritsect.Enter(); + { + sockLock.EnterRead(); + unsigned timerPeriod = sock ? 0 : kAsyncTimeInfinite; + sockLock.LeaveRead(); + + AsyncTimerCreate( + &pingTimer, + CliFileConnPingTimerProc, + timerPeriod, + this + ); + } + timerCritsect.Leave(); +} + +//============================================================================ +void CliFileConn::StopAutoPing () { + timerCritsect.Enter(); + { + if (AsyncTimer * timer = pingTimer) { + pingTimer = nil; + AsyncTimerDeleteCallback(timer, CliFileConnTimerDestroyed); + } + } + timerCritsect.Leave(); +} + +//============================================================================ +void CliFileConn::TimerPing () { + sockLock.EnterRead(); + for (;;) { + if (!sock) // make sure it exists + break; +#if 0 + // if the time difference between when we last sent a ping and when we last + // heard from the server is >= 3x the ping interval, the socket is stale. + if (pingSendTimeMs && abs(int(pingSendTimeMs - lastHeardTimeMs)) >= kPingTimeoutMs) { + // ping timed out, disconnect the socket + AsyncSocketDisconnect(sock, true); + } + else +#endif + { + // Send a ping request + pingSendTimeMs = GetNonZeroTimeMs(); + + Cli2File_PingRequest msg; + msg.messageId = kCli2File_PingRequest; + msg.messageBytes = sizeof(msg); + msg.pingTimeMs = pingSendTimeMs; + + // read locks are reentrant, so calling Send is ok here within the read lock + Send(&msg, msg.messageBytes); + } + break; + } + sockLock.LeaveRead(); +} + +//============================================================================ +void CliFileConn::Destroy () { + AsyncSocket oldSock = nil; + + sockLock.EnterWrite(); + { + SWAP(oldSock, sock); + } + sockLock.LeaveWrite(); + + if (oldSock) + AsyncSocketDelete(oldSock); + recvBuffer.Clear(); +} + +//============================================================================ +void CliFileConn::Send (const void * data, unsigned bytes) { + sockLock.EnterRead(); + if (sock) { + AsyncSocketSend(sock, data, bytes); + } + sockLock.LeaveRead(); +} + +//============================================================================ +void CliFileConn::Dispatch (const Cli2File_MsgHeader * msg) { + +#define DISPATCH(a) case kFile2Cli_##a: Recv_##a((const File2Cli_##a *) msg); break + switch (msg->messageId) { + DISPATCH(PingReply); + DISPATCH(BuildIdReply); + DISPATCH(BuildIdUpdate); + DISPATCH(ManifestReply); + DISPATCH(FileDownloadReply); + DEFAULT_FATAL(msg->messageId) + } +#undef DISPATCH +} + +//============================================================================ +bool CliFileConn::Recv_PingReply ( + const File2Cli_PingReply * msg +) { + ref(msg); + return true; +} + +//============================================================================ +bool CliFileConn::Recv_BuildIdReply ( + const File2Cli_BuildIdReply * msg +) { + NetTransRecv(msg->transId, (const byte *)msg, msg->messageBytes); + + return true; +} + +//============================================================================ +bool CliFileConn::Recv_BuildIdUpdate ( + const File2Cli_BuildIdUpdate * msg +) { + if (s_buildIdCallback) + s_buildIdCallback(msg->buildId); + return true; +} + +//============================================================================ +bool CliFileConn::Recv_ManifestReply ( + const File2Cli_ManifestReply * msg +) { + NetTransRecv(msg->transId, (const byte *)msg, msg->messageBytes); + + return true; +} + +//============================================================================ +bool CliFileConn::Recv_FileDownloadReply ( + const File2Cli_FileDownloadReply * msg +) { + NetTransRecv(msg->transId, (const byte *)msg, msg->messageBytes); + + return true; +} + + +/***************************************************************************** +* +* BuildIdRequestTrans +* +***/ + +//============================================================================ +BuildIdRequestTrans::BuildIdRequestTrans ( + FNetCliFileBuildIdRequestCallback callback, + void * param +) : NetFileTrans(kBuildIdRequestTrans) +, m_callback(callback) +, m_param(param) +{} + +//============================================================================ +bool BuildIdRequestTrans::Send () { + if (!AcquireConn()) + return false; + + Cli2File_BuildIdRequest buildIdReq; + buildIdReq.messageId = kCli2File_BuildIdRequest; + buildIdReq.transId = m_transId; + buildIdReq.messageBytes = sizeof(buildIdReq); + + m_conn->Send(&buildIdReq, buildIdReq.messageBytes); + + return true; +} + +//============================================================================ +void BuildIdRequestTrans::Post () { + m_callback(m_result, m_param, m_buildId); +} + +//============================================================================ +bool BuildIdRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + const File2Cli_BuildIdReply & reply = *(const File2Cli_BuildIdReply *) msg; + + if (IS_NET_ERROR(reply.result)) { + // we have a problem... + m_result = reply.result; + m_state = kTransStateComplete; + return true; + } + + m_buildId = reply.buildId; + + // mark as complete + m_result = reply.result; + m_state = kTransStateComplete; + + return true; +} + + +/***************************************************************************** +* +* ManifestRequestTrans +* +***/ + +//============================================================================ +ManifestRequestTrans::ManifestRequestTrans ( + FNetCliFileManifestRequestCallback callback, + void * param, + const wchar group[], + unsigned buildId +) : NetFileTrans(kManifestRequestTrans) +, m_callback(callback) +, m_param(param) +, m_numEntriesReceived(0) +, m_buildId(buildId) +{ + if (group) + StrCopy(m_group, group, arrsize(m_group)); + else + m_group[0] = L'\0'; +} + +//============================================================================ +bool ManifestRequestTrans::Send () { + if (!AcquireConn()) + return false; + + Cli2File_ManifestRequest manifestReq; + StrCopy(manifestReq.group, m_group, arrsize(manifestReq.group)); + manifestReq.messageId = kCli2File_ManifestRequest; + manifestReq.transId = m_transId; + manifestReq.messageBytes = sizeof(manifestReq); + manifestReq.buildId = m_buildId; + + m_conn->Send(&manifestReq, manifestReq.messageBytes); + + return true; +} + +//============================================================================ +void ManifestRequestTrans::Post () { + m_callback(m_result, m_param, m_group, m_manifest.Ptr(), m_manifest.Count()); +} + +//============================================================================ +void ReadStringFromMsg(const wchar* curMsgPtr, wchar str[], unsigned maxStrLen, unsigned* length) { + StrCopy(str, curMsgPtr, maxStrLen); + str[maxStrLen - 1] = L'\0'; // make sure it's terminated + + (*length) = StrLen(str); +} + +//============================================================================ +void ReadUnsignedFromMsg(const wchar* curMsgPtr, unsigned* val) { + (*val) = ((*curMsgPtr) << 16) + (*(curMsgPtr + 1)); +} + +//============================================================================ +bool ManifestRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + m_timeoutAtMs = TimeGetMs() + NetTransGetTimeoutMs(); // Reset the timeout counter + + ref(bytes); + const File2Cli_ManifestReply & reply = *(const File2Cli_ManifestReply *) msg; + + dword numFiles = reply.numFiles; + dword wcharCount = reply.wcharCount; + const wchar* curChar = reply.manifestData; + + // tell the server we got the data + Cli2File_ManifestEntryAck manifestAck; + manifestAck.messageId = kCli2File_ManifestEntryAck; + manifestAck.transId = reply.transId; + manifestAck.messageBytes = sizeof(manifestAck); + manifestAck.readerId = reply.readerId; + + m_conn->Send(&manifestAck, manifestAck.messageBytes); + + // if wcharCount is 2, the data only contains the terminator "\0\0" and we + // don't need to convert anything (and we are done) + if ((IS_NET_ERROR(reply.result)) || (wcharCount == 2)) { + // we have a problem... or we have nothing to so, so we're done + m_result = reply.result; + m_state = kTransStateComplete; + return true; + } + + if (numFiles > m_manifest.Count()) + m_manifest.SetCount(numFiles); // reserve the space ahead of time + + // manifestData format: "clientFile\0downloadFile\0md5\0filesize\0zipsize\0flags\0...\0\0" + bool done = false; + while (!done) { + if (wcharCount == 0) + { + done = true; + break; + } + + // copy the data over to our array (m_numEntriesReceived is the current index) + NetCliFileManifestEntry& entry = m_manifest[m_numEntriesReceived]; + + // -------------------------------------------------------------------- + // read in the clientFilename + unsigned filenameLen; + ReadStringFromMsg(curChar, entry.clientName, arrsize(entry.clientName), &filenameLen); + curChar += filenameLen; // advance the pointer + wcharCount -= filenameLen; // keep track of the amount remaining + if ((*curChar != L'\0') || (wcharCount <= 0)) + return false; // something is screwy, abort and disconnect + + // point it at the downloadFile + curChar++; + wcharCount--; + + // -------------------------------------------------------------------- + // read in the downloadFilename + ReadStringFromMsg(curChar, entry.downloadName, arrsize(entry.downloadName), &filenameLen); + curChar += filenameLen; // advance the pointer + wcharCount -= filenameLen; // keep track of the amount remaining + if ((*curChar != L'\0') || (wcharCount <= 0)) + return false; // something is screwy, abort and disconnect + + // point it at the md5 + curChar++; + wcharCount--; + + // -------------------------------------------------------------------- + // read in the md5 + ReadStringFromMsg(curChar, entry.md5, arrsize(entry.md5), &filenameLen); + curChar += filenameLen; // advance the pointer + wcharCount -= filenameLen; // keep track of the amount remaining + if ((*curChar != L'\0') || (wcharCount <= 0)) + return false; // something is screwy, abort and disconnect + + // point it at the md5 for compressed files + curChar++; + wcharCount--; + + // -------------------------------------------------------------------- + // read in the md5 for compressed files + ReadStringFromMsg(curChar, entry.md5compressed, arrsize(entry.md5compressed), &filenameLen); + curChar += filenameLen; // advance the pointer + wcharCount -= filenameLen; // keep track of the amount remaining + if ((*curChar != L'\0') || (wcharCount <= 0)) + return false; // something is screwy, abort and disconnect + + // point it at the first part of the filesize value (format: 0xHHHHLLLL) + curChar++; + wcharCount--; + + // -------------------------------------------------------------------- + if (wcharCount < 2) // we have to have 2 chars for the size + return false; // screwy data + ReadUnsignedFromMsg(curChar, &entry.fileSize); + curChar += 2; + wcharCount -= 2; + if ((*curChar != L'\0') || (wcharCount <= 0)) + return false; // screwy data + + // point it at the first part of the zipsize value (format: 0xHHHHLLLL) + curChar++; + wcharCount--; + + // -------------------------------------------------------------------- + if (wcharCount < 2) // we have to have 2 chars for the size + return false; // screwy data + ReadUnsignedFromMsg(curChar, &entry.zipSize); + curChar += 2; + wcharCount -= 2; + if ((*curChar != L'\0') || (wcharCount <= 0)) + return false; // screwy data + + // point it at the first part of the flags value (format: 0xHHHHLLLL) + curChar++; + wcharCount--; + + // -------------------------------------------------------------------- + if (wcharCount < 2) // we have to have 2 chars for the size + return false; // screwy data + ReadUnsignedFromMsg(curChar, &entry.flags); + curChar += 2; + wcharCount -= 2; + if ((*curChar != L'\0') || (wcharCount <= 0)) + return false; // screwy data + + // -------------------------------------------------------------------- + // point it at either the second part of the terminator, or the next filename + curChar++; + wcharCount--; + + // do sanity checking + if (*curChar == L'\0') { + // we hit the terminator + if (wcharCount != 1) + return false; // invalid data, we shouldn't have any more + done = true; // we're done + } + else if (wcharCount < 14) + // we must have at least three 1-char strings, three nulls, three 32-bit ints, and 2-char terminator left (3+3+6+2) + return false; // screwy data + + // increment entries received + m_numEntriesReceived++; + if ((m_numEntriesReceived >= numFiles) && !done) { + // too much data, abort + return false; + } + } + + // check for completion + if (m_numEntriesReceived >= numFiles) + { + // all entires received, mark as complete + m_result = reply.result; + m_state = kTransStateComplete; + } + return true; +} + +/***************************************************************************** +* +* FileDownloadRequestTrans +* +***/ + +//============================================================================ +DownloadRequestTrans::DownloadRequestTrans ( + FNetCliFileDownloadRequestCallback callback, + void * param, + const wchar filename[], + hsStream * writer, + unsigned buildId +) : NetFileTrans(kDownloadRequestTrans) +, m_callback(callback) +, m_param(param) +, m_writer(writer) +, m_totalBytesReceived(0) +, m_buildId(buildId) +{ + StrCopy(m_filename, filename, arrsize(m_filename)); + // This transaction issues "sub transactions" which must complete + // before this one even though they were issued after us. + m_hasSubTrans = true; +} + +//============================================================================ +bool DownloadRequestTrans::Send () { + if (!AcquireConn()) + return false; + + Cli2File_FileDownloadRequest filedownloadReq; + StrCopy(filedownloadReq.filename, m_filename, arrsize(m_filename)); + filedownloadReq.messageId = kCli2File_FileDownloadRequest; + filedownloadReq.transId = m_transId; + filedownloadReq.messageBytes = sizeof(filedownloadReq); + filedownloadReq.buildId = m_buildId; + + m_conn->Send(&filedownloadReq, sizeof(filedownloadReq)); + + return true; +} + +//============================================================================ +void DownloadRequestTrans::Post () { + m_callback(m_result, m_param, m_filename, m_writer); +} + +//============================================================================ +bool DownloadRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + m_timeoutAtMs = TimeGetMs() + NetTransGetTimeoutMs(); // Reset the timeout counter + + ref(bytes); + const File2Cli_FileDownloadReply & reply = *(const File2Cli_FileDownloadReply *) msg; + + dword byteCount = reply.byteCount; + const byte* data = reply.fileData; + + // tell the server we got the data + Cli2File_FileDownloadChunkAck fileAck; + fileAck.messageId = kCli2File_FileDownloadChunkAck; + fileAck.transId = reply.transId; + fileAck.messageBytes = sizeof(fileAck); + fileAck.readerId = reply.readerId; + + m_conn->Send(&fileAck, fileAck.messageBytes); + + if (IS_NET_ERROR(reply.result)) { + // we have a problem... indicate we are done and abort + m_result = reply.result; + m_state = kTransStateComplete; + return true; + } + + // we have data to write, so queue it for write in the main thread (we're + // currently in a net recv thread) + if (byteCount > 0) { + RcvdFileDownloadChunkTrans * writeTrans = NEW(RcvdFileDownloadChunkTrans); + writeTrans->writer = m_writer; + writeTrans->bytes = byteCount; + writeTrans->data = (byte *)ALLOC(byteCount); + MemCopy(writeTrans->data, data, byteCount); + NetTransSend(writeTrans); + } + m_totalBytesReceived += byteCount; + + if (m_totalBytesReceived >= reply.totalFileSize) { + // all bytes received, mark as complete + m_result = reply.result; + m_state = kTransStateComplete; + } + return true; +} + +/***************************************************************************** +* +* RcvdFileDownloadChunkTrans +* +***/ + +//============================================================================ +RcvdFileDownloadChunkTrans::~RcvdFileDownloadChunkTrans () { + FREE(data); +} + +//============================================================================ +void RcvdFileDownloadChunkTrans::Post () { + writer->Write(bytes, data); + m_result = kNetSuccess; + m_state = kTransStateComplete; +} + + +} using namespace File; + + +/***************************************************************************** +* +* NetFileTrans +* +***/ + +//============================================================================ +NetFileTrans::NetFileTrans (ETransType transType) +: NetTrans(kNetProtocolCli2File, transType) +, m_conn(nil) +{ +} + +//============================================================================ +NetFileTrans::~NetFileTrans () { + ReleaseConn(); +} + +//============================================================================ +bool NetFileTrans::AcquireConn () { + if (!m_conn) + m_conn = GetConnIncRef("AcquireConn"); + return m_conn != nil; +} + +//============================================================================ +void NetFileTrans::ReleaseConn () { + if (m_conn) { + m_conn->DecRef("AcquireConn"); + m_conn = nil; + } +} + + +/***************************************************************************** +* +* Protected functions +* +***/ + +//============================================================================ +void FileInitialize () { + s_running = true; +} + +//============================================================================ +void FileDestroy (bool wait) { + s_running = false; + + NetTransCancelByProtocol( + kNetProtocolCli2File, + kNetErrRemoteShutdown + ); + NetMsgProtocolDestroy( + kNetProtocolCli2File, + false + ); + + s_critsect.Enter(); + { + while (CliFileConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_active = nil; + } + s_critsect.Leave(); + + if (!wait) + return; + + while (s_perf[kPerfConnCount]) { + NetTransUpdate(); + AsyncSleep(10); + } +} + +//============================================================================ +bool FileQueryConnected () { + bool result; + s_critsect.Enter(); + result = s_active != nil; + s_critsect.Leave(); + return result; +} + +//============================================================================ +unsigned FileGetConnId () { + unsigned connId; + s_critsect.Enter(); + connId = (s_active) ? s_active->seq : 0; + s_critsect.Leave(); + return connId; +} + +} using namespace Ngl; + +/***************************************************************************** +* +* Exported functions +* +***/ + +//============================================================================ +void NetCliFileStartConnect ( + const wchar * fileAddrList[], + unsigned fileAddrCount, + bool isPatcher /* = false */ +) { + // TEMP: Only connect to one file server until we fill out this module + // to choose the "best" file connection. + fileAddrCount = min(fileAddrCount, 1); + s_connectBuildId = isPatcher ? kFileSrvBuildId : BuildId(); + s_serverType = kSrvTypeNone; + + for (unsigned i = 0; i < fileAddrCount; ++i) { + // Do we need to lookup the address? + const wchar * name = fileAddrList[i]; + while (unsigned ch = *name) { + ++name; + if (!(isdigit(ch) || ch == L'.' || ch == L':')) { + AsyncCancelId cancelId; + AsyncAddressLookupName( + &cancelId, + AsyncLookupCallback, + fileAddrList[i], + kNetDefaultClientPort, + nil + ); + break; + } + } + if (!name[0]) { + NetAddress addr; + NetAddressFromString(&addr, fileAddrList[i], kNetDefaultClientPort); + Connect(fileAddrList[i], addr); + } + } +} + +//============================================================================ +void NetCliFileStartConnectAsServer ( + const wchar * fileAddrList[], + unsigned fileAddrCount, + unsigned serverType, + unsigned serverBuildId +) { + // TEMP: Only connect to one file server until we fill out this module + // to choose the "best" file connection. + fileAddrCount = min(fileAddrCount, 1); + s_connectBuildId = serverBuildId; + s_serverType = serverType; + + for (unsigned i = 0; i < fileAddrCount; ++i) { + // Do we need to lookup the address? + const wchar * name = fileAddrList[i]; + while (unsigned ch = *name) { + ++name; + if (!(isdigit(ch) || ch == L'.' || ch == L':')) { + AsyncCancelId cancelId; + AsyncAddressLookupName( + &cancelId, + AsyncLookupCallback, + fileAddrList[i], + kNetDefaultClientPort, + nil + ); + break; + } + } + if (!name[0]) { + NetAddress addr; + NetAddressFromString(&addr, fileAddrList[i], kNetDefaultServerPort); + Connect(fileAddrList[i], addr); + } + } +} + +//============================================================================ +void NetCliFileDisconnect () { + s_critsect.Enter(); + { + while (CliFileConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_active = nil; + } + s_critsect.Leave(); +} + +//============================================================================ +void NetCliFileBuildIdRequest ( + FNetCliFileBuildIdRequestCallback callback, + void * param +) { + BuildIdRequestTrans * trans = NEW(BuildIdRequestTrans)( + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliFileRegisterBuildIdUpdate (FNetCliFileBuildIdUpdateCallback callback) { + s_buildIdCallback = callback; +} + +//============================================================================ +void NetCliFileManifestRequest ( + FNetCliFileManifestRequestCallback callback, + void * param, + const wchar group[], + unsigned buildId /* = 0 */ +) { + ManifestRequestTrans * trans = NEW(ManifestRequestTrans)( + callback, + param, + group, + buildId + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliFileDownloadRequest ( + const wchar filename[], + hsStream * writer, + FNetCliFileDownloadRequestCallback callback, + void * param, + unsigned buildId /* = 0 */ +) { + DownloadRequestTrans * trans = NEW(DownloadRequestTrans)( + callback, + param, + filename, + writer, + buildId + ); + NetTransSend(trans); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglFile.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglFile.h new file mode 100644 index 00000000..fd7788c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglFile.h @@ -0,0 +1,121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglFile.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLFILE_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglFile.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLFILE_H + + +/***************************************************************************** +* +* Client file functions +* +***/ + + +//============================================================================ +// Connect +//============================================================================ +void NetCliFileStartConnect ( + const wchar * fileAddrList[], + unsigned fileAddrCount, + bool isPatcher = false +); +void NetCliFileStartConnectAsServer ( + const wchar * fileAddrList[], + unsigned fileAddrCount, + unsigned serverType, + unsigned serverBuildId +); + +//============================================================================ +// Disconnect +//============================================================================ +void NetCliFileDisconnect (); + +//============================================================================ +// File server related messages +//============================================================================ +typedef void (*FNetCliFileBuildIdRequestCallback)( + ENetError result, + void * param, + unsigned buildId +); +void NetCliFileBuildIdRequest ( + FNetCliFileBuildIdRequestCallback callback, + void * param +); +typedef void (*FNetCliFileBuildIdUpdateCallback)(unsigned buildId); +void NetCliFileRegisterBuildIdUpdate (FNetCliFileBuildIdUpdateCallback callback); + +//============================================================================ +// Manifest +//============================================================================ +struct NetCliFileManifestEntry { + wchar clientName[MAX_PATH]; // path and file on client side (for comparison) + wchar downloadName[MAX_PATH]; // path and file on server side (for download) + wchar md5[MAX_PATH]; + wchar md5compressed[MAX_PATH]; // md5 for the compressed file + unsigned fileSize; + unsigned zipSize; + unsigned flags; +}; +typedef void (*FNetCliFileManifestRequestCallback)( + ENetError result, + void * param, + const wchar group[], + const NetCliFileManifestEntry manifest[], + unsigned entryCount +); +void NetCliFileManifestRequest ( + FNetCliFileManifestRequestCallback callback, + void * param, + const wchar group[], // the group of files you want (empty or nil = all) + unsigned buildId = 0 // 0 = get latest, other = get particular build (servers only) +); + +//============================================================================ +// File Download +//============================================================================ +typedef void (*FNetCliFileDownloadRequestCallback)( + ENetError result, + void * param, + const wchar filename[], + hsStream * writer +); +void NetCliFileDownloadRequest ( + const wchar filename[], + hsStream * writer, + FNetCliFileDownloadRequestCallback callback, + void * param, + unsigned buildId = 0 // 0 = get latest, other = get particular build (servers only) +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGame.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGame.cpp new file mode 100644 index 00000000..9e9b0c01 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGame.cpp @@ -0,0 +1,813 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGame.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + +// This file excluded from pre-compiled header because it is auto-generated by the build server. +#include "pnNetBase/pnNbGameKey.hpp" + +namespace Ngl { namespace Game { +/***************************************************************************** +* +* Private +* +***/ + +struct CliGmConn : AtomicRef { + LINK(CliGmConn) link; + + CCritSect critsect; + AsyncSocket sock; + AsyncCancelId cancelId; + NetCli * cli; + NetAddress addr; + unsigned seq; + bool abandoned; + + // ping + AsyncTimer * pingTimer; + unsigned pingSendTimeMs; + unsigned lastHeardTimeMs; + + CliGmConn (); + ~CliGmConn (); + + void Send (const unsigned_ptr fields[], unsigned count); +}; + + +//============================================================================ +// JoinAgeRequestTrans +//============================================================================ +struct JoinAgeRequestTrans : NetGameTrans { + FNetCliGameJoinAgeRequestCallback m_callback; + void * m_param; + // sent + unsigned m_ageMcpId; + Uuid m_accountUuid; + unsigned m_playerInt; + + JoinAgeRequestTrans ( + unsigned ageMcpId, + const Uuid & accountUuid, + unsigned playerInt, + FNetCliGameJoinAgeRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// RcvdPropagatedBufferTrans +//============================================================================ +struct RcvdPropagatedBufferTrans : NetNotifyTrans { + + unsigned bufferType; + unsigned bufferBytes; + byte * bufferData; + + RcvdPropagatedBufferTrans () : NetNotifyTrans(kGmRcvdPropagatedBufferTrans) {} + ~RcvdPropagatedBufferTrans (); + void Post (); +}; + +//============================================================================ +// RcvdGameMgrMsgTrans +//============================================================================ +struct RcvdGameMgrMsgTrans : NetNotifyTrans { + + unsigned bufferBytes; + byte * bufferData; + + RcvdGameMgrMsgTrans () : NetNotifyTrans(kGmRcvdGameMgrMsgTrans) {} + ~RcvdGameMgrMsgTrans (); + void Post (); +}; + + +/***************************************************************************** +* +* Private data +* +***/ + +enum { + kPerfConnCount, + kPingDisabled, + kNumPerf +}; + +static bool s_running; +static CCritSect s_critsect; +static LISTDECL(CliGmConn, link) s_conns; +static CliGmConn * s_active; +static FNetCliGameRecvBufferHandler s_bufHandler; +static FNetCliGameRecvGameMgrMsgHandler s_gameMgrMsgHandler; +static long s_perf[kNumPerf]; + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +static unsigned GetNonZeroTimeMs () { + if (unsigned ms = TimeGetMs()) + return ms; + return 1; +} + +//============================================================================ +static CliGmConn * GetConnIncRef_CS (const char tag[]) { + if (CliGmConn * conn = s_active) + if (conn->cli) { + conn->IncRef(tag); + return conn; + } + return nil; +} + +//============================================================================ +static CliGmConn * GetConnIncRef (const char tag[]) { + CliGmConn * conn; + s_critsect.Enter(); + { + conn = GetConnIncRef_CS(tag); + } + s_critsect.Leave(); + return conn; +} + +//============================================================================ +static void UnlinkAndAbandonConn_CS (CliGmConn * conn) { + s_conns.Unlink(conn); + conn->abandoned = true; + if (conn->cancelId) { + AsyncSocketConnectCancel(nil, conn->cancelId); + conn->cancelId = 0; + } + else if (conn->sock) { + AsyncSocketDisconnect(conn->sock, true); + } + else { + conn->DecRef("Lifetime"); + } +} + +//============================================================================ +static bool ConnEncrypt (ENetError error, void * param) { + CliGmConn * conn = (CliGmConn *) param; + + if (IS_NET_SUCCESS(error)) { + s_critsect.Enter(); + { + SWAP(s_active, conn); + } + s_critsect.Leave(); + } + + return IS_NET_SUCCESS(error); +} + +//============================================================================ +static void NotifyConnSocketConnect (CliGmConn * conn) { + + conn->TransferRef("Connecting", "Connected"); + conn->cli = NetCliConnectAccept( + conn->sock, + kNetProtocolCli2Game, + true, + ConnEncrypt, + 0, + nil, + conn + ); +} + +//============================================================================ +static void NotifyConnSocketConnectFailed (CliGmConn * conn) { + bool notify; + s_critsect.Enter(); + { + conn->cancelId = 0; + s_conns.Unlink(conn); + + notify + = s_running + && !conn->abandoned + && (!s_active || conn == s_active); + + if (conn == s_active) + s_active = nil; + } + s_critsect.Leave(); + + NetTransCancelByConnId(conn->seq, kNetErrTimeout); + conn->DecRef("Connecting"); + conn->DecRef("Lifetime"); + + if (notify) + ReportNetError(kNetProtocolCli2Game, kNetErrConnectFailed); +} + +//============================================================================ +static void NotifyConnSocketDisconnect (CliGmConn * conn) { + + bool notify; + s_critsect.Enter(); + { + s_conns.Unlink(conn); + + notify + = s_running + && !conn->abandoned + && (!s_active || conn == s_active); + + if (conn == s_active) + s_active = nil; + } + s_critsect.Leave(); + + // Cancel all transactions in process on this connection. + NetTransCancelByConnId(conn->seq, kNetErrTimeout); + conn->DecRef("Connected"); + conn->DecRef("Lifetime"); + + if (notify) + ReportNetError(kNetProtocolCli2Game, kNetErrDisconnected); +} + +//============================================================================ +static bool NotifyConnSocketRead (CliGmConn * conn, AsyncNotifySocketRead * read) { + // TODO: Only dispatch messages from the active game server + conn->lastHeardTimeMs = GetNonZeroTimeMs(); + bool result = NetCliDispatch(conn->cli, read->buffer, read->bytes, nil); + read->bytesProcessed += read->bytes; + return result; +} + +//============================================================================ +static bool SocketNotifyCallback ( + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +) { + bool result = true; + CliGmConn * conn; + + switch (code) { + case kNotifySocketConnectSuccess: + conn = (CliGmConn *) notify->param; + *userState = conn; + conn->TransferRef("Connecting", "Connected"); + bool abandoned; + s_critsect.Enter(); + { + conn->sock = sock; + conn->cancelId = 0; + abandoned = conn->abandoned; + } + s_critsect.Leave(); + if (abandoned) + AsyncSocketDisconnect(sock, true); + else + NotifyConnSocketConnect(conn); + break; + + case kNotifySocketConnectFailed: + conn = (CliGmConn *) notify->param; + NotifyConnSocketConnectFailed(conn); + break; + + case kNotifySocketDisconnect: + conn = (CliGmConn *) *userState; + NotifyConnSocketDisconnect(conn); + break; + + case kNotifySocketRead: + conn = (CliGmConn *) *userState; + result = NotifyConnSocketRead(conn, (AsyncNotifySocketRead *) notify); + break; + } + + return result; +} + +//============================================================================ +static void Connect ( + const NetAddress & addr +) { + CliGmConn * conn = NEWZERO(CliGmConn); + conn->addr = addr; + conn->seq = ConnNextSequence(); + conn->lastHeardTimeMs = GetNonZeroTimeMs(); + + conn->IncRef("Lifetime"); + conn->IncRef("Connecting"); + + s_critsect.Enter(); + { + while (CliGmConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_conns.Link(conn); + } + s_critsect.Leave(); + + Cli2Game_Connect connect; + connect.hdr.connType = kConnTypeCliToGame; + connect.hdr.hdrBytes = sizeof(connect.hdr); + connect.hdr.buildId = BuildId(); + connect.hdr.buildType = BuildType(); + connect.hdr.branchId = BranchId(); + connect.hdr.productId = ProductId(); + connect.data.dataBytes = sizeof(connect.data); + + AsyncSocketConnect( + &conn->cancelId, + addr, + SocketNotifyCallback, + conn, + &connect, + sizeof(connect), + 0, + 0 + ); +} + + +/***************************************************************************** +* +* CliGmConn +* +***/ + +//============================================================================ +CliGmConn::CliGmConn () { + AtomicAdd(&s_perf[kPerfConnCount], 1); +} + +//============================================================================ +CliGmConn::~CliGmConn () { + if (cli) + NetCliDelete(cli, true); + AtomicAdd(&s_perf[kPerfConnCount], -1); +} + +//============================================================================ +void CliGmConn::Send (const unsigned_ptr fields[], unsigned count) { + critsect.Enter(); + { + NetCliSend(cli, fields, count); + NetCliFlush(cli); + } + critsect.Leave(); +} + + +/***************************************************************************** +* +* Cli2Game protocol +* +***/ + +//============================================================================ +static bool Recv_PingReply ( + const byte msg[], + unsigned bytes, + void * param +) { + ref(msg); + ref(bytes); + ref(param); + return true; +} + +//============================================================================ +static bool Recv_JoinAgeReply ( + const byte msg[], + unsigned bytes, + void * param +) { + ref(bytes); + ref(param); + + const Game2Cli_JoinAgeReply & reply = *(const Game2Cli_JoinAgeReply *)msg; + if (sizeof(reply) != bytes) + return false; + + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_PropagateBuffer ( + const byte msg[], + unsigned bytes, + void * param +) { + ref(bytes); + ref(param); + + const Game2Cli_PropagateBuffer & reply = *(const Game2Cli_PropagateBuffer *)msg; + + RcvdPropagatedBufferTrans * trans = NEW(RcvdPropagatedBufferTrans); + trans->bufferType = reply.type; + trans->bufferBytes = reply.bytes; + trans->bufferData = (byte *)ALLOC(reply.bytes); + MemCopy(trans->bufferData, reply.buffer, reply.bytes); + NetTransSend(trans); + + return true; +} + +//============================================================================ +static bool Recv_GameMgrMsg ( + const byte msg[], + unsigned bytes, + void * param +) { + ref(bytes); + ref(param); + + const Game2Cli_GameMgrMsg & reply = *(const Game2Cli_GameMgrMsg *)msg; + + RcvdGameMgrMsgTrans * trans = NEW(RcvdGameMgrMsgTrans); + trans->bufferBytes = reply.bytes; + trans->bufferData = (byte *)ALLOC(reply.bytes); + MemCopy(trans->bufferData, reply.buffer, reply.bytes); + NetTransSend(trans); + + return true; +} + + +//============================================================================ +// Send/Recv protocol handler init +//============================================================================ +#define MSG(s) kNetMsg_Cli2Game_##s +static NetMsgInitSend s_send[] = { + { MSG(PingRequest), }, + { MSG(JoinAgeRequest), }, + { MSG(PropagateBuffer), }, + { MSG(GameMgrMsg), }, +}; +#undef MSG + +#define MSG(s) kNetMsg_Game2Cli_##s, Recv_##s +static NetMsgInitRecv s_recv[] = { + { MSG(PingReply) }, + { MSG(JoinAgeReply), }, + { MSG(PropagateBuffer), }, + { MSG(GameMgrMsg), }, +}; +#undef MSG + + +/***************************************************************************** +* +* JoinAgeRequestTrans +* +***/ + +//============================================================================ +JoinAgeRequestTrans::JoinAgeRequestTrans ( + unsigned ageMcpId, + const Uuid & accountUuid, + unsigned playerInt, + FNetCliGameJoinAgeRequestCallback callback, + void * param +) : NetGameTrans(kJoinAgeRequestTrans) +, m_ageMcpId(ageMcpId) +, m_accountUuid(accountUuid) +, m_playerInt(playerInt) +, m_callback(callback) +, m_param(param) +{ +} + +//============================================================================ +bool JoinAgeRequestTrans::Send () { + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2Game_JoinAgeRequest, + m_transId, + m_ageMcpId, + (unsigned_ptr) &m_accountUuid, + m_playerInt, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void JoinAgeRequestTrans::Post () { + m_callback( + m_result, + m_param + ); +} + +//============================================================================ +bool JoinAgeRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const Game2Cli_JoinAgeReply & reply = *(const Game2Cli_JoinAgeReply *) msg; + m_result = reply.result; + m_state = kTransStateComplete; + return true; +} + +/***************************************************************************** +* +* RcvdPropagatedBufferTrans +* +***/ + +//============================================================================ +RcvdPropagatedBufferTrans::~RcvdPropagatedBufferTrans () { + FREE(bufferData); +} + +//============================================================================ +void RcvdPropagatedBufferTrans::Post () { + if (s_bufHandler) + s_bufHandler(bufferType, bufferBytes, bufferData); +} + +/***************************************************************************** +* +* RcvdGameMgrMsgTrans +* +***/ + +//============================================================================ +RcvdGameMgrMsgTrans::~RcvdGameMgrMsgTrans () { + FREE(bufferData); +} + +//============================================================================ +void RcvdGameMgrMsgTrans::Post () { + if (s_gameMgrMsgHandler) + s_gameMgrMsgHandler((GameMsgHeader *)bufferData); +} + + +} using namespace Game; + + +/***************************************************************************** +* +* NetGameTrans +* +***/ + +//============================================================================ +NetGameTrans::NetGameTrans (ETransType transType) +: NetTrans(kNetProtocolCli2Game, transType) +, m_conn(nil) +{ +} + +//============================================================================ +NetGameTrans::~NetGameTrans () { + ReleaseConn(); +} + +//============================================================================ +bool NetGameTrans::AcquireConn () { + if (!m_conn) + m_conn = GetConnIncRef("AcquireConn"); + return m_conn != nil; +} + +//============================================================================ +void NetGameTrans::ReleaseConn () { + if (m_conn) { + m_conn->DecRef("AcquireConn"); + m_conn = nil; + } +} + + +/***************************************************************************** +* +* Protected functions +* +***/ + +//============================================================================ +void GameInitialize () { + s_running = true; + NetMsgProtocolRegister( + kNetProtocolCli2Game, + false, + s_send, arrsize(s_send), + s_recv, arrsize(s_recv), + kDhGValue, + BigNum(sizeof(kDhXData), kDhXData), + BigNum(sizeof(kDhNData), kDhNData) + ); +} + +//============================================================================ +void GameDestroy (bool wait) { + s_running = false; + s_bufHandler = nil; + s_gameMgrMsgHandler = nil; + + NetTransCancelByProtocol( + kNetProtocolCli2Game, + kNetErrRemoteShutdown + ); + NetMsgProtocolDestroy( + kNetProtocolCli2Game, + false + ); + + s_critsect.Enter(); + { + while (CliGmConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_active = nil; + } + s_critsect.Leave(); + + if (!wait) + return; + + while (s_perf[kPerfConnCount]) { + NetTransUpdate(); + AsyncSleep(10); + } +} + +//============================================================================ +bool GameQueryConnected () { + bool result; + s_critsect.Enter(); + { + if (nil != (result = s_active)) + result &= (nil != s_active->cli); + } + s_critsect.Leave(); + return result; +} + +//============================================================================ +unsigned GameGetConnId () { + unsigned connId; + s_critsect.Enter(); + connId = (s_active) ? s_active->seq : 0; + s_critsect.Leave(); + return connId; +} + +//============================================================================ +void GamePingEnable (bool enable) { + s_perf[kPingDisabled] = !enable; +} + +} using namespace Ngl; + + +/***************************************************************************** +* +* Exported functions +* +***/ + +//============================================================================ +void NetCliGameStartConnect ( + const NetAddressNode & node +) { + NetAddress addr; + NetAddressFromNode(node, kNetDefaultClientPort, &addr); + Connect(addr); +} + +//============================================================================ +void NetCliGameDisconnect () { + + s_critsect.Enter(); + { + while (CliGmConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_active = nil; + } + s_critsect.Leave(); +} + +//============================================================================ +void NetCliGameJoinAgeRequest ( + unsigned ageMcpId, + const Uuid & accountUuid, + unsigned playerInt, + FNetCliGameJoinAgeRequestCallback callback, + void * param +) { + JoinAgeRequestTrans * trans = NEWZERO(JoinAgeRequestTrans)( + ageMcpId, + accountUuid, + playerInt, + callback, + param + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliGameSetRecvBufferHandler ( + FNetCliGameRecvBufferHandler handler +) { + s_bufHandler = handler; +} + +//============================================================================ +void NetCliGamePropagateBuffer ( + unsigned type, + unsigned bytes, + const byte buffer[] +) { + CliGmConn * conn = GetConnIncRef("PropBuffer"); + if (!conn) + return; + + const unsigned_ptr msg[] = { + kCli2Game_PropagateBuffer, + type, + bytes, + (unsigned_ptr) buffer, + }; + + conn->Send(msg, arrsize(msg)); + + conn->DecRef("PropBuffer"); +} + +//============================================================================ +void NetCliGameSetRecvGameMgrMsgHandler (FNetCliGameRecvGameMgrMsgHandler handler) { + s_gameMgrMsgHandler = handler; +} + +//============================================================================ +void NetCliGameSendGameMgrMsg (GameMsgHeader * msgHdr) { + CliGmConn * conn = GetConnIncRef("GameMgrMsg"); + if (!conn) + return; + + const unsigned_ptr msg[] = { + kCli2Game_GameMgrMsg, + msgHdr->messageBytes, + (unsigned_ptr) msgHdr, + }; + + conn->Send(msg, arrsize(msg)); + + conn->DecRef("GameMgrMsg"); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGame.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGame.h new file mode 100644 index 00000000..e152fad7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGame.h @@ -0,0 +1,94 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGame.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLGAME_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGame.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLGAME_H + + +/***************************************************************************** +* +* Client-side Game functions +* +***/ + +//============================================================================ +// Connect +//============================================================================ +void NetCliGameStartConnect ( + const NetAddressNode & node +); + +//============================================================================ +// Disconnect +//============================================================================ +void NetCliGameDisconnect (); + +//============================================================================ +// Join Age +//============================================================================ +typedef void (*FNetCliGameJoinAgeRequestCallback)( + ENetError result, + void * param +); +void NetCliGameJoinAgeRequest ( + unsigned ageMcpId, + const Uuid & accountUuid, + unsigned playerInt, + FNetCliGameJoinAgeRequestCallback callback, + void * param +); + +//============================================================================ +// Propagate app-specific data +//============================================================================ +typedef void (*FNetCliGameRecvBufferHandler)( + unsigned type, + unsigned bytes, + const byte buffer[] +); +void NetCliGameSetRecvBufferHandler ( + FNetCliGameRecvBufferHandler handler +); +void NetCliGamePropagateBuffer ( + unsigned type, + unsigned bytes, + const byte buffer[] +); + +//============================================================================ +// GameMgrMsg +//============================================================================ +struct GameMsgHeader; +typedef void (*FNetCliGameRecvGameMgrMsgHandler)(GameMsgHeader * msg); +void NetCliGameSetRecvGameMgrMsgHandler (FNetCliGameRecvGameMgrMsgHandler handler); +void NetCliGameSendGameMgrMsg (GameMsgHeader * msg); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.cpp new file mode 100644 index 00000000..32ef18c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.cpp @@ -0,0 +1,1129 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGateKeeperLib/Private/plNglGateKeeper.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + +// This file excluded from pre-compiled header because it is auto-generated by the build server. +#include "pnNetBase/pnNbGateKeeperKey.hpp" + +namespace Ngl { namespace GateKeeper { + +/***************************************************************************** +* +* Private +* +***/ + +struct CliGkConn : AtomicRef { + CliGkConn (); + ~CliGkConn (); + + // Reconnection + AsyncTimer * reconnectTimer; + unsigned reconnectStartMs; + + // Ping + AsyncTimer * pingTimer; + unsigned pingSendTimeMs; + unsigned lastHeardTimeMs; + + // This function should be called during object construction + // to initiate connection attempts to the remote host whenever + // the socket is disconnected. + void AutoReconnect (); + bool AutoReconnectEnabled (); + void StopAutoReconnect (); // call before destruction + void StartAutoReconnect (); + void TimerReconnect (); + + // ping + void AutoPing (); + void StopAutoPing (); + void TimerPing (); + + void Send (const unsigned_ptr fields[], unsigned count); + + CCritSect critsect; + LINK(CliGkConn) link; + AsyncSocket sock; + NetCli * cli; + wchar name[MAX_PATH]; + NetAddress addr; + Uuid token; + unsigned seq; + unsigned serverChallenge; + AsyncCancelId cancelId; + bool abandoned; +}; + + +//============================================================================ +// PingRequestTrans +//============================================================================ +struct PingRequestTrans : NetGateKeeperTrans { + FNetCliGateKeeperPingRequestCallback m_callback; + void * m_param; + unsigned m_pingAtMs; + unsigned m_replyAtMs; + ARRAY(byte) m_payload; + + PingRequestTrans ( + FNetCliGateKeeperPingRequestCallback callback, + void * param, + unsigned pingAtMs, + unsigned payloadBytes, + const void * payload + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// FileSrvIpAddressRequestTrans +//============================================================================ +struct FileSrvIpAddressRequestTrans : NetGateKeeperTrans { + FNetCliGateKeeperFileSrvIpAddressRequestCallback m_callback; + void * m_param; + wchar m_addr[64]; + bool m_isPatcher; + + FileSrvIpAddressRequestTrans ( + FNetCliGateKeeperFileSrvIpAddressRequestCallback callback, + void * param, + bool isPatcher + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + +//============================================================================ +// AuthSrvIpAddressRequestTrans +//============================================================================ +struct AuthSrvIpAddressRequestTrans : NetGateKeeperTrans { + FNetCliGateKeeperAuthSrvIpAddressRequestCallback m_callback; + void * m_param; + wchar m_addr[64]; + + AuthSrvIpAddressRequestTrans ( + FNetCliGateKeeperAuthSrvIpAddressRequestCallback callback, + void * param + ); + + bool Send (); + void Post (); + bool Recv ( + const byte msg[], + unsigned bytes + ); +}; + + +/***************************************************************************** +* +* Private data +* +***/ + +enum { + kPerfConnCount, + kPingDisabled, + kAutoReconnectDisabled, + kNumPerf +}; + +static bool s_running; +static CCritSect s_critsect; +static LISTDECL(CliGkConn, link) s_conns; +static CliGkConn * s_active; + +static long s_perf[kNumPerf]; + + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//=========================================================================== +static unsigned GetNonZeroTimeMs () { + if (unsigned ms = TimeGetMs()) + return ms; + return 1; +} + +//============================================================================ +static CliGkConn * GetConnIncRef_CS (const char tag[]) { + if (CliGkConn * conn = s_active) { + conn->IncRef(tag); + return conn; + } + return nil; +} + +//============================================================================ +static CliGkConn * GetConnIncRef (const char tag[]) { + CliGkConn * conn; + s_critsect.Enter(); + { + conn = GetConnIncRef_CS(tag); + } + s_critsect.Leave(); + return conn; +} + +//============================================================================ +static void UnlinkAndAbandonConn_CS (CliGkConn * conn) { + s_conns.Unlink(conn); + conn->abandoned = true; + + conn->StopAutoReconnect(); + + if (conn->cancelId) { + AsyncSocketConnectCancel(nil, conn->cancelId); + conn->cancelId = 0; + } + else if (conn->sock) { + AsyncSocketDisconnect(conn->sock, true); + } + else { + conn->DecRef("Lifetime"); + } +} + +//============================================================================ +static void SendClientRegisterRequest (CliGkConn * conn) { +/* const unsigned_ptr msg[] = { + kCli2GateKeeper_ClientRegisterRequest, + BuildId(), + }; + + conn->Send(msg, arrsize(msg));*/ +} + +//============================================================================ +static bool ConnEncrypt (ENetError error, void * param) { + CliGkConn * conn = (CliGkConn *) param; + + if (IS_NET_SUCCESS(error)) { + + //SendClientRegisterRequest(conn); + + if (!s_perf[kPingDisabled]) + conn->AutoPing(); + + s_critsect.Enter(); + { + SWAP(s_active, conn); + } + s_critsect.Leave(); + + // AuthConnectedNotifyTrans * trans = NEW(AuthConnectedNotifyTrans); + // NetTransSend(trans); + } + + return IS_NET_SUCCESS(error); +} + +//============================================================================ +static void NotifyConnSocketConnect (CliGkConn * conn) { + + conn->TransferRef("Connecting", "Connected"); + conn->cli = NetCliConnectAccept( + conn->sock, + kNetProtocolCli2GateKeeper, + false, + ConnEncrypt, + 0, + nil, + conn + ); +} + +//============================================================================ +static void CheckedReconnect (CliGkConn * conn, ENetError error) { + + unsigned disconnectedMs = GetNonZeroTimeMs() - conn->lastHeardTimeMs; + + // no auto-reconnect or haven't heard from the server in a while? + if (!conn->AutoReconnectEnabled() || (int)disconnectedMs >= (int)kDisconnectedTimeoutMs) { + // Cancel all transactions in progress on this connection. + NetTransCancelByConnId(conn->seq, kNetErrTimeout); + // conn is dead. + conn->DecRef("Lifetime"); + ReportNetError(kNetProtocolCli2GateKeeper, error); + } + else { + if (conn->token != kNilGuid) + // previously encrypted; reconnect quickly + conn->reconnectStartMs = GetNonZeroTimeMs() + 500; + else + // never encrypted; reconnect slowly + conn->reconnectStartMs = GetNonZeroTimeMs() + kMaxReconnectIntervalMs; + + // clean up the socket and start reconnect + if (conn->cli) + NetCliDelete(conn->cli, true); + conn->cli = nil; + conn->sock = nil; + + conn->StartAutoReconnect(); + } +} + +//============================================================================ +static void NotifyConnSocketConnectFailed (CliGkConn * conn) { + + s_critsect.Enter(); + { + conn->cancelId = 0; + s_conns.Unlink(conn); + + if (conn == s_active) + s_active = nil; + } + s_critsect.Leave(); + + CheckedReconnect(conn, kNetErrConnectFailed); + + conn->DecRef("Connecting"); +} + +//============================================================================ +static void NotifyConnSocketDisconnect (CliGkConn * conn) { + + conn->StopAutoPing(); + + s_critsect.Enter(); + { + conn->cancelId = 0; + s_conns.Unlink(conn); + + if (conn == s_active) + s_active = nil; + } + s_critsect.Leave(); + + // Cancel all transactions in process on this connection. + NetTransCancelByConnId(conn->seq, kNetErrTimeout); + conn->DecRef("Connected"); + conn->DecRef("Lifetime"); +} + +//============================================================================ +static bool NotifyConnSocketRead (CliGkConn * conn, AsyncNotifySocketRead * read) { + // TODO: Only dispatch messages from the active auth server + conn->lastHeardTimeMs = GetNonZeroTimeMs(); + bool result = NetCliDispatch(conn->cli, read->buffer, read->bytes, conn); + read->bytesProcessed += read->bytes; + return result; +} + +//============================================================================ +static bool SocketNotifyCallback ( + AsyncSocket sock, + EAsyncNotifySocket code, + AsyncNotifySocket * notify, + void ** userState +) { + bool result = true; + CliGkConn * conn; + + switch (code) { + case kNotifySocketConnectSuccess: + conn = (CliGkConn *) notify->param; + *userState = conn; + bool abandoned; + s_critsect.Enter(); + { + conn->sock = sock; + conn->cancelId = 0; + abandoned = conn->abandoned; + } + s_critsect.Leave(); + if (abandoned) + AsyncSocketDisconnect(sock, true); + else + NotifyConnSocketConnect(conn); + break; + + case kNotifySocketConnectFailed: + conn = (CliGkConn *) notify->param; + NotifyConnSocketConnectFailed(conn); + break; + + case kNotifySocketDisconnect: + conn = (CliGkConn *) *userState; + NotifyConnSocketDisconnect(conn); + break; + + case kNotifySocketRead: + conn = (CliGkConn *) *userState; + result = NotifyConnSocketRead(conn, (AsyncNotifySocketRead *) notify); + break; + } + + return result; +} + +//============================================================================ +static void Connect ( + CliGkConn * conn +) { + ASSERT(s_running); + + conn->pingSendTimeMs = 0; + + s_critsect.Enter(); + { + while (CliGkConn * oldConn = s_conns.Head()) { + if (oldConn != conn) + UnlinkAndAbandonConn_CS(oldConn); + else + s_conns.Unlink(oldConn); + } + s_conns.Link(conn); + } + s_critsect.Leave(); + + Cli2GateKeeper_Connect connect; + connect.hdr.connType = kConnTypeCliToGateKeeper; + connect.hdr.hdrBytes = sizeof(connect.hdr); + connect.hdr.buildId = BuildId(); + connect.hdr.buildType = BuildType(); + connect.hdr.branchId = BranchId(); + connect.hdr.productId = ProductId(); + connect.data.token = conn->token; + connect.data.dataBytes = sizeof(connect.data); + + AsyncSocketConnect( + &conn->cancelId, + conn->addr, + SocketNotifyCallback, + conn, + &connect, + sizeof(connect), + 0, + 0 + ); +} + +//============================================================================ +static void Connect ( + const wchar name[], + const NetAddress & addr +) { + ASSERT(s_running); + + CliGkConn * conn = NEWZERO(CliGkConn); + conn->addr = addr; + conn->seq = ConnNextSequence(); + conn->lastHeardTimeMs = GetNonZeroTimeMs(); // used in connect timeout, and ping timeout + StrCopy(conn->name, name, arrsize(conn->name)); + + conn->IncRef("Lifetime"); + conn->AutoReconnect(); +} + +//============================================================================ +static void AsyncLookupCallback ( + void * param, + const wchar name[], + unsigned addrCount, + const NetAddress addrs[] +) { + ref(param); + + if (!addrCount) { + ReportNetError(kNetProtocolCli2GateKeeper, kNetErrNameLookupFailed); + return; + } + + for (unsigned i = 0; i < addrCount; ++i) { + Connect(name, addrs[i]); + } +} + + + + +/***************************************************************************** +* +* CliGkConn +* +***/ + +//=========================================================================== +static unsigned CliGkConnTimerDestroyed (void * param) { + CliGkConn * conn = (CliGkConn *) param; + conn->DecRef("TimerDestroyed"); + return kAsyncTimeInfinite; +} + +//=========================================================================== +static unsigned CliGkConnReconnectTimerProc (void * param) { + ((CliGkConn *) param)->TimerReconnect(); + return kAsyncTimeInfinite; +} + +//=========================================================================== +static unsigned CliGkConnPingTimerProc (void * param) { + ((CliGkConn *) param)->TimerPing(); + return kPingIntervalMs; +} + +//============================================================================ +CliGkConn::CliGkConn () { + AtomicAdd(&s_perf[kPerfConnCount], 1); +} + +//============================================================================ +CliGkConn::~CliGkConn () { + if (cli) + NetCliDelete(cli, true); + AtomicAdd(&s_perf[kPerfConnCount], -1); +} + +//=========================================================================== +void CliGkConn::TimerReconnect () { + ASSERT(!sock); + ASSERT(!cancelId); + + if (!s_running) { + s_critsect.Enter(); + UnlinkAndAbandonConn_CS(this); + s_critsect.Leave(); + } + else { + IncRef("Connecting"); + + // Remember the time we started the reconnect attempt, guarding against + // TimeGetMs() returning zero (unlikely), as a value of zero indicates + // a first-time connect condition to StartAutoReconnect() + reconnectStartMs = GetNonZeroTimeMs(); + + Connect(this); + } +} + +//=========================================================================== +// This function is called when after a disconnect to start a new connection +void CliGkConn::StartAutoReconnect () { + critsect.Enter(); + if (reconnectTimer && !s_perf[kAutoReconnectDisabled]) { + // Make reconnect attempts at regular intervals. If the last attempt + // took more than the specified max interval time then reconnect + // immediately; otherwise wait until the time interval is up again + // then reconnect. + unsigned remainingMs = 0; + if (reconnectStartMs) { + remainingMs = reconnectStartMs - GetNonZeroTimeMs(); + if ((signed)remainingMs < 0) + remainingMs = 0; + LogMsg(kLogPerf, L"GateKeeper auto-reconnecting in %u ms", remainingMs); + } + AsyncTimerUpdate(reconnectTimer, remainingMs); + } + critsect.Leave(); +} + +//=========================================================================== +// This function should be called during object construction +// to initiate connection attempts to the remote host whenever +// the socket is disconnected. +void CliGkConn::AutoReconnect () { + + ASSERT(!reconnectTimer); + IncRef("ReconnectTimer"); + critsect.Enter(); + { + AsyncTimerCreate( + &reconnectTimer, + CliGkConnReconnectTimerProc, + 0, // immediate callback + this + ); + } + critsect.Leave(); +} + +//============================================================================ +void CliGkConn::StopAutoReconnect () { + critsect.Enter(); + { + if (AsyncTimer * timer = reconnectTimer) { + reconnectTimer = nil; + AsyncTimerDeleteCallback(timer, CliGkConnTimerDestroyed); + } + } + critsect.Leave(); +} + +//============================================================================ +bool CliGkConn::AutoReconnectEnabled () { + + return (reconnectTimer != nil) && !s_perf[kAutoReconnectDisabled]; +} + +//============================================================================ +void CliGkConn::AutoPing () { + ASSERT(!pingTimer); + IncRef("PingTimer"); + critsect.Enter(); + { + AsyncTimerCreate( + &pingTimer, + CliGkConnPingTimerProc, + sock ? 0 : kAsyncTimeInfinite, + this + ); + } + critsect.Leave(); +} + +//============================================================================ +void CliGkConn::StopAutoPing () { + critsect.Enter(); + { + if (AsyncTimer * timer = pingTimer) { + pingTimer = nil; + AsyncTimerDeleteCallback(timer, CliGkConnTimerDestroyed); + } + } + critsect.Leave(); +} + +//============================================================================ +void CliGkConn::TimerPing () { + +#if 0 + // if the time difference between when we last sent a ping and when we last + // heard from the server is >= 3x the ping interval, the socket is stale. + if (pingSendTimeMs && abs(int(pingSendTimeMs - lastHeardTimeMs)) >= kPingTimeoutMs) { + // ping timed out, disconnect the socket + AsyncSocketDisconnect(sock, true); + } + else +#endif + { + // Send a ping request + pingSendTimeMs = GetNonZeroTimeMs(); + + const unsigned_ptr msg[] = { + kCli2GateKeeper_PingRequest, + pingSendTimeMs, + 0, // not a transaction + 0, // no payload + nil + }; + + //Send(msg, arrsize(msg)); + } +} + +//============================================================================ +void CliGkConn::Send (const unsigned_ptr fields[], unsigned count) { + critsect.Enter(); + { + NetCliSend(cli, fields, count); + NetCliFlush(cli); + } + critsect.Leave(); +} + + +/***************************************************************************** +* +* Cli2GateKeeper message handlers +* +***/ + +//============================================================================ +static bool Recv_PingReply ( + const byte msg[], + unsigned bytes, + void * +) { + const GateKeeper2Cli_PingReply & reply = *(const GateKeeper2Cli_PingReply *)msg; + + if (reply.transId) + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + + +//============================================================================ +static bool Recv_FileSrvIpAddressReply ( + const byte msg[], + unsigned bytes, + void * +) { + const GateKeeper2Cli_FileSrvIpAddressReply & reply = *(const GateKeeper2Cli_FileSrvIpAddressReply *)msg; + + if (reply.transId) + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + +//============================================================================ +static bool Recv_AuthSrvIpAddressReply ( + const byte msg[], + unsigned bytes, + void * +) { + const GateKeeper2Cli_AuthSrvIpAddressReply & reply = *(const GateKeeper2Cli_AuthSrvIpAddressReply *)msg; + + if (reply.transId) + NetTransRecv(reply.transId, msg, bytes); + + return true; +} + + +/***************************************************************************** +* +* Cli2Auth protocol +* +***/ + +#define MSG(s) kNetMsg_Cli2GateKeeper_##s +static NetMsgInitSend s_send[] = { + { MSG(PingRequest) }, + { MSG(FileSrvIpAddressRequest) }, + { MSG(AuthSrvIpAddressRequest) }, +}; +#undef MSG + +#define MSG(s) kNetMsg_GateKeeper2Cli_##s, Recv_##s +static NetMsgInitRecv s_recv[] = { + { MSG(PingReply) }, + { MSG(FileSrvIpAddressReply) }, +}; +#undef MSG + + +/***************************************************************************** +* +* PingRequestTrans +* +***/ + +//============================================================================ +PingRequestTrans::PingRequestTrans ( + FNetCliGateKeeperPingRequestCallback callback, + void * param, + unsigned pingAtMs, + unsigned payloadBytes, + const void * payload +) : NetGateKeeperTrans(kPingRequestTrans) +, m_callback(callback) +, m_param(param) +, m_pingAtMs(pingAtMs) +{ + m_payload.Set((const byte *)payload, payloadBytes); +} + +//============================================================================ +bool PingRequestTrans::Send () { + + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2GateKeeper_PingRequest, + m_pingAtMs, + m_transId, + m_payload.Count(), + (unsigned_ptr) m_payload.Ptr(), + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void PingRequestTrans::Post () { + + m_callback( + m_result, + m_param, + m_pingAtMs, + m_replyAtMs, + m_payload.Count(), + m_payload.Ptr() + ); +} + +//============================================================================ +bool PingRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const GateKeeper2Cli_PingReply & reply = *(const GateKeeper2Cli_PingReply *)msg; + + m_payload.Set(reply.payload, reply.payloadBytes); + m_replyAtMs = TimeGetMs(); + m_result = kNetSuccess; + m_state = kTransStateComplete; + + return true; +} + + +/***************************************************************************** +* +* FileSrvIpAddressRequestTrans +* +***/ + +//============================================================================ +FileSrvIpAddressRequestTrans::FileSrvIpAddressRequestTrans ( + FNetCliGateKeeperFileSrvIpAddressRequestCallback callback, + void * param, + bool isPatcher +) : NetGateKeeperTrans(kGkFileSrvIpAddressRequestTrans) +, m_callback(callback) +, m_param(param) +, m_isPatcher(isPatcher) +{ +} + +//============================================================================ +bool FileSrvIpAddressRequestTrans::Send () { + + if (!AcquireConn()) + return false; + + + const unsigned_ptr msg[] = { + kCli2GateKeeper_FileSrvIpAddressRequest, + m_transId, + m_isPatcher == true ? 1 : 0 + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void FileSrvIpAddressRequestTrans::Post () { + + m_callback( + m_result, + m_param, + m_addr + ); +} + +//============================================================================ +bool FileSrvIpAddressRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const GateKeeper2Cli_FileSrvIpAddressReply & reply = *(const GateKeeper2Cli_FileSrvIpAddressReply *)msg; + + + m_result = kNetSuccess; + m_state = kTransStateComplete; + StrCopy(m_addr, reply.address, 64); + + return true; +} + + +/***************************************************************************** +* +* AuthSrvIpAddressRequestTrans +* +***/ + +//============================================================================ +AuthSrvIpAddressRequestTrans::AuthSrvIpAddressRequestTrans ( + FNetCliGateKeeperFileSrvIpAddressRequestCallback callback, + void * param +) : NetGateKeeperTrans(kGkAuthSrvIpAddressRequestTrans) +, m_callback(callback) +, m_param(param) +{ +} + +//============================================================================ +bool AuthSrvIpAddressRequestTrans::Send () { + + if (!AcquireConn()) + return false; + + const unsigned_ptr msg[] = { + kCli2GateKeeper_AuthSrvIpAddressRequest, + m_transId, + }; + + m_conn->Send(msg, arrsize(msg)); + + return true; +} + +//============================================================================ +void AuthSrvIpAddressRequestTrans::Post () { + + m_callback( + m_result, + m_param, + m_addr + ); +} + +//============================================================================ +bool AuthSrvIpAddressRequestTrans::Recv ( + const byte msg[], + unsigned bytes +) { + ref(bytes); + + const GateKeeper2Cli_AuthSrvIpAddressReply & reply = *(const GateKeeper2Cli_AuthSrvIpAddressReply *)msg; + + m_result = kNetSuccess; + m_state = kTransStateComplete; + StrCopy(m_addr, reply.address, 64); + + return true; +} + + +} using namespace GateKeeper; + + +/***************************************************************************** +* +* NetGateKeeperTrans +* +***/ + +//============================================================================ +NetGateKeeperTrans::NetGateKeeperTrans (ETransType transType) +: NetTrans(kNetProtocolCli2GateKeeper, transType) +, m_conn(nil) +{ +} + +//============================================================================ +NetGateKeeperTrans::~NetGateKeeperTrans () { + ReleaseConn(); +} + +//============================================================================ +bool NetGateKeeperTrans::AcquireConn () { + if (!m_conn) + m_conn = GetConnIncRef("AcquireConn"); + return m_conn != nil; +} + +//============================================================================ +void NetGateKeeperTrans::ReleaseConn () { + if (m_conn) { + m_conn->DecRef("AcquireConn"); + m_conn = nil; + } +} + + +/***************************************************************************** +* +* Protected functions +* +***/ + +//============================================================================ +void GateKeeperInitialize () { + s_running = true; + NetMsgProtocolRegister( + kNetProtocolCli2GateKeeper, + false, + s_send, arrsize(s_send), + s_recv, arrsize(s_recv), + kDhGValue, + BigNum(sizeof(kDhXData), kDhXData), + BigNum(sizeof(kDhNData), kDhNData) + ); +} + +//============================================================================ +void GateKeeperDestroy (bool wait) { + s_running = false; + + NetTransCancelByProtocol( + kNetProtocolCli2GateKeeper, + kNetErrRemoteShutdown + ); + NetMsgProtocolDestroy( + kNetProtocolCli2GateKeeper, + false + ); + + s_critsect.Enter(); + { + while (CliGkConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_active = nil; + } + s_critsect.Leave(); + + if (!wait) + return; + + while (s_perf[kPerfConnCount]) { + NetTransUpdate(); + AsyncSleep(10); + } +} + +//============================================================================ +bool GateKeeperQueryConnected () { + + bool result; + s_critsect.Enter(); + { + if (nil != (result = s_active)) + result &= (nil != s_active->cli); + } + s_critsect.Leave(); + return result; +} + +//============================================================================ +unsigned GateKeeperGetConnId () { + unsigned connId; + s_critsect.Enter(); + connId = (s_active) ? s_active->seq : 0; + s_critsect.Leave(); + return connId; +} + + +} using namespace Ngl; + + +/***************************************************************************** +* +* Exported functions +* +***/ + +//============================================================================ +void NetCliGateKeeperStartConnect ( + const wchar * gateKeeperAddrList[], + unsigned gateKeeperAddrCount +) { + gateKeeperAddrCount = min(gateKeeperAddrCount, 1); + + for (unsigned i = 0; i < gateKeeperAddrCount; ++i) { + // Do we need to lookup the address? + const wchar * name = gateKeeperAddrList[i]; + while (unsigned ch = *name) { + ++name; + if (!(isdigit(ch) || ch == L'.' || ch == L':')) { + AsyncCancelId cancelId; + AsyncAddressLookupName( + &cancelId, + AsyncLookupCallback, + gateKeeperAddrList[i], + kNetDefaultClientPort, + nil + ); + break; + } + } + if (!name[0]) { + NetAddress addr; + NetAddressFromString(&addr, gateKeeperAddrList[i], kNetDefaultClientPort); + Connect(gateKeeperAddrList[i], addr); + } + } +} + +//============================================================================ +void NetCliGateKeeperDisconnect () { + s_critsect.Enter(); + { + while (CliGkConn * conn = s_conns.Head()) + UnlinkAndAbandonConn_CS(conn); + s_active = nil; + } + s_critsect.Leave(); +} + +//============================================================================ +void NetCliGateKeeperPingRequest ( + unsigned pingTimeMs, + unsigned payloadBytes, + const void * payload, + FNetCliGateKeeperPingRequestCallback callback, + void * param +) { + PingRequestTrans * trans = NEW(PingRequestTrans)( + callback, + param, + pingTimeMs, + payloadBytes, + payload + ); + NetTransSend(trans); +} + +//============================================================================ +void NetCliGateKeeperFileSrvIpAddressRequest ( + FNetCliGateKeeperFileSrvIpAddressRequestCallback callback, + void * param, + bool isPatcher +) { + FileSrvIpAddressRequestTrans * trans = NEW(FileSrvIpAddressRequestTrans)(callback, param, isPatcher); + NetTransSend(trans); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.h new file mode 100644 index 00000000..6e5497db --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.h @@ -0,0 +1,121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLGATEKEEPER_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglGateKeeper.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PRIVATE_PLNGLGATEKEEPER_H + + +/***************************************************************************** +* +* Client gatekeeper functions +* +***/ + + +//============================================================================ +// Connect +//============================================================================ +void NetCliGateKeeperStartConnect ( + const wchar * gateKeeperAddrList[], + unsigned gateKeeperAddrCount +); + +bool NetCliGateKeeperQueryConnected (); +void NetCliGateKeeperAutoReconnectEnable (bool enable); // is enabled by default + +// Called after the gatekeeper/client connection is encrypted +typedef void (*FNetCliGateKeeperConnectCallback)(); +void NetCliGateKeeperSetConnectCallback ( + FNetCliGateKeeperConnectCallback callback +); + + +//============================================================================ +// Disconnect +//============================================================================ +void NetCliGateKeeperDisconnect (); +void NetCliGateKeeperUnexpectedDisconnect (); + + +//============================================================================ +// Ping +//============================================================================ +typedef void (*FNetCliGateKeeperPingRequestCallback)( + ENetError result, + void * param, + unsigned pingAtMs, + unsigned replyAtMs, + unsigned payloadbytes, + const byte payload[] +); +void NetCliGateKeeperPingRequest ( + unsigned pingTimeMs, + unsigned payloadBytes, // max 64k (pnNetCli enforced upon send) + const void * payload, + FNetCliGateKeeperPingRequestCallback callback, + void * param +); + + +//============================================================================ +// FileSrvIpAddress +//============================================================================ +typedef void (*FNetCliGateKeeperFileSrvIpAddressRequestCallback)( + ENetError result, + void * param, + const wchar addr[] +); + +void NetCliGateKeeperFileSrvIpAddressRequest ( + FNetCliGateKeeperFileSrvIpAddressRequestCallback callback, + void * param, + bool isPatcher +); + + +//============================================================================ +// AuthSrvIpAddress +//============================================================================ +typedef void (*FNetCliGateKeeperAuthSrvIpAddressRequestCallback)( + ENetError result, + void * param, + const wchar addr[] +); + +void NetCliGateKeeperAuthSrvIpAddressRequest ( + FNetCliGateKeeperAuthSrvIpAddressRequestCallback callback, + void * param +); + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglMisc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglMisc.cpp new file mode 100644 index 00000000..dc7374c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglMisc.cpp @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglMisc.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +namespace Ngl { + +/***************************************************************************** +* +* Private data +* +***/ + +static unsigned s_connSequence; + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +unsigned ConnNextSequence () { + if (!++s_connSequence) + ++s_connSequence; + return s_connSequence; +} + +//============================================================================ +unsigned ConnGetId (ENetProtocol protocol) { + switch (protocol) { + case kNetProtocolCli2Auth: return AuthGetConnId(); + case kNetProtocolCli2Game: return GameGetConnId(); + case kNetProtocolCli2File: return FileGetConnId(); + case kNetProtocolCli2Csr: return CsrGetConnId(); + case kNetProtocolCli2GateKeeper: return GateKeeperGetConnId(); + DEFAULT_FATAL(protocol); + } +} + + +} // namespace Ngl diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglTrans.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglTrans.cpp new file mode 100644 index 00000000..0df533b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglTrans.cpp @@ -0,0 +1,343 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/Private/plNglTrans.cpp +* +***/ + +#include "../Pch.h" +#pragma hdrstop + + +namespace Ngl { +/***************************************************************************** +* +* Private +* +***/ + +enum { + kPerfCurrTransactions, + kNumPerf +}; + +static const unsigned kDefaultTimeoutMs = 5 * 60 * 1000; + +static bool s_running; +static CCritSect s_critsect; +static LISTDECL(NetTrans, m_link) s_transactions; +static long s_perf[kNumPerf]; +static unsigned s_timeoutMs = kDefaultTimeoutMs; + + +/***************************************************************************** +* +* Internal functions +* +***/ + +//============================================================================ +static NetTrans * FindTransIncRef_CS (unsigned transId, const char tag[]) { + // There shouldn't be more than a few transactions; just do a linear scan + for (NetTrans * trans = s_transactions.Head(); trans; trans = s_transactions.Next(trans)) + if (trans->m_transId == transId) { + trans->IncRef(tag); + return trans; + } + + return nil; +} + +//============================================================================ +static NetTrans * FindTransIncRef (unsigned transId, const char tag[]) { + NetTrans * trans; + s_critsect.Enter(); + { + trans = FindTransIncRef_CS(transId, tag); + } + s_critsect.Leave(); + return trans; +} + +//============================================================================ +static void CancelTrans_CS (NetTrans * trans, ENetError error) { + ASSERT(IS_NET_ERROR(error)); + if (trans->m_state != kTransStateComplete) { + trans->m_result = error; + trans->m_state = kTransStateComplete; + } +} + + +/***************************************************************************** +* +* NetTrans implementation +* +***/ + +//============================================================================ +NetTrans::NetTrans (ENetProtocol protocol, ETransType transType) +: m_state(kTransStateWaitServerConnect) +, m_result(kNetPending) +, m_transId(0) +, m_connId(0) +, m_protocol(protocol) +, m_hasSubTrans(false) +, m_transType(transType) +{ + AtomicAdd(&s_perf[kPerfCurrTransactions], 1); + AtomicAdd(&s_perfTransCount[m_transType], 1); +// DebugMsg("%s@%p created", s_transTypes[m_transType], this); +} + +//============================================================================ +NetTrans::~NetTrans () { + ASSERT(!m_link.IsLinked()); + AtomicAdd(&s_perfTransCount[m_transType], -1); + AtomicAdd(&s_perf[kPerfCurrTransactions], -1); +// DebugMsg("%s@%p destroyed", s_transTypes[m_transType], this); +} + +//============================================================================ +bool NetTrans::CanStart () const { + switch (m_protocol) { + case kNetProtocolCli2Auth: return AuthQueryConnected(); + case kNetProtocolCli2Game: return GameQueryConnected(); + case kNetProtocolCli2File: return FileQueryConnected(); + case kNetProtocolCli2Csr: return CsrQueryConnected(); + case kNetProtocolCli2GateKeeper: return GateKeeperQueryConnected(); + DEFAULT_FATAL(m_protocol); + } +} + + +/***************************************************************************** +* +* Module functions +* +***/ + +//============================================================================ +void NetTransInitialize () { + s_critsect.Enter(); + { + s_running = true; + } + s_critsect.Leave(); +} + +//============================================================================ +void NetTransDestroy (bool wait) { + s_critsect.Enter(); + { + s_running = false; + } + s_critsect.Leave(); + + NetTransCancelAll(kNetErrRemoteShutdown); + + if (!wait) + return; + + while (s_perf[kPerfCurrTransactions]) { + NetTransUpdate(); + AsyncSleep(10); + } +} + +//============================================================================ +void NetTransSetTimeoutMs (unsigned ms) { + s_timeoutMs = ms ? ms : kDefaultTimeoutMs; +} + +//============================================================================ +unsigned NetTransGetTimeoutMs () { + return s_timeoutMs; +} + +//============================================================================ +void NetTransSend (NetTrans * trans) { + trans->IncRef("Lifetime"); + s_critsect.Enter(); + { + static unsigned s_transId; + while (!trans->m_transId) + trans->m_transId = ++s_transId; + s_transactions.Link(trans, kListTail); + if (!s_running) + CancelTrans_CS(trans, kNetErrRemoteShutdown); + } + s_critsect.Leave(); +} + +//============================================================================ +bool NetTransRecv (unsigned transId, const byte msg[], unsigned bytes) { + NetTrans * trans = FindTransIncRef(transId, "Recv"); + + if (!trans) + return true; // transaction was canceled. + + // Update the timeout time + trans->m_timeoutAtMs = TimeGetMs() + s_timeoutMs; + + bool result = trans->Recv(msg, bytes); + + if (!result) + NetTransCancel(transId, kNetErrInternalError); + + trans->DecRef("Recv"); + return result; +} + +//============================================================================ +void NetTransCancel (unsigned transId, ENetError error) { + s_critsect.Enter(); + { + NetTrans * trans = s_transactions.Head(); + for (; trans; trans = trans->m_link.Next()) { + if (trans->m_transId == transId) { + CancelTrans_CS(trans, error); + break; + } + } + } + s_critsect.Leave(); +} + +//============================================================================ +void NetTransCancelByProtocol (ENetProtocol protocol, ENetError error) { + s_critsect.Enter(); + { + NetTrans * trans = s_transactions.Head(); + for (; trans; trans = trans->m_link.Next()) { + if (trans->m_protocol == protocol) + CancelTrans_CS(trans, error); + } + } + s_critsect.Leave(); +} + +//============================================================================ +void NetTransCancelByConnId (unsigned connId, ENetError error) { + s_critsect.Enter(); + { + NetTrans * trans = s_transactions.Head(); + for (; trans; trans = trans->m_link.Next()) { + if (trans->m_connId == connId) + CancelTrans_CS(trans, error); + } + } + s_critsect.Leave(); +} + +//============================================================================ +void NetTransCancelAll (ENetError error) { + s_critsect.Enter(); + { + NetTrans * trans = s_transactions.Head(); + for (; trans; trans = trans->m_link.Next()) + CancelTrans_CS(trans, error); + } + s_critsect.Leave(); +} + +//============================================================================ +void NetTransUpdate () { + LISTDECL(NetTrans, m_link) completed; + LISTDECL(NetTrans, m_link) parentCompleted; + + s_critsect.Enter(); + NetTrans * next, * trans = s_transactions.Head(); + for (; trans; trans = next) { + next = s_transactions.Next(trans); + + bool done = false; + while (!done) { + switch (trans->m_state) { + case kTransStateComplete: + if (trans->m_hasSubTrans) + parentCompleted.Link(trans); + else + completed.Link(trans); + done = true; + break; + + case kTransStateWaitServerConnect: + if (!trans->CanStart()) { + done = true; + break; + } + if (trans->m_protocol && 0 == (trans->m_connId = ConnGetId(trans->m_protocol))) { + done = true; + break; + } + // This is the default "next state", trans->Send() can override this + trans->m_state = kTransStateWaitServerResponse; + // Set timeout time before calling Send(), allowing Send() to change it if it wants to. + trans->m_timeoutAtMs = TimeGetMs() + s_timeoutMs; + if (!trans->Send()) { + // Revert back to current state so that we'll attempt to send again + trans->m_state = kTransStateWaitServerConnect; + done = true; + break; + } + break; + + case kTransStateWaitServerResponse: + // Check for timeout + if ((int)(TimeGetMs() - trans->m_timeoutAtMs) > 0) { + // Check to see if the transaction wants to "abort" the timeout + if (trans->TimedOut()) + CancelTrans_CS(trans, kNetErrTimeout); + else + trans->m_timeoutAtMs = TimeGetMs() + s_timeoutMs; // Reset the timeout counter + } + done = true; + break; + + DEFAULT_FATAL(trans->m_state); + } + } + } + s_critsect.Leave(); + + // Post completed transactions + while (NetTrans * trans = completed.Head()) { + completed.Unlink(trans); + trans->Post(); + trans->DecRef("Lifetime"); + } + // Post completed parent transactions + while (NetTrans * trans = parentCompleted.Head()) { + parentCompleted.Unlink(trans); + trans->Post(); + trans->DecRef("Lifetime"); + } +} + + +} // namespace Ngl diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/plNetGameLib.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/plNetGameLib.h new file mode 100644 index 00000000..c9ecba9a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/plNetGameLib.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plNetGameLib/plNetGameLib.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PLNETGAMELIB_H +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PLNETGAMELIB_H + +#define PLNETGAMELIB_INCLUDED + +#ifdef PNNETDIAG_INCLUDED +#error "plNetGameLib and pnNetDiag libraries may not be included in the same project because they invalidate each other's pnNetCli settings" +#endif + +#include "Private/plNglAllIncludes.h" + + +#endif // PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLNETGAMELIB_PLNETGAMELIB_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetCommonMessage.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetCommonMessage.h new file mode 100644 index 00000000..4de12463 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetCommonMessage.h @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetCommonMessage_inc +#define plNetCommonMessage_inc + +#include "hsTypes.h" +#include "hsSafeRefCnt.h" + +// +// refcntable data +// +class plNetCommonMessageData : public hsSafeRefCnt +{ +private: + char *fData; // sent +public: + plNetCommonMessageData(char* d) : fData(d) {} + ~plNetCommonMessageData() + { + hsAssert(RefCnt()==1, "illegal refcnt"); + delete [] fData; + } + + char* GetData() const { return fData; } +}; + +// +// basic msg payload w/ refcntable data +// +class plNetCommonMessage +{ + plNetCommonMessage(const plNetCommonMessage & other); +private: + plNetCommonMessageData* fMsgData; +protected: + UInt32 fLen; // sent +public: + plNetCommonMessage() : fLen(0),fMsgData(nil) {} + virtual ~plNetCommonMessage() { hsRefCnt_SafeUnRef(fMsgData); } + + // setters + void SetData(char *d) + { + plNetCommonMessageData* n = d ? TRACKED_NEW plNetCommonMessageData(d) : nil; + hsRefCnt_SafeAssign(fMsgData, n); + hsRefCnt_SafeUnRef(n); + } + void SetMsgData(plNetCommonMessageData* d) + { + hsRefCnt_SafeAssign(fMsgData, d); + } + void SetLen(UInt32 l) { fLen=l; } + + // getters + char* GetData() const { return fMsgData ? fMsgData->GetData() : nil; } + virtual UInt32 GetDataLen() { return fLen; } + UInt32 GetLen() const { return fLen; } + plNetCommonMessageData* GetMsgData() const { return fMsgData; } +}; +#endif // plNetCommonMessage_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.cpp new file mode 100644 index 00000000..ca423251 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.cpp @@ -0,0 +1,1376 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsWindows.h" +#include "hsResMgr.h" +#include "plNetMessage.h" +#include "plNetCommonMessage.h" +#include "plNetMsgVersion.h" +#include "plCreatableIndex.h" + +#include "../pnKeyedObject/plKeyImp.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnNetCommon/plNetSharedState.h" +#include "../pnMessage/plMessage.h" +#include "../pnNetCommon/pnNetCommon.h" +#include "../pnNetCommon/plGenericVar.h" +#include "../pnFactory/plFactory.h" + +#include "../plVault/plVault.h" +#include "../plNetCommon/plNetCommon.h" +#include "../plSDL/plSDL.h" + +#if defined(HS_BUILD_FOR_UNIX) +#include +#include +#include +#endif + +#include + +// +// static +// +// see plNetMsgVersion.h +const UInt8 plNetMessage::kVerMajor = PLASMA2_NETMSG_MAJOR_VERSION; +const UInt8 plNetMessage::kVerMinor = PLASMA2_NETMSG_MINOR_VERSION; + + + +//////////////////////////////////////////////////////// +// plNetMessage +//////////////////////////////////////////////////////// +plNetMessage::plNetMessage() : +fTimeRecvd(0), +fBytesRead(0), +fNetCoreMsg(nil), +fContext(0), +fPeekStatus(0), +fTransactionID(0), +fPlayerID(kInvalidPlayerID), +fFlags(0), +fProtocolVerMajor(0), +fProtocolVerMinor(0) +{ +} + +plNetMessage::~plNetMessage() +{ + +} + +void plNetMessage::Read(hsStream* s, hsResMgr* mgr) +{ + IPeekBuffer(s); +} + +void plNetMessage::Write(hsStream* s, hsResMgr* mgr) +{ + IPokeBuffer(s); +} + + +void plNetMessage::InitReplyFieldsFrom(plNetMessage * msg) +{ + bool hasContext = msg->GetHasContext(); + SetHasContext(hasContext); + if (hasContext) + SetContext(msg->GetContext()); + + bool hasTransactionID = msg->GetHasTransactionID(); + SetHasTransactionID(hasTransactionID); + if (hasTransactionID) + SetTransactionID(msg->GetTransactionID()); + + bool hasPlayerID = msg->GetHasPlayerID(); + if ( hasPlayerID ) + SetPlayerID( msg->GetPlayerID() ); + + bool hasAcctUUID = msg->GetHasAcctUUID(); + if ( hasAcctUUID ) + SetAcctUUID( msg->GetAcctUUID() ); + + bool hasTimeSent = msg->GetHasTimeSent(); + if ( hasTimeSent ) + SetTimeSent( msg->GetTimeSent() ); + +#if 0 // I don't think the version should be copied + if (msg->IsBitSet(kHasVersion)) + SetVersion(); +#endif +} + +// +// STATIC +// create and READ from lowlevel net buffer +// +plNetMessage* plNetMessage::CreateAndRead(const plNetCommonMessage* msg, plStreamLogger::EventList* el) +{ + // create + plNetMessage* pHdr = Create(msg); + if (!pHdr) + return nil; + + // read + pHdr->PeekBuffer(msg->GetData(), msg->GetLen(), 0, false, el); + return pHdr; +} + +// +// STATIC +// create from lowlevel net buffer +// +plNetMessage* plNetMessage::Create(const plNetCommonMessage* msg) +{ + if (msg) + { + hsReadOnlyStream readStream; + ClassIndexType classIndex; + readStream.Init(sizeof(classIndex), msg->GetData()); + readStream.ReadSwap(&classIndex); + if (!plFactory::IsValidClassIndex(classIndex)) + return nil; + plNetMessage* pnm = plNetMessage::ConvertNoRef(plFactory::Create(classIndex)); + if (pnm) + pnm->SetNetCoreMsg(msg); + else + { + char str[256]; + sprintf(str, "Factory create failed, class index=%d, garbage msg?", classIndex); + hsAssert(false, str); + } + return pnm; + } + return nil; +} + +int plNetMessage::PokeBuffer(char* bufIn, int bufLen, UInt32 peekOptions) +{ + fPeekStatus = 0; + + if (!bufIn) + return 0; + + if (! (peekOptions & kDontClearBuffer)) + memset(bufIn, 0, bufLen); + + ValidatePoke(); + hsWriteOnlyStream writeStream; + writeStream.Init(bufLen, bufIn); + int ret; + if (peekOptions & kBaseClassOnly) + { + ret=plNetMessage::IPokeBuffer(&writeStream, peekOptions); + } + else + { + ret=IPokeBuffer(&writeStream, peekOptions); + } + return ret; +} + +int plNetMessage::PeekBuffer(const char* bufIn, int bufLen, UInt32 peekOptions, bool forcePeek, plStreamLogger::EventList* el) +{ + if(!bufLen || bufLen < 1) + return 0; + + UInt32 partialPeekOptions = (peekOptions & kPartialPeekMask); + if (!forcePeek && (fPeekStatus & partialPeekOptions) ) + return 0; // already peeked, fully or partially + + if (!bufIn) + return 0; + + // set peek status based on peekOptions + fPeekStatus = partialPeekOptions ? partialPeekOptions : kFullyPeeked; + + hsReadOnlyLoggingStream readStream; + readStream.LogSetList(el); + readStream.Init(bufLen, bufIn); + readStream.LogSubStreamStart("plNetMessage"); + readStream.LogStringString(xtl::format("ClassName: %s",this->ClassName()).c_str()); + int ret; + if (peekOptions & kBaseClassOnly) + { + ret=plNetMessage::IPeekBuffer(&readStream, peekOptions); + plNetMessage::ValidatePeek(); + } + else + { + ret=IPeekBuffer(&readStream, peekOptions); + ValidatePeek(); + } + + readStream.LogSubStreamEnd(); + return ret; +} + +void plNetMessage::IWriteClassIndex(hsStream* stream) +{ + ClassIndexType classIndex=ClassIndex(); + hsAssert(sizeof(classIndex)==sizeof(plNetMessageClassIndex), "somebody changed the size of plCreatable::ClassIndex"); + stream->WriteSwap(classIndex); +} + +// put in buffer +int plNetMessage::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + IWriteClassIndex(stream); + + stream->WriteSwap32(fFlags); + + if (IsBitSet(kHasVersion)) + { + stream->WriteByte(fProtocolVerMajor); + stream->WriteByte(fProtocolVerMinor); + } + if (IsBitSet(kHasTimeSent)) + fTimeSent.Write(stream); + if (IsBitSet(kHasContext)) + stream->WriteSwap(fContext); + if (IsBitSet(kHasTransactionID)) + stream->WriteSwap(fTransactionID); + if (IsBitSet(kHasPlayerID)) + stream->WriteSwap(fPlayerID); + if (IsBitSet(kHasAcctUUID)) + fAcctUUID.Write(stream); + + return stream->GetPosition(); +} + +void plNetMessage::IReadClassIndex(hsStream* stream) +{ + ClassIndexType classIndex; + hsAssert(sizeof(classIndex)==sizeof(plNetMessageClassIndex), "somebody changed the size of plCreatable::ClassIndex"); + stream->LogReadSwap(&classIndex,"ClassIndex"); +} + +// get out of buffer +int plNetMessage::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + IReadClassIndex(stream); + + stream->LogReadSwap(&fFlags,"Flags"); + + // verify version first + if (IsBitSet(kHasVersion)) + { + stream->LogReadSwap(&fProtocolVerMajor, "Protocol major version"); + stream->LogReadSwap(&fProtocolVerMinor, "Protocol minor version"); + + if (fProtocolVerMajor != kVerMajor || fProtocolVerMinor != kVerMinor) + return 0; // this will cause derived classes to stop reading + } + + if (!(IsBitSet(kHasVersion)) && (peekOptions & kWantVersion)) + { + return 0; + } + + if (IsBitSet(kHasTimeSent)) + fTimeSent.Read(stream); + if (IsBitSet(kHasContext)) + stream->LogReadSwap(&fContext,"Context"); + if (IsBitSet(kHasTransactionID)) + stream->LogReadSwap(&fTransactionID,"TransactionID"); + if (IsBitSet(kHasPlayerID)) + stream->LogReadSwap(&fPlayerID,"PlayerID"); + if (IsBitSet(kHasAcctUUID)) + fAcctUUID.Read( stream ); + return stream->GetPosition(); +} + +void plNetMessage::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kNetMsgFlags)) + s->LogReadSwap(&fFlags,"Flags"); + + if (contentFlags.IsBitSet(kNetMsgVersion)) + { + if (IsBitSet(kHasVersion)) + { + s->LogReadSwap(&fProtocolVerMajor, "Protocol major version"); + s->LogReadSwap(&fProtocolVerMinor, "Protocol minor version"); + } + } + + if (contentFlags.IsBitSet(kNetMsgTimeSent)) + { + if (IsBitSet(kHasTimeSent)) + fTimeSent.Read(s); + } + + if (contentFlags.IsBitSet(kNetMsgContext)) + { + if (IsBitSet(kHasContext)) + s->LogReadSwap(&fContext,"Context"); + } + + if (contentFlags.IsBitSet(kNetMsgTransactionID)) + { + if (IsBitSet(kHasTransactionID)) + s->LogReadSwap(&fTransactionID,"TransactionID"); + } + + if (contentFlags.IsBitSet(kNetMsgPlayerID)) + { + if (IsBitSet(kHasPlayerID)) + s->LogReadSwap(&fPlayerID,"PlayerID"); + } +} + +void plNetMessage::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kNetMsgFlags); + contentFlags.SetBit(kNetMsgTimeSent); + contentFlags.SetBit(kNetMsgContext); + contentFlags.SetBit(kNetMsgTransactionID); + contentFlags.SetBit(kNetMsgPlayerID); + contentFlags.SetBit(kNetMsgVersion); + contentFlags.Write(s); + + // kNetMsgFlags + s->WriteSwap32(fFlags); + + // version + if (IsBitSet(kHasVersion)) + { + s->WriteByte(fProtocolVerMajor); + s->WriteByte(fProtocolVerMinor); + } + + // kNetMsgTimeSent + if (IsBitSet(kHasTimeSent)) + fTimeSent.Write(s); + + // kNetMsgContext + if (IsBitSet(kHasContext)) + s->WriteSwap(fContext); + + // kNetMsgTransactionID + if (IsBitSet(kHasTransactionID)) + s->WriteSwap(fTransactionID); + + // kNetMsgPlayerID + if (IsBitSet(kHasPlayerID)) + s->WriteSwap(fPlayerID); +} + +// Get the Packed Size +int plNetMessage::GetPackSize() +{ + hsNullStream nullStream; + return IPokeBuffer(&nullStream); +} + +UInt32 plNetMessage::GetNetCoreMsgLen() const +{ + return fNetCoreMsg ? fNetCoreMsg->GetLen() : 0; +} + +void plNetMessage::ValidatePeek() const +{ + +} + +void plNetMessage::ValidatePoke() const +{ + +} + + +//////////////////////////////////////////////////////// +// plNetMsgStream +//////////////////////////////////////////////////////// +int plNetMsgStream::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMessage::IPokeBuffer(stream, peekOptions); + if (bytes) + { + stream->LogSubStreamPushDesc("StreamHelper"); + fStreamHelper.Poke(stream, peekOptions); + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgStream::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMessage::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogSubStreamPushDesc("MsgStreamStream"); + fStreamHelper.Peek(stream, peekOptions); + bytes=stream->GetPosition(); + } + + return bytes; +} + +//////////////////////////////////////////////////////// +// plNetMsgGameMessage +//////////////////////////////////////////////////////// +int plNetMsgGameMessage::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgStream::IPokeBuffer(stream, peekOptions); + if (bytes) + { + if (fDeliveryTime.AtEpoch()) + { + stream->WriteByte(0); // not sending + } + else + { + stream->WriteByte(1); // sending + fDeliveryTime.Write(stream); + } + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgGameMessage::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgStream::IPeekBuffer(stream, peekOptions); + if (bytes) + { + if (stream->ReadByte()) + { + stream->LogSubStreamPushDesc("GameMessage DeliveryTime"); + fDeliveryTime.Read(stream); + } + bytes=stream->GetPosition(); + } + + return bytes; +} + +plMessage* plNetMsgGameMessage::GetContainedMsg(hsResMgr* resmgr) +{ + hsReadOnlyStream s(StreamInfo()->GetStreamLen(), StreamInfo()->GetStreamBuf()); + return plMessage::ConvertNoRef((resmgr?resmgr:hsgResMgr::ResMgr())->ReadCreatable(&s)); +} + +void plNetMsgGameMessage::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMessage::ReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kNetGameMsgDeliveryTime)) + { + if (s->ReadByte()) + fDeliveryTime.Read(s); + } + + if (contentFlags.IsBitSet(kNetGameMsgGameMsg)) + { + plMessage* gameMsg = plMessage::ConvertNoRef(mgr->ReadCreatableVersion(s)); + + // write message (and label) to ram stream + hsRAMStream ramStream; + mgr->WriteCreatable(&ramStream, gameMsg); + + // put stream in net msg wrapper + StreamInfo()->CopyStream(&ramStream); + + hsRefCnt_SafeUnRef(gameMsg); + } +} + +void plNetMsgGameMessage::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMessage::WriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kNetGameMsgDeliveryTime); + contentFlags.SetBit(kNetGameMsgGameMsg); + contentFlags.Write(s); + + // kNetGameMsgDeliveryTime + if (fDeliveryTime.AtEpoch()) + { + s->WriteByte(0); // not sending + } + else + { + s->WriteByte(1); // sending + fDeliveryTime.Write(s); + } + + // kNetGameMsgGameMsg + plMessage* gameMsg = GetContainedMsg(); + mgr->WriteCreatableVersion(s, gameMsg); + hsRefCnt_SafeUnRef(gameMsg); +} + +//////////////////////////////////////////////////////// +// plNetMsgGameMessageDirected +//////////////////////////////////////////////////////// +int plNetMsgGameMessageDirected::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgGameMessage::IPokeBuffer(stream, peekOptions); + if (bytes) + { + fReceivers.Poke(stream, peekOptions); + + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgGameMessageDirected::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgGameMessage::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogSubStreamPushDesc("GameMessageDirected Receivers"); + fReceivers.Peek(stream, peekOptions); + + bytes=stream->GetPosition(); + } + return bytes; +} + +void plNetMsgGameMessageDirected::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgGameMessage::ReadVersion(s,mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kRecievers)) + fReceivers.ReadVersion(s,mgr); +} + +void plNetMsgGameMessageDirected::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgGameMessage::WriteVersion(s,mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kRecievers); + contentFlags.Write(s); + + fReceivers.WriteVersion(s,mgr); +} + + +//////////////////////////////////////////////////////// +// plNetMsgObject +//////////////////////////////////////////////////////// +int plNetMsgObject::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMessage::IPokeBuffer(stream, peekOptions); + if (bytes) + { + fObjectHelper.Poke(stream, peekOptions); + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgObject::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMessage::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogSubStreamPushDesc("MsgObject"); + fObjectHelper.Peek(stream, peekOptions); + bytes=stream->GetPosition(); + } + + return bytes; +} + +void plNetMsgObject::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMessage::ReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kNetMsgObjectHelper)) + fObjectHelper.ReadVersion(s, mgr); +} + +void plNetMsgObject::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMessage::WriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kNetMsgObjectHelper); + contentFlags.Write(s); + + // kNetMsgObjectHelper + fObjectHelper.WriteVersion(s, mgr); +} + +//////////////////////////////////////////////////////// +// plNetMsgStreamedObject +//////////////////////////////////////////////////////// + +int plNetMsgStreamedObject::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgObject::IPokeBuffer(stream, peekOptions); + if (bytes) + { + fStreamHelper.Poke(stream, peekOptions); + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgStreamedObject::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgObject::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogSubStreamPushDesc("StreamedObject"); + fStreamHelper.Peek(stream, peekOptions); + bytes=stream->GetPosition(); + } + return bytes; +} + +void plNetMsgStreamedObject::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgObject::ReadVersion(s,mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kStreamHelper)) + fStreamHelper.ReadVersion(s,mgr); +} + +void plNetMsgStreamedObject::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgObject::WriteVersion(s,mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kStreamHelper); + contentFlags.Write(s); + + fStreamHelper.WriteVersion(s,mgr); +} + + +//////////////////////////////////////////////////////////////////// +// debug +std::string plNetMsgSDLState::AsStdString() const +{ + std::string s; + + ISetDescName(); // set desc name for debug if necessary + +// xtl::format(s,"object:%s, SDL:%s, initial:%d, %s", +// ObjectInfo()->GetObjectName(), fDescName.c_str(), fIsInitialState, plNetMsgStreamedObject::AsStdString().c_str() ); + + xtl::format(s,"object:%s, initial:%d, %s", + ObjectInfo()->GetObjectName(), fIsInitialState, plNetMsgStreamedObject::AsStdString().c_str() ); + + return s; +} + +// +// fill out descName for debugging if needed +// fDescName; // for debugging output only, not read/written +// +void plNetMsgSDLState::ISetDescName() const +{ + if (fDescName.empty() && StreamInfo()->GetStreamLen() && !StreamInfo()->IsCompressed()) + { + hsReadOnlyStream stream(StreamInfo()->GetStreamLen(), StreamInfo()->GetStreamBuf()); + /* This code can crash the game server sometimes -eap + char* descName = nil; + int ver; + if (plStateDataRecord::ReadStreamHeader(&stream, &descName, &ver)) + fDescName = descName; + delete [] descName; + */ + } +} + +int plNetMsgSDLState::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + ISetDescName(); // stash away the descName before poke/compress + plNetMsgStreamedObject::IPokeBuffer(stream, peekOptions); + stream->WriteSwap( fIsInitialState ); + stream->WriteSwap(fPersistOnServer); + stream->WriteSwap(fIsAvatarState); + return stream->GetPosition(); +} + +int plNetMsgSDLState::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + plNetMsgStreamedObject::IPeekBuffer(stream, peekOptions); + stream->LogReadSwap( &fIsInitialState, "IsInitialAgeState" ); + stream->LogReadSwap(&fPersistOnServer, "SDLState PersistOnServer"); + stream->LogReadSwap(&fIsAvatarState, "SDLState IsAvatarState"); + ISetDescName(); // stash away the descName after peek/uncompress + return stream->GetPosition(); +} + +void plNetMsgSDLState::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgStreamedObject::ReadVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kSDLStateStream)) + { + UInt32 len; + s->LogReadSwap(&len,"SDLState StreamLen"); + UInt8* buf = TRACKED_NEW UInt8[len]; + s->LogRead(len, buf,"SDLState StreamData"); + + StreamInfo()->SetStreamLen(len); + StreamInfo()->SetStreamBuf(buf); + } + if (contentFlags.IsBitSet(kSDLIsInitialState)) + s->LogReadSwap( &fIsInitialState, "IsInitialAgeState" ); + if (contentFlags.IsBitSet(kSDLPersist)) + s->ReadSwap(&fPersistOnServer); + if (contentFlags.IsBitSet(kSDLAvatarState)) + s->ReadSwap(&fIsAvatarState); +} + +void plNetMsgSDLState::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgStreamedObject::WriteVersion(s, mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kSDLStateStream); + contentFlags.SetBit(kSDLIsInitialState); + contentFlags.SetBit(kSDLPersist); + contentFlags.SetBit(kSDLAvatarState); + contentFlags.Write(s); + + // kSDLStateStream + s->WriteSwap32(StreamInfo()->GetStreamLen()); + s->Write(StreamInfo()->GetStreamLen(), StreamInfo()->GetStreamBuf()); + s->WriteSwap( fIsInitialState ); + s->WriteSwap(fPersistOnServer); + s->WriteSwap(fIsAvatarState); +} + +//////////////////////////////////////////////////////// +// plNetMsgSDLStateBCast +//////////////////////////////////////////////////////// + +int plNetMsgSDLStateBCast::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes = plNetMsgSDLState::IPokeBuffer(stream, peekOptions); + if (bytes) + { + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgSDLStateBCast::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgSDLState::IPeekBuffer(stream, peekOptions); + if (bytes) + { + bytes=stream->GetPosition(); + } + return bytes; +} + +void plNetMsgSDLStateBCast::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgSDLState::ReadVersion(s, mgr); +} + +void plNetMsgSDLStateBCast::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgSDLState::WriteVersion(s, mgr); +} + +//////////////////////////////////////////////////////// +// plNetMsgRoomsList +//////////////////////////////////////////////////////// +plNetMsgRoomsList::~plNetMsgRoomsList() +{ + int i; + for(i=0;iWriteSwap(numRooms); + for(i=0;iGetPosition(); + } + return bytes; +} + +int plNetMsgRoomsList::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMessage::IPeekBuffer(stream, peekOptions); + if (bytes) + { + int i, numRooms; + stream->LogReadSwap(&numRooms,"RoomList NumRooms"); + fRooms.resize(numRooms); + int oldSize = fRoomNames.size(); + fRoomNames.resize(numRooms); + for(i=0;iLogSubStreamPushDesc("RoomList"); + plMsgCStringHelper::Peek(fRoomNames[i],stream,peekOptions); + } + bytes=stream->GetPosition(); + } + + return bytes; +} + +void plNetMsgRoomsList::AddRoom(plKey rmKey) +{ + fRooms.push_back(rmKey->GetUoid().GetLocation()); + fRoomNames.push_back(hsStrcpy(rmKey->GetName())); +} + +void plNetMsgRoomsList::AddRoomLocation(plLocation loc, const char* rmName) +{ + fRooms.push_back(loc); + fRoomNames.push_back(rmName ? hsStrcpy(rmName) : nil); +} + +int plNetMsgRoomsList::FindRoomLocation(plLocation loc) +{ + std::vector::iterator result = std::find(fRooms.begin(), fRooms.end(), loc); + return result==fRooms.end() ? -1 : result-fRooms.begin(); +} + +//////////////////////////////////////////////////////// +// plNetMsgPagingRoom +//////////////////////////////////////////////////////// +int plNetMsgPagingRoom::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgRoomsList::IPokeBuffer(stream, peekOptions); + if (bytes) + { + stream->WriteSwap(fPageFlags); + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgPagingRoom::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgRoomsList::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogReadSwap(&fPageFlags,"PageFlags"); + bytes=stream->GetPosition(); + } + return bytes; +} + +//////////////////////////////////////////////////////// +// plNetMsgGroupOwner +//////////////////////////////////////////////////////// +int plNetMsgGroupOwner::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgServerToClient::IPokeBuffer(stream, peekOptions); + if (bytes) + { + int i, numGroups=fGroups.size(); + stream->WriteSwap(numGroups); + for(i=0;iGetPosition(); + } + return bytes; +} + +int plNetMsgGroupOwner::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgServerToClient::IPeekBuffer(stream, peekOptions); + if (bytes) + { + int i, num; + stream->LogReadSwap(&num,"GroupOwnerNum"); + fGroups.resize(num); + for(i=0;iGetPosition(); + } + + return bytes; +} + +//////////////////////////////////////////////////////// +// plNetMsgSharedState +//////////////////////////////////////////////////////// + +void plNetMsgSharedState::CopySharedState(plNetSharedState* ss) +{ + hsRAMStream stream; + ss->Write(&stream); + StreamInfo()->CopyStream(&stream); +} + +int plNetMsgSharedState::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgStreamedObject::IPokeBuffer(stream, peekOptions); + if (bytes) + { + stream->WriteSwap(fLockRequest); + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgSharedState::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgStreamedObject::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogReadSwap(&fLockRequest,"SharedState LockRequest"); + bytes=stream->GetPosition(); + } + return bytes; +} + +void plNetMsgSharedState::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgStreamedObject::ReadVersion(s,mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kLockRequest)) + s->ReadSwap(&fLockRequest); +} + +void plNetMsgSharedState::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgStreamedObject::WriteVersion(s,mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kLockRequest); + contentFlags.Write(s); + + s->WriteSwap(fLockRequest); +} + + +//////////////////////////////////////////////////////// +// plNetMsgGetSharedState +//////////////////////////////////////////////////////// +int plNetMsgGetSharedState::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgObject::IPokeBuffer(stream, peekOptions); + if (bytes) + { + plMsgCArrayHelper::Poke(fSharedStateName,sizeof(fSharedStateName),stream,peekOptions); + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgGetSharedState::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgObject::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogSubStreamPushDesc("SharedStateName"); + plMsgCArrayHelper::Peek(fSharedStateName,sizeof(fSharedStateName),stream,peekOptions); + bytes=stream->GetPosition(); + } + + return bytes; +} + +//////////////////////////////////////////////////////// +// plNetMsgObject +//////////////////////////////////////////////////////// +int plNetMsgObjectUpdateFilter::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMessage::IPokeBuffer(stream, peekOptions); + if (bytes) + { + fObjectListHelper.Poke(stream, peekOptions); + stream->WriteSwap(fMaxUpdateFreq); + + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgObjectUpdateFilter::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMessage::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogSubStreamPushDesc("ObjectUpdateFilter"); + fObjectListHelper.Peek(stream, peekOptions); + stream->LogReadSwap(&fMaxUpdateFreq,"MsgObjectUpdateFilter MaxUpdateFreq"); + + bytes=stream->GetPosition(); + } + + return bytes; +} + +//////////////////////////////////////////////////////// +// plNetMsgMembersList +//////////////////////////////////////////////////////// +int plNetMsgMembersList::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgServerToClient::IPokeBuffer(stream, peekOptions); + if (bytes) + { + fMemberListHelper.Poke(stream, peekOptions); + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgMembersList::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgServerToClient::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogSubStreamPushDesc("MembersList"); + fMemberListHelper.Peek(stream, peekOptions); + bytes=stream->GetPosition(); + } + return bytes; +} + +//////////////////////////////////////////////////////// +// plNetMsgMemberUpdate +//////////////////////////////////////////////////////// +int plNetMsgMemberUpdate::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgServerToClient::IPokeBuffer(stream, peekOptions); + if (bytes) + { + // FIX ME to something nice + fMemberInfo.GetClientGuid()->SetClientKey(""); + fMemberInfo.GetClientGuid()->SetAccountUUID(plUUID()); + fMemberInfo.Poke(stream, peekOptions); + stream->WriteByte(fAddMember); + + bytes=stream->GetPosition(); + } + return bytes; +} + +int plNetMsgMemberUpdate::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMsgServerToClient::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogSubStreamPushDesc("MemberUpdate"); + fMemberInfo.Peek(stream, peekOptions); + fAddMember = stream->ReadByte(); + + bytes=stream->GetPosition(); + } + return bytes; +} + +//////////////////////////////////////////////////////// +// plNetMsgVoice +//////////////////////////////////////////////////////// +int plNetMsgVoice::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + plNetMessage::IPokeBuffer(stream, peekOptions); + stream->WriteSwap(fFlags); + stream->WriteSwap(fNumFrames); + plMsgStdStringHelper::Poke(fVoiceData, stream, peekOptions); + fReceivers.Poke(stream, peekOptions); + return stream->GetPosition(); +} + +int plNetMsgVoice::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMessage::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogReadSwap(&fFlags,"Voice Flags"); + stream->LogReadSwap(&fNumFrames, "Number of encoded frames"); + stream->LogSubStreamPushDesc("Voice Data"); + plMsgStdStringHelper::Peek(fVoiceData, stream, peekOptions); + stream->LogSubStreamPushDesc("Voice Receivers"); + fReceivers.Peek(stream, peekOptions); + bytes=stream->GetPosition(); + } + return bytes; +} + +void plNetMsgVoice::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMessage::ReadVersion(s,mgr); + + UInt16 old = 0; + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kDead_FrameSize)) + s->ReadSwap(&old); + if (contentFlags.IsBitSet(kReceivers)) + fReceivers.ReadVersion(s,mgr); + if (contentFlags.IsBitSet(kVoiceFlags)) + s->ReadSwap(&fFlags); + if(contentFlags.IsBitSet(kVoiceData)) + plMsgStdStringHelper::Peek(fVoiceData, s); +} + +void plNetMsgVoice::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMessage::WriteVersion(s,mgr); + + hsBitVector contentFlags; + + contentFlags.SetBit(kReceivers); + contentFlags.SetBit(kVoiceFlags); + contentFlags.SetBit(kVoiceData); + contentFlags.Write(s); + + fReceivers.WriteVersion(s,mgr); + s->WriteSwap(fFlags); + plMsgStdStringHelper::Poke(fVoiceData, s); +} + +void plNetMsgVoice::SetVoiceData(char *data, int len) +{ + fVoiceData.resize( len ); + memcpy((void *)fVoiceData.data(), data, len ); +} + +const char *plNetMsgVoice::GetVoiceData() const +{ + return fVoiceData.c_str(); +} +//////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////// +// plNetMsgListenListUpdate +//////////////////////////////////////////////////////// +int plNetMsgListenListUpdate::IPokeBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMessage::IPokeBuffer(stream, peekOptions); + stream->WriteSwap(fAdding); + fReceivers.Poke(stream, peekOptions); + return stream->GetPosition(); +} + +int plNetMsgListenListUpdate::IPeekBuffer(hsStream* stream, UInt32 peekOptions) +{ + int bytes=plNetMessage::IPeekBuffer(stream, peekOptions); + if (bytes) + { + stream->LogReadSwap(&fAdding,"ListenListUpdate Adding"); + stream->LogSubStreamPushDesc("ListenListUpdate Reveivers"); + fReceivers.Peek(stream, peekOptions); + bytes=stream->GetPosition(); + } + return bytes; +} + +//////////////////////////////////////////////////////////////////// +// plNetMsgPlayerPage +//////////////////////////////////////////////////////////////////// + +int plNetMsgPlayerPage::IPokeBuffer( hsStream* stream, UInt32 peekOptions ) +{ + plNetMessage::IPokeBuffer( stream, peekOptions ); + stream->WriteSwap( fUnload ); + fUoid.Write(stream); + + return stream->GetPosition(); +} + +int plNetMsgPlayerPage::IPeekBuffer( hsStream* stream, UInt32 peekOptions ) +{ + int bytes = plNetMessage::IPeekBuffer(stream, peekOptions ); + if ( bytes ) + { + stream->LogReadSwap( &fUnload,"PlayersPage Unload"); + fUoid.Read(stream); + bytes = stream->GetPosition(); + } + return bytes; +} + + +//////////////////////////////////////////////////////////////////// +// plNetMsgLoadClone +//////////////////////////////////////////////////////////////////// + +int plNetMsgLoadClone::IPokeBuffer( hsStream* stream, UInt32 peekOptions ) +{ + int bytes = plNetMsgGameMessage::IPokeBuffer( stream, peekOptions ); + if ( bytes ) + { + fObjectHelper.Poke(stream, peekOptions); + stream->WriteSwap( fIsPlayer ); + stream->WriteSwap( fIsLoading ); + stream->WriteSwap( fIsInitialState ); + bytes = stream->GetPosition(); + } + return bytes; +} + +int plNetMsgLoadClone::IPeekBuffer( hsStream* stream, UInt32 peekOptions ) +{ + stream->LogSubStreamPushDesc("LoadClone"); + int bytes = plNetMsgGameMessage::IPeekBuffer(stream, peekOptions ); + if ( bytes ) + { + stream->LogSubStreamPushDesc("MsgObject"); + fObjectHelper.Peek(stream, peekOptions); + + stream->LogReadSwap( &fIsPlayer,"LoadClone IsPlayer"); + stream->LogReadSwap( &fIsLoading,"LoadClone IsLoading"); + stream->LogReadSwap( &fIsInitialState, "LoadClone IsInitialState" ); + + bytes = stream->GetPosition(); + } + return bytes; +} + +void plNetMsgLoadClone::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgGameMessage::ReadVersion(s,mgr); + + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kObjectHelper)) + fObjectHelper.ReadVersion(s,mgr); + if (contentFlags.IsBitSet(kIsPlayer)) + s->ReadSwap(&fIsPlayer); + if (contentFlags.IsBitSet(kIsLoading)) + s->ReadSwap(&fIsLoading); + if (contentFlags.IsBitSet(kIsInitialState)) + s->ReadSwap(&fIsInitialState); +} + +void plNetMsgLoadClone::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + plNetMsgGameMessage::WriteVersion(s,mgr); + + hsBitVector contentFlags; + contentFlags.SetBit(kObjectHelper); + contentFlags.SetBit(kIsPlayer); + contentFlags.SetBit(kIsLoading); + contentFlags.SetBit(kIsInitialState); + contentFlags.Write(s); + + fObjectHelper.WriteVersion(s,mgr); + s->WriteSwap(fIsPlayer); + s->WriteSwap(fIsLoading); + s->WriteSwap(fIsInitialState); +} + +//////////////////////////////////////////////////////////////////// + +int plNetMsgInitialAgeStateSent::IPokeBuffer( hsStream* stream, UInt32 peekOptions ) +{ + plNetMessage::IPokeBuffer( stream, peekOptions ); + stream->WriteSwap( fNumInitialSDLStates ); + return stream->GetPosition(); +} + +int plNetMsgInitialAgeStateSent::IPeekBuffer( hsStream* stream, UInt32 peekOptions ) +{ + stream->LogSubStreamPushDesc("InitialAgeStateSent"); + int bytes=plNetMessage::IPeekBuffer(stream, peekOptions ); + if (bytes) + { + stream->LogReadSwap( &fNumInitialSDLStates, "NumInitialSDLStates" ); + bytes=stream->GetPosition(); + } + return bytes; +} + + +//////////////////////////////////////////////////////////////////// +// plNetMsgRelevanceRegions +//////////////////////////////////////////////////////////////////// + +int plNetMsgRelevanceRegions::IPokeBuffer( hsStream* stream, UInt32 peekOptions ) +{ + plNetMessage::IPokeBuffer( stream, peekOptions ); + fRegionsICareAbout.Write(stream); + fRegionsImIn.Write(stream); + + return stream->GetPosition(); +} + +int plNetMsgRelevanceRegions::IPeekBuffer( hsStream* stream, UInt32 peekOptions ) +{ + stream->LogSubStreamPushDesc("RelevanceRegions"); + int bytes=plNetMessage::IPeekBuffer(stream, peekOptions ); + if (bytes) + { + fRegionsICareAbout.Read(stream); + fRegionsImIn.Read(stream); + + bytes=stream->GetPosition(); + } + return bytes; +} + +//////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.h new file mode 100644 index 00000000..6d4afb55 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMessage.h @@ -0,0 +1,997 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetMessage_h_inc +#define plNetMessage_h_inc + +#include "hsUtils.h" +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsStream.h" +#include "hsBitVector.h" +#include "hsTemplates.h" +#include "plGeneric.h" +#include "../pnNetCommon/plNetServers.h" +#include "../pnNetCommon/plNetGroup.h" +#include "../pnFactory/plCreatable.h" +#include "../pnFactory/plFactory.h" +#include "../plUnifiedTime/plClientUnifiedTime.h" +#include "../plNetCommon/plNetServerSessionInfo.h" +#include "../plNetCommon/plNetCommon.h" +#include "../plNetCommon/plNetCommonHelpers.h" +#include "../plNetCommon/plNetCommonConstants.h" + +#include "../plStreamLogger/plStreamLogger.h" + +#include "plNetMsgHelpers.h" + +#include "../pnNetBase/pnNetBase.h" + +#include + +class plMessage; +class plUUID; + + +//////////////////////////////////////////////////////////////////// + +// +// Base class for application network messages. +// These become the data in a plNetCommonMessage when sent over the network. +// +class plNetCommonMessage; +class plKey; + +class plNetMessage : public plCreatable +{ + friend class plNetServerMsgPlsRoutableMsg; + + UInt32 fFlags; // indicates what is present in the message, always transmitted + plUnifiedTime fTimeSent; // time msg was sent (in sender's unified timeSpace), always transmitted to and from the client + double fTimeRecvd; // time msg was recvd (in rcvrs timeSpace), never transmitted + UInt32 fBytesRead; // amount of data we've already read, never transmitted + UInt32 fContext; // set by sender, included in reply. Only written if kHasContext flag set + UInt32 fTransactionID; // set by originator, included in reply. Only written if kHasTransactionID flag set + UInt32 fPlayerID; // set by originator. Only written if kHasPlayerID flag set + plUUID fAcctUUID; // set by sender (app level). Only written if kHasAcctUUID flag set + const plNetCommonMessage* fNetCoreMsg; // not sent, set by the receiver + UInt32 fPeekStatus; // not sent. set on PeekBuffer, cleared on PokeBuffer + UInt8 fProtocolVerMajor; // conditionally sent + UInt8 fProtocolVerMinor; // conditionally sent + ENetProtocol fNetProtocol; // the server this msg should be sent to. this value is not sent over wire. + + enum ContentFlags + { + kNetMsgFlags, + kNetMsgTimeSent, + kNetMsgContext, + kNetMsgTransactionID, + kNetMsgPlayerID, + kNetMsgVersion, + }; + +protected: + virtual int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + virtual int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); + + void IWriteClassIndex(hsStream* stream); + void IReadClassIndex(hsStream* stream); + + bool IPeeked() const { return fPeekStatus==kFullyPeeked;} + void ISetPeekStatus(UInt32 s) { fPeekStatus=s; } + +public: + typedef UInt16 plStrLen; + static const UInt8 kVerMajor, kVerMinor; // version of the networking code + + typedef UInt16 ClassIndexType; // the type returned by plCreatable::ClassIndex() + enum + { + kMaxNameLen=32 + }; + enum BitVectorFlags // indicates what is present in the message, always transmitted + { + kHasTimeSent = 0x1, // means fTimeSent need sending + kHasGameMsgRcvrs = 0x2, // means that this is a direct (not bcast) game msg + kEchoBackToSender = 0x4, // if broadcasting, echo packet back to sender + kRequestP2P = 0x8, // sent to gameServer on joinReq + kAllowTimeOut = 0x10, // sent to gameServer on joinReq (if release code) + kIndirectMember = 0x20, // sent to client on joinAck if he is behind a firewall + // This flag is used when the servers are firewalled and NAT'ed + // It tells a game or lobby server to ask the lookup for an external address + kPublicIPClient = 0x40, // set on a client coming from a public IP + kHasContext = 0x80, // whether or not to write fContext field + // Used with StartProcess server msgs. Whether or not the ServerAgent + // must first ask the vault for a game state associated with the + // game about to be instanced. + kAskVaultForGameState =0x100, + kHasTransactionID = 0x200, // whether or not to write fTransactionID field + kNewSDLState = 0x400, // set by client on first state packet sent, may not be necessary anymore + kInitialAgeStateRequest = 0x800,// initial request for the age state + kHasPlayerID = 0x1000, // is fPlayerID set + kUseRelevanceRegions= 0x2000, // if players use relevance regions are used, this will be filtered by region, currently set on avatar physics and control msgs + kHasAcctUUID = 0x4000, // is fAcctUUID set + kInterAgeRouting = 0x8000, // give to pls for routing. + kHasVersion = 0x10000, // if version is set + kIsSystemMessage = 0x20000, + kNeedsReliableSend = 0x40000, + kRouteToAllPlayers = 0x80000, // send this message to all online players. + }; + enum PeekOptions // options for partial peeking + { + kSkipStream = 1<<0, // means we should not read the actual stream, just the stream info + kBaseClassOnly = 1<<1, // only peek or poke the baseclass info + kPartialPeekMask = kSkipStream | kBaseClassOnly, + // plNetServerMsgWithRoutingInfo derived classes need to check this + // in their PeekBuffer methods. + kJustRoutingInfo = 1<<2, + kDontClearBuffer = 1<<3, + // don't call base class peek/poke. plNetMsgOmnibus/plNetMsgVault uses this. + kDEAD_NoBaseClass = 1<<4, + // don't worry about compressing/uncompressing things. used by plNetMsgStreamHelper, and plNetMsgOmnibus. + kDontCompress = 1<<5, + kWantVersion = 1<<6, + kFullyPeeked = 0xffffffff // default fPeekStatus, set if not partial peeking + }; + enum CompressionType // currently only used for plNetMsgStreams + { + kCompressionNone, // not compressed + kCompressionFailed, // failed to compress + kCompressionZlib, // zlib compressed + kCompressionDont // don't compress + }; + + CLASSNAME_REGISTER( plNetMessage ); + GETINTERFACE_ANY( plNetMessage, plCreatable ); + + // plCreatable + void Read(hsStream* s, hsResMgr* mgr); + void Write(hsStream* s, hsResMgr* mgr); + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); + + // ctor + plNetMessage(); + virtual ~plNetMessage(); + + static plNetMessage* CreateAndRead(const plNetCommonMessage*, plStreamLogger::EventList* el = nil); + static plNetMessage* Create(const plNetCommonMessage*); + int PokeBuffer(char* buf, int bufLen, UInt32 peekOptions=0); // put msg in buffer + int PeekBuffer(const char* buf, int bufLen, UInt32 peekOptions=0, bool forcePeek=false, plStreamLogger::EventList* el = nil); // get msg out of buffer + bool NeedsReliableSend() const { return IsBitSet(kNeedsReliableSend); } + bool IsSystemMessage() const { return IsBitSet(kIsSystemMessage); } + virtual void ValidatePoke() const; + virtual void ValidatePeek() const; + virtual bool NeedsBroadcast() const { return false; } // should game server broadcast this message to other clients? + virtual int ValidationSchemeID() const { return 1; } + + // getters + int GetPackSize(); + const plUnifiedTime& GetTimeSent() const { return fTimeSent; } + bool GetHasTimeSent() const { return IsBitSet(kHasTimeSent); } + double GetTimeReceived() const { return fTimeRecvd; } + bool IsBitSet(int b) const { return (fFlags & b) != 0; } + const plNetCommonMessage* GetNetCoreMsg() const { return fNetCoreMsg; } + UInt32 GetNetCoreMsgLen() const; + bool GetHasContext() const { return IsBitSet(kHasContext);} + UInt32 GetContext() const { return fContext;} + bool GetHasTransactionID() const { return IsBitSet(kHasTransactionID);} + UInt32 GetTransactionID() const { return fTransactionID;} + bool GetHasPlayerID() const { return IsBitSet(kHasPlayerID);} + UInt32 GetPlayerID() const { hsAssert(GetHasPlayerID(), "uninit playerID"); return fPlayerID; } + UInt32 JustGetPlayerID() const { return fPlayerID; } + bool GetHasAcctUUID() const { return IsBitSet(kHasAcctUUID); } + const plUUID * GetAcctUUID() const { return &fAcctUUID; } + UInt8 GetVersionMajor() const { return fProtocolVerMajor; } + UInt8 GetVersionMinor() const { return fProtocolVerMinor; } + ENetProtocol GetNetProtocol () const { return fNetProtocol; } + + // setters + void SetTimeSent(const plUnifiedTime& t) { fTimeSent=t;SetHasTimeSent(true); } + void SetHasTimeSent(bool value) { SetBit( kHasTimeSent, value ); } + void SetTimeReceived(double t) { fTimeRecvd=t; } + void SetBit(int b, bool on=true) { if (on) fFlags |= b; else fFlags &= ~b; } + void SetNetCoreMsg(const plNetCommonMessage* ncmsg) { fNetCoreMsg=ncmsg; } + void SetHasContext(bool value) { SetBit(kHasContext,value);} + void SetContext(UInt32 value) { fContext=value; SetHasContext(true);} + void SetHasTransactionID(bool value) { SetBit(kHasTransactionID,value);} + void SetTransactionID(UInt32 value) { fTransactionID=value; SetHasTransactionID(true);} + void SetHasPlayerID(bool value) { SetBit(kHasPlayerID,value);} + void SetPlayerID(UInt32 value) { fPlayerID=value; SetHasPlayerID(true);} + void SetHasAcctUUID( bool v ) { SetBit( kHasAcctUUID,v ); } + void SetAcctUUID(const plUUID * v ) { fAcctUUID.CopyFrom(v); SetHasAcctUUID(true); } + void SetVersion(UInt8 maj=kVerMajor, UInt8 min=kVerMinor) { SetBit(kHasVersion); fProtocolVerMajor=maj; fProtocolVerMinor=min; } + void SetNetProtocol (ENetProtocol v ) { fNetProtocol = v; } + + // init fContext, fTransactionID, etc. if needed. + void InitReplyFieldsFrom(plNetMessage * msg); + + // debug + virtual std::string AsStdString() const + { + char * delim = ""; + + std::stringstream ss; + if ( GetHasPlayerID() ) + { + ss << delim << "p:" << GetPlayerID(); + delim = ","; + } + if ( GetHasTransactionID() ) + { + ss << delim << "x:" << GetTransactionID(); + delim = ","; + } + if ( GetHasAcctUUID() ) + { + ss << delim << "a:" << GetAcctUUID()->AsStdString(); + delim = ","; + } + if ( IsBitSet(kHasVersion) ) + { + ss << delim << "v:" << (int)fProtocolVerMajor << "." << (int)fProtocolVerMinor; + delim = ","; + } + + return ss.str().c_str(); + } +}; + +// +// for msgs which only go from the gameserver to the client +// +class plNetMsgServerToClient : public plNetMessage +{ +public: + plNetMsgServerToClient() { SetBit(kIsSystemMessage|kNeedsReliableSend); } + + CLASSNAME_REGISTER( plNetMsgServerToClient ); + GETINTERFACE_ANY( plNetMsgServerToClient, plNetMessage ); +}; + +//////////////////////////////////////////////////////////////////// +// Stream msg - abstract base class built with plNetMsgStreamHelper +// +class plNetMsgStream : public plNetMessage +{ +protected: + plNetMsgStreamHelper fStreamHelper; + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + CLASSNAME_REGISTER( plNetMsgStream ); + GETINTERFACE_ANY_AUX(plNetMsgStream,plNetMessage,plNetMsgStreamHelper,fStreamHelper) + + plNetMsgStreamHelper* StreamInfo() { return &fStreamHelper; } + const plNetMsgStreamHelper* StreamInfo() const { return &fStreamHelper; } +}; + +// +// Object info msg - abstract base class built with plNetMsgObjectHelper +// +class plNetMsgObject : public plNetMessage +{ +private: + enum ContentFlags + { + kNetMsgObjectHelper, + }; +protected: + plNetMsgObjectHelper fObjectHelper; + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + CLASSNAME_REGISTER( plNetMsgObject ); + GETINTERFACE_ANY_AUX(plNetMsgObject,plNetMessage,plNetMsgObjectHelper,fObjectHelper) + + plNetMsgObjectHelper* ObjectInfo() { return &fObjectHelper; } + const plNetMsgObjectHelper* ObjectInfo() const { return &fObjectHelper; } + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); + + // debug + std::string AsStdString() const + { + std::string s; + char tmp[256]; + xtl::format(s,"object=%s, %s",fObjectHelper.GetUoid().StringIze(tmp), plNetMessage::AsStdString().c_str()); + return s; + } + +}; + +// +// abstract baseclass which has both object and stream info +// +class plNetMsgStreamedObject : public plNetMsgObject +{ +private: + enum ContentFlags + { + kStreamHelper + }; +protected: + plNetMsgStreamHelper fStreamHelper; + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plNetMsgStreamedObject() {} + ~plNetMsgStreamedObject() {} + + CLASSNAME_REGISTER( plNetMsgStreamedObject ); + GETINTERFACE_ANY_AUX(plNetMsgStreamedObject,plNetMsgObject,plNetMsgStreamHelper,fStreamHelper) + + plNetMsgStreamHelper* StreamInfo() { return &fStreamHelper; } + const plNetMsgStreamHelper* StreamInfo() const { return &fStreamHelper; } + + //virtuals + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); + +}; + +// +// New SaveState system +// +class plNetMsgSDLState : public plNetMsgStreamedObject +{ +private: + enum ContentFlags + { + kSDLStateStream, + kSDLIsInitialState, + kSDLPersist, + kSDLAvatarState, + }; + + void ISetDescName() const; + bool fIsInitialState; + +protected: + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); + + bool fPersistOnServer; + bool fIsAvatarState; + mutable std::string fDescName; // for debugging output only, not read/written +public: + CLASSNAME_REGISTER( plNetMsgSDLState ); + GETINTERFACE_ANY(plNetMsgSDLState, plNetMsgStreamedObject); + + plNetMsgSDLState() : fIsInitialState(0), fPersistOnServer(true), fIsAvatarState(false) { SetBit(kNeedsReliableSend); } + + bool PersistOnServer() const { return fPersistOnServer != 0; } + void SetPersistOnServer(bool b) { fPersistOnServer = b; } + + bool IsAvatarState() const { return fIsAvatarState != 0; } + void SetIsAvatarState(bool b) { fIsAvatarState = b; } + + // debug + std::string AsStdString() const; + bool IsInitialState() const {return fIsInitialState!=0; } + void SetIsInitialState( bool v ) { fIsInitialState=v; } + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +// +// New SaveState system +// +class plNetMsgSDLStateBCast : public plNetMsgSDLState +{ +protected: + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + CLASSNAME_REGISTER( plNetMsgSDLStateBCast ); + GETINTERFACE_ANY(plNetMsgSDLStateBCast, plNetMsgSDLState); + + // virtuals + bool NeedsBroadcast() const { return true; } + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +// +// Object state request msg +// +class plNetMsgObjStateRequest : public plNetMsgObject +{ +public: + plNetMsgObjStateRequest() { SetBit(kIsSystemMessage|kNeedsReliableSend); } + + CLASSNAME_REGISTER( plNetMsgObjStateRequest ); + GETINTERFACE_ANY( plNetMsgObjStateRequest, plNetMsgObject ); +}; + + +// +// abstract message class which contains a list of rooms +// +class plNetMsgRoomsList : public plNetMessage +{ +protected: + std::vector fRooms; + std::vector fRoomNames; // for debug usage only + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plNetMsgRoomsList() {} + ~plNetMsgRoomsList(); + + CLASSNAME_REGISTER( plNetMsgRoomsList ); + GETINTERFACE_ANY( plNetMsgRoomsList, plNetMessage ); + + void AddRoom(plKey rmKey); + void AddRoomLocation(plLocation loc, const char* rmName); + int FindRoomLocation(plLocation loc); + + int GetNumRooms() const { return fRooms.size(); } + plLocation GetRoomLoc(int i) const { return fRooms[i]; } + const char* GetRoomName(int i) const { return fRoomNames[i]; } // debug +}; + +// +// Game msg - wraps a plMessage. +// +class hsResMgr; + +class plNetMsgGameMessage: public plNetMsgStream +{ +protected: + plClientUnifiedTime fDeliveryTime; // for future timestamping + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plNetMsgGameMessage() { SetBit(kNeedsReliableSend); } + + CLASSNAME_REGISTER( plNetMsgGameMessage ); + GETINTERFACE_ANY( plNetMsgGameMessage, plNetMsgStream ); + enum ContentsFlags + { + kNetGameMsgDeliveryTime, + kNetGameMsgGameMsg, + }; + plClientUnifiedTime& GetDeliveryTime() { return fDeliveryTime; } + void SetDeliveryTime(plClientUnifiedTime& ut) { fDeliveryTime=ut; } + + plMessage* GetContainedMsg(hsResMgr* resmgr = nil); + + // virtuals + bool NeedsBroadcast() const { return true; } + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); + + // debug + std::string AsStdString() const + { + std::string s; + const char* noc=plFactory::GetTheFactory()->GetNameOfClass(StreamInfo()->GetStreamType()); + xtl::format(s,"%s %s",plNetMsgStream::AsStdString().c_str(), noc ? noc : "?"); + return s; + } +}; + +// +// special game msg for loading clones/avatars +// +class plNetMsgLoadClone : public plNetMsgGameMessage +{ +private: + enum ContentFlags + { + kObjectHelper, + kIsPlayer, + kIsLoading, + kIsInitialState, + }; +protected: + bool fIsPlayer; + bool fIsLoading; + bool fIsInitialState; + plNetMsgObjectHelper fObjectHelper; + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + + CLASSNAME_REGISTER( plNetMsgLoadClone ); + GETINTERFACE_ANY_AUX(plNetMsgLoadClone, plNetMsgGameMessage,plNetMsgObjectHelper,fObjectHelper) + + plNetMsgLoadClone() : fIsPlayer(true),fIsLoading(true),fIsInitialState(false) {} + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); + + plNetMsgObjectHelper* ObjectInfo() { return &fObjectHelper; } + const plNetMsgObjectHelper* ObjectInfo() const { return &fObjectHelper; } + + bool GetIsPlayer() const { return fIsPlayer!=0; } + bool GetIsLoading() const { return fIsLoading!=0; } + bool GetIsInitialState() const { return fIsInitialState!=0; } + + void SetIsPlayer(bool p) { fIsPlayer=p; } + void SetIsLoading(bool p) { fIsLoading=p; } + void SetIsInitialState(bool p) {fIsInitialState=p; } + + + // debug + std::string AsStdString() const + { + std::string s; + char tmp[256]; + xtl::format(s,"object=%s initial=%d, %s",fObjectHelper.GetUoid().StringIze(tmp), fIsInitialState, + plNetMsgGameMessage::AsStdString().c_str()); + return s; + } +}; + +// +// special msg when a player is paged in/out +// +class plNetMsgPlayerPage : public plNetMessage +{ +protected: + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plUoid fUoid; + bool fUnload; + + plNetMsgPlayerPage() : fUnload(false) { SetBit(kNeedsReliableSend); } + plNetMsgPlayerPage(plUoid uoid, hsBool unload) : fUoid(uoid), fUnload(unload) { } + + CLASSNAME_REGISTER( plNetMsgPlayerPage ); + GETINTERFACE_ANY( plNetMsgPlayerPage, plNetMessage); +}; + +// +// a game message that takes a list of other clients as receivers +// +class plNetMsgGameMessageDirected : public plNetMsgGameMessage +{ +private: + enum ContentFlags + { + kRecievers, + }; +protected: + plNetMsgReceiversListHelper fReceivers; + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + CLASSNAME_REGISTER( plNetMsgGameMessageDirected ); + GETINTERFACE_ANY_AUX(plNetMsgGameMessageDirected,plNetMsgGameMessage, + plNetMsgReceiversListHelper,fReceivers) + + plNetMsgReceiversListHelper* Receivers() { return &fReceivers; } + + // virtuals + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +// +// Message when a room is paged in/out +// +class plNetMsgPagingRoom : public plNetMsgRoomsList +{ +public: + enum PageFlags + { + kPagingOut=0x1, // else paging in + kResetList=0x2, // server should reset his existing list before using this msg + kRequestState=0x4, // also want current room state sent to me + kFinalRoomInAge=0x8 // done paging in the age + }; +protected: + UInt8 fPageFlags; + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plNetMsgPagingRoom() : fPageFlags(0) { SetBit(kIsSystemMessage|kNeedsReliableSend); } + ~plNetMsgPagingRoom() {} + + CLASSNAME_REGISTER( plNetMsgPagingRoom ); + GETINTERFACE_ANY( plNetMsgPagingRoom, plNetMsgRoomsList ); + + void SetPageFlags(UInt8 f) { fPageFlags=f; } + UInt8 GetPageFlags() const { return fPageFlags; } + + void SetPagingOut(hsBool b) { if (b) fPageFlags |= kPagingOut; else fPageFlags&=~kPagingOut; } + hsBool GetPagingOut() const { return (fPageFlags & kPagingOut) != 0; } + + void SetResetList(hsBool b) { if (b) fPageFlags |= kResetList; else fPageFlags &=~kResetList; } + hsBool GetResetList() const { return (fPageFlags & kResetList) != 0; } + + void SetRequestingState(hsBool b) { if (b) fPageFlags |= kRequestState; else fPageFlags &=~kRequestState; } + hsBool GetRequestingState() const { return (fPageFlags & kRequestState) != 0; } + + // debug + std::string AsStdString() const + { + std::string s; + xtl::format(s,"pageFlags:%02X, paging %s, requestingState:%s, resetting=%d",fPageFlags, + (fPageFlags&kPagingOut)?"out":"in", (fPageFlags&kRequestState)?"yes":"no", + (fPageFlags & kResetList)!=0); + return s; + } +}; + +// +// Client requests game state update by rooms. +// an empty rooms list means ALL rooms I have loaded. +// +class plNetMsgGameStateRequest : public plNetMsgRoomsList +{ +public: + plNetMsgGameStateRequest() { SetBit(kIsSystemMessage|kNeedsReliableSend); } + + CLASSNAME_REGISTER( plNetMsgGameStateRequest ); + GETINTERFACE_ANY( plNetMsgGameStateRequest, plNetMsgRoomsList ); +}; + +// +// sent by game server to change clients ownership of a netGroup +// +class plNetMsgGroupOwner: public plNetMsgServerToClient +{ +public: + class GroupInfo + { + public: + plNetGroupId fGroupID; + bool fOwnIt; // else not the owner + + void Read(hsStream* s) { fGroupID.Read(s); s->LogReadSwap(&fOwnIt,"GroupOwner OwnIt"); } + void Write(hsStream* s) { fGroupID.Write(s); s->WriteSwap(fOwnIt); } + + GroupInfo() : fGroupID(plNetGroup::kNetGroupUnknown), fOwnIt(false) {} + GroupInfo(plNetGroupId gID, hsBool o) : fGroupID(gID),fOwnIt(o) {} + }; +protected: + std::vector fGroups; + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + CLASSNAME_REGISTER( plNetMsgGroupOwner ); + GETINTERFACE_ANY( plNetMsgGroupOwner, plNetMsgServerToClient ); + + // getters + int GetNumGroups() const { return fGroups.size(); } + GroupInfo GetGroupInfo(int i) const { return fGroups[i]; } + + // setters + void AddGroupInfo(GroupInfo gi) { fGroups.push_back(gi); } + void ClearGroupInfo() { fGroups.clear(); } + + bool IsOwner() { return fGroups[0].fOwnIt; } +}; + +// +// voice recording buffer +// +class plNetMsgVoice: public plNetMessage +{ +private: + enum ContentFlags + { + kDead_FrameSize, + kReceivers, + kVoiceFlags, + kVoiceData + }; +protected: + UInt8 fFlags; // voice flags + UInt8 fNumFrames; // number of frames encoded + std::string fVoiceData; + + plNetMsgReceiversListHelper fReceivers; + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plNetMsgVoice(): fFlags(0), fNumFrames(0) { } + ~plNetMsgVoice() {} + + CLASSNAME_REGISTER( plNetMsgVoice ); + GETINTERFACE_ANY_AUX(plNetMsgVoice,plNetMessage,plNetMsgReceiversListHelper,fReceivers) + + void SetFlag(int f) { fFlags |= f; } + int GetFlags() { return fFlags; } + + void SetNumFrames(UInt8 f) { fNumFrames = f; } + UInt8 GetNumFrames() const { return fNumFrames; } + + void SetVoiceData(char *data, int len ); + int GetVoiceDataLen() const { return fVoiceData.length(); } + const char *GetVoiceData() const; + + plNetMsgReceiversListHelper* Receivers() { return &fReceivers; } + + // virtuals + bool NeedsBroadcast() const { return true; } + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); + + // debug + std::string AsStdString() const + { + std::string s; + xtl::format(s,"len=%d",fVoiceData.size()); + return s; + } +}; + +// +// base class for dealing with plNetSharedState +// +class plNetSharedState; +class plNetMsgSharedState : public plNetMsgStreamedObject +{ +private: + enum ContentFlags + { + kLockRequest, + }; +protected: + bool fLockRequest; + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plNetMsgSharedState() : fLockRequest(false) {} + ~plNetMsgSharedState() {} + + CLASSNAME_REGISTER( plNetMsgSharedState ); + GETINTERFACE_ANY(plNetMsgSharedState, plNetMsgStreamedObject); + + void CopySharedState(plNetSharedState* ss); + + void SetLockRequest(hsBool b) { fLockRequest=b; } + hsBool GetLockRequest() const { return fLockRequest; } + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); + + // debug + std::string AsStdString() const + { + std::string s; + xtl::format(s,"lockReq=%d, %s",fLockRequest, plNetMsgStreamedObject::AsStdString().c_str()); + return s; + } +}; + +// +// attempt to lock/unlock and set generic shared state on server. +// lock attempts will generate server reply messages confirming or denying the action. +// +class plNetMsgTestAndSet : public plNetMsgSharedState +{ +public: + plNetMsgTestAndSet() { SetBit(kNeedsReliableSend); } + + CLASSNAME_REGISTER( plNetMsgTestAndSet ); + GETINTERFACE_ANY(plNetMsgTestAndSet, plNetMsgSharedState); +}; + +// +// provides a way to query sharedState on the server +// +class plNetMsgGetSharedState : public plNetMsgObject +{ +protected: + char fSharedStateName[kMaxNameLen]; + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plNetMsgGetSharedState() { *fSharedStateName=0; SetBit(kNeedsReliableSend); } + ~plNetMsgGetSharedState() {} + + CLASSNAME_REGISTER( plNetMsgGetSharedState ); + GETINTERFACE_ANY( plNetMsgGetSharedState, plNetMsgObject ); + + void SetSharedStateName(char* n) { if (n) hsStrncpy(fSharedStateName, n, kMaxNameLen); } + char* GetSharedStateName() { return fSharedStateName; } +}; + +// +// msg which sets the update frequency for a group of objects on the server +// +class plNetMsgObjectUpdateFilter : public plNetMessage +{ +protected: + plNetMsgObjectListHelper fObjectListHelper; + float fMaxUpdateFreq; // in secs + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plNetMsgObjectUpdateFilter() : fMaxUpdateFreq(-1) {} + ~plNetMsgObjectUpdateFilter() {} + + CLASSNAME_REGISTER( plNetMsgObjectUpdateFilter ); + GETINTERFACE_ANY_AUX(plNetMsgObjectUpdateFilter,plNetMessage,plNetMsgObjectListHelper,fObjectListHelper) + + plNetMsgObjectListHelper* ObjectListInfo() { return &fObjectListHelper; } + + void SetMaxUpdateFreq(float f) { fMaxUpdateFreq=f; } + float GetMaxUpdateFreq() const { return fMaxUpdateFreq; } +}; + +// +// Client wants a list of all members in the session +// +class plNetMsgMembersListReq : public plNetMessage +{ +public: + plNetMsgMembersListReq() { SetBit(kIsSystemMessage|kNeedsReliableSend); } + + CLASSNAME_REGISTER( plNetMsgMembersListReq ); + GETINTERFACE_ANY( plNetMsgMembersListReq, plNetMessage ); +}; + +// +// Server returns a list of all members in the session +// +class plNetMsgMembersList : public plNetMsgServerToClient +{ +protected: + plNetMsgMemberListHelper fMemberListHelper; +protected: + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + CLASSNAME_REGISTER( plNetMsgMembersList ); + GETINTERFACE_ANY_AUX(plNetMsgMembersList,plNetMsgServerToClient,plNetMsgMemberListHelper,fMemberListHelper) + + plNetMsgMemberListHelper* MemberListInfo() { return &fMemberListHelper; } +}; + +// +// server tells client to add or remove a session member +// +class plNetMsgMemberUpdate : public plNetMsgServerToClient +{ +protected: + plNetMsgMemberInfoHelper fMemberInfo; + bool fAddMember; // else remove member +protected: + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + CLASSNAME_REGISTER( plNetMsgMemberUpdate ); + GETINTERFACE_ANY_AUX(plNetMsgMemberUpdate,plNetMsgServerToClient,plNetMsgMemberInfoHelper,fMemberInfo) + + bool AddingMember() { return fAddMember; } + void SetAddingMember(bool b) { fAddMember=b; } + + plNetMsgMemberInfoHelper* MemberInfo() { return &fMemberInfo; } + bool NeedsBroadcast() const { return true; } // send to all clients +}; + + +// +// ListenList updater msgs. For voice-broadcasting purposes. +// Contains a list of other players which I am [not] listening to. +// Sent client-client or client-server. +// +class plNetMsgListenListUpdate : public plNetMessage +{ +private: + plNetMsgReceiversListHelper fReceivers; // used by server, the players we're listening to + bool fAdding; // else removing + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plNetMsgListenListUpdate() : fAdding(false) {} + ~plNetMsgListenListUpdate() {} + + CLASSNAME_REGISTER( plNetMsgListenListUpdate ); + GETINTERFACE_ANY_AUX(plNetMsgListenListUpdate,plNetMessage,plNetMsgReceiversListHelper,fReceivers) + + plNetMsgReceiversListHelper* Receivers() { return &fReceivers; } + + bool GetAdding() const { return fAdding; } + void SetAdding(bool a) { fAdding=a; } + + // virtuals + bool NeedsBroadcast() const { return true; } // use rcvrs list +}; + + +/////////////////////////////////////////////////////////////////// +class plNetMsgInitialAgeStateSent : public plNetMsgServerToClient +{ + UInt32 fNumInitialSDLStates; + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plNetMsgInitialAgeStateSent():fNumInitialSDLStates(0){} + CLASSNAME_REGISTER( plNetMsgInitialAgeStateSent ); + GETINTERFACE_ANY( plNetMsgInitialAgeStateSent, plNetMsgServerToClient); + void SetNumInitialSDLStates( UInt32 n ) { fNumInitialSDLStates=n; } + UInt32 GetNumInitialSDLStates() const { return fNumInitialSDLStates; } +}; + +// +// msg which sets the update frequency for a group of objects on the server +// +class plNetMsgRelevanceRegions : public plNetMessage +{ +protected: + hsBitVector fRegionsImIn; + hsBitVector fRegionsICareAbout; + + int IPokeBuffer(hsStream* stream, UInt32 peekOptions=0); + int IPeekBuffer(hsStream* stream, UInt32 peekOptions=0); +public: + plNetMsgRelevanceRegions() { SetBit(kNeedsReliableSend); } + ~plNetMsgRelevanceRegions() {} + + CLASSNAME_REGISTER( plNetMsgRelevanceRegions ); + GETINTERFACE_ANY(plNetMsgRelevanceRegions, plNetMessage) + + void SetRegionsICareAbout(const hsBitVector& r) { fRegionsICareAbout=r; } + void SetRegionsImIn(const hsBitVector& r) { fRegionsImIn=r; } + + const hsBitVector& GetRegionsICareAbout() const { return fRegionsICareAbout; } + const hsBitVector& GetRegionsImIn() const { return fRegionsImIn; } + + std::string AsStdString() const + { + std::string s; + std::string b1, b2; + int i; + for(i=0;i. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetMsgCreatable_h +#define plNetMsgCreatable_h + +// kept separate for a reason + +#include "../pnFactory/plCreator.h" +#include "plNetMessage.h" + +// NON CREATABLES +REGISTER_NONCREATABLE(plNetMessage); +REGISTER_NONCREATABLE(plNetMsgObject); +REGISTER_NONCREATABLE(plNetMsgStream); +REGISTER_NONCREATABLE(plNetMsgRoomsList); +REGISTER_NONCREATABLE(plNetMsgStreamedObject); +REGISTER_NONCREATABLE(plNetMsgSharedState); + +// HELPERS +REGISTER_CREATABLE(plNetMsgStreamHelper); +REGISTER_CREATABLE(plNetMsgObjectHelper); +REGISTER_CREATABLE(plNetMsgObjectListHelper); +REGISTER_CREATABLE(plNetMsgMemberInfoHelper); +REGISTER_CREATABLE(plNetMsgMemberListHelper); +REGISTER_CREATABLE(plNetMsgStreamableHelper); +REGISTER_CREATABLE(plNetMsgReceiversListHelper); +REGISTER_CREATABLE(plNetMsgCreatableHelper); + +// CLIENT MSGS +REGISTER_CREATABLE(plNetMsgGameStateRequest); +REGISTER_CREATABLE(plNetMsgSDLState); +REGISTER_CREATABLE(plNetMsgSDLStateBCast); +REGISTER_CREATABLE(plNetMsgLoadClone); +REGISTER_CREATABLE(plNetMsgPlayerPage); +REGISTER_CREATABLE(plNetMsgGameMessage); +REGISTER_CREATABLE(plNetMsgGameMessageDirected); +REGISTER_CREATABLE(plNetMsgPagingRoom); +REGISTER_CREATABLE(plNetMsgGroupOwner); +REGISTER_CREATABLE(plNetMsgVoice); +REGISTER_CREATABLE(plNetMsgTestAndSet); +REGISTER_CREATABLE(plNetMsgGetSharedState); +REGISTER_CREATABLE(plNetMsgObjStateRequest); +REGISTER_CREATABLE(plNetMsgObjectUpdateFilter); +REGISTER_CREATABLE(plNetMsgMembersListReq ); +REGISTER_CREATABLE(plNetMsgMembersList ); +REGISTER_CREATABLE(plNetMsgServerToClient); +REGISTER_CREATABLE(plNetMsgMemberUpdate ); +REGISTER_CREATABLE(plNetMsgListenListUpdate); + +REGISTER_CREATABLE(plNetMsgInitialAgeStateSent); +REGISTER_CREATABLE(plNetMsgRelevanceRegions); + + +#endif // plNetMsgCreatable_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgHelpers.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgHelpers.cpp new file mode 100644 index 00000000..7985232a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgHelpers.cpp @@ -0,0 +1,547 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNetMsgHelpers.h" +#include "plNetMessage.h" +#include "../plCompression/plZlibCompress.h" +#include "../pnNetCommon/plNetServers.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnMessage/plMessage.h" +#include "hsStream.h" +#include + + +//////////////////////////////////////////////////////////////////// +// plNetMsgStreamableHelper + +plNetMsgStreamableHelper & plNetMsgStreamableHelper::operator =(hsStreamable * value) +{ + fObject = value; + return *this; +} + +int plNetMsgStreamableHelper::Poke(hsStream* stream, UInt32 peekOptions) +{ + hsAssert(fObject, "plNetMsgStreamableHelper::Poke: fObject not set."); + fObject->Write(stream); + return stream->GetPosition(); +} + +int plNetMsgStreamableHelper::Peek(hsStream* stream, UInt32 peekOptions) +{ + hsAssert(fObject, "plNetMsgStreamableHelper::Peek: fObject not set."); + fObject->Read(stream); + return stream->GetPosition(); +} + +//////////////////////////////////////////////////////////////////// +// plNetMsgCreatableHelper + +plNetMsgCreatableHelper::plNetMsgCreatableHelper(plCreatable * object) +:fCreatable(object) +,fWeCreatedIt(false) +{ +} + +plNetMsgCreatableHelper::~plNetMsgCreatableHelper() +{ + if (fWeCreatedIt) + hsRefCnt_SafeUnRef(fCreatable); +} + +plNetMsgCreatableHelper & plNetMsgCreatableHelper::operator =(plCreatable * value) +{ + SetObject(value); + return *this; +} + +plNetMsgCreatableHelper::operator plCreatable*() +{ + return GetObject(); +} + +plNetMsgCreatableHelper::operator const plCreatable*() +{ + return GetObject(); +} + +void plNetMsgCreatableHelper::SetObject(plCreatable * object) +{ + if (fWeCreatedIt) + hsRefCnt_SafeUnRef(fCreatable); + fCreatable = object; + fWeCreatedIt = false; +} + +plCreatable * plNetMsgCreatableHelper::GetObject() +{ + return fCreatable; +} + +int plNetMsgCreatableHelper::Poke(hsStream * s, UInt32 peekOptions) +{ + hsAssert(fCreatable,"plNetMsgCreatableHelper::Poke: fCreatable not set"); + UInt16 classIndex = fCreatable->ClassIndex(); + s->WriteSwap(classIndex); + fCreatable->Write(s,nil); + return s->GetPosition(); +} + +int plNetMsgCreatableHelper::Peek(hsStream * s, UInt32 peekOptions) +{ + UInt16 classIndex; + s->LogSubStreamStart("push me"); + s->LogReadSwap(&classIndex,"ClassIdx"); + SetObject(plFactory::Create(classIndex)); + fWeCreatedIt = true; + hsAssert(fCreatable,"plNetMsgCreatableHelper::Peek: Failed to create plCreatable. Invalid ClassIndex?"); + fCreatable->Read(s,nil); + s->LogSubStreamEnd(); + return s->GetPosition(); +} + + +///////////////////////////////////////////////////////// +// NOT A MSG +// PL STREAM MSG - HELPER class +///////////////////////////////////////////////////////// +plNetMsgStreamHelper::plNetMsgStreamHelper() : fStreamBuf(nil), fStreamType(-1), fStreamLen(0), + fCompressionType(plNetMessage::kCompressionNone), fUncompressedSize(0), + fCompressionThreshold( kDefaultCompressionThreshold ) +{ +} + +void plNetMsgStreamHelper::Clear() +{ + delete [] fStreamBuf; + fStreamBuf = nil; + fStreamType = 0xff; + fStreamLen = 0; + fCompressionType = plNetMessage::kCompressionNone; + fCompressionThreshold = kDefaultCompressionThreshold; +} + +int plNetMsgStreamHelper::Poke(hsStream* stream, UInt32 peekOptions) +{ + if ( !(peekOptions & plNetMessage::kDontCompress) ) + Compress(); + stream->WriteSwap(fUncompressedSize); + stream->WriteSwap(fCompressionType); + stream->WriteSwap(fStreamLen); + stream->Write(fStreamLen, fStreamBuf); + return stream->GetPosition(); +} + +int plNetMsgStreamHelper::Peek(hsStream* stream, const UInt32 peekOptions) +{ + stream->LogSubStreamStart("Stream Helper"); + stream->LogReadSwap(&fUncompressedSize,"UncompressedSize"); + stream->LogReadSwap(&fCompressionType,"CompressionType"); + stream->LogReadSwap(&fStreamLen,"StreamLen"); + + if (fStreamLen) // stream data exists + { + if (!(peekOptions & plNetMessage::kSkipStream)) + { + if (!fStreamBuf) + IAllocStream(fStreamLen); + stream->LogRead(fStreamLen, fStreamBuf,"StreamData"); + if ( !(peekOptions & plNetMessage::kDontCompress) ) + Uncompress(); + fStreamType = *(Int16*)fStreamBuf; // grab from start fo stream + } + else + { + stream->ReadSwap(&fStreamType); // never compressed, set by reading directly from stream + stream->LogSkip(fStreamLen-sizeof(fStreamType),"SkippedStreamHelper"); + } + } + stream->LogSubStreamEnd(); + return stream->GetPosition(); +} + +void plNetMsgStreamHelper::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kUncompressedSize)) + s->ReadSwap(&fUncompressedSize); + if (contentFlags.IsBitSet(kCompressionType)) + s->ReadSwap(&fCompressionType); + if (contentFlags.IsBitSet(kStreamLen)) + s->ReadSwap(&fStreamLen); + if (contentFlags.IsBitSet(kStreamBuf)) + { + if (!fStreamBuf) + IAllocStream(fStreamLen); + s->Read(fStreamLen,fStreamBuf); + } +} + +void plNetMsgStreamHelper::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kUncompressedSize); + contentFlags.SetBit(kCompressionType); + contentFlags.SetBit(kStreamLen); + contentFlags.SetBit(kStreamBuf); + contentFlags.Write(s); + + s->WriteSwap(fUncompressedSize); + s->WriteSwap(fCompressionType); + s->WriteSwap(fStreamLen); + s->Write(fStreamLen,fStreamBuf); +} + +void plNetMsgStreamHelper::IAllocStream(UInt32 len) +{ + delete [] fStreamBuf; + fStreamBuf=nil; + fStreamLen=len; + if (len) + fStreamBuf = TRACKED_NEW UInt8[len]; +} + +void plNetMsgStreamHelper::CopyStream(hsStream* ssStream) +{ + UInt32 len=ssStream->GetEOF(); + IAllocStream(len); + ssStream->CopyToMem(fStreamBuf); + fStreamType = *(Int16*)fStreamBuf; +} + +void plNetMsgStreamHelper::CopyStream(Int32 len, const void* buf) +{ + IAllocStream(len); + memcpy(fStreamBuf, buf, len); + fStreamType = *(Int16*)fStreamBuf; +} + +void plNetMsgStreamHelper::CopyFrom(const plNetMsgStreamHelper* other) +{ + fUncompressedSize = other->GetUncompressedSize(); + fCompressionType = other->GetCompressionType(); + CopyStream(other->GetStreamLen(), other->GetStreamBuf()); +} + +bool plNetMsgStreamHelper::Compress(int offset) +{ + if ( !IsCompressable() ) + return true; + + plZlibCompress compressor; + UInt8* buf = (UInt8*)GetStreamBuf(); // skip creatable index + UInt32 bufLen = GetStreamLen(); + UInt32 uncompressedSize = bufLen; + SetUncompressedSize( uncompressedSize ); + if ( compressor.Compress( &buf, &bufLen, offset) ) + { + SetCompressionType( plNetMessage::kCompressionZlib ); + SetStreamLen(bufLen); + SetStreamBuf(buf); + Int32 diff = uncompressedSize-bufLen; +#if 0 + plNetApp::StaticDebugMsg( "\tCompressed stream: %lu->%lu bytes, (%s %d bytes, %.1f%%)", + uncompressedSize, bufLen, (diff>=0)?"shrunk":"GREW?!?", diff, (diff/(float)uncompressedSize)*100 ); +#endif + return true; + } + else + { + hsAssert( false, "plNetMsgStreamHelper: Compression failed" ); + SetCompressionType( plNetMessage::kCompressionFailed ); + return false; + } +} + +bool plNetMsgStreamHelper::Uncompress(int offset) +{ + if ( !IsCompressed() ) + return true; + + UInt32 origLen = GetStreamLen(); + plZlibCompress compressor; + UInt8* buf = (UInt8*)GetStreamBuf(); + UInt32 bufLen = origLen; + if ( compressor.Uncompress( &buf, &bufLen, GetUncompressedSize(), offset ) ) + { + SetCompressionType( plNetMessage::kCompressionNone ); + SetStreamLen(bufLen); + SetStreamBuf(buf); + Int32 diff = bufLen-origLen; +#if 0 + plNetApp::StaticDebugMsg( "\tUncompressed stream: %lu->%lu bytes, (%s %d bytes, %.1f%%)", + origLen, bufLen, (diff>=0)?"grew":"SHRUNK?!?", diff, (diff/(float)bufLen)*100 ); +#endif + return true; + } + else + { + hsAssert( false, "plNetMsgStreamHelper: Uncompression failed" ); + SetCompressionType( plNetMessage::kCompressionFailed ); + return false; + } +} + +bool plNetMsgStreamHelper::IsCompressed() const +{ + return ( fCompressionType==plNetMessage::kCompressionZlib ); +} + +bool plNetMsgStreamHelper::IsCompressable() const +{ + return ( fCompressionType==plNetMessage::kCompressionNone + && fStreamLen>fCompressionThreshold ); +} + + + +//////////////////////////////////////////////////////// +// NOT A MSG +// plNetMsgObject - HELPER class +//////////////////////////////////////////////////////// + +plNetMsgObjectHelper & plNetMsgObjectHelper::operator =(const plNetMsgObjectHelper & other) +{ + fUoid = other.GetUoid(); + return *this; +} + +int plNetMsgObjectHelper::Poke(hsStream* stream, UInt32 peekOptions) +{ + fUoid.Write(stream); + + return stream->GetPosition(); +} + +int plNetMsgObjectHelper::Peek(hsStream* stream, const UInt32 peekOptions) +{ + stream->LogSubStreamStart("push me"); + fUoid.Read(stream); + stream->LogSubStreamEnd(); + return stream->GetPosition(); +} + +hsBool plNetMsgObjectHelper::SetFromKey(const plKey &key) +{ + if (!key || !key->GetName()) + return false; + + fUoid = key->GetUoid(); + + return true; +} + +void plNetMsgObjectHelper::ReadVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.Read(s); + + if (contentFlags.IsBitSet(kObjHelperUoid)) + fUoid.Read(s); +} + +void plNetMsgObjectHelper::WriteVersion(hsStream* s, hsResMgr* mgr) +{ + hsBitVector contentFlags; + contentFlags.SetBit(kObjHelperUoid); + contentFlags.Write(s); + + // kObjHelperUoid + fUoid.Write(s); +} + +//////////////////////////////////////////////////////// +// NOT A MSG +// plNetMsgObjectList - HELPER class +//////////////////////////////////////////////////////// +plNetMsgObjectListHelper::~plNetMsgObjectListHelper() +{ + Reset(); +} + +void plNetMsgObjectListHelper::Reset() +{ + int i; + for( i=0 ; iWriteSwap(num); + int i; + for( i=0 ;iPoke(stream, peekOptions); + } // for + + return stream->GetPosition(); +} + +int plNetMsgObjectListHelper::Peek(hsStream* stream, const UInt32 peekOptions) +{ + Reset(); + + stream->LogSubStreamStart("push me"); + Int16 num; + stream->LogReadSwap(&num,"ObjectListHelper Num"); + + int i; + for( i=0 ;iPeek(stream, peekOptions); + } // for + stream->LogSubStreamEnd(); + return stream->GetPosition(); +} + +//////////////////////////////////////////////////////// +// NOT A MSG +// plNetMsgMemberInfoHelper - HELPER class +//////////////////////////////////////////////////////// +plNetMsgMemberInfoHelper::plNetMsgMemberInfoHelper() +: fFlags(0) +{ +} + +int plNetMsgMemberInfoHelper::Peek(hsStream* s, const UInt32 peekOptions) +{ + s->LogSubStreamStart("push me"); + s->LogReadSwap(&fFlags,"MemberInfoHelper Flags"); + fClientGuid.Read( s, nil ); + fAvatarUoid.Read(s); + s->LogSubStreamEnd(); + return s->GetPosition(); +} + +int plNetMsgMemberInfoHelper::Poke(hsStream* s, const UInt32 peekOptions) +{ + s->WriteSwap(fFlags); + fClientGuid.Write( s, nil ); + fAvatarUoid.Write(s); + return s->GetPosition(); +} + +//////////////////////////////////////////////////////// +// NOT A MSG +// plNetMsgMemberListHelper - HELPER class +//////////////////////////////////////////////////////// +plNetMsgMemberListHelper::~plNetMsgMemberListHelper() +{ + int i; + for(i=0;iLogSubStreamStart("push me"); + stream->LogReadSwap(&numMembers,"MemberListHelper NumMembers"); + fMembers.clear(); + int i; + for(i=0;iPeek(stream, peekOptions); + AddMember(addr); + } + stream->LogSubStreamEnd(); + return stream->GetPosition(); +} + +int plNetMsgMemberListHelper::Poke(hsStream* stream, const UInt32 peekOptions) +{ + Int16 numMembers = (Int16)GetNumMembers(); + stream->WriteSwap(numMembers); + + int i; + for(i=0;iGetClientGuid()->SetClientKey(""); + fMembers[i]->GetClientGuid()->SetAccountUUID(plUUID()); + fMembers[i]->Poke(stream, peekOptions); + } + + return stream->GetPosition(); +} + + +//////////////////////////////////////////////////////// +// NOT A MSG +// plNetMsgReceiversListHelper - HELPER class +//////////////////////////////////////////////////////// + +int plNetMsgReceiversListHelper::Peek(hsStream* stream, const UInt32 peekOptions) +{ + UInt8 numIDs; + stream->LogSubStreamStart("push me"); + stream->LogReadSwap(&numIDs,"ReceiversListHelper NumIDs"); + + fPlayerIDList.clear(); + int i; + for(i=0;iLogReadSwap(&ID,"ReceiversListHelper ID"); + AddReceiverPlayerID(ID); + } + stream->LogSubStreamEnd(); + return stream->GetPosition(); +} + +int plNetMsgReceiversListHelper::Poke(hsStream* stream, const UInt32 peekOptions) +{ + UInt8 numIDs = (UInt8)GetNumReceivers(); + stream->WriteSwap(numIDs); + + int i; + for(i=0;iWriteSwap(GetReceiverPlayerID(i)); + + return stream->GetPosition(); +} + + +bool plNetMsgReceiversListHelper::RemoveReceiverPlayerID(UInt32 n) +{ + std::vector::iterator res = std::find(fPlayerIDList.begin(), fPlayerIDList.end(), n); + if (res != fPlayerIDList.end()) + { + fPlayerIDList.erase(res); + return true; + } + return false; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgHelpers.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgHelpers.h new file mode 100644 index 00000000..188eaa17 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgHelpers.h @@ -0,0 +1,327 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_NET_MSG_HELPERS_inc +#define PL_NET_MSG_HELPERS_inc + +// +// These are not messages per se, but helper classes which are used +// in to avoid multiple derivation by net messages. +// + +#include "hsTypes.h" +#include "hsUtils.h" +#include "hsStream.h" +#include "hsStlUtils.h" +#include "../pnNetCommon/pnNetCommon.h" +#include "../pnFactory/plCreatable.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/plKey.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "../plNetCommon/plClientGuid.h" +#include + +class plKey; +class hsStream; + + +//////////////////////////////////////////////////////////////////// +// plNetMsgStreamableHelper +// Will peek/poke anything derived from hsStreamable + +class plNetMsgStreamableHelper : public plCreatable +{ + hsStreamable * fObject; +public: + plNetMsgStreamableHelper():fObject(nil){} + plNetMsgStreamableHelper(hsStreamable * object):fObject(object){} + plNetMsgStreamableHelper & operator =(hsStreamable * value); + operator hsStreamable *() const { return fObject;} + operator const hsStreamable *() const { return fObject;} + CLASSNAME_REGISTER( plNetMsgStreamableHelper ); + GETINTERFACE_ANY(plNetMsgStreamableHelper, plCreatable); + void SetObject(hsStreamable * object) { fObject=object;} + hsStreamable * GetObject() const { return fObject;} + int Poke(hsStream* stream, UInt32 peekOptions=0); + int Peek(hsStream* stream, UInt32 peekOptions=0); +}; + +//////////////////////////////////////////////////////////////////// +// plNetMsgCreatableHelper +// Will peek/poke anything derived from plCreatable. +// Will create the object upon read if it hasn't been set by SetObject(). +// When helper goes away, object will be unref-ed if it was created by +// the helper, so if you GetObject() and want to keep it longer than the +// lifetime of the helper, ref it. + +class plNetMsgCreatableHelper : public plCreatable +{ + plCreatable * fCreatable; + bool fWeCreatedIt; +public: + plNetMsgCreatableHelper(plCreatable * object = nil); + ~plNetMsgCreatableHelper(); + plNetMsgCreatableHelper & operator =(plCreatable * value); + operator plCreatable*(); + operator const plCreatable*(); + CLASSNAME_REGISTER( plNetMsgCreatableHelper ); + GETINTERFACE_ANY(plNetMsgCreatableHelper, plCreatable); + void SetObject(plCreatable * object); + plCreatable * GetObject(); + int Poke(hsStream* stream, UInt32 peekOptions=0); + int Peek(hsStream* stream, UInt32 peekOptions=0); +}; + +//////////////////////////////////////////////////////////////////// +// +// Net msg helper class for a stream buffer of some type (saveState, voice, plMessage...) +// +class plNetMsgStreamHelper : public plCreatable +{ +private: + enum ContentsFlags + { + kUncompressedSize, + kStreamBuf, + kStreamLen, + kCompressionType, + }; +protected: + UInt32 fUncompressedSize; + Int16 fStreamType; // set to creatable type, not read/written, gleaned from creatable stream + UInt8* fStreamBuf; + UInt32 fStreamLen; + UInt8 fCompressionType; // see plNetMessage::CompressionType + UInt32 fCompressionThreshold; // NOT WRITTEN + + void IAllocStream(UInt32 len); + +public: + enum { kDefaultCompressionThreshold = 255 }; // bytes + + plNetMsgStreamHelper(); + virtual ~plNetMsgStreamHelper() { delete [] fStreamBuf; } + + CLASSNAME_REGISTER( plNetMsgStreamHelper ); + GETINTERFACE_ANY(plNetMsgStreamHelper, plCreatable); + + virtual int Poke(hsStream* stream, UInt32 peekOptions=0); + virtual int Peek(hsStream* stream, UInt32 peekOptions=0); + + // creatable ops + virtual void Read(hsStream* s, hsResMgr* mgr) { Peek(s); } + virtual void Write(hsStream* s, hsResMgr* mgr) { Poke(s); } + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); + + void Clear(); + + // copiers + void CopyFrom(const plNetMsgStreamHelper* other); + void CopyStream(hsStream* ssStream); // copies to fStream + void CopyStream(Int32 len, const void* buf); // copies to fStream + + // setters + void SetCompressionType(UInt8 t) { fCompressionType=t; } + void SetStreamLen(UInt32 l) { fStreamLen=l; } + void SetStreamBuf(UInt8* b) { fStreamBuf=b; } + void SetUncompressedSize(UInt32 s) { fUncompressedSize=s; } + + // Getters + UInt8 GetCompressionType() const { return fCompressionType; } + Int16 GetStreamType() const { return fStreamType; } + UInt32 GetStreamLen() const { return fStreamLen; } + UInt8* GetStreamBuf() const { return fStreamBuf; } + UInt32 GetUncompressedSize() const { return fUncompressedSize; } + + bool Compress(int offset=2 /* skip 2 bytes as creatable index */ ); + bool Uncompress(int offset=2 /* skip 2 bytes as creatable index */ ); + bool IsCompressed() const; + bool IsCompressable() const; + UInt32 GetCompressionThreshold() const { return fCompressionThreshold; } + void SetCompressionThreshold( UInt32 v ) { fCompressionThreshold=v; } +}; + +// +// Contains info about a scene object +// +class plNetMsgObjectHelper : public plCreatable +{ +private: + enum ContentFlags + { + kObjHelperUoid, + }; +protected: + // string names for debug purposes only + plUoid fUoid; + // update operator()= fxn when adding new members +public: + + plNetMsgObjectHelper() {} + plNetMsgObjectHelper(const plKey key) { SetFromKey(key); } + virtual ~plNetMsgObjectHelper() { } + CLASSNAME_REGISTER( plNetMsgObjectHelper ); + GETINTERFACE_ANY(plNetMsgObjectHelper, plCreatable); + + virtual int Poke(hsStream* stream, UInt32 peekOptions=0); + virtual int Peek(hsStream* stream, UInt32 peekOptions=0); + + plNetMsgObjectHelper & operator =(const plNetMsgObjectHelper & other); + + // setters + hsBool SetFromKey(const plKey &key); + void SetUoid(const plUoid &u) { fUoid=u; } + + // getters + const char* GetObjectName() const { return fUoid.GetObjectName(); } + UInt32 GetPageID() const { return fUoid.GetLocation().GetSequenceNumber(); } + const plUoid& GetUoid() const { return fUoid; } + + void ReadVersion(hsStream* s, hsResMgr* mgr); + void WriteVersion(hsStream* s, hsResMgr* mgr); +}; + +// +// Contains a list of info about scene objects. +// +class plNetMsgObjectListHelper : public plCreatable +{ +protected: + std::vector fObjects; +public: + plNetMsgObjectListHelper() {} + virtual ~plNetMsgObjectListHelper(); + + CLASSNAME_REGISTER( plNetMsgObjectListHelper ); + GETINTERFACE_ANY(plNetMsgObjectListHelper, plCreatable); + + virtual int Poke(hsStream* stream, UInt32 peekOptions=0); + virtual int Peek(hsStream* stream, UInt32 peekOptions=0); + + void Reset(); + int GetNumObjects() const { return fObjects.size(); } + plNetMsgObjectHelper* GetObject(int i) { return fObjects[i]; } + void AddObject(plKey key) { fObjects.push_back(TRACKED_NEW plNetMsgObjectHelper(key)); } +}; + +// +// Contains a info about a net member. +// +class plNetMsgMemberInfoHelper : public plCreatable +{ +protected: + UInt32 fFlags; + plUoid fAvatarUoid; + plClientGuid fClientGuid; +public: + plNetMsgMemberInfoHelper(); + + CLASSNAME_REGISTER( plNetMsgMemberInfoHelper ); + GETINTERFACE_ANY( plNetMsgMemberInfoHelper, plCreatable); + + virtual int Poke(hsStream* stream, UInt32 peekOptions=0); + virtual int Peek(hsStream* stream, UInt32 peekOptions=0); + + const plClientGuid * GetClientGuid() const { return &fClientGuid; } + plClientGuid * GetClientGuid() { return &fClientGuid; } + + UInt32 GetFlags() const { return fFlags; } + plUoid GetAvatarUoid() const { return fAvatarUoid; } + + void SetFlags(UInt32 v) { fFlags=v; } + void SetAvatarUoid(plUoid u) { fAvatarUoid=u; } +}; + +// +// Contains a info about a list of net members. +// This is sent from server to client. +// +class plNetMsgMemberListHelper : public plCreatable +{ +public: + typedef std::vector MemberInfoHelperVec; + struct MatchesPlayerID + { + UInt32 fID; + MatchesPlayerID( UInt32 id ): fID( id ){} + bool operator()( const plNetMsgMemberInfoHelper * mbr ) const + { + return ( mbr && mbr->GetClientGuid()->GetPlayerID()==fID ); + } + }; + +protected: + MemberInfoHelperVec fMembers; + +public: + plNetMsgMemberListHelper() {} + virtual ~plNetMsgMemberListHelper(); + + CLASSNAME_REGISTER( plNetMsgMemberListHelper ); + GETINTERFACE_ANY( plNetMsgMemberListHelper, plCreatable); + + virtual int Poke(hsStream* stream, UInt32 peekOptions=0); + virtual int Peek(hsStream* stream, UInt32 peekOptions=0); + + int GetNumMembers() const { return fMembers.size(); } + const plNetMsgMemberInfoHelper* GetMember(int i) const { return fMembers[i]; } + void AddMember(plNetMsgMemberInfoHelper* a) { fMembers.push_back(a); } + const MemberInfoHelperVec * GetMembers() const { return &fMembers;} +}; + + +///////////////////////////////////////////////////////////////// + +// +// Contains a list of other players (members). +// This is commonly used to route p2p msgs to groups of players. +// Sent client to server. +// +class plNetMsgReceiversListHelper : public plCreatable +{ +protected: + std::vector fPlayerIDList; +public: + plNetMsgReceiversListHelper() {} + virtual ~plNetMsgReceiversListHelper() {} + + CLASSNAME_REGISTER( plNetMsgReceiversListHelper ); + GETINTERFACE_ANY( plNetMsgReceiversListHelper, plCreatable); + + virtual int Poke(hsStream* stream, UInt32 peekOptions=0); + virtual int Peek(hsStream* stream, UInt32 peekOptions=0); + + void Clear() { fPlayerIDList.clear(); } + int GetNumReceivers() const { return fPlayerIDList.size(); } + UInt32 GetReceiverPlayerID(int i) const { return fPlayerIDList[i]; } + void AddReceiverPlayerID(UInt32 a) { fPlayerIDList.push_back(a); } + bool RemoveReceiverPlayerID(UInt32 n); // returns true if found and removed +}; + + +#endif // PL_NET_MSG__HELPERS_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgVersion.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgVersion.h new file mode 100644 index 00000000..8249416b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetMessage/plNetMsgVersion.h @@ -0,0 +1,145 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetMsgVersion_h_inc +#define plNetMsgVersion_h_inc + + +// Changing the version number(s)? Make an entry in the corresponding log below. +#define PLASMA2_NETMSG_MAJOR_VERSION 12 +#define PLASMA2_NETMSG_MINOR_VERSION 6 +/*--- Major Version Log --- + # Date Who Comment + 2 10/05/01 eap Moved handling of VaultRequestData message from game server to lobby server + 3 10/09/01 ee Made Uoid changes that impact the net messages + 4 10/11/01 ee Made Uoid changes that impact the net messages + 5 02/12/02 eap Modified auth messages. + 6 4/18/02 MT Changed to using SDL saveStates + 7 04/18/02 eap Redesigned KI messaging + 8 05/17/02 Colin Changed format of Uoid + 9 07/01/02 rje Changed Authentication Scheme + 10 10/16/02 eap Removed low-level KI. Replaced with plVault. + 11 07/03/03 MT Optimized plNetMessage headers for size + 12 09/17/03 eap Removed UInt32 acctID. Added Uuid acctUUID. Changed PlayerUpdate enum values. +*/ + + +/*--- Minor Version Log --- + # Date Who Comment + 5 9/28/01 MT Added senderClientNum to GameMessages (mostly for debugging) + 0 10/05/01 eap Reset on Major Version change + 1 10/26/01 eap Upon sending join ack, the game server now sends a set of initial local unique ids to client. + 2 10/31/01 eap Added (Un)RegisterServer msgs (actually renamed from ServerStarted family msgs). Changed StartProcess msg a little. + 3 11/05/01 eap Changes to request/receive avatar msgs. + 4 11/21/01 MT Removed obsolete plNetMessage flags due to client task reorg + 5 02/02/02 eap Added/Updated KI message classes. + 0 02/12/02 eap Reset on major version change + 1 03/22/02 eap Removed acctID from plNetMsgCreatePlayer. + 2 3/31/02 MT moved uncompressed size from voiceMsg to streamHelper + 0 04/18/02 MT Reset on major version change. + 0 04/18/02 eap Reset on major version change + 1 4/29/02 MT added joinOrder to joinAck + 2 05/02/02 eap Changed plNetServerSessionInfo stream format. + 3 05/08/02 eap Changed KI stream formats. + 0 05/17/02 Colin Reset on major version change + 1 06/03/02 eap Changed KIOperations enum values. Affects KI messages. + 2 06/06/02 eap More changes to KI message format. + 3 6/07/02 MT Enabled compression on SDL msgs + 4 6/19/02 MT Removed general timeOffset in favor of UnifiedTime in game msg + 4 06/12/02 eap Yet more changes to KI messaging stuff + 1 7/01/02 MT Added a member to SDLBCast msgs + 2 07/16/02 eap Added linking rule info to net msgs related to age linking. + 3 08/15/02 eap Changed plNetMsgKI format. + 4 8/15/02 MT Changes related to cloning reorg + 5 08/21/02 eap Changed plNetMsgKI format. + 6 9/17/02 MT Added fIsPlayer to plNetMsgLoadClone + 7 9/24/02 rje Added Packet Size in Client Hello + 8 10/01/02 eap Changed the way the KI is fetched. + 9 10/02/02 eap Changed KI storable stream format (made flag-based instead of stream version-based) + 10 10/03/02 eap Changed KI manifest stream format. + 11 10/04/02 thamer Changed timeSent to be unified time not double, short-circuit version checking + 0 10/16/02 eap Reset on major version change. + 1 10/15/02 thamer minor changes for CCR + 2 11/04/02 eap Changed plNetMsgVault format. + 3 12/04/02 eap Moved compression into plNetMsgStreamHelper. Changed plNetMsgVault format. + 4 12/04/02 eap Changed plNetMsgStreamHelper fUncompressedSize type to UInt32. + 5 12/05/02 eap Added PlayerName and AvatarShape to CreatePlayer msg. + 6 12/11/02 thamer Moved PlayerID into the base class + 7 12/18/02 thamer Changed SDL format + 8 12/17/02 eap Changed format of vault negotiate manifest msg. + 9 01/14/03 eap Added CCRLevel to plClientGuid and plNetMsgSetMyActivePlayer. Removed from plNetMsgJoinReq + 10 01/30/03 eap Changed linking rules and associated net msgs. + 11 02/04/03 eap Changed vault msg format. + 12 02/05/03 thamer Added initial age state to joinAck + 13 02/10/03 eap Changed format of vault FetchNodes msg to support bundling of multiple nodes into one msg. + 14 02/12/03 eap Changed the way ages are (un)registered. client used to do it. now vault server does it. + 15 02/24/03 eap Added a byte to plNetMsgLeave to specify the reason for leaving. + 16 02/25/03 thamer Changed the auth response generation + 17 02/26/03 thamer again + 18 02/28/03 eap Support for multiple spawn points for vault age link nodes. + 19 03/14/03 eap Changed plVaultNode format. + 20 03/14/03 thamer Added buildType and 'experimental' values to authHello and JoinAck msgs + 21 03/17/03 thamer Added streamSubType var to StreamHelper + 22 03/21/03 eap Changed auth error enum values to be negative. + 23 03/24/03 rje Added Invites to CreatePlayer. + 24 04/11/03 eap Changed create player error enum values to be negative. + 25 04/14/03 thamer Changed SharedState R/W format + 26 05/13/03 eap Changed plNetMsgVault a little bit to allow multiple age vaults to live in one process. + 27 05/16/03 thamer Bob changed the LoadClone msg format + 28 05/30/03 thamer Optimized the Uoid read/write format + 29 06/01/03 eap Changed stream format of plGenericType class + 30 06/01/03 eap Changed stream format of plGenericType class + 31 06/02/03 thamer Changed plNetMsgLoadClone format + 32 06/06/03 eap Reimplemented inter-age messaging. Removed vaultserver from the process. + 33 06/10/03 eap Changed plVaultNode stream format + 34 06/24/03 eap Client is now in charge of creating personal age when needed. + 35 06/27/03 eap Added reply msg to SetActivePlayer + 36 06/25/03 thamer SDL size optimizations + 37 07/01/03 eap Vault db version bumped. + 0 07/03/03 thamer Reset on major version change. + 1 07/11/03 eap Added fCreateFlags to CreatePlayer msg. + 2 07/16/03 thamer Added flags to vault player list desc + 3 07/22/03 eap Changed plNetMsgVault and plNetMsgVaultTask format. + 4 07/23/03 eap Changed plNetMsgDeletePlayer format. + 5 07/28/03 thamer Changed StreamHelper format. + 6 08/01/03 eap Changed the format of some vault operations (RegisterOwnedAge et.al.) + 7 08/01/03 eap Added disconnect reply msg to vault protocol. + 8 08/06/03 eap Added some buffer room to the last enum value in plNetMsgTerminated/Leave/ServerMsgUpdatePlayer + 9 08/07/03 eap Fixed enum values in plNetMsgTerminated/Leave/ServerMsgUpdatePlayer + 10 08/22/03 eap Game server no longer queries auth server when authenticating a client. + 11 09/04/03 eap Added camera stack to plSpawnPointInfo. + 12 09/08/03 eap Added server guid to plNetMsgAuthenticated + 13 09/17/03 bob Changed format of Read/WriteSafeString, (and the "long" versions) + 0 09/17/03 eap Reset on major version change. + 1 10/22/03 eap Changed format of VaultFetchNodes message + 2 10/23/03 eap Changed format of VaultFetchNodes message again + 3 10/25/03 bob Changed the format of plLinkEffectsTriggerMsg, which the NetMsgScreener reads. + 4 11/18/03 eap Changed c/s initial SDL state send transaction. + 5 10/29/03 jeffrey Changed the plDynamicTextMsg to use unicode + 6 12/01/03 eap Changed plNetMessage flags (kNoGameTimeSent became kTimeSent) +*/ + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransport.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransport.cpp new file mode 100644 index 00000000..d56fcf42 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransport.cpp @@ -0,0 +1,355 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#pragma warning(disable: 4786) + +#include "hsTimer.h" +#include "plNetTransport.h" +#include "plNetTransportMember.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plNetClient/plNetClientMgr.h" +#include + +plNetTransport::~plNetTransport() +{ + ClearMembers(); +} + +// +// add a member to the master list if not already there +// +int plNetTransport::AddMember(plNetTransportMember* mbr) +{ + if (FindMember(mbr)==-1) + { + fMembers.push_back(mbr); + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg("Adding member %s", mbr->AsStdString().c_str()) ); + plNetClientMgr::GetInstance()->GetListenList()->AddMember(mbr); + plNetClientMgr::GetInstance()->GetTalkList()->AddMember(mbr); + DumpState(); + return fMembers.size()-1; + } + return -1; +} + +void plNetTransport::IUnSubscribeToAllChannelGrps(plNetTransportMember* mbr) +{ + int i; + for( i=mbr->GetNumSubscriptions()-1; i>=0 ; i-- ) + { + int chan=mbr->GetSubscription(i); + hsBool ok=UnSubscribeToChannelGrp(mbr, chan); + hsAssert(ok, "can't find supposed subscription to remove"); + } // for +} + +void plNetTransport::IRemoveMember(plNetTransportMember* mbr) +{ + if (!mbr) + return; + + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg("Removing member %s", mbr->AsStdString().c_str()) ); + +// plNetClientMgr::GetInstance()->GetNetCore()->RemovePeer(mbr->GetPeerID()); + plNetClientMgr::GetInstance()->GetTalkList()->RemoveMember(mbr); + plNetClientMgr::GetInstance()->GetListenList()->RemoveMember(mbr); + + // remove member from subscription lists + IUnSubscribeToAllChannelGrps(mbr); + + plMembersList::iterator it=std::find(fMembers.begin(),fMembers.end(),mbr); + + // remove member from master list + fMembers.erase(it); + + hsLogEntry( plNetClientMgr::GetInstance()->DebugMsg("Done Removing member %s", mbr->AsStdString().c_str()) ); + DumpState(); + + delete mbr; +} + +// +// remove member from master list, and all subscription channels. +// return true on success. +// +hsBool plNetTransport::RemoveMember(int idx) +{ + if (idx>=0) + { + plNetTransportMember* mbr=GetMember(idx); + IRemoveMember(mbr); + return true; + } + return false; +} + +// +// remove member from master list, and all subscription channels. +// return true on success. +// +hsBool plNetTransport::RemoveMember(plNetTransportMember* mbr) +{ + IRemoveMember(mbr); + return true; +} + +// +// return array index or -1 +// +int plNetTransport::FindMember(const plNetTransportMember* mbr) +{ + plMembersList::iterator it = std::find(fMembers.begin(), fMembers.end(), mbr); + return (it==fMembers.end()) ? -1 : (it-fMembers.begin()); +} + + +// +// add this member to the given channel grp +// +void plNetTransport::SubscribeToChannelGrp(plNetTransportMember* mbr, int channel) +{ +// hsAssert(FindMember(mbr) != -1, "must add member before subscribing to channel"); + if (mbr->AddSubscription(channel)) + { + hsAssert(channel=0 && chanbegin(), mList->end(), mbr); + if (it != mList->end()) + { + mList->erase(it); + hsBool ret=mbr->RemoveSubscription(chan); + hsAssert(ret, "error removing subscription"); + return true; + } + return false; +} + +// +// copy list of members channelGrp subscriptions +// +void plNetTransport::GetSubscriptions(plNetTransportMember* mbr, std::vector* channels) const +{ + mbr->CopySubscriptions(channels); +} + + +// +// Send Msg to all members in the given channelGrp. +// Here's where multicasting would be used. +// Returns neg number (NetCore::RetCode) on send error, 1, if not sent, and 0 if sent +// +int plNetTransport::SendMsg(int chan, plNetMessage* netMsg) const +{ + NetCommSendMsg(netMsg); + return hsOK; + + plNetClientMgr* nc=plNetClientMgr::GetInstance(); + int ret=1; // didn't send + + if (chan < fChannelGroups.size()) + { + const plMembersList* mList = &fChannelGroups[chan]; + + // does this msg have a list of receivers + plNetMsgReceiversListHelper* rl = plNetMsgReceiversListHelper::ConvertNoRef(netMsg); + +#if 0 + // send msg to all subscribers to this channel + int size=mList->size(); + for( int i=0 ; iGetPeerID(); +// hsAssert(peerID>=0, "queing message to invalid peer"); + +// if ((ncRet=nc->GetNetClientComm().SendMsg(netMsg, peerID, sendFlags, msgSize)) != plNetCore::kNetOK) + + NetCommSendMsg(netMsg); + if (rl) + { + hsBool ok=rl->RemoveReceiverPlayerID(tm->GetPlayerID()); + hsAssert(ok, "couldn't find rcvr to remove?"); + } + ret=0; // sent ok + } // for +#endif + + // if there are rcvrs left that we couldn't send to, send via server + if (rl && rl->GetNumReceivers()) + { +// if ((ncRet=nc->GetNetClientComm().SendMsg(netMsg, nc->GetServerPeerID(), sendFlags, msgSize)) != plNetCore::kNetOK) + NetCommSendMsg(netMsg); + ret=0; // sent + } + } + else + { + hsStatusMessage("EMPTY TRANSPORT GROUP\n"); + } + return ret; +} + + +void plNetTransport::ClearMembers() +{ + int i; + for( i=0 ;iGetPlayerID()==playerID) + return i; + } + return -1; +} + +// +// return array index or -1 +// +int plNetTransport::FindMember(const plKey avKey) const +{ + int i; + for( i=0 ;iGetAvatarKey()==avKey) + return i; + } + return -1; +} + +// +// clear channel and unsubscribe all members to that channel +// +void plNetTransport::ClearChannelGrp(int channel) +{ + const plMembersList* mList = &fChannelGroups[channel]; + int i, size=mList->size(); + for( i=0 ; iRemoveSubscription(channel); + hsAssert(ok, "error removing subscription"); + } + + fChannelGroups[channel].clear(); +} + +void plNetTransport::DumpState() +{ + plNetClientMgr* nc=plNetClientMgr::GetInstance(); + + hsLogEntry( nc->DebugMsg("-------------------\n") ); + hsLogEntry( nc->DebugMsg("Num Channels=%d\n", fChannelGroups.size()) ); + + int i; + for(i=0;iDebugMsg("\tChannel %d, num mbrs=%d\n", i, mList->size()) ); + int j; + for(j=0; jsize();j++) + { + plNetTransportMember * mbr = (*mList)[j]; + hsLogEntry( nc->DebugMsg("\t\tMbr %s\n",(*mList)[j]->AsStdString().c_str()) ); + } + } + + nc->DebugMsg("Num Mbrs=%d\n", GetNumMembers()); + for(i=0;iDebugMsg("\tMbr %d, name=%s, plyrID=%lu, subs=%d", + i,mbr->AsStdString().c_str(),mbr->GetPlayerID(),mbr->GetNumSubscriptions()) ); + int j; + for(j=0;jGetNumSubscriptions();j++) + { + hsLogEntry( nc->DebugMsg("\t\tSub %d, chan=%d\n", j, mbr->GetSubscription(j)) ); + } + } + hsLogEntry( nc->DebugMsg("\n") ); +} + +void plNetTransport::SetNumChannels(int n) +{ + if (n>fChannelGroups.size()) + fChannelGroups.resize(n); +} + + +int compare( const void* arg1, const void *arg2 ) +{ + plNetTransportMember** m1 = (plNetTransportMember**)arg1; + plNetTransportMember** m2 = (plNetTransportMember**)arg2; + float d1=m1 ? (*m1)->GetDistSq() : hsScalarMax; + float d2=m2 ? (*m2)->GetDistSq() : hsScalarMax; + return (int)(d1-d2); +} + +// +// create a members list sorted by dist. +// caller must delete this when done +// +void plNetTransport::GetMemberListDistSorted(plNetTransportMember**& listIn) const +{ + // copy members list + listIn = TRACKED_NEW plNetTransportMember* [fMembers.size()]; + int i; + for (i=0; i. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetTransport_h +#define plNetTransport_h + +#include "hsTypes.h" +#include "hsStlUtils.h" + +// +// The transport class handles the details of sending net msgs to +// the server or to groups of clients (members). +// It hides whether we are playing in P2P mode or C/S mode. +// It is currently used only clientside but is general enough to +// be used by a server as well. +// +class plKey; +class plNetTransportMember; +typedef std::vector plMembersList; +class plNetMessage; +class plNetTransport +{ +private: + plMembersList fMembers; // master list of all members in the game, server is member[0] + std::vector fChannelGroups; // members grouped by channel + + void IUnSubscribeToAllChannelGrps(plNetTransportMember* mbr); + void IRemoveMember(plNetTransportMember* mbr); +public: + plNetTransport() {} + ~plNetTransport(); + + void DumpState(); + + // master list ops + void GetMemberListDistSorted(plNetTransportMember**& listPtr) const; // allocates and sorts array + int FindMember(const plKey avKey) const; // return array index or -1 + int FindMember(UInt32 playerID) const; // return array index or -1 + int FindMember(const plNetTransportMember* mbr); // return array index or -1 + int AddMember(plNetTransportMember* mbr); // to master list, if not there + hsBool RemoveMember(plNetTransportMember* mbr); // from master list and all channels + hsBool RemoveMember(int idx); // from master list and all channels + int GetNumMembers() const { return fMembers.size(); } + plNetTransportMember* GetMember(int i) const { return i>=0 && i* channels) const; + void ClearChannelGrp(int channel); + void SetNumChannels(int n); + + int SendMsg(int channel, plNetMessage* msg) const; +}; + +#endif // plNetTransport_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransportMember.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransportMember.cpp new file mode 100644 index 00000000..b9c4be6d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransportMember.cpp @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "plNetTransportMember.h" + +// +// add a channel subscription if it's not already there +// returns true if added +// +hsBool plNetTransportMember::AddSubscription(int chan) +{ + if (FindSubscription(chan)==-1) + { + fSubscriptions.push_back(chan); + return true; + } + return false; +} + +hsBool plNetTransportMember::RemoveSubscription(int chan) +{ + int idx=FindSubscription(chan); + if (idx>=0) + { + fSubscriptions.erase(fSubscriptions.begin()+idx); + return true; + } + return false; +} + +int plNetTransportMember::FindSubscription(int chan) +{ + std::vector::iterator it=std::find(fSubscriptions.begin(), fSubscriptions.end(), chan); + return (it==fSubscriptions.end()) ? -1 : (it-fSubscriptions.begin()); +} + +std::string plNetTransportMember::AsStdString() const +{ + if (IsServer()) + { + return "(server)"; + } + else + { + return GetPlayerName(); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransportMember.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransportMember.h new file mode 100644 index 00000000..495a97e9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plNetTransport/plNetTransportMember.h @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNetTransportMember_h +#define plNetTransportMember_h + +#include "hsConfig.h" +#include "hsStlUtils.h" +#include "../plNetCommon/plNetMember.h" +#include "../pnKeyedObject/plKey.h" + +// +// This represents a participant in the game, ie. another +// remote user. +// It is a basic net member with a list of channels that it subscribes to. +// +class plKey; + +class plNetTransportMember : public plNetMember +{ +public: + enum TransportFlags + { + kSendingVoice = 1<<0, + kSendingActions = 1<<1, + }; +protected: + plKey fAvatarKey; + std::string fPlayerName; + UInt32 fPlayerID; + std::vector fSubscriptions; // list of channelGrp subscriptions + UInt32 fTransportFlags; + float fDistSq; // from local player, temp + UInt8 fCCRLevel; +public: + CLASSNAME_REGISTER( plNetTransportMember); + GETINTERFACE_ANY( plNetTransportMember, plNetMember); + + plNetTransportMember() : fAvatarKey(nil), + fTransportFlags(0),fPlayerID(0),fDistSq(-1),fCCRLevel(0) {} + plNetTransportMember(plNetApp* na) : plNetMember(na), + fAvatarKey(nil),fTransportFlags(0),fPlayerID(0),fDistSq(-1),fCCRLevel(0) {} + ~plNetTransportMember() {} + + void SetDistSq(float s) { fDistSq=s; } + float GetDistSq() const { return fDistSq; } + + plKey GetAvatarKey() { return fAvatarKey; } + void SetAvatarKey(plKey k) + { + fAvatarKey=k; + } + void SetPlayerName(const char * value) { fPlayerName=value;} + const char * GetPlayerName() const { return fPlayerName.c_str();} + void SetPlayerID(UInt32 value) { fPlayerID=value;} + UInt32 GetPlayerID() const { return fPlayerID;} + void SetIsServer(bool value) { (value)?SetFlags(GetFlags()|kIsServer):SetFlags(GetFlags()&!kIsServer);} + bool IsServer() const { return (GetFlags()&kIsServer)?true:false;} + + hsBool AddSubscription(int chan); + hsBool RemoveSubscription(int chan); // return true on success + int FindSubscription(int chan); // return index into subscription array or -1 + + int GetNumSubscriptions() { return fSubscriptions.size(); } + int GetSubscription(int i) { return fSubscriptions[i]; } + + void CopySubscriptions(std::vector* channels) { *channels = fSubscriptions; } + + void SetTransportFlags(UInt32 f) { fTransportFlags=f; } + UInt32 GetTransportFlags() const { return fTransportFlags; } + + bool IsPeerToPeer() const { return hsCheckBits(fFlags, kRequestP2P); } + std::string AsStdString() const; + bool IsEqualTo(const plNetMember * other) const + { + const plNetTransportMember * o = plNetTransportMember::ConvertNoRef(other); + if (!o) return false; + return o->fPlayerID==fPlayerID; + } + + bool IsCCR() const { return (fCCRLevel>0); } + UInt8 GetCCRLevel() const { return fCCRLevel; } + void SetCCRLevel(UInt8 cl) { fCCRLevel=cl; } +}; + +#endif // plNetTransportMember_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plBoundInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plBoundInterface.cpp new file mode 100644 index 00000000..283a523a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plBoundInterface.cpp @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsGeometry3.h" +#include "plBoundInterface.h" +#include "plConvexVolume.h" +#include "hsResMgr.h" + +plBoundInterface::plBoundInterface() : fBounds(nil) +{ +} + +plBoundInterface::~plBoundInterface() +{ + ReleaseData(); +} + +void plBoundInterface::ReleaseData() +{ + delete fBounds; + fBounds = nil; +} + +void plBoundInterface::Init(plConvexVolume *bounds) +{ + ReleaseData(); + fBounds = bounds; +} + +// Right now, this is ignoring the enabled property of ObjInterface, since I'm not aware that +// anything ever makes use of it (and if nothing does, this saves us on some needless matrix +// copying). Should we make use of the disabled prop, this function should just store the l2w +// matrix, but not send an update to fBounds. +void plBoundInterface::SetTransform(const hsMatrix44 &l2w, const hsMatrix44&w2l) +{ + if (fBounds != nil) + fBounds->Update(l2w); +} + +void plBoundInterface::Read(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Read(s, mgr); + fBounds = plConvexVolume::ConvertNoRef(mgr->ReadCreatable(s)); + //mgr->ReadKeyNotifyMe(s, new plIntRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plIntRefMsg::kOwner), plRefFlags::kPassiveRef); +} + +void plBoundInterface::Write(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Write(s, mgr); + mgr->WriteCreatable(s, fBounds); + //mgr->WriteKey(s, fBounds); +} + +// No need to save/load. The coordinate interface on our sceneObject will update us. \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plBoundInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plBoundInterface.h new file mode 100644 index 00000000..8b706f52 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plBoundInterface.h @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plBoundInterface_inc +#define plBoundInterface_inc + +#include "../pnSceneObject/plObjInterface.h" + +class plConvexVolume; +struct hsMatrix44; + +class plBoundInterface : public plObjInterface +{ + enum { + kDisable = 0x0, + + kNumProps // last + }; + +protected: + //hsMatrix44 fLocalToWorld; + plConvexVolume *fBounds; + +public: + plBoundInterface(); + ~plBoundInterface(); + + void Init(plConvexVolume *bounds); + plConvexVolume *GetVolume() { return fBounds; } + + CLASSNAME_REGISTER( plBoundInterface ); + GETINTERFACE_ANY( plBoundInterface, plObjInterface ); + + virtual Int32 GetNumProperties() const { return kNumProps; } + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + virtual void ReleaseData(); +}; + + + +#endif // plBoundInterface_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plConvexVolume.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plConvexVolume.cpp new file mode 100644 index 00000000..be14612b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plConvexVolume.cpp @@ -0,0 +1,193 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsGeometry3.h" +#include "hsMatrix44.h" +#include "plConvexVolume.h" +#include "hsStream.h" + +plConvexVolume::plConvexVolume() +{ + //fFlags = nil; + fLocalPlanes = nil; + fWorldPlanes = nil; + fNumPlanes = 0; +} + +plConvexVolume::~plConvexVolume() +{ + IClear(); +} + +void plConvexVolume::IClear() +{ + //delete [] fFlags; + delete [] fLocalPlanes; + delete [] fWorldPlanes; +} + +hsBool plConvexVolume::AddPlane(const hsPlane3 &plane) +{ + // First check for a redundant plane (since we're convex, a comparison of normals should do) + int i; + // Start the comparison with the most recently added plane, it's most likely to match + for (i = fNumPlanes - 1; i >= 0; i--) + { + const float MIN_COS_THETA = 0.99999f; // translates to < 0.25 degree angle + // If the angle betwen the normals is close enough, count them as equal. + if (fLocalPlanes[i].fN.InnerProduct(plane.fN) >= MIN_COS_THETA) + return false; // no need to add it + } + fNumPlanes++; + //delete [] fFlags; + //fFlags = TRACKED_NEW UInt32[fNumPlanes]; + + hsPlane3 *tempPlanes = TRACKED_NEW hsPlane3[fNumPlanes]; + for (i = 0; i < fNumPlanes - 1; i++) + { + tempPlanes[i] = fLocalPlanes[i]; + } + tempPlanes[fNumPlanes - 1] = plane; + + delete [] fLocalPlanes; + fLocalPlanes = tempPlanes; + delete [] fWorldPlanes; + fWorldPlanes = TRACKED_NEW hsPlane3[fNumPlanes]; + + return true; +} + +void plConvexVolume::Update(const hsMatrix44 &l2w) +{ + int i; + hsPoint3 planePt; + for (i = 0; i < fNumPlanes; i++) + { + // Since fN is an hsVector3, it will only apply the rotational aspect of the transform... + fWorldPlanes[i].fN = l2w * fLocalPlanes[i].fN; + planePt.Set(&(fLocalPlanes[i].fN * fLocalPlanes[i].fD)); + fWorldPlanes[i].fD = -(l2w * planePt).InnerProduct(fWorldPlanes[i].fN); + } +} + +void plConvexVolume::SetNumPlanesAndClear(const UInt32 num) +{ + IClear(); + //fFlags = TRACKED_NEW UInt32[num]; + fLocalPlanes = TRACKED_NEW hsPlane3[num]; + fWorldPlanes = TRACKED_NEW hsPlane3[num]; + fNumPlanes = num; +} + +void plConvexVolume::SetPlane(const hsPlane3 &plane, const UInt32 index) +{ + fLocalPlanes[index] = plane; +} + +hsBool plConvexVolume::IsInside(const hsPoint3 &pos) const +{ + int i; + for( i = 0; i < fNumPlanes; i++ ) + { + if (!TestPlane(pos, fWorldPlanes[i])) + return false; + } + + return true; +} + +hsBool plConvexVolume::ResolvePoint(hsPoint3 &pos) const +{ + hsScalar minDist = 1.e33f; + Int32 minIndex = -1; + + hsScalar currDist; + int i; + for (i = 0; i < fNumPlanes; i++) + { + currDist = -fWorldPlanes[i].fD - fWorldPlanes[i].fN.InnerProduct(pos); + if (currDist < 0) + return false; // We're not inside this plane, and thus outside the volume + + if (currDist < minDist) + { + minDist = currDist; + minIndex = i; + } + } + pos += (-fWorldPlanes[minIndex].fD - fWorldPlanes[minIndex].fN.InnerProduct(pos)) * fWorldPlanes[minIndex].fN; + return true; +} + +hsBool plConvexVolume::BouncePoint(hsPoint3 &pos, hsVector3 &velocity, hsScalar bounce, hsScalar friction) const +{ + hsScalar minDist = 1.e33f; + Int32 minIndex = -1; + + hsScalar currDist; + int i; + for (i = 0; i < fNumPlanes; i++) + { + currDist = -fWorldPlanes[i].fD - fWorldPlanes[i].fN.InnerProduct(pos); + if (currDist < 0) + return false; // We're not inside this plane, and thus outside the volume + + if (currDist < minDist) + { + minDist = currDist; + minIndex = i; + } + } + pos += (-fWorldPlanes[minIndex].fD - fWorldPlanes[minIndex].fN.InnerProduct(pos)) * fWorldPlanes[minIndex].fN; + hsVector3 bnc = -velocity.InnerProduct(fWorldPlanes[minIndex].fN) * fWorldPlanes[minIndex].fN; + velocity += bnc; + velocity *= 1.f - friction; + velocity += bnc * bounce; +// velocity += (velocity.InnerProduct(fWorldPlanes[minIndex].fN) * -(1.f + bounce)) * fWorldPlanes[minIndex].fN; + return true; +} + +void plConvexVolume::Read(hsStream* s, hsResMgr *mgr) +{ + SetNumPlanesAndClear(s->ReadSwap32()); + int i; + for (i = 0; i < fNumPlanes; i++) + { + fLocalPlanes[i].Read(s); + //fFlags[i] = s->ReadSwap32(); + } +} + +void plConvexVolume::Write(hsStream* s, hsResMgr *mgr) +{ + s->WriteSwap32(fNumPlanes); + int i; + for (i = 0; i < fNumPlanes; i++) + { + fLocalPlanes[i].Write(s); + //s->WriteSwap32(fFlags[i]); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plConvexVolume.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plConvexVolume.h new file mode 100644 index 00000000..318cb933 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plConvexVolume.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plConvexVolume_inc +#define plConvexVolume_inc + +#include "../pnSceneObject/plObjInterface.h" + +struct hsPlane3; +struct hsPoint3; +struct hsMatrix44; +class hsResMgr; + +// A convex volume defined by several boundary planes +// For now it assumes the user won't add planes that make it concave + +class plConvexVolume : public plCreatable +{ +public: + plConvexVolume(); + ~plConvexVolume(); + + CLASSNAME_REGISTER( plConvexVolume ); + GETINTERFACE_ANY( plConvexVolume, plCreatable ); + + void Update(const hsMatrix44 &l2w); + + hsBool AddPlane(const hsPlane3 &plane); + void SetNumPlanesAndClear(const UInt32 num); + void SetPlane(const hsPlane3 &plane, const UInt32 index); + + // If you only care about the test, call this. Otherwise call ResolvePoint. + hsBool IsInside(const hsPoint3 &pos) const; + + // returns true if the point was inside the volume, and thus moved outward. + hsBool ResolvePoint(hsPoint3 &pos) const; + + // returns true if the point was inside and pos and velocity updated to bounce off offending plane. + // input bounce==1.f for perfect bounce, bounce==0 to slide. + hsBool BouncePoint(hsPoint3 &pos, hsVector3 &velocity, hsScalar bounce, hsScalar friction) const; + + inline hsBool TestPlane(const hsPoint3 &pos, const hsPlane3 &plane) const; // Is the point inside the plane? + virtual void Read(hsStream* s, hsResMgr *mgr); + virtual void Write(hsStream* s, hsResMgr *mgr); + //virtual hsBool MsgReceive(plMessage* msg); + +protected: + void IClear(); + + hsPlane3 *fLocalPlanes; + hsPlane3 *fWorldPlanes; + UInt32 fNumPlanes; +}; + +inline hsBool plConvexVolume::TestPlane(const hsPoint3 &pos, const hsPlane3 &plane) const +{ + hsScalar dis = plane.fN.InnerProduct(pos); + dis += plane.fD; + if( dis >= 0.f ) + return false; + + return true; +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plEffectTargetInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plEffectTargetInfo.h new file mode 100644 index 00000000..f052a007 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plEffectTargetInfo.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plEffectTargetInfo_inc +#define plEffectTargetInfo_inc + +#include "hsTypes.h" + +struct hsPoint3; +class plPipeline; +class plParticleSystem; + +// This is the rendering context passed into an effect to let it cache up +// anything it needs to compute that will be the same for all particles. +// Not a lot of context to go on to begin with, but this will let that +// expand without any interface changes. +class plParticleContext +{ +public: + plPipeline* fPipeline; + plParticleSystem* fSystem; + double fSecs; + hsScalar fDelSecs; +}; + + +// This is just a collection of arrays and strides that a plParticleEffect object will reference and modify +// in the course of doing its job. + +class plEffectTargetInfo +{ +public: + // Byte arrays. Declared as type UInt8 so that adding the stride to the pointer is guaranteed to advance + // the exact number of bytes. + UInt8 *fPos; + UInt8 *fVelocity; + UInt8 *fInvMass; + UInt8 *fAcceleration; + UInt8 *fColor; + UInt8 *fRadsPerSec; + UInt8 *fMiscFlags; + + UInt32 fPosStride; + UInt32 fVelocityStride; + UInt32 fInvMassStride; + UInt32 fAccelerationStride; + UInt32 fColorStride; + UInt32 fRadsPerSecStride; + UInt32 fMiscFlagsStride; + + plParticleContext fContext; + UInt32 fNumValidParticles; + UInt32 fFirstNewParticle; + + // We're going to need some sort of connectivity data for constraint satisfaction, but at least we have + // a system that allows that to be added in smoothly when it's needed, so for now, let's get the main + // goop working. +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticle.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticle.h new file mode 100644 index 00000000..1a922090 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticle.h @@ -0,0 +1,98 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plParticle_inc +#define plParticle_inc + +#include "hsGeometry3.h" +#include "../CoreLib/hsColorRGBA.h" + +// The meat of the particle. These classes, in combination with the plParticleEmitter that spawned it, +// should contain everything specific to a particle, necessary to build a renderable poly to represent a +// particular particle. (The emitter is necessary for properties (like texture) that are common among all +// particles that originated from the same emitter. + +// For any reference in this object to a particle's poly vertices, the structure is as follows: +/* + + |---| "HSize" + + V3-----V2 - + | / | | "VSize" + | / | | + | P | - + | / | + | / | ("P" is the current position of the particle) + V0-----V1 + + So the vertices are arranged counter-clockwise, starting in the lower-left corner. Order all other attributes + accordingly. + +*/ + +// The class plParticleCore should ONLY contain data necessary for the Drawable to create renderable polys +// Everything else goes into plParticleExt. + +// plParticleEmitter is depending on the order that member variables appear in these classes, so +// DON'T MODIFY THEM WITHOUT MAKING SURE THE CONSTRUCTOR TO plParticleEmitter PROPERLY COMPUTES +// BASE ADDRESSES AND STRIDES! + +// No initialization on construct. In nearly all cases, a default value won't be appropriate +// so there's no sense doing extra memory writes + +class plParticleCore +{ +public: + hsPoint3 fPos; + UInt32 fColor; // Particle opacity goes into the color's alpha. + hsPoint3 fOrientation; // fMiscFlags determines how this should be used. + hsVector3 fNormal; + hsScalar fHSize, fVSize; // distance from the heart of the particle to the borders of its poly. + hsPoint3 fUVCoords[4]; +}; + +class plParticleExt +{ +public: + //hsPoint3 fOldPos; + hsVector3 fVelocity; + hsScalar fInvMass; // The inverse (1 / mass) is what we actually need for calculations. Storing it this + // way allows us to make an object immovable with an inverse mass of 0 (and save a divide). + hsVector3 fAcceleration; // Accumulated from multiple forces. + hsScalar fLife; // how many seconds before we recycle this? (My particle has more of a life than I do...) + hsScalar fStartLife; + hsScalar fScale; + hsScalar fRadsPerSec; + //UInt32 fOrigColor; + + enum // Miscellaneous flags for particles + { + kImmortal = 0x00000001, + }; + UInt32 fMiscFlags; // I know... 32 bits for a single flag... + // Feel free to change this if you've got something to pack it against. +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleApplicator.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleApplicator.cpp new file mode 100644 index 00000000..81304413 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleApplicator.cpp @@ -0,0 +1,119 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plParticleSystem.h" +#include "plParticleGenerator.h" +#include "plParticleApplicator.h" +#include "../plAvatar/plScalarChannel.h" +#include "../plAvatar/plAGModifier.h" +#include "../plMessage/plParticleUpdateMsg.h" +#include "../pnSceneObject/plSceneObject.h" + +#define PI 3.14159 + +plParticleGenerator *plParticleApplicator::IGetParticleGen(plSceneObject *so) +{ + UInt32 numMods = so->GetNumModifiers(); + int i; + for (i = 0; i < numMods; i++) + { + const plParticleSystem *result = plParticleSystem::ConvertNoRef(so->GetModifier(i)); + if (result != nil) + return result->GetExportedGenerator(); + } + + return nil; +} + +void plParticleLifeMinApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel); + IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamPartLifeMin, + chan->Value(time)); +} + +void plParticleLifeMaxApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel); + IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamPartLifeMax, + chan->Value(time)); +} + +void plParticlePPSApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel); + IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamParticlesPerSecond, + chan->Value(time)); +} + +void plParticleAngleApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel); + IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamInitPitchRange, + (hsScalar)(chan->Value(time) * PI / 180.f)); +} + +void plParticleVelMinApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel); + IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamVelMin, + chan->Value(time)); +} + +void plParticleVelMaxApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel); + IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamVelMax, + chan->Value(time)); +} + +void plParticleScaleMinApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel); + IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamScaleMin, + chan->Value(time) / 100.f); +} + +void plParticleScaleMaxApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel); + IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamScaleMax, + chan->Value(time) / 100.f); +} + +void plParticleGravityApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel); +// IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamParticlesPerSecond, +// chan->Value(time)); +} + +void plParticleDragApplicator::IApply(const plAGModifier *mod, double time) +{ + plScalarChannel *chan = plScalarChannel::ConvertNoRef(fChannel); +// IGetParticleGen(mod->GetTarget(0))->UpdateParam(plParticleUpdateMsg::kParamParticlesPerSecond, +// chan->Value(time)); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleApplicator.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleApplicator.h new file mode 100644 index 00000000..4063799f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleApplicator.h @@ -0,0 +1,145 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLPARTICLEAPPLICATOR_INC +#define PLPARTICLEAPPLICATOR_INC + +#include "../plAvatar/plAGChannel.h" +#include "../plAvatar/plAGApplicator.h" + +class plParticleSystem; + +class plParticleApplicator : public plAGApplicator +{ +protected: + plParticleGenerator *IGetParticleGen(plSceneObject *so); + virtual void IApply(const plAGModifier *mod, double time) = 0; + +public: + CLASSNAME_REGISTER( plParticleApplicator ); + GETINTERFACE_ANY( plParticleApplicator, plAGApplicator ); +}; + +class plParticleLifeMinApplicator : public plParticleApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plParticleLifeMinApplicator ); + GETINTERFACE_ANY( plParticleLifeMinApplicator, plAGApplicator ); +}; + +class plParticleLifeMaxApplicator : public plParticleApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plParticleLifeMaxApplicator ); + GETINTERFACE_ANY( plParticleLifeMaxApplicator, plAGApplicator ); +}; + +class plParticlePPSApplicator : public plParticleApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plParticlePPSApplicator ); + GETINTERFACE_ANY( plParticlePPSApplicator, plAGApplicator ); +}; + +class plParticleAngleApplicator : public plParticleApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plParticleAngleApplicator ); + GETINTERFACE_ANY( plParticleAngleApplicator, plAGApplicator ); +}; + +class plParticleVelMinApplicator : public plParticleApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plParticleVelMinApplicator ); + GETINTERFACE_ANY( plParticleVelMinApplicator, plAGApplicator ); +}; + +class plParticleVelMaxApplicator : public plParticleApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plParticleVelMaxApplicator ); + GETINTERFACE_ANY( plParticleVelMaxApplicator, plAGApplicator ); +}; + +class plParticleScaleMinApplicator : public plParticleApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plParticleScaleMinApplicator ); + GETINTERFACE_ANY( plParticleScaleMinApplicator, plAGApplicator ); +}; + +class plParticleScaleMaxApplicator : public plParticleApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plParticleScaleMaxApplicator ); + GETINTERFACE_ANY( plParticleScaleMaxApplicator, plAGApplicator ); +}; + +class plParticleGravityApplicator : public plParticleApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plParticleGravityApplicator ); + GETINTERFACE_ANY( plParticleGravityApplicator, plAGApplicator ); +}; + +class plParticleDragApplicator : public plParticleApplicator +{ +protected: + virtual void IApply(const plAGModifier *mod, double time); + +public: + CLASSNAME_REGISTER( plParticleDragApplicator ); + GETINTERFACE_ANY( plParticleDragApplicator, plAGApplicator ); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleCreatable.h new file mode 100644 index 00000000..a84b85c6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleCreatable.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plParticleCreatable_inc +#define plParticleCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plParticleSystem.h" +#include "plParticleEffect.h" +#include "plParticleEmitter.h" +#include "plParticleGenerator.h" +#include "plParticleSystem.h" +#include "plParticleApplicator.h" +#include "plParticleSDLMod.h" +#include "plConvexVolume.h" +#include "plBoundInterface.h" + +REGISTER_CREATABLE( plParticleSystem ); +REGISTER_NONCREATABLE( plParticleEffect ); +REGISTER_NONCREATABLE( plParticleCollisionEffect ); +REGISTER_CREATABLE( plParticleCollisionEffectBeat ); +REGISTER_CREATABLE( plParticleCollisionEffectDie ); +REGISTER_CREATABLE( plParticleCollisionEffectBounce ); +REGISTER_CREATABLE( plParticleFadeVolumeEffect ); +REGISTER_NONCREATABLE( plParticleGenerator ); +REGISTER_CREATABLE( plSimpleParticleGenerator ); +REGISTER_CREATABLE( plOneTimeParticleGenerator ); +REGISTER_CREATABLE( plParticleEmitter ); +REGISTER_CREATABLE( plConvexVolume ); +REGISTER_CREATABLE( plBoundInterface ); +REGISTER_NONCREATABLE( plParticleApplicator ); +REGISTER_CREATABLE( plParticleLifeMinApplicator ); +REGISTER_CREATABLE( plParticleLifeMaxApplicator ); +REGISTER_CREATABLE( plParticlePPSApplicator ); +REGISTER_CREATABLE( plParticleAngleApplicator ); +REGISTER_CREATABLE( plParticleVelMinApplicator ); +REGISTER_CREATABLE( plParticleVelMaxApplicator ); +REGISTER_CREATABLE( plParticleScaleMinApplicator ); +REGISTER_CREATABLE( plParticleScaleMaxApplicator ); +//REGISTER_CREATABLE( plParticleGravityApplicator ); +//REGISTER_CREATABLE( plParticleDragApplicator ); +REGISTER_NONCREATABLE( plParticleWindEffect ); +REGISTER_CREATABLE( plParticleLocalWind ); +REGISTER_CREATABLE( plParticleUniformWind ); +REGISTER_CREATABLE( plParticleFlockEffect ); +REGISTER_CREATABLE( plParticleFollowSystemEffect ); +REGISTER_CREATABLE( plParticleSDLMod ); + +#endif // plParticleCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEffect.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEffect.cpp new file mode 100644 index 00000000..67a1ba15 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEffect.cpp @@ -0,0 +1,895 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsGeometry3.h" +#include "plParticle.h" +#include "plParticleEffect.h" +#include "plEffectTargetInfo.h" +#include "plConvexVolume.h" +#include "plBoundInterface.h" +#include "hsResMgr.h" +#include "plPipeline.h" +#include "hsFastMath.h" +#include "../plMath/plRandom.h" +#include "plParticleSystem.h" +#include "../plMessage/plParticleUpdateMsg.h" + +/////////////////////////////////////////////////////////////////////////////////////////// +plParticleCollisionEffect::plParticleCollisionEffect() +{ + fBounds = nil; + fSceneObj = nil; +} + +plParticleCollisionEffect::~plParticleCollisionEffect() +{ +} + +void plParticleCollisionEffect::PrepareEffect(const plEffectTargetInfo &target) +{ + if (fBounds == nil) + { + plBoundInterface *bi = plBoundInterface::ConvertNoRef(fSceneObj->GetGenericInterface(plBoundInterface::Index())); + if (bi == nil) + return; + fBounds = bi->GetVolume(); + } +} + +hsBool plParticleCollisionEffect::MsgReceive(plMessage* msg) +{ + plRefMsg* refMsg = plRefMsg::ConvertNoRef(msg); + plSceneObject *so; + if (refMsg) + { + if (so = plSceneObject::ConvertNoRef(refMsg->GetRef())) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fSceneObj = so; + else + fSceneObj = nil; + return true; + } + } + return hsKeyedObject::MsgReceive(msg); +} + +void plParticleCollisionEffect::Read(hsStream *s, hsResMgr *mgr) +{ + hsKeyedObject::Read(s, mgr); + + plGenRefMsg* msg; + msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0); // SceneObject + mgr->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef); + fBounds = nil; +} + +void plParticleCollisionEffect::Write(hsStream *s, hsResMgr *mgr) +{ + hsKeyedObject::Write(s, mgr); + + mgr->WriteKey(s, fSceneObj); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// Some permutations on the CollisionEffect follow +/////////////////////////////////////////////////////////////////////////////////////////// + +plParticleCollisionEffectBeat::plParticleCollisionEffectBeat() +{ +} + +hsBool plParticleCollisionEffectBeat::ApplyEffect(const plEffectTargetInfo &target, Int32 i) +{ + hsAssert(i >= 0, "Use of default argument doesn't make sense for plParticleCollisionEffect"); + + if( !fBounds ) + return false; + + hsPoint3 *currPos = (hsPoint3 *)(target.fPos + i * target.fPosStride); + fBounds->ResolvePoint(*currPos); + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +plParticleCollisionEffectDie::plParticleCollisionEffectDie() +{ +} + +hsBool plParticleCollisionEffectDie::ApplyEffect(const plEffectTargetInfo &target, Int32 i) +{ + hsAssert(i >= 0, "Use of default argument doesn't make sense for plParticleCollisionEffect"); + + if( !fBounds ) + return false; + + hsPoint3 *currPos = (hsPoint3 *)(target.fPos + i * target.fPosStride); + return fBounds->IsInside(*currPos); +} + +/////////////////////////////////////////////////////////////////////////////////////////// + +plParticleCollisionEffectBounce::plParticleCollisionEffectBounce() +: fBounce(1.f), + fFriction(0.f) +{ +} + +hsBool plParticleCollisionEffectBounce::ApplyEffect(const plEffectTargetInfo &target, Int32 i) +{ + hsAssert(i >= 0, "Use of default argument doesn't make sense for plParticleCollisionEffect"); + + if( !fBounds ) + return false; + + hsPoint3* currPos = (hsPoint3 *)(target.fPos + i * target.fPosStride); + hsVector3* currVel = (hsVector3*)(target.fVelocity + i * target.fVelocityStride); + fBounds->BouncePoint(*currPos, *currVel, fBounce, fFriction); + + return false; +} + +void plParticleCollisionEffectBounce::Read(hsStream *s, hsResMgr *mgr) +{ + plParticleCollisionEffect::Read(s, mgr); + + fBounce = s->ReadSwapScalar(); + fFriction = s->ReadSwapScalar(); +} + +void plParticleCollisionEffectBounce::Write(hsStream *s, hsResMgr *mgr) +{ + plParticleCollisionEffect::Write(s, mgr); + + s->WriteSwapScalar(fBounce); + s->WriteSwapScalar(fFriction); +} + + + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plParticleFadeVolumeEffect::plParticleFadeVolumeEffect() : fLength(100.0f), fIgnoreZ(true) +{ +} + +plParticleFadeVolumeEffect::~plParticleFadeVolumeEffect() +{ +} + +// +// It's not really clear looking at the math here what's actually going on, +// but once you visualize it, it's pretty easy to follow. So the camera position, +// view direction, and length of the fade volume define a sphere, where the camera +// position is a point on the sphere, the view direction points from that surface +// point toward the center, and the length is the sphere's radius. Since the view +// direction points straight through the sphere, that sphere is the sweet zone for +// putting our particles to pile them up in front of the camera. But we'd like to +// do this independently for each axis (for efficiency, rather than true 3D calculations), +// so we put an axis aligned box around the sphere, and that's the volume we restrict +// our particles to. +// Now we could fade all around the box, but that's not really what we want, because +// that means fading particles that are behind us. And in the case where we're looking +// along an axis, the camera is pushed up against a face of the box (where the axis +// aligned box is tangent to the inscribed sphere), so we'd actually be fading +// particles just in front of the camera. Because of this non-symmetry, we're going to +// define the Max in a given dimension as the world space value for that dimension +// FARTHEST from the camera (NOT largest in value). So if the camera is looking +// in a negative direction in one dimension, the Max will be less than the Min for +// that dimension. +// So we set up our Max's and Min's for each dimension in PrepareEffect(), and then +// at runtime we calculate the parameter value of the particle ranging from 0 where +// particleLoc == Min to 1 where particleLoc == Max. If the parameter is outside +// [0..1], then we can move it into the box using the fractional part of the parameter. +// Finally, if the (possibly relocated) parameter value says the particle is approaching +// the Max value, we can calclulate its faded opacity from the parameter. +// + +// Need to experiment to minimize this fade distance. The greater +// the fade distance, the more faded out (wasted) particles we're drawing. +// The shorter the distance, the more noticable the fade out. +// Note the wierdness between the fractions, because kFadeFrac is fraction +// of fLength, but kFadeParm and kInvFadeFrac are fraction of 2.f*fLength. Sorry. +const hsScalar kFadeFrac = 0.5f; +const hsScalar kFadeParm = 1.f - kFadeFrac * 0.5f; +const hsScalar kInvFadeFrac = 1.f / (kFadeFrac * 0.5f); + +void plParticleFadeVolumeEffect::PrepareEffect(const plEffectTargetInfo &target) +{ + hsPoint3 viewLoc = target.fContext.fPipeline->GetViewPositionWorld(); + hsVector3 viewDir = target.fContext.fPipeline->GetViewDirWorld(); + + // Nuking out the setting of viewDir.fZ to 0 when we aren't centering + // about Z (fIgnoreZ == true), because we still want to center our + // volume about the camera (rather than push the camera to the edge of + // the cylinder) in that case, so we don't get artifacts when we look + // straight up or down. mf + + hsPoint3 signs(viewDir.fX >= 0 ? 1.f : -1.f, viewDir.fY >= 0 ? 1.f : -1.f, viewDir.fZ >= 0 ? 1.f : -1.f); + + fMax.fX = viewLoc.fX + (viewDir.fX + signs.fX) * fLength; + fMin.fX = fMax.fX - 2.f * signs.fX * fLength; + + fMax.fY = viewLoc.fY + (viewDir.fY + signs.fY) * fLength; + fMin.fY = fMax.fY - 2.f * signs.fY * fLength; + + fMax.fZ = viewLoc.fZ + (viewDir.fZ + signs.fZ) * fLength; + fMin.fZ = fMax.fZ - 2.f * signs.fZ * fLength; + + fNorm.fX = 1.f / (fMax.fX - fMin.fX); + fNorm.fY = 1.f / (fMax.fY - fMin.fY); + fNorm.fZ = 1.f / (fMax.fZ - fMin.fZ); +} + +hsBool plParticleFadeVolumeEffect::ApplyEffect(const plEffectTargetInfo& target, Int32 i) +{ + hsPoint3 *currPos = (hsPoint3 *)(target.fPos + i * target.fPosStride); + + hsScalar parm; + + hsScalar fade = 1.f; + + parm = (currPos->fX - fMin.fX) * fNorm.fX; + if( parm < 0 ) + { + parm -= int(parm); + currPos->fX = fMax.fX + parm * (fMax.fX - fMin.fX); + parm += 1.f; + } + else if( parm > 1.f ) + { + parm -= int(parm); + currPos->fX = fMin.fX + parm * (fMax.fX - fMin.fX); + } + if( parm > kFadeParm ) + { + parm = 1.f - parm; + parm *= kInvFadeFrac; + if( parm < fade ) + fade = parm; + } + + parm = (currPos->fY - fMin.fY) * fNorm.fY; + if( parm < 0 ) + { + parm -= int(parm); + currPos->fY = fMax.fY + parm * (fMax.fY - fMin.fY); + parm += 1.f; + } + else if( parm > 1.f ) + { + parm -= int(parm); + currPos->fY = fMin.fY + parm * (fMax.fY - fMin.fY); + } + if( parm > kFadeParm ) + { + parm = 1.f - parm; + parm *= kInvFadeFrac; + if( parm < fade ) + fade = parm; + } + + if( !fIgnoreZ ) + { + parm = (currPos->fZ - fMin.fZ) * fNorm.fZ; + if( parm < 0 ) + { + parm -= int(parm); + currPos->fZ = fMax.fZ + parm * (fMax.fZ - fMin.fZ); + parm += 1.f; + } + else if( parm > 1.f ) + { + parm -= int(parm); + currPos->fZ = fMin.fZ + parm * (fMax.fZ - fMin.fZ); + } + if( parm > kFadeParm ) + { + parm = 1.f - parm; + parm *= kInvFadeFrac; + if( parm < fade ) + fade = parm; + } + } + + if( fade < 1.f ) + { + UInt32 *color = (UInt32 *)(target.fColor + i * target.fColorStride); + UInt32 alpha = (UInt32)((*color >> 24) * fade); + *color = (*color & 0x00ffffff) | (alpha << 24); + } + + return false; +} + +void plParticleFadeVolumeEffect::Read(hsStream *s, hsResMgr *mgr) +{ + hsKeyedObject::Read(s, mgr); + + fLength = s->ReadSwapScalar(); + fIgnoreZ = s->ReadBool(); +} + +void plParticleFadeVolumeEffect::Write(hsStream *s, hsResMgr *mgr) +{ + hsKeyedObject::Write(s, mgr); + + s->WriteSwapScalar(fLength); + s->WriteBool(fIgnoreZ); +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// Particle wind - Base class first +plParticleWindEffect::plParticleWindEffect() +: fWindVec(0,0,0), + fDir(1.f,0,0), + fSwirl(0.1f), + fConstancy(0), + fHorizontal(0), + fLastDirSecs(-1.f), + fRefDir(0.f,0.f,0.f), + fRandDir(1.f,0.f,0.f) +{ +} + +plParticleWindEffect::~plParticleWindEffect() +{ +} + +void plParticleWindEffect::Read(hsStream *s, hsResMgr *mgr) +{ + hsKeyedObject::Read(s, mgr); + + fStrength = s->ReadSwapScalar(); + fConstancy = s->ReadSwapScalar(); + fSwirl = s->ReadSwapScalar(); + fHorizontal = s->ReadBool(); + fRefDir.Read(s); + fDir.Read(s); + fRandDir = fDir; +} + +void plParticleWindEffect::Write(hsStream *s, hsResMgr *mgr) +{ + hsKeyedObject::Write(s, mgr); + + s->WriteSwapScalar(fStrength); + s->WriteSwapScalar(fConstancy); + s->WriteSwapScalar(fSwirl); + s->WriteBool(fHorizontal); + fRefDir.Write(s); + fDir.Write(s); +} + +void plParticleWindEffect::SetRefDirection(const hsVector3& v) +{ + fRefDir = v; + hsScalar lenSq = fRefDir.MagnitudeSquared(); + if( lenSq > 1.e-1f ) + { + fDir = fRefDir * hsFastMath::InvSqrtAppr(lenSq); + fRandDir = fDir; + } +} + +void plParticleWindEffect::PrepareEffect(const plEffectTargetInfo& target) +{ + if( fLastDirSecs != target.fContext.fSecs ) + { + static plRandom random; + fRandDir.fX += random.RandMinusOneToOne() * fSwirl; + fRandDir.fY += random.RandMinusOneToOne() * fSwirl; + if( !GetHorizontal() ) + fRandDir.fZ += random.RandMinusOneToOne() * fSwirl; + hsFastMath::NormalizeAppr(fRandDir); + fDir = fRandDir + fRefDir; + hsFastMath::NormalizeAppr(fDir); + + fWindVec = fDir * (fStrength * target.fContext.fSystem->GetWindMult() * target.fContext.fDelSecs); + + fLastDirSecs = target.fContext.fSecs; + } +} + +//////////////////////////////////////////////////////////////////////// +// Localized wind (how much wind you're getting depends on where you are +plParticleLocalWind::plParticleLocalWind() +: fScale(0, 0, 0), + fSpeed(0), + fPhase(0,0,0), + fInvScale(0,0,0), + fLastPhaseSecs(-1.f) +{ +} + +plParticleLocalWind::~plParticleLocalWind() +{ +} + +void plParticleLocalWind::Read(hsStream *s, hsResMgr *mgr) +{ + plParticleWindEffect::Read(s, mgr); + + fScale.Read(s); + fSpeed = s->ReadSwapScalar(); +} + +void plParticleLocalWind::Write(hsStream *s, hsResMgr *mgr) +{ + plParticleWindEffect::Write(s, mgr); + + fScale.Write(s); + s->WriteSwapScalar(fSpeed); +} + +void plParticleLocalWind::PrepareEffect(const plEffectTargetInfo& target) +{ + if( fLastPhaseSecs != target.fContext.fSecs ) + { + plParticleWindEffect::PrepareEffect(target); + + fPhase += fDir * fSpeed * target.fContext.fDelSecs; + + fInvScale.fX = fScale.fX > 0 ? 1.f / fScale.fX : 0; + fInvScale.fY = fScale.fY > 0 ? 1.f / fScale.fY : 0; + fInvScale.fZ = fScale.fZ > 0 ? 1.f / fScale.fZ : 0; + + fLastPhaseSecs = target.fContext.fSecs; + } +} + + +hsBool plParticleLocalWind::ApplyEffect(const plEffectTargetInfo& target, Int32 i) +{ + const hsPoint3& pos = *(hsPoint3 *)(target.fPos + i * target.fPosStride); + hsVector3& vel = *(hsVector3*)(target.fVelocity + i * target.fVelocityStride); + + const hsScalar kMinToBother = 0; + + float strength = 1.f / ( (1.f + fConstancy) * (1.f + fConstancy) ); + float s, c, t; + t = (pos[0] - fPhase[0]) * fInvScale[0]; + hsFastMath::SinCosAppr(t, s, c); + c += fConstancy; + if( c <= kMinToBother ) + return false; + strength *= c; + + t = (pos[1] - fPhase[1]) * fInvScale[1]; + hsFastMath::SinCosAppr(t, s, c); + c += fConstancy; + if( c <= kMinToBother ) + return false; + strength *= c; + +#if 0 // if you turn this back on, strength needs to drop by another factor of (1.f + fConstancy) + t = (pos[2] - fPhase[2]) * fInvScale[2]; + hsFastMath::SinCosAppr(t, s, c); + c += fConstancy; + if( c <= kMinToBother ) + return false; + strength *= c; +#endif + + const hsScalar& invMass = *(hsScalar*)(target.fInvMass + i * target.fInvMassStride); + strength *= invMass; + + vel += fWindVec * strength; + + return false; +} + +//////////////////////////////////////////////////////////////////////// +// Uniform wind - wind changes over time, but not space +plParticleUniformWind::plParticleUniformWind() +: fFreqMin(0.1f), + fFreqMax(0.2f), + fFreqCurr(0.1f), + fFreqRate(0.05f), + + fCurrPhase(0), + fLastFreqSecs(-1.f), + fCurrentStrength(0) +{ +} + +plParticleUniformWind::~plParticleUniformWind() +{ +} + +void plParticleUniformWind::Read(hsStream *s, hsResMgr *mgr) +{ + plParticleWindEffect::Read(s, mgr); + + fFreqMin = s->ReadSwapScalar(); + fFreqMax = s->ReadSwapScalar(); + fFreqRate = s->ReadSwapScalar(); + +#if 0 + fFreqMin = 1.f / 6.f; + fFreqMax = 1.f / 1.f; + fConstancy = -0.5f; + fSwirl = 0.05f; +#endif + + fFreqCurr = fFreqMin; +} + +void plParticleUniformWind::Write(hsStream *s, hsResMgr *mgr) +{ + plParticleWindEffect::Write(s, mgr); + + s->WriteSwapScalar(fFreqMin); + s->WriteSwapScalar(fFreqMax); + s->WriteSwapScalar(fFreqRate); +} + +void plParticleUniformWind::SetFrequencyRange(hsScalar minSecsPerCycle, hsScalar maxSecsPerCycle) +{ + const hsScalar kMinSecsPerCycle = 1.f; + if( minSecsPerCycle < kMinSecsPerCycle ) + minSecsPerCycle = kMinSecsPerCycle; + if( minSecsPerCycle < kMinSecsPerCycle ) + minSecsPerCycle = kMinSecsPerCycle; + + if( minSecsPerCycle < maxSecsPerCycle ) + { + fFreqMin = 1.f / maxSecsPerCycle; + fFreqMax = 1.f / minSecsPerCycle; + } + else + { + fFreqMin = 1.f / minSecsPerCycle; + fFreqMax = 1.f / maxSecsPerCycle; + } +} + +void plParticleUniformWind::SetFrequencyRate(hsScalar secsPerCycle) +{ + const hsScalar kMinSecsPerCycle = 1.f; + if( secsPerCycle < kMinSecsPerCycle ) + secsPerCycle = kMinSecsPerCycle; + fFreqRate = 1.f / secsPerCycle; +} + + +void plParticleUniformWind::PrepareEffect(const plEffectTargetInfo& target) +{ + plParticleWindEffect::PrepareEffect(target); + + if( fLastFreqSecs != target.fContext.fSecs ) + { + static plRandom random; + + const double kTwoPi = hsScalarPI * 2.0; + double t0 = fFreqCurr * fLastFreqSecs + fCurrPhase; + hsScalar t1 = (hsScalar)fmod(t0, kTwoPi); + fCurrPhase -= t0 - t1; + + fFreqCurr += fFreqRate * target.fContext.fDelSecs * random.RandZeroToOne(); + + if( fFreqCurr > fFreqMax ) + { + fFreqCurr = fFreqMax; + fFreqRate = -fFreqRate; + } + else if( fFreqCurr < fFreqMin ) + { + fFreqCurr = fFreqMin; + fFreqRate = -fFreqRate; + } + + hsScalar phaseDel = (hsScalar)(t1 - (fFreqCurr * fLastFreqSecs + fCurrPhase)); + fCurrPhase += phaseDel; + + hsScalar t = hsScalar(fFreqCurr * target.fContext.fSecs + fCurrPhase); + hsScalar s; + hsFastMath::SinCosAppr(t, s, fCurrentStrength); + fCurrentStrength += fConstancy; + fCurrentStrength /= (1.f + fConstancy); + + if( fCurrentStrength < 0 ) + fCurrentStrength = 0; + + fLastFreqSecs = target.fContext.fSecs; + } +} + + +hsBool plParticleUniformWind::ApplyEffect(const plEffectTargetInfo& target, Int32 i) +{ + hsVector3& vel = *(hsVector3*)(target.fVelocity + i * target.fVelocityStride); + + const hsScalar& invMass = *(hsScalar*)(target.fInvMass + i * target.fInvMassStride); + + vel += fWindVec * (invMass * fCurrentStrength); + + return false; +} + +//////////////////////////////////////////////////////////////////////// +// Simplified flocking. + +plParticleFlockEffect::plParticleFlockEffect() : + fInfAvgRadSq(1), + fInfRepRadSq(1), + fAvgVelStr(1), + fRepDirStr(1), + fGoalOrbitStr(1), + fGoalChaseStr(1), + fGoalDistSq(1), + fFullChaseDistSq(1), + fMaxOrbitSpeed(1), + fMaxChaseSpeed(1), + fMaxParticles(0), + fDistSq(nil), + fInfluences(nil) +{ + fTargetOffset.Set(0.f, 0.f, 0.f); + fDissenterTarget.Set(0.f, 0.f, 0.f); +} + +plParticleFlockEffect::~plParticleFlockEffect() +{ + SetMaxParticles(0); +} + +void plParticleFlockEffect::IUpdateDistances(const plEffectTargetInfo& target) +{ + int i, j; + int numParticles = hsMinimum(fMaxParticles, target.fNumValidParticles); + + for (i = 0; i < numParticles; i++) + { + for (j = i + 1; j < numParticles; j++) + { + hsVector3 diff((hsPoint3*)(target.fPos + i * target.fPosStride), (hsPoint3*)(target.fPos + j * target.fPosStride)); + fDistSq[i * fMaxParticles + j] = fDistSq[j * fMaxParticles + i] = diff.MagnitudeSquared(); + } + } +} + +void plParticleFlockEffect::IUpdateInfluences(const plEffectTargetInfo &target) +{ + int i, j; + int numParticles = hsMinimum(fMaxParticles, target.fNumValidParticles); + + for (i = 0; i < numParticles; i++) + { + int numAvg = 0; + int numRep = 0; + fInfluences[i].fAvgVel.Set(0.f, 0.f, 0.f); + fInfluences[i].fRepDir.Set(0.f, 0.f, 0.f); + + for (j = 0; j < numParticles; j++) + { + if (i == j) + continue; + + const int distIdx = i * fMaxParticles + j; + if (fDistSq[distIdx] > fInfAvgRadSq) + { + numAvg++; + fInfluences[i].fAvgVel += *(hsVector3*)(target.fVelocity + j * target.fVelocityStride); + } + + if (fDistSq[distIdx] > fInfRepRadSq) + { + numRep++; + hsVector3 repDir((hsPoint3*)(target.fPos + i * target.fPosStride), (hsPoint3*)(target.fPos + j * target.fPosStride)); + repDir.Normalize(); + fInfluences[i].fRepDir += repDir; + } + + } + + if (numAvg > 0) + fInfluences[i].fAvgVel /= (hsScalar)numAvg; + if (numRep > 0) + fInfluences[i].fRepDir /= (hsScalar)numRep; + } +} + +void plParticleFlockEffect::PrepareEffect(const plEffectTargetInfo& target) +{ + IUpdateDistances(target); + IUpdateInfluences(target); +} + +// Some of this is the same for every particle and should be cached in PrepareEffect(). +// Holding off on that until I like the behavior. +hsBool plParticleFlockEffect::ApplyEffect(const plEffectTargetInfo& target, Int32 i) +{ + if (i >= fMaxParticles) + return false; // Don't have the memory to deal with you. Good luck kid... + + const hsPoint3 &pos = *(hsPoint3*)(target.fPos + i * target.fPosStride); + hsVector3 &vel = *(hsVector3*)(target.fVelocity + i * target.fVelocityStride); + + hsScalar curSpeed = vel.Magnitude(); + hsPoint3 goal; + if (*(UInt32*)(target.fMiscFlags + i * target.fMiscFlagsStride) & plParticleExt::kImmortal) + goal = target.fContext.fSystem->GetTarget(0)->GetLocalToWorld().GetTranslate() + fTargetOffset; + else + goal = fDissenterTarget; + + hsVector3 goalDir; + goalDir.Set(&(goal - pos)); + hsScalar distSq = goalDir.MagnitudeSquared(); + + goalDir.Normalize(); + + hsScalar goalStr; + hsScalar maxSpeed; + hsScalar maxSpeedSq; + if (distSq <= fGoalDistSq) + { + goalStr = fGoalOrbitStr; + if (i & 0x1) + goalDir.Set(goalDir.fY, -goalDir.fX, goalDir.fZ); + else + goalDir.Set(-goalDir.fY, goalDir.fX, goalDir.fZ); + + maxSpeed = fMaxOrbitSpeed; + } + else if (distSq >= fFullChaseDistSq) + { + goalStr = fGoalChaseStr; + maxSpeed = fMaxChaseSpeed; + } + else + { + hsScalar pct = (distSq - fGoalDistSq) / (fFullChaseDistSq - fGoalDistSq); + goalStr = fGoalOrbitStr + (fGoalChaseStr - fGoalOrbitStr) * pct; + maxSpeed = fMaxOrbitSpeed + (fMaxChaseSpeed - fMaxOrbitSpeed) * pct; + } + maxSpeedSq = maxSpeed * maxSpeed; + + vel += (fInfluences[i].fAvgVel - vel) * (fAvgVelStr * target.fContext.fDelSecs); + vel += goalDir * (curSpeed * goalStr * target.fContext.fDelSecs); + vel += fInfluences[i].fRepDir * (curSpeed * fRepDirStr * target.fContext.fDelSecs); + + if (vel.MagnitudeSquared() > maxSpeedSq) + { + vel.Normalize(); + vel *= maxSpeed; + } + + return false; +} + +void plParticleFlockEffect::SetMaxParticles(const UInt16 num) +{ + delete [] fDistSq; + delete [] fInfluences; + fMaxParticles = num; + + if (num > 0) + { + fDistSq = TRACKED_NEW hsScalar[num * num]; + fInfluences = TRACKED_NEW plParticleInfluenceInfo[num]; + } +} + +void plParticleFlockEffect::Read(hsStream *s, hsResMgr *mgr) +{ + plParticleEffect::Read(s, mgr); + + fTargetOffset.Read(s); + fDissenterTarget.Read(s); + fInfAvgRadSq = s->ReadSwapScalar(); + fInfRepRadSq = s->ReadSwapScalar(); + fGoalDistSq = s->ReadSwapScalar(); + fFullChaseDistSq = s->ReadSwapScalar(); + fAvgVelStr = s->ReadSwapScalar(); + fRepDirStr = s->ReadSwapScalar(); + fGoalOrbitStr = s->ReadSwapScalar(); + fGoalChaseStr = s->ReadSwapScalar(); + SetMaxOrbitSpeed(s->ReadSwapScalar()); + SetMaxChaseSpeed(s->ReadSwapScalar()); + SetMaxParticles((UInt16)s->ReadSwapScalar()); +} + +void plParticleFlockEffect::Write(hsStream *s, hsResMgr *mgr) +{ + plParticleEffect::Write(s, mgr); + + fTargetOffset.Write(s); + fDissenterTarget.Write(s); + s->WriteSwapScalar(fInfAvgRadSq); + s->WriteSwapScalar(fInfRepRadSq); + s->WriteSwapScalar(fGoalDistSq); + s->WriteSwapScalar(fFullChaseDistSq); + s->WriteSwapScalar(fAvgVelStr); + s->WriteSwapScalar(fRepDirStr); + s->WriteSwapScalar(fGoalOrbitStr); + s->WriteSwapScalar(fGoalChaseStr); + s->WriteSwapScalar(fMaxOrbitSpeed); + s->WriteSwapScalar(fMaxChaseSpeed); + s->WriteSwapScalar(fMaxParticles); +} + +hsBool plParticleFlockEffect::MsgReceive(plMessage *msg) +{ + plParticleFlockMsg *flockMsg = plParticleFlockMsg::ConvertNoRef(msg); + if (flockMsg) + { + switch (flockMsg->fCmd) + { + case plParticleFlockMsg::kFlockCmdSetDissentPoint: + fDissenterTarget.Set(flockMsg->fX, flockMsg->fY, flockMsg->fZ); + break; + case plParticleFlockMsg::kFlockCmdSetOffset: + fTargetOffset.Set(flockMsg->fX, flockMsg->fY, flockMsg->fZ); + break; + default: + break; + } + return true; + } + + return plParticleEffect::MsgReceive(msg); +} + +/////////////////////////////////////////////////////////////////////////////////////// + +plParticleFollowSystemEffect::plParticleFollowSystemEffect() : fEvalThisFrame(true) +{ + fOldW2L = hsMatrix44::IdentityMatrix(); +} + +void plParticleFollowSystemEffect::PrepareEffect(const plEffectTargetInfo& target) +{ + fEvalThisFrame = (fOldW2L != target.fContext.fSystem->GetTarget(0)->GetWorldToLocal()); +} + +hsBool plParticleFollowSystemEffect::ApplyEffect(const plEffectTargetInfo& target, Int32 i) +{ + if (fEvalThisFrame) + { + if (i < target.fFirstNewParticle && !fOldW2L.IsIdentity()) + { + hsPoint3 &pos = *(hsPoint3*)(target.fPos + i * target.fPosStride); + pos = target.fContext.fSystem->GetTarget(0)->GetLocalToWorld() * fOldW2L * pos; + } + } + return true; +} + +void plParticleFollowSystemEffect::EndEffect(const plEffectTargetInfo& target) +{ + if (fEvalThisFrame) + fOldW2L = target.fContext.fSystem->GetTarget(0)->GetWorldToLocal(); +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEffect.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEffect.h new file mode 100644 index 00000000..9c1a68c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEffect.h @@ -0,0 +1,340 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plParticleEffect_inc +#define plParticleEffect_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsMatrix44.h" + +class plEffectTargetInfo; +class plConvexVolume; +class hsResMgr; +class plSceneObject; + +class plParticleEffect : public hsKeyedObject +{ +public: + CLASSNAME_REGISTER( plParticleEffect ); + GETINTERFACE_ANY( plParticleEffect, hsKeyedObject ); + + // Order is: + // PrepareEffect is called with a given target (including valid + // ParticleContext). + // ApplyEffect is called some once for each particle (maybe zero times). + // It can return true to kill a particle. + // Target and Context passed in with Prepare will be + // guaranteed to remain valid until, + // EndEffect marks no more particles will be processed with the above + // context (invalidating anything cached). + // Defaults for Prepare and End are no-ops. + virtual void PrepareEffect(const plEffectTargetInfo& target) {} + virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i) = 0; + virtual void EndEffect(const plEffectTargetInfo& target) {} +}; + +class plParticleCollisionEffect : public plParticleEffect +{ +public: + plParticleCollisionEffect(); + ~plParticleCollisionEffect(); + + CLASSNAME_REGISTER( plParticleCollisionEffect ); + GETINTERFACE_ANY( plParticleCollisionEffect, plParticleEffect ); + + virtual void PrepareEffect(const plEffectTargetInfo& target); + + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); + virtual hsBool MsgReceive(plMessage *msg); + +protected: + plSceneObject *fSceneObj; + plConvexVolume *fBounds; +}; + +// Default particle blocker. Doesn't affect particle's velocity, +// so it'll keep "beat"ing on the deflector until the velocity +// dotted with the deflector normal slides it off an edge. +class plParticleCollisionEffectBeat : public plParticleCollisionEffect +{ +public: + plParticleCollisionEffectBeat(); + + CLASSNAME_REGISTER( plParticleCollisionEffectBeat ); + GETINTERFACE_ANY( plParticleCollisionEffectBeat, plParticleCollisionEffect ); + + virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i); +}; + +// This particle blocker just kills any particles that hit it. +class plParticleCollisionEffectDie : public plParticleCollisionEffect +{ +public: + plParticleCollisionEffectDie(); + + CLASSNAME_REGISTER( plParticleCollisionEffectDie ); + GETINTERFACE_ANY( plParticleCollisionEffectDie, plParticleCollisionEffect ); + + virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i); +}; + +class plParticleCollisionEffectBounce : public plParticleCollisionEffect +{ +protected: + hsScalar fBounce; + hsScalar fFriction; +public: + plParticleCollisionEffectBounce(); + + CLASSNAME_REGISTER( plParticleCollisionEffectBounce ); + GETINTERFACE_ANY( plParticleCollisionEffectBounce, plParticleCollisionEffect ); + + virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i); + + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); + + void SetBounce(hsScalar b) { fBounce = b; } + hsScalar GetBounce() const { return fBounce; } + + void SetFriction(hsScalar f) { fFriction = f; } + hsScalar GetFriction() const { return fFriction; } +}; + +class plParticleFadeVolumeEffect : public plParticleEffect +{ +protected: + // Some cached properties. These will be the same for all + // particles between matching PrepareEffect and EndEffect calls. + hsPoint3 fMax; + hsPoint3 fMin; + hsPoint3 fNorm; + +public: + plParticleFadeVolumeEffect(); + ~plParticleFadeVolumeEffect(); + + CLASSNAME_REGISTER( plParticleFadeVolumeEffect ); + GETINTERFACE_ANY( plParticleFadeVolumeEffect, plParticleEffect ); + + virtual void PrepareEffect(const plEffectTargetInfo& target); + virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i); + + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); + //virtual hsBool MsgReceive(plMessage *msg); + + hsScalar fLength; + hsBool fIgnoreZ; +}; + +class plParticleWindEffect : public plParticleEffect +{ +protected: + // The properties that define the wind. These might/should be animatable. + hsScalar fStrength; + hsScalar fConstancy; + hsScalar fSwirl; + hsBool fHorizontal; + hsVector3 fRefDir; + + // Some cached properties. These will be the same for all + // particles between matching PrepareEffect and EndEffect calls. + // These define the current state of the wind. + hsVector3 fWindVec; + hsVector3 fRandDir; + hsVector3 fDir; + double fLastDirSecs; +public: + + plParticleWindEffect(); + ~plParticleWindEffect(); + + CLASSNAME_REGISTER( plParticleWindEffect ); + GETINTERFACE_ANY( plParticleWindEffect, plParticleEffect ); + + virtual void PrepareEffect(const plEffectTargetInfo& target); + virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i) = 0; + + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); + + void SetStrength(hsScalar v) { fStrength = v; } + hsScalar GetStrength() const { return fStrength; } + + void SetConstancy(hsScalar c) { fConstancy = c; } + hsScalar GetConstancy() const { return fConstancy; } + + void SetSwirl(hsScalar s) { fSwirl = s; } + hsScalar GetSwirl() const { return fSwirl; } + + void SetHorizontal(hsBool on) { fHorizontal = on; } + hsBool GetHorizontal() const { return fHorizontal; } + + void SetRefDirection(const hsVector3& v); + const hsVector3& GetRefDirection() const { return fRefDir; } +}; + +class plParticleLocalWind : public plParticleWindEffect +{ +protected: + hsVector3 fScale; + hsScalar fSpeed; + + hsVector3 fPhase; + hsVector3 fInvScale; + double fLastPhaseSecs; + +public: + plParticleLocalWind(); + ~plParticleLocalWind(); + + CLASSNAME_REGISTER( plParticleLocalWind ); + GETINTERFACE_ANY( plParticleLocalWind, plParticleWindEffect ); + + virtual void PrepareEffect(const plEffectTargetInfo& target); + virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i); + + void SetScale(const hsVector3& v) { fScale = v; } + const hsVector3& GetScale() const { return fScale; } + + void SetSpeed(hsScalar v) { fSpeed = v; } + hsScalar GetSpeed() const { return fSpeed; } + + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); + +}; + +class plParticleUniformWind : public plParticleWindEffect +{ +protected: + + hsScalar fFreqMin; + hsScalar fFreqMax; + hsScalar fFreqCurr; + hsScalar fFreqRate; + double fCurrPhase; + + double fLastFreqSecs; + + hsScalar fCurrentStrength; +public: + plParticleUniformWind(); + ~plParticleUniformWind(); + + CLASSNAME_REGISTER( plParticleUniformWind ); + GETINTERFACE_ANY( plParticleUniformWind, plParticleWindEffect ); + + virtual void PrepareEffect(const plEffectTargetInfo& target); + virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i); + + void SetFrequencyRange(hsScalar minSecsPerCycle, hsScalar maxSecsPerCycle); + void SetFrequencyRate(hsScalar secsPerCycle); + + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); + +}; + +class plParticleInfluenceInfo +{ +public: + hsVector3 fAvgVel; + hsVector3 fRepDir; +}; + +class plParticleFlockEffect : public plParticleEffect +{ +//protected: +protected: + hsPoint3 fTargetOffset; // Worldspace offset from our target to get the true goal + hsPoint3 fDissenterTarget; // Where to particles go when they get scared and leave us? + hsScalar fInfAvgRadSq; // Square of the radius of influence for average velocity matching. + hsScalar fInfRepRadSq; // Same, for repelling from neighbors. + hsScalar fAvgVelStr; // How strongly are we influenced by average dir? + hsScalar fRepDirStr; // Same for repelling + hsScalar fGoalOrbitStr; // Same for the goal (when we're within the desired distance) + hsScalar fGoalChaseStr; // Same for the goal (when we're too far away, and chasing it) + hsScalar fGoalDistSq; + hsScalar fFullChaseDistSq; + hsScalar fMaxOrbitSpeed; + hsScalar fMaxChaseSpeed; + + UInt16 fMaxParticles; + hsScalar *fDistSq; // Table of distances from particle to particle + plParticleInfluenceInfo *fInfluences; + + void IUpdateDistances(const plEffectTargetInfo &target); + void IUpdateInfluences(const plEffectTargetInfo &target); + +public: + plParticleFlockEffect(); + ~plParticleFlockEffect(); + + CLASSNAME_REGISTER( plParticleFlockEffect ); + GETINTERFACE_ANY( plParticleFlockEffect, plParticleEffect ); + + virtual void PrepareEffect(const plEffectTargetInfo& target); + virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i); + + void SetTargetOffset(const hsPoint3 &offset) { fTargetOffset = offset; } + void SetDissenterTarget(const hsPoint3 &target) { fDissenterTarget = target; } + void SetInfluenceAvgRadius(hsScalar val) { fInfAvgRadSq = val * val; } + void SetInfluenceRepelRadius(hsScalar val) { fInfRepRadSq = val * val; } + void SetGoalRadius(hsScalar val) { fGoalDistSq = val * val; } + void SetFullChaseRadius(hsScalar val) { fFullChaseDistSq = val * val; } + void SetConformStr(hsScalar val) { fAvgVelStr = val; } + void SetRepelStr(hsScalar val) { fRepDirStr = val; } + void SetGoalOrbitStr(hsScalar val) { fGoalOrbitStr = val; } + void SetGoalChaseStr(hsScalar val) { fGoalChaseStr = val; } + void SetMaxOrbitSpeed(hsScalar val) { fMaxOrbitSpeed = val; } + void SetMaxChaseSpeed(hsScalar val) { fMaxChaseSpeed = val; } + void SetMaxParticles(UInt16 num); + + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); + virtual hsBool MsgReceive(plMessage *msg); +}; + +class plParticleFollowSystemEffect : public plParticleEffect +{ +public: + CLASSNAME_REGISTER( plParticleFollowSystemEffect ); + GETINTERFACE_ANY( plParticleFollowSystemEffect, plParticleEffect ); + + plParticleFollowSystemEffect(); + + virtual void PrepareEffect(const plEffectTargetInfo& target); + virtual hsBool ApplyEffect(const plEffectTargetInfo& target, Int32 i); + virtual void EndEffect(const plEffectTargetInfo& target); + +protected: + hsMatrix44 fOldW2L; + hsBool fEvalThisFrame; +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEmitter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEmitter.cpp new file mode 100644 index 00000000..25e97b42 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEmitter.cpp @@ -0,0 +1,569 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsUtils.h" +#include "hsResMgr.h" +#include "../pnMessage/plRefMsg.h" +#include "../plMessage/plParticleUpdateMsg.h" +#include "plParticleGenerator.h" +#include "plParticleEmitter.h" +#include "plParticleSystem.h" +#include "plParticle.h" +#include "plParticleEffect.h" +#include "../CoreLib/hsColorRGBA.h" +#include "../plInterp/plController.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" +#include "plProfile.h" +#include "hsFastMath.h" + +plProfile_CreateTimer("Update", "Particles", ParticleUpdate); +plProfile_CreateTimer("Generate", "Particles", ParticleGenerate); + +plParticleEmitter::plParticleEmitter() +{ + fParticleCores = nil; + fParticleExts = nil; + fGenerator = nil; + fLocalToWorld.Reset(); + fTimeToLive = 0; +} + +void plParticleEmitter::Init(plParticleSystem *system, UInt32 maxParticles, UInt32 spanIndex, UInt32 miscFlags, + plParticleGenerator *gen /* = nil */) +{ + IClear(); + fSystem = system; + plLayerInterface *layer = system->fTexture->GetLayer(0)->BottomOfStack(); + fMiscFlags = miscFlags | kNeedsUpdate; + if( fMiscFlags & kOnReserve ) + fTimeToLive = -1.f; // Wait for someone to give us a spurt of life. + if( layer->GetShadeFlags() & hsGMatState::kShadeEmissive ) + { + fMiscFlags |= kMatIsEmissive; + fColor = layer->GetAmbientColor(); + } + else + { + fColor = layer->GetRuntimeColor(); + } + fColor.a = layer->GetOpacity(); + fGenerator = gen; + fMaxParticles = maxParticles; + fSpanIndex = spanIndex; + ISetupParticleMem(); +} + +void plParticleEmitter::Clone(plParticleEmitter* src, UInt32 spanIndex) +{ + Init(src->fSystem, + src->fMaxParticles, + spanIndex, + src->fMiscFlags, + src->fGenerator); + + fMiscFlags |= kBorrowedGenerator; + fTimeToLive = -1.f; +} + +void plParticleEmitter::OverrideLocalToWorld(const hsMatrix44& l2w) +{ + fLocalToWorld = l2w; + fMiscFlags |= kOverrideLocalToWorld; +} + +plParticleEmitter::~plParticleEmitter() +{ + IClear(); +} + +void plParticleEmitter::IClear() +{ + delete [] fParticleCores; + fParticleCores = nil; + delete [] fParticleExts; + fParticleExts = nil; + if( !(fMiscFlags & kBorrowedGenerator) ) + delete fGenerator; + fGenerator = nil; +} + +void plParticleEmitter::ISetupParticleMem() +{ + fNumValidParticles = 0; + + fParticleCores = TRACKED_NEW plParticleCore[fMaxParticles]; + fParticleExts = TRACKED_NEW plParticleExt[fMaxParticles]; + + fTargetInfo.fPos = (UInt8 *)fParticleCores; + fTargetInfo.fColor = (UInt8 *)fParticleCores + sizeof(hsPoint3); + fTargetInfo.fPosStride = fTargetInfo.fColorStride = sizeof(plParticleCore); + + fTargetInfo.fVelocity = (UInt8 *)fParticleExts; + fTargetInfo.fInvMass = fTargetInfo.fVelocity + sizeof(hsVector3); + fTargetInfo.fAcceleration = fTargetInfo.fInvMass + sizeof(hsScalar); + fTargetInfo.fMiscFlags = (UInt8 *)&(fParticleExts[0].fMiscFlags); + fTargetInfo.fRadsPerSec = (UInt8 *)&(fParticleExts[0].fRadsPerSec); + fTargetInfo.fVelocityStride + = fTargetInfo.fInvMassStride + = fTargetInfo.fAccelerationStride + = fTargetInfo.fRadsPerSecStride + = fTargetInfo.fMiscFlagsStride + = sizeof(plParticleExt); +} + +UInt32 plParticleEmitter::GetNumTiles() const +{ + return fSystem->GetNumTiles(); +} + +const hsMatrix44 &plParticleEmitter::GetLocalToWorld() const +{ + return fMiscFlags & kOverrideLocalToWorld ? fLocalToWorld : fSystem->GetLocalToWorld(); +} + +void plParticleEmitter::AddParticle(hsPoint3 &pos, hsVector3 &velocity, UInt32 tileIndex, + hsScalar hSize, hsScalar vSize, hsScalar scale, hsScalar invMass, hsScalar life, + hsPoint3 &orientation, UInt32 miscFlags, hsScalar radsPerSec) +{ + plParticleCore *core; + plParticleExt *ext; + UInt32 currParticle; + + if (fNumValidParticles == fMaxParticles) + return; // No more room... you lose! + else + currParticle = fNumValidParticles++; + + core = &fParticleCores[currParticle]; + core->fPos = pos; + core->fOrientation = orientation; + core->fColor = CreateHexColor(fColor); + core->fHSize = hSize * scale; + core->fVSize = vSize * scale; + + // even if the kNormalUp flag isn't there, we should initialize it to something sane. + //if (core->fMiscFlags & kNormalUp != 0) + core->fNormal.Set(0, 0, 1); + + hsScalar xOff = (tileIndex % fSystem->fXTiles) / (float)fSystem->fXTiles; + hsScalar yOff = (tileIndex / fSystem->fXTiles) / (float)fSystem->fYTiles; + + core->fUVCoords[0].fX = xOff; + core->fUVCoords[0].fY = yOff + 1.0f / fSystem->fYTiles; + core->fUVCoords[0].fZ = 1.0f; + core->fUVCoords[1].fX = xOff + 1.0f / fSystem->fXTiles; + core->fUVCoords[1].fY = yOff + 1.0f / fSystem->fYTiles; + core->fUVCoords[1].fZ = 1.0f; + core->fUVCoords[2].fX = xOff + 1.0f / fSystem->fXTiles; + core->fUVCoords[2].fY = yOff; + core->fUVCoords[2].fZ = 1.0f; + core->fUVCoords[3].fX = xOff; + core->fUVCoords[3].fY = yOff; + core->fUVCoords[3].fZ = 1.0f; + + ext = &fParticleExts[currParticle]; + ext->fVelocity = velocity; + ext->fInvMass = invMass; + ext->fLife = ext->fStartLife = life; + ext->fMiscFlags = miscFlags; // Is this ever NOT zero? + if (life <= 0) + ext->fMiscFlags |= plParticleExt::kImmortal; + + ext->fRadsPerSec = radsPerSec; + ext->fAcceleration.Set(0, 0, 0); + ext->fScale = scale; +} + +void plParticleEmitter::WipeExistingParticles() +{ + fNumValidParticles = 0; // That was easy. +} + +// This method is called from a network received message. Don't trust the args without checking. +void plParticleEmitter::KillParticles(hsScalar num, hsScalar timeToDie, UInt8 flags) +{ + if (flags & plParticleKillMsg::kParticleKillPercentage) + { + if (num < 0) + num = 0; + if (num > 1) + num = 1; + + num *= fNumValidParticles; + } + else + { + if (num < 0) + num = 0; + } + + if (timeToDie < 0) + timeToDie = 0; + + int i; + for (i = 0; i < fNumValidParticles && num > 0; i++) + { + if ((flags & plParticleKillMsg::kParticleKillImmortalOnly) && !(fParticleExts[i].fMiscFlags & plParticleExt::kImmortal)) + continue; + + fParticleExts[i].fLife = fParticleExts[i].fStartLife = timeToDie; + fParticleExts[i].fMiscFlags &= ~plParticleExt::kImmortal; + num--; + } +} + +void plParticleEmitter::UpdateGenerator(UInt32 paramID, hsScalar paramValue) +{ + if (fGenerator != nil) + fGenerator->UpdateParam(paramID, paramValue); +} + +UInt16 plParticleEmitter::StealParticlesFrom(plParticleEmitter *victim, UInt16 num) +{ + UInt16 spaceAvail = (UInt16)(fMaxParticles - fNumValidParticles); + UInt16 numToCopy = (UInt16)(hsMinimum(num, (victim ? victim->fNumValidParticles : 0))); + if (spaceAvail < numToCopy) + numToCopy = spaceAvail; + + if (numToCopy > 0) + { + // copy them over + memcpy(&(fParticleCores[fNumValidParticles]), &(victim->fParticleCores[victim->fNumValidParticles - numToCopy]), numToCopy * sizeof(plParticleCore)); + memcpy(&(fParticleExts[fNumValidParticles]), &(victim->fParticleExts[victim->fNumValidParticles- numToCopy]), numToCopy * sizeof(plParticleExt)); + + fNumValidParticles += numToCopy; + victim->fNumValidParticles -= numToCopy; + } + + return numToCopy; +} + +void plParticleEmitter::TranslateAllParticles(hsPoint3 &amount) +{ + int i; + for (i = 0; i < fNumValidParticles; i++) + fParticleCores[i].fPos += amount; +} + +hsBool plParticleEmitter::IUpdate(hsScalar delta) +{ + if (fMiscFlags & kNeedsUpdate) + { + plProfile_BeginTiming(ParticleUpdate); + + IUpdateParticles(delta); + IUpdateBoundsAndNormals(delta); + + plProfile_EndTiming(ParticleUpdate); + } + + if (fGenerator == nil && fNumValidParticles <= 0) + return false; // no generator, no particles, let the system decide if this emitter is done + else + return true; +} + +void plParticleEmitter::IUpdateParticles(hsScalar delta) +{ + + int i, j; + + // Have to remove particles before adding new ones, or we can run out of room. + for (i = 0; i < fNumValidParticles; i++) + { + fParticleExts[i].fLife -= delta; + if (fParticleExts[i].fLife <= 0 && !(fParticleExts[i].fMiscFlags & plParticleExt::kImmortal)) + { + IRemoveParticle(i); + i--; // so that we hit this index again on the next iteration + continue; + } + } + + fTargetInfo.fFirstNewParticle = fNumValidParticles; + + if ((fGenerator != nil) && (fTimeToLive >= 0)) + { + plProfile_BeginLap(ParticleGenerate, fSystem->GetKeyName()); + if (!fGenerator->AddAutoParticles(this, delta)) + { + delete fGenerator; + fGenerator = nil; + } + if( (fTimeToLive > 0) && ((fTimeToLive -= delta) <= 0) ) + fTimeToLive = -1.f; + plProfile_EndLap(ParticleGenerate, fSystem->GetKeyName()); + } + + fTargetInfo.fContext = fSystem->fContext; + fTargetInfo.fNumValidParticles = fNumValidParticles; + const hsVector3 up(0, 0, 1.0f); + hsVector3 currDirection; + hsPoint3 *currPos; + hsVector3 *currVelocity; + hsVector3 *currAccel; + hsPoint3 color(fColor.r, fColor.g, fColor.b); + hsScalar alpha = fColor.a; + plController *colorCtl; + + // Allow effects a chance to cache any upfront calculations + // that will apply to all particles. + for (j = 0; j < fSystem->fForces.GetCount(); j++) + { + fSystem->fForces[j]->PrepareEffect(fTargetInfo); + } + for (j = 0; j < fSystem->fEffects.GetCount(); j++) + { + fSystem->fEffects[j]->PrepareEffect(fTargetInfo); + } + for (j = 0; j < fSystem->fConstraints.GetCount(); j++) + { + fSystem->fConstraints[j]->PrepareEffect(fTargetInfo); + } + + for (i = 0; i < fNumValidParticles; i++) + { + if (!( fParticleExts[i].fMiscFlags & plParticleExt::kImmortal )) + { + hsScalar percent = (1.0f - fParticleExts[i].fLife / fParticleExts[i].fStartLife); + colorCtl = (fMiscFlags & kMatIsEmissive ? fSystem->fAmbientCtl : fSystem->fDiffuseCtl); + if (colorCtl != nil) + colorCtl->Interp(colorCtl->GetLength() * percent, &color); + + if (fSystem->fOpacityCtl != nil) + { + fSystem->fOpacityCtl->Interp(fSystem->fOpacityCtl->GetLength() * percent, &alpha); + alpha /= 100.0f; + if (alpha < 0) + alpha = 0; + else if (alpha > 1.f) + alpha = 1.f; + } + + if (fSystem->fWidthCtl != nil) + { + fSystem->fWidthCtl->Interp(fSystem->fWidthCtl->GetLength() * percent, + &fParticleCores[i].fHSize); + fParticleCores[i].fHSize *= fParticleExts[i].fScale; + } + if (fSystem->fHeightCtl != nil) + { + fSystem->fHeightCtl->Interp(fSystem->fHeightCtl->GetLength() * percent, + &fParticleCores[i].fVSize); + fParticleCores[i].fVSize *= fParticleExts[i].fScale; + } + + fParticleCores[i].fColor = CreateHexColor(color.fX, color.fY, color.fZ, alpha); + } + + for (j = 0; j < fSystem->fForces.GetCount(); j++) + { + fSystem->fForces[j]->ApplyEffect(fTargetInfo, i); + } + + currPos = (hsPoint3 *)(fTargetInfo.fPos + i * fTargetInfo.fPosStride); + currVelocity = (hsVector3 *)(fTargetInfo.fVelocity + i * fTargetInfo.fVelocityStride); + //currAccel = (hsVector3 *)(fTargetInfo.fAcceleration + i * fTargetInfo.fAccelerationStride); + currAccel = &fSystem->fAccel; // Nothing accellerates on a per-particle basis (yet) + + *currPos += *currVelocity * delta; + + // This is the only orientation option (so far) that requires an update here + if (fMiscFlags & (kOrientationVelocityBased | kOrientationVelocityStretch | kOrientationVelocityFlow)) + fParticleCores[i].fOrientation.Set(&(*currVelocity * delta)); // mf - want the orientation to be a delposition + else if( fParticleExts[i].fRadsPerSec != 0 ) + { + hsScalar sinX, cosX; + hsFastMath::SinCos(fParticleExts[i].fLife * fParticleExts[i].fRadsPerSec * 2.f * hsScalarPI, sinX, cosX); + fParticleCores[i].fOrientation.Set(sinX, -cosX, 0); + } + + // Viscous force F(t) = -k V(t) + // Integral S from t0 to t1 of F(t) is + // = S(-kV(t))[t1..t0] + // = -k(P(t1) - P(t0)) + // = -k*(currVelocity * delta) + // or + // V = V + -k*(V * delta) + // V *= (1 + -k * delta) + // Giving the change in velocity. + hsScalar drag = 1.f + fSystem->fDrag * delta; + // Clamp it at 0. Drag should never cause a reversal in velocity direction. + if( drag < 0.f ) + drag = 0.f; + *currVelocity *= drag; + + *currVelocity += *currAccel * delta; + + for (j = 0; j < fSystem->fEffects.GetCount(); j++) + { + fSystem->fEffects[j]->ApplyEffect(fTargetInfo, i); + } + + // We may need to do more than one iteration through the constraints. It's a trade-off + // between accurracy and speed (what's new?) but I'm going to go with just one + // for now until we decide things don't "look right" + for (j = 0; j < fSystem->fConstraints.GetCount(); j++) + { + if( fSystem->fConstraints[j]->ApplyEffect(fTargetInfo, i) ) + { + IRemoveParticle(i); + i--; // so that we hit this index again on the next iteration + break; + // break will break us out of loop over constraints, + // and since we're last, we move onto next particle. + } + } + } + + // Notify the effects that they are done for now. + for (j = 0; j < fSystem->fForces.GetCount(); j++) + { + fSystem->fForces[j]->EndEffect(fTargetInfo); + } + for (j = 0; j < fSystem->fEffects.GetCount(); j++) + { + fSystem->fEffects[j]->EndEffect(fTargetInfo); + } + for (j = 0; j < fSystem->fConstraints.GetCount(); j++) + { + fSystem->fConstraints[j]->EndEffect(fTargetInfo); + } +} + +plProfile_CreateTimer("Bound", "Particles", ParticleBound); +plProfile_CreateTimer("Normal", "Particles", ParticleNormal); + +void plParticleEmitter::IUpdateBoundsAndNormals(hsScalar delta) +{ + plProfile_BeginTiming(ParticleBound); + fBoundBox.MakeEmpty(); + int i; + for (i = 0; i < fNumValidParticles; i++) + fBoundBox.Union(&fParticleCores[i].fPos); + + hsPoint3 center; + if (fNumValidParticles > 0) + center = fBoundBox.GetCenter(); + plProfile_EndTiming(ParticleBound); + + plProfile_BeginTiming(ParticleNormal); + hsVector3 normal; + if (fMiscFlags & kNormalVelUpVel) + { + for (i = fNumValidParticles - 1; i >=0; i--) + { + //currDirection.Set(&fParticleCores[i].fPos, &fParticleExts[i].fOldPos); + //normal = (currDirection % up % currDirection); + normal.Set(-fParticleExts[i].fVelocity.fX * fParticleExts[i].fVelocity.fZ, + -fParticleExts[i].fVelocity.fY * fParticleExts[i].fVelocity.fZ, + (fParticleExts[i].fVelocity.fX * fParticleExts[i].fVelocity.fX + + fParticleExts[i].fVelocity.fY * fParticleExts[i].fVelocity.fY)); + if (!normal.IsEmpty()) // zero length check + { + normal.Normalize(); + fParticleCores[i].fNormal = normal; + } + } + } + else if (fMiscFlags & kNormalFromCenter) + { + for (i = fNumValidParticles - 1; i >=0; i--) + { + normal.Set(&fParticleCores[i].fPos, ¢er); + if (!normal.IsEmpty()) // zero length check + { + normal.Normalize(); + fParticleCores[i].fNormal = normal; + } + } + } + // otherwise we just keep the last normal. + plProfile_EndTiming(ParticleNormal); +} + +void plParticleEmitter::IRemoveParticle(UInt32 index) +{ + hsAssert(index < fNumValidParticles, "Trying to remove an invalid particle"); + + fNumValidParticles--; + if (fNumValidParticles <= 0) + { + fNumValidParticles = 0; + return; + } + + fParticleCores[index] = fParticleCores[fNumValidParticles]; + fParticleExts[index] = fParticleExts[fNumValidParticles]; +} + +// Reading and writing doesn't transfer individual particle info. We assume those are expendable. +// Only the configuration info necessary to began spawning particles again is saved. +// The particle system that owns this emitter is responsible for setting the pointer back to itself +void plParticleEmitter::Read(hsStream *s, hsResMgr *mgr) +{ + plCreatable::Read(s, mgr); + + fGenerator = plParticleGenerator::ConvertNoRef(mgr->ReadCreatable(s)); + fSpanIndex = s->ReadSwap32(); + fMaxParticles = s->ReadSwap32(); + fMiscFlags = s->ReadSwap32(); + fColor.Read(s); + + if( fMiscFlags & kOnReserve ) + fTimeToLive = -1.f; // Wait for someone to give us a spurt of life. + + ISetupParticleMem(); +} + +void plParticleEmitter::Write(hsStream *s, hsResMgr *mgr) +{ + plCreatable::Write(s, mgr); + + mgr->WriteCreatable(s, fGenerator); + s->WriteSwap32(fSpanIndex); + s->WriteSwap32(fMaxParticles); + s->WriteSwap32(fMiscFlags); + fColor.Write(s); +} + +UInt32 plParticleEmitter::CreateHexColor(const hsColorRGBA &color) +{ + return CreateHexColor(color.r, color.g, color.b, color.a); +} + +UInt32 plParticleEmitter::CreateHexColor(const hsScalar r, const hsScalar g, const hsScalar b, const hsScalar a) +{ + UInt32 ru, gu, bu, au; + + au = (UInt32)(a * 255.0f); + ru = (UInt32)(r * 255.0f); + gu = (UInt32)(g * 255.0f); + bu = (UInt32)(b * 255.0f); + return ( au << 24 ) | ( ru << 16 ) | ( gu << 8 ) | ( bu ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEmitter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEmitter.h new file mode 100644 index 00000000..b81301d1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleEmitter.h @@ -0,0 +1,143 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plParticleEmitter_inc +#define plParticleEmitter_inc + +#include "hsGeometry3.h" +#include "hsBounds.h" +#include "../pnNetCommon/plSynchedValue.h" +#include "../CoreLib/hsColorRGBA.h" + +class hsBounds3Ext; +class plParticleSystem; +class plParticleCore; +class plParticleExt; +class plParticleGenerator; +class plSimpleParticleGenerator; +class hsResMgr; + +#include "plEffectTargetInfo.h" + +// This just holds a bunch of parameters for an emission location. A particle system can have several of these + +class plParticleEmitter : public plCreatable +{ + friend plParticleSystem; + friend plSimpleParticleGenerator; + +public: + plParticleEmitter(); + ~plParticleEmitter(); + void Init(plParticleSystem *system, UInt32 maxParticles, UInt32 spanIndex, UInt32 miscFlags, + plParticleGenerator *gen = nil); + void Clone(plParticleEmitter* src, UInt32 spanIndex); + + plParticleCore *GetParticleArray() const { return fParticleCores; } + UInt32 GetParticleCount() const { return fNumValidParticles; } + UInt32 GetNumTiles() const; + const hsBounds3Ext &GetBoundingBox() const { return fBoundBox; } + UInt32 GetSpanIndex() const { return fSpanIndex; } + const hsMatrix44 &GetLocalToWorld() const; + + void AddParticle(hsPoint3 &pos, hsVector3 &velocity, UInt32 tileIndex, + hsScalar hSize, hsScalar vSize, hsScalar scale, hsScalar invMass, hsScalar life, + hsPoint3 &orientation, UInt32 miscFlags, hsScalar radsPerSec=0); + void WipeExistingParticles(); + void KillParticles(hsScalar num, hsScalar timeToDie, UInt8 flags); + UInt16 StealParticlesFrom(plParticleEmitter *victim, UInt16 num); // returns the number actually stolen + void TranslateAllParticles(hsPoint3 &amount); // Used to recenter the system when linking between ages. + void UpdateGenerator(UInt32 paramID, hsScalar paramValue); + + static UInt32 CreateHexColor(const hsColorRGBA &color); + static UInt32 CreateHexColor(const hsScalar r, const hsScalar g, const hsScalar b, const hsScalar a); + + void OverrideLocalToWorld(const hsMatrix44& l2w); + void UnOverrideLocalToWorld() { fMiscFlags &= ~kOverrideLocalToWorld; } + hsBool LocalToWorldOverridden() const { return 0 != (fMiscFlags & kOverrideLocalToWorld); } + void SetTimeToLive(hsScalar dt) { fTimeToLive = dt; } + hsScalar GetTimeToLive() const { return fTimeToLive; } // 0 time to live is never turn off. + + CLASSNAME_REGISTER( plParticleEmitter ); + GETINTERFACE_ANY( plParticleEmitter, plCreatable); + + virtual void Read(hsStream* s, hsResMgr *mgr); + virtual void Write(hsStream* s, hsResMgr *mgr); + + enum // Miscellaneous flags + { + kMatIsEmissive = 0x00000001, + + kNormalUp = 0x00000010, + kNormalVelUpVel = 0x00000020, + kNormalFromCenter = 0x00000040, + kNormalDynamicMask = kNormalVelUpVel | kNormalFromCenter, // precalc methods that need updating each frame + kNormalPrecalcMask = kNormalDynamicMask | kNormalUp, // All types where emitter precalculates the normal + + kNormalViewFacing = 0x00000100, + kNormalNearestLight = 0x00000200, + + kNeedsUpdate = 0x01000000, + kBorrowedGenerator = 0x02000000, + kOverrideLocalToWorld = 0x04000000, + kOnReserve = 0x08000000, + + kOrientationUp = 0x10000000, + kOrientationVelocityBased = 0x20000000, + kOrientationVelocityStretch = 0x40000000, + kOrientationVelocityFlow = 0x80000000, + kOrientationVelocityMask = kOrientationVelocityBased | kOrientationVelocityStretch | kOrientationVelocityFlow, // Velocity dependent + kOrientationMask = kOrientationUp | kOrientationVelocityMask, + }; + UInt32 fMiscFlags; + +protected: + + plParticleSystem *fSystem; // The particle system this belongs to. + plParticleCore *fParticleCores; // The particle pool, created on init, initialized as needed, and recycled. + plParticleExt *fParticleExts; // Same mapping as the Core pool. Contains extra info the render pipeline + // doesn't need. + + plParticleGenerator *fGenerator; // Optional auto generator (have this be nil if you don't want auto-generation) + UInt32 fSpanIndex; // Index of the span that this emitter uses. + UInt32 fNumValidParticles; + UInt32 fMaxParticles; + hsBounds3Ext fBoundBox; + plEffectTargetInfo fTargetInfo; // A collection of pointers and strides that plParticleEffects will manipulate. + hsColorRGBA fColor; + + hsMatrix44 fLocalToWorld; + hsScalar fTimeToLive; + + void IClear(); + void ISetupParticleMem(); + void ISetSystem(plParticleSystem *sys) { fSystem = sys; } + hsBool IUpdate(hsScalar delta); + void IUpdateParticles(hsScalar delta); + void IUpdateBoundsAndNormals(hsScalar delta); + void IRemoveParticle(UInt32 index); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleGenerator.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleGenerator.cpp new file mode 100644 index 00000000..ab782311 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleGenerator.cpp @@ -0,0 +1,438 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsGeometry3.h" +#include "hsStream.h" +#include "hsFastMath.h" +#include "hsUtils.h" +#include "plParticle.h" +#include "plParticleSystem.h" +#include "plParticleEmitter.h" +#include "plParticleGenerator.h" +#include "../CoreLib/hsColorRGBA.h" +#include "../plMessage/plParticleUpdateMsg.h" +#include "../plInterp/plController.h" +#include "hsResMgr.h" +#include "../plMath/plRandom.h" + +static const hsScalar DEFAULT_INVERSE_MASS = 1.f; + +static plRandom sRandom; + +const void plParticleGenerator::ComputeDirection(float pitch, float yaw, hsVector3 &direction) +{ + hsScalar cosPitch, sinPitch; + hsScalar cosYaw, sinYaw; + hsFastMath::SinCos(pitch, sinPitch, cosPitch); + hsFastMath::SinCos(yaw, sinYaw, cosYaw); + + direction.Set(-sinYaw * cosPitch, sinPitch, cosPitch * cosYaw); +} + +// Inverse function of ComputeDirection. Give it a normalized vector, and it will tell you a +// pitch and yaw (angles for the unit Z vector) to get there. +const void plParticleGenerator::ComputePitchYaw(float &pitch, float &yaw, const hsVector3 &dir) +{ + const float PI = 3.14159f; + pitch = asin(dir.fY); + float cos_pitch = cos(pitch); + if (cos_pitch == 0) + { + yaw = 0; + return; + } + float inv = -dir.fX / cos_pitch; + if (inv > 1.0f) + inv = 1.0f; + if (inv < -1.0f) + inv = -1.0f; + yaw = asin(inv); + if (dir.fZ < 0) + yaw = PI - yaw; +} + +plSimpleParticleGenerator::plSimpleParticleGenerator() +{ +} + +plSimpleParticleGenerator::~plSimpleParticleGenerator() +{ + delete [] fInitPos; + delete [] fInitPitch; + delete [] fInitYaw; +} + +void plSimpleParticleGenerator::Init(hsScalar genLife, hsScalar partLifeMin, hsScalar partLifeMax, + hsScalar particlesPerSecond, UInt32 numSources, hsPoint3 *initPos, + hsScalar *initPitch, hsScalar *initYaw, hsScalar angleRange, + hsScalar initVelMin, hsScalar initVelMax, + hsScalar xSize, hsScalar ySize, + hsScalar scaleMin, hsScalar scaleMax, + hsScalar massRange, hsScalar radsPerSecRange) +{ + fGenLife = genLife; + fPartLifeMin = partLifeMin; + fPartLifeMax = partLifeMax; + fParticlesPerSecond = particlesPerSecond; + fNumSources = numSources; + fInitPos = initPos; + fInitPitch = initPitch; + fInitYaw = initYaw; + fAngleRange = angleRange; + fVelMin = initVelMin; + fVelMax = initVelMax; + fXSize = xSize; + fYSize = ySize; + fScaleMin = scaleMin; + fScaleMax = scaleMax; + + fPartInvMassMin = 1.f / (DEFAULT_INVERSE_MASS + massRange); + fPartInvMassRange = 1.f / DEFAULT_INVERSE_MASS - fPartInvMassMin; + + fPartRadsPerSecRange = radsPerSecRange; + + fParticleSum = 0; + fMiscFlags = 0; + if (fGenLife < 0) fMiscFlags |= kImmortal; +} + +hsBool plSimpleParticleGenerator::AddAutoParticles(plParticleEmitter *emitter, float dt, UInt32 numForced /* = 0 */) +{ + Int32 numNewParticles; + + if (numForced == 0) + { + fGenLife -= dt; + if ((fGenLife < 0 && !(fMiscFlags & kImmortal)) || (fMiscFlags & kDisabled)) + return true; // Leave it around so that a message can bring it back to life. + + fParticleSum += fParticlesPerSecond * dt; + numNewParticles = (Int32)fParticleSum; + + if (numNewParticles <= 0 || fParticlesPerSecond == 0) + return true; + } + else + { + numNewParticles = numForced; + } + + UInt32 miscFlags = 0; + hsPoint3 currStart; + fParticleSum -= numNewParticles; + + hsPoint3 orientation; + hsVector3 initDirection; + hsScalar vel = (fVelMax + fVelMin) * 0.5f; + hsScalar velRange = vel - fVelMin; + hsScalar initVelocity; + hsScalar initLife; + hsScalar life = (fPartLifeMax + fPartLifeMin) * 0.5f; + hsScalar lifeRange = life - fPartLifeMin; + hsScalar currSizeVar; + hsScalar scale = (fScaleMax + fScaleMin) * 0.5f; + hsScalar scaleRange = scale - fScaleMin; + hsScalar radsPerSec = 0; + UInt32 tile; + UInt32 sourceIndex; + + const hsScalar lifeDiff = dt / numNewParticles; + hsScalar lifeSoFar; + int i; + for (i = 0, lifeSoFar = 0; i < numNewParticles; i++, lifeSoFar += lifeDiff) + { + initLife = life + lifeRange * sRandom.RandMinusOneToOne() - lifeSoFar; + + // Careful here... if we're supposed to generate immortal particles, we do so + // by giving them a negative life. This is different that generating one with + // a positive lifetime that is now negative because of "lifeSoFar". The if is + // saying "if it's dead, but it was alive before we took away lifeSoFar, ignore it" + if (initLife <= 0 && initLife + lifeSoFar >= 0) + continue; + + sourceIndex = (UInt32)(sRandom.RandZeroToOne() * fNumSources); + + ComputeDirection(fInitPitch[sourceIndex] + fAngleRange * sRandom.RandMinusOneToOne(), + fInitYaw[sourceIndex] + fAngleRange * sRandom.RandMinusOneToOne(), initDirection); + initDirection = emitter->GetLocalToWorld() * initDirection; + initVelocity = (vel + velRange * sRandom.RandMinusOneToOne()); + + currStart = (emitter->GetLocalToWorld() * fInitPos[sourceIndex]) + + (initDirection * initVelocity * lifeSoFar) // Vo * t + + (emitter->fSystem->fAccel * lifeSoFar * lifeSoFar); // at^2 + + if (emitter->fMiscFlags & emitter->kOrientationUp) + orientation.Set(0.0f, -1.0f, 0.0f); + else + orientation.Set(&initDirection); + + tile = (UInt32)(sRandom.RandZeroToOne() * emitter->GetNumTiles()); + currSizeVar = scale + scaleRange * sRandom.RandMinusOneToOne(); + + hsScalar invMass = fPartInvMassMin; + // Might be faster to just do the math instead of checking for zero... + if( fPartInvMassRange > 0 ) + invMass += fPartInvMassRange * sRandom.RandZeroToOne(); + + if( fPartRadsPerSecRange > 0 ) + radsPerSec = fPartRadsPerSecRange * sRandom.RandMinusOneToOne(); + + emitter->AddParticle(currStart, initDirection * initVelocity, tile, fXSize, fYSize, currSizeVar, + invMass, initLife, orientation, miscFlags, radsPerSec); + } + + return true; +} + +void plSimpleParticleGenerator::UpdateParam(UInt32 paramID, hsScalar paramValue) +{ + switch (paramID) + { + case plParticleUpdateMsg::kParamParticlesPerSecond: + fParticlesPerSecond = paramValue; + break; + case plParticleUpdateMsg::kParamInitPitchRange: + case plParticleUpdateMsg::kParamInitYawRange: + fAngleRange = paramValue; + break; +// case plParticleUpdateMsg::kParamInitVel: +// fInitVel = paramValue; +// break; +// case plParticleUpdateMsg::kParamInitVelRange: +// fInitVelRange = paramValue; +// break; + case plParticleUpdateMsg::kParamVelMin: + fVelMin = paramValue; + break; + case plParticleUpdateMsg::kParamVelMax: + fVelMax = paramValue; + break; + case plParticleUpdateMsg::kParamXSize: + fXSize = paramValue; + break; + case plParticleUpdateMsg::kParamYSize: + fYSize = paramValue; + break; +// case plParticleUpdateMsg::kParamSizeRange: +// fSizeRange = paramValue; +// break; + case plParticleUpdateMsg::kParamScaleMin: + fScaleMin = paramValue; + break; + case plParticleUpdateMsg::kParamScaleMax: + fScaleMax = paramValue; + break; + case plParticleUpdateMsg::kParamGenLife: + fGenLife = paramValue; + if (fGenLife < 0) + fMiscFlags |= kImmortal; + else + fMiscFlags &= ~kImmortal; + break; +// case plParticleUpdateMsg::kParamPartLife: +// fPartLife = paramValue; +// if (fPartLife < 0) +// fPartLifeRange = 0; +// break; +// case plParticleUpdateMsg::kParamPartLifeRange: +// fPartLifeRange = paramValue; +// break; + case plParticleUpdateMsg::kParamPartLifeMin: + fPartLifeMin = paramValue; + break; + case plParticleUpdateMsg::kParamPartLifeMax: + fPartLifeMax = paramValue; + break; + case plParticleUpdateMsg::kParamEnabled: + if (paramValue == 0.f) + fMiscFlags |= kDisabled; + else + fMiscFlags &= ~kDisabled; + break; + default: + break; + } +} + +void plSimpleParticleGenerator::Read(hsStream* s, hsResMgr *mgr) +{ + hsScalar genLife = s->ReadSwapScalar(); + hsScalar partLifeMin = s->ReadSwapScalar(); + hsScalar partLifeMax = s->ReadSwapScalar(); + hsScalar pps = s->ReadSwapScalar(); + UInt32 numSources = s->ReadSwap32(); + hsPoint3 *pos = TRACKED_NEW hsPoint3[numSources]; + hsScalar *pitch = TRACKED_NEW hsScalar[numSources]; + hsScalar *yaw = TRACKED_NEW hsScalar[numSources]; + int i; + for (i = 0; i < numSources; i++) + { + pos[i].Read(s); + pitch[i] = s->ReadSwapScalar(); + yaw[i] = s->ReadSwapScalar(); + } + hsScalar angleRange = s->ReadSwapScalar(); + hsScalar velMin = s->ReadSwapScalar(); + hsScalar velMax = s->ReadSwapScalar(); + hsScalar xSize = s->ReadSwapScalar(); + hsScalar ySize = s->ReadSwapScalar(); + hsScalar scaleMin = s->ReadSwapScalar(); + hsScalar scaleMax = s->ReadSwapScalar(); + hsScalar massRange = s->ReadSwapScalar(); + hsScalar radsPerSec = s->ReadSwapScalar(); + + Init(genLife, partLifeMin, partLifeMax, pps, numSources, pos, pitch, yaw, angleRange, velMin, velMax, + xSize, ySize, scaleMin, scaleMax, massRange, radsPerSec); +} + +void plSimpleParticleGenerator::Write(hsStream* s, hsResMgr *mgr) +{ + s->WriteSwapScalar(fGenLife); + s->WriteSwapScalar(fPartLifeMin); + s->WriteSwapScalar(fPartLifeMax); + s->WriteSwapScalar(fParticlesPerSecond); + s->WriteSwap32(fNumSources); + int i; + for (i = 0; i < fNumSources; i++) + { + fInitPos[i].Write(s); + s->WriteSwapScalar(fInitPitch[i]); + s->WriteSwapScalar(fInitYaw[i]); + } + s->WriteSwapScalar(fAngleRange); + s->WriteSwapScalar(fVelMin); + s->WriteSwapScalar(fVelMax); + s->WriteSwapScalar(fXSize); + s->WriteSwapScalar(fYSize); + s->WriteSwapScalar(fScaleMin); + s->WriteSwapScalar(fScaleMax); + + hsScalar massRange = 1.f / fPartInvMassMin - DEFAULT_INVERSE_MASS; + s->WriteSwapScalar(massRange); + s->WriteSwapScalar(fPartRadsPerSecRange); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +plOneTimeParticleGenerator::plOneTimeParticleGenerator() +{ +} + +plOneTimeParticleGenerator::~plOneTimeParticleGenerator() +{ + delete [] fPosition; + delete [] fDirection; +} + +void plOneTimeParticleGenerator::Init(hsScalar count, hsPoint3 *pointArray, hsVector3 *dirArray, + hsScalar xSize, hsScalar ySize, hsScalar scaleMin, hsScalar scaleMax, hsScalar radsPerSecRange) +{ + fCount = count; + fPosition = pointArray; + fDirection = dirArray; + fXSize = xSize; + fYSize = ySize; + fScaleMin = scaleMin; + fScaleMax = scaleMax; + fPartRadsPerSecRange = radsPerSecRange; +} + +// The numForced param is required by the parent class, but ignored by this particular generator +hsBool plOneTimeParticleGenerator::AddAutoParticles(plParticleEmitter *emitter, float dt, UInt32 numForced /* = 0 */) +{ + hsScalar currSizeVar; + hsScalar scale = (fScaleMax + fScaleMin) / 2; + hsScalar scaleRange = scale - fScaleMin; + + hsScalar tile; + hsPoint3 currStart; + hsPoint3 orientation; + hsVector3 initDirection; + hsVector3 zeroVel(0.f, 0.f, 0.f); + hsScalar radsPerSec = 0; + + int i; + for (i = 0; i < fCount; i++) + { + currStart = emitter->GetLocalToWorld() * fPosition[i]; + initDirection = emitter->GetLocalToWorld() * fDirection[i]; + + if (emitter->fMiscFlags & emitter->kOrientationUp) + orientation.Set(0.0f, -1.0f, 0.0f); + else + orientation.Set(&initDirection); + + tile = (hsScalar)(sRandom.Rand() % emitter->GetNumTiles()); + currSizeVar = scale + scaleRange * sRandom.RandMinusOneToOne(); + + if( fPartRadsPerSecRange > 0 ) + radsPerSec = fPartRadsPerSecRange * sRandom.RandMinusOneToOne(); + + emitter->AddParticle(currStart, zeroVel, (UInt32)tile, fXSize, fYSize, currSizeVar, + DEFAULT_INVERSE_MASS, -1, orientation, 0, radsPerSec); + } + emitter->fMiscFlags &= ~plParticleEmitter::kNeedsUpdate; + return false; // We've done our one-time job. Let the emitter know to delete us. +} + +void plOneTimeParticleGenerator::Read(hsStream* s, hsResMgr *mgr) +{ + UInt32 count = s->ReadSwap32(); + hsScalar xSize = s->ReadSwapScalar(); + hsScalar ySize = s->ReadSwapScalar(); + hsScalar scaleMin = s->ReadSwapScalar(); + hsScalar scaleMax = s->ReadSwapScalar(); + hsScalar radsPerSecRange = s->ReadSwapScalar(); + + hsPoint3 *pos = TRACKED_NEW hsPoint3[count]; + hsVector3 *dir = TRACKED_NEW hsVector3[count]; + + int i; + for (i = 0; i < count; i++) + { + pos[i].Read(s); + dir[i].Read(s); + } + + Init((hsScalar)count, pos, dir, xSize, ySize, scaleMin, scaleMax, radsPerSecRange); +} + +void plOneTimeParticleGenerator::Write(hsStream* s, hsResMgr *mgr) +{ + s->WriteSwap32((UInt32)fCount); + s->WriteSwapScalar(fXSize); + s->WriteSwapScalar(fYSize); + s->WriteSwapScalar(fScaleMin); + s->WriteSwapScalar(fScaleMax); + s->WriteSwapScalar(fPartRadsPerSecRange); + + int i; + for (i = 0; i < fCount; i++) + { + fPosition[i].Write(s); + fDirection[i].Write(s); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleGenerator.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleGenerator.h new file mode 100644 index 00000000..4acdadc7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleGenerator.h @@ -0,0 +1,132 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plParticleGenerator_inc +#define plParticleGenerator_inc + +#include "hsGeometry3.h" +#include "../pnNetCommon/plSynchedValue.h" +class plParticleEmitter; +class plScalarController; + +// This class is responsible for all the details of automatically generating particles for a plParticleEmitter. +// It gets a change in time, and must do whatever necessary to generate the appropriate number of particles +// for that timespan + +class plParticleGenerator : public plCreatable +{ + +public: + // returns false if it's done generating particles and is safe to delete. + virtual hsBool AddAutoParticles(plParticleEmitter *emitter, float dt, UInt32 numForced = 0) = 0; + + virtual void UpdateParam(UInt32 paramID, hsScalar paramValue) = 0; + + CLASSNAME_REGISTER( plParticleGenerator ); + GETINTERFACE_ANY( plParticleGenerator, plCreatable ); + + static const void ComputeDirection(float pitch, float yaw, hsVector3 &direction); + static const void ComputePitchYaw(float &pitch, float &yaw, const hsVector3 &dir); + static inline float GetRandomVar() { return 2.0f * (float)hsRand() / RAND_MAX - 1; } // returns a num between +/- 1.0 +}; + +class plSimpleParticleGenerator : public plParticleGenerator +{ +public: + plSimpleParticleGenerator(); + ~plSimpleParticleGenerator(); + void Init(hsScalar genLife, hsScalar partLifeMin, hsScalar partLifeMax, hsScalar particlesPerSecond, + UInt32 numSources, hsPoint3 *pos, hsScalar *initPitch, hsScalar *initYaw, hsScalar angleRange, + hsScalar initVelMin, hsScalar initVelMax, hsScalar xSize, hsScalar ySize, + hsScalar scaleMin, hsScalar scaleMax, + hsScalar massRange, hsScalar radsPerSecRange); + + CLASSNAME_REGISTER( plSimpleParticleGenerator ); + GETINTERFACE_ANY( plSimpleParticleGenerator, plParticleGenerator); + + virtual hsBool AddAutoParticles(plParticleEmitter *emitter, float dt, UInt32 numForced); + virtual void UpdateParam(UInt32 paramID, hsScalar paramValue); + + virtual void Read(hsStream* s, hsResMgr *mgr); + virtual void Write(hsStream* s, hsResMgr *mgr); + +protected: + hsScalar fParticlesPerSecond; + UInt32 fNumSources; + hsPoint3 *fInitPos; + hsScalar *fInitPitch, *fInitYaw; + hsScalar fAngleRange; + hsScalar fVelMin, fVelMax; + hsScalar fXSize, fYSize, fScaleMin, fScaleMax; + hsScalar fGenLife; // How long shall we spit out particles from this location? When this time runs out, we stop + // spitting particles, but we don't actually die until all of our particles die naturally. + // (Even the ones that we feel are suffering needlessly.) + + hsScalar fPartLifeMin; // lifespan for the particles we generate + hsScalar fPartLifeMax; + + hsScalar fPartInvMassMin; // Doing a uniform variant over the inverse mass range (instead of over the mass range + hsScalar fPartInvMassRange; // and then inverting) will favor the low end of the mass range, but then again, + // it's just a freaking game. Note though that fPartInvMassMin == 1.f / massMAX. + + hsScalar fPartRadsPerSecRange; // Zero means no rot, otherwise uniform random between [-range..range] + + hsScalar fParticleSum; + + enum + { + kImmortal = 0x1, + kDisabled = 0x2, + }; + UInt32 fMiscFlags; +}; + +class plOneTimeParticleGenerator : public plParticleGenerator +{ +public: + + plOneTimeParticleGenerator(); + ~plOneTimeParticleGenerator(); + void Init(hsScalar count, hsPoint3 *pointArray, hsVector3 *dirArray, + hsScalar xSize, hsScalar ySize, hsScalar scaleMin, hsScalar scaleMax, hsScalar radsPerSec); + + CLASSNAME_REGISTER( plOneTimeParticleGenerator ); + GETINTERFACE_ANY( plOneTimeParticleGenerator, plParticleGenerator); + + virtual hsBool AddAutoParticles(plParticleEmitter *emitter, float dt, UInt32 numForced = 0); + virtual void UpdateParam(UInt32 paramID, hsScalar paramValue) {} + + virtual void Read(hsStream* s, hsResMgr *mgr); + virtual void Write(hsStream* s, hsResMgr *mgr); + +protected: + hsScalar fCount; + hsPoint3 *fPosition; + hsVector3 *fDirection; + hsScalar fXSize, fYSize, fScaleMin, fScaleMax; + hsScalar fPartRadsPerSecRange; // Zero means no rot, otherwise uniform random between [-range..range] +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSDLMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSDLMod.cpp new file mode 100644 index 00000000..fc7d7d2d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSDLMod.cpp @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plParticleSDLMod.h" +#include "plParticleSystem.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../plSDL/plSDL.h" +#include "../pnKeyedObject/plKey.h" + +// static vars +char plParticleSDLMod::kStrNumParticles[]="numParticles"; + +void plParticleSDLMod::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + plSceneObject* sobj=GetTarget(); + if (!sobj) + return; + + UInt32 flags = sobj->GetKey()->GetUoid().GetLocation().GetFlags(); + + const plParticleSystem *sys = plParticleSystem::ConvertNoRef(sobj->GetModifierByType(plParticleSystem::Index())); + if (!sys) + return; + + int num = sys->GetNumValidParticles(true); + dstState->FindVar(kStrNumParticles)->Set(num); +} + +void plParticleSDLMod::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + plSceneObject* sobj=GetTarget(); + if (!sobj) + return; + + plParticleSystem *sys = const_cast(plParticleSystem::ConvertNoRef(sobj->GetModifierByType(plParticleSystem::Index()))); + if (!sys) + return; + + int num; + srcState->FindVar(kStrNumParticles)->Get(&num); + if (num > sys->GetMaxTotalParticles()) + num = sys->GetMaxTotalParticles(); + + sys->WipeExistingParticles(); + sys->GenerateParticles(num); +} + +UInt32 plParticleSDLMod::IApplyModFlags(UInt32 sendFlags) +{ + if (fAttachedToAvatar) + return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState); + return sendFlags; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSDLMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSDLMod.h new file mode 100644 index 00000000..2b6132c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSDLMod.h @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plParticleSDLMod_inc +#define plParticleSDLMod_inc + +#include "../plModifier/plSDLModifier.h" + +// +// This modifier is responsible for sending and recving +// particle system state. Few need it. Little state is needed. +// + +class plParticleSDLMod : public plSDLModifier +{ +private: + bool fAttachedToAvatar; + +protected: + void IPutCurrentStateIn(plStateDataRecord* dstState); + void ISetCurrentStateFrom(const plStateDataRecord* srcState); + UInt32 IApplyModFlags(UInt32 sendFlags); +public: + // var labels + static char kStrNumParticles[]; + + CLASSNAME_REGISTER( plParticleSDLMod ); + GETINTERFACE_ANY( plParticleSDLMod, plSDLModifier); + + plParticleSDLMod(bool attachedToAvatar = false): fAttachedToAvatar(attachedToAvatar) {} + + void PutCurrentStateIn(plStateDataRecord* dstState); + const char* GetSDLName() const { return kSDLParticleSystem; } + + void SetAttachedToAvatar(bool attached) {fAttachedToAvatar = attached;} +}; + +#endif // plParticleSDLMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.cpp new file mode 100644 index 00000000..b36b1967 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.cpp @@ -0,0 +1,701 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plParticleSystem.h" +#include "plParticleEmitter.h" +#include "plParticleGenerator.h" +#include "plParticleEffect.h" +#include "plParticle.h" +#include "plParticleSDLMod.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnMessage/plTimeMsg.h" +#include "../plMessage/plRenderMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../plMessage/plParticleUpdateMsg.h" +#include "../plInterp/plController.h" +#include "../plSurface/hsGMaterial.h" +#include "plPipeline.h" +#include "hsTimer.h" +#include "plProfile.h" +#include "plTweak.h" + +#include "../plDrawable/plParticleFiller.h" + +plProfile_CreateCounter("Num Particles", "Particles", NumParticles); + +const hsScalar plParticleSystem::GRAVITY_ACCEL_FEET_PER_SEC2 = 32.0f; + +plParticleSystem::plParticleSystem() : fParticleSDLMod(nil), fAttachedToAvatar(false) +{ +} + +plParticleSystem::~plParticleSystem() +{ + int i; + for (i = 0; i < fNumValidEmitters; i++) + { + delete fEmitters[i]; + } + delete [] fEmitters; + + delete fAmbientCtl; + delete fDiffuseCtl; + delete fOpacityCtl; + delete fWidthCtl; + delete fHeightCtl; +} + +void plParticleSystem::Init(UInt32 xTiles, UInt32 yTiles, UInt32 maxTotalParticles, UInt32 maxEmitters, + plController *ambientCtl, plController *diffuseCtl, plController *opacityCtl, + plController *widthCtl, plController *heightCtl) +{ + fTarget = nil; + fLastTime = 0; + fCurrTime = 0; + SetGravity(1.0f); + fDrag = 0; + fWindMult = 1.f; + + fNumValidEmitters = 0; + fNextEmitterToGo = 0; + fMiscFlags = 0; + + fForces.Reset(); + fEffects.Reset(); + fConstraints.Reset(); + + fXTiles = xTiles; + fYTiles = yTiles; + + fMaxTotalParticles = fMaxTotalParticlesLeft = maxTotalParticles; + fMaxEmitters = maxEmitters; + fEmitters = TRACKED_NEW plParticleEmitter *[fMaxEmitters]; + int i; + for (i = 0; i < maxEmitters; i++) + fEmitters[i] = nil; + + fAmbientCtl = ambientCtl; + fDiffuseCtl = diffuseCtl; + fOpacityCtl = opacityCtl; + fWidthCtl = widthCtl; + fHeightCtl = heightCtl; +} + +void plParticleSystem::IAddEffect(plParticleEffect *effect, UInt32 type) +{ + switch(type) + { + case kEffectForce: + fForces.Append(effect); + break; + case kEffectMisc: + default: + fEffects.Append(effect); + break; + case kEffectConstraint: + fConstraints.Append(effect); + break; + } +} + +plParticleEmitter* plParticleSystem::GetAvailEmitter() +{ + if( !fNumValidEmitters ) // got to start with at least one. + return nil; + + hsScalar minTTL = 1.e33; + int iMinTTL = -1; + int i; + for( i = 0; i < fNumValidEmitters; i++ ) + { + if( fEmitters[i]->GetTimeToLive() < minTTL ) + { + minTTL = fEmitters[i]->GetTimeToLive(); + iMinTTL = i; + } + } + if( minTTL > 0 ) + { + if( fNumValidEmitters < fMaxEmitters ) + { + minTTL = 0; + iMinTTL = fNumValidEmitters++; + fEmitters[iMinTTL] = TRACKED_NEW plParticleEmitter(); + fEmitters[iMinTTL]->Clone(fEmitters[0], iMinTTL); + + fMaxTotalParticlesLeft -= fEmitters[iMinTTL]->fMaxParticles; + hsAssert(fMaxTotalParticlesLeft >= 0, "Should have planned better"); + + // Don't really use this. fEmitters[i]->GetSpanIndex() always == i. + fNextEmitterToGo = (fNextEmitterToGo + 1) % fMaxEmitters; + } + } + return fEmitters[iMinTTL]; +} + +UInt32 plParticleSystem::AddEmitter(UInt32 maxParticles, plParticleGenerator *gen, UInt32 emitterFlags) +{ + if (fMaxEmitters == 0) // silly rabbit, Trix are for kids! + return 0; + UInt32 currEmitter; + if (fNumValidEmitters == fMaxEmitters) // No more free spots, snag the next in line. + { + int i; + for (i = 0; i < fMaxEmitters; i++) + { + if (fEmitters[i]->GetSpanIndex() == fNextEmitterToGo) + break; + } + currEmitter = i; + + fMaxTotalParticlesLeft += fEmitters[currEmitter]->fMaxParticles; + hsAssert(fMaxTotalParticlesLeft <= fMaxTotalParticles, "Particle system somehow has more particles than it started with."); + delete fEmitters[currEmitter]; + } + else + { + currEmitter = fNumValidEmitters; + fNumValidEmitters++; + } + + if (maxParticles > fMaxTotalParticlesLeft) + maxParticles = fMaxTotalParticlesLeft; + if (maxParticles < 0) + maxParticles = 0; + + fEmitters[currEmitter] = TRACKED_NEW plParticleEmitter(); + fEmitters[currEmitter]->Init(this, maxParticles, fNextEmitterToGo, emitterFlags, gen); + + fMaxTotalParticlesLeft -= fEmitters[currEmitter]->fMaxParticles; + fNextEmitterToGo = (fNextEmitterToGo + 1) % fMaxEmitters; + + return maxParticles; +} + +void plParticleSystem::AddParticle(hsPoint3 &pos, hsVector3 &velocity, UInt32 tileIndex, + hsScalar hSize, hsScalar vSize, hsScalar scale, hsScalar invMass, hsScalar life, + hsPoint3 &orientation, UInt32 miscFlags, hsScalar radsPerSec) +{ + hsAssert(fNumValidEmitters > 0, "Trying to explicitly add particles to a system with no valid emitters."); + if (fNumValidEmitters == 0) + return; + + fEmitters[0]->AddParticle(pos, velocity, tileIndex, hSize, vSize, scale, invMass, life, orientation, miscFlags, radsPerSec); +} + +void plParticleSystem::GenerateParticles(UInt32 num, hsScalar dt /* = 0.f */) +{ + if (num <= 0) + return; + + if (fNumValidEmitters > 0 && fEmitters[0]->fGenerator) + fEmitters[0]->fGenerator->AddAutoParticles(fEmitters[0], dt, num); + + GetTarget(0)->DirtySynchState(kSDLParticleSystem, 0); +} + +void plParticleSystem::WipeExistingParticles() +{ + int i; + for (i = 0; i < fNumValidEmitters; i++) + fEmitters[i]->WipeExistingParticles(); +} + +void plParticleSystem::KillParticles(hsScalar num, hsScalar timeToDie, UInt8 flags) +{ + if (fEmitters[0]) + fEmitters[0]->KillParticles(num, timeToDie, flags); + + GetTarget(0)->DirtySynchState(kSDLParticleSystem, 0); +} + +void plParticleSystem::TranslateAllParticles(hsPoint3 &amount) +{ + int i; + for (i = 0; i < fNumValidEmitters; i++) + fEmitters[i]->TranslateAllParticles(amount); +} + +void plParticleSystem::DisableGenerators() +{ + int i; + for (i = 0; i < fNumValidEmitters; i++) + fEmitters[i]->UpdateGenerator(plParticleUpdateMsg::kParamEnabled, 0.f); +} + +UInt16 plParticleSystem::StealParticlesFrom(plParticleSystem *victim, UInt16 num) +{ + if (fNumValidEmitters <= 0) + return 0; // you just lose + + if (victim) + { + UInt16 numStolen = fEmitters[0]->StealParticlesFrom(victim->fNumValidEmitters > 0 ? victim->fEmitters[0] : nil, num); + GetTarget(0)->DirtySynchState(kSDLParticleSystem, 0); + victim->GetTarget(0)->DirtySynchState(kSDLParticleSystem, 0); + return numStolen; + } + + return 0; +} + +plParticleGenerator *plParticleSystem::GetExportedGenerator() const +{ + return (fNumValidEmitters > 0 ? fEmitters[0]->fGenerator : nil); +} + +plParticleEffect *plParticleSystem::GetEffect(UInt16 type) const +{ + int i; + for (i = 0; i < fForces.GetCount(); i++) + if (fForces[i]->ClassIndex() == type) + return fForces[i]; + + for (i = 0; i < fEffects.GetCount(); i++) + if (fEffects[i]->ClassIndex() == type) + return fEffects[i]; + + for (i = 0; i < fConstraints.GetCount(); i++) + if (fConstraints[i]->ClassIndex() == type) + return fConstraints[i]; + + return nil; +} + +UInt32 plParticleSystem::GetNumValidParticles(hsBool immortalOnly /* = false */) const +{ + UInt32 count = 0; + int i, j; + for (i = 0; i < fNumValidEmitters; i++) + { + if (immortalOnly) + { + for (j = 0; j < fEmitters[i]->fNumValidParticles; j++) + { + if (fEmitters[i]->fParticleExts[j].fMiscFlags & plParticleExt::kImmortal) + count++; + } + } + else + count += fEmitters[i]->fNumValidParticles; + } + return count; +} + + +const hsMatrix44 &plParticleSystem::GetLocalToWorld() const +{ + return fTarget->GetCoordinateInterface()->GetLocalToWorld(); +} + +hsBool plParticleSystem::IEval(double secs, hsScalar del, UInt32 dirty) +{ + return false; +} + + + +hsBool plParticleSystem::IShouldUpdate(plPipeline* pipe) const +{ + + if (fMiscFlags & kParticleSystemAlwaysUpdate) + return true; + + if (IGetTargetDrawInterface(0) && IGetTargetDrawInterface(0)->GetProperty(plDrawInterface::kDisable)) + return false; + + // First, what are the cumulative bounds for this system. + hsBounds3Ext wBnd; + wBnd.MakeEmpty(); + int i; + for( i = 0; i < fNumValidEmitters; i++ ) + { + if( fEmitters[i]->GetBoundingBox().GetType() == kBoundsNormal ) + wBnd.Union(&fEmitters[i]->GetBoundingBox()); + } + + // Always update if we are currently empty + if( wBnd.GetType() == kBoundsEmpty ) + { + return true; + } + + // Now, are we visible? + hsBool isVisible = pipe->TestVisibleWorld(wBnd); + + hsScalar delta = fLastTime > 0 ? hsScalar(fCurrTime - fLastTime) : hsTimer::GetDelSysSeconds(); + if( isVisible ) + { + // If we know how fast the fastest particle is moving, then we can + // decide if the system is too far away to need to update every single frame. + // In fact, based on the speed of the fastest particle, we can determine an + // exact update rate for a given error (say 3 pixels?). + // But we don't currently know the speed of the fastest particle, so + // until we do, I'm commenting this out. Most of the speed gain from + // culling particle updates was from not updating systems that aren't visible + // anyway. +#if 0 // ALWAYS_IF_VISIBLE + // We're in view, but how close are we to the camera? Look at closest point. + hsPoint2 depth; + wBnd.TestPlane(pipe->GetViewDirWorld(), depth); + hsScalar eyeDist = pipe->GetViewDirWorld().InnerProduct(pipe->GetViewPositionWorld()); + hsScalar dist = depth.fX - eyeDist; + + static hsScalar kUpdateCutoffDist = 100.f; + if( dist > kUpdateCutoffDist ) + { + static hsScalar kDistantUpdateSecs = 0.1f; + return delta >= kDistantUpdateSecs; + } +#endif // ALWAYS_IF_VISIBLE + + return true; + } + + + static hsScalar kOffscreenUpdateSecs = 1.f; + return delta >= kOffscreenUpdateSecs; +} + +plDrawInterface* plParticleSystem::ICheckDrawInterface() +{ + plDrawInterface* di = IGetTargetDrawInterface(0); + if( !di ) + return nil; + + if( di->GetDrawableMeshIndex(0) == UInt32(-1) ) + { + di->SetUpForParticleSystem( fMaxEmitters + 1, fMaxTotalParticles, fTexture, fPermaLights ); + hsAssert(di->GetDrawableMeshIndex( 0 ) != (UInt32)-1, "SetUpForParticleSystem should never fail"); // still invalid, didn't fix it. + } + + return di; +} + +void plParticleSystem::IHandleRenderMsg(plPipeline* pipe) +{ + fCurrTime = hsTimer::GetSysSeconds(); + hsScalar delta = hsScalar(fCurrTime - fLastTime); + if (delta == 0) + return; + plConst(hsScalar) kMaxDelta(0.3f); + if( delta > kMaxDelta ) + delta = kMaxDelta; + + plDrawInterface* di = ICheckDrawInterface(); + if( !di ) + return; + + hsBool disabled = di->GetProperty(plDrawInterface::kDisable); + if (!IShouldUpdate(pipe)) + { + if (disabled) + di->ResetParticleSystem(); // Need to call this, otherwise particles get drawn, even though the DI is disabled. + // (Yes, it's lame.) + + // Otherwise, we leave the DI alone, and the particles draw in the same place they were last frame. + return; + } + + fContext.fPipeline = pipe; + fContext.fSystem = this; + fContext.fSecs = fCurrTime; + fContext.fDelSecs = delta; + + di->ResetParticleSystem(); + + if (fPreSim > 0) + IPreSim(); + + int i; + for (i = 0; i < fNumValidEmitters; i++) + { + fEmitters[i]->IUpdate(delta); + plProfile_IncCount(NumParticles, fEmitters[i]->fNumValidParticles); + if (!disabled) + { + if( fEmitters[ i ]->GetParticleCount() > 0 ) + di->AssignEmitterToParticleSystem( fEmitters[ i ] ); // Go make those polys! + } + } + +// plParticleFiller::FillParticlePolys(pipe, di); + + fLastTime = fCurrTime; +} + + +#include "plProfile.h" +plProfile_CreateTimer("ParticleSys", "RenderSetup", ParticleSys); + + +hsBool plParticleSystem::MsgReceive(plMessage* msg) +{ + plGenRefMsg* refMsg; + plParticleUpdateMsg *partMsg; + plParticleKillMsg *killMsg; + plSceneObject *scene; + plParticleEffect *effect; + hsGMaterial *mat; + plRenderMsg *rend; + plAgeLoadedMsg* ageLoaded; + + if (rend = plRenderMsg::ConvertNoRef(msg)) + { + plProfile_BeginLap(ParticleSys, this->GetKey()->GetUoid().GetObjectName()); + IHandleRenderMsg(rend->Pipeline()); + plProfile_EndLap(ParticleSys, this->GetKey()->GetUoid().GetObjectName()); + return true; + } + else if (refMsg = plGenRefMsg::ConvertNoRef(msg)) + { + if (scene = plSceneObject::ConvertNoRef(refMsg->GetRef())) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) + AddTarget(scene); + else + RemoveTarget(scene); + return true; + } + if (mat = hsGMaterial::ConvertNoRef(refMsg->GetRef())) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) + fTexture = mat; + else + fTexture = nil; + return true; + } + if (effect = plParticleEffect::ConvertNoRef(refMsg->GetRef())) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) + IAddEffect(effect, refMsg->fType); + //else + // IRemoveEffect(effect, refMsg->fType); + return true; + } + } + else if (partMsg = plParticleUpdateMsg::ConvertNoRef(msg)) + { + UpdateGenerator(partMsg->GetParamID(), partMsg->GetParamValue()); + return true; + } + else if (killMsg = plParticleKillMsg::ConvertNoRef(msg)) + { + KillParticles(killMsg->fNumToKill, killMsg->fTimeLeft, killMsg->fFlags); + return true; + } + else if( (ageLoaded = plAgeLoadedMsg::ConvertNoRef(msg)) && ageLoaded->fLoaded ) + { + ICheckDrawInterface(); + return true; + } + return plModifier::MsgReceive(msg); +} + +void plParticleSystem::UpdateGenerator(UInt32 paramID, hsScalar value) +{ + int i; + for (i = 0; i < fNumValidEmitters; i++) + fEmitters[i]->UpdateGenerator(paramID, value); +} + +void plParticleSystem::AddTarget(plSceneObject *so) +{ + if (fTarget != nil) + RemoveTarget(fTarget); + + fTarget = so; + plgDispatch::Dispatch()->RegisterForExactType(plTimeMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); + + delete fParticleSDLMod; + fParticleSDLMod = TRACKED_NEW plParticleSDLMod; + fParticleSDLMod->SetAttachedToAvatar(fAttachedToAvatar); + so->AddModifier(fParticleSDLMod); +} + +void plParticleSystem::RemoveTarget(plSceneObject *so) +{ + if (so == fTarget && so != nil) + { + if (fParticleSDLMod) + { + so->RemoveModifier(fParticleSDLMod); + delete fParticleSDLMod; + fParticleSDLMod = nil; + } + fTarget = nil; + } +} + +// This can be done much faster, but it's only done on load, and very very clean as is. Saving optimization for +// when we observe that it's too slow. +void plParticleSystem::IPreSim() +{ + const double PRESIM_UPDATE_TICK = 0.1; + + int i; + for (i = 0; i < fNumValidEmitters; i++) + { + double secs = fPreSim; + while (secs > 0) + { + fEmitters[i]->IUpdateParticles((hsScalar)PRESIM_UPDATE_TICK); + secs -= PRESIM_UPDATE_TICK; + } + } + + fPreSim = 0; +} + +void plParticleSystem::IReadEffectsArray(hsTArray &effects, UInt32 type, hsStream *s, hsResMgr *mgr) +{ + plGenRefMsg *msg; + effects.Reset(); + UInt32 count = s->ReadSwap32(); + int i; + for (i = 0; i < count; i++) + { + msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, (Int8)type); + mgr->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef); + } +} + +void plParticleSystem::Read(hsStream *s, hsResMgr *mgr) +{ + plModifier::Read(s, mgr); + + plGenRefMsg* msg; + msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0); // Material + mgr->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef); + + fAmbientCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fDiffuseCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fOpacityCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fWidthCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fHeightCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + + UInt32 xTiles = s->ReadSwap32(); + UInt32 yTiles = s->ReadSwap32(); + UInt32 maxTotal = s->ReadSwap32(); + UInt32 maxEmitters = s->ReadSwap32(); + Init(xTiles, yTiles, maxTotal, maxEmitters, fAmbientCtl, fDiffuseCtl, fOpacityCtl, fWidthCtl, fHeightCtl); + + fPreSim = s->ReadSwapScalar(); + fAccel.Read(s); + fDrag = s->ReadSwapScalar(); + fWindMult = s->ReadSwapScalar(); + + fNumValidEmitters = s->ReadSwap32(); + int i; + for (i = 0; i < fNumValidEmitters; i++) + { + fEmitters[i] = plParticleEmitter::ConvertNoRef(mgr->ReadCreatable(s)); + fEmitters[i]->ISetSystem(this); + } + + IReadEffectsArray(fForces, kEffectForce, s, mgr); + IReadEffectsArray(fEffects, kEffectMisc, s, mgr); + IReadEffectsArray(fConstraints, kEffectConstraint, s, mgr); + + int count = s->ReadSwap32(); + fPermaLights.SetCount(count); + for( i = 0; i < count; i++ ) + { + fPermaLights[i] = mgr->ReadKey(s); + } +} + +void plParticleSystem::Write(hsStream *s, hsResMgr *mgr) +{ + plModifier::Write(s, mgr); + + int i; + + mgr->WriteKey(s, fTexture); + + // Arguments to the Init() function + mgr->WriteCreatable(s, fAmbientCtl); + mgr->WriteCreatable(s, fDiffuseCtl); + mgr->WriteCreatable(s, fOpacityCtl); + mgr->WriteCreatable(s, fWidthCtl); + mgr->WriteCreatable(s, fHeightCtl); + + s->WriteSwap32(fXTiles); + s->WriteSwap32(fYTiles); + s->WriteSwap32(fMaxTotalParticles); + s->WriteSwap32(fMaxEmitters); + + s->WriteSwapScalar(fPreSim); + fAccel.Write(s); + s->WriteSwapScalar(fDrag); + s->WriteSwapScalar(fWindMult); + + s->WriteSwap32(fNumValidEmitters); + for (i = 0; i < fNumValidEmitters; i++) + { + mgr->WriteCreatable(s, fEmitters[i]); + } + + int count; + count = fForces.GetCount(); + s->WriteSwap32(count); + for (i = 0; i < count; i++) + mgr->WriteKey(s, fForces.Get(i)); + + count = fEffects.GetCount(); + s->WriteSwap32(count); + for (i = 0; i < count; i++) + mgr->WriteKey(s, fEffects.Get(i)); + + count = fConstraints.GetCount(); + s->WriteSwap32(count); + for (i = 0; i < count; i++) + mgr->WriteKey(s, fConstraints.Get(i)); + + count = fPermaLights.GetCount(); + s->WriteSwap32(count); + for( i = 0; i < count; i++ ) + mgr->WriteKey(s, fPermaLights[i]); +} + +void plParticleSystem::SetAttachedToAvatar(bool attached) +{ + fAttachedToAvatar = attached; + if (fParticleSDLMod) + fParticleSDLMod->SetAttachedToAvatar(attached); +} + +void plParticleSystem::AddLight(plKey liKey) +{ + fPermaLights.Append(liKey); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.h new file mode 100644 index 00000000..bc44ab7b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plParticleSystem/plParticleSystem.h @@ -0,0 +1,185 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plParticleSystem_inc +#define plParticleSystem_inc + +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "../../NucleusLib/pnModifier/plModifier.h" +#include "../pnNetCommon/plSynchedValue.h" +#include "../CoreLib/hsColorRGBA.h" +#include "../CoreLib/hsMatrix44.h" +#include "plEffectTargetInfo.h" + +class plPipeline; +class plParticle; +class plParticleGenerator; +class plSimpleParticleGenerator; +class plParticleEmitter; +class plParticleEffect; +class plParticleSDLMod; +class plController; +class hsGMaterial; +class plDrawableSpans; +class plDrawInterface; +class Mtl; + +// This is responsible for creating and maintaining (allocation and update cycle) a related set of particles. +// Automatic particle generation is handled by the plParticleEmitters (one for each spawning location). + +class plParticleSystem : public plModifier +{ + friend plParticleEmitter; + friend plSimpleParticleGenerator; + +protected: + static const hsScalar GRAVITY_ACCEL_FEET_PER_SEC2; + plSceneObject *fTarget; + + hsGMaterial *fTexture; // One texture per system (Tiling is your friend!) + UInt32 fXTiles, fYTiles; // Width/height of the texture (in tiles) for determining a particle's UVs + + double fCurrTime; + double fLastTime; + hsVector3 fAccel; + hsScalar fPreSim; + hsScalar fDrag; + hsScalar fWindMult; + bool fAttachedToAvatar; + + UInt32 fMaxTotalParticles; + UInt32 fMaxTotalParticlesLeft; + UInt32 fNumValidEmitters; + UInt32 fMaxEmitters; + UInt32 fNextEmitterToGo; + + plParticleEmitter **fEmitters; // Various locations we're emitting particles from (the first one is + // reserved for particles added explicitly (to keep all the bookkeeping + // in the hands of plParticleEmitter). + + hsTArray fForces; // Global forces (wind/gravity/etc) that affect accelleration. + hsTArray fEffects; // Any other non-constraint effects. + hsTArray fConstraints; // Rigid body, collision, connectivity, etc. + plParticleContext fContext; // Rendering context passed to forces/effects/constraints. + + hsTArray fPermaLights; // Runtime lights assigned to this system. Currently don't support projected lights on particles. + + // Material related animations, mapped over the course of a particle's life + plController *fAmbientCtl; + plController *fDiffuseCtl; + plController *fOpacityCtl; + plController *fWidthCtl; + plController *fHeightCtl; + + plParticleSDLMod *fParticleSDLMod; + + hsBool IShouldUpdate(plPipeline* pipe) const; + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); // required by plModifier + void IHandleRenderMsg(plPipeline* pipe); + plDrawInterface* ICheckDrawInterface(); + void IAddEffect(plParticleEffect *effect, UInt32 type); + void IReadEffectsArray(hsTArray &effects, UInt32 type, hsStream *s, hsResMgr *mgr); + void IPreSim(); + +public: + plParticleSystem(); + virtual ~plParticleSystem(); + void Init(UInt32 xTiles, UInt32 yTiles, UInt32 maxTotalParticles, UInt32 numEmitters, + plController *ambientCtl, plController *diffuseCtl, plController *opacityCtl, + plController *widthCtl, plController *heightCtl); + + enum effectType + { + kEffectForce = 0x1, + kEffectMisc = 0x2, + kEffectConstraint = 0x4, + }; + + enum miscFlags + { + kParticleSystemAlwaysUpdate = 0x1 + }; + UInt8 fMiscFlags; // Not read/written (could be, but it's not needed yet.) + + // There might not be enough particles available. this function returns the number of maxParticles that + // the emitter actually received. + UInt32 AddEmitter(UInt32 maxParticles, plParticleGenerator *gen, UInt32 emitterFlags); + + plParticleEmitter* GetAvailEmitter(); + + CLASSNAME_REGISTER( plParticleSystem ); + GETINTERFACE_ANY( plParticleSystem, plModifier); + + // These are just public wrappers to the equivalent plParticleEmitter functions, provided for the purpose + // of adding particles to a system explicitly. + void AddParticle(hsPoint3 &pos, hsVector3 &velocity, UInt32 tileIndex, + hsScalar hSize, hsScalar vSize, hsScalar scale, hsScalar invMass, hsScalar life, + hsPoint3 &orientation, UInt32 miscFlags, hsScalar radsPerSec=0.f); + void GenerateParticles(UInt32 num, hsScalar dt = 0.f); + void WipeExistingParticles(); // Instant nuke + void KillParticles(hsScalar num, hsScalar timeToDie, UInt8 flags); // Sets a death timer. They'll get removed on the next update (if time has run out) + UInt16 StealParticlesFrom(plParticleSystem *victim, UInt16 num); // Returns the number of particles actually stolen + void TranslateAllParticles(hsPoint3 &amount); // Used to recenter the system when linking between ages. + + void DisableGenerators(); + UInt32 GetNumTiles() const { return fXTiles * fYTiles; } + void SetTileIndex(plParticle &particle, UInt32 index); // Sets the UV coordinates appropriate for the current texture + UInt32 GetNumValidParticles(hsBool immortalOnly = false) const; // Takes a bit longer if we want a count of immortal particles... + UInt32 GetMaxTotalParticles() const { return fMaxTotalParticles; } + + const hsMatrix44 &GetLocalToWorld() const; + void SetAccel(const hsVector3& a) { fAccel = GRAVITY_ACCEL_FEET_PER_SEC2 * a; } + void SetGravity(hsScalar pct) { fAccel.Set(0, 0, -GRAVITY_ACCEL_FEET_PER_SEC2 * pct); } + void SetDrag(hsScalar d) { fDrag = -d; } + void SetWindMult(hsScalar m) { fWindMult = m; } + void SetPreSim(hsScalar time) { fPreSim = time; } + void UpdateGenerator(UInt32 paramID, hsScalar value); + plParticleGenerator *GetExportedGenerator() const; + + const hsVector3& GetAccel() const { return fAccel; } + hsScalar GetDrag() const { return fDrag; } + hsScalar GetWindMult() const { return fWindMult; } + plParticleEffect *GetEffect(UInt16 type) const; + + plParticleSDLMod* GetSDLMod() {return fParticleSDLMod;} + // Functions related to/required by plModifier + virtual int GetNumTargets() const { return fTarget ? 1 : 0; } + virtual plSceneObject* GetTarget(int w) const { hsAssert(w < 1, "Bad target"); return fTarget; } + virtual void AddTarget(plSceneObject* so); + virtual void RemoveTarget(plSceneObject* so); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + virtual hsBool MsgReceive(plMessage* msg); + + void SetAttachedToAvatar(bool attached); + + // Export only functions for building the system. Not supported at runtime. + // AddLight allows the particle system to remain in ignorant bliss about runtime lights + void AddLight(plKey liKey); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp new file mode 100644 index 00000000..c49467b4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.cpp @@ -0,0 +1,296 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plLOSDispatch.h" +#include "plSimulationMgr.h" +#include "plgDispatch.h" +#include "../plMessage/plLOSRequestMsg.h" +#include "../plMessage/plLOSHitMsg.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plModifier/plLogicModifier.h" +#include "plPXPhysical.h" +#include "plPXPhysicalControllerCore.h" +#include "plPXConvert.h" + +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plArmatureMod.h" + +#include "NxPhysics.h" + +#include "plProfile.h" +plProfile_Extern(LineOfSight); + +class myRaycastReport : public NxUserRaycastReport +{ +public: + void InitCast(plSimDefs::plLOSDB db, plLOSRequestMsg::TestType type) + { + fDB = db; + fType = type; + fHitObj = nil; + fNormal.Set(0, 0, 0); + fPoint.Set(0, 0, 0); + fDist = FLT_MAX; + } + + bool GotHit() { return fDist != FLT_MAX; } + plKey GetObj() { return fHitObj; } + hsScalar GetDistance() { return fDist; } + const hsVector3& GetNormal() { return fNormal; } + const hsPoint3& GetPoint() { return fPoint; } + void ResetHitObj(){fHitObj=nil;} +private: + virtual bool onHit(const NxRaycastHit& hit) + { + NxActor& hitActor = hit.shape->getActor(); + plPXPhysical* phys = (plPXPhysical*)hitActor.userData; + + plKey objKey = nil; + UInt16 objDB = plSimDefs::kLOSDBNone; + + if (phys) + { + objKey = phys->GetObjectKey(); + objDB = phys->GetAllLOSDBs(); + } + else + { + bool isController; + plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(hitActor,&isController); + if (controller) + { + objKey = controller->GetOwner(); + objDB = controller->GetLOSDB(); + } + } + + // is the object's physic enabled and is it in the database that we are looking for? + if ( !phys || !phys->GetProperty(plSimulationInterface::kDisable) ) + { + if ( (fDB & objDB) != 0) + { + if (fType == plLOSRequestMsg::kTestAny || hit.distance < fDist) + { + // need one more test... if it is a clickable need to see if it is enabled + bool disabled = false; + if ( objKey ) + { + plSceneObject* so = plSceneObject::ConvertNoRef( objKey->GetObjectPtr() ); + if (so) + { + int i; + for ( i=0; i < so->GetNumModifiers(); i++) + { + plLogicModifier* lo = (plLogicModifier*)plLogicModifier::ConvertNoRef(so->GetModifier(i) ); + if (lo) + { + disabled = lo->Disabled(); + break; + } + } + } + } + + if (!disabled) + { + fHitObj = objKey; + fNormal.Set(hit.worldNormal.x, hit.worldNormal.y, hit.worldNormal.z); + fPoint.Set(hit.worldImpact.x, hit.worldImpact.y, hit.worldImpact.z); + fDist = hit.distance; + + if (fType == plLOSRequestMsg::kTestAny) + return false; + } + } + } + } + + return true; + } + + plSimDefs::plLOSDB fDB; + plLOSRequestMsg::TestType fType; + + plKey fHitObj; + hsVector3 fNormal; + hsPoint3 fPoint; + hsScalar fDist; +} gMyReport; + +plLOSDispatch::plLOSDispatch() +{ + RegisterAs(kLOSObject_KEY); + plgDispatch::Dispatch()->RegisterForExactType(plLOSRequestMsg::Index(), GetKey()); +} + +plLOSDispatch::~plLOSDispatch() +{ + plgDispatch::Dispatch()->UnRegisterForExactType(plLOSRequestMsg::Index(), GetKey()); +} + +hsBool plLOSDispatch::MsgReceive(plMessage* msg) +{ + + plLOSRequestMsg* requestMsg = plLOSRequestMsg::ConvertNoRef(msg); + if (requestMsg) + { + plProfile_BeginTiming(LineOfSight); + + plSimulationMgr* sim = plSimulationMgr::GetInstance(); + + plKey worldKey = requestMsg->fWorldKey; + if (!worldKey) + { + plArmatureMod* av = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if ( av && av->GetController() ) + worldKey = av->GetController()->GetSubworld(); + } + + hsPoint3 from = requestMsg->fFrom; + hsPoint3 at = requestMsg->fTo; + + // requests are always sent in world space, but they might + // need to be converted to subworld space + hsMatrix44 l2w, w2l; + if (worldKey) + { + plSceneObject* so = plSceneObject::ConvertNoRef(worldKey->ObjectIsLoaded()); + if (so) + { + l2w = so->GetLocalToWorld(); + w2l = so->GetWorldToLocal(); + from = w2l * from; + at = w2l * at; + } + } + else + { + l2w.Reset(); + w2l.Reset(); + } + + NxScene* scene = sim->GetScene(worldKey); + + gMyReport.InitCast(requestMsg->GetRequestType(), requestMsg->GetTestType()); + + hsVector3 norm = hsVector3(at - from); + hsScalar dist = norm.Magnitude(); + norm.Normalize(); + + NxRay worldRay; + worldRay.dir = plPXConvert::Vector(norm); + worldRay.orig = plPXConvert::Point(from); + //PhysX will complain to log if ray distance is less than or equal to Zero, besides shouldn't bother throwing + // a point, and if we have negative we have some serious problems + if(dist>0.0f) + { + scene->raycastAllShapes(worldRay, gMyReport, NX_ALL_SHAPES, 0xffffffff, dist, NX_RAYCAST_DISTANCE | NX_RAYCAST_IMPACT | NX_RAYCAST_NORMAL); + } + else{ + SimLog("%s sent out a LOS request with a ray length of %d.", requestMsg->GetSender()->GetName(), dist); + } + if (gMyReport.GotHit()) + { + // We got a hit, save off the info + plMessage* hitMsg = ICreateHitMsg(requestMsg, l2w); + + if (requestMsg->GetCullDB() != plSimDefs::kLOSDBNone) + { + // If we have a cull db, adjust the length of the raycast to be from the + // original point to the object we hit. If we find anything from the cull + // db in there, the cast fails. + hsScalar dist = gMyReport.GetDistance(); + if(dist!=0.0) + { + gMyReport.InitCast(requestMsg->GetCullDB(), plLOSRequestMsg::kTestAny); + scene->raycastAllShapes(worldRay, gMyReport, NX_ALL_SHAPES, 0xffffffff, dist, NX_RAYCAST_DISTANCE | NX_RAYCAST_IMPACT | NX_RAYCAST_NORMAL); + + if (gMyReport.GotHit()) + { + delete hitMsg; + hitMsg = nil; + + if (requestMsg->GetReportType() == plLOSRequestMsg::kReportMiss || + requestMsg->GetReportType() == plLOSRequestMsg::kReportHitOrMiss) + { + ICreateMissMsg(requestMsg)->Send(); + } + } + } + else// we are right on top of the object I assume that means we hit it + {// since PhysX would have complained we will log it anyways. Just so we have a better idea, where this + //was happening previously + SimLog("%s sent out a LOS request. The second cast for culling was of length 0. ABORTING and assuming hit.", requestMsg->GetSender()->GetName()); + } + + } + + if (hitMsg && + (requestMsg->GetReportType() == plLOSRequestMsg::kReportHit || + requestMsg->GetReportType() == plLOSRequestMsg::kReportHitOrMiss)) + hitMsg->Send(); + } + else + { + if (requestMsg->GetReportType() == plLOSRequestMsg::kReportMiss || + requestMsg->GetReportType() == plLOSRequestMsg::kReportHitOrMiss) + { + ICreateMissMsg(requestMsg)->Send(); + } + } + + plProfile_EndTiming(LineOfSight); + gMyReport.ResetHitObj(); + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + +plMessage* plLOSDispatch::ICreateHitMsg(plLOSRequestMsg* requestMsg, hsMatrix44& l2w) +{ + plKey ourKey = GetKey(); + plKey rcvKey = requestMsg->GetSender(); + plLOSHitMsg* hitMsg = TRACKED_NEW plLOSHitMsg(ourKey, rcvKey, nil); + hitMsg->fNoHit = false; + hitMsg->fObj = gMyReport.GetObj(); + hitMsg->fDistance = gMyReport.GetDistance(); + hitMsg->fNormal = l2w * gMyReport.GetNormal(); + hitMsg->fHitPoint = l2w * gMyReport.GetPoint(); + hitMsg->fRequestID = requestMsg->GetRequestID(); + return hitMsg; +} + +plMessage* plLOSDispatch::ICreateMissMsg(plLOSRequestMsg* requestMsg) +{ + plKey ourKey = GetKey(); + plKey rcvKey = requestMsg->GetSender(); + plLOSHitMsg* missMsg = TRACKED_NEW plLOSHitMsg(ourKey, rcvKey, nil); + missMsg->fNoHit = true; + missMsg->fObj = nil; + missMsg->fRequestID = requestMsg->GetRequestID(); + return missMsg; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.h new file mode 100644 index 00000000..38963775 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plLOSDispatch.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../pnKeyedObject/hsKeyedObject.h" + +class plLOSRequestMsg; +struct hsMatrix44; + +/** \class plLOSDispatch + Line-of-sight requests are sent to this guy, who then hands them + to the appropriate solvers, which can vary depending on such + criteria as which subworld the player is currently in. + Eventually we will have more variants of requests, such as + "search all subworlds," etc. */ +class plLOSDispatch : public hsKeyedObject +{ +public: + plLOSDispatch(); + ~plLOSDispatch(); + + CLASSNAME_REGISTER(plLOSDispatch); + GETINTERFACE_ANY(plLOSDispatch, hsKeyedObject); + + virtual hsBool MsgReceive(plMessage* msg); + +protected: + plMessage* ICreateHitMsg(plLOSRequestMsg* requestMsg, hsMatrix44& l2w); + plMessage* ICreateMissMsg(plLOSRequestMsg* requestMsg); +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXConvert.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXConvert.cpp new file mode 100644 index 00000000..caf5769b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXConvert.cpp @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPXConvert.h" + +bool plPXConvert::Validate() +{ + NxVec3 nxVec; + hsVector3 plVec; + + int nxVecXOffset = ((char*)&nxVec.x) - ((char*)&nxVec); + int nxVecYOffset = ((char*)&nxVec.y) - ((char*)&nxVec); + int nxVecZOffset = ((char*)&nxVec.z) - ((char*)&nxVec); + + int plVecXOffset = ((char*)&plVec.fX) - ((char*)&plVec); + int plVecYOffset = ((char*)&plVec.fY) - ((char*)&plVec); + int plVecZOffset = ((char*)&plVec.fZ) - ((char*)&plVec); + + NxQuat nxQuat; + hsQuat plQuat; + + int nxQuatXOffset = ((char*)&nxQuat.x) - ((char*)&nxQuat); + int nxQuatYOffset = ((char*)&nxQuat.y) - ((char*)&nxQuat); + int nxQuatZOffset = ((char*)&nxQuat.z) - ((char*)&nxQuat); + int nxQuatWOffset = ((char*)&nxQuat.w) - ((char*)&nxQuat); + + int plQuatXOffset = ((char*)&plQuat.fX) - ((char*)&plQuat); + int plQuatYOffset = ((char*)&plQuat.fY) - ((char*)&plQuat); + int plQuatZOffset = ((char*)&plQuat.fZ) - ((char*)&plQuat); + int plQuatWOffset = ((char*)&plQuat.fW) - ((char*)&plQuat); + + bool offsetsOK = + nxVecXOffset == plVecXOffset && + nxVecYOffset == plVecYOffset && + nxVecZOffset == plVecZOffset && + nxQuatXOffset == plQuatXOffset && + nxQuatYOffset == plQuatYOffset && + nxQuatZOffset == plQuatZOffset && + nxQuatWOffset == plQuatWOffset; + + hsAssert(offsetsOK, "PhysX or Plasma offsets have changed, need to rewrite conversion code"); + + return offsetsOK; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXConvert.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXConvert.h new file mode 100644 index 00000000..c1e577b3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXConvert.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPXConvert_h_inc +#define plPXConvert_h_inc + +#include "hsGeometry3.h" +#include "hsQuat.h" +#include "hsMatrix44.h" + +#include "NxVec3.h" +#include "NxQuat.h" +#include "NxMat34.h" + +// Converts to and from the PhysX types +namespace plPXConvert +{ + // The following conversions are just casts, due to the fact that the Plasma + // and PhysX vector and quat classes don't have any virtual fuctions and have + // all their data in the same offsets. + inline NxVec3& Point(hsPoint3& vec) { return *((NxVec3*)&vec); } + inline const NxVec3& Point(const hsPoint3& vec) { return *((NxVec3*)&vec); } + inline hsPoint3& Point(NxVec3& vec) { return *((hsPoint3*)&vec); } + inline const hsPoint3& Point(const NxVec3& vec) { return *((hsPoint3*)&vec); } + + inline NxVec3& Vector(hsVector3& vel) { return *((NxVec3*)&vel); } + inline const NxVec3& Vector(const hsVector3& vel){ return *((NxVec3*)&vel); } + inline hsVector3& Vector(NxVec3& vec) { return *((hsVector3*)&vec); } + inline const hsVector3& Vector(const NxVec3& vec) { return *((hsVector3*)&vec); } + + inline const NxQuat& Quat(const hsQuat& quat) { return *((NxQuat*)&quat); } + inline const hsQuat& Quat(const NxQuat& quat) { return *((hsQuat*)&quat); } + + // The matrix data doesn't match up, so we have to convert it + inline void Matrix(const hsMatrix44& fromMat, NxMat34& toMat) { toMat.setRowMajor44(&fromMat.fMap[0][0]); } + inline void Matrix(const NxMat34& fromMat, hsMatrix44& toMat) { toMat.NotIdentity(); fromMat.getRowMajor44(&toMat.fMap[0][0]); } + + bool Validate(); +}; + +#endif // plPXConvert_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp new file mode 100644 index 00000000..a9de1f77 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.cpp @@ -0,0 +1,1450 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPXPhysical.h" + +#include "NxPhysics.h" + +#include "hsResMgr.h" +#include "hsStream.h" +#include "hsTimer.h" +#include "plProfile.h" +#include "hsQuat.h" +#include "hsSTLStream.h" + +#include "plSimulationMgr.h" +#include "../plPhysical/plPhysicalSDLModifier.h" +#include "../plPhysical/plPhysicalSndGroup.h" +#include "../plPhysical/plPhysicalProxy.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +#include "../pnKeyedObject/plKey.h" +#include "../pnMessage/plCorrectionMsg.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plSDLModifierMsg.h" +#include "../plMessage/plSimStateMsg.h" +#include "../plMessage/plSimInfluenceMsg.h" +#include "../plMessage/plLinearVelocityMsg.h" +#include "../plMessage/plAngularVelocityMsg.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetTransport/plNetTransportMember.h" +#include "../plStatusLog/plStatusLog.h" +#include "plPXConvert.h" +#include "plPXPhysicalControllerCore.h" + +#include "../plModifier/plDetectorLog.h" + + +#if 0 + #define SpamMsg(x) x +#else + #define SpamMsg(x) +#endif + +#define LogActivate(func) if (fActor->isSleeping()) SimLog("%s activated by %s", GetKeyName(), func); + +PhysRecipe::PhysRecipe() + : mass(0.f) + , friction(0.f) + , restitution(0.f) + , bounds(plSimDefs::kBoundsMax) + , group(plSimDefs::kGroupMax) + , reportsOn(0) + , objectKey(nil) + , sceneNode(nil) + , worldKey(nil) + , convexMesh(nil) + , triMesh(nil) + , radius(0.f) + , offset(0.f, 0.f, 0.f) + , meshStream(nil) +{ + l2s.Reset(); +} + +plProfile_Extern(MaySendLocation); +plProfile_Extern(LocationsSent); +plProfile_Extern(PhysicsUpdates); + +static void ClearMatrix(hsMatrix44 &m) +{ + m.fMap[0][0] = 0.0f; m.fMap[0][1] = 0.0f; m.fMap[0][2] = 0.0f; m.fMap[0][3] = 0.0f; + m.fMap[1][0] = 0.0f; m.fMap[1][1] = 0.0f; m.fMap[1][2] = 0.0f; m.fMap[1][3] = 0.0f; + m.fMap[2][0] = 0.0f; m.fMap[2][1] = 0.0f; m.fMap[2][2] = 0.0f; m.fMap[2][3] = 0.0f; + m.fMap[3][0] = 0.0f; m.fMap[3][1] = 0.0f; m.fMap[3][2] = 0.0f; m.fMap[3][3] = 0.0f; + m.NotIdentity(); +} + +int plPXPhysical::fNumberAnimatedPhysicals = 0; +int plPXPhysical::fNumberAnimatedActivators = 0; + +///////////////////////////////////////////////////////////////// +// +// plPXPhysical IMPLEMENTATION +// +///////////////////////////////////////////////////////////////// + +plPXPhysical::plPXPhysical() + : fSDLMod(nil) + , fActor(nil) + , fBoundsType(plSimDefs::kBoundsMax) + , fLOSDBs(plSimDefs::kLOSDBNone) + , fGroup(plSimDefs::kGroupMax) + , fReportsOn(0) + , fLastSyncTime(0.0f) + , fProxyGen(nil) + , fSceneNode(nil) + , fWorldKey(nil) + , fSndGroup(nil) + , fWorldHull(nil) + , fSaveTriangles(nil) + , fHullNumberPlanes(0) + , fMass(0.f) + , fWeWereHit(false) + , fHitForce(0,0,0) + , fHitPos(0,0,0) + , fInsideConvexHull(false) +{ +} + +plPXPhysical::~plPXPhysical() +{ + SpamMsg(plSimulationMgr::Log("Destroying physical %s", GetKeyName())); + + if (fActor) + { + // Grab any mesh we may have (they need to be released manually) + NxConvexMesh* convexMesh = nil; + NxTriangleMesh* triMesh = nil; + NxShape* shape = fActor->getShapes()[0]; + if (NxConvexShape* convexShape = shape->isConvexMesh()) + convexMesh = &convexShape->getConvexMesh(); + else if (NxTriangleMeshShape* trimeshShape = shape->isTriangleMesh()) + triMesh = &trimeshShape->getTriangleMesh(); + + if (!fActor->isDynamic()) + plPXPhysicalControllerCore::RebuildCache(); + + if (fActor->isDynamic() && fActor->readBodyFlag(NX_BF_KINEMATIC)) + { + if (fGroup == plSimDefs::kGroupDynamic) + fNumberAnimatedPhysicals--; + else + fNumberAnimatedActivators--; + } + + // Release the actor + NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + scene->releaseActor(*fActor); + fActor = nil; + + // Now that the actor is freed, release the mesh + if (convexMesh) + plSimulationMgr::GetInstance()->GetSDK()->releaseConvexMesh(*convexMesh); + if (triMesh) + plSimulationMgr::GetInstance()->GetSDK()->releaseTriangleMesh(*triMesh); + + // Release the scene, so it can be cleaned up if no one else is using it + plSimulationMgr::GetInstance()->ReleaseScene(fWorldKey); + } + + if (fWorldHull) + delete [] fWorldHull; + if (fSaveTriangles) + delete [] fSaveTriangles; + + delete fProxyGen; + + // remove sdl modifier + plSceneObject* sceneObj = plSceneObject::ConvertNoRef(fObjectKey->ObjectIsLoaded()); + if (sceneObj && fSDLMod) + { + sceneObj->RemoveModifier(fSDLMod); + } + delete fSDLMod; +} + +static void MakeBoxFromHull(NxConvexMesh* convexMesh, NxBoxShapeDesc& box) +{ + NxConvexMeshDesc desc; + convexMesh->saveToDesc(desc); + + hsScalar minX, minY, minZ, maxX, maxY, maxZ; + minX = minY = minZ = FLT_MAX; + maxX = maxY = maxZ = -FLT_MAX; + + for (int i = 0; i < desc.numVertices; i++) + { + float* point = (float*)(((char*)desc.points) + desc.pointStrideBytes*i); + float x = point[0]; + float y = point[1]; + float z = point[2]; + + minX = hsMinimum(minX, x); + minY = hsMinimum(minY, y); + minZ = hsMinimum(minZ, z); + maxX = hsMaximum(maxX, x); + maxY = hsMaximum(maxY, y); + maxZ = hsMaximum(maxZ, z); + } + + float xWidth = maxX - minX; + float yWidth = maxY - minY; + float zWidth = maxZ - minZ; + box.dimensions.x = xWidth / 2; + box.dimensions.y = yWidth / 2; + box.dimensions.z = zWidth / 2; + + //hsMatrix44 mat; + //box.localPose.getRowMajor44(&mat.fMap[0][0]); + hsPoint3 trans(minX + (xWidth / 2), minY + (yWidth / 2), minZ + (zWidth / 2)); + //mat.SetTranslate(&trans); + //box.localPose.setRowMajor44(&mat.fMap[0][0]); + + hsMatrix44 boxL2W; + boxL2W.Reset(); + boxL2W.SetTranslate(&trans); + plPXConvert::Matrix(boxL2W, box.localPose); + +} + +void plPXPhysical::IMakeHull(NxConvexMesh* convexMesh, hsMatrix44 l2w) +{ + NxConvexMeshDesc desc; + convexMesh->saveToDesc(desc); + + // make sure there are some triangles to work with + if (desc.numTriangles == 0) + return; + + // get rid of any we may have already had + if (fSaveTriangles) + delete [] fSaveTriangles; + + fHullNumberPlanes = desc.numTriangles; + fSaveTriangles = TRACKED_NEW hsPoint3[fHullNumberPlanes*3]; + + for (int i = 0; i < desc.numTriangles; i++) + { + UInt32* triangle = (UInt32*)(((char*)desc.triangles) + desc.triangleStrideBytes*i); + float* vertex1 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[0]); + float* vertex2 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[1]); + float* vertex3 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[2]); + hsPoint3 pt1(vertex1[0],vertex1[1],vertex1[2]); + hsPoint3 pt2(vertex2[0],vertex2[1],vertex2[2]); + hsPoint3 pt3(vertex3[0],vertex3[1],vertex3[2]); + + fSaveTriangles[(i*3)+0] = pt1; + fSaveTriangles[(i*3)+1] = pt2; + fSaveTriangles[(i*3)+2] = pt3; + } +} + +void plPXPhysical::ISetHullToWorldWTriangles() +{ + // if we have a detector hull and the world hasn't been updated + if (fWorldHull == nil) + { + fWorldHull = TRACKED_NEW hsPlane3[fHullNumberPlanes]; + // use the local2world from the physics engine so that it matches the transform of the positions from the triggerees + hsMatrix44 l2w; + plPXConvert::Matrix(fActor->getGlobalPose(), l2w); + int i; + for( i = 0; i < fHullNumberPlanes; i++ ) + { + hsPoint3 pt1 = fSaveTriangles[i*3]; + hsPoint3 pt2 = fSaveTriangles[(i*3)+1]; + hsPoint3 pt3 = fSaveTriangles[(i*3)+2]; + + // local to world translation + pt1 = l2w * pt1; + pt2 = l2w * pt2; + pt3 = l2w * pt3; + + hsPlane3 plane(&pt1, &pt2, &pt3); + fWorldHull[i] = plane; + } + } +} + + +hsBool plPXPhysical::IsObjectInsideHull(const hsPoint3& pos) +{ + if (fSaveTriangles) + { + ISetHullToWorldWTriangles(); + int i; + for( i = 0; i < fHullNumberPlanes; i++ ) + { + if (!ITestPlane(pos, fWorldHull[i])) + return false; + } + return true; + } + return false; +} + +hsBool plPXPhysical::Should_I_Trigger(hsBool enter, hsPoint3& pos) +{ + // see if we are inside the detector hull, if so, then don't trigger + bool trigger = false; + bool inside = IsObjectInsideHull(pos); + if ( !inside) + { + trigger = true; + fInsideConvexHull = enter; + } + else + { + // catch those rare cases on slow machines that miss the collision before avatar penetrated the face + if (enter && !fInsideConvexHull) + { +#ifdef PHYSX_SAVE_TRIGGERS_WORKAROUND + trigger = true; + fInsideConvexHull = enter; + DetectorLogSpecial("**>Saved a missing enter collision: %s",GetObjectKey()->GetName()); +#else + DetectorLogSpecial("**>Could have saved a missing enter collision: %s",GetObjectKey()->GetName()); +#endif PHYSX_SAVE_TRIGGERS_WORKAROUND + } + } + + return trigger; +} + + +hsBool plPXPhysical::Init(PhysRecipe& recipe) +{ + hsBool startAsleep = false; + fBoundsType = recipe.bounds; + fGroup = recipe.group; + fReportsOn = recipe.reportsOn; + fObjectKey = recipe.objectKey; + fSceneNode = recipe.sceneNode; + fWorldKey = recipe.worldKey; + + NxActorDesc actorDesc; + NxSphereShapeDesc sphereDesc; + NxConvexShapeDesc convexShapeDesc; + NxTriangleMeshShapeDesc trimeshShapeDesc; + NxBoxShapeDesc boxDesc; + + plPXConvert::Matrix(recipe.l2s, actorDesc.globalPose); + + switch (fBoundsType) + { + case plSimDefs::kSphereBounds: + { + hsMatrix44 sphereL2W; + sphereL2W.Reset(); + sphereL2W.SetTranslate(&recipe.offset); + + sphereDesc.radius = recipe.radius; + plPXConvert::Matrix(sphereL2W, sphereDesc.localPose); + sphereDesc.group = fGroup; + actorDesc.shapes.pushBack(&sphereDesc); + } + break; + case plSimDefs::kHullBounds: + // FIXME PHYSX - Remove when hull detection is fixed + // If this is read time (ie, meshStream is nil), turn the convex hull + // into a box. That way the data won't have to change when convex hulls + // actually work right. + if (fGroup == plSimDefs::kGroupDetector && recipe.meshStream == nil) + { +#ifdef USE_BOXES_FOR_DETECTOR_HULLS + MakeBoxFromHull(recipe.convexMesh, boxDesc); + plSimulationMgr::GetInstance()->GetSDK()->releaseConvexMesh(*recipe.convexMesh); + boxDesc.group = fGroup; + actorDesc.shapes.push_back(&boxDesc); +#else +#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND + // make a hull of planes for testing IsInside + IMakeHull(recipe.convexMesh,recipe.l2s); +#endif // USE_PHYSX_CONVEXHULL_WORKAROUND + convexShapeDesc.meshData = recipe.convexMesh; + convexShapeDesc.userData = recipe.meshStream; + convexShapeDesc.group = fGroup; + actorDesc.shapes.pushBack(&convexShapeDesc); +#endif // USE_BOXES_FOR_DETECTOR_HULLS + } + else + { + convexShapeDesc.meshData = recipe.convexMesh; + convexShapeDesc.userData = recipe.meshStream; + convexShapeDesc.group = fGroup; + actorDesc.shapes.pushBack(&convexShapeDesc); + } + break; + case plSimDefs::kBoxBounds: + { + boxDesc.dimensions = plPXConvert::Point(recipe.bDimensions); + + hsMatrix44 boxL2W; + boxL2W.Reset(); + boxL2W.SetTranslate(&recipe.bOffset); + plPXConvert::Matrix(boxL2W, boxDesc.localPose); + + boxDesc.group = fGroup; + actorDesc.shapes.push_back(&boxDesc); + } + break; + case plSimDefs::kExplicitBounds: + case plSimDefs::kProxyBounds: + if (fGroup == plSimDefs::kGroupDetector) + { + SimLog("Someone using an Exact on a detector region: %s", GetKeyName()); + } + trimeshShapeDesc.meshData = recipe.triMesh; + trimeshShapeDesc.userData = recipe.meshStream; + trimeshShapeDesc.group = fGroup; + actorDesc.shapes.pushBack(&trimeshShapeDesc); + break; + default: + hsAssert(false, "Unknown geometry type during read."); + return false; + break; + } + + // Now fill out the body, or dynamic part of the physical + NxBodyDesc bodyDesc; + fMass = recipe.mass; + if (recipe.mass != 0) + { + bodyDesc.mass = recipe.mass; + actorDesc.body = &bodyDesc; + + if (GetProperty(plSimulationInterface::kPinned)) + { + bodyDesc.flags |= NX_BF_FROZEN; + startAsleep = true; // put it to sleep if they are going to be frozen + } + + if (fGroup != plSimDefs::kGroupDynamic || GetProperty(plSimulationInterface::kPhysAnim)) + { + SetProperty(plSimulationInterface::kPassive, true); + + // Even though the code for animated physicals and animated activators are the same + // keep these code snippets separated for fine tuning. Thanks. + if (fGroup == plSimDefs::kGroupDynamic) + { + // handle the animated physicals.... make kinematic for now. + fNumberAnimatedPhysicals++; + bodyDesc.flags |= NX_BF_KINEMATIC; + startAsleep = true; + } + else + { + // handle the animated activators.... + fNumberAnimatedActivators++; + bodyDesc.flags |= NX_BF_KINEMATIC; + startAsleep = true; + } + + } + } + else + { + if ( GetProperty(plSimulationInterface::kPhysAnim) ) + SimLog("An animated physical that has no mass: %s", GetKeyName()); + } + + actorDesc.userData = this; + actorDesc.name = GetKeyName(); + + // Put the dynamics into actor group 1. The actor groups are only used for + // deciding who we get contact reports for. + if (fGroup == plSimDefs::kGroupDynamic) + actorDesc.group = 1; + + NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + try + { + fActor = scene->createActor(actorDesc); + } catch (...) + { + hsAssert(false, "Actor creation crashed"); + return false; + } + hsAssert(fActor, "Actor creation failed"); + if (!fActor) + return false; + + NxShape* shape = fActor->getShapes()[0]; + shape->setMaterial(plSimulationMgr::GetInstance()->GetMaterialIdx(scene, recipe.friction, recipe.restitution)); + + // Turn on the trigger flags for any detectors. + // + // Normally, we'd set these flags on the shape before it's created. However, + // in the case where the detector is going to be animated, it'll have a rigid + // body too, and that will cause problems at creation. According to Ageia, + // a detector shape doesn't actually count as a shape, so the SDK will have + // problems trying to calculate an intertial tensor. By letting it be + // created as a normal dynamic first, then setting the flags, we work around + // that problem. + if (fGroup == plSimDefs::kGroupDetector) + { + shape->setFlag(NX_TRIGGER_ON_ENTER, true); + shape->setFlag(NX_TRIGGER_ON_LEAVE, true); + } + + if (GetProperty(plSimulationInterface::kStartInactive) || startAsleep) + { + if (!fActor->isSleeping()) + { + if (plSimulationMgr::fExtraProfile) + SimLog("Deactivating %s in SetPositionAndRotationSim", GetKeyName()); + fActor->putToSleep(); + } + } + + if (GetProperty(plSimulationInterface::kDisable)) + IEnable(false); + if (GetProperty(plSimulationInterface::kSuppressed_DEAD)) + IEnable(false); + + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(fSceneNode, plRefMsg::kOnCreate, -1, plNodeRefMsg::kPhysical); + hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kActiveRef); + + if (fWorldKey) + { + plGenRefMsg* ref = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPhysRefWorld); + hsgResMgr::ResMgr()->AddViaNotify(fWorldKey, ref, plRefFlags::kActiveRef); + } + + // only dynamic physicals without noSync need SDLs + if ( fGroup == plSimDefs::kGroupDynamic && !fProps.IsBitSet(plSimulationInterface::kNoSynchronize) ) + { + // add SDL modifier + plSceneObject* sceneObj = plSceneObject::ConvertNoRef(fObjectKey->ObjectIsLoaded()); + hsAssert(sceneObj, "nil sceneObject, failed to create and attach SDL modifier"); + + delete fSDLMod; + fSDLMod = TRACKED_NEW plPhysicalSDLModifier; + sceneObj->AddModifier(fSDLMod); + } + + return true; +} + +///////////////////////////////////////////////////////////////// +// +// MESSAGE HANDLING +// +///////////////////////////////////////////////////////////////// + +// MSGRECEIVE +hsBool plPXPhysical::MsgReceive( plMessage* msg ) +{ + if(plGenRefMsg *refM = plGenRefMsg::ConvertNoRef(msg)) + { + return HandleRefMsg(refM); + } + else if(plSimulationMsg *simM = plSimulationMsg::ConvertNoRef(msg)) + { + plLinearVelocityMsg* velMsg = plLinearVelocityMsg::ConvertNoRef(msg); + if(velMsg) + { + SetLinearVelocitySim(velMsg->Velocity()); + return true; + } + plAngularVelocityMsg* angvelMsg = plAngularVelocityMsg::ConvertNoRef(msg); + if(angvelMsg) + { + SetAngularVelocitySim(angvelMsg->AngularVelocity()); + return true; + } + + + return false; + } + // couldn't find a local handler: pass to the base + else + return plPhysical::MsgReceive(msg); +} + +// IHANDLEREFMSG +// there's two things we hold references to: subworlds +// and the simulation manager. +// right now, we're only worrying about the subworlds +hsBool plPXPhysical::HandleRefMsg(plGenRefMsg* refMsg) +{ + UInt8 refCtxt = refMsg->GetContext(); + plKey refKey = refMsg->GetRef()->GetKey(); + plKey ourKey = GetKey(); + PhysRefType refType = PhysRefType(refMsg->fType); + + const char* refKeyName = refKey ? refKey->GetName() : "MISSING"; + + if (refType == kPhysRefWorld) + { + if (refCtxt == plRefMsg::kOnCreate || refCtxt == plRefMsg::kOnRequest) + { + // Cache the initial transform, since we assume the sceneobject already knows + // that and doesn't need to be told again + IGetTransformGlobal(fCachedLocal2World); + } + if (refCtxt == plRefMsg::kOnDestroy) + { + // our world was deleted out from under us: move to the main world +// hsAssert(0, "Lost world"); + } + } + else if (refType == kPhysRefSndGroup) + { + switch (refCtxt) + { + case plRefMsg::kOnCreate: + case plRefMsg::kOnRequest: + fSndGroup = plPhysicalSndGroup::ConvertNoRef( refMsg->GetRef() ); + break; + + case plRefMsg::kOnDestroy: + fSndGroup = nil; + break; + } + } + else + { + hsAssert(0, "Unknown ref type, who sent us this?"); + } + + return true; +} + +void plPXPhysical::IEnable(hsBool enable) +{ + fProps.SetBit(plSimulationInterface::kDisable, !enable); + if (!enable) + { + fActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); + if (fActor->isDynamic()) + fActor->raiseBodyFlag(NX_BF_FROZEN); + else + plPXPhysicalControllerCore::RebuildCache(); + } + else + { + fActor->clearActorFlag(NX_AF_DISABLE_COLLISION); + + // PHYSX FIXME - after re-enabling a possible detector, we need to check to see if any avatar is already in the PhysX turdy hull detector region + plSimulationMgr::GetInstance()->UpdateAvatarInDetector(fWorldKey, this); + + if (fActor->isDynamic()) + fActor->clearBodyFlag(NX_BF_FROZEN); + else + plPXPhysicalControllerCore::RebuildCache(); + } +} + +plPhysical& plPXPhysical::SetProperty(int prop, hsBool status) +{ + if (GetProperty(prop) == status) + { + const char* propName = "(unknown)"; + switch (prop) + { + case plSimulationInterface::kDisable: propName = "kDisable"; break; + case plSimulationInterface::kPinned: propName = "kPinned"; break; + case plSimulationInterface::kPassive: propName = "kPassive"; break; + case plSimulationInterface::kPhysAnim: propName = "kPhysAnim"; break; + case plSimulationInterface::kStartInactive: propName = "kStartInactive"; break; + case plSimulationInterface::kNoSynchronize: propName = "kNoSynchronize"; break; + } + + const char* name = "(unknown)"; + if (GetKey()) + name = GetKeyName(); + if (plSimulationMgr::fExtraProfile) + plSimulationMgr::Log("Warning: Redundant physical property set (property %s, value %s) on %s", propName, status ? "true" : "false", name); + } + + switch (prop) + { + case plSimulationInterface::kDisable: + IEnable(!status); + break; + + case plSimulationInterface::kPinned: + if (fActor->isDynamic()) + { + // if the body is already unpinned and you unpin it again, + // you'll wipe out its velocity. hence the check. + hsBool current = fActor->readBodyFlag(NX_BF_FROZEN); + if (status != current) + { + if (status) + fActor->raiseBodyFlag(NX_BF_FROZEN); + else + { + fActor->clearBodyFlag(NX_BF_FROZEN); + LogActivate("SetProperty"); + fActor->wakeUp(); + } + } + } + break; + } + + fProps.SetBit(prop, status); + + return *this; +} + +plProfile_Extern(SetTransforms); + +#define kMaxNegativeZPos -2000.f + +bool CompareMatrices(const hsMatrix44 &matA, const hsMatrix44 &matB, float tolerance) +{ + return + (fabs(matA.fMap[0][0] - matB.fMap[0][0]) < tolerance) && + (fabs(matA.fMap[0][1] - matB.fMap[0][1]) < tolerance) && + (fabs(matA.fMap[0][2] - matB.fMap[0][2]) < tolerance) && + (fabs(matA.fMap[0][3] - matB.fMap[0][3]) < tolerance) && + + (fabs(matA.fMap[1][0] - matB.fMap[1][0]) < tolerance) && + (fabs(matA.fMap[1][1] - matB.fMap[1][1]) < tolerance) && + (fabs(matA.fMap[1][2] - matB.fMap[1][2]) < tolerance) && + (fabs(matA.fMap[1][3] - matB.fMap[1][3]) < tolerance) && + + (fabs(matA.fMap[2][0] - matB.fMap[2][0]) < tolerance) && + (fabs(matA.fMap[2][1] - matB.fMap[2][1]) < tolerance) && + (fabs(matA.fMap[2][2] - matB.fMap[2][2]) < tolerance) && + (fabs(matA.fMap[2][3] - matB.fMap[2][3]) < tolerance) && + + (fabs(matA.fMap[3][0] - matB.fMap[3][0]) < tolerance) && + (fabs(matA.fMap[3][1] - matB.fMap[3][1]) < tolerance) && + (fabs(matA.fMap[3][2] - matB.fMap[3][2]) < tolerance) && + (fabs(matA.fMap[3][3] - matB.fMap[3][3]) < tolerance); +} + +// Called after the simulation has run....sends new positions to the various scene objects +// *** want to do this in response to an update message.... +void plPXPhysical::SendNewLocation(hsBool synchTransform, hsBool isSynchUpdate) +{ + // we only send if: + // - the body is active or forceUpdate is on + // - the mass is non-zero + // - the physical is not passive + hsBool bodyActive = !fActor->isSleeping(); + hsBool dynamic = fActor->isDynamic(); + + if ((bodyActive || isSynchUpdate) && dynamic)// && fInitialTransform) + { + plProfile_Inc(MaySendLocation); + + if (!GetProperty(plSimulationInterface::kPassive)) + { + hsMatrix44 curl2w = fCachedLocal2World; + // we're going to cache the transform before sending so we can recognize if it comes back + IGetTransformGlobal(fCachedLocal2World); + + if (!CompareMatrices(curl2w, fCachedLocal2World, .0001f)) + { + plProfile_Inc(LocationsSent); + plProfile_BeginLap(PhysicsUpdates, GetKeyName()); + + // quick peek at the translation...last time it was corrupted because we applied a non-unit quaternion +// hsAssert(real_finite(fCachedLocal2World.fMap[0][3]) && +// real_finite(fCachedLocal2World.fMap[1][3]) && +// real_finite(fCachedLocal2World.fMap[2][3]), "Bad transform outgoing"); + + if (fCachedLocal2World.GetTranslate().fZ < kMaxNegativeZPos) + { + SimLog("Physical %s fell to %.1f (%.1f is the max). Suppressing.", GetKeyName(), fCachedLocal2World.GetTranslate().fZ, kMaxNegativeZPos); + // Since this has probably been falling for a while, and thus not getting any syncs, + // make sure to save it's current pos so we'll know to reset it later + DirtySynchState(kSDLPhysical, plSynchedObject::kBCastToClients); + IEnable(false); + } + + hsMatrix44 w2l; + fCachedLocal2World.GetInverse(&w2l); + plCorrectionMsg *pCorrMsg = TRACKED_NEW plCorrectionMsg(GetObjectKey(), fCachedLocal2World, w2l, synchTransform); + pCorrMsg->Send(); + if (fProxyGen) + fProxyGen->SetTransform(fCachedLocal2World, w2l); + plProfile_EndLap(PhysicsUpdates, GetKeyName()); + } + } + } +} + +void plPXPhysical::ApplyHitForce() +{ + if (fActor && fWeWereHit) + { + fActor->addForceAtPos(plPXConvert::Vector(fHitForce), plPXConvert::Point(fHitPos), NX_FORCE); + fWeWereHit = false; + } +} + + +void plPXPhysical::ISetTransformGlobal(const hsMatrix44& l2w) +{ + hsAssert(fActor->isDynamic(), "Shouldn't move a static actor"); + + NxMat34 mat; + + if (fWorldKey) + { + plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded()); + hsAssert(so, "Scene object not loaded while accessing subworld."); + // physical to subworld (simulation space) + hsMatrix44 p2s = so->GetCoordinateInterface()->GetWorldToLocal() * l2w; + plPXConvert::Matrix(p2s, mat); + if (fProxyGen) + { + hsMatrix44 w2l; + p2s.GetInverse(&w2l); + fProxyGen->SetTransform(p2s, w2l); + } + } + // No need to localize + else + { + plPXConvert::Matrix(l2w, mat); + if (fProxyGen) + { + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + fProxyGen->SetTransform(l2w, w2l); + } + } + + if (GetProperty(plSimulationInterface::kPhysAnim)) + { hsAssert(fActor->readBodyFlag(NX_BF_KINEMATIC),"This Should be kinematic"); + fActor->moveGlobalPose(mat); + } + else + fActor->setGlobalPose(mat); +} + +// the physical may have several parents between it and the subworld object, +// but the *havok* transform is only one level away from the subworld. +// to avoid any confusion about this difference, we avoid referring to the +// subworld as "parent" and use, for example, "l2s" (local-to-sub) instead +// of the canonical plasma "l2p" (local-to-parent) +void plPXPhysical::IGetTransformGlobal(hsMatrix44& l2w) const +{ + plPXConvert::Matrix(fActor->getGlobalPose(), l2w); + + if (fWorldKey) + { + plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded()); + hsAssert(so, "Scene object not loaded while accessing subworld."); + // We'll hit this at export time, when the ci isn't ready yet, so do a check + if (so->GetCoordinateInterface()) + { + const hsMatrix44& s2w = so->GetCoordinateInterface()->GetLocalToWorld(); + l2w = s2w * l2w; + } + } +} + +void plPXPhysical::IGetPositionSim(hsPoint3& pos) const +{ + pos = plPXConvert::Point(fActor->getGlobalPosition()); +} + +void plPXPhysical::IGetRotationSim(hsQuat& rot) const +{ + rot = plPXConvert::Quat(fActor->getGlobalOrientationQuat()); +} +void plPXPhysical::ISetPositionSim(const hsPoint3& pos) +{ + if (GetProperty(plSimulationInterface::kPhysAnim)) + fActor->moveGlobalPosition(plPXConvert::Point(pos)); + else + fActor->setGlobalPosition(plPXConvert::Point(pos)); +} + +void plPXPhysical::ISetRotationSim(const hsQuat& rot) +{ + if (GetProperty(plSimulationInterface::kPhysAnim)) + fActor->moveGlobalOrientation(plPXConvert::Quat(rot)); + else + fActor->setGlobalOrientation(plPXConvert::Quat(rot)); +} + +// This form is assumed by convention to be global. +void plPXPhysical::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, hsBool force) +{ +// hsAssert(real_finite(l2w.fMap[0][3]) && real_finite(l2w.fMap[1][3]) && real_finite(l2w.fMap[2][3]), "Bad transform incoming"); + + + // make sure the physical is dynamic. + // also make sure there is some difference between the matrices... + // ... but not when a subworld... because the subworld maybe animating and if the object is still then it is actually moving within the subworld + if (force || (fActor->isDynamic() && (fWorldKey || !CompareMatrices(l2w, fCachedLocal2World, .0001f))) ) + { + ISetTransformGlobal(l2w); + plProfile_Inc(SetTransforms); + } + else + { + if ( !fActor->isDynamic() && plSimulationMgr::fExtraProfile) + SimLog("Setting transform on non-dynamic: %s.", GetKeyName()); + } +} + +// GETTRANSFORM +void plPXPhysical::GetTransform(hsMatrix44& l2w, hsMatrix44& w2l) +{ + IGetTransformGlobal(l2w); + l2w.GetInverse(&w2l); +} + +hsBool plPXPhysical::GetLinearVelocitySim(hsVector3& vel) const +{ + hsBool result = false; + + if (fActor->isDynamic()) + { + vel = plPXConvert::Vector(fActor->getLinearVelocity()); + result = true; + } + else + vel.Set(0, 0, 0); + + return result; +} + +void plPXPhysical::SetLinearVelocitySim(const hsVector3& vel) +{ + if (fActor->isDynamic()) + fActor->setLinearVelocity(plPXConvert::Vector(vel)); +} + +void plPXPhysical::ClearLinearVelocity() +{ + SetLinearVelocitySim(hsVector3(0, 0, 0)); +} + +hsBool plPXPhysical::GetAngularVelocitySim(hsVector3& vel) const +{ + hsBool result = false; + if (fActor->isDynamic()) + { + vel = plPXConvert::Vector(fActor->getAngularVelocity()); + result = true; + } + else + vel.Set(0, 0, 0); + + return result; +} + +void plPXPhysical::SetAngularVelocitySim(const hsVector3& vel) +{ + if (fActor->isDynamic()) + fActor->setAngularVelocity(plPXConvert::Vector(vel)); +} + +/////////////////////////////////////////////////////////////// +// +// NETWORK SYNCHRONIZATION +// +/////////////////////////////////////////////////////////////// + +plKey plPXPhysical::GetSceneNode() const +{ + return fSceneNode; +} + +void plPXPhysical::SetSceneNode(plKey newNode) +{ +#ifdef HS_DEBUGGING + plKey oldNode = GetSceneNode(); + char msg[1024]; + if (newNode) + sprintf(msg,"Physical object %s cannot change scenes. Already in %s, trying to switch to %s.",fObjectKey->GetName(),oldNode->GetName(),newNode->GetName()); + else + sprintf(msg,"Physical object %s cannot change scenes. Already in %s, trying to switch to .",fObjectKey->GetName(),oldNode->GetName()); + hsAssert(oldNode == newNode, msg); +#endif // HS_DEBUGGING +} + +///////////////////////////////////////////////////////////////////// +// +// READING AND WRITING +// +///////////////////////////////////////////////////////////////////// + +#include "plPXStream.h" + +void plPXPhysical::Read(hsStream* stream, hsResMgr* mgr) +{ + plPhysical::Read(stream, mgr); + ClearMatrix(fCachedLocal2World); + + PhysRecipe recipe; + recipe.mass = stream->ReadSwapScalar(); + recipe.friction = stream->ReadSwapScalar(); + recipe.restitution = stream->ReadSwapScalar(); + recipe.bounds = (plSimDefs::Bounds)stream->ReadByte(); + recipe.group = (plSimDefs::Group)stream->ReadByte(); + recipe.reportsOn = stream->ReadSwap32(); + fLOSDBs = stream->ReadSwap16(); + //hack for swim regions currently they are labeled as static av blockers + if(fLOSDBs==plSimDefs::kLOSDBSwimRegion) + { + recipe.group=plSimDefs::kGroupMax; + } + // + recipe.objectKey = mgr->ReadKey(stream); + recipe.sceneNode = mgr->ReadKey(stream); + recipe.worldKey = mgr->ReadKey(stream); + + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPhysRefSndGroup), plRefFlags::kActiveRef); + + hsPoint3 pos; + hsQuat rot; + pos.Read(stream); + rot.Read(stream); + rot.MakeMatrix(&recipe.l2s); + recipe.l2s.SetTranslate(&pos); + + fProps.Read(stream); + + if (recipe.bounds == plSimDefs::kSphereBounds) + { + recipe.radius = stream->ReadSwapScalar(); + recipe.offset.Read(stream); + } + else if (recipe.bounds == plSimDefs::kBoxBounds) + { + recipe.bDimensions.Read(stream); + recipe.bOffset.Read(stream); + } + else + { + // Read in the cooked mesh + plPXStream pxs(stream); + if (recipe.bounds == plSimDefs::kHullBounds) + recipe.convexMesh = plSimulationMgr::GetInstance()->GetSDK()->createConvexMesh(pxs); + else + recipe.triMesh = plSimulationMgr::GetInstance()->GetSDK()->createTriangleMesh(pxs); + } + + Init(recipe); + + hsAssert(!fProxyGen, "Already have proxy gen, double read?"); + + hsColorRGBA physColor; + hsScalar opac = 1.0f; + + if (fGroup == plSimDefs::kGroupAvatar) + { + // local avatar is light purple and transparent + physColor.Set(.2f, .1f, .2f, 1.f); + opac = 0.4f; + } + else if (fGroup == plSimDefs::kGroupDynamic) + { + // Dynamics are red + physColor.Set(1.f,0.f,0.f,1.f); + } + else if (fGroup == plSimDefs::kGroupDetector) + { + if(!fWorldKey) + { + // Detectors are blue, and transparent + physColor.Set(0.f,0.f,1.f,1.f); + opac = 0.3f; + } + else + { + // subworld Detectors are green + physColor.Set(0.f,1.f,0.f,1.f); + opac = 0.3f; + } + } + else if (fGroup == plSimDefs::kGroupStatic) + { + if (GetProperty(plSimulationInterface::kPhysAnim)) + // Statics that are animated are more reddish? + physColor.Set(1.f,0.6f,0.2f,1.f); + else + // Statics are yellow + physColor.Set(1.f,0.8f,0.2f,1.f); + // if in a subworld... slightly transparent + if(fWorldKey) + opac = 0.6f; + } + else + { + // don't knows are grey + physColor.Set(0.6f,0.6f,0.6f,1.f); + } + + fProxyGen = TRACKED_NEW plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); + fProxyGen->Init(this); +} + +void plPXPhysical::Write(hsStream* stream, hsResMgr* mgr) +{ + plPhysical::Write(stream, mgr); + + hsAssert(fActor, "nil actor"); + hsAssert(fActor->getNbShapes() == 1, "Can only write actors with one shape. Writing first only."); + NxShape* shape = fActor->getShapes()[0]; + + NxMaterialIndex matIdx = shape->getMaterial(); + NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + NxMaterial* mat = scene->getMaterialFromIndex(matIdx); + float friction = mat->getStaticFriction(); + float restitution = mat->getRestitution(); + + stream->WriteSwapScalar(fActor->getMass()); + stream->WriteSwapScalar(friction); + stream->WriteSwapScalar(restitution); + stream->WriteByte(fBoundsType); + stream->WriteByte(fGroup); + stream->WriteSwap32(fReportsOn); + stream->WriteSwap16(fLOSDBs); + mgr->WriteKey(stream, fObjectKey); + mgr->WriteKey(stream, fSceneNode); + mgr->WriteKey(stream, fWorldKey); + mgr->WriteKey(stream, fSndGroup); + + hsPoint3 pos; + hsQuat rot; + IGetPositionSim(pos); + IGetRotationSim(rot); + pos.Write(stream); + rot.Write(stream); + + fProps.Write(stream); + + if (fBoundsType == plSimDefs::kSphereBounds) + { + const NxSphereShape* sphereShape = shape->isSphere(); + stream->WriteSwapScalar(sphereShape->getRadius()); + hsPoint3 localPos = plPXConvert::Point(sphereShape->getLocalPosition()); + localPos.Write(stream); + } + else if (fBoundsType == plSimDefs::kBoxBounds) + { + const NxBoxShape* boxShape = shape->isBox(); + hsPoint3 dim = plPXConvert::Point(boxShape->getDimensions()); + dim.Write(stream); + hsPoint3 localPos = plPXConvert::Point(boxShape->getLocalPosition()); + localPos.Write(stream); + } + else + { + if (fBoundsType == plSimDefs::kHullBounds) + hsAssert(shape->isConvexMesh(), "Hull shape isn't a convex mesh"); + else + hsAssert(shape->isTriangleMesh(), "Exact shape isn't a trimesh"); + + // We hide the stream we used to create this mesh away in the shape user data. + // Pull it out and write it to disk. + hsVectorStream* vecStream = (hsVectorStream*)shape->userData; + stream->Write(vecStream->GetEOF(), vecStream->GetData()); + delete vecStream; + } +} + +// +// TESTING SDL +// Send phys sendState msg to object's plPhysicalSDLModifier +// +hsBool plPXPhysical::DirtySynchState(const char* SDLStateName, UInt32 synchFlags ) +{ + if (GetObjectKey()) + { + plSynchedObject* so=plSynchedObject::ConvertNoRef(GetObjectKey()->ObjectIsLoaded()); + if (so) + { + fLastSyncTime = hsTimer::GetSysSeconds(); + return so->DirtySynchState(SDLStateName, synchFlags); + } + } + + return false; +} + +void plPXPhysical::GetSyncState(hsPoint3& pos, hsQuat& rot, hsVector3& linV, hsVector3& angV) +{ + IGetPositionSim(pos); + IGetRotationSim(rot); + GetLinearVelocitySim(linV); + GetAngularVelocitySim(angV); +} + +void plPXPhysical::SetSyncState(hsPoint3* pos, hsQuat* rot, hsVector3* linV, hsVector3* angV) +{ + bool initialSync = plNetClientApp::GetInstance()->IsLoadingInitialAgeState() && + plNetClientApp::GetInstance()->GetJoinOrder() == 0; + + // If the physical has fallen out of the sim, and this is initial age state, and we're + // the first person in, reset it to the original position. (ie, prop the default state + // we've got right now) + if (pos && pos->fZ < kMaxNegativeZPos && initialSync) + { + SimLog("Physical %s loaded out of range state. Forcing initial state to server.", GetKeyName()); + DirtySynchState(kSDLPhysical, plSynchedObject::kBCastToClients); + return; + } + + if (pos) + ISetPositionSim(*pos); + if (rot) + ISetRotationSim(*rot); + + if (linV) + SetLinearVelocitySim(*linV); + if (angV) + SetAngularVelocitySim(*angV); + + SendNewLocation(false, true); +} + +void plPXPhysical::ExcludeRegionHack(hsBool cleared) +{ + NxShape* shape = fActor->getShapes()[0]; + shape->setFlag(NX_TRIGGER_ON_ENTER, !cleared); + shape->setFlag(NX_TRIGGER_ON_LEAVE, !cleared); + fGroup = cleared ? plSimDefs::kGroupExcludeRegion : plSimDefs::kGroupDetector; + shape->setGroup(fGroup); + /*if switching a static need to inform the controller that it needs to rebuild + the collision cache otherwise will still think that the detector is still static or that + the static is still a detector*/ + plPXPhysicalControllerCore::RebuildCache(); + +} +hsBool plPXPhysical::OverlapWithCapsule(NxCapsule& cap) +{ + NxShape* shape = fActor->getShapes()[0]; + return shape->checkOverlapCapsule(cap); +} + +hsBool plPXPhysical::IsDynamic() const +{ + return fGroup == plSimDefs::kGroupDynamic && + !GetProperty(plSimulationInterface::kPhysAnim); +} + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" + +// Some helper functions for pulling info out of a PhysX trimesh description +inline hsPoint3& GetTrimeshVert(NxTriangleMeshDesc& desc, int idx) +{ + return *((hsPoint3*)(((char*)desc.points)+desc.pointStrideBytes*idx)); +} + +void GetTrimeshTri(NxTriangleMeshDesc& desc, int idx, UInt16* out) +{ + if (hsCheckBits(desc.flags, NX_MF_16_BIT_INDICES)) + { + UInt16* descTris = ((UInt16*)(((char*)desc.triangles)+desc.pointStrideBytes*idx)); + out[0] = descTris[0]; + out[1] = descTris[1]; + out[2] = descTris[2]; + } + else + { + UInt32* descTris = ((UInt32*)(((char*)desc.triangles)+desc.pointStrideBytes*idx)); + out[0] = (UInt16)descTris[0]; + out[1] = (UInt16)descTris[1]; + out[2] = (UInt16)descTris[2]; + } +} + +// Some helper functions for pulling info out of a PhysX trimesh description +inline hsPoint3& GetConvexVert(NxConvexMeshDesc& desc, int idx) +{ + return *((hsPoint3*)(((char*)desc.points)+desc.pointStrideBytes*idx)); +} + +void GetConvexTri(NxConvexMeshDesc& desc, int idx, UInt16* out) +{ + if (hsCheckBits(desc.flags, NX_MF_16_BIT_INDICES)) + { + UInt16* descTris = ((UInt16*)(((char*)desc.triangles)+desc.pointStrideBytes*idx)); + out[0] = descTris[0]; + out[1] = descTris[1]; + out[2] = descTris[2]; + } + else + { + UInt32* descTris = ((UInt32*)(((char*)desc.triangles)+desc.pointStrideBytes*idx)); + out[0] = (UInt16)descTris[0]; + out[1] = (UInt16)descTris[1]; + out[2] = (UInt16)descTris[2]; + } +} + +// Make a visible object that can be viewed by users for debugging purposes. +plDrawableSpans* plPXPhysical::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + plDrawableSpans* myDraw = addTo; + hsMatrix44 l2w, unused; + GetTransform(l2w, unused); + + hsBool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); + + NxShape* shape = fActor->getShapes()[0]; + + NxTriangleMeshShape* trimeshShape = shape->isTriangleMesh(); + if (trimeshShape) + { + NxTriangleMeshDesc desc; + trimeshShape->getTriangleMesh().saveToDesc(desc); + + hsTArray pos; + hsTArray tris; + + const int kMaxTris = 10000; + const int kMaxVerts = 32000; + if ((desc.numVertices < kMaxVerts) && (desc.numTriangles < kMaxTris)) + { + pos.SetCount(desc.numVertices); + tris.SetCount(desc.numTriangles * 3); + + for (int i = 0; i < desc.numVertices; i++ ) + pos[i] = GetTrimeshVert(desc, i); + + for (int i = 0; i < desc.numTriangles; i++) + GetTrimeshTri(desc, i, &tris[i*3]); + + myDraw = plDrawableGenerator::GenerateDrawable(pos.GetCount(), + pos.AcquireArray(), + nil, // normals - def to avg (smooth) norm + nil, // uvws + 0, // uvws per vertex + nil, // colors - def to white + true, // do a quick fake shade + nil, // optional color modulation + tris.GetCount(), + tris.AcquireArray(), + mat, + l2w, + blended, + &idx, + myDraw); + } + else + { + int curTri = 0; + int trisToDo = desc.numTriangles; + while (trisToDo > 0) + { + int trisThisRound = trisToDo > kMaxTris ? kMaxTris : trisToDo; + + trisToDo -= trisThisRound; + + pos.SetCount(trisThisRound * 3); + tris.SetCount(trisThisRound * 3); + + for (int i = 0; i < trisThisRound; i++) + { + GetTrimeshTri(desc, curTri, &tris[i*3]); + pos[i*3 + 0] = GetTrimeshVert(desc, tris[i*3+0]); + pos[i*3 + 1] = GetTrimeshVert(desc, tris[i*3+1]); + pos[i*3 + 2] = GetTrimeshVert(desc, tris[i*3+2]); + + curTri++; + } + myDraw = plDrawableGenerator::GenerateDrawable(pos.GetCount(), + pos.AcquireArray(), + nil, // normals - def to avg (smooth) norm + nil, // uvws + 0, // uvws per vertex + nil, // colors - def to white + true, // do a quick fake shade + nil, // optional color modulation + tris.GetCount(), + tris.AcquireArray(), + mat, + l2w, + blended, + &idx, + myDraw); + } + } + } + + NxConvexShape* convexShape = shape->isConvexMesh(); + if (convexShape) + { + NxConvexMeshDesc desc; + convexShape->getConvexMesh().saveToDesc(desc); + + hsTArray pos; + hsTArray tris; + + pos.SetCount(desc.numVertices); + tris.SetCount(desc.numTriangles * 3); + + for (int i = 0; i < desc.numVertices; i++ ) + pos[i] = GetConvexVert(desc, i); + + for (int i = 0; i < desc.numTriangles; i++) + GetConvexTri(desc, i, &tris[i*3]); + + myDraw = plDrawableGenerator::GenerateDrawable(pos.GetCount(), + pos.AcquireArray(), + nil, // normals - def to avg (smooth) norm + nil, // uvws + 0, // uvws per vertex + nil, // colors - def to white + true, // do a quick fake shade + nil, // optional color modulation + tris.GetCount(), + tris.AcquireArray(), + mat, + l2w, + blended, + &idx, + myDraw); + } + + NxSphereShape* sphere = shape->isSphere(); + if (sphere) + { + float radius = sphere->getRadius(); + hsPoint3 offset = plPXConvert::Point(sphere->getLocalPosition()); + myDraw = plDrawableGenerator::GenerateSphericalDrawable(offset, radius, + mat, l2w, blended, + nil, &idx, myDraw); + } + + NxBoxShape* box = shape->isBox(); + if (box) + { + hsPoint3 dim = plPXConvert::Point(box->getDimensions()); + myDraw = plDrawableGenerator::GenerateBoxDrawable(dim.fX*2.f, dim.fY*2.f, dim.fZ*2.f, + mat,l2w,blended, + nil,&idx,myDraw); + } + return myDraw; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h new file mode 100644 index 00000000..2bd5a243 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysical.h @@ -0,0 +1,277 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPXPhysical_h_inc +#define plPXPhysical_h_inc + +#include "plPhysical.h" +#include "hsMatrix44.h" +#include "../plPhysical/plSimDefs.h" +#include "hsBitVector.h" +#include "hsUtils.h" + +class NxActor; +class NxConvexMesh; +class NxTriangleMesh; + +struct hsPoint3; +class hsQuat; +class plPhysicalProxy; +class plDrawableSpans; +class hsGMaterial; +struct hsPlane3; + +class plMessage; +class plLOSHit; +class plSimulationMsg; +class plSDLModifier; +class plPhysicalSndGroup; +class plGenRefMsg; +class plSceneObject; +class hsVectorStream; +class NxCapsule; + +class PhysRecipe +{ +public: + PhysRecipe(); + + hsScalar mass; + hsScalar friction; + hsScalar restitution; + plSimDefs::Bounds bounds; + plSimDefs::Group group; + UInt32 reportsOn; + plKey objectKey; + plKey sceneNode; + plKey worldKey; + + // The local to subworld matrix (or local to world if worldKey is nil) + hsMatrix44 l2s; + + NxConvexMesh* convexMesh; + NxTriangleMesh* triMesh; + + // For spheres only + hsScalar radius; + hsPoint3 offset; + + // For Boxes + hsPoint3 bDimensions; + hsPoint3 bOffset; + + // For export time only. The original data used to create the mesh + hsVectorStream* meshStream; +}; + +class plPXPhysical : public plPhysical +{ +public: + friend class plSimulationMgr; + + enum PhysRefType + { + kPhysRefWorld, + kPhysRefSndGroup + }; + + plPXPhysical(); + virtual ~plPXPhysical(); + + CLASSNAME_REGISTER(plPXPhysical); + GETINTERFACE_ANY(plPXPhysical, plPhysical); + + // Export time and internal use only + hsBool Init(PhysRecipe& recipe); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + // + // From plPhysical + // + virtual plPhysical& SetProperty(int prop, hsBool b); + virtual hsBool GetProperty(int prop) const { return fProps.IsBitSet(prop) != 0; } + + virtual void SetObjectKey(plKey key) { fObjectKey = key; } + virtual plKey GetObjectKey() const { return fObjectKey; } + + virtual void SetSceneNode(plKey node); + virtual plKey GetSceneNode() const; + + virtual hsBool GetLinearVelocitySim(hsVector3& vel) const; + virtual void SetLinearVelocitySim(const hsVector3& vel); + virtual void ClearLinearVelocity(); + + virtual hsBool GetAngularVelocitySim(hsVector3& vel) const; + virtual void SetAngularVelocitySim(const hsVector3& vel); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, hsBool force=false); + virtual void GetTransform(hsMatrix44& l2w, hsMatrix44& w2l); + + virtual int GetGroup() const { return fGroup; } + + virtual void AddLOSDB(UInt16 flag) { hsSetBits(fLOSDBs, flag); } + virtual void RemoveLOSDB(UInt16 flag) { hsClearBits(fLOSDBs, flag); } + virtual UInt16 GetAllLOSDBs() { return fLOSDBs; } + virtual hsBool IsInLOSDB(UInt16 flag) { return hsCheckBits(fLOSDBs, flag); } + + virtual hsBool DoDetectorHullWorkaround() { return fSaveTriangles ? true : false; } + virtual hsBool Should_I_Trigger(hsBool enter, hsPoint3& pos); + virtual hsBool IsObjectInsideHull(const hsPoint3& pos); + virtual void SetInsideConvexHull(hsBool inside) { fInsideConvexHull = inside; } + + virtual plKey GetWorldKey() const { return fWorldKey; } + + virtual plPhysicalSndGroup* GetSoundGroup() const { return fSndGroup; } + + virtual void GetPositionSim(hsPoint3& pos) const { IGetPositionSim(pos); } + + virtual void SendNewLocation(hsBool synchTransform = false, hsBool isSynchUpdate = false); + + virtual void SetHitForce(const hsVector3& force, const hsPoint3& pos) { fWeWereHit=true; fHitForce = force; fHitPos = pos; } + virtual void ApplyHitForce(); + virtual void ResetHitForce() { fWeWereHit=false; fHitForce.Set(0,0,0); fHitPos.Set(0,0,0); } + + virtual void GetSyncState(hsPoint3& pos, hsQuat& rot, hsVector3& linV, hsVector3& angV); + virtual void SetSyncState(hsPoint3* pos, hsQuat* rot, hsVector3* linV, hsVector3* angV); + + virtual void ExcludeRegionHack(hsBool cleared); + + virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); + + hsBool DoReportOn(plSimDefs::Group group) const { return hsCheckBits(fReportsOn, 1<= 0.f ) + return false; + + return true; + } + + // we need to remember the last matrices we sent to the coordinate interface + // so that we can recognize them when we send them back and not reapply them, + // which would reactivate our body. inelegant but effective + hsMatrix44 fCachedLocal2World; + + // Syncronization + double fLastSyncTime; + plSDLModifier* fSDLMod; + + plPhysicalSndGroup* fSndGroup; + + hsBool fWeWereHit; + hsVector3 fHitForce; + hsPoint3 fHitPos; + + plPhysicalProxy* fProxyGen; // visual proxy for debugging + + static int fNumberAnimatedPhysicals; + static int fNumberAnimatedActivators; +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.cpp new file mode 100644 index 00000000..6a5c7d1d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.cpp @@ -0,0 +1,1263 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPXPhysicalController.h" +#include "plSimulationMgr.h" +#include "plPXPhysical.h" +#include "plPXConvert.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plCorrectionMsg.h" +#include "../plAvatar/plArmatureMod.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../plPhysical/plPhysicalProxy.h" + +#include "../pnMessage/plSetNetGroupIDMsg.h" +#include "../plMessage/plCollideMsg.h" + +#include "NxPhysics.h" +#include "ControllerManager.h" +#include "NxCapsuleController.h" +#include "NxCapsuleShape.h" + +#define kPhysxSkinWidth 0.1f +#define kPhysZOffset ((fRadius + (fHeight / 2)) + kPhysxSkinWidth) +#define kSLOPELIMIT (cosf(NxMath::degToRad(55.f))) +//#define kPhysicalHeightFudge 0.4f // this fudge was used for PhysX 2.4 +#define kPhysicalHeightFudge 0.0f + +//#define STEP_OFFSET 1.0f +#define STEP_OFFSET 0.5f +//#define STEP_OFFSET 0.15f + + +#ifndef PLASMA_EXTERNAL_RELEASE +hsBool plPXPhysicalController::fDebugDisplay = false; +#endif // PLASMA_EXTERNAL_RELEASE + +static ControllerManager gControllerMgr; +static std::vector gControllers; +static gRebuildCache = false; + +// KLUDGE: From plPXPhysical.cpp +bool CompareMatrices(const hsMatrix44 &matA, const hsMatrix44 &matB, float tolerance); + +plPhysicalController* plPhysicalController::Create(plKey ownerSO, hsScalar height, hsScalar width) +{ + hsScalar radius = width / 2.f; + //hsScalar realHeight = height - width; + hsScalar realHeight = height - radius + kPhysicalHeightFudge; + return TRACKED_NEW plPXPhysicalController(ownerSO, radius, realHeight); +} + +////////////////////////////////////////////////////////////////////////// + +plPXPhysicalController* plPXPhysicalController::FindController(NxController* controller) +{ + for (int i = 0; i < gControllers.size(); i++) + { + plPXPhysicalController* ac = gControllers[i]; + if (ac->fController == controller) + return ac; + } + return nil; +} + +plPXPhysicalController* plPXPhysicalController::GetController(NxActor& actor, bool* isController) +{ + *isController = false; + for (int i = 0; i < gControllers.size(); i++) + { + plPXPhysicalController* ac = gControllers[i]; + if (ac->fController && ac->fController->getActor() == &actor) + { + *isController = true; + return ac; + } + if ( ac->fKinematicActor == &actor) + { + return ac; + } + } + + return nil; +} +void plPXPhysicalController::GetWorldSpaceCapsule(NxCapsule& cap) +{ + if(fController){ + int numshapes=fController->getActor()->getNbShapes(); + if (numshapes==1) + {//there should only be one shape on a controller + NxShape* const *shapes=fController->getActor()->getShapes(); + //and since it is a capsule controller it better be a capsule; + NxCapsuleShape *capShape = shapes[0]->isCapsule(); + if(capShape) capShape->getWorldCapsule(cap); + + } + } + +} +bool plPXPhysicalController::AnyControllersInThisWorld(plKey world) +{ + for (int i = 0; i < gControllers.size(); i++) + { + plPXPhysicalController* ac = gControllers[i]; + if (ac->GetSubworld() == world) + return true; + } + return false; +} + +int plPXPhysicalController::NumControllers() +{ + return gControllers.size(); +} +int plPXPhysicalController::GetControllersInThisSubWorld(plKey world, int maxToReturn,plPXPhysicalController** bufferout) +{ + int i=0; + for (int j=0;jGetSubworld()==world) + { + if(iGetSubworld()==world)i++; + } + return i; +} +void plPXPhysicalController::Update(bool prestep, hsScalar delSecs) +{ + // Apparently the user data field of the controllers is broken +// UInt32 count = gControllerMgr.getNbControllers(); +// NxController* controllers = (NxController*)gControllerMgr.getControllers(); +// +// for (int i = 0; i < count; i++) +// { +// plPXPhysicalController* ac = (plPXPhysicalController*)controllers[i].getAppData(); + for (int i = 0; i < gControllers.size(); i++) + { + plPXPhysicalController* ac = gControllers[i]; + + hsAssert(ac, "Bad avatar controller"); + if (prestep) + { + if (gRebuildCache) + ac->fController->reportSceneChanged(); + ac->IApply(delSecs); + } + else + { + gControllerMgr.updateControllers(); + ac->ISendUpdates(delSecs); + + if (ac->GetSubworldCI()) + ac->fPrevSubworldW2L = ac->GetSubworldCI()->GetWorldToLocal(); + else + { + if (!ac->fPrevSubworldW2L.IsIdentity()) + ac->fPrevSubworldW2L.Reset(); + } + } + } + + gRebuildCache = false; +} + +void plPXPhysicalController::RebuildCache() +{ + gRebuildCache = true; +} +void plPXPhysicalController::IInformDetectors(bool entering) +{ + static const NxU32 DetectorFlag= 1<GetScene(fWorldKey); + int kNumofShapesToStore=30; + NxCapsule cap; + GetWorldSpaceCapsule(cap); + NxShape* shapes[30]; + int numCollided=scene->overlapCapsuleShapes(cap,NX_ALL_SHAPES,kNumofShapesToStore,shapes,NULL,DetectorFlag,NULL,true); + for (int i=0;igetActor()); + + if (myactor) + { + + plPXPhysical* physical = (plPXPhysical*)myactor->userData; + if (physical) + { + plCollideMsg* msg = TRACKED_NEW plCollideMsg; + + msg->fOtherKey = fOwner; + msg->fEntering = entering; + msg->AddReceiver(physical->GetKey()); + msg->Send(); + } + } + } + + + } + +} + +////////////////////////////////////////////////////////////////////////// + + +class PXControllerHitReport : public NxUserControllerHitReport +{ +public: + virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit) + { + plPXPhysicalController* ac = plPXPhysicalController::FindController(hit.controller); + + NxActor& actor = hit.shape->getActor(); + plPXPhysical* phys = (plPXPhysical*)actor.userData; + + static hsScalar SlopeLimit = kSLOPELIMIT; + hsVector3 normal = plPXConvert::Vector(hit.worldNormal); + hsScalar dot = normal * kAvatarUp; + if ( dot < SlopeLimit ) + ac->AddSlidingNormal(normal); + else + ac->GroundHit(); + +#ifndef PLASMA_EXTERNAL_RELEASE + plDbgCollisionInfo info; + info.fNormal = normal; + info.fSO = plSceneObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); + info.fOverlap = false; + NxShape* const *shapes = hit.controller->getActor()->getShapes(); + int numShapes = hit.controller->getActor()->getNbShapes(); + int i; + for (i = 0; i < numShapes; i++) + { + // should only be one capsule shape + const NxCapsuleShape *capShape = shapes[i]->isCapsule(); + if (capShape) + { + NxCapsule cap; + capShape->getWorldCapsule(cap); + if (hit.shape->checkOverlapCapsule(cap)) + info.fOverlap = true; + } + } + ac->fDbgCollisionInfo.Append(info); +#endif PLASMA_EXTERNAL_RELEASE + + // If the avatar hit a movable physical, apply some force to it. + if (actor.isDynamic() ) + { + if ( !actor.readBodyFlag(NX_BF_KINEMATIC) && !actor.readBodyFlag(NX_BF_FROZEN)) + { + // If this is the local avatar, we need to take ownership of this + // dynamic if we haven't already + if (ac->fLOSDB == plSimDefs::kLOSDBLocalAvatar && !phys->IsLocallyOwned() && + !phys->GetProperty(plSimulationInterface::kNoOwnershipChange)) + { + plSynchedObject* obj = plSynchedObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); + + obj->SetNetGroupConstant(plNetGroup::kNetGroupLocalPhysicals); + + // Tell all the other clients that we own this physical + plSetNetGroupIDMsg* setNetGroupID = TRACKED_NEW plSetNetGroupIDMsg; + setNetGroupID->fId = plNetGroup::kNetGroupRemotePhysicals; + setNetGroupID->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce); + setNetGroupID->SetBCastFlag(plMessage::kLocalPropagate, false); + setNetGroupID->Send(obj->GetKey()); + } + + plSimulationMgr::GetInstance()->ConsiderSynch(phys, nil); + + hsVector3 dir = plPXConvert::Vector(hit.dir); + // We only allow horizontal pushes. Vertical pushes when we stand on + // dynamic objects creates useless stress on the solver. + if (dir.fZ < 0) + { + dir.fZ = 0; + dir.Normalize(); + } + + if (!dir.IsEmpty()) + { + static hsScalar kForceScale = 5.f; + //static hsScalar kForceScale = 4.f; + NxF32 coeff = actor.getMass() * hit.length * kForceScale; + hsPoint3 pos((hsScalar)hit.worldPos.x, (hsScalar)hit.worldPos.y, (hsScalar)hit.worldPos.z); + phys->SetHitForce((dir*coeff), pos); + } + } + } + else // else if the avatar hit a static + { + return NX_ACTION_NONE; + } + + if (phys && phys->GetProperty(plSimulationInterface::kAvAnimPushable)) + { + hsQuat inverseRotation = ac->fLocalRotation.Inverse(); + hsVector3 normal = plPXConvert::Vector(hit.worldNormal); + ac->fPushingPhysical = phys; + ac->fFacingPushingPhysical = (inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false); + } + + return NX_ACTION_NONE; + } + + virtual NxControllerAction onControllerHit(const NxControllersHit& hit) + { + return NX_ACTION_NONE; + } + +} gMyReport; + + +////////////////////////////////////////////////////////////////////////// + +const hsScalar plPXPhysicalController::kAirTimeThreshold = .1f; // seconds + +plPXPhysicalController::plPXPhysicalController(plKey ownerSO, hsScalar radius, hsScalar height) + : fOwner(ownerSO) + , fWorldKey(nil) + , fRadius(radius) + , fHeight(height) + , fController(nil) + , fLinearVelocity(0, 0, 0) + , fAngularVelocity(0) + , fAchievedLinearVelocity(0, 0, 0) + , fLocalPosition(0, 0, 0) + , fLocalRotation(0, 0, 0, 1) + , fEnable(true) + , fEnableChanged(false) + , fLOSDB(plSimDefs::kLOSDBNone) + , fGroundHit(false) + , fFalseGround(false) + , fTimeInAir(0) + , fPushingPhysical(nil) + , fFacingPushingPhysical(false) + , fProxyGen(nil) + , fKinematicActor(nil) + , fKinematic(false) + , fKinematicChanged(false) + , fKinematicEnableNextUpdate(false) + , fHitHead(false) +{ + gControllers.push_back(this); + fLastGlobalLoc.Reset(); + ICreateController(); + Enable(false); +} + +plPXPhysicalController::~plPXPhysicalController() +{ + IDeleteController(); + + for (int i = 0; i < gControllers.size(); i++) + { + if (gControllers[i] == this) + { + gControllers.erase(gControllers.begin()+i); + break; + } + } + + delete fProxyGen; +} + +// WARNING: If this is an armatureMod, it'll have its own idea about when +// physics should be enabled/disabled. Use plArmatureModBase::EnablePhysics() instead. +void plPXPhysicalController::Enable(bool enable) +{ + if (fEnable != enable) + { + fEnable = enable; + if (fEnable) + fEnableChanged = true; + else + { + // See ISendUpdates for why we don't re-enable right away + fController->setCollision(fEnable); + } + } +} + +void plPXPhysicalController::AddSlidingNormal(hsVector3 vec) +{ + // We get lots of duplicates, so check. + int i; + for (i = 0; i < fSlidingNormals.GetCount(); i++) + { + if (hsABS(fSlidingNormals[i].fX - vec.fX) <= .01 && + hsABS(fSlidingNormals[i].fY - vec.fY) <= .01 && + hsABS(fSlidingNormals[i].fZ - vec.fZ) <= .01) + { + return; + } + } + fSlidingNormals.Append(vec); +} + + +void plPXPhysicalController::IGetPositionSim(hsPoint3& pos) const +{ + const NxExtendedVec3& nxPos = fController->getPosition(); + pos.Set(hsScalar(nxPos.x), hsScalar(nxPos.y), hsScalar(nxPos.z) - kPhysZOffset); +} + +void plPXPhysicalController::SetSubworld(plKey world) +{ + if (fWorldKey != world) + { + bool wasEnabled = fEnable; +#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND + // PHYSX FIXME - before leaving this world, sending leaving detector events if we are inside a convex hull detector + hsPoint3 pos; + IGetPositionSim(pos); + plSimulationMgr::GetInstance()->UpdateDetectorsInScene(fWorldKey,GetOwner(),pos,false); +#endif // USE_PHYSX_CONVEXHULL_WORKAROUND + //need to inform detectors in the old world that we are leaving + //IInformDetectors(false); + //done informing old world + + IDeleteController(); + fWorldKey = world; + ICreateController(); + if (wasEnabled) + Enable(false); + // need to disable the kinematic also so that it doesn't trip over random detector regions + fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); + hsMatrix44 globalLoc = fLastGlobalLoc; + if (GetSubworldCI()) + fPrevSubworldW2L = GetSubworldCI()->GetWorldToLocal(); + ISetGlobalLoc(globalLoc); + // we need to let the re-enable code put thing in order... so that 0,0,0 is not triggered and ISendUpdates do the enable and update detectors + if (wasEnabled) + Enable(true); + // and then re-enable the kinematic on the next update (ISendUpdates) + fKinematicEnableNextUpdate = true; + plPXPhysicalController::RebuildCache(); + } +} + +const plCoordinateInterface* plPXPhysicalController::GetSubworldCI() const +{ + if (fWorldKey) + { + plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded()); + if (so) + return so->GetCoordinateInterface(); + } + return nil; +} + +void plPXPhysicalController::GetState(hsPoint3& pos, float& zRot) +{ + // Temporarily use the position point while we get the z rotation + fLocalRotation.NormalizeIfNeeded(); + fLocalRotation.GetAngleAxis(&zRot, (hsVector3*)&pos); + + if (pos.fZ < 0) + zRot = (2 * hsScalarPI) - zRot; // axis is backwards, so reverse the angle too + + pos = fLocalPosition; +} + +void plPXPhysicalController::SetState(const hsPoint3& pos, float zRot) +{ + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + hsQuat worldRot; + hsVector3 zAxis(0.f, 0.f, 1.f); + worldRot.SetAngleAxis(zRot, zAxis); + + hsMatrix44 l2w, w2l; + worldRot.MakeMatrix(&l2w); + l2w.SetTranslate(&pos); + + // Localize new position and rotation to global coords if we're in a subworld + const plCoordinateInterface* ci = GetSubworldCI(); + if (ci) + { + const hsMatrix44& subworldL2W = ci->GetLocalToWorld(); + l2w = subworldL2W * l2w; + } + + l2w.GetInverse(&w2l); + so->SetTransform(l2w, w2l); + so->FlushTransform(); + } +} + +void plPXPhysicalController::ISetGlobalLoc(const hsMatrix44& l2w) +{ + fLastGlobalLoc = l2w; + + // Update our subworld position and rotation + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + { + const hsMatrix44& w2s = fPrevSubworldW2L; + hsMatrix44 l2s = w2s * l2w; + + l2s.GetTranslate(&fLocalPosition); + fLocalRotation.SetFromMatrix44(l2s); + } + else + { + l2w.GetTranslate(&fLocalPosition); + fLocalRotation.SetFromMatrix44(l2w); + } + + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + if (fProxyGen) + fProxyGen->SetTransform(l2w, w2l); + + // Update the physical position + NxExtendedVec3 nxPos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kPhysZOffset); + fController->setPosition(nxPos); + IMatchKinematicToController(); +} + +void plPXPhysicalController::IMatchKinematicToController() +{ + if ( fKinematicActor) + { + NxExtendedVec3 cPos = fController->getPosition(); + NxVec3 prevKinPos = fKinematicActor->getGlobalPosition(); + NxVec3 kinPos; + kinPos.x = (NxReal)cPos.x; + kinPos.y = (NxReal)cPos.y; + kinPos.z = (NxReal)cPos.z; + if (plSimulationMgr::fExtraProfile) + SimLog("Match setting kinematic from %f,%f,%f to %f,%f,%f",prevKinPos.x,prevKinPos.y,prevKinPos.z,kinPos.x,kinPos.y,kinPos.z ); + fKinematicActor->setGlobalPosition(kinPos); + } +} + +void plPXPhysicalController::IMoveKinematicToController(hsPoint3& pos) +{ + if ( fKinematicActor) + { + NxVec3 kinPos = fKinematicActor->getGlobalPosition(); + if ( abs(kinPos.x-pos.fX) + abs(kinPos.y-pos.fY) + (abs(kinPos.z-pos.fZ-kPhysZOffset)) > 0.0001f) + { + NxVec3 newPos; + newPos.x = (NxReal)pos.fX; + newPos.y = (NxReal)pos.fY; + newPos.z = (NxReal)pos.fZ+kPhysZOffset; + if (fEnable || fKinematic) + { + if (plSimulationMgr::fExtraProfile) + SimLog("Moving kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); + // use the position + fKinematicActor->moveGlobalPosition(newPos); + } + else + { + if (plSimulationMgr::fExtraProfile) + SimLog("Setting kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); + fKinematicActor->setGlobalPosition(newPos); + } + } + } +} + +void plPXPhysicalController::ISetKinematicLoc(const hsMatrix44& l2w) +{ + + hsPoint3 kPos; + // Update our subworld position and rotation + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + { + const hsMatrix44& w2s = subworldCI->GetWorldToLocal(); + hsMatrix44 l2s = w2s * l2w; + + l2s.GetTranslate(&kPos); + } + else + { + l2w.GetTranslate(&kPos); + } + + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + if (fProxyGen) + fProxyGen->SetTransform(l2w, w2l); + + // add z offset + kPos.fZ += kPhysZOffset; + // Update the physical position of kinematic + if (fEnable || fKinematic) + fKinematicActor->moveGlobalPosition(plPXConvert::Point(kPos)); + else + fKinematicActor->setGlobalPosition(plPXConvert::Point(kPos)); +} + + +void plPXPhysicalController::Kinematic(bool state) +{ + if (fKinematic != state) + { + fKinematic = state; + if (fKinematic) + { + // See ISendUpdates for why we don't re-enable right away + fController->setCollision(false); +#ifdef PHYSX_KINEMATIC_IS_DISABLED + fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); +#endif + } + else + { + fKinematicChanged = true; + } + } +} + +bool plPXPhysicalController::IsKinematic() +{ + if (fKinematicActor) + { +#ifdef PHYSX_KINEMATIC_IS_DISABLED + if (!fKinematicActor->readActorFlag(NX_AF_DISABLE_COLLISION)) + return true; +#else + return fKinematic; +#endif + } + return false; +} + +void plPXPhysicalController::GetKinematicPosition(hsPoint3& pos) +{ + pos.Set(-1,-1,-1); + if ( fKinematicActor ) + { + NxVec3 klPos = fKinematicActor->getGlobalPosition(); + pos.Set(hsScalar(klPos.x), hsScalar(klPos.y), hsScalar(klPos.z) - kPhysZOffset); + } +} + + +void plPXPhysicalController::IApply(hsScalar delSecs) +{ + /*static const UInt32 collideFlags = + 1<ObjectIsLoaded()); + if (so) + { + // If we've been moved since the last physics update (somebody warped us), + // update the physics before we apply velocity. + const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); + if (!CompareMatrices(l2w, fLastGlobalLoc, .0001f)) + { + ISetKinematicLoc(l2w); + } + } + // then jump out + return; + } + + if (!fEnable) + return; + + bool gotGroundHit = fGroundHit; + fGroundHit = false; + + fPushingPhysical = nil; + fFacingPushingPhysical = false; + + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + // If we've been moved since the last physics update (somebody warped us), + // update the physics before we apply velocity. + const hsMatrix44& l2w = so->GetCoordinateInterface()->GetLocalToWorld(); + if (!CompareMatrices(l2w, fLastGlobalLoc, .0001f)) + ISetGlobalLoc(l2w); + + // Convert our avatar relative velocity to subworld relative + if (!fLinearVelocity.IsEmpty()) + { + fLinearVelocity = l2w * fLinearVelocity; + + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + fLinearVelocity = subworldCI->GetWorldToLocal() * fLinearVelocity; + } + + // Add in gravity if the avatar's z velocity isn't being set explicitly + // (Add in a little fudge factor, since the animations usually add a + // tiny bit of z.) + if (hsABS(fLinearVelocity.fZ) < 0.001f) + { + static const float kGravity = -32.f; + + // Get our previous z velocity. If we're on the ground, clamp it to zero at + // the largest, so we won't launch into the air if we're running uphill. + hsScalar prevZVel = fAchievedLinearVelocity.fZ; + if (IsOnGround()) + prevZVel = hsMinimum(prevZVel, 0.f); + + hsScalar grav = kGravity * delSecs; + + // If our gravity contribution isn't high enough this frame, we won't + // report a collision even when standing on solid ground. + hsScalar maxGrav = -.001f / delSecs; + if (grav > maxGrav) + grav = maxGrav; + + fLinearVelocity.fZ = prevZVel + grav; + + // Technically this is nonsensical and wrong, capping our velocity to + // an accelleration constant. But no one seems to really mind. + if (fLinearVelocity.fZ < kGravity) + fLinearVelocity.fZ = kGravity; + } + + // If we're airborne and the velocity isn't set, use the velocity from + // the last frame so we maintain momentum. + if (!IsOnGround() && fLinearVelocity.fX == 0.f && fLinearVelocity.fY == 0.f) + { + fLinearVelocity.fX = fAchievedLinearVelocity.fX; + fLinearVelocity.fY = fAchievedLinearVelocity.fY; + } + + if (!IsOnGround() || IsOnFalseGround()) + { + // We're not on solid ground, so we should be sliding against whatever + // we're hitting (like a rock cliff). Each vector in fSlidingNormals is + // the surface normal of a collision that's too steep to be ground, so + // we project our current velocity onto that plane and slide along the + // wall. + // + // Also, sometimes PhysX reports a bunch of collisions from the wall, + // but nothing from underneath (when there should be). So if we're not + // touching ground, we offset the avatar in the direction of the + // surface normal(s). This doesn't fix the issue 100%, but it's a hell + // of a lot better than nothing, and suitable duct tape until a future + // PhysX revision fixes the issue. + // + // Yes, there's room for optimization here if we care. + hsVector3 offset(0.f, 0.f, 0.f); + for (i = 0; i < fSlidingNormals.GetCount(); i++) + { + offset += fSlidingNormals[i]; + + hsVector3 velNorm = fLinearVelocity; + if (velNorm.MagnitudeSquared() > 0) + velNorm.Normalize(); + + if (velNorm * fSlidingNormals[i] < 0) + { + hsVector3 proj = (velNorm % fSlidingNormals[i]) % fSlidingNormals[i]; + if (velNorm * proj < 0) + proj *= -1.f; + + fLinearVelocity = fLinearVelocity.Magnitude() * proj; + } + } + if (offset.MagnitudeSquared() > 0) + { + // 5 ft/sec is roughly the speed we walk backwards. + // The higher the value, the less likely you'll trip + // the bug, and this seems reasonable. + offset.Normalize(); + fLinearVelocity += offset * 5; + } + } + + // Scale the velocity to our actual step size (by default it's feet/sec) + NxVec3 vel(fLinearVelocity.fX * delSecs, fLinearVelocity.fY * delSecs, fLinearVelocity.fZ * delSecs); + NxU32 colFlags = 0; + + fGroundHit = false; + fFalseGround = false; + fSlidingNormals.Swap(fPrevSlidingNormals); + fSlidingNormals.SetCount(0); + +#ifndef PLASMA_EXTERNAL_RELEASE + fDbgCollisionInfo.SetCount(0); +#endif // PLASMA_EXTERNAL_RELEASE + + fController->move(vel, collideFlags, 0.0001, colFlags); + + + ICheckForFalseGround(); + /*If the Physx controller thinks we have a collision from below, need to make sure we + have at least have false ground, otherwise Autostepping can send us into the air, and we will some times + float/panic link. For some reason the NxControllerHitReport does not always send messages + regarding Controller contact with ground plane, but will (almost) always return NXCC_COLLISION_DOWN + with the move method. + */ + if(((colFlags&NXCC_COLLISION_DOWN )==NXCC_COLLISION_DOWN )&&(fGroundHit==false)) + { + fFalseGround=true; + } + /* + The top sphere half was hit, but the ControllerHit Report doesn't know + In IUpdate fHitHead will be used to keep from gaining unrealistic velocity in the x&y Direction + */ + if(colFlags&NXCC_COLLISION_UP) + { + fHitHead=true; + } + +#ifndef PLASMA_EXTERNAL_RELEASE + if (fDebugDisplay) + IDrawDebugDisplay(); +#endif // PLASMA_EXTERNAL_RELEASE + } +} + +void plPXPhysicalController::ISendUpdates(hsScalar delSecs) +{ + if (!fEnable || fKinematic) + { + // When we're in non-phys or a behavior we can't go through the rest of the function + // so we need to get out early, but we need to update the current position if we're + // in a subworld. + + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + const plCoordinateInterface* ci = GetSubworldCI(); + if (ci && so) + { + const hsMatrix44& soL2W = so->GetCoordinateInterface()->GetLocalToWorld(); + const hsMatrix44& ciL2W = ci->GetLocalToWorld(); + + hsMatrix44 l2w = fPrevSubworldW2L * soL2W; + l2w = ciL2W * l2w; + + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + + ((plCoordinateInterface*)so->GetCoordinateInterface())->SetTransform(l2w, w2l); + ((plCoordinateInterface*)so->GetCoordinateInterface())->FlushTransform(); + + ISetGlobalLoc(l2w); + } + + return; + } + + // PhysX loves to cache stuff. However, it doesn't like to update it (see + // the RebuildCache crap above). Say the avatar is disabled and sitting at + // point 0,0,0. We warp him to some other position and enable him. If you + // do the enable before the sim step is done, regardless of whether you move + // him first, he will send out a penetration with any detector at 0,0,0. As + // far as I can tell there's no way around this, and I tried a lot of things. + // The only solution I found is to move the avatar, run the sim step, then + // enable him. This means he won't trigger any detectors at his new position + // until the next frame, but hopefully that won't be too noticeable. + if (fEnableChanged) + { + fEnableChanged = false; + fController->setCollision(fEnable); +#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND + // PHYSX FIXME - after re-enabling check to see if we are inside any convex hull detector regions + hsPoint3 pos; + IGetPositionSim(pos); + plSimulationMgr::GetInstance()->UpdateDetectorsInScene(fWorldKey,GetOwner(),pos,fEnable); +#endif // USE_PHYSX_CONVEXHULL_WORKAROUND + //IInformDetectors(true); + } + if (fKinematicChanged) + { + fKinematicChanged = false; + fController->setCollision(true); +#ifdef PHYSX_KINEMATIC_IS_DISABLED + fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); +#endif // PHYSX_KINEMATIC_IS_DISABLED + } + if (fKinematicEnableNextUpdate) + { + fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); + fKinematicEnableNextUpdate = false; + } + + if (!fGroundHit && !fFalseGround) + fTimeInAir += delSecs; + else + fTimeInAir = 0.f; + + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + // Get the current position of the physical + hsPoint3 curLocalPos; + IGetPositionSim(curLocalPos); + IMoveKinematicToController(curLocalPos); + + { + const hsMatrix44& w2l = so->GetCoordinateInterface()->GetLocalToWorld(); + fAchievedLinearVelocity = hsVector3(curLocalPos - fLocalPosition); + fAchievedLinearVelocity /= delSecs; + } + /*if we hit our head the sweep api might try to + move us laterally to go as high as we requested kind of like autostep, to top it off the + way the NxCharacter and the sweep api work as a whole NxControllerHitReport::OnShapeHit + wont be called regarding the head blow. + if we are airborne: with out this we will gain large amounts of velocity in the x y plane + and on account of fAchievedLinearVelocity being used in the next step we will fly sideways + */ + bool headhit=fHitHead; + fHitHead=false; + if(headhit&&!(fGroundHit||fFalseGround)) + { + //we have hit our head and we don't have anything beneath our feet + //not really friction just a way to make it seem more realistic keep between 0 and 1 + hsScalar headFriction=0.0; + fAchievedLinearVelocity.fX=(1.0-headFriction)*fLinearVelocity.fX; + fAchievedLinearVelocity.fY=(1.0-headFriction)*fLinearVelocity.fY; + //only clamping when hitting head and going upwards, if going down leave it be + // this should only occur when going down stairwells with low ceilings like in cleft + //kitchen area + if(fAchievedLinearVelocity.fZ>0.0) + { + fAchievedLinearVelocity.fZ=0.0; + } + + } + + // Apply angular velocity + if (fAngularVelocity != 0.f) + { + hsScalar angle; + hsVector3 axis; + fLocalRotation.NormalizeIfNeeded(); + fLocalRotation.GetAngleAxis(&angle, &axis); + + // adjust it (quaternions are weird...) + if (axis.fZ < 0) + angle = (2*hsScalarPI) - angle; // axis is backwards, so reverse the angle too + + angle += fAngularVelocity * delSecs; + + // make sure we wrap around + if (angle < 0) + angle = (2*hsScalarPI) + angle; // angle is -, so this works like a subtract + if (angle >= (2*hsScalarPI)) + angle = angle - (2*hsScalarPI); + + // and set the new angle + fLocalRotation.SetAngleAxis(angle, hsVector3(0,0,1)); + } + + // We can't only send updates when the physical position changes because the + // world relative position may be changing all the time if we're in a subworld. +// if (curLocalPos != fLocalPosition || fAngularVelocity != 0.f) + { + fLocalPosition = curLocalPos; + + // Apply rotation and translation + fLocalRotation.MakeMatrix(&fLastGlobalLoc); + fLastGlobalLoc.SetTranslate(&fLocalPosition); + + // Localize to global coords if in a subworld + const plCoordinateInterface* ci = GetSubworldCI(); + if (ci) + { + const hsMatrix44& l2w = ci->GetLocalToWorld(); + fLastGlobalLoc = l2w * fLastGlobalLoc; + } + + plCorrectionMsg* corrMsg = TRACKED_NEW plCorrectionMsg; + corrMsg->fLocalToWorld = fLastGlobalLoc; + corrMsg->fLocalToWorld.GetInverse(&corrMsg->fWorldToLocal); + corrMsg->fDirtySynch = true; + + hsMatrix44 w2l; + fLastGlobalLoc.GetInverse(&w2l); + //if (fProxyGen) + // fProxyGen->SetTransform(fLastGlobalLoc, w2l); + + // Send the new position to the plArmatureMod and the scene object + const plArmatureMod* armMod = plArmatureMod::ConvertNoRef(so->GetModifierByType(plArmatureMod::Index())); + if (armMod) + corrMsg->AddReceiver(armMod->GetKey()); + corrMsg->AddReceiver(fOwner); + + corrMsg->Send(); + } + } + + fLinearVelocity.Set(0, 0, 0); + fAngularVelocity = 0; +} + +void plPXPhysicalController::ICheckForFalseGround() +{ + if (fGroundHit) + return; // Already collided with "real" ground. + + // We need to check for the case where the avatar hasn't collided with "ground", but is colliding + // with a few other objects so that he's not actually falling (wedged in between some slopes). + // We do this by answering the following question (in 2d top-down space): "If you sort the contact + // normals by angle, is there a large enough gap between normals?" + // + // If you think in terms of geometry, this means a collection of surfaces are all pushing on you. + // If they're pushing from all sides, you have nowhere to go, and you won't fall. There needs to be + // a gap, so that you're pushed out and have somewhere to fall. This is the same as finding a gap + // larger than 180 degrees between sorted normals. + // + // The problem is that on top of that, the avatar needs enough force to shove him out that gap (he + // has to overcome friction). I deal with that by making the threshold (360 - (180 - 60) = 240). I've + // seen up to 220 reached in actual gameplay in a situation where we'd want this to take effect. + // This is the same running into 2 walls where the angle between them is 60. + int i, j; + const hsScalar threshold = hsScalarDegToRad(240); + int numContacts = fSlidingNormals.GetCount() + fPrevSlidingNormals.GetCount(); + if (numContacts >= 2) + { + // For extra fun... PhysX will actually report some collisions every other frame, as though + // we're bouncing back and forth between the two (or more) objects blocking us. So it's not + // enough to look at this frame's collisions, we have to check previous frames too. + hsTArray fCollisionAngles; + fCollisionAngles.SetCount(numContacts); + int angleIdx = 0; + for (i = 0; i < fSlidingNormals.GetCount(); i++, angleIdx++) + { + fCollisionAngles[angleIdx] = hsATan2(fSlidingNormals[i].fY, fSlidingNormals[i].fX); + } + for (i = 0; i < fPrevSlidingNormals.GetCount(); i++, angleIdx++) + { + fCollisionAngles[angleIdx] = hsATan2(fPrevSlidingNormals[i].fY, fPrevSlidingNormals[i].fX); + } + + // numContacts is rarely larger than 6, so let's do a simple bubble sort. + for (i = 0; i < numContacts; i++) + { + for (j = i + 1; j < numContacts; j++) + { + if (fCollisionAngles[i] > fCollisionAngles[j]) + { + hsScalar tempAngle = fCollisionAngles[i]; + fCollisionAngles[i] = fCollisionAngles[j]; + fCollisionAngles[j] = tempAngle; + } + } + } + + // sorted, now we check. + for (i = 1; i < numContacts; i++) + { + if (fCollisionAngles[i] - fCollisionAngles[i - 1] >= threshold) + break; + } + + if (i == numContacts) + { + // We got to the end. Check the last with the first and make your decision. + if (!(fCollisionAngles[0] - fCollisionAngles[numContacts - 1] >= (threshold - 2 * hsScalarPI))) + fFalseGround = true; + } + } +} + +void plPXPhysicalController::ICreateController() +{ + NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + + NxCapsuleControllerDesc desc; + desc.position.x = 0; + desc.position.y = 0; + desc.position.z = 0; + desc.upDirection = NX_Z; + desc.slopeLimit = kSLOPELIMIT; + desc.skinWidth = kPhysxSkinWidth; + desc.stepOffset = STEP_OFFSET; + desc.callback = &gMyReport; + desc.userData = this; + desc.radius = fRadius; + desc.height = fHeight; + desc.interactionFlag = NXIF_INTERACTION_EXCLUDE; + //desc.interactionFlag = NXIF_INTERACTION_INCLUDE; + fController = (NxCapsuleController*)gControllerMgr.createController(scene, desc); + + // Change the avatars shape groups. The avatar doesn't actually use these when + // it's determining collision, but if you're standing still and an object runs + // into you, it'll pass through without this. + NxActor* actor = fController->getActor(); + NxShape* shape = actor->getShapes()[0]; + shape->setGroup(plSimDefs::kGroupAvatar); + + // need to create the non-bouncing object that can be used to trigger things while the avatar is doing behaviors. + NxActorDesc actorDesc; + NxCapsuleShapeDesc capDesc; + capDesc.radius = fRadius; + capDesc.height = fHeight; + capDesc.group = plSimDefs::kGroupAvatar; + actorDesc.shapes.pushBack(&capDesc); + NxBodyDesc bodyDesc; + bodyDesc.mass = 1.f; + actorDesc.body = &bodyDesc; + bodyDesc.flags |= NX_BF_KINEMATIC; + actorDesc.name = "AvatarTriggerKinematicGuy"; + fSeeking=false; + try + { + fKinematicActor = scene->createActor(actorDesc); + } catch (...) + { + hsAssert(false, "Actor creation crashed"); + } +#ifdef PHYSX_KINEMATIC_IS_DISABLED + // initially start as in-active + fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); +#endif + // set the matrix to be the same as the controller's actor... that should orient it to be the same + fKinematicActor->setGlobalPose(actor->getGlobalPose()); + + // the proxy for the debug display + //hsAssert(!fProxyGen, "Already have proxy gen, double read?"); + + hsColorRGBA physColor; + hsScalar opac = 1.0f; + + // local avatar is light purple and transparent + physColor.Set(.2f, .1f, .2f, 1.f); + opac = 0.8f; + + // the avatar proxy doesn't seem to work... not sure why? + fProxyGen = TRACKED_NEW plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); + fProxyGen->Init(this); +} + +void plPXPhysicalController::IDeleteController() +{ + if (fController) + { + gControllerMgr.releaseController(*fController); + fController = nil; + + if (fKinematicActor) + { + NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + scene->releaseActor(*fKinematicActor); + fKinematicActor = nil; + } + plSimulationMgr::GetInstance()->ReleaseScene(fWorldKey); + } +} + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" + +// Make a visible object that can be viewed by users for debugging purposes. +plDrawableSpans* plPXPhysicalController::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + plDrawableSpans* myDraw = addTo; + + hsBool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); + float radius = fRadius; + myDraw = plDrawableGenerator::GenerateSphericalDrawable(fLocalPosition, radius, + mat, fLastGlobalLoc, blended, + nil, &idx, myDraw); + +/* + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + hsBool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); + + myDraw = plDrawableGenerator::GenerateConicalDrawable(fRadius*10, fHeight*10, + mat, so->GetLocalToWorld(), blended, + nil, &idx, myDraw); + } +*/ + return myDraw; +} + +#ifndef PLASMA_EXTERNAL_RELEASE +#include "../plPipeline/plDebugText.h" + +void plPXPhysicalController::IDrawDebugDisplay() +{ + plDebugText &debugTxt = plDebugText::Instance(); + char strBuf[ 2048 ]; + int lineHeight = debugTxt.GetFontSize() + 4; + UInt32 scrnWidth, scrnHeight; + + debugTxt.GetScreenSize( &scrnWidth, &scrnHeight ); + int y = 10; + int x = 10; + + sprintf(strBuf, "Controller Count: %d", gControllers.size()); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + + debugTxt.DrawString(x, y, "Avatar Collisions:"); + y += lineHeight; + + int i; + for (i = 0; i < fDbgCollisionInfo.GetCount(); i++) + { + hsVector3 normal = fDbgCollisionInfo[i].fNormal; + char *overlapStr = fDbgCollisionInfo[i].fOverlap ? "yes" : "no"; + hsScalar angle = hsScalarRadToDeg(hsACosine(normal * hsVector3(0, 0, 1))); + sprintf(strBuf, " Obj: %s, Normal: (%.2f, %.2f, %.2f), Angle(%.1f), Overlap(%3s)", + fDbgCollisionInfo[i].fSO->GetKeyName(), + normal.fX, normal.fY, normal.fZ, angle, overlapStr); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + } +} + +#endif PLASMA_EXTERNAL_RELEASE diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.h new file mode 100644 index 00000000..b85e220e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalController.h @@ -0,0 +1,195 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPXPhysicalController_h_inc +#define plPXPhysicalController_h_inc + +#include "../plAvatar/plAvCallbackAction.h" +#include "hsQuat.h" + +#define PHYSX_ONLY_TRIGGER_FROM_KINEMATIC 1 + +class NxController; +class NxCapsuleController; +class NxActor; +class plCoordinateInterface; +class plPhysicalProxy; +class plDrawableSpans; +class hsGMaterial; +class NxCapsule; +#ifndef PLASMA_EXTERNAL_RELEASE +class plDbgCollisionInfo +{ +public: + plSceneObject *fSO; + hsVector3 fNormal; + hsBool fOverlap; +}; +#endif // PLASMA_EXTERNAL_RELEASE + +class plPXPhysicalController : public plPhysicalController +{ +public: + plPXPhysicalController(plKey ownerSO, hsScalar height, hsScalar radius); + virtual ~plPXPhysicalController(); + + virtual void Enable(bool enable); + virtual bool IsEnabled() const { return fEnable; } + + virtual void SetLOSDB(plSimDefs::plLOSDB losDB) { fLOSDB = losDB; } + plSimDefs::plLOSDB GetLOSDB() const { return fLOSDB; } + + virtual void SetVelocities(const hsVector3& linearVel, hsScalar angVel) + { + fLinearVelocity = linearVel; + fAngularVelocity = angVel; + } + + virtual const hsVector3& GetLinearVelocity() const { return fAchievedLinearVelocity; } + virtual void ResetAchievedLinearVelocity() { fAchievedLinearVelocity.Set(0.f, 0.f, 0.f); } + + virtual plKey GetSubworld() const { return fWorldKey; } + virtual void SetSubworld(plKey world); + + virtual bool IsOnGround() const { return fTimeInAir < kAirTimeThreshold || fFalseGround; } + virtual bool IsOnFalseGround() const { return fFalseGround && !fGroundHit; } + virtual void GroundHit() { fGroundHit = true; } + virtual hsScalar GetAirTime() const { return fTimeInAir; } + virtual void ResetAirTime() { fTimeInAir = 0.f; } + virtual void AddSlidingNormal(hsVector3 vec); + virtual hsTArray* GetSlidingNormals() { return &fSlidingNormals; } + + virtual plPhysical* GetPushingPhysical() const { return fPushingPhysical; } + virtual bool GetFacingPushingPhysical() const { return fFacingPushingPhysical; } + + virtual const plCoordinateInterface* GetSubworldCI() const; + + virtual void GetState(hsPoint3& pos, float& zRot); + virtual void SetState(const hsPoint3& pos, float zRot); + + plKey GetOwner() const { return fOwner; } + + // Called by the simulation mgr each frame + static void Update(bool prestep, hsScalar delSecs); + // Used by the LOS mgr to find the controller for an actor it hit + static plPXPhysicalController* GetController(NxActor& actor, bool* isController); + // test to see if there are any controllers (i.e. avatars) in this subworld + static bool plPXPhysicalController::AnyControllersInThisWorld(plKey world); + static int plPXPhysicalController::NumControllers(); + static int plPXPhysicalController::GetControllersInThisSubWorld(plKey world, int maxToReturn, + plPXPhysicalController** bufferout); + static int plPXPhysicalController::GetNumberOfControllersInThisSubWorld(plKey world); + // Call this if a static physical in the scene has changed (unloaded, + // collision enabled/disabled, etc) + static void RebuildCache(); + + virtual void GetPositionSim(hsPoint3& pos) const { IGetPositionSim(pos); } + + virtual void Kinematic(bool state); + virtual bool IsKinematic(); + virtual void GetKinematicPosition(hsPoint3& pos); + + virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); + + virtual const hsMatrix44& GetPrevSubworldW2L() { return fPrevSubworldW2L; } + + virtual void SetSeek(bool seek){fSeeking=seek;} + virtual void GetWorldSpaceCapsule(NxCapsule& cap); +#ifndef PLASMA_EXTERNAL_RELEASE + static hsBool fDebugDisplay; +#endif // PLASMA_EXTERNAL_RELEASE + +protected: + static const hsScalar kAirTimeThreshold; + + friend class PXControllerHitReport; + static plPXPhysicalController* FindController(NxController* controller); + + void IApply(hsScalar delSecs); + void ISendUpdates(hsScalar delSecs); + void ICheckForFalseGround(); + void ISetGlobalLoc(const hsMatrix44& l2w); + void IMatchKinematicToController(); + void IMoveKinematicToController(hsPoint3& pos); + void ISetKinematicLoc(const hsMatrix44& l2w); + void IGetPositionSim(hsPoint3& pos) const; + + void ICreateController(); + void IDeleteController(); + + void IInformDetectors(bool entering); + + plKey fOwner; + plKey fWorldKey; + hsScalar fRadius, fHeight; + NxCapsuleController* fController; + + // this is the kinematic actor for triggering things when the avatar is collision-less during behaviors + NxActor* fKinematicActor; + + hsVector3 fLinearVelocity; + hsScalar fAngularVelocity; + + hsVector3 fAchievedLinearVelocity; + + // The global position and rotation of the avatar last time we set it (so we + // can detect if someone else moves him) + hsMatrix44 fLastGlobalLoc; + // + hsPoint3 fLocalPosition; + hsQuat fLocalRotation; + + hsMatrix44 fPrevSubworldW2L; + + bool fEnable; + bool fEnableChanged; + plSimDefs::plLOSDB fLOSDB; + + bool fKinematic; + bool fKinematicChanged; + bool fKinematicEnableNextUpdate; + + bool fGroundHit; + bool fFalseGround; + hsScalar fTimeInAir; + hsTArray fSlidingNormals; + hsTArray fPrevSlidingNormals; + +#ifndef PLASMA_EXTERNAL_RELEASE + hsTArray fDbgCollisionInfo; + void IDrawDebugDisplay(); +#endif // PLASMA_EXTERNAL_RELEASE + + plPhysical* fPushingPhysical; + bool fFacingPushingPhysical; + + plPhysicalProxy* fProxyGen; // visual proxy for debugging + + bool fHitHead; + + bool fSeeking; +}; + +#endif // plPXPhysicalController_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp new file mode 100644 index 00000000..4fb59030 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.cpp @@ -0,0 +1,1225 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPXPhysicalControllerCore.h" +#include "plSimulationMgr.h" +#include "plPXPhysical.h" +#include "plPXConvert.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plCorrectionMsg.h" +#include "../plAvatar/plArmatureMod.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../plPhysical/plPhysicalProxy.h" +#include "../pnMessage/plSetNetGroupIDMsg.h" +#include "../plMessage/plCollideMsg.h" +#include "../plModifier/plDetectorLog.h" +//#include "NxVecExtendedVec3.h" + +#include "NxPhysics.h" +#include "ControllerManager.h" +#include "NxCapsuleController.h" +#include "NxCapsuleShape.h" +#define kPhysxSkinWidth 0.1f +#define kPhysZOffset ((fRadius + (fHeight / 2)) + kPhysxSkinWidth) +//#define kSLOPELIMIT (cosf(NxMath::degToRad(55.f))) +//#define kPhysicalHeightFudge 0.4f // this fudge was used for PhysX 2.4 +#define kPhysicalHeightFudge 0.0f + +//#define STEP_OFFSET 1.0f +#define STEP_OFFSET 0.5f +//#define STEP_OFFSET 0.15f + + +#ifndef PLASMA_EXTERNAL_RELEASE +hsBool plPXPhysicalControllerCore::fDebugDisplay = false; +#endif // PLASMA_EXTERNAL_RELEASE +int plPXPhysicalControllerCore::fPXControllersMax = 0; + +static ControllerManager gControllerMgr; +static std::vector gControllers; +static bool gRebuildCache=false; + +#define AvatarMass 200.f + +class PXControllerHitReportWalk : public NxUserControllerHitReport +{ +public: + virtual NxControllerAction onShapeHit(const NxControllerShapeHit& hit) + { + plPXPhysicalControllerCore* ac = plPXPhysicalControllerCore::FindController(hit.controller); + NxActor& actor = hit.shape->getActor(); + plPXPhysical* phys = (plPXPhysical*)actor.userData; + static hsScalar SlopeLimit = kSLOPELIMIT; + hsVector3 normal = plPXConvert::Vector(hit.worldNormal); + ac->fMovementInterface->IAddContactNormals(normal); +#ifndef PLASMA_EXTERNAL_RELEASE + plDbgCollisionInfo info; + info.fNormal = normal; + info.fSO = plSceneObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); + info.fOverlap = false; + NxShape* const *shapes = hit.controller->getActor()->getShapes(); + int numShapes = hit.controller->getActor()->getNbShapes(); + int i; + for (i = 0; i < numShapes; i++) + { + // should only be one capsule shape + const NxCapsuleShape *capShape = shapes[i]->isCapsule(); + if (capShape) + { + NxCapsule cap; + capShape->getWorldCapsule(cap); + if (hit.shape->checkOverlapCapsule(cap)) + info.fOverlap = true; + } + } + ac->fDbgCollisionInfo.Append(info); +#endif PLASMA_EXTERNAL_RELEASE + // If the avatar hit a movable physical, apply some force to it. + hsVector3 dir = plPXConvert::Vector(hit.dir); + float dirdotup=dir.fZ; + hsPoint3 pos((hsScalar)hit.worldPos.x, (hsScalar)hit.worldPos.y, (hsScalar)hit.worldPos.z); + NxExtendedVec3 controllerPos=hit.controller->getPosition(); + hsVector3 bottomOfTheCapsule((hsScalar)controllerPos.x,(hsScalar)controllerPos.y,(hsScalar)controllerPos.z); + bottomOfTheCapsule.fZ=bottomOfTheCapsule.fZ-(ac->fHeight/2.0f + ac->fRadius); + if (actor.isDynamic() ) + { + if((hit.worldPos.z- bottomOfTheCapsule.fZ)<=ac->fRadius)//bottom hemisphere + { + //onTopOfSlopeLimit + if (phys && phys->GetProperty(plSimulationInterface::kPhysAnim)) + { + if(normal.fZ>=0) + {//we consider this ground + ac->fMovementInterface->AddOnTopOfObject(phys); + } + } + } + if ( !actor.readBodyFlag(NX_BF_KINEMATIC) && !actor.readBodyFlag(NX_BF_FROZEN)) + { + // If this is the local avatar, we need to take ownership of this + // dynamic if we haven't already + if (ac->fLOSDB == plSimDefs::kLOSDBLocalAvatar && !phys->IsLocallyOwned() && + !phys->GetProperty(plSimulationInterface::kNoOwnershipChange)) + { + plSynchedObject* obj = plSynchedObject::ConvertNoRef(phys->GetObjectKey()->ObjectIsLoaded()); + obj->SetNetGroupConstant(plNetGroup::kNetGroupLocalPhysicals); + // Tell all the other clients that we own this physical + plSetNetGroupIDMsg* setNetGroupID = TRACKED_NEW plSetNetGroupIDMsg; + setNetGroupID->fId = plNetGroup::kNetGroupRemotePhysicals; + setNetGroupID->SetBCastFlag(plMessage::kNetPropagate | plMessage::kNetForce); + setNetGroupID->SetBCastFlag(plMessage::kLocalPropagate, false); + setNetGroupID->Send(obj->GetKey()); + } + plSimulationMgr::GetInstance()->ConsiderSynch(phys, nil); + // We only allow horizontal pushes. Vertical pushes when we stand on + // dynamic objects creates useless stress on the solver. + + hsVector3 vel=ac->GetLinearVelocity()- plPXConvert::Vector( actor.getLinearVelocity()); + if(dirdotup>=0)vel.fZ=0.001f; + else vel.fZ=0.0f; + static hsScalar kAvieMass = 140.f/32.f; + if (!vel.IsEmpty()) + { + static hsScalar kForceScale = 140.0f; + NxF32 coeff; + NxExtendedVec3 norm2=hit.controller->getPosition(); + norm2.x=hit.worldPos.x-bottomOfTheCapsule.fX; + norm2.y=hit.worldPos.y-bottomOfTheCapsule.fY; + if((hit.worldPos.z- bottomOfTheCapsule.fZ)fRadius)//bottom hemisphere + { + norm2.normalize(); + norm2.z=0.01f; + } + else if((hit.worldPos.z- bottomOfTheCapsule.fZ)<(ac->fRadius+ac->fHeight)) + { + norm2.z=0.0f; + norm2.normalize(); + } + else + {//must be the top so the normal is displacement from the pos - center + //of top hemisphere + norm2.z=hit.worldPos.z - ((ac->fRadius+ac->fHeight + bottomOfTheCapsule.fZ)); + norm2.normalize(); + } + + + float proj=(float)(norm2.x*dir.fX+dir.fY*norm2.y+dir.fZ*norm2.z); + coeff =abs(proj*kForceScale*vel.Magnitude()); + vel.fZ=(hsScalar)norm2.z; + vel.fY=(hsScalar)norm2.y; + vel.fX=(hsScalar)norm2.x; + phys->SetHitForce(vel*coeff, pos); + } + } + } + else // else if the avatar hit a static + { + return NX_ACTION_NONE; + } + if (phys && phys->GetProperty(plSimulationInterface::kAvAnimPushable)) + { + hsQuat inverseRotation = ac->fLocalRotation.Inverse(); + hsVector3 normal = plPXConvert::Vector(hit.worldNormal); + ac->SetPushingPhysical( phys); + ac->SetFacingPushingPhysical((inverseRotation.Rotate(&kAvatarForward).InnerProduct(normal) < 0 ? true : false)); + } + return NX_ACTION_NONE; + } + virtual NxControllerAction onControllerHit(const NxControllersHit& hit) + { + return NX_ACTION_NONE; + } + +} gMyReport; + + +plPhysicalControllerCore* plPhysicalControllerCore::Create(plKey ownerSO, hsScalar height, hsScalar width) +{ + // Test to see how many controller there already is + if ( !plPXPhysicalControllerCore::fPXControllersMax || plPXPhysicalControllerCore::NumControllers() < plPXPhysicalControllerCore::fPXControllersMax ) + { + hsScalar radius = width / 2.f; + hsScalar realHeight = height - width + kPhysicalHeightFudge; + return TRACKED_NEW plPXPhysicalControllerCore(ownerSO, realHeight,radius); + } + return nil; +} + +//Static Helper Func +plPXPhysicalControllerCore* plPXPhysicalControllerCore::FindController(NxController* controller) +{ + for (int i = 0; i < gControllers.size(); i++) + { + plPXPhysicalControllerCore* ac = gControllers[i]; + if (ac->fController == controller) + return ac; + } + return nil; +} +void plPXPhysicalControllerCore::RebuildCache(){gRebuildCache=true;} + +plPXPhysicalControllerCore* plPXPhysicalControllerCore::GetController(NxActor& actor, bool* isController) +{ + *isController = false; + for (int i = 0; i < gControllers.size(); i++) + { + plPXPhysicalControllerCore* ac = gControllers[i]; + if (ac->fController && ac->fController->getActor() == &actor) + { + *isController = true; + return ac; + } + if ( ac->fKinematicActor == &actor) + { + return ac; + } + } + + return nil; +} +void plPXPhysicalControllerCore::GetWorldSpaceCapsule(NxCapsule& cap) const +{ + if(this->fKinematicActor) + { + int numshapes=fKinematicActor->getNbShapes(); + if (numshapes==1) + { + //there should only be one shape on a controller + NxShape* const *shapes=fKinematicActor->getShapes(); + //and since it is a capsule controller it better be a capsule; + NxCapsuleShape *capShape = shapes[0]->isCapsule(); + if(capShape) capShape->getWorldCapsule(cap); + } + } +} +bool plPXPhysicalControllerCore::AnyControllersInThisWorld(plKey world) +{ + for (int i = 0; i < gControllers.size(); i++) + { + plPXPhysicalControllerCore* ac = gControllers[i]; + if (ac->GetSubworld() == world) + return true; + } + return false; +} + +int plPXPhysicalControllerCore::NumControllers() +{ + return gControllers.size(); +} +int plPXPhysicalControllerCore::GetControllersInThisSubWorld(plKey world, int maxToReturn,plPXPhysicalControllerCore** bufferout) +{ + int i=0; + for (int j=0;jGetSubworld()==world) + { + if(iGetSubworld()==world)i++; + } + return i; +} +// +plPXPhysicalControllerCore::plPXPhysicalControllerCore(plKey ownerSO, hsScalar height, hsScalar radius) + : plPhysicalControllerCore(ownerSO,height,radius) + , fController(nil) + , fProxyGen(nil) + , fKinematicActor(nil) + ,fPreferedRadius(radius) + ,fPreferedHeight(height) + , fBehavingLikeAnimatedPhys(true) +{ + fLocalPosition.Set(0, 0, 0); + fLocalRotation.Set(0, 0, 0, 1); + gControllers.push_back(this); + fLastGlobalLoc.Reset(); + ICreateController(); + Enable(false); +} + +void plPXPhysicalControllerCore::ISetGlobalLoc(const hsMatrix44& l2w) +{ + fLastGlobalLoc = l2w; + // Update our subworld position and rotation + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + { + const hsMatrix44& w2s = fPrevSubworldW2L; + hsMatrix44 l2s = w2s * l2w; + + l2s.GetTranslate(&fLocalPosition); + fLocalRotation.SetFromMatrix44(l2s); + } + else + { + l2w.GetTranslate(&fLocalPosition); + fLocalRotation.SetFromMatrix44(l2w); + } + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + if (fProxyGen) + fProxyGen->SetTransform(l2w, w2l); + // Update the physical position + NxExtendedVec3 nxPos(fLocalPosition.fX, fLocalPosition.fY, fLocalPosition.fZ + kPhysZOffset); + fController->setPosition(nxPos); + IMatchKinematicToController(); +} +plPXPhysicalControllerCore::~plPXPhysicalControllerCore() +{ + IDeleteController(); + //need to make sure my queued messages are released + for(int j=0;jgetPosition(); + NxVec3 prevKinPos = fKinematicActor->getGlobalPosition(); + NxVec3 kinPos; + kinPos.x = (NxReal)cPos.x; + kinPos.y = (NxReal)cPos.y; + kinPos.z = (NxReal)cPos.z; + if (plSimulationMgr::fExtraProfile) + SimLog("Match setting kinematic from %f,%f,%f to %f,%f,%f",prevKinPos.x,prevKinPos.y,prevKinPos.z,kinPos.x,kinPos.y,kinPos.z ); + fKinematicActor->setGlobalPosition(kinPos); + } +} +void plPXPhysicalControllerCore::UpdateControllerAndPhysicalRep() +{ + if ( fKinematicActor) + { + if(this->fBehavingLikeAnimatedPhys) + {//this means we are moving the controller and then synchnig the kin + NxExtendedVec3 ControllerPos= fController->getPosition(); + NxVec3 NewKinPos((NxReal)ControllerPos.x, (NxReal)ControllerPos.y, (NxReal)ControllerPos.z); + if (fEnabled || fKinematic) + { + if (plSimulationMgr::fExtraProfile) + SimLog("Moving kinematic to %f,%f,%f",NewKinPos.x, NewKinPos.y, NewKinPos.z ); + // use the position + fKinematicActor->moveGlobalPosition(NewKinPos); + + } + else + { + if (plSimulationMgr::fExtraProfile) + SimLog("Setting kinematic to %f,%f,%f", NewKinPos.x, NewKinPos.y, NewKinPos.z ); + fKinematicActor->setGlobalPosition(NewKinPos); + } + + } + else + { + NxVec3 KinPos= fKinematicActor->getGlobalPosition(); + NxExtendedVec3 NewControllerPos(KinPos.x, KinPos.y, KinPos.z); + if (plSimulationMgr::fExtraProfile) + SimLog("Setting Controller to %f,%f,%f", NewControllerPos.x, NewControllerPos.y, NewControllerPos.z ); + fController->setPosition(NewControllerPos); + } + hsPoint3 curLocalPos; + GetPositionSim(curLocalPos); + fLocalPosition = curLocalPos; + } +} +void plPXPhysicalControllerCore::MoveKinematicToController(hsPoint3& pos) +{ + if ( fKinematicActor) + { + NxVec3 kinPos = fKinematicActor->getGlobalPosition(); + if ( abs(kinPos.x-pos.fX) + abs(kinPos.y-pos.fY) + (abs(kinPos.z-pos.fZ+kPhysZOffset)) > 0.0001f) + { + NxVec3 newPos; + newPos.x = (NxReal)pos.fX; + newPos.y = (NxReal)pos.fY; + newPos.z = (NxReal)pos.fZ+kPhysZOffset; + if ((fEnabled || fKinematic) && fBehavingLikeAnimatedPhys) + { + if (plSimulationMgr::fExtraProfile) + SimLog("Moving kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); + // use the position + fKinematicActor->moveGlobalPosition(newPos); + } + else + { + if (plSimulationMgr::fExtraProfile) + SimLog("Setting kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); + fKinematicActor->setGlobalPosition(newPos); + } + } + } +} + +void plPXPhysicalControllerCore::ISetKinematicLoc(const hsMatrix44& l2w) +{ + hsPoint3 kPos; + // Update our subworld position and rotation + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + { + const hsMatrix44& w2s = subworldCI->GetWorldToLocal(); + hsMatrix44 l2s = w2s * l2w; + + l2s.GetTranslate(&kPos); + } + else + { + l2w.GetTranslate(&kPos); + } + + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + if (fProxyGen) + fProxyGen->SetTransform(l2w, w2l); + + // add z offset + kPos.fZ += kPhysZOffset; + // Update the physical position of kinematic + if (fEnabled|| fKinematic) + fKinematicActor->moveGlobalPosition(plPXConvert::Point(kPos)); + else + fKinematicActor->setGlobalPosition(plPXConvert::Point(kPos)); +} +void plPXPhysicalControllerCore::IGetPositionSim(hsPoint3& pos) const +{ + + if(this->fBehavingLikeAnimatedPhys) + { + const NxExtendedVec3& nxPos = fController->getPosition(); + pos.Set(hsScalar(nxPos.x), hsScalar(nxPos.y), hsScalar(nxPos.z) - kPhysZOffset); + } + else + { + NxVec3 Pos = fKinematicActor->getGlobalPosition(); + pos.Set(hsScalar(Pos.x), hsScalar(Pos.y), hsScalar(Pos.z) - kPhysZOffset); + } +} +void plPXPhysicalControllerCore::ICreateController() +{ +NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + + NxCapsuleControllerDesc desc; + desc.position.x = 0; + desc.position.y = 0; + desc.position.z = 0; + desc.upDirection = NX_Z; + desc.slopeLimit = kSLOPELIMIT; + desc.skinWidth = kPhysxSkinWidth; + desc.stepOffset = STEP_OFFSET; + desc.callback = &gMyReport; + desc.userData = this; + desc.radius = fRadius; + desc.height = fHeight; + desc.interactionFlag = NXIF_INTERACTION_EXCLUDE; + //desc.interactionFlag = NXIF_INTERACTION_INCLUDE; + fController = (NxCapsuleController*)gControllerMgr.createController(scene, desc); + + // Change the avatars shape groups. The avatar doesn't actually use these when + // it's determining collision, but if you're standing still and an object runs + // into you, it'll pass through without this. + NxActor* actor = fController->getActor(); + NxShape* shape = actor->getShapes()[0]; + shape->setGroup(plSimDefs::kGroupAvatar); + + // need to create the non-bouncing object that can be used to trigger things while the avatar is doing behaviors. + NxActorDesc actorDesc; + NxCapsuleShapeDesc capDesc; + capDesc.radius = fRadius; + capDesc.height = fHeight; + capDesc.group = plSimDefs::kGroupAvatar; + capDesc.materialIndex= plSimulationMgr::GetInstance()->GetMaterialIdx(scene, 0.0,0.0); + actorDesc.shapes.pushBack(&capDesc); + NxBodyDesc bodyDesc; + bodyDesc.mass = AvatarMass;//1.f; + actorDesc.body = &bodyDesc; + bodyDesc.flags = NX_BF_KINEMATIC; + bodyDesc.flags |=NX_BF_DISABLE_GRAVITY ; + + actorDesc.name = "AvatarTriggerKinematicGuy"; + fSeeking=false; + try + { + fKinematicActor = scene->createActor(actorDesc); + } catch (...) + { + hsAssert(false, "Actor creation crashed"); + } +#ifdef PHYSX_KINEMATIC_IS_DISABLED + // initially start as in-active + fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); +#endif + // set the matrix to be the same as the controller's actor... that should orient it to be the same + fKinematicActor->setGlobalPose(actor->getGlobalPose()); + + // the proxy for the debug display + //hsAssert(!fProxyGen, "Already have proxy gen, double read?"); + + hsColorRGBA physColor; + hsScalar opac = 1.0f; + + // local avatar is light purple and transparent + physColor.Set(.2f, .1f, .2f, 1.f); + opac = 0.8f; + + /* + // the avatar proxy doesn't seem to work... not sure why? + fProxyGen = TRACKED_NEW plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); + fProxyGen->Init(this); + */ +} +void plPXPhysicalControllerCore::ICreateController(const hsPoint3& pos) +{ + NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + NxCapsuleControllerDesc desc; + desc.position.x = pos.fX; + desc.position.y = pos.fY; + desc.position.z = pos.fZ; + desc.upDirection = NX_Z; + desc.slopeLimit = kSLOPELIMIT; + desc.skinWidth = kPhysxSkinWidth; + desc.stepOffset = STEP_OFFSET; + desc.callback = &gMyReport; + desc.userData = this; + desc.radius = fRadius; + desc.height = fHeight; + desc.interactionFlag = NXIF_INTERACTION_EXCLUDE; + //desc.interactionFlag = NXIF_INTERACTION_INCLUDE; + fController = (NxCapsuleController*)gControllerMgr.createController(scene, desc); + + // Change the avatars shape groups. The avatar doesn't actually use these when + // it's determining collision, but if you're standing still and an object runs + // into you, it'll pass through without this. + NxActor* actor = fController->getActor(); + NxShape* shape = actor->getShapes()[0]; + shape->setGroup(plSimDefs::kGroupAvatar); + + // need to create the non-bouncing object that can be used to trigger things while the avatar is doing behaviors. + NxActorDesc actorDesc; + NxCapsuleShapeDesc capDesc; + capDesc.radius = fRadius; + capDesc.height = fHeight; + capDesc.group = plSimDefs::kGroupAvatar; + actorDesc.shapes.pushBack(&capDesc); + capDesc.materialIndex= plSimulationMgr::GetInstance()->GetMaterialIdx(scene, 0.0,0.0); + actorDesc.globalPose=actor->getGlobalPose(); + NxBodyDesc bodyDesc; + bodyDesc.mass = AvatarMass; + actorDesc.body = &bodyDesc; + bodyDesc.flags = NX_BF_KINEMATIC; + bodyDesc.flags |=NX_BF_DISABLE_GRAVITY ; + actorDesc.name = "AvatarTriggerKinematicGuy"; + fSeeking=false; + try + { + fKinematicActor = scene->createActor(actorDesc); + } + catch (...) + { + hsAssert(false, "Actor creation crashed"); + } + + // set the matrix to be the same as the controller's actor... that should orient it to be the same + //fKinematicActor->setGlobalPose(actor->getGlobalPose()); + + // the proxy for the debug display + //hsAssert(!fProxyGen, "Already have proxy gen, double read?"); + + hsColorRGBA physColor; + hsScalar opac = 1.0f; + + // local avatar is light purple and transparent + physColor.Set(.2f, .1f, .2f, 1.f); + opac = 0.8f; + + /* + // the avatar proxy doesn't seem to work... not sure why? + fProxyGen = TRACKED_NEW plPhysicalProxy(hsColorRGBA().Set(0,0,0,1.f), physColor, opac); + fProxyGen->Init(this); + */ + +} +void plPXPhysicalControllerCore::IDeleteController() +{ + if (fController) + { + gControllerMgr.releaseController(*fController); + fController = nil; + + if (fKinematicActor) + { + NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + scene->releaseActor(*fKinematicActor); + fKinematicActor = nil; + } + plSimulationMgr::GetInstance()->ReleaseScene(fWorldKey); + } +} + +void plPXPhysicalControllerCore::IInformDetectors(bool entering,bool deferUntilNextSim=true) +{ + static const NxU32 DetectorFlag= 1<GetScene(fWorldKey); + int kNumofShapesToStore=30; + NxCapsule cap; + GetWorldSpaceCapsule(cap); + NxShape* shapes[30]; + int numCollided=scene->overlapCapsuleShapes(cap,NX_ALL_SHAPES,kNumofShapesToStore,shapes,NULL,DetectorFlag,NULL,true); + for (int i=0;igetActor()); + + if (myactor) + { + plPXPhysical* physical = (plPXPhysical*)myactor->userData; + if (physical) + { + bool doReport = physical->DoReportOn(plSimDefs::kGroupAvatar); + if(doReport) + { + plCollideMsg* msg = TRACKED_NEW plCollideMsg; + msg->fOtherKey = fOwner; + msg->fEntering = entering; + msg->AddReceiver(physical->GetObjectKey()); + if(!deferUntilNextSim) + { +#ifndef PLASMA_EXTERNAL_RELEASE + DetectorLog("Sending an %s msg to %s" , entering? "entering":"exit", physical->GetObjectKey()->GetName()); +#endif + msg->Send(); + } + else + { +#ifndef PLASMA_EXTERNAL_RELEASE + DetectorLog("Queuing an %s msg to %s, which will be sent after the next simstep" , entering? "entering":"exit", physical->GetObjectKey()->GetName()); +#endif + //these will be fired in update prestep on the next lap + fQueuedCollideMsgs.Append(msg); + } + } + } + } + } +#ifndef PLASMA_EXTERNAL_RELEASE + DetectorLog("Done informing from plPXPhysicalControllerCore::IInformDetectors"); +#endif + } +} +void plPXPhysicalControllerCore::Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults) +{ + collisionResults=0; + if(fController) + { + NxVec3 dis(displacement.fX,displacement.fY,displacement.fZ); + NxU32 colFlags = 0; + this->fController->move(dis,collideWith,.00001,colFlags); + if(colFlags&NXCC_COLLISION_DOWN)collisionResults|=kBottom; + if(colFlags&NXCC_COLLISION_UP)collisionResults|=kTop; + if(colFlags&&NXCC_COLLISION_SIDES)collisionResults|=kSides; + } + return; +} +void plPXPhysicalControllerCore::Enable(bool enable) +{ + if (fEnabled != enable) + { + fEnabled = enable; + if (fEnabled) + fEnableChanged = true; + else + { + // See ISendUpdates for why we don't re-enable right away + fController->setCollision(fEnabled); + } + } +} + +void plPXPhysicalControllerCore::SetSubworld(plKey world) +{ + if (fWorldKey != world) + { + bool wasEnabled = fEnabled; +#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND + // PHYSX FIXME - before leaving this world, sending leaving detector events if we are inside a convex hull detector + hsPoint3 pos; + IGetPositionSim(pos); + plSimulationMgr::GetInstance()->UpdateDetectorsInScene(fWorldKey,GetOwner(),pos,false); +#endif // USE_PHYSX_CONVEXHULL_WORKAROUND + //need to inform detectors in the old world that we are leaving + IInformDetectors(false); + //done informing old world + SimLog("Changing subworlds!"); + IDeleteController(); + SimLog("Deleted old controller"); + fWorldKey = world; + if (GetSubworldCI()) + fPrevSubworldW2L = GetSubworldCI()->GetWorldToLocal(); + // Update our subworld position and rotation + const plCoordinateInterface* subworldCI = GetSubworldCI(); + if (subworldCI) + { + const hsMatrix44& w2s = fPrevSubworldW2L; + hsMatrix44 l2s = w2s * fLastGlobalLoc; + l2s.GetTranslate(&fLocalPosition); + fLocalRotation.SetFromMatrix44(l2s); + } + else + { + fLastGlobalLoc.GetTranslate(&fLocalPosition); + fLocalRotation.SetFromMatrix44(fLastGlobalLoc); + } + hsMatrix44 w2l; + fLastGlobalLoc.GetInverse(&w2l); + if (fProxyGen) + fProxyGen->SetTransform(fLastGlobalLoc, w2l); + // Update the physical position + SimLog("creating new controller"); + hsPoint3 PositionPlusOffset=fLocalPosition; + PositionPlusOffset.fZ +=kPhysZOffset; + //placing new controller and kinematic in the appropriate location + ICreateController(PositionPlusOffset); + RebuildCache(); + } +} +const plCoordinateInterface* plPXPhysicalControllerCore::GetSubworldCI() const +{ + if (fWorldKey) + { + plSceneObject* so = plSceneObject::ConvertNoRef(fWorldKey->ObjectIsLoaded()); + if (so) + return so->GetCoordinateInterface(); + } + return nil; +} +// For the avatar SDL only +void plPXPhysicalControllerCore::GetState(hsPoint3& pos, float& zRot) +{ + // Temporarily use the position point while we get the z rotation + fLocalRotation.NormalizeIfNeeded(); + fLocalRotation.GetAngleAxis(&zRot, (hsVector3*)&pos); + + if (pos.fZ < 0) + zRot = (2 * hsScalarPI) - zRot; // axis is backwards, so reverse the angle too + + pos = fLocalPosition; + +} + +void plPXPhysicalControllerCore::SetState(const hsPoint3& pos, float zRot) +{ + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + hsQuat worldRot; + hsVector3 zAxis(0.f, 0.f, 1.f); + worldRot.SetAngleAxis(zRot, zAxis); + + hsMatrix44 l2w, w2l; + worldRot.MakeMatrix(&l2w); + l2w.SetTranslate(&pos); + + // Localize new position and rotation to global coords if we're in a subworld + const plCoordinateInterface* ci = GetSubworldCI(); + if (ci) + { + const hsMatrix44& subworldL2W = ci->GetLocalToWorld(); + l2w = subworldL2W * l2w; + } + l2w.GetInverse(&w2l); + so->SetTransform(l2w, w2l); + so->FlushTransform(); + } +} +// kinematic stuff .... should be just for when playing a behavior... +void plPXPhysicalControllerCore::Kinematic(bool state) +{ + if (fKinematic != state) + { + fKinematic = state; + if (fKinematic) + { + // See ISendUpdates for why we don't re-enable right away + fController->setCollision(false); +#ifdef PHYSX_KINEMATIC_IS_DISABLED + fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); +#endif + } + else + { + fKinematicChanged = true; + } + } +} +bool plPXPhysicalControllerCore::IsKinematic() +{ + return fKinematic; +} +void plPXPhysicalControllerCore::GetKinematicPosition(hsPoint3& pos) +{ + pos.Set(-1,-1,-1); + if ( fKinematicActor ) + { + NxVec3 klPos = fKinematicActor->getGlobalPosition(); + pos.Set(hsScalar(klPos.x), hsScalar(klPos.y), hsScalar(klPos.z) - kPhysZOffset); + } +} +void plPXPhysicalControllerCore::UpdatePoststep( hsScalar delSecs) +{ + // Apparently the user data field of the controllers is broken +// UInt32 count = gControllerMgr.getNbControllers(); +// NxController* controllers = (NxController*)gControllerMgr.getControllers(); +// +// for (int i = 0; i < count; i++) +// { +// plPXPhysicalController* ac = (plPXPhysicalController*)controllers[i].getAppData(); + for (int i = 0; i < gControllers.size(); i++) + { + plPXPhysicalControllerCore* ac = gControllers[i]; + + hsAssert(ac, "Bad avatar controller"); + + gControllerMgr.updateControllers(); + if(ac->fMovementInterface) + ac->Update(delSecs); + + if (ac->GetSubworldCI()) + ac->fPrevSubworldW2L = ac->GetSubworldCI()->GetWorldToLocal(); + else + { + if (!ac->fPrevSubworldW2L.IsIdentity()) + ac->fPrevSubworldW2L.Reset(); + } + } +} +void plPXPhysicalControllerCore::UpdatePrestep(hsScalar delSecs) +{ + for (int i = 0; i < gControllers.size(); i++) + { + plPXPhysicalControllerCore* ac = gControllers[i]; + + hsAssert(ac, "Bad avatar controller"); + //FIXME +#ifndef PLASMA_EXTERNAL_RELEASE + ac->fDbgCollisionInfo.SetCount(0); +#endif // PLASMA_EXTERNAL_RELEASE + + if (gRebuildCache&&ac->fController) + ac->fController->reportSceneChanged(); + //hsAssert(ac->fMovementInterface,"Updating a controller with out a movement strategy"); + if(ac) + { + if(ac->fNeedsResize)ac->IHandleResize(); + int storedCollideMsgs=ac->fQueuedCollideMsgs.GetCount(); + if(storedCollideMsgs) + { + plSimulationMgr* simMgr=plSimulationMgr::GetInstance(); + for(int j=0; jAddCollisionMsg(ac->fQueuedCollideMsgs[j]); + } + ac->fQueuedCollideMsgs.SetCount(0); + } + ac->Apply(delSecs); + } +#ifndef PLASMA_EXTERNAL_RELEASE + if (fDebugDisplay) + ac->IDrawDebugDisplay(); +#endif // PLASMA_EXTERNAL_RELEASE + } + gRebuildCache = false; +} +void plPXPhysicalControllerCore::UpdatePostSimStep(hsScalar delSecs) +{ + for (int i = 0; i < gControllers.size(); i++) + { + plPXPhysicalControllerCore* ac = gControllers[i]; + hsAssert(ac, "Bad avatar controller"); + ac->PostStep(delSecs); + + } +} +void plPXPhysicalControllerCore::HandleEnableChanged() +{ + fEnableChanged = false; + if(this->fBehavingLikeAnimatedPhys) + { + fController->setCollision(fEnabled); + } + else + { + fController->setCollision(false); + } +#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND + // PHYSX FIXME - after re-enabling check to see if we are inside any convex hull detector regions + hsPoint3 pos; + IGetPositionSim(pos); + plSimulationMgr::GetInstance()->UpdateDetectorsInScene(fWorldKey,GetOwner(),pos,fEnable); +#endif // USE_PHYSX_CONVEXHULL_WORKAROUND + //IInformDetectors(true); +} + +void plPXPhysicalControllerCore::HandleKinematicChanged() +{ + fKinematicChanged = false; + if(this->fBehavingLikeAnimatedPhys) + { + fController->setCollision(true); + } + else + { + fController->setCollision(false); + } +#ifdef PHYSX_KINEMATIC_IS_DISABLED + fKinematicActor->raiseActorFlag(NX_AF_DISABLE_COLLISION); +#endif // PHYSX_KINEMATIC_IS_DISABLED +} +void plPXPhysicalControllerCore::HandleKinematicEnableNextUpdate() +{ + fKinematicActor->clearActorFlag(NX_AF_DISABLE_COLLISION); + fKinematicEnableNextUpdate = false; +} +void plPXPhysicalControllerCore::IHandleResize() +{ + + UInt32 collideFlags = + 1<GetScene(this->fWorldKey); +// NxShape** response=TRACKED_NEW NxShape*[2]; + + NxVec3 center(fLocalPosition.fX,fLocalPosition.fY,fLocalPosition.fZ+fPreferedRadius); + NxSegment Seg(center,center); + const NxCapsule newCap(Seg,fPreferedRadius); + int numintersect =myscene->checkOverlapCapsule(newCap,NX_ALL_SHAPES,collideFlags); + //with new capsule dimensions check for overlap + //with objects we would collide with + + if(numintersect==0) + { + fHeight=fPreferedHeight; + fRadius=fPreferedRadius; + fController->setRadius(fRadius); + fController->setHeight(fHeight); + + fNeedsResize=false; + } + +// delete[] response; +} +void plPXPhysicalControllerCore::SetControllerDimensions(hsScalar radius, hsScalar height) +{ + fNeedsResize=false; + if(fRadius!=radius) + { + fNeedsResize=true; + } + if(fHeight!=height) + { + fNeedsResize=true; + } + fPreferedRadius=radius; + fPreferedHeight=height; +} + +void plPXPhysicalControllerCore::LeaveAge() +{ + SetPushingPhysical(nil); + if(fWorldKey) this->SetSubworld(nil); + this->fMovementInterface->LeaveAge(); +} +int plPXPhysicalControllerCore::SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, hsBool vsDynamics, hsBool vsStatics, + UInt32& vsSimGroups, std::multiset< plControllerSweepRecord >& WhatWasHitOut) +{ + NxCapsule tempCap; + tempCap.p0 =plPXConvert::Point( startPos); + tempCap.p0.z = tempCap.p0.z + fPreferedRadius; + tempCap.radius = fPreferedRadius ; + tempCap.p1 = tempCap.p0; + tempCap.p1.z = tempCap.p1.z + fPreferedHeight; + + NxVec3 vec; + vec.x = endPos.fX - startPos.fX; + vec.y = endPos.fY - startPos.fY; + vec.z = endPos.fZ - startPos.fZ; + + int numberofHits = 0; + int HitsReturned = 0; + WhatWasHitOut.clear(); + NxScene *myscene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); + NxSweepQueryHit whatdidIhit[10]; + unsigned int flags = NX_SF_ALL_HITS; + if(vsDynamics) + flags |= NX_SF_DYNAMICS; + if(vsStatics) + flags |= NX_SF_STATICS; + numberofHits = myscene->linearCapsuleSweep(tempCap, vec, flags, nil, 10, whatdidIhit, nil, vsSimGroups); + if(numberofHits) + {//we hit a dynamic object lets make sure it is not animatable + for(int i=0; igetActor().userData; + CurrentHit.Norm.fX = whatdidIhit[i].normal.x; + CurrentHit.Norm.fY = whatdidIhit[i].normal.y; + CurrentHit.Norm.fZ = whatdidIhit[i].normal.z; + if(CurrentHit.ObjHit != nil) + { + hsPoint3 where; + where.fX = whatdidIhit[i].point.x; + where.fY = whatdidIhit[i].point.y; + where.fZ = whatdidIhit[i].point.z; + CurrentHit.locHit = where; + CurrentHit.TimeHit = whatdidIhit[i].t ; + WhatWasHitOut.insert(CurrentHit); + HitsReturned++; + } + } + } + + return HitsReturned; +} +void plPXPhysicalControllerCore::BehaveLikeAnimatedPhysical(hsBool actLikeAnAnimatedPhys) +{ + hsAssert(fKinematicActor, "Changing behavior, but plPXPhysicalControllerCore has no Kinematic actor associated with it"); + if(fBehavingLikeAnimatedPhys!=actLikeAnAnimatedPhys) + { + fBehavingLikeAnimatedPhys=actLikeAnAnimatedPhys; + if(fKinematicActor) + { + if(actLikeAnAnimatedPhys) + { + //need to set BX Kinematic if true and kill any rotation + fController->setCollision(fEnabled); + fKinematicActor->raiseBodyFlag(NX_BF_KINEMATIC); + fKinematicActor->clearBodyFlag(NX_BF_FROZEN_ROT); + fKinematicActor->raiseBodyFlag(NX_BF_DISABLE_GRAVITY); + } + else + { + //don't really use the controller now don't bother with collisions + fController->setCollision(false); + fKinematicActor->clearBodyFlag(NX_BF_KINEMATIC); + fKinematicActor->raiseBodyFlag(NX_BF_FROZEN_ROT_X); + fKinematicActor->raiseBodyFlag(NX_BF_FROZEN_ROT_Y); + fKinematicActor->clearBodyFlag(NX_BF_DISABLE_GRAVITY); + + + } + } + } +} + +hsBool plPXPhysicalControllerCore::BehavingLikeAnAnimatedPhysical() +{ + hsAssert(fKinematicActor, "plPXPhysicalControllerCore is missing a kinematic actor"); + return fBehavingLikeAnimatedPhys; +} + +void plPXPhysicalControllerCore::SetLinearVelocity(const hsVector3& linearVel) +{ + plPhysicalControllerCore::SetLinearVelocity(linearVel); + if(fKinematicActor && !fBehavingLikeAnimatedPhys) + { + NxVec3 vel= plPXConvert::Vector(linearVel); + fKinematicActor->setLinearVelocity(vel); + } +} +void plPXPhysicalControllerCore::SetAngularVelocity(const hsScalar angvel) +{ + plPhysicalControllerCore::SetAngularVelocity(angvel); + if(fKinematicActor && !fBehavingLikeAnimatedPhys) + { + NxVec3 vel(0.0f, 0.0f, angvel); + fKinematicActor->setAngularVelocity(vel); + } +} +void plPXPhysicalControllerCore::SetVelocities(const hsVector3& linearVel, hsScalar angVel) +{ + SetLinearVelocity(linearVel); + SetAngularVelocity(angVel); +} + +void plPXPhysicalControllerCore::IMatchControllerToKinematic() +{ + NxExtendedVec3 newpos; + NxVec3 pos=fKinematicActor->getGlobalPosition(); + newpos.x=pos.x; + newpos.y=pos.y; + newpos.z=pos.z; + fController->setPosition(newpos); +} +const hsVector3& plPXPhysicalControllerCore::GetLinearVelocity() +{ + if(BehavingLikeAnAnimatedPhysical()) + return fLinearVelocity; + else + { + fLinearVelocity = plPXConvert::Vector(fKinematicActor->getLinearVelocity()); + return fLinearVelocity; + } +} + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" + +// Make a visible object that can be viewed by users for debugging purposes. +plDrawableSpans* plPXPhysicalControllerCore::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + plDrawableSpans* myDraw = addTo; + hsBool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); + float radius = fRadius; + myDraw = plDrawableGenerator::GenerateSphericalDrawable(fLocalPosition, radius, + mat, fLastGlobalLoc, blended, + nil, &idx, myDraw); + +/* + plSceneObject* so = plSceneObject::ConvertNoRef(fOwner->ObjectIsLoaded()); + if (so) + { + hsBool blended = ((mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendMask)); + + myDraw = plDrawableGenerator::GenerateConicalDrawable(fRadius*10, fHeight*10, + mat, so->GetLocalToWorld(), blended, + nil, &idx, myDraw); + } +*/ + return myDraw; +} +#ifndef PLASMA_EXTERNAL_RELEASE +#include "../plPipeline/plDebugText.h" + +void plPXPhysicalControllerCore::IDrawDebugDisplay() +{ + plDebugText &debugTxt = plDebugText::Instance(); + char strBuf[ 2048 ]; + int lineHeight = debugTxt.GetFontSize() + 4; + UInt32 scrnWidth, scrnHeight; + + debugTxt.GetScreenSize( &scrnWidth, &scrnHeight ); + int y = 10; + int x = 10; + + sprintf(strBuf, "Controller Count: %d", gControllers.size()); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + + debugTxt.DrawString(x, y, "Avatar Collisions:"); + y += lineHeight; + + int i; + for (i = 0; i < fDbgCollisionInfo.GetCount(); i++) + { + hsVector3 normal = fDbgCollisionInfo[i].fNormal; + char *overlapStr = fDbgCollisionInfo[i].fOverlap ? "yes" : "no"; + hsScalar angle = hsScalarRadToDeg(hsACosine(normal * hsVector3(0, 0, 1))); + sprintf(strBuf, " Obj: %s, Normal: (%.2f, %.2f, %.2f), Angle(%.1f), Overlap(%3s)", + fDbgCollisionInfo[i].fSO->GetKeyName(), + normal.fX, normal.fY, normal.fZ, angle, overlapStr); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + } +} +#endif PLASMA_EXTERNAL_RELEASE diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h new file mode 100644 index 00000000..d821120b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXPhysicalControllerCore.h @@ -0,0 +1,145 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../plAvatar/plPhysicalControllerCore.h" +#include "hsQuat.h" +#define PHYSX_ONLY_TRIGGER_FROM_KINEMATIC 1 + +class NxController; +class NxCapsuleController; +class NxActor; +class plCoordinateInterface; +class plPhysicalProxy; +class plDrawableSpans; +class hsGMaterial; +class NxCapsule; +class plSceneObject; +class PXControllerHitReportWalk; +class plCollideMsg; +#ifndef PLASMA_EXTERNAL_RELEASE + +class plDbgCollisionInfo +{ +public: + plSceneObject *fSO; + hsVector3 fNormal; + hsBool fOverlap; +}; +#endif // PLASMA_EXTERNAL_RELEASE +class plPXPhysicalControllerCore: public plPhysicalControllerCore +{ + friend PXControllerHitReportWalk; +public: + plPXPhysicalControllerCore(plKey ownerSO, hsScalar height, hsScalar radius); + ~plPXPhysicalControllerCore(); + //should actually be a 3 vector but everywhere else it is assumed to be just around Z + + inline virtual void Move(hsVector3 displacement, unsigned int collideWith, unsigned int &collisionResults); + // A disabled avatar doesn't move or accumulate air time if he's off the ground. + virtual void Enable(bool enable); + + virtual void SetSubworld(plKey world) ; + virtual const plCoordinateInterface* GetSubworldCI() const ; + // For the avatar SDL only + virtual void GetState(hsPoint3& pos, float& zRot); + virtual void SetState(const hsPoint3& pos, float zRot); + // kinematic stuff .... should be just for when playing a behavior... + virtual void Kinematic(bool state); + virtual bool IsKinematic(); + virtual void GetKinematicPosition(hsPoint3& pos); + virtual const hsMatrix44& GetPrevSubworldW2L(){ return fPrevSubworldW2L; } + //when seeking no longer want to interact with exclusion regions + virtual void GetWorldSpaceCapsule(NxCapsule& cap) const; + static void RebuildCache(); + virtual const hsMatrix44& GetLastGlobalLoc(){return fLastGlobalLoc;} + virtual void SetKinematicLoc(const hsMatrix44& l2w){ISetKinematicLoc(l2w);} + virtual void SetGlobalLoc(const hsMatrix44& l2w){ISetGlobalLoc(l2w);} + virtual void HandleEnableChanged(); + virtual void HandleKinematicChanged(); + virtual void HandleKinematicEnableNextUpdate(); + virtual void GetPositionSim(hsPoint3& pos){IGetPositionSim(pos);} + virtual void MoveKinematicToController(hsPoint3& pos); + virtual const hsPoint3& GetLocalPosition(){return fLocalPosition;} + virtual void SetControllerDimensions(hsScalar radius, hsScalar height); + virtual void LeaveAge(); + virtual void UpdateControllerAndPhysicalRep(); + +////////////////////////////////////////// +//Static Helper Functions +//////////////////////////////////////// + // Used by the LOS mgr to find the controller for an actor it hit + static plPXPhysicalControllerCore* GetController(NxActor& actor, bool* isController); + // test to see if there are any controllers (i.e. avatars) in this subworld + static bool AnyControllersInThisWorld(plKey world); + static int NumControllers(); + static int GetControllersInThisSubWorld(plKey world, int maxToReturn, + plPXPhysicalControllerCore** bufferout); + static int GetNumberOfControllersInThisSubWorld(plKey world); + static void UpdatePrestep(hsScalar delSecs); + static void UpdatePoststep(hsScalar delSecs); + static void UpdatePostSimStep(hsScalar delSecs); + virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); +#ifndef PLASMA_EXTERNAL_RELEASE + static hsBool fDebugDisplay; +#endif // PLASMA_EXTERNAL_RELEASE + static void plPXPhysicalControllerCore::SetMaxNumberOfControllers(int max) { fPXControllersMax = max; } + static int fPXControllersMax; + virtual int SweepControllerPath(const hsPoint3& startPos, const hsPoint3& endPos, hsBool vsDynamics, hsBool vsStatics, UInt32& vsSimGroups, std::multiset< plControllerSweepRecord >& WhatWasHitOut); + virtual void BehaveLikeAnimatedPhysical(hsBool actLikeAnAnimatedPhys); + virtual hsBool BehavingLikeAnAnimatedPhysical(); + virtual const hsVector3& GetLinearVelocity(); + + virtual void SetLinearVelocity(const hsVector3& linearVel); + //should actually be a 3 vector but everywhere else it is assumed to be just around Z + virtual void SetAngularVelocity(const hsScalar angvel); + virtual void SetVelocities(const hsVector3& linearVel, hsScalar angVel); + +protected: + friend class PXControllerHitReport; + static plPXPhysicalControllerCore* FindController(NxController* controller); + void ISetGlobalLoc(const hsMatrix44& l2w); + void IMatchKinematicToController(); + void IMatchControllerToKinematic(); + void ISetKinematicLoc(const hsMatrix44& l2w); + void IGetPositionSim(hsPoint3& pos) const; + void ICreateController(); + void IDeleteController(); + void IInformDetectors(bool entering,bool deferUntilNextSim); + void plPXPhysicalControllerCore::ICreateController(const hsPoint3& pos); + NxActor* fKinematicActor; + NxCapsuleController* fController; +#ifndef PLASMA_EXTERNAL_RELEASE + hsTArray fDbgCollisionInfo; + void IDrawDebugDisplay(); +#endif + void plPXPhysicalControllerCore::IHandleResize(); + hsTArray fQueuedCollideMsgs; + hsScalar fPreferedRadius; + hsScalar fPreferedHeight; + // The global position and rotation of the avatar last time we set it (so we + // can detect if someone else moves him) + plPhysicalProxy* fProxyGen; + hsBool fBehavingLikeAnimatedPhys; +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXStream.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXStream.h new file mode 100644 index 00000000..4a16fd13 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPXStream.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPXStream_h_inc +#define plPXStream_h_inc + +#include "hsStream.h" +#include "NxPhysics.h" +#include "NxStream.h" + +// A super simple wrapper to convert a Plasma stream into a PhysX one +class plPXStream : public NxStream +{ +public: + plPXStream(hsStream* s) : fStream(s) {} + + virtual NxU8 readByte() const { return fStream->ReadByte(); } + virtual NxU16 readWord() const { return fStream->ReadSwap16(); } + virtual NxU32 readDword() const { return fStream->ReadSwap32(); } + virtual float readFloat() const { return fStream->ReadSwapScalar(); } + virtual double readDouble() const { return fStream->ReadSwapDouble(); } + virtual void readBuffer(void* buffer, NxU32 size) const { fStream->Read(size, buffer); } + + virtual NxStream& storeByte(NxU8 b) { fStream->WriteByte(b); return *this; } + virtual NxStream& storeWord(NxU16 w) { fStream->WriteSwap16(w); return *this; } + virtual NxStream& storeDword(NxU32 d) { fStream->WriteSwap32(d); return *this; } + virtual NxStream& storeFloat(NxReal f) { fStream->WriteSwapScalar(f); return *this; } + virtual NxStream& storeDouble(NxF64 f) { fStream->WriteSwapDouble(f); return *this; } + virtual NxStream& storeBuffer(const void* buffer, NxU32 size) { fStream->Write(size, buffer); return *this; } + +protected: + hsStream* fStream; +}; + +#endif // plPXStream_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h new file mode 100644 index 00000000..98dc35d7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plPhysXCreatable.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPhysXCreatable_inc +#define plPhysXCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plPXPhysical.h" + +REGISTER_CREATABLE(plPXPhysical); + +//#include "plHKSimulationSynchMsg.h" +//REGISTER_CREATABLE(plHKSimulationSynchMsg); + +//#include "plHavokConstraintTools.h" +//REGISTER_NONCREATABLE(plHavokConstraintsMod); +//REGISTER_CREATABLE(plHingeConstraintMod); +//REGISTER_CREATABLE(plStrongSpringConstraintMod); +//REGISTER_CREATABLE(plWheelConstraintMod); + + +#include "plLOSDispatch.h" +REGISTER_CREATABLE( plLOSDispatch ); + +#include "plSimulationMgr.h" +REGISTER_CREATABLE( plSimulationMgr ); + +//#include "plVehicleModifier.h" +//REGISTER_CREATABLE(plVehicleModifier); + + +#endif // plPhysXCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp new file mode 100644 index 00000000..64e35a6c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.cpp @@ -0,0 +1,1029 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSimulationMgr.h" + +#include "NxPhysics.h" + +#include "hsTimer.h" +#include "plProfile.h" +#include "plPXPhysical.h" +#include "plPXPhysicalControllerCore.h" +#include "plPXConvert.h" +#include "plLOSDispatch.h" +#include "../plPhysical/plPhysicsSoundMgr.h" +#include "../plStatusLog/plStatusLog.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnNetCommon/plSDLTypes.h" +#include "../plMessage/plCollideMsg.h" + +#include "../plModifier/plDetectorLog.h" + +#ifndef PLASMA_EXTERNAL_RELEASE +bool plSimulationMgr::fDisplayAwakeActors=false; +#endif //PLASMA_EXTERNAL_RELEASE +// This gets called by PhysX whenever a trigger gets penetrated. This is used +// for any Plasma detectors. +class SensorReport : public NxUserTriggerReport +{ + virtual void onTrigger(NxShape& triggerShape, NxShape& otherShape, NxTriggerFlag status) + { + // Get our trigger physical. This should definitely have a plPXPhysical + plPXPhysical* triggerPhys = (plPXPhysical*)triggerShape.getActor().userData; + hsBool doReport = false; + + // Get the triggerer. This may be an avatar, which doesn't have a + // plPXPhysical, so we have to extract the necessary info. + plKey otherKey = nil; + hsPoint3 otherPos = plPXConvert::Point(otherShape.getGlobalPosition()); + + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("-->%s %s (status=%x) other@(%f,%f,%f)",triggerPhys->GetObjectKey()->GetName(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit",status,otherPos.fX,otherPos.fY,otherPos.fZ); + + plPXPhysical* otherPhys = (plPXPhysical*)otherShape.getActor().userData; + if (otherPhys) + { + otherKey = otherPhys->GetObjectKey(); + doReport = triggerPhys->DoReportOn((plSimDefs::Group)otherPhys->GetGroup()); + if (!doReport) + { + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("<--Kill collision %s :failed group. US=%x OTHER=(%s)%x",triggerPhys->GetObjectKey()->GetName(),triggerPhys->GetGroup(),otherPhys->GetObjectKey()->GetName(),otherPhys->GetGroup()); + } + } + else + { + bool isController; + plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(otherShape.getActor(),&isController); + if (controller) + { + if (isController) + { +#ifdef PHYSX_ONLY_TRIGGER_FROM_KINEMATIC + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("<--Kill collision %s : ignoring controller events.",triggerPhys->GetObjectKey()->GetName()); + return; +#else // else if trigger on both controller and kinematic + // only suppress controller collision 'enters' when disabled but let 'exits' continue + // ...this is because there are detector regions that are on the edge on ladders that the exit gets missed. + if ( ( !controller->IsEnabled() /*&& (status & NX_TRIGGER_ON_ENTER)*/ ) || controller->IsKinematic() ) + { + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("<--Kill collision %s : controller is not enabled.",triggerPhys->GetObjectKey()->GetName()); + return; + } +#endif // PHYSX_ONLY_TRIGGER_FROM_KINEMATIC + } +#ifndef PHYSX_ONLY_TRIGGER_FROM_KINEMATIC // if triggering only kinematics, then all should trigger + else + { + // only suppress kinematic collision 'enters' when disabled but let 'exits' continue + // ...this is because there are detector regions that are on the edge on ladders that the exit gets missed. + if ( !controller->IsKinematic() /*&& (status & NX_TRIGGER_ON_ENTER) */ ) + { + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("<--Kill collision %s : kinematic is not enabled.",triggerPhys->GetObjectKey()->GetName()); + return; + } + } +#endif // PHYSX_ONLY_TRIGGER_FROM_KINEMATIC + otherKey = controller->GetOwner(); + doReport = triggerPhys->DoReportOn(plSimDefs::kGroupAvatar); + if (plSimulationMgr::fExtraProfile ) + { + if (!doReport) + { + DetectorLogRed("<--Kill collision %s :failed group. US=%x OTHER=(NotAvatar)",triggerPhys->GetObjectKey()->GetName(),triggerPhys->GetGroup()); + } + else + { + hsPoint3 avpos; + controller->GetPositionSim(avpos); + DetectorLogRed("-->Avatar at (%f,%f,%f)",avpos.fX,avpos.fY,avpos.fZ); + } + } + } + } + + if (doReport) + { +#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND + if ( triggerPhys->DoDetectorHullWorkaround() ) + { + if (status & NX_TRIGGER_ON_ENTER && triggerPhys->Should_I_Trigger(status & NX_TRIGGER_ON_ENTER, otherPos) ) + { + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("-->Send Collision (CH) %s %s",triggerPhys->GetObjectKey()->GetName(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); + SendCollisionMsg(triggerPhys->GetObjectKey(), otherKey, true); + } + else if (status & NX_TRIGGER_ON_ENTER) + { + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("<--Kill collision %s :failed Should I trigger",triggerPhys->GetObjectKey()->GetName()); + } + if (status & NX_TRIGGER_ON_LEAVE && triggerPhys->Should_I_Trigger(status & NX_TRIGGER_ON_ENTER, otherPos) ) + { + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("-->Send Collision (CH) %s %s",triggerPhys->GetObjectKey()->GetName(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); + SendCollisionMsg(triggerPhys->GetObjectKey(), otherKey, false); + } + else if (status & NX_TRIGGER_ON_LEAVE) + { + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("<--Kill collision %s :failed Should I trigger",triggerPhys->GetObjectKey()->GetName()); + } + if (!(status & NX_TRIGGER_ON_ENTER) && !(status & NX_TRIGGER_ON_LEAVE) ) + { + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("<--Kill collision %s :failed event(CH)",triggerPhys->GetObjectKey()->GetName()); + } + } + else + { +#endif // USE_PHYSX_CONVEXHULL_WORKAROUND + if (status & NX_TRIGGER_ON_ENTER) + { + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("-->Send Collision %s %s",triggerPhys->GetObjectKey()->GetName(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); + SendCollisionMsg(triggerPhys->GetObjectKey(), otherKey, true); + } + if (status & NX_TRIGGER_ON_LEAVE) + { + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("-->Send Collision %s %s",triggerPhys->GetObjectKey()->GetName(),status & NX_TRIGGER_ON_ENTER ? "enter" : "exit"); + SendCollisionMsg(triggerPhys->GetObjectKey(), otherKey, false); + } + if (!(status & NX_TRIGGER_ON_ENTER) && !(status & NX_TRIGGER_ON_LEAVE) ) + { + if (plSimulationMgr::fExtraProfile) + DetectorLogRed("<--Kill collision %s :failed event",triggerPhys->GetObjectKey()->GetName()); + } +#ifdef USE_PHYSX_CONVEXHULL_WORKAROUND + } +#endif // USE_PHYSX_CONVEXHULL_WORKAROUND + } + } + + void SendCollisionMsg(plKey receiver, plKey hitter, hsBool entering) + { + plCollideMsg* msg = TRACKED_NEW plCollideMsg; + msg->fOtherKey = hitter; + msg->fEntering = entering; + msg->AddReceiver(receiver); +// msg->Send();\ + //placing message in a list to be fired off after sim step + plSimulationMgr::GetInstance()->AddCollisionMsg(msg); + } + +} gSensorReport; + +// This gets called by PhysX whenever two actor groups that are set to report +// have a collision. We enable this for when a dynamic collides with anything. +class ContactReport : public NxUserContactReport +{ + virtual void onContactNotify(NxContactPair& pair, NxU32 events) + { + plPXPhysical* phys1 = (plPXPhysical*)pair.actors[0]->userData; + plPXPhysical* phys2 = (plPXPhysical*)pair.actors[1]->userData; + + // Normally, these are always valid because the avatar (who doesn't have + // a physical) will push other physicals away before they actually touch + // his actor. However, if the avatar is warped to a new position he may + // collide with the object for a few frames. We just ignore it. + if (!phys1 || !phys2) + return; + + plSimulationMgr::GetInstance()->ConsiderSynch(phys1, phys2); + + if (phys1->GetSoundGroup() && phys2->GetSoundGroup()) + { + hsPoint3 contactPoint(0, 0, 0); + + // Just grab the last contact point + NxContactStreamIterator i(pair.stream); + while (i.goNextPair()) + { + while (i.goNextPatch()) + { + const NxVec3& contactNormal = i.getPatchNormal(); + while (i.goNextPoint()) + { + contactPoint = plPXConvert::Point(i.getPoint()); + } + } + } + + plSimulationMgr::GetInstance()->fSoundMgr->AddContact( + phys1, phys2, contactPoint, + plPXConvert::Vector(pair.sumNormalForce)); + } + } +} gContactReport; + +// This directs any errors or warnings from PhysX to the simulation log. +class ErrorStream : public NxUserOutputStream +{ + virtual void reportError(NxErrorCode e, const char* message, const char* file, int line) + { + const char* errorType = nil; + switch (e) + { + case NXE_INVALID_PARAMETER: errorType = "invalid parameter"; break; + case NXE_INVALID_OPERATION: errorType = "invalid operation"; break; + case NXE_OUT_OF_MEMORY: errorType = "out of memory"; break; + case NXE_DB_INFO: errorType = "info"; break; + case NXE_DB_WARNING: errorType = "warning"; break; + default: errorType = "unknown error"; + } + + plSimulationMgr::Log("%s(%d) : %s: %s", file, line, errorType, message); + } + + virtual NxAssertResponse reportAssertViolation(const char* message, const char* file, int line) + { + plSimulationMgr::Log("access violation : %s (%s(%d))", message, file, line); + hsAssert(0, "PhysX assert, see simulation log for details"); + return NX_AR_CONTINUE; + } + + virtual void print(const char* message) + { + plSimulationMgr::Log(message); + } +} gErrorStream; + +// This class allows PhysX to use our heap manager +static class HeapAllocator : public NxUserAllocator { +public: + void * malloc (NxU32 size) { + return ALLOC(size); + } + void * mallocDEBUG (NxU32 size, const char * fileName, int line) { + return MemAlloc(size, 0, fileName, line); + } + void * realloc (void * memory, NxU32 size) { + return REALLOC(memory, size); + } + void free (void * memory) { + FREE(memory); + } +} gHeapAllocator; + + +///////////////////////////////////////////////////////////////// +// +// DEFAULTS +// +///////////////////////////////////////////////////////////////// + +#define kDefaultMaxDelta 0.1 // if the step is greater than .1 seconds, clamp to that +#define kDefaultStepSize 1.f / 60.f // default simulation freqency is 60hz + +///////////////////////////////////////////////////////////////// +// +// CONSTRUCTION, INITIALIZATION, DESTRUCTION +// +///////////////////////////////////////////////////////////////// + +// +// Alloc all the sim timers here so they make a nice pretty display +// +//plProfile_CreateTimer( "ClearContacts", "Simulation", ClearContacts); +plProfile_CreateTimer( "Step", "Simulation", Step); +// plProfile_CreateTimer( " Broadphase", "Simulation", Broadphase); +plProfile_CreateCounter(" Awake", "Simulation", Awake); +plProfile_CreateCounter(" Contacts", "Simulation", Contacts); +plProfile_CreateCounter(" DynActors", "Simulation", DynActors); +plProfile_CreateCounter(" DynShapes", "Simulation", DynShapes); +plProfile_CreateCounter(" StaticShapes", "Simulation", StaticShapes); +plProfile_CreateCounter(" Actors", "Simulation", Actors); +plProfile_CreateCounter(" PhyScenes", "Simulation", Scenes); + +// plProfile_CreateCounter(" Broadphase Rejected", "Simulation", BroadphaseReject); +// plProfile_CreateCounter(" Broadphase Accepted", "Simulation", BroadphaseAccept); +// plProfile_CreateCounter(" Impact", "Simulation", Impact); +// plProfile_CreateCounter(" Penetration", "Simulation", Penetration); +// plProfile_CreateTimer( "Narrowphase", "Simulation", Narrowphase); +// plProfile_CreateTimer( "ProcessInterpenetration", "Simulation", ProcessInterpenetration); +plProfile_CreateTimer( "LineOfSight", "Simulation", LineOfSight); +plProfile_CreateTimer( "ProcessSyncs", "Simulation", ProcessSyncs); +plProfile_CreateTimer( "UpdateContexts", "Simulation", UpdateContexts); +// plProfile_CreateCounter(" ContextUpdates", "Simulation", ContextUpdates); +plProfile_CreateCounter(" MaySendLocation", "Simulation", MaySendLocation); +plProfile_CreateCounter(" LocationsSent", "Simulation", LocationsSent); +plProfile_CreateTimer( " PhysicsUpdates","Simulation",PhysicsUpdates); +// plProfile_CreateTimer( "EntityCleanup", "Simulation", EntityCleanup); +plProfile_CreateCounter("SetTransforms Accepted", "Simulation", SetTransforms); +plProfile_CreateCounter("AnimatedPhysicals", "Simulation", AnimatedPhysicals); +plProfile_CreateCounter("AnimatedActivators", "Simulation", AnimatedActivators); +plProfile_CreateCounter("Controllers", "Simulation", Controllers); +// plProfile_CreateCounter("NumSteps", "Simulation", NumSteps); +plProfile_CreateCounter("StepLength", "Simulation", StepLen); + +// declared at file scope so that both GetInstance and the destructor can access it. +static plSimulationMgr* gTheInstance = NULL; +bool plSimulationMgr::fExtraProfile = false; +bool plSimulationMgr::fSubworldOptimization = false; +bool plSimulationMgr::fDoClampingOnStep=true; + +void plSimulationMgr::Init() +{ + hsAssert(!gTheInstance, "Initializing the sim when it's already been done"); + gTheInstance = TRACKED_NEW plSimulationMgr(); + if (gTheInstance->InitSimulation()) + { + gTheInstance->RegisterAs(kSimulationMgr_KEY); + gTheInstance->GetKey()->RefObject(); + } + else + { + // There was an error when creating the PhysX simulation + // ...then get rid of the simulation instance + DEL(gTheInstance); // clean up the memory we allocated + gTheInstance = nil; + } +} + +// when the app is going away completely +void plSimulationMgr::Shutdown() +{ + hsAssert(gTheInstance, "Simulation manager missing during shutdown."); + if (gTheInstance) + { + // UnRef to match our Ref in Init(). Unless something strange is + // going on, this should destroy the instance and set gTheInstance to nil. +// gTheInstance->GetKey()->UnRefObject(); + gTheInstance->UnRegister(); // this will destroy the instance + gTheInstance = nil; + } +} + +plSimulationMgr* plSimulationMgr::GetInstance() +{ + return gTheInstance; +} + +////////////////////////////////////////////////////////////////////////// + +plSimulationMgr::plSimulationMgr() + : fSuspended(true) + , fMaxDelta(kDefaultMaxDelta) + , fStepSize(kDefaultStepSize) + , fLOSDispatch(TRACKED_NEW plLOSDispatch()) + , fSoundMgr(new plPhysicsSoundMgr) + , fLog(nil) +{ + +} + +bool plSimulationMgr::InitSimulation() +{ + fSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, &gHeapAllocator, &gErrorStream); + if (!fSDK) + return false; // client will handle this and ask user to install + + fLog = plStatusLogMgr::GetInstance().CreateStatusLog(40, "Simulation.log", plStatusLog::kFilledBackground | plStatusLog::kAlignToTop); + fLog->AddLine("Initialized simulation mgr"); + +#ifndef PLASMA_EXTERNAL_RELEASE + // If this is an internal build, enable the PhysX debugger + fSDK->getFoundationSDK().getRemoteDebugger()->connect("localhost", 5425); +#endif + + if ( !plPXConvert::Validate() ) + { +#ifndef PLASMA_EXTERNAL_RELEASE + hsMessageBox("Ageia's PhysX or Plasma offsets have changed, need to rewrite conversion code.","PhysX Error",MB_OK); +#endif + return false; // client will handle this and ask user to install + } + + return true; +} + +plSimulationMgr::~plSimulationMgr() +{ + fLOSDispatch->UnRef(); + fLOSDispatch = nil; + + delete fSoundMgr; + fSoundMgr = nil; + + hsAssert(fScenes.empty(), "Unreleased scenes at shutdown"); + + if (fSDK) + fSDK->release(); + + delete fLog; + fLog = nil; +} + +NxScene* plSimulationMgr::GetScene(plKey world) +{ + if (!world) + world = GetKey(); + + NxScene* scene = fScenes[world]; + + if (!scene) + { + UInt32 maxSteps = (UInt32)hsCeil(fMaxDelta / fStepSize); + + NxSceneDesc sceneDesc; + sceneDesc.gravity.set(0, 0, -32.174049f); + sceneDesc.userTriggerReport = &gSensorReport; + sceneDesc.userContactReport = &gContactReport; + sceneDesc.maxTimestep = fStepSize; + sceneDesc.maxIter = maxSteps; + scene = fSDK->createScene(sceneDesc); + + // Most physicals use the default friction and restitution values, so we + // make them the default. + NxMaterial* mat = scene->getMaterialFromIndex(0); + float rest = mat->getRestitution(); + float sfriction = mat->getStaticFriction(); + float dfriction = mat->getDynamicFriction(); + mat->setRestitution(0.5); + mat->setStaticFriction(0.5); + mat->setDynamicFriction(0.5); + + // By default we just leave all the collision groups enabled, since + // PhysX already makes sure that things like statics and statics don't + // collide. However, we do make it so the avatar and dynamic blockers + // only block avatars and dynamics. + for (int i = 0; i < plSimDefs::kGroupMax; i++) + { + scene->setGroupCollisionFlag(i, plSimDefs::kGroupAvatarBlocker, false); + scene->setGroupCollisionFlag(i, plSimDefs::kGroupDynamicBlocker, false); + scene->setGroupCollisionFlag(i, plSimDefs::kGroupLOSOnly, false); + scene->setGroupCollisionFlag(plSimDefs::kGroupLOSOnly, i, false); + } + scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupAvatar, false); + scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupAvatarBlocker, true); + scene->setGroupCollisionFlag(plSimDefs::kGroupDynamic, plSimDefs::kGroupDynamicBlocker, true); + scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupStatic, true); + scene->setGroupCollisionFlag( plSimDefs::kGroupStatic, plSimDefs::kGroupAvatar, true); + scene->setGroupCollisionFlag(plSimDefs::kGroupAvatar, plSimDefs::kGroupDynamic, true); + + // The dynamics are in actor group 1, everything else is in 0. Request + // a callback for whenever a dynamic touches something. + scene->setActorGroupPairFlags(0, 1, NX_NOTIFY_ON_TOUCH); + scene->setActorGroupPairFlags(1, 1, NX_NOTIFY_ON_TOUCH); + + fScenes[world] = scene; + } + + return scene; +} + +void plSimulationMgr::ReleaseScene(plKey world) +{ + if (!world) + world = GetKey(); + + SceneMap::iterator it = fScenes.find(world); + hsAssert(it != fScenes.end(), "Unknown scene"); + if (it != fScenes.end()) + { + NxScene* scene = it->second; + if (scene->getNbActors() == 0) + { + fSDK->releaseScene(*scene); + fScenes.erase(it); + } + } +} + +void plSimulationMgr::ISendCollisionMsg(plKey receiver, plKey hitter, hsBool entering) +{ + plCollideMsg* msg = TRACKED_NEW plCollideMsg; + msg->fOtherKey = hitter; + msg->fEntering = entering; + msg->AddReceiver(receiver); + msg->Send(); +} + +void plSimulationMgr::AddCollisionMsg(plCollideMsg* msg) +{ + fCollisionMessages.Append(msg); +} +void plSimulationMgr::IDispatchCollisionMessages() +{ + if(fCollisionMessages.GetCount()) + { +#ifndef PLASMA_EXTERNAL_RELEASE + DetectorLog("--------------------------------------------------"); + DetectorLog("Dispatching collision messages from last sim step"); +#endif + for(int i=0; iGetReceiver(0)->GetName(), + fCollisionMessages[i]->GetSender()?fCollisionMessages[i]->GetSender()->GetName():"An Avatar", + fCollisionMessages[i]->fEntering? "enter" : "exit"); +#endif + fCollisionMessages[i]->Send(); + } +#ifndef PLASMA_EXTERNAL_RELEASE + DetectorLog("--------------------------------------------------"); +#endif + fCollisionMessages.SetCount(0); + } +} + +void plSimulationMgr::UpdateDetectorsInScene(plKey world, plKey avatar, hsPoint3& pos, bool entering) +{ + // search thru the actors in a scene looking for convex hull detectors and see if the avatar is inside it + // ... and then send appropiate collision message if needed + NxScene* scene = GetScene(world); + plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->ObjectIsLoaded()); + const plCoordinateInterface* ci = avObj->GetCoordinateInterface(); + hsPoint3 soPos = ci->GetWorldPos(); + if (scene) + { + UInt32 numActors = scene->getNbActors(); + NxActor** actors = scene->getActors(); + + for (int i = 0; i < numActors; i++) + { + plPXPhysical* physical = (plPXPhysical*)actors[i]->userData; + if (physical && physical->DoDetectorHullWorkaround()) + { + if ( physical->IsObjectInsideHull(pos) ) + { + physical->SetInsideConvexHull(entering); + // we are entering this world... say we entered this detector + ISendCollisionMsg(physical->GetObjectKey(), avatar, entering); + } + } + } + } +} + +void plSimulationMgr::UpdateAvatarInDetector(plKey world, plPXPhysical* detector) +{ + // search thru the actors in a scene looking for avatars that might be in the newly enabled detector region + // ... and then send appropiate collision message if needed + if ( detector->DoDetectorHullWorkaround() ) + { + NxScene* scene = GetScene(world); + if (scene) + { + UInt32 numActors = scene->getNbActors(); + NxActor** actors = scene->getActors(); + + for (int i = 0; i < numActors; i++) + { + if ( actors[i]->userData == nil ) + { + // we go a controller + bool isController; + plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(*actors[i],&isController); + if (controller && controller->IsEnabled()) + { + plKey avatar = controller->GetOwner(); + plSceneObject* avObj = plSceneObject::ConvertNoRef(avatar->ObjectIsLoaded()); + const plCoordinateInterface* ci; + if ( avObj && ( ci = avObj->GetCoordinateInterface() ) ) + { + if ( detector->IsObjectInsideHull(ci->GetWorldPos()) ) + { + detector->SetInsideConvexHull(true); + // we are entering this world... say we entered this detector + ISendCollisionMsg(detector->GetObjectKey(), avatar, true); + } + } + } + } + } + } + } +} + +void plSimulationMgr::Advance(float delSecs) +{ + if (fSuspended) + return; + + if (delSecs > fMaxDelta) + { + if (fExtraProfile) + Log("Step clamped from %f to limit of %f", delSecs, fMaxDelta); + delSecs = fMaxDelta; + } + plProfile_IncCount(StepLen, (int)(delSecs*1000)); + +#ifndef PLASMA_EXTERNAL_RELASE + UInt32 stepTime = hsTimer::GetPrecTickCount(); +#endif + plProfile_BeginTiming(Step); + plPXPhysicalControllerCore::UpdatePrestep(delSecs); + plPXPhysicalControllerCore::UpdatePoststep( delSecs); + + for (SceneMap::iterator it = fScenes.begin(); it != fScenes.end(); it++) + { + NxScene* scene = it->second; + bool do_advance = true; + if (fSubworldOptimization) + { + plKey world = (plKey)it->first; + if (world == GetKey()) + world = nil; + do_advance = plPXPhysicalControllerCore::AnyControllersInThisWorld(world); + } + if (do_advance) + { + scene->simulate(delSecs); + scene->flushStream(); + scene->fetchResults(NX_RIGID_BODY_FINISHED, true); + } + } + plPXPhysicalControllerCore::UpdatePostSimStep(delSecs); + + //sending off and clearing the Collision Messages generated by scene->simulate + IDispatchCollisionMessages(); + + plProfile_EndTiming(Step); +#ifndef PLASMA_EXTERNAL_RELEASE + if(plSimulationMgr::fDisplayAwakeActors)IDrawActiveActorList(); +#endif + if (fExtraProfile) + { + int contacts = 0, dynActors = 0, dynShapes = 0, awake = 0, stShapes=0, actors=0, scenes=0, controllers=0 ; + for (SceneMap::iterator it = fScenes.begin(); it != fScenes.end(); it++) + { + bool do_advance = true; + if (fSubworldOptimization) + { + plKey world = (plKey)it->first; + if (world == GetKey()) + world = nil; + do_advance = plPXPhysicalControllerCore::AnyControllersInThisWorld(world); + } + if (do_advance) + { + NxScene* scene = it->second; + NxSceneStats stats; + scene->getStats(stats); + + contacts += stats.numContacts; + dynActors += stats.numDynamicActors; + dynShapes += stats.numDynamicShapes; + awake += stats.numDynamicActorsInAwakeGroups; + stShapes += stats.numStaticShapes; + actors += stats.numActors; + scenes += 1; + controllers += plPXPhysicalControllerCore::NumControllers(); + } + } + + plProfile_IncCount(Awake, awake); + plProfile_IncCount(Contacts, contacts); + plProfile_IncCount(DynActors, dynActors); + plProfile_IncCount(DynShapes, dynShapes); + plProfile_IncCount(StaticShapes, stShapes); + plProfile_IncCount(Actors, actors); + plProfile_IncCount(Scenes, scenes); + plProfile_IncCount(Controllers, controllers); + } + + plProfile_IncCount(AnimatedPhysicals, plPXPhysical::fNumberAnimatedPhysicals); + plProfile_IncCount(AnimatedActivators, plPXPhysical::fNumberAnimatedActivators); + + fSoundMgr->Update(); + + plProfile_BeginTiming(ProcessSyncs); + IProcessSynchs(); + plProfile_EndTiming(ProcessSyncs); + + plProfile_BeginTiming(UpdateContexts); + ISendUpdates(); + plProfile_EndTiming(UpdateContexts); +} + +void plSimulationMgr::ISendUpdates() +{ + SceneMap::iterator it = fScenes.begin(); + for (; it != fScenes.end(); it++) + { + NxScene* scene = it->second; + UInt32 numActors = scene->getNbActors(); + NxActor** actors = scene->getActors(); + + for (int i = 0; i < numActors; i++) + { + plPXPhysical* physical = (plPXPhysical*)actors[i]->userData; + if (physical) + { + // apply any hit forces + physical->ApplyHitForce(); + + if (physical->GetSceneNode()) + { + physical->SendNewLocation(); + } + else + { + // if there's no scene node, it's not active (probably about to be collected) + const plKey physKey = physical->GetKey(); + if (physKey) + { + const char *physName = physical->GetKeyName(); + if (physName) + { + plSimulationMgr::Log("Removing physical <%s> because of missing scene node.\n", physName); + } + } +// Remove(physical); + } + } + } + +// // iterate through the db types, which are powers-of-two enums. +// for( plLOSDB db = static_cast(1) ; +// db < plSimDefs::kLOSDBMax; +// db = static_cast(db << 1) ) +// { +// fLOSSolvers[db]->Resolve(fSubspace); +// } +// if(fNeedLOSCullPhase) +// { +// for( plLOSDB db = static_cast(1) ; +// db < plSimDefs::kLOSDBMax; +// db = static_cast(db << 1) ) +// { +// fLOSSolvers[db]->Resolve(fSubspace); +// } +// fNeedLOSCullPhase = false; +// } + } +} + +hsBool plSimulationMgr::MsgReceive(plMessage *msg) +{ + return hsKeyedObject::MsgReceive(msg); +} + +///////////////////////////////////////////////////////////////// +// +// RESOLUTION & TIMEOUT PARAMETERS +// +///////////////////////////////////////////////////////////////// + +void plSimulationMgr::SetMaxDelta(float maxDelta) +{ + fMaxDelta = maxDelta; +} + +float plSimulationMgr::GetMaxDelta() const +{ + return fMaxDelta; +} + +void plSimulationMgr::SetStepsPerSecond(int stepsPerSecond) +{ + fStepSize = 1.0f / (float)stepsPerSecond; +} + +int plSimulationMgr::GetStepsPerSecond() +{ + return (int)((1.0 / fStepSize) + 0.5f); // round to nearest int +} + +int plSimulationMgr::GetMaterialIdx(NxScene* scene, hsScalar friction, hsScalar restitution) +{ + if (friction == 0.5f && restitution == 0.5f) + return 0; + + // Use the nutty PhysX method to search for a matching material + #define kNumMatsPerCall 32 + NxMaterial* materials[kNumMatsPerCall]; + NxU32 iterator = 0; + bool getMore = true; + while (getMore) + { + int numMats = scene->getMaterialArray(materials, kNumMatsPerCall, iterator); + + for (int i = 0; i < numMats; i++) + { + if (materials[i]->getDynamicFriction() == friction && + materials[i]->getRestitution() == restitution) + { + return materials[i]->getMaterialIndex(); + } + } + + getMore = (numMats == kNumMatsPerCall); + } + + // Couldn't find the material, so create it + NxMaterialDesc desc; + desc.restitution = restitution; + desc.dynamicFriction = friction; + desc.staticFriction = friction; + NxMaterial* mat = scene->createMaterial(desc); + return mat->getMaterialIndex(); +} + +///////////////////////////////////////////////////////////////// +// +// SYNCHRONIZATION +// Very much a work in progress. +// *** would like to synchronize interacting groups as an atomic unit +// *** need a "morphing synch" that incrementally approaches the target +// +///////////////////////////////////////////////////////////////// + +const double plSimulationMgr::SynchRequest::kDefaultTime = -1000.0; + +void plSimulationMgr::ConsiderSynch(plPXPhysical* physical, plPXPhysical* other) +{ + if (physical->GetProperty(plSimulationInterface::kNoSynchronize) && + (!other || other->GetProperty(plSimulationInterface::kNoSynchronize))) + return; + + // We only need to sync if a dynamic is colliding with something. + // Set it up so the dynamic is in 'physical' + if (other && other->GetGroup() == plSimDefs::kGroupDynamic) + { + plPXPhysical* temp = physical; + physical = other; + other = temp; + } + // Neither is dynamic, so we can exit now + else if (physical->GetGroup() != plSimDefs::kGroupDynamic) + return; + + bool syncPhys = !physical->GetProperty(plSimulationInterface::kNoSynchronize) && + physical->IsDynamic() && + physical->IsLocallyOwned(); + bool syncOther = other && + !other->GetProperty(plSimulationInterface::kNoSynchronize) && + other->IsDynamic() != 0.f && + other->IsLocallyOwned(); + + if (syncPhys) + { + double timeNow = hsTimer::GetSysSeconds(); + double timeElapsed = timeNow - physical->GetLastSyncTime(); + + // If both objects are capable of syncing, we want to do it at the same + // time, so no interpenetration issues pop up on other clients + if (syncOther) + timeElapsed = hsMaximum(timeElapsed, timeNow - other->GetLastSyncTime()); + + // Set the sync time to 1 second from the last sync + double syncTime = 0.0; + if (timeElapsed > 1.0) + syncTime = hsTimer::GetSysSeconds(); + else + syncTime = hsTimer::GetSysSeconds() + (1.0 - timeElapsed); + + // This line will create and insert the request if it's not there already. + SynchRequest& physReq = fPendingSynchs[physical]; + if (physReq.fTime == SynchRequest::kDefaultTime) + physReq.fKey = physical->GetKey(); + physReq.fTime = syncTime; + + if (syncOther) + { + SynchRequest& otherReq = fPendingSynchs[other]; + if (otherReq.fTime == SynchRequest::kDefaultTime) + otherReq.fKey = other->GetKey(); + otherReq.fTime = syncTime; + } + } +} + +void plSimulationMgr::IProcessSynchs() +{ + double time = hsTimer::GetSysSeconds(); + + PhysSynchMap::iterator i = fPendingSynchs.begin(); + + while (i != fPendingSynchs.end()) + { + SynchRequest req = (*i).second; + if (req.fKey->ObjectIsLoaded()) + { + plPXPhysical* phys = (*i).first; + bool timesUp = (time >= req.fTime); + bool allQuiet = false;//phys->GetActo GetBody()->isActive() == false; + + if (timesUp || allQuiet) + { + phys->DirtySynchState(kSDLPhysical, plSynchedObject::kBCastToClients); + i = fPendingSynchs.erase(i); + } + else + { + i++; + } + } + else + { + i = fPendingSynchs.erase(i); + } + } +} + +void plSimulationMgr::Log(const char * fmt, ...) +{ + if(gTheInstance) + { + plStatusLog* log = GetInstance()->fLog; + if(log) + { + va_list args; + va_start(args, fmt); + log->AddLineV(fmt, args); + va_end(args); + } + } +} + +void plSimulationMgr::LogV(const char* formatStr, va_list args) +{ + if(gTheInstance) + { + plStatusLog * log = GetInstance()->fLog; + if(log) + { + log->AddLineV(formatStr, args); + } + } +} + +void plSimulationMgr::ClearLog() +{ + if(gTheInstance) + { + plStatusLog *log = GetInstance()->fLog; + if(log) + { + log->Clear(); + } + } +} +#ifndef PLASMA_EXTERNAL_RELEASE +#include "../plPipeline/plDebugText.h" + +void plSimulationMgr::IDrawActiveActorList() +{ + plDebugText &debugTxt = plDebugText::Instance(); + char strBuf[ 2048 ]; + int lineHeight = debugTxt.GetFontSize() + 4; + UInt32 scrnWidth, scrnHeight; + + debugTxt.GetScreenSize( &scrnWidth, &scrnHeight ); + int y = 10; + int x = 10; + + sprintf(strBuf, "Number of scenes: %d", fScenes.size()); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + int sceneNumber=1; + for (SceneMap::iterator it = fScenes.begin(); it != fScenes.end(); it++) + { + + sprintf(strBuf, "Scene: %s",it->first->GetName()); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + UInt32 numActors =it->second->getNbActors(); + NxActor** actors =it->second->getActors(); + for(UInt32 i=0;iisSleeping()) + { + sprintf(strBuf,"\t%s",actors[i]->getName()); + debugTxt.DrawString(x, y, strBuf); + y += lineHeight; + } + } + sceneNumber++; + } +} +#endif //PLASMA_EXTERNAL_RELEASE diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h new file mode 100644 index 00000000..cf5ee626 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysX/plSimulationMgr.h @@ -0,0 +1,193 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSimulationMgr_H +#define plSimulationMgr_H + +#include "hsStlUtils.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsTemplates.h" + +class plPXPhysical; +class plLOSDispatch; +class plStatusLog; +class plPhysicsSoundMgr; +class NxPhysicsSDK; +class NxScene; +class plCollideMsg; +struct hsPoint3; + +class plSimulationMgr : public hsKeyedObject +{ +public: + CLASSNAME_REGISTER(plSimulationMgr); + GETINTERFACE_ANY(plSimulationMgr, hsKeyedObject); + + static plSimulationMgr* GetInstance(); + static void Init(); + static void Shutdown(); + + static bool fExtraProfile; + static bool fSubworldOptimization; + static bool fDoClampingOnStep; + + // initialiation of the PhysX simulation + virtual bool InitSimulation(); + + // Advance the simulation by the given number of seconds + void Advance(float delSecs); + + hsBool MsgReceive(plMessage* msg); + + // The simulation won't run at all if it is suspended + void Suspend() { fSuspended = true; } + void Resume() { fSuspended = false; } + bool IsSuspended() { return fSuspended; } + + // Output the given debug text to the simulation log. + static void Log(const char* formatStr, ...); + static void LogV(const char* formatStr, va_list args); + static void ClearLog(); + + // We've detected a collision, which may be grounds for synchronizing the involved + // physicals over the network. + void ConsiderSynch(plPXPhysical *physical, plPXPhysical *other); + + NxPhysicsSDK* GetSDK() const { return fSDK; } + NxScene* GetScene(plKey world); + // Called when an actor is removed from a scene, checks if it's time to delete + // the scene + void ReleaseScene(plKey world); + + int GetMaterialIdx(NxScene* scene, hsScalar friction, hsScalar restitution); + + // PHYSX FIXME - walk thru all the convex hull detector regions to see if we are in any... we're either coming or going + void UpdateDetectorsInScene(plKey world, plKey avatar, hsPoint3& pos, bool entering); + void UpdateAvatarInDetector(plKey world, plPXPhysical* detector); + //Fix to Move collision messages and their handling out of the simulation step + void AddCollisionMsg(plCollideMsg* msg); +#ifndef PLASMA_EXTERNAL_RELEASE + static bool fDisplayAwakeActors; +#endif //PLASMA_EXTERNAL_RELEASE +protected: + friend class ContactReport; + + void ISendUpdates(); + + plSimulationMgr(); + virtual ~plSimulationMgr(); + + // Set the maximum amount of time (in seconds) that the physics will advance + // between frames. If a frame-to-frame delta is bigger than this, we'll + // clamp it to this value. + // WARNING: animation doesn't do this, so if we clamp the time animated + // physicals and the avatar may move at a faster rate than usual. + void SetMaxDelta(float maxDelta); + float GetMaxDelta() const; + + // Set the number of steps per second that physics will advance. + // The more steps per second, the less fallthough and more accurate + // simulation response. + void SetStepsPerSecond(int stepsPerSecond); + int GetStepsPerSecond(); + + // Walk through the synchronization requests and send them as appropriate. + void IProcessSynchs(); + + // PHYSX FIXME send a collision message - should only be used with UpdateDetectorsInScene + void ISendCollisionMsg(plKey receiver, plKey hitter, hsBool entering); + + NxPhysicsSDK* fSDK; + + plPhysicsSoundMgr* fSoundMgr; + + //a list of collision messages generated by the simulation steps. Added to by AddCollisionMsg(plCollideMsg* msg) + //cleared by IDispatchCollisionMessages when done + hsTArray fCollisionMessages; + + void IDispatchCollisionMessages(); + + // A mapping from a key to a PhysX scene. The key is either the + // SimulationMgr key, for the main world, or a SceneObject key if it's a + // subworld. + typedef std::map SceneMap; + SceneMap fScenes; + + plLOSDispatch* fLOSDispatch; + + // Is the entire physics world suspended? If so, the clock can still advance + // but nothing will move. + bool fSuspended; + + float fMaxDelta; + float fStepSize; + + // A utility class to keep track of a request for a physical synchronization. + // These requests must pass a certain criteria (see the code for the latest) + // before they are actually either sent over the network or rejected. + class SynchRequest + { + public: + double fTime; // when to synch + plKey fKey; // key of the object to be synched, so we can make sure it still lives + + static const double kDefaultTime; + SynchRequest() : fTime(kDefaultTime) {}; + }; + + // All currently pending synch requests. Keyed by the physical in question + // so we can quickly eliminate redundant requests, which are very common. + typedef std::map PhysSynchMap; + PhysSynchMap fPendingSynchs; + + plStatusLog *fLog; +#ifndef PLASMA_EXTERNAL_RELEASE + void IDrawActiveActorList(); +#endif //PLASMA_EXTERNAL_RELEASE +}; + +#define SIM_VERBOSE + +#ifdef SIM_VERBOSE +#include // only include when we need to call plSimulationMgr::Log + +inline void SimLog(const char *str, ...) +{ + va_list args; + va_start(args, str); + plSimulationMgr::LogV(str, args); + va_end(args); +} + +#else + +inline void SimLog(const char *str, ...) +{ + // will get stripped out +} + +#endif + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp new file mode 100644 index 00000000..cf544a87 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.cpp @@ -0,0 +1,1155 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../plAvatar/plAvCallbackAction.h" + +#include "hsTypes.h" +#include "plCollisionDetector.h" +#include "../plMessage/plCollideMsg.h" +#include "plgDispatch.h" +#include "../plMessage/plActivatorMsg.h" +#include "../pnMessage/plCameraMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../pnInputCore/plControlEventCodes.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClient/plNetLinkingMgr.h" + +#include "plPhysical.h" + +#include "../pnMessage/plPlayerPageMsg.h" +#include "../plMessage/plSimStateMsg.h" + +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plAvatar/plAvBrainDrive.h" + +#include "../plModifier/plDetectorLog.h" + +#define USE_PHYSX_MULTIPLE_CAMREGION_ENTER 1 +#define USE_PHYSX_COLLISION_FLUTTER_WORKAROUND 1 + +plArmatureMod* plCollisionDetector::IGetAvatarModifier(plKey key) +{ + plSceneObject* avObj = plSceneObject::ConvertNoRef(key->ObjectIsLoaded()); + if (avObj) + { + // search through its modifiers to see if one of them is an avatar modifier + plArmatureMod* avMod = nil; + for (int i = 0; i < avObj->GetNumModifiers(); i++) + { + const plModifier* mod = avObj->GetModifier(i); + // see if it is an avatar mod base class + avMod = const_cast(plArmatureMod::ConvertNoRef(mod)); + if (avMod) + return avMod; + } + } + + return nil; +} + +bool plCollisionDetector::IIsDisabledAvatar(plKey key) +{ + plArmatureMod* avMod = IGetAvatarModifier(key); + plArmatureBrain* avBrain = avMod ? avMod->GetCurrentBrain() : nil; + return (plAvBrainDrive::ConvertNoRef(avBrain) != nil); +} + +hsBool plCollisionDetector::MsgReceive(plMessage* msg) +{ + plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg); + + if (pCollMsg) + { + // If the avatar is disabled (flying around), don't trigger + if (IIsDisabledAvatar(pCollMsg->fOtherKey)) + return false; + + if (fType & kTypeBump) + { + if (!fBumped && !fTriggered) + { + for (int i = 0; i < fReceivers.Count(); i++) + { + plActivatorMsg* pMsg = TRACKED_NEW plActivatorMsg; + pMsg->AddReceiver( fReceivers[i] ); + + if (fProxyKey) + pMsg->fHiteeObj = fProxyKey; + else + pMsg->fHiteeObj = GetTarget()->GetKey(); + pMsg->fHitterObj = pCollMsg->fOtherKey; + pMsg->SetSender(GetKey()); + pMsg->SetTriggerType( plActivatorMsg::kCollideContact ); + plgDispatch::MsgSend( pMsg ); + } + fBumped = true; + fTriggered = true; + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + return true; + } + if (fTriggered) + { + fBumped = true; + return true; + } + return false; + } + + for (int i = 0; i < fReceivers.Count(); i++) + { + plActivatorMsg* pMsg = TRACKED_NEW plActivatorMsg; + pMsg->AddReceiver( fReceivers[i] ); + if (fProxyKey) + pMsg->fHiteeObj = fProxyKey; + else + pMsg->fHiteeObj = GetTarget()->GetKey(); + pMsg->fHitterObj = pCollMsg->fOtherKey; + pMsg->SetSender(GetKey()); + + if (fType & kTypeEnter && pCollMsg->fEntering) + { + pMsg->SetTriggerType( plActivatorMsg::kCollideEnter ); + plgDispatch::MsgSend( pMsg ); + continue; + } + if (fType & kTypeUnEnter && pCollMsg->fEntering) + { + pMsg->SetTriggerType( plActivatorMsg::kEnterUnTrigger ); + plgDispatch::MsgSend( pMsg ); + continue; + } + if(fType & kTypeExit && !pCollMsg->fEntering) + { + pMsg->SetTriggerType( plActivatorMsg::kCollideExit ); + plgDispatch::MsgSend( pMsg ); + continue; + } + if(fType & kTypeUnExit && !pCollMsg->fEntering) + { + pMsg->SetTriggerType( plActivatorMsg::kExitUnTrigger ); + plgDispatch::MsgSend( pMsg ); + continue; + } + if (fType & kTypeAny) + { + pMsg->SetTriggerType( plActivatorMsg::kCollideContact ); + plgDispatch::MsgSend( pMsg ); + continue; + } + + delete (pMsg); + } + return true; + } + + plEvalMsg* pEval = plEvalMsg::ConvertNoRef(msg); + if (pEval) + { + if (!fBumped && fTriggered) + { + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + for (int i = 0; i < fReceivers.Count(); i++) + { + plActivatorMsg* pMsg = TRACKED_NEW plActivatorMsg; + pMsg->AddReceiver( fReceivers[i] ); + if (fProxyKey) + pMsg->fHiteeObj = fProxyKey; + else + pMsg->fHiteeObj = GetTarget()->GetKey(); + pMsg->SetSender(GetKey()); + pMsg->SetTriggerType( plActivatorMsg::kCollideUnTrigger ); + plgDispatch::MsgSend( pMsg ); + fTriggered = false; + } + } + else + if (fTriggered && fBumped) + { + fBumped = false; + } + return true; + } + + return plDetectorModifier::MsgReceive(msg); +} + +void plCollisionDetector::Read(hsStream* stream, hsResMgr* mgr) +{ + plDetectorModifier::Read(stream, mgr); + stream->ReadSwap(&fType); +} +void plCollisionDetector::Write(hsStream* stream, hsResMgr* mgr) +{ + plDetectorModifier::Write(stream, mgr); + stream->WriteSwap(fType); +} + +///////////////////////////////// +///////////////////////////////// +///////////////////////////////// +///////////////////////////////// +// camera region detector + +plCameraRegionDetector::~plCameraRegionDetector() +{ + for(int i = 0; i < fMessages.Count(); i++) + { + plMessage* pMsg = fMessages[i]; + fMessages.Remove(i); + delete(pMsg); + } + fMessages.SetCountAndZero(0); +} + +void plCameraRegionDetector::ITrigger(plKey hitter, bool entering, bool immediate) +{ + +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + // PHYSX_FIXME hack for PhysX turd that sends bunches of enter/exits over one frame + if (entering && fNumEvals - fLastExitEval <= 1 && fSavingSendMsg) + { + DetectorLog("%s: Skipping Camera Entering volume", GetKeyName()); + fLastEnterEval = fNumEvals; + if (fSavingSendMsg) + { + DetectorLog("%s: Dumping saved Camera Exiting volume", GetKeyName()); + } + fSavingSendMsg = false; + return; + } + if (!entering && fNumEvals - fLastEnterEval <= 1 && fSavingSendMsg) + { + DetectorLog("%s: Skipping Exiting volume", GetKeyName()); + fLastExitEval = fNumEvals; + if (fSavingSendMsg) + { + DetectorLog("%s: Dumping saved Camera Entering volume", GetKeyName()); + } + fSavingSendMsg = false; + return; + } + + // get rid of any saved messages... this should happen though + if (fSavingSendMsg) + { + DetectorLog("%s: Killing saved camera message... shouldn't happen", GetKeyName()); + } + // end PHYSX_FIXME hack for PhysX turd that sends bunches of enter/exits over one frame +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + + fSavingSendMsg = true; + fSavedMsgEnterFlag = entering; + if (entering) + { + //DetectorLog("%s: Saving camera Entering volume - Evals=%d", GetKeyName(),fNumEvals); + fLastEnterEval = fNumEvals; + } + else + { + //DetectorLog("%s: Saving camera Exiting volume - Evals=%d", GetKeyName(),fNumEvals); + fLastExitEval = fNumEvals; + } + +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + // PHYSX_FIXME hack for PhysX turd that sends bunches of enter/exits over one frame + // we're saving the message to be dispatched later... + if (immediate) + { +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + + ISendSavedTriggerMsgs(); + +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + } +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + + +} + +void plCameraRegionDetector::ISendSavedTriggerMsgs() +{ + if (fSavingSendMsg) + { + for (int i = 0; i < fMessages.Count(); i++) + { + char str[256]; + + hsRefCnt_SafeRef(fMessages[i]); + if (fSavedMsgEnterFlag) + { + fMessages[i]->SetCmd(plCameraMsg::kEntering); + sprintf(str, "Entering cameraRegion: %s - Evals=%d -msg %d of %d\n", GetKeyName(),fNumEvals,i+1,fMessages.Count()); + fIsInside = true; + } + else + { + fMessages[i]->ClearCmd(plCameraMsg::kEntering); + sprintf(str, "Exiting cameraRegion: %s - Evals=%d -msg %d of %d\n", GetKeyName(),fNumEvals,i+1,fMessages.Count()); + fIsInside = false; + } + plgDispatch::MsgSend(fMessages[i]); + DetectorLog("%s", str); + } + } + fSavingSendMsg = false; +} + + +hsBool plCameraRegionDetector::MsgReceive(plMessage* msg) +{ + plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg); + + if (pCollMsg) + { + // camera collisions are only for the local player + if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pCollMsg->fOtherKey) + return true; + + +#ifdef USE_PHYSX_MULTIPLE_CAMREGION_ENTER + // first determine if this is a multiple camera region enter (PHYSX BUG WORKAROUND) + if (!fWaitingForEval) + {//plObjectInVolumeCollisionDetector::MsgReceive() will flip fWaitingForEval + // and registers for the Eval, child objects of plObjectInVolumeCollisionDetector + //must decide when they are no longer interested in Evals. I suggest using IHandleEvals() + + fNumEvals = 0; + fLastEnterEval=-999; + fLastExitEval=-999; + } + + // end of (PHYSX BUG WORKAROUND) +#endif // USE_PHYSX_MULTIPLE_CAMREG_ENTER + + } + return plObjectInVolumeDetector::MsgReceive(msg); +} +void plCameraRegionDetector::Read(hsStream* stream, hsResMgr* mgr) +{ + plDetectorModifier::Read(stream, mgr); + int n = stream->ReadSwap32(); + fMessages.SetCountAndZero(n); + for(int i = 0; i < n; i++ ) + { + plCameraMsg* pMsg = plCameraMsg::ConvertNoRef(mgr->ReadCreatable(stream)); + fMessages[i] = pMsg; + } + +} +void plCameraRegionDetector::Write(hsStream* stream, hsResMgr* mgr) +{ + plDetectorModifier::Write(stream, mgr); + stream->WriteSwap32(fMessages.GetCount()); + for(int i = 0; i < fMessages.GetCount(); i++ ) + mgr->WriteCreatable( stream, fMessages[i] ); + +} +void plCameraRegionDetector::IHandleEval(plEvalMsg *pEval) +{ + fNumEvals++; + if (fNumEvals - fLastEnterEval > 1 && fNumEvals-fLastExitEval>1) + { + ISendSavedTriggerMsgs(); + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + fWaitingForEval = false; + } + else + { + if(fSavedActivatorMsg) + DetectorLog("%s didn't send its message. fNumEvals=%d fLastEnterEval=%d, fLastExit=%d", + GetKeyName(),fNumEvals, fLastEnterEval, fLastExitEval); + + } +} + +///////////////////////////////// +///////////////////////////////// +///////////////////////////////// +///////////////////////////////// +// object-in-volume detector + +void plObjectInVolumeDetector::ITrigger(plKey hitter, bool entering, bool immediate) +{ +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + // PHYSX_FIXME hack for PhysX turd that sends bunches of enter/exits over one frame +/* if (entering && fNumEvals - fLastExitEval <= 1 && fSavedActivatorMsg) + { + //DetectorLog("%s: Skipping Entering volume", GetKeyName()); + fLastEnterEval = fNumEvals; + if (fSavedActivatorMsg) + { + //DetectorLog("%s: Dumping saved Exiting volume", GetKeyName()); + delete fSavedActivatorMsg; + } + fSavedActivatorMsg = nil; + return; + } + if (!entering && fNumEvals - fLastEnterEval <= 1 && fSavedActivatorMsg) + { + //DetectorLog("%s: Skipping Exiting volume", GetKeyName()); + fLastExitEval = fNumEvals; + if (fSavedActivatorMsg) + { + //DetectorLog("%s: Dumping saved Entering volume", GetKeyName()); + delete fSavedActivatorMsg; + } + fSavedActivatorMsg = nil; + return; + } + + // get rid of any saved messages... this should happen though + if (fSavedActivatorMsg) + { + delete fSavedActivatorMsg; + DetectorLog("%s: Killing saved message... shouldn't happen", GetKeyName()); + } + // end PHYSX_FIXME hack for PhysX turd that sends bunches of enter/exits over one frame +*/ + if(!immediate) + { + + bookKeepingList::iterator curit=fCollisionList.begin(); + while(curit!=fCollisionList.end()) + { + if(hitter==(*curit)->hitter) + {//hey the object is already in my list + //try and figure out what my real state is + if(entering) + { + (*curit)->enters++; + if(!(*curit)->fSubStepCurState) + {//We weren't already in + (*curit)->fSubStepCurState =true; + } + } + else + { + (*curit)->exits++; + if((*curit)->fSubStepCurState) + {//We were already in + (*curit)->fSubStepCurState =false; + } + } + //get out + break; + } + curit++; + } + if(curit==fCollisionList.end()) + { + //hitter was not in the list add him in + //hitter was not in the current frame list + //lets find out its state in the begining of the frame + ResidentSet::iterator curres = fCurrentResidents.find(hitter); + bool initialState; + if(curres != fCurrentResidents.end()) + initialState =true; + else + initialState =false; + + plCollisionBookKeepingInfo* BookKeeper=TRACKED_NEW plCollisionBookKeepingInfo(hitter); + if(entering) + { + BookKeeper->enters++; + BookKeeper->fSubStepCurState =true; + } + else + { + BookKeeper->exits++; + BookKeeper->fSubStepCurState =false; + } + fCollisionList.push_front(BookKeeper); + } + + + } + else + { + plActivatorMsg* ActivatorMsg = TRACKED_NEW plActivatorMsg; + ActivatorMsg->AddReceivers(fReceivers); + if (fProxyKey) + ActivatorMsg->fHiteeObj = fProxyKey; + else + ActivatorMsg->fHiteeObj = GetTarget()->GetKey(); + + ActivatorMsg->fHitterObj = hitter; + ActivatorMsg->SetSender(GetKey()); + if (entering) + { + ActivatorMsg->SetTriggerType(plActivatorMsg::kVolumeEnter); + } + else + { + ActivatorMsg->SetTriggerType(plActivatorMsg::kVolumeExit); + } + + plgDispatch::MsgSend(ActivatorMsg); + + + } +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + +/* fSavedActivatorMsg = TRACKED_NEW plActivatorMsg; + + fSavedActivatorMsg->AddReceivers(fReceivers); + + if (fProxyKey) + fSavedActivatorMsg->fHiteeObj = fProxyKey; + else + fSavedActivatorMsg->fHiteeObj = GetTarget()->GetKey(); + + fSavedActivatorMsg->fHitterObj = hitter; + fSavedActivatorMsg->SetSender(GetKey()); + + if (entering) + { + //DetectorLog("%s: Saving Entering volume - Evals=%d", GetKeyName(),fNumEvals); + fSavedActivatorMsg->SetTriggerType(plActivatorMsg::kVolumeEnter); + fLastEnterEval = fNumEvals; + } + else + { + //DetectorLog("%s: Saving Exiting volume - Evals=%d", GetKeyName(),fNumEvals); + fSavedActivatorMsg->SetTriggerType(plActivatorMsg::kVolumeExit); + fLastExitEval = fNumEvals; + } +*/ +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + // PHYSX_FIXME hack for PhysX turd that sends bunches of enter/exits over one frame + // we're saving the message to be dispatched later... + if (immediate) + { +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + + + // fSavedActivatorMsg = nil; + +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + } +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND +} +/* +void plObjectInVolumeDetector::ISendSavedTriggerMsgs() +{ + if (fSavedActivatorMsg) + { + if (fSavedActivatorMsg->fTriggerType == plActivatorMsg::kVolumeEnter) + DetectorLog("%s: Sending Entering volume - Evals=%d", GetKeyName(),fNumEvals); + else + DetectorLog("%s: Sending Exiting volume - Evals=%d", GetKeyName(),fNumEvals); + + // we're saving the message to be dispatched later... + plgDispatch::MsgSend(fSavedActivatorMsg); + } + fSavedActivatorMsg = nil; +} +*/ +hsBool plObjectInVolumeDetector::MsgReceive(plMessage* msg) +{ + plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg); + if (pCollMsg) + { + // If the avatar is disabled (flying around), don't trigger + if (IIsDisabledAvatar(pCollMsg->fOtherKey)) + return false; + +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + // PHYSX_FIXME hack for PhysX turd that sends bunches of enter/exits over one frame + if (!fWaitingForEval) + { + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + fWaitingForEval = true; + } + // end PHYSX_FIXME hack for PhysX turd that sends bunches of enter/exits over one frame +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + ITrigger(pCollMsg->fOtherKey, (pCollMsg->fEntering != 0)); + + return true; + } + +#ifdef USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + // PHYSX_FIXME hack for PhysX turd that sends bunches of enter/exits over one frame + plEvalMsg* pEval = plEvalMsg::ConvertNoRef(msg); + if (pEval) + { + + //if (fSavedActivatorMsg) + // DetectorLog("%s: InVolumeEval=%d with saved message", GetKeyName(), fNumEvals); + //else + // DetectorLog("%s: InVolumeEval=%d without saved message", GetKeyName(), fNumEvals); + IHandleEval(pEval); + + } + // end PHYSX_FIXME hack for PhysX turd that sends bunches of enter/exits over one frame +#endif // USE_PHYSX_COLLISION_FLUTTER_WORKAROUND + + plPlayerPageMsg* pageMsg = plPlayerPageMsg::ConvertNoRef(msg); + if (pageMsg && pageMsg->fUnload) + { + ITrigger(pageMsg->fPlayer, false); + } + + return plCollisionDetector::MsgReceive(msg); +} + +void plObjectInVolumeDetector::IHandleEval(plEvalMsg* pEval) +{ + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + fWaitingForEval = false; + for(bookKeepingList::iterator it= (--fCollisionList.end());it!=(--fCollisionList.begin()); it--) + { + bool alreadyInside; + ResidentSet::iterator HitIt; + HitIt = fCurrentResidents.find((*it)->hitter); + if(HitIt != fCurrentResidents.end()) alreadyInside = true; + else alreadyInside=false; + plActivatorMsg* actout=TRACKED_NEW plActivatorMsg; + actout->fHitterObj=((*it)->hitter); + actout->SetSender(GetKey()); + if (fProxyKey) + actout->fHiteeObj = fProxyKey; + else + actout->fHiteeObj = GetTarget()->GetKey(); + if((*it)->fSubStepCurState)//current substate says we are entered + {//different enters and exits + //figure out what to do + if(!alreadyInside) + {//we are actuall entering + actout->SetTriggerType(plActivatorMsg::kVolumeEnter); + fCurrentResidents.insert((*it)->hitter); + actout->AddReceivers(fReceivers); + actout->Send(); + DetectorLog("%s sent an Enter ActivatorMsg. To: %s", GetKeyName(), GetTarget()->GetKeyName() ); + } + else + { + DetectorLog("%s squelched an Enter ActivatorMsg.", GetKeyName()); + delete actout; + } + } + else + { + //fSubStepCurState says we are outside + if(alreadyInside) + {//we are actuall exiting + actout->SetTriggerType(plActivatorMsg::kVolumeExit); + fCurrentResidents.erase((*it)->hitter); + actout->AddReceivers(fReceivers); + actout->Send(); + DetectorLog("%s sent an Exit ActivatorMsg. To: %s", GetKeyName(), GetTarget()->GetKeyName()); + } + else + { + DetectorLog("%s squelched an Exit ActivatorMsg.", GetKeyName()); + delete actout; + } + } + } + DetectorLog("*********"); + for(bookKeepingList::iterator it = fCollisionList.begin(); it != fCollisionList.end(); it ++) + { + delete (*it); + } + DetectorLog("This is the regions inhabitants after the op"); + for(ResidentSet::iterator it = fCurrentResidents.begin(); it!= fCurrentResidents.end(); it++) + { + DetectorLog("%s", (*it)->GetName()); + } + DetectorLog("*********"); + + fCollisionList.clear(); + +} + +void plObjectInVolumeDetector::SetTarget(plSceneObject* so) +{ + plCollisionDetector::SetTarget(so); + + if (so) + plgDispatch::Dispatch()->RegisterForExactType(plPlayerPageMsg::Index(), GetKey()); + else + plgDispatch::Dispatch()->UnRegisterForExactType(plPlayerPageMsg::Index(), GetKey()); +} + +void plObjectInVolumeDetector::Read(hsStream* stream, hsResMgr* mgr) +{ + plCollisionDetector::Read(stream, mgr); +} + +void plObjectInVolumeDetector::Write(hsStream* stream, hsResMgr* mgr) +{ + plCollisionDetector::Write(stream, mgr); +} + + +/////////////////////////////////////////////////////////////////////////////// + + +plObjectInVolumeAndFacingDetector::plObjectInVolumeAndFacingDetector() : + fFacingTolerance(0), + fNeedWalkingForward(false), + fAvatarInVolume(false), + fTriggered(false) +{ +} + +plObjectInVolumeAndFacingDetector::~plObjectInVolumeAndFacingDetector() +{ +} + +void plObjectInVolumeAndFacingDetector::SetFacingTolerance(int degrees) +{ + fFacingTolerance = hsCosine(hsScalarDegToRad(degrees)); +} + +void plObjectInVolumeAndFacingDetector::ICheckForTrigger() +{ + plArmatureMod* armMod = plAvatarMgr::GetInstance()->GetLocalAvatar(); + plSceneObject* avatar = armMod ? armMod->GetTarget(0) : nil; + plSceneObject* target = GetTarget(); + + if (armMod && target) + { + hsVector3 playerView = avatar->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + hsVector3 objView = target->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView); + + playerView.Normalize(); + objView.Normalize(); + + hsScalar dot = playerView * objView; +// hsStatusMessageF("Dot: %f Tolerance: %f", dot, fFacingTolerance); + bool facing = dot >= fFacingTolerance; + + bool movingForward = false; + if (fNeedWalkingForward) + { + // And are we walking towards it? + plArmatureBrain* abrain = armMod->FindBrainByClass(plAvBrainHuman::Index()); //armMod->GetCurrentBrain(); + plAvBrainHuman* brain = plAvBrainHuman::ConvertNoRef(abrain); + if (brain && brain->IsMovingForward() && brain->fCallbackAction->IsOnGround()) + movingForward = true; + } + else + movingForward = true; + + if (facing && movingForward && !fTriggered) + { + DetectorLog("%s: Trigger InVolume&Facing", GetKeyName()); + fTriggered = true; + ITrigger(avatar->GetKey(), true, true); + } + else if (!facing && fTriggered) + { + DetectorLog("%s: Untrigger InVolume&Facing", GetKeyName()); + fTriggered = false; + ITrigger(avatar->GetKey(), false, true); + } + } +} + +hsBool plObjectInVolumeAndFacingDetector::MsgReceive(plMessage* msg) +{ + // Avatar is entering or exiting our detector box + plCollideMsg* collMsg = plCollideMsg::ConvertNoRef(msg); + if (collMsg) + { + // make sure this is the local player... the notify will be the thing that propagates over the network + if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != collMsg->fOtherKey) + return true; + + // If the avatar is disabled (flying around), don't trigger + if (IIsDisabledAvatar(collMsg->fOtherKey)) + return false; + + fAvatarInVolume = (collMsg->fEntering != 0); + + if (fAvatarInVolume) + { + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + ICheckForTrigger(); + } + else + { + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + + // Avatar is leaving the volume, make sure to untrigger if we haven't already + if (fTriggered) + { + fTriggered = false; + ITrigger(plNetClientApp::GetInstance()->GetLocalPlayerKey(), false, true); + } + } + + return true; + } + + // Avatar is inside our detector box, so every frame we check if we need to trigger + plEvalMsg* evalMsg = plEvalMsg::ConvertNoRef(msg); + if (evalMsg) + { + ICheckForTrigger(); + return true; + } + + return plObjectInVolumeDetector::MsgReceive(msg); +} + +void plObjectInVolumeAndFacingDetector::Read(hsStream* stream, hsResMgr* mgr) +{ + plObjectInVolumeDetector::Read(stream, mgr); + + fFacingTolerance = stream->ReadSwapScalar(); + fNeedWalkingForward = stream->Readbool(); +} + +void plObjectInVolumeAndFacingDetector::Write(hsStream* stream, hsResMgr* mgr) +{ + plObjectInVolumeDetector::Write(stream, mgr); + + stream->WriteSwapScalar(fFacingTolerance); + stream->Writebool(fNeedWalkingForward); +} + +///////////////////////////////// +///////////////////////////////// +///////////////////////////////// +///////////////////////////////// +// subworld region detector + +plSubworldRegionDetector::~plSubworldRegionDetector() +{ +} + + +hsBool plSubworldRegionDetector::MsgReceive(plMessage* msg) +{ + plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg); + + if (pCollMsg) + { + if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pCollMsg->fOtherKey) + return true; + + plArmatureMod* avMod = IGetAvatarModifier(pCollMsg->fOtherKey); + if (avMod) + { + DetectorLog("%s subworld detector %s", pCollMsg->fEntering ? "Entering" : "Exiting", GetKeyName()); + + if ((pCollMsg->fEntering && !fOnExit) || + (!pCollMsg->fEntering && fOnExit)) + { + if (fSub) + { + plSceneObject* SO = plSceneObject::ConvertNoRef(fSub->ObjectIsLoaded()); + if (SO) + { + DetectorLogSpecial("Switching to subworld %s", fSub->GetName()); + + plKey nilKey; + plSubWorldMsg* msg = TRACKED_NEW plSubWorldMsg(GetKey(), avMod->GetKey(), fSub); + msg->Send(); + } + } + else + { + DetectorLogSpecial("Switching to main subworld"); + plSubWorldMsg* msg = TRACKED_NEW plSubWorldMsg(GetKey(), avMod->GetKey(), nil); + msg->Send(); + } + } + } + + return true; + } + + return plCollisionDetector::MsgReceive(msg); +} + +void plSubworldRegionDetector::Read(hsStream* stream, hsResMgr* mgr) +{ + plDetectorModifier::Read(stream, mgr); + fSub = mgr->ReadKey(stream); + fOnExit = stream->ReadBool(); +} +void plSubworldRegionDetector::Write(hsStream* stream, hsResMgr* mgr) +{ + plDetectorModifier::Write(stream, mgr); + mgr->WriteKey(stream, fSub); + stream->WriteBool(fOnExit); +} + +/////////////////////////////////// +/////////////////////////////////// +/// plPanicLinkDetector +/////////////////////////////////// +hsBool plPanicLinkRegion::MsgReceive(plMessage* msg) +{ + plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg); + + if (pCollMsg) + { + if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pCollMsg->fOtherKey) + return true; + + if (pCollMsg->fEntering) + { + plArmatureMod* avMod = IGetAvatarModifier(pCollMsg->fOtherKey); + if (avMod) + { + hsPoint3 kinPos; + if (avMod->GetController()) + { + avMod->GetController()->GetKinematicPosition(kinPos); + DetectorLogSpecial("Avatar is panic linking. KinPos at %f,%f,%f and is %s",kinPos.fX,kinPos.fY,kinPos.fZ,avMod->GetController()->IsEnabled() ? "enabled" : "disabled"); + } + avMod->PanicLink(fPlayLinkOutAnim); + } + } + + return true; + } + + return plCollisionDetector::MsgReceive(msg); +} + +void plPanicLinkRegion::Read(hsStream* stream, hsResMgr* mgr) +{ + plCollisionDetector::Read(stream, mgr); + + fPlayLinkOutAnim = stream->ReadBool(); +} + +void plPanicLinkRegion::Write(hsStream* stream, hsResMgr* mgr) +{ + plCollisionDetector::Write(stream, mgr); + + stream->WriteBool(fPlayLinkOutAnim); +} + +///////////////////////////////////////////////////////////////// +// +// PLSIMPLEREGIONSENSOR +// +///////////////////////////////////////////////////////////////// + +// ctor default +plSimpleRegionSensor::plSimpleRegionSensor() +: fEnterMsg(nil), fExitMsg(nil) +{ +} + +// ctor canonical +plSimpleRegionSensor::plSimpleRegionSensor(plMessage *enterMsg, plMessage *exitMsg) +: fEnterMsg(enterMsg), fExitMsg(exitMsg) +{ +} + +// dtor +plSimpleRegionSensor::~plSimpleRegionSensor() +{ + if(fEnterMsg) + fEnterMsg->UnRef(); + if(fExitMsg) + fExitMsg->UnRef(); +} + +// WRITE +void plSimpleRegionSensor::Write(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Write(stream, mgr); + if(fEnterMsg) + { + stream->Writebool(true); + mgr->WriteCreatable(stream, fEnterMsg); + } else { + stream->Writebool(false); + } + if(fExitMsg) + { + stream->Writebool(true); + mgr->WriteCreatable(stream, fExitMsg); + } else { + stream->Writebool(false); + } +} + +// READ +void plSimpleRegionSensor::Read(hsStream *stream, hsResMgr *mgr) +{ + plSingleModifier::Read(stream, mgr); + if(stream->Readbool()) + { + fEnterMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); + } else { + fEnterMsg = nil; + } + + if(stream->Readbool()) + { + fExitMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream)); + hsAssert(fExitMsg, "Corrupted plSimpleRegionSensor during read."); + } else { + fExitMsg = nil; + } +} + +// MSGRECEIVE +hsBool plSimpleRegionSensor::MsgReceive(plMessage *msg) +{ + plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg); + + if (pCollMsg) + { + // make sure this is the local player... the notify will be the thing that propagates over the network + if (plNetClientApp::GetInstance()->GetLocalPlayerKey() != pCollMsg->fOtherKey) + return true; + + plKey theThingWhatDoneHitUs = pCollMsg->fOtherKey; + + if(pCollMsg->fEntering) + { + if(fEnterMsg) + { + fEnterMsg->ClearReceivers(); + fEnterMsg->AddReceiver(theThingWhatDoneHitUs); + fEnterMsg->Ref(); + fEnterMsg->Send(); + } + } + else { + if(fExitMsg) + { + fExitMsg->ClearReceivers(); + fExitMsg->AddReceiver(theThingWhatDoneHitUs); + fExitMsg->Ref(); + fExitMsg->Send(); + } + } + return true; + } + return plSingleModifier::MsgReceive(msg); +} + +// IEVAL +hsBool plSimpleRegionSensor::IEval(double secs, hsScalar del, UInt32 dirty) +{ + return false; +} + +////////////////////////////////////////////////////////////////////////////// + +// Nuke the Read/Write functions on the next file format change +void plSwimDetector::Write(hsStream *stream, hsResMgr *mgr) +{ + plSimpleRegionSensor::Write(stream, mgr); + + stream->WriteByte(0); + stream->WriteSwapScalar(0); + stream->WriteSwapScalar(0); +} + +void plSwimDetector::Read(hsStream *stream, hsResMgr *mgr) +{ + plSimpleRegionSensor::Read(stream, mgr); + + stream->ReadByte(); + stream->ReadSwapScalar(); + stream->ReadSwapScalar(); +} +hsBool plSwimDetector::MsgReceive(plMessage *msg) +{ + plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg); + + if (pCollMsg) + { + //removed local player check because this will apply the brain to the local + //controller of the foreign avatar which we still want. + //and if we prop swim state by notify messages we still have a chance of missing it from players + //who were in the region before we linked in + plKey theThingWhatDoneHitUs = pCollMsg->fOtherKey; + if(pCollMsg->fEntering) + { + if(fEnterMsg) + { + fEnterMsg->ClearReceivers(); + fEnterMsg->AddReceiver(theThingWhatDoneHitUs); + fEnterMsg->Ref(); + fEnterMsg->Send(); + } + } + else { + if(fExitMsg) + { + fExitMsg->ClearReceivers(); + fExitMsg->AddReceiver(theThingWhatDoneHitUs); + fExitMsg->Ref(); + fExitMsg->Send(); + } + } + return true; + } + return plSimpleRegionSensor::MsgReceive(msg); +} +hsBool plRidingAnimatedPhysicalDetector::MsgReceive(plMessage *msg) +{ + + plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg); + + if (pCollMsg) + { + //removed local player check because this will apply the brain to the local + //controller of the foreign avatar which we still want. + //and if we prop state by notify messages we still have a chance of missing it from players + //who were in the region before we linked in + plKey theThingWhatDoneHitUs = pCollMsg->fOtherKey; + if(pCollMsg->fEntering) + { + if(fEnterMsg) + { + fEnterMsg->ClearReceivers(); + fEnterMsg->AddReceiver(theThingWhatDoneHitUs); + fEnterMsg->Ref(); + fEnterMsg->Send(); + } + } + else { + if(fExitMsg) + { + fExitMsg->ClearReceivers(); + fExitMsg->AddReceiver(theThingWhatDoneHitUs); + fExitMsg->Ref(); + fExitMsg->Send(); + } + } + return true; + } + return plSimpleRegionSensor::MsgReceive(msg); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h new file mode 100644 index 00000000..7223a57b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plCollisionDetector.h @@ -0,0 +1,290 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCollisionDetector_inc +#define plCollisionDetector_inc + +#include "plDetectorModifier.h" +#include "hsGeometry3.h" +#include +#include +class plMessage; +class plCameraMsg; +class plArmatureMod; +class plActivatorMsg; +class plEvalMsg; + +class plCollisionDetector : public plDetectorModifier +{ +protected: + Int8 fType; + hsBool fBumped, fTriggered; + + plArmatureMod* IGetAvatarModifier(plKey key); + bool IIsDisabledAvatar(plKey key); + +public: + enum + { + kTypeEnter = 0x01, + kTypeExit = 0x02, + kTypeAny = 0x04, + kTypeUnEnter = 0x08, + kTypeUnExit = 0x10, + kTypeBump = 0x20, + }; + + plCollisionDetector() : fType(0), fTriggered(false), fBumped(false){;} + virtual ~plCollisionDetector(){;} + + virtual hsBool MsgReceive(plMessage* msg); + + CLASSNAME_REGISTER( plCollisionDetector ); + GETINTERFACE_ANY( plCollisionDetector, plDetectorModifier ); + + virtual void SetType(Int8 i) { fType |= i; } + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +// sub type for object-in-volume detectors +class plObjectInVolumeDetector : public plCollisionDetector +{ +public: + class plCollisionBookKeepingInfo + { + friend plObjectInVolumeDetector; + public: + plCollisionBookKeepingInfo(plKey& hit) + { + hitter=hit; + enters=0; + exits=0; + } + ~plCollisionBookKeepingInfo() + { + hitter=nil; + } + protected: + plKey hitter; + int enters,exits; + bool fSubStepCurState; + }; +protected: + virtual void ITrigger(plKey hitter, bool entering, bool immediate=false); + //virtual void ISendSavedTriggerMsgs(); + virtual void IHandleEval(plEvalMsg* pEval); + bool fWaitingForEval; + + plActivatorMsg* fSavedActivatorMsg; + + typedef std::list bookKeepingList; + bookKeepingList fCollisionList; + typedef std::set ResidentSet; + ResidentSet fCurrentResidents; + +public: + + plObjectInVolumeDetector() + { + fWaitingForEval=false;fSavedActivatorMsg=nil; + + } + plObjectInVolumeDetector(Int8 i){fType = i;fWaitingForEval=false;fSavedActivatorMsg=nil;} + virtual ~plObjectInVolumeDetector(){;} + + virtual hsBool MsgReceive(plMessage* msg); + + CLASSNAME_REGISTER(plObjectInVolumeDetector); + GETINTERFACE_ANY(plObjectInVolumeDetector, plCollisionDetector); + + virtual void SetTarget(plSceneObject* so); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +class plObjectInVolumeAndFacingDetector : public plObjectInVolumeDetector +{ +protected: + hsScalar fFacingTolerance; + bool fNeedWalkingForward; + + bool fAvatarInVolume; + bool fTriggered; + + void ICheckForTrigger(); + +public: + plObjectInVolumeAndFacingDetector(); + virtual ~plObjectInVolumeAndFacingDetector(); + + virtual hsBool MsgReceive(plMessage* msg); + + CLASSNAME_REGISTER(plObjectInVolumeAndFacingDetector); + GETINTERFACE_ANY(plObjectInVolumeAndFacingDetector, plObjectInVolumeDetector); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + + // Export only + void SetFacingTolerance(int degrees); + void SetNeedWalkingForward(bool v) { fNeedWalkingForward = v; } +}; + +// sub-type for camera command regions + +class plCameraRegionDetector : public plObjectInVolumeDetector +{ +protected: + hsTArray fMessages; + bool fIsInside; + bool fSavingSendMsg; + bool fSavedMsgEnterFlag; + int fNumEvals; + int fLastEnterEval; + int fLastExitEval; + + virtual void ITrigger(plKey hitter, bool entering, bool immediate=false); + virtual void ISendSavedTriggerMsgs(); + virtual void IHandleEval(plEvalMsg* pEval); +public: + plCameraRegionDetector(){ fIsInside = false; fSavingSendMsg = false; } + ~plCameraRegionDetector(); + + virtual hsBool MsgReceive(plMessage* msg); + void AddMessage(plCameraMsg* pMsg) { fMessages.Append(pMsg); } + + CLASSNAME_REGISTER( plCameraRegionDetector ); + GETINTERFACE_ANY( plCameraRegionDetector, plCollisionDetector ); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + + +// sub-type for subworld regions + +class plSubworldRegionDetector : public plCollisionDetector +{ +protected: + plKey fSub; + hsBool fOnExit; + +public: + enum + { + kSubworld = 0, + }; + plSubworldRegionDetector() : fSub(nil), fOnExit(false){;} + ~plSubworldRegionDetector(); + + virtual hsBool MsgReceive(plMessage* msg); + void SetSubworldKey(plKey pKey) { fSub = pKey; } + void SetTriggerOnExit(hsBool b) { fOnExit = b; } + + CLASSNAME_REGISTER( plSubworldRegionDetector ); + GETINTERFACE_ANY( plSubworldRegionDetector, plCollisionDetector ); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + +// sub-type for panic link regions + +class plPanicLinkRegion : public plCollisionDetector +{ +public: + hsBool fPlayLinkOutAnim; + + plPanicLinkRegion() : fPlayLinkOutAnim(true) {;} + ~plPanicLinkRegion(){;} + + + virtual hsBool MsgReceive(plMessage* msg); + CLASSNAME_REGISTER( plPanicLinkRegion ); + GETINTERFACE_ANY( plPanicLinkRegion, plCollisionDetector ); + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); +}; + + +/** \Class plSimpleRegionSensor + A dead-simple interface for a collision region. Holds one message that it + sends to anyone who enters, and another message that it sends to anyone + who exits. + We may want to tie this into the plCollisionDetector so that it could be + integrated with responders. +*/ +class plSimpleRegionSensor : public plSingleModifier +{ +public: + plSimpleRegionSensor(); + plSimpleRegionSensor(plMessage *enterMsg, plMessage *exitMsg); + virtual ~plSimpleRegionSensor(); + + virtual hsBool MsgReceive(plMessage *msg); + CLASSNAME_REGISTER( plSimpleRegionSensor ); + GETINTERFACE_ANY( plSimpleRegionSensor, plSingleModifier); + + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *stream, hsResMgr *mgr); + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); +protected: + plMessage *fEnterMsg; + plMessage *fExitMsg; +}; + +// This class really just exists so that I can hunt for it specifically by index +// (and not accidentally get some other SimpleRegionSensor). +class plSwimDetector : public plSimpleRegionSensor +{ +public: + plSwimDetector() {} + plSwimDetector(plMessage *enterMsg, plMessage *exitMsg) : plSimpleRegionSensor(enterMsg, exitMsg) {} + virtual ~plSwimDetector() {} + + CLASSNAME_REGISTER( plSwimDetector ); + GETINTERFACE_ANY( plSwimDetector, plSimpleRegionSensor); + + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *stream, hsResMgr *mgr); + hsBool MsgReceive(plMessage *msg); +}; +class plRidingAnimatedPhysicalDetector: public plSimpleRegionSensor +{ +public: + plRidingAnimatedPhysicalDetector(){} + plRidingAnimatedPhysicalDetector(plMessage *enterMsg, plMessage *exitMsg) : plSimpleRegionSensor(enterMsg, exitMsg) {} + virtual ~plRidingAnimatedPhysicalDetector(){} + virtual hsBool MsgReceive(plMessage *msg); + CLASSNAME_REGISTER( plRidingAnimatedPhysicalDetector ); + GETINTERFACE_ANY( plRidingAnimatedPhysicalDetector, plSimpleRegionSensor); +}; +#endif plCollisionDetector_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plDetectorModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plDetectorModifier.h new file mode 100644 index 00000000..37e868ea --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plDetectorModifier.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDetectorModifier_inc +#define plDetectorModifier_inc + +#include "../pnModifier/plSingleModifier.h" +#include "../pnMessage/plObjRefMsg.h" +#include "hsStream.h" +#include "hsResMgr.h" + +class plDetectorModifier : public plSingleModifier +{ +protected: + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty){ return true; } + + hsTArray fReceivers; + plModifier* fRemoteMod; + plKey fProxyKey; + +public: + plDetectorModifier() : fRemoteMod(nil),fProxyKey(nil){;} + virtual ~plDetectorModifier(){;} + +// virtual hsBool MsgReceive(plMessage* msg) = 0; + + CLASSNAME_REGISTER( plDetectorModifier ); + GETINTERFACE_ANY( plDetectorModifier, plSingleModifier ); + void AddLogicObj(plKey pKey) { fReceivers.Append(pKey); } + void SetRemote(plModifier* p) { fRemoteMod = p; } + plModifier* RemoteMod() { return fRemoteMod; } + virtual void SetType(Int8 i) {;} + int GetNumReceivers() const { return fReceivers.Count(); } + plKey GetReceiver(int i) const { return fReceivers[i]; } + void SetProxyKey(const plKey &k) { fProxyKey = k; } + void Read(hsStream* stream, hsResMgr* mgr) + { + plSingleModifier::Read(stream, mgr); + int n = stream->ReadSwap32(); + fReceivers.Reset(); + for(int i = 0; i < n; i++ ) + { + fReceivers.Append(mgr->ReadKey(stream)); + } + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + fProxyKey = mgr->ReadKey(stream); + } + + void Write(hsStream* stream, hsResMgr* mgr) + { + plSingleModifier::Write(stream, mgr); + stream->WriteSwap32(fReceivers.GetCount()); + for( int i = 0; i < fReceivers.GetCount(); i++ ) + mgr->WriteKey(stream, fReceivers[i]); + + mgr->WriteKey(stream, fRemoteMod); + mgr->WriteKey(stream, fProxyKey); + } +}; + + +#endif plDetectorModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plEnvEffectDetector.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plEnvEffectDetector.cpp new file mode 100644 index 00000000..2524cdf0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plEnvEffectDetector.cpp @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plEnvEffectDetector.h" +#include "../plMessage/plCollideMsg.h" +#include "plgDispatch.h" +#include "../pnMessage/plEnvEffectMsg.h" + +/* +hsBool plEnvEffectDetector::MsgReceive(plMessage* msg) +{ + plCollideMsg* pCollMsg = plCollideMsg::ConvertNoRef(msg); + + if (pCollMsg) + { + for (int i = 0; i < fEffectMsg.Count(); i++) + { + fEffectMsg[i]->ClearReceivers(); + if(pCollMsg->fEntering) + { + fEffectMsg[i]->Enable( true ); + } else { + fEffectMsg[i]->Enable( false ); + } + fEffectMsg[i]->AddReceiver( pCollMsg->fOtherKey ); + hsRefCnt_SafeRef(fEffectMsg[i]); + plgDispatch::MsgSend( fEffectMsg[i] ); + } + } + return plDetectorModifier::MsgReceive(msg); +} + +void plEnvEffectDetector::Read(hsStream* stream, hsResMgr* mgr) +{ + plDetectorModifier::Read(stream, mgr); + int n = stream->ReadSwap32(); + fEffectMsg.SetCountAndZero(n); + for(int i = 0; i < n; i++ ) + { + plEnvEffectMsg* pMsg = plEnvEffectMsg::ConvertNoRef(mgr->ReadCreatable(stream)); + fEffectMsg[i] = pMsg; + } +} + +void plEnvEffectDetector::Write(hsStream* stream, hsResMgr* mgr) +{ + plDetectorModifier::Write(stream, mgr); + stream->WriteSwap32(fEffectMsg.GetCount()); + for(int i = 0; i < fEffectMsg.GetCount(); i++ ) + mgr->WriteCreatable( stream, fEffectMsg[i] ); + +} +*/ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plEnvEffectDetector.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plEnvEffectDetector.h new file mode 100644 index 00000000..f1eff2ed --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plEnvEffectDetector.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plEnvEffectDetector_inc +#define plEnvEffectDetector_inc + +#include "plDetectorModifier.h" + +class plEnvEffectMsg; +class plMessage; +class hsStream; +class hsResMgr; + +class plEnvEffectDetector : public plDetectorModifier +{ +protected: + +public: + plEnvEffectDetector(){;} + virtual ~plEnvEffectDetector(){;} + + virtual hsBool MsgReceive(plMessage* msg); + + CLASSNAME_REGISTER( plEnvEffectDetector ); + GETINTERFACE_ANY( plEnvEffectDetector, plDetectorModifier ); + + hsTArray fEffectMsg; + + void Read(hsStream* stream, hsResMgr* mgr); + void Write(hsStream* stream, hsResMgr* mgr); + +}; + +#endif plEnvEffectDetector_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalCreatable.h new file mode 100644 index 00000000..ec39e2ef --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalCreatable.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPhysicalCreatable_inc +#define plPhysicalCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "plDetectorModifier.h" + +REGISTER_NONCREATABLE( plDetectorModifier ); + +#include "plPickingDetector.h" + +REGISTER_CREATABLE( plPickingDetector ); + +#include "plCollisionDetector.h" + +REGISTER_CREATABLE( plCollisionDetector ); +REGISTER_CREATABLE( plCameraRegionDetector ); +REGISTER_CREATABLE( plObjectInVolumeDetector ); +REGISTER_CREATABLE( plObjectInVolumeAndFacingDetector ); +REGISTER_CREATABLE( plSubworldRegionDetector ); +REGISTER_CREATABLE( plPanicLinkRegion ); +REGISTER_CREATABLE( plSimpleRegionSensor ); +REGISTER_CREATABLE( plSwimDetector ); +REGISTER_CREATABLE( plRidingAnimatedPhysicalDetector ); + +#include "plPhysicalSDLModifier.h" +REGISTER_CREATABLE( plPhysicalSDLModifier ); + +#include "plPhysicalSndGroup.h" +REGISTER_CREATABLE(plPhysicalSndGroup); + +#endif // plPhysicalCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalProxy.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalProxy.cpp new file mode 100644 index 00000000..d8884b52 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalProxy.cpp @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPhysicalProxy.h" +#include "plPhysical.h" +#include "../plPhysX/plPXPhysicalControllerCore.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../pnMessage/plProxyDrawMsg.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayer.h" + +plPhysicalProxy::plPhysicalProxy() +: plProxyGen(hsColorRGBA().Set(0,0,0,1.f), hsColorRGBA().Set(1.f,0.8f,0.2f,1.f), 0.5f), + fOwner(nil) +{ +} + +plPhysicalProxy::plPhysicalProxy(const hsColorRGBA& amb, const hsColorRGBA& dif, hsScalar opac) +: plProxyGen(amb, dif, opac), + fOwner(nil), + fController(nil) +{ +} + +plPhysicalProxy::~plPhysicalProxy() +{ +} + +bool plPhysicalProxy::Init(plPhysical* liInfo) +{ + plProxyGen::Init(liInfo); + + fOwner = liInfo; + fProxyMsgType = plProxyDrawMsg::kPhysical; + + return fOwner != nil; +} + +bool plPhysicalProxy::Init(plPXPhysicalControllerCore* controller) +{ + if (controller) + if (controller->GetOwner()) + plProxyGen::Init(controller->GetOwner()->GetObjectPtr()); + + fController = controller; + fProxyMsgType = plProxyDrawMsg::kPhysical; + + return fController != nil; +} + +plKey plPhysicalProxy::IGetNode() const +{ + if (fOwner) + return fOwner->GetSceneNode(); + if (fController) + return fController->GetOwner(); + return nil; +} + +plDrawableSpans* plPhysicalProxy::ICreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + if (fOwner) + { + return fOwner->CreateProxy(mat, idx, addTo); + } + if (fController) + { + return fController->CreateProxy(mat,idx,addTo); + } + return nil; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalProxy.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalProxy.h new file mode 100644 index 00000000..44c74199 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalProxy.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPhysicalProxy_inc +#define plPhysicalProxy_inc + +#include "../plDrawable/plProxyGen.h" + +class plDrawableSpans; +class hsGMaterial; +class plPhysical; +class plPXPhysicalControllerCore; + +class plPhysicalProxy : public plProxyGen +{ +public: + plPhysicalProxy(); + plPhysicalProxy(const hsColorRGBA& amb, const hsColorRGBA& dif, hsScalar opac); + virtual ~plPhysicalProxy(); + + bool Init(plPhysical* phys); + bool Init(plPXPhysicalControllerCore* controller); + +protected: + plPhysical* fOwner; + plPXPhysicalControllerCore* fController; + + virtual plDrawableSpans* ICreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo=nil); + virtual plKey IGetNode() const; +}; + +#endif // plPhysicalProxy_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSDLModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSDLModifier.cpp new file mode 100644 index 00000000..463abfbc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSDLModifier.cpp @@ -0,0 +1,256 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPhysicalSDLModifier.h" + +#include "hsGeometry3.h" +#include "plPhysical.h" +#include "../plSDL/plSDL.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnNetCommon/plNetApp.h" +#include "hsQuat.h" +//#include "../plHavok1/plSimulationMgr.h" +#include "../plStatusLog/plStatusLog.h" + +// static vars +static const char* kStrLinear = "linear"; +static const char* kStrAngular = "angular"; +static const char* kStrPosition = "position"; +static const char* kStrOrientation = "orientation"; + +int plPhysicalSDLModifier::fLogLevel = 0; + +static void IGetVars(plStateDataRecord::SimpleVarsList& vars, + hsPoint3& pos, bool& isPosSet, + hsQuat& rot, bool& isRotSet, + hsVector3& linV, bool& isLinVSet, + hsVector3& angV, bool& isAngVSet); + +// +// get current state from physical +// fill out state data rec +// +void plPhysicalSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState) +{ + plPhysical* phys = IGetPhysical(); + + // get latest state + hsPoint3 curPos; + hsQuat curOrientation; + hsVector3 curLinear, curAngular; + phys->GetSyncState(curPos, curOrientation, curLinear, curAngular); + + // put it in sdl state record + dstState->FindVar(kStrPosition)->Set(&curPos.fX); + dstState->FindVar(kStrOrientation)->Set(&curOrientation.fX); + dstState->FindVar(kStrLinear)->Set(&curLinear.fX); + dstState->FindVar(kStrAngular)->Set(&curAngular.fX); + + if (fLogLevel > 1) + ILogState(dstState, false, "PUT", plStatusLog::kWhite); +} + +void plPhysicalSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState) +{ + plPhysical* phys = IGetPhysical(); + + // FIXME PHYSX + +// if(phys->GetBody()->isFixed()) +// { +// plSimulationMgr::Log("Received synch for fixed body %s", phys->GetKey()->GetName()); +// return; +// } +// else if (phys->GetProperty(plSimulationInterface::kPinned)) +// { +// // This is mainly intended for avatars. When pinning them (like in a multistage), +// // we don't want physical updates to sneak in due to network lag. If necessary, +// // this could be made a separate property on the physical, orthagonal to kPinned. +// return; +// } +// else + { + hsPoint3 pos; + bool isPosSet; + hsQuat rot; + bool isRotSet; + hsVector3 linV; + bool isLinVSet; + hsVector3 angV; + bool isAngVSet; + + plStateDataRecord::SimpleVarsList vars; + srcState->GetUsedVars(&vars); + IGetVars(vars, pos, isPosSet, rot, isRotSet, linV, isLinVSet, angV, isAngVSet); + + if (fLogLevel > 0) + ILogState(srcState, false, "RCV", plStatusLog::kGreen); + + phys->SetSyncState( + isPosSet ? &pos : nil, + isRotSet ? &rot : nil, + isLinVSet ? &linV : nil, + isAngVSet ? &angV : nil); + } +} + +void plPhysicalSDLModifier::ISentState(const plStateDataRecord* sentState) +{ + if (fLogLevel > 0) + { + ILogState(sentState, true, "SND", plStatusLog::kYellow); + +// plPhysical* phys = IGetPhysical(); +// if (!phys->GetBody()->isActive()) +// IGetLog()->AddLineF("Phys %s sent state because it deactivated", phys->GetKeyName()); + } +} + +static void IGetVars(plStateDataRecord::SimpleVarsList& vars, + hsPoint3& pos, bool& isPosSet, + hsQuat& rot, bool& isRotSet, + hsVector3& linV, bool& isLinVSet, + hsVector3& angV, bool& isAngVSet) +{ + isPosSet = false; + isRotSet = false; + isLinVSet = false; + isAngVSet = false; + + int num = vars.size(); + for (int i = 0; i < num; i++) + { + if (vars[i]->IsNamed(kStrPosition)) + { + vars[i]->Get(&pos.fX); + isPosSet= true; + } + else + if (vars[i]->IsNamed(kStrOrientation)) + { + vars[i]->Get(&rot.fX); + isRotSet = true; + } + else + if (vars[i]->IsNamed(kStrLinear)) + { + vars[i]->Get(&linV.fX); + isLinVSet = true; + } + else + if (vars[i]->IsNamed(kStrAngular)) + { + vars[i]->Get(&angV.fX); + isAngVSet = true; + } + else + if (vars[i]->IsNamed("subworld")) + { + // Unused + } + else + { + hsAssert(false, "Unknown var name"); + } + } +} + +void plPhysicalSDLModifier::ILogState(const plStateDataRecord* state, bool useDirty, const char* prefix, UInt32 color) +{ + hsPoint3 pos; + bool isPosSet; + hsQuat rot; + bool isRotSet; + hsVector3 linV; + bool isLinVSet; + hsVector3 angV; + bool isAngVSet; + + plStateDataRecord::SimpleVarsList vars; + if (useDirty) + state->GetDirtyVars(&vars); + else + state->GetUsedVars(&vars); + + IGetVars(vars, pos, isPosSet, rot, isRotSet, linV, isLinVSet, angV, isAngVSet); + + plPhysical* phys = IGetPhysical(); + + std::string log = xtl::format("%s: %s", phys->GetKeyName(), prefix); + + if (isPosSet) + log += xtl::format(" Pos=%.1f %.1f %.1f", pos.fX, pos.fY, pos.fZ); + else + log += " Pos=None"; + + if (isLinVSet) + log += xtl::format(" LinV=%.1f %.1f %.1f", linV.fX, linV.fY, linV.fZ); + else + log += " LinV=None"; + + if (isAngVSet) + log += xtl::format(" AngV=%.1f %.1f %.1f", angV.fX, angV.fY, angV.fZ); + else + log += " AngV=None"; + + if (isRotSet) + log += xtl::format(" Rot=%.1f %.1f %.1f %.1f", rot.fX, rot.fY, rot.fZ, rot.fW); + else + log += " Rot=None"; + + IGetLog()->AddLine(log.c_str(), color); +} + +plStatusLog* plPhysicalSDLModifier::IGetLog() +{ + static plStatusLog* gLog = nil; + if (!gLog) + { + gLog = plStatusLogMgr::GetInstance().CreateStatusLog(20, "PhysicsSDL.log", + plStatusLog::kFilledBackground | + plStatusLog::kTimestamp | + plStatusLog::kDeleteForMe | + plStatusLog::kAlignToTop); + } + + return gLog; +} + +plPhysical* plPhysicalSDLModifier::IGetPhysical() +{ + plPhysical* phys = nil; + + plSceneObject* sobj = GetTarget(); + if (sobj) + { + const plSimulationInterface* si = sobj->GetSimulationInterface(); + if (si) + phys = si->GetPhysical(); + } + + hsAssert(phys, "nil hkPhysical"); + return phys; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSDLModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSDLModifier.h new file mode 100644 index 00000000..d8e87cd2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSDLModifier.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPhysicalSDLModifier_inc +#define plPhysicalSDLModifier_inc + +#include "../plModifier/plSDLModifier.h" + +class plStateDataRecord; +class plStatusLog; +class plPhysical; + +// +// This modifier is responsible for sending and recving +// an object's physical state. +// +class plPhysicalSDLModifier : public plSDLModifier +{ +public: + CLASSNAME_REGISTER( plPhysicalSDLModifier ); + GETINTERFACE_ANY( plPhysicalSDLModifier, plSDLModifier); + + const char* GetSDLName() const { return kSDLPhysical; } + + // For the console + static void SetLogLevel(int level) { fLogLevel = level; } + +protected: + static int fLogLevel; + static plStatusLog* IGetLog(); + void ILogState(const plStateDataRecord* state, bool useDirty, const char* prefix, UInt32 color); + + plPhysical* IGetPhysical(); + virtual void IPutCurrentStateIn(plStateDataRecord* dstState); + virtual void ISetCurrentStateFrom(const plStateDataRecord* srcState); + virtual void ISentState(const plStateDataRecord* sentState); +}; + +#endif // plPhysicalSDLModifier_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSndGroup.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSndGroup.cpp new file mode 100644 index 00000000..af9af8a8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSndGroup.cpp @@ -0,0 +1,167 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plPhysicalSndGroup Class // +// Simplistic container class to store the matchup info for a given // +// physical sound group. Assigning one of these objects to a physical // +// specifies the sound group it's in as well as the sounds it should make // +// when colliding against objects of other sound groups. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsResMgr.h" +#include "plPhysicalSndGroup.h" +#include "../plAudio/plSound.h" +#include "../pnMessage/plRefMsg.h" +#include "../plMessage/plAnimCmdMsg.h" + + +plPhysicalSndGroup::plPhysicalSndGroup() : fPlayingSlideSound(false) +{ + fGroup = kNone; +} + +plPhysicalSndGroup::plPhysicalSndGroup( UInt32 grp ) : fPlayingSlideSound(false) +{ + fGroup = grp; +} + +plPhysicalSndGroup::~plPhysicalSndGroup() +{ +} + +bool plPhysicalSndGroup::HasSlideSound(UInt32 against) +{ + return against < fSlideSounds.GetCount(); +} + +bool plPhysicalSndGroup::HasImpactSound(UInt32 against) +{ + return against < fImpactSounds.GetCount(); +} + +hsBool plPhysicalSndGroup::MsgReceive( plMessage *pMsg ) +{ + return hsKeyedObject::MsgReceive( pMsg ); +} + +void plPhysicalSndGroup::Read( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Read( s, mgr ); + + s->ReadSwap( &fGroup ); + + UInt32 i, count = s->ReadSwap32(); + fImpactSounds.Reset(); + + for( i = 0; i < count; i++ ) + fImpactSounds.Append( mgr->ReadKey( s ) ); + + count = s->ReadSwap32(); + fSlideSounds.Reset(); + for( i = 0; i < count; i++ ) + fSlideSounds.Append( mgr->ReadKey( s ) ); + +} + +void plPhysicalSndGroup::Write( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Write( s, mgr ); + + s->WriteSwap( fGroup ); + + UInt32 i; + s->WriteSwap32( fImpactSounds.GetCount() ); + for( i = 0; i < fImpactSounds.GetCount(); i++ ) + mgr->WriteKey( s, fImpactSounds[ i ] ); + + s->WriteSwap32( fSlideSounds.GetCount() ); + for( i = 0; i < fSlideSounds.GetCount(); i++ ) + mgr->WriteKey( s, fSlideSounds[ i ] ); +} + +void plPhysicalSndGroup::AddImpactSound( UInt32 against, plKey receiver ) +{ + if( fImpactSounds.GetCount() <= against ) + { + fImpactSounds.Expand( against + 1 ); + fImpactSounds.SetCount( against + 1 ); + } + + fImpactSounds[ against ] = receiver; +} + +void plPhysicalSndGroup::AddSlideSound( UInt32 against, plKey receiver ) +{ + if( fSlideSounds.GetCount() <= against ) + { + fSlideSounds.Expand( against + 1 ); + fSlideSounds.SetCount( against + 1 ); + } + + fSlideSounds[ against ] = receiver; +} + +void plPhysicalSndGroup::PlaySlideSound(UInt32 against) +{ + if(against >= fSlideSounds.Count()) + return; + plAnimCmdMsg* animMsg = TRACKED_NEW plAnimCmdMsg; + animMsg->SetCmd(plAnimCmdMsg::kContinue); + animMsg->Send(fSlideSounds[against]); + fPlayingSlideSound = true; +} + +void plPhysicalSndGroup::StopSlideSound(UInt32 against) +{ + if(against >= fSlideSounds.Count()) + return; + plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg; + animMsg->SetCmd(plAnimCmdMsg::kStop); + animMsg->Send(fSlideSounds[against]); + fPlayingSlideSound = false; +} + +void plPhysicalSndGroup::PlayImpactSound(UInt32 against) +{ + if(against >= fImpactSounds.Count()) + return; + plAnimCmdMsg* animMsg = TRACKED_NEW plAnimCmdMsg; + animMsg->SetCmd(plAnimCmdMsg::kContinue); + animMsg->Send(fImpactSounds[against]); +} + +void plPhysicalSndGroup::SetSlideSoundVolume(UInt32 against, hsScalar volume) +{ + if(against >= fSlideSounds.Count()) + return; + plAnimCmdMsg* animMsg = TRACKED_NEW plAnimCmdMsg; + animMsg->SetCmd(plAnimCmdMsg::kSetSpeed); + animMsg->fSpeed = volume; + animMsg->Send(fSlideSounds[against]); + +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSndGroup.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSndGroup.h new file mode 100644 index 00000000..98ac4c0d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicalSndGroup.h @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plPhysicalSndGroup_h +#define _plPhysicalSndGroup_h + +////////////////////////////////////////////////////////////////////////////// +// // +// plPhysicalSndGroup Class // +// Simplistic container class to store the matchup info for a given // +// physical sound group. Assigning one of these objects to a physical // +// specifies the sound group it's in as well as the sounds it should make // +// when colliding against objects of other sound groups. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsTemplates.h" + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plUoid.h" + +class plSound; +class plPhysicalSndGroup : public hsKeyedObject +{ +public: + + // The group enums + enum SoundGroup + { + kNone = 0, + kMetal, + kGrass, + kWood + }; + + plPhysicalSndGroup(); + plPhysicalSndGroup( UInt32 grp ); + virtual ~plPhysicalSndGroup(); + + CLASSNAME_REGISTER( plPhysicalSndGroup ); + GETINTERFACE_ANY( plPhysicalSndGroup, hsKeyedObject ); + + // Our required virtual + virtual hsBool MsgReceive( plMessage *pMsg ); + + virtual void Read( hsStream *s, hsResMgr *mgr ); + virtual void Write( hsStream *s, hsResMgr *mgr ); + + void PlaySlideSound(UInt32 against); + void StopSlideSound(UInt32 against); + void PlayImpactSound(UInt32 against); + void SetSlideSoundVolume(UInt32 against, hsScalar volume); + bool HasSlideSound(UInt32 against); + bool HasImpactSound(UInt32 against); + + UInt32 GetGroup( void ) const { return fGroup; } + + // Export only + void AddImpactSound( UInt32 against, plKey receiver ); + void AddSlideSound( UInt32 against, plKey receiver ); + bool IsSliding() { return fPlayingSlideSound; } + +protected: + + enum Refs + { + kRefImpactSound, + kRefSlideSound + }; + + UInt32 fGroup; + bool fPlayingSlideSound; + + // Sound key arrays for, well, our sounds! + hsTArray fImpactSounds; + hsTArray fSlideSounds; +}; + + +#endif //_plPhysicalSndGroup_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicsSoundMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicsSoundMgr.cpp new file mode 100644 index 00000000..a5ca9a11 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicsSoundMgr.cpp @@ -0,0 +1,216 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "hsTimer.h" +#include "plPhysicsSoundMgr.h" +#include "plPhysicalSndGroup.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../plStatusLog/plStatusLog.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../FeatureLib/pfAudio/plRandomSoundMod.h" + +#define MIN_VOLUME 0.0001f + +void plPhysicsSoundMgr::AddContact(plPhysical* phys1, plPhysical* phys2, const hsPoint3& hitPoint, const hsVector3& hitNormal) +{ + CollidePair cp(phys1->GetKey(), phys2->GetKey(), hitPoint, hitNormal); + fCurCollisions.insert(cp); +} + +void plPhysicsSoundMgr::Update() +{ + // Get all the physicals that only in the new list (started colliding) + CollideSet startedColliding; + std::set_difference(fCurCollisions.begin(), fCurCollisions.end(), + fPrevCollisions.begin(), fPrevCollisions.end(), + std::inserter(startedColliding, startedColliding.begin())); + + for (CollideSet::iterator it = startedColliding.begin(); it != startedColliding.end(); it++) + IStartCollision(*it); + + for (CollideSet::iterator it = fPrevCollisions.begin(); it != fPrevCollisions.end(); it++) + { + CollideSet::iterator old = fCurCollisions.find(*it); + if (old != fCurCollisions.end()) + { + IUpdateCollision(*it); + } + else + { + IStopCollision(*it); + } + } + + fPrevCollisions = fCurCollisions; + fCurCollisions.clear(); +} + +void plPhysicsSoundMgr::IStartCollision(CollidePair& cp) +{ + hsVector3 v1, v2; + const hsScalar strengthThreshold = 20.0f; + + plPhysical* physicalA = cp.FirstPhysical(); + plPhysical* physicalB = cp.SecondPhysical(); + if (!physicalA || !physicalB) + return; + + plPhysicalSndGroup* sndA = physicalA->GetSoundGroup(); + plPhysicalSndGroup* sndB = physicalB->GetSoundGroup(); + + // If no impact sounds were specified in max don't do anything here. + if (!sndA->HasImpactSound(sndB->GetGroup()) && + !sndB->HasImpactSound(sndA->GetGroup())) + return; + + physicalA->GetLinearVelocitySim(v1); + physicalB->GetLinearVelocitySim(v2); + hsVector3 vel = v1 - v2; + hsScalar strength = vel.MagnitudeSquared(); + + if (strength >= strengthThreshold) + { + if (sndA->HasImpactSound(sndB->GetGroup())) + { + sndA->PlayImpactSound(sndB->GetGroup()); + } + else + { + sndB->PlayImpactSound(sndA->GetGroup()); + } + } +} + +void plPhysicsSoundMgr::IStopCollision(CollidePair& cp) +{ + plPhysical* physicalA = cp.FirstPhysical(); + plPhysical* physicalB = cp.SecondPhysical(); + if (physicalA && physicalB) + { + plPhysicalSndGroup* sndA = physicalA->GetSoundGroup(); + plPhysicalSndGroup* sndB = physicalB->GetSoundGroup(); + + if (sndA->HasSlideSound(sndB->GetGroup())) + { + if(sndA->IsSliding()) + { + sndA->StopSlideSound(sndB->GetGroup()); + } + } + if (sndB->HasSlideSound(sndA->GetGroup())) + { + if(sndB->IsSliding()) + { + sndB->StopSlideSound(sndA->GetGroup()); + } + } + } +} + +void plPhysicsSoundMgr::IUpdateCollision(CollidePair& cp) +{ + const hsScalar slideThreshhold = 0.f; + hsVector3 v1, v2; + plPhysical* physicalA = cp.FirstPhysical(); + plPhysical* physicalB = cp.SecondPhysical(); + if (!physicalA || !physicalB) + return; + + plPhysicalSndGroup* sndA = physicalA->GetSoundGroup(); + plPhysicalSndGroup* sndB = physicalB->GetSoundGroup(); + + physicalA->GetLinearVelocitySim(v1); + physicalB->GetLinearVelocitySim(v2); + hsVector3 vel = v1 - v2; + hsScalar strength = vel.MagnitudeSquared(); + + // scale strength to use as volume + strength /= 16*8; + if(strength < MIN_VOLUME) + strength = 0; + + if (sndA->HasSlideSound(sndB->GetGroup())) + IProcessSlide(sndA, sndB, strength); + else + IProcessSlide(sndB, sndA, strength); +} + +void plPhysicsSoundMgr::IProcessSlide(plPhysicalSndGroup* sndA, plPhysicalSndGroup* sndB, hsScalar strength) +{ + sndA->SetSlideSoundVolume(sndB->GetGroup(), strength); + + if(strength > MIN_VOLUME) + { + if(!sndA->IsSliding()) + { + sndA->PlaySlideSound(sndB->GetGroup()); + } + } +} + +////////////////////////////////////////////////////////////////////////// + +plPhysicsSoundMgr::CollidePair::CollidePair(const plKey& firstPhys, const plKey& secondPhys, const hsPoint3& point, const hsVector3& normal) +{ + // To simplify searching and sorting, all pairs are set up with the pointer value of the + // first element greater than the pointer value of the second. + if (firstPhys->GetObjectPtr() < secondPhys->GetObjectPtr()) + { + this->firstPhysKey = secondPhys; + this->secondPhysKey = firstPhys; + } + else + { + this->firstPhysKey = firstPhys; + this->secondPhysKey = secondPhys; + } + + this->point = point; + this->normalForce = normal; +} + +bool plPhysicsSoundMgr::CollidePair::operator<(const CollidePair& rhs) const +{ + return (firstPhysKey->GetObjectPtr() < rhs.firstPhysKey->GetObjectPtr() + || (rhs.firstPhysKey->GetObjectPtr() == firstPhysKey->GetObjectPtr() && secondPhysKey->GetObjectPtr() < rhs.secondPhysKey->GetObjectPtr())); +} + +bool plPhysicsSoundMgr::CollidePair::operator==(const CollidePair& rhs) const +{ + if (firstPhysKey == rhs.firstPhysKey && secondPhysKey == rhs.secondPhysKey) + return true; + return false; +} + +plPhysical* plPhysicsSoundMgr::CollidePair::FirstPhysical() const +{ + return firstPhysKey ? static_cast(firstPhysKey->GetObjectPtr()) : nil; +} + +plPhysical* plPhysicsSoundMgr::CollidePair::SecondPhysical() const +{ + return secondPhysKey ? static_cast(secondPhysKey->GetObjectPtr()) : nil; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicsSoundMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicsSoundMgr.h new file mode 100644 index 00000000..075d6a22 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPhysicsSoundMgr.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPhysicsSoundMgr_h_inc +#define plPhysicsSoundMgr_h_inc + +#include "hsStlUtils.h" +#include "plPhysical.h" +#include "hsGeometry3.h" + +class plPhysicsSoundMgr +{ +public: + void AddContact(plPhysical* phys1, plPhysical* phys2, const hsPoint3& hitPoint, const hsVector3& hitNormal); + void Update(); + +private: + enum EventType + { + kSlide, + kContact, + kEndSlide + }; + + enum EventStopType + { + kStopFromImpact = 0, + kStopFromEndSlide = 2 + }; + + class CollidePair + { + public: + plKey firstPhysKey; + plKey secondPhysKey; + hsPoint3 point; + hsVector3 normalForce; + + CollidePair(const plKey& firstPhys, const plKey& secondPhys, const hsPoint3& point, const hsVector3& normal); + bool operator<(const CollidePair& rhs) const; + bool operator==(const CollidePair& rhs) const; + plPhysical* FirstPhysical() const; + plPhysical* SecondPhysical() const; + }; + + void IStartCollision(CollidePair& cp); + void IStopCollision(CollidePair& cp); + void IUpdateCollision(CollidePair& cp); + void IProcessSlide(plPhysicalSndGroup* sndA, plPhysicalSndGroup* sndB, hsScalar strength); + + typedef std::set CollideSet; + CollideSet fPrevCollisions; + CollideSet fCurCollisions; +}; + +#endif // plPhysicsSoundMgr_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPickingDetector.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPickingDetector.cpp new file mode 100644 index 00000000..c42d383e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPickingDetector.cpp @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plPickingDetector.h" +#include "../plMessage/plActivatorMsg.h" +#include "../plMessage/plPickedMsg.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plFakeOutMsg.h" +#include "../pnNetCommon/plNetApp.h" +#include "plgDispatch.h" + + +hsBool plPickingDetector::MsgReceive(plMessage* msg) +{ + + plObjRefMsg* refMsg = plObjRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + if( refMsg->fType == plObjRefMsg::kModifier) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + plModifier* mod = plModifier::ConvertNoRef(refMsg->GetRef()); + SetRemote(mod); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + SetRemote(nil); + } + } + return true; + } + + plPickedMsg* pPMsg = plPickedMsg::ConvertNoRef(msg); + if (pPMsg) + { + for (int i = 0; i < fReceivers.Count(); i++) + { + plActivatorMsg* pMsg = TRACKED_NEW plActivatorMsg; + pMsg->AddReceiver( fReceivers[i] ); + if (pPMsg->fPicked) + pMsg->SetTriggerType( plActivatorMsg::kPickedTrigger ); + else + pMsg->SetTriggerType( plActivatorMsg::kUnPickedTrigger ); + // pass on the hit point + pMsg->fHitPoint = pPMsg->fHitPoint; + + if (fProxyKey) + pMsg->fPickedObj = fProxyKey; + else + pMsg->fPickedObj = GetTarget()->GetKey(); + + // assume that since this is something that was PICKED that it was done by the local player. + plKey locPlayerKey = plNetClientApp::GetInstance()->GetLocalPlayerKey(); + if (locPlayerKey) + pMsg->fHitterObj = locPlayerKey; + + pMsg->SetSender(GetKey()); + plgDispatch::MsgSend( pMsg ); + hsStatusMessageF("%s sending activate message to %s\n",GetKey()->GetName(), fReceivers[i]->GetName()); + } + } + + if (RemoteMod() && RemoteMod()->MsgReceive(msg)) + return true; + + return plDetectorModifier::MsgReceive(msg); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPickingDetector.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPickingDetector.h new file mode 100644 index 00000000..3a2073d2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plPickingDetector.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPickingDetector_inc +#define plPickingDetector_inc + +#include "plDetectorModifier.h" + +class plMessage; + +class plPickingDetector : public plDetectorModifier +{ +protected: + +public: + plPickingDetector(){;} + virtual ~plPickingDetector(){;} + + virtual hsBool MsgReceive(plMessage* msg); + + CLASSNAME_REGISTER( plPickingDetector ); + GETINTERFACE_ANY( plPickingDetector, plDetectorModifier ); + +}; +/* +class plClickDragDetector : public plDetectorModifier +{ +protected: + +public: + plPickingDetector(){;} + virtual ~plPickingDetector(){;} + + virtual hsBool MsgReceive(plMessage* msg); + + CLASSNAME_REGISTER( plPickingDetector ); + GETINTERFACE_ANY( plPickingDetector, plDetectorModifier ); + +}; +*/ + + +#endif plPickingDetector_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h new file mode 100644 index 00000000..218c3bb3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSimDefs.h @@ -0,0 +1,123 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLSIMDEFS_H +#define PLSIMDEFS_H +#pragma once + +namespace plSimDefs +{ + // Groups are used to determine what a physical collides with, and what a + // detector detects + enum Group + { + // A physical that blocks avatars and dynamics + kGroupStatic, + // Blocks only avatars + kGroupAvatarBlocker, + // Blocks only dynamics + kGroupDynamicBlocker, + // No physical should actually be set to this, since the avatar is + // controlled by a special kind of physical. However, you can use this + // for detecting. + kGroupAvatar, + // A physical that can be pushed around by the avatar or other dynamics + kGroupDynamic, + // A physical that doesn't block anything, but reports on any physical + // that enters it + kGroupDetector, + // Blocks nothing, for los checks only + kGroupLOSOnly, + //kExcludeRegion setting up so only blocks avatars and only when not in seek mode + kGroupExcludeRegion, + // Just for error checking + kGroupMax, + }; + + /** A taxonomy of action types. Crucial for doing things like making sure you don't + do things like attach duplicate actions. */ + enum ActionType + { + kUnknownAction = 0x01, // don't know the type (probably forgot to override GetType()) + kUnknownZAction = 0x02, // unknown type of z-order action + kAntiGravityAction = 0x03, // an action that counters gravity exactly + kUprightAction = 0x04, // an action that keeps an object upright by apply force + kPhysAnimAction = 0x05, // an action that parses keyframed animation into physical information + kConstraint = 0x06, // a general constraint. + kCallbackAction = 0x07, // an action that just hands us the physics "tick" + kPseudoPhysAction = 0x08, // replacement for the physAnim + kAntiGravAction = 0x09, // makes things float in the air + kBasicGroundAction = 0x0a, // for your basic walkAroundOnGround corrections + kHorizontalFreeze = 0x0b, // Let's you fall vertically, but otherwise keeps you in place (generic brains) + + kMaxAction = 0xffff // force 16-bit + }; + + /** Different types of line-of-sight requests. */ + enum LOSReqType + { + // these are MASKS -- keep powers of two + kLOS_IgnoreCameraRequest = 0x1, + kLOS_IgnoreUIRequest = 0x2, + kLOS_CameraAvoidObject = 0x4, + + kLOS_Max = 0xffff // force 16-bit + }; + + /** Different types of physics shapes. In most cases they dictate how we intepret + the raw vertices. In some cases (sphere) they explain why there aren't any vertices */ + enum Bounds { + kBoxBounds = 0x01, // bounding box + kSphereBounds, // bounding sphere + kHullBounds, // convex hull + kProxyBounds, // use alternate proxy geometry + kExplicitBounds, // use the primary geometry + kNumBounds, // the number of bounds types + + kBoundsMax = 0xff // force 8-bit + }; + + enum plLOSDB { + kLOSDBNone = 0x0000, + + kLOSDBUIBlockers = 0x0001, // things that block ui probes + kLOSDBUIItems = 0x0002, // ui items -- we check these first for a hit before checking blockers + kLOSDBCameraBlockers = 0x0004, // things that block camera probes + kLOSDBCustom = 0x0008, // special things. only the user knows + kLOSDBLocalAvatar = 0x0010, // yes, this is silly. it's transitional :) + kLOSDBShootableItems = 0x0020, // shootable items, like from the VaporMiner gun + + kLOSDBAvatarWalkable = 0x0040, // stuff the avatar is expected to walk on. Used to prevent sliding and allow + // jumping. All terrain automatically goes here. + + kLOSDBSwimRegion = 0x0080, + + kLOSDBMax = 0x0100, // MOVE THIS UP if you add new classes under it. we need it to iterate through the DBs + kLOSDBForce16 = 0xffff + }; +} + + +#endif PLSIMDEFS_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSittingModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSittingModifier.cpp new file mode 100644 index 00000000..04cab7d8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSittingModifier.cpp @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Obsolete \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSittingModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSittingModifier.h new file mode 100644 index 00000000..04cab7d8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPhysical/plSittingModifier.h @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Obsolete \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsFogControl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsFogControl.h new file mode 100644 index 00000000..20f11e11 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsFogControl.h @@ -0,0 +1,135 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsFogControl_inc +#define hsFogControl_inc + +//#include "hsSceneObject.h" +//#include "hsGEnviron.h" + +class hsSceneNode; +class hsG3DDevice; +class hsGView3; +class hsPortal; + +class hsFogControl : public plCreatable { +public: + enum { + kFogCtlPortal + }; +protected: + + plKey fNodeKey; + hsDynamicArray fFogNodes; +// hsGEnvironment::FogState fAvgFog; + + virtual hsScalar IGetStrength(hsSceneNode* node) = 0; + + void IPopNodes(); + void IPushNodes(); + + void IZeroAvgFog(); + void IAverageNodes(); +// void IAccumFog(hsGEnvironment* env, hsScalar wgt); + +public: + hsFogControl() {} + ~hsFogControl() {} + + CLASSNAME_REGISTER( hsFogControl ); + GETINTERFACE_ANY( hsFogControl, plCreatable ); + + virtual UInt32 GetType() = 0; + +// virtual hsGEnvironment* GetHomeEnv() = 0; + virtual void Init(hsSceneNode* node); + + virtual void Blend() = 0; + + virtual void Restore() = 0; + + virtual void Read(hsStream *stream, hsResMgr* mgr) = 0; + virtual void Write(hsStream *stream, hsResMgr* mgr) = 0; + +}; + +#if 0 // Move up to FeatureLevel +class hsNodeFogControl : public hsFogControl { +public: + hsNodeFogControl(); + ~hsNodeFogControl(); + + hsSceneNode* GetFogNode(int i); + hsSceneNode *GetHomeNode(); + virtual void Init(hsSceneNode* node); + + virtual hsGEnvironment* GetHomeEnv(); + + virtual void Blend(); + + virtual void Restore(); + + virtual void Read(hsStream *stream); + virtual void Write(hsStream *stream); +}; + +class hsPortalFogControl : public hsNodeFogControl { +protected: + enum { + kStatusNone = 0x0, + kStatusNodesSet = 0x1 + }; + + hsDynamicArray fPortals; + hsScalar fDefRadius; + + UInt32 fStatus; + + void IFindFogNodes(); + virtual hsScalar IGetStrength(hsSceneNode* node); +public: + hsPortalFogControl(); + + void SetDefaultRadius(hsScalar r) { fDefRadius = r; } + hsScalar GetDefaultRadius() { return fDefRadius; } + + hsPortal* GetPortal(int i); + + virtual UInt32 GetType() { return kFogCtlPortal; } + + virtual void Init(hsSceneNode* node); + virtual void Blend(); + + int GetNumPortalKeys() { return fPortals.GetCount(); } + void AddPortalKey(plKey key) { fPortals.Append(key); } + plKey GetPortalKey(int i) { return fPortals[i]; } + + virtual void Read(hsStream *stream); + virtual void Write(hsStream *stream); +}; +#endif // Move up to FeatureLevel + +#endif hsFogControl_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsG3DDeviceSelector.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsG3DDeviceSelector.cpp new file mode 100644 index 00000000..bee6d217 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsG3DDeviceSelector.cpp @@ -0,0 +1,1968 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +//#define DYNAHEADER_CREATE_STORAGE + +#include "hsTypes.h" + +#include + +#include "hsWindows.h" + +#include "hsG3DDeviceSelector.h" +#include "hsStream.h" +#include "hsUtils.h" +#include "plPipeline.h" + +#ifdef HS_OPEN_GL +#if HS_BUILD_FOR_WIN32 +#include "gls.h" +#include "glswgl.h" +#include "glext.h" +#endif +#endif + +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +hsG3DDeviceMode::hsG3DDeviceMode() +: fWidth(0), fHeight(0), + fDepth(0), + fFlags(kNone) +{ +} + +hsG3DDeviceMode::~hsG3DDeviceMode() +{ + Clear(); +} + +hsBool hsG3DDeviceMode::operator< (const hsG3DDeviceMode &mode) const +{ + // Color depth overrides everything else + if (fDepth < mode.GetColorDepth()) + return true; + // Only compare width and height if the color depth is the same + else if (fDepth == mode.GetColorDepth() ) + { + if( fWidth < mode.GetWidth() ) + return true; + else if( fWidth == mode.GetWidth() && fHeight < mode.GetHeight() ) + return true; + } + + return false; +} + +void hsG3DDeviceMode::Clear() +{ + fFlags = kNone; + fWidth = 0; + fHeight = 0; + fDepth = 0; + fZStencilDepths.Reset(); + fFSAATypes.Reset(); +} + +void hsG3DDeviceMode::Read( hsStream* s ) +{ + Clear(); + + fFlags = s->ReadSwap32(); + fWidth = s->ReadSwap32(); + fHeight = s->ReadSwap32(); + fDepth = s->ReadSwap32(); + + fZStencilDepths.Reset(); + UInt8 count= s->ReadByte(); + while( count-- ) + fZStencilDepths.Append( s->ReadSwap16() ); + + /// Version 9 + fFSAATypes.Reset(); + count = s->ReadByte(); + while( count-- ) + fFSAATypes.Append( s->ReadByte() ); + + fCanRenderToCubics = s->ReadBool(); +} + +void hsG3DDeviceMode::Write( hsStream* s ) const +{ + s->WriteSwap32(fFlags); + s->WriteSwap32(fWidth); + s->WriteSwap32(fHeight); + s->WriteSwap32(fDepth); + + UInt8 i, count = (UInt8)fZStencilDepths.GetCount(); + s->WriteByte( count ); + for( i = 0; i < count; i++ ) + s->WriteSwap16( fZStencilDepths[ i ] ); + + /// Version 9 + count = (UInt8)fFSAATypes.GetCount(); + s->WriteByte( count ); + for( i = 0; i < count; i++ ) + s->WriteByte( fFSAATypes[ i ] ); + + s->WriteBool( fCanRenderToCubics ); +} + +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// + +hsG3DDeviceRecord::hsG3DDeviceRecord() +: fFlags(kNone), + fG3DDeviceType(hsG3DDeviceSelector::kDevTypeUnknown), + fG3DDriverDesc(nil), fG3DDriverName(nil), fG3DDriverVersion(nil), fG3DDeviceDesc(nil), + fLayersAtOnce(0), fMemoryBytes(0), + fG3DHALorHEL(hsG3DDeviceSelector::kHHTypeUnknown), + fZBiasRating( 0 ), fRecordVersion( kCurrRecordVersion ), fLODBiasRating( 0 ), + fFogExpApproxStart( 0.0 ), fFogExp2ApproxStart( 0.0 ), fFogEndBias( 0.0 ), fMaxAnisotropicSamples( 1 ) +{ + SetFogKneeParams( kFogExp, 0, 0 ); + SetFogKneeParams( kFogExp2, 0, 0 ); +} + +hsG3DDeviceRecord::~hsG3DDeviceRecord() +{ + Clear(); +} + +hsG3DDeviceRecord::hsG3DDeviceRecord(const hsG3DDeviceRecord& src) +: fFlags(kNone), + fG3DDeviceType(hsG3DDeviceSelector::kDevTypeUnknown), + fG3DDriverDesc(nil), fG3DDriverName(nil), fG3DDriverVersion(nil), fG3DDeviceDesc(nil), + fG3DHALorHEL(hsG3DDeviceSelector::kHHTypeUnknown), + fZBiasRating( src.fZBiasRating ), fRecordVersion( kCurrRecordVersion ), fLODBiasRating( 0 ), + fFogExpApproxStart( src.fFogExpApproxStart ), fFogExp2ApproxStart( src.fFogExp2ApproxStart ), + fFogEndBias( src.fFogEndBias ), fMaxAnisotropicSamples( src.fMaxAnisotropicSamples ) +{ + *this = src; +} + +hsG3DDeviceRecord& hsG3DDeviceRecord::operator=(const hsG3DDeviceRecord& src) +{ + fFlags = src.fFlags; + + SetG3DDeviceType(src.GetG3DDeviceType()); + SetG3DHALorHEL(src.GetG3DHALorHEL()); + + SetDriverDesc(src.GetDriverDesc()); + SetDriverName(src.GetDriverName()); + SetDriverVersion(src.GetDriverVersion()); + SetDeviceDesc(src.GetDeviceDesc()); + + fCaps = src.fCaps; + fLayersAtOnce = src.fLayersAtOnce; + fMemoryBytes = src.fMemoryBytes; + fZBiasRating = src.fZBiasRating; + fLODBiasRating = src.fLODBiasRating; + fFogExpApproxStart = src.fFogExpApproxStart; + fFogExp2ApproxStart = src.fFogExp2ApproxStart; + fFogEndBias = src.fFogEndBias; + + fModes.SetCount(src.fModes.GetCount()); + int i; + for( i = 0; i < fModes.GetCount(); i++ ) + fModes[i] = src.fModes[i]; + + fFogKnees[ 0 ] = src.fFogKnees[ 0 ]; + fFogKnees[ 1 ] = src.fFogKnees[ 1 ]; + fFogKneeVals[ 0 ] = src.fFogKneeVals[ 0 ]; + fFogKneeVals[ 1 ] = src.fFogKneeVals[ 1 ]; + + fAASetting = src.fAASetting; + + fMaxAnisotropicSamples = src.fMaxAnisotropicSamples; + + return *this; +} + +void hsG3DDeviceRecord::SetDriverDesc( const char *s ) +{ + delete [] fG3DDriverDesc; + fG3DDriverDesc = s ? hsStrcpy(s) : nil; +} + +void hsG3DDeviceRecord::SetDriverName( const char *s ) +{ + delete [] fG3DDriverName; + fG3DDriverName = s ? hsStrcpy(s) : nil; +} + +void hsG3DDeviceRecord::SetDriverVersion( const char *s ) +{ + delete [] fG3DDriverVersion; + fG3DDriverVersion = s ? hsStrcpy(s) : nil; +} + +void hsG3DDeviceRecord::SetDeviceDesc( const char *s ) +{ + delete [] fG3DDeviceDesc; + fG3DDeviceDesc = s ? hsStrcpy(s) : nil; +} + +const char* hsG3DDeviceRecord::GetG3DDeviceTypeName() const +{ + static char* deviceNames[hsG3DDeviceSelector::kNumDevTypes] = { + "Unknown", + "Glide", + "Direct3D", + "OpenGL" + }; + + UInt32 devType = GetG3DDeviceType(); + if( devType > hsG3DDeviceSelector::kNumDevTypes ) + devType = hsG3DDeviceSelector::kDevTypeUnknown; + + return deviceNames[devType]; +} + +void hsG3DDeviceRecord::RemoveDiscarded() +{ + int i; + for( i = 0; i < fModes.GetCount(); ) + { + if( fModes[i].GetDiscarded() ) + { + fModes[i].Clear(); + fModes.Remove(i); + } + else + i++; + } + if( !fModes.GetCount() ) + SetDiscarded(true); +} + +void hsG3DDeviceRecord::ClearModes() +{ + int i; + for( i = 0; i < fModes.GetCount(); i++ ) + fModes[i].Clear(); + fModes.Reset(); +} + +void hsG3DDeviceRecord::Clear() +{ + fFlags = kNone; + + delete [] fG3DDriverDesc; + fG3DDriverDesc = nil; + + delete [] fG3DDriverName; + fG3DDriverName = nil; + + delete [] fG3DDriverVersion; + fG3DDriverVersion = nil; + + delete [] fG3DDeviceDesc; + fG3DDeviceDesc = nil; + + fCaps.Clear(); + fLayersAtOnce = 0; + + int i; + for( i = 0; i < fModes.GetCount(); i++ ) + fModes[i].Clear(); + fModes.Reset(); + + fZBiasRating = 0; + fLODBiasRating = 0; + fFogExpApproxStart = 0; + fFogExp2ApproxStart = 0; + fFogEndBias = 0; + + SetFogKneeParams( kFogExp, 0, 0 ); + SetFogKneeParams( kFogExp2, 0, 0 ); + + fAASetting = 0; + fMaxAnisotropicSamples = 1; +} + +//// Read ///////////////////////////////////////////////////////////////////// +// 9.6.2000 mcn - Updated to reflect version 2 format +// 9.8.2000 mcn - (temporary?) set to invalid on old (<2) versions + +void hsG3DDeviceRecord::Read(hsStream* s) +{ + Clear(); + + /// Read version + fRecordVersion = s->ReadSwap32(); + hsAssert( fRecordVersion <= kCurrRecordVersion, "Invalid version number in hsG3DDeviceRecord::Read()" ); + if( fRecordVersion == kCurrRecordVersion ) + { + fFlags = s->ReadSwap32(); + } + else + { + SetInvalid(); + return; +// fFlags = fRecordVersion; +// fRecordVersion = 1; +// hsStatusMessage( "WARNING: Old version of hsG3DDeviceRecord found. Attempting to read." ); + } + + /// Now read everything else in as normal + fG3DDeviceType = s->ReadSwap32(); + + int len; + + len = s->ReadSwap32(); + fG3DDriverDesc = TRACKED_NEW char[len + 1]; + s->Read(len, fG3DDriverDesc); + fG3DDriverDesc[len] = 0; + + len = s->ReadSwap32(); + fG3DDriverName = TRACKED_NEW char[len + 1]; + s->Read(len, fG3DDriverName); + fG3DDriverName[len] = 0; + + len = s->ReadSwap32(); + fG3DDriverVersion = TRACKED_NEW char[len + 1]; + s->Read(len, fG3DDriverVersion); + fG3DDriverVersion[len] = 0; + + len = s->ReadSwap32(); + fG3DDeviceDesc = TRACKED_NEW char[len + 1]; + s->Read(len, fG3DDeviceDesc); + fG3DDeviceDesc[len] = 0; + + + fCaps.Read(s); + fLayersAtOnce = s->ReadSwap32(); + fMemoryBytes = s->ReadSwap32(); + + len = s->ReadSwap32(); + fModes.SetCount(len); + int i; + for( i = 0; i < len; i++ ) + fModes[i].Read( s ); + + /// Version 3 stuff + fZBiasRating = s->ReadSwapFloat(); + fLODBiasRating = s->ReadSwapFloat(); + fFogExpApproxStart = s->ReadSwapFloat(); + fFogExp2ApproxStart = s->ReadSwapFloat(); + fFogEndBias = s->ReadSwapFloat(); + + /// Version 7 stuff + float knee, kneeVal; + knee = s->ReadSwapFloat(); kneeVal = s->ReadSwapFloat(); + SetFogKneeParams( kFogExp, knee, kneeVal ); + knee = s->ReadSwapFloat(); kneeVal = s->ReadSwapFloat(); + SetFogKneeParams( kFogExp2, knee, kneeVal ); + + /// Version 9 stuff + fAASetting = s->ReadByte(); + + /// Version A stuff + fMaxAnisotropicSamples = s->ReadByte(); + + /// Reset record version now + fRecordVersion = kCurrRecordVersion; +} + +void hsG3DDeviceRecord::Write(hsStream* s) const +{ + s->WriteSwap32( fRecordVersion ); + + s->WriteSwap32(fFlags); + + s->WriteSwap32(fG3DDeviceType); + + int len; + + len = hsStrlen(fG3DDriverDesc); + s->WriteSwap32(len); + s->Write(len, fG3DDriverDesc); + + len = hsStrlen(fG3DDriverName); + s->WriteSwap32(len); + s->Write(len, fG3DDriverName); + + len = hsStrlen(fG3DDriverVersion); + s->WriteSwap32(len); + s->Write(len, fG3DDriverVersion); + + len = hsStrlen(fG3DDeviceDesc); + s->WriteSwap32(len); + s->Write(len, fG3DDeviceDesc); + + fCaps.Write(s); + s->WriteSwap32(fLayersAtOnce); + s->WriteSwap32(fMemoryBytes); + + s->WriteSwap32(fModes.GetCount()); + int i; + for( i = 0; i < fModes.GetCount(); i++ ) + fModes[i].Write( s ); + + /// Version 3 data + s->WriteSwapFloat( fZBiasRating ); + s->WriteSwapFloat( fLODBiasRating ); + s->WriteSwapFloat( fFogExpApproxStart ); + s->WriteSwapFloat( fFogExp2ApproxStart ); + s->WriteSwapFloat( fFogEndBias ); + + /// Version 7 data + s->WriteSwapFloat( fFogKnees[ kFogExp ] ); + s->WriteSwapFloat( fFogKneeVals[ kFogExp ] ); + s->WriteSwapFloat( fFogKnees[ kFogExp2 ] ); + s->WriteSwapFloat( fFogKneeVals[ kFogExp2 ] ); + + /// Version 9 data + s->WriteByte( fAASetting ); + + /// Version A stuff + s->WriteByte( fMaxAnisotropicSamples ); +} + +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// + +hsG3DDeviceModeRecord::hsG3DDeviceModeRecord(const hsG3DDeviceRecord& devRec, const hsG3DDeviceMode& devMode) +: fDevice(devRec), fMode(devMode) +{ +} + +hsG3DDeviceModeRecord::hsG3DDeviceModeRecord() +{ +} + +hsG3DDeviceModeRecord::~hsG3DDeviceModeRecord() +{ +} + +hsG3DDeviceModeRecord::hsG3DDeviceModeRecord(const hsG3DDeviceModeRecord& src) +{ + *this = src; +} + +hsG3DDeviceModeRecord& hsG3DDeviceModeRecord::operator=(const hsG3DDeviceModeRecord& src) +{ + fDevice = src.fDevice; + fMode = src.fMode; + return *this; +} +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// + +hsG3DDeviceSelector::hsG3DDeviceSelector() +{ +} + +hsG3DDeviceSelector::~hsG3DDeviceSelector() +{ + Clear(); +} + +void hsG3DDeviceSelector::RemoveDiscarded() +{ + int i; + for( i = 0; i < fRecords.GetCount(); ) + { + fRecords[i].RemoveDiscarded(); + + if( fRecords[i].GetDiscarded() ) + { + fRecords[i].Clear(); + fRecords.Remove(i); + } + else + i++; + } +} + +void hsG3DDeviceSelector::Clear() +{ + int i; + for( i = 0; i < fRecords.GetCount(); i++ ) + fRecords[i].Clear(); + fRecords.Reset(); +} + +void hsG3DDeviceSelector::RemoveUnusableDevModes(hsBool bTough) +{ + plDemoDebugFile::Write( "Removing unusable devices and modes..." ); + for (int i = 0; i < fRecords.GetCount(); i++) + { + // + // Remove modes + // + hsTArray& modes = fRecords[i].GetModes(); + for (int j = 0; j < modes.GetCount(); j++) + { + // Remove windowed modes + if ((modes[j].GetWidth() == 0) && + (modes[j].GetHeight() == 0) && + (modes[j].GetColorDepth() == 0)) + { + plDemoDebugFile::Write( " Removing windowed mode on ", (char *)fRecords[ i ].GetDriverDesc() ); + modes[j].SetDiscarded(true); + } + // If tough, remove modes less than 640x480 + else if (bTough && ((modes[j].GetWidth() < 640) || (modes[j].GetHeight() < 480))) + { + plDemoDebugFile::Write( " Removing mode < 640x480 on ", (char *)fRecords[ i ].GetDriverDesc() ); + modes[j].SetDiscarded(true); + } + else + { + char str[ 256 ]; + sprintf( str, " Keeping mode (%dx%d) on device %s", modes[j].GetWidth(), modes[j].GetHeight(), fRecords[ i ].GetDriverDesc() ); + plDemoDebugFile::Write( str ); + } + } + + // + // Remove devices + // + if (fRecords[i].GetG3DDeviceType() == hsG3DDeviceSelector::kDevTypeUnknown) + { + plDemoDebugFile::Write( " Removing unknown device. Description", (char *)fRecords[ i ].GetDriverDesc() ); + fRecords[i].SetDiscarded(true); + } + else if( fRecords[i].GetG3DDeviceType() == hsG3DDeviceSelector::kDevTypeDirect3D || + fRecords[i].GetG3DDeviceType() == hsG3DDeviceSelector::kDevTypeDirect3DTnL ) + { + UInt32 totalMem; + char devDesc[ 256 ]; + + + // For our 3dfx test later + strncpy( devDesc, fRecords[i].GetDriverDesc(), sizeof( devDesc ) - 1 ); + hsStrLower( devDesc ); + + // Remove software Direct3D devices + if ((fRecords[i].GetG3DHALorHEL() != hsG3DDeviceSelector::kHHD3DHALDev) && + (fRecords[i].GetG3DHALorHEL() != hsG3DDeviceSelector::kHHD3DTnLHalDev) && + (fRecords[i].GetG3DHALorHEL() != hsG3DDeviceSelector::kHHD3D3dfxDev) && + (fRecords[i].GetG3DHALorHEL() != hsG3DDeviceSelector::kHHD3D3dfxVoodoo5Dev) +#ifdef HS_ALLOW_D3D_REF_DRIVER + && (fRecords[i].GetG3DHALorHEL() != hsG3DDeviceSelector::kHHD3DRefDev) +#endif + ) + { + plDemoDebugFile::Write( " Removing software Direct3D device. Description", (char *)fRecords[ i ].GetDriverDesc() ); + fRecords[i].SetDiscarded(true); + } + // Remove 3Dfx Direct3D devices, take 2 + // 10.13.2000 mcn - Now we do it even when we're wimpy + // 10.25.2000 mcn - Think again. + // 11.3.2000 mcn - Shesh, is this EVER going to be stable?? + else if( bTough && fRecords[i].GetG3DHALorHEL() == hsG3DDeviceSelector::kHHD3D3dfxDev ) +// else if( bTough && ( strstr( devDesc, "3dfx" ) || strstr( devDesc, "voodoo" ) ) ) + { + plDemoDebugFile::Write( " Removing 3Dfx non-Voodoo5 Direct3D device (We only support Glide on 3Dfx). Description", (char *)fRecords[ i ].GetDriverDesc() ); + fRecords[i].SetDiscarded(true); + } + // Remove Direct3D devices with less than 11 megs of RAM + else if (bTough && ( totalMem = IAdjustDirectXMemory( fRecords[i].GetMemoryBytes() ) ) < 11*1024*1024 ) + { + char str[ 256 ]; + sprintf( str, " Removing Direct3D device with < 11MB RAM. Device RAM (in kB): %d (Description: %s)", + totalMem / 1024, fRecords[ i ].GetDriverDesc() ); + plDemoDebugFile::Write( str ); + fRecords[i].SetDiscarded(true); + } + else + { + if( fRecords[i].GetG3DDeviceType() == hsG3DDeviceSelector::kDevTypeDirect3DTnL ) + plDemoDebugFile::Write( " Keeping DX8 Direct3D device", (char *)fRecords[ i ].GetDriverDesc() ); + else + plDemoDebugFile::Write( " Keeping Direct3D device", (char *)fRecords[ i ].GetDriverDesc() ); + } + } + else + plDemoDebugFile::Write( " Keeping device", (char *)fRecords[ i ].GetDriverDesc() ); + } + + RemoveDiscarded(); +} + +//// IAdjustDirectXMemory ///////////////////////////////////////////////////// +// Adjusts the number coming off of the DirectX caps for "total video memory" +// to be more reflective of what is really on the board. According to +// Microsoft, the best way to do this is to add in the memory necessary for +// the entire desktop. Okay, whatever... + +UInt32 hsG3DDeviceSelector::IAdjustDirectXMemory( UInt32 cardMem ) +{ +#if HS_BUILD_FOR_WIN32 + HDC deskDC; + int width, height, bpp, total; + + deskDC = GetDC( nil ); + width = GetDeviceCaps( deskDC, HORZRES ); + height = GetDeviceCaps( deskDC, VERTRES ); + bpp = GetDeviceCaps( deskDC, BITSPIXEL ); + + total = width * height; + if( bpp > 8 ) + total *= ( bpp >> 3 ); + + return cardMem + total; +#else + return cardMem; +#endif +} + +hsBool hsG3DDeviceSelector::Init( void ) +{ + // See if we're all capable of initing + if( !IInitDirect3D() ) + { + return false; + } + + return true; +} + +void hsG3DDeviceSelector::Enumerate(hsWinRef winRef) +{ + Clear(); + +#ifdef HS_BUILD_FOR_WIN32 + /// 9.6.2000 - Create the class to use as our temporary window class + WNDCLASS tempClass; + + strcpy( fTempWinClass, "DSTestClass" ); + memset( &tempClass, 0, sizeof( tempClass ) ); + tempClass.lpfnWndProc = DefWindowProc; + tempClass.hInstance = GetModuleHandle( nil ); + tempClass.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH ); + tempClass.lpszClassName = fTempWinClass; + UInt16 ret = RegisterClass(&tempClass); + hsAssert(ret, "Cannot create temporary window class to test for device modes" ); +#endif + + /// Now try our devices +#ifdef HS_SELECT_DX7 + ITryDirect3D(winRef); +#endif // HS_SELECT_DX7 + + ITryDirect3DTnL(winRef); + +// ITryOpenGL(winRef); + +#ifdef HS_BUILD_FOR_WIN32 + /// Get rid of the class + UnregisterClass( fTempWinClass, GetModuleHandle( nil ) ); +#endif +} + +hsBool hsG3DDeviceSelector::GetDefault (hsG3DDeviceModeRecord *dmr) +{ + Int32 iTnL, iD3D, iOpenGL, device, mode, i; + device = iTnL = iD3D = iOpenGL = mode = -1; + + if (device == -1) + { + // Get an index for any 3D devices + for (i = 0; i < fRecords.GetCount(); i++) + { + switch (fRecords[i].GetG3DDeviceType()) + { + case kDevTypeDirect3D: + case kDevTypeDirect3DTnL: + if (fRecords[i].GetG3DHALorHEL() == kHHD3DTnLHalDev) + { + if (iTnL == -1 +#ifndef PLASMA_EXTERNAL_RELEASE + || plPipeline::fInitialPipeParams.ForceSecondMonitor +#endif // PLASMA_EXTERNAL_RELEASE + ) + { + iTnL = i; + } + } + else if (fRecords[i].GetG3DHALorHEL() == kHHD3DHALDev || fRecords[i].GetG3DHALorHEL() == kHHD3D3dfxVoodoo5Dev ) + { + if (iD3D == -1 +#ifndef PLASMA_EXTERNAL_RELEASE + || plPipeline::fInitialPipeParams.ForceSecondMonitor +#endif // PLASMA_EXTERNAL_RELEASE + ) + { + iD3D = i; + } + } + break; + + case kDevTypeOpenGL: + if (iOpenGL == -1 +#ifndef PLASMA_EXTERNAL_RELEASE + || plPipeline::fInitialPipeParams.ForceSecondMonitor +#endif // PLASMA_EXTERNAL_RELEASE + ) + { + iOpenGL = i; + } + break; + } + } + + // Pick a default device (Priority D3D T&L, D3D HAL, OpenGL) + if (iTnL != -1) + device = iTnL; + else if (iD3D != -1) + device = iD3D; + else if (iOpenGL != -1) + device = iOpenGL; + else + return false; + } + + // + // Try and find the default mode + // + hsTArray& modes = fRecords[device].GetModes(); + + // If there are no modes (for some insane reason), fail + if (modes.GetCount() == 0) + return false; + + for (i = 0; i < modes.GetCount(); i++) + { + if ((modes[i].GetWidth() == kDefaultWidth) && + (modes[i].GetHeight() == kDefaultHeight) && + (modes[i].GetNumZStencilDepths() > 0)) + { + // Don't be too picky about the depth, use what's available if the + // default isn't found. + if (mode == -1 || modes[mode].GetColorDepth() != kDefaultDepth) + mode = i; + } + } + // Default mode not found, what kind of card is this?! + // Regardless, just use the first mode since this isn't a fatal error. + if (mode == -1) + mode = 0; + + *dmr = hsG3DDeviceModeRecord(fRecords[device], modes[mode]); + + return true; +} + +//// ITryOpenGL /////////////////////////////////////////////////////////////// +// Updated 8.24.2000 mcn to (hopefully) detect OpenGL drivers. + +void hsG3DDeviceSelector::ITryOpenGL(hsWinRef winRef) +{ +#ifdef HS_OPEN_GL +#if HS_BUILD_FOR_WIN32 + int i, numDrivers; + int modeRes[ 6 ][ 3 ] = { { 640, 480, 16 }, { 800, 600, 16 }, { 1024, 768, 16 }, + { 1152, 864, 16 }, { 1280, 1024, 16 }, { 1600, 1200, 16 } }; + gls_driver_info driverInfo; + hsG3DDeviceRecord devRec; + hsG3DDeviceMode devMode; + char str[ 128 ]; + HDC hDC; + HGLRC tempContext; + HWND testWindow = nil, testWindow2 = nil; + + WINDOWPLACEMENT oldWindowPlace; + + + /// Save old window position + oldWindowPlace.length = sizeof( oldWindowPlace ); + GetWindowPlacement( (HWND)winRef, &oldWindowPlace ); + + /// Use the GLS API to get us a list of all OpenGL drivers available on + /// this system and their capabilities + numDrivers = glsGetNumberOfDrivers(); + for( i = 0; i < numDrivers; i++ ) + { + /// Get main driver info + glsGetDriverInfo( i, &driverInfo ); + + devRec.SetG3DDeviceType( kDevTypeOpenGL ); + + devRec.SetDriverDesc( driverInfo.aDriverDescription ); + devRec.SetDriverName( driverInfo.GLDriver.aDriverFilePath ); + + sprintf( str, "%d.%d", driverInfo.GLDriver.DriverFileVersionHigh, + driverInfo.GLDriver.DriverFileVersionLow ); + devRec.SetDriverVersion( str ); + + devRec.SetCap( kCapsMipmap ); + devRec.SetCap( kCapsMipmap ); + devRec.SetCap( kCapsPerspective ); + + if( driverInfo.DriverFlags & GLS_FLAGS_FULLSCREEN_ONLY ) + devRec.SetCap( kCapsNoWindow ); + if( !( driverInfo.DriverFlags & GLS_FLAGS_SOFTWARE_ONLY ) ) + devRec.SetCap( kCapsHardware ); + devRec.SetCap( kCapsDoesSmallTextures ); + + /// We have a problem here--OpenGL has no way of detecting the rest of + /// the information we want, so we'll have to guess/kludge on most of it. + + glsLoadDriver( i ); + sprintf( str, "ITryOpenGL(): FOUND OpenGL Driver: %s (%s)\n", driverInfo.aDriverDescription, + driverInfo.GLDriver.aDriverFilePath ); + hsStatusMessage( str ); + + /// (and of COURSE we have to open a bloody rendering context for + /// glGetString to work...whose bright idea was THAT?) + testWindow = CreateWindowEx( WS_EX_APPWINDOW, fTempWinClass, "OpenGL Screen Test Window", + WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE, + 0, 0, 640, 480, nil, nil, GetModuleHandle( nil ), 0 ); + hDC = GetDC( testWindow ); + tempContext = (HGLRC)ICreateTempOpenGLContext( hDC, + driverInfo.DriverFlags & GLS_FLAGS_FULLSCREEN_ONLY ); + if( tempContext != nil ) + { + wglMakeCurrent( hDC, tempContext ); + + IGetExtOpenGLInfo( devRec ); + + /// Reset everything back now + wglMakeCurrent( nil, nil ); + wglDeleteContext( tempContext ); + ReleaseDC( testWindow, hDC ); + } + + /// Resize window to hide what we're about to do + SetWindowPos( testWindow, nil, 0, 0, 1600, 1200, SWP_NOZORDER | SWP_NOMOVE ); + + /// Check for windowed screen mode (which SOMEBODY decided to test for + /// bitdepth of 0 instead of the caps flag we're setting....hmmmm wasn't me....) + if( !( driverInfo.DriverFlags & GLS_FLAGS_FULLSCREEN_ONLY ) ) + { + devMode.Clear(); + devMode.SetWidth( 0 ); + devMode.SetHeight( 0 ); + devMode.SetColorDepth( 0 ); + devRec.GetModes().Append( devMode ); + } + + /// Go get the screen modes + IGetOpenGLModes( devRec, driverInfo.aDriverDescription ); + + /// Get rid of the window now + DestroyWindow( testWindow ); + + /// Unload this driver now + glsUnloadDriver(); + } + + /// Restore old window position + SetWindowPlacement( (HWND)winRef, &oldWindowPlace ); +#endif +#endif +} + +//// IGetOpenGLModes ////////////////////////////////////////////////////////// +// Scans through all the possible imaginable combinations of screen modes, +// pixel formats whatnot and adds the final, culled-down list of graphics +// modes to the given device record. + +void hsG3DDeviceSelector::IGetOpenGLModes( hsG3DDeviceRecord &devRec, char *driverName ) +{ +#ifdef HS_OPEN_GL +#if HS_BUILD_FOR_WIN32 + int j; + int maxMode, mode; + int modeRes[ 6 ][ 3 ] = { { 640, 480, 16 }, { 800, 600, 16 }, { 1024, 768, 16 }, + { 1152, 864, 16 }, { 1280, 1024, 16 }, { 1600, 1200, 16 } }; + int bitDepths[ 3 ] = { 32, 24, 16 }, bitDepth; + hsG3DDeviceMode devMode; + DEVMODE modeInfo; + HWND testWindow = nil, testWindow2 = nil; + char str[ 128 ]; + + + /// Find the maximum resolution that we can support on this monitor--then + /// we'll start there and work down until we find a mode supported by OpenGL + modeInfo.dmSize = sizeof( modeInfo ); + maxMode = -1; + for( j = 0; EnumDisplaySettings( nil, j, &modeInfo ) != 0; j++ ) + { + for( mode = 0; mode < sizeof( modeRes ) / sizeof( modeRes[ 0 ] ); mode++ ) + { + if( modeRes[ mode ][ 0 ] == modeInfo.dmPelsWidth && + modeRes[ mode ][ 1 ] == modeInfo.dmPelsHeight ) + { + if( modeInfo.dmBitsPerPel > modeRes[ mode ][ 2 ] ) + modeRes[ mode ][ 2 ] = modeInfo.dmBitsPerPel; + + if( mode > maxMode ) + { + maxMode = mode; + break; + } + } + } + } + if( maxMode != -1 ) + { + /// Outer loop: loop through color depths + for( bitDepth = 0; bitDepth < 3; bitDepth++ ) + { + /// Loop through each of the display settings, starting at + /// the maxMode and going down, happily get pixel formats for each + modeInfo.dmSize = sizeof( modeInfo ); + modeInfo.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL; + for( mode = maxMode; mode >= 0; mode-- ) + { + /// Does this resolution work at this bit depth? + if( modeRes[ mode ][ 2 ] < bitDepths[ bitDepth ] ) + continue; + + /// Attempt to set this screen mode + modeInfo.dmPelsWidth = modeRes[ mode ][ 0 ]; + modeInfo.dmPelsHeight = modeRes[ mode ][ 1 ]; + modeInfo.dmBitsPerPel = bitDepths[ bitDepth ]; + if( ChangeDisplaySettings( &modeInfo, CDS_FULLSCREEN ) == DISP_CHANGE_SUCCESSFUL ) + { + if( ITestOpenGLRes( modeRes[ mode ][ 0 ], modeRes[ mode ][ 1 ], + bitDepths[ bitDepth ], + devRec, driverName ) ) + { + /// Go and add the rest of 'em (we just assume that we can get + /// lower resolutions if we got this one) + for( mode--; mode >= 0; mode-- ) + { + devMode.SetWidth( modeRes[ mode ][ 0 ] ); + devMode.SetHeight( modeRes[ mode ][ 1 ] ); + devMode.SetColorDepth( bitDepths[ bitDepth ] ); + devRec.GetModes().Append( devMode ); + + sprintf( str, "ITryOpenGL(): Assuming mode: %dx%dx%dbpp, %s\n", + modeRes[ mode ][ 0 ], modeRes[ mode ][ 1 ], bitDepths[ bitDepth ], driverName ); + hsStatusMessage( str ); + } + } + } + } + } + + /// Note: this will also reset the screen after any mode changes from + /// creating our context + ChangeDisplaySettings( nil, 0 ); + + if( devRec.GetModes().GetCount() ) + fRecords.Append( devRec ); + } +#endif +#endif +} + +//// ITestOpenGLRes /////////////////////////////////////////////////////////// +// Tests all the possible OpenGL settings once the screen has been set +// to a given test resolution. + +hsBool hsG3DDeviceSelector::ITestOpenGLRes( int width, int height, int bitDepth, + hsG3DDeviceRecord &devRec, char *driverName ) +{ + hsBool retValue = false; + +#ifdef HS_OPEN_GL +#if HS_BUILD_FOR_WIN32 + int j, bitDepthFlags, myBitDepth; + hsG3DDeviceMode devMode; + char str[ 128 ]; + HDC hDC, hDC2; + HGLRC tempContext; + HWND testWindow = nil, testWindow2 = nil; + + PIXELFORMATDESCRIPTOR pfd; + + + /// Create test window #1 + testWindow = CreateWindowEx( WS_EX_APPWINDOW, fTempWinClass, "OpenGL Screen Test Window", + WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE, + 0, 0, width, height, + nil, nil, GetModuleHandle( nil ), 0 ); + hDC = GetDC( testWindow ); + + /// Loop through using DescribePixelFormat in an attempt to find all the + /// pixel formats we actually do support using this OpenGL driver + devMode.Clear(); + pfd.nSize = sizeof( pfd ); + bitDepthFlags = 0; + for( j = 1; retValue == false && DescribePixelFormat( hDC, j, sizeof( pfd ), &pfd ) != 0; j++ ) + { + /// Can we use this one? + if( pfd.cColorBits != bitDepth ) + continue; + + myBitDepth = ( pfd.cColorBits == 32 ) ? 0x04 : ( pfd.cColorBits == 24 ) ? 0x02 : 0x01; + + if( ( pfd.dwFlags & PFD_SUPPORT_OPENGL ) && + ( pfd.dwFlags & PFD_DRAW_TO_WINDOW ) && + ( pfd.dwFlags & PFD_DOUBLEBUFFER ) && + ( pfd.iPixelType == PFD_TYPE_RGBA ) && + ( pfd.iLayerType == PFD_MAIN_PLANE ) && + ( ( bitDepthFlags & myBitDepth ) == 0 ) ) + { + /// Looks like it! But is it REALLY? + testWindow2 = CreateWindowEx( WS_EX_APPWINDOW, fTempWinClass, "OpenGL Screen Test Window #2", + WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE, + 0, 0, width, height, + nil, nil, GetModuleHandle( nil ), 0 ); + hDC2 = GetDC( testWindow2 ); + + if( SetPixelFormat( hDC2, j, &pfd ) ) + { + tempContext = wglCreateContext( hDC2 ); + if( tempContext != nil ) + { + if( wglMakeCurrent( hDC2, tempContext ) ) + { + /// Guess it really does work... + devMode.SetWidth( width ); + devMode.SetHeight( height ); + devMode.SetColorDepth( pfd.cColorBits ); + devRec.GetModes().Append( devMode ); + bitDepthFlags |= myBitDepth; + + sprintf( str, "ITryOpenGL(): Adding mode: %dx%dx%dbpp, %s\n", + width, height, pfd.cColorBits, driverName ); + hsStatusMessage( str ); + + wglMakeCurrent( nil, nil ); + retValue = true; /// Break us out + } + wglDeleteContext( tempContext ); + } + } + ReleaseDC( testWindow2, hDC2 ); + DestroyWindow( testWindow2 ); + } + } + ReleaseDC( testWindow, hDC ); + DestroyWindow( testWindow ); + +#endif +#endif + return retValue; +} + + +//// IGetExtOpenGLInfo //////////////////////////////////////////////////////// +// Gets extended info--i.e. info requiring an OpenGL context. Assumes the +// said context is already created and active. + +void hsG3DDeviceSelector::IGetExtOpenGLInfo( hsG3DDeviceRecord &devRec ) +{ +#ifdef HS_OPEN_GL +#if HS_BUILD_FOR_WIN32 + GLint numTMUs; + char *extString, *c, *c2; + char str[ 128 ]; + int j; + + + if( ( extString = (char *)glGetString( GL_RENDERER ) ) != nil ) + { + devRec.SetDeviceDesc( extString ); + + /// Can we guess at the amount of texture memory? + c = strstr( extString, "MB" ); + if( c != nil && c != extString && ( isdigit( *( c - 1 ) ) || isspace( *( c - 1 ) ) ) ) + { + /// Looks like we found a "xxMB" texture memory specification--use it + /// as our guess + c2 = c; + do { + c2--; + } while( c2 >= extString && ( isdigit( *c2 ) || isspace( *c2 ) ) ); + c2++; + + strncpy( str, c2, (UInt32)c - (UInt32)c2 ); + j = atoi( str ); + sprintf( str, "ITryOpenGL(): Device has %d MB texture memory\n", j ); + hsStatusMessage( str ); + + j *= 1024 * 1024; /// Translate to bytes + devRec.SetMemoryBytes( j ); + } + else + { + devRec.SetMemoryBytes( 4 * 1024 * 1024 ); + hsStatusMessage( "ITryOpenGL(): WARNING: Cannot determine texture memory for this card, assuming 4MB\n" ); + } + } + else + { + devRec.SetDeviceDesc( "" ); + devRec.SetMemoryBytes( 4 * 1024 * 1024 ); + hsStatusMessage( "ITryOpenGL(): WARNING: Cannot determine texture memory for this card, assuming 4MB\n" ); + } + + + if( ( extString = (char *)glGetString( GL_EXTENSIONS ) ) != nil ) + { + /// For the number of TMUs, we'll detect for the availability of the + /// multitexture extension--if it's there, we'll assume we have two TMUs + /// (if we don't, OpenGL will probably handle it for us--or rather, it BETTER). + if( strstr( extString, "ARB_multitexture" ) ) + devRec.SetLayersAtOnce( 2 ); + else + devRec.SetLayersAtOnce( 1 ); + + /// Can we use compressed textures? + if( strstr( extString, "ARB_texture_compression" ) ) + devRec.SetCap( kCapsCompressTextures ); + } + + /// Get TMU count + glGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &numTMUs ); + if( numTMUs <= 0 ) + numTMUs = 0; + devRec.SetLayersAtOnce( numTMUs ); +#endif +#endif +} + +//// ICreateTempOpenGLContext ///////////////////////////////////////////////// +// Creates a temporary context for testing OpenGL stuff with. + +#ifdef HS_OPEN_GL +#if HS_BUILD_FOR_WIN32 + +UInt32 hsG3DDeviceSelector::ICreateTempOpenGLContext( HDC hDC, hsBool32 makeItFull ) +{ + DEVMODE modeInfo; + int pixFmt; + + + if( makeItFull ) + { + /// Attempt resolution change to 640x480x32bpp + memset( &modeInfo, 0, sizeof( modeInfo ) ); + modeInfo.dmSize = sizeof( modeInfo ); + modeInfo.dmBitsPerPel = 16; + modeInfo.dmPelsWidth = 640; + modeInfo.dmPelsHeight = 480; + if( ChangeDisplaySettings( &modeInfo, CDS_FULLSCREEN ) != DISP_CHANGE_SUCCESSFUL ) + { + return nil; /// We want fullscreen, can't get it, oops. + } + } + + /// Now try to set a pixel format + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd + 1, // version number + PFD_DRAW_TO_WINDOW | // support window + PFD_SUPPORT_OPENGL | // support OpenGL + PFD_DOUBLEBUFFER, // double buffered + PFD_TYPE_RGBA, // RGBA type + 16, // 24-bit color depth + 0, 0, 0, 0, 0, 0, // color bits ignored + 0, // no alpha buffer + 0, // shift bit ignored + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits ignored + 0, // 32-bit z-buffer + 0, // no stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + }; + + pixFmt = ChoosePixelFormat( hDC, &pfd ); + if( pixFmt > 0 && SetPixelFormat( hDC, pixFmt, &pfd ) ) + return (UInt32)wglCreateContext( hDC ); + + return 0; +} +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +//// Fudging Routines ///////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +namespace +{ + /// Here's our CFT--Chipset Fudgefactor Table + /// The table consists of entries for each of our supported chipsets in the table, + /// plus, flags to be forced set and flags to be forced cleared. Also included + /// is a Z-buffer suckiness rating, which represents how badly we need to bias + /// the z and w values to avoid z-buffer artifacts, stored as an hsScalar (i.e + /// a float). A rating of 0 means very good/default (read: Nvidia), while, say, + /// a 9.0 (i.e. shift the scale 9 times above normal) means s****y, like, say, + /// a Savage4. Also also included is a forced value for max # of layers (0 means + /// to use default). Also also also included is an LOD rating indicating how much + /// (and in which direction) to alter the base LOD bias value for this device. General + /// interpretation of this value is to add (-lodRating) to the LOD bias value. + /// This is because the LOD bias starts out negative and typically goes in 0.25 + /// increments. + /// Also also ALSO included are three new values for fog tweaking. The first two-- + /// fFogExp/Exp2ApproxStart, are the start of the linear approximation of exponential + /// fog. Tweak these to adjust the linear approximation on any cards that don't support + /// exponential and exponential-squared fog natively. The third value is the fFogEndBias-- + /// this is a value (stored as a percentage of the max possible fog value) to add on to + /// to the linear fog-end parameter AFTER ALL CALCULATIONS. This is so we can, for + /// example, tweak the end of the fog on the ATI Rage cards to not fog out as quickly. + /// 9.14.2000 - fog end bias now has a new meaning. What it *really* represents is the + /// quantization of fog on a particular card, where the end bias = ( 2^bitdepth - 2 ) / ( 2^bitdepth - 1 ) + /// So, for 8 bit fog, we end up with 254 / 255, etc. So far, everything is set to 8 + /// bit fog, but we have it here just in case we need to change it in the future. + + enum { + kDefaultChipset = 0x00, + kSavage4Chipset, + kATIRageFuryChipset, + kATIRageProChipset, + kNVidiaTNTChipset, + kNVidiaGeForceChipset, + kMatroxG400Chipset, + kIntelI810Chipset, + kSavage2000Chipset, + kS3GenericChipset, + kATIGenericChipset, + kMatroxGenericChipset, + kKYROChipset, + k3dfxV5Chipset, + kSavage3DChipset, + kATIRadeonChipset, + kATIR7X00Chipset, + kATIR7500Chipset, + kATIR8X00Chipset, + kMatroxParhelia, + kNVidiaGeForce2Chipset, + kNVidiaGeForce3Chipset, + kNVidiaGeForce4MXChipset, + kNVidiaGeForce4Chipset, + kNVidiaGeForceFXChipset + }; + + typedef struct + { + hsScalar fFogExpApproxStart; + hsScalar fFogExp2ApproxStart; + hsScalar fFogEndBias; + hsScalar fFogExpKnee; // Fog knees + hsScalar fFogExpKneeVal; + hsScalar fFogExp2Knee; + hsScalar fFogExp2KneeVal; + } FogTweakTable; + + FogTweakTable dsDefaultFogVals = { 0, 0, 254.0 / 255.0, 0.5f, 0.15f, 0.5f, 0.15f }; + FogTweakTable dsATIFogVals = { 0.1f, 0.1f, 254.0 / 255.0, 0.85f, 0.15f, 0.5f, 0.15f }; + FogTweakTable dsS3DFogVals = { 0, 0, 254.0 / 255.0, 1.0f, 1.0f, 1.0f, 1.0f }; + FogTweakTable dsi810FogVals = { 0, 0, 254.0 / 255.0, 0.6f, 0.15f, 0.4f, 0.15f }; + FogTweakTable dsRadeonFogVals = { 0, 0, 254.0 / 255.0, 0.7f, 0.15f, 0.5f, 0.2f }; + + + typedef struct { + UInt8 fType; // Our chipset ID + UInt32 *fFlagsToSet; + UInt32 *fFlagsToClear; + hsScalar fZSuckiness; // See above + UInt32 fForceMaxLayers; // The max # of layers we REALLY want (0 to not force) + hsScalar fLODRating; + FogTweakTable *fFogTweaks; + } CFTable; + + UInt32 dsSavageCapsClr[] = { + 4, // First integer is always the length + hsG3DDeviceSelector::kCapsCompressTextures, + hsG3DDeviceSelector::kCapsFogExp, + hsG3DDeviceSelector::kCapsFogExp2, + hsG3DDeviceSelector::kCapsDoesSmallTextures }; + + UInt32 dsSavageCapsSet[] = { + 1, // First integer is always the length + hsG3DDeviceSelector::kCapsBadYonStuff }; + + UInt32 dsSavage2kCapsClr[] = { + 5, // First integer is always the length + hsG3DDeviceSelector::kCapsCompressTextures, + hsG3DDeviceSelector::kCapsPixelFog, + hsG3DDeviceSelector::kCapsFogExp, + hsG3DDeviceSelector::kCapsFogExp2, + hsG3DDeviceSelector::kCapsDoesSmallTextures }; + + UInt32 dsS3GenerCapsClr[] = { + 4, // First integer is always the length + hsG3DDeviceSelector::kCapsCompressTextures, + hsG3DDeviceSelector::kCapsFogExp, + hsG3DDeviceSelector::kCapsFogExp2, + hsG3DDeviceSelector::kCapsDoesSmallTextures }; + + UInt32 dsATIFuryCapsClr[] = { + 3, // First integer is always the length + hsG3DDeviceSelector::kCapsFogExp, + hsG3DDeviceSelector::kCapsFogExp2, + hsG3DDeviceSelector::kCapsPixelFog }; + + UInt32 dsATIRageCapsClr[] = { + 4, // First integer is always the length + hsG3DDeviceSelector::kCapsFogExp, + hsG3DDeviceSelector::kCapsFogExp2, + hsG3DDeviceSelector::kCapsDoesSmallTextures, + hsG3DDeviceSelector::kCapsPixelFog }; + + UInt32 dsATIGenerCapsClr[] = { + 4, // First integer is always the length + hsG3DDeviceSelector::kCapsPixelFog, + hsG3DDeviceSelector::kCapsFogExp, + hsG3DDeviceSelector::kCapsFogExp2, + hsG3DDeviceSelector::kCapsDoesSmallTextures }; + + UInt32 dsATIRadeonCapsSet[] = { + 2, // First integer is always the length + hsG3DDeviceSelector::kCapsBadManaged, + hsG3DDeviceSelector::kCapsShareDepth + }; + + UInt32 dsATIRadeonCapsClr[] = { + 2, // First integer is always the length + hsG3DDeviceSelector::kCapsWBuffer, + hsG3DDeviceSelector::kCapsDoesSmallTextures }; + + UInt32 dsATIR7X00CapsSet[] = { + 4, // First integer is always the length + hsG3DDeviceSelector::kCapsCantShadow, + hsG3DDeviceSelector::kCapsBadManaged, + hsG3DDeviceSelector::kCapsShareDepth, + hsG3DDeviceSelector::kCapsNoAniso + }; + + UInt32 dsATIR7500CapsSet[] = { + 5, // First integer is always the length + hsG3DDeviceSelector::kCapsMaxUVWSrc2, + hsG3DDeviceSelector::kCapsCantShadow, + hsG3DDeviceSelector::kCapsBadManaged, + hsG3DDeviceSelector::kCapsShareDepth, + hsG3DDeviceSelector::kCapsNoAniso + }; + + UInt32 dsATIR7X00CapsClr[] = { + 2, // First integer is always the length + hsG3DDeviceSelector::kCapsWBuffer, + hsG3DDeviceSelector::kCapsDoesSmallTextures }; + + UInt32 dsATIR8X00CapsSet[] = { + 2, // First integer is always the length + hsG3DDeviceSelector::kCapsBadManaged, + hsG3DDeviceSelector::kCapsShareDepth + }; + + UInt32 dsATIR8X00CapsClr[] = { + 2, // First integer is always the length + hsG3DDeviceSelector::kCapsWBuffer, + hsG3DDeviceSelector::kCapsDoesSmallTextures }; + + UInt32 dsTNTCapsClr[] = { + 1, // First integer is always the length + hsG3DDeviceSelector::kCapsDoesSmallTextures }; + + UInt32 dsDefaultCapsClr[] = { + 1, // First integer is always the length + hsG3DDeviceSelector::kCapsDoesSmallTextures }; + + UInt32 dsMG400CapsClr[] = { + 1, // First integer is always the length + hsG3DDeviceSelector::kCapsDoesSmallTextures }; + + UInt32 dsKYROCapsClr[] = { + 2, // First integer is always the length + hsG3DDeviceSelector::kCapsDoesSmallTextures, + hsG3DDeviceSelector::kCapsPixelFog }; + + UInt32 dsKYROCapsSet[] = { + 1, // First integer is always the length + hsG3DDeviceSelector::kCapsNoKindaSmallTexs }; + + UInt32 ds3dfxV5CapsClr[] = { + 2, // First integer is always the length + hsG3DDeviceSelector::kCapsFogExp, + hsG3DDeviceSelector::kCapsFogExp2 }; + + UInt32 dsMatroxParheliaSet[] = { + 1, + hsG3DDeviceSelector::kCapsNoAA }; + + UInt32 dsGeForceSet[] = { + 2, + hsG3DDeviceSelector::kCapsCantProj, + hsG3DDeviceSelector::kCapsDoubleFlush }; + + UInt32 dsGeForce2Set[] = { + 1, + hsG3DDeviceSelector::kCapsDoubleFlush }; + + UInt32 dsGeForce3Set[] = { + 1, + hsG3DDeviceSelector::kCapsSingleFlush }; + + UInt32 dsGeForce4MXSet[] = { + 1, + hsG3DDeviceSelector::kCapsSingleFlush }; + + UInt32 dsGeForce4Set[] = { + 1, + hsG3DDeviceSelector::kCapsSingleFlush + }; + + CFTable dsCFTable[] = + { + // Chipset ID // F2Set // F2Clear // ZSuck // MaxLayers // LODBias // Fog Value Tables + { kDefaultChipset, nil, dsDefaultCapsClr, 0, 0, 0, &dsDefaultFogVals }, + { kATIRageFuryChipset, nil, dsATIFuryCapsClr, 4.25f, 1, 0, &dsATIFogVals }, + { kATIRageProChipset, nil, dsATIRageCapsClr, 4.25f, 1, 0, &dsATIFogVals }, + { kATIGenericChipset, nil, dsATIGenerCapsClr, 4.25f, 1, 0, &dsATIFogVals }, + { kNVidiaTNTChipset, nil, dsTNTCapsClr, 0, 0, 0, &dsDefaultFogVals }, + { kNVidiaGeForce2Chipset,dsGeForce2Set, nil, 0, 0, 0, &dsDefaultFogVals }, + { kNVidiaGeForce3Chipset,dsGeForce3Set, nil, 0, 0, 0, &dsDefaultFogVals }, + { kNVidiaGeForce4MXChipset,dsGeForce4MXSet, nil, 0, 0, 0, &dsDefaultFogVals }, + { kNVidiaGeForce4Chipset,dsGeForce4Set, nil, 0, 0, 0, &dsDefaultFogVals }, + { kNVidiaGeForceChipset,dsGeForceSet, nil, 0, 0, 0, &dsDefaultFogVals }, + { kNVidiaGeForceFXChipset,nil, nil, 0, 0, 0, &dsDefaultFogVals }, + { kMatroxG400Chipset, nil, dsMG400CapsClr, 3.25f, 0, 0, &dsDefaultFogVals }, + { kMatroxParhelia, dsMatroxParheliaSet,nil, 0, 0, 0, &dsDefaultFogVals }, + { kMatroxGenericChipset,nil, dsMG400CapsClr, 3.25f, 0, 0, &dsDefaultFogVals }, + { kIntelI810Chipset, nil, dsDefaultCapsClr, 4.5f, 1, -0.5f, &dsi810FogVals }, + { kSavage4Chipset, dsSavageCapsSet, dsSavageCapsClr, 4.0f, 1, 0, &dsDefaultFogVals }, // LOD bias should be -0.5 here + { kSavage2000Chipset, dsSavageCapsSet, dsSavage2kCapsClr, 4.0f, 1, 0, &dsDefaultFogVals }, + { kS3GenericChipset, dsSavageCapsSet, dsS3GenerCapsClr, 4.0f, 1, 0, &dsDefaultFogVals }, + { kKYROChipset, dsKYROCapsSet, dsKYROCapsClr, -151.0f, 1, 0, &dsDefaultFogVals }, + { k3dfxV5Chipset, nil, ds3dfxV5CapsClr, 3.5f, 0, 0, &dsDefaultFogVals }, + { kSavage3DChipset, nil, dsDefaultCapsClr, 0, 0, 0, &dsS3DFogVals }, + { kATIRadeonChipset, dsATIRadeonCapsSet, dsATIRadeonCapsClr, 0, 0, 0, &dsRadeonFogVals }, + { kATIR7X00Chipset, dsATIR7X00CapsSet, dsATIR7X00CapsClr, 3.f, 2, 0, &dsRadeonFogVals }, + { kATIR7500Chipset, dsATIR7500CapsSet, dsATIR7X00CapsClr, 3.f, 2, 0, &dsRadeonFogVals }, + { kATIR8X00Chipset, dsATIR8X00CapsSet, dsATIR8X00CapsClr, 0, 0, 0, &dsRadeonFogVals }, + }; + +}; + +//// IFudgeDirectXDevice ////////////////////////////////////////////////////// +// Checks this DirectX device against all our known types and fudges our caps +// flags and bias values, etc, accordingly + +#ifdef HS_SELECT_DIRECT3D +void hsG3DDeviceSelector::IFudgeDirectXDevice( hsG3DDeviceRecord &record, + D3DEnum_DriverInfo *driverInfo, + D3DEnum_DeviceInfo *deviceInfo ) +{ + char desc[ 512 ]; // Can't rely on D3D constant, since that's in another file now + DWORD vendorID, deviceID; + char *szDriver, *szDesc; + + + /// Send it off to each D3D device, respectively + if( record.GetG3DDeviceType() == kDevTypeDirect3DTnL ) + { + if( !IGetD3DCardInfo( record, driverInfo, deviceInfo, &vendorID, &deviceID, &szDriver, &szDesc ) ) + { + // {} to make VC6 happy in release build + hsAssert( false, "Trying to fudge D3D device but D3D support isn't in this EXE!" ); + } + } +#ifdef HS_SELECT_DX7 + else if( record.GetG3DDeviceType() == kDevTypeDirect3D ) + { + if( !IGetD3D7CardInfo( record, driverInfo, deviceInfo, &vendorID, &deviceID, &szDriver, &szDesc ) ) + { + // {} to make VC6 happy in release build + hsAssert( false, "Trying to fudge D3D7 device but D3D7 support isn't in this EXE!" ); + } + } +#endif // HS_SELECT_DX7 + else + { + hsAssert( false, "IFudgeDirectXDevice got a device type that support wasn't compiled for!" ); + } + + /// So capitalization won't matter in our tests + hsAssert( strlen( szDesc ) < sizeof( desc ), "D3D device description longer than expected!" ); + hsStrcpy( desc, szDesc ); + hsStrLower( desc ); + + //// S3-based Cards /////////////////////////////////////////////////////// + /// Detect Savage 4 chipset + if( deviceID == 0x00008a22 || stricmp( szDriver, "s3savg4.dll" ) == 0 || + ( strstr( desc, "diamond" ) != nil && strstr( desc, "stealth iii" ) != nil ) || + strstr( desc, "savage4 " ) != nil ) + { + /// Yup, Savage 4. + hsStatusMessage( "== Using fudge factors for a Savage 4 chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for a Savage 4 chipset" ); + ISetFudgeFactors( kSavage4Chipset, record ); + } + /// Detect Savage 2000 chipset + else if( deviceID == 0x00009102 || + stricmp( szDriver, "s3sav2k.dll" ) == 0 || + ( strstr( desc, "diamond" ) != nil && + strstr( desc, "viperii" ) != nil ) || + strstr( desc, "savage2000 " ) != nil ) + { + /// Yup, Savage 2000. + hsStatusMessage( "== Using fudge factors for a Savage 2000 chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for a Savage 2000 chipset" ); + ISetFudgeFactors( kSavage2000Chipset, record ); + } + /// Detect Savage3D chipset + else if( deviceID == 0x00008a20 || + stricmp( szDriver, "s3_6.dll" ) == 0 || + strstr( desc, "savage3d" ) != nil ) + { + /// Yup, Savage3D. + hsStatusMessage( "== Using fudge factors for a Savage3D chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for a Savage3D chipset" ); + ISetFudgeFactors( kSavage3DChipset, record ); + } + /// Detect Generic S3 chipset + else if( ( strncmp( szDriver, "s3", 2 ) == 0 ) || ( strstr( desc, "savage" ) != nil ) ) + { + /// Yup, Generic S3. + hsStatusMessage( "== Using fudge factors for a generic S3 chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for a generic S3 chipset" ); + ISetFudgeFactors( kS3GenericChipset, record ); + } + + //// ATI-based Cards ////////////////////////////////////////////////////// + /// Detect ATI Rage 128 Pro chipset + else if( ( deviceID == 0x00005046 && // Normal ATI Rage 128 Pro detection + ( stricmp( szDriver, "ati2dvaa.dll" ) == 0 + || strstr( desc, "rage 128 pro" ) != nil ) ) || + ( deviceID == 0x00005246 && // ATI All-in-wonder--same chipset, diff values + ( stricmp( szDriver, "ati3draa.dll" ) == 0 + || strstr( desc, "all-in-wonder 128" ) != nil ) ) ) + { + hsStatusMessage( "== Using fudge factors for an ATI Rage 128 Pro chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for an ATI Rage 128 Pro chipset" ); + ISetFudgeFactors( kATIRageProChipset, record ); + } + /// Detect(detest?) ATI Rage FURY MAXX chipset + else if( deviceID == 0x00005046 && + ( stricmp( szDriver, "ati3drau.dll" ) == 0 + || strstr( desc, "rage fury" ) != nil ) ) + { + hsStatusMessage( "== Using fudge factors for an ATI Rage Fury MAXX chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for an ATI Rage Fury MAXX chipset" ); + ISetFudgeFactors( kATIRageFuryChipset, record ); + } + /// Detect ATI Radeon chipset + // We will probably need to differentiate between different Radeons at some point in + // the future, but not now. + else if( // deviceID == 0x00005144 && + ( stricmp( szDriver, "ati2dvag.dll" ) == 0 + || strstr( desc, "radeon" ) != nil ) ) + { + int series = 0; + const char* str = strstr(desc, "radeon"); + if( str ) + str += strlen("radeon"); + else + { + str = strstr(desc, "all-in-wonder"); + if( str ) + str += strlen("all-in-wonder"); + } + if( str ) + { + if( 1 == sscanf(str, "%d", &series) ) + { + if( (series == 7500) || (series == 7200) ) + { + hsStatusMessage( "== Using fudge factors for ATI Radeon 7200/7500 chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for ATI Radeon 7200/7500 chipset" ); + ISetFudgeFactors( kATIR7500Chipset, record ); + } + else + if( (series >= 7000) && (series < 8000) ) + { + hsStatusMessage( "== Using fudge factors for ATI Radeon 7X00 chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for ATI Radeon 7X00 chipset" ); + ISetFudgeFactors( kATIR7X00Chipset, record ); + } + else + if( (series >= 8000) && (series < 9000) ) + { + hsStatusMessage( "== Using fudge factors for ATI Radeon 8X00 chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for ATI Radeon 8X00 chipset" ); + ISetFudgeFactors( kATIR8X00Chipset, record ); + } + else + { + series = 0; + } + } + else + { + series = 0; + + // Skip white space + while( *str && (*str <= 0x32) ) + str++; + + // I've still never seen either of these, so I'm just going by ATI's site. + // Don't have the option of using device-id's. + if( (str[0] == 'v') && (str[1] == 'e') ) + { + // Got an alias here. If it's an All-in-Wonder VE, it's really a 7500. + // If it's a Radeon VE, it's really a 7000. + if( strstr(desc, "radeon") ) + series = 7000; + else if( strstr(desc, "all-in-wonder") ) + series = 7500; + } + } + } + if( !series ) + { + hsStatusMessage( "== Using fudge factors for ATI Radeon chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for ATI Radeon chipset" ); + ISetFudgeFactors( kATIRadeonChipset, record ); + } + } + /// Detect generic ATI chipset + else if( ( strncmp( szDriver, "ati", 3 ) == 0 ) || ( strstr( desc, "ati " ) != nil ) ) + { + hsStatusMessage( "== Using fudge factors for a generic ATI chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for a generic ATI chipset" ); + ISetFudgeFactors( kATIGenericChipset, record ); + } + + //// Matrox-based Cards /////////////////////////////////////////////////// + else if( (deviceID == 0x527) + || strstr(desc, "parhelia") ) + { + hsStatusMessage( "== Using fudge factors for a Matrox Parhelia chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for a Matrox Millenium G400 chipset" ); + ISetFudgeFactors( kMatroxParhelia, record ); + } + /// Detect Matrox G400 chipset + else if( deviceID == 0x00000525 && + ( stricmp( szDriver, "g400d.dll" ) == 0 + || ( strstr( desc, "matrox" ) != nil && strstr( desc, "g400" ) != nil ) ) ) + { + hsStatusMessage( "== Using fudge factors for a Matrox Millenium G400 chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for a Matrox Millenium G400 chipset" ); + ISetFudgeFactors( kMatroxG400Chipset, record ); + } + /// Detect generic Matrox chipset + else if( strstr( desc, "matrox" ) != nil ) + { + hsStatusMessage( "== Using fudge factors for a generic Matrox chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for a generic Matrox chipset" ); + ISetFudgeFactors( kMatroxGenericChipset, record ); + } + + //// Other Cards ////////////////////////////////////////////////////////// + /// Detect NVidia RIVA TNT chipset + else if( deviceID == 0x00000020 && + ( stricmp( szDriver, "nvdd32.dll" ) == 0 + || strstr( desc, "nvidia riva tnt" ) != nil ) ) + { + hsStatusMessage( "== Using fudge factors for an NVidia RIVA TNT chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for an NVidia RIVA TNT chipset" ); + ISetFudgeFactors( kNVidiaTNTChipset, record ); + if( record.GetMemoryBytes() < 16 * 1024 * 1024 ) + { + hsStatusMessage( "== (also fudging memory up to 16MB) ==\n" ); + plDemoDebugFile::Write( " (also fudging memory up to 16MB)" ); + record.SetMemoryBytes( 16 * 1024 * 1024 ); + } + } + /// Detect Intel i810 chipset + else if( deviceID == 0x00007125 && + ( stricmp( szDriver, "i81xdd.dll" ) == 0 + || ( strstr( desc, "intel" ) != nil && strstr( desc, "810" ) != nil ) ) ) + { + hsStatusMessage( "== Using fudge factors for an Intel i810 chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for an Intel i810 chipset" ); + ISetFudgeFactors( kIntelI810Chipset, record ); + } + /// Detect STMicroelectronics KYRO chipset + else if( deviceID == 0x00000010 && ( strstr( desc, "kyro" ) != nil ) ) + { + hsStatusMessage( "== Using fudge factors for a KYRO chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for a KYRO chipset" ); + ISetFudgeFactors( kKYROChipset, record ); + } + /// Detect for a 3dfx Voodoo5 + else if( vendorID == 0x121a && deviceID == 0x00000009 && + stricmp( szDriver, "3dfxvs.dll" ) == 0 ) + { + hsStatusMessage( "== Using fudge factors for a 3dfx Voodoo5 chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for a 3dfx Voodoo5 chipset" ); + ISetFudgeFactors( k3dfxV5Chipset, record ); + } + /// Detect for a GeForce-class card. We can be loose here because we want + /// to get ALL GeForce/2/256 cards + else if( strstr( desc, "nvidia" ) != nil && strstr( desc, "geforce2" ) != nil ) + { + hsStatusMessage( "== Using fudge factors for an NVidia GeForce2-based chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for an NVidia GeForce2-based chipset" ); + ISetFudgeFactors( kNVidiaGeForce2Chipset, record ); + } + else if( strstr( desc, "nvidia" ) != nil && strstr( desc, "geforce3" ) != nil ) + { + hsStatusMessage( "== Using fudge factors for an NVidia GeForce3-based chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for an NVidia GeForce3-based chipset" ); + ISetFudgeFactors( kNVidiaGeForce3Chipset, record ); + } + else if( strstr( desc, "nvidia" ) != nil && strstr( desc, "geforce4 mx" ) != nil ) + { + hsStatusMessage( "== Using fudge factors for an NVidia GeForce4MX-based chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for an NVidia GeForce4MX-based chipset" ); + ISetFudgeFactors( kNVidiaGeForce4MXChipset, record ); + } + else if( strstr( desc, "nvidia" ) != nil && strstr( desc, "geforce4" ) != nil ) + { + hsStatusMessage( "== Using fudge factors for an NVidia GeForce4-based chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for an NVidia GeForce4-based chipset" ); + ISetFudgeFactors( kNVidiaGeForce4Chipset, record ); + } + else if( + strstr( desc, "nvidia" ) && strstr( desc, "geforce" ) + && ( + (deviceID == 0x101) + ||(deviceID == 0x100) + ||strstr(desc, "256") + ) + ) + { + hsStatusMessage( "== Using fudge factors for an NVidia GeForce-based chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for an NVidia GeForce-based chipset" ); + ISetFudgeFactors( kNVidiaGeForceChipset, record ); + } + else if( strstr( desc, "nvidia" ) != nil && strstr( desc, "geforce" ) != nil ) + { + hsStatusMessage( "== Using fudge factors for an NVidia GeForceFX-based chipset ==\n" ); + plDemoDebugFile::Write( " Using fudge factors for an NVidia GeForceFX-based chipset" ); + ISetFudgeFactors( kNVidiaGeForceFXChipset, record ); + } + /// Detect for a TNT-based card and force it to >= 16MB memory, so we always use it + else if( strstr( desc, "tnt" ) != nil && record.GetMemoryBytes() < 16 * 1024 * 1024 ) + { + hsStatusMessage( "== NVidia TNT-based card detected. Fudging memory reading to 16MB ==\n" ); + plDemoDebugFile::Write( " NVidia TNT-based card detected. Fudging memory reading to 16MB" ); + record.SetMemoryBytes( 16 * 1024 * 1024 ); + } + + /// Default fudge values + else + { + hsStatusMessage( "== Using default fudge factors ==\n" ); + plDemoDebugFile::Write( " Using default fudge factors" ); + ISetFudgeFactors( kDefaultChipset, record ); + } +} +#endif + +//// ISetFudgeFactors ///////////////////////////////////////////////////////// +// Given a chipset ID, looks the values up in the CFT and sets the appropriate +// values. + +void hsG3DDeviceSelector::ISetFudgeFactors( UInt8 chipsetID, hsG3DDeviceRecord &record ) +{ + int i, maxIDs, j; + + + maxIDs = sizeof( dsCFTable ) / sizeof( dsCFTable[ 0 ] ); + + /// Search for our chipset + for( i = 0; i < maxIDs; i++ ) + { + if( dsCFTable[ i ].fType == chipsetID ) + { + /// Found it! + + // Flags to force set + if( dsCFTable[ i ].fFlagsToSet != nil ) + { + for( j = 0; j < dsCFTable[ i ].fFlagsToSet[ 0 ]; j++ ) + record.SetCap( dsCFTable[ i ].fFlagsToSet[ j + 1 ] ); + } + + // Flags to force clear + if( dsCFTable[ i ].fFlagsToClear != nil ) + { + for( j = 0; j < dsCFTable[ i ].fFlagsToClear[ 0 ]; j++ ) + record.SetCap( dsCFTable[ i ].fFlagsToClear[ j + 1 ], false ); + } + + // Suckiness + record.SetZBiasRating( dsCFTable[ i ].fZSuckiness ); + + // Max # of layers + if( dsCFTable[ i ].fForceMaxLayers > 0 ) + record.SetLayersAtOnce( dsCFTable[ i ].fForceMaxLayers ); + + // LOD bias rating + record.SetLODBiasRating( dsCFTable[ i ].fLODRating ); + + // Fog tweaks + FogTweakTable *fogTweaks = dsCFTable[ i ].fFogTweaks; + + record.SetFogApproxStarts( fogTweaks->fFogExpApproxStart, fogTweaks->fFogExp2ApproxStart ); + record.SetFogEndBias( fogTweaks->fFogEndBias ); + record.SetFogKneeParams( hsG3DDeviceRecord::kFogExp, fogTweaks->fFogExpKnee, fogTweaks->fFogExpKneeVal ); + record.SetFogKneeParams( hsG3DDeviceRecord::kFogExp2, fogTweaks->fFogExp2Knee, fogTweaks->fFogExp2KneeVal ); + + if( record.GetCap(kCapsNoAA) ) + { + int j; + for( j = 0; j < record.GetModes().GetCount(); j++ ) + record.GetModes()[j].ClearFSAATypes(); + } + + return; + } + } +} + + + +/////////////////////////////////////////////////////////////////////////////// +// +// Demo Debug File functions +// Created 10.10.2000 by Mathew Burrack @ Cyan, Inc. +// Modified 10.11 mcn to conform (more) to coding standards. +// +/////////////////////////////////////////////////////////////////////////////// + +//// Local Globals //////////////////////////////////////////////////////////// + +#if M3DDEMOINFO // Demo Debug Build +static plDemoDebugFile sMyDDFWriter; + +hsBool plDemoDebugFile::fIsOpen = false; +FILE *plDemoDebugFile::fDemoDebugFP = nil; +hsBool plDemoDebugFile::fEnabled = false; +#endif + + +//// IDDFOpen ///////////////////////////////////////////////////////////////// +// Internal function--opens the demo debug file for writing. Returns true +// if successful, false otherwise. + +hsBool plDemoDebugFile::IDDFOpen( void ) +{ +#if M3DDEMOINFO // Demo Debug Build + char fileName[] = "log/debug_info.dat"; + time_t currTime; + struct tm *localTime; + char timeString[ 27 ]; // see definition of asctime() + char *c; + + + /// Don't open if we're not enabled + if( !fEnabled ) + return false; + + /// Open the file + if( fDemoDebugFP == nil ) + fDemoDebugFP = fopen( fileName, "wt" ); + + if( fDemoDebugFP == nil ) + return( fIsOpen = false ); + + /// Write out a header line + time( &currTime ); + localTime = localtime( &currTime ); + + // Note: asctime includes a carriage return. Gotta strip... + strcpy( timeString, asctime( localTime ) ); + c = strchr( timeString, '\n' ); + if( c != nil ) + *c = 0; + + fprintf( fDemoDebugFP, "\n--- Demo Debug Info File (Created %s) ---\n", timeString ); + + /// All done! + return( fIsOpen = true ); +#else + return false; +#endif +} + +//// IDDFClose //////////////////////////////////////////////////////////////// +// "Whatcha gonna do when the lightning strikes and hits you...." +// -- "Lightning Strikes", Yes, 1999 + + +void plDemoDebugFile::IDDFClose( void ) +{ +#if M3DDEMOINFO // Demo Debug Build + if( fDemoDebugFP != nil ) + { + // Write an exit line (fun fun) + fputs( "--- End of Demo Debug Info File ---\n\n", fDemoDebugFP ); + + // Close + fclose( fDemoDebugFP ); + } + + fIsOpen = false; +#endif +} + +//// Write //////////////////////////////////////////////////////////////////// +// Writes a string to the DDF. If the DDF isn't open, opens it. + +void plDemoDebugFile::Write( char *string ) +{ +#if M3DDEMOINFO // Demo Debug Build + if( !fIsOpen ) + IDDFOpen(); + + if( fIsOpen ) + fprintf( fDemoDebugFP, "%s\n", string ); +#endif +} + +void plDemoDebugFile::Write( char *string1, char *string2 ) +{ +#if M3DDEMOINFO // Demo Debug Build + if( !fIsOpen ) + IDDFOpen(); + + if( fIsOpen ) + fprintf( fDemoDebugFP, "%s: %s\n", string1, string2 ); +#endif +} + +void plDemoDebugFile::Write( char *string1, Int32 value ) +{ +#if M3DDEMOINFO // Demo Debug Build + if( !fIsOpen ) + IDDFOpen(); + + if( fIsOpen ) + fprintf( fDemoDebugFP, "%s: %d (0x%x)\n", string1, value, value ); +#endif +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsG3DDeviceSelector.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsG3DDeviceSelector.h new file mode 100644 index 00000000..bdce7c25 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsG3DDeviceSelector.h @@ -0,0 +1,468 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// hsG3DDeviceSelector Class Header // +// Generic device enumeration (D3D, OpenGL, etc) // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 5.21.2001 mcn - Cleaned out all the old Glide stuff, since Plasma2 will // +// not support Glide :( // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef hsG3DDeviceSelector_inc +#define hsG3DDeviceSelector_inc + +#include "hsWinRef.h" + +#include "hsTemplates.h" +#include "hsBitVector.h" + +#ifdef HS_BUILD_FOR_WIN32 +#define HS_SELECT_DIRECT3D // not supported on the Mac. +#endif // HS_BUILD_FOR_WIN32 + +#ifdef HS_BUILD_FOR_WIN32 +#define __MSC__ +#define DYNAHEADER 1 +#endif // HS_BUILD_FOR_WIN32 + +/// #define the following to allow selection of the D3D reference driver +#define HS_ALLOW_D3D_REF_DRIVER 1 + + +class hsStream; +struct D3DEnum_DeviceInfo; +struct D3DEnum_DriverInfo; +struct D3DEnum_DeviceInfo; +struct D3DEnum_DriverInfo; + +class hsG3DDeviceMode +{ + enum { + kNone = 0x0, + kDiscarded = 0x1 + }; +protected: + UInt32 fFlags; + + UInt32 fWidth; + UInt32 fHeight; + UInt32 fDepth; + + hsTArray fZStencilDepths; // Array of supported depth/stencil buffer formats. + // Each entry is of the form: ( stencil bit count << 8 ) | ( depth bit count ) + hsTArray fFSAATypes; // Array of multisample types supported (each one 2-16) + + hsBool fCanRenderToCubics; + +public: + hsG3DDeviceMode(); + ~hsG3DDeviceMode(); + + hsBool operator< (const hsG3DDeviceMode &mode) const; + + void Clear(); + + hsBool GetDiscarded() const { return 0 != (fFlags & kDiscarded); } + UInt32 GetWidth() const { return fWidth; } + UInt32 GetHeight() const { return fHeight; } + UInt32 GetColorDepth() const { return fDepth; } + UInt8 GetNumZStencilDepths( void ) const { return fZStencilDepths.GetCount(); } + UInt16 GetZStencilDepth( UInt8 i ) const { return fZStencilDepths[ i ]; } + UInt8 GetNumFSAATypes( void ) const { return fFSAATypes.GetCount(); } + UInt8 GetFSAAType( UInt8 i ) const { return fFSAATypes[ i ]; } + hsBool GetCanRenderToCubics( void ) const { return fCanRenderToCubics; } + + void SetDiscarded(hsBool on=true) { if(on) fFlags |= kDiscarded; else fFlags &= ~kDiscarded; } + void SetWidth(UInt32 w) { fWidth = w; } + void SetHeight(UInt32 h) { fHeight = h; } + void SetColorDepth(UInt32 d) { fDepth = d; } + void ClearZStencilDepths( void ) { fZStencilDepths.Reset(); } + void AddZStencilDepth( UInt16 depth ) { fZStencilDepths.Append( depth ); } + + void ClearFSAATypes( void ) { fFSAATypes.Reset(); } + void AddFSAAType( UInt8 type ) { fFSAATypes.Append( type ); } + + void SetCanRenderToCubics( hsBool can ) { fCanRenderToCubics = can; } + + void Read(hsStream* s); + void Write(hsStream* s) const; +}; + +class hsG3DDeviceRecord +{ +public: + enum { + kNone = 0x0, + kDiscarded = 0x1, + kInvalid = 0x2 + }; + + enum FogTypes { + kFogExp = 0, + kFogExp2, + kNumFogTypes + }; + +protected: + + UInt32 fRecordVersion; /// Version starts at 2 (see .cpp for explanation) + enum { + kCurrRecordVersion = 0x0b + /// Version history: + /// 1 - Initial version (had no version #) + /// 2 - Added Z and LOD bias + /// 3 - Changed Z and LOD bias to floats, added fog tweaks + /// 4 - Changed values for fog tweaks; force reload through version # + /// 5 - Same as #4, updated fog end bias to be based solely on fog quantization/bit depth + /// 6 - Updated values for the ATI boards, Matrox, and i810 + /// 7 - Added fog knee tweaks + /// 8 - Added support for multiple depth/stencil formats per mode + /// 9 - Added multisample types to the mode record + /// A - Added anisotropic sample field + /// B - Added flag for cubic textures support + }; + + /// Version < 2 Data + UInt32 fFlags; + + UInt32 fG3DDeviceType; + UInt32 fG3DHALorHEL; + + + char* fG3DDriverDesc; + char* fG3DDriverName; + char* fG3DDriverVersion; + char* fG3DDeviceDesc; + + hsBitVector fCaps; + UInt32 fLayersAtOnce; + UInt32 fMemoryBytes; + + hsTArray fModes; + + /// New to Version 3 + float fZBiasRating; + float fLODBiasRating; + float fFogExpApproxStart; + float fFogExp2ApproxStart; + float fFogEndBias; // As a percentage of the max value for fog + // (i.e. for Z fog, it's a percentage of 1 to add on, + // for W fog, it's a percentage of the yon) + + /// Version 7 - Fog Knee values + float fFogKnees[ kNumFogTypes ]; + float fFogKneeVals[ kNumFogTypes ]; + + /// Version 9 - The actual AA setting we use + UInt8 fAASetting; + + /// Version A - the anisotropic level we use + UInt8 fMaxAnisotropicSamples; // 1 to disable, up to max allowed in hardware + int fPixelShaderMajorVer; + int fPixelShaderMinorVer; + +public: + hsG3DDeviceRecord(); + virtual ~hsG3DDeviceRecord(); + + hsG3DDeviceRecord(const hsG3DDeviceRecord& src); + hsG3DDeviceRecord& operator=(const hsG3DDeviceRecord& src); + + UInt32 GetG3DDeviceType() const { return fG3DDeviceType; } + const char* GetG3DDeviceTypeName() const; + UInt32 GetG3DHALorHEL() const { return fG3DHALorHEL; } + + UInt32 GetMemoryBytes() const { return fMemoryBytes; } + + const char* GetDriverDesc() const { return fG3DDriverDesc; } + const char* GetDriverName() const { return fG3DDriverName; } + const char* GetDriverVersion() const { return fG3DDriverVersion; } + const char* GetDeviceDesc() const { return fG3DDeviceDesc; } + + void SetG3DDeviceType(UInt32 t) { fG3DDeviceType = t; } + void SetG3DHALorHEL(UInt32 h) { fG3DHALorHEL = h; } + void SetMemoryBytes(UInt32 b) { fMemoryBytes = b; } + + void SetDriverDesc(const char* s); + void SetDriverName(const char* s); + void SetDriverVersion(const char* s); + void SetDeviceDesc(const char* s); + + hsBool GetCap(UInt32 cap) const { return fCaps.IsBitSet(cap); } + void SetCap(UInt32 cap, hsBool on=true) { fCaps.SetBit(cap, on); } + + float GetZBiasRating( void ) const { return fZBiasRating; } + void SetZBiasRating( float rating ) { fZBiasRating = rating; } + + float GetLODBiasRating( void ) const { return fLODBiasRating; } + void SetLODBiasRating( float rating ) { fLODBiasRating = rating; } + + void GetFogApproxStarts( float &expApprox, float &exp2Approx ) const { expApprox = fFogExpApproxStart; + exp2Approx = fFogExp2ApproxStart; } + void SetFogApproxStarts( float exp, float exp2 ) { fFogExpApproxStart = exp; + fFogExp2ApproxStart = exp2; } + + float GetFogEndBias( void ) const { return fFogEndBias; } + void SetFogEndBias( float rating ) { fFogEndBias = rating; } + + void GetFogKneeParams( UInt8 type, float &knee, float &kneeVal ) const { knee = fFogKnees[ type ]; kneeVal = fFogKneeVals[ type ]; } + void SetFogKneeParams( UInt8 type, float knee, float kneeVal ) { fFogKnees[ type ] = knee; fFogKneeVals[ type ] = kneeVal; } + + UInt32 GetLayersAtOnce() const { return fLayersAtOnce; } + void SetLayersAtOnce(UInt32 n) { fLayersAtOnce = n; } + + UInt8 GetAASetting() const { return fAASetting; } + void SetAASetting( UInt8 s ) { fAASetting = s; } + + UInt8 GetMaxAnisotropicSamples( void ) const { return fMaxAnisotropicSamples; } + void SetMaxAnisotropicSamples( UInt8 num ) { fMaxAnisotropicSamples = num; } + + void SetDiscarded(hsBool on=true) { if(on)fFlags |= kDiscarded; else fFlags &= ~kDiscarded; } + hsBool GetDiscarded() const { return 0 != (fFlags & kDiscarded); } + + void SetInvalid( hsBool on = true ) { if( on ) fFlags |= kInvalid; else fFlags &= ~kInvalid; } + hsBool IsInvalid() const { return 0 != ( fFlags & kInvalid ); } + + hsTArray& GetModes() { return fModes; } + + hsG3DDeviceMode* GetMode(int i) const { return &fModes[i]; } + + void ClearModes(); + void Clear(); + void RemoveDiscarded(); + + // PlaceHolder - Whether a mode can window is restricted by the current setup + // of the PC. E.g. if the user changes from 16 bit to TrueColor, the Modes that + // can window are pretty much flipped. So we'll have to pass in enough info (like + // the hWnd?) to find out what the current setup is to make sure it's compatible. + hsBool ModeCanWindow(void* ctx, hsG3DDeviceMode* mode) { return false; } + void SetPixelShaderVersion(int major, int minor) { fPixelShaderMajorVer = major; fPixelShaderMinorVer = minor; } + void GetPixelShaderVersion(int &major, int &minor) { major = fPixelShaderMajorVer; minor = fPixelShaderMinorVer; } + + void Read(hsStream* s); + void Write(hsStream* s) const; +}; + +class hsG3DDeviceModeRecord +{ +protected: + hsG3DDeviceRecord fDevice; + hsG3DDeviceMode fMode; +public: + hsG3DDeviceModeRecord(); + hsG3DDeviceModeRecord(const hsG3DDeviceRecord& devRec, const hsG3DDeviceMode& devMode); + ~hsG3DDeviceModeRecord(); + + hsG3DDeviceModeRecord(const hsG3DDeviceModeRecord& src); + hsG3DDeviceModeRecord& operator=(const hsG3DDeviceModeRecord& src); + + const hsG3DDeviceRecord* GetDevice() const { return &fDevice; } + const hsG3DDeviceMode* GetMode() const { return &fMode; } +}; + +class hsG3DDeviceSelector : public hsRefCnt +{ +public: + enum { + kDevTypeUnknown = 0, + kDevTypeGlide, + kDevTypeDirect3D, + kDevTypeOpenGL, + kDevTypeDirect3DTnL, + + kNumDevTypes + }; + enum { + kHHTypeUnknown = 0, + kHHD3DNullDev, + kHHD3DRampDev, + kHHD3DRGBDev, + kHHD3DHALDev, + kHHD3DMMXDev, + kHHD3DTnLHalDev, + kHHD3DRefDev, + kHHD3D3dfxDev, + kHHD3D3dfxVoodoo5Dev, + + kNumHHTypes + }; + enum { + kCapsNone = 0, + kCapsNoWindow, + kCapsMipmap, + kCapsPerspective, + kCapsHardware, + kCapsWBuffer, + kCapsCompressTextures, + kCapsHWTransform, + kCapsDither, + kCapsFogLinear, + kCapsFogExp, + kCapsFogExp2, + kCapsFogRange, + kCapsLODWatch, + kCapsUNUSED, + kCapsDoesSmallTextures, + kCapsPixelFog, + kCapsBadYonStuff, + kCapsNoKindaSmallTexs, + kCapsCubicTextures, + kCapsCubicMipmap, + kCapsZBias, + kCapsPixelShader, + kCapsNoAA, + kCapsDoubleFlush, + kCapsSingleFlush, + kCapsCantShadow, + kCapsMaxUVWSrc2, + kCapsCantProj, + kCapsLimitedProj, + kCapsShareDepth, + kCapsBadManaged, + kCapsNoAniso, + // etc. + + kNumCaps + }; + enum + { + kDefaultWidth = 800, + kDefaultHeight = 600, + kDefaultDepth = 32 + }; + +protected: + hsTArray fRecords; + char fTempWinClass[ 128 ]; + + char fErrorString[ 128 ]; + + void ITryDirect3DTnLDevice(D3DEnum_DeviceInfo* devInfo, hsG3DDeviceRecord& srcDevRec); + void ITryDirect3DTnLDriver(D3DEnum_DriverInfo* drivInfo); + void ITryDirect3DTnL(hsWinRef winRef); + hsBool IInitDirect3D( void ); + +#ifdef HS_SELECT_DX7 + void ITryDirect3DDevice(D3DEnum_DeviceInfo* devInfo, hsG3DDeviceRecord& srcDevRec); + void ITryDirect3DDriver(D3DEnum_DriverInfo* drivInfo); + void ITryDirect3D(hsWinRef winRef); +#endif // HS_SELECT_DX7 + void IFudgeDirectXDevice( hsG3DDeviceRecord &record, + D3DEnum_DriverInfo *driverInfo, D3DEnum_DeviceInfo *deviceInfo ); + UInt32 IAdjustDirectXMemory( UInt32 cardMem ); + + hsBool IGetD3DCardInfo( hsG3DDeviceRecord &record, void *driverInfo, void *deviceInfo, + DWORD *vendorID, DWORD *deviceID, char **driverString, char **descString ); +#ifdef HS_SELECT_DX7 + hsBool IGetD3D7CardInfo( hsG3DDeviceRecord &record, void *driverInfo, void *deviceInfo, + DWORD *vendorID, DWORD *deviceID, char **driverString, char **descString ); +#endif // HS_SELECT_DX7 + + void ITryOpenGL( hsWinRef winRef ); + void IGetExtOpenGLInfo( hsG3DDeviceRecord &devRec ); + void IGetOpenGLModes( hsG3DDeviceRecord &devRec, char *driverName ); + hsBool ITestOpenGLRes( int width, int height, int bitDepth, + hsG3DDeviceRecord &devRec, char *driverName ); +#ifdef HS_OPEN_GL +#if HS_BUILD_FOR_WIN32 + UInt32 ICreateTempOpenGLContext( HDC hDC, hsBool makeItFull ); +#endif +#endif + + void ISetFudgeFactors( UInt8 chipsetID, hsG3DDeviceRecord &record ); + +public: + hsG3DDeviceSelector(); + virtual ~hsG3DDeviceSelector(); + + void Clear(); + void RemoveDiscarded(); + void RemoveUnusableDevModes(hsBool bTough); // Removes modes and devices not allowed supported in release + + hsBool Init( void ); // Returns false if couldn't init + const char *GetErrorString( void ) { return fErrorString; } + + void Enumerate(hsWinRef winRef); + hsTArray& GetDeviceRecords() { return fRecords; } + + hsBool GetDefault(hsG3DDeviceModeRecord *dmr); + + hsG3DDeviceRecord* GetRecord(int i) { return &fRecords[i]; } + + void Read(hsStream* s); + void Write(hsStream* s); +}; + + +#define M3DDEMOINFO 1 /// Always compiled now, but only enabled if + /// WIN_INIT has DemoInfoOutput in it +/////////////////////////////////////////////////////////////////////////////// +// +// Demo Debug File header file stuff +// Created 10.10.2000 by Mathew Burrack @ Cyan, Inc. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include "headspin.h" + +class plDemoDebugFile +{ + public: + plDemoDebugFile() { fDemoDebugFP = nil; fIsOpen = false; fEnabled = false; } + ~plDemoDebugFile() { IDDFClose(); } + + // Static function to write a string to the DDF + static void Write( char *string ); + + // Static function to write two strings to the DDF + static void Write( char *string1, char *string2 ); + + // Static function to write a string and a signed integer value to the DDF + static void Write( char *string1, Int32 value ); + + // Enables or disables the DDF class + static void Enable( hsBool yes ) { fEnabled = yes; } + + protected: + static hsBool fIsOpen; + static FILE *fDemoDebugFP; + static hsBool fEnabled; + + // Opens the DDF for writing + static hsBool IDDFOpen( void ); + + // Closes the DDF + static void IDDFClose( void ); +}; + + +#endif // hsG3DDeviceSelector_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGColorizer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGColorizer.cpp new file mode 100644 index 00000000..99537344 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGColorizer.cpp @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plPipeline.h" +#include "hsGColorizer.h" + +hsBool hsGColorizer::Colorizing() +{ + return fPipeline ? 0 != (fPipeline->GetColorOverride().fFlags & hsColorOverride::kModColor) : false; +} + +hsBool hsGColorizer::Alpharizing() +{ + return fPipeline ? 0 != (fPipeline->GetColorOverride().fFlags & hsColorOverride::kModAlpha) : false; +} + +hsColorRGBA hsGColorizer::GetCurrentColor() +{ + return fPipeline ? fPipeline->GetColorOverride().fColor : hsColorRGBA().Set(1.f,1.f,1.f,1.f); +} + +void hsGColorizer::Init(plPipeline* pipe) +{ + fPipeline = pipe; +} + +void hsGColorizer::PushColorize(hsColorRGBA& col, hsBool alphaOnly) +{ + if( fPipeline ) + { + hsColorOverride colorOver; + colorOver.fFlags = alphaOnly ? hsColorOverride::kModAlpha : hsColorOverride::kModColor | hsColorOverride::kModAlpha; + colorOver.fColor = col; + fResetColor = fPipeline->PushColorOverride(colorOver); + } +} + +void hsGColorizer::PopColorize() +{ + if( fPipeline ) + { + fPipeline->PopColorOverride(fResetColor); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGColorizer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGColorizer.h new file mode 100644 index 00000000..9280a16e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGColorizer.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsGColorizer_inc +#define hsGColorizer_inc + +#include "hsTypes.h" +#include "hsColorRGBA.h" + +class plPipeline; + +class hsGColorizer { +protected: + hsColorOverride fResetColor; + plPipeline* fPipeline; + +public: + hsGColorizer() : fPipeline(nil) {} + hsBool Colorizing(); + hsBool Alpharizing(); + hsColorRGBA GetCurrentColor(); + void Init(plPipeline* pipe); + void PushColorize(hsColorRGBA& col, hsBool alphaOnly); + void PopColorize(); +}; + +#endif // hsGColorizer_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDDrawDllLoad.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDDrawDllLoad.cpp new file mode 100644 index 00000000..cc4d1f7e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDDrawDllLoad.cpp @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" + +#include + +#include "hsGDDrawDllLoad.h" + +static hsGDDrawDllLoad staticDllLoad; + +hsGDDrawDllLoad::hsGDDrawDllLoad() +{ + hsAssert(!staticDllLoad.fD3DDll, "Don't make instances of this class, just use GetDDrawDll func"); + + fD3DDll = LoadLibrary( "D3D9.DLL" ); + if (fD3DDll) + hsStatusMessage( "--- D3D9.DLL loaded successfully.\n" ); + else + hsStatusMessage( "--- Unable to load D3D9.DLL successfully.\n" ); +} + +hsGDDrawDllLoad::~hsGDDrawDllLoad() +{ + if (fD3DDll != nil) + { + hsStatusMessage( "--- Unloading D3D.DLL.\n" ); + FreeLibrary(fD3DDll); + } +} + +HMODULE hsGDDrawDllLoad::GetD3DDll() +{ + return staticDllLoad.fD3DDll; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDDrawDllLoad.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDDrawDllLoad.h new file mode 100644 index 00000000..08aa6c06 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDDrawDllLoad.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsGDDrawDllLoad_inc +#define hsGDDrawDllLoad_inc + + +class hsGDDrawDllLoad +{ +private: + HMODULE fD3DDll; + +public: + hsGDDrawDllLoad(); + ~hsGDDrawDllLoad(); + + static HMODULE GetD3DDll(); +}; + +#endif hsGDDrawDllLoad_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDeviceRef.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDeviceRef.h new file mode 100644 index 00000000..0642ae37 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGDeviceRef.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// hsGDeviceRef.h - Header for the generic deviceRef class // +// Cyan, Inc. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef hsGDeviceRef_inc +#define hsGDeviceRef_inc + +#include "hsRefCnt.h" + + +class hsGDeviceRef : public hsRefCnt +{ +protected: + UInt32 fFlags; + +public: + // Note, derived classes define more flags. Take care if adding flags here. + // Currently have flags 0x0 - 0x8 reserved. + enum { + kNone = 0x0, + kDirty = 0x1 + }; + + UInt32 fUseTime; // time stamp when last used - stat gather only + + hsBool IsDirty() const { return (fFlags & kDirty); } + void SetDirty(hsBool on) { if(on)fFlags |= kDirty; else fFlags &= ~kDirty; } + + hsGDeviceRef() : fFlags(0), fUseTime(0) {} + virtual ~hsGDeviceRef() {} +}; + +#endif // hsGDeviceRef_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGEnviron.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGEnviron.cpp new file mode 100644 index 00000000..b5db5594 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGEnviron.cpp @@ -0,0 +1,1254 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsGEnviron.h" +#include "../plSurface/hsGLayer.h" +#include "../plSurface/hsGMaterial.h" +//#include "hsG3DDevice.h" +//#include "plCamera.h" +#include "hsFogControl.h" +#include "../plGRenderProcs/hsGRenderProcs.h" +#if 0 // SCENENODE_DEFER +#include "hsScene.h" +#endif // SCENENODE_DEFER +#include "../plResMgr/hsResMgr.h" +#include "hsGStatGather3.h" +#include "../plResMgr/plKey.h" +#include "hsTimer.h" +#include "../plResMgr/plRefMsg.h" + +const UInt16 hsGEnvironment::kSaveMagicNumber = 0x1f7a; +const UInt16 hsGEnvironment::kSaveVersion = 2; + +hsScalar hsGEnvironment::fYonScale = 1.f; + +hsGEnvironment::hsGEnvironment() + : fFlags(0), + fValidScale(1.f), + fMap(nil), + fPos(0,0,0), + fRadius(0), + fFogDistance(0), + fFogDepth(0), + fCurrentDepth(0), + fFogDensity(0), + fDevCache(nil), + fYon(0), + fFogControl(nil) +{ + fFogColor.SetValue(hsColorRGBA().Set(0, 0, 0, hsScalar1)); + SetFogColor(fFogColor.GetValue()); + *fMapName = 0; +} + +hsGEnvironment::~hsGEnvironment() +{ + hsRefCnt_SafeUnRef(fMap); + hsRefCnt_SafeUnRef(fDevCache); + + hsRefCnt_SafeUnRef(fFogControl); + + Int32 i; + for (i=0; iGetKey()); +} +#endif // SCENENODE_DEFER + +hsBool32 hsGEnvironment::AddNodeKey(plKey *key) +{ + Int32 i; + for (i = 0; i < GetNumNodes(); i++) + { + if (GetNodeKey(i) == key) // nothing to do + return false; + } + fNodeKeys.Append(key); + + hsSceneNode *node = (hsSceneNode *)(key->GetObjectPtr()); +#if 0 // SCENENODE_DEFER + if (node) + { + if (!(GetFlags() & hsGEnvironment::kFarOut)) + { + node->SetEnvironment(this); + } + else + { + node->SetFarEnvironment(this); + } + } +#endif // SCENENODE_DEFER + + return true; +} + +hsSceneNode* hsGEnvironment::GetNode(Int32 i) +{ + return (hsSceneNode*)(fNodeKeys[i]->GetObjectPtr()); +} + +void hsGEnvironment::FogState::ValidateEnv(hsGEnvironment* env) +{ + char* msg = "hsGEnvironment not in reset state."; + if( fFlags & kYonSet ) + { + hsAssert(env->GetFlags() & hsGEnvironment::kYonSet, msg); + hsAssert(fYon == env->YonState(), msg); + } + + if( fFlags & kDistanceSet ) + { + hsAssert(env->GetFlags() & hsGEnvironment::kFogDistanceSet, msg); + hsAssert(fDistance == env->FogDistanceState(), msg); + } + + if( fFlags & kDepthSet ) + { + hsAssert(env->GetFlags() & hsGEnvironment::kFogDepthSet, msg); + hsAssert(fDepth == env->FogDepthState(), msg); + } + + if( fFlags & kDensitySet ) + { + hsAssert(env->GetFlags() & hsGEnvironment::kFogDensitySet, msg); + hsAssert(fDensity == env->FogDensityState(), msg); + } + + if( fFlags & kColorSet ) + { + hsAssert(env->GetFlags() & hsGEnvironment::kFogColorSet, msg); + hsAssert(fColor == env->FogColorState(), msg); + } + + if( fFlags & kClearSet ) + { + hsAssert(env->GetFlags() & hsGEnvironment::kClearColorSet, msg); + hsAssert(fClear == env->ClearColorState(), msg); + } + + UInt32 envFogType = env->GetFlags() & hsGEnvironment::kFogTypeMask; + switch( fFlags & kTypeMask ) + { + case kLinear: + hsAssert(envFogType == hsGEnvironment::kFogLinear, msg); + break; + case kExp: + hsAssert(envFogType == hsGEnvironment::kFogExp, msg); + break; + case kExp2: + hsAssert(envFogType == hsGEnvironment::kFogExp2, msg); + break; + default: + hsAssert(false, msg); + break; + } +} + +void hsGEnvironment::FogState::SetFromEnv(hsGEnvironment* env) +{ + if( env->GetFlags() & hsGEnvironment::kYonSet ) + { + fYon = env->YonState(); + fFlags |= kYonSet; + } + if( env->GetFlags() & hsGEnvironment::kFogDistanceSet ) + { + fDistance = env->FogDistanceState(); + fFlags |= kDistanceSet; + } + if( env->GetFlags() & hsGEnvironment::kFogDepthSet ) + { + fDepth = env->FogDepthState(); + fFlags |= kDepthSet; + } + if( env->GetFlags() & hsGEnvironment::kFogDensitySet ) + { + fDensity = env->FogDensityState(); + fFlags |= kDensitySet; + } + if( env->GetFlags() & hsGEnvironment::kFogColorSet ) + { + fColor = env->FogColorState(); + fFlags |= kColorSet; + } + if( env->GetFlags() & hsGEnvironment::kClearColorSet ) + { + fClear = env->ClearColorState(); + fFlags |= kClearSet; + } + + fFlags &= ~kTypeMask; + switch( env->GetFlags() & hsGEnvironment::kFogTypeMask ) + { + case hsGEnvironment::kFogLinear: + fFlags |= kLinear; + break; + case hsGEnvironment::kFogExp: + fFlags |= kExp; + break; + case hsGEnvironment::kFogExp2: + fFlags |= kExp2; + break; + default: + hsAssert(false, "Fog type should at least default at load"); + break; + } +} + +void hsGEnvironment::FogState::SetToEnv(hsGEnvironment* env) +{ + if( fFlags & kYonSet ) + env->SetYon(fYon); + else + env->UnSetYon(); + + if( fFlags & kDistanceSet ) + env->SetFogDistance(fDistance); + else + env->UnSetFogDistance(); + + if( fFlags & kDepthSet ) + { + env->SetFogDepth(fDepth); + env->SetCurrentDepth(fDepth.GetValue()); + } + else + env->UnSetFogDepth(); + + if( fFlags & kDensitySet ) + env->SetFogDensity(fDensity); + else + env->UnSetFogDensity(); + + if( fFlags & kColorSet ) + env->SetFogColor(fColor); + else + env->UnSetFogColor(); + + if( fFlags & kClearSet ) + env->SetClearColor(fClear); + else + env->UnSetClearColor(); + + switch( fFlags & kTypeMask ) + { + case kLinear: + env->SetFogType(hsGEnvironment::kFogLinear); + break; + case kExp: + env->SetFogType(hsGEnvironment::kFogExp); + break; + case kExp2: + env->SetFogType(hsGEnvironment::kFogExp2); + break; + default: + hsAssert(false, "Setting lack of fog type"); + env->SetFogType(0); + break; + } +} + +const UInt16 hsGEnvironment::FogState::kSaveMagicNumber = 0x7385; +const UInt16 hsGEnvironment::FogState::kSaveVersion = 1; + +void hsGEnvironment::FogState::Save(hsStream *stream, hsResMgr* mgr) +{ + stream->WriteSwap16(kSaveMagicNumber); + stream->WriteSwap16(kSaveVersion); + + hsScalar currSecs = hsTimer::GetSysSeconds(); + stream->WriteSwap32(fFlags); + fColor.Write(stream, currSecs); + fClear.Write(stream, currSecs); + fDistance.WriteScalar(stream, currSecs); + fDensity.WriteScalar(stream, currSecs); + fDepth.WriteScalar(stream, currSecs); + fYon.WriteScalar(stream, currSecs); +} + +void hsGEnvironment::FogState::Load(hsStream *stream, hsResMgr* mgr) +{ + UInt16 magic = stream->ReadSwap16(); + hsAssert(magic == kSaveMagicNumber, "Bad magic number in hsGEnvironment::FogState on load."); + + UInt16 version = stream->ReadSwap16(); + hsAssert(version == kSaveVersion, "Bad version in hsGEnvironment::FogState on load."); + + hsScalar currSecs = hsTimer::GetSysSeconds(); + fFlags = stream->ReadSwap32(); + fColor.Read(stream, currSecs); + fClear.Read(stream, currSecs); + fDistance.ReadScalar(stream, currSecs); + fDensity.ReadScalar(stream, currSecs); + fDepth.ReadScalar(stream, currSecs); + fYon.ReadScalar(stream, currSecs); +} + + +void hsGEnvironment::SetResetState() +{ + fResetState.SetFromEnv(this); +} + +void hsGEnvironment::Reset() +{ + fResetState.SetToEnv(this); + + Int32 i; + for (i=0; iWriteSwap16(kSaveMagicNumber); + stream->WriteSwap16(kSaveVersion); + + stream->WriteSwap32(fFlags); + + if( fFlags & hsGEnvironment::kCenterSet ) + fPos.Write(stream); + + if( fFlags & hsGEnvironment::kRadiusSet ) + stream->WriteSwapScalar(fRadius); + + FogState tempState; + tempState.SetFromEnv(this); + + tempState.Save(stream, mgr); + fResetState.Save(stream, mgr); + + UInt32 numFogStates = fFogStateStack.GetCount(); + stream->WriteSwap32(numFogStates); + for (UInt32 i = 0; i < numFogStates; i++) + { + fFogStateStack[i]->Save(stream, mgr); + } +} + +void hsGEnvironment::Load(hsStream *stream, hsResMgr* mgr) +{ + // Clear old data + UInt32 i; + for (i=0; iReadSwap16(); + hsAssert(magic == kSaveMagicNumber, "Bad magic number in hsGEnvironment on load."); + + UInt16 version = stream->ReadSwap16(); + hsAssert(version == kSaveVersion, "Bad version in hsGEnvironment on load."); + + fFlags = stream->ReadSwap32(); + + if( fFlags & hsGEnvironment::kCenterSet ) + fPos.Read(stream); + + if( fFlags & hsGEnvironment::kRadiusSet ) + fRadius = stream->ReadSwapScalar(); + + FogState tempState; + tempState.Load(stream, mgr); + tempState.SetToEnv(this); + fResetState.Load(stream, mgr); + + UInt32 numFogStates = stream->ReadSwap32(); + for (i = 0; i < numFogStates; i++) + { + FogState *newFogState = new FogState; + newFogState->Load(stream, mgr); + fFogStateStack.Append(newFogState); + } +} + +void hsGEnvironment::Update(hsScalar s, const hsPoint3& vPos) +{ + if( fFlags & kFogDepthSet ) + fFogDepth.Update(s); + if( fFlags & kFogDensitySet ) + fFogDensity.Update(s); + if( fFlags & kFogColorSet ) + fFogColor.Update(s); + if( fFlags & kClearColorSet ) + fClearColor.Update(s); + if( fFlags & kYonSet ) + fYon.Update(s); + if( fFlags & kMapSet ) + { + hsGStatGather3::UpdateMaterialBegin(); + fMap->Update(s); + hsGStatGather3::UpdateMaterialEnd(); + + if( fFlags & kClearColorAmbient ) + SetClearColor(fMap->GetLayer(0)->GetAmbientColor()); + + if( fFlags & kFogColorAmbient ) + SetFogColor(fMap->GetLayer(0)->GetAmbientColor()); + else + if( fFlags & kFogColorDiffuse ) + SetFogColor(fMap->GetLayer(0)->GetColor()); + } + + if( fFlags & kFogDistanceSet ) + { + fFogDistance.Update(s); + + + hsVector3 del(&vPos, &fPos); + hsScalar dist = del.Magnitude(); + + hsScalar yon = hsMaximum(dist, fFogDistance.GetValue()); + + SetYon(yon); + + hsScalar depth = fFogDistance.GetValue(); + if( fFlags & kFogDepthSet ) + depth *= fFogDepth.GetValue(); + + depth += yon - dist; + depth /= yon; + + if( depth > hsScalar1 ) + depth = hsScalar1; + + fCurrentDepth = depth; + fFlags |= kCurrentDepthSet; + } + else if( fFlags & kFogDepthSet ) + { + fCurrentDepth = fFogDepth.GetValue(); + fFlags |= kCurrentDepthSet; + } + else + fFlags &= ~kCurrentDepthSet; + + if( fValidScale != fYonScale ) + { + if( fDevCache ) + fDevCache->Validate(false); + fValidScale = fYonScale; + } +} + +void hsGEnvironment::SetTimedFogDistance(const hsScalar g, const hsScalar s) +{ + if( fFlags & kFogDistanceSet ) + { + fFogDistance.Update(hsTimer::GetSysSeconds()); + fFogDistance.SetGoal(g); + fFogDistance.SetDuration(s); + fFogDistance.StartClock(hsTimer::GetSysSeconds()); + } + else + SetFogDistance(g); +} + +void hsGEnvironment::SetTimedFogDepth(const hsScalar g, const hsScalar s) +{ + if( fFlags & kFogDepthSet ) + { + fFogDepth.Update(hsTimer::GetSysSeconds()); + fFogDepth.SetGoal(g); + fFogDepth.SetDuration(s); + fFogDepth.StartClock(hsTimer::GetSysSeconds()); + } + else + SetFogDepth(g); +} + +void hsGEnvironment::SetTimedFogDensity(const hsScalar g, const hsScalar s) +{ + if( fFlags & kFogDensitySet ) + { + fFogDensity.Update(hsTimer::GetSysSeconds()); + fFogDensity.SetGoal(g); + fFogDensity.SetDuration(s); + fFogDensity.StartClock(hsTimer::GetSysSeconds()); + } + else + SetFogDensity(g); +} + +void hsGEnvironment::SetTimedFogColor(const hsColorRGBA& g, const hsScalar s) +{ + if( fFlags & kFogColorSet ) + { + fFogColor.Update(hsTimer::GetSysSeconds()); + fFogColor.SetGoal(g); + fFogColor.SetDuration(s); + fFogColor.StartClock(hsTimer::GetSysSeconds()); + } + else + SetFogColor(g); +} + +void hsGEnvironment::SetTimedClearColor(const hsColorRGBA& g, const hsScalar s) +{ + if( fFlags & kClearColorSet ) + { + fClearColor.Update(hsTimer::GetSysSeconds()); + fClearColor.SetGoal(g); + fClearColor.SetDuration(s); + fClearColor.StartClock(hsTimer::GetSysSeconds()); + } + else + SetClearColor(g); +} + +void hsGEnvironment::SetTimedYon(const hsScalar g, const hsScalar s) +{ + if( fFlags & kYonSet ) + { + fYon.Update(hsTimer::GetSysSeconds()); + fYon.SetGoal(g); + fYon.SetDuration(s); + fYon.StartClock(hsTimer::GetSysSeconds()); + } + else + SetYon(g); +} + +void hsGEnvironment::SetMapName(const char *n) +{ + if( n ) + { + hsAssert(strlen(n) < 255, "Environment name overflow"); + fFlags |= kMapSet; + strcpy(fMapName, n); + } + else + { + fFlags &= ~kMapSet; + *fMapName = 0; + } +} + +void hsGEnvironment::SetMap(hsGMaterial *m) +{ + fFlags |= kMapSet; + hsRefCnt_SafeAssign(fMap, m); +} + +void hsGEnvironment::SetCenter(const hsPoint3 &p) +{ + fFlags |= kCenterSet; fPos = p; +} + +void hsGEnvironment::SetRadius(hsScalar r) +{ + fFlags |= kRadiusSet; + fRadius = r; +} + +void hsGEnvironment::SetFogDistance(hsScalar f) +{ + if( fDevCache &&( f != fFogDistance.GetValue()) ) + fDevCache->Validate(false); + fFlags |= kFogDistanceSet; + fFogDistance.SetValue(f); +} + +void hsGEnvironment::SetCurrentDepth(hsScalar f) +{ + if( fDevCache && (f != fCurrentDepth) ) + fDevCache->Validate(false); + fFlags |= kCurrentDepthSet; + fCurrentDepth = f; +} + +void hsGEnvironment::SetFogDepth(hsScalar f) +{ + if( fDevCache && (f != fFogDepth.GetValue()) ) + fDevCache->Validate(false); + fFlags |= kFogDepthSet; + fFogDepth.SetValue(f); +} + +void hsGEnvironment::SetFogDensity(hsScalar f) +{ + if( fDevCache && (f != fFogDensity.GetValue()) ) + fDevCache->Validate(false); + fFlags |= kFogDensitySet; + fFogDensity.SetValue(f); +} + +void hsGEnvironment::SetFogColor(const hsColorRGBA &c) +{ + if( fDevCache + && ( + (c.r != fFogColor.GetValue().r) + || (c.g != fFogColor.GetValue().g) + || (c.b != fFogColor.GetValue().b) + ) ) + fDevCache->Validate(false); + fFlags |= kFogColorSet; + fFogColor.SetValue(c); +} + +void hsGEnvironment::SetClearColor(const hsColorRGBA &c) +{ + fFlags |= kClearColorSet; + fClearColor.SetValue(c); +} + +void hsGEnvironment::SetYon(hsScalar f) +{ + if( fDevCache && (f != fYon.GetValue()) ) + fDevCache->Validate(false); + fFlags |= kYonSet; + fYon.SetValue(f); +} + +void hsGEnvironment::SetFogDistance(const hsTimedValue& f) +{ + if( fDevCache ) + fDevCache->Validate(false); + fFlags |= kFogDistanceSet; + fFogDistance = f; +} + +void hsGEnvironment::SetFogDepth(const hsTimedValue& f) +{ + if( fDevCache ) + fDevCache->Validate(false); + fFlags |= kFogDepthSet; + fFogDepth = f; +} + +void hsGEnvironment::SetFogDensity(const hsTimedValue& f) +{ + if( fDevCache ) + fDevCache->Validate(false); + fFlags |= kFogDensitySet; + fFogDensity = f; +} + +void hsGEnvironment::SetFogColor(const hsTimedValue& c) +{ + if( fDevCache ) + fDevCache->Validate(false); + fFlags |= kFogColorSet; + fFogColor = c; +} + +void hsGEnvironment::SetClearColor(const hsTimedValue& c) +{ + fFlags |= kClearColorSet; + fClearColor = c; +} + +void hsGEnvironment::SetYon(const hsTimedValue& f) +{ + if( fDevCache ) + fDevCache->Validate(false); + fFlags |= kYonSet; + fYon = f; +} + +void hsGEnvironment::SetFogType(UInt32 t) +{ + fFlags &= ~kFogTypeMask; + fFlags |= (t & kFogTypeMask); +} + +void hsGEnvironment::SetFogColorAmbient(hsBool32 on) +{ + if( on ) + { + fFlags &= ~kFogColorDiffuse; + fFlags |= kFogColorAmbient; + } + else + fFlags &= ~kFogColorAmbient; +} + +void hsGEnvironment::SetFogColorDiffuse(hsBool32 on) +{ + if( on ) + { + fFlags &= ~kFogColorAmbient; + fFlags |= kFogColorDiffuse; + } + else + fFlags &= ~kFogColorDiffuse; +} + +void hsGEnvironment::SetClearColorAmbient(hsBool32 on) +{ + if( on ) + fFlags |= kClearColorAmbient; + else + fFlags &= ~kClearColorAmbient; +} + +void hsGEnvironment::SetOverride(hsBool32 on) +{ + if( on ) + fFlags |= kOverride; + else + fFlags &= ~kOverride; +} + +void hsGEnvironment::SetIsFar(hsBool32 on) +{ + if( on ) + fFlags |= kFarOut; + else + fFlags &= ~kFarOut; +} + +void hsGEnvironment::SetSortObjects(hsBool32 on) +{ + if( on ) + fFlags |= kSortObjects; + else + fFlags &= ~kSortObjects; +} + +void hsGEnvironment::SetHasFogControl(hsBool32 on) +{ + if( on ) + fFlags |= kFogControl; + else + fFlags &= ~kFogControl; +} + +void hsGEnvironment::SetFogControl(hsFogControl* fc) +{ + hsRefCnt_SafeAssign(fFogControl, fc); +} + +void hsGEnvironment::SetDeviceCache(hsGDevEnvCache *p) +{ + hsRefCnt_SafeAssign(fDevCache, p); +} + +hsGEnvironment *hsGEnvironment::Copy(hsGEnvironment *env) +{ + if( env->GetFlags() & kMapSet ) + SetMap(env->GetMap()); + else + fFlags &= ~kMapSet; + if( env->GetFlags() & kCenterSet ) + SetCenter(env->GetCenter()); + else + fFlags &= ~kCenterSet; + if( env->GetFlags() & kRadiusSet ) + SetRadius(env->GetRadius()); + else + fFlags &= ~kRadiusSet; + if( env->GetFlags() & kFogDepthSet ) + SetFogDepth(env->GetFogDepth()); + else + fFlags &= ~kFogDepthSet; + if( env->GetFlags() & kFogDensitySet ) + SetFogDensity(env->GetFogDensity()); + else + fFlags &= ~kFogDensitySet; + if( env->GetFlags() & kFogColorSet ) + SetFogColor(env->GetFogColor()); + else + fFlags &= ~kFogColorSet; + if( env->GetFlags() & kClearColorSet ) + SetClearColor(env->GetClearColor()); + else + fFlags &= ~kClearColorSet; + if( env->GetFlags() & kYonSet ) + SetYon(env->GetUnscaledYon()); + else + fFlags &= ~kYonSet; + + if( env->GetFlags() & kCurrentDepthSet ) + { + fCurrentDepth = env->GetCurrentDepth(); + fFlags |= kCurrentDepthSet; + } + + fFlags &= ~hsGEnvironment::kFogTypeMask; + fFlags |= env->GetFlags() & hsGEnvironment::kFogTypeMask; + + SetOverride(env->GetOverride()); + + SetDeviceCache(env->GetDeviceCache()); + + if( env->GetFlags() & kCacheInvalid ) + fFlags |= kCacheInvalid; + else + fFlags &= ~kCacheInvalid; + + int i; + for( i = 0; i < env->GetNumRenderProcs(); i++ ) + AddRenderProc(env->GetRenderProc(i)); + + return this; +} + +void hsGEnvironment::MixEnvirons(hsGEnvironment *env, hsGEnvironment *def) +{ + if( env && (env->GetFlags() & kMapSet) ) + { + SetMap(env->GetMap()); + } + else if( def && (def->GetFlags() & kMapSet) ) + { + hsGMaterial *map = def->GetMap(); + if( map ) + { + SetMap(def->GetMap()); + } + else + { + def->SetMapName(nil); + hsRefCnt_SafeUnRef(fMap); + fMap = nil; + fFlags &= ~kMapSet; + } + } + else + { + hsRefCnt_SafeUnRef(fMap); + fMap = nil; + fFlags &= ~kMapSet; + } + if( env && (env->GetFlags() & kCenterSet) ) + SetCenter(env->GetCenter()); + else if( def &&(def->GetFlags() & kCenterSet) ) + SetCenter(def->GetCenter()); + else + fFlags &= ~kCenterSet; + if( env && (env->GetFlags() & kRadiusSet) ) + SetRadius(env->GetRadius()); + else if( def &&(def->GetFlags() & kRadiusSet) ) + SetRadius(def->GetRadius()); + else + fFlags &= ~kRadiusSet; + + if( env && (env->GetFlags() & kFogDepthSet) ) + SetFogDepth(env->GetFogDepth()); + else if( def &&(def->GetFlags() & kFogDepthSet) ) + SetFogDepth(def->GetFogDepth()); + else + fFlags &= ~kFogDepthSet; + + if( env &&(env->GetFlags() & kCurrentDepthSet) ) + { + fCurrentDepth = env->GetCurrentDepth(); + fFlags |= kCurrentDepthSet; + } + else + if( def &&(def->GetFlags() & kCurrentDepthSet) ) + { + fCurrentDepth = def->GetCurrentDepth(); + fFlags |= kCurrentDepthSet; + } + else + fFlags &= ~kCurrentDepthSet; + + fFlags &= ~kFogTypeMask; + if( env && (env->GetFlags() & kFogTypeMask) ) + fFlags |= env->GetFlags() & kFogTypeMask; + else if( def && (def->GetFlags() & kFogTypeMask) ) + fFlags |= def->GetFlags() & kFogTypeMask; + else + fFlags &= ~kFogTypeMask; + + if( env && (env->GetFlags() & kFogDensitySet) ) + SetFogDensity(env->GetFogDensity()); + else if( def &&(def->GetFlags() & kFogDensitySet) ) + SetFogDensity(def->GetFogDensity()); + else + SetFogDensity(hsScalar1); + + if( env &&(env->GetFlags() & kFogColorSet) ) + SetFogColor(env->GetFogColor()); + else if( def &&(def->GetFlags() & kFogColorSet) ) + SetFogColor(def->GetFogColor()); + else + { + hsColorRGBA col; + col.r = col.g = col.b = col.a = 0; + SetFogColor(col); + } + if( env &&(env->GetFlags() & kClearColorSet) ) + SetClearColor(env->GetClearColor()); + else if( def &&(def->GetFlags() & kClearColorSet) ) + SetClearColor(def->GetClearColor()); + else + fFlags &= ~kClearColorSet; + + if( env && (env->GetFlags() & kYonSet) ) + SetYon(env->GetUnscaledYon()); + else if( def &&(def->GetFlags() & kYonSet) ) + SetYon(def->GetUnscaledYon()); + else + fFlags &= ~kYonSet; + + if( env && (env->GetOverride()) ) + SetOverride(true); + else if( def &&(def->GetOverride()) ) + SetOverride(true); + else + SetOverride(false); + + if( env && env->GetDeviceCache() ) + SetDeviceCache(env->GetDeviceCache()); + else if( def && def->GetDeviceCache() ) + SetDeviceCache(def->GetDeviceCache()); + else + SetDeviceCache(nil); + + int i; + for( i = 0; i < GetNumRenderProcs(); i++ ) + { + hsRefCnt_SafeUnRef(GetRenderProc(i)); + } + fRenderProcs.Reset(); + if( env ) + { + for( i = 0; i < env->GetNumRenderProcs(); i++ ) + AddRenderProc(env->GetRenderProc(i)); + } + if( def ) + { + for( i = 0; i < def->GetNumRenderProcs(); i++ ) + AddRenderProc(def->GetRenderProc(i)); + } + +} + +void hsGEnvironment::Blend() +{ + if( fFogControl ) + fFogControl->Blend(); +} + +void hsGEnvironment::Restore() +{ + if( fFogControl ) + fFogControl->Restore(); +} + +void hsGEnvironment::Init(hsSceneNode* node) +{ + if (!node) + { + char str[256]; + sprintf(str, "hsGEnvironment %s initted with nil room. Weirdness may result.", + GetKeyName()); + HSDebugProc(str); + } + else + { + if( fFogControl ) + fFogControl->Init(node); + } + + SetResetState(); +} + +void hsGEnvironment::SaveFogState() +{ + FogState* curFogState = new FogState; + curFogState->SetFromEnv(this); + fFogStateStack.Push(curFogState); +} + +void hsGEnvironment::RestoreFogState() +{ + FogState* curFogState = fFogStateStack.GetCount() ? fFogStateStack.Pop() : nil; + if (curFogState) + { + curFogState->SetToEnv(this); + } + delete curFogState; +} + +void hsGEnvironment::Read(hsStream *stream) +{ + fFlags = stream->ReadSwap32(); + + if( fFlags & hsGEnvironment::kFogDistanceSet ) + { + hsScalar d = stream->ReadSwapScalar(); + SetFogDistance(d); + } + if( fFlags & hsGEnvironment::kFogDepthSet ) + { + hsScalar d = stream->ReadSwapScalar(); + SetFogDepth(d); + SetCurrentDepth(d); + } + if( fFlags & hsGEnvironment::kFogDensitySet ) + { + hsScalar d = stream->ReadSwapScalar(); + SetFogDensity(d); + } + if( fFlags & hsGEnvironment::kFogColorSet ) + { + hsColorRGBA c; + c.r = stream->ReadSwapScalar(); + c.g = stream->ReadSwapScalar(); + c.b = stream->ReadSwapScalar(); + c.a = stream->ReadSwapScalar(); + SetFogColor(c); + } + if( fFlags & hsGEnvironment::kClearColorSet ) + { + hsColorRGBA c; + c.r = stream->ReadSwapScalar(); + c.g = stream->ReadSwapScalar(); + c.b = stream->ReadSwapScalar(); + c.a = stream->ReadSwapScalar(); + SetClearColor(c); + } + if( fFlags & hsGEnvironment::kYonSet ) + { + hsScalar d = stream->ReadSwapScalar(); + SetYon(d); + } + + if( fFlags & hsGEnvironment::kCenterSet ) + fPos.Read(stream); + if( fFlags & hsGEnvironment::kRadiusSet ) + fRadius = stream->ReadSwapScalar(); + // still don't write the map out + + if( !(fFlags & kFogTypeMask) ) + { + if( GetFogDepth() <= 0.5f ) + fFlags |= kFogLinear; + else + fFlags |= kFogExp; + } +} + +void hsGEnvironment::Write(hsStream *stream) +{ + stream->WriteSwap32(fFlags); + + if( fFlags & hsGEnvironment::kFogDistanceSet ) + stream->WriteSwapScalar(GetFogDistance()); + if( fFlags & hsGEnvironment::kFogDepthSet ) + stream->WriteSwapScalar(GetFogDepth()); + if( fFlags & hsGEnvironment::kFogDensitySet ) + stream->WriteSwapScalar(GetFogDensity()); + if( fFlags & hsGEnvironment::kFogColorSet ) + { + hsColorRGBA c = GetFogColor(); + stream->WriteSwapScalar(c.r); + stream->WriteSwapScalar(c.g); + stream->WriteSwapScalar(c.b); + stream->WriteSwapScalar(c.a); + } + if( fFlags & hsGEnvironment::kClearColorSet ) + { + hsColorRGBA c = GetClearColor(); + stream->WriteSwapScalar(c.r); + stream->WriteSwapScalar(c.g); + stream->WriteSwapScalar(c.b); + stream->WriteSwapScalar(c.a); + } + if( fFlags & hsGEnvironment::kYonSet ) + stream->WriteSwapScalar(GetUnscaledYon()); + + if( fFlags & hsGEnvironment::kCenterSet ) + fPos.Write(stream); + if( fFlags & hsGEnvironment::kRadiusSet ) + stream->WriteSwapScalar(fRadius); + // still don't write the map out + +} + +void hsGEnvironment::Write(hsStream* stream, hsResMgr* group) +{ + Write(stream); + + if( fFlags & hsGEnvironment::kHasRenderProcs ) + { + int n = GetNumRenderProcs(); + stream->WriteSwap32(n); + int i; + for( i = 0; i < n; i++ ) + { +hsAssert(0,"Its Pauls fault"); +// plFactory::LabelAndWrite(stream, group, GetRenderProc(i)); + } + } + + if ((GetFlags() & hsGEnvironment::kEnvironMapSet) == hsGEnvironment::kEnvironMapSet) + { + group->WriteKey(stream, GetMap()); + } + + stream->WriteSwap32(fNodeKeys.GetCount()); + Int32 i; + for (i = 0; i < fNodeKeys.GetCount(); i++) + { + group->WriteKey(stream, fNodeKeys[i]); + } + + if( fFlags & hsGEnvironment::kFogControl ) + IWriteFogControl(stream, group); +} + +void hsGEnvironment::Read(hsStream* stream, hsResMgr* group) +{ + Read(stream); + + if( fFlags & hsGEnvironment::kHasRenderProcs ) + { + int n = stream->ReadSwap32(); + int i; + for( i = 0; i < n; i++ ) + { +hsAssert(0,"Its Pauls fault"); +// hsGRenderProcs* rp = hsGRenderProcs::ConvertNoRef(plFactory::CreateAndRead(stream, group)); +// AddRenderProc(rp); +// hsRefCnt_SafeUnRef(rp); + } + } + + if ((GetFlags() & hsGEnvironment::kEnvironMapSet) == hsGEnvironment::kEnvironMapSet) + { + plRefMsg* refMsg = new plRefMsg(GetKey(), plRefMsg::kOnCreate); + group->ReadKeyNotifyMe(stream,refMsg); + } + + hsAssert(fNodeKeys.GetCount() == 0, "fNodeKeys not empty in hsGEnvironment::Read."); + Int32 nodeCount = stream->ReadSwap32(); + hsAssert(nodeCount > 0, "Environment node not in any rooms in hsGEnvironment::Read."); + Int32 i; + for (i = 0; i < nodeCount; i++) + { + plKey *key = group->ReadKey(stream); + AddNodeKey(key); + } + + if( fFlags & hsGEnvironment::kFogControl ) + IReadFogControl(stream, group); +} + +void hsGEnvironment::IReadFogControl(hsStream* s, hsResMgr* mgr) +{ +hsAssert(0,"Its Pauls fault"); +// hsFogControl* fc = hsFogControl::ConvertNoRef(plFactory::CreateAndRead(s, mgr)); +// SetFogControl(fc); +// hsRefCnt_SafeUnRef(fc); + +} + +void hsGEnvironment::IWriteFogControl(hsStream* s, hsResMgr* mgr) +{ +hsAssert(0,"Its Pauls fault"); +// plFactory::LabelAndWrite(s, mgr, fFogControl); +} + +void hsGEnvironment::Push(hsG3DDevice* d) +{ +#if 0 // Nuking old device - mf + int i; + + for( i = 0; i < GetNumRenderProcs(); i++ ) + d->AddRenderProc(GetRenderProc(i)); +#endif // Nuking old device - mf +} + +void hsGEnvironment::Pop(hsG3DDevice* d) +{ +#if 0 // Nuking old device - mf + int i; + for( i = 0; i < GetNumRenderProcs(); i++ ) + d->RemoveRenderProc(GetRenderProc(i)); +#endif // Nuking old device - mf +} + +void hsGEnvironment::AddRenderProc(hsGRenderProcs* rp) +{ + hsRefCnt_SafeRef(rp); + fRenderProcs.Append(rp); + fFlags |= hsGEnvironment::kHasRenderProcs; +} + +hsGRenderProcs* hsGEnvironment::GetRenderProc(int i) +{ + return fRenderProcs[i]; +} + +UInt32 hsGEnvironment::GetNumRenderProcs() +{ + return fRenderProcs.GetCount(); +} + +hsScalar hsGEnvironment::SetYonScale(hsScalar s) +{ + const hsScalar kMinYonScale = 0.05f; + const hsScalar kMaxYonScale = 100.f; + + if( s < kMinYonScale ) + s = kMinYonScale; + else if( s > kMaxYonScale ) + s = kMaxYonScale; + return fYonScale = s; +} + +hsBool hsGEnvironment::MsgReceive(plMessage* msg) +{ + plRefMsg* refMsg = plRefMsg::ConvertNoRef(msg); + + if( refMsg ) + { + + hsGMaterial *mat = hsGMaterial::ConvertNoRef(refMsg->GetRef()); + SetMap(mat); + } + return false; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGEnviron.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGEnviron.h new file mode 100644 index 00000000..cadbc307 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsGEnviron.h @@ -0,0 +1,273 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsGEnviron_inc +#define hsGEnviron_inc + +#include "hsGeometry3.h" // hsPoint3 +#include "../plInterp/hsTimedValue.h" +#include "../plResMgr/hsKeyedObject.h" +#include "hsColorRGBA.h" +#include "hsTemplates.h" + +// +//----------------------------------- +// Environment +//----------------------------------- +// + +class hsGMaterial; +class hsGDevEnvCache; +class hsFogControl; +class hsSceneNode; +class hsScene; +class hsGRenderProcs; +class hsG3DDevice; +class plKey; +class hsResMgr; + +class hsGEnvironment : public hsKeyedObject { +private: + static const UInt16 kSaveMagicNumber; + static const UInt16 kSaveVersion; + +public: + struct FogState { + private: + static const UInt16 kSaveMagicNumber; + static const UInt16 kSaveVersion; + + public: + FogState() : fFlags(0) {}; + enum { + kColorSet = 0x1, + kDistanceSet = 0x2, + kDepthSet = 0x4, + kDensitySet = 0x8, + kYonSet = 0x10, + kClearSet = 0x20, + + kLinear = 0x10000000, + kExp = 0x20000000, + kExp2 = 0x40000000, + kTypeMask = kLinear | kExp | kExp2 + }; + UInt32 fFlags; + hsTimedValue fColor; + hsTimedValue fClear; + hsTimedValue fDistance; + hsTimedValue fDensity; + hsTimedValue fDepth; + hsTimedValue fYon; + + void ValidateEnv(class hsGEnvironment* env); + void SetFromEnv(class hsGEnvironment* env); + void SetToEnv(class hsGEnvironment* env); + void Save(hsStream *stream, hsResMgr* mgr); + void Load(hsStream *stream, hsResMgr* mgr); + }; + + enum { + kMapSet = 0x1, + kCenterSet = 0x2, + kRadiusSet = 0x4, + kEnvironMapSet = kMapSet | kCenterSet | kRadiusSet, + kFogDepthSet = 0x8, + kFogColorSet = 0x10, + kFogDensitySet = 0x20, + kYonSet = 0x40, + kOverride = 0x80, + kFarOut = 0x100, + kFogDistanceSet = 0x200, + kCacheInvalid = 0x400, + kClearColorSet = 0x800, + kCurrentDepthSet = 0x1000, + kFogControl = 0x2000, + kSortObjects = 0x4000, + kHasRenderProcs = 0x8000, + kFogLinear = 0x10000, + kFogExp = 0x20000, + kFogExp2 = 0x40000, + kFogTypeMask = kFogLinear | kFogExp | kFogExp2, + kClearColorAmbient = 0x80000, + kFogColorAmbient = 0x100000, + kFogColorDiffuse = 0x200000 + }; +protected: + static hsScalar fYonScale; + + UInt32 fFlags; + hsGMaterial* fMap; + char fMapName[256]; + hsPoint3 fPos; + hsScalar fRadius; + hsScalar fValidScale; + hsTArray fFogStateStack; + hsTimedValue fFogDistance; + hsTimedValue fFogDepth; // value 0..1, as fraction of yon, 0 is no fog + hsTimedValue fFogDensity; + hsTimedValue fFogColor; + hsTimedValue fClearColor; + hsTimedValue fYon; + hsScalar fCurrentDepth; // function of Depth and Distance + + FogState fResetState; + hsGDevEnvCache* fDevCache; + + hsDynamicArray fRenderProcs; + hsTArray fNodeKeys; + + hsFogControl* fFogControl; + + void IReadFogControl(hsStream* s, hsResMgr* mgr); + void IWriteFogControl(hsStream* s, hsResMgr* mgr); + +public: + hsGEnvironment(); + virtual ~hsGEnvironment(); + + hsBool32 AddNode(hsSceneNode *node); + hsBool32 AddNodeKey(plKey *key); + Int32 GetNumNodes() { return fNodeKeys.GetCount(); } + hsSceneNode* GetNode(Int32 i); + plKey* GetNodeKey(Int32 i) { return fNodeKeys[i]; } + + char* GetMapName() { return fMapName; } + hsGMaterial* GetMap() const { return fMap; } + hsPoint3 GetCenter() const { return fPos; } + hsScalar GetRadius() const { return fRadius; } + hsScalar GetFogDistance() const { return fFogDistance.GetValue(); } + hsScalar GetFogDepth() const { return fFogDepth.GetValue(); } + hsScalar GetCurrentDepth() const { return fCurrentDepth; } + hsScalar GetFogDensity() const { return fFogDensity.GetValue(); } + hsColorRGBA GetFogColor() const { return fFogColor.GetValue(); } + hsColorRGBA GetClearColor() const { return fClearColor.GetValue(); } + hsScalar GetYon() const { return fYonScale * fYon.GetValue(); } + hsScalar GetUnscaledYon() const { return fYon.GetValue(); } + hsBool32 GetOverride() const { return 0 != (fFlags & kOverride); } + UInt32 GetFlags() const { return fFlags; } + + hsScalar GoalFogDistance() const { return fFogDistance.GetGoal(); } + hsScalar GoalFogDepth() const { return fFogDepth.GetGoal(); } + hsScalar GoalFogDensity() const { return fFogDensity.GetGoal(); } + hsColorRGBA GoalFogColor() const { return fFogColor.GetGoal(); } + hsColorRGBA GoalClearColor() const { return fClearColor.GetGoal(); } + hsScalar GoalYon() const { return fYon.GetGoal(); } + + const hsTimedValue& FogDistanceState() const { return fFogDistance; } + const hsTimedValue& FogDepthState() const { return fFogDepth; } + const hsTimedValue& FogDensityState() const { return fFogDensity; } + const hsTimedValue& FogColorState() const { return fFogColor; } + const hsTimedValue& ClearColorState() const { return fClearColor; } + const hsTimedValue& YonState() const { return fYon; } + + void SetFogDistance(const hsTimedValue& v); + void SetFogDepth(const hsTimedValue& v); + void SetFogDensity(const hsTimedValue& v); + void SetFogColor(const hsTimedValue& v); + void SetClearColor(const hsTimedValue& v); + void SetYon(const hsTimedValue& v); + + void SetMapName(const char *name); + void SetMap(hsGMaterial *m); // refs + void SetCenter(const hsPoint3 &p); + void SetRadius(hsScalar r); + void SetFogDistance(hsScalar d); + void SetFogDepth(hsScalar f); + void SetCurrentDepth(hsScalar f); + void SetFogDensity(hsScalar f); + void SetFogColor(const hsColorRGBA &c); + void SetClearColor(const hsColorRGBA &c); + void SetYon(hsScalar f); + void SetOverride(hsBool32 on); + void SetIsFar(hsBool32 on=true); + void SetHasFogControl(hsBool32 on=true); + void SetSortObjects(hsBool32 on=true); + void SetFogType(UInt32 t); + void SetFogColorAmbient(hsBool32 on=true); + void SetFogColorDiffuse(hsBool32 on=true); + void SetClearColorAmbient(hsBool32 on=true); + + void SetTimedFogDistance(const hsScalar g, const hsScalar s); + void SetTimedFogDepth(const hsScalar g, const hsScalar s); + void SetTimedFogDensity(const hsScalar g, const hsScalar s); + void SetTimedFogColor(const hsColorRGBA& g, const hsScalar s); + void SetTimedClearColor(const hsColorRGBA& g, const hsScalar s); + void SetTimedYon(const hsScalar g, const hsScalar s); + + void UnSetMapName() { *fMapName = 0; } + void UnSetEnvironMap() { fFlags &= ~kEnvironMapSet; } + void UnSetFogDistance() { fFlags &= ~kFogDistanceSet; } + void UnSetFogDepth() { fFlags &= ~kFogDepthSet; } + void UnSetFogDensity() { fFlags &= ~kFogDensitySet; } + void UnSetFogColor() { fFlags &= ~kFogColorSet; } + void UnSetClearColor() { fFlags &= ~kClearColorSet; } + void UnSetYon() { fFlags &= ~kYonSet; } + + hsGEnvironment* Copy(hsGEnvironment* env); // returns this + + void MixEnvirons(hsGEnvironment* env, hsGEnvironment* def); + + void Push(hsG3DDevice* d); + void Pop(hsG3DDevice* d); + + void Blend(); + void Restore(); + void Init(hsSceneNode* node); + + void SetDeviceCache(hsGDevEnvCache* p); + hsGDevEnvCache* GetDeviceCache(){ return fDevCache; } + + void SetFogControl(hsFogControl* fc); + hsFogControl* GetFogControl() { return fFogControl; } + + void SaveFogState(); // push + hsGEnvironment::FogState* PopFogState() { return fFogStateStack.GetCount() ? fFogStateStack.Pop() : nil; } // doesn't restore + void RestoreFogState(); // pop and restore + + void AddRenderProc(hsGRenderProcs* rp); // refs + hsGRenderProcs* GetRenderProc(int i); // no ref + UInt32 GetNumRenderProcs(); + + virtual void SetResetState(); + virtual void Reset(); + virtual void ValidateInResetState(); + virtual void Save(hsStream *stream, hsResMgr* mgr); + virtual void Load(hsStream *stream, hsResMgr* mgr); + virtual void Update(hsScalar secs, const hsPoint3& vPos); + + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); + + virtual void Write(hsStream *stream, hsResMgr *group); + virtual void Read(hsStream *stream, hsResMgr *group); + + static hsScalar GetYonScale() { return fYonScale; } + static hsScalar SetYonScale(hsScalar s); + + virtual hsBool MsgReceive(plMessage* msg); +}; + +#endif // hsGEnviron_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsWinRef.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsWinRef.h new file mode 100644 index 00000000..e1a5d8e6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/hsWinRef.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef hsWinRef_inc +#define hsWinRef_inc + + +#ifdef HS_BUILD_FOR_WIN32 + +#include "hsWindows.h" +typedef HWND hsWinRef; + +#elif HS_BUILD_FOR_MACPPC + +typedef WindowRef hsWinRef; + +#else // Whatever + +typedef void* hsWinRef; + +#endif + +#endif // hsWinRef_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCaptureRender.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCaptureRender.cpp new file mode 100644 index 00000000..b3e7ffee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCaptureRender.cpp @@ -0,0 +1,219 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plCaptureRender.h" + +#ifndef MF_FRONTBUFF_CAPTURE + +#include "../plGImage/plMipmap.h" +#include "../plMessage/plCaptureRenderMsg.h" +#include "plPipeline.h" +#include "plRenderTarget.h" +#include "../plScene/plPageTreeMgr.h" +#include "../plScene/plPostEffectMod.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plUoid.h" + +#include "../pfGameGUIMgr/pfGameGUIMgr.h" + +// CaptureRenderRequest +// +void plCaptureRenderRequest::Render(plPipeline* pipe, plPageTreeMgr* pageMgr) +{ + // If we don't have a render target, something has gone horribly wrong. + if( !GetRenderTarget() ) + { + hsAssert(false, "CaptureRenderRequest with no render target"); + return; + } + + // Set ourselves up like the current pipeline, except with our screen size. + plViewTransform vt = pipe->GetViewTransform(); + vt.SetViewPort(0, 0, fRenderTarget->GetWidth(), fRenderTarget->GetHeight()); + SetViewTransform(vt); + SetClearColor(pipe->GetClearColor()); + SetClearDepth(pipe->GetClearDepth()); + + // Clear our render target + // Render the scene + pipe->PushRenderRequest(this); + + pipe->ClearRenderTarget(); + + pageMgr->Render(pipe); + + pipe->PopRenderRequest(this); + + // set up state so we can clear the z-buffer for every gui dialog (and therefore not have it + // be obscured by other geometry) + SetRenderState(GetRenderState() & ~plPipeline::kRenderClearColor); + SetRenderState(GetRenderState() | plPipeline::kRenderClearDepth); + SetClearDepth(1); + + // render all GUI items + std::vector guiRenderMods = pfGameGUIMgr::GetInstance()->GetDlgRenderMods(); + for (int i = (int)guiRenderMods.size() - 1; i >= 0; i--) // render in reverse, so dialogs on the bottom get rendered first + { + plPageTreeMgr* dlgPageMgr = guiRenderMods[i]->GetPageMgr(); + if (dlgPageMgr) + { + SetViewTransform(guiRenderMods[i]->GetViewTransform()); + pipe->PushRenderRequest(this); + pipe->ClearRenderTarget(); + dlgPageMgr->Render(pipe); + pipe->PopRenderRequest(this); + } + } + + // Callback on plCaptureRender to process the render target into a mipmap + // and send it back to the requester. + plCaptureRender::IProcess(pipe, GetAck(), GetRenderTarget()); + + delete fRenderTarget; + fRenderTarget = nil; +} + +hsTArray plCaptureRender::fProcessed; + +// plCaptureRender::Capture +hsBool plCaptureRender::Capture(const plKey& ack, UInt16 width, UInt16 height) +{ + // Create our render target + const UInt16 flags = plRenderTarget::kIsOffscreen; + const UInt8 bitDepth(32); + const UInt8 zDepth(-1); + const UInt8 stencilDepth(-1); + plRenderTarget* rt = TRACKED_NEW plRenderTarget(flags, width, height, bitDepth, zDepth, stencilDepth); + + static int idx=0; + char buff[32]; + sprintf(buff, "tRT%d", idx++); + hsgResMgr::ResMgr()->NewKey(buff, rt, ack->GetUoid().GetLocation()); + + + // Create a render request and render request message + plCaptureRenderRequest* req = TRACKED_NEW plCaptureRenderRequest; + + const hsScalar pri(-100.f); + req->SetPriority(pri); + + req->SetRenderTarget(rt); + + const UInt32 renderState + = plPipeline::kRenderNormal + | plPipeline::kRenderClearColor + | plPipeline::kRenderClearDepth; + req->SetRenderState(renderState); + + // Set the Ack to be our requestor + req->RequestAck(ack); + + // Submit + plRenderRequestMsg* msg = TRACKED_NEW plRenderRequestMsg(ack, req); + hsRefCnt_SafeUnRef(req); + msg->Send(); + + return true; +} + +// plCaptureRender::IProcess +hsBool plCaptureRender::IProcess(plPipeline* pipe, const plKey& ack, plRenderTarget* targ) +{ + // We've just had a successful render into our render target + + // Copy that into a plMipmap + plMipmap* mipMap = pipe->ExtractMipMap(targ); + if( !mipMap ) + return false; + + static int currentCapIndex = 0; + + // Mipmap isn't created with a key so let's give it one now + char buff[512]; + sprintf(buff, "CaptureRender_%d", currentCapIndex++); + + hsgResMgr::ResMgr()->NewKey(buff, mipMap, plLocation::kGlobalFixedLoc); + mipMap->Ref(); + + // Stash it, and send it off during the update phase. + plCaptureRenderMsg* msg = TRACKED_NEW plCaptureRenderMsg(ack, mipMap); + fProcessed.Append(msg); + + return true; +} + +void plCaptureRender::Update() +{ + int i; + for( i = 0; i < fProcessed.GetCount(); i++ ) + { + fProcessed[i]->Send(); + } + fProcessed.SetCount(0); +} + + +#else // MF_FRONTBUFF_CAPTURE + +#include "plPipeline.h" +#include "plgDispatch.h" + +#include "../plMessage/plCaptureRenderMsg.h" +#include "../plGImage/plMipmap.h" + +hsTArray plCaptureRender::fCapReqs; + +void plCaptureRender::Update(plPipeline* pipe) +{ + int i; + + for( i = 0; i < fCapReqs.GetCount(); i++ ) + { + plMipmap* mipmap = TRACKED_NEW plMipmap(fCapReqs[i].fWidth, fCapReqs[i].fHeight, plMipmap::kARGB32Config, 1); + + pipe->CaptureScreen(mipmap, false, fCapReqs[i].fWidth, fCapReqs[i].fHeight); + + plCaptureRenderMsg* msg = TRACKED_NEW plCaptureRenderMsg(fCapReqs[i].fAck, mipmap); + msg->Send(); + } + + fCapReqs.Reset(); +} + +hsBool plCaptureRender::Capture(const plKey& ack, UInt16 width, UInt16 height) +{ + CapInfo capInfo; + capInfo.fAck = ack; + capInfo.fWidth = width; + capInfo.fHeight = height; + + fCapReqs.Append(capInfo); + + return true; +} + +#endif // MF_FRONTBUFF_CAPTURE diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCaptureRender.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCaptureRender.h new file mode 100644 index 00000000..2bec2571 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCaptureRender.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCaptureRender_inc +#define plCaptureRender_inc + +#ifndef MF_FRONTBUFF_CAPTURE + +#include "../plScene/plRenderRequest.h" + +class plRenderTarget; +class plCaptureRenderMsg; + +class plCaptureRenderRequest : public plRenderRequest +{ +public: + + virtual void Render(plPipeline* pipe, plPageTreeMgr* pageMgr); +}; + +class plCaptureRender +{ +protected: + static hsTArray fProcessed; + + static hsBool IProcess(plPipeline* pipe, const plKey& ack, plRenderTarget* targ); + + friend class plCaptureRenderRequest; + + // Only the client calls this (during the update phase). + static void Update(); + friend class plClient; +public: + + static hsBool Capture(const plKey& ack, UInt16 width=800, UInt16 height=600); + +}; + +#else // MF_FRONTBUFF_CAPTURE + +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" + +class plPipeline; + +class plCaptureRender +{ +protected: + class CapInfo + { + public: + plKey fAck; + UInt16 fWidth; + UInt16 fHeight; + }; + + static hsTArray fCapReqs; + + // Only the client calls this (during the update phase). + static void Update(plPipeline* pipe); + friend class plClient; +public: + + static hsBool Capture(const plKey& ack, UInt16 width=800, UInt16 height=600); + +}; + +#endif // MF_FRONTBUFF_CAPTURE + +#endif // plCaptureRender_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTarget.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTarget.h new file mode 100644 index 00000000..dabc42a1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTarget.h @@ -0,0 +1,121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plCubicRenderTarget Class Header // +// Derived renderTarget class representing a collection of render targets // +// to be used for DYNAMIC cubic environment mapping. // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 6.7.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plCubicRenderTarget_h +#define _plCubicRenderTarget_h + +#include "plRenderTarget.h" +#include "hsMatrix44.h" + + +//// Class Definition ///////////////////////////////////////////////////////// + +class plCubicRenderTarget : public plRenderTarget +{ + protected: + + //// Protected Members //// + + plRenderTarget *fFaces[6]; + hsMatrix44 fWorldToCameras[6]; + hsMatrix44 fCameraToWorlds[6]; + + public: + + //// Public Data //// + + enum Faces + { + kLeftFace = 0, + kRightFace, + kFrontFace, + kBackFace, + kTopFace, + kBottomFace + }; + + //// Public Members //// + + CLASSNAME_REGISTER( plCubicRenderTarget ); + GETINTERFACE_ANY( plCubicRenderTarget, plRenderTarget ); + + plCubicRenderTarget() + { + fFaces[0] = fFaces[1] = fFaces[2] = fFaces[3] = fFaces[4] = fFaces[5] = nil; + } + + plCubicRenderTarget( UInt16 flags, UInt16 width, UInt16 height, UInt8 bitDepth, UInt8 zDepth = -1, UInt8 sDepth = -1 ) + : plRenderTarget( flags, width, height, bitDepth, zDepth, sDepth ) + { + int i; + + + for( i = 0; i < 6; i++ ) + { + fFaces[i] = TRACKED_NEW plRenderTarget( flags, width, height, bitDepth, zDepth, sDepth ); + fFaces[i]->fParent = this; + fWorldToCameras[i].Reset(); + fCameraToWorlds[i].Reset(); + } + } + + virtual ~plCubicRenderTarget() + { + int i; + + + for( i = 0; i < 6; i++ ) + delete fFaces[i]; + } + + // Get the total size in bytes + virtual UInt32 GetTotalSize( void ) const; + + virtual void SetCameraMatrix(const hsPoint3& pos); + virtual const hsMatrix44& GetWorldToCamera(UInt8 face) const { return fWorldToCameras[face]; } + virtual const hsMatrix44& GetCameraToWorld(UInt8 face) const { return fCameraToWorlds[face]; } + + plRenderTarget *GetFace(UInt8 face) const { return fFaces[face]; } + + virtual UInt32 Read(hsStream *s); + virtual UInt32 Write(hsStream *s); + +}; + + +#endif // _plCubicRenderTarget_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTargetModifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTargetModifier.cpp new file mode 100644 index 00000000..69ba5caf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTargetModifier.cpp @@ -0,0 +1,243 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plCubicRenderTargetModifier Class Functions // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 7.20.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plCubicRenderTargetModifier.h" +#include "plCubicRenderTarget.h" +#include "plgDispatch.h" +#include "plPipeline.h" +#include "plDrawable.h" +#include "hsBounds.h" + +#include "../plScene/plRenderRequest.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../pnMessage/plTimeMsg.h" +#include "../plMessage/plRenderRequestMsg.h" +#include "hsResMgr.h" +#include "hsTimer.h" + + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plCubicRenderTargetModifier::plCubicRenderTargetModifier() +{ + fTarget = nil; + fCubic = nil; + + fRequests[ 0 ] = fRequests[ 1 ] = fRequests[ 2 ] = fRequests[ 3 ] = fRequests[ 4 ] = fRequests[ 5 ] = nil; +} + +plCubicRenderTargetModifier::~plCubicRenderTargetModifier() +{ + int i; + + + for( i = 0; i < 6; i++ ) + delete fRequests[ i ]; +} + +//// ICreateRenderRequest ///////////////////////////////////////////////////// +// Creates a blank renderRequest to use for fun stuff. + +void plCubicRenderTargetModifier::ICreateRenderRequest( int face ) +{ + plRenderRequest *rr = fRequests[ face ]; + hsColorRGBA c; + + + if( rr == nil ) + rr = fRequests[ face ] = TRACKED_NEW plRenderRequest; + + UInt32 renderState + = plPipeline::kRenderNormal + | plPipeline::kRenderClearColor + | plPipeline::kRenderClearDepth; + rr->SetRenderState( renderState ); + + rr->SetDrawableMask( plDrawable::kNormal ); + rr->SetSubDrawableMask( plDrawable::kSubAllTypes ); + + rr->SetHither(0.3f); // MF_HORSE ???? + rr->SetYon(1000.f); // MF_HORSE ???? + + rr->SetFovX( 90 ); + rr->SetFovY( 90 ); + + c.Set( 0, 0, 0, 1 ); + rr->SetClearColor( c ); + rr->SetClearDepth( 1.f ); + + rr->SetClearDrawable( nil ); + rr->SetRenderTarget( fCubic->GetFace( face ) ); +} + +//// IEval //////////////////////////////////////////////////////////////////// + +hsBool plCubicRenderTargetModifier::IEval( double secs, hsScalar del, UInt32 dirty ) +{ + hsPoint3 center; + hsMatrix44 mtx, invMtx; + int i; + + plRenderRequestMsg *msg; + + + if( fCubic == nil || fTarget == nil ) + return true; + + /// Get center point for RT + plCoordinateInterface *ci = IGetTargetCoordinateInterface( 0 ); + if( ci == nil ) + { + plDrawInterface *di = IGetTargetDrawInterface( 0 ); + center = di->GetWorldBounds().GetCenter(); + } + else + center = ci->GetLocalToWorld().GetTranslate(); + + /// Set camera position of RT to this center + fCubic->SetCameraMatrix(center); + + /// Submit render requests! + for( i = 0; i < 6; i++ ) + { + if( fRequests[ i ] != nil ) + { + fRequests[ i ]->SetCameraTransform(fCubic->GetWorldToCamera(i), fCubic->GetCameraToWorld(i)); + + msg = TRACKED_NEW plRenderRequestMsg( nil, fRequests[ i ] ); + plgDispatch::MsgSend( msg ); + } + } + + /// Done! + return true; +} + +//// MsgReceive /////////////////////////////////////////////////////////////// + +hsBool plCubicRenderTargetModifier::MsgReceive( plMessage* msg ) +{ + plSceneObject *scene; + plCubicRenderTarget *cubic; + int i; + + + plEvalMsg* eval = plEvalMsg::ConvertNoRef(msg); + if( eval ) + { + const double secs = eval->DSeconds(); + const hsScalar del = eval->DelSeconds(); + IEval( secs, del, 0 ); + return true; + } + plRefMsg *refMsg = plRefMsg::ConvertNoRef( msg ); + if( refMsg ) + { + if( scene = plSceneObject::ConvertNoRef( refMsg->GetRef() ) ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + AddTarget( scene ); + else + RemoveTarget( scene ); + } + if( cubic = plCubicRenderTarget::ConvertNoRef( refMsg->GetRef() ) ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + { + fCubic = cubic; + for( i = 0; i < 6; i++ ) + ICreateRenderRequest( i ); + } + else + { + fCubic = nil; + for( i = 0; i < 6; i++ ) + { + delete fRequests[ i ]; + fRequests[ i ] = nil; + } + } + } + return true; + } + + return plModifier::MsgReceive( msg ); +} + +//// AddTarget //////////////////////////////////////////////////////////////// + +void plCubicRenderTargetModifier::AddTarget( plSceneObject *so ) +{ + if( fTarget != nil ) + RemoveTarget( fTarget ); + + fTarget = so; + plgDispatch::Dispatch()->RegisterForExactType( plEvalMsg::Index(), GetKey() ); +} + +//// RemoveTarget ///////////////////////////////////////////////////////////// + +void plCubicRenderTargetModifier::RemoveTarget( plSceneObject *so ) +{ + fTarget = nil; +} + +//// Read ///////////////////////////////////////////////////////////////////// + +void plCubicRenderTargetModifier::Read( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Read( s, mgr ); + + plGenRefMsg* msg; + msg = TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, 0, 0 ); // SceneObject + mgr->ReadKeyNotifyMe( s, msg, plRefFlags::kActiveRef ); + + msg = TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, 0, 0 ); // cubicRT + mgr->ReadKeyNotifyMe( s, msg, plRefFlags::kActiveRef ); +} + +//// Write //////////////////////////////////////////////////////////////////// + +void plCubicRenderTargetModifier::Write( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Write(s, mgr); + + mgr->WriteKey( s, fTarget ); // Write the SceneNode + mgr->WriteKey( s, fCubic ); // Write the cubicRT +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTargetModifier.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTargetModifier.h new file mode 100644 index 00000000..13908fdc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCubicRenderTargetModifier.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plCubicRenderTargetModifier Class Header // +// Modifier applied to an object to specify the source object to render a // +// cubicRenderTarget from. Owns a key to the cubicRenderTarget in question, // +// which it updates with positions and submits renderRequests for. // +// // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 7.20.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plCubicRenderTargetModifier_h +#define _plCubicRenderTargetModifier_h + +#include "../../NucleusLib/pnModifier/plModifier.h" +#include "../pnNetCommon/plSynchedValue.h" + + +class plCubicRenderTarget; +class plRenderRequest; + +//// Class Definition ///////////////////////////////////////////////////////// + +class plCubicRenderTargetModifier : public plModifier +{ + +public: + plCubicRenderTargetModifier(); + virtual ~plCubicRenderTargetModifier(); + + CLASSNAME_REGISTER( plCubicRenderTargetModifier ); + GETINTERFACE_ANY( plCubicRenderTargetModifier, plModifier); + + // Functions related to/required by plModifier + virtual int GetNumTargets( void ) const { return fTarget ? 1 : 0; } + virtual plSceneObject *GetTarget( int w ) const { hsAssert(w < GetNumTargets(), "Bad target"); return fTarget; } + virtual void AddTarget( plSceneObject* so ); + virtual void RemoveTarget( plSceneObject* so ); + + virtual void Read( hsStream* s, hsResMgr* mgr ); + virtual void Write( hsStream* s, hsResMgr* mgr ); + virtual hsBool MsgReceive( plMessage* msg ); + +protected: + + plSceneObject *fTarget; + plCubicRenderTarget *fCubic; + + plRenderRequest *fRequests[ 6 ]; + + virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // required by plModifier + + void ICreateRenderRequest( int face ); + +}; + + +#endif //_plCubicRenderTargetModifier_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCullTree.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCullTree.cpp new file mode 100644 index 00000000..2997b09c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCullTree.cpp @@ -0,0 +1,1071 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plCullTree.h" +#include "../plDrawable/plSpaceTree.h" +#include "hsFastMath.h" +#include "hsColorRGBA.h" +#include "plProfile.h" + +#include "plTweak.h" + +#define MF_DEBUG_NORM +#ifdef MF_DEBUG_NORM + +#define IDEBUG_NORMALIZE( a, b ) { hsScalar len = hsFastMath::InvSqrtAppr((a).MagnitudeSquared()); a *= len; b *= len; } + +#else // MF_DEBUG_NORM +#define IDEBUG_NORMALIZE( a, b ) +#endif // MF_DEBUG_NORM + +//#define CULL_SMALL_TOLERANCE +#ifdef CULL_SMALL_TOLERANCE +//static const hsScalar kTolerance = 1.e-5f; +static const hsScalar kTolerance = 1.e-3f; +#else //CULL_SMALL_TOLERANCE +static const hsScalar kTolerance = 1.e-1f; +#endif // CULL_SMALL_TOLERANCE + +plProfile_CreateCounter("Harvest Nodes", "Draw", HarvestNodes); + +////////////////////////////////////////////////////////////////////// +// Harvest culling section. +// These are the functions used on a built tree +////////////////////////////////////////////////////////////////////// +plCullNode::plCullStatus plCullNode::ITestBoundsRecur(const hsBounds3Ext& bnd) const +{ + plCullNode::plCullStatus retVal = TestBounds(bnd); + + // No Children, what we say goes. + if( (fOuterChild < 0) && (fInnerChild < 0) ) + return retVal; + + // No innerchild. If we cull, it's culled, else we + // hope our outerchild culls it. + if( fInnerChild < 0 ) + { + if( retVal == kCulled ) + return kCulled; + + return IGetNode(fOuterChild)->ITestBoundsRecur(bnd); + } + + // No outerchild. If we say it's clear, it's clear (or split), but if + // it's culled, we have to pass it to innerchild, who may pronounce it clear + if( fOuterChild < 0 ) + { + if( retVal == kClear ) + return kClear; + if( retVal == kSplit ) + return kSplit; + return IGetNode(fInnerChild)->ITestBoundsRecur(bnd); + } + + // We've got both children to feed. + // We pass the clear ones to the inner child, culled to outer, + // and split to both. Remember, a both children have to agree to cull a split. + if( retVal == kClear ) + return IGetNode(fOuterChild)->ITestBoundsRecur(bnd); + + if( retVal == kCulled ) + return IGetNode(fInnerChild)->ITestBoundsRecur(bnd); + + // Here's the split, to be culled, both children have to + // say its culled. + if( kCulled != IGetNode(fOuterChild)->ITestBoundsRecur(bnd) ) + return kSplit; + + if( kCulled != IGetNode(fInnerChild)->ITestBoundsRecur(bnd) ) + return kSplit; + + return kCulled; +} + +plCullNode::plCullStatus plCullNode::TestBounds(const hsBounds3Ext& bnd) const +{ + // Not sure if doing a sphere test will pay off or not. Some circumstantial evidence + // from TrueTime suggests it could very well, but I really need to do some side by + // side timings to be sure. Still looking for some reasonably constructed real data sets. mf +#define MF_TEST_SPHERE_FIRST +#ifdef MF_TEST_SPHERE_FIRST + hsScalar dist = fNorm.InnerProduct(bnd.GetCenter()) + fDist; + hsScalar rad = bnd.GetRadius(); + if( dist < -rad ) + return kCulled; + if( dist > rad ) + return kClear; +#endif // MF_TEST_SPHERE_FIRST + + hsPoint2 depth; + bnd.TestPlane(fNorm, depth); + + const hsScalar kSafetyDist = -0.1f; + if( depth.fY + fDist < kSafetyDist ) + return kCulled; + + if( depth.fX + fDist >= 0 ) + return kClear; + + return kSplit; +} + +plCullNode::plCullStatus plCullNode::ITestSphereRecur(const hsPoint3& center, hsScalar rad) const +{ + plCullNode::plCullStatus retVal = TestSphere(center, rad); + + // No Children, what we say goes. + if( (fOuterChild < 0) && (fInnerChild < 0) ) + return retVal; + + // No innerchild. If we cull, it's culled, else we + // hope our outerchild culls it. + if( fInnerChild < 0 ) + { + if( retVal == kCulled ) + return kCulled; + + return IGetNode(fOuterChild)->ITestSphereRecur(center, rad); + } + + // No outerchild. If we say it's clear, it's clear (or split), but if + // it's culled, we have to pass it to innerchild, who may pronounce it clear + if( fOuterChild < 0 ) + { + if( retVal == kClear ) + return kClear; + if( retVal == kSplit ) + return kSplit; + return IGetNode(fInnerChild)->ITestSphereRecur(center, rad); + } + + // We've got both children to feed. + // We pass the clear ones to the inner child, culled to outer, + // and split to both. Remember, a both children have to agree to cull a split. + if( retVal == kClear ) + return IGetNode(fOuterChild)->ITestSphereRecur(center, rad); + + if( retVal == kCulled ) + return IGetNode(fInnerChild)->ITestSphereRecur(center, rad); + + // Here's the split, to be culled, both children have to + // say its culled. + if( kCulled != IGetNode(fOuterChild)->ITestSphereRecur(center, rad) ) + return kSplit; + + if( kCulled != IGetNode(fInnerChild)->ITestSphereRecur(center, rad) ) + return kSplit; + + return kCulled; +} + +plCullNode::plCullStatus plCullNode::TestSphere(const hsPoint3& center, hsScalar rad) const +{ + hsScalar dist = fNorm.InnerProduct(center) + fDist; + if( dist < -rad ) + return kCulled; + if( dist > rad ) + return kClear; + + return kSplit; +} + +// For this Cull Node, recur down the space hierarchy pruning out who to test for the next Cull Node. +plCullNode::plCullStatus plCullNode::ITestNode(const plSpaceTree* space, Int16 who, hsLargeArray& clear, hsLargeArray& split, hsLargeArray& culled) const +{ + if( space->IsDisabled(who) || (space->GetNode(who).fWorldBounds.GetType() != kBoundsNormal) ) + { + culled.Append(who); + return kCulled; + } + + plCullStatus retVal = kClear; + plCullStatus stat = TestBounds(space->GetNode(who).fWorldBounds); + + switch( stat ) + { + case kClear: + clear.Append(who); + retVal = kClear; + break; + case kCulled: + culled.Append(who); + retVal = kCulled; + break; + case kSplit: + if( space->GetNode(who).fFlags & plSpaceTreeNode::kIsLeaf ) + { +// split.Append(who); + retVal = kPureSplit; + } + else + { + plCullStatus child0 = ITestNode(space, space->GetNode(who).GetChild(0), clear, split, culled); + plCullStatus child1 = ITestNode(space, space->GetNode(who).GetChild(1), clear, split, culled); + + if( child0 != child1 ) + { + if( child0 == kPureSplit ) + split.Append(space->GetNode(who).GetChild(0)); + else if( child1 == kPureSplit ) + split.Append(space->GetNode(who).GetChild(1)); + retVal = kSplit; + } + else if( child0 == kPureSplit ) + { + retVal = kPureSplit; + } + } + } + return retVal; +} + +// Cycle through the Cull Nodes, paring down the list of who to test (through ITestNode above). +// We reclaim the scratch indices in clear and split when we're done (SetCount(0)), but we can't +// reclaim the culled, because our caller may be looking at who all we culled. See below in split. +// If a node is disabled, we can just ignore we ever got called. +void plCullNode::ITestNode(const plSpaceTree* space, Int16 who, hsBitVector& totList, hsBitVector& outList) const +{ + if( space->IsDisabled(who) ) + return; + + UInt32 myClearStart = ScratchClear().GetCount(); + UInt32 mySplitStart = ScratchSplit().GetCount(); + UInt32 myCullStart = ScratchCulled().GetCount(); + + if( kPureSplit == ITestNode(space, who, ScratchClear(), ScratchSplit(), ScratchCulled()) ) + ScratchSplit().Append(who); + + UInt32 myClearEnd = ScratchClear().GetCount(); + UInt32 mySplitEnd = ScratchSplit().GetCount(); + UInt32 myCullEnd = ScratchCulled().GetCount(); + + int i; + // If there's no OuterChild, everything in clear and split is visible. Everything in culled + // goes to innerchild (if any). + if( fOuterChild < 0 ) + { + plProfile_IncCount(HarvestNodes, myClearEnd - myClearStart + mySplitEnd - mySplitStart); + // Replace these with a memcopy or something!!!! + for( i = myClearStart; i < myClearEnd; i++ ) + { + space->HarvestLeaves(ScratchClear()[i], totList, outList); + } + for( i = mySplitStart; i < mySplitEnd; i++ ) + { + space->HarvestLeaves(ScratchSplit()[i], totList, outList); + } + + if( fInnerChild >= 0 ) + { + for( i = myCullStart; i < myCullEnd; i++ ) + { + IGetNode(fInnerChild)->ITestNode(space, ScratchCulled()[i], totList, outList); + } + } + ScratchClear().SetCount(myClearStart); + ScratchSplit().SetCount(mySplitStart); + ScratchCulled().SetCount(myCullStart); + + return; + } + + // There is an OuterChild, so whether there's an InnerChild or not, + // everything in ClearList is visible soley on the discretion of OuterChild. + for( i = myClearStart; i < myClearEnd; i++ ) + { + IGetNode(fOuterChild)->ITestNode(space, ScratchClear()[i], totList, outList); + } + + // If there's no InnerChild, then the SplitList is also visible soley + // on the discretion of OuterChild. + if( fInnerChild < 0 ) + { + for( i = mySplitStart; i < mySplitEnd; i++ ) + { + IGetNode(fOuterChild)->ITestNode(space, ScratchSplit()[i], totList, outList); + } + + ScratchClear().SetCount(myClearStart); + ScratchSplit().SetCount(mySplitStart); + ScratchCulled().SetCount(myCullStart); + + return; + } + + // There is an inner child. Everything in culled list is visible + // soley on its discretion. + for( i = myCullStart; i < myCullEnd; i++ ) + { + IGetNode(fInnerChild)->ITestNode(space, ScratchCulled()[i], totList, outList); + } + + // Okay, here's the rub. + // Everyone in the split list needs to be tested against InnerChild and OuterChild. + // If either child says it's okay (puts it in OutList), then it's okay. + // The problem is that if both children say it's okay, it will wind up in outList twice. + // This is complicated by the fact that outList is still subTrees at this point, + // so InnerChild adding a subTree and OuterChild adding a child of that subTree isn't + // even appending the same value to the list. + // Sooooo. + // What we do is keep track of every node (interior and leaf) that gets harvested. + // When we go to harvest a subtree, we check in totList for its bit being set. Bits + // set in totList are ENTIRE SUBTREE IS HARVESTED. SpaceTree understands this too in + // its HarvestLeaves. Seems obvious now, but I didn't hear you suggest it. + + for( i = mySplitStart; i < mySplitEnd; i++ ) + { + IGetNode(fOuterChild)->ITestNode(space, ScratchSplit()[i], totList, outList); + } + + for( i = mySplitStart; i < mySplitEnd; i++ ) + { + if( !totList.IsBitSet(ScratchSplit()[i]) ) + IGetNode(fInnerChild)->ITestNode(space, ScratchSplit()[i], totList, outList); + } + + ScratchClear().SetCount(myClearStart); + ScratchSplit().SetCount(mySplitStart); + ScratchCulled().SetCount(myCullStart); +} + +void plCullNode::IHarvest(const plSpaceTree* space, hsTArray& outList) const +{ + ITestNode(space, space->GetRoot(), ScratchTotVec(), ScratchBitVec()); + space->BitVectorToList(outList, ScratchBitVec()); + ScratchBitVec().Clear(); + ScratchTotVec().Clear(); + + ScratchClear().SetCount(0); + ScratchSplit().SetCount(0); + ScratchCulled().SetCount(0); +} + +////////////////////////////////////////////////////////////////////// +// This section builds the tree from the input cullpoly's +////////////////////////////////////////////////////////////////////// + +void plCullNode::IBreakPoly(const plCullPoly& poly, const hsTArray& depths, + hsBitVector& inVerts, + hsBitVector& outVerts, + hsBitVector& onVerts, + plCullPoly& outPoly) const +{ + inVerts.Clear(); + outVerts.Clear(); + onVerts.Clear(); + + outPoly.Init(poly); + + if( depths[0] < -kTolerance ) + inVerts.SetBit(0); + else if( depths[0] > kTolerance ) + outVerts.SetBit(0); + else + onVerts.SetBit(0); + + if( poly.fClipped.IsBitSet(0) ) + outPoly.fClipped.SetBit(0); + outPoly.fVerts.Append(poly.fVerts[0]); + + int i; + for( i = 1; i < poly.fVerts.GetCount(); i++ ) + { + if( depths[i] < -kTolerance ) + { + if( outVerts.IsBitSet(outPoly.fVerts.GetCount()-1) ) + { + hsPoint3 interp; + hsScalar t = IInterpVert(poly.fVerts[i-1], poly.fVerts[i], interp); + // add interp + onVerts.SetBit(outPoly.fVerts.GetCount()); + if( poly.fClipped.IsBitSet(i-1) ) + outPoly.fClipped.SetBit(outPoly.fVerts.GetCount()); + outPoly.fVerts.Append(interp); + } + inVerts.SetBit(outPoly.fVerts.GetCount()); + } + else if( depths[i] > kTolerance ) + { + if( inVerts.IsBitSet(outPoly.fVerts.GetCount()-1) ) + { + hsPoint3 interp; + hsScalar t = IInterpVert(poly.fVerts[i-1], poly.fVerts[i], interp); + // add interp + onVerts.SetBit(outPoly.fVerts.GetCount()); + if( poly.fClipped.IsBitSet(i-1) ) + outPoly.fClipped.SetBit(outPoly.fVerts.GetCount()); + outPoly.fVerts.Append(interp); + } + outVerts.SetBit(outPoly.fVerts.GetCount()); + } + else + { + onVerts.SetBit(outPoly.fVerts.GetCount()); + } + + if( poly.fClipped.IsBitSet(i) ) + outPoly.fClipped.SetBit(outPoly.fVerts.GetCount()); + outPoly.fVerts.Append(poly.fVerts[i]); + } + if( (inVerts.IsBitSet(outPoly.fVerts.GetCount()-1) && outVerts.IsBitSet(0)) + ||(outVerts.IsBitSet(outPoly.fVerts.GetCount()-1) && inVerts.IsBitSet(0)) ) + { + hsPoint3 interp; + hsScalar t = IInterpVert(poly.fVerts[poly.fVerts.GetCount()-1], poly.fVerts[0], interp); + onVerts.SetBit(outPoly.fVerts.GetCount()); + if( poly.fClipped.IsBitSet(poly.fVerts.GetCount()-1) ) + outPoly.fClipped.SetBit(outPoly.fVerts.GetCount()); + outPoly.fVerts.Append(interp); + } +} + +void plCullNode::ITakeHalfPoly(const plCullPoly& srcPoly, + const hsTArray& vtxIdx, + const hsBitVector& onVerts, + plCullPoly& outPoly) const +{ + if( vtxIdx.GetCount() > 2 ) + { + int i; + for( i = 0; i < vtxIdx.GetCount(); i++ ) + { + int next = i < vtxIdx.GetCount()-1 ? i+1 : 0; + int last = i ? i-1 : vtxIdx.GetCount()-1; + + // If these 3 verts are all on the plane, we may have created a collinear vertex (the middle one) + // which we now want to skip. + if( onVerts.IsBitSet(vtxIdx[i]) && onVerts.IsBitSet(vtxIdx[last]) && onVerts.IsBitSet(vtxIdx[next]) ) + { +#if 0 // FISH + hsScalar dot = hsVector3(&srcPoly.fVerts[vtxIdx[last]], &srcPoly.fVerts[vtxIdx[i]]).InnerProduct(hsVector3(&srcPoly.fVerts[vtxIdx[next]], &srcPoly.fVerts[vtxIdx[i]])); + if( dot <= 0 ) +#endif // FISH + continue; + } + if( srcPoly.fClipped.IsBitSet(vtxIdx[i]) + ||(onVerts.IsBitSet(vtxIdx[i]) && onVerts.IsBitSet(vtxIdx[next])) ) + outPoly.fClipped.SetBit(outPoly.fVerts.GetCount()); + outPoly.fVerts.Append(srcPoly.fVerts[vtxIdx[i]]); + } + } + else + { + // Just need a break point + hsStatusMessage("Under 2"); // FISH + } +} + +void plCullNode::IMarkClipped(const plCullPoly& poly, const hsBitVector& onVerts) const +{ + int i; + for( i = 1; i < poly.fVerts.GetCount(); i++ ) + { + int last = i-1; + if( onVerts[i] && onVerts[last] ) + poly.fClipped.SetBit(last); + } + if( onVerts[i] && onVerts[0] ) + poly.fClipped.SetBit(0); +} + +plCullNode::plCullStatus plCullNode::ISplitPoly(const plCullPoly& poly, + plCullPoly*& innerPoly, + plCullPoly*& outerPoly) const +{ + static hsTArray depths; + depths.SetCount(poly.fVerts.GetCount()); + + static hsBitVector onVerts; + onVerts.Clear(); + + hsBool someInner = false; + hsBool someOuter = false; + hsBool someOn = false; + int i; + for( i = 0; i < poly.fVerts.GetCount(); i++ ) + { + depths[i] = fNorm.InnerProduct(poly.fVerts[i]) + fDist; + if( depths[i] < -kTolerance ) + someInner = true; + else if( depths[i] > kTolerance ) + someOuter = true; + else + { + someOn = true; + onVerts.SetBit(i); + } + } + if( !(someInner || someOuter) ) + { + (innerPoly = ScratchPolys().Push())->Init(poly); + (outerPoly = ScratchPolys().Push())->Init(poly); + return kSplit; + } + else if( !someInner ) + { + IMarkClipped(poly, onVerts); + return kClear; + } + else if( !someOuter ) + { + IMarkClipped(poly, onVerts); + return kCulled; + } + + + // Okay, it's split, now break it into the two polys + (innerPoly = ScratchPolys().Push())->Init(poly); + (outerPoly = ScratchPolys().Push())->Init(poly); + + static plCullPoly scrPoly; + + static hsBitVector inVerts; + static hsBitVector outVerts; + + IBreakPoly(poly, depths, + inVerts, + outVerts, + onVerts, + scrPoly); + + static hsTArray inPolyIdx; + inPolyIdx.SetCount(0); + static hsTArray outPolyIdx; + outPolyIdx.SetCount(0); + + for( i = 0; i < scrPoly.fVerts.GetCount(); i++ ) + { + if( inVerts.IsBitSet(i) ) + { + inPolyIdx.Append(i); + } + else if( outVerts.IsBitSet(i) ) + { + outPolyIdx.Append(i); + } + else + { + inPolyIdx.Append(i); + outPolyIdx.Append(i); + } + } + + ITakeHalfPoly(scrPoly, inPolyIdx, onVerts, *innerPoly); + + ITakeHalfPoly(scrPoly, outPolyIdx, onVerts, *outerPoly); + + return kSplit; +} + +hsScalar plCullNode::IInterpVert(const hsPoint3& p0, const hsPoint3& p1, hsPoint3& out) const +{ + hsVector3 oneToOh; + oneToOh.Set(&p0, &p1); + + hsScalar t = -(fNorm.InnerProduct(p1) + fDist) / fNorm.InnerProduct(oneToOh); + if( t >= 1.f ) + { + out = p0; + return 1.f; + } + if( t <= 0 ) + { + out = p1; + return 0; + } + + out = p1; + + out += oneToOh * t; + + return t; +} + +// We use indices so our tree can actually be an array, which may be +// resized at any time, which would invalidate any pointers we have. +// But debugging a large tree is hard enough when stepping through pointers, +// it's pretty much impossible with indices. So when debugging we can +// setup these pointers for stepping through the tree. We just need to +// reset them every time we add a poly, because that's when the tree +// may have been resized invalidating the old pointers. +#ifdef DEBUG_POINTERS +void plCullNode::ISetPointersRecur() const +{ + if( fInnerPtr = IGetNode(fInnerChild) ) + fInnerPtr->ISetPointersRecur(); + if( fOuterPtr = IGetNode(fOuterChild) ) + fOuterPtr->ISetPointersRecur(); +} +#endif // DEBUG_POINTERS + +////////////////////////////////////////////////////////////////////// +// Now the tree proper +////////////////////////////////////////////////////////////////////// +// Build the tree +plCullTree::plCullTree() +: fRoot(-1), + fCapturePolys(false) +{ +} + +plCullTree::~plCullTree() +{ +} + +void plCullTree::AddPoly(const plCullPoly& poly) +{ + const plCullPoly* usePoly = &poly; + + hsVector3 cenToEye(&fViewPos, &poly.fCenter); + hsFastMath::NormalizeAppr(cenToEye); + hsScalar camDist = cenToEye.InnerProduct(poly.fNorm); + plConst(hsScalar) kTol(0.1f); + hsBool backFace = camDist < -kTol; + if( !backFace && (camDist < kTol) ) + return; + + plCullPoly scratchPoly; + if( poly.IsHole() ) + { + if( !backFace ) + return; + } + else + if( backFace ) + { + plConst(hsBool) kAllowTwoSided(true); + if( !kAllowTwoSided || !poly.IsTwoSided() ) + return; + + scratchPoly.Flip(poly); + usePoly = &scratchPoly; + } + + if( !SphereVisible(usePoly->GetCenter(), usePoly->GetRadius()) ) + return; + + usePoly->fClipped.Clear(); + + usePoly->Validate(); + + // Make sure we have enough scratch polys. Each node + // can potentially split this poly, so... + ISetupScratch(fNodeList.GetCount()); + +#if 1 + if( IGetRoot() && IGetNode(IGetRoot()->fOuterChild) ) + { + IAddPolyRecur(*usePoly, IGetRoot()->fOuterChild); + } + else +#endif + { + fRoot = IAddPolyRecur(*usePoly, fRoot); + } + +#ifdef DEBUG_POINTERS + if( IGetRoot() ) + IGetRoot()->ISetPointersRecur(); +#endif // DEBUG_POINTERS +} + +Int16 plCullTree::IAddPolyRecur(const plCullPoly& poly, Int16 iNode) +{ + if( poly.fVerts.GetCount() < 3 ) + return iNode; + + if( iNode < 0 ) + return IMakePolySubTree(poly); + + hsBool addInner = (IGetNode(iNode)->fInnerChild >= 0) + || ((iNode > 5) && poly.IsHole()); + hsBool addOuter = !poly.IsHole() || (IGetNode(iNode)->fOuterChild >= 0); + + plCullPoly* innerPoly = nil; + plCullPoly* outerPoly = nil; + + plCullNode::plCullStatus test = IGetNode(iNode)->ISplitPoly(poly, innerPoly, outerPoly); + + switch( test ) + { + case plCullNode::kClear: + if( addOuter ) + { + int child = IAddPolyRecur(poly, IGetNode(iNode)->fOuterChild); + IGetNode(iNode)->fOuterChild = child; + } + break; + case plCullNode::kCulled: + if( addInner ) + { + int child = IAddPolyRecur(poly, IGetNode(iNode)->fInnerChild); + IGetNode(iNode)->fInnerChild = child; + } + break; + case plCullNode::kSplit: + hsAssert(innerPoly && outerPoly, "Poly should have been split into inner and outer in SplitPoly"); + if( addOuter ) + { + int child = IAddPolyRecur(*outerPoly, IGetNode(iNode)->fOuterChild); + IGetNode(iNode)->fOuterChild = child; + } + if( addInner ) + { + int child = IAddPolyRecur(*innerPoly, IGetNode(iNode)->fInnerChild); + IGetNode(iNode)->fInnerChild = child; + } + break; + } + return iNode; +} + +Int16 plCullTree::IMakePolyNode(const plCullPoly& poly, int i0, int i1) const +{ + Int16 retINode = fNodeList.GetCount(); + plCullNode* nextNode = fNodeList.Push(); + hsVector3 a; + hsVector3 b; + a.Set(&poly.fVerts[i0], &fViewPos); + b.Set(&poly.fVerts[i1], &fViewPos); + hsVector3 n = a % b; + hsScalar d = -n.InnerProduct(fViewPos); + + IDEBUG_NORMALIZE(n, d); + + nextNode->Init(this, n, d); + + return retINode; +} + +Int16 plCullTree::IMakeHoleSubTree(const plCullPoly& poly) const +{ + if( fCapturePolys ) + IVisPoly(poly, true); + + int firstNode = fNodeList.GetCount(); + + Int16 iNode = -1; + + int i; + for( i = 0; i < poly.fVerts.GetCount()-1; i++ ) + { + if( !poly.fClipped.IsBitSet(i) ) + { + Int16 child = IMakePolyNode(poly, i, i+1); + if( iNode >= 0 ) + IGetNode(iNode)->fOuterChild = child; + iNode = child; + } + } + if( !poly.fClipped.IsBitSet(i) ) + { + Int16 child = IMakePolyNode(poly, i, 0); + if( iNode >= 0 ) + IGetNode(iNode)->fOuterChild = child; + iNode = child; + } + + plCullNode* child = fNodeList.Push(); + child->Init(this, poly.fNorm, poly.fDist); + if( iNode >= 0 ) + IGetNode(iNode)->fOuterChild = fNodeList.GetCount()-1; + + return firstNode; +} + +Int16 plCullTree::IMakePolySubTree(const plCullPoly& poly) const +{ + poly.Validate(); + + if( poly.IsHole() ) + return IMakeHoleSubTree(poly); + + if( fCapturePolys ) + IVisPoly(poly, false); + + int firstNode = fNodeList.GetCount(); + + Int16 iNode = -1; + + int i; + for( i = 0; i < poly.fVerts.GetCount()-1; i++ ) + { + if( !poly.fClipped.IsBitSet(i) ) + { + Int16 child = IMakePolyNode(poly, i, i+1); + if( iNode >= 0 ) + IGetNode(iNode)->fInnerChild = child; + iNode = child; + } + } + if( !poly.fClipped.IsBitSet(i) ) + { + Int16 child = IMakePolyNode(poly, i, 0); + if( iNode >= 0 ) + IGetNode(iNode)->fInnerChild = child; + iNode = child; + } + + plCullNode* child = fNodeList.Push(); + child->Init(this, poly.fNorm, poly.fDist); + child->fIsFace = true; + if( iNode >= 0 ) + IGetNode(iNode)->fInnerChild = fNodeList.GetCount()-1; + + return firstNode; +} + +/////////////////////////////////////////////////////////////////// +// Begin visualization section of the program +/////////////////////////////////////////////////////////////////// +void plCullTree::IVisPolyShape(const plCullPoly& poly, hsBool dark) const +{ + int i; + + int vertStart = fVisVerts.GetCount(); + + hsColorRGBA color; + if( dark ) + color.Set(0.2f, 0.2f, 0.2f, 1.f); + else + color.Set(1.f, 1.f, 1.f, 1.f); + + hsVector3 norm = dark ? -poly.fNorm : poly.fNorm; + + for( i = 0; i < poly.fVerts.GetCount(); i++ ) + { + fVisVerts.Append(poly.fVerts[i]); + fVisNorms.Append(poly.fNorm); + fVisColors.Append(color); + } + if( !dark ) + { + for( i = 2; i < poly.fVerts.GetCount(); i++ ) + { + fVisTris.Append(vertStart); + fVisTris.Append(vertStart + i-1); + fVisTris.Append(vertStart + i); + } + } + else + { + for( i = 2; i < poly.fVerts.GetCount(); i++ ) + { + fVisTris.Append(vertStart); + fVisTris.Append(vertStart + i); + fVisTris.Append(vertStart + i-1); + } + } +} + +void plCullTree::IVisPolyEdge(const hsPoint3& p0, const hsPoint3& p1, hsBool dark) const +{ + hsColorRGBA color; + if( dark ) + color.Set(0.2f, 0.2f, 0.2f, 1.f); + else + color.Set(1.f, 1.f, 1.f, 1.f); + + int vertStart = fVisVerts.GetCount(); + + hsVector3 dir0(&p0, &fViewPos); + hsFastMath::NormalizeAppr(dir0); + dir0 *= fVisYon; + hsVector3 dir1(&p1, &fViewPos); + hsFastMath::NormalizeAppr(dir1); + dir1 *= fVisYon; + + hsPoint3 p3 = fViewPos; + p3 += dir0; + hsPoint3 p2 = fViewPos; + p2 += dir1; + + hsVector3 norm = hsVector3(&p0, &fViewPos) % hsVector3(&p1, &fViewPos); + hsFastMath::NormalizeAppr(norm); + + fVisVerts.Append(p0); + fVisNorms.Append(norm); + fVisColors.Append(color); + fVisVerts.Append(p1); + fVisNorms.Append(norm); + fVisColors.Append(color); + fVisVerts.Append(p2); + fVisNorms.Append(norm); + fVisColors.Append(color); + fVisVerts.Append(p3); + fVisNorms.Append(norm); + fVisColors.Append(color); + + fVisTris.Append(vertStart + 0); + fVisTris.Append(vertStart + 2); + fVisTris.Append(vertStart + 1); + + fVisTris.Append(vertStart + 0); + fVisTris.Append(vertStart + 3); + fVisTris.Append(vertStart + 2); +} + +void plCullTree::IVisPoly(const plCullPoly& poly, hsBool dark) const +{ + IVisPolyShape(poly, dark); + + int i; + for( i = 0; i < poly.fVerts.GetCount()-1; i++ ) + { + if( !poly.fClipped.IsBitSet(i) ) + IVisPolyEdge(poly.fVerts[i], poly.fVerts[i+1], dark); + } + if( !poly.fClipped.IsBitSet(i) ) + IVisPolyEdge(poly.fVerts[i], poly.fVerts[0], dark); +} + +void plCullTree::ReleaseCapture() const +{ + fVisVerts.Reset(); + fVisNorms.Reset(); + fVisColors.Reset(); + fVisTris.Reset(); +} + +/////////////////////////////////////////////////////////////////// +// End visualization section of the program +/////////////////////////////////////////////////////////////////// + +void plCullTree::ISetupScratch(UInt16 nNodes) +{ + ScratchPolys().SetCount(nNodes << 1); + ScratchPolys().SetCount(0); +} + +void plCullTree::Reset() +{ + // Using NodeList as scratch will only work if we use indices, + // because a push invalidates any pointers we've stored away. + fNodeList.SetCount(0); + + fRoot = -1; + + ScratchPolys().SetCount(0); +} + + +void plCullTree::InitFrustum(const hsMatrix44& world2NDC) +{ + Reset(); + + fNodeList.SetCount(6); + fNodeList.SetCount(0); + + Int16 lastIdx = -1; + + plCullNode* node; + hsVector3 norm; + hsScalar dist; + + int i; + for( i = 0; i < 2; i++ ) + { + + norm.Set(world2NDC.fMap[3][0] - world2NDC.fMap[i][0], world2NDC.fMap[3][1] - world2NDC.fMap[i][1], world2NDC.fMap[3][2] - world2NDC.fMap[i][2]); + dist = world2NDC.fMap[3][3] - world2NDC.fMap[i][3]; + + IDEBUG_NORMALIZE( norm, dist ); + + node = fNodeList.Push(); + node->Init(this, norm, dist); + node->fOuterChild = lastIdx; + lastIdx = fNodeList.GetCount()-1; + + norm.Set(world2NDC.fMap[3][0] + world2NDC.fMap[i][0], world2NDC.fMap[3][1] + world2NDC.fMap[i][1], world2NDC.fMap[3][2] + world2NDC.fMap[i][2]); + dist = world2NDC.fMap[3][3] + world2NDC.fMap[i][3]; + + IDEBUG_NORMALIZE( norm, dist ); + + node = fNodeList.Push(); + node->Init(this, norm, dist); + node->fOuterChild = lastIdx; + lastIdx = fNodeList.GetCount()-1; + } + norm.Set(world2NDC.fMap[3][0] - world2NDC.fMap[2][0], world2NDC.fMap[3][1] - world2NDC.fMap[2][1], world2NDC.fMap[3][2] - world2NDC.fMap[2][2]); + dist = world2NDC.fMap[3][3] - world2NDC.fMap[2][3]; + + IDEBUG_NORMALIZE( norm, dist ); + + node = fNodeList.Push(); + node->Init(this, norm, dist); + node->fOuterChild = lastIdx; + lastIdx = fNodeList.GetCount()-1; + +#ifdef SYMMET + norm.Set(world2NDC.fMap[3][0] + world2NDC.fMap[2][0], world2NDC.fMap[3][1] + world2NDC.fMap[2][1], world2NDC.fMap[3][2] + world2NDC.fMap[2][2]); + dist = world2NDC.fMap[3][3] + world2NDC.fMap[2][3]; +#else // SYMMET + norm.Set(world2NDC.fMap[2][0], world2NDC.fMap[2][1], world2NDC.fMap[2][2]); + dist = world2NDC.fMap[2][3]; +#endif // SYMMET + + IDEBUG_NORMALIZE( norm, dist ); + + node = fNodeList.Push(); + node->Init(this, norm, dist); + node->fOuterChild = lastIdx; + lastIdx = fNodeList.GetCount()-1; + + fRoot = fNodeList.GetCount()-1; + +#ifdef DEBUG_POINTERS + if( IGetRoot() ) + IGetRoot()->ISetPointersRecur(); +#endif // DEBUG_POINTERS +} + +void plCullTree::SetViewPos(const hsPoint3& p) +{ + fViewPos = p; +} + +////////////////////////////////////////////////////////////////////// +// Use the tree +////////////////////////////////////////////////////////////////////// +void plCullTree::Harvest(const plSpaceTree* space, hsTArray& outList) const +{ + outList.SetCount(0); + if (!space->IsEmpty()) + IGetRoot()->IHarvest(space, outList); + +} + +hsBool plCullTree::BoundsVisible(const hsBounds3Ext& bnd) const +{ + return plCullNode::kCulled != IGetRoot()->ITestBoundsRecur(bnd); +} + +hsBool plCullTree::SphereVisible(const hsPoint3& center, hsScalar rad) const +{ + return plCullNode::kCulled != IGetRoot()->ITestSphereRecur(center, rad); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCullTree.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCullTree.h new file mode 100644 index 00000000..eb253c0a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCullTree.h @@ -0,0 +1,207 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCullTree_inc +#define plCullTree_inc + +#include "hsBounds.h" +#include "hsGeometry3.h" +#include "hsBitVector.h" +#include "plCuller.h" +#include "../plScene/plCullPoly.h" + +#ifdef HS_DEBUGGING +#define DEBUG_POINTERS +#endif // HS_DEBUGGING + +class plCullTree; +class plCullNode; + +// for vis +struct hsPoint3; +struct hsVector3; +struct hsColorRGBA; + +class plCullTree : public plCuller +{ +protected: + + // Visualization stuff, to be nuked from production version. + mutable hsBool fCapturePolys; + mutable hsTArray fVisVerts; + mutable hsTArray fVisNorms; + mutable hsTArray fVisColors; + mutable hsTArray fVisTris; + mutable hsScalar fVisYon; + + mutable hsTArray fScratchPolys; + mutable hsLargeArray fScratchClear; + mutable hsLargeArray fScratchSplit; + mutable hsLargeArray fScratchCulled; + mutable hsBitVector fScratchBitVec; + mutable hsBitVector fScratchTotVec; + + void IVisPolyShape(const plCullPoly& poly, hsBool dark) const; + void IVisPolyEdge(const hsPoint3& p0, const hsPoint3& p1, hsBool dark) const; + void IVisPoly(const plCullPoly& poly, hsBool dark) const; + + + hsPoint3 fViewPos; + + Int16 fRoot; + mutable hsTArray fNodeList; // Scratch list we make the tree from. + plCullNode* IGetRoot() const { return IGetNode(fRoot); } + plCullNode* IGetNode(Int16 i) const { return i >= 0 ? &fNodeList[i] : nil; } + + void ITestNode(const plSpaceTree* space, Int16 who, hsTArray& outList) const; // Appends to outlist + void ITestList(const plSpaceTree* space, const hsTArray& inList, hsTArray& outList) const; + + Int16 IAddPolyRecur(const plCullPoly& poly, Int16 iNode); + Int16 IMakeHoleSubTree(const plCullPoly& poly) const; + Int16 IMakePolySubTree(const plCullPoly& poly) const; + Int16 IMakePolyNode(const plCullPoly& poly, int i0, int i1) const; + + // Some scratch areas for the nodes use when building the tree etc. + hsTArray& ScratchPolys() const { return fScratchPolys; } + hsLargeArray& ScratchClear() const { return fScratchClear; } + hsLargeArray& ScratchSplit() const { return fScratchSplit; } + hsLargeArray& ScratchCulled() const { return fScratchCulled; } + hsBitVector& ScratchBitVec() const { return fScratchBitVec; } + hsBitVector& ScratchTotVec() const { return fScratchTotVec; } + + void ISetupScratch(UInt16 nNodes); + + friend class plCullNode; + +public: + plCullTree(); + ~plCullTree(); + + void Reset(); // Called before starting to add polys for this frame. + void InitFrustum(const hsMatrix44& world2NDC); + void SetViewPos(const hsPoint3& pos); + void AddPoly(const plCullPoly& poly); + + UInt32 GetNumNodes() const { return fNodeList.GetCount(); } + + virtual void Harvest(const plSpaceTree* space, hsTArray& outList) const; + virtual hsBool BoundsVisible(const hsBounds3Ext& bnd) const; + virtual hsBool SphereVisible(const hsPoint3& center, hsScalar rad) const; + + // Visualization stuff. Only to be called by the pipeline (or some other vis manager). + void SetVisualizationYon(hsScalar y) const { fVisYon = y; } + void BeginCapturePolys() const { fCapturePolys = true; } + void EndCapturePolys() const { fCapturePolys = false; } + hsTArray& GetCaptureVerts() const { return fVisVerts; } + hsTArray& GetCaptureNorms() const { return fVisNorms; } + hsTArray& GetCaptureColors() const { return fVisColors; } + hsTArray& GetCaptureTris() const { return fVisTris; } + void ReleaseCapture() const; +}; + +class plCullNode +{ +public: +enum plCullStatus +{ + kClear, + kCulled, + kSplit, + kPureSplit +}; +protected: + hsVector3 fNorm; + hsScalar fDist; + + hsBool fIsFace; + + Int16 fInnerChild; + Int16 fOuterChild; + + const plCullTree* fTree; + + plCullNode* IGetNode(Int16 i) const; + +#ifdef DEBUG_POINTERS + mutable plCullNode* fInnerPtr; + mutable plCullNode* fOuterPtr; + + void ISetPointersRecur() const; +#else // DEBUG_POINTERS + void ISetPointersRecur() const {} +#endif // DEBUG_POINTERS + + // Bounds only version + plCullNode::plCullStatus ITestBoundsRecur(const hsBounds3Ext& bnd) const; + plCullNode::plCullStatus ITestSphereRecur(const hsPoint3& center, hsScalar rad) const; + + // Using the nodes + plCullNode::plCullStatus ITestNode(const plSpaceTree* space, Int16 who, hsLargeArray& clear, hsLargeArray& split, hsLargeArray& culled) const; + void ITestNode(const plSpaceTree* space, Int16 who, hsBitVector& totList, hsBitVector& outList) const; + void IHarvest(const plSpaceTree* space, hsTArray& outList) const; + + // Constructing the tree + hsScalar IInterpVert(const hsPoint3& p0, const hsPoint3& p1, hsPoint3& out) const; + plCullNode::plCullStatus ISplitPoly(const plCullPoly& poly, plCullPoly*& innerPoly, plCullPoly*& outerPoly) const; + void IMarkClipped(const plCullPoly& poly, const hsBitVector& onVerts) const; + void ITakeHalfPoly(const plCullPoly& scrPoly, + const hsTArray& vtxIdx, + const hsBitVector& onVerts, + plCullPoly& outPoly) const; + void IBreakPoly(const plCullPoly& poly, const hsTArray& depths, + hsBitVector& inVerts, + hsBitVector& outVerts, + hsBitVector& onVerts, + plCullPoly& srcPoly) const; + + hsTArray& ScratchPolys() const { return fTree->ScratchPolys(); } + hsLargeArray& ScratchClear() const { return fTree->ScratchClear(); } + hsLargeArray& ScratchSplit() const { return fTree->ScratchSplit(); } + hsLargeArray& ScratchCulled() const { return fTree->ScratchCulled(); } + hsBitVector& ScratchBitVec() const { return fTree->ScratchBitVec(); } + hsBitVector& ScratchTotVec() const { return fTree->ScratchTotVec(); } + + friend class plCullTree; +public: + + void Init(const plCullTree* t, const hsVector3& n, hsScalar d) { fIsFace = false; fTree = t; fInnerChild = fOuterChild = -1; SetPlane(n, d); } + void Init(const plCullTree* t, const plCullPoly& poly) { Init(t, poly.fNorm, poly.fDist); } + + void SetPlane(const hsVector3& n, hsScalar d) { fNorm = n; fDist = d; } + + const hsVector3& GetNormal() const { return fNorm; } + const hsScalar GetDist() const { return fDist; } + + plCullStatus TestBounds(const hsBounds3Ext& bnd) const; + plCullStatus TestSphere(const hsPoint3& center, hsScalar rad) const; +}; + +inline plCullNode* plCullNode::IGetNode(Int16 i) const +{ + return fTree->IGetNode(i); +} + +#endif // plCullTree_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCuller.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCuller.h new file mode 100644 index 00000000..f5a9aa66 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plCuller.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCuller_inc +#define plCuller_inc + +#include "hsTemplates.h" + +class plSpaceTree; + +class plCuller +{ + +public: + plCuller() {} + ~plCuller() {} + + virtual void Harvest(const plSpaceTree* space, hsTArray& outList) const = 0; +}; + +#endif // plCuller_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp new file mode 100644 index 00000000..d9a58592 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp @@ -0,0 +1,238 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDTProgressMgr Functions // +// // +//// History ///////////////////////////////////////////////////////////////// +// // +// 2.28.2001 mcn - Created // +// // +////////////////////////////////////////////////////////////////////////////// + +#include +#include "hsTypes.h" +#include "plDTProgressMgr.h" +#include "plPipeline.h" +#include "plDebugText.h" +#include "plPlates.h" + +#include "../Apps/plClient/res/resource.h" + +#include "hsTimer.h" + + +//// Constructor & Destructor //////////////////////////////////////////////// + +plDTProgressMgr::plDTProgressMgr() : + fCurrentImage(0), + fLastDraw(0.0f), + fStaticTextPlate(nil), + fActivePlate(nil) +{ +} + +plDTProgressMgr::~plDTProgressMgr() +{ +} + +void plDTProgressMgr::DeclareThyself( void ) +{ + static plDTProgressMgr thyself; +} + +void plDTProgressMgr::Activate() +{ + if (fStaticTextPlate == nil && fCurrentStaticText != plProgressMgr::kNone) + { + plPlateManager::Instance().CreatePlate(&fStaticTextPlate); + + fStaticTextPlate->CreateFromJPEGResource(MAKEINTRESOURCE(plProgressMgr::GetStaticTextID(fCurrentStaticText)), 0); + fStaticTextPlate->SetVisible(true); + fStaticTextPlate->SetOpacity(1.0f); + fStaticTextPlate->SetSize(2 * 0.192f, 2 * 0.041f, true); + fStaticTextPlate->SetPosition(0, 0.5f, 0); + } + + if (fActivePlate == nil) + { + plPlateManager::Instance().CreatePlate( &fActivePlate ); + + fActivePlate->CreateFromJPEGResource( MAKEINTRESOURCE( plProgressMgr::GetLoadingFrameID(fCurrentImage) ), 0 ); + fActivePlate->SetVisible(true); + fActivePlate->SetOpacity(1.0f); + fActivePlate->SetSize(0.6, 0.6, true); + fActivePlate->SetPosition(0, 0, 0); + } +} + +void plDTProgressMgr::Deactivate() +{ + if (fStaticTextPlate) + { + fStaticTextPlate->SetVisible(false); + plPlateManager::Instance().DestroyPlate( fStaticTextPlate ); + fStaticTextPlate = nil; + } + + if (fActivePlate) + { + fActivePlate->SetVisible(false); + plPlateManager::Instance().DestroyPlate( fActivePlate ); + fActivePlate = nil; + } +} + +//// Draw //////////////////////////////////////////////////////////////////// + +void plDTProgressMgr::Draw( plPipeline *p ) +{ + UInt16 scrnWidth, scrnHeight, width, height, x, y; + plDebugText &text = plDebugText::Instance(); + + plOperationProgress *prog; + + + if( fOperations == nil ) + return; + + scrnWidth = (UInt16)p->Width(); + scrnHeight = (UInt16)p->Height(); + + width = scrnWidth - 64; + height = 16; + x = ( scrnWidth - width ) >> 1; + y = scrnHeight - 32 - height; + if( fOperations->GetNext() == nil ) + y -= text.GetFontSize() + 8 + height + 4; + + + text.SetDrawOnTopMode( true ); + + if (fActivePlate) + { + float currentMs = hsTimer::FullTicksToMs(hsTimer::GetFullTickCount()); + if ((currentMs - fLastDraw) > 30) + { + fCurrentImage++; + if (fCurrentImage >= 18) + fCurrentImage = 0; + + fLastDraw = currentMs; + + fActivePlate->ReloadFromJPEGResource(MAKEINTRESOURCE(plProgressMgr::GetInstance()->GetLoadingFrameID(fCurrentImage)), 0); + fActivePlate->SetVisible(true); + fActivePlate->SetOpacity(1.0f); + fActivePlate->SetSize(0.6, 0.6, true); + fActivePlate->SetPosition(0, 0, 0); + } + } + + for( prog = fOperations; prog != nil; prog = prog->GetNext() ) + { + IDrawTheStupidThing( p, prog, x, y, width, height ); + y -= text.GetFontSize() + 8 + height + 4; + } + + text.SetDrawOnTopMode( false ); +} + +//// IDrawTheStupidThing ///////////////////////////////////////////////////// + +void plDTProgressMgr::IDrawTheStupidThing( plPipeline *p, plOperationProgress *prog, + UInt16 x, UInt16 y, UInt16 width, UInt16 height ) +{ + plDebugText &text = plDebugText::Instance(); + + // Lets just set the color to blue + UInt32 color = 0xff302b3a; + + if( prog->GetMax() > 0.f ) + { + text.Draw3DBorder(x, y, x + width - 1, y + height - 1, color, color); + + x += 2; + y += 2; + width -= 4; + height -= 4; + + UInt16 drawWidth = width; + Int16 drawX = x; + UInt16 rightX = drawX + drawWidth; + + if (prog->GetProgress() <= prog->GetMax()) + drawWidth = (UInt16)( (hsScalar)width * prog->GetProgress() / prog->GetMax() ); + + rightX = drawX + drawWidth; + + if( drawWidth > 0 ) + text.DrawRect( drawX, y, rightX, y + height, color ); + + int timeRemain = prog->fRemainingSecs; + char remainStr[1024]; + strcpy(remainStr, "APPROXIMATELY "); + if (timeRemain > 3600) + { + const char* term = ((timeRemain / 3600) > 1) ? "HOURS" : "HOUR"; + sprintf(remainStr, "%s%d %s ", remainStr, (timeRemain / 3600), term); + timeRemain %= 3600; + } + if (timeRemain > 60) + { + const char* term = ((timeRemain / 60) > 1) ? "MINUTES" : "MINUTE"; + sprintf(remainStr, "%s%d %s ", remainStr, (timeRemain / 60), term); + timeRemain %= 60; + } + const char* unitTerm = (timeRemain == 1) ? "SECOND" : "SECONDS"; + sprintf(remainStr, "%s%d %s REMAINING", remainStr, timeRemain, unitTerm); + + text.DrawString(x, y + height + 2, remainStr, (UInt32)0xff635e6d ); + + x -= 2; + y -= 2; + } + + y -= ( text.GetFontSize() << 1 ) + 4; + +#ifndef PLASMA_EXTERNAL_RELEASE + bool drawText = true; +#else + bool drawText = false; +#endif + + if (drawText) + { + if( prog->GetTitle()[ 0 ] != 0 ) + { + text.DrawString( x, y, prog->GetTitle(), (UInt32)0xccb0b0b0 ); + x += (UInt16)text.CalcStringWidth( prog->GetTitle() ); + } + + if( prog->GetStatusText()[ 0 ] != 0 ) + text.DrawString( x, y, prog->GetStatusText(), (UInt32)0xccb0b0b0 ); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.h new file mode 100644 index 00000000..ade2ba4d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDTProgressMgr Header // +// // +//// Description ///////////////////////////////////////////////////////////// +// // +// Derived class of plProgressMgr to draw the progress bars via debug text.// +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDTProgressMgr_h +#define _plDTProgressMgr_h + +#include "../plProgressMgr/plProgressMgr.h" + +class plPipeline; + +//// Manager Class Definition //////////////////////////////////////////////// + +class plDTProgressMgr : public plProgressMgr +{ + protected: + Int32 fCurrentImage; + float fLastDraw; + plPlate* fActivePlate; + plPlate* fStaticTextPlate; + StaticText fShowingStaticText; + + void Activate(); + void Deactivate(); + + void IDrawTheStupidThing( plPipeline *p, plOperationProgress *prog, + UInt16 x, UInt16 y, UInt16 width, UInt16 height ); + + public: + + plDTProgressMgr(); + ~plDTProgressMgr(); + + virtual void Draw( plPipeline *p ); + + static void DeclareThyself( void ); +}; + + +#endif //_plDTProgressMgr_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXBufferRefs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXBufferRefs.h new file mode 100644 index 00000000..4c2e00b5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXBufferRefs.h @@ -0,0 +1,155 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDXBufferRefs.h - Hardware Vertex and Index Buffer DeviceRef // +// Definitions // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 4.25.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDXBufferRefs_h +#define _plDXBufferRefs_h + +#include "hsMatrix44.h" +#include "hsGeometry3.h" +#include "hsTemplates.h" +#include "plDXDeviceRef.h" + +struct IDirect3DVertexShader9; + +//// Definitions ////////////////////////////////////////////////////////////// + +class plDXVertexBufferRef : public plDXDeviceRef +{ + public: + IDirect3DVertexBuffer9* fD3DBuffer; + UInt32 fCount; + UInt32 fIndex; + UInt32 fVertexSize; + Int32 fOffset; + UInt8 fFormat; + + plGBufferGroup* fOwner; + UInt8* fData; + IDirect3DDevice9* fDevice; // For releasing the VertexShader + + UInt32 fRefTime; + + enum { + kRebuiltSinceUsed = 0x10, // kDirty = 0x1 is in hsGDeviceRef + kVolatile = 0x20, + kSkinned = 0x40 + }; + + hsBool HasFlag(UInt32 f) const { return 0 != (fFlags & f); } + void SetFlag(UInt32 f, hsBool on) { if(on) fFlags |= f; else fFlags &= ~f; } + + hsBool RebuiltSinceUsed() const { return HasFlag(kRebuiltSinceUsed); } + void SetRebuiltSinceUsed(hsBool b) { SetFlag(kRebuiltSinceUsed, b); } + + hsBool Volatile() const { return HasFlag(kVolatile); } + void SetVolatile(hsBool b) { SetFlag(kVolatile, b); } + + hsBool Skinned() const { return HasFlag(kSkinned); } + void SetSkinned(hsBool b) { SetFlag(kSkinned, b); } + + hsBool Expired(UInt32 t) const { return Volatile() && (IsDirty() || (fRefTime != t)); } + void SetRefTime(UInt32 t) { fRefTime = t; } + + void Link( plDXVertexBufferRef **back ) { plDXDeviceRef::Link( (plDXDeviceRef **)back ); } + plDXVertexBufferRef* GetNext() { return (plDXVertexBufferRef *)fNext; } + + plDXVertexBufferRef() : + fD3DBuffer(nil), + fCount(0), + fIndex(0), + fVertexSize(0), + fOffset(0), + fOwner(nil), + fData(nil), + fFormat(0), + fRefTime(0), + fDevice(nil) + { + } + + virtual ~plDXVertexBufferRef(); + void Release(); +}; + +class plDXIndexBufferRef : public plDXDeviceRef +{ + public: + IDirect3DIndexBuffer9* fD3DBuffer; + UInt32 fCount; + UInt32 fIndex; + Int32 fOffset; + plGBufferGroup* fOwner; + UInt32 fRefTime; + D3DPOOL fPoolType; + + enum { + kRebuiltSinceUsed = 0x10, // kDirty = 0x1 is in hsGDeviceRef + kVolatile = 0x20 + }; + + hsBool HasFlag(UInt32 f) const { return 0 != (fFlags & f); } + void SetFlag(UInt32 f, hsBool on) { if(on) fFlags |= f; else fFlags &= ~f; } + + hsBool RebuiltSinceUsed() const { return HasFlag(kRebuiltSinceUsed); } + void SetRebuiltSinceUsed(hsBool b) { SetFlag(kRebuiltSinceUsed, b); } + + hsBool Volatile() const { return HasFlag(kVolatile); } + void SetVolatile(hsBool b) { SetFlag(kVolatile, b); } + + hsBool Expired(UInt32 t) const { return Volatile() && (IsDirty() || (fRefTime != t)); } + void SetRefTime(UInt32 t) { fRefTime = t; } + + void Link( plDXIndexBufferRef **back ) { plDXDeviceRef::Link( (plDXDeviceRef **)back ); } + plDXIndexBufferRef* GetNext() { return (plDXIndexBufferRef *)fNext; } + + plDXIndexBufferRef() : + fD3DBuffer(nil), + fCount(0), + fIndex(0), + fOffset(0), + fOwner(nil), + fRefTime(0), + fPoolType(D3DPOOL_MANAGED) + { + } + + virtual ~plDXIndexBufferRef(); + void Release(); +}; + + +#endif // _plDXBufferRefs_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXDeviceRef.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXDeviceRef.h new file mode 100644 index 00000000..c3e70af6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXDeviceRef.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDXDeviceRef.h - Header for the generic DX DeviceRef type // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 4.25.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDXDeviceRef_h +#define _plDXDeviceRef_h + +#include "hsTypes.h" +#include "hsGDeviceRef.h" + + +//// Definition /////////////////////////////////////////////////////////////// + +class plDXDeviceRef : public hsGDeviceRef +{ + protected: + plDXDeviceRef *fNext; + plDXDeviceRef **fBack; + + public: + + void Unlink( void ); + void Link( plDXDeviceRef **back ); + plDXDeviceRef *GetNext( void ) { return fNext; } + hsBool IsLinked( void ) { return fBack != nil; } + virtual void Release( void ) { } + + plDXDeviceRef(); + + virtual ~plDXDeviceRef(); +}; + + +#endif // _plDXDeviceRef_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXDeviceRefs.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXDeviceRefs.cpp new file mode 100644 index 00000000..b3e18dbd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXDeviceRefs.cpp @@ -0,0 +1,514 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDXDeviceRefs.cpp - Functions for the various DX DeviceRef classes // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 4.25.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" + +#include +#include + +#include "hsWinRef.h" + +#include "plDXPipeline.h" +#include "plDXDeviceRef.h" +#include "plDXBufferRefs.h" +#include "plDXLightRef.h" +#include "plDXTextureRef.h" +#include "plDXRenderTargetRef.h" +#include "plGBufferGroup.h" +#include "../plDrawable/plGeometrySpan.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plGLight/plLightInfo.h" +#include "plRenderTarget.h" +#include "plCubicRenderTarget.h" +#include "plDynamicEnvMap.h" + +#include "plProfile.h" +#include "../plStatusLog/plStatusLog.h" + +plProfile_CreateMemCounter("Vertices", "Memory", MemVertex); +plProfile_CreateMemCounter("Indices", "Memory", MemIndex); +plProfile_CreateMemCounter("Textures", "Memory", MemTexture); + +/////////////////////////////////////////////////////////////////////////////// +//// Generic plDXDeviceRef Functions ///////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +plDXDeviceRef::plDXDeviceRef() +{ + fNext = nil; + fBack = nil; +} + +plDXDeviceRef::~plDXDeviceRef() +{ + if( fNext != nil || fBack != nil ) + Unlink(); +} + +void plDXDeviceRef::Unlink( void ) +{ + hsAssert( fBack, "plDXDeviceRef not in list" ); + if( fNext ) + fNext->fBack = fBack; + *fBack = fNext; + + fBack = nil; + fNext = nil; +} + +void plDXDeviceRef::Link( plDXDeviceRef **back ) +{ + hsAssert( fNext == nil && fBack == nil, "Trying to link a plDXDeviceRef that's already linked" ); + + fNext = *back; + if( *back ) + (*back)->fBack = &fNext; + fBack = back; + *back = this; +} + + +/////////////////////////////////////////////////////////////////////////////// +//// plDXVertex/IndexBufferRef Funktions ///////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// Destructors ////////////////////////////////////////////////////////////// + +plDXVertexBufferRef::~plDXVertexBufferRef() +{ + Release(); +} + +plDXIndexBufferRef::~plDXIndexBufferRef() +{ + Release(); +} + +//// Releases ///////////////////////////////////////////////////////////////// + +void plDXVertexBufferRef::Release( void ) +{ + if( fD3DBuffer != nil ) + { + ReleaseObject(fD3DBuffer); + if (!Volatile()) + { + plProfile_DelMem(MemVertex, fCount * fVertexSize); + PROFILE_POOL_MEM(D3DPOOL_MANAGED, fCount * fVertexSize, false, "VtxBuff"); + plDXPipeline::FreeManagedVertex(fCount * fVertexSize); + } + } + delete [] fData; + fData = nil; + + SetDirty( true ); +} + +void plDXIndexBufferRef::Release( void ) +{ + if( fD3DBuffer != nil ) + { + plProfile_DelMem(MemIndex, fCount * sizeof(UInt16)); + PROFILE_POOL_MEM(fPoolType, fCount * sizeof(UInt16), false, "IndexBuff"); + ReleaseObject( fD3DBuffer ); + } + + SetDirty( true ); +} + +/////////////////////////////////////////////////////////////////////////////// +//// plDXTextureRef Funktions //////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// Set ////////////////////////////////////////////////////////////////////// + +plDXTextureRef& plDXTextureRef::Set( D3DFORMAT ft, UInt32 ml, UInt32 mw, UInt32 mh, UInt32 np, + UInt32 sz, UInt32 manSize, UInt32* lSz, void* pd, hsBool ed, hsBool renderTarget ) +{ + if( fDataSize > 0 ) + plProfile_DelMem(MemTexture, fDataSize + sizeof(plDXTextureRef)); + + if( ( fFormatType != ft || fMMLvs != ml || fMaxWidth != mw || fMaxHeight != mh ) && fD3DTexture != nil ) + ReleaseObject( fD3DTexture ); + if( !fD3DTexture ) + fUseTime = 0; + + fFormatType = ft; + fMMLvs = ml; + fMaxWidth = mw; + fMaxHeight = mh; + fNumPix = np; + fDataSize = manSize; + if( fLevelSizes != nil ) + delete [] fLevelSizes; + if( lSz ) + fLevelSizes = lSz; + else + { + fLevelSizes = TRACKED_NEW UInt32[1]; + fLevelSizes[0] = sz; + } + fData = pd; + fFlags = ( ed ? kExternData : 0 ) | ( renderTarget ? kRenderTarget : 0 ); + + plProfile_NewMem(MemTexture, fDataSize + sizeof(plDXTextureRef)); + + return *this; +} + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plDXTextureRef::plDXTextureRef( D3DFORMAT ft, UInt32 ml, UInt32 mw, UInt32 mh, UInt32 np, + UInt32 sz, UInt32 manSize, UInt32* lSz, void* pd, hsBool ed, hsBool renderTarget ) +{ + fLevelSizes = nil; + fOwner = nil; + fD3DTexture = nil; + fDataSize = 0; + fFlags = 0; + fFormatType = D3DFMT_UNKNOWN; + fMMLvs = 0; + fMaxWidth = 0; + fMaxHeight = 0; + Set( ft, ml, mw, mh, np, sz, manSize, lSz, pd, ed, renderTarget ); +} + +plDXTextureRef::~plDXTextureRef() +{ + Release(); + + delete [] fLevelSizes; +} + +//// Release ////////////////////////////////////////////////////////////////// + +void plDXTextureRef::Release( void ) +{ + plProfile_DelMem(MemTexture, fDataSize + sizeof(plDXTextureRef)); + plProfile_Extern(ManagedMem); + PROFILE_POOL_MEM(D3DPOOL_MANAGED, fDataSize, false, (fOwner ? fOwner->GetKey() ? fOwner->GetKey()->GetUoid().GetObjectName() : "(UnknownTexture)" : "(UnknownTexture)")); + plDXPipeline::FreeManagedTexture(fDataSize); + fDataSize = 0; + + ReleaseObject( fD3DTexture ); + SetDirty( true ); +} + + +/////////////////////////////////////////////////////////////////////////////// +//// plDXLightRef Funktions ////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// UpdateD3DInfo //////////////////////////////////////////////////////////// + +#define SET_D3DCOLORVALUE( v, color ) { v.r = color.r; v.g = color.g; v.b = color.b; v.a = color.a; } + +void plDXLightRef::UpdateD3DInfo( IDirect3DDevice9 *dev, plDXLightSettings *settings ) +{ + plDirectionalLightInfo *dirOwner; + plOmniLightInfo *omniOwner; + plSpotLightInfo *spotOwner; + const float maxRange = 32767.f; + + + /// Properties that are set for all types + fD3DDevice = dev; + fParentSettings = settings; + + memset( &fD3DInfo, 0, sizeof( D3DLIGHT9 ) ); + SET_D3DCOLORVALUE( fD3DInfo.Diffuse, fOwner->GetDiffuse() ); + SET_D3DCOLORVALUE( fD3DInfo.Ambient, fOwner->GetAmbient() ); + SET_D3DCOLORVALUE( fD3DInfo.Specular, fOwner->GetSpecular() ); + + if( ( omniOwner = plOmniLightInfo::ConvertNoRef( fOwner ) ) != nil ) + { + fD3DInfo.Type = D3DLIGHT_POINT; + + hsPoint3 position = omniOwner->GetWorldPosition(); + fD3DInfo.Position.x = position.fX; + fD3DInfo.Position.y = position.fY; + fD3DInfo.Position.z = position.fZ; + + if( omniOwner->GetRadius() == 0 ) + fD3DInfo.Range = maxRange; + else + fD3DInfo.Range = omniOwner->GetRadius(); + fD3DInfo.Attenuation0 = omniOwner->GetConstantAttenuation(); + fD3DInfo.Attenuation1 = omniOwner->GetLinearAttenuation(); + fD3DInfo.Attenuation2 = omniOwner->GetQuadraticAttenuation(); + + // If the light is a spot, but it has a projected texture, then + // the cone attenuation is handled by the texture. We're only using + // the D3D light for distance attenuation and the N*L term. So + // we can just leave the D3D light as the cheaper and more stable + // Omni light. This sort of obviates the change below. - mf + if( !omniOwner->GetProjection() + && (spotOwner = plSpotLightInfo::ConvertNoRef(fOwner)) ) + { + fD3DInfo.Type = D3DLIGHT_SPOT; + + hsVector3 direction = spotOwner->GetWorldDirection(); + fD3DInfo.Direction.x = direction.fX; + fD3DInfo.Direction.y = direction.fY; + fD3DInfo.Direction.z = direction.fZ; + + fD3DInfo.Falloff = spotOwner->GetFalloff(); + fD3DInfo.Theta = spotOwner->GetSpotInner() * 2; +// fD3DInfo.Phi = spotOwner->GetProjection() ? hsScalarPI : spotOwner->GetSpotOuter() * 2; + // D3D doesn't seem to like a Phi of PI, even though that's supposed to be the + // largest legal value. Symptom is an erratic, intermitant, unpredictable failure + // of the light to light, with bizarreness like lighting one object but not the object + // next to it, alternating which object it fails on each frame (or less often). + // So, whatever. - mf + fD3DInfo.Phi = spotOwner->GetSpotOuter() * 2; + } + } + else if( ( dirOwner = plDirectionalLightInfo::ConvertNoRef( fOwner ) ) != nil ) + { + fD3DInfo.Type = D3DLIGHT_DIRECTIONAL; + + hsVector3 direction = dirOwner->GetWorldDirection(); + fD3DInfo.Direction.x = direction.fX; + fD3DInfo.Direction.y = direction.fY; + fD3DInfo.Direction.z = direction.fZ; + } + else + { + hsAssert( false, "Unrecognized light type passed to plDXLightRef::UpdateD3DInfo()" ); + return; + } + + fD3DDevice->SetLight( fD3DIndex, &fD3DInfo ); + fScale = 1.f; +} + +//// Destructor /////////////////////////////////////////////////////////////// + +plDXLightRef::~plDXLightRef() +{ + Release(); +} + +//// Release ////////////////////////////////////////////////////////////////// + +void plDXLightRef::Release( void ) +{ + // Ensure that this light is disabled + if( fD3DDevice ) + { + fD3DDevice->LightEnable( fD3DIndex, false ); + fD3DDevice = nil; + } + + if( fParentSettings ) + { + fParentSettings->fEnabledFlags.SetBit( fD3DIndex, false ); + fParentSettings->ReleaseD3DIndex( fD3DIndex ); + fParentSettings = nil; + } + fD3DIndex = 0; + + SetDirty( true ); +} + + +/////////////////////////////////////////////////////////////////////////////// +//// plDXRenderTargetRef Functions /////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// Constructor ////////////////////////////////////////////////////////////// + +plDXRenderTargetRef::plDXRenderTargetRef( D3DFORMAT tp, UInt32 ml, plRenderTarget *owner, hsBool releaseDepthOnDelete ) + : plDXTextureRef( tp, ml, owner->GetWidth(), owner->GetHeight(), + owner->GetWidth() * owner->GetHeight(), + owner->GetWidth() * owner->GetHeight() * ( owner->GetPixelSize() >> 3 ), + 0, + nil, + nil, true, true ) +{ + fD3DColorSurface = nil; + fD3DDepthSurface = nil; + fReleaseDepth = releaseDepthOnDelete; + fOwner = owner; + + if( owner->GetFlags() & plRenderTarget::kIsTexture ) + fFlags |= kOffscreenRT; + + if( owner->GetFlags() & plRenderTarget::kIsProjected ) + { + if( owner->GetFlags() & plRenderTarget::kIsOrtho ) + fFlags |= kOrthoProjection; + else + fFlags |= kPerspProjection; + } + + if( plCubicRenderTarget::ConvertNoRef( owner ) != nil ) + fFlags |= kCubicMap; +} + +//// Set ////////////////////////////////////////////////////////////////////// + +plDXRenderTargetRef& plDXRenderTargetRef::Set( D3DFORMAT tp, UInt32 ml, plRenderTarget *owner ) +{ + fOwner = owner; + + plDXTextureRef::Set( tp, ml, owner->GetWidth(), owner->GetHeight(), + owner->GetWidth() * owner->GetHeight(), + owner->GetWidth() * owner->GetHeight() * ( owner->GetPixelSize() >> 3 ), + 0, + nil, + nil, true, true ); + + if( owner->GetFlags() & plRenderTarget::kIsTexture ) + fFlags |= kOffscreenRT; + + if( owner->GetFlags() & plRenderTarget::kIsProjected ) + { + if( owner->GetFlags() & plRenderTarget::kIsOrtho ) + fFlags |= kOrthoProjection; + else + fFlags |= kPerspProjection; + } + + if( plCubicRenderTarget::ConvertNoRef( owner ) != nil ) + fFlags |= kCubicMap; + + return *this; +} + +//// SetTexture /////////////////////////////////////////////////////////////// + +void plDXRenderTargetRef::SetTexture( IDirect3DSurface9 *surface, IDirect3DSurface9 *depth ) +{ + fD3DColorSurface = surface; + fD3DTexture = nil; + fD3DDepthSurface = depth; +} + +void plDXRenderTargetRef::SetTexture( IDirect3DTexture9 *surface, IDirect3DSurface9 *depth ) +{ + fD3DTexture = surface; + fD3DColorSurface = nil; + fD3DDepthSurface = depth; +} + +void plDXRenderTargetRef::SetTexture( IDirect3DCubeTexture9 *surface, IDirect3DSurface9 *depth ) +{ + int i; + IDirect3DSurface9 *surf; + plDXRenderTargetRef *ref; + plCubicRenderTarget *cubic; + D3DCUBEMAP_FACES faces[ 6 ] = { D3DCUBEMAP_FACE_NEGATIVE_X, // Left + D3DCUBEMAP_FACE_POSITIVE_X, // Right + D3DCUBEMAP_FACE_POSITIVE_Z, // Front + D3DCUBEMAP_FACE_NEGATIVE_Z, // Back + D3DCUBEMAP_FACE_POSITIVE_Y, // Top + D3DCUBEMAP_FACE_NEGATIVE_Y }; // Bottom + + + fD3DTexture = surface; + fD3DDepthSurface = depth; + fD3DColorSurface = nil; + + /// Get the faces and assign to each of the child targets + cubic = plCubicRenderTarget::ConvertNoRef( fOwner ); + for( i = 0; i < 6; i++ ) + { + if( surface->GetCubeMapSurface( faces[ i ], 0, &surf ) != D3D_OK ) + { + hsAssert( false, "Unable to get cube map surface" ); + continue; + } + ref = (plDXRenderTargetRef *)cubic->GetFace( i )->GetDeviceRef(); + + ref->SetTexture( surf, depth ); + } +} + +//// Destructor /////////////////////////////////////////////////////////////// + +plDXRenderTargetRef::~plDXRenderTargetRef() +{ + Release(); +} + +//// Release ////////////////////////////////////////////////////////////////// + +void plDXRenderTargetRef::Release( void ) +{ + int i; + plCubicRenderTarget *cubic; + plDXRenderTargetRef *ref; + + + /// Get rid of the children's deviceRefs + if( fFlags & kCubicMap ) + { + cubic = plCubicRenderTarget::ConvertNoRef( fOwner ); + for( i = 0; i < 6; i++ ) + { + ref = (plDXRenderTargetRef *)cubic->GetFace( i )->GetDeviceRef(); + ref->Release(); + ref->SetDirty( true ); + } + // No need to call D3DSURF_MEMDEL on our fD3DTexture. It'll get + // accounted for by our children's surfaces. + } + else + { + // We do internal accounting here. Actual release of fD3DTexture + // happens in plDXTextureRef::Release() + D3DSURF_MEMDEL((IDirect3DTexture9*)fD3DTexture); + } + + D3DSURF_MEMDEL(fD3DColorSurface); + ReleaseObject( fD3DColorSurface ); + if( fReleaseDepth ) + { + // TODO: + // We don't know who all is sharing this depth surface, so we can't + // confidently say this memory is free now. We're reffing and releasing + // it properly as far as DirectX is concerned, but our internal memory + // counter is ignoring it. + //D3DSURF_MEMDEL(fD3DDepthSurface); + ReleaseObject( fD3DDepthSurface ); + } + + plDXTextureRef::Release(); + + SetDirty( true ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXEnumerate.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXEnumerate.cpp new file mode 100644 index 00000000..89e2f7ab --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXEnumerate.cpp @@ -0,0 +1,893 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plDXEnumerate.h" +#include + +#include "hsGDDrawDllLoad.h" +#include "hsG3DDeviceSelector.h" +#include "hsUtils.h" + +//// Local Typedefs /////////////////////////////////////////////////////////// + +typedef LPDIRECT3D9 (WINAPI * Direct3DCreateProc)( UINT sdkVersion ); + +const UInt8 hsGDirect3DTnLEnumerate::kNumDisplayFormats = 6; +const D3DFORMAT hsGDirect3DTnLEnumerate::kDisplayFormats[] = +{ + D3DFMT_A1R5G5B5, + D3DFMT_A2B10G10R10, + D3DFMT_A8R8G8B8, + D3DFMT_R5G6B5, + D3DFMT_X1R5G5B5, + D3DFMT_X8R8G8B8, +}; + +HRESULT hsGDirect3DTnLEnumerate::SelectFromDevMode(const hsG3DDeviceRecord* devRec, const hsG3DDeviceMode* devMode) +{ + + int i; + for( i = 0; i < GetNumDrivers(); i++ ) + { + if( !stricmp(GetDriver(i)->fAdapterInfo.Description, devRec->GetDriverDesc()) ) + { + int j; + for( j = 0; j < GetDriver(i)->fDevices.GetCount(); j++ ) + { + if( !stricmp(GetDriver(i)->fDevices[j].fStrName, devRec->GetDeviceDesc()) ) + { + SetCurrentDriver(GetDriver(i)); + SetCurrentDevice(&GetDriver(i)->fDevices[j]); + D3DEnum_SelectDefaultMode( + devMode->GetWidth(), + devMode->GetHeight(), + devMode->GetColorDepth()); + return false; + } + } + } + } + char errStr[256]; + + sprintf(errStr, "Can't find requested device - %s:%s:%s:%s:%s", + devRec->GetG3DDeviceTypeName(), + devRec->GetDriverDesc(), + devRec->GetDriverName(), + devRec->GetDriverVersion(), + devRec->GetDeviceDesc()); + + DWORD enumFlags = 0; + int width = devMode->GetWidth(); + int height = devMode->GetHeight(); + int colorDepth = devMode->GetColorDepth(); + // for a window, take whatever colordepth we can get. + if( !colorDepth ) + enumFlags |= D3DENUM_CANWINDOW; + enumFlags |= D3DENUM_TNLHAL; +#ifdef HS_ALLOW_D3D_REF_DRIVER + enumFlags |= D3DENUM_REFERENCERAST; +#endif + + D3DEnum_SelectDefaultDriver(enumFlags); + + // If we didn't get what we want, try for anything. + if( !GetCurrentDriver() || !GetCurrentDevice() ) + { + enumFlags = colorDepth ? 0 : D3DENUM_CANWINDOW; + D3DEnum_SelectDefaultDriver(enumFlags); + } + if( !GetCurrentDriver() || !GetCurrentDevice() ) + D3DEnum_SelectDefaultDriver(0); + if( !GetCurrentDriver() || !GetCurrentDevice() ) + { + if( !*GetEnumeErrorStr() ) + SetEnumeErrorStr("Error finding device"); + return true; + } + D3DEnum_SelectDefaultMode(width, height, colorDepth); + if( !GetCurrentMode() ) + { + if( !*GetEnumeErrorStr() ) + SetEnumeErrorStr("Error finding mode"); + return true; + } + + return false; +} + +HRESULT hsGDirect3DTnLEnumerate::D3DEnum_SelectDefaultMode(int width, int height, int depth) +{ + hsAssert(GetCurrentDriver() && GetCurrentDevice(), "Must have selected device already"); + + BOOL windowed = false; + if (depth == 0) + { + // Legacy code writes out 0 bit depth to mean windowed + windowed = true; + depth = 32; + } + + D3DEnum_DeviceInfo* device = GetCurrentDevice(); + int i; + for( i = 0; i < device->fModes.GetCount(); i++ ) + { + D3DEnum_ModeInfo* mode = &device->fModes[i]; + if (mode->fWindowed != windowed) + continue; + + if( depth ) + { + if( width < mode->fDDmode.Width ) + continue; + if( height < mode->fDDmode.Height ) + continue; + } + if( depth < mode->fBitDepth ) + continue; + + if( GetCurrentMode() ) + { + D3DEnum_ModeInfo* curMode = GetCurrentDriver()->fCurrentMode; + if( depth ) + { + if( curMode->fDDmode.Width > mode->fDDmode.Width ) + continue; + if( curMode->fDDmode.Height > mode->fDDmode.Height ) + continue; + } + if( curMode->fBitDepth > mode->fBitDepth ) + continue; + + } + + SetCurrentMode(mode); + } + return S_OK; +} + +//----------------------------------------------------------------------------- +// Name: D3DEnum_SelectDefaultDriver() +// Desc: Picks a default driver according to the passed in flags. +//----------------------------------------------------------------------------- +HRESULT hsGDirect3DTnLEnumerate::D3DEnum_SelectDefaultDriver( DWORD dwFlags ) +{ + + + // If a specific driver was requested, perform that search here + if( dwFlags & D3DENUM_MASK ) + { + int i; + for( i = 0; i < fDrivers.GetCount(); i++ ) + { + D3DEnum_DriverInfo* pDriver = &fDrivers[i]; + int j; + for( j = 0; j < pDriver->fDevices.GetCount(); j++ ) + { + D3DEnum_DeviceInfo* pDevice = &pDriver->fDevices[j]; + BOOL bFound = FALSE; + + if( pDevice->fDDType == D3DDEVTYPE_REF ) + { + if( dwFlags & D3DENUM_REFERENCERAST ) + bFound = TRUE; + } + else if( pDevice->fDDType == D3DDEVTYPE_HAL && + pDevice->fDDCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) + { + if( dwFlags & D3DENUM_TNLHAL ) + bFound = TRUE; + } + else + { +#if !HS_BUILD_FOR_XBOX + if( dwFlags & D3DENUM_CANWINDOW ) + { + if( (pDriver == &fDrivers[0]) + &&( pDevice->fDDCaps.Caps2 & DDCAPS2_CANRENDERWINDOWED ) ) + { + if( ( pDevice->fDDCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) + ^ !(dwFlags & D3DENUM_TNLHAL) ) + bFound = TRUE; + } + } + else +#endif + if( dwFlags & D3DENUM_PRIMARYHAL ) + { + if( pDriver == &fDrivers[0] ) + bFound = TRUE; + } + else + if( dwFlags & D3DENUM_SECONDARYHAL ) + { + if( pDriver != &fDrivers[0] ) + bFound = TRUE; + } + } + + if( bFound ) + { + SetCurrentDriver(pDriver); + SetCurrentDevice(pDevice); + return S_OK; + } + } + } + return D3DENUMERR_NOTFOUND; + } + + int i; + for( i = 0; i < fDrivers.GetCount(); i++ ) + { + D3DEnum_DriverInfo* pDriver = &fDrivers[i]; + int j; + for( j = 0; j < pDriver->fDevices.GetCount(); j++ ) + { + D3DEnum_DeviceInfo* pDevice = &pDriver->fDevices[j]; + if( !pDevice->fIsHardware ) + continue; + + SetCurrentDriver(pDriver); + SetCurrentDevice(pDevice); + + return S_OK; + } + } + + // No compatible devices were found. Return an error code + return D3DENUMERR_NOCOMPATIBLEDEVICES; +} + + +//// Constructor ////////////////////////////////////////////////////////////// +// +// Inits the enumeration and builds our list of devices/whatever. + +hsGDirect3DTnLEnumerate::hsGDirect3DTnLEnumerate() +{ + memset( &fEnumeErrorStr[0], 0x00, sizeof(fEnumeErrorStr) ); + + fCurrentDriver = NULL; // The selected DD driver + fDrivers.Reset(); // List of DD drivers + + + /// New DX Enumeration + + // Get a pointer to the creation function +#if !HS_BUILD_FOR_XBOX + if( hsGDDrawDllLoad::GetD3DDll() == nil ) + { + strcpy( fEnumeErrorStr, "Cannot load Direct3D driver!" ); + return; + } + + Direct3DCreateProc procPtr; + procPtr = (Direct3DCreateProc)GetProcAddress( hsGDDrawDllLoad::GetD3DDll(), "Direct3DCreate9" ); + if( procPtr == nil ) + { + strcpy( fEnumeErrorStr, "Cannot load D3D Create Proc!" ); + return; + } + + // Create a D3D object to use + IDirect3D9 *pD3D = procPtr( D3D_SDK_VERSION ); +#else + IDirect3D9 *pD3D = Direct3DCreate9( D3D_SDK_VERSION ); +#endif + if( pD3D == nil ) + { + strcpy( fEnumeErrorStr, "Cannot load DirectX!" ); + return; + } + + /// Loop through the "adapters" (we don't call them drivers anymore) + UINT iAdapter; + for( iAdapter = 0; iAdapter < pD3D->GetAdapterCount(); iAdapter++ ) + { + D3DEnum_DriverInfo* newDriver = fDrivers.Push(); + ZeroMemory( newDriver, sizeof( *newDriver ) ); + + // Copy data to a device info structure + D3DADAPTER_IDENTIFIER9 adapterInfo; + pD3D->GetAdapterIdentifier( iAdapter, 0, &adapterInfo ); + pD3D->GetAdapterDisplayMode( iAdapter, &newDriver->fDesktopMode ); + + memcpy( &newDriver->fAdapterInfo, &adapterInfo, sizeof( adapterInfo ) ); + strncpy( newDriver->fStrName, adapterInfo.Driver, 39 ); + strncpy( newDriver->fStrDesc, adapterInfo.Description, 39 ); + newDriver->fGuid = adapterInfo.DeviceIdentifier; + newDriver->fMemory = 16 * 1024 * 1024; /// Simulate 16 MB + + /// Do the mode and device enumeration for this adapter + IEnumAdapterDevices( pD3D, iAdapter, newDriver ); + } + + // Cleanup + pD3D->Release(); +} + +//// IEnumAdapterDevices ////////////////////////////////////////////////////// +// +// DirectX: Enumerates all the modes for a given adapter, then using the +// two faked modes for HAL and REF, attaches the modes to each "device" that +// can support them. + +void hsGDirect3DTnLEnumerate::IEnumAdapterDevices( IDirect3D9 *pD3D, UINT iAdapter, D3DEnum_DriverInfo *drivInfo ) +{ + // A bit backwards from DX8... First we have to go through our list of formats and check for validity. + // Then we can enum through the modes for each format. + + const DWORD numDeviceTypes = 2; + const TCHAR* strDeviceDescs[] = { "HAL", "REF" }; + const D3DDEVTYPE deviceTypes[] = { D3DDEVTYPE_HAL, D3DDEVTYPE_REF }; + + BOOL *formatWorks = TRACKED_NEW BOOL[kNumDisplayFormats + 1]; // One for each format + DWORD *behavior = TRACKED_NEW DWORD[kNumDisplayFormats + 1]; + UINT iDevice; + for (iDevice = 0; iDevice < numDeviceTypes; iDevice++) + { + D3DEnum_DeviceInfo *deviceInfo = drivInfo->fDevices.Push(); + ZeroMemory(deviceInfo, sizeof(*deviceInfo)); + + pD3D->GetDeviceCaps(iAdapter, deviceTypes[iDevice], &deviceInfo->fDDCaps); + strncpy(deviceInfo->fStrName, strDeviceDescs[iDevice], 39); + deviceInfo->fDDType = deviceTypes[iDevice]; + deviceInfo->fIsHardware = deviceInfo->fDDCaps.DevCaps & D3DDEVCAPS_HWRASTERIZATION; + + /// Loop through the formats, checking each against this device to see + /// if it will work. If so, add all modes matching that format + UInt8 iFormat; + for (iFormat = 0; iFormat < kNumDisplayFormats + 1; iFormat++ ) + { + // the desktop format gets to be first, everything else is nudged over one. + D3DFORMAT currFormat = (iFormat == 0 ? drivInfo->fDesktopMode.Format : kDisplayFormats[iFormat - 1]); + formatWorks[iFormat] = FALSE; + + int bitDepth = IGetDXBitDepth(currFormat); + if (bitDepth == 0) + continue; // Don't like this mode, skip it + + /// Can it be used as a render target? + if (FAILED(pD3D->CheckDeviceType(iAdapter, deviceTypes[iDevice], + currFormat, + currFormat, + FALSE))) + continue; // Nope--skip it + + if (deviceInfo->fDDCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) + { + /// Confirm that HW vertex processing works on this device + if (deviceInfo->fDDCaps.DevCaps & D3DDEVCAPS_PUREDEVICE) + { +#if 0 + behavior[iFormat] = D3DCREATE_HARDWARE_VERTEXPROCESSING | + D3DCREATE_PUREDEVICE; +#else + behavior[iFormat] = D3DCREATE_HARDWARE_VERTEXPROCESSING; +#endif + if (SUCCEEDED(IConfirmDevice(&deviceInfo->fDDCaps, behavior[iFormat], + currFormat))) + { + formatWorks[iFormat] = TRUE; + } + } + + if (!formatWorks[iFormat]) + { + /// HW vertex & Pure didn't work--just try HW vertex + behavior[iFormat] = D3DCREATE_HARDWARE_VERTEXPROCESSING; + if (SUCCEEDED(IConfirmDevice(&deviceInfo->fDDCaps, behavior[iFormat], + currFormat))) + { + formatWorks[iFormat] = TRUE; + } + } + + if (!formatWorks[iFormat]) + { + /// HW vertex didn't work--can we do mixed? + behavior[iFormat] = D3DCREATE_MIXED_VERTEXPROCESSING; + + if (SUCCEEDED(IConfirmDevice(&deviceInfo->fDDCaps, behavior[iFormat], + currFormat))) + { + formatWorks[iFormat] = TRUE; + } + } + } + + if (!formatWorks[iFormat]) + { + /// Egads. Try SW vertex processing + behavior[iFormat] = D3DCREATE_SOFTWARE_VERTEXPROCESSING; + + if (SUCCEEDED(IConfirmDevice(&deviceInfo->fDDCaps, behavior[iFormat], + currFormat))) + { + formatWorks[iFormat] = TRUE; + } + } + + if (formatWorks[iFormat]) + { + /// Now go through all the modes. If a given mode has a format that works, + /// add it to the device + UINT numAdapterModes = pD3D->GetAdapterModeCount(iAdapter, currFormat); + DWORD iMode; + for (iMode = 0; iMode < numAdapterModes; iMode++) + { + // TODO: Check for modes that only differ by refresh rate and exclude duplicates. + + /// Get the mode attributes + D3DDISPLAYMODE dispMode; + pD3D->EnumAdapterModes(iAdapter, currFormat, iMode, &dispMode); + { + /// Add it to our driver's global mode list + D3DEnum_ModeInfo *modeInfo = drivInfo->fModes.Push(); + ZeroMemory( modeInfo, sizeof( *modeInfo ) ); + modeInfo->fDDmode = dispMode; + sprintf( modeInfo->fStrDesc, TEXT( "%ld x %ld x %ld" ), dispMode.Width, dispMode.Height, bitDepth ); + modeInfo->fBitDepth = bitDepth; + + // Add it to the device + modeInfo->fDDBehavior = behavior[ iFormat ]; + IFindDepthFormats( pD3D, iAdapter, deviceInfo->fDDType, modeInfo ); + IFindFSAATypes( pD3D, iAdapter, deviceInfo->fDDType, modeInfo ); + ICheckCubicRenderTargets( pD3D, iAdapter, deviceInfo->fDDType, modeInfo ); + deviceInfo->fModes.Append( *modeInfo ); + + // Special check for the desktop, which we know is the first entry, because we put it there. + if (iFormat == 0) + { + /// Check if the device can window and/or is compatible with the desktop display mode + deviceInfo->fCompatibleWithDesktop = TRUE; + + // As of DirectX 9, any device supports windowed mode + //if (deviceInfo->fDDCaps.Caps2 & D3DCAPS2_CANRENDERWINDOWED) + { + deviceInfo->fCanWindow = TRUE; + + /// Add a fake mode to represent windowed. Silly, but here for legacy + D3DEnum_ModeInfo *pModeInfo = drivInfo->fModes.Push(); + ZeroMemory(pModeInfo, sizeof(*pModeInfo)); + pModeInfo->fDDmode = dispMode; + pModeInfo->fDDBehavior = behavior[iFormat]; + pModeInfo->fBitDepth = bitDepth; + sprintf(pModeInfo->fStrDesc, TEXT("Windowed")); + pModeInfo->fWindowed = true; + + IFindDepthFormats(pD3D, iAdapter, deviceInfo->fDDType, pModeInfo); + IFindFSAATypes(pD3D, iAdapter, deviceInfo->fDDType, pModeInfo); + ICheckCubicRenderTargets(pD3D, iAdapter, deviceInfo->fDDType, pModeInfo); + deviceInfo->fModes.Append( *pModeInfo ); + } + } + } + } + + + } + } + } + + delete [] formatWorks; + delete [] behavior; +} + +//// IFindDepthFormats //////////////////////////////////////////////////////// +// DirectX: Given a device and mode, find ALL available depth/stencil +// formats and add them to the mode info struct. + +hsBool hsGDirect3DTnLEnumerate::IFindDepthFormats( IDirect3D9 *pD3D, UINT iAdapter, D3DDEVTYPE deviceType, + D3DEnum_ModeInfo *modeInfo ) +{ +#if HS_BUILD_FOR_XBOX + D3DFORMAT formats[] = { D3DFMT_D16, D3DFMT_D24S8, D3DFMT_UNKNOWN }; +#else + D3DFORMAT formats[] = { D3DFMT_D16, D3DFMT_D24X8, D3DFMT_D32, + D3DFMT_D15S1, D3DFMT_D24X4S4, D3DFMT_D24S8, D3DFMT_UNKNOWN }; +#endif + + /// Try 'em + for( int i = 0; formats[ i ] != D3DFMT_UNKNOWN; i++ ) + { + if( SUCCEEDED( pD3D->CheckDeviceFormat( iAdapter, deviceType, modeInfo->fDDmode.Format, + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, + formats[ i ] ) ) ) + { + if( SUCCEEDED( pD3D->CheckDepthStencilMatch( iAdapter, deviceType, + modeInfo->fDDmode.Format, modeInfo->fDDmode.Format, formats[ i ] ) ) ) + { + modeInfo->fDepthFormats.Append( formats[ i ] ); + } + } + } + + return( modeInfo->fDepthFormats.GetCount() > 0 ? true : false ); +} + +//// IFindFSAATypes /////////////////////////////////////////////////////////// +// DirectX: Given a device and mode, find ALL available multisample types +// and add them to the mode info struct. + +hsBool hsGDirect3DTnLEnumerate::IFindFSAATypes( IDirect3D9 *pD3D, UINT iAdapter, D3DDEVTYPE deviceType, + D3DEnum_ModeInfo *modeInfo ) +{ + /// Try 'em + for (int type = 2; type <= 16; type++) + { + if (SUCCEEDED(pD3D->CheckDeviceMultiSampleType(iAdapter, deviceType, modeInfo->fDDmode.Format, + modeInfo->fWindowed ? TRUE : FALSE, + (D3DMULTISAMPLE_TYPE)type, NULL))) + { + modeInfo->fFSAATypes.Append((D3DMULTISAMPLE_TYPE)type); + } + } + + return (modeInfo->fFSAATypes.GetCount() > 0 ? true : false); +} + +//// ICheckCubicRenderTargets ///////////////////////////////////////////////// + +hsBool hsGDirect3DTnLEnumerate::ICheckCubicRenderTargets( IDirect3D9 *pD3D, UINT iAdapter, D3DDEVTYPE deviceType, + D3DEnum_ModeInfo *modeInfo ) +{ + if( SUCCEEDED( pD3D->CheckDeviceFormat( iAdapter, deviceType, modeInfo->fDDmode.Format, + D3DUSAGE_RENDERTARGET, D3DRTYPE_CUBETEXTURE, + modeInfo->fDDmode.Format ) ) ) + { + modeInfo->fCanRenderToCubic = true; + return true; + } + + modeInfo->fCanRenderToCubic = false; + return false; +} + +//// IConfirmDevice /////////////////////////////////////////////////////////// +// +// Nice, encapsulated way of testing for specific caps on a particular device + +HRESULT hsGDirect3DTnLEnumerate::IConfirmDevice( D3DCAPS9* pCaps, DWORD dwBehavior, + D3DFORMAT Format ) +{ + short bits; + + + bits = IGetDXBitDepth( Format ); + if( bits == 16 || bits == 24 || bits == 32 ) + return S_OK; + + return E_FAIL; +} + + +//----------------------------------------------------------------------------- +// Name: ~hsGDirect3DTnLEnumerate() +// Desc: +//----------------------------------------------------------------------------- +hsGDirect3DTnLEnumerate::~hsGDirect3DTnLEnumerate() +{ + D3DEnum_FreeResources(); +} + +//----------------------------------------------------------------------------- +// Name: D3DEnum_FreeResources() +// Desc: Frees all resources used for driver enumeration +//----------------------------------------------------------------------------- +VOID hsGDirect3DTnLEnumerate::D3DEnum_FreeResources() +{ +} + +//----------------------------------------------------------------------------- +// Name: SetEnumeErrorStr() +// Desc: +//----------------------------------------------------------------------------- +void hsGDirect3DTnLEnumerate::SetEnumeErrorStr(const char* s) +{ + hsStrncpy(fEnumeErrorStr, s, 128); +} + +//// IGetDXBitDepth ////////////////////////////////////////////////////////// +// +// From a D3DFORMAT enumeration, return the bit depth associated with it. +// Copied from hsGDirect3DDevice to prevent inclusion of that class in +// the RenderMenu project (for some reason, VC can't figure out we're only +// calling one static function!) + +short hsGDirect3DTnLEnumerate::IGetDXBitDepth( D3DFORMAT format ) +{ +#define ReturnDepth(type, depth) if (format == type) return depth + + ReturnDepth(D3DFMT_UNKNOWN, 0); + ReturnDepth(D3DFMT_A8R8G8B8, 32); + ReturnDepth(D3DFMT_X8R8G8B8, 32); + ReturnDepth(D3DFMT_R5G6B5, 16); + ReturnDepth(D3DFMT_X1R5G5B5, 16); + ReturnDepth(D3DFMT_A1R5G5B5, 16); + + // Supported by DX9, but we don't currently support it. Can add support if needed. + //ReturnDepth(D3DFMT_A2B10G10R10, 32); + + // Unsupported translation format--return 0 + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Direct3D DeviceSelector Code /////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// IGetD3DCardInfo ///////////////////////////////////////////////////////// +// Given two enum structs, strips out and produces the vendor ID, device ID +// and the driver name. Returns true if processed, false otherwise. + +hsBool hsG3DDeviceSelector::IGetD3DCardInfo( hsG3DDeviceRecord &record, // In + void *driverInfo, + void *deviceInfo, + DWORD *vendorID, DWORD *deviceID, // Out + char **driverString, char **descString ) +{ + D3DEnum_DriverInfo *driverD3DInfo = (D3DEnum_DriverInfo *)driverInfo; + D3DEnum_DeviceInfo *deviceD3DInfo = (D3DEnum_DeviceInfo *)deviceInfo; + + D3DADAPTER_IDENTIFIER9 *adapterInfo; + + adapterInfo = &driverD3DInfo->fAdapterInfo; + + /// Print out to our demo data file + plDemoDebugFile::Write( "DeviceSelector detected DX Direct3D device. Info:" ); + plDemoDebugFile::Write( " Driver Description", (char *)adapterInfo->Description ); + plDemoDebugFile::Write( " Driver Name", (char *)adapterInfo->Driver ); + plDemoDebugFile::Write( " Vendor ID", (Int32)adapterInfo->VendorId ); + plDemoDebugFile::Write( " Device ID", (Int32)adapterInfo->DeviceId ); + plDemoDebugFile::Write( " Version", (char *)record.GetDriverVersion() ); + plDemoDebugFile::Write( " Memory size (in MB)", record.GetMemoryBytes() / ( 1024 * 1024 ) ); + plDemoDebugFile::Write( " Memory size (in bytes)", record.GetMemoryBytes() ); + + *vendorID = adapterInfo->VendorId; + *deviceID = adapterInfo->DeviceId; + *driverString = adapterInfo->Driver; + *descString = adapterInfo->Description; + + return true; +} + +//// IInitDirect3D //////////////////////////////////////////////////////////// + +hsBool hsG3DDeviceSelector::IInitDirect3D( void ) +{ + if( hsGDDrawDllLoad::GetD3DDll() == nil ) + { + strcpy( fErrorString, "Cannot load Direct3D driver!" ); + return false; + } + + Direct3DCreateProc procPtr; + procPtr = (Direct3DCreateProc)GetProcAddress( hsGDDrawDllLoad::GetD3DDll(), "Direct3DCreate9" ); + if( procPtr == nil ) + { + strcpy( fErrorString, "Cannot load D3D Create Proc!" ); + return false; + } + + // Create a D3D object to use + IDirect3D9 *pD3D = procPtr( D3D_SDK_VERSION ); + if( pD3D == nil ) + { + strcpy( fErrorString, "Cannot load DirectX!" ); + return false; + } + pD3D->Release(); + + fErrorString[ 0 ] = 0; + return true; +} + +//// ITryDirect3DTnL ////////////////////////////////////////////////////////// + +void hsG3DDeviceSelector::ITryDirect3DTnL(hsWinRef winRef) +{ + hsGDirect3DTnLEnumerate d3dEnum; + + int i; + for( i = 0; i < d3dEnum.GetNumDrivers(); i++ ) + { + ITryDirect3DTnLDriver(d3dEnum.GetDriver(i)); + } +} + +//// ITryDirect3DDriver /////////////////////////////////////////////////////// +// +// New DirectX Way + +void hsG3DDeviceSelector::ITryDirect3DTnLDriver(D3DEnum_DriverInfo* drivInfo) +{ + hsG3DDeviceRecord devRec; + devRec.Clear(); + devRec.SetG3DDeviceType( kDevTypeDirect3DTnL ); + + devRec.SetDriverName( drivInfo->fAdapterInfo.Driver ); + devRec.SetDriverDesc( drivInfo->fAdapterInfo.Description ); + + char buff[ 256 ]; + sprintf( buff, "%d.%02d.%02d.%04d", + HIWORD( drivInfo->fAdapterInfo.DriverVersion.u.HighPart ), + LOWORD( drivInfo->fAdapterInfo.DriverVersion.u.HighPart ), + HIWORD( drivInfo->fAdapterInfo.DriverVersion.u.LowPart ), + LOWORD( drivInfo->fAdapterInfo.DriverVersion.u.LowPart ) ); + + + devRec.SetDriverVersion(buff); + + devRec.SetMemoryBytes(drivInfo->fMemory); + + int i; + for( i = 0; i < drivInfo->fDevices.GetCount(); i++ ) + { + /// 9.6.2000 mcn - Changed here so we can do fudging here, rather + /// than passing all the messy driver data to the function + hsG3DDeviceRecord currDevRec = devRec; + + /// Done first now, so we can alter the D3D type later + ITryDirect3DTnLDevice( &drivInfo->fDevices[i], currDevRec ); + + /// Check the vendor ID to see if it's 3dfx (#0x121a). If it is, don't add it. + /// (we don't support 3dfx D3D devices) -mcn + /// 11.25.2000 mcn - Knew this was going to come back and bite me. Now we just + /// append (3dfx) to the end of the device description, so that our latter test + /// can throw it out or not, depending on whether we're "strong". + + if( drivInfo->fAdapterInfo.VendorId == 0x121a && + ( currDevRec.GetG3DHALorHEL() == hsG3DDeviceSelector::kHHD3DHALDev || + currDevRec.GetG3DHALorHEL() == hsG3DDeviceSelector::kHHD3DTnLHalDev ) ) + { + if( drivInfo->fAdapterInfo.DeviceId >= 0x00000009 ) + { + currDevRec.SetG3DHALorHEL( kHHD3D3dfxVoodoo5Dev ); + plDemoDebugFile::Write( " Tagging device as a 3dfx Voodoo5 or above" ); + } + else + { + currDevRec.SetG3DHALorHEL( kHHD3D3dfxDev ); + plDemoDebugFile::Write( " Tagging device as a non-V5 3dfx card" ); + } + } + + IFudgeDirectXDevice( currDevRec, (D3DEnum_DriverInfo *)drivInfo, (D3DEnum_DeviceInfo *)&drivInfo->fDevices[ i ] ); + + if( currDevRec.GetModes().GetCount() ) + fRecords.Append( currDevRec ); + } +} + +//// ITryDirect3DTnLDevice //////////////////////////////////////////////////// +// +// New DirectX Way + +void hsG3DDeviceSelector::ITryDirect3DTnLDevice(D3DEnum_DeviceInfo* devInfo, hsG3DDeviceRecord& devRec) +{ + devRec.SetDeviceDesc(devInfo->fStrName); + + if( devInfo->fDDType == D3DDEVTYPE_REF ) + devRec.SetG3DHALorHEL( kHHD3DRefDev ); + else if( devInfo->fDDType == D3DDEVTYPE_HAL ) + { + if( devInfo->fDDCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) + { + devRec.SetG3DHALorHEL( kHHD3DTnLHalDev ); + devRec.SetCap( kCapsHWTransform ); + } + else + devRec.SetG3DHALorHEL( kHHD3DHALDev ); + } + + if( devInfo->fDDCaps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP ) + devRec.SetCap( kCapsCubicTextures ); + + devRec.SetLayersAtOnce( devInfo->fDDCaps.MaxSimultaneousTextures ); + + if( devInfo->fDDCaps.TextureFilterCaps & D3DPTFILTERCAPS_MIPFLINEAR ) + devRec.SetCap( kCapsMipmap ); + if( devInfo->fDDCaps.TextureCaps & D3DPTEXTURECAPS_MIPCUBEMAP ) + devRec.SetCap( kCapsCubicMipmap ); + if( devInfo->fDDCaps.TextureCaps & D3DPTEXTURECAPS_PERSPECTIVE ) + devRec.SetCap(kCapsPerspective); + if( devInfo->fIsHardware ) + devRec.SetCap( kCapsHardware ); + if( devInfo->fDDCaps.RasterCaps & D3DPRASTERCAPS_DITHER ) + devRec.SetCap(kCapsDither); + if( devInfo->fDDCaps.RasterCaps & D3DPRASTERCAPS_WBUFFER ) + devRec.SetCap(kCapsWBuffer); + if( devInfo->fDDCaps.RasterCaps & D3DPRASTERCAPS_FOGTABLE ) + { + devRec.SetCap( kCapsFogLinear ); + devRec.SetCap( kCapsFogExp ); + devRec.SetCap( kCapsFogExp2 ); + devRec.SetCap( kCapsPixelFog ); + } + else + { + devRec.SetCap( kCapsFogLinear ); + } + if( devInfo->fDDCaps.RasterCaps & D3DPRASTERCAPS_FOGRANGE ) + devRec.SetCap( kCapsFogRange ); + + if( devInfo->fDDCaps.MaxAnisotropy <= 1 ) + devRec.SetMaxAnisotropicSamples( 0 ); + else + devRec.SetMaxAnisotropicSamples( (UInt8)devInfo->fDDCaps.MaxAnisotropy ); + + if (D3DSHADER_VERSION_MAJOR(devInfo->fDDCaps.PixelShaderVersion) > 0) + devRec.SetCap(kCapsPixelShader); + + /// Assume these by default + devRec.SetCap( kCapsCompressTextures ); + devRec.SetCap( kCapsDoesSmallTextures ); + +#if 1 // mf - want to leave this one off by default + // if( devInfo->fCanAntialias ) + // devRec.SetCap( kCapsAntiAlias ); +#endif // mf - want to leave this one off by default + + hsG3DDeviceMode devMode; + int i, j; + + const struct + { + D3DFORMAT fmt; UInt16 depth; + } depths[] = { { D3DFMT_D16, 0x0010 }, { D3DFMT_D24X8, 0x0018 }, { D3DFMT_D32, 0x0020 }, + { D3DFMT_D15S1, 0x010f }, { D3DFMT_D24X4S4, 0x0418 }, { D3DFMT_D24S8, 0x0818 }, { D3DFMT_UNKNOWN, 0 } }; + + for( i = 0; i < devInfo->fModes.GetCount(); i++ ) + { + D3DEnum_ModeInfo* modeInfo = &devInfo->fModes[i]; + + devMode.Clear(); + devMode.SetWidth( modeInfo->fDDmode.Width ); + devMode.SetHeight( modeInfo->fDDmode.Height ); + devMode.SetColorDepth( modeInfo->fBitDepth ); + + if( modeInfo->fCanRenderToCubic ) + devMode.SetCanRenderToCubics( true ); + else + devMode.SetCanRenderToCubics( false ); + + for( j = 0; depths[ j ].depth != 0; j++ ) + { + if( modeInfo->fDepthFormats.Find( depths[ j ].fmt ) != modeInfo->fDepthFormats.kMissingIndex ) + devMode.AddZStencilDepth( depths[ j ].depth ); + } + + for( j = 2; j <= 16; j++ ) + { + if( modeInfo->fFSAATypes.Find( (D3DMULTISAMPLE_TYPE)j ) != modeInfo->fFSAATypes.kMissingIndex ) + devMode.AddFSAAType( j ); + } + + devRec.GetModes().Append( devMode ); + } +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXEnumerate.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXEnumerate.h new file mode 100644 index 00000000..13b7c32e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXEnumerate.h @@ -0,0 +1,184 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsGDirect3DTnLEnumerate_h +#define hsGDirect3DTnLEnumerate_h +#include "hsConfig.h" + +#include "hsTemplates.h" +//#include "plMemTrackerOff.h" +#include +//#include "plMemTrackerOn.h" + +//----------------------------------------------------------------------------- +// Name: D3DEnum_ModeInfo +// Desc: Structure to hold information about a display mode. This +// info is stored as a width, height, bpp, and pixelformat within a +// DDSURFACEDESC2. +//----------------------------------------------------------------------------- +struct D3DEnum_ModeInfo +{ + D3DDISPLAYMODE fDDmode; + CHAR fStrDesc[40]; + BOOL fWindowed; + char fBitDepth; + DWORD fDDBehavior; + hsTArray fDepthFormats; + hsTArray fFSAATypes; + BOOL fCanRenderToCubic; +}; + +//----------------------------------------------------------------------------- +// Name: D3DEnum_DeviceInfo +// Desc: Linked-list structure to hold information about a Direct3D device. The +// primary information recorded here is the D3DDEVICEDESC and a ptr to a +// list of valid display modes. +//----------------------------------------------------------------------------- +struct D3DEnum_DeviceInfo +{ + D3DDEVTYPE fDDType; + CHAR fStrName[40]; + D3DCAPS9 fDDCaps; + BOOL fCanWindow; + BOOL fCompatibleWithDesktop; + BOOL fIsHardware; + + hsTArray fModes; +}; + + + + +//----------------------------------------------------------------------------- +// Name: D3DEnum_DriverInfo +// Desc: Linked-list structure to hold information about a DirectX driver. The +// info stored is the capability bits for the driver plus a list +// of valid Direct3D devices for the driver. Note: most systems will only +// have one driver. The exception are multi-monitor systems, and systems +// with non-GDI 3D video cards. +//----------------------------------------------------------------------------- +struct D3DEnum_DriverInfo +{ + GUID fGuid; + + CHAR fStrDesc[40]; + CHAR fStrName[40]; + + unsigned int fMemory; + + D3DADAPTER_IDENTIFIER9 fAdapterInfo; + D3DDISPLAYMODE fDesktopMode; + + hsTArray fModes; + D3DEnum_ModeInfo* fCurrentMode; + + hsTArray fDevices; + D3DEnum_DeviceInfo* fCurrentDevice; +}; + + +class hsG3DDeviceRecord; +class hsG3DDeviceMode; + +class hsGDirect3DTnLEnumerate +{ +protected: + HMODULE fDDrawDLL; + + char fEnumeErrorStr[128]; // ƒhƒ‰ƒCƒoAƒfƒoƒCƒX—ñ‹“ƒGƒ‰[ƒƒbƒZ[ƒWŠi”[ƒoƒbƒtƒ@ + + hsTArray fDrivers; + + D3DEnum_DriverInfo* fCurrentDriver; // The selected DD driver + + static short IGetDXBitDepth( D3DFORMAT format ); + + /// DirectX Helper Functions + void IEnumAdapterDevices( IDirect3D9 *pD3D, UINT iAdapter, D3DEnum_DriverInfo *drivInfo ); + hsBool IFindDepthFormats( IDirect3D9 *pD3D, UINT iAdapter, D3DDEVTYPE deviceType, D3DEnum_ModeInfo *modeInfo ); + hsBool IFindFSAATypes( IDirect3D9 *pD3D, UINT iAdapter, D3DDEVTYPE deviceType, D3DEnum_ModeInfo *modeInfo ); + hsBool ICheckCubicRenderTargets( IDirect3D9 *pD3D, UINT iAdapter, D3DDEVTYPE deviceType, D3DEnum_ModeInfo *modeInfo ); + HRESULT IConfirmDevice( D3DCAPS9* pCaps, DWORD dwBehavior, D3DFORMAT format ); + + static const UInt8 kNumDisplayFormats; + static const D3DFORMAT kDisplayFormats[]; + +public: + hsGDirect3DTnLEnumerate(); + virtual ~hsGDirect3DTnLEnumerate(); + + VOID D3DEnum_FreeResources(); + + char* GetErrorString() { return (fEnumeErrorStr[0] ? fEnumeErrorStr : nil); } + + HRESULT SelectFromDevMode(const hsG3DDeviceRecord* devRec, const hsG3DDeviceMode* devMode); + HRESULT D3DEnum_SelectDefaultMode(int width, int height, int depth); + HRESULT D3DEnum_SelectDefaultDriver( DWORD dwFlags ); + + UInt32 GetNumDrivers() { return fDrivers.GetCount(); } + D3DEnum_DriverInfo* GetDriver(int i) { return &fDrivers[i]; } + + D3DEnum_DriverInfo* GetCurrentDriver() { return fCurrentDriver; } + D3DEnum_DeviceInfo* GetCurrentDevice() { return GetCurrentDriver() ? GetCurrentDriver()->fCurrentDevice : nil; } + D3DEnum_ModeInfo* GetCurrentMode() { return GetCurrentDevice() ? GetCurrentDriver()->fCurrentMode : nil; } + + void SetCurrentDriver(D3DEnum_DriverInfo* d) { fCurrentDriver = d; } + void SetCurrentDevice(D3DEnum_DeviceInfo* d) { hsAssert(GetCurrentDriver(), "Set Driver first"); GetCurrentDriver()->fCurrentDevice = d; } + void SetCurrentMode(D3DEnum_ModeInfo* m) { hsAssert(GetCurrentDriver(), "Set Driver first"); GetCurrentDriver()->fCurrentMode = m; } + + char* GetEnumeErrorStr() { return fEnumeErrorStr; } + void SetEnumeErrorStr(const char* s); +}; + + + +//----------------------------------------------------------------------------- +// Name: D3DEnum_SelectDefaultDriver() +// Desc: Picks a driver based on a set of passed in criteria. +//----------------------------------------------------------------------------- +#define D3DENUM_SOFTWAREONLY 0x00000001 +#define D3DENUM_FULLSCREENONLY 0x00000002 +#define D3DENUM_RGBEMULATION 0x00000004 +#define D3DENUM_REFERENCERAST 0x00000008 +#define D3DENUM_PRIMARYHAL 0x00000010 +#define D3DENUM_SECONDARYHAL 0x00000020 +#define D3DENUM_TNLHAL 0x00000040 +#define D3DENUM_CANWINDOW 0x00000080 +#define D3DENUM_MASK 0x000000ff + + + +//----------------------------------------------------------------------------- +// Error codes +//----------------------------------------------------------------------------- +#define D3DENUMERR_ENUMERATIONFAILED 0x81000001 // Enumeration failed +#define D3DENUMERR_SUGGESTREFRAST 0x81000002 // Suggest using the RefRast +#define D3DENUMERR_NOCOMPATIBLEDEVICES 0x81000003 // No devices were found that +// meet the app's desired +// capabilities +#define D3DENUMERR_NODIRECTDRAW 0x81000004 // DDraw couldn't initialize +#define D3DENUMERR_NOTFOUND 0x81000005 // Requested device not found + +#endif //hsGDirect3DTnLEnumerate_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXLightRef.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXLightRef.h new file mode 100644 index 00000000..3b41e21e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXLightRef.h @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDXLightRef.h - Hardware Light DeviceRef Definition // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 4.25.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDXLightRef_h +#define _plDXLightRef_h + +#include "hsMatrix44.h" +#include "hsGeometry3.h" +#include "hsTemplates.h" +#include "plDXDeviceRef.h" + + +//// Definition /////////////////////////////////////////////////////////////// + +class plLightInfo; +class plDXLightSettings; +class plDXLightRef : public plDXDeviceRef +{ + public: + plLightInfo *fOwner; + + D3DLIGHT9 fD3DInfo; + UInt32 fD3DIndex; + hsScalar fScale; + + plDXLightSettings *fParentSettings; + IDirect3DDevice9 *fD3DDevice; + + void Link( plDXLightRef **back ) { plDXDeviceRef::Link( (plDXDeviceRef **)back ); } + plDXLightRef *GetNext( void ) { return (plDXLightRef *)fNext; } + + plDXLightRef() + { + fOwner = nil; + fParentSettings = nil; + fD3DDevice = nil; + fD3DIndex = -1; + fScale = 1.f; + } + + virtual ~plDXLightRef(); + void Release( void ); + + void UpdateD3DInfo( IDirect3DDevice9 *dev, plDXLightSettings *settings ); +}; + + +#endif // _plDXLightRef_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPipeline.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPipeline.cpp new file mode 100644 index 00000000..4df7f818 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPipeline.cpp @@ -0,0 +1,14396 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDXPipeline Class Functions // +// plPipeline derivative for DirectX // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 2.23.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsConfig.h" +#include "hsWindows.h" + +#include +#include +#include +#include + +#include "hsWinRef.h" + +#include "hsTypes.h" +#include "plDXPipeline.h" +#include "plPipelineCreate.h" +#include "plDebugText.h" +#include "plDXEnumerate.h" +#include "hsG3DDeviceSelector.h" +#include "hsGDDrawDllLoad.h" +#include "hsResMgr.h" +#include "plStatusLogDrawer.h" +#include "plQuality.h" + +#include "plPipeDebugFlags.h" + +#include "hsTemplates.h" +//#include "hsGEnviron.h" +#include "plProfile.h" +#include "../plMessage/plDeviceRecreateMsg.h" +#include "../pnMessage/plSelfDestructMsg.h" +#include "../pnMessage/plClientMsg.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" +#include "../plSurface/plLayerShadowBase.h" +#include "../plGImage/plMipmap.h" +#include "../plGImage/plCubicEnvironmap.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plGeometrySpan.h" +#include "../plDrawable/plSpaceTree.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../plDrawable/plSpanTypes.h" +#include "../plDrawable/plAccessSpan.h" +#include "../plDrawable/plAuxSpan.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "hsFastMath.h" +#include "../plGLight/plLightInfo.h" +#include "../plParticleSystem/plParticleEmitter.h" +#include "../plParticleSystem/plParticle.h" +#include "../plAvatar/plAvatarClothing.h" +#include "plDebugText.h" +#include "plFogEnvironment.h" +#include "plDXTextFont.h" +#include "plGBufferGroup.h" +#include "hsTimer.h" +#include "plgDispatch.h" +#include "../plScene/plRenderRequest.h" +#include "../plScene/plVisMgr.h" +#include "plRenderTarget.h" +#include "plCubicRenderTarget.h" +#include "plDynamicEnvMap.h" +#include "../../FeatureLib/pfCamera/plVirtualCamNeu.h" + +#include "plDXBufferRefs.h" +#include "plDXTextureRef.h" +#include "plDXLightRef.h" +#include "plDXRenderTargetRef.h" +#include "plDXVertexShader.h" +#include "plDXPixelShader.h" + +#include "../plGLight/plShadowSlave.h" +#include "../plGLight/plShadowCaster.h" + +#include "hsGMatState.inl" + +#include "../plSurface/plShader.h" +#include "plDXVertexShader.h" +#include "plDXPixelShader.h" + +#include "../pnMessage/plPipeResMakeMsg.h" +#include "plPipeResReq.h" +#include "../pnNetCommon/plNetApp.h" // for dbg logging +#include "../../FeatureLib/pfCamera/plVirtualCamNeu.h" +#include "../../FeatureLib/pfCamera/plCameraModifier.h" +#include "../plResMgr/plLocalization.h" + + +// mf horse - test hack, nuke this later +#include "../plSurface/plLayerDepth.h" + +#include "../plGImage/hsCodecManager.h" +//#include "../plGImage/hsDXTDirectXCodec.h" + +#ifdef HS_DEBUGGING +// This is so VC++ will let us view the contents of plIcicle::fOwnerKey +#include "../pnKeyedObject/plKey.h" +#endif + +#include "plCullTree.h" + +#include "plTweak.h" + +#include + +//#define MF_TOSSER + +int mfCurrentTest = 100; +PipelineParams plPipeline::fDefaultPipeParams; +PipelineParams plPipeline::fInitialPipeParams; +//#define MF_ENABLE_HACKOFF +#ifdef MF_ENABLE_HACKOFF +//WHITE +static hsTArray hackOffscreens; +UInt32 doHackPlate = UInt32(-1); +#endif // MF_ENABLE_HACKOFF + +UInt32 fDbgSetupInitFlags; // HACK temp only + +#ifdef HS_DEBUGGING +void plReleaseObject(IUnknown* x) +{ + if( x ) + { + int refs = x->Release(); + if( refs ) + refs = 0; + } +} +#else // HS_DEBUGGING +void plReleaseObject(IUnknown* x) +{ + if( x ) + x->Release(); +} +#endif // HS_DEBUGGING + +//// Local Static Stuff /////////////////////////////////////////////////////// + +/// Macros for getting/setting data in a D3D vertex buffer +inline UInt8* inlStuffPoint( UInt8* ptr, const hsScalarTriple& point ) +{ + register float* dst = (float*)ptr; + register const float* src = (float*)&point.fX; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + return (UInt8*)dst; +} +inline UInt8* inlStuffUInt32( UInt8* ptr, const UInt32 uint ) +{ + *(UInt32*)ptr = uint; + return ptr + sizeof(uint); +} +inline UInt8* inlExtractPoint( const UInt8* ptr, const hsScalarTriple& pt ) +{ + register const float* src = (float*)ptr; + register float* dst = (float*)&pt.fX; + *dst++ = *src++; + *dst++ = *src++; + *dst++ = *src++; + return (UInt8*)src; +} +inline UInt8* inlExtractFloat( const UInt8*& ptr, float& f ) +{ + register const float* src = (float*)ptr; + f = *src++; + return (UInt8*)src; +} +inline UInt8* inlExtractUInt32( const UInt8*& ptr, UInt32& uint ) +{ + const UInt32* src = (UInt32*)ptr; + uint = *src++; + return (UInt8*)src; +} + +inline DWORD F2DW( FLOAT f ) +{ + return *((DWORD*)&f); +} + +//// Macros for D3D error handling +#define INIT_ERROR_CHECK( cond, errMsg ) if( FAILED( fSettings.fDXError = cond ) ) { return ICreateFail( errMsg ); } + +#if 1 // DEBUG +#define STRONG_ERROR_CHECK( cond ) if( FAILED( fSettings.fDXError = cond ) ) { IGetD3DError(); IShowErrorMessage(); } +#define WEAK_ERROR_CHECK( cond ) STRONG_ERROR_CHECK( cond ) +#else +#define STRONG_ERROR_CHECK( cond ) if( FAILED( fSettings.fDXError = cond ) ) { IGetD3DError(); } +#define WEAK_ERROR_CHECK( cond ) cond +#endif + +static D3DXMATRIX d3dIdentityMatrix( 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f ); + +static const enum _D3DTRANSFORMSTATETYPE sTextureStages[ 8 ] = +{ + D3DTS_TEXTURE0, D3DTS_TEXTURE1, D3DTS_TEXTURE2, D3DTS_TEXTURE3, + D3DTS_TEXTURE4, D3DTS_TEXTURE5, D3DTS_TEXTURE6, D3DTS_TEXTURE7 +}; + +static const float kPerspLayerScale = 0.00001f; +static const float kPerspLayerScaleW = 0.001f; +static const float kPerspLayerTrans = 0.00002f; +static const hsScalar kAvTexPoolShrinkThresh = 30.f; // seconds + +// This caps the number of D3D lights we use. We'll use up to the max allowed +// or this number, whichever is smaller. (This is to prevent us going haywire +// on trying to allocate an array for ALL of the lights in the Ref device.) +//#define kD3DMaxTotalLights 32 +///HAAAAACK Let's be mean and limit the artists to only 4 run-time lights.... hehehehhehe (not my idea!!!) +const int kD3DMaxTotalLights = 8; +// The framerate is the limit on the number of projected lights an object can have. +const int kMaxProjectors = 100; + +/// This controls whether we can draw bounds boxes around all the ice spans. +//#ifdef HS_DEBUGGING +#define MCN_BOUNDS_SPANS 1 +//#endif + +#define MF_BOUNDS_LEVEL_ICE 1 +//#define HS_D3D_USE_SPECULAR + +/// Define this to write out z-buffer debug info to plasmalog.txt +#ifdef HS_DEBUGGING +//#define DBG_WRITE_FORMATS +#endif + +plProfile_CreateMemCounter("Pipeline Surfaces", "Memory", MemPipelineSurfaces); +plProfile_Extern(MemVertex); +plProfile_Extern(MemIndex); +plProfile_CreateCounter("Feed Triangles", "Draw", DrawFeedTriangles); +plProfile_CreateCounter("Polys", "General", DrawTriangles); +plProfile_CreateCounter("Draw Prim Static", "Draw", DrawPrimStatic); +plProfile_CreateMemCounter("Total Texture Size", "Draw", TotalTexSize); +plProfile_CreateTimer("Harvest", "Draw", Harvest); +plProfile_CreateCounter("Material Change", "Draw", MatChange); +plProfile_CreateCounter("Layer Change", "Draw", LayChange); + +plProfile_Extern(DrawOccBuild); + +plProfile_CreateCounterNoReset("Reload", "PipeC", PipeReload); + +plProfile_CreateTimer("RenderScene", "PipeT", RenderScene); +plProfile_CreateTimer("VisEval", "PipeT", VisEval); +plProfile_CreateTimer("VisSelect", "PipeT", VisSelect); +plProfile_CreateTimer("FindSceneLights", "PipeT", FindSceneLights); +plProfile_CreateTimer("PrepShadows", "PipeT", PrepShadows); +plProfile_CreateTimer("PrepDrawable", "PipeT", PrepDrawable); +plProfile_CreateTimer(" Skin", "PipeT", Skin); +plProfile_CreateTimer(" AvSort", "PipeT", AvatarSort); +plProfile_CreateTimer(" Find Lights", "PipeT", FindLights); +plProfile_CreateTimer(" Find Perms", "PipeT", FindPerm); +plProfile_CreateTimer(" FindSpan", "PipeT", FindSpan); +plProfile_CreateTimer(" FindActiveLights", "PipeT", FindActiveLights); +plProfile_CreateTimer(" ApplyActiveLights", "PipeT", ApplyActiveLights); +plProfile_CreateTimer(" ApplyMoving", "PipeT", ApplyMoving); +plProfile_CreateTimer(" ApplyToSpec", "PipeT", ApplyToSpec); +plProfile_CreateTimer(" ApplyToMoving", "PipeT", ApplyToMoving); +plProfile_CreateTimer(" ClearLights", "PipeT", ClearLights); +plProfile_CreateTimer("RenderSpan", "PipeT", RenderSpan); +plProfile_CreateTimer(" MergeCheck", "PipeT", MergeCheck); +plProfile_CreateTimer(" MergeSpan", "PipeT", MergeSpan); +plProfile_CreateTimer(" SpanTransforms", "PipeT", SpanTransforms); +plProfile_CreateTimer(" SpanFog", "PipeT", SpanFog); +plProfile_CreateTimer(" SelectLights", "PipeT", SelectLights); +plProfile_CreateTimer(" SelectProj", "PipeT", SelectProj); +plProfile_CreateTimer(" CheckDyn", "PipeT", CheckDyn); +plProfile_CreateTimer(" CheckStat", "PipeT", CheckStat); +plProfile_CreateTimer(" RenderBuff", "PipeT", RenderBuff); +plProfile_CreateTimer(" RenderPrim", "PipeT", RenderPrim); +plProfile_CreateTimer("PlateMgr", "PipeT", PlateMgr); +plProfile_CreateTimer("DebugText", "PipeT", DebugText); +plProfile_CreateTimer("Reset", "PipeT", Reset); + +plProfile_CreateMemCounter("DefMem", "PipeC", DefaultMem); +plProfile_CreateMemCounter("ManMem", "PipeC", ManagedMem); +plProfile_CreateMemCounterReset("CurrTex", "PipeC", CurrTex); +plProfile_CreateMemCounterReset("CurrVB", "PipeC", CurrVB); +plProfile_CreateMemCounter("TexTot", "PipeC", TexTot); +plProfile_CreateMemCounterReset("fTexUsed", "PipeC", fTexUsed); +plProfile_CreateMemCounterReset("fTexManaged", "PipeC", fTexManaged); +plProfile_CreateMemCounterReset("fVtxUsed", "PipeC", fVtxUsed); +plProfile_CreateMemCounterReset("fVtxManaged", "PipeC", fVtxManaged); +plProfile_CreateMemCounter("ManSeen", "PipeC", ManSeen); +plProfile_CreateCounterNoReset("ManEvict", "PipeC", ManEvict); +plProfile_CreateCounter("LightOn", "PipeC", LightOn); +plProfile_CreateCounter("LightVis", "PipeC", LightVis); +plProfile_CreateCounter("LightChar", "PipeC", LightChar); +plProfile_CreateCounter("LightActive", "PipeC", LightActive); +plProfile_CreateCounter("Lights Found", "PipeC", FindLightsFound); +plProfile_CreateCounter("Perms Found", "PipeC", FindLightsPerm); +plProfile_CreateCounter("Merge", "PipeC", SpanMerge); +plProfile_CreateCounter("TexNum", "PipeC", NumTex); +plProfile_CreateCounter("LiState", "PipeC", MatLightState); +plProfile_CreateCounter("OccPoly", "PipeC", OccPolyUsed); +plProfile_CreateCounter("OccNode", "PipeC", OccNodeUsed); +plProfile_CreateCounter("NumSkin", "PipeC", NumSkin); +plProfile_CreateCounter("AvatarFaces", "PipeC", AvatarFaces); +plProfile_CreateCounter("VertexChange", "PipeC", VertexChange); +plProfile_CreateCounter("IndexChange", "PipeC", IndexChange); +plProfile_CreateCounter("DynVBuffs", "PipeC", DynVBuffs); +plProfile_CreateCounter("EmptyList", "PipeC", EmptyList); +plProfile_CreateCounter("AvRTPoolUsed", "PipeC", AvRTPoolUsed); +plProfile_CreateCounter("AvRTPoolCount", "PipeC", AvRTPoolCount); +plProfile_CreateCounter("AvRTPoolRes", "PipeC", AvRTPoolRes); +plProfile_CreateCounter("AvRTShrinkTime", "PipeC", AvRTShrinkTime); + +#ifndef PLASMA_EXTERNAL_RELEASE +/// Fun inlines for keeping track of surface creation/deletion memory +void D3DSURF_MEMNEW(IDirect3DSurface9* surf) +{ + if( surf ) + { + D3DSURFACE_DESC info; + surf->GetDesc( &info ); + PROFILE_POOL_MEM(D3DPOOL_DEFAULT, info.Width * info.Height * plDXPipeline::GetDXBitDepth(info.Format) / 8 + sizeof(IDirect3DSurface9), true, "D3DSurface"); + plProfile_NewMem(MemPipelineSurfaces, info.Width * info.Height * plDXPipeline::GetDXBitDepth(info.Format) / 8 + sizeof(IDirect3DSurface9)); + } +} + +void D3DSURF_MEMNEW(IDirect3DTexture9* tex) +{ + if( tex ) + { + IDirect3DSurface9* surf; + tex->GetSurfaceLevel(0, &surf); + if( surf ) + { + D3DSURF_MEMNEW(surf); + surf->Release(); + } + } +} + +void D3DSURF_MEMNEW(IDirect3DCubeTexture9* cTex) +{ + if( cTex ) + { + IDirect3DSurface9* surf; + cTex->GetCubeMapSurface(D3DCUBEMAP_FACE_POSITIVE_X, 0, &surf); + if( surf ) + { + D3DSURF_MEMNEW(surf); + D3DSURF_MEMNEW(surf); + D3DSURF_MEMNEW(surf); + D3DSURF_MEMNEW(surf); + D3DSURF_MEMNEW(surf); + D3DSURF_MEMNEW(surf); + surf->Release(); + } + } +} + +void D3DSURF_MEMDEL(IDirect3DSurface9* surf) +{ + if( surf ) + { + D3DSURFACE_DESC info; + surf->GetDesc( &info ); + PROFILE_POOL_MEM(D3DPOOL_DEFAULT, info.Width * info.Height * plDXPipeline::GetDXBitDepth(info.Format) / 8 + sizeof(IDirect3DSurface9), false, "D3DSurface"); + plProfile_DelMem(MemPipelineSurfaces, info.Width * info.Height * plDXPipeline::GetDXBitDepth(info.Format) / 8 + sizeof(IDirect3DSurface9)); + } +} + +void D3DSURF_MEMDEL(IDirect3DTexture9* tex) +{ + if( tex ) + { + IDirect3DSurface9* surf; + tex->GetSurfaceLevel(0, &surf); + if( surf ) + { + D3DSURF_MEMDEL(surf); + surf->Release(); + } + } +} + +void D3DSURF_MEMDEL(IDirect3DCubeTexture9* cTex) +{ + if( cTex ) + { + IDirect3DSurface9* surf; + cTex->GetCubeMapSurface(D3DCUBEMAP_FACE_POSITIVE_X, 0, &surf); + if( surf ) + { + D3DSURF_MEMDEL(surf); + D3DSURF_MEMDEL(surf); + D3DSURF_MEMDEL(surf); + D3DSURF_MEMDEL(surf); + D3DSURF_MEMDEL(surf); + D3DSURF_MEMDEL(surf); + surf->Release(); + } + } +} +#else +void D3DSURF_MEMNEW(IDirect3DSurface9* surf) {} +void D3DSURF_MEMNEW(IDirect3DTexture9* tex) {} +void D3DSURF_MEMNEW(IDirect3DCubeTexture9* cTex) {} +void D3DSURF_MEMDEL(IDirect3DSurface9* surf) {} +void D3DSURF_MEMDEL(IDirect3DTexture9* tex) {} +void D3DSURF_MEMDEL(IDirect3DCubeTexture9* cTex) {} +#endif // PLASMA_EXTERNAL_RELEASE + +#ifndef PLASMA_EXTERNAL_RELEASE +void plDXPipeline::ProfilePoolMem(D3DPOOL poolType, UInt32 size, hsBool add, char *id) +{ + switch( poolType ) + { + case D3DPOOL_MANAGED: + if (add) + { + plProfile_NewMem(ManagedMem, size); + //plStatusLog::AddLineS("pipeline.log", 0xffff0000, "Adding MANAGED mem. Size: %10d, Total: %10d ID: %s", + // size, gProfileVarManagedMem.GetValue(), id); + } + else + { + plProfile_DelMem(ManagedMem, size); + //plStatusLog::AddLineS("pipeline.log", 0xffff0000, "Deleting MANAGED mem. Size: %10d, Total: %10d ID: %s", + // size, gProfileVarManagedMem.GetValue(), id); + } + break; + default: + if (add) + { + plProfile_NewMem(DefaultMem, size); + //plStatusLog::AddLineS("pipeline.log", 0xffff0000, "Adding DEFAULT mem. Size: %10d, Total: %10d ID: %s", + // size, gProfileVarDefaultMem.GetValue(), id); + } + else + { + plProfile_DelMem(DefaultMem, size); + //plStatusLog::AddLineS("pipeline.log", 0xffff0000, "Deleting DEFAULT mem. Size: %10d, Total: %10d ID: %s", + // size, gProfileVarDefaultMem.GetValue(), id); + } + break; + } +} +#endif // PLASMA_EXTERNAL_RELEASE + +///////////////////////////////////////////////////////////////////////////////////////// +// Implementations of RenderPrims types. +// Currently support render tri list +// These allow the same setup code path to be followed, no matter what the primitive type +// (i.e. data-type/draw-call is going to happen once the render state is set. +// Originally useful to make one code path for trilists, tri-patches, and rect-patches, but +// we've since dropped support for patches. We still use the RenderNil function to allow the +// code to go through all the state setup without knowing whether a render call is going to +// come out the other end. +// Would allow easy extension for supporting tristrips or pointsprites, but we've never had +// a strong reason to use either. +// First, Declarations. + +// Adding a nil RenderPrim for turning off drawing +class plRenderNilFunc : public plRenderPrimFunc +{ +public: + plRenderNilFunc() {} + + virtual hsBool RenderPrims() const { return false; } +}; +static plRenderNilFunc sRenderNil; + +class plRenderTriListFunc : public plRenderPrimFunc +{ +protected: + LPDIRECT3DDEVICE9 fD3DDevice; + int fBaseVertexIndex; + int fVStart; + int fVLength; + int fIStart; + int fNumTris; +public: + plRenderTriListFunc(LPDIRECT3DDEVICE9 d3dDevice, int baseVertexIndex, + int vStart, int vLength, int iStart, int iNumTris) + : fD3DDevice(d3dDevice), fBaseVertexIndex(baseVertexIndex), fVStart(vStart), fVLength(vLength), fIStart(iStart), fNumTris(iNumTris) {} + + virtual hsBool RenderPrims() const; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Implementations + +hsBool plRenderTriListFunc::RenderPrims() const +{ + plProfile_IncCount(DrawFeedTriangles, fNumTris); + plProfile_IncCount(DrawTriangles, fNumTris); + plProfile_Inc(DrawPrimStatic); + + return FAILED( fD3DDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, fBaseVertexIndex, fVStart, fVLength, fIStart, fNumTris ) ); +} + +//// Constructor & Destructor ///////////////////////////////////////////////// + +UInt32 plDXPipeline::fTexUsed(0); +UInt32 plDXPipeline::fTexManaged(0); +UInt32 plDXPipeline::fVtxUsed(0); +UInt32 plDXPipeline::fVtxManaged(0); + +plDXPipeline::plDXPipeline( hsWinRef hWnd, const hsG3DDeviceModeRecord *devModeRec ) +: fManagedAlloced(false), + fAllocUnManaged(false) +{ + hsAssert(D3DTSS_TCI_PASSTHRU == plLayerInterface::kUVWPassThru, "D3D Enum has changed. Notify graphics department."); + hsAssert(D3DTSS_TCI_CAMERASPACENORMAL == plLayerInterface::kUVWNormal, "D3D Enum has changed. Notify graphics department."); + hsAssert(D3DTSS_TCI_CAMERASPACEPOSITION == plLayerInterface::kUVWPosition, "D3D Enum has changed. Notify graphics department."); + hsAssert(D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR == plLayerInterface::kUVWReflect, "D3D Enum has changed. Notify graphics department."); + + // Initialize everything to NULL. + IClearMembers(); + + // Get the requested mode and setup + const hsG3DDeviceRecord *devRec = devModeRec->GetDevice(); + const hsG3DDeviceMode *devMode = devModeRec->GetMode(); + + /// Init our screen mode + fSettings.fHWnd = hWnd; + if(!fInitialPipeParams.Windowed) + { + fSettings.fOrigWidth = devMode->GetWidth(); + fSettings.fOrigHeight = devMode->GetHeight(); + } + else + { + // windowed can run in any mode + fSettings.fOrigHeight = fInitialPipeParams.Height; + fSettings.fOrigWidth = fInitialPipeParams.Width; + } + IGetViewTransform().SetScreenSize((UInt16)(fSettings.fOrigWidth), (UInt16)(fSettings.fOrigHeight)); + fSettings.fColorDepth = devMode->GetColorDepth(); + fVSync = fInitialPipeParams.VSync; + + if( devRec->GetAASetting() == 0 ) + fSettings.fNumAASamples = 0; + else + fSettings.fNumAASamples = devMode->GetFSAAType( devRec->GetAASetting() - 1 ); + + hsGDirect3DTnLEnumerate d3dEnum; + if( d3dEnum.GetEnumeErrorStr()[ 0 ] ) + { + IShowErrorMessage( (char *)d3dEnum.GetEnumeErrorStr() ); + return; + } + + if( d3dEnum.SelectFromDevMode(devRec, devMode) ) + { + IShowErrorMessage( (char *)d3dEnum.GetEnumeErrorStr() ); + return; + } + + // Gotta create this very first, so that the device/driver init works + if( !fD3DObject ) + { + if( ICreateMaster() ) + { + IShowErrorMessage( "Cannot create D3D master object" ); + return; + } + } + + // Record the requested mode/setup. + ISetCurrentDriver( d3dEnum.GetCurrentDriver() ); + ISetCurrentDevice( d3dEnum.GetCurrentDevice() ); + D3DEnum_ModeInfo *pModeInfo = d3dEnum.GetCurrentMode(); + pModeInfo->fWindowed = fInitialPipeParams.Windowed; // set windowed mode from ini file + ISetCurrentMode( d3dEnum.GetCurrentMode() ); + + fSettings.fFullscreen = !fCurrentMode->fWindowed; + + fSettings.fNumAASamples = fInitialPipeParams.AntiAliasingAmount; + + // ISetCaps just records the card capabilities that were passed in. + ISetCaps(); + // IRestrictCaps looks over those explicit caps and makes some decisions on + // what the card can really do. + IRestrictCaps( *devRec ); + + fSettings.fMaxAnisotropicSamples = fInitialPipeParams.AnisotropicLevel; + if(fSettings.fMaxAnisotropicSamples > fCurrentDevice->fDDCaps.MaxAnisotropy) + fSettings.fMaxAnisotropicSamples = (UInt8)fCurrentDevice->fDDCaps.MaxAnisotropy; + + + plConst(UInt32) kDefaultDynVtxSize(32000 * 44); + plConst(UInt32) kDefaultDynIdxSize(0 * plGBufferGroup::kMaxNumIndicesPerBuffer * 2); + fDynVtxSize = kDefaultDynVtxSize; + fVtxRefTime = 0; + + // Go create surfaces and DX-dependent objects + if( ICreateDeviceObjects() ) + { + IShowErrorMessage( "Cannot create Direct3D device" ); + return; + } + /*plStatusLog::AddLineS("pipeline.log", "Supported Resolutions:"); + std::vector temp; + GetSupportedDisplayModes( &temp, 16 ); + for(int i = 0; i < temp.size(); i++) + { + plStatusLog::AddLineS("pipeline.log", "%d, %d, %d", temp[i].Width, temp[i].Height, 16); + } + temp.clear(); + GetSupportedDisplayModes( &temp, 32 ); + for(int i = 0; i < temp.size(); i++) + { + plStatusLog::AddLineS("pipeline.log", "%d, %d, %d", temp[i].Width, temp[i].Height, 32); + }*/ + +} + +// Cleanup - Most happens in IReleaseDeviceObject(). +plDXPipeline::~plDXPipeline() +{ + fCurrLay = nil; + hsAssert( fCurrMaterial == nil, "Current material not unrefed properly" ); + + // fCullProxy is a debugging representation of our CullTree. See plCullTree.cpp, + // plScene/plOccluder.cpp and plScene/plOccluderProxy.cpp for more info + if( fCullProxy ) + fCullProxy->GetKey()->UnRefObject(); + delete fCurrentDriver; + delete fCurrentDevice; + delete fCurrentMode; + + IReleaseDeviceObjects(); + IClearClothingOutfits(&fClothingOutfits); + IClearClothingOutfits(&fPrevClothingOutfits); +} + +//// IClearMembers //////////////////////////////////////////////////////////// +// Initialize everything to a nil state. +// This does not initialize to a working state, but to a state that can be +// built from. For example, the fD3DObject pointer is set to nil so that it's safe +// to delete or set to a valid pointer. It must be set to a valid pointer +// before the pipeline can be used for much. +// After the core initialization is done (in ICreateMaster and ICreateDeviceObjects) +// render state will be initialized in IInitDeviceState. + +void plDXPipeline::IClearMembers() +{ + /// Clear some stuff + fVtxBuffRefList = nil; + fIdxBuffRefList = nil; + fTextureRefList = nil; + fTextFontRefList = nil; + fRenderTargetRefList = nil; + fVShaderRefList = nil; + fPShaderRefList = nil; + fCurrMaterial = nil; + fCurrLay = nil; + fCurrRenderLayer = 0; +#if MCN_BOUNDS_SPANS + fBoundsMat = nil; + fBoundsSpans = nil; +#endif + fPlateMgr = nil; + fLogDrawer = nil; + fDebugTextMgr = nil; + fCurrLightingMethod = plSpan::kLiteMaterial; + + fCurrCullMode = D3DCULL_CW; + fTexturing = false; + fCurrNumLayers = 0; + fLastEndingStage = -1; + + fSettings.Reset(); + fStencil.Reset(); + fTweaks.Reset(); + fLights.Reset(this); + fCurrFog.Reset(); + fDeviceLost = false; + fDevWasLost = false; + + fSettings.fCurrFVFFormat = 0; + fDynVtxBuff = nil; + fNextDynVtx = 0; + + int i; + for( i = 0; i < 8; i++ ) + fLayerRef[i] = nil; + + IResetRenderTargetPools(); + fULutTextureRef = nil; + for( i = 0; i < kMaxRenderTargetNext; i++ ) + fBlurVBuffers[i] = nil; + fBlurVSHandle = nil; + + fD3DObject = nil; + fD3DDevice = nil; + fD3DBackBuff = nil; + fD3DDepthSurface = nil; + fD3DMainSurface = nil; + + fSharedDepthSurface[0] = nil; + fSharedDepthFormat[0] = D3DFMT_UNKNOWN; + fSharedDepthSurface[1] = nil; + fSharedDepthFormat[1] = D3DFMT_UNKNOWN; + + fCurrentMode = nil; + fCurrentDriver = nil; + fCurrentDevice = nil; + + fOverLayerStack.Reset(); + fOverBaseLayer = nil; + fOverAllLayer = nil; + fPiggyBackStack.Reset(); + fMatPiggyBacks = 0; + fActivePiggyBacks = 0; + + for( i = 0; i < 8; i++ ) + { + fLayerState[i].Reset(); + fOldLayerState[i].Reset(); + } + fMatOverOn.Reset(); + fMatOverOff.Reset(); +// SetMaterialOverride( hsGMatState::kShade, hsGMatState::kShadeSpecularHighlight, false ); + + fView.Reset(); + + fCullProxy = nil; + + fTime = 0; + fFrame = 0; + + fInSceneDepth = 0; + fTextUseTime = 0; + fEvictTime = 0; + fManagedSeen = 0; + fManagedCutoff = 0; + fRenderCnt = 0; + + fDebugFlags.Clear(); + + fForceMatHandle = true; + fAvRTShrinkValidSince = 0; + fAvRTWidth = 1024; + fAvNextFreeRT = 0; +} + +// plDXViewSettings are just a convenience member struct to segregate the current view settings. +// +// Reset - Initialize the ViewSettings to default (normal/neutral) values. +void plDXViewSettings::Reset() +{ + // Normal render, on clear, clear the color buffer and depth buffer. + fRenderState = plPipeline::kRenderNormal | plPipeline::kRenderClearColor | plPipeline::kRenderClearDepth; + + fRenderRequest = nil; + + fDrawableTypeMask = plDrawable::kNormal; + fSubDrawableTypeMask = plDrawable::kSubNormal; + + // Clear color to black, depth to yon. + fClearColor = 0; + fClearDepth = 1.f; + fDefaultFog.Clear(); + + // Want to limit the number of nodes in the cull tree. After adding so many nodes, + // the benefits (#objects culled) falls off, but the cost (evaluating objects against + // node planes) keeps rising. + const UInt16 kCullMaxNodes = 250; + fCullTree.Reset(); + fCullTreeDirty = true; + fCullMaxNodes = kCullMaxNodes; + + // Object Local to world transform and its inverse. + fLocalToWorld.Reset(); + fWorldToLocal.Reset(); + + // see Core/plViewTransform.h + fTransform.Reset(); + + fTransform.SetScreenSize(800, 600); + + // Keep track of handedness of local to world and camera transform for winding. + fLocalToWorldLeftHanded = false; + fWorldToCamLeftHanded = false; +} + +//// plDXGeneralSettings::Reset ////////////////////////////////////////////// +// Catch all struct of general settings plus pointers to current d3d objects. + +void plDXGeneralSettings::Reset() +{ + fCurrVertexBuffRef = nil; + fCurrIndexBuffRef = nil; + fFullscreen = false; + fHWnd = nil; + fColorDepth = 32; + fD3DCaps = 0; + fBoardKluge = 0; + fStageEnd = 0; + fMaxNumLights = kD3DMaxTotalLights; + fMaxNumProjectors = kMaxProjectors; + fMaxLayersAtOnce = 1; + fMaxPiggyBacks = 0; + fBoundsDrawLevel = -1; + + fProperties = 0; + fClearColor = 0; + + fNoGammaCorrect = false; + fMaxUVWSrc = 8; + fCantProj = false; + fLimitedProj = false; + fBadManaged = false; + fShareDepth = false; + fCurrAnisotropy = false; + fIsIntel = false; + + fDXError = D3D_OK; + memset( fErrorStr, 0, sizeof( fErrorStr ) ); + + fCurrRenderTarget = nil; + fCurrBaseRenderTarget = nil; + fCurrD3DMainSurface = nil; + fCurrD3DDepthSurface = nil; + fCurrRenderTargetRef = nil; + + fCurrFVFFormat = 0; + fCurrVertexShader = nil; + fCurrPixelShader = nil; + + fVeryAnnoyingTextureInvalidFlag = false; +} + +//// IInitDeviceState ///////////////////////////////////////////////////////// +// Initialize the device to a known state. This also syncs it up with our internal state +// as recorded in the fLayerStates. +// Some of these states reflect the caps of the device, but for the most part, the +// important thing here is NOT what state we're in coming out of this function, but +// that we are in a known state, and that the known state is recorded in fLayerStates. +void plDXPipeline::IInitDeviceState() +{ + fLayerState[0].Reset(); + fCurrCullMode = D3DCULL_CW; + + /// Set D3D states + fCurrFog.Reset(); + ISetFogParameters( nil, nil ); + + fD3DDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL ); + fD3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE ); + fD3DDevice->SetRenderState( D3DRS_ZENABLE, ( fSettings.fD3DCaps & kCapsWBuffer ) ? D3DZB_USEW : D3DZB_TRUE ); + fD3DDevice->SetRenderState( D3DRS_CLIPPING, TRUE ); + fD3DDevice->SetRenderState( D3DRS_CULLMODE, fCurrCullMode ); + ISetCullMode(); + + fD3DDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE ); + fD3DDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL ); + fD3DDevice->SetRenderState( D3DRS_ALPHAREF, 0x00000001 ); + + fD3DDevice->SetRenderState( D3DRS_MULTISAMPLEANTIALIAS, ( fSettings.fD3DCaps & kCapsFSAntiAlias ) ? TRUE : FALSE ); + fD3DDevice->SetRenderState( D3DRS_ANTIALIASEDLINEENABLE, FALSE ); + + fD3DDevice->SetRenderState( D3DRS_DITHERENABLE, ( fSettings.fD3DCaps & kCapsDither ) ? TRUE : FALSE ); + fD3DDevice->SetRenderState( D3DRS_SPECULARENABLE, FALSE ); + fD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); + fCurrD3DLiteState = false; + fD3DDevice->SetRenderState( D3DRS_TEXTUREFACTOR, 0x0 ); + fD3DDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE ); + fD3DDevice->SetTransform( D3DTS_TEXTURE0, &d3dIdentityMatrix ); + fD3DDevice->SetTransform( D3DTS_WORLD, &d3dIdentityMatrix ); + + /// NEW: to compensate for scaling transformations that might screw up our nicely + /// normalized normals. Note: nVidia says this is as fast or faster than with + /// this disabled, but who knows what it'll do on other cards... + fD3DDevice->SetRenderState( D3DRS_NORMALIZENORMALS, TRUE ); + fD3DDevice->SetRenderState( D3DRS_LOCALVIEWER, TRUE ); + + UInt32 totalMem = fD3DDevice->GetAvailableTextureMem(); + plProfile_Set(TotalTexSize, totalMem); + + // Initialization for all 8 stages (even though we only use a few of them). + int i; + for( i = 0; i < 8; i++ ) + { + fLayerLODBias[ i ] = fTweaks.fDefaultLODBias; + fLayerTransform[ i ] = false; + fLayerRef[ i ] = nil; + fLayerUVWSrcs[ i ] = i; + fLayerState[ i ].Reset(); + + fD3DDevice->SetTexture( i, nil ); + fD3DDevice->SetTextureStageState( i, D3DTSS_TEXCOORDINDEX, i ); + fD3DDevice->SetSamplerState( i, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP ); + fD3DDevice->SetSamplerState( i, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP ); + fD3DDevice->SetSamplerState( i, D3DSAMP_MIPMAPLODBIAS, *(DWORD *)( &fLayerLODBias[ i ] ) ); + + if( fSettings.fMaxAnisotropicSamples > 0 && !IsDebugFlagSet(plPipeDbg::kFlagNoAnisotropy)) + { + fD3DDevice->SetSamplerState( i, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC ); + fD3DDevice->SetSamplerState( i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); + fD3DDevice->SetSamplerState( i, D3DSAMP_MAXANISOTROPY, (DWORD)fSettings.fMaxAnisotropicSamples ); + fSettings.fCurrAnisotropy = true; + } + else + { + fD3DDevice->SetSamplerState( i, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); + fD3DDevice->SetSamplerState( i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); + fSettings.fCurrAnisotropy = false; + } + fD3DDevice->SetSamplerState( i, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR ); + + fD3DDevice->SetTransform( sTextureStages[ i ], &d3dIdentityMatrix ); + fLayerXformFlags[ i ] = D3DTTFF_COUNT2; + fD3DDevice->SetTextureStageState( i, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ); + } + + // Initialize our bump mapping matrices. + for( i = 0; i < 4; i++ ) + { + int j; + for( j = 0; j < 4; j++ ) + { + fBumpDuMatrix.fMap[i][j] = 0; + fBumpDvMatrix.fMap[i][j] = 0; + fBumpDwMatrix.fMap[i][j] = 0; + + } + } + fBumpDuMatrix.NotIdentity(); + fBumpDvMatrix.NotIdentity(); + fBumpDwMatrix.NotIdentity(); + + PushMaterialOverride( hsGMatState::kShade, hsGMatState::kShadeSpecularHighlight, false ); + + fLights.Reset(this); + + return; +} + +//// ISetCaps ///////////////////////////////////////////////////////////////// +// We've recorded the capabilities of the current device in fCurrentDevice (traditionally in the setup program), +// now translate that into our own caps flags. +void plDXPipeline::ISetCaps() +{ + fSettings.fD3DCaps = kCapsNone; + + // Set relevant caps (ones we can do something about). + if( fCurrentDevice->fDDCaps.RasterCaps & D3DPRASTERCAPS_DEPTHBIAS ) + fSettings.fD3DCaps |= kCapsZBias; + if( fCurrentDevice->fDDCaps.RasterCaps & D3DPRASTERCAPS_FOGRANGE ) + fSettings.fD3DCaps |= kCapsRangeFog; + if( fCurrentDevice->fDDCaps.RasterCaps & D3DPRASTERCAPS_FOGTABLE ) + fSettings.fD3DCaps |= kCapsLinearFog | kCapsExpFog | kCapsExp2Fog | kCapsPixelFog; + else + fSettings.fD3DCaps |= kCapsLinearFog; + if( fCurrentDevice->fDDCaps.TextureFilterCaps & D3DPTFILTERCAPS_MIPFLINEAR ) + fSettings.fD3DCaps |= kCapsMipmap; + if( fCurrentDevice->fDDCaps.TextureCaps & D3DPTEXTURECAPS_MIPCUBEMAP ) + fSettings.fD3DCaps |= kCapsCubicMipmap; + if( fCurrentDevice->fDDCaps.RasterCaps & D3DPRASTERCAPS_WBUFFER ) + fSettings.fD3DCaps |= kCapsWBuffer; + if( fCurrentDevice->fDDCaps.RasterCaps & D3DPRASTERCAPS_DITHER ) + fSettings.fD3DCaps |= kCapsDither; + if( fSettings.fNumAASamples > 0 ) + fSettings.fD3DCaps |= kCapsFSAntiAlias; + if( fCurrentDevice->fDDCaps.RasterCaps & D3DPRASTERCAPS_WFOG ) + fSettings.fD3DCaps |= kCapsDoesWFog; + if( fCurrentDevice->fDDCaps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP ) + fSettings.fD3DCaps |= kCapsCubicTextures; + + /// New 1.5.2000 - cull out mixed vertex processing + if( fCurrentDevice->fDDCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT + && fCurrentMode->fDDBehavior == D3DCREATE_HARDWARE_VERTEXPROCESSING + ) + fSettings.fD3DCaps |= kCapsHWTransform; + + + // Currently always want d3d to transform + fSettings.fD3DCaps |= kCapsHWTransform; + + /// Always assume we can do small textures (IRestrictCaps will turn this off + /// if necessary) + fSettings.fD3DCaps |= kCapsDoesSmallTextures; + + /// Look for supported texture formats + if( IFindCompressedFormats() ) + fSettings.fD3DCaps |= kCapsCompressTextures; + if( IFindLuminanceFormats() ) + fSettings.fD3DCaps |= kCapsLuminanceTextures; + + /// Max # of hardware lights + fSettings.fMaxNumLights = fCurrentDevice->fDDCaps.MaxActiveLights; + if( fSettings.fMaxNumLights > kD3DMaxTotalLights ) + fSettings.fMaxNumLights = kD3DMaxTotalLights; + + // Intel Extreme chips report 0 lights, meaning T&L is done + // in software, so you can have as many lights as you want. + // We only need 8, so set that here. Also turn off shadows, + // since the extreme can't really afford them, and record + // the fact this is the extreme for other driver problem + // workarounds. + if( !fSettings.fMaxNumLights ) + { + fSettings.fMaxNumLights = kD3DMaxTotalLights; + fSettings.fIsIntel = true; + plShadowCaster::SetCanShadowCast(false); + } + + /// Max # of textures at once + fSettings.fMaxLayersAtOnce = fCurrentDevice->fDDCaps.MaxSimultaneousTextures; + if( fCurrentDevice->fDDCaps.DevCaps & D3DDEVCAPS_SEPARATETEXTUREMEMORIES ) + fSettings.fMaxLayersAtOnce = 1; + // Alloc half our simultaneous textures to piggybacks. + // Won't hurt us unless we try to many things at once. + fSettings.fMaxPiggyBacks = fSettings.fMaxLayersAtOnce >> 1; + + // Less than 4 layers at once means we have to fallback on uv bumpmapping + if (fSettings.fMaxLayersAtOnce < 4) + SetDebugFlag(plPipeDbg::kFlagBumpUV, true); + + fSettings.fMaxAnisotropicSamples = (UInt8)(fCurrentDevice->fDDCaps.MaxAnisotropy); + + fSettings.fNoGammaCorrect = !(fCurrentDevice->fDDCaps.Caps2 & D3DCAPS2_FULLSCREENGAMMA); + + if (!(fCurrentDevice->fDDCaps.TextureCaps & D3DPTEXTURECAPS_PROJECTED)) + plDynamicCamMap::SetCapable(false); + + ISetGraphicsCapability(fCurrentDevice->fDDCaps.PixelShaderVersion); +} + +// ISetGraphicsCapability /////////////////////////////////////////////////////// +// Tell our global quality settings what we can do. We'll use this to only load +// versions we can render. So if we can render it, we load it and skip its low quality substitute, +// if we can't render it, we skip it and load its low quality substitute. +// Naturally, this must happen before we do any loading. +void plDXPipeline::ISetGraphicsCapability(UInt32 v) +{ + int pixelMajor = D3DSHADER_VERSION_MAJOR(v); + int pixelMinor = D3DSHADER_VERSION_MINOR(v); + if( pixelMajor > 1 ) + { + plQuality::SetCapability(plQuality::kPS_2_Plus); + } + else if( pixelMajor > 0 ) + { + if( pixelMinor >= 4 ) + plQuality::SetCapability(plQuality::kPS_1_4); + else if( pixelMinor > 0 ) + plQuality::SetCapability(plQuality::kPS_1_1); + } +} + +//// IRestrictCaps //////////////////////////////////////////////////////////// +// ISetCaps() sets our native caps based on the D3D caps bits D3D returns. +// IRestrictCaps looks at our hsG3DDeviceSelector flags and translates those +// into our runtime native caps. +// The DeviceSelector flags aren't set by what the board claims, but rather +// we try to identify the board and set them according to previous knowledge. +// For example, the ATI7500 will only use uvw coordinates 0 or 1. There's +// no d3d cap to reflect this, and it really should support [0..7], but +// there's no way to force it to be d3d compliant. So when we see we have +// an ATI7500, we set the cap kCapsMaxUVWSrc2. +// See hsG3DDeviceSelector.cpp for details and implementation. +void plDXPipeline::IRestrictCaps( const hsG3DDeviceRecord& devRec ) +{ + if( !devRec.GetCap( hsG3DDeviceSelector::kCapsMipmap ) ) + fSettings.fD3DCaps &= ~kCapsMipmap; + if( !devRec.GetCap( hsG3DDeviceSelector::kCapsCubicMipmap ) ) + fSettings.fD3DCaps &= ~kCapsCubicMipmap; + if( !devRec.GetCap( hsG3DDeviceSelector::kCapsWBuffer ) ) + fSettings.fD3DCaps &= ~kCapsWBuffer; + if( !devRec.GetCap( hsG3DDeviceSelector::kCapsZBias ) ) + fSettings.fD3DCaps &= ~kCapsZBias; +// if( !devRec.GetCap( hsG3DDeviceSelector::kCapsHWTransform ) ) +// fSettings.fD3DCaps &= ~kCapsHWTransform; + if( !devRec.GetCap( hsG3DDeviceSelector::kCapsDither ) ) + fSettings.fD3DCaps &= ~kCapsDither; +// if( devRec.GetAASetting() == 0 ) +// fSettings.fD3DCaps &= ~kCapsFSAntiAlias; + if( !devRec.GetCap( hsG3DDeviceSelector::kCapsFogExp ) ) + fSettings.fD3DCaps &= ~kCapsExpFog; + if( !devRec.GetCap( hsG3DDeviceSelector::kCapsCubicTextures ) ) + fSettings.fD3DCaps &= ~kCapsCubicTextures; + + if( devRec.GetCap(hsG3DDeviceSelector::kCapsCantShadow) ) + plShadowCaster::SetCanShadowCast(false); + + if( devRec.GetCap(hsG3DDeviceSelector::kCapsCantProj) ) + fSettings.fCantProj = true; + if( devRec.GetCap(hsG3DDeviceSelector::kCapsLimitedProj) ) + fSettings.fLimitedProj = true; + if( devRec.GetCap(hsG3DDeviceSelector::kCapsBadManaged) ) + fSettings.fBadManaged = true; + if( devRec.GetCap(hsG3DDeviceSelector::kCapsShareDepth) ) + fSettings.fShareDepth = true; + + /// Added 9.6.2000 mcn - shouldn't they be here anyway? + if( !devRec.GetCap( hsG3DDeviceSelector::kCapsFogExp2 ) ) + fSettings.fD3DCaps &= ~kCapsExp2Fog; + if( !devRec.GetCap( hsG3DDeviceSelector::kCapsDoesSmallTextures ) ) + fSettings.fD3DCaps &= ~kCapsDoesSmallTextures; + + /// 9.22.2000 mcn - dFlag for bad (savage4) yon fix + if( devRec.GetCap( hsG3DDeviceSelector::kCapsBadYonStuff ) ) + fSettings.fD3DCaps |= kCapsHasBadYonStuff; + + /// 10.31.2000 mcn - Flag for can't-handle-under-8-pixel-dimensions-on-textures + /// (see, isn't the name flag actually better in retrospect? :) + if( devRec.GetCap( hsG3DDeviceSelector::kCapsNoKindaSmallTexs ) ) + fSettings.fD3DCaps |= kCapsNoKindaSmallTexs; + + /// Note: the following SHOULD be here, but we later detect for texture + /// formats and reset this flag. It should only be set if it is set already, + /// but that means ensuring it's set beforehand, which it might not be. + if( !devRec.GetCap( hsG3DDeviceSelector::kCapsCompressTextures ) ) + fSettings.fD3DCaps &= ~kCapsCompressTextures; + + /// Set up tweaks + SetZBiasScale( (float)devRec.GetZBiasRating() ); + fTweaks.fDefaultLODBias = (float)-( 0.25 + (float)devRec.GetLODBiasRating() ); + devRec.GetFogApproxStarts( fTweaks.fFogExpApproxStart, fTweaks.fFogExp2ApproxStart ); + fTweaks.fFogEndBias = (float)devRec.GetFogEndBias(); + + // Fog knee stuff + devRec.GetFogKneeParams( hsG3DDeviceRecord::kFogExp, fTweaks.fExpFogKnee, fTweaks.fExpFogKneeVal ); + devRec.GetFogKneeParams( hsG3DDeviceRecord::kFogExp2, fTweaks.fExp2FogKnee, fTweaks.fExp2FogKneeVal ); + + // Max # of layers + UInt32 max = devRec.GetLayersAtOnce(); + if( max > 0 && max < fSettings.fMaxLayersAtOnce ) + fSettings.fMaxLayersAtOnce = max; + + /// Debug flag to force high-level cards down to GeForce 2 caps + if( fDbgSetupInitFlags & 0x00000004 ) + { + fSettings.fD3DCaps &= ~kCapsFSAntiAlias; + if( fSettings.fMaxLayersAtOnce > 2 ) + fSettings.fMaxLayersAtOnce = 2; + fSettings.fMaxAnisotropicSamples = 0; + + plQuality::SetCapability(plQuality::kMinimum); + } + + // There's a bug in NVidia drivers on Windows 2000 for GeForce1-4 (all flavors, including MX). + // When the amount allocated into managed memory approaches the on board memory size, the performance + // severely degrades, no matter how little is actually in use in the current rendering. So say all + // our d3d textures are created into managed memory at age load. Also say you are + // consistently viewing only 5Mb of managed materials (texture + vertex buffer). So as + // you walk through the age, the new textures you see get loaded on demand into video memory. + // Once you've seen enough to fill the on board memory, your frame rate starts falling and + // continues to fall as more textures get loaded. So either the memory manager is not letting + // go of LRU textures, or fragmentation is so horrible as to make the manager useless. + // So on these boards and with this OS, we keep track of how much managed memory we've seen, + // and when it reaches a threshhold, we flush managed memory with an EvictManagedResources() call. + // There's an unfortunate glitch, and then the frame rate is fine again. + // So if we need this workaround, we set fManagedCutoff to 1 here, and then once we have our + // D3D device, we query for the amount of memory and set the threshhold for flushing memory + // based on that. + OSVERSIONINFO osinfo; + memset(&osinfo, 0, sizeof(osinfo)); + osinfo.dwOSVersionInfoSize = sizeof(osinfo); + GetVersionEx(&osinfo); + if( (osinfo.dwMajorVersion == 5) + &&(osinfo.dwMinorVersion == 0) ) + { + // It's the dreaded win2k + if( devRec.GetCap(hsG3DDeviceSelector::kCapsDoubleFlush) ) + fManagedCutoff = 1; + else if( devRec.GetCap(hsG3DDeviceSelector::kCapsSingleFlush) ) + fManagedCutoff = 1; + } + + //// Our temp debug flag to force z-buffering... + if( !( fDbgSetupInitFlags & 0x00000001 ) ) + fSettings.fD3DCaps &= ~kCapsWBuffer; + + /// Set up the z-bias scale values, based on z- or w-buffering + if( fSettings.fD3DCaps & kCapsWBuffer ) + fTweaks.fDefaultPerspLayerScale = kPerspLayerScaleW; + else + fTweaks.fDefaultPerspLayerScale = kPerspLayerScale; + + + // Less than 4 layers at once means we have to fallback on uv bumpmapping + if( fSettings.fMaxLayersAtOnce < 4 ) + SetDebugFlag(plPipeDbg::kFlagBumpUV, true); + + if( ( fSettings.fD3DCaps & kCapsHWTransform ) && ( fCurrentMode->fDDBehavior == D3DCREATE_SOFTWARE_VERTEXPROCESSING ) ) + fSettings.fD3DCaps &= ~kCapsHWTransform; + + if( devRec.GetCap(hsG3DDeviceSelector::kCapsMaxUVWSrc2) ) + fSettings.fMaxUVWSrc = 2; + + /// Anisotropy stuff + //if( devRec.GetMaxAnisotropicSamples() < fSettings.fMaxAnisotropicSamples ) + // fSettings.fMaxAnisotropicSamples = devRec.GetMaxAnisotropicSamples(); + if( devRec.GetCap(hsG3DDeviceSelector::kCapsNoAniso) || (fSettings.fMaxAnisotropicSamples <= 1) ) + fSettings.fMaxAnisotropicSamples = 0; +} + +//// Get/SetZBiasScale //////////////////////////////////////////////////////// +// If the board really doesn't support Z-biasing, we adjust the perspective matrix in IGetCameraToNDC +// The layer scale and translation are tailored to the current hardware. +hsScalar plDXPipeline::GetZBiasScale() const +{ + return ( fTweaks.fPerspLayerScale / fTweaks.fDefaultPerspLayerScale ) - 1.0f; +} + +void plDXPipeline::SetZBiasScale( hsScalar scale ) +{ + scale += 1.0f; + fTweaks.fPerspLayerScale = fTweaks.fDefaultPerspLayerScale * scale; + fTweaks.fPerspLayerTrans = kPerspLayerTrans * scale; +} + +// Create all our video memory consuming D3D objects. +hsBool plDXPipeline::ICreateDynDeviceObjects() +{ + // Front/Back/Depth buffers + if( ICreateNormalSurfaces() ) + return true; + + // RenderTarget pools are shared for our shadow generation algorithm. + // Different sizes for different resolutions. + IMakeRenderTargetPools(); + + // Create device-specific stuff + fDebugTextMgr = TRACKED_NEW plDebugTextManager(); + if( fDebugTextMgr == nil ) + return true; + + // Vertex buffers, index buffers, textures, etc. + LoadResources(); + + return false; +} +//// ICreateDeviceObjects ///////////////////////////////////////////////////// +// Create all of our steady state D3D objects. More D3D objects will be created +// and destroyed as ages are loaded and unloaded, but these are the things that +// only go away when we lose the device. + +hsBool plDXPipeline::ICreateDeviceObjects() +{ + // The D3D device + if( ICreateDevice(!fSettings.fFullscreen) ) + return true; + + // Most everything else D3D + if( ICreateDynDeviceObjects() ) + return true; + + // PlateMgr is largely for debugging and performance stats, + // but also gets used for some things like the cursor and + // linking fade to/from black. + fPlateMgr = TRACKED_NEW plDXPlateManager( this, fD3DDevice ); + if( fPlateMgr == nil || !fPlateMgr->IsValid() ) + return true; + + // We've got everything created now, initialize to a known state. + IInitDeviceState(); + if( FAILED( fD3DDevice->Clear( 0, nil, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, fSettings.fClearColor, 1.0f, 0L ) ) ) + return true; + + // You may be wondering what this is. It's a workaround for a GeForce2 driver bug, where + // clears to the Zbuffer (but not color) are getting partially ignored. Don't even ask. + // So this is just to try and get the board used to the kind of foolishness it can expect + // from here out. + if( FAILED( fD3DDevice->Clear( 0, nil, D3DCLEAR_ZBUFFER, fSettings.fClearColor, 1.0f, 0L ) ) ) + return true; + if( FAILED( fD3DDevice->Clear( 0, nil, D3DCLEAR_ZBUFFER, fSettings.fClearColor, 1.0f, 0L ) ) ) + return true; + if( FAILED( fD3DDevice->Clear( 0, nil, D3DCLEAR_ZBUFFER, fSettings.fClearColor, 1.0f, 0L ) ) ) + return true; + + /// Log renderer + fLogDrawer = TRACKED_NEW plStatusLogDrawer( this ); + plStatusLogMgr::GetInstance().SetDrawer( fLogDrawer ); + + /// Ok, we're done now +#if MCN_BOUNDS_SPANS + fBoundsSpans = TRACKED_NEW plDrawableSpans(); + hsgResMgr::ResMgr()->NewKey( "BoundsSpans", fBoundsSpans, plLocation::kGlobalFixedLoc ); + fBoundsSpans->SetNativeProperty( plDrawable::kPropVolatile, true ); + fBoundsMat = TRACKED_NEW hsGMaterial(); + hsgResMgr::ResMgr()->NewKey( "BoundsMaterial", fBoundsMat, plLocation::kGlobalFixedLoc ); + plLayer *lay = fBoundsMat->MakeBaseLayer(); + lay->SetMiscFlags( hsGMatState::kMiscWireFrame | hsGMatState::kMiscTwoSided ); + lay->SetShadeFlags( lay->GetShadeFlags() | hsGMatState::kShadeWhite ); + + // Set up a ref to these. Since we don't have a key, we use the + // generic RefObject() (and matching UnRefObject() when we're done). + // If we had a key, we would use myKey->AddViaNotify(otherKey) and myKey->Release(otherKey). + fBoundsMat->GetKey()->RefObject(); + fBoundsSpans->GetKey()->RefObject(); +#endif + + return false; +} + +//// ISetCurrentDriver //////////////////////////////////////////////////////// +// Copy over the driver info. +void plDXPipeline::ISetCurrentDriver( D3DEnum_DriverInfo *driv ) +{ + if( fCurrentDriver != nil ) + delete fCurrentDriver; + + fCurrentDriver = TRACKED_NEW D3DEnum_DriverInfo; + + fCurrentDriver->fGuid = driv->fGuid; + hsStrncpy( fCurrentDriver->fStrDesc, driv->fStrDesc, 40 ); + hsStrncpy( fCurrentDriver->fStrName, driv->fStrName, 40 ); + + fCurrentDriver->fDesktopMode = driv->fDesktopMode; + fCurrentDriver->fAdapterInfo = driv->fAdapterInfo; + + fCurrentDriver->fCurrentMode = nil; + fCurrentDriver->fCurrentDevice = nil; + + /// Go looking for an adapter to match this one + UINT iAdapter; + for( fCurrentAdapter = 0, iAdapter = 0; iAdapter < fD3DObject->GetAdapterCount(); iAdapter++ ) + { + D3DADAPTER_IDENTIFIER9 adapterInfo; + fD3DObject->GetAdapterIdentifier( iAdapter, 0, &adapterInfo ); + + if( adapterInfo.DeviceIdentifier == fCurrentDriver->fAdapterInfo.DeviceIdentifier ) + { + fCurrentAdapter = iAdapter; + break; + } + } +} + +//// ISetCurrentDevice //////////////////////////////////////////////////////// +// Copy over the device info. +void plDXPipeline::ISetCurrentDevice( D3DEnum_DeviceInfo *dev ) +{ + if( fCurrentDevice != nil ) + delete fCurrentDevice; + fCurrentDevice = TRACKED_NEW D3DEnum_DeviceInfo; + + hsStrncpy( fCurrentDevice->fStrName, dev->fStrName, 40 ); + + fCurrentDevice->fDDCaps = dev->fDDCaps; + fCurrentDevice->fDDType = dev->fDDType; + fCurrentDevice->fIsHardware = dev->fIsHardware; + fCurrentDevice->fCanWindow = dev->fCanWindow; +// fCurrentDevice->fCanAntialias = dev->fCanAntialias; + fCurrentDevice->fCompatibleWithDesktop = dev->fCompatibleWithDesktop; + + // copy over supported device modes + D3DEnum_ModeInfo currMode; + + for(int i = 0; i < dev->fModes.Count(); i++) + { + // filter unusable modes + if(dev->fModes[i].fDDmode.Width < MIN_WIDTH || dev->fModes[i].fDDmode.Height < MIN_HEIGHT) + continue; + + currMode.fBitDepth = dev->fModes[i].fBitDepth; + currMode.fCanRenderToCubic = dev->fModes[i].fCanRenderToCubic; + currMode.fDDBehavior = dev->fModes[i].fDDBehavior; + currMode.fDepthFormats = dev->fModes[i].fDepthFormats; + currMode.fFSAATypes = dev->fModes[i].fFSAATypes; + memcpy(&currMode.fDDmode, &dev->fModes[i].fDDmode, sizeof(D3DDISPLAYMODE)); + strcpy(currMode.fStrDesc, dev->fModes[i].fStrDesc); + currMode.fWindowed = dev->fModes[i].fWindowed; + + fCurrentDevice->fModes.Push(currMode); + } +} + +//// ISetCurrentMode ////////////////////////////////////////////////////////// +// Copy over the mode info. +void plDXPipeline::ISetCurrentMode( D3DEnum_ModeInfo *mode ) +{ + if( fCurrentMode != nil ) + delete fCurrentMode; + fCurrentMode = TRACKED_NEW D3DEnum_ModeInfo; + + *fCurrentMode = *mode; +} + +//// IFindCompressedFormats /////////////////////////////////////////////////// +// +// New DX Way: Check to see if each format is valid. + +hsBool plDXPipeline::IFindCompressedFormats() +{ + D3DFORMAT toCheckFor[] = {D3DFMT_DXT1, + //D3DFMT_DXT2, + //D3DFMT_DXT3, + //D3DFMT_DXT4, + D3DFMT_DXT5, + D3DFMT_UNKNOWN }; + short i = 0; + + + for( i = 0; toCheckFor[ i ] != D3DFMT_UNKNOWN; i++ ) + { + if( FAILED( fD3DObject->CheckDeviceFormat( fCurrentAdapter, fCurrentDevice->fDDType, + fCurrentMode->fDDmode.Format, + 0, D3DRTYPE_TEXTURE, toCheckFor[ i ] ) ) ) + return false; + } + + /// Got here, must have found them all + return true; +} + +//// IFindLuminanceFormats //////////////////////////////////////////////////// +// +// New DX Way: Check to see if each format we want is valid + +hsBool plDXPipeline::IFindLuminanceFormats() +{ + D3DFORMAT toCheckFor[] = { D3DFMT_L8, D3DFMT_A8L8, D3DFMT_UNKNOWN }; + short i = 0; + + + for( i = 0; toCheckFor[ i ] != D3DFMT_UNKNOWN; i++ ) + { + if( FAILED( fD3DObject->CheckDeviceFormat( fCurrentAdapter, fCurrentDevice->fDDType, + fCurrentMode->fDDmode.Format, + 0, D3DRTYPE_TEXTURE, toCheckFor[ i ] ) ) ) + return false; + } + + /// Got here, must have found them all + return true; +} + +//// ITextureFormatAllowed //////////////////////////////////////////////////// +// +// Returns true if the given format is supported on the current device and +// mode, false if it isn't. + +hsBool plDXPipeline::ITextureFormatAllowed( D3DFORMAT format ) +{ + if( FAILED( fD3DObject->CheckDeviceFormat( fCurrentAdapter, fCurrentDevice->fDDType, + fCurrentMode->fDDmode.Format, + 0, D3DRTYPE_TEXTURE, format ) ) ) + return false; + + return true; +} + +//// SetDebugFlag ///////////////////////////////////////////////////////////// +// Debug flags should never be employed to do a game effect, although they can +// be useful for developing effects. Mostly they help in diagnosing problems +// in rendering or performance. +void plDXPipeline::SetDebugFlag( UInt32 flag, hsBool on ) +{ + fDebugFlags.SetBit(flag, on); + + if (flag == plPipeDbg::kFlagColorizeMipmaps) + { + // Force textures to reload + plDXTextureRef *ref = fTextureRefList; + while( ref != nil ) + { + ref->SetDirty( true ); + ref = ref->GetNext(); + } + + // Reset mipmap filtering state (usually is LINEAR, but we set it to POINT for coloring) + int i; + for( i = 0; i < 8; i++ ) + fD3DDevice->SetSamplerState( i, D3DSAMP_MIPFILTER, on ? D3DTEXF_POINT : D3DTEXF_LINEAR ); + } + + if (flag == plPipeDbg::kFlagNoAnisotropy) + { + ISetAnisotropy(!on); + } +} + +hsBool plDXPipeline::IsDebugFlagSet( UInt32 flag ) const +{ + return fDebugFlags.IsBitSet(flag); +} + +/////////////////////////////////////////////////////////////////////////////// +//// Device Creation ////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// ICreateMaster //////////////////////////////////////////////////////////// +// Creates the master Direct3D objects. I guess just in case you want +// multiple Direct3D devices.... :~ + +hsBool plDXPipeline::ICreateMaster() +{ + hsAssert( !fD3DObject, "ICreateMaster() should only be called for Master Direct3DDevice" ); + + /// The new DirectX Way: Create a Direct3D object, out of which everything else springs + if( hsGDDrawDllLoad::GetD3DDll() == nil ) + return ICreateFail( "Cannot load Direct3D driver!" ); + + Direct3DCreateProc procPtr; + procPtr = (Direct3DCreateProc)GetProcAddress( hsGDDrawDllLoad::GetD3DDll(), "Direct3DCreate9" ); + if( procPtr == nil ) + return ICreateFail( "Cannot load D3D Create Proc!" ); + + // Create a D3D object to use + fD3DObject = procPtr( D3D_SDK_VERSION ); + + if( fD3DObject == nil ) + return ICreateFail( "Cannot create Direct3D object" ); + + return false; +} + +//// ICreateDevice //////////////////////////////////////////////////// +// +// Creates the device. Surfaces, buffers, etc. created separately (in case of lost device). +// See ICreateDeviceObjects. + +hsBool plDXPipeline::ICreateDevice(hsBool windowed) +{ + /// First, create the D3D Device object + D3DPRESENT_PARAMETERS params; + D3DDISPLAYMODE dispMode; + int i; +#ifdef DBG_WRITE_FORMATS + char msg[ 256 ]; +#endif // DBG_WRITE_FORMATS + + INIT_ERROR_CHECK( fD3DObject->GetAdapterDisplayMode( fCurrentAdapter, &dispMode ), + "Cannot get desktop display mode" ); + + // save desktop properties + fDesktopParams.Width = dispMode.Width; + fDesktopParams.Height = dispMode.Height; + fDesktopParams.ColorDepth = GetDXBitDepth( dispMode.Format ); + + + if( windowed ) + { + // Reset fColor, since we're getting the desktop bitdepth + fSettings.fColorDepth = GetDXBitDepth( dispMode.Format ); + if(fSettings.fOrigWidth > fDesktopParams.Width || fSettings.fOrigHeight > fDesktopParams.Height) + { + fSettings.fOrigWidth = fDesktopParams.Width; + fSettings.fOrigHeight = fDesktopParams.Height; + IGetViewTransform().SetScreenSize(fDesktopParams.Width, fDesktopParams.Height); + } + } + + memset( ¶ms, 0, sizeof( params ) ); + params.Windowed = ( windowed ? TRUE : FALSE ); + params.Flags = 0;//D3DPRESENTFLAG_LOCKABLE_BACKBUFFER; + params.BackBufferCount = 1; + params.BackBufferWidth = GetViewTransform().GetScreenWidth(); + params.BackBufferHeight = GetViewTransform().GetScreenHeight(); + params.EnableAutoDepthStencil = TRUE; + + // NOTE: This was changed 5.29.2001 mcn to avoid the nasty flashing bug on nVidia's 12.60 beta drivers +// SWAPEFFECT must be _DISCARD when using antialiasing, so we'll just go with _DISCARD for the time being. mf + params.SwapEffect = D3DSWAPEFFECT_DISCARD; + params.FullScreen_RefreshRateInHz = ( windowed ? 0 : D3DPRESENT_RATE_DEFAULT ); + if(windowed) + { + params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + } + else + { + params.PresentationInterval = ( fVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE ); + } + +#ifdef DBG_WRITE_FORMATS + for( i = 0; i < fCurrentMode->fDepthFormats.GetCount(); i++ ) + { + sprintf( msg, "-- Valid depth buffer format: %s", IGetDXFormatName( fCurrentMode->fDepthFormats[ i ] ) ); + hsDebugMessage( msg, 0 ); + } +#endif + + // Attempt to find the closest AA setting we can + params.MultiSampleType = D3DMULTISAMPLE_NONE; + for( i = fSettings.fNumAASamples; i >= 2; i-- ) + { + if( fCurrentMode->fFSAATypes.Find( (D3DMULTISAMPLE_TYPE)i ) != fCurrentMode->fFSAATypes.kMissingIndex ) + { + params.MultiSampleType = (D3DMULTISAMPLE_TYPE)i; + break; + } + } + + if( !IFindDepthFormat(params) ) + { + // If we haven't found a depth format, turn off multisampling and try it again. + params.MultiSampleType = D3DMULTISAMPLE_NONE; + if( !IFindDepthFormat(params) ) + // Okay, we're screwed here, we might as well bail. + return ICreateFail( "Can't find a Depth Buffer format" ); + } + + /// TEMP HACK--if we're running 16-bit z-buffer or below, use our z-bias (go figure, it works better + /// in 16-bit, worse in 24 and 32) + if( params.AutoDepthStencilFormat == D3DFMT_D15S1 || + params.AutoDepthStencilFormat == D3DFMT_D16 || + params.AutoDepthStencilFormat == D3DFMT_D16_LOCKABLE ) + fSettings.fD3DCaps &= ~kCapsZBias; + +#ifdef DBG_WRITE_FORMATS + sprintf( msg, "-- Requesting depth buffer format: %s", IGetDXFormatName( params.AutoDepthStencilFormat ) ); + hsDebugMessage( msg, 0 ); +#endif + + + params.BackBufferFormat = ( windowed ? dispMode.Format : fCurrentMode->fDDmode.Format ); +#ifdef DBG_WRITE_FORMATS + sprintf( msg, "-- Requesting back buffer format: %s", IGetDXFormatName( params.BackBufferFormat ) ); + hsDebugMessage( msg, 0 ); +#endif + + params.hDeviceWindow = fSettings.fHWnd; + + // Enable this to switch to a pure device. +// fCurrentMode->fDDBehavior |= D3DCREATE_PUREDEVICE; +// fCurrentMode->fDDBehavior |= D3DCREATE_DISABLE_DRIVER_MANAGEMENT; + +#ifndef PLASMA_EXTERNAL_RELEASE + UINT adapter; + for (adapter = 0; adapter < fD3DObject->GetAdapterCount(); adapter++) + { + D3DADAPTER_IDENTIFIER9 id; + fD3DObject->GetAdapterIdentifier(adapter, 0, &id); + + // We should be matching against "NVIDIA NVPerfHUD", but the space + // in the description seems to be bogus. This seems to be a fair + // alternative + if (strstr(id.Description, "NVPerfHUD")) + { + // This won't actually use the REF device, but we ask for + // it as part of the handshake to let NVPerfHUD know we give + // it permission to analyze us. + fCurrentAdapter = adapter; + fCurrentDevice->fDDType= D3DDEVTYPE_REF; + SetDebugFlag(plPipeDbg::kFlagNVPerfHUD, true); + break; + } + } +#endif // PLASMA_EXTERNAL_RELEASE + + INIT_ERROR_CHECK( fD3DObject->CreateDevice( fCurrentAdapter, fCurrentDevice->fDDType, + fSettings.fHWnd, fCurrentMode->fDDBehavior, + ¶ms, &fD3DDevice ), + "Cannot create primary display surface via CreateDevice()" ); + + fSettings.fPresentParams = params; + + // This bit matches up with the fManagedCutoff workaround for a problem + // with the NVidia drivers on win2k. Search for "GetVersionEx" in IRestrictCaps + // for more info. + UInt32 mem = fD3DDevice->GetAvailableTextureMem(); + plProfile_IncCount(TexTot, mem); + + const UInt32 kSingleFlush(40000000); + const UInt32 kDoubleFlush(24000000); + if( fManagedCutoff ) + { + if( mem < 64000000 ) + fManagedCutoff = kDoubleFlush; + else + fManagedCutoff = kSingleFlush; + } + + return false; +} + +// IFindDepthFormat ////////////////////////////////////////////////////////////// +// Look through available depth formats for the closest to what we want that +// will work. +hsBool plDXPipeline::IFindDepthFormat(D3DPRESENT_PARAMETERS& params) +{ + // Okay, we're not using the stencil buffer right now, and it's bringing out + // some painful driver bugs on the GeForce2. So rather than go out of our way + // looking for trouble, we're going to look for a depth buffer with NO STENCIL. + int i; + for( i = fCurrentMode->fDepthFormats.GetCount() - 1; i >= 0; i-- ) + { + D3DFORMAT fmt = fCurrentMode->fDepthFormats[ i ]; + if( (fmt == D3DFMT_D32) + ||(fmt == D3DFMT_D24X8) + ||(fmt == D3DFMT_D16) ) + { + HRESULT hr = fD3DObject->CheckDeviceMultiSampleType(fCurrentAdapter, + fCurrentDevice->fDDType, + fmt, + fCurrentMode->fWindowed ? TRUE : FALSE, + params.MultiSampleType, NULL); + if( !FAILED(hr) ) + { + params.AutoDepthStencilFormat = fmt; + fStencil.fDepth = 0; + break; + } + } + } + if( i < 0 ) + { + for( i = fCurrentMode->fDepthFormats.GetCount() - 1; i >= 0; i-- ) + { + D3DFORMAT fmt = fCurrentMode->fDepthFormats[ i ]; + if( fmt == D3DFMT_D15S1 || fmt == D3DFMT_D24X4S4 || fmt == D3DFMT_D24S8 ) + { + HRESULT hr = fD3DObject->CheckDeviceMultiSampleType(fCurrentAdapter, + fCurrentDevice->fDDType, + fmt, + fCurrentMode->fWindowed ? TRUE : FALSE, + params.MultiSampleType, NULL); + if( !FAILED(hr) ) + { + params.AutoDepthStencilFormat = fmt; + if( fmt == D3DFMT_D15S1 ) + fStencil.fDepth = 1; + else if( fmt == D3DFMT_D24X4S4 ) + fStencil.fDepth = 4; + else + fStencil.fDepth = 8; + break; + } + } + } + } + return i >= 0; +} + +// ICreateNormalSurfaces ////////////////////////////////////////////////////// +// Create the primary color and depth buffers. +// +hsBool plDXPipeline::ICreateNormalSurfaces() +{ + /// Now get the backbuffer surface pointer + INIT_ERROR_CHECK( fD3DDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &fD3DBackBuff ), + "Cannot get primary surface's back buffer" ); + + /// And finally, get the main D3D surfaces (for restoring after rendertargets ) + INIT_ERROR_CHECK( fD3DDevice->GetRenderTarget( 0, &fD3DMainSurface ), "Cannot capture primary surface" ); + INIT_ERROR_CHECK( fD3DDevice->GetDepthStencilSurface( &fD3DDepthSurface ), "Cannot capture primary depth surface" ); + + fSettings.fCurrD3DMainSurface = fD3DMainSurface; + fSettings.fCurrD3DDepthSurface = fD3DDepthSurface; + + D3DSURF_MEMNEW( fD3DMainSurface ); + D3DSURF_MEMNEW( fD3DDepthSurface ); + D3DSURF_MEMNEW( fD3DBackBuff ); + + D3DSURFACE_DESC info; + fD3DMainSurface->GetDesc( &info ); + fD3DDepthSurface->GetDesc( &info ); + fD3DBackBuff->GetDesc( &info ); + + return false; +} + +// IReleaseRenderTargetPools ////////////////////////////////////////////////// +// Free up all resources assosiated with our pools of rendertargets of varying +// sizes. Primary user of these pools is the shadow generation. +void plDXPipeline::IReleaseRenderTargetPools() +{ + int i; + + for( i = 0; i < fRenderTargetPool512.GetCount(); i++ ) + { + delete fRenderTargetPool512[i]; + fRenderTargetPool512[i] = nil; + } + fRenderTargetPool512.SetCount(0); + + for( i = 0; i < fRenderTargetPool256.GetCount(); i++ ) + { + delete fRenderTargetPool256[i]; + fRenderTargetPool256[i] = nil; + } + fRenderTargetPool256.SetCount(0); + + for( i = 0; i < fRenderTargetPool128.GetCount(); i++ ) + { + delete fRenderTargetPool128[i]; + fRenderTargetPool128[i] = nil; + } + fRenderTargetPool128.SetCount(0); + + for( i = 0; i < fRenderTargetPool64.GetCount(); i++ ) + { + delete fRenderTargetPool64[i]; + fRenderTargetPool64[i] = nil; + } + fRenderTargetPool64.SetCount(0); + + for( i = 0; i < fRenderTargetPool32.GetCount(); i++ ) + { + delete fRenderTargetPool32[i]; + fRenderTargetPool32[i] = nil; + } + fRenderTargetPool32.SetCount(0); + + for( i = 0; i < kMaxRenderTargetNext; i++ ) + { + fRenderTargetNext[i] = 0; + fBlurScratchRTs[i] = nil; + fBlurDestRTs[i] = nil; + } + +#ifdef MF_ENABLE_HACKOFF + hackOffscreens.Reset(); +#endif // MF_ENABLE_HACKOFF +} + +// IReleaseDynDeviceObjects ////////////////////////////////////////////// +// Make sure we aren't holding on to anything, and release all of +// the D3D resources that we normally hang on to forever. Meaning things +// that persist through unloading one age and loading the next. +void plDXPipeline::IReleaseDynDeviceObjects() +{ + // We should do this earlier, but the textFont objects don't remove + // themselves from their parent objects yet + delete fDebugTextMgr; + fDebugTextMgr = nil; + + if( fD3DDevice ) + { + fD3DDevice->SetStreamSource(0, nil, 0, 0); + fD3DDevice->SetIndices(nil); + } + + /// Delete actual d3d objects + hsRefCnt_SafeUnRef( fSettings.fCurrVertexBuffRef ); + fSettings.fCurrVertexBuffRef = nil; + hsRefCnt_SafeUnRef( fSettings.fCurrIndexBuffRef ); + fSettings.fCurrIndexBuffRef = nil; + + while( fTextFontRefList ) + delete fTextFontRefList; + + while( fRenderTargetRefList ) + { + plDXRenderTargetRef* rtRef = fRenderTargetRefList; + rtRef->Release(); + rtRef->Unlink(); + } + + // The shared dynamic vertex buffers used by things like objects skinned on CPU, or + // particle systems. + IReleaseDynamicBuffers(); + IReleaseAvRTPool(); + IReleaseRenderTargetPools(); + + if( fSharedDepthSurface[0] ) + { + D3DSURF_MEMDEL(fSharedDepthSurface[0]); + ReleaseObject(fSharedDepthSurface[0]); + fSharedDepthFormat[0] = D3DFMT_UNKNOWN; + } + if( fSharedDepthSurface[1] ) + { + D3DSURF_MEMDEL(fSharedDepthSurface[1]); + ReleaseObject(fSharedDepthSurface[1]); + fSharedDepthFormat[1] = D3DFMT_UNKNOWN; + } + + D3DSURF_MEMDEL( fD3DMainSurface ); + D3DSURF_MEMDEL( fD3DDepthSurface ); + D3DSURF_MEMDEL( fD3DBackBuff ); + + ReleaseObject( fD3DBackBuff ); + ReleaseObject( fD3DDepthSurface ); + ReleaseObject( fD3DMainSurface ); + +} + +// IReleaseShaders /////////////////////////////////////////////////////////////// +// Delete our vertex and pixel shaders. Releasing the plasma ref will release the +// D3D handle. +void plDXPipeline::IReleaseShaders() +{ + while( fVShaderRefList ) + { + plDXVertexShader* ref = fVShaderRefList; + ref->Release(); + ref->Unlink(); + } + + while( fPShaderRefList ) + { + plDXPixelShader* ref = fPShaderRefList; + ref->Release(); + ref->Unlink(); + } +} + +//// IReleaseDeviceObjects /////////////////////////////////////////////////////// +// Release everything we've created. This is the main cleanup function. +void plDXPipeline::IReleaseDeviceObjects() +{ + plDXDeviceRef *ref; + + /// Delete d3d-dependent objects +#if MCN_BOUNDS_SPANS + if( fBoundsSpans ) + fBoundsSpans->GetKey()->UnRefObject(); + fBoundsSpans = nil; + if( fBoundsMat ) + fBoundsMat->GetKey()->UnRefObject(); + fBoundsMat = nil; +#endif + + plStatusLogMgr::GetInstance().SetDrawer( nil ); + delete fLogDrawer; + fLogDrawer = nil; + + IGetPixelScratch( 0 ); + + int i; + for( i = 0; i < 8; i++ ) + { + if( fLayerRef[i] ) + { + hsRefCnt_SafeUnRef(fLayerRef[i]); + fLayerRef[i] = nil; + } + } + +#ifdef MF_ENABLE_HACKOFF + //WHITE + hackOffscreens.SetCount(0); +#endif // MF_ENABLE_HACKOFF + + if( fULutTextureRef ) + delete [] fULutTextureRef->fData; + hsRefCnt_SafeUnRef(fULutTextureRef); + fULutTextureRef = nil; + + while( fVtxBuffRefList ) + { + ref = fVtxBuffRefList; + ref->Release(); + ref->Unlink(); + } + while( fIdxBuffRefList ) + { + ref = fIdxBuffRefList; + ref->Release(); + ref->Unlink(); + } + while( fTextureRefList ) + { + ref = fTextureRefList; + ref->Release(); + ref->Unlink(); + } + + IReleaseShaders(); + + fLights.Release(); + + IReleaseDynDeviceObjects(); + + delete fPlateMgr; + fPlateMgr = nil; + + if( fD3DDevice != nil ) + { + LONG ret; + while( ret = fD3DDevice->Release() ) + { + hsStatusMessageF("%d - Error releasing device", ret); + } + fD3DDevice = nil; + } + + if( fD3DObject != nil ) + { + LONG ret; + while( ret = fD3DObject->Release() ) + { + hsStatusMessageF("%d - Error releasing Direct3D Object", ret); + } + fD3DObject = nil; + } + + fManagedAlloced = false; + fAllocUnManaged = false; +} + +// IReleaseDynamicBuffers ///////////////////////////////////////////////// +// Release everything we've created in POOL_DEFAULT. +// This is called on shutdown or when we lose the device. Search for D3DERR_DEVICELOST. +void plDXPipeline::IReleaseDynamicBuffers() +{ + // Workaround for ATI driver bug. + if( fSettings.fBadManaged ) + { + plDXTextureRef* tRef = fTextureRefList; + while( tRef ) + { + tRef->Release(); + tRef = tRef->GetNext(); + } + } + plDXVertexBufferRef* vbRef = fVtxBuffRefList; + while( vbRef ) + { + if( vbRef->Volatile() && vbRef->fD3DBuffer ) + { + vbRef->fD3DBuffer->Release(); + vbRef->fD3DBuffer = nil; + + // Actually, if it's volatile, it's sharing the global dynamic vertex buff, so we're already + // accounting for the memory when we clear the global buffer. + //PROFILE_POOL_MEM(D3DPOOL_DEFAULT, vbRef->fCount * vbRef->fVertexSize, false, "VtxBuff"); + } + // 9600 THRASH + else if( fSettings.fBadManaged ) + { + vbRef->Release(); + } + vbRef = vbRef->GetNext(); + } + plDXIndexBufferRef* iRef = fIdxBuffRefList; + while( iRef ) + { + // If it's volatile, we have to release it. + // If it's not, we want to release it so + // we can make it volatile (D3DPOOL_DEFAULT) + if (iRef->fD3DBuffer) + { + iRef->fD3DBuffer->Release(); + iRef->fD3DBuffer = nil; + PROFILE_POOL_MEM(iRef->fPoolType, iRef->fCount * sizeof(UInt16), false, "IndexBuff"); + } + iRef = iRef->GetNext(); + } + if (fDynVtxBuff) + { + ReleaseObject(fDynVtxBuff); + PROFILE_POOL_MEM(D3DPOOL_DEFAULT, fDynVtxSize, false, "DynVtxBuff"); + fDynVtxBuff = nil; + } + + fNextDynVtx = 0; + + fVtxRefTime++; + + // PlateMgr has a POOL_DEFAULT vertex buffer for drawing quads. + if( fPlateMgr ) + fPlateMgr->IReleaseGeometry(); + + // Also has POOL_DEFAULT vertex buffer. + plDXTextFont::ReleaseShared(fD3DDevice); + + IReleaseBlurVBuffers(); +} + +// ICreateDynamicBuffers ///////////////////////////////////////////////////// +// Create the things we need in POOL_DEFAULT. We clump them into this function, +// because they must be created before anything in POOL_MANAGED. +// So we create these global POOL_DEFAULT objects here, then send out a message +// to the objects in the scene to create anything they need in POOL_DEFAULT, +// then go on to create things on POOL_MANAGED. +// Set LoadResources(). +void plDXPipeline::ICreateDynamicBuffers() +{ + ICreateBlurVBuffers(); + + plDXTextFont::CreateShared(fD3DDevice); + + if( fPlateMgr ) + fPlateMgr->ICreateGeometry(this); + + fNextDynVtx = 0; + + fVtxRefTime++; + + DWORD usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC; + hsAssert(!fManagedAlloced, "Alloc default with managed alloc'd"); + D3DPOOL poolType = D3DPOOL_DEFAULT; + if( fDynVtxSize ) + { + PROFILE_POOL_MEM(poolType, fDynVtxSize, true, "DynVtxBuff"); + if( FAILED( fD3DDevice->CreateVertexBuffer( fDynVtxSize, + usage, + 0, + poolType, + &fDynVtxBuff, NULL) ) ) + { + hsAssert(false, "Don't know what to do here."); + } + } +} + + +void plDXPipeline::IPrintDeviceInitError() +{ + char str[256]; + char err[16]; + switch(plLocalization::GetLanguage()) + { + case plLocalization::kFrench: strcpy(err, "Erreur"); strcpy(str, "Erreur d'initialisation de votre carte graphique. Les valeurs par défaut de ses paramètres ont été rétablis. "); break; + case plLocalization::kGerman: strcpy(err, "Fehler"); strcpy(str, "Bei der Initialisierung Ihrer Grafikkarte ist ein Fehler aufgetreten. Standardeinstellungen werden wiederhergestellt."); break; + case plLocalization::kSpanish: strcpy(err, "Error"); strcpy(str, "Ocurrió un error al inicializar tu tarjeta de vídeo. Hemos restaurado los ajustes por defecto. "); break; + case plLocalization::kItalian: strcpy(err, "Errore"); strcpy(str, "Errore di inizializzazione della scheda video. Sono state ripristinate le impostazioni predefinite."); break; + default: strcpy(err, "Error"); strcpy(str, "There was an error initializing your video card. We have reset it to its Default settings."); break; + } + hsMessageBox(str, err, hsMessageBoxNormal, hsMessageBoxIconError); +} + +// Reset device creation parameters to default and write to ini file +void plDXPipeline::IResetToDefaults(D3DPRESENT_PARAMETERS *params) +{ + // this will reset device parameters to default and make sure all other necessary parameters are updated + params->BackBufferWidth = fDefaultPipeParams.Width; + params->BackBufferHeight = fDefaultPipeParams.Height; + fSettings.fOrigWidth = fDefaultPipeParams.Width; + fSettings.fOrigHeight = fDefaultPipeParams.Height; + IGetViewTransform().SetScreenSize(fDefaultPipeParams.Width, fDefaultPipeParams.Height); + params->BackBufferFormat = D3DFMT_X8R8G8B8; + fSettings.fColorDepth = fDefaultPipeParams.ColorDepth; + + int i; + hsTArray *modes = &fCurrentDevice->fModes; + for( i = 0; i < modes->Count(); i++ ) + { + D3DEnum_ModeInfo *mode = &(*modes)[i]; + if(mode->fDDmode.Width == params->BackBufferWidth && + mode->fDDmode.Height == params->BackBufferHeight && + mode->fBitDepth == 32 ) + { + ISetCurrentMode(&(*modes)[i]); + break; + } + } + params->Windowed = fDefaultPipeParams.Windowed; + fSettings.fFullscreen = !fDefaultPipeParams.Windowed; + fCurrentMode->fWindowed = fDefaultPipeParams.Windowed; + + // Attempt to find the closest AA setting we can + params->MultiSampleType = D3DMULTISAMPLE_NONE; + fSettings.fNumAASamples = 0; + for( int i = fDefaultPipeParams.AntiAliasingAmount; i >= 2; i-- ) + { + if( fCurrentMode->fFSAATypes.Find( (D3DMULTISAMPLE_TYPE)i ) != fCurrentMode->fFSAATypes.kMissingIndex ) + { + fSettings.fNumAASamples = i; + params->MultiSampleType = (D3DMULTISAMPLE_TYPE)i; + break; + } + } + fSettings.fMaxAnisotropicSamples = fDefaultPipeParams.AnisotropicLevel; + + fVSync = false; + + params->PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + + plShadowCaster::EnableShadowCast(fDefaultPipeParams.Shadows ? true : false); + plQuality::SetQuality(fDefaultPipeParams.VideoQuality); + plQuality::SetCapability(fDefaultPipeParams.VideoQuality); + plDynamicCamMap::SetEnabled(fDefaultPipeParams.PlanarReflections ? true : false); + plBitmap::SetGlobalLevelChopCount(2 - fDefaultPipeParams.TextureQuality); + + // adjust camera properties + plVirtualCam1::SetAspectRatio((float)fSettings.fOrigWidth / (float)fSettings.fOrigHeight); + plVirtualCam1::SetFOV(plVirtualCam1::GetFOVw(), plVirtualCam1::GetFOVh()); + + // fire off a message to the client so we can write defaults to the ini file, and adjust the window size + plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY ); + plClientMsg* clientMsg = TRACKED_NEW plClientMsg(plClientMsg::kSetGraphicsDefaults); + clientMsg->Send(clientKey); + +} + +// IResetDevice +// reset the device to its operational state. +// returns true if not ready yet, false if the reset was successful. +// All this is generally in response to a fullscreen alt-tab. +hsBool plDXPipeline::IResetDevice() +{ + hsBool fakeDevLost(false); + if( fakeDevLost ) + fDeviceLost = true; + + if( fDeviceLost ) + { + IClearShadowSlaves(); + + ReleaseCapture(); + Sleep(100); + HRESULT coopLev = fD3DDevice->TestCooperativeLevel(); + if( coopLev == D3DERR_DEVICELOST ) + { + // Nothing to do yet. + return true; + } + if( fakeDevLost ) + coopLev = D3DERR_DEVICENOTRESET; + if( coopLev == D3DERR_DEVICENOTRESET || fForceDeviceReset) + { + plStatusLog::AddLineS("pipeline.log", 0xffff0000, "Resetting Device"); + IReleaseDynDeviceObjects(); + if( !IFindDepthFormat(fSettings.fPresentParams) ) + { + // If we haven't found a depth format, turn off multisampling and try it again. + fSettings.fPresentParams.MultiSampleType = D3DMULTISAMPLE_NONE; + IFindDepthFormat(fSettings.fPresentParams); + } + HRESULT hr = fD3DDevice->Reset(&fSettings.fPresentParams); + int count = 0; + while( FAILED(hr) ) + { + if(count++ == 25) + { + IPrintDeviceInitError(); + IResetToDefaults(&fSettings.fPresentParams); + } + // Still not ready? This is bad. + // Until we called Reset(), we could make any D3D call we wanted, + // and it would turn into a no-op. But once we call Reset(), until + // the device really is reset, anything but TestCoop/Reset/Release + // has just become illegal. We've already released everything, Reset + // just failed, not much to do but wait and try again. + ::Sleep(250); + hr = fD3DDevice->Reset(&fSettings.fPresentParams); + } + fSettings.fCurrFVFFormat = 0; + fSettings.fCurrVertexShader = NULL; + fManagedAlloced = false; + ICreateDynDeviceObjects(); + IInitDeviceState(); + + /// Broadcast a message letting everyone know that we were recreated and that + /// all device-specific stuff needs to be recreated + plDeviceRecreateMsg* clean = TRACKED_NEW plDeviceRecreateMsg(); + plgDispatch::MsgSend(clean); + + SetCapture(fSettings.fHWnd); + } + fDevWasLost = true; + fDeviceLost = false; + + // We return true here, even though we've successfully recreated, to take + // another spin around the update loop and give everyone a chance to + // get back in sync. + return true; + } + return false; +} + +void plDXPipeline::ResetDisplayDevice(int Width, int Height, int ColorDepth, hsBool Windowed, int NumAASamples, int MaxAnisotropicSamples, hsBool VSync /* = false */) +{ + if( fSettings.fPresentParams.BackBufferWidth == Width && + fSettings.fPresentParams.BackBufferHeight == Height && + (fSettings.fPresentParams.Windowed ? 1 : fSettings.fColorDepth == ColorDepth) && // if we're windowed dont check color depth we just use the desktop colordepth + ((fSettings.fPresentParams.Windowed && Windowed) || (!fSettings.fPresentParams.Windowed && !Windowed)) && + fSettings.fNumAASamples == NumAASamples && + fSettings.fMaxAnisotropicSamples == MaxAnisotropicSamples && + fVSync == VSync + ) + { + return; // nothing has changed + } + + fVSync = VSync; + int i = 0; + hsTArray *modes = &fCurrentDevice->fModes; + // check for supported resolution if we're not going to windowed mode + if(!Windowed) + { + for( i = 0; i < modes->Count(); i++ ) + { + D3DEnum_ModeInfo *mode = &(*modes)[i]; + if(mode->fDDmode.Width == Width && + mode->fDDmode.Height == Height && + mode->fBitDepth == ColorDepth ) + { + ISetCurrentMode(&(*modes)[i]); + break; + } + } + } + if(i != modes->Count()) + { + // Set Resolution + fSettings.fOrigWidth = Width; + fSettings.fOrigHeight = Height; + IGetViewTransform().SetScreenSize(Width, Height); + fSettings.fPresentParams.BackBufferWidth = Width; + fSettings.fPresentParams.BackBufferHeight = Height; + fSettings.fColorDepth = ColorDepth; + fSettings.fPresentParams.BackBufferFormat = D3DFMT_X8R8G8B8; + } + + // set windowed/fullscreen mode + fCurrentMode->fWindowed = Windowed; + fSettings.fPresentParams.Windowed = Windowed; + fSettings.fFullscreen = !Windowed; + + // set Antialiasing + fSettings.fNumAASamples = 0; + // Attempt to find the closest AA setting we can + fSettings.fPresentParams.MultiSampleType = D3DMULTISAMPLE_NONE; + for( i = NumAASamples; i >= 2; i-- ) + { + if( fCurrentMode->fFSAATypes.Find( (D3DMULTISAMPLE_TYPE)i ) != fCurrentMode->fFSAATypes.kMissingIndex ) + { + fSettings.fNumAASamples = i; + fSettings.fPresentParams.MultiSampleType = (D3DMULTISAMPLE_TYPE)i; + break; + } + } + if( fSettings.fNumAASamples > 0 ) + fSettings.fD3DCaps |= kCapsFSAntiAlias; + else + fSettings.fD3DCaps &= ~kCapsFSAntiAlias; + + // Set Anisotropic filtering + fSettings.fMaxAnisotropicSamples = MaxAnisotropicSamples; + ISetAnisotropy(MaxAnisotropicSamples > 0); + if(Windowed) + { + fSettings.fPresentParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + } + else + { + fSettings.fPresentParams.PresentationInterval = ( fVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE ); + } + + // Force a device reset + fDeviceLost = true; + fForceDeviceReset = true; + + plVirtualCam1::SetAspectRatio((float)Width / (float)Height); + plVirtualCam1::SetFOV(plVirtualCam1::GetFOVw(), plVirtualCam1::GetFOVh()); + + IResetDevice(); + + + return; +} + +void plDXPipeline::GetSupportedColorDepths(hsTArray &ColorDepths) +{ + int i, j; + // iterate through display modes + for( i = 0; i < fCurrentDevice->fModes.Count(); i++ ) + { + // Check to see if color depth has been added already + for( j = 0; j < ColorDepths.Count(); j++ ) + { + if( fCurrentDevice->fModes[i].fBitDepth == ColorDepths[i] ) + break; + } + if(j == ColorDepths.Count()) + { + //add it + ColorDepths.Push( fCurrentDevice->fModes[i].fBitDepth ); + } + } +} + +void plDXPipeline::GetSupportedDisplayModes(std::vector *res, int ColorDepth ) +{ + int i, j; + std::vector supported; + // loop through display modes + for( i = 0; i < fCurrentDevice->fModes.Count(); i++ ) + { + if( fCurrentDevice->fModes[i].fBitDepth == ColorDepth ) + { + // check for duplicate mode + for( j = 0; j < supported.size(); j++ ) + { + if(supported[j].Width == fCurrentDevice->fModes[i].fDDmode.Width && supported[j].Height == fCurrentDevice->fModes[i].fDDmode.Height) + break; + } + if(j == supported.size()) + { + // new mode, add it + plDisplayMode mode; + mode.Width = fCurrentDevice->fModes[i].fDDmode.Width; + mode.Height = fCurrentDevice->fModes[i].fDDmode.Height; + mode.ColorDepth = ColorDepth; + supported.push_back(mode); + } + } + } + + *res = supported; +} + +// Get max anitialias for the specified displaymode +int plDXPipeline::GetMaxAntiAlias(int Width, int Height, int ColorDepth) +{ + int max = 0; + D3DEnum_ModeInfo *pCurrMode = nil; + hsTArray *modes = &fCurrentDevice->fModes; + for(int i = 0; i < modes->Count(); i++ ) + { + D3DEnum_ModeInfo *mode = &(*modes)[i]; + if( mode->fDDmode.Width == Width && + mode->fDDmode.Height == Height && + mode->fBitDepth == ColorDepth ) + { + pCurrMode = mode; + } + } + if(pCurrMode) + { + for(int i = 0; i < pCurrMode->fFSAATypes.Count(); i++) + { + if(pCurrMode->fFSAATypes[i] > max) + max = pCurrMode->fFSAATypes[i]; + } + } + return max; +} + +int plDXPipeline::GetMaxAnisotropicSamples() +{ + return fCurrentDevice ? fCurrentDevice->fDDCaps.MaxAnisotropy : 0; +} + +//// Resize /////////////////////////////////////////////////////////////////// +// Resize is fairly obsolete, having been replaced by IResetDevice, which is +// automatically called if needed on BeginRender. +// This Resize function used to serve as both to Resize the primary buffers and +// to restore after losing the device (alt-tab). It didn't actually do either +// very well, so I'm not sure why I haven't deleted it. +void plDXPipeline::Resize( UInt32 width, UInt32 height ) +{ + hsMatrix44 w2c, c2w, proj; + + + HRESULT coopLev = fD3DDevice->TestCooperativeLevel(); + if( coopLev == D3DERR_DEVICELOST ) + { + /// Direct3D is reporting that we lost the device but are unable to reset + /// it yet, so ignore. + hsStatusMessage( "Received Resize() request at an invalid time. Ignoring...\n" ); + return; + } + if( !width && !height ) + { + if( D3D_OK == coopLev ) + return; + + IReleaseDynDeviceObjects(); + HRESULT hr = fD3DDevice->Reset(&fSettings.fPresentParams); + fManagedAlloced = false; + if( !FAILED(hr) ) + { + ICreateDynDeviceObjects(); + IInitDeviceState(); + return; + } + } + + // Store some states that we *want* to restore back... + plViewTransform resetTransform = GetViewTransform(); + + /// HACK: Don't recreate if we're windowed, bad things happen + /// Comment out this if if you want to test the crashing thing in windowed alt-tabbing +#if 0 + if( ( width == 0 || height == 0 ) && !fSettings.fFullscreen ) + return; +#endif + + // Destroy old + IReleaseDeviceObjects(); + + // Reset width and height + if( width != 0 && height != 0 ) + { + // Width and height of zero mean just recreate + fSettings.fOrigWidth = width; + fSettings.fOrigHeight = height; + IGetViewTransform().SetScreenSize((UInt16)(fSettings.fOrigWidth), (UInt16)(fSettings.fOrigHeight)); + resetTransform.SetScreenSize((UInt16)(fSettings.fOrigWidth), (UInt16)(fSettings.fOrigHeight)); + } + else + { + // Just for debug + hsStatusMessage( "Recreating the pipeline...\n" ); + } + + // Recreate + if( !fD3DObject ) + { + if( ICreateMaster() ) + { + IShowErrorMessage( "Cannot create D3D master object" ); + return; + } + } + + // Go recreate surfaces and DX-dependent objects + if( ICreateDeviceObjects() ) + { + IShowErrorMessage( "Cannot create Direct3D device" ); + return; + } + + // Restore states + SetViewTransform(resetTransform); + IProjectionMatrixToD3D(); + + /// Broadcast a message letting everyone know that we were recreated and that + /// all device-specific stuff needs to be recreated + plDeviceRecreateMsg* clean = TRACKED_NEW plDeviceRecreateMsg(); + plgDispatch::MsgSend(clean); +} + + +/////////////////////////////////////////////////////////////////////////////// +//// Debug Text /////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// MakeTextFont ///////////////////////////////////////////////////////////// + +plTextFont *plDXPipeline::MakeTextFont( char *face, UInt16 size ) +{ + plTextFont *font; + + + font = TRACKED_NEW plDXTextFont( this, fD3DDevice ); + if( font == nil ) + return nil; + font->Create( face, size ); + font->Link( &fTextFontRefList ); + + return font; +} + + +/////////////////////////////////////////////////////////////////////////////// +//// Drawable Stuff /////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// Draw ///////////////////////////////////////////////////////////////////// + +// PreRender ////////////////////////////////////////////////////////////////// +// Most of this is debugging stuff, drawing the bounds, drawing the normals, etc. +// The functional part is in IGetVisibleSpans, which creates a list of the visible (non-culled) +// span indices within this drawable. +// This is called once per render, and generally well before rendering begins (as part of the +// cull phase). +hsBool plDXPipeline::PreRender( plDrawable* drawable, hsTArray& visList, plVisMgr* visMgr ) +{ + plDrawableSpans *ds = plDrawableSpans::ConvertNoRef(drawable); + if( !ds ) + return false; + if( ( ds->GetType() & fView.fDrawableTypeMask ) == 0 ) + return false; + + IGetVisibleSpans( ds, visList, visMgr ); + +#if MCN_BOUNDS_SPANS + if( ( drawable != fBoundsSpans ) && IsDebugFlagSet(plPipeDbg::kFlagShowAllBounds) ) + { + const hsTArray &spans = ds->GetSpanArray(); + int i; + for( i = 0; i < visList.GetCount(); i++ ) + { + /// Add a span to our boundsIce to show this + IAddBoundsSpan( fBoundsSpans, &spans[ visList[i] ]->fWorldBounds ); + } + } + else if( ( drawable != fBoundsSpans ) && IsDebugFlagSet(plPipeDbg::kFlagShowNormals) ) + { + const hsTArray &spans = ds->GetSpanArray(); + int i; + for( i = 0; i < visList.GetCount(); i++ ) + { + /// Add a span to our boundsIce to show this + plIcicle *span = (plIcicle *)spans[ visList[ i ] ]; + if( span->fTypeMask & plSpan::kIcicleSpan ) + { + IAddNormalsSpan( fBoundsSpans, span, (plDXVertexBufferRef *)ds->GetVertexRef( span->fGroupIdx, span->fVBufferIdx ), 0xff0000ff ); + } + } + } +#endif +#if MF_BOUNDS_LEVEL_ICE + if( (fSettings.fBoundsDrawLevel >= 0) && ( drawable != fBoundsSpans ) ) + { + hsTArray bndList; + drawable->GetSpaceTree()->HarvestLevel(fSettings.fBoundsDrawLevel, bndList); + int i; + for( i = 0; i < bndList.GetCount(); i++ ) + { + IAddBoundsSpan( fBoundsSpans, &hsBounds3Ext(drawable->GetSpaceTree()->GetNode(bndList[i]).GetWorldBounds()), 0xff000000 | (0xf << ((fSettings.fBoundsDrawLevel % 6) << 2)) ); + } + } +#endif // MF_BOUNDS_LEVEL_ICE + + + return visList.GetCount() > 0; +} + +struct plSortFace +{ + UInt16 fIdx[3]; + hsScalar fDist; +}; + +struct plCompSortFace : public std::binary_function +{ + bool operator()( const plSortFace& lhs, const plSortFace& rhs) const + { + return lhs.fDist > rhs.fDist; + } +}; + +// IAvatarSort ///////////////////////////////////////////////////////////////////////// +// We handle avatar sort differently from the rest of the face sort. The reason is that +// within the single avatar index buffer, we want to only sort the faces of spans requesting +// a sort, and sort them in place. +// Contrast that with the normal scene translucency sort. There, we sort all the spans in a drawble, +// then we sort all the faces in that drawable, then for each span in the sorted span list, we extract +// the faces for that span appending onto the index buffer. This gives great efficiency because +// only the visible faces are sorted and they wind up packed into the front of the index buffer, which +// permits more batching. See plDrawableSpans::SortVisibleSpans. +// For the avatar, it's generally the case that all the avatar is visible or not, and there is only +// one material, so neither of those efficiencies is helpful. Moreover, for the avatar the faces we +// want sorted are a tiny subset of the avatar's faces. Moreover, and most importantly, for the avatar, we +// want to preserve the order that spans are drawn, so, for example, the opaque base head will always be +// drawn before the translucent hair fringe, which will always be drawn before the pink clear plastic baseball cap. +hsBool plDXPipeline::IAvatarSort(plDrawableSpans* d, const hsTArray& visList) +{ + plProfile_BeginTiming(AvatarSort); + int i; + for( i = 0; i < visList.GetCount(); i++ ) + { + hsAssert(d->GetSpan(visList[i])->fTypeMask & plSpan::kIcicleSpan, "Unknown type for sorting faces"); + + plIcicle* span = (plIcicle*)d->GetSpan(visList[i]); + + if( span->fProps & plSpan::kPartialSort ) + { + hsAssert(d->GetBufferGroup(span->fGroupIdx)->AreIdxVolatile(), "Badly setup buffer group - set PartialSort too late?"); + + const hsPoint3 viewPos = GetViewPositionWorld(); + + plGBufferGroup* group = d->GetBufferGroup(span->fGroupIdx); + + plDXVertexBufferRef* vRef = (plDXVertexBufferRef*)group->GetVertexBufferRef(span->fVBufferIdx); + + const UInt8* vdata = vRef->fData; + const UInt32 stride = vRef->fVertexSize; + + const int numTris = span->fILength/3; + + static hsTArray sortScratch; + sortScratch.SetCount(numTris); + + plProfile_IncCount(AvatarFaces, numTris); + + plSortFace* begin = sortScratch.AcquireArray(); + plSortFace* end = begin + numTris; + + // + // Have three very similar sorts here, differing only on where the "position" of + // each triangle is defined, either as the center of the triangle, the nearest + // point on the triangle, or the farthest point on the triangle. + // Having tried all three on the avatar (the only thing this sort is used on), + // the best results surprisingly came from using the center of the triangle. + UInt16* indices = group->GetIndexBufferData(span->fIBufferIdx) + span->fIStartIdx; + int j; + for( j = 0; j < numTris; j++ ) + { +#if 1 // TRICENTER + UInt16 idx = *indices++; + sortScratch[j].fIdx[0] = idx; + hsPoint3 pos = *(hsPoint3*)(vdata + idx * stride); + + idx = *indices++; + sortScratch[j].fIdx[1] = idx; + pos += *(hsPoint3*)(vdata + idx * stride); + + idx = *indices++; + sortScratch[j].fIdx[2] = idx; + pos += *(hsPoint3*)(vdata + idx * stride); + + pos *= 0.3333f; + + sortScratch[j].fDist = hsVector3(&pos, &viewPos).MagnitudeSquared(); +#elif 0 // NEAREST + UInt16 idx = *indices++; + sortScratch[j].fIdx[0] = idx; + hsPoint3 pos = *(hsPoint3*)(vdata + idx * stride); + hsScalar dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + hsScalar minDist = dist; + + idx = *indices++; + sortScratch[j].fIdx[1] = idx; + pos = *(hsPoint3*)(vdata + idx * stride); + dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + if( dist < minDist ) + minDist = dist; + + idx = *indices++; + sortScratch[j].fIdx[2] = idx; + pos = *(hsPoint3*)(vdata + idx * stride); + dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + if( dist < minDist ) + minDist = dist; + + sortScratch[j].fDist = minDist; +#elif 1 // FURTHEST + UInt16 idx = *indices++; + sortScratch[j].fIdx[0] = idx; + hsPoint3 pos = *(hsPoint3*)(vdata + idx * stride); + hsScalar dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + hsScalar maxDist = dist; + + idx = *indices++; + sortScratch[j].fIdx[1] = idx; + pos = *(hsPoint3*)(vdata + idx * stride); + dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + if( dist > maxDist ) + maxDist = dist; + + idx = *indices++; + sortScratch[j].fIdx[2] = idx; + pos = *(hsPoint3*)(vdata + idx * stride); + dist = hsVector3(&pos, &viewPos).MagnitudeSquared(); + if( dist > maxDist ) + maxDist = dist; + + sortScratch[j].fDist = maxDist; +#endif // SORTTYPES + } + + std::sort(begin, end, plCompSortFace()); + + indices = group->GetIndexBufferData(span->fIBufferIdx) + span->fIStartIdx; + plSortFace* iter = sortScratch.AcquireArray();; + for( j = 0; j < numTris; j++ ) + { + *indices++ = iter->fIdx[0]; + *indices++ = iter->fIdx[1]; + *indices++ = iter->fIdx[2]; + iter++; + } + + group->DirtyIndexBuffer(span->fIBufferIdx); + } + } + plProfile_EndTiming(AvatarSort); + return true; +} + +// PrepForRender ////////////////////////////////////////////////////////////////// +// Make sure the given drawable and each of the spans to be drawn (as noted in the +// indices in visList) is ready to be rendered. +// This means: +// a) select which lights will be used for each span +// b) do any necessary sorting (if required, spans are already in sorted order in visList, +// so this only means face sorting). +// c) do any necessary software skinning. +// This is called once per render, and before any rendering actually starts. See plPageTreeMgr.cpp. +// So any preperation needs to last until rendering actually begins. So cached information, like +// which lights a span will use, needs to be stored on the span. +hsBool plDXPipeline::PrepForRender(plDrawable* d, hsTArray& visList, plVisMgr* visMgr) +{ + plProfile_BeginTiming(PrepDrawable); + + plDrawableSpans *drawable = plDrawableSpans::ConvertNoRef(d); + if( !drawable ) + { + plProfile_EndTiming(PrepDrawable); + return false; + } + + // Find our lights + ICheckLighting(drawable, visList, visMgr); + + // Sort our faces + if( drawable->GetNativeProperty(plDrawable::kPropSortFaces) ) + { + drawable->SortVisibleSpans(visList, this); + } + + // Prep for render. This is gives the drawable a chance to + // do any last minute updates for its buffers, including + // generating particle tri lists. + drawable->PrepForRender( this ); + + // Any skinning necessary + if( !ISoftwareVertexBlend(drawable, visList) ) + { + plProfile_EndTiming(PrepDrawable); + return false; + } + // Avatar face sorting happens after the software skin. + if( drawable->GetNativeProperty(plDrawable::kPropPartialSort) ) + { + IAvatarSort(drawable, visList); + } + + plProfile_EndTiming(PrepDrawable); + + return true; +} + +// Draw /////////////////////////////////////////////////////////// +// Convenience function for a drawable that needs to get drawn outside of +// the normal scene graph render (i.e. something not managed by the plPageTreeMgr). +// Not nearly as efficient, so only useful as a special case. +void plDXPipeline::Draw( plDrawable *d ) +{ + plDrawableSpans *ds = plDrawableSpans::ConvertNoRef( d ); + + if( ds ) + { + if( ( ds->GetType() & fView.fDrawableTypeMask ) == 0 ) + return; + + static hsTArrayvisList; + + PreRender( ds, visList ); + PrepForRender(ds, visList); + Render( ds, visList ); + } +} + +// Render //////////////////////////////////////////////////////////////////////////////// +// The normal way to render a subset of a drawable. +// This assumes that PreRender and PrepForRender have already been called. +// Note that PreRender and PrepForRender are called once per drawable per render +// with a visList containing all of the spans which will be rendered, but +// Render itself may be called with multiple visList subsets which union to +// the visList passed into PreRender/PrepForRender. This happens when drawing +// sorted spans, because some spans from drawable B may be in the middle of +// the spans of drawable A, so the sequence would be: +// +// PreRender(A, ATotalVisList); +// PreRender(B, BTotalVisList); +// PrepForRender(A, ATotalVisList); +// PrepForRender(B, BTotalVisList); +// Render(A, AFarHalfVisList); +// Render(B, BTotalVisList); +// Render(A, ANearHalfVisList); +// See plPageTreeMgr, which handles all this. +void plDXPipeline::Render( plDrawable *d, const hsTArray& visList ) +{ + // Reset here, since we can push/pop renderTargets after BeginRender() but before + // this function, which necessitates this being called + if( fView.fXformResetFlags != 0 ) + ITransformsToD3D(); + + plDrawableSpans *ds = plDrawableSpans::ConvertNoRef( d ); + + if( ds ) + { + IRenderSpans( ds, visList ); + } +} + +//// BeginDrawable //////////////////////////////////////////////////////////// +// Obsolete, should be removed +hsBool plDXPipeline::BeginDrawable( plDrawable *d ) +{ + return true; +} + +//// EndDrawable ////////////////////////////////////////////////////////////// +// Obsolete, should be removed + +hsBool plDXPipeline::EndDrawable( plDrawable *d ) +{ + return true; +} + +// IMakeLightLists /////////////////////////////////////////////////////////// +// Look through all the current lights, and fill out two lists. +// Only active lights (not disabled, not exactly black, and not +// ignored because of visibility regions by plVisMgr) will +// be considered. +// The first list is lights that will affect the avatar and similar +// indeterminately mobile (physical) objects - fLights.fCharLights. +// The second list is lights that aren't restricted by light include +// lists. +// These two abbreviated lists will be further refined for each object +// and avatar to find the strongest 8 lights which affect that object. +// A light with an include list, or LightGroup Component) has +// been explicitly told which objects it affects, so they don't +// need to be in the search lists. +// These lists are only constructed once per render, but searched +// multiple times +void plDXPipeline::IMakeLightLists(plVisMgr* visMgr) +{ + plProfile_BeginTiming(FindSceneLights); + fLights.fCharLights.SetCount(0); + fLights.fVisLights.SetCount(0); + if( visMgr ) + { + const hsBitVector& visSet = visMgr->GetVisSet(); + const hsBitVector& visNot = visMgr->GetVisNot(); + plLightInfo* light; + for( light = fLights.fActiveList; light != nil; light = light->GetNext() ) + { + plProfile_IncCount(LightActive, 1); + if( !light->IsIdle() && !light->InVisNot(visNot) && light->InVisSet(visSet) ) + { + plProfile_IncCount(LightOn, 1); + if( light->GetProperty(plLightInfo::kLPHasIncludes) ) + { + if( light->GetProperty(plLightInfo::kLPIncludesChars) ) + fLights.fCharLights.Append(light); + } + else + { + fLights.fVisLights.Append(light); + fLights.fCharLights.Append(light); + } + } + } + } + else + { + plLightInfo* light; + for( light = fLights.fActiveList; light != nil; light = light->GetNext() ) + { + plProfile_IncCount(LightActive, 1); + if( !light->IsIdle() ) + { + plProfile_IncCount(LightOn, 1); + if( light->GetProperty(plLightInfo::kLPHasIncludes) ) + { + if( light->GetProperty(plLightInfo::kLPIncludesChars) ) + fLights.fCharLights.Append(light); + } + else + { + fLights.fVisLights.Append(light); + fLights.fCharLights.Append(light); + } + } + } + } + plProfile_IncCount(LightVis, fLights.fVisLights.GetCount()); + plProfile_IncCount(LightChar, fLights.fCharLights.GetCount()); + + plProfile_EndTiming(FindSceneLights); +} + +// BeginVisMgr ///////////////////////////////////////////////////////// +// Marks the beginning of a render with the given visibility manager. +// In particular, we cache which lights the visMgr believes to be +// currently active +void plDXPipeline::BeginVisMgr(plVisMgr* visMgr) +{ + IMakeLightLists(visMgr); +} + +// EndVisMgr /////////////////////////////////////////////////////////// +// Marks the end of a render with the given visibility manager. +void plDXPipeline::EndVisMgr(plVisMgr* visMgr) +{ + fLights.fCharLights.SetCount(0); + fLights.fVisLights.SetCount(0); +} + +// ICheckLighting /////////////////////////////////////////////////////// +// For every span in the list of visible span indices, find the list of +// lights that currently affect the span with an estimate of the strength +// of how much the light affects it. The strongest 8 lights will be used +// to illuminate that span. +// For projective lights, there is no limit on how many are supported, other +// than performance (usually fill rate limited). +// The permaLights and permaProjs are lights explicitly selected for a span +// via the LightGroup component. +// For static objects and static lights, the lighting was done offline and stored +// in the vertex diffuse color. +// So here we're only looking for: +// A) moving objects, which can't be staticly lit, so are affected by all runtime lights. +// B) moving lights, which can't staticly light, so affect all objects +// C) specular objects + specular lights, since specular can't be precomputed. +void plDXPipeline::ICheckLighting(plDrawableSpans* drawable, hsTArray& visList, plVisMgr* visMgr) +{ + if( fView.fRenderState & kRenderNoLights ) + return; + + if( !visList.GetCount() ) + return; + + plLightInfo *light; + int j; + + // First add in the explicit lights (from LightGroups). + // Refresh the lights as they are added (actually a lazy eval). + plProfile_BeginTiming(FindLights); + plProfile_BeginTiming(FindPerm); + for( j = 0; j < visList.GetCount(); j++ ) + { + drawable->GetSpan( visList[ j ] )->ClearLights(); + + if (IsDebugFlagSet(plPipeDbg::kFlagNoRuntimeLights)) + continue; + + // Set the bits for the lights added from the permanent lists (during ClearLights()). + int k; + const hsTArray& permaLights = drawable->GetSpan(visList[j])->fPermaLights; + for( k = 0; k < permaLights.GetCount(); k++ ) + { + permaLights[k]->Refresh(); + if( permaLights[k]->GetProperty(plLightInfo::kLPShadowLightGroup) && !permaLights[k]->IsIdle() ) + { + // If it casts a shadow, attach the shadow now. + ISetShadowFromGroup(drawable, drawable->GetSpan(visList[j]), permaLights[k]); + } + } + const hsTArray& permaProjs = drawable->GetSpan(visList[j])->fPermaProjs; + for( k = 0; k < permaProjs.GetCount(); k++ ) + { + permaProjs[k]->Refresh(); + if( permaProjs[k]->GetProperty(plLightInfo::kLPShadowLightGroup) && !permaProjs[k]->IsIdle() ) + { + // If it casts a shadow, attach the shadow now. + ISetShadowFromGroup(drawable, drawable->GetSpan(visList[j]), permaProjs[k]); + } + } + } + plProfile_EndTiming(FindPerm); + + if (IsDebugFlagSet(plPipeDbg::kFlagNoRuntimeLights)) + { + plProfile_EndTiming( FindLights ); + return; + } + + // Sort the incoming spans as either + // A) moving - affected by all lights - moveList + // B) specular - affected by specular lights - specList + // C) visible - affected by moving lights - visList + static hsTArray tmpList; + static hsTArray moveList; + static hsTArray specList; + + moveList.SetCount(0); + specList.SetCount(0); + + plProfile_BeginTiming(FindSpan); + int k; + for( k = 0; k < visList.GetCount(); k++ ) + { + const plSpan* span = drawable->GetSpan(visList[k]); + + if( span->fProps & plSpan::kPropRunTimeLight ) + { + moveList.Append(visList[k]); + specList.Append(visList[k]); + } + else if( span->fProps & plSpan::kPropMatHasSpecular ) + specList.Append(visList[k]); + } + plProfile_EndTiming(FindSpan); + + // Make a list of lights that can potentially affect spans in this drawable + // based on the drawables bounds and properties. + // If the drawable has the PropCharacter property, it is affected by lights + // in fLights.fCharLights, else only by the smaller list of fLights.fVisLights. + plProfile_BeginTiming(FindActiveLights); + static hsTArray lightList; + lightList.SetCount(0); + const hsBool isChar = 0 != drawable->GetNativeProperty(plDrawable::kPropCharacter); + if( isChar ) + { + int i; + for( i = 0; i < fLights.fCharLights.GetCount(); i++ ) + { + if( fLights.fCharLights[i]->AffectsBound(drawable->GetSpaceTree()->GetWorldBounds()) ) + lightList.Append(fLights.fCharLights[i]); + } + } + else + { + int i; + for( i = 0; i < fLights.fVisLights.GetCount(); i++ ) + { + if( fLights.fVisLights[i]->AffectsBound(drawable->GetSpaceTree()->GetWorldBounds()) ) + lightList.Append(fLights.fVisLights[i]); + } + } + plProfile_EndTiming(FindActiveLights); + + // Loop over the lights and for each light, extract a list of the spans that light + // affects. Append the light to each spans list with a scalar strength of how strongly + // the light affects it. Since the strength is based on the object's center position, + // it's not very accurate, but good enough for selecting which lights to use. + plProfile_BeginTiming(ApplyActiveLights); + for( k = 0; k < lightList.GetCount(); k++ ) + { + light = lightList[k]; + + tmpList.SetCount(0); + if( light->GetProperty(plLightInfo::kLPMovable) ) + { + plProfile_BeginTiming(ApplyMoving); + + const hsTArray& litList = light->GetAffected(drawable->GetSpaceTree(), + visList, + tmpList, + drawable->GetNativeProperty(plDrawable::kPropCharacter) ); + + // PUT OVERRIDE FOR KILLING PROJECTORS HERE!!!! + hsBool proj = nil != light->GetProjection(); + if( fView.fRenderState & kRenderNoProjection ) + proj = false; + + for( j = 0; j < litList.GetCount(); j++ ) + { + // Use the light IF light is enabled and + // 1) light is movable + // 2) span is movable, or + // 3) Both the light and the span have specular + const plSpan* span = drawable->GetSpan(litList[j]); + hsBool currProj = proj; + if( span->fProps & plSpan::kPropProjAsVtx ) + currProj = false; + + if( !(currProj && (span->fProps & plSpan::kPropSkipProjection)) ) + { + plDXLightRef *ref = (plDXLightRef *)light->GetDeviceRef(); + hsScalar strength, scale; + + light->GetStrengthAndScale(span->fWorldBounds, strength, scale); + + // We can't pitch a light because it's "strength" is zero, because the strength is based + // on the center of the span and isn't conservative enough. We can pitch based on the + // scale though, since a light scaled down to zero will have no effect no where. + if( scale > 0 ) + { + plProfile_Inc(FindLightsFound); + span->AddLight(light, strength, scale, currProj); + } + } + } + plProfile_EndTiming(ApplyMoving); + + } + else if( light->GetProperty(plLightInfo::kLPHasSpecular) ) + { + if( !specList.GetCount() ) + continue; + + plProfile_BeginTiming(ApplyToSpec); + + const hsTArray& litList = light->GetAffected(drawable->GetSpaceTree(), + specList, + tmpList, + drawable->GetNativeProperty(plDrawable::kPropCharacter) ); + + // PUT OVERRIDE FOR KILLING PROJECTORS HERE!!!! + hsBool proj = nil != light->GetProjection(); + if( fView.fRenderState & kRenderNoProjection ) + proj = false; + + for( j = 0; j < litList.GetCount(); j++ ) + { + // Use the light IF light is enabled and + // 1) light is movable + // 2) span is movable, or + // 3) Both the light and the span have specular + const plSpan* span = drawable->GetSpan(litList[j]); + hsBool currProj = proj; + if( span->fProps & plSpan::kPropProjAsVtx ) + currProj = false; + + if( !(currProj && (span->fProps & plSpan::kPropSkipProjection)) ) + { + plDXLightRef *ref = (plDXLightRef *)light->GetDeviceRef(); + hsScalar strength, scale; + + light->GetStrengthAndScale(span->fWorldBounds, strength, scale); + + // We can't pitch a light because it's "strength" is zero, because the strength is based + // on the center of the span and isn't conservative enough. We can pitch based on the + // scale though, since a light scaled down to zero will have no effect no where. + if( scale > 0 ) + { + plProfile_Inc(FindLightsFound); + span->AddLight(light, strength, scale, currProj); + } + } + } + plProfile_EndTiming(ApplyToSpec); + } + else + { + if( !moveList.GetCount() ) + continue; + + plProfile_BeginTiming(ApplyToMoving); + + const hsTArray& litList = light->GetAffected(drawable->GetSpaceTree(), + moveList, + tmpList, + drawable->GetNativeProperty(plDrawable::kPropCharacter) ); + + // PUT OVERRIDE FOR KILLING PROJECTORS HERE!!!! + hsBool proj = nil != light->GetProjection(); + if( fView.fRenderState & kRenderNoProjection ) + proj = false; + + for( j = 0; j < litList.GetCount(); j++ ) + { + // Use the light IF light is enabled and + // 1) light is movable + // 2) span is movable, or + // 3) Both the light and the span have specular + const plSpan* span = drawable->GetSpan(litList[j]); + hsBool currProj = proj; + if( span->fProps & plSpan::kPropProjAsVtx ) + currProj = false; + + if( !(currProj && (span->fProps & plSpan::kPropSkipProjection)) ) + { + plDXLightRef *ref = (plDXLightRef *)light->GetDeviceRef(); + hsScalar strength, scale; + + light->GetStrengthAndScale(span->fWorldBounds, strength, scale); + + // We can't pitch a light because it's "strength" is zero, because the strength is based + // on the center of the span and isn't conservative enough. We can pitch based on the + // scale though, since a light scaled down to zero will have no effect no where. + if( scale > 0 ) + { + plProfile_Inc(FindLightsFound); + span->AddLight(light, strength, scale, currProj); + } + } + } + plProfile_EndTiming(ApplyToMoving); + + } + + } + plProfile_EndTiming(ApplyActiveLights); + + IAttachShadowsToReceivers(drawable, visList); + + plProfile_EndTiming(FindLights); +} + +// HarvestVisible //////////////////////////////////////////////////////////////////////// +// Contruct a list of the indices of leaf nodes in the given spacetree which are currently +// visible according to the current cull tree. The cull tree factors in camera frustum and +// occluder polys, but _not_ the current visibility regions, plVisMgr. +// This is the normal path for visibility culling at a gross level (e.g. which SceneNodes +// to bother with, which drawables within the SceneNode). For finer objects, like the spans +// themselves, the culling is done via IGetVisibleSpans, which also takes the plVisMgr into +// account. +hsBool plDXPipeline::HarvestVisible(plSpaceTree* space, hsTArray& visList) +{ + if( !space ) + return false; + + space->SetViewPos(GetViewPositionWorld()); + + space->Refresh(); + + if( fView.fCullTreeDirty ) + IRefreshCullTree(); + + plProfile_BeginTiming(Harvest); + fView.fCullTree.Harvest(space, visList); + plProfile_EndTiming(Harvest); + + return visList.GetCount() != 0; +} + +//// IGetVisibleSpans ///////////////////////////////////////////////////// +// Given a drawable, returns a list of visible span indices. Disabled spans will not +// show up in the list, behaving as if they were culled. +// See plCullTree (in plPipeline) and plSpaceTree (in plDrawable) and plVisMgr (in plScene). +void plDXPipeline::IGetVisibleSpans( plDrawableSpans* drawable, hsTArray& visList, plVisMgr* visMgr ) +{ + static hsTArray tmpVis; + tmpVis.SetCount(0); + visList.SetCount(0); + + drawable->GetSpaceTree()->SetViewPos(GetViewPositionWorld()); + + drawable->GetSpaceTree()->Refresh(); + + if( fView.fCullTreeDirty ) + IRefreshCullTree(); + + const hsScalar viewDist = GetViewDirWorld().InnerProduct(GetViewPositionWorld()); + + const hsTArray &spans = drawable->GetSpanArray(); + + plProfile_BeginTiming(Harvest); + if( visMgr ) + { + drawable->SetVisSet(visMgr); + fView.fCullTree.Harvest(drawable->GetSpaceTree(), tmpVis); + drawable->SetVisSet(nil); + } + else + { + fView.fCullTree.Harvest(drawable->GetSpaceTree(), tmpVis); + } + + // This is a big waste of time, As a desparate "optimization" pass, the artists + // insist on going through and marking objects to fade or pop out of rendering + // past a certain distance. This breaks the batching and requires more CPU to + // check the objects by distance. Since there is no pattern to the distance at + // which objects will be told not to draw, there's no way to make this hierarchical, + // which is what it would take to make it a performance win. So they succeed in + // reducing the poly count, but generally the frame rate goes _down_ as well. + // Unfortunately, this technique actually does work in a few key areas, so + // I haven't been able to purge it. + if (IsDebugFlagSet(plPipeDbg::kFlagSkipVisDist)) + { + int i; + for( i = 0; i < tmpVis.GetCount(); i++ ) + { + if( spans[tmpVis[i]]->fSubType & GetSubDrawableTypeMask() ) + { + visList.Append(tmpVis[i]); + } + } + } + else + { + int i; + for( i = 0; i < tmpVis.GetCount(); i++ ) + { + if( spans[tmpVis[i]]->fSubType & GetSubDrawableTypeMask() ) + { + // We'll check here for spans we can discard because they've completely distance faded out. + // Note this is based on view direction distance (because the fade is), rather than the + // preferrable distance to camera we sort by. + hsScalar minDist, maxDist; + if( drawable->GetSubVisDists(tmpVis[i], minDist, maxDist) ) + { + const hsBounds3Ext& bnd = drawable->GetSpaceTree()->GetNode(tmpVis[i]).fWorldBounds; + hsPoint2 depth; + bnd.TestPlane(GetViewDirWorld(), depth); + if( (0 < minDist + viewDist - depth.fY) + ||(0 > maxDist + viewDist - depth.fX) ) + continue; + } + + visList.Append(tmpVis[i]); + } + } + } + plProfile_EndTiming(Harvest); +} + +// ISetupTransforms ////////////////////////////////////////////////////////////////////////////////// +// Set the D3D world transform according to the input span. +// Engine currently supports HW vertex blending with 2 matrices, +// else a single Local To World. +// If software skinning is being used, the WORLD matrix will be identity, +// because the full local to world is folded into the skinned vertices. +void plDXPipeline::ISetupTransforms(plDrawableSpans* drawable, const plSpan& span, hsMatrix44& lastL2W) +{ + if( span.fNumMatrices ) + { + if( span.fNumMatrices <= 2 ) + { + ISetLocalToWorld( span.fLocalToWorld, span.fWorldToLocal ); + lastL2W = span.fLocalToWorld; + } + else + { + lastL2W.Reset(); + ISetLocalToWorld( lastL2W, lastL2W ); + fView.fLocalToWorldLeftHanded = span.fLocalToWorld.GetParity(); + } + } + else + if( lastL2W != span.fLocalToWorld ) + { + ISetLocalToWorld( span.fLocalToWorld, span.fWorldToLocal ); + lastL2W = span.fLocalToWorld; + } + else + { + fView.fLocalToWorldLeftHanded = lastL2W.GetParity(); + } + + if( span.fNumMatrices == 2 ) + { + D3DXMATRIX mat; + IMatrix44ToD3DMatrix(mat, drawable->GetPaletteMatrix(span.fBaseMatrix+1)); + fD3DDevice->SetTransform(D3DTS_WORLDMATRIX(1), &mat); + fD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_1WEIGHTS); + } + else + { + fD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_DISABLE); + } +} + +// IRefreshDynVertices //////////////////////////////////////////////////////////////////////// +// All dynamic vertices share a single dynamic vertex buffer. They are cycled through +// that buffer using the NOOVERWRITE/DISCARD paradigm. Since the vertices sharing that +// buffer may be of different formats, care is taken to always start a group of vertices +// a the next available position in the buffer aligned with that vertex size. +// Only software skinned objects, dynamic decals, and particle systems currently use the +// dynamic vertex buffer. +hsBool plDXPipeline::IRefreshDynVertices(plGBufferGroup* group, plDXVertexBufferRef* vRef) +{ + // First, pad out our next slot to be on a vertex boundary (for this vertex size). + fNextDynVtx = ((fNextDynVtx + vRef->fVertexSize-1) / vRef->fVertexSize) * vRef->fVertexSize; + + Int32 size = (group->GetVertBufferEnd(vRef->fIndex) - group->GetVertBufferStart(vRef->fIndex)) * vRef->fVertexSize; + if( !size ) + return false; // No error, just nothing to do. + + hsAssert(size > 0, "Bad start and end counts in a group"); + + // If we DON'T have room in our dynamic buffer + if( fNextDynVtx + size > fDynVtxSize ) + { + plProfile_IncCount(DynVBuffs, 1); + + // Advance the timestamp, because we're about to reuse the buffer + fVtxRefTime++; + + // Reset next available spot index to zero + fNextDynVtx = 0; + + } + // Point our ref at the next available spot + Int32 newStart = fNextDynVtx / vRef->fVertexSize; + + vRef->fOffset = newStart - group->GetVertBufferStart(vRef->fIndex); + + // Lock the buffer + // If index is zero, lock with discard, else with overwrite. + DWORD lockFlag = fNextDynVtx ? D3DLOCK_NOOVERWRITE : D3DLOCK_DISCARD; + UInt8* destPtr = nil; + if( FAILED( fDynVtxBuff->Lock( fNextDynVtx, + size, + (void **)&destPtr, + lockFlag) ) ) + { + hsAssert( false, "Cannot lock vertex buffer for writing" ); + return true; + } + + UInt8* vData; + if( vRef->fData ) + { + vData = vRef->fData; + } + else + { + vData = group->GetVertBufferData(vRef->fIndex) + group->GetVertBufferStart(vRef->fIndex) * vRef->fVertexSize; + } + memcpy(destPtr, vData, size); + + // Unlock the buffer + fDynVtxBuff->Unlock(); + + // Advance next available spot index + fNextDynVtx += size; + + // Set the timestamp + vRef->fRefTime = fVtxRefTime; + vRef->SetDirty(false); + + if( !vRef->fD3DBuffer ) + { + vRef->fD3DBuffer = fDynVtxBuff; + fDynVtxBuff->AddRef(); + } + hsAssert(vRef->fD3DBuffer == fDynVtxBuff, "Holding on to an old dynamic buffer?"); + +// vRef->SetRebuiltSinceUsed(true); + + return false; +} + +// ICheckAuxBuffers /////////////////////////////////////////////////////////////////////// +// The AuxBuffers are associated with drawables for things to be drawn right after that +// drawable's contents. In particular, see the plDynaDecal, which includes things like +// water ripples, bullet hits, and footprints. +// This function just makes sure they are ready to be rendered, called right before +// the rendering. +hsBool plDXPipeline::ICheckAuxBuffers(const plAuxSpan* span) +{ + plGBufferGroup* group = span->fGroup; + + plDXVertexBufferRef* vRef = (plDXVertexBufferRef*)group->GetVertexBufferRef(span->fVBufferIdx); + if( !vRef ) + return true; + + plDXIndexBufferRef* iRef = (plDXIndexBufferRef*)group->GetIndexBufferRef(span->fIBufferIdx); + if( !iRef ) + return true; + + // If our vertex buffer ref is volatile and the timestamp is off + // then it needs to be refilled + if( vRef->Expired(fVtxRefTime) ) + { + IRefreshDynVertices(group, vRef); + } + if( vRef->fOffset != iRef->fOffset ) + { + iRef->fOffset = vRef->fOffset; + + iRef->SetRebuiltSinceUsed(true); + } + + return false; // No error +} + +// ICheckDynBuffers //////////////////////////////////////////////////////////////////////////////////////// +// Make sure the buffers underlying this span are ready to be rendered. Meaning that the underlying +// D3D buffers are in sync with the plasma buffers. +hsBool plDXPipeline::ICheckDynBuffers(plDrawableSpans* drawable, plGBufferGroup* group, const plSpan* spanBase) +{ + if( !(spanBase->fTypeMask & plSpan::kVertexSpan) ) + return false; + // If we arent' an trilist, we're toast. + if( !(spanBase->fTypeMask & plSpan::kIcicleSpan) ) + return false; + + plIcicle* span = (plIcicle*)spanBase; + + plDXVertexBufferRef* vRef = (plDXVertexBufferRef*)group->GetVertexBufferRef(span->fVBufferIdx); + if( !vRef ) + return true; + + plDXIndexBufferRef* iRef = (plDXIndexBufferRef*)group->GetIndexBufferRef(span->fIBufferIdx); + if( !iRef ) + return true; + + // If our vertex buffer ref is volatile and the timestamp is off + // then it needs to be refilled + if( vRef->Expired(fVtxRefTime) ) + { + IRefreshDynVertices(group, vRef); + } + if( vRef->fOffset != iRef->fOffset ) + { + iRef->fOffset = vRef->fOffset; + + iRef->SetRebuiltSinceUsed(true); + } + if( iRef->IsDirty() ) + { + IFillIndexBufferRef(iRef, group, span->fIBufferIdx); + iRef->SetRebuiltSinceUsed(true); + } + + return false; // No error +} + +//// IRenderSpans ///////////////////////////////////////////////////////////// +// Renders an array of spans obtained from a plDrawableSpans object +// The incoming visList gives the indices of the spans which are visible and should +// be drawn now, and gives them in sorted order. +void plDXPipeline::IRenderSpans( plDrawableSpans *drawable, const hsTArray& visList ) +{ + plProfile_BeginTiming(RenderSpan); + + hsMatrix44 lastL2W; + UInt32 i, j; + bool drewPatch = false; + hsGMaterial *material; + + const hsTArray& spans = drawable->GetSpanArray(); + + plProfile_IncCount(EmptyList, !visList.GetCount()); + + /// Set this (*before* we do our TestVisibleWorld stuff...) + lastL2W.Reset(); + ISetLocalToWorld( lastL2W, lastL2W ); // This is necessary; otherwise, we have to test for + // the first transform set, since this'll be identity + // but the actual device transform won't be (unless + // we do this) + + + /// Loop through our spans, combining them when possible + for( i = 0; i < visList.GetCount(); ) + { + material = GetOverrideMaterial() ? GetOverrideMaterial() : drawable->GetMaterial( spans[ visList[ i ] ]->fMaterialIdx ); + + /// It's an icicle--do our icicle merge loop + plIcicle tempIce(*( (plIcicle *)spans[ visList[ i ] ] )); + + // Start at i + 1, look for as many spans as we can add to tempIce + for( j = i + 1; j < visList.GetCount(); j++ ) + { + if( GetOverrideMaterial() ) + tempIce.fMaterialIdx = spans[visList[j]]->fMaterialIdx; + + plProfile_BeginTiming(MergeCheck); + if( !spans[ visList[ j ] ]->CanMergeInto( &tempIce ) ) + { + plProfile_EndTiming(MergeCheck); + break; + } + plProfile_EndTiming(MergeCheck); + plProfile_Inc(SpanMerge); + + plProfile_BeginTiming(MergeSpan); + spans[ visList[ j ] ]->MergeInto( &tempIce ); + plProfile_EndTiming(MergeSpan); + } + + if( material != nil ) + { + // What do we change? + + plProfile_BeginTiming(SpanTransforms); + ISetupTransforms(drawable, tempIce, lastL2W); + plProfile_EndTiming(SpanTransforms); + + // Turn on this spans lights and turn off the rest. + IEnableLights( &tempIce ); + + // Check that the underlying buffers are ready to go. + plProfile_BeginTiming(CheckDyn); + ICheckDynBuffers(drawable, drawable->GetBufferGroup(tempIce.fGroupIdx), &tempIce); + plProfile_EndTiming(CheckDyn); + + plProfile_BeginTiming(CheckStat); + CheckVertexBufferRef(drawable->GetBufferGroup(tempIce.fGroupIdx), tempIce.fVBufferIdx); + CheckIndexBufferRef(drawable->GetBufferGroup(tempIce.fGroupIdx), tempIce.fIBufferIdx); + plProfile_EndTiming(CheckStat); + + // Draw this span now + IRenderBufferSpan( tempIce, + drawable->GetVertexRef( tempIce.fGroupIdx, tempIce.fVBufferIdx ), + drawable->GetIndexRef( tempIce.fGroupIdx, tempIce.fIBufferIdx ), + material, + tempIce.fVStartIdx, tempIce.fVLength, // These are used as our accumulated range + tempIce.fIPackedIdx, tempIce.fILength ); + } + + // Restart our search... + i = j; + } + + plProfile_EndTiming(RenderSpan); + /// All done! +} + +//// IAddBoundsSpan /////////////////////////////////////////////////////////// +// Creates a new span for the given drawable to represent the specified +// world bounds. +// Debugging only. + +void plDXPipeline::IAddBoundsSpan( plDrawableSpans *ice, const hsBounds3Ext *bounds, UInt32 bndColor ) +{ +#if MCN_BOUNDS_SPANS + static hsTArray spanArray; + static hsMatrix44 identMatrix; + static hsPoint3 c[ 8 ], n[ 8 ]; + static int nPts[ 8 ][ 3 ] = { { -1, -1, -1 }, { 1, -1, -1 }, { -1, 1, -1 }, { 1, 1, -1 }, + { -1, -1, 1 }, { 1, -1, 1 }, { -1, 1, 1 }, { 1, 1, 1 } }; + int i; + plGeometrySpan *newSpan; + + + if( spanArray.GetCount() == 0 ) + { + spanArray.Reset(); + spanArray.Append( TRACKED_NEW plGeometrySpan() ); + identMatrix.Reset(); + + // Make normals + for( i = 0; i < 8; i++ ) + { + n[ i ].fX = (float)nPts[ i ][ 0 ]; + n[ i ].fY = (float)nPts[ i ][ 1 ]; + n[ i ].fZ = (float)nPts[ i ][ 2 ]; + } + } + else + spanArray[ 0 ] = TRACKED_NEW plGeometrySpan(); + + newSpan = spanArray[ 0 ]; + + newSpan->BeginCreate( fBoundsMat, identMatrix, 0 ); + + // Make corners + c[1] = c[2] = c[4] = *bounds->GetCorner(&c[0]); + hsVector3 axes[3]; + bounds->GetAxes(axes+0, axes+1, axes+2); + c[1] += axes[0]; + c[2] += axes[1]; + c[4] += axes[2]; + + c[3] = c[1]; + c[3] += axes[1]; + + c[5] = c[1]; + c[5] += axes[2]; + + c[6] = c[2]; + c[6] += axes[2]; + + c[7] = c[6]; + c[7] += axes[0]; + + for( i = 0; i < 8; i++ ) + newSpan->AddVertex( &c[ i ], &n[ i ], bndColor ); + + newSpan->AddTriIndices( 0, 1, 2 ); + newSpan->AddTriIndices( 2, 1, 3 ); + + newSpan->AddTriIndices( 6, 3, 7 ); + newSpan->AddTriIndices( 7, 1, 5 ); + newSpan->AddTriIndices( 5, 0, 4 ); + newSpan->AddTriIndices( 4, 2, 6 ); + + newSpan->EndCreate(); + + fBSpansToDelete.Append( ice->AppendDISpans( spanArray ) ); + +#endif +} + +//// IAddNormalsSpan ////////////////////////////////////////////////////////// +// Creates a new span for the given drawable to represent the specified +// world bounds. +// Debugging only. + +void plDXPipeline::IAddNormalsSpan( plDrawableSpans *ice, plIcicle *span, plDXVertexBufferRef *vRef, UInt32 bndColor ) +{ +#if MCN_BOUNDS_SPANS + static hsTArray spanArray; + static hsMatrix44 identMatrix; + static hsPoint3 point, off, blank; + hsVector3 b2; + UInt16 v1, v2, v3; + int i; + plGeometrySpan *newSpan; + + + if( spanArray.GetCount() == 0 ) + { + spanArray.Reset(); + spanArray.Append( TRACKED_NEW plGeometrySpan() ); + identMatrix.Reset(); + } + else + spanArray[ 0 ] = TRACKED_NEW plGeometrySpan(); + + newSpan = spanArray[ 0 ]; + + newSpan->BeginCreate( fBoundsMat, span->fLocalToWorld, 0 ); + + for( i = 0; i < span->fVLength; i++ ) + { + point = vRef->fOwner->Position( span->fVBufferIdx, span->fCellIdx, span->fCellOffset + i ); + b2 = vRef->fOwner->Normal( span->fVBufferIdx, span->fCellIdx, span->fCellOffset + i ); + off.Set( point.fX + b2.fX, point.fY + b2.fY, point.fZ + b2.fZ ); + v1 = newSpan->AddVertex( &point, &blank, bndColor ); + v2 = newSpan->AddVertex( &off, &blank, bndColor ); + v3 = newSpan->AddVertex( &point, &blank, bndColor ); + newSpan->AddTriIndices( v1, v2, v3 ); + } + + newSpan->EndCreate(); + + fBSpansToDelete.Append( ice->AppendDISpans( spanArray ) ); + +#endif +} + +//// BeginRender ////////////////////////////////////////////////////////////// +// Specifies the beginning of the render frame. +// If this succeeds (returns false) it must be matched with a call to EndRender. +// Normally, the main client loop will wrap the entire scene render (including +// any offscreen rendering) in a BeginRender/EndRender pair. There is no need +// for further calls for sub-renders. +hsBool plDXPipeline::BeginRender() +{ + // Do we have some restoration work ahead of us? + // Checks for Device Lost condition + if( IResetDevice() ) + return true; + + // We were lost, but now we're found! Spread the good word brother! + if( fDevWasLost ) + { + /// Broadcast a message letting everyone know that we were recreated and that + /// all device-specific stuff needs to be recreated +// plDeviceRecreateMsg* clean = TRACKED_NEW plDeviceRecreateMsg(); +// plgDispatch::MsgSend(clean); + + fDevWasLost = false; + } + + if (IsDebugFlagSet(plPipeDbg::kFlagReload)) + { + IReleaseShaders(); + fD3DDevice->EvictManagedResources(); + fEvictTime = fTextUseTime; + fManagedSeen = 0; + SetDebugFlag(plPipeDbg::kFlagReload, false); + } + + // offset transform + RefreshScreenMatrices(); + + // If this is the primary BeginRender, make sure we're really ready. + if( !fInSceneDepth++ ) + { + // Workaround for NVidia memory manager bug. Search for "OSVERSIONINFO" to + // find notes on the bug. This is where we purge managed memory periodically. + plProfile_Set(ManSeen, fManagedSeen); + if( fManagedCutoff ) + { + plConst(UInt32) kMinEvictTime(1800); // ~2 minutes @ 15FPS + if( (fManagedSeen > fManagedCutoff) && (fTexUsed + fVtxUsed < fManagedCutoff) && (fTextUseTime - fEvictTime > kMinEvictTime) ) + { + fD3DDevice->EvictManagedResources(); + fManagedSeen = 0; + fEvictTime = fTextUseTime; + plProfile_IncCount(ManEvict, 1); + } + } + + // Superfluous setting of Z state. + fD3DDevice->SetRenderState( D3DRS_ZENABLE, + ( fView.IsPerspective() && ( fSettings.fD3DCaps & kCapsWBuffer ) ) + ? D3DZB_USEW : D3DZB_TRUE ); + + /// If we have a renderTarget active, use its viewport + ISetViewport(); + + // Tell D3D we're ready to start rendering. + if( FAILED(fD3DDevice->BeginScene()) ) + { + fDeviceLost = true; + } + + // Reset all our buffer/image usage counters + fNextDynVtx = 0; + fVtxRefTime++; + + fTexUsed = 0; + fVtxUsed = 0; + fTextUseTime++; + + // Render any shadow maps that have been submitted for this frame. + IPreprocessShadows(); + IPreprocessAvatarTextures(); + } + fRenderCnt++; + + // Would probably rather this be an input. + fTime = hsTimer::GetSysSeconds(); + + return false; +} + +//// ISetViewport ///////////////////////////////////////////////////////////// +// Translate our viewport into a D3D viewport +void plDXPipeline::ISetViewport() +{ + D3DVIEWPORT9 vp = { GetViewTransform().GetViewPortLeft(), + GetViewTransform().GetViewPortTop(), + GetViewTransform().GetViewPortWidth(), + GetViewTransform().GetViewPortHeight(), + 0.f, 1.f }; + + + WEAK_ERROR_CHECK( fD3DDevice->SetViewport( &vp ) ); +} + +//// RenderScreenElements ///////////////////////////////////////////////////// +// Renders all the screen elements, such as debug text and plates. Also puts +// up all the info about vertex buffers and such. Should be called right +// before EndRender(), but only on the main surface (not on renderTargets, +// for example). + +void plDXPipeline::RenderScreenElements() +{ + bool reset = false; + + +#if MCN_BOUNDS_SPANS + if( fBoundsSpans && fBSpansToDelete.GetCount() > 0 ) + { + Draw( fBoundsSpans ); + + int i; + for( i = 0; i < fBSpansToDelete.GetCount(); i++ ) + fBoundsSpans->RemoveDISpans( fBSpansToDelete[ i ] ); + + fBSpansToDelete.Reset(); + } +#endif + if( fCullProxy ) + Draw( fCullProxy ); + +#ifdef MF_ENABLE_HACKOFF + //WHITE + static plPlate* hackPlate = nil; + if( doHackPlate < hackOffscreens.GetCount() ) + { + if( !hackPlate ) + { + fPlateMgr->CreatePlate(&hackPlate, 0.5f, 0.5f, 1.0f, 1.0f); + hackPlate->CreateBlankMaterial(32, 32, false); + } + } + if( hackPlate ) + { + if( doHackPlate < hackOffscreens.GetCount() ) + { + hsGMaterial* hackMat = hackPlate->GetMaterial(); + plLayer* lay = plLayer::ConvertNoRef(hackMat->GetLayer(0)); + if( lay ) + lay->SetTexture(hackOffscreens[doHackPlate]); + hackPlate->SetVisible( true ); + } + else + { + hackPlate->SetVisible( false ); + } + } +#endif // MF_ENABLE_HACKOFF + + hsGMatState tHack = PushMaterialOverride(hsGMatState::kMisc, hsGMatState::kMiscWireFrame, false); + hsGMatState ambHack = PushMaterialOverride(hsGMatState::kShade, hsGMatState::kShadeWhite, true); + + plProfile_BeginTiming(PlateMgr); + /// Plates + if( fPlateMgr ) + { + fPlateMgr->DrawToDevice( this ); + reset = true; + } + plProfile_EndTiming(PlateMgr); + + PopMaterialOverride(ambHack, true); + PopMaterialOverride(tHack, false); + + plProfile_BeginTiming(DebugText); + /// Debug text + if( fDebugTextMgr && plDebugText::Instance().IsEnabled() ) + { + fDebugTextMgr->DrawToDevice( this ); + + reset = true; + } + plProfile_EndTiming(DebugText); + + plProfile_BeginTiming(Reset); + if( reset ) + { + // Reset these since the drawing might have trashed them + hsRefCnt_SafeUnRef( fSettings.fCurrVertexBuffRef ); + hsRefCnt_SafeUnRef( fSettings.fCurrIndexBuffRef ); + fSettings.fCurrVertexBuffRef = nil; + fSettings.fCurrIndexBuffRef = nil; + + fView.fXformResetFlags = fView.kResetAll; // Text destroys view transforms + hsRefCnt_SafeUnRef( fLayerRef[ 0 ] ); + fLayerRef[ 0 ] = nil; // Text destroys stage 0 texture + } + plProfile_EndTiming(Reset); +} + +//// EndRender //////////////////////////////////////////////////////////////// +// Tell D3D we're through rendering for this frame, and flip the back buffer to front. +// Also includes a bit of making sure we're not holding onto anything that might +// get deleted before the next render. +hsBool plDXPipeline::EndRender() +{ +#ifdef MF_ENABLE_HACKOFF + hackOffscreens.SetCount(0); +#endif // MF_ENABLE_HACKOFF + + IBottomLayer(); + + hsBool retVal = false; + /// Actually end the scene + if( !--fInSceneDepth ) + { + WEAK_ERROR_CHECK( fD3DDevice->EndScene() ); + retVal = IFlipSurface(); + + IClearShadowSlaves(); + } + + // Do this last, after we've drawn everything + // Just letting go of things we're done with for the frame. + fForceMatHandle = true; + hsRefCnt_SafeUnRef( fCurrMaterial ); + fCurrMaterial = nil; + + int i; + for( i = 0; i < 8; i++ ) + { + if( fLayerRef[i] ) + { + hsRefCnt_SafeUnRef(fLayerRef[i]); + fLayerRef[i] = nil; + } + } + + return retVal; +} + +// SetGamma //////////////////////////////////////////////////////////// +// Create and set a gamma table based on the input exponent values for +// R, G, and B. Can also set explicit table using the other SetGamma(). +hsBool plDXPipeline::SetGamma(hsScalar eR, hsScalar eG, hsScalar eB) +{ + if( fSettings.fNoGammaCorrect ) + return false; + + D3DGAMMARAMP ramp; + + ramp.red[0] = ramp.green[0] = ramp.blue[0] = 0L; + + plConst(hsScalar) kMinE(0.1f); + if( eR > kMinE ) + eR = 1.f / eR; + else + eR = 1.f / kMinE; + if( eG > kMinE ) + eG = 1.f / eG; + else + eG = 1.f / kMinE; + if( eB > kMinE ) + eB = 1.f / eB; + else + eB = 1.f / kMinE; + + int i; + for( i = 1; i < 256; i++ ) + { + hsScalar orig = hsScalar(i) / 255.f; + + hsScalar gamm; + gamm = pow(orig, eR); + gamm *= hsScalar(UInt16(-1)); + ramp.red[i] = UInt16(gamm); + + gamm = pow(orig, eG); + gamm *= hsScalar(UInt16(-1)); + ramp.green[i] = UInt16(gamm); + + gamm = pow(orig, eB); + gamm *= hsScalar(UInt16(-1)); + ramp.blue[i] = UInt16(gamm); + } + + fD3DDevice->SetGammaRamp(0, D3DSGR_NO_CALIBRATION, &ramp); + + return true; +} + +// SetGamma +// Copy the input gamma tables and pass them to the hardware. +hsBool plDXPipeline::SetGamma(const UInt16* const tabR, const UInt16* const tabG, const UInt16* const tabB) +{ + if( fSettings.fNoGammaCorrect ) + return false; + + D3DGAMMARAMP ramp; + memcpy(ramp.red, tabR, 256 * sizeof(WORD)); + memcpy(ramp.green, tabG, 256 * sizeof(WORD)); + memcpy(ramp.blue, tabB, 256 * sizeof(WORD)); + + fD3DDevice->SetGammaRamp(0, D3DSGR_NO_CALIBRATION, &ramp); + + return true; +} + + +//// IFlipSurface ///////////////////////////////////////////////////////////// +// Initiate moving the back buffer contents to the front buffer. Will detect +// and set the device lost condition when it occurs. +hsBool plDXPipeline::IFlipSurface() +{ + /// Works now for both fullscreen and windowed modes + HRESULT hr = D3D_OK; + if( fSettings.fCurrRenderTarget == nil ) + { + hr = fD3DDevice->Present( nil, nil, fSettings.fHWnd, nil ); + } + + if( FAILED(hr) ) + { + fDeviceLost = true; + } + return fDeviceLost; +} + +// ExtractMipMap +// This code works and is fairly fast for creating a new mipmap +// as a copy of the data in an offscreen render target. It's not +// currently used, because of driver bugs found in rendering to +// offscreen render targets. +plMipmap* plDXPipeline::ExtractMipMap(plRenderTarget* targ) +{ + if( plCubicRenderTarget::ConvertNoRef(targ) ) + return nil; + + if( targ->GetPixelSize() != 32 ) + { + hsAssert(false, "Only RGBA8888 currently implemented"); + return nil; + } + + plDXRenderTargetRef* ref = (plDXRenderTargetRef*)targ->GetDeviceRef(); + if( !ref ) + return nil; + + IDirect3DSurface9* surf = ref->GetColorSurface(); + if( !surf ) + return nil; + + D3DLOCKED_RECT rect; + if( FAILED( surf->LockRect(&rect, nil, D3DLOCK_READONLY) ) ) + { + return nil; + } + + const int width = targ->GetWidth(); + const int height = targ->GetHeight(); + + plMipmap* mipMap = TRACKED_NEW plMipmap(width, height, plMipmap::kARGB32Config, 1); + + UInt8* ptr = (UInt8*)(rect.pBits); + const int pitch = rect.Pitch; + + const UInt32 blackOpaque = 0xff000000; + int y; + for( y = 0; y < height; y++ ) + { + UInt32* destPtr = mipMap->GetAddr32(0, y); + UInt32* srcPtr = (UInt32*)ptr; + int x; + for( x = 0; x < width; x++ ) + { + destPtr[x] = srcPtr[x] | blackOpaque; + } + ptr += pitch; + } + + surf->UnlockRect(); + + return mipMap; +} + +//// CaptureScreen //////////////////////////////////////////////////////////// +// Copy the current contents of the front buffer to the destination mipmap, with optional +// rescaling. Note that the mipmap function which does this rescaling is of low quality +// (pyramid filter even though it claims a box filter) and low performance (slow). +// If it mattered, it would take about an hour to have a higher performance, higher quality, +// more robust rescale function. +// This function is fairly straightforward, the complexity only comes from making sure +// all pixels in dest get written to, even though the client window may be partially +// offscreen. If the client window is partially offscreen, there will be no values +// for the "offscreen pixels" to copy to dest, so opaque black is used. +hsBool plDXPipeline::CaptureScreen( plMipmap *dest, bool flipVertical, UInt16 desiredWidth, UInt16 desiredHeight ) +{ + UInt32 y, *destPtr, *srcPtr, width, height, bigWidth, bigHeight; + IDirect3DSurface9 *surface; + D3DLOCKED_RECT rect; + RECT rToLock; + + + width = GetViewTransform().GetViewPortWidth(); + height = GetViewTransform().GetViewPortHeight(); + + int left = 0; + int right = width; + int top = 0; + int bottom = height; + + if( fSettings.fFullscreen ) + { + if (FAILED(fD3DDevice->CreateOffscreenPlainSurface(width, height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &surface, NULL))) + return false; + + rToLock.left = GetViewTransform().GetViewPortLeft(); + rToLock.top = GetViewTransform().GetViewPortTop(); + rToLock.right = GetViewTransform().GetViewPortRight(); + rToLock.bottom = GetViewTransform().GetViewPortBottom(); + } + else + { + bigWidth = GetSystemMetrics( SM_CXSCREEN ); + bigHeight = GetSystemMetrics( SM_CYSCREEN ); + + if (FAILED(fD3DDevice->CreateOffscreenPlainSurface(bigWidth, bigHeight, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &surface, NULL))) + return false; + + GetClientRect( fSettings.fHWnd, &rToLock ); + MapWindowPoints( fSettings.fHWnd, nil, (POINT *)&rToLock, 2 ); + + if( rToLock.right > bigWidth ) + { + right -= (rToLock.right - bigWidth); + rToLock.right = bigWidth; + } + if( rToLock.bottom > bigHeight ) + { + bottom -= (rToLock.bottom - bigHeight); + rToLock.bottom = bigHeight; + } + if( rToLock.top < 0 ) + { + top -= rToLock.top; + rToLock.top = 0; + } + if( rToLock.left < 0 ) + { + left -= rToLock.left; + rToLock.left = 0; + } + } + + UINT swapChain = 0; + if( FAILED( fD3DDevice->GetFrontBufferData(swapChain, surface) ) ) + { + ReleaseObject( surface ); + return false; + } + + if( FAILED( surface->LockRect( &rect, &rToLock, D3DLOCK_READONLY ) ) ) + { + ReleaseObject( surface ); + return false; + } + + if( dest->GetWidth() != width || dest->GetHeight() != height || + dest->GetPixelSize() != 32 ) + { + dest->Reset(); + dest->Create( width, height, plMipmap::kARGB32Config, 1 ); + } + + const UInt32 blackOpaque = 0xff000000; + /// Copy over + for( y = 0; y < top; y++ ) + { + if (flipVertical) + destPtr = dest->GetAddr32( 0, height - 1 - y ); + else + destPtr = dest->GetAddr32( 0, y ); + + int x; + for( x = 0; x < width; x++ ) + { + *destPtr++ = blackOpaque; + } + } + for( y = top; y < bottom; y++ ) + { + srcPtr = (UInt32 *)( (UInt8 *)rect.pBits + rect.Pitch * y ); + if (flipVertical) + destPtr = dest->GetAddr32( 0, height - 1 - y ); + else + destPtr = dest->GetAddr32( 0, y ); + + int x; + for( x = 0; x < left; x++ ) + *destPtr++ = blackOpaque; + + memcpy( destPtr, srcPtr, (right - left) * sizeof( UInt32 ) ); + destPtr += (right - left); + + for( x = right; x < width; x++ ) + *destPtr++ = blackOpaque; + } + for( y = bottom; y < height; y++ ) + { + if (flipVertical) + destPtr = dest->GetAddr32( 0, height - 1 - y ); + else + destPtr = dest->GetAddr32( 0, y ); + + int x; + for( x = 0; x < width; x++ ) + { + *destPtr++ = blackOpaque; + } + } + + surface->UnlockRect(); + ReleaseObject( surface ); + + if( desiredWidth != 0 && desiredHeight != nil ) + { + // Rescale to the right size + dest->ResizeNicely( desiredWidth, desiredHeight, plMipmap::kDefaultFilter ); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Render Targets /////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// MakeRenderTargetRef ////////////////////////////////////////////////////// +// Create the a Plasma render target ref, filling in the underlying D3D resources +// (e.g. color/depth buffers). +// Note that for ATI boards, we create a single depth surface for them to share. +// That can actually be 2 depth surfaces, if some color surfaces are 16 bit and +// others are 24/32 bit, since the ATI's want to match color depth with depth depth. +hsGDeviceRef *plDXPipeline::MakeRenderTargetRef( plRenderTarget *owner ) +{ + plDXRenderTargetRef *ref = nil; + IDirect3DSurface9 *surface = nil, *depthSurface = nil; + IDirect3DTexture9 *texture = nil; + IDirect3DCubeTexture9 *cTexture = nil; + D3DFORMAT surfFormat = D3DFMT_UNKNOWN, depthFormat = D3DFMT_UNKNOWN; + D3DRESOURCETYPE resType; + int i; + plCubicRenderTarget *cubicRT; + UInt16 width, height; + + hsAssert(!fManagedAlloced, "Allocating non-managed resource with managed resources alloc'd"); + + /// Check--is this renderTarget really a child of a cubicRenderTarget? + if( owner->GetParent() != nil ) + { + /// This'll create the deviceRefs for all of its children as well + MakeRenderTargetRef( owner->GetParent() ); + return owner->GetDeviceRef(); + } + + // If we already have a rendertargetref, we just need it filled out with D3D resources. + if( owner->GetDeviceRef() != nil ) + ref = (plDXRenderTargetRef *)owner->GetDeviceRef(); + + // Look for supported format. Note that the surfFormat and depthFormat are + // passed in by ref, so they may be different after this function call (if + // an exact match isn't supported, but something similar is). + if( !IPrepRenderTargetInfo( owner, surfFormat, depthFormat, resType ) ) + { + hsAssert( false, "Error getting renderTarget info" ); + return nil; + } + + + /// Create the render target now + // Start with the depth surface. + // Note that we only ever give a cubic rendertarget a single shared depth buffer, + // since we only render one face at a time. If we were rendering part of face X, then part + // of face Y, then more of face X, then they would all need their own depth buffers. + if( owner->GetZDepth() && (owner->GetFlags() & ( plRenderTarget::kIsTexture | plRenderTarget::kIsOffscreen )) ) + { + // 9600 THRASH + if( !fSettings.fShareDepth ) + { + /// Create the depthbuffer + if( FAILED( fD3DDevice->CreateDepthStencilSurface( + owner->GetWidth(), owner->GetHeight(), depthFormat, + D3DMULTISAMPLE_NONE, 0, FALSE, + &depthSurface, NULL ) ) ) + { + return nil; + } + + // See plDXRenderTargetRef::Release() + //D3DSURF_MEMNEW(depthSurface); + } + else + { + const int iZ = owner->GetZDepth() / 24; + if( !fSharedDepthSurface[iZ] ) + { + plConst(DWORD) kSharedWidth(800); + plConst(DWORD) kSharedHeight(600); + if( FAILED( fD3DDevice->CreateDepthStencilSurface( + kSharedWidth, kSharedHeight, depthFormat, + D3DMULTISAMPLE_NONE, 0, FALSE, + &fSharedDepthSurface[iZ], NULL ) ) ) + { + return nil; + } + // See plDXRenderTargetRef::Release() + //D3DSURF_MEMNEW(fSharedDepthSurface[iZ]); + fSharedDepthFormat[iZ] = depthFormat; + } + hsAssert(depthFormat == fSharedDepthFormat[iZ], "Mismatch on render target types"); + fSharedDepthSurface[iZ]->AddRef(); + depthSurface = fSharedDepthSurface[iZ]; + } + } + + // See if it's a cubic render target. + // Primary consumer here is the vertex/pixel shader water. + cubicRT = plCubicRenderTarget::ConvertNoRef( owner ); + if( cubicRT != nil ) + { + /// And create the ref (it'll know how to set all the flags) + if( ref != nil ) + ref->Set( surfFormat, 0, owner ); + else + ref = TRACKED_NEW plDXRenderTargetRef( surfFormat, 0, owner ); + + if( !FAILED( fD3DDevice->CreateCubeTexture( owner->GetWidth(), 1, D3DUSAGE_RENDERTARGET, surfFormat, + D3DPOOL_DEFAULT, (IDirect3DCubeTexture9 **)&cTexture, NULL ) ) ) + { + /// Create a CUBIC texture + for( i = 0; i < 6; i++ ) + { + plRenderTarget *face = cubicRT->GetFace( i ); + plDXRenderTargetRef *fRef; + + if( face->GetDeviceRef() != nil ) + { + fRef = (plDXRenderTargetRef *)face->GetDeviceRef(); + fRef->Set( surfFormat, 0, face ); + if( !fRef->IsLinked() ) + fRef->Link( &fRenderTargetRefList ); + } + else + { + face->SetDeviceRef( TRACKED_NEW plDXRenderTargetRef( surfFormat, 0, face, false ) ); + ( (plDXRenderTargetRef *)face->GetDeviceRef())->Link( &fRenderTargetRefList ); + // Unref now, since for now ONLY the RT owns the ref, not us (not until we use it, at least) + hsRefCnt_SafeUnRef( face->GetDeviceRef() ); + } + } + + D3DSURF_MEMNEW(cTexture); + + ref->SetTexture( cTexture, depthSurface ); + } + else + { + ReleaseObject(depthSurface); + hsRefCnt_SafeUnRef(ref); + ref = nil; + } + } + // Not a cubic, is it a texture render target? These are currently used + // primarily for shadow map generation. + else if( owner->GetFlags() & plRenderTarget::kIsTexture ) + { + /// Create a normal texture + if( ref != nil ) + ref->Set( surfFormat, 0, owner ); + else + ref = TRACKED_NEW plDXRenderTargetRef( surfFormat, 0, owner ); + + if( !FAILED( fD3DDevice->CreateTexture( owner->GetWidth(), owner->GetHeight(), 1, D3DUSAGE_RENDERTARGET, surfFormat, + D3DPOOL_DEFAULT, (IDirect3DTexture9 **)&texture, NULL ) ) ) + { + D3DSURF_MEMNEW(texture); + + ref->SetTexture( texture, depthSurface ); + } + else + { + ReleaseObject(depthSurface); + hsRefCnt_SafeUnRef(ref); + ref = nil; + } + } + // Not a texture either, must be a plain offscreen. + // Note that the plain offscreen code path works and was used until recently, + // until it turned up that some hardware had bugs on rendering to + // an offscreen. + // Some GeForce1's had lighting anomolies, although my GeForce1 DDR didn't. + // Some ATI's showed a momemtary glitch of corrupted rendering on the frame + // when rendering both to the primary and an offscreen (again, not mine). + // So the Offscreen isn't currently used for anything. + else if( owner->GetFlags() & plRenderTarget::kIsOffscreen ) + { + /// Create a blank surface + if( ref != nil ) + ref->Set( surfFormat, 0, owner ); + else + ref = TRACKED_NEW plDXRenderTargetRef( surfFormat, 0, owner ); + + width = owner->GetWidth(); + height = owner->GetHeight(); + + // Specify true for lockable, otherwise I'm not sure what we'd do with it. I guess we + // could copyrect to another surface, presumably a texture. But right now the only + // thing we use this for is to render a snapshot and copy it to sysmem, which implies + // lockable. + if( !FAILED( fD3DDevice->CreateRenderTarget( width, height, surfFormat, + D3DMULTISAMPLE_NONE, 0, + TRUE, &surface, NULL ) ) ) + { + D3DSURF_MEMNEW(surface); + + ref->SetTexture( surface, depthSurface ); + } + else + { + ReleaseObject(depthSurface); + hsRefCnt_SafeUnRef(ref); + ref = nil; + } + + } + + // Keep it in a linked list for ready destruction. + if( owner->GetDeviceRef() != ref ) + { + owner->SetDeviceRef( ref ); + // Unref now, since for now ONLY the RT owns the ref, not us (not until we use it, at least) + hsRefCnt_SafeUnRef( ref ); + if( ref != nil && !ref->IsLinked() ) + ref->Link( &fRenderTargetRefList ); + } + else + { + if( ref != nil && !ref->IsLinked() ) + ref->Link( &fRenderTargetRefList ); + } + + // Mark as dirty. + if( ref != nil ) + { + ref->SetDirty( false ); + } + + return ref; +} + +//// SharedRenderTargetRef ////////////////////////////////////////////////////// +// Same as MakeRenderTargetRef, except specialized for the shadow map generation. +// The shadow map pools of a given dimension (called RenderTargetPool) all share +// a single depth buffer of that size. This allows sharing on NVidia hardware +// that wants the depth buffer dimensions to match the color buffer size. +// It may be that NVidia hardware doesn't care any more. Contact Matthias +// about that. +hsGDeviceRef* plDXPipeline::SharedRenderTargetRef(plRenderTarget* share, plRenderTarget *owner) +{ + plDXRenderTargetRef* ref = nil; + IDirect3DSurface9* surface = nil; + IDirect3DSurface9* depthSurface = nil; + IDirect3DTexture9* texture = nil; + IDirect3DCubeTexture9* cTexture = nil; + D3DFORMAT surfFormat = D3DFMT_UNKNOWN, depthFormat = D3DFMT_UNKNOWN; + D3DRESOURCETYPE resType; + int i; + plCubicRenderTarget* cubicRT; + UInt16 width, height; + + // If we don't already have one to share from, start from scratch. + if( !share ) + return MakeRenderTargetRef(owner); + + hsAssert(!fManagedAlloced, "Allocating non-managed resource with managed resources alloc'd"); + +#ifdef HS_DEBUGGING + // Check out the validity of the match. Debug only. + hsAssert(!owner->GetParent() == !share->GetParent(), "Mismatch on shared render target"); + hsAssert(owner->GetWidth() == share->GetWidth(), "Mismatch on shared render target"); + hsAssert(owner->GetHeight() == share->GetHeight(), "Mismatch on shared render target"); + hsAssert(owner->GetZDepth() == share->GetZDepth(), "Mismatch on shared render target"); + hsAssert(owner->GetStencilDepth() == share->GetStencilDepth(), "Mismatch on shared render target"); +#endif // HS_DEBUGGING + + /// Check--is this renderTarget really a child of a cubicRenderTarget? + if( owner->GetParent() != nil ) + { + /// This'll create the deviceRefs for all of its children as well + SharedRenderTargetRef(share->GetParent(), owner->GetParent()); + return owner->GetDeviceRef(); + } + + if( owner->GetDeviceRef() != nil ) + ref = (plDXRenderTargetRef *)owner->GetDeviceRef(); + + // Look for a good format of matching color and depth size. + if( !IFindRenderTargetInfo(owner, surfFormat, resType) ) + { + hsAssert( false, "Error getting renderTarget info" ); + return nil; + } + + + /// Create the render target now + // Start with the depth. We're just going to share the depth surface on the + // input shareRef. + plDXRenderTargetRef* shareRef = (plDXRenderTargetRef*)share->GetDeviceRef(); + hsAssert(shareRef, "Trying to share from a render target with no ref"); + if( shareRef->fD3DDepthSurface ) + shareRef->fD3DDepthSurface->AddRef(); + depthSurface = shareRef->fD3DDepthSurface; + + // Check for Cubic. This is unlikely, since this function is currently only + // used for the shadow map pools. + cubicRT = plCubicRenderTarget::ConvertNoRef( owner ); + if( cubicRT != nil ) + { + /// And create the ref (it'll know how to set all the flags) + if( ref != nil ) + ref->Set( surfFormat, 0, owner ); + else + ref = TRACKED_NEW plDXRenderTargetRef( surfFormat, 0, owner ); + + hsAssert(!fManagedAlloced, "Alloc default with managed alloc'd"); + if( !FAILED( fD3DDevice->CreateCubeTexture( owner->GetWidth(), 1, D3DUSAGE_RENDERTARGET, surfFormat, + D3DPOOL_DEFAULT, (IDirect3DCubeTexture9 **)&cTexture, NULL ) ) ) + { + + /// Create a CUBIC texture + for( i = 0; i < 6; i++ ) + { + plRenderTarget *face = cubicRT->GetFace( i ); + plDXRenderTargetRef *fRef; + + if( face->GetDeviceRef() != nil ) + { + fRef = (plDXRenderTargetRef *)face->GetDeviceRef(); + fRef->Set( surfFormat, 0, face ); + if( !fRef->IsLinked() ) + fRef->Link( &fRenderTargetRefList ); + } + else + { + face->SetDeviceRef( TRACKED_NEW plDXRenderTargetRef( surfFormat, 0, face, false ) ); + ( (plDXRenderTargetRef *)face->GetDeviceRef())->Link( &fRenderTargetRefList ); + // Unref now, since for now ONLY the RT owns the ref, not us (not until we use it, at least) + hsRefCnt_SafeUnRef( face->GetDeviceRef() ); + } + } + + D3DSURF_MEMNEW(cTexture); + + ref->SetTexture( cTexture, depthSurface ); + } + else + { + ReleaseObject(depthSurface); + hsRefCnt_SafeUnRef(ref); + ref = nil; + } + } + // Is it a texture render target? Probably, since shadow maps are all we use this for. + else if( owner->GetFlags() & plRenderTarget::kIsTexture ) + { + /// Create a normal texture + if( ref != nil ) + ref->Set( surfFormat, 0, owner ); + else + ref = TRACKED_NEW plDXRenderTargetRef( surfFormat, 0, owner ); + + hsAssert(!fManagedAlloced, "Alloc default with managed alloc'd"); + if( !FAILED( fD3DDevice->CreateTexture( owner->GetWidth(), owner->GetHeight(), 1, D3DUSAGE_RENDERTARGET, surfFormat, + D3DPOOL_DEFAULT, (IDirect3DTexture9 **)&texture, NULL ) ) ) + { + D3DSURF_MEMNEW(texture); + + ref->SetTexture( texture, depthSurface ); + } + else + { + ReleaseObject(depthSurface); + hsRefCnt_SafeUnRef(ref); + ref = nil; + } + } + // Pretty sure this code path has never been followed. + else if( owner->GetFlags() & plRenderTarget::kIsOffscreen ) + { + /// Create a blank surface + if( ref != nil ) + ref->Set( surfFormat, 0, owner ); + else + ref = TRACKED_NEW plDXRenderTargetRef( surfFormat, 0, owner ); + + width = owner->GetWidth(); + height = owner->GetHeight(); + + if( !FAILED( fD3DDevice->CreateRenderTarget( width, height, surfFormat, + D3DMULTISAMPLE_NONE, 0, + FALSE, &surface, NULL ) ) ) + { + D3DSURF_MEMNEW(surface); + + ref->SetTexture( surface, depthSurface ); + } + else + { + ReleaseObject(depthSurface); + hsRefCnt_SafeUnRef(ref); + ref = nil; + } + + } + + if( owner->GetDeviceRef() != ref ) + { + owner->SetDeviceRef( ref ); + // Unref now, since for now ONLY the RT owns the ref, not us (not until we use it, at least) + hsRefCnt_SafeUnRef( ref ); + if( ref != nil && !ref->IsLinked() ) + ref->Link( &fRenderTargetRefList ); + } + else + { + if( ref != nil && !ref->IsLinked() ) + ref->Link( &fRenderTargetRefList ); + } + + if( ref != nil ) + { + ref->SetDirty( false ); + } + + return ref; +} + +//// IPrepRenderTargetInfo //////////////////////////////////////////////////// +// Shared processing of render target creation parameters. Also does the +// dirty work of finding a good surface format to use. +hsBool plDXPipeline::IPrepRenderTargetInfo( plRenderTarget *owner, D3DFORMAT &surfFormat, + D3DFORMAT &depthFormat, D3DRESOURCETYPE &resType ) +{ + int i, j; + UInt16 flags, width, height; + Int8 bitDepth, zDepth, stencilDepth, stencilIndex; + D3DFORMAT depthFormats[] = { D3DFMT_D24X8, D3DFMT_D24X4S4, D3DFMT_D24S8 }; + + + flags = owner->GetFlags(); + width = owner->GetWidth(); + height = owner->GetHeight(); + bitDepth = owner->GetPixelSize(); + zDepth = owner->GetZDepth(); + stencilDepth = owner->GetStencilDepth(); + + if( flags != 0 ) + { + if( flags & plRenderTarget::kIsTexture ) + { + /// Do an extra check for width and height here + for( i = width >> 1, j = 0; i != 0; i >>= 1, j++ ); + if( width != ( 1 << j ) ) + return false; + + for( i = height >> 1, j = 0; i != 0; i >>= 1, j++ ); + if( height!= ( 1 << j ) ) + return false; + + resType = D3DRTYPE_TEXTURE; + } + else + resType = D3DRTYPE_SURFACE; + + if( bitDepth == 16 ) + surfFormat = D3DFMT_A4R4G4B4; + else if( bitDepth == 32 ) + surfFormat = D3DFMT_A8R8G8B8; + + /// Get the backbuffer format (if one is requested) + if( zDepth ) + { + if( zDepth == 16 && stencilDepth == 0 ) + depthFormat = D3DFMT_D16; + else if( zDepth == 24 ) + { + if( stencilDepth == 0 ) stencilIndex = 0; + else if( stencilDepth <= 4 ) stencilIndex = 1; + else if( stencilDepth <= 8 ) stencilIndex = 2; + else + stencilIndex = 2; + + depthFormat = depthFormats[ stencilIndex ]; + } + else if( zDepth == 32 && stencilDepth == 0 ) + depthFormat = D3DFMT_D32; + else if( zDepth == 15 && stencilDepth == 1 ) + depthFormat = D3DFMT_D15S1; + + if( surfFormat == D3DFMT_UNKNOWN || depthFormat == D3DFMT_UNKNOWN ) + { + return false; + } + } + else + { + depthFormat = D3DFMT_UNKNOWN; + } + + /// Check the device format + if( FAILED( fSettings.fDXError = fD3DObject->CheckDeviceFormat( fCurrentAdapter, fCurrentDevice->fDDType, fCurrentMode->fDDmode.Format, + D3DUSAGE_RENDERTARGET, resType, surfFormat ) ) ) + { + if( bitDepth == 16 ) + { + bitDepth = 32; + surfFormat = D3DFMT_A8R8G8B8; + } + else if( bitDepth == 32 ) + { + bitDepth = 16; + surfFormat = D3DFMT_A4R4G4B4; + } + if( FAILED( fSettings.fDXError = fD3DObject->CheckDeviceFormat( fCurrentAdapter, fCurrentDevice->fDDType, fCurrentMode->fDDmode.Format, + D3DUSAGE_RENDERTARGET, resType, surfFormat ) ) ) + { + IGetD3DError(); + return false; + } + } + + if( zDepth ) + { + while( FAILED( fSettings.fDXError = fD3DObject->CheckDeviceFormat( fCurrentAdapter, fCurrentDevice->fDDType, fCurrentMode->fDDmode.Format, + D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, depthFormat ) ) ) + { + if( stencilIndex < sizeof( depthFormats ) / sizeof( depthFormats[ 0 ] ) - 1 ) + { + stencilIndex++; + depthFormat = depthFormats[ stencilIndex ]; + } + else + { + IGetD3DError(); + return false; + } + } + + if( FAILED( fSettings.fDXError = fD3DObject->CheckDepthStencilMatch( fCurrentAdapter, fCurrentDevice->fDDType, fCurrentMode->fDDmode.Format, + surfFormat, depthFormat ) ) ) + { + IGetD3DError(); + return false; + } + } + } + + return true; +} + +//// IFindRenderTargetInfo //////////////////////////////////////////////////// +// Shared processing of render target creation parameters. Also does the +// dirty work of finding a good surface format to use. +// Doesn't bother checking depth buffer, since this is only used for a render target +// that's going to share a depth buffer that's already been created. +hsBool plDXPipeline::IFindRenderTargetInfo( plRenderTarget *owner, D3DFORMAT &surfFormat, D3DRESOURCETYPE &resType ) +{ + UInt16 flags, width, height; + Int8 bitDepth; + + + flags = owner->GetFlags(); + width = owner->GetWidth(); + height = owner->GetHeight(); + bitDepth = owner->GetPixelSize(); + + if( flags != 0 ) + { + if( flags & plRenderTarget::kIsTexture ) + { + resType = D3DRTYPE_TEXTURE; + } + else + resType = D3DRTYPE_SURFACE; + + if( bitDepth == 16 ) + surfFormat = D3DFMT_A4R4G4B4; + else if( bitDepth == 32 ) + surfFormat = D3DFMT_A8R8G8B8; + + if( surfFormat == D3DFMT_UNKNOWN ) + { + return false; + } + + /// Check the device format + if( FAILED( fSettings.fDXError = fD3DObject->CheckDeviceFormat( fCurrentAdapter, fCurrentDevice->fDDType, fCurrentMode->fDDmode.Format, + D3DUSAGE_RENDERTARGET, resType, surfFormat ) ) ) + { + if( bitDepth == 16 ) + { + bitDepth = 32; + surfFormat = D3DFMT_A8R8G8B8; + } + else if( bitDepth == 32 ) + { + bitDepth = 16; + surfFormat = D3DFMT_A4R4G4B4; + } + if( FAILED( fSettings.fDXError = fD3DObject->CheckDeviceFormat( fCurrentAdapter, fCurrentDevice->fDDType, fCurrentMode->fDDmode.Format, + D3DUSAGE_RENDERTARGET, resType, surfFormat ) ) ) + { + IGetD3DError(); + return false; + } + } + } + + return true; +} + +// PushRenderRequest /////////////////////////////////////////////// +// We're moving from our current render (probably to primary) onto +// another specialized render request. This may be to the primary (if req->GetRenderTarget() is nil) +// or to a texture. This function saves enough state to resume rendering on PopRenderRequest. +// The render request may just be a new camera position. +void plDXPipeline::PushRenderRequest(plRenderRequest* req) +{ + // Save these, since we want to copy them to our current view + hsMatrix44 l2w = fView.fLocalToWorld; + hsMatrix44 w2l = fView.fWorldToLocal; + + plFogEnvironment defFog = fView.fDefaultFog; + + fSettings.fViewStack.Push(fView); + + SetViewTransform(req->GetViewTransform()); + + PushRenderTarget(req->GetRenderTarget()); + fView.fRenderState = req->GetRenderState(); + + fView.fRenderRequest = req; + hsRefCnt_SafeRef(fView.fRenderRequest); + + SetDrawableTypeMask(req->GetDrawableMask()); + SetSubDrawableTypeMask(req->GetSubDrawableMask()); + + fView.fClearColor = inlGetD3DColor( req->GetClearColor() ); + fView.fClearDepth = req->GetClearDepth(); + + if( req->GetFogStart() < 0 ) + { + fView.fDefaultFog = defFog; + } + else + { + fView.fDefaultFog.Set( req->GetYon() * (1.f - req->GetFogStart()), req->GetYon(), 1.f, &req->GetClearColor()); + fCurrFog.fEnvPtr = nil; + } + + if( req->GetOverrideMat() ) + PushOverrideMaterial(req->GetOverrideMat()); + + // Set from our saved ones... + fView.fWorldToLocal = w2l; + fView.fLocalToWorld = l2w; + + RefreshMatrices(); + + if (req->GetIgnoreOccluders()) + fView.fCullMaxNodes = 0; + + fView.fCullTreeDirty = true; +} + +// PopRenderRequest ////////////////////////////////////////////////// +// Restore state to resume rendering as before the preceding PushRenderRequest. +void plDXPipeline::PopRenderRequest(plRenderRequest* req) +{ + if( req->GetOverrideMat() ) + PopOverrideMaterial(nil); + + hsRefCnt_SafeUnRef(fView.fRenderRequest); + fView = fSettings.fViewStack.Pop(); + + // Force the next thing drawn to update the fog settings. + fD3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + fCurrFog.fEnvPtr = nil; + + PopRenderTarget(); + fView.fXformResetFlags = fView.kResetProjection | fView.kResetCamera; +} + +//// PushRenderTarget ///////////////////////////////////////////////////////// +// Begin rendering to the specified target. If target is nil, that's the primary surface. +void plDXPipeline::PushRenderTarget( plRenderTarget *target ) +{ + //WHITE +#ifdef MF_ENABLE_HACKOFF + if( target && (hackOffscreens.kMissingIndex == hackOffscreens.Find(target)) ) + hackOffscreens.Append(target); +#endif // MF_ENABLE_HACKOFF + + + fSettings.fCurrRenderTarget = target; + hsRefCnt_SafeAssign( fSettings.fCurrRenderTargetRef, ( target != nil ) ? (plDXDeviceRef *)target->GetDeviceRef() : nil ); + + while( target != nil ) + { + fSettings.fCurrBaseRenderTarget = target; + target = target->GetParent(); + } + + fSettings.fRenderTargets.Push( fSettings.fCurrRenderTarget ); + ISetRenderTarget( fSettings.fCurrRenderTarget ); +} + +//// PopRenderTarget ////////////////////////////////////////////////////////// +// Resume rendering to the render target before the last PushRenderTarget, +// making sure we aren't holding on to anything from the render target getting +// popped. +plRenderTarget *plDXPipeline::PopRenderTarget() +{ + plRenderTarget *old = fSettings.fRenderTargets.Pop(), *temp; + int i = fSettings.fRenderTargets.GetCount(); + + if( i == 0 ) + { + fSettings.fCurrRenderTarget = nil; + fSettings.fCurrBaseRenderTarget = nil; + hsRefCnt_SafeUnRef( fSettings.fCurrRenderTargetRef ); + fSettings.fCurrRenderTargetRef = nil; + } + else + { + fSettings.fCurrRenderTarget = fSettings.fRenderTargets[ i - 1 ]; + temp = fSettings.fCurrRenderTarget; + while( temp != nil ) + { + fSettings.fCurrBaseRenderTarget = temp; + temp = temp->GetParent(); + } + hsRefCnt_SafeAssign( fSettings.fCurrRenderTargetRef, + ( fSettings.fCurrRenderTarget != nil ) ? + (plDXDeviceRef *)fSettings.fCurrRenderTarget->GetDeviceRef() + : nil ); + } + + ISetRenderTarget( fSettings.fCurrRenderTarget ); + + return old; +} + +// ISetAnisotropy /////////////////////////////////////////////////////////// +// Set the current anisotropic filtering settings to D3D +void plDXPipeline::ISetAnisotropy(hsBool on) +{ + if( (fSettings.fMaxAnisotropicSamples <= 0) || IsDebugFlagSet(plPipeDbg::kFlagNoAnisotropy) ) + on = false; + + if( on == fSettings.fCurrAnisotropy ) + return; + + if( on ) + { + int i; + for( i = 0; i < 8; i++ ) + { + // GeForce cards have decided that they no longer handle anisotropic as a mag filter. + // We could detect caps... but I don't think we'd notice if we just made the mag + // filter always be linear. + fD3DDevice->SetSamplerState( i, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC ); + fD3DDevice->SetSamplerState( i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); + fD3DDevice->SetSamplerState( i, D3DSAMP_MAXANISOTROPY, (DWORD)fSettings.fMaxAnisotropicSamples ); + } + fSettings.fCurrAnisotropy = true; + } + else + { + int i; + for( i = 0; i < 8; i++ ) + { + fD3DDevice->SetSamplerState( i, D3DSAMP_MINFILTER, D3DTEXF_LINEAR ); + fD3DDevice->SetSamplerState( i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR ); + } + fSettings.fCurrAnisotropy = false; + } +} + +//// ISetRenderTarget ///////////////////////////////////////////////////////// +// Set rendering to the specified render target. Nil rendertarget is the primary. +// Invalidates the state as required by experience, not documentation. +void plDXPipeline::ISetRenderTarget( plRenderTarget *target ) +{ + IDirect3DSurface9 *main, *depth; + plDXRenderTargetRef *ref = nil; + + + if( target != nil ) + { + ref = (plDXRenderTargetRef *)target->GetDeviceRef(); + if( ref == nil || ref->IsDirty() ) + ref = (plDXRenderTargetRef *)MakeRenderTargetRef( target ); + } + + if( ref == nil || ref->GetColorSurface() == nil ) + { + /// Set to main screen + main = fD3DMainSurface; + depth = fD3DDepthSurface; + ISetAnisotropy(true); + } + else + { + /// Set to this target + main = ref->GetColorSurface(); + depth = ref->fD3DDepthSurface; + ISetAnisotropy(false); + } + + if( main != fSettings.fCurrD3DMainSurface || depth != fSettings.fCurrD3DDepthSurface ) + { + fSettings.fCurrD3DMainSurface = main; + fSettings.fCurrD3DDepthSurface = depth; + fD3DDevice->SetRenderTarget(0, main); + fD3DDevice->SetDepthStencilSurface(depth); + } + + IInvalidateState(); + + ISetViewport(); +} + +// SetClear ///////////////////////////////////////////////////////////////////// +// Set the color and depth clear values. +void plDXPipeline::SetClear(const hsColorRGBA* col, const hsScalar* depth) +{ + if( col ) + fView.fClearColor = inlGetD3DColor(*col); + if( depth ) + fView.fClearDepth = *depth; +} + +// GetClearColor //////////////////////////////////////////////////////////////// +// Return the current clear color. +hsColorRGBA plDXPipeline::GetClearColor() const +{ + return hsColorRGBA().FromARGB32(fView.fClearColor); +} + +// GetClearDepth //////////////////////////////////////////////////////////////// +// Return the current clear depth. +hsScalar plDXPipeline::GetClearDepth() const +{ + return fView.fClearDepth; +} + +//// ClearRenderTarget //////////////////////////////////////////////////////// +// Clear the current color and depth buffers. If a drawable is passed in, then +// the color buffer will be cleared by rendering that drawable. +// The depth buffer is always cleared with a clear call. +// Clearing of depth and/or color may be turned off by setting the kRenderClearDepth +// and kRenderClearColor bits in fView.fRenderState to false. +void plDXPipeline::ClearRenderTarget( plDrawable* d ) +{ + plDrawableSpans* src = plDrawableSpans::ConvertNoRef(d); + + if( !src ) + { + ClearRenderTarget(); + return; + } + // First clear the depth buffer as normal. + if( fView.fRenderState & kRenderClearDepth ) + { + D3DRECT r; + hsBool useRect = IGetClearViewPort(r); + + if( useRect ) + { + WEAK_ERROR_CHECK( fD3DDevice->Clear( 1, &r, D3DCLEAR_ZBUFFER, 0, fView.fClearDepth, 0L ) ); + } + else + { + WEAK_ERROR_CHECK( fD3DDevice->Clear( 0, nil, D3DCLEAR_ZBUFFER, 0, fView.fClearDepth, 0L ) ); +// debug, clears to red WEAK_ERROR_CHECK( fD3DDevice->Clear( 0, nil, D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET, 0xffff0000, fView.fClearDepth, 0L ) ); + } + } + + UInt32 s = fView.fRenderState; + UInt32 dtm = fView.fDrawableTypeMask; + UInt32 sdtm = fView.fSubDrawableTypeMask; + + fView.fDrawableTypeMask = plDrawable::kNormal; + fView.fSubDrawableTypeMask = UInt32(-1); + + BeginDrawable(d); + Draw(d); + EndDrawable(d); + + fView.fSubDrawableTypeMask = sdtm; + fView.fDrawableTypeMask = dtm; + fView.fRenderState = s; + +} + +// IGetClearViewPort ////////////////////////////////////////////// +// Sets the input rect to the current viewport. Returns true if +// that is a subset of the current render target, else false. +hsBool plDXPipeline::IGetClearViewPort(D3DRECT& r) +{ + r.x1 = GetViewTransform().GetViewPortLeft(); + r.y1 = GetViewTransform().GetViewPortTop(); + r.x2 = GetViewTransform().GetViewPortRight(); + r.y2 = GetViewTransform().GetViewPortBottom(); + + hsBool useRect = false; + if( fSettings.fCurrRenderTarget != nil ) + { + useRect = ( (r.x1 != 0) || (r.y1 != 0) || (r.x2 != fSettings.fCurrRenderTarget->GetWidth()) || (r.y2 != fSettings.fCurrRenderTarget->GetHeight()) ); + + } + else + { + useRect = ( (r.x1 != 0) || (r.y1 != 0) || (r.x2 != fSettings.fOrigWidth) || (r.y2 != fSettings.fOrigHeight) ); + } + + return useRect; +} + +// ClearRenderTarget ////////////////////////////////////////////////////////////////////////////// +// Flat fill the current render target with the specified color and depth values. +void plDXPipeline::ClearRenderTarget( const hsColorRGBA *col, const hsScalar* depth ) +{ + if( fView.fRenderState & (kRenderClearColor | kRenderClearDepth) ) + { + DWORD clearColor = inlGetD3DColor(col ? *col : GetClearColor()); + hsScalar clearDepth = depth ? *depth : fView.fClearDepth; + + DWORD dwFlags = 0;//fStencil.fDepth > 0 ? D3DCLEAR_STENCIL : 0; + if( fView.fRenderState & kRenderClearColor ) + dwFlags |= D3DCLEAR_TARGET; + if( fView.fRenderState & kRenderClearDepth ) + dwFlags |= D3DCLEAR_ZBUFFER; + + D3DRECT r; + hsBool useRect = IGetClearViewPort(r); + if( useRect ) + { + WEAK_ERROR_CHECK( fD3DDevice->Clear( 1, &r, dwFlags, clearColor, clearDepth, 0L ) ); + } + else + { + WEAK_ERROR_CHECK( fD3DDevice->Clear( 0, nil, dwFlags, clearColor, clearDepth, 0L ) ); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// +//// Fog ////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// The current fog system sucks. It was never meant to get used this way, but +// the production artists started using it with debug commands that were around, +// and before they could be stopped it was too late. +// The good news is that there's a lot that could be done with fog here that +// would be greatly appreciated. + +// IGetVSFogSet /////////////////////////////////////////////////////////////// +// Translate the current fog settings into a linear fog that the current +// vertex shaders can use. +void plDXPipeline::IGetVSFogSet(float* const set) const +{ + set[2] = 0.f; + set[3] = 1.f; + if( fCurrFog.fEnvPtr ) + { + hsColorRGBA colorTrash; + hsScalar start; + hsScalar end; + fCurrFog.fEnvPtr->GetPipelineParams(&start, &end, &colorTrash); + if( end > start ) + { + set[0] = -end; + set[1] = 1.f / (start - end); + } + else + { + set[0] = 1.f; + set[1] = 0.f; + } + } + else + { + set[0] = 1.f; + set[1] = 0.f; + } +} + +//// ISetFogParameters //////////////////////////////////////////////////////// +// So looking at this function, one might guess that fog parameters were settable +// individually for different objects, and that was the original intent, with transitions +// as something like the avatar moved from one fog region to another. +// Never happened. +// So the current state is that there is one set of fog parameters per age, and things +// are either fogged, or not fogged. +// This is complicated by the DX vertex/pixel shaders only supporting per-vertex fog, +// so the same plasma fog settings may turn into differing D3D fog state. +void plDXPipeline::ISetFogParameters(const plSpan* span, const plLayerInterface* baseLay) +{ +#ifndef PLASMA_EXTERNAL_RELEASE + if (IsDebugFlagSet(plPipeDbg::kFlagNoFog)) + { + fCurrFog.fEnvPtr = nil; + fD3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + return; + } +#endif // PLASMA_EXTERNAL_RELEASE + + plFogEnvironment* fog = (span ? (span->fFogEnvironment ? span->fFogEnvironment : &fView.fDefaultFog) : nil); + + UInt8 isVertex = 0; + UInt8 isShader = false; + if (baseLay) + { + if ((baseLay->GetShadeFlags() & hsGMatState::kShadeReallyNoFog) && !(fMatOverOff.fShadeFlags & hsGMatState::kShadeReallyNoFog)) + fog = nil; + if (baseLay->GetVertexShader()) + isShader = true; + } + if (fMatOverOn.fShadeFlags & hsGMatState::kShadeReallyNoFog) + fog = nil; + + bool forceLoad = false; + D3DRENDERSTATETYPE d3dFogType = D3DRS_FOGTABLEMODE; // Use VERTEXMODE for vertex fog + +#if !HS_BUILD_FOR_XBOX + if (!(fSettings.fD3DCaps & kCapsPixelFog) || isShader) + { + d3dFogType = D3DRS_FOGVERTEXMODE; + isVertex = true; + } +#endif + + // Quick check + if ((fCurrFog.fEnvPtr == fog) && (fCurrFog.fIsVertex == isVertex) && (fCurrFog.fIsShader == isShader)) + return; + + UInt8 type = ( fog == nil ) ? plFogEnvironment::kNoFog : fog->GetType(); + + if (type == plFogEnvironment::kNoFog) + { + /// No fog, just disable + fD3DDevice->SetRenderState( D3DRS_FOGENABLE, FALSE ); + fCurrFog.fEnvPtr = nil; + return; + } + else if( fCurrFog.fEnvPtr != fog ) + { + fD3DDevice->SetRenderState( D3DRS_FOGENABLE, TRUE ); + forceLoad = true; + fCurrFog.fEnvPtr = fog; + } + + if( isShader ) + type = plFogEnvironment::kLinearFog; + + if( fCurrFog.fIsShader != isShader ) + forceLoad = true; + + if( fCurrFog.fIsVertex != isVertex ) + forceLoad = true; + + fCurrFog.fIsShader = isShader; + fCurrFog.fIsVertex = isVertex; + + hsScalar startOrDensity, end; + hsColorRGBA color; + + /// Get params + if( type == plFogEnvironment::kLinearFog ) + { + fog->GetPipelineParams( &startOrDensity, &end, &color ); + + if (startOrDensity == end) + { + // This should be legal, but some cards don't like it. Just disable. Same thing. + fD3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + return; + } + } + else + fog->GetPipelineParams( &startOrDensity, &color ); + + if( isShader ) + { + // None of this is technically necessary, but it's to work around + // a known goofiness in the NVidia drivers. Actually, I don't think + // having to set the tablemode fog to linear in addition to setting + // the vertexmode is even a "known" issue. But turns out to be + // necessary on GeForceFX latest drivers. + startOrDensity = 1.f; + end = 0.f; + + // Setting FOGTABLEMODE to none seems to work on both ATI and NVidia, + // but I haven't tried it on the GeForceFX yet. + // if( fCurrFog.fMode != D3DFOG_LINEAR ) + // fD3DDevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_LINEAR); + fD3DDevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_NONE); + } + + /// Set color + if( !( fCurrFog.fColor == color ) || forceLoad ) + { + fCurrFog.fColor = color; + fCurrFog.fHexColor = inlGetD3DColor( color ); + fD3DDevice->SetRenderState( D3DRS_FOGCOLOR, fCurrFog.fHexColor ); + } + + D3DFOGMODE modes[ 4 ] = { D3DFOG_LINEAR, D3DFOG_EXP, D3DFOG_EXP2, D3DFOG_NONE }; + + /// Set type + if( fCurrFog.fMode != modes[type] || forceLoad ) + { + fCurrFog.fMode = modes[type]; + + if( fCurrFog.fMode == D3DFOG_LINEAR ) + { + fCurrFog.fStart = startOrDensity; + fCurrFog.fEnd = end; + + fD3DDevice->SetRenderState( d3dFogType, fCurrFog.fMode ); + fD3DDevice->SetRenderState( D3DRS_FOGSTART, *(DWORD *)( &fCurrFog.fStart ) ); + fD3DDevice->SetRenderState( D3DRS_FOGEND, *(DWORD *)( &fCurrFog.fEnd ) ); + } + else + { + fCurrFog.fDensity = startOrDensity; + + fD3DDevice->SetRenderState( d3dFogType, fCurrFog.fMode ); + fD3DDevice->SetRenderState( D3DRS_FOGDENSITY, *(DWORD *)( &fCurrFog.fDensity ) ); + } + } + else + { + // Type is the same, but are the params? + if( fCurrFog.fMode == D3DFOG_LINEAR ) + { + if( fCurrFog.fStart != startOrDensity ) + { + fCurrFog.fStart = startOrDensity; + fD3DDevice->SetRenderState( D3DRS_FOGSTART, *(DWORD *)( &fCurrFog.fStart ) ); + } + + if( fCurrFog.fEnd != end ) + { + fCurrFog.fEnd = end; + fD3DDevice->SetRenderState( D3DRS_FOGEND, *(DWORD *)( &fCurrFog.fEnd ) ); + } + } + else + { + if( fCurrFog.fDensity != startOrDensity ) + { + fCurrFog.fDensity = startOrDensity; + fD3DDevice->SetRenderState( D3DRS_FOGDENSITY, *(DWORD *)( &fCurrFog.fDensity ) ); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +//// Stenciling /////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// I know that none of this stencil code has ever been used in production. +// To my knowledge, none of this stencil code was ever even tested. +// It may save you some time as a starting point, but don't trust it. + +//// StencilEnable //////////////////////////////////////////////////////////// + +hsBool plDXPipeline::StencilEnable( hsBool enable ) +{ + if( fStencil.fEnabled == enable ) + return true; + + if( enable && fStencil.fDepth == 0 ) + return false; // Can't enable stenciling when we don't support it! + + fD3DDevice->SetRenderState( D3DRS_STENCILENABLE, enable ? TRUE : FALSE ); + + return true; +} + +//// StencilSetCompareFunc //////////////////////////////////////////////////// + +void plDXPipeline::StencilSetCompareFunc( UInt8 func, UInt32 refValue ) +{ + D3DCMPFUNC newFunc; + + + switch( func ) + { + case plStencilCaps::kCmpNever: newFunc = D3DCMP_NEVER; break; + case plStencilCaps::kCmpLessThan: newFunc = D3DCMP_LESS; break; + case plStencilCaps::kCmpEqual: newFunc = D3DCMP_EQUAL; break; + case plStencilCaps::kCmpLessThanOrEqual: newFunc = D3DCMP_LESSEQUAL; break; + case plStencilCaps::kCmpGreaterThan: newFunc = D3DCMP_GREATER; break; + case plStencilCaps::kCmpNotEqual: newFunc = D3DCMP_NOTEQUAL; break; + case plStencilCaps::kCmpGreaterThanOrEqual: newFunc = D3DCMP_GREATEREQUAL; break; + case plStencilCaps::kCmpAlways: newFunc = D3DCMP_ALWAYS; break; + default: hsAssert( false, "Invalid compare function to StencilSetCompareFunc()" ); return; + } + + if( fStencil.fCmpFunc != newFunc ) + { + fD3DDevice->SetRenderState( D3DRS_STENCILFUNC, newFunc ); + fStencil.fCmpFunc = newFunc; + } + + if( fStencil.fRefValue != refValue ) + { + fD3DDevice->SetRenderState( D3DRS_STENCILREF, refValue ); + fStencil.fRefValue = refValue; + } +} + +//// StencilSetMask /////////////////////////////////////////////////////////// + +void plDXPipeline::StencilSetMask( UInt32 mask, UInt32 writeMask ) +{ + if( fStencil.fMask != mask ) + { + fD3DDevice->SetRenderState( D3DRS_STENCILMASK, mask ); + fStencil.fMask = mask; + } + + if( fStencil.fWriteMask != writeMask ) + { + fD3DDevice->SetRenderState( D3DRS_STENCILWRITEMASK, writeMask ); + fStencil.fWriteMask = writeMask; + } +} + +//// StencilSetOps //////////////////////////////////////////////////////////// + +void plDXPipeline::StencilSetOps( UInt8 passOp, UInt8 failOp, UInt8 passButZFailOp ) +{ + D3DSTENCILOP op; + + + /// Pass op + switch( passOp ) + { + case plStencilCaps::kOpKeep: op = D3DSTENCILOP_KEEP; break; + case plStencilCaps::kOpSetToZero: op = D3DSTENCILOP_ZERO; break; + case plStencilCaps::kOpReplace: op = D3DSTENCILOP_REPLACE; break; + case plStencilCaps::kOpIncClamp: op = D3DSTENCILOP_INCRSAT; break; + case plStencilCaps::kOpDecClamp: op = D3DSTENCILOP_DECRSAT; break; + case plStencilCaps::kOpInvert: op = D3DSTENCILOP_INVERT; break; + case plStencilCaps::kOpIncWrap: op = D3DSTENCILOP_INCR; break; + case plStencilCaps::kOpDecWrap: op = D3DSTENCILOP_DECR; break; + default: hsAssert( false, "Invalid op to StencilSetOps()" ); return; + } + + if( fStencil.fPassOp != op ) + { + fD3DDevice->SetRenderState( D3DRS_STENCILPASS, op ); + fStencil.fPassOp = op; + } + + /// Fail op + switch( failOp ) + { + case plStencilCaps::kOpKeep: op = D3DSTENCILOP_KEEP; break; + case plStencilCaps::kOpSetToZero: op = D3DSTENCILOP_ZERO; break; + case plStencilCaps::kOpReplace: op = D3DSTENCILOP_REPLACE; break; + case plStencilCaps::kOpIncClamp: op = D3DSTENCILOP_INCRSAT; break; + case plStencilCaps::kOpDecClamp: op = D3DSTENCILOP_DECRSAT; break; + case plStencilCaps::kOpInvert: op = D3DSTENCILOP_INVERT; break; + case plStencilCaps::kOpIncWrap: op = D3DSTENCILOP_INCR; break; + case plStencilCaps::kOpDecWrap: op = D3DSTENCILOP_DECR; break; + default: hsAssert( false, "Invalid op to StencilSetOps()" ); return; + } + + if( fStencil.fFailOp != op ) + { + fD3DDevice->SetRenderState( D3DRS_STENCILFAIL, op ); + fStencil.fFailOp = op; + } + + /// Pass-but-z-fail op + switch( passButZFailOp ) + { + case plStencilCaps::kOpKeep: op = D3DSTENCILOP_KEEP; break; + case plStencilCaps::kOpSetToZero: op = D3DSTENCILOP_ZERO; break; + case plStencilCaps::kOpReplace: op = D3DSTENCILOP_REPLACE; break; + case plStencilCaps::kOpIncClamp: op = D3DSTENCILOP_INCRSAT; break; + case plStencilCaps::kOpDecClamp: op = D3DSTENCILOP_DECRSAT; break; + case plStencilCaps::kOpInvert: op = D3DSTENCILOP_INVERT; break; + case plStencilCaps::kOpIncWrap: op = D3DSTENCILOP_INCR; break; + case plStencilCaps::kOpDecWrap: op = D3DSTENCILOP_DECR; break; + default: hsAssert( false, "Invalid op to StencilSetOps()" ); return; + } + + if( fStencil.fPassButZFailOp != op ) + { + fD3DDevice->SetRenderState( D3DRS_STENCILZFAIL, op ); + fStencil.fPassButZFailOp = op; + } +} + +//// StencilGetCaps /////////////////////////////////////////////////////////// + +hsBool plDXPipeline::StencilGetCaps( plStencilCaps *caps ) +{ + hsAssert( caps != nil, "Invalid pointer to StencilGetCaps()" ); + + int i; + + + /// Find supported depths + caps->fSupportedDepths = 0; + for( i = 0; i < fCurrentMode->fDepthFormats.GetCount(); i++ ) + { + switch( fCurrentMode->fDepthFormats[ i ] ) + { + case D3DFMT_D15S1: caps->fSupportedDepths |= plStencilCaps::kDepth1Bit; break; + case D3DFMT_D24X4S4: caps->fSupportedDepths |= plStencilCaps::kDepth4Bits; break; + case D3DFMT_D24S8: caps->fSupportedDepths |= plStencilCaps::kDepth8Bits; break; + } + } + + if( caps->fSupportedDepths == 0 ) + { + caps->fIsSupported = false; + return false; + } + + /// Get supported ops + caps->fSupportedOps = 0; + + if( fCurrentDevice->fDDCaps.StencilCaps & D3DSTENCILCAPS_DECR ) + caps->fSupportedOps |= plStencilCaps::kOpDecWrap; + if( fCurrentDevice->fDDCaps.StencilCaps & D3DSTENCILCAPS_DECRSAT ) + caps->fSupportedOps |= plStencilCaps::kOpDecClamp; + if( fCurrentDevice->fDDCaps.StencilCaps & D3DSTENCILCAPS_INCR ) + caps->fSupportedOps |= plStencilCaps::kOpIncWrap; + if( fCurrentDevice->fDDCaps.StencilCaps & D3DSTENCILCAPS_INCRSAT ) + caps->fSupportedOps |= plStencilCaps::kOpIncClamp; + + if( fCurrentDevice->fDDCaps.StencilCaps & D3DSTENCILCAPS_INVERT ) + caps->fSupportedOps |= plStencilCaps::kOpInvert; + if( fCurrentDevice->fDDCaps.StencilCaps & D3DSTENCILCAPS_KEEP ) + caps->fSupportedOps |= plStencilCaps::kOpKeep; + if( fCurrentDevice->fDDCaps.StencilCaps & D3DSTENCILCAPS_REPLACE ) + caps->fSupportedOps |= plStencilCaps::kOpReplace; + if( fCurrentDevice->fDDCaps.StencilCaps & D3DSTENCILCAPS_ZERO ) + caps->fSupportedOps |= plStencilCaps::kOpSetToZero; + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////// +//// Lighting ///////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// IMakeLightRef //////////////////////////////////////////////////////////// +// Create a plasma device ref for a light. Includes reserving a D3D light +// index for the light. Ref is kept in a linked list for ready disposal +// as well as attached to the light. +hsGDeviceRef *plDXPipeline::IMakeLightRef( plLightInfo *owner ) +{ + plDXLightRef *lRef = TRACKED_NEW plDXLightRef(); + + + /// Assign stuff and update + lRef->fD3DIndex = fLights.ReserveD3DIndex(); + lRef->fOwner = owner; + owner->SetDeviceRef( lRef ); + // Unref now, since for now ONLY the BG owns the ref, not us (not until we use it, at least) + hsRefCnt_SafeUnRef( lRef ); + + lRef->Link( &fLights.fRefList ); + + lRef->UpdateD3DInfo( fD3DDevice, &fLights ); + + // Neutralize it until we need it. + fD3DDevice->LightEnable(lRef->fD3DIndex, false); + + return lRef; +} + +//// RegisterLight //////////////////////////////////////////////////////////// +// Register a light with the pipeline. Light become immediately +// ready to illuminate the scene. +void plDXPipeline::RegisterLight(plLightInfo* liInfo) +{ + if( liInfo->IsLinked() ) + return; + + liInfo->Link( &fLights.fActiveList ); + liInfo->SetDeviceRef( IMakeLightRef( liInfo ) ); + fLights.fTime++; +} + +//// UnRegisterLight ////////////////////////////////////////////////////////// +// Remove a light from the pipeline's active light list. Light will +// no longer illuminate the scene. +void plDXPipeline::UnRegisterLight(plLightInfo* liInfo) +{ + liInfo->SetDeviceRef( nil ); + liInfo->Unlink(); + + fLights.fTime++; +} + +//// IEnableLights //////////////////////////////////////////////////////////// +// Does the lighting enable pass. Given a span with lights to use, builds +// a bit vector representing the lights to use, then uses that to mask off +// which lights actually need to be enabled/disabled. +// Constructs 2 lists on the span, one for normal lights, and one for projective lights. + +void plDXPipeline::IEnableLights( plSpan *span ) +{ + plProfile_BeginTiming(SelectLights); + ISelectLights( span, fSettings.fMaxNumLights, false ); + plProfile_EndTiming(SelectLights); + if( !(fView.fRenderState & kRenderNoProjection) ) + { + plProfile_BeginTiming(SelectProj); + ISelectLights( span, fSettings.fMaxNumProjectors, true ); + plProfile_EndTiming(SelectProj); + } +} + +// ISelectLights /////////////////////////////////////////////////////////////// +// Find the strongest numLights lights to illuminate the span with. +// Weaker lights are faded out in effect so they won't pop when the +// strongest N changes membership. +void plDXPipeline::ISelectLights( plSpan *span, int numLights, hsBool proj ) +{ + int i, startScale; + static hsBitVector newFlags; + static hsTArray onLights; + plDXLightRef *ref; + float threshhold, overHold = 0.3, scale; + + /// Build new flags + + /// Step 1: Find the n strongest lights + newFlags.Clear(); + onLights.SetCount(0); + + if (!IsDebugFlagSet(plPipeDbg::kFlagNoRuntimeLights) && + !(IsDebugFlagSet(plPipeDbg::kFlagNoApplyProjLights) && proj) && + !(IsDebugFlagSet(plPipeDbg::kFlagOnlyApplyProjLights) && !proj)) + { + hsTArray& spanLights = span->GetLightList(proj); + + for( i = 0; i < spanLights.GetCount() && i < numLights; i++ ) + { + ref = (plDXLightRef *)spanLights[i]->GetDeviceRef(); + + if( ref->IsDirty() ) + { + if( ref->fD3DIndex == 0 ) + ref->fD3DIndex = fLights.ReserveD3DIndex(); + ref->UpdateD3DInfo( fD3DDevice, &fLights ); + ref->SetDirty( false ); + } + + newFlags.SetBit( ref->fD3DIndex ); + onLights.Append(spanLights[i]); + } + startScale = i; + + /// Attempt #2: Take some of the n strongest lights (below a given threshhold) and + /// fade them out to nothing as they get closer to the bottom. This way, they fade + /// out of existence instead of pop out. + + if( i < spanLights.GetCount() - 1 && i > 0 ) + { + threshhold = span->GetLightStrength( i, proj ); + i--; + overHold = threshhold * 1.5f; + if( overHold > span->GetLightStrength( 0, proj ) ) + overHold = span->GetLightStrength( 0, proj ); + + for( ; i > 0 && span->GetLightStrength( i, proj ) < overHold; i-- ) + { + scale = ( overHold - span->GetLightStrength( i, proj ) ) / ( overHold - threshhold ); + + ref = (plDXLightRef *)spanLights[i]->GetDeviceRef(); + + IScaleD3DLight( ref, (1 - scale) * span->GetLightScale(i, proj) ); + } + startScale = i + 1; + } + + /// Make sure those lights that aren't scaled....aren't + for( i = 0; i < startScale; i++ ) + { + ref = (plDXLightRef *)spanLights[i]->GetDeviceRef(); + IScaleD3DLight(ref, span->GetLightScale(i, proj) ); + } + + } + + // If these are non-projected lights, go ahead and enable them. + // For the projected lights, don't enable, just remember who they are. + if( !proj ) + { + // A little change here. Some boards get sticky about exactly + // how many lights you have enabled, whether you are currently + // rendering or not. So if we go through enabling the lights + // we want and disabling the ones we don't, then even though + // at the end of the loop, less than MaxNumLights are enabled, + // we can still wind up screwed. + // Think about if we have 8 lights enabled, and they all happen + // to be at the end of fLights. Now we want to enable a different + // 8 lights, which happen to be at the beginning of the list. + // So we loop through and enable the lights we want, and then later + // in the loop disable the lights we don't want. Problem is that + // when we were enabling the ones we want we went over our 8 light + // limit, and some boards (ATI) react by ignoring the enable request. + // So then we disable the other lights at the end of the loop, but + // it's too late because our enable requests at the beginning of the + // loop were ignored. + // Solution is to go through the list twice, first disabling, then + // enabling. mf + hsBitVector newOff = fLights.fEnabledFlags - newFlags; + hsBitIterator iterOff(newOff); + for( iterOff.Begin(); !iterOff.End(); iterOff.Advance() ) + fD3DDevice->LightEnable(iterOff.Current(), false); + + hsBitVector newOn = newFlags - fLights.fEnabledFlags; + hsBitIterator iterOn(newOn); + for( iterOn.Begin(); !iterOn.End(); iterOn.Advance() ) + fD3DDevice->LightEnable(iterOn.Current(), true); + fLights.fEnabledFlags = newFlags; + } + else + { + fLights.fProjAll.SetCount(0); + fLights.fProjEach.SetCount(0); + for( i = 0; i < onLights.GetCount(); i++ ) + { + if( onLights[i]->OverAll() ) + fLights.fProjAll.Append(onLights[i]); + else + fLights.fProjEach.Append(onLights[i]); + } + onLights.SetCount(0); + } +} + +// IDisableSpanLights ///////////////////////////////////////////////////// +// Disable all the enabled lights, remembering which they are for +// quick reenabling. +void plDXPipeline::IDisableSpanLights() +{ + int i; + for( i = 0; i < fLights.fLastIndex + 1; i++ ) + { + if( fLights.fEnabledFlags.IsBitSet(i) ) + { + fD3DDevice->LightEnable(i, false); + fLights.fHoldFlags.SetBit(i); + } + } + fLights.fEnabledFlags.Clear(); +} + +// IRestoreSpanLights ////////////////////////////////////////////////////// +// Re-enable all the lights disabled by the matching IDisableSpanLights. +void plDXPipeline::IRestoreSpanLights() +{ + int i; + for( i = 0; i < fLights.fLastIndex + 1; i++ ) + { + if( fLights.fHoldFlags.IsBitSet(i) ) + { + fD3DDevice->LightEnable(i, true); + fLights.fEnabledFlags.SetBit(i); + } + } + fLights.fHoldFlags.Clear(); +} + +//// IScaleD3DLight /////////////////////////////////////////////////////////// +// Scale the D3D light by the given scale factor, used for fading lights +// in and out by importance. +void plDXPipeline::IScaleD3DLight( plDXLightRef *ref, hsScalar scale ) +{ + scale = int(scale * 1.e1f) * 1.e-1f; + if( ref->fScale != scale ) + { + D3DLIGHT9 light = ref->fD3DInfo; + + + light.Diffuse.r *= scale; + light.Diffuse.g *= scale; + light.Diffuse.b *= scale; + + light.Ambient.r *= scale; + light.Ambient.g *= scale; + light.Ambient.b *= scale; + + light.Specular.r *= scale; + light.Specular.g *= scale; + light.Specular.b *= scale; + + fD3DDevice->SetLight( ref->fD3DIndex, &light ); + ref->fScale = scale; + } +} + +// inlPlToDWORDColor ///////////////////////////////////////////////// +// Convert a plasma floating point color to a D3D DWORD color +static inline DWORD inlPlToDWORDColor(const hsColorRGBA& c) +{ + return (DWORD(c.a * 255.99f) << 24) + | (DWORD(c.r * 255.99f) << 16) + | (DWORD(c.g * 255.99f) << 8) + | (DWORD(c.b * 255.99f) << 0); +} + +// inlPlToD3DColor //////////////////////////////////////////////////// +// Convert a plasma floating point color to a D3D floating point color. +inline D3DCOLORVALUE plDXPipeline::inlPlToD3DColor(const hsColorRGBA& c, float a) const +{ + D3DCOLORVALUE ret; + ret.r = c.r; + ret.g = c.g; + ret.b = c.b; + ret.a = a; + return ret; +} + +// inlEnsureLightingOn //////////////////////////////////////////////// +// Turn D3D lighting on if it isn't already. +inline void plDXPipeline::inlEnsureLightingOn() +{ + if( !fCurrD3DLiteState ) + { + fD3DDevice->SetRenderState( D3DRS_LIGHTING, TRUE ); + fCurrD3DLiteState = true; + } +} + +// inlEnsureLightingOff /////////////////////////////////////////////// +// Turn D3D lighting off if it isn't already. +inline void plDXPipeline::inlEnsureLightingOff() +{ + if( fCurrD3DLiteState ) + { + fD3DDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); + fCurrD3DLiteState = false; + } +} + +// ColorMul /////////////////////////////////////////////////////////// +// Multiply a D3D floating point color by a plasma floating point color, +// returning the result as a D3D floating point color. +static inline D3DCOLORVALUE ColorMul(const D3DCOLORVALUE& c0, const hsColorRGBA& c1) +{ + D3DCOLORVALUE ret; + ret.r = c0.r * c1.r; + ret.g = c0.g * c1.g; + ret.b = c0.b * c1.b; + ret.a = c0.a * c1.a; + + return ret; +} + +//// ICalcLighting //////////////////////////////////////////////////////////// +// Kind of misnamed. Sets the D3D material lighting model based on what we're +// currently doing. +void plDXPipeline::ICalcLighting( const plLayerInterface *currLayer, const plSpan *currSpan ) +{ + D3DMATERIAL9 mat; + static hsScalar diffScale = 1.f; + static hsScalar ambScale = 1.f; + UInt32 props; + + + plProfile_Inc(MatLightState); + + /// New (temporary) lighting method: + /// The vertices now include the following: + /// diffuse = maxVertexColor * matDiffuse + matAmbient + /// specular = ( maxLighting + maxIllum ) * matDiffuse + matAmbient + /// And we want the lighting set up like: + /// L = I*v1 + v2 + (sigma)(light stuff * v3 + 0) + /// Where I = 0 for now (will be the environmental light constant eventually), + /// v1 is the diffuse vertex color and v2 is the specular vertex color. + /// So it basically translates into: + /// D3D ambient color = diffuse vertex color + /// D3D ambient constant = environmental light constant (0 for now) + /// D3D emissive color = specular vertex color + /// D3D diffuse color = diffuse vertex color + + /// We now provide three lighting equations at the pipeline's disposal: + /// Material: (the one we all know and love) + /// MATd * VTXd + MATa + + /// Vtx preshaded: (particle systems) + /// MATa * VTXd + 0 + + /// Vtx non-preshaded: + /// white * VTXd + MATa + + /// We also have a few more for shadows and such, which are handled individually + + memset( &mat, 0, sizeof( mat ) ); + + /// Normal rendering--select the right lighting equation + if (IsDebugFlagSet(plPipeDbg::kFlagAllBright)) + { + inlEnsureLightingOn(); + mat.Diffuse.r = mat.Diffuse.g = mat.Diffuse.b = mat.Diffuse.a = 1.f; + mat.Ambient.r = mat.Ambient.g = mat.Ambient.b = mat.Ambient.a = 1.f; + mat.Emissive.r = mat.Emissive.g = mat.Emissive.b = mat.Emissive.a = 1.f; + fD3DDevice->SetMaterial( &mat ); + fD3DDevice->SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_AMBIENT, 0xffffffff ); + return; + } + + props = ( currSpan != nil ) ? ( currSpan->fProps & plSpan::kLiteMask ) : plSpan::kLiteMaterial; + + if( fLayerState[0].fMiscFlags & hsGMatState::kMiscBumpChans ) + { + props = plSpan::kLiteMaterial; + fLayerState[0].fShadeFlags |= hsGMatState::kShadeNoShade | hsGMatState::kShadeWhite; + } + /// Select one of our three lighting methods + switch( props ) + { + case plSpan::kLiteMaterial: // Material shading + + /// Material: (the one we all know and love) + /// MATd * VTXd + MATa + + + inlEnsureLightingOn(); + + // D3D ambient - give it our material static diffuse, since it will be multiplied by the vertex color + if( fLayerState[0].fShadeFlags & hsGMatState::kShadeWhite ) + { + mat.Ambient.r = mat.Ambient.g = mat.Ambient.b = diffScale; + mat.Ambient.a = 1.f; + + } + else if (IsDebugFlagSet(plPipeDbg::kFlagNoPreShade)) + { + mat.Ambient.r = mat.Ambient.g = mat.Ambient.b = 0; + mat.Ambient.a = 1.f; + } + else + mat.Ambient = inlPlToD3DColor(currLayer->GetPreshadeColor() * diffScale, 1.f); + + // D3D diffuse - give it our runtime material diffuse + mat.Diffuse = inlPlToD3DColor(currLayer->GetRuntimeColor() * diffScale, currLayer->GetOpacity()); + + // D3D emissive - give it our material ambient + mat.Emissive = inlPlToD3DColor(currLayer->GetAmbientColor() * ambScale, 1.f); + + // Set specular properties + if( fLayerState[0].fShadeFlags & hsGMatState::kShadeSpecular ) + { + mat.Specular = inlPlToD3DColor( currLayer->GetSpecularColor(), 1.f); + mat.Power = currLayer->GetSpecularPower(); + } + + fD3DDevice->SetMaterial( &mat ); + fD3DDevice->SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); + + if( fLayerState[0].fShadeFlags & hsGMatState::kShadeWhite ) + fD3DDevice->SetRenderState( D3DRS_AMBIENT, 0xffffffff ); + else + fD3DDevice->SetRenderState( D3DRS_AMBIENT, inlGetD3DColor( *(hsColorRGBA*)&mat.Ambient ) ); + + if( fLayerState[0].fShadeFlags & hsGMatState::kShadeNoShade ) + fD3DDevice->SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL ); + else + fD3DDevice->SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1 ); + + fCurrLightingMethod = plSpan::kLiteMaterial; + break; + + case plSpan::kLiteVtxPreshaded: // Vtx preshaded + // MATa * VTXd + 0 + + // Mapping to: GLa * AMSrc + EMSrc + <.....................DMSrc> + +#if 0 // PARTICLESHADE + if( fLayerState[0].fShadeFlags & hsGMatState::kShadeEmissive ) + { + inlEnsureLightingOff(); + } + else + { + inlEnsureLightingOn(); + + // Set a black material (we ONLY care about vertex color when doing particles, + // er I mean, vtxPreshaded) + fD3DDevice->SetMaterial( &mat ); + + fD3DDevice->SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1 ); + fD3DDevice->SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_AMBIENT, 0 ); + + fD3DDevice->SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL ); + } +#else // PARTICLESHADE + inlEnsureLightingOn(); + + // MATa * white + 0 + + + + fD3DDevice->SetMaterial( &mat ); + + fD3DDevice->SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1 ); + fD3DDevice->SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_AMBIENT, 0 ); + + fD3DDevice->SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL ); + + if( fLayerState[0].fShadeFlags & hsGMatState::kShadeEmissive ) + fD3DDevice->SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1 ); + else + fD3DDevice->SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); + +#endif // PARTICLESHADE + + fCurrLightingMethod = plSpan::kLiteVtxPreshaded; + break; + + + case plSpan::kLiteVtxNonPreshaded: // Vtx non-preshaded + // white * VTXd + MATa + + // Mapping to: GLa * AMSrc + EMSrc + <.....................DMSrc> + + inlEnsureLightingOn(); + + // D3D emissive - give it our material ambient + mat.Emissive = inlPlToD3DColor(currLayer->GetAmbientColor() * ambScale, 1.f); + + // Set specular properties + if( fLayerState[0].fShadeFlags & hsGMatState::kShadeSpecular ) + { + mat.Specular = inlPlToD3DColor( currLayer->GetSpecularColor(), 1.f); + mat.Power = currLayer->GetSpecularPower(); + } + fD3DDevice->SetMaterial( &mat ); + + // Lightmaps want WHITE here, otherwise we want BLACK + DWORD preShadeStrength; + preShadeStrength = inlPlToDWORDColor(currLayer->GetPreshadeColor()); + fD3DDevice->SetRenderState(D3DRS_AMBIENT, preShadeStrength); + + fD3DDevice->SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1 ); + fD3DDevice->SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1 ); + fD3DDevice->SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL ); + + fCurrLightingMethod = plSpan::kLiteVtxNonPreshaded; + break; + + default: + hsAssert( false, "Bad lighting type" ); + break; + } + +} + +/////////////////////////////////////////////////////////////////////////////// +//// plDXLightSettings Functions ///////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +plDXLightSettings::plDXLightSettings() +: fActiveList(nil), + fRefList(nil), + fPipeline(nil) +{ +} + +//// Reset //////////////////////////////////////////////////////////////////// +// Sets member variables to initial states. + +void plDXLightSettings::Reset( plDXPipeline *pipe ) +{ + Release(); + + fNextShadowLight = 0; + + fUsedFlags.Clear(); + fEnabledFlags.Clear(); + fHoldFlags.Clear(); + fProjEach.Reset(); + fProjAll.Reset(); + fNextIndex = 1; /// Light 0 is reserved + fLastIndex = 1; + fTime = 0; + fRefList = nil; + fPipeline = pipe; +} + +//// Release ////////////////////////////////////////////////////////////////// +// Releases/deletes anything associated with these settings. +// This includes unregistering all lights. +void plDXLightSettings::Release() +{ + plDXLightRef *ref; + + fProjEach.Reset(); + fProjAll.Reset(); + + while( fRefList ) + { + ref = fRefList; + ref->Release(); + ref->Unlink(); + } + + // Tell the light infos to unlink themselves + while( fActiveList ) + fPipeline->UnRegisterLight( fActiveList ); + + fShadowLights.SetCount(fShadowLights.GetNumAlloc()); + int i; + for( i = 0; i < fShadowLights.GetCount(); i++ ) + { + hsRefCnt_SafeUnRef(fShadowLights[i]); + fShadowLights[i] = nil; + } + fShadowLights.SetCount(0); + +} + +//// ReserveD3DIndex ////////////////////////////////////////////////////////// +// Reserve a D3D light index. + +UInt32 plDXLightSettings::ReserveD3DIndex() +{ + for( ; fNextIndex < (UInt32)-1; fNextIndex++ ) + { + if( !fUsedFlags.IsBitSet( fNextIndex ) ) + break; + } + + fUsedFlags.SetBit( fNextIndex ); + fEnabledFlags.ClearBit( fNextIndex ); // Ensure it's cleared + fHoldFlags.ClearBit( fNextIndex ); + if( fNextIndex > fLastIndex ) + fLastIndex = fNextIndex; + + return fNextIndex; +} + +//// ReleaseD3DIndex ////////////////////////////////////////////////////////// +// Release a reserved D3D light index to be reused. + +void plDXLightSettings::ReleaseD3DIndex( UInt32 idx ) +{ + fUsedFlags.SetBit( idx, false ); + if( fNextIndex > idx ) + fNextIndex = idx; // Forces search to start here next time + + // Dec down fLastIndex + while( fLastIndex > 0 && !fUsedFlags.IsBitSet( fLastIndex ) ) + fLastIndex--; + + if( fNextIndex > fLastIndex ) + fNextIndex = fLastIndex; +} + + +/////////////////////////////////////////////////////////////////////////////// +//// Materials //////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// ISetLayer //////////////////////////////////////////////////////////////// +// Sets whether we're rendering a base layer or upper layer. Upper layer has +// a Z bias to avoid Z fighting. +void plDXPipeline::ISetLayer( UInt32 lay ) +{ + if( lay ) + { + if( fCurrRenderLayer != lay ) + { + fCurrRenderLayer = lay; + + plCONST(int) kBiasMult = 8; + if( !( fSettings.fD3DCaps & kCapsZBias ) ) + IProjectionMatrixToD3D(); + else + fD3DDevice->SetRenderState( D3DRS_DEPTHBIAS, kBiasMult * fCurrRenderLayer ); + } + } + else + IBottomLayer(); +} + +//// IBottomLayer ///////////////////////////////////////////////////////////// +// Turn off any Z bias. +void plDXPipeline::IBottomLayer() +{ + if( fCurrRenderLayer != 0 ) + { + fCurrRenderLayer = 0; + if( !( fSettings.fD3DCaps & kCapsZBias ) ) + IProjectionMatrixToD3D(); + else + fD3DDevice->SetRenderState( D3DRS_DEPTHBIAS, 0 ); + } +} + +// Special effects ///////////////////////////////////////////////////////////// + +// IPushOverBaseLayer ///////////////////////////////////////////////////////// +// Sets fOverBaseLayer (if any) as a wrapper on top of input layer. +// This allows the OverBaseLayer to intercept and modify queries of +// the real current layer's properties (e.g. color or state). +// fOverBaseLayer is set to only get applied to the base layer during +// multitexturing. +// Must be matched with call to IPopOverBaseLayer. +plLayerInterface* plDXPipeline::IPushOverBaseLayer(plLayerInterface* li) +{ + if( !li ) + return nil; + + fOverLayerStack.Push(li); + + if( !fOverBaseLayer ) + return fOverBaseLayer = li; + + fForceMatHandle = true; + fOverBaseLayer = fOverBaseLayer->Attach(li); + fOverBaseLayer->Eval(fTime, fFrame, 0); + return fOverBaseLayer; +} + +// IPopOverBaseLayer ///////////////////////////////////////////////////////// +// Removes fOverBaseLayer as wrapper on top of input layer. +// Should match calls to IPushOverBaseLayer. +plLayerInterface* plDXPipeline::IPopOverBaseLayer(plLayerInterface* li) +{ + if( !li ) + return nil; + + fForceMatHandle = true; + + plLayerInterface* pop = fOverLayerStack.Pop(); + fOverBaseLayer = fOverBaseLayer->Detach(pop); + + return pop; +} + +// IPushOverAllLayer /////////////////////////////////////////////////// +// Push fOverAllLayer (if any) as wrapper around the input layer. +// fOverAllLayer is set to be applied to each layer during multitexturing. +// Must be matched by call to IPopOverAllLayer +plLayerInterface* plDXPipeline::IPushOverAllLayer(plLayerInterface* li) +{ + if( !li ) + return nil; + + fOverLayerStack.Push(li); + + if( !fOverAllLayer ) + { + fOverAllLayer = li; + fOverAllLayer->Eval(fTime, fFrame, 0); + return fOverAllLayer; + } + + fForceMatHandle = true; + fOverAllLayer = fOverAllLayer->Attach(li); + fOverAllLayer->Eval(fTime, fFrame, 0); + + return fOverAllLayer; +} + +// IPopOverAllLayer ////////////////////////////////////////////////// +// Remove fOverAllLayer as wrapper on top of input layer. +// Should match calls to IPushOverAllLayer. +plLayerInterface* plDXPipeline::IPopOverAllLayer(plLayerInterface* li) +{ + if( !li ) + return nil; + + fForceMatHandle = true; + + plLayerInterface* pop = fOverLayerStack.Pop(); + fOverAllLayer = fOverAllLayer->Detach(pop); + + return pop; +} + +// PiggyBacks - used in techniques like projective lighting. +// PiggyBacks are layers appended to each drawprimitive pass. +// For example, if a material has 3 layers which will be drawn +// in 2 passes, +// pass0: layer0+layer1 +// pass1: layer2 +// Then if a piggyback layer layerPB is active, the actual rendering would be +// pass0: layer0+layer1+layerPB +// pass1: layer2 + layerPB + +// ISetNumActivePiggyBacks ///////////////////////////////////////////// +// Calculate the number of active piggy backs. +int plDXPipeline::ISetNumActivePiggyBacks() +{ + return fActivePiggyBacks = hsMinimum(fSettings.fMaxPiggyBacks, fPiggyBackStack.GetCount()); +} + +// IPushProjPiggyBack ////////////////////////////////////////////////// +// Push a projected texture on as a piggy back. +void plDXPipeline::IPushProjPiggyBack(plLayerInterface* li) +{ + if( fView.fRenderState & plPipeline::kRenderNoPiggyBacks ) + return; + + fPiggyBackStack.Push(li); + fActivePiggyBacks = fPiggyBackStack.GetCount() - fMatPiggyBacks; + fForceMatHandle = true; +} + +// IPopProjPiggyBacks ///////////////////////////////////////////////// +// Remove a projected texture from use as a piggy back. +void plDXPipeline::IPopProjPiggyBacks() +{ + if( fView.fRenderState & plPipeline::kRenderNoPiggyBacks ) + return; + + fPiggyBackStack.SetCount(fMatPiggyBacks); + ISetNumActivePiggyBacks(); + fForceMatHandle = true; +} + +// IPushPiggyBacks //////////////////////////////////////////////////// +// Push any piggy backs associated with a material, presumed to +// be a light map because that's all they are used for. +// Matched with IPopPiggyBacks +void plDXPipeline::IPushPiggyBacks(hsGMaterial* mat) +{ + hsAssert(!fMatPiggyBacks, "Push/Pop Piggy mismatch"); + + if( fView.fRenderState & plPipeline::kRenderNoPiggyBacks ) + return; + + int i; + for( i = 0; i < mat->GetNumPiggyBacks(); i++ ) + { + if( !mat->GetPiggyBack(i) ) + continue; + + if ((mat->GetPiggyBack(i)->GetMiscFlags() & hsGMatState::kMiscLightMap) + && IsDebugFlagSet(plPipeDbg::kFlagNoLightmaps)) + continue; + + fPiggyBackStack.Push(mat->GetPiggyBack(i)); + fMatPiggyBacks++; + } + ISetNumActivePiggyBacks(); + fForceMatHandle = true; +} + +// IPopPiggyBacks /////////////////////////////////////////////////////// +// Pop any current piggy backs set from IPushPiggyBacks. +// Matches IPushPiggyBacks. +void plDXPipeline::IPopPiggyBacks() +{ + if( fView.fRenderState & plPipeline::kRenderNoPiggyBacks ) + return; + + fPiggyBackStack.SetCount(fPiggyBackStack.GetCount() - fMatPiggyBacks); + fMatPiggyBacks = 0; + + ISetNumActivePiggyBacks(); + fForceMatHandle = true; +} + +//// IHandleMaterial ////////////////////////////////////////////////////////// +// Takes the starting "layer" and uses as many layers as possible in the given +// material and sets up the device to draw with it. Returns the first layer +// index not yet used. (I.e. if we ate layers 0 and 1, it'll return 2). +// A return value of -1 means don't bother rendering. + +Int32 plDXPipeline::IHandleMaterial( hsGMaterial *newMat, UInt32 layer, const plSpan *currSpan ) +{ + // No material means no draw. + if( !newMat && newMat->GetLayer(layer) ) + return -1; + + // If this is a bump mapping pass but the object isn't currently runtime lit, just skip. + // Note that may change here, if we're skipping past the bump layers but there + // are more layers (passes) to do after that. + if( ISkipBumpMap(newMat, layer, currSpan) ) + { + return -1; + } + + // Workaround for the ATI Radeon 7500's inability to use uvw coordinates above 1. + // If we have a layer trying to use uvw 2 or higher, skip it and any layers bound to + // it. + while( (layer < newMat->GetNumLayers()) + && newMat->GetLayer(layer) + && ((newMat->GetLayer(layer)->GetUVWSrc() & 0xf) > fSettings.fMaxUVWSrc) ) + { + if( newMat->GetLayer(layer)->GetMiscFlags() & hsGMatState::kMiscBindNext ) + layer++; + layer++; + } + if( layer >= newMat->GetNumLayers() ) + return -1; + + // If nothing has changed, we don't need to recompute and set state. + if( !fForceMatHandle && (newMat == fCurrMaterial && layer == fCurrLayerIdx) ) + { + // Before returning, check if we have to redo our lighting + UInt32 lightType = ( currSpan != nil ) ? ( currSpan->fProps & plSpan::kLiteMask ) : plSpan::kLiteMaterial; + if( lightType != fCurrLightingMethod ) + ICalcLighting( fCurrLay, currSpan ); + + if( fLayerState[0].fMiscFlags & (hsGMatState::kMiscBumpDu|hsGMatState::kMiscBumpDw) ) + ISetBumpMatrices(fCurrLay, currSpan); + + return layer + fCurrNumLayers; + } + + fForceMatHandle = false; + + fCurrLayerIdx = layer; +// fCurrNumLayers = newMat->GetNumLayers(); + + if (newMat != fCurrMaterial) + plProfile_Inc(MatChange); + plProfile_Inc(LayChange); + + /// Test for fail states + if (IsDebugFlagSet(plPipeDbg::kFlagNoDecals) && (newMat->GetCompositeFlags() & hsGMaterial::kCompDecal)) + { + return -1; + } + + /// Workaround for a D3D limitation--you're not allowed to render with a texture that you're + /// rendering INTO. Hence we can't have self-reflecting cubicRenderTargets (damn) + if( fSettings.fCurrBaseRenderTarget != nil && + newMat->GetLayer( layer )->GetTexture() == plBitmap::ConvertNoRef( fSettings.fCurrBaseRenderTarget ) ) + { + return -1; + } + + /// Figure out our current states + // Start with the base layer. + plLayerInterface *currLay = IPushOverBaseLayer(newMat->GetLayer(layer)); + + if (IsDebugFlagSet(plPipeDbg::kFlagBumpW) && (currLay->GetMiscFlags() & hsGMatState::kMiscBumpDu) ) + currLay = newMat->GetLayer(fCurrLayerIdx = ++layer); + + currLay = IPushOverAllLayer(currLay); + + /// Save stuff for next time around + ICompositeLayerState(0, currLay); + hsRefCnt_SafeAssign( fCurrMaterial, newMat ); + fCurrLayerIdx = layer; + fCurrLay = currLay; + + if (IsDebugFlagSet(plPipeDbg::kFlagDisableSpecular)) + fLayerState[0].fShadeFlags &= ~hsGMatState::kShadeSpecular; + + // ZIncLayer requests Z bias for upper layers. + if( fLayerState[0].fZFlags & hsGMatState::kZIncLayer ) + ISetLayer( 1 ); + else + IBottomLayer(); + + /// A few debugging things + if (IsDebugFlagSet(plPipeDbg::kFlagNoAlphaBlending)) + fLayerState[0].fBlendFlags &= ~hsGMatState::kBlendMask; + + if ((IsDebugFlagSet(plPipeDbg::kFlagBumpUV) || IsDebugFlagSet(plPipeDbg::kFlagBumpW)) && (fLayerState[0].fMiscFlags & hsGMatState::kMiscBumpChans) ) + { + switch( fLayerState[0].fMiscFlags & hsGMatState::kMiscBumpChans ) + { + case hsGMatState::kMiscBumpDu: + break; + case hsGMatState::kMiscBumpDv: + if( !(fCurrMaterial->GetLayer(layer-2)->GetBlendFlags() & hsGMatState::kBlendAdd) ) + { + fLayerState[0].fBlendFlags &= ~hsGMatState::kBlendMask; + fLayerState[0].fBlendFlags |= hsGMatState::kBlendMADD; + } + break; + case hsGMatState::kMiscBumpDw: + if( !(fCurrMaterial->GetLayer(layer-1)->GetBlendFlags() & hsGMatState::kBlendAdd) ) + { + fLayerState[0].fBlendFlags &= ~hsGMatState::kBlendMask; + fLayerState[0].fBlendFlags |= hsGMatState::kBlendMADD; + } + break; + default: + break; + } + } + + /// Get the # of layers we can draw in this pass into fCurrNumLayers + int oldNumLayers = fCurrNumLayers; + ILayersAtOnce( newMat, layer ); + if( oldNumLayers != fCurrNumLayers ) + { + // This hack is necessary to cover a hack necessary to cover a "limitation" in the GeForce2 drivers. + // Basically, we have to handle NoTexAlpha/Color differently if it's stage 1 than other stages, + // so even though the BlendFlags haven't changed, the calls to D3D are different. Another + // way to handle this would be to have a different handler based on whether we are 2 TMU limited + // or not, but whatever. + if( fLayerState[1].fBlendFlags & (hsGMatState::kBlendNoTexAlpha | hsGMatState::kBlendNoTexColor) ) + fLayerState[1].fBlendFlags = UInt32(-1); + } + + // Placed here, since it's material-dependent (or more accurately, current-layer-dependent) + ICalcLighting( currLay, currSpan ); + + // If we're bump mapping, compute the texture transforms. + if( fLayerState[0].fMiscFlags & (hsGMatState::kMiscBumpDu|hsGMatState::kMiscBumpDw) ) + ISetBumpMatrices(currLay, currSpan); + + /// Transfer states to D3D now + IHandleFirstTextureStage( currLay ); + + currLay = IPopOverAllLayer(currLay); + currLay = IPopOverBaseLayer(currLay); + fCurrLay = currLay; + + int nextLayer = fCurrLayerIdx + fCurrNumLayers; + if (IsDebugFlagSet(plPipeDbg::kFlagBumpW) && (fLayerState[0].fMiscFlags & hsGMatState::kMiscBumpDw) ) + { + // Bump mapping approximation using only the W (normal direction) component of lighting. + plLayerInterface* layPtr = IPushOverAllLayer(newMat->GetLayer(fCurrLayerIdx + 2)); + if( !layPtr ) + return -1; + ICompositeLayerState(1, layPtr); + IHandleTextureStage( 1, layPtr ); + layPtr = IPopOverAllLayer(layPtr); + nextLayer = fCurrLayerIdx + 3; + } + else if (IsDebugFlagSet(plPipeDbg::kFlagBumpUV) && (fLayerState[0].fMiscFlags & hsGMatState::kMiscBumpDu) ) + { + // Bump mapping approximation using only the UV (surface tangent directions) component of lighting. + plLayerInterface* layPtr = IPushOverAllLayer(newMat->GetLayer(fCurrLayerIdx + 3)); + if( !layPtr ) + return -1; + ICompositeLayerState(1, layPtr); + IHandleTextureStage( 1, layPtr ); + layPtr = IPopOverAllLayer(layPtr); + nextLayer = fCurrLayerIdx + 2; + } + else + { + // Normal multi texturing. + /// Loop through all multitexturing layers + int i; + if( fView.fRenderState & plPipeline::kRenderBaseLayerOnly ) + nextLayer = newMat->GetNumLayers(); + + for( i = 1; i < fCurrNumLayers; i++ ) + { + plLayerInterface* layPtr = newMat->GetLayer( fCurrLayerIdx + i ); + if( !layPtr ) + return -1; + + // Can't render into a render target using same rendertarget as a texture. + if( fSettings.fCurrBaseRenderTarget + && + layPtr->GetTexture() == (plBitmap*)(fSettings.fCurrBaseRenderTarget) ) + { + // Oops, just bail + return -1; + } + + layPtr = IPushOverAllLayer(layPtr); + ICompositeLayerState(i, layPtr); + IHandleTextureStage( i, layPtr ); + layPtr = IPopOverAllLayer(layPtr); + } + + } + + // More cleanup for the DX9.0c 2 texture limitation. See ILayersAtOnce() + if (fSettings.fMaxLayersAtOnce == 2) + { + if ((fLayerState[0].fBlendFlags & hsGMatState::kBlendAdd) + && (newMat->GetNumLayers() > fCurrLayerIdx + 1) + && (newMat->GetLayer(fCurrLayerIdx + 1)->GetUVWSrc() & plLayerInterface::kUVWPosition)) + { + // If we're doing additive blending and the next layer is based on position, + // it's probably a distance fade. We'd rather have our diffuse color. + // ILayersAtOnce will already have told us we can't use it this pass. + // Skip it so it won't draw on its own next pass. + nextLayer++; + } + } + + int numActivePiggyBacks = 0; + if( !(fLayerState[0].fMiscFlags & hsGMatState::kMiscBumpChans) && !(fLayerState[0].fShadeFlags & hsGMatState::kShadeEmissive) ) + { + /// Tack lightmap onto last stage if we have one + numActivePiggyBacks = fActivePiggyBacks; + if( numActivePiggyBacks > fSettings.fMaxLayersAtOnce - fCurrNumLayers ) + numActivePiggyBacks = fSettings.fMaxLayersAtOnce - fCurrNumLayers; + if( numActivePiggyBacks ) + { + int i; + for( i = 0; i < numActivePiggyBacks; i++ ) + { + // Note that we take piggybacks off the end of fPiggyBackStack. + plLayerInterface* layPtr = IPushOverAllLayer( fPiggyBackStack[fPiggyBackStack.GetCount()-1-i] ); + if( !layPtr ) + return -1; + ICompositeLayerState(fCurrNumLayers+i, layPtr); + IHandleTextureStage( fCurrNumLayers+i, layPtr ); + layPtr = IPopOverAllLayer(layPtr); + } + + // If we've got a piggyback, plus two layers that must be drawn together, but + // only two TMU's to work with, we're screwed. Someone has got to get skipped and + // hope no one notices. Typically, the first (base) layer has the color info, + // and the second the opacity. So we'll try using the projection to brighten + // the color, ignoring the opacity. +// if( ((fCurrNumLayers + numActivePiggyBacks) == fSettings.fMaxLayersAtOnce) +// && (fLayerState[0].fMiscFlags & hsGMatState::kMiscBindNext) ) + if( (fLayerState[0].fMiscFlags & hsGMatState::kMiscBindNext) + && (fCurrNumLayers < 2) ) + nextLayer++; + } + } + + // Declare we won't be using any more texture stages. + IStageStop( fCurrNumLayers + numActivePiggyBacks ); + + return nextLayer; +} + +// ICompositeLayerState ///////////////////////////////////////////////////////////////// +// Set the current Plasma state based on the input layer state and the material overrides. +// fMatOverOn overrides to set a state bit whether it is set in the layer or not. +// fMatOverOff overrides to clear a state bit whether it is set in the layer or not. +const hsGMatState& plDXPipeline::ICompositeLayerState(int which, plLayerInterface* layer) +{ + fOldLayerState[which] = fLayerState[which]; + fLayerState[which].Composite(layer->GetState(), fMatOverOn, fMatOverOff); + if( fOldLayerState[which].fBlendFlags == UInt32(-1) ) + fOldLayerState[which].fBlendFlags = ~fLayerState[which].fBlendFlags; + + return fLayerState[which]; +} + +//// IHandleFirstTextureStage ///////////////////////////////////////////////// +// Convert internal material state to D3D state for the base layer. +void plDXPipeline::IHandleFirstTextureStage( plLayerInterface *layer ) +{ + IHandleTextureMode(layer); + IHandleShadeMode(); + if( fLayerState[0].Differs( fLayerState[0].fZFlags, fOldLayerState[0].fZFlags, hsGMatState::kZMask ) ) + IHandleZMode(); + IHandleMiscMode(); + + IHandleTextureStage( 0, layer ); +} + +//// IHandleShadeMode ///////////////////////////////////////////////////////// +// Convert shade state into D3D settings. +void plDXPipeline::IHandleShadeMode() +{ + if( fLayerState[0].Differs( fLayerState[0].fShadeFlags, fOldLayerState[0].fShadeFlags, hsGMatState::kShadeSpecular ) ) + { + if( fLayerState[0].fShadeFlags & hsGMatState::kShadeSpecular ) + fD3DDevice->SetRenderState( D3DRS_SPECULARENABLE, TRUE ); + else + fD3DDevice->SetRenderState( D3DRS_SPECULARENABLE, FALSE ); + } +} + +//// IHandleZMode ///////////////////////////////////////////////////////////// +// Convert Z state into D3D settings. +void plDXPipeline::IHandleZMode() +{ + switch( fLayerState[0].fZFlags & hsGMatState::kZMask ) + { + case hsGMatState::kZClearZ: + fD3DDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_ALWAYS ); + fD3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE ); + break; + case hsGMatState::kZNoZRead: + fD3DDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_ALWAYS ); + fD3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE ); + break; + case hsGMatState::kZNoZWrite: + fD3DDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL ); + fD3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE ); + break; + case hsGMatState::kZNoZRead | hsGMatState::kZClearZ: + fD3DDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_ALWAYS ); + fD3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE ); + break; + case hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite: + fD3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE ); + fD3DDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_ALWAYS ); + break; + case 0: + fD3DDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL ); + fD3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE ); + break; + + // illegal combinations + case hsGMatState::kZClearZ | hsGMatState::kZNoZWrite: + case hsGMatState::kZClearZ | hsGMatState::kZNoZWrite | hsGMatState::kZNoZRead: + hsAssert(false, "Illegal combination of Z Buffer modes (Clear but don't write)"); + break; + } +} + +//// IHandleMiscMode ////////////////////////////////////////////////////////// +// Convert Misc state into D3D settings. +void plDXPipeline::IHandleMiscMode() +{ + if( fLayerState[0].Differs(fLayerState[0].fMiscFlags, fOldLayerState[0].fMiscFlags, hsGMatState::kMiscWireFrame) ) + { + if( fLayerState[0].fMiscFlags & hsGMatState::kMiscWireFrame ) + fD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME ); + else + fD3DDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); + } +} + +//// IHandleTextureStage ////////////////////////////////////////////////////// +// Issue D3D calls to enable rendering the given layer at the given texture stage. +void plDXPipeline::IHandleTextureStage( UInt32 stage, plLayerInterface *layer ) +{ + hsGDeviceRef *ref = nil; + plBitmap *texture; + + // Blend mode + const hsGMatState& layState = fLayerState[stage]; + if( fLayerState[ stage ].fBlendFlags ^ fOldLayerState[stage].fBlendFlags ) + IHandleStageBlend(stage); + + // Texture wrap/clamp mode + if( fLayerState[ stage ].fClampFlags ^ fOldLayerState[stage].fClampFlags ) + IHandleStageClamp(stage); + + // UVW transform + IHandleStageTransform( stage, layer ); + + // Create the D3D texture (if necessary) and set it to the device. + if( ( texture = layer->GetTexture() ) != nil ) + { + ref = texture->GetDeviceRef(); + if( ref == nil || ref->IsDirty() ) + { + // Normal textures + plMipmap *mip; + plCubicEnvironmap *cubic; + + if( ( mip = plMipmap::ConvertNoRef( texture ) ) != nil ) + ref = MakeTextureRef( layer, mip ); + + // Cubic environment maps + else if( ( cubic = plCubicEnvironmap::ConvertNoRef( texture ) ) != nil ) + ref = IMakeCubicTextureRef( layer, cubic ); + } + } + + if( ref != nil ) + IUseTextureRef(stage, ref, layer); + else + { + fD3DDevice->SetTexture( stage, NULL ); + hsRefCnt_SafeUnRef( fLayerRef[ stage ] ); + fLayerRef[ stage ] = nil; + } +} + +// CheckTextureRef ////////////////////////////////////////////////////// +// Make sure the given layer's texture has background D3D resources allocated. +void plDXPipeline::CheckTextureRef(plLayerInterface* layer) +{ + plBitmap* bitmap = layer->GetTexture(); + if( bitmap ) + { + hsGDeviceRef* ref = bitmap->GetDeviceRef(); + + if( !ref ) + { + plMipmap* mip = plMipmap::ConvertNoRef(bitmap); + if( mip ) + { + MakeTextureRef(layer, mip); + return; + } + + plCubicEnvironmap* cubic = plCubicEnvironmap::ConvertNoRef(bitmap); + if( cubic ) + { + IMakeCubicTextureRef(layer, cubic); + return; + } + } + } +} + +// IHandleBumpEnv ////////////////////////////////////////////////////////////// +// D3D settings for BUMPENVMAPLUMINANCE. +// This has never been used in production assets, because I never got +// a good effect out of it, and BUMPENVMAPLUMINANCE isn't universally +// supported in hardware. +void plDXPipeline::IHandleBumpEnv(int stage, UInt32 blendFlags) +{ + DWORD current = stage ? D3DTA_CURRENT : D3DTA_DIFFUSE; + UInt32 colorSrc = blendFlags & hsGMatState::kBlendInvertColor ? D3DTA_TEXTURE | D3DTA_COMPLEMENT : D3DTA_TEXTURE; + + fD3DDevice->SetTextureStageState(stage, D3DTSS_COLOROP, D3DTOP_BUMPENVMAPLUMINANCE); + fD3DDevice->SetTextureStageState(stage, D3DTSS_COLORARG1, colorSrc); + fD3DDevice->SetTextureStageState(stage, D3DTSS_COLORARG2, current); + + fD3DDevice->SetTextureStageState(stage, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + fD3DDevice->SetTextureStageState(stage, D3DTSS_ALPHAARG2, D3DTA_CURRENT); + + const hsMatrix44& envXfm = fCurrLay->GetBumpEnvMatrix(); + fD3DDevice->SetTextureStageState(stage, D3DTSS_BUMPENVMAT00, F2DW(envXfm.fMap[0][0])); + fD3DDevice->SetTextureStageState(stage, D3DTSS_BUMPENVMAT01, F2DW(envXfm.fMap[1][0])); + fD3DDevice->SetTextureStageState(stage, D3DTSS_BUMPENVMAT10, F2DW(envXfm.fMap[0][1])); + fD3DDevice->SetTextureStageState(stage, D3DTSS_BUMPENVMAT11, F2DW(envXfm.fMap[1][1])); + + fD3DDevice->SetTextureStageState(stage, D3DTSS_BUMPENVLSCALE, F2DW(envXfm.fMap[2][2])); + fD3DDevice->SetTextureStageState(stage, D3DTSS_BUMPENVLOFFSET, F2DW(envXfm.fMap[2][3])); +} + +//// IHandleStageBlend //////////////////////////////////////////////////////// +// Translate current blend state for this stage into D3D settings. +void plDXPipeline::IHandleStageBlend(int stage) +{ + const UInt32 blendFlags = fLayerState[stage].fBlendFlags; + // If it's the base layer, handle that differently, because it's not really + // texture stage settings, but frame buffer blend settings. + if( stage == 0 ) + { + IHandleFirstStageBlend(); + return; + } + + UInt32 colorSrc = D3DTA_TEXTURE; + if( blendFlags & hsGMatState::kBlendInvertColor ) + colorSrc |= D3DTA_COMPLEMENT ; + // kBlendEnvBumpNext not really used. + if( blendFlags & hsGMatState::kBlendEnvBumpNext ) + { + IHandleBumpEnv(stage, blendFlags); + } + else switch( blendFlags & hsGMatState::kBlendMask ) + { + // Alpha blending. Complicated by the ability to ignore either + // color or alpha for any given texture. The lower end GeForces + // don't orthogonally support settings, especially when the final + // (3rd) stage is the diffuse color/alpha modulate and the board + // really only wants to support 2 stages. + // So we couldn't just translate our internal plasma stage states + // into D3D states, we had to do some rearranging. + // Note that by the time we get here, we _know_ that this isn't the + // base layer (stage 0), because that's handled elsewhere. + case hsGMatState::kBlendAlpha: + // If the current number of layers is 2, then we've already handled the + // base layer, so this must be layer 1 and the final layer. + // If the base layer has NoTexColor or this layer has NoTexColor, we need + // to do some rearranging. + if( (fCurrNumLayers == 2) + &&((blendFlags | fLayerState[0].fBlendFlags) & hsGMatState::kBlendNoTexColor) ) + { + // If this layer AND base layer are NoTexColor, then we just want the diffuse color. + if( (blendFlags & hsGMatState::kBlendNoTexColor) + &&(fLayerState[0].fBlendFlags & hsGMatState::kBlendNoTexColor) ) + { + // select diffuse color + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_SELECTARG2 ); + } + // If the base layer has NoTexColor but this layer doesn't, then we + // want the output to be this texture color times diffuse (ignoring base texture color). + else if( fLayerState[0].fBlendFlags & hsGMatState::kBlendNoTexColor ) + { + // diffuse is arg2, modulate + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG1, colorSrc ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_MODULATE ); + } + // If base layer doesn't have NoTexColor, but this layer does, then + // we want the output to be diffuse times base texture, which is in current. + else if( blendFlags & hsGMatState::kBlendNoTexColor ) + { + // diffuse is arg1, modulate + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG1, D3DTA_DIFFUSE ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG2, D3DTA_CURRENT ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_MODULATE ); + } + + } + // If we get here and this layer has NoTexColor, then we MUST be on a layer + // above 1, which means we're on an advanced enough board to handle this orthogonally, + // i.e. one with more than 2 texture stages. + else if( blendFlags & hsGMatState::kBlendNoTexColor ) + { + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG2, D3DTA_CURRENT ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_SELECTARG2 ); + } + // Finally, no NoTexColor in sight, just set it. + else + { + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG1, colorSrc ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG2, D3DTA_CURRENT ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLOROP, + blendFlags & hsGMatState::kBlendInvertAlpha + ? D3DTOP_MODULATEINVALPHA_ADDCOLOR + : D3DTOP_BLENDTEXTUREALPHA ); + } + // The same ordeal for alpha, and the ability to ignore the alpha on any texture. + // Note the additional logic for how to combine the alphas of multiple textures + // into a final FB alpha. + // This is orthogonal to using the alpha to combine colors of two different textures. + // The default behavior is to use the upper texture alpha to blend the upper layer color + // with the lower texture color, but retain the lower texture alpha (modulated by diffuse) + // for the frame buffer alpha. + switch( blendFlags & ( hsGMatState::kBlendAlphaAdd | hsGMatState::kBlendAlphaMult ) ) + { + default: + case 0: + // Using alpha to blend textures, but this layer's alpha doesn't affect final FB + // alpha. + // Two layer setup with one or the other (or both) ignoring alpha. + if( (fCurrNumLayers == 2) + &&((blendFlags | fLayerState[0].fBlendFlags) & hsGMatState::kBlendNoTexAlpha) ) + { + // Both ignoring alpha, use diffuse. + if( (blendFlags & hsGMatState::kBlendNoTexAlpha) + &&(fLayerState[0].fBlendFlags & hsGMatState::kBlendNoTexAlpha) ) + { + // select diffuse alpha + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 ); + } + // Base ignoring alpha, use diffuse times this texure alpha. + else if( fLayerState[0].fBlendFlags & hsGMatState::kBlendNoTexAlpha ) + { + // diffuse is arg2, modulate + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG1, + blendFlags & hsGMatState::kBlendInvertAlpha + ? D3DTA_TEXTURE | D3DTA_COMPLEMENT + : D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); + } + // This ignoring alpha, use diffuse times base alpha (in current). + else if( blendFlags & hsGMatState::kBlendNoTexAlpha ) + { + // diffuse is arg1, modulate + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); + } + } + // Ignoring alpha or not, with more than 2 texture stages, + // Either way, we'll ignore this texture's alpha, because it's an upper layer + // and has already been used (if it's going to get used) to blend this texture's + // color with the lower layers. + else + { + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); + } + break; + // Alpha coming out of this stage is lower stage alpha plus this texture alpha. + case hsGMatState::kBlendAlphaAdd: + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_ADD ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG1, + blendFlags & hsGMatState::kBlendInvertAlpha + ? D3DTA_TEXTURE | D3DTA_COMPLEMENT + : D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); + break; + // Alpha coming out of this stage is lower stage alpha times this texture alpha. + case hsGMatState::kBlendAlphaMult: + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG1, + blendFlags & hsGMatState::kBlendInvertAlpha + ? D3DTA_TEXTURE | D3DTA_COMPLEMENT + : D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); + break; + } + break; + + // Add texture colors, pass through current alpha. + case hsGMatState::kBlendAdd: + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG1, colorSrc ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG2, D3DTA_CURRENT ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_ADD ); + + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); + break; + + // Multiply texture colors, pass through current alpha + case hsGMatState::kBlendMult: + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG1, colorSrc ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG2, D3DTA_CURRENT ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_MODULATE ); + + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); + + if (fSettings.fMaxLayersAtOnce == 2 && stage == 1) + { + // On these boards, the only way we can do 2 textures plus diffuse is to + // multiply it in during stage 0, but that only gives the same result + // when doing a mult blend, which we won't know when setting up stage 0. + // Now that we know, adjust stage 0 settings. + fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + fD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + } + break; + + // Dot3 texture colors, pass through current alpha. + case hsGMatState::kBlendDot3: + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG1, colorSrc ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG2, D3DTA_CURRENT ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_DOTPRODUCT3 ); + + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); + break; + + // Add signed texture colors, pass through current alpha. + case hsGMatState::kBlendAddSigned: + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG1, colorSrc ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG2, D3DTA_CURRENT ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_ADDSIGNED ); + + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); + break; + + // Add signed * 2 texture colors, pass through current alpha. + case hsGMatState::kBlendAddSigned2X: + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG1, colorSrc ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG2, D3DTA_CURRENT ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_ADDSIGNED2X ); + + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); + break; + + // kBlendAddColorTimesAlpha is only supported for the base layer. + case hsGMatState::kBlendAddColorTimesAlpha: + hsAssert(false, "Blend mode unsupported on upper layers"); + break; + + // No blend, select this texture color and pass through current alpha + case 0: + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG1, colorSrc ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLORARG2, D3DTA_CURRENT ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); + + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 ); + fD3DDevice->SetTextureStageState( stage, D3DTSS_ALPHAARG2, D3DTA_CURRENT ); + break; + } +} + +//// IHandleFirstStageBlend /////////////////////////////////////////////////// +// Set frame buffer blend mode for blending the base layer +// For the case of rendering to a texture with alpha, the alpha written to +// the render target will be computed exactly as the color (limitation of D3D). +void plDXPipeline::IHandleFirstStageBlend() +{ + // No color, just writing out Z values. + if( fLayerState[0].fBlendFlags & hsGMatState::kBlendNoColor ) + { + fD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + fD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ZERO ); + fD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE ); + fLayerState[0].fBlendFlags |= 0x80000000; + } + else + { + switch( fLayerState[0].fBlendFlags & hsGMatState::kBlendMask ) + { + // Detail is just a special case of alpha, handled in construction of the texture + // mip chain by making higher levels of the chain more transparent. + case hsGMatState::kBlendDetail: + case hsGMatState::kBlendAlpha: + fD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + if( fLayerState[0].fBlendFlags & hsGMatState::kBlendInvertFinalAlpha ) + { + fD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_INVSRCALPHA ); + fD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_SRCALPHA ); + } + else + { + fD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); + fD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); + } + break; + // Multiply the final color onto the frame buffer. + case hsGMatState::kBlendMult: + fD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + if( fLayerState[0].fBlendFlags & hsGMatState::kBlendInvertFinalColor ) + { + fD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ZERO ); + fD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR ); + } + else + { + fD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ZERO ); + fD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR ); + } + break; + + // Add final color to FB. + case hsGMatState::kBlendAdd: + fD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + fD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE ); + fD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE ); + + break; + + // Multiply final color by FB color and add it into the FB. + case hsGMatState::kBlendMADD: + fD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + fD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR ); + fD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE ); + + break; + + // Final color times final alpha, added into the FB. + case hsGMatState::kBlendAddColorTimesAlpha: + fD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + if( fLayerState[0].fBlendFlags & hsGMatState::kBlendInvertFinalAlpha ) + fD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_INVSRCALPHA ); + else + fD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); + fD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE ); + + break; + + // Overwrite final color onto FB + case 0: + fD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + fD3DDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE ); + fD3DDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO ); + + break; + + default: + { + hsAssert(false, "Too many blend modes specified in material"); + plLayer* lay = plLayer::ConvertNoRef(fCurrMaterial->GetLayer(fCurrLayerIdx)->BottomOfStack()); + if( lay ) + { + if( lay->GetBlendFlags() & hsGMatState::kBlendAlpha ) + { + lay->SetBlendFlags((lay->GetBlendFlags() & ~hsGMatState::kBlendMask) | hsGMatState::kBlendAlpha); + } + else + { + lay->SetBlendFlags((lay->GetBlendFlags() & ~hsGMatState::kBlendMask) | hsGMatState::kBlendAdd); + } + } + } + break; + } + } + // Blend ops, not currently used in production. + if( fLayerState[0].Differs( fLayerState[0].fBlendFlags, fOldLayerState[0].fBlendFlags, (hsGMatState::kBlendSubtract | hsGMatState::kBlendRevSubtract) ) ) + { + if( fLayerState[0].fBlendFlags & hsGMatState::kBlendSubtract ) + fD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_SUBTRACT ); + else if( fLayerState[0].fBlendFlags & hsGMatState::kBlendRevSubtract ) + fD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT ); + else + fD3DDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD ); + + } + + // AlphaTestHigh is used for reducing sort artifacts on textures that are mostly opaque or transparent, but + // have regions of translucency in transition. Like a texture for a bush billboard. It lets there be some + // transparency falloff, but quit drawing before it gets so transparent that draw order problems (halos) + // become apparent. + if( fLayerState[0].Differs( fLayerState[0].fBlendFlags, fOldLayerState[0].fBlendFlags, hsGMatState::kBlendAlphaTestHigh) ) + { + plConst(UInt32) kHighAlphaTest(0x40); + if( fLayerState[0].fBlendFlags & hsGMatState::kBlendAlphaTestHigh ) + fD3DDevice->SetRenderState(D3DRS_ALPHAREF, kHighAlphaTest); + else + fD3DDevice->SetRenderState(D3DRS_ALPHAREF, 0x00000001); + } + // Set the alpha test function, turn on for alpha blending, else off. + if( fLayerState[0].Differs( fLayerState[0].fBlendFlags, fOldLayerState[0].fBlendFlags, hsGMatState::kBlendAlpha | hsGMatState::kBlendTest | hsGMatState::kBlendAlphaAlways | hsGMatState::kBlendAddColorTimesAlpha) ) + { + if( (fLayerState[0].fBlendFlags & (hsGMatState::kBlendAlpha | hsGMatState::kBlendTest | hsGMatState::kBlendAddColorTimesAlpha)) + && !(fLayerState[0].fBlendFlags & hsGMatState::kBlendAlphaAlways) ) + fD3DDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATER ); + else + fD3DDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_ALWAYS ); + } + // Adjust the fog color based on the blend mode. Setting fog color to black for additive modes is + // an exact solution, setting it to white for multipication is as close of an approximation to correct + // as you're going to get with DX. + if( fLayerState[0].Differs( fLayerState[0].fBlendFlags, fOldLayerState[0].fBlendFlags, hsGMatState::kBlendAdd | hsGMatState::kBlendMult | hsGMatState::kBlendMADD | hsGMatState::kBlendAddColorTimesAlpha ) ) + { + if( fLayerState[0].fBlendFlags & (hsGMatState::kBlendAdd | hsGMatState::kBlendMADD | hsGMatState::kBlendAddColorTimesAlpha) ) + fD3DDevice->SetRenderState( D3DRS_FOGCOLOR, 0 ); + else if( fLayerState[0].fBlendFlags & hsGMatState::kBlendMult ) + fD3DDevice->SetRenderState( D3DRS_FOGCOLOR, 0xffffffff ); + else + fD3DDevice->SetRenderState( D3DRS_FOGCOLOR, fCurrFog.fHexColor ); + } +} + +//// IHandleTextureMode /////////////////////////////////////////////////////// +// Handle the texture stage state for the base layer. +void plDXPipeline::IHandleTextureMode(plLayerInterface* layer) +{ + plBitmap *bitmap = layer->GetTexture(); + if( bitmap ) + { + // EnvBumpNext not used in production. + if( fLayerState[0].fBlendFlags & hsGMatState::kBlendEnvBumpNext ) + { + IHandleBumpEnv(0, fLayerState[0].fBlendFlags); + } + // If the texture stage settings have changed. Note that this + // is a bad test, we should just be doing something like keeping + // an array of D3D TextureStageStates as we set them and checking against + // that directly rather than trying to infer from higher level state + // whether we need to make the D3D call. + else if( fSettings.fVeryAnnoyingTextureInvalidFlag + || !fTexturing + || ( fLayerState[ 0 ].fBlendFlags ^ fOldLayerState[0].fBlendFlags ) + || ( fCurrNumLayers + fActivePiggyBacks != fLastEndingStage ) + ) + { + // If we're only doing one layer, just modulate texture color by diffuse and we're done. + if( ( fCurrNumLayers + fActivePiggyBacks ) <= 1 ) + { + // See IHandleStageBlend for notes on NoTexColor. + if( fLayerState[0].fBlendFlags & hsGMatState::kBlendNoTexColor ) + fD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2 ); + else + fD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); + fD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, + fLayerState[0].fBlendFlags & hsGMatState::kBlendInvertColor + ? D3DTA_TEXTURE | D3DTA_COMPLEMENT + : D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); + + } + else + { + // See the check in IHandleStageBlend for fSettings.fMaxLayersAtOnce == 2. + // It depends on these settings and adjusts what it needs. + + // Multitexturing, select texture color to make its way upstream on stages. + fD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); + fD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, + fLayerState[0].fBlendFlags & hsGMatState::kBlendInvertColor + ? D3DTA_TEXTURE | D3DTA_COMPLEMENT + : D3DTA_TEXTURE); + + // If our NoTexColor setting has changed, for a refresh of blend state on the next stage + // since it's affected by our NoTexColor state. + if( fLayerState[0].Differs( fLayerState[0].fBlendFlags, fOldLayerState[0].fBlendFlags, hsGMatState::kBlendNoTexColor) ) + fLayerState[1].fBlendFlags = UInt32(-1); + } + + // Alpha Arg1 is texture alpha (possibly complemented), and Arg2 is diffuse (possibly complemented). + // If we want to ignore vertex alpha, select arg1 + // If we want to ignore texture alpha, select arg2 + // Otherwise (and normally) multiply the two. + fD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, + fLayerState[0].fBlendFlags & hsGMatState::kBlendNoVtxAlpha + ? D3DTOP_SELECTARG1 + : fLayerState[0].fBlendFlags & hsGMatState::kBlendNoTexAlpha + ? D3DTOP_SELECTARG2 + : D3DTOP_MODULATE ); + fD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, + fLayerState[0].fBlendFlags & hsGMatState::kBlendInvertAlpha + ? D3DTA_TEXTURE | D3DTA_COMPLEMENT + : D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE | + ( fLayerState[0].fBlendFlags & hsGMatState::kBlendInvertVtxAlpha + ? D3DTA_COMPLEMENT + : 0 ) ); + + fTexturing = true; + } + } + // Here we've no texture for the base layer, but we have more than layer. + // Select diffuse color and alpha, and pretend we have a texture but we're ignoring its + // color and alpha. + else if( fCurrNumLayers + fActivePiggyBacks > 1 ) + { + fLayerState[0].fBlendFlags |= hsGMatState::kBlendNoTexColor | hsGMatState::kBlendNoTexAlpha; + fD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); + if( fLayerState[0].Differs( fLayerState[0].fBlendFlags, fOldLayerState[0].fBlendFlags, (hsGMatState::kBlendNoTexColor|hsGMatState::kBlendNoTexAlpha)) ) + fLayerState[1].fBlendFlags = UInt32(-1); + fTexturing = false; + } + // Finally, a color only (non-textured) pass. Just select diffuse. + else + { + if( fTexturing || fSettings.fVeryAnnoyingTextureInvalidFlag ) + { + fD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); + fD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); + fD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_DIFFUSE ); + fD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE | + ( fLayerState[0].fBlendFlags & hsGMatState::kBlendInvertVtxAlpha ? D3DTA_COMPLEMENT : 0 ) ); + + fTexturing = false; + } + } + + fSettings.fVeryAnnoyingTextureInvalidFlag = false; +} + +//// IHandleStageClamp //////////////////////////////////////////////////////// +// Translate our current wrap/clamp mode to D3D calls. +void plDXPipeline::IHandleStageClamp(int stage) +{ + const UInt32 flags = fLayerState[stage].fClampFlags; + switch( flags ) + { + case 0: + fD3DDevice->SetSamplerState( stage, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP ); + fD3DDevice->SetSamplerState( stage, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP ); + break; + case hsGMatState::kClampTextureU: + fD3DDevice->SetSamplerState( stage, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP ); + fD3DDevice->SetSamplerState( stage, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP ); + break; + case hsGMatState::kClampTextureV: + fD3DDevice->SetSamplerState( stage, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP ); + fD3DDevice->SetSamplerState( stage, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP ); + break; + case hsGMatState::kClampTexture: + fD3DDevice->SetSamplerState( stage, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP ); + fD3DDevice->SetSamplerState( stage, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP ); + break; + } +} + +void plDXPipeline::ISetBumpMatrices(const plLayerInterface* layer, const plSpan* span) +{ +//#define BUMP_COMPARE_MATH +#ifdef BUMP_COMPARE_MATH + // This section is just debugging, to compute the matrices that will be set. + static hsMatrix44 preMDu; + static hsMatrix44 preMDv; + static hsMatrix44 preMDw; + static int preMInit = false; + if( !preMInit ) + { + hsMatrix44 rotAndCollapseToX; + int i, j; + for( i = 0; i < 4; i++ ) + { + for( j = 0; j < 4; j++ ) + { + rotAndCollapseToX.fMap[i][j] = 0; + } + } + rotAndCollapseToX.fMap[0][2] = 1.f; + rotAndCollapseToX.fMap[3][3] = 1.f; + rotAndCollapseToX.NotIdentity(); + + hsMatrix44 offset; + offset.Reset(); + offset.fMap[0][0] = 0.5f; + offset.fMap[0][3] = 0.5f; + offset.NotIdentity(); + + preMDu = offset * rotAndCollapseToX; + + offset.fMap[1][3] = 0.5f; + + preMDv = offset * rotAndCollapseToX; + + offset.fMap[1][3] = 1.f; + + preMDw = offset * rotAndCollapseToX; + + preMInit = true; + } + + hsMatrix44 localToLight = span->GetLight(0, false)->GetWorldToLight() * span->fLocalToWorld; + localToLight.fMap[0][3] = localToLight.fMap[1][3] = localToLight.fMap[2][3] = 0; + + fBumpDuMatrix = preMDu * localToLight; + fBumpDvMatrix = preMDv * localToLight; + + hsMatrix44 c2w = fView.fCameraToWorld; + hsMatrix44 cameraToLight = span->GetLight(0, false)->GetWorldToLight() * c2w; + cameraToLight.fMap[0][3] = cameraToLight.fMap[1][3] = cameraToLight.fMap[2][3] = 0; + fBumpDwMatrix = preMDw * cameraToLight; + + // HACK PART - FOR COMPARISON + hsMatrix44 bDu = fBumpDuMatrix; + hsMatrix44 bDv = fBumpDvMatrix; + hsMatrix44 bDw = fBumpDwMatrix; + static hsMatrix44 zeroMatrix; + fBumpDuMatrix = zeroMatrix; + fBumpDvMatrix = zeroMatrix; + fBumpDwMatrix = zeroMatrix; + // HACK PART - FOR COMPARISON + +#endif // BUMP_COMPARE_MATH + + // Here's the math + // The incoming uv coordinate is either: + // kMiscBumpDu - dPos/dU (in other words, the direction in space from this vertex where U increases and V remains constant) in local space. + // kMiscBumpDv - dPos/dV (in other words, the direction in space from this vertex where V increases and U remains constant) in local space. + // kMiscBumpDw - the normal in camera space. + // + // In each case, we need to transform the vector (uvw coord) into light space, and dot it with the light direction. + // Well, in light space, the light direction is always (0,0,1). + // So really, we just transform the vector into light space, and the z component is what we want. + // Then, for each of these, we take that z value (the dot product) and put it into a color channel. + // R = dPos/dU dot liDir + // G = dPos/dV dot liDir + // B = dPos/dW dot liDir + // + // That's what we want, here's how we get it. + // Here, Li(vec) means the vector in light space, Loc(vec) is local space, Tan(vec) is tangent space + // + // Li(uvw) = local2Light * Loc(uvw) (uvw comes in in local space, ie input uvw == Loc(uvw) + // Then we want to: + // a) Rotate the Z component to be along X (U) axis + // b) Zero out the new Y and Z + // c) Scale and offset our new X (the old Z) so -1 => 0, 1 => 1 (scale by 0.5, add 0.5). + // The following matrix does all this (it's just a concatenation of the above 3 simple matrices). + // M = |0 0 0.5 0.5| + // |0 0 0 0 | + // |0 0 0 0 | + // |0 0 0 1 | + // + // Our lookup texture that these transformed coords will read into has three horizontal bands, + // the bottom 3rd is a ramp along U of 0->red + // middle 3rd is a ramp along U of 0->green + // last third (highest V) is a ramp along U of 0->blue. + // So we can do the conversion from our dot to a color with an appropriate V offset in the above M. + // + // dPos/dU and dPos/dV are both input in local space, so the transform to get them into light space is + // the same for each, and that's obviously WorldToLight * LocalToWorld. + // That's a little inconvenient and inefficient. It's inconvenient, because for an omni light, we + // can easily fake a light direction (span position - light position), but the full matrix is kind + // of arbitrary. We could fake it, but instead we move on. It's inefficient because, looking at the + // form of matrix M, we know we'll be throwing away a lot of it anyway. So we work through the matrix + // math and find that we're going to wind up with: + // + // M1 = | M[0][2] * loc2li[2][0] M[0][2] * loc2li[2][1] M[0][2] * loc2li[2][2] 0.5 | + // | 0 0 0 0 | + // | 0 0 0 0 | + // | 0 0 0 1 | + // + // So all we really need is loc2li[2] (row 2). A little more matrix math gives us: + // + // loc2li[2] = (w2li[2] dot loc2wT[0], w2li[2] dot loc2wT[1], w2li[2] dot loc2wT[2]) (where loc2wT is Transpose(loc2w) + // + // And hey, that's just dependent on the light's direction w2li[2]. The same thing works out for dPos/dW, except + // substitue cam2w for loc2w (since input is in camera space instead of world space). + // + // And that's about it. We don't actually have to multiply all those matrices at run-time, because + // we know what the answer will be anyway. We just construct the matrices, making sure we set the + // appropriate translate for V to get each into the right color channel. The hardware does the three + // uv transforms and lookups, sums the results, and the output is: + // (dPos/dU dot liDir, dPos/dV dot liDir, dPos/dW dot liDir), which also happens to be the light direction + // transformed into tangent space. We dot that with our bump map (which has the normals in tangent space), + // and we've got per-pixel shading for this light direction. + + + hsPoint3 spanPos = span->fWorldBounds.GetCenter(); + hsVector3 liDir(0,0,0); + int i; + const hsTArray& spanLights = span->GetLightList(false); + hsScalar maxStrength = 0; + for( i = 0; i < spanLights.GetCount(); i++ ) + { + hsScalar liWgt = span->GetLightStrength(i, false); + // A light strength of 2.f means it's from a light group, and we haven't actually calculated + // the strength. So calculate it now. + if( liWgt == 2.f ) + { + hsScalar scale; + spanLights[i]->GetStrengthAndScale(span->fWorldBounds, liWgt, scale); + } + if( liWgt > maxStrength ) + maxStrength = liWgt; + liDir += spanLights[i]->GetNegativeWorldDirection(spanPos) * liWgt; + } + hsFastMath::NormalizeAppr(liDir); + + static hsScalar kUVWScale = 1.f; + hsScalar uvwScale = kUVWScale; + if( fLayerState[0].fBlendFlags & hsGMatState::kBlendAdd ) + { + hsVector3 cam2span(&GetViewPositionWorld(), &spanPos); + hsFastMath::NormalizeAppr(cam2span); + liDir += cam2span; + hsFastMath::NormalizeAppr(liDir); + static hsScalar kSpecularMax = 0.1f; + static hsScalar kSpecularMaxUV = 0.5f; + if (IsDebugFlagSet(plPipeDbg::kFlagBumpUV)) + uvwScale *= kSpecularMaxUV; + else + uvwScale *= kSpecularMax; + } + + switch( fCurrMaterial->GetLayer(fCurrLayerIdx)->GetMiscFlags() & hsGMatState::kMiscBumpChans ) + { + case hsGMatState::kMiscBumpDu: + uvwScale *= fCurrMaterial->GetLayer(fCurrLayerIdx+3)->GetRuntimeColor().r; + break; + case hsGMatState::kMiscBumpDv: // This currently should never happen + uvwScale *= fCurrMaterial->GetLayer(fCurrLayerIdx+1)->GetRuntimeColor().r; + break; + case hsGMatState::kMiscBumpDw: + uvwScale *= fCurrMaterial->GetLayer(fCurrLayerIdx+2)->GetRuntimeColor().r; + break; + } + maxStrength *= 20.f; + if( maxStrength > 1.f ) + maxStrength = 1.f; + liDir *= uvwScale * maxStrength; + + const hsScalar kUVWOffset = 0.5f; + + hsScalar kOffsetToRed; + hsScalar kOffsetToGreen; + hsScalar kOffsetToBlue; + + if (IsDebugFlagSet(plPipeDbg::kFlagBumpUV) || IsDebugFlagSet(plPipeDbg::kFlagBumpW)) + { + kOffsetToRed = 0.2f; + kOffsetToGreen = 0.6f; + kOffsetToBlue = 1.f; + } + else + { + kOffsetToRed = 0.f; + kOffsetToGreen = 0.4f; + kOffsetToBlue = 0.8f; + } + + const hsMatrix44& l2w = span->fLocalToWorld; + + fBumpDvMatrix.fMap[0][0] = fBumpDuMatrix.fMap[0][0] = (liDir.fX * l2w.fMap[0][0] + liDir.fY * l2w.fMap[1][0] + liDir.fZ * l2w.fMap[2][0]); + fBumpDvMatrix.fMap[0][1] = fBumpDuMatrix.fMap[0][1] = (liDir.fX * l2w.fMap[0][1] + liDir.fY * l2w.fMap[1][1] + liDir.fZ * l2w.fMap[2][1]); + fBumpDvMatrix.fMap[0][2] = fBumpDuMatrix.fMap[0][2] = (liDir.fX * l2w.fMap[0][2] + liDir.fY * l2w.fMap[1][2] + liDir.fZ * l2w.fMap[2][2]); + + fBumpDvMatrix.fMap[0][3] = fBumpDuMatrix.fMap[0][3] = kUVWOffset; + + fBumpDuMatrix.fMap[1][3] = kOffsetToRed; + fBumpDvMatrix.fMap[1][3] = kOffsetToGreen; + +#ifndef BUMP_COMPARE_MATH + hsMatrix44 c2w = fView.GetCameraToWorld(); +#endif // BUMP_COMPARE_MATH + + // The bump textures created so far have very strong blue components, which make anything + // bump mapped glow. The ideal fix would be to have the artists adjust the blue component + // to a better (lower) value, so there would be a little extra illumination where the bump + // is straight out into the normal direction, to complement the lateral illumination. + // Attempts so far have been unsuccessful in getting them to get a better understanding + // of bump maps, so I've just zeroed out the contribution in the normal direction. + plConst(int) kBumpUVOnly(true); + if( !kBumpUVOnly ) + { + fBumpDwMatrix.fMap[0][0] = (liDir.fX * c2w.fMap[0][0] + liDir.fY * c2w.fMap[1][0] + liDir.fZ * c2w.fMap[2][0]); + fBumpDwMatrix.fMap[0][1] = (liDir.fX * c2w.fMap[0][1] + liDir.fY * c2w.fMap[1][1] + liDir.fZ * c2w.fMap[2][1]); + fBumpDwMatrix.fMap[0][2] = (liDir.fX * c2w.fMap[0][2] + liDir.fY * c2w.fMap[1][2] + liDir.fZ * c2w.fMap[2][2]); + } + else + { + fBumpDwMatrix.fMap[0][0] = 0; + fBumpDwMatrix.fMap[0][1] = 0; + fBumpDwMatrix.fMap[0][2] = 0; + } + + fBumpDwMatrix.fMap[0][3] = kUVWOffset; + fBumpDwMatrix.fMap[1][3] = kOffsetToBlue; +} + +// IGetBumpMatrix /////////////////////////////////////////////////////// +// Return the correct uvw transform for the bump map channel implied +// in the miscFlags. The matrices have been previously set in ISetBumpMatrices. +const hsMatrix44& plDXPipeline::IGetBumpMatrix(UInt32 miscFlags) const +{ + switch( miscFlags & hsGMatState::kMiscBumpChans ) + { + case hsGMatState::kMiscBumpDu: + return fBumpDuMatrix; + case hsGMatState::kMiscBumpDv: + return fBumpDvMatrix; + case hsGMatState::kMiscBumpDw: + default: + return fBumpDwMatrix; + } +} + +// ISkipBumpMap ///////////////////////////////////////////////////////////////////////// +// Determine whether to skip bumpmapping on this object/material/layer combination. +// We skip if the span isn't illuminated by any lights, or bump mapping is disabled. +// If skipping, we advance past the bump layers. +// If there are no more layers after that, we return true (to abort further rendering of currSpan), +// else false to continue rendering. +hsBool plDXPipeline::ISkipBumpMap(hsGMaterial* newMat, UInt32& layer, const plSpan* currSpan) const +{ + if( newMat && currSpan ) + { + if (newMat->GetLayer(layer) + &&(newMat->GetLayer(layer)->GetMiscFlags() & hsGMatState::kMiscBumpChans) + &&(!currSpan->GetNumLights(false) || IsDebugFlagSet(plPipeDbg::kFlagNoBump)) ) + { + layer += 4; + if( layer >= newMat->GetNumLayers() ) + return true; + } + } + return false; +} + +//// IHandleStageTransform //////////////////////////////////////////////////// +// Compute and set the UVW transform to D3D. +// This only gets interesting if the transform is dependent on on the current camera transform, +// as is the case with Reflection, Projection, or bump mapping. +void plDXPipeline::IHandleStageTransform( int stage, plLayerInterface *layer ) +{ + if( 1 + || !(layer->GetTransform().fFlags & hsMatrix44::kIsIdent) + || (fLayerState[stage].fMiscFlags & (hsGMatState::kMiscUseReflectionXform|hsGMatState::kMiscUseRefractionXform|hsGMatState::kMiscProjection|hsGMatState::kMiscBumpChans)) ) + { + D3DXMATRIX tXfm; + + if( fLayerState[stage].fMiscFlags & (hsGMatState::kMiscUseReflectionXform | hsGMatState::kMiscUseRefractionXform) ) + { + // Reflection - this is just the camera to world, with translation removed, + // and rotated to match cube map conventions. + hsMatrix44 c2env = fView.GetCameraToWorld(); + c2env = fView.GetCameraToWorld(); + + c2env.fMap[0][3] + = c2env.fMap[1][3] + = c2env.fMap[2][3] + = 0.f; + + + if( fLayerState[stage].fMiscFlags & hsGMatState::kMiscUseReflectionXform ) + { + + // This is just a rotation about X of Pi/2 (y = z, z = -y), + // followed by flipping Z to reflect back towards us (z = -z). + hsScalar t = c2env.fMap[1][0]; + c2env.fMap[1][0] = c2env.fMap[2][0]; + c2env.fMap[2][0] = t; + + t = c2env.fMap[1][1]; + c2env.fMap[1][1] = c2env.fMap[2][1]; + c2env.fMap[2][1] = t; + + t = c2env.fMap[1][2]; + c2env.fMap[1][2] = c2env.fMap[2][2]; + c2env.fMap[2][2] = t; + } + else // must be kMiscUseRefractionXform + { + + // Okay, I know this refraction isn't any where near + // right, so don't sit down and try to figure out the + // math and hook it to the refractive index. + // It's just a hack that will fool anyone that isn't + // really paying attention. + + // This is just a rotation about X of Pi/2 (y = z, z = -y), + // followed by NOT flipping Z to reflect back towards us (z = -z). + // In other words, same as reflection, but then c2env = c2env * scaleMatNegateZ. + hsScalar t = c2env.fMap[1][0]; + c2env.fMap[1][0] = c2env.fMap[2][0]; + c2env.fMap[2][0] = t; + + t = c2env.fMap[1][1]; + c2env.fMap[1][1] = c2env.fMap[2][1]; + c2env.fMap[2][1] = t; + + t = c2env.fMap[1][2]; + c2env.fMap[1][2] = c2env.fMap[2][2]; + c2env.fMap[2][2] = t; + + c2env.fMap[0][2] = -c2env.fMap[0][2]; + c2env.fMap[1][2] = -c2env.fMap[1][2]; + c2env.fMap[2][2] = -c2env.fMap[2][2]; + +#if 0 + const hsScalar kFishEyeScale = 0.5f; + // You can adjust the fish-eye-ness of this by scaling + // X and Y as well. Eventually, you wind up with the same + // as c2env * scaleMatXYAndNegateZ, but this is shorter. + // kFishEyeScale gets pretty fish-eye at about 0.5, and + // like you're looking through the wrong end of a telescope + // at about 1.5. + // Ideally kFishEyeScale would be a parameter of the layer. + c2env.fMap[0][0] *= kFishEyeScale; + c2env.fMap[1][0] *= kFishEyeScale; + c2env.fMap[2][0] *= kFishEyeScale; + + c2env.fMap[0][1] *= kFishEyeScale; + c2env.fMap[1][1] *= kFishEyeScale; + c2env.fMap[2][1] *= kFishEyeScale; +#endif + } + + IMatrix44ToD3DMatrix( tXfm, c2env ); + } + // cam2Screen will also have the kMiscPerspProjection flag set, so this needs + // to go before the regular kMiscProjection check. + else if (fLayerState[stage].fMiscFlags & hsGMatState::kMiscCam2Screen ) + { + // Still needs a bit of cleaning... + static hsVector3 camScale(0.5f, -0.5f, 1.f); + static hsVector3 camTrans(0.5f, 0.5f, 0.f); + hsMatrix44 p2s; + p2s.MakeScaleMat(&camScale); + p2s.fMap[0][3] += camTrans.fX; + p2s.fMap[1][3] += camTrans.fY; + + // The scale and trans move us from NDC to Screen space. We need to swap + // the Z and W coordinates so that the texture projection will divide by W + // and give us projected 2D coordinates. + hsScalar temp = p2s.fMap[2][2]; + p2s.fMap[2][2] = p2s.fMap[3][2]; + p2s.fMap[3][2] = temp; + + temp = p2s.fMap[2][3]; + p2s.fMap[2][3] = p2s.fMap[3][3]; + p2s.fMap[3][3] = temp; + + IMatrix44ToD3DMatrix(tXfm, p2s * IGetCameraToNDC()); + } + else if( fLayerState[stage].fMiscFlags & hsGMatState::kMiscProjection ) + { + // For projection, the worldToLight transform is in the layer transform, + // so we append the cameraToWorld, getting cameraToLight + hsMatrix44 c2w = fView.GetCameraToWorld(); + if( !(layer->GetUVWSrc() & plLayerInterface::kUVWPosition) ) + { + c2w.fMap[0][3] = 0; + c2w.fMap[1][3] = 0; + c2w.fMap[2][3] = 0; + } + + // We've already stuffed the worldToLight transform into the layer. + hsMatrix44 c2l = layer->GetTransform() * c2w; + + IMatrix44ToD3DMatrix(tXfm, c2l); + } + else if( fLayerState[stage].fMiscFlags & hsGMatState::kMiscBumpChans ) + { + // Bump matrices are already set, just get the right one and stuff it in. + hsMatrix44 m = IGetBumpMatrix(fLayerState[stage].fMiscFlags); + + IMatrix44ToD3DMatrix(tXfm, m); + } + else + { + // Just put take the layer transform and stuff it in. + IMatrix44ToD3DMatrix( tXfm, layer->GetTransform() ); + } + + fD3DDevice->SetTransform( sTextureStages[ stage ], &tXfm ); + fLayerTransform[ stage ] = true; + } + else if( fLayerTransform[ stage ] ) + { + // We'd like to just turn it off, but the Voodoo board freaks if the + // texture coordinates are 3-tuple for no apparent reason. + fD3DDevice->SetTransform( sTextureStages[ stage ], &d3dIdentityMatrix ); + fLayerTransform[ stage ] = false; + } + + // If there's an lod bias associated with the layer, set it here. + // There usually isn't. + float newBias = fLayerState[stage].fZFlags & hsGMatState::kZLODBias ? layer->GetLODBias() : fTweaks.fDefaultLODBias; + if( newBias != fLayerLODBias[ stage ] ) + { + fLayerLODBias[ stage ] = newBias; + fD3DDevice->SetSamplerState( stage, D3DSAMP_MIPMAPLODBIAS, *(DWORD*)(&fLayerLODBias[ stage ]) ); + } +} + +//// IUseTextureRef /////////////////////////////////////////////////////////// +// Set the texturing flags and texture. +void plDXPipeline::IUseTextureRef( int stage, hsGDeviceRef *dRef, plLayerInterface* layer ) +{ + plDXTextureRef *ref = (plDXTextureRef *)dRef; + UInt32 xformFlags; + + UInt32 uvwSrc = layer->GetUVWSrc(); + + // Keep track of how much managed memory has been "seen" since the last + // evict, for that NVidia bug. Look for OSVERSIONINFO for more notes. + if( ref->fUseTime <= fEvictTime ) + fManagedSeen += ref->fDataSize; + + // Also used for the same thing. + if( ref->fUseTime ^ fTextUseTime ) + { + plProfile_NewMem(CurrTex, ref->fDataSize); + plProfile_Inc(NumTex); + ref->fUseTime = fTextUseTime; + + fTexUsed += ref->fDataSize; + } + + // DX pixel shaders require the TEXCOORDINDEX to be equal to the stage, + // even though its ignored. + if( layer->GetPixelShader() && (stage != uvwSrc) ) + uvwSrc = stage; + + // Update our UVW source + if( fLayerUVWSrcs[ stage ] != uvwSrc ) + { + fD3DDevice->SetTextureStageState( stage, D3DTSS_TEXCOORDINDEX, uvwSrc ); + fLayerUVWSrcs[ stage ] = uvwSrc; + } + + if (!layer->GetVertexShader() && !layer->GetPixelShader()) + { + /// Set the transform flags + /// Note: the perspective projection flag must be taken from the layer, since it's layer-specific. + /// Storing it on the texture ref is bad, because the texture ref can be shared among layers whose + /// projection flags might not match. This should probably be cleaned up, but for now this fixes the + /// problem. + if( ref->GetFlags() & plDXTextureRef::kCubicMap ) + xformFlags = D3DTTFF_COUNT3; + else if( layer->GetMiscFlags() & hsGMatState::kMiscPerspProjection ) + xformFlags = D3DTTFF_COUNT3 | D3DTTFF_PROJECTED; + else + xformFlags = D3DTTFF_COUNT2; + + if( xformFlags != fLayerXformFlags[ stage ] ) + { + fLayerXformFlags[ stage ] = xformFlags; + fD3DDevice->SetTextureStageState( stage, D3DTSS_TEXTURETRANSFORMFLAGS, xformFlags ); + } + } + + // Update our current ref + if( !ref->fD3DTexture ) + { + if( ref->fData ) + IReloadTexture( ref ); + } + else if( dRef == fLayerRef[ stage ] ) + { + return; + } + hsRefCnt_SafeAssign( fLayerRef[ stage ], dRef ); + + /// Actually make it active! + fD3DDevice->SetTexture( stage, ref->fD3DTexture ); +} + +//// IStageStop /////////////////////////////////////////////////////////////// +// Tell the hardware we won't be using any more stages. +// This is more complicated than it sounds. Cases: +// a) single texture stage, we're done (because we've already set +// texture times diffuse), so just disable stage 1. +// b) we have 2 stages active. +// b.0) we're skipping texture color on one of those 2 stages. In that +// case, we've already modulated in our diffuse, so just +// disable stage 2. +// b.1) we're using texture color from both stages 0 and 1, and still need +// to modulate in diffuse. So set stage 2 to modulate in diffuse, +// and disable stage 3. +// c) we have 3 or more stages active. Append a modulation by diffuse +// Note that this only applies to color, because diffuse alpha is always modulated +// in from the start. +void plDXPipeline::IStageStop( UInt32 stage ) +{ + int disableStage = stage; + + // Note: even if we don't have a texture, we handle it similar to if we had one, + // so the only special case we need here is if we only had one stage to set up -mcn + if( ( stage <= 1 ) ) + { + fD3DDevice->SetTextureStageState(stage, D3DTSS_COLOROP, D3DTOP_DISABLE); + fD3DDevice->SetTextureStageState(stage, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + fLayerState[ stage ].fBlendFlags = UInt32(-1); + disableStage = stage; + } + else if( stage == 2 ) + { + // The fMaxLayersAtOnce == 2 check is for the DX9.0c 2 texture limitation. + // See ILayersAtOnce() + if ((fLayerState[0].fBlendFlags & hsGMatState::kBlendNoTexColor) + || (fLayerState[1].fBlendFlags & hsGMatState::kBlendNoTexColor) + || fSettings.fMaxLayersAtOnce == 2) + { + fD3DDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE); + disableStage = 2; + } + else + { + fD3DDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_MODULATE); + fD3DDevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_DIFFUSE); + fD3DDevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_CURRENT); + + fD3DDevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE); + disableStage = 3; + } + + fD3DDevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + fLayerState[2].fBlendFlags = UInt32(-1); + fLayerState[3].fBlendFlags = UInt32(-1); + } + else + { + // This is directly contrary to the DX documentation, but in line with + // the code generated by MFCTex (which works). The docs say: + // "Alpha operations cannot be disabled when color operations are enabled. + // Setting the alpha operation to D3DTOP_DISABLE when color blending + // is enabled causes undefined behavior." + // But not disabling the earliest possible alpha stage causes the driver + // to choke. + + + fD3DDevice->SetTextureStageState(stage, D3DTSS_COLOROP, D3DTOP_MODULATE); + fD3DDevice->SetTextureStageState(stage, D3DTSS_COLORARG1, D3DTA_DIFFUSE); + fD3DDevice->SetTextureStageState(stage, D3DTSS_COLORARG2, D3DTA_CURRENT); + + fD3DDevice->SetTextureStageState(stage, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + fLayerState[stage].fBlendFlags = UInt32(-1); + + fD3DDevice->SetTextureStageState(stage+1, D3DTSS_COLOROP, D3DTOP_DISABLE); + fLayerState[stage+1].fBlendFlags = UInt32(-1); + + disableStage = stage+1; + } + + fLastEndingStage = stage; + + if( fSettings.fIsIntel ) + { + int maxUVW = 0; + int k; + for( k = 0; k < fCurrNumLayers; k++ ) + { + if( (fCurrMaterial->GetLayer(k + fCurrLayerIdx)->GetUVWSrc() & 0xf) > maxUVW ) + maxUVW = fCurrMaterial->GetLayer(k + fCurrLayerIdx)->GetUVWSrc() & 0xf; + } + for( k = disableStage; k <= maxUVW; k++ ) + { + fD3DDevice->SetTextureStageState(k, D3DTSS_COLOROP, D3DTOP_SELECTARG2); + fD3DDevice->SetTextureStageState(k, D3DTSS_COLORARG2, D3DTA_CURRENT); + } + fD3DDevice->SetTextureStageState(k, D3DTSS_COLOROP, D3DTOP_DISABLE); + } +} + +// IInvalidateState ///////////////////////////////////////////////////////////// +// Documentation is unclear on what state persists or becomes invalid on switching +// a render target or finishing a frame. I put into this function things that show +// up as suspect, whether they "ought" to be here or not. +void plDXPipeline::IInvalidateState() +{ + fLastEndingStage = 0; + fTexturing = false; + int i; + for( i = 0; i < 8; i++ ) + { + hsRefCnt_SafeUnRef( fLayerRef[ i ] ); + fLayerRef[ i ] = nil; + fD3DDevice->SetTexture( i, nil ); + } + + fLayerState[ 0 ].fZFlags = 0; + fD3DDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL ); + fD3DDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE ); + + // This is a workaround for the latest ATI drivers (6.14.10.6422). + // They seem to be caching something on lights (possibly only specular + // lights, but I haven't been able to prove it) the first time they + // are used in a render, and then not letting go when the camera + // is moved for another render in the same frame (same BeginScene/EndScene pair). + // The effect is very incorrect lighting. Moreover, if the multiple renders + // per frame are infrequent (e.g. refreshing an environment map every few + // seconds), you'll get flashes after the double render frames. + // Workaround is to Disable all lights at render target switch, although + // a more correct workaround might be to disable all lights at camera move. + // All of this is strictly conjecture, so I'm going with what works. + // Note also that I'm only disabling lights that are currently enabled + // at the time of the render target switch. Since this is dealing with + // a driver bug, it might be safer to disable them all, but timings + // show that looping through all the lights in a scene like Teledahn exterior, + // with hundreds of active lights, incurs a measurable expense (some milliseconds), + // whereas disabling only the active lights fixes the known problem but costs + // zero. + plProfile_BeginTiming(ClearLights); + + hsBitIterator iterOff(fLights.fEnabledFlags); + for( iterOff.Begin(); !iterOff.End(); iterOff.Advance() ) + fD3DDevice->LightEnable(iterOff.Current(), false); + fLights.fEnabledFlags.Clear(); + fLights.fHoldFlags.Clear(); + + plProfile_EndTiming(ClearLights); + + // This is very annoying. Set fTexturing to false doesn't work if the next layer + // we draw doesn't have a texture. So we have to set this flag instead to force + // a state update. I have an idea about how to do all of this a lot better, but + // it's not time to do it...not yet at least.... --mcn + fSettings.fVeryAnnoyingTextureInvalidFlag = true; +} + +//// ILayersAtOnce //////////////////////////////////////////////////////////// +// Compute how many of the upcoming layers we can render in a single pass on the +// current hardware. +UInt32 plDXPipeline::ILayersAtOnce( hsGMaterial *mat, UInt32 which ) +{ + fCurrNumLayers = 1; + + if( fView.fRenderState & plPipeline::kRenderBaseLayerOnly ) + return fCurrNumLayers; + + plLayerInterface *lay = mat->GetLayer( which ); + + if (IsDebugFlagSet(plPipeDbg::kFlagNoMultitexture)) + return fCurrNumLayers; + + if ((IsDebugFlagSet(plPipeDbg::kFlagBumpUV) || IsDebugFlagSet(plPipeDbg::kFlagBumpW)) && (lay->GetMiscFlags() & hsGMatState::kMiscBumpChans) ) + return fCurrNumLayers = 2; + + if( (lay->GetBlendFlags() & hsGMatState::kBlendNoColor) + ||(lay->GetMiscFlags() & hsGMatState::kMiscTroubledLoner) + ) + return fCurrNumLayers; + + // New DX9.0c limitation for cards that can only do 2 textures per pass. + // We used to be able to set stage 0 and 1 to textures, and set stage 2 to the + // diffuse color. With DX9.0c we just get two texture stages. Period. + // Either we give up a texture or the diffuse color. + if (fSettings.fMaxLayersAtOnce == 2) + { + if ((mat->GetNumLayers() > which + 1) + && !(mat->GetLayer(which + 1)->GetBlendFlags() & hsGMatState::kBlendNoTexColor)) + { + // If we're just using the texture for alpha, we can multiply + // the diffuse color in stage 1. Otherwise, save it for the next pass. + return fCurrNumLayers; + } + } + + int i; + int maxLayersAtOnce = fSettings.fMaxLayersAtOnce; + + // Now Reserve space for piggy backs, and see if there are + // are any more layers we can pick up. + // + maxLayersAtOnce = fSettings.fMaxLayersAtOnce - fActivePiggyBacks; + if( which + maxLayersAtOnce > mat->GetNumLayers() ) + maxLayersAtOnce = mat->GetNumLayers() - which; + + for( i = fCurrNumLayers; i < maxLayersAtOnce; i++ ) + { + plLayerInterface *lay = mat->GetLayer(which + i); + if( (lay->GetUVWSrc() & 0xf) > fSettings.fMaxUVWSrc ) + break; + if( (lay->GetMiscFlags() & hsGMatState::kMiscBindNext) + &&(i+1 >= maxLayersAtOnce) ) + break; + if( lay->GetMiscFlags() & hsGMatState::kMiscRestartPassHere ) + break; + if( !(mat->GetLayer(which+i-1)->GetMiscFlags() & hsGMatState::kMiscBindNext) + && !ICanEatLayer(lay) ) + break; + fCurrNumLayers++; + } + return fCurrNumLayers; +} + +//// ICanEatLayer ///////////////////////////////////////////////////////////// +// Determine if this layer can be an upper layer, or if it needs +// to be the base on another pass. +hsBool plDXPipeline::ICanEatLayer( plLayerInterface* lay ) +{ + if( !lay->GetTexture() ) + return false; + + + if( (lay->GetBlendFlags() & hsGMatState::kBlendNoColor) + ||(lay->GetBlendFlags() & hsGMatState::kBlendAddColorTimesAlpha) // has to be base layer + ||(lay->GetMiscFlags() & hsGMatState::kMiscTroubledLoner) ) + return false; + + if( (lay->GetBlendFlags() & hsGMatState::kBlendAlpha ) + &&(lay->GetAmbientColor().a < hsScalar1) ) + return false; + + if( !(lay->GetZFlags() & hsGMatState::kZNoZWrite) ) + return false; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Textures ///////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// IReloadTexture /////////////////////////////////////////////////////////// +// Fills in D3D texture resource, creating it if necessary. +void plDXPipeline::IReloadTexture( plDXTextureRef *ref ) +{ + if( ref->GetFlags() & plDXTextureRef::kCubicMap ) + { + if( ref->fD3DTexture == nil ) + ref->fD3DTexture = IMakeD3DCubeTexture( ref, ref->fFormatType ); + + if( ref->fD3DTexture != nil ) + IFillD3DCubeTexture( (plDXCubeTextureRef *)ref ); + } + else + { + if( ref->fD3DTexture == nil ) + ref->fD3DTexture = IMakeD3DTexture( ref, ref->fFormatType ); + + if( ref->fD3DTexture != nil ) + IFillD3DTexture( ref ); + } +} + +//// IMakeD3DTexture ////////////////////////////////////////////////////////// +// Makes a DX Texture object based on the ref given. + +IDirect3DTexture9 *plDXPipeline::IMakeD3DTexture( plDXTextureRef *ref, D3DFORMAT formatType ) +{ + D3DPOOL poolType = D3DPOOL_MANAGED; + IDirect3DTexture9 *texPtr; + fManagedAlloced = true; + if( FAILED( fSettings.fDXError = fD3DDevice->CreateTexture( ref->fMaxWidth, ref->fMaxHeight, + ref->fMMLvs, + 0, + formatType, + poolType, + &texPtr, NULL ) ) ) + { + IGetD3DError(); + plStatusLog::AddLineS( "pipeline.log", 0xffff0000, "Unable to create texture (%s) Owner: %s " + "Size: %d x %d NumLvls: %d Flags: %x", + fSettings.fErrorStr, ref->fOwner ? ref->fOwner->GetKey() ? ref->fOwner->GetKey()->GetUoid().GetObjectName() : "" : "", + ref->fMaxWidth, ref->fMaxHeight, ref->fMMLvs, ref->GetFlags() ); + return nil; + } + PROFILE_POOL_MEM(poolType, ref->fDataSize, true, (ref->fOwner ? ref->fOwner->GetKey() ? ref->fOwner->GetKey()->GetUoid().GetObjectName() : "(UnknownTexture)" : "(UnknownTexture)")); + fTexManaged += ref->fDataSize; + + return texPtr; +} + +//// IFillD3DTexture ////////////////////////////////////////////////////////// +// Copies the data from the ref into the D3D texture, filling in all +// mip levels. +void plDXPipeline::IFillD3DTexture( plDXTextureRef *ref ) +{ + int i; + UInt8 *pTexDat = (UInt8 *)ref->fData; + + + if( pTexDat == nil ) + { + plStatusLog::AddLineS( "pipeline.log", 0xffff0000, "Unable to fill texture ref (data is nil) Owner: %s", + ref->fOwner ? ref->fOwner->GetKey() ? ref->fOwner->GetKey()->GetUoid().GetObjectName() : "" : "" ); + return; + } + + IDirect3DTexture9 *lpDst = (IDirect3DTexture9 *)ref->fD3DTexture; + + for( i = 0; i < ref->fMMLvs; i++ ) + { + D3DLOCKED_RECT lockInfo; + + if( FAILED( fSettings.fDXError = lpDst->LockRect( i, &lockInfo, nil, 0 ) ) ) + { + IGetD3DError(); + plStatusLog::AddLineS( "pipeline.log", 0xffff0000, "Unable to lock texture level %d for filling (%s) Owner: %s " + "Size: %d x %d NumLvls: %d Flags: %x", + i, fSettings.fErrorStr, ref->fOwner ? ref->fOwner->GetKey() ? ref->fOwner->GetKey()->GetUoid().GetObjectName() : "" : "", + ref->fMaxWidth, ref->fMaxHeight, ref->fMMLvs, ref->GetFlags() ); + return; + } + + memcpy( (char *)lockInfo.pBits, pTexDat, ref->fLevelSizes[ i ] ); + pTexDat += ref->fLevelSizes[ i ]; + lpDst->UnlockRect( i ); + } +} + +//// IMakeD3DCubeTexture ////////////////////////////////////////////////////// +// Makes a DX Cubic Texture object based on the ref given. + +IDirect3DCubeTexture9 *plDXPipeline::IMakeD3DCubeTexture( plDXTextureRef *ref, D3DFORMAT formatType ) +{ + D3DPOOL poolType = D3DPOOL_MANAGED; + IDirect3DCubeTexture9 *texPtr = nil; + fManagedAlloced = true; + WEAK_ERROR_CHECK(fD3DDevice->CreateCubeTexture( ref->fMaxWidth, ref->fMMLvs, 0, formatType, poolType, &texPtr, NULL)); + PROFILE_POOL_MEM(poolType, ref->fDataSize, true, (ref->fOwner ? ref->fOwner->GetKey() ? ref->fOwner->GetKey()->GetUoid().GetObjectName() : "(UnknownTexture)" : "(UnknownTexture)")); + fTexManaged += ref->fDataSize; + return texPtr; +} + +//// IFillD3DCubeTexture ////////////////////////////////////////////////////// +// Fill in all faces of the D3D cube map from the input reference. +void plDXPipeline::IFillD3DCubeTexture( plDXCubeTextureRef *ref ) +{ + int i, f; + D3DCUBEMAP_FACES faces[ 6 ] = { D3DCUBEMAP_FACE_NEGATIVE_X, // Left + D3DCUBEMAP_FACE_POSITIVE_X, // Right + D3DCUBEMAP_FACE_POSITIVE_Z, // Front + D3DCUBEMAP_FACE_NEGATIVE_Z, // Back + D3DCUBEMAP_FACE_POSITIVE_Y, // Top + D3DCUBEMAP_FACE_NEGATIVE_Y }; // Bottom + + for( f = 0; f < 6; f++ ) + { + UInt8 *pTexDat = ( f == 0 ) ? (UInt8 *)ref->fData : (UInt8 *)ref->fFaceData[ f - 1 ]; + IDirect3DCubeTexture9 *lpDst = (IDirect3DCubeTexture9 *)ref->fD3DTexture; + + for( i = 0; i < ref->fMMLvs; i++ ) + { + D3DLOCKED_RECT lockInfo; + + lpDst->LockRect( faces[ f ], i, &lockInfo, nil, 0 ); + memcpy( (char *)lockInfo.pBits, pTexDat, ref->fLevelSizes[ i ] ); + pTexDat += ref->fLevelSizes[ i ]; + lpDst->UnlockRect( faces[ f ], i ); + } + } +} + +//// MakeTextureRef /////////////////////////////////////////////////////////// +// Creates a hsGDeviceRef for a texture. +// May have to decompress the texture if the hardware doesn't support compressed textures (unlikely). +hsGDeviceRef *plDXPipeline::MakeTextureRef( plLayerInterface* layer, plMipmap *b ) +{ + plMipmap *original = b, *colorized = nil; + + // If the hardware doesn't support Luminance maps, we'll just treat as ARGB. + if( !( fSettings.fD3DCaps & kCapsLuminanceTextures ) ) + b->SetFlags( b->GetFlags() & ~plMipmap::kIntensityMap ); + + /// Colorize if we're supposed to (8.21.2000 mcn) + // Debugging only. + if (IsDebugFlagSet(plPipeDbg::kFlagColorizeMipmaps)) + { + b = original->Clone(); + if( b != nil ) + b->Colorize(); + else + b = original; + } + + if( !( fSettings.fD3DCaps & kCapsCompressTextures ) && b->IsCompressed() ) + b = hsCodecManager::Instance().CreateUncompressedMipmap( b, hsCodecManager::k16BitDepth ); + + /// Set up some stuff + UInt32 mmlvs = 1; + D3DFORMAT formatType = D3DFMT_UNKNOWN; // D3D Format + UInt32 formatSize = 0; + UInt32 totalSize = 0; + UInt32* levelSizes = nil; + UInt32 numPix = 0; + UInt32 externData = false; + void *tData; + hsBool noMip = !(fSettings.fD3DCaps & kCapsMipmap); + + + /// Convert the bitmap over + // Select a target format + IGetD3DTextureFormat( b, formatType, formatSize ); + + // Process the texture data into a format that can be directly copied to the D3D texture. + // externData returned as true means that tData just points directly into the mipmap's fImage, + // so don't delete it when deleting the texture device ref. externData false means this is + // a reformatted copy, so the ref owns it. + externData = IProcessMipmapLevels( b, mmlvs, levelSizes, totalSize, numPix, tData, noMip ); + + // If the texture has a device ref, just re-purpose it, else make one and initialize it. + plDXTextureRef *ref = (plDXTextureRef *)b->GetDeviceRef(); + if( !ref ) + { + ref = TRACKED_NEW plDXTextureRef( formatType, + mmlvs, b->GetWidth(), b->GetHeight(), + numPix, totalSize, totalSize, levelSizes, + tData, externData ); + ref->fOwner = original; + ref->Link( &fTextureRefList ); + original->SetDeviceRef( ref ); + // Note: this is because SetDeviceRef() will ref it, and at this point, + // only the bitmap should own the ref, not us. We ref/unref it on Use() + hsRefCnt_SafeUnRef( ref ); + } + else + ref->Set( formatType, mmlvs, b->GetWidth(), b->GetHeight(), + numPix, totalSize, totalSize, levelSizes, tData, externData ); + + // Keep the refs in a linked list for easy disposal. + if( !ref->IsLinked() ) + { + // Re-linking + ref->Link( &fTextureRefList ); + } + + /// Copy the data into the ref + IReloadTexture( ref ); + + ref->fData = nil; + ref->SetDirty( false ); + + // Set any implied flags. + if (layer) + { + if( layer->GetMiscFlags() & hsGMatState::kMiscPerspProjection ) + ref->SetFlags(ref->GetFlags() | plDXTextureRef::kPerspProjection); + else if( layer->GetMiscFlags() & hsGMatState::kMiscOrthoProjection ) + ref->SetFlags(ref->GetFlags() | plDXTextureRef::kOrthoProjection); + + if( layer->GetMiscFlags() & hsGMatState::kMiscBumpDw ) + ref->SetFlags(ref->GetFlags() | plDXTextureRef::kUVWNormal); + } + + if( b != original ) + delete b; // Delete if we created a new (temporary) one + + // Turn this on to delete the plasma system memory copy once we have a D3D managed version. + // Currently disabled, because there are still mipmaps that are read from after their managed + // versions are created, but aren't flagged DontThrowAwayImage or kUserOwnesBitmap. + if( !( original->GetFlags() & ( plMipmap::kUserOwnsBitmap | plMipmap::kDontThrowAwayImage ) ) + && !GetProperty( kPropDontDeleteTextures ) ) + { +#ifdef MF_TOSSER + original->Reset(); +#endif // MF_TOSSER + } + + return ref; +} + +//// IMakeCubicTextureRef ///////////////////////////////////////////////////// +// Same as MakeTextureRef, except done for the six faces of a cube map. +hsGDeviceRef *plDXPipeline::IMakeCubicTextureRef( plLayerInterface* layer, plCubicEnvironmap *cubic ) +{ + plDXCubeTextureRef *ref; + plMipmap *faces[ 6 ]; + int i; + D3DFORMAT formatType = D3DFMT_UNKNOWN; + UInt32 formatSize = 0; + UInt32 numLevels = 1; + UInt32 totalSize = 0; + UInt32 *levelSizes = nil; + UInt32 numPixels = 0; + UInt32 externData; + void *textureData[ 6 ]; + + if( cubic == nil || !( fSettings.fD3DCaps & kCapsCubicTextures ) ) + return nil; + + + hsBool noMip = !(fSettings.fD3DCaps & kCapsMipmap) || !(fSettings.fD3DCaps & kCapsCubicMipmap); + + /// Get the mips + if( !( fSettings.fD3DCaps & kCapsCompressTextures ) ) + { + for( i = 0; i < 6; i++ ) + { + faces[ i ] = cubic->GetFace( i ); + if( faces[ i ]->IsCompressed() ) + faces[ i ] = hsCodecManager::Instance().CreateUncompressedMipmap( faces[ i ], hsCodecManager::k16BitDepth ); + } + } + else + { + for( i = 0; i < 6; i++ ) + faces[ i ] = cubic->GetFace( i ); + } + + /// Create the ref + // Get format + IGetD3DTextureFormat( faces[0], formatType, formatSize ); + + // Process the data. + if( faces[0]->IsCompressed() || ( faces[0]->GetPixelSize() < 32 ) ) + { + /// For this, we just take the image data pointers directly, so only call IProcess once + externData = IProcessMipmapLevels( faces[ 0 ], numLevels, levelSizes, totalSize, numPixels, textureData[ 0 ], noMip ); + for( i = 1; i < 6; i++ ) + textureData[ i ] = faces[ i ]->GetImage(); + } + else + { + for( i = 0; i < 6; i++ ) + { + /// Some of this will be redundant, but oh well + externData = IProcessMipmapLevels( faces[ i ], numLevels, levelSizes, totalSize, numPixels, textureData[ i ], noMip ); + } + } + + ref = (plDXCubeTextureRef *)cubic->GetDeviceRef(); + if( !ref ) + { + ref = TRACKED_NEW plDXCubeTextureRef( formatType, + numLevels, faces[ 0 ]->GetWidth(), faces[ 0 ]->GetHeight(), + numPixels, totalSize, totalSize * 6, levelSizes, + textureData[ 0 ], externData ); + ref->fOwner = cubic; + ref->Link( &fTextureRefList ); // So we don't ref later on down + for( i = 0; i < 5; i++ ) + ref->fFaceData[ i ] = textureData[ i + 1 ]; + + cubic->SetDeviceRef( ref ); + // Note: this is because SetDeviceRef() will ref it, and at this point, + // only the bitmap should own the ref, not us. We ref/unref it on Use() + hsRefCnt_SafeUnRef( ref ); + } + else + { + ref->Set( formatType, numLevels, faces[ 0 ]->GetWidth(), faces[ 0 ]->GetHeight(), + numPixels, totalSize, totalSize * 6, levelSizes, textureData[ 0 ], externData ); + + for( i = 0; i < 5; i++ ) + ref->fFaceData[ i ] = textureData[ i + 1 ]; + } + ref->SetFlags( ref->GetFlags() | plDXTextureRef::kCubicMap ); + + // Put in linked list for easy disposal. + if( !ref->IsLinked() ) + { + // Re-linking + ref->Link( &fTextureRefList ); + } + + /// Copy the data into the ref + IReloadTexture( ref ); + ref->SetDirty( false ); + + /// Cleanup + for( i = 0; i < 6; i++ ) + { + if( faces[ i ] != cubic->GetFace( i ) ) + delete faces[ i ]; + if( !( cubic->GetFace(i)->GetFlags() & (plMipmap::kUserOwnsBitmap | plMipmap::kDontThrowAwayImage) ) && !GetProperty( kPropDontDeleteTextures ) ) + { + // Turn this on to delete the plasma system memory copy once we have a D3D managed version. + // Currently disabled, because there are still mipmaps that are read from after their managed + // versions are created, but aren't flagged DontThrowAwayImage or kUserOwnesBitmap. +// cubic->GetFace(i)->Reset(); + } + } + + return ref; +} + +//// IProcessMipmapLevels ///////////////////////////////////////////////////// +// Compute proper values for the arguments passed in. +// Return true if the data returned points directly into the mipmap data, +// return false if textureData is a reformatted copy of the mipmap's data. +hsBool plDXPipeline::IProcessMipmapLevels( plMipmap *mipmap, UInt32 &numLevels, + UInt32 *&levelSizes, UInt32 &totalSize, + UInt32 &numPixels, void *&textureData, hsBool noMip ) +{ + hsBool externData = false; + D3DFORMAT formatType = D3DFMT_UNKNOWN; // D3D Format + UInt32 formatSize; + + + IGetD3DTextureFormat( mipmap, formatType, formatSize ); + + // Compressed or 16 bit, we can use directly. + if( mipmap->IsCompressed() || ( mipmap->GetPixelSize() < 32 ) ) + { + numPixels = 0; + if( noMip ) + { + numLevels = 1; + levelSizes = nil; + totalSize = mipmap->GetLevelSize(0); + } + else + { + UInt32 sizeMask = 0x03; + + /// 10.31.2000 - If we have this flag set, we really have to cut out + /// sizes under 8x8. So far only true on the KYRO... + if( fSettings.fD3DCaps & kCapsNoKindaSmallTexs ) + sizeMask = 0x07; + + int maxLevel = mipmap->GetNumLevels() - 1; + + /// 9.7.2000 - Also do this test if the card doesn't support + /// itty bitty textures + if( mipmap->IsCompressed() || !( fSettings.fD3DCaps & kCapsDoesSmallTextures ) ) + { + mipmap->SetCurrLevel( maxLevel ); + while( ( mipmap->GetCurrWidth() | mipmap->GetCurrHeight() ) & sizeMask ) + { + maxLevel--; + hsAssert( maxLevel >= 0, "How was this ever compressed?" ); + mipmap->SetCurrLevel( maxLevel ); + } + } + + mipmap->SetCurrLevel( 0 ); + totalSize = 0; + numLevels = maxLevel + 1; + levelSizes = TRACKED_NEW UInt32[ numLevels ]; + int i; + for( i = 0; i < numLevels; i++ ) + { + levelSizes[ i ] = mipmap->GetLevelSize( i ); + totalSize += mipmap->GetLevelSize( i ); + } + } + + textureData = mipmap->GetImage(); + externData = true; + } + else + { + // 32 bit uncompressed data. In general, we reformat to 16 bit if we're running + // 16 bit, or if 32 bit leave it at 32. All subject to what the hardware can do + // and what the texture is for. See IGetD3DTextureFormat. + formatSize >>= 3; + + if( !noMip ) + { + numPixels = mipmap->GetTotalSize() * 8 / mipmap->GetPixelSize(); + numLevels = mipmap->GetNumLevels(); + + levelSizes = TRACKED_NEW UInt32[ numLevels ]; + + int i; + UInt32 w, h; + for( i = 0; i < numLevels; i++ ) + { + mipmap->GetLevelPtr( i, &w, &h ); + levelSizes[ i ] = w * h * formatSize; + } + } + else + { + numPixels = mipmap->GetWidth() * mipmap->GetHeight(); + numLevels = 1; + levelSizes = nil; + } + totalSize = numPixels * formatSize; + + // Shared scratch space to reformat a texture before it's copied into + // the D3D surface. + textureData = IGetPixelScratch( totalSize ); + + // Convert it to the requested format. + IFormatTextureData( formatType, numPixels, (hsRGBAColor32 *)mipmap->GetImage(), textureData ); + } + + return externData; +} + +//// IGetPixelScratch ///////////////////////////////////////////////////////// +// Return scratch space at least of at least size bytes, to reformat a mipmap into. +void *plDXPipeline::IGetPixelScratch( UInt32 size ) +{ + static char *sPtr = nil; + static UInt32 sSize = 0; + + if( size > sSize ) + { + if( sPtr != nil ) + delete [] sPtr; + + if( size > 0 ) + sPtr = TRACKED_NEW char[ sSize = size ]; + else + sPtr = nil; + } + else if( size == 0 ) + { + if( sPtr != nil ) + delete [] sPtr; + + sPtr = nil; + sSize = 0; + } + + return sPtr; +} + +//// IGetD3DTextureFormat ///////////////////////////////////////////////////// +// Given a bitmap, finds the matching D3D format. + +void plDXPipeline::IGetD3DTextureFormat( plBitmap *b, D3DFORMAT &formatType, UInt32& texSize ) +{ + hsAssert( b, "Nil input to GetTextureFormat()" ); + + hsBool prefer32bit = 0 != (b->GetFlags() & plBitmap::kForce32Bit); + + if( b->IsCompressed() ) + { + hsAssert( plMipmap::kDirectXCompression == b->fCompressionType, "Unsupported compression format" ); + texSize = 0; + switch( b->fDirectXInfo.fCompressionType ) + { + case plMipmap::DirectXInfo::kDXT1: + formatType = D3DFMT_DXT1; + break; +// case plMipmap::DirectXInfo::kDXT2: +// formatType = D3DFMT_DXT2; +// break; +// case plMipmap::DirectXInfo::kDXT3: +// formatType = D3DFMT_DXT3; +// break; +// case plMipmap::DirectXInfo::kDXT4: +// formatType = D3DFMT_DXT4; +// break; + case plMipmap::DirectXInfo::kDXT5: + formatType = D3DFMT_DXT5; + break; + default: + hsAssert(false, "Unknown DirectX compression format"); + } + } + else if( b->GetFlags() & plMipmap::kBumpEnvMap ) + { + texSize = 16; + if( b->GetFlags() & plMipmap::kAlphaChannelFlag ) + formatType = D3DFMT_L6V5U5; + else + formatType = D3DFMT_V8U8; + } + else if( b->GetPixelSize() == 16 ) + { + texSize = 16; + if( b->GetFlags() & plMipmap::kIntensityMap ) + { + if( b->GetFlags() & plMipmap::kAlphaChannelFlag ) + formatType = D3DFMT_A8L8; + else + formatType = D3DFMT_L8; + } + else + { + if( b->GetFlags() & plMipmap::kAlphaChannelFlag ) + formatType = D3DFMT_A4R4G4B4; + else + formatType = D3DFMT_A1R5G5B5; + } + } + else if( b->GetFlags() & plMipmap::kIntensityMap ) + { + if( b->GetFlags() & plMipmap::kAlphaChannelFlag ) + { + if( ITextureFormatAllowed( D3DFMT_A8L8 ) ) + { + formatType = D3DFMT_A8L8; + texSize = 16; + } + else if( !prefer32bit && ( fSettings.fColorDepth == 16 ) && ITextureFormatAllowed( D3DFMT_A4R4G4B4 ) ) + { + formatType = D3DFMT_A4R4G4B4; + texSize = 16; + } + else if( ITextureFormatAllowed( D3DFMT_A8R8G8B8 ) ) + { + formatType = D3DFMT_A8R8G8B8; + texSize = 32; + } + else if( ITextureFormatAllowed( D3DFMT_A4R4G4B4 ) ) + { + formatType = D3DFMT_A4R4G4B4; + texSize = 16; + } + } + else + { + if( ITextureFormatAllowed( D3DFMT_L8 ) ) + { + formatType = D3DFMT_L8; + texSize = 8; + } + else if( !prefer32bit && ( fSettings.fColorDepth == 16 ) && ITextureFormatAllowed( D3DFMT_A1R5G5B5 ) ) + { + formatType = D3DFMT_A1R5G5B5; + texSize = 16; + } + else if( ITextureFormatAllowed( D3DFMT_A8R8G8B8 ) ) + { + formatType = D3DFMT_A8R8G8B8; + texSize = 32; + } + else if( ITextureFormatAllowed( D3DFMT_A1R5G5B5 ) ) + { + formatType = D3DFMT_A1R5G5B5; + texSize = 16; + } + } + } + else + { + if( b->GetFlags() & plMipmap::kAlphaChannelFlag ) + { + if( !prefer32bit && ( fSettings.fColorDepth == 16 ) && ITextureFormatAllowed( D3DFMT_A4R4G4B4 ) ) + { + formatType = D3DFMT_A4R4G4B4; + texSize = 16; + } + else if( ITextureFormatAllowed( D3DFMT_A8R8G8B8 ) ) + { + formatType = D3DFMT_A8R8G8B8; + texSize = 32; + } + else if( ITextureFormatAllowed( D3DFMT_A4R4G4B4 ) ) + { + formatType = D3DFMT_A4R4G4B4; + texSize = 16; + } + } + else + { + if( !prefer32bit && ( fSettings.fColorDepth == 16 ) && ITextureFormatAllowed( D3DFMT_A1R5G5B5 ) ) + { + formatType = D3DFMT_A1R5G5B5; + texSize = 16; + } + else if( ITextureFormatAllowed( D3DFMT_A8R8G8B8 ) ) + { + formatType = D3DFMT_A8R8G8B8; + texSize = 32; + } + else if( ITextureFormatAllowed( D3DFMT_A1R5G5B5 ) ) + { + formatType = D3DFMT_A1R5G5B5; + texSize = 16; + } + } + } + + hsAssert( formatType, "failing to find format type" ); +} + +//// IFormatTextureData /////////////////////////////////////////////////////// +// Convert the input 32 bit uncompressed RGBA data into the requested format. +void plDXPipeline::IFormatTextureData( UInt32 formatType, UInt32 numPix, hsRGBAColor32* const src, void *dst ) +{ + switch( formatType ) + { + case D3DFMT_L6V5U5: + { + UInt16 *pixels = (UInt16 *)dst; + hsRGBAColor32* p = src; + hsRGBAColor32* end = src + numPix; + + while( p < end ) + { + *pixels = ((p->a << 8) & 0xfc00) + | ((p->g << 2) & 0x03e0) + | ((p->r >> 3) & 0x001f); +#ifdef HS_DEBUGGING + if( *pixels & 0xfc00 ) + pixels++; + else if( *pixels & 0x03e0 ) + pixels++; + else if( *pixels & 0x001f ) + pixels++; + else +#endif // HS_DEBUGGING + pixels++; + p++; + } + } + break; + + case D3DFMT_V8U8: + { + UInt16 *pixels = (UInt16 *)dst; + hsRGBAColor32* p = src; + hsRGBAColor32* end = src + numPix; + + while( p < end ) + { + *pixels = (p->g << 8) + | (p->r << 0); + pixels++; + p++; + } + } + break; + + case D3DFMT_A8L8: + { + UInt16 *pixels = (UInt16 *)dst; + int i; + hsRGBAColor32* const p = src; + + for(i =0; i < numPix; i++) + pixels[i]= ((p[i].a & 0xff) << 8) | (p[i].r & 0xff); + } + break; + + case D3DFMT_A4R4G4B4: + { + UInt16 *pixels = (UInt16 *)dst; + int i; + hsRGBAColor32* const p = src; + + for(i =0; i < numPix; i++) + { + pixels[i]= (((p[i].r>>4) & 0xf) << 8) + | (((p[i].g >> 4) & 0xf) << 4) + | (((p[i].b >> 4) & 0xf) ) + | (((p[i].a >> 4) & 0xf) << 12); + } + } + break; + + case D3DFMT_A1R5G5B5: + { + UInt16 *pixels = (UInt16 *)dst; + int i; + hsRGBAColor32* const p = src; + + for(i =0; i < numPix; i++) + { + pixels[i]= (((p[i].r>>3) & 0x1f) << 10) | + (((p[i].g >> 3) & 0x1f) << 5) | + ((p[i].b >> 3) & 0x1f) | ((p[i].a == 0) ? 0 : 0x8000); + } + } + break; + + case D3DFMT_L8: + { + UInt8 *pixels = (UInt8 *)dst; + int i; + hsRGBAColor32* const p = src; + + for(i =0; i < numPix; i++) + pixels[i]= p[i].r; + } + break; + + case D3DFMT_A8R8G8B8: + { + UInt32 *pixels = (UInt32 *)dst; + int i; + hsRGBAColor32* const p = src; + + for(i =0; i < numPix; i++) + pixels[i]= ( ( p[i].a << 24 ) | ( p[i].r << 16 ) | ( p[i].g << 8 ) | p[i].b ); + } + break; + + default: + hsAssert(false, "Unknown texture format selected"); + break; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +//// View Stuff /////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +//// TestVisibleWorld ///////////////////////////////////////////////////////// +// Check if the world space bounds are visible within the current view frustum. +hsBool plDXPipeline::TestVisibleWorld( const hsBounds3Ext& wBnd ) +{ + if( fView.fCullTreeDirty ) + IRefreshCullTree(); + return fView.fCullTree.BoundsVisible(wBnd); +} + +hsBool plDXPipeline::TestVisibleWorld( const plSceneObject* sObj ) +{ + const plDrawInterface* di = sObj->GetDrawInterface(); + if( !di ) + return false; + + const int numDraw = di->GetNumDrawables(); + int i; + for( i = 0; i < numDraw; i++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(i)); + if( !dr ) + continue; + + plDISpanIndex& diIndex = dr->GetDISpans(di->GetDrawableMeshIndex(i)); + if( diIndex.IsMatrixOnly() ) + continue; + + const int numSpan = diIndex.GetCount(); + int j; + for( j = 0; j < numSpan; j++ ) + { + const plSpan* span = dr->GetSpan(diIndex[j]); + + if( span->fProps & plSpan::kPropNoDraw ) + continue; + + if( !span->GetVisSet().Overlap(plGlobalVisMgr::Instance()->GetVisSet()) + || span->GetVisSet().Overlap(plGlobalVisMgr::Instance()->GetVisNot()) ) + + continue; + + if( !TestVisibleWorld(span->fWorldBounds) ) + continue; + + return true; + } + } + return false; +} + +//// GetViewAxesWorld ///////////////////////////////////////////////////////// +// Get the current view direction, up and direction X up. +void plDXPipeline::GetViewAxesWorld(hsVector3 axes[3] /* ac,up,at */ ) const +{ + axes[ 0 ] = GetViewAcrossWorld(); + axes[ 1 ] = GetViewUpWorld(); + axes[ 2 ] = GetViewDirWorld(); +} + +//// GetFOV /////////////////////////////////////////////////////////////////// +// Get the current FOV in degrees. +void plDXPipeline::GetFOV(hsScalar& fovX, hsScalar& fovY) const +{ + fovX = GetViewTransform().GetFovXDeg(); + fovY = GetViewTransform().GetFovYDeg(); +} + +//// SetFOV /////////////////////////////////////////////////////////////////// +// Set the current FOV in degrees. Forces perspective rendering to be true. +void plDXPipeline::SetFOV( hsScalar fovX, hsScalar fovY ) +{ + IGetViewTransform().SetFovDeg(fovX, fovY); + IGetViewTransform().SetPerspective(true); +} + +// Get the orthogonal projection view size in world units (e.g. feet). +void plDXPipeline::GetSize( hsScalar& width, hsScalar& height ) const +{ + width = GetViewTransform().GetScreenWidth(); + height = GetViewTransform().GetScreenHeight(); +} + +// Set the orthogonal projection view size in world units (e.g. feet). +// Forces projection to orthogonal if it wasn't. +void plDXPipeline::SetSize( hsScalar width, hsScalar height ) +{ + IGetViewTransform().SetWidth(width); + IGetViewTransform().SetHeight(height); + IGetViewTransform().SetOrthogonal(true); +} + +//// GetDepth ///////////////////////////////////////////////////////////////// +// Get the current hither and yon. +void plDXPipeline::GetDepth(hsScalar& hither, hsScalar& yon) const +{ + GetViewTransform().GetDepth(hither, yon); +} + +//// SetDepth ///////////////////////////////////////////////////////////////// +// Set the current hither and yon. +void plDXPipeline::SetDepth(hsScalar hither, hsScalar yon) +{ + IGetViewTransform().SetDepth(hither, yon); +} + +//// ISavageYonHack /////////////////////////////////////////////////////////// +// Corrects the yon for the *#(&$*#&$(*& Savage4 chipset (ex. Diamond Stealth +// III S540). Let's just say this card SUCKS. +// Obsolete since we don't support the Savage4 chipset any more. +void plDXPipeline::ISavageYonHack() +{ + hsScalar yon = GetViewTransform().GetYon(); + + + if( ( yon > 128.f - 5.0f ) && ( yon < 128.f + 1.01f ) ) + yon = 128.f + 1.01f; + else if( ( yon > 256.f - 10.0f ) && ( yon < 256.f + 1.02f ) ) + yon = 256.f + 1.02f; + else if( ( yon > 512.f - 35.0f ) && ( yon < 512.f + 1.02f ) ) + yon = 512.f + 1.02f; + else if( ( yon > 1024.f - 120.0f ) && ( yon < 1024.f + 1.f ) ) + yon = 1024.f + 1.f; +} + +//// GetWorldToCamera ///////////////////////////////////////////////////////// +// Return current world to camera transform. +const hsMatrix44& plDXPipeline::GetWorldToCamera() const +{ + return fView.GetWorldToCamera(); +} + +//// GetCameraToWorld ///////////////////////////////////////////////////////// +// Return current camera to world transform. +const hsMatrix44& plDXPipeline::GetCameraToWorld() const +{ + return fView.GetCameraToWorld(); +} + +// IUpdateViewFlags ///////////////////////////////////////////////////////// +// Dirty anything cached dependent on the current camera matrix. +void plDXPipeline::IUpdateViewFlags() +{ + fView.fCullTreeDirty = true; + + fView.fWorldToCamLeftHanded = fView.GetWorldToCamera().GetParity(); +} +//// SetWorldToCamera ///////////////////////////////////////////////////////// +// Immediate set of camera transform. +void plDXPipeline::SetWorldToCamera(const hsMatrix44& w2c, const hsMatrix44& c2w) +{ + IGetViewTransform().SetCameraTransform(w2c, c2w); + + IUpdateViewFlags(); + + IWorldToCameraToD3D(); +} + +// IWorldToCameraToD3D /////////////////////////////////////////////////////// +// Pass the current camera transform through to D3D. +void plDXPipeline::IWorldToCameraToD3D() +{ + D3DXMATRIX mat; + + IMatrix44ToD3DMatrix( mat, fView.GetWorldToCamera() ); + fD3DDevice->SetTransform( D3DTS_VIEW, &mat ); + + fView.fXformResetFlags &= ~fView.kResetCamera; + + fFrame++; +} + +// SetViewTransform /////////////////////////////////////////////////////////// +// ViewTransform encapsulates everything about the current camera, viewport and +// window necessary to render or convert from world space to pixel space. Doesn't +// include the object dependent local to world transform. +// Set plViewTransform.h +void plDXPipeline::SetViewTransform(const plViewTransform& v) +{ + fView.fTransform = v; + + if( !v.GetScreenWidth() || !v.GetScreenHeight() ) + { + fView.fTransform.SetScreenSize((UInt16)(fSettings.fOrigWidth), (UInt16)(fSettings.fOrigHeight)); + } + + IUpdateViewFlags(); + + IWorldToCameraToD3D(); +} + +//// GetWorldToLocal ////////////////////////////////////////////////////////// +// Return current World to Local transform. Note that this is only meaningful while an +// object is being rendered, so this function is pretty worthless. +const hsMatrix44& plDXPipeline::GetWorldToLocal() const +{ + return fView.fWorldToLocal; +} + +//// GetLocalToWorld ////////////////////////////////////////////////////////// +// Return current Local to World transform. Note that this is only meaningful while an +// object is being rendered, so this function is pretty worthless. + +const hsMatrix44& plDXPipeline::GetLocalToWorld() const +{ + return fView.fLocalToWorld; +} + +//// ISetLocalToWorld ///////////////////////////////////////////////////////// +// Record and pass on to D3D the current local to world transform for the object +// about to be rendered. +void plDXPipeline::ISetLocalToWorld( const hsMatrix44& l2w, const hsMatrix44& w2l ) +{ + + fView.fLocalToWorld = l2w; + fView.fWorldToLocal = w2l; + + fView.fViewVectorsDirty = true; + + // We keep track of parity for winding order culling. + fView.fLocalToWorldLeftHanded = fView.fLocalToWorld.GetParity(); + + ILocalToWorldToD3D(); +} + +// ILocalToWorldToD3D /////////////////////////////////////////////////////////// +// pass the current local to world tranform on to D3D. +void plDXPipeline::ILocalToWorldToD3D() +{ + D3DXMATRIX mat; + + if( fView.fLocalToWorld.fFlags & hsMatrix44::kIsIdent ) + fD3DDevice->SetTransform( D3DTS_WORLD, &d3dIdentityMatrix ); + else + { + IMatrix44ToD3DMatrix( mat, fView.fLocalToWorld ); + fD3DDevice->SetTransform( D3DTS_WORLD, &mat ); + } + + fView.fXformResetFlags &= ~fView.kResetL2W; +} + +//// IIsViewLeftHanded //////////////////////////////////////////////////////// +// Returns true if the combination of the local2world and world2camera +// matrices is left-handed. + +hsBool plDXPipeline::IIsViewLeftHanded() +{ + return fView.fTransform.GetOrthogonal() ^ ( fView.fLocalToWorldLeftHanded ^ fView.fWorldToCamLeftHanded ) ? true : false; +} + +//// ScreenToWorldPoint /////////////////////////////////////////////////////// +// Given a screen space pixel position, and a world space distance from the camera, return a +// full world space position. I.e. cast a ray through a screen pixel dist feet, and where +// is it. +void plDXPipeline::ScreenToWorldPoint( int n, UInt32 stride, Int32 *scrX, Int32 *scrY, hsScalar dist, UInt32 strideOut, hsPoint3 *worldOut ) +{ + while( n-- ) + { + hsPoint3 scrP; + scrP.Set(float(*scrX++), float(*scrY++), float(dist)); + *worldOut++ = GetViewTransform().ScreenToWorld(scrP); + } +} + +// IRefreshCullTree //////////////////////////////////////////////////////////////////// +// The cull tree captures the view frustum and any occluders in the scene into a single +// BSP tree. See plCullTree.h. It must be recomputed any time the camera moves. +void plDXPipeline::IRefreshCullTree() +{ + if( fView.fCullTreeDirty ) + { + plProfile_BeginTiming(DrawOccBuild); + + fView.fCullTree.Reset(); + + fView.fCullTree.SetViewPos(GetViewPositionWorld()); + + if (fCullProxy && !IsDebugFlagSet(plPipeDbg::kFlagOcclusionSnap)) + { + fCullProxy->GetKey()->UnRefObject(); + fCullProxy = nil; + SetDrawableTypeMask(GetDrawableTypeMask() & ~plDrawable::kOccSnapProxy); + } + hsBool doCullSnap = IsDebugFlagSet(plPipeDbg::kFlagOcclusionSnap)&& !fCullProxy && !fSettings.fViewStack.GetCount(); + if( doCullSnap ) + { + fView.fCullTree.BeginCapturePolys(); + fView.fCullTree.SetVisualizationYon(GetViewTransform().GetYon()); + } + fView.fCullTree.InitFrustum(GetViewTransform().GetWorldToNDC()); + fView.fCullTreeDirty = false; + + if( fView.fCullMaxNodes ) + { + int i; + for( i = 0; i < fCullPolys.GetCount(); i++ ) + { + fView.fCullTree.AddPoly(*fCullPolys[i]); + if( fView.fCullTree.GetNumNodes() >= fView.fCullMaxNodes ) + break; + } + fCullPolys.SetCount(0); + plProfile_Set(OccPolyUsed, i); + + for( i = 0; i < fCullHoles.GetCount(); i++ ) + { + fView.fCullTree.AddPoly(*fCullHoles[i]); + } + fCullHoles.SetCount(0); + plProfile_Set(OccNodeUsed, fView.fCullTree.GetNumNodes()); + } + if( doCullSnap ) + { + fView.fCullTree.EndCapturePolys(); + IMakeOcclusionSnap(); + } + + plProfile_EndTiming(DrawOccBuild); + } +} + +// IMakeOcclusionSnap ///////////////////////////////////////////////////////////////////// +// Debugging visualization tool only. Takes a snapshot of the current occlusion +// BSP tree and renders it until told to stop. +void plDXPipeline::IMakeOcclusionSnap() +{ + hsTArray& pos = fView.fCullTree.GetCaptureVerts(); + hsTArray& norm = fView.fCullTree.GetCaptureNorms(); + hsTArray& color = fView.fCullTree.GetCaptureColors(); + hsTArray& tris = fView.fCullTree.GetCaptureTris(); + + if( tris.GetCount() ) + { + hsMatrix44 ident; + ident.Reset(); + + hsGMaterial* mat = TRACKED_NEW hsGMaterial; + hsgResMgr::ResMgr()->NewKey( "OcclusionSnapMat", mat, plLocation::kGlobalFixedLoc ); + plLayer *lay = mat->MakeBaseLayer(); + lay->SetZFlags(hsGMatState::kZNoZWrite); + lay->SetPreshadeColor(hsColorRGBA().Set(1.f, 0.5f, 0.5f, 1.f)); + lay->SetRuntimeColor(hsColorRGBA().Set(1.f, 0.5f, 0.5f, 1.f)); + lay->SetAmbientColor(hsColorRGBA().Set(0,0,0,1.f)); + lay->SetOpacity(0.5f); + lay->SetBlendFlags(lay->GetBlendFlags() | hsGMatState::kBlendAlpha); + + fCullProxy = plDrawableGenerator::GenerateDrawable(pos.GetCount(), + pos.AcquireArray(), + norm.AcquireArray(), + nil, + 0, + color.AcquireArray(), + true, + nil, + tris.GetCount(), + tris.AcquireArray(), + mat, + ident, + true, + nil, + nil); + + if( fCullProxy ) + { + fCullProxy->GetKey()->RefObject(); + fCullProxy->SetType(plDrawable::kOccSnapProxy); + + SetDrawableTypeMask(GetDrawableTypeMask() | plDrawable::kOccSnapProxy); + + fCullProxy->PrepForRender(this); + } + } + fView.fCullTree.ReleaseCapture(); +} + +// SubmitOccluders ///////////////////////////////////////////////////////////// +// Add the input polys into the list of polys from which to generate the cull tree. +hsBool plDXPipeline::SubmitOccluders(const hsTArray& polyList) +{ + fCullPolys.SetCount(0); + fCullHoles.SetCount(0); + int i; + for( i = 0; i < polyList.GetCount(); i++ ) + { + if( polyList[i]->IsHole() ) + fCullHoles.Append(polyList[i]); + else + fCullPolys.Append(polyList[i]); + } + fView.fCullTreeDirty = true; + + return true; +} + +//// RefreshScreenMatrices //////////////////////////////////////////////////// +// Force a refresh of cached state when the projection matrix changes. +void plDXPipeline::RefreshScreenMatrices() +{ + fView.fCullTreeDirty = true; + IProjectionMatrixToD3D(); +} + +//// RefreshMatrices ////////////////////////////////////////////////////////// +// Just a wrapper + +void plDXPipeline::RefreshMatrices() +{ + RefreshScreenMatrices(); +} + + + + +/////////////////////////////////////////////////////////////////////////////// +//// Overrides //////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// PushOverrideMaterial ///////////////////////////////////////////////////// +// Push a material to be used instead of the material associated with objects +// for rendering. +// Must be matched with a PopOverrideMaterial. +hsGMaterial *plDXPipeline::PushOverrideMaterial( hsGMaterial *mat ) +{ + hsGMaterial *ret = GetOverrideMaterial(); + hsRefCnt_SafeRef( mat ); + fOverrideMat.Push( mat ); + fForceMatHandle = true; + + return ret; +} + +//// PopOverrideMaterial ////////////////////////////////////////////////////// +// Stop overriding with the current override material. +// Must match a preceding PushOverrideMaterial. +void plDXPipeline::PopOverrideMaterial( hsGMaterial *restore ) +{ + hsGMaterial *pop = fOverrideMat.Pop(); + hsRefCnt_SafeUnRef( pop ); + + if( fCurrMaterial == pop ) + { + fForceMatHandle = true; + } +} + +//// GetOverrideMaterial ////////////////////////////////////////////////////// +// Return the current override material, or nil if there isn't any. +hsGMaterial *plDXPipeline::GetOverrideMaterial() const +{ + return fOverrideMat.GetCount() ? fOverrideMat.Peek() : nil; +} + +//// GetMaterialOverrideOn //////////////////////////////////////////////////// +// Return the current bits set to be always on for the given category (e.g. ZFlags). +UInt32 plDXPipeline::GetMaterialOverrideOn( hsGMatState::StateIdx category ) const +{ + return fMatOverOn.Value(category); +} + +//// GetMaterialOverrideOff /////////////////////////////////////////////////// +// Return the current bits set to be always off for the given category (e.g. ZFlags). +UInt32 plDXPipeline::GetMaterialOverrideOff( hsGMatState::StateIdx category ) const +{ + return fMatOverOff.Value(category); +} + +//// PushMaterialOverride ///////////////////////////////////////////////////// +// Force material state bits on or off. If you use this, save the return value +// as input to PopMaterialOverride, to restore previous values. +hsGMatState plDXPipeline::PushMaterialOverride( const hsGMatState& state, hsBool on ) +{ + hsGMatState ret = GetMaterialOverride( on ); + if( on ) + { + fMatOverOn |= state; + fMatOverOff -= state; + } + else + { + fMatOverOff |= state; + fMatOverOn -= state; + } + fForceMatHandle = true; + return ret; +} + +// PushMaterialOverride /////////////////////////////////////////////////////// +// Force material state bits on or off. If you use this, save the return value +// as input to PopMaterialOverride, to restore previous values. +// This version just sets for one category (e.g. Z flags). +hsGMatState plDXPipeline::PushMaterialOverride(hsGMatState::StateIdx cat, UInt32 which, hsBool on) +{ + hsGMatState ret = GetMaterialOverride( on ); + if( on ) + { + fMatOverOn[ cat ] |= which; + fMatOverOff[ cat ] &= ~which; + } + else + { + fMatOverOn[ cat ] &= ~which; + fMatOverOff[ cat ] |= which; + } + fForceMatHandle = true; + return ret; +} + +//// PopMaterialOverride ////////////////////////////////////////////////////// +// Restore the previous settings returned from the matching PushMaterialOverride. +void plDXPipeline::PopMaterialOverride(const hsGMatState& restore, hsBool on) +{ + if( on ) + { + fMatOverOn = restore; + fMatOverOff.Clear( restore ); + } + else + { + fMatOverOff = restore; + fMatOverOn.Clear( restore ); + } + fForceMatHandle = true; +} + +//// GetMaterialOverride ////////////////////////////////////////////////////// +// Return the current material state bits force to on or off, depending on input . +const hsGMatState& plDXPipeline::GetMaterialOverride(hsBool on) const +{ + return on ? fMatOverOn : fMatOverOff; +} + +//// PushColorOverride ////////////////////////////////////////////////// +// Obsolete and unused. +hsColorOverride plDXPipeline::PushColorOverride(const hsColorOverride& over) +{ + hsColorOverride ret = GetColorOverride(); + PopColorOverride( over ); + return ret; +} + +// PopColorOverride //////////////////////////////////////////////////////// +// Obsolete and unused. +void plDXPipeline::PopColorOverride(const hsColorOverride& restore) +{ + return; +/* + hsColorOverride cpy = restore; + if( !(cpy.fFlags & hsColorOverride::kModAlpha) ) + cpy.fColor.a = 1.f; + if( !(cpy.fFlags & (hsColorOverride::kModAlpha | hsColorOverride::kModColor)) ) + fDev->SetColorNormal(); + else + fDev->SetColorOverride(cpy.fColor, !(cpy.fFlags & hsColorOverride::kModColor)); +*/ +} + +//// GetColorOverride ///////////////////////////////////////////////////////// +// Obsolete and unused. +const hsColorOverride& plDXPipeline::GetColorOverride() const +{ + static hsColorOverride ret; + return ret; + +/* ret.fFlags = hsColorOverride::kNone; + if( fDev->GetDebugFlags() & hsG3DDevice::kDeviceColor ) + ret.fFlags |= hsColorOverride::kModColor; + if( fDev->GetDebugFlags() & hsG3DDevice::kDeviceAlpha ) + ret.fFlags |= hsColorOverride::kModAlpha; + + ret.fColor = fDev->GetColorOverride(); +*/ + return ret; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Transforms /////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// IMatrix44ToD3DMatrix ///////////////////////////////////////////////////// +// Make a D3DXMATRIX matching the input plasma matrix. Mostly a transpose. +D3DXMATRIX& plDXPipeline::IMatrix44ToD3DMatrix( D3DXMATRIX& dst, const hsMatrix44& src ) +{ + if( src.fFlags & hsMatrix44::kIsIdent ) + { + dst = d3dIdentityMatrix; + } + else + { + dst(0,0) = src.fMap[0][0]; + dst(1,0) = src.fMap[0][1]; + dst(2,0) = src.fMap[0][2]; + dst(3,0) = src.fMap[0][3]; + + dst(0,1) = src.fMap[1][0]; + dst(1,1) = src.fMap[1][1]; + dst(2,1) = src.fMap[1][2]; + dst(3,1) = src.fMap[1][3]; + + dst(0,2) = src.fMap[2][0]; + dst(1,2) = src.fMap[2][1]; + dst(2,2) = src.fMap[2][2]; + dst(3,2) = src.fMap[2][3]; + + dst(0,3) = src.fMap[3][0]; + dst(1,3) = src.fMap[3][1]; + dst(2,3) = src.fMap[3][2]; + dst(3,3) = src.fMap[3][3]; + } + + return dst; +} + +///////////////////////////////////////////////////////// +// IGetCameraToNDC ///////////////////////////////////////////// +// Get the camera to NDC transform. This may be adjusted to create +// a Z bias towards the camera for cases where the D3D Z bias fails us. +hsMatrix44 plDXPipeline::IGetCameraToNDC() +{ + hsMatrix44 cam2ndc = GetViewTransform().GetCameraToNDC(); + + if( fView.IsPerspective() ) + { + // Want to scale down W and offset in Z without + // changing values of x/w, y/w. This is just + // minimal math for + // Mproj' * p = Mscaletrans * Mproj * p + // where Mscaletrans = + // [ s 0 0 0 ] + // [ 0 s 0 0 ] + // [ 0 0 s 0 ] + // [ 0 0 t s ] + // Resulting matrix Mproj' is not exactly "Fog Friendly", + // but is close enough. + // Resulting point is [sx, sy, sz + tw, sw] and after divide + // is [x/w, y/w, z/w + t/s, 1/sw] + + + if( fSettings.fD3DCaps & kCapsWBuffer ) + { + // W-buffering is only true w-buffering on 3dfx cards. On everything else, + // they REALLY base it off the Z value. So we want to scale (but NOT translate) + // the Z... + // Note: the base value for perspLayerScale should be 0.001 for w-buffering, + // not the normal 0.00001 + float scale = 1.f - float(fCurrRenderLayer) * fTweaks.fPerspLayerScale; + + cam2ndc.fMap[0][0] *= scale; + cam2ndc.fMap[1][1] *= scale; + cam2ndc.fMap[2][2] *= scale; + cam2ndc.fMap[3][2] *= scale; + } + else + { + // Z-buffering, so do it the traditional way + float scale = 1.f - float(fCurrRenderLayer) * fTweaks.fPerspLayerScale; +// scale = -1.f; + float zTrans = -scale * float(fCurrRenderLayer) * fTweaks.fPerspLayerTrans; + + cam2ndc.fMap[0][0] *= scale; + cam2ndc.fMap[1][1] *= scale; + + cam2ndc.fMap[2][2] *= scale; + cam2ndc.fMap[2][2] += zTrans * cam2ndc.fMap[3][2]; + cam2ndc.fMap[3][2] *= scale; + } + } + else + { + plConst(float) kZTrans = -1.e-4f; + cam2ndc.fMap[2][3] += kZTrans * fCurrRenderLayer; + } + + return cam2ndc; +} + +// IProjectionMatrixToD3D ////////////////////////////////////////////////////////// +// Send the current camera to NDC transform to D3D. +void plDXPipeline::IProjectionMatrixToD3D() +{ + D3DXMATRIX matProjection; + + IMatrix44ToD3DMatrix( matProjection, IGetCameraToNDC() ); + + fD3DDevice->SetTransform( D3DTS_PROJECTION, &matProjection ); + fView.fXformResetFlags &= ~fView.kResetProjection; +} + +//// ISetCullMode ///////////////////////////////////////////////////////////// +// Tests and sets the current winding order cull mode (CW, CCW, or none). +// Will reverse the cull mode as necessary for left handed camera or local to world +// transforms. +void plDXPipeline::ISetCullMode(hsBool flip) +{ + D3DCULL newCull = D3DCULL_NONE; + + if( !(fLayerState[0].fMiscFlags & hsGMatState::kMiscTwoSided) ) + newCull = !IIsViewLeftHanded() ^ !flip ? D3DCULL_CW : D3DCULL_CCW; + + if( newCull != fCurrCullMode ) + { + fCurrCullMode = newCull; + fD3DDevice->SetRenderState( D3DRS_CULLMODE, fCurrCullMode ); + } +} + +//// ITransformsToD3D ////////////////////////////////////////////////////////// +// Refreshes all transforms. Useful after popping renderTargets :) + +void plDXPipeline::ITransformsToD3D() +{ + hsBool resetCullMode = fView.fXformResetFlags & (fView.kResetCamera | fView.kResetL2W); + + if( fView.fXformResetFlags & fView.kResetCamera ) + IWorldToCameraToD3D(); + + if( fView.fXformResetFlags & fView.kResetL2W ) + ILocalToWorldToD3D(); + + if( fView.fXformResetFlags & fView.kResetProjection ) + IProjectionMatrixToD3D(); +} + +// ISetupVertexBufferRef ///////////////////////////////////////////////////////// +// Initialize input vertex buffer ref according to source. +void plDXPipeline::ISetupVertexBufferRef(plGBufferGroup* owner, UInt32 idx, plDXVertexBufferRef* vRef) +{ + // Initialize to nil, in case something goes wrong. + vRef->fD3DBuffer = nil; + + UInt8 format = owner->GetVertexFormat(); + + // All indexed skinning is currently done on CPU, so the source data + // will have indices, but we strip them out for the D3D buffer. + if( format & plGBufferGroup::kSkinIndices ) + { + format &= ~(plGBufferGroup::kSkinWeightMask | plGBufferGroup::kSkinIndices); + format |= plGBufferGroup::kSkinNoWeights; // Should do nothing, but just in case... + vRef->SetSkinned(true); + vRef->SetVolatile(true); + } + + UInt32 vertSize = IGetBufferFormatSize(format); // vertex stride + UInt32 numVerts = owner->GetVertBufferCount(idx); + + vRef->fDevice = fD3DDevice; + + vRef->fOwner = owner; + vRef->fCount = numVerts; + vRef->fVertexSize = vertSize; + vRef->fFormat = format; + vRef->fRefTime = 0; + + vRef->SetDirty(true); + vRef->SetRebuiltSinceUsed(true); + vRef->fData = nil; + + vRef->SetVolatile(vRef->Volatile() || owner->AreVertsVolatile()); + + vRef->fIndex = idx; + + owner->SetVertexBufferRef(idx, vRef); + hsRefCnt_SafeUnRef(vRef); +} + +// ICheckStaticVertexBuffer /////////////////////////////////////////////////////////////////////// +// Ensure a static vertex buffer has any D3D resources necessary for rendering created and filled +// with proper vertex data. +void plDXPipeline::ICheckStaticVertexBuffer(plDXVertexBufferRef* vRef, plGBufferGroup* owner, UInt32 idx) +{ + hsAssert(!vRef->Volatile(), "Creating a managed vertex buffer for a volatile buffer ref"); + + if( !vRef->fD3DBuffer ) + { + // Okay, haven't done this one. + + DWORD fvfFormat = IGetBufferD3DFormat(vRef->fFormat); + + + D3DPOOL poolType = D3DPOOL_MANAGED; +// DWORD usage = D3DUSAGE_WRITEONLY; + DWORD usage = 0; + const int numVerts = vRef->fCount; + const int vertSize = vRef->fVertexSize; + fManagedAlloced = true; + if( FAILED( fD3DDevice->CreateVertexBuffer( numVerts * vertSize, + usage, + fvfFormat, + poolType, + &vRef->fD3DBuffer, NULL) ) ) + { + hsAssert( false, "CreateVertexBuffer() call failed!" ); + vRef->fD3DBuffer = nil; + return; + } + PROFILE_POOL_MEM(poolType, numVerts * vertSize, true, "VtxBuff"); + + // Record that we've allocated this into managed memory, in case we're + // fighting that NVidia driver bug. Search for OSVERSION for mor info. + AllocManagedVertex(numVerts * vertSize); + + // Fill in the vertex data. + IFillStaticVertexBufferRef(vRef, owner, idx); + + // This is currently a no op, but this would let the buffer know it can + // unload the system memory copy, since we have a managed version now. + owner->PurgeVertBuffer(idx); + } +} + +// IFillStaticVertexBufferRef ////////////////////////////////////////////////// +// BufferRef is set up, just copy the data in. +// This is uglied up hugely by the insane non-interleaved data case with cells +// and whatever else. +void plDXPipeline::IFillStaticVertexBufferRef(plDXVertexBufferRef *ref, plGBufferGroup *group, UInt32 idx) +{ + IDirect3DVertexBuffer9* vertexBuff = ref->fD3DBuffer; + + if( !vertexBuff ) + { + // We most likely already warned about this earlier, best to just quietly return now + return; + } + + const UInt32 vertSize = ref->fVertexSize; + const UInt32 vertStart = group->GetVertBufferStart(idx) * vertSize; + const UInt32 size = group->GetVertBufferEnd(idx) * vertSize - vertStart; + if( !size ) + return; + + /// Lock the buffer + UInt8* ptr; + if( FAILED( vertexBuff->Lock( vertStart, size, (void **)&ptr, group->AreVertsVolatile() ? D3DLOCK_DISCARD : 0 ) ) ) + { + hsAssert( false, "Failed to lock vertex buffer for writing" ); + } + + if( ref->fData ) + { + memcpy(ptr, ref->fData + vertStart, size); + } + else + { + hsAssert(0 == vertStart, "Offsets on non-interleaved data not supported"); + hsAssert(group->GetVertBufferCount(idx) * vertSize == size, "Trailing dead space on non-interleaved data not supported"); + + const UInt32 vertSmallSize = group->GetVertexLiteStride() - sizeof( hsPoint3 ) * 2; + UInt8* srcVPtr = group->GetVertBufferData(idx); + plGBufferColor* const srcCPtr = group->GetColorBufferData( idx ); + + const int numCells = group->GetNumCells(idx); + int i; + for( i = 0; i < numCells; i++ ) + { + plGBufferCell *cell = group->GetCell( idx, i ); + + if( cell->fColorStart == (UInt32)-1 ) + { + /// Interleaved, do straight copy + memcpy( ptr, srcVPtr + cell->fVtxStart, cell->fLength * vertSize ); + ptr += cell->fLength * vertSize; + } + else + { + /// Separated, gotta interleave + UInt8* tempVPtr = srcVPtr + cell->fVtxStart; + plGBufferColor* tempCPtr = srcCPtr + cell->fColorStart; + int j; + for( j = 0; j < cell->fLength; j++ ) + { + memcpy( ptr, tempVPtr, sizeof( hsPoint3 ) * 2 ); + ptr += sizeof( hsPoint3 ) * 2; + tempVPtr += sizeof( hsPoint3 ) * 2; + + memcpy( ptr, &tempCPtr->fDiffuse, sizeof( UInt32 ) ); + ptr += sizeof( UInt32 ); + memcpy( ptr, &tempCPtr->fSpecular, sizeof( UInt32 ) ); + ptr += sizeof( UInt32 ); + + memcpy( ptr, tempVPtr, vertSmallSize ); + ptr += vertSmallSize; + tempVPtr += vertSmallSize; + tempCPtr++; + } + } + } + } + + /// Unlock and clean up + vertexBuff->Unlock(); + ref->SetRebuiltSinceUsed(true); + ref->SetDirty(false); +} + +// OpenAccess //////////////////////////////////////////////////////////////////////////////////////// +// Lock the managed buffer and setup the accessSpan to point into the buffers data. +hsBool plDXPipeline::OpenAccess(plAccessSpan& dst, plDrawableSpans* drawable, const plVertexSpan* span, hsBool readOnly) +{ + plGBufferGroup* grp = drawable->GetBufferGroup(span->fGroupIdx); + hsAssert(!grp->AreVertsVolatile(), "Don't ask for D3DBuffer data on a volatile buffer"); + + plDXVertexBufferRef* vRef = (plDXVertexBufferRef*)grp->GetVertexBufferRef(span->fVBufferIdx); + if( !vRef ) + { + dst.SetType(plAccessSpan::kUndefined); + return false; + } + + IDirect3DVertexBuffer9* vertexBuff = vRef->fD3DBuffer; + if( !vertexBuff ) + { + dst.SetType(plAccessSpan::kUndefined); + return false; + } + + const UInt32 stride = vRef->fVertexSize; + const UInt32 vertStart = span->fVStartIdx * stride; + const UInt32 size = span->fVLength * stride; + + if( !size ) + { + dst.SetType(plAccessSpan::kUndefined); + return false; + } + + DWORD lockFlags = readOnly ? D3DLOCK_READONLY : 0; + + UInt8* ptr; + if( FAILED( vertexBuff->Lock(vertStart, size, (void **)&ptr, lockFlags) ) ) + { + hsAssert( false, "Failed to lock vertex buffer for writing" ); + dst.SetType(plAccessSpan::kUndefined); + return false; + } + + plAccessVtxSpan& acc = dst.AccessVtx(); + + acc.SetVertCount((UInt16)(span->fVLength)); + + Int32 offset = (-(Int32)(span->fVStartIdx)) * ((Int32)stride); + + acc.PositionStream(ptr, (UInt16)stride, offset); + ptr += sizeof(hsPoint3); + + int numWgts = grp->GetNumWeights(); + if( numWgts ) + { + acc.SetNumWeights(numWgts); + acc.WeightStream(ptr, (UInt16)stride, offset); + ptr += numWgts * sizeof(hsScalar); + if( grp->GetVertexFormat() & plGBufferGroup::kSkinIndices ) + { + acc.WgtIndexStream(ptr, (UInt16)stride, offset); + ptr += sizeof(UInt32); + } + else + { + acc.WgtIndexStream(nil, 0, offset); + } + } + else + { + acc.SetNumWeights(0); + } + + acc.NormalStream(ptr, (UInt16)stride, offset); + ptr += sizeof(hsVector3); + + acc.DiffuseStream(ptr, (UInt16)stride, offset); + ptr += sizeof(UInt32); + + acc.SpecularStream(ptr, (UInt16)stride, offset); + ptr += sizeof(UInt32); + + acc.UVWStream(ptr, (UInt16)stride, offset); + + acc.SetNumUVWs(grp->GetNumUVs()); + + acc.SetVtxDeviceRef(vRef); + + return true; +} + +// CloseAccess ///////////////////////////////////////////////////////////////////// +// Unlock the buffer, invalidating the accessSpan. +hsBool plDXPipeline::CloseAccess(plAccessSpan& dst) +{ + if( !dst.HasAccessVtx() ) + return false; + + plAccessVtxSpan& acc = dst.AccessVtx(); + + plDXVertexBufferRef* vRef = (plDXVertexBufferRef*)acc.GetVtxDeviceRef(); + if( !vRef ) + return false; + + IDirect3DVertexBuffer9* vertexBuff = vRef->fD3DBuffer; + if( !vertexBuff ) + return false; + + vertexBuff->Unlock(); + + return true; +} + +// CheckVertexBufferRef ///////////////////////////////////////////////////// +// Make sure the buffer group has a valid buffer ref and that it is up to date. +void plDXPipeline::CheckVertexBufferRef(plGBufferGroup* owner, UInt32 idx) +{ + // First, do we have a device ref at this index? + plDXVertexBufferRef* vRef = (plDXVertexBufferRef*)owner->GetVertexBufferRef(idx); + // If not + if( !vRef ) + { + // Make the blank ref + vRef = TRACKED_NEW plDXVertexBufferRef; + + ISetupVertexBufferRef(owner, idx, vRef); + + } + if( !vRef->IsLinked() ) + vRef->Link( &fVtxBuffRefList ); + + // One way or another, we now have a vbufferref[idx] in owner. + // Now, does it need to be (re)filled? + // If the owner is volatile, then we hold off. It might not + // be visible, and we might need to refill it again if we + // have an overrun of our dynamic D3D buffer. + if( !vRef->Volatile() ) + { + if( fAllocUnManaged ) + return; + + // If it's a static buffer, allocate a D3D vertex buffer for it. Otherwise, it'll + // be sharing the global D3D dynamic buffer, and marked as volatile. + ICheckStaticVertexBuffer(vRef, owner, idx); + + // Might want to remove this assert, and replace it with a dirty check if + // we have static buffers that change very seldom rather than never. + hsAssert(!vRef->IsDirty(), "Non-volatile vertex buffers should never get dirty"); + } + else + { + // Make sure we're going to be ready to fill it. + + if( !vRef->fData && (vRef->fFormat != owner->GetVertexFormat()) ) + { + vRef->fData = TRACKED_NEW UInt8[vRef->fCount * vRef->fVertexSize]; + } + } +} + +// CheckIndexBufferRef ///////////////////////////////////////////////////// +// Make sure the buffer group has an index buffer ref and that its data is current. +void plDXPipeline::CheckIndexBufferRef(plGBufferGroup* owner, UInt32 idx) +{ + plDXIndexBufferRef* iRef = (plDXIndexBufferRef*)owner->GetIndexBufferRef(idx); + if( !iRef ) + { + // Create one from scratch. + + iRef = TRACKED_NEW plDXIndexBufferRef; + + ISetupIndexBufferRef(owner, idx, iRef); + + } + if( !iRef->IsLinked() ) + iRef->Link(&fIdxBuffRefList); + + // Make sure it has all D3D resources created. + ICheckIndexBuffer(iRef); + + // If it's dirty, refill it. + if( iRef->IsDirty() ) + IFillIndexBufferRef(iRef, owner, idx); +} + +// IFillIndexBufferRef //////////////////////////////////////////////////////////// +// Refresh the D3D index buffer from the plasma index buffer. +void plDXPipeline::IFillIndexBufferRef(plDXIndexBufferRef* iRef, plGBufferGroup* owner, UInt32 idx) +{ + UInt32 startIdx = owner->GetIndexBufferStart(idx); + UInt32 size = (owner->GetIndexBufferEnd(idx) - startIdx) * sizeof(UInt16); + if( !size ) + return; + + DWORD lockFlags = iRef->Volatile() ? D3DLOCK_DISCARD : 0; + UInt16* destPtr = nil; + if( FAILED( iRef->fD3DBuffer->Lock(startIdx * sizeof(UInt16), size, (void **)&destPtr, lockFlags) ) ) + { + hsAssert( false, "Cannot lock index buffer for writing" ); + return; + } + + memcpy( destPtr, owner->GetIndexBufferData(idx) + startIdx, size ); + + iRef->fD3DBuffer->Unlock(); + + iRef->SetDirty( false ); + +} + +// ICheckIndexBuffer //////////////////////////////////////////////////////// +// Make sure index buffer ref has any D3D resources it needs. +void plDXPipeline::ICheckIndexBuffer(plDXIndexBufferRef* iRef) +{ + if( !iRef->fD3DBuffer && iRef->fCount ) + { + D3DPOOL poolType = fAllocUnManaged ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; + DWORD usage = D3DUSAGE_WRITEONLY; + iRef->SetVolatile(false); + if( FAILED( fD3DDevice->CreateIndexBuffer( sizeof( UInt16 ) * iRef->fCount, + usage, + D3DFMT_INDEX16, + poolType, + &iRef->fD3DBuffer, NULL) ) ) + { + hsAssert( false, "CreateIndexBuffer() call failed!" ); + iRef->fD3DBuffer = nil; + return; + } + PROFILE_POOL_MEM(poolType, sizeof(UInt16) * iRef->fCount, true, "IndexBuff"); + + iRef->fPoolType = poolType; + iRef->SetDirty(true); + iRef->SetRebuiltSinceUsed(true); + } +} + +// ISetupIndexBufferRef //////////////////////////////////////////////////////////////// +// Initialize the index buffer ref, but don't create anything for it. +void plDXPipeline::ISetupIndexBufferRef(plGBufferGroup* owner, UInt32 idx, plDXIndexBufferRef* iRef) +{ + UInt32 numIndices = owner->GetIndexBufferCount(idx); + iRef->fCount = numIndices; + iRef->fOwner = owner; + iRef->fIndex = idx; + iRef->fRefTime = 0; + + iRef->SetDirty(true); + iRef->SetRebuiltSinceUsed(true); + + owner->SetIndexBufferRef(idx, iRef); + hsRefCnt_SafeUnRef(iRef); + + iRef->SetVolatile(owner->AreIdxVolatile()); +} + +//// ISoftwareVertexBlend /////////////////////////////////////////////////////// +// Emulate matrix palette operations in software. The big difference between the hardware +// and software versions is we only want to lock the vertex buffer once and blend all the +// verts we're going to in software, so the vertex blend happens once for an entire drawable. +// In hardware, we want the opposite, to break it into managable chunks, manageable meaning +// few enough matrices to fit into hardware registers. So for hardware version, we set up +// our palette, draw a span or few, setup our matrix palette with new matrices, draw, repeat. +hsBool plDXPipeline::ISoftwareVertexBlend( plDrawableSpans* drawable, const hsTArray& visList ) +{ + if (IsDebugFlagSet(plPipeDbg::kFlagNoSkinning)) + return true; + + if( drawable->GetSkinTime() == fRenderCnt ) + return true; + + const hsBitVector &blendBits = drawable->GetBlendingSpanVector(); + + if( drawable->GetBlendingSpanVector().Empty() ) + { + // This sucker doesn't have any skinning spans anyway. Just return + drawable->SetSkinTime( fRenderCnt ); + return true; + } + + plProfile_BeginTiming(Skin); + + // lock the data buffer + + // First, figure out which buffers we need to blend. + const int kMaxBufferGroups = 20; + const int kMaxVertexBuffers = 20; + static char blendBuffers[kMaxBufferGroups][kMaxVertexBuffers]; + memset(blendBuffers, 0, kMaxBufferGroups * kMaxVertexBuffers * sizeof(**blendBuffers)); + + hsAssert(kMaxBufferGroups >= drawable->GetNumBufferGroups(), "Bigger than we counted on num groups skin."); + + const hsTArray& spans = drawable->GetSpanArray(); + int i; + for( i = 0; i < visList.GetCount(); i++ ) + { + if( blendBits.IsBitSet( visList[ i ] ) ) + { + const plVertexSpan &vSpan = *(plVertexSpan *)spans[visList[i]]; + hsAssert(kMaxVertexBuffers > vSpan.fVBufferIdx, "Bigger than we counted on num buffers skin."); + + blendBuffers[vSpan.fGroupIdx][vSpan.fVBufferIdx] = 1; + drawable->SetBlendingSpanVectorBit( visList[ i ], false ); + } + } + + // Now go through each of the group/buffer (= a real vertex buffer) pairs we found, + // and blend into it. We'll lock the buffer once, and then for each span that + // uses it, set the matrix palette and and then do the blend for that span. + // When we've done all the spans for a group/buffer, we unlock it and move on. + int j; + for( i = 0; i < kMaxBufferGroups; i++ ) + { + for( j = 0; j < kMaxVertexBuffers; j++ ) + { + if( blendBuffers[i][j] ) + { + // Found one. Do the lock. + plDXVertexBufferRef* vRef = (plDXVertexBufferRef*)drawable->GetVertexRef(i, j); + + hsAssert(vRef->fData, "Going into skinning with no place to put results!"); + + UInt8* destPtr = vRef->fData; + + int k; + for( k = 0; k < visList.GetCount(); k++ ) + { + const plIcicle& span = *(plIcicle*)spans[visList[k]]; + if( (span.fGroupIdx == i)&&(span.fVBufferIdx == j) ) + { + plProfile_Inc(NumSkin); + + hsMatrix44* matrixPalette = drawable->GetMatrixPalette(span.fBaseMatrix); + matrixPalette[0] = span.fLocalToWorld; + + UInt8* ptr = vRef->fOwner->GetVertBufferData(vRef->fIndex); + ptr += span.fVStartIdx * vRef->fOwner->GetVertexSize(); + IBlendVertsIntoBuffer( (plSpan*)&span, + matrixPalette, span.fNumMatrices, + ptr, + vRef->fOwner->GetVertexFormat(), + vRef->fOwner->GetVertexSize(), + destPtr + span.fVStartIdx * vRef->fVertexSize, + vRef->fVertexSize, + span.fVLength, + span.fLocalUVWChans ); + vRef->SetDirty(true); + } + } + // Unlock and move on. + } + } + } + + plProfile_EndTiming(Skin); + + if( drawable->GetBlendingSpanVector().Empty() ) + { + // Only do this if we've blended ALL of the spans. Thus, this becomes a trivial + // rejection for all the skinning flags being cleared + drawable->SetSkinTime(fRenderCnt); + } + + return true; +} + +// IBeginAllocUnmanaged /////////////////////////////////////////////////////////////////// +// Before allocating anything into POOL_DEFAULT, we must evict managed memory. +// See LoadResources. +void plDXPipeline::IBeginAllocUnManaged() +{ + // Flush out all managed resources to make room for unmanaged resources. + fD3DDevice->EvictManagedResources(); + fEvictTime = fTextUseTime; + fManagedSeen = 0; + + fManagedAlloced = false; + fAllocUnManaged = true; // we're currently only allocating POOL_DEFAULT +} + +// IEndAllocUnManged. +// Before allocating anything into POOL_DEFAULT, we must evict managed memory. +// See LoadResources. +void plDXPipeline::IEndAllocUnManaged() +{ + fAllocUnManaged = false; + + // Flush the (should be empty) resource manager to reset its internal allocation pool. + fD3DDevice->EvictManagedResources(); + fEvictTime = fTextUseTime; + fManagedSeen = 0; +} + +// ICheckTextureUsage //////////////////////////////////////////////////////////////////// +// Obsolete, unused. +// Deletes textures LRU to try to get around NVidia memory manager bug. Found a +// more robust/efficent way. Besides, it didn't help. See OSVERSION. +void plDXPipeline::ICheckTextureUsage() +{ + plProfile_IncCount(fTexUsed, fTexUsed); + plProfile_IncCount(fTexManaged, fTexManaged); + + plConst(UInt32) kMinTexManaged(5000000); + if( fTexManaged < kMinTexManaged ) + return; + + plConst(UInt32) kScale(2); + if( fTexUsed * kScale < fTexManaged ) + { + // Find the stalest + UInt32 stalest = fTextUseTime; + plDXTextureRef* ref = fTextureRefList; + while( ref ) + { + // I don't know if render targets even get put in this list. + if( !(ref->GetFlags() & plDXTextureRef::kRenderTarget) && (ref->fUseTime < stalest) ) + stalest = ref->fUseTime; + ref = ref->GetNext(); + } + stalest = fTextUseTime - stalest; + + // If the stalest is fresh, live with thrashing + plConst(UInt32) kMinAge(60); + if( stalest < kMinAge ) + return; + + // Kill the stalest, and everything more than half as stale + stalest /= 2; + if( stalest < kMinAge ) + stalest = kMinAge; + + stalest = fTextUseTime - stalest; + + // Go through again slaughtering left and right + ref = fTextureRefList; + while( ref ) + { + if( !(ref->GetFlags() & plDXTextureRef::kRenderTarget) && (ref->fUseTime < stalest) ) + { + plDXTextureRef* nuke = ref; + ref = ref->GetNext(); + nuke->Release(); + nuke->Unlink(); + } + else + { + ref = ref->GetNext(); + } + } + } +} + +// ICheckVtxUsage //////////////////////////////////////////////////////////////////// +// Obsolete, unused. +// Deletes textures LRU to try to get around NVidia memory manager bug. Found a +// more robust/efficent way. Besides, it didn't help. See OSVERSION. +void plDXPipeline::ICheckVtxUsage() +{ + plProfile_IncCount(fVtxUsed, fVtxUsed); + plProfile_IncCount(fVtxManaged, fVtxManaged); + + plConst(UInt32) kMinVtxManaged(5000000); + if( fVtxManaged < kMinVtxManaged ) + return; + + plConst(UInt32) kScale(2); + if( fVtxUsed * kScale < fVtxManaged ) + { + // Find the stalest + UInt32 stalest = fTextUseTime; + plDXVertexBufferRef* ref = fVtxBuffRefList; + while( ref ) + { + if( !ref->Volatile() && (ref->fUseTime < stalest) ) + stalest = ref->fUseTime; + ref = ref->GetNext(); + } + stalest = fTextUseTime - stalest; + + // If the stalest is fresh, live with thrashing + plConst(UInt32) kMinAge(60); + if( stalest < kMinAge ) + return; + + // Kill the stalest, and everything more than half as stale + stalest /= 2; + if( stalest < kMinAge ) + stalest = kMinAge; + + stalest = fTextUseTime - stalest; + + // Go through again slaughtering left and right + ref = fVtxBuffRefList; + while( ref ) + { + if( !ref->Volatile() && (ref->fUseTime < stalest) ) + { + plDXVertexBufferRef* nuke = ref; + ref = ref->GetNext(); + nuke->Release(); + nuke->Unlink(); + } + else + { + ref = ref->GetNext(); + } + } + } +} + +hsBool plDXPipeline::CheckResources() +{ + if ((fClothingOutfits.GetCount() <= 1 && fAvRTPool.GetCount() > 1) || + (fAvRTPool.GetCount() >= 16 && (fAvRTPool.GetCount() / 2 >= fClothingOutfits.GetCount()))) + { + return (hsTimer::GetSysSeconds() - fAvRTShrinkValidSince > kAvTexPoolShrinkThresh); + } + + fAvRTShrinkValidSince = hsTimer::GetSysSeconds(); + return (fAvRTPool.GetCount() < fClothingOutfits.GetCount()); +} + +// LoadResources /////////////////////////////////////////////////////////////////////// +// Basically, we have to load anything that goes into POOL_DEFAULT before +// anything into POOL_MANAGED, or the memory manager gets confused. +// More precisely, we have to evict everything from POOL_MANAGED before we +// can allocate anything into POOL_DEFAULT. +// So, this function frees up everything in POOL_DEFAULT, evicts managed memory, +// calls out for anything needing to be created POOL_DEFAULT to do so, +// Then we're free to load into POOL_MANAGED on demand. +// This is typically called at the beginning of the first render after loading +// a new age. +void plDXPipeline::LoadResources() +{ + hsStatusMessageF("Begin Device Reload t=%f",hsTimer::GetSeconds()); + plNetClientApp::StaticDebugMsg("Begin Device Reload"); + + // Just to be safe. + IInitDeviceState(); // 9700 THRASH + + // Evict mananged memory. + IBeginAllocUnManaged(); + + // Release everything we have in POOL_DEFAULT. + IReleaseDynamicBuffers(); + IReleaseAvRTPool(); + + // Create all RenderTargets + plPipeRTMakeMsg* rtMake = TRACKED_NEW plPipeRTMakeMsg(this); + rtMake->Send(); + + // Create all our shadow render targets and pipeline specific POOL_DEFAULT vertex buffers. + // This includes our single dynamic vertex buffer that we cycle through for software + // skinned, particle systems, etc. + ICreateDynamicBuffers(); + + // Create all POOL_DEFAULT (sorted) index buffers in the scene. + plPipeGeoMakeMsg* defMake = TRACKED_NEW plPipeGeoMakeMsg(this, true); + defMake->Send(); + + // This can be a bit of a mem hog and will use more mem if available, so keep it last in the + // POOL_DEFAULT allocs. + IFillAvRTPool(); + + // We should have everything POOL_DEFAULT we need now. + IEndAllocUnManaged(); + + // Force a create of all our static D3D vertex buffers. +#define MF_PRELOAD_MANAGEDBUFFERS +#ifdef MF_PRELOAD_MANAGEDBUFFERS + plPipeGeoMakeMsg* manMake = TRACKED_NEW plPipeGeoMakeMsg(this, false); + manMake->Send(); +#endif // MF_PRELOAD_MANAGEDBUFFERS + + // Forcing a preload of textures turned out to not be so great, + // since there are typically so many in an age, it swamped out + // VM. +#ifdef MF_TOSSER +#define MF_PRELOAD_TEXTURES +#endif // MF_TOSSER +#ifdef MF_PRELOAD_TEXTURES + plPipeTexMakeMsg* texMake = TRACKED_NEW plPipeTexMakeMsg(this); + texMake->Send(); +#endif // MF_PRELOAD_TEXTURES + + fD3DDevice->EvictManagedResources(); + + // Okay, we've done it, clear the request. + plPipeResReq::Clear(); + + plProfile_IncCount(PipeReload, 1); + + hsStatusMessageF("End Device Reload t=%f",hsTimer::GetSeconds()); + plNetClientApp::StaticDebugMsg("End Device Reload"); +} + +// Sorry about this, but it really did speed up the skinning. +// Just some macros for the inner loop of IBlendVertsIntoBuffer. +#define MATRIXMULTBEGIN(xfm, wgt) \ + register float m00 = xfm.fMap[0][0]; \ + register float m01 = xfm.fMap[0][1]; \ + register float m02 = xfm.fMap[0][2]; \ + register float m03 = xfm.fMap[0][3]; \ + register float m10 = xfm.fMap[1][0]; \ + register float m11 = xfm.fMap[1][1]; \ + register float m12 = xfm.fMap[1][2]; \ + register float m13 = xfm.fMap[1][3]; \ + register float m20 = xfm.fMap[2][0]; \ + register float m21 = xfm.fMap[2][1]; \ + register float m22 = xfm.fMap[2][2]; \ + register float m23 = xfm.fMap[2][3]; \ + register float m_wgt = wgt; \ + register float srcX, srcY, srcZ; + +#define MATRIXMULTPOINTADD(dst, src) \ + srcX = src.fX; \ + srcY = src.fY; \ + srcZ = src.fZ; \ + \ + dst.fX += (srcX * m00 + srcY * m01 + srcZ * m02 + m03) * m_wgt; \ + dst.fY += (srcX * m10 + srcY * m11 + srcZ * m12 + m13) * m_wgt; \ + dst.fZ += (srcX * m20 + srcY * m21 + srcZ * m22 + m23) * m_wgt; + +#define MATRIXMULTVECTORADD(dst, src) \ + srcX = src.fX; \ + srcY = src.fY; \ + srcZ = src.fZ; \ + \ + dst.fX += (srcX * m00 + srcY * m01 + srcZ * m02) * m_wgt; \ + dst.fY += (srcX * m10 + srcY * m11 + srcZ * m12) * m_wgt; \ + dst.fZ += (srcX * m20 + srcY * m21 + srcZ * m22) * m_wgt; + +// inlTESTPOINT ///////////////////////////////////////// +// Update mins and maxs if destP is outside. +inline void inlTESTPOINT(const hsPoint3& destP, + hsScalar& minX, hsScalar& minY, hsScalar& minZ, + hsScalar& maxX, hsScalar& maxY, hsScalar& maxZ) +{ + if( destP.fX < minX ) + minX = destP.fX; + else if( destP.fX > maxX ) + maxX = destP.fX; + + if( destP.fY < minY ) + minY = destP.fY; + else if( destP.fY > maxY ) + maxY = destP.fY; + + if( destP.fZ < minZ ) + minZ = destP.fZ; + else if( destP.fZ > maxZ ) + maxZ = destP.fZ; +} + +//// IBlendVertsIntoBuffer //////////////////////////////////////////////////// +// Given a pointer into a buffer of verts that have blending data in the D3D +// format, blends them into the destination buffer given without the blending +// info. + +void plDXPipeline::IBlendVertsIntoBuffer( plSpan* span, + hsMatrix44* matrixPalette, int numMatrices, + UInt8 *src, UInt8 format, UInt32 srcStride, + UInt8 *dest, UInt32 destStride, UInt32 count, + UInt16 localUVWChans ) +{ + UInt8 numUVs, numWeights; + UInt32 i, j, indices, color, specColor, uvChanSize; + float weights[ 4 ], weightSum; + hsPoint3 pt, tempPt, destPt; + hsVector3 vec, tempNorm, destNorm; + + + /// Get some counts + switch( format & plGBufferGroup::kSkinWeightMask ) + { + case plGBufferGroup::kSkin1Weight: numWeights = 1; break; + case plGBufferGroup::kSkin2Weights: numWeights = 2; break; + case plGBufferGroup::kSkin3Weights: numWeights = 3; break; + default: hsAssert( false, "Invalid weight count in IBlendVertsIntoBuffer()" ); + } + + numUVs = plGBufferGroup::CalcNumUVs( format ); + uvChanSize = numUVs * sizeof( float ) * 3; + +//#define MF_RECALC_BOUNDS +#ifdef MF_RECALC_BOUNDS + hsScalar minX = 1.e33f; + hsScalar minY = 1.e33f; + hsScalar minZ = 1.e33f; + + hsScalar maxX = -1.e33f; + hsScalar maxY = -1.e33f; + hsScalar maxZ = -1.e33f; +#endif // MF_RECALC_BOUNDS + + // localUVWChans is bump mapping tangent space vectors, which need to + // be skinned like the normal, as opposed to passed through like + // garden variety UVW coordinates. + // There are no localUVWChans that I know of in production assets (i.e. + // the avatar is not skinned). + if( !localUVWChans ) + { + /// Copy whilst blending + for( i = 0; i < count; i++ ) + { + // Extract data + src = inlExtractPoint( src, pt ); + for( j = 0, weightSum = 0; j < numWeights; j++ ) + { + src = inlExtractFloat( src, weights[ j ] ); + weightSum += weights[ j ]; + } + weights[ j ] = 1 - weightSum; + + if( format & plGBufferGroup::kSkinIndices ) + { + src = inlExtractUInt32( src, indices ); + } + else + { + indices = 1 << 8; + } + src = inlExtractPoint( src, vec ); + src = inlExtractUInt32( src, color ); + src = inlExtractUInt32( src, specColor ); + + // Blend + destPt.Set( 0, 0, 0 ); + destNorm.Set( 0, 0, 0 ); + for( j = 0; j < numWeights + 1; j++ ) + { + if( weights[ j ] ) + { + MATRIXMULTBEGIN(matrixPalette[indices & 0xff], weights[j]); + + MATRIXMULTPOINTADD(destPt, pt); + MATRIXMULTVECTORADD(destNorm, vec); + } + + indices >>= 8; + } + // Probably don't really need to renormalize this. There errors are + // going to be subtle and "smooth". +// hsFastMath::NormalizeAppr(destNorm); + +#ifdef MF_RECALC_BOUNDS + inlTESTPOINT(destPt, minX, minY, minZ, maxX, maxY, maxZ); +#endif // MF_RECALC_BOUNDS + + // Slam data into position now + dest = inlStuffPoint( dest, destPt ); + dest = inlStuffPoint( dest, destNorm ); + dest = inlStuffUInt32( dest, color ); + dest = inlStuffUInt32( dest, specColor ); + memcpy( dest, src, uvChanSize ); + src += uvChanSize; + dest += uvChanSize; + } + } + else + { + UInt8 hiChan = localUVWChans >> 8; + UInt8 loChan = localUVWChans & 0xff; + /// Copy whilst blending + for( i = 0; i < count; i++ ) + { + hsVector3 srcUVWs[plGeometrySpan::kMaxNumUVChannels]; + hsVector3 dstUVWs[plGeometrySpan::kMaxNumUVChannels]; + + // Extract data + src = inlExtractPoint( src, pt ); + for( j = 0, weightSum = 0; j < numWeights; j++ ) + { + src = inlExtractFloat( src, weights[ j ] ); + weightSum += weights[ j ]; + } + weights[ j ] = 1 - weightSum; + + if( format & plGBufferGroup::kSkinIndices ) + { + src = inlExtractUInt32( src, indices ); + } + else + { + indices = 1 << 8; + } + + src = inlExtractPoint( src, vec ); + src = inlExtractUInt32( src, color ); + src = inlExtractUInt32( src, specColor ); + + UInt8 k; + for( k = 0; k < numUVs; k++ ) + { + src = inlExtractPoint( src, srcUVWs[k] ); + } + memcpy( dstUVWs, srcUVWs, uvChanSize); + dstUVWs[loChan].Set(0,0,0); + dstUVWs[hiChan].Set(0,0,0); + + // Blend + destPt.Set( 0, 0, 0 ); + destNorm.Set( 0, 0, 0 ); + for( j = 0; j < numWeights + 1; j++ ) + { + if( weights[ j ] ) + { + MATRIXMULTBEGIN(matrixPalette[indices & 0xff], weights[j]); + + MATRIXMULTPOINTADD(destPt, pt); + MATRIXMULTVECTORADD(destNorm, vec); + MATRIXMULTVECTORADD(dstUVWs[loChan], srcUVWs[loChan]); + MATRIXMULTVECTORADD(dstUVWs[hiChan], srcUVWs[hiChan]); + } + + indices >>= 8; + } + // Probably don't really need to renormalize this. There errors are + // going to be subtle and "smooth". +// hsFastMath::NormalizeAppr(destNorm); +// hsFastMath::NormalizeAppr(dstUVWs[loChan]); +// hsFastMath::NormalizeAppr(dstUVWs[hiChan]); + +#ifdef MF_RECALC_BOUNDS + inlTESTPOINT(destPt, minX, minY, minZ, maxX, maxY, maxZ); +#endif // MF_RECALC_BOUNDS + + // Slam data into position now + dest = inlStuffPoint( dest, destPt ); + dest = inlStuffPoint( dest, destNorm ); + dest = inlStuffUInt32( dest, color ); + dest = inlStuffUInt32( dest, specColor ); + memcpy( dest, dstUVWs, uvChanSize ); + dest += uvChanSize; + } + } +#ifdef MF_RECALC_BOUNDS + hsBounds3Ext wBnd; + wBnd.Reset(&hsPoint3(minX, minY, minZ)); + wBnd.Union(&hsPoint3(maxX, maxY, maxZ)); + span->fWorldBounds = wBnd; +#endif // MF_RECALC_BOUNDS +} + +// ISetPipeConsts ////////////////////////////////////////////////////////////////// +// A shader can request that the pipeline fill in certain constants that are indeterminate +// until the pipeline is about to render the object the shader is applied to. For example, +// the object's local to world. A single shader may be used on multiple objects with +// multiple local to world transforms. This ensures the pipeline will shove the proper +// local to world into the shader immediately before the render. +// See plShader.h for the list of available pipe constants. +// Note that the lighting pipe constants are NOT implemented. +void plDXPipeline::ISetPipeConsts(plShader* shader) +{ + int n = shader->GetNumPipeConsts(); + int i; + for( i = 0; i < n; i++ ) + { + const plPipeConst& pc = shader->GetPipeConst(i); + switch( pc.fType ) + { + case plPipeConst::kFogSet: + { + float set[4]; + IGetVSFogSet(set); + shader->SetFloat4(pc.fReg, set); + } + break; + case plPipeConst::kLayAmbient: + { + hsColorRGBA col = fCurrLay->GetAmbientColor(); + shader->SetColor(pc.fReg, col); + } + break; + case plPipeConst::kLayRuntime: + { + hsColorRGBA col = fCurrLay->GetRuntimeColor(); + col.a = fCurrLay->GetOpacity(); + shader->SetColor(pc.fReg, col); + } + break; + case plPipeConst::kLaySpecular: + { + hsColorRGBA col = fCurrLay->GetSpecularColor(); + shader->SetColor(pc.fReg, col); + } + break; + case plPipeConst::kTex3x4_0: + case plPipeConst::kTex3x4_1: + case plPipeConst::kTex3x4_2: + case plPipeConst::kTex3x4_3: + case plPipeConst::kTex3x4_4: + case plPipeConst::kTex3x4_5: + case plPipeConst::kTex3x4_6: + case plPipeConst::kTex3x4_7: + { + int stage = pc.fType - plPipeConst::kTex3x4_0; + + if( stage > fCurrNumLayers ) + { + // Ooops. This is bad, means the shader is expecting more layers than + // we actually have (or is just bogus). Assert and quietly continue. + hsAssert(false, "Shader asking for higher stage transform than we have"); + continue; + } + const hsMatrix44& xfm = fCurrMaterial->GetLayer(fCurrLayerIdx + stage)->GetTransform(); + + shader->SetMatrix34(pc.fReg, xfm); + } + break; + case plPipeConst::kTex2x4_0: + case plPipeConst::kTex2x4_1: + case plPipeConst::kTex2x4_2: + case plPipeConst::kTex2x4_3: + case plPipeConst::kTex2x4_4: + case plPipeConst::kTex2x4_5: + case plPipeConst::kTex2x4_6: + case plPipeConst::kTex2x4_7: + { + int stage = pc.fType - plPipeConst::kTex2x4_0; + + if( stage > fCurrNumLayers ) + { + // Ooops. This is bad, means the shader is expecting more layers than + // we actually have (or is just bogus). Assert and quietly continue. + hsAssert(false, "Shader asking for higher stage transform than we have"); + continue; + } + const hsMatrix44& xfm = fCurrMaterial->GetLayer(fCurrLayerIdx + stage)->GetTransform(); + + shader->SetMatrix24(pc.fReg, xfm); + } + break; + case plPipeConst::kTex1x4_0: + case plPipeConst::kTex1x4_1: + case plPipeConst::kTex1x4_2: + case plPipeConst::kTex1x4_3: + case plPipeConst::kTex1x4_4: + case plPipeConst::kTex1x4_5: + case plPipeConst::kTex1x4_6: + case plPipeConst::kTex1x4_7: + { + int stage = pc.fType - plPipeConst::kTex1x4_0; + + if( stage > fCurrNumLayers ) + { + // Ooops. This is bad, means the shader is expecting more layers than + // we actually have (or is just bogus). Assert and quietly continue. + hsAssert(false, "Shader asking for higher stage transform than we have"); + continue; + } + const hsMatrix44& xfm = fCurrMaterial->GetLayer(fCurrLayerIdx + stage)->GetTransform(); + + shader->SetFloat4(pc.fReg, xfm.fMap[0]); + } + break; + case plPipeConst::kLocalToNDC: + { + hsMatrix44 cam2ndc = IGetCameraToNDC(); + hsMatrix44 world2cam = GetViewTransform().GetWorldToCamera(); + + hsMatrix44 local2ndc = cam2ndc * world2cam * GetLocalToWorld(); + + shader->SetMatrix44(pc.fReg, local2ndc); + } + break; + + case plPipeConst::kCameraToNDC: + { + hsMatrix44 cam2ndc = IGetCameraToNDC(); + + shader->SetMatrix44(pc.fReg, cam2ndc); + } + break; + + case plPipeConst::kWorldToNDC: + { + hsMatrix44 cam2ndc = IGetCameraToNDC(); + hsMatrix44 world2cam = GetViewTransform().GetWorldToCamera(); + + hsMatrix44 world2ndc = cam2ndc * world2cam; + + shader->SetMatrix44(pc.fReg, world2ndc); + } + break; + + case plPipeConst::kLocalToWorld: + shader->SetMatrix34(pc.fReg, GetLocalToWorld()); + break; + + case plPipeConst::kWorldToLocal: + shader->SetMatrix34(pc.fReg, GetWorldToLocal()); + break; + + case plPipeConst::kWorldToCamera: + { + hsMatrix44 world2cam = GetViewTransform().GetWorldToCamera(); + + shader->SetMatrix34(pc.fReg, world2cam); + } + break; + + case plPipeConst::kCameraToWorld: + { + hsMatrix44 cam2world = GetViewTransform().GetCameraToWorld(); + + shader->SetMatrix34(pc.fReg, cam2world); + } + break; + + case plPipeConst::kLocalToCamera: + { + hsMatrix44 world2cam = GetViewTransform().GetWorldToCamera(); + + hsMatrix44 local2cam = world2cam * GetLocalToWorld(); + + shader->SetMatrix34(pc.fReg, local2cam); + } + break; + + case plPipeConst::kCameraToLocal: + { + hsMatrix44 cam2world = GetViewTransform().GetCameraToWorld(); + + hsMatrix44 cam2local = GetWorldToLocal() * cam2world; + + shader->SetMatrix34(pc.fReg, cam2local); + } + break; + + case plPipeConst::kCamPosWorld: + { + shader->SetVectorW(pc.fReg, GetViewTransform().GetCameraToWorld().GetTranslate(), 1.f); + } + break; + + case plPipeConst::kCamPosLocal: + { + hsPoint3 localCam = GetWorldToLocal() * GetViewTransform().GetCameraToWorld().GetTranslate(); + + shader->SetVectorW(pc.fReg, localCam, 1.f); + } + break; + + case plPipeConst::kObjPosWorld: + { + shader->SetVectorW(pc.fReg, GetLocalToWorld().GetTranslate(), 1.f); + } + break; + + // UNIMPLEMENTED + case plPipeConst::kDirLight1: + case plPipeConst::kDirLight2: + case plPipeConst::kDirLight3: + case plPipeConst::kDirLight4: + case plPipeConst::kPointLight1: + case plPipeConst::kPointLight2: + case plPipeConst::kPointLight3: + case plPipeConst::kPointLight4: + break; + } + } +} + +// ISetShaders ///////////////////////////////////////////////////////////////////////////////////// +// Setup to render using the input vertex and pixel shader. Either or both may +// be nil, in which case the fixed function pipeline is indicated. +// Any Pipe Constants the non-FFP shader wants will be set here. +// Lastly, all constants will be set (as a block) for any non-FFP vertex or pixel shader. +HRESULT plDXPipeline::ISetShaders(plShader* vShader, plShader* pShader) +{ + IDirect3DVertexShader9 *vsHandle = NULL; + if( vShader ) + { + hsAssert(vShader->IsVertexShader(), "Wrong type shader as vertex shader"); + ISetPipeConsts(vShader); + + plDXVertexShader* vRef = (plDXVertexShader*)vShader->GetDeviceRef(); + if( !vRef ) + { + vRef = TRACKED_NEW plDXVertexShader(vShader); + hsRefCnt_SafeUnRef(vRef); + } + if( !vRef->IsLinked() ) + vRef->Link(&fVShaderRefList); + vsHandle = vRef->GetShader(this); + + // This is truly obnoxious, but D3D insists that, while using the progammable pipeline, + // all stages be set up like this, not just the ones we're going to use. We have to + // do this if we have either a vertex or a pixel shader. See below. Whatever. mf + int i; + for( i = 0; i < 8; i++ ) + { + fD3DDevice->SetTextureStageState(i, D3DTSS_TEXCOORDINDEX, fLayerUVWSrcs[i] = i); + fD3DDevice->SetTextureStageState(i, D3DTSS_TEXTURETRANSFORMFLAGS, fLayerXformFlags[i] = 0); + } + } + + IDirect3DPixelShader9 *psHandle = NULL; + if( pShader ) + { + hsAssert(pShader->IsPixelShader(), "Wrong type shader as pixel shader"); + + ISetPipeConsts(pShader); + + plDXPixelShader* pRef = (plDXPixelShader*)pShader->GetDeviceRef(); + if( !pRef ) + { + pRef = TRACKED_NEW plDXPixelShader(pShader); + hsRefCnt_SafeUnRef(pRef); + } + if( !pRef->IsLinked() ) + pRef->Link(&fPShaderRefList); + psHandle = pRef->GetShader(this); + + if( !vShader ) + { + int i; + for( i = 0; i < 8; i++ ) + { + fD3DDevice->SetTextureStageState(i, D3DTSS_TEXCOORDINDEX, fLayerUVWSrcs[i] = i); + fD3DDevice->SetTextureStageState(i, D3DTSS_TEXTURETRANSFORMFLAGS, fLayerXformFlags[i] = 0); + } + } + } + + if( vsHandle != fSettings.fCurrVertexShader ) + { + HRESULT hr = fD3DDevice->SetVertexShader(fSettings.fCurrVertexShader = vsHandle); + hsAssert(!FAILED(hr), "Error setting vertex shader"); + } + + if( psHandle != fSettings.fCurrPixelShader ) + { + HRESULT hr = fD3DDevice->SetPixelShader(fSettings.fCurrPixelShader = psHandle); + hsAssert(!FAILED(hr), "Error setting pixel shader"); + } + + // Handle cull mode here, because current cullmode is dependent on + // the handedness of the LocalToCamera AND whether we are twosided. + ISetCullMode(); + + return S_OK; +} + +// IRenderAuxSpan ////////////////////////////////////////////////////////// +// Aux spans (auxilliary) are geometry rendered immediately after, and therefore dependent, on +// other normal geometry. They don't have SceneObjects, Drawables, DrawInterfaces or +// any of that, and therefore don't correspond to any object in the scene. +// They are dynamic procedural decals. See plDynaDecal.cpp and plDynaDecalMgr.cpp. +// This is wrapped by IRenderAuxSpans, which makes sure state is restored to resume +// normal rendering after the AuxSpan is rendered. +void plDXPipeline::IRenderAuxSpan(const plSpan& span, const plAuxSpan* aux) +{ + // Make sure the underlying resources are created and filled in with current data. + CheckVertexBufferRef(aux->fGroup, aux->fVBufferIdx); + CheckIndexBufferRef(aux->fGroup, aux->fIBufferIdx); + ICheckAuxBuffers(aux); + + // Set to render from the aux spans buffers. + plDXVertexBufferRef* vRef = (plDXVertexBufferRef*)aux->fGroup->GetVertexBufferRef(aux->fVBufferIdx); + + if( !vRef ) + return; + + plDXIndexBufferRef* iRef = (plDXIndexBufferRef*)aux->fGroup->GetIndexBufferRef(aux->fIBufferIdx); + + if( !iRef ) + return; + + HRESULT r; + + r = fD3DDevice->SetStreamSource( 0, vRef->fD3DBuffer, 0, vRef->fVertexSize ); + hsAssert( r == D3D_OK, "Error trying to set the stream source!" ); + plProfile_Inc(VertexChange); + + fD3DDevice->SetFVF(fSettings.fCurrFVFFormat = IGetBufferD3DFormat(vRef->fFormat)); + + r = fD3DDevice->SetIndices( iRef->fD3DBuffer ); + hsAssert( r == D3D_OK, "Error trying to set the indices!" ); + + plRenderTriListFunc render(fD3DDevice, iRef->fOffset, aux->fVStartIdx, aux->fVLength, aux->fIStartIdx, aux->fILength/3); + + // Now just loop through the aux material, rendering in as many passes as it takes. + hsGMaterial* material = aux->fMaterial; + int j; + for( j = 0; j < material->GetNumLayers(); ) + { + int iCurrMat = j; + j = IHandleMaterial( material, iCurrMat, &span ); + if (j == -1) + break; + + ISetShaders(material->GetLayer(iCurrMat)->GetVertexShader(), material->GetLayer(iCurrMat)->GetPixelShader()); + + if( aux->fFlags & plAuxSpan::kOverrideLiteModel ) + { + static D3DMATERIAL9 mat; + fD3DDevice->SetRenderState(D3DRS_AMBIENT, 0xffffffff); + + fD3DDevice->SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1 ); + fD3DDevice->SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL ); + + fD3DDevice->SetMaterial( &mat ); + } + + render.RenderPrims(); + } + +} + +// IRenderAuxSpans //////////////////////////////////////////////////////////////////////////// +// Save and restore render state around calls to IRenderAuxSpan. This lets +// a list of aux spans get rendered with only one save/restore state. +void plDXPipeline::IRenderAuxSpans(const plSpan& span) +{ + if (IsDebugFlagSet(plPipeDbg::kFlagNoAuxSpans)) + return; + + plDXVertexBufferRef* oldVRef = fSettings.fCurrVertexBuffRef; + plDXIndexBufferRef* oldIRef = fSettings.fCurrIndexBuffRef; + + ISetLocalToWorld(hsMatrix44::IdentityMatrix(), hsMatrix44::IdentityMatrix()); + + int i; + for( i = 0; i < span.GetNumAuxSpans(); i++ ) + IRenderAuxSpan(span, span.GetAuxSpan(i)); + + ISetLocalToWorld(span.fLocalToWorld, span.fWorldToLocal); + + HRESULT r; + + r = fD3DDevice->SetStreamSource( 0, oldVRef->fD3DBuffer, 0, oldVRef->fVertexSize ); + hsAssert( r == D3D_OK, "Error trying to set the stream source!" ); + plProfile_Inc(VertexChange); + + r = fD3DDevice->SetFVF(fSettings.fCurrFVFFormat = IGetBufferD3DFormat(oldVRef->fFormat)); + + r = fD3DDevice->SetIndices( oldIRef->fD3DBuffer ); + hsAssert( r == D3D_OK, "Error trying to set the indices!" ); + +} + +// ICheckVBUsage ////////////////////////////////////////////////////////////// +// Keep track of how much managed vertex buffer memory is being used and +// has been used since the last evict. +inline void plDXPipeline::ICheckVBUsage(plDXVertexBufferRef* vRef) +{ + if( !vRef->fOwner->AreVertsVolatile() ) + { + if( vRef->fUseTime <= fEvictTime ) + fManagedSeen += vRef->fVertexSize * vRef->fCount; + + if( vRef->fUseTime != fTextUseTime ) + { + plProfile_NewMem(CurrVB, vRef->fVertexSize * vRef->fCount); + fVtxUsed += vRef->fVertexSize * vRef->fCount; + vRef->fUseTime = fTextUseTime; + } + } +} + +//// IRenderBufferSpan //////////////////////////////////////////////////////// +// Sets up the vertex and index buffers for a span, and then +// renders it in as many passes as it takes in ILoopOverLayers. +void plDXPipeline::IRenderBufferSpan( const plIcicle& span, + hsGDeviceRef *vb, hsGDeviceRef *ib, + hsGMaterial *material, UInt32 vStart, UInt32 vLength, + UInt32 iStart, UInt32 iLength ) +{ + plProfile_BeginTiming(RenderBuff); + + plDXVertexBufferRef *vRef = (plDXVertexBufferRef *)vb; + plDXIndexBufferRef *iRef = (plDXIndexBufferRef *)ib; + + HRESULT r; + + if( vRef->fD3DBuffer == nil || iRef->fD3DBuffer == nil ) + { + plProfile_EndTiming(RenderBuff); + hsAssert( false, "Trying to render a nil buffer pair!" ); + return; + } + + /// Switch to the vertex buffer we want + if( fSettings.fCurrVertexBuffRef != vRef ) + { + hsRefCnt_SafeAssign( fSettings.fCurrVertexBuffRef, vRef ); + hsAssert( vRef->fD3DBuffer != nil, "Trying to render a buffer pair without a vertex buffer!" ); + vRef->SetRebuiltSinceUsed(true); + } + + if( vRef->RebuiltSinceUsed() ) + { + r = fD3DDevice->SetStreamSource( 0, vRef->fD3DBuffer, 0, vRef->fVertexSize ); + hsAssert( r == D3D_OK, "Error trying to set the stream source!" ); + plProfile_Inc(VertexChange); + + DWORD fvf = IGetBufferD3DFormat(vRef->fFormat); + if (fSettings.fCurrFVFFormat != fvf) + fD3DDevice->SetFVF(fSettings.fCurrFVFFormat = fvf); + + vRef->SetRebuiltSinceUsed(false); + + ICheckVBUsage(vRef); + } + + // Note: both these stats are the same, since we don't do any culling or clipping on the tris + if( fSettings.fCurrIndexBuffRef != iRef ) + { + hsRefCnt_SafeAssign( fSettings.fCurrIndexBuffRef, iRef ); + hsAssert( iRef->fD3DBuffer != nil, "Trying to render with a nil index buffer" ); + iRef->SetRebuiltSinceUsed(true); + } + + if( iRef->RebuiltSinceUsed() ) + { + r = fD3DDevice->SetIndices( iRef->fD3DBuffer ); + hsAssert( r == D3D_OK, "Error trying to set the indices!" ); + plProfile_Inc(IndexChange); + iRef->SetRebuiltSinceUsed(false); + } + + plRenderTriListFunc render(fD3DDevice, iRef->fOffset, vStart, vLength, iStart, iLength/3); + + plProfile_EndTiming(RenderBuff); + ILoopOverLayers(render, material, span); +} + +// ILoopOverLayers ///////////////////////////////////////////////////////////////////////////////// +// Render the input span with the input material in as many passes as it takes. +// Also handles rendering projected lights, either onto each pass or +// once onto the FB after all the passes, as appropriate. +hsBool plDXPipeline::ILoopOverLayers(const plRenderPrimFunc& inRender, hsGMaterial* material, const plSpan& span) +{ + plProfile_BeginTiming(RenderPrim); + + const plRenderPrimFunc& render = IsDebugFlagSet(plPipeDbg::kFlagNoRender) ? (const plRenderPrimFunc&)sRenderNil : inRender; + + if( GetOverrideMaterial() ) + material = GetOverrideMaterial(); + + IPushPiggyBacks(material); + + hsBool normalLightsDisabled = false; + + // Loop across all the layers we need to draw + int j; + for( j = 0; j < material->GetNumLayers(); ) + { + int iCurrMat = j; + j = IHandleMaterial( material, iCurrMat, &span ); + if (j == -1) + break; + + if( (fLayerState[0].fBlendFlags & hsGMatState::kBlendAlpha) + &&(material->GetLayer(iCurrMat)->GetOpacity() <= 0) + &&(fCurrLightingMethod != plSpan::kLiteVtxPreshaded) ) // This opt isn't good for particles, since their + // material opacity is undefined/unused... -mcn + continue; + + plProfile_BeginTiming(SpanFog); + ISetFogParameters(&span, material->GetLayer(iCurrMat)); + plProfile_EndTiming(SpanFog); + + ISetShaders(material->GetLayer(iCurrMat)->GetVertexShader(), material->GetLayer(iCurrMat)->GetPixelShader()); + + if( normalLightsDisabled ) + IRestoreSpanLights(); + +#ifdef HS_DEBUGGING + DWORD nPass; + fSettings.fDXError = fD3DDevice->ValidateDevice(&nPass); + if( fSettings.fDXError != D3D_OK ) + IGetD3DError(); +#endif // HS_DEBUGGING + + // Do the regular draw. + render.RenderPrims(); + + // Take care of projections that get applied to each pass. + if( fLights.fProjEach.GetCount() && !(fView.fRenderState & kRenderNoProjection) ) + { + // Disable all the normal lights. + IDisableSpanLights(); + normalLightsDisabled = true; + + IRenderProjectionEach(render, material, iCurrMat, span); + + } + if (IsDebugFlagSet(plPipeDbg::kFlagNoUpperLayers)) + j = material->GetNumLayers(); + } + IPopPiggyBacks(); + + // If we disabled lighting, re-enable it. + if( normalLightsDisabled ) + IRestoreSpanLights(); + + // Render any aux spans associated. + if( span.GetNumAuxSpans() ) + IRenderAuxSpans(span); + + // Only render projections and shadows if we successfully rendered the span. + // j == -1 means we aborted render. + if( j >= 0 ) + { + // Projections that get applied to the frame buffer (after all passes). + if( fLights.fProjAll.GetCount() && !(fView.fRenderState & kRenderNoProjection) ) + IRenderProjections(render); + + // Handle render of shadows onto geometry. + if( fShadows.GetCount() ) + IRenderShadowsOntoSpan(render, &span, material); + } + + // Debug only + if (IsDebugFlagSet(plPipeDbg::kFlagOverlayWire)) + { + IRenderOverWire(render, material, span); + } + plProfile_EndTiming(RenderPrim); + + return false; +} + +// IRenderOverWire /////////////////////////////////////////////////////////////////////////////// +// Debug only, renders wireframe on top of normal render. +void plDXPipeline::IRenderOverWire(const plRenderPrimFunc& render, hsGMaterial* material, const plSpan& span) +{ + UInt32 state = fView.fRenderState; + fView.fRenderState |= plPipeline::kRenderBaseLayerOnly; + static plLayerDepth depth; + depth.SetMiscFlags(depth.GetMiscFlags() | hsGMatState::kMiscWireFrame | hsGMatState::kMiscTwoSided); + depth.SetZFlags((depth.GetZFlags() & ~hsGMatState::kZNoZRead) | hsGMatState::kZIncLayer); + + AppendLayerInterface(&depth, false); + + if( IHandleMaterial( material, 0, &span ) >= 0 ) + { + ISetShaders(nil, nil); + render.RenderPrims(); + } + + RemoveLayerInterface(&depth, false) ; + + fView.fRenderState = state; +} + +// IRenderProjectionEach /////////////////////////////////////////////////////////////////////////////////////// +// Render any lights that are to be projected onto each pass of the object. +void plDXPipeline::IRenderProjectionEach(const plRenderPrimFunc& render, hsGMaterial* material, int iPass, const plSpan& span) +{ + // If this is a bump map pass, forget it, we've already "done" per-pixel lighting. + if( fLayerState[iPass].fMiscFlags & (hsGMatState::kMiscBumpLayer | hsGMatState::kMiscBumpChans) ) + return; + + // Push the LayerShadowBase override. This sets the blend + // to framebuffer as Add/ZNoWrite and AmbientColor = 0. + static plLayerLightBase layLightBase; + + int iNextPass = iPass + fCurrNumLayers; + + if( fSettings.fLimitedProj && (material->GetLayer(iPass)->GetUVWSrc() & ~plLayerInterface::kUVWIdxMask) ) + return; + + // For each projector: + int k; + for( k = 0; k < fLights.fProjEach.GetCount(); k++ ) + { + // Push it's projected texture as a piggyback. + plLightInfo* li = fLights.fProjEach[k]; + + // Lower end boards are iffy on when they'll project correctly. + if( fSettings.fCantProj && !li->GetProperty(plLightInfo::kLPForceProj) ) + continue; + + plLayerInterface* proj = li->GetProjection(); + hsAssert(proj, "A projector with no texture to project?"); + IPushProjPiggyBack(proj); + + // Enable the projecting light only. + plDXLightRef* ref = (plDXLightRef *)li->GetDeviceRef(); + fD3DDevice->LightEnable( ref->fD3DIndex, true ); + + AppendLayerInterface(&layLightBase, false); + + // Render until it's done. + int iRePass = iPass; + while( iRePass < iNextPass ) + { + iRePass = IHandleMaterial( material, iRePass, &span ); + ISetShaders(nil, nil); + + // Do the render with projection. + render.RenderPrims(); + } + + RemoveLayerInterface(&layLightBase, false); + + // Disable the projecting light + fD3DDevice->LightEnable(ref->fD3DIndex, false); + + // Pop it's projected texture off piggyback + IPopProjPiggyBacks(); + + } + +} + +// IRenderProjections /////////////////////////////////////////////////////////// +// Render any projected lights that want to be rendered a single time after +// all passes on the object are complete. +void plDXPipeline::IRenderProjections(const plRenderPrimFunc& render) +{ + IDisableSpanLights(); + int i; + for( i = 0; i < fLights.fProjAll.GetCount(); i++ ) + { + plLightInfo* li = fLights.fProjAll[i]; + + if( fSettings.fCantProj && !li->GetProperty(plLightInfo::kLPForceProj) ) + continue; + + IRenderProjection(render, li); + } + IRestoreSpanLights(); +} + +// IRenderProjection ////////////////////////////////////////////////////////////// +// Render this light's projection onto the frame buffer. +void plDXPipeline::IRenderProjection(const plRenderPrimFunc& render, plLightInfo* li) +{ + plDXLightRef* ref = (plDXLightRef *)li->GetDeviceRef(); + fD3DDevice->LightEnable(ref->fD3DIndex, true); + + plLayerInterface* proj = li->GetProjection(); + + static D3DMATERIAL9 mat; + mat.Diffuse.r = mat.Diffuse.g = mat.Diffuse.b = mat.Diffuse.a = 1.f; + + fD3DDevice->SetMaterial( &mat ); + fD3DDevice->SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL ); + fD3DDevice->SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL ); + + fD3DDevice->SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL ); + + fD3DDevice->SetRenderState( D3DRS_AMBIENT, 0xffffffff ); //@@@ + + // Set the FB blend mode, texture, all that. + ICompositeLayerState(0, proj); + // We should have put ZNoZWrite on during export, but we didn't. + fLayerState[0].fZFlags = hsGMatState::kZNoZWrite; + fCurrNumLayers = 1; + IHandleFirstTextureStage(proj); + + if( proj->GetBlendFlags() & hsGMatState::kBlendInvertFinalColor ) + { + fD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE | D3DTA_COMPLEMENT); + } + + // Seal it up + fLastEndingStage = 1; + fD3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + fD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + fLayerState[1].fBlendFlags = UInt32(-1); + +#ifdef HS_DEBUGGING + DWORD nPass; + fSettings.fDXError = fD3DDevice->ValidateDevice(&nPass); + if( fSettings.fDXError != D3D_OK ) + IGetD3DError(); +#endif // HS_DEBUGGING + + // Okay, render it already. + + render.RenderPrims(); + + fD3DDevice->LightEnable(ref->fD3DIndex, false); +} + +//// IGetBufferD3DFormat ////////////////////////////////////////////////////// +// Convert the dumbest vertex format on the planet (ours) into an FVF code. +// Note the assumption of position, normal, diffuse, and specular. +// We no longer use FVF codes, just shader handles. +long plDXPipeline::IGetBufferD3DFormat( UInt8 format ) const +{ + long fmt, i; + + + switch( format & plGBufferGroup::kSkinWeightMask ) + { + case plGBufferGroup::kSkinNoWeights: + fmt = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_NORMAL; + break; + case plGBufferGroup::kSkin1Weight: + fmt = D3DFVF_XYZB1 | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_NORMAL; + break; + case plGBufferGroup::kSkin2Weights: + fmt = D3DFVF_XYZB2 | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_NORMAL; + break; + case plGBufferGroup::kSkin3Weights: + fmt = D3DFVF_XYZB3 | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_NORMAL; + break; + default: + hsAssert( false, "Bad skin weight value in IGetBufferD3DFormat()" ); + } + if( format & plGBufferGroup::kSkinIndices ) + { + hsAssert(false, "Indexed skinning not supported"); + fmt |= D3DFVF_LASTBETA_UBYTE4; + } + + switch( plGBufferGroup::CalcNumUVs( format ) ) + { + case 0: fmt |= D3DFVF_TEX0; break; + case 1: fmt |= D3DFVF_TEX1; break; + case 2: fmt |= D3DFVF_TEX2; break; + case 3: fmt |= D3DFVF_TEX3; break; + case 4: fmt |= D3DFVF_TEX4; break; + case 5: fmt |= D3DFVF_TEX5; break; + case 6: fmt |= D3DFVF_TEX6; break; + case 7: fmt |= D3DFVF_TEX7; break; + case 8: fmt |= D3DFVF_TEX8; break; + } + + for( i = 0; i < plGBufferGroup::CalcNumUVs( format ); i++ ) + fmt |= D3DFVF_TEXCOORDSIZE3( i ); + + return fmt; +} + +//// IGetBufferFormatSize ///////////////////////////////////////////////////// +// Calculate the vertex stride from the given format. +UInt32 plDXPipeline::IGetBufferFormatSize( UInt8 format ) const +{ + UInt32 size = sizeof( float ) * 6 + sizeof( UInt32 ) * 2; // Position and normal, and two packed colors + + + switch( format & plGBufferGroup::kSkinWeightMask ) + { + case plGBufferGroup::kSkinNoWeights: + break; + case plGBufferGroup::kSkin1Weight: + size += sizeof(float); + break; + default: + hsAssert( false, "Invalid skin weight value in IGetBufferFormatSize()" ); + } + + size += sizeof( float ) * 3 * plGBufferGroup::CalcNumUVs( format ); + + return size; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Plate and PlateManager Functions ///////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// None of this plate code is mine, so your guess is as good as mine. +// I'll throw in comments where I happen to know what it's doing, but a lot +// of this is just ugly. +// The plates are mostly used for debugging/performance tools, but they do +// unfortunately get used for some production things like the cursor. +// By the way, a Plate is just a screen aligned textured quad that is rendered +// on top of the normal scene. mf + +// ICreateGeometry ///////////////////////////////////////////////////////// +// Make a quad suitable for rendering as a tristrip. +void plDXPlateManager::ICreateGeometry(plDXPipeline* pipe) +{ + UInt32 fvfFormat = PLD3D_PLATEFVF; + D3DPOOL poolType = D3DPOOL_DEFAULT; + hsAssert(!pipe->ManagedAlloced(), "Alloc default with managed alloc'd"); + if( FAILED( fD3DDevice->CreateVertexBuffer( 4 * sizeof( plPlateVertex ), + D3DUSAGE_WRITEONLY, + fvfFormat, + poolType, &fVertBuffer, NULL ) ) ) + { + hsAssert( false, "CreateVertexBuffer() call failed!" ); + fCreatedSucessfully = false; + return; + } + PROFILE_POOL_MEM(poolType, 4 * sizeof(plPlateVertex), true, "PlateMgrVtxBuff"); + + /// Lock the buffer + plPlateVertex *ptr; + if( FAILED( fVertBuffer->Lock( 0, 0, (void **)&ptr, D3DLOCK_NOSYSLOCK ) ) ) + { + hsAssert( false, "Failed to lock vertex buffer for writing" ); + fCreatedSucessfully = false; + return; + } + + /// Set 'em up + ptr[ 0 ].fPoint.Set( -0.5f, -0.5f, 0.0f ); + ptr[ 0 ].fColor = 0xffffffff; + ptr[ 0 ].fUV.Set( 0.0f, 0.0f, 0.0f ); + + ptr[ 1 ].fPoint.Set( -0.5f, 0.5f, 0.0f ); + ptr[ 1 ].fColor = 0xffffffff; + ptr[ 1 ].fUV.Set( 0.0f, 1.0f, 0.0f ); + + ptr[ 2 ].fPoint.Set( 0.5f, -0.5f, 0.0f ); + ptr[ 2 ].fColor = 0xffffffff; + ptr[ 2 ].fUV.Set( 1.0f, 0.0f, 0.0f ); + + ptr[ 3 ].fPoint.Set( 0.5f, 0.5f, 0.0f ); + ptr[ 3 ].fColor = 0xffffffff; + ptr[ 3 ].fUV.Set( 1.0f, 1.0f, 0.0f ); + + /// Unlock and we're done! + fVertBuffer->Unlock(); + fCreatedSucessfully = true; + +} + +// IReleaseGeometry //////////////////////////////////////////////////////////// +// Let go of any D3D resources created for this. +void plDXPlateManager::IReleaseGeometry() +{ + if (fVertBuffer) + { + ReleaseObject(fVertBuffer); + PROFILE_POOL_MEM(D3DPOOL_DEFAULT, 4 * sizeof(plPlateVertex), false, "PlateMgrVtxBuff"); + fVertBuffer = nil; + } +} + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plDXPlateManager::plDXPlateManager( plDXPipeline *pipe, IDirect3DDevice9 *device ) : plPlateManager( pipe ), + PLD3D_PLATEFVF( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0) ), + fD3DDevice(device), + fVertBuffer(nil) +{ +} + +plDXPlateManager::~plDXPlateManager() +{ + IReleaseGeometry(); +} + +//// IDrawPlate /////////////////////////////////////////////////////////////// +// Render all currently enabled plates to the screen. +void plDXPlateManager::IDrawToDevice( plPipeline *pipe ) +{ + plDXPipeline *dxPipe = (plDXPipeline *)pipe; + plPlate *plate; + UInt32 scrnWidthDiv2 = fOwner->Width() >> 1; + UInt32 scrnHeightDiv2 = fOwner->Height() >> 1; + D3DXMATRIX mat; + D3DCULL oldCullMode; + + if( !fVertBuffer ) + return; + + // Make sure skinning is disabled. + fD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_DISABLE); + fD3DDevice->SetVertexShader( dxPipe->fSettings.fCurrVertexShader = NULL); + fD3DDevice->SetFVF(dxPipe->fSettings.fCurrFVFFormat = PLD3D_PLATEFVF); + fD3DDevice->SetStreamSource( 0, fVertBuffer, 0, sizeof( plPlateVertex ) ); + plProfile_Inc(VertexChange); + fD3DDevice->SetTransform( D3DTS_VIEW, &d3dIdentityMatrix ); + oldCullMode = dxPipe->fCurrCullMode; + + for( plate = fPlates; plate != nil; plate = plate->GetNext() ) + { + if( plate->IsVisible() ) + { + dxPipe->IDrawPlate( plate ); + + const char *title = plate->GetTitle(); + if( plDebugText::Instance().IsEnabled() && title[ 0 ] != 0 ) + { + hsPoint3 pt; + pt.Set( 0, -0.5, 0 ); + pt = plate->GetTransform() * pt; + pt.fX = pt.fX * scrnWidthDiv2 + scrnWidthDiv2; + pt.fY = pt.fY * scrnHeightDiv2 + scrnHeightDiv2; + pt.fX -= plDebugText::Instance().CalcStringWidth( title ) >> 1; + plDebugText::Instance().DrawString( (UInt16)pt.fX, (UInt16)pt.fY + 1, title, 255, 255, 255, 255, plDebugText::kStyleBold ); + } + + if( plate->GetFlags() & plPlate::kFlagIsAGraph ) + { + plGraphPlate *graph = (plGraphPlate *)plate; + hsPoint3 pt, pt2; + int i; + + if( graph->GetLabelText( 0 )[ 0 ] != 0 ) + { + /// Draw key + const char *str; + + pt.Set( -0.5, -0.5, 0 ); + pt = plate->GetTransform() * pt; + pt.fX = pt.fX * scrnWidthDiv2 + scrnWidthDiv2; + pt.fY = pt.fY * scrnHeightDiv2 + scrnHeightDiv2; + pt.fY += plDebugText::Instance().GetFontHeight(); + + UInt32 numLabels = graph->GetNumLabels(); + if (numLabels > graph->GetNumColors()) + numLabels = graph->GetNumColors(); + + for( i = 0; i < numLabels; i++ ) + { + str = graph->GetLabelText( i ); + if( str[ 0 ] == 0 ) + break; + + pt2 = pt; + pt2.fX -= plDebugText::Instance().CalcStringWidth( str ); + plDebugText::Instance().DrawString( (UInt16)pt2.fX, (UInt16)pt2.fY, str, + graph->GetDataColor( i ), plDebugText::kStyleBold ); + pt.fY += plDebugText::Instance().GetFontHeight(); + } + } + } + } + } + + dxPipe->fCurrCullMode = ( dxPipe->fLayerState[0].fMiscFlags & hsGMatState::kMiscTwoSided ) ? D3DCULL_NONE : oldCullMode; + fD3DDevice->SetRenderState( D3DRS_CULLMODE, dxPipe->fCurrCullMode ); +} + +// IDrawPlate /////////////////////////////////////////////////////////////////////// +// Render this plate, in as many passes as it takes. +void plDXPipeline::IDrawPlate( plPlate *plate ) +{ + int i; + hsGMaterial *material = plate->GetMaterial(); + D3DXMATRIX mat; + + + /// Set up the D3D transform directly + IMatrix44ToD3DMatrix( mat, plate->GetTransform() ); + fD3DDevice->SetTransform( D3DTS_WORLD, &mat ); + mat = d3dIdentityMatrix; + mat(1,1) = -1.0f; + mat(2,2) = 2.0f; + mat(2,3) = 1.0f; + mat(3,2) = -2.0f; + mat(3,3) = 0.0f; + + IPushPiggyBacks(material); + + /// Draw the vertex buffer once for each material pass + for( i = 0; i < material->GetNumLayers(); ) + { + // Stat gather adjust: since IHandleMaterial will count this in the stat gather, + // artificially decrement here so that the plates don't skew the stat gathering + // Taking this out. If the plates are causing more material changes, they should + // show up in the stats. mf + + + i = IHandleMaterial( material, i, nil ); + ISetShaders(nil, nil); + + // To override the transform done by the z-bias + fD3DDevice->SetTransform( D3DTS_PROJECTION, &mat ); + // And this to override cullmode set based on material 2-sidedness. + fD3DDevice->SetRenderState( D3DRS_CULLMODE, fCurrCullMode = D3DCULL_CW ); + + WEAK_ERROR_CHECK( fD3DDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 ) ); + } + + IPopPiggyBacks(); +} + + +/////////////////////////////////////////////////////////////////////////////// +//// Error Message Stuff ////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// IAddErrorMessage //////////////////////////////////////////////////// +// Append the error string to the current error string. +void plDXPipeline::IAddErrorMessage( char *errStr ) +{ + static char str[ 512 ]; + if( errStr && strlen( errStr ) + strlen( fSettings.fErrorStr ) < sizeof( fSettings.fErrorStr ) - 4 ) + { + strcpy( str, fSettings.fErrorStr ); + sprintf( fSettings.fErrorStr, "%s\n(%s)", errStr, str ); + plStatusLog::AddLineS("pipeline.log", fSettings.fErrorStr); + } +} + +// ISetErrorMessage ////////////////////////////////////////////////////////// +// Clear the current error string to the input string. +void plDXPipeline::ISetErrorMessage( char *errStr ) +{ + if( errStr ) + { + strcpy( fSettings.fErrorStr, errStr ); + plStatusLog::AddLineS("pipeline.log", fSettings.fErrorStr); + } + else + fSettings.fErrorStr[ 0 ] = nil; +} + +// IGetD3DError ///////////////////////////////////////////////////////////////// +// Convert the last D3D error code to a string (probably "Conflicting Render State"). +void plDXPipeline::IGetD3DError() +{ + sprintf( fSettings.fErrorStr, "D3DError : %s", (char *)DXGetErrorString9( fSettings.fDXError ) ); +} + +// IShowErrorMessage ///////////////////////////////////////////////////////////// +// Append the string to the running error string. +void plDXPipeline::IShowErrorMessage( char *errStr ) +{ + if( errStr != nil ) + IAddErrorMessage( errStr ); + +// hsAssert( false, fSettings.fErrorStr ); +} + +// ICreateFail //////////////////////////////////////////////////////////////////// +// Called on unrecoverable error during device creation. Frees up anything +// allocated so far, sets the error string, and returns true. +hsBool plDXPipeline::ICreateFail( char *errStr ) +{ + // Don't overwrite any error string we already had + if( fSettings.fErrorStr[ 0 ] == 0 ) + IGetD3DError(); + + if( errStr && *errStr ) + { + IAddErrorMessage( errStr ); + } + else if( !*fSettings.fErrorStr ) + IAddErrorMessage( "unknown" ); + + IReleaseDeviceObjects(); + return true; +} + +// GetErrorString /////////////////////////////////////////////////////////////////////////// +// Return the current error string. +const char *plDXPipeline::GetErrorString() +{ + if( fSettings.fErrorStr[ 0 ] == 0 ) + return nil; + + return fSettings.fErrorStr; +} + + +/////////////////////////////////////////////////////////////////////////////// +//// Miscellaneous Utility Functions ////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// GetDXBitDepth ////////////////////////////////////////////////////////// +// +// From a D3DFORMAT enumeration, return the bit depth associated with it. + +short plDXPipeline::GetDXBitDepth( D3DFORMAT format ) +{ + if( format == D3DFMT_UNKNOWN ) + return 0; + else if( format == D3DFMT_R8G8B8 ) + return 24; + else if( format == D3DFMT_A8R8G8B8 ) + return 32; + else if( format == D3DFMT_X8R8G8B8 ) + return 32; + else if( format == D3DFMT_R5G6B5 ) + return 16; + else if( format == D3DFMT_X1R5G5B5 ) + return 16; + else if( format == D3DFMT_A1R5G5B5 ) + return 16; + else if( format == D3DFMT_A4R4G4B4 ) + return 16; + else if( format == D3DFMT_R3G3B2 ) + return 8; + else if( format == D3DFMT_A8 ) + return 8; + else if( format == D3DFMT_A8R3G3B2 ) + return 16; + else if( format == D3DFMT_X4R4G4B4 ) + return 16; + else if( format == D3DFMT_A8P8 ) + return 16; + else if( format == D3DFMT_P8 ) + return 8; + else if( format == D3DFMT_L8 ) + return 8; + else if( format == D3DFMT_A8L8 ) + return 16; + else if( format == D3DFMT_A4L4 ) + return 8; + else if( format == D3DFMT_V8U8 ) + return 16; + else if( format == D3DFMT_L6V5U5 ) + return 16; + else if( format == D3DFMT_X8L8V8U8 ) + return 32; + else if( format == D3DFMT_Q8W8V8U8 ) + return 32; + else if( format == D3DFMT_V16U16 ) + return 32; +// else if( format == D3DFMT_W11V11U10 ) +// return 32; + /* /// These formats really don't have bit depths associated with them + D3DFMT_UYVY + D3DFMT_YUY2 + D3DFMT_DXT1 + D3DFMT_DXT2 + D3DFMT_DXT3 + D3DFMT_DXT4 + D3DFMT_DXT5 + D3DFMT_VERTEXDATA + */ + else if( format == D3DFMT_D16_LOCKABLE ) + return 16; + else if( format == D3DFMT_D32 ) + return 32; + else if( format == D3DFMT_D15S1 ) + return 16; + else if( format == D3DFMT_D24S8 ) + return 32; + else if( format == D3DFMT_D16 ) + return 16; + else if( format == D3DFMT_D24X8 ) + return 32; + else if( format == D3DFMT_D24X4S4 ) + return 32; + else if( format == D3DFMT_INDEX16 ) + return 16; + else if( format == D3DFMT_INDEX32 ) + return 32; + + // Unsupported translation format--return 0 + return 0; +} + +//// IGetDXFormatName //////////////////////////////////////////////////////// +// +// From a D3DFORMAT enumeration, return the string for it. + +const char *plDXPipeline::IGetDXFormatName( D3DFORMAT format ) +{ + switch( format ) + { + case D3DFMT_UNKNOWN: return "D3DFMT_UNKNOWN"; + case D3DFMT_R8G8B8: return "D3DFMT_R8G8B8"; + case D3DFMT_A8R8G8B8: return "D3DFMT_A8R8G8B8"; + case D3DFMT_X8R8G8B8: return "D3DFMT_X8R8G8B8"; + case D3DFMT_R5G6B5: return "D3DFMT_R5G6B5"; + case D3DFMT_X1R5G5B5: return "D3DFMT_X1R5G5B5"; + case D3DFMT_A1R5G5B5: return "D3DFMT_A1R5G5B5"; + case D3DFMT_A4R4G4B4: return "D3DFMT_A4R4G4B4"; + case D3DFMT_R3G3B2: return "D3DFMT_R3G3B2"; + case D3DFMT_A8: return "D3DFMT_A8"; + case D3DFMT_A8R3G3B2: return "D3DFMT_A8R3G3B2"; + case D3DFMT_X4R4G4B4: return "D3DFMT_X4R4G4B4"; + case D3DFMT_A8P8: return "D3DFMT_A8P8"; + case D3DFMT_P8: return "D3DFMT_P8"; + case D3DFMT_L8: return "D3DFMT_L8"; + case D3DFMT_A8L8: return "D3DFMT_A8L8"; + case D3DFMT_A4L4: return "D3DFMT_A4L4"; + case D3DFMT_V8U8: return "D3DFMT_V8U8"; + case D3DFMT_L6V5U5: return "D3DFMT_L6V5U5"; + case D3DFMT_X8L8V8U8: return "D3DFMT_X8L8V8U8"; + case D3DFMT_Q8W8V8U8: return "D3DFMT_Q8W8V8U8"; + case D3DFMT_V16U16: return "D3DFMT_V16U16"; + //case D3DFMT_W11V11U10: return "D3DFMT_W11V11U10"; + case D3DFMT_UYVY: return "D3DFMT_UYVY"; + case D3DFMT_YUY2: return "D3DFMT_YUY2"; + case D3DFMT_DXT1: return "D3DFMT_DXT1"; +// case D3DFMT_DXT2: return "D3DFMT_DXT2"; +// case D3DFMT_DXT3: return "D3DFMT_DXT3"; +// case D3DFMT_DXT4: return "D3DFMT_DXT4"; + case D3DFMT_DXT5: return "D3DFMT_DXT5"; + case D3DFMT_VERTEXDATA: return "D3DFMT_VERTEXDATA"; + case D3DFMT_D16_LOCKABLE: return "D3DFMT_D16_LOCKABLE"; + case D3DFMT_D32: return "D3DFMT_D32"; + case D3DFMT_D15S1: return "D3DFMT_D15S1"; + case D3DFMT_D24S8: return "D3DFMT_D24S8"; + case D3DFMT_D16: return "D3DFMT_D16"; + case D3DFMT_D24X8: return "D3DFMT_D24X8"; + case D3DFMT_D24X4S4: return "D3DFMT_D24X4S4"; + case D3DFMT_INDEX16: return "D3DFMT_INDEX16"; + case D3DFMT_INDEX32: return "D3DFMT_INDEX32"; + default: return "Bad format"; + } +} + +//// IFPUCheck //////////////////////////////////////////////////////////////// +// Checks the FPU to make sure it's in the right mode +// This should return wSave to allow it to be restored after rendering. +// This is obsolete as of DX8 +void plDXPipeline::IFPUCheck() +{ + WORD wSave, wTemp; + __asm fstcw wSave + if (wSave & 0x300 || // Not single mode + 0x3f != (wSave & 0x3f) || // Exceptions enabled + wSave & 0xC00) // Not round to nearest mode + { + __asm + { + mov ax, wSave + and ax, not 0x300 ;; single mode + or ax, 0x3f ;; disable all exceptions + and ax, not 0xC00 ;; round to nearest mode + mov wTemp, ax + fldcw wTemp + } + } +} + +// PushPiggyBackLayer ///////////////////////////////////////////////////// +// Push a piggy back onto the stack. +plLayerInterface* plDXPipeline::PushPiggyBackLayer(plLayerInterface* li) +{ + fPiggyBackStack.Push(li); + + ISetNumActivePiggyBacks(); + + fForceMatHandle = true; + + return li; +} + +// PopPiggyBackLayer /////////////////////////////////////////////////////////////////// +// Pull the piggy back out of the stack (if it's there). +plLayerInterface* plDXPipeline::PopPiggyBackLayer(plLayerInterface* li) +{ + int idx = fPiggyBackStack.Find(li); + if( fPiggyBackStack.kMissingIndex == idx ) + return nil; + fPiggyBackStack.Remove(idx); + + ISetNumActivePiggyBacks(); + + fForceMatHandle = true; + + return li; +} + +// AppendLayerInterface /////////////////////////////////////////////////////////////////// +// Setup a layer wrapper to wrap around either all layers rendered with or just the base layers. +// Note that a single material has multiple base layers if it takes mutliple passes to render. +// Stays in effect until removed by RemoveLayerInterface. +plLayerInterface* plDXPipeline::AppendLayerInterface(plLayerInterface* li, hsBool onAllLayers) +{ + fForceMatHandle = true; + if( onAllLayers ) + return fOverAllLayer = li->Attach(fOverAllLayer); + else + return fOverBaseLayer = li->Attach(fOverBaseLayer); +} + +// RemoveLayerInterface ////////////////////////////////////////////////////////////////// +// Removes a layer wrapper installed by AppendLayerInterface. +plLayerInterface* plDXPipeline::RemoveLayerInterface(plLayerInterface* li, hsBool onAllLayers) +{ + fForceMatHandle = true; + + if( onAllLayers ) + { + if( !fOverAllLayer ) + return nil; + return fOverAllLayer = fOverAllLayer->Remove(li); + } + + if( !fOverBaseLayer ) + return nil; + + return fOverBaseLayer = fOverBaseLayer->Remove(li); +} + +/////////////////////////////////////////////////////////////////////////////// +//// ShadowSection +//// Shadow specific internal functions +/////////////////////////////////////////////////////////////////////////////// +// See plGLight/plShadowMaster.cpp for more notes. + +// IAttachShadowsToReceivers /////////////////////////////////////////////////////////// +// For each active shadow map (in fShadows), attach it to all of the visible spans in drawable +// that it affects. Shadows explicitly attached via light groups are handled separately in ISetShadowFromGroup. +void plDXPipeline::IAttachShadowsToReceivers(plDrawableSpans* drawable, const hsTArray& visList) +{ + int i; + for( i = 0; i < fShadows.GetCount(); i++ ) + IAttachSlaveToReceivers(i, drawable, visList); +} + +// IAttachSlaveToReceivers ///////////////////////////////////////////////////// +// Find all the visible spans in this drawable affected by this shadow map, +// and attach it to them. +void plDXPipeline::IAttachSlaveToReceivers(int which, plDrawableSpans* drawable, const hsTArray& visList) +{ + plShadowSlave* slave = fShadows[which]; + + // Whether the drawable is a character affects which lights/shadows affect it. + hsBool isChar = drawable->GetNativeProperty(plDrawable::kPropCharacter); + + // If the shadow is part of a light group, it gets handled in ISetShadowFromGroup. + // Unless the drawable is a character (something that moves around indeterminately, + // like the avatar or a physical object), and the shadow affects all characters. + if( slave->ObeysLightGroups() && !(slave->IncludesChars() && isChar) ) + return; + + // Do a space tree harvest looking for spans that are visible and whose bounds + // intercect the shadow volume. + plSpaceTree* space = drawable->GetSpaceTree(); + + static hsBitVector cache; + cache.Clear(); + space->EnableLeaves(visList, cache); + + static hsTArray hitList; + hitList.SetCount(0); + space->HarvestEnabledLeaves(slave->fIsect, cache, hitList); + + // For the visible spans that intercect the shadow volume, attach the shadow + // to all appropriate for receiving this shadow map. + int i; + for( i = 0; i < hitList.GetCount(); i++ ) + { + const plSpan* span = drawable->GetSpan(hitList[i]); + hsGMaterial* mat = drawable->GetMaterial(span->fMaterialIdx); + + // Check that the span isn't flagged as unshadowable, or has + // a material that we can't shadow onto. + if( !IReceivesShadows(span, mat) ) + continue; + + // Check for self shadowing. If the shadow doesn't want self shadowing, + // and the span is part of the shadow caster, then skip. + if( !IAcceptsShadow(span, slave) ) + continue; + + // Add it to this span's shadow list for this frame. + span->AddShadowSlave(fShadows[which]->fIndex); + } + +} + +// ISetShadowFromGroup //////////////////////////////////////////////////////////////////////// +// The light casting this shadow has been explicitly attached to this span, so no need +// for checking bounds, but we do anyway because the artists aren't very conservative +// along those lines. The light has a bitvector indicating which of the current shadows +// are from it (there will be a shadow map for each shadow-light/shadow-caster pair), +// so we look through those shadow maps and if they are acceptable, attach them to +// the span. +// Note that a shadow slave corresponds to a shadow map. +void plDXPipeline::ISetShadowFromGroup(plDrawableSpans* drawable, const plSpan* span, plLightInfo* liInfo) +{ + hsGMaterial* mat = drawable->GetMaterial(span->fMaterialIdx); + + // Check that this span/material combo can receive shadows at all. + if( !IReceivesShadows(span, mat) ) + return; + + const hsBitVector& slaveBits = liInfo->GetSlaveBits(); + int i; + for( i = 0; i < fShadows.GetCount(); i++ ) + { + if( slaveBits.IsBitSet(fShadows[i]->fIndex) ) + { + // Check self shadowing. + if( IAcceptsShadow(span, fShadows[i]) ) + { + // Check for overlapping bounds. + if( fShadows[i]->fIsect->Test(span->fWorldBounds) != kVolumeCulled ) + span->AddShadowSlave(fShadows[i]->fIndex); + } + } + } +} + + +// SubmitShadowSlave //////////////////////////////////////////////////////// +// Puts the slave in a list valid for this frame only. The list will +// be preprocessed at BeginRender. See IPreprocessShadows. + +void plDXPipeline::SubmitShadowSlave(plShadowSlave* slave) +{ + // Check that it's a valid slave. + if( !(slave && slave->fCaster && slave->fCaster->GetKey()) ) + return; + + // A board with limited projection capability (i.e. GeForce1) can't + // do perspective shadows (from point source lights) because it + // requires a count3 uvw on 2 texture units (0,1) simultaneously. Just skip. + if( (fSettings.fLimitedProj || fSettings.fCantProj) && slave->fView.GetPerspective() ) + return; + + // Ref the shadow caster so we're sure it will still be around when we go to + // render it. + slave->fCaster->GetKey()->RefObject(); + + // Keep the shadow slaves in a priority sorted list. For performance reasons, + // we may want only the strongest N or those of a minimum priority. + int i; + for( i = 0; i < fShadows.GetCount(); i++ ) + { + if( slave->fPriority <= fShadows[i]->fPriority ) + break; + } + + // Note that fIndex is no longer the index in the fShadows list, but + // is still used as a unique identifier for this slave. + slave->fIndex = fShadows.GetCount(); + fShadows.Insert(i, slave); +} + +hsScalar blurScale = -1.f; +static const int kL2NumSamples = 3; // Log2(4) + +// IBlurShadowMap ////////////////////////////////////////////////////////////////// +// For a shadow map, we've got a specific (non-general) blurring in mind. +// This could be used as a loose model for more general blurring, but you +// wouldn't want to run a generic texture or render target through this. +// Specifically, we assume: +// Input: +// An RGBA rendertarget with an alpha we want to preserve, and color +// going from black (unused) to white (written). +// A blur factor +// Output: +// The rendertarget with alpha preserved, and the color channel blurred +// appropriately. +// We'll want to minimize our render target changes, so +// we clear our scratch render target to black/white (color/alpha), then +// render additively the color of our input with a zero alpha. The scratch +// accumulates the color sum, but the alpha starts and stays saturated to 100%. +// Then we modulate that back into the input, so the alpha is unchanged, the +// color (within the white region) falls off at the edges. The color outside the +// white region is black and stays black, but we don't care because we'll be ignoring +// that anyway. +// Notice that this depends on the input, each pixel having been all black or all "white". +// Also depends on "white" having 1/N premodulated in, where N is the number of samples. +// That's why we can just sum up the colors, without needing to do a divide. Otherwise +// we'd saturate at 255 during the sum, and the divide would be pointless. +// One other thing we're counting on here, is that we've just been rendering to an +// offscreen, we're done, and we're about to pop our rendertarget, which is going +// to reset a lot of render state that we would otherwise be responsible for here. +// We're hoping that this blur function (if efficient enough) can get called enough times +// per frame to warrant the sins described above. +void plDXPipeline::IBlurShadowMap(plShadowSlave* slave) +{ + plRenderTarget* smap = (plRenderTarget*)slave->fPipeData; + hsScalar scale = slave->fBlurScale; + + // Find a scratch rendertarget which matches the input. + int which = IGetScratchRenderTarget(smap); + plRenderTarget* scratchRT = fBlurScratchRTs[which]; + if( !scratchRT ) + return; + plRenderTarget* destRT = fBlurDestRTs[which]; + if( !destRT ) + return; + + // Set up to render into it. + IBlurSetRenderTarget(scratchRT); + + // Clear it appropriately + fD3DDevice->Clear(0, nil, D3DCLEAR_TARGET, 0xff000000L, 1.0f, 0L); + + // Setup our quad for rendering + ISetBlurQuadToRender(smap); + + // Render the input image into the scratch image, creating the blur. + IRenderBlurFromShadowMap(scratchRT, smap, scale); + + // Set the rendertarget back to src + // Setup renderstate to render it back modulating. + // Render the scratch back into the input. + IRenderBlurBackToShadowMap(smap, scratchRT, destRT); + + // dst is now now slave's rendertarget and smap is the new scratch dst + // for this size. + slave->fPipeData = (void*)destRT; + fBlurDestRTs[which] = smap; +} + +// IGetScratchRenderTarget //////////////////////////////////////////// +// Look for a render target for as scratch space for blurring the input render target. +// Note that the whole blur process requires 3 render targets, the source, +// an intermediate, and the destination (which gets swapped with the source). +// But that's only an extra 2 render targets for all shadow maps of a given +// size. +// Note also that the intermediate is one size smaller than the source, +// to get better blurring through bilerp magnification. +int plDXPipeline::IGetScratchRenderTarget(plRenderTarget* smap) +{ + int which = -1; + switch(smap->GetHeight()) + { + case 512: + which = 9; + break; + case 256: + which = 8; + break; + case 128: + which = 7; + break; + case 64: + which = 6; + break; + case 32: + which = 5; + break; + default: + return false; + } + if( !fBlurScratchRTs[which] ) + { + // We may or may not get back the size we requested here, but if we didn't, + // we aren't going to later, so we might as well stuff the smaller render target + // into the bigger slot. Bad thing is that we might want a smaller render target + // later, and we won't know to look in the bigger slot for it, so we could wind + // up using say two 128x128's (one in the 256 slot, one in the 128 slot). + // This intermediate is one power of 2 smaller than the source. + UInt32 width = smap->GetWidth(); + UInt32 height = smap->GetHeight(); + if( width > 32 ) + { + width >>= 1; + height >>= 1; + } + fBlurScratchRTs[which] = IFindRenderTarget(width, height, smap->GetFlags() & plRenderTarget::kIsOrtho); + } + if( !fBlurDestRTs[which] ) + { + // Destination is same size as source. + UInt32 width = smap->GetWidth(); + UInt32 height = smap->GetHeight(); + fBlurDestRTs[which] = IFindRenderTarget(width, height, smap->GetFlags() & plRenderTarget::kIsOrtho); + } +#ifdef MF_ENABLE_HACKOFF + if( hackOffscreens.kMissingIndex == hackOffscreens.Find(fBlurScratchRTs[which]) ) + hackOffscreens.Append(fBlurScratchRTs[which]); + if( hackOffscreens.kMissingIndex == hackOffscreens.Find(fBlurDestRTs[which]) ) + hackOffscreens.Append(fBlurDestRTs[which]); +#endif // MF_ENABLE_HACKOFF + return which; +} + +// IBlurSetRenderTarget ///////////////////////////////////////////////////////////////////// +// Set the input render target up to be rendered into. This abbreviated version +// of PushRenderTarget is possible because of the special case of the state coming +// in, and that we know we're going to immediately pop the previous render target +// when we're done. +void plDXPipeline::IBlurSetRenderTarget(plRenderTarget* rt) +{ + plDXRenderTargetRef* ref = (plDXRenderTargetRef *)rt->GetDeviceRef(); + // Set the rendertarget + IDirect3DSurface9* main = ref->GetColorSurface(); + IDirect3DSurface9* depth = ref->fD3DDepthSurface; + + fSettings.fCurrD3DMainSurface = main; + fSettings.fCurrD3DDepthSurface = depth; + fD3DDevice->SetRenderTarget(0, main); + fD3DDevice->SetDepthStencilSurface(depth); + + // Now set the correct viewport + D3DVIEWPORT9 vp = { 0, + 0, + rt->GetWidth(), + rt->GetHeight(), + 0.f, 1.f }; + + + WEAK_ERROR_CHECK( fD3DDevice->SetViewport( &vp ) ); +} + + +// IRenderBlurFromShadowMap //////////////////////////////////////////////////////////////////////////////// +// Render a shadow map into a scratch render target multiple times offset slightly to create a blur +// in the color, preserving alpha exactly. It's just rendering a single quad with slight offsets +// in the UVW transform. +void plDXPipeline::IRenderBlurFromShadowMap(plRenderTarget* scratchRT, plRenderTarget* smap, hsScalar scale) +{ + // Quad is set up in camera space. + fD3DDevice->SetTransform(D3DTS_VIEW, &d3dIdentityMatrix); + fD3DDevice->SetTransform(D3DTS_WORLD, &d3dIdentityMatrix); + fD3DDevice->SetTransform(D3DTS_PROJECTION, &d3dIdentityMatrix); + + // Figure out how many passes we'll need. +// const int kNumSamples = 1 << kL2NumSamples; // HACKSAMPLE + const int kNumSamples = mfCurrentTest > 101 ? 8 : 4; + int nPasses = (int)hsCeil(float(kNumSamples) / fSettings.fMaxLayersAtOnce); + int nSamplesPerPass = kNumSamples / nPasses; + + // Attenuate by number of passes, to average as we sum. + DWORD atten = 255 / nPasses; + plConst(float) kAtten(1.f); + atten = DWORD(atten * kAtten); + atten = (atten << 24) + | (atten << 16) + | (atten << 8) + | (atten << 0); + + // Disable skinning + fD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_DISABLE); + // + // AlphaEnable = true + // AlphaTest OFF + fD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + fD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + fD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + fD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS); + + // ZBUFFER disabled + fD3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS); + fD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + fLayerState[0].fZFlags &= ~hsGMatState::kZMask; + fLayerState[0].fZFlags |= hsGMatState::kZNoZWrite | hsGMatState::kZNoZRead; + // + // Cullmode is NONE + fCurrCullMode = D3DCULL_NONE; + fD3DDevice->SetRenderState( D3DRS_CULLMODE, fCurrCullMode ); + + plDXTextureRef* ref = (plDXTextureRef*)smap->GetDeviceRef(); + hsAssert(ref, "Shadow map ref should have been made when it was rendered"); + if( !ref ) + return; + + // TFactor contains the attenuation + fD3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR, atten); + + // Set the N texture stages all to use the same + // src rendertarget texture. + // Blend modes are: + // Stage0: + // Color + // Arg1 = texture + // Op = selectArg1 + // Alpha + // Arg1 = TFACTOR = white + // Op = selectArg1 + // Stage[1..n-1] + // Color + // Arg1 = texture + // Arg2 = current + // Op = AddSigned + // Alpha + // Arg1 = texture + // Arg2 = current + // Op = SelectArg2 + // StageN + // Color/Alpha + // Op = disable + // + // Frame buffer blend is + // SRCBLEND = ONE + // DSTBLEND = ONE + // All texture stages are clamped + // + // Set stage0, then loop over the rest + fD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + fD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + fLayerState[0].fClampFlags = hsGMatState::kClampTexture; + + fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); + + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TFACTOR); + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + fLayerState[0].fBlendFlags = UInt32(-1); + + hsRefCnt_SafeAssign( fLayerRef[0], ref ); + fD3DDevice->SetTexture( 0, ref->fD3DTexture ); + + if( D3DTTFF_COUNT2 != fLayerXformFlags[0] ) + { + fLayerXformFlags[0] = D3DTTFF_COUNT2; + fD3DDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2); + } + fD3DDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0); + fLayerUVWSrcs[0] = 0; + + int i; + for( i = 1; i < nSamplesPerPass; i++ ) + { + fD3DDevice->SetSamplerState(i, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + fD3DDevice->SetSamplerState(i, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + fLayerState[i].fClampFlags = hsGMatState::kClampTexture; + + fD3DDevice->SetTextureStageState(i, D3DTSS_COLORARG1, D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState(i, D3DTSS_COLORARG2, D3DTA_CURRENT); + fD3DDevice->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_ADDSIGNED); + + fD3DDevice->SetTextureStageState(i, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState(i, D3DTSS_ALPHAARG2, D3DTA_CURRENT); + fD3DDevice->SetTextureStageState(i, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + fLayerState[i].fBlendFlags = UInt32(-1); + + hsRefCnt_SafeAssign( fLayerRef[i], ref ); + fD3DDevice->SetTexture( i, ref->fD3DTexture ); + + if( D3DTTFF_COUNT2 != fLayerXformFlags[i] ) + { + fLayerXformFlags[i] = D3DTTFF_COUNT2; + fD3DDevice->SetTextureStageState(i, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2); + } + fD3DDevice->SetTextureStageState(i, D3DTSS_TEXCOORDINDEX, 0); + fLayerUVWSrcs[i] = 0; + } + fD3DDevice->SetTextureStageState(nSamplesPerPass, D3DTSS_COLOROP, D3DTOP_DISABLE); + fD3DDevice->SetTextureStageState(nSamplesPerPass, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + // N offsets are { (-1,-1), (1, -1), (1, 1), (-1, 1) } * offsetScale / size, with + // useful offsetScales probably going from 0.5 to 1.5, but we'll just have + // to experiment and see. Larger values likely to require more than the current + // 4 samples + struct offsetStruct + { + float fU; + float fV; + }; + offsetStruct offsetScale = { scale / scratchRT->GetWidth(), scale / scratchRT->GetHeight() }; + static offsetStruct offsets[8] = { + {-1.f, -1.f}, + {1.f, -1.f}, + {1.f, 1.f}, + {-1.f, 1.f}, + {0.f, -0.5f}, + {0.f, 0.5f}, + {-0.5f, 0.f}, + {0.5f, 0.f} + }; + + int iSample = 0; + // For each pass, + for( i = 0; i < nPasses; i++ ) + { + // Set the N texture stage uv transforms to the + // next N offsets. + int j; + for( j = 0; j < nSamplesPerPass; j++ ) + { + D3DXMATRIX offXfm = d3dIdentityMatrix; + offXfm(2,0) = offsets[iSample].fU * offsetScale.fU; + offXfm(2,1) = offsets[iSample].fV * offsetScale.fV; + fD3DDevice->SetTransform(sTextureStages[j], &offXfm); + fLayerTransform[j] = true; + + iSample++; + } + + // Render our quad + fD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + +// fD3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR, 0L); +// fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); +// fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR); +// fD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADDSIGNED); +// fD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2); + + } +} + +// IRenderBlurBackToShadowMap ///////////////////////////////////////////////////////////////////// +// Render our intermediate blurred map back into a useable shadow map. +void plDXPipeline::IRenderBlurBackToShadowMap(plRenderTarget* smap, plRenderTarget* scratch, plRenderTarget* dst) +{ + // Set the rendertarget + IBlurSetRenderTarget(dst); + + // Clear it appropriately. This might not be necessary, since we're just going to overwrite. + fD3DDevice->Clear(0, nil, D3DCLEAR_TARGET, 0xff000000L, 1.0f, 0L); + + // Scratch has an all white alpha, and the blurred color from smap. But the color + // is a signed biased color. We need to remap [128..255] from scratch into [0..255] + // on dst. Plus, we need to copy the alpha as is from smap into dst. + // So, scratch is texture0, smap is texture1. TFACTOR is 0. + // Color is ADDSIGNED2X(TFACTOR, texture0). + // Alpha is SELECTARG1(texture1, current). + // Then FB blend is just opaque copy. + + // Set Stage0 texture transform + // Clamp still on (from RBFSM) + D3DXMATRIX offXfm = d3dIdentityMatrix; + fD3DDevice->SetTransform(sTextureStages[0], &offXfm); + fD3DDevice->SetTransform(sTextureStages[1], &offXfm); + fLayerTransform[0] = false; + fLayerTransform[1] = false; + + plDXTextureRef* ref = (plDXTextureRef*)scratch->GetDeviceRef(); + hsAssert(ref, "Blur scratch map ref should have been made when it was rendered"); + if( !ref ) + return; + hsRefCnt_SafeAssign( fLayerRef[0], ref ); + fD3DDevice->SetTexture( 0, ref->fD3DTexture ); + + ref = (plDXTextureRef*)smap->GetDeviceRef(); + hsAssert(ref, "Blur src map ref should have been made when it was rendered"); + if( !ref ) + return; + hsRefCnt_SafeAssign( fLayerRef[1], ref ); + fD3DDevice->SetTexture( 1, ref->fD3DTexture ); + + // Stage0: + // Color + // Arg1 = TFACTOR = black + // Arg2 = texture + // Op = ADDSIGNED2X + // Alpha + // Arg1 = texture + // Op = selectArg1 + // Texture = scratch + // Stage1: + // Color + // Arg1 = texture + // Arg2 = current + // Op = selectArg2 + // Alpha + // Arg1 = texture + // Op = selectArg1 + // Texture = smap + // FB blend + // SRCBLEND = ONE + // DSTBLEND = ZERO + + fD3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR, 0L); + fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR); + fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADDSIGNED2X); + + // This alpha will be ignored, because in the next stage we select texture alpha. + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + + fLayerState[0].fBlendFlags = UInt32(-1); + + fD3DDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); + fD3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG2); + + fD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + + fLayerState[1].fBlendFlags = UInt32(-1); + + fD3DDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE); + fD3DDevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + fLastEndingStage = 2; + + fD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + fD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); + + // Our quad should still be setup to go. + fD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + +} + +struct plShadowVertStruct +{ + float fPos[3]; + float fUv[2]; +}; + +// IReleaseBlurVBuffers ////////////////////////////////////////////////////////// +// Free up our blur quad vertex buffers. Note these are in POOL_DEFAULT +void plDXPipeline::IReleaseBlurVBuffers() +{ + const UInt32 kVSize = sizeof(plShadowVertStruct); + int i; + for( i = 0; i < kMaxRenderTargetNext; i++ ) + { + if (fBlurVBuffers[i]) + { + ReleaseObject(fBlurVBuffers[i]); + PROFILE_POOL_MEM(D3DPOOL_DEFAULT, 4 * kVSize, false, "BlurVtxBuff"); + fBlurVBuffers[i] = nil; + } + } +} + +// ICreateBlurVBuffers ////////////////////////////////////////////////////////////////// +// We need a quad for each size of shadow map, because there's a slight dependency +// of UVW coordinates on size of render target. Sucks but it's true. +hsBool plDXPipeline::ICreateBlurVBuffers() +{ + // vertex size is 4 verts, with 4 floats each for position, and 2 floats each for uv. + const UInt32 kVSize = sizeof(plShadowVertStruct); + const UInt32 kVFormat = D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0) ; + + int i; + for( i = 0; i < kMaxRenderTargetNext; i++ ) + { + int width = 0; + int height = 0; + int which = -1; + switch( i ) + { + default: + case 0: + case 1: + case 2: + case 3: + case 4: + break; + case 5: + width = height = 1 << i; + which = i; + break; + case 6: + width = height = 1 << i; + which = i; + break; + case 7: + width = height = 1 << i; + which = i; + break; + case 8: + width = height = 1 << i; + which = i; + break; + case 9: + width = height = 1 << i; + which = i; + break; + } + if( which < 0 ) + continue; + + // positions are { (-0.5,-0.5,0,1), (w-0.5,-0.5,0,1), (w-0.5,h-0.5,0,1), (-0.5,h-0.5,0,1) } + // UVs are { (0,0), (1,0), (1,1), (0,1) } + // So we won't have to bother with indices, we'll put them in as + // p1, p2, p0, p3 and render tristrip + + + // Create the buffer. + IDirect3DVertexBuffer9* vBuffer = nil; + + UInt32 fvfFormat = kVFormat; + hsAssert(!ManagedAlloced(), "Alloc default with managed alloc'd"); + if( FAILED( fD3DDevice->CreateVertexBuffer( 4 * kVSize, + D3DUSAGE_WRITEONLY, + fvfFormat, + D3DPOOL_DEFAULT, + &vBuffer, NULL) ) ) + { + hsAssert( false, "CreateVertexBuffer() call failed!" ); + return false; + } + plShadowVertStruct* ptr = nil; + + /// Lock the buffer and fill it in. + if( FAILED( vBuffer->Lock( 0, 0, (void **)&ptr, 0 ) ) ) + { + hsAssert( false, "Failed to lock vertex buffer for writing" ); + vBuffer->Release(); + return false; + } + PROFILE_POOL_MEM(D3DPOOL_DEFAULT, 4 * kVSize, true, "BlurVtxBuff"); + + plShadowVertStruct vert; + vert.fPos[0] = -1.f; + vert.fPos[1] = -1.f; + vert.fPos[2] = 0.5f; + + vert.fUv[0] = 0.5f / width; + vert.fUv[1] = 1.f + 0.5f / height; + + // P0 + ptr[2] = vert; + + // P1 + ptr[0] = vert; + ptr[0].fPos[0] += 2.f; + ptr[0].fUv[0] += 1.f; + + // P2 + ptr[1] = vert; + ptr[1].fPos[0] += 2.f; + ptr[1].fUv[0] += 1.f; + ptr[1].fPos[1] += 2.f; + ptr[1].fUv[1] -= 1.f; + + // P3 + ptr[3] = vert; + ptr[3].fPos[1] += 2.f; + ptr[3].fUv[1] -= 1.f; + + vBuffer->Unlock(); + + fBlurVBuffers[which] = vBuffer; + } + return true; +} + +// ISetBlurQuadToRender //////////////////////////////////////////////////// +// Select the appropriate blur quad (based on size of shadow map) and set it up to render. +hsBool plDXPipeline::ISetBlurQuadToRender(plRenderTarget* smap) +{ + const UInt32 kVSize = sizeof(plShadowVertStruct); + const UInt32 kVFormat = D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0) ; + + // Each vb will be rendertarget size specific, so select one based on input rendertarget + int which = -1; + switch(smap->GetHeight()) + { + case 512: + which = 9; + break; + case 256: + which = 8; + break; + case 128: + which = 7; + break; + case 64: + which = 6; + break; + case 32: + which = 5; + break; + default: + return false; + } + + // If we haven't created (or have lost) our d3d resources, make them + IDirect3DVertexBuffer9* vBuffer = fBlurVBuffers[which]; + if( !vBuffer ) + { + ICreateBlurVBuffers(); + vBuffer = fBlurVBuffers[which]; + hsAssert(vBuffer, "AllocBlurVBuffers failed"); + } + + HRESULT r = fD3DDevice->SetVertexShader(fSettings.fCurrVertexShader = NULL); + fD3DDevice->SetFVF(fSettings.fCurrFVFFormat = kVFormat); + hsAssert( r == D3D_OK, "Error trying to set the vertex shader!" ); + + hsRefCnt_SafeUnRef(fSettings.fCurrVertexBuffRef); + fSettings.fCurrVertexBuffRef = nil; + + r = fD3DDevice->SetStreamSource(0, vBuffer, 0, kVSize); + plProfile_Inc(VertexChange); + + // No SetIndices, we'll do a direct DrawPrimitive (not DrawIndexedPrimitive) + + // No transforms, we're supplying screen ready verts. + + return true; +} + +// IRenderShadowCasterSpan ////////////////////////////////////////////////////////////////////// +// Render the span into a rendertarget of the correct size, generating +// a depth map from this light to that span. +void plDXPipeline::IRenderShadowCasterSpan(plShadowSlave* slave, plDrawableSpans* drawable, const plIcicle& span) +{ + // Check that it's ready to render. + plProfile_BeginTiming(CheckDyn); + ICheckDynBuffers(drawable, drawable->GetBufferGroup(span.fGroupIdx), &span); + plProfile_EndTiming(CheckDyn); + + plDXVertexBufferRef* vRef = (plDXVertexBufferRef *)drawable->GetVertexRef(span.fGroupIdx, span.fVBufferIdx); + plDXIndexBufferRef* iRef = (plDXIndexBufferRef *)drawable->GetIndexRef(span.fGroupIdx, span.fIBufferIdx); + + HRESULT r; + + if( vRef->fD3DBuffer == nil || iRef->fD3DBuffer == nil ) + { + hsAssert( false, "Trying to render a nil buffer pair!" ); + return; + } + + /// Switch to the vertex buffer we want + if( fSettings.fCurrVertexBuffRef != vRef ) + { + hsRefCnt_SafeAssign( fSettings.fCurrVertexBuffRef, vRef ); + hsAssert( vRef->fD3DBuffer != nil, "Trying to render a buffer pair without a vertex buffer!" ); + vRef->SetRebuiltSinceUsed(true); + } + + if( vRef->RebuiltSinceUsed() ) + { + r = fD3DDevice->SetStreamSource( 0, vRef->fD3DBuffer, 0, vRef->fVertexSize ); + hsAssert( r == D3D_OK, "Error trying to set the stream source!" ); + plProfile_Inc(VertexChange); + + fSettings.fCurrFVFFormat = IGetBufferD3DFormat(vRef->fFormat); + r = fD3DDevice->SetVertexShader(fSettings.fCurrVertexShader = NULL); + fD3DDevice->SetFVF(fSettings.fCurrFVFFormat); + hsAssert( r == D3D_OK, "Error trying to set the vertex shader!" ); + + vRef->SetRebuiltSinceUsed(false); + + } + + if( fSettings.fCurrIndexBuffRef != iRef ) + { + hsRefCnt_SafeAssign( fSettings.fCurrIndexBuffRef, iRef ); + hsAssert( iRef->fD3DBuffer != nil, "Trying to render with a nil index buffer" ); + iRef->SetRebuiltSinceUsed(true); + } + + if( iRef->RebuiltSinceUsed() ) + { + r = fD3DDevice->SetIndices( iRef->fD3DBuffer ); + hsAssert( r == D3D_OK, "Error trying to set the indices!" ); + plProfile_Inc(IndexChange); + iRef->SetRebuiltSinceUsed(false); + } + + UInt32 vStart = span.fVStartIdx; + UInt32 vLength = span.fVLength; + UInt32 iStart = span.fIPackedIdx; + UInt32 iLength= span.fILength; + + plRenderTriListFunc render(fD3DDevice, iRef->fOffset, vStart, vLength, iStart, iLength/3); + + static hsMatrix44 emptyMatrix; + hsMatrix44 m = emptyMatrix; + + ISetupTransforms(drawable, span, m); + + hsBool flip = slave->ReverseCull(); + ISetCullMode(flip); + + render.RenderPrims(); +} + +// IGetULutTextureRef /////////////////////////////////////////////////////////// +// The ULut just translates a U coordinate in range [0..1] into +// color and alpha of U * 255.9f. We just have the one we keep +// lying around. +plDXTextureRef* plDXPipeline::IGetULutTextureRef() +{ + const int width = 256; + const int height = 1; + if( !fULutTextureRef ) + { + UInt32* tData = TRACKED_NEW UInt32[width * height]; + + UInt32* pData = tData; + int j; + for( j = 0; j < height; j++ ) + { + int i; + for( i = 0; i < width; i++ ) + { + *pData = (i << 24) + | (i << 16) + | (i << 8) + | (i << 0); + pData++; + } + } + + plDXTextureRef* ref = TRACKED_NEW plDXTextureRef( D3DFMT_A8R8G8B8, + 1, // Num mip levels + width, height, // width by height + width * height, // numpix + width*height*sizeof(UInt32), // totalsize + width*height*sizeof(UInt32), + nil, // levels data + tData, + false // externData + ); + ref->Link(&fTextureRefList); + + fULutTextureRef = ref; + } + return fULutTextureRef; +} + +// IFindRenderTarget ////////////////////////////////////////////////////////////////// +// Find a matching render target from the pools. We prefer the requested size, but +// will look for a smaller size if there isn't one available. +// Param ortho indicates whether it will be used for orthogonal projection as opposed +// to perspective (directional light vs. point light), but is no longer used. +plRenderTarget* plDXPipeline::IFindRenderTarget(UInt32& width, UInt32& height, hsBool ortho) +{ + hsTArray* pool = nil; + UInt32* iNext = nil; + // NOT CURRENTLY SUPPORTING NON-SQUARE SHADOWS. IF WE DO, CHANGE THIS. + switch(height) + { + case 512: + pool = &fRenderTargetPool512; + iNext = &fRenderTargetNext[9]; + break; + case 256: + pool = &fRenderTargetPool256; + iNext = &fRenderTargetNext[8]; + break; + case 128: + pool = &fRenderTargetPool128; + iNext = &fRenderTargetNext[7]; + break; + case 64: + pool = &fRenderTargetPool64; + iNext = &fRenderTargetNext[6]; + break; + case 32: + pool = &fRenderTargetPool32; + iNext = &fRenderTargetNext[5]; + break; + default: + return nil; + } + plRenderTarget* rt = (*pool)[*iNext]; + if( !rt ) + { + // We didn't find one, try again the next size down. + if( height > 32 ) + return IFindRenderTarget(width >>= 1, height >>= 1, ortho); + + // We must be totally out. Oh well. + return nil; + } + (*iNext)++; + + return rt; +} + +// IPushShadowCastState //////////////////////////////////////////////////////////////////////////////// +// Push all the state necessary to start rendering this shadow map, but independent of the +// actual shadow caster to be rendered into the map. +hsBool plDXPipeline::IPushShadowCastState(plShadowSlave* slave) +{ + plRenderTarget* renderTarg = IFindRenderTarget(slave->fWidth, slave->fHeight, slave->fView.GetOrthogonal()); + if( !renderTarg ) + return false; + + // Let the slave setup the transforms, viewport, etc. necessary to render it's shadow + // map. This just goes into a plViewTransform, we translate that into D3D state ourselves below. + if (!slave->SetupViewTransform(this)) + return false; + + // Turn off fogging and specular. + fD3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + fCurrFog.fEnvPtr = nil; + fD3DDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE); + fLayerState[0].fShadeFlags &= ~hsGMatState::kShadeSpecular; + + // Push the shadow slave's view transform as our current render state. + fSettings.fViewStack.Push(fView); + fView.fCullMaxNodes = 0; + SetViewTransform(slave->fView); + IProjectionMatrixToD3D(); + + // Push the shadow map as the current render target + PushRenderTarget(renderTarg); + + // We'll be rendering the light space distance to the span fragment into + // alpha (color is white), so our camera space position, transformed into light space + // and then converted to [0..255] via our ULut. + + // For stage 0: + // Set uvw src + if( fLayerUVWSrcs[0] != D3DTSS_TCI_CAMERASPACEPOSITION ) + { + fD3DDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION); + fLayerUVWSrcs[0] = D3DTSS_TCI_CAMERASPACEPOSITION; + } + UInt32 xformFlags = D3DTTFF_COUNT3; + + if( xformFlags != fLayerXformFlags[0] ) + { + fLayerXformFlags[0] = xformFlags; + fD3DDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, xformFlags); + } + + // Set texture transform to slave's lut transform. See plShadowMaster::IComputeLUT(). + hsMatrix44 castLUT = slave->fCastLUT; + if( slave->fFlags & plShadowSlave::kCastInCameraSpace ) + { + hsMatrix44 c2w = GetCameraToWorld(); + + castLUT = castLUT * c2w; + } + + D3DXMATRIX tXfm; + IMatrix44ToD3DMatrix(tXfm, castLUT); + + fD3DDevice->SetTransform( sTextureStages[0], &tXfm ); + fLayerTransform[0] = true; + + // Set texture to clamp + fD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + fD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + fLayerState[0].fClampFlags = hsGMatState::kClampTexture; + + DWORD clearColor = 0xff000000L; +// const int l2NumSamples = kL2NumSamples; // HACKSAMPLE + const int l2NumSamples = mfCurrentTest > 101 ? 3 : 2; + DWORD intens; + if( slave->fBlurScale > 0 ) + { + const int kNumSamples = mfCurrentTest > 101 ? 8 : 4; + int nPasses = (int)hsCeil(float(kNumSamples) / fSettings.fMaxLayersAtOnce); + int nSamplesPerPass = kNumSamples / nPasses; + DWORD k = int(128.f / float(nSamplesPerPass)); + intens = (0xff << 24) + | ((128 + k) << 16) + | ((128 + k) << 8) + | ((128 + k) << 0); + clearColor = (0xff << 24) + | ((128 - k) << 16) + | ((128 - k) << 8) + | ((128 - k) << 0); + } + else + intens = 0xffffffff; + + // Note that we discard the shadow caster's alpha here, although we don't + // need to. Even on a 2 texture stage system, we could include the diffuse + // alpha and the texture alpha from the base texture. But we don't. + + // Set color to white. We could accomplish this easier by making the color + // in our ULut white. + fD3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR, intens); + + fSettings.fVeryAnnoyingTextureInvalidFlag = true; + fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR); + fD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); + + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + fLayerState[0].fBlendFlags = UInt32(-1); + + // For stage 1 - disable + fLastEndingStage = 1; + fD3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + fD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + fLayerState[1].fBlendFlags = UInt32(-1); + + // Set texture to U_LUT + plDXTextureRef* ref = IGetULutTextureRef(); + + if( !ref->fD3DTexture ) + { + if( ref->fData ) + IReloadTexture( ref ); + } + + hsRefCnt_SafeAssign( fLayerRef[0], ref ); + fD3DDevice->SetTexture( 0, ref->fD3DTexture ); + + fD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + fD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + fD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); + + fD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS); + + slave->fPipeData = renderTarg; + + // Enable ZBuffering w/ write + fD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE); + fLayerState[0].fZFlags &= ~hsGMatState::kZMask; + + // Clear the render target: + // alpha to white ensures no shadow where there's no caster + // color to black in case we ever get blurring going + // Z to 1 + // Stencil ignored + if( slave->ReverseZ() ) + { + fD3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_GREATEREQUAL); + fD3DDevice->Clear(0, nil, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, clearColor, 0.0f, 0L); + } + else + { + fD3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL); + fD3DDevice->Clear(0, nil, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, clearColor, 1.0f, 0L); + } + + // Bring the viewport in (AFTER THE CLEAR) to protect the alpha boundary. + fView.fTransform.SetViewPort(1, 1, (float)(slave->fWidth-2), (float)(slave->fHeight-2), false); + ISetViewport(); + + inlEnsureLightingOff(); + + // See ISetupShadowLight below for how the shadow light is used. + // The shadow light isn't used in generating the shadow map, it's used + // in projecting the shadow map onto the scene. + ISetupShadowLight(slave); + + return true; +} + +// ISetupShadowLight ////////////////////////////////////////////////////////////////// +// We use the shadow light to modulate the shadow effect in two ways while +// projecting the shadow map onto the scene. +// First, the intensity of the shadow follows the N dot L of the light on +// the surface being projected onto. So on a sphere, the darkening effect +// of the shadow will fall off as the normals go from pointing to the light to +// pointing 90 degrees off. +// Second, we attenuate the whole shadow effect through the lights diffuse color. +// We attenuate for different reasons, like the intensity of the light, or +// to fade out a shadow as it gets too far in the distance to matter. +void plDXPipeline::ISetupShadowLight(plShadowSlave* slave) +{ + plDXLightRef* lRef = INextShadowLight(slave); + + lRef->fD3DInfo.Diffuse.r + = lRef->fD3DInfo.Diffuse.g + = lRef->fD3DInfo.Diffuse.b + = slave->fPower; + + slave->fSelfShadowOn = false; + + if( slave->Positional() ) + { + hsPoint3 position = slave->fLightPos; + lRef->fD3DInfo.Position.x = position.fX; + lRef->fD3DInfo.Position.y = position.fY; + lRef->fD3DInfo.Position.z = position.fZ; + + const float maxRange = 32767.f; + lRef->fD3DInfo.Range = maxRange; + lRef->fD3DInfo.Attenuation0 = 1.f; + lRef->fD3DInfo.Attenuation1 = 0; + lRef->fD3DInfo.Attenuation2 = 0; + + lRef->fD3DInfo.Type = D3DLIGHT_POINT; + } + else + { + hsVector3 dir = slave->fLightDir; + lRef->fD3DInfo.Direction.x = dir.fX; + lRef->fD3DInfo.Direction.y = dir.fY; + lRef->fD3DInfo.Direction.z = dir.fZ; + + lRef->fD3DInfo.Type = D3DLIGHT_DIRECTIONAL; + } + + fD3DDevice->SetLight( lRef->fD3DIndex, &lRef->fD3DInfo ); + + slave->fLightIndex = lRef->fD3DIndex; +} + +// INextShadowLight ///////////////////////////////////////////////////// +// Get a scratch light for this shadow slave and assign it. The slave +// only keeps it for this render frame. +plDXLightRef* plDXPipeline::INextShadowLight(plShadowSlave* slave) +{ + fLights.fShadowLights.ExpandAndZero(fLights.fNextShadowLight+1); + + if( !fLights.fShadowLights[fLights.fNextShadowLight] ) + { + plDXLightRef *lRef = TRACKED_NEW plDXLightRef(); + + /// Assign stuff and update + lRef->fD3DIndex = fLights.ReserveD3DIndex(); + lRef->fOwner = nil; + lRef->fD3DDevice = fD3DDevice; + + lRef->Link( &fLights.fRefList ); + + fLights.fShadowLights[fLights.fNextShadowLight] = lRef; + + // Neutralize it until we need it. + fD3DDevice->LightEnable(lRef->fD3DIndex, false); + + // Some things never change. + memset(&lRef->fD3DInfo, 0, sizeof(lRef->fD3DInfo)); + lRef->fD3DInfo.Ambient.r = lRef->fD3DInfo.Ambient.g = lRef->fD3DInfo.Ambient.b = 0; + lRef->fD3DInfo.Specular.r = lRef->fD3DInfo.Specular.g = lRef->fD3DInfo.Specular.b = 0; + + } + slave->fLightRefIdx = fLights.fNextShadowLight; + + return fLights.fShadowLights[fLights.fNextShadowLight++]; +} + +// IPopShadowCastState /////////////////////////////////////////////////// +// Pop the state set to render this shadow caster, so we're ready to render +// a different shadow caster, or go on to our main render. +hsBool plDXPipeline::IPopShadowCastState(plShadowSlave* slave) +{ + fView = fSettings.fViewStack.Pop(); + + PopRenderTarget(); + fView.fXformResetFlags = fView.kResetProjection | fView.kResetCamera; + + return true; +} + +// IMakeRenderTargetPools ///////////////////////////////////////////////////////////// +// These are actually only used as shadow map pools, but they could be used for other +// render targets. +// All these are created here in a single call because they go in POOL_DEFAULT, so they +// must be created before we start creating things in POOL_MANAGED. +void plDXPipeline::IMakeRenderTargetPools() +{ + hsAssert(!fManagedAlloced, "Allocating rendertargets with managed resources alloced"); + IReleaseRenderTargetPools(); // Just to be sure. + + // Numbers of render targets to be created for each size. + // These numbers were set with multi-player in mind, so should be reconsidered. + // But do keep in mind that there are many things in production assets that cast + // shadows besides the avatar. + plConst(hsScalar) kCount[kMaxRenderTargetNext] = { + 0, // 1x1 + 0, // 2x2 + 0, // 4x4 + 0, // 8x8 + 0, // 16x16 + 32, // 32x32 + 16, // 64x64 + 8, // 128x128 + 4, // 256x256 + 0 // 512x512 + }; + int i; + for( i = 0; i < kMaxRenderTargetNext; i++ ) + { + hsTArray* pool = nil; + switch( i ) + { + default: + case 0: + case 1: + case 2: + case 3: + case 4: + break; + + case 5: + pool = &fRenderTargetPool32; + break; + case 6: + pool = &fRenderTargetPool64; + break; + case 7: + pool = &fRenderTargetPool128; + break; + case 8: + pool = &fRenderTargetPool256; + break; + case 9: + pool = &fRenderTargetPool512; + break; + } + if( pool ) + { + pool->SetCount((int)(kCount[i]+1)); + (*pool)[0] = nil; + (*pool)[(int)(kCount[i])] = nil; + int j; + for( j = 0; j < kCount[i]; j++ ) + { + UInt16 flags = plRenderTarget::kIsTexture | plRenderTarget::kIsProjected; + UInt8 bitDepth = 32; + UInt8 zDepth = 24; + UInt8 stencilDepth = 0; + + // If we ever allow non-square shadows, change this. + int width = 1 << i; + int height = width; + + plRenderTarget* rt = TRACKED_NEW plRenderTarget(flags, width, height, bitDepth, zDepth, stencilDepth); + + // If we've failed to create our render target ref, we're probably out of + // video memory. We'll return nil, and this guy just doesn't get a shadow + // until more video memory turns up (not likely). + if( !SharedRenderTargetRef((*pool)[0], rt) ) + { + delete rt; + pool->SetCount(j+1); + (*pool)[j] = nil; + break; + } + (*pool)[j] = rt; + } + } + } +} + +// IResetRenderTargetPools ///////////////////////////////////////////////////////////////// +// No release of resources, this just resets for the start of a frame. So if a shadow +// slave gets a render target from a pool, once this is called (conceptually at the +// end of the frame), the slave no longer owns that render target. +void plDXPipeline::IResetRenderTargetPools() +{ + int i; + for( i = 0; i < kMaxRenderTargetNext; i++ ) + { + fRenderTargetNext[i] = 0; + fBlurScratchRTs[i] = nil; + fBlurDestRTs[i] = nil; + } + + fLights.fNextShadowLight = 0; +} + +// IPrepShadowCaster //////////////////////////////////////////////////////////////////////// +// Make sure all the geometry in this shadow caster is ready to be rendered. +// Keep in mind the single shadow caster may be multiple spans possibly in +// multiple drawables. +// The tricky part here is that we need to prep each drawable involved, +// but only prep it once. Say the caster is composed of: +// drawableA, span0 +// drawableA, span1 +// drawableB, span0 +// Then we need to call plDrawable::PrepForRender() ONCE on drawableA, +// and once on drawableB. Further, we need to do any necessary CPU +// skinning with ISofwareVertexBlend(drawableA, visList={0,1}) and +// ISofwareVertexBlend(drawableB, visList={1}). +hsBool plDXPipeline::IPrepShadowCaster(const plShadowCaster* caster) +{ + static hsBitVector done; + done.Clear(); + const hsTArray& castSpans = caster->Spans(); + + int i; + for( i = 0; i < castSpans.GetCount(); i++ ) + { + if( !done.IsBitSet(i) ) + { + // We haven't already done this castSpan + + plDrawableSpans* drawable = castSpans[i].fDraw; + + // Start a visList with this index. + static hsTArray visList; + visList.SetCount(0); + visList.Append((Int16)(castSpans[i].fIndex)); + + // We're about to have done this castSpan. + done.SetBit(i); + + // Look forward through castSpans for any other spans + // with the same drawable, and add them to visList. + // We'll handle all the spans from this drawable at once. + int j; + for( j = i+1; j < castSpans.GetCount(); j++ ) + { + if( !done.IsBitSet(j) && (castSpans[j].fDraw == drawable) ) + { + // Add to list + visList.Append((Int16)(castSpans[j].fIndex)); + + // We're about to have done this castSpan. + done.SetBit(j); + } + } + // That's all, prep the drawable. + drawable->PrepForRender( this ); + + // Do any software skinning. + if( !ISoftwareVertexBlend(drawable, visList) ) + return false; + } + } + + return true; +} + +// IRenderShadowCaster //////////////////////////////////////////////// +// Render the shadow caster into the slave's render target, creating a shadow map. +hsBool plDXPipeline::IRenderShadowCaster(plShadowSlave* slave) +{ + const plShadowCaster* caster = slave->fCaster; + + // Setup to render into the slave's render target. + if( !IPushShadowCastState(slave) ) + return false; + + // Get the shadow caster ready to render. + if( !IPrepShadowCaster(slave->fCaster) ) + return false; + + // for each shadowCaster.fSpans + int iSpan; + for( iSpan = 0; iSpan < caster->Spans().GetCount(); iSpan++ ) + { + plDrawableSpans* dr = caster->Spans()[iSpan].fDraw; + const plSpan* sp = caster->Spans()[iSpan].fSpan; + UInt32 spIdx = caster->Spans()[iSpan].fIndex; + + hsAssert(sp->fTypeMask & plSpan::kIcicleSpan, "Shadow casting from non-trimeshes not currently supported"); + + // render shadowcaster.fSpans[i] to rendertarget + if( !(sp->fProps & plSpan::kPropNoShadowCast) ) + IRenderShadowCasterSpan(slave, dr, *(const plIcicle*)sp); + + // Keep track of which shadow slaves this span was rendered into. + // If self-shadowing is off, we use that to determine not to + // project the shadow map onto its source geometry. + sp->SetShadowBit(slave->fIndex); //index set in SubmitShadowSlave + } + + // Debug only. + if( blurScale >= 0.f ) + slave->fBlurScale = blurScale; + + // If this shadow requests being blurred, do it. + if( slave->fBlurScale > 0.f ) + IBlurShadowMap(slave); + + // Finished up, restore previous state. + IPopShadowCastState(slave); + +#if MCN_BOUNDS_SPANS + if (IsDebugFlagSet(plPipeDbg::kFlagShowShadowBounds)) + { + /// Add a span to our boundsIce to show this + IAddBoundsSpan(fBoundsSpans, &slave->fWorldBounds); + } +#endif // MCN_BOUNDS_SPANS + + return true; +} + +// We have a (possibly empty) list of shadows submitted for this frame. +// At BeginRender, we need to accomplish: +// Find render targets for each shadow request of the requested size. +// Render the associated spans into the render targets. Something like the following: +void plDXPipeline::IPreprocessShadows() +{ + plProfile_BeginTiming(PrepShadows); + + // Mark our shared resources as free to be used. + IResetRenderTargetPools(); + + // Some board (possibly the Parhelia) freaked if anistropic filtering + // was enabled when rendering to a render target. We never need it for + // shadow maps, and it is slower, so we just kill it here. + ISetAnisotropy(false); + + // Generate a shadow map for each submitted shadow slave. + // Shadow slave corresponds to one shadow caster paired + // with one shadow light that affects it. So a single caster + // may be in multiple slaves (from different lights), or a + // single light may be in different slaves (affecting different + // casters). The overall number is low in spite of the possible + // permutation explosion, because a slave is only generated + // for a caster being affected (in range etc.) by a light. + int iSlave; + for( iSlave = 0; iSlave < fShadows.GetCount(); iSlave++ ) + { + plShadowSlave* slave = fShadows[iSlave]; + + // Any trouble, remove it from the list for this frame. + if( !IRenderShadowCaster(slave) ) + { + fShadows.Remove(iSlave); + iSlave--; + continue; + } + + } + + // Restore + ISetAnisotropy(true); + + plProfile_EndTiming(PrepShadows); +} + +// IClearShadowSlaves /////////////////////////////////////////////////////////////////////////// +// At EndRender(), we need to clear our list of shadow slaves. They are only valid for one frame. +void plDXPipeline::IClearShadowSlaves() +{ + int i; + for( i = 0; i < fShadows.GetCount(); i++ ) + { + const plShadowCaster* caster = fShadows[i]->fCaster; + caster->GetKey()->UnRefObject(); + } + fShadows.SetCount(0); +} + + +// IRenderShadowsOntoSpan ///////////////////////////////////////////////////////////////////// +// After doing the usual render for a span (all passes), we call the following. +// If the span accepts shadows, this will loop over all the shadows active this +// frame, and apply the ones that intersect this spans bounds. See below for details. +void plDXPipeline::IRenderShadowsOntoSpan(const plRenderPrimFunc& render, const plSpan* span, hsGMaterial* mat) +{ + // We've already computed which shadows affect this span. That's recorded in slaveBits. + const hsBitVector& slaveBits = span->GetShadowSlaves(); + + hsBool first = true; + + int i; + for( i = 0; i < fShadows.GetCount(); i++ ) + { + if( slaveBits.IsBitSet(fShadows[i]->fIndex) ) + { + // This slave affects this span. + if( first ) + { + // On the first, we do all the setup that is independent of + // the shadow slave, so state that needs to get set once before + // projecting any number of shadow maps. + ISetupShadowRcvTextureStages(mat); + + first = false; + + } + + // Now setup any state specific to this shadow slave. + ISetupShadowSlaveTextures(fShadows[i]); + + int selfShadowNow = span->IsShadowBitSet(fShadows[i]->fIndex); + + // We vary the shadow intensity when self shadowing (see below), + // so we cache whether the shadow light is set for regular or + // self shadowing intensity. If what we're doing now is different + // than what we're currently set for, set it again. + if( selfShadowNow != fShadows[i]->fSelfShadowOn ) + { + plDXLightRef* lRef = fLights.fShadowLights[fShadows[i]->fLightRefIdx]; + + // We lower the power on self shadowing, because the artists like to + // crank up the shadow strength to huge values to get a darker shadow + // on the environment, which causes the shadow on the avatar to get + // way too dark. Another way to look at it is when self shadowing, + // the surface being projected onto is going to be very close to + // the surface casting the shadow (because they are the same object). + if( selfShadowNow ) + { + plConst(hsScalar) kMaxSelfPower = 0.3f; + hsScalar power = fShadows[i]->fPower > kMaxSelfPower ? kMaxSelfPower : fShadows[i]->fPower; + lRef->fD3DInfo.Diffuse.r + = lRef->fD3DInfo.Diffuse.g + = lRef->fD3DInfo.Diffuse.b + = power; + } + else + { + lRef->fD3DInfo.Diffuse.r + = lRef->fD3DInfo.Diffuse.g + = lRef->fD3DInfo.Diffuse.b + = fShadows[i]->fPower; + } + fD3DDevice->SetLight(lRef->fD3DIndex, &lRef->fD3DInfo); + + // record which our intensity is now set for. + fShadows[i]->fSelfShadowOn = selfShadowNow; + } + + // Enable the light. + fD3DDevice->LightEnable(fShadows[i]->fLightIndex, true); + +#ifdef HS_DEBUGGING + DWORD nPass; + fSettings.fDXError = fD3DDevice->ValidateDevice(&nPass); + if( fSettings.fDXError != D3D_OK ) + IGetD3DError(); +#endif // HS_DEBUGGING + +#ifndef PLASMA_EXTERNAL_RELEASE + if (!IsDebugFlagSet(plPipeDbg::kFlagNoShadowApply)) +#endif // PLASMA_EXTERNAL_RELEASE + render.RenderPrims(); + + // Disable it again. + fD3DDevice->LightEnable(fShadows[i]->fLightIndex, false); + + } + } + +} + +// ISetupShadowRcvTextureStages //////////////////////////////////////////// +// Set the generic stage states. We'll fill in the specific textures +// for each slave later. +void plDXPipeline::ISetupShadowRcvTextureStages(hsGMaterial* mat) +{ + // Setup for nil shaders to get us back to fixed function pipeline. + ISetShaders(nil, nil); + + // We're whacking about with renderstate independent of current material, + // so make sure the next span processes it's material, even if it's the + // same one. + fForceMatHandle = true; + + // Set the D3D lighting/material model + ISetShadowLightState(mat); + + // Zbuffering on read-only + fD3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL); + fD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + fLayerState[0].fZFlags &= ~hsGMatState::kZMask; + fLayerState[0].fZFlags |= hsGMatState::kZNoZWrite; + + // Stage 0: + // Texture is slave specific + // Texture transform is slave specific + // ColorArg1 = texture + // ColorArg2 = diffuse + // ColorOp = modulate + // AlphaArg1 = texture + // AlphaOp = SelectArg1 + fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + fD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + + if( fLayerUVWSrcs[0] != D3DTSS_TCI_CAMERASPACEPOSITION ) + { + fD3DDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION); + fLayerUVWSrcs[0] = D3DTSS_TCI_CAMERASPACEPOSITION; + } + + // Set texture to clamp + fD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + fD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + fLayerState[0].fClampFlags = hsGMatState::kClampTexture; + + // Stage 1: + // Set texture to ULut + // Texture transform is slave specific + // *** With the optional texture blurring, the state here becomes + // *** partially slave dependent. Specifically, if we've done a blur, + // *** then we want to modulate the lut color value by current (which is + // *** the blurred color), else just select the lut. So we'll just move + // *** the ColorOp down to the slave specific section. + // %%% Okay, get this. The GeForce2 won't take a SelectArg1 on Stage1 if + // %%% we're also trying to use Stage2 to modulate in the diffuse. But + // %%% it WILL let us do a modulate on Stage1. So we're going to make sure + // %%% that our shadowmap texture is white, then we can just modulate them + // %%% with no effect. If we're blurring, we already wanted to modulate, so + // %%% no change there. This means we can set the ColorOp now, rather than + // %%% having to wait for the Slave specific section later. + // ColorArg1 = 1 - ULut + // ColorArg2 = Current + // ColorOp = Modulate + // AlphaArg1 = ULut + // AlphaArg2 = Current + // AlphaOp = Subtract + plDXTextureRef* ref = IGetULutTextureRef(); + if( !ref->fD3DTexture ) + { + if( ref->fData ) + IReloadTexture(ref); + } + hsRefCnt_SafeAssign(fLayerRef[1], ref); + fD3DDevice->SetTexture(1, ref->fD3DTexture); + + // The following commented out block is kind of cool, because it + // bases the darkness of the shadow on the distance between the + // shadow caster and the point receiving the shadow. So, for example, + // the hand's shadow would get darker as it reaches for the lever. + // Unfortunately, it doesn't guarantee that the shadow will completely + // attenuate out at the fAttenDist (in fact, it pretty much guarantees + // that it won't), so shadows will pop in and out. So instead, we'll + // base the color on the distance from the start of the slave. The + // difference is subtle, and usually unnoticable, and we get no popping. + fD3DDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE | D3DTA_COMPLEMENT); + fD3DDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); + fD3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); + + fD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CURRENT); + fD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SUBTRACT); + fLayerState[1].fBlendFlags = UInt32(-1); + + if( fLayerUVWSrcs[1] != D3DTSS_TCI_CAMERASPACEPOSITION ) + { + fD3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION); + fLayerUVWSrcs[1] = D3DTSS_TCI_CAMERASPACEPOSITION; + } + if( D3DTTFF_COUNT3 != fLayerXformFlags[1] ) + { + fLayerXformFlags[1] = D3DTTFF_COUNT3; + fD3DDevice->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT3); + } + + // Set texture to clamp + fD3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + fD3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + fLayerState[1].fClampFlags = hsGMatState::kClampTexture; + + int iNextStage = 2; + + // If mat's base layer is alpha'd, and we have > 3 TMU's factor + // in the base layer's alpha. + if( (fSettings.fMaxLayersAtOnce > 3) && mat->GetLayer(0)->GetTexture() && (mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendAlpha) ) + { + plLayerInterface* layer = mat->GetLayer(0); + + // If the following conditions are met, it means that layer 1 is a better choice to + // get the transparency from. The specific case we're looking for is vertex alpha + // simulated by an invisible second layer alpha LUT (known as the alpha hack). + if( (layer->GetMiscFlags() & hsGMatState::kMiscBindNext) + && mat->GetLayer(1) + && !(mat->GetLayer(1)->GetMiscFlags() & hsGMatState::kMiscNoShadowAlpha) + && !(mat->GetLayer(1)->GetBlendFlags() & hsGMatState::kBlendNoTexAlpha) + && mat->GetLayer(1)->GetTexture() ) + layer = mat->GetLayer(1); + + // Take the texture alpha and modulate the color so far with it. In + // the final shadow map, black will have no effect, white will be maximal + // darkening. + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_COLORARG1, D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE); + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_COLORARG2, D3DTA_CURRENT); + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_COLOROP, D3DTOP_MODULATE); + + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_ALPHAARG2, D3DTA_CURRENT); + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + + // Blend flags to layer blend (alpha +- complement) + fLayerState[iNextStage].fBlendFlags = UInt32(-1); + + // Clamp to whatever the texture wants. + if( fLayerState[iNextStage].fClampFlags ^ layer->GetClampFlags() ) + { + fLayerState[iNextStage].fClampFlags = layer->GetClampFlags(); + IHandleStageClamp(iNextStage); + } + + // Shade to 0 + fLayerState[iNextStage].fShadeFlags = 0; + + // ZFlags to ZNoZWrite + fLayerState[iNextStage].fZFlags = hsGMatState::kZNoZWrite; + + // MiscFlags to layer's misc flags + fLayerState[iNextStage].fMiscFlags = layer->GetMiscFlags(); + + // Set up whatever UVW transform the layer normally uses. + IHandleStageTransform(iNextStage, layer); + + // Normal UVW source. + UInt32 uvwSrc = layer->GetUVWSrc(); + + if( fLayerUVWSrcs[ iNextStage ] != uvwSrc ) + { + fD3DDevice->SetTextureStageState( iNextStage, D3DTSS_TEXCOORDINDEX, uvwSrc ); + fLayerUVWSrcs[ iNextStage ] = uvwSrc; + } + + UInt32 xformFlags; + if( layer->GetMiscFlags() & hsGMatState::kMiscPerspProjection ) + xformFlags = D3DTTFF_COUNT3 | D3DTTFF_PROJECTED; + else if( uvwSrc & (plLayerInterface::kUVWNormal | plLayerInterface::kUVWPosition | plLayerInterface::kUVWReflect) ) + xformFlags = D3DTTFF_COUNT3; + else + xformFlags = D3DTTFF_COUNT2; + + if( xformFlags != fLayerXformFlags[iNextStage] ) + { + fLayerXformFlags[iNextStage] = xformFlags; + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_TEXTURETRANSFORMFLAGS, xformFlags); + } + + // This ref should be pretty safe to use, because we just rendered it. + ref = (plDXTextureRef*)layer->GetTexture()->GetDeviceRef(); + + hsRefCnt_SafeAssign( fLayerRef[iNextStage], ref ); + fD3DDevice->SetTexture( iNextStage, ref->fD3DTexture ); + + iNextStage++; + + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_COLORARG1, D3DTA_DIFFUSE | D3DTA_ALPHAREPLICATE); + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_COLORARG2, D3DTA_CURRENT); + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_COLOROP, D3DTOP_MODULATE); + + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + fLayerState[iNextStage].fBlendFlags = UInt32(-1); + + iNextStage++; + } + + fLayerState[iNextStage].fBlendFlags = UInt32(-1); + + // And seal it up + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_COLOROP, D3DTOP_DISABLE); + fD3DDevice->SetTextureStageState(iNextStage, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + fLayerState[iNextStage].fBlendFlags = UInt32(-1); + + fLastEndingStage = 0; + + // Now set the frame buffer blend + // Remember that white darkens and black is no effect. + // Form is Src * SrcBlend + Dst * DstBlend + // We want inverse Src * Dst, so + // Src * ZERO + Dst * InvSrc + fD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + fD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); + fD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR); + + fLayerState[0].fBlendFlags = UInt32(-1); + + // Turn on alpha test. Alpha of zero means the shadow map depth + // is greater or equal to the surface depth, i.e. the surface + // is between the shadow caster and the light and doesn't receive + // shadow. + fD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); + fD3DDevice->SetRenderState(D3DRS_ALPHAREF, 0x00000001); + fLayerState[0].fBlendFlags |= hsGMatState::kBlendTest; + + fD3DDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE); + fLayerState[0].fShadeFlags &= ~hsGMatState::kShadeSpecular; + + // Set fog color to black + // We should automatically reset it, because our blend mode is -1'd. + fD3DDevice->SetRenderState(D3DRS_FOGCOLOR, 0); + +#ifdef HS_DEBUGGING + DWORD nPass; + fSettings.fDXError = fD3DDevice->ValidateDevice(&nPass); + if( fSettings.fDXError != D3D_OK ) + IGetD3DError(); +#endif // HS_DEBUGGING +} + +// ISetupShadowSlaveTextures ////////////////////////////////////////////// +// Set any state specific to this shadow slave for projecting the slave's +// shadow map onto the surface. +void plDXPipeline::ISetupShadowSlaveTextures(plShadowSlave* slave) +{ + D3DXMATRIX tXfm; + + hsMatrix44 c2w = GetCameraToWorld(); + + // Stage 0: + // Set Stage 0's texture to the slave's rendertarget. + // Set texture transform to slave's camera to texture transform + plRenderTarget* renderTarg = (plRenderTarget*)slave->fPipeData; + hsAssert(renderTarg, "Processing a slave that hasn't been rendered"); + if( !renderTarg ) + return; + plDXTextureRef* ref = (plDXTextureRef*)renderTarg->GetDeviceRef(); + hsAssert(ref, "Shadow map ref should have been made when it was rendered"); + if( !ref ) + return; + + hsRefCnt_SafeAssign( fLayerRef[0], ref ); + fD3DDevice->SetTexture( 0, ref->fD3DTexture ); + + hsMatrix44 cameraToTexture = slave->fWorldToTexture * c2w; + IMatrix44ToD3DMatrix(tXfm, cameraToTexture); + + fD3DDevice->SetTransform( sTextureStages[0], &tXfm ); + fLayerTransform[0] = true; + + // Directional lights (ortho projection) just use COUNT2, point lights use COUNT3|PROJECTED. + UInt32 xformFlags = slave->fView.GetOrthogonal() ? D3DTTFF_COUNT2 : D3DTTFF_COUNT3 | D3DTTFF_PROJECTED; + + if( xformFlags != fLayerXformFlags[0] ) + { + fLayerXformFlags[0] = xformFlags; + fD3DDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, xformFlags); + } + + // Stage 1: the lut + // Set the texture transform to slave's fRcvLUT + hsMatrix44 cameraToLut = slave->fRcvLUT * c2w; + IMatrix44ToD3DMatrix(tXfm, cameraToLut); + + fD3DDevice->SetTransform( sTextureStages[1], &tXfm ); + fLayerTransform[1] = true; + +} + +// ISetShadowLightState ////////////////////////////////////////////////////////////////// +// Set the D3D lighting/material model for projecting the shadow map onto this material. +void plDXPipeline::ISetShadowLightState(hsGMaterial* mat) +{ + IDisableLightsForShadow(); + inlEnsureLightingOn(); + + fCurrLightingMethod = plSpan::kLiteShadow; + + static D3DMATERIAL9 d3dMat; + if( mat && mat->GetNumLayers() && mat->GetLayer(0) ) + d3dMat.Diffuse.r = d3dMat.Diffuse.g = d3dMat.Diffuse.b = mat->GetLayer(0)->GetOpacity(); + else + d3dMat.Diffuse.r = d3dMat.Diffuse.g = d3dMat.Diffuse.b = 1.f; + d3dMat.Diffuse.a = 1.f; + + fD3DDevice->SetMaterial(&d3dMat); + fD3DDevice->SetRenderState( D3DRS_AMBIENT, 0 ); +} + +// IDisableLightsForShadow /////////////////////////////////////////////////////////// +// Disable any lights that are enabled. We'll only want the shadow light illuminating +// the surface. +void plDXPipeline::IDisableLightsForShadow() +{ + int i; + for( i = 0; i < fLights.fLastIndex + 1; i++ ) + { + if( fLights.fEnabledFlags.IsBitSet(i) ) + { + fD3DDevice->LightEnable(i, false); + } + } + fLights.fEnabledFlags.Clear(); +} + +// IEnableShadowLight /////////////////////////////////////////////// +// Enable this shadow slave's light. +// NOT USED. +void plDXPipeline::IEnableShadowLight(plShadowSlave* slave) +{ + fD3DDevice->LightEnable(slave->fLightIndex, true); +} + +// IAcceptsShadow //////////////////////////////////////////////////////////////// +// Only allow self shadowing if requested. +hsBool plDXPipeline::IAcceptsShadow(const plSpan* span, plShadowSlave* slave) +{ + // The span's shadow bits records which shadow maps that span was rendered + // into. + return slave->SelfShadow() || !span->IsShadowBitSet(slave->fIndex); +} + +// IReceivesShadows //////////////////////////////////////////////////////////////////// +// Want artists to be able to just disable shadows for spans where they'll either +// look goofy, or won't contribute. +// Also, if we have less than 3 simultaneous textures, we want to skip anything with +// an alpha'd base layer, unless it's been overriden. +hsBool plDXPipeline::IReceivesShadows(const plSpan* span, hsGMaterial* mat) +{ + if( span->fProps & plSpan::kPropNoShadow ) + return false; + + if( span->fProps & plSpan::kPropForceShadow ) + return true; + + if( span->fProps & (plSpan::kPropSkipProjection | plSpan::kPropProjAsVtx) ) + return false; + + if( (fSettings.fMaxLayersAtOnce < 3) + && mat->GetLayer(0)->GetTexture() + && (mat->GetLayer(0)->GetBlendFlags() & hsGMatState::kBlendAlpha) ) + return false; + +#ifdef ENABLE_INTEL_SHADOWS + // Shouldn't hit this, since we're disabling shadows on the Intel chips, + // but just in case. + // To enable this, you'll need to start passing in the drawable as well. + if( fSettings.fIsIntel ) + { + const plVertexSpan* vertSpan = static_cast(span); + plGBufferGroup* group = drawable->GetBufferGroup(vertSpan->fGroupIdx); + if( !group->GetNumUVs() ) + return false; + } +#endif // ENABLE_INTEL_SHADOWS + + return true; +} + +void plDXPipeline::SubmitClothingOutfit(plClothingOutfit* co) +{ + if (fClothingOutfits.Find(co) == fClothingOutfits.kMissingIndex) + { + fClothingOutfits.Append(co); + if (!fPrevClothingOutfits.RemoveItem(co)) + co->GetKey()->RefObject(); + } +} + +void plDXPipeline::IClearClothingOutfits(hsTArray* outfits) +{ + int i; + for (i = outfits->GetCount() - 1; i >= 0; i--) + { + plClothingOutfit *co = outfits->Get(i); + outfits->Remove(i); + IFreeAvRT((plRenderTarget*)co->fTargetLayer->GetTexture()); + co->fTargetLayer->SetTexture(nil); + co->GetKey()->UnRefObject(); + } +} + +void plDXPipeline::IFillAvRTPool() +{ + fAvNextFreeRT = 0; + fAvRTShrinkValidSince = hsTimer::GetSysSeconds(); + int numRTs = 1; + if (fClothingOutfits.GetCount() > 1) + { + // Just jump to 8 for starters so we don't have to refresh for the 2nd, 4th, AND 8th player + numRTs = 8; + while (numRTs < fClothingOutfits.GetCount()) + numRTs *= 2; + } + + // I could see a 32MB video card going down to 64x64 RTs in extreme cases + // (over 100 players onscreen at once), but really, if such hardware is ever trying to push + // that, the low texture resolution is not going to be your major concern. + for (fAvRTWidth = 1024 >> plMipmap::GetGlobalLevelChopCount(); fAvRTWidth >= 32; fAvRTWidth /= 2) + { + if (IFillAvRTPool(numRTs, fAvRTWidth)) + return; + + // Nope? Ok, lower the resolution and try again. + } +} + +hsBool plDXPipeline::IFillAvRTPool(UInt16 numRTs, UInt16 width) +{ + fAvRTPool.SetCount(numRTs); + int i; + for (i = 0; i < numRTs; i++) + { + UInt16 flags = plRenderTarget::kIsTexture | plRenderTarget::kIsProjected; + UInt8 bitDepth = 32; + UInt8 zDepth = 0; + UInt8 stencilDepth = 0; + fAvRTPool[i] = TRACKED_NEW plRenderTarget(flags, width, width, bitDepth, zDepth, stencilDepth); + + // If anyone fails, release everyone we've created. + if (!MakeRenderTargetRef(fAvRTPool[i])) + { + int j; + for (j = 0; j <= i; j++) + { + delete fAvRTPool[j]; + } + return false; + } + } + return true; +} + +void plDXPipeline::IReleaseAvRTPool() +{ + int i; + for (i = 0; i < fClothingOutfits.GetCount(); i++) + { + fClothingOutfits[i]->fTargetLayer->SetTexture(nil); + } + for (i = 0; i < fPrevClothingOutfits.GetCount(); i++) + { + fPrevClothingOutfits[i]->fTargetLayer->SetTexture(nil); + } + for (i = 0; i < fAvRTPool.GetCount(); i++) + { + delete(fAvRTPool[i]); + } + fAvRTPool.Reset(); +} + +plRenderTarget *plDXPipeline::IGetNextAvRT() +{ + return fAvRTPool[fAvNextFreeRT++]; +} + +void plDXPipeline::IFreeAvRT(plRenderTarget* tex) +{ + UInt32 index = fAvRTPool.Find(tex); + if (index != fAvRTPool.kMissingIndex) + { + hsAssert(index < fAvNextFreeRT, "Freeing an avatar RT that's already free?"); + fAvRTPool[index] = fAvRTPool[fAvNextFreeRT - 1]; + fAvRTPool[fAvNextFreeRT - 1] = tex; + fAvNextFreeRT--; + } +} + +struct plAVTexVert +{ + float fPos[3]; + float fUv[2]; +}; + +void plDXPipeline::IPreprocessAvatarTextures() +{ + plProfile_Set(AvRTPoolUsed, fClothingOutfits.GetCount()); + plProfile_Set(AvRTPoolCount, fAvRTPool.GetCount()); + plProfile_Set(AvRTPoolRes, fAvRTWidth); + plProfile_Set(AvRTShrinkTime, UInt32(hsTimer::GetSysSeconds() - fAvRTShrinkValidSince)); + + IClearClothingOutfits(&fPrevClothingOutfits); // Frees anyone used last frame that we don't need this frame + + const UInt32 kVFormat = D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0); + + if (fClothingOutfits.GetCount() == 0) + return; + + plMipmap *itemBufferTex = nil; + + fForceMatHandle = true; + ISetShaders(nil, nil); // Has a side effect of futzing with our cull settings... + + // Even though we're going to use DrawPrimitiveUP, we explicitly set the current VB ref to nil, + // otherwise we might try and use the same VB ref later, think it hasn't changed, and + // not update our FVF. + hsRefCnt_SafeUnRef(fSettings.fCurrVertexBuffRef); + fSettings.fCurrVertexBuffRef = nil; + fD3DDevice->SetStreamSource(0, NULL, 0, 0); + fD3DDevice->SetFVF(fSettings.fCurrFVFFormat = kVFormat); + fD3DDevice->SetTransform(D3DTS_VIEW, &d3dIdentityMatrix); + fD3DDevice->SetTransform(D3DTS_WORLD, &d3dIdentityMatrix); + fD3DDevice->SetTransform(D3DTS_PROJECTION, &d3dIdentityMatrix); + fD3DDevice->SetRenderState(D3DRS_CULLMODE, fCurrCullMode = D3DCULL_NONE); + fD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_DISABLE); + fD3DDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS); + fD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + fLayerState[0].fZFlags &= ~hsGMatState::kZMask; + fLayerState[0].fZFlags |= hsGMatState::kZNoZWrite | hsGMatState::kZNoZRead; + if (fLayerUVWSrcs[0] != 0) + { + fD3DDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0); + fLayerUVWSrcs[0] = 0; + } + fD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + fD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + fLayerState[0].fClampFlags = hsGMatState::kClampTexture; + if (D3DTTFF_DISABLE != fLayerXformFlags[0]) + { + fLayerXformFlags[0] = D3DTTFF_DISABLE; + fD3DDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE); + } + fD3DDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE); + fLayerState[0].fShadeFlags &= ~hsGMatState::kShadeSpecular; + fD3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + fCurrFog.fEnvPtr = nil; + fD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR); + fD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TFACTOR); + fD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE); + fD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS); + fLayerState[0].fBlendFlags = UInt32(-1); + fD3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + fD3DDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + fLayerState[1].fBlendFlags = UInt32(-1); + inlEnsureLightingOff(); + + int oIdx; + for (oIdx = 0; oIdx < fClothingOutfits.GetCount(); oIdx++) + { + plClothingOutfit *co = fClothingOutfits[oIdx]; + if (co->fBase == nil || co->fBase->fBaseTexture == nil) + continue; + + plRenderTarget *rt = plRenderTarget::ConvertNoRef(co->fTargetLayer->GetTexture()); + if (rt != nil && co->fDirtyItems.Empty()) + { + // we've still got our valid RT from last frame and we have nothing to do. + continue; + } + + if (rt == nil) + { + rt = IGetNextAvRT(); + co->fTargetLayer->SetTexture(rt); + } + + PushRenderTarget(rt); + D3DVIEWPORT9 vp = {0, 0, rt->GetWidth(), rt->GetHeight(), 0.f, 1.f}; + WEAK_ERROR_CHECK(fD3DDevice->SetViewport(&vp)); + + hsScalar uOff = 0.5f / rt->GetWidth(); + hsScalar vOff = 0.5f / rt->GetHeight(); + + // Copy over the base + fD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + fD3DDevice->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA); + fD3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR, 0xffffffff); + fLayerState[0].fBlendFlags = UInt32(-1); + IDrawClothingQuad(-1.f, -1.f, 2.f, 2.f, uOff, vOff, co->fBase->fBaseTexture); + plClothingLayout *layout = plClothingMgr::GetClothingMgr()->GetLayout(co->fBase->fLayoutName); + + int i, j, k; + for (i = 0; i < co->fItems.GetCount(); i++) + { + plClothingItem *item = co->fItems[i]; + //if (!co->fDirtyItems.IsBitSet(item->fTileset)) + // continue; // Not dirty, don't update + + for (j = 0; j < item->fElements.GetCount(); j++) + { + for (k = 0; k < plClothingElement::kLayerMax; k++) + { + if (item->fTextures[j][k] == nil) + continue; + + itemBufferTex = item->fTextures[j][k]; + hsColorRGBA tint = co->GetItemTint(item, k); + if (k >= plClothingElement::kLayerSkinBlend1 && k <= plClothingElement::kLayerSkinLast) + tint.a = co->fSkinBlends[k - plClothingElement::kLayerSkinBlend1]; + + if (k == plClothingElement::kLayerBase) + { + fD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + fD3DDevice->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA); + } + else + { + fD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + fD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + fD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + fD3DDevice->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); + } + fD3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR, tint.ToARGB32()); + fLayerState[0].fBlendFlags = UInt32(-1); + hsScalar screenW = (hsScalar)item->fElements[j]->fWidth / layout->fOrigWidth * 2.f; + hsScalar screenH = (hsScalar)item->fElements[j]->fHeight / layout->fOrigWidth * 2.f; + hsScalar screenX = (hsScalar)item->fElements[j]->fXPos / layout->fOrigWidth * 2.f - 1.f; + hsScalar screenY = (1.f - (hsScalar)item->fElements[j]->fYPos / layout->fOrigWidth) * 2.f - 1.f - screenH; + IDrawClothingQuad(screenX, screenY, screenW, screenH, uOff, vOff, itemBufferTex); + } + } + } + PopRenderTarget(); + co->fDirtyItems.Clear(); + } + // Nothing else sets this render state, so let's just set it back to the default to be safe + fD3DDevice->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA); + fView.fXformResetFlags = fView.kResetAll; + + fClothingOutfits.Swap(fPrevClothingOutfits); +} + +void plDXPipeline::IDrawClothingQuad(hsScalar x, hsScalar y, hsScalar w, hsScalar h, + hsScalar uOff, hsScalar vOff, plMipmap *tex) +{ + const UInt32 kVSize = sizeof(plAVTexVert); + plDXTextureRef* ref = (plDXTextureRef*)tex->GetDeviceRef(); + if (!ref || ref->IsDirty()) + { + MakeTextureRef(nil, tex); + ref = (plDXTextureRef*)tex->GetDeviceRef(); + } + if (!ref->fD3DTexture) + { + if (ref->fData) + IReloadTexture(ref); + } + hsRefCnt_SafeAssign( fLayerRef[0], ref ); + fD3DDevice->SetTexture(0, ref->fD3DTexture); + + plAVTexVert ptr[4]; + plAVTexVert vert; + vert.fPos[0] = x; + vert.fPos[1] = y; + vert.fPos[2] = 0.5f; + vert.fUv[0] = uOff; + vert.fUv[1] = 1.f + vOff; + + // P0 + ptr[2] = vert; + + // P1 + ptr[0] = vert; + ptr[0].fPos[0] += w; + ptr[0].fUv[0] += 1.f; + + // P2 + ptr[1] = vert; + ptr[1].fPos[0] += w; + ptr[1].fUv[0] += 1.f; + ptr[1].fPos[1] += h; + ptr[1].fUv[1] -= 1.f; + + // P3 + ptr[3] = vert; + ptr[3].fPos[1] += h; + ptr[3].fUv[1] -= 1.f; + +#ifdef HS_DEBUGGING + DWORD nPass; + fSettings.fDXError = fD3DDevice->ValidateDevice(&nPass); + if( fSettings.fDXError != D3D_OK ) + IGetD3DError(); +#endif // HS_DEBUGGING + fD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, ptr, kVSize); +} + +/////////////////////////////////////////////////////////////////////////////// +// Test hackery as R&D for water +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// End Test hackery as R&D for water +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +//// Functions from Other Classes That Need to Be Here to Compile Right /////// +/////////////////////////////////////////////////////////////////////////////// + +plPipeline *plPipelineCreate::ICreateDXPipeline( hsWinRef hWnd, const hsG3DDeviceModeRecord *devMode ) +{ + plDXPipeline *pipe = TRACKED_NEW plDXPipeline( hWnd, devMode ); + + // Taken out 8.1.2001 mcn - If we have an error, still return so the client can grab the string +// if( pipe->GetErrorString() != nil ) +// { +// delete pipe; +// pipe = nil; +// } + + return pipe; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPipeline.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPipeline.h new file mode 100644 index 00000000..cbf603a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPipeline.h @@ -0,0 +1,818 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plDX9Pipeline_h +#define _plDX9Pipeline_h + +#include "plPipeline.h" +#include "plDXSettings.h" + +#include "../plSurface/plLayerInterface.h" +#include "hsMatrix44.h" +#include "plFogEnvironment.h" +#include "hsG3DDeviceSelector.h" +#include "hsGeometry3.h" +#include "hsTemplates.h" +#include "hsColorRGBA.h" +#include "hsGDeviceRef.h" +#include "hsPoint2.h" + +class plAccessSpan; +class plAuxSpan; +class plVertexSpan; + +#include "plPlates.h" // Used to define plDXPlateManager + + +//// Defines and Konstants and Other Nifty Stuff ////////////////////////////// + +class plDXLightRef; +class plDXVertexBufferRef; +class plDXIndexBufferRef; +class plDXTextureRef; +class plDXCubeTextureRef; +class plDXVertexShader; +class plDXPixelShader; + +class plShader; + +class plVisMgr; + +//#define HS_D3D_USE_SPECULAR + +class hsGMaterial; +class plMipmap; + +class plLightInfo; +class plCullTree; +class plShadowSlave; +class plShadowCaster; + +struct D3DXMATRIX; + +#ifdef HS_DEBUGGING +#define HS_CHECK_RELEASE +#endif + +#ifndef PLASMA_EXTERNAL_RELEASE +#define PROFILE_POOL_MEM(pool, size, add, id) plDXPipeline::ProfilePoolMem(pool, size, add, id); +#else +#define PROFILE_POOL_MEM(pool, size, add, id) +#endif // PLASMA_EXTERNAL_RELEASE + +extern void D3DSURF_MEMNEW(IDirect3DSurface9* surf); +extern void D3DSURF_MEMNEW(IDirect3DTexture9* tex); +extern void D3DSURF_MEMNEW(IDirect3DCubeTexture9* cTex); +extern void D3DSURF_MEMDEL(IDirect3DSurface9* surf); +extern void D3DSURF_MEMDEL(IDirect3DTexture9* tex); +extern void D3DSURF_MEMDEL(IDirect3DCubeTexture9* cTex); + + +extern void plReleaseObject(IUnknown* x); + +#define ReleaseObject(x) if(x){ plReleaseObject(x); x=NULL; } + +typedef LPDIRECT3D9 (WINAPI * Direct3DCreateProc)( UINT sdkVersion ); + + +//// Helper Classes /////////////////////////////////////////////////////////// + +//// The RenderPrimFunc lets you have one function which does a lot of stuff +// around the actual call to render whatever type of primitives you have, instead +// of duplicating everything because the one line to render is different. +class plRenderPrimFunc +{ +public: + virtual hsBool RenderPrims() const = 0; // return true on error +}; + +//// DX-specific Plate Manager implementation +class plDXPlateManager : public plPlateManager +{ + friend class plDXPipeline; + + public: + + virtual ~plDXPlateManager(); + + protected: + + const long PLD3D_PLATEFVF; + + struct plPlateVertex + { + hsPoint3 fPoint; + UInt32 fColor; + hsPoint3 fUV; + }; + + IDirect3DDevice9 *fD3DDevice; + IDirect3DVertexBuffer9 *fVertBuffer; + + plDXPlateManager( plDXPipeline *pipe, IDirect3DDevice9 *device ); + + void ICreateGeometry(plDXPipeline* pipe); + void IReleaseGeometry(); + + virtual void IDrawToDevice( plPipeline *pipe ); +}; + +//// Class Definition ///////////////////////////////////////////////////////// + +class plDebugTextManager; +struct D3DEnum_DriverInfo; +struct D3DEnum_DeviceInfo; +struct D3DEnum_ModeInfo; +class plGeometrySpan; +class plDrawableSpans; +class plSpan; +class plIcicle; +class hsG3DDeviceModeRecord; +class plDXDeviceRef; +class plParticleSpan; +class plCubicEnvironmap; +class plDXRenderTargetRef; +class plStatusLogDrawer; +class plBinkPlayer; + +class plDXPipeline : public plPipeline +{ +protected: + enum { + kCapsNone = 0x0, + kCapsCompressTextures = 0x1, + kCapsMipmap = 0x2, + kCapsHWTransform = 0x4, + kCapsHWLighting = 0x8, + kCapsZBias = 0x10, + kCapsLinearFog = 0x20, + kCapsExpFog = 0x40, + kCapsExp2Fog = 0x80, + kCapsRangeFog = 0x100, + kCapsWBuffer = 0x200, + kCapsTexBoundToStage = 0x400, + kCapsDither = 0x800, + kCapsLODWatch = 0x1000, + kCapsFSAntiAlias = 0x2000, + kCapsLuminanceTextures = 0x4000, + kCapsDoesSmallTextures = 0x8000, + kCapsDoesWFog = 0x10000, + kCapsPixelFog = 0x20000, + kCapsHasBadYonStuff = 0x40000, + kCapsNoKindaSmallTexs = 0x80000, + + kCapsCubicTextures = 0x200000, + kCapsCubicMipmap = 0x400000 + }; + enum { + kKNone = 0x0, + kKTNT = 0x1 + }; + + plDebugTextManager* fDebugTextMgr; + plDXPlateManager* fPlateMgr; + + // The main D3D interfaces + LPDIRECT3D9 fD3DObject; // The main D3D object + LPDIRECT3DDEVICE9 fD3DDevice; // The D3D rendering device + IDirect3DSurface9* fD3DMainSurface; + IDirect3DSurface9* fD3DDepthSurface; + IDirect3DSurface9* fD3DBackBuff; + + IDirect3DSurface9* fSharedDepthSurface[2]; + D3DFORMAT fSharedDepthFormat[2]; + + // Dynamic buffers + UInt32 fVtxRefTime; + UInt32 fNextDynVtx; + UInt32 fDynVtxSize; + IDirect3DVertexBuffer9* fDynVtxBuff; + hsBool fManagedAlloced; + hsBool fAllocUnManaged; + + + // States + plDXGeneralSettings fSettings; + plDXTweakSettings fTweaks; + plDXStencilSettings fStencil; + hsBool fDeviceLost; + hsBool fDevWasLost; + + hsTArray fCullPolys; + hsTArray fCullHoles; + plDrawableSpans* fCullProxy; + + plDXVertexBufferRef* fVtxBuffRefList; + plDXIndexBufferRef* fIdxBuffRefList; + plDXTextureRef* fTextureRefList; + plTextFont* fTextFontRefList; + plDXRenderTargetRef* fRenderTargetRefList; + plDXVertexShader* fVShaderRefList; + plDXPixelShader* fPShaderRefList; + + hsGMaterial* fCurrMaterial; + plLayerInterface* fCurrLay; + UInt32 fCurrLayerIdx, fCurrNumLayers, fCurrRenderLayer; + UInt32 fCurrLightingMethod; // Based on plSpan flags + + D3DCULL fCurrCullMode; + hsGMatState fMatOverOn; + hsGMatState fMatOverOff; + hsTArray fOverrideMat; + hsGMaterial* fHoldMat; + hsBool fCurrD3DLiteState; + + hsMatrix44 fBumpDuMatrix; + hsMatrix44 fBumpDvMatrix; + hsMatrix44 fBumpDwMatrix; + + hsTArray fOverLayerStack; + plLayerInterface* fOverBaseLayer; + plLayerInterface* fOverAllLayer; + hsTArray fPiggyBackStack; + Int32 fMatPiggyBacks; + Int32 fActivePiggyBacks; + + UINT fCurrentAdapter; + D3DEnum_DriverInfo* fCurrentDriver; + D3DEnum_DeviceInfo* fCurrentDevice; + D3DEnum_ModeInfo* fCurrentMode; + + hsGDeviceRef* fLayerRef[ 8 ]; + hsGMatState fLayerState[ 8 ]; // base stage (0) state is held in base class + hsGMatState fOldLayerState[ 8 ]; + hsBool fLayerTransform[ 8 ]; + float fLayerLODBias[ 8 ]; + UInt32 fLayerUVWSrcs[ 8 ]; + UInt32 fLayerXformFlags[ 8 ]; + UInt32 fLastEndingStage; + hsBool fTexturing; + hsBool fForceMatHandle; + + UInt32 fInSceneDepth; + UInt32 fTextUseTime; // inc'd every frame - stat gather only + static UInt32 fTexManaged; + static UInt32 fTexUsed; + static UInt32 fVtxManaged; + static UInt32 fVtxUsed; + UInt32 fEvictTime; + UInt32 fManagedSeen; + UInt32 fManagedCutoff; + + double fTime; // World time. + UInt32 fFrame; // inc'd every time the camera moves. + UInt32 fRenderCnt; // inc'd every begin scene. + + // View stuff + plDXViewSettings fView; + + hsBitVector fDebugFlags; + UInt32 fDebugSpanGraphY; + + // Fog + plDXFogSettings fCurrFog; + + // Light + plDXLightSettings fLights; + + // Shadows + hsTArray fShadows; + hsTArray fRenderTargetPool512; + hsTArray fRenderTargetPool256; + hsTArray fRenderTargetPool128; + hsTArray fRenderTargetPool64; + hsTArray fRenderTargetPool32; + enum { kMaxRenderTargetNext = 10 }; + UInt32 fRenderTargetNext[kMaxRenderTargetNext]; + plDXTextureRef* fULutTextureRef; + plRenderTarget* fBlurScratchRTs[kMaxRenderTargetNext]; + plRenderTarget* fBlurDestRTs[kMaxRenderTargetNext]; + IDirect3DVertexBuffer9* fBlurVBuffers[kMaxRenderTargetNext]; + UInt32 fBlurVSHandle; + + hsTArray fClothingOutfits; + hsTArray fPrevClothingOutfits; + + // Debug stuff + plDrawableSpans *fBoundsSpans; + hsGMaterial *fBoundsMat; + hsTArray fBSpansToDelete; + + plStatusLogDrawer *fLogDrawer; + + hsBool fVSync; + hsBool fForceDeviceReset; + + void IBeginAllocUnManaged(); + void IEndAllocUnManaged(); + void ICheckTextureUsage(); + void ICheckVtxUsage(); + inline void ICheckVBUsage(plDXVertexBufferRef* vRef); + + hsBool IRefreshDynVertices(plGBufferGroup* group, plDXVertexBufferRef* vRef); + hsBool ICheckAuxBuffers(const plAuxSpan* span); + hsBool ICheckDynBuffers(plDrawableSpans* drawable, plGBufferGroup* group, const plSpan* span); + void ICheckStaticVertexBuffer(plDXVertexBufferRef* vRef, plGBufferGroup* owner, UInt32 idx); + void ICheckIndexBuffer(plDXIndexBufferRef* iRef); + void IFillStaticVertexBufferRef(plDXVertexBufferRef *ref, plGBufferGroup *group, UInt32 idx); + void IFillIndexBufferRef(plDXIndexBufferRef* iRef, plGBufferGroup* owner, UInt32 idx); + void ISetupVertexBufferRef(plGBufferGroup* owner, UInt32 idx, plDXVertexBufferRef* vRef); + void ISetupIndexBufferRef(plGBufferGroup* owner, UInt32 idx, plDXIndexBufferRef* iRef); + void ICreateDynamicBuffers(); + void IReleaseDynamicBuffers(); + + void IAddBoundsSpan( plDrawableSpans *ice, const hsBounds3Ext *bounds, UInt32 bndColor = 0xffff0000 ); + void IAddNormalsSpan( plDrawableSpans *ice, plIcicle *span, plDXVertexBufferRef *vRef, UInt32 bndColor ); + + // Rendering + hsBool IFlipSurface(); + long IGetBufferD3DFormat(UInt8 format) const; + UInt32 IGetBufferFormatSize(UInt8 format) const; + void IGetVisibleSpans( plDrawableSpans* drawable, hsTArray& visList, plVisMgr* visMgr ); + void IRenderSpans( plDrawableSpans *ice, const hsTArray& visList ); + hsBool ILoopOverLayers(const plRenderPrimFunc& render, hsGMaterial* material, const plSpan& span); + void IRenderBufferSpan( const plIcicle& span, + hsGDeviceRef *vb, hsGDeviceRef *ib, + hsGMaterial *material, + UInt32 vStart, UInt32 vLength, UInt32 iStart, UInt32 iLength ); + void IRenderAuxSpan(const plSpan& span, const plAuxSpan* aux); + void IRenderAuxSpans(const plSpan& span); + + // Fog + void IGetVSFogSet(float* const set) const; + void ISetFogParameters(const plSpan* span, const plLayerInterface* baseLay); + + // Lighting + hsGDeviceRef *IMakeLightRef( plLightInfo *owner ); + void IScaleD3DLight( plDXLightRef *ref, hsScalar scale); + void ICalcLighting( const plLayerInterface *currLayer, const plSpan *currSpan ); + void IDisableSpanLights(); + void IRestoreSpanLights(); + void ISelectLights( plSpan *span, int numLights, hsBool proj ); + void IEnableLights( plSpan *span ); + void IMakeLightLists(plVisMgr* visMgr); + void ICheckLighting(plDrawableSpans* drawable, hsTArray& visList, plVisMgr* visMgr); + inline void inlEnsureLightingOff(); + inline void inlEnsureLightingOn(); + void IRenderProjection(const plRenderPrimFunc& render, plLightInfo* li); + void IRenderProjections(const plRenderPrimFunc& render); + void IRenderProjectionEach(const plRenderPrimFunc& render, hsGMaterial* material, int iPass, const plSpan& span); + void IRenderOverWire(const plRenderPrimFunc& render, hsGMaterial* material, const plSpan& span); + + hsBool ISkipBumpMap(hsGMaterial* newMat, UInt32& layer, const plSpan* currSpan) const; + void ISetBumpMatrices(const plLayerInterface* layer, const plSpan* span); + const hsMatrix44& IGetBumpMatrix(UInt32 miscFlags) const; + + // Materials + const hsGMatState& ICompositeLayerState(int which, plLayerInterface* layer); + Int32 IHandleMaterial(hsGMaterial* newMat, UInt32 which, const plSpan* currSpan); + void IHandleFirstTextureStage( plLayerInterface* layer ); + void IHandleShadeMode(); + void IHandleZMode(); + void IHandleMiscMode(); + void IHandleTextureStage(UInt32 stage, plLayerInterface* layer); + void IHandleFirstStageBlend(); + void IHandleBumpEnv(int stage, UInt32 blendFlags); + void IHandleStageBlend(int stage); + void IHandleStageClamp(int stage); + void IHandleStageTransform(int stage, plLayerInterface* layer); + void IHandleTextureMode(plLayerInterface* layer); + void IUseTextureRef(int stage, hsGDeviceRef* dRef, plLayerInterface* layer); + void IStageStop(UInt32 stage); + UInt32 ILayersAtOnce(hsGMaterial* mat, UInt32 which); + hsBool ICanEatLayer(plLayerInterface* lay); + void ISetLayer(UInt32 lay); + void IBottomLayer(); + + // Push special effects + plLayerInterface* IPushOverBaseLayer(plLayerInterface* li); + plLayerInterface* IPopOverBaseLayer(plLayerInterface* li); + plLayerInterface* IPushOverAllLayer(plLayerInterface* li); + plLayerInterface* IPopOverAllLayer(plLayerInterface* li); + + int ISetNumActivePiggyBacks(); + void IPushPiggyBacks(hsGMaterial* mat); + void IPopPiggyBacks(); + void IPushProjPiggyBack(plLayerInterface* li); + void IPopProjPiggyBacks(); + + void ISetPipeConsts(plShader* shader); + HRESULT ISetShaders(plShader* vShader, plShader* pShader); + + // Stenciling + virtual hsBool StencilEnable( hsBool enable ); + virtual void StencilSetCompareFunc( UInt8 func, UInt32 refValue ); + virtual void StencilSetMask( UInt32 mask, UInt32 writeMask ); + virtual void StencilSetOps( UInt8 passOp, UInt8 failOp, UInt8 passButZFailOp ); + virtual hsBool StencilGetCaps( plStencilCaps *caps ); + + hsGDeviceRef *MakeTextureRef( plLayerInterface* layer, plMipmap *b ); + void IReloadTexture( plDXTextureRef *ref ); + void IFillD3DTexture( plDXTextureRef *ref ); + void IFillD3DCubeTexture( plDXCubeTextureRef *ref ); + void IGetD3DTextureFormat( plBitmap *b, D3DFORMAT &formatType, UInt32& texSize ); + void IFormatTextureData( UInt32 formatType, UInt32 numPix, hsRGBAColor32* const src, void *dst ); + void *IGetPixelScratch( UInt32 size ); + hsGDeviceRef *IMakeCubicTextureRef( plLayerInterface* layer, plCubicEnvironmap *cubic ); + hsBool IProcessMipmapLevels( plMipmap *mipmap, UInt32 &numLevels, + UInt32 *&levelSizes, UInt32 &totalSize, + UInt32 &numPixels, void *&textureData, hsBool noMip ); + IDirect3DTexture9 *IMakeD3DTexture( plDXTextureRef *ref, D3DFORMAT formatType ); + IDirect3DCubeTexture9 *IMakeD3DCubeTexture( plDXTextureRef *ref, D3DFORMAT formatType ); + + // Visualization of active occluders + void IMakeOcclusionSnap(); + + hsBool IAvatarSort(plDrawableSpans* d, const hsTArray& visList); + void IBlendVertsIntoBuffer( plSpan* span, + hsMatrix44* matrixPalette, int numMatrices, + UInt8 *src, UInt8 format, UInt32 srcStride, + UInt8 *dest, UInt32 destStride, UInt32 count, UInt16 localUVWChans ); + hsBool ISoftwareVertexBlend( plDrawableSpans* drawable, const hsTArray& visList ); + + + void ILinkDevRef( plDXDeviceRef *ref, plDXDeviceRef **refList ); + void IUnlinkDevRef( plDXDeviceRef *ref ); + + // Properties + inline DWORD inlGetD3DColor( const hsColorRGBA &c ) const; + inline D3DCOLORVALUE inlPlToD3DColor(const hsColorRGBA& c, float a) const; + + // Error handling + void IAddErrorMessage( char *errStr ); + void ISetErrorMessage( char *errStr = nil ); + void IGetD3DError(); + void IShowErrorMessage( char *errStr = nil ); + hsBool ICreateFail( char *errStr ); + + // FPU mode check + void IFPUCheck(); + + // Device initialization + void IInvalidateState(); + void IInitDeviceState(); + void IClearMembers(); + void ISetCaps(); + void IRestrictCaps( const hsG3DDeviceRecord& devRec ); + void ISetGraphicsCapability(UInt32 v); + + hsBool IFindDepthFormat(D3DPRESENT_PARAMETERS& params); + hsBool IFindCompressedFormats(); + hsBool IFindLuminanceFormats(); + hsBool ITextureFormatAllowed( D3DFORMAT format ); + + void ISetCurrentDriver( D3DEnum_DriverInfo *driv ); + void ISetCurrentDevice( D3DEnum_DeviceInfo *dev ); + void ISetCurrentMode( D3DEnum_ModeInfo *mode ); + + hsBool ICreateMaster(); + hsBool ICreateDevice(hsBool windowed); + hsBool ICreateNormalSurfaces(); + + hsBool ICreateDeviceObjects(); + void IReleaseDeviceObjects(); + + hsBool ICreateDynDeviceObjects(); + void IReleaseDynDeviceObjects(); + void IReleaseShaders(); + + hsBool IResetDevice(); + + // View and clipping + void ISetViewport(); + void IUpdateViewVectors() const; + void IRefreshCullTree(); + void ISetAnisotropy(hsBool on); + + // Transforms + D3DXMATRIX& IMatrix44ToD3DMatrix( D3DXMATRIX& dst, const hsMatrix44& src ); + void ITransformsToD3D(); + hsMatrix44 IGetCameraToNDC(); + void IProjectionMatrixToD3D(); + void IWorldToCameraToD3D(); + void ILocalToWorldToD3D(); + void ISavageYonHack(); + void ISetLocalToWorld( const hsMatrix44& l2w, const hsMatrix44& w2l ); + void ISetCullMode(hsBool flip=false); + hsBool inline IIsViewLeftHanded(); + hsBool IGetClearViewPort(D3DRECT& r); + plViewTransform& IGetViewTransform() { return fView.fTransform; } + void IUpdateViewFlags(); + void ISetupTransforms(plDrawableSpans* drawable, const plSpan& span, hsMatrix44& lastL2W); + + // Plate management + friend plDXPlateManager; + friend plBinkPlayer; + + void IDrawPlate( plPlate *plate ); + + void ISetRenderTarget( plRenderTarget *target ); + + hsBool IPrepRenderTargetInfo( plRenderTarget *owner, D3DFORMAT &surfFormat, + D3DFORMAT &depthFormat, D3DRESOURCETYPE &resType ); + hsBool IFindRenderTargetInfo( plRenderTarget *owner, D3DFORMAT &surfFormat, D3DRESOURCETYPE &resType ); + + // From a D3DFORMAT enumeration, return the string literal for it + static const char *IGetDXFormatName( D3DFORMAT format ); + + /////// Shadow internals + // Generation + void IClearShadowSlaves(); + void IPreprocessShadows(); + hsBool IPrepShadowCaster(const plShadowCaster* caster); + void IRenderShadowCasterSpan(plShadowSlave* slave, plDrawableSpans* drawable, const plIcicle& span); + void ISetupShadowCastTextureStages(plShadowSlave* slave); + hsBool IRenderShadowCaster(plShadowSlave* slave); + void ISetupShadowLight(plShadowSlave* slave); + plDXLightRef* INextShadowLight(plShadowSlave* slave); + + hsBool IPushShadowCastState(plShadowSlave* slave); + hsBool IPopShadowCastState(plShadowSlave* slave); + + plDXTextureRef* IGetULutTextureRef(); + hsBool ICreateBlurVBuffers(); + void IReleaseBlurVBuffers(); + void IMakeRenderTargetPools(); + void IResetRenderTargetPools(); + plRenderTarget* IFindRenderTarget(UInt32& w, UInt32& h, hsBool ortho); + void IReleaseRenderTargetPools(); + + // Selection + void IAttachSlaveToReceivers(int iSlave, plDrawableSpans* drawable, const hsTArray& visList); + void IAttachShadowsToReceivers(plDrawableSpans* drawable, const hsTArray& visList); + hsBool IAcceptsShadow(const plSpan* span, plShadowSlave* slave); + hsBool IReceivesShadows(const plSpan* span, hsGMaterial* mat); + void ISetShadowFromGroup(plDrawableSpans* drawable, const plSpan* span, plLightInfo* liInfo); + + // Application + void IRenderShadowsOntoSpan(const plRenderPrimFunc& render, const plSpan* span, hsGMaterial* mat); + void ISetupShadowRcvTextureStages(hsGMaterial* mat); + void ISetShadowLightState(hsGMaterial* mat); + void IDisableLightsForShadow(); + void IEnableShadowLight(plShadowSlave* slave); + void ISetupShadowSlaveTextures(plShadowSlave* slave); + + // Postprocess (blurring) + hsBool ISetBlurQuadToRender(plRenderTarget* smap); + void IRenderBlurBackToShadowMap(plRenderTarget* smap, plRenderTarget* scratch, plRenderTarget* dst); + void IRenderBlurFromShadowMap(plRenderTarget* scratchRT, plRenderTarget* smap, hsScalar scale); + void IBlurSetRenderTarget(plRenderTarget* rt); + int IGetScratchRenderTarget(plRenderTarget* smap); + void IBlurShadowMap(plShadowSlave* slave); + + // Avatar Texture Rendering + double fAvRTShrinkValidSince; + hsTArray fAvRTPool; + UInt16 fAvRTWidth; + UInt32 fAvNextFreeRT; + void IFillAvRTPool(); + hsBool IFillAvRTPool(UInt16 numRTs, UInt16 width); // Returns true if we successfully filled the pool. Otherwise cleans up. + void IReleaseAvRTPool(); + plRenderTarget* IGetNextAvRT(); + void IFreeAvRT(plRenderTarget* tex); + void IPreprocessAvatarTextures(); + void IDrawClothingQuad(hsScalar x, hsScalar y, hsScalar w, hsScalar h, hsScalar uOff, hsScalar vOff, plMipmap *tex); + void IClearClothingOutfits(hsTArray* outfits); + + void IPrintDeviceInitError(); + + void IResetToDefaults(D3DPRESENT_PARAMETERS *params); + +public: + plDXPipeline( hsWinRef hWnd, const hsG3DDeviceModeRecord *devMode ); + virtual ~plDXPipeline(); + + CLASSNAME_REGISTER( plDXPipeline ); + GETINTERFACE_ANY( plDXPipeline, plPipeline ); + + virtual IDirect3DDevice9* GetD3DDevice() const { return fD3DDevice; } + + // Typical 3D device + virtual hsBool PreRender(plDrawable* drawable, hsTArray& visList, plVisMgr* visMgr=nil); + virtual hsBool PrepForRender(plDrawable* drawable, hsTArray& visList, plVisMgr* visMgr=nil); + virtual void Render(plDrawable* d, const hsTArray& visList); + virtual void Draw(plDrawable* d); + + virtual void PushRenderRequest(plRenderRequest* req); + virtual void PopRenderRequest(plRenderRequest* req); + + void ResetDisplayDevice(int Width, int Height, int ColorDepth, hsBool Windowed, int NumAASamples, int MaxAnisotropicSamples, hsBool VSync = false ); + + virtual void ClearRenderTarget( plDrawable* d ); + virtual void ClearRenderTarget( const hsColorRGBA* col = nil, const hsScalar* depth = nil ); + virtual void SetClear(const hsColorRGBA* col=nil, const hsScalar* depth=nil); + virtual hsColorRGBA GetClearColor() const; + virtual hsScalar GetClearDepth() const; + virtual hsGDeviceRef* MakeRenderTargetRef( plRenderTarget *owner ); + virtual hsGDeviceRef* SharedRenderTargetRef(plRenderTarget* sharer, plRenderTarget *owner); + virtual void PushRenderTarget( plRenderTarget *target ); + virtual plRenderTarget* PopRenderTarget(); + + virtual hsBool BeginRender(); + virtual hsBool EndRender(); + virtual void RenderScreenElements(); + + virtual hsBool BeginDrawable(plDrawable* d); + virtual hsBool EndDrawable(plDrawable* d); + + virtual void BeginVisMgr(plVisMgr* visMgr); + virtual void EndVisMgr(plVisMgr* visMgr); + + virtual hsBool IsFullScreen() const { return fSettings.fFullscreen; } + virtual UInt32 Width() const { return fView.fTransform.GetViewPortWidth(); } + virtual UInt32 Height() const { return fView.fTransform.GetViewPortHeight(); } + virtual UInt32 ColorDepth() const { return fSettings.fColorDepth; } + virtual void Resize( UInt32 width, UInt32 height ); + + // Culling. Might be used in Update before bothering to do any serious computation. + virtual hsBool TestVisibleWorld(const hsBounds3Ext& wBnd); + virtual hsBool TestVisibleWorld(const plSceneObject* sObj); + virtual hsBool HarvestVisible(plSpaceTree* space, hsTArray& visList); + virtual hsBool SubmitOccluders(const hsTArray& polyList); + + // Debug flags + virtual void SetDebugFlag( UInt32 flag, hsBool on ); + virtual hsBool IsDebugFlagSet( UInt32 flag ) const; + + // These are also only for debugging. + virtual void SetMaxCullNodes(UInt16 n) { fView.fCullMaxNodes = n; } + virtual UInt16 GetMaxCullNodes() const { return fView.fCullMaxNodes; } + + virtual hsBool CheckResources(); + virtual void LoadResources(); // Tells us where it's a good time to load in unmanaged resources. + + // Properties + virtual void SetProperty( UInt32 prop, hsBool on ) { on ? fSettings.fProperties |= prop : fSettings.fProperties &= ~prop; } + virtual hsBool GetProperty( UInt32 prop ) const { return ( fSettings.fProperties & prop ) ? true : false; } + + virtual UInt32 GetMaxLayersAtOnce() const { return fSettings.fMaxLayersAtOnce; } + + // Drawable type mask + virtual void SetDrawableTypeMask( UInt32 mask ) { fView.fDrawableTypeMask = mask; } + virtual UInt32 GetDrawableTypeMask() const { return fView.fDrawableTypeMask; } + virtual void SetSubDrawableTypeMask( UInt32 mask ) { fView.fSubDrawableTypeMask = mask; } + virtual UInt32 GetSubDrawableTypeMask() const { return fView.fSubDrawableTypeMask; } + + // Create a debug text font object + virtual plTextFont *MakeTextFont( char *face, UInt16 size ); + + // Create and/or Refresh geometry buffers + virtual void CheckVertexBufferRef(plGBufferGroup* owner, UInt32 idx); + virtual void CheckIndexBufferRef(plGBufferGroup* owner, UInt32 idx); + + virtual hsBool OpenAccess(plAccessSpan& dst, plDrawableSpans* d, const plVertexSpan* span, hsBool readOnly); + virtual hsBool CloseAccess(plAccessSpan& acc); + + virtual void CheckTextureRef(plLayerInterface* lay); + static void FreeManagedTexture(UInt32 sz) { hsAssert(fTexManaged >= sz, "Freeing mem we don't have"); fTexManaged -= sz; } + static void AllocManagedTexture(UInt32 sz) { fTexManaged += sz; } + static void FreeManagedVertex(UInt32 sz) { hsAssert(fVtxManaged >= sz, "Freeing mem we don't have"); fVtxManaged -= sz; } + static void AllocManagedVertex(UInt32 sz) { fVtxManaged += sz; } + +#ifndef PLASMA_EXTERNAL_RELEASE + static void ProfilePoolMem(D3DPOOL poolType, UInt32 size, hsBool add, char *id); +#endif // PLASMA_EXTERNAL_RELEASE + + // From a D3DFORMAT enumeration, return the bit depth associated with it. + static short GetDXBitDepth( D3DFORMAT format ); + + // Default fog settings + virtual void SetDefaultFogEnviron( plFogEnvironment *fog ) { fView.fDefaultFog = *fog; fCurrFog.fEnvPtr = nil; } + virtual const plFogEnvironment &GetDefaultFogEnviron() const { return fView.fDefaultFog; } + + // View state + virtual hsPoint3 GetViewPositionWorld() const { return GetViewTransform().GetPosition(); } + virtual hsVector3 GetViewAcrossWorld() const { return GetViewTransform().GetAcross(); } + virtual hsVector3 GetViewUpWorld() const { return GetViewTransform().GetUp(); } + virtual hsVector3 GetViewDirWorld() const { return GetViewTransform().GetDirection(); } + virtual void GetViewAxesWorld(hsVector3 axes[3] /* ac,up,at */ ) const; + + virtual void GetFOV(hsScalar& fovX, hsScalar& fovY) const; + virtual void SetFOV(hsScalar fovX, hsScalar fovY); + + virtual void GetSize(hsScalar& width, hsScalar& height) const; + virtual void SetSize(hsScalar width, hsScalar height); + + virtual void GetDepth(hsScalar& hither, hsScalar& yon) const; + virtual void SetDepth(hsScalar hither, hsScalar yon); + + virtual hsScalar GetZBiasScale() const; + virtual void SetZBiasScale(hsScalar scale); + + virtual const hsMatrix44& GetWorldToCamera() const; + virtual const hsMatrix44& GetCameraToWorld() const; + virtual void SetWorldToCamera(const hsMatrix44& w2c, const hsMatrix44& c2w); + + virtual void SetViewTransform(const plViewTransform& trans); + virtual const plViewTransform& GetViewTransform() const { return fView.fTransform; } + + virtual const hsMatrix44& GetWorldToLocal() const; + virtual const hsMatrix44& GetLocalToWorld() const; + + virtual void ScreenToWorldPoint( int n, UInt32 stride, Int32 *scrX, Int32 *scrY, + hsScalar dist, UInt32 strideOut, hsPoint3 *worldOut ); + + virtual void RefreshMatrices(); + virtual void RefreshScreenMatrices(); + + virtual void RegisterLight(plLightInfo* light); + virtual void UnRegisterLight(plLightInfo* light); + + // Overrides, always push returns whatever is necessary to restore on pop. + virtual hsGMaterial* PushOverrideMaterial(hsGMaterial* mat); + virtual void PopOverrideMaterial(hsGMaterial* restore); + virtual hsGMaterial* GetOverrideMaterial() const; + + virtual plLayerInterface* AppendLayerInterface(plLayerInterface* li, hsBool onAllLayers = false); + virtual plLayerInterface* RemoveLayerInterface(plLayerInterface* li, hsBool onAllLayers = false); + + virtual plLayerInterface* PushPiggyBackLayer(plLayerInterface* li); + virtual plLayerInterface* PopPiggyBackLayer(plLayerInterface* li); + + virtual UInt32 GetMaterialOverrideOn(hsGMatState::StateIdx category) const; + virtual UInt32 GetMaterialOverrideOff(hsGMatState::StateIdx category) const; + + virtual hsGMatState PushMaterialOverride(const hsGMatState& state, hsBool on); + virtual hsGMatState PushMaterialOverride(hsGMatState::StateIdx cat, UInt32 which, hsBool on); + virtual void PopMaterialOverride(const hsGMatState& restore, hsBool on); + virtual const hsGMatState& GetMaterialOverride(hsBool on) const; + + virtual hsColorOverride PushColorOverride(const hsColorOverride& over); + virtual void PopColorOverride(const hsColorOverride& restore); + virtual const hsColorOverride& GetColorOverride() const; + + virtual void SubmitShadowSlave(plShadowSlave* slave); + virtual void SubmitClothingOutfit(plClothingOutfit* co); + + virtual hsBool SetGamma(hsScalar eR, hsScalar eG, hsScalar eB); + virtual hsBool SetGamma(const UInt16* const tabR, const UInt16* const tabG, const UInt16* const tabB); + + virtual hsBool CaptureScreen( plMipmap *dest, bool flipVertical = false, UInt16 desiredWidth = 0, UInt16 desiredHeight = 0 ); + virtual plMipmap* ExtractMipMap(plRenderTarget* targ); + + /// Error handling + virtual const char *GetErrorString(); + + hsBool ManagedAlloced() const { return fManagedAlloced; } + + virtual void GetSupportedColorDepths(hsTArray &ColorDepths); + virtual void GetSupportedDisplayModes(std::vector *res, int ColorDepth = 32 ); + virtual int GetMaxAnisotropicSamples(); + virtual int GetMaxAntiAlias(int Width, int Height, int ColorDepth); + +}; + + +//// Direct3D Inlines ////////////////////////////////////////////////////// +// ??.?? - Some mild optimizations PBG +// MMW - take advantage of the 32 bit float representation on a PC + +#define CONVERT_FLOAT_TO_BYTE_COLOR( f, dest ) \ + { \ + LONG const floatBitsOne = 0x3f800000; \ + LONG const floatBits = *( (LONG const *)( &f ) ); \ + if( floatBits <= 0 ) dest = 0; \ + else if( floatBits >= floatBitsOne ) dest = 255; \ + else \ + { \ + LONG const times256 = floatBits + ( 8 << 23 ); \ + dest = (DWORD)( *( (float const *)( ×256 ) ) ); \ + } \ + } + +inline DWORD plDXPipeline::inlGetD3DColor( const hsColorRGBA &col ) const +{ + DWORD dr, dg, db, da; + + CONVERT_FLOAT_TO_BYTE_COLOR( col.r, dr ); + CONVERT_FLOAT_TO_BYTE_COLOR( col.g, dg ); + CONVERT_FLOAT_TO_BYTE_COLOR( col.b, db ); + CONVERT_FLOAT_TO_BYTE_COLOR( col.a, da ); + + return( ( da << 24 ) | ( dr << 16 ) | ( dg << 8 ) | db ); +} + + +#endif // _plDX9Pipeline_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPixelShader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPixelShader.cpp new file mode 100644 index 00000000..60c9583d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPixelShader.cpp @@ -0,0 +1,149 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" +#include "hsWindows.h" + +#include +#include + +#include "hsTypes.h" + +#include "plDXPixelShader.h" + +#include "../plSurface/plShader.h" + +#include "plDXPipeline.h" + + +plDXPixelShader::plDXPixelShader(plShader* owner) +: plDXShader(owner), fHandle(nil) +{ +} + +plDXPixelShader::~plDXPixelShader() +{ + Release(); +} + +void plDXPixelShader::Release() +{ + ReleaseObject(fHandle); + fHandle = nil; + fPipe = nil; + + ISetError(nil); +} + +hsBool plDXPixelShader::VerifyFormat(UInt8 format) const +{ + return (fOwner->GetInputFormat() & format) == fOwner->GetInputFormat(); +} + +IDirect3DPixelShader9 *plDXPixelShader::GetShader(plDXPipeline* pipe) +{ + HRESULT hr = S_OK; + if ( !fHandle ) + { + if( FAILED(hr = ICreate(pipe)) ) + return nil; + } + + if( FAILED(hr = ISetConstants(pipe)) ) + return nil; + + return fHandle; +} + +HRESULT plDXPixelShader::ICreate(plDXPipeline* pipe) +{ + fHandle = nil; // in case something goes wrong. + fPipe = nil; + ISetError(nil); + +#ifdef HS_DEBUGGING + DWORD flags = D3DXSHADER_DEBUG | D3DXSHADER_SKIPOPTIMIZATION; +#else // HS_DEBUGGING + DWORD flags = 0; +#endif // HS_DEBUGGING + + DWORD* shaderCodes = nil; + + HRESULT hr = S_OK; + if( plShaderTable::LoadFromFile() || !fOwner->GetDecl()->GetCodes() ) + { + if( fOwner->GetDecl()->GetFileName() ) + { + LPD3DXBUFFER compiledShader = nil; + LPD3DXBUFFER compilationErrors = nil; + + hr = D3DXAssembleShaderFromFile( + fOwner->GetDecl()->GetFileName(), + NULL, NULL, flags, + &compiledShader, + &compilationErrors); + + if( FAILED(hr) ) + { + return IOnError(hr, compilationErrors ? (char*)compilationErrors->GetBufferPointer() : "File not found"); + } + + shaderCodes = (DWORD*)(compiledShader->GetBufferPointer()); + } + } + if( !shaderCodes ) + { + shaderCodes = (DWORD*)(fOwner->GetDecl()->GetCodes()); + } + if( !shaderCodes ) + return IOnError(-1, "No file and no compiled codes"); + + hr = pipe->GetD3DDevice()->CreatePixelShader(shaderCodes, &fHandle); + if( FAILED(hr) ) + { + return IOnError(hr, "Error on CreatePixelShader"); + } + + hsAssert(fHandle, "No error, but no pixel shader handle. Grrrr."); + + fPipe = pipe; + + return S_OK; +} + +HRESULT plDXPixelShader::ISetConstants(plDXPipeline* pipe) +{ + hsAssert(fHandle, "Pixel shader called to set constants without initialization"); + if( fOwner->GetNumConsts() ) + { + HRESULT hr = pipe->GetD3DDevice()->SetPixelShaderConstantF(0, + (float*)fOwner->GetConstBasePtr(), + fOwner->GetNumConsts()); + if( FAILED(hr) ) + return IOnError(hr, "Error setting constants"); + } + + return S_OK; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPixelShader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPixelShader.h new file mode 100644 index 00000000..7837f25a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXPixelShader.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDXPixelShader_inc +#define plDXPixelShader_inc + +#include "plDXShader.h" + +class plShader; +class plDXPipeline; + +struct IDirect3DPixelShader9; + +class plDXPixelShader : public plDXShader +{ + virtual HRESULT ICreate(plDXPipeline* pipe); // On error, sets error string. + virtual HRESULT ISetConstants(plDXPipeline* pipe); + + IDirect3DPixelShader9 *fHandle; + +public: + plDXPixelShader(plShader* owner); + virtual ~plDXPixelShader(); + + void Release(); + void Link(plDXPixelShader** back) { plDXDeviceRef::Link((plDXDeviceRef**)back); } + + hsBool VerifyFormat(UInt8 format) const; + IDirect3DPixelShader9 *GetShader(plDXPipeline* pipe); +}; + + +#endif // plDXPixelShader_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXRenderTargetRef.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXRenderTargetRef.h new file mode 100644 index 00000000..152a8580 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXRenderTargetRef.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDXRenderTargetRef.h - RenderTarget DeviceRef Definitions // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 7.19.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDXRenderTargetRef_h +#define _plDXRenderTargetRef_h + +#include "hsMatrix44.h" +#include "hsTemplates.h" +#include "plDXTextureRef.h" + + +//// Definition /////////////////////////////////////////////////////////////// + +class plRenderTarget; +class plDXRenderTargetRef: public plDXTextureRef +{ + public: + + IDirect3DSurface9 *fD3DColorSurface; + IDirect3DSurface9 *fD3DDepthSurface; + + hsBool fReleaseDepth; + + void Link( plDXRenderTargetRef **back ) { plDXDeviceRef::Link( (plDXDeviceRef **)back ); } + plDXRenderTargetRef *GetNext( void ) { return (plDXRenderTargetRef *)fNext; } + + + plDXRenderTargetRef( D3DFORMAT tp, UInt32 ml, plRenderTarget *owner, hsBool releaseDepthOnDelete = true ); + plDXRenderTargetRef& Set( D3DFORMAT tp, UInt32 ml, plRenderTarget *owner ); + + virtual void SetOwner( plRenderTarget *targ ) { fOwner = (plBitmap *)targ; } + + void SetTexture( IDirect3DSurface9 *surface, IDirect3DSurface9 *depth ); + void SetTexture( IDirect3DTexture9 *surface, IDirect3DSurface9 *depth ); + void SetTexture( IDirect3DCubeTexture9 *surface, IDirect3DSurface9 *depth ); + + IDirect3DSurface9 *GetColorSurface( void ) const + { + if( fD3DTexture != nil ) + { + IDirect3DSurface9* psurf; + ((IDirect3DTexture9*)fD3DTexture)->GetSurfaceLevel(0, &psurf); + if( psurf ) + psurf->Release(); + return psurf; + } + + return fD3DColorSurface; + } + + virtual ~plDXRenderTargetRef(); + void Release( void ); +}; + + +#endif // _plDXRenderTargetRef_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXSettings.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXSettings.h new file mode 100644 index 00000000..8270fad4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXSettings.h @@ -0,0 +1,285 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDXSettings - Header for the various settings groups for plDXPipeline // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 4.25.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDXSettings_h +#define _plDXSettings_h + +#include "hsMatrix44.h" +#include "plFogEnvironment.h" +#include "hsGeometry3.h" +#include "hsTemplates.h" +#include "hsColorRGBA.h" +#include "hsBitVector.h" +#include "plStencil.h" +#include "hsPoint2.h" +#include "plCullTree.h" +#include "hsWinRef.h" +#include "plViewTransform.h" + +//// General Settings ///////////////////////////////////////////////////////// + +class plRenderRequest; +class plRenderTarget; +struct IDirect3DSurface9; +class plDXDeviceRef; +class plDXVertexBufferRef; +class plDXIndexBufferRef; + +class plDXViewSettings +{ +public: + UInt32 fRenderState; + + plRenderRequest* fRenderRequest; + + UInt32 fDrawableTypeMask; + UInt32 fSubDrawableTypeMask; + + DWORD fClearColor; + float fClearDepth; + + plFogEnvironment fDefaultFog; + + plCullTree fCullTree; + hsBool fCullTreeDirty; + UInt16 fCullMaxNodes; + + enum XformResets + { + kResetProjection = 0x01, + kResetCamera = 0x02, + kResetL2W = 0x04, + + kResetAll = 0x07 + }; + + UInt8 fXformResetFlags; + hsBool fLocalToWorldLeftHanded; + hsBool fWorldToCamLeftHanded; + + mutable hsVector3 fDirection; + mutable hsVector3 fUp; + mutable hsVector3 fAcross; + hsPoint3 fWorldPos; + + mutable hsBool fViewVectorsDirty; + + hsMatrix44 fLocalToWorld; + hsMatrix44 fWorldToLocal; + + const hsMatrix44& GetLocalToWorld() const { return fLocalToWorld; } + const hsMatrix44& GetWorldToLocal() const { return fWorldToLocal; } + + plViewTransform fTransform; + + const hsMatrix44& GetWorldToCamera() const { return fTransform.GetWorldToCamera(); } + const hsMatrix44& GetCameraToWorld() const { return fTransform.GetCameraToWorld(); } + hsBool IsPerspective() const { return fTransform.GetPerspective(); } + + void Reset(); +}; + +class plDXGeneralSettings +{ + public: + + hsBool fFullscreen; + hsWinRef fHWnd; + UInt32 fColorDepth; + UInt8 fNumAASamples; + UInt32 fD3DCaps, fBoardKluge, fStageEnd; + UInt32 fMaxNumLights; + UInt32 fMaxNumProjectors; + UInt32 fMaxLayersAtOnce; + UInt32 fMaxPiggyBacks; + Int32 fBoundsDrawLevel; + UInt32 fProperties; + DWORD fClearColor; + UInt8 fMaxAnisotropicSamples; + D3DPRESENT_PARAMETERS fPresentParams; + hsBool fVeryAnnoyingTextureInvalidFlag; + hsBool fNoGammaCorrect; + int fMaxUVWSrc; + hsBool fCantProj; + hsBool fLimitedProj; + hsBool fBadManaged; + hsBool fShareDepth; + hsBool fCurrAnisotropy; + hsBool fIsIntel; + + IDirect3DSurface9 *fCurrD3DMainSurface; + IDirect3DSurface9 *fCurrD3DDepthSurface; + + hsTArray fViewStack; // One for the main view, then one for each rendertarget + hsTArray fRenderTargets; + plRenderTarget *fCurrRenderTarget; + plRenderTarget *fCurrBaseRenderTarget; + plDXDeviceRef *fCurrRenderTargetRef; + plDXVertexBufferRef *fCurrVertexBuffRef; + plDXIndexBufferRef *fCurrIndexBuffRef; + UInt32 fOrigWidth, fOrigHeight; + + IDirect3DVertexShader9 *fCurrVertexShader; + IDirect3DPixelShader9 *fCurrPixelShader; + DWORD fCurrFVFFormat; + + HRESULT fDXError; + char fErrorStr[ 256 ]; + + void Reset( void ); +}; + +//// Tweak Settings /////////////////////////////////////////////////////////// + +class plDXTweakSettings +{ + public: + float fDefaultPerspLayerScale; + float fPerspLayerScale; + float fPerspLayerTrans; + float fDefaultLODBias; + float fFogExpApproxStart; + float fFogExp2ApproxStart; + float fFogEndBias; + + float fExp2FogKnee; + float fExp2FogKneeVal; + float fExpFogKnee; + float fExpFogKneeVal; + + void Reset( void ) + { + fDefaultPerspLayerScale = 0.00001f; + fPerspLayerScale = 0.00001f; + fPerspLayerTrans = 0.00002f; + fDefaultLODBias = -0.25f; + fFogExpApproxStart = 0.0f; + fFogExp2ApproxStart = 0.0f; + fFogEndBias = 0.0f; + + fExpFogKnee = fExp2FogKnee = 0.5f; + fExpFogKneeVal = fExp2FogKneeVal = 0.15f; + } +}; + +//// Fog Settings ///////////////////////////////////////////////////////////// + +class plDXFogSettings +{ + public: + plFogEnvironment* fEnvPtr; // nil means no fog + D3DFOGMODE fMode; + UInt8 fIsVertex; + UInt8 fIsShader; + UInt32 fHexColor; + float fStart; + float fEnd; + float fDensity; + hsColorRGBA fColor; + + void Reset( void ) + { + fEnvPtr = nil; + fMode = D3DFOG_NONE; + fIsVertex = 0; + fIsShader = 0; + fHexColor = 0; + fStart = fEnd = fDensity = 0.0f; + fColor.Set( 0, 0, 0, 0 ); + } +}; + +//// Light Settings /////////////////////////////////////////////////////////// + +class plDXLightRef; +class plDXPipeline; +class plDXLightSettings +{ + public: + hsBitVector fUsedFlags; + hsBitVector fEnabledFlags; + hsBitVector fHoldFlags; + UInt32 fNextIndex, fLastIndex; + UInt16 fTime; + plLightInfo* fActiveList; + plDXLightRef* fRefList; + plDXPipeline* fPipeline; + hsTArray fProjEach; + hsTArray fProjAll; + + hsTArray fCharLights; + hsTArray fVisLights; + + UInt32 fNextShadowLight; + hsTArray fShadowLights; + + plDXLightSettings(); + + // Sets member variables to initial states. Does NOT release anything. + void Reset( plDXPipeline *pipe ); + // Releases/deletes anything associated with these settings + void Release( void ); + // Reserve a D3D light index + UInt32 ReserveD3DIndex( void ); + // Release a reserved D3D light index + void ReleaseD3DIndex( UInt32 idx ); +}; + +//// Stencil Settings ///////////////////////////////////////////////////////// + +class plDXStencilSettings +{ + public: + UInt8 fDepth; + hsBool fEnabled; + UInt8 fCmpFunc; + UInt8 fFailOp, fPassOp, fPassButZFailOp; + UInt32 fRefValue; + UInt32 fMask; + UInt32 fWriteMask; + + void Reset( void ) + { + fEnabled = false; + fCmpFunc = 0; + fFailOp = fPassOp = fPassButZFailOp = 0; + fRefValue = 0; + fMask = 0xffffffff; + fWriteMask = 0xffffffff; + } +}; + +#endif // _plDXSettings_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXShader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXShader.cpp new file mode 100644 index 00000000..d2807272 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXShader.cpp @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" +#include "hsWindows.h" + +#include +#include + +#include "hsTypes.h" + +#include "plDXShader.h" + +#include "../plSurface/plShader.h" + +#include "plDXPipeline.h" + +plDXShader::plDXShader(plShader* owner) +: fOwner(owner), + fErrorString(nil), + fPipe(nil) +{ + owner->SetDeviceRef(this); +} + +plDXShader::~plDXShader() +{ + fPipe = nil; + + ISetError(nil); +} + +void plDXShader::SetOwner(plShader* owner) +{ + if( owner != fOwner ) + { + Release(); + fOwner = owner; + owner->SetDeviceRef(this); + } +} + +const char* plDXShader::ISetError(const char* errStr) +{ + delete [] fErrorString; + if( errStr ) + fErrorString = hsStrcpy(errStr); + else + fErrorString = nil; + + return fErrorString; +} + +HRESULT plDXShader::IOnError(HRESULT hr, const char* errStr) +{ + ISetError(errStr); + + fOwner->Invalidate(); + + hsStatusMessage(errStr); + + return hr; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXShader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXShader.h new file mode 100644 index 00000000..67cd475f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXShader.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDXShader_inc +#define plDXShader_inc + +#include "plDXDeviceRef.h" + +class plShader; +class plDXPipeline; + +class plDXShader : public plDXDeviceRef +{ +protected: + plShader* fOwner; + char* fErrorString; + plDXPipeline* fPipe; + + HRESULT IOnError(HRESULT hr, const char* errStr); + const char* ISetError(const char* errStr); + + virtual HRESULT ICreate(plDXPipeline* pipe) = 0; + virtual HRESULT ISetConstants(plDXPipeline* pipe) = 0; // On error, sets error string. + +public: + plDXShader(plShader* owner); + virtual ~plDXShader(); + + const char* GetErrorString() const { return fErrorString; } + void SetOwner(plShader* owner); +}; + +#endif // plDXShader_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextFont.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextFont.cpp new file mode 100644 index 00000000..5c112aa4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextFont.cpp @@ -0,0 +1,308 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDXTextFont Class Functions // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 2.19.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsConfig.h" +#include "hsWindows.h" + +#include +#include +#include + +#if HS_BUILD_FOR_MAC +#include +#endif + +#include "hsWinRef.h" +#include "hsTypes.h" +#include "plDXTextFont.h" +#include "plDXPipeline.h" + + +//// Local Stuff ////////////////////////////////////////////////////////////// + +static const long PLD3D_FONTFVF = D3DFVF_XYZ | D3DFVF_DIFFUSE + | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0); + +static D3DXMATRIX d3dIdentityMatrix( 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f ); + +// Following number needs to be at least: 64 chars max in plTextFont drawn at any one time +// * 4 primitives per char max (for bold text) +// * 3 verts per primitive + +//const UInt32 kNumVertsInBuffer(32768); +const UInt32 kNumVertsInBuffer(4608); + +// See the declaration for plFontVertex in plTextFont.h for info +const DWORD plDXTextFont::kFVF = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0); + +IDirect3DVertexBuffer9* plDXTextFont::fBuffer = nil; +UInt32 plDXTextFont::fBufferCursor = 0; + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plDXTextFont::plDXTextFont( plPipeline *pipe, IDirect3DDevice9 *device ) : plTextFont( pipe ) +{ + fDevice = device; + fD3DTexture = nil; + + fOldStateBlock = fTextStateBlock = 0; +} + +plDXTextFont::~plDXTextFont() +{ + DestroyObjects(); +} + +//// ICreateTexture /////////////////////////////////////////////////////////// + +void plDXTextFont::ICreateTexture( UInt16 *data ) +{ + HRESULT hr; + D3DLOCKED_RECT lockInfo; + D3DCAPS9 d3dCaps; + + + // Check to make sure we can support it + fDevice->GetDeviceCaps( &d3dCaps ); + hsAssert( fTextureWidth <= d3dCaps.MaxTextureWidth, "Cannot initialize DX font--texture size too big" ); + + // Create our texture object + hr = fDevice->CreateTexture( fTextureWidth, fTextureHeight, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &fD3DTexture, NULL ); + hsAssert( !FAILED( hr ), "Cannot create D3D texture" ); + + // Lock the texture and write our values out + fD3DTexture->LockRect( 0, &lockInfo, 0, 0 ); + memcpy( lockInfo.pBits, data, fTextureWidth * fTextureHeight * sizeof( UInt16 ) ); + fD3DTexture->UnlockRect( nil ); +} + +void plDXTextFont::CreateShared(IDirect3DDevice9* device) +{ + if( FAILED( device->CreateVertexBuffer( sizeof( plFontVertex ) * kNumVertsInBuffer, + D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &fBuffer, NULL ) ) ) + { + hsAssert( false, "CreateVertexBuffer() call failed!" ); + } +} + +void plDXTextFont::ReleaseShared(IDirect3DDevice9* device) +{ + ReleaseObject( fBuffer ); +} + +//// IInitStateBlocks ///////////////////////////////////////////////////////// + +void plDXTextFont::IInitStateBlocks( void ) +{ + + for( int i = 0; i < 2; i++ ) + { + fDevice->BeginStateBlock(); + fDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + fDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); + fDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); + fDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE ); + fDevice->SetRenderState( D3DRS_ALPHAREF, 0x08 ); + fDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL ); + fDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); + fDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW ); + + fDevice->SetRenderState( D3DRS_ZENABLE, TRUE ); + fDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_ALWAYS ); + fDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE ); + fDevice->SetRenderState( D3DRS_DEPTHBIAS, 0 ); + + fDevice->SetRenderState( D3DRS_STENCILENABLE, FALSE ); + fDevice->SetRenderState( D3DRS_CLIPPING, TRUE ); + fDevice->SetRenderState( D3DRS_ANTIALIASEDLINEENABLE, FALSE ); + fDevice->SetRenderState( D3DRS_VERTEXBLEND, FALSE ); + fDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE ); + fDevice->SetRenderState( D3DRS_FOGENABLE, FALSE ); + fDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); + fDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); + fDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); + fDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); + fDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); + fDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); + fDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT ); + fDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT ); + fDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE ); + fDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 ); + fDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ); + fDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE ); + fDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); + fDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); + + if( i == 0 ) + fDevice->EndStateBlock( &fOldStateBlock ); + else + fDevice->EndStateBlock( &fTextStateBlock ); + } +} + +//// DestroyObjects /////////////////////////////////////////////////////////// + +void plDXTextFont::DestroyObjects() +{ + ReleaseObject(fOldStateBlock); + ReleaseObject(fTextStateBlock); + ReleaseObject(fD3DTexture); + + fOldStateBlock = fTextStateBlock = 0; + fD3DTexture = nil; + fInitialized = false; +} + +//// IDrawPrimitive /////////////////////////////////////////////////////////// + +void plDXTextFont::IDrawPrimitive( UInt32 count, plFontVertex *array ) +{ + plFontVertex *v; + + if( !fBuffer ) + return; + + /// Lock the buffer and write to it + if( fBufferCursor && (fBufferCursor + count * 3 < kNumVertsInBuffer) ) + { + // We can lock part of it + if( FAILED( fBuffer->Lock( fBufferCursor * sizeof( plFontVertex ), + count * 3 * sizeof( plFontVertex ), + (void **)&v, D3DLOCK_NOOVERWRITE ) ) ) + { + hsAssert( false, "Failed to lock vertex buffer for writing" ); + return; + } + + fBufferCursor += count * 3; + } + else + { + // Gotta start over + FlushDraws(); + fBufferCursor = count * 3; + + if( FAILED( fBuffer->Lock( 0, count * 3 * sizeof( plFontVertex ), + (void **)&v, D3DLOCK_DISCARD ) ) ) + { + hsAssert( false, "Failed to lock vertex buffer for writing" ); + return; + } + } + + if( v != nil && array != nil ) + { + memcpy( v, array, count * sizeof( plFontVertex ) * 3 ); + } + + fBuffer->Unlock(); +} + +//// IDrawLines /////////////////////////////////////////////////////////////// + +void plDXTextFont::IDrawLines( UInt32 count, plFontVertex *array ) +{ + if( !fBuffer ) + return; + + if( count == 0 || array == nil ) + return; + + fDevice->SetVertexShader(NULL); + fDevice->SetFVF(kFVF); + fDevice->SetStreamSource(0, fBuffer, 0, sizeof(plFontVertex)); + fDevice->DrawPrimitiveUP( D3DPT_LINELIST, count, (const void *)array, sizeof( plFontVertex ) ); +} + +//// FlushDraws /////////////////////////////////////////////////////////////// +// Flushes out and finishes any drawing left to be done. + +void plDXTextFont::FlushDraws( void ) +{ + if( !fBuffer ) + return; + + if( fBufferCursor > 0 ) + { + fDevice->SetVertexShader( NULL ); + fDevice->SetFVF(kFVF); + fDevice->SetStreamSource( 0, fBuffer, 0, sizeof( plFontVertex ) ); + fDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, fBufferCursor / 3 ); + fBufferCursor = 0; + } +} + +//// SaveStates /////////////////////////////////////////////////////////////// + +void plDXTextFont::SaveStates( void ) +{ + if( !fInitialized ) + IInitObjects(); + + if (fOldStateBlock) + fOldStateBlock->Capture(); + if (fTextStateBlock) + fTextStateBlock->Apply(); + + fDevice->SetTexture( 0, fD3DTexture ); + fDevice->SetTransform( D3DTS_TEXTURE0, &d3dIdentityMatrix ); + + /// Set up the transform matrices so that the vertices can range (0-screenWidth,0-screenHeight) + fDevice->SetTransform( D3DTS_WORLD, &d3dIdentityMatrix ); + fDevice->SetTransform( D3DTS_VIEW, &d3dIdentityMatrix ); + D3DXMATRIX mat; + mat = d3dIdentityMatrix; + mat(0,0) = 2.0f / (float)fPipe->Width(); + mat(1,1) = -2.0f / (float)fPipe->Height(); + mat(3,0) = -1.0; + mat(3,1) = 1.0; + fDevice->SetTransform( D3DTS_PROJECTION, &mat ); +} + +//// RestoreStates //////////////////////////////////////////////////////////// + +void plDXTextFont::RestoreStates( void ) +{ + if (fOldStateBlock) + fOldStateBlock->Apply(); + + fDevice->SetTexture( 0, nil ); + fDevice->SetTransform( D3DTS_TEXTURE0, &d3dIdentityMatrix ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextFont.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextFont.h new file mode 100644 index 00000000..1a1ab98b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextFont.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plDXTextFont_h +#define _plDXTextFont_h + +#include "plTextFont.h" +//#include "hsGDirect3DTnLDevice.h" + + +//// plDXTextFont Class Definition /////////////////////////////////////////// + +struct IDirect3DTexture9; +struct IDirect3DDevice9; +struct IDirect3DStateBlock9; +class plPipeline; + +class plDXTextFont : public plTextFont +{ +protected: + IDirect3DTexture9 *fD3DTexture; + IDirect3DDevice9 *fDevice; + + static IDirect3DVertexBuffer9 *fBuffer; + static UInt32 fBufferCursor; + + IDirect3DStateBlock9 *fOldStateBlock; + IDirect3DStateBlock9 *fTextStateBlock; + + virtual void ICreateTexture( UInt16 *data ); + virtual void IInitStateBlocks( void ); + virtual void IDrawPrimitive( UInt32 count, plFontVertex *array ); + virtual void IDrawLines( UInt32 count, plFontVertex *array ); + +public: + plDXTextFont( plPipeline *pipe, IDirect3DDevice9 *device ); + ~plDXTextFont(); + + static void CreateShared(IDirect3DDevice9* device); + static void ReleaseShared(IDirect3DDevice9* device); + + virtual void FlushDraws( void ); + virtual void SaveStates( void ); + virtual void RestoreStates( void ); + virtual void DestroyObjects( void ); + + static const DWORD kFVF; +}; + + +#endif // _plDXTextFont_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextureRef.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextureRef.h new file mode 100644 index 00000000..ee07954b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXTextureRef.h @@ -0,0 +1,107 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDXTextureRef.h - Hardware Texture DeviceRef Definition // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 4.25.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDXTextureRef_h +#define _plDXTextureRef_h + +#include "hsMatrix44.h" +#include "hsGeometry3.h" +#include "hsTemplates.h" +#include "plDXDeviceRef.h" + + +//// Definition /////////////////////////////////////////////////////////////// + +class plBitmap; + +class plDXTextureRef : public plDXDeviceRef +{ + public: + enum Flags + { + kExternData = 0x00000002, // fData points to user data, don't delete + kRenderTarget = 0x00000004, // Created via a render target + kCubicMap = 0x00000008, // Texture is really a cubic map texture + kPerspProjection = 0x00000010, // Perspective projection + kOrthoProjection = 0x00000020, // Orthogonal projection + kProjection = kPerspProjection | kOrthoProjection, + kOffscreenRT = 0x00000040, // Offscreen renderTarget. Never used as an actual texture, + // but handy to still have it as a textureRef + kUVWNormal = 0x00000080 // Use the normal as the UVW src + }; + + IDirect3DBaseTexture9 *fD3DTexture; + D3DFORMAT fFormatType; // Format of the D3D texture object + + UInt32 fMMLvs; // Number of mipmap levels + UInt32 fMaxWidth; // Width of the highest mipmap level + UInt32 fMaxHeight; // Height of the highest mipmap level (no pun intended) + UInt32 fNumPix; // total num texels in all mip levels + UInt32 fDataSize; // size of fData[0..n] in bytes + UInt32* fLevelSizes; // fLevelSize[i] == size in bytes of level i + //UInt32 fCurrLOD; // Current LOD setting for this texture + + plBitmap *fOwner; + + void* fData; // for reloading + + UInt32 GetFlags( void ) { return fFlags; } + void SetFlags( UInt32 flag ) { fFlags = flag; } + + plDXTextureRef& Set( D3DFORMAT tp, UInt32 ml, UInt32 mw, UInt32 mh, UInt32 np, UInt32 sz, UInt32 manSize, UInt32* lSz, void* pd, hsBool ed=false, hsBool renderTarget = false ); + + plDXTextureRef( D3DFORMAT tp, UInt32 ml, UInt32 mw, UInt32 mh, UInt32 np, UInt32 sz, UInt32 manSize, UInt32* lSz, void* pd, hsBool ed=false, hsBool renderTarget = false ); + virtual ~plDXTextureRef(); + + void Link( plDXTextureRef **back ) { plDXDeviceRef::Link( (plDXDeviceRef **)back ); } + plDXTextureRef *GetNext( void ) { return (plDXTextureRef *)fNext; } + + void Release( void ); +}; + +class plDXCubeTextureRef : public plDXTextureRef +{ + public: + void *fFaceData[ 5 ]; // First face is in the inherited fData + + plDXCubeTextureRef( D3DFORMAT tp, UInt32 ml, UInt32 mw, UInt32 mh, UInt32 np, UInt32 sz, UInt32 manSize, UInt32* lSz, void* pd, hsBool ed=false, hsBool renderTarget = false ) : + plDXTextureRef( tp, ml, mw, mh, np, sz, manSize, lSz, pd, ed, renderTarget ) + { + + } +}; + +#endif // _plDXTextureRef_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXVertexShader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXVertexShader.cpp new file mode 100644 index 00000000..a993c792 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXVertexShader.cpp @@ -0,0 +1,152 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" +#include "hsWindows.h" + +#include +#include + +#include "hsTypes.h" + +#include "plDXVertexShader.h" + +#include "../plSurface/plShader.h" + +#include "plGBufferGroup.h" +#include "plDXPipeline.h" + +plDXVertexShader::plDXVertexShader(plShader* owner) +: plDXShader(owner), fHandle(nil) +{ +} + +plDXVertexShader::~plDXVertexShader() +{ + Release(); +} + +void plDXVertexShader::Release() +{ + ReleaseObject(fHandle); + fHandle = nil; + fPipe = nil; + + ISetError(nil); +} + +hsBool plDXVertexShader::VerifyFormat(UInt8 format) const +{ + return (fOwner->GetInputFormat() & format) == fOwner->GetInputFormat(); +} + +IDirect3DVertexShader9 *plDXVertexShader::GetShader(plDXPipeline* pipe) +{ + HRESULT hr = S_OK; + if ( !fHandle ) + { + if( FAILED(hr = ICreate(pipe)) ) + return nil; + } + + if( FAILED(hr = ISetConstants(pipe)) ) + return nil; + + return fHandle; +} + +HRESULT plDXVertexShader::ICreate(plDXPipeline* pipe) +{ + fHandle = nil; // in case something goes wrong. + fPipe = nil; + ISetError(nil); + +#ifdef HS_DEBUGGING + DWORD flags = D3DXSHADER_DEBUG | D3DXSHADER_SKIPOPTIMIZATION; +#else // HS_DEBUGGING + DWORD flags = 0; +#endif // HS_DEBUGGING + + // We could store the compiled buffer and skip the assembly step + // if we need to recreate the shader (e.g. on device lost). + // But whatever. + DWORD* shaderCodes = nil; + + HRESULT hr = S_OK; + if( plShaderTable::LoadFromFile() || !fOwner->GetDecl()->GetCodes() ) + { + if( fOwner->GetDecl()->GetFileName() ) + { + LPD3DXBUFFER compiledShader = nil; + LPD3DXBUFFER compilationErrors = nil; + + hr = D3DXAssembleShaderFromFile( + fOwner->GetDecl()->GetFileName(), + NULL, NULL, flags, + &compiledShader, + &compilationErrors); + + if( FAILED(hr) ) + { + return IOnError(hr, compilationErrors ? (char*)compilationErrors->GetBufferPointer() : "File not found"); + } + + shaderCodes = (DWORD*)(compiledShader->GetBufferPointer()); + } + } + if( !shaderCodes ) + { + shaderCodes = (DWORD*)(fOwner->GetDecl()->GetCodes()); + } + if( !shaderCodes ) + return IOnError(-1, "No file and no compiled codes"); + + hr = pipe->GetD3DDevice()->CreateVertexShader(shaderCodes, &fHandle); + + if( FAILED(hr) ) + return IOnError(hr, "Error on CreateVertexShader"); + + hsAssert(fHandle, "No error, but no vertex shader handle. Grrrr."); + + fPipe = pipe; + + return S_OK; +} + +HRESULT plDXVertexShader::ISetConstants(plDXPipeline* pipe) +{ + hsAssert(fHandle, "Vertex shader called to set constants without initialization"); + if( fOwner->GetNumConsts() ) + { + HRESULT hr = pipe->GetD3DDevice()->SetVertexShaderConstantF(0, + (float*)fOwner->GetConstBasePtr(), + fOwner->GetNumConsts()); + if( FAILED(hr) ) + return IOnError(hr, "Failure setting vertex shader constants"); + } + + return S_OK; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXVertexShader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXVertexShader.h new file mode 100644 index 00000000..5f52d8c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDXVertexShader.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDXVertexSahder_inc +#define plDXVertexSahder_inc + +#include "plDXShader.h" +#include "hsTemplates.h" + +struct IDirect3DDevice9; +class plShader; +class plDXPipeline; + +class plDXVertexShader : public plDXShader +{ +protected: + IDirect3DVertexShader9* fHandle; + hsTArray& IMakeDecl(hsTArray& decl) const; + virtual HRESULT ICreate(plDXPipeline* pipe); // On error, sets error string. + + virtual HRESULT ISetConstants(plDXPipeline* pipe); // On error, sets error string. + +public: + plDXVertexShader(plShader* owner); + virtual ~plDXVertexShader(); + + virtual void Release(); + void Link(plDXVertexShader** back) { plDXDeviceRef::Link((plDXDeviceRef**)back); } + + hsBool VerifyFormat(UInt8 format) const; + IDirect3DVertexShader9* GetShader(plDXPipeline* pipe); +}; + +#endif // plDXVertexSahder_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDebugText.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDebugText.cpp new file mode 100644 index 00000000..aae9029a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDebugText.cpp @@ -0,0 +1,288 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDebugText and plDebugTextManager Functions // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "plPipeline.h" +#include "plDebugText.h" +#include "plTextFont.h" + +////////////////////////////////////////////////////////////////////////////// +//// plDebugText Functions /////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +plDebugText plDebugText::fInstance; + +//// DrawString ////////////////////////////////////////////////////////////// + +void plDebugText::DrawString( UInt16 x, UInt16 y, const char *string, UInt32 hexColor, UInt8 style ) +{ + if( IsEnabled() && fManager && string != nil && string[ 0 ] != 0 ) + fManager->AddString( x, y, string, hexColor, style, fDrawOnTopMode ); +} + +//// CalcStringWidth ///////////////////////////////////////////////////////// + +UInt32 plDebugText::CalcStringWidth( const char *string ) +{ + if( IsEnabled() && fManager && string ) + return fManager->CalcStringWidth( string ); + + return 0; +} + +//// DrawRect //////////////////////////////////////////////////////////////// +// TEMPORARY function to draw a flat-shaded 2D rectangle to the screen. Used +// to create a background for our console; will be obliterated once we figure +// a better way to do so. + +void plDebugText::DrawRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 hexColor ) +{ + if( IsEnabled() && fManager ) + fManager->DrawRect( left, top, right, bottom, hexColor, fDrawOnTopMode ); +} + +//// Draw3DBorder //////////////////////////////////////////////////////////// + +void plDebugText::Draw3DBorder( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 hexColor1, UInt32 hexColor2 ) +{ + if( IsEnabled() && fManager ) + fManager->Draw3DBorder( left, top, right, bottom, hexColor1, hexColor2, fDrawOnTopMode ); +} + +//// GetScreenSize /////////////////////////////////////////////////////////// + +void plDebugText::GetScreenSize( UInt32 *width, UInt32 *height ) +{ + if( fManager ) + fManager->GetScreenSize( width, height ); +} + +UInt16 plDebugText::GetFontHeight() +{ + if (fManager) + return fManager->GetFontHeight(); + return 0; +} + +////////////////////////////////////////////////////////////////////////////// +//// plDebugTextManager Functions //////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// plDebugTextNode Constructor ///////////////////////////////////////////// + +plDebugTextManager::plDebugTextNode::plDebugTextNode( const char *s, UInt32 c, UInt16 x, UInt16 y, UInt8 style ) +{ + HSMemory::Clear( fText, sizeof( fText ) ); + strncpy( fText, s, sizeof( fText ) - 1 ); + fColor = c; + fX = x; + fY = y; + fStyle = style; +} + +plDebugTextManager::plDebugTextNode::plDebugTextNode( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 c ) +{ + memset( fText, 0, sizeof( fText ) ); + fColor = c; + fX = left; + fY = top; + fRight = right; + fBottom = bottom; + fStyle = 0xff; +} + +plDebugTextManager::plDebugTextNode::plDebugTextNode( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 c1, UInt32 c2 ) +{ + memset( fText, 0, sizeof( fText ) ); + fColor = c1; + fDarkColor = c2; + fX = left; + fY = top; + fRight = right; + fBottom = bottom; + fStyle = 0xfe; +} + +//// plDebugTextManager destructor /////////////////////////////////////////// + +plDebugTextManager::~plDebugTextManager() +{ + if( fFont != nil ) + delete fFont; +} + +//// AddString /////////////////////////////////////////////////////////////// + +void plDebugTextManager::AddString( UInt16 x, UInt16 y, const char *s, UInt32 hexColor, UInt8 style, hsBool drawOnTop ) +{ + if( drawOnTop ) + fDrawOnTopList.Append( plDebugTextNode( s, hexColor, x, y, style ) ); + else + fList.Append( plDebugTextNode( s, hexColor, x, y, style ) ); +} + +//// DrawRect //////////////////////////////////////////////////////////////// +// TEMPORARY function to draw a flat-shaded 2D rectangle to the screen. Used +// to create a background for our console; will be obliterated once we figure +// a better way to do so. + +void plDebugTextManager::DrawRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 hexColor, hsBool drawOnTop ) +{ + if( drawOnTop ) + fDrawOnTopList.Append( plDebugTextNode( left, top, right, bottom, hexColor ) ); + else + fList.Append( plDebugTextNode( left, top, right, bottom, hexColor ) ); +} + +//// Draw3DBorder //////////////////////////////////////////////////////////// + +void plDebugTextManager::Draw3DBorder( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 hexColor1, UInt32 hexColor2, hsBool drawOnTop ) +{ + if( drawOnTop ) + fDrawOnTopList.Append( plDebugTextNode( left, top, right, bottom, hexColor1, hexColor2 ) ); + else + fList.Append( plDebugTextNode( left, top, right, bottom, hexColor1, hexColor2 ) ); +} + +//// DrawToDevice //////////////////////////////////////////////////////////// + +void plDebugTextManager::DrawToDevice( plPipeline *pipe ) +{ + int i, j; + hsTArray *list; + + + if( fList.GetCount() == 0 && fDrawOnTopList.GetCount() == 0 ) + { + return; + } + + if( fFont == nil ) + { + // Create font first time around + fFont = pipe->MakeTextFont( (char *)plDebugText::Instance().GetFontFace(), + plDebugText::Instance().GetFontSize() ); + + if( fFont == nil ) + { + plDebugText::Instance().DisablePermanently(); + return; + } + } + + // Get stuff + fSWidth = pipe->Width(); + fSHeight = pipe->Height(); + + // Start other stuff + fFont->SaveStates(); + + for( j = 0; j < 2; j++ ) + { + if( j == 0 ) + list = &fList; + else + list = &fDrawOnTopList; + + for( i = 0; i < list->GetCount(); i++ ) + { + plDebugTextNode& node = (*list)[i]; + + if( node.fStyle == 0xff ) + { + fFont->DrawRect( node.fX, node.fY, + node.fRight, node.fBottom, node.fColor ); + } + else if( node.fStyle == 0xfe ) + { + fFont->Draw3DBorder( node.fX, node.fY, + node.fRight, node.fBottom, node.fColor, node.fDarkColor ); + } + else + { + + /// Draw string only if its in bounds (clip to right edge if necessary) + if( node.fX >= 0 && node.fY >= 0 ) + { + if( node.fY + fFont->GetFontHeight() < fSHeight ) + { + if( node.fX + CalcStringWidth( node.fText ) < fSWidth ) + { + fFont->DrawString( node.fText, node.fX, node.fY, + node.fColor, node.fStyle ); + } + else + { + fFont->DrawString( node.fText, node.fX, node.fY, + node.fColor, node.fStyle, fSWidth ); + } + } + } + } + } + } + + // Call this to ensure the font object finishes all its drawing + fFont->FlushDraws(); + fFont->RestoreStates(); + + fList.Reset(); + fDrawOnTopList.Reset(); +} + +//// CalcStringWidth ///////////////////////////////////////////////////////// + +UInt32 plDebugTextManager::CalcStringWidth( const char *string ) +{ + if( !plDebugText::Instance().IsEnabled() || fFont == nil ) + return 0; + + return fFont->CalcStringWidth( string ); +} + +//// GetScreenSize /////////////////////////////////////////////////////////// + +void plDebugTextManager::GetScreenSize( UInt32 *width, UInt32 *height ) +{ + if( width != nil ) + *width = fSWidth; + if( height != nil ) + *height = fSHeight; +} + +UInt16 plDebugTextManager::GetFontHeight() +{ + if (fFont) + return fFont->GetFontHeight(); + + // Just return a quick default height until we get a real font + return 10; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDebugText.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDebugText.h new file mode 100644 index 00000000..965ad784 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDebugText.h @@ -0,0 +1,187 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plDebugText and plDebugTextManager Headers // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDebugText_h +#define _plDebugText_h + +#include "hsTypes.h" +#include "hsColorRGBA.h" +#include "hsTemplates.h" +#include "hsUtils.h" + +//// plDebugText Class Definition //////////////////////////////////////////// + +class plPipeline; +class plDebugTextManager; + +class plDebugText +{ + private: + + static plDebugText fInstance; + + plDebugText() + { + fManager = nil; +#ifdef PLASMA_EXTERNAL_RELEASE + SetFont( "Trebuchet MS Bold", 8 ); +#else + SetFont( "Courier New", 8 ); +#endif + SetEnable( true ); + fLockEnable = false; + fDrawOnTopMode = false; + } + + protected: + + plDebugTextManager *fManager; + + char fFontFace[ 128 ]; + UInt16 fFontSize; + hsBool fEnabled, fLockEnable, fDrawOnTopMode; + + public: + + enum Styles + { + kStyleItalic = 0x01, + kStyleBold = 0x02 + }; + + ~plDebugText() { ; } + + static plDebugText &Instance( void ) { return fInstance; } + + UInt32 CalcStringWidth( const char *string ); + + void DrawString( UInt16 x, UInt16 y, const char *string, UInt32 hexColor, UInt8 style = 0 ); + + void DrawString( UInt16 x, UInt16 y, const char *string, hsColorRGBA &color, UInt8 style = 0 ) + { + UInt32 hex; + UInt8 r, g, b, a; + + + r = (UInt8)( color.r * 255.0 ); + g = (UInt8)( color.g * 255.0 ); + b = (UInt8)( color.b * 255.0 ); + a = (UInt8)( color.a * 255.0 ); + hex = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | ( b ); + + DrawString( x, y, string, hex, style ); + } + + void DrawString( UInt16 x, UInt16 y, const char *string, UInt8 r = 255, UInt8 g = 255, UInt8 b = 255, UInt8 a = 255, UInt8 style = 0 ) + { + DrawString( x, y, string, (UInt32)( ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | ( b ) ), style ); + } + + void SetDrawOnTopMode( hsBool enable ) { fDrawOnTopMode = enable; } + + /// TEMPORARY FUNCTION (until we can find a better way to do this, one way or the other) + void DrawRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 hexColor ); + + /// TEMPORARY FUNCTION (until we can find a better way to do this, one way or the other) + void DrawRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt8 r, UInt8 g, UInt8 b, UInt8 a = 255 ) + { + DrawRect( left, top, right, bottom, (UInt32)( ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | ( b ) ) ); + } + + /// EVEN MORE TEMPORARY FUNCTION (until we can find a better way to do this, one way or the other) + void Draw3DBorder( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 hexColor1, UInt32 hexColor2 ); + + void SetManager( plDebugTextManager *m ) { fManager = m; } + + void SetFont( char *face, UInt16 size ) { hsStrncpy( fFontFace, face, sizeof( fFontFace ) ); fFontSize = size; } + const char *GetFontFace( void ) { return fFontFace; } + const UInt16 GetFontSize( void ) { return fFontSize; } + UInt16 GetFontHeight(); + + void SetEnable( hsBool on ) { fEnabled = on; } + void DisablePermanently( void ) { fEnabled = false; fLockEnable = true; } + const hsBool IsEnabled( void ) { return fEnabled; } + + void GetScreenSize( UInt32 *width, UInt32 *height ); +}; + +//// plDebugTextManager Class Definition ///////////////////////////////////// + +class plTextFont; + +class plDebugTextManager +{ + protected: + + struct plDebugTextNode + { + char fText[ 256 ]; + UInt32 fColor, fDarkColor; + UInt16 fX, fY, fRight, fBottom; // Last 2 are for rects only + UInt8 fStyle; // 0xff means rectangle, 0xfe means 3d border + + plDebugTextNode() { fText[ 0 ] = 0; fColor = 0; fX = fY = 0; fStyle = 0; } + plDebugTextNode( const char *s, UInt32 c, UInt16 x, UInt16 y, UInt8 style ); + plDebugTextNode( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 c ); + plDebugTextNode( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 c1, UInt32 c2 ); + ~plDebugTextNode() {;} + }; + + hsTArray fList; + hsTArray fDrawOnTopList; + + plTextFont *fFont; + UInt32 fSWidth, fSHeight; + + public: + + plDebugTextManager() { plDebugText::Instance().SetManager( this ); fFont = nil; } + ~plDebugTextManager(); + + void AddString( UInt16 x, UInt16 y, const char *s, UInt32 hexColor, UInt8 style, hsBool drawOnTop = false ); + UInt32 CalcStringWidth( const char *string ); + + /// TEMPORARY FUNCTION (until we can find a better way to do this, one way or the other) + void DrawRect( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 hexColor, hsBool drawOnTop = false ); + + /// EVEN MORE TEMPORARY FUNCTION (until we can find a better way to do this, one way or the other) + void Draw3DBorder( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom, UInt32 hexColor1, UInt32 hexColor2, hsBool drawOnTop = false ); + + void DrawToDevice( plPipeline *pipe ); + + void GetScreenSize( UInt32 *width, UInt32 *height ); + + UInt16 GetFontHeight(); +}; + + +#endif //_plDebugText_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDrawPrim.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDrawPrim.h new file mode 100644 index 00000000..817ae1c7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDrawPrim.h @@ -0,0 +1,88 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#include "hsKeyedObject.h" + + +struct hsMatrix44; +class hsBounds3Ext; +class hsGMaterial; +class hsTriangle3; +struct hsGSplat3; + +class plDrawPrim : public hsRefCnt +{ +public: + enum plDPPrimType + { + kTypeNone = 0x0, + kTypeTriList = 0x1, + kTypeSplatList = 0x2 + }; +protected: + UInt32 fPrimType; + + UInt32 fDrawProps; + + hsGMaterial* fMaterial; + +public: + plDrawPrim() : fMaterial(nil), fDrawProps(0), fPrimType(kTypeNone) {} + virtual ~plDrawPrim(); + + virtual const hsBounds3Ext& GetLocalBounds() const = 0; + + hsGMaterial* GetMaterial() { return fMaterial; } + UInt32 GetPrimType() { return fPrimType; } + UInt32 GetDrawProps() { return fDrawProps; } +}; + +class plTriListPrim : public plDrawPrim +{ +public: + plTriListPrim() { fPrimType |= kTypeTriList; } + virtual ~plTriListPrim(); + + virtual const hsBounds3Ext& GetLocalBounds() const = 0; + + virtual hsTriangle3* GetTriList(int& num) = 0; + +}; + +class plSplatListPrim : public plDrawPrim +{ +public: + plSplatListPrim() { fPrimType |= kTypeSplatList; } + + virtual const hsBounds3Ext& GetLocalBounds() const = 0; + + virtual hsGSplat3* GetSplatList(int& num) = 0; +}; + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDynamicEnvMap.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDynamicEnvMap.cpp new file mode 100644 index 00000000..1c9939cd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDynamicEnvMap.cpp @@ -0,0 +1,952 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plDynamicEnvMap.h" + +#include "plPipeline.h" +#include "plPipeDebugFlags.h" +#include "plDrawable.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + +#include "hsTimer.h" +#include "hsStream.h" + +#include "../plMessage/plRenderRequestMsg.h" +#include "../plMessage/plDynamicEnvMapMsg.h" +#include "../pfCamera/plCameraModifier.h" +#include "../pfCamera/plVirtualCamNeu.h" +#include "../plMessage/plRenderMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../plMessage/plLayRefMsg.h" +#include "../pnMessage/plPipeResMakeMsg.h" +#include "../pnMessage/plRefMsg.h" + +#include "../plScene/plVisRegion.h" +#include "../plScene/plVisMgr.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plSurface/plLayer.h" + +plDynamicEnvMap::plDynamicEnvMap() +: fPos(0,0,0), + fHither(0.3f), + fYon(1000.f), + fFogStart(1000.f), + fRefreshRate(0.f), + fLastRefresh(0.0), + fLastRender(0), + fOutStanding(0), + fIncCharacters(false), + fRootNode(nil) +{ + fColor.Set(0,0,0,1.f); + int i; + for( i = 0; i < 6; i++ ) + fReqMsgs[i] = TRACKED_NEW plRenderRequestMsg(nil, &fReqs[i]);; + + SetPosition(fPos); +} + +plDynamicEnvMap::plDynamicEnvMap(UInt16 width, UInt16 height, UInt8 bitDepth, UInt8 zDepth, UInt8 sDepth) +: fPos(0,0,0), + fHither(0.3f), + fYon(0.f), // yon < hither means ignore and use current settings + fFogStart(-1.f), // - fog start means use current settings + fRefreshRate(0.f), + fLastRefresh(0.0), + fLastRender(0), + fOutStanding(0), + fIncCharacters(false), + fRootNode(nil), + plCubicRenderTarget(plRenderTarget::kIsTexture, width, height, bitDepth, zDepth, sDepth) +{ + fColor.Set(0,0,0,1.f); + int i; + for( i = 0; i < 6; i++ ) + fReqMsgs[i] = TRACKED_NEW plRenderRequestMsg(nil, &fReqs[i]);; + + SetPosition(fPos); +} + +plDynamicEnvMap::~plDynamicEnvMap() +{ + SetDeviceRef(nil); + + int i; + for( i = 0; i < 6; i++ ) + delete fReqMsgs[i]; +} + +void plDynamicEnvMap::Init() +{ + plgDispatch::Dispatch()->RegisterForExactType(plPipeRTMakeMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); + + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + + ISetupRenderRequests(); +} + +hsPoint3 plDynamicEnvMap::GetPosition() const +{ + if (fRootNode) + { + // This is to catch export issues where we've got a root node, but its iface + // hasn't fully been set up yet. + if (fRootNode->GetCoordinateInterface()) + return fRootNode->GetLocalToWorld().GetTranslate(); + } + + return fPos; +} + +void plDynamicEnvMap::SetPosition(const hsPoint3& pos) +{ + hsAssert(fRootNode == nil, "Trying to override a cube map's root node."); + fPos = pos; + SetCameraMatrix(fPos); +} + +void plDynamicEnvMap::IUpdatePosition() +{ + hsPoint3 pos = GetPosition(); + if (pos != fPos) + SetCameraMatrix(fPos); +} + +void plDynamicEnvMap::SetHither(hsScalar f) +{ + fHither = f; +} + +void plDynamicEnvMap::SetYon(hsScalar f) +{ + fYon = f; +} + +void plDynamicEnvMap::SetFogStart(hsScalar f) +{ + fFogStart = f; +} + +void plDynamicEnvMap::SetColor(const hsColorRGBA& col) +{ + fColor = col; +} + +void plDynamicEnvMap::SetRefreshRate(hsScalar secs) +{ + fRefreshRate = secs / 6.f; + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); +} + +void plDynamicEnvMap::ISetupRenderRequests() +{ + UInt32 renderState + = plPipeline::kRenderNormal + | plPipeline::kRenderClearColor + | plPipeline::kRenderClearDepth; + + int i; + for( i = 0; i < 6; i++ ) + { + fReqs[i].SetRenderState(renderState); + + fReqs[i].SetDrawableMask(plDrawable::kNormal); + fReqs[i].SetSubDrawableMask(plDrawable::kSubAllTypes); + + fReqs[i].SetHither(fHither); + fReqs[i].SetYon(fYon); + + fReqs[i].SetFogStart(fFogStart); + + fReqs[i].SetFovX(90.f); + fReqs[i].SetFovY(90.f); + + fReqs[i].SetClearColor(fColor); + fReqs[i].SetClearDepth(1.f); + + fReqs[i].SetClearDrawable(nil); + fReqs[i].SetRenderTarget(GetFace(i)); + + fReqs[i].SetCameraTransform(GetWorldToCamera(i), GetCameraToWorld(i)); + + fReqs[i].SetVisForce(fVisSet); + + fReqs[i].RequestAck(GetKey()); + } +} + +void plDynamicEnvMap::ISubmitRenderRequest(int i) +{ + IUpdatePosition(); + fReqMsgs[i]->SendAndKeep(); + fLastRender = i; + fOutStanding++; +} + +void plDynamicEnvMap::ISubmitRenderRequests() +{ + IUpdatePosition(); + int i; + for( i = 0; i < 6; i++ ) + fReqMsgs[i]->SendAndKeep(); + fLastRefresh = hsTimer::GetSysSeconds(); + fOutStanding += 6; +} + +void plDynamicEnvMap::ICheckForRefresh(double t, plPipeline *pipe) +{ + if( fLastRefresh <= 0 ) + { + ISubmitRenderRequests(); + return; + } + + if( fRefreshRate <= 0 ) + return; + +#ifndef PLASMA_EXTERNAL_RELEASE + if (pipe->IsDebugFlagSet(plPipeDbg::kFlagNVPerfHUD) && hsTimer::GetDelSysSeconds() == 0) + { + ISubmitRenderRequests(); + return; + } +#endif // PLASMA_EXTERNAL_RELEASE + + if( t > fLastRefresh + 6.f * fRefreshRate ) + { + ISubmitRenderRequests(); + return; + } + while( t > fLastRefresh + fRefreshRate ) + { + int nextRender = fLastRender+1; + if( nextRender > 5 ) + nextRender = 0; + ISubmitRenderRequest(nextRender); + fLastRefresh += fRefreshRate; + } +} + +void plDynamicEnvMap::ReRender() +{ + ISetupRenderRequests(); + ISubmitRenderRequests(); +} + +hsBool plDynamicEnvMap::INeedReRender() +{ + fOutStanding = 0; + fLastRefresh = 0; + return true; +} + +hsBool plDynamicEnvMap::MsgReceive(plMessage* msg) +{ + plRenderRequestAck* ack = plRenderRequestAck::ConvertNoRef(msg); + if( ack ) + { + fOutStanding--; + return true; + } + plRenderMsg* rendMsg = plRenderMsg::ConvertNoRef(msg); + if( rendMsg ) + { + if( fOutStanding ) + INeedReRender(); + + ICheckForRefresh(hsTimer::GetSysSeconds(), rendMsg->Pipeline()); + return true; + } + if( plPipeRTMakeMsg::ConvertNoRef(msg) ) + { + INeedReRender(); + plCubicRenderTarget::MsgReceive(msg); + return true; + } + plAgeLoadedMsg* ageLoaded = plAgeLoadedMsg::ConvertNoRef(msg); + if( ageLoaded && ageLoaded->fLoaded ) + return INeedReRender(); + + if( plInitialAgeStateLoadedMsg::ConvertNoRef(msg) ) + return INeedReRender(); + + plDynamicEnvMapMsg* cmd = plDynamicEnvMapMsg::ConvertNoRef(msg); + if( cmd ) + { + if( cmd->fCmd & plDynamicEnvMapMsg::kSetPosition ) + SetPosition(cmd->fPos); + + if( cmd->fCmd & plDynamicEnvMapMsg::kSetHither ) + SetHither(cmd->fHither); + + if( cmd->fCmd & plDynamicEnvMapMsg::kSetFogStart ) + SetFogStart(cmd->fFogStart); + + if( cmd->fCmd & plDynamicEnvMapMsg::kSetYon ) + SetYon(cmd->fYon); + + if( cmd->fCmd & plDynamicEnvMapMsg::kSetColor ) + SetColor(cmd->fColor); + + if( cmd->fCmd & plDynamicEnvMapMsg::kSetRefresh ) + SetRefreshRate(cmd->fRefresh); + + // If we're going to ReRender, make sure we've gotten any + // parameter changes first. + if( cmd->fCmd & plDynamicEnvMapMsg::kReRender ) + { + ISetupRenderRequests(); + INeedReRender(); + } + + return true; + } + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + if( IOnRefMsg(refMsg) ) + return true; + } + + return plCubicRenderTarget::MsgReceive(msg); +} + +hsBool plDynamicEnvMap::IOnRefMsg(plGenRefMsg* refMsg) +{ + switch( refMsg->fType) + { + case kRefVisSet: + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + plVisRegion* reg = plVisRegion::ConvertNoRef(refMsg->GetRef()); + int idx = fVisRegions.Find(reg); + if( reg && (fVisRegions.kMissingIndex == idx) ) + { + fVisRegions.Append(reg); + fVisSet.SetBit(reg->GetIndex()); + } + ISetupRenderRequests(); + return true; + } + else + { + plVisRegion* reg = plVisRegion::ConvertNoRef(refMsg->GetRef()); + int idx = fVisRegions.Find(reg); + if( reg && (fVisRegions.kMissingIndex != idx) ) + { + fVisRegions.Remove(idx); + fVisSet.ClearBit(reg->GetIndex()); + } + ISetupRenderRequests(); + return true; + } + break; + + case kRefRootNode: + plSceneObject *so = plSceneObject::ConvertNoRef(refMsg->GetRef()); + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + fRootNode = so; + else + fRootNode = nil; + return true; + } + + return false; +} + +void plDynamicEnvMap::SetIncludeCharacters(hsBool b) +{ + fIncCharacters = b; + if( b ) + fVisSet.SetBit(plVisMgr::kCharacter); + else + fVisSet.ClearBit(plVisMgr::kCharacter); +} + +void plDynamicEnvMap::AddVisRegion(plVisRegion* reg) +{ + plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefVisSet); + hsgResMgr::ResMgr()->AddViaNotify(reg->GetKey(), msg, plRefFlags::kActiveRef); +} + +void plDynamicEnvMap::Read(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Read(s, mgr); + UInt32 sz = plCubicRenderTarget::Read(s); + + fPos.Read(s); + fHither = s->ReadSwapScalar(); + fYon = s->ReadSwapScalar(); + fFogStart = s->ReadSwapScalar(); + fColor.Read(s); + + fRefreshRate = s->ReadSwapScalar(); + + SetPosition(fPos); + + sz += sizeof(fPos) + sizeof(fHither) + sizeof(fYon) + sizeof(fFogStart) + sizeof(fColor) + sizeof(fRefreshRate); + + fIncCharacters = s->ReadByte(); + SetIncludeCharacters(fIncCharacters); + int nVis = s->ReadSwap32(); + int i; + for( i = 0; i < nVis; i++ ) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefVisSet), plRefFlags::kActiveRef); + + nVis = s->ReadSwap32(); + for( i = 0; i < nVis; i++) + { + char *name = s->ReadSafeString(); + plKey key = plKeyFinder::Instance().StupidSearch(nil, nil, plVisRegion::Index(), name); + delete[] name; + if (key) + hsgResMgr::ResMgr()->AddViaNotify(key, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefVisSet), plRefFlags::kActiveRef); + } + + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefRootNode), plRefFlags::kActiveRef); + + Init(); +} + +void plDynamicEnvMap::Write(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Write(s, mgr); + + UInt32 sz = plCubicRenderTarget::Write(s); + + fPos.Write(s); + s->WriteSwapScalar(fHither); + s->WriteSwapScalar(fYon); + s->WriteSwapScalar(fFogStart); + fColor.Write(s); + + s->WriteSwapScalar(fRefreshRate); + + sz += sizeof(fPos) + sizeof(fHither) + sizeof(fYon) + sizeof(fFogStart) + sizeof(fColor) + sizeof(fRefreshRate); + + s->WriteByte(fIncCharacters); + s->WriteSwap32(fVisRegions.GetCount()); + int i; + for( i = 0; i < fVisRegions.GetCount(); i++ ) + mgr->WriteKey(s, fVisRegions[i]); + + s->WriteSwap32(fVisRegionNames.Count()); + for( i = 0; i < fVisRegionNames.Count(); i++) + { + s->WriteSafeString(fVisRegionNames[i]); + } + + mgr->WriteKey(s, fRootNode); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +UInt8 plDynamicCamMap::fFlags = kReflectionEnabled | kReflectionCapable; + +plDynamicCamMap::plDynamicCamMap() : +fHither(0.3f), +fYon(500.f), +fFogStart(1000.f), +fRefreshRate(0.f), +fLastRefresh(0.0), +fOutStanding(0), +fCamera(nil), +fRootNode(nil), +fIncCharacters(false), +fDisableTexture(nil) +{ + fColor.Set(0,0,0,1.f); + fReqMsg = TRACKED_NEW plRenderRequestMsg(nil, &fReq); +} + +plDynamicCamMap::plDynamicCamMap(UInt16 width, UInt16 height, UInt8 bitDepth, UInt8 zDepth, UInt8 sDepth) : +fHither(0.3f), +fYon(-1.f), +fFogStart(-1.f), +fRefreshRate(0.f), +fLastRefresh(0.0), +fOutStanding(0), +fCamera(nil), +fRootNode(nil), +fIncCharacters(false), +fDisableTexture(nil), +plRenderTarget(plRenderTarget::kIsTexture, width, height, bitDepth, zDepth, sDepth) +{ + fColor.Set(0,0,0,1.f); + fReqMsg = TRACKED_NEW plRenderRequestMsg(nil, &fReq); +} + +plDynamicCamMap::~plDynamicCamMap() +{ + plgDispatch::Dispatch()->UnRegisterForExactType(plPipeRTMakeMsg::Index(), GetKey()); + plgDispatch::Dispatch()->UnRegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->UnRegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); + + SetDeviceRef(nil); + + delete fReqMsg; +} + +void plDynamicCamMap::Init() +{ + plgDispatch::Dispatch()->RegisterForExactType(plPipeRTMakeMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); +} + +void plDynamicCamMap::SetRefreshRate(hsScalar secs) +{ + fRefreshRate = secs; + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); +} + +void plDynamicCamMap::ISetupRenderRequest(plPipeline *pipe) +{ + fReq.SetRenderState(plPipeline::kRenderNormal | plPipeline::kRenderClearColor | plPipeline::kRenderClearDepth); + fReq.SetDrawableMask(plDrawable::kNormal); + fReq.SetSubDrawableMask(plDrawable::kSubAllTypes); + fReq.SetHither(fHither); + fReq.SetYon(fYon); + fReq.SetFogStart(fFogStart); + + // For a reflection map, this must match the camera FOV, or else the camera based + // texture coordinates for the reflection texture will be off. + // + // For a fixed camera, you might want to use the height in both params, so that + // you're rendering a square FOV into your square texture. In practice, the artists + // don't mind the visual results when just scaling their UVs, so I'll leave it the + // same for both cases. + fReq.SetFovX(fCamera ? fCamera->GetFOVw() : plVirtualCam1::Instance()->GetFOVw()); + fReq.SetFovY(fCamera ? fCamera->GetFOVh() : plVirtualCam1::Instance()->GetFOVh()); + + fReq.SetClearColor(fColor); + fReq.SetClearDepth(1.f); + fReq.SetClearDrawable(nil); + fReq.SetRenderTarget(this); + fReq.SetVisForce(fVisSet); + fReq.SetIgnoreOccluders(true); + fReq.RequestAck(GetKey()); + + hsMatrix44 w2c, c2w; + if (fCamera) + { + w2c.MakeCamera(&fCamera->GetTargetPos(), &fCamera->GetTargetPOA(), &hsVector3(0.f, 0.f, 1.f)); + w2c.GetInverse(&c2w); + } + else + { + if (!fRootNode) + return; + + // Could be optimized, but the matrix construction work here seems cheap relative to the cost + // of rerendering all this stuff to a separate target, so I doubt we'd notice. + hsMatrix44 invert; + invert.MakeScaleMat(&(hsVector3(1.f, 1.f, -1.f))); + w2c = pipe->GetWorldToCamera(); + c2w = pipe->GetCameraToWorld(); + + w2c = w2c * fRootNode->GetLocalToWorld() * invert * fRootNode->GetWorldToLocal(); + c2w = fRootNode->GetWorldToLocal() * invert * fRootNode->GetLocalToWorld() * c2w; + } + fReq.SetCameraTransform(w2c, c2w); +} + +void plDynamicCamMap::ISubmitRenderRequest(plPipeline *pipe) +{ + ISetupRenderRequest(pipe); + fReqMsg->SendAndKeep(); + fOutStanding++; + fLastRefresh = hsTimer::GetSysSeconds(); +} + +void plDynamicCamMap::ICheckForRefresh(double t, plPipeline *pipe) +{ + int i; + + hsBool useRefl = (fFlags & kReflectionMask) == kReflectionMask; + if (!fCamera) + { + if ((useRefl && fMatLayers[0]->GetTexture() != this) || (!useRefl && fMatLayers[0]->GetTexture() != fDisableTexture)) + IPrepTextureLayers(); + } + + // So no one's using us, eh? Hitting this condition is likely a bug, + // but an assert every frame would be annoying. We'll notice when + // the render target never updates. + if (fTargetNodes.GetCount() == 0) + return; + + // If dynamic planar reflections are disabled and we're using our substitute + // texture, don't update. Otherwise, this particular reflection is forced to + // always display. + if (!useRefl && fDisableTexture) + return; + + hsBool inView = false; + for (i = 0; i < fTargetNodes.GetCount(); i++) + { + if (pipe->TestVisibleWorld(fTargetNodes[i])) + { + inView = true; + break; + } + } + + if (!inView) + return; + + if( fLastRefresh <= 0 ) + { + ISubmitRenderRequest(pipe); + return; + } + + if( fRefreshRate <= 0 ) + return; + +#ifndef PLASMA_EXTERNAL_RELEASE + if (pipe->IsDebugFlagSet(plPipeDbg::kFlagNVPerfHUD) && hsTimer::GetDelSysSeconds() == 0) + { + ISubmitRenderRequest(pipe); + } +#endif // PLASMA_EXTERNAL_RELEASE + + if (t > fLastRefresh + fRefreshRate) + { + ISubmitRenderRequest(pipe); + return; + } +} + +hsBool plDynamicCamMap::INeedReRender() +{ + fOutStanding = 0; + fLastRefresh = 0; + return true; +} + +hsBool plDynamicCamMap::MsgReceive(plMessage* msg) +{ + plRenderRequestAck* ack = plRenderRequestAck::ConvertNoRef(msg); + if( ack ) + { + fOutStanding--; + return true; + } + plRenderMsg* rendMsg = plRenderMsg::ConvertNoRef(msg); + if( rendMsg ) + { + if( fOutStanding ) + INeedReRender(); + + ICheckForRefresh(hsTimer::GetSysSeconds(), rendMsg->Pipeline()); + return true; + } + if( plPipeRTMakeMsg::ConvertNoRef(msg) ) + { + INeedReRender(); + plRenderTarget::MsgReceive(msg); + return true; + } + plAgeLoadedMsg* ageLoaded = plAgeLoadedMsg::ConvertNoRef(msg); + if( ageLoaded && ageLoaded->fLoaded ) + return INeedReRender(); + + if( plInitialAgeStateLoadedMsg::ConvertNoRef(msg) ) + return INeedReRender(); + + plDynamicEnvMapMsg* cmd = plDynamicEnvMapMsg::ConvertNoRef(msg); + if( cmd ) + { + if( cmd->fCmd & plDynamicEnvMapMsg::kSetFogStart ) + fFogStart = cmd->fFogStart; + + if( cmd->fCmd & plDynamicEnvMapMsg::kSetColor ) + fColor = cmd->fColor; + + if( cmd->fCmd & plDynamicEnvMapMsg::kSetRefresh ) + SetRefreshRate(cmd->fRefresh); + + return true; + } + plRefMsg* refMsg = plRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + if( IOnRefMsg(refMsg) ) + return true; + } + + return plRenderTarget::MsgReceive(msg); +} + +void plDynamicCamMap::IPrepTextureLayers() +{ + if (fDisableTexture) + { + int i; + for (i = 0; i < fMatLayers.GetCount(); i++) + { + if ((fFlags & kReflectionMask) == kReflectionMask) + { + fMatLayers[i]->SetUVWSrc(plLayerInterface::kUVWPosition); + fMatLayers[i]->SetMiscFlags(hsGMatState::kMiscCam2Screen | hsGMatState::kMiscPerspProjection); + hsgResMgr::ResMgr()->SendRef(GetKey(), TRACKED_NEW plGenRefMsg(fMatLayers[i]->GetKey(), plRefMsg::kOnRequest, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef); + } + else + { + fMatLayers[i]->SetUVWSrc(0); + fMatLayers[i]->SetMiscFlags(0); + hsgResMgr::ResMgr()->SendRef(fDisableTexture->GetKey(), TRACKED_NEW plGenRefMsg(fMatLayers[i]->GetKey(), plRefMsg::kOnRequest, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef); + } + } + } +} + +hsBool plDynamicCamMap::IOnRefMsg(plRefMsg* refMsg) +{ + plGenRefMsg* genRefMsg = plGenRefMsg::ConvertNoRef(refMsg); + + if (genRefMsg) + { + if (genRefMsg->fType == kRefVisSet) + { + plVisRegion* reg = plVisRegion::ConvertNoRef(refMsg->GetRef()); + if (reg) + { + int idx = fVisRegions.Find(reg); + if ((refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) && fVisRegions.kMissingIndex == idx) + { + fVisRegions.Append(reg); + fVisSet.SetBit(reg->GetIndex()); + } + else if (!(refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) && fVisRegions.kMissingIndex != idx) + { + fVisRegions.Remove(idx); + fVisSet.ClearBit(reg->GetIndex()); + } + return true; + } + } + else if (genRefMsg->fType == kRefCamera) + { + plCameraModifier1 *cam = plCameraModifier1::ConvertNoRef(refMsg->GetRef()); + if (cam) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) + fCamera = cam; + else + fCamera = nil; + + return true; + } + } + else if (genRefMsg->fType == kRefRootNode) + { + plSceneObject *so = plSceneObject::ConvertNoRef(refMsg->GetRef()); + if (so) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) + fRootNode = so; + else + fRootNode = nil; + + return true; + } + } + else if (genRefMsg->fType == kRefTargetNode) + { + plSceneObject *so = plSceneObject::ConvertNoRef(refMsg->GetRef()); + if (so) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) + { + if (fTargetNodes.Find(so) == fTargetNodes.kMissingIndex) + fTargetNodes.Append(so); + } + else + { + fTargetNodes.RemoveItem(so); + } + + return true; + } + } + else if (genRefMsg->fType == kRefDisableTexture) + { + plBitmap *bitmap = plBitmap::ConvertNoRef(refMsg->GetRef()); + if (bitmap) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) + fDisableTexture = bitmap; + else + fDisableTexture = nil; + + return true; + } + } + else if (genRefMsg->fType == kRefMatLayer) + { + plLayer *lay = plLayer::ConvertNoRef(refMsg->GetRef()); + if (lay) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace)) + { + if (fMatLayers.Find(lay) == fMatLayers.kMissingIndex) + fMatLayers.Append(lay); + } + else + { + fMatLayers.RemoveItem(lay); + } + + return true; + } + } + } + + return false; +} + +void plDynamicCamMap::SetIncludeCharacters(hsBool b) +{ + fIncCharacters = b; + if( b ) + fVisSet.SetBit(plVisMgr::kCharacter); + else + fVisSet.ClearBit(plVisMgr::kCharacter); +} + +void plDynamicCamMap::AddVisRegion(plVisRegion* reg) +{ + hsgResMgr::ResMgr()->AddViaNotify( reg->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plGenRefMsg::kOnReplace, -1, kRefVisSet ), plRefFlags::kActiveRef ); +} + +void plDynamicCamMap::SetEnabled(hsBool enable) +{ + if (enable) + fFlags |= kReflectionEnabled; + else + fFlags &= ~kReflectionEnabled; +} + +void plDynamicCamMap::SetCapable(hsBool capable) +{ + if (capable) + fFlags |= kReflectionCapable; + else + fFlags &= ~kReflectionCapable; +} + +void plDynamicCamMap::Read(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Read(s, mgr); + plRenderTarget::Read(s); + + fHither = s->ReadSwapScalar(); + fYon = s->ReadSwapScalar(); + fFogStart = s->ReadSwapScalar(); + fColor.Read(s); + + fRefreshRate = s->ReadSwapScalar(); + fIncCharacters = s->ReadBool(); + SetIncludeCharacters(fIncCharacters); + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefCamera), plRefFlags::kPassiveRef); + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefRootNode), plRefFlags::kActiveRef); + + int numTargs = s->ReadByte(); + int i; + for (i = 0; i < numTargs; i++) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, i, kRefTargetNode), plRefFlags::kPassiveRef); + + int nVis = s->ReadSwap32(); + for( i = 0; i < nVis; i++ ) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefVisSet), plRefFlags::kActiveRef); + + nVis = s->ReadSwap32(); + for( i = 0; i < nVis; i++) + { + char *name = s->ReadSafeString(); + plKey key = plKeyFinder::Instance().StupidSearch(nil, nil, plVisRegion::Index(), name); + delete[] name; + if (key) + hsgResMgr::ResMgr()->AddViaNotify(key, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kRefVisSet), plRefFlags::kActiveRef); + } + + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefDisableTexture), plRefFlags::kActiveRef); + + UInt8 numLayers = s->ReadByte(); + for (i = 0; i < numLayers; i++) + { + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefMatLayer), plRefFlags::kPassiveRef); + } + + Init(); +} + +void plDynamicCamMap::Write(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Write(s, mgr); + plRenderTarget::Write(s); + + s->WriteSwapScalar(fHither); + s->WriteSwapScalar(fYon); + s->WriteSwapScalar(fFogStart); + fColor.Write(s); + + s->WriteSwapScalar(fRefreshRate); + s->WriteByte(fIncCharacters); + mgr->WriteKey(s, (fCamera ? fCamera->GetKey() : nil)); + mgr->WriteKey(s, (fRootNode ? fRootNode->GetKey() : nil)); + + s->WriteByte(fTargetNodes.GetCount()); + int i; + for (i = 0; i < fTargetNodes.GetCount(); i++) + mgr->WriteKey(s, fTargetNodes[i]); + + s->WriteSwap32(fVisRegions.GetCount()); + for( i = 0; i < fVisRegions.GetCount(); i++ ) + mgr->WriteKey(s, fVisRegions[i]); + + s->WriteSwap32(fVisRegionNames.Count()); + for( i = 0; i < fVisRegionNames.Count(); i++) + { + s->WriteSafeString(fVisRegionNames[i]); + } + + mgr->WriteKey(s, fDisableTexture ? fDisableTexture->GetKey() : nil); + + s->WriteByte(fMatLayers.GetCount()); + for (i = 0; i < fMatLayers.GetCount(); i++) + { + mgr->WriteKey(s, fMatLayers[i]->GetKey()); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDynamicEnvMap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDynamicEnvMap.h new file mode 100644 index 00000000..6444d1e7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plDynamicEnvMap.h @@ -0,0 +1,209 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDynamicEnvMap_inc +#define plDynamicEnvMap_inc + +#include "plCubicRenderTarget.h" +#include "../plScene/plRenderRequest.h" +#include "hsBitVector.h" + +class plRenderRequestMsg; +class hsStream; +class plMessage; +class plVisRegion; +class plGenRefMsg; +class hsResMgr; +class plCameraModifier1; +class plSceneObject; +class plBitmap; +class plLayer; + +class plDynamicEnvMap : public plCubicRenderTarget +{ +public: + enum { + kRefVisSet, + kRefRootNode, + }; +protected: + + plRenderRequest fReqs[6]; + plRenderRequestMsg* fReqMsgs[6]; + + plSceneObject* fRootNode; + hsPoint3 fPos; + hsScalar fHither; + hsScalar fYon; + hsScalar fFogStart; + hsColorRGBA fColor; + + hsScalar fRefreshRate; + double fLastRefresh; + int fLastRender; + int fOutStanding; + + hsBitVector fVisSet; + hsTArray fVisRegions; + hsTArray fVisRegionNames; + hsBool fIncCharacters; + + void IUpdatePosition(); + hsBool INeedReRender(); + + void ISetupRenderRequests(); + void ISubmitRenderRequests(); + void ISubmitRenderRequest(int i); + void ICheckForRefresh(double t, plPipeline *pipe); + + hsBool IOnRefMsg(plGenRefMsg* refMsg); + +public: + plDynamicEnvMap(); + plDynamicEnvMap(UInt16 width, UInt16 height, UInt8 bitDepth, UInt8 zDepth = -1, UInt8 sDepth = -1); + + virtual ~plDynamicEnvMap(); + + CLASSNAME_REGISTER( plDynamicEnvMap ); + GETINTERFACE_ANY( plDynamicEnvMap, plCubicRenderTarget ); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + void ReRender(); + + void Init(); + + void SetPosition(const hsPoint3& pos); + void SetHither(hsScalar f); + void SetYon(hsScalar f); + void SetFogStart(hsScalar f); + void SetColor(const hsColorRGBA& col); + void SetRefreshRate(hsScalar secs); + + hsPoint3 GetPosition() const; + hsScalar GetHither() const { return fHither; } + hsScalar GetYon() const { return fYon; } + hsScalar GetFogStart() const { return fFogStart; } + hsColorRGBA GetColor() const { return fColor; } + hsScalar GetRefreshRate() const { return 6.f * fRefreshRate; } + + void AddVisRegion(plVisRegion* reg); // Will just send a ref + + void SetIncludeCharacters(hsBool b); + hsBool GetIncludeCharacters() const { return fIncCharacters; } + void SetVisRegionName(char *name){ fVisRegionNames.Push(name); } +}; + +//////////////////////////////////////////////////////////////////////////// +// Yes, it's lame that a lot of this code is nearly the same as +// plDynamicEnvMap, but this derives from plRenderTarget, not plCubicRenderTarget +// and I don't want to touch multiple inheritance. +class plDynamicCamMap : public plRenderTarget +{ +public: + enum + { + kRefVisSet, + kRefCamera, + kRefRootNode, + kRefTargetNode, + kRefDisableTexture, + kRefMatLayer, + }; + + hsScalar fHither; + hsScalar fYon; + hsScalar fFogStart; + hsColorRGBA fColor; + +protected: + plRenderRequest fReq; + plRenderRequestMsg* fReqMsg; + + hsScalar fRefreshRate; + double fLastRefresh; + int fOutStanding; + + hsBitVector fVisSet; + hsTArray fVisRegions; + hsTArray fVisRegionNames; // this allows us to specify vis-regions in other pages. + hsBool fIncCharacters; + plCameraModifier1* fCamera; + plSceneObject* fRootNode; + hsTArray fTargetNodes; + + // Extra info for swapping around textures when reflections are disabled. + plBitmap* fDisableTexture; + hsTArray fMatLayers; + static UInt8 fFlags; + enum + { + kReflectionCapable = 0x01, + kReflectionEnabled = 0x02, + kReflectionMask = kReflectionCapable | kReflectionEnabled, + }; + + hsBool INeedReRender(); + + void ISetupRenderRequest(plPipeline *pipe); + void ISubmitRenderRequest(plPipeline *pipe); + void ICheckForRefresh(double t, plPipeline *pipe); + void IPrepTextureLayers(); + + hsBool IOnRefMsg(plRefMsg* refMsg); + +public: + plDynamicCamMap(); + plDynamicCamMap(UInt16 width, UInt16 height, UInt8 bitDepth, UInt8 zDepth = -1, UInt8 sDepth = -1); + + virtual ~plDynamicCamMap(); + + CLASSNAME_REGISTER( plDynamicCamMap ); + GETINTERFACE_ANY( plDynamicCamMap, plRenderTarget ); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + void ReRender(); + void Init(); + + void SetIncludeCharacters(hsBool b); + void SetRefreshRate(hsScalar secs); + void AddVisRegion(plVisRegion* reg); + void SetVisRegionName(char *name){ fVisRegionNames.Push(name); } + + static hsBool GetEnabled() { return (fFlags & kReflectionEnabled) != 0; } + static void SetEnabled(hsBool enable); + static hsBool GetCapable() { return (fFlags & kReflectionCapable) != 0; } + static void SetCapable(hsBool capable); +}; + +#endif // plDynamicEnvMap_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plFogEnvironment.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plFogEnvironment.cpp new file mode 100644 index 00000000..fc0f4284 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plFogEnvironment.cpp @@ -0,0 +1,197 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plFogEnvironment.cpp - Functions for the fog volume class // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "plFogEnvironment.h" + +#include "plTweak.h" + +//// Constructors & Destructor /////////////////////////////////////////////// + +plFogEnvironment::plFogEnvironment() +{ + fType = kNoFog; +} + +plFogEnvironment::plFogEnvironment( hsScalar start, hsScalar end, hsScalar density, hsColorRGBA &color ) +{ + Set( start, end, density, &color ); +} + +plFogEnvironment::plFogEnvironment( FogType type, hsScalar end, hsScalar density, hsColorRGBA &color ) +{ + SetExp( type, end, density, &color ); +} + +plFogEnvironment::~plFogEnvironment() +{ +} + +//// Set ///////////////////////////////////////////////////////////////////// + +void plFogEnvironment::Set( hsScalar start, hsScalar end, hsScalar density, const hsColorRGBA *color ) +{ + if( density <= 0.f ) + { + fType = kNoFog; + fStart = 0.f; + fEnd = 0.f; + fDensity = 0.f; + } + else + { + fType = kLinearFog; + fStart = start; + fEnd = end; + fDensity = density; + } + if( color != nil ) + fColor = *color; +} + +void plFogEnvironment::SetExp( FogType type, hsScalar end, hsScalar density, const hsColorRGBA *color ) +{ + hsAssert( type == kExpFog || type == kExp2Fog, "Invalid fog type passed to plFogEnvironment" ); + if( density <= 0.f ) + { + fType = kNoFog; + fStart = 0.f; + fEnd = 0.f; + fDensity = 0.f; + } + else + { + fType = type; + fStart = 0.0f; + fEnd = end; + fDensity = density; + } + if( color != nil ) + fColor = *color; +} + +//// GetParameters /////////////////////////////////////////////////////////// +// Gets the parameters. Sets start to 0 if the type is not linear (can be +// nil). + +void plFogEnvironment::GetParameters( hsScalar *start, hsScalar *end, hsScalar *density, hsColorRGBA *color ) +{ + hsAssert( fType != kLinearFog || start != nil, "Trying to get non-linear paramters on linear fog!" ); + hsAssert( end != nil && density != nil && color != nil, "Bad pointer to plFogEnvironment::GetParameters()" ); + + if( fType == kLinearFog ) + *start = fStart; + else if( start != nil ) + *start = 0.0f; + + *end = fEnd; + *density = fDensity; + *color = fColor; +} + +//// GetPipelineParams /////////////////////////////////////////////////////// +// Gets linear pipeline (DX8) specific parameters. Basically massages our +// interface values into values that DX8 can use. In this case, we simply +// scale our end value out by the density. The whole formula is: +// pipelineEnd = ( end - start ) / density + start + +void plFogEnvironment::GetPipelineParams( hsScalar *start, hsScalar *end, hsColorRGBA *color ) +{ +// hsAssert( fType == kLinearFog, "Getting linear pipeline params on non-linear fog!" ); + + *color = fColor; + switch(fType) + { + case kLinearFog: + *start = fStart; + *end = (fEnd - fStart) / fDensity + fStart; + break; + case kExpFog: + { + plConst(float) kKnee(0.0f); + *start = fEnd * kKnee; + *end = (fEnd - *start) / fDensity + *start; + } + break; + default: + case kExp2Fog: + { + plConst(float) kKnee(0.3f); + *start = fEnd * kKnee; + *end = (fEnd - *start) / fDensity + *start; + } + break; + } +} + +//// GetPipelineParams /////////////////////////////////////////////////////// +// Gets exp/exp^2 pipeline (DX8) specific parameters. Basically massages our +// interface values into values that DX8 can use. In this case, we're going +// to modulate the density by the end value so that it actually ends at the +// right spot. + +void plFogEnvironment::GetPipelineParams( hsScalar *density, hsColorRGBA *color ) +{ + const float ln256 = logf( 256.f ); + const float sqrtLn256 = sqrtf( ln256 ); + + + hsAssert( fType == kExpFog || fType == kExp2Fog, "Getting non-linear pipeline params on linear fog!" ); + + *density = ( ( fType == kExpFog ) ? ln256: sqrtLn256 ) * fDensity / fEnd; + *color = fColor; +} + +//// Read //////////////////////////////////////////////////////////////////// + +void plFogEnvironment::Read( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Read( s, mgr ); + + fType = s->ReadByte(); + fStart = s->ReadSwapFloat(); + fEnd = s->ReadSwapFloat(); + fDensity = s->ReadSwapFloat(); + fColor.Read( s ); +} + +//// Write /////////////////////////////////////////////////////////////////// + +void plFogEnvironment::Write( hsStream *s, hsResMgr *mgr ) +{ + hsKeyedObject::Write( s, mgr ); + + s->WriteByte( fType ); + s->WriteSwapFloat( fStart ); + s->WriteSwapFloat( fEnd ); + s->WriteSwapFloat( fDensity ); + fColor.Write( s ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plFogEnvironment.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plFogEnvironment.h new file mode 100644 index 00000000..1fc20422 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plFogEnvironment.h @@ -0,0 +1,104 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plFogEnvironment.h - Header file for the fog environment class // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plFogEnvironment_h +#define _plFogEnvironment_h + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsTypes.h" +#include "hsColorRGBA.h" +#include "hsTemplates.h" +#include "hsUtils.h" + + +//// plFogEnvironment Class Definition //////////////////////////////////////////// +// Defines a fog environment. Bite me. + +class plFogEnvironment : public hsKeyedObject +{ + protected: + + UInt8 fType; + hsScalar fStart; // Used for linear fog only + hsScalar fEnd, fDensity; // Always used! + hsColorRGBA fColor; + + public: + + CLASSNAME_REGISTER( plFogEnvironment ); + GETINTERFACE_ANY( plFogEnvironment, hsKeyedObject ); + + enum FogType + { + kLinearFog = 0, + kExpFog, + kExp2Fog, + kNoFog + }; + + plFogEnvironment(); + plFogEnvironment( hsScalar start, hsScalar end, hsScalar density, hsColorRGBA &color ); + plFogEnvironment( FogType type, hsScalar end, hsScalar density, hsColorRGBA &color ); + ~plFogEnvironment(); + + // Sets the parameters for linear fog + void Set( hsScalar start, hsScalar end, hsScalar density, const hsColorRGBA *color = nil ); + + // Sets the parameters for exp or exp^2 fog + void SetExp( FogType type, hsScalar end, hsScalar density, const hsColorRGBA *color = nil ); + + // Sets the color + void SetColor( hsColorRGBA &color ) { fColor = color; } + + // Clear the environment to no fog + void Clear( void ) { fType = kNoFog; } + + // Gets the type + UInt8 GetType( void ) { return fType; } + + // Gets the color + hsColorRGBA &GetColor( void ) { return fColor; } + + // Gets the parameters. Sets start to 0 if the type is not linear (can be nil) + void GetParameters( hsScalar *start, hsScalar *end, hsScalar *density, hsColorRGBA *color ); + + // Gets linear pipeline (DX) specific parameters. + void GetPipelineParams( hsScalar *start, hsScalar *end, hsColorRGBA *color ); + + // Gets exp or exp^2 pipeline (DX) specific parameters. + void GetPipelineParams( hsScalar *density, hsColorRGBA *color ); + + virtual void Read(hsStream *s, hsResMgr *mgr); + virtual void Write(hsStream *s, hsResMgr *mgr); +}; + +#endif //_plFogEnvironment_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plGBufferGroup.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plGBufferGroup.cpp new file mode 100644 index 00000000..7b84a1b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plGBufferGroup.cpp @@ -0,0 +1,1277 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plGBufferGroup Class Functions // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 2.21.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsWindows.h" +#include "HeadSpin.h" +#include "plGBufferGroup.h" +#include "hsStream.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plDrawable/plGeometrySpan.h" +#include "plPipeline.h" +#include "hsGDeviceRef.h" +#include "plProfile.h" + +#include "plVertCoder.h" + +plProfile_CreateMemCounter("Buf Group Vertices", "Memory", MemBufGrpVertex); +plProfile_CreateMemCounter("Buf Group Indices", "Memory", MemBufGrpIndex); +plProfile_CreateTimer("Refill Vertex", "Draw", DrawRefillVertex); +plProfile_CreateTimer("Refill Index", "Draw", DrawRefillIndex); + +const UInt32 plGBufferGroup::kMaxNumVertsPerBuffer = 32000; +const UInt32 plGBufferGroup::kMaxNumIndicesPerBuffer = 32000; + + +//// plGBufferTriangle Read and Write ///////////////////////////////////////// + +void plGBufferTriangle::Read( hsStream *s ) +{ + fIndex1 = s->ReadSwap16(); + fIndex2 = s->ReadSwap16(); + fIndex3 = s->ReadSwap16(); + fSpanIndex = s->ReadSwap16(); + fCenter.Read( s ); +} + +void plGBufferTriangle::Write( hsStream *s ) +{ + s->WriteSwap16( fIndex1 ); + s->WriteSwap16( fIndex2 ); + s->WriteSwap16( fIndex3 ); + s->WriteSwap16( fSpanIndex ); + fCenter.Write( s ); +} + +//// plGBufferCell Read/Write ///////////////////////////////////////////////// + +void plGBufferCell::Read( hsStream *s ) +{ + fVtxStart = s->ReadSwap32(); + fColorStart = s->ReadSwap32(); + fLength = s->ReadSwap32(); +} + +void plGBufferCell::Write( hsStream *s ) +{ + s->WriteSwap32( fVtxStart ); + s->WriteSwap32( fColorStart ); + s->WriteSwap32( fLength ); +} + +//// Constructor ////////////////////////////////////////////////////////////// + +plGBufferGroup::plGBufferGroup( UInt8 format, hsBool vertsVolatile, hsBool idxVolatile, int LOD ) +{ + fVertBuffStorage.Reset(); + fIdxBuffStorage.Reset(); + fColorBuffStorage.Reset(); + fVertexBufferRefs.Reset(); + fIndexBufferRefs.Reset(); + fCells.Reset(); + fNumVerts = fNumIndices = 0; + + fFormat = format; + fStride = ICalcVertexSize( fLiteStride ); + fVertsVolatile = vertsVolatile; + fIdxVolatile = idxVolatile; + fLOD = LOD; +} + +//// Destructor /////////////////////////////////////////////////////////////// + +plGBufferGroup::~plGBufferGroup() +{ + UInt32 i; + + CleanUp(); + + for( i = 0; i < fVertexBufferRefs.GetCount(); i++ ) + hsRefCnt_SafeUnRef( fVertexBufferRefs[ i ] ); + + for( i = 0; i < fIndexBufferRefs.GetCount(); i++ ) + hsRefCnt_SafeUnRef( fIndexBufferRefs[ i ] ); + + fVertexBufferRefs.Reset(); + fIndexBufferRefs.Reset(); +} + +void plGBufferGroup::DirtyVertexBuffer(int i) +{ + if( (i < fVertexBufferRefs.GetCount()) && fVertexBufferRefs[i] ) + fVertexBufferRefs[i]->SetDirty(true); +} + +void plGBufferGroup::DirtyIndexBuffer(int i) +{ + if( (i < fIndexBufferRefs.GetCount()) && fIndexBufferRefs[i] ) + fIndexBufferRefs[i]->SetDirty(true); +} + +//// TidyUp /////////////////////////////////////////////////////////////////// + +void plGBufferGroup::TidyUp( void ) +{ +/* if( fVertBuffStorage.GetCount() == 0 && fNumVerts > 0 ) + return; // Already tidy'd! + +// IConvertToStorage(); +*/ +} + +void plGBufferGroup::PurgeVertBuffer(UInt32 idx) +{ + if( AreVertsVolatile() ) + return; + +//#define MF_TOSSER +#ifdef MF_TOSSER + plProfile_DelMem(MemBufGrpVertex, fVertBuffSizes[idx]); + delete [] fVertBuffStorage[idx]; + fVertBuffStorage[idx] = nil; + + plProfile_DelMem(MemBufGrpVertex, fColorBuffCounts[idx] * sizeof(plGBufferColor)); + delete [] fColorBuffStorage[idx]; + fColorBuffStorage[idx] = nil; + + delete fCells[idx]; + fCells[idx] = nil; + +#endif // MF_TOSSER + return; +} + +void plGBufferGroup::PurgeIndexBuffer(UInt32 idx) +{ + if( AreIdxVolatile() ) + return; + + return; +} + +//// CleanUp ////////////////////////////////////////////////////////////////// + +void plGBufferGroup::CleanUp( void ) +{ + int i; + + + // Clean up the storage + for( i = 0; i < fVertBuffStorage.GetCount(); i++ ) + { + plProfile_DelMem(MemBufGrpVertex, fVertBuffSizes[i]); + delete [] fVertBuffStorage[ i ]; + } + for( i = 0; i < fIdxBuffStorage.GetCount(); i++ ) + { + plProfile_DelMem(MemBufGrpIndex, fIdxBuffCounts[i] * sizeof(UInt16)); + delete [] fIdxBuffStorage[ i ]; + } + for( i = 0; i < fColorBuffStorage.GetCount(); i++ ) + { + plProfile_DelMem(MemBufGrpVertex, fColorBuffCounts[i] * sizeof(plGBufferColor)); + delete [] fColorBuffStorage[ i ]; + } + + for( i = 0; i < fCells.GetCount(); i++ ) + delete fCells[ i ]; + + fVertBuffStorage.Reset(); + fVertBuffSizes.Reset(); + fVertBuffStarts.Reset(); + fVertBuffEnds.Reset(); + fIdxBuffStorage.Reset(); + fIdxBuffCounts.Reset(); + fIdxBuffStarts.Reset(); + fIdxBuffEnds.Reset(); + fColorBuffStorage.Reset(); + fColorBuffCounts.Reset(); + + fCells.Reset(); +} + +//// SetVertexBufferRef /////////////////////////////////////////////////////// + +void plGBufferGroup::SetVertexBufferRef( UInt32 index, hsGDeviceRef *vb ) +{ + hsAssert( index < fVertexBufferRefs.GetCount() + 1, "Vertex buffers must be assigned linearly!" ); + + if( (int)index > (int)fVertexBufferRefs.GetCount() - 1 ) + { + fVertexBufferRefs.Append( vb ); + hsRefCnt_SafeRef( vb ); + } + else + { + hsRefCnt_SafeAssign( fVertexBufferRefs[ index ], vb ); + } + +} + +//// SetIndexBufferRef //////////////////////////////////////////////////////// + +void plGBufferGroup::SetIndexBufferRef( UInt32 index, hsGDeviceRef *ib ) +{ + hsAssert( index < fIndexBufferRefs.GetCount() + 1, "Index buffers must be assigned linearly!" ); + + if( (int)index > (int)fIndexBufferRefs.GetCount() - 1 ) + { + fIndexBufferRefs.Append( ib ); + hsRefCnt_SafeRef( ib ); + } + else + { + hsRefCnt_SafeAssign( fIndexBufferRefs[ index ], ib ); + } + +} + +//// PrepForRendering ///////////////////////////////////////////////////////// + +void plGBufferGroup::PrepForRendering( plPipeline *pipe, hsBool adjustForNvidiaLighting ) +{ + ISendStorageToBuffers( pipe, adjustForNvidiaLighting ); + // The following line was taken out so we'd keep our data around, allowing + // us to rebuild the buffer if necessary on the fly +// CleanUp(); + +} + +hsGDeviceRef* plGBufferGroup::GetVertexBufferRef(UInt32 i) +{ + if( i >= fVertexBufferRefs.GetCount() ) + fVertexBufferRefs.ExpandAndZero(i+1); + + return fVertexBufferRefs[i]; +} + +hsGDeviceRef* plGBufferGroup::GetIndexBufferRef(UInt32 i) +{ + if( i >= fIndexBufferRefs.GetCount() ) + fIndexBufferRefs.ExpandAndZero(i+1); + + return fIndexBufferRefs[i]; +} + +//// ISendStorageToBuffers //////////////////////////////////////////////////// + +void plGBufferGroup::ISendStorageToBuffers( plPipeline *pipe, hsBool adjustForNvidiaLighting ) +{ + plProfile_BeginTiming(DrawRefillVertex); + + /// Creating or refreshing? + int i; + for( i = 0; i < fVertBuffStorage.GetCount(); i++ ) + { + pipe->CheckVertexBufferRef(this, i); + } + plProfile_EndTiming(DrawRefillVertex); + + plProfile_BeginTiming(DrawRefillIndex); + + for( i = 0; i < fIdxBuffStorage.GetCount(); i++ ) + { + pipe->CheckIndexBufferRef(this, i); + } + + plProfile_EndTiming(DrawRefillIndex); +} + + +//// ICalcVertexSize ////////////////////////////////////////////////////////// + +UInt8 plGBufferGroup::ICalcVertexSize( UInt8 &liteStride ) +{ + UInt8 size; + + + size = sizeof( float ) * ( 3 + 3 ); // pos + normal + size += sizeof( float ) * 3 * GetNumUVs(); + + switch( fFormat & kSkinWeightMask ) + { + case kSkinNoWeights: fNumSkinWeights = 0; break; + case kSkin1Weight: fNumSkinWeights = 1; break; + case kSkin2Weights: fNumSkinWeights = 2; break; + case kSkin3Weights: fNumSkinWeights = 3; break; + default: hsAssert( false, "Bad weight count in ICalcVertexSize()" ); + } + if( fNumSkinWeights ) + { + size += sizeof( float ) * fNumSkinWeights; + if( fFormat & kSkinIndices ) + size += sizeof( UInt32 ); + } + + liteStride = size; + size += sizeof( DWORD ) * 2; // diffuse + specular + return size; +} + +//// I/O Functions //////////////////////////////////////////////////////////// + +void plGBufferGroup::Read( hsStream *s ) +{ + UInt32 totalDynSize, i, count, temp = 0, j; + UInt8 *vData; + UInt16 *iData; + plGBufferColor *cData; + + + s->ReadSwap( &fFormat ); + totalDynSize = s->ReadSwap32(); + fStride = ICalcVertexSize( fLiteStride ); + + fVertBuffSizes.Reset(); + fVertBuffStarts.Reset(); + fVertBuffEnds.Reset(); + fColorBuffCounts.Reset(); + fIdxBuffCounts.Reset(); + fIdxBuffStarts.Reset(); + fIdxBuffEnds.Reset(); + fVertBuffStorage.Reset(); + fIdxBuffStorage.Reset(); + + plVertCoder coder; + /// Create buffers and read in as we go + count = s->ReadSwap32(); + for( i = 0; i < count; i++ ) + { + if( fFormat & kEncoded ) + { + const UInt16 numVerts = s->ReadSwap16(); + const UInt32 size = numVerts * fStride; + + fVertBuffSizes.Append(size); + fVertBuffStarts.Append(0); + fVertBuffEnds.Append(-1); + + vData = TRACKED_NEW UInt8[size]; + fVertBuffStorage.Append( vData ); + plProfile_NewMem(MemBufGrpVertex, temp); + + coder.Read(s, vData, fFormat, fStride, numVerts); + + fColorBuffCounts.Append(0); + fColorBuffStorage.Append(nil); + + } + else + { + temp = s->ReadSwap32(); + + fVertBuffSizes.Append( temp ); + fVertBuffStarts.Append(0); + fVertBuffEnds.Append(-1); + + vData = TRACKED_NEW UInt8[ temp ]; + hsAssert( vData != nil, "Not enough memory to read in vertices" ); + s->Read( temp, (void *)vData ); + fVertBuffStorage.Append( vData ); + plProfile_NewMem(MemBufGrpVertex, temp); + + temp = s->ReadSwap32(); + fColorBuffCounts.Append( temp ); + + if( temp > 0 ) + { + cData = TRACKED_NEW plGBufferColor[ temp ]; + s->Read( temp * sizeof( plGBufferColor ), (void *)cData ); + plProfile_NewMem(MemBufGrpVertex, temp * sizeof(plGBufferColor)); + } + else + cData = nil; + + fColorBuffStorage.Append( cData ); + } + } + + count = s->ReadSwap32(); + for( i = 0; i < count; i++ ) + { + temp = s->ReadSwap32(); + fIdxBuffCounts.Append( temp ); + fIdxBuffStarts.Append(0); + fIdxBuffEnds.Append(-1); + + iData = TRACKED_NEW UInt16[ temp ]; + hsAssert( iData != nil, "Not enough memory to read in indices" ); + s->ReadSwap16( temp, (UInt16 *)iData ); + fIdxBuffStorage.Append( iData ); + plProfile_NewMem(MemBufGrpIndex, temp * sizeof(UInt16)); + } + + /// Read in cell arrays, one per vBuffer + for( i = 0; i < fVertBuffStorage.GetCount(); i++ ) + { + temp = s->ReadSwap32(); + + fCells.Append( TRACKED_NEW hsTArray ); + fCells[ i ]->SetCount( temp ); + + for( j = 0; j < temp; j++ ) + (*fCells[ i ])[ j ].Read( s ); + } + +} + +//#define VERT_LOG + +void plGBufferGroup::Write( hsStream *s ) +{ + UInt32 totalDynSize, i, j; + +#define MF_VERTCODE_ENABLED +#ifdef MF_VERTCODE_ENABLED + fFormat |= kEncoded; +#endif // MF_VERTCODE_ENABLED + +#ifdef VERT_LOG + hsUNIXStream log; + log.Open("log\\GBuf.log", "ab"); +#endif + + /// Calc total dynamic data size, for fun + totalDynSize = 0; + for( i = 0; i < fVertBuffSizes.GetCount(); i++ ) + totalDynSize += fVertBuffSizes[ i ]; + for( i = 0; i < fIdxBuffCounts.GetCount(); i++ ) + totalDynSize += sizeof( UInt16 ) * fIdxBuffCounts[ i ]; + + s->WriteSwap( fFormat ); + s->WriteSwap32( totalDynSize ); + + plVertCoder coder; + + /// Write out dyanmic data + s->WriteSwap32( (UInt32)fVertBuffStorage.GetCount() ); + for( i = 0; i < fVertBuffStorage.GetCount(); i++ ) + { +#ifdef MF_VERTCODE_ENABLED + + hsAssert(fCells[i]->GetCount() == 1, "Data must be interleaved for compression"); + UInt32 numVerts = fVertBuffSizes[i] / fStride; + s->WriteSwap16((UInt16)numVerts); + coder.Write(s, fVertBuffStorage[i], fFormat, fStride, (UInt16)numVerts); + +#ifdef VERT_LOG + char buf[256]; + sprintf(buf, "Vert Buff: %u bytes, idx=%u\r\n", fVertBuffSizes[i], i); + log.WriteString(buf); + for (int xx = 0; xx < fVertBuffSizes[i] / 4; xx++) + { + float* buff32 = (float*)fVertBuffStorage[i]; + buff32 += xx; + sprintf(buf, "[%d]%f\r\n", xx*4, *buff32); + log.WriteString(buf); + } +#endif + +#else // MF_VERTCODE_ENABLED + + s->WriteSwap32( fVertBuffSizes[ i ] ); + s->Write( fVertBuffSizes[ i ], (void *)fVertBuffStorage[ i ] ); + + + s->WriteSwap32( fColorBuffCounts[ i ] ); + s->Write( fColorBuffCounts[ i ] * sizeof( plGBufferColor ), (void *)fColorBuffStorage[ i ] ); + +#endif // MF_VERTCODE_ENABLED + } + + s->WriteSwap32( (UInt32)fIdxBuffCounts.GetCount() ); + for( i = 0; i < fIdxBuffStorage.GetCount(); i++ ) + { + s->WriteSwap32( fIdxBuffCounts[ i ] ); + s->WriteSwap16( fIdxBuffCounts[ i ], fIdxBuffStorage[ i ] ); + } + + /// Write out cell arrays + for( i = 0; i < fVertBuffStorage.GetCount(); i++ ) + { + s->WriteSwap32( fCells[ i ]->GetCount() ); + for( j = 0; j < fCells[ i ]->GetCount(); j++ ) + (*fCells[ i ])[ j ].Write( s ); + } + +#ifdef VERT_LOG + log.Close(); +#endif + // All done! +} + +/////////////////////////////////////////////////////////////////////////////// +//// Editing Functions //////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// DeleteVertsFromStorage /////////////////////////////////////////////////// +// Deletes a span of verts from the vertex storage. Remember to Prep this +// group after doing this! +// Note: does NOT adjust index storage, since we don't know inside here +// which indices to adjust. Have to call that separately. +// Note 2: for simplicity sake, we only do this for groups with ONE interleaved +// cell. Doing this for multiple separated cells would be, literally, hell. + +void plGBufferGroup::DeleteVertsFromStorage( UInt32 which, UInt32 start, UInt32 length ) +{ + UInt8 *dstPtr, *srcPtr; + UInt32 amount; + + + hsAssert( fCells[ which ]->GetCount() == 1, "Cannot delete verts on a mixed buffer group" ); + + // Adjust cell 0 + (*fCells[ which ])[ 0 ].fLength -= length; + + start *= fStride; + length *= fStride; + + if( start + length < fVertBuffSizes[ which ] ) + { + dstPtr = &( fVertBuffStorage[ which ][ start ] ); + srcPtr = &( fVertBuffStorage[ which ][ start + length ] ); + + amount = ( fVertBuffSizes[ which ] ) - ( start + length ); + + memmove( dstPtr, srcPtr, amount ); + } + + fVertBuffSizes[ which ] -= length; + plProfile_DelMem(MemBufGrpVertex, length); + + if( fVertexBufferRefs.GetCount() > which && fVertexBufferRefs[ which ] != nil ) + { + hsRefCnt_SafeUnRef(fVertexBufferRefs[which]); + fVertexBufferRefs[which] = nil; + } + +} + +//// AdjustIndicesInStorage /////////////////////////////////////////////////// +// Adjusts indices >= a given threshold by a given delta. Use it to adjust +// indices after vertex deletion. Affects only the given buffer. + +void plGBufferGroup::AdjustIndicesInStorage( UInt32 which, UInt16 threshhold, Int16 delta ) +{ + int i; + + + for( i = 0; i < fIdxBuffCounts[ which ]; i++ ) + { + if( fIdxBuffStorage[ which ][ i ] >= threshhold ) + fIdxBuffStorage[ which ][ i ] += delta; + } + + if( fIndexBufferRefs.GetCount() > which && fIndexBufferRefs[ which ] != nil ) + fIndexBufferRefs[ which ]->SetDirty( true ); + +} + +//// DeleteIndicesFromStorage ///////////////////////////////////////////////// +// Deletes a span of indices from the index storage. Remember to Prep this +// group after doing this! + +void plGBufferGroup::DeleteIndicesFromStorage( UInt32 which, UInt32 start, UInt32 length ) +{ + UInt16 *dstPtr, *srcPtr; + UInt32 amount; + + + hsAssert( start + length <= fIdxBuffCounts[ which ], "Illegal range to DeleteIndicesFromStorage()" ); + + if( start + length < fIdxBuffCounts[ which ] ) + { + dstPtr = &( fIdxBuffStorage[ which ][ start ] ); + srcPtr = &( fIdxBuffStorage[ which ][ start + length ] ); + + amount = fIdxBuffCounts[ which ] - ( start + length ); + + memmove( dstPtr, srcPtr, amount * sizeof( UInt16 ) ); + } + + fIdxBuffCounts[ which ] -= length; + plProfile_DelMem(MemBufGrpIndex, length * sizeof(UInt16)); + + if( fIndexBufferRefs.GetCount() > which && fIndexBufferRefs[ which ] != nil ) + { + hsRefCnt_SafeUnRef(fIndexBufferRefs[which]); + fIndexBufferRefs[which] = nil; + } + +} + +//// GetNumPrimaryVertsLeft /////////////////////////////////////////////////// +// Base on the cells, so we can take instanced cells into account + +UInt32 plGBufferGroup::GetNumPrimaryVertsLeft( void ) const +{ + return GetNumVertsLeft( 0 ); +} + +//// GetNumVertsLeft ////////////////////////////////////////////////////////// +// Base on the cells, so we can take instanced cells into account + +UInt32 plGBufferGroup::GetNumVertsLeft( UInt32 idx ) const +{ + if( idx >= fCells.GetCount() ) + return kMaxNumVertsPerBuffer; + + UInt32 i, total = kMaxNumVertsPerBuffer; + + + for( i = 0; i < fCells[ idx ]->GetCount(); i++ ) + total -= (*fCells[ idx ])[ i ].fLength; + + return total; +} + +//// IMakeCell //////////////////////////////////////////////////////////////// +// Get a cell from the given cell array. + +UInt32 plGBufferGroup::IMakeCell( UInt32 vbIndex, UInt8 flags, UInt32 vStart, UInt32 cStart, UInt32 len, UInt32 *offset ) +{ + hsTArray *cells = fCells[ vbIndex ]; + + + if( !(flags & kReserveInterleaved) ) + { + /// Note that there are THREE types of cells: interleaved (colorStart == -1), + /// first of an instance group (colorStart != -1 && vStart != -1) and + /// an instance (colorStart != -1 && vStart == -1 ). To simplify things, + /// we never merge any separated cells + + if( flags & kReserveSeparated ) + cells->Append( plGBufferCell( vStart, cStart, len ) ); + else + cells->Append( plGBufferCell( (UInt32)-1, cStart, len ) ); + *offset = 0; + } + else + { + /// Merge if the last cell was an interleaved cell + if( cells->GetCount() > 0 && (*cells)[ cells->GetCount() - 1 ].fColorStart == (UInt32)-1 ) + { + *offset = (*cells)[ cells->GetCount() - 1 ].fLength; + (*cells)[ cells->GetCount() - 1 ].fLength += len; + } + else + { + cells->Append( plGBufferCell( vStart, (UInt32)-1, len ) ); + *offset = 0; + } + } + + return cells->GetCount() - 1; +} + +//// ReserveVertStorage /////////////////////////////////////////////////////// +// Takes a length to reserve in a vertex buffer and returns the buffer index +// and starting position. Basically does what AppendToVertStorage() used to +// do except it doesn't actually copy any data into the space reserved. + +hsBool plGBufferGroup::ReserveVertStorage( UInt32 numVerts, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset, UInt8 flags ) +{ + UInt8 *storagePtr = nil; + UInt32 cStartIdx = 0, vStartIdx = 0; + plGBufferColor *cStoragePtr = nil; + int i; + + + if( numVerts >= kMaxNumVertsPerBuffer ) + { + hsAssert( false, "Egad, why on earth are you adding that many verts???" ); + return false; + } + + /// Find a spot + if( !(flags & kReserveIsolate) ) + { + for( i = 0; i < fVertBuffStorage.GetCount(); i++ ) + { + if( GetNumVertsLeft( i ) >= numVerts ) + break; + } + } + else + { + i = fVertBuffStorage.GetCount(); + } + if( i == fVertBuffStorage.GetCount() ) + { + if( (flags & kReserveInterleaved) || (flags & kReserveSeparated) ) + { + fVertBuffStorage.Append( nil ); + fVertBuffSizes.Append( 0 ); + } + fVertBuffStarts.Append(0); + fVertBuffEnds.Append(-1); + + fColorBuffStorage.Append( nil ); + fColorBuffCounts.Append( 0 ); + + fCells.Append( TRACKED_NEW hsTArray ); + } + + *vbIndex = i; + + if( !(flags & kReserveInterleaved) ) + { + // Splitting the data into vertex and color storage + if( flags & kReserveSeparated ) + { + /// Increase the storage size + vStartIdx = fVertBuffSizes[ i ]; + storagePtr = TRACKED_NEW UInt8[ fVertBuffSizes[ i ] + numVerts * fLiteStride ]; + if( fVertBuffSizes[ i ] > 0 ) + memcpy( storagePtr, fVertBuffStorage[ i ], fVertBuffSizes[ i ] ); + fVertBuffSizes[ i ] += numVerts * fLiteStride; + plProfile_NewMem(MemBufGrpVertex, numVerts * fLiteStride); + } + + /// Color too + cStartIdx = fColorBuffCounts[ i ]; + cStoragePtr = TRACKED_NEW plGBufferColor[ fColorBuffCounts[ i ] + numVerts ]; + if( fColorBuffCounts[ i ] > 0 ) + memcpy( cStoragePtr, fColorBuffStorage[ i ], fColorBuffCounts[ i ] * sizeof( plGBufferColor ) ); + } + else + { + // Interleaved + + /// Increase the storage size + vStartIdx = fVertBuffSizes[ i ]; + storagePtr = TRACKED_NEW UInt8[ fVertBuffSizes[ i ] + numVerts * fStride ]; + if( fVertBuffSizes[ i ] > 0 ) + memcpy( storagePtr, fVertBuffStorage[ i ], fVertBuffSizes[ i ] ); + fVertBuffSizes[ i ] += numVerts * fStride; + plProfile_NewMem(MemBufGrpVertex, numVerts * fStride); + } + + /// Switch over + if( storagePtr != nil ) + { + if( fVertBuffStorage[ i ] != nil ) + delete [] fVertBuffStorage[ i ]; + fVertBuffStorage[ i ] = storagePtr; + } + if( cStoragePtr != nil ) + { + if( fColorBuffStorage[ i ] != nil ) + delete [] fColorBuffStorage[ i ]; + fColorBuffStorage[ i ] = cStoragePtr; + fColorBuffCounts[ i ] += numVerts; + plProfile_NewMem(MemBufGrpVertex, numVerts * sizeof(plGBufferColor)); + } + + if( fVertexBufferRefs.GetCount() > i && fVertexBufferRefs[ i ] != nil ) + { + hsRefCnt_SafeUnRef(fVertexBufferRefs[i]); + fVertexBufferRefs[i] = nil; + } + + /// Append a cell entry + *cell = IMakeCell( i, flags, vStartIdx, cStartIdx, numVerts, offset ); + + /// All done! + return true; +} + +//// AppendToVertStorage ////////////////////////////////////////////////////// +// Given an opaque array of vertex data, puts it into the first available spot +// in fVertStorage. If there is no array on fVertStorage that can hold them, +// we create a new one. Returns the index of the storage array and the +// starting index into the array. Note that we basically stick it wherever +// we can, instead of at the end. +// Updated 4.30.2001 mcn to take in a plGeometrySpan as a source rather than +// raw data, since the plGeometrySpan no longer stores data in exactly the +// same format. +// Updated 6.15.2001 mcn to use ReserveVertStorage(). + +void plGBufferGroup::AppendToVertStorage( plGeometrySpan *srcSpan, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset ) +{ + if( !ReserveVertStorage( srcSpan->fNumVerts, vbIndex, cell, offset, kReserveInterleaved ) ) + return; + + StuffToVertStorage( srcSpan, *vbIndex, *cell, *offset, kReserveInterleaved ); +} + +//// AppendToVertAndColorStorage ////////////////////////////////////////////// +// Same as AppendToVertStorage(), but writes only the verts to the vertex +// storage and the colors to the separate color storage. + +void plGBufferGroup::AppendToVertAndColorStorage( plGeometrySpan *srcSpan, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset ) +{ + if( !ReserveVertStorage( srcSpan->fNumVerts, vbIndex, cell, offset, kReserveSeparated ) ) + return; + + StuffToVertStorage( srcSpan, *vbIndex, *cell, *offset, kReserveSeparated ); +} + +//// AppendToColorStorage ///////////////////////////////////////////////////// +// Same as AppendToVertStorage(), but writes JUST to color storage. + +void plGBufferGroup::AppendToColorStorage( plGeometrySpan *srcSpan, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset, UInt32 origCell ) +{ + if( !ReserveVertStorage( srcSpan->fNumVerts, vbIndex, cell, offset, kReserveColors ) ) + return; + + (*fCells[ *vbIndex ])[ *cell ].fVtxStart = (*fCells[ *vbIndex ])[ origCell ].fVtxStart; + + StuffToVertStorage( srcSpan, *vbIndex, *cell, *offset, kReserveColors ); +} + +//// IGetStartVtxPointer ////////////////////////////////////////////////////// +// Get the start vertex and color buffer pointers for a given cell and offset + +void plGBufferGroup::IGetStartVtxPointer( UInt32 vbIndex, UInt32 cell, UInt32 offset, UInt8 *&tempPtr, plGBufferColor *&cPtr ) +{ + hsAssert( vbIndex < fVertBuffStorage.GetCount(), "Invalid vbIndex in StuffToVertStorage()" ); + hsAssert( cell < fCells[ vbIndex ]->GetCount(), "Invalid cell in StuffToVertStorage()" ); + + tempPtr = fVertBuffStorage[ vbIndex ]; + cPtr = fColorBuffStorage[ vbIndex ]; + + tempPtr += (*fCells[ vbIndex ])[ cell ].fVtxStart; + cPtr += (*fCells[ vbIndex ])[ cell ].fColorStart; + + if( offset > 0 ) + { + tempPtr += offset * ( ( (*fCells[ vbIndex ])[ cell ].fColorStart == (UInt32)-1 ) ? fStride : fLiteStride ); + cPtr += offset; + } +} + +//// GetVertBufferCount /////////////////////////////////////////////////////// + +UInt32 plGBufferGroup::GetVertBufferCount( UInt32 idx ) const +{ + return GetVertStartFromCell( idx, fCells[ idx ]->GetCount(), 0 ); +} + +//// GetVertStartFromCell ///////////////////////////////////////////////////// + +UInt32 plGBufferGroup::GetVertStartFromCell( UInt32 vbIndex, UInt32 cell, UInt32 offset ) const +{ + UInt32 i, numVerts; + + + hsAssert( vbIndex < fVertBuffStorage.GetCount(), "Invalid vbIndex in StuffToVertStorage()" ); + hsAssert( cell <= fCells[ vbIndex ]->GetCount(), "Invalid cell in StuffToVertStorage()" ); + + numVerts = 0; + for( i = 0; i < cell; i++ ) + numVerts += (*fCells[ vbIndex ])[ i ].fLength; + + numVerts += offset; + + return numVerts; +} + +//// StuffToVertStorage /////////////////////////////////////////////////////// +// Stuffs data from a plGeometrySpan into the specified location in the +// specified vertex buffer. + +void plGBufferGroup::StuffToVertStorage( plGeometrySpan *srcSpan, UInt32 vbIndex, UInt32 cell, UInt32 offset, UInt8 flags ) +{ + UInt8 *tempPtr, stride; + plGBufferColor *cPtr; + int i, j, numVerts; + plGBufferCell *cellPtr; + + + hsAssert( vbIndex < fVertBuffStorage.GetCount(), "Invalid vbIndex in StuffToVertStorage()" ); + hsAssert( cell < fCells[ vbIndex ]->GetCount(), "Invalid cell in StuffToVertStorage()" ); + + IGetStartVtxPointer( vbIndex, cell, offset, tempPtr, cPtr ); + cellPtr = &(*fCells[ vbIndex ])[ cell ]; + stride = ( cellPtr->fColorStart != (UInt32)-1 ) ? fLiteStride : fStride; + + numVerts = srcSpan->fNumVerts; + + /// Copy the data over + for( i = 0; i < numVerts; i++ ) + { + hsPoint3 pos; + float weights[ 3 ]; + UInt32 weightIndices; + hsVector3 norm; + UInt32 color, specColor; + hsPoint3 uvs[ plGeometrySpan::kMaxNumUVChannels ]; + float *fPtr; + UInt32 *dPtr; + + + // Gotta swap the data around, since plGeometrySpans store the data slightly differently + if( flags & kReserveColors ) + { + /// Just do colors + srcSpan->ExtractVertex( i, &pos, &norm, &color, &specColor ); + + cPtr->fDiffuse = color; + cPtr->fSpecular = specColor; + } + else + { + /// Do verts, possibly colors as well + srcSpan->ExtractVertex( i, &pos, &norm, &color, &specColor ); + if( ( fFormat & kSkinWeightMask ) != kSkinNoWeights ) + srcSpan->ExtractWeights( i, weights, &weightIndices ); + for( j = 0; j < GetNumUVs(); j++ ) + srcSpan->ExtractUv( i, j, &uvs[ j ] ); + + // Stuff it in now + fPtr = (float *)tempPtr; + fPtr[ 0 ] = pos.fX; + fPtr[ 1 ] = pos.fY; + fPtr[ 2 ] = pos.fZ; + fPtr += 3; + + if( fNumSkinWeights > 0 ) + { + for( j = 0; j < fNumSkinWeights; j++ ) + { + *fPtr = weights[ j ]; + fPtr++; + } + + if( fNumSkinWeights > 1 ) + { + dPtr = (UInt32 *)fPtr; + *dPtr = weightIndices; + + dPtr++; + fPtr = (float *)dPtr; + } + } + + fPtr[ 0 ] = norm.fX; + fPtr[ 1 ] = norm.fY; + fPtr[ 2 ] = norm.fZ; + fPtr += 3; + + if( flags & kReserveInterleaved ) + { + dPtr = (UInt32 *)fPtr; + dPtr[ 0 ] = color; + dPtr[ 1 ] = specColor; + dPtr += 2; + fPtr = (float *)dPtr; + } + else + { + cPtr->fDiffuse = color; + cPtr->fSpecular = specColor; + } + + for( j = 0; j < GetNumUVs(); j++ ) + { + fPtr[ 0 ] = uvs[ j ].fX; + fPtr[ 1 ] = uvs[ j ].fY; + fPtr[ 2 ] = uvs[ j ].fZ; + fPtr += 3; + } + } + + tempPtr += stride; + cPtr++; + } + + if( ( vbIndex < fVertexBufferRefs.GetCount() ) && fVertexBufferRefs[ vbIndex ] ) + fVertexBufferRefs[ vbIndex ]->SetDirty( true ); +} + + +//// ReserveIndexStorage ////////////////////////////////////////////////////// +// Same as ReserveVertStorage(), only for indices :) + +hsBool plGBufferGroup::ReserveIndexStorage( UInt32 numIndices, UInt32 *ibIndex, UInt32 *ibStart, UInt16 **dataPtr ) +{ + UInt16 *storagePtr; + int i; + + + if( numIndices >= kMaxNumIndicesPerBuffer ) + { + hsAssert( false, "Egad, why on earth are you adding that many indices???" ); + return false; + } + + /// Find a spot + for( i = 0; i < fIdxBuffStorage.GetCount(); i++ ) + { + + if( fIdxBuffCounts[ i ] + numIndices < kMaxNumIndicesPerBuffer ) + break; + } + if( i == fIdxBuffStorage.GetCount() ) + { + fIdxBuffStorage.Append( nil ); + fIdxBuffCounts.Append( 0 ); + + fIdxBuffStarts.Append(0); + fIdxBuffEnds.Append(-1); + } + + *ibIndex = i; + *ibStart = fIdxBuffCounts[ i ]; + + /// Increase the storage size + storagePtr = TRACKED_NEW UInt16[ fIdxBuffCounts[ i ] + numIndices ]; + if( fIdxBuffCounts[ i ] > 0 ) + memcpy( storagePtr, fIdxBuffStorage[ i ], fIdxBuffCounts[ i ] * sizeof( UInt16 ) ); + + if( dataPtr != nil ) + *dataPtr = storagePtr + fIdxBuffCounts[ i ]; + + /// Switch over + i = *ibIndex; + if( fIdxBuffStorage[ i ] != nil ) + delete [] fIdxBuffStorage[ i ]; + fIdxBuffStorage[ i ] = storagePtr; + fIdxBuffCounts[ i ] += numIndices; + plProfile_NewMem(MemBufGrpIndex, numIndices * sizeof(UInt16)); + + /// All done! + if( fIndexBufferRefs.GetCount() > i && fIndexBufferRefs[ i ] != nil ) + { + hsRefCnt_SafeUnRef(fIndexBufferRefs[i]); + fIndexBufferRefs[i] = nil; + } + + return true; +} + +//// AppendToIndexStorage ///////////////////////////////////////////////////// +// Same as AppendToVertStorage, only for the index buffers and indices. Duh :) + +void plGBufferGroup::AppendToIndexStorage( UInt32 numIndices, UInt16 *data, UInt32 addToAll, + UInt32 *ibIndex, UInt32 *ibStart ) +{ + UInt16 *tempPtr; + int i; + + + if( !ReserveIndexStorage( numIndices, ibIndex, ibStart, &tempPtr ) ) + return; + + /// Copy new indices over, offsetting them as we were told to + for( i = 0; i < numIndices; i++ ) + tempPtr[ i ] = data[ i ] + (UInt16)addToAll; + + /// All done! +} + +//// ConvertToTriList ///////////////////////////////////////////////////////// +// Returns an array of plGBufferTriangles representing the span of indices +// specified. + +plGBufferTriangle *plGBufferGroup::ConvertToTriList( Int16 spanIndex, UInt32 whichIdx, UInt32 whichVtx, UInt32 whichCell, UInt32 start, UInt32 numTriangles ) +{ + plGBufferTriangle *array; + UInt16 *storagePtr; + UInt8 *vertStgPtr, stride; + float *vertPtr; + int i, j; + hsPoint3 center; + UInt32 offsetBy; + plGBufferColor *wastePtr; + + + /// Sanity checks + hsAssert( whichIdx < fIdxBuffStorage.GetCount(), "Invalid index buffer ID to ConvertToTriList()" ); + hsAssert( whichVtx < fVertBuffStorage.GetCount(), "Invalid vertex buffer ID to ConvertToTriList()" ); + hsAssert( start < fIdxBuffCounts[ whichIdx ], "Invalid start index to ConvertToTriList()" ); + hsAssert( start + numTriangles * 3 <= fIdxBuffCounts[ whichIdx ], "Invalid count to ConvertToTriList()" ); + hsAssert( whichCell < fCells[ whichVtx ]->GetCount(), "Invalid cell to ConvertToTriList()" ); + + /// Create the array and fill it + array = TRACKED_NEW plGBufferTriangle[ numTriangles ]; + hsAssert( array != nil, "Not enough memory to create triangle data in ConvertToTriList()" ); + + storagePtr = fIdxBuffStorage[ whichIdx ]; + IGetStartVtxPointer( whichVtx, whichCell, 0, vertStgPtr, wastePtr ); + offsetBy = GetVertStartFromCell( whichVtx, whichCell, 0 ); + stride = ( (*fCells[ whichVtx ])[ whichCell ].fColorStart == (UInt32)-1 ) ? fStride : fLiteStride; + + for( i = 0, j = 0; i < numTriangles; i++, j += 3 ) + { + center.fX = center.fY = center.fZ = 0; + + vertPtr = (float *)( vertStgPtr + stride * ( storagePtr[ start + j + 0 ] - offsetBy ) ); + + center.fX += vertPtr[ 0 ]; + center.fY += vertPtr[ 1 ]; + center.fZ += vertPtr[ 2 ]; + + vertPtr = (float *)( vertStgPtr + stride * ( storagePtr[ start + j + 1 ] - offsetBy ) ); + + center.fX += vertPtr[ 0 ]; + center.fY += vertPtr[ 1 ]; + center.fZ += vertPtr[ 2 ]; + + vertPtr = (float *)( vertStgPtr + stride * ( storagePtr[ start + j + 2 ] - offsetBy ) ); + + center.fX += vertPtr[ 0 ]; + center.fY += vertPtr[ 1 ]; + center.fZ += vertPtr[ 2 ]; + + center.fX /= 3.0f; + center.fY /= 3.0f; + center.fZ /= 3.0f; + + array[ i ].fSpanIndex = spanIndex; + array[ i ].fIndex1 = storagePtr[ start + j + 0 ]; + array[ i ].fIndex2 = storagePtr[ start + j + 1 ]; + array[ i ].fIndex3 = storagePtr[ start + j + 2 ]; + array[ i ].fCenter = center; + } + + /// All done! + return array; +} + +//// StuffFromTriList ///////////////////////////////////////////////////////// +// Stuffs the indices from an array of plGBufferTriangles into the index +// storage. + +void plGBufferGroup::StuffFromTriList( UInt32 which, UInt32 start, UInt32 numTriangles, UInt16 *data ) +{ + UInt16 *storagePtr; + + + /// Sanity checks + hsAssert( which < fIdxBuffStorage.GetCount(), "Invalid index buffer ID to StuffFromTriList()" ); + hsAssert( start < fIdxBuffCounts[ which ], "Invalid start index to StuffFromTriList()" ); + hsAssert( start + numTriangles * 3 <= fIdxBuffCounts[ which ], "Invalid count to StuffFromTriList()" ); + + + /// This is easy--just stuff! + storagePtr = fIdxBuffStorage[ which ]; +#define MF_SPEED_THIS_UP +#ifndef MF_SPEED_THIS_UP + int i, j; + for( i = 0, j = 0; i < numTriangles; i++, j += 3 ) + { + storagePtr[ start + j ] = data[ i ].fIndex1; + storagePtr[ start + j + 1 ] = data[ i ].fIndex2; + storagePtr[ start + j + 2 ] = data[ i ].fIndex3; + } +#else // MF_SPEED_THIS_UP + memcpy( storagePtr + start, data, numTriangles * 3 * sizeof(*data) ); +#endif // MF_SPEED_THIS_UP + + /// All done! Just make sure we refresh before we render... + if( fIndexBufferRefs.GetCount() > which && fIndexBufferRefs[ which ] != nil ) + fIndexBufferRefs[ which ]->SetDirty( true ); + +} + +//// StuffTri ///////////////////////////////////////////////////////////////// + +void plGBufferGroup::StuffTri( UInt32 iBuff, UInt32 iTri, UInt16 idx0, UInt16 idx1, UInt16 idx2 ) +{ + /// Sanity checks + hsAssert( iBuff < fIdxBuffStorage.GetCount(), "Invalid index buffer ID to StuffFromTriList()" ); + hsAssert( iTri < fIdxBuffCounts[ iBuff ], "Invalid start index to StuffFromTriList()" ); + + fIdxBuffStorage[ iBuff ][ iTri + 0 ] = idx0; + fIdxBuffStorage[ iBuff ][ iTri + 1 ] = idx1; + fIdxBuffStorage[ iBuff ][ iTri + 2 ] = idx2; + +} + +//// Accessor Functions /////////////////////////////////////////////////////// + +hsPoint3 &plGBufferGroup::Position( int iBuff, UInt32 cell, int iVtx ) +{ + UInt8 *vertStgPtr; + plGBufferColor *cPtr; + + IGetStartVtxPointer( iBuff, cell, iVtx, vertStgPtr, cPtr ); + + return *(hsPoint3 *)( vertStgPtr + 0 ); +} + +hsVector3 &plGBufferGroup::Normal( int iBuff, UInt32 cell, int iVtx ) +{ + UInt8 *vertStgPtr; + plGBufferColor *cPtr; + + IGetStartVtxPointer( iBuff, cell, iVtx, vertStgPtr, cPtr ); + + return *(hsVector3 *)( vertStgPtr + sizeof( hsPoint3 ) ); +} + +UInt32 &plGBufferGroup::Color( int iBuff, UInt32 cell, int iVtx ) +{ + UInt8 *vertStgPtr; + plGBufferColor *cPtr; + + IGetStartVtxPointer( iBuff, cell, iVtx, vertStgPtr, cPtr ); + + if( (*fCells[ iBuff ])[ cell ].fColorStart != (UInt32)-1 ) + return *(UInt32 *)( &cPtr->fDiffuse ); + else + return *(UInt32 *)( vertStgPtr + 2 * sizeof( hsPoint3 ) ); +} + +UInt32 &plGBufferGroup::Specular( int iBuff, UInt32 cell, int iVtx ) +{ + UInt8 *vertStgPtr; + plGBufferColor *cPtr; + + IGetStartVtxPointer( iBuff, cell, iVtx, vertStgPtr, cPtr ); + + if( (*fCells[ iBuff ])[ cell ].fColorStart != (UInt32)-1 ) + return *(UInt32 *)( &cPtr->fSpecular ); + else + return *(UInt32 *)( vertStgPtr + 2 * sizeof( hsPoint3 ) ); +} + +hsPoint3 &plGBufferGroup::UV( int iBuff, UInt32 cell, int iVtx, int channel ) +{ + UInt8 *vertStgPtr; + plGBufferColor *cPtr; + + IGetStartVtxPointer( iBuff, cell, iVtx, vertStgPtr, cPtr ); + + vertStgPtr += 2 * sizeof( hsPoint3 ) + channel * sizeof( hsPoint3 ); + + if( (*fCells[ iBuff ])[ cell ].fColorStart != (UInt32)-1 ) + return *(hsPoint3 *)( vertStgPtr ); + else + return *(hsPoint3 *)( vertStgPtr + 2 * sizeof( UInt32 ) ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plGBufferGroup.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plGBufferGroup.h new file mode 100644 index 00000000..bd5633c6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plGBufferGroup.h @@ -0,0 +1,292 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plGBufferGroup Class Header // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 2.21.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plGBufferGroup_h +#define _plGBufferGroup_h + +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "hsColorRGBA.h" + +//// plGBufferTriangle Struct Definition ////////////////////////////////////// +// +// Represents a single triangle inside a plGBufferGroup, which consists of +// three indices (the indices of the three vertices) and a 3-D point +// representing the center of the triangle. + +class plGBufferTriangle +{ + public: + UInt16 fIndex1, fIndex2, fIndex3, fSpanIndex; + hsPoint3 fCenter; + + void Read( hsStream *s ); + void Write( hsStream *s ); +}; + +//// plGBufferCell and plGBufferColor Definitions ///////////////////////////// + +class plGBufferCell +{ + public: + UInt32 fVtxStart; // In bytes + UInt32 fColorStart; // In bytes + UInt32 fLength; + + plGBufferCell( UInt32 vStart, UInt32 cStart, UInt32 len ) + { + fVtxStart = vStart; fColorStart = cStart; fLength = len; + } + + plGBufferCell() {} + + void Read( hsStream *s ); + void Write( hsStream *s ); +}; + +class plGBufferColor +{ + public: + UInt32 fDiffuse, fSpecular; +}; + +//// plGBufferGroup Class Definition ////////////////////////////////////////// +// +// Represents a list of vertex and index buffers in a nice package. + +class hsStream; +class hsResMgr; +class plPipeline; +class hsGDeviceRef; +class plGeometrySpan; + +class plGBufferGroup +{ + protected: + UInt8 fFormat; + UInt8 fStride; + UInt8 fLiteStride; + UInt8 fNumSkinWeights; + UInt32 fNumVerts; + UInt32 fNumIndices; + hsBool fVertsVolatile; + hsBool fIdxVolatile; + int fLOD; + + hsTArray fVertexBufferRefs; + hsTArray fIndexBufferRefs; + + hsTArray fVertBuffSizes; + hsTArray fIdxBuffCounts; + hsTArray fColorBuffCounts; + hsTArray fVertBuffStorage; + hsTArray fIdxBuffStorage; + + hsTArray fVertBuffStarts; + hsTArray fVertBuffEnds; + hsTArray fIdxBuffStarts; + hsTArray fIdxBuffEnds; + + hsTArray fColorBuffStorage; + + hsTArray *> fCells; + + virtual void ISendStorageToBuffers( plPipeline *pipe, hsBool adjustForNvidiaLighting ); + + UInt8 ICalcVertexSize( UInt8 &liteStride ); + + UInt8* IVertBuffStorage(int iBuff, int iVtx) const { return fVertBuffStorage[iBuff] + iVtx*fStride; } + + UInt32 IMakeCell( UInt32 vbIndex, UInt8 flags, UInt32 vStart, UInt32 cStart, UInt32 len, UInt32 *offset ); + void IGetStartVtxPointer( UInt32 vbIndex, UInt32 cell, UInt32 offset, UInt8 *&tempPtr, plGBufferColor *&cPtr ); + + public: + + static const UInt32 kMaxNumVertsPerBuffer; + static const UInt32 kMaxNumIndicesPerBuffer; + + enum Formats + { + kUVCountMask = 0x0f, // Problem is, we need enough bits to store the max #, which means + // we really want ( max # << 1 ) - 1 + + kSkinNoWeights = 0x00, // 0000000 + kSkin1Weight = 0x10, // 0010000 + kSkin2Weights = 0x20, // 0100000 + kSkin3Weights = 0x30, // 0110000 + kSkinWeightMask = 0x30, // 0110000 + + kSkinIndices = 0x40, // 1000000 + + kEncoded = 0x80 + }; + + enum + { + kReserveInterleaved = 0x01, + kReserveVerts = 0x02, + kReserveColors = 0x04, + kReserveSeparated = 0x08, + kReserveIsolate = 0x10 + }; + + plGBufferGroup(UInt8 format, hsBool vertsVolatile, hsBool idxVolatile, int LOD = 0); + ~plGBufferGroup(); + + UInt8 GetNumUVs( void ) const { return ( fFormat & kUVCountMask ); } + UInt8 GetNumWeights() const { return (fFormat & kSkinWeightMask) >> 4; } + + static UInt8 CalcNumUVs( UInt8 format ) { return ( format & kUVCountMask ); } + static UInt8 UVCountToFormat( UInt8 numUVs ) { return numUVs & kUVCountMask; } + + void DirtyVertexBuffer(int i); + void DirtyIndexBuffer(int i); + hsBool VertexReady(int i) const { return (i < fVertexBufferRefs.GetCount()) && fVertexBufferRefs[i]; } + hsBool IndexReady(int i) const { return (i < fIndexBufferRefs.GetCount()) && fIndexBufferRefs[i]; } + UInt8 GetVertexSize( void ) const { return fStride; } + UInt8 GetVertexLiteStride( void ) const { return fLiteStride; } + UInt8 GetVertexFormat( void ) const { return fFormat; } + UInt32 GetMemUsage( void ) const { return ( fNumVerts * GetVertexSize() ) + ( fNumIndices * sizeof( UInt16 ) ); } + UInt32 GetNumVerts( void ) const { return fNumVerts; } + UInt32 GetNumIndices( void ) const { return fNumIndices; } + UInt32 GetNumPrimaryVertsLeft( void ) const; + UInt32 GetNumVertsLeft( UInt32 idx ) const; + + UInt32 GetVertBufferSize(UInt32 idx) const { return fVertBuffSizes[idx]; } + UInt32 GetVertBufferCount(UInt32 idx) const; + UInt32 GetIndexBufferCount(UInt32 idx) const { return fIdxBuffCounts[idx]; } + UInt32 GetVertStartFromCell(UInt32 idx, UInt32 cell, UInt32 offset) const; + + // These should only be called by the pipeline, because only it knows when it's safe. + // If the data is volatile, these are no-ops + void PurgeVertBuffer(UInt32 idx); + void PurgeIndexBuffer(UInt32 idx); + + /////////////////////////////////////////////////////////////////////////////// + // The following group of functions is an advanced optimization, and a pretty + // specialized one at that. It just limits the amount of data that will get + // uploaded to video. If you don't know you are limited by bandwidth to the + // board, or you just don't know what your are doing, don't mess with them. + // If you never touch them, everything will work. If you set them correcly, + // things may work faster. If you set them incorrectly, be sure to save + // all files before running. + // All of these are indices, not bytes. from the beginning of the buffer. + UInt32 GetVertBufferStart(UInt32 idx) const { return fVertBuffStarts[idx]; } + UInt32 GetVertBufferEnd(UInt32 idx) const { return fVertBuffEnds[idx] >= 0 ? UInt32(fVertBuffEnds[idx]) : GetVertBufferCount(idx); } + UInt32 GetIndexBufferStart(UInt32 idx) const { return fIdxBuffStarts[idx]; } + UInt32 GetIndexBufferEnd(UInt32 idx) const { return fIdxBuffEnds[idx] >= 0 ? UInt32(fIdxBuffEnds[idx]) : GetIndexBufferCount(idx); } + + void SetVertBufferStart(UInt32 idx, UInt32 s) { fVertBuffStarts[idx] = s; } + void SetVertBufferEnd(UInt32 idx, UInt32 e) { fVertBuffEnds[idx] = e; } + void SetIndexBufferStart(UInt32 idx, UInt32 s) { fIdxBuffStarts[idx] = s; } + void SetIndexBufferEnd(UInt32 idx, UInt32 e) { fIdxBuffEnds[idx] = e; } + /////////////////////////////////////////////////////////////////////////////// + + UInt32 GetNumVertexBuffers( void ) const { return fVertBuffStorage.GetCount(); } + UInt32 GetNumIndexBuffers( void ) const { return fIdxBuffStorage.GetCount(); } + + UInt8 *GetVertBufferData( UInt32 idx ) { return fVertBuffStorage[ idx ]; } + UInt16 *GetIndexBufferData( UInt32 idx ) { return fIdxBuffStorage[ idx ]; } + plGBufferColor *GetColorBufferData( UInt32 idx ) { return fColorBuffStorage[ idx ]; } + + hsGDeviceRef *GetVertexBufferRef( UInt32 i ); + hsGDeviceRef *GetIndexBufferRef( UInt32 i ); + + UInt32 GetNumCells( UInt32 idx ) const { return fCells[ idx ]->GetCount(); } + plGBufferCell *GetCell( UInt32 idx, UInt32 cell ) { return &( (*fCells[ idx ])[ cell ] ); } + + void SetVertexBufferRef( UInt32 index, hsGDeviceRef *vb ); + void SetIndexBufferRef( UInt32 index, hsGDeviceRef *ib ); + + virtual void Read( hsStream* s ); + virtual void Write( hsStream* s ); + + // Accessor functions + hsPoint3 &Position( int iBuff, UInt32 cell, int iVtx ); + hsVector3 &Normal( int iBuff, UInt32 cell, int iVtx ); + UInt32 &Color( int iBuff, UInt32 cell, int iVtx ); + UInt32 &Specular( int iBuff, UInt32 cell, int iVtx ); + hsPoint3 &UV( int iBuff, UInt32 cell, int iVtx, int channel ); + UInt32 Format() const { return fFormat; } + + // Take temp accumulators and actually build buffer data from them + void TidyUp( void ); + + // Delete the buffer data storage + void CleanUp( void ); + + // Take buffer data and convert it to device-specific buffers + void PrepForRendering( plPipeline *pipe, hsBool adjustForNvidiaLighting ); + + // Reserves space in a vertex buffer + hsBool ReserveVertStorage( UInt32 numVerts, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset, UInt8 flags ); + + // Append vertex data to the first available storage buffer + void AppendToVertStorage( plGeometrySpan *srcSpan, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset ); + void AppendToVertAndColorStorage( plGeometrySpan *srcSpan, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset ); + void AppendToColorStorage( plGeometrySpan *srcSpan, UInt32 *vbIndex, UInt32 *cell, UInt32 *offset, UInt32 origCell ); + + // Reserves space in an index buffer + hsBool ReserveIndexStorage( UInt32 numIndices, UInt32 *ibIndex, UInt32 *ibStart, UInt16 **dataPtr = nil ); + + // Append index data to the first available storage buffer + void AppendToIndexStorage( UInt32 numIndices, UInt16 *data, UInt32 addToAll, UInt32 *ibIndex, UInt32 *ibStart ); + + + /// Dynamic functions (addition/deletion of raw data) + void DeleteVertsFromStorage( UInt32 which, UInt32 start, UInt32 length ); + void AdjustIndicesInStorage( UInt32 which, UInt16 threshhold, Int16 delta ); + void DeleteIndicesFromStorage( UInt32 which, UInt32 start, UInt32 length ); + + // Returns an array of plGBufferTriangles representing the span of indices specified + plGBufferTriangle *ConvertToTriList( Int16 spanIndex, UInt32 whichIdx, UInt32 whichVtx, UInt32 whichCell, UInt32 start, UInt32 numTriangles ); + + // Stuffs the indices from an array of plGBufferTriangles into the index storage + void StuffFromTriList( UInt32 which, UInt32 start, UInt32 numTriangles, UInt16 *data ); + void StuffTri( UInt32 iBuff, UInt32 iTri, UInt16 idx0, UInt16 idx1, UInt16 idx2 ); + + // Stuff the data from a geometry span into vertex storage + void StuffToVertStorage( plGeometrySpan *srcSpan, UInt32 vbIndex, UInt32 cell, UInt32 offset, UInt8 flags ); + + // Are our verts volatile? + hsBool AreVertsVolatile() const { return fVertsVolatile; } + hsBool AreIdxVolatile() const { return fIdxVolatile; } + + int GetLOD() const { return fLOD; } +}; + +#endif // _plGBufferGroup_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipeDebugFlags.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipeDebugFlags.h new file mode 100644 index 00000000..f9903fbb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipeDebugFlags.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPipeDebugFlags_inc +#define plPipeDebugFlags_inc + +namespace plPipeDbg +{ + // Debug flags + enum DebugFlags + { + kFlagReload, + kFlagShowAllBounds, + kFlagNoMultitexture, + kFlagNoLightmaps, + kFlagNoRuntimeLights, + kFlagOcclusionSnap, + kFlagNoAlphaBlending, + kFlagNoDecals, + kFlagDontSortFaces, + kFlagDisableSpecular, + kFlagOverlayWire, + kFlagShowNormals, + kFlagColorizeMipmaps, + kFlagSingleMat, + kFlagSkipVisDist, + kFlagNoPlates, + kFlagNoShadows, + kFlagNoUpperLayers, + kFlagNoProjLights, + kFlagNoSkinning, + kFlagBumpUV, + kFlagBumpW, + kFlagNoBump, + kFlagNoRender, + kFlagNoAnisotropy, + kFlagAllBright, + kFlagNoApplyProjLights, + kFlagOnlyApplyProjLights, + kFlagShowShadowBounds, + kFlagNoAuxSpans, + kFlagNoShadowApply, + kFlagNoPreShade, + kFlagNVPerfHUD, + kFlagNoFog, + }; + +} + +#endif // plPipeDebugFlags_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipelineCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipelineCreatable.h new file mode 100644 index 00000000..ee0e9c22 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipelineCreatable.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPipelineCreatable_inc +#define plPipelineCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include + +#include "plDXPipeline.h" + +REGISTER_NONCREATABLE( plDXPipeline ); + +#include "hsFogControl.h" + +REGISTER_NONCREATABLE( hsFogControl ); + +#include "plFogEnvironment.h" + +REGISTER_CREATABLE( plFogEnvironment ); + +#include "plRenderTarget.h" + +REGISTER_CREATABLE( plRenderTarget ); + +#include "plCubicRenderTarget.h" + +REGISTER_CREATABLE( plCubicRenderTarget ); + +#include "plCubicRenderTargetModifier.h" + +REGISTER_CREATABLE( plCubicRenderTargetModifier ); + +#include "plTransitionMgr.h" + +REGISTER_CREATABLE( plTransitionMgr ); + +#include "plDynamicEnvMap.h" +REGISTER_CREATABLE( plDynamicEnvMap ); +REGISTER_CREATABLE( plDynamicCamMap ); + +#endif // plPipelineCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipelineCreate.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipelineCreate.h new file mode 100644 index 00000000..38f698bc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPipelineCreate.h @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plPipelineCreate Class Header // +// Cyan, Inc. // +// // +// Here's a fun static class. All it does is contain wrapper functions for // +// returning pointers to new pipelines of various types. This way, someone // +// else, say, plClient, can create a new plDXPipeline without having to // +// include every DX header in the known universe. Its messy and there's // +// probably a better way to do it with the resource manager, but neither // +// Chris nor Matt can tell me what it is, so there. :P // +// // +// Note: complile-time trick is that the actual functions for these are // +// in the pipeline sources themselves. Makes more sense, esp. since we // +// then avoid any possible header conflict between the pipelines, should // +// one ever exist. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 3.8.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plPipelineCreate_h +#define _plPipelineCreate_h + + +//// plPipelineCreate Class Definition //////////////////////////////////////// + +class plPipeline; + +class plPipelineCreate +{ + protected: + + static plPipeline *ICreateDXPipeline( hsWinRef hWnd, const hsG3DDeviceModeRecord *devMode ); + + public: + + static plPipeline *CreatePipeline( hsWinRef hWnd, const hsG3DDeviceModeRecord *devMode ) + { + // Just this for now. Later we'll key off of the devMode + return ICreateDXPipeline( hWnd, devMode ); + } + +}; + + +#endif // _plPipelineCreate_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPlates.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPlates.cpp new file mode 100644 index 00000000..b5b3e343 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPlates.cpp @@ -0,0 +1,1046 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plPlates.cpp - Implementation of plates and plate manager // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsWindows.h" +#include "hsTypes.h" +#include "plPipeline.h" +#include "plPlates.h" + +#include "../plJPEG/plJPEG.h" +#include "../plGImage/plMipmap.h" +#include "../plSurface/plLayer.h" +#include "../plSurface/hsGMaterial.h" +#include "../plMessage/plLayRefMsg.h" +#include "../pnMessage/plRefMsg.h" +#include "hsGDeviceRef.h" +#include "hsResMgr.h" +#include "plPipeDebugFlags.h" + + +// A bit of a hack so that we will have the correct instance in the SceneViewer +static HINSTANCE gHInstance = GetModuleHandle(nil); + +void SetHInstance(void *instance) +{ + gHInstance = (HINSTANCE)instance; +} + +////////////////////////////////////////////////////////////////////////////// +//// plPlate Functions /////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +UInt32 plPlate::fMagicUniqueKeyInt = 0; + +plPlate::plPlate( plPlate **owningHandle ) +{ + fXformMatrix.Reset(); + fDepth = 1.0f; + fMaterial = nil; + fFlags = 0; + fOpacity = 1.f; + + fNext = nil; + fPrevPtr = nil; + fOwningHandle = owningHandle; + fMipmap = nil; + memset( fTitle, 0, sizeof( fTitle ) ); +} + +plPlate::~plPlate() +{ + if( fFlags & kFlagLocalMaterial ) + fMaterial->GetKey()->UnRefObject(); + else + { + hsRefCnt_SafeUnRef( fMaterial ); + } + + fMaterial = nil; + *fOwningHandle = nil; +} + +//// SetPosition ///////////////////////////////////////////////////////////// + +void plPlate::SetPosition( hsScalar x, hsScalar y, hsScalar z ) +{ + hsVector3 triple; + + + if( z != -1.0f ) + { + /// Gotta resort--let the manager do it + plPlateManager::Instance().IResortPlate( this, ( z + 1.0f <= fDepth ) ? true : false ); + fDepth = z + 1.0f; + } + + x *= fDepth / 1.0f; + y *= fDepth / 1.0f; + triple.fX = x; + triple.fY = y; + triple.fZ = fDepth; + + fXformMatrix.SetTranslate( &triple ); +} + +//// SetSize ///////////////////////////////////////////////////////////////// + +void plPlate::SetSize( hsScalar width, hsScalar height, bool adjustByAspectRation ) +{ + hsVector3 size; + + width *= fDepth / 1.0f; + height *= fDepth / 1.0f; + + size.fX = adjustByAspectRation ? (width * ((hsScalar)plPlateManager::Instance().GetPipeHeight() / (hsScalar)plPlateManager::Instance().GetPipeWidth())) : width; + size.fY = height; + size.fZ = 1.0f; + + fXformMatrix.SetScale( &size ); +} + +//// SetTransform //////////////////////////////////////////////////////////// + +void plPlate::SetTransform( hsMatrix44 &matrix, hsBool reSort ) +{ + fXformMatrix = matrix; + if( reSort ) + plPlateManager::Instance().IResortPlate( this, false ); +} + +//// SetMaterial ///////////////////////////////////////////////////////////// + +void plPlate::SetMaterial( hsGMaterial *material ) +{ + hsRefCnt_SafeAssign( fMaterial, material ); +} + +void plPlate::SetTexture(plBitmap *texture) +{ + plLayer *layer; + hsGMaterial *material; + char keyName[ 128 ]; + + material = TRACKED_NEW hsGMaterial(); + sprintf( keyName, "PlateBlank#%d", fMagicUniqueKeyInt++ ); + hsgResMgr::ResMgr()->NewKey( keyName, material, plLocation::kGlobalFixedLoc ); + layer = material->MakeBaseLayer(); + layer->SetShadeFlags( layer->GetShadeFlags() | hsGMatState::kShadeNoShade | hsGMatState::kShadeWhite | hsGMatState::kShadeReallyNoFog ); + layer->SetZFlags( layer->GetZFlags() | hsGMatState::kZNoZRead ); + layer->SetBlendFlags( layer->GetBlendFlags() | hsGMatState::kBlendAlpha ); + layer->SetOpacity( fOpacity ); + layer->SetUVWSrc(plLayerInterface::kUVWPassThru); + + hsgResMgr::ResMgr()->AddViaNotify(texture->GetKey(), TRACKED_NEW plGenRefMsg(layer->GetKey(), plRefMsg::kOnCreate, -1, plLayRefMsg::kTexture), plRefFlags::kActiveRef); + + SetMaterial(material); +} + +//// SetOpacity ////////////////////////////////////////////////////////////// + +void plPlate::SetOpacity( hsScalar opacity ) +{ + if( fMaterial != nil && fMaterial->GetLayer( 0 ) != nil ) + { + plLayer *layer = (plLayer *)fMaterial->GetLayer( 0 ); + layer->SetOpacity( opacity ); + } + + fOpacity = opacity; +} + +//// CreateMaterial ///////////////////////////////////////////////////// +// Creates a new material for this plate with either a specified texture +// or an empty, white-filled bitmap. + +plMipmap *plPlate::CreateMaterial( UInt32 width, UInt32 height, hsBool withAlpha, plMipmap* texture ) +{ + plLayer *layer; + hsGMaterial *material; + char keyName[ 128 ]; + + + if (texture) + { + fMipmap = texture; + } + else + { + /// Create a new bitmap + fMipmap = TRACKED_NEW plMipmap( width, height, withAlpha ? plMipmap::kARGB32Config : plMipmap::kRGB32Config, 1 ); + memset( fMipmap->GetImage(), 0xff, height * fMipmap->GetRowBytes() ); + sprintf( keyName, "PlateBitmap#%d", fMagicUniqueKeyInt++ ); + hsgResMgr::ResMgr()->NewKey( keyName, fMipmap, plLocation::kGlobalFixedLoc ); + fMipmap->SetFlags( fMipmap->GetFlags() | plMipmap::kDontThrowAwayImage ); + } + + /// NOW create a layer wrapper and a material for that layer + material = TRACKED_NEW hsGMaterial(); + sprintf( keyName, "PlateBlank#%d", fMagicUniqueKeyInt++ ); + hsgResMgr::ResMgr()->NewKey( keyName, material, plLocation::kGlobalFixedLoc ); + layer = material->MakeBaseLayer(); + layer->SetShadeFlags( layer->GetShadeFlags() | hsGMatState::kShadeNoShade | hsGMatState::kShadeWhite | hsGMatState::kShadeReallyNoFog ); + layer->SetZFlags( layer->GetZFlags() | hsGMatState::kZNoZRead ); + layer->SetBlendFlags( layer->GetBlendFlags() | hsGMatState::kBlendAlpha ); + layer->SetOpacity( fOpacity ); + + hsgResMgr::ResMgr()->AddViaNotify( fMipmap->GetKey(), TRACKED_NEW plLayRefMsg( layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture ), plRefFlags::kActiveRef ); + + // Set up a ref to these. Since we don't have a key, we use the + // generic RefObject() (and matching UnRefObject() when we're done). + // If we had a key, we would use myKey->AddViaNotify(otherKey) and myKey->Release(otherKey). + material->GetKey()->RefObject(); + + /// Set this as our new material and return the bitmap + fFlags |= kFlagLocalMaterial; + fMaterial = material; + return fMipmap; +} + +//// CreateFromResource ////////////////////////////////////////////////////// +// Creates a plate's material from a resource of the given name. + +// This is where hacks beget hacks. +// We have two problems here. First, the main cursor we use most of the time (IDB_CURSOR_UP) +// is a greyscale cursor, so it's color is copied to alpha and then set to white. But its +// color doesn't go to black, it goes to 0x040404, which when used for alpha is just enough +// to be annoying in 32 bit (ghost white square around cursor). +// Second, Win98 seems to be doing some sort of dither on the cursors that use a color key +// (colorKey == 0xff00ff), so the purple parts aren't exactly matching the colorKey, so +// we have a big opaque (except for the parts that dither out to 0xff00ff) purple square +// around the cursor. +// So, when it comes to color keying, we're going to pretend we're in 16 bit mode, and only +// check the upper 5 bits of each channel. If they match the colorKey, close enough to be +// transparent. +// For grey scale, if the alpha comes out less than 8 (upper 5 bits off), again close enough +// for complete transparency. +// +// All this happens in CreateFromResource and ReloadFromResource, so I've moved the alpha setting +// code to a function they can both use, rather than cut and pasting code (what a concept, must +// be one of those new-fangled OOP patterns). +// +void plPlate::ISetResourceAlphas(UInt32 colorKey) +{ + if( !fMipmap ) + return; + + /// Set alphas + colorKey &= 0x00f8f8f8; + + UInt32 numPix = fMipmap->GetWidth() * fMipmap->GetHeight(); + + UInt32 *d; + int i; + hsBool hasColorKey = false; + for( i = 0, d = (UInt32 *)(fMipmap->GetImage()); i < numPix; i++ ) + { + if( (d[ i ] & 0x00f8f8f8) == colorKey ) + { + hasColorKey = true; + break; + } + } + if( hasColorKey ) + { + for( i = 0, d = (UInt32 *)(fMipmap->GetImage()); i < numPix; i++ ) + { + // Win98 for some reason likes to return full alpha on the pixels, + // whereas Win2k/XP likes 0 alpha. Go figure... + if( ( d[ i ] & 0x00f8f8f8 ) == colorKey ) + d[ i ] = 0; + else + d[ i ] |= 0xff000000; + } + } + else + { + // No color key, must be a b/w alpha mask + for( i = 0, d = (UInt32 *)(fMipmap->GetImage()); i < numPix; i++ ) + { + UInt32 alpha = d[i] & 0xff; + if( !(alpha & 0xf8) ) + d[i] = 0x00ffffff; + else + d[ i ] = ( alpha << 24 ) | 0x00ffffff; + } + } +} + +void plPlate::CreateFromResource( const char *resName, UInt32 colorKey ) +{ +/* + Someday the following might actually work, once we get a plugin that + exports a material in the latest format, plus have the material actually + read in its layers (or maybe we'll have to read() them in manually?) + Right now, we just keep it here so we don't have to look up how to do it + when we need it. + + hsRAMStream rsrcStream; + HGLOBAL rsrcHdl; + HRSRC findInfo; + UInt8 *ptr; + UInt32 size; + + + findInfo = FindResource( GetModuleHandle( nil ), (LPCTSTR)1001, "HSMR" ); + size = SizeofResource( GetModuleHandle( nil ), findInfo ); + rsrcHdl = LoadResource( GetModuleHandle( nil ), findInfo ); + ptr = (UInt8 *)LockResource( rsrcHdl ); + + rsrcStream.Write( size, ptr ); + + UnlockResource( rsrcHdl ); + + rsrcStream.Rewind(); + + fMaterial = TRACKED_NEW hsGMaterial; + fMaterial->Read( &rsrcStream ); +*/ + UInt32 width, height; + + +#if HS_BUILD_FOR_WIN32 + HBITMAP rsrc; + BITMAPINFO bMapInfo; + HDC hDC = GetDC( nil ); + + rsrc = (HBITMAP)LoadImage( gHInstance, resName, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION ); + if( rsrc == nil ) + { + /// Copy data into a new material + CreateMaterial( 32, 32, true ); + SetSize( 0.1, 0.1 ); + ReleaseDC( nil, hDC ); + return; + } + +// hsAssert( rsrc != nil, "Cannot find specified resource" ); + + memset( &bMapInfo, 0, sizeof( bMapInfo ) ); + bMapInfo.bmiHeader.biSize = sizeof( bMapInfo.bmiHeader ); + height = GetDIBits( hDC, rsrc, 0, 0, nil, &bMapInfo, DIB_RGB_COLORS ); + hsAssert( height != 0, "Cannot get resource bitmap bits" ); + + width = bMapInfo.bmiHeader.biWidth; + height = bMapInfo.bmiHeader.biHeight; + bMapInfo.bmiHeader.biBitCount = 32; + bMapInfo.bmiHeader.biCompression = BI_RGB; +#endif + + /// Copy data into a new material + CreateMaterial( width, height, true ); + SetSize( (float)width, (float)height ); + +#if HS_BUILD_FOR_WIN32 + bMapInfo.bmiHeader.biHeight *= -1; + GetDIBits( hDC, rsrc, 0, height, fMipmap->GetImage(), &bMapInfo, DIB_RGB_COLORS ); + + ReleaseDC( nil, hDC ); + DeleteObject( rsrc ); +#endif + + ISetResourceAlphas(colorKey); +} + +//// ReloadFromResource ////////////////////////////////////////////////////// +// Creates a plate's material from a resource of the given name. + +void plPlate::ReloadFromResource( const char *resName, UInt32 colorKey ) +{ + UInt32 width, height; + + + if( !fMaterial || fMaterial->GetNumLayers() < 1 || fMaterial->GetLayer( 0 ) == nil || fMipmap == nil ) + { + hsStatusMessage( "WARNING: Not refilling plate material; bitmap not yet assigned\n" ); + return; + } + +#if HS_BUILD_FOR_WIN32 + HBITMAP rsrc; + BITMAPINFO bMapInfo; + HDC hDC = GetDC( nil ); + + rsrc = (HBITMAP)LoadImage( gHInstance, resName, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION ); + if( rsrc == nil ) + { + ReleaseDC( nil, hDC ); + return; + } + +// hsAssert( rsrc != nil, "Cannot find specified resource" ); + + memset( &bMapInfo, 0, sizeof( bMapInfo ) ); + bMapInfo.bmiHeader.biSize = sizeof( bMapInfo.bmiHeader ); + height = GetDIBits( hDC, rsrc, 0, 0, nil, &bMapInfo, DIB_RGB_COLORS ); + hsAssert( height != 0, "Cannot get resource bitmap bits" ); + + width = bMapInfo.bmiHeader.biWidth; + height = bMapInfo.bmiHeader.biHeight; + bMapInfo.bmiHeader.biBitCount = 32; + bMapInfo.bmiHeader.biCompression = BI_RGB; +#endif + + /// Copy the data into the existing material + if( fMipmap->GetWidth() != width || fMipmap->GetHeight() != height ) + { + hsStatusMessage( "WARNING: Not refilling plate material; resource size does not match\n" ); + } + +#if HS_BUILD_FOR_WIN32 + bMapInfo.bmiHeader.biHeight *= -1; + GetDIBits( hDC, rsrc, 0, height, fMipmap->GetImage(), &bMapInfo, DIB_RGB_COLORS ); + + ReleaseDC( nil, hDC ); + DeleteObject( rsrc ); +#endif + + ISetResourceAlphas(colorKey); + + if( fMipmap->GetDeviceRef() ) + fMipmap->GetDeviceRef()->SetDirty( true ); +} + +void plPlate::CreateFromJPEGResource( const char *resName, UInt32 colorKey ) +{ + hsRAMStream stream; + plMipmap* jpgTexture = nil; + +#if HS_BUILD_FOR_WIN32 + + HRSRC res = FindResource(NULL, resName, "JPEG"); + if (!res) + goto error; + + HGLOBAL resourceLoaded = LoadResource(NULL, res); + if (!resourceLoaded) + goto error; + + byte* data = (byte*)LockResource(resourceLoaded); + if (!data) + goto error; + + DWORD resSize = SizeofResource(NULL, res); + if (resSize == 0) + goto error; + + stream.Write(sizeof(DWORD), &resSize); + stream.Write(resSize, data); + stream.Rewind(); + + UnlockResource(resourceLoaded); + + char keyName[128]; + sprintf( keyName, "PlateJPEG#%d", fMagicUniqueKeyInt++ ); + + jpgTexture = plJPEG::Instance().ReadFromStream(&stream); + + if (jpgTexture) + { + hsgResMgr::ResMgr()->NewKey(keyName, jpgTexture, plLocation::kGlobalFixedLoc); + +#endif + + CreateMaterial( 256, 256, true, jpgTexture); + ISetResourceAlphas(colorKey); + return; + } + +error: + /// Copy data into a new material + CreateMaterial( 32, 32, true ); + SetSize( 0.1, 0.1 ); + return; +} + +void plPlate::ReloadFromJPEGResource( const char *resName, UInt32 colorKey ) +{ + hsRAMStream stream; + plMipmap* jpgTexture = nil; + +#if HS_BUILD_FOR_WIN32 + + HRSRC res = FindResource(NULL, resName, "JPEG"); + if (!res) + return; + + HGLOBAL resourceLoaded = LoadResource(NULL, res); + if (!resourceLoaded) + return; + + byte* data = (byte*)LockResource(resourceLoaded); + if (!data) + return; + + DWORD resSize = SizeofResource(NULL, res); + if (resSize == 0) + return; + + stream.Write(sizeof(DWORD), &resSize); + stream.Write(resSize, data); + stream.Rewind(); + + UnlockResource(resourceLoaded); + + jpgTexture = plJPEG::Instance().ReadFromStream(&stream); + + if (jpgTexture) + { + +#endif + fMipmap->CopyFrom(jpgTexture); + + ISetResourceAlphas(colorKey); + + if( fMipmap->GetDeviceRef() ) + fMipmap->GetDeviceRef()->SetDirty( true ); + + delete jpgTexture; + } +} + +//// ILink /////////////////////////////////////////////////////////////////// +// Links a plate into a plate list, but also sorts by decreasing depth, +// so the plate won't actually necessarily be added after the pointer +// given. + +void plPlate::ILink( plPlate **back ) +{ + hsAssert( fNext == nil && fPrevPtr == nil, "Trying to link a plate that's already linked" ); + + + /// Advance back as far as we need to go + while( *back != nil && (*back)->fDepth > fDepth ) + back = &( (*back)->fNext ); + + /// Link! + fNext = *back; + if( *back ) + (*back)->fPrevPtr = &fNext; + fPrevPtr = back; + *back = this; +} + +hsBool plPlate::IsVisible() +{ + // return not-visible if our material is not valid + if (fMaterial->GetNumLayers() == 0) + return false; + plLayerInterface* layer = fMaterial->GetLayer(0); + if (layer->GetTexture() == nil) + return false; + + // cursory check of material indicates it's valid, return our visible flag status + return ( fFlags & kFlagVisible ) ? true : false; +} + + +////////////////////////////////////////////////////////////////////////////// +//// plGraphPlate Functions ////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Constructor & Destructor //////////////////////////////////////////////// + +plGraphPlate::plGraphPlate( plPlate **owningHandle ) : plPlate( owningHandle ) +{ + fFlags |= kFlagIsAGraph; + SetLabelText( nil ); +} + +plGraphPlate::~plGraphPlate() +{ +} + +//// IMakePow2 /////////////////////////////////////////////////////////////// + +UInt32 plGraphPlate::IMakePow2( UInt32 value ) +{ + int i; + + + for( i = 0; value != 0; i++, value >>= 1 ); + return 1 << i; +} + +//// SetDataRange //////////////////////////////////////////////////////////// + +void plGraphPlate::SetDataRange( UInt32 min, UInt32 max, UInt32 width ) +{ + UInt32 height; + + + width = IMakePow2( width + 4 ); + height = IMakePow2( max - min + 1 + 4 ); + + CreateMaterial( width, height, true ); + fMin = min; + fMax = max; + + SetDataLabels( fMin, fMax ); + SetColors(); + SetDataColors(); + ClearData(); +} + +//// SetDataLabels /////////////////////////////////////////////////////////// + +void plGraphPlate::SetDataLabels( UInt32 min, UInt32 max ) +{ + fLabelMin = min; + fLabelMax = max; +} + +//// ClearData /////////////////////////////////////////////////////////////// + +void plGraphPlate::ClearData( void ) +{ + UInt32 *bits = (UInt32 *)fMipmap->GetImage(), *ptr; + int i; + + + // Background color + for( i = 0; i < fMipmap->GetWidth() * fMipmap->GetHeight(); bits[ i ] = fBGHexColor, i++ ); + + // Axes + ptr = bits + fMipmap->GetWidth(); + *ptr = fAxesHexColor; + for( ptr++, i = 0; i < fMipmap->GetHeight() - 4; i++, ptr += fMipmap->GetWidth() ) + *ptr = fAxesHexColor; + for( i = 0; i < fMipmap->GetWidth() - 4; ptr[ i ] = fAxesHexColor, i++ ); + ptr += fMipmap->GetWidth() - 1; + ptr[ 0 ] = fAxesHexColor; + ptr[ fMipmap->GetWidth() - 5 + 1 ] = fAxesHexColor; + + if( fMaterial->GetLayer( 0 ) != nil && fMaterial->GetLayer( 0 )->GetTexture() ) + { + hsGDeviceRef *ref = fMaterial->GetLayer( 0 )->GetTexture()->GetDeviceRef(); + if( ref != nil ) + ref->SetDirty( true ); + } +} + +//// AddData ///////////////////////////////////////////////////////////////// +// Scroll graph data left by one, then add the new value + +void plGraphPlate::AddData( Int32 value, Int32 value2, Int32 value3, Int32 value4 ) +{ + std::vector values; + if (value != -1) + values.push_back(value); + if (value2 != -1) + values.push_back(value2); + if (value3 != -1) + values.push_back(value3); + if (value4 != -1) + values.push_back(value4); + AddData(values); +} + +void plGraphPlate::AddData( std::vector values ) +{ + hsAssert( fMipmap != nil, "Trying to add data to an uninitialized plGraphPlate" ); + + fMipmap->SetCurrLevel( 0 ); + + UInt32 *bits = (UInt32 *)fMipmap->GetImage(), *ptr; + UInt32 *minDPos = fMipmap->GetAddr32( 3, fMipmap->GetHeight() - 3 - 10 ); + UInt32 *maxDPos = fMipmap->GetAddr32( 3, 2 ); + int i, j; + std::vector lows, his; + float lineCtr, lineInc; + int lastLineInt, lineInt, bumpCtr; + + // make sure we have enough colors + if (values.size() > fDataHexColors.size()) + { + for (i=fDataHexColors.size(); i fLastValues.size()) + { + for (i=fLastValues.size(); i fLabelMax ) + values[ i ] = fLabelMax; + else if( values[ i ] < fLabelMin ) + values[ i ] = fLabelMin; + values[ i ] = (UInt32)( (float)values[ i ] * ( fMipmap->GetHeight() - 4 ) / ( fLabelMax - fLabelMin + 1 ) ); + + if( values[ i ] < fLastValues[ i ] ) + { + lows[ i ] = values[ i ] - 1; + his[ i ] = fLastValues[ i ]; + } + else + { + lows[ i ] = fLastValues[ i ] - 1; + his[ i ] = values[ i ]; + } + } + + lineCtr = 0; + lastLineInt = 0; + bumpCtr = 0; + lineInc = 8.0f / ( fMipmap->GetHeight() - 4 ); + IDrawNumber( fLabelMin, minDPos, fMipmap->GetWidth(), fBGHexColor ); + IDrawNumber( fLabelMax, maxDPos, fMipmap->GetWidth(), fBGHexColor ); + for( i = 0, ptr = bits + fMipmap->GetWidth() + 2, j = fMipmap->GetHeight() - 4; i < fMipmap->GetHeight() - 4; i++, j-- ) + { + lineInt = (int)lineCtr; + if( lineInt != lastLineInt ) + bumpCtr = 2; + + memmove( ptr, ptr + 1, ( fMipmap->GetWidth() - 5 ) * sizeof( UInt32 ) ); + int dataIndex; + bool dataPlotted = false; + for (dataIndex = 0; dataIndex < values.size(); dataIndex++) + { + if( j >= lows[ dataIndex ] && j <= his[ dataIndex ] ) + { + ptr[ fMipmap->GetWidth() - 5 ] = fDataHexColors[ dataIndex ]; + dataPlotted = true; + break; + } + } + + if (!dataPlotted) + { + if( bumpCtr > 0 ) + { + if( lineInt == 4 ) + ptr[ fMipmap->GetWidth() - 5 ] = fGraphHexColor | 0xff000000; + else + ptr[ fMipmap->GetWidth() - 5 ] = fGraphHexColor; + bumpCtr--; + } + else + ptr[ fMipmap->GetWidth() - 5 ] = fBGHexColor; + } + + ptr += fMipmap->GetWidth(); + + lastLineInt = lineInt; + lineCtr += lineInc; + } + IDrawNumber( fLabelMin, minDPos, fMipmap->GetWidth(), fAxesHexColor ); + IDrawNumber( fLabelMax, maxDPos, fMipmap->GetWidth(), fAxesHexColor ); + + fLastValues = values; + + if( fMaterial->GetLayer( 0 ) != nil && fMaterial->GetLayer( 0 )->GetTexture() != nil ) + { + hsGDeviceRef *ref = fMaterial->GetLayer( 0 )->GetTexture()->GetDeviceRef(); + if( ref != nil ) + ref->SetDirty( true ); + } +} + +//// SetColors /////////////////////////////////////////////////////////////// + +void plGraphPlate::SetColors( UInt32 bgHexColor, UInt32 axesHexColor, UInt32 dataHexColor, UInt32 graphHexColor ) +{ + fBGHexColor = bgHexColor; + fAxesHexColor = axesHexColor; + if (fDataHexColors.size() == 0) + fDataHexColors.push_back(dataHexColor); + else + fDataHexColors[ 0 ] = dataHexColor; + fGraphHexColor = graphHexColor; + + ClearData(); +} + +//// SetDataColors /////////////////////////////////////////////////////////// + +void plGraphPlate::SetDataColors( UInt32 hexColor1, UInt32 hexColor2, UInt32 hexColor3, UInt32 hexColor4 ) +{ + std::vector colors; + colors.push_back(hexColor1); + colors.push_back(hexColor2); + colors.push_back(hexColor3); + colors.push_back(hexColor4); + SetDataColors(colors); +} + +void plGraphPlate::SetDataColors( const std::vector & hexColors ) +{ + fDataHexColors = hexColors; +} + +//// SetLabelText //////////////////////////////////////////////////////////// + +void plGraphPlate::SetLabelText( char *text1, char *text2, char *text3, char *text4 ) +{ + std::vector strings; + if( text1 != nil ) + strings.push_back(text1); + else + strings.push_back(""); + + if( text2 != nil ) + strings.push_back(text2); + else + strings.push_back(""); + + if( text3 != nil ) + strings.push_back(text3); + else + strings.push_back(""); + + if( text4 != nil ) + strings.push_back(text4); + else + strings.push_back(""); + SetLabelText(strings); +} + +void plGraphPlate::SetLabelText( const std::vector & text ) +{ + fLabelText = text; +} + +//// IDrawNumber ///////////////////////////////////////////////////////////// + +void plGraphPlate::IDrawNumber( UInt32 number, UInt32 *dataPtr, UInt32 stride, UInt32 color ) +{ + char str[ 16 ]; + int i; + + + sprintf( str, "%d", number ); + for( i = 0; str[ i ] != 0; i++ ) + { + IDrawDigit( str[ i ] - '0', dataPtr, stride, color ); + dataPtr += 7; + } +} + +//// IDrawDigit ////////////////////////////////////////////////////////////// + +void plGraphPlate::IDrawDigit( char digit, UInt32 *dataPtr, UInt32 stride, UInt32 color ) +{ + /// Yes, I know this is ugly. Move it into another file if you like. + char digits[ 10 ][ 5 * 3 ] = + { { 1,1,1, + 1,0,1, + 1,0,1, + 1,0,1, + 1,1,1 }, + { 0,1,0, + 1,1,0, + 0,1,0, + 0,1,0, + 1,1,1 }, + { 2,2,2, + 0,0,2, + 0,2,0, + 2,0,0, + 2,2,2 }, + { 3,3,3, + 0,0,3, + 3,3,3, + 0,0,3, + 3,3,3 }, + { 4,0,4, + 4,0,4, + 4,4,4, + 0,0,4, + 0,0,4 }, + { 5,5,5, + 5,0,0, + 5,5,5, + 0,0,5, + 5,5,5 }, + { 6,6,6, + 6,0,0, + 6,6,6, + 6,0,6, + 6,6,6 }, + { 7,7,7, + 0,0,7, + 0,0,7, + 0,0,7, + 0,0,7 }, + { 8,8,8, + 8,0,8, + 8,8,8, + 8,0,8, + 8,8,8 }, + { 9,9,9, + 9,0,9, + 9,9,9, + 0,0,9, + 0,0,9 } }; + + + char *digData = digits[ digit ]; + int i, j; + + + for( i = 0; i < 5; i++ ) + { + for( j = 0; j < 6; j += 2 ) + { + if( *digData ) + { + dataPtr[ j ] = color; + dataPtr[ j + 1 ] = color; + dataPtr[ j + stride ] = color; + dataPtr[ j + stride + 1 ] = color; + } + digData++; + } + dataPtr += stride + stride; + } +} + + +////////////////////////////////////////////////////////////////////////////// +//// plPlateManager Functions //////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +plPlateManager *plPlateManager::fInstance = nil; + + +//// Destructor ///////////////////////////////////////////////////////////// + +plPlateManager::~plPlateManager() +{ + while( fPlates != nil ) + DestroyPlate( fPlates ); + + fInstance = nil; +} + +//// CreatePlate ///////////////////////////////////////////////////////////// + +void plPlateManager::CreatePlate( plPlate **handle ) +{ + plPlate *plate = TRACKED_NEW plPlate( handle ); + + + plate->ILink( &fPlates ); + *handle = plate; +} + +void plPlateManager::CreatePlate( plPlate **handle, hsScalar width, hsScalar height ) +{ + CreatePlate( handle ); + (*handle)->SetSize( width, height ); +} + +void plPlateManager::CreatePlate( plPlate **handle, hsScalar x, hsScalar y, hsScalar width, hsScalar height ) +{ + CreatePlate( handle ); + (*handle)->SetPosition( x, y ); + (*handle)->SetSize( width, height ); +} + +void plPlateManager::CreateGraphPlate( plGraphPlate **handle ) +{ + plGraphPlate *plate = TRACKED_NEW plGraphPlate( (plPlate **)handle ); + + + plate->ILink( &fPlates ); + *handle = plate; +} + +//// DestroyPlate //////////////////////////////////////////////////////////// + +void plPlateManager::DestroyPlate( plPlate *plate ) +{ + if( plate != nil ) + { + plate->IUnlink(); + delete plate; + } +} + +//// GetPipeWidth/Height ///////////////////////////////////////////////////// + +UInt32 plPlateManager::GetPipeWidth( void ) +{ + return fOwner->Width(); +} + +UInt32 plPlateManager::GetPipeHeight( void ) +{ + return fOwner->Height(); +} + +//// DrawToDevice //////////////////////////////////////////////////////////// + +void plPlateManager::DrawToDevice( plPipeline *pipe ) +{ + if( !pipe->IsDebugFlagSet(plPipeDbg::kFlagNoPlates) ) + IDrawToDevice( pipe ); +} + +//// IResortPlate //////////////////////////////////////////////////////////// + +void plPlateManager::IResortPlate( plPlate *plate, bool fromCurrent ) +{ + plPlate **start = &fPlates; + + + if( fromCurrent ) + start = plate->fPrevPtr; + + plate->IUnlink(); + plate->ILink( start ); +} + +//// SetPlateScreenPos /////////////////////////////////////////////////////// + +void plPlateManager::SetPlateScreenPos( plPlate *plate, UInt32 x, UInt32 y ) +{ + float cX = ( (float)x / fOwner->Width() ) * 2.0f - 1.0f; + float cY = ( (float)y / fOwner->Height() ) * 2.0f - 1.0f; + + plate->SetPosition( cX, cY ); +} + +void plPlateManager::SetPlatePixelSize( plPlate *plate, UInt32 pWidth, UInt32 pHeight ) +{ + float width = (float)pWidth / fOwner->Width() * 2.0f; + float height = (float)pHeight / fOwner->Height() * 2.0f; + + plate->SetSize(width, height); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPlates.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPlates.h new file mode 100644 index 00000000..91d6541a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plPlates.h @@ -0,0 +1,236 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plPlates - Header file for the plates and plPlateManager // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plPlates_h +#define _plPlates_h + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "hsColorRGBA.h" +#include "hsTemplates.h" +#include "hsUtils.h" +#include "hsMatrix44.h" + + + +//// plPlate Class Definition //////////////////////////////////////////////// +// plPlate is the actual plate object that represents one plate on the +// screen. It has a transform matrix (which includes position, scale and +// rotation), a material, a depth value and a color that is applied to all +// four corners. All plates are parallelograms. + +class plPlateManager; +class hsGMaterial; +class plMipmap; +class plBitmap; + +class plPlate +{ + friend class plPlateManager; + + protected: + + hsMatrix44 fXformMatrix; + hsGMaterial *fMaterial; + plMipmap *fMipmap; + hsScalar fDepth, fOpacity; + UInt32 fFlags; + char fTitle[ 64 ]; + + plPlate *fNext; + plPlate **fPrevPtr; + + plPlate **fOwningHandle; + + static UInt32 fMagicUniqueKeyInt; + + plPlate( plPlate** owningHandle ); + virtual ~plPlate(); + + void ILink( plPlate **back ); + + void IUnlink( void ) + { + hsAssert( fPrevPtr, "Plate not in list" ); + if( fNext ) + fNext->fPrevPtr = fPrevPtr; + *fPrevPtr = fNext; + + fNext = nil; + fPrevPtr = nil; + } + + void ISetResourceAlphas(UInt32 colorKey); + + public: + + enum + { + kFlagVisible = 0x00000001, + kFlagLocalMaterial = 0x00000002, + kFlagIsAGraph = 0x00000004 + }; + + /// Basic properties + + void SetTransform( hsMatrix44 &matrix, hsBool reSort = true ); + void SetMaterial( hsGMaterial *material ); + void SetTexture(plBitmap *texture); // Creates a new single layer material to use the texture. + void SetTitle( const char *title ) { if( title != nil ) strncpy( fTitle, title, sizeof( fTitle ) ); else fTitle[ 0 ] = 0; } + + hsGMaterial *GetMaterial( void ) { return fMaterial; } + hsMatrix44 &GetTransform( void ) { return fXformMatrix; } + const char *GetTitle( void ) { return fTitle; } + UInt32 GetFlags( void ) { return fFlags; } + + void SetVisible( hsBool vis ) { if( vis ) fFlags |= kFlagVisible; else fFlags &= ~kFlagVisible; } + hsBool IsVisible( void ); + + void SetOpacity( hsScalar opacity = 1.f ); + + plPlate *GetNext( void ) { return fNext; } + + + /// Helper functions + + void SetDepth( hsScalar depth) { fDepth = depth; } + void SetPosition( hsScalar x, hsScalar y, hsScalar z = -1.0f ); + void SetSize( hsScalar width, hsScalar height, bool adjustByAspectRatio = false ); + + plMipmap *CreateMaterial( UInt32 width, UInt32 height, hsBool withAlpha, plMipmap* texture = NULL ); + void CreateFromResource( const char *resName, UInt32 colorKey = 0x00ff00ff ); + void ReloadFromResource( const char *resName, UInt32 colorKey = 0x00ff00ff ); + void CreateFromJPEGResource( const char *resName, UInt32 colorKey = 0x00ff00ff ); + void ReloadFromJPEGResource( const char *resName, UInt32 colorKey = 0x00ff00ff ); +}; + +//// plGraphPlate Class Definition /////////////////////////////////////////// +// A derivation of plPlate that maintains a procedural texture which displays +// a scrolling graph of data. + +class plGraphPlate : public plPlate +{ + protected: + UInt32 fBGHexColor, fAxesHexColor, fGraphHexColor; + std::vector fDataHexColors; + UInt32 fMin, fMax, fLabelMin, fLabelMax; + std::vector fLastValues; + std::vector fLabelText; + + UInt32 IMakePow2( UInt32 value ); + void IDrawNumber( UInt32 number, UInt32 *dataPtr, UInt32 stride, UInt32 color ); + void IDrawDigit( char digit, UInt32 *dataPtr, UInt32 stride, UInt32 color ); + + public: + plGraphPlate( plPlate **owningHandle ); + virtual ~plGraphPlate(); + + void SetDataRange( UInt32 min, UInt32 max, UInt32 width ); + void SetDataLabels( UInt32 min, UInt32 max ); + void SetLabelText( char *text1, char *text2 = nil, char *text3 = nil, char *text4 = nil ); + void SetLabelText( const std::vector & text ); + void ClearData( void ); + + void AddData( Int32 value, Int32 value2 = -1, Int32 value3 = -1, Int32 value4 = -1 ); + void AddData( std::vector values ); + + void SetColors( UInt32 bgHexColor = 0x80000000, UInt32 axesHexColor = 0xffffffff, UInt32 dataHexColor = 0xff00ff00, UInt32 graphHexColor = 0x80ff0000 ); + void SetDataColors( UInt32 hexColor1 = 0xff00ff00, UInt32 hexColor2 = 0xff0000ff, UInt32 hexColor3 = 0xffffff00, UInt32 hexColor4 = 0xffff00ff ); + void SetDataColors( const std::vector & hexColors ); + + const char *GetLabelText( int i ) { return fLabelText[ i ].c_str(); } + const UInt32 GetDataColor( int i ) { return fDataHexColors[ i ]; } + const UInt32 GetNumLabels() { return fLabelText.size(); } + const UInt32 GetNumColors() { return fDataHexColors.size(); } +}; + +//// plPlateManager Class Definition ///////////////////////////////////////// +// This class handles all the plates--it keeps track of all the plates, +// creates and destroys them, and draws them when the pipeline tells it to. + +class plPipeline; + +class plPlateManager +{ + friend class plPlate; + + private: + + static plPlateManager *fInstance; + + protected: + + plPlate *fPlates; + plPipeline *fOwner; + hsBool fCreatedSucessfully; + + plPlateManager( plPipeline *pipe ) + { + fInstance = this; + fPlates = nil; + fOwner = pipe; + fCreatedSucessfully = true; + } + + virtual void IDrawToDevice( plPipeline *pipe ) = 0; + + void IResortPlate( plPlate *plate, bool fromCurrent ); + + public: + + virtual ~plPlateManager(); + + static plPlateManager &Instance( void ) { return *fInstance; } + static bool InstanceValid( void ) { return fInstance != nil; } + + void CreatePlate( plPlate **handle ); + void CreatePlate( plPlate **handle, hsScalar width, hsScalar height ); + void CreatePlate( plPlate **handle, hsScalar x, hsScalar y, hsScalar width, hsScalar height ); + + void CreateGraphPlate( plGraphPlate **handle ); + + void DestroyPlate( plPlate *plate ); + + void SetPlateScreenPos( plPlate *plate, UInt32 x, UInt32 y ); + void SetPlatePixelSize( plPlate *plate, UInt32 pWidth, UInt32 pHeight ); + + UInt32 GetPipeWidth( void ); + UInt32 GetPipeHeight( void ); + void DrawToDevice( plPipeline *pipe ); + + hsBool IsValid( void ) { return fCreatedSucessfully; } +}; + +// Sets the hInstance that we load our resources from. A SceneViewer hack. +void SetHInstance(void *instance); + +#endif //_plPlates_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plRenderTarget.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plRenderTarget.cpp new file mode 100644 index 00000000..33e40bc9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plRenderTarget.cpp @@ -0,0 +1,189 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plRenderTarget.cpp - RenderTarget functions // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 7.19.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plRenderTarget.h" +#include "plCubicRenderTarget.h" +#include "hsStream.h" +#include "hsGDeviceRef.h" + +#include "plPipeline.h" +#include "plgDispatch.h" +#include "../pnMessage/plPipeResMakeMsg.h" + +/////////////////////////////////////////////////////////////////////////////// +//// plRenderTarget Functions ///////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void plRenderTarget::SetKey(plKey k) +{ + hsKeyedObject::SetKey(k); + if( k ) + { + if( !fParent ) + plgDispatch::Dispatch()->RegisterForExactType( plPipeRTMakeMsg::Index(), GetKey() ); + } +} + + + +hsBool plRenderTarget::MsgReceive(plMessage* msg) +{ + plPipeRTMakeMsg* make = plPipeRTMakeMsg::ConvertNoRef(msg); + if( make ) + { + if( !GetDeviceRef() || GetDeviceRef()->IsDirty() ) + { + make->Pipeline()->MakeRenderTargetRef(this); + } + return true; + } + return plBitmap::MsgReceive(msg); +} + +UInt32 plRenderTarget::Read( hsStream *s ) +{ + UInt32 total = plBitmap::Read( s ); + + fWidth = s->ReadSwap16(); + fHeight = s->ReadSwap16(); + + fProportionalViewport = s->ReadBool(); + if( fProportionalViewport ) + { + fViewport.fProportional.fLeft = s->ReadSwapScalar(); + fViewport.fProportional.fTop = s->ReadSwapScalar(); + fViewport.fProportional.fRight = s->ReadSwapScalar(); + fViewport.fProportional.fBottom = s->ReadSwapScalar(); + } + else + { + fViewport.fAbsolute.fLeft = s->ReadSwap16(); + fViewport.fAbsolute.fTop = s->ReadSwap16(); + fViewport.fAbsolute.fRight = s->ReadSwap16(); + fViewport.fAbsolute.fBottom = s->ReadSwap16(); + } + + fZDepth = s->ReadByte(); + fStencilDepth = s->ReadByte(); + + return total + 2 * 2 + 2 + 4 * ( fProportionalViewport ? sizeof( hsScalar ) : sizeof( UInt16 ) ) + sizeof( hsBool ); +} + +UInt32 plRenderTarget::Write( hsStream *s ) +{ + UInt32 total = plBitmap::Write( s ); + + s->WriteSwap16( fWidth ); + s->WriteSwap16( fHeight ); + + s->WriteBool( fProportionalViewport ); + if( fProportionalViewport ) + { + s->WriteSwapScalar( fViewport.fProportional.fLeft ); + s->WriteSwapScalar( fViewport.fProportional.fTop ); + s->WriteSwapScalar( fViewport.fProportional.fRight ); + s->WriteSwapScalar( fViewport.fProportional.fBottom ); + } + else + { + s->WriteSwap16( fViewport.fAbsolute.fLeft ); + s->WriteSwap16( fViewport.fAbsolute.fTop ); + s->WriteSwap16( fViewport.fAbsolute.fRight ); + s->WriteSwap16( fViewport.fAbsolute.fBottom ); + } + + s->WriteByte( fZDepth ); + s->WriteByte( fStencilDepth ); + + return total + 2 * 2 + 2 + 4 * ( fProportionalViewport ? sizeof( hsScalar ) : sizeof( UInt16 ) ) + sizeof( hsBool ); +} + +/////////////////////////////////////////////////////////////////////////////// +//// plCubicRenderTarget Functions //////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +UInt32 plCubicRenderTarget::Read( hsStream *s ) +{ + int i; + UInt32 total = plRenderTarget::Read( s ); + + + for( i = 0; i < 6; i++ ) + { + if( fFaces[ i ] == nil ) + fFaces[ i ] = TRACKED_NEW plRenderTarget(); + + fFaces[ i ]->fParent = this; + total += fFaces[ i ]->Read( s ); + } + + return total; +} + +UInt32 plCubicRenderTarget::Write( hsStream *s ) +{ + int i; + UInt32 total = plRenderTarget::Write( s ); + + + for( i = 0; i < 6; i++ ) + { + total += fFaces[ i ]->Write( s ); + } + + return total; +} + +UInt32 plCubicRenderTarget::GetTotalSize( void ) const +{ + UInt32 size = 0, i; + + for( i = 0; i < 6; i++ ) + { + if( fFaces[ i ] != nil ) + size += fFaces[ i ]->GetTotalSize(); + } + + return size; +} + +//// SetCameraMatrix ////////////////////////////////////////////////////////// + +void plCubicRenderTarget::SetCameraMatrix(const hsPoint3& pos) +{ + hsMatrix44::MakeEnvMapMatrices(pos, fWorldToCameras, fCameraToWorlds); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plRenderTarget.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plRenderTarget.h new file mode 100644 index 00000000..67b9b2fb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plRenderTarget.h @@ -0,0 +1,189 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plRenderTarget.h - Header file for the plRenderTarget class // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 5.21.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plRenderTarget_h +#define _plRenderTarget_h + +#include "plPipeResReq.h" + +#include "../plGImage/plBitmap.h" + +#define ASSERT_ABSOLUTE hsAssert( !fProportionalViewport, "Cannot perform this on a proportional RenderTarget" ); +#define ASSERT_PROPORTIONAL hsAssert( fProportionalViewport, "Cannot perform this on an absolute RenderTarget" ); + +//// Class Definition ///////////////////////////////////////////////////////// + +class hsGDeviceRef; +class plCubicRenderTarget; + +class plRenderTarget : public plBitmap +{ + friend plCubicRenderTarget; + + protected: + + UInt16 fWidth, fHeight; + + union + { + struct + { + UInt16 fLeft, fTop, fRight, fBottom; + } fAbsolute; + struct + { + hsScalar fLeft, fTop, fRight, fBottom; + } fProportional; + } fViewport; + + hsBool fApplyTexQuality; + hsBool fProportionalViewport; + UInt8 fZDepth, fStencilDepth; + + plCubicRenderTarget *fParent; + + virtual void SetKey(plKey k); + + virtual UInt32 Read( hsStream *s ); + virtual UInt32 Write( hsStream *s ); + public: + + CLASSNAME_REGISTER( plRenderTarget ); + GETINTERFACE_ANY( plRenderTarget, plBitmap ); + + plRenderTarget() + { + fWidth = 0; + fHeight = 0; + fPixelSize = 0; + fZDepth = 0; + fStencilDepth = 0; + fApplyTexQuality = false; + fProportionalViewport = true; + SetViewport( 0, 0, 1.f, 1.f ); + fFlags = 0; + fParent = nil; + + plPipeResReq::Request(); + } + + plRenderTarget( UInt16 flags, UInt16 width, UInt16 height, UInt8 bitDepth, UInt8 zDepth = 0xff, UInt8 stencilDepth = 0xff ) + { + fWidth = width; + fHeight = height; + fPixelSize = bitDepth; + fZDepth = ( zDepth != 0xff ) ? zDepth : (bitDepth > 16 ? 24 : 16); + fStencilDepth = ( stencilDepth != 0xff ) ? stencilDepth : 0; + + fFlags = flags; + fParent = nil; + + hsAssert( fFlags & (kIsTexture|kIsOffscreen), "Cannot perform this on an on-screen RenderTarget" ); + fApplyTexQuality = false; + fProportionalViewport = false; + SetViewport( 0, 0, width, height ); + + plPipeResReq::Request(); + } + + // Render-to-Screen constructor + plRenderTarget( UInt16 flags, hsScalar left, hsScalar top, hsScalar right, hsScalar bottom, UInt8 bitDepth, UInt8 zDepth = 0xff, UInt8 stencilDepth = 0xff ) + { + fWidth = 0; // Can't really set these, at least not yet + fHeight = 0; + fPixelSize = bitDepth; + fZDepth = ( zDepth != 0xff ) ? zDepth : 16; + fStencilDepth = ( stencilDepth != 0xff ) ? stencilDepth : 0; + + fFlags = flags; + fParent = nil; + + hsAssert( !( fFlags & (kIsTexture|kIsOffscreen) ), "Cannot perform this on an offscreen RenderTarget" ); + fApplyTexQuality = false; + fProportionalViewport = true; + SetViewport( left, top, right, bottom ); + + plPipeResReq::Request(); + } + + virtual ~plRenderTarget() {} + + virtual void SetViewport( UInt16 left, UInt16 top, UInt16 right, UInt16 bottom ) + { + ASSERT_ABSOLUTE; + fViewport.fAbsolute.fLeft = left; + fViewport.fAbsolute.fTop = top; + fViewport.fAbsolute.fRight = right; + fViewport.fAbsolute.fBottom = bottom; + } + + virtual void SetViewport( hsScalar left, hsScalar top, hsScalar right, hsScalar bottom ) + { + ASSERT_PROPORTIONAL; + fViewport.fProportional.fLeft = left; + fViewport.fProportional.fTop = top; + fViewport.fProportional.fRight = right; + fViewport.fProportional.fBottom = bottom; + } + + UInt16 GetWidth( void ) { return fWidth; } + UInt16 GetHeight( void ) { return fHeight; } + UInt8 GetZDepth( void ) { return fZDepth; } + UInt8 GetStencilDepth( void ) { return fStencilDepth; } + + UInt16 GetVPLeft( void ) { ASSERT_ABSOLUTE; return fViewport.fAbsolute.fLeft; } + UInt16 GetVPTop( void ) { ASSERT_ABSOLUTE; return fViewport.fAbsolute.fTop; } + UInt16 GetVPRight( void ) { ASSERT_ABSOLUTE; return fViewport.fAbsolute.fRight; } + UInt16 GetVPBottom( void ) { ASSERT_ABSOLUTE; return fViewport.fAbsolute.fBottom; } + + hsScalar GetVPLeftProp( void ) { ASSERT_PROPORTIONAL; return fViewport.fProportional.fLeft; } + hsScalar GetVPTopProp( void ) { ASSERT_PROPORTIONAL; return fViewport.fProportional.fTop; } + hsScalar GetVPRightProp( void ) { ASSERT_PROPORTIONAL; return fViewport.fProportional.fRight; } + hsScalar GetVPBottomProp( void ) { ASSERT_PROPORTIONAL; return fViewport.fProportional.fBottom; } + + hsBool ViewIsProportional( void ) const { return fProportionalViewport; } + + plCubicRenderTarget *GetParent( void ) const { return fParent; } + + virtual UInt32 GetTotalSize( void ) const { return fWidth * fHeight * ( fPixelSize >> 3 ); } + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void SetVisRegionName(char *name){} // override to set vis region names for anyone who cares +}; + + +#endif // _plRenderTarget_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStatusLogDrawer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStatusLogDrawer.cpp new file mode 100644 index 00000000..62b0385e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStatusLogDrawer.cpp @@ -0,0 +1,112 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plStatusLogDrawer class // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plStatusLogDrawer.h" +#include "plPipeline.h" +#include "plDebugText.h" +#include "../plStatusLog/plStatusLog.h" + +//// Draw //////////////////////////////////////////////////////////////////// + +void plStatusLogDrawer::IDrawLogNames(plStatusLog* curLog, plStatusLog* firstLog) +{ + plDebugText& drawText = plDebugText::Instance(); + + UInt32 width = 0, numLogs = 0; + + plStatusLog* iLog = firstLog; + while (iLog) + { + width = hsMaximum(drawText.CalcStringWidth(iLog->GetFileName()) + 4, width); + iLog = iLog->fNext; + numLogs++; + } + + UInt32 height = drawText.GetFontHeight() + 2; + drawText.DrawRect(0, 0, (UInt16)width, (UInt16)(height*numLogs), 0, 0, 0); + + UInt32 yPos = 0; + iLog = firstLog; + while (iLog) + { + if (iLog == curLog) + drawText.DrawString(2, (UInt16)yPos, iLog->GetFileName(), 0, 255, 0); + else + drawText.DrawString(2, (UInt16)yPos, iLog->GetFileName()); + + iLog = iLog->fNext; + yPos += height; + } +} + +void plStatusLogDrawer::Draw(plStatusLog* curLog, plStatusLog* firstLog) +{ + int i, x, y, width, height, lineHt; + plDebugText &drawText = plDebugText::Instance(); + + + /// Calc position on screen + lineHt = drawText.GetFontHeight() + 2; + height = lineHt * ( IGetMaxNumLines( curLog ) + 2 ) + 8; + if( IGetFlags( curLog ) & plStatusLog::kAlignToTop ) + { + width = fPipeline->Width() - 8; + x = 4; + y = 4; + } + else + { + width = fPipeline->Width() >> 1; + x = width - 10; + y = ( fPipeline->Height() - height ) >> 1; + } + + /// Draw! + if( IGetFlags( curLog ) & plStatusLog::kFilledBackground ) + drawText.DrawRect( x, y, x + width, y + height, 0, 0, 0, 127 ); + + drawText.DrawString( x + 2, y + ( lineHt >> 1 ), IGetFilename( curLog ), 127, 127, 255, 255, plDebugText::kStyleBold ); + drawText.DrawRect( x + 2, y + ( lineHt << 1 ) + 1, + x + width - 8, y + ( lineHt << 1 ) + 2, 127, 127, 255, 255 ); + + y += lineHt * 2; + for( i = 0; i < IGetMaxNumLines( curLog ); i++ ) + { + if( IGetLines( curLog )[ i ] != nil ) + drawText.DrawString( x + 4, y, IGetLines( curLog )[ i ], IGetColors( curLog )[ i ] ); + y += lineHt; + } + + if (firstLog) + IDrawLogNames(curLog, firstLog); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStatusLogDrawer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStatusLogDrawer.h new file mode 100644 index 00000000..49759432 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStatusLogDrawer.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plStatusLogDrawer class // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plStatusLogDrawer_h +#define _plStatusLogDrawer_h + +#include "../plStatusLog/plStatusLog.h" + + +//// plStatusLogDrawer Class Definition //////////////////////////////////////////// + +class plPipeline; +class plStatusLogDrawer : public plStatusLogDrawerStub +{ + protected: + plPipeline *fPipeline; + + void IDrawLogNames(plStatusLog* curLog, plStatusLog* firstLog); + + public: + + plStatusLogDrawer( plPipeline *pipe ) : fPipeline( pipe ) {} + virtual ~plStatusLogDrawer() {} + + virtual void Draw(plStatusLog* curLog, plStatusLog* firstLog); +}; + + +#endif //_plStatusLogDrawer_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStencil.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStencil.h new file mode 100644 index 00000000..64f4af73 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plStencil.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plStencil.h - Header for various stencil settings and enums // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 5.17.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plStencil_h +#define _plStencil_h + +#include "hsTypes.h" + + +//// Stencil Caps ///////////////////////////////////////////////////////////// + +class plStencilCaps +{ + public: + + enum Depths + { + kDepth1Bit = 0x01, + kDepth4Bits = 0x02, + kDepth8Bits = 0x04 + }; + + enum CompareFuncs + { + kCmpNever = 0, + kCmpLessThan, + kCmpEqual, + kCmpLessThanOrEqual, + kCmpGreaterThan, + kCmpNotEqual, + kCmpGreaterThanOrEqual, + kCmpAlways + + }; + + enum Ops + { + kOpKeep = 0x01, + kOpSetToZero = 0x02, + kOpReplace = 0x04, + kOpIncClamp = 0x08, + kOpDecClamp = 0x10, + kOpInvert = 0x20, + kOpIncWrap = 0x40, + kOpDecWrap = 0x80 + + }; + + hsBool fIsSupported; + UInt8 fSupportedDepths; + UInt8 fSupportedOps; +}; + +#endif // _plStencil_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp new file mode 100644 index 00000000..42d3fa3d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp @@ -0,0 +1,431 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plTextFont Class Functions // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 2.19.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsWindows.h" +#include "HeadSpin.h" +#include "plTextFont.h" +#include "plDebugText.h" + +#define DisplayableChar(c) (c >= 0 && c <= 128) + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plTextFont::plTextFont( plPipeline *pipe ) +{ + fMaxNumIndices = 1024; + fInitialized = false; + fPipe = pipe; +} + +plTextFont::~plTextFont() +{ + IUnlink(); +} + +//// IInitFontTexture ///////////////////////////////////////////////////////// + +UInt16 *plTextFont::IInitFontTexture( void ) +{ + int nHeight, x, y, c; + char myChar[ 2 ] = "x"; + UInt16 *tBits; + + DWORD *bitmapBits; + BITMAPINFO bmi; + HDC hDC; + HBITMAP hBitmap; + HFONT hFont; + SIZE size; + BYTE bAlpha; + + + // Figure out our texture size + if( fSize > 40 ) + fTextureWidth = fTextureHeight = 1024; + else if( fSize > 20 ) + fTextureWidth = fTextureHeight = 512; + else + fTextureWidth = fTextureHeight = 256; + + + // Create a new DC and bitmap that we can draw characters to + memset( &bmi.bmiHeader, 0, sizeof( BITMAPINFOHEADER ) ); + bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); + bmi.bmiHeader.biWidth = fTextureWidth; + bmi.bmiHeader.biHeight = -(int)fTextureHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biBitCount = 32; + + hDC = CreateCompatibleDC( nil ); + hBitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS, (void **)&bitmapBits, nil, 0 ); + SetMapMode( hDC, MM_TEXT ); + + nHeight = -MulDiv( fSize, GetDeviceCaps( hDC, LOGPIXELSY ), 72 ); + fFontHeight = -nHeight; + + hFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, fFace ); + hsAssert( hFont != nil, "Cannot create Windows font" ); + + SelectObject( hDC, hBitmap ); + SelectObject( hDC, hFont ); + + // Set text colors + SetTextColor( hDC, RGB( 255, 255, 255 ) ); + SetBkColor( hDC, 0 ); + SetTextAlign( hDC, TA_TOP ); + + // Loop through characters, drawing them one at a time + RECT r; + r.left = r.top = 0; + r.right = r.bottom = 10; + FillRect( hDC, &r, (HBRUSH)GetStockObject( GRAY_BRUSH ) ); + + // (Make first character a black dot, for filling rectangles) + SetPixel( hDC, 0, 0, RGB( 255, 255, 255 ) ); + for( c = 32, x = 1, y = 0; c < 127; c++ ) + { + myChar[ 0 ] = c; + GetTextExtentPoint32( hDC, myChar, 1, &size ); + + if( (UInt32)( x + size.cx + 1 ) > fTextureWidth ) + { + x = 0; + y += size.cy + 1; + } + + ExtTextOut( hDC, x, y, ETO_OPAQUE, nil, myChar, 1, nil ); + + fCharInfo[ c ].fW = (UInt16)size.cx; + fCharInfo[ c ].fH = (UInt16)size.cy; + fCharInfo[ c ].fUVs[ 0 ].fX = (float)x / (float)fTextureWidth; + fCharInfo[ c ].fUVs[ 0 ].fY = (float)y / (float)fTextureHeight; + fCharInfo[ c ].fUVs[ 1 ].fX = (float)( x + size.cx ) / (float)fTextureWidth; + fCharInfo[ c ].fUVs[ 1 ].fY = (float)( y + size.cy ) / (float)fTextureHeight; + fCharInfo[ c ].fUVs[ 0 ].fZ = fCharInfo[ c ].fUVs[ 1 ].fZ = 0; + + x += size.cx + 1; + } + fCharInfo[ 32 ].fUVs[ 1 ].fX = fCharInfo[ 32 ].fUVs[ 0 ].fX; + + // Special case the tab key + fCharInfo[ '\t' ].fUVs[ 1 ].fX = fCharInfo[ '\t' ].fUVs[ 0 ].fX = fCharInfo[ 32 ].fUVs[ 0 ].fX; + fCharInfo[ '\t' ].fUVs[ 1 ].fY = fCharInfo[ '\t' ].fUVs[ 0 ].fY = 0; + fCharInfo[ '\t' ].fUVs[ 0 ].fZ = fCharInfo[ '\t' ].fUVs[ 1 ].fZ = 0; + fCharInfo[ '\t' ].fW = fCharInfo[ 32 ].fW * 4; + fCharInfo[ '\t' ].fH = fCharInfo[ 32 ].fH; + + /// Now create the data block + UInt16 *data = TRACKED_NEW UInt16[ fTextureWidth * fTextureHeight ]; + tBits = data; + for( y = 0; y < fTextureHeight; y++ ) + { + for( x = 0; x < fTextureWidth; x++ ) + { + bAlpha = (BYTE)( ( bitmapBits[ fTextureWidth * y + x ] & 0xff ) >> 4 ); + + if( bitmapBits[ fTextureWidth * y + x ] ) + *tBits = 0xffff; + else + *tBits = 0; + + tBits++; + } + } + + // Cleanup and return + DeleteObject( hBitmap ); + DeleteDC( hDC ); + DeleteObject( hFont ); + + return data; +} + +//// Create /////////////////////////////////////////////////////////////////// + +void plTextFont::Create( char *face, UInt16 size ) +{ + // Init normal stuff + strncpy( fFace, face, sizeof( fFace ) ); + fSize = size; +} + +//// IInitObjects ///////////////////////////////////////////////////////////// + +void plTextFont::IInitObjects( void ) +{ + UInt16 *data; + + + // Create texture + data = IInitFontTexture(); + hsAssert( data != nil, "Cannot create font texture" ); + + ICreateTexture( data ); + delete [] data; + + // Create state blocks + IInitStateBlocks(); + + fInitialized = true; +} + +//// DrawString /////////////////////////////////////////////////////////////// + +void plTextFont::DrawString( const char *string, int sX, int sY, UInt32 hexColor, + UInt8 style, UInt32 rightEdge ) +{ + static hsTArray verts; + + int i, j, width, height, count, thisCount, italOffset; + float x = (float)sX; + char c, *strPtr; + + + if( !fInitialized ) + IInitObjects(); + + /// Set up to draw + italOffset = ( style & plDebugText::kStyleItalic ) ? fSize / 2: 0; + count = strlen( string ); + strPtr = (char *)string; + while( count > 0 ) + { + thisCount = ( count > 64 ) ? 64 : count; + count -= thisCount; + + // Create an array for our vertices + verts.SetCountAndZero( thisCount * ( ( style & plDebugText::kStyleBold ) ? 12 : 6 ) ); + + // Fill them all up now + for( i = 0; i < thisCount * ( ( style & plDebugText::kStyleBold ) ? 12 : 6 ); i++ ) + { + verts[ i ].fColor = hexColor; + verts[ i ].fPoint.fZ = 0; + } + + for( i = 0, j = 0; i < thisCount; i++, j += 6 ) + { + c = strPtr[ i ]; + // make sure its a character we will display + if ( DisplayableChar(c) ) + { + width = fCharInfo[ c ].fW + 1; + height = fCharInfo[ c ].fH + 1; + + if( rightEdge > 0 && x + width + italOffset >= rightEdge ) + { + count = 0; + thisCount = i; + break; + } + + verts[ j ].fPoint.fX = x + italOffset; + verts[ j ].fPoint.fY = (float)sY; + verts[ j ].fUV = fCharInfo[ c ].fUVs[ 0 ]; + + verts[ j + 1 ].fPoint.fX = x + width + italOffset; + verts[ j + 1 ].fPoint.fY = (float)sY; + verts[ j + 1 ].fUV = fCharInfo[ c ].fUVs[ 0 ]; + verts[ j + 1 ].fUV.fX = fCharInfo[ c ].fUVs[ 1 ].fX; + + verts[ j + 2 ].fPoint.fX = x; + verts[ j + 2 ].fPoint.fY = (float)sY + height; + verts[ j + 2 ].fUV = fCharInfo[ c ].fUVs[ 0 ]; + verts[ j + 2 ].fUV.fY = fCharInfo[ c ].fUVs[ 1 ].fY; + + verts[ j + 3 ].fPoint.fX = x; + verts[ j + 3 ].fPoint.fY = (float)sY + height; + verts[ j + 3 ].fUV = fCharInfo[ c ].fUVs[ 0 ]; + verts[ j + 3 ].fUV.fY = fCharInfo[ c ].fUVs[ 1 ].fY; + + verts[ j + 4 ].fPoint.fX = x + width + italOffset; + verts[ j + 4 ].fPoint.fY = (float)sY; + verts[ j + 4 ].fUV = fCharInfo[ c ].fUVs[ 0 ]; + verts[ j + 4 ].fUV.fX = fCharInfo[ c ].fUVs[ 1 ].fX; + + verts[ j + 5 ].fPoint.fX = x + width; + verts[ j + 5 ].fPoint.fY = (float)sY + height; + verts[ j + 5 ].fUV = fCharInfo[ c ].fUVs[ 1 ]; + + x += width + 1; + } + } + + if( thisCount == 0 ) + break; + + if( style & plDebugText::kStyleBold ) + { + for( i = 0; i < thisCount * 6; i++, j++ ) + { + verts[ j ] = verts[ i ]; + verts[ j ].fPoint.fX = verts[ j ].fPoint.fX + 1.0f; + } + } + + /// TEMPORARY DEBUG ONLY: see if we can catch this stupid random draw bug +#if 0 + for( i = 0; i < thisCount * ( ( style & plDebugText::kStyleBold ) ? 12 : 6 ); i += 3 ) + { + for( j = 0; j < 3; j++ ) + { + hsAssert( verts[ i + j ].fPoint.fX >= 0, "Text point out of range" ); + hsAssert( verts[ i + j ].fPoint.fY >= 0, "Text point out of range" ); + hsAssert( verts[ i + j ].fPoint.fX < 1024, "Text point out of range" ); + hsAssert( verts[ i + j ].fPoint.fY < 768, "Text point out of range" ); + } + int lt = ( verts[ i ].fPoint.fX < verts[ i + 1 ].fPoint.fX ? verts[ i ].fPoint.fX : verts[ i + 1 ].fPoint.fX ); + lt = ( verts[ i + 2 ].fPoint.fX < lt ? verts[ i + 2 ].fPoint.fX : lt ); + + int tp = ( verts[ i ].fPoint.fY < verts[ i + 1 ].fPoint.fY ? verts[ i ].fPoint.fY : verts[ i + 1 ].fPoint.fY ); + tp = ( verts[ i + 2 ].fPoint.fY < tp ? verts[ i + 2 ].fPoint.fY : tp ); + + int rt = ( verts[ i ].fPoint.fX > verts[ i + 1 ].fPoint.fX ? verts[ i ].fPoint.fX : verts[ i + 1 ].fPoint.fX ); + rt = ( verts[ i + 2 ].fPoint.fX > rt ? verts[ i + 2 ].fPoint.fX : rt ); + + int bt = ( verts[ i ].fPoint.fY > verts[ i + 1 ].fPoint.fY ? verts[ i ].fPoint.fY : verts[ i + 1 ].fPoint.fY ); + bt = ( verts[ i + 2 ].fPoint.fY > bt ? verts[ i + 2 ].fPoint.fY : bt ); + + hsAssert( rt - lt < 32, "Text character too big" ); + hsAssert( bt - tp < 32, "Text character too big" ); + } +#endif + /// TEMPORARY DEBUG ONLY: see if we can catch this stupid random draw bug + + + /// Draw a set of tris now + IDrawPrimitive( thisCount * ( ( style & plDebugText::kStyleBold ) ? 4 : 2 ), verts.AcquireArray() ); + + strPtr += thisCount; + } + + /// All done! +} + +//// CalcStringWidth ////////////////////////////////////////////////////////// + +UInt32 plTextFont::CalcStringWidth( const char *string ) +{ + int i, width = 0; + + + if( !fInitialized ) + IInitObjects(); + + for( i = 0; i < strlen( string ); i++ ) + { + // make sure its a character we will display + if ( DisplayableChar(string[i]) ) + width += fCharInfo[ string[ i ] ].fW + 2; + } + + return width; +} + +//// DrawRect ///////////////////////////////////////////////////////////////// +// TEMPORARY function to draw a flat-shaded 2D rectangle to the screen. Used +// to create a background for our console; will be obliterated once we figure +// a better way to do so. + +void plTextFont::DrawRect( int left, int top, int right, int bottom, UInt32 hexColor ) +{ + static hsTArray verts; + int i; + + + if( !fInitialized ) + IInitObjects(); + + /// Draw! + verts.SetCountAndZero( 6 ); + for( i = 0; i < 6; i++ ) + { + verts[ i ].fColor = hexColor; + verts[ i ].fPoint.fZ = 0; + verts[ i ].fUV.fX = verts[ i ].fUV.fY = 0; + } + + verts[ 0 ].fPoint.fX = verts[ 2 ].fPoint.fX = verts[ 3 ].fPoint.fX = (float)left; + verts[ 1 ].fPoint.fX = verts[ 4 ].fPoint.fX = verts[ 5 ].fPoint.fX = (float)right; + verts[ 0 ].fPoint.fY = verts[ 1 ].fPoint.fY = verts[ 4 ].fPoint.fY = (float)top; + verts[ 2 ].fPoint.fY = verts[ 3 ].fPoint.fY = verts[ 5 ].fPoint.fY = (float)bottom; + + // omg I had this at 6...just slap the dunce cap on me...-mcn + IDrawPrimitive( 2, verts.AcquireArray() ); + + /// All done! +} + +//// Draw3DBorder ///////////////////////////////////////////////////////////// +// Draws a 3d border, upper-left with the first color, lower-right with the +// second. I just LOOOOVE temporary functions :) +// Note: this way sucks. Live with it. + +void plTextFont::Draw3DBorder( int left, int top, int right, int bottom, UInt32 hexColor1, UInt32 hexColor2 ) +{ + static hsTArray verts; + int i; + + + if( !fInitialized ) + IInitObjects(); + + /// Draw! + verts.SetCountAndZero( 8 ); + for( i = 0; i < 8; i++ ) + { + verts[ i ].fColor = hexColor1; + verts[ i ].fPoint.fZ = 0; + verts[ i ].fUV.fX = verts[ i ].fUV.fY = 0; + } + + verts[ 1 ].fPoint.fX = verts[ 2 ].fPoint.fX = verts[ 3 ].fPoint.fX = verts[ 4 ].fPoint.fX = (float)left; + verts[ 0 ].fPoint.fX = verts[ 5 ].fPoint.fX = verts[ 6 ].fPoint.fX = verts[ 7 ].fPoint.fX = (float)right; + + verts[ 0 ].fPoint.fY = verts[ 1 ].fPoint.fY = verts[ 2 ].fPoint.fY = verts[ 7 ].fPoint.fY = (float)top; + verts[ 3 ].fPoint.fY = verts[ 4 ].fPoint.fY = verts[ 5 ].fPoint.fY = verts[ 6 ].fPoint.fY = (float)bottom; + + for( i = 4; i < 8; i++ ) + verts[ i ].fColor = hexColor2; + + IDrawLines( 4, verts.AcquireArray() ); + + /// All done! +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.h new file mode 100644 index 00000000..9975f040 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.h @@ -0,0 +1,141 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plTextFont Class Header // +// Generic 3D text font handler // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 2.19.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plTextFont_h +#define _plTextFont_h + +#include "hsTemplates.h" +#include "hsGeometry3.h" + +//// plTextFont Class Definition ////////////////////////////////////////////// + +class plPipeline; + +class plTextFont +{ + protected: + + struct plDXCharInfo + { + UInt16 fW, fH; + hsPoint3 fUVs[ 2 ]; + }; + + struct plFontVertex + { + hsPoint3 fPoint; + UInt32 fColor; + hsPoint3 fUV; + + plFontVertex& operator=(const int zero) + { + fPoint.Set(0,0,0); + fColor = 0; + fUV.Set(0,0,0); + + return *this; + } + }; + + UInt32 fMaxNumIndices; + UInt32 fTextureWidth, fTextureHeight; + + char fFace[ 128 ]; + UInt16 fSize; + hsBool fInitialized; + UInt16 fFontHeight; + + plPipeline *fPipe; + + plTextFont *fNext; + plTextFont **fPrevPtr; + + plDXCharInfo fCharInfo[ 128 ]; + + + virtual void IInitObjects( void ); + virtual void ICreateTexture( UInt16 *data ) = 0; + virtual void IInitStateBlocks( void ) = 0; + virtual void IDrawPrimitive( UInt32 count, plFontVertex *array ) = 0; + virtual void IDrawLines( UInt32 count, plFontVertex *array ) = 0; + + UInt16 *IInitFontTexture( void ); + + void IUnlink( void ) + { + hsAssert( fPrevPtr, "Font not in list" ); + if( fNext ) + fNext->fPrevPtr = fPrevPtr; + *fPrevPtr = fNext; + + fNext = nil; + fPrevPtr = nil; + } + + public: + + plTextFont( plPipeline *pipe ); + virtual ~plTextFont(); + + void Create( char *face, UInt16 size ); + void DrawString( const char *string, int x, int y, UInt32 hexColor, UInt8 style, UInt32 rightEdge = 0 ); + void DrawRect( int left, int top, int right, int bottom, UInt32 hexColor ); + void Draw3DBorder( int left, int top, int right, int bottom, UInt32 hexColor1, UInt32 hexColor2 ); + UInt32 CalcStringWidth( const char *string ); + UInt32 GetFontSize( void ) { return fSize; } + + UInt16 GetFontHeight() { return fFontHeight; } + + virtual void DestroyObjects( void ) = 0; + virtual void SaveStates( void ) = 0; + virtual void RestoreStates( void ) = 0; + virtual void FlushDraws( void ) = 0; + + void Link( plTextFont **back ) + { + fNext = *back; + if( *back ) + (*back)->fPrevPtr = &fNext; + fPrevPtr = back; + *back = this; + } + +}; + + +#endif // _plTextFont_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextGenerator.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextGenerator.cpp new file mode 100644 index 00000000..78a5b54f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextGenerator.cpp @@ -0,0 +1,594 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plTextGenerator Class Functions // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 12.13.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsWindows.h" +#include "hsTypes.h" +#include "hsMatrix44.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "plTextGenerator.h" +#include "../plGImage/plMipmap.h" +#include "../plPipeline/hsGDeviceRef.h" +#include "../pnMessage/plRefMsg.h" + +#include "plgDispatch.h" +#include "hsResMgr.h" + +// Because tempKeys haven't been fixed yet (mf says to blame Eric Ellis), reffing +// objects when we have a tempKey (or they have a tempKey) just don't work. In +// fact, it will do nasty things like crashing on shutdown. Until then, we simply +// won't do the refs. Note that this is BAD, but given the only time we currently +// use these objects are very limited, controlled cases that *should* be okay +// for now, we should be reasonably safe. For now. +//#define MCN_DO_REFS + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plTextGenerator::plTextGenerator() +{ + fHost = nil; +} + +plTextGenerator::plTextGenerator( plMipmap *host, UInt16 width, UInt16 height ) +{ + fHost = nil; + Attach( host, width, height ); +} + +plTextGenerator::~plTextGenerator() +{ + // This also won't work until tempKeys work, since the mipmap will be gone by + // this time, in which case, calling Detach() crashes +#ifdef MCN_DO_REFS + Detach(); +#endif +} + +//// Attach /////////////////////////////////////////////////////////////////// +// Grab onto a plMipmap, suck the texture out of it and replace it with our +// own. + +void plTextGenerator::Attach( plMipmap *host, UInt16 width, UInt16 height ) +{ + UInt16 textWidth, textHeight; + + + hsAssert( fHost == nil, "Attempting to attach an already attached plTextGenerator" ); + + fHost = host; + + /// Suck the old texture data out + fHost->Reset(); + + /// Make some new + + // Note that we need POW-2 textures, so we go for the next one up that will + // fit what we need + for( textWidth = 1; textWidth < width; textWidth <<= 1 ); + for( textHeight = 1; textHeight < height; textHeight <<= 1 ); + + fWidth = width; + fHeight = height; + fHost->fImage = (void *)IAllocateOSSurface( textWidth, textHeight ); + fHost->SetConfig( plMipmap::kARGB32Config ); + fHost->fWidth = textWidth; + fHost->fHeight = textHeight; + fHost->fPixelSize = 32; + fHost->fRowBytes = textWidth * 4; + fHost->fNumLevels = 1; + fHost->fFlags |= plMipmap::kUserOwnsBitmap | plMipmap::kDontThrowAwayImage; + fHost->fCompressionType = plMipmap::kUncompressed; + fHost->fUncompressedInfo.fType = plMipmap::UncompressedInfo::kRGB8888; + fHost->IBuildLevelSizes(); + fHost->fTotalSize = fHost->GetLevelSize( 0 ); + + // Destroy the old texture ref, since it's probably completely nutsoid at this point. + // This should force the pipeline to recreate one more suitable for our use + fHost->SetDeviceRef( nil ); + + // Some init color + hsColorRGBA color; + color.Set( 0.f, 0.f, 0.f, 1.f ); + ClearToColor( color ); + FlushToHost(); + +#ifdef MCN_DO_REFS + /// Of course, brilliantly enough, if we did an attach on the constructor, we don't have a key + /// yet, so we better give ourselves one before we can call AddViaNotify() + if( GetKey() == nil ) + { + char str[ 256 ]; + sprintf( str, "plTextGen:%s", fHost->GetKeyName() ); + hsgResMgr::ResMgr()->NewKey( str, this, plLocation::kGlobalFixedLoc ); + } + + /// Send ourselves a passive ref of the mipmap, so we get notified if and when it goes away + hsgResMgr::ResMgr()->AddViaNotify( fHost->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, 0, 0 ), plRefFlags::kActiveRef ); +#endif + /// All done! +} + +//// IAllocateOSSurface /////////////////////////////////////////////////////// +// OS-specific. Allocates a rectangular bitmap of the given dimensions that +// the OS can draw text into. Returns a pointer to the pixels. + +UInt32 *plTextGenerator::IAllocateOSSurface( UInt16 width, UInt16 height ) +{ +#if HS_BUILD_FOR_WIN32 + + BITMAPINFO bmi; + + + // Create a new DC and bitmap that we can draw characters to + memset( &bmi.bmiHeader, 0, sizeof( BITMAPINFOHEADER ) ); + bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -(int)height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biBitCount = 32; + + fWinRGBDC = CreateCompatibleDC( nil ); + fWinRGBBitmap = CreateDIBSection( fWinRGBDC, &bmi, DIB_RGB_COLORS, (void **)&fWinRGBBits, nil, 0 ); + SetMapMode( fWinRGBDC, MM_TEXT ); + SetBkMode( fWinRGBDC, TRANSPARENT ); + SetTextAlign( fWinRGBDC, TA_TOP | TA_LEFT ); + + SelectObject( fWinRGBDC, fWinRGBBitmap ); + + // Now create a second DC/bitmap combo, this one for writing alpha values to + memset( &bmi.bmiHeader, 0, sizeof( BITMAPINFOHEADER ) ); + bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -(int)height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biBitCount = 8; + + fWinAlphaDC = CreateCompatibleDC( nil ); + fWinAlphaBitmap = CreateDIBSection( fWinAlphaDC, &bmi, DIB_RGB_COLORS, (void **)&fWinAlphaBits, nil, 0 ); + SetMapMode( fWinAlphaDC, MM_TEXT ); + SetBkMode( fWinAlphaDC, TRANSPARENT ); + SetTextAlign( fWinAlphaDC, TA_TOP | TA_LEFT ); + + SelectObject( fWinAlphaDC, fWinAlphaBitmap ); + + return (UInt32 *)fWinRGBBits; +#endif +} + +//// Detach /////////////////////////////////////////////////////////////////// +// Release the mipmap unto itself. + +void plTextGenerator::Detach( void ) +{ + if( fHost == nil ) + return; +// hsAssert( fHost != nil, "Attempting to detach unattached host" ); + + SetFont( nil, 0 ); + IDestroyOSSurface(); + + fHost->Reset(); + fHost->fFlags &= ~( plMipmap::kUserOwnsBitmap | plMipmap::kDontThrowAwayImage ); + + // Destroy the old texture ref, since we're no longer using it + fHost->SetDeviceRef( nil ); + + plMipmap *oldHost = fHost; + + fHost = nil; + +#ifdef MCN_DO_REFS + // Now send ourselves a unref msg, just in case we were called directly (if this was done by + // message, we'll get called a few times, but that's ok, we're set up to handle that, and it + // won't happen 'cept on destruction so the speed penalty shouldn't be a problem) + GetKey()->Release( oldHost->GetKey() ); +#endif +} + +//// IDestroyOSSurface //////////////////////////////////////////////////////// +// Opposite of allocate. DUH! + +void plTextGenerator::IDestroyOSSurface( void ) +{ +#if HS_BUILD_FOR_WIN32 + + fHost->fImage = nil; // DeleteObject() will get rid of it for us + DeleteObject( fWinRGBBitmap ); + DeleteDC( fWinRGBDC ); + + DeleteObject( fWinAlphaBitmap ); + DeleteDC( fWinAlphaDC ); + +#endif +} + +//// ClearToColor ///////////////////////////////////////////////////////////// + +void plTextGenerator::ClearToColor( hsColorRGBA &color ) +{ + int i; + UInt32 *data = (UInt32 *)fHost->fImage; + UInt32 hexColor = color.ToARGB32(); + +#if HS_BUILD_FOR_WIN32 + GdiFlush(); +#endif + + for( i = 0; i < fHost->fWidth * fHost->fHeight; i++ ) + data[ i ] = hexColor; + + // Fill our alpha bitmap as well, since we use that too +#if HS_BUILD_FOR_WIN32 + memset( fWinAlphaBits, (UInt8)( color.a * 255.f ), fHost->fWidth * fHost->fHeight ); +#endif +} + +//// SetFont ////////////////////////////////////////////////////////////////// +// OS-specific. Load the given font for drawing the text with. + +void plTextGenerator::SetFont( const char *face, UInt16 size, hsBool antiAliasRGB ) +{ +#if HS_BUILD_FOR_WIN32 + if( fWinFont != nil ) + { + DeleteObject( fWinFont ); + fWinFont = nil; + } + if( fWinAlphaFont != nil ) + { + DeleteObject( fWinAlphaFont ); + fWinAlphaFont = nil; + } + + if( face != nil ) + { + int nHeight = -MulDiv( size, GetDeviceCaps( fWinRGBDC, LOGPIXELSY ), 72 ); + fWinFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, antiAliasRGB ? ANTIALIASED_QUALITY : DEFAULT_QUALITY, VARIABLE_PITCH, face ); + hsAssert( fWinFont != nil, "Cannot create Windows font for plTextGenerator" ); + + // The font for the alpha channel is identical except that it's antialiased, whereas the RGB version isn't. + fWinAlphaFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, (!antiAliasRGB) ? ANTIALIASED_QUALITY : DEFAULT_QUALITY, VARIABLE_PITCH, face ); + hsAssert( fWinAlphaFont != nil, "Cannot create Windows font for plTextGenerator" ); + + SelectObject( fWinRGBDC, fWinFont ); + SelectObject( fWinAlphaDC, fWinAlphaFont ); + } +#endif +} + +//// SetTextColor ///////////////////////////////////////////////////////////// +// blockRGB basically forces the RGB channel to write in blocks instead of +// actual characters. This isn't useful unless you're relying on the alpha +// channel to do the text (opaque text and transparent background), in which +// case you want plenty of block color in your RGB channel because it'll get +// alpha-ed out by the alpha channel. + +void plTextGenerator::SetTextColor( hsColorRGBA &color, hsBool blockRGB ) +{ +#if HS_BUILD_FOR_WIN32 + int r = (int)(color.r * 255.f); + int g = (int)(color.g * 255.f); + int b = (int)(color.b * 255.f); + int a = (int)(color.a * 255.f); + + if( blockRGB ) + { + ::SetBkColor( fWinRGBDC, RGB( r, g, b ) ); + ::SetBkMode( fWinRGBDC, OPAQUE ); + } + else + ::SetBkMode( fWinRGBDC, TRANSPARENT ); + + ::SetTextColor( fWinRGBDC, RGB( r, g, b ) ); + ::SetTextColor( fWinAlphaDC, RGB( a, a, a ) ); +#endif +} + +//// DrawString /////////////////////////////////////////////////////////////// + +void plTextGenerator::DrawString( UInt16 x, UInt16 y, const char *text ) +{ + wchar_t *wText = hsStringToWString(text); + DrawString(x,y,wText); + delete [] wText; +} + +void plTextGenerator::DrawString( UInt16 x, UInt16 y, const wchar_t *text ) +{ +#if HS_BUILD_FOR_WIN32 + + ::TextOutW( fWinRGBDC, x, y, text, wcslen( text ) ); + ::TextOutW( fWinAlphaDC, x, y, text, wcslen( text ) ); + +#endif +} + +//// DrawClippedString //////////////////////////////////////////////////////// + +void plTextGenerator::DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 width, UInt16 height ) +{ + wchar_t *wText = hsStringToWString(text); + DrawClippedString(x,y,wText,width,height); + delete [] wText; +} + +void plTextGenerator::DrawClippedString( Int16 x, Int16 y, const wchar_t *text, UInt16 width, UInt16 height ) +{ +#if HS_BUILD_FOR_WIN32 + + RECT r; + ::SetRect( &r, x, y, x + width, y + height ); + + ::ExtTextOutW( fWinRGBDC, x, y, ETO_CLIPPED, &r, text, wcslen( text ), nil ); + ::ExtTextOutW( fWinAlphaDC, x, y, ETO_CLIPPED, &r, text, wcslen( text ), nil ); + +#endif +} + +//// DrawClippedString //////////////////////////////////////////////////////// + +void plTextGenerator::DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height ) +{ + wchar_t *wText = hsStringToWString(text); + DrawClippedString(x,y,wText,clipX,clipY,width,height); + delete [] wText; +} + +void plTextGenerator::DrawClippedString( Int16 x, Int16 y, const wchar_t *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height ) +{ +#if HS_BUILD_FOR_WIN32 + + RECT r; + ::SetRect( &r, clipX, clipY, clipX + width, clipY + height ); + + ::ExtTextOutW( fWinRGBDC, x, y, ETO_CLIPPED, &r, text, wcslen( text ), nil ); + ::ExtTextOutW( fWinAlphaDC, x, y, ETO_CLIPPED, &r, text, wcslen( text ), nil ); + +#endif +} + +//// DrawWrappedString //////////////////////////////////////////////////////// + +void plTextGenerator::DrawWrappedString( UInt16 x, UInt16 y, const char *text, UInt16 width, UInt16 height ) +{ + wchar_t *wText = hsStringToWString(text); + DrawWrappedString(x,y,wText,width,height); + delete [] wText; +} + +void plTextGenerator::DrawWrappedString( UInt16 x, UInt16 y, const wchar_t *text, UInt16 width, UInt16 height ) +{ +#if HS_BUILD_FOR_WIN32 + + RECT r; + ::SetRect( &r, x, y, x + width, y + height ); + +// HBRUSH brush = ::CreateSolidBrush( RGB( 255, 255, 255 ) ); +// ::FillRect( fWinRGBDC, &r, brush ); +// ::DeleteObject( brush ); + ::DrawTextW( fWinRGBDC, text, wcslen( text ), &r, + DT_TOP | DT_LEFT | DT_NOPREFIX | DT_WORDBREAK ); + ::DrawTextW( fWinAlphaDC, text, wcslen( text ), &r, + DT_TOP | DT_LEFT | DT_NOPREFIX | DT_WORDBREAK ); + +#endif +} + +//// CalcStringWidth ////////////////////////////////////////////////////////// + +UInt16 plTextGenerator::CalcStringWidth( const char *text, UInt16 *height ) +{ + wchar_t *wText = hsStringToWString(text); + UInt16 retVal = CalcStringWidth(wText,height); + delete [] wText; + return retVal; +} + +UInt16 plTextGenerator::CalcStringWidth( const wchar_t *text, UInt16 *height ) +{ +#if HS_BUILD_FOR_WIN32 + + SIZE size; + ::GetTextExtentPoint32W( fWinRGBDC, text, wcslen( text ), &size ); + + if( height != nil ) + *height = (UInt16)size.cy; + + return (UInt16)size.cx; +#endif +} + +//// CalcWrappedStringSize //////////////////////////////////////////////////// + +void plTextGenerator::CalcWrappedStringSize( const char *text, UInt16 *width, UInt16 *height ) +{ + wchar_t *wText = hsStringToWString(text); + CalcWrappedStringSize(wText,width,height); + delete [] wText; +} + +void plTextGenerator::CalcWrappedStringSize( const wchar_t *text, UInt16 *width, UInt16 *height ) +{ +#if HS_BUILD_FOR_WIN32 + + RECT r; + ::SetRect( &r, 0, 0, *width, 0 ); + + ::DrawTextW( fWinRGBDC, text, wcslen( text ), &r, DT_TOP | DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT ); + + *width = (UInt16)(r.right); + if( height != nil ) + *height = (UInt16)r.bottom; +#endif +} + +//// FillRect ///////////////////////////////////////////////////////////////// + +void plTextGenerator::FillRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ) +{ +#if HS_BUILD_FOR_WIN32 + + RECT rc; + ::SetRect( &rc, x, y, x + width, y + height ); + + int r = (int)(color.r * 255.f); + int g = (int)(color.g * 255.f); + int b = (int)(color.b * 255.f); + int a = (int)(color.a * 255.f); + + HBRUSH brush = ::CreateSolidBrush( RGB( r, g, b ) ); + ::FillRect( fWinRGBDC, &rc, brush ); + ::DeleteObject( brush ); + + brush = ::CreateSolidBrush( RGB( a, a, a ) ); + ::FillRect( fWinAlphaDC, &rc, brush ); + ::DeleteObject( brush ); +#endif +} + +//// FrameRect //////////////////////////////////////////////////////////////// + +void plTextGenerator::FrameRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ) +{ +#if HS_BUILD_FOR_WIN32 + + RECT rc; + ::SetRect( &rc, x, y, x + width, y + height ); + + int r = (int)(color.r * 255.f); + int g = (int)(color.g * 255.f); + int b = (int)(color.b * 255.f); + int a = (int)(color.a * 255.f); + + HBRUSH brush = ::CreateSolidBrush( RGB( r, g, b ) ); + ::FrameRect( fWinRGBDC, &rc, brush ); + ::DeleteObject( brush ); + + brush = ::CreateSolidBrush( RGB( a, a, a ) ); + ::FrameRect( fWinAlphaDC, &rc, brush ); + ::DeleteObject( brush ); +#endif +} + +//// FlushToHost ////////////////////////////////////////////////////////////// + +void plTextGenerator::FlushToHost( void ) +{ +#if HS_BUILD_FOR_WIN32 + // Flush the GDI first, to make sure it's done + GdiFlush(); + + // Now copy our alpha channel over. I hate the GDI + UInt32 i = fHost->fWidth * fHost->fHeight; + UInt32 *dest = fWinRGBBits; + UInt8 *src = fWinAlphaBits; + +/* while( i-- ) + { + *dest &= 0x00ffffff; + *dest |= ( *src ) << 24; +// *dest |= ( *dest << 16 ) & 0xff000000; + dest++; + src++; + } +*/ + do + { + i--; + dest[ i ] &= 0x00ffffff; + dest[ i ] |= src[ i ] << 24; + } while( i ); +#endif + + // Dirty the mipmap's deviceRef, if there is one + if( fHost->GetDeviceRef() != nil ) + fHost->GetDeviceRef()->SetDirty( true ); +} + +//// GetTextWidth/Height ////////////////////////////////////////////////////// + +UInt16 plTextGenerator::GetTextWidth( void ) +{ + return ( fHost != nil ) ? (UInt16)(fHost->fWidth) : 0; +} + +UInt16 plTextGenerator::GetTextHeight( void ) +{ + return ( fHost != nil ) ? (UInt16)(fHost->fHeight) : 0; +} + +//// GetLayerTransform //////////////////////////////////////////////////////// +// Since the textGen can actually create a texture bigger than you were expecting, +// you want to be able to apply a layer texture transform that will compensate. This +// function will give you that transform. Just feed it into plLayer->SetTransform(). + +hsMatrix44 plTextGenerator::GetLayerTransform( void ) +{ + hsMatrix44 xform; + hsVector3 scale; + + scale.Set( (float)GetWidth() / (float)GetTextWidth(), + (float)GetHeight() / (float)GetTextHeight(), 1.f ); + + xform.MakeScaleMat( &scale ); + return xform; +} + +//// MsgReceive /////////////////////////////////////////////////////////////// + +hsBool plTextGenerator::MsgReceive( plMessage *msg ) +{ +#ifdef MCN_DO_REFS + plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( msg ); + if( refMsg != nil ) + { + if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest ) ) + { + // Don't do anything--already did an attach + } + else if( refMsg->GetContext() & ( plRefMsg::kOnDestroy | plRefMsg::kOnRemove ) ) + { + Detach(); + } + return true; + } +#endif + + return hsKeyedObject::MsgReceive( msg ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextGenerator.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextGenerator.h new file mode 100644 index 00000000..8703fadc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTextGenerator.h @@ -0,0 +1,124 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plTextGenerator Class Header // +// Helper utility class that "attaches" to a mipmap and fills that mipmap // +// with text. +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 12.13.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plTextGenerator_h +#define _plTextGenerator_h + +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "hsColorRGBA.h" + +//// plTextGenerator Class Definition ////////////////////////////////////////////// + +class plMipmap; +struct hsMatrix44; + +class plTextGenerator : public hsKeyedObject +{ + protected: + + plMipmap *fHost; + UInt16 fWidth, fHeight; + +#if HS_BUILD_FOR_WIN32 + HDC fWinRGBDC; + HBITMAP fWinRGBBitmap; + HFONT fWinFont; + UInt32 *fWinRGBBits; + + HFONT fWinAlphaFont; + HDC fWinAlphaDC; + HBITMAP fWinAlphaBitmap; + UInt8 *fWinAlphaBits; +#endif + + UInt32 *IAllocateOSSurface( UInt16 width, UInt16 height ); + void IDestroyOSSurface( void ); + + public: + + plTextGenerator(); + plTextGenerator( plMipmap *host, UInt16 width, UInt16 height ); + virtual ~plTextGenerator(); + + void Attach( plMipmap *host, UInt16 width, UInt16 height ); + void Detach( void ); + + /// Operations to perform on the text block + + void ClearToColor( hsColorRGBA &color ); + + void SetFont( const char *face, UInt16 size, hsBool antiAliasRGB = true ); + void SetTextColor( hsColorRGBA &color, hsBool blockRGB = false ); + + void DrawString( UInt16 x, UInt16 y, const char *text ); + void DrawString( UInt16 x, UInt16 y, const wchar_t *text ); + void DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 width, UInt16 height ); + void DrawClippedString( Int16 x, Int16 y, const wchar_t *text, UInt16 width, UInt16 height ); + void DrawClippedString( Int16 x, Int16 y, const char *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height ); + void DrawClippedString( Int16 x, Int16 y, const wchar_t *text, UInt16 clipX, UInt16 clipY, UInt16 width, UInt16 height ); + void DrawWrappedString( UInt16 x, UInt16 y, const char *text, UInt16 width, UInt16 height ); + void DrawWrappedString( UInt16 x, UInt16 y, const wchar_t *text, UInt16 width, UInt16 height ); + UInt16 CalcStringWidth( const char *text, UInt16 *height = nil ); + UInt16 CalcStringWidth( const wchar_t *text, UInt16 *height = nil ); + void CalcWrappedStringSize( const char *text, UInt16 *width, UInt16 *height ); + void CalcWrappedStringSize( const wchar_t *text, UInt16 *width, UInt16 *height ); + void FillRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ); + void FrameRect( UInt16 x, UInt16 y, UInt16 width, UInt16 height, hsColorRGBA &color ); + + void FlushToHost( void ); + + UInt16 GetTextWidth( void ); + UInt16 GetTextHeight( void ); + + UInt16 GetWidth( void ) { return fWidth; } + UInt16 GetHeight( void ) { return fHeight; } + + // Since the textGen can actually create a texture bigger than you were expecting, + // you want to be able to apply a layer texture transform that will compensate. This + // function will give you that transform. Just feed it into plLayer->SetTransform(). + + hsMatrix44 GetLayerTransform( void ); + + + virtual hsBool MsgReceive( plMessage *msg ); +}; + + +#endif // _plTextGenerator_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTransitionMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTransitionMgr.cpp new file mode 100644 index 00000000..2f07e215 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTransitionMgr.cpp @@ -0,0 +1,358 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plTransitionMgr - Class to handle fullscreen transitions (fades, etc) // +// // +// Note: eventually, I would like to drive these transitions on material // +// animations (it's just a big screen-covering plate with a material, // +// after all). This would allow the artists to specify their own // +// transitions and do really cool effects. However, that would require // +// somehow loading the materials in, and I'm not sure exactly how to do // +// that.... // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsWindows.h" +#include "hsTypes.h" +#include "plTransitionMgr.h" +#include "plPlates.h" + +#include "../plGImage/plMipmap.h" +#include "../plSurface/plLayer.h" +#include "../plSurface/hsGMaterial.h" +#include "../plMessage/plLayRefMsg.h" +#include "../pnMessage/plRefMsg.h" +#include "../plMessage/plTransitionMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "plgDispatch.h" +#include "hsGDeviceRef.h" +#include "hsResMgr.h" +#include "hsTimer.h" + +#include "../plAudio/plAudioSystem.h" +#include "../pnNetCommon/plNetApp.h" +#include "../plNetClient/plLinkEffectsMgr.h" +#include "../pnNetCommon/plNetApp.h" + +#include "../plStatusLog/plStatusLog.h" + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plTransitionMgr::plTransitionMgr() +{ + fEffectPlate = nil; + fCurrentEffect = kIdle; + fPlaying = false; +} + +void plTransitionMgr::Init( void ) +{ + ICreatePlate(); + plgDispatch::Dispatch()->RegisterForExactType( plTransitionMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->RegisterForExactType( plLinkEffectBCMsg::Index(), GetKey() ); +} + +plTransitionMgr::~plTransitionMgr() +{ + int i; + + + for( i = 0; i < fCallbacks.GetCount(); i++ ) + hsRefCnt_SafeUnRef( fCallbacks[ i ] ); + + if( fEffectPlate != nil ) + plPlateManager::Instance().DestroyPlate( fEffectPlate ); + + if( fRegisteredForTime ) + plgDispatch::Dispatch()->UnRegisterForExactType( plTimeMsg::Index(), GetKey() ); + + plgDispatch::Dispatch()->UnRegisterForExactType( plTransitionMsg::Index(), GetKey() ); + plgDispatch::Dispatch()->UnRegisterForExactType( plLinkEffectBCMsg::Index(), GetKey() ); +} + +//// ICreatePlate //////////////////////////////////////////////////////////// + +void plTransitionMgr::ICreatePlate( void ) +{ + int x, y; + + + fEffectPlate = nil; + + // +0.01 to deal with the half-pixel antialiasing stuff + plPlateManager::Instance().CreatePlate( &fEffectPlate, 0, 0, 2.01, 2.01 ); + fEffectPlate->SetDepth(2); + + // hack for now--create a black layer that we will animate the opacity on + plMipmap *ourMip = fEffectPlate->CreateMaterial( 16, 16, true ); + for( y = 0; y < ourMip->GetHeight(); y++ ) + { + UInt32 *pixels = ourMip->GetAddr32( 0, y ); + for( x = 0; x < ourMip->GetWidth(); x++ ) + pixels[ x ] = 0xff000000; + } + + fEffectPlate->SetVisible( false ); +} + +//// IStartFadeOut /////////////////////////////////////////////////////////// + +void plTransitionMgr::IStartFadeOut( hsScalar lengthInSecs, UInt8 effect ) +{ + fCurrentEffect = effect; // default - kFadeOut; + fEffectLength = lengthInSecs; + + // Special case for length 0--just jump straight to fadeout + if( lengthInSecs == 0.f ) + { + fCurrOpacity = 1.f; + fLastTime = -1.f; + fPlaying = false; + plgAudioSys::SetGlobalFadeVolume( 0.f ); + } + else + { + fCurrOpacity = 0; + fOpacDelta = 1.f / lengthInSecs; + fLastTime = -1.f; + fPlaying = true; + + // Register for time message + plgDispatch::Dispatch()->RegisterForExactType( plTimeMsg::Index(), GetKey() ); + fRegisteredForTime = true; + } + + if( fEffectPlate == nil ) + ICreatePlate(); + fEffectPlate->SetVisible( true ); + + plLayer *layer = (plLayer *)fEffectPlate->GetMaterial()->GetLayer( 0 ); + if( layer != nil ) + { + layer->SetOpacity( fCurrOpacity ); + } +} + +//// IStartFadeIn //////////////////////////////////////////////////////////// + +void plTransitionMgr::IStartFadeIn( hsScalar lengthInSecs, UInt8 effect ) +{ + fCurrentEffect = effect; // default - kFadeIn; + fEffectLength = lengthInSecs; + fCurrOpacity = 1.f; + fOpacDelta = -1.f / lengthInSecs; + fLastTime = -1.f; + fPlaying = true; + + // Register for time message + plgDispatch::Dispatch()->RegisterForExactType( plTimeMsg::Index(), GetKey() ); + fRegisteredForTime = true; + + if( fEffectPlate == nil ) + ICreatePlate(); + fEffectPlate->SetVisible( true ); + + plLayer *layer = (plLayer *)fEffectPlate->GetMaterial()->GetLayer( 0 ); + if( layer != nil ) + { + layer->SetOpacity( fCurrOpacity ); + } +} + +//// IStop /////////////////////////////////////////////////////////////////// + +void plTransitionMgr::IStop( hsBool aboutToStartAgain /*= false*/ ) +{ + int i; + + plgDispatch::Dispatch()->UnRegisterForExactType( plTimeMsg::Index(), GetKey() ); + fRegisteredForTime = false; + + if( fPlaying ) + { + if( !fHoldAtEnd && fEffectPlate != nil && !aboutToStartAgain ) + fEffectPlate->SetVisible( false ); + + // finish the opacity to the end opacity + if( fEffectPlate != nil ) + { + plLayer *layer = (plLayer *)fEffectPlate->GetMaterial()->GetLayer( 0 ); + if( layer != nil ) + { + layer->SetOpacity( (fCurrentEffect == kFadeIn || fCurrentEffect == kTransitionFadeIn) ? 0.f : 1.f ); + } + } + + if( !aboutToStartAgain ) + { + if (!fNoSoundFade) + plgAudioSys::SetGlobalFadeVolume( (fCurrentEffect == kFadeIn || fCurrentEffect == kTransitionFadeIn) ? 1.f : 0.f ); + } + + for( i = 0; i < fCallbacks.GetCount(); i++ ) + { + fCallbacks[ i ]->SetSender( GetKey() ); + plgDispatch::MsgSend( fCallbacks[ i ] ); + } + fCallbacks.Reset(); + + fPlaying = false; + } +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool plTransitionMgr::MsgReceive( plMessage* msg ) +{ + int i; + + + plTimeMsg *time = plTimeMsg::ConvertNoRef( msg ); + if( time != nil ) + { + if( !fPlaying ) + return false; + + if (fLastTime < 0) + { + // Semi-hack. We trigger transitions after we've finished loading. + // Problem is the loading all happens in one really long frame, so that + // if we record the time we started, we'll instantly be done next frame, + // even though we triggered just at the "end" of the last frame. + // + // So instead we don't start the clock until we get our first plTimeMsg. + + + fLastTime = (hsScalar)(time->DSeconds()); + return false; + } + + fEffectLength -= (hsScalar)( time->DSeconds() - fLastTime );//*/time->DelSeconds(); + if( fEffectLength < 0 ) + IStop(); + else + { + // Grab the layer so we can set the opacity + fCurrOpacity += (hsScalar)(fOpacDelta * ( time->DSeconds() - fLastTime ));//*/time->DelSeconds(); + if( fEffectPlate == nil ) + ICreatePlate(); + + plLayer *layer = (plLayer *)fEffectPlate->GetMaterial()->GetLayer( 0 ); + if( layer != nil ) + { + layer->SetOpacity( fCurrOpacity ); + } + + // Let the audiosystem handle fading in sounds + if(!fNoSoundFade) + plgAudioSys::SetGlobalFadeVolume( 1.f - fCurrOpacity ); + + + fLastTime = (hsScalar)(time->DSeconds()); + } + + return false; + } + + plTransitionMsg *effect = plTransitionMsg::ConvertNoRef( msg ); + if( effect != nil ) + { + if( fRegisteredForTime ) + IStop( true ); + + for( i = 0; i < effect->GetNumCallbacks(); i++ ) + { + plEventCallbackMsg *pMsg = effect->GetEventCallback( i ); + hsRefCnt_SafeRef( pMsg ); + fCallbacks.Append( pMsg ); + } + + fHoldAtEnd = effect->GetHoldState(); + + fNoSoundFade = false; + + switch(effect->GetEffect()) + { + case plTransitionMsg::kFadeInNoSound: + fNoSoundFade = true; + case plTransitionMsg::kFadeIn: + IStartFadeIn( effect->GetLengthInSecs(), kTransitionFadeIn ); + break; + case plTransitionMsg::kFadeOutNoSound: + fNoSoundFade = true; + case plTransitionMsg::kFadeOut: + IStartFadeOut( effect->GetLengthInSecs(), kTransitionFadeOut ); + break; + } + + return false; + } + + plLinkEffectBCMsg *link = plLinkEffectBCMsg::ConvertNoRef( msg ); + if( link != nil ) + { + const float kScreenFadeTime = 3.f; // seconds + + // Go ahead and auto-trigger based on link FX messages + if( plNetClientApp::GetInstance() != nil && link->fLinkKey == plNetClientApp::GetInstance()->GetLocalPlayerKey() ) + { + if( fRegisteredForTime ) + IStop( true ); + + if( link->HasLinkFlag( plLinkEffectBCMsg::kLeavingAge ) ) + { + plNetApp::GetInstance()->DebugMsg("Local player linking out, fading screen\n"); + + fHoldAtEnd = true; + IStartFadeOut( kScreenFadeTime ); + } + else + { + plNetApp::GetInstance()->DebugMsg("Local player linking in, fading screen\n"); + + fHoldAtEnd = false; + IStartFadeIn( kScreenFadeTime ); + } + + if (link->HasLinkFlag(plLinkEffectBCMsg::kSendCallback)) + { + plLinkEffectsMgr *mgr; + if( ( mgr = plLinkEffectsMgr::ConvertNoRef( link->GetSender()->ObjectIsLoaded() ) ) != nil ) + { + plEventCallbackMsg *cback = plEventCallbackMsg::ConvertNoRef( mgr->WaitForEffect( link->fLinkKey ) ); +// hsRefCnt_SafeRef( cback ); // mgr has given us ownership, his ref is now ours. No need for another. -mf- + fCallbacks.Append( cback ); + } + } + } + return true; + } + return hsKeyedObject::MsgReceive( msg ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTransitionMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTransitionMgr.h new file mode 100644 index 00000000..08a25003 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plTransitionMgr.h @@ -0,0 +1,90 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plTransitionMgr - Class to handle fullscreen transitions (fades, etc) // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plTransitionMgr_h +#define _plTransitionMgr_h + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsUtils.h" + + +//// Class Definition //////////////////////////////////////////////////////// + +class plPlate; +class plEventCallbackMsg; + +class plTransitionMgr : public hsKeyedObject +{ + protected: + + plPlate *fEffectPlate; + + enum + { + kIdle, + kFadeOut, + kFadeIn, + kTransitionFadeIn, + kTransitionFadeOut + }; + + UInt8 fCurrentEffect; + hsBool fRegisteredForTime, fHoldAtEnd, fPlaying, fNoSoundFade; + hsScalar fEffectLength, fCurrOpacity, fOpacDelta; + hsScalar fLastTime; + + void IStartFadeIn( hsScalar lengthInSecs, UInt8 effect = kFadeIn ); + void IStartFadeOut( hsScalar lengthInSecs, UInt8 effect = kFadeOut ); + + void ICreatePlate( void ); + + void IStop( hsBool aboutToStartAgain = false ); + + hsTArray fCallbacks; + + public: + + plTransitionMgr(); + virtual ~plTransitionMgr(); + + CLASSNAME_REGISTER( plTransitionMgr ); + GETINTERFACE_ANY( plTransitionMgr, hsKeyedObject ); + + void Init( void ); + + virtual hsBool MsgReceive( plMessage* msg ); +}; + + +#endif //_plTransitionMgr_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plVertCoder.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plVertCoder.cpp new file mode 100644 index 00000000..9083d976 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plVertCoder.cpp @@ -0,0 +1,447 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plVertCoder.h" + +#include "hsStream.h" + +#include "plGBufferGroup.h" + +const hsScalar kPosQuantum = 1.f / hsScalar(1 << 10); +const hsScalar kWeightQuantum = 1.f / hsScalar(1 << 15); +const hsScalar kUVWQuantum = 1.f / hsScalar(1 << 16); + +UInt32 plVertCoder::fCodedVerts = 0; +UInt32 plVertCoder::fCodedBytes = 0; +UInt32 plVertCoder::fRawBytes = 0; +UInt32 plVertCoder::fSkippedBytes = 0; + +static const hsScalar kQuanta[plVertCoder::kNumFloatFields] = +{ + kPosQuantum, + kWeightQuantum, + + kUVWQuantum, + kUVWQuantum, + kUVWQuantum, + kUVWQuantum, + + kUVWQuantum, + kUVWQuantum, + kUVWQuantum, + kUVWQuantum, +}; + + +inline void plVertCoder::ICountFloats(const UInt8* src, UInt16 maxCnt, const hsScalar quant, const UInt32 stride, + hsScalar& lo, hsBool &allSame, UInt16& count) +{ + lo = *(hsScalar*)src; + lo = floor(lo / quant + 0.5f) * quant; + allSame = false; + hsScalar hi = lo; + + count = 1; + + const hsScalar maxRange = hsScalar(UInt16(0xffff)) * quant; + + src += stride; + maxCnt--; + + while( maxCnt-- ) + { + hsScalar val = *(hsScalar*)src; + val = floor(val / quant + 0.5f) * quant; + if( val < lo ) + { + if( hi - val > maxRange ) + return; + lo = val; + } + else if( val > hi ) + { + if( val - lo > maxRange ) + return; + hi = val; + } + count++; + src += stride; + } + allSame = (lo == hi); +} + +static inline void IWriteFloat(hsStream* s, const UInt8*& src, const hsScalar offset, const hsScalar quantum) +{ + float fval = *(float*)src; + fval -= offset; + fval /= quantum; +// hsAssert(fval < hsScalar(UInt16(0xffff)), "Bad offset?"); + + const UInt16 ival = UInt16(floor(fval + 0.5f)); + s->WriteSwap16(ival); + + src += 4; +} + +static inline void IReadFloat(hsStream* s, UInt8*& dst, const hsScalar offset, const hsScalar quantum) +{ + const UInt16 ival = s->ReadSwap16(); + float fval = float(ival) * quantum; + fval += offset; + + hsScalar* val = (hsScalar*)dst; + *val = fval; + + dst += 4; +} + +inline void plVertCoder::IEncodeFloat(hsStream* s, const UInt32 vertsLeft, const int field, const int chan, const UInt8*& src, const UInt32 stride) +{ + if( !fFloats[field][chan].fCount ) + { + ICountFloats(src, (UInt16)vertsLeft, kQuanta[field], stride, fFloats[field][chan].fOffset, fFloats[field][chan].fAllSame, fFloats[field][chan].fCount); + + s->WriteSwapScalar(fFloats[field][chan].fOffset); + s->WriteBool(fFloats[field][chan].fAllSame); + s->WriteSwap16(fFloats[field][chan].fCount); + + } + if (!fFloats[field][chan].fAllSame) + IWriteFloat(s, src, fFloats[field][chan].fOffset, kQuanta[field]); + else + src += 4; + + fFloats[field][chan].fCount--; +} + +inline void plVertCoder::IDecodeFloat(hsStream* s, const int field, const int chan, UInt8*& dst, const UInt32 stride) +{ + if( !fFloats[field][chan].fCount ) + { + fFloats[field][chan].fOffset = s->ReadSwapScalar(); + fFloats[field][chan].fAllSame = s->ReadBool(); + fFloats[field][chan].fCount = s->ReadSwap16(); + } + + if (!fFloats[field][chan].fAllSame) + IReadFloat(s, dst, fFloats[field][chan].fOffset, kQuanta[field]); + else + { + *((hsScalar*)dst) = fFloats[field][chan].fOffset; + dst += 4; + } + + fFloats[field][chan].fCount--; +} + +static inline int INumWeights(const UInt8 format) +{ + return (format & plGBufferGroup::kSkinWeightMask) >> 4; +} + +static const hsScalar kNormalScale(Int16(0x7fff)); +static const hsScalar kInvNormalScale(1.f / kNormalScale); + +inline void plVertCoder::IEncodeNormal(hsStream* s, const UInt8*& src, const UInt32 stride) +{ + + hsScalar x = *(hsScalar*)src; + s->WriteByte((UInt8)((x / 2.f + .5f) * 255.9f)); + src += 4; + + x = *(hsScalar*)src; + s->WriteByte((UInt8)((x / 2.f + .5f) * 255.9f)); + src += 4; + + x = *(hsScalar*)src; + s->WriteByte((UInt8)((x / 2.f + .5f) * 255.9f)); + src += 4; +} + +inline void plVertCoder::IDecodeNormal(hsStream* s, UInt8*& dst, const UInt32 stride) +{ + + UInt8 ix = s->ReadByte(); + hsScalar* x = (hsScalar*)dst; + *x = (ix / 255.9f - .5f) * 2.f; + dst += 4; + + ix = s->ReadByte(); + x = (hsScalar*)dst; + *x = (ix / 255.9f - .5f) * 2.f; + dst += 4; + + ix = s->ReadByte(); + x = (hsScalar*)dst; + *x = (ix / 255.9f - .5f) * 2.f; + dst += 4; +} + +inline void plVertCoder::ICountBytes(const UInt32 vertsLeft, const UInt8* src, const UInt32 stride, UInt16& len, UInt8& same) +{ + // We want to run length encode this. So we're looking here for either + // the number of consecutive bytes of the same value, + // or the number of consective bytes of different values. + // The latter is so we don't wind up getting larger when there aren't any + // runs of the same value (count=1 and val=c1, count=1 and val=c2, etc.). + // The break-even point is a run of 3, so we'll look for a minimum run of 4. + + if( vertsLeft < 4 ) + { + len = (UInt16)vertsLeft; + same = false; + + return; + } + + // First, count how many values are the same as the first one + int i; + for( i = 0; i < vertsLeft; i++ ) + { + if( src[i * stride] != src[0] ) + break; + } + + if( i >= 4 ) + { + // Found a good run. + len = i; + same = true; + + return; + } + + // Okay, we're in a section of varying values. How far to the next + // section of sameness? + same = false; + for( ; i < vertsLeft-4; i++ ) + { + if( (src[i*stride] == src[(i+1)*stride]) + &&(src[i*stride] == src[(i+2)*stride]) + &&(src[i*stride] == src[(i+3)*stride]) ) + break; + } + + if( i < vertsLeft-4 ) + { + len = i; + return; + } + + len = (UInt16)vertsLeft; + return; +} + +static const UInt16 kSameMask(0x8000); + +inline void plVertCoder::IEncodeByte(hsStream* s, const int chan, const UInt32 vertsLeft, const UInt8*& src, const UInt32 stride) +{ + if( !fColors[chan].fCount ) + { + ICountBytes(vertsLeft, src, stride, fColors[chan].fCount, fColors[chan].fSame); + + UInt16 cnt = fColors[chan].fCount; + if( fColors[chan].fSame ) + cnt |= kSameMask; + s->WriteSwap16(cnt); + + if( fColors[chan].fSame ) + s->WriteByte(*src); + } + + if( !fColors[chan].fSame ) + s->WriteByte(*src); + + src++; + fColors[chan].fCount--; +} + +inline void plVertCoder::IDecodeByte(hsStream* s, const int chan, UInt8*& dst, const UInt32 stride) +{ + if( !fColors[chan].fCount ) + { + UInt16 cnt = s->ReadSwap16(); + if( cnt & kSameMask ) + { + fColors[chan].fSame = true; + fColors[chan].fVal = s->ReadByte(); + + cnt &= ~kSameMask; + } + else + { + fColors[chan].fSame = false; + } + fColors[chan].fCount = cnt; + } + if( !fColors[chan].fSame ) + *dst = s->ReadByte(); + else + *dst = fColors[chan].fVal; + + dst++; + fColors[chan].fCount--; +} + +inline void plVertCoder::IEncodeColor(hsStream* s, const UInt32 vertsLeft, const UInt8*& src, const UInt32 stride) +{ + IEncodeByte(s, 0, vertsLeft, src, stride); + IEncodeByte(s, 1, vertsLeft, src, stride); + IEncodeByte(s, 2, vertsLeft, src, stride); + IEncodeByte(s, 3, vertsLeft, src, stride); +} + +inline void plVertCoder::IDecodeColor(hsStream* s, UInt8*& dst, const UInt32 stride) +{ + IDecodeByte(s, 0, dst, stride); + IDecodeByte(s, 1, dst, stride); + IDecodeByte(s, 2, dst, stride); + IDecodeByte(s, 3, dst, stride); +} + +inline void plVertCoder::IEncode(hsStream* s, const UInt32 vertsLeft, const UInt8*& src, const UInt32 stride, const UInt8 format) +{ + IEncodeFloat(s, vertsLeft, kPosition, 0, src, stride); + IEncodeFloat(s, vertsLeft, kPosition, 1, src, stride); + IEncodeFloat(s, vertsLeft, kPosition, 2, src, stride); + + // Weights and indices? + const int numWeights = INumWeights(format); + if( numWeights ) + { + int j; + for( j = 0; j < numWeights; j++ ) + IEncodeFloat(s, vertsLeft, kWeight, j, src, stride); + + if( format & plGBufferGroup::kSkinIndices ) + { + const UInt32 idx = *(UInt32*)src; + s->WriteSwap32(idx); + src += 4; + } + } + + IEncodeNormal(s, src, stride); + + IEncodeColor(s, vertsLeft, src, stride); + + // COLOR2 + src += 4; + + const int numUVWs = format & plGBufferGroup::kUVCountMask; + int i; + for( i = 0; i < numUVWs; i++ ) + { + IEncodeFloat(s, vertsLeft, kUVW + i, 0, src, stride); + IEncodeFloat(s, vertsLeft, kUVW + i, 1, src, stride); + IEncodeFloat(s, vertsLeft, kUVW + i, 2, src, stride); + } +} + +inline void plVertCoder::IDecode(hsStream* s, UInt8*& dst, const UInt32 stride, const UInt8 format) +{ + IDecodeFloat(s, kPosition, 0, dst, stride); + IDecodeFloat(s, kPosition, 1, dst, stride); + IDecodeFloat(s, kPosition, 2, dst, stride); + + // Weights and indices? + const int numWeights = INumWeights(format); + if( numWeights ) + { + int j; + for( j = 0; j < numWeights; j++ ) + IDecodeFloat(s, kWeight, j, dst, stride); + + if( format & plGBufferGroup::kSkinIndices ) + { + UInt32* idx = (UInt32*)dst; + *idx = s->ReadSwap32(); + dst += 4; + } + } + + IDecodeNormal(s, dst, stride); + + IDecodeColor(s, dst, stride); + + // COLOR2 + UInt32* trash = (UInt32*)dst; + *trash = 0; + dst += 4; + + const int numUVWs = format & plGBufferGroup::kUVCountMask; + int i; + for( i = 0; i < numUVWs; i++ ) + { + IDecodeFloat(s, kUVW + i, 0, dst, stride); + IDecodeFloat(s, kUVW + i, 1, dst, stride); + IDecodeFloat(s, kUVW + i, 2, dst, stride); + } +} + +void plVertCoder::Read(hsStream* s, UInt8* dst, const UInt8 format, const UInt32 stride, const UInt16 numVerts) +{ + Clear(); + + int i = numVerts; + for( i = 0; i < numVerts; i++ ) + IDecode(s, dst, stride, format); +} + + +void plVertCoder::Write(hsStream* s, const UInt8* src, const UInt8 format, const UInt32 stride, const UInt16 numVerts) +{ + Clear(); + + UInt32 streamStart = s->GetPosition(); + + int numLeft = numVerts; + while( numLeft ) + { + IEncode(s, numLeft, src, stride, format); + numLeft--; + } + + fCodedVerts += numVerts; + fCodedBytes += (s->GetPosition() - streamStart); + fRawBytes += numVerts * stride; +} + +plVertCoder::plVertCoder() +{ + Clear(); +} + +plVertCoder::~plVertCoder() +{ +} + +void plVertCoder::Clear() +{ + memset(this, 0, sizeof(*this)); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plVertCoder.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plVertCoder.h new file mode 100644 index 00000000..25b71319 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plPipeline/plVertCoder.h @@ -0,0 +1,106 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plVertCoder_inc +#define plVertCoder_inc + +class hsStream; + +class plVertCoder +{ +public: + enum { + kPosition, + kWeight, + kUVW, + kNumFloatFields = kUVW + 8 + }; + +protected: + + class FloatCode + { + public: + hsScalar fOffset; + hsBool fAllSame; + UInt16 fCount; + }; + + FloatCode fFloats[kNumFloatFields][3]; + + class ByteCode + { + public: + UInt16 fCount; + UInt8 fVal; + UInt8 fSame; + }; + + ByteCode fColors[4]; + + static UInt32 fCodedVerts; + static UInt32 fCodedBytes; + static UInt32 fRawBytes; + static UInt32 fSkippedBytes; + + inline void ICountFloats(const UInt8* src, UInt16 maxCnt, const hsScalar quant, const UInt32 stride, hsScalar& lo, hsBool& allSame, UInt16& count); + inline void IEncodeFloat(hsStream* s, const UInt32 vertsLeft, const int field, const int chan, const UInt8*& src, const UInt32 stride); + inline void IDecodeFloat(hsStream* s, const int field, const int chan, UInt8*& dst, const UInt32 stride); + + inline void IEncodeNormal(hsStream* s, const UInt8*& src, const UInt32 stride); + inline void IDecodeNormal(hsStream* s, UInt8*& dst, const UInt32 stride); + + inline void ICountBytes(const UInt32 vertsLeft, const UInt8* src, const UInt32 stride, UInt16& len, UInt8& same); + inline void IEncodeByte(hsStream* s, const int chan, const UInt32 vertsLeft, const UInt8*& src, const UInt32 stride); + inline void IDecodeByte(hsStream* s, const int chan, UInt8*& dst, const UInt32 stride); + inline void IEncodeColor(hsStream* s, const UInt32 vertsLeft, const UInt8*& src, const UInt32 stride); + inline void IDecodeColor(hsStream* s, UInt8*& dst, const UInt32 stride); + + inline void IEncode(hsStream* s, const UInt32 vertsLeft, const UInt8*& src, const UInt32 stride, const UInt8 format); + inline void IDecode(hsStream* s, UInt8*& dst, const UInt32 stride, const UInt8 format); + +public: + plVertCoder(); + ~plVertCoder(); + + void Clear(); + + void Read(hsStream* s, UInt8* dst, const UInt8 format, const UInt32 stride, const UInt16 numVerts); + void Write(hsStream* s, const UInt8* src, const UInt8 format, const UInt32 stride, const UInt16 numVerts); + + + static void ClearAverage() { fCodedVerts = 0; fCodedBytes = 0; fRawBytes = 0; fSkippedBytes = 0; } + static UInt32 CodedBytes() { return fCodedBytes; } + static UInt32 RawBytes() { return fRawBytes; } + static UInt32 CodedVerts() { return fCodedVerts; } + static float AverageCodedVertSize() { return fCodedVerts ? float(fCodedBytes) / float(fCodedVerts) : 0; } + static float AverageRawVertSize() { return fCodedVerts ? float(fRawBytes) / float(fCodedVerts) : 0; } + + static UInt32 SkippedBytes() { return fSkippedBytes; } + static void AddSkippedBytes(UInt32 f) { fSkippedBytes += f; } +}; + +#endif // plVertCoder_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp new file mode 100644 index 00000000..98935e62 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.cpp @@ -0,0 +1,387 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plProgressMgr Functions // +// // +//// History ///////////////////////////////////////////////////////////////// +// // +// 10.26.2001 mcn - Created // +// // +////////////////////////////////////////////////////////////////////////////// + +#include +#include "hsTypes.h" +#include "plProgressMgr.h" +#include "hsTimer.h" + +#include "../plPipeline/plPlates.h" +#include "../Apps/plClient/res/resource.h" + +#include + + +////////////////////////////////////////////////////////////////////////////// +//// plProgressMgr Functions ///////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +plProgressMgr *plProgressMgr::fManager = nil; + +int plProgressMgr::fImageRotation[] = { + IDR_LOADING_01, + IDR_LOADING_18, + IDR_LOADING_17, + IDR_LOADING_16, + IDR_LOADING_15, + IDR_LOADING_14, + IDR_LOADING_13, + IDR_LOADING_12, + IDR_LOADING_11, + IDR_LOADING_10, + IDR_LOADING_09, + IDR_LOADING_08, + IDR_LOADING_07, + IDR_LOADING_06, + IDR_LOADING_05, + IDR_LOADING_04, + IDR_LOADING_03, + IDR_LOADING_02 +}; + +int plProgressMgr::fStaticTextIDs[] = { + 0, + IDR_LOADING_UPDATETEXT, +}; + +//// Constructor & Destructor //////////////////////////////////////////////// + +plProgressMgr::plProgressMgr() +{ + fOperations = nil; + fManager = this; + fCallbackProc = nil; + fCurrentStaticText = kNone; +} + +plProgressMgr::~plProgressMgr() +{ + while( fOperations != nil ) + delete fOperations; + fManager = nil; +} + +//// RegisterOperation /////////////////////////////////////////////////////// + +plOperationProgress* plProgressMgr::RegisterOperation(hsScalar length, const char *title, StaticText staticTextType, bool isRetry, bool alwaysDrawText) +{ + return IRegisterOperation(length, title, staticTextType, isRetry, false, alwaysDrawText); +} + +plOperationProgress* plProgressMgr::RegisterOverallOperation(hsScalar length, const char *title, StaticText staticTextType, bool alwaysDrawText) +{ + return IRegisterOperation(length, title, staticTextType, false, true, alwaysDrawText); +} + +plOperationProgress* plProgressMgr::IRegisterOperation(hsScalar length, const char *title, StaticText staticTextType, bool isRetry, bool isOverall, bool alwaysDrawText) +{ + if (fOperations == nil) + { + fCurrentStaticText = staticTextType; + Activate(); + } + + plOperationProgress *op = TRACKED_NEW plOperationProgress( length ); + + op->SetTitle( title ); + + if (fOperations) + { + fOperations->fBack = op; + op->fNext = fOperations; + } + fOperations = op; + + if (isRetry) + hsSetBits(op->fFlags, plOperationProgress::kRetry); + if (isOverall) + hsSetBits(op->fFlags, plOperationProgress::kOverall); + if (alwaysDrawText) + hsSetBits(op->fFlags, plOperationProgress::kAlwaysDrawText); + + IUpdateCallbackProc( op ); + + return op; +} + +void plProgressMgr::IUnregisterOperation(plOperationProgress* op) +{ + plOperationProgress* last = nil; + plOperationProgress* cur = fOperations; + + while (cur) + { + if (cur == op) + { + if (cur->fNext) + cur->fNext->fBack = last; + + if (last) + last->fNext = cur->fNext; + else + fOperations = cur->fNext; + + break; + } + + last = cur; + cur = cur->fNext; + } + + if (fOperations == nil) + { + fCurrentStaticText = kNone; + Deactivate(); + } +} + +//// IUpdateCallbackProc ///////////////////////////////////////////////////// + +void plProgressMgr::IUpdateFlags(plOperationProgress* progress) +{ + // Init update is done, clear it and set first update + if (hsCheckBits(progress->fFlags, plOperationProgress::kInitUpdate)) + { + hsClearBits(progress->fFlags, plOperationProgress::kInitUpdate); + hsSetBits(progress->fFlags, plOperationProgress::kFirstUpdate); + } + // First update is done, clear it + else if (hsCheckBits(progress->fFlags, plOperationProgress::kFirstUpdate)) + hsClearBits(progress->fFlags, plOperationProgress::kFirstUpdate); +} + +void plProgressMgr::IUpdateCallbackProc(plOperationProgress* progress) +{ + // Update the parent, if necessary + plOperationProgress* parentProgress = progress->GetNext(); + while (parentProgress && parentProgress->IsOverallProgress()) + { + parentProgress->IChildUpdateBegin(progress); + parentProgress = parentProgress->GetNext(); + } + + // Update everyone who wants to know about progress + IDerivedCallbackProc(progress); + if (fCallbackProc != nil) + fCallbackProc(progress); + + IUpdateFlags(progress); + + parentProgress = progress->GetNext(); + while (parentProgress && parentProgress->IsOverallProgress()) + { + parentProgress->IChildUpdateEnd(progress); + parentProgress = parentProgress->GetNext(); + } +} + +//// SetCallbackProc ///////////////////////////////////////////////////////// + +plProgressMgrCallbackProc plProgressMgr::SetCallbackProc( plProgressMgrCallbackProc proc ) +{ + plProgressMgrCallbackProc old = fCallbackProc; + fCallbackProc = proc; + return old; +} + +//// CancelAllOps //////////////////////////////////////////////////////////// + +void plProgressMgr::CancelAllOps( void ) +{ + plOperationProgress *op; + + + for( op = fOperations; op != nil; op = op->GetNext() ) + op->SetCancelFlag( true ); + + fCurrentStaticText = kNone; +} + +int plProgressMgr::GetLoadingFrameID(int index) +{ + if (index < (sizeof(fImageRotation) / sizeof(int))) + return fImageRotation[index]; + else + return fImageRotation[0]; +} + +int plProgressMgr::GetStaticTextID(StaticText staticTextType) +{ + return fStaticTextIDs[staticTextType]; +} + + +////////////////////////////////////////////////////////////////////////////// +//// plOperationProgress //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +plOperationProgress::plOperationProgress( hsScalar length ) : + fMax(length), + fValue(0), + fNext(nil), + fBack(nil), + fContext(0), + fFlags(kInitUpdate), + fStartTime(hsTimer::GetSeconds()), + fElapsedSecs(0), + fRemainingSecs(0), + fAmtPerSec(0.f) +{ + memset( fStatusText, 0, sizeof( fStatusText ) ); + memset( fTitle, 0, sizeof( fTitle ) ); +} + +plOperationProgress::~plOperationProgress() +{ + hsSetBits(fFlags, kLastUpdate); + if (!IsOverallProgress()) + plProgressMgr::GetInstance()->IUpdateCallbackProc(this); + plProgressMgr::GetInstance()->IUnregisterOperation(this); +} + +void plOperationProgress::IUpdateStats() +{ + double curTime = hsTimer::GetSeconds(); + double elapsed = 0; + if (curTime > fStartTime) + elapsed = curTime - fStartTime; + else + elapsed = fStartTime - curTime; + + hsScalar progress = GetProgress(); + + if (elapsed > 0) + fAmtPerSec = progress / hsScalar(elapsed); + else + fAmtPerSec = 0; + fElapsedSecs = (UInt32)elapsed; + if (progress < fMax) + fRemainingSecs = (UInt32)((fMax - progress) / fAmtPerSec); + else + fRemainingSecs = 0; +} + +void plOperationProgress::IChildUpdateBegin(plOperationProgress* child) +{ + if (child->IsFirstUpdate() && child->IsRetry()) + { + // We're retrying this file, so update the overall stats to reflect the additional download + fMax += child->GetMax(); + } + fValue += child->GetProgress(); + + IUpdateStats(); +} + +void plOperationProgress::IChildUpdateEnd(plOperationProgress* child) +{ + // If we're aborting, modify the total bytes to reflect any data we didn't download + if (hsCheckBits(child->fFlags, plOperationProgress::kAborting)) + fMax += child->GetProgress() - child->GetMax(); + else if (!child->IsLastUpdate()) + fValue -= child->GetProgress(); +} + +//// Increment /////////////////////////////////////////////////////////////// + +void plOperationProgress::Increment( hsScalar byHowMuch ) +{ + fValue += byHowMuch; + if( fValue > fMax ) + fValue = fMax; + IUpdateStats(); + + plProgressMgr::GetInstance()->IUpdateCallbackProc( this ); +} + +//// SetHowMuch ////////////////////////////////////////////////////////////// + +void plOperationProgress::SetHowMuch( hsScalar howMuch ) +{ + fValue = howMuch; + if( fValue > fMax ) + fValue = fMax; + IUpdateStats(); + + plProgressMgr::GetInstance()->IUpdateCallbackProc( this ); +} + +//// SetStatusText /////////////////////////////////////////////////////////// + +void plOperationProgress::SetStatusText( const char *text ) +{ + if( text != nil ) + strncpy( fStatusText, text, sizeof( fStatusText ) ); + else + fStatusText[ 0 ] = 0; +} + +//// SetTitle //////////////////////////////////////////////////////////////// + +void plOperationProgress::SetTitle( const char *text ) +{ + if (text != nil) + { + strncpy(fTitle, text, sizeof(fTitle)); + } + else + fTitle[0] = 0; +} + +//// SetLength /////////////////////////////////////////////////////////////// + +void plOperationProgress::SetLength( hsScalar length ) +{ + fMax = length; + if( fValue > fMax ) + fValue = fMax; + IUpdateStats(); + + plProgressMgr::GetInstance()->IUpdateCallbackProc( this ); +} + +void plOperationProgress::SetAborting() +{ + hsSetBits(fFlags, kAborting); + plProgressMgr::GetInstance()->IUpdateCallbackProc(this); + fMax = fValue = 0.f; + hsClearBits(fFlags, kAborting); +} + +void plOperationProgress::SetRetry() +{ + hsSetBits(fFlags, kRetry); + hsSetBits(fFlags, kFirstUpdate); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h new file mode 100644 index 00000000..557db452 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plProgressMgr/plProgressMgr.h @@ -0,0 +1,234 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plProgressMgr Header // +// // +//// Description ///////////////////////////////////////////////////////////// +// // +// The plProgressMgr is a method by which any part of the client can // +// display a progress bar indicating a lengthy operation. // +// Basically, a function/class/whatnot registers an operation with the // +// plProgressMgr. +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plProgressMgr_h +#define _plProgressMgr_h + +#include "hsTypes.h" +#include "hsUtils.h" + +class plPipeline; +class plPlate; + +//// plOperationProgress Definition ////////////////////////////////////////// +// The object you get back when you register a lengthy operation. Call this +// to update your progress, destroy it when you're done. + +class plProgressMgr; +class plOperationProgress +{ + friend class plProgressMgr; + friend class plDTProgressMgr; + + protected: + + hsScalar fValue, fMax; + char fStatusText[ 256 ]; + char fTitle[ 256 ]; + UInt32 fContext; + double fStartTime; + + UInt32 fElapsedSecs, fRemainingSecs; + hsScalar fAmtPerSec; + + enum Flags + { + kShouldCancel = 0x1, + kInitUpdate = 0x2, + kFirstUpdate = 0x4, + kLastUpdate = 0x8, + kAborting = 0x10, + kRetry = 0x20, + kOverall = 0x40, + kAlwaysDrawText = 0x80, + }; + UInt8 fFlags; + + plOperationProgress *fNext, *fBack; + + void IUpdateStats(); + + // For overall progress bars + void IChildUpdateBegin(plOperationProgress* child); + void IChildUpdateEnd(plOperationProgress* child); + + plOperationProgress( hsScalar length ); + + public: + + ~plOperationProgress(); + + hsScalar GetMax( void ) const { return fMax; } + hsScalar GetProgress( void ) const { return fValue; } + const char * GetTitle( void ) const { return fTitle; } + const char * GetStatusText( void ) const { return fStatusText; } + UInt32 GetContext( void ) const { return fContext; } + UInt32 GetElapsedSecs() { return fElapsedSecs; } + UInt32 GetRemainingSecs() { return fRemainingSecs; } + hsScalar GetAmtPerSec() { return fAmtPerSec; } + + // Adds on to current value + void Increment( hsScalar byHowMuch ); + + // Sets current value + void SetHowMuch( hsScalar byHowMuch ); + + // Set the length + void SetLength( hsScalar length ); + + // Sets the display text above the bar (nil for nothing) + void SetStatusText( const char *text ); + + // Sets the title + void SetTitle( const char *title ); + + // Application data + void SetContext( UInt32 context ) { fContext = context;} + + hsBool IsDone( void ) { return ( fValue < fMax ) ? false : true; } + + // True if this is the initial update (progress was just created) + bool IsInitUpdate() { return hsCheckBits(fFlags, kInitUpdate); } + // True if this is the first time the progress was updated + bool IsFirstUpdate() { return hsCheckBits(fFlags, kFirstUpdate); } + // Returns true if this is the last update you'll get from this + // operation (because it's getting deleted) + bool IsLastUpdate() { return hsCheckBits(fFlags, kLastUpdate); } + + // This type of progress is just tracking the overall progress of it's children + bool IsOverallProgress() { return hsCheckBits(fFlags, kOverall); } + + // Set if this progress is aborting before it completes. This will let any overall + // progress bars above this one know to adjust their totals to not include any amount + // that wasn't completed, and will set this progress bar to zero + void SetAborting(); + // If you're reusing an existing progress bar to retry a failed operation, call this. + // It will set the retry flag, and reset the progress bar so the next update will + // count as the first. If you set retry in RegisterOperation, don't use this too. + void SetRetry(); + bool IsRetry() { return hsCheckBits(fFlags, kRetry); } + + // The progress manager can decide at any time to cancel your operation on you. Check this + // value if you want to play nice and behave. + bool ShouldCancel() const { return hsCheckBits(fFlags, kShouldCancel); } + + bool AlwaysDrawText() const { return hsCheckBits(fFlags, kAlwaysDrawText); } + + // Please ignore this and don't use it unless you're me :P + plOperationProgress* GetPrev() const { return fBack; } + plOperationProgress* GetNext() const { return fNext; } + + // Or this + void SetCancelFlag( hsBool f ) { hsChangeBits(fFlags, kShouldCancel, f); } +}; + +// This is a callback proc you set that gets called every time the progressManager +// needs updating (like, say, you need to redraw progress bars). The client generally +// sets this callback and nobody should ever touch it. +typedef void(*plProgressMgrCallbackProc)( plOperationProgress* ); + +//// Manager Class Definition //////////////////////////////////////////////// + +class plProgressMgr +{ + friend class plOperationProgress; + + public: + // this must match the order of the fStaticTextIDs array + // for it to be useful + enum StaticText + { + kNone, + kUpdateText, + }; + + private: + + static plProgressMgr *fManager; + static int fImageRotation[]; + static int fStaticTextIDs[]; + + protected: + + plProgressMgr(); + + plOperationProgress *fOperations; + + plProgressMgrCallbackProc fCallbackProc; + + StaticText fCurrentStaticText; + + void IUpdateCallbackProc(plOperationProgress* progress); + // For derived classes to use, so they don't have to set a callback proc + virtual void IDerivedCallbackProc(plOperationProgress* progress) {} + + void IUpdateFlags(plOperationProgress* progress); + + plOperationProgress* IRegisterOperation(hsScalar length, const char *title, StaticText staticTextType, bool isRetry, bool isOverall, bool alwaysDrawText); + // Called by the operation + void IUnregisterOperation(plOperationProgress* op); + + virtual void Activate() {} + virtual void Deactivate() {} + + static plProgressMgr *IGetManager( void ) { return fManager; } + + public: + + virtual ~plProgressMgr(); + + static plProgressMgr* GetInstance() { return fManager; } + static int GetLoadingFrameID(int index); + static int GetStaticTextID(StaticText staticTextType); + + virtual void Draw( plPipeline *p ) { } + + plOperationProgress* RegisterOperation(hsScalar length, const char *title = nil, StaticText staticTextType = kNone, bool isRetry = false, bool alwaysDrawText = false); + plOperationProgress* RegisterOverallOperation(hsScalar length, const char *title = nil, StaticText staticTextType = kNone, bool alwaysDrawText = false); + + + plProgressMgrCallbackProc SetCallbackProc( plProgressMgrCallbackProc proc ); + + hsBool IsActive( void ) const { return ( fOperations != nil ) ? true : false; } + + void CancelAllOps( void ); +}; + + +#endif //_plProgressMgr_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plBSDiffBuffer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plBSDiffBuffer.cpp new file mode 100644 index 00000000..27428dd7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plBSDiffBuffer.cpp @@ -0,0 +1,628 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plBSDiffBuffer - A utility class for writing and applying a difference +// buffer--i.e. a buffer containing a series of modifications +// that will modify an old data buffer to match a new one. +// It's a useful utility class when doing binary file +// patching, for example, as you can write out the changes +// to this class, get back a data buffer suitable for writing, +// then use this class again later to reconstruct the new buffer. +// +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plBSDiffBuffer.h" +#include "hsUtils.h" +#include "hsStream.h" + +#define plBSDiffBuffer_MIN(x,y) (((x)<(y)) ? (x) : (y)) + +////////////////////////////////////////////////////////////////////////////// +//// Constructor/Destructors ///////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Creation Constructor //////////////////////////////////////////////////// +// Use this constructor when creating a new diff buffer. Pass in the length +// of the final new buffer. You don't need to pass in the length of the old +// buffer, but if you do, it'll help this class do some internal space +// optimization. + +plBSDiffBuffer::plBSDiffBuffer( UInt32 newLength, UInt32 oldLength ) +: fNewLength( newLength ) +, fPatchLength( 0 ) +{ + fPatchBuffer = nil; + fWriting = true; +} + +//// Application Constructor ///////////////////////////////////////////////// +// Use this constructor when taking a diff buffer and applying it to an old +// buffer. Pass in a pointer to the diff buffer and its length. The buffer +// will be copied, so you don't need to keep it around after you construct +// this object. + +plBSDiffBuffer::plBSDiffBuffer( void *buffer, UInt32 length ) +: fNewLength( 0 ) +, fPatchLength( 0 ) +, fPatchBuffer( nil ) +, fWriting( false ) +{ + if (!buffer || length < 32) + hsAssert(false, "Corrupt Patch Buffer!"); + + if((fPatchBuffer=TRACKED_NEW unsigned char[length+1])!=nil) + { + fPatchLength = length; + memcpy(fPatchBuffer, buffer, fPatchLength); + } +} + +plBSDiffBuffer::~plBSDiffBuffer() +{ + if ( fPatchBuffer ) + delete[] fPatchBuffer; +} + + +////////////////////////////////////////////////////////////////////////////// +//// Creation/Write Functions //////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Diff ///////////////////////////////////////////////////////////////////// +// Diff() creates the diff buffer from the new and old. +UInt32 plBSDiffBuffer::Diff( UInt32 oldLength, void *oldBuffer, UInt32 newLength, void *newBuffer ) +{ + hsAssert( fWriting, "Trying to Add() to a difference buffer that's reading" ); + + Int32 *I,*V; + + Int32 scan,pos,len; + Int32 lastscan,lastpos,lastoffset; + Int32 oldscore,scsc; + + Int32 s,Sf,lenf,Sb,lenb; + Int32 overlap,Ss,lens; + Int32 i; + + UInt32 cblen,dblen,eblen; + unsigned char *oldbuf, *newbuf; + unsigned char *cb,*db,*eb; + + unsigned char header[32]; + + if (oldBuffer == nil || + oldLength < 0 || + newBuffer == nil || + newLength < 0) + { + return (-1); + } + + oldbuf = (unsigned char *)oldBuffer; + newbuf = (unsigned char *)newBuffer; + + if(((I=TRACKED_NEW Int32[oldLength+1])==nil) || + ((V=TRACKED_NEW Int32[oldLength+1])==nil)) + { + delete[] I; + delete[] V; + return fPatchLength; + } + + IQSuffixSort(I,V,oldbuf,oldLength); + + delete[] V; + + /* + * These could probably be smaller, especially cb. + */ + if(((cb=TRACKED_NEW unsigned char[newLength+1])==nil) || + ((db=TRACKED_NEW unsigned char[newLength+1])==nil) || + ((eb=TRACKED_NEW unsigned char[newLength+1])==nil)) + { + delete[] I; + delete[] cb; + delete[] db; + delete[] eb; + return fPatchLength; + } + cblen=0; + dblen=0; + eblen=0; + + /* Header is + 0 8 "BSDIFF40" + 8 8 length of bzip2ed ctrl block + 16 8 length of bzip2ed diff block + 24 8 length of new file */ + /* File is + 0 32 Header + 32 ?? Bzip2ed ctrl block + ?? ?? Bzip2ed diff block + ?? ?? Bzip2ed extra block */ + memcpy(header,"BSDIFF40",8); + memset(header+8,0,24); + + scan=0;len=0; + lastscan=0;lastpos=0;lastoffset=0; + while(scanoldscore+8)) break; + + if((scan+lastoffsetSf*2-lenf) { Sf=s; lenf=i; }; + }; + + lenb=0; + if(scan=lastscan+i)&&(pos>=i);i++) { + if(oldbuf[pos-i]==newbuf[scan-i]) s++; + if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; }; + }; + }; + + if(lastscan+lenf>scan-lenb) { + overlap=(lastscan+lenf)-(scan-lenb); + s=0;Ss=0;lens=0; + for(i=0;iSs) { Ss=s; lens=i+1; }; + }; + + lenf+=lens-overlap; + lenb-=lens; + }; + + for(i=0;istart) ISplit(I,V,start,jj-start,h); + + for(i=0;ikk) ISplit(I,V,kk,start+len-kk,h); +} + +void plBSDiffBuffer::IQSuffixSort(Int32 *I,Int32 *V,unsigned char *old,UInt32 oldsize) +{ + UInt32 buckets[256]; + UInt32 i,h,len; + + for(i=0;i<256;i++) buckets[i]=0; + for(i=0;i0;i--) buckets[i]=buckets[i-1]; + buckets[0]=0; + + for(i=0;iy) { + *pos=I[st]; + return x; + } else { + *pos=I[en]; + return y; + } + }; + + x=st+(en-st)/2; + if(memcmp(oldBuffer+I[x],newBuffer,plBSDiffBuffer_MIN(oldLength-I[x],newLength))<0) { + return ISearch(I,oldBuffer,oldLength,newBuffer,newLength,x,en,pos); + } else { + return ISearch(I,oldBuffer,oldLength,newBuffer,newLength,st,x,pos); + }; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plBSDiffBuffer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plBSDiffBuffer.h new file mode 100644 index 00000000..4ae84189 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plBSDiffBuffer.h @@ -0,0 +1,111 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plBSDiffBuffer - A utility class for writing and applying a difference +// buffer--i.e. a buffer containing a series of modifications +// that will modify an old data buffer to match a new one. +// It's a useful utility class when doing binary file +// patching, for example, as you can write out the changes +// to this class, get back a data buffer suitable for writing, +// then use this class again later to reconstruct the new buffer. +// +// This class is copied in structure (not substance) from +// plDiffBuffer. It is based on bsdiff-4.1 from BSD +// Linux (http://www.daemonology.org/bsdiff). It's *extremely* +// hard to read code (written by a PhD), but it works well. The +// original BSD code has been modified to have the bzip2 pipes +// it used to compress data removed. It has also been converted +// to work a C++ utility class. +// +// There isn't really an Add or Copy command in bsdiff. It just +// uses three control numbers and two diff/data buffers. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plBSDiffBuffer_h +#define _plBSDiffBuffer_h + +#include "hsTypes.h" +#include "hsStream.h" + +//// Class Definition //////////////////////////////////////////////////////// + +class hsRAMStream; +class plBSDiffBuffer +{ + protected: + + hsBool fWriting; + UInt32 fNewLength, fPatchLength; + unsigned char* fPatchBuffer; + + public: + + plBSDiffBuffer( UInt32 newLength, UInt32 oldLength = 0 ); // Constructor for writing new buffers. oldLength isn't required but helpful for optimizations + plBSDiffBuffer( void *buffer, UInt32 length ); // Constructor for applying a given diff set + // to an old buffer + virtual ~plBSDiffBuffer(); + + + /// Creation/write functions + + // Diff() creates the diff buffer from the new and old. + UInt32 Diff( UInt32 oldLength, void *oldBuffer, UInt32 newLength, void *newBuffer ); + + // GetBuffer() will copy the diff stream into a new buffer and return it. You are responsible for freeing the buffer. + void GetBuffer( UInt32 &length, void *&bufferPtr ); + + + /// Apply functions + + // Apply() is another way to call Patch(). + UInt32 Apply( UInt32 oldLength, void *oldBuffer, UInt32 &newLength, void *&newBuffer ) + { return Patch(oldLength, oldBuffer, newLength, newBuffer); }; + + // Patch() will take this diff buffer and apply it to the given old buffer, + // allocating and producing a new buffer. You are responsible for freeing the new buffer. + UInt32 Patch( UInt32 oldLength, void *oldBuffer, UInt32 &newLength, void *&newBuffer ); + + private: + + UInt32 IReadUnsignedInt8(unsigned char *buf); + void IWriteUnsignedInt8(UInt32 x,unsigned char *buf); + void ISafeMemcpy(unsigned char *dest, unsigned char *src, size_t nbytes, + unsigned char *destend, unsigned char *srcend); + void ISplit(Int32 *I,Int32 *V,UInt32 start,UInt32 len,UInt32 h); + void IQSuffixSort(Int32 *I,Int32 *V,unsigned char *old,UInt32 oldsize); + UInt32 IMatchLen( unsigned char *oldBuffer, UInt32 oldLength, + unsigned char *newBuffer, UInt32 newLength); + UInt32 ISearch( Int32 *I, + unsigned char *oldBuffer, UInt32 oldLength, + unsigned char *newBuffer, UInt32 newLength, + UInt32 st, UInt32 en, Int32 *pos); + + +}; + +#endif // _plBSDiffBuffer_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plDiffBuffer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plDiffBuffer.cpp new file mode 100644 index 00000000..a11b6a47 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plDiffBuffer.cpp @@ -0,0 +1,250 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plDiffBuffer - A utility class for writing and applying a difference +// buffer--i.e. a buffer containing a series of modifications +// (specifically, adds and copys) that will modify an old +// data buffer to match a new one. It's a useful utility +// class when doing binary file patching, for example, as you +// can write out the changes to this class, get back a data +// buffer suitable for writing, then use this class again +// later to reconstruct the new buffer. +// +//// History ///////////////////////////////////////////////////////////////// +// +// 7.24.2002 mcn - Created (Happy late b-day to me!) +// +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plDiffBuffer.h" +#include "plBSDiffBuffer.h" +#include "hsUtils.h" +#include "hsStream.h" + + +////////////////////////////////////////////////////////////////////////////// +//// Constructor/Destructors ///////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Creation Constructor //////////////////////////////////////////////////// +// Use this constructor when creating a new diff buffer. Pass in the length +// of the final new buffer. You don't need to pass in the length of the old +// buffer, but if you do, it'll help this class do some internal space +// optimization. + +plDiffBuffer::plDiffBuffer( UInt32 newLength, UInt32 oldLength ) +: fBSDiffBuffer( nil ) +, fIsBSDiff( false ) +{ + // Basically, if the new and old lengths can both fit into 16 bits + // (not including a potential negation), then we can store all the + // segment info as 16-bit values instead of 32. + if( oldLength > 0 && oldLength < 32767 && newLength < 32767 ) + f16BitMode = true; + else + f16BitMode = false; + + fNewLength = newLength; + fStream = TRACKED_NEW hsRAMStream(); + fStream->WriteSwap32( fNewLength ); + fStream->WriteBool( f16BitMode ); + fWriting = true; +} + +//// Application Constructor ///////////////////////////////////////////////// +// Use this constructor when taking a diff buffer and applying it to an old +// buffer. Pass in a pointer to the diff buffer and its length. The buffer +// will be copied, so you don't need to keep it around after you construct +// this object. + +plDiffBuffer::plDiffBuffer( void *buffer, UInt32 length ) +: fBSDiffBuffer( nil ) +, fStream( nil ) +, fIsBSDiff( false ) +, fWriting( false ) +{ + // Check to see if this uses the newer BSDiff format + if ( buffer && length > 32 && + memcmp(buffer,"BSDIFF40",8)==0 ) + { + // This is a bsdiff buffer. Use plBSDiffBuffer to handle it. + fBSDiffBuffer = TRACKED_NEW plBSDiffBuffer(buffer, length); + fIsBSDiff = true; + } + else + { + fStream = TRACKED_NEW hsRAMStream(); + fStream->Write( length, buffer ); + fStream->Rewind(); + + fNewLength = fStream->ReadSwap32(); + f16BitMode = fStream->ReadBool(); + } +} + +plDiffBuffer::~plDiffBuffer() +{ + if (fStream) + delete fStream; + if (fBSDiffBuffer) + delete fBSDiffBuffer; +} + + +////////////////////////////////////////////////////////////////////////////// +//// Creation/Write Functions //////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Add ///////////////////////////////////////////////////////////////////// +// Add() appends an Add-New-Data operation to the diff buffer. The data +// supplied will be copied internally, so you can discard it after you call +// this function. + +void plDiffBuffer::Add( Int32 length, void *newData ) +{ + hsAssert( fWriting, "Trying to Add() to a difference buffer that's reading" ); + + // We flag our two different op types by the sign of the length. Negative + // lengths are an add operation, positive ones are copy ops. + if( f16BitMode ) + fStream->WriteSwap16( -( (Int16)length ) ); + else + fStream->WriteSwap32( -length ); + fStream->Write( length, newData ); +} + +//// Copy //////////////////////////////////////////////////////////////////// +// Copy() appends a Copy-Data-From-Old operation to the diff buffer. + +void plDiffBuffer::Copy( Int32 length, UInt32 oldOffset ) +{ + hsAssert( fWriting, "Trying to Copy() to a difference buffer that's reading" ); + + // We flag our two different op types by the sign of the length. Negative + // lengths are an add operation, positive ones are copy ops. + if( f16BitMode ) + { + fStream->WriteSwap16( (Int16)length ); + fStream->WriteSwap16( (UInt16)oldOffset ); + } + else + { + fStream->WriteSwap32( length ); + fStream->WriteSwap32( oldOffset ); + } +} + +//// GetBuffer /////////////////////////////////////////////////////////////// +// GetBuffer() will copy the diff stream into a new buffer and return it. +// You are responsible for freeing the buffer. Call this once you're done +// adding ops and want the raw data to write out somewhere. Note: this +// function will rewind the diff stream, so once you call it, you can't do +// anything else on the object. + +void plDiffBuffer::GetBuffer( UInt32 &length, void *&bufferPtr ) +{ + hsAssert( fWriting, "Trying to GetBuffer() on a difference buffer that's reading" ); + + length = fStream->GetPosition(); + bufferPtr = (void *)TRACKED_NEW UInt8[ length ]; + + fStream->Rewind(); + fStream->Read( length, bufferPtr ); +} + + +////////////////////////////////////////////////////////////////////////////// +//// Application Functions /////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Apply /////////////////////////////////////////////////////////////////// +// Apply() will take this diff buffer and apply it to the given old buffer, +// allocating and producing a new buffer. You are responsible for freeing +// the new buffer. + +#define hsAssertAndBreak( cond, msg ) { if( cond ) { hsAssert( false, msg ); break; } } + +void plDiffBuffer::Apply( UInt32 oldLength, void *oldBuffer, UInt32 &newLength, void *&newBuffer ) +{ + hsAssert( !fWriting, "Trying to Apply() a difference buffer that's writing" ); + + // Is this is a BSDiff patch, use plBSDiffBuffer and return. + if (fIsBSDiff) + { + fBSDiffBuffer->Apply(oldLength, oldBuffer, newLength, newBuffer); + return; + } + + /// Step 1: Allocate the new buffer + newLength = fNewLength; + UInt8 *new8Buffer = TRACKED_NEW UInt8[ newLength ]; + UInt8 *old8Buffer = (UInt8 *)oldBuffer; + newBuffer = (void *)new8Buffer; + + + /// Step 2: Loop through the difference stream + Int32 opLength; + UInt32 newBufferPos = 0; + while( newBufferPos < newLength ) + { + // Read in the op length + if( f16BitMode ) + { + Int16 opLen16 = fStream->ReadSwap16(); + if( opLen16 < 0 ) + opLength = -( (Int32)( -opLen16 ) ); + else + opLength = (UInt32)opLen16; + } + else + opLength = fStream->ReadSwap32(); + + // As defined, negative ops are add ops, positive ones are copys + if( opLength < 0 ) + { + hsAssertAndBreak( newBufferPos - opLength > newLength, "Destination buffer offset in plDiffBuffer() is out of range!" ); + + // Add op, read in the added data + fStream->Read( -opLength, &new8Buffer[ newBufferPos ] ); + newBufferPos += -opLength; + } + else + { + // Copy op, so get the old offset and copy from there + UInt32 oldOffset = f16BitMode ? fStream->ReadSwap16() : fStream->ReadSwap32(); + + hsAssertAndBreak( newBufferPos + opLength > newLength, "Destination buffer offset in plDiffBuffer() is out of range!" ); + hsAssertAndBreak( oldOffset + opLength > oldLength, "Difference buffer offset in plDiffBuffer() is out of range of the old buffer!" ); + + memcpy( &new8Buffer[ newBufferPos ], old8Buffer + oldOffset, opLength ); + newBufferPos += opLength; + } + } + + hsAssert( newBufferPos == newLength, "Invalid sequence of difference ops in plDiffBuffer::Apply()" ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plDiffBuffer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plDiffBuffer.h new file mode 100644 index 00000000..0afa78a7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plDiffBuffer.h @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plDiffBuffer - A utility class for writing and applying a difference +// buffer--i.e. a buffer containing a series of modifications +// (specifically, adds and copys) that will modify an old +// data buffer to match a new one. It's a useful utility +// class when doing binary file patching, for example, as you +// can write out the changes to this class, get back a data +// buffer suitable for writing, then use this class again +// later to reconstruct the new buffer. +// +// This class is meant to construct diff buffers using two +// ops: add and copy. Basically, the syntax is defined so +// that to reconstruct the new buffer, you run through the +// list of ops sequentially, each one defining the next +// chunk of data in the new buffer. Add ops will add new data +// to the buffer that didn't exist in the old buffer, and +// copy ops will copy data that existed in the old buffer +// (from an arbitrary offset, to facilitate encoding data +// shuffling). Delete ops are implicit, as they simply aren't +// defined in the stream of ops. +// +//// History ///////////////////////////////////////////////////////////////// +// +// 7.24.2002 mcn - Created (Happy late b-day to me!) +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDiffBuffer_h +#define _plDiffBuffer_h + +#include "hsTypes.h" +#include "hsStream.h" + +//// Class Definition //////////////////////////////////////////////////////// + +class hsRAMStream; +class plBSDiffBuffer; +class plDiffBuffer +{ + protected: + + hsBool fWriting, f16BitMode; + UInt32 fNewLength; + hsRAMStream *fStream; + + // Support for BSDiff patch buffers (Patching only) + plBSDiffBuffer *fBSDiffBuffer; + hsBool fIsBSDiff; + + public: + + plDiffBuffer( UInt32 newLength, UInt32 oldLength = 0 ); // Constructor for writing new buffers. oldLength isn't required but helpful for optimizations + plDiffBuffer( void *buffer, UInt32 length ); // Constructor for applying a given diff set + // to an old buffer + virtual ~plDiffBuffer(); + + + /// Creation/write functions + + // Add() appends an Add-New-Data operation to the diff buffer. The data supplied will be copied internally. + void Add( Int32 length, void *newData ); + + // Copy() appends a Copy-Data-From-Old operation to the diff buffer + void Copy( Int32 length, UInt32 oldOffset ); + + // GetBuffer() will copy the diff stream into a new buffer and return it. You are responsible for freeing the buffer. + void GetBuffer( UInt32 &length, void *&bufferPtr ); + + + /// Apply functions + + // Apply() will take this diff buffer and apply it to the given old buffer, allocating and producing a new buffer. You are responsible for freeing the new buffer. + void Apply( UInt32 oldLength, void *oldBuffer, UInt32 &newLength, void *&newBuffer ); + +}; + +#endif // _plDiffBuffer_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plIndexFile.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plIndexFile.cpp new file mode 100644 index 00000000..f6fccb5f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plIndexFile.cpp @@ -0,0 +1,195 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#define plIndexFile_cpp // for version numbers in plVersion.h + +#include "plIndexFile.h" +#include "hsStream.h" +#include "plVersion.h" + +//-------------------- +// plIndexFileHeader +//-------------------- + +plIndexFileHeader::plIndexFileHeader() +{ + fTimeStamp[ 0 ] = fTimeStamp[ 1 ] = 0; + fStrTblStartPos = 0; + fStrTblKnt = 0; + fMajorVersion = plVersion::GetMajorVersion(); + fMinorVersion = plVersion::GetMinorVersion(); + fExportLocal = 0; +} + +void plIndexFileHeader::Write(hsStream *s) +{ + s->WriteSwap32(2,fTimeStamp); + s->WriteSwap32(fStrTblStartPos); + s->WriteSwap16(fStrTblKnt); + s->WriteSwap16(fMajorVersion); + s->WriteSwap16(fMinorVersion); + s->WriteSwap16(fExportLocal); +} +void plIndexFileHeader::Read(hsStream *s) +{ + s->ReadSwap32(2,fTimeStamp); + fStrTblStartPos = s->ReadSwap32(); + fStrTblKnt = s->ReadSwap16(); + fMajorVersion = s->ReadSwap16(); + fMinorVersion = s->ReadSwap16(); + fExportLocal = s->ReadSwap16(); +} + +//-------------------- +// plIndexFileRoom +//-------------------- + +void plIndexFileRoom::Write(hsStream *s) +{ + fRmUoid.Write(s); + s->WriteSwap32(fTypesInRoom); + s->WriteSwap16(fFiller1_16); + s->WriteSwap32(fFiller2_32); + s->WriteSwap32(fFiller3_32); +} +void plIndexFileRoom::Read(hsStream *s) +{ + fRmUoid.Read(s); + fTypesInRoom = s->ReadSwap32(); + s->ReadSwap16(); + s->ReadSwap32(); + s->ReadSwap32(); +} +//-------------------- +// plIndexFileType +//-------------------- + + +void plIndexFileType::Write(hsStream *s) +{ + fTypeUoid.Write(s); + s->WriteSwap32(fNumKeys); + s->WriteSwap32(fFiller1_32); + s->WriteSwap32(fFiller2_32); +} + +void plIndexFileType::Read(hsStream *s) +{ + fTypeUoid.Read(s); + fNumKeys = s->ReadSwap32(); + s->ReadSwap32(); + s->ReadSwap32(); +} +//-------------------- +// plIndexFileKey +//-------------------- + + +void plIndexFileKey::Write(hsStream *s) +{ + fKeyUoid.Write(s); + s->WriteSwap32(fStartPos); + s->WriteSwap32(fDataLen); + s->WriteSwap16(fNameIx); + s->WriteSwap16(fFiller1_16); +} + + +void plIndexFileKey::Read(hsStream *s) +{ + fKeyUoid.Read(s); + fStartPos = s->ReadSwap32(); + fDataLen = s->ReadSwap32(); + fNameIx = s->ReadSwap16(); + s->ReadSwap16(); +} + + + + +//-------------------- +// plIxStrTbl +//-------------------- + +plIxStrTbl::~plIxStrTbl() +{ + if (fpStrings) delete []fpStrings; // if strings came from elsewhere, not our responsibility +} + +UInt16 plIxStrTbl::AddString(const char *p) +{ Int16 ix = FindString(p); + if (ix != -1) + return ix; // duplicate + fStringTbl.push_back(p); return fStringTbl.size() - 1; +} + +Int16 plIxStrTbl::FindString(const char *p) +{ + for (int i=0; i < fStringTbl.size(); i++) + { + if (!_stricmp(p,fStringTbl[i])) + return i; + } + return -1; +} + +void plIxStrTbl::Write(hsStream *s) +{ + for (int i=0; i < fStringTbl.size(); i++) + { Int32 len= fStringTbl[i] ? strlen(fStringTbl[i]) : 0; + hsAssert(len < 256,"Name string too long"); + UInt8 l = (UInt8) len; + s->WriteByte(l); // FUTURE, don't really need length! + if (len) + { + s->Write(len, fStringTbl[i]); + } + s->WriteByte(0); // Null terminate + } +} + +void plIxStrTbl::Read(hsStream *s) +{ UInt32 pos = s->GetPosition(); + s->FastFwd(); + fTabSize = s->GetPosition() - pos; // Get size of table + s->SetPosition(pos); + fpStrings = new char[fTabSize]; + hsAssert(fpStrings,"new failed"); + s->Read(fTabSize,fpStrings); // Read all the string in + + char *p = fpStrings; + while (p < fpStrings + fTabSize) + { + UInt8 len = *p; + p++; + hsAssert(p < fpStrings + fTabSize,"String Index error"); + fStringTbl.push_back(p); + p += len + 1; // past len and NULL + }; +} + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plIndexFile.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plIndexFile.h new file mode 100644 index 00000000..2dfba62e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plIndexFile.h @@ -0,0 +1,152 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLINDEXFILE_H +#define PLINDEXFILE_H + +#include "../pnKeyedObject/plUoid.h" +#include + //--------------------------------------------------------------- + // These Classes are used to Read and Write the Index file for the Database + // Records are kept the same size Currently 20 Bytes + // At the End of the file, the Strings are stored + // plIxStrTbl is used to Read and Write the String section of the Index + //--------------------------------------------------------------- + + //--------------------------------------------------------------- + // Main header Entry, One per Index file + //--------------------------------------------------------------- +class plIndexFileHeader +{ +public: + plIndexFileHeader(); // I buried Paul + ~plIndexFileHeader(){} + + UInt32 fTimeStamp[2]; + UInt32 fStrTblStartPos; // where the String table starts in the Index file + UInt16 fStrTblKnt; // how many strings in the string table + UInt16 fMajorVersion; + UInt16 fMinorVersion; + UInt16 fExportLocal; // was this file exported locally or downloaded from server? + + void Write(hsStream *s); + void Read(hsStream *s); +}; + + //--------------------------------------------------------------- + // Room Entry One Entry per Room + //--------------------------------------------------------------- +class plIndexFileRoom +{ +public: + plIndexFileRoom() { memset(this,1,sizeof(this)); } // no virtuals...relax + ~plIndexFileRoom(){} + + plUoid fRmUoid; + UInt16 fTypesInRoom; + UInt16 fFiller1_16; + UInt32 fFiller2_32; + UInt32 fFiller3_32; + + void Write(hsStream *s); + void Read(hsStream *s); +}; + + //--------------------------------------------------------------- + // Type Entry One Entry per Type in each Room + //--------------------------------------------------------------- + +class plIndexFileType +{ +public: + plIndexFileType() { memset(this,1,sizeof(this)); } // no virtuals...relax + ~plIndexFileType(){} + + plUoid fTypeUoid; + UInt16 fNumKeys; + UInt32 fFiller1_32; + UInt32 fFiller2_32; + + void Write(hsStream *s); + void Read(hsStream *s); +}; + + //--------------------------------------------------------------- + // Key Entry One Entry per Type in each Room + //--------------------------------------------------------------- + +class plIndexFileKey +{ +public: + plIndexFileKey() { memset(this,1,sizeof(this)); } // no virtuals...relax + ~plIndexFileKey(){} + + + plUoid fKeyUoid; + UInt32 fStartPos; + UInt32 fDataLen; + UInt16 fNameIx; // Index into string table of name + UInt16 fFiller1_16; + + void Write(hsStream *s) ; + void Read(hsStream *s); +}; + + //--------------------------------------------------------------- + // String Table, Lives at the end of the Index File + //--------------------------------------------------------------- +class plIxStrTbl +{ + std::vectorfStringTbl; + char *fpStrings; + UInt32 fTabSize; // buffer size for strings + +public: + + plIxStrTbl() :fpStrings(nil), fTabSize(0){} + ~plIxStrTbl(); + + Int16 FindString(const char *p); // returns -1 if not found, otherwise the index from zero + UInt16 AddString(const char *p); + UInt16 NumStrings() { return fStringTbl.size(); } + const char * GetString(UInt16 x) { return fStringTbl[x]; } + + void Write(hsStream *s); + void Read(hsStream *s); +}; + +class plLinkRecord +{ +public: + plLinkRecord(UInt16 a, UInt16 d, UInt16 r) : fAgeIx(a), fDistIx(d), fRoomIx(r){} + ~plLinkRecord(){} + + UInt16 fAgeIx; // Index into string table + UInt16 fDistIx; + UInt16 fRoomIx; + UInt32 fTimeStamp[2]; +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plKeyFinder.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plKeyFinder.cpp new file mode 100644 index 00000000..8befe2f3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plKeyFinder.cpp @@ -0,0 +1,498 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plKeyFinder.h" + +#include "hsTemplates.h" +#include "hsStlUtils.h" + +#include "hsResMgr.h" +#include "plResManager.h" + +#include "plRegistryHelpers.h" +#include "plRegistryNode.h" +#include "plRegistryKeyList.h" +#include "plPageInfo.h" +#include "../pnFactory/plFactory.h" +#include "hsUtils.h" +#include "plCreatableIndex.h" + +plResManager* IGetResMgr() { return (plResManager*)hsgResMgr::ResMgr(); } + +plKeyFinder& plKeyFinder::Instance() +{ + static plKeyFinder theInstance; + return theInstance; +} + +const char* plKeyFinder::GetLastErrorString() // For Console display +{ + // For Console display + static const char* KeyFinderErrors[] = + { + "Ok", + "Age not found", + "Page not found", + "Invalid class", + "None of those classes in this page", + "Object not found" + }; + return KeyFinderErrors[fLastError]; +} + +// +// Does name string compare with potentially mangled (ie. [1 0 0]foo) names +// +hsBool NameMatches(const char* obName, const char* pKName, hsBool subString) +{ + if (!obName || !pKName) + return false; + + const char *o = obName; + const char *p = pKName; + + // If names are mangled, unmangle + if (*o != '[' || *p != '[') + { + // skip past ']' in both names in case mangled + while (*o && *o != ']') + o++; + o = (*o==']') ? o+1 : obName; + + while (*p && *p != ']') + p++; + p = (*p==']') ? p+1 : pKName; + } + + if (!subString) + { + if (!_stricmp(o, p)) + return true; // FOUND IT!!!!!!!!!!!!!!!!!!! + } + else + { + char oCopy[256], pCopy[256]; + strcpy(oCopy, o); + strcpy(pCopy, p); + hsStrLower(oCopy); + hsStrLower(pCopy); + if (strstr(pCopy, oCopy)) + return true; + } + + return false; +} + +plKey plKeyFinder::StupidSearch(const char * age, const char * rm, + const char *className, const char *obName, hsBool subString) +{ + UInt16 ty = plFactory::FindClassIndex(className); + return StupidSearch(age, rm, ty, obName, subString); +} + +class plKeyFinderIter : public plRegistryKeyIterator, public plRegistryPageIterator +{ +protected: + UInt16 fClassType; + const char *fObjName; + hsBool fSubstr; + plKey fFoundKey; + const char *fAgeName; + +public: + plKey GetFoundKey( void ) const { return fFoundKey; } + + plKeyFinderIter( UInt16 classType, const char *obName, hsBool substr ) + : fFoundKey( nil ), fClassType( classType ), fObjName( obName ), fSubstr( substr ) { } + + plKeyFinderIter( UInt16 classType, const char *obName, hsBool substr, const char *ageName ) + : fFoundKey( nil ), fClassType( classType ), fObjName( obName ), fSubstr( substr ), + fAgeName( ageName ) {} + + virtual hsBool EatKey( const plKey& key ) + { + if( key->GetUoid().GetClassType() == fClassType && + NameMatches( fObjName, key->GetUoid().GetObjectName(), fSubstr ) ) + { + fFoundKey = key; + return false; + } + + return true; + } + + virtual hsBool EatPage( plRegistryPageNode *pageNode ) + { +#ifndef _DEBUG + try + { +#endif + if( stricmp( pageNode->GetPageInfo().GetAge(), fAgeName ) == 0 ) + { + // Try loading and searching thru this page + hsTArray keyRefs; + + IGetResMgr()->LoadPageKeys( pageNode ); + plKeyCollector coll( keyRefs ); + pageNode->IterateKeys( &coll ); + + if( !pageNode->IterateKeys( this ) ) + return false; + } +#ifndef _DEBUG + } catch (...) + { + } +#endif + return true; + } +}; + +plKey plKeyFinder::StupidSearch(const char * age, const char * rm, + UInt16 classType, const char *obName, hsBool subString) +{ + if (!obName) + return nil; + + plUoid newOid; + + fLastError = kOk; + + UInt16 maxClasses = plFactory::GetNumClasses(); + + UInt16 ty = classType; + if (ty == maxClasses) // error + { fLastError = kInvalidClass; + return nil; + } + + if( age != nil && rm != nil ) + { + const plLocation &loc = IGetResMgr()->FindLocation( age, rm ); + if( !loc.IsValid() ) + { + fLastError = kPageNotFound; + return nil; + } + + plKeyFinderIter keyFinder( classType, obName, subString ); + + if( !IGetResMgr()->IterateKeys( &keyFinder, loc ) ) + // Return value of false means it stopped somewhere, i.e. found something + return keyFinder.GetFoundKey(); + } + else if( age != nil ) + { + plKeyFinderIter keyFinder(classType, obName, subString, age); + + if( !IGetResMgr()->IterateAllPages( &keyFinder ) ) + return keyFinder.GetFoundKey(); + } + else + { + plKeyFinderIter keyFinder( classType, obName, subString ); + + if( !IGetResMgr()->IterateKeys( &keyFinder ) ) + // Return value of false means it stopped somewhere, i.e. found something + return keyFinder.GetFoundKey(); + } + + fLastError = kObjectNotFound; + return nil; +} + +void plKeyFinder::ReallyStupidResponderSearch(const char *name, std::vector& foundKeys, const plLocation &hintLocation ) +{ + ReallyStupidSubstringSearch(name, CLASS_INDEX_SCOPED(plResponderModifier), foundKeys, hintLocation); +} + +void plKeyFinder::ReallyStupidActivatorSearch(const char *name, std::vector& foundKeys, const plLocation &hintLocation) +{ + // use the createable macro so we don't have to pull in all of Python + ReallyStupidSubstringSearch(name, CLASS_INDEX_SCOPED(plLogicModifier), foundKeys, hintLocation); + ReallyStupidSubstringSearch(name, CLASS_INDEX_SCOPED(plPythonFileMod), foundKeys, hintLocation); + ReallyStupidSubstringSearch(name, CLASS_INDEX_SCOPED(plSittingModifier), foundKeys, hintLocation); +} + +void plKeyFinder::IGetNames(std::vector& names, const char* searchName, int index) +{ + // Not really searching for any particular key, just need all the logic mods + std::vector keys; + ReallyStupidSubstringSearch(searchName, index, keys); + + for (int i = 0; i < keys.size(); i++) + { + // Only allow loaded ones to cut down on the crap + plKey key = keys[i]; + if (key->ObjectIsLoaded()) + names.push_back(key->GetName()); + } +} + +void plKeyFinder::GetResponderNames(std::vector& names) +{ + IGetNames(names, "", CLASS_INDEX_SCOPED(plResponderModifier)); +} + +void plKeyFinder::GetActivatorNames(std::vector& names) +{ + IGetNames(names, "", CLASS_INDEX_SCOPED(plLogicModifier)); +} + +class plKeyFinderIterator : public plRegistryKeyIterator, public plRegistryPageIterator +{ + protected: + + UInt16 fClassType; + const char *fObjName; + + std::vector &fFoundKeys; + + public: + + plKeyFinderIterator( UInt16 classType, const char *obName, std::vector& foundKeys ) + : fClassType( classType ), fObjName( obName ), fFoundKeys( foundKeys ) { } + + virtual hsBool EatKey( const plKey& key ) + { + if( key->GetUoid().IsValid() && key->GetUoid().GetClassType() == fClassType && + strstr( key->GetUoid().GetObjectName(), fObjName ) ) + { + fFoundKeys.push_back( key ); + } + + return true; + } + + virtual hsBool EatPage( plRegistryPageNode *page ) + { + hsBool ret = page->IterateKeys( this ); + return ret; + } +}; + +void plKeyFinder::ReallyStupidSubstringSearch(const char *name, UInt16 objType, std::vector& foundKeys, const plLocation &hintLocation ) +{ + if (!name) + return; + + plKeyFinderIterator collector( objType, name, foundKeys ); + if( hintLocation.IsValid() ) + { + plRegistryPageNode *hintPage = IGetResMgr()->FindPage( hintLocation ); + if( hintPage != nil ) + { + // Try all pages in the same age as that page + IGetResMgr()->IteratePages( &collector, hintPage->GetPageInfo().GetAge() ); + } + + if (foundKeys.size() > 0) + { + return; + } + } + + //fpReg->IterateKeys( &collector ); + IGetResMgr()->IterateAllPages( &collector ); +} + +//// Helper Class for FindSceneNodeKey /////////////////////////////////////// + +class plPageFinder : public plRegistryPageIterator +{ + protected: + + plRegistryPageNode **fPagePtr; + const char *fFindString, *fAgeString; + + public: + + plPageFinder( plRegistryPageNode **page, const char *find ) : fPagePtr( page ), fFindString( find ), fAgeString( nil ) + { *fPagePtr = nil; } + + plPageFinder( plRegistryPageNode **page, const char *ageS, const char *pageS ) : fPagePtr( page ), fFindString( pageS ), fAgeString( ageS ) + { *fPagePtr = nil; } + + virtual hsBool EatPage( plRegistryPageNode *node ) + { + static char str[ 512 ]; + const plPageInfo &info = node->GetPageInfo(); + + // Are we searching by age/page? + if( fAgeString != nil ) + { + if( stricmp( info.GetAge(), fAgeString ) == 0 && + stricmp( info.GetPage(), fFindString ) == 0 ) + { + *fPagePtr = node; + return false; + } + return true; + } + + // Try for page only + if( stricmp( info.GetPage(), fFindString ) == 0 ) + { + *fPagePtr = node; + return false; + } + + // Try for full location + sprintf( str, "%s_%s_%s", info.GetAge(), info.GetPage() ); + if( stricmp( str, fFindString ) == 0 ) + { + *fPagePtr = node; + return false; + } + + return true; // Keep searching + } +}; + +//// FindSceneNodeKey //////////////////////////////////////////////////////// +// Given a string for either a page name or a fully qualified location name, +// finds the page and then returns the key for the sceneNode in that page. +// Note: in our case, we want to force the page's keys to load if necessary, +// since the only time we call this function will be to actually load +// the darned thing. + +plKey plKeyFinder::FindSceneNodeKey( const char *pageOrFullLocName ) const +{ + plRegistryPageNode *pageNode; + plPageFinder pageFinder( &pageNode, pageOrFullLocName ); + + + // Use our own page finder, since we want to do nifty things like partial + // matches, etc. + if( IGetResMgr()->IterateAllPages( &pageFinder ) || pageNode == nil ) + return nil; + + return IFindSceneNodeKey( pageNode ); +} + +//// FindSceneNodeKey //////////////////////////////////////////////////////// +// Age/page pair version + +plKey plKeyFinder::FindSceneNodeKey( const char *ageName, const char *pageName ) const +{ + plRegistryPageNode *pageNode; + plPageFinder pageFinder( &pageNode, ageName, pageName ); + + // Use our own page finder, since we want to do nifty things like partial + // matches, etc. + if (IGetResMgr()->IterateAllPages(&pageFinder) || pageNode == nil) + return nil; + + return IFindSceneNodeKey( pageNode ); +} + +//// FindSceneNodeKey //////////////////////////////////////////////////////// +// plLocation version + +plKey plKeyFinder::FindSceneNodeKey(const plLocation& location) const +{ + plRegistryPageNode* pageNode = IGetResMgr()->FindPage(location); + if (pageNode == nil) + return nil; + + return IFindSceneNodeKey(pageNode); +} + +//// IFindSceneNodeKey /////////////////////////////////////////////////////// + + +plKey plKeyFinder::IFindSceneNodeKey(plRegistryPageNode* page) const +{ + // Got the pageNode, try a find before loading + plRegistryKeyList* keyList = page->IGetKeyList(CLASS_INDEX_SCOPED(plSceneNode)); + if (keyList) + { + if (keyList->fStaticKeys.size() == 1) + { + return plKey::Make((plKeyData*)keyList->fStaticKeys[0]); + } + else if (keyList->fDynamicKeys.size() == 1) // happens during export + { + plRegistryKeyList::DynSet::const_iterator it = keyList->fDynamicKeys.begin(); + plKeyImp* keyImp = *it; + return plKey::Make(keyImp); + } + } + + // Try loading and see if that helps + if (page->IsFullyLoaded()) + return nil; + + IGetResMgr()->LoadPageKeys(page); + + // Get the list of all sceneNodes + plKey retVal(nil); + keyList = page->IGetKeyList(CLASS_INDEX_SCOPED(plSceneNode)); + if (keyList && keyList->fStaticKeys.size() == 1) + { + retVal = plKey::Make((plKeyData*)keyList->fStaticKeys[0]); + } + // If we just loaded up all the keys for this page, then we + // may have a bunch of keys with a refcount of 0. For any of + // these keys that nothing else refs (yes, we have unused objects + // in the data), they don't get deleted because the refcount never + // rises above zero or falls back to zero. So we'll go ahead and + // ref and unref all of them. The ones in use stay put, the ones + // not being used go away. This is less than ideal. + IGetResMgr()->DumpUnusedKeys(page); + + return retVal; +} + +//// FindLocation //////////////////////////////////////////////////////////// + +const plLocation &plKeyFinder::FindLocation( const char *age, const char *page ) const +{ + if (age == nil) + { + static plLocation invalidLoc; + plRegistryPageNode *pageNode; + plPageFinder pageFinder( &pageNode, page ); + + if( IGetResMgr()->IterateAllPages( &pageFinder ) || pageNode == nil ) + return invalidLoc; + + return pageNode->GetPageInfo().GetLocation(); + } + + return IGetResMgr()->FindLocation( age, page ); +} + +//// GetLocationInfo ///////////////////////////////////////////////////////// + +const plPageInfo* plKeyFinder::GetLocationInfo( const plLocation &loc ) const +{ + plRegistryPageNode *node = IGetResMgr()->FindPage( loc ); + if (node) + return &node->GetPageInfo(); + else + return nil; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plKeyFinder.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plKeyFinder.h new file mode 100644 index 00000000..b8e316b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plKeyFinder.h @@ -0,0 +1,106 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plKeyFinder_h_inc +#define plKeyFinder_h_inc + +//---------------------------- +// plKeyFinder +//---------------------------- +// provides a way to look up an object (via its plKey) +// Using strings. The should only be used at Program Init time or console use (cause its not fast) +// The error codes are remembered, and used for subsequent calls to GetLastErrorString(); which can +// be display to tell the user where he screwed up the input. +// If a key is not found it returns nil + +//---------------------------- +// EXAMPLE OF USE: +//---------------------------- +// +// plKeyFinder *pFind = hsgResMgr::ResMgr()->GetKeyFinder(); +// plKey pKey = pFind->StupidSearch("Global", "Globalx", "1", "plSceneNode", "1"); +// if (!pKey) +// hsStatusMessage(pFind->GetLastErrorString()); +// delete pFind; +// +//---------------------------- + +#include "hsTypes.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plUoid.h" +#include "hsStlUtils.h" +#include + +class plLocation; +class plRegistryPageNode; +class plPageInfo; + +class plKeyFinder +{ +public: + enum eErrCodes + { + kOk, + kAgeNotFound, + kPageNotFound, + kInvalidClass, + kNoClassesInPage, + kObjectNotFound + }; + + static plKeyFinder& Instance(); + + // These are Stupid search because they just do string searchs on the objects. + plKey StupidSearch(const char * age, const char * rm, const char *className, const char *obName, hsBool subString=false); + plKey StupidSearch(const char * age, const char * rm, UInt16 objType, const char *obName, hsBool subString=false); + + eErrCodes GetLastErrorCode() { return fLastError; } + const char* GetLastErrorString(); // For Console display + + void ReallyStupidResponderSearch(const char* name, std::vector& foundKeys, const plLocation& hintLocation = plLocation::kInvalidLoc); + void ReallyStupidActivatorSearch(const char* name, std::vector& foundKeys, const plLocation& hintLocation = plLocation::kInvalidLoc); + + void ReallyStupidSubstringSearch(const char* name, UInt16 objType, std::vector& foundKeys, const plLocation& hintLocation = plLocation::kInvalidLoc); + + void GetActivatorNames(std::vector& names); + void GetResponderNames(std::vector& names); + + plKey FindSceneNodeKey(const char* pageOrFullLocName) const; + plKey FindSceneNodeKey(const char* ageName, const char* pageName) const; + plKey FindSceneNodeKey(const plLocation& location) const; + + const plLocation& FindLocation(const char* age, const char* page) const; + const plPageInfo* GetLocationInfo(const plLocation& loc) const; + +protected: + plKeyFinder() {} + + void IGetNames(std::vector& names, const char* name, int index); + plKey IFindSceneNodeKey(plRegistryPageNode* page) const; + + eErrCodes fLastError; +}; + +#endif // plKeyFinder_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLoc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLoc.cpp new file mode 100644 index 00000000..ec44946b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLoc.cpp @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plLoc.h" +#include "../pnKeyedObject/hsKeyedObject.h" + + +plLocFileParser::plLocFileParser(): fThrowBack(nil), fThrowBackLevel(0) +{ fpFile = fopen(LOCTXTFILE,"rt"); // NEED to figure out where to put this +} + +int plLocFileParser::NextLine(char **pP) +{ + + int levelKnt=0; + *pP = 0; + // If someone previously threwback a line...use it... + if (ThrowBackAvailable()) + { *pP = fThrowBack; + int rtnLevel = fThrowBackLevel; + ClearThrowBack(); + return rtnLevel; + } + // Get a New Line from the file + char str[512]; + while(fgets(str,512 - 1,fpFile)) + { + if (*str == '#') // indicates a missing file message, ignore + continue; + + int len = strlen(str); + str[len - 1] = 0; // clobber new line + //---------------------------------------------- + // If its the database file, remember the name, and skip it. + //---------------------------------------------- + int i=0; + while (str[i] == '\t') + { levelKnt++; + i++; + + } + *pP = hsStrcpy(str + levelKnt); // Allocate a string copy, advance past TABS + return levelKnt; + } + return LOC_EOF; +} + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLoc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLoc.h new file mode 100644 index 00000000..b6e44144 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLoc.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLLOC_H +#define PLLOC_H + +#include "HeadSpin.h" +#include "hsUtils.h" + + +// Values for UOID, such as AGE, District Room +// Are kept in configuration file(s) +// Levels are defined by the number of TABS in front of a string. +const int LOC_EOF = -1; + + // plLocFileParser Is a utility class used by plRegistry to Parse the Location.txt + // configuration file. + +#if HS_BUILD_FOR_WIN32 +#define LOCTXTFILE "c:\\Location.txt" +#else +#define LOCTXTFILE "./Location.txt" +#endif + +class plLocFileParser +{ +public: + plLocFileParser(); + ~plLocFileParser() { if (fpFile) fclose(fpFile); fpFile = 0; } + void ThrowBack(char *p,UInt8 lev) { fThrowBack = p; fThrowBackLevel = lev; } + void ClearThrowBack() { fThrowBack = NULL; fThrowBackLevel = 0; } + bool ThrowBackAvailable() { return (fThrowBack == NULL) ? false: true; } + int NextLine(char **pP); // returns an Allocated string in pP of next valid line, and Level #, or LOC_EOF + hsBool8 Ready() { return fpFile ? true: false; } +private: + FILE * fpFile; + char * fThrowBack; // If a line is not used, it can be thrown back...unget() + int fThrowBackLevel; +}; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLocalization.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLocalization.cpp new file mode 100644 index 00000000..c1032ada --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plLocalization.cpp @@ -0,0 +1,244 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plLocalization.h" +#include "../plFile/plFileUtils.h" +#include "hsUtils.h" + +plLocalization::Language plLocalization::fLanguage = plLocalization::kEnglish; + +char* plLocalization::fLangTags[] = +{ + "_eng", // kEnglish + "_fre", // kFrench + "_ger", // kGerman + "_spa", // kSpanish + "_ita", // kItalian + "_jpn" // kJapanese +}; +const int kLangTagLen = 4; + +char* plLocalization::fLangNames[] = +{ + "English", // kEnglish + "French", // kFrench + "German", // kGerman + "Spanish", // kSpanish + "Italian", // kItalian + "Japanese" // kJapanese +}; + +bool plLocalization::fUsesUnicode[] = +{ + false, // kEnglish + false, // kFrench + false, // kGerman + false, // kSpanish + false, // kItalian + true // kJapanese +}; + +plLocalization::encodingTypes plLocalization::fUnicodeEncoding[] = +{ + Enc_Unencoded, // kEnglish + Enc_Unencoded, // kFrench + Enc_Unencoded, // kGerman + Enc_Unencoded, // kSpanish + Enc_Unencoded, // kItalian + Enc_UTF8, // kJapanese +}; + +hsBool plLocalization::IGetLocalized(const char* name, Language lang, char* localizedName) +{ + const char* underscore = strrchr(name, '_'); + + if (underscore) + { + char langTag[kLangTagLen+1]; + strncpy(langTag,underscore,kLangTagLen); + langTag[kLangTagLen] = '\0'; + + if (strncmp(langTag, fLangTags[kEnglish], kLangTagLen) == 0) + { + if (localizedName) + { + strcpy(localizedName, name); + int underscorePos = underscore - name; + memcpy(localizedName + underscorePos, fLangTags[lang], kLangTagLen); + } + + return true; + } + } + + return false; +} + +hsBool plLocalization::ExportGetLocalized(const char* name, int lang, char* localizedName) +{ + return IGetLocalized(name, Language(lang+1), localizedName) && + plFileUtils::FileExists(localizedName); +} + +std::string plLocalization::LocalToString(const std::vector & localizedText) +{ + std::string retVal = ""; + for (int i=0; i kNumLanguages-1) + break; + std::string langHeader = "$"; + std::string langName = GetLanguageName((Language)i); + langHeader += langName.substr(0,2) + "$"; + retVal += langHeader + localizedText[i]; + } + return retVal; +} + +std::vector plLocalization::StringToLocal(const std::string & localizedText) +{ + std::vector retVal; + wchar_t *temp = hsStringToWString(localizedText.c_str()); + std::wstring wLocalizedText = temp; + delete [] temp; + + std::vector wStringVector = StringToLocal(wLocalizedText); + int i; + for (i=0; i plLocalization::StringToLocal(const std::wstring & localizedText) +{ + std::vector tags; + std::vector tagLocs; + std::vector sortedTagLocs; + std::vector retVal; + int i; + for (i=0; i tagLocs[sortedTagLocs[j]]) + sortedTagLocs[i]^=sortedTagLocs[j]^=sortedTagLocs[i]^=sortedTagLocs[j]; // swap the contents (yes, it works) + } + } + // now sortedTagLocs has the indexes of tagLocs sorted from smallest loc to highest loc + hsBool noTags = true; + for (i=0; i. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plLocalization_h_inc +#define plLocalization_h_inc + +#include "hsStlUtils.h" + +class plLocalization +{ +public: + enum Language + { + kEnglish, + kFrench, + kGerman, + kSpanish, + kItalian, + kJapanese, + + kNumLanguages, + }; + + typedef enum encodingTypes + { + Enc_Unencoded, // This can also mean that python did the decoding for us and we don't need to tweak it on our end + Enc_Split_String, + Enc_Hybrid_Split_String, + Enc_UTF8, + Enc_UTF16, + Enc_Unicode_Escape, + Enc_Raw_Unicode_Escape, + Enc_Latin_1, + Enc_ASCII, + Enc_MBCS + }; + +protected: + static Language fLanguage; + static char* fLangTags[kNumLanguages]; + static char* fLangNames[kNumLanguages]; + static bool fUsesUnicode[kNumLanguages]; + static encodingTypes fUnicodeEncoding[kNumLanguages]; + + static hsBool IGetLocalized(const char* name, Language lang, char* localizedName); + +public: + // Sets the default language, as determined by the installer + static void SetDefaultLanguage(); + + static void SetLanguage(Language lang) { fLanguage = lang; } + static Language GetLanguage() { return fLanguage; } + + static char* GetLanguageName(Language lang) { return fLangNames[lang]; } + + static hsBool UsingUnicode() { return fUsesUnicode[fLanguage]; } + static encodingTypes UnicodeEncoding() { return fUnicodeEncoding[fLanguage]; } + + // Returns true if we're using localized assets. If it returns false, you + // don't need to bother calling GetLocalized + static hsBool IsLocalized() { return fLanguage != kEnglish; } + + // Pass in a key name and this will give you the localized name + // Returns false if the original keyname is not for a localized asset + static hsBool GetLocalized(const char* name, char* localizedName) { return IGetLocalized(name, fLanguage, localizedName); } + + // + // Export only + // + // When you're exporting an asset that could be localized, you'll want to do + // a loop something like this to try and find any localized versions. + // + // for (int i = 0; i < plLocalization::GetNumLocales(); i++) + // { + // char localName[MAX_PATH]; + // if (plLocalization::ExportGetLocalized(fileName, i, localName)) + // { + // ... + // } + // } + // + static int GetNumLocales() { return kNumLanguages - 1; } + static hsBool ExportGetLocalized(const char* name, int lang, char* localizedName); + // Just tells us if this is localized, doesn't actually convert it for us + static hsBool IsLocalizedName(const char* name) { return IGetLocalized(name, kEnglish, nil); } + + // Converts a vector of translated strings to a encoded string that can be decoded by StringToLocal() + // The index in the vector of a string is it's language + static std::string LocalToString(const std::vector & localizedText); + // Converts a string encoded by LocalToString to a vector of translated strings + static std::vector StringToLocal(const std::string & localizedText); + static std::vector StringToLocal(const std::wstring & localizedText); +}; + +#endif // plLocalization_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plPageInfo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plPageInfo.cpp new file mode 100644 index 00000000..561ee730 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plPageInfo.cpp @@ -0,0 +1,193 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPageInfo.h" +#include "hsUtils.h" +#include "hsStream.h" +#include "../pnKeyedObject/plUoid.h" +#include "plVersion.h" + +static UInt32 sCurrPageInfoVersion = 6; + +//// Constructor/Destructor ////////////////////////////////////////////////// +plPageInfo::plPageInfo() +{ + IInit(); +} + +plPageInfo::plPageInfo( const plLocation &loc ) +{ + IInit(); + fLocation = loc; +} + +void plPageInfo::IInit() +{ + fAge = fPage = nil; + fLocation.Invalidate(); + SetMajorVersion(plVersion::GetMajorVersion()); + fClassVersions.clear(); + fChecksum = 0; + fDataStart = fIndexStart = 0; +} + +plPageInfo::~plPageInfo() +{ + SetStrings( nil, nil ); +} + +plPageInfo::plPageInfo( const plPageInfo &src ) +{ + IInit(); + ISetFrom( src ); +} + +plPageInfo &plPageInfo::operator=( const plPageInfo &src ) +{ + ISetFrom( src ); + return *this; +} + +void plPageInfo::ISetFrom( const plPageInfo &src ) +{ + fLocation = src.fLocation; + + SetStrings( src.fAge, src.fPage ); + fMajorVersion = src.fMajorVersion; + fClassVersions = src.fClassVersions; + fChecksum = src.fChecksum; + fDataStart = src.fDataStart; + fIndexStart = src.fIndexStart; +} + +void plPageInfo::SetStrings( const char *age, const char *page ) +{ + delete [] fAge; + delete [] fPage; + + fAge = ( age == nil ) ? nil : hsStrcpy( age ); + fPage = ( page == nil ) ? nil : hsStrcpy( page ); +} + +void plPageInfo::SetLocation( const plLocation &loc ) +{ + fLocation = loc; +} + +void plPageInfo::AddClassVersion(UInt16 classIdx, UInt16 version) +{ + ClassVersion cv; + cv.Class = classIdx; + cv.Version = version; + fClassVersions.push_back(cv); +} + +const plLocation& plPageInfo::GetLocation() const +{ + return fLocation; +} + +void plPageInfo::Read( hsStream *s ) +{ + delete [] fAge; + delete [] fPage; + + IInit(); + + // 5 is the earliest version since we began working again on the P20 codebase in Sep 2005, + // after Uru's online component was cancelled in Feb 2004, so I've removed support for + // anything prior to that to clean things up a bit. + UInt32 version = s->ReadSwap32(); + if (version > sCurrPageInfoVersion || version < 5) + { + hsAssert( false, "Invalid header version in plPageInfo::Read()" ); + return; + } + if (version >= 5) + { + fLocation.Read( s ); + fAge = s->ReadSafeString(); + if (version < 6) + delete s->ReadSafeString(); // fChapter was never used, and always "District". + fPage = s->ReadSafeString(); + + s->ReadSwap( &fMajorVersion ); + + if (version < 6) + { + UInt16 unusedMinorVersion; + s->ReadSwap(&unusedMinorVersion); + Int32 unusedReleaseVersion; + s->ReadSwap(&unusedReleaseVersion); // This was always zero... yanked. + UInt32 unusedFlags; + s->ReadSwap(&unusedFlags); + } + + s->ReadSwap( &fChecksum ); + s->ReadSwap( &fDataStart ); + s->ReadSwap( &fIndexStart ); + } + + if (version >= 6) + { + UInt16 numClassVersions = s->ReadSwap16(); + fClassVersions.reserve(numClassVersions); + for (UInt16 i = 0; i < numClassVersions; i++) + { + ClassVersion cv; + cv.Class = s->ReadSwap16(); + cv.Version = s->ReadSwap16(); + fClassVersions.push_back(cv); + } + } +} + +void plPageInfo::Write( hsStream *s ) +{ + s->WriteSwap32( sCurrPageInfoVersion ); + fLocation.Write( s ); + s->WriteSafeString( fAge ); + s->WriteSafeString( fPage ); + s->WriteSwap( fMajorVersion ); + s->WriteSwap( fChecksum ); + s->WriteSwap( fDataStart ); + s->WriteSwap( fIndexStart ); + UInt16 numClassVersions = UInt16(fClassVersions.size()); + s->WriteSwap16(numClassVersions); + for (UInt16 i = 0; i < numClassVersions; i++) + { + ClassVersion& cv = fClassVersions[i]; + s->WriteSwap16(cv.Class); + s->WriteSwap16(cv.Version); + } +} + +//// IsValid ///////////////////////////////////////////////////////////////// +// Just a simple test for now. + +hsBool plPageInfo::IsValid( void ) const +{ + return fLocation.IsValid(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plPageInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plPageInfo.h new file mode 100644 index 00000000..b386fc8b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plPageInfo.h @@ -0,0 +1,96 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plPageInfo - Pack of info about an individual page +// + +#ifndef _plPageInfo_h +#define _plPageInfo_h + +#include "hsTypes.h" +#include "../pnKeyedObject/plUoid.h" +#include + +class hsStream; +class plLocation; +class plPageInfo +{ +public: + struct ClassVersion { UInt16 Class; UInt16 Version; }; + typedef std::vector ClassVerVec; + +protected: + plLocation fLocation; + char* fAge; + char* fPage; + UInt16 fMajorVersion; + ClassVerVec fClassVersions; + UInt32 fChecksum; + UInt32 fDataStart, fIndexStart; + + void IInit( void ); + void ISetFrom( const plPageInfo &src ); + +public: + + plPageInfo(); + plPageInfo( const plLocation &loc ); + plPageInfo( const plPageInfo &src ); + virtual ~plPageInfo(); + + const char* GetAge() const { return fAge; } + const char* GetPage() const { return fPage; } + + plPageInfo &operator=( const plPageInfo &src ); + + void ClearClassVersions() { fClassVersions.clear(); } + void AddClassVersion(UInt16 classIdx, UInt16 version); + const ClassVerVec& GetClassVersions() const { return fClassVersions; } + + void SetStrings( const char *age, const char *page ); + + void SetLocation(const plLocation& loc); + const plLocation& GetLocation() const; + + UInt16 GetMajorVersion() const { return fMajorVersion; } + void SetMajorVersion(UInt16 major) { fMajorVersion = major; } + + void SetChecksum( UInt32 c ) { fChecksum = c; } + UInt32 GetChecksum( void ) const { return fChecksum; } + + void Read( hsStream *s ); + void Write( hsStream *s ); + + hsBool IsValid( void ) const; + + UInt32 GetDataStart( void ) const { return fDataStart; } + void SetDataStart( UInt32 s ) { fDataStart = s; } + + UInt32 GetIndexStart( void ) const { return fIndexStart; } + void SetIndexStart( UInt32 s ) { fIndexStart = s; } +}; +#endif // _plPageInfo_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryHelpers.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryHelpers.cpp new file mode 100644 index 00000000..734ac787 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryHelpers.cpp @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plRegistryHelpers.h" +#include "plRegistryNode.h" + +plKeyCollector::plKeyCollector( hsTArray &keys ) : fKeys( keys ) +{ +} + +hsBool plKeyCollector::EatKey(const plKey& key) +{ + fKeys.Append(key); + return true; +} + +hsBool plIndirectUnloadIterator::EatPage(plRegistryPageNode* page) +{ + page->IterateKeys(this); + return true; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryHelpers.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryHelpers.h new file mode 100644 index 00000000..610c62d6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryHelpers.h @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plRegistryHelpers - Little helper classes for the registry and resManager +// +//// History ///////////////////////////////////////////////////////////////// +// +// 3.25.2002 mcn - Created +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plRegistryHelpers_h +#define _plRegistryHelpers_h + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" + +class plKey; +class plRegistryPageNode; + +//// Little Iterator Class Defs ////////////////////////////////////////////// + +class plRegistryKeyIterator +{ +public: + virtual ~plRegistryKeyIterator() {} + virtual hsBool EatKey(const plKey& key) = 0; +}; + +class plRegistryPageIterator +{ +public: + virtual ~plRegistryPageIterator() {} + virtual hsBool EatPage(plRegistryPageNode* keyNode) = 0; +}; + + +//// plKeyCollector ////////////////////////////////////////////////////////// +// Helper key iterator that collects the given keys into the given hsTArray +class plKeyCollector : public plRegistryKeyIterator +{ +protected: + hsTArray &fKeys; + +public: + plKeyCollector(hsTArray& keys); + virtual hsBool EatKey(const plKey& key); +}; + +// If you loaded keys with another iterator, this will ensure that they're unloaded +class plIndirectUnloadIterator : public plRegistryPageIterator, public plRegistryKeyIterator +{ +public: + plIndirectUnloadIterator() {} + + hsBool EatKey(const plKey& key) { return true; } + + hsBool EatPage(plRegistryPageNode* page); +}; + +#endif // _plRegistryHelpers_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.cpp new file mode 100644 index 00000000..ea96af17 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.cpp @@ -0,0 +1,347 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plRegistryKeyList.h" +#include "plRegistryHelpers.h" +#include "hsStream.h" +#include + +plRegistryKeyList::plRegistryKeyList(UInt16 classType) +{ + fClassType = classType; + fReffedStaticKeys = 0; + fLocked = 0; + fFlags = 0; +} + +plRegistryKeyList::~plRegistryKeyList() +{ + hsAssert(fLocked == 0, "Key list still locked on delete"); + + for (int i = 0; i < fStaticKeys.size(); i++) + { + plKeyImp* keyImp = fStaticKeys[i]; + if (!keyImp->ObjectIsLoaded()) + delete keyImp; + } +} + +// Special dummy key that lets us set the return value of the GetName call. +// Makes it easier to do STL searches. +class plSearchKeyImp : public plKeyImp +{ +public: + const char* fSearchKeyName; + const char* GetName() const { return fSearchKeyName; } +}; + +plKeyImp* plRegistryKeyList::FindKey(const char* keyName) +{ + static plSearchKeyImp searchKey; + searchKey.fSearchKeyName = keyName; + + // Search the static key list + if (fFlags & kStaticUnsorted) + { + // We're unsorted, brute force it. May do a separate search table in the + // future if this is a bottlneck + for (int i = 0; i < fStaticKeys.size(); i++) + { + plKeyImp* curKey = fStaticKeys[i]; + if (curKey && hsStrCaseEQ(keyName, curKey->GetName())) + return curKey; + } + } + else + { + // We're sorted, do a fast lookup + StaticVec::const_iterator it = std::lower_bound(fStaticKeys.begin(), fStaticKeys.end(), &searchKey, KeySorter()); + if (it != fStaticKeys.end() && hsStrCaseEQ(keyName, (*it)->GetName())) + return *it; + } + + // Search the dynamic key list + DynSet::const_iterator dynIt = fDynamicKeys.find(&searchKey); + if (dynIt != fDynamicKeys.end()) + return *dynIt; + + return nil; +} + +plKeyImp* plRegistryKeyList::FindKey(const plUoid& uoid) +{ + UInt32 objectID = uoid.GetObjectID(); + + // Key is dynamic or doesn't know it's index. Do a find by name. + if (objectID == 0) + return FindKey(uoid.GetObjectName()); + + // Direct lookup + if (objectID <= fStaticKeys.size()) + { +#ifdef PLASMA_EXTERNAL_RELEASE + return fStaticKeys[objectID-1]; +#else + // If this is an internal release, our objectIDs might not match + // because of local data. Verify that we have the right key by + // name, and if it's wrong, do the slower find-by-name. + plKeyImp *keyImp = fStaticKeys[objectID-1]; + if (!hsStrCaseEQ(keyImp->GetName(), uoid.GetObjectName())) + return FindKey(uoid.GetObjectName()); + else + return keyImp; +#endif // PLASMA_EXTERNAL_RELEASE + } + + // If we got here it probably means we just deleted all our keys of the matching type + // because no one was using them. No worries. The resManager will catch this and + // reload our keys, then try again. + + return nil; +} + +void plRegistryKeyList::ILock() +{ + fLocked++; +} + +void plRegistryKeyList::IUnlock() +{ + fLocked--; + if (fLocked == 0) + IRepack(); +} + +bool plRegistryKeyList::IterateKeys(plRegistryKeyIterator* iterator) +{ + ILock(); + + for (int i = 0; i < fStaticKeys.size(); i++) + { + plKeyImp* keyImp = fStaticKeys[i]; + if (keyImp != nil) + { + if (!iterator->EatKey(plKey::Make(keyImp))) + { + IUnlock(); + return false; + } + } + } + + DynSet::const_iterator it; + for (it = fDynamicKeys.begin(); it != fDynamicKeys.end(); it++) + { + plKeyImp* keyImp = *it; + hsAssert(keyImp, "Shouldn't ever have a nil dynamic key"); + if (!iterator->EatKey(plKey::Make(keyImp))) + { + IUnlock(); + return false; + } + } + + IUnlock(); + return true; +} + +void plRegistryKeyList::AddKey(plKeyImp* key, LoadStatus& loadStatusChange) +{ + loadStatusChange = kNoChange; + + hsAssert(fLocked == 0, "Don't currently support adding keys while locked"); + if (fLocked == 0 && key != nil) + { + // If this is the first key added, we just became loaded + if (fDynamicKeys.empty()) + loadStatusChange = kDynLoaded; + + hsAssert(fDynamicKeys.find(key) == fDynamicKeys.end(), "Key already added"); + fDynamicKeys.insert(key); + } +} + +void plRegistryKeyList::SetKeyUsed(plKeyImp* key) +{ + // If this is a static key, mark that we used it. Otherwise, just ignore it. + UInt32 id = key->GetUoid().GetObjectID(); + if (id > 0) + fReffedStaticKeys++; +} + +bool plRegistryKeyList::SetKeyUnused(plKeyImp* key, LoadStatus& loadStatusChange) +{ + loadStatusChange = kNoChange; + + // Clones never officially get added to the key list (they're maintained by + // the original key), so just ignore them + if (key->GetUoid().IsClone()) + { + delete key; + return true; + } + + // Check if it's a static key + UInt32 id = key->GetUoid().GetObjectID(); + hsAssert(id <= fStaticKeys.size(), "Bad static key id"); + if (id != 0 && id <= fStaticKeys.size()) + { + fReffedStaticKeys--; + if (fLocked == 0) + IRepack(); + + // That was our last used static key, we're static unloaded + if (fReffedStaticKeys == 0) + loadStatusChange = kStaticUnloaded; + + return true; + } + + // Try to find it in the dynamic key list + DynSet::iterator dynIt = fDynamicKeys.find(key); + if (dynIt != fDynamicKeys.end()) + { + hsAssert(fLocked == 0, "Don't currently support removing dynamic keys while locked"); + if (fLocked == 0) + { + fDynamicKeys.erase(dynIt); + delete key; + + // That was our last dynamic key, notify of dynamic unloaded + if (fDynamicKeys.empty()) + loadStatusChange = kDynUnloaded; + + return true; + } + return false; + } + + hsAssert(0, "Couldn't find this key, what is it?"); + return false; +} + +//// IRepack ///////////////////////////////////////////////////////////////// +// Frees the memory for our static key array if none of them are loaded +void plRegistryKeyList::IRepack() +{ + if (fReffedStaticKeys == 0 && !fStaticKeys.empty()) + { + + for (int i = 0; i < fStaticKeys.size(); i++) + delete fStaticKeys[i]; + + fStaticKeys.clear(); + } +} + +void plRegistryKeyList::PrepForWrite() +{ + // If we have any static keys already, we were read in. To keep from + // invalidating old key indexes any new keys have to go on the end, hence we're + // unsorted now. + if (!fStaticKeys.empty()) + fFlags |= kStaticUnsorted; + + // If a dynamic keys doesn't have an object assigned to it, we're not writing + // it out. Figure out how many valid keys we have. + int numDynKeys = 0; + DynSet::const_iterator cIt; + for (cIt = fDynamicKeys.begin(); cIt != fDynamicKeys.end(); cIt++) + { + plKeyImp* key = *cIt; + // We're only going to write out keys that have objects + if (key->ObjectIsLoaded()) + numDynKeys++; + } + + // Start our new object id's after any already created ones + UInt32 objectID = fStaticKeys.size()+1; + // Make room for our new keys + fStaticKeys.resize(fStaticKeys.size()+numDynKeys); + + DynSet::iterator it = fDynamicKeys.begin(); + while (it != fDynamicKeys.end()) + { + plKeyImp* key = *it; + it++; + + // If we're gonna use this key, tag it with it's object id and move it to the static array. + if (key->ObjectIsLoaded()) + { + key->SetObjectID(objectID); + fStaticKeys[objectID-1] = key; + + objectID++; + fReffedStaticKeys++; + fDynamicKeys.erase(key); + } + } +} + +void plRegistryKeyList::Read(hsStream* s) +{ + UInt32 keyListLen = s->ReadSwap32(); + if (!fStaticKeys.empty()) + { + s->Skip(keyListLen); + return; + } + + fFlags = s->ReadByte(); + + UInt32 numKeys = s->ReadSwap32(); + fStaticKeys.resize(numKeys); + + for (int i = 0; i < numKeys; i++) + { + plKeyImp* newKey = TRACKED_NEW plKeyImp; + newKey->Read(s); + fStaticKeys[i] = newKey; + } +} + +void plRegistryKeyList::Write(hsStream* s) +{ + // Save space for the length of our data + UInt32 beginPos = s->GetPosition(); + s->WriteSwap32(0); + s->WriteByte(fFlags); + + int numKeys = fStaticKeys.size(); + s->WriteSwap32(numKeys); + + // Write out all our keys (anything in dynamic is unused, so just ignore those) + for (int i = 0; i < numKeys; i++) + { + plKeyImp* key = fStaticKeys[i]; + key->Write(s); + } + + // Go back to the start and write the length of our data + UInt32 endPos = s->GetPosition(); + s->SetPosition(beginPos); + s->WriteSwap32(endPos-beginPos-sizeof(UInt32)); + s->SetPosition(endPos); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.h new file mode 100644 index 00000000..5c588211 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryKeyList.h @@ -0,0 +1,113 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plRegistryKeyList_h_inc +#define plRegistryKeyList_h_inc + +#include "hsTypes.h" +#include "../pnKeyedObject/plKeyImp.h" +#include +#include + +class plRegistryKeyIterator; + +class KeySorter +{ +public: + bool operator() (plKeyImp* k1, plKeyImp* k2) const + { + hsAssert(k1 && k2, "Should have valid keys here"); + return stricmp(k1->GetName(), k2->GetName()) < 0; + } +}; + +// +// List of keys for a single class type. +// +class plRegistryKeyList +{ +protected: + friend class plKeyFinder; + + UInt16 fClassType; + // Lock counter for iterating. If this is >0, don't do any ops that + // can change key positions in the array (instead, just leave holes) + UInt16 fLocked; + + enum Flags { kStaticUnsorted = 0x1 }; + UInt8 fFlags; + + // Static keys are one's we read off disk. These don't change and are + // assumed to be already sorted when they're read in. + typedef std::vector StaticVec; + StaticVec fStaticKeys; + UInt32 fReffedStaticKeys; // Number of static keys that are loaded + + // Dynamic keys are anything created at runtime. They are put in the + // correct sorted position when they are added + typedef std::set DynSet; + DynSet fDynamicKeys; + + plRegistryKeyList() {} + + void ILock(); + void IUnlock(); + + void IRepack(); + +public: + plRegistryKeyList(UInt16 classType); + ~plRegistryKeyList(); + + UInt16 GetClassType() const { return fClassType; } + + // Find a key by name (case-insensitive) + plKeyImp* FindKey(const char* keyName); + // Find a key by uoid index. + plKeyImp* FindKey(const plUoid& uoid); + + bool IterateKeys(plRegistryKeyIterator* iterator); + + // Changes in our load status that can be caused by loading or unloading a key + enum LoadStatus + { + kNoChange, + kDynLoaded, + kDynUnloaded, + kStaticUnloaded, + }; + void AddKey(plKeyImp* key, LoadStatus& loadStatusChange); + void SetKeyUsed(plKeyImp* key); + bool SetKeyUnused(plKeyImp* key, LoadStatus& loadStatusChange); + + // Export time only. Before we write to disk, assign all the static keys + // object ID's that they can use to do fast lookups at load time. + void PrepForWrite(); + + void Read(hsStream* s); + void Write(hsStream* s); +}; + +#endif // plRegistryKeyList_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.cpp new file mode 100644 index 00000000..c962ca24 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.cpp @@ -0,0 +1,403 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plRegistryNode.h" +#include "plRegistryKeyList.h" +#include "plRegistryHelpers.h" + +#include "../pnKeyedObject/plKeyImp.h" +#include "../plStatusLog/plStatusLog.h" +#include "../pnFactory/plFactory.h" +#include "../plFile/plFileUtils.h" +#include "hsStlUtils.h" + +#include "plVersion.h" + +plRegistryPageNode::plRegistryPageNode(const char* path) + : fValid(kPageCorrupt) + , fPath(nil) + , fDynLoadedTypes(0) + , fStaticLoadedTypes(0) + , fOpenRequests(0) + , fIsNewPage(false) +{ + fPath = hsStrcpy(path); + + hsStream* stream = OpenStream(); + if (stream) + { + fPageInfo.Read(&fStream); + fValid = IVerify(); + CloseStream(); + } +} + +plRegistryPageNode::plRegistryPageNode(const plLocation& location, const char* age, const char* page, const char* dataPath) + : fValid(kPageOk) + , fPath(nil) + , fPageInfo(location) + , fDynLoadedTypes(0) + , fStaticLoadedTypes(0) + , fOpenRequests(0) + , fIsNewPage(true) +{ + fPageInfo.SetStrings(age, page); + + char filePath[512]; + + // Copy the path over + strncpy(filePath, dataPath, sizeof(filePath)); + plFileUtils::AddSlash(filePath); + + // Time to construct our actual file name. For now, we'll use the same old format + // of age_page.extension + strncat(filePath, fPageInfo.GetAge(), sizeof(filePath)); + strncat(filePath, "_District_", sizeof(filePath)); + strncat(filePath, fPageInfo.GetPage(), sizeof(filePath)); + strncat(filePath, ".prp", sizeof(filePath)); + + fPath = hsStrcpy(filePath); +} + +plRegistryPageNode::~plRegistryPageNode() +{ + delete [] fPath; + UnloadKeys(); +} + +PageCond plRegistryPageNode::IVerify() +{ + // Check the checksum values first, to make sure the files aren't corrupt + UInt32 ourChecksum = 0; + hsStream* stream = OpenStream(); + if (stream) + { + ourChecksum = stream->GetEOF() - fPageInfo.GetDataStart(); + CloseStream(); + } + if (ourChecksum != fPageInfo.GetChecksum()) + return kPageCorrupt; + + // If major version out-of-date, entire location is screwed + if (fPageInfo.GetMajorVersion() > plVersion::GetMajorVersion()) + return kPageTooNew; + else if (fPageInfo.GetMajorVersion() < plVersion::GetMajorVersion()) + return kPageOutOfDate; + + // Check the minor versions + const plPageInfo::ClassVerVec& classVersions = fPageInfo.GetClassVersions(); + for (int i = 0; i < classVersions.size(); i++) + { + const plPageInfo::ClassVersion& cv = classVersions[i]; + UInt16 curVersion = plVersion::GetCreatableVersion(cv.Class); + + if (curVersion > cv.Version) + return kPageOutOfDate; + else if (curVersion < cv.Version) + return kPageTooNew; + } + + return kPageOk; +} + +hsStream* plRegistryPageNode::OpenStream() +{ + if (fOpenRequests == 0) + { + if (!fStream.Open(fPath, "rb")) + return nil; + } + fOpenRequests++; + return &fStream; +} + +void plRegistryPageNode::CloseStream() +{ + if (fOpenRequests > 0) + fOpenRequests--; + + if (fOpenRequests == 0) + fStream.Close(); +} + +void plRegistryPageNode::LoadKeys() +{ + hsAssert(IsValid(), "Trying to load keys for invalid page"); + hsAssert(!fIsNewPage, "Trying to read a new page"); + if (IsFullyLoaded()) + return; + + hsStream* stream = OpenStream(); + if (!stream) + { + hsAssert(0, xtl::format("plRegistryPageNode::LoadKeysFromSource - bad stream %s,%s", + GetPageInfo().GetAge(), GetPageInfo().GetPage()).c_str()); + return; + } + + // If we're loading keys in the middle of a read because FindKey() failed, we'd better + // make note of our stream position and restore it when we're done. + UInt32 oldPos = stream->GetPosition(); + stream->SetPosition(GetPageInfo().GetIndexStart()); + + // Read in the number of key types + UInt32 numTypes = stream->ReadSwap32(); + for (UInt32 i = 0; i < numTypes; i++) + { + UInt16 classType = stream->ReadSwap16(); + plRegistryKeyList* keyList = IGetKeyList(classType); + if (!keyList) + { + keyList = TRACKED_NEW plRegistryKeyList(classType); + fKeyLists[classType] = keyList; + } + keyList->Read(stream); + } + + stream->SetPosition(oldPos); + CloseStream(); + fStaticLoadedTypes = fKeyLists.size(); +} + +void plRegistryPageNode::UnloadKeys() +{ + KeyMap::iterator it = fKeyLists.begin(); + for (; it != fKeyLists.end(); it++) + { + plRegistryKeyList* keyList = it->second; + delete keyList; + } + fKeyLists.clear(); + + fDynLoadedTypes = 0; + fStaticLoadedTypes = 0; +} + +//// plWriteIterator ///////////////////////////////////////////////////////// +// Key iterator for writing objects +class plWriteIterator : public plRegistryKeyIterator +{ +protected: + hsStream* fStream; + +public: + plWriteIterator(hsStream* s) : fStream(s) {} + + virtual hsBool EatKey(const plKey& key) + { + plKeyImp* imp = (plKeyImp*)key; + imp->WriteObject(fStream); + return true; + } +}; + +void plRegistryPageNode::Write() +{ + hsAssert(fOpenRequests == 0, "Trying to write while the page is open for reading"); + + if (!fStream.Open(fPath, "wb")) + { + hsAssert(0, "Couldn't open file for writing"); + return; + } + + // Some prep stuff. Assign object IDs for every key in this page, and put the + // versions of all our creatable types in the pageinfo. + fPageInfo.ClearClassVersions(); + + KeyMap::const_iterator it; + for (it = fKeyLists.begin(); it != fKeyLists.end(); it++) + { + plRegistryKeyList* keyList = it->second; + keyList->PrepForWrite(); + + int ver = plVersion::GetCreatableVersion(keyList->GetClassType()); + fPageInfo.AddClassVersion(keyList->GetClassType(), ver); + } + + // First thing we write is the pageinfo. Later we'll rewind and overwrite this with the final values + fPageInfo.Write(&fStream); + + fPageInfo.SetDataStart(fStream.GetPosition()); + + // Write all our objects + plWriteIterator writer(&fStream); + IterateKeys(&writer); + + fPageInfo.SetIndexStart(fStream.GetPosition()); + + // Write our keys + fStream.WriteSwap32(fKeyLists.size()); + for (it = fKeyLists.begin(); it != fKeyLists.end(); it++) + { + plRegistryKeyList* keyList = it->second; + fStream.WriteSwap16(keyList->GetClassType()); + keyList->Write(&fStream); + } + + // Rewind and write the pageinfo with the correct data and index offsets + fStream.Rewind(); + fPageInfo.SetChecksum(fStream.GetEOF() - fPageInfo.GetDataStart()); + fPageInfo.Write(&fStream); + + fStream.Close(); +} + +//// IterateKeys ///////////////////////////////////////////////////////////// + +hsBool plRegistryPageNode::IterateKeys(plRegistryKeyIterator* iterator) const +{ + KeyMap::const_iterator it = fKeyLists.begin(); + for (; it != fKeyLists.end(); it++) + { + plRegistryKeyList* keyList = it->second; + if (!keyList->IterateKeys(iterator)) + return false; + } + + return true; +} + +//// IterateKeys ///////////////////////////////////////////////////////////// +// Restricted version that only iterates through the keys of a given class +// type. + +hsBool plRegistryPageNode::IterateKeys(plRegistryKeyIterator* iterator, UInt16 classToRestrictTo) const +{ + plRegistryKeyList* keyList = IGetKeyList(classToRestrictTo); + if (keyList != nil) + return keyList->IterateKeys(iterator); + + return true; +} + +plKeyImp* plRegistryPageNode::FindKey(UInt16 classType, const char* name) const +{ + plRegistryKeyList* keys = IGetKeyList(classType); + if (keys == nil) + return nil; + + return keys->FindKey(name); +} + +plKeyImp* plRegistryPageNode::FindKey(const plUoid& uoid) const +{ + plRegistryKeyList* keys = IGetKeyList(uoid.GetClassType()); + if (keys == nil) + return nil; + + return keys->FindKey(uoid); +} + +void plRegistryPageNode::AddKey(plKeyImp* key) +{ + UInt16 classType = key->GetUoid().GetClassType(); + plRegistryKeyList* keys = fKeyLists[classType]; + if (keys == nil) + { + keys = TRACKED_NEW plRegistryKeyList(classType); + fKeyLists[classType] = keys; + } + + // Error check + if (keys->FindKey(key->GetUoid().GetObjectName()) != nil) + { + //char str[512], tempStr[128]; + //sprintf(str, "Attempting to add a key with a duplicate name. Not allowed." + // "\n\n(Key name: %s, Class: %s, Loc: %s)", key->GetUoid().GetObjectName(), + // plFactory::GetNameOfClass(classType), key->GetUoid().GetLocation().StringIze(tempStr)); + //hsStatusMessage(str); + hsBool recovered = false; + + // Attempt recovery + for (int i = 0; i < 500; i++) + { + char tempName[512]; + sprintf(tempName, "%s%d", key->GetUoid().GetObjectName(), i); + if (keys->FindKey(tempName) == nil) + { + plUoid uoid(key->GetUoid().GetLocation(), key->GetUoid().GetClassType(), tempName, key->GetUoid().GetLoadMask()); + key->SetUoid(uoid); + recovered = true; + break; + } + } + + if (!recovered) + { + hsAssert(0, "Couldn't allocate a unique key"); + return; + } + } + + plRegistryKeyList::LoadStatus loadStatusChange; + keys->AddKey(key, loadStatusChange); + + if (loadStatusChange == plRegistryKeyList::kDynLoaded) + fDynLoadedTypes++; +} + +void plRegistryPageNode::SetKeyUsed(plKeyImp* key) +{ + plRegistryKeyList* keys = IGetKeyList(key->GetUoid().GetClassType()); + if (keys == nil) + return; + + keys->SetKeyUsed(key); +} + +hsBool plRegistryPageNode::SetKeyUnused(plKeyImp* key) +{ + plRegistryKeyList* keys = IGetKeyList(key->GetUoid().GetClassType()); + if (keys == nil) + return false; + + plRegistryKeyList::LoadStatus loadStatusChange; + hsBool removed = keys->SetKeyUnused(key, loadStatusChange); + + // If the key type just changed load status, update our load counts + if (loadStatusChange == plRegistryKeyList::kDynUnloaded) + fDynLoadedTypes--; + else if (loadStatusChange == plRegistryKeyList::kStaticUnloaded) + fStaticLoadedTypes--; + + return removed; +} + +plRegistryKeyList* plRegistryPageNode::IGetKeyList(UInt16 classType) const +{ + KeyMap::const_iterator it = fKeyLists.find(classType); + if (it != fKeyLists.end()) + return it->second; + + return nil; +} + +void plRegistryPageNode::DeleteSource() +{ + hsAssert(fOpenRequests == 0, "Deleting a stream that's open for reading"); + plFileUtils::RemoveFile(fPath); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.h new file mode 100644 index 00000000..e2d38c44 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plRegistryNode.h @@ -0,0 +1,132 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plRegistryNode_h_inc +#define plRegistryNode_h_inc + +#include "hsTypes.h" +#include "hsStream.h" +#include "plPageInfo.h" + +#include + +class plRegistryKeyList; +class hsStream; +class plKeyImp; +class plRegistryKeyIterator; + +enum PageCond +{ + kPageOk, + kPageOutOfDate, + kPageTooNew, + kPageCorrupt, +}; + +// +// Represents one entire (age,page) location and contains all keys in that +// location. Note: just because the node exists does not mean that the keys are loaded. +// +class plRegistryPageNode +{ +protected: + friend class plKeyFinder; + + // Map from class type to a list of keys of that type + typedef std::map KeyMap; + KeyMap fKeyLists; + int fDynLoadedTypes; // The number of key types that have dynamic keys loaded + int fStaticLoadedTypes; // The number of key types that have all their keys loaded + + PageCond fValid; // Condition of the page + char* fPath; // Path to the page file + plPageInfo fPageInfo; // Info about this page + + hsBufferedStream fStream; // Stream for reading/writing our page + UInt8 fOpenRequests; // How many handles there are to fStream (or + // zero if it's closed) + hsBool fIsNewPage; // True if this page is new (not read off disk) + + plRegistryPageNode() {} + + inline plRegistryKeyList* IGetKeyList(UInt16 classType) const; + PageCond IVerify(); + +public: + // For reading a page off disk + plRegistryPageNode(const char* path); + + // For creating a new page. + plRegistryPageNode(const plLocation& location, const char* age, const char* page, const char* dataPath); + ~plRegistryPageNode(); + + hsBool IsValid() const { return fValid == kPageOk; } + PageCond GetPageCondition() { return fValid; } + + // True if we have any static or dynamic keys loaded + hsBool IsLoaded() const { return fDynLoadedTypes > 0 || fStaticLoadedTypes > 0; } + // True if all of our static keys are loaded + hsBool IsFullyLoaded() const { return (fStaticLoadedTypes == fKeyLists.size() && !fKeyLists.empty()) || fIsNewPage; } + + // Export time only. If we want to reuse a page, load the keys we want then + // call SetNewPage, so it will be considered a new page from now on. That + // way we won't try to load it's keys again. + hsBool IsNewPage() const { return fIsNewPage; } + void SetNewPage() { fIsNewPage = true; } + + const plPageInfo& GetPageInfo() const { return fPageInfo; } + + void LoadKeys(); // Loads the keys off disk + void UnloadKeys(); // Frees all our keys + + // Find a key by type and name + plKeyImp* FindKey(UInt16 classType, const char* name) const; + // Find a key by direct uoid lookup (or fallback to name lookup if that doesn't work) + plKeyImp* FindKey(const plUoid& uoid) const; + + void AddKey(plKeyImp* key); + + // Sets a key as used or unused, ie there aren't any refs to it anymore. + // When all the static keys are unused we can free the memory associated with + // them. When a dynamic key is unused we just delete it right away. + void SetKeyUsed(plKeyImp* key); + hsBool SetKeyUnused(plKeyImp* key); + + hsBool IterateKeys(plRegistryKeyIterator* iterator) const; + hsBool IterateKeys(plRegistryKeyIterator* iterator, UInt16 classToRestrictTo) const; + + // Call this to get a read stream for the page. If a valid pointer is + // returned, make sure to call CloseStream when you're done using it. + hsStream* OpenStream(); + void CloseStream(); + + // Takes care of everything involved in writing this page to disk + void Write(); + void DeleteSource(); + + const char* GetPagePath() const { return fPath; } +}; + +#endif // plRegistryNode_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManager.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManager.cpp new file mode 100644 index 00000000..79852fd4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManager.cpp @@ -0,0 +1,1826 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plResManager.h" +#include "plRegistryNode.h" +#include "plResManagerHelper.h" +#include "plResMgrSettings.h" +#include "plLocalization.h" +#include "hsSTLStream.h" + +#include "hsTimer.h" +#include "../pnTimer/plTimerCallbackManager.h" +#include "hsStlUtils.h" + +#include "../plScene/plSceneNode.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnKeyedObject/plKeyImp.h" +#include "../pnDispatch/plDispatch.h" +#include "../plStatusLog/plStatusLog.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../pnMessage/plClientMsg.h" +#include "../plFile/hsFiles.h" +#include "../plFile/plFileUtils.h" +#include "../pnFactory/plCreator.h" +#include "../pnNetCommon/plSynchedObject.h" +#include "../pnNetCommon/plNetApp.h" + +hsBool gDataServerLocal = false; + +/// Logging #define for easier use +#define kResMgrLog(level, log) if (plResMgrSettings::Get().GetLoggingLevel() >= level) log + +static void ILog(UInt8 level, const char* format, ...) +{ + static plStatusLog* log = plStatusLogMgr::GetInstance().CreateStatusLog + ( + plStatusLogMgr::kDefaultNumLines, + "resources.log", + plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe + ); + + va_list arguments; + va_start(arguments, format); + + UInt32 color = 0; + switch (level) + { + case 1: color = 0xffffffff; break; + case 2: color = 0xff8080ff; break; + case 3: color = 0xffffff80; break; + case 4: color = 0xff8080ff; break; + } + + log->AddLineV(color, format, arguments); +} + +plResManager::plResManager(): + fInited(false), + fPageOutHint(0), + fDispatch(nil), + fReadingObject( false ), + fCurCloneID(0), + fCurClonePlayerID(0), + fCloningCounter(0), + fProgressProc(nil), + fMyHelper(nil), + fLogReadTimes(false), + fPageListLock(0), + fPagesNeedCleanup(false), + fLastFoundPage(nil) +{ +#ifdef HS_DEBUGGING + plFactory::Validate(hsKeyedObject::Index()); +#endif +} + +plResManager::~plResManager() +{ + // verify shutDown + hsAssert(!fInited,"ResMgr not shutdown"); +} + +hsBool plResManager::IInit() +{ + if (fInited) + return true; + fInited = true; + + kResMgrLog(1, ILog(1, "Initializing resManager...")); + + if (plResMgrSettings::Get().GetLoadPagesOnInit()) + { + // We want to go through all the data files in our data path and add new + // plRegistryPageNodes to the regTree for each + hsFolderIterator pathIterator(fDataPath.c_str()); + while (pathIterator.NextFileSuffix(".prp")) + { + char fileName[kFolderIterator_MaxPath]; + pathIterator.GetPathAndName(fileName); + + plRegistryPageNode* node = TRACKED_NEW plRegistryPageNode(fileName); + fAllPages.insert(node); + } + } + + // Special case: we always create pages for the predefined pages + CreatePage(plLocation::kGlobalFixedLoc, "Global", "FixedKeys"); + + hsAssert(!fDispatch, "Dispatch already set"); + fDispatch = TRACKED_NEW plDispatch; + + plgTimerCallbackMgr::Init(); + + // Init our helper + fMyHelper = TRACKED_NEW plResManagerHelper(this); + fMyHelper->Init(); + hsAssert(fMyHelper->GetKey() != nil, "ResManager helper didn't init properly!" ); + + kResMgrLog(1, ILog(1, " ...Init was successful!")); + + return true; +} + +hsBool plResManager::IReset() // Used to Re-Export (number of times) +{ + BeginShutdown(); + IShutdown(); + return IInit(); +} + +void plResManager::BeginShutdown() +{ + if (fMyHelper) + fMyHelper->SetInShutdown(true); +} + +void plResManager::IShutdown() +{ + if (!fInited) + return; + + kResMgrLog(1, ILog(1, "Shutting down resManager...")); + + // Make sure we're not holding on to any ages for load optimization + IDropAllAgeKeys(); + + // At this point, we may have an undelivered future time stamped message + // in the Dispatch, which is reffing a bunch of keys we "temporarily" loaded. + // The obvious problems with that solution to avoid loading and unloading + // and reloading keys aside, those keys will continue to exist until the + // dispatch is destroyed, so they will show up as key leaks in the report. + // But they won't show up as memory leaks, because they'll be destroyed + // when the Dispatch is destructed. + // Update - now we call BeginShutdown, well, at the beginning of Shutdown. + // We pass that on to the Helper, so it knows to immediately dump the keys + // for any pages it loads, cuz there's no tomorrow. + + IPageOutSceneNodes(false); + + // Shut down our helper + fMyHelper->Shutdown(); // This will call UnregisterAs(), which will delete itself + fMyHelper = nil; + + // TimerCallbackMgr is a fixed-keyed object, so needs to shut down before the registry + plgTimerCallbackMgr::Shutdown(); + + // Destroy the dispatch. Note that we do this before the registry so that any lingering messages + // can free up keys properly + hsRefCnt_SafeUnRef(fDispatch); + fDispatch = nil; + + // Just before we shut down the registry, page out any keys that still exist. + // (They shouldn't... they're baaaaaad.) + IPageOutSceneNodes(true); + + // Shut down the registry (finally!) + ILockPages(); + + PageSet::const_iterator it; + for (it = fAllPages.begin(); it != fAllPages.end(); it++) + delete *it; + fAllPages.clear(); + fLoadedPages.clear(); + + IUnlockPages(); + + fLastFoundPage = nil; + + kResMgrLog(1, ILog(1, " ...Shutdown successful!")); + + fInited = false; +} + +void plResManager::AddSinglePage(const char* pagePath) +{ + plRegistryPageNode* node = TRACKED_NEW plRegistryPageNode(pagePath); + AddPage(node); +} + +plRegistryPageNode* plResManager::FindSinglePage(const char* path) const +{ + PageSet::const_iterator it; + for (it = fAllPages.begin(); it != fAllPages.end(); it++) + { + if (hsStrCaseEQ((*it)->GetPagePath(), path)) + return *it; + } + + return nil; +} + +void plResManager::RemoveSinglePage(const char* path) +{ + plRegistryPageNode* node = FindSinglePage(path); + if (node) + { + fAllPages.erase(node); + delete node; + } +} + +plDispatchBase *plResManager::Dispatch() +{ + return fDispatch; +} + + +void plResManager::LogReadTimes(hsBool logReadTimes) +{ + fLogReadTimes = logReadTimes; + if (fLogReadTimes) + { + plStatusLog::AddLineS("readtimings.log", plStatusLog::kWhite, "Created readtimings log"); + } +} + +hsKeyedObject* plResManager::IGetSharedObject(plKeyImp* pKey) +{ + plKeyImp* origKey = (plKeyImp*)pKey->GetCloneOwner(); + + // Find the first non-nil key and ask it to clone itself + UInt32 count = origKey->GetNumClones(); + for (UInt32 i = 0; i < count; i++) + { + plKey cloneKey = origKey->GetCloneByIdx(i); + if (cloneKey) + { + hsKeyedObject* obj = cloneKey->ObjectIsLoaded(); + if (obj) + { + hsKeyedObject* sharedObj = obj->GetSharedObject(); + if (sharedObj) + return sharedObj; + } + } + } + + return nil; +} + +//// ReadObject ///////////////////////////////////////////////////////////// +// Given a key, goes off and reads in the actual object from its source +hsBool plResManager::ReadObject(plKeyImp* key) +{ + // Read in the object. If while we are doing this something else requests a + // load (through AddViaNotify or ReadKeyNotifyMe) we consider it a child load + // and put it in a queue. This keeps us from jumping over to another object's + // data while we're still reading in its parent (which is bad because that + // trashes our file buffering) + // + // Also, we find the pageNode and open its stream here. We close the + // stream when child reads are done. If a child load is using the same stream, + // it will just inc/dec the open/close count during its read, and not actually + // close the stream, so we don't lose our place, lose our file handle, and thrash. + + kResMgrLog(4, ILog(4, " ...Opening page data stream for location 0x%x...", key->GetUoid().GetLocation())); + plRegistryPageNode *pageNode = FindPage(key->GetUoid().GetLocation()); + if (!pageNode) + { + kResMgrLog(3, ILog(3, " ...Data stream failed to open on read!")); + return false; + } + fReadingObject = true; + bool ret = IReadObject(key, pageNode->OpenStream()); + fReadingObject = false; + + if (!fQueuedReads.empty()) + { + // Now that the parent object is completely read in, we can do the child + // loads. We copy off all the children that were queued during our load so + // that we won't get our children's child loads mixed in (a parent only loads + // its immediate children) + std::vector children = fQueuedReads; + fQueuedReads.clear(); + + for (int i = 0; i < children.size(); i++) + { + plKey childKey = children[i]; + childKey->VerifyLoaded(); + } + } + + // we're done loading, and all our children are too, so send the notify + key->NotifyCreated(); + + pageNode->CloseStream(); + + return ret; +} + +hsBool plResManager::IReadObject(plKeyImp* pKey, hsStream *stream) +{ + static UInt64 totalTime = 0; + + UInt64 startTotalTime = totalTime; + UInt64 startTime = 0; + if (fLogReadTimes) + startTime = hsTimer::GetFullTickCount(); + + hsKeyedObject* ko = nil; + + hsAssert(pKey, "Null Key"); + if (pKey->GetUoid().GetLoadMask().DontLoad()) + return nil; + + hsAssert(pKey->GetStartPos() != UInt32(-1), "Missing StartPos"); + hsAssert(pKey->GetDataLen() != UInt32(-1), "Missing Data Length"); + + if (pKey->GetStartPos() == UInt32(-1) || pKey->GetDataLen() == UInt32(-1)) + return false; // Try to recover from this by just not reading an object + + kResMgrLog(3, ILog(3, " Reading object %s::%s", plFactory::GetNameOfClass(pKey->GetUoid().GetClassType()), pKey->GetUoid().GetObjectName())); + + const plUoid& uoid = pKey->GetUoid(); + + bool isClone = uoid.IsClone(); + + kResMgrLog(4, ILog(4, " ...is%s a clone", isClone ? "" : " not")); + + // If we're loading the root object of a clone (the object for the key that + // was actually cloned), set up the global cloning flags so any child objects + // read in will get them. Also turn off synching until the object is fully + // loaded, so we don't send out any partially loaded state. + bool setClone = false; + if (isClone && fCurCloneID != uoid.GetCloneID()) + { + kResMgrLog(4, ILog(4, " ...fCurCloneID = %d, uoid's cloneID = %d", fCurCloneID, uoid.GetCloneID())); + + if (fCurCloneID != 0) + { + hsAssert(false, "Recursive clone"); + kResMgrLog(3, ILog(3, " ...RECURSIVE CLONE DETECTED. ABORTING READ...")); + return false; + } + fCurClonePlayerID = uoid.GetClonePlayerID(); + fCurCloneID = uoid.GetCloneID(); + setClone = true; + + kResMgrLog(4, ILog(4, " ...now fCurCloneID = %d, fCurClonePlayerID = %d", fCurCloneID, fCurClonePlayerID)); + } + + // If this is a clone key, try and get the original object to give us a clone + if (isClone) + { + kResMgrLog(4, ILog(4, " ...Trying to get shared object...")); + ko = IGetSharedObject(pKey); + kResMgrLog(4, ILog(4, " ...IGetSharedObject() %s", (ko != nil) ? "succeeded" : "failed")); + } + + // If we couldn't share the object, read in a fresh copy + if (!ko) + { + stream->SetPosition(pKey->GetStartPos()); + kResMgrLog(4, ILog(4, " ...Reading from position %d bytes...", pKey->GetStartPos())); + + plCreatable* cre = ReadCreatable(stream); + hsAssert(cre, "Could not Create Object"); + if (cre) + { + ko = hsKeyedObject::ConvertNoRef(cre); + + if (ko != nil) + kResMgrLog(4, ILog(4, " ...Creatable read and valid")); + else + kResMgrLog(3, ILog(3, " ...Creatable read from stream not keyed object!")); + + if (fProgressProc != nil) + fProgressProc(plKey::Make(pKey)); + } + else + { + kResMgrLog(3, ILog(3, " ...ERROR: Unable to read creatable from stream!")); + } + } + + if (isClone && setClone) + { + fCurClonePlayerID = 0; + fCurCloneID = 0; + } + + kResMgrLog(4, ILog(4, " ...Read complete for object %s::%s", plFactory::GetNameOfClass(pKey->GetUoid().GetClassType()), pKey->GetUoid().GetObjectName())); + + if (fLogReadTimes) + { + UInt64 ourTime = hsTimer::GetFullTickCount() - startTime; + UInt64 childTime = totalTime - startTotalTime; + ourTime -= childTime; + + plStatusLog::AddLineS("readtimings.log", plStatusLog::kWhite, "%s, %s, %u, %.1f", + pKey->GetUoid().GetObjectName(), + plFactory::GetNameOfClass(pKey->GetUoid().GetClassType()), + pKey->GetDataLen(), + hsTimer::FullTicksToMs(ourTime)); + + totalTime += (hsTimer::GetFullTickCount() - startTime) - childTime; + } + + return (ko != nil); +} + +//// plPageOutIterator /////////////////////////////////////////////////////// +// See below function +class plPageOutIterator : public plRegistryPageIterator +{ +protected: + plResManager* fResMgr; + UInt16 fHint; + +public: + plPageOutIterator(plResManager* resMgr, UInt16 hint) : fResMgr(resMgr), fHint(hint) + { + fResMgr->IterateAllPages(this); + } + + virtual hsBool EatPage(plRegistryPageNode* page) + { + fResMgr->UnloadPageObjects(page, fHint); + return true; + } +}; + +#if HS_BUILD_FOR_UNIX +static void sLeakReportRedirectFn( const char message[] ) +{ + hsUNIXStream stream; + stream.Open( "resMgrMemLeaks.txt", "at" ); + stream.WriteString( message ); + stream.Close(); +} +static bool sFirstTime = true; +#endif + +// Just the scene nodes (and objects referenced by the node... and so on) +void plResManager::IPageOutSceneNodes(hsBool forceAll) +{ + plSynchEnabler ps(false); // disable dirty tracking while paging out + +#if HS_BUILD_FOR_UNIX + if (sFirstTime) + { + hsUNIXStream stream; + stream.Open("resMgrMemLeaks.txt", "wt"); + stream.Close(); + sFirstTime = false; + } + hsDebugMessageProc oldProc = hsSetStatusMessageProc(sLeakReportRedirectFn); +#endif + + if (forceAll) + { + hsStatusMessage( "--- plResManager Object Leak Report (BEGIN) ---" ); + plPageOutIterator iter(this, UInt16(-1)); + hsStatusMessage( "--- plResManager Object Leak Report (END) ---" ); + } + else + { + plPageOutIterator iter(this, fPageOutHint); + } + +#if HS_BUILD_FOR_UNIX + hsSetStatusMessageProc( oldProc ); +#endif +} + +//// FindKey ///////////////////////////////////////////////////////////////// + +inline plKeyImp* IFindKeyLocalized(const plUoid& uoid, plRegistryPageNode* page) +{ + const char* objectName = uoid.GetObjectName(); + + // If we're running localized, try to find a localized version first + if (plLocalization::IsLocalized()) + { + char localName[256]; + if (plLocalization::GetLocalized(objectName, localName)) + { + plKeyImp* localKey = page->FindKey(uoid.GetClassType(), localName); + if (localKey != nil) + return localKey; + } + } + + // Try to find the non-localized version + return page->FindKey(uoid); +} + +plKey plResManager::FindOriginalKey(const plUoid& uoid) +{ + plKey key; + plKeyImp* foundKey = nil; + + plRegistryPageNode* page = FindPage(uoid.GetLocation()); + if (page == nil) + return key; + + // Try our find first, without loading + foundKey = IFindKeyLocalized(uoid, page); + if (foundKey != nil) + key = plKey::Make(foundKey); + + if (!key && plResMgrSettings::Get().GetPassiveKeyRead()) + { + // Passive key read mode is where we read keys in and attempt to match + // them to keys in the registry, but we will NOT force a load on the page + // to find the keys. If the key isn't already in the registry to match, + // we create what we call a "passive key", i.e. it's a key with no real + // info apart from the uoid. Used when you want to read in a object that + // contains keys but don't want to actually use those keys (only write + // them back out). + + // Note: startPos of -1 means we didn't read it from disk, but 0 length + // is our special key that we're a passively created key + foundKey = TRACKED_NEW plKeyImp(uoid, UInt32(-1), UInt32(0)); + key = plKey::Make(foundKey); + } + + // OK, find didn't work. Can we load and try again? + if (!key && !page->IsFullyLoaded()) + { + // Tell the resManager's helper to load and hold our page keys temporarily + plResManagerHelper::GetInstance()->LoadAndHoldPageKeys(page); + + // Try again + foundKey = IFindKeyLocalized(uoid, page); + if (foundKey != nil) + key = plKey::Make(foundKey); + } + return key; +} + +plKey plResManager::FindKey(const plUoid& uoid) +{ + plKey key = FindOriginalKey(uoid); + + // If we're looking for a clone, get the clone instead of the original + if (key && uoid.IsClone()) + key = ((plKeyImp*)key)->GetClone(uoid.GetClonePlayerID(), uoid.GetCloneID()); + + return key; +} + +const plLocation& plResManager::FindLocation(const char* age, const char* page) const +{ + static plLocation invalidLoc; + + plRegistryPageNode* pageNode = FindPage(age, page); + if (pageNode) + return pageNode->GetPageInfo().GetLocation(); + + return invalidLoc; +} + +const void plResManager::GetLocationStrings(const plLocation& loc, char* ageBuffer, char* pageBuffer) const +{ + plRegistryPageNode* page = FindPage(loc); + const plPageInfo& info = page->GetPageInfo(); + + // Those buffers better be big enough... + if (ageBuffer) + hsStrcpy(ageBuffer, info.GetAge()); + if (pageBuffer) + hsStrcpy(pageBuffer, info.GetPage()); +} + +hsBool plResManager::AddViaNotify(plRefMsg* msg, plRefFlags::Type flags) +{ + hsAssert(msg && msg->GetRef() && msg->GetRef()->GetKey(), "Improperly filled out ref message"); + plKey key = msg->GetRef()->GetKey(); // for linux build + return AddViaNotify(key, msg, flags); +} + +hsBool plResManager::AddViaNotify(const plKey &key, plRefMsg* msg, plRefFlags::Type flags) +{ + hsAssert(key, "Can't add without a Key"); + if (!key) + { + hsRefCnt_SafeUnRef(msg); + return false; + } + + ((plKeyImp*)key)->SetupNotify(msg,flags); + + if (flags != plRefFlags::kPassiveRef) + { + hsKeyedObject* ko = key->ObjectIsLoaded(); + if (!ko) + Load(key); + } + + return true; +} + +hsBool plResManager::SendRef(hsKeyedObject* ko, plRefMsg* refMsg, plRefFlags::Type flags) +{ + if (!ko) + return false; + plKey key = ko->GetKey(); + return SendRef(key, refMsg, flags); +} + +////////////////////////// +// This one does the dirty. Calls, the protected ISetupNotify on the key being reffed. +// That will setup the notifications on the key, but not send any. If the object is +// currently not loaded, we're done (and this behaves exactly like AddViaNotify(). +// If it is in memory, and the one making the reference is in memory (it presumably +// is in the absence of strange doings), we bypass the Dispatch system and call +// the object making the reference's MsgReceive directly, so the object will have +// received the reference via its normal message processing without the message +// having to wait in the queue, and more importantly, before the SendRef call returns. +// This doesn't mean you are guaranteed to have your ref at the return of SendRef, +// because if it's not in memory, we don't wait around while we load it, we just +// return false. +hsBool plResManager::SendRef(const plKey& key, plRefMsg* refMsg, plRefFlags::Type flags) +{ + if (!key) + { + hsRefCnt_SafeUnRef(refMsg); + return false; + } + + plKeyImp* iKey = (plKeyImp*)key; + iKey->ISetupNotify(refMsg, flags); + hsRefCnt_SafeUnRef(refMsg); + + if (flags != plRefFlags::kPassiveRef) + iKey->VerifyLoaded(); + + hsKeyedObject* ko = key->ObjectIsLoaded(); + if (!ko) + return false; + + refMsg->SetRef(ko); + refMsg->SetTimeStamp(hsTimer::GetSysSeconds()); + + for (int i = 0; i < refMsg->GetNumReceivers(); i++) + { + hsKeyedObject* rcv = refMsg->GetReceiver(i)->ObjectIsLoaded(); + if (rcv) + rcv->MsgReceive(refMsg); + } + return true; +} + +void plResManager::Load(const plKey &key) // places on list to be loaded +{ + if (fReadingObject) + fQueuedReads.push_back(key); + else + key->VerifyLoaded(); // force Load +} + +plKey plResManager::ReadKeyNotifyMe(hsStream* stream, plRefMsg* msg, plRefFlags::Type flags) +{ + plKey key = ReadKey(stream); + + if (!key) + { + hsRefCnt_SafeUnRef(msg); + return nil; + } + if(key->GetUoid().GetLoadMask().DontLoad()) + { + hsStatusMessageF("%s being skipped because of load mask", key->GetName()); + hsRefCnt_SafeUnRef(msg); + return nil; + } + + ((plKeyImp*)key)->SetupNotify(msg,flags); + + hsKeyedObject* ko = key->ObjectIsLoaded(); + + if (!ko) + { + Load(key); + } + + return key; +} + +//// NewKey ////////////////////////////////////////////////////////////////// +// Creates a new key and assigns it to the given keyed object, also placing +// it into the registry. + +plKey plResManager::NewKey(const char* name, hsKeyedObject* object, const plLocation& loc, const plLoadMask& m ) +{ + hsAssert(name && name[0] != '\0', "No name for new key"); + plUoid newUoid(loc, object->ClassIndex(), name, m); + return NewKey(newUoid, object); +} + +plKey plResManager::NewKey(plUoid& newUoid, hsKeyedObject* object) +{ + hsAssert(fInited, "Attempting to create a new key before we're inited!"); + + plKeyImp* newKey = TRACKED_NEW plKeyImp; + newKey->SetUoid(newUoid); + AddKey(newKey); + + plKey keyPtr = plKey::Make(newKey); + object->SetKey(keyPtr); + + return keyPtr; +} + +plKey plResManager::ReRegister(const char* nm, const plUoid& oid) +{ + hsAssert(fInited, "Attempting to reregister a key before we're inited!"); + + bool canClone = false; + if (fCurCloneID != 0) + { + // Not allowed to clone these things + int oidType = oid.GetClassType(); + if (oidType != CLASS_INDEX_SCOPED(plSceneNode) && + oidType != CLASS_INDEX_SCOPED(plLOSDispatch) && + oidType != CLASS_INDEX_SCOPED(plTimerCallbackManager) && + oidType != CLASS_INDEX_SCOPED(pfConsole) && + oidType != CLASS_INDEX_SCOPED(plAudioSystem) && + oidType != CLASS_INDEX_SCOPED(plInputManager) && + oidType != CLASS_INDEX_SCOPED(plClient) && + oidType != CLASS_INDEX_SCOPED(plNetClientMgr) && + oidType != CLASS_INDEX_SCOPED(plAvatarAnimMgr) && + oidType != CLASS_INDEX_SCOPED(plSoundBuffer) && + oidType != CLASS_INDEX_SCOPED(plResManagerHelper) && + oidType != CLASS_INDEX_SCOPED(plSharedMesh)) + canClone = true; + + // Can't clone fixed keys + if (oid.GetLocation() == plLocation::kGlobalFixedLoc) + canClone = false; + } + + plKey pOrigKey = FindOriginalKey(oid); + if (!canClone) + { + if (pOrigKey) + { + return pOrigKey; + } + // the clone doesn't exist + else if (oid.IsClone()) + { + return nil; + } + } + else //we are cloning + { + if (pOrigKey) + { + plKey cloneKey = ((plKeyImp*)pOrigKey)->GetClone(fCurClonePlayerID, fCurCloneID); + if (cloneKey) + return cloneKey; + } + } + + plKeyImp* pKey = TRACKED_NEW plKeyImp; + if (canClone && pOrigKey) + { + pKey->CopyForClone((plKeyImp*)pOrigKey, fCurClonePlayerID, fCurCloneID); + ((plKeyImp*)pOrigKey)->AddClone(pKey); + } + else + { + // Make sure key doesn't already exist + if (pOrigKey) + { + hsAssert(false, "Attempting to add duplicate key"); + delete pKey; + return nil; + } + + pKey->SetUoid(oid); // Tell the Key its ID + AddKey(pKey); + } + + hsAssert(pKey, "ReRegister: returning nil key?"); + return plKey::Make(pKey); +} + +//// ReadKey ///////////////////////////////////////////////////////////////// +// Reads a "key" from the given stream. What we secretly do is read in the +// plUoid for a key and look up to find the key. Nobody else will know :) + +plKey plResManager::ReadKey(hsStream* s) +{ + hsBool nonNil = s->ReadBool(); + if (!nonNil) + return nil; + + plUoid uoid; + uoid.Read(s); + + plKey key; + + if (fCurCloneID != 0) + { + // We're reading child of a clone object, it needs to be cloned too + key = ReRegister(uoid.GetObjectName(), uoid); + } + else if (uoid.GetCloneID() != 0) + { + // We're reading a clone key. first see if we already have that key around.... + key = FindKey(uoid); + if (key == nil) + { + fCurClonePlayerID = uoid.GetClonePlayerID(); + fCurCloneID = uoid.GetCloneID(); + key = ReRegister(uoid.GetObjectName(), uoid); + fCurClonePlayerID = 0; + fCurCloneID = 0; + } + } + else + { + // We're reading a regular, non-clone object + key = FindKey(uoid); + } + + return key; +} + +//// WriteKey //////////////////////////////////////////////////////////////// + +void plResManager::WriteKey(hsStream* s, hsKeyedObject* obj) +{ + if (obj) + WriteKey(s, obj->GetKey()); + else + WriteKey(s, plKey(nil)); +} + +void plResManager::WriteKey(hsStream *s, const plKey &key) +{ + s->WriteBool(key != nil); + if (key) + key->GetUoid().Write(s); +} + +// +// Create cloned key but don't load yet +// +plKey plResManager::CloneKey(const plKey& objKey) +{ + if (!objKey) + { + hsStatusMessage("CloneKey: nil key, returning nil"); + return nil; + } + + fCloningCounter++; + return ICloneKey(objKey->GetUoid(), plNetClientApp::GetInstance()->GetPlayerID(), fCloningCounter); +} + +plKey plResManager::ICloneKey(const plUoid& objUoid, UInt32 playerID, UInt32 cloneID) +{ + hsAssert(fCurCloneID == 0, "Recursive clone"); + fCurCloneID = cloneID; + fCurClonePlayerID = playerID; + + plKey cloneKey = ReRegister("", objUoid); + + fCurClonePlayerID = 0; + fCurCloneID = 0; + + // Then notify NetClientMgr when object loads + plObjRefMsg* refMsg = TRACKED_NEW plObjRefMsg(plNetClientApp::GetInstance()->GetKey(), plRefMsg::kOnCreate, 0, 0); + AddViaNotify(cloneKey, refMsg, plRefFlags::kPassiveRef); + + return cloneKey; +} + +// +// Unregisters (deletes) an object. +// Currently, this means the object is going away permanently. +// When support for paging is added, key->UnRegister() should not clear its notify lists. +// Return true if successful. +// +hsBool plResManager::Unload(const plKey& objKey) +{ + if (objKey) + { + ((plKeyImp*)objKey)->UnRegister(); + fDispatch->UnRegisterAll(objKey); + return true; + } + return false; +} + +plCreatable* plResManager::IReadCreatable(hsStream* s) const +{ + UInt16 hClass = s->ReadSwap16(); + plCreatable* pCre = plFactory::Create(hClass); + if (!pCre) + hsAssert( hClass == 0x8000, "Invalid creatable index" ); + + return pCre; +} + +plCreatable* plResManager::ReadCreatable(hsStream* s) +{ + plCreatable *pCre = IReadCreatable(s); + if (pCre) + pCre->Read(s, this); + return pCre; +} + +plCreatable* plResManager::ReadCreatableVersion(hsStream* s) +{ + plCreatable *pCre = IReadCreatable(s); + if (pCre) + pCre->ReadVersion(s, this); + return pCre; +} + +inline void IWriteCreatable(hsStream* s, plCreatable* pCre) +{ + Int16 hClass = pCre ? pCre->ClassIndex() : 0x8000; + hsAssert(pCre == nil || plFactory::IsValidClassIndex(hClass), "Invalid class index on write"); + s->WriteSwap16(hClass); +} + +void plResManager::WriteCreatable(hsStream* s, plCreatable* pCre) +{ + IWriteCreatable(s, pCre); + if (pCre) + pCre->Write(s, this); +} + +void plResManager::WriteCreatableVersion(hsStream* s, plCreatable* pCre) +{ + IWriteCreatable(s, pCre); + if (pCre) + pCre->WriteVersion(s, this); +} + +void plResManager::SetProgressBarProc(plProgressProc proc) +{ + fProgressProc = proc; +} + +////////////////////////////////////////////////////////////////////////////// +//// Paging Functions //////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// plResAgeHolder ////////////////////////////////////////////////////////// +// Helper object that stores all the keys for an age, to optimize the load +// process. + +class plResAgeHolder : public hsRefCnt +{ + public: + hsTArray fKeys; + std::string fAge; + + plResAgeHolder() {} + plResAgeHolder( const char* age ) : fAge( age ) {} + ~plResAgeHolder() { fKeys.Reset(); } +}; + +//// plResHolderIterator ///////////////////////////////////////////////////// + +class plResHolderIterator : public plRegistryPageIterator +{ +protected: + hsTArray& fKeys; + const char* fAgeName; + plResManager* fResMgr; + +public: + plResHolderIterator(const char* age, hsTArray& keys, plResManager* resMgr) + : fAgeName(age), fKeys(keys), fResMgr(resMgr) {} + + virtual hsBool EatPage(plRegistryPageNode* page) + { + if (stricmp(page->GetPageInfo().GetAge(), fAgeName) == 0) + { + fResMgr->LoadPageKeys(page); + plKeyCollector collector(fKeys); + page->IterateKeys(&collector); + } + + return true; + } +}; + +//// LoadAndHoldAgeKeys ////////////////////////////////////////////////////// + +void plResManager::LoadAgeKeys(const char* age) +{ + hsAssert(age && age[0] != '\0', "age is nil"); + HeldAgeKeyMap::const_iterator it = fHeldAgeKeys.find(age); + if (it != fHeldAgeKeys.end()) + { + kResMgrLog(1, ILog(1, "Reffing age keys for age %s", age)); + hsStatusMessageF("*** Reffing age keys for age %s ***\n", age); + plResAgeHolder* holder = it->second; + holder->Ref(); + } + else + { + kResMgrLog(1, ILog(1, "Loading age keys for age %s", age)); + hsStatusMessageF("*** Loading age keys for age %s ***\n", age); + + plResAgeHolder* holder = TRACKED_NEW plResAgeHolder(age); + fHeldAgeKeys[age] = holder; + // Go find pages that match this age, load the keys, and ref them all + plResHolderIterator iter(age, holder->fKeys, this); + IterateAllPages(&iter); + } +} + +//// DropAgeKeys ///////////////////////////////////////////////////////////// + +void plResManager::DropAgeKeys(const char* age) +{ + HeldAgeKeyMap::iterator it = fHeldAgeKeys.find(age); + if (it != fHeldAgeKeys.end()) + { + plResAgeHolder* holder = it->second; + if (holder->RefCnt() == 1) + { + // Found it! + kResMgrLog(1, ILog(1, "Dropping held age keys for age %s", age)); + fHeldAgeKeys.erase(it); + } + else + { + kResMgrLog(1, ILog(1, "Unreffing age keys for age %s", age)); + } + + holder->UnRef(); + } +} + +//// IDropAllAgeKeys ///////////////////////////////////////////////////////// + +void plResManager::IDropAllAgeKeys() +{ + kResMgrLog(1, ILog(1, "Dropping any remaining age keys")); + for (HeldAgeKeyMap::iterator it = fHeldAgeKeys.begin(); it != fHeldAgeKeys.end(); ++it) + { + plResAgeHolder* holder = it->second; + kResMgrLog(1, ILog(1, "Dropping age keys for age %s", holder->fAge.c_str())); + while (holder->RefCnt() > 1) + holder->UnRef(); + holder->UnRef(); // deletes holder + } +} + +//// PageInRoom ////////////////////////////////////////////////////////////// +// Normal finds will have to potentially reload all the keys for a page, but +// paging in this way will avoid having to reload keys every single find +// during a load--we load all the keys once, ref them so they're in, then +// do the entire load and unref when we're done. +// +// The objClassToRef parameter is a bit tricky. Basically, you assume that +// there's one object in the page that you're loading based off of (say, a +// sceneNode). That's the object that'll get reffed by the refMsg passed in. +// The only reason we abstract it is so we can keep plResManager from being +// dependent on any particular class type. +// +// This function is not guaranteed to be synchronous, so you better wait for +// the refMsg to be sent before you assume it's done. + +class plOurRefferAndFinder : public plRegistryKeyIterator +{ + hsTArray &fRefArray; + UInt16 fClassToFind; + plKey &fFoundKey; + + public: + + plOurRefferAndFinder( hsTArray &refArray, UInt16 classToFind, plKey &foundKey ) + : fRefArray( refArray ), fClassToFind( classToFind ), fFoundKey( foundKey ) { } + + virtual hsBool EatKey( const plKey& key ) + { + // This is cute. Thanks to our new plKey smart pointers, all we have to + // do is append the key to our ref array. This automatically guarantees us + // an extra ref on the key, which is what we're trying to do. Go figure. + fRefArray.Append( key ); + + // Also do our find + if( key->GetUoid().GetClassType() == fClassToFind ) + fFoundKey = key; + + return true; + } +}; + +void plResManager::PageInRoom(const plLocation& page, UInt16 objClassToRef, plRefMsg* refMsg) +{ + UInt64 readRoomTime = 0; + if (fLogReadTimes) + readRoomTime = hsTimer::GetFullTickCount(); + + plSynchEnabler ps(false); // disable dirty tracking while paging in + + kResMgrLog(1, ILog(1, "Paging in room 0x%x...", page.GetSequenceNumber())); + + // Step 0: Find the pageNode + plRegistryPageNode* pageNode = FindPage(page); + if (pageNode == nil) + { + kResMgrLog(1, ILog(1, "...Page not found!")); + hsAssert(false, "Invalid location given to PageInRoom()"); + return; + } + + kResMgrLog(2, ILog(2, "...Found, page is ID'd as %s>%s", pageNode->GetPageInfo().GetAge(), pageNode->GetPageInfo().GetPage())); + + // Step 0.5: Verify the page, just to make sure we really should be loading it + PageCond cond = pageNode->GetPageCondition(); + if (cond != kPageOk) + { + std::string condStr ="Checksum invalid"; + if (cond == kPageTooNew) + condStr = "Page Version too new"; + else + if (cond == kPageOutOfDate) + condStr = "Page Version out of date"; + + kResMgrLog(1, ILog(1, "...IGNORING pageIn request; verification failed! (%s)", condStr.c_str())); + + std::string msg = xtl::format("Data Problem: Age:%s Page:%s Error:%s", + pageNode->GetPageInfo().GetAge(), pageNode->GetPageInfo().GetPage(), condStr.c_str()); + hsMessageBox(msg.c_str(), "Error", hsMessageBoxNormal, hsMessageBoxIconError); + + hsRefCnt_SafeUnRef(refMsg); + return; + } + + // Step 1: We force a load on all the keys in the given page + kResMgrLog(2, ILog(2, "...Loading page keys...")); + LoadPageKeys(pageNode); + + // Step 2: Now ref all the keys in that page, every single one. This lets us unref + // (and thus potentially delete) them later. Note that we also use this for our find. + kResMgrLog(2, ILog(2, "...Reffing keys...")); + plKey objKey; + hsTArray keyRefList; + plOurRefferAndFinder reffer(keyRefList, objClassToRef, objKey); + pageNode->IterateKeys(&reffer); + + // Step 3: Do our load + if (objKey == nil) + { + kResMgrLog(1, ILog(1, "...SceneNode not found to base page-in op on. Aborting...")); + // This is coming up a lot lately; too intrusive to be an assert. + // hsAssert( false, "No object found on which to base our PageInRoom()" ); + return; + } + + // Forces a load + kResMgrLog(2, ILog(2, "...Forcing load via sceneNode...")); + objKey->VerifyLoaded(); + + // Step 4: Unref the keys. This'll make the unused ones go away again. And guess what, + // since we just have an array of keys, all we have to do to do this is clear the array. + // Note that since objKey is a plKey, our object that we loaded will have an extra ref... + // Scary, huh? + kResMgrLog(2, ILog(2, "...Dumping extra key refs...")); + keyRefList.Reset(); + + // Step 5: Ref the object + kResMgrLog(2, ILog(2, "...Dispatching refMessage...")); + AddViaNotify(objKey, refMsg, plRefFlags::kActiveRef); + + // All done! + kResMgrLog(1, ILog(1, "...Page in complete!")); + + if (fLogReadTimes) + { + readRoomTime = hsTimer::GetFullTickCount() - readRoomTime; + + plStatusLog::AddLineS("readtimings.log", plStatusLog::kWhite, "----- Reading page %s>%s took %.1f ms", + pageNode->GetPageInfo().GetAge(), pageNode->GetPageInfo().GetPage(), + hsTimer::FullTicksToMs(readRoomTime)); + } +} + +class plPageInAgeIter : public plRegistryPageIterator +{ +private: + plKey fDestKey; + const char* fAgeName; + std::vector fLocations; + +public: + plPageInAgeIter(plKey destKey, const char *ageName) : fDestKey(destKey), fAgeName(ageName) {} + ~plPageInAgeIter() + { + plClientMsg* pMsg1 = TRACKED_NEW plClientMsg(plClientMsg::kLoadRoomHold); + for (int i = 0; i < fLocations.size(); i++) + { + pMsg1->AddRoomLoc(fLocations[i]); + } + pMsg1->Send(fDestKey); + } + virtual hsBool EatPage(plRegistryPageNode* page) + { + if (stricmp(page->GetPageInfo().GetAge(), fAgeName) == 0) + { + plUoid uoid(page->GetPageInfo().GetLocation(), 0, ""); + fLocations.push_back(uoid.GetLocation()); + } + return true; + } +}; + +// PageInAge is intended for bulk global ages, like GlobalAnimations or GlobalClothing +// that store a lot of data we always want available. (Used to be known as PageInHold) +void plResManager::PageInAge(const char *age) +{ + plSynchEnabler ps(false); // disable dirty tracking while paging in + plUoid lu(kClient_KEY); + plKey clientKey = hsgResMgr::ResMgr()->FindKey(lu); + + // Tell the client to load all the keys for this age, to make the loading process work better + plClientMsg *loadAgeKeysMsg = TRACKED_NEW plClientMsg(plClientMsg::kLoadAgeKeys); + loadAgeKeysMsg->SetAgeName(age); + loadAgeKeysMsg->Send(clientKey); + + // Then iterate through each room in the age. The iterator will send the load message + // off on destruction. + plPageInAgeIter iter(clientKey, age); + IterateAllPages(&iter); +} + +//// VerifyPages ///////////////////////////////////////////////////////////// +// Runs through all the pages and ensures they are all up-to-date in version +// numbers and that no out-of-date objects exist in them + +hsBool plResManager::VerifyPages() +{ + hsTArray invalidPages, newerPages; + + // Step 1: verify major/minor version changes + if (plResMgrSettings::Get().GetFilterNewerPageVersions() || + plResMgrSettings::Get().GetFilterOlderPageVersions()) + { + PageSet::iterator it = fAllPages.begin(); + while (it != fAllPages.end()) + { + plRegistryPageNode* page = *it; + it++; + + if (page->GetPageCondition() == kPageTooNew && plResMgrSettings::Get().GetFilterNewerPageVersions()) + { + newerPages.Append(page); + fAllPages.erase(page); + } + else if ( + (page->GetPageCondition() == kPageCorrupt || + page->GetPageCondition() == kPageOutOfDate) + && plResMgrSettings::Get().GetFilterOlderPageVersions()) + { + invalidPages.Append(page); + fAllPages.erase(page); + } + } + } + + // Handle all our invalid pages now + if (invalidPages.GetCount() > 0) + { + if (!IDeleteBadPages(invalidPages, false)) + return false; + } + + // Warn about newer pages + if (newerPages.GetCount() > 0) + { + if (!IWarnNewerPages(newerPages)) + return false; + } + + // Step 2 of verification: make sure no sequence numbers conflict + PageSet::iterator it = fAllPages.begin(); + for (; it != fAllPages.end(); it++) + { + plRegistryPageNode* page = *it; + + PageSet::iterator itUp = it; + itUp++; + for (; itUp != fAllPages.end(); itUp++) + { + plRegistryPageNode* upPage = *itUp; + if (page->GetPageInfo().GetLocation() == upPage->GetPageInfo().GetLocation()) + { + invalidPages.Append(upPage); + fAllPages.erase(itUp); + break; + } + } + } + + // Redo our loaded pages list, since Verify() might force the page's keys to load or unload + fLoadedPages.clear(); + it = fAllPages.begin(); + while (it != fAllPages.end()) + { + plRegistryPageNode* page = *it; + it++; + + if (page->IsLoaded()) + fLoadedPages.insert(page); + } + + // Handle all our conflicting pages now + if (invalidPages.GetCount() > 0) + return IDeleteBadPages(invalidPages, true); + + return true; +} + +//// IDeleteBadPages ///////////////////////////////////////////////////////// +// Given an array of pages that are invalid (major version out-of-date or +// whatnot), asks the user what we should do about them. + +static void ICatPageNames(hsTArray& pages, char* buf, int bufSize) +{ + for (int i = 0; i < pages.GetCount(); i++) + { + if (i >= 25) + { + strcat(buf, "...\n"); + break; + } + + const char* pagePath = pages[i]->GetPagePath(); + const char* pageFile = plFileUtils::GetFileName(pagePath); + + if (strlen(buf) + strlen(pageFile) > bufSize - 5) + { + strcat(buf, "...\n"); + break; + } + + strcat(buf, pageFile); + strcat(buf, "\n"); + } +} + +hsBool plResManager::IDeleteBadPages(hsTArray& invalidPages, hsBool conflictingSeqNums) +{ +#ifndef PLASMA_EXTERNAL_RELEASE + if (!hsMessageBox_SuppressPrompts) + { + char msg[4096]; + + // Prompt what to do + if (conflictingSeqNums) + strcpy(msg, "The following pages have conflicting sequence numbers. This usually happens when " + "you copy data files between machines that had random sequence numbers assigned at " + "export. To avoid crashing, these pages will be deleted:\n\n"); + else + strcpy(msg, "The following pages are out of date and will be deleted:\n\n"); + + ICatPageNames(invalidPages, msg, sizeof(msg)); + + hsMessageBox(msg, "Warning", hsMessageBoxNormal); + } +#endif // PLASMA_EXTERNAL_RELEASE + + // Delete 'em + for (int i = 0; i < invalidPages.GetCount(); i++) + { + invalidPages[i]->DeleteSource(); + delete invalidPages[i]; + } + invalidPages.Reset(); + + fLastFoundPage = nil; + + return true; +} + +//// IWarnNewerPages ///////////////////////////////////////////////////////// +// Given an array of pages that are newer (minor or major version are newer +// than the "current" one), warns the user about them but does nothing to +// them. + +hsBool plResManager::IWarnNewerPages(hsTArray &newerPages) +{ +#ifndef PLASMA_EXTERNAL_RELEASE + if (!hsMessageBox_SuppressPrompts) + { + char msg[4096]; + // Prompt what to do + strcpy(msg, "The following pages have newer version numbers than this client and cannot be \nloaded. " + "They will be ignored but their files will NOT be deleted:\n\n"); + + ICatPageNames(newerPages, msg, sizeof(msg)); + + hsMessageBox(msg, "Warning", hsMessageBoxNormal); + } +#endif // PLASMA_EXTERNAL_RELEASE + + + // Not deleting the files, just delete them from memory + for (int i = 0; i < newerPages.GetCount(); i++) + delete newerPages[i]; + newerPages.Reset(); + + fLastFoundPage = nil; + + return true; +} + +//// plOurReffer ///////////////////////////////////////////////////////////// +// Our little reffer key iterator + +class plOurReffer : public plRegistryKeyIterator +{ +protected: + hsTArray fRefArray; + +public: + plOurReffer() {} + virtual ~plOurReffer() { UnRef(); } + + void UnRef() { fRefArray.Reset(); } + + virtual hsBool EatKey(const plKey& key) + { + // This is cute. Thanks to our new plKey smart pointers, all we have to + // do is append the key to our ref array. This automatically guarantees us + // an extra ref on the key, which is what we're trying to do. Go figure. + fRefArray.Append(key); + + return true; + } +}; + +void plResManager::DumpUnusedKeys(plRegistryPageNode* page) const +{ + plOurReffer reffer; + page->IterateKeys(&reffer); +} + +plRegistryPageNode* plResManager::CreatePage(const plLocation& location, const char* age, const char* page) +{ + plRegistryPageNode* pageNode = TRACKED_NEW plRegistryPageNode(location, age, page, fDataPath.c_str()); + fAllPages.insert(pageNode); + + return pageNode; +} + +//// AddPage ///////////////////////////////////////////////////////////////// + +void plResManager::AddPage(plRegistryPageNode* page) +{ + fAllPages.insert(page); + if (page->IsLoaded()) + fLoadedPages.insert(page); +} + +//// LoadPageKeys /////////////////////////////////////////////////////////// + +void plResManager::LoadPageKeys(plRegistryPageNode* pageNode) +{ + if (pageNode->IsFullyLoaded()) + return; + + // Load it and add it to the loaded list + pageNode->LoadKeys(); + + if (fPageListLock == 0) + fLoadedPages.insert(pageNode); + else + fPagesNeedCleanup = true; +} + +//// sIReportLeak //////////////////////////////////////////////////////////// +// Handy tiny function here + +static void sIReportLeak(plKeyImp* key, plRegistryPageNode* page) +{ + class plKeyImpRef : public plKeyImp + { + public: + UInt16 GetRefCnt() const { return fRefCount; } + }; + + static bool alreadyDone = false; + static plRegistryPageNode* lastPage; + + if (page != nil) + lastPage = page; + + if (key == nil) + { + alreadyDone = false; + return; + } + + if (!alreadyDone) + { + // Print out page header + hsStatusMessageF(" Leaks in page %s>%s[%08x]:\n", lastPage->GetPageInfo().GetAge(), lastPage->GetPageInfo().GetPage(), lastPage->GetPageInfo().GetLocation().GetSequenceNumber()); + alreadyDone = true; + } + + int refsLeft = ((plKeyImpRef*)key)->GetRefCnt() - 1; + if (refsLeft == 0) + return; + + char tempStr[256], tempStr2[128]; + if (key->ObjectIsLoaded() == nil) + sprintf(tempStr2, "(key only, %d refs left)", refsLeft); + else + sprintf(tempStr2, "- %d bytes - %d refs left", key->GetDataLen(), refsLeft); + + hsStatusMessageF(" %s: %s %s\n", plFactory::GetNameOfClass(key->GetUoid().GetClassType()), + key->GetUoid().StringIze(tempStr), tempStr2); +} + +//// UnloadPageObjects /////////////////////////////////////////////////////// +// Unloads all the objects in a given page. Once this is complete, all +// object pointers for every key in the page *should* be nil. Note that we're +// given a hint class index to start with (like plSceneNode) that should do +// most of the work for us via unreffing. +// +// Update 5.20: since there are so many problems with doing this, don't +// delete the objects, just print out a memleak report. -mcn + +void plResManager::UnloadPageObjects(plRegistryPageNode* pageNode, UInt16 classIndexHint) +{ + if (!pageNode->IsLoaded()) + return; + + class plUnloadObjectsIterator : public plRegistryKeyIterator + { + public: + virtual hsBool EatKey(const plKey& key) + { + sIReportLeak((plKeyImp*)key, nil); + return true; + } + }; + + sIReportLeak(nil, pageNode); + + plUnloadObjectsIterator iterator; + + if (classIndexHint != UInt16(-1)) + pageNode->IterateKeys(&iterator, classIndexHint); + else + pageNode->IterateKeys(&iterator); +} + +//// FindPage //////////////////////////////////////////////////////////////// + +plRegistryPageNode* plResManager::FindPage(const plLocation& location) const +{ + // Quick optimization + if (fLastFoundPage != nil && fLastFoundPage->GetPageInfo().GetLocation() == location) + return fLastFoundPage; + + PageSet::const_iterator it; + for (it = fAllPages.begin(); it != fAllPages.end(); it++) + { + const plLocation& pageloc = (*it)->GetPageInfo().GetLocation(); + if (pageloc == location) + { + fLastFoundPage = *it; + return fLastFoundPage; + } + } + + return nil; +} + +//// FindPage //////////////////////////////////////////////////////////////// + +plRegistryPageNode* plResManager::FindPage(const char* age, const char* page) const +{ + PageSet::const_iterator it; + for (it = fAllPages.begin(); it != fAllPages.end(); it++) + { + const plPageInfo& info = (*it)->GetPageInfo(); + if (hsStrCaseEQ(info.GetAge(), age) && + hsStrCaseEQ(info.GetPage(), page)) + return *it; + } + + return nil; +} + +////////////////////////////////////////////////////////////////////////////// +//// Key Operations ////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// AddKey ////////////////////////////////////////////////////////////////// +// Adds a key to the registry. Assumes uoid already set. + +void plResManager::AddKey(plKeyImp* key) +{ + plRegistryPageNode* page = FindPage(key->GetUoid().GetLocation()); + if (page == nil) + return; + + page->AddKey(key); + fLoadedPages.insert(page); +} + +void plResManager::IKeyReffed(plKeyImp* key) +{ + plRegistryPageNode* page = FindPage(key->GetUoid().GetLocation()); + if (page == nil) + { + hsAssert(0, "Couldn't find page that key belongs to"); + return; + } + + page->SetKeyUsed(key); +} + +void plResManager::IKeyUnreffed(plKeyImp* key) +{ + plRegistryPageNode* page = FindPage(key->GetUoid().GetLocation()); + if (page == nil) + { + hsAssert(0, "Couldn't find page that key belongs to"); + return; + } + + bool removed = page->SetKeyUnused(key); + hsAssert(removed, "Key wasn't removed from page"); + + if (removed) + { + if (!page->IsLoaded()) + { + if (fPageListLock == 0) + fLoadedPages.erase(page); + else + fPagesNeedCleanup = true; + } + } +} + +////////////////////////////////////////////////////////////////////////////// +//// Iterator Functions ////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// IterateKeys Helper Class //////////////////////////////////////////////// + +class plKeyIterEater : public plRegistryPageIterator +{ +protected: + plRegistryKeyIterator* fIter; + +public: + plKeyIterEater(plRegistryKeyIterator* iter) : fIter(iter) {} + virtual hsBool EatPage(plRegistryPageNode* keyNode) + { + return keyNode->IterateKeys(fIter); + } +}; + +//// IterateKeys ///////////////////////////////////////////////////////////// + +hsBool plResManager::IterateKeys(plRegistryKeyIterator* iterator) +{ + plKeyIterEater myEater(iterator); + return IteratePages(&myEater, nil); +} + +hsBool plResManager::IterateKeys(plRegistryKeyIterator* iterator, const plLocation& pageToRestrictTo) +{ + plRegistryPageNode* page = FindPage(pageToRestrictTo); + if (page == nil) + { + hsAssert(false, "Page not found to iterate through"); + return false; + } + + plKeyIterEater myEater(iterator); + return myEater.EatPage(page); +} + +//// IteratePages //////////////////////////////////////////////////////////// +// Iterate through all LOADED pages + +hsBool plResManager::IteratePages(plRegistryPageIterator* iterator, const char* ageToRestrictTo) +{ + ILockPages(); + + PageSet::const_iterator it; + for (it = fLoadedPages.begin(); it != fLoadedPages.end(); it++) + { + plRegistryPageNode* page = *it; + if (page->GetPageInfo().GetLocation() == plLocation::kGlobalFixedLoc) + continue; + + if (!ageToRestrictTo || hsStrCaseEQ(page->GetPageInfo().GetAge(), ageToRestrictTo)) + { + if (!iterator->EatPage(page)) + { + IUnlockPages(); + return false; + } + } + } + + IUnlockPages(); + + return true; +} + +//// IterateAllPages ///////////////////////////////////////////////////////// +// Iterate through ALL pages + +hsBool plResManager::IterateAllPages(plRegistryPageIterator* iterator) +{ + ILockPages(); + + PageSet::const_iterator it; + for (it = fAllPages.begin(); it != fAllPages.end(); it++) + { + plRegistryPageNode* page = *it; + if (page->GetPageInfo().GetLocation() == plLocation::kGlobalFixedLoc) + continue; + + if (!iterator->EatPage(page)) + { + IUnlockPages(); + return false; + } + } + + IUnlockPages(); + + return true; +} + +//// ILockPages ////////////////////////////////////////////////////////////// +// See, when we iterate through pages, our iterate function might decide to +// move pages, either explicitly through loads or implicitly through key +// deletions. So, before we iterate, we lock 'em all so they won't move, +// then unlock and move them to their proper places at the end. + +void plResManager::ILockPages() +{ + if (fPageListLock == 0) + fPagesNeedCleanup = false; + + fPageListLock++; +} + +//// IUnlockPages //////////////////////////////////////////////////////////// + +void plResManager::IUnlockPages() +{ + fPageListLock--; + if (fPageListLock == 0 && fPagesNeedCleanup) + { + fPagesNeedCleanup = false; + + fLoadedPages.clear(); + + PageSet::const_iterator it; + for (it = fAllPages.begin(); it != fAllPages.end(); it++) + { + plRegistryPageNode* page = *it; + if (page->IsLoaded()) + fLoadedPages.insert(page); + } + } +} + +// Defined here 'cause release build hates it defined in settings.h for some reason +#include "plResMgrSettings.h" +plResMgrSettings& plResMgrSettings::Get() +{ + static plResMgrSettings fSettings; + return fSettings; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManager.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManager.h new file mode 100644 index 00000000..65c587f9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManager.h @@ -0,0 +1,227 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plResManager_h_inc +#define plResManager_h_inc + +#include "hsResMgr.h" +#include +#include +#include + +class plRegistryPageNode; +class plRegistryKeyIterator; +class plRegistryPageIterator; +class plRegistryDataStream; +class plResAgeHolder; +class plResManagerHelper; +class plDispatch; + +// plProgressProc is a proc called every time an object loads, to keep a progress bar for +// loading ages up-to-date. +typedef void(*plProgressProc)(plKey key); + +class plResManager : public hsResMgr +{ +public: + plResManager(); + virtual ~plResManager(); + + // If the ResManager has already been initialized, you should call Reset after setting this + void SetDataPath(const char* path) { fDataPath = path; } + + // Mainly for external tools. + void AddSinglePage(const char* path); + plRegistryPageNode* FindSinglePage(const char* path) const; + void RemoveSinglePage(const char* path); + + //--------------------------- + // Load and Unload + //--------------------------- + virtual void Load (const plKey& objKey); // places on list to be loaded + virtual hsBool Unload(const plKey& objKey); // Unregisters (deletes) an object, Return true if successful + virtual plKey CloneKey(const plKey& objKey); + + //--------------------------- + // Finding Functions + //--------------------------- + plKey FindOriginalKey(const plUoid&); + virtual plKey FindKey(const plUoid&); // Same as above, but will check the uoid for clones + const plLocation& FindLocation(const char* age, const char* page) const; + // Use nil for any strings you don't need + const void GetLocationStrings(const plLocation& loc, char* ageBuffer, char* pageBuffer) const; + + //--------------------------- + // Establish reference linkage + //--------------------------- + virtual hsBool AddViaNotify(const plKey& key, plRefMsg* msg, plRefFlags::Type flags); + virtual hsBool AddViaNotify(plRefMsg* msg, plRefFlags::Type flags); // msg->fRef->GetKey() == sentKey + + virtual hsBool SendRef(const plKey& key, plRefMsg* refMsg, plRefFlags::Type flags); + virtual hsBool SendRef(hsKeyedObject* ko, plRefMsg* refMsg, plRefFlags::Type flags); + + //--------------------------- + // Reding and Writing keys + //--------------------------- + // Read a Key in, and Notify me when the Object is loaded + virtual plKey ReadKeyNotifyMe(hsStream* stream, plRefMsg* retMsg, plRefFlags::Type flags); + // Just read the Key data in and find a match in the registry and return it. + virtual plKey ReadKey(hsStream* stream); + + // For convenience you can write a key using the KeyedObject or the Key...same result + virtual void WriteKey(hsStream* s, hsKeyedObject* obj); + virtual void WriteKey(hsStream* s, const plKey& key); + + //--------------------------- + // Reding and Writing Objects directly + //--------------------------- + virtual plCreatable* ReadCreatable(hsStream* s); + virtual void WriteCreatable(hsStream* s, plCreatable* cre); + + virtual plCreatable* ReadCreatableVersion(hsStream* s); + virtual void WriteCreatableVersion(hsStream* s, plCreatable* cre); + + //--------------------------- + // Registry Modification Functions + //--------------------------- + virtual plKey NewKey(const char* name, hsKeyedObject* object, const plLocation& loc, const plLoadMask& m = plLoadMask::kAlways); + virtual plKey NewKey(plUoid& newUoid, hsKeyedObject* object); + + virtual plDispatchBase* Dispatch(); + + virtual void SetProgressBarProc(plProgressProc proc); + + //--------------------------- + // Load optimizations + //--------------------------- + void LoadAgeKeys(const char* age); + void DropAgeKeys(const char* age); + void PageInRoom(const plLocation& page, UInt16 objClassToRef, plRefMsg* refMsg); + void PageInAge(const char* age); + + // Usually, a page file is kept open during load because the first keyed object + // read causes all the other objects to be read before it returns. In some + // cases though (mostly just the texture file), this doesn't work. In that + // case, we just want to force it to stay open until we're done reading the age. + void KeepPageOpen(const plLocation& page, hsBool keepOpen); + + // We're on the way down, act accordingly. + virtual void BeginShutdown(); + + // Determines whether the time to read each object is dumped to a log + void LogReadTimes(hsBool logReadTimes); + + // All keys version + hsBool IterateKeys(plRegistryKeyIterator* iterator); + // Single page version + hsBool IterateKeys(plRegistryKeyIterator* iterator, const plLocation& pageToRestrictTo); + // Iterate through loaded pages + hsBool IteratePages(plRegistryPageIterator* iterator, const char* ageToRestrictTo = nil); + // Iterate through ALL pages, loaded or not + hsBool IterateAllPages(plRegistryPageIterator* iterator); + + // Helpers for key iterators + void LoadPageKeys(plRegistryPageNode* pageNode); + void UnloadPageObjects(plRegistryPageNode* pageNode, UInt16 classIndexHint); + void DumpUnusedKeys(plRegistryPageNode* page) const; + plRegistryPageNode* FindPage(const plLocation& location) const; + plRegistryPageNode* FindPage(const char* age, const char* page) const; + + // Runs through all the pages and verifies that the data versions are good + hsBool VerifyPages(); + +protected: + friend class hsKeyedObject; + friend class plKeyImp; + friend class plResManagerHelper; + + virtual plKey ReRegister(const char* nm, const plUoid& uoid); + virtual hsBool ReadObject(plKeyImp* key); // plKeys call this when needed + virtual hsBool IReadObject(plKeyImp* pKey, hsStream *stream); + + plCreatable* IReadCreatable(hsStream* s) const; + plKey ICloneKey(const plUoid& objUoid, UInt32 playerID, UInt32 cloneID); + + virtual void IKeyReffed(plKeyImp* key); + virtual void IKeyUnreffed(plKeyImp* key); + + virtual hsBool IReset(); + virtual hsBool IInit(); + virtual void IShutdown(); + + void IPageOutSceneNodes(hsBool forceAll); + void IDropAllAgeKeys(); + + hsKeyedObject* IGetSharedObject(plKeyImp* pKey); + + void IUnloadPageKeys(plRegistryPageNode* pageNode, hsBool dontClear = false); + + hsBool IDeleteBadPages(hsTArray& invalidPages, hsBool conflictingSeqNums); + hsBool IWarnNewerPages(hsTArray& newerPages); + + void ILockPages(); + void IUnlockPages(); + + void AddPage(plRegistryPageNode* page); + + // Adds a key to the registry. Assumes uoid already set + void AddKey(plKeyImp* key); + + plRegistryPageNode* CreatePage(const plLocation& location, const char* age, const char* page); + + hsBool fInited; + UInt16 fPageOutHint; + + // True if we're reading in an object. We only read one object at a time + hsBool fReadingObject; + std::vector fQueuedReads; + + std::string fDataPath; + + plDispatch* fDispatch; + + UInt32 fCurCloneID; // Current clone ID. If it isn't zero, we're cloning + UInt32 fCurClonePlayerID; + UInt32 fCloningCounter; // Next clone ID to use. + + typedef std::map HeldAgeKeyMap; + HeldAgeKeyMap fHeldAgeKeys; + plProgressProc fProgressProc; + + plResManagerHelper *fMyHelper; + + hsBool fLogReadTimes; + + UInt8 fPageListLock; // Number of locks on the page lists. If it's greater than zero, they can't be modified + hsBool fPagesNeedCleanup; // True if something modified the page lists while they were locked. + + typedef std::set PageSet; + PageSet fAllPages; // All the pages, loaded or not + PageSet fLoadedPages; // Just the loaded pages + + mutable plRegistryPageNode* fLastFoundPage; +}; + +#endif // plResManager_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManagerHelper.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManagerHelper.cpp new file mode 100644 index 00000000..cfae039d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManagerHelper.cpp @@ -0,0 +1,725 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plResManagerHelper - The wonderful helper class that can receive messages +// for the resManager. +// +//// History ///////////////////////////////////////////////////////////////// +// +// 6.7.2002 mcn - Created +// +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plResManagerHelper.h" +#include "plResManager.h" +#include "plRegistryNode.h" +#include "plRegistryHelpers.h" +//#include "plRegistry.h" +#include "plResMgrSettings.h" + +#include "../pnKeyedObject/plFixedKey.h" +#include "../plMessage/plResMgrHelperMsg.h" +#include "../plStatusLog/plStatusLog.h" +#include "hsTimer.h" + +#ifdef MCN_RESMGR_DEBUGGING + +static const int kLogSize = 40; +static const float kUpdateDelay = 0.5f; + +#include "../plInputCore/plInputInterface.h" +#include "../plInputCore/plInputDevice.h" +#include "../plInputCore/plInputInterfaceMgr.h" +#include "../pnInputCore/plInputMap.h" +#include "../plMessage/plInputEventMsg.h" +#include "../plMessage/plInputIfaceMgrMsg.h" +#include "../pnKeyedObject/plKeyImp.h" + +#endif + +/// Logging #define for easier use +#define kResMgrLog( level ) if( plResMgrSettings::Get().GetLoggingLevel() >= level ) plStatusLog::AddLineS( "resources.log", + + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plResManagerHelper *plResManagerHelper::fInstance = nil; + +plResManagerHelper::plResManagerHelper( plResManager *resMgr ) +{ + fResManager = resMgr; + fInstance = this; + + fInShutdown = false; +#ifdef MCN_RESMGR_DEBUGGING + fDebugScreen = nil; + fCurrAge = -1; + fCurrAgeExpanded = false; + fRefreshing = false; + fDebugDisplayType = 0; +#endif +} +plResManagerHelper::~plResManagerHelper() +{ + fInstance = nil; +} + +//// Shutdown //////////////////////////////////////////////////////////////// + +void plResManagerHelper::Shutdown( void ) +{ + EnableDebugScreen( false ); + UnRegisterAs( kResManagerHelper_KEY ); +} + +//// Init //////////////////////////////////////////////////////////////////// + +void plResManagerHelper::Init( void ) +{ + RegisterAs( kResManagerHelper_KEY ); +} + +//// MsgReceive ////////////////////////////////////////////////////////////// + +hsBool plResManagerHelper::MsgReceive( plMessage *msg ) +{ + plResMgrHelperMsg *refferMsg = plResMgrHelperMsg::ConvertNoRef( msg ); + if( refferMsg != nil ) + { + if( refferMsg->GetCommand() == plResMgrHelperMsg::kKeyRefList ) + { + // Message to let go of these keys. So unref the key list, destroy it and we're done! + kResMgrLog( 2 ) 0xff80ff80, "Dropping page keys after timed delay" ); + hsStatusMessage( "*** Dropping page keys after timed delay ***" ); + + delete refferMsg->fKeyList; + refferMsg->fKeyList = nil; + } + else if( refferMsg->GetCommand() == plResMgrHelperMsg::kUpdateDebugScreen ) + { + IUpdateDebugScreen(); + } + else if( refferMsg->GetCommand() == plResMgrHelperMsg::kEnableDebugScreen ) + EnableDebugScreen( true ); + else if( refferMsg->GetCommand() == plResMgrHelperMsg::kDisableDebugScreen ) + EnableDebugScreen( false ); + + return true; + } + + return hsKeyedObject::MsgReceive( msg ); +} + +//// Read/Write ////////////////////////////////////////////////////////////// + +void plResManagerHelper::Read( hsStream *s, hsResMgr *mgr ) +{ + hsAssert( false, "You should never read me in!" ); +} + +void plResManagerHelper::Write( hsStream *s, hsResMgr *mgr ) +{ + hsAssert( false, "You should never write me out!" ); +} + +//// LoadAndHoldPageKeys ///////////////////////////////////////////////////// +// Loads and refs the keys for the given page, then sends the ref list as +// a list to ourself, time delayed 1 second, so that we can unref them one +// second later. + +void plResManagerHelper::LoadAndHoldPageKeys( plRegistryPageNode *page ) +{ + hsAssert( GetKey() != nil, "Can't load and hold keys when we don't have a key for the helper" ); + + // Create our msg + plResMgrHelperMsg *refferMsg = TRACKED_NEW plResMgrHelperMsg( plResMgrHelperMsg::kKeyRefList ); + refferMsg->fKeyList = TRACKED_NEW plResPageKeyRefList; + + fResManager->LoadPageKeys(page); + page->IterateKeys( refferMsg->fKeyList ); + + // Load and ref the keys +#ifdef HS_DEBUGGING + char msg[ 256 ]; + sprintf( msg, "*** Temporarily loading keys for room %s>%s based on FindKey() query, will drop in 1 sec ***", page->GetPageInfo().GetAge(), page->GetPageInfo().GetPage() ); + hsStatusMessage( msg ); +#endif + kResMgrLog( 2 ) 0xff80ff80, "Temporarily loading keys for room %s>%s, will drop in 1 sec", page->GetPageInfo().GetAge(), page->GetPageInfo().GetPage() ); + + // Deliver the message to ourselves! + refferMsg->SetTimeStamp( hsTimer::GetSysSeconds() + 1.f ); + refferMsg->Send( GetKey() ); +} + +#ifdef MCN_RESMGR_DEBUGGING + +////////////////////////////////////////////////////////////////////////////// +//// plResMgrDebugInterface Definition /////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +class plResMgrDebugInterface : public plInputInterface +{ + protected: + plResManagerHelper * const fParent; + + virtual ControlEventCode *IGetOwnedCodeList( void ) const + { + static ControlEventCode codes[] = { END_CONTROLS }; + return codes; + } + + public: + + plResMgrDebugInterface( plResManagerHelper * const mgr ) : fParent( mgr ) { SetEnabled( true ); } + + virtual UInt32 GetPriorityLevel( void ) const { return kGUISystemPriority + 10; } + virtual hsBool InterpretInputEvent( plInputEventMsg *pMsg ) + { + plKeyEventMsg *pKeyMsg = plKeyEventMsg::ConvertNoRef( pMsg ); + if( pKeyMsg != nil && pKeyMsg->GetKeyDown() ) + { + if( pKeyMsg->GetKeyCode() == KEY_UP && fParent->fCurrAge >= 0 ) + { + fParent->fCurrAge--; + fParent->IUpdateDebugScreen( true ); + return true; + } + else if( pKeyMsg->GetKeyCode() == KEY_DOWN ) + { + fParent->fCurrAge++; + fParent->IUpdateDebugScreen( true ); + return true; + } + else if( pKeyMsg->GetKeyCode() == KEY_ENTER ) + { + fParent->fCurrAgeExpanded = !fParent->fCurrAgeExpanded; + fParent->IUpdateDebugScreen( true ); + return true; + } + else if( pKeyMsg->GetKeyCode() == KEY_ESCAPE ) + { + plResMgrHelperMsg *msg = TRACKED_NEW plResMgrHelperMsg( plResMgrHelperMsg::kDisableDebugScreen ); + msg->Send( fParent->GetKey() ); + return true; + } + else if( pKeyMsg->GetKeyCode() == KEY_RIGHT ) + { + if( !fParent->fCurrAgeExpanded ) + fParent->fCurrAgeExpanded = true; + else + { + fParent->fDebugDisplayType++; + if( fParent->fDebugDisplayType == plResManagerHelper::kMaxDisplayType ) + fParent->fDebugDisplayType = 0; + } + fParent->IUpdateDebugScreen( true ); + return true; + } + else if( pKeyMsg->GetKeyCode() == KEY_LEFT ) + { + fParent->fCurrAgeExpanded = false; + fParent->IUpdateDebugScreen( true ); + return true; + } + } + + return false; + } + + virtual UInt32 GetCurrentCursorID( void ) const { return 0; } + virtual hsBool HasInterestingCursorID( void ) const { return false; } +}; + +#endif + +//// EnableDebugScreen /////////////////////////////////////////////////////// + +void plResManagerHelper::EnableDebugScreen( hsBool enable ) +{ +#ifdef MCN_RESMGR_DEBUGGING + if( enable ) + { + if( fDebugScreen == nil ) + { + fDebugScreen = plStatusLogMgr::GetInstance().CreateStatusLog( kLogSize, "ResManager Status", plStatusLog::kFilledBackground | plStatusLog::kDontWriteFile ); + fRefreshing = true; + + plResMgrHelperMsg *msg = TRACKED_NEW plResMgrHelperMsg( plResMgrHelperMsg::kUpdateDebugScreen ); +// msg->SetTimeStamp( hsTimer::GetSysSeconds() + kUpdateDelay ); + msg->Send( GetKey() ); + + fDebugInput = TRACKED_NEW plResMgrDebugInterface( this ); + plInputIfaceMgrMsg *imsg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kAddInterface ); + imsg->SetIFace( fDebugInput ); + imsg->Send(); + } + } + else + { + fRefreshing = false; + if( fDebugScreen != nil ) + { + delete fDebugScreen; + fDebugScreen = nil; + + plInputIfaceMgrMsg *imsg = TRACKED_NEW plInputIfaceMgrMsg( plInputIfaceMgrMsg::kRemoveInterface ); + imsg->SetIFace( fDebugInput ); + imsg->Send(); + + hsRefCnt_SafeUnRef( fDebugInput ); + fDebugInput = nil; + } + } +#endif +} + +//// IUpdateDebugScreen ///////////////////////////////////////////////////// + +#ifdef MCN_RESMGR_DEBUGGING +class plDebugPrintIterator : public plRegistryPageIterator, plRegistryKeyIterator +{ + public: + plStatusLog *fLog; + UInt8 fStep, fLines; + UInt32 &fLoadedCount, &fHoldingCount, fPageCount, fAgeIndex; + char fCurrAge[ 128 ]; + UInt32 fLoadedKeys, fTotalKeys, fTotalSize, fLoadedSize; + + plResManagerHelper *fParent; + + plDebugPrintIterator( plResManagerHelper *parent, plStatusLog *log, UInt32 &loadedCount, UInt32 &holdingCount ) + : fParent( parent ), fLog( log ), fStep( 0 ), fLines( 0 ), fLoadedCount( loadedCount ), fHoldingCount( holdingCount ) + { + fLoadedCount = fHoldingCount = 0; + fCurrAge[ 0 ] = 0; + fPageCount = 0; + fAgeIndex = 0; + } + + virtual hsBool EatPage( plRegistryPageNode *page ) + { + if( fStep == 0 ) + { + fLog->AddLineF( 0xff80ff80, "Loaded Pages" ); + fStep = 1; + fLines++; + } + else if( fStep == 1 && page != nil && !page->IsLoaded() ) + { + fStep = 2; + fLog->AddLineF( 0xff80ff80, "Holding Pages" ); + fLines++; + } + + if( page != nil && page->IsLoaded() ) + fLoadedCount++; + else if( page != nil ) + fHoldingCount++; + + // Changed ages? + if( page == nil || strcmp( fCurrAge, page->GetPageInfo().GetAge() ) != 0 ) + { + // Print some info for the last age we were on + if( fCurrAge[ 0 ] != 0 ) + { + if( fParent->fCurrAge != fAgeIndex || !fParent->fCurrAgeExpanded ) + { + if( fLines < kLogSize - 4 ) + { + UInt32 color = plStatusLog::kWhite; + if( fParent->fCurrAge == fAgeIndex ) + color = plStatusLog::kYellow; + + fLog->AddLineF( color, " %s (%d pages)", fCurrAge, fPageCount ); + fLines++; + } + else if( fLines == kLogSize - 4 ) + { + fLog->AddLineF( plStatusLog::kWhite, " ..." ); + fLines++; + } + } + fAgeIndex++; + } + fPageCount = 0; + if( page != nil ) + strncpy( fCurrAge, page->GetPageInfo().GetAge(), sizeof( fCurrAge ) - 1 ); + else + fCurrAge[ 0 ] = 0; + + if( fParent->fCurrAge == fAgeIndex && fParent->fCurrAgeExpanded ) + { + // Print header now, since we won't be printing a footer + if( fLines < kLogSize - 4 ) + { + fLog->AddLineF( plStatusLog::kYellow, " %s>", fCurrAge ); + fLines++; + } + else if( fLines == kLogSize - 4 ) + { + fLog->AddLineF( plStatusLog::kWhite, " ..." ); + fLines++; + } + } + } + + fPageCount++; + + if( fParent->fCurrAge == fAgeIndex && fParent->fCurrAgeExpanded && page != nil ) + { + // Count keys for this page + fTotalKeys = fLoadedKeys = fTotalSize = fLoadedSize = 0; + page->IterateKeys( this ); + + // Print page for this expanded age view + if( fLines < kLogSize - 4 ) + { + if( fParent->fDebugDisplayType == plResManagerHelper::kSizes ) + fLog->AddLineF( plStatusLog::kWhite, " %s (%d keys @ %4.1fk, %d loaded @ %4.1fk)", page->GetPageInfo().GetPage(), fTotalKeys, fTotalSize / 1024.f, fLoadedKeys, fLoadedSize / 1024.f ); + else if( fParent->fDebugDisplayType == plResManagerHelper::kPercents ) + fLog->AddLineF( plStatusLog::kWhite, " %s (%d%% loaded of %d keys @ %4.1fk)", page->GetPageInfo().GetPage(), fLoadedSize * 100 / ( fTotalSize > 0 ? fTotalSize : -1 ), fTotalKeys, fTotalSize / 1024.f ); + else //if( fParent->fDebugDisplayType == plResManagerHelper::kBars ) + { + const int startPos = 20, length = 32; + + char line[ 128 ]; + memset( line, ' ', sizeof( line ) - 1 ); + line[ 127 ] = 0; + if( strlen( page->GetPageInfo().GetPage() ) < startPos - 2 ) + memcpy( line + 2, page->GetPageInfo().GetPage(), strlen( page->GetPageInfo().GetPage() ) ); + else + memcpy( line + 2, page->GetPageInfo().GetPage(), startPos - 2 ); + + line[ startPos ] = '|'; + if( fTotalSize == 0 ) + { + line[ startPos + 1 ] = '|'; + line[ startPos + 2 ] = 0; + } + else + { + char temp[ 12 ]; + sprintf( temp, "%d%%", fLoadedSize * 100 / fTotalSize ); + + line[ startPos + length + 1 ] = '|'; + int i, sum = 0; + for( i = startPos + 1; i < startPos + length + 1 && sum < fLoadedSize; i++ ) + { + line[ i ] = '='; + sum += fTotalSize / length; + } + line[ startPos + length + 2 ] = 0; + + memcpy( line + startPos + 1, temp, strlen( temp ) ); + } + + fLog->AddLine( line, plStatusLog::kWhite ); + } + fLines++; + } + else if( fLines == kLogSize - 4 ) + { + fLog->AddLineF( plStatusLog::kWhite, " ..." ); + fLines++; + } + } + + return true; + } + + virtual hsBool EatKey( const plKey& key ) + { + if( key->ObjectIsLoaded() ) + { + fLoadedKeys++; + fLoadedSize += ((plKeyImp *)key)->GetDataLen(); + } + fTotalKeys++; + fTotalSize += ((plKeyImp *)key)->GetDataLen(); + return true; + } +}; +#endif + +void plResManagerHelper::IUpdateDebugScreen( hsBool force ) +{ +#ifdef MCN_RESMGR_DEBUGGING + + if( !fRefreshing ) + return; + + plRegistry *reg = fResManager->IGetRegistry(); + UInt32 loadedCnt, holdingCnt; + + fDebugScreen->Clear(); + + plDebugPrintIterator iter( this, fDebugScreen, loadedCnt, holdingCnt ); + reg->IterateAllPages( &iter ); + iter.EatPage( nil ); // Force a final update + + fDebugScreen->AddLineF( plStatusLog::kGreen, "%d pages loaded, %d holding", loadedCnt, holdingCnt ); + + if( fCurrAge >= iter.fAgeIndex ) + fCurrAge = -1; + + // Repump our update + if( !force ) + { + plResMgrHelperMsg *msg = TRACKED_NEW plResMgrHelperMsg( plResMgrHelperMsg::kUpdateDebugScreen ); + msg->SetTimeStamp( hsTimer::GetSysSeconds() + kUpdateDelay ); + msg->Send( GetKey() ); + } + +#endif +} + +#if 0 +// FIXME + hsBool VerifyKeyUnloaded(const char* logFile, const plKey& key); + // Verifies that a key which shouldn't be loaded isn't, and if it is tries to figure out why. + void VerifyAgeUnloaded(const char* logFile, const char* age); + + // Helper for VerifyKeyUnloaded + hsBool IVerifyKeyUnloadedRecur(const char* logFile, const plKey& baseKey, const plKey& upKey, const char* baseAge); + bool ILookForCyclesRecur(const char* logFile, const plKey& key, hsTArray& tree, int& cycleStart); + +bool plResManager::ILookForCyclesRecur(const char* logFile, const plKey& key, hsTArray& tree, int& cycleStart) +{ + int idx = tree.Find(key); + tree.Append(key); + if (tree.kMissingIndex != idx) + { + cycleStart = idx; + // Found a cycle. + return true; + } + + // Now recurse up the active reference tree. + for (int i = 0; i < key->GetNumNotifyCreated(); i++) + { + if (key->GetActiveBits().IsBitSet(i)) + { + for (int j = 0; j < key->GetNotifyCreated(i)->GetNumReceivers(); j++) + { + plKey reffer = key->GetNotifyCreated(i)->GetReceiver(j); + + if (ILookForCyclesRecur(logFile, reffer, tree, cycleStart)) + return true; + } + } + } + tree.Pop(); + return false; +} + +bool plResManager::IVerifyKeyUnloadedRecur(const char* logFile, const plKey& baseKey, const plKey& upKey, const char* baseAge) +{ + const plPageInfo& pageInfo = FindPage(upKey->GetUoid().GetLocation())->GetPageInfo(); + const char* upAge = pageInfo.GetAge(); + const char* upPage = pageInfo.GetPage(); + + if( !upKey->GetActiveRefs() ) + { + // We've hit a key active reffing us that should be inactive. + // If it's object is loaded, then it somehow missed getting unloaded. + // Else it must have missed letting go of us when it got unloaded. + if( upKey->ObjectIsLoaded() ) + { + plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] page %s which is loaded but nothing is reffing", + upKey->GetName(), + plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()), + upPage); + + return true; + } + else + { + plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] page %s which isn't even loaded", + upKey->GetName(), + plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()), + upPage); + + return true; + } + } + + // if the age of this key is different from the age on the baseKey, + // we've got a cross age active ref, which is illegal. + if( stricmp(upAge, baseAge) ) + { + plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] which is in a different age %s-%s", + upKey->GetName(), + plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()), + upAge, + upPage); + + return true; + } + + int numActive = 0; + int i; + for( i = 0; i < upKey->GetNumNotifyCreated(); i++ ) + { + if( upKey->GetActiveBits().IsBitSet(i) ) + { + numActive++; + } + } + if( numActive < upKey->GetActiveRefs() ) + { + // Someone has AddRef'd us + plStatusLog::AddLineS(logFile, "\tHeld by %s [%s] page %s which is loaded due to %d AddRef(s)", + upKey->GetName(), + plFactory::GetNameOfClass(upKey->GetUoid().GetClassType()), + upPage, + upKey->GetActiveRefs()-numActive); + + return true; + } + + // Now recurse up the active reference tree. + for( i = 0; i < upKey->GetNumNotifyCreated(); i++ ) + { + if( upKey->GetActiveBits().IsBitSet(i) ) + { + int j; + for( j = 0; j < upKey->GetNotifyCreated(i)->GetNumReceivers(); j++ ) + { + plKey reffer = upKey->GetNotifyCreated(i)->GetReceiver(j); + + + if( IVerifyKeyUnloadedRecur(logFile, baseKey, reffer, baseAge) ) + { + return true; + } + } + } + } + return false; +} + +hsBool plResManager::VerifyKeyUnloaded(const char* logFile, const plKey& key) +{ + if( key->ObjectIsLoaded() ) + { + const plPageInfo& pageInfo = FindPage(key->GetUoid().GetLocation())->GetPageInfo(); + const char* age = pageInfo.GetAge(); + const char* page = pageInfo.GetPage(); + + plStatusLog::AddLineS(logFile, "=================================="); + plStatusLog::AddLineS(logFile, "Object %s [%s] page %s is loaded", key->GetName(), plFactory::GetNameOfClass(key->GetUoid().GetClassType()), page); + + hsTArray tree; + int cycleStart; + hsBool hasCycle = ILookForCyclesRecur(logFile, key, tree, cycleStart); + if( hasCycle ) + { + plStatusLog::AddLineS(logFile, "\t%s [%s] held by dependency cycle", key->GetName(), plFactory::GetNameOfClass(key->GetUoid().GetClassType())); + int i; + for( i = cycleStart; i < tree.GetCount(); i++ ) + { + plStatusLog::AddLineS(logFile, "\t%s [%s]", tree[i]->GetName(), plFactory::GetNameOfClass(tree[i]->GetUoid().GetClassType())); + } + plStatusLog::AddLineS(logFile, "\tEnd Cycle"); + return true; + } + else + { + return IVerifyKeyUnloadedRecur(logFile, key, key, age); + } + } + return false; +} + +class plValidateKeyIterator : public plRegistryKeyIterator +{ +protected: + plRegistry* fRegistry; + const char* fLogFile; + +public: + plValidateKeyIterator(const char* logFile, plRegistry* reg) + { + fRegistry = reg; + fLogFile = logFile; + } + virtual hsBool EatKey(const plKey& key) + { + fRegistry->VerifyKeyUnloaded(fLogFile, key); + return true; + } +}; + +class plValidatePageIterator : public plRegistryPageIterator +{ +protected: + const char* fAge; + plRegistryKeyIterator* fIter; + +public: + plValidatePageIterator(const char* age, plRegistryKeyIterator* iter) : fAge(age), fIter(iter) {} + + + virtual hsBool EatPage( plRegistryPageNode *keyNode ) + { + if( !stricmp(fAge, keyNode->GetPageInfo().GetAge()) ) + return keyNode->IterateKeys( fIter ); + return true; + } +}; + +void plResManager::VerifyAgeUnloaded(const char* logFile, const char* age) +{ + hsBool autoLog = false; + char buff[256]; + if( !logFile || !*logFile ) + { + sprintf(buff, "%s.log", age); + logFile = buff; + autoLog = true; + } + + if( !autoLog ) + { + plStatusLog::AddLineS(logFile, "///////////////////////////////////"); + plStatusLog::AddLineS(logFile, "Begin Verification of age %s", age); + } + + plValidateKeyIterator keyIter(logFile, this); + plValidatePageIterator pageIter(age, &keyIter); + + IterateAllPages(&pageIter); + + if( !autoLog ) + { + plStatusLog::AddLineS(logFile, "End Verification of age %s", age); + plStatusLog::AddLineS(logFile, "///////////////////////////////////"); + } +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManagerHelper.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManagerHelper.h new file mode 100644 index 00000000..2799bebf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResManagerHelper.h @@ -0,0 +1,130 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plResManagerHelper - The wonderful helper class that can receive messages +// for the resManager. +// +//// History ///////////////////////////////////////////////////////////////// +// +// 6.7.2002 mcn - Created +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plResManagerHelper_h +#define _plResManagerHelper_h + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "plRegistryHelpers.h" +#include "../pnKeyedObject/hsKeyedObject.h" + +// Defined as a project setting so we can do this right +//#define MCN_RESMGR_DEBUGGING + + +//// Class Definition //////////////////////////////////////////////////////// + +#ifdef MCN_RESMGR_DEBUGGING +class plStatusLog; +class plDebugPrintIterator; +class plResMgrDebugInterface; +#endif + +class plResManager; +class plRegistryPageNode; +class plResManagerHelper : public hsKeyedObject +{ + protected: + + plResManager *fResManager; + static plResManagerHelper *fInstance; + + hsBool fInShutdown; + +#ifdef MCN_RESMGR_DEBUGGING + friend class plDebugPrintIterator; + friend class plResMgrDebugInterface; + + plStatusLog *fDebugScreen; + hsBool fRefreshing, fCurrAgeExpanded; + int fCurrAge; + int fDebugDisplayType; + + enum DebugDisplayTypes + { + kSizes = 0, + kPercents, + kBars, + kMaxDisplayType + }; + plResMgrDebugInterface *fDebugInput; +#endif + + void IUpdateDebugScreen( hsBool force = false ); + + public: + + plResManagerHelper( plResManager *resMgr ); + virtual ~plResManagerHelper(); + + CLASSNAME_REGISTER( plResManagerHelper ); + GETINTERFACE_ANY( plResManagerHelper, hsKeyedObject ); + + virtual hsBool MsgReceive( plMessage *msg ); + + virtual void Read( hsStream *s, hsResMgr *mgr ); + virtual void Write( hsStream *s, hsResMgr *mgr ); + + void Init( void ); + void Shutdown( void ); + + void LoadAndHoldPageKeys( plRegistryPageNode *page ); + + void EnableDebugScreen( hsBool enable ); + + // Please let the res manager handle telling this. + void SetInShutdown(hsBool b) { fInShutdown = b; } + hsBool GetInShutdown() const { return fInShutdown; } + + static plResManagerHelper *GetInstance( void ) { return fInstance; } +}; + +//// Reffer Class //////////////////////////////////////////////////////////// + +class plResPageKeyRefList : public plKeyCollector +{ + protected: + + hsTArray fKeyList; + + public: + + plResPageKeyRefList() : plKeyCollector( fKeyList ) {} + virtual ~plResPageKeyRefList() { fKeyList.Reset(); } +}; + +#endif // _plResManagerHelper_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgr.cpp new file mode 100644 index 00000000..a41f7213 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgr.cpp @@ -0,0 +1,29 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +// Future home of plResMgr + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgr.h new file mode 100644 index 00000000..9c0c6c96 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgr.h @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Future home of plResMgr diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgrCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgrCreatable.h new file mode 100644 index 00000000..867c4029 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgrCreatable.h @@ -0,0 +1,37 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plResMgrCreatable_inc +#define plResMgrCreatable_inc + +#include "../pnFactory/plCreator.h" + + +#include "plResManagerHelper.h" +REGISTER_NONCREATABLE(plResManagerHelper); + + +#endif // plResMgrCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgrSettings.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgrSettings.h new file mode 100644 index 00000000..3c14e949 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plResMgrSettings.h @@ -0,0 +1,92 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plResMgrSettings - Class that holds all the various settings for +// plResManager +// +//// History ///////////////////////////////////////////////////////////////// +// +// 6.22.2002 mcn - Created +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plResMgrSettings_h +#define _plResMgrSettings_h + +#include "hsTypes.h" + +class plResMgrSettings +{ +protected: + friend class plResManager; + + bool fFilterOlderPageVersions; + bool fFilterNewerPageVersions; + + UInt8 fLoggingLevel; + + bool fPassiveKeyRead; + bool fLoadPagesOnInit; + + plResMgrSettings() + { + fFilterOlderPageVersions = true; + fFilterNewerPageVersions = true; + fPassiveKeyRead = false; + fLoadPagesOnInit = true; + fLoggingLevel = 0; + } + +public: + enum LogLevels + { + kNoLogging = 0, + kBasicLogging = 1, + kDetailedLogging = 2, + kObjectLogging = 3, + kObjectDetailLogging = 4 + }; + + bool GetFilterOlderPageVersions() const { return fFilterOlderPageVersions; } + void SetFilterOlderPageVersions(bool f) { fFilterOlderPageVersions = f; } + + bool GetFilterNewerPageVersions() const { return fFilterNewerPageVersions; } + void SetFilterNewerPageVersions(bool f) { fFilterNewerPageVersions = f; } + + UInt8 GetLoggingLevel() const { return fLoggingLevel; } + void SetLoggingLevel(UInt8 level) { fLoggingLevel = level; } + + bool GetPassiveKeyRead() const { return fPassiveKeyRead; } + void SetPassiveKeyRead(bool p) { fPassiveKeyRead = p; } + + bool GetLoadPagesOnInit() const { return fLoadPagesOnInit; } + void SetLoadPagesOnInit(bool load) { fLoadPagesOnInit = load; } + + static plResMgrSettings& Get(); +}; + +#endif // _plResMgrSettings_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plVersion.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plVersion.cpp new file mode 100644 index 00000000..66aaa7d8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plVersion.cpp @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plVersion.h" +#include "../pnFactory/plFactory.h" +#include + +#include "plCreatableIndex.h" +#define ChangedCreatable(ver, creatable) if (minorVersion == ver) creatables.push_back(CLASS_INDEX_SCOPED(creatable)); + +// +// Every time you bump the minor version number, add entries here for each +// creatable that was changed. Every time the minor version is reset (for a +// major version change), delete all the entries. +// +static void GetChangedCreatables(int minorVersion, std::vector& creatables) +{ + ChangedCreatable(1, plLoadAvatarMsg); + ChangedCreatable(1, plArmatureMod); + ChangedCreatable(2, plAvBrainHuman); +} + + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + + +static int CreatableVersions[plCreatableIndex::plNumClassIndices]; + +static void CalcCreatableVersions() +{ + memset(CreatableVersions, 0, sizeof(CreatableVersions)); + + for (int minorVer = 1; minorVer <= PLASMA2_MINOR_VERSION; minorVer++) + { + std::vector changedTypes; + changedTypes.reserve(10); + + GetChangedCreatables(minorVer, changedTypes); + + for (int i = 0; i < changedTypes.size(); i++) + { + UInt16 changedType = changedTypes[i]; + CreatableVersions[changedType] = minorVer; + + // Bump any classes that derive from this one + for (UInt16 toCheck = 0; toCheck < plFactory::GetNumClasses(); toCheck++) + { + if (plFactory::DerivesFrom(changedType, toCheck)) + CreatableVersions[toCheck] = minorVer; + } + } + } +} + +UInt16 plVersion::GetMajorVersion() { return PLASMA2_MAJOR_VERSION; } +UInt16 plVersion::GetMinorVersion() { return PLASMA2_MINOR_VERSION; } + +int plVersion::GetCreatableVersion(UInt16 creatableIndex) +{ + static bool calced = false; + if (!calced) + { + calced = true; + CalcCreatableVersions(); + } + + return CreatableVersions[creatableIndex]; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plVersion.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plVersion.h new file mode 100644 index 00000000..d7bd287a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plResMgr/plVersion.h @@ -0,0 +1,277 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plVersion_h_inc +#define plVersion_h_inc + +#include "hsTypes.h" + +// RULES: +// Log your change +// Set Minor Version to ZERO when you Bump Major Version +// If you change the minor, see GetChangedCreateables in plVersion.cpp + +#define PLASMA2_MAJOR_VERSION 70 // Major Version...every file will need to be reexported +#define PLASMA2_MINOR_VERSION 2 // Minor Version...only files with the specified creatables + // will need to be reexported + +// Don't modify this, it's automatically updated when branches are made +#define PLASMA2_BRANCH_NAME "Main Branch" + +class plVersion +{ +public: + static UInt16 GetMajorVersion(); + static UInt16 GetMinorVersion(); + + // Pass in a creatable index to get its current version. Zero means it + // hasn't changed since the last major version change, anything else is the + // last minor version it changed at. This takes into account the version of + // parent classes. + static int GetCreatableVersion(UInt16 creatableIndex); +}; + +/* Major Log ---Death to those who do not log changes--- + # Date Who comment + 1 5/8/01 Paulg Added version Number + 2 5/11/01 mf Changed sortable mesh format by 2 bytes. + 3 5/14/01 Paulg Index files Location is used for ReadConfig at Client Startup, RoomLoc added to Index + 4 5/21/01 matt Fixed errant bounding types for physicals (old bad codes will choke) + 5 5/24/01 mf Added Occluders to the data format. + 6 5/29/01 mf Purvis changed the sound data format and didn't change the version. + 7 5/3/01 thamer Cleaned up some object read/write routines + 8 6/12/01 mcn Obliterated hsGTexture on down. Now we use plBitmaps and plMipmaps (class IDs changed) + 9 6/21/01 mcn Updated drawable disk format. Forgot a field that made particle systems break when it wasn't there + 10 6/25/01 mcn Updated plMipmap format. Helps resolve the huge memory issues we were having + 11 6/28/01 mf Slight changes to material and geometry formats to support projective texturing. + 12 7/02/01 mcn Changed file format to fix disappearing objects in SceneViewer + 13 7/09/01 bob Changed file format of plSimpleParticleGenerator to allow mesh generated systems. + 14 7/13/01 thamer Changed uoid read/write to include clone number + 15 7/20/01 thamer Changed uoid sequence to support up to 4 player rooms + 16 7/20/01 mcn Added a second drawable + key to drawInterfaces + 17 7/22/01 bob Changed file format to have particle systems support material diffuse/opacity animations + 18 7/27/01 mcn Added stuff to drawableSpans for SceneViewer. Are we ever going to have another non-zero minor version? :) + 19 7/31/01 mf Did a get and the format had changed but the version hadn't been updated (again). Is this version thing dead? + 20 8/13/01 mcn Not that I know of. Change to various object formats to support object instancing. + 21 8/20/01 mf Like you'd know. Revised inter-object dependency connections throughout the five scene graphs. + 22 8/21/01 mf CoordinateInterfaces keep refs to child SceneObjects instead of child CoordinateInterfaces. + 22 8/21/01 mcn You hurt my feelings. I bump major version in retaliation. (Also upped max UV count) + 23 8/24/01 mf Made runtime lights respect Max's OTM. + 24 8/26/01 mcn Changed plSound format to support stereo (background music) sounds + 25 9/05/01 thamer Changed uoid to support 32bit clones + 26 9/14/01 mcn Added all 12 parameters to the Audio Region component. + 27 9/25/01 mf Added soft volumes to run-time lights. Breaks any scene with RT lights. + 28 10/03/01 matt Changed physics format just enough to make everyone reexport everything. + 29 10/9/01 ee Changed Uoid member types to a new format that can accomodate strings. + 30 10/10/01 mcn Split diffuse layer colors to static and runtime diffuse colors. Breaks the entire universe and parts of New Jersey. + 31 10/11/01 ee Activated string usage in uoids -- this version thing is sure not dead now. + 32 10/11/01 mf Changed format of plSpan. Only breaks scenes with visible geometry. Should that be Minor Version? + 33 10/12/01 cp changed read/write functions in plAnimTimeConvert. Breaks all previously exported animations + 34 10/22/01 Colin Changed format of plBitmap + 35 10/30/01 mf Change to span format, breaks anything with visible geometry. + 36 11/5/01 mcn Changed structure of sound classes to be far more intuitive and bug-free. + 37 11/9/01 ee Just for fun. + 38 11/13/01 bob Changed file format for plAnimTimeConvert / plAnimCmdMsg / plAGAnim (sense a theme?) + 39 11/25/01 mf More sorting info for drawable geometry. + 40 11/29/01 mcn Fixed sound fade param read/write. Anything with sounds must be re-exported. + 41 12/04/01 bob More animation file format changes. + 42 12/05/01 mcn Changes to sound format. Now properties are in a general props field. Also added disable-LOD prop to sounds + 43 12/18/01 cjp changes to physics system + 44 12/18/01 bob anim file format. + 45 12/19/01 Colin anim file format, again. + 46 12/28/01 mf DrawInterface format change to purge the last of the Opaque/Blending dualism. + 47 1/2/01 mf Decoupled the hardware and software skinning vertex formats. + 48 1/6/01 mf Added grey area to Soft Regions (formerly Soft Volumes). + 49 1/17/02 cp new camera system checked into client and plugins + 50 1/21/02 Colin Physics changes + 51 1/24/02 Colin Animation format change + 52 2/14/02 bob plAnimTimeConvert format change + 53 2/14/02 matt made all brains non-keyed. reordering all over the place + 54 3/15/02 cjp new LOS query types, format change to plHKPhysical + 55 4/09/02 cjp more new LOS query types, format change to plHKPhysical, camera type changes + 56 4/23/02 bob nuked plSimpleModifier from plAGMasterMod. Changes file format for animation. + 57 5/17/02 Colin Changed Uoid format for cloning + 58 7/02/02 thamer Changed synchedOject R/W format to support more LocalOnly options + 59 8/14/02 bob Anim file format + 60 2/08/03 mf Changed formats for everything drawable and/or physical. That leaves behaviors, but it's about time for a major version change anyway. + 61 2/18/03 mf Between my changes and Bob's, we're just not sure what hasn't changed anymore. At least drawables and avatars. + 62 3/30/03 mf Added LoadMask to plUoid + 63 5/30/03 thamer optimized Uoid size + 64 10/28/05 jeff changed plLocation to handle more pages and plUoid for optimization reasons + 65 2/22/06 bob animation key rewrite to save space + 66 2/27/06 bob 64-bit quaternion anim keys + 67 2/28/06 bob Anims store UInt16 frame numbers, not 32-bit float times. + 68 3/03/06 bob constant anim channels (plMatrixConstant, plScalarConstant had no R/W methods) + 69 5/08/06 bob changed plVertCoder and hsMatrix44::Read/Write + 70 2/12/07 bob Merged in several registry/resMangaer fixes +*/ + +/* Minor Log ---Death to those who do not log changes--- + # Date Who comment + 1 5/8/01 Paulg Added version Number + 2 5/9/01 mcn Changed color components of drawableSpans + 0 5/11/01 mf Dropped back to zero on Major Version change + 1 8/06/01 thamer Upped the version for mf's anim callback changes. + 2 8/06/01 bob Changes to particle system read/write. + 0 8/13/01 mcn Bumped back to 0 due to major version change. + 1 8/23/01 bob Added animation controller to particle systems, changing their format. + 0 8/24/01 mcn Bumped back to 0 yet again. + 1 8/27/01 mcn Changed how emissive flags are handled on materials. + 2 8/29/01 bob Changed plAvatarMod file format + 0 9/05/01 thamer Bumped back to 0 yet again. + 1 9/17/01 matt Avatar only. + 2 9/24/01 bob Avatar and sound file formats changed. (For age linking effects) + 0 9/25/01 mf Reset to zero for major change. + 1 10/7/01 Colin Format of plResponderModifier and plEventCallbackMsg changed. + 0 10/9/01 ee Reset to zero for major change. + 1 10/11/01 mcn Changed sound format. Anything with sounds (including avatar b/c of linking sounds) breaks. Is this a record for # of version changes in one week? + 1 10/15/01 mcn Added soft volumes to sounds. Breaks anything with sounds. Again. + 2 10/17/01 Colin Changed format of plAGAnim + 0 10/22/01 Colin Reset to zero for major change. + 1 11/01/01 bob Changed format for plAGAnim and plAGMasterMod. Will break avatars without re-export. + 0 11/02/01 mf Reset to zero for major change. + 1 11/28/01 mcn Changed meaning of sound volume property. Will work if not re-exported, but will sound wrong. + 0 11/29/01 mcn Reset on major version change. + 1 12/10/01 bob Changes to animTimeConvert. Animated materials will need a re-export. + 0 12/18/01 bob Reset to zero for major change. + 1 1/07/02 bob File format change for particle systems. + 0 1/21/02 Colin Reset to zero for major change. + 1 1/24/02 Colin File format for responders + 2 1/24/02 mcn Changed postEffectMod format. Not many people use it though, so only minor version change. + 0 1/24/02 Colin Reset to zero for major change. + 1 1/28/02 bob File format change to avatars for clothing customization. + 2 1/30/02 Colin File format change to sit component + 3 1/31/02 Colin File format change to logic modifier (all detector components) + 4 2/13/02 Colin File format change to ladder modifier + 0 2/14/02 bob Reset to zero for major change. + 1(5) 2/14/02 mcn GUI control format changes to support dynamic text layers (the RIGHT way) + 2(6) 2/19/02 mcn Added version field to dialogs, to allow synching with C++ and Python code (just in case) + 7 3/01/02 mcn Moved GUI controls to pfGUIColorScheme methods of handling colors/fonts + 8 3/01/02 cjp changed format of new camera brains, only affects the few using them right now + 10 3/08/02 mcn Changed internal format of sounds to facilitate packing them in files later + 11 3/08/02 mcn Added a shadow option to GUI color schemes. Also added mouse-over animations to buttons. + 12 3/12/02 bob Change format of clothing + 13 3/15/02 matt Changed base class for sit modifier + 0 3/15/02 cjp Only chris forgot to log it - mf + 1 3/29/02 mf Added light group support for particle systems. + 2 4/02/02 Colin Format change for responder modifiers + 0 4/15/02 cjp Only chris forgot to log it - mf + 1 4/15/02 mf Added a parm to partycle systems just for fun. + 2 4/17/02 cjp Added new modifier for interface information, changed plMaxNode.cpp. + 0 4/23/02 bob Reset to zero. + 1 4/25/02 mcn GUI objects no longer fog. Must reexport GUI components. + 2 5/05/02 mf Particle system enhancements + 3 5/06/02 matt Changes to one shot modifier and multistage modifier + 4 5/06/02 bob Changes to plArmatureMod and Clothing stuff. For swappable meshes and footsteps. + 5 5/07/02 bob Changed clothing item file format (again) for text descriptions and thumbnails + 6 5/09/02 mcn Added sound event capabilities to GUI controls + 7 5/13/02 mcn Added some new options to GUI knobs + 8 5/14/02 matt Fix net propagation for animation stages (fading) and generic brains (exit flags) + 0 5/17/02 Colin Reset for major version + 1 5/21/02 mcn Added a channel select option for plWin32Sounds + 2 5/23/02 bob Added multiple texture layers per element in plClothingItem + 3 5/23/02 mcn Added some options to plSoundBuffer. + 4 6/18/02 mcn Added some more options to plSound, incl. localOnly flags + 6 6/21/02 mcn Updated plDynamicTextMap to include an initial image buffer + 7 6/26/02 mcn Major revision to sound system to support hardware acceleration + 0 7/02/02 thamer Reset for major version + 1 7/08/02 matt Format change for animation stages -- added next/prevStage override + 2 7/10/02 bob Format changes for avatar footstep sounds + 3 7/12/02 mcn Format change to sounds for EAX effects + 4 7/23/02 bob Format change to footstep sounds for more surface options + 5 7/29/02 mcn More EAX format changes to sounds + 6 7/29/02 mcn Added cutoff attenuation to spot/omni lights + 7 8/01/02 bob Format change to clothing items + 0 8/14/02 bob Reset for major version + 1 8/19/02 bob plClothingItem file format + 2 9/02/02 bob plArmatureLODMod file format change for bone LOD + 3 9/12/02 mf Making ripples and waves play nice with each other. + 4 9/18/02 bob plClothingItem file format... again + 5 9/19/02 mcn New GUI control proxy stuff + 6 9/23/02 mcn Removed dead sound stuff + 7 9/24/02 mcn Sound priority stuff + 8 9/25/02 mcn Support for new grouped random sound objects + 9 10/15/02 mcn Material anim support in GUI controls + 10 10/21/02 mcn Variable volume support to group sounds. No format break. + 11 10/29/02 cjp proper python notification of avatar page out & last avatar out plus elevator persistance fix - breaks message format for exclude regions + 12 10/29/02 mcn Fixing chris's booboo + 13 10/29/02 cjp Changed camera component data format slightly. + 16 11/18.02 cjp changed camera speed components & objects, don't know where v 14 & 15 went + 17 12/04/20 matt New line-of-sight categories; format change for physicals. + 18 12/05/02 mf Enhanced linefollowmod to play nice with stereizer, and added field to occluder's cullpoly. + 19 12/17/02 matt Bumped armaturemod, based on strong circumstantial evidenced that I missed something. + 20 12/31/02 matt New format for animation stages, written by the multistage mod. + 21 01/03/03 matt Change to sitmodifier. + 22 01/16/03 matt More simplification for animation stages and generic brains. + 23 01/20/03 bob Added layer to clothing items for aged skin blending + 24 01/21/03 mf plLayers now read and write out their shaders. + 25 01/29/03 bob plMorphSequence read/writes the plSharedMeshes it uses + 27 02/05/03 mcn Updates to pfGUIButtonMod + 0 02/18/03 mf Reset for major version change. + 1 02/19/03 bob Added layers to plClothingItem. Took the opp to cleanup the R/W functions + 2 02/21/03 mf Added features to dynamic water. No one should notice. + 3 02/24/03 cjp changed animated cameras - added new commands for controlling them + 4 02/24/03 mcn Updates to GUI stuff to support, er, new GUI stuff. + 5 03/11/03 bob Clothing and linkSound format change + 0 03/30/03 mf Reset for major version change. + 9 04/14/03 Colin Havok change. At 9 since mf forgot to bump the version (and Matt did a bunch of undocumented bumps) + 10 04/15/03 bob Added a footstep surface type + 11 4/18/03 mcn Added list of the layers grabbed by a GUI DynDisplay + 12 4/23/03 bob File format for plMultistageBehMod, needed for a ladder fix + 13 05/01/03 Colin Changed how Havok vectors and quats are written + 14 03/30/03 mf Particle effect enhancement + 0 05/30/03 thamer Reset for major version change. + 1 06/01/03 bob Added a flags variable to plSharedMesh + 2 06/06/03 markd Added NotifyType for GUIButtons + 3 06/07/03 mcn Added sound groups to physicals + 4 06/24/03 bob More params for Flocking particles + 5 07/05/03 mf Particle and footprints working together + 6 07/13/03 bob Avatar file format + 7 07/31/03 bob LinkToAgeMsg (which affects responders) and panic link regions + 8 08/21/03 Colin Removed some stuff from plSoundBuffer + 9 08/22/03 bob Added info to plClothingItem + 10 09/16/03 bob Removed stuff in plAnimCmdMsg, which affects responders. + 11 09/18/03 mf Changed plLoadMask, of which plWaveSet7 is the biggest user. + 12 01/03/04 jeff pfGUIDynDisplayCtrl now stores material as well as layer information +!! 11 02/10/04 mf Dropped back a version, to reduce patch size for Expansion 1. + 12 03/12/04 bob New stuff in Swim Regions + 0 10/28/05 jeff Reset for major version change + 1 11/28/05 jeff GUI text boxes can now pull strings from the localization mgr + 2 12/01/05 jeff pfKIMsg can now handle unicode strings + 3 02/02/06 adam modified plDynamicEnvMap as a part of back-porting planar reflections from P21 + 0 02/22/06 bob Reset for major version change + 1 04/12/06 Colin Changed physical format + 0 05/08/06 bob Reset for major version change + 1 05/16/06 markd Changed physics format to include boxes + 2 06/26/06 jeff Changed coop brain format so book sharing works again + 3 12/05/06 bob Avatar uses a render target instead of a plMipmap. + 4 01/16/07 bob Still does, it's just not created at export. + 5 01/24/07 adam Changed plAGMasterMod so one can be set as a group master in grouped anims + 0 02/12/07 bob Reset for major version change + 1 03/29/07 jeff Changed plLoadAvatarMsg and plArmatureMod to be more flexible + 2 06/28/07 jeff Changed plAvBrainHuman format to store whether it's an actor or not +*/ + +#endif // plVersion_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/animation.sdl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/animation.sdl new file mode 100644 index 00000000..1c475e3b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/animation.sdl @@ -0,0 +1,72 @@ +#=============================================== +# Changing an SDL record? Be sure to leave the +# legacy record intact and make changes in +# a new copy of the record. - eap +#=============================================== + + +# +# Animation State Description +# + + +# +# Defines the state of an animation controller +# +STATEDESC AnimTimeConvert +{ + VERSION 5 + VAR INT flags[1] DEFAULT=0 + VAR FLOAT lastStateAnimTime[1] DEFAULT=0 + VAR FLOAT loopEnd[1] DEFAULT=0 + VAR FLOAT loopBegin[1] DEFAULT=0 + VAR FLOAT speed[1] DEFAULT=1 + VAR CREATABLE speedEaseCurve[1] + VAR BYTE currentEaseCurve[1] DEFAULT=0 # 0=nil, 1=easeIn, 2=easeOut, 3=speed + VAR TIME currentEaseBeginWorldTime[1] DEFAULT=0 + VAR TIME lastStateChange[1] DEFAULT=0 +} +STATEDESC AnimTimeConvert +{ + VERSION 6 + VAR INT flags[1] DEFAULT=0 + VAR FLOAT lastStateAnimTime[1] DEFAULT=0 + VAR FLOAT loopEnd[1] DEFAULT=0 + VAR FLOAT loopBegin[1] DEFAULT=0 + VAR FLOAT speed[1] DEFAULT=1 + VAR BYTE currentEaseCurve[1] DEFAULT=0 # 0=nil, 1=easeIn, 2=easeOut, 3=speed + VAR TIME currentEaseBeginWorldTime[1] DEFAULT=0 + VAR TIME lastStateChange[1] DEFAULT=0 +} + +# +# TOP LEVEL +# Describes the state of an animated object (non-material) +# +STATEDESC AGMaster +{ + VERSION 5 + VAR $AnimTimeConvert atcs[] # variable length list + VAR BYTE blends[] +} + +# +# TOP LEVEL +# Describes the state of an animated layer +# +STATEDESC Layer +{ + VERSION 6 + VAR $AnimTimeConvert atc[1] + + VAR INT passThruChannels[1] DEFAULT=0 + VAR FLOAT transform[] # Size will be 0 or 16, depending on whether we own the channel + + VAR BYTE channelData[] # A byte array for the channels below. Its size + # will depend on the layer's fOwnedChannels. + #VAR RGB8 preshadeColor[1] DEFAULT=(1,1,1) + #VAR RGB8 runtimeColor[1] DEFAULT=(1,1,1) + #VAR RGB8 ambientColor[1] DEFAULT=(1,1,1) + #VAR BYTE opacity[1] DEFAULT=255 +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/avatar.sdl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/avatar.sdl new file mode 100644 index 00000000..cc7f34e6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/avatar.sdl @@ -0,0 +1,150 @@ +#=============================================== +# Changing an SDL record? Be sure to leave the +# legacy record intact and make changes in +# a new copy of the record. - eap +#=============================================== + +# +# Avatar State Description +# You might want to read these from the bottom of the file up to get the top-down perspective +# + +# +# Defines a standard anim stage (used by the genericBrain) +# +STATEDESC standardStage +{ + VERSION 2 + VAR STRING32 name[1] + VAR SHORT numLoops[1] DEFAULT=0 # -1=loopForever + VAR BYTE forward[1] DEFAULT=0 # 0=none, 1=kbd, 2=auto + VAR BYTE backward[1] DEFAULT=0 # 0=none, 1=kbd + VAR BYTE stageAdvance[1] DEFAULT=0 # 0=none, 1=auto + VAR BYTE stageRegress[1] DEFAULT=0 # 0=none, 1=auto + VAR BOOL notifyEnter[1] DEFAULT=false + VAR BOOL notifyLoop[1] DEFAULT=false + VAR BOOL notifyStageAdvance[1] DEFAULT=false + VAR BOOL notifyStageRegress[1] DEFAULT=false + VAR BOOL useGlobalCoords[1] DEFAULT=false + VAR FLOAT localTime[1] DEFAULT=0 + VAR FLOAT length[1] DEFAULT=0 + VAR SHORT currentLoop[1] DEFAULT=0 + VAR BOOL isAttached[1] DEFAULT=false +} +STATEDESC standardStage +{ + VERSION 3 + VAR STRING32 name[1] + VAR SHORT numLoops[1] DEFAULT=0 # -1=loopForever + VAR BYTE forward[1] DEFAULT=0 # 0=none, 1=kbd, 2=auto + VAR BYTE backward[1] DEFAULT=0 # 0=none, 1=kbd + VAR BYTE stageAdvance[1] DEFAULT=0 # 0=none, 1=auto + VAR BYTE stageRegress[1] DEFAULT=0 # 0=none, 1=auto + VAR BOOL notifyEnter[1] DEFAULT=false + VAR BOOL notifyLoop[1] DEFAULT=false + VAR BOOL notifyStageAdvance[1] DEFAULT=false + VAR BOOL notifyStageRegress[1] DEFAULT=false + VAR BOOL useGlobalCoords[1] DEFAULT=false + VAR FLOAT localTime[1] DEFAULT=0 + VAR SHORT currentLoop[1] DEFAULT=0 + VAR BOOL isAttached[1] DEFAULT=false +} + +# +# State for the plAvBrainGeneric +# +STATEDESC genericBrain +{ + VERSION 3 + VAR BOOL noBrain[1] DEFAULT=true # set if not a brain + VAR $standardStage stages[] + VAR BYTE currentStage[1] DEFAULT=0 + VAR BOOL freezePhysicalAtEnd[1] DEFAULT=false + VAR PLKEY callbackRcvr[1] + VAR BOOL movingForward[1] DEFAULT=true + VAR BYTE exitFlags[1] DEFAULT=0 + VAR BYTE type[1] # could be byte + VAR BYTE mode[1] # could be byte + VAR FLOAT fadeIn[1] DEFAULT=1.0 + VAR FLOAT fadeOut[1] DEFAULT=1.0 + VAR BYTE moveMode[1] # could be byte + VAR BYTE bodyUsage[1] # could be byte +} + +# +# State for the plAvBrainClimb +# +STATEDESC climbBrain +{ + VERSION 1 + VAR INT curMode[1] + VAR INT nextMode[1] + VAR INT allowedDirections[1] + VAR INT allowedDismounts[1] + VAR FLOAT vertProbeLength[1] + VAR FLOAT horizProbeLength[1] + VAR BOOL curStageAttached[1] + VAR INT curStage[1] + VAR FLOAT curStageTime[1] + VAR FLOAT curStageStrength[1] + VAR BOOL exitStageAttached[1] + VAR INT exitStage[1] + VAR FLOAT exitStageTime[1] + VAR BOOL exitStageStrength[1] +} + +STATEDESC driveBrain +{ + VERSION 1 + VAR INT unUsed[1] +} + +# +# A pseudo-union structure. It looks like it contains several varying-length +# arrays, but in reality only one of them has anything in it, and only one +# brain at that. +STATEDESC brainUnion +{ + VERSION 1 + VAR $genericBrain fGenericBrain[] + VAR $climbBrain fClimbBrain[] + VAR $driveBrain fDriveBrain[] +} + +# +# The base block contains state for the plAvBrainHuman (which all avatars must have) +# and a variable-length state of brains of varying type. +# +STATEDESC avatar +{ + VERSION 5 + VAR $brainUnion brainStack[] + VAR BYTE invisibilityLevel[1] DEFAULT=0 # 0 means visible, >0 is CCR level of invisibility +} + +STATEDESC avatar +{ + VERSION 6 + VAR $brainUnion brainStack[] + VAR BYTE invisibilityLevel[1] DEFAULT=0 # 0 means visible, >0 is CCR level of invisibility + + VAR POINT3 position[1] DEFAULT=(0,0,0) + VAR FLOAT rotation[1] DEFAULT=0.0 + VAR PLKEY subworld[1] +} + +STATEDESC avatar +{ + VERSION 7 + VAR $brainUnion brainStack[] + VAR BYTE invisibilityLevel[1] DEFAULT=0 # 0 means visible, >0 is CCR level of invisibility +} + +STATEDESC avatarPhysical +{ + VERSION 1 + + VAR POINT3 position[1] DEFAULT=(0,0,0) + VAR FLOAT rotation[1] DEFAULT=0.0 + VAR PLKEY subworld[1] +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/cloneMessage.sdl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/cloneMessage.sdl new file mode 100644 index 00000000..75e3cca7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/cloneMessage.sdl @@ -0,0 +1,23 @@ +#=============================================== +# Changing an SDL record? Be sure to leave the +# legacy record intact and make changes in +# a new copy of the record. - eap +#=============================================== + +# +# State Description Language for a LoadClone message +# Currently only used by the gameserver to store a loadClone msg as dynamic object state +# + +# +# This is the only SDL file allowed to use the "CREATABLE" type. Normally that type is +# a security hole. We can't guarantee that trying to read a creatable on an arbitrary +# (i.e. hacked) stream won't crash the game. However, since this state is generated by the +# server, we can trust that. +# + +STATEDESC CloneMessage +{ + VERSION 1 + VAR CREATABLE message[1] # The message as a creatable stream +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/clothing.sdl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/clothing.sdl new file mode 100644 index 00000000..8dd600e0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/clothing.sdl @@ -0,0 +1,46 @@ +#=============================================== +# Changing an SDL record? Be sure to leave the +# legacy record intact and make changes in +# a new copy of the record. - eap +#=============================================== + +# +# State Description for an avatar's clothing +# + + +# +# Helper SDL desc +# Defines an item of clothing +# +STATEDESC clothingItem +{ + VERSION 3 # version is an int + VAR PLKEY item[1] + VAR RGB8 tint[1] DEFAULT=(1,1,1) + VAR RGB8 tint2[1] DEFAULT=(1,1,1) +} + +# +# Helper SDL desc +# Defines appearance options +# +STATEDESC appearanceOptions +{ + VERSION 2 + VAR RGB8 skinTint[1] DEFAULT=(1,1,1) + VAR BYTE faceBlends[] DEFAULT=0; +} + +# +# Top level SDL desc +# A variable-length list of clothing items +# +STATEDESC clothing +{ + VERSION 4 + VAR $clothingItem wardrobe[] + VAR $appearanceOptions appearance[1] + VAR PLKEY linkInAnim[1] DEFAULT=nil +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/morph.sdl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/morph.sdl new file mode 100644 index 00000000..9fa103f8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/morph.sdl @@ -0,0 +1,28 @@ +#=============================================== +# Changing an SDL record? Be sure to leave the +# legacy record intact and make changes in +# a new copy of the record. - eap +#=============================================== + +# +# State Description for a morph sequence modifier +# + +STATEDESC MorphSet +{ + VERSION 2 + VAR PLKEY mesh[1] DEFAULT=nil + VAR BYTE weights[] DEFAULT=0 +} + +STATEDESC MorphSequence +{ + VERSION 2 + + # User data. The avatar sets this so that it can later differentiate between + # various records in the vault + VAR BYTE targetID[1] DEFAULT=0 + + VAR $MorphSet morphs[] +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/particle.sdl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/particle.sdl new file mode 100644 index 00000000..045b5d00 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/particle.sdl @@ -0,0 +1,17 @@ +#=============================================== +# Changing an SDL record? Be sure to leave the +# legacy record intact and make changes in +# a new copy of the record. - eap +#=============================================== + +# +# State Description for a morph sequence modifier +# 99% of the particle systems won't need this (and won't save state) +# This is intended for cloned systems that attach to the avatar. +# + +STATEDESC ParticleSystem +{ + VERSION 1 + VAR INT numParticles[1] DEFAULT=0 +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/physical.sdl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/physical.sdl new file mode 100644 index 00000000..14ee6ebf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/physical.sdl @@ -0,0 +1,23 @@ +#=============================================== +# Changing an SDL record? Be sure to leave the +# legacy record intact and make changes in +# a new copy of the record. - eap +#=============================================== + +# +# State Description Language for +# physical changes to an object +# + +STATEDESC physical +{ + VERSION 2 + VAR POINT3 position[1] DEFAULT=(0,0,0) + VAR QUATERNION orientation[1] DEFAULT=(0,0,0,1) + VAR VECTOR3 linear[1] DEFAULT=(0,0,0) + VAR VECTOR3 angular[1] DEFAULT=(0,0,0) + VAR PLKEY subworld[1] + +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/responder.sdl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/responder.sdl new file mode 100644 index 00000000..fc751b38 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/responder.sdl @@ -0,0 +1,24 @@ +#=============================================== +# Changing an SDL record? Be sure to leave the +# legacy record intact and make changes in +# a new copy of the record. - eap +#=============================================== + +# +# State Description Language for a responder +# + +STATEDESC Responder +{ + VERSION 3 + VAR INT curState[1] DEFAULT=0 # The current state (first index for fCommandList) + + VAR INT curCommand[1] DEFAULT=-1 # The command we are currently waiting to send + # (or -1 if we're not sending) + VAR BOOL netRequest[1] DEFAULT=false # Was the last trigger a net request + VAR INT completedEvents[] # Which events that commands are waiting on have completed + VAR BOOL enabled[1] DEFAULT=true + VAR PLKEY playerKey[1] # The player who triggered this last + VAR PLKEY triggerer[1] # Whoever triggered us (for sending notify callbacks) +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/sound.sdl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/sound.sdl new file mode 100644 index 00000000..02234649 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/sound.sdl @@ -0,0 +1,29 @@ +#=============================================== +# Changing an SDL record? Be sure to leave the +# legacy record intact and make changes in +# a new copy of the record. - eap +#=============================================== + +# +# State Description Language for sounds +# + +STATEDESC SoundState +{ + VERSION 1 + VAR FLOAT desiredVolume[1] DEFAULT=0 # 0 to 1 + VAR TIME time[1] DEFAULT=0 # time sound was started + VAR BOOL playing[1] DEFAULT=false # is it playing +} + +# +# Describes the sound state for a sceneObject +# TOPLEVEL +# +STATEDESC Sound +{ + VERSION 1 + VAR $SoundState sounds[] # List of soundStates on a sceneObject +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/xregion.sdl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/xregion.sdl new file mode 100644 index 00000000..eb949cfe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/SDL/xregion.sdl @@ -0,0 +1,15 @@ +#=============================================== +# Changing an SDL record? Be sure to leave the +# legacy record intact and make changes in +# a new copy of the record. - eap +#=============================================== + +# +# State Description Language for Exclude Regions +# + +STATEDESC XRegion +{ + VERSION 1 + VAR BOOL cleared[1] DEFAULT=false # is it cleared +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDL.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDL.h new file mode 100644 index 00000000..993d155c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDL.h @@ -0,0 +1,552 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_SDL_inc +#define PL_SDL_inc + +// +// Code for the State Description Language (SDL) +// + +#include "plSDLDescriptor.h" +#include "hsUtils.h" +#include "hsStlUtils.h" + +#include "../pnFactory/plCreatable.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plUoid.h" + +#include "../plUnifiedTime/plUnifiedTime.h" + +namespace plSDL +{ + typedef std::list DescriptorList; + enum GeneralPurpose + { + kLatestVersion = -1, // used when requesting the latest version of a state descriptor + kMaxListSize = 9999 // maximum size of var lists + }; + + enum ContentsFlags // or saveFlags, 16 bits + { + kHasUoid = 0x1, + kHasNotificationInfo = 0x2, + kHasTimeStamp = 0x4, + kSameAsDefault = 0x8, + kHasDirtyFlag = 0x10, + kWantTimeStamp = 0x20, + + kAddedVarLengthIO = 0x8000, // using to establish a new version in the header, can delete in 8/03 + + kHasMaximumValue= 0xffff, + }; + + enum RWOptions + { + kDirtyOnly = 1<< 0, // write option + kSkipNotificationInfo = 1<< 1, // read/write option + kBroadcast = 1<< 2, // send option + kWriteTimeStamps = 1<< 3, // write out time stamps + kTimeStampOnRead = 1<< 4, // read: timestamp each var when it gets read. write: request that the reader timestamp the dirty vars. + kTimeStampOnWrite = 1<< 5, // read: n/a. write: timestamp each var when it gets written. + kKeepDirty = 1<< 6, // don't clear dirty flag on read + kDontWriteDirtyFlag = 1<< 7, // write option. don't write var dirty flag. + kMakeDirty = 1<< 8, // read/write: set dirty flag on var read/write. + kDirtyNonDefaults = 1<< 9, // dirty the var if non default value. + kForceConvert = 1<<10, // always try to convert rec to latest on read + }; + + enum BehaviorFlags + { + kDisallowTimeStamping = 0x1, + }; + + extern const char* kAgeSDLObjectName; + void VariableLengthRead(hsStream* s, int size, int* val); + void VariableLengthWrite(hsStream* s, int size, int val); +}; + +class plStateVarNotificationInfo +{ +private: + std::string fHintString; +public: + void SetHintString(const char* c) { fHintString=c; } + const char* GetHintString() const { return fHintString.c_str(); } + + void Read(hsStream* s, UInt32 readOptions); + void Write(hsStream* s, UInt32 writeOptions) const; +}; + +// +// Base class for a state variable. +// A state var is a var descriptor and it's value (contents) +// +class plSimpleStateVariable; +class plSDStateVariable; +class plStateVariable +{ +public: + enum Flags + { + kDirty = 0x1, // true when someone sets the value using Set(...), can be cleared after writing + kUsed = 0x2 // true when it contains some value (either by Set(...) or Read() ) + }; +protected: + UInt32 fFlags; + plStateVarNotificationInfo fNotificationInfo; +public: + plStateVariable() : fFlags(0) {} + virtual ~plStateVariable() {} + + const char* GetName() const { return GetVarDescriptor()->GetName(); } + bool IsNamed(const char* n) const { return (n && !stricmp(GetName(), n)); } + virtual int GetCount() const = 0; + + // conversion ops + virtual plSimpleStateVariable* GetAsSimpleStateVar() = 0; + virtual plSDStateVariable* GetAsSDStateVar() = 0; + virtual plVarDescriptor* GetVarDescriptor() = 0; + virtual const plVarDescriptor* GetVarDescriptor() const = 0; + virtual void Alloc(int cnt=-1 /* -1 means don't change count */) = 0; + + virtual bool IsDirty() const { return (fFlags & kDirty) != 0; } + virtual bool IsUsed() const { return (fFlags & kUsed) != 0; } + + void SetDirty(bool d) { if (d) fFlags |= kDirty; else fFlags &= ~kDirty; } + void SetUsed(bool d) { if (d) fFlags |= kUsed; else fFlags &= ~kUsed; } + virtual void SetFromDefaults(bool timeStampNow) = 0; + virtual void TimeStamp( const plUnifiedTime & ut=plUnifiedTime::GetCurrentTime() ) = 0; + virtual const plUnifiedTime& GetTimeStamp() const = 0; + + plStateVarNotificationInfo& GetNotificationInfo() { return fNotificationInfo; } + const plStateVarNotificationInfo& GetNotificationInfo() const { return fNotificationInfo; } + + virtual void DumpToObjectDebugger(bool dirtyOnly, int level) const {} + virtual void DumpToStream(hsStream* stream, bool dirtyOnly, int level) const {} + + // IO + virtual bool ReadData(hsStream* s, float timeConvert, UInt32 readOptions); + virtual bool WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const; +}; + +// +// Change Notifier. +// When a plSimpleStateVariable changes it's value by more than the given delta value, +// a notification msg will be sent to the objects that registered interest. +// +class plStateChangeNotifier +{ + friend class plSimpleStateVariable; +private: + float fDelta; + typedef std::vector KeyList; + KeyList fKeys; // the objects to notify on change>delta + static UInt32 fCurrentPlayerID; + + void IAddKey(plKey k); + int IRemoveKey(plKey k); +public: + plStateChangeNotifier(); + plStateChangeNotifier(float i, plKey k); + + void AddNotificationKey(plKey k) { IAddKey(k); } + void AddNotificationKeys(KeyList keys); + int RemoveNotificationKey(plKey k); // returns number of keys left after removal + int RemoveNotificationKeys(KeyList keys); // returns number of keys left after removal + + void SendNotificationMsg(const plSimpleStateVariable* srcVar, const plSimpleStateVariable* dstVar, const char* sdlName); + + bool GetValue(float* i) const; + bool SetValue(float i); + + static UInt32 GetCurrentPlayerID() { return fCurrentPlayerID; } + static void SetCurrentPlayerID(UInt32 p) { fCurrentPlayerID=p; } + + bool operator==(const plStateChangeNotifier &) const; +}; + +// +// A (non-nested) variable descriptor and its data contents. +// +class plUoid; +class plKey; +class plClientUnifiedTime; +typedef unsigned char byte; +class plSimpleStateVariable : public plStateVariable +{ +protected: + union + { + int* fI; // array of int + short* fS; // array of short + byte* fBy; // array of byte + float* fF; // array of float + double* fD; // array of double + bool* fB; // array of bool + plUoid* fU; // array of uoid + plCreatable** fC; // array of plCreatable ptrs + plVarDescriptor::String32* fS32; // array of strings + plClientUnifiedTime* fT; // array of Times + }; + mutable plUnifiedTime fTimeStamp; // the last time the var was changed + plSimpleVarDescriptor fVar; + + typedef std::vector StateChangeNotifiers; + StateChangeNotifiers fChangeNotifiers; + + void IDeAlloc(); + void IInit(); // initize vars + void IVarSet(bool timeStampNow=false); + + // converter fxns + bool IConvertFromBool(plVarDescriptor::Type newType); + bool IConvertFromInt(plVarDescriptor::Type newType); + bool IConvertFromByte(plVarDescriptor::Type newType); + bool IConvertFromShort(plVarDescriptor::Type newType); + bool IConvertFromFloat(plVarDescriptor::Type newType); + bool IConvertFromDouble(plVarDescriptor::Type newType); + bool IConvertFromString(plVarDescriptor::Type newType); + bool IConvertFromRGB(plVarDescriptor::Type newType); + bool IConvertFromRGBA(plVarDescriptor::Type newType); + bool IConvertFromRGB8(plVarDescriptor::Type newType); + bool IConvertFromRGBA8(plVarDescriptor::Type newType); + + bool IReadData(hsStream* s, float timeConvert, int idx, UInt32 readOptions); + bool IWriteData(hsStream* s, float timeConvert, int idx, UInt32 writeOptions) const; + +public: + + plSimpleStateVariable() { IInit(); } + plSimpleStateVariable(plVarDescriptor* vd) { IInit(); CopyFrom(vd); } + ~plSimpleStateVariable() { IDeAlloc(); } + + // conversion ops + plSimpleStateVariable* GetAsSimpleStateVar() { return this; } + plSDStateVariable* GetAsSDStateVar() { return nil; } + bool operator==(const plSimpleStateVariable &other) const; // assumes matching var descriptors + + void TimeStamp( const plUnifiedTime & ut=plUnifiedTime::GetCurrentTime() ); + void CopyFrom(plVarDescriptor* v); + void CopyData(const plSimpleStateVariable* other, UInt32 writeOptions=0); + bool SetFromString(const char* value, int idx, bool timeStampNow); // set value from string, type. return false on err + char* GetAsString(int idx) const; + bool ConvertTo(plSimpleVarDescriptor* toVar, bool force=false); // return false on err + void Alloc(int cnt=-1 /* -1 means don't change count */); // alloc memory after setting type + void Reset(); + + // setters + bool Set(float v, int idx=0); + bool Set(float* v, int idx=0); // floatVector + bool Set(double v, int idx=0); + bool Set(double* v, int idx=0); // doubleVector + bool Set(int v, int idx=0); + bool Set(int* v, int idx=0) { return Set(*v, idx); } // helper since there is no int vec type + bool Set(byte v, int idx=0); + bool Set(byte* v, int idx=0); // byteVector + bool Set(short v, int idx=0); + bool Set(short* v, int idx=0) { return Set(*v, idx); } // helper since there is no short vec type + bool Set(bool v, int idx=0); + bool Set(bool* v, int idx=0) { return Set(*v, idx); } // helper since there is no bool vec type + bool Set(const char* v, int idx=0); // string + bool Set(const plKey& v, int idx=0); + bool Set(plCreatable*, int idx=0); // only SDL generated by the server is allowed to use this type. + void SetFromDefaults(bool timeStampNow); + + // getters + bool Get(int* value, int idx=0) const; + bool Get(short* value, int idx=0) const; + bool Get(byte* value, int idx=0) const; // returns byte or byteVector + bool Get(float* value, int idx=0) const; // returns float or floatVector + bool Get(double* value, int idx=0) const; // returns double or doubleVector + bool Get(bool* value, int idx=0) const; + bool Get(char value[], int idx=0) const; + bool Get(plKey* value, int idx=0) const; + bool Get(plCreatable** value, int idx=0) const; + const plUnifiedTime& GetTimeStamp() const { return fTimeStamp; } + + // Special backdoor so the KI Manager can get the key name without having a ResMgr + const char* GetKeyName(int idx=0) const; + + int GetCount() const { return fVar.GetCount(); } // helper + plVarDescriptor* GetVarDescriptor() { return &fVar; } + plSimpleVarDescriptor* GetSimpleVarDescriptor() { return fVar.GetAsSimpleVarDescriptor(); } + const plVarDescriptor* GetVarDescriptor() const { return &fVar; } + const plSimpleVarDescriptor* GetSimpleVarDescriptor() const { return fVar.GetAsSimpleVarDescriptor(); } + + // State Change Notification + void AddStateChangeNotification(plStateChangeNotifier& n); + void RemoveStateChangeNotification(plKey notificationObj); // remove all with this key + void RemoveStateChangeNotification(plStateChangeNotifier n); // remove ones which match + void NotifyStateChange(const plSimpleStateVariable* other, const char* sdlName); // send notification msg if necessary, internal use + + void DumpToObjectDebugger(bool dirtyOnly, int level) const; + void DumpToStream(hsStream* stream, bool dirtyOnly, int level) const; + + // IO + bool ReadData(hsStream* s, float timeConvert, UInt32 readOptions); + bool WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const; +}; + +// +// A list of state data records, all of which are the same kind. +// Corresponds to a SD var descriptor. +// +class plStateDataRecord; +class plSDStateVariable : public plStateVariable +{ +public: + typedef std::vector ConstDataRecList; +protected: + typedef std::vector DataRecList; + DataRecList fDataRecList; + plSDVarDescriptor* fVarDescriptor; + + void IDeInit(); +public: + plSDStateVariable(plSDVarDescriptor* sdvd); + ~plSDStateVariable(); // delete all records + + // conversion ops + plSimpleStateVariable* GetAsSimpleStateVar() { return nil; } + plSDStateVariable* GetAsSDStateVar() { return this; } + bool operator==(const plSDStateVariable &other) const; // assumes matching var descriptors + + void ConvertTo(plSDStateVariable* otherSDVar, bool force=false); + void CopyFrom(plSDStateVariable* other, UInt32 writeOptions=0); + void UpdateFrom(plSDStateVariable* other, UInt32 writeOptions=0); + void AddStateDataRecord(plStateDataRecord *sdr) { fDataRecList.push_back(sdr); SetDirty(true); SetUsed(true); } + void InsertStateDataRecord(plStateDataRecord *sdr, int i) { fDataRecList[i] = sdr; SetDirty(true); SetUsed(true);} + void SetFromDefaults(bool timeStampNow); + void TimeStamp( const plUnifiedTime & ut=plUnifiedTime::GetCurrentTime() ); + const plUnifiedTime& GetTimeStamp() const { static plUnifiedTime foo; return foo; } + + void Alloc(int cnt=-1 /* -1 means don't change count */); // wipe and re-create + void Alloc(plSDVarDescriptor* sdvd, int cnt=-1); // wipe and re-create + void Resize(int cnt); + + bool IsDirty() const; + bool IsUsed() const; + + // getters + plStateDataRecord* GetStateDataRecord(int i) { return fDataRecList[i]; } + const plStateDataRecord* GetStateDataRecord(int i) const { return fDataRecList[i]; } + int GetCount() const { return fDataRecList.size(); } + int GetUsedCount() const; + int GetDirtyCount() const; + void GetUsedDataRecords(ConstDataRecList*) const; + void GetDirtyDataRecords(ConstDataRecList*) const; + plVarDescriptor* GetVarDescriptor() { return fVarDescriptor; } + plSDVarDescriptor* GetSDVarDescriptor() { return fVarDescriptor->GetAsSDVarDescriptor(); } + const plVarDescriptor* GetVarDescriptor() const { return fVarDescriptor; } + const plSDVarDescriptor* GetSDVarDescriptor() const { return fVarDescriptor->GetAsSDVarDescriptor(); } + void FlagNewerState(const plSDStateVariable&, bool respectAlwaysNew); + void FlagAlwaysNewState(); + void DumpToObjectDebugger(bool dirtyOnly, int level) const; + void DumpToStream(hsStream* stream, bool dirtyOnly, int level) const; + + // IO + bool ReadData(hsStream* s, float timeConvert, UInt32 readOptions); + bool WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const; +}; + +// +// Contains the actual data contents and points to its associated descriptor +// +class plNetMsgSDLState; +class plStateDataRecord : public plCreatable +{ +public: + typedef std::vector SimpleVarsList; + typedef std::vector SDVarsList; + enum Flags + { + kVolatile = 0x1 + }; +protected: + typedef std::vector VarsList; + + const plStateDescriptor* fDescriptor; + plUoid fAssocObject; // optional + VarsList fVarsList; // list of variables + VarsList fSDVarsList; // list of nested data records + UInt32 fFlags; + static const UInt8 kIOVersion; // I/O Version + + void IDeleteVarsList(VarsList& vars); + void IInitDescriptor(const char* name, int version); // or plSDL::kLatestVersion + void IInitDescriptor(const plStateDescriptor* sd); + + void IReadHeader(hsStream* s); + void IWriteHeader(hsStream* s) const; + bool IConvertVar(plSimpleStateVariable* fromVar, plSimpleStateVariable* toVar, bool force); + + plStateVariable* IFindVar(const VarsList& vars, const char* name) const; + int IGetNumUsedVars(const VarsList& vars) const; + int IGetUsedVars(const VarsList& varsOut, VarsList *varsIn) const; // build a list of vars that have data + bool IHasUsedVars(const VarsList& vars) const; + + int IGetNumDirtyVars(const VarsList& vars) const; + int IGetDirtyVars(const VarsList& varsOut, VarsList *varsIn) const; // build a list of vars that are dirty + bool IHasDirtyVars(const VarsList& vars) const; +public: + CLASSNAME_REGISTER( plStateDataRecord ); + GETINTERFACE_ANY( plStateDataRecord, plCreatable); + + plStateDataRecord(const char* sdName, int version=plSDL::kLatestVersion); + plStateDataRecord(plStateDescriptor* sd); + plStateDataRecord(const plStateDataRecord &other, UInt32 writeOptions=0 ):fFlags(0) { CopyFrom(other, writeOptions); } + plStateDataRecord():fFlags(0) {} + ~plStateDataRecord(); + + bool ConvertTo(plStateDescriptor* other, bool force=false ); + bool operator==(const plStateDataRecord &other) const; // assumes matching state descriptors + + UInt32 GetFlags() const { return fFlags; } + void SetFlags(UInt32 f) { fFlags =f; } + + plSimpleStateVariable* FindVar(const char* name) const { return (plSimpleStateVariable*)IFindVar(fVarsList, name); } + plSDStateVariable* FindSDVar(const char* name) const { return (plSDStateVariable*)IFindVar(fSDVarsList, name); } + + plStateDataRecord& operator=(const plStateDataRecord& other) { CopyFrom(other); } + void CopyFrom(const plStateDataRecord& other, UInt32 writeOptions=0); + void UpdateFrom(const plStateDataRecord& other, UInt32 writeOptions=0); + void SetFromDefaults(bool timeStampNow); + void TimeStampDirtyVars(); + + int GetNumVars() const { return fVarsList.size(); } + plSimpleStateVariable* GetVar(int i) const { return (plSimpleStateVariable*)fVarsList[i]; } + int GetNumSDVars() const { return fSDVarsList.size(); } + plSDStateVariable* GetSDVar(int i) const { return (plSDStateVariable*)fSDVarsList[i]; } + + // Used vars + bool IsUsed() const { return (HasUsedVars() || HasUsedSDVars()); } + + int GetNumUsedVars() const { return IGetNumUsedVars(fVarsList); } + int GetUsedVars(SimpleVarsList *vars) const { return IGetUsedVars(fVarsList, (VarsList*)vars); } // build a list of vars that have data + bool HasUsedVars() const { return IHasUsedVars(fVarsList); } + + int GetNumUsedSDVars() const { return IGetNumUsedVars(fSDVarsList); } + int GetUsedSDVars(SDVarsList *vars) const { return IGetUsedVars(fSDVarsList, (VarsList*)vars); } // build a list of SD vars that have data + bool HasUsedSDVars() const { return IHasUsedVars(fSDVarsList); } + + // Dirty Vars + bool IsDirty() const { return (HasDirtyVars() || HasDirtySDVars()); } + + int GetNumDirtyVars() const { return IGetNumDirtyVars(fVarsList); } + int GetDirtyVars(SimpleVarsList *vars) const { return IGetDirtyVars(fVarsList, (VarsList*)vars); } // build a list of vars that are dirty + bool HasDirtyVars() const { return IHasDirtyVars(fVarsList); } + + int GetNumDirtySDVars() const { return IGetNumDirtyVars(fSDVarsList); } + int GetDirtySDVars(SDVarsList *vars) const { return IGetDirtyVars(fSDVarsList, (VarsList*)vars); }; // build a list of Sdvars that are dirty + bool HasDirtySDVars() const { return IHasDirtyVars(fSDVarsList); } + + const plStateDescriptor* GetDescriptor() const { return fDescriptor; } + void SetDescriptor(const char* sdName, int version); + + plNetMsgSDLState* PrepNetMsg(float timeConvert, UInt32 writeOptions) const; // create/prep a net msg with this data + + void SetAssocObject(const plUoid& u) { fAssocObject=u; } // optional + plUoid* GetAssocObject() { return &fAssocObject; } // optional + const plUoid* GetAssocObject() const { return &fAssocObject; } // optional + + // utils + void FlagDifferentState(const plStateDataRecord& other); // mark items which differ from 'other' as dirty + void FlagNewerState(const plStateDataRecord& other, bool respectAlwaysNew=false); // mark items which are newer than 'other' as dirty + void FlagAlwaysNewState(); // mark 'alwaysNew' items as dirty + void DumpToObjectDebugger(const char* msg, bool dirtyOnly=false, int level=0) const; + void DumpToStream(hsStream* stream, const char* msg, bool dirtyOnly=false, int level=0) const; + + // IO + bool Read(hsStream* s, float timeConvert, UInt32 readOptions=0); + void Write(hsStream* s, float timeConvert, UInt32 writeOptions=0) const; + + static bool ReadStreamHeader(hsStream* s, char** name, int* version, plUoid* objUoid=nil); + void WriteStreamHeader(hsStream* s, plUoid* objUoid=nil) const; +}; + +// +// Simple SDL parser +// +class plSDLMgr; +class plSDLParser +{ +private: + bool IReadDescriptors() const; + bool ILoadSDLFile(const char* fileName) const; + bool IParseVarDesc(const char* fileName, hsStream* stream, char token[], plStateDescriptor*& curDesc, + plVarDescriptor*& curVar) const; + bool IParseStateDesc(const char* fileName, hsStream* stream, char token[], plStateDescriptor*& curDesc) const; + + void DebugMsg(char* fmt, ...) const; + void DebugMsgV(char* fmt, va_list args) const; + +public: + + bool Parse() const; // reads sdl folder, creates descriptor list +}; + +// +// Holds, loads and unloads all state descriptors from sdl files. +// Singleton. +// +class plNetApp; +class plSDLMgr +{ + friend class plSDLParser; +private: + std::string fSDLDir; + plSDL::DescriptorList fDescriptors; + plNetApp* fNetApp; + UInt32 fBehaviorFlags; + + void IDeleteDescriptors(plSDL::DescriptorList* dl); +public: + plSDLMgr(); + ~plSDLMgr(); + + static plSDLMgr* GetInstance(); + plStateDescriptor* FindDescriptor(const char* name, int version, const plSDL::DescriptorList * dl=nil) const; // version or kLatestVersion + + const plSDL::DescriptorList * GetDescriptors( void ) const { return &fDescriptors;} + + void SetSDLDir(const char* s) { fSDLDir=s; } + const char* GetSDLDir() const { return fSDLDir.c_str(); } + + void SetNetApp(plNetApp* a) { fNetApp=a; } + plNetApp* GetNetApp() const { return fNetApp; } + + bool Init( UInt32 behaviorFlags=0 ); // parse sdl folder + void DeInit(); + UInt32 GetBehaviorFlags() const { return fBehaviorFlags; } + void SetBehaviorFlags(UInt32 v) { fBehaviorFlags=v; } + bool AllowTimeStamping() const { return ! ( fBehaviorFlags&plSDL::kDisallowTimeStamping ); } + + // I/O - return # of bytes read/written + int Write(hsStream* s, const plSDL::DescriptorList* dl=nil); // write descriptors to a stream + int Read(hsStream* s, plSDL::DescriptorList* dl=nil); // read descriptors into provided list (use legacyList if nil) +}; + +#endif // PL_SDL_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLCreatable.h new file mode 100644 index 00000000..6f6deddc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLCreatable.h @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSDLCreatable_h +#define plSDLCreatable_h + +#include "plSDL.h" +REGISTER_CREATABLE(plStateDataRecord); + +#endif // plSDLCreatable_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLDescriptor.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLDescriptor.h new file mode 100644 index 00000000..1857900c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLDescriptor.h @@ -0,0 +1,240 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSDL_DESC_inc +#define plSDL_DESC_inc + +// +// Code for State Description Language (SDL) Descriptors. +// These define a schema representing an object's saveState buffer. +// + +#include "hsTypes.h" +#include "hsUtils.h" +#include "hsStlUtils.h" + +// +// Describes a variable in a state descriptor. +// Every variable is actually a list, either fixed or variable length. +// Abstract base class. +// +class hsStream; +class plSimpleVarDescriptor; +class plSDVarDescriptor; +class plVarDescriptor +{ +public: + enum Type + { + kNone = -1, + + // atomic types + kInt, + kFloat, + kBool, + kString32, + kKey, // plKey - basically a uoid + kStateDescriptor, // this var refers to another state descriptor + kCreatable, // plCreatable - basically a classIdx and a read/write buffer + kDouble, + kTime, // double which is automatically converted to server clock and back, use for animation times + kByte, + kShort, + kAgeTimeOfDay, // float which is automatically set to the current age time of day (0-1) + + // the following are a vector of floats + kVector3=50,// atomicCount=3 + kPoint3, // atomicCount=3 + kRGB, // atomicCount=3 + kRGBA, // atomicCount=4 + kQuaternion, // atomicCount=4 + kRGB8, // atomicCount=3 + kRGBA8, // atomicCount=4 + }; + typedef char String32[32]; + + enum Flags + { + kInternal = 0x1, // Don't allow access to this var in Vault Mgr + kAlwaysNew = 0x2, // Treat this var as always having the latest timestamp when FlaggingNewerState + kVariableLength = 0x4 // Var is defined as int foo[], so it's length is variable, starting at 0 + }; +protected: + static const UInt8 kVersion; // for Read/Write format + char* fDefault; // set by .sdl + char* fName; // set by .sdl + int fCount; // set by .sdl + Type fType; // set by .sdl + char* fTypeString; // string version of fType + UInt32 fFlags; + std::string fDisplayOptions; // set by .sdl +public: + plVarDescriptor(); + virtual ~plVarDescriptor(); + + virtual void CopyFrom(const plVarDescriptor* v); + + // conversion ops + virtual plSimpleVarDescriptor* GetAsSimpleVarDescriptor() = 0; + virtual plSDVarDescriptor* GetAsSDVarDescriptor() = 0; + virtual const plSimpleVarDescriptor* GetAsSimpleVarDescriptor() const = 0; + virtual const plSDVarDescriptor* GetAsSDVarDescriptor() const = 0; + + // getters + const char* GetDefault() const { return fDefault; } + const char* GetName() const { return fName; } + Type GetType() const { return fType; } + const char* GetTypeString() const { return fTypeString; } + int GetCount() const { return fCount; } + bool IsInternal() const { return (fFlags & kInternal) != 0; } + bool IsAlwaysNew() const { return (fFlags & kAlwaysNew) != 0; } + bool IsVariableLength() const { return (fFlags & kVariableLength) != 0; } + const char* GetDisplayOptions() const { return fDisplayOptions.c_str(); } + + // setters + void SetDefault(const char* n) { delete [] fDefault; fDefault= hsStrcpy(n); } + void SetName(const char* n) { delete [] fName; fName = hsStrcpy(n); } + void SetCount(int c) { fCount=c; } + virtual bool SetType(const char* type); + void SetType(Type t) { fType=t; } + void SetInternal(bool d) { if (d) fFlags |= kInternal; else fFlags &= ~kInternal; } + void SetAlwaysNew(bool d) { if (d) fFlags |= kAlwaysNew; else fFlags &= ~kAlwaysNew; } + void SetVariableLength(bool d) { if (d) fFlags |= kVariableLength; else fFlags &= ~kVariableLength; } + void SetDisplayOptions(const char* s) { fDisplayOptions=s; } + + // IO + virtual bool Read(hsStream* s); + virtual void Write(hsStream* s) const; +}; + +// +// Simple, non-nested var descriptors. These are comprised of single types, as opposed to +// referring to another state descriptor. +// +class plSimpleVarDescriptor : public plVarDescriptor +{ +protected: + Type fAtomicType; // base type (it. quaternion == kFloat) + int fAtomicCount; // computed from type in .sdl (ie. quaternion == 4) +public: + plSimpleVarDescriptor(); + virtual ~plSimpleVarDescriptor() { } + + plSimpleVarDescriptor* GetAsSimpleVarDescriptor() { return this; } + plSDVarDescriptor* GetAsSDVarDescriptor() { return nil; } + const plSimpleVarDescriptor* GetAsSimpleVarDescriptor() const { return this; } + const plSDVarDescriptor* GetAsSDVarDescriptor() const { return nil; } + + void CopyFrom(const plSimpleVarDescriptor* v); + void CopyFrom(const plVarDescriptor* v) { plVarDescriptor::CopyFrom(v); } // lame compiler + + // getters + int GetSize() const; + int GetAtomicSize() const; // size of one item in bytes (regardless of count) + Type GetAtomicType() const { return fAtomicType; } + int GetAtomicCount() const { return fAtomicCount; } + + // setters + bool SetType(const char* type); + void SetType(Type t) { plVarDescriptor::SetType(t); } // for lame compiler + void SetAtomicType(Type t) { fAtomicType=t; } + + // IO + virtual bool Read(hsStream* s); + virtual void Write(hsStream* s) const; +}; + +// +// A var descriptor which references another state descriptor. +// +class plStateDescriptor; +class plSDVarDescriptor : public plVarDescriptor +{ +protected: + plStateDescriptor* fStateDesc; +public: + plSDVarDescriptor(plStateDescriptor* sd=nil) : fStateDesc(sd) { } + + plSimpleVarDescriptor* GetAsSimpleVarDescriptor() { return nil; } + plSDVarDescriptor* GetAsSDVarDescriptor() { return this; } + const plSimpleVarDescriptor* GetAsSimpleVarDescriptor() const { return nil; } + const plSDVarDescriptor* GetAsSDVarDescriptor() const { return this; } + + // getters + plStateDescriptor* GetStateDescriptor() const { return fStateDesc; } + + // setters + void SetStateDesc(plStateDescriptor* sd) { fStateDesc=sd; } + + void CopyFrom(const plSDVarDescriptor* v); + void CopyFrom(const plVarDescriptor* v) { plVarDescriptor::CopyFrom(v); } // lame compiler + + // IO + bool Read(hsStream* s); + void Write(hsStream* s) const; +}; + +// +// A state descriptor - describes the contents of a type of state buffer. +// There is one of these for each persistent object type. +// These descriptors are defined in a user-created .sdl file. +// +class plKey; +class plStateDescriptor +{ +private: + static const UInt8 kVersion; // for Read/Write format + typedef std::vector VarsList; + VarsList fVarsList; + int fVersion; + char* fName; + std::string fFilename; // the filename this descriptor was read from + + void IDeInit(); +public: + plStateDescriptor() : fVersion(-1),fName(nil) {} + ~plStateDescriptor(); + + // getters + const char* GetName() const { return fName; } + int GetNumVars() const { return fVarsList.size(); } + plVarDescriptor* GetVar(int i) const { return fVarsList[i]; } + int GetVersion() const { return fVersion; } + const char * GetFilename() const { return fFilename.c_str();} + + // setters + void SetVersion(int v) { fVersion=v; } + void SetName(const char* n) { delete [] fName; fName=hsStrcpy(n); } + void AddVar(plVarDescriptor* v) { fVarsList.push_back(v); } + void SetFilename( const char * n ) { fFilename=n;} + + plVarDescriptor* FindVar(const char* name, int* idx=nil) const; + + // IO + bool Read(hsStream* s); + void Write(hsStream* s) const; +}; + +#endif // plSDL_DESC_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLMgr.cpp new file mode 100644 index 00000000..4c6a6c9f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLMgr.cpp @@ -0,0 +1,191 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "hsStlUtils.h" +#include "plSDL.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnNetCommon/pnNetCommon.h" +#include + +///////////////////////////////////////////////////////////////////////////////// +// SDL MGR +///////////////////////////////////////////////////////////////////////////////// + +// +// +// +plSDLMgr::plSDLMgr() : fSDLDir("SDL"), fNetApp(nil), fBehaviorFlags(0) +{ + +} + +// +// +// +plSDLMgr::~plSDLMgr() +{ + DeInit(); +} + +bool plSDLMgr::Init( UInt32 behaviorFlags ) +{ + fBehaviorFlags = behaviorFlags; + plSDLParser parser; + return parser.Parse(); +} + +void plSDLMgr::DeInit() +{ + IDeleteDescriptors(&fDescriptors); +} + +// +// delete all descriptors +// +void plSDLMgr::IDeleteDescriptors(plSDL::DescriptorList* dl) +{ + std::for_each( dl->begin(), dl->end(), xtl::delete_ptr() ); + dl->clear(); +} + + +// +// STATIC +// +plSDLMgr* plSDLMgr::GetInstance() +{ + static plSDLMgr gSDLMgr; + return &gSDLMgr; +} + +// +// search latest and legacy descriptors for one that matches. +// if version is -1, search for latest descriptor with matching name +// +plStateDescriptor* plSDLMgr::FindDescriptor(const char* name, int version, const plSDL::DescriptorList * dl) const +{ + if (!name) + return nil; + + if ( !dl ) + dl = &fDescriptors; + + plStateDescriptor* sd = nil; + + plSDL::DescriptorList::const_iterator it; + + int highestFound = -1; + for(it=(*dl).begin(); it!= (*dl).end(); it++) + { + if (!_stricmp((*it)->GetName(), name) ) + { + if ( (*it)->GetVersion()==version ) + { + sd = *it; + break; + } + else if ( version==plSDL::kLatestVersion && (*it)->GetVersion()>highestFound ) + { + sd = *it; + highestFound = (*it)->GetVersion(); + } + } + } + + return sd; +} + +// +// write latest descriptors to a stream. +// return number of bytes +// +int plSDLMgr::Write(hsStream* s, const plSDL::DescriptorList* dl) +{ + int pos=s->GetPosition(); + + if (dl==nil) + dl=&fDescriptors; + + UInt16 num=dl->size(); + s->WriteSwap(num); + + plSDL::DescriptorList::const_iterator it; + for(it=dl->begin(); it!= dl->end(); it++) + (*it)->Write(s); + + int bytes=s->GetPosition()-pos; + if (fNetApp) + { + hsLogEntry(fNetApp->DebugMsg("Writing %d SDL descriptors, %d bytes", num, bytes)); + } + return bytes; +} + +// +// read descriptors into provided list +// return number of bytes +// +int plSDLMgr::Read(hsStream* s, plSDL::DescriptorList* dl) +{ + int pos=s->GetPosition(); + + if (dl==nil) + dl=&fDescriptors; + + // clear dl + IDeleteDescriptors(dl); + + UInt16 num; + try + { + // read dtor list + s->ReadSwap(&num); + + int i; + for(i=0;iRead(s)) + dl->push_back(sd); + } + } + catch(...) + { + if (fNetApp) + { + hsLogEntry(fNetApp->DebugMsg("Something bad happened while reading SDLMgr data")); + } + return 0; + } + + int bytes=s->GetPosition()-pos; + if (fNetApp) + { + hsLogEntry(fNetApp->DebugMsg("Reading %d SDL descriptors, %d bytes", num, bytes)); + } + return bytes; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLParser.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLParser.cpp new file mode 100644 index 00000000..d310655f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plSDLParser.cpp @@ -0,0 +1,422 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStlUtils.h" +#include "plSDL.h" +#include "../plFile/hsFiles.h" +#include "../plFile/plStreamSource.h" +#include "../pnNetCommon/pnNetCommon.h" +#include "../pnNetCommon/plNetApp.h" + +static const int kTokenLen=256; + +void plSDLParser::DebugMsg(char* fmt, ...) const +{ + return; + plNetApp* netApp = plSDLMgr::GetInstance()->GetNetApp(); + + va_list args; + va_start(args, fmt); + + if (netApp) + { + hsLogEntry(netApp->DebugMsgV(fmt, args)); + } + else + DebugMsgV(fmt, args); + va_end(args); +} + +void plSDLParser::DebugMsgV(char* fmt, va_list args) const +{ + if (strlen(fmt)==nil) + return; + hsStatusMessage(xtl::formatv(fmt,args).c_str()); +} + +// +// parsing stateDesc +// read name, version +// return true to skip the next token read +// +bool plSDLParser::IParseStateDesc(const char* fileName, hsStream* stream, char token[], plStateDescriptor*& curDesc) const +{ + plSDL::DescriptorList* descList = &plSDLMgr::GetInstance()->fDescriptors; + + bool ok = true; + + // + // NAME + // +// curDesc=plSDLMgr::GetInstance()->FindDescriptor(token, plSDL::kLatestVersion); +// if (!curDesc) + { + curDesc = TRACKED_NEW plStateDescriptor; + curDesc->SetName(token); + + DebugMsg("SDL: DESC name=%s", token); + } + + // + // { + // + stream->GetToken(token, kTokenLen); // skip '{' + + // + // VERSION + // + if (stream->GetToken(token, kTokenLen)) + { + if (!strcmp(token, "VERSION")) + { + // read desc version + hsAssert(curDesc, xtl::format("Syntax problem with .sdl file, fileName=%s", fileName).c_str()); + if (stream->GetToken(token, kTokenLen)) + { + int v=atoi(token); + curDesc->SetVersion(v); + DebugMsg("\tVersion=%d", v); + } + } + else + { + hsAssert(false, xtl::format("Error parsing state desc, missing VERSION, fileName=%s", + fileName).c_str()); + ok = false; + } + } + else + { + hsAssert(false, xtl::format("Error parsing state desc, fileName=%s", fileName).c_str()); + ok = false; + } + + if ( ok ) + { + ok = ( plSDLMgr::GetInstance()->FindDescriptor(curDesc->GetName(), curDesc->GetVersion())==nil ); + if ( !ok ) + { + std::string err = xtl::format( "Found duplicate SDL descriptor for %s version %d.\nFailed to parse file: %s", curDesc->GetName(), curDesc->GetVersion(), fileName ); + plNetApp::StaticErrorMsg( err.c_str() ); + hsAssert( false, err.c_str() ); + } + } + + if ( ok ) + { + descList->push_back(curDesc); + } + else + { + delete curDesc; + curDesc = nil; + } + + return false; +} + +// +// Parse a variable descriptor. +// read type, name, count [default] +// return true to skip the next token read +// +bool plSDLParser::IParseVarDesc(const char* fileName, hsStream* stream, char token[], plStateDescriptor*& curDesc, + plVarDescriptor*& curVar) const +{ + hsAssert(curDesc, xtl::format("Syntax problem with .sdl file, fileName=%s", fileName).c_str()); + if ( !curDesc ) + return false; + + bool skipNext=false; + std::string dbgStr; + static char seps[] = "( ,)[]"; + // read type, name, cnt, [default] + + // + // TYPE + // create new state var, make current + // + if (*token == '$') + { + // nested sdls + char* sdlName = token+1; + plStateDescriptor* stateDesc = plSDLMgr::GetInstance()->FindDescriptor(sdlName, plSDL::kLatestVersion); + hsAssert(stateDesc, xtl::format("can't find nested state desc reference %s, fileName=%s", + sdlName, fileName).c_str()); + curVar = TRACKED_NEW plSDVarDescriptor(stateDesc); + } + else + curVar = TRACKED_NEW plSimpleVarDescriptor; + + curDesc->AddVar(curVar); + bool ok=curVar->SetType(token); + hsAssert(ok, xtl::format("Variable 'type' syntax problem with .sdl file, type=%s, fileName=%s", token, fileName).c_str()); + dbgStr = xtl::format("\tVAR Type=%s ", token).c_str(); + + // + // NAME (foo[1]) + // + if (stream->GetToken(token, kTokenLen)) + { + hsAssert(strstr(token, "[") && strstr(token, "]"), xtl::format("invalid var syntax, missing [x], fileName=%s", + fileName).c_str()); + char* ptr = strtok( token, seps ); // skip [ + + hsAssert(curVar, xtl::format("Missing current var. Syntax problem with .sdl file, fileName=%s", fileName).c_str()); + curVar->SetName(token); + // + // COUNT + // + char* cntTok=strtok(nil, seps); // kill ] + int cnt = cntTok ? atoi(cntTok) : 0; + curVar->SetCount(cnt); + if (cnt==0) + curVar->SetVariableLength(true); + dbgStr += xtl::format("Name=%s[%d]", curVar->GetName(), cnt).c_str(); + } + + // + // optional tokens: DEFAULT, INTERNAL + // + while (stream->GetToken(token, kTokenLen)) + { + if (!strcmp(token, "DEFAULT")) + { + hsAssert(curVar, xtl::format("Syntax problem with .sdl file, fileName=%s", fileName).c_str()); + // read state var type + + std::string defaultStr; + plSimpleVarDescriptor* sVar=(plSimpleVarDescriptor*)curVar; + if (sVar) + { + int i; + for(i=0;iGetAtomicCount();i++) + { + if (stream->GetToken(token, kTokenLen)) + { + defaultStr += token; + if (i!=sVar->GetAtomicCount()-1) + defaultStr += ","; + } + } + } + if (defaultStr.size()) + { + curVar->SetDefault(defaultStr.c_str()); + dbgStr += std::string(" DEFAULT=") + defaultStr; + } + } + else + if (!strcmp(token, "DISPLAYOPTION")) + { + hsAssert(curVar, xtl::format("Syntax problem with .sdl file, fileName=%s", fileName).c_str()); + dbgStr += std::string(" ") + token; + + hsBool read=stream->GetToken(token, kTokenLen); + if (read) + { + std::string oldOptions=curVar->GetDisplayOptions(); + if (oldOptions.size()) + oldOptions += std::string(","); + oldOptions += token; + curVar->SetDisplayOptions(oldOptions.c_str()); + dbgStr += std::string("=") + token; + if (!stricmp(token, "hidden")) + curVar->SetInternal(true); + } + else + { + hsAssert(false, xtl::format("missing displayOption string, fileName=%s", fileName).c_str()); + } + } + else + if (!strcmp(token, "DEFAULTOPTION")) + { + hsAssert(curVar, xtl::format("Syntax problem with .sdl file, fileName=%s", fileName).c_str()); + dbgStr += std::string(" ") + token; + + hsBool read=stream->GetToken(token, kTokenLen); + if (read) + { + dbgStr += std::string("=") + token; + if (!stricmp(token, "vault")) + curVar->SetAlwaysNew(true); + } + else + { + hsAssert(false, xtl::format("missing defaultOption string, fileName=%s", fileName).c_str()); + } + } + +#if 1 // delete me in May 2003 + else + if (!strcmp(token, "INTERNAL")) + { + hsAssert(curVar, xtl::format("Syntax problem with .sdl file, fileName=%s", fileName).c_str()); + curVar->SetInternal(true); + dbgStr += std::string(" ") + token; + } + else + if (!strcmp(token, "PHASED")) + { + hsAssert(curVar, xtl::format("Syntax problem with .sdl file, fileName=%s", fileName).c_str()); + curVar->SetAlwaysNew(true); + dbgStr += std::string(" ") + token; + } +#endif + else + { + skipNext=true; + break; + } + } + + DebugMsg((char*)dbgStr.c_str()); + + return skipNext; +} + +// +// create state descriptor from sdl file. +// return false on err. +// +bool plSDLParser::ILoadSDLFile(const char* fileName) const +{ + DebugMsg("Parsing SDL file %s", fileName); + + wchar_t* temp = hsStringToWString(fileName); + hsStream* stream = plStreamSource::GetInstance()->GetFile(temp); + delete [] temp; + if (!stream) + return false; + + stream->Rewind(); + + plVarDescriptor* curVar=nil; + plStateDescriptor* curDesc=nil; + char token[kTokenLen]; + bool parsingStateDesc=false; + bool skip=false; + while (1) + { + if (!skip) + { + if (!stream->GetToken(token, kTokenLen)) + break; + } + skip=false; + + if (!strcmp(token, "VAR")) + { + parsingStateDesc=false; + curVar=nil; // start fresh + continue; + } + + if (!strcmp(token, "STATEDESC")) + { + parsingStateDesc=true; + curDesc=nil; // start fresh + continue; + } + + if (!strcmp(token, "}")) + { + if ( curDesc ) + curDesc->SetFilename( fileName ); + parsingStateDesc=false; + continue; + } + + if (parsingStateDesc) + { + skip=IParseStateDesc(fileName, stream, token, curDesc); + if ( !curDesc ) + break; // failed to parse state desc + } + else + { + skip=IParseVarDesc(fileName, stream, token, curDesc, curVar); + } + } + + // If the very last char is a } without a \n, then it won't be handled above for some reason, so we have to catch it here. + if ( curDesc ) + curDesc->SetFilename( fileName ); + + // do not close or delete the stream, we do not own it + return true; +} + +// +// load all .sdl files in sdl directory, and create descriptors for each. +// return false on error +// +bool plSDLParser::IReadDescriptors() const +{ + std::string sdlDir = plSDLMgr::GetInstance()->GetSDLDir(); + DebugMsg("SDL: Reading latest descriptors from directory %s", sdlDir.c_str()); + + wchar_t* temp = hsStringToWString(sdlDir.c_str()); + std::wstring wSDLDir = temp; + delete [] temp; + + // Get the names of all the sdl files + std::vector files = plStreamSource::GetInstance()->GetListOfNames(wSDLDir, L".sdl"); + + bool ret=true; + int cnt=0; + for (int i = 0; i < files.size(); i++) + { + char* str = hsWStringToString(files[i].c_str()); + if (!ILoadSDLFile(str)) + { + plNetApp* netApp = plSDLMgr::GetInstance()->GetNetApp(); + if (netApp) + netApp->ErrorMsg("Error loading SDL file %s", str); + else + hsStatusMessageF("Error loading SDL file %s", str); + ret=false; + } + else + cnt++; + delete [] str; + } + DebugMsg("Done reading SDL files"); + + if (!cnt) + ret=false; + + return ret; +} + + +// +// reads sdl folder, creates descriptor list +// +bool plSDLParser::Parse() const +{ + return IReadDescriptors(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateChangeNotifier.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateChangeNotifier.cpp new file mode 100644 index 00000000..f40500f3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateChangeNotifier.cpp @@ -0,0 +1,125 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSDL.h" +#include "../pnMessage/plSDLNotificationMsg.h" +#include "algorithm" + +// static +UInt32 plStateChangeNotifier::fCurrentPlayerID = 0; + +plStateChangeNotifier::plStateChangeNotifier() : +fDelta(0) +{ + +} + +plStateChangeNotifier::plStateChangeNotifier(float i, plKey k) +{ + SetValue(i); + IAddKey(k); +} + +void plStateChangeNotifier::IAddKey(plKey k) +{ + KeyList::iterator it = std::find(fKeys.begin(), fKeys.end(), k); + if (it==fKeys.end()) + fKeys.push_back(k); +} + +int plStateChangeNotifier::IRemoveKey(plKey k) +{ + KeyList::iterator it = std::find(fKeys.begin(), fKeys.end(), k); + if (it!=fKeys.end()) + fKeys.erase(it); + return fKeys.size(); +} + +// +// returns number of keys left after removal +// +int plStateChangeNotifier::RemoveNotificationKey(plKey k) +{ + return IRemoveKey(k); +} + +// +// returns number of keys left after removal +// +int plStateChangeNotifier::RemoveNotificationKeys(KeyList keys) +{ + KeyList::iterator it=keys.begin(); + for( ; it != keys.end(); it++) + IRemoveKey(*it); + + return fKeys.size(); +} + +void plStateChangeNotifier::AddNotificationKeys(KeyList keys) +{ + KeyList::iterator it=keys.begin(); + for( ; it != keys.end(); it++) + IAddKey(*it); +} + +bool plStateChangeNotifier::GetValue(float* i) const +{ + *i=fDelta; + return true; +} + +bool plStateChangeNotifier::SetValue(float i) +{ + fDelta=i; + return true; +} + +bool plStateChangeNotifier::operator==(const plStateChangeNotifier &other) const +{ + return (other.fDelta==fDelta && other.fKeys==fKeys); +} + +// +// send notification msg to all registered recipients +// +void plStateChangeNotifier::SendNotificationMsg(const plSimpleStateVariable* srcVar, const plSimpleStateVariable* dstVar, + const char* sdlName) +{ + plSDLNotificationMsg* m = TRACKED_NEW plSDLNotificationMsg; + + // add receivers + KeyList::iterator kit=fKeys.begin(); + for(; kit != fKeys.end(); kit++) + m->AddReceiver(*kit); + + m->fDelta=fDelta; + m->fVar=dstVar; + m->fSDLName = sdlName; + m->fPlayerID = GetCurrentPlayerID(); + m->fHintString = srcVar->GetNotificationInfo().GetHintString(); + + m->Send(); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateDataRecord.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateDataRecord.cpp new file mode 100644 index 00000000..a228bca8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateDataRecord.cpp @@ -0,0 +1,831 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "hsTimer.h" +#include "hsTemplates.h" +#include "hsStream.h" +#include "hsStlUtils.h" +#include "plSDL.h" + +#include "../plNetMessage/plNetMessage.h" +#include "../pnNetCommon/plNetApp.h" + +const char* plSDL::kAgeSDLObjectName = {"AgeSDLHook"}; + +// static +const UInt8 plStateDataRecord::kIOVersion=6; + +// +// helper +// +void plSDL::VariableLengthRead(hsStream* s, int size, int* val) +{ + // hsAssert(size, "unexpected size"); + + if (size < (1<<8)) + *val = s->ReadByte(); + else + if (size < (1<<16)) + *val = s->ReadSwap16(); + else + *val = s->ReadSwap32(); +} + +// +// helper +// +void plSDL::VariableLengthWrite(hsStream* s, int size, int val) +{ + // hsAssert(size, "unexpected size"); + + if (size < (1<<8)) + { + hsAssert(val < (1<<8), "SDL data loss"); + s->WriteByte(val); + } + else + if (size < (1<<16)) + { + hsAssert(val < (1<<16), "SDL data loss"); + s->WriteSwap16(val); + } + else + s->WriteSwap32(val); +} + +///////////////////////////////////////////////////////////////////////////////// +// State Data +///////////////////////////////////////////////////////////////////////////////// +plStateDataRecord::plStateDataRecord(const char* name, int version) : fFlags(0) +, fDescriptor( nil ) +{ + SetDescriptor(name, version); +} + +plStateDataRecord::plStateDataRecord(plStateDescriptor* sd) : fFlags(0) +, fDescriptor( nil ) +{ + IInitDescriptor(sd); +} + +plStateDataRecord::~plStateDataRecord() +{ + IDeleteVarsList(fVarsList); + IDeleteVarsList(fSDVarsList); +} + +void plStateDataRecord::SetDescriptor(const char* name, int version) +{ + IInitDescriptor(name, version); +} + + +void plStateDataRecord::IDeleteVarsList(VarsList& vars) +{ + std::for_each( vars.begin(), vars.end(), xtl::delete_ptr() ); + vars.clear(); +} + +void plStateDataRecord::IInitDescriptor(const char* name, int version) +{ + plStateDescriptor* sd = plSDLMgr::GetInstance()->FindDescriptor(name, version); + //hsAssert( sd, xtl::format("Failed to find sdl descriptor: %s,%d. Missing legacy descriptor?", name, version ).c_str() ); + if (sd) + IInitDescriptor(sd); +} + +// +// Point to descriptor. +// setup state variables which correspond to the state descriptor. +// +void plStateDataRecord::IInitDescriptor(const plStateDescriptor* sd) +{ + // pt to state desc + fDescriptor=sd; + + // delete old vars + IDeleteVarsList(fVarsList); + IDeleteVarsList(fSDVarsList); + + // create vars defined by state desc + if (sd) + { + for(int i = 0; i < sd->GetNumVars(); ++i) + { + if (plVarDescriptor* vd = sd->GetVar(i)) + { + if (vd->GetAsSDVarDescriptor()) + { // it's a var which references another state descriptor. + fSDVarsList.push_back(TRACKED_NEW plSDStateVariable(vd->GetAsSDVarDescriptor())); + } + else + { + hsAssert(vd->GetAsSimpleVarDescriptor(), "var class problem"); + fVarsList.push_back(TRACKED_NEW plSimpleStateVariable(vd->GetAsSimpleVarDescriptor())); + } + } + } + } +} + +/////////////////// +// DIRTY VARS +/////////////////// +int plStateDataRecord::IGetNumDirtyVars(const VarsList& vars) const +{ + int i, cnt=0; + for(i=0;iIsDirty()) + cnt++; + return cnt; +} + +int plStateDataRecord::IGetDirtyVars(const VarsList& varsIn, VarsList* varsOut) const +{ + int i; + for(i=0;iIsDirty()) + varsOut->push_back(varsIn[i]); + return varsOut->size(); +} + +bool plStateDataRecord::IHasDirtyVars(const VarsList& vars) const +{ + int i; + for(i=0;iIsDirty()) + return true; + return false; +} + +//////////////// +// USED VARS +//////////////// +int plStateDataRecord::IGetNumUsedVars(const VarsList& vars) const +{ + int i, cnt=0; + for(i=0;iIsUsed()) + cnt++; + return cnt; +} + +int plStateDataRecord::IGetUsedVars(const VarsList& varsIn, VarsList* varsOut) const +{ + int i; + for(i=0;iIsUsed()) + varsOut->push_back(varsIn[i]); + return varsOut->size(); +} + +bool plStateDataRecord::IHasUsedVars(const VarsList& vars) const +{ + int i; + for(i=0;iIsUsed()) + return true; + return false; +} + +/////////////////////////////////////// +// IO +/////////////////////////////////////// + +// +// read state vars and indices, return true on success +// +bool plStateDataRecord::Read(hsStream* s, float timeConvert, UInt32 readOptions) +{ + fFlags = s->ReadSwap16(); + UInt8 ioVersion = s->ReadByte(); + if (ioVersion != kIOVersion) + return false; + + // + // read simple var data + // + hsAssert(fDescriptor, "State Data Record has nil SDL descriptor"); + if (!fDescriptor) + return false; + + int num; + plSDL::VariableLengthRead(s, fDescriptor->GetNumVars(), &num ); + + // if we are readeing the entire list, we don't need to read each index + bool all = (num==fVarsList.size()); + + int i; + try + { + for(i=0;iGetNumVars(), &idx ); + else + idx=i; + if (idx>=fVarsList.size() || !fVarsList[idx]->ReadData(s, timeConvert, readOptions)) + { + if (plSDLMgr::GetInstance()->GetNetApp()) + plSDLMgr::GetInstance()->GetNetApp()->ErrorMsg("Failed reading SDL, desc %s", + fDescriptor && fDescriptor->GetName() ? fDescriptor->GetName() : "?"); + return false; + } + } + } + catch(...) + { + hsAssert( false, + xtl::format("Something bad happened while reading simple var data, desc:%s", + fDescriptor && fDescriptor->GetName() ? fDescriptor->GetName() : "?").c_str()); + return false; + } + + // + // read nested var data + // + plSDL::VariableLengthRead(s, fDescriptor->GetNumVars(), &num ); + + // if we are readeing the entire list, we don't need to write each index + all = (num==fSDVarsList.size()); + + try + { + for(i=0;iGetNumVars(), &idx ); + else + idx=i; + if (idx>=fSDVarsList.size() || !fSDVarsList[idx]->ReadData(s, timeConvert, readOptions)) // calls plStateDataRecord::Read recursively + { + if (plSDLMgr::GetInstance()->GetNetApp()) + plSDLMgr::GetInstance()->GetNetApp()->ErrorMsg("Failed reading nested SDL, desc %s", + fDescriptor && fDescriptor->GetName() ? fDescriptor->GetName() : "?"); + return false; + } + } + } + catch(...) + { + hsAssert( false, + xtl::format("Something bad happened while reading nested var data, desc:%s", + fDescriptor && fDescriptor->GetName() ? fDescriptor->GetName() : "?").c_str()); + return false; + } + + // convert to latest descriptor + // Only really need to do this the first time this descriptor is read... + plStateDescriptor* latestDesc=plSDLMgr::GetInstance()->FindDescriptor(fDescriptor->GetName(), plSDL::kLatestVersion); + hsAssert( latestDesc, xtl::format("Failed to find latest sdl descriptor for: %s", fDescriptor->GetName() ).c_str() ); + bool forceConvert = (readOptions&plSDL::kForceConvert)!=0; + if ( latestDesc && ( forceConvert || ( fDescriptor->GetVersion()!=latestDesc->GetVersion() ) ) ) + { + DumpToObjectDebugger( "PreConvert" ); + ConvertTo( latestDesc, forceConvert ); + DumpToObjectDebugger( "PostConvert" ); + } + + return true; // ok +} + +// +// write out the state vars, along with their index +// +void plStateDataRecord::Write(hsStream* s, float timeConvert, UInt32 writeOptions) const +{ +#ifdef HS_DEBUGGING + if ( !plSDLMgr::GetInstance()->AllowTimeStamping() && (writeOptions & plSDL::kWriteTimeStamps) ) + { + hsAssert( false, "SDL behavior flags disallow var timestamping on write.\nRemoving kWriteTimeStamps flag from writeOptions..." ); + writeOptions &= ~plSDL::kWriteTimeStamps; + } +#endif + + s->WriteSwap16((UInt16)fFlags); + s->WriteByte(kIOVersion); + + // + // write simple vars + // + bool dirtyOnly = (writeOptions & plSDL::kDirtyOnly) != 0; + int num = dirtyOnly ? GetNumDirtyVars() : GetNumUsedVars(); + plSDL::VariableLengthWrite(s, fDescriptor->GetNumVars(), num ); // write affected vars count + + // if we are writing he entire list, we don't need to write each index + bool all = (num==fVarsList.size()); + + int i; + for(i=0;iIsDirty()) || + (!dirtyOnly && fVarsList[i]->IsUsed()) ) + { + if (!all) + plSDL::VariableLengthWrite(s, fDescriptor->GetNumVars(), i ); // index + fVarsList[i]->WriteData(s, timeConvert, writeOptions); // data + } + } + + // + // write nested vars + // + num = dirtyOnly ? GetNumDirtySDVars() : GetNumUsedSDVars(); + plSDL::VariableLengthWrite(s, fDescriptor->GetNumVars(), num ); // write affected vars count + + // if we are writing he entire list, we don't need to write each index + all = (num==fSDVarsList.size()); + + for(i=0;iIsDirty()) || + (!dirtyOnly && fSDVarsList[i]->IsUsed()) ) + { + if (!all) + plSDL::VariableLengthWrite(s, fDescriptor->GetNumVars(), i ); // index + fSDVarsList[i]->WriteData(s, timeConvert, writeOptions); // data, calls plStateDataRecord::Write recursively + } + } +} + +// +// STATIC - read prefix header. returns true on success +// +bool plStateDataRecord::ReadStreamHeader(hsStream* s, char** name, int* version, plUoid* objUoid) +{ + UInt16 savFlags; + s->ReadSwap(&savFlags); + if (!(savFlags & plSDL::kAddedVarLengthIO)) // using to establish a new version in the header, can delete in 8/03 + { + *name = nil; + return false; // bad version + } + + *name = s->ReadSafeString(); + *version = s->ReadSwap16(); + + if (objUoid) + { + hsAssert(savFlags & plSDL::kHasUoid, "SDL state data rec expecting to read a uoid, but there isn't one"); + } + + if (savFlags & plSDL::kHasUoid) + { + if (objUoid) + objUoid->Read(s); + else + { + plUoid tmp; + tmp.Read(s); + } + } + + return true; // ok +} + +// +// non-static - write prefix header. helper fxn +// +void plStateDataRecord::WriteStreamHeader(hsStream* s, plUoid* objUoid) const +{ + UInt16 savFlags=plSDL::kAddedVarLengthIO; // using to establish a new version in the header, can delete in 8/03 + if (objUoid) + savFlags |= plSDL::kHasUoid; + + s->WriteSwap(savFlags); + s->WriteSafeString(GetDescriptor()->GetName()); + s->WriteSwap16((Int16)GetDescriptor()->GetVersion()); + if (objUoid) + objUoid->Write(s); +} + +// +// create and prepare a net msg with this data +// +plNetMsgSDLState* plStateDataRecord::PrepNetMsg(float timeConvert, UInt32 writeOptions) const +{ + // save to stream + hsRAMStream stream; + WriteStreamHeader(&stream); + Write(&stream, timeConvert, writeOptions); + + // fill in net msg + plNetMsgSDLState* msg; + if (writeOptions & plSDL::kBroadcast) + msg = TRACKED_NEW plNetMsgSDLStateBCast; + else + msg = TRACKED_NEW plNetMsgSDLState; + + msg->StreamInfo()->CopyStream(&stream); + return msg; +} + +// +// Destroys 'this' and makes a total copy of other +// +void plStateDataRecord::CopyFrom(const plStateDataRecord& other, UInt32 writeOptions/*=0*/) +{ + fFlags = other.GetFlags(); + IInitDescriptor(other.GetDescriptor()); + int i; + for(i=0;iIsUsed()) + GetVar(i)->CopyData(other.GetVar(i),writeOptions); + } + + for(i=0;iIsUsed()) + GetSDVar(i)->CopyFrom(other.GetSDVar(i),writeOptions); + } +} + +// +// Find the data items which are dirty in 'other' and +// copy them to my corresponding item. +// Requires that records have the same descriptor. +// +void plStateDataRecord::UpdateFrom(const plStateDataRecord& other, UInt32 writeOptions/*=0*/) +{ + if ( GetDescriptor()->GetVersion()!=other.GetDescriptor()->GetVersion() ) + { + plStateDescriptor* sd=plSDLMgr::GetInstance()->FindDescriptor( other.GetDescriptor()->GetName(), other.GetDescriptor()->GetVersion() ); + hsAssert( sd, xtl::format( "Failed to find sdl descriptor %s,%d. Missing legacy descriptor?", other.GetDescriptor()->GetName(), other.GetDescriptor()->GetVersion() ).c_str() ); + ConvertTo( sd ); + } + + hsAssert(other.GetDescriptor()==fDescriptor, + xtl::format("descriptor mismatch in UpdateFromDirty, SDL=%s,%s version %d %d", + GetDescriptor()->GetName(), other.GetDescriptor()->GetName(), + GetDescriptor()->GetVersion(), other.GetDescriptor()->GetVersion()).c_str()); + + bool dirtyOnly = (writeOptions & plSDL::kDirtyOnly); + + int i; + for(i=0;iIsDirty()) || (!dirtyOnly && other.GetVar(i)->IsUsed()) ) + { + GetVar(i)->NotifyStateChange(other.GetVar(i), GetDescriptor()->GetName()); // see if there is enough difference to send state chg notification + GetVar(i)->CopyData(other.GetVar(i), writeOptions ); // simple vars get copied completely, non-partial + } + } + + for(i=0;iIsDirty()) || (!dirtyOnly && other.GetSDVar(i)->IsUsed()) ) + GetSDVar(i)->UpdateFrom(other.GetSDVar(i), writeOptions); + } +} + +// +// dirty my items which are different from the corresponding one in 'other'. +// Requires that records have the same descriptor. +// +void plStateDataRecord::FlagDifferentState(const plStateDataRecord& other) +{ + if (other.GetDescriptor()==fDescriptor) + { + int i; + for(i=0;iIsUsed() && ! (*other.GetVar(i) == *GetVar(i)) ); + GetVar(i)->SetDirty(diff); + } + + for(i=0;iIsUsed() && ! (*other.GetSDVar(i) == *GetSDVar(i)) ); + GetSDVar(i)->SetDirty(diff); + } + } + else + { + hsAssert(false, xtl::format("descriptor mismatch in FlagDifferentState, mine %s %d, other %s %d", + fDescriptor->GetName(), fDescriptor->GetVersion(), + other.GetDescriptor()->GetName(), other.GetDescriptor()->GetVersion()).c_str()); + } +} + +// +// dirty my items which are flagged as alwaysNew. +// +void plStateDataRecord::FlagAlwaysNewState() +{ + int i; + for(i=0;iIsUsed() && GetVar(i)->GetVarDescriptor()->IsAlwaysNew()); // flagged to be always new + GetVar(i)->SetDirty(newer); + } + + for(i=0;iIsUsed()) + { + GetSDVar(i)->FlagAlwaysNewState(); + } + } +} + +// +// dirty my items which are newer than the corresponding one in 'other'. +// Requires that records have the same descriptor. +// +void plStateDataRecord::FlagNewerState(const plStateDataRecord& other, bool respectAlwaysNew) +{ + if (other.GetDescriptor()==fDescriptor) + { + int i; + for(i=0;iIsUsed() && + (GetVar(i)->GetTimeStamp() > other.GetVar(i)->GetTimeStamp() || // later timestamp + (respectAlwaysNew && GetVar(i)->GetVarDescriptor()->IsAlwaysNew())) ); // flagged to be always new + GetVar(i)->SetDirty(newer); + } + + for(i=0;iIsUsed()) + { + GetSDVar(i)->FlagNewerState(*other.GetSDVar(i), respectAlwaysNew); + } + } + } + else + { + hsAssert(false, xtl::format("descriptor mismatch in FlagNewerState, mine %s %d, other %s %d", + fDescriptor->GetName(), fDescriptor->GetVersion(), + other.GetDescriptor()->GetName(), other.GetDescriptor()->GetVersion()).c_str()); + } +} + +// +// assumes matching state descriptors +// +bool plStateDataRecord::operator==(const plStateDataRecord &other) const +{ + // hsAssert(other.GetDescriptor()==fDescriptor, "descriptor mismatch in equality check"); + if (other.GetDescriptor()!=fDescriptor) + return false; + + int i; + for(i=0;iSetFromDefaults(false); + + if (!fromVar) + return true; // no corresponding var in the old state, done + + // Copy the value to the dst, converting if necessary + fromVar->ConvertTo(toVar->GetSimpleVarDescriptor(),force); + toVar->CopyData(fromVar, plSDL::kWriteTimeStamps | plSDL::kKeepDirty); + + return true; // ok +} + +plStateVariable* plStateDataRecord::IFindVar(const VarsList& vars, const char* name) const +{ + for (int i = 0; i < vars.size(); i++) + { + if (!stricmp(vars[i]->GetVarDescriptor()->GetName(), name)) + return vars[i]; + } + + if (plSDLMgr::GetInstance()->GetNetApp()) + plSDLMgr::GetInstance()->GetNetApp()->ErrorMsg("Failed to find SDL var %s", name); + + return nil; +} + +// +// try to convert our data (old version) to other's (new) version. +// return false on err. +// +bool plStateDataRecord::ConvertTo( plStateDescriptor* other, bool force ) +{ + if (!other && !force) + return false; // err + + hsAssert(!stricmp(fDescriptor->GetName(), other->GetName()), "descriptor mismatch"); + + if ( !force && (other == fDescriptor || other->GetVersion()==fDescriptor->GetVersion())) + return true; // ok, nothing to do + + hsAssert(other->GetVersion()>=fDescriptor->GetVersion(), "converting to an older state descriptor version?"); + + hsLogEntry( plNetApp::StaticDebugMsg( "SDR(%p) converting sdl record %s from version %d to %d (force:%d)", + this, fDescriptor->GetName(), fDescriptor->GetVersion(), other->GetVersion(), force ) ); + + // make other StateData to represent other descriptor, + // this will be the destination for the convert operation + plStateDataRecord otherStateData(other); + + // for each var in the other dtor, + // use the corresponding value in mine (type converted if necessary), + // or use other's default value. Put the final value in the otherData buffer + int i; + for(i=0;iGetVarDescriptor()->GetName(); + + // find corresponding var in my data + plSimpleStateVariable* myVar=FindVar(otherVarName); + IConvertVar(myVar /* fromVar */, otherVar /* toVar */, force); + } + + // for each nested stateDesc in the other guy, + // run the convert operation on it recursively. + for(i=0;iGetVarDescriptor()->GetName(); + + // find corresponding var in my data + plSDStateVariable* mySDVar=FindSDVar(otherSDVarName); + if (mySDVar) + { + mySDVar->ConvertTo( otherSDVar, force ); + otherSDVar->CopyFrom( mySDVar ); + } + } + + // adopt new descriptor and data + CopyFrom(otherStateData, plSDL::kWriteTimeStamps | plSDL::kKeepDirty); + + return true; +} + +// +// +// +void plStateDataRecord::DumpToObjectDebugger(const char* msg, bool dirtyOnly, int level) const +{ + plNetObjectDebuggerBase* dbg = plNetObjectDebuggerBase::GetInstance(); + if (!dbg) + return; + + std::string pad; + int i; + for(i=0;iLogMsg(xtl::format("%s", fAssocObject.IsValid() ? fAssocObject.GetObjectName() : " ").c_str()); + if (msg) + dbg->LogMsg(xtl::format("%s%s", pad.c_str(),msg).c_str()); + + dbg->LogMsg(xtl::format("%sSDR(%p), desc=%s, showDirty=%d, numVars=%d, vol=%d", + pad.c_str(), this, fDescriptor->GetName(), dirtyOnly, numVars+numSDVars, fFlags&kVolatile).c_str()); + + // dump simple vars + for(i=0;iIsDirty()) || (!dirtyOnly && fVarsList[i]->IsUsed()) ) + { + fVarsList[i]->DumpToObjectDebugger(dirtyOnly, level+1); + } + } + + // dump nested vars + for(i=0;iIsDirty()) || (!dirtyOnly && fSDVarsList[i]->IsUsed()) ) + { + fSDVarsList[i]->DumpToObjectDebugger(dirtyOnly, level+1); + } + } +} + +void plStateDataRecord::DumpToStream(hsStream* stream, const char* msg, bool dirtyOnly, int level) const +{ + std::string pad; + int i; + for(i=0;iWrite(logStr.length(), logStr.c_str()); + if (msg) + { + logStr = xtl::format("%s%s", pad.c_str(),msg); + stream->Write(logStr.length(), logStr.c_str()); + } + + logStr = xtl::format("%sSDR(%p), desc=%s, showDirty=%d, numVars=%d, vol=%d", pad.c_str(), this, fDescriptor->GetName(), dirtyOnly, numVars+numSDVars, fFlags&kVolatile); + stream->Write(logStr.length(), logStr.c_str()); + + // dump simple vars + for(i=0;iIsDirty()) || (!dirtyOnly && fVarsList[i]->IsUsed()) ) + { + fVarsList[i]->DumpToStream(stream, dirtyOnly, level+1); + } + } + + // dump nested vars + for(i=0;iIsDirty()) || (!dirtyOnly && fSDVarsList[i]->IsUsed()) ) + { + fSDVarsList[i]->DumpToStream(stream, dirtyOnly, level+1); + } + } + + logStr = '\n'; + stream->Write(logStr.length(), logStr.c_str()); +} + +void plStateDataRecord::SetFromDefaults(bool timeStampNow) +{ + int i; + // set simple vars + for(i=0;iSetFromDefaults(timeStampNow); + } + + // set nested vars + for(i=0;iSetFromDefaults(timeStampNow); + } +} + +void plStateDataRecord::TimeStampDirtyVars() +{ + int i; + // set simple vars + for(i=0;iIsDirty() ) + fVarsList[i]->TimeStamp(); + } + + // set nested vars + for(i=0;iIsDirty() ) + fSDVarsList[i]->TimeStamp(); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateDescriptor.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateDescriptor.cpp new file mode 100644 index 00000000..7d2ee12e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateDescriptor.cpp @@ -0,0 +1,131 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "plSDL.h" +#include "../pnNetCommon/plNetApp.h" + +const UInt8 plStateDescriptor::kVersion=1; // for Read/Write format + +///////////////////////////////////////////////////////////////////////////////// +// STATE DESC +///////////////////////////////////////////////////////////////////////////////// + +plStateDescriptor::~plStateDescriptor() +{ + IDeInit(); +} + +void plStateDescriptor::IDeInit() +{ + delete [] fName; + int i; + for(i=0;iGetName(), name)) + { + if (idx) + *idx = it-fVarsList.begin(); + return *it; + } + } + + return nil; +} + + +// +// Usage: The GameServer reads and write state descriptors along with each saved game +// +bool plStateDescriptor::Read(hsStream* s) +{ + UInt8 rwVersion; + s->ReadSwap(&rwVersion); + if (rwVersion != kVersion) + { + plNetApp::StaticWarningMsg("StateDescriptor Read/Write version mismatch, mine %d, read %d", kVersion, rwVersion); + return false; + } + + IDeInit(); + + delete [] fName; + fName = s->ReadSafeString(); + + UInt16 version=s->ReadSwap16(); + fVersion=version; + + UInt16 numVars=s->ReadSwap16(); + fVarsList.reserve(numVars); + + int i; + for(i=0;iReadByte(); + plVarDescriptor* var = nil; + if (SDVar) + var = TRACKED_NEW plSDVarDescriptor; + else + var = TRACKED_NEW plSimpleVarDescriptor; + if (var->Read(s)) + fVarsList.push_back(var); + else + return false; + } + return true; +} + +// +// Usage: The GameServer reads and write state descriptors alon with each saved game +// +void plStateDescriptor::Write(hsStream* s) const +{ + s->WriteSwap(kVersion); + + s->WriteSafeString(fName); + + UInt16 version=fVersion; + s->WriteSwap(version); + + UInt16 numVars=fVarsList.size(); + s->WriteSwap(numVars); + + VarsList::const_iterator it; + for(it=fVarsList.begin(); it!=fVarsList.end(); it++) + { + UInt8 SDVar = ((*it)->GetAsSDVarDescriptor() != nil); + s->WriteByte(SDVar); + (*it)->Write(s); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateVariable.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateVariable.cpp new file mode 100644 index 00000000..b4c78858 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plStateVariable.cpp @@ -0,0 +1,2762 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "hsStream.h" +#include "hsTimer.h" +#include "hsStlUtils.h" +#include "plSDL.h" + +#include "../pnProduct/pnProduct.h" +#include "../pnFactory/plCreatable.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plKeyImp.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnNetCommon/pnNetCommon.h" + +#include "../plResMgr/plResManager.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plUnifiedTime/plClientUnifiedTime.h" + + +#include "../plResMgr/plResManager.h" +#include "../plUnifiedTime/plClientUnifiedTime.h" + + +/***************************************************************************** +* +* VALIDATE_WITH_FALSE_RETURN +* Used in var getters and setters to validate incoming parameters and +* bail in a non-fatal way on error. +* +***/ + +#define VALIDATE_WITH_FALSE_RETURN(cond) \ + if (!(cond)) { \ + plSDLMgr::GetInstance()->GetNetApp()->DebugMsg("SDL var. Validation failed: "#cond); \ + ASSERT(!(cond)); \ + return false; \ + } // + + + + +// +// generic creatable which holds any kind of creatable blob +// +class plSDLCreatableStub : public plCreatable +{ +private: + UInt16 fClassIndex; +public: + void* fData; + int fDataLen; + + plSDLCreatableStub(UInt16 classIndex, int len) : fClassIndex(classIndex),fData(nil),fDataLen(len) {} + ~plSDLCreatableStub() { delete [] fData; } + + const char* ClassName() const { return "SDLCreatable"; } + UInt16 ClassIndex() const { return fClassIndex; } + + void Read(hsStream* s, hsResMgr* mgr) { delete [] fData; fData = TRACKED_NEW char[fDataLen]; s->Read(fDataLen, fData); } + void Write(hsStream* s, hsResMgr* mgr) { s->Write(fDataLen, fData); } +}; + +///////////////////////////////////////////////////// +// plStateVarNotificationInfo +///////////////////////////////////////////////////// + +void plStateVarNotificationInfo::Read(hsStream* s, UInt32 readOptions) +{ + UInt8 saveFlags=s->ReadByte(); // unused + char* hint=s->ReadSafeString(); + if (hint && !(readOptions & plSDL::kSkipNotificationInfo)) + fHintString = (const char*)hint; + // we're done with it... + delete [] hint; +} + +void plStateVarNotificationInfo::Write(hsStream* s, UInt32 writeOptions) const +{ + UInt8 saveFlags=0; // unused + s->WriteSwap(saveFlags); + s->WriteSafeString(fHintString.c_str()); +} + +///////////////////////////////////////////////////// +// plStateVariable +///////////////////////////////////////////////////// +bool plStateVariable::ReadData(hsStream* s, float timeConvert, UInt32 readOptions) +{ + UInt8 saveFlags; + s->ReadSwap(&saveFlags); + if (saveFlags & plSDL::kHasNotificationInfo) + { + GetNotificationInfo().Read(s, readOptions); + } + return true; +} + +bool plStateVariable::WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const +{ + bool writeNotificationInfo = ((writeOptions & plSDL::kSkipNotificationInfo)==0); + + UInt8 saveFlags=0; + if (writeNotificationInfo) + saveFlags |= plSDL::kHasNotificationInfo; + + s->WriteSwap(saveFlags); + if (writeNotificationInfo) + { + GetNotificationInfo().Write(s, writeOptions); + } + return true; +} + +///////////////////////////////////////////////////// +// plSimpleStateVariable +///////////////////////////////////////////////////// + +void plSimpleStateVariable::IInit() +{ + SetDirty(false); + SetUsed(false); + fBy=nil; + fS=nil; + fI=nil; + fF=nil; + fD=nil; + fB=nil; + fU=nil; + fS32=nil; + fC=nil; + fT=nil; + fTimeStamp.ToEpoch(); +} + +// +// delete memory +// + +#define DEALLOC(type, var) \ + case type: \ + delete [] var; \ + break; + +void plSimpleStateVariable::IDeAlloc() +{ + int cnt = fVar.GetAtomicCount()*fVar.GetCount(); + int type = fVar.GetAtomicType(); + switch (type) + { + DEALLOC (plVarDescriptor::kInt, fI) + DEALLOC (plVarDescriptor::kAgeTimeOfDay, fF) + DEALLOC (plVarDescriptor::kShort, fS) + DEALLOC (plVarDescriptor::kByte, fBy) + DEALLOC (plVarDescriptor::kFloat, fF) + DEALLOC (plVarDescriptor::kTime, fT) + DEALLOC (plVarDescriptor::kDouble, fD) + DEALLOC (plVarDescriptor::kBool, fB) + DEALLOC (plVarDescriptor::kKey, fU) + DEALLOC (plVarDescriptor::kString32, fS32) + case plVarDescriptor::kCreatable: + { + if(fC) + { + int i; + // delete each creatable + for(i=0;i1) + str = str + "("; + + plVarDescriptor::Type type=fVar.GetAtomicType(); + switch(type) + { + case plVarDescriptor::kAgeTimeOfDay: + case plVarDescriptor::kTime: + case plVarDescriptor::kDouble: + case plVarDescriptor::kFloat: + case plVarDescriptor::kInt: + case plVarDescriptor::kByte: + case plVarDescriptor::kShort: + { + // handles value in the form "(i,j,k)" for vectors + int i=idx*fVar.GetAtomicCount(); + for(j=0;jGetType(); + + int cnt = toVar->GetCount() ? toVar->GetCount() : fVar.GetCount(); + + if (cnt > fVar.GetCount()) { + #if BUILD_TYPE == BUILD_TYPE_DEV + FATAL("SDL Convert: array size increased, conversion loses data"); + #endif + // Reallocate new memory (destroys existing variable state) + Alloc(cnt); + // match types now + fVar.SetCount(cnt); + fVar.SetType(toVar->GetType()); + fVar.SetAtomicType(toVar->GetAtomicType()); + return true; + } + + fVar.SetCount(cnt); // convert count + + // types are already the same, done. + if (fVar.GetType()==newType ) + return true; + + hsLogEntry( plNetApp::StaticDebugMsg( "SSV(%p) converting %s from %s to %s", + this, fVar.GetName(), fVar.GetTypeString(), toVar->GetTypeString() ) ); + + switch(fVar.GetType()) // original type + { + // FROM RGB + case plVarDescriptor::kRGB: + if (!IConvertFromRGB(newType)) + return false; + break; + + // FROM RGBA + case plVarDescriptor::kRGBA: + if (!IConvertFromRGBA(newType)) + return false; + break; + + // FROM RGB8 + case plVarDescriptor::kRGB8: + if (!IConvertFromRGB8(newType)) + return false; + break; + + // FROM RGBA8 + case plVarDescriptor::kRGBA8: + if (!IConvertFromRGBA8(newType)) + return false; + break; + + // FROM INT + case plVarDescriptor::kInt: + if (!IConvertFromInt(newType)) + return false; + break; + + // FROM SHORT + case plVarDescriptor::kShort: + if (!IConvertFromShort(newType)) + return false; + break; + + // FROM Byte + case plVarDescriptor::kByte: + if (!IConvertFromByte(newType)) + return false; + break; + + // FROM FLOAT + case plVarDescriptor::kFloat: + if (!IConvertFromFloat(newType)) + return false; + break; + + // FROM DOUBLE + case plVarDescriptor::kDouble: + if (!IConvertFromDouble(newType)) + return false; + break; + + // FROM BOOL + case plVarDescriptor::kBool: + if (!IConvertFromBool(newType)) + return false; + break; + + // FROM STRING32 + case plVarDescriptor::kString32: + if (!IConvertFromString(newType)) + return false; + break; + + default: + return false; // err + } + + // match types now + fVar.SetType(toVar->GetType()); + fVar.SetAtomicType(toVar->GetAtomicType()); + + return true; // ok +} + +///////////////////////////////////////////////////////////// +// SETTERS +///////////////////////////////////////////////////////////// + +bool plSimpleStateVariable::Set(float v, int idx) +{ + VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); + + if (fVar.GetType()==plVarDescriptor::kAgeTimeOfDay) + { + hsAssert(false, "AgeTime variables are read-only, can't set"); + } + else + if (fVar.GetType()==plVarDescriptor::kFloat) + { + fF[idx]=v; + IVarSet(); + return true; + } + hsAssert(false, "passing wrong value type to SDL variable"); + return false; +} + +bool plSimpleStateVariable::Set(double v, int idx) +{ + VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); + + if (fVar.GetType()==plVarDescriptor::kDouble) + { + fD[idx]=v; + IVarSet(); + return true; + } + + if (fVar.GetType()==plVarDescriptor::kTime) + { // convert from, + fT[idx].SetFromGameTime(v, hsTimer::GetSysSeconds()); + IVarSet(); + return true; + } + + hsAssert(false, "passing wrong value type to SDL variable"); + return false; +} + +// floatvector +bool plSimpleStateVariable::Set(float* v, int idx) +{ + VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); + + if (fVar.GetType()==plVarDescriptor::kAgeTimeOfDay) + { + hsAssert(false, "AgeTime variables are read-only, can't set"); + } + else + if (fVar.GetAtomicType()==plVarDescriptor::kFloat) + { + int i; + int cnt=fVar.GetAtomicCount()*idx; + for(i=0;iGetUoid(); + } + else + { + fU[idx] = plUoid(); + } + IVarSet(); + return true; + } + hsAssert(false, "passing wrong value type to SDL variable"); + return false; +} + +bool plSimpleStateVariable::Set(plCreatable* v, int idx) +{ + VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); + + if (fVar.GetType()==plVarDescriptor::kCreatable) + { + // copy creatable via stream + hsRAMStream stream; + if(v) + { + hsgResMgr::ResMgr()->WriteCreatable(&stream, v); + stream.Rewind(); + } + plCreatable* copy = v ? hsgResMgr::ResMgr()->ReadCreatable(&stream): nil; + hsAssert(!v || copy, "failed to create creatable copy"); + fC[idx]=copy; + IVarSet(); + return true; + } + hsAssert(false, "passing wrong value type to SDL variable"); + return false; +} + +///////////////////////////////////////////////////////////// +// GETTERS +///////////////////////////////////////////////////////////// + +bool plSimpleStateVariable::Get(int* value, int idx) const +{ + VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); + + if (fVar.GetAtomicType()==plVarDescriptor::kInt) + { + int i; + int cnt=fVar.GetAtomicCount()*idx; + for(i=0;iGetCurrentAgeTimeOfDayPercent(); + value[i]=fF[cnt+i]; + } + return true; + } + + if (fVar.GetAtomicType()==plVarDescriptor::kFloat) + { + int i; + int cnt=fVar.GetAtomicCount()*idx; + for(i=0;iFindKey(fU[idx]); + if (*value) + { + const plUoid& newUoid = (*value)->GetUoid(); + if (stricmp(newUoid.GetObjectName(), fU[idx].GetObjectName()) != 0) + { + // uoid names don't match... chances are the key changed in the local data after the key was written to the sdl + // do a search by name, which takes longer, to get the correct key + std::vector foundKeys; + plKeyFinder::Instance().ReallyStupidSubstringSearch(fU[idx].GetObjectName(), fU[idx].GetClassType(), foundKeys, fU[idx].GetLocation()); + // not really sure what we can do if it finds MORE then one (they are supposed to be unique names), so just grab the + // first one and return it + if (foundKeys.size() >= 1) + *value = foundKeys[0]; + else + *value = nil; + } + } + } else { + *value = nil; + } + return true; + } + hsAssert(false, "passing wrong value type to SDL variable"); + return false; +} + +bool plSimpleStateVariable::Get(char value[], int idx) const +{ + VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); + + if (fVar.GetType()==plVarDescriptor::kString32) + { + hsStrcpy(value, fS32[idx]); + return true; + } + hsAssert(false, "passing wrong value type to SDL variable"); + return false; +} + +bool plSimpleStateVariable::Get(plCreatable** value, int idx) const +{ + VALIDATE_WITH_FALSE_RETURN(idx < fVar.GetCount()); + + if (fVar.GetAtomicType()==plVarDescriptor::kCreatable) + { + *value = nil; + plCreatable* v = fC[idx]; + if (v) + { + *value = plFactory::Create(v->ClassIndex()); + hsAssert(*value, "failed to create creatable copy"); + hsRAMStream stream; + v->Write(&stream, hsgResMgr::ResMgr()); + stream.Rewind(); + (*value)->Read(&stream, hsgResMgr::ResMgr()); + } + return true; + } + hsAssert(false, "passing wrong value type to SDL variable"); + return false; +} + +///////////////////////////////////////////////////////////// + +const char* plSimpleStateVariable::GetKeyName(int idx) const +{ + if (fVar.GetAtomicType()==plVarDescriptor::kKey) + { + if(!(fU[idx] == plUoid())) // compare to default "nil uoid" + { + return fU[idx].GetObjectName(); + } + } + hsAssert(false, "passing wrong value type to SDL variable"); + return "(nil)"; +} + +#pragma optimize( "g", off ) // disable float optimizations +bool plSimpleStateVariable::IWriteData(hsStream* s, float timeConvert, int idx, UInt32 writeOptions) const +{ +#ifdef HS_DEBUGGING + if (!IsUsed()) + { + // hsAssert(false, "plSimpleStateVariable::WriteData doesn't contain data?"); + plNetApp::StaticWarningMsg("plSimpleStateVariable::WriteData Var %s doesn't contain data?", + GetName()); + } +#endif + + int j=idx*fVar.GetAtomicCount(); + int i; + switch(fVar.GetAtomicType()) + { + case plVarDescriptor::kAgeTimeOfDay: + // don't need to write out ageTime, since it's computed on the fly when Get is called + break; + case plVarDescriptor::kInt: + for(i=0;iWriteSwap32(fI[j+i]); + break; + case plVarDescriptor::kShort: + for(i=0;iWriteSwap16(fS[j+i]); + break; + case plVarDescriptor::kByte: + for(i=0;iWriteByte(fBy[j+i]); + break; + case plVarDescriptor::kFloat: + for(i=0;iWriteSwapScalar(fF[j+i]); + break; + case plVarDescriptor::kTime: + for(i=0;iWriteSwapDouble(fD[j+i]); + break; + case plVarDescriptor::kBool: + for(i=0;iWritebool(fB[j+i]); + break; + case plVarDescriptor::kKey: + for(i=0;iWrite(32, fS32[j+i]); + break; + case plVarDescriptor::kCreatable: + { + hsAssert(fVar.GetAtomicCount()==1, "invalid atomic count"); + plCreatable* cre = fC[j]; + s->WriteSwap16(cre ? cre->ClassIndex() : 0x8000); // creatable class index + if (cre) + { + hsRAMStream ramStream; + cre->Write(&ramStream, hsgResMgr::ResMgr()); + s->WriteSwap32(ramStream.GetEOF()); // write length + cre->Write(s, hsgResMgr::ResMgr()); // write data + } + } + break; + } + return true; +} + +bool plSimpleStateVariable::IReadData(hsStream* s, float timeConvert, int idx, UInt32 readOptions) +{ + int j=idx*fVar.GetAtomicCount(); + int i; + switch(fVar.GetAtomicType()) + { + case plVarDescriptor::kAgeTimeOfDay: + // don't need to read in ageTime, since it's computed on the fly when Get is called + break; + case plVarDescriptor::kInt: + for(i=0;iReadSwap32(); + break; + case plVarDescriptor::kShort: + for(i=0;iReadSwap16(); + break; + case plVarDescriptor::kByte: + for(i=0;iReadByte(); + break; + case plVarDescriptor::kFloat: + for(i=0;iReadSwapScalar(); + break; + case plVarDescriptor::kTime: + for(i=0;i=0, "negative unified time"); + fT[j+i].SetSecsDouble(newUt); + } + } + break; + case plVarDescriptor::kDouble: + for(i=0;iReadSwapDouble(); + break; + case plVarDescriptor::kBool: + for(i=0;iReadbool(); + break; + case plVarDescriptor::kKey: + for(i=0;iRead(32, fS32[j+i]); + break; + case plVarDescriptor::kCreatable: + { + hsAssert(fVar.GetAtomicCount()==1, "invalid atomic count"); + UInt16 hClass = s->ReadSwap16(); // class index + if (hClass != 0x8000) + { + UInt32 len = s->ReadSwap32(); // length + if (plFactory::CanCreate(hClass)) + { + delete fC[j]; + fC[j] = plFactory::Create(hClass); + } + else + { + plSDLCreatableStub* stub = TRACKED_NEW plSDLCreatableStub(hClass, len); + fC[j] = stub; + } + fC[j]->Read(s, hsgResMgr::ResMgr()); // data + } + } + break; + default: + hsAssert(false, "invalid atomic type"); + return false; + } + + return true; +} +#pragma optimize( "", on ) // restore optimizations to their defaults + +bool plSimpleStateVariable::WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const +{ +#ifdef HS_DEBUGGING + if (!IsUsed()) + { + // hsAssert(false, "plSimpleStateVariable::WriteData Var doesn't contain data?"); + plNetApp::StaticWarningMsg("plSimpleStateVariable::WriteData Var %s doesn't contain data?", + GetName()); + } +#endif + + // write base class data + plStateVariable::WriteData(s, timeConvert, writeOptions); + + // check if the same as default + bool sameAsDefaults=false; + if (!GetVarDescriptor()->IsVariableLength()) + { + plSimpleStateVariable def; + def.fVar.CopyFrom(&fVar); // copy descriptor + def.Alloc(); // and rest + def.SetFromDefaults(false /* timeStamp */); // may do nothing if nor default + sameAsDefaults = (def == *this); + } + + bool writeTimeStamps = (writeOptions & plSDL::kWriteTimeStamps)!=0; + bool writeDirtyFlags = (writeOptions & plSDL::kDontWriteDirtyFlag)==0; + bool forceDirtyFlags = (writeOptions & plSDL::kMakeDirty)!=0; + bool wantTimeStamp = (writeOptions & plSDL::kTimeStampOnRead)!=0; + bool needTimeStamp = (writeOptions & plSDL::kTimeStampOnWrite)!=0; + forceDirtyFlags = forceDirtyFlags || (!sameAsDefaults && (writeOptions & plSDL::kDirtyNonDefaults)!=0); + + // write save flags + UInt8 saveFlags = 0; + saveFlags |= writeTimeStamps ? plSDL::kHasTimeStamp : 0; + saveFlags |= forceDirtyFlags || (writeDirtyFlags && IsDirty()) ? plSDL::kHasDirtyFlag : 0; + saveFlags |= wantTimeStamp ? plSDL::kWantTimeStamp : 0; + saveFlags |= needTimeStamp ? plSDL::kHasTimeStamp : 0; + + if (sameAsDefaults) + saveFlags |= plSDL::kSameAsDefault; + s->WriteSwap(saveFlags); + + if (needTimeStamp) { + // timestamp on write + fTimeStamp.ToCurrentTime(); + fTimeStamp.Write(s); + } + else if (writeTimeStamps) { + // write time stamps + fTimeStamp.Write(s); + } + + // write var data + if (!sameAsDefaults) + { + // list size + if (GetVarDescriptor()->IsVariableLength()) + s->WriteSwap32(GetVarDescriptor()->GetCount()); // have to write out as long since we don't know how big the list is + + // list + int i; + for(i=0;iReadSwap(&saveFlags); + + bool isDirty = ( saveFlags & plSDL::kHasDirtyFlag )!=0; + bool setDirty = ( isDirty && ( readOptions & plSDL::kKeepDirty ) ) || ( readOptions & plSDL::kMakeDirty ); + bool wantTimestamp = isDirty && + plSDLMgr::GetInstance()->AllowTimeStamping() && + ( ( saveFlags & plSDL::kWantTimeStamp ) || + ( readOptions & plSDL::kTimeStampOnRead ) ); + + if (saveFlags & plSDL::kHasTimeStamp) + ut.Read(s); + else if ( wantTimestamp ) + ut.ToCurrentTime(); + + if (!(saveFlags & plSDL::kSameAsDefault)) + { + setDirty = setDirty || ( readOptions & plSDL::kDirtyNonDefaults )!=0; + + // read list size + if (GetVarDescriptor()->IsVariableLength()) + { + UInt32 cnt; + s->ReadSwap(&cnt); // have to read as long since we don't know how big the list is + + if (cnt>=0 && cnt ut) + return true; + + if ( (saveFlags & plSDL::kHasTimeStamp) || (readOptions & plSDL::kTimeStampOnRead) ) + TimeStamp(ut); + + // read list + if (!(saveFlags & plSDL::kSameAsDefault)) + { + int i; + for(i=0;iWriteData(&stream, 0, writeOptions); + stream.Rewind(); + ReadData(&stream, 0, writeOptions); +} + +// +// send notification msg if necessary, called internally +// + +#define NOTIFY_CHECK(type, var) \ +case type: \ + for(i=0;ivar[i])>d) \ + { \ + notify=true; \ + break; \ + } \ + break; + +void plSimpleStateVariable::NotifyStateChange(const plSimpleStateVariable* other, const char* sdlName) +{ + if (fChangeNotifiers.size()==0) + return; + + bool different=!(*this == *other); + bool notify=false; + int numNotifiers=0; + if (different) + { + StateChangeNotifiers::iterator it=fChangeNotifiers.begin(); + for( ; it!=fChangeNotifiers.end(); it++) + { + float d=(*it).fDelta; + if (d==0 && different) // delta of 0 means notify on any change + { + notify=true; + } + else + { + int i; + int cnt = fVar.GetAtomicCount()*fVar.GetCount(); + switch(fVar.GetAtomicType()) + { + NOTIFY_CHECK(plVarDescriptor::kInt, fI) + NOTIFY_CHECK(plVarDescriptor::kShort, fS) + NOTIFY_CHECK(plVarDescriptor::kByte, fBy) + NOTIFY_CHECK(plVarDescriptor::kFloat, fF) + NOTIFY_CHECK(plVarDescriptor::kDouble, fD) + } + } + if (notify) + { + numNotifiers += (*it).fKeys.size(); + (*it).SendNotificationMsg(other /* src */, this /* dst */, sdlName); + } + } + } + + if (plNetObjectDebuggerBase::GetInstance() && plNetObjectDebuggerBase::GetInstance()->GetDebugging()) + { + plNetObjectDebuggerBase::GetInstance()->LogMsg( + xtl::format("Var %s did %s send notification difference. Has %d notifiers with %d recipients.", + GetName(), !notify ? "NOT" : "", fChangeNotifiers.size(), numNotifiers).c_str()); + } + +} + +// +// Checks to see if data contents are the same on two matching vars. +// + +#define EQ_CHECK(type, var) \ +case type: \ + for(i=0;iGetType(), "type mismatch in equality check"); + hsAssert(fVar.GetAtomicCount() == other.GetVarDescriptor()->GetAsSimpleVarDescriptor()->GetAtomicCount(), + "atomic cnt mismatch in equality check"); + + if (GetCount() != other.GetCount()) + return false; + + int i; + int cnt = fVar.GetAtomicCount()*fVar.GetCount(); + switch(fVar.GetAtomicType()) + { + EQ_CHECK(plVarDescriptor::kAgeTimeOfDay, fF) + EQ_CHECK(plVarDescriptor::kInt, fI) + EQ_CHECK(plVarDescriptor::kFloat, fF) + EQ_CHECK(plVarDescriptor::kTime, fT) + EQ_CHECK(plVarDescriptor::kDouble, fD) + EQ_CHECK(plVarDescriptor::kBool, fB) + EQ_CHECK(plVarDescriptor::kKey, fU) + EQ_CHECK(plVarDescriptor::kCreatable, fC) + EQ_CHECK(plVarDescriptor::kShort, fS) + EQ_CHECK(plVarDescriptor::kByte, fBy) + case plVarDescriptor::kString32: + for(i=0;i1) + { + dbg->LogMsg(logMsg.c_str()); // it's going to be a long msg, so print it on its own line + logMsg = ""; + } + + pad += "\t"; + for(i=0;iLogMsg(logMsg.c_str()); + logMsg = ""; + } +} + +void plSimpleStateVariable::DumpToStream(hsStream* stream, bool dirtyOnly, int level) const +{ + std::string pad; + int i; + for(i=0;i1) + { + stream->WriteString(logMsg.c_str()); // it's going to be a long msg, so print it on its own line + logMsg = ""; + } + + pad += "\t"; + for(i=0;iWriteString(logMsg.c_str()); + logMsg = ""; + } +} + +// +// set var to its defalt value +// +void plSimpleStateVariable::SetFromDefaults(bool timeStampNow) +{ + int i; + for(i=0;iGetDefault(), i, timeStampNow); +} + +/////////////////////////////////////////////////////////////////////////////// +// plSDStateVariable +/////////////////////////////////////////////////////////////////////////////// + +plSDStateVariable::plSDStateVariable(plSDVarDescriptor* sdvd) : fVarDescriptor(nil) +{ + Alloc(sdvd); +} + +plSDStateVariable::~plSDStateVariable() +{ + IDeInit(); +} + +// +// resize the array of state data records +// +void plSDStateVariable::Resize(int cnt) +{ + int origCnt=GetCount(); + // when sending, this is always freshly allocated. if you then set it to zero, + // the change won't be sent, even though the version on the server might not be zero + // for now, we're just not going to do this optimization + // we could, however, change it to (origCnt==cnt==0), because for sizes other + // than zero the bug won't happen +// if (origCnt==cnt) +// return; // no work to do + + // shrinking + if (cntorigCnt) + { + int i; + for(i=origCnt;iGetStateDescriptor()); + } + + SetDirty(true); + SetUsed(true); +} + +// +// create/allocate data records based on the given SDVarDesc +// +void plSDStateVariable::Alloc(plSDVarDescriptor* sdvd, int listSize) +{ + if (sdvd==fVarDescriptor) + { + // trick to not have to delete and recreate fVarDescriptor + fVarDescriptor=nil; + IDeInit(); + fVarDescriptor=sdvd; + } + else + IDeInit(); // will delete fVarDescriptor + + if (sdvd) + { + if (fVarDescriptor==nil) + { + fVarDescriptor = TRACKED_NEW plSDVarDescriptor; + fVarDescriptor->CopyFrom(sdvd); + } + + int cnt = listSize==-1 ? sdvd->GetCount() : listSize; + fDataRecList.resize(cnt); + int j; + for (j=0;jGetStateDescriptor()), j); + } +} + +// +// help alloc fxn +// +void plSDStateVariable::Alloc(int listSize) +{ + Alloc(fVarDescriptor, listSize); +} + +// +// delete all records +// +void plSDStateVariable::IDeInit() +{ + DataRecList::iterator it; + for (it=fDataRecList.begin(); it != fDataRecList.end(); it++) + delete *it; + fDataRecList.clear(); + delete fVarDescriptor; + fVarDescriptor=nil; +} + +// +// Make 'this' into a copy of 'other'. +// +void plSDStateVariable::CopyFrom(plSDStateVariable* other, UInt32 writeOptions/*=0*/) +{ + // IDeInit(); + Alloc(other->GetSDVarDescriptor(), other->GetCount()); + int i; + for(i=0; iGetCount(); i++) + fDataRecList[i]->CopyFrom(*other->GetStateDataRecord(i),writeOptions); +} + +// +// Find the data items which are dirty in 'other' and +// copy them to my corresponding item. +// Requires that records have the same descriptor. +// +void plSDStateVariable::UpdateFrom(plSDStateVariable* other, UInt32 writeOptions/*=0*/) +{ + hsAssert(!stricmp(other->GetSDVarDescriptor()->GetName(), fVarDescriptor->GetName()), + xtl::format("var descriptor mismatch in UpdateFrom, name %s,%s ver %d,%d", + GetName(), other->GetName()).c_str()); + Resize(other->GetCount()); // make sure sizes match + + bool dirtyOnly = (writeOptions & plSDL::kDirtyOnly); + + int i; + for(i=0; iGetCount(); i++) + { + if ( (dirtyOnly && other->GetStateDataRecord(i)->IsDirty()) || + (!dirtyOnly &&other->GetStateDataRecord(i)->IsUsed()) ) + fDataRecList[i]->UpdateFrom(*other->GetStateDataRecord(i), writeOptions); + } +} + +// +// Convert all my stateDataRecords to the type defined by the 'other var' +// +void plSDStateVariable::ConvertTo(plSDStateVariable* otherSDVar, bool force ) +{ + plStateDescriptor* otherSD=otherSDVar->GetSDVarDescriptor()->GetStateDescriptor(); + + hsLogEntry( plNetApp::StaticDebugMsg( "SDSV(%p) converting %s from %s to %s (force:%d)", + this, fVarDescriptor->GetName(), fVarDescriptor->GetTypeString(), + otherSDVar->GetSDVarDescriptor()->GetTypeString(), force ) ); + + int j; + for(j=0;jConvertTo( otherSD, force ); + } +} + +bool plSDStateVariable::IsDirty() const +{ + if (plStateVariable::IsDirty()) + return true; + + int j; + for(j=0;jIsDirty()) + return true; + return false; +} + +int plSDStateVariable::GetDirtyCount() const +{ + int cnt=0; + int j; + for(j=0;jIsDirty()) + cnt++; + return cnt; +} + +bool plSDStateVariable::IsUsed() const +{ + if (plStateVariable::IsUsed()) + return true; + + int j; + for(j=0;jIsUsed()) + return true; + return false; +} + +int plSDStateVariable::GetUsedCount() const +{ + int cnt=0; + int j; + for(j=0;jIsUsed()) + cnt++; + return cnt; +} + +void plSDStateVariable::GetUsedDataRecords(ConstDataRecList* recList) const +{ + recList->clear(); + int j; + for(j=0;jIsUsed()) + recList->push_back(GetStateDataRecord(j)); +} + +void plSDStateVariable::GetDirtyDataRecords(ConstDataRecList* recList) const +{ + recList->clear(); + int j; + for(j=0;jIsDirty()) + recList->push_back(GetStateDataRecord(j)); +} + +// +// read all SDVars +// +bool plSDStateVariable::ReadData(hsStream* s, float timeConvert, UInt32 readOptions) +{ + plStateVariable::ReadData(s, timeConvert, readOptions); + + UInt8 saveFlags; + s->ReadSwap(&saveFlags); // unused + + // read total list size + if (GetVarDescriptor()->IsVariableLength()) + { + UInt32 total; + s->ReadSwap(&total); + Resize(total); + } + + // read dirty list size + int cnt; + plSDL::VariableLengthRead(s, + GetVarDescriptor()->IsVariableLength() ? 0xffffffff : GetVarDescriptor()->GetCount(), &cnt); + + // if we are reading the entire list in, then we don't need to read each index + bool all = (cnt==fDataRecList.size()); + + // read list + int i; + for(i=0;iIsVariableLength() ? 0xffffffff : GetVarDescriptor()->GetCount(), &idx); + else + idx=i; + + if (idxRead(s, timeConvert, readOptions); + else + return false; + } + return true; +} + +// +// write all SDVars +// +bool plSDStateVariable::WriteData(hsStream* s, float timeConvert, UInt32 writeOptions) const +{ + plStateVariable::WriteData(s, timeConvert, writeOptions); + + UInt8 saveFlags=0; // unused + s->WriteSwap(saveFlags); + + // write total list size + UInt32 total=GetCount(); + if (GetVarDescriptor()->IsVariableLength()) + s->WriteSwap(total); + + // write dirty list size + bool dirtyOnly = (writeOptions & plSDL::kDirtyOnly) != 0; + int writeCnt = dirtyOnly ? GetDirtyCount() : GetUsedCount(); + plSDL::VariableLengthWrite(s, + GetVarDescriptor()->IsVariableLength() ? 0xffffffff : GetVarDescriptor()->GetCount(), writeCnt); + + // if we are writing the entire list in, then we don't need to read each index + bool all = (writeCnt==fDataRecList.size()); + + // write list + int i, written=0; + for(i=0;iIsDirty()) || + (!dirtyOnly && fDataRecList[i]->IsUsed()) ) + { + if (!all) + plSDL::VariableLengthWrite(s, + GetVarDescriptor()->IsVariableLength() ? 0xffffffff : GetVarDescriptor()->GetCount(), i); // idx + fDataRecList[i]->Write(s, timeConvert, dirtyOnly); // item + written++; + } + } + hsAssert(writeCnt==written, "write mismatch"); + return true; +} + +// +// +// +void plSDStateVariable::DumpToObjectDebugger(bool dirtyOnly, int level) const +{ + plNetObjectDebuggerBase* dbg = plNetObjectDebuggerBase::GetInstance(); + if (!dbg) + return; + + std::string pad; + int i; + for(i=0;iLogMsg(xtl::format( "%sSDVar, name:%s dirtyOnly:%d count:%d", + pad.c_str(), GetName(), dirtyOnly, cnt).c_str()); + + for(i=0;iIsDirty()) || + (!dirtyOnly && fDataRecList[i]->IsUsed()) ) + { + fDataRecList[i]->DumpToObjectDebugger(nil, dirtyOnly, level+1); + } + } +} + +void plSDStateVariable::DumpToStream(hsStream* stream, bool dirtyOnly, int level) const +{ + std::string pad; + int i; + for(i=0;iWriteString(xtl::format( "%sSDVar, name:%s dirtyOnly:%d count:%d", + pad.c_str(), GetName(), dirtyOnly, cnt).c_str()); + + for(i=0;iIsDirty()) || + (!dirtyOnly && fDataRecList[i]->IsUsed()) ) + { + fDataRecList[i]->DumpToStream(stream, nil, dirtyOnly, level+1); + } + } +} + +// +// Checks to see if data contents are the same on two matching vars. +// +bool plSDStateVariable::operator==(const plSDStateVariable &other) const +{ + hsAssert(GetSDVarDescriptor()->GetStateDescriptor() == other.GetSDVarDescriptor()->GetStateDescriptor(), + "SD var descriptor mismatch in equality check"); + + if (GetCount() != other.GetCount()) + return false; // different list sizes + + int i; + for(i=0;iSetFromDefaults(timeStampNow); +} + +void plSDStateVariable::TimeStamp( const plUnifiedTime & ut/*=plUnifiedTime::GetCurrentTime()*/ ) +{ + hsAssert( false, "not impl" ); +} + +void plSDStateVariable::FlagNewerState(const plSDStateVariable& other, bool respectAlwaysNew) +{ + int i; + for(i=0;iFlagNewerState(*other.GetStateDataRecord(i), respectAlwaysNew); +} + +void plSDStateVariable::FlagAlwaysNewState() +{ + int i; + for(i=0;iFlagAlwaysNewState(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plVarDescriptor.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plVarDescriptor.cpp new file mode 100644 index 00000000..28485d68 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDL/plVarDescriptor.cpp @@ -0,0 +1,404 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsStream.h" +#include "plSDL.h" +#include "hsStlUtils.h" + +#include "../pnKeyedObject/plUoid.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnMessage/plMessage.h" + +#include "../plUnifiedTime/plUnifiedTime.h" + +const UInt8 plVarDescriptor::kVersion=3; // for Read/Write format + +///////////////////////////////////////////////////////////////////////////////// +// State Var +///////////////////////////////////////////////////////////////////////////////// + +plVarDescriptor::plVarDescriptor() : + fName(nil), + fCount(1), + fType(kNone), + fTypeString(nil), + fDefault(nil), + fFlags(0) +{ + +} + +plVarDescriptor::~plVarDescriptor() +{ + delete [] fName; + delete [] fDefault; + delete [] fTypeString; +} + +// +// Set type from a string. Return false on err. +// +bool plVarDescriptor::SetType(const char* type) +{ + if (!type) + return false; + + if (!stricmp(type, "vector3")) + fType=kVector3; + else + if (!stricmp(type, "point3")) + fType=kPoint3; + else + if (!stricmp(type, "rgb")) + fType=kRGB; + else + if (!stricmp(type, "rgba")) + fType=kRGBA; + else + if (!stricmp(type, "rgb8")) + fType=kRGB8; + else + if (!stricmp(type, "rgba8")) + fType=kRGBA8; + else + if (!strnicmp(type, "quat",4)) + fType=kQuaternion; + else + if (!stricmp(type, "rgba")) + fType=kRGBA; + else + if (!stricmp(type, "int")) + fType=kInt; + else + if (!stricmp(type, "byte")) + fType=kByte; + else + if (!stricmp(type, "short")) + fType=kShort; + else + if (!stricmp(type, "float")) + fType=kFloat; + else + if (!stricmp(type, "double")) + fType=kDouble; + else + if (!stricmp(type, "time")) + fType=kTime; + else + if (!stricmp(type, "ageTimeOfDay")) + fType=kAgeTimeOfDay; + else + if (!stricmp(type, "bool")) + fType=kBool; + else + if (!stricmp(type, "string32")) + fType=kString32; + else + if (!stricmp(type, "plKey")) + fType=kKey; + else + if (!stricmp(type, "message") || !stricmp(type, "creatable") ) + fType=kCreatable; + else + if (*type=='$') + fType=kStateDescriptor; + else + return false; // err + + delete [] fTypeString; + fTypeString = hsStrcpy(type); + + return true; // ok +} + +void plVarDescriptor::CopyFrom(const plVarDescriptor* other) +{ + SetName(other->GetName()); + SetDefault(other->GetDefault()); + SetCount(other->GetCount()); + SetDisplayOptions(other->GetDisplayOptions()); + + delete [] fTypeString; + fTypeString=hsStrcpy(other->GetTypeString()); + + fType = other->GetType(); + fFlags = other->fFlags; +} + +// +// Var descriptors are read and written by state descriptors +// +bool plVarDescriptor::Read(hsStream* s) +{ + UInt8 version; + s->ReadSwap(&version); + if (version != kVersion) + { + if (plSDLMgr::GetInstance()->GetNetApp()) + plSDLMgr::GetInstance()->GetNetApp()->WarningMsg("SDL VarDescriptor version mismatch, read %d, should be %d - ignoring", + version, kVersion); + return false; + } + + delete [] fName; + fName=s->ReadSafeString(); + + plMsgStdStringHelper::Peek(fDisplayOptions, s); + + fCount=s->ReadSwap32(); + + fType=(Type)s->ReadByte(); + + delete [] fDefault; + fDefault = s->ReadSafeString(); + + fFlags = s->ReadSwap32(); + return true; +} + +// +// Var descriptors are read and written by state descriptors +// +void plVarDescriptor::Write(hsStream* s) const +{ + s->WriteSwap(kVersion); + s->WriteSafeString(fName); + plMsgStdStringHelper::Poke(fDisplayOptions, s); + s->WriteSwap32(fCount); + s->WriteByte((UInt8)fType); + s->WriteSafeString(fDefault); + s->WriteSwap32(fFlags); +} + +///////////////////////////////////////////////////////////////////////////////// +// plSimpleVarDescriptor +///////////////////////////////////////////////////////////////////////////////// + +plSimpleVarDescriptor::plSimpleVarDescriptor() : + fAtomicType(kNone), + fAtomicCount(1) +{ + +} + +// size in bytes +int plSimpleVarDescriptor::GetAtomicSize() const +{ + switch(fAtomicType) + { + case kInt: + return sizeof(int)*GetAtomicCount(); + case kByte: + return sizeof(byte)*GetAtomicCount(); + case kShort: + return sizeof(short)*GetAtomicCount(); + case kAgeTimeOfDay: + case kFloat: + return sizeof(float)*GetAtomicCount(); + case kTime: + return sizeof(plUnifiedTime)*GetAtomicCount(); + case kDouble: + return sizeof(double)*GetAtomicCount(); + case kBool: + return sizeof(bool)*GetAtomicCount(); + case kString32: + return sizeof(String32)*GetAtomicCount(); + case kKey: + return sizeof(plUoid)*GetAtomicCount(); + default: + return -1; // err + } +} + +// size of var in bytes +int plSimpleVarDescriptor::GetSize() const +{ + int size=GetAtomicSize(); + return size>=0 ? size*GetCount() : size; +} + +// +// Set type from a string. Return false on err. +// Sets atomicCount and atomicType +// +bool plSimpleVarDescriptor::SetType(const char* type) +{ + if (!type) + return false; + + if (!plVarDescriptor::SetType(type)) + return false; + + if (!stricmp(type, "vector3")) + { + fAtomicCount = 3; + fAtomicType=kFloat; + } + else + if (!stricmp(type, "point3")) + { + fAtomicCount = 3; + fAtomicType=kFloat; + } + else + if (!stricmp(type, "rgb")) + { + fAtomicCount = 3; + fAtomicType=kFloat; + } + else + if (!stricmp(type, "rgba")) + { + fAtomicCount = 4; + fAtomicType=kFloat; + } + else + if (!stricmp(type, "rgb8")) + { + fAtomicCount = 3; + fAtomicType=kByte; + } + else + if (!stricmp(type, "rgba8")) + { + fAtomicCount = 4; + fAtomicType=kByte; + } + else + if (!strnicmp(type, "quat",4)) + { + fAtomicCount = 4; + fAtomicType=kFloat; + } + else + if (!stricmp(type, "int")) + fAtomicType=kInt; + else + if (!stricmp(type, "byte")) + fAtomicType=kByte; + else + if (!stricmp(type, "short")) + fAtomicType=kShort; + else + if (!stricmp(type, "float")) + fAtomicType=kFloat; + else + if (!stricmp(type, "double")) + fAtomicType=kDouble; + else + if (!stricmp(type, "time")) + fAtomicType=kTime; + else + if (!stricmp(type, "ageTimeOfDay")) + fAtomicType=kAgeTimeOfDay; + else + if (!stricmp(type, "bool")) + fAtomicType=kBool; + else + if (!stricmp(type, "string32")) + fAtomicType=kString32; + else + if (!stricmp(type, "plKey")) + fAtomicType=kKey; + else + if (!stricmp(type, "message") || !stricmp(type, "creatable")) + fAtomicType=kCreatable; + else + return false; // err + + return true; // ok +} + +void plSimpleVarDescriptor::CopyFrom(const plSimpleVarDescriptor* other) +{ + plVarDescriptor::CopyFrom(other); + + fAtomicCount=other->GetAtomicCount(); + fAtomicType=other->GetAtomicType(); +} + +// +// Var descriptors are read and written by state descriptors +// +bool plSimpleVarDescriptor::Read(hsStream* s) +{ + if (!plVarDescriptor::Read(s)) + return false; + + fAtomicCount=s->ReadSwap16(); + fAtomicType=(Type)s->ReadByte(); + return true; +} + +// +// Var descriptors are read and written by state descriptors +// +void plSimpleVarDescriptor::Write(hsStream* s) const +{ + plVarDescriptor::Write(s); + + s->WriteSwap16((Int16)fAtomicCount); + s->WriteByte((UInt8)fAtomicType); +} + +///////////////////////////////////////////////////////////////////////////////// +// plSDVarDescriptor +// A var which references another state descriptor +///////////////////////////////////////////////////////////////////////////////// +void plSDVarDescriptor::CopyFrom(const plSDVarDescriptor* other) +{ + plVarDescriptor::CopyFrom(other); + + SetStateDesc(other->GetStateDescriptor()); +} + +// +// Var descriptors are read and written by state descriptors +// +bool plSDVarDescriptor::Read(hsStream* s) +{ + if (!plVarDescriptor::Read(s)) + return false; + + char* sdName=s->ReadSafeString(); + UInt16 version = s->ReadSwap16(); + plStateDescriptor* sd=plSDLMgr::GetInstance()->FindDescriptor(sdName, version); + hsAssert( sd, xtl::format("Failed to find sdl descriptor: %s,%d. Missing legacy descriptor?", sdName, version ).c_str() ); + SetStateDesc(sd); + delete [] sdName; + return true; +} + +// +// Var descriptors are read and written by state descriptors +// +void plSDVarDescriptor::Write(hsStream* s) const +{ + plVarDescriptor::Write(s); + + s->WriteSafeString(GetStateDescriptor()->GetName()); + UInt16 version=GetStateDescriptor()->GetVersion(); + s->WriteSwap(version); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowser.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowser.rc new file mode 100644 index 00000000..b3ff213b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowser.rc @@ -0,0 +1,106 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DIALOG_SDLBROWSER DIALOG DISCARDABLE 0, 0, 372, 227 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "SDL Browser" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,129,189,50,14 + PUSHBUTTON "Cancel",IDCANCEL,192,189,50,14 + LTEXT "State Data Record",IDC_STATIC_SDREC,34,23,70,12 + CONTROL "Values",IDC_STATIC_VALUELABEL,"Static", + SS_LEFTNOWORDWRAP | WS_GROUP,197,23,70,12 + LISTBOX IDC_LIST_VAR,34,39,140,109,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_VALUE,197,39,140,109,CBS_SIMPLE | WS_VSCROLL | + WS_TABSTOP + CONTROL "Slider1",IDC_SLIDER_SDRECS,"msctls_trackbar32", + TBS_AUTOTICKS | TBS_BOTH | WS_TABSTOP,34,158,140,9 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_DIALOG_SDLBROWSER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 365 + TOPMARGIN, 7 + BOTTOMMARGIN, 220 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlg.cpp new file mode 100644 index 00000000..c1ec0d92 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlg.cpp @@ -0,0 +1,171 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSDLBrowserDlg.h" +#include "hsTemplates.h" +#include "../plSDL/plSDL.h" + +// 'this' : used in base member initializer list +#pragma warning(disable:4355) + +plSDLBrowserDlg::plSDLBrowserDlg(int inDialogId) : +plDialog(inDialogId) , +fVarListBox(this,IDC_LIST_VAR), +fValueComboBox(this,IDC_COMBO_VALUE), +fOK(this,IDOK), +fCancel(this,IDCANCEL), +fSDRecSlider(this,IDC_SLIDER_SDRECS), +fCancelled(false), +fReadOnly(true), +fCurSDVar(nil), +fCurComboListBoxPos(-1), +fModified(false) +{ + fVarListBox.SelectionChangeDelegate = plDelegate(this,(TDelegate)OnVarListSelChanged); + fVarListBox.DoubleClickDelegate = plDelegate(this,(TDelegate)OnVarListDoubleClicked); + fValueComboBox.fSelectionChangeDelegate= plDelegate(this,(TDelegate)OnValueComboSelChanged); + fValueComboBox.fEditUpdateDelegate= plDelegate(this,(TDelegate)OnValueComboEditChanged); + fOK.fClickDelegate = plDelegate(this,(TDelegate)OnOKClicked); + fCancel.fClickDelegate = plDelegate(this,(TDelegate)OnCancelClicked); + fSDRecSlider.fThumbPositionDelegate= plDelegate(this,(TDelegate)OnSDRecSliderChanged); + fSDRecSlider.fThumbTrackDelegate= plDelegate(this,(TDelegate)OnSDRecSliderChanged); +} + +plSDLBrowserDlg::~plSDLBrowserDlg() +{ + +} + +void plSDLBrowserDlg::SetDefaults() +{ + +} + +plStateVariable* plSDLBrowserDlg::IGetListBoxVar(int cur) +{ + plStateDataRecord* sdRec=IGetCurrentStateDataRec(); + plStateVariable* var = (plStateVariable*)fVarListBox.GetItemData(cur); // sometimes doesn't work? + if (!var) + { + if (!IAtTopLevel()) + cur++; + + if (curGetNumVars()) + var=sdRec->GetVar(cur); + else + { + cur-=sdRec->GetNumVars(); + var=sdRec->GetSDVar(cur); + } + } + return var; +} + + +int plSDLBrowserDlg::Run() +{ + SetDefaults(); + int ret=DoModal(); + if (ret<0) + { + hsAssert(false, hsTempString(kFmtCtor, "SDL Browser dialog failed to initialize, err code %d, GetLastError %d", + ret, GetLastError())); + return hsFail; + } + return ret; +} + +plStateDataRecord* plSDLBrowserDlg::IPopStateDataRec() +{ + plStateDataRecord* sd=IGetCurrentStateDataRec(); + fStateDataRecStack.pop_back(); + return sd; +} + +void plSDLBrowserDlg::IAddListBoxVar(plStateVariable* var, int cnt) +{ + std::string s; + s = s + (char*)hsTempString(kFmtCtor, "%s[",var->GetVarDescriptor()->GetName()); + if (var->GetVarDescriptor()->GetCount()) + s = s + (char*)hsTempString(kFmtCtor, "%d",var->GetVarDescriptor()->GetCount()); + s = s + (char*)hsTempString(kFmtCtor, "], %s",var->GetVarDescriptor()->GetTypeString()); + + fVarListBox.AddString(s.c_str()); + fVarListBox.SetItemData(cnt, var); + hsAssert(var==fVarListBox.GetItemData(cnt), "set item data failed"); + + hsStatusMessageF("%s\n", s.c_str()); +} + +void plSDLBrowserDlg::IPopulateVarListBox(plStateDataRecord* sd) +{ + fVarListBox.Empty(); + if (!sd) + return; + + hsTempString title(kFmtCtor, "SDL Browser - %s, version %d", + sd->GetDescriptor()->GetName(), sd->GetDescriptor()->GetVersion()); + SetText(title); + + int i, cnt=0; + + if (!IAtTopLevel()) + { + fVarListBox.AddString(".."); + fVarListBox.SetItemData(cnt, IGetPreviousStateDataRec()); + cnt++; + } + + for (i=0;iGetNumVars(); i++, cnt++) + { + plSimpleStateVariable* var=sd->GetVar(i); + IAddListBoxVar(var, cnt); + } + + for (i=0;iGetNumSDVars(); i++, cnt++) + { + plSDStateVariable* var=sd->GetSDVar(i); + IAddListBoxVar(var, cnt); + } +} + +void plSDLBrowserDlg::IPopulateValueComboBox(plSimpleStateVariable* var) +{ + fValueComboBox.Empty(); + + int i; + for(i=0;iGetCount(); i++) + { + fValueComboBox.AddString((char*)hsTempString(var->GetAsString(i))); + } +} + +plStateDataRecord* plSDLBrowserDlg::IGetPreviousStateDataRec() const +{ + SDRecStack::const_iterator it=fStateDataRecStack.end(); + it--; // last + it--; // 2nd to last + return *it; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlg.h new file mode 100644 index 00000000..3096d551 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlg.h @@ -0,0 +1,90 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSDLBrowserDLG_inc +#define plSDLBrowserDLG_inc + +#include "resource.h" +#include "../plWndCtrls/plWndCtrls.h" +#include + +class plSimpleStateVariable; +class plStateDataRecord; +class plStateVariable; +class plSDStateVariable; +class plSDLBrowserDlg : public plDialog +{ +private: + bool fCancelled; + bool fModified; + bool fReadOnly; + plListBox fVarListBox; + plComboBox fValueComboBox; + plButton fOK, fCancel; + plTrackBar fSDRecSlider; + plSDStateVariable* fCurSDVar; + int fCurComboListBoxPos; + + typedef std::vector SDRecStack; + SDRecStack fStateDataRecStack; + + plStateDataRecord* IGetCurrentStateDataRec() const { return fStateDataRecStack.back(); } + plStateDataRecord* IGetPreviousStateDataRec() const; + bool IAtTopLevel() const { return fStateDataRecStack.size()==1; } + void IPushStateDataRec(plStateDataRecord* sd) { fStateDataRecStack.push_back(sd); } + plStateDataRecord* IPopStateDataRec(); + void IPopulateVarListBox(plStateDataRecord* sd) ; + void IAddListBoxVar(plStateVariable* var, int cnt); + void IPopulateValueComboBox(plSimpleStateVariable* var) ; + plStateVariable* IGetListBoxVar(int cur); +public: + + DECLARE_WINDOWCLASS(plSDLBrowserDialog, plDialog); + plSDLBrowserDlg(int inDialogId=IDD_DIALOG_SDLBROWSER); + ~plSDLBrowserDlg(); + + int Run(); + + // callbacks + void OnInitDialog(); + void OnVarListSelChanged(); + void OnVarListDoubleClicked(); + void OnValueComboSelChanged(); + void OnValueComboEditChanged(); + void OnOKClicked(); + void OnCancelClicked(); + void OnSDRecSliderChanged(); + + // setters + void SetReadOnly(bool r) { fReadOnly=r; } + void SetStateDataRec(plStateDataRecord* sd) { fStateDataRecStack.clear(); fStateDataRecStack.push_back(sd); } + void SetDefaults(); + + // getters + bool GetCancelled() const { return fCancelled; } + bool GetModified() const { return fModified; } +}; + +#endif // plSDLBrowserDLG_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlgHandlers.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlgHandlers.cpp new file mode 100644 index 00000000..68e760d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/plSDLBrowserDlgHandlers.cpp @@ -0,0 +1,167 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSDLBrowserDlg.h" +#include "../plSDL/plSDL.h" + +void plSDLBrowserDlg::OnInitDialog() +{ + plDialog::OnInitDialog(); + const int kWinPos=250; + + plRect r=GetWindowRect(); + r.Min.X+=kWinPos; + r.Min.Y+=kWinPos; + r.Max.X+=kWinPos; + r.Max.Y+=kWinPos; + MoveWindow(r, true); + + fSDRecSlider.Hide(); + + if (IGetCurrentStateDataRec()) + IPopulateVarListBox(IGetCurrentStateDataRec()); +} + +void plSDLBrowserDlg::OnOKClicked() +{ + + EndDialogTrue(); +} + +void plSDLBrowserDlg::OnCancelClicked() +{ + EndDialogFalse(); + fCancelled=true; +} + +void plSDLBrowserDlg::OnVarListSelChanged() +{ + int cur=fVarListBox.GetCurrent(); + std::string curString=fVarListBox.GetString(cur); + if (curString=="..") + return; + + plStateVariable* var=IGetListBoxVar(cur); + hsAssert(var, "nil var?"); + if (var->GetAsSimpleStateVar()) + { + plSimpleStateVariable* sVar=var->GetAsSimpleStateVar(); + IPopulateValueComboBox(sVar); + fValueComboBox.SetCurrent(0); + fCurComboListBoxPos=0; + } +} + +void plSDLBrowserDlg::OnVarListDoubleClicked() +{ + fCurSDVar=nil; + fSDRecSlider.Hide(); + + int cur=fVarListBox.GetCurrent(); + std::string curString=fVarListBox.GetString(cur); + if (curString=="..") + { + IPopStateDataRec(); + IPopulateVarListBox(IGetCurrentStateDataRec()); + return; + } + + plStateVariable* var=IGetListBoxVar(cur); + hsAssert(var, "nil var?"); + if (var->GetAsSDStateVar()) + { + // user doubleclicked an SDVar + plSDStateVariable* sdVar=var->GetAsSDStateVar(); + if (sdVar->GetCount()==0) + { + sdVar->Resize(1); + } + else + if (sdVar->GetCount()>1) + { + fSDRecSlider.Show(); + int max=var->GetCount(); + fSDRecSlider.SetRange(0, max-1); + } + IPushStateDataRec(sdVar->GetStateDataRecord(0)); + IPopulateVarListBox(IGetCurrentStateDataRec()); + fCurSDVar=sdVar; + fValueComboBox.Empty(); + } +} + +void plSDLBrowserDlg::OnValueComboSelChanged() +{ + int cur=fValueComboBox.GetCurrent(); + hsStatusMessageF("Changing cur combo box sel to %d\n", cur); + if (cur>=0) + fCurComboListBoxPos=cur; +} + +void plSDLBrowserDlg::OnValueComboEditChanged() +{ + if (fReadOnly) + return; + + // get var from list box + int listBoxPos=fVarListBox.GetCurrent(); + plStateVariable* var=IGetListBoxVar(listBoxPos); + hsAssert(var, "nil var?"); + hsAssert(var->GetAsSimpleStateVar(), "wrong type of var"); + + // change value of var + int comboxBoxPos=fValueComboBox.GetCurrent(); + if (comboxBoxPos<0) + comboxBoxPos=fCurComboListBoxPos; + if (comboxBoxPos>=0) + { + std::string editString=fValueComboBox.GetText(); + if (var->GetAsSimpleStateVar()->SetFromString(editString.c_str(), comboxBoxPos)) + { + hsStatusMessageF("changing item %d to %s", comboxBoxPos, editString.c_str()); + fValueComboBox.InsertString(comboxBoxPos, editString.c_str()); + fValueComboBox.DeleteString(comboxBoxPos+1); + var->GetAsSimpleStateVar()->SetDirty(true); + var->GetAsSimpleStateVar()->SetUsed(true); + } +#if 0 + editString.reverse(); + fValueComboBox.SetText(editString.c_str()); +#endif + } + fModified=true; +} + +void plSDLBrowserDlg::OnSDRecSliderChanged() +{ + int pos=fSDRecSlider.GetPos(); + IPopStateDataRec(); + IPushStateDataRec(fCurSDVar->GetStateDataRecord(pos)); + IPopulateVarListBox(IGetCurrentStateDataRec()); + fValueComboBox.Empty(); + + hsStatusMessageF("Slider pos=%d\n", pos); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/resource.h new file mode 100644 index 00000000..2b7382bc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSDLBrowser/resource.h @@ -0,0 +1,22 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by plSDLBrowser.rc +// +#define IDD_DIALOG_SDLBROWSER 201 +#define IDC_STATIC_VARLABEL 2000 +#define IDC_STATIC_SDREC 2001 +#define IDC_STATIC_VALUELABEL 2002 +#define IDC_LIST_VAR 2003 +#define IDC_COMBO_VALUE 2004 +#define IDC_SLIDER_SDRECS 2005 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 202 +#define _APS_NEXT_COMMAND_VALUE 41001 +#define _APS_NEXT_CONTROL_VALUE 2006 +#define _APS_NEXT_SYMED_VALUE 202 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plCullPoly.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plCullPoly.cpp new file mode 100644 index 00000000..c3362f93 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plCullPoly.cpp @@ -0,0 +1,186 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plCullPoly.h" +#include "hsMatrix44.h" +#include "hsStream.h" +#include "hsFastMath.h" + +plCullPoly& plCullPoly::InitFromVerts(UInt32 f) +{ + fFlags = f; + + hsAssert(fVerts.GetCount() > 2, "Initializing from degenerate poly"); + + hsVector3 a; + hsVector3 b; + a.Set(&fVerts[1], &fVerts[0]); + b.Set(&fVerts[2], &fVerts[0]); + fNorm = a % b; + hsFastMath::Normalize(fNorm); + fDist = -(fNorm.InnerProduct(fVerts[0])); + + fCenter.Set(0,0,0); + int i; + for( i = 0; i < fVerts.GetCount(); i++ ) + { + fCenter += fVerts[i]; + } + fCenter *= 1.f / hsScalar(fVerts.GetCount()); + + fRadius = ICalcRadius(); + + return *this; +} + +hsScalar plCullPoly::ICalcRadius() const +{ + hsScalar radSq = 0; + int i; + for( i = 0; i < fVerts.GetCount(); i++ ) + { + hsScalar tmpSq = hsVector3(&fVerts[i], &fCenter).MagnitudeSquared(); + if( tmpSq > radSq ) + radSq = tmpSq; + } + return radSq * hsFastMath::InvSqrt(radSq); +} + +plCullPoly& plCullPoly::Flip(const plCullPoly& p) +{ + fFlags = p.fFlags; + + fNorm = -p.fNorm; + fDist = -p.fDist; + fCenter = p.fCenter; + fRadius = p.fRadius; + + int n = p.fVerts.GetCount(); + fVerts.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + fVerts[n-i-1] = p.fVerts[i]; + + return *this; +} + +plCullPoly& plCullPoly::Transform(const hsMatrix44& l2w, const hsMatrix44& w2l, plCullPoly& dst) const +{ + hsMatrix44 tpose; + w2l.GetTranspose(&tpose); + + dst.fFlags = fFlags; + + dst.fVerts.SetCount(fVerts.GetCount()); + + int i; + for( i = 0; i < fVerts.GetCount(); i++ ) + { + dst.fVerts[i] = l2w * fVerts[i]; + } + dst.fCenter = l2w * fCenter; + + dst.fNorm = tpose * fNorm; + + dst.fDist = -(dst.fNorm .InnerProduct(dst.fVerts[0])); + + ICalcRadius(); + + return dst; +} + +void plCullPoly::Read(hsStream* s, hsResMgr* mgr) +{ + fFlags = s->ReadSwap32(); + + fNorm.Read(s); + fDist = s->ReadSwapScalar(); + fCenter.Read(s); + + fRadius = s->ReadSwapScalar(); + + int n = s->ReadSwap32(); + fVerts.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + fVerts[i].Read(s); +} + +void plCullPoly::Write(hsStream* s, hsResMgr* mgr) +{ + s->WriteSwap32(fFlags); + + fNorm.Write(s); + s->WriteSwapScalar(fDist); + fCenter.Write(s); + + s->WriteSwapScalar(fRadius); + + s->WriteSwap32(fVerts.GetCount()); + int i; + for( i = 0; i < fVerts.GetCount(); i++ ) + fVerts[i].Write(s); +} + +#ifdef HS_DEBUGGING +#define MF_VALIDATE_POLYS +#endif // HS_DEBUGGING + +#ifdef MF_VALIDATE_POLYS +hsBool plCullPoly::Validate() const +{ + const hsScalar kMinMag = 1.e-8f; + hsScalar magSq = fNorm.MagnitudeSquared(); + if( magSq < kMinMag ) + return false; + if( fVerts.GetCount() < 3 ) + return false; + hsVector3 norm = hsVector3(&fVerts[1], &fVerts[0]) % hsVector3(&fVerts[2], &fVerts[0]); + magSq = norm.MagnitudeSquared(); + if( magSq < kMinMag ) + return false; + norm *= hsFastMath::InvSqrtAppr(magSq); + int i; + for( i = 3; i < fVerts.GetCount(); i++ ) + { + hsVector3 nextNorm = hsVector3(&fVerts[i-1], &fVerts[0]) % hsVector3(&fVerts[i], &fVerts[0]); + magSq = nextNorm.MagnitudeSquared(); + if( magSq < kMinMag ) + return false; + nextNorm *= hsFastMath::InvSqrtAppr(magSq); + if( nextNorm.InnerProduct(norm) < kMinMag ) + return false; + } + return true; +} +#else // MF_VALIDATE_POLYS +hsBool plCullPoly::Validate() const +{ + return true; +} +#endif // MF_VALIDATE_POLYS + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plCullPoly.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plCullPoly.h new file mode 100644 index 00000000..8a37abf7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plCullPoly.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plCullPoly_inc +#define plCullPoly_inc + +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "hsBitVector.h" + +class hsStream; +class hsResMgr; +struct hsMatrix44; + +const hsScalar kCullPolyDegen = 1.e-4f; + +class plCullPoly +{ +public: + enum { + kNone = 0x0, + kHole = 0x1, + kTwoSided = 0x2 + }; + + UInt32 fFlags; + mutable hsBitVector fClipped; // fClipped[i] => edge(fVerts[i], fVerts[(i+1)%n]) + + hsTArray fVerts; + hsVector3 fNorm; + hsScalar fDist; + hsPoint3 fCenter; + hsScalar fRadius; + + const hsPoint3& GetCenter() const { return fCenter; } + hsScalar GetRadius() const { return fRadius; } + + void SetHole(hsBool on) { if( on )fFlags |= kHole; else fFlags &= ~kHole; } + void SetTwoSided(hsBool on) { if( on )fFlags |= kTwoSided; else fFlags &= ~kTwoSided; } + + hsBool IsHole() const { return fFlags & kHole; } // Assumes kHole is 0x1 + hsBool IsTwoSided() const { return 0 != (fFlags & kTwoSided); } + + plCullPoly& Init(const plCullPoly& p) { fClipped.Clear(); fVerts.SetCount(0); fFlags = p.fFlags; fNorm = p.fNorm; fDist = p.fDist; fCenter = p.fCenter; return *this; } + plCullPoly& Flip(const plCullPoly& p); + plCullPoly& InitFromVerts(UInt32 f=kNone); + hsScalar ICalcRadius() const; + + plCullPoly& Transform(const hsMatrix44& l2w, const hsMatrix44& w2l, plCullPoly& dst) const; + + void Read(hsStream* s, hsResMgr* mgr); + void Write(hsStream* s, hsResMgr* mgr); + + hsBool DegenerateVert(const hsPoint3& p) const { return fVerts.GetCount() && (kCullPolyDegen > hsVector3(&p, &fVerts[fVerts.GetCount()-1]).MagnitudeSquared()); } + + hsBool Validate() const; // no-op, except for special debugging circumstances. +}; + +#endif // plCullPoly_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccTree.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccTree.cpp new file mode 100644 index 00000000..f1c796cc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccTree.cpp @@ -0,0 +1,221 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +void plOccTree::AddPoly(plPolygon* poly) +{ + fBasePolys.Append(*poly); +} + +void plOccTree::MakeOccTree() +{ + if( !fBasePolys.GetCount() ) + return; + + ISortBasePolys(); + + int i; + for( i = 0; i < fBasePolys.GetCount(); i++ ) + fRoot = IAddPolyRecur(fRoot, fBasePolys[i], false); + + fBasePolys.SetCount(0); +} + +plOccNode* poOccTree::IMakeSubTree(plOccPoly* poly) +{ + plOccNode* nextNode = nil; + plOccNode* lastNode = nil; + + int i; + for( i = 0; i < poly->fVerts.GetCount(); i++ ) + { + if( poly->fEdgeFlags[i] & plOccPoly::kEdgeClipped ) + continue; + + nextNode = fNodePool.Append(); + nextNode->fFlags = 0; + nextNode->fOutChild = nil; + + int j = i+1 < poly->fVerts.GetCount() ? i+1 : 0; + + // Need to set the viewplane here. Calc once per base poly and use + // that for fragments? + nextNode->Init(poly->fVerts[i], poly->fVerts[j], fViewPos); + + if( nextNode->fInChild = lastChild ) + nextNode->fFlags = plOccNode::kHasInChild; + else + { + nextNode->fInChild = fNodePool.Append(); + nextNode->fInChild->Init(poly, false); + } + + lastNode = nextNode; + } + + // If we have no nextNode, all our edges were clipped. In + // that case, we'll just return an "out" leaf. + if( !nextNode ) + { + nextNode = fNodePool.Append(); + nextNode->fFlags = 0; + nextNode->fInChild = nextNode->fOutChild = nil; + nextNode->Init(poly, true); + } + + return nextNode; +} + +void plOccNode::Init(const hsPoint3& p0, const hsPoint3& p1, const hsPoint3& pv) +{ + hsVector3 v0, v1; + v0.Set(&p0, &pv); + v1.Set(&p1, &pv); + + fPlane.fNormal = v0 % v1; + fPlane.fDist = fPlane.fNormal.InnerProduct(v0); +} + +void plOccNode::Init(plOccPoly* poly) +{ + fPlane = poly->fPlane; + // set the viewplane + fFlags = kIsLeaf; +} + +// Adding a poly to a node +// if the node is nil +// IMakeSubTree(poly) replaces the node +// else +// if the node is a leaf +// pitch the poly +// return node (no replacement) +// else +// if poly is inside the node +// recur on node's inner child +// return node (no replacement) +// else +// if poly is ouside the node +// recur on node's outer child +// return node (no replacement) +// else (node splits poly) +// recur on node's inner child +// recur on node's outer child +// return node (no replacement) +// end +// +// Special case - Degenarate poly's can come +// from ITestPoly if an edge of the input poly +// is on the plane. In that case, the function +// will return kSplit, but either inPoly or outPoly +// will have no vertices. That degenerate poly, +// when added to a node, should just be pitched. +// + + + + +// Returns new root, in case it changed. +// This assumes polys are being added front to back. +// This function will break the poly into fragments that fit in the +// current planes within the tree. Planes are added when a final fragment +// is added (in IMakeSubTree). +// We count on ITestPoly to properly mark edges which were created by +// clipping, as those won't generate leaf nodes. +plOccNode* plOccTree::IAddPolyRecur(plOccNode* node, plOccPoly* poly) +{ + if( !poly->fVerts.GetCount() ) + return node; + + if( !node ) + { + return IMakeSubTree(poly); + } + + plOccPoly* inPoly = nil; + plOccPoly* outPoly = nil; + + + UInt32 test = ITestPoly(node->fPlane, poly, inPoly, outPoly); + + switch( test ) + { + case kAllIn: + node->fInChild = IAddPolyRecur(node->fInChild, poly); + break; + case kAllOut: + node->fOutChild = IAddPolyRecur(node->fOutChild, poly); + break; + case kSplit: + node->fInChild = IAddPolyRecur(node->fInChild, inPoly); + node->fOutChild = IAddPolyRecur(node->fOutChild, outPoly); + break; + }; + + return node; +} + +hsBool plOccTree::BoundsVisible(const hsBounds3Ext& bnd) const +{ + if( !fRoot ) + return true; + + return fRoot->IBoundsVisible(bnd); +} + + +hsBool plOccNode::IInChildBoundsVisible(const hsBounds3Ext& bnd) const +{ + return fInChild + ? fInChild->IBoundsVisible(bnd) + : false; +} + +hsBool plOccNode::IOutChildBoundsVisible(const hsBounds3Ext& bnd) const +{ + return fOutChild + ? fOutChild->IBoundsVisible(bnd) + : true; +} + +hsBool plOccNode::IBoundsVisible(const hsBounds3Ext& bnd) const +{ + hsPoint2 depth; + bnd.TestPlane(fPlane.fNormal, depth); + if( depth.fX > fPlane.fDist ) + { + return IOutChildVisible(bnd); + } + else if( depth.fY < fPlane.fDist ) + { + return IInChildVisible(bnd); + } + + // here's where it gets wierd. we pass the bounds in + // both directions. if either says it's visible, it's visible. + // doesn't seem like it would work, but you never know. + return IOutChildVisible(bnd) || IInChildVisible(bnd); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccTree.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccTree.h new file mode 100644 index 00000000..126d1788 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccTree.h @@ -0,0 +1,164 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plOccTree_inc +#define plOccTree_inc + +#include "hsTemplates.h" +#include "hsGeometry3.h" + +class plBoundsHierarchy; + +class plOccPlane +{ +public: + hsVector3 fNormal; + hsScalar fDist; + +}; + +class plOccPoly +{ +public: + enum { + kEdgeClipped = 0x1 + }; + + plOccPlane fPlane; + + hsTArray fVerts; + hsTArray fEdgeFlags; // flag[i] => edge(fVerts[i], fVerts[(i+1)%n]) +}; + +class plOccNode +{ +protected: + enum { + kNone = 0x0, + kIsLeaf = 0x1 + }; + enum { + kAllIn = 0x0, + kAllOut = 0x1, + kSplit = 0x2 + }; + + UInt32 fFlags; + + plOccPlane fPolyPlane; // Plane of the poly we came from + plOccPlane fViewPlane; // Plane perp to view dir. + // For an interior node, ViewPlane is for the nearest (to view) point + // on the poly. A bound closer than that will not be occluded by this + // node or any nodes deeper in the tree. + // For a leaf it's the farthest point on the poly. A bound inside this + // plane OR the PolyPlane is occluded. + + plOccNode* fInChild; + + plOccNode* fOutChild; +}; + +class plOccTree +{ +protected: + + enum { + kNone = 0x0, + kNeedsBuild = 0x1 + }; + + UInt8 fFlags; + + // Temp pools for building our trees each frame. + hsTArray fPolyPool; + hsTArray fBasePolys; + + // The BSP used to add our polys front to back. This BSP is constant. + plOccNode* fBSP; + + // This current frame's view pos and occluder tree. + plOccNode* fRoot; + hsPoint3 fViewPos; + + + plOccNode* IAddPolyRecur(plOccNode* n, plOccPoly* poly); + + void ITrimPoly(plOccPlane& plane, plOccPoly* polyIn, plOccPoly*& polyIn, plOccPoly*& polyOut); + + plOccNode* IBuildOccTree(); + +public: + + plOccTree() : fFlags(kNone), fBSP(nil), fRoot(nil) {} + ~plOccTree() {} + + // We'll take in the view position (for sorting and building). + // The view direction isn't necessary, but may be useful for + // selecting a subset of occluders (like don't bother with ones parallel to the view dir). + // What we really want is to pass in the viewport walls, or all the clip planes to initialize + // the occtree, then occluders out of view are automatically pruned, and the single test + // does the full view/portal/occluder test. + void SetView(const hsPoint3& pos, const hsVector3& dir); + + + // The algorithm is: + // if bnd is totally inside this node's plane + // recur bnd on inside child/leaf + // else if bnd is totaly outside this node's plane + // recur bnd on outside child + // else + // recur bnd's children on this node + // + // There's two ways to output the visibility info + // 1) Set a visible/invisible bit for each of the bnd leaves + // 2) output a list of visible bnds. + // The second is preferable, since leaves determined invisible by interior + // node tests never get traversed. But if the rendering pipeline has needs + // to traverse the drawable data in some other order (for depth or material + // sorting for example), then the list of visible bnds needs to be translated + // into the first option anyway. + // + // Notes on the vague algorithm: + // When recurring on the inside child, hitting a leaf checks against the source + // occluder poly, with the usual inside=hidden, outside=visible, split recurs + // the bnd's children on this leaf. + // Hitting a nil outside child == visible + // It's a double recursion, recurring first on the bnd hierarchy, and second on the occluder tree. + // Recursion stops when: + // 1) A bnd is totally in or totally out of a leaf of the occluder tree + // 2) A bnd is a leaf of the bnd hierarchy. + // + void TestHeirarchy(plBoundsHierarchy* bnd); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + // Export only + void AddPoly(plOccPoly* poly); + void BuildBSP(); +}; + +#endif // plOccTree_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluder.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluder.cpp new file mode 100644 index 00000000..84c0c0a4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluder.cpp @@ -0,0 +1,387 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plOccluder.h" +#include "hsStream.h" +#include "plOccluderProxy.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" +#include "../plSurface/plLayer.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "hsResMgr.h" +#include "plgDispatch.h" +#include "plVisRegion.h" +#include "plVisMgr.h" + +plOccluder::plOccluder() +: fSceneNode(nil) +{ + fProxyGen = TRACKED_NEW plOccluderProxy; + fProxyGen->Init(this); + + fVisSet.SetBit(0); +} + +plOccluder::~plOccluder() +{ + delete fProxyGen; +} + +hsBool plOccluder::MsgReceive(plMessage* msg) +{ + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + switch( refMsg->fType ) + { + case kRefVisRegion: + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + IAddVisRegion(plVisRegion::ConvertNoRef(refMsg->GetRef())); + } + else + { + IRemoveVisRegion(plVisRegion::ConvertNoRef(refMsg->GetRef())); + } + return true; + default: + break; + } + + } + return plObjInterface::MsgReceive(msg); +} + +void plOccluder::IAddVisRegion(plVisRegion* reg) +{ + if( reg ) + { + int idx = fVisRegions.Find(reg); + if( fVisRegions.kMissingIndex == idx ) + { + fVisRegions.Append(reg); + if( reg->GetProperty(plVisRegion::kIsNot) ) + fVisNot.SetBit(reg->GetIndex()); + else + { + fVisSet.SetBit(reg->GetIndex()); + if( reg->ReplaceNormal() ) + fVisSet.ClearBit(plVisMgr::kNormal); + } + } + } +} + +void plOccluder::IRemoveVisRegion(plVisRegion* reg) +{ + if( reg ) + { + int idx = fVisRegions.Find(reg); + if( fVisRegions.kMissingIndex != idx ) + { + fVisRegions.Remove(idx); + if( reg->GetProperty(plVisRegion::kIsNot) ) + fVisNot.ClearBit(reg->GetIndex()); + else + fVisSet.ClearBit(reg->GetIndex()); + } + } +} + +plDrawableSpans* plOccluder::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + hsTArray pos; + hsTArray norm; + hsTArray color; + hsTArray tris; + + plLayer* lay = plLayer::ConvertNoRef(mat->GetLayer(0)->BottomOfStack()); + if( lay ) + lay->SetMiscFlags(lay->GetMiscFlags() & ~hsGMatState::kMiscTwoSided); + + const hsTArray& polys = GetLocalPolyList(); + int i; + for( i = 0; i < polys.GetCount(); i++ ) + { + hsColorRGBA col; + if( polys[i].IsHole() ) + col.Set(0,0,0,1.f); + else + col.Set(1.f, 1.f, 1.f, 1.f); + + int triStart = tris.GetCount(); + + int idx0 = pos.GetCount(); + pos.Append(polys[i].fVerts[0]); + norm.Append(polys[i].fNorm); + color.Append(col); + pos.Append(polys[i].fVerts[1]); + norm.Append(polys[i].fNorm); + color.Append(col); + int j; + for( j = 2; j < polys[i].fVerts.GetCount(); j++ ) + { + int idxCurr = pos.GetCount(); + pos.Append(polys[i].fVerts[j]); + norm.Append(polys[i].fNorm); + color.Append(col); + tris.Append(idx0); + tris.Append(idxCurr-1); + tris.Append(idxCurr); + } +#if 1 + if( polys[i].IsTwoSided() ) + { + int n = tris.GetCount(); + while( --n >= triStart ) + { + int idx = tris[n]; + tris.Append(idx); + } + } +#endif + } + return plDrawableGenerator::GenerateDrawable(pos.GetCount(), + pos.AcquireArray(), + norm.AcquireArray(), + nil, 0, + color.AcquireArray(), + true, + nil, + tris.GetCount(), + tris.AcquireArray(), + mat, + GetLocalToWorld(), + true, + &idx, + addTo); +} + +void plOccluder::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ +// Commenting out the following asserts. Although they are fundamentally correct, +//essentially identity matrices which aren't so flagged (because of numerical +// precision) are triggering bogus asserts. mf +// hsAssert(l2w.fFlags & hsMatrix44::kIsIdent, "Non-identity transform to non-movable Occluder"); +// hsAssert(w2l.fFlags & hsMatrix44::kIsIdent, "Non-identity transform to non-movable Occluder"); +} + +const hsMatrix44& plOccluder::GetLocalToWorld() const +{ + return hsMatrix44::IdentityMatrix(); +} + +const hsMatrix44& plOccluder::GetWorldToLocal() const +{ + return hsMatrix44::IdentityMatrix(); +} + +void plOccluder::ComputeFromPolys() +{ + IComputeBounds(); + IComputeSurfaceArea(); +} + +void plOccluder::IComputeBounds() +{ + fWorldBounds.MakeEmpty(); + + const hsTArray& polys = GetLocalPolyList(); + int i; + for( i =0 ; i < polys.GetCount(); i++ ) + { + int j; + for( j = 0; j < polys[i].fVerts.GetCount(); j++ ) + fWorldBounds.Union(&polys[i].fVerts[j]); + } +} + +hsScalar plOccluder::IComputeSurfaceArea() +{ + hsScalar area = 0; + const hsTArray& polys = GetLocalPolyList(); + int i; + for( i =0 ; i < polys.GetCount(); i++ ) + { + int j; + for( j = 2; j < polys[i].fVerts.GetCount(); j++ ) + { + area += (hsVector3(&polys[i].fVerts[j], &polys[i].fVerts[j-2]) % hsVector3(&polys[i].fVerts[j-1], &polys[i].fVerts[j-2])).Magnitude(); + } + } + area *= 0.5f; + + return fPriority = area; +} + +void plOccluder::SetPolyList(const hsTArray& list) +{ + UInt16 n = list.GetCount(); + fPolys.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + fPolys[i] = list[i]; +} + +void plOccluder::ISetSceneNode(plKey node) +{ + if( fSceneNode != node ) + { + if( node ) + { + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(node, plRefMsg::kOnCreate, -1, plNodeRefMsg::kOccluder); + hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kPassiveRef); + } + if( fSceneNode ) + { + fSceneNode->Release(GetKey()); + } + fSceneNode = node; + } +} + +void plOccluder::Read(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Read(s, mgr); + + fWorldBounds.Read(s); + fPriority = s->ReadSwapScalar(); + + hsTArray& localPolys = IGetLocalPolyList(); + UInt16 n = s->ReadSwap16(); + localPolys.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + localPolys[i].Read(s, mgr); + + plKey nodeKey = mgr->ReadKey(s); + ISetSceneNode(nodeKey); + + n = s->ReadSwap16(); + fVisRegions.SetCountAndZero(n); + for( i = 0; i < n; i++ ) + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefVisRegion), plRefFlags::kActiveRef); +} + +void plOccluder::Write(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Write(s, mgr); + + fWorldBounds.Write(s); + s->WriteSwapScalar(fPriority); + + const hsTArray& localPolys = IGetLocalPolyList(); + s->WriteSwap16(localPolys.GetCount()); + int i; + for( i = 0; i < localPolys.GetCount(); i++ ) + localPolys[i].Write(s, mgr); + + mgr->WriteKey(s, fSceneNode); + + s->WriteSwap16(fVisRegions.GetCount()); + for( i = 0; i < fVisRegions.GetCount(); i++ ) + mgr->WriteKey(s, fVisRegions[i]); +} + +plMobileOccluder::plMobileOccluder() +{ + fLocalToWorld.Reset(); + fWorldToLocal.Reset(); +} + +plMobileOccluder::~plMobileOccluder() +{ +} + +void plMobileOccluder::IComputeBounds() +{ + plOccluder::IComputeBounds(); + fLocalBounds = fWorldBounds; + fWorldBounds.Transform(&fLocalToWorld); +} + +void plMobileOccluder::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + fLocalToWorld = l2w; + fWorldToLocal = w2l; + + if( fPolys.GetCount() != fOrigPolys.GetCount() ) + fPolys.SetCount(fOrigPolys.GetCount()); + + int i; + for( i = 0; i < fPolys.GetCount(); i++ ) + fOrigPolys[i].Transform(l2w, w2l, fPolys[i]); + + if( fProxyGen ) + fProxyGen->SetTransform(l2w, w2l); +} + +void plMobileOccluder::SetPolyList(const hsTArray& list) +{ + UInt16 n = list.GetCount(); + fOrigPolys.SetCount(n); + fPolys.SetCount(n); + + int i; + for( i = 0; i < n; i++ ) + { + fPolys[i] = fOrigPolys[i] = list[i]; + } +} + +void plMobileOccluder::Read(hsStream* s, hsResMgr* mgr) +{ + plOccluder::Read(s, mgr); + + fLocalToWorld.Read(s); + fWorldToLocal.Read(s); + + fLocalBounds.Read(s); + + fPolys.SetCount(fOrigPolys.GetCount()); + + SetTransform(fLocalToWorld, fWorldToLocal); +} + +void plMobileOccluder::Write(hsStream* s, hsResMgr* mgr) +{ + plOccluder::Write(s, mgr); + + fLocalToWorld.Write(s); + fWorldToLocal.Write(s); + + fLocalBounds.Write(s); +} + +void plMobileOccluder::ComputeFromPolys() +{ + SetTransform(fLocalToWorld, fWorldToLocal); + plOccluder::ComputeFromPolys(); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluder.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluder.h new file mode 100644 index 00000000..3096cc3e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluder.h @@ -0,0 +1,153 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plOccluder_inc +#define plOccluder_inc + +#include "../pnSceneObject/plObjInterface.h" +#include "hsTemplates.h" +#include "hsMatrix44.h" +#include "plCullPoly.h" +#include "hsBounds.h" +#include "hsBitVector.h" + +class plOccluderProxy; +class plDrawableSpans; +class hsGMaterial; +class plVisRegion; + +class plOccluder : public plObjInterface +{ +public: + enum { + kDisable = 0x0, + + kNumProps + }; + enum { + kRefVisRegion + }; +protected: + hsTArray fPolys; + + plOccluderProxy* fProxyGen; + + hsBitVector fVisSet; + hsTArray fVisRegions; + hsBitVector fVisNot; + + hsScalar fPriority; + hsBounds3Ext fWorldBounds; + + plKey fSceneNode; + + virtual hsScalar IComputeSurfaceArea(); + virtual void IComputeBounds(); + + virtual hsTArray& IGetLocalPolyList() { return fPolys; } + + virtual void ISetSceneNode(plKey node); + + void IAddVisRegion(plVisRegion* reg); + void IRemoveVisRegion(plVisRegion* reg); + +public: + plOccluder(); + virtual ~plOccluder(); + + CLASSNAME_REGISTER( plOccluder ); + GETINTERFACE_ANY( plOccluder, plObjInterface); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual hsScalar GetPriority() const { return fPriority; } + + hsBool InVisSet(const hsBitVector& visSet) const { return fVisSet.Overlap(visSet); } + hsBool InVisNot(const hsBitVector& visNot) const { return fVisNot.Overlap(visNot); } + + virtual const hsBounds3Ext& GetWorldBounds() const { return fWorldBounds; } + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + virtual const hsMatrix44& GetLocalToWorld() const; + virtual const hsMatrix44& GetWorldToLocal() const; + + virtual void SetPolyList(const hsTArray& list); + virtual const hsTArray& GetWorldPolyList() const { return fPolys; } + virtual const hsTArray& GetLocalPolyList() const { return fPolys; } + + virtual Int32 GetNumProperties() const { return kNumProps; } + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + // Visualization + virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo); + + // Export only function to initialize. + virtual void ComputeFromPolys(); + + // These two should only be called internally and on export/convert + virtual plKey GetSceneNode() const { return fSceneNode; } +}; + +class plMobileOccluder : public plOccluder +{ +protected: + hsMatrix44 fLocalToWorld; + hsMatrix44 fWorldToLocal; + + hsBounds3Ext fLocalBounds; + + hsTArray fOrigPolys; + + virtual void IComputeBounds(); + + virtual hsTArray& IGetLocalPolyList() { return fOrigPolys; } + +public: + + plMobileOccluder(); + virtual ~plMobileOccluder(); + + CLASSNAME_REGISTER( plMobileOccluder ); + GETINTERFACE_ANY( plMobileOccluder, plOccluder ); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + virtual const hsMatrix44& GetLocalToWorld() const { return fLocalToWorld; } + virtual const hsMatrix44& GetWorldToLocal() const { return fWorldToLocal; } + + virtual void SetPolyList(const hsTArray& list); + + virtual const hsTArray& GetLocalPolyList() const { return fOrigPolys; } + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + // Export only function to initialize. + virtual void ComputeFromPolys(); +}; + +#endif // plOccluder_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluderProxy.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluderProxy.cpp new file mode 100644 index 00000000..6c696757 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluderProxy.cpp @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plOccluderProxy.h" +#include "plOccluder.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plDrawableGenerator.h" +#include "../pnMessage/plProxyDrawMsg.h" + +plOccluderProxy::plOccluderProxy() +: plProxyGen(hsColorRGBA().Set(0.2f,0.2f,0.8f,1.f), hsColorRGBA().Set(1.f,0.5f,0.5f,1.f), 0.5f), + fOwner(nil) +{ +} + +plOccluderProxy::~plOccluderProxy() +{ +} + +hsBool plOccluderProxy::Init(plOccluder* occluder) +{ + plProxyGen::Init(occluder); + + fOwner = occluder; + fProxyMsgType = plProxyDrawMsg::kOccluder; + + return fOwner != nil; +} + +plKey plOccluderProxy::IGetNode() const +{ + return fOwner ? fOwner->GetSceneNode() : nil; +} + +plDrawableSpans* plOccluderProxy::ICreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo) +{ + if( fOwner ) + { + return fOwner->CreateProxy(mat, idx, addTo); + } + return nil; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluderProxy.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluderProxy.h new file mode 100644 index 00000000..8d49781c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plOccluderProxy.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plOccluderProxy_inc +#define plOccluderProxy_inc + +#include "../plDrawable/plProxyGen.h" + +class plOccluder; +class plDrawableSpans; +class hsGMaterial; + +class plOccluderProxy : public plProxyGen +{ +protected: + plOccluder* fOwner; + + + virtual plDrawableSpans* ICreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo=nil); + virtual plKey IGetNode() const; +public: + plOccluderProxy(); + virtual ~plOccluderProxy(); + + hsBool Init(plOccluder* occluder); +}; + +#endif // plOccluderProxy_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPageTreeMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPageTreeMgr.cpp new file mode 100644 index 00000000..bb17863b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPageTreeMgr.cpp @@ -0,0 +1,696 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plPageTreeMgr.h" +#include "../plDrawable/plSpaceTreeMaker.h" +#include "../plDrawable/plSpaceTree.h" +#include "plDrawable.h" +#include "../plScene/plSceneNode.h" +#include "plPipeline.h" +#include "../plMath/hsRadixSort.h" +#include "plCullPoly.h" +#include "plOccluder.h" +#include "hsFastMath.h" +#include "plProfile.h" +#include "plVisMgr.h" + +#include "plTweak.h" + +static hsTArray scratchList; + +hsBool plPageTreeMgr::fDisableVisMgr = 0; + +plProfile_CreateTimer("Object Sort", "Draw", DrawObjSort); +plProfile_CreateCounter("Objects Sorted", "Draw", DrawObjSorted); +plProfile_CreateTimer("Occluder Sort", "Draw", DrawOccSort); +plProfile_CreateCounter("Occluders Used", "Draw", DrawOccUsed); +plProfile_CreateTimer("Occluder Build", "Draw", DrawOccBuild); +plProfile_CreateCounter("Occluder Polys Processed", "Draw", DrawOccPolyProc); +plProfile_CreateTimer("Occluder Poly Sort", "Draw", DrawOccPolySort); + +plPageTreeMgr::plPageTreeMgr() +: fSpaceTree(nil) +{ + fVisMgr = plGlobalVisMgr::Instance(); +} + +plPageTreeMgr::~plPageTreeMgr() +{ + delete fSpaceTree; +} + +void plPageTreeMgr::AddNode(plSceneNode* node) +{ + ITrashSpaceTree(); + + node->Init(); + + fNodes.Append(node); +} + +void plPageTreeMgr::RemoveNode(plSceneNode* node) +{ + ITrashSpaceTree(); + + int idx = fNodes.Find(node); + if( idx != fNodes.kMissingIndex ) + fNodes.Remove(idx); +} + +void plPageTreeMgr::Reset() +{ + fNodes.Reset(); + + ITrashSpaceTree(); +} + +void plPageTreeMgr::ITrashSpaceTree() +{ + delete fSpaceTree; + fSpaceTree = nil; +} + +hsBool plPageTreeMgr::Harvest(plVolumeIsect* isect, hsTArray& levList) +{ + levList.SetCount(0); + if( !(GetSpaceTree() || IBuildSpaceTree()) ) + return false; + + static hsTArray list; + + GetSpaceTree()->HarvestLeaves(isect, list); + + int i; + for( i = 0; i < list.GetCount(); i++ ) + { + fNodes[list[i]]->Harvest(isect, levList); + } + + return levList.GetCount() > 0; +} + +#include "plProfile.h" +plProfile_CreateTimer("DrawableTime", "Draw", DrawableTime); +plProfile_Extern(RenderScene); + +int plPageTreeMgr::Render(plPipeline* pipe) +{ + // If we don't have a space tree and can't make one, just bail + if( !(GetSpaceTree() || IBuildSpaceTree()) ) + return 0; + + static hsTArray list; + list.SetCount(0); + + plProfile_BeginTiming(RenderScene); + + plVisMgr* visMgr = fDisableVisMgr ? nil : fVisMgr; + + if( visMgr ) + { + plProfile_Extern(VisEval); + plProfile_BeginTiming(VisEval); + visMgr->Eval(pipe->GetViewPositionWorld()); + plProfile_EndTiming(VisEval); + } + + pipe->BeginVisMgr(visMgr); + + IRefreshTree(pipe); + + IGetOcclusion(pipe, list); + pipe->HarvestVisible(GetSpaceTree(), list); + + static hsTArray levList; + levList.SetCount(0); + int i; + for( i = 0; i < list.GetCount(); i++ ) + { + fNodes[list[i]]->CollectForRender(pipe, levList, visMgr); + } + + int numDrawn = IRenderVisList(pipe, levList); + + IResetOcclusion(pipe); + + pipe->EndVisMgr(visMgr); + + plProfile_EndTiming(RenderScene); + + return numDrawn; + +} + +int plPageTreeMgr::IRenderVisList(plPipeline* pipe, hsTArray& levList) +{ + // Sort levList into sortedDrawList, which is just a list + // of drawable/visList pairs in ascending render priority order. + // visLists are just lists of span indices, but only of the + // spans which are visible (on screen and non-occluded and non-disabled). + static hsTArray sortedDrawList; + if( !ISortByLevel(pipe, levList, sortedDrawList) ) + { + return 0; + } + + int numDrawn = 0; + + plVisMgr* visMgr = fDisableVisMgr ? nil : fVisMgr; + + // Going through the list in order, if we hit a drawable which doesn't need + // its spans sorted, we can just draw it. + // If we hit a drawable which does need its spans sorted, we could just draw + // it, but that precludes sorting spans between drawables (like the player avatar + // sorting with normal scene objects). So when we hit a drawable which needs + // span sorting, we sort its spans with the spans of the next N drawables in + // the sorted list which have the same render priority and which also want their + // spans sorted. + int i; + for( i = 0; i < sortedDrawList.GetCount(); i++ ) + { + plDrawable* p = sortedDrawList[i].fDrawable; + + + plProfile_BeginLap(DrawableTime, p->GetKey()->GetUoid().GetObjectName()); + + if( sortedDrawList[i].fDrawable->GetNativeProperty(plDrawable::kPropSortSpans) ) + { + // IPrepForRenderSortingSpans increments "i" to the next index to be drawn (-1 so the i++ + // at the top of the loop is correct. + numDrawn += IPrepForRenderSortingSpans(pipe, sortedDrawList, i); + } + else + { + pipe->PrepForRender(sortedDrawList[i].fDrawable, sortedDrawList[i].fVisList, visMgr); + + pipe->Render(sortedDrawList[i].fDrawable, sortedDrawList[i].fVisList); + + numDrawn += sortedDrawList[i].fVisList.GetCount(); + + } + + plProfile_EndLap(DrawableTime, p->GetKey()->GetUoid().GetObjectName()); + } + + return numDrawn; +} + +hsBool plPageTreeMgr::ISortByLevel(plPipeline* pipe, hsTArray& drawList, hsTArray& sortedDrawList) +{ + sortedDrawList.SetCount(0); + + if( !drawList.GetCount() ) + return false; + + scratchList.SetCount(drawList.GetCount()); + + hsRadixSort::Elem* listTrav = nil; + int i; + for( i = 0; i < drawList.GetCount(); i++ ) + { + listTrav = &scratchList[i]; + listTrav->fBody = (void*)&drawList[i]; + listTrav->fNext = listTrav+1; + listTrav->fKey.fULong = drawList[i].fDrawable->GetRenderLevel().Level(); + } + listTrav->fNext = nil; + + hsRadixSort rad; + hsRadixSort::Elem* sortedList = rad.Sort(scratchList.AcquireArray(), hsRadixSort::kUnsigned); + + listTrav = sortedList; + + while( listTrav ) + { + plDrawVisList& drawVis = *(plDrawVisList*)listTrav->fBody; + sortedDrawList.Append(drawVis); + + listTrav = listTrav->fNext; + } + + return true; +} + +// Render from iDrawStart in drawVis list all drawables with the sort by spans property, well, sorting +// by spans. +// Returns the index of the last one drawn. +int plPageTreeMgr::IPrepForRenderSortingSpans(plPipeline* pipe, hsTArray& drawVis, int& iDrawStart) +{ + UInt32 renderLevel = drawVis[iDrawStart].fDrawable->GetRenderLevel().Level(); + + int i; + + static hsTArray drawables; + static hsTArray pairs; + + // Given the input drawVisList (list of drawable/visList pairs), we make two new + // lists. The list "drawables" is just the excerpted sub-list from drawVis starting + // from the input index and going through all compatible drawables (drawables which + // are appropriate to sort (and hence intermix) with the first drawable in the list. + // The second list is the drawableIndex/spanIndex pairs convenient for sorting (where + // drawIndex indexes into drawables and spanIndex indexes into drawVis[iDraw].fVisList. + // So pairs[i] resolves into + // drawables[pairs[i].fDrawable].fDrawable->GetSpan(pairs[i].fSpan) + + drawables.Append(&drawVis[iDrawStart]); + for( i = 0; i < drawVis[iDrawStart].fVisList.GetCount(); i++ ) + { + plDrawSpanPair* pair = pairs.Push(); + pair->fDrawable = 0; + pair->fSpan = drawVis[iDrawStart].fVisList[i]; + } + + int iDraw; + for( iDraw = iDrawStart+1; + (iDraw < drawVis.GetCount()) + && (drawVis[iDraw].fDrawable->GetRenderLevel().Level() == renderLevel) + && drawVis[iDraw].fDrawable->GetNativeProperty(plDrawable::kPropSortSpans); + iDraw++ ) + { + plDrawable* drawable = drawVis[iDraw].fDrawable; + hsTArray& visList = drawVis[iDraw].fVisList; + for( i = 0; i < visList.GetCount(); i++ ) + { + plDrawSpanPair* pair = pairs.Push(); + pair->fDrawable = drawables.GetCount(); + pair->fSpan = visList[i]; + } + drawables.Append(&drawVis[iDraw]); + } + + // Now that we have them in a more convenient format, sort them and render. + IRenderSortingSpans(pipe, drawables, pairs); + + int numDrawn = pairs.GetCount(); + + drawables.SetCount(0); + pairs.SetCount(0); + + iDrawStart = iDraw - 1; + + return numDrawn; +} + +hsBool plPageTreeMgr::IRenderSortingSpans(plPipeline* pipe, hsTArray& drawList, hsTArray& pairs) +{ + + if( !pairs.GetCount() ) + return false; + + hsPoint3 viewPos = pipe->GetViewPositionWorld(); + + plProfile_BeginTiming(DrawObjSort); + plProfile_IncCount(DrawObjSorted, pairs.GetCount()); + + hsRadixSort::Elem* listTrav; + scratchList.SetCount(pairs.GetCount()); + + // First, sort on distance to the camera (squared). + listTrav = nil; + int iSort = 0; + int i; + for( i = 0; i < pairs.GetCount(); i++ ) + { + plDrawable* drawable = drawList[pairs[i].fDrawable]->fDrawable; + + listTrav = &scratchList[iSort++]; + listTrav->fBody = (void*)*(UInt32*)&pairs[i]; + listTrav->fNext = listTrav + 1; + + if( drawable->GetNativeProperty(plDrawable::kPropSortAsOne) ) + { + const hsBounds3Ext& bnd = drawable->GetSpaceTree()->GetNode(drawable->GetSpaceTree()->GetRoot()).fWorldBounds; + plConst(hsScalar) kDistFudge(1.e-1f); + listTrav->fKey.fFloat = -(bnd.GetCenter() - viewPos).MagnitudeSquared() + hsScalar(pairs[i].fSpan) * kDistFudge; + } + else + { + const hsBounds3Ext& bnd = drawable->GetSpaceTree()->GetNode(pairs[i].fSpan).fWorldBounds; + listTrav->fKey.fFloat = -(bnd.GetCenter() - viewPos).MagnitudeSquared(); + } + } + if( !listTrav ) + { + plProfile_EndTiming(DrawObjSort); + return false; + } + listTrav->fNext = nil; + + hsRadixSort rad; + hsRadixSort::Elem* sortedList = rad.Sort(scratchList.AcquireArray(), 0); + + plProfile_EndTiming(DrawObjSort); + + static hsTArray visList; + visList.SetCount(0); + + plVisMgr* visMgr = fDisableVisMgr ? nil : fVisMgr; + + // Call PrepForRender on each of these bad boys. We only want to call + // PrepForRender once on each drawable, no matter how many times we're + // going to pass it off to be rendered (like if we render span 0 from + // drawable A, span 1 from drawable A, span 0 from drawable B, span 1 from Drawable A, we + // don't want to PrepForRender twice or three times on drawable A). + // So we're going to convert our sorted list back into a list of drawable/visList + // pairs. We could have done this with our original drawable/visList, but we've + // hopefully trimmed out some spans because of the fades. This drawable/visList + // isn't appropriate for rendering (because it doesn't let us switch back and forth + // from a drawable, but it's right for the PrepForRenderCall (which does things like + // face sorting). + for( i = 0; i < drawList.GetCount(); i++ ) + drawList[i]->fVisList.SetCount(0); + listTrav = sortedList; + while( listTrav ) + { + plDrawSpanPair& curPair = *(plDrawSpanPair*)&listTrav->fBody; + drawList[curPair.fDrawable]->fVisList.Append(curPair.fSpan); + listTrav = listTrav->fNext; + } + for( i = 0; i < drawList.GetCount(); i++ ) + { + pipe->PrepForRender(drawList[i]->fDrawable, drawList[i]->fVisList, visMgr); + } + + // We'd like to call Render once on a drawable for each contiguous + // set of spans (so we want to render span 0 and span 1 on a single Render + // of drawable A in the above, then render drawable B, then back to A). + // So we go through the sorted drawable/spanIndex pairs list, building + // a visList for as long as the drawable remains the same. When it + // changes, we render what we have so far, and start again with the + // next drawable. Repeat until done. + +#if 0 + listTrav = sortedList; + plDrawSpanPair& curPair = *(plDrawSpanPair*)&listTrav->fBody; + int curDraw = curPair.fDrawable; + visList.Append(curPair.fSpan); + listTrav = listTrav->fNext; + + while( listTrav ) + { + curPair = *(plDrawSpanPair*)&listTrav->fBody; + if( curPair.fDrawable != curDraw ) + { + pipe->Render(drawList[curDraw]->fDrawable, visList); + curDraw = curPair.fDrawable; + visList.SetCount(0); + visList.Append(curPair.fSpan); + } + else + { + visList.Append(curPair.fSpan); + } + listTrav = listTrav->fNext; + } + pipe->Render(drawList[curDraw]->fDrawable, visList); +#else + listTrav = sortedList; + plDrawSpanPair& curPair = *(plDrawSpanPair*)&listTrav->fBody; + int curDraw = curPair.fDrawable; + listTrav = listTrav->fNext; + + static hsTArray numDrawn; + numDrawn.SetCountAndZero(drawList.GetCount()); + + visList.Append(drawList[curDraw]->fVisList[numDrawn[curDraw]++]); + + while( listTrav ) + { + curPair = *(plDrawSpanPair*)&listTrav->fBody; + if( curPair.fDrawable != curDraw ) + { + pipe->Render(drawList[curDraw]->fDrawable, visList); + curDraw = curPair.fDrawable; + visList.SetCount(0); + } + visList.Append(drawList[curDraw]->fVisList[numDrawn[curDraw]++]); + listTrav = listTrav->fNext; + } + pipe->Render(drawList[curDraw]->fDrawable, visList); +#endif + + return true; +} + +hsBool plPageTreeMgr::IBuildSpaceTree() +{ + if( !fNodes.GetCount() ) + return false; + + plSpaceTreeMaker maker; + maker.Reset(); + int i; + for( i = 0; i < fNodes.GetCount(); i++ ) + { + maker.AddLeaf(fNodes[i]->GetSpaceTree()->GetWorldBounds(), fNodes[i]->GetSpaceTree()->IsEmpty()); + } + fSpaceTree = maker.MakeTree(); + + return true; +} + +hsBool plPageTreeMgr::IRefreshTree(plPipeline* pipe) +{ + int i; + for( i = 0; i < fNodes.GetCount(); i++ ) + { + if( fNodes[i]->GetSpaceTree()->IsDirty() ) + { + fNodes[i]->GetSpaceTree()->Refresh(); + + GetSpaceTree()->MoveLeaf(i, fNodes[i]->GetSpaceTree()->GetWorldBounds()); + + if( !fNodes[i]->GetSpaceTree()->IsEmpty() && fSpaceTree->HasLeafFlag(i, plSpaceTreeNode::kDisabled) ) + fSpaceTree->SetLeafFlag(i, plSpaceTreeNode::kDisabled, false); + + } + } + + GetSpaceTree()->SetViewPos(pipe->GetViewPositionWorld()); + + GetSpaceTree()->Refresh(); + + return true; +} + +void plPageTreeMgr::AddOccluderList(const hsTArray occList) +{ + int iStart = fOccluders.GetCount(); + fOccluders.Expand(iStart + occList.GetCount()); + fOccluders.SetCount(iStart + occList.GetCount()); + + plVisMgr* visMgr = fDisableVisMgr ? nil : fVisMgr; + + if( visMgr ) + { + const hsBitVector& visSet = visMgr->GetVisSet(); + const hsBitVector& visNot = visMgr->GetVisNot(); + int i; + for( i = 0; i < occList.GetCount(); i++ ) + { + if( occList[i] && !occList[i]->InVisNot(visNot) && occList[i]->InVisSet(visSet) ) + fOccluders[iStart++] = occList[i]; + } + } + else + { + int i; + for( i = 0; i < occList.GetCount(); i++ ) + { + if( occList[i] ) + fOccluders[iStart++] = occList[i]; + } + } + fOccluders.SetCount(iStart); + +} + +void plPageTreeMgr::IAddCullPolyList(const hsTArray& polyList) +{ + int iStart = fCullPolys.GetCount(); + fCullPolys.Expand(iStart + polyList.GetCount()); + fCullPolys.SetCount(iStart + polyList.GetCount()); + int i; + for( i = 0; i < polyList.GetCount(); i++ ) + { + fCullPolys[i + iStart] = &polyList[i]; + } +} + +void plPageTreeMgr::ISortCullPolys(plPipeline* pipe) +{ + fSortedCullPolys.SetCount(0); + if( !fCullPolys.GetCount() ) + return; + + const int kMaxCullPolys = 300; + int numSubmit = 0; + + hsPoint3 viewPos = pipe->GetViewPositionWorld(); + + hsRadixSort::Elem* listTrav; + scratchList.SetCount(fCullPolys.GetCount()); + int i; + for( i = 0; i < fCullPolys.GetCount(); i++ ) + { + hsBool backFace = fCullPolys[i]->fNorm.InnerProduct(viewPos) + fCullPolys[i]->fDist <= 0; + if( backFace ) + { + if( !fCullPolys[i]->IsHole() && !fCullPolys[i]->IsTwoSided() ) + continue; + } + else + { + if( fCullPolys[i]->IsHole() ) + continue; + } + + listTrav = &scratchList[numSubmit]; + listTrav->fBody = (void*)fCullPolys[i]; + listTrav->fNext = listTrav + 1; + listTrav->fKey.fFloat = (fCullPolys[i]->GetCenter() - viewPos).MagnitudeSquared(); + + numSubmit++; + } + if( !numSubmit ) + return; + + listTrav->fNext = nil; + + hsRadixSort rad; + hsRadixSort::Elem* sortedList = rad.Sort(scratchList.AcquireArray(), 0); + listTrav = sortedList; + + if( numSubmit > kMaxCullPolys ) + numSubmit = kMaxCullPolys; + + fSortedCullPolys.SetCount(numSubmit); + + for( i = 0; i < numSubmit; i++ ) + { + fSortedCullPolys[i] = (const plCullPoly*)listTrav->fBody; + listTrav = listTrav->fNext; + } +} + +hsBool plPageTreeMgr::IGetCullPolys(plPipeline* pipe) +{ + if( !fOccluders.GetCount() ) + return false; + + plProfile_BeginTiming(DrawOccSort); + + hsRadixSort::Elem* listTrav = nil; + scratchList.SetCount(fOccluders.GetCount()); + + hsPoint3 viewPos = pipe->GetViewPositionWorld(); + + // cull test the occluders submitted + int numSubmit = 0; + int i; + for( i = 0; i < fOccluders.GetCount(); i++ ) + { + if( pipe->TestVisibleWorld(fOccluders[i]->GetWorldBounds()) ) + { + hsScalar invDist = -hsFastMath::InvSqrtAppr((viewPos - fOccluders[i]->GetWorldBounds().GetCenter()).MagnitudeSquared()); + listTrav = &scratchList[numSubmit++]; + listTrav->fBody = (void*)fOccluders[i]; + listTrav->fNext = listTrav+1; + listTrav->fKey.fFloat = fOccluders[i]->GetPriority() * invDist; + } + } + if( !listTrav ) + { + plProfile_EndTiming(DrawOccSort); + return false; + } + + listTrav->fNext = nil; + + + // Sort the occluders by priority + hsRadixSort rad; + hsRadixSort::Elem* sortedList = rad.Sort(scratchList.AcquireArray(), 0); + listTrav = sortedList; + + const UInt32 kMaxOccluders = 1000; + if( numSubmit > kMaxOccluders ) + numSubmit = kMaxOccluders; + + plProfile_IncCount(DrawOccUsed, numSubmit); + + // Take the polys from the first N of them + for( i = 0; i < numSubmit; i++ ) + { + plOccluder* occ = (plOccluder*)listTrav->fBody; + IAddCullPolyList(occ->GetWorldPolyList()); + + listTrav = listTrav->fNext; + } + + plProfile_EndTiming(DrawOccSort); + + return fCullPolys.GetCount() > 0; +} + +hsBool plPageTreeMgr::IGetOcclusion(plPipeline* pipe, hsTArray& list) +{ + plProfile_BeginTiming(DrawOccBuild); + + fCullPolys.SetCount(0); + fOccluders.SetCount(0); + int i; + for( i = 0; i < fNodes.GetCount(); i++ ) + { + fNodes[i]->SubmitOccluders(this); + } + + if( !IGetCullPolys(pipe) ) + { + plProfile_EndTiming(DrawOccBuild); + return false; + } + + plProfile_IncCount(DrawOccPolyProc, fCullPolys.GetCount()); + + plProfile_BeginTiming(DrawOccPolySort); + ISortCullPolys(pipe); + plProfile_EndTiming(DrawOccPolySort); + + if( fSortedCullPolys.GetCount() ) + pipe->SubmitOccluders(fSortedCullPolys); + + plProfile_EndTiming(DrawOccBuild); + + return fSortedCullPolys.GetCount() > 0; +} + +void plPageTreeMgr::IResetOcclusion(plPipeline* pipe) +{ + fCullPolys.SetCount(0); + fSortedCullPolys.SetCount(0); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPageTreeMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPageTreeMgr.h new file mode 100644 index 00000000..48119f7d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPageTreeMgr.h @@ -0,0 +1,117 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPageTreeMgr_inc +#define plPageTreeMgr_inc + +#include "hsTemplates.h" + +class plSceneNode; +class plSpaceTree; +class plPipeline; +class plCullPoly; +class plOccluder; +class plDrawable; +class plDrawVisList; +class plVolumeIsect; +class plVisMgr; + +class plDrawSpanPair +{ +public: + plDrawSpanPair() {} + plDrawSpanPair(UInt16 d, UInt16 s) : fDrawable(d), fSpan(s) {} + UInt16 fDrawable; + UInt16 fSpan; +}; + +class plDrawVisList +{ +public: + plDrawVisList() : fDrawable(nil) {} + virtual ~plDrawVisList() {} + + plDrawable* fDrawable; + hsTArray fVisList; + + plDrawVisList& operator=(const plDrawVisList& v) { fDrawable = v.fDrawable; fVisList = v.fVisList; return *this; } +}; + +class plPageTreeMgr +{ +protected: + hsTArray fNodes; + + plSpaceTree* fSpaceTree; + plVisMgr* fVisMgr; + + static hsBool fDisableVisMgr; + + hsTArray fOccluders; + hsTArray fCullPolys; + hsTArray fSortedCullPolys; + + void ITrashSpaceTree(); + hsBool IBuildSpaceTree(); + hsBool IRefreshTree(plPipeline* pipe); + void ISortCullPolys(plPipeline* pipe); + hsBool IGetOcclusion(plPipeline* pipe, hsTArray& list); + hsBool IGetCullPolys(plPipeline* pipe); + void IResetOcclusion(plPipeline* pipe); + void IAddCullPolyList(const hsTArray& polyList); + + hsBool ISortByLevel(plPipeline* pipe, hsTArray& drawList, hsTArray& sortedDrawList); + int IPrepForRenderSortingSpans(plPipeline* pipe, hsTArray& drawVis, int& iDrawStart); + hsBool IRenderSortingSpans(plPipeline* pipe, hsTArray& drawList, hsTArray& pairs); + int IRenderVisList(plPipeline* pipe, hsTArray& visList); + +public: + plPageTreeMgr(); + virtual ~plPageTreeMgr(); + + const hsTArray& GetNodes() const { return fNodes; } + + void AddNode(plSceneNode* node); + void RemoveNode(plSceneNode* node); + virtual void Reset(); // remove all nodes, nuke the space tree + virtual hsBool Empty() const { return !fNodes.GetCount(); } + + virtual int Render(plPipeline* pipe); + + hsBool Harvest(plVolumeIsect* isect, hsTArray& levList); + + void AddOccluderList(const hsTArray occList); + + plSpaceTree* GetSpaceTree() { if( !fSpaceTree ) IBuildSpaceTree(); return fSpaceTree; } + + void SetVisMgr(plVisMgr* visMgr) { fVisMgr = visMgr; } + plVisMgr* GetVisMgr() const { return fVisMgr; } + + static void EnableVisMgr(hsBool on) { fDisableVisMgr = !on; } + static hsBool VisMgrEnabled() { return !fDisableVisMgr; } +}; + +#endif // plPageTreeMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPostEffectMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPostEffectMod.cpp new file mode 100644 index 00000000..d0b3e8b7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPostEffectMod.cpp @@ -0,0 +1,321 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plPostEffectMod.h" +#include "plPageTreeMgr.h" +#include "plSceneNode.h" +#include "plRenderRequest.h" + +#include "../plPipeline/plRenderTarget.h" + +#include "../plMessage/plRenderRequestMsg.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plRenderMsg.h" + +#include "../pnMessage/plRefMsg.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "plDrawable.h" +#include "plPipeline.h" +#include "plgDispatch.h" +#include "hsResMgr.h" + + +plPostEffectMod::plPostEffectMod() +: fHither(1.f), + fYon(100.f), + fFovX(hsScalarPI * 0.25f), + fFovY(hsScalarPI * 0.25f * 0.75f), + fPageMgr(nil), + fRenderTarget(nil), + fRenderRequest(nil) +{ + fDefaultW2C = hsMatrix44::IdentityMatrix(); + fDefaultC2W = hsMatrix44::IdentityMatrix(); + + ISetupRenderRequest(); +} + +plPostEffectMod::~plPostEffectMod() +{ + IDestroyRenderRequest(); +} + +void plPostEffectMod::ISetupRenderRequest() +{ + UInt32 rtFlags = 0; + + // If we go to rendering to sub-window, we'll want to explicitly set width and height + UInt32 width = 0; + UInt32 height = 0; + + UInt32 colorDepth = 0; + UInt32 zDepth = 0; + UInt32 stencilDepth = 0; + + fRenderRequest = TRACKED_NEW plRenderRequest; + UInt32 renderState = plPipeline::kRenderNormal + | plPipeline::kRenderNoProjection + | plPipeline::kRenderNoLights + | plPipeline::kRenderClearDepth; + fRenderRequest->SetRenderState(renderState); + + fRenderRequest->SetDrawableMask(plDrawable::kNormal); + fRenderRequest->SetSubDrawableMask(plDrawable::kSubAllTypes); + + fRenderRequest->SetPerspective(); + + fRenderRequest->SetRenderTarget(fRenderTarget); + + fPageMgr = TRACKED_NEW plPageTreeMgr; + + fRenderRequest->SetPageTreeMgr(fPageMgr); + + fRenderRequest->SetPriority(1.f); + + IUpdateRenderRequest(); +} +void plPostEffectMod::EnableLightsOnRenderRequest( void ) +{ + fRenderRequest->SetRenderState( fRenderRequest->GetRenderState() & ~plPipeline::kRenderNoLights ); +} + +void plPostEffectMod::IDestroyRenderRequest() +{ + delete fRenderTarget; + fRenderTarget = nil; + delete fRenderRequest; + fRenderRequest = nil; + delete fPageMgr; + fPageMgr = nil; +} + +void plPostEffectMod::IRegisterForRenderMsg(hsBool on) +{ + if( on ) + plgDispatch::Dispatch()->RegisterForExactType(plRenderMsg::Index(), GetKey()); + else + plgDispatch::Dispatch()->UnRegisterForExactType(plRenderMsg::Index(), GetKey()); +} + +void plPostEffectMod::ISetEnable(hsBool on) +{ + if( on ) + { + IRegisterForRenderMsg(true); + fState.SetBit(kEnabled); + } + else + { + IRegisterForRenderMsg(false); + fState.ClearBit(kEnabled); + } +} + +hsBool plPostEffectMod::IIsEnabled() const +{ + return /*GetTarget() &&*/ !fPageMgr->Empty() && fState.IsBitSet(kEnabled); +} + +hsBool plPostEffectMod::IEval(double secs, hsScalar del, UInt32 dirty) +{ + return false; +} + +void plPostEffectMod::IUpdateRenderRequest() +{ + fRenderRequest->SetHither(fHither); + fRenderRequest->SetYon(fYon); + + fRenderRequest->SetFovX(fFovX); + fRenderRequest->SetFovY(fFovY); + + if( GetTarget() ) + { + hsMatrix44 w2c = GetTarget()->GetWorldToLocal(); + hsMatrix44 c2w = GetTarget()->GetLocalToWorld(); + + int i; + for( i = 0; i < 4; i++ ) + { + w2c.fMap[2][i] *= -1.f; + c2w.fMap[i][2] *= -1.f; + } + w2c.NotIdentity(); + c2w.NotIdentity(); + + fRenderRequest->SetCameraTransform(w2c, c2w); + } + else + fRenderRequest->SetCameraTransform( fDefaultW2C, fDefaultC2W ); +// fRenderRequest->SetCameraTransform(hsMatrix44::IdentityMatrix(), hsMatrix44::IdentityMatrix()); +} + +// If translating from a scene object, send WorldToLocal() and LocalToWorld(), in that order +void plPostEffectMod::SetWorldToCamera( hsMatrix44 &w2c, hsMatrix44 &c2w ) +{ + int i; + + + fDefaultW2C = w2c; + fDefaultC2W = c2w; + + for( i = 0; i < 4; i++ ) + { + fDefaultW2C.fMap[2][i] *= -1.f; + fDefaultC2W.fMap[i][2] *= -1.f; + } + fDefaultW2C.NotIdentity(); + fDefaultC2W.NotIdentity(); +} + +void plPostEffectMod::GetDefaultWorldToCamera( hsMatrix44 &w2c, hsMatrix44 &c2w ) +{ + w2c = fDefaultW2C; + c2w = fDefaultC2W; +} + +void plPostEffectMod::ISubmitRequest() +{ + hsAssert(fState.IsBitSet(kEnabled), "Submitting request when not active"); + // No target is now valid... +// hsAssert(GetTarget(), "Submitting request without target loaded"); + + IUpdateRenderRequest(); + + plRenderRequestMsg* req = TRACKED_NEW plRenderRequestMsg(GetKey(), fRenderRequest); + plgDispatch::MsgSend(req); +} + +void plPostEffectMod::IAddToPageMgr(plSceneNode* node) +{ + fPageMgr->AddNode(node); +} + +void plPostEffectMod::IRemoveFromPageMgr(plSceneNode* node) +{ + fPageMgr->RemoveNode(node); +} + +#include "plProfile.h" +plProfile_CreateTimer("PostEffect", "RenderSetup", PostEffect); + +hsBool plPostEffectMod::MsgReceive(plMessage* msg) +{ + plRenderMsg* rend = plRenderMsg::ConvertNoRef(msg); + if( rend && IIsEnabled() ) + { + plProfile_BeginLap(PostEffect, this->GetKey()->GetUoid().GetObjectName()); + ISubmitRequest(); + plProfile_EndLap(PostEffect, this->GetKey()->GetUoid().GetObjectName()); + + return true; + } + plAnimCmdMsg* anim = plAnimCmdMsg::ConvertNoRef(msg); + if( anim ) + { + if( anim->Cmd(plAnimCmdMsg::kContinue) ) + ISetEnable(true); + else if( anim->Cmd(plAnimCmdMsg::kStop) ) + ISetEnable(false); + else if( anim->Cmd(plAnimCmdMsg::kToggleState) ) + ISetEnable(!fState.IsBitSet(kEnabled)); + + return true; + } + plGenRefMsg* ref = plGenRefMsg::ConvertNoRef(msg); + if( ref ) + { + switch( ref->fType ) + { + case kNodeRef: + if( ref->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest) ) + { + IAddToPageMgr(plSceneNode::ConvertNoRef(ref->GetRef())); + } + else if( ref->GetContext() & plRefMsg::kOnReplace ) + { + IRemoveFromPageMgr(plSceneNode::ConvertNoRef(ref->GetOldRef())); + IAddToPageMgr(plSceneNode::ConvertNoRef(ref->GetRef())); + } + else if( ref->GetContext() & (plRefMsg::kOnRemove | plRefMsg::kOnDestroy) ) + { + IRemoveFromPageMgr(plSceneNode::ConvertNoRef(ref->GetRef())); + } + break; + } + return true; + } + + return plSingleModifier::MsgReceive(msg); +} + +void plPostEffectMod::Read(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Read(s, mgr); + + fState.Read(s); + +#if 0 // FORCE ENABLE ON LOAD - ONLY FOR DEBUGGING + ISetEnable(true); +#endif // FORCE ENABLE ON LOAD - ONLY FOR DEBUGGING + + fHither = s->ReadSwapScalar(); + fYon = s->ReadSwapScalar(); + fFovX = s->ReadSwapScalar(); + fFovY = s->ReadSwapScalar(); + + fNodeKey = mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, kNodeRef), plRefFlags::kPassiveRef); + + fDefaultW2C.Read( s ); + fDefaultC2W.Read( s ); + + IUpdateRenderRequest(); +} + +void plPostEffectMod::Write(hsStream* s, hsResMgr* mgr) +{ + plSingleModifier::Write(s, mgr); + + fState.Write(s); + + s->WriteSwapScalar(fHither); + s->WriteSwapScalar(fYon); + s->WriteSwapScalar(fFovX); + s->WriteSwapScalar(fFovY); + + mgr->WriteKey(s, fNodeKey); + + fDefaultW2C.Write( s ); + fDefaultC2W.Write( s ); +} + +const plViewTransform& plPostEffectMod::GetViewTransform() +{ + IUpdateRenderRequest(); + return fRenderRequest->GetViewTransform(); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPostEffectMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPostEffectMod.h new file mode 100644 index 00000000..da4c656c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plPostEffectMod.h @@ -0,0 +1,126 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plPostEffectMod_inc +#define plPostEffectMod_inc + +#include "../pnModifier/plSingleModifier.h" + +#include "hsMatrix44.h" +#include "hsBitVector.h" + +class plSceneNode; +class plPageTreeMgr; +class plMessage; +class plRenderTarget; +class plRenderRequest; +class plViewTransform; + +class plPostEffectMod : public plSingleModifier +{ +public: + enum plPostEffectModStates { + kEnabled = 0 + }; + + enum { + kNodeRef = 0x0 + }; +protected: + + hsBitVector fState; + + hsScalar fHither; + hsScalar fYon; + + hsScalar fFovX; + hsScalar fFovY; + + plKey fNodeKey; + plPageTreeMgr* fPageMgr; + + plRenderTarget* fRenderTarget; + plRenderRequest* fRenderRequest; + + hsMatrix44 fDefaultW2C, fDefaultC2W; + + + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); // called only by owner object's Eval() + + void ISetupRenderRequest(); + void IDestroyRenderRequest(); + void IUpdateRenderRequest(); + + void IRegisterForRenderMsg(hsBool on); + void ISubmitRequest(); + + void IAddToPageMgr(plSceneNode* node); + void IRemoveFromPageMgr(plSceneNode* node); + + void ISetEnable(hsBool on); + hsBool IIsEnabled() const; + +public: + plPostEffectMod(); + virtual ~plPostEffectMod(); + + CLASSNAME_REGISTER( plPostEffectMod ); + GETINTERFACE_ANY( plPostEffectMod, plSingleModifier ); + + + virtual hsBool MsgReceive(plMessage* pMsg); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + void GetDefaultWorldToCamera( hsMatrix44 &w2c, hsMatrix44 &c2w ); + + // Export only + void SetNodeKey(plKey key) { fNodeKey = key; } + plKey GetNodeKey() const { return fNodeKey; } + + void SetHither(hsScalar h) { fHither = h; } + void SetYon(hsScalar y) { fYon = y; } + void SetFovX(hsScalar f) { fFovX = f; } + void SetFovY(hsScalar f) { fFovY = f; } + + hsScalar GetHither() const { return fHither; } + hsScalar GetYon() const { return fYon; } + hsScalar GetFovX() const { return fFovX; } + hsScalar GetFovY() const { return fFovY; } + + plPageTreeMgr* GetPageMgr() const { return fPageMgr; } + + const plViewTransform& GetViewTransform(); + + // If translating from a scene object, send WorldToLocal() and LocalToWorld(), in that order + void SetWorldToCamera( hsMatrix44 &w2c, hsMatrix44 &c2w ); + + // Very bad + void EnableLightsOnRenderRequest( void ); +}; + +#endif // plPostEffectMod_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceMgr.cpp new file mode 100644 index 00000000..b8c6b2ee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceMgr.cpp @@ -0,0 +1,255 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plRelevanceMgr.h" +#include "plRelevanceRegion.h" +#include "../plIntersect/plRegionBase.h" +#include "hsStream.h" +#include "hsStringTokenizer.h" + +plRelevanceMgr* plRelevanceMgr::fInstance = nil; + +plRelevanceMgr::plRelevanceMgr() : fEnabled(true) +{ +} + +void plRelevanceMgr::Init() +{ + fInstance = TRACKED_NEW plRelevanceMgr; + fInstance->RegisterAs(kRelevanceMgr_KEY); +} + +void plRelevanceMgr::DeInit() +{ + if (fInstance) + { + fInstance->UnRegisterAs(kRelevanceMgr_KEY); + fInstance = nil; + } +} + +void plRelevanceMgr::IAddRegion(plRelevanceRegion *region) +{ + int i; + int dstIdx = fRegions.GetCount(); + for (i = 0; i < fRegions.GetCount(); i++) + { + if (fRegions[i] == nil) + { + dstIdx = i; + break; + } + } + + if (dstIdx == fRegions.GetCount()) + fRegions.Append(region); + else + fRegions[i] = region; + + region->SetMgrIndex(dstIdx + 1); +} + +void plRelevanceMgr::IRemoveRegion(plRelevanceRegion *region) +{ + fRegions[region->fMgrIdx - 1] = nil; +} + +void plRelevanceMgr::SetRegionVectors(const hsPoint3 &pos, hsBitVector ®ionsImIn, hsBitVector ®ionsICareAbout) +{ + regionsImIn.Clear(); + regionsICareAbout.Clear(); + regionsICareAbout.SetBit(0, true); // Always care about region zero, the special "No region" node + + hsBool inAnyRegion = false; + + int i; + for (i = 0; i < fRegions.GetCount(); i++) + { + if (fRegions[i] && fRegions[i]->fRegion->IsInside(pos)) + { + regionsImIn.SetBit(i + 1, true); + regionsICareAbout |= fRegions[i]->fRegionsICareAbout; + inAnyRegion = true; + } + } + + // If I'm not in any region, that means I'm in the special zero region and care about everything. + if (!inAnyRegion) + { + regionsImIn.SetBit(0, true); + regionsICareAbout.Set(fRegions.GetCount()); + } +} + +UInt32 plRelevanceMgr::GetNumRegions() const +{ + int i; + + for (i = fRegions.GetCount(); i > 0 && fRegions[i - 1] == nil; i--); + + return i + 1; // Add 1 for the special zero-region +} + + +hsBool plRelevanceMgr::MsgReceive(plMessage* msg) +{ + plGenRefMsg *genMsg = plGenRefMsg::ConvertNoRef(msg); + if (genMsg) + { + plRelevanceRegion *region = plRelevanceRegion::ConvertNoRef(genMsg->GetRef()); + if( genMsg->GetContext() & (plRefMsg::kOnCreate) ) + { + IAddRegion(region); + } + else if( genMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + IRemoveRegion(region); + } + return true; + } + + return hsKeyedObject::MsgReceive(msg); +} + +UInt32 plRelevanceMgr::GetIndex(char *regionName) +{ + int i; + for (i = 0; i < fRegions.GetCount(); i++) + { + if (fRegions[i] && !stricmp(regionName, fRegions[i]->GetKeyName())) + return i + 1; + } + + return -1; +} + +void plRelevanceMgr::MarkRegion(UInt32 localIdx, UInt32 remoteIdx, hsBool doICare) +{ + if (localIdx == (UInt32)-1 || remoteIdx == (UInt32)-1) + return; + + if (localIdx - 1 >= fRegions.GetCount() || remoteIdx - 1 >= fRegions.GetCount() || fRegions[localIdx - 1] == nil) + return; + + fRegions[localIdx - 1]->fRegionsICareAbout.SetBit(remoteIdx, doICare); +} + +// tiny class for the function below +class plRegionInfo +{ +public: + char *fName; + int fIndex; + + plRegionInfo() : fName(nil), fIndex(-1) {} + ~plRegionInfo() { delete [] fName; } +}; + +/* +* This function expects a CSV file representing the matrix +* +* name1 name2 name3 +* name1 value value value +* name2 value value value +* name3 value value value +* +* where the value determines how much the that row's region cares about the region in the current column. +* (Currently, the possible values are: +* 0: Doesn't care +* 1 or greater: row cares about column +*/ +void plRelevanceMgr::ParseCsvInput(hsStream *s) +{ + const int kBufSize = 512; + char buff[kBufSize]; + hsTArray regions; + hsStringTokenizer toke; + hsBool firstLine = true; + + while (!s->AtEnd()) + { + if (!s->ReadLn(buff, kBufSize)) + break; + + if (firstLine) + { + firstLine = false; + toke.Reset(buff, ","); + + while (toke.Next(buff, kBufSize)) + { + if (strcmp(buff, "") == 0) + continue; // ignore the initial blank one + + plRegionInfo *info = TRACKED_NEW plRegionInfo; + regions.Append(info); + info->fName = hsStrcpy(buff); + info->fIndex = GetIndex(buff); + } + } + else // parsing actual settings. + { + toke.Reset(buff, ","); + if (!toke.Next(buff, kBufSize)) + continue; + + int rowIndex = GetIndex(buff); + int column = 0; + while (toke.Next(buff, kBufSize) && column < regions.GetCount()) + { + int value = atoi(buff); + MarkRegion(rowIndex, regions[column]->fIndex, value != 0); + + column++; + } + } + } + + int i; + for (i = regions.GetCount() - 1; i >= 0; i--) + delete regions[i]; +} + +std::string plRelevanceMgr::GetRegionNames(hsBitVector regions) +{ + std::string retVal = ""; + if (regions.IsBitSet(0)) + retVal = "-Nowhere (0)-"; + + for (int i = 0; i < fRegions.GetCount(); ++i) + { + if (regions.IsBitSet(i + 1)) + { + if (retVal.length() != 0) + retVal += ", "; + if (fRegions[i]) + retVal += fRegions[i]->GetKeyName(); + } + } + + if (retVal.length() == 0) + retVal = ""; + return retVal; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceMgr.h new file mode 100644 index 00000000..ff21cf4a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceMgr.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plRelevanceMgr_inc +#define plRelevanceMgr_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsGeometry3.h" +#include "hsTemplates.h" +#include "hsBitVector.h" +#include "hsStlUtils.h" + + +class plRelevanceRegion; +class hsStream; + +class plRelevanceMgr : public hsKeyedObject +{ +protected: + static plRelevanceMgr *fInstance; +public: + static plRelevanceMgr *Instance() { return fInstance; } + + static void Init(); + static void DeInit(); + +protected: + hsTArray fRegions; + hsBool fEnabled; + + void IAddRegion(plRelevanceRegion *); + void IRemoveRegion(plRelevanceRegion *); + +public: + plRelevanceMgr(); + + CLASSNAME_REGISTER( plRelevanceMgr ); + GETINTERFACE_ANY( plRelevanceMgr, hsKeyedObject ); + + virtual hsBool MsgReceive(plMessage* msg); + + hsBool GetEnabled() { return fEnabled; } + void SetEnabled(hsBool val) { fEnabled = val; } + + UInt32 GetIndex(char *regionName); + void MarkRegion(UInt32 localIdx, UInt32 remoteIdx, hsBool doICare); + void SetRegionVectors(const hsPoint3 &pos, hsBitVector ®ionsImIn, hsBitVector ®ionsICareAbout); + UInt32 GetNumRegions() const; // includes the secret 0 region in its count + void ParseCsvInput(hsStream *s); + + std::string GetRegionNames(hsBitVector regions); +}; + +#endif // plRelevanceMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceRegion.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceRegion.cpp new file mode 100644 index 00000000..ecc9f996 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceRegion.cpp @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsResMgr.h" +#include "plRelevanceRegion.h" +#include "plRelevanceMgr.h" +#include "../plIntersect/plRegionBase.h" + +void plRelevanceRegion::Read(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Read(s, mgr); + + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0), plRefFlags::kActiveRef); + + // Added to the manager when read in. + // Removed when paged out, due to passive ref. + if (plRelevanceMgr::Instance()) + { + plGenRefMsg *msg = TRACKED_NEW plGenRefMsg(plRelevanceMgr::Instance()->GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(GetKey(), msg, plRefFlags::kPassiveRef); + } +} + +void plRelevanceRegion::Write(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Write(s, mgr); + + mgr->WriteKey(s, fRegion); +} + +hsBool plRelevanceRegion::MsgReceive(plMessage* msg) +{ + plGenRefMsg *genMsg = plGenRefMsg::ConvertNoRef(msg); + if (genMsg) + { + plRegionBase *base = plRegionBase::ConvertNoRef(genMsg->GetRef()); + if( genMsg->GetContext() & (plRefMsg::kOnCreate) ) + { + fRegion = base; + } + else if( genMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + fRegion = nil; + } + return true; + } + + return plObjInterface::MsgReceive(msg); +} + + +void plRelevanceRegion::SetMgrIndex(UInt32 index) +{ + if (fMgrIdx != (UInt32)-1) + fRegionsICareAbout.SetBit(fMgrIdx, false); + + fMgrIdx = index; + fRegionsICareAbout.SetBit(index, true); // I care about myself. Awww... +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceRegion.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceRegion.h new file mode 100644 index 00000000..e19d9246 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRelevanceRegion.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plRelevanceRegion_inc +#define plRelevanceRegion_inc + +#include "../pnSceneObject/plObjInterface.h" + +class plRelevanceMgr; +class plRegionBase; + +class plRelevanceRegion : public plObjInterface +{ + friend class plRelevanceMgr; + +protected: + plRegionBase *fRegion; + hsBitVector fRegionsICareAbout; + UInt32 fMgrIdx; + +public: + plRelevanceRegion() : fRegion(nil), fMgrIdx((UInt32)-1) {} + virtual ~plRelevanceRegion() {} + + CLASSNAME_REGISTER( plRelevanceRegion ); + GETINTERFACE_ANY( plRelevanceRegion, plObjInterface ); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) {} + virtual Int32 GetNumProperties() const { return 1; } + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void SetMgrIndex(UInt32 idx); +}; + +#endif // plRelevanceRegion_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRenderRequest.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRenderRequest.cpp new file mode 100644 index 00000000..94ed7555 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRenderRequest.cpp @@ -0,0 +1,154 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plRenderRequest.h" +#include "plPageTreeMgr.h" +#include "../plPipeline/plRenderTarget.h" +#include "hsFastMath.h" +#include "hsStream.h" +#include "plPipeline.h" +#include "../plMessage/plRenderRequestMsg.h" +#include "plgDispatch.h" +#include "plVisMgr.h" + +plRenderRequest::plRenderRequest() +: fRenderTarget(nil), + fPageMgr(nil), + fAck(nil), + fOverrideMat(nil), + fEraseMat(nil), + fDrawableMask(UInt32(-1)), + fSubDrawableMask(UInt32(-1)), + fRenderState(0), + fClearDepth(1.f), + fFogStart(-1.f), + fClearDrawable(nil), + fPriority(-1.e6f), + fUserData(0), + fIgnoreOccluders(false) +{ + fClearColor.Set(0,0,0,1.f); + + fLocalToWorld.Reset(); + fWorldToLocal.Reset(); + +} + +plRenderRequest::~plRenderRequest() +{ +} + +void plRenderRequest::SetLocalTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) +{ + fLocalToWorld = l2w; + fWorldToLocal = w2l; +} + +void plRenderRequest::Read(hsStream* s, hsResMgr* mgr) +{ + fClearDrawable = nil; + fRenderTarget = nil; + fPageMgr = nil; + + fDrawableMask = s->ReadSwap32(); + fSubDrawableMask = s->ReadSwap32(); + + fRenderState = s->ReadSwap32(); + + fLocalToWorld.Read(s); + fWorldToLocal.Read(s); + + fPriority = s->ReadSwapScalar(); +} + +void plRenderRequest::Write(hsStream* s, hsResMgr* mgr) +{ + s->WriteSwap32(fDrawableMask); + s->WriteSwap32(fSubDrawableMask); + + s->WriteSwap32(fRenderState); + + fLocalToWorld.Write(s); + fWorldToLocal.Write(s); + + s->WriteSwapScalar(fPriority); +} + +void plRenderRequest::Render(plPipeline* pipe, plPageTreeMgr* pageMgr) +{ + if( !fVisForce.Empty() ) + { + plGlobalVisMgr::Instance()->DisableNormal(); + plGlobalVisMgr::Instance()->ForceVisSets(fVisForce, false); + } + + pipe->PushRenderRequest(this); + + pipe->ClearRenderTarget(GetClearDrawable()); + + int numDrawn = 0; + if( GetPageTreeMgr() ) + numDrawn = GetPageTreeMgr()->Render(pipe); + else + numDrawn = pageMgr->Render(pipe); + + pipe->PopRenderRequest(this); + + if( GetAck() ) + { + plRenderRequestAck* ack = TRACKED_NEW plRenderRequestAck( GetAck(), GetUserData() ); + ack->SetNumDrawn(numDrawn); + plgDispatch::MsgSend( ack ); + } +} + +void plRenderRequest::SetRenderTarget(plRenderTarget* t) +{ + if( t != fRenderTarget ) + { + fRenderTarget = t; + + if( fRenderTarget ) + { + fViewTransform.SetWidth(t->GetWidth()); + fViewTransform.SetHeight(t->GetHeight()); + } + } +} + +void plRenderRequest::SetVisForce(const hsBitVector& b) +{ + if( b.Empty() ) + fVisForce.Reset(); + else + fVisForce = b; +} + +hsBool plRenderRequest::GetRenderCharacters() const +{ + return fVisForce.IsBitSet(plVisMgr::kCharacter); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRenderRequest.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRenderRequest.h new file mode 100644 index 00000000..658a40dc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plRenderRequest.h @@ -0,0 +1,191 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRenderRequest_inc +#define plRenderRequest_inc + +#include "hsMatrix44.h" +#include "hsColorRGBA.h" +#include "plViewTransform.h" +#include "hsRefCnt.h" +#include "hsBitVector.h" + +#include "../pnKeyedObject/plKey.h" +#include "../plMessage/plRenderRequestMsg.h" + +class plRenderTarget; +class plPageTreeMgr; +class hsStream; +class hsResMgr; +class plDrawable; +class hsGMaterial; +class plPipeline; + +class plRenderRequest : public plRenderRequestBase +{ +public: +protected: + UInt32 fRenderState; // Or'ed from plPipeline::RenderStateSettings::kRender* + + plDrawable* fClearDrawable; + plRenderTarget* fRenderTarget; + plPageTreeMgr* fPageMgr; + + hsGMaterial* fOverrideMat; + hsGMaterial* fEraseMat; + + plKey fAck; + + hsScalar fPriority; + + UInt32 fDrawableMask; + UInt32 fSubDrawableMask; + + hsColorRGBA fClearColor; + hsScalar fClearDepth; + + hsScalar fFogStart; + + hsMatrix44 fLocalToWorld; + hsMatrix44 fWorldToLocal; + + plViewTransform fViewTransform; + + hsBitVector fVisForce; + + UInt32 fUserData; + hsBool fIgnoreOccluders; + +public: + plRenderRequest(); + ~plRenderRequest(); + + hsBool GetRenderSelect() const { return !fVisForce.Empty(); } + hsBool GetRenderCharacters() const; + + void SetRenderState(UInt32 st) { fRenderState = st; } + UInt32 GetRenderState() const { return fRenderState; } + + void SetDrawableMask(UInt32 m) { fDrawableMask = m; } + UInt32 GetDrawableMask() const { return fDrawableMask; } + + void SetSubDrawableMask(UInt32 m) { fSubDrawableMask = m; } + UInt32 GetSubDrawableMask() const { return fSubDrawableMask; } + + void RequestAck(plKey key) { fAck = key; } + plKey GetAck() const { return fAck; } + + plDrawable* GetClearDrawable() const { return fClearDrawable; } + void SetClearDrawable(plDrawable* d) { fClearDrawable = d; } + + hsGMaterial* GetOverrideMat() const { return fOverrideMat; } + void SetOverrideMat(hsGMaterial* m) { fOverrideMat = m; } + + hsGMaterial* GetEraseMat() const { return fEraseMat; } + void SetEraseMat(hsGMaterial* m) { fEraseMat = m; } + + plRenderTarget* GetRenderTarget() const { return fRenderTarget; } + void SetRenderTarget(plRenderTarget* t); + + plPageTreeMgr* GetPageTreeMgr() const { return fPageMgr; } + void SetPageTreeMgr(plPageTreeMgr* mgr) { fPageMgr = mgr; } + + const hsBitVector& GetVisForce() const { return fVisForce; } + void SetVisForce(const hsBitVector& b); + + const hsMatrix44& GetLocalToWorld() const { return fLocalToWorld; } + const hsMatrix44& GetWorldToLocal() const { return fWorldToLocal; } + const hsMatrix44& GetWorldToCamera() const { return fViewTransform.GetWorldToCamera(); } + const hsMatrix44& GetCameraToWorld() const { return fViewTransform.GetCameraToWorld(); } + + const plViewTransform& GetViewTransform() const { return fViewTransform; } + + hsScalar GetHither() const { return fViewTransform.GetHither(); } + hsScalar GetYon() const { return fViewTransform.GetYon(); } + + hsScalar GetFovX() const { return fViewTransform.GetFovXDeg(); } + hsScalar GetFovY() const { return fViewTransform.GetFovYDeg(); } + + hsScalar GetSizeX() const { return fViewTransform.GetOrthoWidth(); } + hsScalar GetSizeY() const { return fViewTransform.GetOrthoHeight(); } + + UInt16 GetScreenWidth() const { return fViewTransform.GetScreenWidth(); } + UInt16 GetScreenHeight() const { return fViewTransform.GetScreenHeight(); } + + const hsColorRGBA& GetClearColor() const { return fClearColor; } + hsScalar GetClearDepth() const { return fClearDepth; } + // FogStart + // negative => use current settings (default) + // 0 => no fog == fog starts at yon + // 1 => fog starts at camera. + // Fog start greater than 1 is legal. Fog always linear. + hsScalar GetFogStart() const { return fFogStart; } + + hsScalar GetPriority() const { return fPriority; } + + void SetLocalTransform(const hsMatrix44& l2w, const hsMatrix44& w2l); + + void SetViewTransform(const plViewTransform& v) { fViewTransform = v; } + + void SetCameraTransform(const hsMatrix44& w2c, const hsMatrix44& c2w) { fViewTransform.SetCameraTransform(w2c, c2w); } + + void SetPerspective(hsBool on=true) { fViewTransform.SetPerspective(on); } + void SetOrthogonal(hsBool on=true) { fViewTransform.SetOrthogonal(on); } + + void SetHither(hsScalar f) { fViewTransform.SetHither(f); } + void SetYon(hsScalar f) { fViewTransform.SetYon(f); } + + void SetFovX(hsScalar f) { fViewTransform.SetFovXDeg(f); } + void SetFovY(hsScalar f) { fViewTransform.SetFovYDeg(f); } + + void SetSizeX(hsScalar f) { fViewTransform.SetWidth(f); } + void SetSizeY(hsScalar f) { fViewTransform.SetHeight(f); } + + void SetClearColor(const hsColorRGBA& c) { fClearColor = c; } + void SetClearDepth(hsScalar d) { fClearDepth = d; } + // FogStart + // negative => use current settings (default) + // 0 => no fog == fog starts at yon + // 1 => fog starts at camera. + // Fog start greater than 1 is legal. Fog always linear. + void SetFogStart(hsScalar d) { fFogStart = d; } + + void SetPriority(hsScalar p) { fPriority = p; } + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + void SetUserData(UInt32 n) { fUserData = n; } + UInt32 GetUserData() const { return fUserData; } + + void SetIgnoreOccluders(hsBool b) { fIgnoreOccluders = b; } + hsBool GetIgnoreOccluders() { return fIgnoreOccluders; } + + // This function is called after the render request is processed by the client + virtual void Render(plPipeline* pipe, plPageTreeMgr* pageMgr); +}; + +#endif // plRenderRequest_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneCreatable.h new file mode 100644 index 00000000..199ba91a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneCreatable.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSceneCreatable_inc +#define plSceneCreatable_inc + +#include "../pnFactory/plCreatable.h" + +#include "plSceneNode.h" + +REGISTER_CREATABLE( plSceneNode ); + +#include "plOccluder.h" + +REGISTER_CREATABLE( plOccluder ); +REGISTER_CREATABLE( plMobileOccluder ); + +#include "plPostEffectMod.h" + +REGISTER_CREATABLE( plPostEffectMod ); + +#include "plVisMgr.h" + +REGISTER_CREATABLE( plVisMgr ); + +#include "plVisRegion.h" + +REGISTER_CREATABLE( plVisRegion ); + +#include "plRelevanceMgr.h" + +REGISTER_CREATABLE( plRelevanceMgr ); + +#include "plRelevanceRegion.h" + +REGISTER_CREATABLE( plRelevanceRegion ); + +#endif // plSceneCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneNode.cpp new file mode 100644 index 00000000..778d9c70 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneNode.cpp @@ -0,0 +1,507 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#include "hsTypes.h" +#include "plSceneNode.h" +#include "../pnDispatch/plDispatch.h" +#include "../plMessage/plNodeCleanupMsg.h" +#include "../pnMessage/plNodeRefMsg.h" + +#include "hsStream.h" +#include "hsResMgr.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "plDrawable.h" +#include "plPhysical.h" +#include "plAudible.h" +#include "../plGLight/plLightInfo.h" +#include "../pnMessage/plRefMsg.h" +#include "plPipeline.h" +#include "../pnKeyedObject/plKey.h" +#include "../plDrawable/plSpaceTreeMaker.h" +#include "../plDrawable/plSpaceTree.h" +#include "plPageTreeMgr.h" +#include "plOccluder.h" + +//MFHORSE +//BLACK +// temp hack for debugging +#include "../plDrawable/plDrawableSpans.h" +#include "../pnKeyedObject/plKeyImp.h" + +plSceneNode::plSceneNode() +: fDepth(0), + fSpaceTree(nil), + fFilterGenerics(false) +{ +} + +plSceneNode::~plSceneNode() +{ + plgDispatch::Dispatch()->UnRegisterForExactType(plNodeCleanupMsg::Index(), GetKey()); + + delete fSpaceTree; +} + +//// Init //////////////////////////////////////////////////////////////////// +// Because we can't register for messages on construction. Doh. + +void plSceneNode::Init() +{ + /// :P + plgDispatch::Dispatch()->RegisterForExactType(plNodeCleanupMsg::Index(), GetKey()); +} + +void plSceneNode::Read(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Read(s, mgr); + + UInt32 n; + int i; + + n = s->ReadSwap32(); + fSceneObjects.Reset(); + for( i = 0; i < n; i++ ) + { + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(GetKey(), plRefMsg::kOnCreate, i, plNodeRefMsg::kObject); + plKey key = mgr->ReadKeyNotifyMe(s, refMsg, plRefFlags::kActiveRef); + } + + n = s->ReadSwap32(); + fGenericPool.Reset(); + for( i = 0; i < n; i++ ) + { + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(GetKey(), plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric); + mgr->ReadKeyNotifyMe(s, refMsg, plRefFlags::kActiveRef); + } +} + +void plSceneNode::Write(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Write(s, mgr); + + int i; + + s->WriteSwap32(fSceneObjects.GetCount()); + for( i = 0; i < fSceneObjects.GetCount(); i++ ) + mgr->WriteKey(s,fSceneObjects[i]); + + s->WriteSwap32(fGenericPool.GetCount()); + for( i = 0; i < fGenericPool.GetCount(); i++ ) + mgr->WriteKey(s, fGenericPool[i]); +} + +void plSceneNode::Harvest(plVolumeIsect* isect, hsTArray& levList) +{ + static hsTArray visList; + visList.SetCount(0); + GetSpaceTree()->HarvestLeaves(isect, visList); + static hsTArray visSpans; + visSpans.SetCount(0); + + int i; + for( i = 0; i < visList.GetCount(); i++ ) + { + int idx = visList[i]; + fDrawPool[idx]->GetSpaceTree()->HarvestLeaves(isect, visSpans); + if( visSpans.GetCount() ) + { + plDrawVisList* drawVis = levList.Push(); + drawVis->fDrawable = fDrawPool[idx]; + drawVis->fVisList.Swap(visSpans); + } + } +} + +void plSceneNode::CollectForRender(plPipeline* pipe, hsTArray& levList, plVisMgr* visMgr) +{ + static hsTArray visList; + visList.SetCount(0); + pipe->HarvestVisible(GetSpaceTree(), visList); + static hsTArray visSpans; + visSpans.SetCount(0); + + int i; + for( i = 0; i < visList.GetCount(); i++ ) + { + int idx = visList[i]; + if( pipe->PreRender(fDrawPool[idx], visSpans, visMgr) ) + { + plDrawVisList* drawVis = levList.Push(); + drawVis->fDrawable = fDrawPool[idx]; + drawVis->fVisList.Swap(visSpans); + } + } +} + +void plSceneNode::SubmitOccluders(plPageTreeMgr* pageMgr) const +{ + pageMgr->AddOccluderList(fOccluders); +} + +plSpaceTree* plSceneNode::IBuildSpaceTree() +{ + plSpaceTreeMaker maker; + maker.Reset(); + + hsBounds3Ext bnd; + bnd.Reset(&hsPoint3(0,0,0)); + + int i; + for( i = 0; i < fDrawPool.GetCount(); i++ ) + { + if( fDrawPool[i] ) + maker.AddLeaf(fDrawPool[i]->GetSpaceTree()->GetWorldBounds()); + else + maker.AddLeaf(bnd, true); + } + + fSpaceTree = maker.MakeTree(); + fSpaceTree->MakeDirty(); + + return fSpaceTree; +} + +plSpaceTree* plSceneNode::ITrashSpaceTree() +{ + delete fSpaceTree; + return fSpaceTree = nil; +} + +void plSceneNode::IDirtySpaceTree() +{ + int i; + for( i = 0; i < fDrawPool.GetCount(); i++ ) + { + if( fDrawPool[i] && fDrawPool[i]->GetSpaceTree()->IsDirty() ) + { + fDrawPool[i]->GetSpaceTree()->Refresh(); + fSpaceTree->MoveLeaf(i, fDrawPool[i]->GetSpaceTree()->GetWorldBounds()); + } + } +} + +plSpaceTree* plSceneNode::GetSpaceTree() +{ + if( !fSpaceTree ) + { + IBuildSpaceTree(); + } + IDirtySpaceTree(); + return fSpaceTree; +} + +void plSceneNode::ISetDrawable(plDrawable* d) +{ + if( !d ) + return; + + if (fDrawPool.Find(d) == fDrawPool.kMissingIndex) + { + fDrawPool.Append(d); + } + + ITrashSpaceTree(); +} + +void plSceneNode::ISetAudible(plAudible* a) +{ + if( !a ) + return; + + if( fAudioPool.kMissingIndex == fAudioPool.Find(a) ) + { + fAudioPool.Append(a); + } +} + +void plSceneNode::ISetPhysical(plPhysical* p) +{ + if( !p ) + return; + + if( fSimulationPool.kMissingIndex == fSimulationPool.Find(p) ) + { + fSimulationPool.Append(p); + } +} + +void plSceneNode::ISetObject(plSceneObject* o) +{ + if( o && (fSceneObjects.kMissingIndex == fSceneObjects.Find(o)) ) + { + fSceneObjects.Append(o); + + // MF_NET_GROUPS_TEST + // This will have no effect on members of NetGroupConstants + o->SetNetGroup(o->SelectNetGroup(GetKey())); + + o->SetSceneNode(GetKey()); + } +} + +void plSceneNode::ISetLight(plLightInfo* l) +{ + if( fLightPool.kMissingIndex == fLightPool.Find(l) ) + fLightPool.Append( l ); + +} + +void plSceneNode::ISetOccluder(plOccluder* o) +{ + if( fOccluders.kMissingIndex == fOccluders.Find(o) ) + { + fOccluders.Append(o); + } +} + +void plSceneNode::ISetGeneric(hsKeyedObject* k) +{ + if( fGenericPool.kMissingIndex == fGenericPool.Find(k) ) + fGenericPool.Append(k); +} + +void plSceneNode::IRemoveDrawable(plDrawable* d) +{ + int idx = fDrawPool.Find(d); + if( idx != fDrawPool.kMissingIndex ) + fDrawPool.Remove(idx); + + ITrashSpaceTree(); +} + +void plSceneNode::IRemoveAudible(plAudible* a) +{ + int idx = fAudioPool.Find(a); + if( idx != fAudioPool.kMissingIndex ) + fAudioPool.Remove(idx); + +} + +void plSceneNode::IRemovePhysical(plPhysical* p) +{ + hsAssert(p, "Removing nil physical"); + +#ifdef HS_DEBUGGING + if (p) + { + plKey oldNodeKey = p->GetSceneNode(); + if (oldNodeKey && oldNodeKey != GetKey()) + { + char buf[256]; + sprintf(buf, "Trying to remove physical %s from scenenode %s,\nbut it's actually in %s", + p->GetKeyName(), GetKeyName(), oldNodeKey->GetName()); + hsAssert(0, buf); + } + } +#endif + + int idx = fSimulationPool.Find(p); + if( idx != fSimulationPool.kMissingIndex ) + fSimulationPool.Remove(idx); +} + +void plSceneNode::IRemoveObject(plSceneObject* o) +{ + int idx = fSceneObjects.Find(o); + if( idx != fSceneObjects.kMissingIndex ) + fSceneObjects.Remove(idx); +} + +void plSceneNode::IRemoveLight(plLightInfo* l) +{ + hsAssert(l, "Removing nil light"); + + int idx = fLightPool.Find(l); + if( idx != fLightPool.kMissingIndex ) + { + fLightPool.Remove(idx); + } +} + +void plSceneNode::IRemoveOccluder(plOccluder* o) +{ + int idx = fOccluders.Find(o); + if( idx != fOccluders.kMissingIndex ) + fOccluders.Remove(idx); +} + +void plSceneNode::IRemoveGeneric(hsKeyedObject* k) +{ + int idx = fGenericPool.Find(k); + if( idx != fGenericPool.kMissingIndex ) + fGenericPool.Remove(idx); +} + +hsBool plSceneNode::IOnRemove(plNodeRefMsg* refMsg) +{ + + switch( refMsg->fType ) + { + case plNodeRefMsg::kDrawable: + IRemoveDrawable(plDrawable::ConvertNoRef(refMsg->GetRef())); + break; + case plNodeRefMsg::kPhysical: + IRemovePhysical(plPhysical::ConvertNoRef(refMsg->GetRef())); + break; + case plNodeRefMsg::kAudible: + IRemoveAudible(plAudible::ConvertNoRef(refMsg->GetRef())); + break; + case plNodeRefMsg::kObject: + IRemoveObject(plSceneObject::ConvertNoRef(refMsg->GetRef())); + break; + case plNodeRefMsg::kLight: + IRemoveLight(plLightInfo::ConvertNoRef(refMsg->GetRef())); + break; + case plNodeRefMsg::kOccluder: + IRemoveOccluder(plOccluder::ConvertNoRef(refMsg->GetRef())); + break; + case plNodeRefMsg::kGeneric: + IRemoveGeneric(refMsg->GetRef()); + break; + } + if( refMsg->GetRef() && (refMsg->GetContext() & plRefMsg::kOnRemove) ) + GetKey()->Release(refMsg->GetRef()->GetKey()); + + return true; +} + +hsBool plSceneNode::IOnAdd(plNodeRefMsg* refMsg) +{ + int which = refMsg->fWhich; + + switch( refMsg->fType ) + { + case plNodeRefMsg::kDrawable: + ISetDrawable(plDrawable::ConvertNoRef(refMsg->GetRef())); + return true; + case plNodeRefMsg::kPhysical: + ISetPhysical(plPhysical::ConvertNoRef(refMsg->GetRef())); + return true; + case plNodeRefMsg::kAudible: + ISetAudible(plAudible::ConvertNoRef(refMsg->GetRef())); + return true; + case plNodeRefMsg::kObject: + ISetObject(plSceneObject::ConvertNoRef(refMsg->GetRef())); + return true; + case plNodeRefMsg::kLight: + ISetLight(plLightInfo::ConvertNoRef(refMsg->GetRef())); + return true; + case plNodeRefMsg::kOccluder: + ISetOccluder(plOccluder::ConvertNoRef(refMsg->GetRef())); + return true; + case plNodeRefMsg::kGeneric: + ISetGeneric(refMsg->GetRef()); + } + return true; +} + +hsBool plSceneNode::MsgReceive(plMessage* msg) +{ + plNodeCleanupMsg *cleanMsg = plNodeCleanupMsg::ConvertNoRef( msg ); + + if( cleanMsg ) + { + ICleanUp(); + return true; + } + + plNodeRefMsg* refMsg = plNodeRefMsg::ConvertNoRef(msg); + + if( refMsg ) + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + return IOnAdd(refMsg); + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + return IOnRemove(refMsg); + + } + + return hsKeyedObject::MsgReceive(msg); +} + +//// ICleanUp //////////////////////////////////////////////////////////////// +// Export only: Clean up the scene node (i.e. make sure drawables optimize) + +void plSceneNode::ICleanUp( void ) +{ + int i; + + + /// Go find drawables to delete + for( i = 0; i < fDrawPool.GetCount(); i++ ) + fDrawPool[ i ]->Optimize(); + + if (fFilterGenerics) + { + for( i = fSceneObjects.GetCount() - 1; i >= 0; i--) + GetKey()->Release(fSceneObjects[i]->GetKey()); + for( i = fDrawPool.GetCount() - 1; i >= 0; i--) + GetKey()->Release(fDrawPool[i]->GetKey()); + for( i = fSimulationPool.GetCount() - 1; i >= 0; i--) + GetKey()->Release(fSimulationPool[i]->GetKey()); + for( i = fAudioPool.GetCount() - 1; i >= 0; i--) + GetKey()->Release(fAudioPool[i]->GetKey()); + for( i = fOccluders.GetCount() - 1; i >= 0; i--) + GetKey()->Release(fOccluders[i]->GetKey()); + for( i = fLightPool.GetCount() - 1; i >= 0; i--) + GetKey()->Release(fLightPool[i]->GetKey()); + } + + ITrashSpaceTree(); +} + +//// GetMatchingDrawable ///////////////////////////////////////////////////// +// Export only: Query for a given drawable. + +plDrawable *plSceneNode::GetMatchingDrawable( const plDrawableCriteria& crit ) +{ + int i; + + + for( i = 0; i < fDrawPool.GetCount(); i++ ) + { + if( fDrawPool[ i ]->DoIMatch( crit ) ) + return fDrawPool[ i ]; + } + + return nil; +} + +//// OptimizeDrawables /////////////////////////////////////////////////////// +// Loops through all the drawables and calls Optimize on each one. For the +// export side, to be called right before writing the drawables to disk. + +void plSceneNode::OptimizeDrawables( void ) +{ + int i; + + + for( i = 0; i < fDrawPool.GetCount(); i++ ) + fDrawPool[ i ]->Optimize(); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneNode.h new file mode 100644 index 00000000..cfa363a2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plSceneNode.h @@ -0,0 +1,139 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSceneNode_inc +#define plSceneNode_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsTemplates.h" + + +class plSceneObject; +class plDrawable; +class plPhysical; +class plAudible; +class plLightInfo; +class plPipeline; +class plNodeRefMsg; +class plDispatchBase; +class plSpaceTree; +class plDrawSpanPair; +class plDrawVisList; +class plOccluder; +class plPageTreeMgr; +class plDrawableCriteria; +class plVolumeIsect; +class plVisMgr; + +class plSceneNode : public hsKeyedObject +{ +public: + enum { + kMaxSceneDepth = 4 + }; + +protected: + hsBool fFilterGenerics; // Export only + + Int16 fDepth; + + hsTArray fSceneObjects; + + hsTArray fDrawPool; + hsTArray fSimulationPool; + hsTArray fAudioPool; + + hsTArray fOccluders; + + hsTArray fLightPool; + + hsTArray fGenericPool; + + plSpaceTree* fSpaceTree; + + void IDirtySpaceTree(); + plSpaceTree* ITrashSpaceTree(); + plSpaceTree* IBuildSpaceTree(); + + void IRemoveDrawable(plDrawable* d); + void IRemoveAudible(plAudible* a); + void IRemovePhysical(plPhysical* p); + void IRemoveObject(plSceneObject* o); + void IRemoveLight(plLightInfo* l); + void IRemoveOccluder(plOccluder* o); + void IRemoveGeneric(hsKeyedObject* k); + + void ISetObject(plSceneObject* o); + void ISetPhysical(plPhysical* p); + void ISetAudible(plAudible* a); + void ISetDrawable(plDrawable* d); + void ISetLight(plLightInfo* l); + void ISetOccluder(plOccluder* o); + void ISetGeneric(hsKeyedObject* k); + + hsBool IOnRemove(plNodeRefMsg* refMsg); + hsBool IOnAdd(plNodeRefMsg* refMsg); + + // Export only: Clean up empty drawables + void ICleanUp( void ); + +public: + plSceneNode(); + virtual ~plSceneNode(); + + CLASSNAME_REGISTER( plSceneNode ); + GETINTERFACE_ANY( plSceneNode, hsKeyedObject ); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual void Harvest(plVolumeIsect* isect, hsTArray& levList); + virtual void CollectForRender(plPipeline* pipe, hsTArray& levList, plVisMgr* visMgr); + + virtual void SubmitOccluders(plPageTreeMgr* pageMgr) const; + + virtual hsBool MsgReceive(plMessage* msg); + + Int16 GetDepth() { return fDepth; } + Int16 IncDepth() { return ++fDepth; } + Int16 DecDepth() { return --fDepth; } + + void Init( void ); + + plSpaceTree* GetSpaceTree(); + + // Export only: Query for a given drawable + virtual plDrawable *GetMatchingDrawable( const plDrawableCriteria& crit ); + + // Export only: Optimize all my stinkin' drawables + virtual void OptimizeDrawables( void ); + + void SetFilterGenericsOnly(hsBool b) { fFilterGenerics = b; } + + const hsTArray& GetDrawPool() const { return fDrawPool; } +}; + +#endif // plSceneNode_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisMgr.cpp new file mode 100644 index 00000000..0a9ce211 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisMgr.cpp @@ -0,0 +1,207 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plVisMgr.h" + +#include "plVisRegion.h" + +hsBitVector plVisMgr::fIdxSet; +hsBitVector plVisMgr::fIdxNot; + +plVisMgr::plVisMgr() +: fMaxSet(kCharacter), + fMaxNot(-1) +{ + ResetNormal(); +} + +plVisMgr::~plVisMgr() +{ +} + +hsBool plVisMgr::MsgReceive(plMessage* msg) +{ + + return hsKeyedObject::MsgReceive(msg); +} + +void plVisMgr::Read(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Read(s, mgr); + +} + +void plVisMgr::Write(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Write(s, mgr); +} + +void plVisMgr::Register(plVisRegion* reg, hsBool not) +{ + // This should happen pretty infrequently, or + // I wouldn't be doing it so cloth-headed-ly. + hsTArray& regions = not ? fNotRegions : fRegions; + hsBitVector& indices = not ? fIdxNot : fIdxSet; + int& maxIdx = not ? fMaxNot : fMaxSet; + int i; + for( i = kNumReserved; ; i++ ) + { + if( !indices.IsBitSet(i) ) + { + if( i > maxIdx ) + maxIdx = i; + + indices.SetBit(i); + reg->SetIndex(i); + regions.Append(reg); + return; + } + } + hsAssert(false, "Infinite bitvector has all bits set?"); +} + +void plVisMgr::UnRegister(plVisRegion* reg, hsBool not) +{ + // Mark our index for recycling + hsBitVector& indices= not ? fIdxNot : fIdxSet; + indices.ClearBit(reg->GetIndex()); + + // Nuke the region from our list. + hsTArray& regions = not ? fNotRegions : fRegions; + int idx = regions.Find(reg); + if( regions.kMissingIndex != idx ) + regions.Remove(idx); +} + +void plVisMgr::Eval(const hsPoint3& pos) +{ + fVisSet = fOnBitSet; + + int i; + for( i = 0; i < fRegions.GetCount(); i++ ) + { + hsAssert(fRegions[i], "Nil region in list"); + if( !fOffBitSet.IsBitSet(fRegions[i]->GetIndex()) ) + { + if( fRegions[i]->Eval(pos) ) + { + fVisSet.SetBit(fRegions[i]->GetIndex()); + if( fRegions[i]->DisableNormal() ) + { + fVisSet.ClearBit(kNormal); + fVisSet.SetBit(kCharacter); + } + } + } + } + + fVisNot = fOnBitNot; + + for( i = 0; i < fNotRegions.GetCount(); i++ ) + { + hsAssert(fNotRegions[i], "Nil region in list"); + if( !fOffBitNot.IsBitSet(fNotRegions[i]->GetIndex()) ) + { + if( fNotRegions[i]->Eval(pos) ) + { + fVisNot.SetBit(fNotRegions[i]->GetIndex()); + } + } + } + + ResetNormal(); +} + +void plVisMgr::ResetNormal() +{ + fOnBitSet.Clear(); + fOnBitSet.SetBit(kNormal); + + fOffBitSet.Clear(); + fOnBitNot.Clear(); + fOffBitNot.Clear(); +} + +void plVisMgr::DisableNormal() +{ + fOnBitSet.Clear(); + if( fMaxSet > 0 ) + fOffBitSet.Set(fMaxSet); + + fOnBitNot.Clear(); + if( fMaxNot > 0 ) + fOffBitNot.Set(fMaxNot); +} + +void plVisMgr::EnableVisSet(int idx, hsBool isNot) +{ + hsBitVector& offs = isNot ? fOffBitNot : fOffBitSet; + + offs.ClearBit(idx); +} + +void plVisMgr::EnableVisSets(const hsBitVector& enabled, hsBool isNot) +{ + hsBitVector& offs = isNot ? fOffBitNot : fOffBitSet; + offs -= enabled; +} + +void plVisMgr::ForceVisSet(int idx, hsBool isNot) +{ + EnableVisSet(idx, isNot); + hsBitVector& ons = isNot ? fOnBitNot : fOnBitSet; + ons.SetBit(idx); +} + +void plVisMgr::ForceVisSets(const hsBitVector& enabled, hsBool isNot) +{ + EnableVisSets(enabled, isNot); + hsBitVector& ons = isNot ? fOnBitNot : fOnBitSet; + ons |= enabled; +} + +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////// + +plVisMgr* plGlobalVisMgr::fInstance = nil; + +void plGlobalVisMgr::Init() +{ + fInstance = TRACKED_NEW plVisMgr; + fInstance->RegisterAs(kGlobalVisMgr_KEY); +} + +void plGlobalVisMgr::DeInit() +{ + if (fInstance) + { + fInstance->UnRegisterAs(kGlobalVisMgr_KEY); + fInstance = nil; + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisMgr.h new file mode 100644 index 00000000..e831cd16 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisMgr.h @@ -0,0 +1,122 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plVisMgr_inc +#define plVisMgr_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsTemplates.h" +#include "hsBitVector.h" + +class hsStream; +class hsResMgr; +class plVisRegion; +class plMessage; +struct hsPoint3; + +class plVisMgr : public hsKeyedObject +{ +public: + enum RsvBits + { + kNormal, + kCharacter, + + kNumReserved + }; +protected: + hsTArray fRegions; + hsTArray fNotRegions; + + hsBitVector fVisSet; + hsBitVector fVisNot; + + int fMaxSet; + int fMaxNot; + + hsBitVector fOnBitSet; // Forces a true response from that enabling region + hsBitVector fOffBitSet; // Forces a false response from that enabling region + + hsBitVector fOnBitNot; // Forces a true response from that disabling region + hsBitVector fOffBitNot; // Forces a falseresponse from that disabling region + + static hsBitVector fIdxSet; + static hsBitVector fIdxNot; + + // There's currently no reason why you would call ResetNormal + // because it's called after every Eval. + void ResetNormal(); + +public: + plVisMgr(); + virtual ~plVisMgr(); + + CLASSNAME_REGISTER( plVisMgr ); + GETINTERFACE_ANY( plVisMgr, hsKeyedObject ); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + void Register(plVisRegion* reg, hsBool not); + void UnRegister(plVisRegion* reg, hsBool not); + + void Eval(const hsPoint3& pos); + + const hsBitVector& GetVisSet() const { return fVisSet; } + const hsBitVector& GetVisNot() const { return fVisNot; } + + // All the following persist only through the next Eval. So a normal + // use would be to call DisableNormal() in your RenderRequest's Render method, + // then Enable a few vissets of personal interest, then call the base RenderRequest::Render(). + // + // Turns all regions off, so NOTHING gets drawn. That includes Normal and Character. + void DisableNormal(); + + // Enable drawing of selected sets. Either one index at a time or pass in a bitvector. + // The regions are just enabled, they can still say no. + void EnableVisSet(int idx, hsBool isNot = false); + void EnableVisSets(const hsBitVector& enabled, hsBool isNot = false); + + // Make specified regions say yes, no matter where the camera is. + // This will implicitly call EnableVisSet for you. + void ForceVisSet(int idx, hsBool isNot = false); + void ForceVisSets(const hsBitVector& enabled, hsBool isNot = false); +}; + +class plGlobalVisMgr +{ +protected: + static plVisMgr* fInstance; +public: + static plVisMgr* Instance() { return fInstance; } + + static void Init(); + static void DeInit(); +}; + +#endif // plVisMgr_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisRegion.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisRegion.cpp new file mode 100644 index 00000000..7e55bb92 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisRegion.cpp @@ -0,0 +1,129 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plVisRegion.h" + +#include "hsStream.h" +#include "hsResMgr.h" + +#include "plVisMgr.h" + +#include "../pnMessage/plEnableMsg.h" +#include "../plIntersect/plRegionBase.h" + +plVisRegion::plVisRegion() +: fIndex(0), + fRegion(nil), + fMgr(nil) +{ + fMgr = plGlobalVisMgr::Instance(); + + SetProperty(kReplaceNormal, true); +} + +plVisRegion::~plVisRegion() +{ + if( fMgr ) + fMgr->UnRegister(this, GetProperty(kIsNot)); +} + +hsBool plVisRegion::Eval(const hsPoint3& pos) const +{ + if( GetProperty(kDisable) ) + return false; + + if( !fRegion ) + return true; + + return fRegion->IsInside(pos); +} + +hsBool plVisRegion::MsgReceive(plMessage* msg) +{ + plEnableMsg* enaMsg = plEnableMsg::ConvertNoRef(msg); + if( enaMsg ) + { + SetProperty(kDisable, enaMsg->Cmd(plEnableMsg::kDisable)); + return true; + } + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + switch( refMsg->fType ) + { + case kRefRegion: + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + fRegion = plRegionBase::ConvertNoRef(refMsg->GetRef()); + } + else + { + fRegion = nil; + } + return true; + case kRefVisMgr: + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + if( fMgr ) + fMgr->UnRegister(this, GetProperty(kIsNot)); + fMgr = plVisMgr::ConvertNoRef(refMsg->GetRef()); + hsAssert(fMgr, "Just set my manager to nil."); + fMgr->Register(this, GetProperty(kIsNot)); + } + else + { + fMgr = nil; + } + return true; + + default: + break; + } + } + return plObjInterface::MsgReceive(msg); +} + +void plVisRegion::Read(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Read(s, mgr); + + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefRegion), plRefFlags::kActiveRef); + mgr->ReadKeyNotifyMe(s, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kRefVisMgr), plRefFlags::kActiveRef); + + if( fMgr ) + fMgr->Register(this, GetProperty(kIsNot)); +} + +void plVisRegion::Write(hsStream* s, hsResMgr* mgr) +{ + plObjInterface::Write(s, mgr); + + mgr->WriteKey(s, fRegion); + + mgr->WriteKey(s, fMgr); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisRegion.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisRegion.h new file mode 100644 index 00000000..d2bcf9ff --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plScene/plVisRegion.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plVisRegion_inc +#define plVisRegion_inc + +#include "../pnSceneObject/plObjInterface.h" + +class hsStream; +class hsResMgr; +class plMessage; +class plVisMgr; +class plRegionBase; +struct hsPoint3; + +class plVisRegion : public plObjInterface +{ +public: + enum + { + kRefRegion, + kRefVisMgr + }; + enum + { + kDisable = 0, // Always disable is zero + kIsNot, + kReplaceNormal, // Defaults to true + kDisableNormal + }; +protected: + plRegionBase* fRegion; + + plVisMgr* fMgr; + + Int32 fIndex; + + void SetIndex(Int32 i) { fIndex = i; } + + friend class plVisMgr; +public: + plVisRegion(); + virtual ~plVisRegion(); + + CLASSNAME_REGISTER( plVisRegion ); + GETINTERFACE_ANY( plVisRegion, plObjInterface ); + + virtual Int32 GetNumProperties() const { return 3; } // This is stupid. + + virtual hsBool MsgReceive(plMessage* msg); + + // Set transform doesn't do anything, because the regions themselves are + // object interfaces, so they'll move when their sceneobjects move. + virtual void SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l) {} + + virtual void Read(hsStream* stream, hsResMgr* mgr); + virtual void Write(hsStream* stream, hsResMgr* mgr); + + hsBool Eval(const hsPoint3& pos) const; + + Int32 GetIndex() const { return fIndex; } + + hsBool Registered() const { return GetIndex() > 0; } + + hsBool IsNot() const { return GetProperty(kIsNot); } + hsBool ReplaceNormal() const { return GetProperty(kReplaceNormal); } + hsBool DisableNormal() const { return GetProperty(kDisableNormal); } +}; + +#endif // plVisRegion_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketReader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketReader.cpp new file mode 100644 index 00000000..132170e8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketReader.cpp @@ -0,0 +1,196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plBufferedSocketReader.h" +#include "plTcpSocket.h" +#include + + +plBufferedSocketReader::plBufferedSocketReader(int size) +: plRingBuffer(size) +{ +} + +int plBufferedSocketReader::ReadBlock(char * buf, int buflen, plTcpSocket & sck) +{ + if(GetBlock(buf, buflen)) + return kSuccessWithData; + + int ans = ReadFrom(sck); + if(ans<=0) + return ans; + + if(GetBlock(buf, buflen)) + return kSuccessWithData; + + return kSuccessNoData; +} + +int plBufferedSocketReader::ReadString(char * buf, int buflen, char * termChars, plTcpSocket & sck) +{ + if(GetString(buf, buflen, termChars)) + return kSuccessWithData; + + int ans = kSuccessNoData; + + while ( ans>=0 ) + { + ans = ReadFrom(sck); + if(ans>0) + { + if ( GetString(buf, buflen, termChars) ) + return kSuccessWithData; + } + } + + return ans; +} + +int plBufferedSocketReader::ReadStringInPlace(char ** buf, char * termChars, plTcpSocket & sck) +{ + if(GetStringInPlace(buf, termChars)) + return kSuccessWithData; + + int ans = kSuccessNoData; + + while ( ans>=0 ) + { + ans = ReadFrom(sck); + if(ans>0) + { + if ( GetStringInPlace(buf, termChars) ) + return kSuccessWithData; + } + } + + return ans; +} + +void plBufferedSocketReader::Reset() +{ + plRingBuffer::Reset(); +} + +int plBufferedSocketReader::ReadFrom(plTcpSocket & sck) // this is where things get ugly. +{ + int ans = kSuccessNoData; + int readSize = BufferAvailable(); + + if(readSize < 1) + { + Compress(); + readSize = BufferAvailable(); + } + + if(readSize > 0) + { + char * dst = GetBufferOpen(); + int nBytesRead = sck.RecvData(dst, readSize); + if(nBytesRead < 0) + { + int err = plNet::GetError(); + if(err != kBlockingError) + { + ans = kFailedReadError; + } + } + else if(nBytesRead > 0) + { + fEndPos += nBytesRead; + ans = kSuccessWithData; + } + else + { + ans = kFailedSocketClosed; + } + } + else + { + ans = kFailedNoBufferSpace; + } + return ans; +} + + +bool plBufferedSocketReader::GetBlock(char * buf, int buflen) +{ + int dataAvailable = FastAmountBuffered(); + int maxRead = buflen; + if(maxRead > dataAvailable) + maxRead = dataAvailable; + + if (maxRead==0) + return false; + + char * wrk = FastGetBufferStart(); + memcpy(buf,FastGetBufferStart(),maxRead); + + return true; +} + +bool plBufferedSocketReader::GetString(char * buf, int buflen, char * termChars) +{ + bool ans = false; + int dataAvailable = FastAmountBuffered(); + int maxRead = buflen; + if(maxRead > dataAvailable) + maxRead = dataAvailable; + + char * wrk = FastGetBufferStart(); + for(int i=0; i. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plBufferedSocketReader_h_inc +#define plBufferedSocketReader_h_inc + +#include "plRingBuffer.h" + +class plTcpSocket; + + +class plBufferedSocketReader : protected plRingBuffer +{ +public: + enum ReadResult + { + kSuccessNoData = 0, + kSuccessWithData = 1, + kFailedReadError =-1, + kFailedSocketClosed =-2, + kFailedNoBufferSpace =-3, + }; + +public: + plBufferedSocketReader(int size=8192); + int ReadBlock(char * buf, int buflen, plTcpSocket & sck); + int ReadString(char * buf, int buflen, char * termChars, plTcpSocket & sck); + int ReadStringInPlace(char ** buf, char * termChars, plTcpSocket & sck); + void Reset(); + +protected: + int ReadFrom(plTcpSocket & sck); + bool GetBlock(char * buf, int buflen); + bool GetString(char * buf, int buflen, char * termChars); + bool GetStringInPlace(char ** buf, char * termChars); +}; + + +#endif // plBufferedSocketReader_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketWriter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketWriter.cpp new file mode 100644 index 00000000..71f2e12d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketWriter.cpp @@ -0,0 +1,109 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plBufferedSocketWriter.h" +#include "plTcpSocket.h" + +plBufferedSocketWriter::plBufferedSocketWriter(int size, int bytesPerFlush, bool blockOnSend, int flushPoint) +: plRingBuffer(size) +, fFlushPoint(flushPoint) +, fBlockOnSend(blockOnSend) +, fBytesPerFlush(bytesPerFlush) +{} + + +int plBufferedSocketWriter::WriteBlock(const char * data, int len, plTcpSocket & sck) +{ + int ans = kSuccessNoDataSent; + + if(len > BufferAvailable()) + ans = Flush(sck); + + if(ans >= 0) + ans = WriteBlock(data,len); + + if(ans >= 0 && fFlushPoint >= 0 && fFlushPoint < AmountBuffered()) + ans = Flush(sck); + + return ans; +} + +int plBufferedSocketWriter::WriteBlock(const char * data, int len) +{ + int ans = kSuccessNoDataSent; + if(!Put(data,len)) + ans = kFailedNoBufferSpace; + return ans; +} + +int plBufferedSocketWriter::Flush(plTcpSocket & sck) // this is where things get ugly. +{ + int ans = kSuccessNoDataSent; + + int writeSize = MIN(FastAmountBuffered(),fBytesPerFlush); + + if(writeSize > 0) + { + int nBytesWritten = 0; +// int nBytesWritten = sck.SendData(FastGetBufferStart(), writeSize); + +// if (nBytesWritten<0 && fBlockOnSend +// && plNet::GetError()==kBlockingError) + { + bool wasBlockingOrNot = sck.IsBlocking(); + sck.SetBlocking(fBlockOnSend); + nBytesWritten = sck.SendData(FastGetBufferStart(), writeSize); + sck.SetBlocking(wasBlockingOrNot); + } + + if (nBytesWritten > 0) + { + fStartPos += nBytesWritten; + FullCompress(); + ans = kSuccessDataSent; + } + else if (nBytesWritten < 0) + { + if (plNet::GetError()!=kBlockingError) + ans = kFailedWriteError; + } + else + { + ans = kFailedSocketClosed; + } + } + return ans; +} + +bool plBufferedSocketWriter::IsEmpty() +{ + return !(FastAmountBuffered() > 0); +} + +void plBufferedSocketWriter::Reset() +{ + plRingBuffer::Reset(); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketWriter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketWriter.h new file mode 100644 index 00000000..13b06f57 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plBufferedSocketWriter.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plBufferedSocketWriter_h_inc +#define plBufferedSocketWriter_h_inc + +#include "plRingBuffer.h" + +class plTcpSocket; + + +class plBufferedSocketWriter : protected plRingBuffer +{ +protected: + bool fBlockOnSend; + int fFlushPoint; + int fBytesPerFlush; + +public: + enum WriteResult + { + kSuccessNoDataSent = 0, + kSuccessDataSent = 1, + kFailedWriteError =-1, + kFailedSocketClosed =-2, + kFailedNoBufferSpace =-3, + }; + +public: + plBufferedSocketWriter(int size=8192, int bytesPerFlush=4096, bool blockOnSend=true, int flushPoint=-1); + int WriteBlock(const char * data, int len, plTcpSocket & sck); + int WriteBlock(const char * data, int len); + int Flush(plTcpSocket & sck); + bool IsEmpty(); + void Reset(); +}; + + + +#endif // plBufferedSocketWriter_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plFdSet.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plFdSet.cpp new file mode 100644 index 00000000..202ae704 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plFdSet.cpp @@ -0,0 +1,157 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plFdSet.h" +#include "plSocket.h" + +#if HS_BUILD_FOR_UNIX +#include +#endif + +plFdSet::plFdSet(void) +{ + ZeroFds(); +} + + +void plFdSet::SetForSocket(plSocket & in) +{ + SOCKET sck = in.GetSocket(); + hsAssert(sck>=0, "plFdSet::SetForSocket: socket<0"); + if ( sck<0 ) + return; + FD_SET(sck, &fFds); + FD_SET(sck, &fErrFds); + if(fMaxFd < sck) + fMaxFd = sck; +} + +void plFdSet::ClearForSocket(plSocket & in) +{ + SOCKET sck = in.GetSocket(); + hsAssert(sck>=0, "plFdSet::ClearForSocket: socket<0"); + if ( sck<0 ) + return; + FD_CLR(sck, &fFds); + FD_CLR(sck, &fErrFds); +} + + +bool plFdSet::IsSetFor(plSocket & in) +{ + SOCKET sck = in.GetSocket(); + hsAssert(sck>=0, "plFdSet::IsSetFor: socket<0"); + if ( sck<0 ) + return false; + return (FD_ISSET(sck, &fFds) != 0); +} + +bool plFdSet::IsErrFor(plSocket & in) +{ + SOCKET sck = in.GetSocket(); + hsAssert(sck>=0, "plFdSet::IsErrFor: socket<0"); + if ( sck<0 ) + return false; + return (FD_ISSET(sck, &fErrFds) != 0); +} + + +int plFdSet::WaitForRead(bool shouldZeroFds, unsigned long timeoutMillis) +{ + int ret_val = 0; + + if(timeoutMillis == kInfinite) + { + ret_val = select(fMaxFd+1,&fFds,NULL,&fErrFds,NULL); + } + else + { + struct timeval tv; + tv.tv_sec = timeoutMillis / 1000; + tv.tv_usec = (timeoutMillis % 1000) * 1000; + + ret_val = select(fMaxFd+1,&fFds,NULL,&fErrFds,&tv); + } + if (shouldZeroFds) + ZeroFds(); + + return ret_val; +} + + +int plFdSet::WaitForWrite(bool shouldZeroFds, unsigned long timeoutMillis) +{ + int ret_val = 0; + + if(timeoutMillis == kInfinite) + { + ret_val = select(fMaxFd+1,NULL,&fFds,&fErrFds,NULL); + } + else + { + timeval tv; + tv.tv_sec = timeoutMillis / 1000; + tv.tv_usec = (timeoutMillis % 1000) * 1000; + + ret_val = select(fMaxFd+1,NULL,&fFds,&fErrFds,&tv); + } + if (shouldZeroFds) + ZeroFds(); + + return ret_val; +} + + +int plFdSet::WaitForError(bool shouldZeroFds, unsigned long timeoutMillis) +{ + int ret_val = 0; + + if(timeoutMillis == kInfinite) + { + ret_val = select(fMaxFd+1,NULL,NULL,&fErrFds,NULL); + } + else + { + timeval tv; + tv.tv_sec = timeoutMillis / 1000; + tv.tv_usec = (timeoutMillis % 1000) * 1000; + + ret_val = select(fMaxFd+1,NULL,NULL,&fErrFds,&tv); + } + if (shouldZeroFds) + ZeroFds(); + + return ret_val; +} + + +void plFdSet::ZeroFds(void) +{ + fMaxFd = 0; + FD_ZERO(&fFds); + FD_ZERO(&fErrFds); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plFdSet.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plFdSet.h new file mode 100644 index 00000000..4e99fd54 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plFdSet.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plFdSet_h_inc +#define plFdSet_h_inc + +#include "plNet.h" + +//////////////////////////////////////////////////// + +class plSocket; + +//////////////////////////////////////////////////// + +class plFdSet +{ +public: + enum { kInfinite = 0xffffffff }; + +public: + plFdSet(); + void SetForSocket(plSocket & in); + void ClearForSocket(plSocket & in); + bool IsSetFor(plSocket & in); + bool IsErrFor(plSocket & in); + int WaitForRead(bool shouldZeroFds, unsigned long timeoutMillis=kInfinite); + int WaitForWrite(bool shouldZeroFds, unsigned long timeoutMillis=kInfinite); + int WaitForError(bool shouldZeroFds, unsigned long timeoutMillis=0); + void ZeroFds(); + +private: + unsigned fMaxFd; + fd_set fFds; + fd_set fErrFds; +}; + + + + +#endif //plFdSet_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plMemBuffer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plMemBuffer.cpp new file mode 100644 index 00000000..63995104 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plMemBuffer.cpp @@ -0,0 +1,135 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsUtils.h" +#include "plMemBuffer.h" + +///////////////////////////////////////////////// + +plMemBuffer::plMemBuffer() +{ + fBuffer = nil; + fBufferLocal = false; + fBufferLen = 0; +} + + +plMemBuffer::plMemBuffer(int len) +{ + AllocBuffer(len); +} + + +plMemBuffer::plMemBuffer(char * data, int len) +{ + fBufferLocal = false; + fBufferLen = len; + fBuffer = data; +} + + +plMemBuffer::~plMemBuffer() +{ + ClearBuffer(); +} + + +void plMemBuffer::SetBuffer(char * data, int len) +{ + ClearBuffer(); + fBufferLocal = false; + fBufferLen = len; + fBuffer = data; +} + + +void plMemBuffer::CopyBuffer(char * data, int len) +{ + char * tmp = TRACKED_NEW char[len]; + memcpy(tmp,data,len); + ClearBuffer(); + AllocBuffer(len); + memcpy(fBuffer,tmp,len); + delete [] tmp; +} + + +void plMemBuffer::GrowBuffer(int newLen) +{ + const int kThrashSize = 25; + if(newLen >= fBufferLen) + { + int len = newLen + kThrashSize; + len = len+len; + + char * tmp = TRACKED_NEW char[len]; + + if(fBuffer != nil) + memcpy(tmp,fBuffer,fBufferLen); + + ClearBuffer(); + + fBuffer = tmp; + fBufferLocal = true; + fBufferLen = len; + } +} + + +int plMemBuffer::GetBufferSize() +{ + return fBufferLen; +}; + + +char * plMemBuffer::GetBuffer() +{ + return fBuffer; +}; + + +bool plMemBuffer::InBufferRange(char * inpos) +{ + return (inpos >= fBuffer && inpos <= (fBuffer + fBufferLen)); +} + +void plMemBuffer::ClearBuffer() +{ + if(fBufferLocal == true) + { + if(fBuffer != nil) + delete [] fBuffer; + fBuffer = nil; + } +} + +void plMemBuffer::AllocBuffer(int len) +{ + fBuffer = TRACKED_NEW char[len]; + fBufferLocal = true; + fBufferLen = len; +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plMemBuffer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plMemBuffer.h new file mode 100644 index 00000000..265918c6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plMemBuffer.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plMemBuffer_h_inc +#define plMemBuffer_h_inc + + +class plMemBuffer +{ +public: + plMemBuffer(); + plMemBuffer(int len); + plMemBuffer(char * data, int len); + virtual ~plMemBuffer(); + void SetBuffer(char * data, int len); + void CopyBuffer(char * data, int len); + void GrowBuffer(int len); + int GetBufferSize(); + char * GetBuffer(); + bool InBufferRange(char *); + +protected: + bool fBufferLocal; + int fBufferLen; + char * fBuffer; + + void ClearBuffer(); + void AllocBuffer(int len); +}; + + + +#endif // plMemBuffer_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plNet.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plNet.cpp new file mode 100644 index 00000000..e76bbf6c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plNet.cpp @@ -0,0 +1,278 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plNet.h" + +//////////////////////////////////////////////////// + +plNet plNet::_; + +//////////////////////////////////////////////////// +// Windows socket interface +#if HS_BUILD_FOR_WIN32 + +plNet::plNet() +{ + static struct WSAData wsa; + WSAStartup(0x0101, &wsa); +} + +plNet::~plNet() +{ + WSACleanup(); +} + +SOCKET plNet::NewUDP() +{ + return ::socket(AF_INET, SOCK_DGRAM, 0); +} + +SOCKET plNet::NewTCP() +{ + SOCKET x = ::socket(AF_INET, SOCK_STREAM, 0); + unsigned int timeoutval; + timeoutval = kDefaultSocketTimeout; + setsockopt(x, SOL_SOCKET, (int)SO_RCVTIMEO,(const char*)&timeoutval,sizeof(timeoutval)); + timeoutval = kDefaultSocketTimeout; + setsockopt(x, SOL_SOCKET, (int)SO_SNDTIMEO,(const char*)&timeoutval,sizeof(timeoutval)); + return x; +} + +int plNet::GetError() +{ + return WSAGetLastError(); +} + +int plNet::Read(const SOCKET sck, char * buf, const int size) +{ + return ::recv(sck,buf,size,0); +} + +int plNet::Write(const SOCKET sck, const char * buf, const int len) +{ + return ::send(sck,buf,len,0); +} + +int plNet::ReadFrom(const SOCKET sck, char * buf, int len, sockaddr_in * addr) +{ + int addrlen = sizeof(sockaddr); + return ::recvfrom(sck,buf,len,0,reinterpret_cast(addr),&addrlen); +} + +int plNet::WriteTo(const SOCKET sck, const char * buf, const int len, sockaddr_in * addr) +{ + return ::sendto(sck,buf,len,0,reinterpret_cast(addr),sizeof(sockaddr)); +} + +int plNet::Connect(const SOCKET sck, const sockaddr_in * addr) +{ + return ::connect(sck, reinterpret_cast(addr), sizeof(sockaddr)); +} + +int plNet::Close(const SOCKET sck) +{ + return ::closesocket(sck); +} + +int plNet::Bind(const SOCKET sck, const sockaddr_in * addr) +{ + return ::bind(sck,reinterpret_cast(addr),sizeof(sockaddr)); +} + +int plNet::Listen(const SOCKET sck, const int qsize) +{ + return ::listen(sck,qsize); +} + +int plNet::Accept(const SOCKET sck, sockaddr_in * addr) +{ + int addrlen = sizeof(sockaddr); + return ::accept(sck,reinterpret_cast(addr),&addrlen); +} + +int plNet::Ioctl(const SOCKET sck, const long flags, unsigned long * val) +{ + return ::ioctlsocket(sck,flags,val); +} + +// static +const char * plNet::GetErrorMsg(int error) +{ + switch(error) + { + case WSAEINTR: return "Interrupted system call"; break; + case WSAEBADF: return "Bad file number"; break; + case WSAEACCES: return "Permission denied"; break; + case WSAEFAULT: return "Bad address"; break; + case WSAEINVAL: return "Invalid argument"; break; + case WSAEMFILE: return "Too many open sockets"; break; + case WSAEWOULDBLOCK: return "Operation would block"; break; + case WSAEINPROGRESS: return "Operation now in progress"; break; + case WSAEALREADY: return "Operation already in progress"; break; + case WSAENOTSOCK: return "Socket operation on non-socket"; break; + case WSAEDESTADDRREQ: return "Destination address required"; break; + case WSAEMSGSIZE: return "Message too long"; break; + case WSAEPROTOTYPE: return "Protocol wrong type for socket"; break; + case WSAENOPROTOOPT: return "Bad protocol option"; break; + case WSAEPROTONOSUPPORT: return "Protocol not supported"; break; + case WSAESOCKTNOSUPPORT: return "Socket type not supported"; break; + case WSAEOPNOTSUPP: return "Operation not supported on socket"; break; + case WSAEPFNOSUPPORT: return "Protocol family not supported"; break; + case WSAEAFNOSUPPORT: return "Address family not supported"; break; + case WSAEADDRINUSE: return "Address already in use"; break; + case WSAEADDRNOTAVAIL: return "Can't assign requested address"; break; + case WSAENETDOWN: return "Network is down"; break; + case WSAENETUNREACH: return "Network is unreachable"; break; + case WSAENETRESET: return "Net connection reset"; break; + case WSAECONNABORTED: return "Software caused connection abort"; break; + case WSAECONNRESET: return "Connection reset by peer"; break; + case WSAENOBUFS: return "No buffer space available"; break; + case WSAEISCONN: return "Socket is already connected"; break; + case WSAENOTCONN: return "Socket is not connected"; break; + case WSAESHUTDOWN: return "Can't send after socket shutdown"; break; + case WSAETOOMANYREFS: return "Too many references, can't splice"; break; + case WSAETIMEDOUT: return "Connection timed out"; break; + case WSAECONNREFUSED: return "Connection refused"; break; + case WSAELOOP: return "Too many levels of symbolic links"; break; + case WSAENAMETOOLONG: return "File name too long"; break; + case WSAEHOSTDOWN: return "Host is down"; break; + case WSAEHOSTUNREACH: return "No route to host"; break; + case WSAENOTEMPTY: return "Directory not empty"; break; + case WSAEPROCLIM: return "Too many processes"; break; + case WSAEUSERS: return "Too many users"; break; + case WSAEDQUOT: return "Disc quota exceeded"; break; + case WSAESTALE: return "Stale NFS file handle"; break; + case WSAEREMOTE: return "Too many levels of remote in path"; break; + case WSASYSNOTREADY: return "Network subsystem is unavailable"; break; + case WSAVERNOTSUPPORTED: return "Winsock version not supported"; break; + case WSANOTINITIALISED: return "Winsock not yet initialized"; break; + case WSAHOST_NOT_FOUND: return "Host not found"; break; + case WSATRY_AGAIN: return "Non-authoritative host not found"; break; + case WSANO_RECOVERY: return "Non-recoverable errors"; break; + case WSANO_DATA: return "Valid name, no data record of requested type"; break; + case WSAEDISCON: return "Graceful disconnect in progress"; break; + case WSASYSCALLFAILURE: return "System call failure"; break; + case WSA_NOT_ENOUGH_MEMORY: return "Insufficient memory available"; break; + case WSA_OPERATION_ABORTED: return "Overlapped operation aborted"; break; + case WSA_IO_INCOMPLETE: return "Overlapped I/O object not signalled"; break; + case WSA_IO_PENDING: return "Overlapped I/O will complete later"; break; + //case WSAINVALIDPROCTABLE: return "Invalid proc. table from service provider"; break; + //case WSAINVALIDPROVIDER: return "Invalid service provider version number"; break; + //case WSAPROVIDERFAILEDINIT, return "Unable to init service provider"; break; + case WSA_INVALID_PARAMETER: return "One or more parameters are invalid"; break; + case WSA_INVALID_HANDLE: return "Event object handle not valid"; break; + }; + return "\0"; +} + +//////////////////////////////////////////////////// +// UNIX socket interface +#elif HS_BUILD_FOR_UNIX + +#include + +plNet::plNet() +{ } + +plNet::~plNet() +{ } + +SOCKET plNet::NewUDP() +{ + return ::socket(AF_INET, SOCK_DGRAM, 0); +} + +SOCKET plNet::NewTCP() +{ + SOCKET x = ::socket(AF_INET, SOCK_STREAM, 0); + unsigned int timeoutval; + timeoutval = kDefaultSocketTimeout; + setsockopt(x, SOL_SOCKET, (int)SO_RCVTIMEO,(const char*)&timeoutval,sizeof(timeoutval)); + timeoutval = kDefaultSocketTimeout; + setsockopt(x, SOL_SOCKET, (int)SO_SNDTIMEO,(const char*)&timeoutval,sizeof(timeoutval)); + return x; +} + +int plNet::GetError() +{ + return errno; +} + +int plNet::Read(const SOCKET sck, char * buf, const int size) +{ + return ::recv(sck,buf,size,0); +} + +int plNet::Write(const SOCKET sck, const char * buf, const int len) +{ + return ::send(sck,buf,len,0); +} + +int plNet::ReadFrom(const SOCKET sck, char * buf, int len, sockaddr_in * addr) +{ + unsigned addrlen = sizeof(sockaddr); + return ::recvfrom(sck,buf,len,0,reinterpret_cast(addr),&addrlen); +} + +int plNet::WriteTo(const SOCKET sck, const char * buf, const int len, sockaddr_in * addr) +{ + return ::sendto(sck,buf,len,0,reinterpret_cast(addr),sizeof(sockaddr)); +} + +int plNet::Connect(const SOCKET sck, const sockaddr_in * addr) +{ + return ::connect(sck, reinterpret_cast(addr), sizeof(sockaddr)); +} + +int plNet::Close(const SOCKET sck) +{ + return ::close(sck); +} + +int plNet::Bind(const SOCKET sck, const sockaddr_in * addr) +{ + return ::bind(sck,reinterpret_cast(addr),sizeof(sockaddr)); +} + +int plNet::Listen(const SOCKET sck, const int qsize) +{ + return ::listen(sck,qsize); +} + +int plNet::Accept(const SOCKET sck, sockaddr_in * addr) +{ + unsigned addrlen = sizeof(sockaddr); + return ::accept(sck,reinterpret_cast(addr),&addrlen); +} + +int plNet::Ioctl(const SOCKET sck, const long flags, unsigned long * val) +{ + return ::ioctl(sck,flags,val); +} + +#else +#error "plNet: Sockets not ported" +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plNet.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plNet.h new file mode 100644 index 00000000..d871465d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plNet.h @@ -0,0 +1,101 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plNet_h_inc +#define plNet_h_inc + + +#include "hsTypes.h" // for hsAssert + + +//////////////////////////////////////////////////// +// Windows net types +#if HS_BUILD_FOR_WIN32 + +#include + +const int kBlockingError = WSAEWOULDBLOCK; +const int kTimeoutError = WSAETIMEDOUT; +const SOCKET kBadSocket = 0xffffffff; +typedef int socklen_t; + + +//////////////////////////////////////////////////// +// UNIX net types +#else +#ifdef HS_BUILD_FOR_FREEBSD +#include +#endif +#include +#include +#include +#include +#include +#include + +typedef int SOCKET; +const int kBlockingError = EWOULDBLOCK; +const int kTimeoutError = ETIMEDOUT; +const SOCKET kBadSocket = -1; + +// must #define BSDBLOCK if compiling on BSD + +#endif + +const unsigned int kDefaultSocketTimeout = 5*60*1000; // 5 mins in millis + +//////////////////////////////////////////////////// +// OS socket interface wrapper +struct plNet +{ + static SOCKET NewUDP(); + static SOCKET NewTCP(); + static int GetError(); + static int Read(const SOCKET sck, char * buf, const int size); + static int Write(const SOCKET sck, const char * buf, const int len); + static int ReadFrom(const SOCKET sck, char * buf, int len, sockaddr_in * addr); + static int WriteTo(const SOCKET sck, const char * buf, const int len, sockaddr_in * addr); + static int Connect(const SOCKET sck, const sockaddr_in * addr); + static int Close(const SOCKET sck); + static int Bind(const SOCKET sck, const sockaddr_in * addr); + static int Listen(const SOCKET sck, const int qsize); + static int Accept(const SOCKET sck, sockaddr_in * addr); + static int Ioctl(const SOCKET sck, const long flags, unsigned long * val); + static const char * plNet::GetErrorMsg(int error); + // TODO: Add get/setsockopt() here + ~plNet(); +private: + static plNet _; + plNet(); + // not impl + plNet(const plNet &); + plNet & operator=(const plNet &); +}; + + + + + +#endif // plNet_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plOutgoingUdpSocket.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plOutgoingUdpSocket.cpp new file mode 100644 index 00000000..b34563bb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plOutgoingUdpSocket.cpp @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plOutgoingUdpSocket.h" +#include "plFdSet.h" +#include "../pnNetCommon/plNetAddress.h" + +#if HS_BUILD_FOR_UNIX +#include +#include +#include +#endif + + +plOutgoingUdpSocket::plOutgoingUdpSocket() +{ +} + +plOutgoingUdpSocket::plOutgoingUdpSocket(SOCKET sck) +: plSocket(sck) +{ + +} + +bool plOutgoingUdpSocket::operator==(const plOutgoingUdpSocket & rhs) +{ + return fSocket==rhs.fSocket; +} + +int plOutgoingUdpSocket::SetSendBufferSize(int insize) +{ + if (setsockopt(fSocket, (int) SOL_SOCKET, (int) SO_SNDBUF, (char *) &insize, sizeof(int))) + return -1; + return 0; +} + +bool plOutgoingUdpSocket::ActiveOpen(plNetAddress & addr) +{ + SetSocket(plNet::NewUDP()); + if(fSocket == kBadSocket) + return false; + + if(plNet::Connect(fSocket, &addr.GetAddressInfo()) != 0) + return ErrorClose(); + + return true; +} + + +bool plOutgoingUdpSocket::ActiveOpenNonBlocking(plNetAddress & addr) +{ + SetSocket(plNet::NewUDP()); + if(fSocket == kBadSocket) + return false; + + SetBlocking(false); + + if(plNet::Connect(fSocket, &addr.GetAddressInfo()) != 0) + return ErrorClose(); + + return true; +} + + +// Returns: +// - if error +// 0 if socket closed or size=0 +// + bytes written +int plOutgoingUdpSocket::SendData(const char * data, int size) +{ + return plNet::Write(fSocket,data,size); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plOutgoingUdpSocket.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plOutgoingUdpSocket.h new file mode 100644 index 00000000..e4b21921 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plOutgoingUdpSocket.h @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plOutgoingUdpSocket_h_inc +#define plOutgoingUdpSocket_h_inc + +#include "plSocket.h" + +class plNetAddress; + + +class plOutgoingUdpSocket : public plSocket +{ +public: + plOutgoingUdpSocket(); + plOutgoingUdpSocket(SOCKET sck); + bool operator==(const plOutgoingUdpSocket & rhs); + int SetSendBufferSize(int size); + bool ActiveOpen(plNetAddress & addr); + bool ActiveOpenNonBlocking(plNetAddress & addr); + int SendData(const char * data, int size); + friend bool operator<(const plOutgoingUdpSocket & lhs, const plOutgoingUdpSocket & rhs); + friend bool operator==(const plOutgoingUdpSocket & lhs, const plOutgoingUdpSocket & rhs); +}; + + +inline bool operator<(const plOutgoingUdpSocket & lhs, const plOutgoingUdpSocket & rhs) +{ + return lhs.fSocket. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsUtils.h" +#include "plRingBuffer.h" + +plRingBuffer::plRingBuffer(int size) +: plMemBuffer(size) +{ + fEndPos = 0; + fStartPos = 0; +} + +void plRingBuffer::FullCompress() +{ + if (fStartPos == fEndPos) + { + fStartPos = 0; + fEndPos = 0; + } + else + { + ForceWindowSlide(); + } +} + +void plRingBuffer::Compress() +{ + if (fStartPos == fEndPos) + { + fStartPos = 0; + fEndPos = 0; + } + else if(fStartPos >= GetBufferSize() / 2) + { + ForceWindowSlide(); + } +} + +bool plRingBuffer::Put(const char * data, int len) +{ + bool ans = false; + + if (len > BufferAvailable()) + Compress(); + + if (len <= BufferAvailable()) + { + memcpy(GetBufferOpen(), data, len); + fEndPos += len; + ans = true; + } + return ans; +} + +bool plRingBuffer::Get(char * data, int len) +{ + bool ans = false; + + if (len < AmountBuffered()) + { + memcpy(data, GetBufferStart(), len); + fStartPos += len; + Compress(); + ans = true; + } + return ans; +} + +int plRingBuffer::AmountBuffered() +{ + return fEndPos - fStartPos; +} + +int plRingBuffer::BufferAvailable() +{ + return GetBufferSize() - fEndPos; +} + +void plRingBuffer::Reset() +{ + fStartPos = 0; + fEndPos = 0; +} + +char * plRingBuffer::GetBufferStart() +{ + return fBuffer+fStartPos; +} + +char * plRingBuffer::GetBufferOpen() +{ + return fBuffer+fEndPos; +} + +void plRingBuffer::ForceWindowSlide() +{ + int len = AmountBuffered(); + if(len > 0 && fStartPos != 0) + { + memmove(fBuffer, GetBufferStart(), len); + fStartPos = 0; + fEndPos = len; + } +} + +bool plRingBuffer::PutFast(const char * data, int len) +{ + // no checking be careful + memcpy(GetBufferOpen(), data, len); // or memmove? + fEndPos += len; + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plRingBuffer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plRingBuffer.h new file mode 100644 index 00000000..93150ed0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plRingBuffer.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plRingBuffer_h_inc +#define plRingBuffer_h_inc + +#include "plMemBuffer.h" + + +class plRingBuffer : protected plMemBuffer +{ +public: + plRingBuffer(int size=4096); + void FullCompress(); + void Compress(); + bool Put(const char * data, int len); + bool Get(char * data, int len); + int AmountBuffered(); + int BufferAvailable(); + void Reset(); + +protected: + int fStartPos; + int fEndPos; + char * GetBufferStart(); + char * GetBufferOpen(); + void ForceWindowSlide(); +#define FastGetBufferStart() (fBuffer+fStartPos) +#define FastAmountBuffered() (fEndPos-fStartPos) + bool PutFast(const char * data, int len); + +}; + + + +#endif // plRingBuffer_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plSocket.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plSocket.cpp new file mode 100644 index 00000000..ee51c799 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plSocket.cpp @@ -0,0 +1,195 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plSocket.h" + +#if HS_BUILD_FOR_UNIX +#include +#endif + +///////////////////////////////////////////////// + +plSocket::plSocket() +{ + fCloseOnDestroy = false; + fCloseBeforeSet = false; + fTimeoutsSet = 0; + fSocket = kBadSocket; +} + + +plSocket::plSocket(SOCKET sck) +{ + fCloseOnDestroy = false; + fCloseBeforeSet = false; + fTimeoutsSet = 0; + SetSocket(sck); +} + +plSocket::~plSocket() +{ + if (fCloseOnDestroy) + Close(); +} + +bool plSocket::operator==(const plSocket & rhs) +{ + return fSocket == rhs.fSocket; +} + +bool plSocket::ErrorClose() +{ + if(Active()) + plNet::Close(fSocket); + fSocket = kBadSocket; + return false; +} + + +bool plSocket::Active() +{ + return (fSocket != kBadSocket); +} + + +void plSocket::Close() +{ + if(Active()) + plNet::Close(fSocket); + fSocket = kBadSocket; +} + + +int plSocket::GetLastError() +{ + return plNet::GetError(); +} + + +void plSocket::SetSocket(SOCKET sck) +{ + if (fSocket!=sck) + { + if (fCloseBeforeSet) + Close(); + fSocket = sck; + if (fTimeoutsSet & kRecvTimeoutSet) + setsockopt(fSocket, SOL_SOCKET, (int)SO_RCVTIMEO,(const char*)&fRecvTimeOut,sizeof(fRecvTimeOut)); + + if (fTimeoutsSet & kSendTimeoutSet) + setsockopt(fSocket, SOL_SOCKET, (int)SO_SNDTIMEO,(const char*)&fSendTimeOut,sizeof(fSendTimeOut)); + + IGetTimeOutsFromSocket(); + } +} + + +int plSocket::SetRecvBufferSize(int insize) +{ + if (setsockopt(fSocket, (int)SOL_SOCKET, (int)SO_RCVBUF, (char *)&insize, sizeof(int))) + return 0; + + return -1; +} + + +void plSocket::SetBlocking(bool value) +{ + unsigned long val = value?0:1; +#ifdef BSDBLOCK +// NOTE: Bsd does this a little differently, using fcntl() +#error "BSDBLOCK: This code needs to be verified right now. Don't assume it will work at all." + int flags = fcntl(fSocket,F_GETFL,val); + flags = flags |O_NONBLOCK; + fcntl(fSocket,F_SETFL,flags); +#else + plNet::Ioctl(fSocket,FIONBIO,&val); +#endif +} + + +bool plSocket::IsBlocking() +{ +#ifdef BSDBLOCK +#error "BSDBLOCK: TODO: IsBlocking() for bsd." +#else + int ans = plNet::Ioctl(fSocket,FIONBIO,NULL); + return (ans!=0); +#endif +} + + +void plSocket::SetReuseAddress() +{ + int opt = 1; + setsockopt(fSocket, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)); +} + +int plSocket::SetSendTimeOut(unsigned int milliSecs/* =0 */) +{ + fTimeoutsSet |= kSendTimeoutSet; + fSendTimeOut = milliSecs; + + if (fSocket != kBadSocket) + { + if (setsockopt(fSocket, SOL_SOCKET, (int)SO_SNDTIMEO,(const char*)&fSendTimeOut,sizeof(fSendTimeOut)) != 0) + return -1; + } + + return 0; +} + +int plSocket::SetRecvTimeOut(unsigned int milliSecs/* =0 */) +{ + fTimeoutsSet |= kRecvTimeoutSet; + fRecvTimeOut = milliSecs; + + if (fSocket != kBadSocket) + { + if (setsockopt(fSocket, SOL_SOCKET, (int)SO_RCVTIMEO,(const char*)&fRecvTimeOut,sizeof(fRecvTimeOut)) != 0) + return -1; + } + + return 0; +} + +int plSocket::IGetTimeOutsFromSocket() +{ + unsigned int rtimeoutval, stimeoutval; + + if (fSocket != kBadSocket) + { + socklen_t sizeval; + sizeval = sizeof(rtimeoutval); + if (getsockopt(fSocket, SOL_SOCKET, (int)SO_RCVTIMEO,(char*)&rtimeoutval,&sizeval) != 0) + return -1; + sizeval = sizeof(stimeoutval); + if (getsockopt(fSocket, SOL_SOCKET, (int)SO_SNDTIMEO,(char*)&stimeoutval,&sizeval) != 0) + return -1; + fRecvTimeOut = rtimeoutval; + fSendTimeOut = stimeoutval; + } + return 0; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plSocket.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plSocket.h new file mode 100644 index 00000000..e726484e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plSocket.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plSocket_h_inc +#define plSocket_h_inc + +#include "plNet.h" + +//////////////////////////////////////////////////// +// Socket base class +// +// plSocket +// plTcpSocket +// plTcpListenSocket +// plIncomingUdpSocket - TODO (simple to impl, no immediate need) +// plOutgoingUdpSocket - TODO "" +// plConnectedOutgoingUdpSocket - TODO "" +// plFdSet +// + +class plSocket +{ +public: + plSocket(); + plSocket(SOCKET sck); + virtual ~plSocket(); + bool operator==(const plSocket & rhs); + static int GetLastError(); + void Close(); + void SetBlocking(bool value); + bool IsBlocking(); + void SetReuseAddress(); + bool Active(); + int SetRecvBufferSize(int size); + void SetSocket(SOCKET sck); + SOCKET GetSocket() const { return fSocket; } + void CloseOnDestroy(bool value) { fCloseOnDestroy=value; } + void CloseBeforeSet(bool value) { fCloseBeforeSet=value; } + int SetSendTimeOut(unsigned int milliSecs=0); + int SetRecvTimeOut(unsigned int milliSecs=0); + +protected: + enum TimeoutsSet + { + kRecvTimeoutSet = 1<<0, + kSendTimeoutSet = 1<<1, + }; + bool ErrorClose(); + SOCKET fSocket; + bool fCloseOnDestroy; + bool fCloseBeforeSet; + unsigned int fTimeoutsSet; + unsigned int fRecvTimeOut; + unsigned int fSendTimeOut; + int IGetTimeOutsFromSocket(); +}; + + + +#endif // plSocket_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpListenSocket.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpListenSocket.cpp new file mode 100644 index 00000000..d397d8c2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpListenSocket.cpp @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plTcpListenSocket.h" +#include "../pnNetCommon/plNetAddress.h" + + +// Initialize a socket for listening +bool plTcpListenSocket::OpenForListen(plNetAddress & inAddess, int backlogSize) +{ + ErrorClose(); + SetSocket(plNet::NewTCP()); + + SetReuseAddress(); + + if(plNet::Bind(fSocket, &inAddess.GetAddressInfo()) != 0) + return ErrorClose(); + + if(plNet::Listen(fSocket, backlogSize) != 0) + return ErrorClose(); + + return true; +} + + +// Initialize a socket for listening. non blocking version +bool plTcpListenSocket::OpenForListenNonBlocking(plNetAddress & inAddess, int backlogSize) +{ + ErrorClose(); + SetSocket(plNet::NewTCP()); + + SetReuseAddress(); + SetBlocking(false); // so GetIncomingConnection() won't block. + + if(plNet::Bind(fSocket, &inAddess.GetAddressInfo()) != 0) + return ErrorClose(); + + if(plNet::Listen(fSocket, backlogSize) != 0) + return ErrorClose(); + + return true; +} + + +// Accept an incoming connection +bool plTcpListenSocket::GetIncomingConnection(SOCKET & outNewSession, plNetAddress & outAddress) +{ + outNewSession = plNet::Accept(fSocket, &outAddress.GetAddressInfo()); + if(outNewSession == kBadSocket) + return false; + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpListenSocket.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpListenSocket.h new file mode 100644 index 00000000..a6a0845c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpListenSocket.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plTcpListenSocket_h_inc +#define plTcpListenSocket_h_inc + +#include "plSocket.h" + +class plNetAddress; + + +class plTcpListenSocket : public plSocket +{ +public: + bool OpenForListen(plNetAddress & inAddress, int backlogSize=1024); + bool OpenForListenNonBlocking(plNetAddress & inAddress, int backlogSize=1024); + bool GetIncomingConnection(SOCKET & outNewSession, plNetAddress & outRemoteAddress); +}; + + +#endif // plTcpListenSocket_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpSocket.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpSocket.cpp new file mode 100644 index 00000000..7b9e8f22 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpSocket.cpp @@ -0,0 +1,140 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plTcpSocket.h" +#include "plFdSet.h" +#include "../pnNetCommon/plNetAddress.h" + +#if HS_BUILD_FOR_UNIX +#include +#include +#include +#endif + + +plTcpSocket::plTcpSocket() +{ +} + +plTcpSocket::plTcpSocket(SOCKET sck) +: plSocket(sck) +{ + +} + +bool plTcpSocket::operator==(const plTcpSocket & rhs) +{ + return fSocket==rhs.fSocket; +} + +// Disable Nagle algorithm. +int plTcpSocket::SetNoDelay(void) +{ + int nodel = 1; + int ret1; + ret1 = setsockopt(fSocket, IPPROTO_TCP, TCP_NODELAY,(char *)&nodel, sizeof(nodel)); + + if(ret1 != 0) + return -1; + + return 0; +} + + +// Control the behavior of SO_LINGER +int plTcpSocket::SetLinger(int intervalSecs) +{ + linger ll; + ll.l_linger = intervalSecs; + ll.l_onoff = intervalSecs?1:0; + int ret1 = setsockopt(fSocket, SOL_SOCKET, SO_LINGER, (const char *)&ll,sizeof(linger)); + if(ret1 != 0) + return -1; + return 0; +} + + +int plTcpSocket::SetSendBufferSize(int insize) +{ + if (setsockopt(fSocket, (int) SOL_SOCKET, (int) SO_SNDBUF, (char *) &insize, sizeof(int))) + return -1; + return 0; +} + +bool plTcpSocket::ActiveOpen(plNetAddress & addr) +{ + SetSocket(plNet::NewTCP()); + if(fSocket == kBadSocket) + return false; + + if(plNet::Connect(fSocket, &addr.GetAddressInfo()) != 0) + return ErrorClose(); + + return true; +} + + +bool plTcpSocket::ActiveOpenNonBlocking(plNetAddress & addr) +{ + SetSocket(plNet::NewTCP()); + if(fSocket == kBadSocket) + return false; + + SetBlocking(false); + + if(plNet::Connect(fSocket, &addr.GetAddressInfo()) != 0) + return ErrorClose(); + + return true; +} + + +// Returns: +// - if error +// 0 if socket closed or size=0 +// + bytes written +int plTcpSocket::SendData(const char * data, int size) +{ + return plNet::Write(fSocket,data,size); +} + + +// Returns: +// - if error +// 0 if socket closed or size=0 +// + bytes read +int plTcpSocket::RecvData(char * data, int len) +{ + plFdSet fdset; + fdset.SetForSocket(*this); + fdset.WaitForRead(false,fRecvTimeOut); + if (fdset.IsErrFor(*this)) + return -1; + if (fdset.IsSetFor(*this)) + return plNet::Read(fSocket,data,len); // returns -1 on error + else + return -2; +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpSocket.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpSocket.h new file mode 100644 index 00000000..364fa168 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSockets/plTcpSocket.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plTcpSocket_h_inc +#define plTcpSocket_h_inc + +#include "plSocket.h" + +class plNetAddress; + + +class plTcpSocket : public plSocket +{ +public: + plTcpSocket(); + plTcpSocket(SOCKET sck); + bool operator==(const plTcpSocket & rhs); + int SetNoDelay(); + int SetLinger(int intervalSecs=0); + int SetSendBufferSize(int size); + bool ActiveOpen(plNetAddress & addr); + bool ActiveOpenNonBlocking(plNetAddress & addr); + int SendData(const char * data, int size); + int RecvData(char * data, int size); + friend bool operator<(const plTcpSocket & lhs, const plTcpSocket & rhs); + friend bool operator==(const plTcpSocket & lhs, const plTcpSocket & rhs); +}; + + +inline bool operator<(const plTcpSocket & lhs, const plTcpSocket & rhs) +{ + return lhs.fSocket. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAutoProfile.h" + +#include "plgDispatch.h" +#include "../plNetClient/plNetClientMgr.h" +#include "../plNetClient/plNetLinkingMgr.h" + +#include "hsStream.h" +#include "hsTimer.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../pnTimer/plTimerCallbackManager.h" +#include "../plMessage/plTimerCallbackMsg.h" +#include "../plAvatar/plAvatarMgr.h" +#include "../plAvatar/plArmatureMod.h" +#include "../plModifier/plSpawnModifier.h" +#include "../plMessage/plConsoleMsg.h" +#include "../pnMessage/plClientMsg.h" +#include "../plAgeLoader/plAgeLoader.h" +#include "plProfileManagerFull.h" +#include "../plFile/plFileUtils.h" + +#include "../plPipeline/plDebugText.h" +#include "../pnMessage/plTimeMsg.h" + +#include "../plStatusLog/plStatusLog.h" +#include "../plVault/plVault.h" + +#include "../plContainer/plConfigInfo.h" // for plStringList + +// For taking screenshots +#include "../plGImage/plMipmap.h" +#include "../Apps/plClient/plClient.h" +#include "../plJPEG/plJPEG.h" +#include "plPipeline.h" + +#include + +class plAutoProfileImp : public plAutoProfile +{ +protected: + plStringList fAges; + int fNextAge; + int fNextSpawnPoint; + const char* fLastSpawnPointName; + // For profiling a single age + std::string fAgeName; + bool fLinkedToSingleAge; + bool fJustLinkToAges; + + UInt64 fLinkTime; + + std::string fStatusMessage; + + void INextProfile(); + bool INextAge(); + bool INextSpawnPoint(); + + void IInit(); + void IShutdown(); + +public: + plAutoProfileImp(); + + virtual void StartProfile(const char* ageName); + virtual void LinkToAllAges(); + + virtual hsBool MsgReceive(plMessage* msg); +}; + +plAutoProfile* plAutoProfile::Instance() +{ + static plAutoProfileImp theInstance; + return &theInstance; +} + +//////////////////////////////////////////////////////////////////////////////// + +plAutoProfileImp::plAutoProfileImp() : fNextAge(0), fNextSpawnPoint(0), fLastSpawnPointName(nil), fLinkedToSingleAge(false), fJustLinkToAges(false) +{ +} + +void plAutoProfileImp::StartProfile(const char* ageName) +{ + if (ageName) + fAgeName = ageName; + else + fAgeName = ""; + + IInit(); + + plProfileManagerFull::Instance().ActivateAllStats(); +} + +void plAutoProfileImp::LinkToAllAges() +{ + fJustLinkToAges = true; + IInit(); +} + +void plAutoProfileImp::IInit() +{ + // TODO: Find a better way to grab a list of age names, since the old data server + // no longer exists + /*plIDataServer* dataServer = plNetClientMgr::GetInstance()->GetDataServer(); + if (!dataServer) + return; + + dataServer->GetDatasetAges(fAges);*/ + + // The first age we link into is AvatarCustomization, since we have a new avatar. + // Coincidentally, the first age in our list is AvatarCustomization. However, + // sometimes linking from ACA to ACA causes a crash. To get around that, we just + // reverse the list, so it's last. + std::reverse(fAges.begin(), fAges.end()); + + fNextAge = 0; + + RegisterAs(kAutoProfile_KEY); + + plgDispatch::Dispatch()->RegisterForExactType(plAgeBeginLoadingMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + + plConsoleMsg* consoleMsg = TRACKED_NEW plConsoleMsg(plConsoleMsg::kExecuteLine, "Camera.AlwaysCut true"); + consoleMsg->Send(); +} + +#ifdef HS_BUILD_FOR_WIN32 +#include "hsWindows.h" +#include +#endif + +void plAutoProfileImp::IShutdown() +{ + // KLUDGE - Copy the load timing log, in case we used it + #define kTimingLog L"readtimings.0.log" + #define kAgeTimingLog L"agetimings.0.log" + wchar destPath[MAX_PATH]; + wchar sourcePath[MAX_PATH]; + + PathAddFilename(destPath, plProfileManagerFull::Instance().GetProfilePath(), kTimingLog, arrsize(destPath)); + PathGetLogDirectory(sourcePath, arrsize(sourcePath)); + PathAddFilename(sourcePath, sourcePath, kTimingLog, arrsize(sourcePath)); + plFileUtils::FileCopy(sourcePath, destPath); + + PathAddFilename(destPath, plProfileManagerFull::Instance().GetProfilePath(), kAgeTimingLog, arrsize(destPath)); + PathGetLogDirectory(sourcePath, arrsize(sourcePath)); + PathAddFilename(sourcePath, sourcePath, kAgeTimingLog, arrsize(sourcePath)); + plFileUtils::FileCopy(sourcePath, destPath); + +#ifdef HS_BUILD_FOR_WIN32 + ShellExecute(nil, nil, "PostRun.bat", nil, nil, SW_SHOWNORMAL); +#endif + + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + plgDispatch::Dispatch()->UnRegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->UnRegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); + + UnRegisterAs(kAutoProfile_KEY); + // Pump the queue so we get fully unregistered + plgDispatch::Dispatch()->MsgQueueProcess(); + + plClientMsg* clientMsg = TRACKED_NEW plClientMsg(plClientMsg::kQuit); + clientMsg->Send(hsgResMgr::ResMgr()->FindKey(kClient_KEY)); +} + +void plAutoProfileImp::INextProfile() +{ + // Haven't linked to our first age yet, do that before we start profiling + if (fNextAge == 0) + { + if (!INextAge()) + IShutdown(); + } + else + { + // Log the stats for this spawn point + if (fLastSpawnPointName) + { + const char * ageName = NetCommGetAge()->ageDatasetName; + plProfileManagerFull::Instance().LogStats(ageName, fLastSpawnPointName); + + plMipmap mipmap; + if (plClient::GetInstance()->GetPipeline()->CaptureScreen(&mipmap)) + { + char fileName[256]; + sprintf(fileName, "%s%s_%s.jpg", + plProfileManagerFull::Instance().GetProfilePath(), + ageName, fLastSpawnPointName); + + plJPEG::Instance().SetWriteQuality(100); + plJPEG::Instance().WriteToFile(fileName, &mipmap); + } + + fLastSpawnPointName = nil; + } + + // Try to go to the next spawn point + if (!INextSpawnPoint()) + { + // Link to the next age + if (!INextAge()) + { + // We've done all the ages, shut down + IShutdown(); + } + } + } +} + +bool plAutoProfileImp::INextAge() +{ + const char* ageName = nil; + + if (fAgeName.length() > 0) + { + if (fLinkedToSingleAge) + return false; + + fLinkedToSingleAge = true; + ageName = fAgeName.c_str(); + } + else + { + if (fNextAge >= fAges.size()) + return false; + + ageName = fAges[fNextAge].c_str(); + } + + fNextAge++; + fNextSpawnPoint = 0; + + plAgeLinkStruct link; + link.GetAgeInfo()->SetAgeFilename(ageName); + link.SetLinkingRules(plNetCommon::LinkingRules::kBasicLink); + plNetLinkingMgr::GetInstance()->LinkToAge(&link); + + fStatusMessage = "Linking to age "; + fStatusMessage += ageName; + + return true; +} + +bool plAutoProfileImp::INextSpawnPoint() +{ + if (fJustLinkToAges) + return false; + + const char* kPerfSpawnPrefix = "cPerf-"; + int kPerfSpawnLen = strlen(kPerfSpawnPrefix); + + // Find the next perf spawn point + bool foundGood = false; + while (fNextSpawnPoint < plAvatarMgr::GetInstance()->NumSpawnPoints()) + { + const plSpawnModifier* spawnMod = plAvatarMgr::GetInstance()->GetSpawnPoint(fNextSpawnPoint); + fLastSpawnPointName = spawnMod->GetKeyName(); + + if (strncmp(fLastSpawnPointName, kPerfSpawnPrefix, kPerfSpawnLen) == 0) + { + fStatusMessage = "Profiling spawn point "; + fStatusMessage += fLastSpawnPointName; + + foundGood = true; + break; + } + else + fNextSpawnPoint++; + } + + if (!foundGood) + { + fLastSpawnPointName = nil; + fStatusMessage = "No profile spawn point found"; + return false; + } + + plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar(); + if (avatar) + { + double fakeTime = 0.0f; + avatar->SpawnAt(fNextSpawnPoint, fakeTime); + } + + fNextSpawnPoint++; + + plTimerCallbackMsg* timerMsg = TRACKED_NEW plTimerCallbackMsg(GetKey()); + plgTimerCallbackMgr::NewTimer(30, timerMsg); + + return true; +} + +hsBool plAutoProfileImp::MsgReceive(plMessage* msg) +{ + plEvalMsg* evalMsg = plEvalMsg::ConvertNoRef(msg); + if (evalMsg) + { + if (fStatusMessage.length() > 0) + plDebugText::Instance().DrawString(10, 10, fStatusMessage.c_str()); + } + + plAgeLoadedMsg* ageLoaded = plAgeLoadedMsg::ConvertNoRef(msg); + if (ageLoaded) + { + if (!ageLoaded->fLoaded) + { + fLinkTime = hsTimer::GetFullTickCount(); + hsStatusMessage("Age unloaded"); + } + return true; + } + + plInitialAgeStateLoadedMsg* ageStateLoaded = plInitialAgeStateLoadedMsg::ConvertNoRef(msg); + if (ageStateLoaded) + { + if (fNextAge > 0) + { + fLinkTime = hsTimer::GetFullTickCount() - fLinkTime; + float ms = hsTimer::FullTicksToMs(fLinkTime); + + hsStatusMessageF("Age %s finished load, took %.1f ms", + fAges[fNextAge-1].c_str(), + ms); + + plStatusLog::AddLineS("agetimings.log", "Age %s took %.1f ms", + fAges[fNextAge-1].c_str(), + ms); + } + + fStatusMessage = "Age loaded. Preparing to profile."; + + // Age is loaded, start profiling in 5 seconds (to make sure the avatar is linked in all the way) + plTimerCallbackMsg* timerMsg = TRACKED_NEW plTimerCallbackMsg(GetKey()); + plgTimerCallbackMgr::NewTimer(5, timerMsg); + return true; + } + + plTimerCallbackMsg* timerMsg = plTimerCallbackMsg::ConvertNoRef(msg); + if (timerMsg) + { + INextProfile(); + return true; + } + + // When the first age starts to load, register the stupid avatar customization variable + // so the calibration screen won't pop up and ruin everything. I'm sure I could try + // and do this earlier, but I'm not sure when that player folder is set so screw it, it works here. + plAgeBeginLoadingMsg* ageBeginLoadingMsg = plAgeBeginLoadingMsg::ConvertNoRef(msg); + if (ageBeginLoadingMsg) + { + plgDispatch::Dispatch()->UnRegisterForExactType(plAgeBeginLoadingMsg::Index(), GetKey()); + VaultAddChronicleEntryAndWait(L"InitialAvCursomizationsDone", 0, L"1"); + return true; + } + + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plAutoProfile.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plAutoProfile.h new file mode 100644 index 00000000..db48caf0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plAutoProfile.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAutoProfile_h_inc +#define plAutoProfile_h_inc + +#include "../pnKeyedObject/hsKeyedObject.h" + +class plAutoProfile : public hsKeyedObject +{ +public: + CLASSNAME_REGISTER(plAutoProfile); + GETINTERFACE_ANY(plAutoProfile, hsKeyedObject); + + static plAutoProfile* Instance(); + + // If ageName is nil, do all ages + virtual void StartProfile(const char* ageName = nil)=0; + + // For when we just want to link to each age, for other reasons (profiling load times) + virtual void LinkToAllAges()=0; +}; + +#endif // plAutoProfile_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plCalculatedProfiles.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plCalculatedProfiles.cpp new file mode 100644 index 00000000..2898838d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plCalculatedProfiles.cpp @@ -0,0 +1,304 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plProfile.h" +#include "plProfileManager.h" +#include "../plNetClient/plNetClientMgr.h" +#include "hsTimer.h" + +plProfile_CreateCounter("Age Upload BitsPerSec", "Network", UploadAgeBitsPerSec); +plProfile_CreateMemCounter("Upload Bytes", "Network", UploadBW); +plProfile_CreateMemCounter("Upload Avg Packet Size", "Network", UploadAPS); +plProfile_CreateCounter("Upload Num Packets", "Network", UploadPPS); +plProfile_CreateCounter("Upload Avg Packets Queued", "Network", UploadPQ); +plProfile_CreateCounter("Recvd Multiple Acks", "Network", RMultAcksPQ); + +plProfile_CreateCounter("Age Download BitsPerSec", "Network", DownloadAgeBitsPerSec); +plProfile_CreateMemCounter("Download Bytes", "Network", DownloadBW); +plProfile_CreateMemCounter("Download Avg Packet Size", "Network", DownloadAPS); +plProfile_CreateCounter("Download Num Packets", "Network", DownloadPPS); +plProfile_CreateCounter("Download Avg Packets Queued", "Network", DownloadPQ); +plProfile_CreateCounter("Download Dropped Pkts", "Network", DownloadDP); + +plProfile_CreateCounter("Remote Players", "Network", RemotePlayers); +plProfile_CreateCounter("Peers", "Network", Peers); +plProfile_CreateCounter("Player ID", "Network", PlayerID); +plProfile_CreateCounter("Account ID", "Network", AccountID); + +plProfile_CreateTimer("Avg Receipt Time", "Network", AvgReceiptTime); +plProfile_CreateTimer("Peak Receipt Time", "Network", PeakReceiptTime); + +#ifdef HS_FIND_MEM_LEAKS +plProfile_CreateMemCounter("Allocated", "Memory", MemAllocated); +plProfile_CreateMemCounter("Peak Alloc", "Memory", MemPeakAlloc); +#endif + +static plProfileVar gVarRFPS("RFPS", "General", plProfileVar::kDisplayTime | plProfileVar::kDisplayFPS); + +plProfile_Extern(DrawTriangles); +plProfile_Extern(MatChange); +plProfile_CreateCounter("Polys Per Material", "General", PolysPerMat); + +#ifdef PL_PROFILE_ENABLED +#define plProfile_GetValue(varName) gProfileVar##varName.GetValue() +#else +#define plProfile_GetValue(varName) 0 +#endif + +void CalculateProfiles() +{ + // KLUDGE - do timing that overlaps the beginframe / endframe (where timing is normally reset) + static UInt32 lastTicks = plProfileManager::GetTime(); + UInt32 curTicks = plProfileManager::GetTime(); + gVarRFPS.Set(curTicks - lastTicks); + lastTicks = curTicks; + + // KLUDGE - calulate the polys per material + if (plProfile_GetValue(MatChange) == 0) + plProfile_Set(PolysPerMat, 0); + else + plProfile_Set(PolysPerMat, plProfile_GetValue(DrawTriangles) / plProfile_GetValue(MatChange)); + + #ifdef HS_FIND_MEM_LEAKS +// plProfile_Set(MemAllocated, MemGetAllocated()); +// plProfile_Set(MemPeakAlloc, MemGetPeakAllocated()); + #endif + + // Network stats + plNetClientMgr* nc = plNetClientMgr::GetInstance(); + if (!nc->GetFlagsBit(plNetClientMgr::kDisabled)) + { +#if 0 + hsAssert(nc->GetNetCore(), "nil net core in stats?"); + plNetCoreStats* ns = nc->GetNetCore()->GetStats(); + + plProfile_Set(UploadAgeBitsPerSec, (UInt32)nc->GetNetClientStats().GetAgeStatsULBitsPerSec()); + plProfile_Set(UploadBW, ns->GetULBits()/8); + plProfile_Set(UploadPPS, ns->GetULPackets()); + plProfile_Set(UploadAPS, (UInt32)ns->GetULAvgPacketBytes()); + plProfile_Set(UploadPQ, (UInt32)ns->GetULAvgNumPacketsQueued()); + plProfile_Set(RMultAcksPQ, nc->GetNetClientStats().GetRecvdMultipleAcks()); + + plProfile_Set(DownloadAgeBitsPerSec, (UInt32)nc->GetNetClientStats().GetAgeStatsDLBitsPerSec()); + plProfile_Set(DownloadBW, ns->GetDLBits()/8); + plProfile_Set(DownloadPPS, ns->GetDLPackets()); + plProfile_Set(DownloadAPS, (UInt32)ns->GetDLAvgPacketBytes()); + plProfile_Set(DownloadPQ, (UInt32)ns->GetDLAvgNumPacketsQueued()); + plProfile_Set(DownloadDP, ns->GetDLDroppedPackets()); + + plProfile_Set(RemotePlayers, nc->RemotePlayerKeys().size()); + plProfile_Set(Peers, ns->GetNumPeers()); + plProfile_Set(PlayerID, nc->GetPlayerID()); + plProfile_Set(AccountID, nc->GetAccountID()); + + plProfile_Set(AvgReceiptTime, hsTimer::PrecSecsToTicks(ns->GetAvgReceiptTime())); + plProfile_Set(PeakReceiptTime, hsTimer::PrecSecsToTicks(ns->GetPeakReceiptTime())); +#endif + } +} + +#include "../plPipeline/plPlates.h" + +static plGraphPlate* fFPSPlate = nil; +static plGraphPlate* fNetBWPlate = nil; +static plGraphPlate* fNetPPSPlate = nil; +static plGraphPlate* fNetQueuesPlate = nil; +static plGraphPlate* fNetAvgBWPlate = nil; +static plGraphPlate* fNetAvgPPSPlate = nil; +static plGraphPlate* fNetAvgQueuesPlate = nil; + +static int ICreateStdPlate(plGraphPlate** graph) +{ + if (plPlateManager::InstanceValid()) + { + plPlateManager::Instance().CreateGraphPlate(graph); + (*graph)->SetSize(0.25, 0.25); + (*graph)->SetDataRange(0, 100, 100); + return hsOK; + } + return hsFail; +} + +void CreateStandardGraphs(const char* groupName, bool create) +{ + if (strcmp(groupName, "General") == 0) + { + if (create) + { + if (ICreateStdPlate(&fFPSPlate) == hsOK) + { + fFPSPlate->SetTitle("mSecs"); + fFPSPlate->SetLabelText("Tot", "Draw", "Upd"); + } + } + else + { + plPlateManager::Instance().DestroyPlate(fFPSPlate); + fFPSPlate = nil; + } + } + else if (strcmp(groupName, "Network") == 0) + { + if (create) + { + if (ICreateStdPlate(&fNetBWPlate) == hsOK) + { + fNetBWPlate->SetDataLabels(0, 2000); + fNetBWPlate->SetTitle("Bytes"); + fNetBWPlate->SetLabelText("UL", "DL"); + + ICreateStdPlate(&fNetPPSPlate); + fNetPPSPlate->SetDataLabels(0, 20); + fNetPPSPlate->SetTitle("Packets"); + fNetPPSPlate->SetLabelText("UL", "DL"); + + ICreateStdPlate(&fNetQueuesPlate); + fNetQueuesPlate->SetDataLabels(0, 20); + fNetQueuesPlate->SetTitle("Queue Counts"); + fNetQueuesPlate->SetLabelText("UL", "DL"); + + ICreateStdPlate(&fNetQueuesPlate); + fNetQueuesPlate->SetDataLabels(0, 20); + fNetQueuesPlate->SetTitle("Queue Counts"); + fNetQueuesPlate->SetLabelText("UL", "DL"); + + ICreateStdPlate(&fNetQueuesPlate); + fNetQueuesPlate->SetDataLabels(0, 20); + fNetQueuesPlate->SetTitle("Queue Counts"); + fNetQueuesPlate->SetLabelText("UL", "DL"); + + ICreateStdPlate(&fNetAvgBWPlate); + fNetAvgBWPlate->SetDataLabels(0, 5000); + fNetAvgBWPlate->SetTitle("Avg BytesPS"); + fNetAvgBWPlate->SetLabelText("UL", "DL"); + + ICreateStdPlate(&fNetAvgPPSPlate); + fNetAvgPPSPlate->SetDataLabels(0, 40); + fNetAvgPPSPlate->SetTitle("Avg PacketsPS"); + fNetAvgPPSPlate->SetLabelText("UL", "DL"); + + ICreateStdPlate(&fNetAvgQueuesPlate); + fNetAvgQueuesPlate->SetDataLabels(0, 40); + fNetAvgQueuesPlate->SetTitle("Avg Queue CountsPS"); + fNetAvgQueuesPlate->SetLabelText("UL", "DL"); + } + } + else + { + plPlateManager::Instance().DestroyPlate(fNetBWPlate); + plPlateManager::Instance().DestroyPlate(fNetPPSPlate); + plPlateManager::Instance().DestroyPlate(fNetQueuesPlate); + plPlateManager::Instance().DestroyPlate(fNetAvgBWPlate); + plPlateManager::Instance().DestroyPlate(fNetAvgPPSPlate); + plPlateManager::Instance().DestroyPlate(fNetAvgQueuesPlate); + fNetBWPlate = nil; + fNetPPSPlate = nil; + fNetQueuesPlate = nil; + fNetAvgBWPlate = nil; + fNetAvgPPSPlate = nil; + fNetAvgQueuesPlate = nil; + } + } +} + +plProfile_CreateTimer("Draw", "General", DrawTime); +plProfile_CreateTimer("Update", "General", UpdateTime); + +void UpdateStandardGraphs(float xPos, float yPos) +{ + #define PositionPlate(plate) \ + plate->SetPosition(xPos, yPos); \ + yPos += 0.25; \ + plate->SetVisible(true); + + if (fFPSPlate) + { + fFPSPlate->AddData( + gVarRFPS.GetValue(), + plProfile_GetValue(DrawTime), + plProfile_GetValue(UpdateTime)); + PositionPlate(fFPSPlate); + } + + plNetClientMgr* nc = plNetClientMgr::GetInstance(); + + +#if 0 + plNetCoreStats* ns = nc ? nc->GetNetCore()->GetStats() : nil; + + if (!nc || !ns) + return; + + if (fNetBWPlate) + { + fNetBWPlate->AddData( + (UInt32)(ns->GetULBits()/8.f), + (UInt32)(ns->GetDLBits()/8.f)); + PositionPlate(fNetBWPlate); + } + + if (fNetPPSPlate) + { + fNetPPSPlate->AddData( + ns->GetULPackets(), + ns->GetDLPackets()); + PositionPlate(fNetPPSPlate); + } + + if (fNetQueuesPlate) + { + unsigned int ul, dl; + nc->GetNetCore()->GetOutQueueMsgCount(plNetCore::kPeerAll,ul); + nc->GetNetCore()->GetInQueueMsgCount(plNetCore::kPeerAll,dl); + fNetQueuesPlate->AddData(ul,dl); + PositionPlate(fNetQueuesPlate); + } + + if (fNetAvgBWPlate) + { + fNetAvgBWPlate->AddData( + (UInt32)(ns->GetULBitsPS()/8.f), + (UInt32)(ns->GetDLBitsPS()/8.f)); + PositionPlate(fNetAvgBWPlate); + } + + if (fNetAvgPPSPlate) + { + fNetAvgPPSPlate->AddData( + (Int32)ns->GetULNumPacketsPS(), + (Int32)ns->GetDLNumPacketsPS()); + PositionPlate(fNetAvgPPSPlate); + } + + if (fNetAvgQueuesPlate) + { + fNetAvgQueuesPlate->AddData( + (Int32)ns->GetULAvgNumPacketsQueued(), + (Int32)ns->GetDLAvgNumPacketsQueued()); + PositionPlate(fNetAvgQueuesPlate); + } +#endif + +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plCalculatedProfiles.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plCalculatedProfiles.h new file mode 100644 index 00000000..4ac7c412 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plCalculatedProfiles.h @@ -0,0 +1,29 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +void CalculateProfiles(); +void CreateStandardGraphs(const char* groupName, bool create); +void UpdateStandardGraphs(float xPos, float yPos); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plProfileManagerFull.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plProfileManagerFull.cpp new file mode 100644 index 00000000..4083f337 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plProfileManagerFull.cpp @@ -0,0 +1,676 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plProfileManagerFull.h" +#include "plProfileManager.h" + +#include "../plPipeline/plDebugText.h" +#include "../plPipeline/plPlates.h" + +#include "plCalculatedProfiles.h" + +#include "hsStream.h" +#include "../pnUtils/pnUtils.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "../plFile/plFileUtils.h" + +plProfileManagerFull::plProfileManagerFull() : + fVars(plProfileManager::Instance().fVars), + fLogStats(false), + fShowLaps(nil), + fMinLap(0), + fDetailGraph(nil) +{ +} + +plProfileManagerFull& plProfileManagerFull::Instance() +{ + static plProfileManagerFull theInstance; + return theInstance; +} + +void plProfileManagerFull::GetGroups(GroupSet& groups) +{ + groups.clear(); + for (int i = 0; i < fVars.size(); i++) + groups.insert(fVars[i]->GetGroup()); +} + +void plProfileManagerFull::ShowGroup(const char* groupName) +{ + if (!groupName) + groupName = "General"; + + // If we're already showing this group, stop + if (fShowGroups.find(groupName) != fShowGroups.end()) + { + CreateStandardGraphs(groupName, false); + fShowGroups.erase(groupName); + ISetActive(groupName, false); + } + else + { + const char* shareGroupName = nil; + for (int i = 0; i < fVars.size(); i++) + { + if (stricmp(fVars[i]->GetGroup(), groupName) == 0) + { + shareGroupName = fVars[i]->GetGroup(); + } + } + + // We do have a group with this name, so insert one of the variable's + // pointers to the name into our list (we can hang on to those pointers) + if (shareGroupName) + { + ISetActive(shareGroupName, true); + CreateStandardGraphs(shareGroupName, true); + fShowGroups.insert(shareGroupName); + } + } +} + +void plProfileManagerFull::ShowNextGroup() +{ + const char* curGroup = nil; + if (fShowGroups.begin() != fShowGroups.end()) + curGroup = *(fShowGroups.begin()); + + GroupSet groups; + GetGroups(groups); + + const char* nextGroup = nil; + if (curGroup) + { + CreateStandardGraphs(curGroup, false); + + GroupSet::iterator it = groups.find(curGroup); + it++; + if (it != groups.end()) + { + nextGroup = *it; + } + ISetActive(curGroup,false); + } + else + { + nextGroup = *(groups.begin()); + } + + fShowGroups.clear(); + if (nextGroup) + { + ISetActive(nextGroup, true); + CreateStandardGraphs(nextGroup, true); + fShowGroups.insert(nextGroup); + } +} + +plProfileVar* plProfileManagerFull::IFindTimer(const char *name) +{ + for (int i = 0; i < fVars.size(); i++) + { + if (stricmp(fVars[i]->GetName(), name) == 0) + return fVars[i]; + } + + return nil; +} + +void plProfileManagerFull::GetLaps(LapNames& lapNames) +{ + for (int i = 0; i < fVars.size(); i++) + { + plProfileVar* var = fVars[i]; + if (var->GetLaps()) + { + LapPair lapPair; + lapPair.group = var->GetGroup(); + lapPair.varName = var->GetName(); + lapNames.push_back(lapPair); + } + } +} + + +enum +{ + kColName, + kColValue, + kColAvg, + kColMax, + kColIndex, +}; + +typedef std::vector ProfileGroup; + +static void PrintColumn(ProfileGroup& group, const char* groupName, int column, int x, int y, int& width, int& height, int off =0) +{ + plDebugText& txt = plDebugText::Instance(); + int yInc = txt.GetFontHeight() + 2; + + height = 0; + width = 0; + + width = hsMaximum(width, txt.CalcStringWidth(groupName) + 1); + txt.DrawString(x, y+height, groupName, 255, 255, 255, 255, plDebugText::kStyleBold); + height += yInc; + + UInt32 samplesWidth = txt.CalcStringWidth("[000]"); + + for (int i = 0; i < group.size(); i++) + { + char str[1024]; + + switch (column) + { + case kColName: + strcpy(str, group[i]->GetName()); + + // Since we don't draw the samples text for stats that only have 1 sample, + // if the stat with the longest name is fluctuating between 1 and more than + // 1 sample the width of the column will jump around. So we calculate the + // width based on the stat name plus the width of the widest sample we should + // get + width = hsMaximum(width, txt.CalcStringWidth(str) + samplesWidth + 1); + + // Now add on the samples text, if we have any + if (group[i]->GetTimerSamples()) + { + char cnt[20]; + sprintf(cnt, "[%d]", group[i]->GetTimerSamples()); + strcat(str, cnt); + } + break; + case kColValue: + group[i]->PrintValue(str); + break; + case kColAvg: + group[i]->PrintAvg(str); + break; + case kColMax: + group[i]->PrintMax(str); + break; + case kColIndex: + sprintf(str,"[%3d]",i+off); + break; + } + + txt.DrawString(x, y+height, str); + if (column != kColName) + width = hsMaximum(width, txt.CalcStringWidth(str) + 1); + height += yInc; + } + + // So the columns don't jump around as much as values change, pad them out to a certain width + width = hsMaximum(width, txt.CalcStringWidth("000.0 ms") + 1); +} + +static void PrintGroup(ProfileGroup& group, const char* groupName, int& x, int& y) +{ + int width, height; + + PrintColumn(group, groupName, kColName, x, y, width, height); + x += width + 10; + + PrintColumn(group, "Avg", kColAvg, x, y, width, height); + x += width + 10; + + PrintColumn(group, "Cur", kColValue, x, y, width, height); + x += width + 10; + + PrintColumn(group, "Max", kColMax, x, y, width, height); + x += width + 10; + + y += height; +} + + +static void PrintLapGroup(ProfileGroup& group, const char* groupName, int& x, int& y, int min) +{ + int width, height; + + if(min > 0) + { + PrintColumn(group, "Index", kColIndex, x, y, width, height, min); + x += width + 10; + } + + PrintColumn(group, "Avg", kColAvg, x, y, width, height); + x += width + 10; + + PrintColumn(group, groupName, kColName, x, y, width, height); + x += width + 10; + + PrintColumn(group, "Cur", kColValue, x, y, width, height); + x += width + 10; + + y += height; +} + +void plProfileManagerFull::EndFrame() +{ + CalculateProfiles(); +} + +void plProfileManagerFull::Update() +{ + if (fLogStats) + ILogStats(); + + // + // Print the groups we're showing + // + int maxX = 0; + + int y = 10; + GroupSet::iterator it; + for (it = fShowGroups.begin(); it != fShowGroups.end(); it++) + { + const char* groupName = *it; + + std::vector group; + + for (int i = 0; i < fVars.size(); i++) + if (hsStrEQ(fVars[i]->GetGroup(), groupName)) + group.push_back(fVars[i]); + + int x = 10; + PrintGroup(group, groupName, x, y); + + maxX = hsMaximum(maxX, x); + y += 10; + } + + // + // Print the laps we're showing + // + if (fShowLaps && fShowLaps->GetLaps()) + { + plProfileLaps* laps = fShowLaps->GetLaps(); + + std::vector group; + int numLaps = laps->GetNumLaps(); + + if(numLaps < fMinLap) + fMinLap = 0; + for (int i = 0; i < numLaps; i++) + { + if(i >= fMinLap && i < (fMinLap + 40)) + group.push_back(laps->GetLap(i)); + } + y = 10; + char buf[256]; + sprintf(buf, "%s - %s", fShowLaps->GetGroup(), fShowLaps->GetName()); + PrintLapGroup(group, buf, maxX, y, fMinLap); + } + + // + // Update the graphs + // + float size = 0.25; + float xPos = 1 - size / 2; + float yPos = -1 + size / 2; + + for (int i = 0; i < fGraphs.size(); i++) + { + plGraphPlate* graph = fGraphs[i]; + plProfileVar* var = IFindTimer(graph->GetTitle()); + + if (var) + { + graph->SetPosition(xPos, yPos); + graph->AddData(var->GetValue()); + graph->SetVisible(true); + + yPos += size; + } + } + + UpdateStandardGraphs(xPos, yPos); + + float detailSize = 0.9; + float detailX = 1 - detailSize / 2; + float detailY = 1 - detailSize / 2; + if (fDetailGraph) + { + fDetailGraph->SetPosition(detailX,detailY); + double value; + double scale; + int i; + std::vector values; + for (i=0; iGetValue(); + scale = 100.0/((double)(fDetailVars[i].max-fDetailVars[i].min)); + value = scale*value-fDetailVars[i].min; + values.push_back((Int32)value); + } + fDetailGraph->AddData(values); + fDetailGraph->SetVisible(true); + } +} + +void plProfileManagerFull::ActivateAllStats() +{ + for (int i = 0; i < fVars.size(); i++) + { + fVars[i]->SetActive(true); + fVars[i]->Start(); + } +} + +void plProfileManagerFull::IPrintGroup(hsStream* s, const char* groupName, bool printTitle) +{ + char buf[256]; + + for (int i = 0; i < fVars.size(); i++) + { + plProfileVar* var = fVars[i]; + if (hsStrEQ(var->GetGroup(), groupName)) + { + if (printTitle) + sprintf(buf, "%s:%s", var->GetGroup(), var->GetName()); + else + var->PrintAvg(buf, false); + + s->Write(strlen(buf), buf); + s->WriteByte(','); + } + } +} + +void plProfileManagerFull::LogStats(const char* ageName, const char* spawnName) +{ + fLogStats = true; + wchar* temp = hsStringToWString(ageName); + fLogAgeName = temp; + delete [] temp; + fLogSpawnName = spawnName; +} + +const wchar* plProfileManagerFull::GetProfilePath() +{ + static wchar profilePath[MAX_PATH]; + static bool initialized = false; + + if (!initialized) + { + initialized = true; + + plUnifiedTime curTime = plUnifiedTime::GetCurrentTime(plUnifiedTime::kLocal); + + PathGetUserDirectory(profilePath, arrsize(profilePath)); + PathAddFilename(profilePath, profilePath, L"Profile", arrsize(profilePath)); + plFileUtils::CreateDir(profilePath); + + wchar buff[256]; + swprintf(buff, L"%02d-%02d-%04d_%02d-%02d//", + curTime.GetMonth(), + curTime.GetDay(), + curTime.GetYear(), + curTime.GetHour(), + curTime.GetMinute()); + + PathAddFilename(profilePath, profilePath, buff, arrsize(profilePath)); + plFileUtils::CreateDir(profilePath); + } + + return profilePath; +} + +void plProfileManagerFull::ILogStats() +{ + wchar statFilename[256]; + swprintf(statFilename, L"%s%s.csv", GetProfilePath(), fLogAgeName.c_str()); + + bool exists = plFileUtils::FileExists(statFilename); + + hsUNIXStream s; + if (s.Open(statFilename, L"ab")) + { + GroupSet groups; + GetGroups(groups); + + GroupSet::iterator it; + + if (!exists) + { + const char* kSpawn = "Spawn"; + s.Write(strlen(kSpawn), kSpawn); + s.WriteByte(','); + + for (it = groups.begin(); it != groups.end(); it++) + { + const char* groupName = *it; + IPrintGroup(&s, groupName, true); + } + s.WriteByte('\r'); + s.WriteByte('\n'); + } + + s.Write(fLogSpawnName.length(), fLogSpawnName.c_str()); + s.WriteByte(','); + + for (it = groups.begin(); it != groups.end(); it++) + { + const char* groupName = *it; + IPrintGroup(&s, groupName); + } + s.WriteByte('\r'); + s.WriteByte('\n'); + + s.Close(); + } + + fLogStats = false; + fLogAgeName = L""; + fLogSpawnName = ""; +} + + +void plProfileManagerFull::ShowLaps(const char* groupName, const char* varName) +{ + plProfileVar* var = nil; + + + if(fShowLaps) + fShowLaps->SetLapsActive(false); + + for (int i = 0; i < fVars.size(); i++) + { + int j = 0; + while(fVars[i]->GetName()[j++] == ' ') {} + if (stricmp(&(fVars[i]->GetName()[j-1]), varName) == 0 && + stricmp(fVars[i]->GetGroup(), groupName) == 0) + { + var = fVars[i]; + break; + } + } + + if (var) + { + if (var == fShowLaps) + { + + fShowLaps = nil; + } + else + { + fShowLaps = var; + } + } + if(fShowLaps) + fShowLaps->SetLapsActive(true); +} + +void plProfileManagerFull::CreateGraph(const char* varName, UInt32 min, UInt32 max) +{ + // If the graph is already created, destroy it + for (int i = 0; i < fGraphs.size(); i++) + { + if (strcmp(fGraphs[i]->GetTitle(), varName) == 0) + { + plPlateManager::Instance().DestroyPlate(fGraphs[i]); + fGraphs.erase(fGraphs.begin()+i); + return; + } + } + + plProfileVar* var = IFindTimer(varName); + if (var) + { + plGraphPlate* graph = nil; + plPlateManager::Instance().CreateGraphPlate(&graph); + graph->SetSize(0.25, 0.25); + graph->SetDataRange(min, max, 100); + graph->SetTitle(var->GetName()); + + fGraphs.push_back(graph); + } +} + +void plProfileManagerFull::ResetDefaultDetailVars() +{ + fDetailVars.clear(); + AddDetailVar("ApplyAnimation",0,50); + AddDetailVar("AnimatingPhysicals",0,50); + AddDetailVar("StoppedAnimPhysicals",0,50); + AddDetailVar("DrawableTime",0,50); + AddDetailVar("Polys",0,150000); + AddDetailVar("Step",0,50); + AddDetailVar("LineOfSight",0,50); + AddDetailVar(" PhysicsUpdates",0,50); + AddDetailVar("Stream Shove Time",0,50); + AddDetailVar("RenderSetup",0,50); +} + +void plProfileManagerFull::ShowDetailGraph() +{ + // if graph is already created, kill it + if (fDetailGraph) + HideDetailGraph(); + if (fDetailVars.size() == 0) + ResetDefaultDetailVars(); + + plPlateManager::Instance().CreateGraphPlate(&fDetailGraph); + fDetailGraph->SetSize(0.9,0.9); + fDetailGraph->SetDataRange(0,500,500); + fDetailGraph->SetDataLabels(0,100); // should be relatively simple to cast everything to a 0-100 range + fDetailGraph->SetTitle("Detail"); + UpdateDetailLabels(); +} + +void plProfileManagerFull::HideDetailGraph() +{ + if (fDetailGraph) + { + plPlateManager::Instance().DestroyPlate(fDetailGraph); + fDetailGraph = nil; + } +} + +void plProfileManagerFull::AddDetailVar(const char* varName, UInt32 min, UInt32 max) +{ + int i=0; + for (i=0; iGetName(), varName) == 0) + return; // don't add it again + } + + plProfileVar* var = IFindTimer(varName); + if (!var) + return; + var->SetActive(true); + + if (fDetailVars.size() == 10) + fDetailVars.erase(fDetailVars.begin()); // we don't want any more then 10 at this point, so drop the oldest one + detailVar temp; + temp.var = var; + temp.min = min; + temp.max = max; + fDetailVars.push_back(temp); + UpdateDetailLabels(); +} + +void plProfileManagerFull::RemoveDetailVar(const char* varName) +{ + int i=0; + for (i=0; iGetName(), varName) == 0) + { + fDetailVars.erase(fDetailVars.begin()+i); + } + } + UpdateDetailLabels(); +} + +void plProfileManagerFull::UpdateDetailLabels() +{ + if (fDetailGraph) + { + int i; + std::vector labels; + for (i=0; iGetName()); + + fDetailGraph->SetLabelText(labels); + + // update the colors as well, just in case + std::vector colors; + colors.push_back(0xff00ff00); // green + colors.push_back(0xff0000ff); // blue + colors.push_back(0xffffff00); // yellow + colors.push_back(0xffff00ff); // pink + colors.push_back(0xffffffff); // white + colors.push_back(0xff00ffff); // cyan + colors.push_back(0xffff8000); // orange + colors.push_back(0xff8000ff); // purple + colors.push_back(0xffff0080); // fuscha + colors.push_back(0xff808080); // grey + + fDetailGraph->SetDataColors(colors); + } +} + +void plProfileManagerFull::ResetMax() +{ + for (int i = 0; i < fVars.size(); i++) + fVars[i]->ResetMax(); +} + +void plProfileManagerFull::ISetActive(const char* groupName, bool active) +{ + for (int i = 0; i < fVars.size(); i++) + { + if (stricmp(fVars[i]->GetGroup(), groupName) == 0) + { + fVars[i]->SetActive(active); + } + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plProfileManagerFull.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plProfileManagerFull.h new file mode 100644 index 00000000..113ecec8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plProfileManagerFull.h @@ -0,0 +1,115 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plProfileManagerFull_h_inc +#define plProfileManagerFull_h_inc + +#include "plProfileManager.h" + +#include "hsStlUtils.h" + +#include "hsStlSortUtils.h" + +class plProfileManager; +class plGraphPlate; +class plStatusLog; +class hsStream; +class plProfileVar; + +class plProfileManagerFull +{ +public: + typedef std::set GroupSet; + +protected: + plProfileManager::VarVec& fVars; + + bool fLogStats; // If true, log the stats at the end of the frame + std::wstring fLogAgeName; + std::string fLogSpawnName; + + std::vector fGraphs; + plGraphPlate* fDetailGraph; + + struct detailVar + { + plProfileVar* var; + Int32 min; + Int32 max; + }; + + std::vector fDetailVars; // the vars we want to show on the detail graph + + GroupSet fShowGroups; + plProfileVar* fShowLaps; + UInt32 fMinLap; // For Display + + void IPrintGroup(hsStream* s, const char* groupName, bool printTitle=false); + void ILogStats(); + + plProfileVar* IFindTimer(const char* name); + + void ISetActive(const char* groupName, bool active); + + plProfileManagerFull(); + +public: + static plProfileManagerFull& Instance(); + + void EndFrame(); // Call end frame on our special timers + void Update(); + + void GetGroups(GroupSet& groups); + void ShowGroup(const char* groupName); + void ShowNextGroup(); + + struct LapPair { const char* group; const char* varName; }; + typedef std::vector LapNames; + void GetLaps(LapNames& lapNames); + void ShowLaps(const char* groupName, const char* varName); + void SetMinLap(int m) { fMinLap = m; }; + void PageDownLaps() { fMinLap += 40; } + void PageUpLaps() { fMinLap = (fMinLap < 40) ? 0 : fMinLap - 40;} + + void CreateGraph(const char* varName, UInt32 min, UInt32 max); + + void ResetDefaultDetailVars(); + void ShowDetailGraph(); + void HideDetailGraph(); + void AddDetailVar(const char* varName, UInt32 min, UInt32 max); + void RemoveDetailVar(const char* varName); + void UpdateDetailLabels(); + + void ResetMax(); + + void LogStats(const char* ageName, const char* spawnName); + const wchar* GetProfilePath(); + + // If you're going to call LogStats, make sure to call this first so all stats will be evaluated before logging + void ActivateAllStats(); + +}; + +#endif // plProfileManagerFull_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plStatGatherCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plStatGatherCreatable.h new file mode 100644 index 00000000..d7f98b23 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatGather/plStatGatherCreatable.h @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plStatGatherCreatable_h_inc +#define plStatGatherCreatable_h_inc + +#include "../pnFactory/plCreator.h" + +#include "plAutoProfile.h" +REGISTER_NONCREATABLE(plAutoProfile); + +#endif // plStatGatherCreatable_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plEncryptLogLine.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plEncryptLogLine.cpp new file mode 100644 index 00000000..337a2cf9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plEncryptLogLine.cpp @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plEncryptLogLine Header // +// // +//// Description ///////////////////////////////////////////////////////////// +// // +// Broken into a separate file for easy include in utility apps // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "plEncryptLogLine.h" + +#include + +void plStatusEncrypt::Encrypt( UInt8 *line, UInt8 hint ) +{ + // Current encryption scheme: rotate all characters right by 2 bits, + // then rotate the whole damn line by 3 bits to the right + UInt32 i, len = strlen( (char *)line ); + UInt8 newHi, hiBits = ( ( line[ len - 1 ] ) << ( 5 - 2 ) ) & 0xe0; + + for( i = 0; i < len; i++ ) + { + // So each character will be the src char rotated right 2 bits, then shifted + // right 3 bits, or'ed with the last high bits, and the 3 discarded bits + // become the new high bits + // Too bad C doesn't have a bit-rotate op + + line[ i ] = ( line[ i ] << 6 ) | ( line[ i ] >> 2 ); + newHi = line[ i ] << 5; + line[ i ] = ( line[ i ] >> 3 ) | hiBits; + line[ i ] ^= hint; // Should wrap around + + hiBits = newHi; + } +} + +void plStatusEncrypt::Decrypt( UInt8 *line, Int32 len, UInt8 hint ) +{ + // Da reverse, of course! + Int32 i; + UInt8 lastChar = 0, newLo, loBits = ( line[ 0 ] ^ hint ) >> 5; + + for( i = len - 1; i >= 0; i-- ) + { + lastChar = line[ i ]; + line[ i ] ^= hint; + newLo = line[ i ] >> 5; + line[ i ] = ( line[ i ] << 3 ) | loBits; + line[ i ] = ( line[ i ] >> 6 ) | ( line[ i ] << 2 ); + + loBits = newLo; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plEncryptLogLine.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plEncryptLogLine.h new file mode 100644 index 00000000..91b5eb0d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plEncryptLogLine.h @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plEncryptLogLine Header // +// // +//// Description ///////////////////////////////////////////////////////////// +// // +// Broken into a separate file for easy include in utility apps // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plEncryptLogLine_h +#define _plEncryptLogLine_h + +#include "hsTypes.h" + +namespace plStatusEncrypt +{ + void Encrypt( UInt8 *line, UInt8 hint ); + void Decrypt( UInt8 *line, Int32 length, UInt8 hint ); +}; + +#endif //_plEncryptLogLine_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plLoggable.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plLoggable.cpp new file mode 100644 index 00000000..c4c03baf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plLoggable.cpp @@ -0,0 +1,144 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plLoggable.h" +#include "plStatusLog.h" +#include "hsStlUtils.h" +#include "hsTemplates.h" + + +plLoggable::~plLoggable() +{ + IDeleteLog(); +} + +void plLoggable::IDeleteLog() +{ + if ( fWeCreatedLog ) + delete fStatusLog; + fWeCreatedLog = false; + fStatusLog = nil; +} + +plStatusLog* plLoggable::GetLog() const +{ + // create status log if necessary + if(fStatusLog==nil) + { + ICreateStatusLog(); // Usually overridden by derived class + if ( fStatusLog ) + fWeCreatedLog = true; + } +#ifdef HS_DEBUGGING + if ( fComplainAboutMissingLog ) + { + hsAssert(fStatusLog, "nil fStatusLog. Should override ICreateStatusLog()"); + } +#endif + return fStatusLog; +} + +void plLoggable::SetLog( plStatusLog * log, bool deleteOnDestruct/*=false */) +{ + IDeleteLog(); + fStatusLog = log; + fWeCreatedLog = deleteOnDestruct; +} + + +bool plLoggable::Log( const char * str ) const +{ + if ( !str || strlen( str )==0 ) + return true; + + GetLog(); + + if ( fStatusLog ) + return fStatusLog->AddLine( str ); + + return true; +} + +bool plLoggable::LogF( const char * fmt, ... ) const +{ + va_list args; + va_start(args, fmt); + return Log( xtl::formatv( fmt, args ).c_str() ); +} + +bool plLoggable::LogV( const char * fmt, va_list args ) const +{ + return Log( xtl::formatv( fmt, args ).c_str() ); +} + +bool plLoggable::DebugMsgV(const char* fmt, va_list args) const +{ + return LogF("DBG: %s", xtl::formatv(fmt,args).c_str()); +} + +bool plLoggable::ErrorMsgV(const char* fmt, va_list args) const +{ + return LogF("ERR: %s", xtl::formatv(fmt,args).c_str()); +} + +bool plLoggable::WarningMsgV(const char* fmt, va_list args) const +{ + return LogF("WRN: %s", xtl::formatv(fmt,args).c_str()); +} + +bool plLoggable::AppMsgV(const char* fmt, va_list args) const +{ + return LogF("APP: %s", xtl::formatv(fmt,args).c_str()); +} + +/////////////////////////////////////////////////////////////// + +bool plLoggable::DebugMsg(const char* fmt, ...) const +{ + va_list args; + va_start(args, fmt); + return DebugMsgV(fmt, args); +} + +bool plLoggable::ErrorMsg(const char* fmt, ...) const +{ + va_list args; + va_start(args, fmt); + return ErrorMsgV(fmt, args); +} + +bool plLoggable::WarningMsg(const char* fmt, ...) const +{ + va_list args; + va_start(args, fmt); + return WarningMsgV(fmt, args); +} + +bool plLoggable::AppMsg(const char* fmt, ...) const +{ + va_list args; + va_start(args, fmt); + return AppMsgV(fmt, args); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plLoggable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plLoggable.h new file mode 100644 index 00000000..21b99732 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plLoggable.h @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plLoggable_inc +#define plLoggable_inc + +#include +#include "hsTypes.h" + +// +// An abstract base class which contains a status log and simple functions +// for writing Debug and Error msgs to the log +// +class plStatusLog; +class plLoggable +{ +protected: + mutable plStatusLog* fStatusLog; + mutable bool fWeCreatedLog; + mutable bool fComplainAboutMissingLog; + + virtual void ICreateStatusLog() const { }; // call plStatusLogMgr::CreateStatusLog with the options you want + void IDeleteLog(); + +public: + plLoggable() : fStatusLog(nil), fWeCreatedLog(false),fComplainAboutMissingLog(true) { } + virtual ~plLoggable(); + + plStatusLog* GetLog() const; + void SetLog( plStatusLog * log, bool deleteOnDestruct=false ); + + // logging + + virtual bool Log( const char * str ) const; + virtual bool LogF( const char * fmt, ... ) const; + virtual bool LogV( const char * fmt, va_list args ) const; + virtual bool ErrorMsgV(const char* fmt, va_list args) const ; + virtual bool DebugMsgV(const char* fmt, va_list args) const ; + virtual bool WarningMsgV(const char* fmt, va_list args) const ; + virtual bool AppMsgV(const char* fmt, va_list args) const ; + virtual bool ErrorMsg(const char* fmt, ...) const ; + virtual bool DebugMsg(const char* fmt, ...) const ; + virtual bool WarningMsg(const char* fmt, ...) const ; + virtual bool AppMsg(const char* fmt, ...) const ; +}; + +#endif // plLoggable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp new file mode 100644 index 00000000..5ed3bdc3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.cpp @@ -0,0 +1,906 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plStatusLog Functions // +// // +//// History ///////////////////////////////////////////////////////////////// +// // +// 10.02.2001 mcn - Created // +// 10.16.2001 mcn - Added static versions of AddLine() so you don't need // +// to explicitly create a log // +// 10.17.2001 mcn - Added support for carriage returns in printed lines // +// 10.24.2002 eap - Added kDebugOutput flag for writing to debug window // +// 10.25.2002 eap - Updated to work under unix // +// 12.13.2002 eap - Added kStdout flag // +// // +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "hsThread.h" +#include "hsTemplates.h" +#include "hsTimer.h" +#include "hsStlUtils.h" +#include "../plFile/plFileUtils.h" +#include "plStatusLog.h" +#include "plPipeline.h" +#include "../plPipeline/plDebugText.h" +#include "hsStlUtils.h" +#include "../plFile/hsFiles.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "../pnNetCommon/plNetApp.h" +#include "../pnUtils/pnUtils.h" +#include "../pnProduct/pnProduct.h" + +#include "plEncryptLogLine.h" + +#if HS_BUILD_FOR_UNIX + #include + #define MAX_PATH PATH_MAX +#endif + +////////////////////////////////////////////////////////////////////////////// +//// plStatusLogMgr Stuff //////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +wchar plStatusLogMgr::fBasePath[ MAX_PATH ] = L""; + +//// Constructor & Destructor //////////////////////////////////////////////// + +plStatusLogMgr::plStatusLogMgr() +{ + fDisplays = nil; + fCurrDisplay = nil; + fDrawer = nil; + fLastLogChangeTime = 0; + + PathGetLogDirectory(fBasePath, arrsize(fBasePath)); +} + +plStatusLogMgr::~plStatusLogMgr() +{ + // Unlink all the displays, but don't delete them; leave that to whomever owns them + while( fDisplays != nil ) + { + plStatusLog *log = fDisplays; + + fDisplays->IUnlink(); + + if( log->fFlags & plStatusLog::kDeleteForMe ) + DEL(log); + } +} + +plStatusLogMgr &plStatusLogMgr::GetInstance( void ) +{ + static plStatusLogMgr theManager; + return theManager; +} + +//// IEnsurePathExists /////////////////////////////////////////////////////// + +void plStatusLogMgr::IEnsurePathExists( const wchar *dirName ) +{ + // Note: this creates the directory if it doesn't exist, or if it does, + // returns false + plFileUtils::CreateDir( dirName ); +} + +//// IPathAppend ///////////////////////////////////////////////////////////// + +void plStatusLogMgr::IPathAppend( wchar *base, const wchar *extra, unsigned maxLen ) +{ + if (!base || !extra) + return; + + unsigned baseLen = wcslen(base); + unsigned extraLen = wcslen(extra); + + bool needsSeparator = false; + if (baseLen >= 1) + needsSeparator = (base[baseLen - 1] != WPATH_SEPARATOR); + + if (needsSeparator) + { + if ((baseLen + 1 + 1) >= maxLen) + return; // abort, buffer isn't big enough + base[baseLen] = WPATH_SEPARATOR; + ++baseLen; + base[baseLen] = '\0'; + } + + // concat the strings, making sure not to overrun the buffer + unsigned curExtraPos = 0; + for (unsigned curBasePos = baseLen; curBasePos < maxLen; ++curBasePos) + { + base[curBasePos] = extra[curExtraPos]; + if (extra[curExtraPos] == '\0') + break; // done + ++curExtraPos; + } + + // ensure we are null-terminated + base[maxLen - 1] = '\0'; +} + +//// Draw //////////////////////////////////////////////////////////////////// + +void plStatusLogMgr::Draw( void ) +{ + /// Just draw current plStatusLog + if( fCurrDisplay != nil && fDrawer != nil ) + { + plStatusLog* firstLog = nil; + if (hsTimer::GetSysSeconds() - fLastLogChangeTime < 1) + firstLog = fDisplays; + + fDrawer->Draw(fCurrDisplay, firstLog); + } +} + +//// CreateStatusLog ///////////////////////////////////////////////////////// + +plStatusLog *plStatusLogMgr::CreateStatusLog( UInt8 numDisplayLines, const char *filename, UInt32 flags ) +{ + wchar* wFilename = hsStringToWString(filename); + plStatusLog* ret = CreateStatusLog(numDisplayLines, wFilename, flags); + delete [] wFilename; + return ret; +} + +plStatusLog *plStatusLogMgr::CreateStatusLog( UInt8 numDisplayLines, const wchar *filename, UInt32 flags ) +{ + IEnsurePathExists( fBasePath ); + plStatusLog *log = NEW(plStatusLog)( numDisplayLines, filename, flags ); + + // Put the new log in its alphabetical position + plStatusLog** nextLog = &fDisplays; + while (*nextLog) + { + if (wcsicmp(filename, (*nextLog)->GetFileNameW()) <= 0) + break; + nextLog = &(*nextLog)->fNext; + } + log->ILink(nextLog); + + log->fDisplayPointer = &fCurrDisplay; + + return log; +} + +//// ToggleStatusLog ///////////////////////////////////////////////////////// + +void plStatusLogMgr::ToggleStatusLog( plStatusLog *logToDisplay ) +{ + if( fCurrDisplay == logToDisplay ) + fCurrDisplay = nil; + else + fCurrDisplay = logToDisplay; + + fLastLogChangeTime = hsTimer::GetSysSeconds(); +} + +//// SetCurrStatusLog //////////////////////////////////////////////////////// + +void plStatusLogMgr::SetCurrStatusLog(const char* logName) +{ + wchar* wLogName = hsStringToWString(logName); + SetCurrStatusLog(wLogName); + delete [] wLogName; +} + +void plStatusLogMgr::SetCurrStatusLog(const wchar* logName) +{ + plStatusLog* log = FindLog(logName, false); + if (log != nil) + fCurrDisplay = log; +} + +//// NextStatusLog /////////////////////////////////////////////////////////// + +void plStatusLogMgr::NextStatusLog( void ) +{ + if( fCurrDisplay == nil ) + fCurrDisplay = fDisplays; + else + fCurrDisplay = fCurrDisplay->fNext; + + fLastLogChangeTime = hsTimer::GetSysSeconds(); +} + +void plStatusLogMgr::PrevStatusLog( void ) +{ + if( fCurrDisplay == nil ) + { + fCurrDisplay = fDisplays; + while (fCurrDisplay && fCurrDisplay->fNext) + fCurrDisplay = fCurrDisplay->fNext; + } + else + { + plStatusLog* lastLog = fDisplays; + while (lastLog && lastLog->fNext != fCurrDisplay) + lastLog = lastLog->fNext; + + fCurrDisplay = lastLog; + } + + fLastLogChangeTime = hsTimer::GetSysSeconds(); +} + +//// FindLog //////////////////////////////////////////////////////////////// + +plStatusLog *plStatusLogMgr::FindLog( const char *filename, hsBool createIfNotFound ) +{ + wchar* wFilename = hsStringToWString(filename); + plStatusLog* ret = FindLog(wFilename, createIfNotFound); + delete [] wFilename; + return ret; +} + +plStatusLog *plStatusLogMgr::FindLog( const wchar *filename, hsBool createIfNotFound ) +{ + plStatusLog *log = fDisplays; + + while( log != nil ) + { + if( wcsicmp( log->GetFileNameW(), filename ) == 0 ) + return log; + + log = log->fNext; + } + + if( !createIfNotFound ) + return nil; + + // Didn't find one, so create one! (make it a nice default one :) + log = CreateStatusLog( kDefaultNumLines, filename, plStatusLog::kFilledBackground | + plStatusLog::kDeleteForMe ); + + return log; +} + +//// SetBasePath //////////////////////////////////////////////////////////////// + +void plStatusLogMgr::SetBasePath( const char * path ) +{ + wchar* wPath = hsStringToWString(path); + SetBasePath(wPath); + delete [] wPath; +} + +void plStatusLogMgr::SetBasePath( const wchar * path ) +{ + wcscpy( fBasePath, path ); +} + + +//// BounceLogs /////////////////////////////////////////////////////////////// + +void plStatusLogMgr::BounceLogs() +{ + plStatusLog *log = fDisplays; + + while( log != nil ) + { + plStatusLog * tmp = log; + log = log->fNext; + tmp->Bounce(); + } +} + +//// DumpLogs //////////////////////////////////////////////////////////////// + +bool plStatusLogMgr::DumpLogs( const char *newFolderName ) +{ + wchar* wFolderName = hsStringToWString(newFolderName); + bool ret = DumpLogs(wFolderName); + delete [] wFolderName; + return ret; +} + +bool plStatusLogMgr::DumpLogs( const wchar *newFolderName ) +{ + bool retVal = true; // assume success + // create root path and make sure it exists + wchar temp[MAX_PATH]; + std::wstring newPath = L""; + if (fBasePath) + { + wcsncpy(temp, fBasePath, MAX_PATH); + IPathAppend(temp, newFolderName, MAX_PATH); + newPath = temp; + } + else + newPath = newFolderName; + IEnsurePathExists(newPath.c_str()); + + hsWFolderIterator folderIterator; + if (fBasePath) + folderIterator.SetPath(fBasePath); + else + folderIterator.SetPath(L"."); + + while (folderIterator.NextFile()) + { + if (folderIterator.IsDirectory()) + continue; + + std::wstring baseFilename = folderIterator.GetFileName(); + std::wstring source; + if (fBasePath) + { + wcsncpy(temp, fBasePath, MAX_PATH); + IPathAppend(temp, baseFilename.c_str(), MAX_PATH); + source = temp; + } + else + source = baseFilename; + + std::wstring destination; + wcsncpy(temp, newPath.c_str(), MAX_PATH); + IPathAppend(temp, baseFilename.c_str(), MAX_PATH); + destination = temp; + + bool succeeded = (CopyFileW(source.c_str(), destination.c_str(), FALSE) != 0); + retVal = retVal && succeeded; + } + return retVal; +} + +////////////////////////////////////////////////////////////////////////////// +//// plStatusLog //////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +#if defined(PLASMA_EXTERNAL_RELEASE) && (BUILD_TYPE == BUILD_TYPE_LIVE) +// If this is an external live build then don't write log files +UInt32 plStatusLog::fLoggingOff = true; +#else +UInt32 plStatusLog::fLoggingOff = false; +#endif + + +plStatusLog::plStatusLog( UInt8 numDisplayLines, const wchar *filename, UInt32 flags ) +{ + fFileHandle = nil; + fSize = 0; + fForceLog = false; + + fMaxNumLines = numDisplayLines; + if( filename != nil ) + { + fFilename = filename; + char* temp = hsWStringToString(filename); + fCFilename = temp; + delete [] temp; + } + else + { + fFilename = L""; + fCFilename = ""; + flags |= kDontWriteFile; + } + + fOrigFlags = fFlags = flags; + + IInit(); +} + +plStatusLog::~plStatusLog() +{ + IFini(); +} + +void plStatusLog::IInit() +{ + int i; + + fFlags = fOrigFlags; + + fLines = TRACKED_NEW char *[ fMaxNumLines ]; + fColors = TRACKED_NEW UInt32[ fMaxNumLines ]; + for( i = 0; i < fMaxNumLines; i++ ) + { + fLines[ i ] = nil; + fColors[ i ] = kWhite; + } + + fNext = nil; + fBack = nil; + +} + +bool plStatusLog::IReOpen( void ) +{ + if( fFileHandle != nil ) + { + fclose( fFileHandle ); + fFileHandle = nil; + } + + // Open the file, clearing it, if necessary + if(!(fFlags & kDontWriteFile)) + { + wchar file[ MAX_PATH ]; + wchar fileNoExt[MAX_PATH]; + wchar* ext=nil; + IParseFileName(file, fileNoExt, &ext); + fEncryptMe = false; +#ifdef PLASMA_EXTERNAL_RELEASE + fEncryptMe = ( wcsicmp( fFilename.c_str(), L"chat.log" ) != 0 ) ? true : false; + if( fEncryptMe ) + ext = L".elf"; +#endif + wchar fileToOpen[MAX_PATH]; + swprintf(fileToOpen, L"%s.0%s", fileNoExt, ext); + if (!(fFlags & kDontRotateLogs)) + { + wchar work[MAX_PATH], work2[MAX_PATH]; + swprintf(work, L"%s.3%s",fileNoExt,ext); + _wremove(work); + swprintf(work2, L"%s.2%s",fileNoExt,ext); + _wrename(work2, work); + swprintf(work, L"%s.1%s",fileNoExt,ext); + _wrename(work, work2); + _wrename(fileToOpen, work); + } + + if (fFlags & kAppendToLast) + { + fFileHandle = _wfopen( fileToOpen, fEncryptMe ? L"ab" : L"at" ); + } + else + { + fFileHandle = _wfopen( fileToOpen, fEncryptMe ? L"wb" : L"wt" ); + // if we need to reopen lets just append + fFlags |= kAppendToLast; + } + } + + if (fFileHandle) + fSize = ftell( fFileHandle ); + else + fSize = 0; + + return fFileHandle != nil; +} + +void plStatusLog::IFini( void ) +{ + int i; + + if( fFileHandle != nil ) + { + fclose( fFileHandle ); + fFileHandle = nil; + } + + if( *fDisplayPointer == this ) + *fDisplayPointer = nil; + + if( fBack != nil || fNext != nil ) + IUnlink(); + + for( i = 0; i < fMaxNumLines; i++ ) + delete [] fLines[ i ]; + + delete [] fLines; + delete [] fColors; +} + + +void plStatusLog::IParseFileName(wchar* file, wchar* fileNoExt, wchar** ext) const +{ + const wchar *base = plStatusLogMgr::IGetBasePath(); + if( wcslen( base ) != nil ) + swprintf( file, L"%s%s%s", base, WPATH_SEPARATOR_STR, fFilename.c_str() ); + else + wcscpy( file, fFilename.c_str() ); + + plFileUtils::EnsureFilePathExists( file ); + + // apache-style file backup + + *ext = wcsrchr(file, L'.'); + if (*ext) + { + int fileLen = *ext - file; + wcsncpy(fileNoExt, file, fileLen); + fileNoExt[fileLen] = L'\0'; + } + else + { + wcscpy(fileNoExt, file); + *ext = L""; + } + +} + +//// IUnlink ///////////////////////////////////////////////////////////////// + +void plStatusLog::IUnlink( void ) +{ + hsAssert( fBack, "plStatusLog not in list" ); + if( fNext ) + fNext->fBack = fBack; + *fBack = fNext; + + fBack = nil; + fNext = nil; +} + +//// ILink /////////////////////////////////////////////////////////////////// + +void plStatusLog::ILink( plStatusLog **back ) +{ + hsAssert( fNext == nil && fBack == nil, "Trying to link a plStatusLog that's already linked" ); + + fNext = *back; + if( *back ) + (*back)->fBack = &fNext; + fBack = back; + *back = this; +} + +//// IAddLine //////////////////////////////////////////////////////////////// +// Actually add a stinking line. + +bool plStatusLog::IAddLine( const char *line, Int32 count, UInt32 color ) +{ + int i; + + if(fLoggingOff && !fForceLog) + return true; + + /// Scroll pointers up + hsTempMutexLock lock( fMutex ); + + bool ret = true; + + if (fMaxNumLines > 0) + { + delete [] fLines[ 0 ]; + for( i = 0; i < fMaxNumLines - 1; i++ ) + { + fLines[ i ] = fLines[ i + 1 ]; + fColors[ i ] = fColors[ i + 1 ]; + } + } + + /// Add new + if( line == nil || strlen( line ) == 0 ) + { + if (fMaxNumLines > 0) + { + fColors[ i ] = 0; + fLines[ i ] = nil; + } + ret = IPrintLineToFile( "", 0 ); + } + else + { + if( count < 0 ) + count = strlen( line ); + + if (fMaxNumLines > 0) + { + fLines[ i ] = TRACKED_NEW char[ count + 1 ]; + hsStrncpy( fLines[ i ], line, count + 1 ); + fLines[ i ][ count ] = 0; + + char *c = strchr( fLines[ i ], '\n' ); + if( c != nil ) + { + *c = 0; + count--; + } + + fColors[ i ] = color; + } + + ret = IPrintLineToFile( line, count ); + } + + return ret; +} + +//// AddLine ///////////////////////////////////////////////////////////////// + +bool plStatusLog::AddLine( const char *line, UInt32 color ) +{ + char *c, *str; + if(fLoggingOff && !fForceLog) + return true; + + bool ret = true; + + /// Scan for carriage returns and feed each section into IAddLine() + for( str = (char *)line; ( c = strchr( str, '\n' ) ) != nil; str = c + 1 ) + { + // So if we got here, c points to a carriage return... + ret = IAddLine( str, (UInt32)c - (UInt32)str, color ); + } + + /// We might have some left over + if( strlen( str ) > 0 ) + { + ret &= IAddLine( str, -1, color ); + } + + return ret; +} + +//// AddLine printf-style Variations ///////////////////////////////////////// + +bool plStatusLog::AddLineV( const char *format, va_list arguments ) +{ + if(fLoggingOff && !fForceLog) + return true; + return AddLineV( kWhite, format, arguments ); +} + +bool plStatusLog::AddLineV( UInt32 color, const char *format, va_list arguments ) +{ + if(fLoggingOff && !fForceLog) + return true; + char buffer[2000]; + vsprintf(buffer, format, arguments); + return AddLine( buffer, color ); +} + +bool plStatusLog::AddLineF( const char *format, ... ) +{ + if(fLoggingOff && !fForceLog) + return true; + va_list arguments; + va_start( arguments, format ); + + return AddLineV( kWhite, format, arguments ); +} + +bool plStatusLog::AddLineF( UInt32 color, const char *format, ... ) +{ + if(fLoggingOff && !fForceLog) + return true; + va_list arguments; + va_start( arguments, format ); + + return AddLineV( color, format, arguments ); +} + +//// AddLine Static Variations /////////////////////////////////////////////// + +bool plStatusLog::AddLineS( const char *filename, const char *format, ... ) +{ + plStatusLog *log = plStatusLogMgr::GetInstance().FindLog( filename ); + + if(fLoggingOff && !log->fForceLog) + return true; + + va_list arguments; + va_start( arguments, format ); + + return log->AddLineV( format, arguments ); +} + +bool plStatusLog::AddLineS( const char *filename, UInt32 color, const char *format, ... ) +{ + plStatusLog *log = plStatusLogMgr::GetInstance().FindLog( filename ); + + if(fLoggingOff && !log->fForceLog) + return true; + + va_list arguments; + va_start( arguments, format ); + + return log->AddLineV( color, format, arguments ); +} + +//// Clear /////////////////////////////////////////////////////////////////// + +void plStatusLog::Clear( void ) +{ + int i; + + + for( i = 0; i < fMaxNumLines; i++ ) + { + delete [] fLines[ i ]; + fLines[ i ] = nil; + } +} + + +//// Bounce ////////////////////////////////////////////////////////////////// + +void plStatusLog::Bounce( UInt32 flags) +{ + if (flags) + fOrigFlags=flags; + Clear(); + if( fFileHandle != nil ) + { + fclose( fFileHandle ); + fFileHandle = nil; + } + AddLine( "--------- Bounced Log ---------" ); +} + +//// IPrintLineToFile //////////////////////////////////////////////////////// + +bool plStatusLog::IPrintLineToFile( const char *line, UInt32 count ) +{ + if( fFlags & kDontWriteFile ) + return true; + +#ifdef PLASMA_EXTERNAL_RELEASE + UInt8 hint = 0; + if( fFlags & kAppendToLast ) + { + hint = (UInt8)fSize; + } +#endif + + if (!fFileHandle) + IReOpen(); + + bool ret = ( fFileHandle!=nil ); + + if( fFileHandle != nil ) + { + char work[256]; + char buf[2000]; + buf[0] = 0; + + //build line to encrypt + + if( count != 0 ) + { + if ( fFlags & kTimestamp ) + { + StrPrintf(work, arrsize(work), "(%s) ", plUnifiedTime(kNow).Format("%m/%d %H:%M:%S").c_str()); + StrPack(buf, work, arrsize(buf)); + } + if ( fFlags & kTimestampGMT ) + { + StrPrintf(work, arrsize(work), "(%s) ", plUnifiedTime::GetCurrentTime().Format("%m/%d %H:%M:%S UTC").c_str()); + StrPack(buf, work, arrsize(buf)); + } + if ( fFlags & kTimeInSeconds ) + { + StrPrintf(work, arrsize(work), "(%u) ", plUnifiedTime(kNow).GetSecs()); + StrPack(buf, work, arrsize(buf)); + } + if ( fFlags & kTimeAsDouble ) + { + StrPrintf(work, arrsize(work), "(%f) ", plUnifiedTime(kNow).GetSecsDouble()); + StrPack(buf, work, arrsize(buf)); + } + if (fFlags & kRawTimeStamp) + { + StrPrintf(work, arrsize(work), "[t=%10u] ", hsTimer::GetSeconds()); + StrPack(buf, work, arrsize(buf)); + } + if (fFlags & kThreadID) + { + StrPrintf(work, arrsize(work), "[t=%u] ", hsThread::GetMyThreadId()); + StrPack(buf, work, arrsize(buf)); + } + + // find the size of the buf plus the size of the line and only pack that much + unsigned BufAndLine = StrLen(buf)+count+1; + if ( BufAndLine > arrsize(buf) ) + BufAndLine = arrsize(buf); + + StrPack(buf, line, BufAndLine ); + + if(!fEncryptMe ) + { + StrPack(buf, "\n", arrsize(buf)); + } + } + + unsigned length = StrLen(buf); + +#ifdef PLASMA_EXTERNAL_RELEASE + // Print to a separate line, since we have to encrypt it + if( fEncryptMe ) + { + // Encrypt! + plStatusEncrypt::Encrypt( (UInt8 *)buf, hint ); + + // xor the line length, then write it out, then the line, no terminating character + UInt16 encrySize = length ^ ((UInt16)fSize); + + // try the first write, if it fails reopen and try again + int err; + err = fputc( encrySize & 0xff, fFileHandle ); + if (err == EOF && IReOpen()) + { + err = fputc( encrySize & 0xff, fFileHandle ); + } + + if (err != EOF) + { + fSize++; // inc for the last putc + err = fputc( encrySize >> 8, fFileHandle ); + if (err != EOF) + fSize++; // inc for the last putc + err = fwrite( buf, 1, length, fFileHandle ); + fSize += err; + + if (!(fFlags & kNonFlushedLog)) + fflush(fFileHandle); + } + else + { + ret = false; + } + } + else +#endif + { + int err; + err = fwrite(buf,1,length,fFileHandle); + ret = ( ferror( fFileHandle )==0 ); + + if ( ret ) + { + fSize += err; + if (!(fFlags & kNonFlushedLog)) + fflush(fFileHandle); + } + } + + if ( fSize>=kMaxFileSize ) + { + plStatusLogMgr::GetInstance().BounceLogs(); + } + + } + + if ( fFlags & kDebugOutput ) + { +#if HS_BUILD_FOR_WIN32 +#ifndef PLASMA_EXTERNAL_RELEASE + std::string str; + xtl::format( str, "%.*s\n", count, line ); + OutputDebugString( str.c_str() ); +#endif +#else + fprintf( stderr, "%.*s\n", count, line ); +#endif + } + + if ( fFlags & kStdout ) + { + fprintf( stdout, "%.*s\n", count, line ); + } + + return ret; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.h new file mode 100644 index 00000000..1153176f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStatusLog/plStatusLog.h @@ -0,0 +1,261 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plStatusLog Header // +// // +//// Description ///////////////////////////////////////////////////////////// +// // +// plStatusLogs are our new encapsulated method of outputting text logs // +// for status (or other things, like, say, errors). They do more than just // +// output to the file, though; they maintain a scrolling buffer that // +// can be drawn on to the screen so you can actually view what's being // +// outputted to the log file (you can disable file writing if you wish). // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plStatusLog_h +#define _plStatusLog_h + +#include "hsTypes.h" +#include "hsThread.h" + +#include + +class plPipeline; + +//// plStatusLog Definition ////////////////////////////////////////////////// +// Currently, they all display in the same location, so only one should +// really be visible at any given time. + +class plStatusLogMgr; +class hsMutex; +class plStatusLogDrawerStub; +class plStatusLog +{ + friend class plStatusLogMgr; + friend class plStatusLogDrawerStub; + friend class plStatusLogDrawer; + + protected: + + + mutable UInt32 fFlags; // Mutable so we can change it in IPrintLineToFile() internally + UInt32 fOrigFlags; + + UInt32 fMaxNumLines; + std::string fCFilename; // used ONLY by GetFileName() + std::wstring fFilename; + char **fLines; + UInt32 *fColors; + hsMutex fMutex; // To make multithreaded-safe + FILE* fFileHandle; + UInt32 fSize; + bool fEncryptMe; + bool fForceLog; + + plStatusLog *fNext, **fBack; + + plStatusLog **fDisplayPointer; // Inside pfConsole + + void IUnlink( void ); + void ILink( plStatusLog **back ); + + bool IAddLine( const char *line, Int32 count, UInt32 color ); + bool IPrintLineToFile( const char *line, UInt32 count ); + void IParseFileName(wchar* file, wchar* fileNoExt, wchar** ext) const; + + void IInit( void ); + void IFini( void ); + bool IReOpen( void ); + + plStatusLog( UInt8 numDisplayLines, const wchar *filename, UInt32 flags ); + + public: + + static UInt32 fLoggingOff; + enum StatusFlagType + { + kFilledBackground = 0x00000001, + kAppendToLast = 0x00000002, + kDontWriteFile = 0x00000004, + kDeleteForMe = 0x00000008, // BE CAREFUL USING THIS!! + // kDeleteForMe instructs the statusLogMgr to delete + // this log itself when it destructs, which is at the + // very end of client shutdown (the manager is static). + // Because of this, it's safe to use this so long as you'll + // never reference it at the very end of shutdown (as an + // object is deleted by the resManager, for example, is + // okay because that's done in plClient::Shutdown(), not + // at the very end of app destruction). If you use this + // and your log is deleted before you, your pointer will + // NOT reflect this; it's up to you + kAlignToTop = 0x00000010, + kDebugOutput = 0x00000020, // Also write string to debug console + kTimestamp = 0x00000040, // Write a timestamp in Local time with each entry. + kStdout = 0x00000080, // Also write string to stdout + kTimeInSeconds = 0x00000100, // Write timestamp as seconds since epoch + kTimeAsDouble = 0x00000200, // Write timestamp as seconds.millis + kDontRotateLogs = 0x00000400, // Don't rename/renumber log fileName + kServerTimestamp = 0x00000800, // Timestamp each entry with the server's time + kRawTimeStamp = 0x00001000, // hsTimer::GetSeconds() + kThreadID = 0x00002000, // ID of current thread + kTimestampGMT = 0x00004000, // Write a timestamp in GMT with each entry. + kNonFlushedLog = 0x00008000, // Do not flush the log after each write + }; + + enum + { + kRed = 0xffff0000, + kGreen = 0xff00ff00, + kBlue = 0xff0000ff, + kYellow = 0xffffff00, + kWhite = 0xffffffff + }; + + enum + { + kMaxFileSize = 300000000, // 300 megs + }; + + ~plStatusLog(); + + bool AddLine( const char *line, UInt32 color = kWhite ); + + /// printf-like functions + + bool AddLineF( const char *format, ... ); + bool AddLineF( UInt32 color, const char *format, ... ); + + bool AddLineV( const char *format, va_list arguments ); + bool AddLineV( UInt32 color, const char *format, va_list arguments ); + + /// Static functions that you give a filename to and it searches for a log based on that + /// (or creates one if it isn't available) + static bool AddLineS( const char *filename, const char *format, ... ); + static bool AddLineS( const char *filename, UInt32 color, const char *format, ... ); + + void Clear( void ); + + // Clear and open a new file. + void Bounce( UInt32 flags=0 ); + + const char* GetFileName() const {return fCFilename.c_str();} + const wchar* GetFileNameW() const {return fFilename.c_str();} + + void SetForceLog(bool force) { fForceLog = force; } +}; + + +//// Manager Class Definition //////////////////////////////////////////////// + +class plStatusLogMgr +{ + friend class plStatusLog; + + private: + + plStatusLogMgr(); + static plStatusLogMgr fManager; + + protected: + + plStatusLog *fDisplays; + plStatusLog *fCurrDisplay; + + plStatusLogDrawerStub *fDrawer; + + double fLastLogChangeTime; + + static wchar fBasePath[]; + + static const wchar *IGetBasePath( void ) { return fBasePath; } + + void IEnsurePathExists( const wchar *dirName ); + void IPathAppend( wchar *base, const wchar *extra, unsigned maxLen ); + + hsMutex fMutex; // To make multithreaded-safe + + public: + + enum + { + kDefaultNumLines = 40 + }; + + ~plStatusLogMgr(); + + static plStatusLogMgr &GetInstance( void ); + + void Draw( void ); + + plStatusLog *CreateStatusLog( UInt8 numDisplayLines, const char *filename, UInt32 flags = plStatusLog::kFilledBackground ); + plStatusLog *CreateStatusLog( UInt8 numDisplayLines, const wchar *filename, UInt32 flags = plStatusLog::kFilledBackground ); + void ToggleStatusLog( plStatusLog *logToDisplay ); + void NextStatusLog( void ); + void PrevStatusLog( void ); + void SetCurrStatusLog( const char *logName ); + void SetCurrStatusLog( const wchar *logName ); + plStatusLog *FindLog( const char *filename, hsBool createIfNotFound = true ); + plStatusLog *FindLog( const wchar *filename, hsBool createIfNotFound = true ); + + void SetDrawer( plStatusLogDrawerStub *drawer ) { fDrawer = drawer; } + void SetBasePath( const char * path ); + void SetBasePath( const wchar * path ); + + void BounceLogs(); + + // Create a new folder and copy all log files into it (returns false on failure) + bool DumpLogs( const char *newFolderName ); + bool DumpLogs( const wchar *newFolderName ); +}; + +//// plStatusLogDrawerStub Class //////////////////////////////////////////// +// Sometimes, we want to be able to link to plStatusLog without having the +// pipeline-drawing functionality. So we do it this way: we define a single +// class of type plStatusLogDrawerStub. If it's allocated and we're given a +// pointer to it (by, say, the pipeline), then we use it to draw and all +// is happy. If not, we don't draw. + +class plStatusLogDrawerStub +{ + protected: + + UInt32 IGetMaxNumLines( plStatusLog *log ) const { return log->fMaxNumLines; } + char **IGetLines( plStatusLog *log ) const { return log->fLines; } + const char *IGetFilename( plStatusLog *log ) const { return log->GetFileName(); } + const wchar *IGetFilenameW( plStatusLog *log ) const { return log->GetFileNameW(); } + UInt32 *IGetColors( plStatusLog *log ) const { return log->fColors; } + UInt32 IGetFlags( plStatusLog *log ) const { return log->fFlags; } + + public: + virtual ~plStatusLogDrawerStub() {} + + virtual void Draw(plStatusLog* curLog, plStatusLog* firstLog) {} +}; + +#endif //_plStatusLog_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.cpp new file mode 100644 index 00000000..be77a841 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.cpp @@ -0,0 +1,204 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plStreamLogger.h" +#include "hsExceptions.h" + +#ifdef STREAM_LOGGER + +// +// Base Stream Logger +// + +void plStreamLogger::LogEntry(plGenericType::Types type, unsigned int size, void* value, const char* desc) +{ + if (fList) + { + plGenericVar var(desc); + var.Value().SetVar(type,size,value); + Event e(Event::kValue,size,var); + fList->push_back(e); + fEntryWaiting = false; + } +} + +void plStreamLogger::ILogEntryWaiting() +{ + fEntryWaiting = true; +} + +bool plStreamLogger::IsLogEntryWaiting() +{ + return fEntryWaiting; +} + +// +// Read-Only Logging Stream +// + +void hsReadOnlyLoggingStream::LogStringString(const char* s) +{ + if (fList) + { + plGenericVar var; + + var.SetName(s); + fList->push_back(Event(Event::kString,0,var)); + } +} + +void hsReadOnlyLoggingStream::LogSubStreamStart(const char* desc) +{ + if (fList) + { + plGenericVar var; + if (!fDescStack.empty()) + { + var.SetName(fDescStack.front().c_str()); + fDescStack.pop_front(); + } + else + var.SetName(desc); + fList->push_back(Event(Event::kSubStart,0,var)); + } +} + +void hsReadOnlyLoggingStream::LogSubStreamEnd() +{ + if (fList) + { + plGenericVar var; + fList->push_back(Event(Event::kSubEnd,0,var)); + } +} + +void hsReadOnlyLoggingStream::LogSubStreamPushDesc(const char* desc) +{ + fDescStack.push_back(std::string(desc)); +} + +void hsReadOnlyLoggingStream::Rewind() +{ + hsThrow( "can't rewind a logging stream"); +} + +void hsReadOnlyLoggingStream::FastFwd() +{ + hsThrow( "can't fast forward a logging stream"); +} + +void hsReadOnlyLoggingStream::SetPosition(UInt32 position) +{ + hsThrow( "can't set position on a logging stream"); +} + +void hsReadOnlyLoggingStream::Skip(UInt32 deltaByteCount) +{ + hsReadOnlyStream::Skip(deltaByteCount); + if (deltaByteCount > 0 && !IsLogEntryWaiting()) + { + LogEntry(plGenericType::kNone,deltaByteCount,nil,"Unknown Skip"); + } +} + +UInt32 hsReadOnlyLoggingStream::Read(UInt32 byteCount, void * buffer) +{ + UInt32 ret = hsReadOnlyStream::Read(byteCount,buffer); + if (ret > 0 && !IsLogEntryWaiting()) + { + LogEntry(plGenericType::kNone,byteCount,nil,"Unknown Read"); + } + + return ret; +} + + +void hsReadOnlyLoggingStream::LogSkip(UInt32 deltaByteCount, const char* desc) +{ + ILogEntryWaiting(); + Skip(deltaByteCount); + if (deltaByteCount > 0) + { + LogEntry(plGenericType::kNone,deltaByteCount,nil,desc); + } +} + +UInt32 hsReadOnlyLoggingStream::LogRead(UInt32 byteCount, void * buffer, const char* desc) +{ + ILogEntryWaiting(); + UInt32 ret = Read(byteCount,buffer); + if (ret > 0) + { + LogEntry(plGenericType::kNone,byteCount,nil,desc); + } + + return ret; +} +char *hsReadOnlyLoggingStream::LogReadSafeString() +{ + LogSubStreamStart("push me"); + UInt16 numChars; + LogReadSwap(&numChars,"NumChars"); + + numChars &= ~0xf000; // XXX: remove when hsStream no longer does this. + if (numChars > 0) + { + char *name = TRACKED_NEW char[numChars+1]; + ILogEntryWaiting(); + UInt32 ret = Read(numChars, name); + name[numChars] = '\0'; + if (ret > 0) + { + LogEntry(plGenericType::kString,ret,name,"Value"); + } + LogSubStreamEnd(); + return name; + } + LogSubStreamEnd(); + return nil; +} + +char *hsReadOnlyLoggingStream::LogReadSafeStringLong() +{ + LogSubStreamStart("push me"); + UInt32 numChars; + LogReadSwap(&numChars,"NumChars"); + if (numChars > 0) + { + char *name = TRACKED_NEW char[numChars+1]; + ILogEntryWaiting(); + UInt32 ret = Read(numChars, name); + name[numChars] = '\0'; + if (ret > 0) + { + LogEntry(plGenericType::kString,ret,name,"Value"); + } + LogSubStreamEnd(); + return name; + } + LogSubStreamEnd(); + return nil; +} +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.h new file mode 100644 index 00000000..2566c194 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLogger.h @@ -0,0 +1,143 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_STREAM_LOGGER +#define PL_STREAM_LOGGER + +#include "hsStream.h" +#include "hsStlUtils.h" +#include "../../NucleusLib/pnNetCommon/plGenericVar.h" + +class plStreamLogger +{ +public: + class Event + { + public: + enum Type + { + kSubStart, + kSubEnd, + kValue, + kString, + }; + private: + Type fType; + plGenericVar fVar; + unsigned int fSize; + public: + Event(Type type, unsigned int size, plGenericVar& var) : fType(type), fSize(size), fVar(var) { } + Type GetType() { return fType; } + const plGenericVar& GetVar() { return fVar; } + unsigned int GetSize() { return fSize; } + }; + + typedef std::list EventList; + typedef std::list DescStack; + +#ifdef STREAM_LOGGER +protected: + + EventList* fList; + bool fEntryWaiting; // don't log an "Unknown" b/c an entry is waiting + DescStack fDescStack; + + void ILogEntryWaiting(); + +public: + plStreamLogger() : fEntryWaiting(false), fList(nil) { } + const EventList* GetList() { return fList; } + void LogSetList(EventList* el) { fList = el; } + + void LogEntry(plGenericType::Types type, unsigned int size, void* value, const char* desc); + bool IsLogEntryWaiting(); + +#endif +}; + + +#ifndef STREAM_LOGGER +#define LogSetList(l) LogVoidFunc() +#else + +#define LOG_READ_SWAP(type, enumtype) \ + void LogReadSwap(type* value, const char* desc) \ + { ILogEntryWaiting(); ReadSwap(value); LogEntry(plGenericType::enumtype,sizeof(type),value, desc);} + +#define LOG_READ_SWAP_ARRAY(type, enumtype) \ + void LogReadSwapV(int count, type values[], const char* desc) \ + { ILogEntryWaiting(); ReadSwap(count,values); int i; for (i=0; i < count; i++) LogEntry(plGenericType::enumtype,sizeof(type),&(values[i]), desc);} + +class hsReadOnlyLoggingStream : public hsReadOnlyStream, public plStreamLogger +{ +private: + +public: + void Rewind(); + void FastFwd(); + void SetPosition(UInt32 position); + + UInt32 Read(UInt32 byteCount, void * buffer); + void Skip(UInt32 deltaByteCount); + + UInt32 LogRead(UInt32 byteCount, void * buffer, const char* desc); + char* LogReadSafeString(); + char* LogReadSafeStringLong(); + void LogSkip(UInt32 deltaByteCount, const char* desc); + void LogStringString(const char* s); + void LogSubStreamStart(const char* desc); + void LogSubStreamEnd(); + void LogSubStreamPushDesc(const char* desc); + + LOG_READ_SWAP(bool, kBool) + LOG_READ_SWAP(UInt8, kUInt) + LOG_READ_SWAP(UInt16, kUInt) + LOG_READ_SWAP(UInt32, kUInt) + LOG_READ_SWAP_ARRAY(UInt8, kUInt) + LOG_READ_SWAP_ARRAY(UInt16, kUInt) + LOG_READ_SWAP_ARRAY(UInt32, kUInt) + + LOG_READ_SWAP(Int8, kInt) + LOG_READ_SWAP(char, kChar) + LOG_READ_SWAP(Int16, kInt) + LOG_READ_SWAP(Int32, kInt) + LOG_READ_SWAP(int, kInt) + LOG_READ_SWAP_ARRAY(Int8, kInt) + LOG_READ_SWAP_ARRAY(char, kChar) + LOG_READ_SWAP_ARRAY(Int16, kInt) + LOG_READ_SWAP_ARRAY(Int32, kInt) + LOG_READ_SWAP_ARRAY(int, kInt) + + LOG_READ_SWAP(float, kFloat) + LOG_READ_SWAP(double, kDouble) + LOG_READ_SWAP_ARRAY(float, kFloat) + LOG_READ_SWAP_ARRAY(double, kDouble) + +}; + +#endif //STREAM_LOGGER + +#endif //PL_STREAM_LOGGER + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLoggerC.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLoggerC.h new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plStreamLogger/plStreamLoggerC.h @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/AssShader.zip b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/AssShader.zip new file mode 100644 index 0000000000000000000000000000000000000000..2dd774b2d6166ebe054ff926cd21bc8c3ae12fb8 GIT binary patch literal 9619 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 Gr~d=`{lR1a literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_BiasNormals.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_BiasNormals.inl new file mode 100644 index 00000000..247ebc99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_BiasNormals.inl @@ -0,0 +1,17 @@ + + +// Grab noise texture, +// modulate biased version by vtx color 0, +// add to vtx color 1 + +ps.1.1 + +tex t0; +tex t1; + +add r0.rgb, t0_bias, t1_bias; ++add r0.a, t0, t1; +//mov r0, t1_bias; +mad r0.rgb, r0, v0, v1; +//mov r0, v1; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAadd.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAadd.inl new file mode 100644 index 00000000..c70a7962 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAadd.inl @@ -0,0 +1,14 @@ + +ps.1.1 + +// Add blend color, output sum of alpha + +// Color is t0 + t1 +// Alpha is t0.a + t1.a + +tex t0; +tex t1; + +add r0.rgb, t0, t1; ++add r0.a, t0, t1; +mul r0, r0, v0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAbase.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAbase.inl new file mode 100644 index 00000000..56fd183e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAbase.inl @@ -0,0 +1,14 @@ + +ps.1.1 + +// Add blend color, output base alpha + +// Color is t0 + t1 +// Alpha is t0.a + +tex t0; +tex t1; + +add r0.rgb, t0, t1; ++mov r0.a, t0; +mul r0, r0, v0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAmult.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAmult.inl new file mode 100644 index 00000000..f9ab0db2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CaddAmult.inl @@ -0,0 +1,14 @@ + +ps.1.1 + +// Add blend color, output product of alpha + +// Color is t0 + t1 +// Alpha is t0.a * t1.a + +tex t0; +tex t1; + +add r0.rgb, t0, t1; ++mul r0.a, t0, t1; +mul r0, r0, v0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAadd.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAadd.inl new file mode 100644 index 00000000..8c7c54d4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAadd.inl @@ -0,0 +1,14 @@ + +ps.1.1 + +// Alpha blend color, output sum of alphas + +// Color is t0 * (1 - t1.a) + t1 * t1.a +// Alpha is t0.a + t1.a + +tex t0 +tex t1 + +lrp r0.rgb, t1.a, t1, t0 +add r0.a, t0, t1; +mul r0, r0, v0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAbase.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAbase.inl new file mode 100644 index 00000000..384e0769 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAbase.inl @@ -0,0 +1,14 @@ + +ps.1.1 + +// Alpha blend layers, output base alpha +// +// Color is t0 * (1 - t1.a) + t1 * t1.a +// Alpha is t0.a + +tex t0 +tex t1 + +lrp r0.rgb, t1.a, t1, t0 +mov r0.a, t0; +mul r0, r0, v0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAmult.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAmult.inl new file mode 100644 index 00000000..b00fcc0d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CalphaAmult.inl @@ -0,0 +1,14 @@ + +ps.1.1 + +// Alpha blend color, output product of alphas + +// Color is t0 * (1 - t1.a) + t1 * t1.a +// Alpha is t0.a * t1.a + +tex t0 +tex t1 + +lrp r0.rgb, t1.a, t1, t0 +mul r0.a, t0, t1; +mul r0, r0, v0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CbaseAbase.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CbaseAbase.inl new file mode 100644 index 00000000..bfc9df7c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CbaseAbase.inl @@ -0,0 +1,9 @@ + +ps.1.1 + +// Single layer, just modulate by vertex color and emit +// + +tex t0 + +mul r0, t0, v0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAadd.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAadd.inl new file mode 100644 index 00000000..09a0c0b1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAadd.inl @@ -0,0 +1,14 @@ + +ps.1.1 + +// Multiply blend color, output sum of alpha + +// Color is t0 * t1 +// Alpha is t0.a + t1.a + +tex t0; +tex t1; + +mul r0.rgb, t0, t1; ++add r0.a, t0, t1; +mul r0, r0, v0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAbase.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAbase.inl new file mode 100644 index 00000000..752e992a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAbase.inl @@ -0,0 +1,14 @@ + +ps.1.1 + +// Multiply blend color, output base alpha + +// Color is t0 * t1 +// Alpha is t0.a + +tex t0; +tex t1; + +mul r0.rgb, t0, t1; ++mov r0.a, t0; +mul r0, r0, v0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAmult.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAmult.inl new file mode 100644 index 00000000..f4ce22d1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CmultAmult.inl @@ -0,0 +1,14 @@ + +ps.1.1 + +// Multiply blend color, output product of alpha + +// Color is t0 * t1 +// Alpha is t0.a * t1.a + +tex t0; +tex t1; + +mul r0.rgb, t0, t1; ++mul r0.a, t0, t1; +mul r0, r0, v0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CompCosines.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CompCosines.inl new file mode 100644 index 00000000..e491badb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_CompCosines.inl @@ -0,0 +1,31 @@ + +// Composite the cosines together. +// Input map is cosine(pix) for each of +// the 4 waves. +// +// The constants are set up so: +// Nx = -freq * amp * dirX * cos(pix); +// Ny = -freq * amp * dirY * cos(pix); +// So c[i].x = -freq[i] * amp[i] * dirX[i] +// etc. +// All textures are: +// (r,g,b,a) = (cos(), cos(), 1, 1) +// +// So c[0].z = 1, but all other c[i].z = 0 +// Note also the c4 used for biasing back at the end. + +ps.1.1 + +tex t0; +tex t1; +tex t2; +tex t3; + +mul r0, t0_bx2, c0; +mad r0, t1_bx2, c1, r0; +mad r0, t2_bx2, c2, r0; +mad r0, t3_bx2, c3, r0; +// Now bias it back into range [0..1] for output. +mul r0, r0, c4; // c4 = (0.5, 0.5, 0.5, 1) +add r0, r0, c4; +//mov r0, c4; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_GrassShader.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_GrassShader.inl new file mode 100644 index 00000000..49457135 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_GrassShader.inl @@ -0,0 +1,6 @@ +ps.1.1 + +// Grass shader. Just does a simple tex mult + +tex t0 +mul r0, t0, v0 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_MoreCosines.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_MoreCosines.inl new file mode 100644 index 00000000..71ce7c4f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_MoreCosines.inl @@ -0,0 +1,35 @@ + + +// Composite the cosines together. +// Input map is cosine(pix) for each of +// the 4 waves. +// +// The constants are set up so: +// Nx = -freq * amp * dirX * cos(pix); +// Ny = -freq * amp * dirY * cos(pix); +// So c[i].x = -freq[i] * amp[i] * dirX[i] +// etc. +// All textures are: +// (r,g,b,a) = (cos(), cos(), 1, 1) +// +// Here all c[i].z = 0, because we're accumulating ontop +// of layers that have been primed with z = 1. +// Note also the c4 used for biasing back at the end. + +ps.1.1 + +tex t0; +tex t1; +tex t2; +tex t3; + +mul r0, t0_bx2, c0; +mad r0, t1_bx2, c1, r0; +mad r0, t2_bx2, c2, r0; +mad r0, t3_bx2, c3, r0; + +// Now bias it back into range [0..1] for output. +mul r0.rgb, r0, c4; ++mov r0.a, c4; +add r0.rgb, r0, c5; +//mov r0, c4; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_ShoreLeave6.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_ShoreLeave6.inl new file mode 100644 index 00000000..b794cbc7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_ShoreLeave6.inl @@ -0,0 +1,21 @@ + +ps.1.1 + +def c0, 1.0, 1.0, 1.0, 1.0 // Temp Hack + +tex t0; +tex t1; +tex t2; + +mov r1.a, t1; +lrp r0.rgb, r1.a, t1, t0; ++mul r0.a, 1-t1, 1-t0; +lrp r0.rgb, t2.a, t2, r0; ++mul r0.a, 1-t2, r0; +mul r0.rgb, r0, v0; ++mul r0.a, 1-r0, v0; + +//mov r0.a, c1; + +//mov r0.rgb, t2; +//+mov r0.a, 1-t2; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveDecEnv.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveDecEnv.inl new file mode 100644 index 00000000..1e8776b3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveDecEnv.inl @@ -0,0 +1,35 @@ + +// Very simular to ps_WaveFixed.inl. Only the final coloring is different. +// Even though so far they are identical. + +ps.1.1 + +//def c0, 1.0, 0.0, 0.0, 1.0 // Temp Hack + + +tex t0 // Bind texture in stage 0 to register t0. +texm3x3pad t1, t0_bx2 // First row of matrix multiply. +texm3x3pad t2, t0_bx2 // Second row of matrix multiply. +texm3x3vspec t3, t0_bx2 // Third row of matrix multiply to get a 3-vector. + // Reflect 3-vector by the eye-ray vector. + // Use reflected vector to do a texture lookup + // at stage 3. + +// t3 now has our reflected environment map value +// We've (presumably) attenuated the effect on a vertex basis +// and have our color w/ attenuated alpha in v0. So all we need +// is to multiply t3 by v0 into r0 and we're done. +mul r0.rgb, t3, v0; ++mul r0.a, t0, v0; + +// mov r0, t0; + +/* +tex t0; +texcoord t1; +texcoord t2; +texcoord t3; + +mov r0.rgb, t3; ++mov r0.a, c0; +*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveFixed.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveFixed.inl new file mode 100644 index 00000000..5eaee895 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveFixed.inl @@ -0,0 +1,77 @@ +//ps.1.1 + +// def c0, 1.0, 0.0, 0.0, 1.0 + +// mov r0, c0 + +// Short pixel shader. Use the texm3x3vspec to do a per-pixel +// reflected lookup into our environment map. +// Input: +// t0 - Normal map in tangent space. Apply _bx2 modifier to shift +// [0..255] -> [-1..1] +// t1 - UVW = tangent + eye2pos.x, map ignored. +// t2 - UVW = binormal + eye2pos.y, map ignored +// t3 - UVW = normal + eye2pos.z, map = environment cube map +// v0 - attenuating color/alpha. +// See docs on texm3x3vspec for explanation of the eye2pos wackiness. +// Output: +// r0 = reflected lookup from environment map X input v0. +// Since environment map has alpha = 255, the output of this +// shader can be used for either alpha or additive blending, +// as long as v0 is fed in appropriately. + +ps.1.1 + +def c0, 1.0, 0.0, 0.0, 1.0 // Temp Hack +/* +def c1, 0.0, 1.0, 0.0, 1.0 +def c2, 0.0, 0.0, 1.0, 1.0 +*/ + + +tex t0 // Bind texture in stage 0 to register t0. +texm3x3pad t1, t0_bx2 // First row of matrix multiply. +texm3x3pad t2, t0_bx2 // Second row of matrix multiply. +texm3x3vspec t3, t0_bx2 // Third row of matrix multiply to get a 3-vector. + // Reflect 3-vector by the eye-ray vector. + // Use reflected vector to do a texture lookup + // at stage 3. + +// t3 now has our reflected environment map value +// We've (presumably) attenuated the effect on a vertex basis +// and have our color w/ attenuated alpha in v0. So all we need +// is to multiply t3 by v0 into r0, add our base color from v1 and we're done. +mad r0.rgb, t3, v0, v1; +/* HACKAGE +//+mul r0.a, v1, v0; +HACKAGE */ +mov r0.a, v0; //HACKAGE +/* +mov r0.rgb, v0; +mov r0.a, v0; +*/ + +/* +tex t0; +texcoord t1; +texcoord t2; +texcoord t3; + +mov r0.rgb, t3; + ++mov r0.a, c0; +*/ + + + + + +/* +tex t0; +texcoord t1; +texcoord t2; +texcoord t3; + +mul r0.rgb, t0_bx2, c1; ++mov r0.a, c2; +*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveGraph.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveGraph.inl new file mode 100644 index 00000000..13cf07b5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveGraph.inl @@ -0,0 +1,30 @@ + +ps.1.1 + +// Have a couple extra textures to burn here. Only thing +// I've thought of is to have an additional texture to +// make the front of the wave solid. So it's UVW would be +// the same as the base texture, but the texture itself would +// be just a thin horizontal band of alpha. Then just add that +// alpha to the output alpha. +// +// Let's get the first cut running first. + +tex t0; +tex t1; +tex t2; + +//mul r0, v0, t0; +//mul r0, r0, t1; +//add r0.a, r0, t2; + +// 1.0 mov r0, t0; +// 1.0 mul r0, r0, t1; +mul r0, t0, t1; +// TEST add r0.a, r0, t2; // TEST +add r0, r0, t2; // TEST +mul r0, r0, v0; + +//mul r0.rgb, r0, r0.a; // TEST + +//mov r0, t1; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveGrid.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveGrid.inl new file mode 100644 index 00000000..1cc05abc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveGrid.inl @@ -0,0 +1,63 @@ +//ps.1.1 + +// def c0, 1.0, 0.0, 0.0, 1.0 + +// mov r0, c0 + +// Short pixel shader. Use the texm3x3vspec to do a per-pixel +// reflected lookup into our environment map. +// Input: +// t0 - Normal map in tangent space. Apply _bx2 modifier to shift +// [0..255] -> [-1..1] +// t1 - UVW = tangent + eye2pos.x, map ignored. +// t2 - UVW = binormal + eye2pos.y, map ignored +// t3 - UVW = normal + eye2pos.z, map = environment cube map +// v0 - attenuating color/alpha. +// See docs on texm3x3vspec for explanation of the eye2pos wackiness. +// Output: +// r0 = reflected lookup from environment map X input v0. +// Since environment map has alpha = 255, the output of this +// shader can be used for either alpha or additive blending, +// as long as v0 is fed in appropriately. + +ps.1.1 + +//def c0, 1.0, 1.0, 1.0, 1.0 // Temp Hack +//def c1, 2.0, 2.0, 2.0, 1.0 + +//texcoord t0; +//texcoord t1; +//texcoord t2; +//texcoord t3; + +tex t0 // Bind texture in stage 0 to register t0. +texm3x3pad t1, t0_bx2 // First row of matrix multiply. +texm3x3pad t2, t0_bx2 // Second row of matrix multiply. +texm3x3vspec t3, t0_bx2 // Third row of matrix multiply to get a 3-vector. + // Reflect 3-vector by the eye-ray vector. + // Use reflected vector to do a texture lookup + // at stage 3. + +// t3 now has our reflected environment map value +// We've (presumably) attenuated the effect on a vertex basis +// and have our color w/ attenuated alpha in v0. So all we need +// is to multiply t3 by v0 into r0 and we're done. +mad r0.rgb, t3, v1, v0; +//add r0.rgb, t3, v0; ++mov r0.a, v1; + +//mov r0.rgb, v1.a; // HACKAGE +//mov r0.a, v1.a; // HACKAGE +//mov r0, v1; // HACKAGE + +//mov r0, c0 + +//mul r0, r0, t0; + +//mov r0, v1; +//mov r0, t3; + +//mov r0.rgb, t3; +//+mov r0.a, c0; + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveRip.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveRip.inl new file mode 100644 index 00000000..40ad05e9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/ps_WaveRip.inl @@ -0,0 +1,21 @@ + +ps.1.1 + +//def c0, 1.0, 0.0, 0.0, 1.0 // Temp Hack + +// Want +// Color: vert.rgb * t0.rgb +// Alpha: vert.a * t0.a * t1.a + +tex t0; +//tex t1; + +//mul r0.rgb, v0, t0; +//+mul r0.a, v0.a, t0.a; +//mul r0.a, r0.a, t1.a; + +//mul r0, t0, t1; + +mul r0, t0, v0; + +//mov r0, t0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_BiasNormals.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_BiasNormals.inl new file mode 100644 index 00000000..237edea2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_BiasNormals.inl @@ -0,0 +1,34 @@ + + +vs.1.1 + +dcl_position v0 +dcl_texcoord0 v7 + +// Take in a screen space position, +// transform the UVW, +// and spit it out. +// c0 = uvXform0[0] +// c1 = uvXform0[1] +// c2 = uvXform1[0] +// c3 = uvXform1[1] +// c4 = (0,0.5,1.0,2.0) +// c5 = (noiseScale, bias, 0, 1) + +mov oPos, v0; + +mov r0.zw, c4.xxxz; // yzw will stay constant (0,0,1); + +dp4 r0.x, v7, c0; +dp4 r0.y, v7, c1; + +mov oT0, r0; + +dp4 r0.x, v7, c2; +dp4 r0.y, v7, c3; + +mov oT1, r0; + +mov oD0, c5.xxzz; +mov oD1, c5.yyzz; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_CompCosines.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_CompCosines.inl new file mode 100644 index 00000000..91246648 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_CompCosines.inl @@ -0,0 +1,31 @@ +vs.1.1 + +dcl_position v0 +dcl_texcoord0 v7 + +// Take in a screen space position, +// transform the UVW, +// and spit it out. +// c4 = (0,0.5,1.0,2.0) + +//mov r0, v0; +//mov r0.w, c4.zzzz; +//mov oPos, r0; +mov oPos, v0; + +dp4 r0.x, v7, c0; +mov r0.yzw, c4.xxxz; // yzw will stay constant (0,0,1); + +mov oT0, r0; + +dp4 r0.x, v7, c1; + +mov oT1, r0; + +dp4 r0.x, v7, c2; + +mov oT2, r0; + +dp4 r0.x, v7, c3; + +mov oT3, r0; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_GrassShader.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_GrassShader.inl new file mode 100644 index 00000000..073f5738 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_GrassShader.inl @@ -0,0 +1,60 @@ +vs.1.1 + +// Grass shader. Moves verts according sine waves seeded by position +// Based on the article "Animated Grass with Pixel and Vertex Shaders" +// by John Isidoro and Drew Card, in the book +// "Direct3D ShaderX Vertex and Pixel Shader Tips and Tricks" + +// c0 = Local2NDC +// c4 = (0.0, 0.5, 1.0, 2.0) +// c5 = (time, X, X, X) +// c6 = Pi constants +// c7 = Sin constants (-1/3!, 1/!5, -1/7!, 1/9!) +// c8 = waveDistortX +// c9 = waveDistortY +// c10 = waveDistortZ +// c11 = waveDirX (0.25, 0.0, -0.7, -0.8) +// c12 = waveDirY (0.0, 0.15, -0.7, 0.1) +// c13 = waveSpeed (0.2, 0.15, 0.4, 0.4) + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 + +mul r0, c11, v0.x // pos X,Y input to waves +mad r0, c12, v0.y, r0 + +mov r1, c5.x // time +mad r0, r1, c13, r0 // scale by speed and add to X,Y input +frc r0.xy, r0 +frc r1.xy, r0.zwzw +mov r0.zw, r1.xyxy + +sub r0, r0, c4.y // - 0.5 +mul r1, r0, c6.w // *= 2 pi + +mul r2, r1, r1 // ^2 +mul r3, r2, r1 // ^3 +mul r5, r3, r2 // ^5 +mul r7, r5, r2 // ^7 +mul r9, r7, r2 // ^9 + +mad r0, r3, c7.x, r1 // - r1^3 / 3! +mad r0, r5, c7.y, r0 // + r1^5 / 5! +mad r0, r7, c7.z, r0 // - r1^7 / 7! +mad r0, r9, c7.w, r0 // + r1^9 / 9! + +dp4 r3.x, r0, c8 +dp4 r3.y, r0, c9 +dp4 r3.zw, r0, c10 + +sub r4, c4.z, v7.y +mul r3, r3, r4 // mult by Y tex coord. So the waves only affect the top verts +mov r2.w, v0 // +add r2.xyz, r3, v0 // add offset to position + +m4x4 oPos, r2, c0 // trans to NDC + +mov oFog, c4.z // no fog +mov oD0, v5 +mov oT0, v7 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_ShoreLeave6.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_ShoreLeave6.inl new file mode 100644 index 00000000..59fef8e0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_ShoreLeave6.inl @@ -0,0 +1,245 @@ +vs.1.1 + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 + +// Store our input position in world space in r6 +m4x3 r6, v0, c25; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c16.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = reflection strength (transparency) +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c30 = waterlevel + offset +// c31 = (maxAtten - minAtten) / depthFalloff +// c32 = minAtten. +// And in particular: +// c30.w = waterlevel +// c31.w = 1.f; +// c32.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c8, r6.xxxx; +mad r0, c9, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c5; +add r0, r0, c6; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c15.wwww; +add r0, r0, c15.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c15.wwww; +// dist += -kPi; +sub r0, r0, c15.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c14.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c13.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c14.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c13.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c14.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c13.wwww, r2; +mad r1, r4, c14.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c30, r6.zzzz; +mul r4, r4, c31; +add r4, r4, c32; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c16.zzzz; +max r4.xyz, r4, c16.xxxx; + +// Calc our filter (see above). +mul r11, v5.wwww, c29; +max r11, r11, c16.xxxx; +min r11, r11, c16.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c7; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c16.zzzz; + +// Smooth the approach to the shore. +sub r10.x, r6.z, c30.w; // r10.x = height +mul r10.x, r10.x, r10.x; // r10.x = h^2 +mul r10.x, r10.x, c10.x; // r10.x = -h^2 * k1 / k2^2 +add r10.x, r10.x, c10.y; // r10.x = k1 + -h^2 * k1 / k2^2 +max r10.x, r10.x, c16.xxxx; // Clamp to >= zero +add r8.x, r8.x, r10.x; // r8.x += del + +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c30.w; +max r6.z, r6.z, r8.z; +add r6.z, r6.z, c12.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= kFreq.xyzw; +mul r1, r1, c5; +// cosDist *= kAmplitude.xyzw; // Combine? +mul r1, r1, c7; +// cosDist *= filter; +mul r1, r1, r11; +// +// accumCos = (0, 0, 0, 0); +mov r7, c16.xxxx; +// temp = dp4( cosDist, toCenter_X ); +// accumCos.x += temp.xxxx; (but accumCos = (0,0,0,0) +dp4 r7.x, r1, -c8 +// +// temp = dp4( cosDist, toCenter_Y ); +// accumCos.y += temp.xxxx; +dp4 r7.y, r1, -c9 +// +// } +// +// accumBin = (1, 0, -accumCos.x); +// accumTan = (0, 1, -accumCos.y); +// accumNorm = (accumCos.x, accumCos.y, 1); +mov r11, c16.xxzx; +add r11, r11, r7; +dp3 r10.x, r11, r11; +rsq r10.x, r10.x; +mul r11, r11, r10.xxxx; + +// +// Add in our scrunch (offset in X/Y plane). +// Scale down our scrunch amount by the wave scaling +mul r10.x, c12.y, r4.z; +mad r6.xy, r11.xy, r10.xx, r6.xy; + +// mul r6.z, r6.z, r10.xxxx; DEBUG + +// mad r6, r11, c12.yyzz, r6; + +// accumNorm = mul (accumNorm, kScrunchScale ); // kScrunchScale = (scrunchScale, scrunchScale, 1, 1); +// accumCos *= (scrunchScale, scrunchScale, 0, 0); + +//##mul r2.x, r6.z, c12.x; +//##add r2.x, r2.x, c16.z; + +//##mul r7.xy, r7.xy, r2.xx; + +// This is actually wrong, but useful right now for visualizing the generated coords. +// See below for correct version. + +//##sub r3, c16.xxzx, r7.xyzz; + +// Normalize? + + +// Now rotate our normal vector into the wind +//##dp3 r0.x, r3, c18.xyww; +//##dp3 r0.y, r3, c18.zxww; +//##mov r3.xy, r0; + +// Initialize r0.w +mov r0.w, c16.zzzz; + +//##dp3 r0.x, r3, r3; +//##rsq r0.x, r0.x; +//##mul r3, r3, r0.xxxw; + + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c25; // HACKAGE +//mov r6.w, c16.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c11.x; +mul oFog, r10.x, c11.y; +mov oPos, r9; + + +// Color +mul oD0, c4, v5.xxxx; + +// UVW0 +// This layer just stays put. The motion's in the texture +// U = transformed U +// V = transformed V +dp4 r0.x, v7, c19; +dp4 r0.y, v7, c20; +//mul r0.y, r0.y, -c16.z; +//add r0.y, r0.y, c16.z; +//add r0.y, r0.y, c16.z; +//add r0.y, r0.y, c16.y; +mov oT0, r0.xyww; +mov oT1, r0.xyww; +mov oT2, r0.xyww; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_ShoreLeave7.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_ShoreLeave7.inl new file mode 100644 index 00000000..3cdb8ec7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_ShoreLeave7.inl @@ -0,0 +1,203 @@ + +vs.1.1 + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 + + +// Store our input position in world space in r6 +m4x3 r6, v0, c25; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c16.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = reflection strength (transparency) +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c30 = waterlevel + offset +// c31 = (maxAtten - minAtten) / depthFalloff +// c32 = minAtten. +// And in particular: +// c30.w = waterlevel +// c31.w = 1.f; +// c32.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c8, r6.xxxx; +mad r0, c9, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c5; +add r0, r0, c6; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c15.wwww; +add r0, r0, c15.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c15.wwww; +// dist += -kPi; +sub r0, r0, c15.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c14.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c13.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c14.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c13.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c14.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c13.wwww, r2; +mad r1, r4, c14.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c30, r6.zzzz; +mul r4, r4, c31; +add r4, r4, c32; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c16.zzzz; +max r4.xyz, r4, c16.xxxx; + +// Calc our filter (see above). +mul r11, v5.wwww, c29; +max r11, r11, c16.xxxx; +min r11, r11, c16.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c7; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c16.zzzz; + +// Smooth the approach to the shore. +/* +sub r10.x, r6.z, c30.w; // r10.x = height +mul r10.x, r10.x, r10.x; // r10.x = h^2 +mul r10.x, r10.x, c10.x; // r10.x = -h^2 * k1 / k2^2 +add r10.x, r10.x, c10.y; // r10.x = k1 + -h^2 * k1 / k2^2 +max r10.x, r10.x, c16.xxxx; // Clamp to >= zero +add r8.x, r8.x, r10.x; // r8.x += del +*/ + +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c30.w; +max r6.z, r6.z, r8.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= filter; +mul r1, r1, r11; + +// Pos = (in.x + S, in.y + R, r6.z) +// S = sum(k Dir.x A cos()) +// R = sum(k Dir.y A cos()) +// c17 = k Dir.x A +// c18 = k Dir.y A +// S = sum(cosDist * c17); +dp4 r7.x, r1, c17; +dp4 r7.y, r1, c18; + +add r6.xy, r6.xy, r7.xy; + +// Initialize r0.w +mov r0.w, c16.zzzz; + +//##dp3 r0.x, r3, r3; +//##rsq r0.x, r0.x; +//##mul r3, r3, r0.xxxw; + + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c25; // HACKAGE +//mov r6.w, c16.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c11.x; +mul oFog, r10.x, c11.y; +mov oPos, r9; + + +// Color +mul oD0, c4, v5.xxxx; + +// UVW0 +// This layer just stays put. The motion's in the texture +// U = transformed U +// V = transformed V +dp4 r0.x, v7, c19; +dp4 r0.y, v7, c20; +//mul r0.y, r0.y, -c16.z; +//add r0.y, r0.y, c16.z; +//add r0.y, r0.y, c16.z; +//add r0.y, r0.y, c16.y; +mov oT0, r0.xyww; +mov oT1, r0.xyww; +mov oT2, r0.xyww; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec1Lay.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec1Lay.inl new file mode 100644 index 00000000..bcc319ba --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec1Lay.inl @@ -0,0 +1,207 @@ +vs.1.1 + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 + +// Store our input position in world space in r6 +m4x3 r6, v0, c18; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c13.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = illumination +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c22 = waterlevel + offset +// c23 = (maxAtten - minAtten) / depthFalloff +// c24 = minAtten. +// And in particular: +// c22.w = waterlevel +// c23.w = 1.f; +// c24.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c7, r6.xxxx; +mad r0, c8, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c4; +add r0, r0, c5; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c12.wwww; +add r0, r0, c12.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c12.wwww; +// dist += -kPi; +sub r0, r0, c12.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c11.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c10.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c11.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c10.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c11.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c10.wwww, r2; +mad r1, r4, c11.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c22, r6.zzzz; +mul r4, r4, c23; +add r4, r4, c24; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c13.zzzz; +max r4.xyz, r4, c13.xxxx; +//mov r4.xyz, c13.xxx; // HACKTEST + +// Calc our filter (see above). +mul r11, v5.wwww, c21; +max r11, r11, c13.xxxx; +min r11, r11, c13.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c6; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c13.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c22.w; +max r6.z, r6.z, r8.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= kFreq.xyzw; +mul r1, r1, c4; +// cosDist *= kAmplitude.xyzw; // Combine? +mul r1, r1, c6; +// cosDist *= filter; +mul r1, r1, r11; +// +// accumCos = (0, 0, 0, 0); +mov r7, c13.xxxx; +// temp = dp4( cosDist, toCenter_X ); +// accumCos.x += temp.xxxx; (but accumCos = (0,0,0,0) +dp4 r7.x, r1, -c7 +// +// temp = dp4( cosDist, toCenter_Y ); +// accumCos.y += temp.xxxx; +dp4 r7.y, r1, -c8 +// +// } +// +// accumBin = (1, 0, -accumCos.x); +// accumTan = (0, 1, -accumCos.y); +// accumNorm = (accumCos.x, accumCos.y, 1); +mov r11, c13.xxzx; +add r11, r11, r7; +dp3 r10.x, r11, r11; +rsq r10.x, r10.x; +mul r11, r11, r10.xxxx; + +// +// Add in our scrunch (offset in X/Y plane). +// Scale down our scrunch amount by the wave scaling +mul r10.x, c9.y, r4.z; +mad r6.xy, r11.xy, r10.xx, r6.xy; + +// Bias our vert up a bit to compensate for precision errors. +// In particular, our filter coefficients are coming in as +// interpolated bytes, so there's bound to be a lot of slop +// from that. We've got a free slot in c25.x, so we'll use that. +// A better implementation would be to bias and scale our screen +// vert, effectively pushing the vert toward the camera without +// actually moving it, but this is easier and might work just +// as well. +add r6.z, r6.z, c25.x; + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c18; // HACKAGE +//mov r6.w, c13.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c29.x; +mul oFog, r10.x, c29.y; +mov oPos, r9; + + +// Output color is vertex green +// Output alpha is vertex red (vtx alpha is used for wave filtering) +// Whole thing modulated by material color/opacity. +mul oD0, v5.yyyx, c26; + +// Usual texture transform +mov r11.zw, c13.zzzz; +dp4 r11.x, v7, c14; +dp4 r11.y, v7, c15; +mov oT0, r11; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec1Lay_7.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec1Lay_7.inl new file mode 100644 index 00000000..62403eb1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec1Lay_7.inl @@ -0,0 +1,189 @@ + +vs.1.1 + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 + +// Store our input position in world space in r6 +m4x3 r6, v0, c18; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c13.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = illumination +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c22 = waterlevel + offset +// c23 = (maxAtten - minAtten) / depthFalloff +// c24 = minAtten. +// And in particular: +// c22.w = waterlevel +// c23.w = 1.f; +// c24.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c7, r6.xxxx; +mad r0, c8, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c4; +add r0, r0, c5; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c12.wwww; +add r0, r0, c12.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c12.wwww; +// dist += -kPi; +sub r0, r0, c12.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c11.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c10.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c11.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c10.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c11.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c10.wwww, r2; +mad r1, r4, c11.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c22, r6.zzzz; +mul r4, r4, c23; +add r4, r4, c24; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c13.zzzz; +max r4.xyz, r4, c13.xxxx; +//mov r4.xyz, c13.xxx; // HACKTEST + +// Calc our filter (see above). +mul r11, v5.wwww, c21; +max r11, r11, c13.xxxx; +min r11, r11, c13.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c6; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c13.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c22.w; +max r6.z, r6.z, r8.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= filter; +mul r1, r1, r11; + +// Pos = (in.x + S, in.y + R, r6.z) +// S = sum(k Dir.x A cos()) +// R = sum(k Dir.y A cos()) +// c30 = k Dir.x A +// c31 = k Dir.y A +// S = sum(cosDist * c30); +dp4 r7.x, r1, c30; +// R = sum(cosDist * c31); +dp4 r7.y, r1, c31; + +add r6.xy, r6.xy, r7.xy; + +// Bias our vert up a bit to compensate for precision errors. +// In particular, our filter coefficients are coming in as +// interpolated bytes, so there's bound to be a lot of slop +// from that. We've got a free slot in c25.x, so we'll use that. +// A better implementation would be to bias and scale our screen +// vert, effectively pushing the vert toward the camera without +// actually moving it, but this is easier and might work just +// as well. +add r6.z, r6.z, c25.x; + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c18; // HACKAGE +//mov r6.w, c13.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c29.x; +mul oFog, r10.x, c29.y; +mov oPos, r9; + + +// Output color is vertex green +// Output alpha is vertex red (vtx alpha is used for wave filtering) +// Whole thing modulated by material color/opacity. +mul oD0, v5.yyyx, c26; + +// Usual texture transform +mov r11.zw, c13.zzzz; +dp4 r11.x, v7, c14; +dp4 r11.y, v7, c15; +mov oT0, r11; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay11.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay11.inl new file mode 100644 index 00000000..ad2ed025 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay11.inl @@ -0,0 +1,209 @@ +vs.1.1 + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 + +// Store our input position in world space in r6 +m4x3 r6, v0, c18; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c13.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = illumination +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c22 = waterlevel + offset +// c23 = (maxAtten - minAtten) / depthFalloff +// c24 = minAtten. +// And in particular: +// c22.w = waterlevel +// c23.w = 1.f; +// c24.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c7, r6.xxxx; +mad r0, c8, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c4; +add r0, r0, c5; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c12.wwww; +add r0, r0, c12.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c12.wwww; +// dist += -kPi; +sub r0, r0, c12.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c11.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c10.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c11.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c10.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c11.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c10.wwww, r2; +mad r1, r4, c11.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c22, r6.zzzz; +mul r4, r4, c23; +add r4, r4, c24; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c13.zzzz; +max r4.xyz, r4, c13.xxxx; +//mov r4.xyz, c13.xxx; // HACKTEST + +// Calc our filter (see above). +mul r11, v5.wwww, c21; +max r11, r11, c13.xxxx; +min r11, r11, c13.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c6; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c13.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c22.w; +max r6.z, r6.z, r8.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= kFreq.xyzw; +mul r1, r1, c4; +// cosDist *= kAmplitude.xyzw; // Combine? +mul r1, r1, c6; +// cosDist *= filter; +mul r1, r1, r11; +// +// accumCos = (0, 0, 0, 0); +mov r7, c13.xxxx; +// temp = dp4( cosDist, toCenter_X ); +// accumCos.x += temp.xxxx; (but accumCos = (0,0,0,0) +dp4 r7.x, r1, -c7 +// +// temp = dp4( cosDist, toCenter_Y ); +// accumCos.y += temp.xxxx; +dp4 r7.y, r1, -c8 +// +// } +// +// accumBin = (1, 0, -accumCos.x); +// accumTan = (0, 1, -accumCos.y); +// accumNorm = (accumCos.x, accumCos.y, 1); +mov r11, c13.xxzx; +add r11, r11, r7; +dp3 r10.x, r11, r11; +rsq r10.x, r10.x; +mul r11, r11, r10.xxxx; + +// +// Add in our scrunch (offset in X/Y plane). +// Scale down our scrunch amount by the wave scaling +mul r10.x, c9.y, r4.z; +mad r6.xy, r11.xy, r10.xx, r6.xy; + +// Bias our vert up a bit to compensate for precision errors. +// In particular, our filter coefficients are coming in as +// interpolated bytes, so there's bound to be a lot of slop +// from that. We've got a free slot in c25.x, so we'll use that. +// A better implementation would be to bias and scale our screen +// vert, effectively pushing the vert toward the camera without +// actually moving it, but this is easier and might work just +// as well. +add r6.z, r6.z, c25.x; + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c18; // HACKAGE +//mov r6.w, c13.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c29.x; +mul oFog, r10.x, c29.y; +mov oPos, r9; + +// Output color is vertex green +// Output alpha is vertex red (vtx alpha is used for wave filtering) +// Whole thing modulated by material color/opacity. +mul oD0, v5.yyyx, c26; + +// Usual texture transform +mov r11.zw, c13.zzzz; +dp4 r11.x, v7, c14; +dp4 r11.y, v7, c15; +mov oT0, r11; + +dp4 r11.x, v7, c16; +dp4 r11.y, v7, c17; +mov oT1, r11; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay11_7.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay11_7.inl new file mode 100644 index 00000000..f2f2ff26 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay11_7.inl @@ -0,0 +1,191 @@ + +vs.1.1 + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 + +// Store our input position in world space in r6 +m4x3 r6, v0, c18; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c13.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = illumination +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c22 = waterlevel + offset +// c23 = (maxAtten - minAtten) / depthFalloff +// c24 = minAtten. +// And in particular: +// c22.w = waterlevel +// c23.w = 1.f; +// c24.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c7, r6.xxxx; +mad r0, c8, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c4; +add r0, r0, c5; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c12.wwww; +add r0, r0, c12.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c12.wwww; +// dist += -kPi; +sub r0, r0, c12.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c11.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c10.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c11.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c10.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c11.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c10.wwww, r2; +mad r1, r4, c11.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c22, r6.zzzz; +mul r4, r4, c23; +add r4, r4, c24; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c13.zzzz; +max r4.xyz, r4, c13.xxxx; +//mov r4.xyz, c13.xxx; // HACKTEST + +// Calc our filter (see above). +mul r11, v5.wwww, c21; +max r11, r11, c13.xxxx; +min r11, r11, c13.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c6; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c13.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c22.w; +max r6.z, r6.z, r8.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= filter; +mul r1, r1, r11; + +// Pos = (in.x + S, in.y + R, r6.z) +// S = sum(k Dir.x A cos()) +// R = sum(k Dir.y A cos()) +// c30 = k Dir.x A +// c31 = k Dir.y A +// S = sum(cosDist * c30); +dp4 r7.x, r1, c30; +// R = sum(cosDist * c31); +dp4 r7.y, r1, c31; + +add r6.xy, r6.xy, r7.xy; + +// Bias our vert up a bit to compensate for precision errors. +// In particular, our filter coefficients are coming in as +// interpolated bytes, so there's bound to be a lot of slop +// from that. We've got a free slot in c25.x, so we'll use that. +// A better implementation would be to bias and scale our screen +// vert, effectively pushing the vert toward the camera without +// actually moving it, but this is easier and might work just +// as well. +add r6.z, r6.z, c25.x; + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c18; // HACKAGE +//mov r6.w, c13.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c29.x; +mul oFog, r10.x, c29.y; +mov oPos, r9; + +// Output color is vertex green +// Output alpha is vertex red (vtx alpha is used for wave filtering) +// Whole thing modulated by material color/opacity. +mul oD0, v5.yyyx, c26; + +// Usual texture transform +mov r11.zw, c13.zzzz; +dp4 r11.x, v7, c14; +dp4 r11.y, v7, c15; +mov oT0, r11; + +dp4 r11.x, v7, c16; +dp4 r11.y, v7, c17; +mov oT1, r11; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay12.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay12.inl new file mode 100644 index 00000000..4e57a965 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay12.inl @@ -0,0 +1,210 @@ +vs.1.1 + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 +dcl_texcoord1 v8 + +// Store our input position in world space in r6 +m4x3 r6, v0, c18; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c13.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = illumination +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c22 = waterlevel + offset +// c23 = (maxAtten - minAtten) / depthFalloff +// c24 = minAtten. +// And in particular: +// c22.w = waterlevel +// c23.w = 1.f; +// c24.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c7, r6.xxxx; +mad r0, c8, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c4; +add r0, r0, c5; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c12.wwww; +add r0, r0, c12.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c12.wwww; +// dist += -kPi; +sub r0, r0, c12.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c11.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c10.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c11.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c10.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c11.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c10.wwww, r2; +mad r1, r4, c11.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c22, r6.zzzz; +mul r4, r4, c23; +add r4, r4, c24; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c13.zzzz; +max r4.xyz, r4, c13.xxxx; +//mov r4.xyz, c13.xxx; // HACKTEST + +// Calc our filter (see above). +mul r11, v5.wwww, c21; +max r11, r11, c13.xxxx; +min r11, r11, c13.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c6; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c13.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c22.w; +max r6.z, r6.z, r8.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= kFreq.xyzw; +mul r1, r1, c4; +// cosDist *= kAmplitude.xyzw; // Combine? +mul r1, r1, c6; +// cosDist *= filter; +mul r1, r1, r11; +// +// accumCos = (0, 0, 0, 0); +mov r7, c13.xxxx; +// temp = dp4( cosDist, toCenter_X ); +// accumCos.x += temp.xxxx; (but accumCos = (0,0,0,0) +dp4 r7.x, r1, -c7 +// +// temp = dp4( cosDist, toCenter_Y ); +// accumCos.y += temp.xxxx; +dp4 r7.y, r1, -c8 +// +// } +// +// accumBin = (1, 0, -accumCos.x); +// accumTan = (0, 1, -accumCos.y); +// accumNorm = (accumCos.x, accumCos.y, 1); +mov r11, c13.xxzx; +add r11, r11, r7; +dp3 r10.x, r11, r11; +rsq r10.x, r10.x; +mul r11, r11, r10.xxxx; + +// +// Add in our scrunch (offset in X/Y plane). +// Scale down our scrunch amount by the wave scaling +mul r10.x, c9.y, r4.z; +mad r6.xy, r11.xy, r10.xx, r6.xy; + +// Bias our vert up a bit to compensate for precision errors. +// In particular, our filter coefficients are coming in as +// interpolated bytes, so there's bound to be a lot of slop +// from that. We've got a free slot in c25.x, so we'll use that. +// A better implementation would be to bias and scale our screen +// vert, effectively pushing the vert toward the camera without +// actually moving it, but this is easier and might work just +// as well. +add r6.z, r6.z, c25.x; + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c18; // HACKAGE +//mov r6.w, c13.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c29.x; +mul oFog, r10.x, c29.y; +mov oPos, r9; + +// Output color is vertex green +// Output alpha is vertex red (vtx alpha is used for wave filtering) +// Whole thing modulated by material color/opacity. +mul oD0, v5.yyyx, c26; + +// Usual texture transform +mov r11.zw, c13.zzzz; +dp4 r11.x, v7, c14; +dp4 r11.y, v7, c15; +mov oT0, r11; + +dp4 r11.x, v8, c16; +dp4 r11.y, v8, c17; +mov oT1, r11; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay12_7.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay12_7.inl new file mode 100644 index 00000000..5769c98d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDec2Lay12_7.inl @@ -0,0 +1,192 @@ + +vs.1.1 + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 +dcl_texcoord1 v8 + +// Store our input position in world space in r6 +m4x3 r6, v0, c18; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c13.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = illumination +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c22 = waterlevel + offset +// c23 = (maxAtten - minAtten) / depthFalloff +// c24 = minAtten. +// And in particular: +// c22.w = waterlevel +// c23.w = 1.f; +// c24.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c7, r6.xxxx; +mad r0, c8, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c4; +add r0, r0, c5; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c12.wwww; +add r0, r0, c12.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c12.wwww; +// dist += -kPi; +sub r0, r0, c12.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c11.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c10.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c11.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c10.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c11.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c10.wwww, r2; +mad r1, r4, c11.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c22, r6.zzzz; +mul r4, r4, c23; +add r4, r4, c24; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c13.zzzz; +max r4.xyz, r4, c13.xxxx; +//mov r4.xyz, c13.xxx; // HACKTEST + +// Calc our filter (see above). +mul r11, v5.wwww, c21; +max r11, r11, c13.xxxx; +min r11, r11, c13.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c6; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c13.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c22.w; +max r6.z, r6.z, r8.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= filter; +mul r1, r1, r11; + +// Pos = (in.x + S, in.y + R, r6.z) +// S = sum(k Dir.x A cos()) +// R = sum(k Dir.y A cos()) +// c30 = k Dir.x A +// c31 = k Dir.y A +// S = sum(cosDist * c30); +dp4 r7.x, r1, c30; +// R = sum(cosDist * c31); +dp4 r7.y, r1, c31; + +add r6.xy, r6.xy, r7.xy; + +// Bias our vert up a bit to compensate for precision errors. +// In particular, our filter coefficients are coming in as +// interpolated bytes, so there's bound to be a lot of slop +// from that. We've got a free slot in c25.x, so we'll use that. +// A better implementation would be to bias and scale our screen +// vert, effectively pushing the vert toward the camera without +// actually moving it, but this is easier and might work just +// as well. +add r6.z, r6.z, c25.x; + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c18; // HACKAGE +//mov r6.w, c13.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c29.x; +mul oFog, r10.x, c29.y; +mov oPos, r9; + +// Output color is vertex green +// Output alpha is vertex red (vtx alpha is used for wave filtering) +// Whole thing modulated by material color/opacity. +mul oD0, v5.yyyx, c26; + +// Usual texture transform +mov r11.zw, c13.zzzz; +dp4 r11.x, v7, c14; +dp4 r11.y, v7, c15; +mov oT0, r11; + +dp4 r11.x, v8, c16; +dp4 r11.y, v8, c17; +mov oT1, r11; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDecEnv.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDecEnv.inl new file mode 100644 index 00000000..a0895766 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDecEnv.inl @@ -0,0 +1,298 @@ +vs.1.1 + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 +dcl_texcoord1 v8 +dcl_texcoord2 v9 + +// Store our input position in world space in r6 +m4x3 r6, v0, c18; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c13.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = illumination +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c22 = waterlevel + offset +// c23 = (maxAtten - minAtten) / depthFalloff +// c24 = minAtten. +// And in particular: +// c22.w = waterlevel +// c23.w = 1.f; +// c24.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c7, r6.xxxx; +mad r0, c8, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c4; +add r0, r0, c5; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c12.wwww; +add r0, r0, c12.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c12.wwww; +// dist += -kPi; +sub r0, r0, c12.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c11.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c10.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c11.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c10.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c11.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c10.wwww, r2; +mad r1, r4, c11.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c22, r6.zzzz; +mul r4, r4, c23; +add r4, r4, c24; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c13.zzzz; +max r4.xyz, r4, c13.xxxx; +//mov r4.xyz, c13.xxx; // HACKTEST + +// Calc our filter (see above). +mul r11, v5.wwww, c21; +max r11, r11, c13.xxxx; +min r11, r11, c13.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c6; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c13.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c22.w; +max r6.z, r6.z, r8.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= kFreq.xyzw; +mul r1, r1, c4; +// cosDist *= kAmplitude.xyzw; // Combine? +mul r1, r1, c6; +// cosDist *= filter; +mul r1, r1, r11; +// +// accumCos = (0, 0, 0, 0); +mov r7, c13.xxxz; +// temp = dp4( cosDist, toCenter_X ); +// accumCos.x += temp.xxxx; (but accumCos = (0,0,0,0) +dp4 r7.x, r1, -c7 +// +// temp = dp4( cosDist, toCenter_Y ); +// accumCos.y += temp.xxxx; +dp4 r7.y, r1, -c8 +// +// } +// +// accumBin = (1, 0, -accumCos.x); +// accumTan = (0, 1, -accumCos.y); +// accumNorm = (accumCos.x, accumCos.y, 1); +mov r11, c13.xxzx; +add r11, r11, r7.xyzz; +dp3 r10.x, r11, r11; +rsq r10.x, r10.x; +mul r11, r11, r10.xxxx; + +// +// Add in our scrunch (offset in X/Y plane). +// Scale down our scrunch amount by the wave scaling +mul r10.x, c9.y, r4.z; +mad r6.xy, r11.xy, r10.xx, r6.xy; + +// Bias our vert up a bit to compensate for precision errors. +// In particular, our filter coefficients are coming in as +// interpolated bytes, so there's bound to be a lot of slop +// from that. We've got a free slot in c25.x, so we'll use that. +// A better implementation would be to bias and scale our screen +// vert, effectively pushing the vert toward the camera without +// actually moving it, but this is easier and might work just +// as well. +add r6.z, r6.z, c25.x; + +// +// // Transform position to screen +// +// +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c29.x; +mul oFog, r10.x, c29.y; +//mov oFog.x, c13.y; +mov oPos, r9; + +// Calculate our normal scrunch and apply to our cosines. +mul r2.x, r6.z, c9.x; +add r2.x, r2.x, c13.z; +mul r2.x, r2.x, r4.z; +mul r7.xy, r7.xy, r2.xx; + +// Now onto texture coordinate generation. +// +// First is the usual texture transform +mov r11.zw, c13.zzzz; +dp4 r11.x, v7, c14; +dp4 r11.y, v7, c15; +mov oT0, r11; + +// Calculate our basis vectors as input into our tex3x3vspec +// This would be like: +//add r1, c13.zxxx, r7.zzxz; +//add r2, c13.xzxx, r7.zzyz; +//sub r3, c13.xxzz, r7.xyzz; +// BUT => +// Now r1-r3 are surface2world, but we still need to fold +// in texture2surface. That's imbedded in our uv's v8,v9, plus +// the normal we just computed into r11. +// So the full matrix multiply surface2world * texture2surface would be: +// | r1.v8 r1.v9 r1.(0,0,1) | +// | r2.v8 r2.v9 r2.(0,0,1) | +// | r3.v8 r3.v9 r3.(0,0,1) | +// But we notice that +// r1 = (1, 0, r7.x) +// r2 = (0, 1, r7.y) +// r3 = (-r7.x, -r7.y, 1) +// and also: +// r7.z == v8.z == v9.z == 0 +// and r7.w == 1.0 +// +// Considering the zeros, and doing the matrix multiply by hand, we get +// the final matrix of +// | v8.x v9.x r7.x | +// | v8.y v9.y r7.y | +// | -dp3(r7,v8) -dp3(r7,v9) 1 | +// So we wind up not needing r1-r3 at all +add r1, v8.xzzz, r7.zzxw; +mov r1.y, v9.x; + +add r2, v8.yzzz, r7.zzxw; +mov r2.y, v9.y; + +dp3 r3.x, -r7, v8; +dp3 r3.y, -r7, v9; +mov r3.zw, r7.ww; + +// Following section is debug only to skip the per-vert tangent space axes. +//add r1, c13.zxxx, r7.zzxw; +//add r2, c13.xzxx, r7.zzyw; +// +//mov r3.x, -r7.x; +//mov r3.y, -r7.y; +//mov r3.zw, c13.zz; + +// See vs_WaveFixedFin6.inl for derivation of the following +sub r0, r6, c27; // c27 is camera position. +dp3 r10.x, r0, r0; +rsq r10.x, r10.x; +mul r0, r0, r10.xxxx; + +dp3 r10.x, r0, c28; // c28 is kEnvAdjust +mad r10.y, r10.x, r10.x, -c28.w; + +rsq r9.x, r10.y; + +mad r10.z, r10.y, r9.x, r10.x; + +mad r0.xyz, r0, r10.zzz, -c28.xyz; + +mov r1.w, -r0.x; +mov r2.w, -r0.y; +mov r3.w, -r0.z; + +// Now r1-r3 are texture2world, with the eye-ray vector in .w. We just +// need to normalize them and bung them into output UV's 1-3. +// Note we're accounting for our environment map being flipped from +// D3D (and all rational thought) by putting r2 into UV3 and r3 into UV2. +mov r10.w, c13.z; +dp3 r10.x, r1, r1; +rsq r10.x, r10.x; +mul oT1, r1, r10.xxxw; + +dp3 r10.x, r3, r3; +rsq r10.x, r10.x; +mul oT2, r3, r10.xxxw; +//mul oT3, r3, r10.xxxw; // YZHACK + +dp3 r10.x, r2, r2; +rsq r10.x, r10.x; +mul oT3, r2, r10.xxxw; +//mul oT2, r2, r10.xxxw; + +// Output color is vertex green +// Output alpha is vertex red (vtx alpha is used for wave filtering) +// Whole thing modulated by material color/opacity. +mul oD0, v5.yyyx, c26; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDecEnv_7.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDecEnv_7.inl new file mode 100644 index 00000000..f090fb66 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveDecEnv_7.inl @@ -0,0 +1,331 @@ + + +vs.1.0 + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 +dcl_texcoord1 v8 +dcl_texcoord2 v9 + +// Store our input position in world space in r6 +m4x3 r6, v0, c18; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c13.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = illumination +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c22 = waterlevel + offset +// c23 = (maxAtten - minAtten) / depthFalloff +// c24 = minAtten. +// And in particular: +// c22.w = waterlevel +// c23.w = 1.f; +// c24.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c7, r6.xxxx; +mad r0, c8, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c4; +add r0, r0, c5; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c12.wwww; +add r0, r0, c12.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c12.wwww; +// dist += -kPi; +sub r0, r0, c12.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c11.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c10.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c11.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c10.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c11.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c10.wwww, r2; +mad r1, r4, c11.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c22, r6.zzzz; +mul r4, r4, c23; +add r4, r4, c24; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c13.zzzz; +max r4.xyz, r4, c13.xxxx; +//mov r4.xyz, c13.xxx; // HACKTEST + +// Calc our filter (see above). +mul r11, v5.wwww, c21; +max r11, r11, c13.xxxx; +min r11, r11, c13.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c6; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c13.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c22.w; +max r6.z, r6.z, r8.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= filter; +mul r1, r1, r11; +// Pos = (in.x + S, in.y + R, r6.z) +// S = sum(k Dir.x A cos()) +// R = sum(k Dir.y A cos()) +// c30 = k Dir.x A +// c31 = k Dir.y A +// S = sum(cosDist * c30); +dp4 r7.x, r1, c30; +// R = sum(cosDist * c31); +dp4 r7.y, r1, c31; + +add r6.xy, r6.xy, r7.xy; + + +// Bias our vert up a bit to compensate for precision errors. +// In particular, our filter coefficients are coming in as +// interpolated bytes, so there's bound to be a lot of slop +// from that. We've got a free slot in c25.x, so we'll use that. +// A better implementation would be to bias and scale our screen +// vert, effectively pushing the vert toward the camera without +// actually moving it, but this is easier and might work just +// as well. +add r6.z, r6.z, c25.x; + +// +// // Transform position to screen +// +// +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c29.x; +mul oFog, r10.x, c29.y; +//mov oFog, c13.y; +mov oPos, r9; + +// Now onto texture coordinate generation. +// +// First is the usual texture transform +mov r11.zw, c13.zzzz; +dp4 r11.x, v7, c14; +dp4 r11.y, v7, c15; +mov oT0, r11; + +// Calculate our basis vectors as input into our tex3x3vspec +// First we get our basis set off our surface. This is +// Okay, here we go: +// W == sum(k w Dir.x^2 A sin()) x +// V == sum(k w Dir.x Dir.y A sin()) x +// U == sum(k w Dir.y^2 A sin()) x +// +// T == sum(A sin()) +// +// S == sum(k Dir.x A cos()) +// R == sum(k Dir.y A cos()) +// +// Q == sum(k w A cos()) x +// +// M == sum(A cos()) +// +// P == sum(w Dir.x A cos()) x +// N == sum(w Dir.y A cos()) x +// +// Then: +// Pos = (in.x + S, in.y + R, waterheight + T) // Already done above. +// +// Bin = (1 - W, -V, P) +// Tan = (-V, 1 - U, N) +// Nor = (-P, -N, 1 - Q) +// +// The matrix +// |Bx, Tx, Nx| +// |By, Ty, Ny| +// |Bz, Tz, Nz| +// is surface2world, but we still need to fold in +// texture2surface. We'll go with the generalized +// (not assuming a flat surface) partials of dPos/dU and dPos/dV +// as coming in as uv coords v8 and v9. +// Then, if r5 = v8 X v9, then texture to surface is +// |v8.x, v9.x, r5.x| +// |v8.y, v9.y, r5.y| +// |v8.z, v9.z, r5.z| +// +// So, let's say we calc 3 vectors, +// r7 = (Bx, Tx, Nx) +// r8 = (By, Ty, Ny) +// r9 = (Bz, Tz, Nz) +// +// Then surface2world * texture2surface = +// |r7 dot v8, r7 dot v9, r7 dot r5| +// |r8 dot v8, r8 dot v9, r8 dot r5| +// |r9 dot v8, r9 dot v9, r9 dot r5| +// +// We will need r5 as v8 X v9 +mov r7, v8; +mul r5.xyz, r7.yzx, v9.zxy; +mad r5.xyz, r7.zxy, -v9.yzx, r5.xyz; + +// Okay, r1 currently has the vector of cosines, and r2 has vector of sines. +// Everything will want that times amplitude, so go ahead and fold that in. +mul r1, r1, c6; // r1 = A cos() = M +// Sines already have amplitude folded in, so r2 = A sin() = T. +// Now just compute r7-9 one element at a time. +dp4 r7.x, r2, -c35; // r7.x = -W +dp4 r7.y, r2, -c36; // r7.y = -V +dp4 r7.z, r1, -c32; // r7.z = -P +add r7.x, r7.x, c13.z; // r7.x = 1 - W; + +dp4 r8.x, r2, -c36; // r8.x = -V +dp4 r8.y, r2, -c37; // r8.y = -U +dp4 r8.z, r1, -c33; // r8.z = -N +add r8.y, r8.y, c13.z; // r8.y = 1 - U + +dp4 r9.z, r2, -c34; // r9.z = -Q +mov r9.x, -r7.z; // r9.x = P = -r7.z +mov r9.y, -r8.z; // r9.y = N = -r8.z +add r9.z, r9.z, c13.z; // r9.z = 1 - Q + +// Okay, got everything we need, construct r1-3 as surface2world*texture2surface. +dp3 r1.x, r7, v8; +dp3 r1.y, r7, v9; +dp3 r1.z, r7, r5; + +dp3 r2.x, r8, v8; +dp3 r2.y, r8, v9; +dp3 r2.z, r8, r5; + +dp3 r3.x, r9, v8; +dp3 r3.y, r9, v9; +dp3 r3.z, r9, r5; + +// Following section is debug only to skip the per-vert tangent space axes. +//add r1, c13.zxxx, r7.zzxw; +//add r2, c13.xzxx, r7.zzyw; +// +//mov r3.x, -r7.x; +//mov r3.y, -r7.y; +//mov r3.zw, c13.zz; + +// See vs_WaveFixedFin6.inl for derivation of the following +sub r0, r6, c27; // c27 is camera position. +dp3 r10.x, r0, r0; +rsq r10.x, r10.x; +mul r0, r0, r10.xxxx; + +dp3 r10.x, r0, c28; // c28 is kEnvAdjust +mad r10.y, r10.x, r10.x, -c28.w; + +rsq r9.x, r10.y; + +mad r10.z, r10.y, r9.x, r10.x; + +mad r0.xyz, r0, r10.zzz, -c28.xyz; + +// ATI 9000 is having trouble with eyeVec as computed. Normalizing seems to get it over the hump. +dp3 r10.x, r0, r0; +rsq r9.x, r10.x; +mul r0.xyz, r0.xyz, r9.xxx; + +mov r1.w, -r0.x; +mov r2.w, -r0.y; +mov r3.w, -r0.z; + + +// Now r1-r3 are texture2world, with the eye-ray vector in .w. We just +// need to normalize them and bung them into output UV's 1-3. +// Note we're accounting for our environment map being flipped from +// D3D (and all rational thought) by putting r2 into UV3 and r3 into UV2. +mov r10.w, c13.z; +dp3 r10.x, r1, r1; +rsq r10.x, r10.x; +mul oT1, r1, r10.xxxw; + +dp3 r10.x, r3, r3; +rsq r10.x, r10.x; +mul oT2, r3, r10.xxxw; +//mul oT3, r3, r10.xxxw; // YZHACK + +dp3 r10.x, r2, r2; +rsq r10.x, r10.x; +mul oT3, r2, r10.xxxw; +//mul oT2, r2, r10.xxxw; + +// Output color is vertex green +// Output alpha is vertex red (vtx alpha is used for wave filtering) +// Whole thing modulated by material color/opacity. +mul oD0, v5.yyyx, c26; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveFixedFin6.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveFixedFin6.inl new file mode 100644 index 00000000..c9a820a5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveFixedFin6.inl @@ -0,0 +1,449 @@ +vs.1.1 + +dcl_position v0 +dcl_color v5 + +// Store our input position in world space in r6 +m4x3 r6, v0, c21; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c16.zzzz; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = reflection strength (transparency) +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c25 = waterlevel + offset +// c26 = (maxAtten - minAtten) / depthFalloff +// c27 = minAtten. +// And in particular: +// c25.w = waterlevel +// c26.w = 1.f; +// c27.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c8, r6.xxxx; +mad r0, c9, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c5; +add r0, r0, c6; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c15.wwww; +add r0, r0, c15.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c15.wwww; +// dist += -kPi; +sub r0, r0, c15.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c14.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c13.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c14.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c13.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c14.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c13.wwww, r2; +mad r1, r4, c14.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c25, r6.zzzz; +mul r4, r4, c26; +add r4, r4, c27; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c16.zzzz; +max r4.xyz, r4, c16.xxxx; + +// Calc our filter (see above). +mul r11, v5.wwww, c24; +max r11, r11, c16.xxxx; +min r11, r11, c16.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c7; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c16.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c25.w; +max r6.z, r6.z, r8.z; // CLAMP +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= kFreq.xyzw; +mul r1, r1, c5; +// cosDist *= kAmplitude.xyzw; // Combine? +mul r1, r1, c7; +// cosDist *= filter; +mul r1, r1, r11; +// +// accumCos = (0, 0, 0, 0); +mov r7, c16.xxxx; +// temp = dp4( cosDist, toCenter_X ); +// accumCos.x += temp.xxxx; (but accumCos = (0,0,0,0) +dp4 r7.x, r1, -c8 +// +// temp = dp4( cosDist, toCenter_Y ); +// accumCos.y += temp.xxxx; +dp4 r7.y, r1, -c9 +// +// } +// +// accumBin = (1, 0, -accumCos.x); +// accumTan = (0, 1, -accumCos.y); +// accumNorm = (accumCos.x, accumCos.y, 1); +mov r11, c16.xxzx; +add r11, r11, r7; +dp3 r10.x, r11, r11; +rsq r10.x, r10.x; +mul r11, r11, r10.xxxx; + +// +// // Scrunch in based on computed (normalized) normal +// temp = mul( accumNorm, kNegScrunchScale ); // kNegScrunchScale = (-scrunchScale, -scrunchScale, 0, 0); +// accumPos += temp; +//dp3 r10.x, r11, c18.zxw; // winddir.x, winddir.y, 0, 0 // NUKE +// r10.x tells us whether our normal is opposed to the wind. +// If opposed, r10.x = 0, else r10.x = 1.f; +// We'll use this to kill the Scrunch on the back sides of waves. +// We use it for position right here, and then again for the +// normal just down a bit further. +//slt r10.x, r10.x, c16.x; // NUKE +//mov r10.x, c16.z; // HACKAGE NUKE +//mul r9, r10.xxxx, r11; // NUKE + +// Add in our scrunch (offset in X/Y plane). +// Scale down our scrunch amount by the wave scaling +mul r10.x, c12.y, r4.z; +//mov r10.x, c12.y; // NUKETEST TAKEOUT +mad r6.xy, r11.xy, r10.xx, r6.xy; + +// mul r6.z, r6.z, r10.xxxx; DEBUG + +// mad r6, r11, c12.yyzz, r6; + +// accumNorm = mul (accumNorm, kScrunchScale ); // kScrunchScale = (scrunchScale, scrunchScale, 1, 1); +// accumCos *= (scrunchScale, scrunchScale, 0, 0); + +mul r2.x, r6.z, c12.x; +//mad r2.x, r2.x, r10.x, c16.z; NUKE +add r2.x, r2.x, c16.z; +mul r2.x, r2.x, r4.z; // HACKAGE // NUKETEST BACKIN + +// mul r7, r7, c12.xxzz; +mul r7.xy, r7.xy, r2.xx; + +// This is actually wrong, but useful right now for visualizing the generated coords. +// See below for correct version. + +sub r3, c16.xxzz, r7.xyzz; + +//mov oD0, r3; // SEENORM + +dp3 r8.x, r3, c18.zxww; // WAVEFACE +mul r8.x, r8.x, c12.w; // WAVEFACE +max r8.x, r8.x, c16.x; // WAVEFACE +min r8.x, r8.x, c16.z; // WAVEFACE +//mov r9.x, c12.z; +//add r9.x, r9.x, -c16.z; +//mad r8.x, r9.x, r8.x, c16.z; // WAVEFACE +mul r8.x, r8.x, -c16.z; +add r8.x, r8.x, c16.z; + +// Normalize? + +// We can either calculate an orthonormal basis from the +// computed normal, with Binormal = (0,1,0) X Normal, Tangent = Normal X (1,0,0), +// or compute our basis directly from the partial derivatives, with +// Binormal = (1, 0, -cosX), Tangent = (0, 1, -cosY), Normal = (cosX, cosY, 1) +// +// These work out to identically the same result, so we'll compute directly +// from the partials because it takes 2 fewer instructions. +// +// Note that our basis is NOT orthonormal. The Normal is equal to +// Binormal X Tangent, but Dot(Binormal, Tangent) != 0. The Binormal and Tangents +// are both correct tangents to the surface, and their projections on the XY plane +// are 90 degrees apart, but in 3-space, they are not orthogonal. Practical implications? +// Not really. I'm actually not really sure which is more "proper" for bump mapping. +// +// Note also that we add when we should subtract and subtract when we should +// add, so that r1, r2, r3 aren't Binormal, Tangent, Normal, but the rows +// of our transform, (Bx, Tx, Nx), (By, Ty, Ny), (Bz, Tz, Nz). See below for +// explanation. +// +// Binormal = Y % Normal +// Cross product3 is: +// mul res.xyz, a.yzx, b.zxy +// mad res.xyz, -a.zxy, b.yzx, res.xyz +// mul r1.xyz, c16.zxx, r3.zxy; +// mad r1.xyz, -c16.xxz, r3.yzx, r1.xyz; + +// Tangent = Normal % X +// mul r2.xyz, r3.yzx, c16.xzx; +// mad r2.xyz, -r3.zxy, c16.xxz, r2; + +add r1, c16.zxxx, r7.zzxz; +add r2, c16.xzxx, r7.zzyz; + + +// Note that we're swapping z and y to match our environment map tools in max. +// We do this through our normal map transform (oT1, oT2, oT3), making it +// a concatenation of: +// +// rotate about Z (blue) to turn our map into the wind +// windRot = | dirY -dirX 0 | +// | dirX dirY 0 | +// | 0 0 1 | +// +// swap our Y and Z axes to match our environment map +// swapYZ = | 1 0 0 | +// | 0 0 1 | +// | 0 1 0 | +// +// rotate the normal into the surface's tangent space basis +// basis = | Bx Tx Nx | +// | By Ty Ny | +// | Bz Tz Nz | +// +// Note that we've constucted the basis by taking advantage of the +// matrix being a pure rotation, as noted below, so r1, r2 and r3 +// are actually constructed as: +// basis = | Bx -By -Bz | +// | -Tx Ty -Tz | +// | -Nx -Ny -Nz | +// +// Then the final normal map transform is: +// +// basis * swapYZ * windRot [ * normal ] + + +// sub r1.w, c17.x, r6.x; +// sub r2.w, c17.z, r6.z; +// sub r3.w, c17.y, r6.y; + +// Big note here. All this math can blow up if the camera position +// is outside the environment sphere. It's assumed that's dealt +// with in the app setting up the constants. For that reason, the +// camera position used here might not be the real local camera position, +// which is needed for the angular attenuation, so we burn another constant +// with our pseudo-camera position. To restrain the pseudo-camera from +// leaving the sphere, we make: +// pseudoPos = envCenter + (realPos - envCenter) * dist * R / (dist + R) +// where dist = |realPos - envCenter| + +// So, our "finitized" eyeray is: +// camPos + D * t - envCenter = D * t - (envCenter - camPos) +// with +// D = (pos - camPos) / |pos - camPos| // normalized usual eyeray +// and +// t = D dot F + sqrt( (D dot F)^2 - G ) +// with +// F = (envCenter - camPos) => c19.xyz +// G = F^2 - R^2 => c19.w +// R = environment radius. => unused +// +// This all derives from the positive root of equation +// (camPos + (pos - camPos) * t - envCenter)^2 = R^2, +// In other words, where on a sphere of radius R centered about envCenter +// does the ray from the real camera position through this point hit. +// +// Note that F, G, and R are all constants (one point, two scalars). +// +// So first we calculate D into r0, +// then D dot F into r10.x, +// then (D dot F)^2 - G into r10.y +// then rsq( (D dot F)^2 - G ) into r9.x; +// then t = r10.z = r10.x + r10.y * r9.x; +// and +// r0 = D * t - (envCenter - camPos) +// = r0 * r10.zzzz - F; +// +sub r0, r6, c17; +dp3 r10.x, r0, r0; +rsq r10.x, r10.x; +mul r0, r0, r10.xxxx; // r0 = D + +dp3 r10.x, r0, c19; // r10.x = D dot F +mad r10.y, r10.x, r10.x, -c19.w; // r10.y = (D dot F)^2 - G + +rsq r9.x, r10.y; // r9.x = 1/SQRT((D dot F)^2 - G) + +mad r10.z, r10.y, r9.x, r10.x; // r10.z = D dot F + SQRT((D dot F)^2 - G) + +mad r0.xyz, r0, r10.zzz, -c19.xyz; // r0.xyz = D * t - (envCenter - camPos) + +mov r1.w, -r0.x; +mov r2.w, -r0.y; +mov r3.w, -r0.z; + +// Now rotate our basis vectors into the wind +// This should be redone, and put our wind direction into +// the water texture. +dp3 r0.x, r1, c18.xyww; +dp3 r0.y, r1, c18.zxww; +mov r1.xy, r0; + +dp3 r0.x, r2, c18.xyww; +dp3 r0.y, r2, c18.zxww; +mov r2.xy, r0; + +dp3 r0.x, r3, c18.xyww; +dp3 r0.y, r3, c18.zxww; +mov r3.xy, r0; + +mov r0.zw, c16.zzxz; + +dp3 r0.x, r1, r1; +rsq r0.x, r0.x; +mul oT1, r1.xyzw, r0.xxxw; +// mul r8, r1.xyzw, r0.xxxw; // VISUAL + +dp3 r0.x, r2, r2; +rsq r0.x, r0.x; +mul oT3, r2.xyzw, r0.xxxw; +// mul r9, r2.xyzw, r0.xxxw; // VISUAL + +dp3 r0.x, r3, r3; +rsq r0.x, r0.x; +mul oT2, r3.xyzw, r0.xxxw; +// mul r9, r3.xyzw, r0.xxxw; // VISUAL + +// mul r3, r3.xzyw, r0.xxxw; +// mul r3.xy, r3, -c16.zzzz; + + +/* +// Want: +// oT1 = (BIN.x, TAN.x, NORM.x, view2pos.x) +// oT2 = (BIN.y, TAN.y, NORM.y, view2pos.y) +// ot3 = (BIN.z, TAN.z, NORM.z, view2pos.z) +// with BIN, TAN, and NORM normalized. +// Unnormalized, we have +// BIN = (1, 0, -r7.x) where r7 == accumCos +// TAN = (0, 1, -r7.y) +// NORM= (r7.x, r7.y, 1) +// So, unnormalized, we have +// oT1 = (1, 0, r7.x, view2pos.x) +// oT2 = (0, 1, r7.y, view2pos.y) +// oT3 = (-r7.x, -r7.y, 1, view2pos.z) +// which is just reversing the signs on the accumCos +// terms above. So the normalized version is just +// reversing the signs on the normalized version above. +*/ +//mov oT3, r4; + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c21; // HACKAGE +//mov r6.w, c16.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c28.x; +mul oFog, r10.x, c28.y; +//mov oFog, c16.y; // TESTFOGHACK +mov oPos, r9; + +mov oD0, c4; // SEENORM + +// Transform our uvw +dp4 r0.x, v0, c10; +dp4 r0.y, v0, c11; + +//mov r0.zw, c16.xxxz; +mov oT0, r0 + +// Questionble attenuation follows +// Find vector from this point to camera and normalize +sub r0, c17, r6; +dp3 r1.x, r0, r0; +rsq r1.x, r1.x; +mul r0, r0, r1.xxxx; +// Dot that with the computed normal +dp3 r1.x, r0, r11; +mul r1.x, r1.x, v5.z; +// dp3 r1.x, r0, r3; // if you want the adjusted normal, you'll need to normalize/swizzle r3 +// Map dot=1 => 0, dot=0 => 1 +sub r1.xyzw, c16.zzzz, r1.xxxx; +add r1.w, r1.wwww, c16.zzzz; +mul r1.w, r1.wwww, c16.yyyy; +// No need to clamp, since the destination register (in the pixel shader) +// will saturate [0..1] anyway. +//%%% mul r1.w, r1.w, r4.x; +//%%% mul r1.xyz, r1.xyz, r4.yyy; +mul r1, r1, r4.yyyx; // HACKTESTCOLOR +mul r1.xyz, r1, r8.xxx; // WAVEFACE +mul r1.w, r1.wwww, v5.xxxx; +mul oD1, r1, c20; + +// mov oD1, r4.yyyy; + +//mov oD1, c16.zzzz; // HACKAGE +// mov oD1, r9; +// mov oD1, r8.xzyw; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveFixedFin7.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveFixedFin7.inl new file mode 100644 index 00000000..90728af7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveFixedFin7.inl @@ -0,0 +1,437 @@ + +vs.1.1 + +dcl_position v0 +dcl_color v5 + +// Store our input position in world space in r6 +m4x3 r6, v0, c21; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c16.zzzz; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = reflection strength (transparency) +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c25 = waterlevel + offset +// c26 = (maxAtten - minAtten) / depthFalloff +// c27 = minAtten. +// And in particular: +// c25.w = waterlevel +// c26.w = 1.f; +// c27.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c8, r6.xxxx; +mad r0, c9, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c5; +add r0, r0, c6; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c15.wwww; +add r0, r0, c15.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c15.wwww; +// dist += -kPi; +sub r0, r0, c15.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c14.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c13.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c14.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c13.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c14.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c13.wwww, r2; +mad r1, r4, c14.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c25, r6.zzzz; +mul r4, r4, c26; +add r4, r4, c27; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c16.zzzz; +max r4.xyz, r4, c16.xxxx; + +// Calc our filter (see above). +mul r11, v5.wwww, c24; +max r11, r11, c16.xxxx; +min r11, r11, c16.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r5, r2, c7; +// r5 is now T = sum(Ai * sin()) +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r5, c16.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c25.w; +max r6.z, r6.z, r8.z; // CLAMP +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= kAmplitude.xyzw; // Combine? +mul r7, r1, c7; +// cosDist *= filter; +mul r7, r7, r11; +// r7 is now M = sum(Ai * cos()) + +// Okay, here we go: +// W == sum(k w Dir.x^2 A sin()) +// V == sum(k w Dir.x Dir.y A sin()) +// U == sum(k w Dir.y^2 A sin()) +// +// T == sum(A sin()) +// +// S == sum(k Dir.x A cos()) +// R == sum(k Dir.y A cos()) +// +// Q == sum(k w A cos()) +// +// M == sum(A cos()) +// +// P == sum(w Dir.x A cos()) +// N == sum(w Dir.y A cos()) +// +// Then: +// Pos = (in.x + S, in.y + R, waterheight + T) +// +// Bin = (1 - W, -V, P) +// Tan = (-V, 1 - U, N) +// Nor = (-P, -N, 1 - Q) +// +// But we want the transpose of that to go into r1-r3 + +dp4 r10.x, r7, c29; +add r6.x, r6.x, r10.x; +dp4 r10.x, r7, c30; +add r6.y, r6.y, r10.x; + +dp4 r1.x, r5, -c34; +dp4 r2.x, r5, -c35; +dp4 r3.x, r7, c31; +add r1.x, r1.xxxx, c16.zzzz; + +dp4 r1.y, r5, -c35; +dp4 r2.y, r5, -c36; +dp4 r3.y, r7, c32; +add r2.y, r2.yyyy, c16.zzzz; + +dp4 r1.z, r7, -c31; +dp4 r2.z, r7, -c32; +dp4 r3.z, r5, -c33; +add r3.z, r3.zzzz, c16.zzzz; + + +// Calculate our normalized vector from camera to vtx. +// We'll use that a couple of times coming up. +sub r5, r6, c17; +dp3 r10.x, r5, r5; +rsq r10.x, r10.x; +mul r5, r5, r10.xxxx; // r0 = D +rcp r5.w, r10.x; + +// Calculate our specular attenuation from and into r5.w. +// r5.w starts off the distance from vtx to camera. +// Once we've turned it into an attenuation factor, we +// scale the x and y of our normal map (through the transform bases) +// so that in the distance, the normal map is flat. Note that the +// geometry in the distance isn't necessarily flat. We want to apply +// this scale to the normal read from the normal map before it is +// transformed into surface space. +add r5.w, r5.w, c11.x; +mul r5.w, r5.w, c11.y; +min r5.w, r5.w, c16.z; +max r5.w, r5.w, c16.x; +mul r5.w, r5.w, r5.w; // Square it to account for perspective +mul r5.w, r5.w, c11.z; + + +// Normalize? + +// We can either calculate an orthonormal basis from the +// computed normal, with Binormal = (0,1,0) X Normal, Tangent = Normal X (1,0,0), +// or compute our basis directly from the partial derivatives, with +// Binormal = (1, 0, -cosX), Tangent = (0, 1, -cosY), Normal = (cosX, cosY, 1) +// +// These work out to identically the same result, so we'll compute directly +// from the partials because it takes 2 fewer instructions. +// +// Note that our basis is NOT orthonormal. The Normal is equal to +// Binormal X Tangent, but Dot(Binormal, Tangent) != 0. The Binormal and Tangents +// are both correct tangents to the surface, and their projections on the XY plane +// are 90 degrees apart, but in 3-space, they are not orthogonal. Practical implications? +// Not really. I'm actually not really sure which is more "proper" for bump mapping. +// +// Note also that we add when we should subtract and subtract when we should +// add, so that r1, r2, r3 aren't Binormal, Tangent, Normal, but the rows +// of our transform, (Bx, Tx, Nx), (By, Ty, Ny), (Bz, Tz, Nz). See below for +// explanation. +// +// Binormal = Y % Normal +// Cross product3 is: +// mul res.xyz, a.yzx, b.zxy +// mad res.xyz, -a.zxy, b.yzx, res.xyz +// mul r1.xyz, c16.zxx, r3.zxy; +// mad r1.xyz, -c16.xxz, r3.yzx, r1.xyz; + +// Tangent = Normal % X +// mul r2.xyz, r3.yzx, c16.xzx; +// mad r2.xyz, -r3.zxy, c16.xxz, r2; + +//mad r1, r5.wwww, c16.zxxx, r7.zzxz; +//mad r2, r5.wwww, c16.xzxx, r7.zzyz; +//mul r3.xy, r3.xy, r5.wwww; + + +// Note that we're swapping z and y to match our environment map tools in max. +// We do this through our normal map transform (oT1, oT2, oT3), making it +// a concatenation of: +// +// rotate about Z (blue) to turn our map into the wind +// windRot = | dirY -dirX 0 | +// | dirX dirY 0 | +// | 0 0 1 | +// +// swap our Y and Z axes to match our environment map +// swapYZ = | 1 0 0 | +// | 0 0 1 | +// | 0 1 0 | +// +// rotate the normal into the surface's tangent space basis +// basis = | Bx Tx Nx | +// | By Ty Ny | +// | Bz Tz Nz | +// +// Note that we've constucted the basis by taking advantage of the +// matrix being a pure rotation, as noted below, so r1, r2 and r3 +// are actually constructed as: +// basis = | Bx -By -Bz | +// | -Tx Ty -Tz | +// | -Nx -Ny -Nz | +// +// Then the final normal map transform is: +// +// basis * swapYZ * windRot [ * normal ] + + +// sub r1.w, c17.x, r6.x; +// sub r2.w, c17.z, r6.z; +// sub r3.w, c17.y, r6.y; + +// Big note here. All this math can blow up if the camera position +// is outside the environment sphere. It's assumed that's dealt +// with in the app setting up the constants. For that reason, the +// camera position used here might not be the real local camera position, +// which is needed for the angular attenuation, so we burn another constant +// with our pseudo-camera position. To restrain the pseudo-camera from +// leaving the sphere, we make: +// pseudoPos = envCenter + (realPos - envCenter) * dist * R / (dist + R) +// where dist = |realPos - envCenter| + +// So, our "finitized" eyeray is: +// camPos + D * t - envCenter = D * t - (envCenter - camPos) +// with +// D = (pos - camPos) / |pos - camPos| // normalized usual eyeray +// and +// t = D dot F + sqrt( (D dot F)^2 - G ) +// with +// F = (envCenter - camPos) => c19.xyz +// G = F^2 - R^2 => c19.w +// R = environment radius. => unused +// +// This all derives from the positive root of equation +// (camPos + (pos - camPos) * t - envCenter)^2 = R^2, +// In other words, where on a sphere of radius R centered about envCenter +// does the ray from the real camera position through this point hit. +// +// Note that F, G, and R are all constants (one point, two scalars). +// +// So first we calculate D into r0, +// then D dot F into r10.x, +// then (D dot F)^2 - G into r10.y +// then rsq( (D dot F)^2 - G ) into r9.x; +// then t = r10.z = r10.x + r10.y * r9.x; +// and +// r0 = D * t - (envCenter - camPos) +// = r0 * r10.zzzz - F; +// +mov r0, r5; // r0 = D + +dp3 r10.x, r0, c19; // r10.x = D dot F +mad r10.y, r10.x, r10.x, -c19.w; // r10.y = (D dot F)^2 - G + +rsq r9.x, r10.y; // r9.x = 1/SQRT((D dot F)^2 - G) + +mad r10.z, r10.y, r9.x, r10.x; // r10.z = D dot F + SQRT((D dot F)^2 - G) + +mad r0.xyz, r0, r10.zzz, -c19.xyz; // r0.xyz = D * t - (envCenter - camPos) + +// ATI 9000 is having trouble with eyeVec as computed. Normalizing seems to get it over the hump. +dp3 r10.x, r0, r0; +rsq r9.x, r10.x; +mul r0.xyz, r0.xyz, r9.xxx; + +mov r1.w, -r0.x; +mov r2.w, -r0.y; +mov r3.w, -r0.z; + +mov r0.zw, c16.zzxz; + +dp3 r0.x, r1, r1; +rsq r0.xy, r0.x; +mul r0.x, r0.x, r5.w; +mul oT1, r1.xyzw, r0.xxyw; +// mul r8, r1.xyzw, r0.xxxw; // VISUAL +mul r11.x, r1.z, r0.y; + + +dp3 r0.x, r2, r2; +rsq r0.xy, r0.x; +mul r0.x, r0.x, r5.w; +mul oT3, r2.xyzw, r0.xxyw; +// mul r9, r2.xyzw, r0.xxxw; // VISUAL +mul r11.y, r2.z, r0.y; + +dp3 r0.x, r3, r3; +rsq r0.xy, r0.x; +mul r0.x, r0.x, r5.w; +mul oT2, r3.xyzw, r0.xxyw; +// mul r9, r3.xyzw, r0.xxxw; // VISUAL +mul r11.z, r3.z, r0.y; + + +/* +// Want: +// oT1 = (BIN.x, TAN.x, NORM.x, view2pos.x) +// oT2 = (BIN.y, TAN.y, NORM.y, view2pos.y) +// ot3 = (BIN.z, TAN.z, NORM.z, view2pos.z) +// with BIN, TAN, and NORM normalized. +// Unnormalized, we have +// BIN = (1, 0, -r7.x) where r7 == accumCos +// TAN = (0, 1, -r7.y) +// NORM= (r7.x, r7.y, 1) +// So, unnormalized, we have +// oT1 = (1, 0, r7.x, view2pos.x) +// oT2 = (0, 1, r7.y, view2pos.y) +// oT3 = (-r7.x, -r7.y, 1, view2pos.z) +// which is just reversing the signs on the accumCos +// terms above. So the normalized version is just +// reversing the signs on the normalized version above. +*/ +//mov oT3, r4; + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c21; // HACKAGE +//mov r6.w, c16.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c28.x; +mul oFog, r10.x, c28.y; +//mov oFog, c16.zzzz; // TESTFOGHACK +mov oPos, r9; + +// Transform our uvw +mul r0.x, v0.xxxx, c10.xxxx; +mul r0.y, v0.yyyy, c10.xxxx; + +//mov r0.zw, c16.xxxz; +mov oT0, r0 + +// Questionble attenuation follows +// vector from this point to camera and normalize stashed in r5 +// Dot that with the computed normal +dp3 r1.x, -r5, r11; +mul r1.x, r1.x, v5.z; +// dp3 r1.x, r5, r3; // if you want the adjusted normal, you'll need to normalize/swizzle r3 +// Map dot=1 => 0, dot=0 => 1 +sub r1.xyzw, c16.zzzz, r1.xxxx; +add r1.w, r1.wwww, c16.zzzz; +mul r1.w, r1.wwww, c16.yyyy; +// No need to clamp, since the destination register (in the pixel shader) +// will saturate [0..1] anyway. +//%%% mul r1.w, r1.w, r4.x; +//%%% mul r1.xyz, r1.xyz, r4.yyy; +mul r1, r1, r4.yyyx; // HACKTESTCOLOR +//mul r1.xyz, r1, r8.xxx; // WAVEFACE +mul r1.w, r1.wwww, v5.xxxx; +mul r1.w, r1.wwww, c4.wwww; +mul oD0, r1, c20; + +mov oD1, c4; // SEENORM +//mov oD1, c16.xxxx; +// mov oD1, r4.yyyy; + +//mov oD1, c16.zzzz; // HACKAGE +// mov oD1, r9; +// mov oD1, r8.xzyw; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveGraph2.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveGraph2.inl new file mode 100644 index 00000000..f3b6e07c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveGraph2.inl @@ -0,0 +1,166 @@ + +vs.1.1 + +dcl_position v0 +dcl_normal v3 + +// c0 = (0,0.5,1.0,2.0) (aka NumericConsts) +// c1 = frequencies +// c2 = phases +// c3 = amplitudes + +// c4 = PiConsts = (1/(2PI), PI/2, PI, 2*PI) // NOTE THIS IS DIFFERENT +// because we don't need oonsqpi here but do want 1/2Pi. +// c5 = cosConsts = (1.0f, -1.0f/2.0f, 1.0f/ 24.0f, -1.0f/ 720.0f); + +// c6 = ((cMax - cMin), cMin, 2ndLayerVOffset, 2ndLayerScale); +// c7 = overall color, including current opacity. Will +// probably only use the opacity, which we could stuff into +// the free slot of c6, but we're a wuss. + +// First, "move" the position to oPos +mov r0, v0; +//mov r0.y, -r0.yyyy; +mov r0.w, c0.zzzz; +mov oPos, r0; + +// Now the tricky part. + +// The base layer defines the shape of the incoming wave +// The next layer has bubbles (noise) and moves in when the +// wave is moving in, moves out when wave is moving out. +// So calculate uvw for first layer, second uvw shares u val +// and v val is const + +// The .x component of the normal +// tells us how much to shift this vert based on the +// cumulative cosine wave. + +// Figure c = Sigma((cosine(v0.x * freq + phase) + 1) * amp); +// Note that range c must be [0..1] +// Also, c(-1) must equal c(1) so it will wrap. +// That implies freq = k * 2 * PI, where k is an integer. +// To keep c >= 0, we can add 1 to each term in the sigma BEFORE +// modulating by the amplitude. +// That puts our range at [0..2*sigma(amp)], so as long as +// sigma(amp) <= 0.5, we're fine. + +// Get our input to cosine value (v0.x * freq + phase). +add r0, v0.xxxx, c0.zzzz; +mul r0, r0, c1; +add r0, r0, c2; + +// Get it into range [-Pi..Pi] +// First divide out the 2PI +// add r0, r0, c4.zzzz; HACKOUT +mul r0, r0, c4.xxxx; + +// Do an integer mod +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy + +//mov oD1, r1; // HACKTEST +//mov oD1.w, c0.zzzz; // HACKTEST + +// Move back into PI space, w/ *= 2P, -= PI +mul r0, r1, c4.wwww; +sub r0, r0, c4.zzzz; + +// Okay, compute cosine here. +// cos = 1 + r0^2 * kCos.y + r0^4 * kCos.Z + r0^6 * kCos.w +// Note: could pare off an instr by putting 1/kCos.w in kCos.x, +// then doing a mad to get r3=(1/kCos.w + r0^6), then mad that +// into the accum by kCos.w to get (1 + r0^6*kCos.x). But who cares. +mul r1, r0, r0; // r0^2 +mul r2, r1, r1; // r0^4 +mul r3, r1, r2; // r0^6 + +mov r4, c5.xxxx; // r4 = 1 +mad r4, r1, c5.yyyy, r4; // r4 += r0^2 * kCos.y +mad r4, r2, c5.zzzz, r4; // r4 += r0^4 * kCos.z +mad r4, r3, c5.wwww, r4; // r4 += r0^6 * kCos.w + +add r4, r4, c0.zzzz; // shift from [-1..1] to [0..2] +//mov r4, c0.xxxx; // HACKLAST +mul r4, r4, c3; // times amplitude + +dp4 r5.y, r4, c0.zzzz; // r5.x = sigma((cos() + 1) * amp); + +// V calculation, goes something like: +// For layers 0 and 2: +// V = { 1 + c6.z <= r5.y = 0 } * norm.x // norm.x == v3.x +// { 1 + 0 <= r5.y = 1 } +// For layer 1: +// V = (norm.x + c6.z) * c6.w // Scaled like U +// +// Another way to formulate that is +// baseV = cMin + sinAge * (cMax-cMin) where +// cMin = 2 +// cMax = 1 +// sinAge = color.a = c7.w +// delV = sigma(cos) = r5.y +// Then +// V0 = V2 = (baseV + delV) * v3.x +// V1 = (norm.x + baseV + delV) * c6.w +// +// If we're sure we want cMin = 2 and cMax = 1, then it simplifies to: +// baseV = 2 - sinAge = c0.w - c7.w +// delV = r5.y +// (baseV + delV) = c0.w - c7.w + r5.y +// +// If we want to stay general, then +// baseV = c6.x * c7.w + c6.y +// delV = -r5.y +// (baseV + delV) = constant + r5.y +// + +// make r5.y = (baseV + delV) +add r5.y, c6.xxxx, r5.yyyy; + +//mov oD1, r5.yyyy; // HACKLAST +//mov oD1.w, c0.zzzz; // HACKLAST + +// U is input U (or v0.x * 0.5f + 0.5f) +mul r5.x, v0.x, c0.y; +add r5.x, r5.x, c0.y; + +// Fill out wq. +mov r5.zw, c0.xz; + +mul oT0, r5, v3.wxww; +// mov oD1, r5.yyyw; // HACKTEST +mul oT2, r5, v3.wxww; + +// Second uv shares u, but v is norm.x + c6.x; +// Then we scale it. +// If we want the bubble texture to move with the +// wave front, we want the second UV calc (RESCALE1). +// But it looks better to have the bubbles moving +// slightly faster than the wave front. RESCALE0 +// happens to do that, because we're scaling the +// texture by a factor of 2, but we should probably +// supply an independent scale of the motion vs. the +// scale of the texture. +// Let's move c6 to r6 for ease of use. +mov r6, c6; +// add r5.x, r5.x, c6.y; +// add r5.y, c6.xxxx, v3.xxxx; // RESCALE0 +// mul r5.xy, r5, c6.wwww; // RESCALE0 +add r5.x, r5.x, r6.y; // RESCALE1 // offset U +mov r5.y, v3.xx; // RESCALE1 // Init V to value stashed in normal.x +mul r5.xy, r5, r6.wwww; // RESCALE1 // scale them by single scale value +mad r5.y, r6.xx, r6.zz, r5.yy; // RESCALE1 // add in our scaled V offset (sinage * vScale) +mov oT1, r5; + +//mov oT0, v7; // HACKTEST +//mov oT1, v7; // HACKTEST +//mov oT2, v7; // HACKTEST + +// Just slam in the constant color (includes our current opacity). +mov oD0, c7; +//mov oD0, c0.zzzz; // HACKTEST diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveGridFin.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveGridFin.inl new file mode 100644 index 00000000..6a622c0f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveGridFin.inl @@ -0,0 +1,471 @@ +vs.1.1 + +dcl_position v0 + +//m4x4 oPos, v0, c0 + + +/* +In fact, I was trying to understand how it was possible to expand FRC into 4 +instructions... +Actually, I can do it in 7 instructions :) + +EXPP r0.y, r1.xxxx +MOV r0.x, r0.y +EXPP r0.y, r1.zzzz +MOV r0.z, r0.y +EXPP r0.y, r1.wwww +MOV r0.w, r0.y +EXPP r0.y, r1.yyyy +*/ + +/* + // Constants for sin and cos. 3 term approximation seems plenty + // (it's what i used for software sim, and had no visibly different + // results than the math library functions). + // When doing sin/cos together, some speedup might be obtained + // with good pairing of ops doing them simultaneously. Also save + // an instruction calculating r0^3. + D3DXVECTOR4 vSin( 1.0f, -1.0f/6.0f, 1.0f/120.0f, -1.0f/5040.0f ); + D3DXVECTOR4 vCos( 1.0f, -1.0f/2.0f, 1.0f/ 24.0f, -1.0f/ 720.0f ); +*/ + +/* +Cos(): + + + r1 = mul(r0, r0); // r0^2 + r2 = mul(r1, r1); // r0^4 + + //cos + r3 = mad( r1, vCos.yyyy, vCos.xxxx ); + r3 = mad( r2, vCos.zzzz, r3 ); +*/ + +/* +Sin(); + r1 = mul(r0, r0); // r0^3 + r1 = mul(r0, r1); + r2 = mul(r1, r1); // r0^6 + + r3 = mad( r1, vSin.yyyy, r0 ); + r3 = mad( r2, vSin.zzzz, r3 ); +*/ + +/* +SinCos(): + + r1 = mul(r0, r0); // r0^2 + r2 = mul(r1, r0); // r0^3 // probably stall + r3 = mul(r1, r1); // r0^4 + r4 = mul(r2, r2); // r0^6 + + r5 = mad( r1, vCos.yyyy, vCos.xxxx ); + r6 = mad( r2, vSin.yyyy, r0 ); + r5 = mad( r3, vCos.zzzz, r5 ); + r6 = mad( r4, vSin.zzzz, r6 ); + +*/ + +/* +consts + kOneOverEightNsqPi = 1.f / ( 8.f * Pi * 4.f * 4.f ); + kPiOverTwo = Pi / 2.f; + kTwoPi = Pi * 2.f; + kPi = Pi; +*/ +/* +CONSTANT REGISTERS +VOLATILE CONSTS - change per invocation +C0-C3 local2proj matrix +C4 color +C5 freq vector +C6 phase vector +C7 amplitude vector +C8 center0 +C9 center1 +C10 center2 +C11 center3 +C12 scrunch = (scrunch, -scrunch, 0, 1); +CONSTANT CONSTS - forever more +C13 SinConsts = (1.0f, -1.0f/6.0f, 1.0f/120.0f, -1.0f/5040.0f); +C14 CosConsts = (1.0f, -1.0f/2.0f, 1.0f/ 24.0f, -1.0f/ 720.0f); +C15 PiConsts = (1.f / 8*Pi*N^2, Pi/2, Pi, 2*Pi); +C16 numberConsts = (0.f, 0.5f, 1.f, 2.f); +//===================================== +TEMP REGISTERS +r6 accumPos +r7 accumCos +r8 toCenter_Y +r9 toCenter_X +r11 filter +r10 tempFloat +*/ +// const float4 kCosConsts = float4(1.0f, -1.0f/2.0f, 1.0f/ 24.0f, -1.0f/ 720.0f); +// const float4 kSinConsts = float4(1.0f, -1.0f/6.0f, 1.0f/120.0f, -1.0f/5040.0f); + +// const float4 kPiConsts = float4(1.f / (8.f * 3.1415f * 16f), 3.1415f*0.5f, 3.1415f, 3.1515f*2.f); +// const float4 k0512 = float4(0.f, 0.5f, 1.f, 2.f); + +// accumPos = inPos; + mov r6, v0; +// +// For each wave +// { +// // First, we want to filter out waves based on distance from the local origin +// dist = dp3(inPos, inPos); + dp3 r0, r6, r6; +// dist *= kFreqSq.xyzw; + mul r0, r0, c5; + mul r0, r0, c5; +// dist *= kOneOverEightNsqPi; // combine this into kFreqSq? + mul r0, r0, c15.xxxx; +// dist = min(dist, kPiOverTwo); + min r0, r0, c15.yyyy; +// filter = cos(dist); + mul r1, r0, r0; // r0^2 + mul r2, r1, r1; // r1^2 + mul r1, r1, c14.yyyy; + add r11, r1, c14.xxxx; + mad r11, r2, c14.zzzz, r11; + + +// filter *= kAmplitude.xyzw; +// mul r11, r11, c7; +// // Notice that if dist is a 4vec, all this can be simultaneously done for 4 waves at a time. +// +// Find the x/y distances and stuff them into r9(x) and r8(y) respectively + // toCenter_X.x = dir0.x * pos.x; + // toCenter_Y.x = dir0.y * pos.y; + mul r0, c8, r6.xxxx; + mad r0, c9, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); + mul r0, r0, c5; + add r0, r0, c6; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); + rcp r4, c15.wwww; + add r0, r0, c15.zzzz; + mul r0, r0, r4; +// dist = frac(dist); + expp r1.y, r0.xxxx + mov r1.x, r1.yyyy + expp r1.y, r0.zzzz + mov r1.z, r1.yyyy + expp r1.y, r0.wwww + mov r1.w, r1.yyyy + expp r1.y, r0.yyyy +// dist *= kTwoPi; + mul r0, r1, c15.wwww; +// dist += -kPi; + sub r0, r0, c15.zzzz; + +// +// sincos(dist, sinDist, cosDist); + // sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z + // cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z + mul r1, r0, r0; // r0^2 + mul r2, r1, r0; // r0^3 - probably stall + mul r3, r1, r1; // r0^4 + mul r4, r1, r2; // r0^5 + mul r5, r2, r3; // r0^7 + + mul r1, r1, c14.yyyy; // r1 = r0^2 * vCos.y + mad r2, r2, c13.yyyy, r0; // r2 = r0 + r0^3 * vSin.y + add r1, r1, c14.xxxx; // r1 = 1 + r0^2 * vCos.y + mad r2, r4, c13.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z + mad r1, r3, c14.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + + // r0^7 & r0^6 terms + mul r4, r4, r0; // r0^6 + mad r2, r5, c13.wwww, r2; + mad r1, r4, c14.wwww, r1; + +//mov r2, r1; + // r2 == sinDist + // r1 == cosDist +// sinDist *= filter; + mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw + mul r2, r2, c7; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). + dp4 r6.z, r2, c16.zzzz; +// +// cosDist *= kFreq.xyzw; + mul r1, r1, c5; +// cosDist *= kAmplitude.xyzw; // Combine? + mul r1, r1, c7; +// cosDist *= filter; + mul r1, r1, r11; +// +// accumCos = (0, 0, 0, 0); + mov r7, c16.xxxx; +// temp = dp4( cosDist, toCenter_X ); +// accumCos.x += temp.xxxx; (but accumCos = (0,0,0,0) + dp4 r7.x, r1, -c8 +// +// temp = dp4( cosDist, toCenter_Y ); +// accumCos.y += temp.xxxx; + dp4 r7.y, r1, -c9 +// +// } +// +// accumBin = (1, 0, -accumCos.x); +// accumTan = (0, 1, -accumCos.y); +// accumNorm = (accumCos.x, accumCos.y, 1); + mov r11, c16.xxzx; + add r11, r11, r7; + dp3 r10.x, r11, r11; + rsq r10.x, r10.x; + mul r11, r11, r10.xxxx; + +// +// // Scrunch in based on computed (normalized) normal +// temp = mul( accumNorm, kNegScrunchScale ); // kNegScrunchScale = (-scrunchScale, -scrunchScale, 0, 0); +// accumPos += temp; + dp3 r10.x, r11, c18.zxw; // winddir.x, winddir.y, 0, 0 + // r10.x tells us whether our normal is opposed to the wind. + // If opposed, r10.x = 0, else r10.x = 1.f; + // We'll use this to kill the Scrunch on the back sides of waves. + // We use it for position right here, and then again for the + // normal just down a bit further. + slt r10.x, r10.x, c16.x; + mul r9, r10.xxxx, r11; + + mad r6, r9, c12.yyzz, r6; + +// mul r6.z, r6.z, r10.xxxx; DEBUG + +// mad r6, r11, c12.yyzz, r6; + +// accumNorm = mul (accumNorm, kScrunchScale ); // kScrunchScale = (scrunchScale, scrunchScale, 1, 1); + // accumCos *= (scrunchScale, scrunchScale, 0, 0); + + mul r2.x, r6.z, c12.x; + mul r2.x, r2.x, r10.x; // ??? + add r2.x, r2.x, c16.z; + +// mul r7, r7, c12.xxzz; + mul r7.xy, r7.xy, r2.xx; + +// This is actually wrong, but useful right now for visualizing the generated coords. +// See below for correct version. + + sub r3, c16.xxzx, r7.xyzz; + + // Normalize? + + // We can either calculate an orthonormal basis from the + // computed normal, with Binormal = (0,1,0) X Normal, Tangent = Normal X (1,0,0), + // or compute our basis directly from the partial derivatives, with + // Binormal = (1, 0, -cosX), Tangent = (0, 1, -cosY), Normal = (cosX, cosY, 1) + // + // These work out to identically the same result, so we'll compute directly + // from the partials because it takes 2 fewer instructions. + // + // Note that our basis is NOT orthonormal. The Normal is equal to + // Binormal X Tangent, but Dot(Binormal, Tangent) != 0. The Binormal and Tangents + // are both correct tangents to the surface, and their projections on the XY plane + // are 90 degrees apart, but in 3-space, they are not orthogonal. Practical implications? + // Not really. I'm actually not really sure which is more "proper" for bump mapping. + // + // Note also that we add when we should subtract and subtract when we should + // add, so that r1, r2, r3 aren't Binormal, Tangent, Normal, but the rows + // of our transform, (Bx, Tx, Nx), (By, Ty, Ny), (Bz, Tz, Nz). See below for + // explanation. + // + // Binormal = Y % Normal + // Cross product3 is: + // mul res.xyz, a.yzx, b.zxy + // mad res.xyz, -a.zxy, b.yzx, res.xyz +// mul r1.xyz, c16.zxx, r3.zxy; +// mad r1.xyz, -c16.xxz, r3.yzx, r1.xyz; + + // Tangent = Normal % X +// mul r2.xyz, r3.yzx, c16.xzx; +// mad r2.xyz, -r3.zxy, c16.xxz, r2; + + add r1, c16.zxxx, r7.zzxz; + add r2, c16.xzxx, r7.zzyz; + + // Note that we're swapping z and y to match our environment map tools in max. + // We do this through our normal map transform (oT1, oT2, oT3), making it + // a concatenation of: + // + // rotate about Z (blue) to turn our map into the wind + // windRot = | dirY -dirX 0 | + // | dirX dirY 0 | + // | 0 0 1 | + // + // swap our Y and Z axes to match our environment map + // swapYZ = | 1 0 0 | + // | 0 0 1 | + // | 0 1 0 | + // + // rotate the normal into the surface's tangent space basis + // basis = | Bx Tx Nx | + // | By Ty Ny | + // | Bz Tz Nz | + // + // Note that we've constucted the basis by taking advantage of the + // matrix being a pure rotation, as noted below, so r1, r2 and r3 + // are actually constructed as: + // basis = | Bx -By -Bz | + // | -Tx Ty -Tz | + // | -Nx -Ny -Nz | + // + // Then the final normal map transform is: + // + // basis * swapYZ * windRot [ * normal ] + + +// sub r1.w, c17.x, r6.x; +// sub r2.w, c17.z, r6.z; +// sub r3.w, c17.y, r6.y; + + // Big note here. All this math can blow up if the camera position + // is outside the environment sphere. It's assumed that's dealt + // with in the app setting up the constants. For that reason, the + // camera position used here might not be the real local camera position, + // which is needed for the angular attenuation, so we burn another constant + // with our pseudo-camera position. To restrain the pseudo-camera from + // leaving the sphere, we make: + // pseudoPos = envCenter + (realPos - envCenter) * dist * R / (dist + R) + // where dist = |realPos - envCenter| + + // So, our "finitized" eyeray is: + // camPos + D * t - envCenter = D * t - (envCenter - camPos) + // with + // D = (pos - camPos) / |pos - camPos| // normalized usual eyeray + // and + // t = D dot F + sqrt( (D dot F)^2 - G ) + // with + // F = (envCenter - camPos) => c19.xyz + // G = F^2 - R^2 => c19.w + // R = environment radius. => unused + // + // This all derives from the positive root of equation + // (camPos + (pos - camPos) * t - envCenter)^2 = R^2, + // In other words, where on a sphere of radius R centered about envCenter + // does the ray from the real camera position through this point hit. + // + // Note that F, G, and R are all constants (one point, two scalars). + // + // So first we calculate D into r0, + // then D dot F into r10.x, + // then (D dot F)^2 - G into r10.y + // then rsq( (D dot F)^2 - G ) into r9.x; + // then t = r10.z = r10.x + r10.y * r9.x; + // and + // r0 = D * t - (envCenter - camPos) + // = r0 * r10.zzzz - F; + // + sub r0, r6, c17; + dp3 r10.x, r0, r0; + rsq r10.x, r10.x; + mul r0, r0, r10.xxxx; + + dp3 r10.x, r0, c19; + mad r10.y, r10.x, r10.x, -c19.w; + + rsq r9.x, r10.y; + + mad r10.z, r10.y, r9.x, r10.x; + + mad r0.xyz, r0, r10.zzz, -c19.xyz; + + mov r1.w, -r0.x; + mov r2.w, -r0.y; + mov r3.w, -r0.z; + + // Now rotate our basis vectors into the wind + dp3 r0.x, r1, c18.xyww; + dp3 r0.y, r1, c18.zxww; + mov r1.xy, r0; + + dp3 r0.x, r2, c18.xyww; + dp3 r0.y, r2, c18.zxww; + mov r2.xy, r0; + + dp3 r0.x, r3, c18.xyww; + dp3 r0.y, r3, c18.zxww; + mov r3.xy, r0; + + mov r0.w, c16.zzzz; + + dp3 r0.x, r1, r1; + rsq r0.x, r0.x; + mul oT1, r1.xyzw, r0.xxxw; +// mul r8, r1.xyzw, r0.xxxw; // VISUAL + + dp3 r0.x, r2, r2; + rsq r0.x, r0.x; + mul oT3, r2.xyzw, r0.xxxw; +// mul r9, r2.xyzw, r0.xxxw; // VISUAL + + dp3 r0.x, r3, r3; + rsq r0.x, r0.x; + mul oT2, r3.xyzw, r0.xxxw; +// mul r9, r3.xyzw, r0.xxxw; // VISUAL + +// mul r3, r3.xzyw, r0.xxxw; +// mul r3.xy, r3, -c16.zzzz; + +/* + // Want: + // oT1 = (BIN.x, TAN.x, NORM.x, view2pos.x) + // oT2 = (BIN.y, TAN.y, NORM.y, view2pos.y) + // ot3 = (BIN.z, TAN.z, NORM.z, view2pos.z) + // with BIN, TAN, and NORM normalized. + // Unnormalized, we have + // BIN = (1, 0, -r7.x) where r7 == accumCos + // TAN = (0, 1, -r7.y) + // NORM= (r7.x, r7.y, 1) + // So, unnormalized, we have + // oT1 = (1, 0, r7.x, view2pos.x) + // oT2 = (0, 1, r7.y, view2pos.y) + // oT3 = (-r7.x, -r7.y, 1, view2pos.z) + // which is just reversing the signs on the accumCos + // terms above. So the normalized version is just + // reversing the signs on the normalized version above. +*/ +//mov oT3, r4; + +// +// // Transform position to screen +// +// + m4x4 oPos, r6, c0; + +// Still need to attenuate based on position + mov oD0, c4; + +// This should be in local space after xforming v0 + dp4 r0.x, v0, c10; + dp4 r0.y, v0, c11; + mov r0.zw, c16.xxxz; + mov oT0, r0 +// mov oT0, v7; + +// Questionble attenuation follows + // Find vector from this point to camera and normalize + sub r0, c17, r6; + dp3 r1.x, r0, r0; + rsq r1.x, r1.x; + mul r0, r0, r1.xxxx; + // Dot that with the computed normal + dp3 r1.x, r0, r11; +// dp3 r1.x, r0, r3; // if you want the adjusted normal, you'll need to normalize/swizzle r3 + // Map dot=1 => 0, dot=0 => 1 + sub r1.xyzw, c16.zzzz, r1.xxxx; + add r1.w, r1.wwww, c16.zzzz; + mul r1.w, r1.wwww, c16.yyyy; + // No need to clamp, since the destination register (in the pixel shader) + // will saturate [0..1] anyway. + mul oD1, r1, c20; +// mov oD1, r9; +// mov oD1, r8.xzyw; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveRip.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveRip.inl new file mode 100644 index 00000000..e0746a20 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveRip.inl @@ -0,0 +1,243 @@ +vs.1.1 +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 + + +// Store our input position in world space in r6 +m4x3 r6, v0, c25; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c16.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = reflection strength (transparency) +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c30 = waterlevel + offset +// c31 = (maxAtten - minAtten) / depthFalloff +// c32 = minAtten. +// And in particular: +// c30.w = waterlevel +// c31.w = 1.f; +// c32.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c8, r6.xxxx; +mad r0, c9, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c5; +add r0, r0, c6; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c15.wwww; +add r0, r0, c15.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c15.wwww; +// dist += -kPi; +sub r0, r0, c15.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c14.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c13.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c14.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c13.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c14.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c13.wwww, r2; +mad r1, r4, c14.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c30, r6.zzzz; +mul r4, r4, c31; +add r4, r4, c32; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c16.zzzz; +max r4.xyz, r4, c16.xxxx; +//mov r4.xyz, c16.xxx; // HACKTEST + +// Calc our filter (see above). +mul r11, v5.wwww, c29; +max r11, r11, c16.xxxx; +min r11, r11, c16.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c7; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c16.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c30.w; +max r6.z, r6.z, r8.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= kFreq.xyzw; +mul r1, r1, c5; +// cosDist *= kAmplitude.xyzw; // Combine? +mul r1, r1, c7; +// cosDist *= filter; +mul r1, r1, r11; +// +// accumCos = (0, 0, 0, 0); +mov r7, c16.xxxx; +// temp = dp4( cosDist, toCenter_X ); +// accumCos.x += temp.xxxx; (but accumCos = (0,0,0,0) +dp4 r7.x, r1, -c8 +// +// temp = dp4( cosDist, toCenter_Y ); +// accumCos.y += temp.xxxx; +dp4 r7.y, r1, -c9 +// +// } +// +// accumBin = (1, 0, -accumCos.x); +// accumTan = (0, 1, -accumCos.y); +// accumNorm = (accumCos.x, accumCos.y, 1); +mov r11, c16.xxzx; +add r11, r11, r7; +dp3 r10.x, r11, r11; +rsq r10.x, r10.x; +mul r11, r11, r10.xxxx; + +// +// Add in our scrunch (offset in X/Y plane). +// Scale down our scrunch amount by the wave scaling +mul r10.x, c12.y, r4.z; +mad r6.xy, r11.xy, r10.xx, r6.xy; + +// Bias our vert up a bit to compensate for precision errors. +// In particular, our filter coefficients are coming in as +// interpolated bytes, so there's bound to be a lot of slop +// from that. We've got a free slot in c35.z, so we'll use that. +// A better implementation would be to bias and scale our screen +// vert, effectively pushing the vert toward the camera without +// actually moving it, but this is easier and might work just +// as well. +add r6.z, r6.z, c35.z; + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c25; // HACKAGE +//mov r6.w, c16.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c4.x; +mul oFog, r10.x, c4.y; +mov oPos, r9; + + +// Dyna Stuff +// Constants +// c33 = fC1U, fC2U, fC1V, fC2V +// c34 = fInitAtten, t, life, 1.f / (life-decay) +// c35 = ramp, 1.f / ramp, BIAS (positive is up), FREE +// +// Vertex Info +// v7.z = fBirth (because we don't use it for anything else). +// +// Initialize r1.zw to 0,1 +mov r1, c16.xxxz; +// Calc r1.x = age, r1.y = atten +// age = t - birth. +sub r1.x, c34.y, v7.z; +// atten = clamp0_1(age / ramp) * clamp0_1((life-age) / (life-decay)); +// first clamp0_1(age/ramp) +mul r1.y, r1.x, c35.y; +min r1.y, r1.y, c16.z; // Clamp to one (can't go negative). +// now clamp0_1((life-age) / (life-decay)); +sub r1.z, c34.z, r1.x; +mul r1.z, r1.z, c34.w; +min r1.z, r1.z, c16.z; // Clamp to one +max r1.z, r1.z, c16.x; // Clamp to zero +mul r1.y, r1.y, r1.z; // atten is the product of the two terms. + +// color is (atten, atten, atten, 1.f) +// Need to calculate opacity we would have had from vs_WaveFixedFin6.inl +// Right now that's just modulating by r4.y. +mul r0.y, r4.y, c34.x; +mul oD0, r0.yyyy, r1.yyyw; +//mov oD0, c16.zzzz; // HACKTEST + +// UVW = (inUVW - 0.5) * scale + 0.5 +// where: +// scale = (fC1U / (age * fC2U + 1.f)), fC1V / (age * fC2U + 1.f), 1.f, 1.f +mov r2, c16.xxxz; +mul r2.xy, r1.xx, c33.yw; +add r2.xy, r2.xy, c16.zz; +rcp r2.x, r2.x; +rcp r2.y, r2.y; +mul r2.xy, r2.xy, c33.xz; +sub r1.xy, v7.xy, c16.yy; +mul r1.xy, r1.xy, r2.xy; +add r1.xy, r1.xy, c16.yy; +mov oT0, r1; + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveRip7.inl b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveRip7.inl new file mode 100644 index 00000000..c0ee5b68 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ShaderSrc/vs_WaveRip7.inl @@ -0,0 +1,226 @@ + +vs.1.1 + +dcl_position v0 +dcl_color v5 +dcl_texcoord0 v7 + +// Store our input position in world space in r6 +m4x3 r6, v0, c25; // v0 * l2w +// Fill out our w (m4x3 doesn't touch w). +mov r6.w, c16.z; + +// + +// Input diffuse v5 color is: +// v5.r = overall transparency +// v5.g = reflection strength (transparency) +// v5.b = overall wave scaling +// +// v5.a is: +// v5.w = 1/(2.f * edge length) +// So per wave filtering is: +// min(max( (waveLen * v5.wwww) - 1), 0), 1.f); +// So a wave effect starts dying out when the wave is 4 times the sampling frequency, +// and is completely filtered at 2 times sampling frequency. + +// We'd like to make this autocalculated based on the depth of the water. +// The frequency filtering (v5.w) still needs to be calculated offline, because +// it's dependent on edge length, but the first 3 filterings can be calculated +// based on this vertex. +// Basically, we want the transparency, reflection strength, and wave scaling +// to go to zero as the water depth goes to zero. Linear falloffs are as good +// a place to start as any. +// +// depth = waterlevel - r6.z => depth in feet (may be negative) +// depthNorm = depth / depthFalloff => zero at watertable, one at depthFalloff beneath +// atten = minAtten + depthNorm * (maxAtten - minAtten); +// These are all vector ops. +// This provides separate ramp ups for each of the channels (they reach full unfiltered +// values at different depths), but doesn't provide separate controls for where they +// go to zero (they all go to zero at zero depth). For that we need an offset. An offset +// in feet (depth) is probably the most intuitive. So that changes the first calculation +// of depth to: +// depth = waterlevel - r6.z + offset +// = (waterlevel + offset) - r6.z +// And since we only need offsets for 3 channels, we can make the waterlevel constant +// waterlevel[chan] = watertableheight + offset[chan], +// with waterlevel.w = watertableheight. +// +// So: +// c30 = waterlevel + offset +// c31 = (maxAtten - minAtten) / depthFalloff +// c32 = minAtten. +// And in particular: +// c30.w = waterlevel +// c31.w = 1.f; +// c32.w = 0; +// So r4.w is the depth of this vertex in feet. + +// Dot our position with our direction vectors. +mul r0, c8, r6.xxxx; +mad r0, c9, r6.yyyy, r0; + +// +// dist = mad( dist, kFreq.xyzw, kPhase.xyzw); +mul r0, r0, c5; +add r0, r0, c6; +// +// // Now we need dist mod'd into range [-Pi..Pi] +// dist *= rcp(kTwoPi); +rcp r4, c15.wwww; +add r0, r0, c15.zzzz; +mul r0, r0, r4; +// dist = frac(dist); +expp r1.y, r0.xxxx +mov r1.x, r1.yyyy +expp r1.y, r0.zzzz +mov r1.z, r1.yyyy +expp r1.y, r0.wwww +mov r1.w, r1.yyyy +expp r1.y, r0.yyyy +// dist *= kTwoPi; +mul r0, r1, c15.wwww; +// dist += -kPi; +sub r0, r0, c15.zzzz; + +// +// sincos(dist, sinDist, cosDist); +// sin = r0 + r0^3 * vSin.y + r0^5 * vSin.z +// cos = 1 + r0^2 * vCos.y + r0^4 * vCos.z +mul r1, r0, r0; // r0^2 +mul r2, r1, r0; // r0^3 - probably stall +mul r3, r1, r1; // r0^4 +mul r4, r1, r2; // r0^5 +mul r5, r2, r3; // r0^7 + +mul r1, r1, c14.yyyy; // r1 = r0^2 * vCos.y +mad r2, r2, c13.yyyy, r0; // r2 = r0 + r0^3 * vSin.y +add r1, r1, c14.xxxx; // r1 = 1 + r0^2 * vCos.y +mad r2, r4, c13.zzzz, r2; // r2 = r0 + r0^3 * vSin.y + r0^5 * vSin.z +mad r1, r3, c14.zzzz, r1; // r1 = 1 + r0^2 * vCos.y + r0^4 * vCos.z + +// r0^7 & r0^6 terms +mul r4, r4, r0; // r0^6 +mad r2, r5, c13.wwww, r2; +mad r1, r4, c14.wwww, r1; + +// Calc our depth based filtering here into r4 (because we don't use it again +// after here, and we need our filtering shortly). +sub r4, c30, r6.zzzz; +mul r4, r4, c31; +add r4, r4, c32; +// Clamp .xyz to range [0..1] +min r4.xyz, r4, c16.zzzz; +max r4.xyz, r4, c16.xxxx; +//mov r4.xyz, c16.xxx; // HACKTEST + +// Calc our filter (see above). +mul r11, v5.wwww, c29; +max r11, r11, c16.xxxx; +min r11, r11, c16.zzzz; + +//mov r2, r1; +// r2 == sinDist +// r1 == cosDist +// sinDist *= filter; +mul r2, r2, r11; +// sinDist *= kAmplitude.xyzw +mul r2, r2, c7; +// height = dp4(sinDist, kOne); +// accumPos.z += height; (but accumPos.z is currently 0). +dp4 r8.x, r2, c16.zzzz; +mul r8.y, r8.x, r4.z; +add r8.z, r8.y, c30.w; +max r6.z, r6.z, r8.z; +// r8.x == wave height relative to 0 +// r8.y == dampened wave relative to 0 +// r8.z == dampened wave height in world space +// r6.z == wave height clamped to never go beneath ground level +// +// cosDist *= filter; +mul r1, r1, r11; + +// Pos = (in.x + S, in.y + R, r6.z) +// S = sum(k Dir.x A cos()) +// R = sum(k Dir.y A cos()) +// c10 = k Dir.x A +// c11 = k Dir.y A +// S = sum(cosDist * c10); +dp4 r7.x, r1, c10; +// R = sum(cosDist * c11); +dp4 r7.y, r1, c11; + +add r6.xy, r6.xy, r7.xy; + + +// Bias our vert up a bit to compensate for precision errors. +// In particular, our filter coefficients are coming in as +// interpolated bytes, so there's bound to be a lot of slop +// from that. We've got a free slot in c35.z, so we'll use that. +// A better implementation would be to bias and scale our screen +// vert, effectively pushing the vert toward the camera without +// actually moving it, but this is easier and might work just +// as well. +add r6.z, r6.z, c35.z; + +// +// // Transform position to screen +// +// +//m4x3 r6, v0, c25; // HACKAGE +//mov r6.w, c16.z; // HACKAGE +//m4x4 oPos, r6, c0; // ADDFOG +m4x4 r9, r6, c0; +add r10.x, r9.w, c4.x; +mul oFog, r10.x, c4.y; +mov oPos, r9; + + +// Dyna Stuff +// Constants +// c33 = fC1U, fC2U, fC1V, fC2V +// c34 = fInitAtten, t, life, 1.f / (life-decay) +// c35 = ramp, 1.f / ramp, BIAS (positive is up), FREE +// +// Vertex Info +// v7.z = fBirth (because we don't use it for anything else). +// +// Initialize r1.zw to 0,1 +mov r1, c16.xxxz; +// Calc r1.x = age, r1.y = atten +// age = t - birth. +sub r1.x, c34.y, v7.z; +// atten = clamp0_1(age / ramp) * clamp0_1((life-age) / (life-decay)); +// first clamp0_1(age/ramp) +mul r1.y, r1.x, c35.y; +min r1.y, r1.y, c16.z; // Clamp to one (can't go negative). +// now clamp0_1((life-age) / (life-decay)); +sub r1.z, c34.z, r1.x; +mul r1.z, r1.z, c34.w; +min r1.z, r1.z, c16.z; // Clamp to one +max r1.z, r1.z, c16.x; // Clamp to zero +mul r1.y, r1.y, r1.z; // atten is the product of the two terms. + +// color is (atten, atten, atten, 1.f) +// Need to calculate opacity we would have had from vs_WaveFixedFin7.inl +// Right now that's just modulating by r4.y. +mul r0.y, r4.y, c34.x; +mul oD0, r0.yyyy, r1.yyyw; +//mov oD0, c16.zzzz; // HACKTEST + +// UVW = (inUVW - 0.5) * scale + 0.5 +// where: +// scale = (fC1U / (age * fC2U + 1.f)), fC1V / (age * fC2U + 1.f), 1.f, 1.f +mov r2, c16.xxxz; +mul r2.xy, r1.xx, c33.yw; +add r2.xy, r2.xy, c16.zz; +rcp r2.x, r2.x; +rcp r2.y, r2.y; +mul r2.xy, r2.xy, c33.xz; +sub r1.xy, v7.xy, c16.yy; +mul r1.xy, r1.xy, r2.xy; +add r1.xy, r1.xy, c16.yy; +mov oT0, r1; + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.cpp new file mode 100644 index 00000000..babcd1a8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.cpp @@ -0,0 +1,322 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsGMaterial.h" +#include + +#include "hsTypes.h" +#include "hsMemory.h" +//#include "../plGeometry/hsTriangle3.h" +#include "hsResMgr.h" +#include "plLayerInterface.h" +#include "plLayer.h" +#include "../plMessage/plMatRefMsg.h" +#include "plProfile.h" + +plProfile_CreateTimer("MaterialAnims", "Animation", MaterialAnims); + +plLayer defaultLayer; +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + +hsGMaterial::hsGMaterial() : +fLOD(0), +fCompFlags(0), +fLoadFlags(0), +fLastUpdateTime(0) +{ +} + +hsGMaterial::~hsGMaterial() +{ + IClearLayers(); +} + +plLayerInterface* hsGMaterial::GetPiggyBack(UInt32 which) +{ + return fPiggyBacks[which]; +} + +plLayerInterface* hsGMaterial::GetLayer(UInt32 which) +{ + return fLayers[which]; +} + +UInt32 hsGMaterial::IMakeExtraLayer() +{ + fLayers.ExpandAndZero(GetNumLayers()+1); + return fLayers.GetCount(); +} + +void hsGMaterial::IClearLayers() +{ + fLayers.Reset(); +} + +void hsGMaterial::SetNumLayers(int cnt) +{ + if( cnt < fLayers.GetCount() ) + fLayers.SetCount(cnt); + else + fLayers.ExpandAndZero(cnt); +} + +hsGMaterial* hsGMaterial::Clone() +{ + hsGMaterial* clo = CloneNoLayers(); + + clo->SetNumLayers(GetNumLayers()); + + int i; + for( i = 0; i < GetNumLayers(); i++ ) + clo->SetLayer(fLayers[i], i); + + return clo; +} + +hsGMaterial* hsGMaterial::CloneNoLayers() +{ + hsGMaterial* clo = TRACKED_NEW hsGMaterial; + + clo->fCompFlags = fCompFlags; + clo->fLoadFlags = fLoadFlags; + + return clo; +} + +plLayer* hsGMaterial::MakeBaseLayer() +{ + plLayer* newLay = TRACKED_NEW plLayer; + newLay->InitToDefault(); + IClearLayers(); + + hsAssert(GetKey(), "All materials need a key (or temp key)"); + + char buff[256]; + if( GetKey()->GetName() ) + sprintf(buff, "%s_%s", GetKey()->GetName(), "Layer"); + else + strcpy(buff, "Layer"); + hsgResMgr::ResMgr()->NewKey( buff, newLay, GetKey() != nil ? GetKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc ); + + // Add layer so we have it now. + AddLayerViaNotify(newLay); + + return newLay; +} + +UInt32 hsGMaterial::AddLayerViaNotify(plLayerInterface* layer) +{ + int idx = GetNumLayers(); + + // Add via notify so we'll dispose of it properly later. + plMatRefMsg* msg = TRACKED_NEW plMatRefMsg(GetKey(), plRefMsg::kOnRequest, idx, plMatRefMsg::kLayer); + hsgResMgr::ResMgr()->SendRef(layer->GetKey(), msg, plRefFlags::kActiveRef); + + fLayers.SetCount(idx+1); + fLayers[idx] = layer; + + return idx; +} + +void hsGMaterial::ReplaceLayer(plLayerInterface* oldLay, plLayerInterface* newLay, hsBool piggyBack) +{ + hsTArray& layers = piggyBack ? fPiggyBacks : fLayers; + int i; + for( i = 0; i < layers.GetCount(); i++ ) + { + if( layers[i] == oldLay ) + break; + } + hsAssert(i < layers.GetCount(), "Replacing a layer we don't have"); + if( i >= layers.GetCount() ) + return; + SetLayer(newLay, i, piggyBack); +} + +void hsGMaterial::RemoveLayer(plLayerInterface* lay, hsBool piggyBack) +{ + hsTArray& layers = piggyBack ? fPiggyBacks : fLayers; + int i; + for( i = 0; i < layers.GetCount(); i++ ) + { + if( layers[i] == lay ) + break; + } + + if (i >= layers.GetCount()) + return; + + layers.Remove(i); +} + +void hsGMaterial::InsertLayer(plLayerInterface* layer, Int32 which, hsBool piggyBack) +{ + hsTArray& layers = piggyBack ? fPiggyBacks : fLayers; + hsAssert(which <= layers.GetCount(), "Material layers Exceeding test depth"); + layers.InsertAtIndex(which, layer); +} + +void hsGMaterial::SetLayer(plLayerInterface* layer, Int32 which, hsBool insert, hsBool piggyBack) +{ + if( insert ) + { + InsertLayer(layer, which, piggyBack); + } + else + { + hsTArray& layers = piggyBack ? fPiggyBacks : fLayers; + if( which < 0 ) + which = layers.GetCount(); + hsAssert(which <= layers.GetCount(), "Material layers Exceeding test depth"); + if( which < layers.GetCount() ) + layers[which] = layer; + else + layers.Append(layer); + } +} + + +void hsGMaterial::Write(hsStream* s) +{ + s->WriteSwap32(fLoadFlags); + s->WriteSwap32(fCompFlags); + + s->WriteSwap32(GetNumLayers()); + s->WriteSwap32(GetNumPiggyBacks()); +} + +void hsGMaterial::Read(hsStream* s) +{ + fLoadFlags = s->ReadSwap32(); + fCompFlags = s->ReadSwap32(); + + IClearLayers(); + int n = s->ReadSwap32(); + fLayers.SetCountAndZero(n); + n = s->ReadSwap32(); + fPiggyBacks.SetCountAndZero(n); +} + +void hsGMaterial::Write(hsStream *stream, hsResMgr *group) +{ + plSynchedObject::Write(stream, group); + + Write(stream); + + // Write one (or many) texture indices + int iLay; + for( iLay = 0; iLay < GetNumLayers(); iLay++ ) + { + group->WriteKey(stream,GetLayer(iLay)); + } + for( iLay = 0; iLay < GetNumPiggyBacks(); iLay++ ) + { + group->WriteKey(stream, GetPiggyBack(iLay)); + } +} + +void hsGMaterial::Read(hsStream *stream, hsResMgr *group) +{ + plSynchedObject::Read(stream, group); + + Read(stream); + + int iLay; + // Assign texture(s) + for (iLay = 0; iLay < GetNumLayers(); iLay++) + { + plMatRefMsg* msg = TRACKED_NEW plMatRefMsg(GetKey(), plRefMsg::kOnCreate, iLay, plMatRefMsg::kLayer); + plKey key = group->ReadKeyNotifyMe(stream, msg, plRefFlags::kActiveRef); + } + for (iLay = 0; iLay < GetNumPiggyBacks(); iLay++) + { + plMatRefMsg* msg = TRACKED_NEW plMatRefMsg(GetKey(), plRefMsg::kOnCreate, iLay, plMatRefMsg::kPiggyBack); + plKey key = group->ReadKeyNotifyMe(stream, msg, plRefFlags::kActiveRef); + } +} + +void hsGMaterial::Eval(double secs, UInt32 frame) +{ + plProfile_BeginLap(MaterialAnims, GetKeyName()); + + int i; + for( i = 0; i < GetNumLayers(); i++ ) + { + if( fLayers[i] ) + fLayers[i]->Eval(secs, frame, 0); + } + for( i = 0; i < GetNumPiggyBacks(); i++ ) + { + if( fPiggyBacks[i] ) + fPiggyBacks[i]->Eval(secs, frame, 0); + } + + plProfile_EndLap(MaterialAnims, GetKeyName()); +} + +void hsGMaterial::Reset() +{ + int i; + for( i = 0; i < GetNumLayers(); i++ ) + { + if( fLayers[i] ) + fLayers[i]->Eval(0, 0, 0); + } +} + +void hsGMaterial::Init() +{ + Reset(); +} + +hsBool hsGMaterial::MsgReceive(plMessage* msg) +{ + plMatRefMsg* refMsg = plMatRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + int which = refMsg->fWhich; + hsBool piggyBack = 0 != (refMsg->fType & plMatRefMsg::kPiggyBack); + plLayerInterface* lay= plLayerInterface::ConvertNoRef(refMsg->GetRef()); + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest) ) + { + hsBool insert = 0 != (refMsg->fType & plMatRefMsg::kInsert); + SetLayer(lay, which, + insert, + piggyBack ); + } + else if( refMsg->GetContext() & plRefMsg::kOnReplace ) + ReplaceLayer(plLayerInterface::ConvertNoRef(refMsg->GetOldRef()), lay, piggyBack); + else if( refMsg->GetContext() & (plRefMsg::kOnRemove | plRefMsg::kOnDestroy) ) + RemoveLayer(lay, piggyBack); + else + ReplaceLayer(lay, &defaultLayer, piggyBack); + return true; + } + return plSynchedObject::MsgReceive(msg); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.h new file mode 100644 index 00000000..54f128f8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/hsGMaterial.h @@ -0,0 +1,130 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef hsGCompMatDefined +#define hsGCompMatDefined + +#include "hsTemplates.h" +#include "../pnNetCommon/plSynchedObject.h" +#include "hsGMatState.h" +#include "hsColorRGBA.h" + +class hsScene; +class hsResMgr; +class hsG3DDevice; +class plLayerInterface; +class plLayer; + +// inlines for Texture and Material after class declarations + +class hsGMaterial : public plSynchedObject +{ +public: + // Things we have to know that some layer has + enum hsGCompFlags { + kCompShaded = 0x1, + kCompEnvironMap = 0x2, + kCompProjectOnto = 0x4, + kCompSoftShadow = 0x8, + kCompSpecular = 0x10, + kCompTwoSided = 0x20, + kCompDrawAsSplats = 0x40, + kCompAdjusted = 0x80, + kCompNoSoftShadow = 0x100, + kCompDynamic = 0x200, + kCompDecal = 0x400, + kCompIsEmissive_OBSOLETE = 0x800, + kCompIsLightMapped = 0x1000, + kCompNeedsBlendChannel = 0x2000 // For materials that have extra layers to simulate vtx alpha + }; + enum UpdateFlags + { + kUpdateAgain = 0x01 + }; + +protected: + UInt32 fLOD; + hsTArray fLayers; + hsTArray fPiggyBacks; + + UInt32 fCompFlags; + UInt32 fLoadFlags; + + hsScalar fLastUpdateTime; + + void IClearLayers(); + UInt32 IMakeExtraLayer(); + + void InsertLayer(plLayerInterface* lay, Int32 which = 0, hsBool piggyBack = false); + void SetLayer(plLayerInterface* lay, Int32 which = 0, hsBool insert=false, hsBool piggyBack=false); + void ReplaceLayer(plLayerInterface* oldLay, plLayerInterface* newLay, hsBool piggyBack = false); + void RemoveLayer(plLayerInterface* oldLay, hsBool piggyBack = false); +public: + hsGMaterial(); + ~hsGMaterial(); + + virtual hsGMaterial* Clone(); + virtual hsGMaterial* CloneNoLayers(); // For things like blending copies, that manipulate layers directly. + // copies no keyed objects. + plLayer* MakeBaseLayer(); + plLayerInterface* GetLayer(UInt32 which); + plLayerInterface* GetPiggyBack(UInt32 which); + UInt32 AddLayerViaNotify(plLayerInterface* lay); + UInt32 GetNumLayers() const { return fLayers.GetCount(); } + void SetNumLayers(int cnt); + UInt32 GetNumPiggyBacks() const { return fPiggyBacks.GetCount(); } + void SetNumPiggyBacks(); + + void SetLOD(UInt32 l) { fLOD = l; } + UInt32 GetLOD() const { return fLOD; } + + void SetCompositeFlags(UInt32 f) { fCompFlags = f; } // normally composite flags are calculated internally, not set. + UInt32 GetCompositeFlags() const { return fCompFlags; } + UInt32 GetLoadFlags() const { return fLoadFlags; } + + hsScalar GetLastUpdateTime() const { return fLastUpdateTime; } + void SetLastUpdateTime(hsScalar f) { fLastUpdateTime = f; } + hsBool IShouldUpdate(hsScalar secs, UInt32 flags) { return GetLastUpdateTime() != secs || (flags & kUpdateAgain); } + + hsBool IsDynamic() const { return (fCompFlags & kCompDynamic); } + hsBool IsDecal() const { return (fCompFlags & kCompDecal); } + hsBool NeedsBlendChannel() { return (fCompFlags & kCompNeedsBlendChannel); } + + virtual void Read(hsStream* s); + virtual void Write(hsStream* s); + virtual void Read(hsStream* s, hsResMgr *group); + virtual void Write(hsStream* s, hsResMgr *group); + + virtual void Eval(double secs, UInt32 frame); + virtual void Reset(); + virtual void Init(); + + CLASSNAME_REGISTER( hsGMaterial ); + GETINTERFACE_ANY( hsGMaterial, hsKeyedObject ); + + virtual hsBool MsgReceive(plMessage* msg); +}; + +#endif // hsGCompMatDefined diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plGrassShaderMod.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plGrassShaderMod.cpp new file mode 100644 index 00000000..1602e0c9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plGrassShaderMod.cpp @@ -0,0 +1,259 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plGrassShaderMod.h" + +#include "hsTimer.h" +#include "hsResMgr.h" +#include "plgDispatch.h" + +#include "../pnKeyedObject/plUoid.h" + +//#include "../pnSceneObject/plDrawInterface.h" + +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plTimeMsg.h" +#include "../plMessage/plMatRefMsg.h" +#include "../plMessage/plAgeLoadedMsg.h" +#include "../plMessage/plLayRefMsg.h" + +#include "../plDrawable/plAccessGeometry.h" +#include "../plDrawable/plAccessSpan.h" +#include "../plDrawable/plAccessVtxSpan.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plShader.h" +#include "../plSurface/plLayer.h" + +void plGrassWave::Write(hsStream *s) +{ + s->WriteSwapScalar(fDistX); + s->WriteSwapScalar(fDistY); + s->WriteSwapScalar(fDistZ); + s->WriteSwapScalar(fDirX); + s->WriteSwapScalar(fDirY); + s->WriteSwapScalar(fSpeed); +} + +void plGrassWave::Read(hsStream *s) +{ + fDistX = s->ReadSwapScalar(); + fDistY = s->ReadSwapScalar(); + fDistZ = s->ReadSwapScalar(); + fDirX = s->ReadSwapScalar(); + fDirY = s->ReadSwapScalar(); + fSpeed = s->ReadSwapScalar(); +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +plGrassShaderMod::~plGrassShaderMod() +{ + plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey()); + plgDispatch::Dispatch()->UnRegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->UnRegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); +} + +void plGrassShaderMod::ResetWaves() +{ + int i; + for (i = 0; i < kNumWaves; i++) + { + fWaves[i].fDistX = 0.F; + fWaves[i].fDistY = 0.F; + fWaves[i].fDistZ = 0.F; + fWaves[i].fDirX = 0.F; + fWaves[i].fDirY = 0.F; + fWaves[i].fSpeed = 0.F; + } + RefreshWaves(); +} + +void plGrassShaderMod::RefreshWaves() +{ + IRefreshWaves(fVShader); +} + +void plGrassShaderMod::IRefreshWaves(plShader *vShader) +{ + // Dynamic params, set by artist + vShader->SetVector(plGrassVS::kWaveDistX, fWaves[0].fDistX, fWaves[1].fDistX, fWaves[2].fDistX, fWaves[3].fDistX); + vShader->SetVector(plGrassVS::kWaveDistY, fWaves[0].fDistY, fWaves[1].fDistY, fWaves[2].fDistY, fWaves[3].fDistY); + vShader->SetVector(plGrassVS::kWaveDistZ, fWaves[0].fDistZ, fWaves[1].fDistZ, fWaves[2].fDistZ, fWaves[3].fDistZ); + vShader->SetVector(plGrassVS::kWaveDirX, fWaves[0].fDirX, fWaves[1].fDirX, fWaves[2].fDirX, fWaves[3].fDirX); + vShader->SetVector(plGrassVS::kWaveDirY, fWaves[0].fDirY, fWaves[1].fDirY, fWaves[2].fDirY, fWaves[3].fDirY); + vShader->SetVector(plGrassVS::kWaveSpeed, fWaves[0].fSpeed, fWaves[1].fSpeed, fWaves[2].fSpeed, fWaves[3].fSpeed); +} + +void plGrassShaderMod::AddTarget(plSceneObject *object) +{ + fTarget = object; +} + +void plGrassShaderMod::RemoveTarget(plSceneObject *object) +{ + fTarget = nil; +} + +hsBool plGrassShaderMod::MsgReceive(plMessage *msg) +{ + plGenRefMsg* refMsg = plGenRefMsg::ConvertNoRef(msg); + if (refMsg) + { + if (refMsg->GetContext() & (plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace)) + { + switch (refMsg->fType) + { + case kRefGrassVS: + fVShader = plShader::ConvertNoRef(refMsg->GetRef()); + break; + case kRefGrassPS: + fPShader = plShader::ConvertNoRef(refMsg->GetRef()); + break; + case kRefMaterial: + fMaterial = hsGMaterial::ConvertNoRef(refMsg->GetRef()); + break; + default: + break; + } + } + else + { + switch (refMsg->fType) + { + case kRefGrassVS: + fVShader = nil; + break; + case kRefGrassPS: + fPShader = nil; + break; + case kRefMaterial: + fMaterial = nil; + break; + default: + break; + } + } + return true; + } + + plAgeLoadedMsg* ageLoaded = plAgeLoadedMsg::ConvertNoRef(msg); + if( (ageLoaded && ageLoaded->fLoaded) || plInitialAgeStateLoadedMsg::ConvertNoRef(msg) ) + { + ISetupShaders(); + return true; + } + + return plModifier::MsgReceive(msg); +} + +void plGrassShaderMod::Write(hsStream *stream, hsResMgr *mgr) +{ + plModifier::Write(stream, mgr); + + mgr->WriteKey(stream, fMaterial ? fMaterial->GetKey() : nil); + + int i; + for (i = 0; i < kNumWaves; i++) + fWaves[i].Write(stream); +} + +void plGrassShaderMod::Read(hsStream *stream, hsResMgr *mgr) +{ + plModifier::Read(stream, mgr); + + mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefMaterial), plRefFlags::kActiveRef); + + int i; + for (i = 0; i < kNumWaves; i++) + fWaves[i].Read(stream); + + plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); +} + +hsBool plGrassShaderMod::IEval(double secs, hsScalar del, UInt32 dirty) +{ + if (fVShader) + { + fVShader->SetVector(plGrassVS::kAppConsts, float(hsTimer::GetSysSeconds()), 0.f, 0.f, 0.f); + } + + return TRUE; +} + +void plGrassShaderMod::ISetupShaders() +{ + if (!fVShader) + { + plShader* vShader = TRACKED_NEW plShader; + char buff[256]; + sprintf(buff, "%s_GrassVS", GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(buff, vShader, GetKey()->GetUoid().GetLocation()); + vShader->SetIsPixelShader(false); + vShader->SetInputFormat(1); + vShader->SetOutputFormat(0); + + vShader->SetNumConsts(plGrassVS::kNumConsts); + vShader->SetVector(plGrassVS::kNumericConsts, 0.f, 0.5f, 1.f, 2.f); + vShader->SetVector(plGrassVS::kPiConsts, 1.f / (8.f*hsScalarPI*4.f*4.f), hsScalarPI/2.f, hsScalarPI, hsScalarPI*2.f); + vShader->SetVector(plGrassVS::kSinConsts, -1.f/6.f, 1.f/120.f, -1.f/5040.f, 1.f/362880.f); + + IRefreshWaves(vShader); + + vShader->SetNumPipeConsts(1); + vShader->SetPipeConst(0, plPipeConst::kLocalToNDC, plGrassVS::kLocalToNDC); + + vShader->SetDecl(plShaderTable::Decl(plShaderID::vs_GrassShader)); + hsgResMgr::ResMgr()->SendRef(vShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefGrassVS), plRefFlags::kActiveRef); + } + + if (!fPShader) + { + plShader* pShader = TRACKED_NEW plShader; + char buff[256]; + sprintf(buff, "%s_GrassPS", GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(buff, pShader, GetKey()->GetUoid().GetLocation()); + pShader->SetIsPixelShader(true); + pShader->SetNumConsts(0); + pShader->SetInputFormat(0); + pShader->SetOutputFormat(0); + pShader->SetDecl(plShaderTable::Decl(plShaderID::ps_GrassShader)); + hsgResMgr::ResMgr()->SendRef(pShader->GetKey(), TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, 0, kRefGrassPS), plRefFlags::kActiveRef); + } + + plLayer* layer = plLayer::ConvertNoRef(fMaterial->GetLayer(0)->BottomOfStack()); + if (layer && (layer->GetVertexShader() != fVShader)) + { + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kVertexShader); + hsgResMgr::ResMgr()->SendRef(fVShader->GetKey(), refMsg, plRefFlags::kActiveRef); + } + if (layer && (layer->GetPixelShader() != fPShader)) + { + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kPixelShader); + hsgResMgr::ResMgr()->SendRef(fPShader->GetKey(), refMsg, plRefFlags::kActiveRef); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plGrassShaderMod.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plGrassShaderMod.h new file mode 100644 index 00000000..e8374a3f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plGrassShaderMod.h @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLGRASSSHADERMOD_INC +#define PLGRASSSHADERMOD_INC + +#include "../pnModifier/plModifier.h" + +class plSceneObject; +class hsGMaterial; +class plShader; + +class plGrassWave +{ +public: + plGrassWave() : fDistX(0.F), fDistY(0.F), fDistZ(0.F), fDirX(0.F), fDirY(0.F), fSpeed(0.F) {} + + hsScalar fDistX; + hsScalar fDistY; + hsScalar fDistZ; + hsScalar fDirX; + hsScalar fDirY; + hsScalar fSpeed; + + void Write(hsStream *s); + void Read(hsStream *s); +}; + +class plGrassShaderMod : public plModifier +{ +public: + plGrassShaderMod() : fTarget(nil), fMaterial(nil), fVShader(nil), fPShader(nil) {} + ~plGrassShaderMod(); + + void ResetWaves(); + void RefreshWaves(); + + virtual int GetNumTargets() const { return fTarget ? 1 : 0; } + virtual plSceneObject* GetTarget(int w) const { return fTarget; } + virtual void AddTarget(plSceneObject *object); + virtual void RemoveTarget(plSceneObject *object); + + virtual hsBool MsgReceive(plMessage *msg); + + virtual void Write(hsStream *stream, hsResMgr *mgr); + virtual void Read(hsStream *stream, hsResMgr *mgr); + + CLASSNAME_REGISTER( plGrassShaderMod ); + GETINTERFACE_ANY( plGrassShaderMod, plModifier ); + + enum { + kRefGrassVS, + kRefGrassPS, + kRefMaterial, + }; + + enum { + kNumWaves = 4, + }; + + plGrassWave fWaves[kNumWaves]; + +protected: + virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty); + virtual void IApplyDynamic() {}; // dummy function required by base class + void ISetupShaders(); + void IRefreshWaves(plShader *vShader); + + plSceneObject *fTarget; + hsGMaterial *fMaterial; + plShader *fVShader; + plShader *fPShader; +}; + +namespace plGrassVS +{ + enum { + kLocalToNDC = 0, + kNumericConsts = 4, + kAppConsts = 5, + kPiConsts = 6, + kSinConsts = 7, + kWaveDistX = 8, + kWaveDistY = 9, + kWaveDistZ = 10, + kWaveDirX = 11, + kWaveDirY = 12, + kWaveSpeed = 13, + + kNumConsts = 14, + }; +}; + +#endif // PLGRASSSHADERMOD \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayer.cpp new file mode 100644 index 00000000..2e0c5e59 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayer.cpp @@ -0,0 +1,293 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLayer.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "hsMatrix44.h" +#include "hsGMatState.inl" +#include "../plMessage/plLayRefMsg.h" +#include "../plGImage/plBitmap.h" +#include "../plPipeline/hsGDeviceRef.h" +#include "plShader.h" + +#include "plPipeline.h" +#include "plgDispatch.h" +#include "../pnMessage/plPipeResMakeMsg.h" + +plLayer::plLayer() +{ + fOwnedChannels = kTransform + | kPreshadeColor + | kRuntimeColor + | kAmbientColor + | kOpacity + | kState + | kUVWSrc + | kLODBias + | kSpecularColor + | kSpecularPower + | kTexture + | kVertexShader + | kPixelShader + | kBumpEnvXfm; + + fTransform = TRACKED_NEW hsMatrix44; + fTransform->Reset(); + + fPreshadeColor = TRACKED_NEW hsColorRGBA; + fRuntimeColor = TRACKED_NEW hsColorRGBA; + fAmbientColor = TRACKED_NEW hsColorRGBA; + fSpecularColor = TRACKED_NEW hsColorRGBA; + fOpacity = TRACKED_NEW hsScalar; + + fState = TRACKED_NEW hsGMatState; + fState->Reset(); + + fUVWSrc = TRACKED_NEW UInt32; + fLODBias = TRACKED_NEW hsScalar; + fSpecularPower = TRACKED_NEW hsScalar; + + fTexture = TRACKED_NEW plBitmap*; + *fTexture = nil; + + fVertexShader = TRACKED_NEW plShader*; + *fVertexShader = nil; + + fPixelShader = TRACKED_NEW plShader*; + *fPixelShader = nil; + + fBumpEnvXfm = TRACKED_NEW hsMatrix44; + fBumpEnvXfm->Reset(); +} + +plLayer::~plLayer() +{ +} + +UInt32 plLayer::Eval(double secs, UInt32 frame, UInt32 ignore) +{ + return UInt32(0); +} + +void plLayer::Read(hsStream* s, hsResMgr* mgr) +{ + plLayerInterface::Read(s, mgr); + + fState->Read(s); + + fTransform->Read(s); + fPreshadeColor->Read(s); + fRuntimeColor->Read( s ); + fAmbientColor->Read(s); + fSpecularColor->Read( s ); + + *fUVWSrc = s->ReadSwap32(); + *fOpacity = s->ReadSwapScalar(); + *fLODBias = s->ReadSwapScalar(); + *fSpecularPower = s->ReadSwapScalar(); + + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture); + mgr->ReadKeyNotifyMe(s,refMsg, plRefFlags::kActiveRef); + +#if 1 // For read/write shaders + refMsg = TRACKED_NEW plLayRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kVertexShader); + mgr->ReadKeyNotifyMe(s,refMsg, plRefFlags::kActiveRef); + + refMsg = TRACKED_NEW plLayRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kPixelShader); + mgr->ReadKeyNotifyMe(s,refMsg, plRefFlags::kActiveRef); + + fBumpEnvXfm->Read(s); +#endif // For read/write shaders +} + +void plLayer::Write(hsStream* s, hsResMgr* mgr) +{ + plLayerInterface::Write(s, mgr); + + fState->Write(s); + + fTransform->Write(s); + fPreshadeColor->Write(s); + fRuntimeColor->Write( s ); + fAmbientColor->Write(s); + fSpecularColor->Write( s ); + + s->WriteSwap32(*fUVWSrc); + s->WriteSwapScalar(*fOpacity); + s->WriteSwapScalar(*fLODBias); + s->WriteSwapScalar(*fSpecularPower); + + + mgr->WriteKey(s, GetTexture()); + mgr->WriteKey(s, GetVertexShader()); + mgr->WriteKey(s, GetPixelShader()); + + fBumpEnvXfm->Write(s); +} + +hsBool plLayer::MsgReceive(plMessage* msg) +{ + plLayRefMsg* refMsg = plLayRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + switch( refMsg->fType ) + { + case plLayRefMsg::kTexture: + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + plBitmap *tex = plBitmap::ConvertNoRef(refMsg->GetRef()); + *fTexture = tex; + if( tex ) + plgDispatch::Dispatch()->RegisterForExactType(plPipeTexMakeMsg::Index(), GetKey()); + else + plgDispatch::Dispatch()->UnRegisterForExactType(plPipeTexMakeMsg::Index(), GetKey()); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + *fTexture = nil; + plgDispatch::Dispatch()->UnRegisterForExactType(plPipeTexMakeMsg::Index(), GetKey()); + } + } + return true; + case plLayRefMsg::kVertexShader: + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + plShader* shader = plShader::ConvertNoRef(refMsg->GetRef()); + *fVertexShader = shader; + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + *fVertexShader = nil; + } + } + return true; + case plLayRefMsg::kPixelShader: + { + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + plShader* shader = plShader::ConvertNoRef(refMsg->GetRef()); + *fPixelShader = shader; + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + *fPixelShader = nil; + } + } + return true; + } + } + plPipeTexMakeMsg* texMake = plPipeTexMakeMsg::ConvertNoRef(msg); + if( texMake ) + { + texMake->Pipeline()->CheckTextureRef(this); + return true; + } + return plLayerInterface::MsgReceive(msg); +} + +void plLayer::SetState(const hsGMatState& s) +{ + *fState = s; +} + +void plLayer::SetTransform(const hsMatrix44& xfm) +{ + *fTransform = xfm; +} + +void plLayer::SetBumpEnvMatrix(const hsMatrix44& xfm) +{ + *fBumpEnvXfm = xfm; +} + + +plLayer& plLayer::InitToDefault() +{ + fState->Reset(); + + *fTexture = nil; + + SetRuntimeColor(hsColorRGBA().Set(0.5f, 0.5f, 0.5f, 1.f)); + SetPreshadeColor(hsColorRGBA().Set(0.5f, 0.5f, 0.5f, 1.f)); + SetAmbientColor(hsColorRGBA().Set(0,0,0,1.f)); + SetOpacity(1.f); + + fTransform->Reset(); + + SetUVWSrc(0); + SetLODBias(-1.f); + SetSpecularColor( hsColorRGBA().Set(0,0,0,1.f)); + SetSpecularPower(1.f); + + *fVertexShader = nil; + *fPixelShader = nil; + + fBumpEnvXfm->Reset(); + + return *this; +} + + +plLayerInterface* plLayer::DefaultLayer() +{ + static plLayer defLayer; + defLayer.InitToDefault(); + return &defLayer; +} + +//// CloneNoTexture /////////////////////////////////////////////////////////// +// Copies all the fields from the original layer given, not including the +// texture + +void plLayer::CloneNoTexture( plLayerInterface *original ) +{ + SetBlendFlags( original->GetBlendFlags() ); + SetClampFlags( original->GetClampFlags() ); + SetShadeFlags( original->GetShadeFlags() ); + SetZFlags( original->GetZFlags() ); + SetMiscFlags( original->GetMiscFlags() ); + SetState( original->GetState() ); + + SetPreshadeColor( original->GetPreshadeColor() ); + SetRuntimeColor( original->GetRuntimeColor() ); + SetAmbientColor( original->GetAmbientColor() ); + SetSpecularColor( original->GetSpecularColor() ); + SetOpacity( original->GetOpacity() ); + SetTransform( original->GetTransform() ); + SetUVWSrc( original->GetUVWSrc() ); + SetLODBias( original->GetLODBias() ); + SetSpecularPower( original->GetSpecularPower() ); + + SetVertexShader( original->GetVertexShader() ); + SetPixelShader( original->GetPixelShader() ); + SetBumpEnvMatrix( original->GetBumpEnvMatrix() ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayer.h new file mode 100644 index 00000000..a6374e9b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayer.h @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLayer_inc +#define plLayer_inc + +#include "hsTemplates.h" +#include "plLayerInterface.h" + +class plLayer : public plLayerInterface +{ +protected: +public: + plLayer(); + virtual ~plLayer(); + + CLASSNAME_REGISTER( plLayer ); + GETINTERFACE_ANY( plLayer, plLayerInterface ); + + virtual UInt32 Eval(double secs, UInt32 frame, UInt32 ignore); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + + // Flat layer specifics + plLayer& InitToDefault(); + + void SetBlendFlags(UInt32 f) { fState->fBlendFlags = f; } + void SetClampFlags(UInt32 f) { fState->fClampFlags = f; } + void SetShadeFlags(UInt32 f) { fState->fShadeFlags = f; } + void SetZFlags(UInt32 f) { fState->fZFlags = f; } + void SetMiscFlags(UInt32 f) { fState->fMiscFlags = f; } + void SetState(const hsGMatState& state); + + void SetTexture(plBitmap* t) { *fTexture = t; } + + void SetPreshadeColor(const hsColorRGBA& col) { *fPreshadeColor = col; } + void SetRuntimeColor( const hsColorRGBA& col ) { *fRuntimeColor = col; } + void SetAmbientColor(const hsColorRGBA& col) { *fAmbientColor = col; } + void SetSpecularColor(const hsColorRGBA& col) { *fSpecularColor = col; } + void SetOpacity(hsScalar a) { *fOpacity = a; } + void SetTransform(const hsMatrix44& xfm); + void SetUVWSrc(UInt32 chan) { *fUVWSrc = chan; } + void SetLODBias(hsScalar f) { *fLODBias = f; } + void SetSpecularPower(hsScalar f) { *fSpecularPower = f; } + + void SetVertexShader(plShader* shader) { *fVertexShader = shader; } + void SetPixelShader(plShader* shader) { *fPixelShader = shader; } + + void SetBumpEnvMatrix(const hsMatrix44& xfm); + + static plLayerInterface* DefaultLayer(); + + // Copies all the fields from the original layer given, not including the texture + void CloneNoTexture( plLayerInterface *original ); +}; + +#endif // plLayerInterfaceStack_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerAnimation.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerAnimation.cpp new file mode 100644 index 00000000..053e6aea --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerAnimation.cpp @@ -0,0 +1,742 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsTimer.h" +#include "plLayerAnimation.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plInterp/plController.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "../plMessage/plLinkToAgeMsg.h" +#include "../pnMessage/plSDLModifierMsg.h" +#include "../plModifier/plLayerSDLModifier.h" +#include "../pnMessage/plCameraMsg.h" +#include "../plNetClient/plLinkEffectsMgr.h" +#include "plgDispatch.h" +#include "hsResMgr.h" +#include "../plModifier/plSDLModifier.h" +#include "../plSDL/plSDL.h" +#include "../pnMessage/plSDLNotificationMsg.h" +#include "../plMessage/plAvatarMsg.h" + +plLayerAnimationBase::plLayerAnimationBase() +: + fPreshadeColorCtl(nil), + fRuntimeColorCtl(nil), + fAmbientColorCtl(nil), + fSpecularColorCtl(nil), + fOpacityCtl(nil), + fTransformCtl(nil), + fEvalTime(-1.0), + fCurrentTime(-1.f), + fSegmentID(nil) +{ +} + +plLayerAnimationBase::~plLayerAnimationBase() +{ + delete fPreshadeColorCtl; + delete fRuntimeColorCtl; + delete fAmbientColorCtl; + delete fSpecularColorCtl; + delete fOpacityCtl; + delete fTransformCtl; + delete [] fSegmentID; +} + +void plLayerAnimationBase::Read(hsStream* s, hsResMgr* mgr) +{ + plLayerInterface::Read(s, mgr); + + fPreshadeColorCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fRuntimeColorCtl = plController::ConvertNoRef( mgr->ReadCreatable( s ) ); + fAmbientColorCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fSpecularColorCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fOpacityCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + fTransformCtl = plController::ConvertNoRef(mgr->ReadCreatable(s)); + + if( fOpacityCtl ) + { + fOwnedChannels |= kOpacity; + fOpacity = TRACKED_NEW hsScalar; + } + if( fPreshadeColorCtl ) + { + fOwnedChannels |= kPreshadeColor; + fPreshadeColor = TRACKED_NEW hsColorRGBA; + } + if( fRuntimeColorCtl ) + { + fOwnedChannels |= kRuntimeColor; + fRuntimeColor = TRACKED_NEW hsColorRGBA; + } + if( fAmbientColorCtl ) + { + fOwnedChannels |= kAmbientColor; + fAmbientColor = TRACKED_NEW hsColorRGBA; + } + if( fSpecularColorCtl ) + { + fOwnedChannels |= kSpecularColor; + fSpecularColor = TRACKED_NEW hsColorRGBA; + } + if( fTransformCtl ) + { + fOwnedChannels |= kTransform; + fTransform = TRACKED_NEW hsMatrix44; + } + fLength = IMakeUniformLength(); +} + +void plLayerAnimationBase::Write(hsStream* s, hsResMgr* mgr) +{ + plLayerInterface::Write(s, mgr); + + mgr->WriteCreatable(s, fPreshadeColorCtl); + mgr->WriteCreatable(s, fRuntimeColorCtl); + mgr->WriteCreatable(s, fAmbientColorCtl); + mgr->WriteCreatable(s, fSpecularColorCtl); + mgr->WriteCreatable(s, fOpacityCtl); + mgr->WriteCreatable(s, fTransformCtl); +} + +plLayerInterface* plLayerAnimationBase::Attach(plLayerInterface* prev) +{ + return plLayerInterface::Attach(prev); +} + +void plLayerAnimationBase::IEvalConvertedTime(hsScalar secs, UInt32 passChans, UInt32 evalChans, UInt32 &dirty) +{ + if( evalChans & kPreshadeColor ) + { + fPreshadeColorCtl->Interp(fCurrentTime, fPreshadeColor); + dirty |= kPreshadeColor; + } + else if( passChans & kPreshadeColor ) + { + *fPreshadeColor = fUnderLay->GetPreshadeColor(); + } + + if( evalChans & kRuntimeColor ) + { + fRuntimeColorCtl->Interp( fCurrentTime, fRuntimeColor ); + dirty |= kRuntimeColor; + } + else if( passChans & kRuntimeColor ) + { + *fRuntimeColor = fUnderLay->GetRuntimeColor(); + } + + if( evalChans & kAmbientColor ) + { + fAmbientColorCtl->Interp(fCurrentTime, fAmbientColor); + dirty |= kAmbientColor; + } + else if( passChans & kAmbientColor ) + { + *fAmbientColor = fUnderLay->GetAmbientColor(); + } + + if( evalChans & kSpecularColor ) + { + fSpecularColorCtl->Interp( fCurrentTime, fSpecularColor ); + dirty |= kSpecularColor; + } + else if( passChans & kSpecularColor ) + { + *fSpecularColor = fUnderLay->GetSpecularColor(); + } + + if( evalChans & kOpacity ) + { + fOpacityCtl->Interp(fCurrentTime, fOpacity); + *fOpacity *= 1.e-2f; + dirty |= kOpacity; + } + else if( passChans & kOpacity ) + { + *fOpacity = fUnderLay->GetOpacity(); + } + + if( evalChans & kTransform ) + { + fTransformCtl->Interp(fCurrentTime, fTransform); + dirty |= kTransform; + } + else if( passChans & kTransform ) + { + *fTransform = fUnderLay->GetTransform(); + } + fPassThruChannels = 0; // already handled, don't need to keep passing them through. +} + +hsBool plLayerAnimationBase::MsgReceive(plMessage* msg) +{ + return plLayerInterface::MsgReceive(msg); +} + +void plLayerAnimationBase::SetPreshadeColorCtl(plController* colCtl) +{ + if( fPreshadeColorCtl ) + delete fPreshadeColorCtl; + else + fPreshadeColor = TRACKED_NEW hsColorRGBA; + + fOwnedChannels |= kPreshadeColor; + fPreshadeColorCtl = colCtl; +} + +void plLayerAnimationBase::SetRuntimeColorCtl(plController* colCtl) +{ + if( fRuntimeColorCtl ) + delete fRuntimeColorCtl; + else + fRuntimeColor = TRACKED_NEW hsColorRGBA; + + fOwnedChannels |= kRuntimeColor; + fRuntimeColorCtl = colCtl; +} + +void plLayerAnimationBase::SetAmbientColorCtl(plController* ambCtl) +{ + if( fAmbientColorCtl ) + delete fAmbientColorCtl; + else + fAmbientColor = TRACKED_NEW hsColorRGBA; + + fOwnedChannels |= kAmbientColor; + fAmbientColorCtl = ambCtl; +} + +void plLayerAnimationBase::SetSpecularColorCtl(plController* ambCtl) +{ + if( fSpecularColorCtl ) + delete fSpecularColorCtl; + else + fSpecularColor = TRACKED_NEW hsColorRGBA; + + fOwnedChannels |= kSpecularColor; + fSpecularColorCtl = ambCtl; +} + +void plLayerAnimationBase::SetOpacityCtl(plController* opaCtl) +{ + if( fOpacityCtl ) + delete fOpacityCtl; + else + fOpacity = TRACKED_NEW hsScalar; + + fOwnedChannels |= kOpacity; + fOpacityCtl = opaCtl; +} + +void plLayerAnimationBase::SetTransformCtl(plController* xfmCtl) +{ + if( fTransformCtl ) + delete fTransformCtl; + else + fTransform = TRACKED_NEW hsMatrix44; + + fOwnedChannels |= kTransform; + fTransformCtl = xfmCtl; +} + +hsScalar plLayerAnimationBase::IMakeUniformLength() +{ + fLength = 0; + if( fPreshadeColorCtl && (fPreshadeColorCtl->GetLength() > fLength) ) + fLength = fPreshadeColorCtl->GetLength(); + if( fRuntimeColorCtl && (fRuntimeColorCtl->GetLength() > fLength) ) + fLength = fRuntimeColorCtl->GetLength(); + if( fAmbientColorCtl && (fAmbientColorCtl->GetLength() > fLength) ) + fLength = fAmbientColorCtl->GetLength(); + if( fSpecularColorCtl && (fSpecularColorCtl->GetLength() > fLength) ) + fLength = fSpecularColorCtl->GetLength(); + if( fOpacityCtl && (fOpacityCtl->GetLength() > fLength) ) + fLength = fOpacityCtl->GetLength(); + if( fTransformCtl && (fTransformCtl->GetLength() > fLength) ) + fLength = fTransformCtl->GetLength(); + + return fLength; +} + +///////////////////////////////////////////////////////////////////////////////// + +plLayerAnimation::plLayerAnimation() +: + plLayerAnimationBase(), + fLayerSDLMod(nil) +{ + fTimeConvert.SetOwner(this); +} + +plLayerAnimation::~plLayerAnimation() +{ + delete fLayerSDLMod; +} + +void plLayerAnimation::Read(hsStream* s, hsResMgr* mgr) +{ + plLayerAnimationBase::Read(s, mgr); + + fTimeConvert.Read(s, mgr); + if (!(fTimeConvert.IsStopped())) + { + plSynchEnabler ps(true); // enable dirty tracking so that we send state about + // the anim resetting to start now. + fTimeConvert.SetCurrentAnimTime(0, true); + } + Eval(hsTimer::GetSysSeconds(),0,0); + + // add sdl modifier + delete fLayerSDLMod; + fLayerSDLMod = TRACKED_NEW plLayerSDLModifier; + fLayerSDLMod->SetLayerAnimation(this); +} + + +void plLayerAnimation::Write(hsStream* s, hsResMgr* mgr) +{ + plLayerAnimationBase::Write(s, mgr); + + fTimeConvert.Write(s, mgr); +} + +plLayerInterface* plLayerAnimation::Attach(plLayerInterface* prev) +{ + fCurrentTime = fTimeConvert.CurrentAnimTime()-1.f; + + return plLayerAnimationBase::Attach(prev); +} + +UInt32 plLayerAnimation::Eval(double wSecs, UInt32 frame, UInt32 ignore) +{ + UInt32 dirty = plLayerInterface::Eval(wSecs, frame, ignore); + if( wSecs != fEvalTime ) + { + UInt32 evalChans = 0; + UInt32 passChans = dirty | fPassThruChannels; + hsScalar secs = fTimeConvert.WorldToAnimTime(wSecs); + if( secs != fCurrentTime ) + { + evalChans = fOwnedChannels & ~ignore & ~fPassThruChannels; + fCurrentTime = secs; + } + + IEvalConvertedTime(secs, passChans, evalChans, dirty); + } + fEvalTime = wSecs; + return dirty; +} + +hsBool plLayerAnimation::MsgReceive(plMessage* msg) +{ + // pass sdl msg to sdlMod + plSDLModifierMsg* sdlMsg = plSDLModifierMsg::ConvertNoRef(msg); + if (sdlMsg && fLayerSDLMod) + { + if (fLayerSDLMod->MsgReceive(sdlMsg)) + return true; // msg handled + } + + hsBool retVal = false; + plAnimCmdMsg* cmdMsg = plAnimCmdMsg::ConvertNoRef(msg); + if( cmdMsg ) + { + // Evaluate first, so we'll be transitioning from our + // real current state, whether we've been evaluated (in view) + // lately or not. + TopOfStack()->Eval(hsTimer::GetSysSeconds(), 0, 0); + retVal = fTimeConvert.HandleCmd(cmdMsg); + DirtySynchState(kSDLLayer, 0); + } + + if( retVal ) + { + if( !fTimeConvert.IsStopped() || fTimeConvert.GetFlag(plAnimTimeConvert::kForcedMove) ) + { + ClaimChannels(fOwnedChannels); + fCurrentTime = -1.f; // force an eval + } + } + else + { + retVal = plLayerAnimationBase::MsgReceive(msg); + } + + return retVal; +} + +void plLayerAnimation::DefaultAnimation() +{ + IMakeUniformLength(); + fTimeConvert.SetBegin(0); + fTimeConvert.SetEnd(fLength); + fTimeConvert.SetLoopPoints(0,fLength); + fTimeConvert.Loop(); + fTimeConvert.Start(); +} + +/////////////////////////////////////////////////////////////////////////////////////// + +plLayerLinkAnimation::plLayerLinkAnimation() : + fLinkKey(nil), + fLeavingAge(true), + fEnabled(true), + fFadeFlags(0), + fLastFadeFlag(0), + fFadeFlagsDirty(false) +{ + fIFaceCallback = TRACKED_NEW plEventCallbackMsg(); + fIFaceCallback->fEvent = kTime; + fIFaceCallback->fRepeats = 0; +} + +plLayerLinkAnimation::~plLayerLinkAnimation() +{ + hsRefCnt_SafeUnRef(fIFaceCallback); +} + + +void plLayerLinkAnimation::Read(hsStream* s, hsResMgr* mgr) +{ + plLayerAnimation::Read(s, mgr); + + fLinkKey = mgr->ReadKey(s); + fLeavingAge = s->ReadBool(); + plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectBCMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectPrepBCMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plCameraTargetFadeMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plAvatarStealthModeMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plIfaceFadeAvatarMsg::Index(), GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plPseudoLinkAnimTriggerMsg::Index(), GetKey()); + + fIFaceCallback->AddReceiver(GetKey()); +} + + +void plLayerLinkAnimation::Write(hsStream* s, hsResMgr* mgr) +{ + plLayerAnimation::Write(s, mgr); + + mgr->WriteKey(s, fLinkKey); + s->WriteBool(fLeavingAge); +} + +UInt32 plLayerLinkAnimation::Eval(double wSecs, UInt32 frame, UInt32 ignore) +{ + UInt32 dirty = plLayerInterface::Eval(wSecs, frame, ignore); + if (wSecs != fEvalTime) + { + UInt32 evalChans = 0; + UInt32 passChans = dirty | fPassThruChannels; + hsScalar oldAnimTime = fTimeConvert.CurrentAnimTime(); + hsScalar secs = oldAnimTime; + + if (fFadeFlagsDirty) + { + hsScalar goal = 0.f; + + if (fFadeFlags & kFadeLinkPrep) + secs = goal = fLength; + else + { + hsScalar rate = 0.f; + hsScalar delta = (hsScalar)(wSecs - fEvalTime); + + if (fFadeFlags & kFadeLinking) + { + goal = fLength; + rate = 1.f; + } + else if (fFadeFlags & kFadeCamera) + { + goal = fLength; + rate = 10.f; + } + else if (fFadeFlags & (kFadeIFace | kFadeCCR)) + { + goal = fLength * 0.4f; + rate = 10.f; + } + else if (fFadeFlags == 0) + { + goal = 0.f; + if (fLastFadeFlag == kFadeLinking) + rate = 1.f; + else + rate = 10.f; + } + + if (fabs(oldAnimTime - goal) < delta * rate || rate == 0) + secs = goal; + else if (goal > oldAnimTime) + secs = oldAnimTime + delta * rate; + else + secs = oldAnimTime - delta * rate; + } + + if (secs == goal) + fFadeFlagsDirty = false; + } + + if( secs != fCurrentTime ) + { + fTimeConvert.SetCurrentAnimTime(secs); + if (secs == 0.f || oldAnimTime == 0.f) + { + // Either we're going opaque, or we were opaque and now we're fading. + // Tell the armature to re-eval its opacity settings. + plAvatarOpacityCallbackMsg *opacityMsg = TRACKED_NEW plAvatarOpacityCallbackMsg(fLinkKey, kStop); + opacityMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + opacityMsg->Send(); + } + evalChans = fOwnedChannels & ~ignore & ~fPassThruChannels; + fCurrentTime = secs; + } + IEvalConvertedTime(secs, passChans, evalChans, dirty); + } + fEvalTime = wSecs; + return dirty; +} + +void plLayerLinkAnimation::SetFadeFlag(UInt8 flag, hsBool val) +{ + if (val) + fFadeFlags |= flag; + else + fFadeFlags &= ~flag; + + if (fFadeFlags == 0) + fLastFadeFlag = flag; + + TopOfStack()->Eval(hsTimer::GetSysSeconds(), 0, 0); + ClaimChannels(fOwnedChannels); + fCurrentTime = -1; // force eval + fFadeFlagsDirty = true; +} + +hsBool plLayerLinkAnimation::MsgReceive( plMessage* pMsg ) +{ + plLinkEffectPrepBCMsg *bcpMsg = plLinkEffectPrepBCMsg::ConvertNoRef(pMsg); + if (bcpMsg != nil) + { + if (bcpMsg->fLinkKey != fLinkKey || bcpMsg->fLeavingAge) + return true; + + SetFadeFlag(kFadeLinkPrep, true); + return true; + } + + + plLinkEffectBCMsg *msg = plLinkEffectBCMsg::ConvertNoRef(pMsg); + if (msg != nil) + { + if (msg->fLinkKey == fLinkKey) + { + SetFadeFlag(kFadeLinkPrep, false); + if (msg->HasLinkFlag(plLinkEffectBCMsg::kLeavingAge)) + SetFadeFlag(kFadeLinking, true); + else + SetFadeFlag(kFadeLinking, false); + + if (msg->HasLinkFlag(plLinkEffectBCMsg::kSendCallback)) + { + plLinkEffectsMgr *mgr; + if (mgr = plLinkEffectsMgr::ConvertNoRef(msg->GetSender()->ObjectIsLoaded())) + mgr->WaitForEffect(msg->fLinkKey, fTimeConvert.GetEnd() - fTimeConvert.GetBegin()); + } + } + return true; + } + + plPseudoLinkAnimTriggerMsg* pSeudoMsg = plPseudoLinkAnimTriggerMsg::ConvertNoRef(pMsg); + if (pSeudoMsg) + { + if (fLinkKey != pSeudoMsg->fAvatarKey) + return true; + + if (pSeudoMsg->fForward) + SetFadeFlag(kFadeLinking, true); + else + SetFadeFlag(kFadeLinking, false); + + // add a callback for when it's done if it's in forward + plLinkEffectsMgr *mgr; + if (mgr = plLinkEffectsMgr::ConvertNoRef(pMsg->GetSender()->ObjectIsLoaded())) + if (pSeudoMsg->fForward) + mgr->WaitForPseudoEffect(fLinkKey, fTimeConvert.GetEnd() - fTimeConvert.GetBegin()); + + return true; + } + // used to fade the player in or out when entering / exiting first person mode + // or when distance between camera and player is too small... + plCameraTargetFadeMsg* fMsg = plCameraTargetFadeMsg::ConvertNoRef(pMsg); + if (fMsg) + { + if (fLinkKey != fMsg->GetSubjectKey()) + return true; + + if (fMsg->FadeOut()) + SetFadeFlag(kFadeCamera, true); + else + SetFadeFlag(kFadeCamera, false); + + return true; + } + plIfaceFadeAvatarMsg* iMsg = plIfaceFadeAvatarMsg::ConvertNoRef(pMsg); + if (iMsg) + { + if (fLinkKey != iMsg->GetSubjectKey()) + return true; + + if (iMsg->GetEnable()) + { + Enable(true); + } + else if (iMsg->GetDisable()) + { + Enable(false); // disable and un-fade + SetFadeFlag(kFadeIFace, false); + } + else + if (fEnabled) + { + if (iMsg->FadeOut()) + SetFadeFlag(kFadeIFace, true); + else + SetFadeFlag(kFadeIFace, false); + } + return true; + } + + plAvatarStealthModeMsg *sMsg = plAvatarStealthModeMsg::ConvertNoRef(pMsg); + if (sMsg) + { + if (sMsg->GetSender() == fLinkKey) + { + if (sMsg->fMode == plAvatarStealthModeMsg::kStealthCloakedButSeen) + { + SetFadeFlag(kFadeCCR, true); + } + else if (sMsg->fMode == plAvatarStealthModeMsg::kStealthVisible) + { + SetFadeFlag(kFadeCCR, false); + } + // Don't need to set opacity if we're fully cloaked, since we won't + // even be drawing the spans (due to plEnableMsg() on the sceneObject) + } + return true; + } + + return plLayerAnimation::MsgReceive( pMsg ); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +plLayerSDLAnimation::plLayerSDLAnimation() : plLayerAnimationBase(), fVar(nil), fVarName(nil) {} + +plLayerSDLAnimation::~plLayerSDLAnimation() +{ + delete [] fVarName; +} + +UInt32 plLayerSDLAnimation::Eval(double wSecs, UInt32 frame, UInt32 ignore) +{ + UInt32 dirty = plLayerInterface::Eval(wSecs, frame, ignore); + if( wSecs != fEvalTime ) + { + UInt32 evalChans = 0; + UInt32 passChans = dirty | fPassThruChannels; + + if (fEvalTime < 0) + { + if (fVarName != nil) + { + extern const plSDLModifier *ExternFindAgeSDL(); + const plSDLModifier *sdlMod = ExternFindAgeSDL(); + if (sdlMod) + { + fVar = sdlMod->GetStateCache()->FindVar(fVarName); + if (fVar) + sdlMod->AddNotifyForVar(GetKey(), fVarName, 0); + } + } + } + hsScalar secs; + if (fVar) + fVar->Get(&secs); + else + secs = 0.f; + + // We're guaranteed a 0-1 time. Scale that to our animation length. + secs *= GetLength(); + + if( secs != fCurrentTime ) + { + evalChans = fOwnedChannels & ~ignore & ~fPassThruChannels; + fCurrentTime = secs; + } + + IEvalConvertedTime(secs, passChans, evalChans, dirty); + } + fEvalTime = wSecs; + return dirty; +} + +hsBool plLayerSDLAnimation::MsgReceive(plMessage* msg) +{ + plSDLNotificationMsg* nMsg = plSDLNotificationMsg::ConvertNoRef(msg); + if (nMsg) + { + TopOfStack()->Eval(hsTimer::GetSysSeconds(), 0, 0); + ClaimChannels(fOwnedChannels); + return true; + } + + return plLayerAnimationBase::MsgReceive(msg); +} + +void plLayerSDLAnimation::Read(hsStream* s, hsResMgr* mgr) +{ + plLayerAnimationBase::Read(s, mgr); + + fVarName = s->ReadSafeString(); +} + +void plLayerSDLAnimation::Write(hsStream* s, hsResMgr* mgr) +{ + plLayerAnimationBase::Write(s, mgr); + + s->WriteSafeString(fVarName); +} + +void plLayerSDLAnimation::SetVarName(char *name) +{ + delete [] fVarName; + fVarName = hsStrcpy(name); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerAnimation.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerAnimation.h new file mode 100644 index 00000000..1bbd30eb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerAnimation.h @@ -0,0 +1,196 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLayerAnimation_inc +#define plLayerAnimation_inc + +#include "plLayerInterface.h" +#include "../plInterp/plAnimTimeConvert.h" + +class plMessage; +class plController; +class plLayerSDLModifier; +class plSimpleStateVariable; + +// LayerAnimations take advantage of the simplifying +// factor that they are write only. That is, it always +// overwrites the output of previous interfaces with +// the current value of the animation. Unanimated channels +// are unaffected. + +class plLayerAnimationBase : public plLayerInterface +{ +protected: + char* fSegmentID; + double fEvalTime; + hsScalar fCurrentTime; + hsScalar fLength; + + plController* fPreshadeColorCtl; + plController* fRuntimeColorCtl; + plController* fAmbientColorCtl; + plController* fSpecularColorCtl; + plController* fOpacityCtl; + plController* fTransformCtl; + + hsScalar IMakeUniformLength(); + void IEvalConvertedTime(hsScalar secs, UInt32 passChans, UInt32 evalChans, UInt32 &dirty); + +public: + plLayerAnimationBase(); + virtual ~plLayerAnimationBase(); + + CLASSNAME_REGISTER( plLayerAnimationBase ); + GETINTERFACE_ANY( plLayerAnimationBase, plLayerInterface ); + + virtual plLayerInterface* Attach(plLayerInterface* prev); + //virtual UInt32 Eval(double secs, UInt32 frame, UInt32 ignore) = 0; + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + // Specialized + hsScalar GetLength() const { return fLength; } + char *GetSegmentID() const { return fSegmentID; } + void SetSegmentID(char *ID) { delete fSegmentID; fSegmentID = hsStrcpy(ID); } + + // Export construction functions follow + void SetPreshadeColorCtl(plController* colCtl); + void SetRuntimeColorCtl( plController *colCtl ); + void SetAmbientColorCtl(plController* ambCtl); + void SetSpecularColorCtl(plController* ambCtl); + void SetOpacityCtl(plController* opaCtl); + void SetTransformCtl(plController* xfmCtl); + + plController* GetPreshadeColorCtl() const { return fPreshadeColorCtl; } + plController* GetRuntimeColorCtl() const { return fRuntimeColorCtl; } + plController* GetAmbientColorCtl() const { return fAmbientColorCtl; } + plController* GetSpecularColorCtl() const { return fSpecularColorCtl; } + plController* GetOpacityCtl() const { return fOpacityCtl; } + plController* GetTransformCtl() const { return fTransformCtl; } +}; + +class plLayerAnimation : public plLayerAnimationBase +{ + friend class plLayerSDLModifier; + +protected: + plAnimTimeConvert fTimeConvert; + plLayerSDLModifier* fLayerSDLMod; // handles sending/recving sdl state + +public: + plLayerAnimation(); + virtual ~plLayerAnimation(); + + CLASSNAME_REGISTER( plLayerAnimation ); + GETINTERFACE_ANY( plLayerAnimation, plLayerAnimationBase ); + + virtual plLayerInterface* Attach(plLayerInterface* prev); + virtual UInt32 Eval(double wSecs, UInt32 frame, UInt32 ignore); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + const plLayerSDLModifier* GetSDLModifier() const { return fLayerSDLMod; } + plAnimTimeConvert& GetTimeConvert() { return fTimeConvert; } + + void DefaultAnimation(); +}; + +class plLayerLinkAnimation : public plLayerAnimation +{ +protected: + plKey fLinkKey; + hsBool fEnabled; + plEventCallbackMsg *fIFaceCallback; + + enum + { + kFadeLinkPrep = 0x01, + kFadeLinking = 0x02, + kFadeCamera = 0x04, + kFadeIFace = 0x08, + kFadeCCR = 0x10, + }; + UInt8 fFadeFlags; + UInt8 fLastFadeFlag; + hsBool fFadeFlagsDirty; + +public: + plLayerLinkAnimation(); + ~plLayerLinkAnimation(); + + CLASSNAME_REGISTER( plLayerLinkAnimation ); + GETINTERFACE_ANY( plLayerLinkAnimation, plLayerAnimation ); + + void SetLinkKey(plKey linkKey) { fLinkKey = linkKey; } + plKey GetLinkKey() { return fLinkKey; } + + // NOTE: The link animation should NEVER NEVER NEVER send its state to the server. + // NEVER! + // If you think it should... talk to Bob. He will explain why it can't be, and beat you up. + // If he can't remember, beat him up until he does (or ask Moose). + virtual hsBool DirtySynchState(const char* sdlName, UInt32 sendFlags) { return false; } // don't send link state + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + virtual UInt32 Eval(double wSecs, UInt32 frame, UInt32 ignore); + virtual hsBool MsgReceive(plMessage* pMsg); + void Enable(hsBool b) { fEnabled = b; } + void SetFadeFlag(UInt8 flag, hsBool val); + + hsBool fLeavingAge; +}; + +class plLayerSDLAnimation : public plLayerAnimationBase +{ +protected: + plSimpleStateVariable *fVar; + char *fVarName; + +public: + plLayerSDLAnimation(); + virtual ~plLayerSDLAnimation(); + + CLASSNAME_REGISTER( plLayerSDLAnimation ); + GETINTERFACE_ANY( plLayerSDLAnimation, plLayerAnimationBase ); + + virtual UInt32 Eval(double wSecs, UInt32 frame, UInt32 ignore); + + virtual hsBool MsgReceive(plMessage* msg); + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + char *GetVarName() { return fVarName; } + void SetVarName(char *name); +}; + +#endif // plLayerAnimation_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerDepth.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerDepth.cpp new file mode 100644 index 00000000..69fc1a23 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerDepth.cpp @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLayerDepth.h" + +plLayerDepth::plLayerDepth() +{ + SetZFlags(hsGMatState::kZNoZRead | hsGMatState::kZNoZWrite); + SetBlendFlags(hsGMatState::kBlendAdd); + SetMiscFlags(hsGMatState::kMiscRestartPassHere | hsGMatState::kMiscTroubledLoner); + + SetPreshadeColor(hsColorRGBA().Set(0,0,0,1.f)); + SetRuntimeColor(hsColorRGBA().Set(0,0,0,1.f)); + SetAmbientColor(hsColorRGBA().Set(0.1f, 0.1f, 0.1f, 1.f)); + SetOpacity(1.f); + + *fTexture = nil; + +} + +plLayerDepth::~plLayerDepth() +{ +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerDepth.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerDepth.h new file mode 100644 index 00000000..94eab9ce --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerDepth.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLayerDepth_inc +#define plLayerDepth_inc + +#include "plLayer.h" + +class plLayerDepth : public plLayer +{ +public: + plLayerDepth(); + virtual ~plLayerDepth(); + + CLASSNAME_REGISTER( plLayerDepth ); + GETINTERFACE_ANY( plLayerDepth, plLayer ); + +}; + +#endif plLayerDepth_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerInterface.cpp new file mode 100644 index 00000000..96bfd557 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerInterface.cpp @@ -0,0 +1,359 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLayerInterface.h" +#include "../plMessage/plLayRefMsg.h" +#include "plLayer.h" +#include "hsMatrix44.h" +#include "hsGMatState.h" +#include "hsResMgr.h" +#include "../pnNetCommon/plSDLTypes.h" + +plLayerInterface::plLayerInterface() +: fUnderLay(nil), + fOverLay(nil), + fState(nil), + fTransform(nil), + fPreshadeColor(nil), + fRuntimeColor(nil), + fAmbientColor(nil), + fOpacity(nil), + fTexture(nil), + fUVWSrc(nil), + fLODBias(nil), + fSpecularColor(nil), + fSpecularPower(nil), + fOwnedChannels(0), + fPassThruChannels(0), + fVertexShader(nil), + fPixelShader(nil), + fBumpEnvXfm(nil) +{ + +} + +plLayerInterface::~plLayerInterface() +{ + if( fUnderLay ) + Detach(fUnderLay); + + delete fState; + delete fPreshadeColor; + delete fRuntimeColor; + delete fAmbientColor; + delete fSpecularColor; + delete fOpacity; + delete fTransform; + + delete fTexture; + + delete fUVWSrc; + delete fLODBias; + delete fSpecularPower; + + delete fVertexShader; + delete fPixelShader; + + delete fBumpEnvXfm; +} + +void plLayerInterface::ISetPassThru(UInt32 chans) +{ + fPassThruChannels |= chans; + if( fOverLay ) + fOverLay->ISetPassThru(chans); + + // Since plLayerAnimation is the only derived class that uses its + // fPassThruChannels info, it's the only one that actually saves + // it to state. + DirtySynchState(kSDLLayer, 0); +} + +// The arbitration rules for different layers on the same stack +// wanting to control the same channel are currently: +// 1) Only one write-only value setter can be active at a time, +// otherwise results are undefined. +// 2) A layer will only become active due to receiving a message. +// 3) A channel value for the stack is the value as set by the +// last layer that was active. If no layers have ever been +// active, the value is the static value of the bottom of the stack. +// 4) Since the stack is only Eval'd when visible, the third rule +// must appear to be true when different layers become active +// and inactive without ever having been Eval'd. +// 5) Taking advantage of rules 1) and 2), it follows that the last +// layer to have become active on response to a message is also +// the last layer to have been active. +// 6) So when a layer becomes active in it's MsgReceive(), it notifies +// all channels above it that it now owns its channels, and they +// should just pass through those channel values. +// Note that a layer may claim ownership of its channels but then lose +// ownership (because another layer went active) before ever having +// been Eval'd. +void plLayerInterface::ClaimChannels(UInt32 chans) +{ + if( fOverLay ) + fOverLay->ISetPassThru(chans); + fPassThruChannels &= ~chans; + DirtySynchState(kSDLLayer, 0); +} + +UInt32 plLayerInterface::Eval(double secs, UInt32 frame, UInt32 ignore) +{ + if( fUnderLay ) + return fUnderLay->Eval(secs, frame, ignore); + + return UInt32(0); +} + +// Export Only +void plLayerInterface::AttachViaNotify(plLayerInterface *prev) +{ + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kUnderLay); + hsgResMgr::ResMgr()->AddViaNotify(prev->GetKey(), refMsg, plRefFlags::kActiveRef); +} + +plLayerInterface* plLayerInterface::Attach(plLayerInterface* prev) +{ + if( !prev ) + return this; + + if( fUnderLay == prev ) + return this; + + if( fUnderLay ) + { + fUnderLay->Attach(prev); + prev = fUnderLay; + } + + if( !OwnChannel(kState) ) + fState = prev->fState; + + if( !OwnChannel(kPreshadeColor) ) + fPreshadeColor = prev->fPreshadeColor; + + if( !OwnChannel( kRuntimeColor ) ) + fRuntimeColor = prev->fRuntimeColor; + + if( !OwnChannel(kAmbientColor) ) + fAmbientColor = prev->fAmbientColor; + + if( !OwnChannel( kSpecularColor ) ) + fSpecularColor = prev->fSpecularColor; + + if( !OwnChannel(kOpacity) ) + fOpacity = prev->fOpacity; + + if( !OwnChannel(kTransform) ) + fTransform = prev->fTransform; + + if( !OwnChannel(kTexture) ) + fTexture = prev->fTexture; + + if( !OwnChannel(kUVWSrc) ) + fUVWSrc = prev->fUVWSrc; + + if( !OwnChannel(kLODBias) ) + fLODBias = prev->fLODBias; + + if( !OwnChannel(kSpecularPower) ) + fSpecularPower = prev->fSpecularPower; + + if( !OwnChannel(kVertexShader) ) + fVertexShader = prev->fVertexShader; + + if( !OwnChannel(kPixelShader) ) + fPixelShader = prev->fPixelShader; + + if( !OwnChannel(kBumpEnvXfm) ) + fBumpEnvXfm = prev->fBumpEnvXfm; + + fUnderLay = prev; + prev->fOverLay = this; + + return this; +} + +void plLayerInterface::IUnthread() +{ + if( fUnderLay ) + { + if( !OwnChannel(kState) ) + fState = nil; + if( !OwnChannel(kPreshadeColor) ) + fPreshadeColor = nil; + if( !OwnChannel( kRuntimeColor ) ) + fRuntimeColor = nil; + if( !OwnChannel(kAmbientColor) ) + fAmbientColor = nil; + if( !OwnChannel( kSpecularColor ) ) + fSpecularColor = nil; + if( !OwnChannel(kOpacity) ) + fOpacity = nil; + if( !OwnChannel(kTransform) ) + fTransform = nil; + if( !OwnChannel(kTexture) ) + fTexture = nil; + + if( !OwnChannel(kUVWSrc) ) + fUVWSrc = nil; + if( !OwnChannel(kLODBias) ) + fLODBias = nil; + if( !OwnChannel(kSpecularPower) ) + fSpecularPower = nil; + + if( !OwnChannel(kVertexShader) ) + fVertexShader = nil; + if( !OwnChannel(kPixelShader) ) + fPixelShader = nil; + + if( !OwnChannel(kBumpEnvXfm) ) + fBumpEnvXfm = nil; + + fUnderLay->fOverLay = nil; + fUnderLay = nil; + } +} + +// Detach: +// If we are the one being detached, break our links to underlay +// and then return nil, since everything has just been detached +// from the stack. +// If our underlay is the one being detached, we need to unthread from it +// and return ourselves. +// If it's not us, and not our underlay, just pass it to our underlay and let +// it deal. +// +// Return value is new TOP of stack. li is now top of a separate stack. +plLayerInterface* plLayerInterface::Detach(plLayerInterface* li) +{ + if( li == this ) + return nil; + + if( li == fUnderLay ) + { + IUnthread(); + return this; + } + + fUnderLay->Detach(li); + + return this; +} + +// Remove: +// If we are the one being removed, break our links to underlay +// and then just return underlay, since it doesn't even know +// about our existence (so it doesn't need to know about the remove). +// If our underlay is the one being removed, we need to unthread it from +// its underlay (if any), and then thread ourselves onto the underlay's +// former underlay. +// If it's not us, and not our underlay, just pass it to our underlay and let +// it deal. +// +// Return value is new TOP of stack. +plLayerInterface* plLayerInterface::Remove(plLayerInterface* li) +{ + plLayerInterface* under = fUnderLay; + + if( li == this ) + { + + IUnthread(); + + return under; + } + + // This is an error, because it means we're being asked + // to detach from something we aren't attached to. + if( !under ) + { + hsAssert(false, "Detaching from unknown layerinterface"); + return this; + } + + IUnthread(); + + plLayerInterface* newUnderLay = under->Remove(li); + + Attach(newUnderLay); + + return this; +} + +plLayerInterface *plLayerInterface::GetAttached() +{ + return fUnderLay; +} + +void plLayerInterface::Read(hsStream* s, hsResMgr* mgr) +{ + plSynchedObject::Read(s, mgr); + + plLayRefMsg* refMsg = TRACKED_NEW plLayRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kUnderLay); + plKey key = mgr->ReadKeyNotifyMe(s,refMsg, plRefFlags::kActiveRef); + if( key && !fUnderLay ) + Attach(plLayer::DefaultLayer()); + + // Temporary setting default netgroup by our key. + SetNetGroup(SelectNetGroup(GetKey())); +} + +void plLayerInterface::Write(hsStream* s, hsResMgr* mgr) +{ + plSynchedObject::Write(s, mgr); + + mgr->WriteKey(s, fUnderLay); +} + +hsBool plLayerInterface::MsgReceive(plMessage* msg) +{ + plLayRefMsg* refMsg = plLayRefMsg::ConvertNoRef(msg); + if( refMsg ) + { + switch( refMsg->fType ) + { + case plLayRefMsg::kUnderLay: + { + plLayerInterface* underLay = plLayerInterface::ConvertNoRef(refMsg->GetRef()); + if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) + { + if( fUnderLay ) + Detach(fUnderLay); + + Attach(underLay); + } + else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) + { + Detach(fUnderLay); + } + return true; + } + } + } + return plSynchedObject::MsgReceive(msg); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerInterface.h new file mode 100644 index 00000000..0b5fe064 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerInterface.h @@ -0,0 +1,204 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLayerInterface_inc +#define plLayerInterface_inc + +#include "../pnNetCommon/plSynchedValue.h" +#include "../pnNetCommon/plSynchedObject.h" +#include "hsGMatState.h" + +struct hsMatrix44; +struct hsColorRGBA; +class plBitmap; +class plMessage; +class hsGMatState; +class plShader; + +class plLayerInterface : public plSynchedObject +{ + friend class plLayerSDLModifier; +public: + enum plLayerDirtyBits { + kTransform = 0x1, + kPreshadeColor = 0x2, + kAmbientColor = 0x4, + kOpacity = 0x8, + kTexture = 0x10, + kState = 0x20, + kUVWSrc = 0x40, + kLODBias = 0x80, + kSpecularColor = 0x100, + kSpecularPower = 0x200, + kRuntimeColor = 0x400, + kVertexShader = 0x800, + kPixelShader = 0x1000, + kBumpEnvXfm = 0x2000, + + kAllDirty = 0xffffffff + }; + enum plUVWSrcModifiers { + kUVWPassThru = 0x00000000, + kUVWIdxMask = 0x0000ffff, + kUVWNormal = 0x00010000, + kUVWPosition = 0x00020000, + kUVWReflect = 0x00030000 + }; + +protected: + plLayerInterface* fUnderLay; + plLayerInterface* fOverLay; + + // NEVER MODIFY A FIELD THAT ISN'T + // YOUR OWN PERSONAL COPY. + // These are accessible so that if you're interface doesn't touch a field, + // it can set it's pointer to the previous interfaces pointer to that field + // and never even do a copy. For any fields this interface will alter, you + // need to alloc your own copy, and in your eval get the previous interface's + // value, modify it, and write it to your copy. + // So, if you don't touch a field, copy the pointer from prev into your channel pointer, + // else alloc your own and set the value to whatever you like. + // Then you only need to update your value when the source value changes (you'll know + // from dirty bits), or when you want to (you'll know from secs/frame). + + // fOwnedChannels specifies which channels you have allocated and own (and will delete) + UInt32 fOwnedChannels; + // fPassThruChannels are channels which we need to pass through our underlay's values, + // even if we have a differing opinion on what the value should be. This let's us arbitrate + // between different layers that control the same channels. A layer can claim control of + // a channel by telling all other layers to pass through that channel via the + // ClaimChannels(UInt32 chans) member function. See .cpp for arbitration rules. + UInt32 fPassThruChannels; + + hsMatrix44* fTransform; + hsColorRGBA* fPreshadeColor; + hsColorRGBA* fRuntimeColor; // Diffuse color to be used with runtime lights vs. static preshading + hsColorRGBA* fAmbientColor; + hsColorRGBA* fSpecularColor; + hsScalar* fOpacity; + + // Would like to abstract out the mipmap, but we'll bring it + // along for now. + plBitmap** fTexture; + + // (Currently) unanimatables. + hsGMatState* fState; + UInt32* fUVWSrc; + hsScalar* fLODBias; + hsScalar* fSpecularPower; + + plShader** fVertexShader; + plShader** fPixelShader; + + hsMatrix44* fBumpEnvXfm; + + void IUnthread(); + void ISetPassThru(UInt32 chans); + +public: + plLayerInterface(); + virtual ~plLayerInterface(); + + CLASSNAME_REGISTER( plLayerInterface ); + GETINTERFACE_ANY( plLayerInterface, plSynchedObject ); + + plLayerInterface* BottomOfStack() { return fUnderLay ? fUnderLay->BottomOfStack() : this; } + plLayerInterface* TopOfStack() { return fOverLay ? fOverLay->TopOfStack() : this; } + + // Used by debug code. + plLayerInterface* GetUnderLay() { return fUnderLay; } + plLayerInterface* GetOverLay() { return fOverLay; } + + const hsMatrix44& GetTransform() const { return *fTransform; } + const hsColorRGBA& GetPreshadeColor() const { return *fPreshadeColor; } + const hsColorRGBA& GetRuntimeColor() const { return *fRuntimeColor; } + const hsColorRGBA& GetAmbientColor() const { return *fAmbientColor; } + const hsColorRGBA& GetSpecularColor() const { return *fSpecularColor; } + hsScalar GetOpacity() const { return *fOpacity; } + + plBitmap* GetTexture() const { return *fTexture; } + + // (Currently) unanimatables + UInt32 GetUVWSrc() const { return *fUVWSrc; } + hsScalar GetLODBias() const { return *fLODBias; } + hsScalar GetSpecularPower() const { return *fSpecularPower; } + + const hsGMatState& GetState() const { return *fState; } + UInt32 GetBlendFlags() const { return fState->fBlendFlags; } + UInt32 GetClampFlags() const { return fState->fClampFlags; } + UInt32 GetShadeFlags() const { return fState->fShadeFlags; } + UInt32 GetZFlags() const { return fState->fZFlags; } + UInt32 GetMiscFlags() const { return fState->fMiscFlags; } + + plShader* GetVertexShader() const { return *fVertexShader; } + plShader* GetPixelShader() const { return *fPixelShader; } + + const hsMatrix44& GetBumpEnvMatrix() const { return *fBumpEnvXfm; } + + // ClaimChannels will tell every other layer on this stack (besides this) to + // pass through the value, giving this layer the final say on it's value + void ClaimChannels(UInt32 chans); + + // Eval may be called multiple times per frame, or even multiple times per render (for multiple + // renders per frame). The burden of deciding whether any update is necessary falls to the + // derived interface, but here's some info to go on. + // secs - world time. Time dependent effects (like time of day) look mostly at this. + // frame - incremented each time the camera moves. View dependent effects look at this. + // ignore - fields marked ignore will be overwritten (not modified) by an downstream layer, so don't bother computing. + // return value of fUnderLay->Eval() - bits are true for fields that an interface earlier in the chain dirtied. A field + // flagged dirty that you modify (as opposed to overwrite) should be updated regardless of secs and frame. + // + virtual UInt32 Eval(double secs, UInt32 frame, UInt32 ignore); + + // Attach gives you a chance to decide whether you want to pass through fields from prev (by copying + // the pointers which you then sooner put long pins through your own eyes than modify). Alloc + // your own fields before Attach, and you can play with them at will. Base class will pass through + // (via pointer copy) all nil fields. Detach nils out any fields that are just pass through, and + // unthreads the requested layer from the stack, returning new top-of-stack. + // + // Given two stacks A->B and C->D, A->Attach(C) makes A->B->C->D + virtual plLayerInterface* Attach(plLayerInterface* prev); + // Given stack A->B->C->D, A->Detach(C) gives two stacks, A->B and C->D (returned value is A) + // If A == C (A->B->C && A->Remove(A)), it returns nil, since the it's removed A from the stack, + // so the two stacks are now nil and A->B->C + virtual plLayerInterface* Detach(plLayerInterface* nuke); + // Given stack A->B->C->D, A->Remove(C) gives two stacks, A->B->D and C. It returns the stack with C removed. + // If A==C (A->B->C && A->Remove(A)), it returns B->C. + virtual plLayerInterface* Remove(plLayerInterface* nuke); + + plLayerInterface* GetAttached(); + void AttachViaNotify(plLayerInterface *prev); // Export only + + hsBool OwnChannel(UInt32 which) const { return 0 != (fOwnedChannels & which); } + + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + virtual hsBool MsgReceive(plMessage* msg); + +}; + +#endif // plLayerInterface_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerMultiply.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerMultiply.cpp new file mode 100644 index 00000000..852d9020 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerMultiply.cpp @@ -0,0 +1,168 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plLayerMultiply.h" + +plLayerMultiply::plLayerMultiply() : fDirtyChannels(0), fSrcOpacity(1.f) +{ + fSrcPreshadeColor.Set(1.f, 1.f, 1.f, 1.f); + fSrcRuntimeColor.Set(1.f, 1.f, 1.f, 1.f); + fSrcAmbientColor.Set(1.f, 1.f, 1.f, 1.f); + fSrcTransform.Reset(); +} + +plLayerMultiply::~plLayerMultiply() +{ +} + +void plLayerMultiply::Read(hsStream* s, hsResMgr* mgr) +{ + plLayerInterface::Read(s, mgr); + + fOwnedChannels = s->ReadSwap32(); + if (fOwnedChannels & kOpacity) + { + fOpacity = TRACKED_NEW hsScalar; + *fOpacity = fSrcOpacity = s->ReadSwapScalar(); + fDirtyChannels |= kOpacity; + } + + if (fOwnedChannels & kPreshadeColor) + { + fPreshadeColor = TRACKED_NEW hsColorRGBA; + fSrcPreshadeColor.Read(s); + *fPreshadeColor = fSrcPreshadeColor; + fDirtyChannels |= kPreshadeColor; + } + + if (fOwnedChannels & kRuntimeColor) + { + fRuntimeColor = TRACKED_NEW hsColorRGBA; + fSrcRuntimeColor.Read(s); + *fRuntimeColor = fSrcRuntimeColor; + fDirtyChannels |= kRuntimeColor; + } + + if (fOwnedChannels & kAmbientColor) + { + fAmbientColor = TRACKED_NEW hsColorRGBA; + fSrcAmbientColor.Read(s); + *fAmbientColor = fSrcAmbientColor; + fDirtyChannels |= kAmbientColor; + } + + if (fOwnedChannels & kTransform) + { + fTransform = TRACKED_NEW hsMatrix44; + fSrcTransform.Read(s); + *fTransform = fSrcTransform; + fDirtyChannels |= kTransform; + } +} + +void plLayerMultiply::Write(hsStream* s, hsResMgr* mgr) +{ + plLayerInterface::Write(s, mgr); + + s->WriteSwap32(fOwnedChannels); + if (fOwnedChannels & kOpacity) + s->WriteSwapScalar(fSrcOpacity); + + if (fOwnedChannels & kPreshadeColor) + fSrcPreshadeColor.Write(s); + + if (fOwnedChannels & kRuntimeColor) + fSrcRuntimeColor.Write(s); + + if (fOwnedChannels & kAmbientColor) + fSrcAmbientColor.Write(s); + + if (fOwnedChannels & kTransform) + fSrcTransform.Write(s); +} + +plLayerInterface* plLayerMultiply::Attach(plLayerInterface* prev) +{ + return plLayerInterface::Attach(prev); +} + +UInt32 plLayerMultiply::Eval(double wSecs, UInt32 frame, UInt32 ignore) +{ + UInt32 dirtyChannels = fDirtyChannels | plLayerInterface::Eval(wSecs, frame, ignore); + UInt32 evalChannels = dirtyChannels & fOwnedChannels; + + if (evalChannels & kPreshadeColor) + *fPreshadeColor = fSrcPreshadeColor * fUnderLay->GetPreshadeColor(); + + if (evalChannels & kRuntimeColor) + *fRuntimeColor = fSrcRuntimeColor * fUnderLay->GetRuntimeColor(); + + if (evalChannels & kAmbientColor) + *fAmbientColor = fSrcAmbientColor * fUnderLay->GetAmbientColor(); + + if (evalChannels & kOpacity) + *fOpacity = fSrcOpacity * fUnderLay->GetOpacity(); + + if (evalChannels & kTransform) + *fTransform = fSrcTransform * fUnderLay->GetTransform(); + + fDirtyChannels = 0; + return dirtyChannels; +} + +hsBool plLayerMultiply::MsgReceive(plMessage* msg) +{ + return plLayerMultiply::MsgReceive(msg); +} + +void plLayerMultiply::SetPreshadeColor(const hsColorRGBA& col) +{ + fSrcPreshadeColor = col; + fDirtyChannels |= kPreshadeColor; +} + +void plLayerMultiply::SetRuntimeColor(const hsColorRGBA& col) +{ + fSrcRuntimeColor = col; + fDirtyChannels |= kRuntimeColor; +} + +void plLayerMultiply::SetAmbientColor(const hsColorRGBA& col) +{ + fSrcAmbientColor = col; + fDirtyChannels |= kAmbientColor; +} + +void plLayerMultiply::SetOpacity(hsScalar a) +{ + fSrcOpacity = a; + fDirtyChannels |= kOpacity; +} + +void plLayerMultiply::SetTransform(const hsMatrix44& xfm) +{ + fSrcTransform = xfm; + fDirtyChannels |= kTransform; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerMultiply.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerMultiply.h new file mode 100644 index 00000000..630af990 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerMultiply.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_LAYER_ANIMATION_INC +#define PL_LAYER_ANIMATION_INC + +#include "plLayerInterface.h" +#include "hsMatrix44.h" + +// Instead of overwriting owned channels, this layer type will multiply +// its source channel data to the underlayer +class plLayerMultiply : public plLayerInterface +{ +public: + plLayerMultiply(); + virtual ~plLayerMultiply(); + + CLASSNAME_REGISTER( plLayerMultiply ); + GETINTERFACE_ANY( plLayerMultiply, plLayerInterface ); + + virtual plLayerInterface* Attach(plLayerInterface* prev); + virtual UInt32 Eval(double secs, UInt32 frame, UInt32 ignore); + virtual hsBool MsgReceive(plMessage* msg); + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + void SetPreshadeColor(const hsColorRGBA& col); + void SetRuntimeColor(const hsColorRGBA& col); + void SetAmbientColor(const hsColorRGBA& col); + void SetOpacity(hsScalar a); + void SetTransform(const hsMatrix44& xfm); + +protected: + UInt32 fDirtyChannels; + hsColorRGBA fSrcPreshadeColor; + hsColorRGBA fSrcRuntimeColor; + hsColorRGBA fSrcAmbientColor; + hsScalar fSrcOpacity; + hsMatrix44 fSrcTransform; +}; + +#endif // PL_LAYER_ANIMATION_INC \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerOr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerOr.cpp new file mode 100644 index 00000000..a63a8c60 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerOr.cpp @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLayerOr.h" + +plLayerOr::plLayerOr() +{ + fState = TRACKED_NEW hsGMatState; + fState->Reset(); + + fOwnedChannels = kState; +} + +plLayerOr::~plLayerOr() +{ +} + +void plLayerOr::SetState( const hsGMatState& state ) +{ + SetBlendFlags( state.fBlendFlags ); + SetClampFlags( state.fClampFlags ); + SetShadeFlags( state.fShadeFlags ); + SetZFlags( state.fZFlags ); + SetMiscFlags( state.fMiscFlags ); +} + +plLayerInterface *plLayerOr::Attach( plLayerInterface* prev ) +{ + fDirty = true; + + return plLayerInterface::Attach( prev ); +} + +UInt32 plLayerOr::Eval(double secs, UInt32 frame, UInt32 ignore) +{ + UInt32 ret = plLayerInterface::Eval(secs, frame, ignore); + if( fUnderLay ) + { + if( fDirty || (ret & kState) ) + { + *fState = fUnderLay->GetState(); + if( fOringState.fBlendFlags & hsGMatState::kBlendMask ) + fState->fBlendFlags &= ~hsGMatState::kBlendMask; + fState->fBlendFlags |= fOringState.fBlendFlags; + + fState->fClampFlags = fUnderLay->GetClampFlags() | fOringState.fClampFlags; + fState->fShadeFlags = fUnderLay->GetShadeFlags() | fOringState.fShadeFlags; + fState->fZFlags = fUnderLay->GetZFlags() | fOringState.fZFlags; + fState->fMiscFlags = fUnderLay->GetMiscFlags() | fOringState.fMiscFlags; + + fDirty = false; + } + } + return ret; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerOr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerOr.h new file mode 100644 index 00000000..d444b5f7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerOr.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef _plLayerOr_h +#define _plLayerOr_h + +#include "plLayerInterface.h" +#include "hsGMatState.h" + +class plLayerOr : public plLayerInterface +{ + protected: + hsGMatState fOringState; + hsBool fDirty; + + public: + plLayerOr(); + virtual ~plLayerOr(); + + CLASSNAME_REGISTER( plLayerOr ); + GETINTERFACE_ANY( plLayerOr, plLayerInterface ); + + void SetBlendFlags( UInt32 f ) { fOringState.fBlendFlags = f; } + void SetClampFlags( UInt32 f ) { fOringState.fClampFlags = f; } + void SetShadeFlags( UInt32 f ) { fOringState.fShadeFlags = f; } + void SetZFlags( UInt32 f ) { fOringState.fZFlags = f; } + void SetMiscFlags( UInt32 f ) { fOringState.fMiscFlags = f; } + void SetState( const hsGMatState& state ); + + virtual plLayerInterface* Attach(plLayerInterface* prev); + + virtual UInt32 Eval(double secs, UInt32 frame, UInt32 ignore); +}; + +#endif _plLayerOr_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerShadowBase.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerShadowBase.cpp new file mode 100644 index 00000000..2ab37c4a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerShadowBase.cpp @@ -0,0 +1,144 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLayerShadowBase.h" + +/////////////////////////////////////////////////////////////////////////// +plLayerLightBase::plLayerLightBase() +: fDirty(true) +{ + fOwnedChannels = kState + | kAmbientColor + | kPreshadeColor; + + fState = TRACKED_NEW hsGMatState; + fState->Reset(); + + fAmbientColor = TRACKED_NEW hsColorRGBA; + fAmbientColor->Set(0,0,0,1.f); + + fPreshadeColor = TRACKED_NEW hsColorRGBA; + fPreshadeColor->Set(0,0,0,1.f); +} + +plLayerLightBase::~plLayerLightBase() +{ +} + +plLayerInterface* plLayerLightBase::Attach(plLayerInterface* prev) +{ + fDirty = true; + return plLayerInterface::Attach(prev); +} + +UInt32 plLayerLightBase::Eval(double secs, UInt32 frame, UInt32 ignore) +{ + UInt32 ret = plLayerInterface::Eval(secs, frame, ignore); + if( fUnderLay ) + { + if( fDirty || (ret & kState) ) + { + *fState = fUnderLay->GetState(); + + UInt32 blend = fState->fBlendFlags; + + fState->fBlendFlags &= ~hsGMatState::kBlendMask; + + switch( blend ) + { + case hsGMatState::kBlendAlpha: + fState->fBlendFlags |= hsGMatState::kBlendAddColorTimesAlpha; + break; + default: + fState->fBlendFlags |= hsGMatState::kBlendAdd; + break; + } + + fState->fZFlags |= hsGMatState::kZNoZWrite; + + fState->fShadeFlags |= hsGMatState::kShadeIgnoreVtxIllum; + + ret |= kState | kAmbientColor; + + fDirty = false; + } + } + return ret; +} + +/////////////////////////////////////////////////////////////////////////// +plLayerShadowBase::plLayerShadowBase() +: fDirty(true) +{ + fOwnedChannels = kState + | kAmbientColor + | kPreshadeColor; + + fState = TRACKED_NEW hsGMatState; + fState->Reset(); + + fAmbientColor = TRACKED_NEW hsColorRGBA; + fAmbientColor->Set(0,0,0,1.f); + + fPreshadeColor = TRACKED_NEW hsColorRGBA; + fPreshadeColor->Set(0,0,0,1.f); +} + +plLayerShadowBase::~plLayerShadowBase() +{ +} + +plLayerInterface* plLayerShadowBase::Attach(plLayerInterface* prev) +{ + fDirty = true; + return plLayerInterface::Attach(prev); +} + +UInt32 plLayerShadowBase::Eval(double secs, UInt32 frame, UInt32 ignore) +{ + UInt32 ret = plLayerInterface::Eval(secs, frame, ignore); + if( fUnderLay ) + { + if( fDirty || (ret & kState) ) + { + *fState = fUnderLay->GetState(); + fState->fBlendFlags &= ~hsGMatState::kBlendMask; + //WHITE + fState->fBlendFlags |= hsGMatState::kBlendAlpha; + + fState->fZFlags |= hsGMatState::kZNoZWrite; + + fState->fShadeFlags |= hsGMatState::kShadeIgnoreVtxIllum; + + ret |= kState | kAmbientColor | kPreshadeColor; + + fDirty = false; + } + } + + return ret; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerShadowBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerShadowBase.h new file mode 100644 index 00000000..b29ad3bc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerShadowBase.h @@ -0,0 +1,74 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLayerShadowBase_inc +#define plLayerShadowBase_inc + +#include "plLayerInterface.h" +#include "hsGMatState.h" + +class plLayerLightBase : public plLayerInterface +{ +protected: + hsBool fDirty; +public: + + plLayerLightBase(); + virtual ~plLayerLightBase(); + + CLASSNAME_REGISTER( plLayerLightBase ); + GETINTERFACE_ANY( plLayerLightBase, plLayerInterface ); + + + virtual plLayerInterface* Attach(plLayerInterface* prev); + + virtual UInt32 Eval(double secs, UInt32 frame, UInt32 ignore); + + +}; + +class plLayerShadowBase : public plLayerInterface +{ +protected: + hsBool fDirty; +public: + + plLayerShadowBase(); + virtual ~plLayerShadowBase(); + + CLASSNAME_REGISTER( plLayerShadowBase ); + GETINTERFACE_ANY( plLayerShadowBase, plLayerInterface ); + + + virtual plLayerInterface* Attach(plLayerInterface* prev); + + virtual UInt32 Eval(double secs, UInt32 frame, UInt32 ignore); + + +}; + + +#endif // plLayerShadowBase_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerWrapper.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerWrapper.cpp new file mode 100644 index 00000000..02c8d645 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerWrapper.cpp @@ -0,0 +1,42 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plLayerWrapper.h" + +plLayerWrapper::plLayerWrapper() +{ +} + +void plLayerWrapper::Init(hsGLayer* lay) +{ + fState = &lay->fState; + fUVWSrc = &lay->fUVWSrc; + fTransform = lay->fXform; + fColor = &lay->fColor; + fAmbientColor = &lay->fAmbientColor; + fTexture = lay->fTexture; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerWrapper.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerWrapper.h new file mode 100644 index 00000000..cbe89991 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plLayerWrapper.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLayerWrapper_inc +#define plLayerWrapper_inc + +#include "plLayerInterface.h" + +class hsGLayer; + +class plLayerWrapper : public plLayerInterface +{ +protected: +public: + + plLayerWrapper(); + + CLASSNAME_REGISTER( plLayerWrapper ); + GETINTERFACE_ANY( plLayerWrapper, plLayerInterface ); + + virtual void Init(const plLayerInterface* prev) {} // Init(layer) currently handles all this + void Init(hsGLayer* lay); + + virtual UInt32 Eval(double secs, UInt32 frame, UInt32 dirty, plLayerInterface* prev) + { + return dirty; + } + + virtual hsBool MsgReceive(plMessage* msg) { return plLayerInterface::MsgReceive(msg); } +}; + +#endif // plLayerWrapper_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShader.cpp new file mode 100644 index 00000000..d44d9e2b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShader.cpp @@ -0,0 +1,321 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#include "hsTypes.h" +#include "plShader.h" +#include "plShaderTable.h" + +#include "hsStream.h" +#include "hsMatrix44.h" +#include "hsColorRGBA.h" + +#include "../plPipeline/hsGDeviceRef.h" + + +// Little shader const helper +void plShaderConst::Read(hsStream* s) +{ + fX = s->ReadSwapScalar(); + fY = s->ReadSwapScalar(); + fZ = s->ReadSwapScalar(); + fW = s->ReadSwapScalar(); +} + +void plShaderConst::Write(hsStream* s) +{ + s->WriteSwapScalar(fX); + s->WriteSwapScalar(fY); + s->WriteSwapScalar(fZ); + s->WriteSwapScalar(fW); +} + +////////////////////////////////////////////////////////////////////////////////// +// Real Shader follows +////////////////////////////////////////////////////////////////////////////////// + +plShader::plShader() +: fFlags(0), + fDeviceRef(nil), + fInput(0), + fOutput(0), + fDecl(0) +{ +} + +plShader::~plShader() +{ + delete fDeviceRef; +} + +void plShader::SetDeviceRef(hsGDeviceRef* ref) const +{ + hsRefCnt_SafeAssign(fDeviceRef, ref); +} + + +void plShader::SetMatrix(int i, const plFloat44& xfm) +{ + // Stuff in the transpose + SetVector(i+0, xfm.m[0][0], xfm.m[1][0], xfm.m[2][0], xfm.m[3][0]); + SetVector(i+1, xfm.m[0][1], xfm.m[1][1], xfm.m[2][1], xfm.m[3][1]); + SetVector(i+2, xfm.m[0][2], xfm.m[1][2], xfm.m[2][2], xfm.m[3][2]); + SetVector(i+3, xfm.m[0][3], xfm.m[1][3], xfm.m[2][3], xfm.m[3][3]); +} + +void plShader::SetMatrix3(int i, const plFloat44& xfm) +{ + // Stuff in the transpose + SetVector(i+0, xfm.m[0][0], xfm.m[1][0], xfm.m[2][0], xfm.m[3][0]); + SetVector(i+1, xfm.m[0][1], xfm.m[1][1], xfm.m[2][1], xfm.m[3][1]); + SetVector(i+2, xfm.m[0][2], xfm.m[1][2], xfm.m[2][2], xfm.m[3][2]); +} + +void plShader::SetMatrix44(int i, const hsMatrix44& xfm) +{ + // hsMatrix44 is already transpose of the rest of the world + SetVector(i+0, xfm.fMap[0][0], xfm.fMap[0][1], xfm.fMap[0][2], xfm.fMap[0][3]); + SetVector(i+1, xfm.fMap[1][0], xfm.fMap[1][1], xfm.fMap[1][2], xfm.fMap[1][3]); + SetVector(i+2, xfm.fMap[2][0], xfm.fMap[2][1], xfm.fMap[2][2], xfm.fMap[2][3]); + SetVector(i+3, xfm.fMap[3][0], xfm.fMap[3][1], xfm.fMap[3][2], xfm.fMap[3][3]); +} + +void plShader::SetMatrix34(int i, const hsMatrix44& xfm) +{ + // hsMatrix44 is already transpose of the rest of the world + SetVector(i+0, xfm.fMap[0][0], xfm.fMap[0][1], xfm.fMap[0][2], xfm.fMap[0][3]); + SetVector(i+1, xfm.fMap[1][0], xfm.fMap[1][1], xfm.fMap[1][2], xfm.fMap[1][3]); + SetVector(i+2, xfm.fMap[2][0], xfm.fMap[2][1], xfm.fMap[2][2], xfm.fMap[2][3]); +} + +void plShader::SetMatrix24(int i, const hsMatrix44& xfm) +{ + // hsMatrix44 is already transpose of the rest of the world + SetVector(i+0, xfm.fMap[0][0], xfm.fMap[0][1], xfm.fMap[0][2], xfm.fMap[0][3]); + SetVector(i+1, xfm.fMap[1][0], xfm.fMap[1][1], xfm.fMap[1][2], xfm.fMap[1][3]); +} + +void plShader::SetColor(int i, const hsColorRGBA& col) +{ + SetVector(i, col.r, col.g, col.b, col.a); +} + +void plShader::SetVector(int i, const hsScalarTriple& vec) +{ + /* Doesn't touch .fW */ + fConsts[i].fX = vec.fX; + fConsts[i].fY = vec.fY; + fConsts[i].fZ = vec.fZ; +} + +void plShader::SetVector(int i, hsScalar x, hsScalar y, hsScalar z, hsScalar w) +{ + fConsts[i].x = x; + fConsts[i].y = y; + fConsts[i].z = z; + fConsts[i].w = w; +} + +void plShader::SetFloat(int i, int chan, float v) +{ + fConsts[i].fArray[chan] = v; +} + +void plShader::SetFloat4(int i, const float* const f) +{ + fConsts[i].fX = f[0]; + fConsts[i].fY = f[1]; + fConsts[i].fZ = f[2]; + fConsts[i].fW = f[3]; +} + +plFloat44 plShader::GetMatrix(int i) const +{ + // untranspose + plFloat44 xfm; + int j; + for( j = 0; j < 4; j++ ) + { + int k; + for( k = 0; k < 4; k++ ) + xfm.m[j][k] = fConsts[i+k].fArray[j]; + } + return xfm; +} + +plFloat44 plShader::GetMatrix3(int i) const +{ + // untranspose + plFloat44 xfm; + int j; + for( j = 0; j < 4; j++ ) + { + int k; + for( k = 0; k < 3; k++ ) + xfm.m[j][k] = fConsts[i+k].fArray[j]; + } + xfm.m[0][3] = xfm.m[1][3] = xfm.m[2][3] = 0; + xfm.m[3][3] = 1.f; + return xfm; +} + +hsMatrix44 plShader::GetMatrix44(int i) const +{ + hsMatrix44 xfm; + xfm.NotIdentity(); + int j; + for( j = 0; j < 4; j++ ) + { + int k; + for( k = 0; k < 4; k++ ) + xfm.fMap[j][k] = fConsts[i+j][k]; + } + + return xfm; +} + +hsMatrix44 plShader::GetMatrix34(int i) const +{ + hsMatrix44 xfm; + xfm.NotIdentity(); + int j; + for( j = 0; j < 3; j++ ) + { + int k; + for( k = 0; k < 4; k++ ) + xfm.fMap[j][k] = fConsts[i+j][k]; + } + xfm.fMap[3][0] = xfm.fMap[3][1] = xfm.fMap[3][2] = 0; + xfm.fMap[3][3] = 1.f; + + return xfm; +} + +hsMatrix44 plShader::GetMatrix24(int i) const +{ + hsMatrix44 xfm; + xfm.NotIdentity(); + int j; + for( j = 0; j < 2; j++ ) + { + int k; + for( k = 0; k < 4; k++ ) + xfm.fMap[j][k] = fConsts[i+j][k]; + } + xfm.fMap[2][0] = xfm.fMap[2][1] = xfm.fMap[2][2] = xfm.fMap[2][3] = 0; + xfm.fMap[3][0] = xfm.fMap[3][1] = xfm.fMap[3][2] = 0; + xfm.fMap[3][3] = 1.f; + + return xfm; +} + +hsColorRGBA plShader::GetColor(int i) const +{ + return hsColorRGBA().Set(fConsts[i].r, fConsts[i].g, fConsts[i].b, fConsts[i].a); +} + +hsPoint3 plShader::GetPosition(int i) const +{ + return hsPoint3(fConsts[i].fX, fConsts[i].fY, fConsts[i].fZ); +} + +hsVector3 plShader::GetVector(int i) const +{ + return hsVector3(fConsts[i].fX, fConsts[i].fY, fConsts[i].fZ); +} + +void plShader::GetVector(int i, hsScalar& x, hsScalar& y, hsScalar& z, hsScalar& w) const +{ + x = fConsts[i].x; + y = fConsts[i].y; + z = fConsts[i].z; + w = fConsts[i].w; +} + +hsScalar plShader::GetFloat(int i, int chan) const +{ + return fConsts[i].fArray[chan]; +} + +const float* const plShader::GetFloat4(int i) const +{ + return fConsts[i].fArray; +} + +void plShader::Read(hsStream* s, hsResMgr* mgr) +{ + fFlags = 0; + + hsKeyedObject::Read(s, mgr); + + UInt32 n = s->ReadSwap32(); + fConsts.SetCount(n); + int i; + for( i = 0; i < n; i++ ) + fConsts[i].Read(s); + + plShaderID::ID id = plShaderID::ID(s->ReadSwap32()); + SetDecl(plShaderTable::Decl(id)); + + fInput = s->ReadByte(); + fOutput = s->ReadByte(); +} + +void plShader::Write(hsStream* s, hsResMgr* mgr) +{ + hsKeyedObject::Write(s, mgr); + + s->WriteSwap32(fConsts.GetCount()); + int i; + for( i = 0; i < fConsts.GetCount(); i++ ) + fConsts[i].Write(s); + + s->WriteSwap32(fDecl->GetID()); + + s->WriteByte(fInput); + s->WriteByte(fOutput); +} + +void plShader::SetDecl(const plShaderDecl* decl) +{ + fDecl = decl; +} + +void plShader::SetDecl(plShaderID::ID id) +{ + SetDecl(plShaderTable::Decl(id)); +} + +void plShader::SetNumPipeConsts(int n) +{ + int nOld = fPipeConsts.GetCount(); + if( n > nOld ) + { + // This will copy forward any existing entries. + fPipeConsts.Expand(n); + } + fPipeConsts.SetCount(n); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShader.h new file mode 100644 index 00000000..1db69fe5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShader.h @@ -0,0 +1,266 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plShader_inc +#define plShader_inc + +#include "../pnKeyedObject/hsKeyedObject.h" +#include "hsTemplates.h" +#include "hsGeometry3.h" +#include "hsMatrix44.h" +#include "plShaderTable.h" + +class hsStream; +class hsResMgr; +class hsMatrix; +struct hsColorRGBA; +class hsGDeviceRef; + +class plFloat4 +{ +public: + float f[4]; +}; + +class plFloat44 +{ +public: + float m[4][4]; +}; + +class plFloat34 +{ +public: + float m[3][4]; +}; + +class plShaderConst +{ +public: + union + { + struct { + float r; + float g; + float b; + float a; + }; + struct { + float x; + float y; + float z; + float w; + }; + struct { + float fX; + float fY; + float fZ; + float fW; + }; + float fArray[4]; + }; + + float& operator[](int i) { return fArray[i]; } + + void Read(hsStream* s); + void Write(hsStream* s); +}; + +class plShaderDecl; + +class plPipeConst +{ +public: + enum Type + { + kLocalToNDC, // 4x4 + kCameraToNDC, // 4x4 + kWorldToNDC, // 4x4 + kLocalToWorld, // 3x4 + kWorldToLocal, // 3x4 + kWorldToCamera, // 3x4 + kCameraToWorld, // 3x4 + kLocalToCamera, // 3x4 + kCameraToLocal, // 3x4 + kCamPosWorld, // 1x4 + kCamPosLocal, // 1x4 + kObjPosWorld, // 1x4 + kTex3x4_0, // 3x4, stage 0 + kTex3x4_1, // 3x4, stage 1 + kTex3x4_2, // 3x4, stage 2 + kTex3x4_3, // 3x4, stage 3 + kTex3x4_4, // 3x4, stage 4 + kTex3x4_5, // 3x4, stage 5 + kTex3x4_6, // 3x4, stage 6 + kTex3x4_7, // 3x4, stage 7 + kTex2x4_0, // 2x4, stage 0 + kTex2x4_1, // 2x4, stage 1 + kTex2x4_2, // 2x4, stage 2 + kTex2x4_3, // 2x4, stage 3 + kTex2x4_4, // 2x4, stage 4 + kTex2x4_5, // 2x4, stage 5 + kTex2x4_6, // 2x4, stage 6 + kTex2x4_7, // 2x4, stage 7 + kTex1x4_0, // 1x4, stage 0 + kTex1x4_1, // 1x4, stage 1 + kTex1x4_2, // 1x4, stage 2 + kTex1x4_3, // 1x4, stage 3 + kTex1x4_4, // 1x4, stage 4 + kTex1x4_5, // 1x4, stage 5 + kTex1x4_6, // 1x4, stage 6 + kTex1x4_7, // 1x4, stage 7 + kDirLight1, // 2x4, dir, then color + kDirLight2, // 4x4, kDirLight1 x 2 + kDirLight3, // 6x4, kDirLight1 x 3 + kDirLight4, // 8x4, kDirLight1 x 4 + kPointLight1, // 3x4, pos, dir, distAtten (spotAtten.xy dropped on end of pos.w/dir.w) + kPointLight2, // 6x4, kPointLight1 x 2 + kPointLight3, // 9x4, kPointLight1 x 3 + kPointLight4, // 12x4, kPointLight1 x4 + kLayAmbient, // 4x4 + kLayRuntime, // 4x4 (r,g,b,a) + kLaySpecular, // 4x4 (but alpha is ignored). + kFogSet, // 1x4 = (-FogMax, 1.f/(FogMin - FogMax), density, 1) + kColorFilter, // 1x4 color filter currently applied to entire scene. + + kMaxType + }; +public: + + plPipeConst() {} + plPipeConst(Type t, UInt16 r) : fType(t), fReg(r) {} + + Type fType; + UInt16 fReg; +}; + +typedef plPipeConst::Type plPipeConstType; + +class plShader : public hsKeyedObject +{ +public: + enum { + kValidated = 0x1, + kInvalid = 0x2, + kIsPixel = 0x4, + kShaderNotFound = 0x8, + kShaderError = 0x10, + kShaderUnsupported = 0x20 + }; +protected: + mutable UInt32 fFlags; + + hsTArray fConsts; + + mutable hsGDeviceRef* fDeviceRef; + + const plShaderDecl* fDecl; + + UInt8 fInput; + UInt8 fOutput; + + hsTArray fPipeConsts; + +public: + plShader(); + virtual ~plShader(); + + CLASSNAME_REGISTER( plShader ); + GETINTERFACE_ANY( plShader, hsKeyedObject ); + + // Read and write + virtual void Read(hsStream* s, hsResMgr* mgr); + virtual void Write(hsStream* s, hsResMgr* mgr); + + void SetNumConsts(int cnt) { fConsts.SetCount(cnt); } + UInt32 GetNumConsts() const { return fConsts.GetCount(); } + plShaderConst& GetConst(int i) { return fConsts[i]; } + const plShaderConst& GetConst(int i) const { return fConsts[i]; } + void SetConst(int i, const plShaderConst& c) { fConsts[i] = c; } + + plFloat44 GetMatrix(int i) const; // Will untranspose + plFloat44 GetMatrix3(int i) const; // Will untranspose + hsMatrix44 GetMatrix44(int i) const; + hsMatrix44 GetMatrix34(int i) const; + hsMatrix44 GetMatrix24(int i) const; + hsColorRGBA GetColor(int i) const; + hsPoint3 GetPosition(int i) const; + hsVector3 GetVector(int i) const; + void GetVector(int i, hsScalar& x, hsScalar& y, hsScalar& z, hsScalar& w) const; + hsScalar GetFloat(int i, int chan) const; + const float* const GetFloat4(int i) const; + + void SetMatrix(int i, const plFloat44& xfm); // Will transpose + void SetMatrix3(int i, const plFloat44& xfm); // Will transpose + void SetMatrix44(int i, const hsMatrix44& xfm); + void SetMatrix34(int i, const hsMatrix44& xfm); + void SetMatrix24(int i, const hsMatrix44& xfm); + void SetColor(int i, const hsColorRGBA& col); + void SetVector(int i, const hsScalarTriple& vec); /* Doesn't touch .fW */ + void SetVectorW(int i, const hsScalarTriple& vec, hsScalar w=1.f) { SetVector(i, vec.fX, vec.fY, vec.fZ, w); } + void SetVector(int i, hsScalar x, hsScalar y, hsScalar z, hsScalar w); + void SetFloat(int i, int chan, float v); + void SetFloat4(int i, const float* const f); + + const plShaderDecl* GetDecl() const { return fDecl; } + + void SetDecl(const plShaderDecl* p); // will reference (pointer copy) + void SetDecl(plShaderID::ID id); + + hsBool IsValid() const { return !(fFlags & kInvalid); } + void Invalidate() const { fFlags |= kInvalid; } + + hsBool IsPixelShader() const { return 0 != (fFlags & kIsPixel); } + hsBool IsVertexShader() const { return !IsPixelShader(); } + void SetIsPixelShader(hsBool on) { if(on)fFlags |= kIsPixel; else fFlags &= ~kIsPixel; } + + // These are only for use by the pipeline. + hsGDeviceRef* GetDeviceRef() const { return fDeviceRef; } + void SetDeviceRef(hsGDeviceRef* ref) const; + + void* GetConstBasePtr() const { return fConsts.GetCount() ? &fConsts[0] : nil; } + + void CopyConsts(const plShader* src) { fConsts = src->fConsts; } + + void SetInputFormat(UInt8 format) { fInput = format; } + void SetOutputFormat(UInt8 format) { fOutput = format; } + + UInt8 GetInputFormat() const { return fInput; } + UInt8 GetOutputFormat() const { return fOutput; } + + UInt32 GetNumPipeConsts() const { return fPipeConsts.GetCount(); } + const plPipeConst& GetPipeConst(int i) const { return fPipeConsts[i]; } + plPipeConst::Type GetPipeConstType(int i) const { return fPipeConsts[i].fType; } + UInt16 GetPipeConstReg(int i) const { return fPipeConsts[i].fReg; } + + void SetNumPipeConsts(int n); + void SetPipeConst(int i, const plPipeConst& c) { fPipeConsts[i] = c; } + void SetPipeConst(int i, plPipeConstType t, UInt16 r) { fPipeConsts[i].fType = t; fPipeConsts[i].fReg = r; } + void SetPipeConstType(int i, plPipeConstType t) { fPipeConsts[i].fType = t; } + void SetPipeConstReg(int i, UInt16 r) { fPipeConsts[i].fReg = r; } +}; + +#endif // plShader_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShaderTable.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShaderTable.cpp new file mode 100644 index 00000000..6e2eefaf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShaderTable.cpp @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "plShaderTable.h" + +#include "plShader.h" + +using namespace plShaderID; + +/////////////////////////////////////////////////////////////////////// +// Includes for compiled shaders +/////////////////////////////////////////////////////////////////////// +#include "vs_WaveFixedFin6.h" +#include "ps_WaveFixed.h" +#include "vs_CompCosines.h" +#include "ps_CompCosines.h" +#include "vs_ShoreLeave6.h" +#include "ps_ShoreLeave6.h" +#include "vs_WaveRip.h" +#include "ps_WaveRip.h" +#include "vs_WaveDec1Lay.h" +#include "vs_WaveDec2Lay11.h" +#include "vs_WaveDec2Lay12.h" +#include "vs_WaveDecEnv.h" +#include "ps_CbaseAbase.h" +#include "ps_CalphaAbase.h" +#include "ps_CalphaAMult.h" +#include "ps_CalphaAadd.h" +#include "ps_CaddAbase.h" +#include "ps_CaddAMult.h" +#include "ps_CaddAAdd.h" +#include "ps_CmultAbase.h" +#include "ps_CmultAMult.h" +#include "ps_CmultAAdd.h" +#include "ps_WaveDecEnv.h" +#include "vs_WaveGraph2.h" +#include "ps_WaveGraph.h" +#include "vs_WaveGridFin.h" +#include "ps_WaveGrid.h" +#include "vs_BiasNormals.h" +#include "ps_BiasNormals.h" +#include "vs_ShoreLeave7.h" +#include "vs_WaveRip7.h" +#include "ps_MoreCosines.h" +#include "vs_WaveDec1Lay_7.h" +#include "vs_WaveDec2Lay11_7.h" +#include "vs_WaveDec2Lay12_7.h" +#include "vs_WaveDecEnv_7.h" +#include "vs_WaveFixedFin7.h" +#include "vs_GrassShader.h" +#include "ps_GrassShader.h" + + +plShaderTableInst::plShaderTableInst() +: fFlags(0) +{ +} + +plShaderTableInst::~plShaderTableInst() +{ +} + +void plShaderTableInst::Register(const plShaderDecl* decl) +{ + hsAssert(decl->GetID() && (decl->GetID() < plShaderID::kNumShaders), "Unexpected registration"); + fTable[decl->GetID()] = decl; +} + +plShaderTableInst& plShaderTable::IMakeInstance() +{ + static plShaderTableInst inst; + fInst = &inst; + + return *fInst; +} + + +plShaderTableInst* plShaderTable::fInst; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShaderTable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShaderTable.h new file mode 100644 index 00000000..ae7a8363 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plShaderTable.h @@ -0,0 +1,160 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plShaderTable_inc +#define plShaderTable_inc + +#include "hsTemplates.h" + +// When adding to the compiled table, make sure +// you add the include in plShaderTable.cpp, or you'll +// compile fine but have a nil shader (FFP) at runtime. +namespace plShaderID +{ + enum ID + { + Unregistered = 0, + vs_WaveFixedFin6, //OBSOLETE + ps_WaveFixed, + vs_CompCosines, + ps_CompCosines, //OBSOLETE + vs_ShoreLeave6, //OBSOLETE + ps_ShoreLeave6, + vs_WaveRip, //OBSOLETE + ps_WaveRip, + vs_WaveDec1Lay, //OBSOLETE + vs_WaveDec2Lay11, //OBSOLETE + vs_WaveDec2Lay12, //OBSOLETE + vs_WaveDecEnv, //OBSOLETE + ps_CbaseAbase, + ps_CalphaAbase, + ps_CalphaAMult, + ps_CalphaAadd, + ps_CaddAbase, + ps_CaddAMult, + ps_CaddAAdd, + ps_CmultAbase, + ps_CmultAMult, + ps_CmultAAdd, + ps_WaveDecEnv, + vs_WaveGraph2, + ps_WaveGraph, + vs_WaveGridFin, //OBSOLETE + ps_WaveGrid, //OBSOLETE + vs_BiasNormals, + ps_BiasNormals, + vs_ShoreLeave7, + vs_WaveRip7, + ps_MoreCosines, + vs_WaveDec1Lay_7, + vs_WaveDec2Lay11_7, + vs_WaveDec2Lay12_7, + vs_WaveDecEnv_7, + vs_WaveFixedFin7, + vs_GrassShader, + ps_GrassShader, + + kNumShaders + }; +}; + + +class plShaderDecl +{ +protected: + const plShaderID::ID fID; + const UInt32 fByteLen; + const UInt8* const fCodes; + const char* const fFileName; + +public: + plShaderDecl(const char* const fname, plShaderID::ID id = plShaderID::Unregistered, UInt32 byteLen = 0, const UInt8* const codes = 0L) : fID(id), fByteLen(byteLen), fCodes(codes), fFileName(fname) {} + // Data (fCodes) is never deleted, It points to memory compiled in. + + plShaderID::ID GetID() const { return fID; } + UInt32 GetByteLen() const { return fByteLen; } + const UInt8* GetCodes() const { return fCodes; } + const char* const GetFileName() const { return fFileName; } +}; + +class plShaderTableInst +{ +protected: + enum + { + kLoadFromFile = 0x1 + }; + + UInt32 fFlags; + + const plShaderDecl* fTable[plShaderID::kNumShaders]; + + plShaderTableInst(); + + hsBool LoadFromFile() const { return 0 != (fFlags & kLoadFromFile); } + void SetLoadFromFile(hsBool on) { if(on) fFlags |= kLoadFromFile; else fFlags &= ~kLoadFromFile; } + + const plShaderDecl* Decl(plShaderID::ID id) const { return fTable[id]; } + + void Register(const plShaderDecl* decl); + + hsBool IsRegistered(plShaderID::ID id) const { return (id == 0) || ((id < plShaderID::kNumShaders) && fTable[id]); } + +public: + virtual ~plShaderTableInst(); + + friend class plShaderTable; +}; + +class plShaderTable +{ +protected: + static plShaderTableInst* fInst; + + static plShaderTableInst& IMakeInstance(); + + static plShaderTableInst& Instance() { return fInst ? *fInst : IMakeInstance(); } + +public: + + static hsBool LoadFromFile() { return Instance().LoadFromFile(); } + static void SetLoadFromFile(hsBool on) { Instance().SetLoadFromFile(on); } + + static const plShaderDecl* Decl(plShaderID::ID id) { return Instance().Decl(id); } + + static void Register(const plShaderDecl* decl) { Instance().Register(decl); } + + static hsBool IsRegistered(plShaderID::ID id) { return Instance().IsRegistered(id); } +}; + +class plShaderRegister +{ +public: + plShaderRegister(const plShaderDecl* decl) { plShaderTable::Register(decl); } +}; + + +#endif // plShaderTable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plSurfaceCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plSurfaceCreatable.h new file mode 100644 index 00000000..e38170f9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/plSurfaceCreatable.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSurfaceCreatable_inc +#define plSurfaceCreatable_inc + +#include "../pnFactory/plCreator.h" + +#include "hsGMaterial.h" + +REGISTER_CREATABLE( hsGMaterial ); + +#include "plLayerInterface.h" + +REGISTER_NONCREATABLE( plLayerInterface ); + +#include "plLayer.h" + +REGISTER_CREATABLE( plLayer ); + +#include "plLayerAnimation.h" + +REGISTER_CREATABLE( plLayerAnimation ); +REGISTER_CREATABLE( plLayerLinkAnimation ); +REGISTER_NONCREATABLE( plLayerAnimationBase ); +REGISTER_CREATABLE( plLayerSDLAnimation ); + +#include "plLayerDepth.h" + +REGISTER_CREATABLE( plLayerDepth ); + +#include "plLayerOr.h" + +REGISTER_CREATABLE( plLayerOr ); + +#include "plLayerShadowBase.h" + +REGISTER_CREATABLE( plLayerShadowBase ); +REGISTER_CREATABLE( plLayerLightBase ); + +#include "plShader.h" + +REGISTER_CREATABLE( plShader ); + +#include "plGrassShaderMod.h" + +REGISTER_CREATABLE( plGrassShaderMod ); + +#endif // plSurfaceCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_BiasNormals.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_BiasNormals.h new file mode 100644 index 00000000..536fc4cf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_BiasNormals.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_BiasNormalsByteLen = 76; + +static const UInt8 ps_BiasNormalsCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0xb2, + 0x1, 0x0, 0xe4, 0xb2, + 0x2, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x1, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_BiasNormalsDecl("sha/ps_BiasNormals.inl", ps_BiasNormals, ps_BiasNormalsByteLen, ps_BiasNormalsCodes); + +static const plShaderRegister ps_BiasNormalsRegister(&ps_BiasNormalsDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAAdd.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAAdd.h new file mode 100644 index 00000000..be0f07b1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAAdd.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_CaddAAddByteLen = 72; + +static const UInt8 ps_CaddAAddCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x2, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_CaddAAddDecl("sha/ps_CaddAAdd.inl", ps_CaddAAdd, ps_CaddAAddByteLen, ps_CaddAAddCodes); + +static const plShaderRegister ps_CaddAAddRegister(&ps_CaddAAddDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAMult.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAMult.h new file mode 100644 index 00000000..d07bc489 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAMult.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_CaddAMultByteLen = 72; + +static const UInt8 ps_CaddAMultCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_CaddAMultDecl("sha/ps_CaddAMult.inl", ps_CaddAMult, ps_CaddAMultByteLen, ps_CaddAMultCodes); + +static const plShaderRegister ps_CaddAMultRegister(&ps_CaddAMultDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAbase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAbase.h new file mode 100644 index 00000000..38e1d037 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CaddAbase.h @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_CaddAbaseByteLen = 68; + +static const UInt8 ps_CaddAbaseCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_CaddAbaseDecl("sha/ps_CaddAbase.inl", ps_CaddAbase, ps_CaddAbaseByteLen, ps_CaddAbaseCodes); + +static const plShaderRegister ps_CaddAbaseRegister(&ps_CaddAbaseDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAMult.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAMult.h new file mode 100644 index 00000000..55d29b03 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAMult.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_CalphaAMultByteLen = 76; + +static const UInt8 ps_CalphaAMultCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x12, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x1, 0x0, 0xff, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x0, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_CalphaAMultDecl("sha/ps_CalphaAMult.inl", ps_CalphaAMult, ps_CalphaAMultByteLen, ps_CalphaAMultCodes); + +static const plShaderRegister ps_CalphaAMultRegister(&ps_CalphaAMultDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAadd.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAadd.h new file mode 100644 index 00000000..266f9e1b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAadd.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_CalphaAaddByteLen = 76; + +static const UInt8 ps_CalphaAaddCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x12, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x1, 0x0, 0xff, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x0, 0x0, 0xe4, 0xb0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_CalphaAaddDecl("sha/ps_CalphaAadd.inl", ps_CalphaAadd, ps_CalphaAaddByteLen, ps_CalphaAaddCodes); + +static const plShaderRegister ps_CalphaAaddRegister(&ps_CalphaAaddDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAbase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAbase.h new file mode 100644 index 00000000..80931259 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CalphaAbase.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_CalphaAbaseByteLen = 72; + +static const UInt8 ps_CalphaAbaseCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x12, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x1, 0x0, 0xff, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_CalphaAbaseDecl("sha/ps_CalphaAbase.inl", ps_CalphaAbase, ps_CalphaAbaseByteLen, ps_CalphaAbaseCodes); + +static const plShaderRegister ps_CalphaAbaseRegister(&ps_CalphaAbaseDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CbaseAbase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CbaseAbase.h new file mode 100644 index 00000000..8739846b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CbaseAbase.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_CbaseAbaseByteLen = 32; + +static const UInt8 ps_CbaseAbaseCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_CbaseAbaseDecl("sha/ps_CbaseAbase.inl", ps_CbaseAbase, ps_CbaseAbaseByteLen, ps_CbaseAbaseCodes); + +static const plShaderRegister ps_CbaseAbaseRegister(&ps_CbaseAbaseDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAAdd.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAAdd.h new file mode 100644 index 00000000..685a0c98 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAAdd.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_CmultAAddByteLen = 72; + +static const UInt8 ps_CmultAAddCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x2, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_CmultAAddDecl("sha/ps_CmultAAdd.inl", ps_CmultAAdd, ps_CmultAAddByteLen, ps_CmultAAddCodes); + +static const plShaderRegister ps_CmultAAddRegister(&ps_CmultAAddDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAMult.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAMult.h new file mode 100644 index 00000000..3d418bd8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAMult.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_CmultAMultByteLen = 72; + +static const UInt8 ps_CmultAMultCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_CmultAMultDecl("sha/ps_CmultAMult.inl", ps_CmultAMult, ps_CmultAMultByteLen, ps_CmultAMultCodes); + +static const plShaderRegister ps_CmultAMultRegister(&ps_CmultAMultDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAbase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAbase.h new file mode 100644 index 00000000..d72b39c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CmultAbase.h @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_CmultAbaseByteLen = 68; + +static const UInt8 ps_CmultAbaseCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_CmultAbaseDecl("sha/ps_CmultAbase.inl", ps_CmultAbase, ps_CmultAbaseByteLen, ps_CmultAbaseCodes); + +static const plShaderRegister ps_CmultAbaseRegister(&ps_CmultAbaseDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CompCosines.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CompCosines.h new file mode 100644 index 00000000..a6b23ad0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_CompCosines.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_CompCosinesByteLen = 148; + +static const UInt8 ps_CompCosinesCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0xb4, + 0x0, 0x0, 0xe4, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0xb4, + 0x1, 0x0, 0xe4, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0xb4, + 0x2, 0x0, 0xe4, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0xb4, + 0x3, 0x0, 0xe4, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_CompCosinesDecl("sha/ps_CompCosines.inl", ps_CompCosines, ps_CompCosinesByteLen, ps_CompCosinesCodes); + +static const plShaderRegister ps_CompCosinesRegister(&ps_CompCosinesDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_GrassShader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_GrassShader.h new file mode 100644 index 00000000..7c7591c9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_GrassShader.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_GrassShaderByteLen = 32; + +static const UInt8 ps_GrassShaderCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_GrassShaderDecl("sha/ps_GrassShader.inl", ps_GrassShader, ps_GrassShaderByteLen, ps_GrassShaderCodes); + +static const plShaderRegister ps_GrassShaderRegister(&ps_GrassShaderDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_MoreCosines.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_MoreCosines.h new file mode 100644 index 00000000..ad7ce0c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_MoreCosines.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_MoreCosinesByteLen = 160; + +static const UInt8 ps_MoreCosinesCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0xb4, + 0x0, 0x0, 0xe4, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0xb4, + 0x1, 0x0, 0xe4, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0xb4, + 0x2, 0x0, 0xe4, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0xb4, + 0x3, 0x0, 0xe4, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_MoreCosinesDecl("sha/ps_MoreCosines.inl", ps_MoreCosines, ps_MoreCosinesByteLen, ps_MoreCosinesCodes); + +static const plShaderRegister ps_MoreCosinesRegister(&ps_MoreCosinesDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_ShoreLeave6.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_ShoreLeave6.h new file mode 100644 index 00000000..6c1bcd3d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_ShoreLeave6.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_ShoreLeave6ByteLen = 172; + +static const UInt8 ps_ShoreLeave6Codes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x51, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xa0, + 0x0, 0x0, 0x80, 0x3f, + 0x0, 0x0, 0x80, 0x3f, + 0x0, 0x0, 0x80, 0x3f, + 0x0, 0x0, 0x80, 0x3f, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xb0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xe4, 0xb0, + 0x12, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x1, 0x0, 0xff, 0x80, + 0x1, 0x0, 0xe4, 0xb0, + 0x0, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xe4, 0xb6, + 0x0, 0x0, 0xe4, 0xb6, + 0x12, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x2, 0x0, 0xff, 0xb0, + 0x2, 0x0, 0xe4, 0xb0, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x2, 0x0, 0xe4, 0xb6, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x5, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0x86, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_ShoreLeave6Decl("sha/ps_ShoreLeave6.inl", ps_ShoreLeave6, ps_ShoreLeave6ByteLen, ps_ShoreLeave6Codes); + +static const plShaderRegister ps_ShoreLeave6Register(&ps_ShoreLeave6Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveDecEnv.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveDecEnv.h new file mode 100644 index 00000000..e36ebe5f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveDecEnv.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_WaveDecEnvByteLen = 84; + +static const UInt8 ps_WaveDecEnvCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x49, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x0, 0x0, 0xe4, 0xb4, + 0x49, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xb0, + 0x0, 0x0, 0xe4, 0xb4, + 0x4d, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0xb0, + 0x0, 0x0, 0xe4, 0xb4, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x3, 0x0, 0xe4, 0xb0, + 0x0, 0x0, 0xe4, 0x90, + 0x5, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_WaveDecEnvDecl("sha/ps_WaveDecEnv.inl", ps_WaveDecEnv, ps_WaveDecEnvByteLen, ps_WaveDecEnvCodes); + +static const plShaderRegister ps_WaveDecEnvRegister(&ps_WaveDecEnvDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveFixed.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveFixed.h new file mode 100644 index 00000000..0472b9d8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveFixed.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_WaveFixedByteLen = 108; + +static const UInt8 ps_WaveFixedCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x51, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xa0, + 0x0, 0x0, 0x80, 0x3f, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x80, 0x3f, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x49, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x0, 0x0, 0xe4, 0xb4, + 0x49, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xb0, + 0x0, 0x0, 0xe4, 0xb4, + 0x4d, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0xb0, + 0x0, 0x0, 0xe4, 0xb4, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x3, 0x0, 0xe4, 0xb0, + 0x0, 0x0, 0xe4, 0x90, + 0x1, 0x0, 0xe4, 0x90, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_WaveFixedDecl("sha/ps_WaveFixed.inl", ps_WaveFixed, ps_WaveFixedByteLen, ps_WaveFixedCodes); + +static const plShaderRegister ps_WaveFixedRegister(&ps_WaveFixedDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveGraph.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveGraph.h new file mode 100644 index 00000000..306d1324 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveGraph.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_WaveGraphByteLen = 80; + +static const UInt8 ps_WaveGraphCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x42, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0xb0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_WaveGraphDecl("sha/ps_WaveGraph.inl", ps_WaveGraph, ps_WaveGraphByteLen, ps_WaveGraphCodes); + +static const plShaderRegister ps_WaveGraphRegister(&ps_WaveGraphDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveGrid.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveGrid.h new file mode 100644 index 00000000..ef461051 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveGrid.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_WaveGridByteLen = 84; + +static const UInt8 ps_WaveGridCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x49, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xb0, + 0x0, 0x0, 0xe4, 0xb4, + 0x49, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xb0, + 0x0, 0x0, 0xe4, 0xb4, + 0x4d, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0xb0, + 0x0, 0x0, 0xe4, 0xb4, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x3, 0x0, 0xe4, 0xb0, + 0x1, 0x0, 0xe4, 0x90, + 0x0, 0x0, 0xe4, 0x90, + 0x1, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_WaveGridDecl("sha/ps_WaveGrid.inl", ps_WaveGrid, ps_WaveGridByteLen, ps_WaveGridCodes); + +static const plShaderRegister ps_WaveGridRegister(&ps_WaveGridDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveRip.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveRip.h new file mode 100644 index 00000000..7302c0fb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/ps_WaveRip.h @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 ps_WaveRipByteLen = 32; + +static const UInt8 ps_WaveRipCodes[] = { + 0x1, 0x1, 0xff, 0xff, + 0x42, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xb0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0xb0, + 0x0, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl ps_WaveRipDecl("sha/ps_WaveRip.inl", ps_WaveRip, ps_WaveRipByteLen, ps_WaveRipCodes); + +static const plShaderRegister ps_WaveRipRegister(&ps_WaveRipDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_BiasNormals.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_BiasNormals.h new file mode 100644 index 00000000..40dedcf0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_BiasNormals.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_BiasNormalsByteLen = 168; + +static const UInt8 vs_BiasNormalsCodes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x0, 0x0, 0xe4, 0x90, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xc, 0x80, + 0x4, 0x0, 0x80, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x0, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x1, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x2, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x3, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x5, 0x0, 0xa0, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xd0, + 0x5, 0x0, 0xa5, 0xa0, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_BiasNormalsDecl("sha/vs_BiasNormals.inl", vs_BiasNormals, vs_BiasNormalsByteLen, vs_BiasNormalsCodes); + +static const plShaderRegister vs_BiasNormalsRegister(&vs_BiasNormalsDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_CompCosines.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_CompCosines.h new file mode 100644 index 00000000..12ebab6d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_CompCosines.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_CompCosinesByteLen = 168; + +static const UInt8 vs_CompCosinesCodes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x0, 0x0, 0xe4, 0x90, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x0, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xe, 0x80, + 0x4, 0x0, 0x80, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x1, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x2, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x3, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xe4, 0x80, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_CompCosinesDecl("sha/vs_CompCosines.inl", vs_CompCosines, vs_CompCosinesByteLen, vs_CompCosinesCodes); + +static const plShaderRegister vs_CompCosinesRegister(&vs_CompCosinesDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_GrassShader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_GrassShader.h new file mode 100644 index 00000000..f3902fc9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_GrassShader.h @@ -0,0 +1,161 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_GrassShaderByteLen = 500; + +static const UInt8 vs_GrassShaderCodes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0xa0, + 0x0, 0x0, 0x0, 0x90, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0xc, 0x0, 0xe4, 0xa0, + 0x0, 0x0, 0x55, 0x90, + 0x0, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x5, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xe4, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x13, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x13, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x3, 0x80, + 0x0, 0x0, 0xee, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xc, 0x80, + 0x1, 0x0, 0x44, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x55, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xff, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xaa, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x9, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xff, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xc, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xaa, 0xa0, + 0x7, 0x0, 0x55, 0x91, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x2, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x7, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x14, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x2, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0x4, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x5, 0x0, 0xe4, 0x90, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0x7, 0x0, 0xe4, 0x90, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_GrassShaderDecl("sha/vs_GrassShader.inl", vs_GrassShader, vs_GrassShaderByteLen, vs_GrassShaderCodes); + +static const plShaderRegister vs_GrassShaderRegister(&vs_GrassShaderDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_ShoreLeave6.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_ShoreLeave6.h new file mode 100644 index 00000000..ab09d2f7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_ShoreLeave6.h @@ -0,0 +1,341 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_ShoreLeave6ByteLen = 1220; + +static const UInt8 vs_ShoreLeave6Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x19, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x9, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1e, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x1f, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x20, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x1d, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x1e, 0x0, 0xff, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x55, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x1e, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0xc, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0xe4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x20, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xc, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0xaa, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0xb, 0x0, 0x54, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x8, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0xb, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0xb, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x4, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x90, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x13, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x14, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xf4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xf4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xf4, 0x80, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_ShoreLeave6Decl("sha/vs_ShoreLeave6.inl", vs_ShoreLeave6, vs_ShoreLeave6ByteLen, vs_ShoreLeave6Codes); + +static const plShaderRegister vs_ShoreLeave6Register(&vs_ShoreLeave6Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_ShoreLeave7.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_ShoreLeave7.h new file mode 100644 index 00000000..eb71bc61 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_ShoreLeave7.h @@ -0,0 +1,279 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_ShoreLeave7ByteLen = 972; + +static const UInt8 vs_ShoreLeave7Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x19, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x9, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1e, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x1f, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x20, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x1d, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x1e, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x11, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x7, 0x0, 0x54, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x8, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0xb, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0xb, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x4, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x90, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x13, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x14, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xf4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xf4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xf4, 0x80, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_ShoreLeave7Decl("sha/vs_ShoreLeave7.inl", vs_ShoreLeave7, vs_ShoreLeave7ByteLen, vs_ShoreLeave7Codes); + +static const plShaderRegister vs_ShoreLeave7Register(&vs_ShoreLeave7Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec1Lay.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec1Lay.h new file mode 100644 index 00000000..a25b7ca4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec1Lay.h @@ -0,0 +1,311 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveDec1LayByteLen = 1100; + +static const UInt8 vs_WaveDec1LayCodes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x12, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x16, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x17, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x18, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x15, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x16, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xd, 0x0, 0x20, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0xaa, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0xb, 0x0, 0x54, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x19, 0x0, 0x0, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x1d, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x1d, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x5, 0x0, 0x15, 0x90, + 0x1a, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xc, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xe, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xf, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveDec1LayDecl("sha/vs_WaveDec1Lay.inl", vs_WaveDec1Lay, vs_WaveDec1LayByteLen, vs_WaveDec1LayCodes); + +static const plShaderRegister vs_WaveDec1LayRegister(&vs_WaveDec1LayDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec1Lay_7.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec1Lay_7.h new file mode 100644 index 00000000..10b632d1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec1Lay_7.h @@ -0,0 +1,277 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveDec1Lay_7ByteLen = 964; + +static const UInt8 vs_WaveDec1Lay_7Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x12, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x16, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x17, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x18, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x15, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x16, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1e, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1f, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x7, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x19, 0x0, 0x0, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x1d, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x1d, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x5, 0x0, 0x15, 0x90, + 0x1a, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xc, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xe, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xf, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveDec1Lay_7Decl("sha/vs_WaveDec1Lay_7.inl", vs_WaveDec1Lay_7, vs_WaveDec1Lay_7ByteLen, vs_WaveDec1Lay_7Codes); + +static const plShaderRegister vs_WaveDec1Lay_7Register(&vs_WaveDec1Lay_7Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay11.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay11.h new file mode 100644 index 00000000..a7187b01 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay11.h @@ -0,0 +1,322 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveDec2Lay11ByteLen = 1144; + +static const UInt8 vs_WaveDec2Lay11Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x12, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x16, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x17, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x18, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x15, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x16, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xd, 0x0, 0x20, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0xaa, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0xb, 0x0, 0x54, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x19, 0x0, 0x0, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x1d, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x1d, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x5, 0x0, 0x15, 0x90, + 0x1a, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xc, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xe, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xf, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x10, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x11, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveDec2Lay11Decl("sha/vs_WaveDec2Lay11.inl", vs_WaveDec2Lay11, vs_WaveDec2Lay11ByteLen, vs_WaveDec2Lay11Codes); + +static const plShaderRegister vs_WaveDec2Lay11Register(&vs_WaveDec2Lay11Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay11_7.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay11_7.h new file mode 100644 index 00000000..32b2fc62 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay11_7.h @@ -0,0 +1,288 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveDec2Lay11_7ByteLen = 1008; + +static const UInt8 vs_WaveDec2Lay11_7Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x12, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x16, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x17, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x18, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x15, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x16, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1e, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1f, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x7, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x19, 0x0, 0x0, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x1d, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x1d, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x5, 0x0, 0x15, 0x90, + 0x1a, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xc, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xe, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xf, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x10, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0x11, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveDec2Lay11_7Decl("sha/vs_WaveDec2Lay11_7.inl", vs_WaveDec2Lay11_7, vs_WaveDec2Lay11_7ByteLen, vs_WaveDec2Lay11_7Codes); + +static const plShaderRegister vs_WaveDec2Lay11_7Register(&vs_WaveDec2Lay11_7Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay12.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay12.h new file mode 100644 index 00000000..96163bc2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay12.h @@ -0,0 +1,325 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveDec2Lay12ByteLen = 1156; + +static const UInt8 vs_WaveDec2Lay12Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x1, 0x80, + 0x8, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x12, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x16, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x17, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x18, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x15, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x16, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xd, 0x0, 0x20, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0xaa, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0xb, 0x0, 0x54, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x19, 0x0, 0x0, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x1d, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x1d, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x5, 0x0, 0x15, 0x90, + 0x1a, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xc, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xe, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xf, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x8, 0x0, 0xe4, 0x90, + 0x10, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x8, 0x0, 0xe4, 0x90, + 0x11, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveDec2Lay12Decl("sha/vs_WaveDec2Lay12.inl", vs_WaveDec2Lay12, vs_WaveDec2Lay12ByteLen, vs_WaveDec2Lay12Codes); + +static const plShaderRegister vs_WaveDec2Lay12Register(&vs_WaveDec2Lay12Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay12_7.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay12_7.h new file mode 100644 index 00000000..486d56cd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDec2Lay12_7.h @@ -0,0 +1,291 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveDec2Lay12_7ByteLen = 1020; + +static const UInt8 vs_WaveDec2Lay12_7Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x1, 0x80, + 0x8, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x12, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x16, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x17, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x18, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x15, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x16, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1e, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1f, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x7, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x19, 0x0, 0x0, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x1d, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x1d, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x5, 0x0, 0x15, 0x90, + 0x1a, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xc, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xe, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xf, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x8, 0x0, 0xe4, 0x90, + 0x10, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x8, 0x0, 0xe4, 0x90, + 0x11, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveDec2Lay12_7Decl("sha/vs_WaveDec2Lay12_7.inl", vs_WaveDec2Lay12_7, vs_WaveDec2Lay12_7ByteLen, vs_WaveDec2Lay12_7Codes); + +static const plShaderRegister vs_WaveDec2Lay12_7Register(&vs_WaveDec2Lay12_7Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDecEnv.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDecEnv.h new file mode 100644 index 00000000..39e97433 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDecEnv.h @@ -0,0 +1,440 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveDecEnvByteLen = 1616; + +static const UInt8 vs_WaveDecEnvCodes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x1, 0x80, + 0x8, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x2, 0x80, + 0x9, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x12, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x16, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x17, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x18, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x15, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x16, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0xd, 0x0, 0x80, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xd, 0x0, 0x20, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xa4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0xaa, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0xb, 0x0, 0x54, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x19, 0x0, 0x0, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x1d, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x1d, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x9, 0x0, 0x0, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x2, 0x0, 0x0, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x2, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x3, 0x80, + 0x7, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xc, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xe, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xf, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xa8, 0x90, + 0x7, 0x0, 0xca, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x9, 0x0, 0x0, 0x90, + 0x2, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xa9, 0x90, + 0x7, 0x0, 0xca, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x2, 0x80, + 0x9, 0x0, 0x55, 0x90, + 0x8, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x81, + 0x8, 0x0, 0xe4, 0x90, + 0x8, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x81, + 0x9, 0x0, 0xe4, 0x90, + 0x1, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xc, 0x80, + 0x7, 0x0, 0xff, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x1b, 0x0, 0xe4, 0xa1, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x1c, 0x0, 0xe4, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x2, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x1c, 0x0, 0xff, 0xa1, + 0x7, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x55, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x4, 0x80, + 0xa, 0x0, 0x55, 0x80, + 0x9, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0x80, + 0x1c, 0x0, 0xa4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x0, 0x0, 0x0, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x8, 0x80, + 0x0, 0x0, 0x55, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xaa, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x8, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0x1, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xc0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xe0, + 0x3, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xc0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0xe0, + 0x2, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xc0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x5, 0x0, 0x15, 0x90, + 0x1a, 0x0, 0xe4, 0xa0, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveDecEnvDecl("sha/vs_WaveDecEnv.inl", vs_WaveDecEnv, vs_WaveDecEnvByteLen, vs_WaveDecEnvCodes); + +static const plShaderRegister vs_WaveDecEnvRegister(&vs_WaveDecEnvDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDecEnv_7.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDecEnv_7.h new file mode 100644 index 00000000..2487597b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveDecEnv_7.h @@ -0,0 +1,474 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveDecEnv_7ByteLen = 1752; + +static const UInt8 vs_WaveDecEnv_7Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x1, 0x80, + 0x8, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x2, 0x80, + 0x9, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x12, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x16, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x17, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x18, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x15, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x16, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1e, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1f, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x7, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x19, 0x0, 0x0, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x1d, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x1d, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xc, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xe, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x90, + 0xf, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0xb, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0x90, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x7, 0x80, + 0x7, 0x0, 0x9, 0x80, + 0x9, 0x0, 0x52, 0x90, + 0x4, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x7, 0x80, + 0x7, 0x0, 0x52, 0x80, + 0x9, 0x0, 0x9, 0x91, + 0x5, 0x0, 0xa4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x23, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x24, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x20, 0x0, 0xe4, 0xa1, + 0x2, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x7, 0x0, 0x0, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x24, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x25, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x21, 0x0, 0xe4, 0xa1, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x22, 0x0, 0xe4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xaa, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x2, 0x80, + 0x8, 0x0, 0xaa, 0x81, + 0x2, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x4, 0x80, + 0x9, 0x0, 0xaa, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0x90, + 0x8, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0xe4, 0x90, + 0x8, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x8, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0x90, + 0x8, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x2, 0x80, + 0x8, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0xe4, 0x90, + 0x8, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x4, 0x80, + 0x8, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0x90, + 0x8, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x2, 0x80, + 0x9, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0xe4, 0x90, + 0x8, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x4, 0x80, + 0x9, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x1b, 0x0, 0xe4, 0xa1, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x1c, 0x0, 0xe4, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x2, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x1c, 0x0, 0xff, 0xa1, + 0x7, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x55, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x4, 0x80, + 0xa, 0x0, 0x55, 0x80, + 0x9, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0x80, + 0x1c, 0x0, 0xa4, 0xa1, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xa4, 0x80, + 0x9, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x0, 0x0, 0x0, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x8, 0x80, + 0x0, 0x0, 0x55, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xaa, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x8, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0x1, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xc0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xe0, + 0x3, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xc0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0xe0, + 0x2, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xc0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x5, 0x0, 0x15, 0x90, + 0x1a, 0x0, 0xe4, 0xa0, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveDecEnv_7Decl("sha/vs_WaveDecEnv_7.inl", vs_WaveDecEnv_7, vs_WaveDecEnv_7ByteLen, vs_WaveDecEnv_7Codes); + +static const plShaderRegister vs_WaveDecEnv_7Register(&vs_WaveDecEnv_7Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveFixedFin6.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveFixedFin6.h new file mode 100644 index 00000000..cb406049 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveFixedFin6.h @@ -0,0 +1,518 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveFixedFin6ByteLen = 1928; + +static const UInt8 vs_WaveFixedFin6Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x15, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x9, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x19, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x1a, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x1b, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x18, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x19, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0xe4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x20, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xc, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0xaa, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0xb, 0x0, 0x54, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0xc, 0x0, 0x0, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x2, 0x0, 0x0, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x2, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x3, 0x80, + 0x7, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x10, 0x0, 0xa0, 0xa0, + 0x7, 0x0, 0xa4, 0x81, + 0x8, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf2, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0xc, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x10, 0x0, 0xaa, 0xa1, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x2, 0xa0, + 0x7, 0x0, 0x8a, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x8, 0xa0, + 0x7, 0x0, 0x9a, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x11, 0x0, 0xe4, 0xa1, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x13, 0x0, 0xe4, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x2, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x13, 0x0, 0xff, 0xa1, + 0x7, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x55, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x4, 0x80, + 0xa, 0x0, 0x55, 0x80, + 0x9, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0x80, + 0x13, 0x0, 0xa4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x0, 0x0, 0x0, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x8, 0x80, + 0x0, 0x0, 0x55, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xaa, 0x81, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf4, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf2, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x3, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf4, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf2, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x3, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf4, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf2, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x3, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xc, 0x80, + 0x10, 0x0, 0x8a, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xc0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0xe0, + 0x2, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xc0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xe0, + 0x3, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xc0, 0x80, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x1c, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x1c, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x4, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xa, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xb, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x11, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xe4, 0x81, + 0x8, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xaa, 0x90, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0x0, 0x81, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xff, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xff, 0x80, + 0x10, 0x0, 0x55, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x15, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x7, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xff, 0x80, + 0x5, 0x0, 0x0, 0x90, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xd0, + 0x1, 0x0, 0xe4, 0x80, + 0x14, 0x0, 0xe4, 0xa0, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveFixedFin6Decl("sha/vs_WaveFixedFin6.inl", vs_WaveFixedFin6, vs_WaveFixedFin6ByteLen, vs_WaveFixedFin6Codes); + +static const plShaderRegister vs_WaveFixedFin6Register(&vs_WaveFixedFin6Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveFixedFin7.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveFixedFin7.h new file mode 100644 index 00000000..23b4f80b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveFixedFin7.h @@ -0,0 +1,505 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveFixedFin7ByteLen = 1876; + +static const UInt8 vs_WaveFixedFin7Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x15, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x9, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x19, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x1a, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x1b, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x18, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x19, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x1d, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x1, 0x80, + 0x6, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x1e, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x2, 0x80, + 0x6, 0x0, 0x55, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x22, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x23, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x1, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x1f, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x0, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x23, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x2, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x24, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x2, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x20, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x2, 0x80, + 0x2, 0x0, 0x55, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x1f, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x4, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x20, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x4, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x21, 0x0, 0xe4, 0xa1, + 0x2, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x4, 0x80, + 0x3, 0x0, 0xaa, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x11, 0x0, 0xe4, 0xa1, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x6, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x8, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x8, 0x80, + 0x5, 0x0, 0xff, 0x80, + 0xb, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x8, 0x80, + 0x5, 0x0, 0xff, 0x80, + 0xb, 0x0, 0x55, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x8, 0x80, + 0x5, 0x0, 0xff, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x8, 0x80, + 0x5, 0x0, 0xff, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x8, 0x80, + 0x5, 0x0, 0xff, 0x80, + 0x5, 0x0, 0xff, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x8, 0x80, + 0x5, 0x0, 0xff, 0x80, + 0xb, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x13, 0x0, 0xe4, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x2, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x13, 0x0, 0xff, 0xa1, + 0x7, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x55, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x4, 0x80, + 0xa, 0x0, 0x55, 0x80, + 0x9, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0x80, + 0x13, 0x0, 0xa4, 0xa1, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xa4, 0x80, + 0x9, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x0, 0x0, 0x0, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x8, 0x80, + 0x0, 0x0, 0x55, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xaa, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xc, 0x80, + 0x10, 0x0, 0x8a, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xff, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xd0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xaa, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xff, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0xe0, + 0x2, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xd0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x2, 0x80, + 0x2, 0x0, 0xaa, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xff, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xe0, + 0x3, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xd0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0x4, 0x80, + 0x3, 0x0, 0xaa, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x1c, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x1c, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0x0, 0x90, + 0xa, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x90, + 0xa, 0x0, 0x0, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x5, 0x0, 0xe4, 0x81, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xaa, 0x90, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0x0, 0x81, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xff, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xff, 0x80, + 0x10, 0x0, 0x55, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x15, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xff, 0x80, + 0x5, 0x0, 0x0, 0x90, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xff, 0x80, + 0x4, 0x0, 0xff, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x1, 0x0, 0xe4, 0x80, + 0x14, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xd0, + 0x4, 0x0, 0xe4, 0xa0, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveFixedFin7Decl("sha/vs_WaveFixedFin7.inl", vs_WaveFixedFin7, vs_WaveFixedFin7ByteLen, vs_WaveFixedFin7Codes); + +static const plShaderRegister vs_WaveFixedFin7Register(&vs_WaveFixedFin7Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveGraph2.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveGraph2.h new file mode 100644 index 00000000..2ef001dc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveGraph2.h @@ -0,0 +1,188 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveGraph2ByteLen = 608; + +static const UInt8 vs_WaveGraph2Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x0, 0x80, + 0x3, 0x0, 0xf, 0x90, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0x0, 0x90, + 0x0, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0xa0, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x5, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xaa, 0xa0, + 0x4, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xff, 0xa0, + 0x4, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x2, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x2, 0x80, + 0x6, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x1, 0x80, + 0x0, 0x0, 0x0, 0x90, + 0x0, 0x0, 0x55, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x1, 0x80, + 0x5, 0x0, 0x0, 0x80, + 0x0, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xc, 0x80, + 0x0, 0x0, 0xa8, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0x5, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xf3, 0x90, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xe0, + 0x5, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xf3, 0x90, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x1, 0x80, + 0x5, 0x0, 0x0, 0x80, + 0x6, 0x0, 0x55, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x2, 0x80, + 0x3, 0x0, 0x0, 0x90, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x3, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xff, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x2, 0x80, + 0x6, 0x0, 0x0, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x55, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0x5, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x7, 0x0, 0xe4, 0xa0, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveGraph2Decl("sha/vs_WaveGraph2.inl", vs_WaveGraph2, vs_WaveGraph2ByteLen, vs_WaveGraph2Codes); + +static const plShaderRegister vs_WaveGraph2Register(&vs_WaveGraph2Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveGridFin.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveGridFin.h new file mode 100644 index 00000000..a53d50dc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveGridFin.h @@ -0,0 +1,468 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveGridFinByteLen = 1728; + +static const UInt8 vs_WaveGridFinCodes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0x55, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x55, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x9, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0xe4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x20, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf2, 0xa0, + 0xc, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x6, 0x0, 0xf, 0x80, + 0x9, 0x0, 0xe4, 0x80, + 0xc, 0x0, 0xa5, 0xa0, + 0x6, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0xc, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x2, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x2, 0x0, 0x0, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x3, 0x80, + 0x7, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x20, 0xa0, + 0x7, 0x0, 0xa4, 0x81, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x2, 0xa0, + 0x7, 0x0, 0x8a, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x8, 0xa0, + 0x7, 0x0, 0x9a, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x11, 0x0, 0xe4, 0xa1, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x13, 0x0, 0xe4, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x2, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x13, 0x0, 0xff, 0xa1, + 0x7, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x55, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x4, 0x80, + 0xa, 0x0, 0x55, 0x80, + 0x9, 0x0, 0x0, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xaa, 0x80, + 0x13, 0x0, 0xa4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x0, 0x0, 0x0, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x8, 0x80, + 0x0, 0x0, 0x55, 0x81, + 0x1, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x8, 0x80, + 0x0, 0x0, 0xaa, 0x81, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf4, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf2, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x3, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf4, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf2, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x3, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf4, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x12, 0x0, 0xf2, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x3, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x8, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xe0, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xc0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0xe0, + 0x2, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xc0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0xe0, + 0x3, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xc0, 0x80, + 0x14, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x4, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xa, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0xb, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xc, 0x80, + 0x10, 0x0, 0x80, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x11, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xe4, 0x81, + 0x8, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0x0, 0x81, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xff, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0xff, 0x80, + 0x10, 0x0, 0x55, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xd0, + 0x1, 0x0, 0xe4, 0x80, + 0x14, 0x0, 0xe4, 0xa0, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveGridFinDecl("sha/vs_WaveGridFin.inl", vs_WaveGridFin, vs_WaveGridFinByteLen, vs_WaveGridFinCodes); + +static const plShaderRegister vs_WaveGridFinRegister(&vs_WaveGridFinDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveRip.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveRip.h new file mode 100644 index 00000000..1ca61713 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveRip.h @@ -0,0 +1,372 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveRipByteLen = 1344; + +static const UInt8 vs_WaveRipCodes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x19, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x9, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1e, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x1f, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x20, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x1d, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x1e, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x7, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0xe4, 0xa1, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0xe4, 0xa1, + 0x1, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x20, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0x80, + 0x8, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0xc, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0xaa, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0xb, 0x0, 0x54, 0x80, + 0xa, 0x0, 0x0, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x23, 0x0, 0xaa, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x4, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x80, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x22, 0x0, 0x55, 0xa0, + 0x7, 0x0, 0xaa, 0x91, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x1, 0x0, 0x0, 0x80, + 0x23, 0x0, 0x55, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x22, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0x0, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0xaa, 0x80, + 0x22, 0x0, 0xff, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0xaa, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0xaa, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x1, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x4, 0x0, 0x55, 0x80, + 0x22, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x0, 0x0, 0x55, 0x80, + 0x1, 0x0, 0xd5, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x80, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x3, 0x80, + 0x1, 0x0, 0x0, 0x80, + 0x21, 0x0, 0xfd, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x3, 0x80, + 0x2, 0x0, 0x54, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x2, 0x0, 0x0, 0x80, + 0x6, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x2, 0x80, + 0x2, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x3, 0x80, + 0x2, 0x0, 0x54, 0x80, + 0x21, 0x0, 0xa8, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x3, 0x80, + 0x7, 0x0, 0x54, 0x90, + 0x10, 0x0, 0x55, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x3, 0x80, + 0x1, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x3, 0x80, + 0x1, 0x0, 0x54, 0x80, + 0x10, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0x1, 0x0, 0xe4, 0x80, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveRipDecl("sha/vs_WaveRip.inl", vs_WaveRip, vs_WaveRipByteLen, vs_WaveRipCodes); + +static const plShaderRegister vs_WaveRipRegister(&vs_WaveRipDecl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveRip7.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveRip7.h new file mode 100644 index 00000000..845b5972 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plSurface/vs_WaveRip7.h @@ -0,0 +1,338 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +static const UInt32 vs_WaveRip7ByteLen = 1208; + +static const UInt8 vs_WaveRip7Codes[] = { + 0x1, 0x1, 0xfe, 0xff, + 0x1f, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x80, + 0x0, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x0, 0x80, + 0x5, 0x0, 0xf, 0x90, + 0x1f, 0x0, 0x0, 0x0, + 0x5, 0x0, 0x0, 0x80, + 0x7, 0x0, 0xf, 0x90, + 0x15, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x7, 0x80, + 0x0, 0x0, 0xe4, 0x90, + 0x19, 0x0, 0xe4, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x8, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x8, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x9, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x55, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x6, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x0, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xaa, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0xff, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x4e, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x0, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0xf, 0x0, 0xaa, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x3, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x5, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x55, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0x55, 0xa0, + 0x0, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0x0, 0xa0, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x3, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xe4, 0x80, + 0xd, 0x0, 0xff, 0xa0, + 0x2, 0x0, 0xe4, 0x80, + 0x4, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0xe, 0x0, 0xff, 0xa0, + 0x1, 0x0, 0xe4, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x1e, 0x0, 0xe4, 0xa0, + 0x6, 0x0, 0xaa, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x1f, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x4, 0x0, 0xf, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x20, 0x0, 0xe4, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x4, 0x0, 0x7, 0x80, + 0x4, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0x5, 0x0, 0xff, 0x90, + 0x1d, 0x0, 0xe4, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0xb, 0x0, 0xf, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x7, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x1, 0x80, + 0x2, 0x0, 0xe4, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x2, 0x80, + 0x8, 0x0, 0x0, 0x80, + 0x4, 0x0, 0xaa, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x8, 0x0, 0x4, 0x80, + 0x8, 0x0, 0x55, 0x80, + 0x1e, 0x0, 0xff, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x8, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0x80, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x1, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xa, 0x0, 0xe4, 0xa0, + 0x9, 0x0, 0x0, 0x0, + 0x7, 0x0, 0x2, 0x80, + 0x1, 0x0, 0xe4, 0x80, + 0xb, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x3, 0x80, + 0x6, 0x0, 0x54, 0x80, + 0x7, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x6, 0x0, 0x4, 0x80, + 0x6, 0x0, 0xaa, 0x80, + 0x23, 0x0, 0xaa, 0xa0, + 0x14, 0x0, 0x0, 0x0, + 0x9, 0x0, 0xf, 0x80, + 0x6, 0x0, 0xe4, 0x80, + 0x0, 0x0, 0xe4, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0xa, 0x0, 0x1, 0x80, + 0x9, 0x0, 0xff, 0x80, + 0x4, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0xc0, + 0xa, 0x0, 0x0, 0x80, + 0x4, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xc0, + 0x9, 0x0, 0xe4, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x1, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x80, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x1, 0x80, + 0x22, 0x0, 0x55, 0xa0, + 0x7, 0x0, 0xaa, 0x91, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x1, 0x0, 0x0, 0x80, + 0x23, 0x0, 0x55, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x22, 0x0, 0xaa, 0xa0, + 0x1, 0x0, 0x0, 0x81, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0xaa, 0x80, + 0x22, 0x0, 0xff, 0xa0, + 0xa, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0xaa, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0xb, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x4, 0x80, + 0x1, 0x0, 0xaa, 0x80, + 0x10, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x2, 0x80, + 0x1, 0x0, 0x55, 0x80, + 0x1, 0x0, 0xaa, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x2, 0x80, + 0x4, 0x0, 0x55, 0x80, + 0x22, 0x0, 0x0, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xd0, + 0x0, 0x0, 0x55, 0x80, + 0x1, 0x0, 0xd5, 0x80, + 0x1, 0x0, 0x0, 0x0, + 0x2, 0x0, 0xf, 0x80, + 0x10, 0x0, 0x80, 0xa0, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x3, 0x80, + 0x1, 0x0, 0x0, 0x80, + 0x21, 0x0, 0xfd, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x3, 0x80, + 0x2, 0x0, 0x54, 0x80, + 0x10, 0x0, 0xaa, 0xa0, + 0x6, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x1, 0x80, + 0x2, 0x0, 0x0, 0x80, + 0x6, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x2, 0x80, + 0x2, 0x0, 0x55, 0x80, + 0x5, 0x0, 0x0, 0x0, + 0x2, 0x0, 0x3, 0x80, + 0x2, 0x0, 0x54, 0x80, + 0x21, 0x0, 0xa8, 0xa0, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x3, 0x80, + 0x7, 0x0, 0x54, 0x90, + 0x10, 0x0, 0x55, 0xa1, + 0x5, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x3, 0x80, + 0x1, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x54, 0x80, + 0x2, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x3, 0x80, + 0x1, 0x0, 0x54, 0x80, + 0x10, 0x0, 0x55, 0xa0, + 0x1, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xf, 0xe0, + 0x1, 0x0, 0xe4, 0x80, + 0xff, 0xff, 0x0, 0x0 + }; + +static const plShaderDecl vs_WaveRip7Decl("sha/vs_WaveRip7.inl", vs_WaveRip7, vs_WaveRip7ByteLen, vs_WaveRip7Codes); + +static const plShaderRegister vs_WaveRip7Register(&vs_WaveRip7Decl); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsAffineParts.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsAffineParts.cpp new file mode 100644 index 00000000..ae9eb19a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsAffineParts.cpp @@ -0,0 +1,426 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "hsAffineParts.h" +#include "../plInterp/hsInterp.h" +#include "hsStream.h" + +#include "plProfile.h" + +#define PL_OPTIMIZE_COMPOSE + +inline void QuatTo3Vectors(const hsQuat& q, hsVector3* const v) +{ + v[0][0] = 1.0f - 2.0f*q.fY*q.fY - 2.0f*q.fZ*q.fZ; + v[0][1] = 2.0f*q.fX*q.fY - 2.0f*q.fW*q.fZ; + v[0][2] = 2.0f*q.fX*q.fZ + 2.0f*q.fW*q.fY; + + v[1][0] = 2.0f*q.fX*q.fY + 2.0f*q.fW*q.fZ; + v[1][1] = 1.0f - 2.0f*q.fX*q.fX - 2.0f*q.fZ*q.fZ; + v[1][2] = 2.0f*q.fY*q.fZ - 2.0f*q.fW*q.fX; + + v[2][0] = 2.0f*q.fX*q.fZ - 2.0f*q.fW*q.fY; + v[2][1] = 2.0f*q.fY*q.fZ + 2.0f*q.fW*q.fX; + v[2][2] = 1.0f - 2.0f*q.fX*q.fX - 2.0f*q.fY*q.fY; +} + +inline void QuatTo3VectorsTranspose(const hsQuat& q, hsVector3* const v) +{ + v[0][0] = 1.0f - 2.0f*q.fY*q.fY - 2.0f*q.fZ*q.fZ; + v[1][0] = 2.0f*q.fX*q.fY - 2.0f*q.fW*q.fZ; + v[2][0] = 2.0f*q.fX*q.fZ + 2.0f*q.fW*q.fY; + + v[0][1] = 2.0f*q.fX*q.fY + 2.0f*q.fW*q.fZ; + v[1][1] = 1.0f - 2.0f*q.fX*q.fX - 2.0f*q.fZ*q.fZ; + v[2][1] = 2.0f*q.fY*q.fZ - 2.0f*q.fW*q.fX; + + v[0][2] = 2.0f*q.fX*q.fZ - 2.0f*q.fW*q.fY; + v[1][2] = 2.0f*q.fY*q.fZ + 2.0f*q.fW*q.fX; + v[2][2] = 1.0f - 2.0f*q.fX*q.fX - 2.0f*q.fY*q.fY; +} + +// +// Constructors +// Convert from Gems struct for now +// +hsAffineParts::hsAffineParts(gemAffineParts *ap) +{ + AP_SET((*this), (*ap)); +} + +// +// +// +hsAffineParts::hsAffineParts() +{ + +} + +// +// +// +void hsAffineParts::Reset() +{ + fT.Set(0,0,0); + fQ.Identity(); + fU.Identity(); + fK.Set(1,1,1); + fF = 1.0; +} + +plProfile_CreateTimer("Compose", "Affine", Compose); +plProfile_CreateTimer("ComposeInv", "Affine", ComposeInv); +// +// Create an affine matrix from the various parts +// +// AffineParts: +// Vector t; /* Translation components */ +// Quat q; /* Essential rotation */ +// Quat u; /* Stretch rotation */ +// Vector k; /* Stretch factors */ +// float f; /* Sign of determinant */ +// +// A matrix M is decomposed by : M = T F R U K Utranspose. +// T is the translate mat. +// F is +-Identity (to flip the rotation or not). +// R is the rot matrix. +// U is the stretch matrix. +// K is the scale factor matrix. +// +void hsAffineParts::ComposeMatrix(hsMatrix44 *out) const +{ + plProfile_BeginTiming(Compose); +#ifndef PL_OPTIMIZE_COMPOSE + // Built U matrix + hsMatrix44 U; + fU.MakeMatrix(&U); + + // Build scale factor matrix + hsMatrix44 K; + K.MakeScaleMat(&fK); + + // Build Utranspose matrix + hsMatrix44 Utp; + U.GetTranspose(&Utp); + + // Build R matrix + hsMatrix44 R; + fQ.MakeMatrix(&R); + + // Build flip matrix +// hsAssert(fF == 1.0 || fF == -1.0, "Invalid flip portion of affine parts"); + hsMatrix44 F; + if (fF==-1.0) + { + hsVector3 s; + s.Set(-1,-1,-1); + F.MakeScaleMat(&s); + } + else + F.Reset(); + + // Build translate matrix + hsMatrix44 T; + T.MakeTranslateMat(&fT); + + // + // Concat mats + // + *out = K * Utp; + *out = U * (*out); + *out = R * (*out); // Q + *out = F * (*out); + *out = T * (*out); // Translate happens last +#else // PL_OPTIMIZE_COMPOSE + // M = T F R U K Ut, + // but these are mostly very sparse matrices. So rather + // than construct the full 6 matrices and concatenate them, + // we'll work out by hand what the non-zero results will be. + // T = |1 0 0 Tx| + // |0 1 0 Ty| + // |0 0 1 Tz| + // F = |f 0 0 0| + // |0 f 0 0| + // |0 0 f 0|, where f is either 1 or -1 + // R = |R00 R01 R02 0| + // |R10 R11 R12 0| + // |R20 R21 R22 0| + // U = |U00 U01 U02 0| + // |U10 U11 U12 0| + // |U20 U21 U22 0| + // K = |Sx 0 0 0| + // |0 Sy 0 0| + // |0 0 Sz 0| + // Ut = |U00 U10 U20 0| + // |U01 U11 U21 0| + // |U02 U12 U22 0|, where Uij is from matrix U + // + // So, K * Ut = + // |Sx*U00 Sx*U10 Sx*U20 0| + // |Sy*U01 Sy*U11 Sy*U21 0| + // |Sz*U02 Sz*U12 Sz*U22 0| + // + // U * (K * Ut) = + // | U0 dot S*U0 U0 dot S*U1 U0 dot S*U2 0| + // | U1 dot S*U0 U1 dot S*U1 U1 dot S*U2 0| + // | U2 dot S*U0 U2 dot S*U1 U2 dot S*U2 0| + // + // Let's call that matrix UK + // + // Now R * U * K * Ut = R * UK = + // | R0 dot UKc0 R0 dot UKc1 R0 dot UKc2 0| + // | R1 dot UKc0 R1 dot UKc1 R1 dot UKc2 0| + // | R2 dot UKc0 R2 dot UKc1 R2 dot UKc2 0|, where UKci is column i from UK + // + // if f is -1, we negate the matrix we have so far, else we don't. We can + // accomplish this cleanly by just negating the scale vector S if f == -1. + // + // Since the translate is last, we can just stuff it into the 4th column. + // + // Since we only ever use UK as column vectors, we'll just construct it + // into 3 vectors representing the columns. + // + // The quat MakeMatrix function is pretty efficient, but it does a little more work + // than it has to filling out the whole matrix when we only need the 3x3 rotation, + // and we'd rather have it in the form of vectors anyway, so we'll use our own + // quat to 3 vectors function here. + + hsVector3 U[3]; + QuatTo3Vectors(fU, U); + + int i, j; + + hsVector3 UKt[3]; + for( i = 0; i < 3; i++ ) + { + for( j = 0; j < 3; j++ ) + { + // SU[j] = (fK.fX * U[j].fX, fK.fY * U[j].fY, fK.fZ * U[j].fZ) + UKt[j][i] = U[i].fX * fK.fX * U[j].fX + + U[i].fY * fK.fY * U[j].fY + + U[i].fZ * fK.fZ * U[j].fZ; + } + } + + hsVector3 R[3]; + QuatTo3Vectors(fQ, R); + + hsScalar f = fF < 0 ? -1.f : 1.f; + for( i = 0; i < 3; i++ ) + { + for( j = 0; j < 3; j++ ) + { + out->fMap[i][j] = R[i].InnerProduct(UKt[j]) * f; + } + + out->fMap[i][3] = fT[i]; + } + + out->fMap[3][0] = out->fMap[3][1] = out->fMap[3][2] = 0.f; + out->fMap[3][3] = 1.f; + out->NotIdentity(); + +#endif // PL_OPTIMIZE_COMPOSE + plProfile_EndTiming(Compose); +} + +void hsAffineParts::ComposeInverseMatrix(hsMatrix44 *out) const +{ + plProfile_BeginTiming(Compose); +#ifndef PL_OPTIMIZE_COMPOSE + // Built U matrix + hsMatrix44 U; + fU.Conjugate().MakeMatrix(&U); + + // Build scale factor matrix + hsMatrix44 K; + hsVector3 invK; + invK.Set(hsScalarInvert(fK.fX),hsScalarInvert(fK.fY),hsScalarInvert(fK.fZ)); + K.MakeScaleMat(&invK); + + // Build Utranspose matrix + hsMatrix44 Utp; + U.GetTranspose(&Utp); + + // Build R matrix + hsMatrix44 R; + fQ.Conjugate().MakeMatrix(&R); + + // Build flip matrix +// hsAssert(fF == 1.0 || fF == -1.0, "Invalid flip portion of affine parts"); + hsMatrix44 F; + if (fF==-1.0) + { + hsVector3 s; + s.Set(-1,-1,-1); + F.MakeScaleMat(&s); + } + else + F.Reset(); + + // Build translate matrix + hsMatrix44 T; + T.MakeTranslateMat(&-fT); + + // + // Concat mats + // + *out = Utp * K; + *out = (*out) * U; + *out = (*out) * R; + *out = (*out) * F; + *out = (*out) * T; +#else // PL_OPTIMIZE_COMPOSE + // Same kind of thing here, except now M = Ut K U R F T + // and again + // T = |1 0 0 Tx| + // |0 1 0 Ty| + // |0 0 1 Tz| + // F = |f 0 0 0| + // |0 f 0 0| + // |0 0 f 0|, where f is either 1 or -1 + // R = |R00 R01 R02 0| + // |R10 R11 R12 0| + // |R20 R21 R22 0| + // U = |U00 U01 U02 0| + // |U10 U11 U12 0| + // |U20 U21 U22 0| + // K = |Sx 0 0 0| + // |0 Sy 0 0| + // |0 0 Sz 0| + // Ut = |U00 U10 U20 0| + // |U01 U11 U21 0| + // |U02 U12 U22 0|, where Uij is from matrix U + // + // So, Ut * K = + // |U00*Sx U10*Sy U20*Sz 0| + // |U01*Sx U11*Sy U21*Sz 0| + // |U02*Sx U12*Sy U22*Sz 0| + // + // (Ut * K) * U = UK = + // |Ut0*S dot Ut0 Ut0*S dot Ut1 Ut0*S dot Ut2 0| + // |Ut1*S dot Ut0 Ut1*S dot Ut1 Ut1*S dot Ut2 0| + // |Ut2*S dot Ut0 Ut2*S dot Ut1 Ut2*S dot Ut2 0| + // + // (((Ut * K) * U) * R)[i][j] = UK[i] dot Rc[j] + // + // Again we'll stuff the flip into the scale. + // + // Now, because the T is on the other end of the concat (closest + // to the vertex), we can't just stuff it in. If Mr is the + // rotation part of the final matrix (Ut * K * U * R * F), then + // the translation components M[i][3] = Mr[i] dot T. + // + // + hsVector3 Ut[3]; + QuatTo3VectorsTranspose(fU.Conjugate(), Ut); + + int i, j; + + hsVector3 invK; + invK.Set(hsScalarInvert(fK.fX),hsScalarInvert(fK.fY),hsScalarInvert(fK.fZ)); + hsVector3 UK[3]; + for( i = 0; i < 3; i++ ) + { + for( j = 0; j < 3; j++ ) + { + // SUt[i] = (Ut[i].fX * invK.fX, Ut[i].fY * invK.fY, Ut[i].fZ * invK.fZ) + // So SUt[i].InnerProduct(Ut[j]) == + // Ut[i].fX * invK.fX * Ut[j].fX + // + Ut[i].fY * invK.fY * Ut[j].fY + // + Ut[i].fZ * invK.fZ * Ut[j].fZ + + UK[i][j] = Ut[i].fX * invK.fX * Ut[j].fX + + Ut[i].fY * invK.fY * Ut[j].fY + + Ut[i].fZ * invK.fZ * Ut[j].fZ; + } + } + + hsVector3 Rt[3]; + QuatTo3VectorsTranspose(fQ.Conjugate(), Rt); + + hsScalar f = fF < 0 ? -1.f : 1.f; + for( i = 0; i < 3; i++ ) + { + for( j = 0; j < 3; j++ ) + { + out->fMap[i][j] = UK[i].InnerProduct(Rt[j]) * f; + } + + out->fMap[i][3] = -(fT.InnerProduct((hsPoint3*)(&out->fMap[i]))); + } + + out->fMap[3][0] = out->fMap[3][1] = out->fMap[3][2] = 0.f; + out->fMap[3][3] = 1.f; + out->NotIdentity(); + +#endif // PL_OPTIMIZE_COMPOSE + plProfile_EndTiming(Compose); +} + +// +// Given 2 affineparts structs and a p value (between 0-1), +// compute a new affine parts. +// +void hsAffineParts::SetFromInterp(const hsAffineParts &ap1, const hsAffineParts &ap2, float p) +{ + hsAssert(p>=0.0 && p<=1.0, "Interpolate param must be 0-1"); + +#if 0 + // Debug + float rad1,rad2, rad3; + hsVector3 axis1, axis2, axis3; + k1->fQ.GetAngleAxis(&rad1, &axis1); + k2->fQ.GetAngleAxis(&rad2, &axis2); + fQ.GetAngleAxis(&rad3, &axis3); +#endif + + hsInterp::LinInterp(&ap1, &ap2, p, this); +} + +// +// Read +// +void hsAffineParts::Read(hsStream *stream) +{ + fT.Read(stream); + fQ.Read(stream); + fU.Read(stream); + fK.Read(stream); + fF = stream->ReadSwapFloat(); +} + +// +// Write +// +void hsAffineParts::Write(hsStream *stream) +{ + fT.Write(stream); + fQ.Write(stream); + fU.Write(stream); + fK.Write(stream); + stream->WriteSwapFloat(fF); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsAffineParts.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsAffineParts.h new file mode 100644 index 00000000..7365e5d3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsAffineParts.h @@ -0,0 +1,73 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef HSAFFINEPARTS_inc +#define HSAFFINEPARTS_inc + +#include "hsGeometry3.h" +#include "hsQuat.h" + +#include "mat_decomp.h" + +class hsAffineParts +{ + +public: + // Constructors + hsAffineParts(gemAffineParts *); // Convert from Gems struct for now + hsAffineParts(); + + void Reset(); + + hsVector3 fT; /* Translation components */ + hsQuat fQ; /* Essential rotation */ + hsQuat fU; /* Stretch rotation */ + hsVector3 fK; /* Stretch factors */ + float fF; /* Sign of determinant */ + + void ComposeMatrix(hsMatrix44 *out) const; + void ComposeInverseMatrix(hsMatrix44 *out) const; + void SetFromInterp(const hsAffineParts &ap1, const hsAffineParts &ap2, float t); + + void Read(hsStream *); + void Write(hsStream *); + + int operator==(const hsAffineParts& a) const + { return (fT == a.fT && fQ == a.fQ && fU == a.fU && fK == a.fK && fF == a.fF); } +}; + +// +// General set macro can also be used for 3DSMax struct +// +#define AP_SET(dst, src) \ +{ \ + dst.fT.Set(src.t.x, src.t.y, src.t.z); \ + dst.fQ.Set(src.q.x, src.q.y, src.q.z, src.q.w); \ + dst.fU.Set(src.u.x, src.u.y, src.u.z, src.u.w); \ + dst.fK.Set(src.k.x, src.k.y, src.k.z); \ + dst.fF = src.f; \ +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsEuler.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsEuler.cpp new file mode 100644 index 00000000..d80ca3b4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsEuler.cpp @@ -0,0 +1,212 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// +////////////////////////////////////////////////////////////////////////// +// EULER STUFF +// See Gems IV, Ken Shoemake +////////////////////////////////////////////////////////////////////////// +// +#include // for FLT_EPSILON +#include "hsEuler.h" +#include "hsQuat.h" +#include "hsMatrix44.h" + +enum QuatPart +{ + X, Y, Z, W +}; + +// +// Construct quaternion from Euler angles (in radians). +// +void hsEuler::GetQuat(hsQuat* qu) +{ + double a[3], ti, tj, th, ci, cj, ch, si, sj, sh, cc, cs, sc, ss; + int i,j,k,h,n,s,f; + + hsEuler ea=*this; // copy + EulGetOrd(ea.fOrder,i,j,k,h,n,s,f); + if (f==EulFrmR) + { + hsScalar t = ea.fX; ea.fX = ea.fZ; ea.fZ = t; + } + if (n==EulParOdd) + ea.fY = -ea.fY; + ti = ea.fX*0.5; tj = ea.fY*0.5; th = ea.fZ*0.5; + ci = cos(ti); cj = cos(tj); ch = cos(th); + si = sin(ti); sj = sin(tj); sh = sin(th); + cc = ci*ch; cs = ci*sh; sc = si*ch; ss = si*sh; + if (s==EulRepYes) + { + a[i] = cj*(cs + sc); /* Could speed up with */ + a[j] = sj*(cc + ss); /* trig identities. */ + a[k] = sj*(cs - sc); + qu->fW = static_cast(cj*(cc - ss)); + } + else + { + a[i] = cj*sc - sj*cs; + a[j] = cj*ss + sj*cc; + a[k] = cj*cs - sj*sc; + qu->fW = static_cast(cj*cc + sj*ss); + } + if (n==EulParOdd) + a[j] = -a[j]; + qu->fX = static_cast(a[X]); + qu->fY = static_cast(a[Y]); + qu->fZ = static_cast(a[Z]); +} + +// +// Construct matrix from Euler angles (in radians). +// +void hsEuler::GetMatrix44(hsMatrix44* mat) +{ + double ti, tj, th, ci, cj, ch, si, sj, sh, cc, cs, sc, ss; + int i,j,k,h,n,s,f; + + hsEuler ea=*this; // copy + EulGetOrd(ea.fOrder,i,j,k,h,n,s,f); + if (f==EulFrmR) + { + hsScalar t = ea.fX; ea.fX = ea.fZ; ea.fZ = t; + } + if (n==EulParOdd) + { + ea.fX = -ea.fX; ea.fY = -ea.fY; ea.fZ = -ea.fZ; + } + ti = ea.fX; tj = ea.fY; th = ea.fZ; + ci = cos(ti); cj = cos(tj); ch = cos(th); + si = sin(ti); sj = sin(tj); sh = sin(th); + cc = ci*ch; cs = ci*sh; sc = si*ch; ss = si*sh; + if (s==EulRepYes) + { + mat->fMap[i][i] = static_cast(cj); + mat->fMap[i][j] = static_cast(sj*si); + mat->fMap[i][k] = static_cast(sj*ci); + + mat->fMap[j][i] = static_cast(sj*sh); + mat->fMap[j][j] = static_cast(-cj*ss+cc); + mat->fMap[j][k] = static_cast(-cj*cs-sc); + + mat->fMap[k][i] = static_cast(-sj*ch); + mat->fMap[k][j] = static_cast(cj*sc+cs); + mat->fMap[k][k] = static_cast(cj*cc-ss); + } + else + { + mat->fMap[i][i] = static_cast(cj*ch); + mat->fMap[i][j] = static_cast(sj*sc-cs); + mat->fMap[i][k] = static_cast(sj*cc+ss); + + mat->fMap[j][i] = static_cast(cj*sh); + mat->fMap[j][j] = static_cast(sj*ss+cc); + mat->fMap[j][k] = static_cast(sj*cs-sc); + + mat->fMap[k][i] = static_cast(-sj); + mat->fMap[k][j] = static_cast(cj*si); + mat->fMap[k][k] = static_cast(cj*ci); + } + mat->fMap[W][X]=mat->fMap[W][Y]=mat->fMap[W][Z]=mat->fMap[X][W]=mat->fMap[Y][W]=mat->fMap[Z][W]=0.0; + mat->fMap[W][W]=1.0; +} + +// +// Convert matrix to Euler angles (in radians) +// +void hsEuler::SetFromMatrix44(const hsMatrix44* mat, UInt32 order) +{ + int i,j,k,h,n,s,f; + + EulGetOrd(order,i,j,k,h,n,s,f); + if (s==EulRepYes) + { + double sy = sqrt(mat->fMap[i][j]*mat->fMap[i][j] + mat->fMap[i][k]*mat->fMap[i][k]); + if (sy > 16*FLT_EPSILON) + { + fX = static_cast(atan2(mat->fMap[i][j], mat->fMap[i][k])); + fY = static_cast(atan2(sy, (double)mat->fMap[i][i])); + fZ = static_cast(atan2(mat->fMap[j][i], -mat->fMap[k][i])); + } else + { + fX = static_cast(atan2(-mat->fMap[j][k], mat->fMap[j][j])); + fY = static_cast(atan2(sy, (double)mat->fMap[i][i])); + fZ = 0; + } + } + else + { + double cy = sqrt(mat->fMap[i][i]*mat->fMap[i][i] + mat->fMap[j][i]*mat->fMap[j][i]); + if (cy > 16*FLT_EPSILON) + { + fX = static_cast(atan2(mat->fMap[k][j], mat->fMap[k][k])); + fY = static_cast(atan2((double)(-mat->fMap[k][i]), cy)); + fZ = static_cast(atan2(mat->fMap[j][i], mat->fMap[i][i])); + } + else + { + fX = static_cast(atan2(-mat->fMap[j][k], mat->fMap[j][j])); + fY = static_cast(atan2((double)(-mat->fMap[k][i]), cy)); + fZ = 0; + } + } + if (n==EulParOdd) + { + fX = -fX; fY = - fY; fZ = -fZ; + } + if (f==EulFrmR) + { + hsScalar t = fX; fX = fZ; fZ = t; + } + fOrder = order; +} + +// +// Convert quaternion to Euler angles (in radians) +// +void hsEuler::SetFromQuat(const hsQuat* q, UInt32 order) +{ + hsMatrix44 mat; + double Nq = q->fX*q->fX+q->fY*q->fY+q->fZ*q->fZ+q->fW*q->fW; + double s = (Nq > 0.0) ? (2.0 / Nq) : 0.0; + double xs = q->fX*s, ys = q->fY*s, zs = q->fZ*s; + double wx = q->fW*xs, wy = q->fW*ys, wz = q->fW*zs; + double xx = q->fX*xs, xy = q->fX*ys, xz = q->fX*zs; + double yy = q->fY*ys, yz = q->fY*zs, zz = q->fZ*zs; + mat.fMap[X][X] = static_cast(1.0 - (yy + zz)); + mat.fMap[X][Y] = static_cast(xy - wz); + mat.fMap[X][Z] = static_cast(xz + wy); + mat.fMap[Y][X] = static_cast(xy + wz); + mat.fMap[Y][Y] = static_cast(1.0 - (xx + zz)); + mat.fMap[Y][Z] = static_cast(yz - wx); + mat.fMap[Z][X] = static_cast(xz - wy); + mat.fMap[Z][Y] = static_cast(yz + wx); + mat.fMap[Z][Z] = static_cast(1.0 - (xx + yy)); + mat.fMap[W][X] = mat.fMap[W][Y] = mat.fMap[W][Z] = + mat.fMap[X][W] = mat.fMap[Y][W] = mat.fMap[Z][W] = 0.0; + mat.fMap[W][W] = 1.0; + SetFromMatrix44(&mat, order); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsEuler.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsEuler.h new file mode 100644 index 00000000..bc9d71bb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/hsEuler.h @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef HS_EULER_inc +#define HS_EULER_inc + +#include "hsGeometry3.h" + +// +////////////////////////////////////////////////////////////////////////// +// EULER STUFF +// See Gems IV, Ken Shoemake +////////////////////////////////////////////////////////////////////////// +// + +/*** Order type constants, constructors, extractors ***/ + /* There are 24 possible conventions, designated by: */ + /* o EulAxI = axis used initially */ + /* o EulPar = parity of axis permutation */ + /* o EulRep = repetition of initial axis as last */ + /* o EulFrm = frame from which axes are taken */ + /* Axes I,J,K will be a permutation of X,Y,Z. */ + /* Axis H will be either I or K, depending on EulRep. */ + /* Frame S takes axes from initial static frame. */ + /* If ord = (AxI=X, Par=Even, Rep=No, Frm=S), then */ + /* {a,b,c,ord} means Rz(c)Ry(b)Rx(a), where Rz(c)v */ + /* rotates v around Z by c radians. */ +#define EulFrmS 0 +#define EulFrmR 1 +#define EulFrm(ord) ((unsigned)(ord)&1) +#define EulRepNo 0 +#define EulRepYes 1 +#define EulRep(ord) (((unsigned)(ord)>>1)&1) +#define EulParEven 0 +#define EulParOdd 1 +#define EulPar(ord) (((unsigned)(ord)>>2)&1) +#define EulSafe "\000\001\002\000" +#define EulNext "\001\002\000\001" +#define EulAxI(ord) ((int)(EulSafe[(((unsigned)(ord)>>3)&3)])) +#define EulAxJ(ord) ((int)(EulNext[EulAxI(ord)+(EulPar(ord)==EulParOdd)])) +#define EulAxK(ord) ((int)(EulNext[EulAxI(ord)+(EulPar(ord)!=EulParOdd)])) +#define EulAxH(ord) ((EulRep(ord)==EulRepNo)?EulAxK(ord):EulAxI(ord)) + /* EulGetOrd unpacks all useful information about order simultaneously. */ +#define EulGetOrd(ord,i,j,k,h,n,s,f) {unsigned o=ord;f=o&1;o>>=1;s=o&1;o>>=1;\ + n=o&1;o>>=1;i=EulSafe[o&3];j=EulNext[i+n];k=EulNext[i+1-n];h=s?k:i;} + /* EulOrd creates an order value between 0 and 23 from 4-tuple choices. */ +#define EulOrd(i,p,r,f) (((((((i)<<1)+(p))<<1)+(r))<<1)+(f)) + /* Static axes */ +#define EulOrdXYZs EulOrd(X,EulParEven,EulRepNo,EulFrmS) +#define EulOrdXYXs EulOrd(X,EulParEven,EulRepYes,EulFrmS) +#define EulOrdXZYs EulOrd(X,EulParOdd,EulRepNo,EulFrmS) +#define EulOrdXZXs EulOrd(X,EulParOdd,EulRepYes,EulFrmS) +#define EulOrdYZXs EulOrd(Y,EulParEven,EulRepNo,EulFrmS) +#define EulOrdYZYs EulOrd(Y,EulParEven,EulRepYes,EulFrmS) +#define EulOrdYXZs EulOrd(Y,EulParOdd,EulRepNo,EulFrmS) +#define EulOrdYXYs EulOrd(Y,EulParOdd,EulRepYes,EulFrmS) +#define EulOrdZXYs EulOrd(Z,EulParEven,EulRepNo,EulFrmS) +#define EulOrdZXZs EulOrd(Z,EulParEven,EulRepYes,EulFrmS) +#define EulOrdZYXs EulOrd(Z,EulParOdd,EulRepNo,EulFrmS) +#define EulOrdZYZs EulOrd(Z,EulParOdd,EulRepYes,EulFrmS) + /* Rotating axes */ +#define EulOrdZYXr EulOrd(X,EulParEven,EulRepNo,EulFrmR) +#define EulOrdXYXr EulOrd(X,EulParEven,EulRepYes,EulFrmR) +#define EulOrdYZXr EulOrd(X,EulParOdd,EulRepNo,EulFrmR) +#define EulOrdXZXr EulOrd(X,EulParOdd,EulRepYes,EulFrmR) +#define EulOrdXZYr EulOrd(Y,EulParEven,EulRepNo,EulFrmR) +#define EulOrdYZYr EulOrd(Y,EulParEven,EulRepYes,EulFrmR) +#define EulOrdZXYr EulOrd(Y,EulParOdd,EulRepNo,EulFrmR) +#define EulOrdYXYr EulOrd(Y,EulParOdd,EulRepYes,EulFrmR) +#define EulOrdYXZr EulOrd(Z,EulParEven,EulRepNo,EulFrmR) +#define EulOrdZXZr EulOrd(Z,EulParEven,EulRepYes,EulFrmR) +#define EulOrdXYZr EulOrd(Z,EulParOdd,EulRepNo,EulFrmR) +#define EulOrdZYZr EulOrd(Z,EulParOdd,EulRepYes,EulFrmR) + +struct hsMatrix44; +class hsQuat; +class hsEuler +{ +public: + hsScalar fX,fY,fZ; + UInt32 fOrder; + + hsEuler(hsScalar ai, hsScalar aj, hsScalar ah, UInt32 order) : fX(ai),fY(aj),fZ(ah),fOrder(order) {} + + // getters, converters + void GetQuat(hsQuat* res ); + void GetMatrix44(hsMatrix44* M); + + // setters, converters + void SetFromMatrix44(const hsMatrix44* M, UInt32 order); + void SetFromQuat(const hsQuat* q, UInt32 order); +}; + +#endif // HS_EULER_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/mat_decomp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/mat_decomp.cpp new file mode 100644 index 00000000..e28ea69f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/mat_decomp.cpp @@ -0,0 +1,536 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/**** Decompose.c ****/ +/* Ken Shoemake, 1993 */ +// +// Gems IV. Polar Decomp +// +#include +#include "mat_decomp.h" +#ifdef __MWERKS__ +//#pragma optimization_level 0 +#endif + +/******* Matrix Preliminaries *******/ + +/** Fill out 3x3 matrix to 4x4 **/ +#define mat_pad(A) (A[W][X]=A[X][W]=A[W][Y]=A[Y][W]=A[W][Z]=A[Z][W]=0,A[W][W]=1) + +/** Copy nxn matrix A to C using "gets" for assignment **/ +#define mat_copy(C,gets,A,n) {int i,j; for(i=0;i= 0.0) { + s = sqrt(tr + mat[W][W]); + qu.w = static_cast(s*0.5); + s = 0.5 / s; + qu.x = static_cast((mat[Z][Y] - mat[Y][Z]) * s); + qu.y = static_cast((mat[X][Z] - mat[Z][X]) * s); + qu.z = static_cast((mat[Y][X] - mat[X][Y]) * s); + } else { + int h = X; + if (mat[Y][Y] > mat[X][X]) h = Y; + if (mat[Z][Z] > mat[h][h]) h = Z; + switch (h) { +#define caseMacro(i,j,k,I,J,K) \ + case I:\ + s = sqrt( (mat[I][I] - (mat[J][J]+mat[K][K])) + mat[W][W] );\ + qu.i = static_cast(s*0.5);\ + s = 0.5 / s;\ + qu.j = static_cast((mat[I][J] + mat[J][I]) * s);\ + qu.k = static_cast((mat[K][I] + mat[I][K]) * s);\ + qu.w = static_cast((mat[K][J] - mat[J][K]) * s);\ + break + caseMacro(x,y,z,X,Y,Z); + caseMacro(y,z,x,Y,Z,X); + caseMacro(z,x,y,Z,X,Y); + } + } + if (mat[W][W] != 1.0) qu = Qt_Scale(qu, static_cast(1/sqrt(mat[W][W]))); + return (qu); +} +/******* Decomp Auxiliaries *******/ + +static HMatrix mat_id = {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}; + +/** Compute either the 1 or infinity norm of M, depending on tpose **/ +float mat_norm(HMatrix M, int tpose) +{ + int i; + float sum, max; + max = 0.0; + for (i=0; i<3; i++) { + if (tpose) sum = static_cast(fabs(M[0][i])+fabs(M[1][i])+fabs(M[2][i])); + else sum = static_cast(fabs(M[i][0])+fabs(M[i][1])+fabs(M[i][2])); + if (maxmax) {max = abs; col = j;} + } + return col; +} + +/** Setup u for Household reflection to zero all v components but first **/ +void make_reflector(float *v, float *u) +{ + float s = static_cast(sqrt(vdot(v, v))); + u[0] = v[0]; u[1] = v[1]; + u[2] = v[2] + ((v[2]<0.0) ? -s : s); + s = static_cast(sqrt(2.0/vdot(u, u))); + u[0] = u[0]*s; u[1] = u[1]*s; u[2] = u[2]*s; +} + +/** Apply Householder reflection represented by u to column vectors of M **/ +void reflect_cols(HMatrix M, float *u) +{ + int i, j; + for (i=0; i<3; i++) { + float s = u[0]*M[0][i] + u[1]*M[1][i] + u[2]*M[2][i]; + for (j=0; j<3; j++) M[j][i] -= u[j]*s; + } +} +/** Apply Householder reflection represented by u to row vectors of M **/ +void reflect_rows(HMatrix M, float *u) +{ + int i, j; + for (i=0; i<3; i++) { + float s = vdot(u, M[i]); + for (j=0; j<3; j++) M[i][j] -= u[j]*s; + } +} + +/** Find orthogonal factor Q of rank 1 (or less) M **/ +void do_rank1(HMatrix M, HMatrix Q) +{ + float v1[3], v2[3], s; + int col; + mat_copy(Q,=,mat_id,4); + /* If rank(M) is 1, we should find a non-zero column in M */ + col = find_max_col(M); + if (col<0) return; /* Rank is 0 */ + v1[0] = M[0][col]; v1[1] = M[1][col]; v1[2] = M[2][col]; + make_reflector(v1, v1); reflect_cols(M, v1); + v2[0] = M[2][0]; v2[1] = M[2][1]; v2[2] = M[2][2]; + make_reflector(v2, v2); reflect_rows(M, v2); + s = M[2][2]; + if (s<0.0) Q[2][2] = -1.0; + reflect_cols(Q, v1); reflect_rows(Q, v2); +} + +/** Find orthogonal factor Q of rank 2 (or less) M using adjoint transpose **/ +void do_rank2(HMatrix M, HMatrix MadjT, HMatrix Q) +{ + float v1[3], v2[3]; + float w, x, y, z, c, s, d; + int col; + /* If rank(M) is 2, we should find a non-zero column in MadjT */ + col = find_max_col(MadjT); + if (col<0) {do_rank1(M, Q); return;} /* Rank<2 */ + v1[0] = MadjT[0][col]; v1[1] = MadjT[1][col]; v1[2] = MadjT[2][col]; + make_reflector(v1, v1); reflect_cols(M, v1); + vcross(M[0], M[1], v2); + make_reflector(v2, v2); reflect_rows(M, v2); + w = M[0][0]; x = M[0][1]; y = M[1][0]; z = M[1][1]; + if (w*z>x*y) { + c = z+w; s = y-x; d = static_cast(sqrt(c*c+s*s)); c = c/d; s = s/d; + Q[0][0] = Q[1][1] = c; Q[0][1] = -(Q[1][0] = s); + } else { + c = z-w; s = y+x; d = static_cast(sqrt(c*c+s*s)); c = c/d; s = s/d; + Q[0][0] = -(Q[1][1] = c); Q[0][1] = Q[1][0] = s; + } + Q[0][2] = Q[2][0] = Q[1][2] = Q[2][1] = 0.0; Q[2][2] = 1.0; + reflect_cols(Q, v1); reflect_rows(Q, v2); +} + + +/******* Polar Decomposition *******/ + +/* Polar Decomposition of 3x3 matrix in 4x4, + * M = QS. See Nicholas Higham and Robert S. Schreiber, + * Fast Polar Decomposition of An Arbitrary Matrix, + * Technical Report 88-942, October 1988, + * Department of Computer Science, Cornell University. + */ +float polar_decomp(const HMatrix M, HMatrix Q, HMatrix S) +{ +#define TOL 1.0e-6 + HMatrix Mk, MadjTk, Ek; + float det, M_one, M_inf, MadjT_one, MadjT_inf, E_one, gamma, g1, g2; + int i, j; + mat_tpose(Mk,=,M,3); + M_one = norm_one(Mk); M_inf = norm_inf(Mk); + do { + adjoint_transpose(Mk, MadjTk); + det = vdot(Mk[0], MadjTk[0]); + if (det==0.0) {do_rank2(Mk, MadjTk, Mk); break;} + MadjT_one = norm_one(MadjTk); MadjT_inf = norm_inf(MadjTk); + gamma = static_cast(sqrt(sqrt((MadjT_one*MadjT_inf)/(M_one*M_inf))/fabs(det))); + g1 = gamma*0.5f; + g2 = 0.5f/(gamma*det); + mat_copy(Ek,=,Mk,3); + mat_binop(Mk,=,g1*Mk,+,g2*MadjTk,3); + mat_copy(Ek,-=,Mk,3); + E_one = norm_one(Ek); + M_one = norm_one(Mk); M_inf = norm_inf(Mk); + } while (E_one>(M_one*TOL)); + mat_tpose(Q,=,Mk,3); mat_pad(Q); + mat_mult(Mk, M, S); mat_pad(S); + for (i=0; i<3; i++) for (j=i; j<3; j++) + S[i][j] = S[j][i] = 0.5f*(S[i][j]+S[j][i]); + return (det); +} + + + + + + + + + + + + + + + + + +/******* Spectral Decomposition *******/ + +/* Compute the spectral decomposition of symmetric positive semi-definite S. + * Returns rotation in U and scale factors in result, so that if K is a diagonal + * matrix of the scale factors, then S = U K (U transpose). Uses Jacobi method. + * See Gene H. Golub and Charles F. Van Loan. Matrix Computations. Hopkins 1983. + */ +HVect spect_decomp(HMatrix S, HMatrix U) +{ + HVect kv; + double Diag[3],OffD[3]; /* OffD is off-diag (by omitted index) */ + double g,h,fabsh,fabsOffDi,t,theta,c,s,tau,ta,OffDq,a,b; + static char nxt[] = {Y,Z,X}; + int sweep, i, j; + mat_copy(U,=,mat_id,4); + Diag[X] = S[X][X]; Diag[Y] = S[Y][Y]; Diag[Z] = S[Z][Z]; + OffD[X] = S[Y][Z]; OffD[Y] = S[Z][X]; OffD[Z] = S[X][Y]; + for (sweep=20; sweep>0; sweep--) { + float sm = static_cast(fabs(OffD[X])+fabs(OffD[Y])+fabs(OffD[Z])); + if (sm==0.0) break; + for (i=Z; i>=X; i--) { + int p = nxt[i]; int q = nxt[p]; + fabsOffDi = fabs(OffD[i]); + g = 100.0*fabsOffDi; + if (fabsOffDi>0.0) { + h = Diag[q] - Diag[p]; + fabsh = fabs(h); + if (fabsh+g==fabsh) { + t = OffD[i]/h; + } else { + theta = 0.5*h/OffD[i]; + t = 1.0/(fabs(theta)+sqrt(theta*theta+1.0)); + if (theta<0.0) t = -t; + } + c = 1.0/sqrt(t*t+1.0); s = t*c; + tau = s/(c+1.0); + ta = t*OffD[i]; OffD[i] = 0.0; + Diag[p] -= ta; Diag[q] += ta; + OffDq = OffD[q]; + OffD[q] -= s*(OffD[p] + tau*OffD[q]); + OffD[p] += s*(OffDq - tau*OffD[p]); + for (j=Z; j>=X; j--) { + a = U[j][p]; b = U[j][q]; + U[j][p] -= static_cast(s*(b + tau*a)); + U[j][q] += static_cast(s*(a - tau*b)); + } + } + } + } + kv.x = static_cast(Diag[X]); + kv.y = static_cast(Diag[Y]); + kv.z = static_cast(Diag[Z]); + kv.w = 1.0f; + return (kv); +} + +/******* Spectral Axis Adjustment *******/ + +/* Given a unit quaternion, q, and a scale vector, k, find a unit quaternion, p, + * which permutes the axes and turns freely in the plane of duplicate scale + * factors, such that q p has the largest possible w component, i.e. the + * smallest possible angle. Permutes k's components to go with q p instead of q. + * See Ken Shoemake and Tom Duff. Matrix Animation and Polar Decomposition. + * Proceedings of Graphics Interface 1992. Details on p. 262-263. + */ +gemQuat snuggle(gemQuat q, HVect *k) +{ +#define SQRTHALF (0.7071067811865475244f) +#define sgn(n,v) ((n)?-(v):(v)) +#define swap(a,i,j) {a[3]=a[i]; a[i]=a[j]; a[j]=a[3];} +#define cycle(a,p) if (p) {a[3]=a[0]; a[0]=a[1]; a[1]=a[2]; a[2]=a[3];}\ + else {a[3]=a[2]; a[2]=a[1]; a[1]=a[0]; a[0]=a[3];} + gemQuat p; + float ka[4]; + int i, turn = -1; + ka[X] = k->x; ka[Y] = k->y; ka[Z] = k->z; + if (ka[X]==ka[Y]) {if (ka[X]==ka[Z]) turn = W; else turn = Z;} + else {if (ka[X]==ka[Z]) turn = Y; else if (ka[Y]==ka[Z]) turn = X;} + if (turn>=0) { + gemQuat qtoz, qp; + unsigned neg[3], win; + double mag[3], t; + static gemQuat qxtoz = {0,SQRTHALF,0,SQRTHALF}; + static gemQuat qytoz = {SQRTHALF,0,0,SQRTHALF}; + static gemQuat qppmm = { 0.5, 0.5,-0.5,-0.5}; + static gemQuat qpppp = { 0.5, 0.5, 0.5, 0.5}; + static gemQuat qmpmm = {-0.5, 0.5,-0.5,-0.5}; + static gemQuat qpppm = { 0.5, 0.5, 0.5,-0.5}; + static gemQuat q0001 = { 0.0, 0.0, 0.0, 1.0}; + static gemQuat q1000 = { 1.0, 0.0, 0.0, 0.0}; + switch (turn) { + default: return (Qt_Conj(q)); + case X: q = Qt_Mul(q, qtoz = qxtoz); swap(ka,X,Z) break; + case Y: q = Qt_Mul(q, qtoz = qytoz); swap(ka,Y,Z) break; + case Z: qtoz = q0001; break; + } + q = Qt_Conj(q); + mag[0] = (double)q.z*q.z+(double)q.w*q.w-0.5; + mag[1] = (double)q.x*q.z-(double)q.y*q.w; + mag[2] = (double)q.y*q.z+(double)q.x*q.w; + for (i=0; i<3; i++) if (neg[i] = (mag[i]<0.0)) mag[i] = -mag[i]; + if (mag[0]>mag[1]) {if (mag[0]>mag[2]) win = 0; else win = 2;} + else {if (mag[1]>mag[2]) win = 1; else win = 2;} + switch (win) { + case 0: if (neg[0]) p = q1000; else p = q0001; break; + case 1: if (neg[1]) p = qppmm; else p = qpppp; cycle(ka,0) break; + case 2: if (neg[2]) p = qmpmm; else p = qpppm; cycle(ka,1) break; + } + qp = Qt_Mul(q, p); + t = sqrt(mag[win]+0.5); + p = Qt_Mul(p, Qt_(0.0,0.0,static_cast(-qp.z/t),static_cast(qp.w/t))); + p = Qt_Mul(qtoz, Qt_Conj(p)); + } else { + float qa[4], pa[4]; + unsigned lo, hi, neg[4], par = 0; + double all, big, two; + qa[0] = q.x; qa[1] = q.y; qa[2] = q.z; qa[3] = q.w; + for (i=0; i<4; i++) { + pa[i] = 0.0; + if (neg[i] = (qa[i]<0.0)) qa[i] = -qa[i]; + par ^= neg[i]; + } + /* Find two largest components, indices in hi and lo */ + if (qa[0]>qa[1]) lo = 0; else lo = 1; + if (qa[2]>qa[3]) hi = 2; else hi = 3; + if (qa[lo]>qa[hi]) { + if (qa[lo^1]>qa[hi]) {hi = lo; lo ^= 1;} + else {hi ^= lo; lo ^= hi; hi ^= lo;} + } else {if (qa[hi^1]>qa[lo]) lo = hi^1;} + all = (qa[0]+qa[1]+qa[2]+qa[3])*0.5; + two = (qa[hi]+qa[lo])*SQRTHALF; + big = qa[hi]; + if (all>two) { + if (all>big) {/*all*/ + {int i; for (i=0; i<4; i++) pa[i] = static_cast(sgn(neg[i], 0.5));} + cycle(ka,par) + } else {/*big*/ pa[hi] = static_cast(sgn(neg[hi],1.0));} + } else { + if (two>big) {/*two*/ + pa[hi] = static_cast(sgn(neg[hi],SQRTHALF)); + pa[lo] = static_cast(sgn(neg[lo], SQRTHALF)); + if (lo>hi) {hi ^= lo; lo ^= hi; hi ^= lo;} + if (hi==W) {hi = "\001\002\000"[lo]; lo = 3-hi-lo;} + swap(ka,hi,lo) + } else {/*big*/ pa[hi] = static_cast(sgn(neg[hi],1.0));} + } + p.x = -pa[0]; p.y = -pa[1]; p.z = -pa[2]; p.w = pa[3]; + } + k->x = ka[X]; k->y = ka[Y]; k->z = ka[Z]; + return (p); +} + + + + + + + + + + + +/******* Decompose Affine Matrix *******/ + +/* Decompose 4x4 affine matrix A as TFRUK(U transpose), where t contains the + * translation components, q contains the rotation R, u contains U, k contains + * scale factors, and f contains the sign of the determinant. + * Assumes A transforms column vectors in right-handed coordinates. + * See Ken Shoemake and Tom Duff. Matrix Animation and Polar Decomposition. + * Proceedings of Graphics Interface 1992. + */ +void decomp_affine(const HMatrix A, gemAffineParts *parts) +{ + HMatrix Q, S, U; + gemQuat p; + float det; + parts->t = Qt_(A[X][W], A[Y][W], A[Z][W], 0); + det = polar_decomp(A, Q, S); + if (det<0.0) { + mat_copy(Q,=,-Q,3); + parts->f = -1; + } else parts->f = 1; + parts->q = Qt_FromMatrix(Q); + parts->k = spect_decomp(S, U); + parts->u = Qt_FromMatrix(U); + p = snuggle(parts->u, &parts->k); + parts->u = Qt_Mul(parts->u, p); +} + +/******* Invert Affine Decomposition *******/ + +/* Compute inverse of affine decomposition. + */ +void invert_affine(gemAffineParts *parts, gemAffineParts *inverse) +{ + gemQuat t, p; + inverse->f = parts->f; + inverse->q = Qt_Conj(parts->q); + inverse->u = Qt_Mul(parts->q, parts->u); + inverse->k.x = static_cast((parts->k.x==0.0) ? 0.0 : 1.0/parts->k.x); + inverse->k.y = static_cast((parts->k.y==0.0) ? 0.0 : 1.0/parts->k.y); + inverse->k.z = static_cast((parts->k.z==0.0) ? 0.0 : 1.0/parts->k.z); + inverse->k.w = parts->k.w; + t = Qt_(-parts->t.x, -parts->t.y, -parts->t.z, 0); + t = Qt_Mul(Qt_Conj(inverse->u), Qt_Mul(t, inverse->u)); + t = Qt_(inverse->k.x*t.x, inverse->k.y*t.y, inverse->k.z*t.z, 0); + p = Qt_Mul(inverse->q, inverse->u); + t = Qt_Mul(p, Qt_Mul(t, Qt_Conj(p))); + inverse->t = (inverse->f>0.0) ? t : Qt_(-t.x, -t.y, -t.z, 0); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/mat_decomp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/mat_decomp.h new file mode 100644 index 00000000..56edff96 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/mat_decomp.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef MATDECOMP_inc +#define MATDECOMP_inc + +// +// Types +// +typedef struct {float x, y, z, w;} gemQuat; /* Quaternion */ + +enum QuatPart {X, Y, Z, W}; + +typedef gemQuat HVect; /* Homogeneous 3D vector */ + +typedef float HMatrix[4][4]; /* Right-handed, for column vectors */ + +typedef struct { + HVect t; /* Translation components */ + gemQuat q; /* Essential rotation */ + gemQuat u; /* Stretch rotation */ + HVect k; /* Stretch factors */ + float f; /* Sign of determinant */ +} gemAffineParts; + +// +// Funcs +// +float polar_decomp(const HMatrix M, HMatrix Q, HMatrix S); +HVect spect_decomp(const HMatrix S, HMatrix U); +gemQuat snuggle(gemQuat q, HVect *k); +void decomp_affine(const HMatrix A, gemAffineParts *parts); +void invert_affine(gemAffineParts *parts, gemAffineParts *inverse); + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/notes.txt b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/notes.txt new file mode 100644 index 00000000..98003340 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plTransform/notes.txt @@ -0,0 +1 @@ +-Didn't include hsGTransform.cpp,h because it references hsSceneObject, and plTMController. \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID.cpp new file mode 100644 index 00000000..da05f303 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID.cpp @@ -0,0 +1,105 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plUUID.h" +#include "hsStream.h" + +COMPILER_ASSERT(msizeof(Uuid, data) == msizeof(plUUID, fData)); + +plUUID::plUUID() +{ + Clear(); +} + +plUUID::plUUID( const char * s ) +{ + FromString( s ); +} + +plUUID::plUUID( const plUUID & other ) +{ + CopyFrom( &other ); +} + +plUUID::plUUID( const Uuid & uuid ) +{ + MemCopy(fData, uuid.data, sizeof(fData)); +} + +void plUUID::Read( hsStream * s) +{ + s->LogSubStreamPushDesc("plUUID"); + s->Read( sizeof( fData ), (void*)fData ); +} + +void plUUID::Write( hsStream * s) +{ + s->Write( sizeof( fData ), (const void*)fData ); +} + +plUUID::operator Uuid () const { + Uuid uuid; + MemCopy(uuid.data, fData, sizeof(uuid.data)); + return uuid; +} + +const char * plUUID::AsString() const { + static std::string str; + ToStdString(str); + return str.c_str(); +} + +void plUUID::CopyFrom( const plUUID * v ) { + if (!v) + Clear(); + else + CopyFrom(*v); +} + +void plUUID::CopyFrom( const plUUID & v ) { + MemCopy(fData, v.fData, sizeof(fData)); +} + +/***************************************************************************** +* +* plCreatableUuid +* +***/ + +//============================================================================ +plCreatableUuid::plCreatableUuid () { +} + +//============================================================================ +plCreatableUuid::plCreatableUuid (const plCreatableUuid & other) +: plUUID(other) +{ +} + +//============================================================================ +plCreatableUuid::plCreatableUuid (const plUUID & other) +: plUUID(other) +{ +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID.h new file mode 100644 index 00000000..7c05d7e0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID.h @@ -0,0 +1,88 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plUUID_h_inc +#define plUUID_h_inc + +#include "hsTypes.h" +#include "hsStlUtils.h" +#include "../pnUtils/pnUtils.h" +#include "../pnFactory/plCreatable.h" + +class hsStream; + +class plUUID +{ + // must be first field in class +public: + UInt8 fData[16]; + struct Match + { + const plUUID * fGuid; + Match( const plUUID * guid ):fGuid( guid ){} + bool operator()( const plUUID * guid ) const { return guid->IsEqualTo( fGuid );} + }; + + plUUID(); + plUUID( const char * s ); + plUUID( const plUUID & other ); + plUUID( const Uuid & uuid ); + void Clear(); + bool IsNull() const; + bool IsSet() const { return !IsNull(); } + void CopyFrom( const plUUID * v ); + void CopyFrom( const plUUID & v ); + int CompareTo( const plUUID * v ) const; + bool IsEqualTo( const plUUID * v ) const; + bool FromString( const char * str ); + bool ToStdString( std::string & out ) const; + inline std::string AsStdString() const { return AsString(); } + const char * AsString() const; // returns static buffer + void Read( hsStream * s ); + void Write( hsStream * s ); + operator std::string ( void ) const { return AsStdString();} + bool operator==( const plUUID & other ) const { return IsEqualTo( &other ); } + bool operator!=( const plUUID & other ) const { return !IsEqualTo( &other ); } + int operator <( const plUUID & other ) const { return CompareTo( &other ); } + operator Uuid () const; + + static plUUID Generate(); +}; + +class plCreatableUuid : public plUUID, public plCreatable { +public: + CLASSNAME_REGISTER( plCreatableUuid ); + GETINTERFACE_ANY( plCreatableUuid, plCreatable ); + + plCreatableUuid (); + plCreatableUuid (const plCreatableUuid & other); + plCreatableUuid (const plUUID & other); + + void Read( hsStream * s, hsResMgr* ) { plUUID::Read(s); } + void Write( hsStream * s, hsResMgr* ) { plUUID::Write(s); } +}; + + +#endif // plUUID_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID_Unix.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID_Unix.cpp new file mode 100644 index 00000000..59dd4e5a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID_Unix.cpp @@ -0,0 +1,128 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plUUID.h" + +#ifdef HS_BUILD_FOR_UNIX + +#include + +struct plUUIDHelper +{ + static inline void CopyToPlasma( plUUID * dst, const uuid_t & src ) + { + hsAssert( sizeof(uuid_t)==sizeof(dst->fData), "sizeof(uuid_t)!=sizeof(plUUID)" ); + memcpy( (void*)dst->fData, (const void *)src, sizeof( plUUID ) ); + } + + static inline void CopyToNative( uuid_t & dst, const plUUID * src ) + { + hsAssert( sizeof(uuid_t)==sizeof(src->fData), "sizeof(uuid_t)!=sizeof(plUUID)" ); + memcpy( (void*)dst, (const void *)src->fData, sizeof( plUUID ) ); + } +}; + + +void plUUID::Clear() +{ + uuid_t g; + plUUIDHelper::CopyToNative( g, this ); + uuid_clear( g ); + plUUIDHelper::CopyToPlasma( this, g ); +} + +bool plUUID::IsNull() const +{ + uuid_t g; + plUUIDHelper::CopyToNative( g, this ); + return ( uuid_is_null( g )!=0 ); +} + +void plUUID::CopyFrom( const plUUID * v ) +{ + memcpy( (void*)fData, (const void*)v->fData, sizeof(fData) ); +} + +int plUUID::CompareTo( const plUUID * v ) const +{ + uuid_t ga, gb; + plUUIDHelper::CopyToNative( ga, this ); + plUUIDHelper::CopyToNative( gb, v ); + int ans = uuid_compare( ga, gb ); + return ans; +} + +bool plUUID::IsEqualTo( const plUUID * v ) const +{ + return ( CompareTo( v )==0 ); +} + +bool plUUID::FromString( const char * str ) +{ + Clear(); + if ( !str ) + return false; + uuid_t g; + uuid_parse( str, g ); + plUUIDHelper::CopyToPlasma( this, g ); + return true; +} + +bool plUUID::ToString( std::string & out ) const +{ + uuid_t g; + plUUIDHelper::CopyToNative( g, this ); + char buf[40]; + uuid_unparse( g, buf ); + out = buf; + return true; +} + +std::string plUUID::ToString() const +{ + std::string str; + plUUID::ToString( str ); + return str; +} + +// static +plUUID plUUID::Generate() +{ + uuid_t g; + uuid_generate( g ); + plUUID result; + plUUIDHelper::CopyToPlasma( &result, g ); + return result; +} + +#else + +// dummy function to prevent a linker warning complaining about no public symbols if the +// contents of the file get compiled out via pre-processor +void _preventLNK4221WarningStub() +{ +} + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID_Win32.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID_Win32.cpp new file mode 100644 index 00000000..62d81f95 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUUID/plUUID_Win32.cpp @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plUUID.h" + +#ifdef HS_BUILD_FOR_WIN32 + +#include "hsUtils.h" +#include "hsWindows.h" +#include + +void plUUID::Clear() +{ + UuidCreateNil( (GUID *)this ); +} + +int plUUID::CompareTo( const plUUID * v ) const +{ + RPC_STATUS s; + return UuidCompare( (GUID *)this, (GUID *)v, &s ); +} + +bool plUUID::IsEqualTo( const plUUID * v ) const +{ + return ( CompareTo( v )==0 ); +} + +bool plUUID::IsNull() const +{ + RPC_STATUS s; + return 1 == UuidIsNil( (GUID *)this, &s ); +} + +bool plUUID::FromString( const char * str ) +{ + Clear(); + if ( !str ) + return false; + return RPC_S_OK == UuidFromString( (unsigned char *)str, (GUID *)this ); +} + +bool plUUID::ToStdString( std::string & out ) const +{ + out = ""; + unsigned char * ubuf; + RPC_STATUS s; + s = UuidToString( (GUID *) this, &ubuf ); + bool success = ( s==RPC_S_OK ); + if ( success ) + out = (char*)ubuf; + RpcStringFree( &ubuf ); + return success; +} + +// static +plUUID plUUID::Generate() +{ + hsAssert(sizeof(plUUID) >= sizeof(GUID), "plUUID size"); + plUUID result; + UuidCreate( (GUID *)&result ); + return result; +} + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plClientUnifiedTime.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plClientUnifiedTime.cpp new file mode 100644 index 00000000..ea76c2a5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plClientUnifiedTime.cpp @@ -0,0 +1,120 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "hsTimer.h" +#include "hsTemplates.h" +#include "plClientUnifiedTime.h" + +#if 0 +#include "../plNetClient/plNetObjectDebugger.h" +#include "../plNetClient/plNetClientMgr.h" +#endif + +// static +plUnifiedTime plClientUnifiedTime::fFrameStartTime = plUnifiedTime::GetCurrentTime(); // the 'current time' at the start of each time +double plClientUnifiedTime::fSysTimeOffset = 0.0; + +// +// static, called once at the start of each frame +// +void plClientUnifiedTime::SetSysTime() +{ + fFrameStartTime.ToCurrentTime(); + + if (fSysTimeOffset == 0.0) + fSysTimeOffset = hsTimer::GetSysSeconds() - fFrameStartTime.GetSecsDouble(); +} + +// +// convert from game clock to unified time +// +#pragma optimize( "g", off ) // disable global optimizations +void plClientUnifiedTime::SetFromGameTime(double gameTime, double curGameSecs) +{ + hsDoublePrecBegin; + //double gameTimeOff = curGameSecs-gameTime; // when did this happen relative to our currrent sysTime + //*this = GetFrameStartTime() - plUnifiedTime(gameTimeOff); + SetSecsDouble(gameTime - fSysTimeOffset); + hsDoublePrecEnd; + +#if 0 + extern bool gMooseDump; + if (gMooseDump) + { + plUnifiedTime ct = plUnifiedTime::GetCurrentTime(); + plUnifiedTime ft = GetFrameStartTime(); + + plNetObjectDebugger::GetInstance()->LogMsg(hsTempStringF("SFGT: CT=%s\n", ct.PrintWMillis())); + plNetObjectDebugger::GetInstance()->LogMsg(hsTempStringF("SFGT: FT=%s\n", ft.PrintWMillis())); + plNetObjectDebugger::GetInstance()->LogMsg(hsTempStringF("SFGT: gt=%f secs in the past\n", gameTimeOff)); + plNetObjectDebugger::GetInstance()->LogMsg(hsTempStringF("SFGT: this=%s\n\n", PrintWMillis())); + } +#endif +} + +// +// convert from unified time to game clock +// +void plClientUnifiedTime::ConvertToGameTime(double* gameTimeOut, double curGameSecs) +{ + hsDoublePrecBegin; + //plUnifiedTime utOff = GetFrameStartTime() - GetAsUnifiedTime(); // compute offset relative to current startFrame time + //*gameTimeOut = curGameSecs - utOff.GetSecsDouble(); + *gameTimeOut = GetSecsDouble() + fSysTimeOffset; + hsDoublePrecEnd; + +#if 0 + extern bool gMooseDump; + if (gMooseDump) + { + plUnifiedTime ct = plUnifiedTime::GetCurrentTime(); + plUnifiedTime ft = GetFrameStartTime(); + + plNetObjectDebugger::GetInstance()->LogMsg( hsTempStringF("CTGT: this=%s\n", PrintWMillis())); + plNetObjectDebugger::GetInstance()->LogMsg(hsTempStringF("CTGT: CT=%s\n", ct.PrintWMillis())); + plNetObjectDebugger::GetInstance()->LogMsg(hsTempStringF("CTGT: FT=%s\n", ft.PrintWMillis())); + plNetObjectDebugger::GetInstance()->LogMsg(hsTempStringF("CTGT: OT=%s\n", utOff.PrintWMillis())); + plNetObjectDebugger::GetInstance()->LogMsg( + hsTempStringF("CTGT: ct=%f TO=%f\n\n", curGameSecs, *gameTimeOut)); + } +#endif +} +#pragma optimize( "", on ) // restore optimizations to their defaults + +const plClientUnifiedTime & plClientUnifiedTime::operator=(const plUnifiedTime & src) +{ + plUnifiedTime* ut=this; + *ut=src; + return *this; +} + +const plClientUnifiedTime & plClientUnifiedTime::operator=(const plClientUnifiedTime & src) +{ + plUnifiedTime* ut=this; + plUnifiedTime* utSrc=this; + *ut=*utSrc; + return *this; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plClientUnifiedTime.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plClientUnifiedTime.h new file mode 100644 index 00000000..63ddc8a1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plClientUnifiedTime.h @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plClientUnifiedTime_inc +#define plClientUnifiedTime_inc + +#include "plUnifiedTime.h" + +// +// class based on UnifiedTime which has some specialized client code in it as well. +// WARNING: plUnifiedTime math operators will not work correctly unless +// you convert this object using GetAsUnifiedTIme(). I decided not to +// recreate all the plUnifiedTime operators in this class. +// +class plClientUnifiedTime : public plUnifiedTime +{ +private: + static plUnifiedTime fFrameStartTime; + static double fSysTimeOffset; +public: + plClientUnifiedTime(plUnifiedTime ut) { *this=ut; } + plClientUnifiedTime() {} + + static void SetSysTime(); + static plUnifiedTime& GetFrameStartTime() { return fFrameStartTime; } + + plUnifiedTime& GetAsUnifiedTime() { return *(plUnifiedTime*)this; } + + // game secs conversions + void SetFromGameTime(double gameTime, double curGameSecs); + void ConvertToGameTime(double* gameTimeOut, double curGameSecs); + + const plClientUnifiedTime & operator=(const plUnifiedTime & src); + const plClientUnifiedTime & operator=(const plClientUnifiedTime & src); +}; + +#endif // plClientUnifiedTime_inc' + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plTimeSpan.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plTimeSpan.cpp new file mode 100644 index 00000000..b05621e7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plTimeSpan.cpp @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plTimeSpan.h" + +long plTimeSpan::GetTotalDays() const +{ + return fSecs / (24*3600L); +} + +long plTimeSpan::GetTotalHours() const +{ + return fSecs / 3600; +} + +long plTimeSpan::GetTotalMinutes() const +{ + return fSecs / 60; +} + +long plTimeSpan::GetTotalSeconds() const +{ + return fSecs; +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plTimeSpan.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plTimeSpan.h new file mode 100644 index 00000000..8c1c5fe6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plTimeSpan.h @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plTimeSpan_h_inc +#define plTimeSpan_h_inc + +#include "plUnifiedTime.h" + +class plTimeSpan : public plUnifiedTime +{ +public: + plTimeSpan():plUnifiedTime() {} + plTimeSpan(const timeval & tv):plUnifiedTime(tv) {} + plTimeSpan(time_t t):plUnifiedTime(t) {} + plTimeSpan(int year, int month, int day, int hour, int min, int sec, unsigned long usec=0, int dst=-1):plUnifiedTime(year, month, day, hour, min, sec, usec, dst) {} + plTimeSpan(const plUnifiedTime & src):plUnifiedTime(src) {} + + // get length of span + long GetTotalDays() const; + long GetTotalHours() const; + long GetTotalMinutes() const; + long GetTotalSeconds() const; +}; + + +#endif // plTimeSpan_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.cpp new file mode 100644 index 00000000..924be7b4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.cpp @@ -0,0 +1,1032 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include +#include "plUnifiedTime.h" +#include "hsStlUtils.h" +#include "hsWindows.h" + +#if HS_BUILD_FOR_UNIX +#include +#include +#endif + +#if HS_BUILD_FOR_WIN32 +#include // for timeb +#endif + +#include "hsUtils.h" + +#include +#include "hsStream.h" + + +#if HS_BUILD_FOR_WIN32 +// +// Converts Windows Time to Unified Time +// +#define MAGICWINDOWSOFFSET ((__int64)11644473600) // magic number, taken from Python Source +// +hsBool plUnifiedTime::SetFromWinFileTime(const FILETIME ft) +{ + // FILETIME resolution seems to be 0.01 sec + + __int64 ff,ffsecs; + ff = *(__int64*)(&ft); + ffsecs = ff/(__int64)10000000; + + if (ffsecs >= MAGICWINDOWSOFFSET) // make sure we won't end up negatice + { + fSecs = (UInt32)(ffsecs-MAGICWINDOWSOFFSET); + fMicros = (UInt32)(ff % 10000000)/10; + return true; + } + else + // before the UNIX Epoch + return false; +} + +// +// Sets the unified time to the current UTC time +// +hsBool plUnifiedTime::SetToUTC() +{ + FILETIME ft; + + GetSystemTimeAsFileTime(&ft); /* 100 ns blocks since 01-Jan-1641 */ + return SetFromWinFileTime(ft); +} +#elif HS_BUILD_FOR_UNIX + +// +// Sets the unified time to the current UTC time +// +hsBool plUnifiedTime::SetToUTC() +{ + struct timeval tv; + + // get the secs and micros from Jan 1, 1970 + int ret = gettimeofday(&tv, nil); + if (ret == 0) + { + fSecs = tv.tv_sec; + fMicros = tv.tv_usec; + return true; + } + else + { + return false; + } +} +#else +#error "Unified Time Not Implemented on this platform!" +#endif + + +struct tm * plUnifiedTime::IGetTime(const time_t * timer) const +{ + struct tm * tm = nil; + switch (fMode) + { + case kGmt: + tm = gmtime(timer); + break; + default: + tm = localtime(timer); + } + if ( tm ) + tm->tm_isdst = -1; + return tm; +} + +plUnifiedTime::plUnifiedTime(plUnifiedTime_CtorNow,int mode) +{ + SetMode((Mode)mode); + ToCurrentTime(); +} + + +plUnifiedTime::plUnifiedTime(const timeval & tv) +: fMode(kGmt) +{ + *this = tv; +} + +plUnifiedTime::plUnifiedTime(int mode, const struct tm & src) +: fMode((Mode)mode) +{ + *this = src; +} + +plUnifiedTime::plUnifiedTime(time_t t) +: fMode(kGmt) +{ + *this = t; +} + +plUnifiedTime::plUnifiedTime(unsigned long t) +: fMode(kGmt) +{ + *this = t; +} + +plUnifiedTime::plUnifiedTime(int year, int month, int day, int hour, int min, int sec, unsigned long usec, int dst) +: fMode(kGmt) +{ + SetTime(year,month,day,hour,min,sec,usec,dst); +} + +plUnifiedTime::plUnifiedTime(int mode, const char * buf, const char * fmt) +: fMode((Mode)mode) +{ + FromString(buf,fmt); +} + +plUnifiedTime::plUnifiedTime(const plUnifiedTime & src) +: fMode(src.fMode) +{ + *this = src; +} + +plUnifiedTime::plUnifiedTime(const plUnifiedTime * src) +: fMode(src->fMode) +{ + *this = *src; +} + +plUnifiedTime plUnifiedTime::GetCurrentTime(Mode mode) +{ + plUnifiedTime t; + t.SetMode(mode); + t.ToCurrentTime(); + return t; +} + + +const plUnifiedTime & plUnifiedTime::operator=(const plUnifiedTime & src) +{ + fSecs = src.fSecs; + fMicros = src.fMicros; + fMode = src.fMode; + return *this; +} + +const plUnifiedTime & plUnifiedTime::operator=(const plUnifiedTime * src) +{ + return operator=(*src); +} + +const plUnifiedTime & plUnifiedTime::operator=(time_t src) +{ + fSecs = src; + fMicros = 0; + return *this; +} + +const plUnifiedTime & plUnifiedTime::operator=(unsigned long src) +{ + fSecs = src; + fMicros = 0; + return *this; +} + +const plUnifiedTime & plUnifiedTime::operator=(const struct timeval & src) +{ + fSecs = src.tv_sec; + fMicros = src.tv_usec; + return *this; +} + +const plUnifiedTime & plUnifiedTime::operator=(const struct tm & src) +{ + struct tm atm = src; + fSecs = mktime(&atm); // this won't work after 2030 something, sorry + return *this; +} + +void plUnifiedTime::SetSecsDouble(double secs) +{ + hsAssert(secs>=0, "plUnifiedTime::SetSecsDouble negative time"); + double x,y; + x = modf(secs,&y); + fSecs = (UInt32)y; + fMicros = (UInt32)(x*1000000); +} + +void plUnifiedTime::FromMillis(UInt32 millis) +{ + fSecs = millis/1000; + fMicros = 0; +} + + +void plUnifiedTime::ToCurrentTime() +{ + SetToUTC(); +} + +hsBool plUnifiedTime::SetGMTime(short year, short month, short day, short hour, short minute, short second, unsigned long usec, int dst) +{ + if( !SetTime( year, month, day, hour, minute, second, usec, dst ) ) + return false; + + fSecs -= IGetLocalTimeZoneOffset(); + fMode = kGmt; + + return true; +} + +hsBool plUnifiedTime::SetTime(short year, short month, short day, short hour, short minute, short second, unsigned long usec, int dst) +{ + struct tm atm; + atm.tm_sec = second; + atm.tm_min = minute; + atm.tm_hour = hour; + atm.tm_mday = day; + atm.tm_mon = month - 1; + atm.tm_year = year - 1900; + atm.tm_isdst = dst; + fSecs = mktime(&atm); // this won't work after 2030 something, sorry + if (fSecs == -1) + return false; + if (fMicros >= 1000000) + return false; + fMicros = usec; + fMode = kLocal; + return true; +} + +hsBool plUnifiedTime::GetTime(short &year, short &month, short &day, short &hour, short &minute, short &second) const +{ + struct tm* time = IGetTime((const time_t *)&fSecs); + if (!time) + return false; + year = time->tm_year+1900; + month = time->tm_mon+1; + day = time->tm_mday; + hour = time->tm_hour; + minute = time->tm_min; + second = time->tm_sec; + return true; +} + +const char* plUnifiedTime::Print() const +{ + static std::string s; +// short year, month, day, hour, minute, second; +// GetTime(year, month, day, hour, minute, second); +// +// xtl::format(s,"yr %d mo %d day %d hour %d min %d sec %d", +// year, month, day, hour, minute, second); + + s = Format("%c"); + return s.c_str(); +} + +const char* plUnifiedTime::PrintWMillis() const +{ + static std::string s; + xtl::format(s,"%s,s:%d,ms:%d", + Print(), GetSecs(), GetMillis() ); + return s.c_str(); +} + +struct tm * plUnifiedTime::GetTm(struct tm * ptm) const +{ + if (ptm != nil) + { + *ptm = *IGetTime((const time_t *)&fSecs); + return ptm; + } + else + return IGetTime((const time_t *)&fSecs); +} + +int plUnifiedTime::GetYear() const +{ + return GetTm() ? GetTm()->tm_year + 1900 : 0; +} +int plUnifiedTime::GetMonth() const +{ + return GetTm() ? GetTm()->tm_mon + 1 : 0; +} +int plUnifiedTime::GetDay() const +{ + return GetTm() ? GetTm()->tm_mday : 0; +} +int plUnifiedTime::GetHour() const +{ + return GetTm() ? GetTm()->tm_hour : 0; +} +int plUnifiedTime::GetMinute() const +{ + return GetTm() ? GetTm()->tm_min : 0; +} +int plUnifiedTime::GetSecond() const +{ + return GetTm() ? GetTm()->tm_sec : 0; +} +int plUnifiedTime::GetDayOfWeek() const +{ + return GetTm() ? GetTm()->tm_wday : 0; +} +int plUnifiedTime::GetMillis() const +{ + return fMicros/1000; +} + +#pragma optimize( "g", off ) // disable global optimizations +double plUnifiedTime::GetSecsDouble() const +{ + hsDoublePrecBegin + double ret = GetSecs() + GetMicros() / 1000000.0; + hsDoublePrecEnd + return ret; +} +#pragma optimize( "", on ) // restore optimizations to their defaults + +UInt32 plUnifiedTime::AsMillis() +{ + return GetSecs()*1000; +} + +void plUnifiedTime::Read(hsStream* s) +{ + s->LogSubStreamStart("UnifiedTime"); + s->LogReadSwap(&fSecs,"Seconds"); + s->LogReadSwap(&fMicros,"MicroSeconds"); + s->LogSubStreamEnd(); + // preserve fMode +} + +void plUnifiedTime::Write(hsStream* s) const +{ + s->WriteSwap(fSecs); + s->WriteSwap(fMicros); + // preserve fMode +} + +const plUnifiedTime & plUnifiedTime::operator-=(const plUnifiedTime & rhs) +{ + // carry if needed + if ((*this).fMicros < rhs.fMicros) + { + (*this).fSecs--; + (*this).fMicros += 1000000; + } + (*this).fMicros -= rhs.fMicros; + (*this).fSecs -= rhs.fSecs; + return *this; +} + +const plUnifiedTime & plUnifiedTime::operator+=(const plUnifiedTime & rhs) +{ + (*this).fMicros += rhs.fMicros; + // carry if needed + if ((*this).fMicros >= 1000000) + { + (*this).fSecs++; + (*this).fMicros -= 1000000; + } + (*this).fSecs += rhs.fSecs; + return *this; +} + +bool plUnifiedTime::operator==(const plUnifiedTime & rhs) const +{ + return ((fSecs == rhs.fSecs) && (fMicros == rhs.fMicros)); +} +bool plUnifiedTime::operator!=(const plUnifiedTime & rhs) const +{ + return ((fSecs != rhs.fSecs) || (fMicros != rhs.fMicros)); +} +bool plUnifiedTime::operator <(const plUnifiedTime & rhs) const +{ + return ((fSecs < rhs.fSecs) || ((fSecs == rhs.fSecs) && (fMicros < rhs.fMicros))); +} +bool plUnifiedTime::operator >(const plUnifiedTime & rhs) const +{ + return ((fSecs > rhs.fSecs) || ((fSecs == rhs.fSecs) && (fMicros > rhs.fMicros))); +} +bool plUnifiedTime::operator<=(const plUnifiedTime & rhs) const +{ + return (*this=(const plUnifiedTime & rhs) const +{ + return (*this>rhs) || (*this==rhs); +} + + +plUnifiedTime::operator timeval() const +{ + struct timeval t = {fSecs, fMicros}; + return t; +} + + +plUnifiedTime::operator struct tm() const +{ + return *GetTm(); +} + + +std::string plUnifiedTime::Format(const char * fmt) const +{ + char buf[128]; + struct tm * t = IGetTime((const time_t *)&fSecs); + if (t == nil || + !strftime(buf, sizeof(buf), fmt, t)) + buf[0] = '\0'; + return std::string(buf); +} + + +plUnifiedTime operator -(const plUnifiedTime & left, const plUnifiedTime & right) +{ + plUnifiedTime ans = left; + ans -= right; + return ans; +} + +plUnifiedTime operator +(const plUnifiedTime & left, const plUnifiedTime & right) +{ + plUnifiedTime ans = left; + ans += right; + return ans; +} + +bool operator <(const plUnifiedTime & time, int secs) +{ + return (time.GetSecs() '9') \ + return NULL; \ + do { \ + val *= 10; \ + val += *rp++ - '0'; \ + } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \ + if (val < from || val > to) \ + return NULL; \ + } while (0) +#define recursive(new_fmt) \ + (*(new_fmt) != '\0' \ + && (rp = strptime_internal (rp, (new_fmt), tm, mode)) != NULL) + + static char const weekday_name[][10] = + { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" + }; + static char const ab_weekday_name[][4] = + { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static char const month_name[][10] = + { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + }; + static char const ab_month_name[][4] = + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; +#define HERE_D_T_FMT "%m/%d/%y %H:%M:%S" +#define HERE_D_FMT "%m/%d/%y" +#define HERE_AM_STR "AM" +#define HERE_PM_STR "PM" +#define HERE_T_FMT_AMPM "%I:%M:%S %p" +#define HERE_T_FMT "%H:%M:%S" + + const unsigned short int __mon_yday[2][13] = + { + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } + }; + + + + static struct tm * + time_r( + const time_t *t, + struct tm *tp, + int mode) + { + struct tm *l = 0; + switch (mode) + { + case plUnifiedTime::kGmt: + l = gmtime(t); + break; + default: + l = localtime(t); + } + if (! l) + return 0; + *tp = *l; + tp->tm_isdst = -1; + return tp; + } + + /* Compute the day of the week. */ + static void + day_of_the_week (struct tm *tm) + { + /* We know that January 1st 1970 was a Thursday (= 4). Compute the + the difference between this data in the one on TM and so determine + the weekday. */ + int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2); + int wday = (-473 + + (365 * (tm->tm_year - 70)) + + (corr_year / 4) + - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0) + + (((corr_year / 4) / 25) / 4) + + __mon_yday[0][tm->tm_mon] + + tm->tm_mday - 1); + tm->tm_wday = ((wday % 7) + 7) % 7; + } + + /* Compute the day of the year. */ + static void + day_of_the_year (struct tm *tm) + { + tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon] + + (tm->tm_mday - 1)); + } + + + + static char * strptime_internal( + const char * rp, + const char * fmt, + struct tm * tm, + int mode) + { + const char *rp_backup; + int cnt; + size_t val; + int have_I, is_pm; + int century, want_century; + int have_wday, want_xday; + int have_yday; + int have_mon, have_mday; + + have_I = is_pm = 0; + century = -1; + want_century = 0; + + have_wday = want_xday = have_yday = have_mon = have_mday = 0; + + while (*fmt != '\0') + { + /* A white space in the format string matches 0 more or white + space in the input string. */ + if (isspace (*fmt)) + { + while (isspace (*rp)) + ++rp; + ++fmt; + continue; + } + + /* Any character but `%' must be matched by the same character + in the iput string. */ + if (*fmt != '%') + { + match_char (*fmt++, *rp++); + continue; + } + + ++fmt; + /* We need this for handling the `E' modifier. */ +start_over: + + /* Make back up of current processing pointer. */ + rp_backup = rp; + + switch (*fmt++) + { + case '%': + /* Match the `%' character itself. */ + match_char ('%', *rp++); + break; + case 'a': + case 'A': + /* Match day of week. */ + for (cnt = 0; cnt < 7; ++cnt) + { + if (match_string (weekday_name[cnt], rp) + || match_string (ab_weekday_name[cnt], rp)) + { + break; + } + } + if (cnt == 7) + /* Does not match a weekday name. */ + return NULL; + tm->tm_wday = cnt; + have_wday = 1; + break; + case 'b': + case 'B': + case 'h': + /* Match month name. */ + for (cnt = 0; cnt < 12; ++cnt) + { + if (match_string (month_name[cnt], rp) + || match_string (ab_month_name[cnt], rp)) + { + break; + } + } + if (cnt == 12) + /* Does not match a month name. */ + return NULL; + tm->tm_mon = cnt; + want_xday = 1; + break; + case 'c': + /* Match locale's date and time format. */ + if (!recursive (HERE_D_T_FMT)) + return NULL; + want_xday = 1; + break; + case 'C': + /* Match century number. */ + get_number (0, 99, 2); + century = val; + want_xday = 1; + break; + case 'd': + case 'e': + /* Match day of month. */ + get_number (1, 31, 2); + tm->tm_mday = val; + have_mday = 1; + want_xday = 1; + break; + case 'F': + if (!recursive ("%Y-%m-%d")) + return NULL; + want_xday = 1; + break; + case 'x': + /* Fall through. */ + case 'D': + /* Match standard day format. */ + if (!recursive (HERE_D_FMT)) + return NULL; + want_xday = 1; + break; + case 'k': + case 'H': + /* Match hour in 24-hour clock. */ + get_number (0, 23, 2); + tm->tm_hour = val; + have_I = 0; + break; + case 'I': + /* Match hour in 12-hour clock. */ + get_number (1, 12, 2); + tm->tm_hour = val % 12; + have_I = 1; + break; + case 'j': + /* Match day number of year. */ + get_number (1, 366, 3); + tm->tm_yday = val - 1; + have_yday = 1; + break; + case 'm': + /* Match number of month. */ + get_number (1, 12, 2); + tm->tm_mon = val - 1; + have_mon = 1; + want_xday = 1; + break; + case 'M': + /* Match minute. */ + get_number (0, 59, 2); + tm->tm_min = val; + break; + case 'n': + case 't': + /* Match any white space. */ + while (isspace (*rp)) + ++rp; + break; + case 'p': + /* Match locale's equivalent of AM/PM. */ + if (!match_string (HERE_AM_STR, rp)) + if (match_string (HERE_PM_STR, rp)) + is_pm = 1; + else + return NULL; + break; + case 'r': + if (!recursive (HERE_T_FMT_AMPM)) + return NULL; + break; + case 'R': + if (!recursive ("%H:%M")) + return NULL; + break; + case 's': + { + /* The number of seconds may be very high so we cannot use + the `get_number' macro. Instead read the number + character for character and construct the result while + doing this. */ + time_t secs = 0; + if (*rp < '0' || *rp > '9') + /* We need at least one digit. */ + return NULL; + + do + { + secs *= 10; + secs += *rp++ - '0'; + } + while (*rp >= '0' && *rp <= '9'); + + if (time_r (&secs, tm, mode) == NULL) + /* Error in function. */ + return NULL; + } + break; + case 'S': + get_number (0, 61, 2); + tm->tm_sec = val; + break; + case 'X': + /* Fall through. */ + case 'T': + if (!recursive (HERE_T_FMT)) + return NULL; + break; + case 'u': + get_number (1, 7, 1); + tm->tm_wday = val % 7; + have_wday = 1; + break; + case 'g': + get_number (0, 99, 2); + /* XXX This cannot determine any field in TM. */ + break; + case 'G': + if (*rp < '0' || *rp > '9') + return NULL; + /* XXX Ignore the number since we would need some more + information to compute a real date. */ + do + ++rp; + while (*rp >= '0' && *rp <= '9'); + break; + case 'U': + case 'V': + case 'W': + get_number (0, 53, 2); + /* XXX This cannot determine any field in TM without some + information. */ + break; + case 'w': + /* Match number of weekday. */ + get_number (0, 6, 1); + tm->tm_wday = val; + have_wday = 1; + break; + case 'y': + /* Match year within century. */ + get_number (0, 99, 2); + /* The "Year 2000: The Millennium Rollover" paper suggests that + values in the range 69-99 refer to the twentieth century. */ + tm->tm_year = val >= 69 ? val : val + 100; + /* Indicate that we want to use the century, if specified. */ + want_century = 1; + want_xday = 1; + break; + case 'Y': + /* Match year including century number. */ + get_number (0, 9999, 4); + tm->tm_year = val - 1900; + want_century = 0; + want_xday = 1; + break; + + goto start_over; + + case 'O': + switch (*fmt++) + { + case 'd': + case 'e': + /* Match day of month using alternate numeric symbols. */ + get_number (1, 31, 2); + tm->tm_mday = val; + have_mday = 1; + want_xday = 1; + break; + case 'H': + /* Match hour in 24-hour clock using alternate numeric + symbols. */ + get_number (0, 23, 2); + tm->tm_hour = val; + have_I = 0; + break; + case 'I': + /* Match hour in 12-hour clock using alternate numeric + symbols. */ + get_number (1, 12, 2); + tm->tm_hour = val - 1; + have_I = 1; + break; + case 'm': + /* Match month using alternate numeric symbols. */ + get_number (1, 12, 2); + tm->tm_mon = val - 1; + have_mon = 1; + want_xday = 1; + break; + case 'M': + /* Match minutes using alternate numeric symbols. */ + get_number (0, 59, 2); + tm->tm_min = val; + break; + case 'S': + /* Match seconds using alternate numeric symbols. */ + get_number (0, 61, 2); + tm->tm_sec = val; + break; + case 'U': + case 'V': + case 'W': + get_number (0, 53, 2); + /* XXX This cannot determine any field in TM without + further information. */ + break; + case 'w': + /* Match number of weekday using alternate numeric symbols. */ + get_number (0, 6, 1); + tm->tm_wday = val; + have_wday = 1; + break; + case 'y': + /* Match year within century using alternate numeric symbols. */ + get_number (0, 99, 2); + tm->tm_year = val >= 69 ? val : val + 100; + want_xday = 1; + break; + default: + return NULL; + } + break; + default: + return NULL; + } + } + + if (have_I && is_pm) + tm->tm_hour += 12; + + if (century != -1) + { + if (want_century) + tm->tm_year = tm->tm_year % 100 + (century - 19) * 100; + else + /* Only the century, but not the year. Strange, but so be it. */ + tm->tm_year = (century - 19) * 100; + } + + if (want_xday && !have_wday) + { + if ( !(have_mon && have_mday) && have_yday) + { + /* We don't have tm_mon and/or tm_mday, compute them. */ + int t_mon = 0; + while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday) + t_mon++; + if (!have_mon) + tm->tm_mon = t_mon - 1; + if (!have_mday) + tm->tm_mday = + (tm->tm_yday + - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); + } + day_of_the_week (tm); + } + if (want_xday && !have_yday) + day_of_the_year (tm); + + return (char *) rp; +} + + +} // namespace pvt_strptime + +#endif + +bool plUnifiedTime::FromString(const char * buf, const char * fmt) +{ + struct tm tm; + tm.tm_isdst = -1; +#if !defined(HS_BUILD_FOR_UNIX) + bool result = (pvt_strptime::strptime_internal(buf, fmt, &tm, fMode)!=nil); +#else + bool result = (strptime(buf, fmt, &tm)!=nil); +#endif + if (result) + *this = tm; + return result; +} + +/// Local time zone offset stuff + +Int32 plUnifiedTime::fLocalTimeZoneOffset = -1; + +Int32 plUnifiedTime::IGetLocalTimeZoneOffset( void ) +{ + static bool inited = false; + + if( !inited ) + { + inited = true; + + // Calculate the difference between local time and GMT for this system currently + // Taken from devx.com from an article written by Danny Kalev + // http://gethelp.devx.com/techtips/cpp_pro/10min/2001/10min1200-3.asp + + time_t currLocalTime = time( 0 ); // current local time + + struct tm local = *gmtime( &currLocalTime ); // convert curr to GMT, store as tm + + time_t utc = mktime( &local ); // convert GMT tm to GMT time_t + + double diffInSecs = difftime( utc, currLocalTime ); + + fLocalTimeZoneOffset = (Int32)diffInSecs; + } + + return fLocalTimeZoneOffset; +} + +// +// static helper, return difference timeA-timeB, may be negative +// +double plUnifiedTime::GetTimeDifference(const plUnifiedTime& timeA, const plUnifiedTime& timeB) +{ + bool neg = (timeB > timeA); + plUnifiedTime timeDiff = neg ? (timeB - timeA) : (timeA - timeB); // always positive + double t = (float)(neg ? timeDiff.GetSecsDouble() * -1. : timeDiff.GetSecsDouble()); + return t; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.h new file mode 100644 index 00000000..5268c8b3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTime.h @@ -0,0 +1,229 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _PL_UNIFIEDTIME_INC_ +#define _PL_UNIFIEDTIME_INC_ + +#include "hsTypes.h" +#include "hsStlUtils.h" + +#if HS_BUILD_FOR_WIN32 +#include "hsWindows.h" +#endif + +// +// Plasma Unified System Time & Data Class +// the time and date are in secs and micros from +// Jan 1, 1970 00:00:00 +// + +struct timeval; +class hsStream; + +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + +enum plUnifiedTime_CtorNow { kNow }; + + +class plUnifiedTime // +{ +public: + enum Mode + { + kGmt, + kLocal + }; + +protected: + UInt32 fSecs; + UInt32 fMicros; + Mode fMode; + + static Int32 fLocalTimeZoneOffset; + + struct tm * IGetTime(const time_t * timer) const; + + static Int32 IGetLocalTimeZoneOffset( void ); + +public: + plUnifiedTime() : fSecs(0),fMicros(0), fMode(kGmt) { } // set ToEpoch() at start + plUnifiedTime(double secsDouble) { SetSecsDouble(secsDouble); } + plUnifiedTime(plUnifiedTime_CtorNow,int mode=kLocal); + plUnifiedTime(const timeval & tv); + plUnifiedTime(int mode, const struct tm & src); + plUnifiedTime(time_t t); + plUnifiedTime(unsigned long t); + plUnifiedTime(int year, int month, int day, int hour, int min, int sec, unsigned long usec=0, int dst=-1); + plUnifiedTime(int mode, const char * buf, const char * fmt); + plUnifiedTime(const plUnifiedTime & src); + plUnifiedTime(const plUnifiedTime * src); + static plUnifiedTime GetCurrentTime(Mode mode=kGmt); + + // assignment + const plUnifiedTime & operator=(const plUnifiedTime & src); + const plUnifiedTime & operator=(const plUnifiedTime * src); + const plUnifiedTime & operator=(const struct timeval & src); + const plUnifiedTime & operator=(time_t src); + const plUnifiedTime & operator=(unsigned long t); + const plUnifiedTime & operator=(const struct tm & src); + + // getters + UInt32 GetSecs() const { return fSecs; } + UInt32 GetMicros() const { return fMicros; } + double GetSecsDouble() const; // get the secs and micros as a double floating point value + hsBool GetTime(short &year, short &month, short &day, short &hour, short &minute, short &second) const; + struct tm * GetTm(struct tm * ptm=nil) const; + int GetYear() const; + int GetMonth() const; + int GetDay() const; + int GetHour() const; + int GetMinute() const; + int GetSecond() const; + int GetMillis() const; + int GetDayOfWeek() const; + int GetMode() const {return fMode;} // local or gmt. + UInt32 AsMillis(); + + // setters + void SetSecs(const UInt32 secs) { fSecs = secs; } + void SetSecsDouble(double secs); + void SetMicros(const UInt32 micros) { fMicros = micros; } + hsBool SetTime(short year, short month, short day, short hour, short minute, short second, unsigned long usec=0, int dst=-1); + hsBool SetGMTime(short year, short month, short day, short hour, short minute, short second, unsigned long usec=0, int dst=-1); + hsBool SetToUTC(); + void ToCurrentTime(); + void ToEpoch() { fSecs = 0; fMicros = 0;} + void SetMode(Mode mode) { fMode=mode;} + void FromMillis(UInt32 millis); +#if HS_BUILD_FOR_WIN32 + hsBool SetFromWinFileTime(const FILETIME ft); +#endif + + // query + bool AtEpoch() const { return fSecs == 0 && fMicros == 0;} + + void Read(hsStream* s); + void Write(hsStream* s) const; + + // time math + const plUnifiedTime & operator+=(const plUnifiedTime & rhs); + const plUnifiedTime & operator-=(const plUnifiedTime & rhs); + friend plUnifiedTime operator -(const plUnifiedTime & left, const plUnifiedTime & right); + friend plUnifiedTime operator +(const plUnifiedTime & left, const plUnifiedTime & right); + static double GetTimeDifference(const plUnifiedTime& timeA, const plUnifiedTime& timeB); // handles negative + + // time compare + bool operator==(const plUnifiedTime & rhs) const; + bool operator!=(const plUnifiedTime & rhs) const; + bool operator <(const plUnifiedTime & rhs) const; + bool operator >(const plUnifiedTime & rhs) const; + bool operator<=(const plUnifiedTime & rhs) const; + bool operator>=(const plUnifiedTime & rhs) const; + + friend bool operator <(const plUnifiedTime & time, int secs); + + + // casting + operator time_t() const { return fSecs;} + operator timeval() const; + operator struct tm() const; + + // formatting (ala strftime) + std::string Format(const char * fmt) const; + + // parsing + bool FromString(const char * buf, const char * fmt); + + const char* Print() const; // print as simple string + const char* PrintWMillis() const; // print as simple string w/ millis +/* +FromString: (from glibc's strptime() man page) + Converts the character string pointed to by buf to values which are + stored in the ``tm'' structure pointed to by tm, using the format + specified by format. + + The following conversion specifications are supported: + + %% A `%' is written. No argument is converted. + %a the day of week, using the locale's weekday names; either the ab- + breviated or full name may be specified. + %A the same as %a. + %b the month, using the locale's month names; either the abbreviated + or full name may be specified. + %B the same as %b. + %c the date and time, using the locale's date and time format. + %C the century number [0,99]; leading zeros are permitted but not re- + quired. This conversion should be used in conjunction with the %y + conversion. + %d the day of month [1,31]; leading zeros are permitted but not re- + quired. + %D the date as %m/%d/%y. + %e the same as %d. + %h the same as %b. + %H the hour (24-hour clock) [0,23]; leading zeros are permitted but + not required. + %I the hour (12-hour clock) [1,12]; leading zeros are permitted but + not required. + %j the day number of the year [1,366]; leading zeros are permitted but + not required. + %k the same as %H. + %l the same as %I. + %m the month number [1,12]; leading zeros are permitted but not re- + quired. + %M the minute [0,59]; leading zeros are permitted but not required. + %n any white-space, including none. + %p the locale's equivalent of a.m. or p.m.. + %r the time (12-hour clock) with %p, using the locale's time format. + %R the time as %H:%M. + %S the seconds [0,61]; leading zeros are permitted but not required. + %t any white-space, including none. + %T the time as %H:%M:%S. + %U the week number of the year (Sunday as the first day of the week) + as a decimal number [0,53]; leading zeros are permitted but not re- + quired. All days in a year preceding the first Sunday are consid- + ered to be in week 0. + %w the weekday as a decimal number [0,6], with 0 representing Sunday; + leading zeros are permitted but not required. + %W the week number of the year (Monday as the first day of the week) + as a decimal number [0,53]; leading zeros are permitted but not re- + quired. All days in a year preceding the first Monday are consid- + ered to be in week 0. + %x the date, using the locale's date format. + %X the time, using the locale's time format. + %y the year within the 20th century [69,99] or the 21st century + [0,68]; leading zeros are permitted but not required. If specified + in conjunction with %C, specifies the year [0,99] within that cen- + tury. + %Y the year, including the century (i.e., 1996). +*/ +}; + + +plUnifiedTime operator -(const plUnifiedTime & left, const plUnifiedTime & right); +plUnifiedTime operator +(const plUnifiedTime & left, const plUnifiedTime & right); + +#endif //_PL_UNIFIEDTIME_INC_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTimeCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTimeCreatable.h new file mode 100644 index 00000000..6e6657e2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plUnifiedTime/plUnifiedTimeCreatable.h @@ -0,0 +1,36 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plUnifiedTimeCreatable_inc +#define plUnifiedTimeCreatable_inc + +#if 0 +#include "../pnFactory/plCreator.h" + +#include "plUnifiedTime.h" +REGISTER_CREATABLE( plUnifiedTime ); +#endif + +#endif // plUnifiedTimeCreatable_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/Intern.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/Intern.h new file mode 100644 index 00000000..ab3d0a68 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/Intern.h @@ -0,0 +1,35 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plVault/Intern.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_INTERN_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plVault/Intern.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_INTERN_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/Pch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/Pch.h new file mode 100644 index 00000000..77889bf7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/Pch.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plVault/Pch.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_PCH_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plVault/Pch.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_PCH_H + + +// 'Old' system is full of compiler warnings at /W4, so just hide them +#pragma warning(push, 0) +#include "plVault.h" +#pragma warning(pop) + + +#ifdef CLIENT + +// 'Old' system is full of compiler warnings at /W4, so just hide them +#pragma warning(push, 0) +#include +#include +#include "hsStlUtils.h" +#include "hsStringTokenizer.h" +#include "hsGeometry3.h" +#include "../plSDL/plSDL.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "../plNetCommon/plNetCommon.h" +#include "../plNetCommon/plNetServerSessionInfo.h" +#include "../plNetCommon/plSpawnPointInfo.h" +#include "../pnDispatch/plDispatch.h" +#include "plDniCoordinateInfo.h" +#include "../plGImage/plMipmap.h" +#include "../plJPEG/plJPEG.h" +#include "../plMessage/plVaultNotifyMsg.h" +#include "../plNetClientComm/plNetClientComm.h" + +#define KI_CONSTANTS_ONLY +#include "../../FeatureLib/pfMessage/pfKIMsg.h" // for KI level constants =( +#undef KI_CONSTANTS_ONLY +#pragma warning(pop) + +#include "../plNetGameLib/plNetGameLib.h" + +#endif // def CLIENT + +#include "Intern.h" + +#include diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plAgeInfoSource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plAgeInfoSource.h new file mode 100644 index 00000000..e4df0d8f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plAgeInfoSource.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAgeInfoSource_h_inc +#define plAgeInfoSource_h_inc + +/////////////////////////////////////////////////////////////////// + +class plUnifiedTime; +class plUUID; + +class plAgeInfoSource +{ +public: + // current time in current age + virtual const plUnifiedTime * GetAgeTime( void ) const = 0; + // name of current age + virtual const char * GetAgeName( void ) const = 0; + // unique identifier for this age instance + virtual const plUUID * GetAgeGuid( void ) const = 0; +}; + + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plDniCoordinateInfo.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plDniCoordinateInfo.cpp new file mode 100644 index 00000000..d0209e2a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plDniCoordinateInfo.cpp @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plDniCoordinateInfo.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + + +#ifdef CLIENT +/////////////////////////////////////////////////////////////////// + +const UInt8 plDniCoordinateInfo::StreamVersion = 1; + + +plDniCoordinateInfo::plDniCoordinateInfo() +: fHSpans(0) +, fVSpans(0) +, fTorans(0) +{ +} + +void plDniCoordinateInfo::CopyFrom( const plDniCoordinateInfo * other ) +{ + hsRAMStream stream; + plCreatable * otherNonConst = const_cast( other ); // because plCreatable Write isn't const, but should be. + otherNonConst->Write( &stream, nil ); + stream.Rewind(); + Read( &stream, nil ); +} + +void plDniCoordinateInfo::Read( hsStream* s, hsResMgr* mgr ) +{ + UInt8 streamVer; + s->ReadSwap( &streamVer ); + if ( streamVer==StreamVersion ) + { + s->ReadSwap( &fHSpans ); + s->ReadSwap( &fVSpans ); + s->ReadSwap( &fTorans ); + } +} + +void plDniCoordinateInfo::Write( hsStream* s, hsResMgr* mgr ) +{ + s->WriteSwap( StreamVersion ); + s->WriteSwap( fHSpans ); + s->WriteSwap( fVSpans ); + s->WriteSwap( fTorans ); +} + +std::string plDniCoordinateInfo::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%sDniCoords[%d,%d,%d]", space.c_str(), fHSpans, fVSpans, fTorans ); + return result; +} +#endif // def CLIENT diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plDniCoordinateInfo.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plDniCoordinateInfo.h new file mode 100644 index 00000000..fd851bfd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plDniCoordinateInfo.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plDniCoordinateInfo_h_inc +#define plDniCoordinateInfo_h_inc + +#include "hsConfig.h" +#include "hsStlUtils.h" +#include "../pnFactory/plCreatable.h" + +/////////////////////////////////////////////////////////////////// + +class hsStream; +class hsResMgr; + + +class plDniCoordinateInfo : public plCreatable +{ + static const UInt8 StreamVersion; + +protected: + // spherical coords (rho,theta,phi) + int fHSpans; // horizontal distance in d'ni spans to point P from origin O + int fVSpans; // vertical distance in d'ni spans to point P from origin O + int fTorans; // angle in d'ni torans from the zero vector to line OP. + +public: + plDniCoordinateInfo(); + + CLASSNAME_REGISTER( plDniCoordinateInfo ); + GETINTERFACE_ANY( plDniCoordinateInfo, plCreatable ); + + int GetHSpans( void ) const { return fHSpans;} + void SetHSpans( int v ) { fHSpans = v; } + int GetVSpans( void ) const { return fVSpans;} + void SetVSpans( int v ) { fVSpans = v;} + int GetTorans( void ) const { return fTorans; } + void SetTorans( int v ) { fTorans = v; } + + void CopyFrom( const plDniCoordinateInfo * other ); + void Read( hsStream* s, hsResMgr* mgr ); + void Write( hsStream* s, hsResMgr* mgr ); + + // debug + std::string AsStdString( int level=0 ) const; +}; + + + +#endif // plDniCoordinateInfo_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVault.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVault.cpp new file mode 100644 index 00000000..e79ee6df --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVault.cpp @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVault.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVault.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVault.h new file mode 100644 index 00000000..cfcdb182 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVault.h @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVault.h +* +***/ + +#ifndef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_PLVAULT_H +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_PLVAULT_H + +#include "../pnUtils/pnUtils.h" +#include "../pnNetBase/pnNetBase.h" +#include "../pnNetProtocol/pnNetProtocol.h" +#include "../pnAsyncCore/pnAsyncCore.h" + +#include "plVaultConstants.h" +#include "plVaultNodeAccess.h" + +#ifdef CLIENT +#include "plVaultClientApi.h" +#endif + + +#endif // PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_PLVAULT_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultClientApi.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultClientApi.cpp new file mode 100644 index 00000000..8e23779b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultClientApi.cpp @@ -0,0 +1,4608 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultClientApi.cpp +* +***/ + + +#include "Pch.h" +#pragma hdrstop + +#ifdef CLIENT + +/***************************************************************************** +* +* Private +* +***/ + +struct IVaultCallback { + LINK(IVaultCallback) link; + VaultCallback * cb; +}; + +struct INotifyAfterDownload : THashKeyVal { + HASHLINK(INotifyAfterDownload) link; + unsigned parentId; + unsigned childId; + + INotifyAfterDownload (unsigned parentId, unsigned childId) + : THashKeyVal(childId) + , parentId(parentId) + , childId(childId) + {} +}; + +struct DeviceInbox : CHashKeyStr { + HASHLINK(DeviceInbox) link; + wchar inboxName[kMaxVaultNodeStringLength]; + + DeviceInbox (const wchar device[], const wchar inbox[]) + : CHashKeyStr(device) + { + StrCopy(inboxName, inbox, arrsize(inboxName)); + } +}; + +// A RelVaultNodeLink may be either stored in the global table, +// or stored in an IRelVaultNode's parents or children table. +struct RelVaultNodeLink : THashKeyVal { + HASHLINK(RelVaultNodeLink) link; + RelVaultNode * node; + unsigned ownerId; + bool seen; + + RelVaultNodeLink (bool seen, unsigned ownerId, unsigned nodeId, RelVaultNode * node) + : THashKeyVal(nodeId) + , seen(seen) + , ownerId(ownerId) + , node(node) + { + node->IncRef(); + } + ~RelVaultNodeLink () { + node->DecRef(); + } +}; + + +struct IRelVaultNode { + RelVaultNode * node; + + HASHTABLEDECL( + RelVaultNodeLink, + THashKeyVal, + link + ) parents; + + HASHTABLEDECL( + RelVaultNodeLink, + THashKeyVal, + link + ) children; + + IRelVaultNode (RelVaultNode * node); + ~IRelVaultNode (); + + // Unlink our node from all our parent and children + void UnlinkFromRelatives (); + + // Unlink the node from our parent and children lists + void Unlink (RelVaultNode * other); +}; + + +struct VaultCreateNodeTrans { + FVaultCreateNodeCallback callback; + void * state; + void * param; + + unsigned nodeId; + RelVaultNode * node; + + static void VaultNodeCreated ( + ENetError result, + void * param, + unsigned nodeId + ); + static void VaultNodeFetched ( + ENetError result, + void * param, + NetVaultNode * node + ); + + void Complete (ENetError result); +}; + + +struct VaultFindNodeTrans { + FVaultFindNodeCallback callback; + void * param; + + static void VaultNodeFound ( + ENetError result, + void * param, + unsigned nodeIdCount, + const unsigned nodeIds[] + ); +}; + + +struct VaultDownloadTrans { + FVaultDownloadCallback callback; + void * cbParam; + FVaultProgressCallback progressCallback; + void * cbProgressParam; + + wchar tag[MAX_PATH]; + unsigned nodeCount; + unsigned nodesLeft; + unsigned vaultId; + ENetError result; + + VaultDownloadTrans (); + + static void VaultNodeFetched ( + ENetError result, + void * param, + NetVaultNode * node + ); + static void VaultNodeRefsFetched ( + ENetError result, + void * param, + NetVaultNodeRef * refs, + unsigned refCount + ); +}; + +struct VaultAgeInitTrans { + FVaultInitAgeCallback callback; + void * cbState; + void * cbParam; + + static void AgeInitCallback ( + ENetError result, + void * param, + unsigned ageVaultId, + unsigned ageInfoVaultId + ); +}; + +struct AddChildNodeFetchTrans { + FVaultAddChildNodeCallback callback; + void * cbParam; + ENetError result; + long opCount; + + static void VaultNodeFetched ( + ENetError result, + void * param, + NetVaultNode * node + ); + static void VaultNodeRefsFetched ( + ENetError result, + void * param, + NetVaultNodeRef * refs, + unsigned refCount + ); +}; + + +/***************************************************************************** +* +* Private data +* +***/ + +static bool s_running; + +static HASHTABLEDECL( + RelVaultNodeLink, + THashKeyVal, + link +) s_nodes; + +static LISTDECL( + IVaultCallback, + link +) s_callbacks; + +static HASHTABLEDECL( + INotifyAfterDownload, + THashKeyVal, + link +) s_notifyAfterDownload; + +static HASHTABLEDECL( + DeviceInbox, + CHashKeyStr, + link +) s_ageDeviceInboxes; + +static bool s_processPlayerInbox = false; + +/***************************************************************************** +* +* Local functions +* +***/ + +static void VaultProcessVisitNote(RelVaultNode * rvnVisit); +static void VaultProcessUnvisitNote(RelVaultNode * rvnUnVisit); + +static void VaultNodeFetched ( + ENetError result, + void * param, + NetVaultNode * node +); +static void VaultNodeFound ( + ENetError result, + void * param, + unsigned nodeIdCount, + const unsigned nodeIds[] +); + +//============================================================================ +static void VaultNodeAddedDownloadCallback(ENetError result, void * param) { + unsigned childId = (unsigned)param; + + INotifyAfterDownload* notify = s_notifyAfterDownload.Find(childId); + + if (notify) { + if (IS_NET_SUCCESS(result)) { + RelVaultNodeLink* parentLink = s_nodes.Find(notify->parentId); + RelVaultNodeLink* childLink = s_nodes.Find(notify->childId); + + if (parentLink && childLink) { + if (childLink->node->nodeType == plVault::kNodeType_TextNote) { + VaultTextNoteNode textNote(childLink->node); + if (textNote.noteType == plVault::kNoteType_Visit) + VaultProcessVisitNote(childLink->node); + else if (textNote.noteType == plVault::kNoteType_UnVisit) + VaultProcessUnvisitNote(childLink->node); + } + + for (IVaultCallback * cb = s_callbacks.Head(); cb; cb = s_callbacks.Next(cb)) + cb->cb->AddedChildNode(parentLink->node, childLink->node); + } + } + + DEL(notify); + } +} + +//============================================================================ +static void __cdecl LogDumpProc ( + void * , + const wchar fmt[], + ... +) { + va_list args; + va_start(args, fmt); + LogMsgV(kLogDebug, fmt, args); + va_end(args); +} + +//============================================================================ +// Returns ids of nodes that had to be created (so we can fetch them) +static void BuildNodeTree ( + const NetVaultNodeRef refs[], + unsigned refCount, + ARRAY(unsigned) * newNodeIds, + ARRAY(unsigned) * existingNodeIds, + bool notifyNow = true +) { + for (unsigned i = 0; i < refCount; ++i) { + // Find/Create global links + RelVaultNodeLink * parentLink = s_nodes.Find(refs[i].parentId); + if (!parentLink) { + newNodeIds->Add(refs[i].parentId); + parentLink = NEWZERO(RelVaultNodeLink)(false, 0, refs[i].parentId, NEWZERO(RelVaultNode)); + parentLink->node->nodeId = refs[i].parentId; // set directly so that the field's dirty flag isn't set + s_nodes.Add(parentLink); + } + else { + existingNodeIds->Add(refs[i].parentId); + } + RelVaultNodeLink * childLink = s_nodes.Find(refs[i].childId); + if (!childLink) { + newNodeIds->Add(refs[i].childId); + childLink = NEWZERO(RelVaultNodeLink)(refs[i].seen, refs[i].ownerId, refs[i].childId, NEWZERO(RelVaultNode)); + childLink->node->nodeId = refs[i].childId; // set directly so that the field's dirty flag isn't set + s_nodes.Add(childLink); + } + else { + existingNodeIds->Add(refs[i].childId); + if (unsigned ownerId = refs[i].ownerId) + childLink->ownerId = ownerId; + } + + RelVaultNode * parentNode = parentLink->node; + RelVaultNode * childNode = childLink->node; + + bool isImmediateParent = parentNode->IsParentOf(refs[i].childId, 1); + bool isImmediateChild = childNode->IsChildOf(refs[i].parentId, 1); + + if (!isImmediateParent) { + // Add parent to child's parents table + parentLink = NEWZERO(RelVaultNodeLink)(false, 0, parentNode->nodeId, parentNode); + childNode->state->parents.Add(parentLink); + LogMsg(kLogDebug, L"Added relationship: p:%u,c:%u", refs[i].parentId, refs[i].childId); + } + + if (!isImmediateChild) { + // Add child to parent's children table + childLink = NEWZERO(RelVaultNodeLink)(refs[i].seen, refs[i].ownerId, childNode->nodeId, childNode); + parentNode->state->children.Add(childLink); + + if (notifyNow || childNode->nodeType != 0) { + // We made a new link, so make the callbacks + for (IVaultCallback * cb = s_callbacks.Head(); cb; cb = s_callbacks.Next(cb)) + cb->cb->AddedChildNode(parentNode, childNode); + } + else { + INotifyAfterDownload* notify = NEWZERO(INotifyAfterDownload)(parentNode->nodeId, childNode->nodeId); + s_notifyAfterDownload.Add(notify); + } + } + } +} + +//============================================================================ +static void InitFetchedNode (RelVaultNode * rvn) { + + switch (rvn->nodeType) { + case plVault::kNodeType_SDL: { + VaultSDLNode access(rvn); + if (!access.sdlData || !access.sdlDataLen) + access.InitStateDataRecord(access.sdlName); + } + break; + } +} + +//============================================================================ +static void FetchRefOwners ( + NetVaultNodeRef * refs, + unsigned refCount +) { + ARRAY(unsigned) ownerIds; + { for (unsigned i = 0; i < refCount; ++i) + if (unsigned ownerId = refs[i].ownerId) + ownerIds.Add(ownerId); + } + QSORT(unsigned, ownerIds.Ptr(), ownerIds.Count(), elem1 < elem2); + RelVaultNode * templateNode = NEWZERO(RelVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_PlayerInfo); + { unsigned prevId = 0; + for (unsigned i = 0; i < ownerIds.Count(); ++i) { + if (ownerIds[i] != prevId) { + prevId = ownerIds[i]; + VaultPlayerInfoNode access(templateNode); + access.SetPlayerId(refs[i].ownerId); + if (RelVaultNode * rvn = VaultGetNodeIncRef(templateNode)) { + rvn->DecRef(); + continue; + } + NetCliAuthVaultNodeFind( + templateNode, + VaultNodeFound, + nil + ); + } + } + } + templateNode->DecRef(); +} + +//============================================================================ +static void FetchNodesFromRefs ( + NetVaultNodeRef * refs, + unsigned refCount, + FNetCliAuthVaultNodeFetched fetchCallback, + void * fetchParam, + unsigned * fetchCount + +) { + // On the side, start downloading PlayerInfo nodes of ref owners we don't already have locally + FetchRefOwners(refs, refCount); + + *fetchCount = 0; + + ARRAY(unsigned) newNodeIds; + ARRAY(unsigned) existingNodeIds; + + BuildNodeTree(refs, refCount, &newNodeIds, &existingNodeIds); + + ARRAY(unsigned) nodeIds; + nodeIds.Add(newNodeIds.Ptr(), newNodeIds.Count()); + nodeIds.Add(existingNodeIds.Ptr(), existingNodeIds.Count()); + QSORT(unsigned, nodeIds.Ptr(), nodeIds.Count(), elem1 < elem2); + + // Fetch the nodes that do not yet have a nodetype + unsigned prevId = 0; + {for (unsigned i = 0; i < nodeIds.Count(); ++i) { + RelVaultNodeLink * link = s_nodes.Find(nodeIds[i]); + if (link->node->nodeType != 0) + continue; + // filter duplicates + if (link->node->nodeId == prevId) + continue; + prevId = link->node->nodeId; + NetCliAuthVaultNodeFetch( + nodeIds[i], + fetchCallback, + fetchParam + ); + ++(*fetchCount); + }} +} + +//============================================================================ +static void VaultNodeFound ( + ENetError result, + void * , + unsigned nodeIdCount, + const unsigned nodeIds[] +) { + // TODO: Support some sort of optional transaction object/callback state + + // error? + if (IS_NET_ERROR(result)) + return; + + for (unsigned i = 0; i < nodeIdCount; ++i) { + + // See if we already have this node + if (RelVaultNodeLink * link = s_nodes.Find(nodeIds[i])) + return; + + // Start fetching the node + NetCliAuthVaultNodeFetch(nodeIds[i], VaultNodeFetched, nil); + } +} + +//============================================================================ +static void VaultNodeFetched ( + ENetError result, + void * , + NetVaultNode * node +) { + if (IS_NET_ERROR(result)) { + LogMsg(kLogDebug, L"VaultNodeFetched failed: %u (%s)", result, NetErrorToString(result)); + return; + } + + // Add to global node table + RelVaultNodeLink * link = s_nodes.Find(node->nodeId); + if (!link) { + link = NEWZERO(RelVaultNodeLink)(false, 0, node->nodeId, NEWZERO(RelVaultNode)); + link->node->nodeId = node->nodeId; // set directly so that the field's dirty flag isn't set + s_nodes.Add(link); + } + link->node->CopyFrom(node, NetVaultNode::kCopyOverwrite); + InitFetchedNode(link->node); + + link->node->Print(L"Fetched", LogDumpProc, 0); +} + +//============================================================================ +static void ChangedVaultNodeFetched ( + ENetError result, + void * param, + NetVaultNode * node +) { + if (IS_NET_ERROR(result)) { + LogMsg(kLogDebug, L"ChangedVaultNodeFetched failed: %u (%s)", result, NetErrorToString(result)); + return; + } + + VaultNodeFetched(result, param, node); + + RelVaultNodeLink* savedLink = s_nodes.Find(node->nodeId); + + if (savedLink) { + for (IVaultCallback * cb = s_callbacks.Head(); cb; cb = s_callbacks.Next(cb)) + cb->cb->ChangedNode(savedLink->node); + } +} + +//============================================================================ +static void VaultNodeChanged ( + unsigned nodeId, + const Uuid & revisionId +) { + LogMsg(kLogDebug, L"Notify: Node changed: %u", nodeId); + + RelVaultNodeLink * link = s_nodes.Find(nodeId); + + // We don't have the node, so we don't care that it changed (we actually + // shouldn't have been notified) + if (!link) { + LogMsg(kLogDebug, L"rcvd change notification for node %u, but node doesn't exist locally.", nodeId); + return; + } + + // We are the party responsible for the change, so we already have the + // latest version of the node; no need to fetch it. + if (link->node->revisionId == revisionId) + return; + + // We have the node and we weren't the one that changed it, so fetch it. + NetCliAuthVaultNodeFetch( + nodeId, + ChangedVaultNodeFetched, + nil + ); +} + +//============================================================================ +static void VaultNodeAdded ( + unsigned parentId, + unsigned childId, + unsigned ownerId +) { + LogMsg(kLogDebug, L"Notify: Node added: p:%u,c:%u", parentId, childId); + + unsigned inboxId = 0; + if (RelVaultNode * rvnInbox = VaultGetPlayerInboxFolderIncRef()) { + inboxId = rvnInbox->nodeId; + rvnInbox->DecRef(); + } + + // Build the relationship locally + NetVaultNodeRef refs[] = { + { parentId, childId, ownerId } + }; + ARRAY(unsigned) newNodeIds; + ARRAY(unsigned) existingNodeIds; + + BuildNodeTree(refs, arrsize(refs), &newNodeIds, &existingNodeIds, false); + + ARRAY(unsigned) nodeIds; + nodeIds.Add(newNodeIds.Ptr(), newNodeIds.Count()); + nodeIds.Add(existingNodeIds.Ptr(), existingNodeIds.Count()); + QSORT(unsigned, nodeIds.Ptr(), nodeIds.Count(), elem1 < elem2); + + // Fetch the nodes that do not yet have a nodetype + unsigned prevId = 0; + unsigned i = 0; + {for (; i < nodeIds.Count(); ++i) { + RelVaultNodeLink * link = s_nodes.Find(nodeIds[i]); + if (link->node->nodeType != 0) + continue; + // filter duplicates + if (link->node->nodeId == prevId) + continue; + prevId = link->node->nodeId; + VaultDownload( + L"NodeAdded", + nodeIds[i], + VaultNodeAddedDownloadCallback, + (void*)nodeIds[i], + nil, + nil + ); + }} + + if (parentId == inboxId) { + if (i > 0) + s_processPlayerInbox = true; + else + VaultProcessPlayerInbox(); + } + + // if the added element is already downloaded then send the callbacks now + RelVaultNodeLink* parentLink = s_nodes.Find(parentId); + RelVaultNodeLink* childLink = s_nodes.Find(childId); + + if (childLink->node->nodeType != 0) { + for (IVaultCallback * cb = s_callbacks.Head(); cb; cb = s_callbacks.Next(cb)) + cb->cb->AddedChildNode(parentLink->node, childLink->node); + } +} + +//============================================================================ +static void VaultNodeRemoved ( + unsigned parentId, + unsigned childId +) { + LogMsg(kLogDebug, L"Notify: Node removed: p:%u,c:%u", parentId, childId); + for (;;) { + // Unlink 'em locally, if we can + RelVaultNodeLink * parentLink = s_nodes.Find(parentId); + if (!parentLink) + break; + + RelVaultNodeLink * childLink = s_nodes.Find(childId); + if (!childLink) + break; + + if (parentLink->node->IsParentOf(childId, 1)) { + // We have the relationship, so make the callbacks + for (IVaultCallback * cb = s_callbacks.Head(); cb; cb = s_callbacks.Next(cb)) + cb->cb->RemovingChildNode(parentLink->node, childLink->node); + } + + parentLink->node->state->Unlink(childLink->node); + childLink->node->state->Unlink(parentLink->node); + break; + } +} + +//============================================================================ +static void VaultNodeDeleted ( + unsigned nodeId +) { + LogMsg(kLogDebug, L"Notify: Node deleted: %u", nodeId); + VaultCull(nodeId); +} + +//============================================================================ +static void SaveDirtyNodes () { + // Save a max of 5Kb every quarter second + static const unsigned kSaveUpdateIntervalMs = 250; + static const unsigned kMaxBytesPerSaveUpdate = 5 * 1024; + static unsigned s_nextSaveMs; + unsigned currTimeMs = TimeGetMs() | 1; + if (!s_nextSaveMs || signed(s_nextSaveMs - currTimeMs) <= 0) { + s_nextSaveMs = (currTimeMs + kSaveUpdateIntervalMs) | 1; + unsigned bytesWritten = 0; + for (RelVaultNodeLink * link = s_nodes.Head(); link; link = s_nodes.Next(link)) { + if (bytesWritten >= kMaxBytesPerSaveUpdate) + break; + if (link->node->dirtyFlags) { + + // Auth server needs the name of the sdl record + if (link->node->nodeType == plVault::kNodeType_SDL) + link->node->dirtyFlags |= VaultSDLNode::kSDLName; + + if (unsigned bytes = NetCliAuthVaultNodeSave(link->node, nil, nil)) { + bytesWritten += bytes; + link->node->Print(L"Saving", LogDumpProc, 0); + } + } + } + } +} + +//============================================================================ +static RelVaultNode * GetChildFolderNode ( + RelVaultNode * parent, + unsigned folderType, + unsigned maxDepth +) { + if (!parent) + return nil; + + RelVaultNode * rvn = parent->GetChildFolderNodeIncRef(folderType, maxDepth); + if (rvn) + rvn->DecRef(); + + return rvn; +} + +//============================================================================ +static RelVaultNode * GetChildPlayerInfoListNode ( + RelVaultNode * parent, + unsigned folderType, + unsigned maxDepth +) { + if (!parent) + return nil; + + RelVaultNode * rvn = parent->GetChildPlayerInfoListNodeIncRef(folderType, maxDepth); + if (rvn) + rvn->DecRef(); + + return rvn; +} + + +/***************************************************************************** +* +* VaultCreateNodeTrans +* +***/ + +//============================================================================ +void VaultCreateNodeTrans::VaultNodeCreated ( + ENetError result, + void * param, + unsigned nodeId +) { + VaultCreateNodeTrans * trans = (VaultCreateNodeTrans *)param; + if (IS_NET_ERROR(result)) { + trans->Complete(result); + } + else { + trans->nodeId = nodeId; + NetCliAuthVaultNodeFetch( + nodeId, + VaultCreateNodeTrans::VaultNodeFetched, + trans + ); + } +} + +//============================================================================ +void VaultCreateNodeTrans::VaultNodeFetched ( + ENetError result, + void * param, + NetVaultNode * node +) { + ::VaultNodeFetched(result, param, node); + + VaultCreateNodeTrans * trans = (VaultCreateNodeTrans *)param; + + if (IS_NET_SUCCESS(result)) + trans->node = s_nodes.Find(node->nodeId)->node; + else + trans->node = nil; + + trans->Complete(result); +} + +//============================================================================ +void VaultCreateNodeTrans::Complete (ENetError result) { + + if (callback) + callback( + result, + state, + param, + node + ); + + DEL(this); +} + + +/***************************************************************************** +* +* VaultFindNodeTrans +* +***/ + +//============================================================================ +void VaultFindNodeTrans::VaultNodeFound ( + ENetError result, + void * param, + unsigned nodeIdCount, + const unsigned nodeIds[] +) { + VaultFindNodeTrans * trans = (VaultFindNodeTrans*)param; + if (trans->callback) + trans->callback( + result, + trans->param, + nodeIdCount, + nodeIds + ); + DEL(trans); +} + + +/***************************************************************************** +* +* VaultDownloadTrans +* +***/ + +//============================================================================ +VaultDownloadTrans::VaultDownloadTrans () { + ASSERT(!nodeCount); // must be alloced with +} + +//============================================================================ +void VaultDownloadTrans::VaultNodeFetched ( + ENetError result, + void * param, + NetVaultNode * node +) { + ::VaultNodeFetched(result, param, node); + + VaultDownloadTrans * trans = (VaultDownloadTrans *)param; + if (IS_NET_ERROR(result)) { + trans->result = result; + //LogMsg(kLogError, L"Error fetching node...most likely trying to fetch a nodeid of 0"); + } + + --trans->nodesLeft; +// LogMsg(kLogDebug, L"(Download) %u of %u nodes fetched", trans->nodeCount - trans->nodesLeft, trans->nodeCount); + + if (trans->progressCallback) { + trans->progressCallback( + trans->nodeCount, + trans->nodeCount - trans->nodesLeft, + trans->cbProgressParam + ); + } + + if (!trans->nodesLeft) { + VaultDump(trans->tag, trans->vaultId, LogDumpProc); + + if (trans->callback) + trans->callback( + trans->result, + trans->cbParam + ); + + DEL(trans); + } +} + +//============================================================================ +void VaultDownloadTrans::VaultNodeRefsFetched ( + ENetError result, + void * param, + NetVaultNodeRef * refs, + unsigned refCount +) { + VaultDownloadTrans * trans = (VaultDownloadTrans *)param; + + if (IS_NET_ERROR(result)) { + LogMsg(kLogDebug, L"VaultNodeRefsFetched failed: %u (%s)", result, NetErrorToString(result)); + trans->result = result; + trans->nodesLeft = 0; + } + else { + if (refCount) { + FetchNodesFromRefs( + refs, + refCount, + VaultDownloadTrans::VaultNodeFetched, + param, + &trans->nodeCount + ); + trans->nodesLeft = trans->nodeCount; + } + else { + // root node has no child heirarchy? Make sure we still d/l the root node if necessary. + RelVaultNodeLink* rootNodeLink = s_nodes.Find(trans->vaultId); + if (!rootNodeLink || rootNodeLink->node->nodeType == 0) { + NetCliAuthVaultNodeFetch( + trans->vaultId, + VaultDownloadTrans::VaultNodeFetched, + trans + ); + trans->nodesLeft = 1; + } + } + } + + // Make the callback now if there are no nodes to fetch, or if error + if (!trans->nodesLeft) { + if (trans->callback) + trans->callback( + trans->result, + trans->cbParam + ); + + DEL(trans); + } +} + + +/***************************************************************************** +* +* VaultAgeInitTrans +* +***/ + +//============================================================================ +void VaultAgeInitTrans::AgeInitCallback ( + ENetError result, + void * param, + unsigned ageVaultId, + unsigned ageInfoVaultId +) { + VaultAgeInitTrans * trans = (VaultAgeInitTrans *)param; + + if (trans->callback) + trans->callback( + result, + trans->cbState, + trans->cbParam, + ageVaultId, + ageInfoVaultId + ); + + DEL(trans); +} + + +/***************************************************************************** +* +* AddChildNodeFetchTrans +* +***/ + +//============================================================================ +void AddChildNodeFetchTrans::VaultNodeRefsFetched ( + ENetError result, + void * param, + NetVaultNodeRef * refs, + unsigned refCount +) { + AddChildNodeFetchTrans * trans = (AddChildNodeFetchTrans *)param; + + if (IS_NET_ERROR(result)) { + trans->result = result; + } + else { + unsigned incFetchCount = 0; + FetchNodesFromRefs( + refs, + refCount, + AddChildNodeFetchTrans::VaultNodeFetched, + param, + &incFetchCount + ); + AtomicAdd(&trans->opCount, incFetchCount); + } + + // Make the callback now if there are no nodes to fetch, or if error + AtomicAdd(&trans->opCount, -1); + if (!trans->opCount) { + if (trans->callback) + trans->callback( + trans->result, + trans->cbParam + ); + DEL(trans); + } +} + +//============================================================================ +void AddChildNodeFetchTrans::VaultNodeFetched ( + ENetError result, + void * param, + NetVaultNode * node +) { + ::VaultNodeFetched(result, param, node); + + AddChildNodeFetchTrans * trans = (AddChildNodeFetchTrans *)param; + + if (IS_NET_ERROR(result)) + trans->result = result; + + AtomicAdd(&trans->opCount, -1); + if (!trans->opCount) { + if (trans->callback) + trans->callback( + trans->result, + trans->cbParam + ); + DEL(trans); + } +} + + +/***************************************************************************** +* +* IRelVaultNode +* +***/ + +//============================================================================ +IRelVaultNode::IRelVaultNode (RelVaultNode * node) +: node(node) +{ +} + +//============================================================================ +IRelVaultNode::~IRelVaultNode () { + ASSERT(!parents.Head()); + ASSERT(!children.Head()); +} + +//============================================================================ +void IRelVaultNode::UnlinkFromRelatives () { + + RelVaultNodeLink * link, * next; + for (link = parents.Head(); link; link = next) { + next = parents.Next(link); + + // We have the relationship, so make the callbacks + for (IVaultCallback * cb = s_callbacks.Head(); cb; cb = s_callbacks.Next(cb)) + cb->cb->RemovingChildNode(link->node, this->node); + + link->node->state->Unlink(node); + } + for (link = children.Head(); link; link = next) { + next = children.Next(link); + link->node->state->Unlink(node); + } + + ASSERT(!parents.Head()); + ASSERT(!children.Head()); +} + + +//============================================================================ +void IRelVaultNode::Unlink (RelVaultNode * other) { + ASSERT(other != node); + + RelVaultNodeLink * link; + if (nil != (link = parents.Find(other->nodeId))) { + // make them non-findable in our parents table + link->link.Unlink(); + // remove us from other's tables. + link->node->state->Unlink(node); + DEL(link); + } + if (nil != (link = children.Find(other->nodeId))) { + // make them non-findable in our children table + link->link.Unlink(); + // remove us from other's tables. + link->node->state->Unlink(node); + DEL(link); + } +} + +/***************************************************************************** +* +* RelVaultNode +* +***/ + +//============================================================================ +RelVaultNode::RelVaultNode () { + state = NEWZERO(IRelVaultNode)(this); +} + +//============================================================================ +RelVaultNode::~RelVaultNode () { + DEL(state); +} + +//============================================================================ +bool RelVaultNode::IsParentOf (unsigned childId, unsigned maxDepth) { + if (nodeId == childId) + return false; + if (maxDepth == 0) + return false; + if (state->children.Find(childId)) + return true; + RelVaultNodeLink * link = state->children.Head(); + for (; link; link = state->children.Next(link)) + if (link->node->IsParentOf(childId, maxDepth - 1)) + return true; + return false; +} + +//============================================================================ +bool RelVaultNode::IsChildOf (unsigned parentId, unsigned maxDepth) { + if (nodeId == parentId) + return false; + if (maxDepth == 0) + return false; + if (state->parents.Find(parentId)) + return true; + RelVaultNodeLink * link = state->parents.Head(); + for (; link; link = state->parents.Next(link)) + if (link->node->IsChildOf(parentId, maxDepth - 1)) + return true; + return false; +} + +//============================================================================ +void RelVaultNode::GetRootIds (ARRAY(unsigned) * nodeIds) { + RelVaultNodeLink * link = state->parents.Head(); + if (!link) { + nodeIds->Add(nodeId); + } + else { + for (; link; link = state->parents.Next(link)) + link->node->GetRootIds(nodeIds); + } +} + +//============================================================================ +unsigned RelVaultNode::RemoveChildNodes (unsigned maxDepth) { + hsAssert(false, "eric, implement me."); + return 0; +} + +//============================================================================ +void RelVaultNode::GetChildNodeIds ( + ARRAY(unsigned) * nodeIds, + unsigned maxDepth +) { + if (!maxDepth) + return; + RelVaultNodeLink * link = state->children.Head(); + for (; link; link = state->children.Next(link)) { + nodeIds->Add(link->node->nodeId); + link->node->GetChildNodeIds(nodeIds, maxDepth-1); + } +} + +//============================================================================ +void RelVaultNode::GetParentNodeIds ( + ARRAY(unsigned) * nodeIds, + unsigned maxDepth +) { + if (!maxDepth) + return; + RelVaultNodeLink * link = state->parents.Head(); + for (; link; link = state->parents.Next(link)) { + nodeIds->Add(link->node->nodeId); + link->node->GetParentNodeIds(nodeIds, maxDepth-1); + } +} + + +//============================================================================ +RelVaultNode * RelVaultNode::GetParentNodeIncRef ( + NetVaultNode * templateNode, + unsigned maxDepth +) { + if (maxDepth == 0) + return false; + + RelVaultNodeLink * link; + link = state->parents.Head(); + for (; link; link = state->parents.Next(link)) { + if (link->node->Matches(templateNode)) { + link->node->IncRef("Found"); + return link->node; + } + } + + link = state->parents.Head(); + for (; link; link = state->parents.Next(link)) { + if (RelVaultNode * node = link->node->GetParentNodeIncRef(templateNode, maxDepth-1)) + return node; + } + + return nil; +} + +//============================================================================ +RelVaultNode * RelVaultNode::GetChildNodeIncRef ( + NetVaultNode * templateNode, + unsigned maxDepth +) { + if (maxDepth == 0) + return false; + + RelVaultNodeLink * link; + link = state->children.Head(); + for (; link; link = state->children.Next(link)) { + if (link->node->Matches(templateNode)) { + link->node->IncRef("Found"); + return link->node; + } + } + + link = state->children.Head(); + for (; link; link = state->children.Next(link)) { + if (RelVaultNode * node = link->node->GetChildNodeIncRef(templateNode, maxDepth-1)) + return node; + } + + return nil; +} + +//============================================================================ +RelVaultNode * RelVaultNode::GetChildNodeIncRef ( + unsigned nodeType, + unsigned maxDepth +) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(nodeType); + RelVaultNode * result = GetChildNodeIncRef(templateNode, maxDepth); + templateNode->DecRef(); + return result; +} + +//============================================================================ +RelVaultNode * RelVaultNode::GetChildFolderNodeIncRef ( + unsigned folderType, + unsigned maxDepth +) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_Folder); + VaultFolderNode folder(templateNode); + folder.SetFolderType(folderType); + RelVaultNode * result = GetChildNodeIncRef(templateNode, maxDepth); + templateNode->DecRef(); + return result; +} + +//============================================================================ +RelVaultNode * RelVaultNode::GetChildPlayerInfoListNodeIncRef ( + unsigned folderType, + unsigned maxDepth +) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_PlayerInfoList); + VaultPlayerInfoListNode access(templateNode); + access.SetFolderType(folderType); + RelVaultNode * result = GetChildNodeIncRef(templateNode, maxDepth); + templateNode->DecRef(); + return result; +} + +//============================================================================ +RelVaultNode * RelVaultNode::GetChildAgeInfoListNodeIncRef ( + unsigned folderType, + unsigned maxDepth +) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_AgeInfoList); + VaultAgeInfoListNode access(templateNode); + access.SetFolderType(folderType); + RelVaultNode * result = GetChildNodeIncRef(templateNode, maxDepth); + templateNode->DecRef(); + return result; +} + +//============================================================================ +void RelVaultNode::GetChildNodesIncRef ( + unsigned maxDepth, + ARRAY(RelVaultNode*) * nodes +) { + if (maxDepth == 0) + return; + + RelVaultNodeLink * link; + link = state->children.Head(); + for (; link; link = state->children.Next(link)) { + nodes->Add(link->node); + link->node->IncRef(); + link->node->GetChildNodesIncRef( + maxDepth - 1, + nodes + ); + } +} + +//============================================================================ +void RelVaultNode::GetChildNodesIncRef ( + NetVaultNode * templateNode, + unsigned maxDepth, + ARRAY(RelVaultNode*) * nodes +) { + RelVaultNodeLink * link; + link = state->children.Head(); + for (; link; link = state->children.Next(link)) { + if (link->node->Matches(templateNode)) { + nodes->Add(link->node); + link->node->IncRef(); + } + link->node->GetChildNodesIncRef( + templateNode, + maxDepth - 1, + nodes + ); + } +} + +//============================================================================ +void RelVaultNode::GetChildNodesIncRef ( + unsigned nodeType, + unsigned maxDepth, + ARRAY(RelVaultNode*) * nodes +) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(nodeType); + GetChildNodesIncRef( + templateNode, + maxDepth, + nodes + ); + templateNode->DecRef(); +} + +//============================================================================ +void RelVaultNode::GetChildFolderNodesIncRef ( + unsigned folderType, + unsigned maxDepth, + ARRAY(RelVaultNode*) * nodes +) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_Folder); + VaultFolderNode fldr(templateNode); + fldr.SetFolderType(folderType); + GetChildNodesIncRef( + templateNode, + maxDepth, + nodes + ); + templateNode->DecRef(); +} + +//============================================================================ +unsigned RelVaultNode::GetRefOwnerId (unsigned parentId) { + // find our parents' link to us and return its ownerId + if (RelVaultNodeLink * parentLink = state->parents.Find(parentId)) + if (RelVaultNodeLink * childLink = parentLink->node->state->children.Find(nodeId)) + return childLink->ownerId; + return 0; +} + +//============================================================================ +bool RelVaultNode::BeenSeen (unsigned parentId) const { + // find our parents' link to us and return its seen flag + if (RelVaultNodeLink * parentLink = state->parents.Find(parentId)) + if (RelVaultNodeLink * childLink = parentLink->node->state->children.Find(nodeId)) + return childLink->seen; + return true; +} + +//============================================================================ +void RelVaultNode::SetSeen (unsigned parentId, bool seen) { + // find our parents' link to us and set its seen flag + if (RelVaultNodeLink * parentLink = state->parents.Find(parentId)) + if (RelVaultNodeLink * childLink = parentLink->node->state->children.Find(nodeId)) + if (childLink->seen != seen) { + childLink->seen = seen; + NetCliAuthVaultSetSeen(parentId, nodeId, seen); + } +} + +//============================================================================ +void RelVaultNode::Print (const wchar tag[], FStateDump dumpProc, unsigned level) { + wchar str[1024]; + StrPrintf( + str, + arrsize(str), + L"%s%*s%*s%u, %S", + tag ? tag : L"", + tag ? 1 : 0, + " ", + level * 2, + " ", + nodeId, + plVault::NodeTypeStr(nodeType, false) + ); + + NetVaultNodeFieldArray fields(this); + for (qword bit = 1; bit; bit <<= 1) { + if (!(fieldFlags & bit)) + continue; + if (bit > fieldFlags) + break; + + StrPack(str, L", ", arrsize(str)); + StrPack(str, fields.GetFieldName(bit), arrsize(str)); + if (fields.GetFieldAddress(bit)) { + StrPack(str, L"=", arrsize(str)); + const unsigned chars = StrLen(str); + fields.GetFieldValueString_LCS(bit, str + chars, arrsize(str) - chars * sizeof(str[0])); + } + } + + dumpProc(nil, str); +} + +//============================================================================ +void RelVaultNode::PrintTree (FStateDump dumpProc, unsigned level) { + Print(L"", dumpProc, level); + for (RelVaultNodeLink * link = state->children.Head(); link; link = state->children.Next(link)) + link->node->PrintTree(dumpProc, level + 1); +} + +//============================================================================ +RelVaultNode * RelVaultNode::GetParentAgeLinkIncRef () { + + // this function only makes sense when called on age info nodes + ASSERT(nodeType == plVault::kNodeType_AgeInfo); + + RelVaultNode * result = nil; + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_AgeLink); + + // Get our parent AgeLink node + if (RelVaultNode * rvnLink = GetParentNodeIncRef(templateNode, 1)) { + // Get the next AgeLink node in our parent tree + result = rvnLink->GetParentNodeIncRef(templateNode, 3); + } + + templateNode->DecRef(); + return result; +} + + +/***************************************************************************** +* +* Exports - Callbacks +* +***/ + +//============================================================================ +void VaultRegisterCallback (VaultCallback * cb) { + IVaultCallback * internal = NEW(IVaultCallback); + internal->cb = cb; + cb->internal = internal; + s_callbacks.Link(internal); +} + +//============================================================================ +void VaultUnregisterCallback (VaultCallback * cb) { + ASSERT(cb->internal); + DEL(cb->internal); + cb->internal = nil; +} + + +/***************************************************************************** +* +* Exports - Initialize +* +***/ + +//============================================================================ +void VaultInitialize () { + s_running = true; + + NetCliAuthVaultSetRecvNodeChangedHandler(VaultNodeChanged); + NetCliAuthVaultSetRecvNodeAddedHandler(VaultNodeAdded); + NetCliAuthVaultSetRecvNodeRemovedHandler(VaultNodeRemoved); + NetCliAuthVaultSetRecvNodeDeletedHandler(VaultNodeDeleted); +} + +//============================================================================ +void VaultDestroy () { + s_running = false; + + NetCliAuthVaultSetRecvNodeChangedHandler(nil); + NetCliAuthVaultSetRecvNodeAddedHandler(nil); + NetCliAuthVaultSetRecvNodeRemovedHandler(nil); + NetCliAuthVaultSetRecvNodeDeletedHandler(nil); + + VaultClearDeviceInboxMap(); + + RelVaultNodeLink * next, * link = s_nodes.Head(); + for (; link; link = next) { + next = s_nodes.Next(link); + link->node->state->UnlinkFromRelatives(); + DEL(link); + } +} + +//============================================================================ +void VaultUpdate () { + SaveDirtyNodes(); +} + + +/***************************************************************************** +* +* Exports - Generic Vault Access +* +***/ + +//============================================================================ +static RelVaultNode * GetNode ( + unsigned id +) { + RelVaultNodeLink * link = s_nodes.Find(id); + if (link) + return link->node; + return nil; +} + +//============================================================================ +static RelVaultNode * GetNode ( + NetVaultNode * templateNode +) { + ASSERT(templateNode); + RelVaultNodeLink * link = s_nodes.Head(); + while (link) { + if (link->node->Matches(templateNode)) + return link->node; + link = s_nodes.Next(link); + } + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetNodeIncRef ( + NetVaultNode * templateNode +) { + if (RelVaultNode * node = GetNode(templateNode)) { + node->IncRef(); + return node; + } + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetNodeIncRef ( + unsigned nodeId +) { + if (RelVaultNode * node = GetNode(nodeId)) { + node->IncRef(); + return node; + } + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetNodeIncRef ( + unsigned nodeId, + const char reftag[] +) { + if (RelVaultNodeLink * link = s_nodes.Find(nodeId)) { + link->node->IncRef(reftag); + return link->node; + } + return nil; +} + +//============================================================================ +void VaultAddChildNode ( + unsigned parentId, + unsigned childId, + unsigned ownerId, + FVaultAddChildNodeCallback callback, + void * param +) { + // Make sure we only do the callback once + bool madeCallback = false; + + // Too much of the client relies on the assumption that the node will be immediately + // associated with its parent. THIS SUCKS, because there's no way to guarantee the + // association won't be circular (the db checks this in a comprehensive way). + // Because the client depends on this so much, we just link 'em together here if + // we have both of them present locally. + // This directly affects: New clothing items added to the avatar outfit folder, + // new chronicle entries in some ages, and I'm sure several other situations. + + if (RelVaultNodeLink * parentLink = s_nodes.Find(parentId)) { + RelVaultNodeLink * childLink = s_nodes.Find(childId); + if (!childLink) { + childLink = NEWZERO(RelVaultNodeLink)(false, ownerId, childId, NEWZERO(RelVaultNode)); + childLink->node->nodeId = childId; // set directly so that the field's dirty flag isn't set + s_nodes.Add(childLink); + } + else if (ownerId) { + childLink->ownerId = ownerId; + } + + // We can do a sanity check for a would-be circular link, but it isn't + // authoritative. The db will prevent circular links from entering into + // the persistent state, but because we are hacking in the association + // before the authoritative check, we're risking the local client operating + // on bad, possibly harmful vault state. Not harmful in a national security + // kinda way, but still harmful. + if (parentLink->node->IsChildOf(childId, 255)) { + LogMsg(kLogDebug, L"Node relationship would be circular: p:%u, c:%u", parentId, childId); + // callback now with error code + if (callback) + callback(kNetErrCircularReference, param); + } + else if (childLink->node->IsParentOf(parentId, 255)) { + LogMsg(kLogDebug, L"Node relationship would be circular: p:%u, c:%u", parentId, childId); + // callback now with error code + if (callback) + callback(kNetErrCircularReference, param); + } + else { + NetVaultNodeRef refs[] = { + { parentId, childId, ownerId } + }; + + ARRAY(unsigned) newNodeIds; + ARRAY(unsigned) existingNodeIds; + + BuildNodeTree(refs, arrsize(refs), &newNodeIds, &existingNodeIds); + + if (!childLink->node->nodeType || !parentLink->node->nodeType) { + // One or more nodes need to be fetched before the callback is made + AddChildNodeFetchTrans * trans = NEWZERO(AddChildNodeFetchTrans); + trans->callback = callback; + trans->cbParam = param; + if (!childLink->node->nodeType) { + AtomicAdd(&trans->opCount, 1); + NetCliAuthVaultNodeFetch( + childId, + AddChildNodeFetchTrans::VaultNodeFetched, + trans + ); + AtomicAdd(&trans->opCount, 1); + NetCliAuthVaultFetchNodeRefs( + childId, + AddChildNodeFetchTrans::VaultNodeRefsFetched, + trans + ); + } + if (!parentLink->node->nodeType) { + AtomicAdd(&trans->opCount, 1); + NetCliAuthVaultNodeFetch( + parentId, + AddChildNodeFetchTrans::VaultNodeFetched, + trans + ); + AtomicAdd(&trans->opCount, 1); + NetCliAuthVaultFetchNodeRefs( + parentId, + AddChildNodeFetchTrans::VaultNodeRefsFetched, + trans + ); + } + } + else { + // We have both nodes already, so make the callback now. + if (callback) { + callback(kNetSuccess, param); + madeCallback = true; + } + } + } + } + else { + // Parent doesn't exist locally (and we may not want it to), just make the callback now. + if (callback) { + callback(kNetSuccess, param); + madeCallback = true; + } + } + + // Send it on up to the vault. The db server filters out duplicate and + // circular node relationships. We send the request up even if we think + // the relationship would be circular since the db does a universal + // check and is the only real authority in this matter. + NetCliAuthVaultNodeAdd( + parentId, + childId, + ownerId, + madeCallback ? nil : callback, + madeCallback ? nil : param + ); +} + +//============================================================================ +namespace _VaultAddChildNodeAndWait { + +struct _AddChildNodeParam { + ENetError result; + bool complete; +}; +static void _AddChildNodeCallback ( + ENetError result, + void * vparam +) { + _AddChildNodeParam * param = (_AddChildNodeParam *)vparam; + param->result = result; + param->complete = true; +} + +} // namespace _VaultAddChildNodeAndWait + +//============================================================================ +void VaultAddChildNodeAndWait ( + unsigned parentId, + unsigned childId, + unsigned ownerId +) { + using namespace _VaultAddChildNodeAndWait; + + _AddChildNodeParam param; + ZERO(param); + + VaultAddChildNode( + parentId, + childId, + ownerId, + _AddChildNodeCallback, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) + LogMsg(kLogError, L"VaultAddChildNodeAndWait: Failed to add child node: p:%u,c:%u. %s", parentId, childId, NetErrorToString(param.result)); +} + +//============================================================================ +void VaultRemoveChildNode ( + unsigned parentId, + unsigned childId, + FVaultRemoveChildNodeCallback callback, + void * param +) { + for (;;) { + // Unlink 'em locally, if we can + RelVaultNodeLink * parentLink = s_nodes.Find(parentId); + if (!parentLink) + break; + + RelVaultNodeLink * childLink = s_nodes.Find(childId); + if (!childLink) + break; + + if (parentLink->node->IsParentOf(childId, 1)) { + // We have the relationship, so make the callbacks + for (IVaultCallback * cb = s_callbacks.Head(); cb; cb = s_callbacks.Next(cb)) + cb->cb->RemovingChildNode(parentLink->node, childLink->node); + } + + parentLink->node->state->Unlink(childLink->node); + childLink->node->state->Unlink(parentLink->node); + break; + } + + // Send it on up to the vault + NetCliAuthVaultNodeRemove( + parentId, + childId, + callback, + param + ); +} + +//============================================================================ +void VaultSetNodeSeen ( + unsigned nodeId, + bool seen +) { + hsAssert(false, "eric, implement me"); +} + +//============================================================================ +void VaultDeleteNode ( + unsigned nodeId +) { + // Send request up to vault. We will remove it locally upon notification of deletion. + NetCliAuthVaultNodeDelete(nodeId); +} + +//============================================================================ +void VaultPublishNode ( + unsigned nodeId, + const wchar deviceName[] +) { + RelVaultNode * rvn; + + rvn = VaultAgeGetDeviceInboxIncRef(deviceName); + if (!rvn) { + LogMsg(kLogDebug, L"Failed to find inbox for device %s, adding it on-the-fly", deviceName); + VaultAgeSetDeviceInboxAndWaitIncRef(deviceName, DEFAULT_DEVICE_INBOX); + + rvn = VaultAgeGetDeviceInboxIncRef(deviceName); + if (!rvn) { + LogMsg(kLogDebug, L"Failed to add inbox to device %s on-the-fly", deviceName); + return; + } + } + + VaultAddChildNode(rvn->nodeId, nodeId, VaultGetPlayerId(), nil, nil); + rvn->DecRef(); +} + +//============================================================================ +void VaultSendNode ( + RelVaultNode* srcNode, + unsigned dstPlayerId +) { + NetCliAuthVaultNodeSave(srcNode, nil, nil); + NetCliAuthVaultSendNode(srcNode->nodeId, dstPlayerId); +} + +//============================================================================ +void VaultCreateNode ( + NetVaultNode * templateNode, + FVaultCreateNodeCallback callback, + void * state, + void * param +) { + VaultCreateNodeTrans * trans = NEWZERO(VaultCreateNodeTrans); + trans->callback = callback; + trans->state = state; + trans->param = param; + + if (RelVaultNode * age = VaultGetAgeNodeIncRef()) { + VaultAgeNode access(age); + if (!(templateNode->fieldFlags & NetVaultNode::kCreateAgeName)) + templateNode->SetCreateAgeName(access.ageName); + if (!(templateNode->fieldFlags & NetVaultNode::kCreateAgeUuid)) + templateNode->SetCreateAgeUuid(access.ageInstUuid); + age->DecRef(); + } + + NetCliAuthVaultNodeCreate( + templateNode, + VaultCreateNodeTrans::VaultNodeCreated, + trans + ); +} + +//============================================================================ +void VaultCreateNode ( + plVault::NodeTypes nodeType, + FVaultCreateNodeCallback callback, + void * state, + void * param +) { + RelVaultNode * templateNode = NEWZERO(RelVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(nodeType); + + VaultCreateNode( + templateNode, + callback, + state, + param + ); + + templateNode->DecRef(); +} + +//============================================================================ +namespace _VaultCreateNodeAndWaitIncRef { + +struct _CreateNodeParam { + RelVaultNode * node; + ENetError result; + bool complete; +}; +static void _CreateNodeCallback ( + ENetError result, + void * , + void * vparam, + RelVaultNode * node +) { + _CreateNodeParam * param = (_CreateNodeParam *)vparam; + param->node = node; + param->result = result; + param->complete = true; +} + +} // namespace _VaultCreateNodeAndWaitIncRef + +RelVaultNode * VaultCreateNodeAndWaitIncRef ( + NetVaultNode * templateNode, + ENetError * result +) { + using namespace _VaultCreateNodeAndWaitIncRef; + + _CreateNodeParam param; + ZERO(param); + + VaultCreateNode( + templateNode, + _CreateNodeCallback, + nil, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + *result = param.result; + if (IS_NET_SUCCESS(param.result)) + param.node->IncRef(); + return param.node; +} + +//============================================================================ +RelVaultNode * VaultCreateNodeAndWaitIncRef ( + plVault::NodeTypes nodeType, + ENetError * result +) { + RelVaultNode * node; + RelVaultNode * templateNode = NEWZERO(RelVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(nodeType); + + node = VaultCreateNodeAndWaitIncRef(templateNode, result); + + templateNode->DecRef(); + return node; +} + +//============================================================================ +namespace _VaultForceSaveNodeAndWait { + +struct _SaveNodeParam { + ENetError result; + bool complete; +}; +static void _SaveNodeCallback ( + ENetError result, + void * vparam +) { + _SaveNodeParam * param = (_SaveNodeParam *)vparam; + param->result = result; + param->complete = true; +} + +} // namespace _VaultForceSaveNodeAndWait + +void VaultForceSaveNodeAndWait ( + NetVaultNode * node +) { + using namespace _VaultForceSaveNodeAndWait; + + _SaveNodeParam param; + ZERO(param); + + NetCliAuthVaultNodeSave( + node, + _SaveNodeCallback, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } +} + +//============================================================================ +void VaultFindNodes ( + NetVaultNode * templateNode, + FVaultFindNodeCallback callback, + void * param +) { + VaultFindNodeTrans * trans = NEWZERO(VaultFindNodeTrans); + trans->callback = callback; + trans->param = param; + + NetCliAuthVaultNodeFind( + templateNode, + VaultFindNodeTrans::VaultNodeFound, + trans + ); +} + +//============================================================================ +namespace _VaultFindNodesAndWait { + struct _FindNodeParam { + ARRAY(unsigned) nodeIds; + ENetError result; + bool complete; + }; + static void _FindNodeCallback ( + ENetError result, + void * vparam, + unsigned nodeIdCount, + const unsigned nodeIds[] + ) { + _FindNodeParam * param = (_FindNodeParam *)vparam; + param->nodeIds.Set(nodeIds, nodeIdCount); + param->result = result; + param->complete = true; + } + +} // namespace _VaultFindNodesAndWait + +void VaultFindNodesAndWait ( + NetVaultNode * templateNode, + ARRAY(unsigned) * nodeIds +) { + using namespace _VaultFindNodesAndWait; + + _FindNodeParam param; + ZERO(param); + + NetCliAuthVaultNodeFind( + templateNode, + _FindNodeCallback, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_SUCCESS(param.result)) + nodeIds->Add(param.nodeIds.Ptr(), param.nodeIds.Count()); +} + +//============================================================================ +void VaultLocalFindNodes ( + NetVaultNode * templateNode, + ARRAY(unsigned) * nodeIds +) { + for (RelVaultNodeLink * link = s_nodes.Head(); link != nil; link = s_nodes.Next(link)) { + if (link->node->Matches(templateNode)) + nodeIds->Add(link->node->nodeId); + } +} + +//============================================================================ +namespace _VaultFetchNodesAndWait { + + static void _VaultNodeFetched ( + ENetError result, + void * param, + NetVaultNode * node + ) { + ::VaultNodeFetched(result, nil, node); + + long * nodeCount = (long *)param; + AtomicAdd(nodeCount, -1); + } + +} // namespace _VaultFetchNodesAndWait + +void VaultFetchNodesAndWait ( + const unsigned nodeIds[], + unsigned count, + bool force +) { + using namespace _VaultFetchNodesAndWait; + + long nodeCount = (long)count; + + for (unsigned i = 0; i < count; ++i) { + + if (!force) { + // See if we already have this node + if (RelVaultNodeLink * link = s_nodes.Find(nodeIds[i])) { + AtomicAdd(&nodeCount, -1); + continue; + } + } + + // Start fetching the node + NetCliAuthVaultNodeFetch(nodeIds[i], _VaultNodeFetched, (void *)&nodeCount); + } + + while (nodeCount) { + NetClientUpdate(); + AsyncSleep(10); + } +} + + +//============================================================================ +void VaultInitAge ( + const plAgeInfoStruct * info, + const Uuid & parentAgeInstId, // optional + FVaultInitAgeCallback callback, + void * state, + void * param +) { + VaultAgeInitTrans * trans = NEWZERO(VaultAgeInitTrans); + trans->callback = callback; + trans->cbState = state; + trans->cbParam = param; + + wchar ageFilename[MAX_PATH]; + wchar ageInstName[MAX_PATH]; + wchar ageUserName[MAX_PATH]; + wchar ageDesc[1024]; + + StrToUnicode(ageFilename, info->GetAgeFilename(), arrsize(ageFilename)); + StrToUnicode(ageInstName, info->GetAgeInstanceName(), arrsize(ageInstName)); + StrToUnicode(ageUserName, info->GetAgeUserDefinedName(), arrsize(ageUserName)); + StrToUnicode(ageDesc, info->GetAgeDescription(), arrsize(ageDesc)); + + NetCliAuthVaultInitAge( + (Uuid)*info->GetAgeInstanceGuid(), + parentAgeInstId, + ageFilename, + ageInstName, + ageUserName, + ageDesc, + info->GetAgeSequenceNumber(), + info->GetAgeLanguage(), + VaultAgeInitTrans::AgeInitCallback, + trans + ); +} + +/***************************************************************************** +* +* Exports - Player Vault Access +* +***/ + +//============================================================================ +static RelVaultNode * GetPlayerNode () { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_VNodeMgrPlayer); + if (NetCommGetPlayer()) + templateNode->SetNodeId(NetCommGetPlayer()->playerInt); + RelVaultNode * result = GetNode(templateNode); + templateNode->DecRef(); + return result; +} + +//============================================================================ +unsigned VaultGetPlayerId () { + if (RelVaultNode * rvn = GetPlayerNode()) + return rvn->nodeId; + return 0; +} + +//============================================================================ +RelVaultNode * VaultGetPlayerNodeIncRef () { + if (RelVaultNode * rvnPlr = GetPlayerNode()) { + rvnPlr->IncRef(); + return rvnPlr; + } + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetPlayerInfoNodeIncRef () { + RelVaultNode * rvnPlr = VaultGetPlayerNodeIncRef(); + if (!rvnPlr) + return nil; + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_PlayerInfo); + VaultPlayerInfoNode plrInfo(templateNode); + plrInfo.SetPlayerId(rvnPlr->nodeId); + + rvnPlr->DecRef(); + + RelVaultNode * result = nil; + if (RelVaultNode * rvnPlrInfo = rvnPlr->GetChildNodeIncRef(templateNode, 1)) + result = rvnPlrInfo; + + templateNode->DecRef(); + + return result; +} + +//============================================================================ +RelVaultNode * VaultGetAvatarOutfitFolderIncRef () { + if (RelVaultNode * rvn = GetPlayerNode()) + return rvn->GetChildFolderNodeIncRef(plVault::kAvatarOutfitFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetAvatarClosetFolderIncRef () { + if (RelVaultNode * rvn = GetPlayerNode()) + return rvn->GetChildFolderNodeIncRef(plVault::kAvatarClosetFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetChronicleFolderIncRef () { + if (RelVaultNode * rvn = GetPlayerNode()) + return rvn->GetChildFolderNodeIncRef(plVault::kChronicleFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetAgesIOwnFolderIncRef () { + if (RelVaultNode * rvn = GetPlayerNode()) + return rvn->GetChildAgeInfoListNodeIncRef(plVault::kAgesIOwnFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetAgesICanVisitFolderIncRef () { + if (RelVaultNode * rvn = GetPlayerNode()) + return rvn->GetChildAgeInfoListNodeIncRef(plVault::kAgesICanVisitFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetPlayerInboxFolderIncRef () { + if (RelVaultNode * rvn = GetPlayerNode()) + return rvn->GetChildFolderNodeIncRef(plVault::kInboxFolder, 1); + return nil; +} + +//============================================================================ +bool VaultGetLinkToMyNeighborhood (plAgeLinkStruct * link) { + RelVaultNode * rvnFldr = VaultGetAgesIOwnFolderIncRef(); + if (!rvnFldr) + return false; + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + VaultAgeInfoNode ageInfo(templateNode); + wchar str[MAX_PATH]; + StrToUnicode(str, kNeighborhoodAgeFilename, arrsize(str)); + ageInfo.SetAgeFilename(str); + + RelVaultNode * node; + if (nil != (node = rvnFldr->GetChildNodeIncRef(templateNode, 2))) { + VaultAgeInfoNode info(node); + info.CopyTo(link->GetAgeInfo()); + node->DecRef(); + } + templateNode->DecRef(); + rvnFldr->DecRef(); + + return node != nil; +} + +//============================================================================ +bool VaultGetLinkToMyPersonalAge (plAgeLinkStruct * link) { + RelVaultNode * rvnFldr = VaultGetAgesIOwnFolderIncRef(); + if (!rvnFldr) + return false; + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + VaultAgeInfoNode ageInfo(templateNode); + wchar str[MAX_PATH]; + StrToUnicode(str, kPersonalAgeFilename, arrsize(str)); + ageInfo.SetAgeFilename(str); + + RelVaultNode * node; + if (nil != (node = rvnFldr->GetChildNodeIncRef(templateNode, 2))) { + VaultAgeInfoNode info(node); + info.CopyTo(link->GetAgeInfo()); + node->DecRef(); + } + templateNode->DecRef(); + rvnFldr->DecRef(); + + return node != nil; +} + +//============================================================================ +bool VaultGetLinkToCity (plAgeLinkStruct * link) { + RelVaultNode * rvnFldr = VaultGetAgesIOwnFolderIncRef(); + if (!rvnFldr) + return false; + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + + VaultAgeInfoNode ageInfo(templateNode); + wchar str[MAX_PATH]; + StrToUnicode(str, kCityAgeFilename, arrsize(str)); + ageInfo.SetAgeFilename(str); + + RelVaultNode * node; + if (nil != (node = rvnFldr->GetChildNodeIncRef(templateNode, 2))) { + VaultAgeInfoNode info(node); + info.CopyTo(link->GetAgeInfo()); + node->DecRef(); + } + templateNode->DecRef(); + rvnFldr->DecRef(); + + return node != nil; +} + +//============================================================================ +RelVaultNode * VaultGetOwnedAgeLinkIncRef (const plAgeInfoStruct * info) { + + RelVaultNode * rvnLink = nil; + + if (RelVaultNode * rvnFldr = VaultGetAgesIOwnFolderIncRef()) { + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + + VaultAgeInfoNode ageInfo(templateNode); + if (info->HasAgeFilename()) { + wchar str[MAX_PATH]; + StrToUnicode(str, info->GetAgeFilename(), arrsize(str)); + ageInfo.SetAgeFilename(str); + } + if (info->HasAgeInstanceGuid()) { + ageInfo.SetAgeInstGuid(*info->GetAgeInstanceGuid()); + } + + if (RelVaultNode * rvnInfo = rvnFldr->GetChildNodeIncRef(templateNode, 2)) { + templateNode->fieldFlags = 0; + templateNode->SetNodeType(plVault::kNodeType_AgeLink); + rvnLink = rvnInfo->GetParentNodeIncRef(templateNode, 1); + rvnInfo->DecRef(); + } + + templateNode->DecRef(); + rvnFldr->DecRef(); + } + + return rvnLink; +} + +//============================================================================ +RelVaultNode * VaultGetOwnedAgeInfoIncRef (const plAgeInfoStruct * info) { + + RelVaultNode * rvnInfo = nil; + + if (RelVaultNode * rvnFldr = VaultGetAgesIOwnFolderIncRef()) { + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + + VaultAgeInfoNode ageInfo(templateNode); + if (info->HasAgeFilename()) { + wchar str[MAX_PATH]; + StrToUnicode(str, info->GetAgeFilename(), arrsize(str)); + ageInfo.SetAgeFilename(str); + } + if (info->HasAgeInstanceGuid()) { + ageInfo.SetAgeInstGuid(*info->GetAgeInstanceGuid()); + } + + rvnInfo = rvnFldr->GetChildNodeIncRef(templateNode, 2); + + templateNode->DecRef(); + rvnFldr->DecRef(); + } + + return rvnInfo; +} + +//============================================================================ +bool VaultGetOwnedAgeLink (const plAgeInfoStruct * info, plAgeLinkStruct * link) { + bool result = false; + if (RelVaultNode * rvnLink = VaultGetOwnedAgeLinkIncRef(info)) { + if (RelVaultNode * rvnInfo = rvnLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + VaultAgeInfoNode ageInfo(rvnInfo); + ageInfo.CopyTo(link->GetAgeInfo()); + rvnInfo->DecRef(); + result = true; + } + + rvnLink->DecRef(); + } + + return result; +} + +//============================================================================ +bool VaultFindOrCreateChildAgeLinkAndWait (const wchar ownedAgeName[], const plAgeInfoStruct * info, plAgeLinkStruct * link) { + hsAssert(false, "eric, implement me"); + return false; +} + +//============================================================================ +bool VaultAddOwnedAgeSpawnPoint (const Uuid & ageInstId, const plSpawnPointInfo & spawnPt) { + + RelVaultNode * fldr = nil; + RelVaultNode * link = nil; + + for (;;) { + if (!spawnPt.GetName()) + break; + if (!spawnPt.GetTitle()) + break; + if (!StrLen(spawnPt.GetName())) + break; + if (!StrLen(spawnPt.GetTitle())) + break; + + fldr = VaultGetAgesIOwnFolderIncRef(); + if (!fldr) + break; + + ARRAY(unsigned) nodeIds; + fldr->GetChildNodeIds(&nodeIds, 1); + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + VaultAgeInfoNode access(templateNode); + access.SetAgeInstGuid(ageInstId); + + for (unsigned i = 0; i < nodeIds.Count(); ++i) { + link = VaultGetNodeIncRef(nodeIds[i]); + if (!link) + continue; + if (RelVaultNode * info = link->GetChildNodeIncRef(templateNode, 1)) { + VaultAgeLinkNode access(link); + access.AddSpawnPoint(spawnPt); + info->DecRef(); + link->DecRef(); + link = nil; + break; + } + } + templateNode->DecRef(); + + break; + } + + if (fldr) + fldr->DecRef(); + if (link) + link->DecRef(); + + return true; +} + +//============================================================================ +bool VaultSetOwnedAgePublicAndWait (const plAgeInfoStruct * info, bool publicOrNot) { + if (RelVaultNode * rvnLink = VaultGetOwnedAgeLinkIncRef(info)) { + if (RelVaultNode * rvnInfo = rvnLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + NetCliAuthSetAgePublic(rvnInfo->nodeId, publicOrNot); + + VaultAgeInfoNode access(rvnInfo); + char ageName[MAX_PATH]; + StrToAnsi(ageName, access.ageFilename, arrsize(ageName)); + + plVaultNotifyMsg * msg = NEWZERO(plVaultNotifyMsg); + if (publicOrNot) + msg->SetType(plVaultNotifyMsg::kPublicAgeCreated); + else + msg->SetType(plVaultNotifyMsg::kPublicAgeRemoved); + msg->SetResultCode(true); + msg->GetArgs()->AddString(plNetCommon::VaultTaskArgs::kAgeFilename, ageName); + msg->Send(); + + rvnInfo->DecRef(); + } + rvnLink->DecRef(); + } + return true; +} + +//============================================================================ +RelVaultNode * VaultGetVisitAgeLinkIncRef (const plAgeInfoStruct * info) { + RelVaultNode * rvnLink = nil; + + if (RelVaultNode * rvnFldr = VaultGetAgesICanVisitFolderIncRef()) { + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + + VaultAgeInfoNode ageInfo(templateNode); + if (info->HasAgeFilename()) { + wchar str[MAX_PATH]; + StrToUnicode(str, info->GetAgeFilename(), arrsize(str)); + ageInfo.SetAgeFilename(str); + } + if (info->HasAgeInstanceGuid()) { + ageInfo.SetAgeInstGuid(*info->GetAgeInstanceGuid()); + } + + if (RelVaultNode * rvnInfo = rvnFldr->GetChildNodeIncRef(templateNode, 2)) { + templateNode->fieldFlags = 0; + templateNode->SetNodeType(plVault::kNodeType_AgeLink); + rvnLink = rvnInfo->GetParentNodeIncRef(templateNode, 1); + rvnInfo->DecRef(); + } + + templateNode->DecRef(); + rvnFldr->DecRef(); + } + + return rvnLink; +} + +//============================================================================ +bool VaultGetVisitAgeLink (const plAgeInfoStruct * info, class plAgeLinkStruct * link) { + RelVaultNode * rvn = VaultGetVisitAgeLinkIncRef(info); + if (!rvn) + return false; + + VaultAgeLinkNode ageLink(rvn); + ageLink.CopyTo(link); + + rvn->DecRef(); + return true; +} + +//============================================================================ +namespace _VaultRegisterOwnedAgeAndWait { + +struct _InitAgeParam { + ENetError result; + bool complete; + unsigned ageInfoId; +}; +static void _InitAgeCallback ( + ENetError result, + void * , + void * vparam, + unsigned ageVaultId, + unsigned ageInfoVaultId +) { + _InitAgeParam * param = (_InitAgeParam *)vparam; + param->ageInfoId = ageInfoVaultId; + param->result = result; + param->complete = true; +} +struct _FetchVaultParam { + ENetError result; + bool complete; +}; +static void _FetchVaultCallback ( + ENetError result, + void * vparam +) { + _FetchVaultParam * param = (_FetchVaultParam *)vparam; + param->result = result; + param->complete = true; +} +struct _CreateNodeParam { + ENetError result; + bool complete; + unsigned nodeId; +}; +static void _CreateNodeCallback ( + ENetError result, + void * , + void * vparam, + RelVaultNode * node +) { + _CreateNodeParam * param = (_CreateNodeParam *)vparam; + if (IS_NET_SUCCESS(result)) + param->nodeId = node->nodeId; + param->result = result; + param->complete = true; +} +struct _AddChildNodeParam { + ENetError result; + bool complete; +}; +static void _AddChildNodeCallback ( + ENetError result, + void * vparam +) { + _AddChildNodeParam * param = (_AddChildNodeParam *)vparam; + param->result = result; + param->complete = true; +} + +} // namespace _VaultRegisterOwnedAgeAndWait + +//============================================================================ +bool VaultRegisterOwnedAgeAndWait (const plAgeLinkStruct * link) { + using namespace _VaultRegisterOwnedAgeAndWait; + + unsigned ageLinkId = 0; + unsigned ageInfoId; + unsigned agesIOwnId; + + bool result = false; + + for (;;) { + if (RelVaultNode * rvn = VaultGetAgesIOwnFolderIncRef()) { + agesIOwnId = rvn->nodeId; + rvn->DecRef(); + } + else { + LogMsg(kLogError, L"RegisterOwnedAge: Failed to get player's AgesIOwnFolder"); + break; + } + + // Check for existing link to this age + plAgeLinkStruct existing; + if (VaultGetOwnedAgeLink(link->GetAgeInfo(), &existing)) { + result = true; + break; + } + + { // Init age vault + _InitAgeParam param; + ZERO(param); + + VaultInitAge( + link->GetAgeInfo(), + kNilGuid, + _InitAgeCallback, + nil, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"RegisterOwnedAge: Failed to init age %S", link->GetAgeInfo()->GetAgeFilename()); + break; + } + + ageInfoId = param.ageInfoId; + } + + { // Create age link + _CreateNodeParam param; + ZERO(param); + + VaultCreateNode( + plVault::kNodeType_AgeLink, + _CreateNodeCallback, + nil, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"RegisterOwnedAge: Failed create age link node"); + break; + } + + ageLinkId = param.nodeId; + } + + { // Fetch age info node tree + _FetchVaultParam param; + ZERO(param); + + VaultDownload( + L"RegisterOwnedAge", + ageInfoId, + _FetchVaultCallback, + ¶m, + nil, + nil + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"RegisterOwnedAge: Failed to download age info vault"); + break; + } + } + + { // Link: + // ageLink to player's bookshelf folder + // ageInfo to ageLink + // playerInfo to ageOwners + _AddChildNodeParam param1; + _AddChildNodeParam param2; + _AddChildNodeParam param3; + ZERO(param1); + ZERO(param2); + ZERO(param3); + + unsigned ageOwnersId = 0; + if (RelVaultNode * rvnAgeInfo = VaultGetNodeIncRef(ageInfoId)) { + if (RelVaultNode * rvnAgeOwners = rvnAgeInfo->GetChildPlayerInfoListNodeIncRef(plVault::kAgeOwnersFolder, 1)) { + ageOwnersId = rvnAgeOwners->nodeId; + rvnAgeOwners->DecRef(); + } + rvnAgeInfo->DecRef(); + } + + unsigned playerInfoId = 0; + if (RelVaultNode * rvnPlayerInfo = VaultGetPlayerInfoNodeIncRef()) { + playerInfoId = rvnPlayerInfo->nodeId; + rvnPlayerInfo->DecRef(); + } + + VaultAddChildNode( + agesIOwnId, + ageLinkId, + 0, + _AddChildNodeCallback, + ¶m1 + ); + + VaultAddChildNode( + ageLinkId, + ageInfoId, + 0, + _AddChildNodeCallback, + ¶m2 + ); + + VaultAddChildNode( + ageOwnersId, + playerInfoId, + 0, + _AddChildNodeCallback, + ¶m3 + ); + + while (!param1.complete && !param2.complete && !param3.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param1.result)) { + LogMsg(kLogError, L"RegisterOwnedAge: Failed to add link to player's bookshelf"); + break; + } + if (IS_NET_ERROR(param2.result)) { + LogMsg(kLogError, L"RegisterOwnedAge: Failed to add info to link"); + break; + } + if (IS_NET_ERROR(param3.result)) { + LogMsg(kLogError, L"RegisterOwnedAge: Failed to add playerInfo to ageOwners"); + break; + } + } + + // Copy the link spawn point to the link node + if (RelVaultNode * node = VaultGetNodeIncRef(ageLinkId)) { + VaultAgeLinkNode access(node); + access.AddSpawnPoint(link->SpawnPoint()); + node->DecRef(); + } + + result = true; + break; + } + + plVaultNotifyMsg * msg = NEWZERO(plVaultNotifyMsg); + msg->SetType(plVaultNotifyMsg::kRegisteredOwnedAge); + msg->SetResultCode(result); + msg->GetArgs()->AddInt(plNetCommon::VaultTaskArgs::kAgeLinkNode, ageLinkId); + msg->Send(); + + return result; +} + +//============================================================================ +namespace _VaultRegisterVisitAgeAndWait { + +struct _InitAgeParam { + ENetError result; + bool complete; + unsigned ageInfoId; +}; +static void _InitAgeCallback ( + ENetError result, + void * , + void * vparam, + unsigned ageVaultId, + unsigned ageInfoVaultId +) { + _InitAgeParam * param = (_InitAgeParam *)vparam; + param->ageInfoId = ageInfoVaultId; + param->result = result; + param->complete = true; +} +struct _FetchVaultParam { + ENetError result; + bool complete; +}; +static void _FetchVaultCallback ( + ENetError result, + void * vparam +) { + _FetchVaultParam * param = (_FetchVaultParam *)vparam; + param->result = result; + param->complete = true; +} +struct _CreateNodeParam { + ENetError result; + bool complete; + unsigned nodeId; +}; +static void _CreateNodeCallback ( + ENetError result, + void * , + void * vparam, + RelVaultNode * node +) { + _CreateNodeParam * param = (_CreateNodeParam *)vparam; + if (IS_NET_SUCCESS(result)) + param->nodeId = node->nodeId; + param->result = result; + param->complete = true; +} +struct _AddChildNodeParam { + ENetError result; + bool complete; +}; +static void _AddChildNodeCallback ( + ENetError result, + void * vparam +) { + _AddChildNodeParam * param = (_AddChildNodeParam *)vparam; + param->result = result; + param->complete = true; +} + +} // namespace _VaultRegisterVisitAgeAndWait + +//============================================================================ +bool VaultRegisterVisitAgeAndWait (const plAgeLinkStruct * link) { + using namespace _VaultRegisterVisitAgeAndWait; + + unsigned ageLinkId = 0; + unsigned ageInfoId; + unsigned agesICanVisitId; + + bool result = false; + for (;;) { + if (RelVaultNode * rvn = VaultGetAgesICanVisitFolderIncRef()) { + agesICanVisitId = rvn->nodeId; + rvn->DecRef(); + } + else { + LogMsg(kLogError, L"RegisterVisitAge: Failed to get player's AgesICanVisitFolder"); + break; + } + + // Check for existing link to this age + plAgeLinkStruct existing; + if (VaultGetVisitAgeLink(link->GetAgeInfo(), &existing)) { + result = true; + break; + } + + + { // Init age vault + _InitAgeParam param; + ZERO(param); + + VaultInitAge( + link->GetAgeInfo(), + kNilGuid, + _InitAgeCallback, + nil, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"RegisterVisitAge: Failed to init age %S", link->GetAgeInfo()->GetAgeFilename()); + break; + } + + ageInfoId = param.ageInfoId; + } + + { // Create age link + _CreateNodeParam param; + ZERO(param); + + VaultCreateNode( + plVault::kNodeType_AgeLink, + _CreateNodeCallback, + nil, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"RegisterVisitAge: Failed create age link node"); + break; + } + + ageLinkId = param.nodeId; + } + + { // Fetch age info node tree + _FetchVaultParam param; + ZERO(param); + + VaultDownload( + L"RegisterVisitAge", + ageInfoId, + _FetchVaultCallback, + ¶m, + nil, + nil + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"RegisterVisitAge: Failed to download age info vault"); + break; + } + } + + { // Link: + // ageLink to player's "can visit" folder + // ageInfo to ageLink + _AddChildNodeParam param1; + _AddChildNodeParam param2; + _AddChildNodeParam param3; + ZERO(param1); + ZERO(param2); + ZERO(param3); + + unsigned ageVisitorsId = 0; + if (RelVaultNode * rvnAgeInfo = VaultGetNodeIncRef(ageInfoId)) { + if (RelVaultNode * rvnAgeVisitors = rvnAgeInfo->GetChildPlayerInfoListNodeIncRef(plVault::kCanVisitFolder, 1)) { + ageVisitorsId = rvnAgeVisitors->nodeId; + rvnAgeVisitors->DecRef(); + } + rvnAgeInfo->DecRef(); + } + + unsigned playerInfoId = 0; + if (RelVaultNode * rvnPlayerInfo = VaultGetPlayerInfoNodeIncRef()) { + playerInfoId = rvnPlayerInfo->nodeId; + rvnPlayerInfo->DecRef(); + } + + VaultAddChildNode( + agesICanVisitId, + ageLinkId, + 0, + _AddChildNodeCallback, + ¶m1 + ); + + VaultAddChildNode( + ageLinkId, + ageInfoId, + 0, + _AddChildNodeCallback, + ¶m2 + ); + + VaultAddChildNode( + ageVisitorsId, + playerInfoId, + 0, + _AddChildNodeCallback, + ¶m3 + ); + + while (!param1.complete && !param2.complete && !param3.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param1.result)) { + LogMsg(kLogError, L"RegisterVisitAge: Failed to add link to folder"); + break; + } + if (IS_NET_ERROR(param2.result)) { + LogMsg(kLogError, L"RegisterVisitAge: Failed to add info to link"); + break; + } + if (IS_NET_ERROR(param3.result)) { + LogMsg(kLogError, L"RegisterVisitAge: Failed to add playerInfo to canVisit folder"); + break; + } + } + + // Copy the link spawn point to the link node + if (RelVaultNode * node = VaultGetNodeIncRef(ageLinkId)) { + VaultAgeLinkNode access(node); + access.AddSpawnPoint(link->SpawnPoint()); + node->DecRef(); + } + + result = true; + break; + } + + plVaultNotifyMsg * msg = NEWZERO(plVaultNotifyMsg); + msg->SetType(plVaultNotifyMsg::kRegisteredVisitAge); + msg->SetResultCode(result); + msg->GetArgs()->AddInt(plNetCommon::VaultTaskArgs::kAgeLinkNode, ageLinkId); + msg->Send(); + + return result; +} + + +//============================================================================ +bool VaultUnregisterOwnedAgeAndWait (const plAgeInfoStruct * info) { + + unsigned ageLinkId = 0; + unsigned agesIOwnId; + + bool result = false; + for (;;) { + RelVaultNode * rvnLink = VaultGetOwnedAgeLinkIncRef(info); + if (!rvnLink) { + result = true; + break; // we aren't an owner of the age, just return true + } + + if (RelVaultNode * rvn = VaultGetAgesIOwnFolderIncRef()) { + agesIOwnId = rvn->nodeId; + rvn->DecRef(); + } + else { + LogMsg(kLogError, L"UnregisterOwnedAge: Failed to get player's AgesIOwnFolder"); + break; // something's wrong with the player vault, it doesn't have a required folder node + } + + ageLinkId = rvnLink->nodeId; + + unsigned ageOwnersId = 0; + if (RelVaultNode * rvnAgeInfo = rvnLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + if (RelVaultNode * rvnAgeOwners = rvnAgeInfo->GetChildPlayerInfoListNodeIncRef(plVault::kAgeOwnersFolder, 1)) { + ageOwnersId = rvnAgeOwners->nodeId; + rvnAgeOwners->DecRef(); + } + rvnAgeInfo->DecRef(); + } + + unsigned playerInfoId = 0; + if (RelVaultNode * rvnPlayerInfo = VaultGetPlayerInfoNodeIncRef()) { + playerInfoId = rvnPlayerInfo->nodeId; + rvnPlayerInfo->DecRef(); + } + + rvnLink->DecRef(); + + // remove our playerInfo from the ageOwners folder + VaultRemoveChildNode(ageOwnersId, playerInfoId, nil, nil); + + // remove the link from AgesIOwn folder + VaultRemoveChildNode(agesIOwnId, ageLinkId, nil, nil); + + // delete the link node since link nodes aren't shared with anyone else + // VaultDeleteNode(ageLinkId); + + result = true; + break; + } + + plVaultNotifyMsg * msg = NEWZERO(plVaultNotifyMsg); + msg->SetType(plVaultNotifyMsg::kUnRegisteredOwnedAge); + msg->SetResultCode(result); + msg->GetArgs()->AddInt(plNetCommon::VaultTaskArgs::kAgeLinkNode, ageLinkId); + msg->Send(); + + return result; +} + +//============================================================================ +bool VaultUnregisterVisitAgeAndWait (const plAgeInfoStruct * info) { + + unsigned ageLinkId = 0; + unsigned agesICanVisitId; + + bool result = false; + for (;;) { + RelVaultNode * rvnLink = VaultGetVisitAgeLinkIncRef(info); + if (!rvnLink) { + result = true; + break; // we aren't an owner of the age, just return true + } + + if (RelVaultNode * rvn = VaultGetAgesICanVisitFolderIncRef()) { + agesICanVisitId = rvn->nodeId; + rvn->DecRef(); + } + else { + LogMsg(kLogError, L"UnregisterOwnedAge: Failed to get player's AgesICanVisitFolder"); + break; // something's wrong with the player vault, it doesn't have a required folder node + } + + ageLinkId = rvnLink->nodeId; + + unsigned ageVisitorsId = 0; + if (RelVaultNode * rvnAgeInfo = rvnLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + if (RelVaultNode * rvnAgeVisitors = rvnAgeInfo->GetChildPlayerInfoListNodeIncRef(plVault::kCanVisitFolder, 1)) { + ageVisitorsId = rvnAgeVisitors->nodeId; + rvnAgeVisitors->DecRef(); + } + rvnAgeInfo->DecRef(); + } + + unsigned playerInfoId = 0; + if (RelVaultNode * rvnPlayerInfo = VaultGetPlayerInfoNodeIncRef()) { + playerInfoId = rvnPlayerInfo->nodeId; + rvnPlayerInfo->DecRef(); + } + + rvnLink->DecRef(); + + // remove our playerInfo from the ageVisitors folder + VaultRemoveChildNode(ageVisitorsId, playerInfoId, nil, nil); + + // remove the link from AgesICanVisit folder + VaultRemoveChildNode(agesICanVisitId, ageLinkId, nil, nil); + + // delete the link node since link nodes aren't shared with anyone else + // VaultDeleteNode(ageLinkId); + + result = true; + break; + } + + plVaultNotifyMsg * msg = NEWZERO(plVaultNotifyMsg); + msg->SetType(plVaultNotifyMsg::kUnRegisteredVisitAge); + msg->SetResultCode(result); + msg->GetArgs()->AddInt(plNetCommon::VaultTaskArgs::kAgeLinkNode, ageLinkId); + msg->Send(); + + return result; +} + +//============================================================================ +RelVaultNode * VaultFindChronicleEntryIncRef (const wchar entryName[], int entryType) { + + RelVaultNode * result = nil; + if (RelVaultNode * rvnFldr = GetChildFolderNode(GetPlayerNode(), plVault::kChronicleFolder, 1)) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_Chronicle); + VaultChronicleNode chrn(templateNode); + chrn.SetEntryName(entryName); + if (entryType >= 0) + chrn.SetEntryType(entryType); + if (RelVaultNode * rvnChrn = rvnFldr->GetChildNodeIncRef(templateNode, 255)) + result = rvnChrn; + templateNode->DecRef(); + } + return result; +} + +//============================================================================ +bool VaultHasChronicleEntry (const wchar entryName[], int entryType) { + if (RelVaultNode * rvn = VaultFindChronicleEntryIncRef(entryName, entryType)) { + rvn->DecRef(); + return true; + } + return false; +} + +//============================================================================ +void VaultAddChronicleEntryAndWait ( + const wchar entryName[], + int entryType, + const wchar entryValue[] +) { + if (RelVaultNode * rvnChrn = VaultFindChronicleEntryIncRef(entryName, entryType)) { + VaultChronicleNode chrnNode(rvnChrn); + chrnNode.SetEntryValue(entryValue); + } + else if (RelVaultNode * rvnFldr = GetChildFolderNode(GetPlayerNode(), plVault::kChronicleFolder, 1)) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_Chronicle); + VaultChronicleNode chrnNode(templateNode); + chrnNode.SetEntryName(entryName); + chrnNode.SetEntryType(entryType); + chrnNode.SetEntryValue(entryValue); + ENetError result; + if (RelVaultNode * rvnChrn = VaultCreateNodeAndWaitIncRef(templateNode, &result)) { + VaultAddChildNode(rvnFldr->nodeId, rvnChrn->nodeId, 0, nil, nil); + rvnChrn->DecRef(); + } + templateNode->DecRef(); + } +} + +//============================================================================ +bool VaultAmIgnoringPlayer (unsigned playerId) { + bool retval = false; + if (RelVaultNode * rvnFldr = GetChildPlayerInfoListNode(GetPlayerNode(), plVault::kIgnoreListFolder, 1)) { + rvnFldr->IncRef(); + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_PlayerInfo); + VaultPlayerInfoNode pinfoNode(templateNode); + pinfoNode.SetPlayerId(playerId); + + if (RelVaultNode * rvnPlayerInfo = rvnFldr->GetChildNodeIncRef(templateNode, 1)) { + retval = true; + rvnPlayerInfo->DecRef(); + } + + templateNode->DecRef(); + rvnFldr->DecRef(); + } + + return retval; +} + +//============================================================================ +unsigned VaultGetKILevel () { + hsAssert(false, "eric, implement me"); + return pfKIMsg::kNanoKI; +} + +//============================================================================ +bool VaultGetCCRStatus () { + bool retval = false; + if (RelVaultNode * rvnSystem = VaultGetSystemNodeIncRef()) { + VaultSystemNode sysNode(rvnSystem); + retval = (sysNode.ccrStatus != 0); + + rvnSystem->DecRef(); + } + + return retval; +} + +//============================================================================ +bool VaultSetCCRStatus (bool online) { + bool retval = false; + if (RelVaultNode * rvnSystem = VaultGetSystemNodeIncRef()) { + VaultSystemNode sysNode(rvnSystem); + sysNode.SetCCRStatus(online ? 1 : 0); + + rvnSystem->DecRef(); + retval = true; + } + + return retval; +} + +//============================================================================ +void VaultDump (const wchar tag[], unsigned vaultId, FStateDump dumpProc) { + LogMsg(kLogDebug, L"<---- ID:%u, Begin Vault%*s%s ---->", vaultId, tag ? 1 : 0, L" ", tag); + + if (RelVaultNode * rvn = GetNode(vaultId)) + rvn->PrintTree(dumpProc, 0); + + LogMsg(kLogDebug, L"<---- ID:%u, End Vault%*s%s ---->", vaultId, tag ? 1 : 0, L" ", tag); +} + +//============================================================================ +void VaultDump (const wchar tag[], unsigned vaultId) { + VaultDump (tag, vaultId, LogDumpProc); +} + +//============================================================================ +bool VaultAmInMyPersonalAge () { + bool result = false; + + plAgeInfoStruct info; + info.SetAgeFilename(kPersonalAgeFilename); + + if (RelVaultNode * rvnLink = VaultGetOwnedAgeLinkIncRef(&info)) { + if (RelVaultNode * rvnInfo = rvnLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + VaultAgeInfoNode ageInfo(rvnInfo); + + if (RelVaultNode* currentAgeInfoNode = VaultGetAgeInfoNodeIncRef()) { + VaultAgeInfoNode curAgeInfo(currentAgeInfoNode); + + if (ageInfo.ageInstUuid == curAgeInfo.ageInstUuid) + result = true; + + currentAgeInfoNode->DecRef(); + } + + rvnInfo->DecRef(); + } + + rvnLink->DecRef(); + } + + return result; +} + +//============================================================================ +bool VaultAmInMyNeighborhoodAge () { + bool result = false; + + plAgeInfoStruct info; + info.SetAgeFilename(kNeighborhoodAgeFilename); + + if (RelVaultNode * rvnLink = VaultGetOwnedAgeLinkIncRef(&info)) { + if (RelVaultNode * rvnInfo = rvnLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + VaultAgeInfoNode ageInfo(rvnInfo); + + if (RelVaultNode* currentAgeInfoNode = VaultGetAgeInfoNodeIncRef()) { + VaultAgeInfoNode curAgeInfo(currentAgeInfoNode); + + if (ageInfo.ageInstUuid == curAgeInfo.ageInstUuid) + result = true; + + currentAgeInfoNode->DecRef(); + } + + rvnInfo->DecRef(); + } + + rvnLink->DecRef(); + } + + return result; +} + +//============================================================================ +bool VaultAmOwnerOfCurrentAge () { + bool result = false; + + if (RelVaultNode* currentAgeInfoNode = VaultGetAgeInfoNodeIncRef()) { + VaultAgeInfoNode curAgeInfo(currentAgeInfoNode); + + char* ageFilename = StrDupToAnsi(curAgeInfo.ageFilename); + + plAgeInfoStruct info; + info.SetAgeFilename(ageFilename); + + FREE(ageFilename); + + if (RelVaultNode * rvnLink = VaultGetOwnedAgeLinkIncRef(&info)) { + + if (RelVaultNode * rvnInfo = rvnLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + VaultAgeInfoNode ageInfo(rvnInfo); + + if (ageInfo.ageInstUuid == curAgeInfo.ageInstUuid) + result = true; + + rvnInfo->DecRef(); + } + + rvnLink->DecRef(); + } + + currentAgeInfoNode->DecRef(); + } + + return result; +} + +//============================================================================ +bool VaultAmCzarOfCurrentAge () { + hsAssert(false, "eric, implement me"); + return true; +} + +//============================================================================ +bool VaultAmOwnerOfAge (const Uuid & ageInstId) { + hsAssert(false, "eric, implement me"); + return true; +} + +//============================================================================ +bool VaultAmCzarOfAge (const Uuid & ageInstId) { +// hsAssert(false, "eric, implement me"); + return false; +} + +//============================================================================ +bool VaultRegisterMTStationAndWait ( + const wchar stationName[], + const wchar linkBackSpawnPtObjName[] +) { + plAgeInfoStruct info; + info.SetAgeFilename(kCityAgeFilename); + if (RelVaultNode * rvn = VaultGetOwnedAgeLinkIncRef(&info)) { + char title[MAX_PATH], spawnPt[MAX_PATH]; + StrToAnsi(title, stationName, arrsize(title)); + StrToAnsi(spawnPt, linkBackSpawnPtObjName, arrsize(spawnPt)); + VaultAgeLinkNode link(rvn); + link.AddSpawnPoint(plSpawnPointInfo(title, spawnPt)); + rvn->DecRef(); + return true; + } + return false; +} + +//============================================================================ +void VaultProcessVisitNote(RelVaultNode * rvnVisit) { + if (RelVaultNode * rvnInbox = VaultGetPlayerInboxFolderIncRef()) { + rvnVisit->IncRef(); + + VaultTextNoteNode visitAcc(rvnVisit); + plAgeLinkStruct link; + if (visitAcc.GetVisitInfo(link.GetAgeInfo())) { + // Add it to our "ages i can visit" folder + VaultRegisterVisitAgeAndWait(&link); + } + // remove it from the inbox + VaultRemoveChildNode(rvnInbox->nodeId, rvnVisit->nodeId, nil, nil); + rvnVisit->DecRef(); + + rvnInbox->DecRef(); + } +} + +//============================================================================ +void VaultProcessUnvisitNote(RelVaultNode * rvnUnVisit) { + if (RelVaultNode * rvnInbox = VaultGetPlayerInboxFolderIncRef()) { + rvnUnVisit->IncRef(); + VaultTextNoteNode unvisitAcc(rvnUnVisit); + plAgeInfoStruct info; + if (unvisitAcc.GetVisitInfo(&info)) { + // Remove it from our "ages i can visit" folder + VaultUnregisterVisitAgeAndWait(&info); + } + // remove it from the inbox + VaultRemoveChildNode(rvnInbox->nodeId, rvnUnVisit->nodeId, nil, nil); + rvnUnVisit->DecRef(); + + rvnInbox->DecRef(); + } +} + +//============================================================================ +void VaultProcessPlayerInbox () { + if (RelVaultNode * rvnInbox = VaultGetPlayerInboxFolderIncRef()) { + { // Process new visit requests + ARRAY(RelVaultNode*) visits; + RelVaultNode * templateNode = NEWZERO(RelVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_TextNote); + VaultTextNoteNode tmpAcc(templateNode); + tmpAcc.SetNoteType(plVault::kNoteType_Visit); + rvnInbox->GetChildNodesIncRef(templateNode, 1, &visits); + templateNode->DecRef(); + + for (unsigned i = 0; i < visits.Count(); ++i) { + RelVaultNode * rvnVisit = visits[i]; + VaultTextNoteNode visitAcc(rvnVisit); + plAgeLinkStruct link; + if (visitAcc.GetVisitInfo(link.GetAgeInfo())) { + // Add it to our "ages i can visit" folder + VaultRegisterVisitAgeAndWait(&link); + } + // remove it from the inbox + VaultRemoveChildNode(rvnInbox->nodeId, rvnVisit->nodeId, nil, nil); + rvnVisit->DecRef(); + } + } + { // Process new unvisit requests + ARRAY(RelVaultNode*) unvisits; + RelVaultNode * templateNode = NEWZERO(RelVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_TextNote); + VaultTextNoteNode tmpAcc(templateNode); + tmpAcc.SetNoteType(plVault::kNoteType_UnVisit); + rvnInbox->GetChildNodesIncRef(templateNode, 1, &unvisits); + templateNode->DecRef(); + + for (unsigned i = 0; i < unvisits.Count(); ++i) { + RelVaultNode * rvnUnVisit = unvisits[i]; + VaultTextNoteNode unvisitAcc(rvnUnVisit); + plAgeInfoStruct info; + if (unvisitAcc.GetVisitInfo(&info)) { + // Remove it from our "ages i can visit" folder + VaultUnregisterVisitAgeAndWait(&info); + } + // remove it from the inbox + VaultRemoveChildNode(rvnInbox->nodeId, rvnUnVisit->nodeId, nil, nil); + rvnUnVisit->DecRef(); + } + } + + rvnInbox->DecRef(); + } +} + + +/***************************************************************************** +* +* Exports - Age Vault Access +* +***/ + +//============================================================================ +static RelVaultNode * GetAgeNode () { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_VNodeMgrAge); + if (NetCommGetAge()) + templateNode->SetNodeId(NetCommGetAge()->ageVaultId); + RelVaultNode * result = GetNode(templateNode); + templateNode->DecRef(); + return result; +} + +//============================================================================ +RelVaultNode * VaultGetAgeNodeIncRef () { + RelVaultNode * result = nil; + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_VNodeMgrAge); + if (NetCommGetAge()) + templateNode->SetNodeId(NetCommGetAge()->ageVaultId); + if (RelVaultNode * rvnAge = VaultGetNodeIncRef(templateNode)) + result = rvnAge; + templateNode->DecRef(); + return result; +} + +//============================================================================ +static RelVaultNode * GetAgeInfoNode () { + RelVaultNode * rvnAge = VaultGetAgeNodeIncRef(); + if (!rvnAge) + return nil; + + RelVaultNode * result = nil; + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + templateNode->SetCreatorId(rvnAge->nodeId); + + if (RelVaultNode * rvnAgeInfo = rvnAge->GetChildNodeIncRef(templateNode, 1)) { + rvnAgeInfo->DecRef(); + result = rvnAgeInfo; + } + + templateNode->DecRef(); + rvnAge->DecRef(); + + return result; +} + +//============================================================================ +RelVaultNode * VaultGetAgeInfoNodeIncRef () { + RelVaultNode * rvnAge = VaultGetAgeNodeIncRef(); + if (!rvnAge) + return nil; + + RelVaultNode * result = nil; + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + templateNode->SetCreatorId(rvnAge->nodeId); + + if (RelVaultNode * rvnAgeInfo = rvnAge->GetChildNodeIncRef(templateNode, 1)) + result = rvnAgeInfo; + + templateNode->DecRef(); + rvnAge->DecRef(); + + return result; +} + +//============================================================================ +RelVaultNode * VaultGetAgeChronicleFolderIncRef () { + if (RelVaultNode * rvn = GetAgeNode()) + return rvn->GetChildFolderNodeIncRef(plVault::kChronicleFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetAgeDevicesFolderIncRef () { + if (RelVaultNode * rvn = GetAgeNode()) + return rvn->GetChildFolderNodeIncRef(plVault::kAgeDevicesFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetAgeAgeOwnersFolderIncRef () { + if (RelVaultNode * rvn = GetAgeInfoNode()) + return rvn->GetChildPlayerInfoListNodeIncRef(plVault::kAgeOwnersFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetAgeCanVisitFolderIncRef () { + if (RelVaultNode * rvn = GetAgeInfoNode()) + return rvn->GetChildPlayerInfoListNodeIncRef(plVault::kCanVisitFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetAgePeopleIKnowAboutFolderIncRef () { + if (RelVaultNode * rvn = GetAgeNode()) + return rvn->GetChildPlayerInfoListNodeIncRef(plVault::kPeopleIKnowAboutFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetAgeSubAgesFolderIncRef () { + if (RelVaultNode * rvn = GetAgeNode()) + return rvn->GetChildAgeInfoListNodeIncRef(plVault::kSubAgesFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultGetAgePublicAgesFolderIncRef () { + hsAssert(false, "eric, implement me"); + return nil; +} + +//============================================================================ +RelVaultNode * VaultAgeGetBookshelfFolderIncRef () { + if (RelVaultNode * rvn = GetAgeNode()) + return rvn->GetChildAgeInfoListNodeIncRef(plVault::kAgesIOwnFolder, 1); + return nil; +} + +//============================================================================ +RelVaultNode * VaultFindAgeSubAgeLinkIncRef (const plAgeInfoStruct * info) { + RelVaultNode * rvnLink = nil; + + if (RelVaultNode * rvnFldr = VaultGetAgeSubAgesFolderIncRef()) { + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + + VaultAgeInfoNode ageInfo(templateNode); + wchar str[MAX_PATH]; + StrToUnicode(str, info->GetAgeFilename(), arrsize(str)); + ageInfo.SetAgeFilename(str); + + if (RelVaultNode * rvnInfo = rvnFldr->GetChildNodeIncRef(templateNode, 2)) { + templateNode->fieldFlags = 0; + templateNode->SetNodeType(plVault::kNodeType_AgeLink); + rvnLink = rvnInfo->GetParentNodeIncRef(templateNode, 1); + rvnInfo->DecRef(); + } + + templateNode->DecRef(); + rvnFldr->DecRef(); + } + + return rvnLink; +} + +//============================================================================ +RelVaultNode * VaultFindAgeChronicleEntryIncRef (const wchar entryName[], int entryType) { + hsAssert(false, "eric, implement me"); + return nil; +} + +//============================================================================ +void VaultAddAgeChronicleEntry ( + const wchar entryName[], + int entryType, + const wchar entryValue[] +) { + hsAssert(false, "eric, implement me"); +} + +//============================================================================ +RelVaultNode * VaultAgeAddDeviceAndWaitIncRef (const wchar deviceName[]) { + if (RelVaultNode * existing = VaultAgeGetDeviceIncRef(deviceName)) + return existing; + + RelVaultNode * device = nil; + RelVaultNode * folder = nil; + + for (;;) { + folder = VaultGetAgeDevicesFolderIncRef(); + if (!folder) + break; + + ENetError result; + device = VaultCreateNodeAndWaitIncRef(plVault::kNodeType_TextNote, &result); + if (!device) + break; + + VaultTextNoteNode access(device); + access.SetNoteType(plVault::kNoteType_Device); + access.SetNoteTitle(deviceName); + + VaultAddChildNodeAndWait(folder->nodeId, device->nodeId, 0); + break; + } + + if (folder) + folder->DecRef(); + + return device; +} + +//============================================================================ +void VaultAgeRemoveDevice (const wchar deviceName[]) { + if (RelVaultNode * folder = VaultGetAgeDevicesFolderIncRef()) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_TextNote); + VaultTextNoteNode access(templateNode); + access.SetNoteTitle(deviceName); + if (RelVaultNode * device = folder->GetChildNodeIncRef(templateNode, 1)) { + VaultRemoveChildNode(folder->nodeId, device->nodeId, nil, nil); + device->DecRef(); + + if (DeviceInbox * deviceInbox = s_ageDeviceInboxes.Find(CHashKeyStr(deviceName))) + DEL(device); + } + templateNode->DecRef(); + folder->DecRef(); + } +} + +//============================================================================ +bool VaultAgeHasDevice (const wchar deviceName[]) { + bool found = false; + if (RelVaultNode * folder = VaultGetAgeDevicesFolderIncRef()) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_TextNote); + VaultTextNoteNode access(templateNode); + access.SetNoteTitle(deviceName); + if (RelVaultNode * device = folder->GetChildNodeIncRef(templateNode, 1)) { + found = true; + device->DecRef(); + } + templateNode->DecRef(); + folder->DecRef(); + } + return found; +} + +//============================================================================ +RelVaultNode * VaultAgeGetDeviceIncRef (const wchar deviceName[]) { + RelVaultNode * result = nil; + if (RelVaultNode * folder = VaultGetAgeDevicesFolderIncRef()) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_TextNote); + VaultTextNoteNode access(templateNode); + access.SetNoteTitle(deviceName); + if (RelVaultNode * device = folder->GetChildNodeIncRef(templateNode, 1)) + result = device; + templateNode->DecRef(); + folder->DecRef(); + } + return result; +} + +//============================================================================ +RelVaultNode * VaultAgeSetDeviceInboxAndWaitIncRef (const wchar deviceName[], const wchar inboxName[]) { + DeviceInbox * devInbox = s_ageDeviceInboxes.Find(CHashKeyStr(deviceName)); + if (devInbox) { + StrCopy(devInbox->inboxName, inboxName, arrsize(devInbox->inboxName)); + } + else { + devInbox = NEWZERO(DeviceInbox)(deviceName, inboxName); + s_ageDeviceInboxes.Add(devInbox); + } + + // if we found the inbox or its a global inbox then return here, otherwise if its the default inbox and + // it wasn't found then continue on and create the inbox + RelVaultNode * existing = VaultAgeGetDeviceInboxIncRef(deviceName); + if (existing || StrCmp(inboxName, DEFAULT_DEVICE_INBOX) != 0) + return existing; + + RelVaultNode * device = nil; + RelVaultNode * inbox = nil; + + for (;;) { + device = VaultAgeGetDeviceIncRef(deviceName); + if (!device) + break; + + ENetError result; + inbox = VaultCreateNodeAndWaitIncRef(plVault::kNodeType_Folder, &result); + if (!inbox) + break; + + VaultFolderNode access(inbox); + access.SetFolderName(inboxName); + access.SetFolderType(plVault::kDeviceInboxFolder); + + VaultAddChildNodeAndWait(device->nodeId, inbox->nodeId, 0); + break; + } + + return inbox; +} + +//============================================================================ +RelVaultNode * VaultAgeGetDeviceInboxIncRef (const wchar deviceName[]) { + RelVaultNode * result = nil; + DeviceInbox * devInbox = s_ageDeviceInboxes.Find(CHashKeyStr(deviceName)); + + if (devInbox) + { + RelVaultNode * parentNode = nil; + const wchar * inboxName = nil; + + if (StrCmp(devInbox->inboxName, DEFAULT_DEVICE_INBOX) == 0) { + parentNode = VaultAgeGetDeviceIncRef(deviceName); + } + else { + parentNode = VaultGetGlobalInboxIncRef(); + } + + if (parentNode) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_Folder); + VaultFolderNode access(templateNode); + access.SetFolderType(plVault::kDeviceInboxFolder); + access.SetFolderName(devInbox->inboxName); + result = parentNode->GetChildNodeIncRef(templateNode, 1); + templateNode->DecRef(); + parentNode->DecRef(); + } + } + return result; +} + +//============================================================================ +void VaultClearDeviceInboxMap () { + while (DeviceInbox * inbox = s_ageDeviceInboxes.Head()) { + DEL(inbox); + } +} + +//============================================================================ +bool VaultAgeGetAgeSDL (plStateDataRecord * out) { + bool result = false; + if (RelVaultNode * rvn = VaultGetAgeInfoNodeIncRef()) { + if (RelVaultNode * rvnSdl = rvn->GetChildNodeIncRef(plVault::kNodeType_SDL, 1)) { + VaultSDLNode sdl(rvnSdl); + result = sdl.GetStateDataRecord(out, plSDL::kKeepDirty); + if (!result) { + sdl.InitStateDataRecord(sdl.sdlName); + result = sdl.GetStateDataRecord(out, plSDL::kKeepDirty); + } + rvnSdl->DecRef(); + } + rvn->DecRef(); + } + return result; +} + +//============================================================================ +void VaultAgeUpdateAgeSDL (const plStateDataRecord * rec) { + if (RelVaultNode * rvn = VaultGetAgeInfoNodeIncRef()) { + if (RelVaultNode * rvnSdl = rvn->GetChildNodeIncRef(plVault::kNodeType_SDL, 1)) { + VaultSDLNode sdl(rvnSdl); + sdl.SetStateDataRecord(rec, plSDL::kDirtyOnly | plSDL::kTimeStampOnRead); + rvnSdl->DecRef(); + } + rvn->DecRef(); + } +} + +//============================================================================ +unsigned VaultAgeGetAgeTime () { + hsAssert(false, "eric, implement me"); + return 0; +} + +//============================================================================ +RelVaultNode * VaultGetSubAgeLinkIncRef (const plAgeInfoStruct * info) { + + RelVaultNode * rvnLink = nil; + + if (RelVaultNode * rvnFldr = VaultGetAgeSubAgesFolderIncRef()) { + + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + + VaultAgeInfoNode ageInfo(templateNode); + wchar str[MAX_PATH]; + StrToUnicode(str, info->GetAgeFilename(), arrsize(str)); + ageInfo.SetAgeFilename(str); + + if (RelVaultNode * rvnInfo = rvnFldr->GetChildNodeIncRef(templateNode, 2)) { + templateNode->fieldFlags = 0; + templateNode->SetNodeType(plVault::kNodeType_AgeLink); + rvnLink = rvnInfo->GetParentNodeIncRef(templateNode, 1); + rvnInfo->DecRef(); + } + + templateNode->DecRef(); + rvnFldr->DecRef(); + } + + return rvnLink; +} + +//============================================================================ +bool VaultAgeGetSubAgeLink (const plAgeInfoStruct * info, plAgeLinkStruct * link) { + bool result = false; + if (RelVaultNode * rvnLink = VaultGetSubAgeLinkIncRef(info)) { + if (RelVaultNode * rvnInfo = rvnLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + VaultAgeInfoNode ageInfo(rvnInfo); + ageInfo.CopyTo(link->GetAgeInfo()); + rvnInfo->DecRef(); + result = true; + } + + rvnLink->DecRef(); + } + + return result; +} + +//============================================================================ +namespace _VaultCreateSubAgeAndWait { + +struct _InitAgeParam { + ENetError result; + bool complete; + unsigned ageInfoId; +}; +static void _InitAgeCallback ( + ENetError result, + void * , + void * vparam, + unsigned ageVaultId, + unsigned ageInfoVaultId +) { + _InitAgeParam * param = (_InitAgeParam *)vparam; + param->ageInfoId = ageInfoVaultId; + param->result = result; + param->complete = true; +} +struct _FetchVaultParam { + ENetError result; + bool complete; +}; +static void _FetchVaultCallback ( + ENetError result, + void * vparam +) { + _FetchVaultParam * param = (_FetchVaultParam *)vparam; + param->result = result; + param->complete = true; +} +struct _CreateNodeParam { + ENetError result; + bool complete; + unsigned nodeId; +}; +static void _CreateNodeCallback ( + ENetError result, + void * , + void * vparam, + RelVaultNode * node +) { + _CreateNodeParam * param = (_CreateNodeParam *)vparam; + if (IS_NET_SUCCESS(result)) + param->nodeId = node->nodeId; + param->result = result; + param->complete = true; +} +struct _AddChildNodeParam { + ENetError result; + bool complete; +}; +static void _AddChildNodeCallback ( + ENetError result, + void * vparam +) { + _AddChildNodeParam * param = (_AddChildNodeParam *)vparam; + param->result = result; + param->complete = true; +} + +} // namespace _VaultCreateSubAgeAndWait + +//============================================================================ +bool VaultAgeFindOrCreateSubAgeLinkAndWait ( + const plAgeInfoStruct * info, + plAgeLinkStruct * link, + const Uuid & parentAgeInstId +) { + if (RelVaultNode * rvnLink = VaultFindAgeSubAgeLinkIncRef(info)) { + VaultAgeLinkNode linkAcc(rvnLink); + linkAcc.CopyTo(link); + if (RelVaultNode * rvnInfo = rvnLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + VaultAgeInfoNode infoAcc(rvnInfo); + infoAcc.CopyTo(link->GetAgeInfo()); + rvnInfo->DecRef(); + rvnLink->DecRef(); + return true; + } + } + + using namespace _VaultCreateSubAgeAndWait; + + unsigned subAgesId; + unsigned ageInfoId; + unsigned ageLinkId; + + if (RelVaultNode * rvnSubAges = VaultGetAgeSubAgesFolderIncRef()) { + subAgesId = rvnSubAges->nodeId; + rvnSubAges->DecRef(); + } + else { + LogMsg(kLogError, L"CreateSubAge: Failed to get ages's SubAges folder"); + return false; + } + + { // Init age vault + _InitAgeParam param; + ZERO(param); + + VaultInitAge( + info, + parentAgeInstId, + _InitAgeCallback, + nil, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"CreateSubAge: Failed to init age %S", link->GetAgeInfo()->GetAgeFilename()); + return false; + } + + ageInfoId = param.ageInfoId; + } + + { // Create age link + _CreateNodeParam param; + ZERO(param); + + VaultCreateNode( + plVault::kNodeType_AgeLink, + _CreateNodeCallback, + nil, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"CreateSubAge: Failed create age link node"); + return false; + } + + ageLinkId = param.nodeId; + } + + { // Fetch age info node tree + _FetchVaultParam param; + ZERO(param); + + VaultDownload( + L"CreateSubAge", + ageInfoId, + _FetchVaultCallback, + ¶m, + nil, + nil + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"CreateSubAge: Failed to download age info vault"); + return false; + } + } + + { // Link: + // ageLink to ages's subages folder + // ageInfo to ageLink + _AddChildNodeParam param1; + _AddChildNodeParam param2; + ZERO(param1); + ZERO(param2); + + VaultAddChildNode( + subAgesId, + ageLinkId, + 0, + _AddChildNodeCallback, + ¶m1 + ); + + VaultAddChildNode( + ageLinkId, + ageInfoId, + 0, + _AddChildNodeCallback, + ¶m2 + ); + + while (!param1.complete && !param2.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param1.result)) { + LogMsg(kLogError, L"CreateSubAge: Failed to add link to ages's subages"); + return false; + } + if (IS_NET_ERROR(param2.result)) { + LogMsg(kLogError, L"CreateSubAge: Failed to add info to link"); + return false; + } + } + + if (RelVaultNode * rvnLink = VaultGetNodeIncRef(ageLinkId)) { + VaultAgeLinkNode linkAcc(rvnLink); + linkAcc.CopyTo(link); + rvnLink->DecRef(); + } + + if (RelVaultNode * rvnInfo = VaultGetNodeIncRef(ageInfoId)) { + VaultAgeInfoNode infoAcc(rvnInfo); + infoAcc.CopyTo(link->GetAgeInfo()); + rvnInfo->DecRef(); + } + + return true; +} + + +//============================================================================ +namespace _VaultCreateChildAgeAndWait { + +struct _InitAgeParam { + ENetError result; + bool complete; + unsigned ageInfoId; +}; +static void _InitAgeCallback ( + ENetError result, + void * , + void * vparam, + unsigned ageVaultId, + unsigned ageInfoVaultId +) { + _InitAgeParam * param = (_InitAgeParam *)vparam; + param->ageInfoId = ageInfoVaultId; + param->result = result; + param->complete = true; +} +struct _FetchVaultParam { + ENetError result; + bool complete; +}; +static void _FetchVaultCallback ( + ENetError result, + void * vparam +) { + _FetchVaultParam * param = (_FetchVaultParam *)vparam; + param->result = result; + param->complete = true; +} +struct _CreateNodeParam { + ENetError result; + bool complete; + unsigned nodeId; +}; +static void _CreateNodeCallback ( + ENetError result, + void * , + void * vparam, + RelVaultNode * node +) { + _CreateNodeParam * param = (_CreateNodeParam *)vparam; + if (IS_NET_SUCCESS(result)) + param->nodeId = node->nodeId; + param->result = result; + param->complete = true; +} +struct _AddChildNodeParam { + ENetError result; + bool complete; +}; +static void _AddChildNodeCallback ( + ENetError result, + void * vparam +) { + _AddChildNodeParam * param = (_AddChildNodeParam *)vparam; + param->result = result; + param->complete = true; +} + +} // namespace _VaultCreateChildAgeAndWait + +//============================================================================ +bool VaultAgeFindOrCreateChildAgeLinkAndWait ( + const wchar parentAgeName[], + const plAgeInfoStruct * info, + plAgeLinkStruct * link +) { + using namespace _VaultCreateChildAgeAndWait; + + unsigned childAgesId; + unsigned ageInfoId; + unsigned ageLinkId; + + { // Get id of child ages folder + RelVaultNode * rvnAgeInfo = nil; + if (parentAgeName) { + char ansi[MAX_PATH]; + StrToAnsi(ansi, parentAgeName, arrsize(ansi)); + plAgeInfoStruct pinfo; + pinfo.SetAgeFilename(ansi); + if (RelVaultNode * rvnAgeLink = VaultGetOwnedAgeLinkIncRef(&pinfo)) { + rvnAgeInfo = rvnAgeLink->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1); + rvnAgeLink->DecRef(); + } + } + else { + rvnAgeInfo = VaultGetAgeInfoNodeIncRef(); + } + + if (!rvnAgeInfo) { + LogMsg(kLogError, L"CreateChildAge: Failed to get ages's AgeInfo node"); + return false; + } + + RelVaultNode * rvnChildAges; + if (nil != (rvnChildAges = rvnAgeInfo->GetChildAgeInfoListNodeIncRef(plVault::kChildAgesFolder, 1))) { + childAgesId = rvnChildAges->nodeId; + } + else { + rvnAgeInfo->DecRef(); + LogMsg(kLogError, L"CreateChildAge: Failed to get ages's ChildAges folder"); + return false; + } + rvnAgeInfo->DecRef(); + + // Check for existing child age in folder + RelVaultNode * rvnLink = nil; + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_AgeInfo); + + VaultAgeInfoNode ageInfo(templateNode); + wchar str[MAX_PATH]; + StrToUnicode(str, info->GetAgeFilename(), arrsize(str)); + ageInfo.SetAgeFilename(str); + + if (RelVaultNode * rvnInfo = rvnChildAges->GetChildNodeIncRef(templateNode, 2)) { + templateNode->fieldFlags = 0; + templateNode->SetNodeType(plVault::kNodeType_AgeLink); + rvnLink = rvnInfo->GetParentNodeIncRef(templateNode, 1); + rvnInfo->DecRef(); + } + + templateNode->DecRef(); + rvnChildAges->DecRef(); + + if (rvnLink) { + VaultAgeLinkNode access(rvnLink); + access.CopyTo(link); + rvnLink->DecRef(); + return true; + } + } + + { // Init age vault + _InitAgeParam param; + ZERO(param); + + Uuid parentAgeInstId; + ZERO(parentAgeInstId); + if (RelVaultNode * rvnAge = VaultGetAgeNodeIncRef()) { + VaultAgeNode access(rvnAge); + parentAgeInstId = access.ageInstUuid; + rvnAge->DecRef(); + } + + VaultInitAge( + info, + parentAgeInstId, + _InitAgeCallback, + nil, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"CreateChildAge: Failed to init age %S", link->GetAgeInfo()->GetAgeFilename()); + return false; + } + + ageInfoId = param.ageInfoId; + } + + { // Create age link + _CreateNodeParam param; + ZERO(param); + + VaultCreateNode( + plVault::kNodeType_AgeLink, + _CreateNodeCallback, + nil, + ¶m + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"CreateChildAge: Failed create age link node"); + return false; + } + + ageLinkId = param.nodeId; + } + + { // Fetch age info node tree + _FetchVaultParam param; + ZERO(param); + + VaultDownload( + L"CreateChildAge", + ageInfoId, + _FetchVaultCallback, + ¶m, + nil, + nil + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param.result)) { + LogMsg(kLogError, L"CreateChildAge: Failed to download age info vault"); + return false; + } + } + + { // Link: + // ageLink to ages's subages folder + // ageInfo to ageLink + _AddChildNodeParam param1; + _AddChildNodeParam param2; + ZERO(param1); + ZERO(param2); + + VaultAddChildNode( + childAgesId, + ageLinkId, + 0, + _AddChildNodeCallback, + ¶m1 + ); + + VaultAddChildNode( + ageLinkId, + ageInfoId, + 0, + _AddChildNodeCallback, + ¶m2 + ); + + while (!param1.complete && !param2.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } + + if (IS_NET_ERROR(param1.result)) { + LogMsg(kLogError, L"CreateChildAge: Failed to add link to ages's subages"); + return false; + } + if (IS_NET_ERROR(param2.result)) { + LogMsg(kLogError, L"CreateChildAge: Failed to add info to link"); + return false; + } + } + + if (RelVaultNode * rvnLink = VaultGetNodeIncRef(ageLinkId)) { + VaultAgeLinkNode linkAcc(rvnLink); + linkAcc.CopyTo(link); + rvnLink->DecRef(); + } + + if (RelVaultNode * rvnInfo = VaultGetNodeIncRef(ageInfoId)) { + VaultAgeInfoNode infoAcc(rvnInfo); + infoAcc.CopyTo(link->GetAgeInfo()); + rvnInfo->DecRef(); + } + + return true; +} + + +/***************************************************************************** +* +* CCR Vault Access +* +***/ + +//============================================================================ +void VaultCCRDumpPlayers() { + hsAssert(false, "eric, implement me"); +} + + +/***************************************************************************** +* +* Exports - Vault download +* +***/ + +//============================================================================ +void VaultDownload ( + const wchar tag[], + unsigned vaultId, + FVaultDownloadCallback callback, + void * cbParam, + FVaultProgressCallback progressCallback, + void * cbProgressParam +) { + VaultDownloadTrans * trans = NEWZERO(VaultDownloadTrans); + StrCopy(trans->tag, tag, arrsize(trans->tag)); + trans->callback = callback; + trans->cbParam = cbParam; + trans->progressCallback = progressCallback; + trans->cbProgressParam = cbProgressParam; + trans->vaultId = vaultId; + + NetCliAuthVaultFetchNodeRefs( + vaultId, + VaultDownloadTrans::VaultNodeRefsFetched, + trans + ); +} + +//============================================================================ +struct _DownloadVaultParam { + ENetError result; + bool complete; +}; +static void _DownloadVaultCallback ( + ENetError result, + void * vparam +) { + _DownloadVaultParam * param = (_DownloadVaultParam *)vparam; + param->result = result; + param->complete = true; +} + +void VaultDownloadAndWait ( + const wchar tag[], + unsigned vaultId, + FVaultProgressCallback progressCallback, + void * cbProgressParam +) { + _DownloadVaultParam param; + ZERO(param); + + VaultDownload( + tag, + vaultId, + _DownloadVaultCallback, + ¶m, + progressCallback, + cbProgressParam + ); + + while (!param.complete) { + NetClientUpdate(); + plgDispatch::Dispatch()->MsgQueueProcess(); + AsyncSleep(10); + } +} + +//============================================================================ +void VaultCull (unsigned vaultId) { + // Remove the node from the global table + if (RelVaultNodeLink * link = s_nodes.Find(vaultId)) { + LogMsg(kLogDebug, L"Vault: Culling node %u", link->node->nodeId); + link->node->state->UnlinkFromRelatives(); + DEL(link); + } + + // Remove all orphaned nodes from the global table + for (RelVaultNodeLink * next, * link = s_nodes.Head(); link; link = next) { + next = s_nodes.Next(link); + + if (link->node->nodeType > plVault::kNodeType_VNodeMgrLow && link->node->nodeType < plVault::kNodeType_VNodeMgrHigh) + continue; + + ARRAY(unsigned) nodeIds; + link->node->GetRootIds(&nodeIds); + bool foundRoot = false; + for (unsigned i = 0; i < nodeIds.Count(); ++i) { + RelVaultNodeLink * root = s_nodes.Find(nodeIds[i]); + if (root && root->node->nodeType > plVault::kNodeType_VNodeMgrLow && root->node->nodeType < plVault::kNodeType_VNodeMgrHigh) { + foundRoot = true; + break; + } + } + if (!foundRoot) { + LogMsg(kLogDebug, L"Vault: Culling node %u", link->node->nodeId); + link->node->state->UnlinkFromRelatives(); + DEL(link); + } + } +} + +/***************************************************************************** +* +* Exports - Vault global node handling +* +***/ + +//============================================================================ +RelVaultNode * VaultGetSystemNodeIncRef () { + RelVaultNode * result = nil; + if (RelVaultNode * player = VaultGetPlayerNodeIncRef()) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_System); + if (RelVaultNode * systemNode = player->GetChildNodeIncRef(templateNode, 1)) + result = systemNode; + templateNode->DecRef(); + player->DecRef(); + } + return result; +} + +//============================================================================ +RelVaultNode * VaultGetGlobalInboxIncRef () { + RelVaultNode * result = nil; + if (RelVaultNode * system = VaultGetSystemNodeIncRef()) { + NetVaultNode * templateNode = NEWZERO(NetVaultNode); + templateNode->IncRef(); + templateNode->SetNodeType(plVault::kNodeType_Folder); + VaultFolderNode folder(templateNode); + folder.SetFolderType(plVault::kGlobalInboxFolder); + if (RelVaultNode * inbox = system->GetChildNodeIncRef(templateNode, 1)) + result = inbox; + templateNode->DecRef(); + system->DecRef(); + } + return result; +} + +#endif // def CLIENT diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultClientApi.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultClientApi.h new file mode 100644 index 00000000..1e79835f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultClientApi.h @@ -0,0 +1,478 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultClientApi.h +* +***/ + +#ifdef CLIENT + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_PLVAULTCLIENTAPI_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultClientApi.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_PLVAULTCLIENTAPI_H + + +/***************************************************************************** +* +* VaultCallback +* +***/ + +struct RelVaultNode; + +struct VaultCallback { + struct IVaultCallback * internal; + + virtual void AddedChildNode ( + RelVaultNode * parent, + RelVaultNode * child + ) = 0; + + virtual void RemovingChildNode ( + RelVaultNode * parent, + RelVaultNode * child + ) = 0; + + virtual void ChangedNode ( + RelVaultNode * changedNode + ) = 0; +}; + +void VaultRegisterCallback (VaultCallback * cb); +void VaultUnregisterCallback (VaultCallback * cb); + + +/***************************************************************************** +* +* RelVaultNode +* A "relational" vault node (contains child and parent node linkage) +* +* NOTE: This API is not thread-safe and therefore not suitable for use +* in server applications. +* +***/ +struct RelVaultNode : NetVaultNode { + struct IRelVaultNode * state; + + RelVaultNode (); + ~RelVaultNode (); + + bool IsParentOf (unsigned nodeId, unsigned maxDepth); + bool IsChildOf (unsigned nodeId, unsigned maxDepth); + + void GetRootIds (ARRAY(unsigned) * nodeIds); + + unsigned RemoveChildNodes (unsigned maxDepth); // returns # of nodes removed + + void GetChildNodeIds ( + ARRAY(unsigned) * nodeIds, + unsigned maxDepth + ); + void GetParentNodeIds ( + ARRAY(unsigned) * nodeIds, + unsigned maxDepth + ); + + void GetMatchingChildNodeIds ( + NetVaultNode * templateNode, + ARRAY(unsigned) * nodeIds, + unsigned maxDepth + ); + void GetMatchingParentNodeIds ( + NetVaultNode * templateNode, + ARRAY(unsigned) * nodeIds, + unsigned maxDepth + ); + + // returns first matching node found + RelVaultNode * GetParentNodeIncRef ( + NetVaultNode * templateNode, + unsigned maxDepth + ); + RelVaultNode * GetChildNodeIncRef ( + NetVaultNode * templateNode, + unsigned maxDepth + ); + RelVaultNode * GetChildNodeIncRef ( + unsigned nodeType, + unsigned maxDepth + ); + RelVaultNode * GetChildFolderNodeIncRef ( + unsigned folderType, + unsigned maxDepth + ); + RelVaultNode * GetChildPlayerInfoListNodeIncRef ( + unsigned folderType, + unsigned maxDepth + ); + RelVaultNode * GetChildAgeInfoListNodeIncRef ( + unsigned folderType, + unsigned maxDepth + ); + + // returns all matching nodes found + void GetChildNodesIncRef ( + unsigned maxDepth, + ARRAY(RelVaultNode*) * nodes + ); + void GetChildNodesIncRef ( + NetVaultNode * templateNode, + unsigned maxDepth, + ARRAY(RelVaultNode*) * nodes + ); + void GetChildNodesIncRef ( + unsigned nodeType, + unsigned maxDepth, + ARRAY(RelVaultNode*) * nodes + ); + void GetChildFolderNodesIncRef ( + unsigned folderType, + unsigned maxDepth, + ARRAY(RelVaultNode*) * nodes + ); + + unsigned GetRefOwnerId (unsigned parentId); + + bool BeenSeen (unsigned parentId) const; + void SetSeen (unsigned parentId, bool seen); + + // logging + void Print (const wchar tag[], FStateDump dumpProc, unsigned level); + void PrintTree (FStateDump dumpProc, unsigned level); + + // AgeInfoNode-specific (and it checks!) + RelVaultNode * GetParentAgeLinkIncRef (); +}; + + +/***************************************************************************** +* +* Vault Initialize +* +***/ + +void VaultInitialize (); +void VaultDestroy (); +void VaultUpdate (); + + +/***************************************************************************** +* +* Generic vault access +* +***/ + +RelVaultNode * VaultGetNodeIncRef ( + unsigned nodeId +); +RelVaultNode * VaultGetNodeIncRef ( + NetVaultNode * templateNode +); +// VaultAddChildNode will download the child node if necessary +// the parent exists locally before making the callback. +typedef void (*FVaultAddChildNodeCallback)( + ENetError result, + void * param +); +void VaultAddChildNode ( + unsigned parentId, + unsigned childId, + unsigned ownerId, + FVaultAddChildNodeCallback callback, // optional + void * param // optional +); +void VaultAddChildNodeAndWait ( + unsigned parentId, + unsigned childId, + unsigned ownerId +); +typedef void (*FVaultRemoveChildNodeCallback)( + ENetError result, + void * param +); +void VaultRemoveChildNode ( + unsigned parentId, + unsigned childId, + FVaultRemoveChildNodeCallback callback, + void * param +); +void VaultSetNodeSeen ( + unsigned nodeId, + bool seen +); +void VaultDeleteNode ( + unsigned nodeId +); +void VaultPublishNode ( + unsigned nodeId, + const wchar deviceName[] +); +void VaultSendNode ( + RelVaultNode* srcNode, + unsigned dstPlayerId +); + +typedef void (*FVaultCreateNodeCallback)( + ENetError result, + void * state, + void * param, + RelVaultNode * node +); +void VaultCreateNode ( // non-blocking + plVault::NodeTypes nodeType, + FVaultCreateNodeCallback callback, + void * state, + void * param +); +void VaultCreateNode ( // non-blocking + NetVaultNode * templateNode, + FVaultCreateNodeCallback callback, + void * state, + void * param +); +RelVaultNode * VaultCreateNodeAndWaitIncRef ( // block until completion. returns node. nil --> failure + plVault::NodeTypes nodeType, + ENetError * result +); +RelVaultNode * VaultCreateNodeAndWaitIncRef ( // block until completion. returns node. nil --> failure + NetVaultNode * templateNode, + ENetError * result +); +void VaultForceSaveNodeAndWait ( + NetVaultNode * node +); + +typedef void (*FVaultFindNodeCallback)( + ENetError result, + void * param, + unsigned nodeIdCount, + const unsigned nodeIds[] +); +void VaultFindNodes ( + NetVaultNode * templateNode, + FVaultFindNodeCallback callback, + void * param +); +void VaultFindNodesAndWait ( + NetVaultNode * templateNode, + ARRAY(unsigned) * nodeIds +); +void VaultLocalFindNodes ( + NetVaultNode * templateNode, + ARRAY(unsigned) * nodeIds +); +void VaultFetchNodesAndWait ( // Use VaultGetNodeIncRef to access the fetched nodes + const unsigned nodeIds[], + unsigned count, + bool force = false +); +typedef void (*FVaultInitAgeCallback)( + ENetError result, + void * state, + void * param, + unsigned ageVaultId, + unsigned ageInfoVaultId +); +void VaultInitAge ( + const class plAgeInfoStruct * info, + const Uuid & parentAgeInstId, + FVaultInitAgeCallback callback, + void * state, + void * param +); + + +/***************************************************************************** +* +* Player Vault Access +* +***/ + +unsigned VaultGetPlayerId (); +RelVaultNode * VaultGetPlayerNodeIncRef (); +RelVaultNode * VaultGetPlayerInfoNodeIncRef (); +RelVaultNode * VaultGetAvatarOutfitFolderIncRef (); +RelVaultNode * VaultGetAvatarClosetFolderIncRef (); +bool VaultGetLinkToMyNeighborhood (plAgeLinkStruct * link); +bool VaultGetLinkToMyPersonalAge (plAgeLinkStruct * link); +bool VaultGetLinkToCity (plAgeLinkStruct * link); +RelVaultNode * VaultGetAgesIOwnFolderIncRef (); +RelVaultNode * VaultGetAgesICanVisitFolderIncRef (); +RelVaultNode * VaultGetPlayerInboxFolderIncRef (); +RelVaultNode * VaultGetOwnedAgeLinkIncRef (const plAgeInfoStruct * info); +RelVaultNode * VaultGetOwnedAgeInfoIncRef (const plAgeInfoStruct * info); +bool VaultGetOwnedAgeLink (const plAgeInfoStruct * info, plAgeLinkStruct * link); +bool VaultAddOwnedAgeSpawnPoint (const Uuid & ageInstId, const plSpawnPointInfo & spawnPt); +bool VaultSetOwnedAgePublicAndWait (const plAgeInfoStruct * info, bool publicOrNot); +RelVaultNode * VaultGetVisitAgeLinkIncRef (const plAgeInfoStruct * info); +bool VaultGetVisitAgeLink (const plAgeInfoStruct * info, class plAgeLinkStruct * link); +bool VaultRegisterOwnedAgeAndWait (const plAgeLinkStruct * link); +bool VaultRegisterVisitAgeAndWait (const plAgeLinkStruct * link); +bool VaultUnregisterOwnedAgeAndWait (const plAgeInfoStruct * info); +bool VaultUnregisterVisitAgeAndWait (const plAgeInfoStruct * info); +RelVaultNode * VaultFindChronicleEntryIncRef (const wchar entryName[], int entryType = -1); +bool VaultHasChronicleEntry (const wchar entryName[], int entryType = -1); +// if entry of same name and type already exists, value is updated +void VaultAddChronicleEntryAndWait ( + const wchar entryName[], + int entryType, + const wchar entryValue[] +); +bool VaultAmIgnoringPlayer (unsigned playerId); +unsigned VaultGetKILevel (); +bool VaultGetCCRStatus (); // true=online, false=away +bool VaultSetCCRStatus (bool online); // true=online, false=away +void VaultDump (const wchar tag[], unsigned vaultId, FStateDump dumpProc); +void VaultDump (const wchar tag[], unsigned vaultId); + +bool VaultAmInMyPersonalAge (); +bool VaultAmInMyNeighborhoodAge (); +bool VaultAmOwnerOfCurrentAge (); +bool VaultAmCzarOfCurrentAge (); +bool VaultAmOwnerOfAge (const Uuid & ageInstId); +bool VaultAmCzarOfAge (const Uuid & ageInstId); +bool VaultRegisterMTStationAndWait ( + const wchar stationName[], + const wchar linkBackSpawnPtObjName[] +); +void VaultProcessPlayerInbox (); + + +/***************************************************************************** +* +* Age Vault Access +* +***/ + +#define DEFAULT_DEVICE_INBOX L"DevInbox" + +RelVaultNode * VaultGetAgeNodeIncRef (); +RelVaultNode * VaultGetAgeInfoNodeIncRef (); +RelVaultNode * VaultGetAgeChronicleFolderIncRef (); +RelVaultNode * VaultGetAgeDevicesFolderIncRef (); +RelVaultNode * VaultGetAgeSubAgesFolderIncRef (); +RelVaultNode * VaultGetAgeChildAgesFolderIncRef (); +RelVaultNode * VaultGetAgeAgeOwnersFolderIncRef (); +RelVaultNode * VaultGetAgeCanVisitFolderIncRef (); +RelVaultNode * VaultGetAgePeopleIKnowAboutFolderIncRef (); +RelVaultNode * VaultGetAgePublicAgesFolderIncRef (); +RelVaultNode * VaultAgeGetBookshelfFolderIncRef (); +RelVaultNode * VaultFindAgeSubAgeLinkIncRef (const plAgeInfoStruct * info); +RelVaultNode * VaultFindAgeChildAgeLinkIncRef (const plAgeInfoStruct * info); +RelVaultNode * VaultFindAgeChronicleEntryIncRef (const wchar entryName[], int entryType = -1); +// if entry of same name and type already exists, value is updated +void VaultAddAgeChronicleEntry ( + const wchar entryName[], + int entryType, + const wchar entryValue[] +); +RelVaultNode * VaultAgeAddDeviceAndWaitIncRef (const wchar deviceName[]); // blocks until completion +void VaultAgeRemoveDevice (const wchar deviceName[]); +bool VaultAgeHasDevice (const wchar deviceName[]); +RelVaultNode * VaultAgeGetDeviceIncRef (const wchar deviceName[]); +RelVaultNode * VaultAgeSetDeviceInboxAndWaitIncRef (const wchar deviceName[], const wchar inboxName[]); // blocks until completion +RelVaultNode * VaultAgeGetDeviceInboxIncRef (const wchar deviceName[]); +void VaultClearDeviceInboxMap (); + +bool VaultAgeGetAgeSDL (class plStateDataRecord * out); +void VaultAgeUpdateAgeSDL (const class plStateDataRecord * rec); + +unsigned VaultAgeGetAgeTime (); + +bool VaultAgeGetSubAgeLink ( + const plAgeInfoStruct * info, + plAgeLinkStruct * link +); +bool VaultAgeFindOrCreateSubAgeLinkAndWait ( + const plAgeInfoStruct * info, + plAgeLinkStruct * link, + const Uuid & parentAgeInstId +); +bool VaultAgeFindOrCreateChildAgeLinkAndWait ( + const wchar parentAgeName[], // nil --> current age, non-nil --> owned age by given name + const plAgeInfoStruct * info, + plAgeLinkStruct * link +); + + + +/***************************************************************************** +* +* CCR Vault Access +* +***/ + +void VaultCCRDumpPlayers(); + + + +/***************************************************************************** +* +* Vault download +* +***/ + +typedef void (*FVaultDownloadCallback)( + ENetError result, + void * param +); +typedef void (*FVaultProgressCallback)( + unsigned total, + unsigned curr, + void * param +); + +void VaultDownload ( + const wchar tag[], + unsigned vaultId, + FVaultDownloadCallback callback, + void * cbParam, + FVaultProgressCallback progressCallback, + void * cbProgressParam +); +void VaultDownloadAndWait ( + const wchar tag[], + unsigned vaultId, + FVaultProgressCallback progressCallback, + void * cbProgressParam +); + +void VaultCull ( + unsigned vaultId +); + +/***************************************************************************** +* +* Vault global node handling +* +***/ + +RelVaultNode * VaultGetSystemNodeIncRef (); +RelVaultNode * VaultGetGlobalInboxIncRef (); + +#endif // def CLIENT diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultConstants.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultConstants.cpp new file mode 100644 index 00000000..43a35438 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultConstants.cpp @@ -0,0 +1,126 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultConstants.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + +namespace plVault { +#ifdef CLIENT + +const char * NodeTypeStr( int type, bool pretty ) +{ + if (!pretty) + { + switch ( type ) + { + case kNodeType_VNodeMgrPlayer: return "PLR"; + case kNodeType_VNodeMgrAge: return "AGE"; + case kNodeType_Folder: return "FLDR"; + case kNodeType_PlayerInfo: return "PLRINFO"; + case kNodeType_System: return "SYSTEM"; + case kNodeType_Image: return "IMG"; + case kNodeType_TextNote: return "TXT"; + case kNodeType_SDL: return "SDL"; + case kNodeType_AgeLink: return "LINK"; + case kNodeType_Chronicle: return "CRN"; + case kNodeType_PlayerInfoList: return "PLRINFOLIST"; + case kNodeType_AgeInfo: return "AGEINFO"; + case kNodeType_AgeInfoList: return "AGEINFOLIST"; + case kNodeType_MarkerGame: return "MRKRGAME"; + default: return "???"; + } + } + else + { + switch ( type ) + { + case kNodeType_VNodeMgrPlayer: return "Player"; + case kNodeType_VNodeMgrAge: return "Age"; + case kNodeType_Folder: return "Folder"; + case kNodeType_PlayerInfo: return "Player Info"; + case kNodeType_System: return "System"; + case kNodeType_Image: return "Image"; + case kNodeType_TextNote: return "Text Note"; + case kNodeType_SDL: return "SDL"; + case kNodeType_AgeLink: return "Age Link"; + case kNodeType_Chronicle: return "Chronicle"; + case kNodeType_PlayerInfoList: return "Player Info List"; + case kNodeType_AgeInfo: return "Age Info"; + case kNodeType_AgeInfoList: return "Age Info List"; + case kNodeType_MarkerGame: return "Marker Game"; + default: return "UNKNOWN"; + } + } +} + +const char * StandardNodeStr( int type ) +{ + switch ( type ) + { + case kUserDefinedNode: return "Generic"; + case kInboxFolder: return "InboxFolder"; + case kBuddyListFolder: return "BuddyListFolder"; + case kPeopleIKnowAboutFolder: return"PeopleIKnowAboutFolder"; + case kIgnoreListFolder: return "IgnoreListFolder"; + case kVaultMgrGlobalDataFolder: return "VaultMgrGlobalDataFolder"; + case kChronicleFolder: return "ChronicleFolder"; + case kAvatarOutfitFolder: return "AvatarOutfitFolder"; + case kAgeTypeJournalFolder: return "AgeTypeJournalFolder"; + case kSubAgesFolder: return "SubAgesFolder"; + case kDeviceInboxFolder: return "DeviceInboxFolder"; + case kAgeInstanceSDLNode: return "AgeInstanceSDLNode"; + case kAgeGlobalSDLNode: return "AgeGlobalSDLNode"; + case kHoodMembersFolder: return "HoodMembersFolder"; + case kAllPlayersFolder: return "AllPlayers"; + case kAgeMembersFolder: return "AgeMembersFolder"; + case kAgeJournalsFolder: return "AgeJournalsFolder"; + case kAgeDevicesFolder: return "AgeDevicesFolder"; + case kAllAgeGlobalSDLNodesFolder: return "AllAgeGlobalSDLNodesFolder"; + case kPlayerInfoNode: return "PlayerInfoNode"; + case kPublicAgesFolder: return "PublicAgesFolder"; + case kAgesIOwnFolder: return "AgesIOwnFolder"; + case kAgesICanVisitFolder: return "AgesICanVisitFolder"; + case kAvatarClosetFolder: return "AvatarClosetFolder"; + case kCanVisitFolder: return "CanVisitFolder"; + case kAgeOwnersFolder: return "AgeOwnersFolder"; + case kAgeInfoNode: return "AgeInfoNode"; + case kSystemNode: return "SystemNode"; + case kPlayerInviteFolder: return "PlayerInviteFolder"; + case kCCRPlayersFolder: return "CCRPlayersFolder"; + case kGlobalInboxFolder: return "GlobalInboxFolder"; + case kChildAgesFolder: return "ChildAgesFolder"; + case kGameScoresFolder: return "GameScoresFolder"; + default: return "UNKNOWN"; + } +} + +#endif // def CLIENT +} // namespace plVault diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultConstants.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultConstants.h new file mode 100644 index 00000000..0a49cb11 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultConstants.h @@ -0,0 +1,175 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultConstants.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_PLVAULTCONSTANTS_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultConstants.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_PLVAULTCONSTANTS_H + + +//////////////////////////////////////////////////////////////////// +// plVault: Plasma Network File System +// + +class plAgeInfoSource; + + +//////////////////////////////////////////////////////////////////// + +namespace plVault { + // ONLY APPEND TO THIS ENUM. + // Many of these values are stored in db. + // These are also commonly used as the Initialized Contexts in various node init tasks. + // These values may not change unless all clients, servers, and databases are simultaneously + // replaced; so forget it, basically. =) + enum StandardNodes { + // Just a node. + kUserDefinedNode = 0, + // Every vnode mgr has an inbox. + kInboxFolder, + // player buddies list. + kBuddyListFolder, + // player ignore list. + kIgnoreListFolder, + // people I know about folder. + kPeopleIKnowAboutFolder, + // vault manager place to store its stuff. + kVaultMgrGlobalDataFolder, + // player chronicle. + kChronicleFolder, + // player avatar outfit. + kAvatarOutfitFolder, + // player Age Type Journals. + kAgeTypeJournalFolder, + // age Sub Ages list. + kSubAgesFolder, + // an imager's inbox. + kDeviceInboxFolder, + // hood members folder. + kHoodMembersFolder, // DO NOT DELETE without MarkD's permission. + // all players folder. + kAllPlayersFolder, + // folder for keeping age members in + kAgeMembersFolder, + // List of player's KI Age journals. + kAgeJournalsFolder, + // an age's list of devices (imagers, whatever). + kAgeDevicesFolder, + // an sdl node that applies to one specific instance of an age. + kAgeInstanceSDLNode, + // an sdl node that applies to all ages by the same filename. + kAgeGlobalSDLNode, + // people who are invited to visit an age + kCanVisitFolder, + // people who are owners of an age + kAgeOwnersFolder, + // global folder for game servers + kAllAgeGlobalSDLNodesFolder, + // a player's player info node. used in init task as the init context for the node + kPlayerInfoNode, + // global folder: ages that are public + kPublicAgesFolder, + // ages a player owns (personal age bookshelf comes from here) + kAgesIOwnFolder, + // ages a player can visit + kAgesICanVisitFolder, + // the player's closet + kAvatarClosetFolder, + // an age's info node. used in age init task. + kAgeInfoNode, + // the system status node + kSystemNode, + // a players invite keyring + kPlayerInviteFolder, + // CCR players folder + kCCRPlayersFolder, + // Global Inbox folder + kGlobalInboxFolder, + // Child Age folder node list + kChildAgesFolder, + // Game Scores folder + kGameScoresFolder, + // THIS MUST BE THE LAST ENUM VALUE + kLastStandardNode, + }; + + // All possible node types. These values may not change unless all clients, + // servers, and databases are simultaneously replaced; so forget it, basically. =) + enum NodeTypes { + kNodeType_Invalid, + kNodeType_VNodeMgrLow, // low marker for range of VNodeMgr types + kNodeType_VNodeMgrPlayer, + kNodeType_VNodeMgrAge, + kNodeType_VNodeMgr_UNUSED00, + kNodeType_VNodeMgr_UNUSED01, + kNodeType_VNodeMgr_UNUSED02, + kNodeType_VNodeMgr_UNUSED03, + kNodeType_VNodeMgrHigh = kNodeType_VNodeMgrLow + 20, // high marker for range of VNodeMgr client types + kNodeType_Folder, + kNodeType_PlayerInfo, + kNodeType_System, + kNodeType_Image, + kNodeType_TextNote, + kNodeType_SDL, + kNodeType_AgeLink, + kNodeType_Chronicle, + kNodeType_PlayerInfoList, + kNodeType_UNUSED00, + kNodeType_UNUSED01, + kNodeType_AgeInfo, + kNodeType_AgeInfoList, + kNodeType_MarkerGame, + }; + + // All possible text note types + enum NoteTypes { + kNoteType_Generic, + kNoteType_CCRPetition, + kNoteType_Device, + kNoteType_Invite, + kNoteType_Visit, + kNoteType_UnVisit, + kNumNoteTypes + }; + enum NoteSubTypes { + kNoteSubType_Generic, + }; + +//============================================================================ + + +#ifdef CLIENT + + const char * NodeTypeStr( int type, bool pretty=false ); + const char * StandardNodeStr( int type ); + +#endif +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultCreatable.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultCreatable.h new file mode 100644 index 00000000..1697a116 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultCreatable.h @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plVaultCreatable_h_inc +#define plVaultCreatable_h_inc + +#include "../pnFactory/plCreator.h" + +#include "plDniCoordinateInfo.h" +REGISTER_CREATABLE( plDniCoordinateInfo ); + +#endif // plVaultCreatable_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNode.cpp new file mode 100644 index 00000000..e0af4852 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNode.cpp @@ -0,0 +1,2743 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#if 0 +#include "hsTemplates.h" +#include "hsStlUtils.h" +#include "plVaultNode.h" +#include "plVaultNodeIterator.h" +#include "../pnMessage/plMessage.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plNetCommon/plNetCommon.h" +#include "../plStatusLog/plStatusLog.h" +#include "../plSDL/plSDL.h" +#include +#include "hsGeometry3.h" +#include "hsStringTokenizer.h" + +//////////////////////////////////////////////////////////////////// + +#define plSafeGetLog() if (fMyNodeMgr) fMyNodeMgr->GetStatusLog() + + +//////////////////////////////////////////////////////////////////// + + +bool plVaultNode::MatchesNode::operator()( const plVaultNode * node ) const +{ + if ( !node ) + return false; + + bool matches = true; + + if ( fNode->IIsFieldFlagSet( plVaultNode::kID ) ) + { + matches &= ( fNode->GetID()==node->GetID() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kType ) ) + { + matches &= ( fNode->GetType()==node->GetType() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kPermissions ) ) + { + matches &= ( fNode->GetPermissions()==node->GetPermissions() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kOwnerNodeID ) ) + { + matches &= ( fNode->GetOwnerNodeID()==node->GetOwnerNodeID() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kGroupNodeID ) ) + { + matches &= ( fNode->GetGroupNodeID()==node->GetGroupNodeID() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kModifyTime ) ) + { + matches &= ( *fNode->GetModifyTime()==*node->GetModifyTime() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kCreatorNodeID ) ) + { + matches &= ( fNode->GetCreatorNodeID()==node->GetCreatorNodeID() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kCreateTime ) ) + { + matches &= ( *fNode->GetCreateTime()==*node->GetCreateTime() ); + } +// if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kCreateAgeCoords ) ) +// { +// hsAssert( false, "Compare age coords not supported" ); +// } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kCreateAgeTime ) ) + { + matches &= ( *fNode->GetCreateAgeTime()==*node->GetCreateAgeTime() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kCreateAgeName ) ) + { + matches &= ( strcmp( fNode->GetCreateAgeName(), node->GetCreateAgeName() )==0 ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kCreateAgeGuid ) ) + { + matches &= ( fNode->GetCreateAgeGuid()->IsEqualTo( node->GetCreateAgeGuid() ) ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kInt32_1 ) ) + { + matches &= ( fNode->IGetInt32_1()==node->IGetInt32_1() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kInt32_2 ) ) + { + matches &= ( fNode->IGetInt32_2()==node->IGetInt32_2() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kInt32_3 ) ) + { + matches &= ( fNode->IGetInt32_3()==node->IGetInt32_3() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kInt32_4 ) ) + { + matches &= ( fNode->IGetInt32_4()==node->IGetInt32_4() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kUInt32_1 ) ) + { + matches &= ( fNode->IGetUInt32_1()==node->IGetUInt32_1() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kUInt32_2 ) ) + { + matches &= ( fNode->IGetUInt32_2()==node->IGetUInt32_2() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kUInt32_3 ) ) + { + matches &= ( fNode->IGetUInt32_3()==node->IGetUInt32_3() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kUInt32_4 ) ) + { + matches &= ( fNode->IGetUInt32_4()==node->IGetUInt32_4() ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kString64_1 ) ) + { + matches &= ( strcmp( fNode->IGetString64_1(), node->IGetString64_1() )==0 ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kString64_2 ) ) + { + matches &= ( strcmp( fNode->IGetString64_2(), node->IGetString64_2() )==0 ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kString64_3 ) ) + { + matches &= ( strcmp( fNode->IGetString64_3(), node->IGetString64_3() )==0 ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kString64_4 ) ) + { + matches &= ( strcmp( fNode->IGetString64_4(), node->IGetString64_4() )==0 ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kString64_5 ) ) + { + matches &= ( strcmp( fNode->IGetString64_5(), node->IGetString64_5() )==0 ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kString64_6 ) ) + { + matches &= ( strcmp( fNode->IGetString64_6(), node->IGetString64_6() )==0 ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kIString64_1 ) ) + { + matches &= ( stricmp( fNode->IGetIString64_1(), node->IGetIString64_1() )==0 ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kIString64_2 ) ) + { + matches &= ( stricmp( fNode->IGetIString64_2(), node->IGetIString64_2() )==0 ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kText_1 ) ) + { + matches &= ( strcmp( fNode->IGetText_1(), node->IGetText_1() )==0 ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kText_2 ) ) + { + matches &= ( strcmp( fNode->IGetText_2(), node->IGetText_2() )==0 ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kBlob_1 ) ) + { +// hsAssert( false, "Compare blobs not supported" ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kBlob_2 ) ) + { +// hsAssert( false, "Compare blobs not supported" ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kBlob_1_Guid ) ) + { + matches &= ( fNode->IGetBlob_1_Guid()->IsEqualTo( node->IGetBlob_1_Guid() ) ); + } + if ( matches && fNode->IIsFieldFlagSet( plVaultNode::kBlob_2_Guid ) ) + { + matches &= ( fNode->IGetBlob_2_Guid()->IsEqualTo( node->IGetBlob_2_Guid() ) ); + } + + return matches; +} + +bool plVaultNode::MatchesNode::operator()( const plVaultNodeRef * nodeRef ) const +{ + return operator()( nodeRef->GetChild() ); +} + +plVaultNode::plVaultNode() +: fID( 0 ) +, fType( 0 ) +, fPermissions( 0 ) +, fOwnerNodeID( 0 ) +, fGroupNodeID( 0 ) +, fCreatorNodeID( 0 ) +, fInt32_1( 0 ) +, fInt32_2( 0 ) +, fInt32_3( 0 ) +, fInt32_4( 0 ) +, fUInt32_1( 0 ) +, fUInt32_2( 0 ) +, fUInt32_3( 0 ) +, fUInt32_4( 0 ) +, fOwnerNode( nil ) +, fGroupNode( nil ) +, fCreatorNode( nil ) +, fPersistent( true ) +, fSaving( false ) +, fInitialized( false ) +, fUserData( 0 ) +, fMyNodeMgr( nil ) +{ +} + +plVaultNode::~plVaultNode() +{ + IFailPendingSaves(); + std::for_each( fChildNodes.begin(), fChildNodes.end(), xtl::delete_ptr() ); + fChildNodes.clear(); +} + +bool plVaultNode::IIsFieldFlagSet( int b ) const +{ + return fFieldFlags.IsBitSet( b )?true:false; +} + +void plVaultNode::IGotUpdated( void ) +{ + hsLogEntry( plSafeGetLog()->AddLineF( "Updated remotely: %s", AsStdString().c_str() ) ); +} + + +UInt32 plVaultNode::GetID( void ) const +{ + return fID; +} + +void plVaultNode::ISetID( UInt32 v ) +{ + ISetFieldFlag( kID ); + fID = v; +} + + +const plVaultPlayerInfoNode * plVaultNode::GetOwnerNode( void ) const +{ + if (!fMyNodeMgr) + return nil; + + plVaultPlayerInfoNode templateNode; + templateNode.SetPlayerID( fOwnerNodeID ); + plVaultNode * result; + fMyNodeMgr->FindNode( &templateNode, result ); + return plVaultPlayerInfoNode::ConvertNoRef( result ); +} + + +const plVaultPlayerInfoNode * plVaultNode::GetCreatorNode( void ) const +{ + if (!fMyNodeMgr) + return nil; + + plVaultPlayerInfoNode templateNode; + templateNode.SetPlayerID( fCreatorNodeID ); + plVaultNode * result; + fMyNodeMgr->FindNode( &templateNode, result ); + return plVaultPlayerInfoNode::ConvertNoRef( result ); +} + +const plVaultNode * plVaultNode::GetGroupNode( void ) const +{ + return nil; +} + + +bool plVaultNode::IHasFieldFlagsSet( void ) +{ + fFieldFlags.Compact(); + return fFieldFlags.GetSize()>0; +} + +bool plVaultNode::HasValidID( void ) const +{ + return ( fID>0 ) && ( !fPersistent || ( fPersistent && ( fID>=plVault::kFirstStoredNodeID ) ) ); +} + +bool plVaultNode::IsStored( void ) const +{ + if( !fMyNodeMgr ) + { + return fID>plVault::kFirstStoredNodeID; + } + else + { + return ( fMyNodeMgr->IAmOnline() && ( ( fID>plVault::kFirstStoredNodeID ) || fSaving ) ) + || ( !fMyNodeMgr->IAmOnline() ); // if not online then, sure... it's stored.. yeah. + } +} + +bool plVaultNode::IsMgrNode( void ) const +{ + return ( IsStored() && fType>plVault::kNodeType_VNodeMgrLow && fTypeAddLineF( "Starting pending save operation on node %lu", GetID() )); + task->Start(); + } +} + +void plVaultNode::IFailPendingSaves() +{ + while ( !fPendingSaves.empty() ) + { + plVaultSaveNodeTask * task = fPendingSaves.front(); + fPendingSaves.erase( fPendingSaves.begin() ); + hsLogEntry(plSafeGetLog()->AddLineF( "Aborting pending save operation on node %lu", GetID() )); + task->TaskComplete( false ); + } +} + +void plVaultNode::IAddPendingSave( plVaultSaveNodeTask * task ) +{ + hsLogEntry(plSafeGetLog()->AddLineF( "Adding pending save operation to node %lu", GetID() )); + fPendingSaves.push_back( task ); +} + + +static bool CompareNodeRefCreateTimes( const plVaultNodeRef * A, const plVaultNodeRef * B ) +{ + return ( A->GetCreateTime()->GetSecs()GetCreateTime()->GetSecs() ); +} + +plVaultNodeRef * plVaultNode::ILocalAddNode( plVaultNode * node, bool notify ) +{ + plVaultNodeRef nodeRef; + nodeRef.ISetChildNode( node ); + return ILocalAddNode( &nodeRef, notify ); +} + +plVaultNodeRef * plVaultNode::ILocalAddNode( const plVaultNodeRef * in, bool notify ) +{ + hsAssert( in->GetChildID()!=0, "ILocalAddNode: Invalid child ID" ); + + if ( in->fChildIDGetChildID() ) ) + { +// plSafeGetLog()->AddLine( "ILocalAddNode: node being added as a child exists above me in family tree!" ); + hsAssert( false, "ILocalAddNode: node being added as a child exists above me in family tree!" ); + return nil; + } + + plVaultNodeRef * nodeRef; + bool found = GetNode( in->GetChildID(), nodeRef ); + if ( !found && fMyNodeMgr) + { + nodeRef = fMyNodeMgr->CreateNodeRef(); + nodeRef->CopyFrom( in ); + nodeRef->ISetParentID( GetID() ); + if ( in->GetChild() ) + nodeRef->ISetChildNode( in->GetChild() ); + // add node + fChildNodes.push_back( nodeRef ); + // sort nodes by create time + std::sort( fChildNodes.begin(), fChildNodes.end(), CompareNodeRefCreateTimes ); + // call internal notify + ILocalNodeAdded( nodeRef ); + hsLogEntry(plSafeGetLog()->AddLineF( "Added child %lu to parent %lu", nodeRef->GetChildID(), nodeRef->GetParentID() )); + // do callbacks + if ( notify ) + fMyNodeMgr->INotifyNodeRefAdded( nodeRef ); + } + return nodeRef; +} + +bool plVaultNode::ILocalRemoveNode( UInt32 nodeID, bool notify ) +{ + plVaultNode tmp; + tmp.ISetID( nodeID ); + plVault::NodeRefVec::iterator it = + std::find_if( fChildNodes.begin(), fChildNodes.end(), + plVaultNode::MatchesNode( &tmp ) ); + bool found = ( it!=fChildNodes.end() ); + if ( found && fMyNodeMgr) + { + plVaultNodeRef * nodeRef = *it; + if ( notify ) + fMyNodeMgr->INotifyRemovingNodeRef( nodeRef ); + delete *it; + fChildNodes.erase( it ); + if ( notify ) + fMyNodeMgr->INotifyNodeRefRemoved( nodeID, GetID() ); + } + return found; +} + +bool plVaultNode::IIsAnUpstreamNode( UInt32 nodeID ) +{ + plVaultNode * node; + if ( fMyNodeMgr && fMyNodeMgr->GetNode( nodeID, node ) ) + return node->IIsADownstreamNode( GetID() ); + return false; +} + +bool plVaultNode::IIsADownstreamNode( UInt32 nodeID ) +{ + for ( int i=0; iGetChildID()==nodeID ) + return true; + if ( fChildNodes[i]->GetChild()->IIsADownstreamNode( nodeID ) ) + return true; + } + return false; +} + +plVaultNodeRef * plVaultNode::AddNode( plVaultNode * node, plVaultOperationCallback * cb, UInt32 cbContext ) +{ + if ( IsPersistent() && !IsStored() && node->IsPersistent() ) + { + std::string msg; + xtl::format( msg, "Tried to add persistent child to non-saved persistent parent. parent fSaving=%s", fSaving?"yep":"nope" ); + hsLogEntry(plSafeGetLog()->AddLineF( plStatusLog::kRed, "%s", msg.c_str() )); +// hsAssert( false, msg.c_str() ); + plVaultOperationCallbackHolder cbHolder( fMyNodeMgr, cb, cbContext ); + cbHolder.OperationComplete( hsFail ); + return false; + } + plVaultNodeRef * nodeRef; + if ( FindNode( node, nodeRef ) ) + { + // we already have this child, short circuit. + plVaultOperationCallbackHolder cbHolder( fMyNodeMgr, cb, cbContext ); + cbHolder.SetCbFields( nodeRef->GetChild(), nodeRef ); + cbHolder.OperationComplete( hsOK ); + return nodeRef; + } + + return fMyNodeMgr ? fMyNodeMgr->IAddNodeRef( node, GetID(), cb, cbContext ) : nil; // starts network operation +} + +bool plVaultNode::LinkToNode( const plVaultNode * templateNode, int childFetchLevel, bool allowCreate, plVaultOperationCallback * cb, UInt32 cbContext ) +{ + if ( IsPersistent() && !IsStored() ) + { + std::string msg; + xtl::format( msg, "Tried to link a child to non-saved persistent parent. parent fSaving=%s", fSaving?"yep":"nope" ); + hsLogEntry(plSafeGetLog()->AddLineF( plStatusLog::kRed, "%s", msg.c_str() )); +// hsAssert( false, msg.c_str() ); + plVaultOperationCallbackHolder cbHolder( fMyNodeMgr, cb, cbContext ); + cbHolder.OperationComplete( hsFail ); + return false; + } + plVaultNodeRef * nodeRef; + if ( FindNode( templateNode, nodeRef ) ) + { + // we already have this child, short circuit. + plVaultOperationCallbackHolder cbHolder( fMyNodeMgr, cb, cbContext ); + cbHolder.SetCbFields( nodeRef->GetChild(), nodeRef ); + cbHolder.OperationComplete( hsOK ); + return true; + } + + // Can't allowCreate if template specified an ID, unless we aren't online. + allowCreate &= ( fMyNodeMgr && !templateNode->IIsFieldFlagSet( plVaultNode::kID ) || !fMyNodeMgr->IAmOnline() ); + + fMyNodeMgr ? fMyNodeMgr->ILinkToNode( templateNode, childFetchLevel, allowCreate, GetID(), cb, cbContext ) : NULL_STMT; + + return true; +} + +bool plVaultNode::LinkToNode( UInt32 nodeID, int childFetchLevel, plVaultOperationCallback * cb, UInt32 cbContext ) +{ + plVaultNode templateNode; + templateNode.ISetID( nodeID ); + return LinkToNode( &templateNode, childFetchLevel, false, cb,cbContext ); +} + +bool plVaultNode::RemoveNode( const plVaultNode * node, plVaultOperationCallback * cb/*=nil*/, UInt32 cbContext/*=0 */) +{ + // remove by id + if ( node->GetID()!=0 ) + return RemoveNode( node->GetID(), cb, cbContext ); + // remove by template match + plVaultNodeRef * nodeRef; + if ( FindNode( node, nodeRef ) ) + return RemoveNode( nodeRef->GetChildID(), cb, cbContext ); + plVaultOperationCallbackHolder cbHolder( fMyNodeMgr, cb, cbContext ); + cbHolder.OperationComplete( hsFail ); + return false; +} + +bool plVaultNode::RemoveNode( UInt32 nodeID, plVaultOperationCallback * cb/*=nil*/, UInt32 cbContext/*=0 */) +{ + fMyNodeMgr ? fMyNodeMgr->IRemoveNodeRef( nodeID, GetID(), cb, cbContext ) : NULL_STMT;// starts network operation + return true; +} + +void plVaultNode::GetNodes( plVault::NodeRefVec & out ) +{ + std::copy( fChildNodes.begin(), fChildNodes.end(), std::back_inserter( out ) ); +} + +bool plVaultNode::GetNode( UInt32 nodeID, plVaultNodeRef *& out ) const +{ + plVaultNode tmp; + tmp.ISetID( nodeID ); + return FindNode( &tmp, out ); +} + +bool plVaultNode::FindNode( const plVaultNode * templateNode, plVaultNodeRef *& out ) const +{ + plVaultNode tmp; + if ( templateNode->IsStored() ) + tmp.ISetID( templateNode->GetID() ); + else + tmp.CopyFrom( templateNode ); + + out = nil; + plVault::NodeRefVec::const_iterator it = + std::find_if( fChildNodes.begin(), fChildNodes.end(), + plVaultNode::MatchesNode( &tmp ) ); + bool found = ( it!=fChildNodes.end() ); + if ( found ) + out = *it; + return found; +} + +bool plVaultNode::FindNodes( const plVaultNode * templateNode, plVault::NodeRefVec & out ) const +{ + plVaultNode tmp; + if ( templateNode->IsStored() ) + tmp.ISetID( templateNode->GetID() ); + else + tmp.CopyFrom( templateNode ); + + int n = out.size(); + MatchesNode matcher( &tmp ); + for ( int i=0; i 0 ); +} + +bool plVaultNode::FindNodeRecurse( const plVaultNode * templateNode, plVaultNodeRef *& out ) const +{ + if ( !FindNode( templateNode, out ) ) + { + for ( plVault::NodeRefVec::const_iterator it=fChildNodes.begin(); it!=fChildNodes.end(); ++it ) + { + if ( (*it)->GetChild()->FindNodeRecurse( templateNode, out ) ) + return true; + } + } + return false; +} + +bool plVaultNode::HasNode( UInt32 nodeID ) +{ + plVaultNode templateNode; + templateNode.SetID( nodeID ); + return HasNode( &templateNode ); +} + +bool plVaultNode::HasNode( const plVaultNode * templateNode ) +{ + plVaultNodeRef * nodeRef; + return FindNode( templateNode, nodeRef ); +} + +bool plVaultNode::Modified( void ) const +{ + fFieldFlags.Compact(); + return ( fFieldFlags.GetSize()>0 ); +} + +void plVaultNode::SetModified( bool b ) +{ + if ( !b ) + { + IClearAllFieldFlags(); + } + else + { + ISetAllFieldFlags(); + SetModifyTime( &plUnifiedTime::GetCurrentTime() ); + } +} + + + +UInt32 plVaultNode::GetClientID( void ) const +{ + if ( !fMyNodeMgr ) + return 0; + return fMyNodeMgr->GetClientID(); +} + + +void plVaultNode::Save( plVaultOperationCallback * cb, UInt32 cbContext ) +{ + if (Modified() && fMyNodeMgr) + { + fMyNodeMgr->ISaveNode( this, cb, cbContext ); + } + else + { + plVaultOperationCallbackHolder cbHolder( fMyNodeMgr, cb, cbContext ); + cbHolder.OperationComplete( hsOK ); + } +} + +void plVaultNode::SaveAll( plVaultOperationCallback * cb, UInt32 cbContext ) +{ + for ( int i=0; iGetChild(); + child->SaveAll( cb, cbContext ); + } + Save( cb, cbContext ); +} + + +void plVaultNode::SendTo( UInt32 destClientNodeID, plVaultOperationCallback * cb, UInt32 cbContext ) +{ + if (!fMyNodeMgr) + return; + fMyNodeMgr->ISendNode( this, destClientNodeID, cb, cbContext ); +} + + +void plVaultNode::RemoveAllNodes( void ) +{ + plVault::IDVec ids; + for ( plVaultNodeIterator nodeIt=GetIterator(); nodeIt; ++nodeIt ) + { + ids.push_back( (*nodeIt)->GetChildID() ); + } + for ( int i=0; iReadSwap( &fID ); + s->ReadSwap( &fType ); + s->ReadSwap( &fPermissions ); + s->ReadSwap( &fOwnerNodeID ); + s->ReadSwap( &fGroupNodeID ); + fModifyTime.Read( s ); + // set these to nil in case their corresponding ID changed above. + fOwnerNode = nil; + fGroupNode = nil; + + // fields relating to creation of this node + if ( IIsFieldFlagSet( kCreatorNodeID ) ) + { + s->ReadSwap( &fCreatorNodeID ); + fCreatorNode = nil; + } + if ( IIsFieldFlagSet( kCreateTime ) ) + { + fCreateTime.Read( s ); + } +// if ( IIsFieldFlagSet( kCreateAgeCoords ) ) +// { +// fCreateAgeCoords.Read( s, mgr ); +// } + if ( IIsFieldFlagSet( kCreateAgeTime ) ) + { + fCreateAgeTime.Read( s ); + } + if ( IIsFieldFlagSet( kCreateAgeName ) ) + { + plMsgStdStringHelper::Peek( fCreateAgeName, s ); + } + if ( IIsFieldFlagSet( kCreateAgeGuid ) ) + { + fCreateAgeGuid.Read( s ); + } + // generic fields + if ( IIsFieldFlagSet( kInt32_1 ) ) + { + s->ReadSwap( &fInt32_1 ); + } + if ( IIsFieldFlagSet( kInt32_2 ) ) + { + s->ReadSwap( &fInt32_2 ); + } + if ( IIsFieldFlagSet( kInt32_3 ) ) + { + s->ReadSwap( &fInt32_3 ); + } + if ( IIsFieldFlagSet( kInt32_4 ) ) + { + s->ReadSwap( &fInt32_4 ); + } + if ( IIsFieldFlagSet( kUInt32_1 ) ) + { + s->ReadSwap( &fUInt32_1 ); + } + if ( IIsFieldFlagSet( kUInt32_2 ) ) + { + s->ReadSwap( &fUInt32_2 ); + } + if ( IIsFieldFlagSet( kUInt32_3 ) ) + { + s->ReadSwap( &fUInt32_3 ); + } + if ( IIsFieldFlagSet( kUInt32_4 ) ) + { + s->ReadSwap( &fUInt32_4 ); + } + if ( IIsFieldFlagSet( kString64_1 ) ) + { + plMsgStdStringHelper::Peek( fString64_1, s ); + } + if ( IIsFieldFlagSet( kString64_2 ) ) + { + plMsgStdStringHelper::Peek( fString64_2, s ); + } + if ( IIsFieldFlagSet( kString64_3 ) ) + { + plMsgStdStringHelper::Peek( fString64_3, s ); + } + if ( IIsFieldFlagSet( kString64_4 ) ) + { + plMsgStdStringHelper::Peek( fString64_4, s ); + } + if ( IIsFieldFlagSet( kString64_5 ) ) + { + plMsgStdStringHelper::Peek( fString64_5, s ); + } + if ( IIsFieldFlagSet( kString64_6 ) ) + { + plMsgStdStringHelper::Peek( fString64_6, s ); + } + if ( IIsFieldFlagSet( kIString64_1 ) ) + { + plMsgStdStringHelper::Peek( fIString64_1, s ); + } + if ( IIsFieldFlagSet( kIString64_2 ) ) + { + plMsgStdStringHelper::Peek( fIString64_2, s ); + } + if ( IIsFieldFlagSet( kText_1 ) ) + { + plMsgStdStringHelper::Peek( fText_1, s ); + } + if ( IIsFieldFlagSet( kText_2 ) ) + { + plMsgStdStringHelper::Peek( fText_2, s ); + } + if ( IIsFieldFlagSet( kBlob_1 ) ) + { + UInt32 buflen; + s->ReadSwap( &buflen ); + IAllocBufferBlob_1( buflen ); + if (buflen) + s->Read( buflen,(void*)IGetBufferBlob_1() ); + } + if ( IIsFieldFlagSet( kBlob_2 ) ) + { + UInt32 buflen; + s->ReadSwap( &buflen ); + IAllocBufferBlob_2( buflen ); + if (buflen) + s->Read( buflen,(void*)IGetBufferBlob_2() ); + } + if ( IIsFieldFlagSet( kBlob_1_Guid ) ) + { + fBlob_1_Guid.Read( s ); + } + if ( IIsFieldFlagSet( kBlob_2_Guid ) ) + { + fBlob_2_Guid.Read( s ); + } +} + +// NOTE: IF YOU CHANGE plVaultNode STREAM FORMAT, BE SURE TO +// INCREMENT VaultNodeVersion (ABOVE). +void plVaultNode::Write( hsStream * s, hsResMgr * mgr ) +{ + hsAssert( s, "nil stream" ); +#ifdef HS_BUILD_FOR_UNIX + plOperationTimer t( "plVaultNode::Write" ); + t.Start( "write operation", 2 ); +#endif + + + fFieldFlags.Write( s ); + + // these fields are always streamed + s->WriteSwap( fID ); + s->WriteSwap( fType ); + s->WriteSwap( fPermissions ); + s->WriteSwap( fOwnerNodeID ); + s->WriteSwap( fGroupNodeID ); + fModifyTime.Write( s ); + + // fields relating to creation of this node + if ( IIsFieldFlagSet( kCreatorNodeID ) ) + { + s->WriteSwap( fCreatorNodeID ); + } + if ( IIsFieldFlagSet( kCreateTime ) ) + { + fCreateTime.Write( s ); + } +// if ( IIsFieldFlagSet( kCreateAgeCoords ) ) +// { +// fCreateAgeCoords.Write( s, mgr ); +// } + if ( IIsFieldFlagSet( kCreateAgeTime ) ) + { + fCreateAgeTime.Write( s ); + } + if ( IIsFieldFlagSet( kCreateAgeName ) ) + { + plMsgStdStringHelper::Poke( fCreateAgeName, s ); + } + if ( IIsFieldFlagSet( kCreateAgeGuid ) ) + { + fCreateAgeGuid.Write( s ); + } + // generic fields + if ( IIsFieldFlagSet( kInt32_1 ) ) + { + s->WriteSwap( fInt32_1 ); + } + if ( IIsFieldFlagSet( kInt32_2 ) ) + { + s->WriteSwap( fInt32_2 ); + } + if ( IIsFieldFlagSet( kInt32_3 ) ) + { + s->WriteSwap( fInt32_3 ); + } + if ( IIsFieldFlagSet( kInt32_4 ) ) + { + s->WriteSwap( fInt32_4 ); + } + if ( IIsFieldFlagSet( kUInt32_1 ) ) + { + s->WriteSwap( fUInt32_1 ); + } + if ( IIsFieldFlagSet( kUInt32_2 ) ) + { + s->WriteSwap( fUInt32_2 ); + } + if ( IIsFieldFlagSet( kUInt32_3 ) ) + { + s->WriteSwap( fUInt32_3 ); + } + if ( IIsFieldFlagSet( kUInt32_4 ) ) + { + s->WriteSwap( fUInt32_4 ); + } + if ( IIsFieldFlagSet( kString64_1 ) ) + { + plMsgStdStringHelper::Poke( fString64_1, s ); + } + if ( IIsFieldFlagSet( kString64_2 ) ) + { + plMsgStdStringHelper::Poke( fString64_2, s ); + } + if ( IIsFieldFlagSet( kString64_3 ) ) + { + plMsgStdStringHelper::Poke( fString64_3, s ); + } + if ( IIsFieldFlagSet( kString64_4 ) ) + { + plMsgStdStringHelper::Poke( fString64_4, s ); + } + if ( IIsFieldFlagSet( kString64_5 ) ) + { + plMsgStdStringHelper::Poke( fString64_5, s ); + } + if ( IIsFieldFlagSet( kString64_6 ) ) + { + plMsgStdStringHelper::Poke( fString64_6, s ); + } + if ( IIsFieldFlagSet( kIString64_1 ) ) + { + plMsgStdStringHelper::Poke( fIString64_1, s ); + } + if ( IIsFieldFlagSet( kIString64_2 ) ) + { + plMsgStdStringHelper::Poke( fIString64_2, s ); + } + if ( IIsFieldFlagSet( kText_1 ) ) + { + plMsgStdStringHelper::Poke( fText_1, s ); + } + if ( IIsFieldFlagSet( kText_2 ) ) + { + plMsgStdStringHelper::Poke( fText_2, s ); + } + if ( IIsFieldFlagSet( kBlob_1 ) ) + { + UInt32 buflen = IGetBufferSizeBlob_1(); + s->WriteSwap( buflen ); + if ( buflen ) + s->Write( buflen, IGetBufferBlob_1() ); + } + if ( IIsFieldFlagSet( kBlob_2 ) ) + { + UInt32 buflen = IGetBufferSizeBlob_2(); + s->WriteSwap( buflen ); + if ( buflen ) + s->Write( buflen, IGetBufferBlob_2() ); + } + if ( IIsFieldFlagSet( kBlob_1_Guid ) ) + { + fBlob_1_Guid.Write( s ); + } + if ( IIsFieldFlagSet( kBlob_2_Guid ) ) + { + fBlob_2_Guid.Write( s ); + } +} + +void plVaultNode::CopyFrom( const plVaultNode * other, bool forceCopyAllFields ) +{ + hsRAMStream ram; + plVaultNode * nonConstOther = const_cast( other ); + + hsBitVector saveFlags; + if ( forceCopyAllFields ) + { + saveFlags = nonConstOther->fFieldFlags; + nonConstOther->ISetAllFieldFlags(); + } + + nonConstOther->Write( &ram, nil ); + ram.Rewind(); + this->Read( &ram, nil ); + + if ( forceCopyAllFields ) + { + nonConstOther->fFieldFlags = saveFlags; + } +} + +std::string plVaultNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%sNode[ID:%lu,Owner:%lu,Creator:%lu,Mod:%s]", + space.c_str(), GetID(), GetOwnerNodeID(), GetCreatorNodeID(), + fModifyTime.Format("%m/%d/%y %H:%M:%S GMT").c_str() ); + return result; +} + +std::string plVaultNode::FieldsAsStdString( void ) const +{ + std::stringstream stm; + + stm << "ID=" << GetID(); + + if ( IIsFieldFlagSet( plVaultNode::kPermissions ) ) + { + stm << ", Permissions=" << GetPermissions(); + } + if ( IIsFieldFlagSet( plVaultNode::kOwnerNodeID ) ) + { + stm << ", OwnerIdx=" <GetStatusLog(); + std::string str; + if ( displaySeen ) + xtl::format( str, "%s, seen:%s", AsStdString( level ).c_str(), beenSeen?"yes":"no" ); + else + xtl::format( str, "%s", AsStdString( level ).c_str() ); + hsLogEntry(log->AddLine( str.c_str() )); + for ( plVault::NodeRefVec::const_iterator it=fChildNodes.begin(); it!=fChildNodes.end(); ++it ) + { + plVaultNodeRef * nodeRef = *it; + plVaultNode * node = nodeRef->GetChild(); + if ( node ) + node->Dump( level+2, true, nodeRef->BeenSeen() ); + } +} + +void plVaultNode::SetInitialized( bool b ) +{ + fInitialized = b; + if (!fMyNodeMgr) + return; + if ( fInitialized ) + fMyNodeMgr->INotifyNodeInitialized( this ); +} + +bool plVaultNode::StartInitializing( plVaultOperationCallback * cb, UInt32 cbContext ) +{ + plVNodeInitTask * initTask = fMyNodeMgr ? fMyNodeMgr->IGetNodeInitializationTask( this ) : nil; + if ( initTask ) + { + initTask->SetCbObject( cb, cbContext ); + initTask->Start(); + return true; // initTask started + } + else + { + SetInitialized( true ); + plVaultOperationCallbackHolder cbHolder( fMyNodeMgr, cb, cbContext ); + cbHolder.OperationComplete( hsOK ); + return false; // no initTask started + } +} + + + +//////////////////////////////////////////////////////////////////// + +plVaultFolderNode::plVaultFolderNode() +{ + ISetType( plVault::kNodeType_Folder ); +} + +plVaultFolderNode::~plVaultFolderNode() +{ +} + +std::string plVaultFolderNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%s%s[Type:%s,Name:%s] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), plVault::StandardNodeStr( GetFolderType() ), + GetFolderName(), plVaultNode::AsStdString().c_str() ); + return result; +} + +std::string plVaultFolderNode::AsHtmlForm( const char * action ) +{ + std::stringstream ss; + ss << "

    " << std::endl; + ss << "
    " << std::endl; + return ss.str(); +} + + +//////////////////////////////////////////////////////////////////// + +plVaultImageNode::plVaultImageNode() +: fMipmap( nil ) +{ + ISetType( plVault::kNodeType_Image ); +} + +plVaultImageNode::~plVaultImageNode() +{ +} + +void plVaultImageNode::CopyFrom( const plVaultNode * other, bool forceCopyAllFields ) +{ + const plVaultImageNode * IMG = plVaultImageNode::ConvertNoRef( other ); + if ( IMG ) + fMipmap = IMG->fMipmap; + plVaultNode::CopyFrom( other, forceCopyAllFields ); +} + +std::string plVaultImageNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%s%s[Size:%d] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), GetBufSize(), plVaultNode::AsStdString().c_str() ); + return result; +} + +//////////////////////////////////////////////////////////////////// + +plVaultTextNoteNode::plVaultTextNoteNode() +{ + ISetType( plVault::kNodeType_TextNote ); +} + +plVaultTextNoteNode::~plVaultTextNoteNode() +{ +} + +void plVaultTextNoteNode::SetText( const char * text ) +{ + std::string work=text; memcpy( IAllocBufferBlob_1( work.size() ), work.c_str(), work.size() ); +} + +std::string plVaultTextNoteNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%s%s[Title:%s,Text:%s,Type:%d,Sub:%d] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), GetTitle(), + GetText(), GetNoteType(), GetNoteSubType(), plVaultNode::AsStdString().c_str() ); + return result; +} + +plVaultFolderNode * plVaultTextNoteNode::GetDeviceInbox() const +{ + hsAssert( GetNoteType()==plVault::kNoteType_Device, "This text note is not a device." ); + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kDeviceInboxFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + return plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + return nil; +} + +void plVaultTextNoteNode::SetDeviceInbox( const char * inboxName, + plVaultOperationCallback * cb/*=nil*/, UInt32 cbContext/*=0*/ ) +{ + // Remove old inbox. + plVaultFolderNode * inbox = GetDeviceInbox(); + if ( inbox ) + RemoveNode( inbox->GetID() ); + // Link to new inbox. + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kDeviceInboxFolder ); + tmp.SetFolderName( inboxName ); + // if the inbox name matches our device title, then + // connect to our own personal version of the inbox, + // else connect to the shared copy of the inbox. + if ( stricmp( inboxName, GetTitle() )==0 ) + tmp.SetOwnerNodeID( GetID() ); + LinkToNode( &tmp, plVault::kFetchAllChildren, true, cb, cbContext ); +} + + +//////////////////////////////////////////////////////////////////// + +plVaultSDLNode::plVaultSDLNode() +: fSDLDataRec( nil ) +{ + ISetType( plVault::kNodeType_SDL ); +} + +plVaultSDLNode::~plVaultSDLNode() +{ + delete fSDLDataRec; +} + +void plVaultSDLNode::Save( + plVaultOperationCallback * cb/*=nil*/, + UInt32 cbContext/*=0 */) +{ +#ifdef HS_DEBUGGING + hsLogEntry( DumpStateDataRecord("Saving plVaultSDLNode", false ) ); +#endif + plVaultNode::Save( cb, cbContext ); +} + +plStateDataRecord * plVaultSDLNode::GetStateDataRecord( UInt32 readOptions/*=0 */) const +{ + delete fSDLDataRec; + fSDLDataRec = nil; + + if ( GetBufSize()==0 || GetBuffer()==nil ) + return nil; + + hsRAMStream ram; + ram.Write( GetBufSize(), GetBuffer() ); + ram.Rewind(); + char * sdlRecName=nil; + int sdlRecVersion; + plStateDataRecord::ReadStreamHeader( &ram, &sdlRecName, &sdlRecVersion ); + + hsLogEntry(plSafeGetLog()->AddLineF( "SDLNode: SDL: name %s, version %d.", sdlRecName, sdlRecVersion )); + + plStateDescriptor* des = plSDLMgr::GetInstance()->FindDescriptor(sdlRecName, sdlRecVersion ); + if (!des) + { + hsLogEntry(plSafeGetLog()->AddLineF( "SDLNode: Can't find SDL desc, name:%s ver:%d. Attempting to read with latest descriptor.", sdlRecName, sdlRecVersion )); + des = plSDLMgr::GetInstance()->FindDescriptor(sdlRecName, plSDL::kLatestVersion ); + if (!des) + { + hsLogEntry(plSafeGetLog()->AddLineF( "SDLNode: Can't find SDL desc, name:%s ver:kLatestVersion. Cannot read sdl record.", sdlRecName )); + return nil; + } + } + + fSDLDataRec = TRACKED_NEW plStateDataRecord( des ); + if ( !fSDLDataRec->Read( &ram, 0, readOptions ) ) + { + hsLogEntry(plSafeGetLog()->AddLineF( "SDLNode: Failed to read SDL desc, name:%s ver:%d", sdlRecName, sdlRecVersion )); + delete fSDLDataRec; + fSDLDataRec = nil; + } + else + { + hsLogEntry(plSafeGetLog()->AddLineF( "SDLNode: After reading SDL: name %s, version %d.", + fSDLDataRec->GetDescriptor()->GetName(), fSDLDataRec->GetDescriptor()->GetVersion() )); + } + + delete [] sdlRecName; + return fSDLDataRec; +} + +void plVaultSDLNode::InitStateDataRecord( const char * sdlRecName, UInt32 writeOptions/*=0 */) +{ + if ( !GetStateDataRecord() ) + { + delete fSDLDataRec; + fSDLDataRec = nil; + + plStateDescriptor* des = plSDLMgr::GetInstance()->FindDescriptor(sdlRecName, plSDL::kLatestVersion); + if ( !des ) + { + hsLogEntry( plSafeGetLog()->AddLineF( "SDLNode: Can't find SDL desc, name:%s", sdlRecName ) ); + return; + } + + fSDLDataRec = TRACKED_NEW plStateDataRecord( des ); + fSDLDataRec->SetFromDefaults( false ); + SetStateDataRecord( fSDLDataRec, writeOptions|plSDL::kDontWriteDirtyFlag ); + Save(); + } +} + + +void plVaultSDLNode::SetStateDataRecord( const plStateDataRecord * rec, UInt32 writeOptions/*=0 */) +{ + hsRAMStream ram; + rec->WriteStreamHeader( &ram ); + rec->Write( &ram, 0, writeOptions ); + ram.Rewind(); + ram.CopyToMem( AllocBuffer( ram.GetEOF() ) ); +} + +void plVaultSDLNode::DumpStateDataRecord( const char * msg/*=nil */, bool dirtyOnly/*=false*/ ) const +{ + if ( !fSDLDataRec ) + GetStateDataRecord(); + + if ( !fSDLDataRec ) + return; + + if ( !msg ) + msg = "VaultSDLNode"; + + fSDLDataRec->DumpToObjectDebugger( msg, dirtyOnly ); +} + +std::string plVaultSDLNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%s%s[Size:%d,Ident:%s] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), + GetBufSize(), plVault::StandardNodeStr( GetIdent() ), + plVaultNode::AsStdString().c_str() ); + return result; +} + +//////////////////////////////////////////////////////////////////// + +plVaultAgeInfoNode::plVaultAgeInfoNode() +: fCanVisitFolder( nil ) +, fAgeOwnersFolder( nil ) +, fChildAgesFolder( nil ) +, fAgeSDL( nil ) +{ + ISetType( plVault::kNodeType_AgeInfo ); +} + +plVaultAgeInfoNode::~plVaultAgeInfoNode() +{ +} + +plVaultAgeLinkNode * plVaultAgeInfoNode::IGetLink( plVaultFolderNode * folder, const plAgeInfoStruct * info ) const +{ + plVaultNodeIterator it = folder->GetIterator(); + + for ( it; it; ++it ) + { + plVaultAgeLinkNode * linkNode = plVaultAgeLinkNode::ConvertNoRef( it->GetChild() ); + if ( !linkNode ) + continue; + plVaultAgeInfoNode * infoNode = linkNode->GetAgeInfo(); + if ( !infoNode ) + continue; + if ( info->HasAgeInstanceGuid() && infoNode->GetAgeInstanceGuid()->IsEqualTo( info->GetAgeInstanceGuid() ) ) + return linkNode; + if ( info->HasAgeInstanceName() && stricmp( infoNode->GetAgeInstanceName(), info->GetAgeInstanceName() )==0 ) + return linkNode; + if ( info->HasAgeFilename() && stricmp( infoNode->GetAgeFilename(), info->GetAgeFilename() )==0 ) + return linkNode; + } + + return nil; +} + +const plNetServerSessionInfo * plVaultAgeInfoNode::AsServerInfo() const +{ + fServerInfo.Clear(); + fServerInfo.SetServerName( GetAgeInstanceName() ); + fServerInfo.SetServerGuid( GetAgeInstanceGuid() ); + return &fServerInfo; +} + +const plAgeInfoStruct * plVaultAgeInfoNode::AsAgeInfoStruct() const +{ + fAgeInfoStruct.SetAgeFilename( GetAgeFilename() ); + fAgeInfoStruct.SetAgeInstanceName( GetAgeInstanceName() ); + fAgeInfoStruct.SetAgeInstanceGuid( GetAgeInstanceGuid() ); + fAgeInfoStruct.SetAgeUserDefinedName( GetAgeUserDefinedName() ); + fAgeInfoStruct.SetAgeSequenceNumber( GetSequenceNumber() ); + fAgeInfoStruct.SetAgeDescription( GetAgeDescription() ); + return &fAgeInfoStruct; +} + + +void plVaultAgeInfoNode::FromAgeInfoStruct( const plAgeInfoStruct * ageInfo ) +{ + if ( ageInfo->HasAgeFilename() ) + SetAgeFilename( ageInfo->GetAgeFilename() ); + if ( ageInfo->HasAgeInstanceName() ) + SetAgeInstanceName( ageInfo->GetAgeInstanceName() ); + if ( ageInfo->HasAgeInstanceGuid() ) + SetAgeInstanceGuid( ageInfo->GetAgeInstanceGuid() ); + if ( ageInfo->HasAgeUserDefinedName() ) + SetAgeUserDefinedName( ageInfo->GetAgeUserDefinedName() ); + if ( ageInfo->HasAgeSequenceNumber() ) + SetSequenceNumber( ageInfo->GetAgeSequenceNumber() ); + if ( ageInfo->HasAgeDescription() ) + SetAgeDescription( ageInfo->GetAgeDescription() ); +} + +plVaultPlayerInfoListNode * plVaultAgeInfoNode::GetCanVisitFolder( void ) const +{ + if ( fCanVisitFolder ) + return fCanVisitFolder; + plVaultPlayerInfoListNode tmp; + tmp.SetFolderType( plVault::kCanVisitFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fCanVisitFolder = plVaultPlayerInfoListNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fCanVisitFolder ) + hsLogEntry(plSafeGetLog()->AddLineF( "fCanVisitFolder node not found" )); + return fCanVisitFolder; +} + +plVaultPlayerInfoListNode * plVaultAgeInfoNode::GetAgeOwnersFolder( void ) const +{ + if ( fAgeOwnersFolder ) + return fAgeOwnersFolder; + plVaultPlayerInfoListNode tmp; + tmp.SetFolderType( plVault::kAgeOwnersFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fAgeOwnersFolder = plVaultPlayerInfoListNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fAgeOwnersFolder ) + hsLogEntry(plSafeGetLog()->AddLineF( "fAgeOwnersFolder node not found" )); + return fAgeOwnersFolder; +} + +plVaultFolderNode * plVaultAgeInfoNode::GetChildAgesFolder( void ) const +{ + if ( fChildAgesFolder ) + return fChildAgesFolder; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kChildAgesFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fChildAgesFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fChildAgesFolder ) + hsLogEntry(plSafeGetLog()->AddLineF( "fChildAgesFolder node not found" )); + return fChildAgesFolder; +} + +plVaultAgeLinkNode * plVaultAgeInfoNode::GetChildAgeLink( const plAgeInfoStruct * info ) const +{ + GetChildAgesFolder(); + hsAssert( fChildAgesFolder, "Missing ChildAges folder" ); + if (fChildAgesFolder) + return IGetLink( fChildAgesFolder, info ); + return nil; +} + +plVaultSDLNode * plVaultAgeInfoNode::IGetAgeSDL() const +{ + if ( fAgeSDL ) + return fAgeSDL; + plVaultSDLNode tmp; + tmp.SetIdent( plVault::kAgeInstanceSDLNode ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fAgeSDL = plVaultSDLNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fAgeSDL ) + hsLogEntry(plSafeGetLog()->AddLineF( "fAgeSDL node not found" )); + return fAgeSDL; +} + +plVaultPlayerInfoNode * plVaultAgeInfoNode::GetCzar() const +{ + if ( !GetAgeOwnersFolder() ) + return nil; + GetAgeOwnersFolder()->Sort(); + plVaultNodeIterator it = GetAgeOwnersFolder()->GetIterator(); + if ( it.GetCount()==0 ) + return nil; + return plVaultPlayerInfoNode::ConvertNoRef( it.GetAt( 0 )->GetChild() ); +} + + +const char * plVaultAgeInfoNode::GetDisplayName() const +{ + return AsAgeInfoStruct()->GetDisplayName(); +} + +std::string plVaultAgeInfoNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%s%s[Age:%lu,FName:%s,IName:%s,Guid:%s,UName:%s,Seq:%d,Pub:%s] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), + GetAgeID(), GetAgeFilename(), GetAgeInstanceName(), + GetAgeInstanceGuid()->AsStdString().c_str(), GetAgeUserDefinedName(), + GetSequenceNumber(), IsPublic()?"yes":"no", plVaultNode::AsStdString().c_str() ); + return result; +} + + + +//////////////////////////////////////////////////////////////////// + +plVaultAgeLinkNode::plVaultAgeLinkNode() +: fAgeInfo( nil ) +{ + ISetType( plVault::kNodeType_AgeLink ); +} + +plVaultAgeLinkNode::~plVaultAgeLinkNode() +{ +} + +plVaultAgeInfoNode * plVaultAgeLinkNode::GetAgeInfo() const +{ + if ( fAgeInfo ) + return fAgeInfo; + plVaultAgeInfoNode tmp; + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fAgeInfo = plVaultAgeInfoNode::ConvertNoRef( nodeRef->GetChild() ); + return fAgeInfo; +} + +void plVaultAgeLinkNode::SetAgeInfo( plVaultAgeInfoNode * node ) +{ + plVaultAgeInfoNode * curr = GetAgeInfo(); + if ( curr ) + { + if ( curr->GetID()==node->GetID() ) + return; + + RemoveNode( curr->GetID() ); + } + fAgeInfo = nil; + AddNode( node ); +} + +struct MatchesSpawnPointTitle +{ + std::string fTitle; + MatchesSpawnPointTitle( const char * title ):fTitle( title ){} + bool operator ()( const plSpawnPointInfo & p ) const { return ( p.fTitle==fTitle ); } +}; +struct MatchesSpawnPointName +{ + std::string fName; + MatchesSpawnPointName( const char * name ):fName( name ){} + bool operator ()( const plSpawnPointInfo & p ) const { return ( p.fSpawnPt==fName ); } +}; + +void plVaultAgeLinkNode::AddSpawnPoint( const plSpawnPointInfo & point ) +{ + plSpawnPointVec points; + GetSpawnPoints( points ); + if ( std::find_if( points.begin(), points.end(), MatchesSpawnPointTitle( point.fTitle.c_str() ) )!=points.end() ) + return; + + // only check to see if the titles are the same... + //... so we can add the same spawnpoint as long as they have different titles + //if ( std::find_if( points.begin(), points.end(), MatchesSpawnPointName( point.fSpawnPt.c_str() ) )!=points.end() ) + // return; + + points.push_back( point ); + SetSpawnPoints( points ); +} + +void plVaultAgeLinkNode::RemoveSpawnPoint( const char * spawnPtName ) +{ + plSpawnPointVec points; + GetSpawnPoints( points ); + plSpawnPointVec::iterator it = std::find_if( points.begin(), points.end(), MatchesSpawnPointName( spawnPtName ) ); + if ( it!=points.end() ) + { + points.erase( it ); + SetSpawnPoints( points ); + } +} + +bool plVaultAgeLinkNode::HasSpawnPoint( const plSpawnPointInfo & point ) const +{ + return HasSpawnPoint( point.GetName() ); +} + +bool plVaultAgeLinkNode::HasSpawnPoint( const char * spawnPtName ) const +{ + plSpawnPointVec points; + GetSpawnPoints( points ); + return ( std::find_if( points.begin(), points.end(), MatchesSpawnPointName( spawnPtName ) )!=points.end() ); +} + +void plVaultAgeLinkNode::GetSpawnPoints( plSpawnPointVec & out ) const +{ + char token1[ 1024 ]; + hsStringTokenizer izer1( (const char *)IGetBufferBlob_1(), ";" ); + while ( izer1.Next( token1, sizeof( token1 ) ) ) + { + plSpawnPointInfo point; + char token2[ 1024 ]; + hsStringTokenizer izer2( token1, ":" ); + if ( izer2.Next( token2, sizeof( token2 ) ) ) + point.fTitle = token2; + if ( izer2.Next( token2, sizeof( token2 ) ) ) + point.fSpawnPt = token2; + if ( izer2.Next( token2, sizeof( token2 ) ) ) + point.fCameraStack = token2; + out.push_back( point ); + } +} + +void plVaultAgeLinkNode::SetSpawnPoints( const plSpawnPointVec & in ) +{ + std::stringstream ss; + for ( int i=0; iIIsThisMe( this ) ) ); + ISetIString64_1( text ); +} + +void plVaultPlayerInfoNode::SetPlayerID( UInt32 v ) +{ + if ( fMyNodeMgr ) + CheckIAmSuperUserOr_VoidReturn( fMyNodeMgr, ( !IsStored()||fMyNodeMgr->IIsThisMe( this ) ) ); + ISetUInt32_1( v ); +} + +void plVaultPlayerInfoNode::SetAgeInstanceName( const char * v ) +{ + ISetString64_1( v ); +} + +const char * plVaultPlayerInfoNode::GetAgeInstanceName( void ) const +{ + return ( IsOnline() ) ? IGetString64_1() : ""; +} + +void plVaultPlayerInfoNode::SetAgeGuid( const plUUID * v) +{ + ISetString64_2( v->AsStdString().c_str() ); +} + +const plUUID * plVaultPlayerInfoNode::GetAgeGuid( void ) const +{ + if ( IsOnline() ) + fGuid.FromString( IGetString64_2() ); + else + fGuid.Clear(); + return &fGuid; +} + +void plVaultPlayerInfoNode::SetOnline( bool b ) +{ + ISetInt32_1( b ); +} + + +std::string plVaultPlayerInfoNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%s%s[PlayerID:%lu,PlayerName:%s,IsOnline:%s,InAge:%s,InAgeGuid:%s] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), GetPlayerID(), + GetPlayerName(), (IsOnline())?"yes":"no", + GetAgeInstanceName(), GetAgeGuid()->AsStdString().c_str(), + plVaultNode::AsStdString().c_str() ); + return result; +} + +//////////////////////////////////////////////////////////////////// + +// true if A is less than B +bool PlayerInfoNodeCompare( const plVaultNode * a, const plVaultNode * b ) +{ + const plVaultPlayerInfoNode * A = plVaultPlayerInfoNode::ConvertNoRef( a ); + const plVaultPlayerInfoNode * B = plVaultPlayerInfoNode::ConvertNoRef( b ); + + if ( !A ) + return false; + if ( !B ) + return true; + if ( A->IsOnline() && !B->IsOnline() ) + return true; + if ( B->IsOnline() && !A->IsOnline() ) + return false; + return ( stricmp( A->GetPlayerName(), B->GetPlayerName() )<0 ); +} + +bool PlayerInfoNodeRefCompare( const plVaultNodeRef * nodeRefA, const plVaultNodeRef * nodeRefB ) +{ + if ( !nodeRefA || !nodeRefB ) + return false; + + return PlayerInfoNodeCompare( nodeRefA->GetChild(), nodeRefB->GetChild() ); +}; + +plVaultPlayerInfoListNode::plVaultPlayerInfoListNode() +{ + ISetType( plVault::kNodeType_PlayerInfoList ); +} + +plVaultPlayerInfoListNode::~plVaultPlayerInfoListNode() +{ +} + +bool plVaultPlayerInfoListNode::HasPlayer( UInt32 playerID ) +{ + plVaultPlayerInfoNode templateNode; + templateNode.SetPlayerID( playerID ); + plVaultNodeRef * tmp; + return FindNode( &templateNode, tmp ); +} + +bool plVaultPlayerInfoListNode::AddPlayer( UInt32 playerID ) +{ + plVaultPlayerInfoNode templateNode; + templateNode.SetPlayerID( playerID ); + plVaultNodeRef * tmp; + if ( !FindNode( &templateNode, tmp ) ) + return LinkToNode( &templateNode ); + return true; +} + +void plVaultPlayerInfoListNode::RemovePlayer( UInt32 playerID ) +{ + plVaultPlayerInfoNode templateNode; + templateNode.SetPlayerID( playerID ); + plVaultNodeRef * tmp; + if ( FindNode( &templateNode, tmp ) ) + RemoveNode( tmp->GetChildID() ); +} + +plVaultPlayerInfoNode * plVaultPlayerInfoListNode::GetPlayer( UInt32 playerID ) +{ + plVaultPlayerInfoNode templateNode; + templateNode.SetPlayerID( playerID ); + plVaultNodeRef * tmp; + if ( FindNode( &templateNode, tmp ) ) + return plVaultPlayerInfoNode::ConvertNoRef( tmp->GetChild() ); + return nil; +} + + +void plVaultPlayerInfoListNode::SetPlayers( const plVault::IDVec & playerIDs ) +{ + RemoveAllNodes(); + for ( int i=0; iGetChildID() ); +} + +//////////////////////////////////////////////////////////////////// +plVaultMgrNode::plVaultMgrNode() +: fMyInboxFolder( nil ), fSystemNode(nil) +{ +} + +plVaultMgrNode::~plVaultMgrNode() +{ +} + +plVaultFolderNode * plVaultMgrNode::GetInbox( void ) const +{ + if ( fMyInboxFolder ) + return fMyInboxFolder; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kInboxFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fMyInboxFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + return fMyInboxFolder; +} + +plVaultSystemNode * plVaultMgrNode::GetSystemNode( ) const +{ + if ( fSystemNode ) + return fSystemNode; + plVaultSystemNode tmp; + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fSystemNode = plVaultSystemNode::ConvertNoRef( nodeRef->GetChild() ); + return fSystemNode; +} + +//////////////////////////////////////////////////////////////////// + +plVaultPlayerNode::plVaultPlayerNode() +: fPlayerInfo( nil ) +, fAvatarOutfitFolder( nil ) +, fAvatarClosetFolder( nil ) +, fChronicleFolder( nil ) +, fIgnoreListFolder( nil ) +, fBuddyListFolder( nil ) +, fPeopleIKnowAboutFolder( nil ) +, fAgeJournalsFolder( nil ) +, fAgesICanVisitFolder( nil ) +, fAgesIOwnFolder( nil ) +, fInviteFolder( nil ) +{ + ISetType( plVault::kNodeType_VNodeMgrPlayer ); +} + +plVaultPlayerNode::~plVaultPlayerNode() +{ +} + +plVaultPlayerInfoNode * plVaultPlayerNode::GetPlayerInfo() const +{ + static plVaultPlayerInfoNode s_nilNode; + if ( fPlayerInfo && fPlayerInfo != &s_nilNode) + return fPlayerInfo; + fPlayerInfo = nil; + plVaultPlayerInfoNode tmp; + tmp.SetPlayerID( GetID() ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fPlayerInfo = plVaultPlayerInfoNode::ConvertNoRef( nodeRef->GetChild() ); + if (!fPlayerInfo) { + hsLogEntry(plSafeGetLog()->AddLineF( "fPlayerInfo node not found" )); + fPlayerInfo = &s_nilNode; + } + return fPlayerInfo; +} + +plVaultFolderNode * plVaultPlayerNode::GetAvatarOutfitFolder( void ) const +{ + static plVaultFolderNode s_nilNode; + if ( fAvatarOutfitFolder && fAvatarOutfitFolder != &s_nilNode) + return fAvatarOutfitFolder; + fAvatarOutfitFolder = nil; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kAvatarOutfitFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fAvatarOutfitFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fAvatarOutfitFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fAvatarOutfitFolder node not found" )); + fAvatarOutfitFolder = &s_nilNode; + } + return fAvatarOutfitFolder; +} + +plVaultFolderNode * plVaultPlayerNode::GetAvatarClosetFolder( void ) const +{ + static plVaultFolderNode s_nilNode; + if ( fAvatarClosetFolder && fAvatarClosetFolder != &s_nilNode) + return fAvatarClosetFolder; + fAvatarClosetFolder = nil; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kAvatarClosetFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fAvatarClosetFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fAvatarClosetFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fAvatarClosetFolder node not found" )); + fAvatarClosetFolder = &s_nilNode; + } + return fAvatarClosetFolder; +} + +plVaultFolderNode * plVaultPlayerNode::GetChronicleFolder( void ) const +{ + static plVaultFolderNode s_nilNode; + if ( fChronicleFolder && fChronicleFolder != &s_nilNode) + return fChronicleFolder; + fChronicleFolder = nil; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kChronicleFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fChronicleFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fChronicleFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fChronicleFolder node not found" )); + fChronicleFolder = &s_nilNode; + } + return fChronicleFolder; +} + +plVaultFolderNode * plVaultPlayerNode::GetAgeJournalsFolder( void ) const +{ + static plVaultFolderNode s_nilNode; + if ( fAgeJournalsFolder && fAgeJournalsFolder != &s_nilNode) + return fAgeJournalsFolder; + fAgeJournalsFolder = nil; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kAgeJournalsFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fAgeJournalsFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fAgeJournalsFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fAgeJournalsFolder node not found" )); + fAgeJournalsFolder = &s_nilNode; + } + return fAgeJournalsFolder; +} + +plVaultFolderNode * plVaultPlayerNode::GetAgesICanVisitFolder( void ) const +{ + static plVaultFolderNode s_nilNode; + if ( fAgesICanVisitFolder && fAgesICanVisitFolder != &s_nilNode) + return fAgesICanVisitFolder; + fAgesICanVisitFolder = nil; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kAgesICanVisitFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fAgesICanVisitFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fAgesICanVisitFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fAgesICanVisitFolder node not found" )); + fAgesICanVisitFolder = &s_nilNode; + } + return fAgesICanVisitFolder; +} + +plVaultFolderNode * plVaultPlayerNode::GetAgesIOwnFolder( void ) const +{ + static plVaultFolderNode s_nilNode; + if ( fAgesIOwnFolder && fAgesIOwnFolder != &s_nilNode) + return fAgesIOwnFolder; + fAgesIOwnFolder = nil; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kAgesIOwnFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fAgesIOwnFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fAgesIOwnFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fAgesIOwnFolder node not found" )); + fAgesIOwnFolder = &s_nilNode; + } + return fAgesIOwnFolder; +} + +plVaultPlayerInfoListNode * plVaultPlayerNode::GetIgnoreListFolder( void ) const +{ + static plVaultPlayerInfoListNode s_nilNode; + if ( fIgnoreListFolder && fIgnoreListFolder != &s_nilNode) + return fIgnoreListFolder; + fIgnoreListFolder = nil; + plVaultPlayerInfoListNode tmp; + tmp.SetFolderType( plVault::kIgnoreListFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fIgnoreListFolder = plVaultPlayerInfoListNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fIgnoreListFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fIgnoreListFolder node not found" )); + fIgnoreListFolder = &s_nilNode; + } + return fIgnoreListFolder; +} + +plVaultPlayerInfoListNode * plVaultPlayerNode::GetBuddyListFolder( void ) const +{ + static plVaultPlayerInfoListNode s_nilNode; + if ( fBuddyListFolder && fBuddyListFolder != &s_nilNode) + return fBuddyListFolder; + fBuddyListFolder = nil; + plVaultPlayerInfoListNode tmp; + tmp.SetFolderType( plVault::kBuddyListFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fBuddyListFolder = plVaultPlayerInfoListNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fBuddyListFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fBuddyListFolder node not found" )); + fBuddyListFolder = &s_nilNode; + } + return fBuddyListFolder; +} + +plVaultPlayerInfoListNode * plVaultPlayerNode::GetPeopleIKnowAboutFolder( void ) const +{ + static plVaultPlayerInfoListNode s_nilNode; + if ( fPeopleIKnowAboutFolder && fPeopleIKnowAboutFolder != &s_nilNode) + return fPeopleIKnowAboutFolder; + fPeopleIKnowAboutFolder = nil; + plVaultPlayerInfoListNode tmp; + tmp.SetFolderType( plVault::kPeopleIKnowAboutFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fPeopleIKnowAboutFolder = plVaultPlayerInfoListNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fPeopleIKnowAboutFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fPeopleIKnowAboutFolder node not found" )); + fPeopleIKnowAboutFolder = &s_nilNode; + } + return fPeopleIKnowAboutFolder; +} + +plVaultFolderNode * plVaultPlayerNode::GetInviteFolder( void ) const +{ + static plVaultFolderNode s_nilNode; + if ( fInviteFolder && fInviteFolder != &s_nilNode) + return fInviteFolder; + fInviteFolder = nil; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kPlayerInviteFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fInviteFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fInviteFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fInviteFolder node not found" )); + fInviteFolder = &s_nilNode; + } + return fInviteFolder; +} + +////// + +plVaultAgeLinkNode * plVaultPlayerNode::GetLinkToMyNeighborhood() const +{ + plAgeInfoStruct info; + info.SetAgeFilename( kNeighborhoodAgeFilename ); + return GetOwnedAgeLink( &info ); +} + + +plVaultAgeLinkNode * plVaultPlayerNode::GetLinkToCity() const +{ + plAgeInfoStruct info; + info.SetAgeFilename( kCityAgeFilename ); + return GetOwnedAgeLink( &info ); +} + +////// + +plVaultAgeLinkNode * plVaultPlayerNode::IFindLink( plVaultFolderNode * folder, const plAgeInfoStruct * info ) const +{ + plVaultNodeIterator it = folder->GetIterator(); + + for ( it; it; ++it ) + { + plVaultAgeLinkNode * linkNode = plVaultAgeLinkNode::ConvertNoRef( it->GetChild() ); + if ( !linkNode ) + continue; + plVaultAgeInfoNode * infoNode = linkNode->GetAgeInfo(); + if ( !infoNode ) + continue; + if ( info->IsEqualTo( infoNode->AsAgeInfoStruct() ) ) + return linkNode; + } + + return nil; +} + +void plVaultPlayerNode::IRemoveLink( plVaultFolderNode * folder, const plUUID * guid ) +{ + plVaultNodeIterator it = folder->GetIterator(); + + for ( it; it; ++it ) + { + plVaultAgeLinkNode * linkNode = plVaultAgeLinkNode::ConvertNoRef( it->GetChild() ); + if ( !linkNode ) + continue; + plVaultAgeInfoNode * infoNode = linkNode->GetAgeInfo(); + if ( !infoNode ) + continue; + if ( infoNode->GetAgeInstanceGuid()->IsEqualTo( guid ) ) + { + folder->RemoveNode( linkNode->GetID() ); + } + } +} + +/////////// + +plVaultAgeLinkNode * plVaultPlayerNode::GetOwnedAgeLink( const plAgeInfoStruct * info, bool skipVolatile/*=false */) const +{ + GetAgesIOwnFolder(); + hsAssert( fAgesIOwnFolder, "Missing AgesIOwn folder" ); + plAgeInfoStruct tmp; + if ( info->HasAgeFilename() ) + tmp.SetAgeFilename( info->GetAgeFilename() ); + else if ( info->HasAgeInstanceGuid() ) + tmp.SetAgeInstanceGuid( info->GetAgeInstanceGuid() ); + else + tmp.CopyFrom( info ); + plVaultAgeLinkNode * result = IFindLink( fAgesIOwnFolder, &tmp ); + + if ( result && result->GetVolatile() && skipVolatile ) + return nil; + + return result; +} + + +void plVaultPlayerNode::RemoveOwnedAgeLink( const plUUID * guid ) +{ + GetAgesIOwnFolder(); + hsAssert( fAgesIOwnFolder, "Missing AgesIOwn folder" ); + IRemoveLink( fAgesIOwnFolder, guid ); +} + +/////////// + +plVaultAgeLinkNode * plVaultPlayerNode::GetVisitAgeLink( const plAgeInfoStruct * info ) const +{ + GetAgesICanVisitFolder(); + hsAssert( fAgesICanVisitFolder, "Missing AgesICanVisit folder" ); + plAgeInfoStruct tmp; + if ( info->HasAgeInstanceGuid() ) + tmp.SetAgeInstanceGuid( info->GetAgeInstanceGuid() ); + else + tmp.CopyFrom( info ); + return IFindLink( fAgesICanVisitFolder, &tmp ); +} + +void plVaultPlayerNode::RemoveVisitAgeLink( const plUUID * guid ) +{ + GetAgesICanVisitFolder(); + hsAssert( fAgesICanVisitFolder, "Missing AgesICanVisit folder" ); + IRemoveLink( fAgesICanVisitFolder, guid ); +} + +/////////// + +plVaultChronicleNode * plVaultPlayerNode::FindChronicleEntry( const char * entryName ) +{ + GetChronicleFolder(); + hsAssert( fChronicleFolder, "Missing Chronicle folder" ); + plVaultChronicleNode templateNode; + templateNode.SetName( entryName ); + plVaultNodeRef * tmp; + if ( fChronicleFolder->FindNode( &templateNode, tmp ) ) + return plVaultChronicleNode::ConvertNoRef( tmp->GetChild() ); + return nil; +} + + +/////////// + +void plVaultPlayerNode::SetAccountUUID( const plUUID * v ) +{ + if ( fMyNodeMgr ) + CheckIAmSuperUserOr_VoidReturn( fMyNodeMgr, ( !IsStored()||fMyNodeMgr->IIsThisMe( this ) ) ); + fAcctUUID.CopyFrom( v ); + ISetIString64_2( fAcctUUID.AsStdString().c_str() ); +} + +const plUUID * plVaultPlayerNode::GetAccountUUID( void ) const +{ + if ( fAcctUUID.IsNull() ) + fAcctUUID.FromString( IGetIString64_2() ); + return &fAcctUUID; +} + +void plVaultPlayerNode::SetPlayerName( const char * v ) +{ + if ( fMyNodeMgr ) + CheckIAmSuperUserOr_VoidReturn( fMyNodeMgr, ( !IsStored()||fMyNodeMgr->IIsThisMe( this ) ) ); + ISetIString64_1( v ); + plVaultPlayerInfoNode * playerInfo = GetPlayerInfo(); + if ( playerInfo ) + playerInfo->SetPlayerName( v ); +} + +void plVaultPlayerNode::SetAvatarShapeName( const char * v ) +{ + if ( fMyNodeMgr ) + CheckIAmSuperUserOr_VoidReturn( fMyNodeMgr, ( !IsStored()||fMyNodeMgr->IIsThisMe( this ) ) ); + ISetString64_1( v ); +} + +void plVaultPlayerNode::Save( plVaultOperationCallback * cb, UInt32 cbContext ) +{ + if ( GetPlayerInfo() ) + GetPlayerInfo()->Save(); + plVaultNode::Save( cb, cbContext ); +} + + +std::string plVaultPlayerNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%s%s[ID:%lu,Nam:%s,Av:%s:Acct:%s] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), GetID(), + GetPlayerName(), GetAvatarShapeName(), GetAccountUUID()->AsStdString().c_str(), + plVaultMgrNode::AsStdString().c_str() ); + return result; +} + + +//////////////////////////////////////////////////////////////////// + +plVaultAgeNode::plVaultAgeNode() +: fAgeInfo( nil ) +, fAgesIOwnFolder( nil ) +, fAgeDevicesFolder( nil ) +, fSubAgesFolder( nil ) +, fPeopleIKnowAboutFolder( nil ) +, fPublicAgesFolder( nil ) +, fChronicleFolder( nil ) +{ + ISetType( plVault::kNodeType_VNodeMgrAge ); +} + +plVaultAgeNode::~plVaultAgeNode() +{ +} + +plVaultAgeLinkNode * plVaultAgeNode::IGetLink( plVaultFolderNode * folder, const plAgeInfoStruct * info ) const +{ + plVaultNodeIterator it = folder->GetIterator(); + + for ( it; it; ++it ) + { + plVaultAgeLinkNode * linkNode = plVaultAgeLinkNode::ConvertNoRef( it->GetChild() ); + if ( !linkNode ) + continue; + plVaultAgeInfoNode * infoNode = linkNode->GetAgeInfo(); + if ( !infoNode ) + continue; + if ( info->HasAgeInstanceGuid() && infoNode->GetAgeInstanceGuid()->IsEqualTo( info->GetAgeInstanceGuid() ) ) + return linkNode; + if ( info->HasAgeInstanceName() && stricmp( infoNode->GetAgeInstanceName(), info->GetAgeInstanceName() )==0 ) + return linkNode; + if ( info->HasAgeFilename() && stricmp( infoNode->GetAgeFilename(), info->GetAgeFilename() )==0 ) + return linkNode; + } + + return nil; +} + + +void plVaultAgeNode::SetAgeGuid( const plUUID * guid ) +{ + fAgeGuid.CopyFrom( guid ); + ISetString64_1( guid->AsStdString().c_str() ); + if ( GetAgeInfo() ) + GetAgeInfo()->SetAgeInstanceGuid( guid ); +} + +void plVaultAgeNode::Save( plVaultOperationCallback * cb/* =nil */, UInt32 cbContext/* =0 */) +{ + if ( GetAgeInfo() ) + GetAgeInfo()->Save(); + plVaultNode::Save( cb, cbContext ); +} + +plVaultAgeInfoNode * plVaultAgeNode::GetAgeInfo() const +{ + if ( fAgeInfo ) + return fAgeInfo; + plVaultAgeInfoNode tmp; + tmp.SetAgeID( GetID() ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fAgeInfo = plVaultAgeInfoNode::ConvertNoRef( nodeRef->GetChild() ); + return fAgeInfo; +} + +plVaultFolderNode * plVaultAgeNode::GetAgeDevicesFolder( void ) const +{ + if ( fAgeDevicesFolder ) + return fAgeDevicesFolder; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kAgeDevicesFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fAgeDevicesFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fAgeDevicesFolder ) + hsLogEntry(plSafeGetLog()->AddLineF( "fAgeDevicesFolder node not found" )); + return fAgeDevicesFolder; +} + +plVaultFolderNode * plVaultAgeNode::GetSubAgesFolder( void ) const +{ + if ( fSubAgesFolder ) + return fSubAgesFolder; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kSubAgesFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fSubAgesFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fSubAgesFolder ) + hsLogEntry(plSafeGetLog()->AddLineF( "fSubAgesFolder node not found" )); + return fSubAgesFolder; +} + +plVaultPlayerInfoListNode * plVaultAgeNode::GetPeopleIKnowAboutFolder( void ) const +{ + if ( fPeopleIKnowAboutFolder ) + return fPeopleIKnowAboutFolder; + plVaultPlayerInfoListNode tmp; + tmp.SetFolderType( plVault::kPeopleIKnowAboutFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fPeopleIKnowAboutFolder = plVaultPlayerInfoListNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fPeopleIKnowAboutFolder ) + hsLogEntry(plSafeGetLog()->AddLineF( "fPeopleIKnowAboutFolder node not found" )); + return fPeopleIKnowAboutFolder; +} + +plVaultFolderNode * plVaultAgeNode::GetChronicleFolder( void ) const +{ + static plVaultFolderNode s_nilNode; + if ( fChronicleFolder && fChronicleFolder != &s_nilNode) + return fChronicleFolder; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kChronicleFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fChronicleFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fChronicleFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fChronicleFolder node not found" )); + fChronicleFolder = &s_nilNode; + } + return fChronicleFolder; +} + +plVaultFolderNode * plVaultAgeNode::GetAgesIOwnFolder( void ) const +{ + static plVaultFolderNode s_nilNode; + if ( fAgesIOwnFolder && fAgesIOwnFolder != &s_nilNode) + return fAgesIOwnFolder; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kAgesIOwnFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fAgesIOwnFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fAgesIOwnFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fAgesIOwnFolder node not found" )); + fAgesIOwnFolder = &s_nilNode; + } + return fAgesIOwnFolder; +} + +plVaultFolderNode * plVaultAgeNode::GetPublicAgesFolder( void ) const +{ + static plVaultFolderNode s_nilNode; + if ( fPublicAgesFolder && fPublicAgesFolder != &s_nilNode) + return fPublicAgesFolder; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kPublicAgesFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fPublicAgesFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + if ( !fPublicAgesFolder ) { + hsLogEntry(plSafeGetLog()->AddLineF( "fPublicAgesFolder node not found" )); + fPublicAgesFolder = &s_nilNode; + } + return fPublicAgesFolder; +} + +// Add a new device. +void plVaultAgeNode::AddDevice( const char * deviceName, + plVaultOperationCallback * cb /*=nil*/, UInt32 cbContext /*=0*/ ) +{ + plVaultTextNoteNode * device = GetDevice( deviceName ); + if ( !device && fMyNodeMgr) + { + device = plVaultTextNoteNode::ConvertNoRef( fMyNodeMgr->CreateNode( plVault::kNodeType_TextNote, kNewPersistentNodeOptions( fMyNodeMgr ) ) ); + device->SetTitle( deviceName ); + device->SetNoteType( plVault::kNoteType_Device ); + GetAgeDevicesFolder()->AddNode( device, cb, cbContext ); + } + else + { + if ( cb ) + { + cb->fNode = device; + cb->VaultOperationStarted( cbContext ); + cb->VaultOperationComplete( cbContext, device->IsStored()?hsOK:hsFail ); + } + } +} + +// Remove a device. +void plVaultAgeNode::RemoveDevice( const char * deviceName ) +{ + plVaultTextNoteNode * device = GetDevice( deviceName ); + if ( device ) + GetAgeDevicesFolder()->RemoveNode( device->GetID() ); +} + +// True if device exists in age. +bool plVaultAgeNode::HasDevice( const char * deviceName ) +{ + return ( GetDevice( deviceName )!=nil ); +} + +plVaultTextNoteNode * plVaultAgeNode::GetDevice( const char * deviceName ) +{ + GetAgeDevicesFolder(); + hsAssert( fAgeDevicesFolder, "Missing AgeDevicesFolder" ); + plVaultTextNoteNode tmp; + tmp.SetTitle( deviceName ); + tmp.SetNoteType( plVault::kNoteType_Device ); + plVaultNodeRef * nodeRef; + if ( fAgeDevicesFolder->FindNode( &tmp, nodeRef ) ) + return plVaultTextNoteNode::ConvertNoRef( nodeRef->GetChild() ); + hsLogEntry(plSafeGetLog()->AddLineF( "Device %s not found", deviceName )); + return nil; +} + + +plVaultAgeLinkNode * plVaultAgeNode::GetSubAgeLink( const plAgeInfoStruct * info ) const +{ + GetSubAgesFolder(); + hsAssert( fSubAgesFolder, "Missing SubAges folder" ); + return IGetLink( fSubAgesFolder, info ); +} + +plVaultChronicleNode * plVaultAgeNode::FindChronicleEntry( const char * entryName ) +{ + GetChronicleFolder(); + hsAssert( fChronicleFolder, "Missing Chronicle folder" ); + plVaultChronicleNode templateNode; + templateNode.SetName( entryName ); + plVaultNodeRef * tmp; + if ( fChronicleFolder->FindNode( &templateNode, tmp ) ) + return plVaultChronicleNode::ConvertNoRef( tmp->GetChild() ); + return nil; +} + +std::string plVaultAgeNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%s%s[Guid:%s] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), + GetAgeGuid()->AsStdString().c_str(), + plVaultNode::AsStdString().c_str() ); + return result; +} + + +//////////////////////////////////////////////////////////////////// + +plVaultAdminNode::plVaultAdminNode() +: fAllAgeSDLEventInboxesFolder(nil) +{ + ISetType( plVault::kNodeType_VNodeMgrAdmin ); +} + +plVaultAdminNode::~plVaultAdminNode() +{ +} + +std::string plVaultAdminNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%s%s[] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), + plVaultNode::AsStdString().c_str() ); + return result; +} + +//////////////////////////////////////////////////////////////////// + +plVaultMarkerNode::plVaultMarkerNode() +{ + ISetType(plVault::kNodeType_Marker); +} + +plVaultMarkerNode::~plVaultMarkerNode() +{ +} + +void plVaultMarkerNode::SetPosition(const hsPoint3& pos) +{ + // To be efficient, write our floats as UInt32's, so we don't have to allocate + // a blob or some crap like that. + UInt32 x = *((UInt32*)&pos.fX); + UInt32 y = *((UInt32*)&pos.fY); + UInt32 z = *((UInt32*)&pos.fZ); + + ISetUInt32_1(x); + ISetUInt32_2(y); + ISetUInt32_3(z); +} + +hsPoint3 plVaultMarkerNode::GetPosition() const +{ + UInt32 uX = IGetUInt32_1(); + UInt32 uY = IGetUInt32_2(); + UInt32 uZ = IGetUInt32_3(); + + float x = *((float*)&uX); + float y = *((float*)&uY); + float z = *((float*)&uZ); + + return hsPoint3(x, y, z); +} + +std::string plVaultMarkerNode::AsStdString(int level) const +{ + std::string result; + std::string space( level, ' ' ); + hsPoint3 pos = GetPosition(); + xtl::format( result, "%s%s[Text:%s Pos: %.1f,%.1f,%.1f GPS: %d,%d,%d] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), + GetText(), pos.fX, pos.fY, pos.fZ, GetGPSTorans(),GetGPSHSpans(),GetGPSVSpans(), plVaultNode::AsStdString().c_str() ); + return result; +} + +//////////////////////////////////////////////////////////////////// + +plVaultMarkerListNode::plVaultMarkerListNode() +{ + ISetType(plVault::kNodeType_MarkerList); +} + +plVaultMarkerListNode::~plVaultMarkerListNode() +{ +} + +std::string plVaultMarkerListNode::AsStdString(int level) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%s%s[Owner:%s[%d], GameType: %d, RoundLength: %d] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), + GetOwnerName(),GetOwnerID(),GetGameType(),GetRoundLength(), plVaultNode::AsStdString().c_str() ); + return result; +} + +//////////////////////////////////////////////////////////////////// + +plVaultSystemNode::plVaultSystemNode() +: fGlobalInboxFolder( nil ) +{ + ISetType( plVault::kNodeType_System ); +} + +plVaultFolderNode * plVaultSystemNode::GetGlobalInbox( void ) const +{ + if ( fGlobalInboxFolder ) + return fGlobalInboxFolder; + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kGlobalInboxFolder ); + plVaultNodeRef * nodeRef; + if ( FindNode( &tmp, nodeRef ) ) + fGlobalInboxFolder = plVaultFolderNode::ConvertNoRef( nodeRef->GetChild() ); + return fGlobalInboxFolder; +} + + +std::string plVaultSystemNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%s%s[CCRAway:%d] %s", + space.c_str(), plVault::NodeTypeStr( GetType() ), + GetCCRAwayStatus(), + plVaultNode::AsStdString().c_str() ); + return result; +} + +//////////////////////////////////////////////////////////////////// + +plNetVaultServerNode::plNetVaultServerNode() +: fCityInfo( nil ) +, fAllPlayersFolderID( 0 ) +, fAllAgeGlobalSDLNodesFolderID( 0 ) +, fPublicAgesFolderID( 0 ) +{ + ISetType( plVault::kNodeType_VNodeMgrServer ); +} + +plNetVaultServerNode::~plNetVaultServerNode() +{ +} + +std::string plNetVaultServerNode::AsStdString( int level ) const +{ + std::string result; + std::string space( level, ' ' ); + xtl::format( result, "%sVAULT[] %s", + space.c_str(), plVaultNode::AsStdString().c_str() ); + return result; +} + + +// End. + + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNode.h new file mode 100644 index 00000000..6e930dc9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNode.h @@ -0,0 +1,1013 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#if 0 +#ifndef plVaultNode_h_inc +#define plVaultNode_h_inc + +#include "hsTypes.h" +#include "hsBitVector.h" +#include "../plUnifiedTime/plUnifiedTime.h" +#include "../plNetCommon/plNetServerSessionInfo.h" +#include "../plNetCommon/plSpawnPointInfo.h" +#include "../plNetCommon/plNetCommon.h" +#include "../plUUID/plUUID.h" +#include "plVault.h" +#include "plVaultNodeIterator.h" +#include +#include + +//////////////////////////////////////////////////////////////////// + +class plVaultNode; +class plVaultFolderNode; +class plVaultImageNode; +class plVaultTextNoteNode; +class plVaultSDLNode; +class plVaultAgeLinkNode; +class plVaultChronicleNode; +class plVaultMgrNode; +class plVaultPlayerNode; +class plVaultPlayerInfoNode; +class plVaultPlayerInfoListNode; +class plVaultAgeNode; +class plVaultAgeInfoNode; +class plVaultAgeInfoListNode; +class plVaultSystemNode; +class plStateDataRecord; +class plURL; + +struct hsPoint3; + + +//////////////////////////////////////////////////////////////////// + +typedef bool (*plNodeCompareProc)( const plVaultNode * A, const plVaultNode * B ); + + +class plVaultNode +{ +public: + struct MatchesNode + { + const plVaultNode * fNode; + MatchesNode( const plVaultNode * node ): fNode( node ) {} + bool operator()( const plVaultNode * node ) const; + }; + friend struct MatchesNode; + + void SetID( UInt32 v ); + void SetType( UInt8 v ); + void SetPermissions( UInt32 v ); + void SetOwnerNodeID( UInt32 v ); + void SetGroupNodeID( UInt32 v ); + void SetCreatorNodeID( UInt32 v ); + void SetCreateTime( const plUnifiedTime * v ); + void SetCreateTime( const plUnifiedTime & v ); + void SetCreateAgeTime( const plUnifiedTime * v ); + void SetCreateAgeTime( const plUnifiedTime & v ); + void SetCreateAgeName( const char * v ); + void SetCreateAgeGuid( const plUUID * v ); + void SetCreateAgeGuid( const plUUID & v ); + void SetInt32_1( Int32 v ); + void SetInt32_2( Int32 v ); + void SetInt32_3( Int32 v ); + void SetInt32_4( Int32 v ); + void SetUInt32_1( UInt32 v ); + void SetUInt32_2( UInt32 v ); + void SetUInt32_3( UInt32 v ); + void SetUInt32_4( UInt32 v ); + void SetString64_1( const char * v ); + void SetString64_2( const char * v ); + void SetString64_3( const char * v ); + void SetString64_4( const char * v ); + void SetString64_5( const char * v ); + void SetString64_6( const char * v ); + void SetIString64_1( const char * v ); + void SetIString64_2( const char * v ); + void SetText_1( const char * v ); + void SetText_2( const char * v ); + void SetBlob_1_Guid( const plUUID * v ); + void SetBlob_2_Guid( const plUUID * v ); + void * AllocBufferBlob_1( int size ); + void * AllocBufferBlob_2( int size ); + + UInt32 GetID () const; + UInt8 GetType () const; + UInt32 GetPermissions () const; + UInt32 GetOwnerNodeID () const; + const plVaultPlayerInfoNode * GetOwnerNode () const; + UInt32 GetGroupNodeID () const; + const plVaultNode * GetGroupNode () const; + const plUnifiedTime * GetModifyTime () const; + UInt32 GetCreatorNodeID () const; + const plVaultPlayerInfoNode * GetCreatorNode () const; + const plUnifiedTime * GetCreateTime () const; + const plUnifiedTime * GetCreateAgeTime () const; + const char * GetCreateAgeName () const; + const plUUID * GetCreateAgeGuid () const; + Int32 GetInt32_1 () const; + Int32 GetInt32_2 () const; + Int32 GetInt32_3 () const; + Int32 GetInt32_4 () const; + UInt32 GetUInt32_1 () const; + UInt32 GetUInt32_2 () const; + UInt32 GetUInt32_3 () const; + UInt32 GetUInt32_4 () const; + const char * GetString64_1 () const; + const char * GetString64_2 () const; + const char * GetString64_3 () const; + const char * GetString64_4 () const; + const char * GetString64_5 () const; + const char * GetString64_6 () const; + const char * GetIString64_1 () const; + const char * GetIString64_2 () const; + const char * GetText_1 () const; + const char * GetText_2 () const; + std::string & GetBlob_1 (); + const std::string & GetBlob_1 () const; + void * GetBufferBlob_1 () const; + int GetBufferSizeBlob_1 () const; + const plUUID * GetBlob_1_Guid () const; + std::string & GetBlob_2 (); + const std::string & GetBlob_2 () const; + void * GetBufferBlob_2 () const; + int GetBufferSizeBlob_2 () const; + const plUUID * GetBlob_2_Guid () const; + +public: + plVaultNode(); + virtual ~plVaultNode(); + + bool IsADownstreamNode( UInt32 nodeID ) const; + + + ///////////////////////////////////////////////// + // Vault Node API + +#if 0 + // Add child node + plVaultNodeRef * AddNode( + plVaultNode * node, + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); + // Find matching node on server and add it to this node. + // Optionally don't fetch children of this node. + bool LinkToNode( + const plVaultNode * templateNode, + int childFetchLevel=plVault::kFetchAllChildren, + bool allowCreate=true, + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); + bool LinkToNode( + UInt32 nodeID, + int childFetchLevel=plVault::kFetchAllChildren, + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); + // Remove child node + bool RemoveNode( const plVaultNode * node, + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); + bool RemoveNode( UInt32 nodeID, + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); + // Remove all child nodes + void RemoveAllNodes( void ); + // Add/Save this node to vault + virtual void Save( + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); + // Save this node and all child nodes that need saving. + // NOTE: Currently, the cb object is called back for + // each node saved. + void SaveAll( + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); + // Send this node to the destination client node. will be received in it's inbox folder. + void SendTo( + UInt32 destClientNodeID, + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); +#endif + + // Get an iterator to this node's children + plVaultNodeIterator GetIterator( void ); + + +#if 0 + // Get child node count + int GetNodeCount( void ) const { return fChildNodes.size(); } + // Get all child nodes. + void GetNodes( plVault::NodeRefVec & out ); + // Get child node by ID + bool GetNode( UInt32 nodeID, plVaultNodeRef *& out ) const; + // Get child node matching template node + bool FindNode( const plVaultNode * templateNode, plVaultNodeRef *& out ) const; + // Get all matching child nodes + bool FindNodes( const plVaultNode * templateNode, plVault::NodeRefVec & out ) const; + // Get first matching node recursively among children + bool FindNodeRecurse( const plVaultNode * templateNode, plVaultNodeRef *& out ) const; + // Returns true if node is a child of this node + bool HasNode( UInt32 nodeID ); + bool HasNode( const plVaultNode * templateNode ); + + // true if node needs saving + bool Modified( void ) const; + void SetModified( bool b ); + + // Get the client ID from my Vault client. + UInt32 GetClientID( void ) const; + + // application data keyed to this node. not stored. + void SetUserData( UInt32 v ) { fUserData=v; } + UInt32 GetUserData( void ) const { return fUserData; } +#endif + + ///////////////////////////////////////////////// + +#if 0 + void Read( hsStream * s, hsResMgr * mgr ); + void Write( hsStream * s, hsResMgr * mgr ); + virtual void CopyFrom( const plVaultNode * other, bool forceCopyAllFields=false ); + + virtual std::string AsStdString( int level=0 ) const; + std::string FieldsAsStdString( void ) const; + void Dump( int level=0, bool displaySeen=false, bool beenSeen=true, plStatusLog * log=nil ) const; + void GetChildNodes( const plVault::NodeRefVec *& out ) const { out=&fChildNodes; } + + virtual std::string AsHtmlForm( const char * action ) { return ""; } + bool FromHtmlPost( plURL & url ) { return false; } +#endif + +private: + plVaultNode( const plVaultNode & ); + plVaultNode & operator =( const plVaultNode & ); +}; + + +//============================================================================ +//============================================================================ +//============================================================================ +//============================================================================ +#if 0 + +//////////////////////////////////////////////////////////////////// + +class plVaultFolderNode : public plVaultNode +{ +public: + enum FieldMap + { + kFolderType = kInt32_1, + kFolderName = kString64_1, + }; + + plVaultFolderNode(); + ~plVaultFolderNode(); + CLASSNAME_REGISTER( plVaultFolderNode ); + GETINTERFACE_ANY( plVaultFolderNode, plVaultNode ); + + void SetFolderType( int type ) { ISetInt32_1( type ); } + int GetFolderType( void ) const { return IGetInt32_1();} + void SetFolderName( const char * v ) { ISetString64_1( v ); } + const char * GetFolderName( void ) const{ return IGetString64_1();} + + std::string AsStdString( int level=0 ) const; + + std::string AsHtmlForm( const char * action ); +}; + +//////////////////////////////////////////////////////////////////// + +class plMipmap; +class plVaultImageNode : public plVaultNode +{ + friend class plNetClientVNodeMgr; + + plMipmap * fMipmap; + void ISetMipmap( plMipmap * v ) { fMipmap=v; } + plMipmap * IGetMipmap( void ) const { return fMipmap; } + +public: + enum FieldMap + { + kImageType = kInt32_1, + kImageTitle = kString64_1, + kImageData = kBlob_1, + }; + + enum ImageTypes { kJPEG=1 }; + + plVaultImageNode(); + ~plVaultImageNode(); + CLASSNAME_REGISTER( plVaultImageNode ); + GETINTERFACE_ANY( plVaultImageNode, plVaultNode ); + + const plMipmap * GetMipmap( void ) const { return fMipmap; } + // this override copies the mipmap ptr too. + void CopyFrom( const plVaultNode * other, bool forceCopyAllFields=false ); + + void SetImageType( int type ) { ISetInt32_1( type ); } + int GetImageType( void ) const { return IGetInt32_1(); } + void * AllocBuffer( int size ) { return IAllocBufferBlob_1( size ); } + void * GetBuffer( void ) const { return IGetBufferBlob_1(); } + int GetBufSize() const { return IGetBufferSizeBlob_1(); } + + void SetTitle( const char * text ) { ISetString64_1( text ); } + const char * GetTitle( void ) const { return IGetString64_1(); } + + std::string AsStdString( int level=0 ) const; +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultTextNoteNode : public plVaultNode +{ +public: + enum FieldMap + { + kNoteType = kInt32_1, + kNoteSubType = kInt32_2, + kNoteTitle = kString64_1, + kNoteText = kBlob_1, + }; + + plVaultTextNoteNode(); + ~plVaultTextNoteNode(); + CLASSNAME_REGISTER( plVaultTextNoteNode ); + GETINTERFACE_ANY( plVaultTextNoteNode, plVaultNode ); + + void SetText( const char * text ); + const char * GetText( void ) const { return IGetBlob_1().c_str(); } + + void SetTitle( const char * text ) { ISetString64_1( text ); } + const char * GetTitle( void ) const { return IGetString64_1(); } + + void SetNoteType( Int32 type ) { ISetInt32_1( type ); } + Int32 GetNoteType( void ) const { return IGetInt32_1(); } + + void SetNoteSubType( Int32 type ) { ISetInt32_2( type ); } + Int32 GetNoteSubType( void ) const { return IGetInt32_2(); } + + // Device-specific: + plVaultFolderNode * GetDeviceInbox() const; + void SetDeviceInbox( const char * inboxName, + plVaultOperationCallback * cb=nil, UInt32 cbContext=0 ); + + std::string AsStdString( int level=0 ) const; +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultSDLNode : public plVaultNode +{ + mutable plStateDataRecord * fSDLDataRec; // for GetStateDataRecord() + +public: + enum FieldMap + { + kName = kString64_1, + kIdent = kInt32_1, // pgmr-defined, from plVault::StandardNodes enum. + kSDLData = kBlob_1, + }; + + plVaultSDLNode(); + ~plVaultSDLNode(); + CLASSNAME_REGISTER( plVaultSDLNode ); + GETINTERFACE_ANY( plVaultSDLNode, plVaultNode ); + + void SetIdent( int v ) { ISetInt32_1( v ); } + int GetIdent() const { return IGetInt32_1(); } + + void * AllocBuffer( int size ) { return IAllocBufferBlob_1( size ); } + void * GetBuffer( void ) const { return IGetBufferBlob_1(); } + int GetBufSize() const { return IGetBufferSizeBlob_1(); } + + plStateDataRecord * GetStateDataRecord( UInt32 readOptions=0 ) const; // returned pointer will be valid until next call to this fxn, or until this node instance goes away. + void SetStateDataRecord( const plStateDataRecord * rec, UInt32 writeOptions=0 ); + void InitStateDataRecord( const char * sdlRecName, UInt32 writeOptions=0 ); + void DumpStateDataRecord( const char * msg=nil, bool dirtyOnly=false ) const; + + // override to dump debug info. + void Save( + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); + + std::string AsStdString( int level=0 ) const; +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultAgeInfoNode : public plVaultNode +{ + mutable plVaultPlayerInfoListNode * fCanVisitFolder; + mutable plVaultPlayerInfoListNode * fAgeOwnersFolder; + mutable plVaultSDLNode * fAgeSDL; + mutable plVaultFolderNode * fChildAgesFolder; + + mutable plUUID fAgeInstanceGuid; + mutable plNetServerSessionInfo fServerInfo; + mutable plAgeInfoStruct fAgeInfoStruct; + + ///////////////////////////////// + friend class pyVault; + friend class pyAgeVault; + friend class plVaultAgeInfoInitializationTask; + plVaultSDLNode * IGetAgeSDL() const; + plVaultAgeLinkNode * IGetLink( plVaultFolderNode * folder, const plAgeInfoStruct * info ) const; + ///////////////////////////////// + +public: + enum FieldMap + { + kAgeFilename = kString64_1, // "Garden" + kAgeInstanceName = kString64_2, // "Eder Kemo" + kAgeUserDefinedName = kString64_3, // "Joe's" + kAgeInstanceGuid = kString64_4, + kAgeDescription = kText_1, // "Stay out!" + kAgeSequenceNumber = kInt32_1, + kIsPublic = kInt32_2, + kAgeLanguage = kInt32_3, // The language of the client that made this age + kAgeID = kUInt32_1, + kAgeCzarID = kUInt32_2, + kAgeInfoFlags = kUInt32_3, + }; + + enum AgeFlagBits // 32 bits max + { + kIsStartupNeighborhood = 1<<0, + }; + + plVaultAgeInfoNode(); + ~plVaultAgeInfoNode(); + + CLASSNAME_REGISTER( plVaultAgeInfoNode ); + GETINTERFACE_ANY( plVaultAgeInfoNode, plVaultNode ); + + plVaultPlayerInfoListNode * GetCanVisitFolder() const; + plVaultPlayerInfoListNode * GetAgeOwnersFolder() const; + plVaultFolderNode * GetChildAgesFolder( void ) const; + plVaultAgeLinkNode * GetChildAgeLink( const plAgeInfoStruct * info ) const; + const plVaultSDLNode * GetAgeSDL() const { return IGetAgeSDL(); } + plVaultSDLNode * GetAgeSDL() { return IGetAgeSDL(); } + plVaultPlayerInfoNode * GetCzar() const; + + const char * GetAgeFilename() const { return IGetString64_1(); } + void SetAgeFilename( const char * v ) { ISetString64_1( v ); } + + const char * GetAgeInstanceName() const { return IGetString64_2(); } + void SetAgeInstanceName( const char * v ) { ISetString64_2( v ); } + + const char * GetAgeUserDefinedName() const { return IGetString64_3(); } + void SetAgeUserDefinedName( const char * v ) { ISetString64_3( v ); } + + const plUUID * GetAgeInstanceGuid() const { fAgeInstanceGuid.FromString( IGetString64_4() ); return &fAgeInstanceGuid; } + void SetAgeInstanceGuid( const plUUID * guid ) { fAgeInstanceGuid.CopyFrom( guid ); ISetString64_4( fAgeInstanceGuid.AsString() ); } + + const char * GetAgeDescription() const { return IGetText_1(); } + void SetAgeDescription( const char * v ) { ISetText_1( v ); } + + Int32 GetSequenceNumber() const { return IGetInt32_1(); } + void SetSequenceNumber( Int32 v ) { ISetInt32_1( v ); } + + UInt32 GetAgeID() const { return IGetUInt32_1(); } + void SetAgeID( UInt32 v ) { ISetUInt32_1( v ); } + + // aka mayor + UInt32 GetCzarID() const { return IGetUInt32_2(); } + void SetCzarID( UInt32 v ) { ISetUInt32_2( v ); } + + bool IsPublic() const { return ( IGetInt32_2()!=0 ); } + // no glue for this one. Use plVaultAgeMgr::SetAgePublic() instead. + void ISetPublic( bool v ) { ISetInt32_2( v ); } + + Int32 GetAgeLanguage() const { return IGetInt32_3(); } + void SetAgeLanguage( Int32 v ) { ISetInt32_3( v ); } + + bool GetAgeFlag( UInt32 bit ) const { return ( IGetUInt32_3()&bit )!=0; } + void SetAgeFlag( UInt32 bit, bool on=true ) + { if ( on ) ISetUInt32_3( IGetUInt32_3()|bit ); + else ISetUInt32_3( IGetUInt32_3()&~bit ); + } + + // helpers for linking. + const plNetServerSessionInfo * AsServerInfo() const; + const plAgeInfoStruct * AsAgeInfoStruct() const; + // helpers for init + void FromAgeInfoStruct( const plAgeInfoStruct * ageInfo ); + // other helpers + const char * GetDisplayName() const; + + // debug + std::string AsStdString( int level=0 ) const; +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultAgeInfoListNode : public plVaultFolderNode +{ +public: + enum FieldMap + { + }; + + plVaultAgeInfoListNode(); + ~plVaultAgeInfoListNode(); + CLASSNAME_REGISTER( plVaultAgeInfoListNode ); + GETINTERFACE_ANY( plVaultAgeInfoListNode, plVaultFolderNode ); + bool HasAge( UInt32 AgeID ); + bool AddAge( UInt32 AgeID ); + void RemoveAge( UInt32 AgeID ); +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultAgeLinkNode : public plVaultNode +{ + mutable plVaultAgeInfoNode * fAgeInfo; + +public: + enum FieldMap + { + kLocked = kInt32_1, // locked on psnl bookshelf + kVolatile = kInt32_2, // volatile on psnl bookshelf + kSpawnPoints = kBlob_1, // aka treasure stones. + }; + + plVaultAgeLinkNode(); + ~plVaultAgeLinkNode(); + CLASSNAME_REGISTER( plVaultAgeLinkNode ); + GETINTERFACE_ANY( plVaultAgeLinkNode, plVaultNode ); + + plVaultAgeInfoNode * GetAgeInfo() const; + void SetAgeInfo( plVaultAgeInfoNode * v ); + // locked on psnl age bookshelf + void SetLocked( bool v ) { ISetInt32_1( v?1:0 ); } + bool GetLocked() const { return ( IGetInt32_1()!=0 ); } + // volatile on psnl age bookshelf + void SetVolatile( bool v ) { ISetInt32_2( v?1:0 ); } + bool GetVolatile() const { return ( IGetInt32_2()!=0 ); } + // spawn points + void AddSpawnPoint( const plSpawnPointInfo & point ); // will only add if not there already. + void RemoveSpawnPoint( const char * spawnPtName ); + bool HasSpawnPoint( const char * spawnPtName ) const; + bool HasSpawnPoint( const plSpawnPointInfo & point ) const; // compares spawn name only, not title. + void GetSpawnPoints( plSpawnPointVec & out ) const; + void SetSpawnPoints( const plSpawnPointVec & in ); + + std::string AsStdString( int level=0 ) const; +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultChronicleNode : public plVaultNode +{ +public: + enum FieldMap + { + kEntryType = kInt32_1, + kEntryName = kString64_1, + kEntryValue = kText_1, + }; + + plVaultChronicleNode(); + ~plVaultChronicleNode(); + CLASSNAME_REGISTER( plVaultChronicleNode ); + GETINTERFACE_ANY( plVaultChronicleNode, plVaultNode ); + + void SetName( const char * text ) { ISetString64_1( text ); } + const char * GetName( void ) const { return IGetString64_1(); } + void SetValue( const char * text ) { ISetText_1( text ); } + const char * GetValue( void ) const { return IGetText_1(); } + void SetEntryType( UInt32 type ) { ISetInt32_1( type ); } + UInt32 GetEntryType( void ) const { return IGetInt32_1(); } + + std::string AsStdString( int level=0 ) const; +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultPlayerInfoNode : public plVaultNode +{ + mutable plUUID fGuid; // used with GetAgeGuid() only. + +public: + enum FieldMap + { + kPlayerID = kUInt32_1, + kPlayerName = kIString64_1, + kAgeInstanceName = kString64_1, // name of age player is currently in + kAgeInstanceGuid = kString64_2, // guid of age player is currently in + kOnline = kInt32_1, // whether or not player is online + }; + + plVaultPlayerInfoNode(); + ~plVaultPlayerInfoNode(); + CLASSNAME_REGISTER( plVaultPlayerInfoNode ); + GETINTERFACE_ANY( plVaultPlayerInfoNode, plVaultNode ); + + // player ID + void SetPlayerID( UInt32 v ); + UInt32 GetPlayerID( void ) const { return IGetUInt32_1(); } + // player name + void SetPlayerName( const char * text ); + const char * GetPlayerName( void ) const { return IGetIString64_1(); } + // age the player is currently in, if any. + void SetAgeInstanceName( const char * v ); + const char * GetAgeInstanceName( void ) const; + void SetAgeGuid( const plUUID * v); + const plUUID * GetAgeGuid( void ) const; + // online status + void SetOnline( bool b ); + bool IsOnline( void ) const { return IGetInt32_1()!=0; } + + std::string AsStdString( int level=0 ) const; +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultPlayerInfoListNode : public plVaultFolderNode +{ + void ISortPlayers( plNodeRefCompareProc proc ); + void ILocalNodeAdded( plVaultNodeRef * nodeRef ); + +public: + enum FieldMap + { + }; + + plVaultPlayerInfoListNode(); + ~plVaultPlayerInfoListNode(); + CLASSNAME_REGISTER( plVaultPlayerInfoListNode ); + GETINTERFACE_ANY( plVaultPlayerInfoListNode, plVaultFolderNode ); + bool HasPlayer( UInt32 playerID ); + bool AddPlayer( UInt32 playerID ); + void RemovePlayer( UInt32 playerID ); + plVaultPlayerInfoNode * GetPlayer( UInt32 playerID ); + void SetPlayers( const plVault::IDVec & playerIDs ); + void SortBy( plNodeRefCompareProc proc ) { ISortPlayers( proc ); } + void Sort(); +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultMgrNode : public plVaultNode +{ + friend class plVaultServer; + friend class plVaultConnectTask; + friend class plVNodeMgrInitializationTask; + + mutable plVaultFolderNode * fMyInboxFolder; + mutable plVaultSystemNode * fSystemNode; +public: + enum FieldMap + { + }; + + plVaultMgrNode(); + ~plVaultMgrNode(); + CLASSNAME_REGISTER( plVaultMgrNode ); + GETINTERFACE_ANY( plVaultMgrNode, plVaultNode ); + plVaultFolderNode * GetInbox( void ) const; + plVaultSystemNode * GetSystemNode() const; +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultPlayerNode : public plVaultMgrNode +{ + mutable plVaultPlayerInfoNode * fPlayerInfo; + mutable plVaultFolderNode * fAvatarOutfitFolder; + mutable plVaultFolderNode * fAvatarClosetFolder; + mutable plVaultFolderNode * fChronicleFolder; + mutable plVaultFolderNode * fAgeJournalsFolder; + mutable plVaultPlayerInfoListNode * fIgnoreListFolder; + mutable plVaultPlayerInfoListNode * fBuddyListFolder; + mutable plVaultPlayerInfoListNode * fPeopleIKnowAboutFolder; + mutable plVaultFolderNode * fAgesICanVisitFolder; + mutable plVaultFolderNode * fAgesIOwnFolder; + mutable plVaultFolderNode * fInviteFolder; + mutable plUUID fAcctUUID; + + plVaultAgeLinkNode * IFindLink( plVaultFolderNode * folder, const plAgeInfoStruct * info ) const; + void IRemoveLink( plVaultFolderNode * folder, const plUUID * guid ); + +public: + enum FieldMap + { + kPlayerName = kIString64_1, + kAvatarShapeName = kString64_1, + kDisabled = kInt32_2, + kOnlineTime = kUInt32_2, // seconds + kAccountUUID = kIString64_2, + }; + + plVaultPlayerNode(); + ~plVaultPlayerNode(); + CLASSNAME_REGISTER( plVaultPlayerNode ); + GETINTERFACE_ANY( plVaultPlayerNode, plVaultMgrNode ); + + plVaultPlayerInfoNode * GetPlayerInfo( void ) const; + plVaultFolderNode * GetAvatarOutfitFolder( void ) const; + plVaultFolderNode * GetAvatarClosetFolder( void ) const; + plVaultFolderNode * GetChronicleFolder( void ) const; + plVaultFolderNode * GetAgeJournalsFolder( void ) const; + plVaultPlayerInfoListNode * GetIgnoreListFolder( void ) const; + plVaultPlayerInfoListNode * GetBuddyListFolder( void ) const; + plVaultPlayerInfoListNode * GetPeopleIKnowAboutFolder( void ) const; + plVaultFolderNode * GetAgesICanVisitFolder( void ) const; + plVaultFolderNode * GetAgesIOwnFolder( void ) const; + plVaultFolderNode * GetInviteFolder( void ) const; + + plVaultAgeLinkNode * GetLinkToMyNeighborhood() const; + plVaultAgeLinkNode * GetLinkToCity() const; + + /////////////// + // Owned ages + plVaultAgeLinkNode * GetOwnedAgeLink( const plAgeInfoStruct * info, bool skipVolatile=false ) const; + void RemoveOwnedAgeLink( const plUUID * guid ); + // Visit ages + plVaultAgeLinkNode * GetVisitAgeLink( const plAgeInfoStruct * info ) const; + void RemoveVisitAgeLink( const plUUID * guid ); + /////////////// + // Chronicle + plVaultChronicleNode * FindChronicleEntry( const char * entryName ); + + // player name + void SetPlayerName( const char * v ); + const char * GetPlayerName( void ) const { return IGetIString64_1(); } + // avatar shape name + void SetAvatarShapeName( const char * v ); + const char * GetAvatarShapeName( void ) const{return IGetString64_1(); } + // disabled? + void SetDisabled( bool v ) { ISetInt32_2( v ); } + bool IsDisabled( void ) const { return IGetInt32_2()!=0; } + // account ID + void SetAccountUUID( const plUUID * v ); + const plUUID * GetAccountUUID( void ) const; + // online time accumulator (plUnifiedTime.GetSecs()) + void SetOnlineTime( UInt32 v ) { ISetUInt32_2( v ); } + UInt32 GetOnlineTime( void ) const { return IGetUInt32_2(); } + void IncOnlineTime( UInt32 v ) { SetOnlineTime( GetOnlineTime()+v ); } + + // override to also save player info node. + void Save( + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); + + std::string AsStdString( int level=0 ) const; +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultAgeNode : public plVaultMgrNode +{ + mutable plUUID fAgeGuid; + + mutable plVaultAgeInfoNode * fAgeInfo; + mutable plVaultFolderNode * fAgeDevicesFolder; + mutable plVaultFolderNode * fSubAgesFolder; + mutable plVaultPlayerInfoListNode * fPeopleIKnowAboutFolder; + mutable plVaultFolderNode * fChronicleFolder; + // used only with personal ages + mutable plVaultFolderNode * fAgesIOwnFolder; + // used only with nexus age + mutable plVaultFolderNode * fPublicAgesFolder; + + plVaultAgeLinkNode * IGetLink( plVaultFolderNode * folder, const plAgeInfoStruct * info ) const; + +public: + enum FieldMap + { + kAgeGuid = kString64_1, + }; + + plVaultAgeNode(); + ~plVaultAgeNode(); + CLASSNAME_REGISTER( plVaultAgeNode ); + GETINTERFACE_ANY( plVaultAgeNode, plVaultMgrNode ); + + plVaultAgeInfoNode * GetAgeInfo() const; + + plVaultFolderNode * GetAgeDevicesFolder( void ) const; + plVaultFolderNode * GetSubAgesFolder( void ) const; + // age chronicle + plVaultFolderNode * GetChronicleFolder( void ) const; + // People who have published to devices in this age + plVaultPlayerInfoListNode * GetPeopleIKnowAboutFolder( void ) const; + + // PERSONAL AGE SPECIFIC + plVaultFolderNode * GetAgesIOwnFolder( void ) const; + + // NEXUS AGE SPECIFIC + plVaultFolderNode * GetPublicAgesFolder( void ) const; + + // To publish a node to a device, get its device inbox ID, then + // call node->SendTo( deviceInboxID ); + plVaultAgeLinkNode * GetSubAgeLink( const plAgeInfoStruct * info ) const; + + // AGE DEVICES. AKA IMAGERS, WHATEVER. + // Add a new device. + void AddDevice( const char * deviceName, + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); + // Remove a device. + void RemoveDevice( const char * deviceName ); + // True if device exists in age. + bool HasDevice( const char * deviceName ); + // Get a device. + plVaultTextNoteNode * GetDevice( const char * deviceName ); + + // age guid + const plUUID * GetAgeGuid() const { fAgeGuid.FromString( IGetString64_1() ); return &fAgeGuid; } + void SetAgeGuid( const plUUID * guid ); + + // Age chronicle + plVaultChronicleNode * FindChronicleEntry( const char * entryName ); + + // override to also save age info node. + void Save( + plVaultOperationCallback * cb=nil, + UInt32 cbContext=0 ); + + std::string AsStdString( int level=0 ) const; +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultAdminNode : public plVaultMgrNode +{ + mutable plVaultFolderNode * fAllAgeSDLEventInboxesFolder; + +public: + enum FieldMap + { + }; + + plVaultAdminNode(); + ~plVaultAdminNode(); + CLASSNAME_REGISTER( plVaultAdminNode ); + GETINTERFACE_ANY( plVaultAdminNode, plVaultMgrNode ); + + plVaultFolderNode * GetAllAgeSDLEventInboxesFolder( void ) const; + + std::string AsStdString( int level=0 ) const; +}; + + +//////////////////////////////////////////////////////////////////// + +class plVaultMarkerNode : public plVaultNode +{ +public: + enum FieldMap + { + kAgeName = kCreateAgeName, + kMarkerText = kText_1, + kGPSTorans = kInt32_1, + kGPSHSpans = kInt32_2, + kGPSVSpans = kInt32_3, + kMarkerPosX = kUInt32_1, + kMarkerPosY = kUInt32_2, + kMarkerPosZ = kUInt32_3, + }; + + plVaultMarkerNode(); + ~plVaultMarkerNode(); + CLASSNAME_REGISTER(plVaultMarkerNode); + GETINTERFACE_ANY(plVaultMarkerNode, plVaultNode); + + void SetAge(const char* ageName) { ISetCreateAgeName(ageName); } + const char* GetAge() const { return IGetCreateAgeName(); } + + void SetPosition(const hsPoint3& pos); + hsPoint3 GetPosition() const; + + void SetText(const char* text) { ISetText_1(text); } + const char* GetText() const { return IGetText_1(); } + + void SetGPS(Int32 t, Int32 h, Int32 v) { ISetInt32_1(t);ISetInt32_2(h);ISetInt32_3(v); } + Int32 GetGPSTorans() const { return IGetInt32_1(); } + Int32 GetGPSHSpans() const { return IGetInt32_2(); } + Int32 GetGPSVSpans() const { return IGetInt32_3(); } + + std::string AsStdString(int level=0) const; +}; + +//////////////////////////////////////////////////////////////////// + +class plVaultMarkerListNode : public plVaultFolderNode +{ +public: + enum FieldMap + { + kOwnerID = kUInt32_1, + kOwnerName = kString64_2, + kGameType = kInt32_1, + kRoundLength = kInt32_2, + }; + + plVaultMarkerListNode(); + ~plVaultMarkerListNode(); + + CLASSNAME_REGISTER(plVaultMarkerListNode); + GETINTERFACE_ANY(plVaultMarkerListNode, plVaultFolderNode); + + void SetOwnerName( const char * v ) { ISetString64_2( v ); } + const char * GetOwnerName( void ) const{ return IGetString64_2();} + + Int32 GetGameType() const { return IGetInt32_1(); } + void SetGameType( Int32 v ) { ISetInt32_1( v ); } + + Int32 GetRoundLength() const { return IGetInt32_2(); } + void SetRoundLength( Int32 v ) { ISetInt32_2( v ); } + + std::string AsStdString(int level=0) const; + +}; + +//////////////////////////////////////////////////////////////////// + +// +// A node that ALL vault mgrs get - for system wide info +// +class plVaultSystemNode : public plVaultNode +{ + mutable plVaultFolderNode * fGlobalInboxFolder; + +public: + enum FieldMap + { + kCCRAwayStatus = kInt32_1 + }; + + plVaultSystemNode(); + CLASSNAME_REGISTER( plVaultSystemNode); + GETINTERFACE_ANY( plVaultSystemNode, plVaultNode ); + + void SetCCRAwayStatus( bool b) { ISetInt32_1( b); } + int GetCCRAwayStatus( void ) const { return IGetInt32_1();} + + plVaultFolderNode * GetGlobalInbox( void ) const; + + std::string AsStdString( int level=0 ) const; +}; + +//////////////////////////////////////////////////////////////////// +// Vault Server VNode +// Makes sure nodes like AllPlayers exist. + +class plNetVaultServerNode : public plVaultMgrNode +{ + friend class plNetVaultServerInitializationTask; + + mutable plVaultAgeInfoNode * fCityInfo; + UInt32 fAllPlayersFolderID; + UInt32 fAllAgeGlobalSDLNodesFolderID; + UInt32 fPublicAgesFolderID; + +public: + plNetVaultServerNode(); + ~plNetVaultServerNode(); + CLASSNAME_REGISTER( plNetVaultServerNode ); + GETINTERFACE_ANY( plNetVaultServerNode, plVaultMgrNode ); + + UInt32 GetAllPlayersFolderID() const { return fAllPlayersFolderID; } + UInt32 GetAllAgeGlobalSDLNodesFolderID() const { return fAllAgeGlobalSDLNodesFolderID; } + UInt32 GetPublicAgesFolderID() const { return fPublicAgesFolderID; } + + std::string AsStdString( int level=0 ) const; +}; + +#endif // 0 +//============================================================================ +//============================================================================ +//============================================================================ +//============================================================================ + + +#endif // plVaultNode_h_inc + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNodeAccess.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNodeAccess.cpp new file mode 100644 index 00000000..7e7f9889 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNodeAccess.cpp @@ -0,0 +1,1073 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNodeAccess.cpp +* +***/ + +#include "Pch.h" +#pragma hdrstop + +//============================================================================ +// Volatile Vault Node Fields - be very careful when adding to this +//============================================================================ +struct NodeTypeToVolatileField { + unsigned nodeType; + qword volatileFields; +}; + +NodeTypeToVolatileField volatileFieldList[] = { + {plVault::kNodeType_PlayerInfo, VaultPlayerInfoNode::kOnline | VaultPlayerInfoNode::kAgeInstName | VaultPlayerInfoNode::kAgeInstUuid}, + {0, 0} +}; + +//============================================================================ +qword GetNodeVolatileFields(NetVaultNode* node) { + qword volatileFields = 0; + unsigned index = 0; + + while (volatileFieldList[index].nodeType != 0) { + if (node->nodeType == volatileFieldList[index].nodeType) { + volatileFields |= volatileFieldList[index].volatileFields; + break; + } + + ++index; + } + + return volatileFields; +} + +/***************************************************************************** +* +* NetVaultNodeAccess +* +***/ + +//============================================================================ +NetVaultNodeAccess::NetVaultNodeAccess (NetVaultNode * node) +: base(node) +, fieldFlags(node->fieldFlags) +, dirtyFlags(node->dirtyFlags) +{ } + + +/***************************************************************************** +* +* VaultPlayerNode +* +***/ + +//============================================================================ +VaultPlayerNode::VaultPlayerNode (NetVaultNode * node) +: NetVaultNodeAccess(node) +, playerName(node->istring64_1) +, avatarShapeName(node->string64_1) +, disabled(node->int32_1) +, explorer(node->int32_2) +, onlineTime(node->uint32_1) +, accountUuid(node->uuid_1) +, inviteUuid(node->uuid_2) +{ +} + +//============================================================================ +void VaultPlayerNode::SetPlayerName (const wchar v[]) { + IVaultNodeSetString(kPlayerName, base, &playerName, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void VaultPlayerNode::SetAvatarShapeName (const wchar v[]) { + IVaultNodeSetString(kAvatarShapeName, base, &avatarShapeName, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void VaultPlayerNode::SetDisabled (int v) { + IVaultNodeSetValue(kDisabled, base, &disabled, v); +} + +//============================================================================ +void VaultPlayerNode::SetOnlineTime (unsigned v) { + IVaultNodeSetValue(kOnlineTime, base, &onlineTime, v); +} + +//============================================================================ +void VaultPlayerNode::SetAccountUuid (const Uuid & v) { + IVaultNodeSetValue(kAccountUuid, base, &accountUuid, v); +} + +//============================================================================ +void VaultPlayerNode::SetInviteUuid (const Uuid & v) { + IVaultNodeSetValue(kInviteUuid, base, &inviteUuid, v); +} + +//============================================================================ +void VaultPlayerNode::SetExplorer (int v) { + IVaultNodeSetValue(kExplorer, base, &explorer, v); +} + + +/***************************************************************************** +* +* VaultPlayerInfoNode +* +***/ + +//============================================================================ +VaultPlayerInfoNode::VaultPlayerInfoNode (NetVaultNode * node) +: NetVaultNodeAccess(node) +, playerId(node->uint32_1) +, playerName(node->istring64_1) +, ageInstName(node->string64_1) +, ageInstUuid(node->uuid_1) +, online(node->int32_1) +, ccrLevel(node->int32_2) +{ +} + +//============================================================================ +void VaultPlayerInfoNode::SetPlayerId (unsigned v) { + IVaultNodeSetValue(kPlayerId, base, &playerId, v); +} + +//============================================================================ +void VaultPlayerInfoNode::SetPlayerName (const wchar v[]) { + IVaultNodeSetString(kPlayerName, base, &playerName, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void VaultPlayerInfoNode::SetAgeInstName (const wchar v[]) { + IVaultNodeSetString(kAgeInstName, base, &ageInstName, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void VaultPlayerInfoNode::SetAgeInstUuid (const Uuid & v) { + IVaultNodeSetValue(kAgeInstUuid, base, &ageInstUuid, v); +} + +//============================================================================ +void VaultPlayerInfoNode::SetOnline (int v) { + IVaultNodeSetValue(kOnline, base, &online, v); +} + +//============================================================================ +void VaultPlayerInfoNode::SetCCRLevel (int v) { + IVaultNodeSetValue(kCCRLevel, base, &ccrLevel, v); +} + +/***************************************************************************** +* +* VaultFolderNode +* +***/ + +//============================================================================ +VaultFolderNode::VaultFolderNode (NetVaultNode * node) +: NetVaultNodeAccess(node) +, folderType(node->int32_1) +, folderName(node->string64_1) +{ +} + +//============================================================================ +void VaultFolderNode::SetFolderName (const wchar v[]) { + IVaultNodeSetString(kFolderName, base, &folderName, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void VaultFolderNode::SetFolderType (int v) { + IVaultNodeSetValue(kFolderType, base, &folderType, v); +} + + +/***************************************************************************** +* +* VaultPlayerInfoListNode +* +***/ + +//============================================================================ +VaultPlayerInfoListNode::VaultPlayerInfoListNode (NetVaultNode * node) +: VaultFolderNode(node) +{ +} + + +/***************************************************************************** +* +* VaultAgeInfoListNode +* +***/ + +//============================================================================ +VaultAgeInfoListNode::VaultAgeInfoListNode (NetVaultNode * node) +: VaultFolderNode(node) +{ +} + + +/***************************************************************************** +* +* VaultChronicleNode +* +***/ + +//============================================================================ +VaultChronicleNode::VaultChronicleNode (NetVaultNode * node) +: NetVaultNodeAccess(node) +, entryType(node->int32_1) +, entryName(node->string64_1) +, entryValue(node->text_1) +{ +} + +//============================================================================ +void VaultChronicleNode::SetEntryType (int v) { + IVaultNodeSetValue(kEntryType, base, &entryType, v); +} + +//============================================================================ +void VaultChronicleNode::SetEntryName (const wchar v[]) { + IVaultNodeSetString(kEntryName, base, &entryName, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void VaultChronicleNode::SetEntryValue (const wchar v[]) { + IVaultNodeSetString(kEntryValue, base, &entryValue, v, (unsigned)-1); +} + + +/***************************************************************************** +* +* VaultTextNoteNode +* +***/ + +//============================================================================ +VaultTextNoteNode::VaultTextNoteNode (NetVaultNode * node) +: NetVaultNodeAccess(node) +, noteType(node->int32_1) +, noteSubType(node->int32_2) +, noteTitle(node->string64_1) +, noteText(node->text_1) +{ +} + +//============================================================================ +void VaultTextNoteNode::SetNoteType (int v) { + IVaultNodeSetValue(kNoteType, base, ¬eType, v); +} + +//============================================================================ +void VaultTextNoteNode::SetNoteSubType (int v) { + IVaultNodeSetValue(kNoteSubType, base, ¬eSubType, v); +} + +//============================================================================ +void VaultTextNoteNode::SetNoteTitle (const wchar v[]) { + IVaultNodeSetString(kNoteTitle, base, ¬eTitle, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void VaultTextNoteNode::SetNoteText (const wchar v[]) { + IVaultNodeSetString(kNoteText, base, ¬eText, v, (unsigned)-1); +} + +//============================================================================ +enum EAgeInfoFields { + kAgeFilename, + kAgeInstName, + kAgeUserName, + kAgeDesc, + kAgeInstGuid, + kAgeLanguage, + kAgeSequence, + kNumAgeInfoFields +}; + +#ifdef CLIENT +void VaultTextNoteNode::SetVisitInfo (const plAgeInfoStruct & info) { + + ARRAY(wchar) buf; + + for (unsigned i = 0; i < kNumAgeInfoFields; ++i) { + switch (i) { + case kAgeFilename: { + wchar src[128]; + StrToUnicode(src, info.GetAgeFilename(), arrsize(src)); + unsigned len = StrLen(src); + wchar * dst = buf.New(len); + MemCopy(dst, src, len * sizeof(src[0])); + } + break; + + case kAgeInstName: { + wchar src[128]; + StrToUnicode(src, info.GetAgeInstanceName(), arrsize(src)); + unsigned len = StrLen(src); + wchar * dst = buf.New(len); + MemCopy(dst, src, len * sizeof(src[0])); + } + break; + + case kAgeUserName: { + wchar src[128]; + StrToUnicode(src, info.GetAgeUserDefinedName(), arrsize(src)); + unsigned len = StrLen(src); + wchar * dst = buf.New(len); + MemCopy(dst, src, len * sizeof(src[0])); + } + break; + + case kAgeDesc: { + wchar src[128]; + StrToUnicode(src, info.GetAgeDescription(), arrsize(src)); + unsigned len = StrLen(src); + wchar * dst = buf.New(len); + MemCopy(dst, src, len * sizeof(src[0])); + } + break; + + case kAgeInstGuid: { + Uuid guid = (Uuid)*info.GetAgeInstanceGuid(); + wchar src[64]; + GuidToString(guid, src, arrsize(src)); + unsigned len = StrLen(src); + wchar * dst = buf.New(len); + MemCopy(dst, src, len * sizeof(src[0])); + } + break; + + case kAgeLanguage: { + wchar src[32]; + StrPrintf(src, arrsize(src), L"%u", info.GetAgeLanguage()); + unsigned len = StrLen(src); + wchar * dst = buf.New(len); + MemCopy(dst, src, len * sizeof(src[0])); + } + break; + + case kAgeSequence: { + wchar src[32]; + StrPrintf(src, arrsize(src), L"%u", info.GetAgeSequenceNumber()); + unsigned len = StrLen(src); + wchar * dst = buf.New(len); + MemCopy(dst, src, len * sizeof(src[0])); + } + break; + + DEFAULT_FATAL(i); + } + + wchar * sep = buf.New(1); + *sep = L'|'; + } + + wchar * term = buf.New(1); + *term = 0; + + SetNoteText(buf.Ptr()); +} +#endif + +//============================================================================ +#ifdef CLIENT +bool VaultTextNoteNode::GetVisitInfo (plAgeInfoStruct * info) { + + wchar * mem; + const wchar * str = mem = StrDup(noteText); + + for (unsigned i = 0; i < kNumAgeInfoFields; ++i) { + + wchar token[1024]; + switch (i) { + case kAgeFilename: { + StrTokenize(&str, token, arrsize(token), L"|", 1); + if (StrLen(token) > 0) { + char ansi[1024]; + StrToAnsi(ansi, token, arrsize(ansi)); + info->SetAgeFilename(ansi); + } + } + break; + + case kAgeInstName: { + StrTokenize(&str, token, arrsize(token), L"|", 1); + if (StrLen(token) > 0) { + char ansi[1024]; + StrToAnsi(ansi, token, arrsize(ansi)); + info->SetAgeInstanceName(ansi); + } + } + break; + + case kAgeUserName: { + StrTokenize(&str, token, arrsize(token), L"|", 1); + if (StrLen(token) > 0) { + char ansi[1024]; + StrToAnsi(ansi, token, arrsize(ansi)); + info->SetAgeUserDefinedName(ansi); + } + } + break; + + case kAgeDesc: { + StrTokenize(&str, token, arrsize(token), L"|", 1); + if (StrLen(token) > 0) { + char ansi[1024]; + StrToAnsi(ansi, token, arrsize(ansi)); + info->SetAgeDescription(ansi); + } + } + break; + + case kAgeInstGuid: { + StrTokenize(&str, token, arrsize(token), L"|", 1); + if (StrLen(token) > 0) { + Uuid guid; + GuidFromString(token, &guid); + info->SetAgeInstanceGuid(&plUUID(guid)); + } + } + break; + + case kAgeLanguage: { + StrTokenize(&str, token, arrsize(token), L"|", 1); + if (StrLen(token) > 0) { + info->SetAgeLanguage(StrToUnsigned(token, nil, 10)); + } + } + break; + + case kAgeSequence: { + StrTokenize(&str, token, arrsize(token), L"|", 1); + if (StrLen(token) > 0) { + info->SetAgeSequenceNumber(StrToUnsigned(token, nil, 10)); + } + } + break; + + DEFAULT_FATAL(i); + } + } + + FREE(mem); + return true; +} +#endif + + +/***************************************************************************** +* +* VaultSDLNode +* +***/ + +//============================================================================ +VaultSDLNode::VaultSDLNode (NetVaultNode * node) +: NetVaultNodeAccess(node) +, sdlIdent(node->int32_1) +, sdlName(node->string64_1) +, sdlData(node->blob_1) +, sdlDataLen(node->blob_1Length) +{ +} + +//============================================================================ +void VaultSDLNode::SetSdlIdent (int v) { + IVaultNodeSetValue(kSDLIdent, base, &sdlIdent, v); +} + +//============================================================================ +void VaultSDLNode::SetSdlName (const wchar v[]) { + IVaultNodeSetString(kSDLName, base, &sdlName, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +#ifdef CLIENT +bool VaultSDLNode::GetStateDataRecord (plStateDataRecord * rec, unsigned readOptions) { + if (!sdlDataLen || !sdlData) + return false; + + hsRAMStream ram; + ram.Write(sdlDataLen, sdlData); + ram.Rewind(); + + char * sdlRecName = nil; + int sdlRecVersion; + if (!plStateDataRecord::ReadStreamHeader(&ram, &sdlRecName, &sdlRecVersion)) + return false; + + rec->SetDescriptor(sdlRecName, sdlRecVersion); + FREE(sdlRecName); + + // Note: Setting from default here results in a bug causing age SDL to + // be incorrectly shown when immediately linking back to an age you linked + // out of (relto door will be closed, window shut, etc). + // rec->SetFromDefaults(false); + + if (!rec->Read( &ram, 0, readOptions)) + return false; + + // If we converted the record to a newer version, re-save it. + if (rec->GetDescriptor()->GetVersion() != sdlRecVersion) + SetStateDataRecord(rec, readOptions); + + return true; +} +#endif // def CLIENT + +//============================================================================ +#ifdef CLIENT +void VaultSDLNode::SetStateDataRecord (const plStateDataRecord * rec, unsigned writeOptions) { + hsRAMStream ram; + rec->WriteStreamHeader(&ram); + rec->Write(&ram, 0, writeOptions); + ram.Rewind(); + + unsigned bytes = ram.GetEOF(); + byte * buf, * heap = nil; + if (bytes <= 2048) + buf = ALLOCA(byte, bytes); + else + buf = (byte *)ALLOC(bytes); + + ram.CopyToMem(buf); + + IVaultNodeSetBlob(kSDLData, base, &sdlData, &sdlDataLen, buf, bytes); + + FREE(heap); +} +#endif // def CLIENT + +//============================================================================ +#ifdef CLIENT +void VaultSDLNode::InitStateDataRecord (const wchar sdlRecName[], unsigned writeOptions) { + { + plStateDataRecord * rec = NEWZERO(plStateDataRecord); + bool exists = GetStateDataRecord(rec, 0); + DEL(rec); + if (exists) + return; + } + + char aStr[MAX_PATH]; + StrToAnsi(aStr, sdlRecName, arrsize(aStr)); + if (plStateDescriptor * des = plSDLMgr::GetInstance()->FindDescriptor(aStr, plSDL::kLatestVersion)) { + plStateDataRecord rec(des); + rec.SetFromDefaults(false); + SetStateDataRecord(&rec, writeOptions|plSDL::kDontWriteDirtyFlag); + } +} +#endif // def CLIENT + + +/***************************************************************************** +* +* VaultImageNode +* +***/ + +//============================================================================ +VaultImageNode::VaultImageNode (NetVaultNode * node) +: NetVaultNodeAccess(node) +, title(node->string64_1) +, imgType(node->int32_1) +, imgData(node->blob_1) +, imgDataLen(node->blob_1Length) +{ +} + +//============================================================================ +void VaultImageNode::SetImageTitle (const wchar v[]) { + IVaultNodeSetString(kImageTitle, base, &title, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void VaultImageNode::SetImageType (int v) { + IVaultNodeSetValue(kImageType, base, &imgType, v); +} + +//============================================================================ +void VaultImageNode::SetImageData (const byte buffer[], unsigned bytes) { + IVaultNodeSetBlob(kImageData, base, &imgData, &imgDataLen, buffer, bytes); +} + +//============================================================================ +#ifdef CLIENT +void VaultImageNode::StuffImage (plMipmap * src) { + hsRAMStream ramStream; + plJPEG::Instance().SetWriteQuality(30/*percent*/); + if (plJPEG::Instance().WriteToStream(&ramStream, src)) { + unsigned bytes = ramStream.GetEOF(); + byte * buffer = (byte *)ALLOC(bytes); + ramStream.CopyToMem(buffer); + IVaultNodeSetBlob(kImageData, base, &imgData, &imgDataLen, buffer, bytes); + SetImageType(kJPEG); + FREE(buffer); + } + else { + IVaultNodeSetBlob(kImageData, base, &imgData, &imgDataLen, nil, 0); + SetImageType(kNone); + } +} +#endif + +//============================================================================ +#ifdef CLIENT +bool VaultImageNode::ExtractImage (plMipmap ** dst) { + + switch (imgType) { + case kNone: + (*dst) = nil; + return false; + + case kJPEG: { + hsRAMStream ramStream; + ramStream.Write(imgDataLen, imgData); + ramStream.Rewind(); + (*dst) = plJPEG::Instance().ReadFromStream(&ramStream); + } + return ((*dst) != nil); + + DEFAULT_FATAL(imgType); + } +} +#endif + + +/***************************************************************************** +* +* VaultAgeLinkNode +* +***/ + +#ifdef CLIENT +struct MatchesSpawnPointTitle +{ + std::string fTitle; + MatchesSpawnPointTitle( const char * title ):fTitle( title ){} + bool operator ()( const plSpawnPointInfo & p ) const { return ( p.fTitle==fTitle ); } +}; +struct MatchesSpawnPointName +{ + std::string fName; + MatchesSpawnPointName( const char * name ):fName( name ){} + bool operator ()( const plSpawnPointInfo & p ) const { return ( p.fSpawnPt==fName ); } +}; +#endif + +//============================================================================ +VaultAgeLinkNode::VaultAgeLinkNode (NetVaultNode * node) +: NetVaultNodeAccess(node) +, unlocked(node->int32_1) +, volat(node->int32_2) +, spawnPoints(node->blob_1) +, spawnPointsLen(node->blob_1Length) +{ +} + +//============================================================================ +void VaultAgeLinkNode::SetUnlocked (int v) { + IVaultNodeSetValue(kUnlocked, base, &unlocked, v); +} + +//============================================================================ +void VaultAgeLinkNode::SetVolatile (int v) { + IVaultNodeSetValue(kVolatile, base, &volat, v); +} + +//============================================================================ +#ifdef CLIENT +bool VaultAgeLinkNode::CopyTo (plAgeLinkStruct * link) { + if (RelVaultNode * me = VaultGetNodeIncRef(base->nodeId)) { + if (RelVaultNode * info = me->GetChildNodeIncRef(plVault::kNodeType_AgeInfo, 1)) { + VaultAgeInfoNode access(info); + access.CopyTo(link->GetAgeInfo()); + me->DecRef(); + return true; + } + me->DecRef(); + } + link->Clear(); + return false; +} +#endif + +//============================================================================ +#ifdef CLIENT +void VaultAgeLinkNode::AddSpawnPoint (const plSpawnPointInfo & point) { + + plSpawnPointVec points; + GetSpawnPoints( &points ); + if ( std::find_if( points.begin(), points.end(), MatchesSpawnPointTitle( point.fTitle.c_str() ) )!=points.end() ) + return; + + // only check to see if the titles are the same... + //... so we can add the same spawnpoint as long as they have different titles + //if ( std::find_if( points.begin(), points.end(), MatchesSpawnPointName( point.fSpawnPt.c_str() ) )!=points.end() ) + // return; + + points.push_back( point ); + SetSpawnPoints( points ); +} +#endif + +//============================================================================ +#ifdef CLIENT +void VaultAgeLinkNode::RemoveSpawnPoint (const char spawnPtName[]) { + + plSpawnPointVec points; + GetSpawnPoints( &points ); + plSpawnPointVec::iterator it = std::find_if( points.begin(), points.end(), MatchesSpawnPointName( spawnPtName ) ); + while ( it!=points.end() ) + { + points.erase( it ); + SetSpawnPoints( points ); + it = std::find_if( points.begin(), points.end(), MatchesSpawnPointName( spawnPtName ) ); + } +} +#endif + +//============================================================================ +#ifdef CLIENT +bool VaultAgeLinkNode::HasSpawnPoint (const char spawnPtName[]) const { + + plSpawnPointVec points; + GetSpawnPoints( &points ); + return ( std::find_if( points.begin(), points.end(), MatchesSpawnPointName( spawnPtName ) )!=points.end() ); +} +#endif + +//============================================================================ +#ifdef CLIENT +bool VaultAgeLinkNode::HasSpawnPoint (const plSpawnPointInfo & point) const { + + return HasSpawnPoint(point.GetName()); +} +#endif + +//============================================================================ +#ifdef CLIENT +void VaultAgeLinkNode::GetSpawnPoints (plSpawnPointVec * out) const { + + char str[2048]; + ZERO(str); + MemCopy(str, spawnPoints, min(spawnPointsLen, arrsize(str) - 1)); + + char token1[ 1024 ]; + hsStringTokenizer izer1( str, ";" ); + while ( izer1.Next( token1, sizeof( token1 ) ) ) + { + plSpawnPointInfo point; + char token2[ 1024 ]; + hsStringTokenizer izer2( token1, ":" ); + if ( izer2.Next( token2, sizeof( token2 ) ) ) + point.fTitle = token2; + if ( izer2.Next( token2, sizeof( token2 ) ) ) + point.fSpawnPt = token2; + if ( izer2.Next( token2, sizeof( token2 ) ) ) + point.fCameraStack = token2; + + out->push_back(point); + } +} +#endif + +//============================================================================ +#ifdef CLIENT +void VaultAgeLinkNode::SetSpawnPoints (const plSpawnPointVec & in) { + + std::stringstream ss; + for ( unsigned i=0; iuuid_1) +, parentAgeInstUuid(node->uuid_2) +, ageName(node->string64_1) +{ +} + +//============================================================================ +void VaultAgeNode::SetAgeInstGuid (const Uuid & v) { + IVaultNodeSetValue(kAgeInstanceGuid, base, &ageInstUuid, v); +} + +//============================================================================ +void VaultAgeNode::SetParentAgeInstGuid (const Uuid & v) { + IVaultNodeSetValue(kParentAgeInstanceGuid, base, &parentAgeInstUuid, v); +} + +//============================================================================ +void VaultAgeNode::SetAgeName (const wchar v[]) { + IVaultNodeSetString(kAgeName, base, &ageName, v, kMaxVaultNodeStringLength); +} + + +/***************************************************************************** +* +* VaultAgeInfoNode +* +***/ + +//============================================================================ +VaultAgeInfoNode::VaultAgeInfoNode (NetVaultNode * node) +: NetVaultNodeAccess(node) +, ageFilename(node->string64_2) +, ageInstName(node->string64_3) +, ageUserDefinedName(node->string64_4) +, ageInstUuid(node->uuid_1) +, parentAgeInstUuid(node->uuid_2) +, ageSequenceNumber(node->int32_1) +, ageIsPublic(node->int32_2) +, ageLanguage(node->int32_3) +, ageId(node->uint32_1) +, ageCzarId(node->uint32_2) +, ageInfoFlags(node->uint32_3) +, ageDescription(node->text_1) +{ +} + +//============================================================================ +void VaultAgeInfoNode::SetAgeFilename (const wchar v[]) { + IVaultNodeSetString(kAgeFilename, base, &ageFilename, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void VaultAgeInfoNode::SetAgeInstName (const wchar v[]) { + IVaultNodeSetString(kAgeInstanceName, base, &ageInstName, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void VaultAgeInfoNode::SetAgeUserDefinedName (const wchar v[]) { + IVaultNodeSetString(kAgeUserDefinedName, base, &ageUserDefinedName, v, kMaxVaultNodeStringLength); +} + +//============================================================================ +void VaultAgeInfoNode::SetAgeInstGuid (const Uuid & v) { + IVaultNodeSetValue(kAgeInstanceGuid, base, &ageInstUuid, v); +} + +//============================================================================ +void VaultAgeInfoNode::SetParentAgeInstGuid (const Uuid & v) { + IVaultNodeSetValue(kParentAgeInstanceGuid, base, &parentAgeInstUuid, v); +} + +//============================================================================ +void VaultAgeInfoNode::SetAgeSequenceNumber (int v) { + IVaultNodeSetValue(kAgeSequenceNumber, base, &ageSequenceNumber, v); +} + +//============================================================================ +void VaultAgeInfoNode::_SetAgeIsPublic (int v) { + IVaultNodeSetValue(kIsPublic, base, &ageIsPublic, v); +} + +//============================================================================ +void VaultAgeInfoNode::SetAgeLanguage (int v) { + IVaultNodeSetValue(kAgeLanguage, base, &ageLanguage, v); +} + +//============================================================================ +void VaultAgeInfoNode::SetAgeId (unsigned v) { + IVaultNodeSetValue(kAgeId, base, &ageId, v); +} + +//============================================================================ +void VaultAgeInfoNode::SetAgeCzarId (unsigned v) { + IVaultNodeSetValue(kAgeCzarId, base, &ageCzarId, v); +} + +//============================================================================ +void VaultAgeInfoNode::SetAgeInfoFlags (unsigned v) { + IVaultNodeSetValue(kAgeInfoFlags, base, &ageInfoFlags, v); +} + +//============================================================================ +void VaultAgeInfoNode::SetAgeDescription (const wchar v[]) { + IVaultNodeSetString(kAgeDescription, base, &ageDescription, v, (unsigned)-1); +} + +//============================================================================ +#ifdef CLIENT +const class plUnifiedTime * VaultAgeInfoNode::GetAgeTime () const { + hsAssert(false, "eric, implement me."); + return nil; +} +#endif // def CLIENT + +//============================================================================ +#ifdef CLIENT +void VaultAgeInfoNode::CopyFrom (const plAgeInfoStruct * info) { + wchar str[MAX_PATH]; + + // age filename + if (info->HasAgeFilename()) { + StrToUnicode(str, info->GetAgeFilename(), arrsize(str)); + SetAgeFilename(str); + } + else { + SetAgeFilename(nil); + } + + // age instance name + if (info->HasAgeInstanceName()) { + StrToUnicode(str, info->GetAgeInstanceName(), arrsize(str)); + SetAgeInstName(str); + } + else { + SetAgeInstName(nil); + } + + // age user-defined name + if (info->HasAgeUserDefinedName()) { + StrToUnicode(str, info->GetAgeUserDefinedName(), arrsize(str)); + SetAgeUserDefinedName(str); + } + else { + SetAgeUserDefinedName(nil); + } + + // age description + // TODO + if (info->HasAgeDescription()) { +// StrToUnicode(str, info->GetAgeDescription(), arrsize(str)); +// SetAgeDescription(str); + } + else { +// SetAgeDescription(nil); + } + + // age sequence number + SetAgeSequenceNumber(info->GetAgeSequenceNumber()); + + // age instance guid + SetAgeInstGuid((Uuid)*info->GetAgeInstanceGuid()); + + // age language + SetAgeLanguage(info->GetAgeLanguage()); +} +#endif // def CLIENT + +//============================================================================ +#ifdef CLIENT +void VaultAgeInfoNode::CopyTo (plAgeInfoStruct * info) const { + char str[MAX_PATH]; + + // age filename + StrToAnsi(str, ageFilename, arrsize(str)); + info->SetAgeFilename(str); + + // age instance name + StrToAnsi(str, ageInstName, arrsize(str)); + info->SetAgeInstanceName(str); + + // age user-defined name + StrToAnsi(str, ageUserDefinedName, arrsize(str)); + info->SetAgeUserDefinedName(str); + + // age description + // TODO + + // age sequence number + info->SetAgeSequenceNumber(ageSequenceNumber); + + // age instance guid + plUUID uuid(ageInstUuid); + info->SetAgeInstanceGuid(&uuid); + + // age language + info->SetAgeLanguage(ageLanguage); +} +#endif // def CLIENT + + +/***************************************************************************** +* +* VaultSystemNode +* +***/ + +//============================================================================ +VaultSystemNode::VaultSystemNode (NetVaultNode * node) +: NetVaultNodeAccess(node) +, ccrStatus(node->int32_1) +{ +} + +//============================================================================ +void VaultSystemNode::SetCCRStatus (int v) { + IVaultNodeSetValue(kCCRStatus, base, &ccrStatus, v); +} + + +/***************************************************************************** +* +* VaultMarkerGameNode +* +***/ + +//============================================================================ +VaultMarkerGameNode::VaultMarkerGameNode (NetVaultNode * node) +: NetVaultNodeAccess(node) +, gameName(node->text_1) +, gameGuid(node->uuid_1) +{ +} + +//============================================================================ +void VaultMarkerGameNode::SetGameName (const wchar v[]) { + IVaultNodeSetString(kGameName, base, &gameName, v, (unsigned)-1); +} + +//============================================================================ +void VaultMarkerGameNode::SetGameGuid (const Uuid & v) { + IVaultNodeSetValue(kGameGuid, base, &gameGuid, v); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNodeAccess.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNodeAccess.h new file mode 100644 index 00000000..78236ef8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNodeAccess.h @@ -0,0 +1,457 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/***************************************************************************** +* +* $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNodeAccess.h +* +***/ + +#ifdef PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_PLVAULTNODEACCESS_H +#error "Header $/Plasma20/Sources/Plasma/PubUtilLib/plVault/plVaultNodeAccess.h included more than once" +#endif +#define PLASMA20_SOURCES_PLASMA_PUBUTILLIB_PLVAULT_PLVAULTNODEACCESS_H + +// 'Old' system is full of compiler warnings at /W4, so just hide them +#pragma warning(push, 0) +#include "hsStlUtils.h" +#pragma warning(pop) + + +/***************************************************************************** +* +* VaultNode field access strutures +* +***/ + +class plAgeInfoStruct; +class plAgeLinkStruct; +struct plSpawnPointInfo; + +#ifdef CLIENT +typedef std::vector plSpawnPointVec; +#endif + +//============================================================================ +// Volatile Vault Node Fields +//============================================================================ +qword GetNodeVolatileFields(NetVaultNode* node); + +//============================================================================ +// NetVaultNodeAccess +//============================================================================ +struct NetVaultNodeAccess { + NetVaultNode * base; + qword & fieldFlags; + qword & dirtyFlags; + + NetVaultNodeAccess (NetVaultNode * node); + NetVaultNodeAccess (const NetVaultNodeAccess &); // not implemented + const NetVaultNodeAccess & operator= (const NetVaultNodeAccess &); // not implemented +}; + + +//============================================================================ +// VaultPlayerNode +//============================================================================ +struct VaultPlayerNode : NetVaultNodeAccess { + static const qword kPlayerName = NetVaultNode::kIString64_1; + static const qword kAvatarShapeName = NetVaultNode::kString64_1; + static const qword kDisabled = NetVaultNode::kInt32_1; + static const qword kExplorer = NetVaultNode::kInt32_2; // explorer = 1, visitor = 0 + static const qword kOnlineTime = NetVaultNode::kUInt32_1; + static const qword kAccountUuid = NetVaultNode::kUuid_1; + static const qword kInviteUuid = NetVaultNode::kUuid_2; + + // Treat these as read-only or node flag fields will become invalid + // Threaded apps: Must be accessed with node->critsect locked + wchar *& playerName; + wchar *& avatarShapeName; + int & disabled; + unsigned & onlineTime; + Uuid & accountUuid; + Uuid & inviteUuid; + int & explorer; + + VaultPlayerNode (NetVaultNode * node); + VaultPlayerNode (const VaultPlayerNode &); // not implemented + const VaultPlayerNode & operator= (const VaultPlayerNode &); // not implemented + + // Threaded apps: Must be called with node->critsect locked + void SetPlayerName (const wchar v[]); + void SetAvatarShapeName (const wchar v[]); + void SetDisabled (int v); + void SetOnlineTime (unsigned v); + void SetAccountUuid (const Uuid & v); + void SetInviteUuid (const Uuid & v); + void SetExplorer (int v); +}; + + +//============================================================================ +// VaultPlayerInfoNode +//============================================================================ +struct VaultPlayerInfoNode : NetVaultNodeAccess { + static const qword kPlayerId = NetVaultNode::kUInt32_1; + static const qword kPlayerName = NetVaultNode::kIString64_1; + static const qword kAgeInstName = NetVaultNode::kString64_1; // name of age player is currently in + static const qword kAgeInstUuid = NetVaultNode::kUuid_1; // guid of age player is currently in + static const qword kOnline = NetVaultNode::kInt32_1; // whether or not player is online + static const qword kCCRLevel = NetVaultNode::kInt32_2; + + // Treat these as read-only or node flag fields will become invalid + // Threaded apps: Must be accessed with node->critsect locked + unsigned & playerId; + wchar *& playerName; + wchar *& ageInstName; + Uuid & ageInstUuid; + int & online; + int & ccrLevel; + + VaultPlayerInfoNode (NetVaultNode * node); + VaultPlayerInfoNode (const VaultPlayerInfoNode &); // not implemented + const VaultPlayerInfoNode & operator= (const VaultPlayerInfoNode &); // not implemented + + // Threaded apps: Must be called with node->critsect locked + void SetPlayerId (unsigned v); + void SetPlayerName (const wchar v[]); + void SetAgeInstName (const wchar v[]); + void SetAgeInstUuid (const Uuid & v); + void SetOnline (int v); + void SetCCRLevel (int v); +}; + + +//============================================================================ +// VaultFolderNode +//============================================================================ +struct VaultFolderNode : NetVaultNodeAccess { + static const qword kFolderType = NetVaultNode::kInt32_1; + static const qword kFolderName = NetVaultNode::kString64_1; + + // Treat these as read-only or node flag fields will become invalid + // Threaded apps: Must be accessed with node->critsect locked + int & folderType; + wchar *& folderName; + + VaultFolderNode (NetVaultNode * node); + VaultFolderNode (const VaultFolderNode &); // not implemented + const VaultFolderNode & operator= (const VaultFolderNode &); // not implemented + + // Threaded apps: Must be called with node->critsect locked + void SetFolderName (const wchar v[]); + void SetFolderType (int v); +}; + + +//============================================================================ +// VaultPlayerInfoListNode +//============================================================================ +struct VaultPlayerInfoListNode : VaultFolderNode { + + VaultPlayerInfoListNode (NetVaultNode * node); + VaultPlayerInfoListNode (const VaultPlayerInfoListNode &); // not implemented + const VaultPlayerInfoListNode & operator= (const VaultPlayerInfoListNode &); // not implemented +}; + +//============================================================================ +// VaultAgeInfoListNode +//============================================================================ +struct VaultAgeInfoListNode : VaultFolderNode { + + VaultAgeInfoListNode (NetVaultNode * node); + VaultAgeInfoListNode (const VaultAgeInfoListNode &); // not implemented + const VaultAgeInfoListNode & operator= (const VaultAgeInfoListNode &); // not implemented +}; + +//============================================================================ +// VaultChronicleNode +//============================================================================ +struct VaultChronicleNode : NetVaultNodeAccess { + static const qword kEntryType = NetVaultNode::kInt32_1; + static const qword kEntryName = NetVaultNode::kString64_1; + static const qword kEntryValue = NetVaultNode::kText_1; + + // Treat these as read-only or node flag fields will become invalid + // Threaded apps: Must be accessed with node->critsect locked + int & entryType; + wchar *& entryName; + wchar *& entryValue; + + VaultChronicleNode (NetVaultNode * node); + VaultChronicleNode (const VaultChronicleNode &); // not implemented + const VaultChronicleNode & operator= (const VaultChronicleNode &); // not implemented + + // Threaded apps: Must be called with node->critsect locked + void SetEntryType (int v); + void SetEntryName (const wchar v[]); + void SetEntryValue (const wchar v[]); +}; + + +//============================================================================ +// VaultSDLNode +//============================================================================ +struct VaultSDLNode : NetVaultNodeAccess { + static const qword kSDLName = NetVaultNode::kString64_1; + static const qword kSDLIdent = NetVaultNode::kInt32_1; + static const qword kSDLData = NetVaultNode::kBlob_1; + + int & sdlIdent; + wchar *& sdlName; + byte *& sdlData; + unsigned & sdlDataLen; + + VaultSDLNode (NetVaultNode * node); + VaultSDLNode (const VaultSDLNode &); // not implemented + const VaultSDLNode & operator= (const VaultSDLNode &); // not implemented + + void SetSdlIdent (int v); + void SetSdlName (const wchar v[]); + +#ifdef CLIENT + bool GetStateDataRecord (class plStateDataRecord * out, unsigned readOptions = 0); + void SetStateDataRecord (const class plStateDataRecord * rec, unsigned writeOptions = 0); + void InitStateDataRecord (const wchar sdlRecName[], unsigned writeOptions = 0); +#endif // def CLIENT +}; + +//============================================================================ +// VaultAgeLinkNode +//============================================================================ +struct VaultAgeLinkNode : NetVaultNodeAccess { + static const qword kUnlocked = NetVaultNode::kInt32_1; + static const qword kVolatile = NetVaultNode::kInt32_2; + static const qword kSpawnPoints = NetVaultNode::kBlob_1; + + int & unlocked; + int & volat; + byte *& spawnPoints; + unsigned & spawnPointsLen; + + VaultAgeLinkNode (NetVaultNode * node); + VaultAgeLinkNode (const VaultAgeLinkNode &); // not implemented + const VaultAgeLinkNode & operator= (const VaultAgeLinkNode &); // not implemented + + void SetUnlocked (int v); + void SetVolatile (int v); + +#ifdef CLIENT + bool CopyTo (plAgeLinkStruct * link); + void AddSpawnPoint (const plSpawnPointInfo & point); // will only add if not there already. + void RemoveSpawnPoint (const char spawnPtName[]); + bool HasSpawnPoint (const char spawnPtName[]) const; + bool HasSpawnPoint (const plSpawnPointInfo & point) const; // compares spawn name only, not title. + void GetSpawnPoints (plSpawnPointVec * out) const; + void SetSpawnPoints (const plSpawnPointVec & in); +#endif +}; + +//============================================================================ +// VaultImageNode +//============================================================================ +struct VaultImageNode : NetVaultNodeAccess { + + enum ImageTypes { kNone=0, kJPEG=1 }; + + static const qword kImageType = NetVaultNode::kInt32_1; + static const qword kImageTitle = NetVaultNode::kString64_1; + static const qword kImageData = NetVaultNode::kBlob_1; + + wchar *& title; + int & imgType; + byte *& imgData; + unsigned & imgDataLen; + + VaultImageNode (NetVaultNode * node); + VaultImageNode (const VaultImageNode &); // not implemented + const VaultImageNode & operator= (const VaultImageNode &); // not implemented + + void SetImageTitle (const wchar v[]); + void SetImageType (int v); + void SetImageData (const byte buffer[], unsigned bytes); + +#ifdef CLIENT + void StuffImage (class plMipmap * src); + bool ExtractImage (class plMipmap ** dst); +#endif +}; + +//============================================================================ +// VaultCliImageNode +//============================================================================ +#ifdef CLIENT +struct VaultCliImageNode : VaultImageNode { + class plMipmap * fMipmap; + + VaultCliImageNode (NetVaultNode * node); + VaultCliImageNode (const VaultCliImageNode &); // not implemented + const VaultCliImageNode & operator= (const VaultCliImageNode &); // not implemented +}; +#endif // def CLIENT + +//============================================================================ +// VaultTextNoteNode +//============================================================================ +struct VaultTextNoteNode : NetVaultNodeAccess { + + static const qword kNoteType = NetVaultNode::kInt32_1; + static const qword kNoteSubType = NetVaultNode::kInt32_2; + static const qword kNoteTitle = NetVaultNode::kString64_1; + static const qword kNoteText = NetVaultNode::kText_1; + + int & noteType; + int & noteSubType; + wchar *& noteTitle; + wchar *& noteText; + + VaultTextNoteNode (NetVaultNode * node); + VaultTextNoteNode (const VaultTextNoteNode &); // not implemented + const VaultTextNoteNode & operator= (const VaultTextNoteNode &); // not implemented + + void SetNoteType (int v); + void SetNoteSubType (int v); + void SetNoteTitle (const wchar v[]); + void SetNoteText (const wchar v[]); + +#ifdef CLIENT + // for kNoteType_Visit/UnVisit + void SetVisitInfo (const plAgeInfoStruct & info); + bool GetVisitInfo (plAgeInfoStruct * info); +#endif +}; + +//============================================================================ +// VaultAgeNode +//============================================================================ +struct VaultAgeNode : NetVaultNodeAccess { + + static const qword kAgeInstanceGuid = NetVaultNode::kUuid_1; + static const qword kParentAgeInstanceGuid = NetVaultNode::kUuid_2; + static const qword kAgeName = NetVaultNode::kString64_1; + + Uuid & ageInstUuid; + Uuid & parentAgeInstUuid; + wchar *& ageName; + + VaultAgeNode (NetVaultNode * node); + VaultAgeNode (const VaultAgeNode &); // not implemented + const VaultAgeNode & operator= (const VaultAgeNode &); // not implemented + + void SetAgeInstGuid (const Uuid & v); + void SetParentAgeInstGuid (const Uuid & v); + void SetAgeName (const wchar v[]); +}; + +//============================================================================ +// VaultAgeInfoNode +//============================================================================ +struct VaultAgeInfoNode : NetVaultNodeAccess { + + static const qword kAgeFilename = NetVaultNode::kString64_2; // "Garden" + static const qword kAgeInstanceName = NetVaultNode::kString64_3; // "Eder Kemo" + static const qword kAgeUserDefinedName = NetVaultNode::kString64_4; // "Joe's" + static const qword kAgeInstanceGuid = NetVaultNode::kUuid_1; // 6278b081-342a-4229-ac1b-a0b8a2658390 + static const qword kParentAgeInstanceGuid = NetVaultNode::kUuid_2; // 9192be7f-89ef-41bc-83db-79afe451e399 + static const qword kAgeDescription = NetVaultNode::kText_1; // "Stay out!" + static const qword kAgeSequenceNumber = NetVaultNode::kInt32_1; + static const qword kIsPublic = NetVaultNode::kInt32_2; + static const qword kAgeLanguage = NetVaultNode::kInt32_3; // The language of the client that made this age + static const qword kAgeId = NetVaultNode::kUInt32_1; + static const qword kAgeCzarId = NetVaultNode::kUInt32_2; + static const qword kAgeInfoFlags = NetVaultNode::kUInt32_3; + + wchar *& ageFilename; + wchar *& ageInstName; + wchar *& ageUserDefinedName; + Uuid & ageInstUuid; + Uuid & parentAgeInstUuid; + int & ageSequenceNumber; + int & ageIsPublic; + int & ageLanguage; + unsigned & ageId; + unsigned & ageCzarId; + unsigned & ageInfoFlags; + wchar *& ageDescription; + + VaultAgeInfoNode (NetVaultNode * node); + VaultAgeInfoNode (const VaultAgeInfoNode &); // not implemented + const VaultAgeInfoNode & operator= (const VaultAgeInfoNode &); // not implemented + + void SetAgeFilename (const wchar v[]); + void SetAgeInstName (const wchar v[]); + void SetAgeUserDefinedName (const wchar v[]); + void SetAgeInstGuid (const Uuid & v); + void SetParentAgeInstGuid (const Uuid & v); + void SetAgeSequenceNumber (int v); + void _SetAgeIsPublic (int v); // WARNING: Do not call this. The age will not be set public this way. Use NetCliAuthSetAgePublic instead (changes this field's value in the process). + void SetAgeLanguage (int v); + void SetAgeId (unsigned v); + void SetAgeCzarId (unsigned v); + void SetAgeInfoFlags (unsigned v); + void SetAgeDescription (const wchar v[]); + +#ifdef CLIENT + const class plUnifiedTime * GetAgeTime () const; + void CopyFrom (const plAgeInfoStruct * info); + void CopyTo (plAgeInfoStruct * info) const; +#endif // def CLIENT +}; + +//============================================================================ +// VaultSystemNode +//============================================================================ +struct VaultSystemNode : NetVaultNodeAccess { + + static const qword kCCRStatus = NetVaultNode::kInt32_1; + + int & ccrStatus; + + VaultSystemNode (NetVaultNode * node); + VaultSystemNode (const VaultTextNoteNode &); // not implemented + const VaultSystemNode & operator= (const VaultSystemNode &); // not implemented + + void SetCCRStatus (int v); +}; + + +//============================================================================ +// VaultMarkerGameNode +//============================================================================ +struct VaultMarkerGameNode : NetVaultNodeAccess { + + static const qword kGameName = NetVaultNode::kText_1; + static const qword kGameGuid = NetVaultNode::kUuid_1; + + wchar *& gameName; + Uuid & gameGuid; + + VaultMarkerGameNode (NetVaultNode * node); + VaultMarkerGameNode (const VaultMarkerGameNode &); // not implemented + const VaultMarkerGameNode & operator= (const VaultMarkerGameNode &); // not implemented + + void SetGameName (const wchar v[]); + void SetGameGuid (const Uuid & v); +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWinStrBlock/strblock.c b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWinStrBlock/strblock.c new file mode 100644 index 00000000..fb6aed82 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWinStrBlock/strblock.c @@ -0,0 +1,691 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/************************************************************************** + THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF + ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A + PARTICULAR PURPOSE. + + Copyright 1998 Microsoft Corporation. All Rights Reserved. +**************************************************************************/ + +/************************************************************************** + + File: strblock.c + + Description: Implements the functions that manipulate a string block. + +**************************************************************************/ + +#include +#include "strblock.h" + + +// The format of string resources is explained below. +// +// The smallest granularity of string resource that can be loaded/updated is a block. +// Each block is identified by an ID, starting with 1. You need to use the block ID +// when calling FindResource(), LoadResource(), UpdateResource(). +// +// A string with ID, nStringID, is in the block with ID, nBlockID, given by the following +// formula: +// nBlockID = (nStringID / 16) + 1; // Note integer division. +// +// A block of string resource is laid out as follows: +// Each block has NO_OF_STRINGS_PER_BLOCK (= 16) strings. Each string is represented as +// an ordered pair, (LENGTH, TEXT). The LENGTH is a WORD that specifies the size, in terms +// of number of characters, of the string that follows. TEXT follows the LENGTH and is +// a sequence of UNICODE characters, NOT terminated by a NULL character.Any TEXT may be of +// zero-length, in which case, LENGTH is zero. +// +// An executable does not have a string table block with ID, nBlockID, if it does not have any +// strings with IDs - ((nBlockID - 1) * 16) thru' ((nBlockID * 16) - 1). +// +// This format is the same for Windows NT, Windows 95 & Windows 98. Yes, strings in a resource +// are internally stored in UNICODE format even in Windows 95 & Windows 98. + + +// Internal data structure format for a string block. +// Our block of strings has as an array of UNICODE string pointers. + +typedef struct tagSTRINGBLOCK +{ + UINT nBlockID; // The ID of the block. + WORD wLangID; // The language ID. + LPWSTR strArray[NO_OF_STRINGS_PER_BLOCK]; // We maintain the strings + // internally in UNICODE. +} STRINGBLOCK, * PSTRINGBLOCK; + + +// A thread-specific error number for the last block operation. +__declspec(thread) STRBLOCKERR g_strBlockErr = STRBLOCKERR_OK; + +// Set the error code. +void SetBlockError( STRBLOCKERR err ) { g_strBlockErr = err; } + + +// Forward declarations. + +// Create a string block & return the pointer to the block. Return NULL on failure. +// Sets the error code. +PSTRINGBLOCK CreateBlock( HINSTANCE hInstLib, UINT nBlockID, WORD wLangID ); + +// Parse the string block resource pointed at by, pParse, and fill the strings in pStrBlock. +BOOL ParseRes( LPVOID pRes, PSTRINGBLOCK pStrBlock ); + +// Get the size of the raw string block resource in the given block. +DWORD GetResSize( PSTRINGBLOCK pStrBlock ); + +// Update a block of string in the specified library. +// hUpdate specifies the update-file handle. This handle is returned by the BeginUpdateResource. +// pStrBlock contains the new strings. +// nBlockID specifies the ID of the block. Use the same block ID as of pStrBlock if this value is -1. +// wlangID specifies the language ID of the block. Use the same language ID as of pStrBlock, if this value is 0. +// Returns TRUE on success and FALSE on failure. +// Sets the error code. +BOOL UpdateBlock( HANDLE hUpdate, PSTRINGBLOCK pStrBlock, int nBlockID, WORD wLangID ); + +// Use the strings in the block, pStrBloc, and build a buffer whose format matches that of the +// string resource block that can be used to update string resource. +// pRes points to a buffer that gets filled. It must be large enough to hold the entire block. +// To figure out the size needed, call GetResSize(). +VOID BuildRes( PSTRINGBLOCK pStrBlock, LPVOID pRes ); + + +// Create a string block. + +HSTRBLOCK WINAPI GetStringBlockA( LPCSTR strAppName, UINT nBlockID, WORD wLangID ) +{ + PSTRINGBLOCK pStrBlock = NULL; + HINSTANCE hInstLib = NULL; + + hInstLib = LoadLibraryExA( + strAppName, + NULL, + DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE + ); + + if( NULL == hInstLib ) + { + SetBlockError(STRBLOCKERR_APPLOADFAILED); + return NULL; + } + + // Create the block of strings. + pStrBlock = CreateBlock(hInstLib, nBlockID, wLangID); + + // Free the library. + FreeLibrary(hInstLib); + + if( pStrBlock ) + SetBlockError(STRBLOCKERR_OK); + + return (HSTRBLOCK)pStrBlock; +} + + +// Create a string block. + +HSTRBLOCK WINAPI GetStringBlockW( LPCWSTR strAppName, UINT nBlockID, WORD wLangID ) +{ + PSTRINGBLOCK pStrBlock = NULL; + HINSTANCE hInstLib = NULL; + + hInstLib = LoadLibraryExW( + strAppName, + NULL, + DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE + ); + + if( NULL == hInstLib ) + { + SetBlockError(STRBLOCKERR_APPLOADFAILED); + return NULL; + } + + // Create the block of strings. + pStrBlock = CreateBlock(hInstLib, nBlockID, wLangID); + + // Free the library. + FreeLibrary(hInstLib); + + if( pStrBlock ) + SetBlockError(STRBLOCKERR_OK); + + return (HSTRBLOCK)pStrBlock; +} + + +BOOL WINAPI DeleteStringBlock( HSTRBLOCK hStrBlock ) +{ + PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock; + int i; + + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_INVALIDBLOCK); + return FALSE; + } + + for( i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ ) + { + if( pStrBlock->strArray[i] ) + free(pStrBlock->strArray[i]); + } + free( pStrBlock); + + SetBlockError(STRBLOCKERR_OK); + + return TRUE; +} + + +int WINAPI GetStringLength( HSTRBLOCK hStrBlock, UINT nIndex ) +{ + int nLen; + PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock; + + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_INVALIDBLOCK); + return -1; + } + if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) ) + { + SetBlockError(STRBLOCKERR_INVALIDINDEX); + return -1; + } + + nLen = wcslen(pStrBlock->strArray[nIndex]); + SetBlockError(STRBLOCKERR_OK); + + return nLen; +} + + +BOOL WINAPI GetStringA( HSTRBLOCK hStrBlock, UINT nIndex, LPSTR pszText ) +{ + PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock; + + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_INVALIDBLOCK); + return FALSE; + } + if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) ) + { + SetBlockError(STRBLOCKERR_INVALIDINDEX); + return FALSE; + } + if( NULL == pszText ) + { + SetBlockError(STRBLOCKERR_STRINVALID); + return FALSE; + } + + if( !WideCharToMultiByte(CP_ACP, 0, pStrBlock->strArray[nIndex], -1, pszText, + wcslen(pStrBlock->strArray[nIndex]) + 1, NULL, NULL) ) + { + SetBlockError(STRBLOCKERR_UNKNOWN); + return FALSE; + } + + SetBlockError(STRBLOCKERR_OK); + + return TRUE; +} + + +BOOL WINAPI GetStringW( HSTRBLOCK hStrBlock, UINT nIndex, LPWSTR pszText ) +{ + PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock; + + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_INVALIDBLOCK); + return FALSE; + } + if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) ) + { + SetBlockError(STRBLOCKERR_INVALIDINDEX); + return FALSE; + } + if( NULL == pszText ) + { + SetBlockError(STRBLOCKERR_STRINVALID); + return FALSE; + } + + wcscpy(pszText, pStrBlock->strArray[nIndex]); + SetBlockError(STRBLOCKERR_OK); + + return TRUE; +} + + +BOOL WINAPI SetStringA( HSTRBLOCK hStrBlock, UINT nIndex, LPCSTR pszText ) +{ + PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock; + int nLen; + + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_INVALIDBLOCK); + return FALSE; + } + if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) ) + { + SetBlockError(STRBLOCKERR_INVALIDINDEX); + return FALSE; + } + + // Delete the current string & reallocate a new one.. + free(pStrBlock->strArray[nIndex]); + + nLen = strlen(pszText) + 1; + pStrBlock->strArray[nIndex] = (LPWSTR)malloc( sizeof(WCHAR) * nLen); + + if( NULL == pStrBlock->strArray[nIndex] ) + { + SetBlockError(STRBLOCKERR_NOMEMORY); + return FALSE; + } + + if( !MultiByteToWideChar(CP_ACP, 0, pszText, -1, pStrBlock->strArray[nIndex], nLen) ) + { + SetBlockError(STRBLOCKERR_UNKNOWN); + return FALSE; + } + + SetBlockError(STRBLOCKERR_OK); + + return TRUE; +} + + +BOOL WINAPI SetStringW( HSTRBLOCK hStrBlock, UINT nIndex, LPCWSTR pszText ) +{ + PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock; + int nLen; + + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_INVALIDBLOCK); + return FALSE; + } + if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) ) + { + SetBlockError(STRBLOCKERR_INVALIDINDEX); + return FALSE; + } + + // Delete the current string & reallocate a new one.. + free(pStrBlock->strArray[nIndex]); + nLen = wcslen(pszText) + 1; + + pStrBlock->strArray[nIndex] = (LPWSTR)malloc( sizeof(WCHAR) * nLen); + + if( NULL == pStrBlock->strArray[nIndex] ) + { + SetBlockError(STRBLOCKERR_NOMEMORY); + return FALSE; + } + + wcscpy(pStrBlock->strArray[nIndex], pszText); + SetBlockError(STRBLOCKERR_OK); + + return TRUE; +} + + +int WINAPI GetFirstStringID( HSTRBLOCK hStrBlock ) +{ + PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock; + + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_INVALIDBLOCK); + return -1; + } + SetBlockError(STRBLOCKERR_OK); + + return (pStrBlock->nBlockID - 1) * NO_OF_STRINGS_PER_BLOCK; +} + + +int WINAPI GetBlockID( HSTRBLOCK hStrBlock ) +{ + PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock; + + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_INVALIDBLOCK); + return -1; + } + SetBlockError(STRBLOCKERR_OK); + + return pStrBlock->nBlockID; +} + + +WORD WINAPI GetBlockLanguage( HSTRBLOCK hStrBlock ) +{ + PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock; + + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_INVALIDBLOCK); + return -1; + } + + SetBlockError(STRBLOCKERR_OK); + return pStrBlock->wLangID; +} + + +BOOL WINAPI UpdateStringBlockA( LPCSTR strAppName, HSTRBLOCK hStrBlock, int nBlockID, WORD wLangID ) +{ + HANDLE hUpdate; + PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock; + + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_INVALIDBLOCK); + return -1; + } + + hUpdate = BeginUpdateResourceA(strAppName, FALSE); + + if( NULL == hUpdate ) + { + DWORD dwError = GetLastError(); + + switch( dwError ) + { + case ERROR_CALL_NOT_IMPLEMENTED: + + SetBlockError(STRBLOCKERR_UPDATENOTIMPLEMENTED); + break; + + default: + + SetBlockError(STRBLOCKERR_UPDATEFAILED); + break; + } + + return FALSE; + } + + // Update the resource. + if( !UpdateBlock(hUpdate, pStrBlock, nBlockID, wLangID) ) + { + EndUpdateResource(hUpdate, FALSE); + return FALSE; + } + + if( !EndUpdateResource(hUpdate, FALSE) ) + { + SetBlockError(STRBLOCKERR_UPDATEFAILED); + return FALSE; + } + + SetBlockError(STRBLOCKERR_OK); + + return TRUE; +} + + +BOOL WINAPI UpdateStringBlockW( LPCWSTR strAppName, HSTRBLOCK hStrBlock, int nBlockID, WORD wLangID ) +{ + HANDLE hUpdate; + PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock; + + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_INVALIDBLOCK); + return -1; + } + + hUpdate = BeginUpdateResourceW(strAppName, FALSE); + + if( NULL == hUpdate ) + { + DWORD dwError = GetLastError(); + + switch( dwError ) + { + case ERROR_CALL_NOT_IMPLEMENTED: + + SetBlockError(STRBLOCKERR_UPDATENOTIMPLEMENTED); + break; + + default: + + SetBlockError(STRBLOCKERR_UPDATEFAILED); + break; + } + + return FALSE; + } + + // Update the resource. + if( !UpdateBlock(hUpdate, pStrBlock, nBlockID, wLangID) ) + { + EndUpdateResource(hUpdate, FALSE); + return FALSE; + } + + if( !EndUpdateResource(hUpdate, FALSE) ) + { + SetBlockError(STRBLOCKERR_UPDATEFAILED); + return FALSE; + } + + SetBlockError(STRBLOCKERR_OK); + + return TRUE; +} + + +STRBLOCKERR WINAPI GetStringBlockError() +{ + return g_strBlockErr; +} + + +// Create a string block & return the pointer to the block. Return NULL on failure. + +PSTRINGBLOCK CreateBlock( HINSTANCE hInstLib, UINT nBlockID, WORD wLangID ) +{ + PSTRINGBLOCK pStrBlock; + HRSRC hFindRes; + HGLOBAL hLoadRes; + LPVOID pRes; + + hFindRes = FindResourceEx(hInstLib, RT_STRING, MAKEINTRESOURCE(nBlockID), wLangID); + if( NULL == hFindRes ) + { + SetBlockError(STRBLOCKERR_RESNOTFOUND); + return NULL; + } + + hLoadRes = LoadResource(hInstLib, hFindRes); + if( NULL == hLoadRes ) + { + SetBlockError(STRBLOCKERR_LOADRESFAILED); + return NULL; + } + + pRes = LockResource(hLoadRes); + if( NULL == pRes ) + { + SetBlockError(STRBLOCKERR_LOADRESFAILED); + return NULL; + } + + // Create a new string block, fill the strings based on the resource contents. + pStrBlock = (PSTRINGBLOCK)malloc(sizeof(STRINGBLOCK)); + if( NULL == pStrBlock ) + { + SetBlockError(STRBLOCKERR_NOMEMORY); + return NULL; + } + + pStrBlock->nBlockID = nBlockID; + pStrBlock->wLangID = wLangID; + + if( !ParseRes(pRes, pStrBlock) ) + { + free(pStrBlock); + return NULL; + } + + return pStrBlock; +} + + +// Parse the raw string resource, pRes, and build up the string block, pStrBlock. +// The parsing illustrates the format of a string block in an executable. + +BOOL ParseRes( LPVOID pRes, PSTRINGBLOCK pStrBlock ) +{ + int i, j; + int nLen; + WCHAR* pParse = (WCHAR *)pRes; + + // There are NO_OF_STRINGS_PER_BLOCK(=16) strings per block. + for( i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ ) + { + nLen = (int)*pParse++; // The length of the string. + pStrBlock->strArray[i] = (LPWSTR)malloc((nLen + 1) * sizeof(WCHAR)); + + if( NULL == pStrBlock->strArray[i] ) + { + int k; + + for( k = 0; k < i; k++ ) // Free up the memory allocated so far. + free(pStrBlock->strArray[k]); + SetBlockError(STRBLOCKERR_NOMEMORY); + + return FALSE; + } + + for( j = 0; j < nLen; j++ ) // Copy the string. + pStrBlock->strArray[i][j] = *pParse++; + pStrBlock->strArray[i][j] = 0; + } + + SetBlockError(STRBLOCKERR_OK); + return TRUE; +} + + +DWORD GetResSize( PSTRINGBLOCK pStrBlock ) +{ + DWORD dwResSize = 0; + int i = 0; + + for( i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ ) + dwResSize += (wcslen(pStrBlock->strArray[i]) + 1); + + return dwResSize * sizeof(WCHAR); +} + + +// Build a raw resource block, pRes, based on our string block, pStrBlock. +// The raw resource block may be used to update a string resource. + +VOID BuildRes( PSTRINGBLOCK pStrBlock, LPVOID pRes ) +{ + int i, j; + int nLen; + WCHAR* pParse = (WCHAR *)pRes; + + // There are NO_OF_STRINGS_PER_BLOCK (= 16) strings per block. + for( i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ ) + { + *pParse++ = nLen = wcslen(pStrBlock->strArray[i]); + for( j = 0; j < nLen; j++ ) + *pParse++ = pStrBlock->strArray[i][j]; + } +} + + +BOOL UpdateBlock( HANDLE hUpdate, PSTRINGBLOCK pStrBlock, int nBlockID, WORD wLangID ) +{ + DWORD dwResSize; + LPVOID pRes; + DWORD dwRet = 0; + WORD wLanguageID = (0 == wLangID) ? pStrBlock->wLangID : wLangID; + + // Get the resource length as required by a raw string resource block. + dwResSize = GetResSize(pStrBlock); + pRes = malloc(dwResSize); + if( NULL == pRes ) + { + SetBlockError(STRBLOCKERR_NOMEMORY); + return FALSE; + } + + BuildRes(pStrBlock, pRes); + + if( !UpdateResource( + hUpdate, + RT_STRING, + MAKEINTRESOURCE(((-1 == nBlockID) ? pStrBlock->nBlockID : nBlockID)), + wLanguageID, + pRes, + dwResSize + ) ) + { + DWORD dwError = GetLastError(); + + switch( dwError ) + { + case ERROR_CALL_NOT_IMPLEMENTED: + + SetBlockError(STRBLOCKERR_UPDATENOTIMPLEMENTED); + break; + + default: + + SetBlockError(STRBLOCKERR_UPDATEFAILED); + break; + } + + free(pRes); + return FALSE; + } + + free(pRes); + + SetBlockError(STRBLOCKERR_OK); + + return TRUE; +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWinStrBlock/strblock.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWinStrBlock/strblock.h new file mode 100644 index 00000000..6311dbfe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWinStrBlock/strblock.h @@ -0,0 +1,244 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/************************************************************************** + THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF + ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A + PARTICULAR PURPOSE. + + Copyright 1998 Microsoft Corporation. All Rights Reserved. +**************************************************************************/ + +/************************************************************************** + + File: strblock.h + + Description: Programmer's interface to manipulate a block of string + resources. It defines an API with functions to Create, + Access, Modify & Destroy a string block. + +**************************************************************************/ + +#ifndef _STRBLOCK_H +#define _STRBLOCK_H + +#include + +// The number of strings in a block. The string resource is internally +// stored as blocks of NO_OF_STRINGS_PER_BLOCK ( = 16) strings. A block +// is the smallest granularity for manipulating a string resource. +#define NO_OF_STRINGS_PER_BLOCK 16 + +// Error codes for operations on a string block. +typedef enum { + STRBLOCKERR_OK, // The last operation was successful. + STRBLOCKERR_NOMEMORY, // The last operation on the block failed due to inadequate memory. + STRBLOCKERR_INVALIDINDEX, // The index passed was incorrect. + STRBLOCKERR_UPDATEFAILED, // The specified app could not be updated. + STRBLOCKERR_APPLOADFAILED, // The specified app could not be loaded. + STRBLOCKERR_RESNOTFOUND, // The specified resource could not be located. + STRBLOCKERR_LOADRESFAILED, // The specified resource could not be loaded. + STRBLOCKERR_INVALIDBLOCK, // The specified block handle is invalid. + STRBLOCKERR_STRINVALID, // The string pointer passed was invalid. + STRBLOCKERR_UPDATENOTIMPLEMENTED, // UpdateResource not implemented. + STRBLOCKERR_UNKNOWN // An unspecified error. +} STRBLOCKERR; + + +// Handle to a block of string. We hide the internal format we use for manipulating +// a string resource by providing access to it through a handle. + +DECLARE_HANDLE(HSTRBLOCK); + + +// Methods to access a string block. + +// Function: HSTRBLOCK WINAPI GetStringBlockA( LPCSTR strAppName, UINT nBlockID, +// WORD wLangID ); +// Purpose: Get the block of string with the specified ID & language from the +// specified application (ANSI version). +// LPCSTR strAppName: The name of the application. +// UINT nBlockID: The ID of the block. +// WORD wLangID: The language identifier. You can create a language +// identifier using the macro, MAKELANGID. +// Returns: Handle to a string block if successful, NULL otherwise. +// Comments: This function creates a string block. Call DeleteStringBlock() +// when you no longer need the block. +HSTRBLOCK WINAPI GetStringBlockA( LPCSTR strAppName, UINT nBlockID, WORD wLangID ); + + +// Function: HSTRBLOCK WINAPI GetStringBlockW( LPCWSTR strAppName, UINT nBlockID, +// WORD wLangID ); +// Purpose: Get the block of string with the specified ID & language from the +// specified application (UNICODE version). +// LPCWSTR strAppName: The name of the application. +// UINT nBlockID: The ID of the block. +// WORD wLangID: The language identifier. You can create a language +// identifier using the macro, MAKELANGID. +// Returns: Handle to a string block if successful, NULL otherwise. +// Comments: This function creates a string block. Call DeleteStringBlock() +// when you no longer need the block. +HSTRBLOCK WINAPI GetStringBlockW( LPCWSTR strAppName, UINT nBlockID, WORD wLangID ); + + +// Function: BOOL WINAPI DeleteStringBlock( HSTRBLOCK hStrBlock ); +// Purpose: Delete a block of string. +// HSTRBLOCK hStrBlock: The handle to the block. +// Returns: TRUE if successful, FALSE on failure. +// Comments: Call this function when you no longer need a block. Calling this function +// frees the memory occupied by the block. Failure to delete a block +// results in memory leaks. +BOOL WINAPI DeleteStringBlock( HSTRBLOCK hStrBlock ); + + +// Function: int WINAPI GetStringLength( HSTRBLOCK hStrBlock, UINT nIndex ); +// Purpose: Get the length of a string in the block. +// HSTRBLOCK hStrBlock: The handle to the string block. +// UINT nIndex: The zero-based index of the string in the block. The index can be +// any value from 0 thru' (NO_OF_STRINGS_PER_BLOCK - 1). +// Returns: If successful, the string length in number of characters, excluding the terminating +// NULL character; -1 if the function fails. +// Comments: Call this function to get the string length before calling GetString. +int WINAPI GetStringLength( HSTRBLOCK hStrBlock, UINT nIndex ); + + +// Function: BOOL WINAPI GetStringA( HSTRBLOCK hStrBlock, UINT nIndex, LPSTR pszStr ); +// Purpose: Get the string at the specified index in the block (ANSI version). +// HSTRBLOCK hStrBlock: The handle to the string block. +// UINT nIndex: The zero-based index of the string in the block. The index can be a value +// from 0 through (NO_OF_STRINGS_PER_BLOCK - 1). +// LPSTR pszStr: The pointer to the memory to which the string requested will be copied. +// This memory should be large enough to contain the string. +// Returns: TRUE on success, FALSE otherwise. +// Comments: Before calling this function, call GetStringLength() to get the string length. +BOOL WINAPI GetStringA( HSTRBLOCK hStrBlock, UINT nIndex, LPSTR pszStr ); + + +// Function: BOOL WINAPI GetStringW( HSTRBLOCK hStrBlock, UINT nIndex, LPWSTR pszStr ); +// Purpose: Get the string at the specified index in the block (UNICODE version). +// HSTRBLOCK hStrBlock: The handle to the string block. +// UINT nIndex: The zero-based index of the string in the block. The index can be a value +// from 0 through (NO_OF_STRINGS_PER_BLOCK - 1). +// LPSTR pszStr: The pointer to the memory to which the string requested will be copied. +// This memory should be large enough to contain the string. +// Returns: TRUE on success, FALSE otherwise. +// Comments: Before calling this function, call GetStringLength() to get the string length. +BOOL WINAPI GetStringW( HSTRBLOCK hStrBlock, UINT nIndex, LPWSTR pszStr ); + + +// Function: BOOL WINAPI SetStringA( HSTRBLOCK hStrBlock, UINT nIndex, LPCSTR pszText ); +// Purpose: Change the string at the specified index in the block (ANSI version). +// HSTRBLOCK hStrBlock: The handle to the string block. +// UINT nIndex: The zero-based index of the string in the block. The index can be a value +// from 0 through (NO_OF_STRINGS_PER_BLOCK - 1). +// LPCSTR pszText: The pointer to the new string. +// Returns: TRUE on success, FALSE otherwise. +BOOL WINAPI SetStringA( HSTRBLOCK hStrBlock, UINT nIndex, LPCSTR pszText ); + + +// Function: BOOL WINAPI SetStringW( HSTRBLOCK hStrBlock, UINT nIndex, LPCWSTR pszStr ); +// Purpose: Change the string at the specified index in the block (UNICODE version). +// HSTRBLOCK hStrBlock: The handle to the string block. +// UINT nIndex: The zero-based index of the string in the block. The index can be a value +// from 0 through (NO_OF_STRINGS_PER_BLOCK - 1). +// LPCWSTR pszText: The pointer to the new string. +// Returns: TRUE on success, FALSE otherwise. +BOOL WINAPI SetStringW( HSTRBLOCK hStrBlock, UINT nIndex, LPCWSTR pszText ); + + +// Function: int WINAPI GetFirstStringID( HSTRBLOCK hStrBlock ); +// Purpose: Get the ID of the first string in the block. +// HSTRBLOCK hStrBlock: The handle to the string block. +// Returns: The ID of the first string in the block. -1 if the function fails. +int WINAPI GetFirstStringID( HSTRBLOCK hStrBlock ); + + +// Function: int WINAPI GetBlockID( HSTRBLOCK hStrBlock ); +// Purpose: Get the ID of the string block. This is the ID of the string block that was +// specified when the block was created. +// HSTRBLOCK hStrBlock: The handle to the string block. +// Returns: The block ID, if sucessful, or -1 on failure. +int WINAPI GetBlockID( HSTRBLOCK hStrBlock ); + + +// Function: WORD WINAPI GetBlockLanguage( HSTRBLOCK hStrBlock ); +// Purpose: Get the identifier of the language for the string block. This is the language ID +// specified when the block was created. +// HSTRBLOCK hStrBlock: The handle to the string block. +// Returns: The language ID of the block, if successful; 0 on failure. +// Comments: For info on language IDs, refer help of MAKELANGID. +WORD WINAPI GetBlockLanguage( HSTRBLOCK hStrBlock ); + + +// Function: BOOL WINAPI UpdateStringBlockA( LPCSTR strAppName, HSTRBLOCK hStrBlock, +// int nBlockID, WORD wLangID ); +// Purpose: Update the block of string resource in the specified application +// (ANSI version). +// LPCTSTR strAppName: The name of the application whose string resource has to be updated. +// HSTRBLOCK hStrBlock: The handle to the block that contains the new strings. +// int nBlockID: The ID of the block to update in the app. If it is -1, use the same block +// ID as that of hStrBlock. +// WORD wLangID: The identifier of the language whaose string block has to be updated. If this +// value is 0, use the same language as that of hStrBlock. +// Returns: TRUE on success, FALSE on failure. +BOOL WINAPI UpdateStringBlockA( LPCSTR strAppName, HSTRBLOCK hStrBlock, int nBlockID, WORD wLangID ); + + +// Function: BOOL WINAPI UpdateStringBlockW( LPCWTR strAppName, HSTRBLOCK hStrBlock, +// int nBlockID, WORD wLangID ); +// Purpose: Update the block of string resource in the specified application +// (UNICODE version). +// LPCTSTR strAppName: The name of the application whose string resource has to be updated. +// HSTRBLOCK hStrBlock: The handle to the block that contains the new strings. +// int nBlockID: The ID of the block to update in the app. If it is -1, use the same block +// ID as that of hStrBlock. +// WORD wLangID: The identifier of the language whaose string block has to be updated. If this +// value is 0, use the same language as that of hStrBlock. +// Returns: TRUE on success, FALSE on failure. +BOOL WINAPI UpdateStringBlockW( LPCWSTR strAppName, HSTRBLOCK hStrBlock, int nBlockID, WORD wLangID ); + + +// Function: STRBLOCKERR WINAPI GetStringBlockError(); +// Purpose: Get the error status of the last string block action. +// Returns: The error status of the last operation on the specified block. +// Comments: The error code is maintained on a per-thread basis. Multiple threads do not +// overwrite each other's last block error code. +STRBLOCKERR WINAPI GetStringBlockError(); + + +#ifdef UNICODE +#define GetStringBlock GetStringBlockW +#define GetString GetStringW +#define SetString SetStringW +#define UpdateStringBlock UpdateStringBlockW +#else +#define GetStringBlock GetStringBlockA +#define GetString GetStringA +#define SetString SetStringA +#define UpdateStringBlock UpdateStringBlockA +#endif // UNICODE + +#endif // _STRBLOCK_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/basewnd.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/basewnd.cpp new file mode 100644 index 00000000..2ae1fbc0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/basewnd.cpp @@ -0,0 +1,109 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" + + +// Stolen from: http://www.mvps.org/user32/webhost.cab +// No copyright notices, so I assume it's public domain -Colin +#include "basewnd.h" + +#include "hsConfig.h" +#if HS_BUILD_FOR_WIN32 + + +wchar_t basewnd::szClassName[] = L"BASEWND"; + + +void basewnd::Initialize(HINSTANCE hAppInstance,UINT style) +{ + WNDCLASSEX wc = + { + sizeof(wc), + style, + WindowProc, + 0,0, + hAppInstance, + LoadIcon(NULL,IDI_APPLICATION), + LoadCursor(NULL,IDC_ARROW), + (HBRUSH)(COLOR_WINDOW+1), + NULL, + szClassName, + LoadIcon(NULL,IDI_APPLICATION) + }; + RegisterClassEx(&wc); +} + + +LRESULT CALLBACK basewnd::WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) +{ + basewnd* _this; + if(uMsg == WM_CREATE && (_this = (basewnd*)(LPCREATESTRUCT(lParam))->lpCreateParams)) + { + SetWindowLong(hwnd,GWL_USERDATA,(long)_this); + _this->hwnd = hwnd; + _this->AddRef(); + } + else + _this = (basewnd*)GetWindowLong(hwnd,GWL_USERDATA); + + LRESULT result = 0; + BOOL fDoDef = !(_this && _this->HandleMessage(uMsg,wParam,lParam,&result)); + + if(uMsg == WM_DESTROY) + { + SetWindowLong(hwnd,GWL_USERDATA,(long)NULL); + _this->Release(); + } + + return fDoDef?DefWindowProc(hwnd,uMsg,wParam,lParam):result; +} + + +basewnd::basewnd() +: mcRef(1) +{ +} + + +basewnd::~basewnd() +{ +} + +ULONG basewnd::AddRef() +{ + return mcRef++; +} + +ULONG basewnd::Release() +{ + if(--mcRef) + return mcRef; + delete this; + return 0; +} + + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/basewnd.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/basewnd.h new file mode 100644 index 00000000..43f5738b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/basewnd.h @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Stolen from: http://www.mvps.org/user32/webhost.cab +// No copyright notices, so I assume it's public domain -Colin + +#include "hsConfig.h" +#if HS_BUILD_FOR_WIN32 + +#pragma once + +#include + + +struct basewnd +{ + static wchar_t szClassName[]; + static LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); + static void Initialize(HINSTANCE hAppInstance,UINT style=0); + + HWND hwnd; + ULONG mcRef; + basewnd(); + virtual ~basewnd(); + +public: + virtual ULONG AddRef(); + virtual ULONG Release(); + virtual BOOL HandleMessage(UINT,WPARAM,LPARAM,LRESULT*)=0; + +public: // inline overrides + BOOL ShowWindow(int nCmdShow){return ::ShowWindow(hwnd,nCmdShow);} + BOOL UpdateWindow(void){return ::UpdateWindow(hwnd);} +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plButton.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plButton.h new file mode 100644 index 00000000..20fe8e6a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plButton.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plButton_h_inc +#define plButton_h_inc + + +class plButton : public plControl +{ +public: + DECLARE_WINDOWSUBCLASS(plButton,plControl) + + plDelegate fClickDelegate; + plDelegate fDoubleClickDelegate; + plDelegate fPushDelegate; + plDelegate fUnPushDelegate; + plDelegate fSetFocusDelegate; + plDelegate fKillFocusDelegate; + + plButton() + {} + plButton( plWindow * inOwner, int inId=0, plDelegate inClicked=plDelegate(), WNDPROC inSuperProc=nil ) + : plControl( inOwner, inId, inSuperProc?inSuperProc:_SuperProc ) + , fClickDelegate( inClicked ) + {} + + void OpenWindow( bool visible, int X, int Y, int XL, int YL, const wchar_t * text ) + { + PerformCreateWindowEx + ( + 0, + nil, + WS_CHILD, + X, Y, + XL, YL, + *fOwnerWindow, + (HMENU)fControlID, + plWndCtrls::Instance() + ); + SendMessage( *this, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(0,0) ); + SetText( text ); + if( visible ) + ShowWindow( *this, SW_SHOWNOACTIVATE ); + } + void SetVisibleText( const wchar_t * text ) + { + CHECK(Handle()); + if( text ) + SetText( text ); + Show( text!=nil ); + } + + void Click() + { + SendMessage( *this, BM_CLICK, 0, 0 ); + } + + bool InterceptControlCommand( unsigned int message, unsigned int wParam, LONG lParam ) + { + if ( HIWORD(wParam)==BN_CLICKED ) {fClickDelegate(); return 1;} + else if( HIWORD(wParam)==BN_DBLCLK ) {fDoubleClickDelegate(); return 1;} + else if( HIWORD(wParam)==BN_PUSHED ) {fPushDelegate(); return 1;} + else if( HIWORD(wParam)==BN_UNPUSHED ) {fUnPushDelegate(); return 1;} + else if( HIWORD(wParam)==BN_SETFOCUS ) {fSetFocusDelegate(); return 1;} + else if( HIWORD(wParam)==BN_KILLFOCUS ) {fUnPushDelegate(); return 1;} + else return 0; + } +}; + + +#endif // plButton_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plCheckBox.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plCheckBox.h new file mode 100644 index 00000000..33cb4a16 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plCheckBox.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plCheckBox_h_inc +#define plCheckBox_h_inc + + +class plCheckBox : public plButton +{ + DECLARE_WINDOWSUBCLASS(plCheckBox,plButton) + plCheckBox() + {} + plCheckBox( plWindow * inOwner, int inId=0, plDelegate inClicked=plDelegate(), WNDPROC inSuperProc=nil ) + : plButton( inOwner, inId, inClicked, inSuperProc?inSuperProc:_SuperProc ) + {} + void OpenWindow( bool visible, int X, int Y, int XL, int YL, const wchar_t * text ) + { + PerformCreateWindowEx + ( + 0, + nil, + WS_CHILD|BS_CHECKBOX, + X, Y, + XL, YL, + *fOwnerWindow, + (HMENU)fControlID, + plWndCtrls::Instance() + ); + SendMessage( *this, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(0,0) ); + SetText( text ); + if( visible ) + ShowWindow( *this, SW_SHOWNOACTIVATE ); + } + void Check(bool check=true) + { + SendMessage(*this,BM_SETCHECK,(check)?BST_CHECKED:BST_UNCHECKED,0); + } + bool IsChecked() const + { + return (SendMessage(*this,BM_GETCHECK,0,0)==BST_CHECKED); + } + bool IsUnchecked() const + { + return !IsChecked(); + } + std::string IGetValue() const + { + char tmp[20]; + sprintf(tmp,"%s",IsChecked()?"true":"false"); + return tmp; + } + void ISetValue(const char * value) + { + if (stricmp(value,"true")==0) + Check(true); + else if (stricmp(value,"false")==0) + Check(false); + else + Check(atoi(value)?true:false); + } +}; + +#endif // plCheckBox_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plComboBox.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plComboBox.h new file mode 100644 index 00000000..2631eb54 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plComboBox.h @@ -0,0 +1,213 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plComboBox_h_inc +#define plComboBox_h_inc + +class plComboBox : public plControl +{ +public: + DECLARE_WINDOWSUBCLASS(plComboBox,plControl) + + plDelegate fDoubleClickDelegate; + plDelegate fDropDownDelegate; + plDelegate fCloseComboDelegate; + plDelegate fEditChangeDelegate; + plDelegate fEditUpdateDelegate; + plDelegate fSetFocusDelegate; + plDelegate fKillFocusDelegate; + plDelegate fSelectionChangeDelegate; + plDelegate fSelectionEndOkDelegate; + plDelegate fSelectionEndCancelDelegate; + plDelegate fEnterKeyPressedDelegate; + + plComboBox() + {} + plComboBox( plWindow * inOwner, int inId=0, WNDPROC inSuperProc=nil ) + : plControl( inOwner, inId, inSuperProc?inSuperProc:_SuperProc ) + {} + + void OpenWindow( bool visible ) + { + PerformCreateWindowEx + ( + 0, + nil, + WS_CHILD | WS_VSCROLL | CBS_DROPDOWNLIST | (visible?WS_VISIBLE:0), + 0, 0, + 64, 384, + *fOwnerWindow, + (HMENU)fControlID, + plWndCtrls::Instance() + ); + SendMessage( *this, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(0,0) ); + } + LONG WndProc( unsigned int message, unsigned int wParam, LONG lParam ) + { + switch (message) + { + case WM_GETDLGCODE: + return DLGC_WANTALLKEYS; + + case WM_CHAR: + //Process this message to avoid message beeps. + if ((wParam == VK_RETURN) || (wParam == VK_TAB)) + return 0; + else + return plControl::WndProc( message, wParam, lParam ); + + default: + return plControl::WndProc( message, wParam, lParam ); + } + } + + void OnKeyDown( UInt16 ch ) + { + if (ch==VK_RETURN) + fEnterKeyPressedDelegate(); + else if (ch==VK_TAB) + PostMessage (*fOwnerWindow, WM_NEXTDLGCTL, 0, 0L); + } + + bool InterceptControlCommand( unsigned int message, unsigned int wParam, LONG lParam ) + { + if ( HIWORD(wParam)==CBN_DBLCLK ) {fDoubleClickDelegate(); return 1;} + else if( HIWORD(wParam)==CBN_DROPDOWN ) {fDropDownDelegate(); return 1;} + else if( HIWORD(wParam)==CBN_CLOSEUP ) {fCloseComboDelegate(); return 1;} + else if( HIWORD(wParam)==CBN_EDITCHANGE ) {fEditChangeDelegate(); return 1;} + else if( HIWORD(wParam)==CBN_EDITUPDATE ) {fEditUpdateDelegate(); return 1;} + else if( HIWORD(wParam)==CBN_SETFOCUS ) {fSetFocusDelegate(); return 1;} + else if( HIWORD(wParam)==CBN_KILLFOCUS ) {fKillFocusDelegate(); return 1;} + else if( HIWORD(wParam)==CBN_SELCHANGE ) {fSelectionChangeDelegate(); return 1;} + else if( HIWORD(wParam)==CBN_SELENDOK ) {fSelectionEndOkDelegate(); return 1;} + else if( HIWORD(wParam)==CBN_SELENDCANCEL ) {fSelectionEndCancelDelegate(); return 1;} + else return 0; + } + + virtual int InsertString( int index, const wchar_t * str ) + { + return SendMessage( *this, CB_INSERTSTRING, index, (LPARAM)str ); + } + virtual void InsertStringAndData( int index, const wchar_t * str, void * item ) + { + int i = SendMessage( *this, CB_INSERTSTRING, index, (LPARAM)str ); + SetItemData(i,item); + } + virtual int AddString( const wchar_t * str ) + { + return SendMessage( *this, CB_ADDSTRING, 0, (LPARAM)str ); + } + virtual void AddStringAndData( const wchar_t * str, void * item ) + { + int index = SendMessage( *this, CB_ADDSTRING, 0, (LPARAM)str ); + SetItemData(index,item); + } + virtual void AddStrings( const std::vector & strings ) + { + for (int i=0; i & strings ) + { + for (int i=0; i=0) + tempString = GetString(index); + else + tempString = GetText(); + + char *temp = hsWStringToString(tempString.c_str()); + retVal = temp; + delete [] temp; + return retVal; + } + void ISetValue(const char * value) + { + wchar_t *temp = hsStringToWString(value); + SetCurrent(FindString(temp)); + delete [] temp; + } +}; + + +#endif // plComboBox_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plControl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plControl.h new file mode 100644 index 00000000..3926a3b8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plControl.h @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plControl_h_inc +#define plControl_h_inc + + +class plControl : public plWindow +{ +public: + WNDPROC WindowDefWndProc; + + plControl() + {} + plControl( plWindow * ownerWindow, int inId, WNDPROC inSuperProc ) + : plWindow( ownerWindow ) + { + CHECK(fOwnerWindow); + WindowDefWndProc = inSuperProc; + fControlID = inId ? inId : ownerWindow->fTopControlID++; + fOwnerWindow->fControls.push_back( this ); + } + ~plControl() + { + CHECK(fOwnerWindow); + std::vector::iterator it = std::find(fOwnerWindow->fControls.begin(),fOwnerWindow->fControls.end(),this); + if (it!=fOwnerWindow->fControls.end()) + fOwnerWindow->fControls.erase(it); + } + + int CallDefaultProc( unsigned int message, unsigned int wParam, LONG lParam ) + { + return CallWindowProc( WindowDefWndProc, Handle(), message, wParam, lParam ); + } + static WNDPROC RegisterWindowClass( const wchar_t * name, const wchar_t * winBaseClass ) + { +#ifdef UNICODE + WNDPROC superProc=nil; + WNDCLASSEX cls; + HSMemory::ClearMemory( &cls, sizeof(cls) ); + cls.cbSize = sizeof(cls); + CHECK( GetClassInfoEx( nil, winBaseClass, &cls ) ); + superProc = cls.lpfnWndProc; + cls.lpfnWndProc = plWindow::StaticWndProc; + cls.lpszClassName = name; + cls.hInstance = plWndCtrls::Instance(); + CHECK(cls.lpszMenuName==nil); + CHECK(RegisterClassEx( &cls )); +#else + char* cWinBaseClass = hsWStringToString(winBaseClass); + char* cName = hsWStringToString(name); + + WNDPROC superProc=nil; + WNDCLASSEX cls; + HSMemory::ClearMemory( &cls, sizeof(cls) ); + cls.cbSize = sizeof(cls); + CHECK( GetClassInfoEx( nil, cWinBaseClass, &cls ) ); + superProc = cls.lpfnWndProc; + cls.lpfnWndProc = plWindow::StaticWndProc; + cls.lpszClassName = cName; + cls.hInstance = plWndCtrls::Instance(); + CHECK(cls.lpszMenuName==nil); + CHECK(RegisterClassEx( &cls )); + + delete [] cName; + delete [] cWinBaseClass; +#endif + return superProc; + } +}; + + +#endif // plControl_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plDialog.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plDialog.h new file mode 100644 index 00000000..437d5327 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plDialog.h @@ -0,0 +1,135 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plDialog_h_inc +#define plDialog_h_inc + +class plDialog : public plWindow +{ +protected: + DLGTEMPLATE* ILoadDlgTemplate() + { + HRSRC hRsrc = FindResourceEx(plWndCtrls::Instance(), RT_DIALOG, MAKEINTRESOURCE(fControlID), (WORD)(plWndCtrls::GetLanguage())); + HGLOBAL hGlobal = LoadResource(plWndCtrls::Instance(), hRsrc); + LPVOID lpsz = LockResource(hGlobal); + return (DLGTEMPLATE*)lpsz; + } + +public: + plDialog() + {} + plDialog( int inDialogId, plWindow * ownerWindow=nil ) + : plWindow( ownerWindow ) + { + fControlID = inDialogId; + } + + int CallDefaultProc( unsigned int message, unsigned int wParam, LONG lParam ) + { + return 0; + } + virtual int DoModal() + { + CHECK(Handle()==nil); + _Windows.push_back( this ); + _ModalCount++; + int result = 0; + if (plWndCtrls::GetLanguage() != 0) + result = DialogBoxIndirectParam(plWndCtrls::Instance(),ILoadDlgTemplate(),fOwnerWindow?fOwnerWindow->Handle():nil,(int(APIENTRY*)(HWND,unsigned int,WPARAM,LPARAM))StaticWndProc,(LPARAM)this); + else + result = DialogBoxParam(plWndCtrls::Instance(),MAKEINTRESOURCE(fControlID),fOwnerWindow?fOwnerWindow->Handle():nil,(int(APIENTRY*)(HWND,unsigned int,WPARAM,LPARAM))StaticWndProc,(LPARAM)this); + int err = GetLastError(); + _ModalCount--; + return result; + } + void OpenWindow(bool visible=true) + { + CHECK(Handle()==nil); + _Windows.push_back( this ); + + HWND hWndCreated = nil; + if (plWndCtrls::GetLanguage() != 0) + hWndCreated = CreateDialogIndirectParam(plWndCtrls::Instance(),ILoadDlgTemplate(),nil,(int(APIENTRY*)(HWND,unsigned int,WPARAM,LPARAM))StaticWndProc,(LPARAM)this); + else + hWndCreated = CreateDialogParam(plWndCtrls::Instance(),MAKEINTRESOURCE(fControlID),nil,(int(APIENTRY*)(HWND,unsigned int,WPARAM,LPARAM))StaticWndProc,(LPARAM)this); + + int err = GetLastError(); + CHECK(hWndCreated); + CHECK(hWndCreated==Handle()); + Show(visible); + } + void OpenChildWindow( int inControlId, bool visible ) + { + CHECK(!Handle()); + _Windows.push_back( this ); + HWND hWndParent = inControlId ? GetDlgItem(fOwnerWindow->Handle(),inControlId) : fOwnerWindow ? fOwnerWindow->Handle() : nil; + + HWND hWndCreated = nil; + if (plWndCtrls::GetLanguage() != 0) + hWndCreated = CreateDialogIndirectParam(plWndCtrls::Instance(),ILoadDlgTemplate(),hWndParent,(int(APIENTRY*)(HWND,unsigned int,WPARAM,LPARAM))StaticWndProc,(LPARAM)this); + else + hWndCreated = CreateDialogParam(plWndCtrls::Instance(),MAKEINTRESOURCE(fControlID),hWndParent,(int(APIENTRY*)(HWND,unsigned int,WPARAM,LPARAM))StaticWndProc,(LPARAM)this); + + int err = GetLastError(); + CHECK(hWndCreated); + CHECK(hWndCreated==Handle()); + Show( visible ); + } + virtual void OnInitDialog() + { + plWindow::OnInitDialog(); + for( int i=0; iHandle()); + control->Handle() = GetDlgItem( *this, control->fControlID ); + CHECK(control->Handle()); + _Windows.push_back(control); + control->WindowDefWndProc = (WNDPROC)GetWindowLong( control->Handle(), GWL_WNDPROC ); + SetWindowLong( control->Handle(), GWL_WNDPROC, (LONG)plWindow::StaticWndProc ); + } + for( int i=0; iOnCreate(); + } + } + void EndDialog( int result ) + { + ::EndDialog( Handle(), result ); + } + void EndDialogTrue() + { + EndDialog( 1 ); + } + void EndDialogFalse() + { + EndDialog( 0 ); + } +}; + + +#endif // plDialog_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plEdit.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plEdit.h new file mode 100644 index 00000000..fe4c5cfc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plEdit.h @@ -0,0 +1,146 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plEdit_h_inc +#define plEdit_h_inc + + +class plEdit : public plControl +{ +private: + bool fRestrictToAlphaNum; +public: + DECLARE_WINDOWSUBCLASS(plEdit,plControl) + + plDelegate fChangeDelegate; + plDelegate fKillFocusDelegate; + + plEdit() + {fRestrictToAlphaNum = false;} + plEdit( plWindow * inOwner, int inId=0, WNDPROC inSuperProc=nil, bool restrict=false ) + : plControl( inOwner, inId, inSuperProc?inSuperProc:_SuperProc ), fRestrictToAlphaNum(restrict) + {} + + void OpenWindow( bool visible, bool multiline, bool readOnly ) + { + PerformCreateWindowEx + ( + WS_EX_CLIENTEDGE, + nil, + WS_CHILD | (visible?WS_VISIBLE:0) | ES_LEFT | (multiline?(ES_MULTILINE|WS_VSCROLL|WS_HSCROLL):0) | ES_AUTOVSCROLL | ES_AUTOHSCROLL | (readOnly?ES_READONLY:0), + 0, 0, + 0, 0, + *fOwnerWindow, + (HMENU)fControlID, + plWndCtrls::Instance() + ); + SendMessage( *this, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(0,0) ); + } + bool InterceptControlCommand( unsigned int message, unsigned int wParam, LONG lParam ) + { + if (HIWORD(wParam)==EN_CHANGE) + { + fChangeDelegate(); + return 1; + } + else if(HIWORD(wParam)==EN_KILLFOCUS) + { + fKillFocusDelegate(); + return 1; + } + else + return 0; + } + + virtual LONG OnChar( char ch ) + { + if (fRestrictToAlphaNum) + { + // we only accept 0-9, a-z, A-Z, or backspace + if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'z') && (ch < 'A' || ch >'Z') && !(ch == VK_BACK)) + { + MessageBeep(-1); // alert the user + return FALSE; // and make sure the default handler doesn't get it + } + } + return TRUE; + } + + bool GetReadOnly() + { + CHECK(Handle()); + return (GetWindowLong( *this, GWL_STYLE )&ES_READONLY)!=0; + } + void SetReadOnly( bool readOnly ) + { + CHECK(Handle()); + SendMessage( *this, EM_SETREADONLY, readOnly, 0 ); + } + int GetLineCount() + { + CHECK(Handle()); + return SendMessage( *this, EM_GETLINECOUNT, 0, 0 ); + } + int GetLineIndex( int line ) + { + CHECK(Handle()); + return SendMessage( *this, EM_LINEINDEX, line, 0 ); + } + void GetSelection( int& start, int& end ) + { + CHECK(Handle()); + SendMessage( *this, EM_GETSEL, (WPARAM)&start, (LPARAM)&end ); + } + void SetSelection( int start, int end ) + { + CHECK(Handle()); + SendMessage( *this, EM_SETSEL, start, end ); + } + void SetSelectedText( const wchar_t * text ) + { + CHECK(Handle()); + SendMessage( *this, EM_REPLACESEL, 1, (LPARAM)text ); + } + void SetLimitText( int maxChars ) + { + CHECK(Handle()); + SendMessage( *this, EM_SETLIMITTEXT, maxChars, 0 ); + } + bool GetModify() + { + return SendMessage( *this, EM_GETMODIFY, 0, 0 )!=0; + } + void SetModify( bool modified ) + { + SendMessage( *this, EM_SETMODIFY, modified, 0 ); + } + void ScrollCaret() + { + SendMessage( *this, EM_SCROLLCARET, 0, 0 ); + } +}; + + +#endif // plEdit_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plLabel.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plLabel.h new file mode 100644 index 00000000..df43b256 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plLabel.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plLabel_h_inc +#define plLabel_h_inc + + +class plLabel : public plControl +{ +public: + DECLARE_WINDOWSUBCLASS(plLabel,plControl) + + plLabel() + {} + plLabel( plWindow * inOwner, int inId=0, WNDPROC inSuperProc=nil ) + : plControl( inOwner, inId, inSuperProc?inSuperProc:_SuperProc ) + {} + + void OpenWindow( bool visible ) + { + PerformCreateWindowEx + ( + WS_EX_CLIENTEDGE, + nil, + WS_CHILD | (visible?WS_VISIBLE:0), + 0, 0, + 0, 0, + *fOwnerWindow, + (HMENU)fControlID, + plWndCtrls::Instance() + ); + SendMessage( *this, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(0,0) ); + } +}; + + +#endif // plLabel_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plListBox.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plListBox.h new file mode 100644 index 00000000..8174042e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plListBox.h @@ -0,0 +1,236 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plListBox_h_inc +#define plListBox_h_inc + + +class plListBox : public plControl +{ +public: + DECLARE_WINDOWSUBCLASS(plListBox,plControl) + + plDelegate DoubleClickDelegate; + plDelegate SelectionChangeDelegate; + plDelegate SelectionCancelDelegate; + plDelegate SetFocusDelegate; + plDelegate KillFocusDelegate; + + plListBox() + {} + plListBox( plWindow * inOwner, int inId=0, WNDPROC inSuperProc=nil ) + : plControl( inOwner, inId, inSuperProc?inSuperProc:_SuperProc ) + { + CHECK(fOwnerWindow); + } + + void OpenWindow( bool visible, bool integral, bool multiSel, bool ownerDrawVariable ) + { + PerformCreateWindowEx + ( + WS_EX_CLIENTEDGE, + nil, + WS_CHILD | WS_BORDER | WS_VSCROLL | WS_CLIPCHILDREN | LBS_NOTIFY | (visible?WS_VISIBLE:0) | (integral?0:LBS_NOINTEGRALHEIGHT) | (multiSel?(LBS_EXTENDEDSEL|LBS_MULTIPLESEL):0) | (ownerDrawVariable?LBS_OWNERDRAWVARIABLE:0), + 0, 0, + 0, 0, + *fOwnerWindow, + (HMENU)fControlID, + plWndCtrls::Instance() + ); + SendMessage( *this, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(0,0) ); + } + + bool InterceptControlCommand( unsigned int message, unsigned int wParam, LONG lParam ) + { + if ( HIWORD(wParam)==LBN_DBLCLK ) {DoubleClickDelegate(); return 1;} + else if( HIWORD(wParam)==LBN_SELCHANGE) {SelectionChangeDelegate(); return 1;} + else if( HIWORD(wParam)==LBN_SELCANCEL) {SelectionCancelDelegate(); return 1;} + else if( HIWORD(wParam)==LBN_SETFOCUS) {SetFocusDelegate(); return 1;} + else if( HIWORD(wParam)==LBN_KILLFOCUS) {KillFocusDelegate(); return 1;} + else return 0; + } + + std::wstring GetString( int index ) const + { + int length = SendMessage(*this,LB_GETTEXTLEN,index,0); + if (length == LB_ERR) + return L""; + std::wstring ch; + ch.resize(length); + length = SendMessage( *this, LB_GETTEXT, index, (LPARAM)ch.data() ); + ch.resize(length); + return ch; + } + void * GetItemData( int index ) + { + return (void*)SendMessage( *this, LB_GETITEMDATA, index, 0 ); + } + void SetItemData( int index, void * value ) + { + SendMessage( *this, LB_SETITEMDATA, index, (LPARAM)value ); + } + int GetCurrent() const + { + return SendMessage( *this, LB_GETCARETINDEX, 0, 0 ); + } + void SetCurrent( int index, bool bScrollIntoView ) + { + SendMessage( *this, LB_SETCURSEL, index, 0 ); + SendMessage( *this, LB_SETCARETINDEX, index, bScrollIntoView ); + } + int GetTop() + { + return SendMessage( *this, LB_GETTOPINDEX, 0, 0 ); + } + void SetTop( int index ) + { + SendMessage( *this, LB_SETTOPINDEX, index, 0 ); + } + void DeleteString( int index ) + { + SendMessage( *this, LB_DELETESTRING, index, 0 ); + } + int GetCount() + { + return SendMessage( *this, LB_GETCOUNT, 0, 0 ); + } + int GetItemHeight( int index ) + { + return SendMessage( *this, LB_GETITEMHEIGHT, index, 0 ); + } + int ItemFromPoint( const plPoint & P ) + { + DWORD result=SendMessage( *this, LB_ITEMFROMPOINT, 0, MAKELPARAM(P.X,P.Y) ); + return HIWORD(result) ? -1 : LOWORD(result); + } + plRect GetItemRect( int index ) + { + RECT R; R.left=R.right=R.top=R.bottom=0; + SendMessage( *this, LB_GETITEMRECT, index, (LPARAM)&R ); + return R; + } + void Empty() + { + SendMessage( *this, LB_RESETCONTENT, 0, 0 ); + } + bool GetSelected( int index ) + { + return SendMessage( *this, LB_GETSEL, index, 0 )?true:false; + } + + int AddString( const wchar_t * C ) + { + return SendMessage( *this, LB_ADDSTRING, 0, (LPARAM)C ); + } + void AddStrings( std::vector & strings ) + { + for (int i=0; i=0) + tempString = GetString(index); + else + tempString = GetText(); + + char *temp = hsWStringToString(tempString.c_str()); + retVal = temp; + delete [] temp; + return retVal; + } + void ISetValue(const char * value) + { + wchar_t *temp = hsStringToWString(value); + SetCurrent(FindString(temp), true); + delete [] temp; + } +}; + +#endif // plListBox_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plProgressBar.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plProgressBar.h new file mode 100644 index 00000000..978867a9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plProgressBar.h @@ -0,0 +1,90 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plProgressBar_h_inc +#define plProgressBar_h_inc + +class plProgressBar : public plControl +{ +public: + DECLARE_WINDOWSUBCLASS(plProgressBar,plControl) + + int fPercent; + int fMax; + + plProgressBar() + {} + plProgressBar( plWindow * inOwner, int inId=0, WNDPROC inSuperProc=nil ) + : plControl( inOwner, inId, inSuperProc?inSuperProc:_SuperProc ) + , fPercent( 0 ) + , fMax( 100 ) + {} + + void OpenWindow( bool Visible ) + { + PerformCreateWindowEx + ( + WS_EX_CLIENTEDGE, + nil, + WS_CHILD | (Visible?WS_VISIBLE:0), + 0, 0, + 0, 0, + *fOwnerWindow, + (HMENU)fControlID, + plWndCtrls::Instance() + ); + SendMessage( *this, PBM_SETRANGE, 0, 100 ); + } + + void SetMax(int inMax) + { + fMax = inMax; + } + void SetProgress( int inCurrent ) + { + int inPercent = (int)((float(inCurrent)/float(Max(fMax,1)))*100.f); + if( inPercent!=fPercent ) + SendMessage( *this, PBM_SETPOS, inPercent, 0 ); + fPercent = inPercent; + } + int GetProgress() const + { + int value = SendMessage( *this, PBM_GETPOS, 0, 0 ); + return value; + } + std::string IGetValue() const + { + char tmp[20]; + sprintf(tmp,"%d",GetProgress()); + return tmp; + } + void ISetValue(const char * value) + { + SetProgress(atoi(value)); + } +}; + + +#endif // plProgressBar_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plRadioButton.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plRadioButton.h new file mode 100644 index 00000000..7298a0ad --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plRadioButton.h @@ -0,0 +1,81 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plRadioButton_h_inc +#define plRadioButton_h_inc + + +class plRadioButton : public plButton +{ + DECLARE_WINDOWSUBCLASS(plRadioButton,plButton) + plRadioButton() + {} + plRadioButton( plWindow * inOwner, int inId=0, plDelegate inClicked=plDelegate(), WNDPROC inSuperProc=nil ) + : plButton( inOwner, inId, inClicked, inSuperProc?inSuperProc:_SuperProc ) + {} + void OpenWindow( bool visible, int X, int Y, int XL, int YL, const wchar_t * text ) + { + PerformCreateWindowEx + ( + 0, + nil, + WS_CHILD|BS_RADIOBUTTON, + X, Y, + XL, YL, + *fOwnerWindow, + (HMENU)fControlID, + plWndCtrls::Instance() + ); + SendMessage( *this, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(0,0) ); + SetText( text ); + if( visible ) + ShowWindow( *this, SW_SHOWNOACTIVATE ); + } + void Check(bool check=true) + { + SendMessage(*this,BM_SETCHECK,(check)?BST_CHECKED:BST_UNCHECKED,0); + } + bool IsChecked() const + { + return (SendMessage(*this,BM_GETCHECK,0,0)==BST_CHECKED); + } + std::string IGetValue() const + { + char tmp[20]; + sprintf(tmp,"%s",IsChecked()?"true":"false"); + return tmp; + } + void ISetValue(const char * value) + { + if (stricmp(value,"true")==0) + Check(true); + else if (stricmp(value,"false")==0) + Check(false); + else + Check(atoi(value)?true:false); + } +}; + +#endif // plRadioButton_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plStatusBar.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plStatusBar.h new file mode 100644 index 00000000..168b0c7e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plStatusBar.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plStatusBar_h_inc +#define plStatusBar_h_inc + + +class plStatusBar : public plControl +{ +public: + DECLARE_WINDOWSUBCLASS(plStatusBar,plControl) + + plStatusBar() + {} + plStatusBar( plWindow * inOwner, int inId=0, WNDPROC inSuperProc=nil ) + : plControl( inOwner, inId, inSuperProc?inSuperProc:_SuperProc ) + {} + + void OpenWindow( plWindow * inOwner, bool Visible ) + { + if (inOwner) + { + if (fOwnerWindow) + { + std::vector::iterator it = std::find(fOwnerWindow->fControls.begin(),fOwnerWindow->fControls.end(),this); + if (it!=fOwnerWindow->fControls.end()) + fOwnerWindow->fControls.erase(it); + } + fOwnerWindow = inOwner; + fOwnerWindow->fControls.push_back( this ); + } + CHECK(fOwnerWindow); + LONG style = WS_CHILD|WS_BORDER; + if (Visible) + style |= WS_VISIBLE; +#if UNICODE + fhWnd = CreateStatusWindow(style, + L"",*fOwnerWindow,fControlID); +#else + fhWnd = CreateStatusWindow(style, + "",*fOwnerWindow,fControlID); +#endif + } +}; + +#endif // plStatusBar_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plTrackBar.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plTrackBar.h new file mode 100644 index 00000000..7ca11124 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plTrackBar.h @@ -0,0 +1,102 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plTrackBar_h_inc +#define plTrackBar_h_inc + + +class plTrackBar : public plControl +{ +public: + DECLARE_WINDOWSUBCLASS(plTrackBar,plControl) + + plDelegate fThumbTrackDelegate; + plDelegate fThumbPositionDelegate; + + plTrackBar() + {} + plTrackBar( plWindow * inOwner, int inId=0, WNDPROC inSuperProc=nil ) + : plControl( inOwner, inId, inSuperProc?inSuperProc:_SuperProc ) + {} + + void OpenWindow( bool Visible ) + { + PerformCreateWindowEx + ( + WS_EX_CLIENTEDGE, + nil, + WS_CHILD | TBS_HORZ | TBS_AUTOTICKS | TBS_BOTTOM | (Visible?WS_VISIBLE:0), + 0, 0, + 0, 0, + *fOwnerWindow, + (HMENU)fControlID, + plWndCtrls::Instance() + ); + } + + bool InterceptControlCommand( unsigned int Message, unsigned int wParam, LONG lParam ) + { + if ( Message==WM_HSCROLL && LOWORD(wParam)==TB_THUMBTRACK ) {fThumbTrackDelegate(); return 1;} + else if( Message==WM_HSCROLL ) {fThumbPositionDelegate(); return 1;} + else return 0; + } + + void SetTicFreq( int TicFreq ) + { + SendMessage( *this, TBM_SETTICFREQ, TicFreq, 0 ); + } + void SetRange( int Min, int Max ) + { + SendMessage( *this, TBM_SETRANGE, 1, MAKELONG(Min,Max) ); + } + void SetPos( int Pos ) + { + SendMessage( *this, TBM_SETPOS, 1, Pos ); + } + int GetRangeMin() + { + return SendMessage(*this, TBM_GETRANGEMIN, 0, 0); + } + int GetRangeMax() + { + return SendMessage(*this, TBM_GETRANGEMAX, 0, 0); + } + int GetPos() const + { + return SendMessage( *this, TBM_GETPOS, 0, 0 ); + } + std::string IGetValue() const + { + char tmp[20]; + sprintf(tmp,"%d",GetPos()); + return tmp; + } + void ISetValue(const char * value) + { + SetPos(atoi(value)); + } +}; + +#endif // plTrackBar_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWindow.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWindow.h new file mode 100644 index 00000000..610021d1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWindow.h @@ -0,0 +1,661 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plWindow_h_inc +#define plWindow_h_inc + +class plControl; + +#include + + +class plWindow +: public plClass // plClass _must_ be the first base class in list. +, public plConfigValueBase +{ +public: + HWND fhWnd; + WORD fControlID; + WORD fTopControlID; + bool fDestroyed; + bool fMdiChild; + plWindow * fOwnerWindow; + std::vector fControls; + HWND & Handle() { return fhWnd;} + const HWND & Handle() const { return fhWnd;} + bool fEdited; + + static int _ModalCount; + static std::vector _Windows; + static std::vector _DeleteWindows; + static LONG APIENTRY StaticWndProc(HWND hWnd, unsigned int message, unsigned int wParam, LONG lParam ) + { + // look for this hwnd in window list + int i; + for(i=0; i<_Windows.size(); i++ ) + if( _Windows[i]->Handle()==hWnd ) + break; + if (i==_Windows.size()) + { +// hsStatusMessage("hwnd not found in _Windows.\n"); + } + + // if window not found and this is WM_NCCREATE or WM_INITDIALOG msg... + if( i==_Windows.size() && (message==WM_NCCREATE || message==WM_INITDIALOG || message==WM_ACTIVATE) ) + { + // get the plWindow object + plWindow * WindowCreate + = message!=WM_NCCREATE + ? (plWindow*)lParam + : (GetWindowLong(hWnd,GWL_EXSTYLE) & WS_EX_MDICHILD) + ? (plWindow*)((MDICREATESTRUCT*)((CREATESTRUCT*)lParam)->lpCreateParams)->lParam + : (plWindow*)((CREATESTRUCT*)lParam)->lpCreateParams; + CHECK(WindowCreate); + CHECK(!WindowCreate->Handle()); + + // set the hwnd for this plWindow + WindowCreate->Handle() = hWnd; + // look for this plWindow in window list + for( i=0; i<_Windows.size(); i++ ) + if( _Windows[i]==WindowCreate ) + break; + if (i==_Windows.size()) + { + hsStatusMessage("plWindow not found in _Windows.\n"); + } + CHECK(i<_Windows.size()); + } + // if window not found and message is not WM_NCCREATE or WM_INITDIALOG msg... + if( i==_Windows.size() ) + { + // Gets through before WM_NCCREATE: WM_GETMINMAXINFO + return DefWindowProc( hWnd, message, wParam, lParam ); + } + else + { + // call the plWindow class's message handler + return _Windows[i]->WndProc( message, wParam, lParam ); + } + } + static WNDPROC RegisterWindowClass( const wchar_t * name, DWORD style, int iconId=0 ) + { +#ifdef UNICODE + WNDCLASSEX cls; + HSMemory::ClearMemory( &cls, sizeof(cls) ); + cls.cbSize = sizeof(cls); + cls.style = style; + cls.lpfnWndProc = StaticWndProc; + cls.hInstance = plWndCtrls::Instance(); + cls.hIcon = LoadIcon(plWndCtrls::Instance(),MAKEINTRESOURCE(iconId)); + cls.lpszClassName = name; + cls.hIconSm = LoadIcon(plWndCtrls::Instance(),MAKEINTRESOURCE(iconId)); + CHECK(RegisterClassEx( &cls )); +#else + char *cName = hsWStringToString(name); + WNDCLASSEX cls; + HSMemory::ClearMemory( &cls, sizeof(cls) ); + cls.cbSize = sizeof(cls); + cls.style = style; + cls.lpfnWndProc = StaticWndProc; + cls.hInstance = plWndCtrls::Instance(); + cls.hIcon = LoadIcon(plWndCtrls::Instance(),MAKEINTRESOURCE(iconId)); + cls.lpszClassName = cName; + cls.hIconSm = LoadIcon(plWndCtrls::Instance(),MAKEINTRESOURCE(iconId)); + CHECK(RegisterClassEx( &cls )); + delete [] cName; +#endif + return nil; + } + + plWindow( plWindow * ownerWindow=nil ) + : fhWnd (nil) + , fControlID (0) + , fTopControlID (FIRST_AUTO_CONTROL) + , fDestroyed (0) + , fMdiChild (0) + , fOwnerWindow (ownerWindow) + , fEdited (false) + { + fReadEvaluate = plEvaluate(this,(TEvaluate)HasConfigName); + fWriteEvaluate = plEvaluate(this,(TEvaluate)HasConfigName); + } + virtual ~plWindow() + { + MaybeDestroy(); + std::vector::iterator it = std::find(_DeleteWindows.begin(),_DeleteWindows.end(),this); + if (it!=_DeleteWindows.end()) + _DeleteWindows.erase(it); + } + + plRect GetClientRect() const + { + RECT R; + ::GetClientRect( Handle(), &R ); + return plRect( R ); + } + plRect GetUpdateRect(bool erase) const + { + RECT R; + ::GetUpdateRect(*this,&R,erase); + return plRect(R); + } + void MoveWindow( plRect R, bool bRepaint ) + { + ::MoveWindow( Handle(), R.Min.X, R.Min.Y, R.Width(), R.Height(), bRepaint ); + } + plRect GetWindowRect() const + { + RECT R; + ::GetWindowRect( Handle(), &R ); + return fOwnerWindow ? fOwnerWindow->ScreenToClient(R) : plRect(R); + } + plPoint ClientToScreen( const plPoint& inP ) + { + POINT P; + P.x = inP.X; + P.y = inP.Y; + ::ClientToScreen( Handle(), &P ); + return plPoint( P.x, P.y ); + } + plPoint ScreenToClient( const plPoint& inP ) + { + POINT P; + P.x = inP.X; + P.y = inP.Y; + ::ScreenToClient( Handle(), &P ); + return plPoint( P.x, P.y ); + } + plRect ClientToScreen( const plRect& inR ) + { + return plRect( ClientToScreen(inR.Min), ClientToScreen(inR.Max) ); + } + plRect ScreenToClient( const plRect& inR ) + { + return plRect( ScreenToClient(inR.Min), ScreenToClient(inR.Max) ); + } + plPoint GetCursorPos() + { + plPoint mouse; + ::GetCursorPos( mouse ); + return ScreenToClient( mouse ); + } + void Show( bool show = true ) + { + ShowWindow( Handle(), show ? SW_SHOW : SW_HIDE ); + } + void ShowHow( int how ) + { + ShowWindow( Handle(), how ); + } + void Hide() + { + Show(false); + } + bool IsVisible() + { + return ::IsWindowVisible(*this)?true:false; + } + + virtual void DoDestroy() + { + if( Handle() ) + DestroyWindow( *this ); + std::vector::iterator it = std::find(_Windows.begin(),_Windows.end(),this); + if (it!=_Windows.end()) + _Windows.erase(it); + } + virtual void GetWindowClassName( wchar_t * result )=0; + virtual LONG WndProc( unsigned int message, unsigned int wParam, LONG lParam ) + { + try + { + if( message==WM_DESTROY ) + { + OnDestroy(); + } + else if( message==WM_DRAWITEM ) + { + DRAWITEMSTRUCT * Info = (DRAWITEMSTRUCT*)lParam; + for( int i=0; iHandle()==Info->hwndItem ) + {((plWindow*)fControls[i])->OnDrawItem(Info); break;} + return 1; + } + else if( message==WM_MEASUREITEM ) + { + MEASUREITEMSTRUCT * Info = (MEASUREITEMSTRUCT*)lParam; + for( int i=0; ifControlID==Info->CtlID ) + {((plWindow*)fControls[i])->OnMeasureItem(Info); break;} + return 1; + } + else if ( message==WM_NOTIFY ) + { + OnNotify((int)wParam,(LPNMHDR)lParam); + } + else if( message==WM_CLOSE ) + { + OnClose(); + } + else if( message==WM_CHAR ) + { + if (!OnChar( wParam )) // give the control a chance to filter input + return FALSE; + } + else if( message==WM_KEYDOWN ) + { + OnKeyDown( wParam ); + } + else if( message==WM_KEYUP ) + { + OnKeyUp( wParam ); + } + else if( message==WM_PAINT ) + { + OnPaint(); + } + else if( message==WM_CREATE ) + { + OnCreate(); + } + else if( message==WM_TIMER ) + { + OnTimer( (int)wParam ); + } + else if( message==WM_INITDIALOG ) + { + OnInitDialog(); + } + else if( message==WM_SETFOCUS ) + { + OnSetFocus( (HWND)wParam ); + } + else if( message==WM_ACTIVATE ) + { + OnActivate( LOWORD(wParam)!=0 ); + } + else if( message==WM_KILLFOCUS ) + { + OnKillFocus( (HWND)wParam ); + } + else if( message==WM_SIZE ) + { + OnSize( wParam, LOWORD(lParam), HIWORD(lParam) ); + } + else if( message==WM_PASTE ) + { + OnPaste(); + } + else if( message==WM_SHOWWINDOW ) + { + OnShowWindow( wParam?true:false ); + } + else if( message==WM_COPYDATA ) + { + OnCopyData( (HWND)wParam, (COPYDATASTRUCT*)lParam ); + } + else if( message==WM_CAPTURECHANGED ) + { + OnReleaseCapture(); + } + else if( message==WM_MDIACTIVATE ) + { + OnMdiActivate( (HWND)lParam==Handle()); + } + else if( message==WM_MOUSEMOVE ) + { + OnMouseMove( wParam, plPoint(LOWORD(lParam), HIWORD(lParam)) ); + } + else if( message==WM_LBUTTONDOWN ) + { + OnLeftButtonDown(); + } + else if( message==WM_RBUTTONDOWN ) + { + OnRightButtonDown(); + } + else if( message==WM_LBUTTONUP ) + { + OnLeftButtonUp(); + } + else if( message==WM_RBUTTONUP ) + { + OnRightButtonUp(); + } + else if( message==WM_CUT ) + { + OnCut(); + } + else if( message==WM_COPY ) + { + OnCopy(); + } + else if( message==WM_UNDO ) + { + OnUndo(); + } + else if( message==WM_SETCURSOR ) + { + if( OnSetCursor() ) + return 1; + } + else if( message==WM_COMMAND || message==WM_HSCROLL || message==WM_VSCROLL ) + { + for( int i=0; iHandle() + && ((plWindow*)fControls[i])->InterceptControlCommand(message,wParam,lParam) ) + return 1; + OnCommand( wParam ); + } + else if ( message==WM_SYSCOMMAND ) + { + OnSysCommand( wParam ); + } + return CallDefaultProc( message, wParam, lParam ); + } + catch( const char * e ) + { + hsStatusMessage( e ); + hsStatusMessage("\n"); + return 0; + } + } + virtual int CallDefaultProc( unsigned int message, unsigned int wParam, LONG lParam ) + { + if( fMdiChild ) + return DefMDIChildProc( Handle(), message, wParam, lParam ); + else + return DefWindowProc( Handle(), message, wParam, lParam ); + } + virtual bool InterceptControlCommand( unsigned int message, unsigned int wParam, LONG lParam ) + { + return 0; + } + virtual std::wstring GetText() const + { + CHECK(Handle()); + int length = GetLength(); + std::wstring result; + if (length==0) + return result; + result.resize(length); + SendMessage( *this, WM_GETTEXT, length+1, (LPARAM)result.data() ); + return result; + } + virtual void SetText( const wchar_t * text ) + { + CHECK(Handle()); + SendMessage( *this, WM_SETTEXT, 0, (LPARAM)text ); + } + virtual void SetTextF( const wchar_t * fmt, ... ) + { + va_list args; + va_start( args, fmt ); + SetTextV( fmt, args ); + va_end( args ); + } + virtual void SetTextV( const wchar_t * fmt, va_list args ) + { + std::wstring s; + xtl::formatv( s, fmt, args ); + SetText( s.c_str() ); + } + virtual void SetEnabled(bool enabled) + { + CHECK(Handle()); + EnableWindow(*this,enabled); + } + virtual bool IsEnabled() + { + return IsWindowEnabled(*this)?true:false; + } + virtual int GetLength() const + { + CHECK(Handle()); + return SendMessage( *this, WM_GETTEXTLENGTH, 0, 0 ); + } + virtual void SetFocus() + { + ::SetFocus(*this); + } + virtual void Activate() + { + ::SetActiveWindow(*this); + } + + // plWindow notifications. + virtual void OnNotify( int idCtrl, LPNMHDR pnmh ) + {} + virtual void OnCopyData( HWND hWndSender, COPYDATASTRUCT * CD ) + {} + virtual void OnSetFocus( HWND hWndLosingFocus ) + {} + virtual void OnKillFocus( HWND hWndGainingFocus ) + {} + virtual void OnSize( DWORD flags, int newX, int newY ) + {} + virtual void OnCommand( int command ) + {} + virtual void OnSysCommand( int command ) + {} + virtual void OnActivate( bool active ) + {} + virtual LONG OnChar( char ch ) // Return TRUE if you want to let the default handler grab it, FALSE if you don't + {return TRUE;} + virtual void OnKeyDown( UInt16 ch ) + {} + virtual void OnKeyUp( UInt16 ch ) + {} + virtual void OnCut() + {} + virtual void OnCopy() + {} + virtual void OnPaste() + {} + virtual void OnShowWindow( bool bShow ) + {} + virtual void OnUndo() + {} + virtual void OnPaint() + {} + virtual void OnCreate() + {} + virtual void OnDrawItem( DRAWITEMSTRUCT * info ) + {} + virtual void OnMeasureItem( MEASUREITEMSTRUCT * info ) + {} + virtual void OnInitDialog() + {} + virtual void OnMouseEnter() + {} + virtual void OnMouseLeave() + {} + virtual void OnMouseHover() + {} + virtual void OnTimer( int timer ) + {} + virtual void OnReleaseCapture() + {} + virtual void OnMdiActivate( bool active ) + {} + virtual void OnMouseMove( DWORD flags, plPoint location ) + {} + virtual void OnLeftButtonDown() + {} + virtual void OnRightButtonDown() + {} + virtual void OnLeftButtonUp() + {} + virtual void OnRightButtonUp() + {} + virtual int OnSetCursor() + { + return 0; + } + virtual void OnClose() + { + DestroyWindow( *this ); + } + virtual void OnDestroy() + { + CHECK(Handle()); + std::vector::iterator it = std::find(_Windows.begin(),_Windows.end(),this); + if (it!=_Windows.end()) + _Windows.erase(it); + Handle() = nil; + } + + // plWindow functions. + void MaybeDestroy() + { + if( !fDestroyed ) + { + fDestroyed=1; + DoDestroy(); + } + } + void _CloseWindow() + { + CHECK(Handle()); + DestroyWindow( *this ); + } + operator HWND() const + { + return Handle(); + } + void SetFont( HFONT hFont ) + { + SendMessage( *this, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(0,0) ); + } + void SetSmallIcon( HICON hIcon ) + { + SendMessage( *this, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); + } + void SetBigIcon( HICON hIcon ) + { + SendMessage( *this, WM_SETICON, ICON_BIG, (LPARAM)hIcon); + } + void PerformCreateWindowEx( + DWORD dwExStyle, + LPCTSTR lpWindowName, + DWORD dwStyle, + int x, int y, + int nWidth, int nHeight, + HWND hWndParent, + HMENU hMenu, + HINSTANCE hInstance) + { + CHECK(Handle()==nil); + _Windows.push_back( this ); + + wchar_t className[256]; + GetWindowClassName( className ); +#ifdef UNICODE + HWND hWndCreated = CreateWindowEx( + dwExStyle, + className, + lpWindowName, + dwStyle,x,y, + nWidth,nHeight, + hWndParent,hMenu, + plWndCtrls::Instance(), + this); +#else + char *cClassName = hsWStringToString(className); + HWND hWndCreated = CreateWindowEx( + dwExStyle, + cClassName, + lpWindowName, + dwStyle,x,y, + nWidth,nHeight, + hWndParent,hMenu, + plWndCtrls::Instance(), + this); + delete [] cClassName; +#endif + +/* +#define SAFE(s) ((s)?s:"null") + + std::strstream str; + str + << "vars: " << std::endl + << dwExStyle << std::endl + << SAFE(className) << std::endl + << SAFE(lpWindowName) << std::endl + << dwStyle << std::endl + << x << std::endl + << y << std::endl + << nWidth << std::endl + << nHeight << std::endl + << hWndParent << std::endl + << hMenu << std::endl + << plWndCtrls::Instance() << std::endl + << '\0'; + MessageBox(nil,str.str(),"",MB_OK); + str.freeze(false); +*/ + + if( !hWndCreated ) + hsStatusMessage( "CreateWindowEx failed" ); + CHECK(hWndCreated); + CHECK(hWndCreated==Handle()); + } + void SetRedraw( bool redraw ) + { + SendMessage( *this, WM_SETREDRAW, redraw, 0 ); + } + + // plConfigValueBase + std::string IGetValue() const + { + std::string sText = ""; + char *temp = hsWStringToString(GetText().c_str()); + sText = temp; + delete [] temp; + return sText; + } + void ISetValue(const char * value) + { + wchar_t *wValue = hsStringToWString(value); + SetText(wValue); + delete [] wValue; + } + virtual void SetEdited(bool value) + { + fEdited = value; + } + virtual bool Edited() const + { + return fEdited; + } +}; + + + + + +#endif plWindow_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.cpp new file mode 100644 index 00000000..9fa05925 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.cpp @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" + +#include "plWndCtrls.h" +#include "basewnd.h" + +#if HS_BUILD_FOR_WIN32 + +//////////////////////////////////////////////////////////////////// + +int plWindow::_ModalCount = 0; +std::vector plWindow::_Windows; +std::vector plWindow::_DeleteWindows; + +//////////////////////////////////////////////////////////////////// + +WNDPROC plButton::_SuperProc = nil; +WNDPROC plCheckBox::_SuperProc = nil; +WNDPROC plRadioButton::_SuperProc = nil; +WNDPROC plEdit::_SuperProc = nil; +WNDPROC plLabel::_SuperProc = nil; +WNDPROC plProgressBar::_SuperProc = nil; +WNDPROC plTrackBar::_SuperProc = nil; +WNDPROC plComboBox::_SuperProc = nil; +WNDPROC plListBox::_SuperProc = nil; +WNDPROC plStatusBar::_SuperProc = nil; + +//////////////////////////////////////////////////////////////////// + +HINSTANCE plWndCtrls::hInstance = nil; +DWORD plWndCtrls::fLanguage = 0; + +//////////////////////////////////////////////////////////////////// + +void plWndCtrls::Init(HINSTANCE hInst) +{ + hInstance = hInst; + + CoInitialize(NULL); + + INITCOMMONCONTROLSEX icex; + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_BAR_CLASSES; + InitCommonControlsEx(&icex); + + REGISTER_WINDOWSUBCLASS(plEdit,L"EDIT"); + REGISTER_WINDOWSUBCLASS(plButton,L"BUTTON"); + REGISTER_WINDOWSUBCLASS(plCheckBox,L"BUTTON"); + REGISTER_WINDOWSUBCLASS(plRadioButton,L"BUTTON"); + REGISTER_WINDOWSUBCLASS(plLabel,L"STATIC"); + REGISTER_WINDOWSUBCLASS(plComboBox,L"COMBOBOX"); + REGISTER_WINDOWSUBCLASS(plListBox,L"LISTBOX"); + REGISTER_WINDOWSUBCLASS(plTrackBar,TRACKBAR_CLASS); + REGISTER_WINDOWSUBCLASS(plProgressBar,PROGRESS_CLASS); + REGISTER_WINDOWSUBCLASS(plStatusBar,STATUSCLASSNAME); + + basewnd::Initialize( hInst ); +} + +void plWndCtrls::Shutdown() +{ + CoUninitialize(); +} + + +//////////////////////////////////////////////////////////////////// +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.h new file mode 100644 index 00000000..14b9e005 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/plWndCtrls.h @@ -0,0 +1,307 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plWndCtrls_h_inc +#define plWndCtrls_h_inc + +#include "hsConfig.h" + + +#if HS_BUILD_FOR_WIN32 + +#include "hsUtils.h" +#include "hsMemory.h" +#include "../plContainer/plConfigInfo.h" + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// + +class plWndCtrls +{ +public: + static void Init(HINSTANCE hInst); + static void Shutdown(); + static HINSTANCE Instance() { return hInstance;} + static void MakeWndClassName(wchar_t * result, const wchar_t * base) + { + swprintf(result, L"Plasma_%s", base); + } + static void SetLanguage(DWORD lang) { fLanguage = lang; } + static DWORD GetLanguage() { return fLanguage; } + +private: + static HINSTANCE hInstance; + static DWORD fLanguage; + plWndCtrls(); + plWndCtrls & operator=(const plWndCtrls &); +}; + +//////////////////////////////////////////////////////////////////// + +#define CHECK(c) hsAssert(c,#c) +#define N_ELEMENTS(a) ( sizeof(a) / sizeof((a)[0]) ) + +template inline T Max(const T & A, const T & B){return (A>=B)?A:B;} +template inline T Min(const T & A, const T & B){return (A<=B)?A:B;} + + +//////////////////////////////////////////////////////////////////// +// Window class definition macros. + + +#define DECLARE_WINDOWCLASS(cls,parentcls) \ +public: \ + void GetWindowClassName( wchar_t * result ) {plWndCtrls::MakeWndClassName(result,L#cls);} + +#define DECLARE_WINDOWSUBCLASS(cls,parentcls) \ + DECLARE_WINDOWCLASS(cls,parentcls) \ + static WNDPROC _SuperProc; + +#define REGISTER_WINDOWCLASS(cls,clsf) \ + {wchar_t Temp[256]; plWndCtrls::MakeWndClassName(Temp,L#cls); cls::RegisterWindowClass( Temp, clsf );} + +#define REGISTER_WINDOWCLASSWITHICON(cls,clsf,iconid) \ + {wchar_t Temp[256]; plWndCtrls::MakeWndClassName(Temp,L#cls); cls::RegisterWindowClass( Temp, clsf, iconid );} + +#define REGISTER_WINDOWSUBCLASS(cls,wincls) \ + {wchar_t Temp[256]; plWndCtrls::MakeWndClassName(Temp,L#cls); cls::_SuperProc = cls::RegisterWindowClass( Temp, wincls );} + +#define FIRST_AUTO_CONTROL 8192 + + +//////////////////////////////////////////////////////////////////// + +struct plPoint +{ + int X, Y; + plPoint() + {} + plPoint( int InX, int InY ) + : X( InX ) + , Y( InY ) + {} + static plPoint ZeroValue() + { + return plPoint(0,0); + } + static plPoint NoneValue() + { + return plPoint(-1,-1); + } + operator POINT*() const + { + return (POINT*)this; + } + const int& operator()( int i ) const + { + return (&X)[i]; + } + int& operator()( int i ) + { + return (&X)[i]; + } + static int Num() + { + return 2; + } + bool operator==( const plPoint& Other ) const + { + return X==Other.X && Y==Other.Y; + } + bool operator!=( const plPoint& Other ) const + { + return X!=Other.X || Y!=Other.Y; + } + plPoint& operator+=( const plPoint& Other ) + { + X += Other.X; + Y += Other.Y; + return *this; + } + plPoint& operator-=( const plPoint& Other ) + { + X -= Other.X; + Y -= Other.Y; + return *this; + } + plPoint operator+( const plPoint& Other ) const + { + return plPoint(*this) += Other; + } + plPoint operator-( const plPoint& Other ) const + { + return plPoint(*this) -= Other; + } +}; + +//////////////////////////////////////////////////////////////////// + +struct plRect +{ + plPoint Min, Max; + plRect() + {} + plRect( int X0, int Y0, int X1, int Y1 ) + : Min( X0, Y0 ) + , Max( X1, Y1 ) + {} + plRect( plPoint InMin, plPoint InMax ) + : Min( InMin ) + , Max( InMax ) + {} + plRect( RECT R ) + : Min( R.left, R.top ) + , Max( R.right, R.bottom ) + {} + operator RECT*() const + { + return (RECT*)this; + } + const plPoint& operator()( int i ) const + { + return (&Min)[i]; + } + plPoint& operator()( int i ) + { + return (&Min)[i]; + } + bool operator==( const plRect& Other ) const + { + return Min==Other.Min && Max==Other.Max; + } + bool operator!=( const plRect& Other ) const + { + return Min!=Other.Min || Max!=Other.Max; + } + plRect Right( int Width ) + { + return plRect( ::Max(Min.X,Max.X-Width), Min.Y, Max.X, Max.Y ); + } + plRect Bottom( int Height ) + { + return plRect( Min.X, ::Max(Min.Y,Max.Y-Height), Max.X, Max.Y ); + } + plPoint Size() + { + return plPoint( Max.X-Min.X, Max.Y-Min.Y ); + } + void Resize(plPoint size) + { + Max.X=Min.X+size.X; + Max.Y=Min.Y+size.Y; + } + int Width() + { + return Max.X-Min.X; + } + int Height() + { + return Max.Y-Min.Y; + } + plRect& operator+=( const plPoint& P ) + { + Min += P; + Max += P; + return *this; + } + plRect& operator-=( const plPoint& P ) + { + Min -= P; + Max -= P; + return *this; + } + plRect operator+( const plPoint& P ) const + { + return plRect( Min+P, Max+P ); + } + plRect operator-( const plPoint& P ) const + { + return plRect( Min-P, Max-P ); + } + plRect operator+( const plRect& R ) const + { + return plRect( Min+R.Min, Max+R.Max ); + } + plRect operator-( const plRect& R ) const + { + return plRect( Min-R.Min, Max-R.Max ); + } + plRect Inner( plPoint P ) const + { + return plRect( Min+P, Max-P ); + } + bool Contains( plPoint P ) const + { + return P.X>=Min.X && P.X=Min.Y && P.Y*fDelegate)();} } +}; + +//////////////////////////////////////////////////////////////////// +// include private headers + +// 'this' : used in base member initializer list +#pragma warning(disable:4355) + +#include "plWindow.h" +#include "plControl.h" +#include "plLabel.h" +#include "plButton.h" +#include "plCheckBox.h" +#include "plRadioButton.h" +#include "plComboBox.h" +#include "plEdit.h" +#include "plButton.h" +#include "plDialog.h" +#include "plTrackBar.h" +#include "plProgressBar.h" +#include "plListBox.h" +#include "plStatusBar.h" + +#pragma warning(default:4355) + +//////////////////////////////////////////////////////////////////// +#endif // HS_BUILD_FOR_WIN32 +#endif // plWndCtrls_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/webhost.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/webhost.cpp new file mode 100644 index 00000000..2303ca26 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/webhost.cpp @@ -0,0 +1,495 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsConfig.h" +#if HS_BUILD_FOR_WIN32 + +// Stolen from: http://www.mvps.org/user32/webhost.cab +// No copyright notices, so I assume it's public domain -Colin +#include "webhost.h" +#include "plWndCtrls.h" + +// IUnknown + +STDMETHODIMP CNullStorage::QueryInterface(REFIID riid,void ** ppvObject) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP_(ULONG) CNullStorage::AddRef(void) +{ + return 1; +} + +STDMETHODIMP_(ULONG) CNullStorage::Release(void) +{ + return 1; +} + + +// IStorage +STDMETHODIMP CNullStorage::CreateStream(const WCHAR * pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2,IStream ** ppstm) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::OpenStream(const WCHAR * pwcsName,void * reserved1,DWORD grfMode,DWORD reserved2,IStream ** ppstm) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::CreateStorage(const WCHAR * pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2,IStorage ** ppstg) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::OpenStorage(const WCHAR * pwcsName,IStorage * pstgPriority,DWORD grfMode,SNB snbExclude,DWORD reserved,IStorage ** ppstg) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::CopyTo(DWORD ciidExclude,IID const * rgiidExclude,SNB snbExclude,IStorage * pstgDest) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::MoveElementTo(const OLECHAR * pwcsName,IStorage * pstgDest,const OLECHAR* pwcsNewName,DWORD grfFlags) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::Commit(DWORD grfCommitFlags) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::Revert(void) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::EnumElements(DWORD reserved1,void * reserved2,DWORD reserved3,IEnumSTATSTG ** ppenum) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::DestroyElement(const OLECHAR * pwcsName) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::RenameElement(const WCHAR * pwcsOldName,const WCHAR * pwcsNewName) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::SetElementTimes(const WCHAR * pwcsName,FILETIME const * pctime,FILETIME const * patime,FILETIME const * pmtime) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::SetClass(REFCLSID clsid) +{ + return S_OK; +} + +STDMETHODIMP CNullStorage::SetStateBits(DWORD grfStateBits,DWORD grfMask) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CNullStorage::Stat(STATSTG * pstatstg,DWORD grfStatFlag) +{ + NOTIMPLEMENTED; +} + + + + +STDMETHODIMP CMySite::QueryInterface(REFIID riid,void ** ppvObject) +{ + if(riid == IID_IUnknown || riid == IID_IOleClientSite) + *ppvObject = (IOleClientSite*)this; + else if(riid == IID_IOleInPlaceSite) // || riid == IID_IOleInPlaceSiteEx || riid == IID_IOleInPlaceSiteWindowless) + *ppvObject = (IOleInPlaceSite*)this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + return S_OK; +} + +STDMETHODIMP_(ULONG) CMySite::AddRef(void) +{ + return 1; +} + +STDMETHODIMP_(ULONG) CMySite::Release(void) +{ + return 1; +} + + +// IOleClientSite + +STDMETHODIMP CMySite::SaveObject() +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CMySite::GetMoniker(DWORD dwAssign,DWORD dwWhichMoniker,IMoniker ** ppmk) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CMySite::GetContainer(LPOLECONTAINER FAR* ppContainer) +{ + // We are a simple object and don't support a container. + *ppContainer = NULL; + + return E_NOINTERFACE; +} + +STDMETHODIMP CMySite::ShowObject() +{ + // NOTIMPLEMENTED; + // huh? + return NOERROR; +} + +STDMETHODIMP CMySite::OnShowWindow(BOOL fShow) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CMySite::RequestNewObjectLayout() +{ + NOTIMPLEMENTED; +} + +// IOleWindow + +STDMETHODIMP CMySite::GetWindow(HWND FAR* lphwnd) +{ + *lphwnd = host->hwnd; + + return S_OK; +} + +STDMETHODIMP CMySite::ContextSensitiveHelp(BOOL fEnterMode) +{ + NOTIMPLEMENTED; +} + +// IOleInPlaceSite + + +STDMETHODIMP CMySite::CanInPlaceActivate() +{ + // Yes we can + return S_OK; +} + +STDMETHODIMP CMySite::OnInPlaceActivate() +{ + // Why disagree. + return S_OK; +} + +STDMETHODIMP CMySite::OnUIActivate() +{ + return S_OK; +} + +STDMETHODIMP CMySite::GetWindowContext( + LPOLEINPLACEFRAME FAR* ppFrame, + LPOLEINPLACEUIWINDOW FAR* ppDoc, + LPRECT prcPosRect, + LPRECT prcClipRect, + LPOLEINPLACEFRAMEINFO lpFrameInfo) +{ + *ppFrame = &host->frame; + *ppDoc = NULL; + GetClientRect(host->hwnd,prcPosRect); + GetClientRect(host->hwnd,prcClipRect); + + lpFrameInfo->fMDIApp = FALSE; + lpFrameInfo->hwndFrame = host->hwnd; + lpFrameInfo->haccel = NULL; + lpFrameInfo->cAccelEntries = 0; + + return S_OK; +} + +STDMETHODIMP CMySite::Scroll(SIZE scrollExtent) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CMySite::OnUIDeactivate(BOOL fUndoable) +{ + return S_OK; +} + +STDMETHODIMP CMySite::OnInPlaceDeactivate() +{ + return S_OK; +} + +STDMETHODIMP CMySite::DiscardUndoState() +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CMySite::DeactivateAndUndo() +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CMySite::OnPosRectChange(LPCRECT lprcPosRect) +{ + return S_OK; +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// CMyFrame +// + + +// IUnknown +STDMETHODIMP CMyFrame::QueryInterface(REFIID riid,void ** ppvObject) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP_(ULONG) CMyFrame::AddRef(void) +{ + return 1; +} + +STDMETHODIMP_(ULONG) CMyFrame::Release(void) +{ + return 1; +} + +// IOleWindow +STDMETHODIMP CMyFrame::GetWindow(HWND FAR* lphwnd) +{ + *lphwnd = this->host->hwnd; + return S_OK; + // NOTIMPLEMENTED; +} + +STDMETHODIMP CMyFrame::ContextSensitiveHelp(BOOL fEnterMode) +{ + NOTIMPLEMENTED; +} + +// IOleInPlaceUIWindow +STDMETHODIMP CMyFrame::GetBorder(LPRECT lprectBorder) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CMyFrame::RequestBorderSpace(LPCBORDERWIDTHS pborderwidths) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CMyFrame::SetBorderSpace(LPCBORDERWIDTHS pborderwidths) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CMyFrame::SetActiveObject(IOleInPlaceActiveObject *pActiveObject,LPCOLESTR pszObjName) +{ + return S_OK; +} + +// IOleInPlaceFrame +STDMETHODIMP CMyFrame::InsertMenus(HMENU hmenuShared,LPOLEMENUGROUPWIDTHS lpMenuWidths) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CMyFrame::SetMenu(HMENU hmenuShared,HOLEMENU holemenu,HWND hwndActiveObject) +{ + return S_OK; +} + +STDMETHODIMP CMyFrame::RemoveMenus(HMENU hmenuShared) +{ + NOTIMPLEMENTED; +} + +STDMETHODIMP CMyFrame::SetStatusText(LPCOLESTR pszStatusText) +{ + return S_OK; +} + +STDMETHODIMP CMyFrame::EnableModeless(BOOL fEnable) +{ + return S_OK; +} + +STDMETHODIMP CMyFrame::TranslateAccelerator( LPMSG lpmsg,WORD wID) +{ + NOTIMPLEMENTED; +} + + + +webhostwnd::webhostwnd() +{ + site.host = this; + frame.host = this; +} + +webhostwnd::~webhostwnd() +{ +} + +HWND webhostwnd::operator =(webhostwnd* rhs) +{ + return hwnd; +} + + +webhostwnd* webhostwnd::Create(HWND hParent, RECT& rect) +{ + webhostwnd* _this = TRACKED_NEW webhostwnd; + + CreateWindowEx( + 0, + szClassName,L"TheWebbyWindow", + WS_CHILD, + rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top, + hParent,NULL,plWndCtrls::Instance(),_this); + + int err = GetLastError(); + + return _this; +} + + +BOOL webhostwnd::HandleMessage(UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT* r) +{ + if(uMsg == WM_DESTROY) + { + UnCreateEmbeddedWebControl(); + PostQuitMessage(0); + return TRUE; + } + else if(uMsg == WM_CREATE) + { + CreateEmbeddedWebControl(); + return TRUE; + } + + return FALSE; +} + + +void webhostwnd::CreateEmbeddedWebControl(void) +{ + OleCreate(CLSID_WebBrowser,IID_IOleObject,OLERENDER_DRAW,0,&site,&storage,(void**)&mpWebObject); + + mpWebObject->SetHostNames(L"Web Host",L"Web View"); + + // I have no idea why this is necessary. remark it out and everything works perfectly. + OleSetContainedObject(mpWebObject,TRUE); + + RECT rect; + GetClientRect(hwnd,&rect); + + mpWebObject->DoVerb(OLEIVERB_SHOW,NULL,&site,-1,hwnd,&rect); + + IWebBrowser2* iBrowser; + mpWebObject->QueryInterface(IID_IWebBrowser2,(void**)&iBrowser); + + VARIANT vURL; + vURL.vt = VT_BSTR; + vURL.bstrVal = SysAllocString(L"about:blank"); + VARIANT ve1, ve2, ve3, ve4; + ve1.vt = VT_EMPTY; + ve2.vt = VT_EMPTY; + ve3.vt = VT_EMPTY; + ve4.vt = VT_EMPTY; + + iBrowser->put_Left(0); + iBrowser->put_Top(0); + iBrowser->put_Width(rect.right); + iBrowser->put_Height(rect.bottom); + + iBrowser->Navigate2(&vURL, &ve1, &ve2, &ve3, &ve4); + + VariantClear(&vURL); + + iBrowser->Release(); +} + + +void webhostwnd::UnCreateEmbeddedWebControl(void) +{ + mpWebObject->Close(OLECLOSE_NOSAVE); + mpWebObject->Release(); +} + +#include + +void webhostwnd::GoToPage(const char* address) +{ + IWebBrowser2* iBrowser; + mpWebObject->QueryInterface(IID_IWebBrowser2,(void**)&iBrowser); + + + _bstr_t s(address); + + VARIANT vURL; + vURL.vt = VT_BSTR; + vURL.bstrVal = SysAllocString(s); + VARIANT vHeaders; + vHeaders.vt = VT_EMPTY; + + VARIANT ve1, ve2, ve3; + ve1.vt = VT_EMPTY; + ve2.vt = VT_EMPTY; + ve3.vt = VT_EMPTY; + + iBrowser->Navigate2(&vURL, &ve1, &ve2, &ve3, &vHeaders); + + VariantClear(&vURL); + + iBrowser->Release(); +} + +// OleUninitialize(); + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/webhost.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/webhost.h new file mode 100644 index 00000000..3a1b4151 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PubUtilLib/plWndCtrls/webhost.h @@ -0,0 +1,168 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Stolen from: http://www.mvps.org/user32/webhost.cab +// No copyright notices, so I assume it's public domain -Colin +#include +#include "basewnd.h" +#include +#include +#include + +#define NOTIMPLEMENTED __asm{ int 3 }; return E_NOTIMPL + + +struct CNullStorage +: public IStorage +{ + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid,void ** ppvObject); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + // IStorage + STDMETHODIMP CreateStream(const WCHAR * pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2,IStream ** ppstm); + STDMETHODIMP OpenStream(const WCHAR * pwcsName,void * reserved1,DWORD grfMode,DWORD reserved2,IStream ** ppstm); + STDMETHODIMP CreateStorage(const WCHAR * pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2,IStorage ** ppstg); + STDMETHODIMP OpenStorage(const WCHAR * pwcsName,IStorage * pstgPriority,DWORD grfMode,SNB snbExclude,DWORD reserved,IStorage ** ppstg); + STDMETHODIMP CopyTo(DWORD ciidExclude,IID const * rgiidExclude,SNB snbExclude,IStorage * pstgDest); + STDMETHODIMP MoveElementTo(const OLECHAR * pwcsName,IStorage * pstgDest,const OLECHAR* pwcsNewName,DWORD grfFlags); + STDMETHODIMP Commit(DWORD grfCommitFlags); + STDMETHODIMP Revert(void); + STDMETHODIMP EnumElements(DWORD reserved1,void * reserved2,DWORD reserved3,IEnumSTATSTG ** ppenum); + STDMETHODIMP DestroyElement(const OLECHAR * pwcsName); + STDMETHODIMP RenameElement(const WCHAR * pwcsOldName,const WCHAR * pwcsNewName); + STDMETHODIMP SetElementTimes(const WCHAR * pwcsName,FILETIME const * pctime,FILETIME const * patime,FILETIME const * pmtime); + STDMETHODIMP SetClass(REFCLSID clsid); + STDMETHODIMP SetStateBits(DWORD grfStateBits,DWORD grfMask); + STDMETHODIMP Stat(STATSTG * pstatstg,DWORD grfStatFlag); +}; + +struct webhostwnd; + + +struct CMyFrame +: public IOleInPlaceFrame +{ + webhostwnd* host; + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid,void ** ppvObject); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + // IOleWindow + STDMETHODIMP GetWindow(HWND FAR* lphwnd); + STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode); + // IOleInPlaceUIWindow + STDMETHODIMP GetBorder(LPRECT lprectBorder); + STDMETHODIMP RequestBorderSpace(LPCBORDERWIDTHS pborderwidths); + STDMETHODIMP SetBorderSpace(LPCBORDERWIDTHS pborderwidths); + STDMETHODIMP SetActiveObject(IOleInPlaceActiveObject *pActiveObject,LPCOLESTR pszObjName); + // IOleInPlaceFrame + STDMETHODIMP InsertMenus(HMENU hmenuShared,LPOLEMENUGROUPWIDTHS lpMenuWidths); + STDMETHODIMP SetMenu(HMENU hmenuShared,HOLEMENU holemenu,HWND hwndActiveObject); + STDMETHODIMP RemoveMenus(HMENU hmenuShared); + STDMETHODIMP SetStatusText(LPCOLESTR pszStatusText); + STDMETHODIMP EnableModeless(BOOL fEnable); + STDMETHODIMP TranslateAccelerator( LPMSG lpmsg,WORD wID); + +}; + + + +struct CMySite +: public IOleClientSite, +public IOleInPlaceSite +{ + webhostwnd* host; + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid,void ** ppvObject); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + // IOleClientSite + STDMETHODIMP SaveObject(); + STDMETHODIMP GetMoniker(DWORD dwAssign,DWORD dwWhichMoniker,IMoniker ** ppmk); + STDMETHODIMP GetContainer(LPOLECONTAINER FAR* ppContainer); + STDMETHODIMP ShowObject(); + STDMETHODIMP OnShowWindow(BOOL fShow); + STDMETHODIMP RequestNewObjectLayout(); + // IOleWindow + STDMETHODIMP GetWindow(HWND FAR* lphwnd); + STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode); + // IOleInPlaceSite methods + STDMETHODIMP CanInPlaceActivate(); + STDMETHODIMP OnInPlaceActivate(); + STDMETHODIMP OnUIActivate(); + STDMETHODIMP GetWindowContext(LPOLEINPLACEFRAME FAR* lplpFrame,LPOLEINPLACEUIWINDOW FAR* lplpDoc,LPRECT lprcPosRect,LPRECT lprcClipRect,LPOLEINPLACEFRAMEINFO lpFrameInfo); + STDMETHODIMP Scroll(SIZE scrollExtent); + STDMETHODIMP OnUIDeactivate(BOOL fUndoable); + STDMETHODIMP OnInPlaceDeactivate(); + STDMETHODIMP DiscardUndoState(); + STDMETHODIMP DeactivateAndUndo(); + STDMETHODIMP OnPosRectChange(LPCRECT lprcPosRect); +}; + + +struct CMyContainer +: public IOleContainer +{ + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid,void ** ppvObject); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + // IParseDisplayName + STDMETHODIMP ParseDisplayName(IBindCtx *pbc,LPOLESTR pszDisplayName,ULONG *pchEaten,IMoniker **ppmkOut); + // IOleContainer + STDMETHODIMP EnumObjects(DWORD grfFlags,IEnumUnknown **ppenum); + STDMETHODIMP LockContainer(BOOL fLock); +}; + + +struct webhostwnd : public basewnd +{ + webhostwnd(); + ~webhostwnd(); + + static webhostwnd* Create(HWND hParent, RECT& rect); + + virtual BOOL HandleMessage(UINT,WPARAM,LPARAM,LRESULT*); + + void CreateEmbeddedWebControl(void); + void UnCreateEmbeddedWebControl(void); + + HWND operator =(webhostwnd* rhs); + + CNullStorage storage; + CMySite site; + CMyFrame frame; + + IOleObject* mpWebObject; + + void OnPaint(HDC hdc); + void GoToPage(const char* address); +}; + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.cpp new file mode 100644 index 00000000..46854c9a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.cpp @@ -0,0 +1,316 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyNetClientComm.h" +#include "../pfPython/pyAgeLinkStruct.h" +#include "../pfPython/pyNetServerSessionInfo.h" +#include "../pfPython/pyStatusLog.h" +#include "../plNetCommon/plCreatePlayerFlags.h" +#include "../pnNetCommon/plGenericVar.h" +#include "hsStlUtils.h" +#include "hsTimer.h" + +#include + +//////////////////////////////////////////////////////////////////// + +class pyNetClientCommCallback : public plNetClientComm::Callback +{ +public: + PyObject * fPyObject; + pyNetClientCommCallback( PyObject * pyObject ) + : fPyObject( pyObject ) + { + Py_XINCREF( fPyObject ); + } + ~pyNetClientCommCallback() + { + Py_XDECREF( fPyObject ); + } + void OperationStarted( UInt32 context ) + { + if ( fPyObject ) + { + // Call the callback. + PyObject* func = PyObject_GetAttrString( fPyObject, "operationStarted" ); + if ( func ) + { + if ( PyCallable_Check(func)>0 ) + { + PyObject* retVal = PyObject_CallMethod(fPyObject, "operationStarted", "l", context); + Py_XDECREF(retVal); + } + } + } + } + void OperationComplete( UInt32 context, int resultCode ) + { + if ( fPyObject ) + { + // Pass args. + PyObject* pyArgs = PyObject_GetAttrString( fPyObject, "fCbArgs" ); + if ( pyArgs ) + { + PyObject* pyDict = PyDict_New(); + std::map args; + fCbArgs.GetItems( args ); + for ( std::map::iterator ii=args.begin(); ii!=args.end(); ++ii ) + { + UInt16 key = ii->first; + PyObject* keyObj = PyInt_FromLong(key); + char* strTemp = NULL; + plCreatable* arg = ii->second; + plCreatableGenericValue * genValue = plCreatableGenericValue::ConvertNoRef( arg ); + if ( genValue ) + { + PyObject* valueObj; + plGenericType & value = genValue->Value(); + switch ( value.GetType() ) + { + case plGenericType::kInt: + valueObj = PyLong_FromLong((Int32)value); + PyDict_SetItem(pyDict, keyObj, valueObj); + Py_DECREF(valueObj); + break; + case plGenericType::kUInt: + valueObj = PyLong_FromUnsignedLong((UInt32)value); + PyDict_SetItem(pyDict, keyObj, valueObj); + Py_DECREF(valueObj); + break; + case plGenericType::kFloat: + valueObj = PyFloat_FromDouble((float)value); + PyDict_SetItem(pyDict, keyObj, valueObj); + Py_DECREF(valueObj); + break; + case plGenericType::kDouble: + valueObj = PyFloat_FromDouble((double)value); + PyDict_SetItem(pyDict, keyObj, valueObj); + Py_DECREF(valueObj); + break; + case plGenericType::kBool: + if ((bool)value) + valueObj = PyInt_FromLong(1); + else + valueObj = PyInt_FromLong(0); + PyDict_SetItem(pyDict, keyObj, valueObj); + Py_DECREF(valueObj); + break; + case plGenericType::kChar: + strTemp = new char[2]; + strTemp[0] = (char)value; + strTemp[1] = 0; + valueObj = PyString_FromString(strTemp); + PyDict_SetItem(pyDict, keyObj, valueObj); + Py_DECREF(valueObj); + delete [] strTemp; + break; + case plGenericType::kString: + valueObj = PyString_FromString((const char*)value); + PyDict_SetItem(pyDict, keyObj, valueObj); + Py_DECREF(valueObj); + break; + case plGenericType::kAny: + break; + case plGenericType::kNone: + break; + } + } + plNetServerSessionInfo * serverInfo = plNetServerSessionInfo::ConvertNoRef( arg ); + if ( serverInfo ) + { + PyObject* valueObj = pyNetServerSessionInfo::New(*serverInfo); + PyDict_SetItem(pyDict, keyObj, valueObj); + Py_DECREF(valueObj); + } + Py_DECREF(keyObj); + } + PyObject_SetAttrString( fPyObject, "fCbArgs", pyDict ); + Py_DECREF(pyDict); + } + + // Call the callback. + PyObject* func = PyObject_GetAttrString( fPyObject, "operationComplete" ); + if ( func ) + { + if ( PyCallable_Check(func)>0 ) + { + PyObject* retVal = PyObject_CallMethod(fPyObject, "operationComplete", "li", context, resultCode); + Py_XDECREF(retVal); + } + } + } + delete this; + } +}; + +//////////////////////////////////////////////////////////////////// + +// Error handler - throws exception in python script +class pyNetClientCommErrorHandler : public plNetClientComm::ErrorHandler +{ +public: + void HandleError( Error err, int result ) + { + std::string msg; + xtl::format( msg, "pyNetClientComm: Error: %s", plNetClientComm::ErrorHandler::ErrorStr( err ) ); + PyErr_SetString(PyExc_KeyError, msg.c_str()); + } +} ThePyNetClientCommErrorHandler; + +//////////////////////////////////////////////////////////////////// + +// pyNetClientComm ---------------------------------------------- +pyNetClientComm::pyNetClientComm() +{ + fNetClient.SetErrorHandler( &ThePyNetClientCommErrorHandler ); +} + +// ~pyNetClientComm ---------------------------------------------- +pyNetClientComm::~pyNetClientComm() +{ +} + +// NetAuthenticate ---------------------------------------------- +int pyNetClientComm::NetAuthenticate( double maxAuthSecs, PyObject* cbClass/*=nil*/, UInt32 cbContext/*=0 */) +{ + return fNetClient.NetAuthenticate( maxAuthSecs, new pyNetClientCommCallback( cbClass ), cbContext ); +} + +// NetLeave ---------------------------------------------- +int pyNetClientComm::NetLeave( UInt8 reason, PyObject* cbClass/*=nil*/, UInt32 cbContext/*=0 */) +{ + return fNetClient.NetLeave( reason, new pyNetClientCommCallback( cbClass ), cbContext ); +} + +// NetPing ---------------------------------------------- +int pyNetClientComm::NetPing( int serverType, int timeoutSecs/*=0*/, PyObject* cbClass/*=nil*/, UInt32 cbContext/*=0 */) +{ + return fNetClient.NetPing( serverType, timeoutSecs, new pyNetClientCommCallback( cbClass ), cbContext ); +} + +// NetFindAge ---------------------------------------------- +int pyNetClientComm::NetFindAge( const pyAgeLinkStruct* linkInfo, PyObject* cbClass/*=nil*/, UInt32 cbContext/*=0 */) +{ + return fNetClient.NetFindAge( linkInfo->GetAgeLink(), new pyNetClientCommCallback( cbClass ), cbContext ); +} + +// NetGetPlayerList ---------------------------------------------- +int pyNetClientComm::NetGetPlayerList( PyObject* cbClass/*=nil*/, UInt32 cbContext/*=0 */) +{ + return fNetClient.NetGetPlayerList( new pyNetClientCommCallback( cbClass ), cbContext ); +} + +// NetSetActivePlayer ---------------------------------------------- +int pyNetClientComm::NetSetActivePlayer( UInt32 playerID, const char* playerName, PyObject* cbClass/*=nil*/, UInt32 cbContext/*=0 */) +{ + return fNetClient.NetSetActivePlayer( playerID, playerName, 0 /*ccrLevel*/, new pyNetClientCommCallback( cbClass ), cbContext ); +} + +// NetCreatePlayer ---------------------------------------------- +int pyNetClientComm::NetCreatePlayer( const char* playerName, const char* avatarShape, UInt32 createFlags, PyObject* cbClass/*=nil*/, UInt32 cbContext/*=0 */) +{ + return fNetClient.NetCreatePlayer( playerName, avatarShape, createFlags, nil, nil, nil, new pyNetClientCommCallback( cbClass ), cbContext ); +} + +// NetJoinAge ---------------------------------------------- +int pyNetClientComm::NetJoinAge( PyObject* cbClass/*=nil*/, UInt32 cbContext/*=0 */) +{ + return fNetClient.NetJoinAge( true /*tryP2P*/, true /*allowTimeout*/, new pyNetClientCommCallback( cbClass ), cbContext ); +} + +// NetSetTimeout ---------------------------------------------- +int pyNetClientComm::NetSetTimeout( float timeoutSecs, PyObject* cbClass/*=nil*/, UInt32 cbContext/*=0 */) +{ + return fNetClient.NetSetTimeout( timeoutSecs, new pyNetClientCommCallback( cbClass ), cbContext ); +} + +// SetLogLevel ---------------------------------------------- +void pyNetClientComm::SetLogLevel( int logLevel ) +{ + fNetClient.SetLogLevel( logLevel ); +} + +// Startup ---------------------------------------------- +int pyNetClientComm::Init( bool threaded/*=true */, int logLevel/*=0 */) +{ + return fNetClient.Init( threaded, logLevel ); +} + +// Shutdown ---------------------------------------------- +int pyNetClientComm::Fini( float flushMsgsSecs/*=0.f */) +{ + return fNetClient.Fini( flushMsgsSecs ); +} + +// Update ---------------------------------------------- +int pyNetClientComm::Update() +{ + return fNetClient.Update( hsTimer::GetSeconds() ); +} + +// SetActiveServer ---------------------------------------------- +int pyNetClientComm::SetActiveServer( pyNetServerSessionInfo* nfo ) +{ + return fNetClient.SetActiveServer( &nfo->ServerInfo() ); +} + +// SetActiveServer2 ---------------------------------------------- +int pyNetClientComm::SetActiveServer2( const char * addr, int port ) +{ + plNetServerSessionInfo nfo; + nfo.SetServerAddr( addr ); + nfo.SetServerPort( port ); + return fNetClient.SetActiveServer( &nfo ); +} + + +// SetAuthInfo ---------------------------------------------- +int pyNetClientComm::SetAuthInfo( const char* acctName, const char* password ) +{ + return fNetClient.SetAuthInfo( acctName, password ); +} + +// SetLogByName ---------------------------------------------- +void pyNetClientComm::SetLogByName( const char * name, UInt32 flags ) +{ + plStatusLog * log = plStatusLogMgr::GetInstance().CreateStatusLog( 80, name, + flags | plStatusLog::kTimestamp | plStatusLog::kDeleteForMe ); + fNetClient.SetLog( log ); +} + +// GetLog ---------------------------------------------- +PyObject* pyNetClientComm::GetLog() const +{ + return pyStatusLog::New( fNetClient.GetLog() ); +} + +// SetServerSilenceTime ---------------------------------------------- +void pyNetClientComm::SetServerSilenceTime( float secs ) +{ + fNetClient.SetServerSilenceTime( secs ); +} + +//////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.h new file mode 100644 index 00000000..132f188e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientComm.h @@ -0,0 +1,193 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +//////////////////////////////////////////////////////////////////// +// pyNetClientComm - python wrapper for plNetClientComm class. + +#ifndef pyNetClientComm_h_inc +#define pyNetClientComm_h_inc + +#include "../plNetClientComm/plNetClientComm.h" +#include "../plStatusLog/plStatusLog.h" + +#include "../FeatureLib/pfPython/pyGlueHelpers.h" +#include + +//////////////////////////////////////////////////////////////////// + +class pyAgeLinkStruct; +class pyNetServerSessionInfo; +class pyStatusLog; +class pyNetCore; + +//////////////////////////////////////////////////////////////// +// plNetClientComm Callback Wrappers + +// Message handler for unsolicited msgs or registered +// for specific msg types. +class pyNetClientCommMsgHandler : public plNetClientComm::MsgHandler +{ +public: + PyObject* fPyObject; + pyNetClientCommMsgHandler( PyObject* pyObject ): fPyObject( pyObject ) {} + int HandleMessage( plNetMessage* msg ); +}; + +// Receipt handler for changed msg receipts. +class pyNetClientCommRcptHandler : public plNetClientComm::RcptHandler +{ +public: + PyObject* fPyObject; + pyNetClientCommRcptHandler( PyObject* pyObject ): fPyObject( pyObject ) {} + void HandleMsgReceipt( plNetCoreMsgReceipt* rcpt ); +}; + +//////////////////////////////////////////////////////////////////// + +class pyNetClientComm +{ + // We contain the plNetClientComm we are wrapping. + plNetClientComm fNetClient; + +protected: + + //////////////////////////////////////////////////////////////// + + pyNetClientComm(); + +public: + ~pyNetClientComm(); + + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptNetClientComm); + PYTHON_CLASS_NEW_DEFINITION; + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyNetClientComm object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyNetClientComm); // converts a PyObject to a pyNetClientComm (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + plNetClientComm * GetNetClientComm() { return &fNetClient; } + + //////////////////////////////////////////////////////////////// + // NETWORK OPERATIONS + + // Auth with active server using auth info set earlier. + // Will timeout after maxAuthSecs elapsed. + int NetAuthenticate( double maxAuthSecs, PyObject* cbClass=nil, UInt32 cbContext=0 ); + // Leave the active server. + int NetLeave( UInt8 reason, PyObject* cbClass=nil, UInt32 cbContext=0 ); + // Ping the specified server. + int NetPing( int serverType, int timeoutSecs=0, PyObject* cbClass=nil, UInt32 cbContext=0 ); + // Spawn a game for us. + int NetFindAge( const pyAgeLinkStruct* linkInfo, PyObject* cbClass=nil, UInt32 cbContext=0 ); + // Get player list. + int NetGetPlayerList( PyObject* cbClass=nil, UInt32 cbContext=0 ); + // Set the active player. + int NetSetActivePlayer( UInt32 playerID, const char* playerName, PyObject* cbClass=nil, UInt32 cbContext=0 ); + // Create a player + int NetCreatePlayer( const char* playerName, const char* avatarShape, UInt32 createFlags, PyObject* cbClass=nil, UInt32 cbContext=0 ); + // Join age + int NetJoinAge( PyObject* cbClass=nil, UInt32 cbContext=0 ); + // Set server-side timeout + int NetSetTimeout( float timeoutSecs, PyObject* cbClass=nil, UInt32 cbContext=0 ); + + //////////////////////////////////////////////////////////////// + + // Calls ErrorHandler, if set. Returns value of result + // that was passed in (for use in return/compound statements). + int ReportError( int err, int result ); + + //////////////////////////////////////////////////////////////// + + // Get/Set Log object + void SetLog( pyStatusLog* log ); + void SetLogByName( const char * name, UInt32 flags=0 ); + PyObject* GetLog() const; // return pyStatusLog + + // NetCore log level + void SetLogLevel( int logLevel ); + + // Startup/Shutdown this object + int Init( bool threaded=true, int logLevel=0 ); + // flushMsgsSecs: time to spend flushing net msgs queued in net core. + // <0 means no time limit. spin until all msgs are flushed. + int Fini( float flushMsgsSecs=0.f ); + + // Call this in your update loop. + int Update(); + + // Access to the NetCore object. + pyNetCore* GetNetCore() const; + + // Get/Set Authentication info + int SetAuthInfo( const char* acctName, const char* password ); + const char* GetAcctName() const; + const char* GetPassword() const; + + // Sets the server we want to communicate with. + int SetActiveServer( pyNetServerSessionInfo* nfo ); + int SetActiveServer2( const char * addr, int port ); + const pyNetServerSessionInfo* GetActiveServer() const; + + // Sets/clears receipt tracking for given message class. + void SetReceiptTrackingForType( UInt16 msgClassIdx, bool on ); + + // Adds a msg handler for a msg that is convertable to specified type. + void AddMsgHandlerForType( UInt16 msgClassIdx, pyNetClientCommMsgHandler* handler ); + + // Adds a msg handler for a specific msg type. + void AddMsgHandlerForExactType( UInt16 msgClassIdx, pyNetClientCommMsgHandler* handler ); + + void RemoveMsgHandler( pyNetClientCommMsgHandler* handler ); + + // Msgs not part of a task controlled by this + // object, and doesn't have a handler set for its type + // are sent to this handler (if set). + void SetDefaultHandler( pyNetClientCommMsgHandler* msgHandler ); + + // Changed message rcpts are sent to this handler if set. + void SetMsgReceiptHandler( pyNetClientCommRcptHandler* rcptHandler ); + + // Send a message to the server. + int SendMsg( plNetMessage* msg, UInt32 sendFlags=0 ); + // Send a message to specified peer + int SendMsg( plNetMessage* msg, plNetCore::PeerID peerID, UInt32 sendFlags=0 ); + + // Set the alive message send frequency. 0 means don't send periodic alive msgs. + void SetAliveFreq( float secs ); + float GetAliveFreq() const; + + // Set the amount of time before we declare server-silence. + void SetServerSilenceTime( float secs ); + float GetServerSilenceTime() const; + + // Set the maximum amount of time to spend processing + // incoming msgs per call to Update(). + void SetMaxMsgProcessingTime( float secs ); + +}; + +//////////////////////////////////////////////////////////////////// +#endif // pyNetClientComm_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientCommGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientCommGlue.cpp new file mode 100644 index 00000000..24dedb56 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientComm/pyNetClientCommGlue.cpp @@ -0,0 +1,320 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyNetClientComm.h" +#include "../FeatureLib/pfPython/pyEnum.h" +#include "../FeatureLib/pfPython/pyNetServerSessionInfo.h" +#include "../FeatureLib/pfPython/pyAgeLinkStruct.h" + +#include "../plNetCommon/plCreatePlayerFlags.h" +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptNetClientComm, pyNetClientComm); + +PYTHON_DEFAULT_NEW_DEFINITION(ptNetClientComm, pyNetClientComm) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptNetClientComm) + +PYTHON_INIT_DEFINITION(ptNetClientComm, args, keywords) +{ + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, initObj, args) +{ + char threaded = 1; + int logLevel = 0; + if (!PyArg_ParseTuple(args, "bi", &threaded, &logLevel)) + { + PyErr_SetString(PyExc_TypeError, "initObj expects a boolean and an int"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->Init(threaded != 0, logLevel)); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, fini, args) +{ + float flushMsgsSecs = 0; + if (!PyArg_ParseTuple(args, "f", &flushMsgsSecs)) + { + PyErr_SetString(PyExc_TypeError, "fini expects a float"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->Fini(flushMsgsSecs)); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetClientComm, update) +{ + return PyInt_FromLong(self->fThis->Update()); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, setActiveServer, args) +{ + PyObject* arg1; + int port = 0; + if (!PyArg_ParseTuple(args, "O|i", &arg1, &port)) + { + PyErr_SetString(PyExc_TypeError, "setActiveServer expects a string and an int, or a ptNetServerSessionInfo"); + PYTHON_RETURN_ERROR; + } + if (pyNetServerSessionInfo::Check(arg1)) + { + pyNetServerSessionInfo* info = pyNetServerSessionInfo::ConvertFrom(arg1); + return PyInt_FromLong(self->fThis->SetActiveServer(info)); + } + else if (PyString_Check(arg1)) + { + char* addr = PyString_AsString(arg1); + return PyInt_FromLong(self->fThis->SetActiveServer2(addr, port)); + } + PyErr_SetString(PyExc_TypeError, "setActiveServer expects a string and an int, or a ptNetServerSessionInfo"); + PYTHON_RETURN_ERROR; +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, setAuthInfo, args) +{ + char* account; + char* password; + if (!PyArg_ParseTuple(args, "ss", &account, &password)) + { + PyErr_SetString(PyExc_TypeError, "setAuthInfo expects two strings"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->SetAuthInfo(account, password)); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, authenticate, args) +{ + double maxAuthSecs; + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "d|Ol", &maxAuthSecs, &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "authenticate expects a double, an optional object and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->NetAuthenticate(maxAuthSecs, cb, context)); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, getPlayerList, args) +{ + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "|Ol", &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "getPlayerList expects an optional object and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->NetGetPlayerList(cb, context)); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, setActivePlayer, args) +{ + unsigned long playerID; + char* playerName; + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "ls|Ol", &playerID, &playerName, &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "setActivePlayer expects a double, a string, an optional object and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->NetSetActivePlayer(playerID, playerName, cb, context)); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, createPlayer, args) +{ + char* playerName; + char* avatarShape; + unsigned long createFlags; + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "ssl|Ol", &playerName, &avatarShape, &createFlags, &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "createPlayer expects two strings, a double, an optional object and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->NetCreatePlayer(playerName, avatarShape, createFlags, cb, context)); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, findAge, args) +{ + PyObject* linkObj = NULL; + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "O|Ol", &linkObj, &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "findAge expects a ptAgeLinkStruct, an optional object and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + if (!pyAgeLinkStruct::Check(linkObj)) + { + PyErr_SetString(PyExc_TypeError, "findAge expects a ptAgeLinkStruct, an optional object and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + pyAgeLinkStruct* link = pyAgeLinkStruct::ConvertFrom(linkObj); + return PyInt_FromLong(self->fThis->NetFindAge(link, cb, context)); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, joinAge, args) +{ + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "|Ol", &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "joinAge expects an optional object and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->NetJoinAge(cb, context)); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, leave, args) +{ + unsigned char reason; + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "b|Ol", &reason, &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "leave expects an unsigned 8-bit int, an optional object and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->NetLeave(reason, cb, context)); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, ping, args) +{ + int serverType; + int timeoutSecs = 0; + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "i|iOl", &serverType, &timeoutSecs, &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "ping expects an int, and optional int, an optional object and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->NetPing(serverType, timeoutSecs, cb, context)); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, setTimeout, args) +{ + float timeoutSecs; + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "f|Ol", &timeoutSecs, &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "setTimeout expects a float, an optional object and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->NetSetTimeout(timeoutSecs, cb, context)); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, setLog, args) +{ + char* name; + unsigned long flags = 0; + if (!PyArg_ParseTuple(args, "s|l", &name, &flags)) + { + PyErr_SetString(PyExc_TypeError, "setLog expects a string and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetLogByName(name, flags); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptNetClientComm, getLog) +{ + return self->fThis->GetLog(); +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, setLogLevel, args) +{ + int level; + if (!PyArg_ParseTuple(args, "i", &level)) + { + PyErr_SetString(PyExc_TypeError, "setLogLevel expects an integer"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetLogLevel(level); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptNetClientComm, setServerSilenceTime, args) +{ + float secs; + if (!PyArg_ParseTuple(args, "f", &secs)) + { + PyErr_SetString(PyExc_TypeError, "setServerSilenceTime expects a float"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetServerSilenceTime(secs); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptNetClientComm) + PYTHON_METHOD(ptNetClientComm, initObj, "Params: threaded=1,logLevel=0\nInitialize this object"), + PYTHON_METHOD(ptNetClientComm, fini, "Params: flushMsgsSecs=0\nFinalize this object"), + PYTHON_METHOD_NOARGS(ptNetClientComm, update, "Update this object"), + PYTHON_METHOD(ptNetClientComm, setActiveServer, "Params: addr,port\nAlso accepts a ptNetServerSessionInfo instead of address and port"), + PYTHON_METHOD(ptNetClientComm, setAuthInfo, "Params: accountName,password\nSets the authentication info"), + PYTHON_METHOD(ptNetClientComm, authenticate, "Params: maxAuthSecs,callback=None,cbContext=0\nAuthenticate with the server"), + PYTHON_METHOD(ptNetClientComm, getPlayerList, "Params: callback=None,cbContext=0\nGets a list of players and uses the callback"), + PYTHON_METHOD(ptNetClientComm, setActivePlayer, "Params: playerID,playerName,callback=None,cbContext=0\nSets the current active player"), + PYTHON_METHOD(ptNetClientComm, createPlayer, "Params: playerName,avatarShape,createFlags,callback=None,cbContext=0\nCreates a new player"), + PYTHON_METHOD(ptNetClientComm, findAge, "Params: ageLink,callback=None,cbContext=0\nFinds an age based on a ptAgeLinkStruct"), + PYTHON_METHOD(ptNetClientComm, joinAge, "Params: callback=None,cbContext=0\nUNKNOWN"), + PYTHON_METHOD(ptNetClientComm, leave, "Params: reason,callback=None,cbContext=0\nLeaves the lobby"), + PYTHON_METHOD(ptNetClientComm, ping, "Params: serverType,timeoutSecs=0,callback=None,cbContext=0\nPings a server"), + PYTHON_METHOD(ptNetClientComm, setTimeout, "Params: timeoutSecs,callback=None,cbContext=0\nSets the timeout duration"), + PYTHON_METHOD(ptNetClientComm, setLog, "Params: name,flags=0\nUNKNOWN"), + PYTHON_METHOD_NOARGS(ptNetClientComm, getLog, "UNKNOWN"), + PYTHON_METHOD(ptNetClientComm, setLogLevel, "Params: level\nSets the logging level"), + PYTHON_METHOD(ptNetClientComm, setServerSilenceTime, "Params: secs\nUNKNOWN"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptNetClientComm, "UNKNOWN"); + +// required functions for PyObject interoperability +PYTHON_CLASS_NEW_IMPL(ptNetClientComm, pyNetClientComm) + +PYTHON_CLASS_CHECK_IMPL(ptNetClientComm, pyNetClientComm) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptNetClientComm, pyNetClientComm) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyNetClientComm::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptNetClientComm); + PYTHON_CLASS_IMPORT_END(m); + + PYTHON_ENUM_START(PtCreatePlayerFlags); + PYTHON_ENUM_ELEMENT(PtCreatePlayerFlags, kDefaultFlags, plCreatePlayerFlags::kDefaultFlags); + PYTHON_ENUM_ELEMENT(PtCreatePlayerFlags, kNoNeighborhood, plCreatePlayerFlags::kNoNeighborhood); + PYTHON_ENUM_ELEMENT(PtCreatePlayerFlags, kNoCityLink, plCreatePlayerFlags::kNoCityLink); + PYTHON_ENUM_END(m, PtCreatePlayerFlags); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientGame/pyNetClientGame.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientGame/pyNetClientGame.cpp new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientGame/pyNetClientGame.cpp @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientGame/pyNetClientGame.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientGame/pyNetClientGame.h new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyNetClientGame/pyNetClientGame.h @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/creatables.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/creatables.cpp new file mode 100644 index 00000000..fbda11c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/creatables.cpp @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../plNetClientComm/plNetClientCommCreatable.h" +#include "../plUnifiedTime/plUnifiedTimeCreatable.h" +#include "../pnKeyedObject/pnKeyedObjectCreatable.h" +#include "../pnNetCommon/pnNetCommonCreatable.h" +#include "../pnMessage/pnMessageCreatable.h" +#include "../plNetMessage/plNetMessageCreatable.h" +#include "../plNetCommon/plNetCommonCreatable.h" +#include "../plVault/plVaultCreatable.h" +#include "../plSDL/plSDLCreatable.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/dllmain.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/dllmain.cpp new file mode 100644 index 00000000..a6dc7f72 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/dllmain.cpp @@ -0,0 +1,139 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsConfig.h" +#include "../plSDL/plSDL.h" +#include "../plVault/plVaultCache.h" +#include "../pyNetClientComm/pyNetClientComm.h" +#include "../pyVault/pyVNodeMgr.h" +#include "../pfPython/pyNetServerSessionInfo.h" +#include "../pfPython/pyVault.h" +#include "../pfPython/pyVaultNodeRef.h" +#include "../pfPython/pyVaultAgeInfoListNode.h" +#include "../pfPython/pyVaultAgeInfoNode.h" +#include "../pfPython/pyVaultAgeLinkNode.h" +#include "../pfPython/pyVaultChronicleNode.h" +#include "../pfPython/pyVaultFolderNode.h" +#include "../pfPython/pyVaultImageNode.h" +#include "../pfPython/pyVaultMarkerNode.h" +#include "../pfPython/pyVaultPlayerInfoListNode.h" +#include "../pfPython/pyVaultPlayerInfoNode.h" +#include "../pfPython/pyVaultPlayerNode.h" +#include "../pfPython/pySDL.h" +#include "../pfPython/pyVaultSDLNode.h" +#include "../pfPython/pyVaultTextNoteNode.h" +#include "../pfPython/pySpawnPointInfo.h" +#include "../pfPython/pyAgeInfoStruct.h" +#include "../pfPython/pyAgeLinkStruct.h" +#include "../pfPython/pyDniCoordinates.h" +#include "../pfPython/pyImage.h" +#include "../pfPython/pyNetLinkingMgr.h" +#include "../pfPython/pyStatusLog.h" +#include "../pfPython/pyColor.h" + +#include "../pfPython/pyEnum.h" +#include "../pfPython/pyGlueHelpers.h" + +#include + +extern "C" __declspec(dllexport) void PyInit_pyPlasma(void) +{ + // If a glue function (AddPlasmaClasses, AddPlasmaMethds, etc) is commented out, it is included + // in the source in this project... but the original version of this function did not call the + // function. So in order to keep the module identical, those specified classes/functions are not + // added, but can be un-commented in the future if needed + + std::vector methods; // this is temporary, for easy addition of new methods + //pyImage::AddPlasmaMethods(methods); + pySpawnPointInfo::AddPlasmaMethods(methods); + + // now copy the data to our real method definition structure + PyMethodDef* plasmaMethods = new PyMethodDef[methods.size() + 1]; + for (int curMethod = 0; curMethod < methods.size(); curMethod++) + plasmaMethods[curMethod] = methods[curMethod]; + PyMethodDef terminator = {NULL}; + plasmaMethods[methods.size()] = terminator; // add the terminator + + // Init the module + PyObject *m = Py_InitModule("pyPlasma", plasmaMethods); + + // Inits + plSDLMgr::GetInstance()->Init(); + plVaultCache::GetInstance()->SetEnabled( true ); + + // Enum + pyEnum::AddPlasmaConstantsClasses(m); + + // Classes + pyAgeInfoStruct::AddPlasmaClasses(m); + pyAgeInfoStructRef::AddPlasmaClasses(m); + pyAgeLinkStruct::AddPlasmaClasses(m); + pyAgeLinkStructRef::AddPlasmaClasses(m); + pyColor::AddPlasmaClasses(m); + //pyDniCoordinates::AddPlasmaClasses(m); + //pyPoint3::AddPlasmaClasses(m); + //pyVector3::AddPlasmaClasses(m); + //pyImage::AddPlasmaClasses(m); + //pyMatrix44::AddPlasmaClasses(m); + pyNetClientComm::AddPlasmaClasses(m); + pyNetServerSessionInfo::AddPlasmaClasses(m); + pyNetServerSessionInfoRef::AddPlasmaClasses(m); + pySDLStateDataRecord::AddPlasmaClasses(m); + pySimpleStateVariable::AddPlasmaClasses(m); + pySpawnPointInfo::AddPlasmaClasses(m); + pySpawnPointInfoRef::AddPlasmaClasses(m); + pyStatusLog::AddPlasmaClasses(m); + + pyVNodeMgr::AddPlasmaClasses(m); + pyAdminVNodeMgr::AddPlasmaClasses(m); + pyAgeVNodeMgr::AddPlasmaClasses(m); + pyPlayerVNodeMgr::AddPlasmaClasses(m); + + pyVaultNode::AddPlasmaClasses(m); + pyVaultNodeRef::AddPlasmaClasses(m); + pyVaultFolderNode::AddPlasmaClasses(m); + + pyVaultAgeInfoListNode::AddPlasmaClasses(m); + pyVaultAgeInfoNode::AddPlasmaClasses(m); + pyVaultAgeLinkNode::AddPlasmaClasses(m); + pyVaultChronicleNode::AddPlasmaClasses(m); + pyVaultImageNode::AddPlasmaClasses(m); + //pyVaultMarkerListNode::AddPlasmaClasses(m); + pyVaultMarkerNode::AddPlasmaClasses(m); + pyVaultPlayerInfoListNode::AddPlasmaClasses(m); + pyVaultPlayerInfoNode::AddPlasmaClasses(m); + pyVaultPlayerNode::AddPlasmaClasses(m); + pyVaultSDLNode::AddPlasmaClasses(m); + //pyVaultSystemNode::AddPlasmaClasses(m); + pyVaultTextNoteNode::AddPlasmaClasses(m); + + // Constants + pyNetLinkingMgr::AddPlasmaConstantsClasses(m); + pySDL::AddPlasmaConstantsClasses(m); + pyStatusLog::AddPlasmaConstantsClasses(m); + pyVault::AddPlasmaConstantsClasses(m); + + delete [] plasmaMethods; // cleanup +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/pyPlasmaTest.py b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/pyPlasmaTest.py new file mode 100644 index 00000000..0dad71b9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPlasma/pyPlasmaTest.py @@ -0,0 +1,147 @@ +""" *==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + + *==LICENSE==* """ +from pyPlasma import * +from pyPlasmaHelpers import * +from traceback import print_exc + +kLogToDebugger = 32 +kPeristentNode = 1 +kTransientNode = 0 +kQuittingGame = 7 +kLinkingOut = 8 +kExitingLobby = 9 + +#------------------------------------- + +print "BEGIN" + +# create client-side networking +net = ptNetClientComm() +# init log. this must be done before creating the vault manager +net.setLog("pyPlasmaTest.log", kLogToDebugger ) +# create vault manager +#vault = ptPlayerVNodeMgr(net) +vault = ptAdminVNodeMgr(net) +vault.setWantGlobalSDL(1) +vault.setWantAllPlayers(1) +# create the NetClientMgr. +nc = NetClientMgr(net) +# create the VaultConnectMgr +vc = VaultConnectMgr(vault) + +# startup networking +print "Net: starting up..." +net.init() +print "Net: started" + +# point to lobby server +net.setActiveServer('ea1-2k',5000) +# set acct username/password +net.setAuthInfo('reseng0221','tooann42') +# specify the name of player we want to use. +nc.setDesiredPlayer('Scooby5',1) + +#------------------ +success = 0 + +while 1: + try: + # login to the lobby server + if nc.login(NetClientMgr.kLobby)<0: break + + # connect to vault + if vc.connect()<0: break + # get root node + rootNode = vault.getRootNode() + print rootNode + # create a template node for finding the global sdl folder node + tmpNode = vault.createNode(PtVaultNodeTypes.kFolderNode,kTransientNode).upcastToFolderNode() + tmpNode.setFolderType(PtVaultStandardNodes.kAllAgeGlobalSDLNodesFolder) + # find global SDL folder + globalSDLFolder = vault.findNode(tmpNode) + if globalSDLFolder: + globalSDLFolder = globalSDLFolder.upcastToFolderNode() + print globalSDLFolder + + # startup an age or three (forces global sdl nodes to initialize) + ageLink = ptAgeLinkStruct() + # ageLink.getAgeInfo().setAgeFilename('Teledahn') + # nc.startFindingAge(ageLink) # we don't need to wait around for the operation to complete + # ageLink.getAgeInfo().setAgeFilename('city') + # nc.startFindingAge(ageLink) # we don't need to wait around for the operation to complete + # ageLink.getAgeInfo().setAgeFilename('Personal') + # nc.startFindingAge(ageLink) # we don't need to wait around for the operation to complete + # ageLink.getAgeInfo().setAgeFilename('Garden') + # nc.startFindingAge(ageLink) # we don't need to wait around for the operation to complete + # ageLink.getAgeInfo().setAgeFilename('BaronCityOffice') + # nc.startFindingAge(ageLink) # we don't need to wait around for the operation to complete + # ageLink.getAgeInfo().setAgeFilename('Kadish') + # nc.startFindingAge(ageLink) # we don't need to wait around for the operation to complete + # ageLink.getAgeInfo().setAgeFilename('Neighborhood') + # nc.startFindingAge(ageLink) # we don't need to wait around for the operation to complete + # ageLink.getAgeInfo().setAgeFilename('Cleft') + # nc.startFindingAge(ageLink) # we don't need to wait around for the operation to complete + # ageLink.getAgeInfo().setAgeFilename('Garrison') + # nc.startFindingAge(ageLink) # we don't need to wait around for the operation to complete + + # spawn a game + ageLink.getAgeInfo().setAgeFilename('Teledahn') + ageLink.setLinkingRules(PtLinkingRules.kOriginalBook) + if nc.findAge(ageLink)<0: break + serverInfo = nc.fCbArgs[0] + + # leave the lobby + nc.logout(kExitingLobby) + + # log into the game server + net.setActiveServer(serverInfo) + if nc.login(NetClientMgr.kGame)<0: break + + # join the age + if nc.joinAge()<0: break + + # done trying things + success = 1 + break + except: + print_exc() + break + +# disconnect from vault +vc.disconnect() +# leave the server +nc.logout(kQuittingGame) + +#------------------ + +# shutdown networking. only flush msgs if all went well (not required, but speeds up shutdown on error) +print "Net: shutting down..." +net.fini(success) +print "Net: shut down" + + +print "END" +raw_input("\npress return") diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/dllmain.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/dllmain.cpp new file mode 100644 index 00000000..e90e9278 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/dllmain.cpp @@ -0,0 +1,39 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyPloticus.h" + +#pragma warning(push) +// disable warnings that appear in boost +# pragma warning(disable:4800) // disable int to bool performance warning +# pragma warning(disable:4275) // disable non dll-interface warning +# pragma warning(disable:4251) // similar to above warning +# include +#pragma warning(pop) + +BOOST_PYTHON_MODULE(pyPloticus) +{ + pyPloticus::PythonModDef(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/pyPloticus.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/pyPloticus.cpp new file mode 100644 index 00000000..e8930ad5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/pyPloticus.cpp @@ -0,0 +1,200 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyPloticus.h" + +#include "../pfPython/pyGlueHelpers.h" +#include + +#include + +//////////////////////////////////////////////////////////////////// + +extern "C" { +// Ploticus C API +int ploticus_init(char *, char *); +int ploticus_arg(char *, char *); +int ploticus_begin(); +void ploticus_end(); +void ploticus_execline(char *); +int ploticus_execscript(char *, int); +int ploticus_getvar(char *, char *); +void ploticus_setvar(char *, char *); +} + +//////////////////////////////////////////////////////////////////// + +void pyPloticus::Init(char* device, char* outfilename) +{ + ploticus_init( device, outfilename ); +} + +void pyPloticus::Arg(char* name, char* value) +{ + ploticus_arg( name, value ); +} + +void pyPloticus::Begin() +{ + ploticus_begin(); +} + +void pyPloticus::End() +{ + ploticus_end(); +} + +void pyPloticus::ExecLine(char* line) +{ + ploticus_execline( line ); +} + +void pyPloticus::ExecScript(char* scriptfile, int prefab) +{ + ploticus_execscript( scriptfile, prefab ); +} + +void pyPloticus::GetVar(char* name, char* value) +{ + ploticus_getvar( name, value ); +} + +void pyPloticus::SetVar(char* name, char* value) +{ + ploticus_setvar( name, value ); +} + +PYTHON_GLOBAL_METHOD_DEFINITION(init, args, "Params: device,outfilename\nUNKNOWN") +{ + char* device; + char* outfilename; + if (!PyArg_ParseTuple(args, "ss", &device, &outfilename)) + { + PyErr_SetString(PyExc_TypeError, "init expects two strings"); + PYTHON_RETURN_ERROR; + } + pyPloticus::Init(device, outfilename); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(arg, args, "Params: name,value\nUNKNOWN") +{ + char* name; + char* value; + if (!PyArg_ParseTuple(args, "ss", &name, &value)) + { + PyErr_SetString(PyExc_TypeError, "arg expects two strings"); + PYTHON_RETURN_ERROR; + } + pyPloticus::Arg(name, value); + PYTHON_RETURN_NONE; +} + +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(begin, pyPloticus::Begin, "UNKNOWN") +PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(end, pyPloticus::End, "UNKNOWN") + +PYTHON_GLOBAL_METHOD_DEFINITION(execLine, args, "Params: line\nUNKNOWN") +{ + char* line; + if (!PyArg_ParseTuple(args, "s", &line)) + { + PyErr_SetString(PyExc_TypeError, "execLine expects a string"); + PYTHON_RETURN_ERROR; + } + pyPloticus::ExecLine(line); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(execScript, args, "Params: file,prefab\nUNKNOWN") +{ + char* file; + int prefab; + if (!PyArg_ParseTuple(args, "si", &file, &prefab)) + { + PyErr_SetString(PyExc_TypeError, "execScript expects a string and an int"); + PYTHON_RETURN_ERROR; + } + pyPloticus::ExecScript(file, prefab); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(getVar, args, "Params: name,value\nUNKNOWN") +{ + char* name; + char* value; + if (!PyArg_ParseTuple(args, "ss", &name, &value)) + { + PyErr_SetString(PyExc_TypeError, "getVar expects two strings"); + PYTHON_RETURN_ERROR; + } + pyPloticus::GetVar(name, value); + PYTHON_RETURN_NONE; +} + +PYTHON_GLOBAL_METHOD_DEFINITION(setVar, args, "Params: name,value\nUNKNOWN") +{ + char* name; + char* value; + if (!PyArg_ParseTuple(args, "ss", &name, &value)) + { + PyErr_SetString(PyExc_TypeError, "setVar expects two strings"); + PYTHON_RETURN_ERROR; + } + pyPloticus::SetVar(name, value); + PYTHON_RETURN_NONE; +} + +void AddPlasmaMethods(std::vector &methods) +{ + PYTHON_GLOBAL_METHOD(methods, init); + PYTHON_GLOBAL_METHOD(methods, arg); + PYTHON_BASIC_GLOBAL_METHOD(methods, begin); + PYTHON_BASIC_GLOBAL_METHOD(methods, end); + PYTHON_GLOBAL_METHOD(methods, execLine); + PYTHON_GLOBAL_METHOD(methods, execScript); + PYTHON_GLOBAL_METHOD(methods, getVar); + PYTHON_GLOBAL_METHOD(methods, setVar); +} + +//////////////////////////////////////////////////////////////////// +extern "C" __declspec(dllexport) void PyInit_pyPloticus(void) +{ + std::vector methods; // this is temporary, for easy addition of new methods + AddPlasmaMethods(methods); + + // now copy the data to our real method definition structure + PyMethodDef* plasmaMethods = new PyMethodDef[methods.size() + 1]; + for (int curMethod = 0; curMethod < methods.size(); curMethod++) + plasmaMethods[curMethod] = methods[curMethod]; + PyMethodDef terminator = {NULL}; + plasmaMethods[methods.size()] = terminator; // add the terminator + + // Init the module + PyObject *m = Py_InitModule("pyPloticus", plasmaMethods); + + delete [] plasmaMethods; // clean up +} + +//////////////////////////////////////////////////////////////////// diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/pyPloticus.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/pyPloticus.h new file mode 100644 index 00000000..b4980048 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyPloticus/pyPloticus.h @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef pyPloticus_h_inc +#define pyPloticus_h_inc + +#include "hsTypes.h" +#include "Python.h" + +// Ploticus C API + +class pyPloticus +{ +public: + static void Init(char* device, char* outfilename); + static void Arg(char* name, char* value); + static void Begin(); + static void End(); + static void ExecLine(char* line); + static void ExecScript(char* scriptfile, int prefab); + static void GetVar(char* name, char* value); + static void SetVar(char* name, char* value); +}; + +#endif // pyPloticus_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgr.cpp new file mode 100644 index 00000000..5e674756 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgr.cpp @@ -0,0 +1,487 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVNodeMgr.h" +#include "../plVault/plVaultCallback.h" +#include "../plVault/plVaultInitTasks.h" +#include "../pfPython/pyVaultNode.h" +#include "../pfPython/pyVaultFolderNode.h" +#include "../pyNetClientComm/pyNetClientComm.h" +#include "../plNetMessage/plNetMessage.h" +#include "../plStatusLog/plStatusLog.h" + +//////////////////////////////////////////////////////////////////// + +class pyVaultOperationCallback : public plVaultOperationCallback +{ +public: + PyObject * fPyObject; + pyVaultOperationCallback( PyObject * pyObject ) + : fPyObject( pyObject ) + { + Py_XINCREF( fPyObject ); + } + ~pyVaultOperationCallback() + { + Py_XDECREF( fPyObject ); + } + void VaultOperationStarted( UInt32 context ) + { + if ( fPyObject ) + { + // Do callback + PyObject* func = PyObject_GetAttrString( fPyObject, "operationStarted" ); + if ( func ) + { + if ( PyCallable_Check(func)>0 ) + { + PyObject* retVal = PyObject_CallMethod(fPyObject, "operationStarted", "l", context); + Py_XDECREF(retVal); + } + } + } + } + void VaultOperationComplete( UInt32 context, int resultCode ) + { + if ( fPyObject ) + { + // Pass args. +// PyObject* pyArgs = PyObject_GetAttrString( fPyObject, "fCbArgs" ); +// if ( pyArgs ) +// { +// dict pyDict = dict(); +// std::map args; +// fCbArgs.GetItems( args ); +// for ( std::map::iterator ii=args.begin(); ii!=args.end(); ++ii ) +// { +// UInt16 key = ii->first; +// plCreatable* arg = ii->second; +// plCreatableGenericValue * genValue = plCreatableGenericValue::ConvertNoRef( arg ); +// if ( genValue ) +// { +// plGenericType & value = genValue->Value(); +// switch ( value.GetType() ) +// { +// case plGenericType::kInt: +// pyDict[key] = (int)value; +// break; +// case plGenericType::kUInt: +// pyDict[key] = (unsigned int)value; +// break; +// case plGenericType::kFloat: +// pyDict[key] = (float)value; +// break; +// case plGenericType::kDouble: +// pyDict[key] = (double)value; +// break; +// case plGenericType::kBool: +// pyDict[key] = (bool)value; +// break; +// case plGenericType::kChar: +// pyDict[key] = (char)value; +// break; +// case plGenericType::kString: +// pyDict[key] = (const char *)value; +// break; +// case plGenericType::kAny: +// break; +// case plGenericType::kNone: +// break; +// } +// } +// } +// PyObject_SetAttrString( fPyObject, "fCbArgs", pyDict.ptr() ); +// } + // Do callback + PyObject* func = PyObject_GetAttrString( fPyObject, "operationComplete" ); + if ( func ) + { + if ( PyCallable_Check(func)>0 ) + { + PyObject* retVal = PyObject_CallMethod(fPyObject, "operationComplete", "li", context, resultCode); + Py_XDECREF(retVal); + } + } + } + delete this; + } +}; + + +class pyVaultCallback : public plVaultStubbedCallback +{ +public: + PyObject * fPyObject; + pyVaultCallback( PyObject * pyObject ) + : fPyObject( pyObject ) + { + Py_XINCREF( fPyObject ); + } + ~pyVaultCallback() + { + Py_XDECREF( fPyObject ); + } +}; + + +//////////////////////////////////////////////////////////////////// + +int pyVNodeMgr::VaultMsgHandler::HandleMessage( plNetMessage* msg ) +{ + plNetMsgVault * vaultMsg = plNetMsgVault::ConvertNoRef( msg ); + + if ( vaultMsg ) + { + plNetCoreMessage * ncmsg = (plNetCoreMessage*)msg->GetNetCoreMsg(); + msg->PeekBuffer( ncmsg->GetData(), ncmsg->GetLen() ); + fMyVNodeMgr->GetStatusLog()->AddLineF( "\t%s", msg->AsStdString().c_str() ); + plVault::ProcessMsg( vaultMsg ); + return plNetClientComm::kOK_MsgConsumed; + } + + return hsFail; +} + + +// pyVNodeMgr ---------------------------------------------- +pyVNodeMgr::pyVNodeMgr( PyObject* thaComm ) +{ + if (!pyNetClientComm::Check(thaComm)) + { + fMyCommObj = NULL; + return; // screwed! + } + + fMsgHandler.setMgr(this); + + fMyCommObj = thaComm; + Py_INCREF(fMyCommObj); + fMyComm = pyNetClientComm::ConvertFrom(fMyCommObj); + fMyComm->GetNetClientComm()->AddMsgHandlerForType( plNetMsgVault::Index(), &fMsgHandler ); + plVNodeMgr::SetStatusLog( fMyComm->GetNetClientComm()->GetLog(), false ); +} + +// ~pyVNodeMgr ---------------------------------------------- +pyVNodeMgr::~pyVNodeMgr() +{ + fMyComm->GetNetClientComm()->RemoveMsgHandler( &fMsgHandler ); + Py_DECREF(fMyCommObj); +} + +void pyVNodeMgr::setMyComm(PyObject* thaComm) +{ + if (fMyComm) + { + fMyComm->GetNetClientComm()->RemoveMsgHandler(&fMsgHandler); + Py_DECREF(fMyCommObj); + fMyCommObj = NULL; + fMyComm = NULL; + } + if (!pyNetClientComm::Check(thaComm)) + return; // screwed! + + fMyCommObj = thaComm; + Py_INCREF(fMyCommObj); + fMyComm = pyNetClientComm::ConvertFrom(fMyCommObj); + fMyComm->GetNetClientComm()->AddMsgHandlerForType(plNetMsgVault::Index(), &fMsgHandler); + plVNodeMgr::SetStatusLog(fMyComm->GetNetClientComm()->GetLog(), false); +} + +// IAmOnline ---------------------------------------------- +bool pyVNodeMgr::IAmOnline() const +{ + return true; +} + +// IIsThisMe ---------------------------------------------- +bool pyVNodeMgr::IIsThisMe( plVaultPlayerInfoNode* node ) const +{ + return ( fMyComm->GetNetClientComm()->GetPlayerID()==node->GetPlayerID() ); +} + +// IIsThisMe ---------------------------------------------- +bool pyVNodeMgr::IIsThisMe( plVaultPlayerNode * node ) const +{ + return ( fMyComm->GetNetClientComm()->GetPlayerID()==node->GetID() ); +} + +// ISendNetMsg ---------------------------------------------- +int pyVNodeMgr::ISendNetMsg( plNetMsgVault* msg, UInt32 sendFlags/*=0 */) +{ + return fMyComm->GetNetClientComm()->SendMsg( msg, sendFlags ); +} + +// IGetPlayerID ---------------------------------------------- +UInt32 pyVNodeMgr::IGetPlayerID() const +{ + return fMyComm->GetNetClientComm()->GetPlayerID(); +} + +// Update ---------------------------------------------- +int pyVNodeMgr::Update( double secs ) +{ + return plVNodeMgr::Update( secs ); +} + +// Startup ---------------------------------------------- +void pyVNodeMgr::Startup() +{ + plVNodeMgr::Startup(); +} + +// Shutdown ---------------------------------------------- +void pyVNodeMgr::Shutdown() +{ + plVNodeMgr::Shutdown(); +} + + +// IsConnected ---------------------------------------------- +bool pyVNodeMgr::IsConnected() +{ + return plVNodeMgr::IsConnected(); +} + +// Disconnect ---------------------------------------------- +void pyVNodeMgr::Disconnect( PyObject* cb/*=nil*/, UInt32 cbContext/*=0 */) +{ + // disconnect from allplayers and globalsdl folders + plVaultNodeRef * out; + plVaultNode * root = plVNodeMgr::GetRootNode(); + if ( root ) + { + plVaultFolderNode tmpGlobalSDL; + tmpGlobalSDL.SetFolderType( plVault::kAllAgeGlobalSDLNodesFolder ); + if ( root->FindNode( &tmpGlobalSDL, out ) ) + root->RemoveNode( out->GetChildID() ); + plVaultFolderNode tmpAllPlayers; + tmpAllPlayers.SetFolderType( plVault::kAllPlayersFolder ); + if ( root->FindNode( &tmpAllPlayers, out ) ) + root->RemoveNode( out->GetChildID() ); + } + plVNodeMgr::Disconnect( new pyVaultOperationCallback( cb ), cbContext ); +} + +// Connect ---------------------------------------------- +void pyVNodeMgr::Connect( int childFetchLevel/*=plVault::kFetchAllChildren*/, PyObject* cb/*=nil*/, UInt32 cbContext/*=0 */) +{ + plVNodeMgr::Connect( childFetchLevel, new pyVaultOperationCallback( cb ), cbContext ); +} + +// FetchNode ---------------------------------------------- +bool pyVNodeMgr::FetchNode( UInt32 nodeID, + int childFetchLevel/*=plVault::kFetchAllChildren*/, + PyObject* cb/*=nil*/, + UInt32 cbContext/*=0 */) +{ + return plVNodeMgr::FetchNode( nodeID, childFetchLevel, new pyVaultOperationCallback( cb ), cbContext ); +} + +// GetRootNode ---------------------------------------------- +PyObject* pyVNodeMgr::GetRootNode() const +{ + return pyVaultNode::New( plVNodeMgr::GetRootNode() ); +} + +// GetClientID ---------------------------------------------- +UInt32 pyVNodeMgr::GetClientID() const +{ + return plVNodeMgr::GetClientID(); +} + +// GetNode ---------------------------------------------- +PyObject* pyVNodeMgr::GetNode( UInt32 id ) const +{ + plVaultNode * tmp; + if ( plVNodeMgr::GetNode( id, tmp ) ) + return pyVaultNode::New( tmp ); + PYTHON_RETURN_NONE; +} + +// FindNode ---------------------------------------------- +PyObject* pyVNodeMgr::FindNode( pyVaultNode* templateNode ) const +{ + plVaultNode * node; + if ( plVNodeMgr::FindNode( templateNode->GetNode(), node ) ) + return pyVaultNode::New( node ); + PYTHON_RETURN_NONE; +} + +// EnableCallbacks ---------------------------------------------- +bool pyVNodeMgr::EnableCallbacks( bool b ) +{ + return plVNodeMgr::EnableCallbacks( b ); +} + +// AddCallback ---------------------------------------------- +void pyVNodeMgr::AddCallback( PyObject* cb ) +{ + pyVaultCallback * pycb = new pyVaultCallback( cb ); + fPyCallbacks.push_back( pycb ); + plVNodeMgr::AddCallback( pycb ); +} + +// RemoveCallback ---------------------------------------------- +void pyVNodeMgr::RemoveCallback( PyObject* cb ) +{ + PyCallbackVec tmp; + for ( int i=0; ifPyObject==cb ) + tmp.push_back( fPyCallbacks[i] ); + } + for ( int i=0; iGetArgs()->AddInt( plVault::kArg_NodeMgrType, plVault::kNodeType_VNodeMgrPlayer ); + msg->GetArgs()->AddInt( plVault::kArg_NodeMgrID, fMyComm->GetNetClientComm()->GetPlayerID() ); +} + +// IGetNodeInitializationTask ---------------------------------------------- +plVNodeInitTask * pyPlayerVNodeMgr::IGetNodeInitializationTask( plVaultNode * node ) +{ + if ( plVaultPlayerNode::ConvertNoRef( node ) ) + return new plVaultPlayerInitializationTask( this, node, true ); + return nil; +} + +//////////////////////////////////////////////////////////////////////// + +pyAgeVNodeMgr::pyAgeVNodeMgr( PyObject* thaComm ) +: pyVNodeMgr( thaComm ) +{} + +// IAmSuperUser ---------------------------------------------- +bool pyAgeVNodeMgr::IAmSuperUser( void ) const +{ + return false; +} + +// IFillOutConnectFields ---------------------------------------------- +void pyAgeVNodeMgr::IFillOutConnectFields( plNetMsgVault* msg ) const +{ + msg->GetArgs()->AddInt( plVault::kArg_NodeMgrType, plVault::kNodeType_VNodeMgrAge ); + msg->GetArgs()->AddString( plVault::kArg_NodeMgrAgeInstanceName, fAgeFilename.c_str() ); + msg->GetArgs()->AddItem( plVault::kArg_NodeMgrAgeGuid, &fAgeInstanceGuid ); +} + +// IGetNodeInitializationTask ---------------------------------------------- +plVNodeInitTask * pyAgeVNodeMgr::IGetNodeInitializationTask( plVaultNode * node ) +{ + if ( plVaultAgeNode::ConvertNoRef( node ) ) + return new plVaultAgeInitializationTask( this, node, nil, true ); + if ( plVaultAgeInfoNode::ConvertNoRef( node ) ) + return new plVaultAgeInfoInitializationTask( this, node ); + return nil; +} + +// SetAgeInfo ---------------------------------------------- +void pyAgeVNodeMgr::SetAgeInfo( const char * ageFilename, const char * ageInstanceGuid ) +{ + fAgeFilename = ageFilename; + fAgeInstanceGuid.FromString( ageInstanceGuid ); +} + +//////////////////////////////////////////////////////////////////////// + +pyAdminVNodeMgr::pyAdminVNodeMgr( PyObject* thaComm ) +: pyVNodeMgr( thaComm ) +, fWantGlobalSDL( true ) +, fWantAllPlayers( false ) +{} + +// IAmSuperUser ---------------------------------------------- +bool pyAdminVNodeMgr::IAmSuperUser( void ) const +{ + return true; +} + +// IFillOutConnectFields ---------------------------------------------- +void pyAdminVNodeMgr::IFillOutConnectFields( plNetMsgVault* msg ) const +{ + msg->GetArgs()->AddInt( plVault::kArg_NodeMgrType, plVault::kNodeType_VNodeMgrAdmin ); + msg->GetArgs()->AddInt( plVault::kArg_NodeMgrID, fMyComm->GetNetClientComm()->GetPlayerID() ); +} + +// IGetNodeInitializationTask ---------------------------------------------- +plVNodeInitTask * pyAdminVNodeMgr::IGetNodeInitializationTask( plVaultNode * node ) +{ + if ( plVaultAdminNode::ConvertNoRef( node ) ) + return new plVaultAdminInitializationTask( this, node, fWantGlobalSDL, fWantAllPlayers ); + return nil; +} + +// GetGlobalInbox ---------------------------------------------- +PyObject * pyAdminVNodeMgr::GetGlobalInbox() const +{ + plVaultFolderNode tmp; + tmp.SetFolderType( plVault::kGlobalInboxFolder ); + plVaultNode * node; + if ( plVNodeMgr::FindNode( &tmp, node ) ) + { + return pyVaultFolderNode::New( plVaultFolderNode::ConvertNoRef( node ) ); + } + PYTHON_RETURN_NONE; +} + +//////////////////////////////////////////////////////////////////// +// End. diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgr.h new file mode 100644 index 00000000..c15cf760 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgr.h @@ -0,0 +1,231 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +//////////////////////////////////////////////////////////////////// +// pyVNodeMgr - python wrapper for plVNodeMgr class. + +#ifndef pyVNodeMgr_h_inc +#define pyVNodeMgr_h_inc + +#include "../plVault/plVaultClient.h" +#include "../plNetClientComm/plNetClientComm.h" + +#include "../FeatureLib/pfPython/pyGlueHelpers.h" +#include + +//////////////////////////////////////////////////////////////////// + +class pyVaultNode; +class pyVaultCallback; +class pyStatusLog; +class pyNetClientComm; +class pyVaultFolderNode; + +//////////////////////////////////////////////////////////////////// + +class pyVNodeMgr : public plVNodeMgr +{ +private: + typedef std::vector PyCallbackVec; + PyCallbackVec fPyCallbacks; + + class VaultMsgHandler : public plNetClientComm::MsgHandler + { + private: + pyVNodeMgr * fMyVNodeMgr; + int HandleMessage( plNetMessage* msg ); + public: + VaultMsgHandler(): fMyVNodeMgr(nil) {} + void setMgr(pyVNodeMgr * thaNodeMgr) {fMyVNodeMgr = thaNodeMgr;} + }; + friend class VaultMsgHandler; + VaultMsgHandler fMsgHandler; + +protected: + PyObject* fMyCommObj; + pyNetClientComm* fMyComm; // pointer to object stored in fMyCommObj + + bool IAmOnline() const; + bool IIsThisMe( plVaultPlayerInfoNode* node ) const; + bool IIsThisMe( plVaultPlayerNode * node ) const; + int ISendNetMsg( plNetMsgVault* msg, UInt32 sendFlags=0 ); + UInt32 IGetPlayerID() const; + + pyVNodeMgr(): fMyComm(nil) {fMsgHandler.setMgr(this);} // for python glue only, do NOT call + pyVNodeMgr( PyObject* thaComm ); + +public: + ~pyVNodeMgr(); + + void setMyComm(PyObject* thaComm); // for python glue only, do NOT call + + // required functions for PyObject interoperability + PYTHON_EXPOSE_TYPE; + PYTHON_CLASS_NEW_FRIEND(ptVNodeMgr); + static PyObject* New(PyObject* thaComm); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyVNodeMgr object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyVNodeMgr); // converts a PyObject to a pyVNodeMgr (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + PyObject * GetNetClient() const { Py_INCREF(fMyCommObj); return fMyCommObj; } // returns pyNetClientComm + + ///////////////////////////////////////////////// + // Vault Client API + + int Update( double secs ); + + void Startup(); + void Shutdown(); + + // connect/disconnect + bool IsConnected(); + void Disconnect( + PyObject* cb=nil, + UInt32 cbContext=0 ); + void Connect( + int childFetchLevel=plVault::kFetchAllChildren, + PyObject* cb=nil, + UInt32 cbContext=0 ); + // TODO: Glue this. + // Fetch matching node from server and hold onto it. + // Note: You won't receive notifications about the fetched node or + // it's children until it has been added to your root node or any + // of it's children. +// bool FetchNode( +// pyVaultNode* templateNode, +// int childFetchLevel=plVault::kFetchAllChildren, +// bool allowCreate = false, +// PyObject* cb=nil, +// UInt32 cbContext=0 ); + bool FetchNode( UInt32 nodeID, + int childFetchLevel=plVault::kFetchAllChildren, + PyObject* cb=nil, + UInt32 cbContext=0 ); + + // get our root node + PyObject* GetRootNode() const; // returns pyVaultNode + // get the client node ID returned to us by the server ( if we didn't + // fetch when we connected then we have to use this to identify ourselves ). + UInt32 GetClientID() const; + // search all nodes in client locally + PyObject* GetNode( UInt32 id ) const; // returns pyVaultNode + // TODO: Glue these. + PyObject* FindNode( pyVaultNode* templateNode ) const; // returns pyVaultNode +// bool FindNodes( const pyVaultNode* templateNode, PyObject * out ) const; + // callback management + bool EnableCallbacks( bool b ); // returns previous enabled setting. + void AddCallback( PyObject* cb ); + void RemoveCallback( PyObject* cb ); + + // create a node of the given type. + PyObject* CreateNode( int nodeType, bool persistent ); // returns pyVaultNode + + // dump contents to log + void Dump() const; +}; + +//////////////////////////////////////////////////////////////////// + +class pyPlayerVNodeMgr : public pyVNodeMgr +{ +protected: + bool IAmSuperUser( void ) const; + void IFillOutConnectFields( plNetMsgVault* msg ) const; + plVNodeInitTask * IGetNodeInitializationTask( plVaultNode * node ); + + pyPlayerVNodeMgr(): pyVNodeMgr() {} // for python glue only, do NOT call + pyPlayerVNodeMgr( PyObject* thaComm ); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptPlayerVNodeMgr); + static PyObject* New(PyObject* thaComm); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyPlayerVNodeMgr object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyPlayerVNodeMgr); // converts a PyObject to a pyPlayerVNodeMgr (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); +}; + +//////////////////////////////////////////////////////////////////// + +class pyAgeVNodeMgr : public pyVNodeMgr +{ + std::string fAgeFilename; + plServerGuid fAgeInstanceGuid; + +protected: + bool IAmSuperUser( void ) const; + void IFillOutConnectFields( plNetMsgVault* msg ) const; + plVNodeInitTask * IGetNodeInitializationTask( plVaultNode * node ); + + pyAgeVNodeMgr(): pyVNodeMgr() {} // for python glue only, do NOT call + pyAgeVNodeMgr( PyObject* thaComm ); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptAgeVNodeMgr); + static PyObject* New(PyObject* thaComm); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyAgeVNodeMgr object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyAgeVNodeMgr); // converts a PyObject to a pyAgeVNodeMgr (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + void SetAgeInfo( const char * ageFilename, const char * ageInstanceGuid ); +}; + +//////////////////////////////////////////////////////////////////// + +class pyAdminVNodeMgr : public pyVNodeMgr +{ +private: + bool fWantGlobalSDL; + bool fWantAllPlayers; + +protected: + bool IAmSuperUser( void ) const; + void IFillOutConnectFields( plNetMsgVault* msg ) const; + plVNodeInitTask * IGetNodeInitializationTask( plVaultNode * node ); + + pyAdminVNodeMgr(): pyVNodeMgr(), fWantGlobalSDL(true), fWantAllPlayers(false) {} // for python glue only, do NOT call + pyAdminVNodeMgr( PyObject* thaComm ); + +public: + // required functions for PyObject interoperability + PYTHON_CLASS_NEW_FRIEND(ptAdminVNodeMgr); + static PyObject* New(PyObject* thaComm); + PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyAdminVNodeMgr object + PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyAdminVNodeMgr); // converts a PyObject to a pyAdminVNodeMgr (throws error if not correct type) + + static void AddPlasmaClasses(PyObject *m); + + void SetWantGlobalSDL( bool v ) { fWantGlobalSDL=v; } + void SetWantAllPlayers( bool v ) { fWantAllPlayers=v; } + + PyObject * GetGlobalInbox() const; // returns pyVaultFolderNode +}; + +//////////////////////////////////////////////////////////////////// +#endif // pyVNodeMgr_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgrGlue.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgrGlue.cpp new file mode 100644 index 00000000..a889ad76 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/PythonLib/pyVault/pyVNodeMgrGlue.cpp @@ -0,0 +1,461 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "pyVNodeMgr.h" +#include "../FeatureLib/pfPython/pyVaultNode.h" +#include "../pyNetClientComm/pyNetClientComm.h" + +#include + +// glue functions +PYTHON_CLASS_DEFINITION(ptVNodeMgr, pyVNodeMgr); + +PYTHON_DEFAULT_NEW_DEFINITION(ptVNodeMgr, pyVNodeMgr) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptVNodeMgr) + +PYTHON_NO_INIT_DEFINITION(ptVNodeMgr) + +PYTHON_METHOD_DEFINITION(ptVNodeMgr, update, args) +{ + double secs; + if (!PyArg_ParseTuple(args, "d", &secs)) + { + PyErr_SetString(PyExc_TypeError, "update expects a double"); + PYTHON_RETURN_ERROR; + } + return PyInt_FromLong(self->fThis->Update(secs)); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptVNodeMgr, startup, Startup) +PYTHON_BASIC_METHOD_DEFINITION(ptVNodeMgr, shutdown, Shutdown) + +PYTHON_METHOD_DEFINITION_NOARGS(ptVNodeMgr, isConnected) +{ + PYTHON_RETURN_BOOL(self->fThis->IsConnected()); +} + +PYTHON_METHOD_DEFINITION(ptVNodeMgr, disconnect, args) +{ + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "|Ol", &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "disconnect expects an optional object, and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->Disconnect(cb, context); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVNodeMgr, connect, args) +{ + int childFetchLevel = -1; + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "|iOl", &childFetchLevel, &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "connect expects an optional int, an optional object, and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + self->fThis->Connect(childFetchLevel, cb, context); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVNodeMgr, fetchNode, args) +{ + unsigned long nodeID; + int childFetchLevel = -1; + PyObject* cb = NULL; + unsigned long context = 0; + if (!PyArg_ParseTuple(args, "l|iOl", &nodeID, &childFetchLevel, &cb, &context)) + { + PyErr_SetString(PyExc_TypeError, "fetchNode expects an unsigned long, an optional int, an optional object, and an optional unsigned long"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->FetchNode(nodeID, childFetchLevel, cb, context)); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVNodeMgr, getRootNode) +{ + return self->fThis->GetRootNode(); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVNodeMgr, getClientID) +{ + return PyLong_FromUnsignedLong(self->fThis->GetClientID()); +} + +PYTHON_METHOD_DEFINITION(ptVNodeMgr, getNode, args) +{ + unsigned long nodeID; + if (!PyArg_ParseTuple(args, "l", &nodeID)) + { + PyErr_SetString(PyExc_TypeError, "getNode expects an unsigned long"); + PYTHON_RETURN_ERROR; + } + return self->fThis->GetNode(nodeID); +} + +PYTHON_METHOD_DEFINITION(ptVNodeMgr, findNode, args) +{ + PyObject* templateObj = NULL; + if (!PyArg_ParseTuple(args, "O", &templateObj)) + { + PyErr_SetString(PyExc_TypeError, "findNode expects a ptVaultNode"); + PYTHON_RETURN_ERROR; + } + if (!pyVaultNode::Check(templateObj)) + { + PyErr_SetString(PyExc_TypeError, "findNode expects a ptVaultNode"); + PYTHON_RETURN_ERROR; + } + pyVaultNode* templateNode = pyVaultNode::ConvertFrom(templateObj); + return self->fThis->FindNode(templateNode); +} + +PYTHON_METHOD_DEFINITION(ptVNodeMgr, enableCallbacks, args) +{ + char enable; + if (!PyArg_ParseTuple(args, "b", &enable)) + { + PyErr_SetString(PyExc_TypeError, "enableCallbacks expects a boolean"); + PYTHON_RETURN_ERROR; + } + PYTHON_RETURN_BOOL(self->fThis->EnableCallbacks(enable != 0)); +} + +PYTHON_METHOD_DEFINITION(ptVNodeMgr, addCallback, args) +{ + PyObject* cb = NULL; + if (!PyArg_ParseTuple(args, "O", &cb)) + { + PyErr_SetString(PyExc_TypeError, "addCallback expects an object"); + PYTHON_RETURN_ERROR; + } + self->fThis->AddCallback(cb); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVNodeMgr, removeCallback, args) +{ + PyObject* cb = NULL; + if (!PyArg_ParseTuple(args, "O", &cb)) + { + PyErr_SetString(PyExc_TypeError, "removeCallback expects an object"); + PYTHON_RETURN_ERROR; + } + self->fThis->RemoveCallback(cb); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptVNodeMgr, createNode, args) +{ + int nodeType; + bool persistent; + if (!PyArg_ParseTuple(args, "ib", &nodeType, &persistent)) + { + PyErr_SetString(PyExc_TypeError, "createNode expects an int and a boolean"); + PYTHON_RETURN_ERROR; + } + return self->fThis->CreateNode(nodeType, persistent != 0); +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptVNodeMgr, getNetClient) +{ + return self->fThis->GetNetClient(); +} + +PYTHON_BASIC_METHOD_DEFINITION(ptVNodeMgr, dump, Dump) + +PYTHON_START_METHODS_TABLE(ptVNodeMgr) + PYTHON_METHOD(ptVNodeMgr, update, "Params: secs\nUNKNOWN"), + PYTHON_BASIC_METHOD(ptVNodeMgr, startup, "UNKNOWN"), + PYTHON_BASIC_METHOD(ptVNodeMgr, shutdown, "UNKNOWN"), + PYTHON_METHOD_NOARGS(ptVNodeMgr, isConnected, "Are we connected to the vault?"), + PYTHON_METHOD(ptVNodeMgr, disconnect, "Params: callback=None,cbContext=0\nDisconnects us from the vault"), + PYTHON_METHOD(ptVNodeMgr, connect, "Params: childFetchLevel=-1,callback=None,cbContext=0\nConnects us ti the vault"), + PYTHON_METHOD(ptVNodeMgr, fetchNode, "Params: nodeID,childFetchLevel=-1,callback=None,cbContext=0\nFetchs the specified node"), + PYTHON_METHOD_NOARGS(ptVNodeMgr, getRootNode, "Returns the root node"), + PYTHON_METHOD_NOARGS(ptVNodeMgr, getClientID, "Returns the client ID number"), + PYTHON_METHOD(ptVNodeMgr, getNode, "Params: nodeID\nReturns the specified node"), + PYTHON_METHOD(ptVNodeMgr, findNode, "Params: templateNode\nLocates a node matching the template"), + PYTHON_METHOD(ptVNodeMgr, enableCallbacks, "Params: enable\nEnable/disable callbacks"), + PYTHON_METHOD(ptVNodeMgr, addCallback, "Params: callback\nUNKNOWN"), + PYTHON_METHOD(ptVNodeMgr, removeCallback, "Params: callback\nUNKNOWN"), + PYTHON_METHOD(ptVNodeMgr, createNode, "Params: nodeType,persistent\nCreates a new node"), + PYTHON_METHOD(ptVNodeMgr, getNetClient, "Returns our internal ptNetClientComm"), + PYTHON_BASIC_METHOD(ptVNodeMgr, dump, "Dump contents to our log"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE(ptVNodeMgr, "UNKNOWN"); +PYTHON_EXPOSE_TYPE_DEFINITION(ptVNodeMgr, pyVNodeMgr); + +// required functions for PyObject interoperability +PyObject *pyVNodeMgr::New(PyObject* thaComm) +{ + ptVNodeMgr *newObj = (ptVNodeMgr*)ptVNodeMgr_type.tp_new(&ptVNodeMgr_type, NULL, NULL); + if (!pyNetClientComm::Check(thaComm)) + { + Py_DECREF(newObj); + return NULL; // bad parameter + } + newObj->fThis->setMyComm(thaComm); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptVNodeMgr, pyVNodeMgr) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptVNodeMgr, pyVNodeMgr) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyVNodeMgr::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptVNodeMgr); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +// glue functions +PYTHON_CLASS_DEFINITION(ptPlayerVNodeMgr, pyPlayerVNodeMgr); + +PYTHON_DEFAULT_NEW_DEFINITION(ptPlayerVNodeMgr, pyPlayerVNodeMgr) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptPlayerVNodeMgr) + +PYTHON_INIT_DEFINITION(ptPlayerVNodeMgr, args, keywords) +{ + PyObject* netClientComm = NULL; + if (!PyArg_ParseTuple(args, "O", &netClientComm)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptNetClientComm"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyNetClientComm::Check(netClientComm)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptNetClientComm"); + PYTHON_RETURN_INIT_ERROR; + } + self->fThis->setMyComm(netClientComm); + PYTHON_RETURN_INIT_OK; +} + +PYTHON_START_METHODS_TABLE(ptPlayerVNodeMgr) +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptPlayerVNodeMgr, pyVNodeMgr, "UNKNOWN"); + +// required functions for PyObject interoperability +PyObject *pyPlayerVNodeMgr::New(PyObject* thaComm) +{ + ptPlayerVNodeMgr *newObj = (ptPlayerVNodeMgr*)ptPlayerVNodeMgr_type.tp_new(&ptPlayerVNodeMgr_type, NULL, NULL); + if (!pyNetClientComm::Check(thaComm)) + { + Py_DECREF(newObj); + return NULL; // bad parameter + } + newObj->fThis->setMyComm(thaComm); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptPlayerVNodeMgr, pyPlayerVNodeMgr) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptPlayerVNodeMgr, pyPlayerVNodeMgr) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyPlayerVNodeMgr::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptPlayerVNodeMgr); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +// glue functions +PYTHON_CLASS_DEFINITION(ptAgeVNodeMgr, pyAgeVNodeMgr); + +PYTHON_DEFAULT_NEW_DEFINITION(ptAgeVNodeMgr, pyAgeVNodeMgr) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptAgeVNodeMgr) + +PYTHON_INIT_DEFINITION(ptAgeVNodeMgr, args, keywords) +{ + PyObject* netClientComm = NULL; + if (!PyArg_ParseTuple(args, "O", &netClientComm)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptNetClientComm"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyNetClientComm::Check(netClientComm)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptNetClientComm"); + PYTHON_RETURN_INIT_ERROR; + } + self->fThis->setMyComm(netClientComm); + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptAgeVNodeMgr, setAgeInfo, args) +{ + char* filename; + char* guid; + if (!PyArg_ParseTuple(args, "ss", &filename, &guid)) + { + PyErr_SetString(PyExc_TypeError, "setAgeInfo expects two strings"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetAgeInfo(filename, guid); + PYTHON_RETURN_NONE; +} + +PYTHON_START_METHODS_TABLE(ptAgeVNodeMgr) + PYTHON_METHOD(ptAgeVNodeMgr, setAgeInfo, "Params: filename,guid\nUNKNOWN"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptAgeVNodeMgr, pyVNodeMgr, "UNKNOWN"); + +// required functions for PyObject interoperability +PyObject *pyAgeVNodeMgr::New(PyObject* thaComm) +{ + ptAgeVNodeMgr *newObj = (ptAgeVNodeMgr*)ptAgeVNodeMgr_type.tp_new(&ptAgeVNodeMgr_type, NULL, NULL); + if (!pyNetClientComm::Check(thaComm)) + { + Py_DECREF(newObj); + return NULL; // bad parameter + } + newObj->fThis->setMyComm(thaComm); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptAgeVNodeMgr, pyAgeVNodeMgr) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptAgeVNodeMgr, pyAgeVNodeMgr) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyAgeVNodeMgr::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptAgeVNodeMgr); + PYTHON_CLASS_IMPORT_END(m); +} + +/////////////////////////////////////////////////////////////////////////////// + +// glue functions +PYTHON_CLASS_DEFINITION(ptAdminVNodeMgr, pyAdminVNodeMgr); + +PYTHON_DEFAULT_NEW_DEFINITION(ptAdminVNodeMgr, pyAdminVNodeMgr) +PYTHON_DEFAULT_DEALLOC_DEFINITION(ptAdminVNodeMgr) + +PYTHON_INIT_DEFINITION(ptAdminVNodeMgr, args, keywords) +{ + PyObject* netClientComm = NULL; + if (!PyArg_ParseTuple(args, "O", &netClientComm)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptNetClientComm"); + PYTHON_RETURN_INIT_ERROR; + } + if (!pyNetClientComm::Check(netClientComm)) + { + PyErr_SetString(PyExc_TypeError, "__init__ expects a ptNetClientComm"); + PYTHON_RETURN_INIT_ERROR; + } + self->fThis->setMyComm(netClientComm); + PYTHON_RETURN_INIT_OK; +} + +PYTHON_METHOD_DEFINITION(ptAdminVNodeMgr, setWantGlobalSDL, args) +{ + bool flag; + if (!PyArg_ParseTuple(args, "b", &flag)) + { + PyErr_SetString(PyExc_TypeError, "setWantGlobalSDL expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetWantGlobalSDL(flag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION(ptAdminVNodeMgr, setWantAllPlayers, args) +{ + bool flag; + if (!PyArg_ParseTuple(args, "b", &flag)) + { + PyErr_SetString(PyExc_TypeError, "setWantAllPlayers expects a boolean"); + PYTHON_RETURN_ERROR; + } + self->fThis->SetWantAllPlayers(flag != 0); + PYTHON_RETURN_NONE; +} + +PYTHON_METHOD_DEFINITION_NOARGS(ptAdminVNodeMgr, getGlobalInbox) +{ + return self->fThis->GetGlobalInbox(); +} + +PYTHON_START_METHODS_TABLE(ptAdminVNodeMgr) + PYTHON_METHOD(ptAdminVNodeMgr, setWantGlobalSDL, "Params: flag\nUNKNOWN"), + PYTHON_METHOD(ptAdminVNodeMgr, setWantAllPlayers, "Params: flag\nUNKNOWN"), + PYTHON_METHOD_NOARGS(ptAdminVNodeMgr, getGlobalInbox, "UNKNOWN"), +PYTHON_END_METHODS_TABLE; + +// Type structure definition +PLASMA_DEFAULT_TYPE_WBASE(ptAdminVNodeMgr, pyVNodeMgr, "UNKNOWN"); + +// required functions for PyObject interoperability +PyObject *pyAdminVNodeMgr::New(PyObject* thaComm) +{ + ptAdminVNodeMgr *newObj = (ptAdminVNodeMgr*)ptAdminVNodeMgr_type.tp_new(&ptAdminVNodeMgr_type, NULL, NULL); + if (!pyNetClientComm::Check(thaComm)) + { + Py_DECREF(newObj); + return NULL; // bad parameter + } + newObj->fThis->setMyComm(thaComm); + return (PyObject*)newObj; +} + +PYTHON_CLASS_CHECK_IMPL(ptAdminVNodeMgr, pyAdminVNodeMgr) +PYTHON_CLASS_CONVERT_FROM_IMPL(ptAdminVNodeMgr, pyAdminVNodeMgr) + +/////////////////////////////////////////////////////////////////////////// +// +// AddPlasmaClasses - the python module definitions +// +void pyAdminVNodeMgr::AddPlasmaClasses(PyObject *m) +{ + PYTHON_CLASS_IMPORT_START(m); + PYTHON_CLASS_IMPORT(m, ptAdminVNodeMgr); + PYTHON_CLASS_IMPORT_END(m); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/CheckFolderVar/CheckFolderVar.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/CheckFolderVar/CheckFolderVar.cpp new file mode 100644 index 00000000..2c5ea108 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/CheckFolderVar/CheckFolderVar.cpp @@ -0,0 +1,150 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +//////////////////////////////////////////////////////////////////// +// This little app checks to see if the specified envrionment +// variable exists, creating it if necessary. If the variable +// doesn't exist, the app prompts the user with the Browse for +// Folder dialog box then sets the envrionment variable, using the +// selected folder as the value. +// +// Example: +// C:\>CheckFolderVar maxr4dir "Select the folder where max is installed then click Ok." +// + + +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// + +std::string gCurrentValue; + +// get window handle of cancel button so we can disable it. +BOOL CALLBACK EnumChildWindowsCallbackProc(HWND hwnd,LPARAM lParam) +{ + char text[256]; + GetWindowText(hwnd,text,256); + if (stricmp(text,"Cancel")==0) + { + *((HWND*)lParam) = hwnd; + return FALSE; + } + return TRUE; +} + +int CALLBACK BrosweForFolderCallbackProc(HWND hwnd,UINT uMsg,LPARAM lp, LPARAM pData) +{ + switch(uMsg) + { + case BFFM_INITIALIZED: + // disable cancel button + HWND hCancelBtn = NULL; + EnumChildWindows(hwnd,EnumChildWindowsCallbackProc,(LPARAM)&hCancelBtn); + EnableWindow(hCancelBtn,FALSE); + SendMessage(hwnd,BFFM_SETSELECTION,true,(LPARAM)gCurrentValue.data()); + break; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +int main(int argc, char ** argv) +{ + if (argc<2) + { + fprintf(stderr,"Usage: CheckFolderVar varname [-replace] [\"prompt msg\"]\n"); + return EXIT_FAILURE; + } + + // read cmdline + char ** args = argv; + char * varname = NULL; + bool replace = false; + char prompt[1024] = ""; + for (int i=1; iFree(itemList); + shMalloc->Release(); + + // set environment var + RegSetValueEx(hEnvKey,varname,0,REG_SZ,(const BYTE*)value,strlen(value)); + + // close registry key + RegCloseKey(hEnvKey); + + // bubbye + return EXIT_SUCCESS; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxAss/ValdezInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxAss/ValdezInterface.h new file mode 100644 index 00000000..4f679ac1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxAss/ValdezInterface.h @@ -0,0 +1,96 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/****************************************************************************** + ValdezInterface.h + + Eric Ellis +******************************************************************************/ + +#ifndef _JV_VALDEZINTERFACE_H_ +#define _JV_VALDEZINTERFACE_H_ + +#define MAXASS_CLASS_ID Class_ID(0x5c61b5a6, 0x3b298521) +#define kMaxAssGetValdezInterface 33 + +#include +#include +#include +#include +#include +#include + +using std::vector; +using std::string; + +#pragma warning(disable:4786) + + +#define kAssetTypeIdTexure 1 +#define kAssetTypeIdSound 2 +#define kAssetTypeIdMaxFile 4 +#define kAssetTypeIdAge 7 + +#define kStatusIdDraft 1 +#define kStatusIdStable 2 + + + +class jvValdezInterface +{ +public: + // MAX File Replacement Operations + virtual int ChooseAssetAndOpen() = 0; + virtual int Save() = 0; + virtual int SaveAs() = 0; + virtual int Add() = 0; + virtual int OpenBitmapDlg(VARIANT* assetId, TCHAR* localFilenameRet, int localFilenameBufSize) = 0; + virtual int OpenSoundDlg(VARIANT* assetId, TCHAR* localFilenameRet, int localFilenameBufSize) = 0; + virtual int NewAgeDlg(VARIANT* assetId, TCHAR* localFilenameRet, int localFilenameBufSize) = 0; + virtual int NewTextureDlg(VARIANT* assetId, TCHAR* localFilenameRet, int localFilenameBufSize) = 0; + + // Asset Database Operations + virtual int GetLatestVersionFile(VARIANT& assetId, TCHAR* localFilenameRet, int localFilenameBufSize) = 0; + virtual int GetAssetsByType(int assetTypeId, vector<_variant_t>& assetIds, vector& assetNames) = 0; + virtual int CheckOutAsset(VARIANT& assetId, TCHAR* localFilenameRet, int localFilenameBufSize) = 0; + virtual int CheckInAsset(VARIANT& assetId, TCHAR* localFilename, int statusId, TCHAR* comments) = 0; + + virtual int FindAndCompareAssetByFilename(const TCHAR* localFilename, VARIANT* assetId, bool* filesMatch) = 0; + virtual int FindAssetsByFilename(const TCHAR* filename, vector<_variant_t>& assets) = 0; + + virtual int IsAssetCheckedOutLocally(VARIANT& assetId, bool& checkedOut) = 0; +}; + + +inline jvValdezInterface* GetValdezInterface() +{ + GUP* maxAssGup = OpenGupPlugIn(MAXASS_CLASS_ID); + + if(!maxAssGup) return NULL; + + return (jvValdezInterface*)maxAssGup->Control(kMaxAssGetValdezInterface); +} + +#endif _JV_VALDEZINTERFACE_H_ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/BipedKiller.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/BipedKiller.h new file mode 100644 index 00000000..eb19e542 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/BipedKiller.h @@ -0,0 +1,29 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +class INode; +class Interface; + +void RemoveBiped(INode *bipRoot, Interface *theInterface); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/ComponentDummies.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/ComponentDummies.h new file mode 100644 index 00000000..98e94ea7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/ComponentDummies.h @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +void DummyCodeIncludeFunc(); //Anim Comp +void DummyCodeIncludeFuncActive(); //Activator Comp +void DummyCodeIncludeFuncResponder(); //Responder Comp +void DummyCodeIncludeFuncAudio(); //Audio Comps +void DummyCodeIncludeFuncA(); //Player Comp +void DummyCodeIncludeFuncCollection(); // ??? Isn't used! ??? Why is it here? ??? +void DummyCodeIncludeFuncTypes(); //Type Comps (Portal, StartingPoint, etc...) +void DummyCodeIncludeFuncMisc(); //Misc Comps (Interesting, PageInfo, Room, etc..) +void DummyCodeIncludeFuncPhys(); //Physical Comps +void DummyCodeIncludeFuncParticles(); //Particle Comps +void DummyCodeIncludeAvatarFunc(); //Avatar Comps +void DummyCodeIncludeFuncSmooth(); //Smoot Comp +void DummyCodeIncludeFuncClickable(); //Clickable Comp +void DummyCodeIncludeFuncSeekPoint(); //Avatar Seekpoint Comp +void DummyCodeIncludeFuncSingleSht(); //OneShot Comp +void DummyCodeIncludeFuncAGComp(); //Avatar Anim Comp +void DummyCodeIncludeFuncClickDrag(); //click-draggable gadget +void DummyCodeIncludeFuncInventStuff(); //InventoryObj Comp +void DummyCodeIncludeFuncVolumeGadget();// phys volume activator +//void DummyCodeIncludeFuncActivatorGadget();// activator activator +void DummyCodeIncludeFuncImpactGadget(); // collision activator +void DummyCodeIncludeFuncSoftVolume(); // Soft Volume +void DummyCodeIncludeFuncPhysConst(); // Phys Constraints +void DummyCodeIncludeFuncCameras(); // new cameras +void DummyCodeIncludePythonFileFunc(); +void DummyCodeIncludeFuncDistrib(); // Geometry distribution functions +void DummyCodeIncludeFuncCluster(); // Geometry clustering functions +void DummyCodeIncludeFuncExcludeRegion(); +void DummyCodeIncludeFuncGUI(); // User Interface components +void DummyCodeIncludeFuncIgnore(); // User Interface components + +void DummyCodeIncludeFuncBlow(); // Procedural wind modifier +void DummyCodeIncludeFuncWater(); // All things wet. +void DummyCodeIncludeFuncLightMap(); // LightMap +void DummyCodeIncludeFuncXImposter(); // Like a billboard, but exier. +void DummyCodeIncludeFuncRepComp(); // Different representations +void DummyCodeIncludeFuncLineFollow(); // Things that follow a line +void DummyCodeIncludeFuncMorph(); // Like putty in my hands +void DummyCodeIncludeFuncLODFade(); // Alpha blending lod transition +void DummyCodeIncludeFuncBehaviors(); // Av Behaviors, like Sitting +void DummyCodeIncludeFuncNavigablesRegion(); //Ladder Climbing... +void DummyCodeIncludeFuncTemplate(); +void DummyCodeIncludeFuncClothing(); +void DummyCodeIncludeFuncMultistageBeh(); +void DummyCodeIncludeFuncAnimDetector(); +void DummyCodeIncludeFuncShadow(); // Components controlling shadow generation. +void DummyCodeIncludeFuncFootstepSound(); +void DummyCodeIncludeFuncFootPrint(); // dynamic decals +void DummyCodeIncludFuncNPCSpawn(); // npc spawners +void DummyCodeIncludeFuncClimbTrigger(); +void DummyCodeIncludeFuncObjectFlocker(); +void DummyCodeIncludeFuncGrassShader(); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/WavFileStructs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/WavFileStructs.h new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/WavFileStructs.h @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/icon1.ico b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/icon1.ico new file mode 100644 index 0000000000000000000000000000000000000000..b3bc37becaedb5b44f0aae53af523c6f0d263ee9 GIT binary patch literal 766 zcmd^7F%AMT4D%r*#xk+ekzc@*Zp&>%V&PfkDQt`&21er?LU%<-Fe2g9NgSs`r2>s2 z=Q#qW39wLIiCNr;4W`O~%X<%(DaZX$l+~h?q8bMK12Q^M5u-?dfxTDHH0!E?wKmpC zx*J_cEl}wfAGg-DGJsZV@I08m)#(p>fva|o;z8Vzml;_S=}U2~U!?kE=wR7uj~{Ud DoOPF; literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/pfGUISkinComp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/pfGUISkinComp.cpp new file mode 100644 index 00000000..b7dbce7f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/pfGUISkinComp.cpp @@ -0,0 +1,770 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "resource.h" +#include "hsTemplates.h" + +#include "pfGUISkinComp.h" +#include "plGUICompClassIDs.h" + +#include "../MaxMain/plMaxNodeBase.h" + + +pfGUISkinEditProc *pfGUISkinEditProc::fInstance = nil; +int pfGUISkinEditProc::fZoom = 3; // So re-opening the dialog will keep the same zoom level + +extern HINSTANCE hInstance; + +pfGUISkinEditProc::pfGUISkinEditProc( plGUISkinComp *comp ) +{ + fInstance = this; + fComp = comp; + + fDblDC = nil; + fDblBitmap = nil; + + fXOffset = fYOffset = 0; + + fCurrPBRefSet = plGUISkinComp::kRefUpLeftCorner; + + fDefPen = CreatePen( PS_SOLID, 3, RGB( 200, 0, 0 ) ); + fOtherPen = CreatePen( PS_DOT, 1, RGB( 200, 0, 0 ) ); + + fDragging = false; + fDragTimer = 0; + + // Back up the old settings + IParamBlock2 *pb = fComp->GetParamBlockByID( plComponent::kBlkComp ); + for( int i = 0; i < pfGUISkin::kNumElements; i++ ) + { + int id = i * 4 + plGUISkinComp::kRefUpLeftCorner; + fBackups[ i ].fX = pb->GetInt( id + 0 ); + fBackups[ i ].fY = pb->GetInt( id + 1 ); + fBackups[ i ].fWidth = pb->GetInt( id + 2 ); + fBackups[ i ].fHeight = pb->GetInt( id + 3 ); + } +} + +pfGUISkinEditProc::~pfGUISkinEditProc() +{ + fInstance = nil; + DeleteObject( fDefPen ); + IKillDblBuffer(); +} + +void pfGUISkinEditProc::IJustDrawOneRect( int whichElement, IParamBlock2 *pb, HDC hDC, HPEN whichPen, int refToIgnore ) +{ + whichElement = ( whichElement * 4 ) + plGUISkinComp::kRefUpLeftCorner; + + if( whichElement == refToIgnore ) + return; + + RECT r; + SetRect( &r, pb->GetInt( whichElement + 0 ), + pb->GetInt( whichElement + 1 ), + pb->GetInt( whichElement + 0 ) + pb->GetInt( whichElement + 2 ), + pb->GetInt( whichElement + 1 ) + pb->GetInt( whichElement + 3 ) ); + + r.left *= fZoom; r.right *= fZoom; r.top *= fZoom; r.bottom *= fZoom; + + SelectObject( hDC, whichPen ); + int rop2 = SetROP2( hDC, R2_NOTXORPEN ); + + MoveToEx( hDC, r.left, r.top, nil ); + LineTo( hDC, r.right, r.top ); + LineTo( hDC, r.right, r.bottom ); + LineTo( hDC, r.left, r.bottom ); + LineTo( hDC, r.left, r.top ); + + SetROP2( hDC, rop2 ); +} + +void pfGUISkinEditProc::IRefreshDblBuffer( void ) +{ + // Image buffer is where we keep our resized image. Dbl buffer is where we draw our bounds + if( fDblDC == nil ) + IInitDblBuffer(); + else + { + // Copy the zoomed image as our backdrop + BitBlt( fDblDC, 0, 0, fDblWidth, fDblHeight, fImageDC, 0, 0, SRCCOPY ); + + RECT r; + IParamBlock2 *pb = fComp->GetParamBlockByID( plComponent::kBlkComp ); + if( pb != nil ) + { + // Draw all the other elements other than our current one + for( int i = 0; i < pfGUISkin::kNumElements; i++ ) + IJustDrawOneRect( i, pb, fDblDC, fOtherPen, fCurrPBRefSet ); + + // Now draw the bounds of our current element + SetRect( &r, pb->GetInt( fCurrPBRefSet + 0 ), + pb->GetInt( fCurrPBRefSet + 1 ), + pb->GetInt( fCurrPBRefSet + 0 ) + pb->GetInt( fCurrPBRefSet + 2 ), + pb->GetInt( fCurrPBRefSet + 1 ) + pb->GetInt( fCurrPBRefSet + 3 ) ); + + // While we have it here, go ahead and update our status text for this element + char str[ 256 ]; + sprintf( str, "Left: %d\nTop: %d\nWidth: %d\nHeight: %d\n", r.left, r.top, r.right - r.left, r.bottom - r.top ); + SetDlgItemText( fHWnd, IDC_GUI_INFO, str ); + + r.left *= fZoom; r.right *= fZoom; r.top *= fZoom; r.bottom *= fZoom; + + SelectObject( fDblDC, fDefPen ); + int rop2 = SetROP2( fDblDC, R2_NOTXORPEN ); + + MoveToEx( fDblDC, r.left, r.top, nil ); + LineTo( fDblDC, r.right, r.top ); + LineTo( fDblDC, r.right, r.bottom ); + LineTo( fDblDC, r.left, r.bottom ); + LineTo( fDblDC, r.left, r.top ); + + SetROP2( fDblDC, rop2 ); + + fCurrElemRect = r; + MapWindowPoints( GetDlgItem( fHWnd, IDC_GUI_PREVIEW ), fHWnd, (POINT *)&fCurrElemRect, 2 ); + OffsetRect( &fCurrElemRect, -fXOffset, -fYOffset ); + } + } +} + +void pfGUISkinEditProc::IRefreshImageBuffer( void ) +{ + IInitDblBuffer(); + + plLayerTex *layer = fComp->GetSkinBitmap(); + PBBitmap *pbBMap = layer->GetPBBitmap(); + + if( pbBMap->bm == nil ) + pbBMap->Load(); + if( pbBMap->bm != nil ) + { + // Copy into a new temp bitmap that is the right format for us to read + Bitmap *newBM; + BitmapInfo bi; + bi.SetName( _T("y879873b") ); + bi.SetType( BMM_TRUE_32 ); + bi.SetFlags( MAP_HAS_ALPHA ); + bi.SetWidth( fDblWidth ); + bi.SetHeight( fDblHeight ); + newBM = TheManager->Create( &bi ); + + BMM_Color_64 foo = { 0, 0, 0, 0 }; + newBM->CopyImage( pbBMap->bm, COPY_IMAGE_RESIZE_LO_QUALITY, foo, nil ); + + // Now copy from our newly created bitmap into our DC....way slow :( + BITMAPINFO *bitInfo = newBM->ToDib( 24, nil, false ); + if( bitInfo != nil ) + { + SetDIBitsToDevice( fImageDC, 0, 0, fDblWidth, fDblHeight, + 0, 0, 0, fDblHeight, + ( (UInt8 *)bitInfo ) + bitInfo->bmiHeader.biSize, + bitInfo, + DIB_RGB_COLORS ); + } + + newBM->DeleteThis(); + } + + IRefreshDblBuffer(); +} + +void pfGUISkinEditProc::IInitDblBuffer( void ) +{ + if( fDblDC == NULL ) + { + int width, height; + HDC desk = GetDC( NULL ); + + plLayerTex *layer = fComp->GetSkinBitmap(); + PBBitmap *pbBMap = layer->GetPBBitmap(); + if( pbBMap == nil ) + return; + width = pbBMap->bi.Width() * fZoom; + height = pbBMap->bi.Height() * fZoom; +// GetClientRect( fHWnd, &r ); +// width = r.right - r.left; +// height = r.bottom - r.top; + + // Note: For some strange reason, grabbing the HDC of the window doesn't do + // any good, 'cause it's black-and-white (right, THAT makes sense). Grabbing + // the desktop DC works, however. + + fDblDC = CreateCompatibleDC( desk ); + fDblBitmap = CreateCompatibleBitmap( desk/*fDblDC*/, width, height ); + SelectObject( fDblDC, fDblBitmap ); + + fImageDC = CreateCompatibleDC( desk ); + fImageBitmap = CreateCompatibleBitmap( desk/*fDblDC*/, width, height ); + SelectObject( fImageDC, fImageBitmap ); + + ReleaseDC( NULL, desk ); + + fDblWidth = width; + fDblHeight = height; + + ISetScrollRanges(); + IRefreshImageBuffer(); + } +} + +void pfGUISkinEditProc::IKillDblBuffer( void ) +{ + if( fDblDC != NULL ) + { + SelectObject( fDblDC, (HBITMAP)NULL ); + DeleteObject( fDblBitmap ); + DeleteDC( fDblDC ); + } + + if( fImageDC != NULL ) + { + SelectObject( fImageDC, (HBITMAP)NULL ); + DeleteObject( fImageBitmap ); + DeleteDC( fImageDC ); + } + + fDblDC = fImageDC = nil; + fDblBitmap = fImageBitmap = nil; +} + +void pfGUISkinEditProc::ISetScrollRanges( void ) +{ + SCROLLINFO info; + + + int visW = fPreviewRect.right - fPreviewRect.left; + int visH = fPreviewRect.bottom - fPreviewRect.top; + + if( visW < fDblWidth ) + { + if( fXOffset > fDblWidth - visW ) + fXOffset = fDblWidth - visW; + else if( fXOffset < 0 ) + fXOffset = 0; + + info.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + info.nMin = 0; + info.nMax = fDblWidth;// - visW; + info.nPage = visW; + info.nPos = fXOffset; + info.cbSize = sizeof( info ); + + SetScrollInfo( GetDlgItem( fHWnd, IDC_GUI_HORZSCROLL ), SB_CTL, &info, true ); + ShowWindow( GetDlgItem( fHWnd, IDC_GUI_HORZSCROLL ), true ); + } + else + { + ShowWindow( GetDlgItem( fHWnd, IDC_GUI_HORZSCROLL ), false ); + fXOffset = 0; + } + + if( visH < fDblHeight ) + { + if( fYOffset > fDblHeight - visH ) + fYOffset = fDblHeight - visH; + else if( fYOffset < 0 ) + fYOffset = 0; + + info.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + info.nMin = 0; + info.nMax = fDblHeight;// - visH; + info.nPage = visH; + info.nPos = fYOffset; + info.cbSize = sizeof( info ); + + SetScrollInfo( GetDlgItem( fHWnd, IDC_GUI_VERTSCROLL ), SB_CTL, &info, true ); + ShowWindow( GetDlgItem( fHWnd, IDC_GUI_VERTSCROLL ), true ); + } + else + { + ShowWindow( GetDlgItem( fHWnd, IDC_GUI_VERTSCROLL ), false ); + fYOffset = 0; + } +} + +bool pfGUISkinEditProc::IPointWithinRange( int x, int y, int ptX, int ptY ) +{ + if( x > ptX - kRangeSlop && x < ptX + kRangeSlop && + y > ptY - kRangeSlop && y < ptY + kRangeSlop ) + return true; + return false; +} + +bool pfGUISkinEditProc::IPointWithinVertRange( int x, int y, int ptX, int ptY1, int ptY2 ) +{ + if( x > ptX - kRangeSlop && x < ptX + kRangeSlop && + y > ptY1 - kRangeSlop && y < ptY2 + kRangeSlop ) + return true; + return false; +} + +bool pfGUISkinEditProc::IPointWithinHorzRange( int x, int y, int ptX1, int ptX2, int ptY ) +{ + if( x > ptX1 - kRangeSlop && x < ptX2 + kRangeSlop && + y > ptY - kRangeSlop && y < ptY + kRangeSlop ) + return true; + return false; +} + +UInt8 pfGUISkinEditProc::IGetDragTypeFlags( int x, int y ) +{ + // Corners + if( IPointWithinRange( x, y, fCurrElemRect.left, fCurrElemRect.top ) ) + return kLeft | kTop; + if( IPointWithinRange( x, y, fCurrElemRect.right, fCurrElemRect.top ) ) + return kRight | kTop; + if( IPointWithinRange( x, y, fCurrElemRect.right, fCurrElemRect.bottom ) ) + return kRight | kBottom; + if( IPointWithinRange( x, y, fCurrElemRect.left, fCurrElemRect.bottom ) ) + return kLeft | kBottom; + + // Edges + if( IPointWithinVertRange( x, y, fCurrElemRect.left, fCurrElemRect.top, fCurrElemRect.bottom ) ) + return kLeft; + if( IPointWithinVertRange( x, y, fCurrElemRect.right, fCurrElemRect.top, fCurrElemRect.bottom ) ) + return kRight; + + if( IPointWithinHorzRange( x, y, fCurrElemRect.left, fCurrElemRect.right, fCurrElemRect.top ) ) + return kTop; + if( IPointWithinHorzRange( x, y, fCurrElemRect.left, fCurrElemRect.right, fCurrElemRect.bottom ) ) + return kBottom; + + // The middle + if( x >= fCurrElemRect.left && x <= fCurrElemRect.right && y >= fCurrElemRect.top && y <= fCurrElemRect.bottom ) + return kDragAll; + + return 0; +} + +INT_PTR CALLBACK pfGUISkinEditProc::DlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + return fInstance->DialogProc( hDlg, msg, wParam, lParam ); +} + +INT_PTR CALLBACK pfGUISkinEditProc::DialogProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + PAINTSTRUCT pInfo; + RECT r; + HDC hDC; + int maxDim, i, j, x, y; + bool timerActive = false; + + + static struct plElemPair + { + pfGUISkin::Elements el; + const char *name; + } sElemPairs[] = { { pfGUISkin::kUpLeftCorner, "Upper-left Corner" }, + { pfGUISkin::kTopSpan, "Top Span" }, + { pfGUISkin::kUpRightCorner, "Upper-right Corner" }, + { pfGUISkin::kRightSpan, "Right Span" }, + { pfGUISkin::kLowerRightCorner, "Lower-right Corner" }, + { pfGUISkin::kBottomSpan, "Bottom Span" }, + { pfGUISkin::kLowerLeftCorner, "Lower-left Corner" }, + { pfGUISkin::kLeftSpan, "Left Span" }, + { pfGUISkin::kMiddleFill, "Middle Fill" }, + { pfGUISkin::kSelectedFill, "Selected Middle Fill" }, + { pfGUISkin::kSubMenuArrow, "Sub-Menu Arrow" }, + { pfGUISkin::kSelectedSubMenuArrow, "Selected Sub-Menu Arrow" }, + { pfGUISkin::kTreeButtonClosed, "Tree-view Button, Closed" }, + { pfGUISkin::kTreeButtonOpen, "Tree-view Button, Open" }, + { pfGUISkin::kNumElements, nil } }; + + + fHWnd = hDlg; + + switch( msg ) + { + case WM_INITDIALOG: + + // Get preview rect + GetClientRect( GetDlgItem( hDlg, IDC_GUI_PREVIEW ), &fPreviewRect ); + MapWindowPoints( GetDlgItem( hDlg, IDC_GUI_PREVIEW ), hDlg, (POINT *)&fPreviewRect, 2 ); + + SendDlgItemMessage( hDlg, IDC_GUI_ZIN, BM_SETIMAGE, (WPARAM)IMAGE_ICON, + (LPARAM)LoadImage( hInstance, MAKEINTRESOURCE( IDI_ZOOMIN ), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR ) ); + SendDlgItemMessage( hDlg, IDC_GUI_ZOUT, BM_SETIMAGE, (WPARAM)IMAGE_ICON, + (LPARAM)LoadImage( hInstance, MAKEINTRESOURCE( IDI_ZOOMOUT ), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR ) ); + + // Fill element list + SendDlgItemMessage( hDlg, IDC_GUI_ELEMENTS, LB_RESETCONTENT, 0, 0 ); + for( i = 0; sElemPairs[ i ].el != pfGUISkin::kNumElements; i++ ) + { + int idx = SendDlgItemMessage( hDlg, IDC_GUI_ELEMENTS, LB_ADDSTRING, 0, (LPARAM)sElemPairs[ i ].name ); + SendDlgItemMessage( hDlg, IDC_GUI_ELEMENTS, LB_SETITEMDATA, (WPARAM)idx, (LPARAM)sElemPairs[ i ].el ); + if( sElemPairs[ i ].el == pfGUISkin::kUpLeftCorner ) + j = idx; + } + SendDlgItemMessage( hDlg, IDC_GUI_ELEMENTS, LB_SETCURSEL, j, 0 ); + + fOrigCursor = LoadCursor( nil, IDC_ARROW );//GetCursor(); + + break; + + case WM_COMMAND: + if( LOWORD( wParam ) == IDCANCEL ) + { + // Since we've been editing the PB directly, we have to now restore them + // to their original values + IParamBlock2 *pb = fComp->GetParamBlockByID( plComponent::kBlkComp ); + for( int i = 0; i < pfGUISkin::kNumElements; i++ ) + { + int id = i * 4 + plGUISkinComp::kRefUpLeftCorner; + pb->SetValue( id + 0, 0, (int)fBackups[ i ].fX ); + pb->SetValue( id + 1, 0, (int)fBackups[ i ].fY ); + pb->SetValue( id + 2, 0, (int)fBackups[ i ].fWidth ); + pb->SetValue( id + 3, 0, (int)fBackups[ i ].fHeight ); + } + EndDialog( hDlg, 1 ); + } + else if( LOWORD( wParam ) == IDOK ) + EndDialog( hDlg, 0 ); + else if( LOWORD( wParam ) == IDC_GUI_ZIN ) + { + fXOffset /= fZoom; fYOffset /= fZoom; + fZoom++; + fXOffset *= fZoom; fYOffset *= fZoom; + + IKillDblBuffer(); + IRefreshImageBuffer(); + InvalidateRect( hDlg, &fPreviewRect, false ); + } + else if( LOWORD( wParam ) == IDC_GUI_ZOUT ) + { + if( fZoom > 1 ) + { + fXOffset /= fZoom; fYOffset /= fZoom; + fZoom--; + fXOffset *= fZoom; fYOffset *= fZoom; + + IKillDblBuffer(); + IRefreshImageBuffer(); + InvalidateRect( hDlg, &fPreviewRect, false ); + } + } + else if( LOWORD( wParam ) == IDC_GUI_ELEMENTS ) + { + int idx = SendDlgItemMessage( hDlg, IDC_GUI_ELEMENTS, LB_GETCURSEL, 0, 0 ); + fCurrPBRefSet = SendDlgItemMessage( hDlg, IDC_GUI_ELEMENTS, LB_GETITEMDATA, (WPARAM)idx, 0 ) * 4 + plGUISkinComp::kRefUpLeftCorner; + + IRefreshDblBuffer(); + InvalidateRect( hDlg, &fPreviewRect, false ); + } + return true; + + case WM_CLOSE: + EndDialog( hDlg, 0 ); + return true; + + case WM_HSCROLL: + OffsetRect( &fCurrElemRect, fXOffset, fYOffset ); + switch( LOWORD( wParam ) ) + { + case SB_PAGEUP: fXOffset -= 300; break; + case SB_PAGEDOWN: fXOffset += 300; break; + case SB_LINEUP: fXOffset -= 16; break; + case SB_LINEDOWN: fXOffset += 16; break; + case SB_THUMBPOSITION: + case SB_THUMBTRACK: + fXOffset = HIWORD( wParam ); + break; + } + maxDim = fDblWidth - ( fPreviewRect.right - fPreviewRect.left ); + if( fXOffset < 0 ) + fXOffset = 0; + else if( fXOffset > maxDim ) + fXOffset = maxDim; + SetScrollPos( GetDlgItem( hDlg, IDC_GUI_HORZSCROLL ), SB_CTL, fXOffset, true ); + + OffsetRect( &fCurrElemRect, -fXOffset, -fYOffset ); + InvalidateRect( hDlg, &fPreviewRect, false ); + break; + + case WM_VSCROLL: + OffsetRect( &fCurrElemRect, fXOffset, fYOffset ); + switch( LOWORD( wParam ) ) + { + case SB_PAGEUP: fYOffset -= 300; break; + case SB_PAGEDOWN: fYOffset += 300; break; + case SB_LINEUP: fYOffset -= 16; break; + case SB_LINEDOWN: fYOffset += 16; break; + case SB_THUMBPOSITION: + case SB_THUMBTRACK: + fYOffset = HIWORD( wParam ); + break; + } + maxDim = fDblHeight - ( fPreviewRect.bottom - fPreviewRect.top ); + if( fYOffset < 0 ) + fYOffset = 0; + else if( fYOffset > maxDim ) + fYOffset = maxDim; + SetScrollPos( GetDlgItem( hDlg, IDC_GUI_VERTSCROLL ), SB_CTL, fYOffset, true ); + + OffsetRect( &fCurrElemRect, -fXOffset, -fYOffset ); + InvalidateRect( hDlg, &fPreviewRect, false ); + break; + + case WM_PAINT: + { + BeginPaint( hDlg, &pInfo ); + hDC = (HDC)pInfo.hdc; + + if( fDblDC == NULL ) + IInitDblBuffer(); + + int width = fDblWidth; + int height = fDblHeight; + if( width > fPreviewRect.right - fPreviewRect.left ) + width = fPreviewRect.right - fPreviewRect.left; + if( height > fPreviewRect.bottom - fPreviewRect.top ) + height = fPreviewRect.bottom - fPreviewRect.top; + + BitBlt( hDC, fPreviewRect.left, fPreviewRect.top, width, height, fDblDC, fXOffset, fYOffset, SRCCOPY ); + + r = fPreviewRect; + r.left += width; + FillRect( hDC, &r, ColorMan()->GetBrush( kBackground ) ); + + r = fPreviewRect; + r.top += height; + FillRect( hDC, &r, ColorMan()->GetBrush( kBackground ) ); + + EndPaint( hDlg, &pInfo ); + } + break; + + case WM_LBUTTONDOWN: + SetCapture( hDlg ); + fDragging = true; + fDragType = IGetDragTypeFlags( GET_X_LPARAM( lParam ), GET_Y_LPARAM( lParam ) ); + if( fDragType == kDragAll ) + { + fDragOffsetX = fCurrElemRect.left - GET_X_LPARAM( lParam ); + fDragOffsetY = fCurrElemRect.top - GET_Y_LPARAM( lParam ); + } + else if( fDragType == 0 ) + { + fDragOffsetX = GET_X_LPARAM( lParam ) + fXOffset; + fDragOffsetY = GET_Y_LPARAM( lParam ) + fYOffset; + } + else + fDragOffsetX = fDragOffsetY = 0; + + break; + + case WM_LBUTTONUP: + ReleaseCapture(); + fDragging = false; + break; + + case WM_TIMER: + // We do the same processing as MOUSEMOVE, but we need to make sure we have the right + // mouse position first + { + POINT pt; + GetCursorPos( &pt ); + MapWindowPoints( nil, hDlg, &pt, 1 ); + lParam = MAKELPARAM( pt.x, pt.y ); + } + // Fall thru... + + case WM_MOUSEMOVE: + x = GET_X_LPARAM( lParam ); + y = GET_Y_LPARAM( lParam ); + + if( fDragging ) + { + if( fDragType == 0 ) + { + fXOffset = fDragOffsetX - x; + fYOffset = fDragOffsetY - y; + ISetScrollRanges(); // Will clamp offset \for us + } + else + { + // Translate x and y into bitmap space + POINT pt; + pt.x = x; + pt.y = y; + + if( PtInRect( &fPreviewRect, pt ) ) + { + MapWindowPoints( hDlg, GetDlgItem( hDlg, IDC_GUI_PREVIEW ), &pt, 1 ); + + pt.x += fDragOffsetX; + pt.y += fDragOffsetY; + + // Note the + 1/2 zoom so it's the closest pixel by center, not by area + x = ( pt.x + fXOffset + ( fZoom >> 1 ) ) / fZoom; + y = ( pt.y + fYOffset + ( fZoom >> 1 ) ) / fZoom; + + // Set depending on our current drag flags + // Note the logic here: if we drag left, we want width and left changing, + // if we drag both, just left, if we drag right, just width + IParamBlock2 *pb = fComp->GetParamBlockByID( plComponent::kBlkComp ); + if( fDragType & kLeft ) + { + if( fDragType & kRight ) + pb->SetValue( fCurrPBRefSet + 0, 0, (int)x ); + else + { + int old = pb->GetInt( fCurrPBRefSet + 0 ) + pb->GetInt( fCurrPBRefSet + 2 ); + + pb->SetValue( fCurrPBRefSet + 0, 0, (int)x ); + pb->SetValue( fCurrPBRefSet + 2, 0, (int)old - x ); + } + } + else if( fDragType & kRight ) + pb->SetValue( fCurrPBRefSet + 2, 0, (int)x - pb->GetInt( fCurrPBRefSet + 0 ) ); + + if( fDragType & kTop ) + { + if( fDragType & kBottom ) + pb->SetValue( fCurrPBRefSet + 1, 0, (int)y ); + else + { + int old = pb->GetInt( fCurrPBRefSet + 1 ) + pb->GetInt( fCurrPBRefSet + 3 ); + + pb->SetValue( fCurrPBRefSet + 1, 0, (int)y ); + pb->SetValue( fCurrPBRefSet + 3, 0, (int)old - y ); + } + } + else if( fDragType & kBottom ) + pb->SetValue( fCurrPBRefSet + 3, 0, (int)y - pb->GetInt( fCurrPBRefSet + 1 ) ); + + // Clamp width and height + if( pb->GetInt( fCurrPBRefSet + 2 ) < 0 ) + pb->SetValue( fCurrPBRefSet + 2, 0, (int)0 ); + if( pb->GetInt( fCurrPBRefSet + 3 ) < 0 ) + pb->SetValue( fCurrPBRefSet + 3, 0, (int)0 ); + + // Clamp X and Y + if( pb->GetInt( fCurrPBRefSet + 0 ) < 0 ) + pb->SetValue( fCurrPBRefSet + 0, 0, (int)0 ); + else if( pb->GetInt( fCurrPBRefSet + 0 ) + pb->GetInt( fCurrPBRefSet + 2 ) > fDblWidth / fZoom ) + pb->SetValue( fCurrPBRefSet + 0, 0, (int)fDblWidth / fZoom - pb->GetInt( fCurrPBRefSet + 2 ) ); + + if( pb->GetInt( fCurrPBRefSet + 1 ) < 0 ) + pb->SetValue( fCurrPBRefSet + 1, 0, (int)0 ); + else if( pb->GetInt( fCurrPBRefSet + 1 ) + pb->GetInt( fCurrPBRefSet + 3 ) > fDblHeight / fZoom ) + pb->SetValue( fCurrPBRefSet + 1, 0, (int)fDblHeight / fZoom - pb->GetInt( fCurrPBRefSet + 3 ) ); + } + else + { + // Mouse is outside our preview, so scroll if possible + int dX = ( x < fPreviewRect.left ) ? x - fPreviewRect.left : ( x > fPreviewRect.right ) ? x - fPreviewRect.right : 0; + int dY = ( y < fPreviewRect.top ) ? y - fPreviewRect.top : ( y > fPreviewRect.bottom ) ? y - fPreviewRect.bottom : 0; + + fXOffset += dX; + fYOffset += dY; + OffsetRect( &fCurrElemRect, -dX, -dY ); + + ISetScrollRanges(); // Will clamp origin for us + + // Don't actually drag our bounds, 'cause we're scrolling + + // Note: since we only get MOUSEMOVE when, gee, the mouse moves, if we've scrolled over, it'll only + // do it once and then wait for the mouse to nudge again. We'd rather it keep going until the user + // moves the mouse again, so we create a timer that calls us back in n somethingths so we can check again + if( fDragTimer == 0 ) + fDragTimer = SetTimer( hDlg, 0, 200, nil ); + timerActive = true; // So we don't kill it at the end here... + } + } + + IRefreshDblBuffer(); + InvalidateRect( hDlg, &fPreviewRect, false ); + } + else + { + UInt8 dragType = IGetDragTypeFlags( x, y ); + HCURSOR cursor; + switch( dragType ) + { + case kLeft | kTop: + case kRight | kBottom: + cursor = LoadCursor( nil, IDC_SIZENWSE ); + break; + case kLeft | kBottom: + case kRight | kTop: + cursor = LoadCursor( nil, IDC_SIZENESW ); + break; + case kLeft: + case kRight: + cursor = LoadCursor( nil, IDC_SIZEWE ); + break; + case kTop: + case kBottom: + cursor = LoadCursor( nil, IDC_SIZENS ); + break; + case kLeft | kTop | kRight | kBottom: + cursor = LoadCursor( nil, IDC_SIZEALL ); + break; + default: + { + POINT pt; + pt.x = x; + pt.y = y; + if( PtInRect( &fPreviewRect, pt ) ) + cursor = LoadCursor( nil, IDC_HAND ); + else + cursor = fOrigCursor; + } + break; + } + + SetCursor( cursor ); + + if( !timerActive ) + { + // No longer need our trick timer, so kill it + KillTimer( hDlg, fDragTimer ); + fDragTimer = 0; + } + } + break; + } + + return false; +} + +plGUISkinComp *plGUISkinComp::GetGUIComp( INode *node ) +{ + if( node == nil ) + return nil; + + plComponentBase *base = ( ( plMaxNodeBase *)node )->ConvertToComponent(); + if( base == nil ) + return nil; + + if( base->ClassID() == GUI_SKIN_CLASSID ) + return (plGUISkinComp *)base; + + return nil; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/pfGUISkinComp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/pfGUISkinComp.h new file mode 100644 index 00000000..86a214be --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/pfGUISkinComp.h @@ -0,0 +1,154 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _pfGUISkinComp_h +#define _pfGUISkinComp_h + + +#include "plComponent.h" +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" +#include "../pfGameGUIMgr/pfGUIPopUpMenu.h" + +/// skin component class +class plGUISkinComp : public plComponent +{ +public: + plGUISkinComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + plLayerTex *GetSkinBitmap( void ); + + virtual UInt32 GetNumMtls( void ) const; + virtual Texmap *GetMtl( UInt32 idx ); + + enum + { + kRefBitmap = 256, // So we can share it among other components + kRefUpLeftCorner = 257, + kRefTopSpan = 257 + 4, + kRefUpRightCorner = 257 + 8, + kRefRightSpan = 257 + 12, + kRefLowerRightCorner = 257 + 16, + kRefBottomSpan = 257 + 20, + kRefLowerLeftCorner = 257 + 24, + kRefLeftSpan = 257 + 28, + kRefMiddleFill = 257 + 32, + kRefSelectedFill = 257 + 36, + kRefSubMenuArrow = 257 + 40, + kRefSelectedSubMenuArrow = 257 + 44, + kRefTreeButtonClosed = 257 + 48, + kRefTreeButtonOpen = 257 + 52, + kRefItemMargin = 400, + kRefBorderMargin + }; + + pfGUISkin *GetConvertedSkin( void ) const { return fConvertedSkin; } + plKey GetConvertedSkinKey( void ) const; + + // Given an INode, gives you a pointer to the GUI component if it actually is one, nil otherwise + static plGUISkinComp *GetGUIComp( INode *node ); + +protected: + + pfGUISkin *fConvertedSkin; +}; + +/// skin editor proc class +class pfGUISkinEditProc +{ + protected: + static pfGUISkinEditProc *fInstance; + + static int fZoom; + + plGUISkinComp *fComp; + RECT fPreviewRect, fCurrElemRect; + HDC fDblDC, fImageDC; + HBITMAP fDblBitmap, fImageBitmap; + int fDblWidth, fDblHeight; + int fXOffset, fYOffset; + int fCurrPBRefSet; + HWND fHWnd; + HPEN fDefPen, fOtherPen; + + bool fDragging; + UInt8 fDragType; + int fDragTimer; + int fDragOffsetX, fDragOffsetY; + HCURSOR fOrigCursor; + + pfGUISkin::pfSRect fBackups[ pfGUISkin::kNumElements ]; + + + void IRefreshDblBuffer( void ); + void IRefreshImageBuffer( void ); + void IInitDblBuffer( void ); + void IKillDblBuffer( void ); + + void ISetScrollRanges( void ); + + enum + { + kRangeSlop = 4 + }; + + bool IPointWithinRange( int x, int y, int ptX, int ptY ); + bool IPointWithinVertRange( int x, int y, int ptX, int ptY1, int ptY2 ); + bool IPointWithinHorzRange( int x, int y, int ptX1, int ptX2, int ptY ); + + enum DragTypeFlags + { + kLeft = 0x01, + kTop = 0x02, + kRight = 0x04, + kBottom = 0x08, + kDragAll = kLeft | kTop | kRight | kBottom + }; + + UInt8 IGetDragTypeFlags( int x, int y ); + + void IJustDrawOneRect( int whichElement, IParamBlock2 *pb, HDC hDC, HPEN whichPen, int refToIgnore ); + + public: + + pfGUISkinEditProc( plGUISkinComp *comp ); + ~pfGUISkinEditProc(); + + static INT_PTR CALLBACK DlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ); + + INT_PTR CALLBACK DialogProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ); +}; + + +#endif // _pfGUISkinComp_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAGComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAGComponents.cpp new file mode 100644 index 00000000..8ef05516 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAGComponents.cpp @@ -0,0 +1,445 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +//Resource related +#include "resource.h" + +//Max related +#include "plComponent.h" +#include "plComponentReg.h" + +//Messages related +#include "plgDispatch.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +//Scene related +#include "../plScene/plSceneNode.h" +#include "../plInterp/plController.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxMain/plMaxNodeData.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "hsResMgr.h" + +//Conversion related +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/hsControlConverter.h" + +//Avatar related +#include "../plAvatar/plAGAnim.h" +#include "../plAvatar/plMatrixChannel.h" +#include "BipedKiller.h" + +//Anim related +#include "plNotetrackAnim.h" + + +// +// DummyCodeIncludeFuncAGComp Function +// Necessary to keep the compiler from tossing this file. +// No functions herein are directly called, excepting this +// one. +// +// +void DummyCodeIncludeFuncAGComp() +{ +} + +enum { + kShareableBool, //Added in v1 + kGlobalBool, //Added in v1 + }; + +////////////////////////////////////////////////////////////// +// +// AnimAvatar Component +// +// +// +class plAnimAvatarComponent : public plComponent +{ +//protected: +// static plAGAnimMgr *fManager; +public: + plAnimAvatarComponent(); + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg *pErrMsg); + + virtual hsBool Convert(plMaxNode* node, plErrorMsg *pErrMsg); + + virtual plATCAnim * NewAnimation(const char *name, double begin, double end); + + hsBool ConvertNode(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool ConvertNodeSegmentBranch(plMaxNode *node, plAGAnim *mod, plErrorMsg *pErrMsg); + hsBool MakePersistent(plMaxNode *node, plAGAnim *anim, const char *animName, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } + + void DeleteThis() { delete this; } +}; + +//plAGAnimMgr * plAnimAvatarComponent::fManager = nil; + +CLASS_DESC(plAnimAvatarComponent, gAnimAvatarDesc, "Compound Animation", "Compound Animation", COMP_TYPE_AVATAR, Class_ID(0x3192253d, 0x60c4178c)) + +// +// Anim Avatar ParamBlock2 +// +// +ParamBlockDesc2 gAnimAvatarBk +( + plComponent::kBlkComp, _T("CompoundAnim"), 0, &gAnimAvatarDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_ANIM_AVATAR, IDS_COMP_ANIM_AVATARS, 0, 0, NULL, + + // params + kShareableBool, _T("ShareableBool"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_AVATAR_SHAREBOOL, + end, + + kGlobalBool, _T("ShareableBool"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_AVATAR_GLOBALBOOL, + end, + + //kBoundCondRadio, _T("BoundingConditions"), TYPE_INT, 0, 0, + // p_ui, TYPE_RADIO, 2, IDC_COMP_PHYS_DETECTOR_RAD1, IDC_COMP_PHYS_DETECTOR_RAD2, + // end, + + end +); + + +// +// Anim Avatar CONSTRUCTOR +// +// +plAnimAvatarComponent::plAnimAvatarComponent() +{ + fClassDesc = &gAnimAvatarDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + +// +// Anim Avatar PRECONVERT +// +// +hsBool plAnimAvatarComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if(node->GetMaxNodeData()) + { + node->SetMovable(true); + node->SetForceLocal(true); + node->SetDrawable(false); + } + + int childCount = node->NumberOfChildren(); + for (int i = 0; i < childCount; i++) + { + SetupProperties((plMaxNode *)node->GetChildNode(i), pErrMsg); + } + + return true; +} + +// +// +// CONVERT +// top level conversion: recursive descent on the node and children +// for each node, search for segments to convert... +// +// +hsBool plAnimAvatarComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + Interface *theInterface = node->GetInterface(); + RemoveBiped(node, theInterface); + + ConvertNode(node, pErrMsg); + + ((plSceneNode *)node->GetRoomKey()->GetObjectPtr())->SetFilterGenericsOnly(true); + + return true; +} + +// +// CONVERTNODE +// look for all the segments on this node and convert them +// recurse on children +// +// +hsBool plAnimAvatarComponent::ConvertNode(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plNotetrackAnim noteAnim(node, pErrMsg); + // does this node have any segments specified? + if (noteAnim.HasNotetracks()) + { + // for each segment we found: + while (const char *animName = noteAnim.GetNextAnimName()) + { + plAnimInfo info = noteAnim.GetAnimInfo(animName); + + plATCAnim *anim = NewAnimation(info.GetAnimName(), info.GetAnimStart(), info.GetAnimEnd()); + + const char *loopName = info.GetNextLoopName(); + if (loopName) + { + anim->SetLoop(true); + hsScalar loopStart = info.GetLoopStart(loopName); + hsScalar loopEnd = info.GetLoopEnd(loopName); + anim->SetLoopStart(loopStart == -1 ? anim->GetStart() : loopStart); + anim->SetLoopEnd(loopEnd == -1 ? anim->GetEnd() : loopEnd); + } + while (const char *marker = info.GetNextMarkerName()) + anim->AddMarker(marker, info.GetMarkerTime(marker)); + + ConvertNodeSegmentBranch(node, anim, pErrMsg); + MakePersistent(node, anim, info.GetAnimName(), pErrMsg); + } + } + + // let's see if the children have any segments specified... + int childCount = node->NumberOfChildren(); + for (int i = 0; i < childCount; i++) + ConvertNode((plMaxNode *)(node->GetChildNode(i)), pErrMsg); + + return true; +} + +// NewAnimation ------------------------------------------------------------------------- +// ------------- +plATCAnim * plAnimAvatarComponent::NewAnimation(const char *name, double begin, double end) +{ + return TRACKED_NEW plATCAnim(name, begin, end); +} + + +// +// CONVERT NODE SEGMENT BRANCH +// we're now in the middle of converting a segment +// every node gets an animation channel for the time period in question +// +// +hsBool plAnimAvatarComponent::ConvertNodeSegmentBranch(plMaxNode *node, plAGAnim *mod, plErrorMsg *pErrMsg) +{ + // Check for a suppression marker + plNotetrackAnim noteAnim(node, pErrMsg); + plAnimInfo info = noteAnim.GetAnimInfo(nil); + hsBool suppressed = info.IsSuppressed(mod->GetName()); + + // Get the affine parts and the TM Controller + plSceneObject *obj = node->GetSceneObject(); + if(obj && !suppressed) { + hsAffineParts parts; + hsControlConverter::Instance().ReduceKeys(node->GetTMController(), node->GetKeyReduceThreshold()); + plController* tmc = hsControlConverter::Instance().ConvertTMAnim(obj, node, &parts, mod->GetStart(), mod->GetEnd()); + + if (tmc) + { + plMatrixChannel *channel; + hsMatrix44 constSetting; + parts.ComposeMatrix(&constSetting); + + // If all our keys match, there's no point in keeping an animation controller + // around. Just nuke it and replace it with a constant channel. + if (tmc->PurgeRedundantSubcontrollers()) + { + channel = TRACKED_NEW plMatrixConstant(constSetting); + delete tmc; + tmc = nil; + } + else + { + channel = TRACKED_NEW plMatrixControllerChannel(tmc, &parts); + } + plMatrixChannelApplicator *app = TRACKED_NEW plMatrixChannelApplicator(); + app->SetChannelName(node->GetKey()->GetName()); + app->SetChannel(channel); + mod->AddApplicator(app); + } + + // let's see if the children have any segments specified... + int childCount = node->NumberOfChildren(); + for (int i = 0; i < childCount; i++) + ConvertNodeSegmentBranch((plMaxNode *)(node->GetChildNode(i)), mod, pErrMsg); + + return true; + } else { + return false; + } +} + +plKey FindSceneNode(plMaxNode *node) +{ + plSceneObject *obj = node->GetSceneObject(); + if(obj) + { + return obj->GetSceneNode(); + } else { + plMaxNode *parent = (plMaxNode *)node->GetParentNode(); + + if(parent) + { + return FindSceneNode(parent); + } else { + return nil; + } + } +} + +// +// MAKE PERSISTENT +// Perform wizardry necessary to make the object save itself. +// +// +hsBool plAnimAvatarComponent::MakePersistent(plMaxNode *node, plAGAnim *anim, const char *animName, plErrorMsg *pErrMsg) +{ + // new approach: add to the generic pool on the scene node + plLocation nodeLoc = node->GetLocation(); + plKey sceneNodeKey = FindSceneNode(node); + if(sceneNodeKey) + { + plKey animKey = hsgResMgr::ResMgr()->NewKey(animName, anim, nodeLoc); + + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(sceneNodeKey, plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kGeneric); + + hsgResMgr::ResMgr()->AddViaNotify(animKey, refMsg, plRefFlags::kActiveRef); + } + else + { + pErrMsg->Set(true, "Sorry", "Can't find node to save animation. Animation will not be saved."); + } + + return true; +} + + + + +// plEmoteComponent --------------------------------- +// ----------------- +class plEmoteComponent : public plAnimAvatarComponent +{ +public: + enum { + kBodyUsage, + kFadeIn, + kFadeOut + }; + + enum { + kBodyUnknown, + kBodyUpper, + kBodyFull + }; + + plEmoteComponent(); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual plATCAnim * NewAnimation(const char *name, double begin, double end); + +protected: + float fFadeIn; + float fFadeOut; + plEmoteAnim::BodyUsage fBodyUsage; +}; + + +// gEmoteDesc --------------------------------------------------------------------------------------------------------------------- +// ----------- +CLASS_DESC(plEmoteComponent, gEmoteDesc, "Emote Animation", "Emote Animation", COMP_TYPE_AVATAR, Class_ID(0x383c55ba, 0x6f1d454c)) + + +// gEmoteDesc ---------- +// ----------- +ParamBlockDesc2 gEmoteBk +( + plComponent::kBlkComp, _T("EmoteAnim"), 0, &gEmoteDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_EMOTE, IDS_COMP_EMOTE, 0, 0, NULL, + + plEmoteComponent::kBodyUsage, _T("Blend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_BODY_UNKNOWN, IDC_BODY_UPPER, IDC_BODY_FULL, + p_vals, plEmoteComponent::kBodyUnknown, plEmoteComponent::kBodyUpper, plEmoteComponent::kBodyFull, + p_default, plEmoteComponent::kBodyUnknown, + end, + + plEmoteComponent::kFadeIn, _T("Length"), TYPE_FLOAT, 0, 0, + p_default, 2.0, + p_range, 0.1, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_EMO_FADEIN, IDC_EMO_FADEIN_SPIN, 0.1, + end, + + plEmoteComponent::kFadeOut, _T("Length"), TYPE_FLOAT, 0, 0, + p_default, 2.0, + p_range, 0.1, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_EMO_FADEOUT, IDC_EMO_FADEOUT_SPIN, 0.1, + end, + + + end +); + + +// plEmoteComponent ---------------- +// ----------------- +plEmoteComponent::plEmoteComponent() +{ + fClassDesc = &gEmoteDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + +// Convert ------------------------------------------------------------ +// -------- +plEmoteComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + Interface *theInterface = node->GetInterface(); + RemoveBiped(node, theInterface); + + fFadeIn = fCompPB->GetFloat(kFadeIn); + fFadeOut = fCompPB->GetFloat(kFadeOut); + fBodyUsage = static_cast(fCompPB->GetInt(kBodyUsage)); + + ConvertNode(node, pErrMsg); + ((plSceneNode *)node->GetRoomKey()->GetObjectPtr())->SetFilterGenericsOnly(true); + return true; +} + + +// NewAnimation ---------------------------------------------------------------------- +// ------------- +plATCAnim * plEmoteComponent::NewAnimation(const char *name, double begin, double end) +{ + return TRACKED_NEW plEmoteAnim(name, begin, end, fFadeIn, fFadeOut, fBodyUsage); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorBaseComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorBaseComponent.cpp new file mode 100644 index 00000000..4a5cac88 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorBaseComponent.cpp @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plActivatorBaseComponent.h" + +#include "../pnKeyedObject/plKey.h" +#include "../MaxMain/plMaxNode.h" + +#include "../plModifier/plLogicModifier.h" +#include "../pnSceneObject/plSceneObject.h" +#include "hsResMgr.h" +#include "../pnMessage/plObjRefMsg.h" + +void plActivatorBaseComponent::AddReceiverKey(plKey pKey, plMaxNode* node) +{ + fReceivers.insert(ReceiverKey(node, pKey)); +} + +plKey plActivatorBaseComponent::GetLogicKey(plMaxNode* node) +{ + LogicKeys::const_iterator it = fLogicModKeys.find(node); + if (it != fLogicModKeys.end()) + return it->second; + + return nil; +} + +void plActivatorBaseComponent::IGetReceivers(plMaxNode* node, hsTArray& receivers) +{ + // Add the guys who want to be notified by all instances + ReceiverKeys::iterator lowIt = fReceivers.lower_bound(nil); + ReceiverKeys::iterator highIt = fReceivers.upper_bound(nil); + for (; lowIt != highIt; lowIt++) + receivers.Append(lowIt->second); + + // Add the ones for just this instance + lowIt = fReceivers.lower_bound(node); + highIt = fReceivers.upper_bound(node); + for (; lowIt != highIt; lowIt++) + receivers.Append(lowIt->second); +} + +// Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plActivatorBaseComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fLogicModKeys.clear(); + fReceivers.clear(); + return true; +} + +hsBool plActivatorBaseComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetForceLocal(true); + + plLocation loc = node->GetLocation(); + plSceneObject *obj = node->GetSceneObject(); + + // Create and register the VolumeGadget's logic component + plLogicModifier *logic = TRACKED_NEW plLogicModifier; + plKey logicKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), logic, node->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(logicKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + fLogicModKeys[node] = logicKey; + + return true; +} + +hsBool plActivatorBaseComponent::DeInit( plMaxNode *node, plErrorMsg *pErrMsg ) +{ + fReceivers.clear(); + fLogicModKeys.clear(); + return plPhysicCoreComponent::DeInit( node, pErrMsg ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorBaseComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorBaseComponent.h new file mode 100644 index 00000000..4b13e955 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorBaseComponent.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plActivatorBaseComponent_inc +#define plActivatorBaseComponent_inc + +#include "plComponent.h" +#include "plPhysicalComponents.h" +#include +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" + +class plMaxNode; + +#define ACTIVATOR_BASE_CID Class_ID(0x23915577, 0x2d0f4cdd) + +class plActivatorBaseComponent : public plPhysicCoreComponent +{ +public: + typedef std::map LogicKeys; +protected: + LogicKeys fLogicModKeys; + typedef std::multimap ReceiverKeys; + typedef std::pair ReceiverKey; + ReceiverKeys fReceivers; +// hsTArray fReceivers; + + void IGetReceivers(plMaxNode* node, hsTArray& receivers); + +public: + // Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg* pErrMsg); + virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + const LogicKeys& GetLogicKeys() { return fLogicModKeys; } + virtual plKey GetLogicKey(plMaxNode* node); + virtual void AddReceiverKey(plKey pKey, plMaxNode* node=nil); + virtual bool HasLogicOut() { return false; } + + int CanConvertToType(Class_ID obtype) + { return (obtype == ACTIVATOR_BASE_CID) ? 1 : plComponent::CanConvertToType(obtype); } +}; + +#endif // plActivatorBaseComponent_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorComponent.cpp new file mode 100644 index 00000000..22a5d2d1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorComponent.cpp @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plComponent.h" +#include "plComponentReg.h" + +void DummyCodeIncludeFuncActive() {} + +#define ACTIVATOR_CID Class_ID(0x205a7c50, 0x7a095602) + +class plActiveComponent : public plComponent +{ +public: + plActiveComponent(); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + +OBSOLETE_CLASS_DESC(plActiveComponent, gActiveDesc, "Activator", "Activator", COMP_TYPE_LOGIC, ACTIVATOR_CID) + +plActiveComponent::plActiveComponent() +{ + fClassDesc = &gActiveDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +ParamBlockDesc2 gActiveBlock +( + plComponent::kBlkComp, _T("activeComp"), 0, &gActiveDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + + end +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorComponent.h new file mode 100644 index 00000000..d67f1a99 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plActivatorComponent.h @@ -0,0 +1,25 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimCompProc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimCompProc.cpp new file mode 100644 index 00000000..4acd8e09 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimCompProc.cpp @@ -0,0 +1,425 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plAnimCompProc.h" + +#include "../MaxMain/plMaxNode.h" +#include "plComponentBase.h" + +#include "plPickNode.h" +#include "plAnimComponent.h" +#include "../../PubUtilLib/plInterp/plAnimEaseTypes.h" + +plAnimCompProc::plAnimCompProc() : + fCompButtonID(0), + fCompParamID(0), + fNodeButtonID(0), + fNodeParamID(0) +{ +} + +BOOL plAnimCompProc::DlgProc(TimeValue t, IParamMap2* pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2* pb = pm->GetParamBlock(); + + IUpdateNodeButton(hWnd, pb); + IUpdateCompButton(hWnd, pb); + + ILoadUser(hWnd, pb); + } + return TRUE; + + case WM_COMMAND: + { + int cmd = HIWORD(wParam); + int resID = LOWORD(wParam); + + if (cmd == BN_CLICKED && resID == fCompButtonID) + { + ICompButtonPress(hWnd, pm->GetParamBlock()); + return TRUE; + } + else if (cmd == BN_CLICKED && resID == fNodeButtonID) + { + INodeButtonPress(hWnd, pm->GetParamBlock()); + return TRUE; + } + else if (IUserCommand(hWnd, pm->GetParamBlock(), cmd, resID)) + return TRUE; + + } + break; + } + return FALSE; +} + +void plAnimCompProc::ICompButtonPress(HWND hWnd, IParamBlock2* pb) +{ + IPickComponent(pb); + + IUpdateCompButton(hWnd, pb); + IUpdateNodeButton(hWnd, pb); + + ILoadUser(hWnd, pb); +} + +void plAnimCompProc::IPickNode(IParamBlock2* pb, plComponentBase* comp) +{ + plPick::CompTargets(pb, fNodeParamID, comp); +} + +void plAnimCompProc::INodeButtonPress(HWND hWnd, IParamBlock2* pb) +{ + plComponentBase* comp = IGetComp(pb); + if (comp) + IPickNode(pb, comp); + + IUpdateNodeButton(hWnd, pb); + ILoadUser(hWnd, pb); +} + +void plAnimCompProc::IUpdateNodeButton(HWND hWnd, IParamBlock2* pb) +{ + HWND hButton = GetDlgItem(hWnd, fNodeButtonID); + + plComponentBase* comp = IGetComp(pb); + if (!comp) + { + SetWindowText(hButton, "(none)"); + EnableWindow(hButton, FALSE); + return; + } + + // If this is an anim grouped component you can't pick a target + if (comp->ClassID() == ANIM_GROUP_COMP_CID) + { + IClearNode(pb); + SetWindowText(hButton, "(none)"); + EnableWindow(hButton, FALSE); + return; + } + + EnableWindow(hButton, TRUE); + + // Make sure the node is actually in the components target list + plMaxNode* node = IGetNode(pb); + if (comp->IsTarget((plMaxNodeBase*)node)) + SetWindowText(hButton, node->GetName()); + else + SetWindowText(hButton, "(none)"); +} + +void plAnimCompProc::IUpdateCompButton(HWND hWnd, IParamBlock2* pb) +{ + HWND hAnim = GetDlgItem(hWnd, fCompButtonID); + + plComponentBase* comp = IGetComp(pb); + if (comp) + SetWindowText(hAnim, comp->GetINode()->GetName()); + else + SetWindowText(hAnim, "(none)"); +} + +plComponentBase* plAnimCompProc::IGetComp(IParamBlock2* pb) +{ + plMaxNode* node = nil; + if (pb->GetParameterType(fCompParamID) == TYPE_REFTARG) + node = (plMaxNode*)pb->GetReferenceTarget(fCompParamID); + else + node = (plMaxNode*)pb->GetINode(fCompParamID); + + if (node) + return node->ConvertToComponent(); + + return nil; +} + +plMaxNode* plAnimCompProc::IGetNode(IParamBlock2* pb) +{ + if (pb->GetParameterType(fNodeParamID) == TYPE_REFTARG) + return (plMaxNode*)pb->GetReferenceTarget(fNodeParamID); + else + return (plMaxNode*)pb->GetINode(fNodeParamID); +} + +void plAnimCompProc::IClearNode(IParamBlock2* pb) +{ + if (pb->GetParameterType(fNodeParamID) == TYPE_REFTARG) + pb->SetValue(fNodeParamID, 0, (ReferenceTarget*)nil); + else + pb->SetValue(fNodeParamID, 0, (INode*)nil); +} + +bool plAnimCompProc::GetCompAndNode(IParamBlock2* pb, plComponentBase*& comp, plMaxNode*& node) +{ + comp = IGetComp(pb); + if (comp) + { + node = IGetNode(pb); + + // If it's an anim group component (don't need a node), or we have a node + // and the component is attached to it, we're ok. + if (comp->ClassID() == ANIM_GROUP_COMP_CID || + (node && comp->IsTarget((plMaxNodeBase*)node))) + return true; + } + + return false; +} + + +/////////////////////////////////////////////////////////////////////////////////////////// + +plMtlAnimProc::plMtlAnimProc() : + fMtlButtonID(0), + fMtlParamID(0), + fNodeButtonID(0), + fNodeParamID(0), + fAnimComboID(0), + fAnimParamID(0) +{ +} + +BOOL plMtlAnimProc::DlgProc(TimeValue t, IParamMap2* pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2* pb = pm->GetParamBlock(); + + IOnInitDlg(hWnd, pb); + + IUpdateMtlButton(hWnd, pb); + } + return TRUE; + + case WM_COMMAND: + { + int cmd = HIWORD(wParam); + int resID = LOWORD(wParam); + + IParamBlock2* pb = pm->GetParamBlock(); + + if (cmd == BN_CLICKED && resID == fMtlButtonID) + { + IMtlButtonPress(hWnd, pb); + return TRUE; + } + else if (cmd == BN_CLICKED && resID == fNodeButtonID) + { + INodeButtonPress(hWnd, pb); + return TRUE; + } + else if (cmd == CBN_SELCHANGE && resID == fAnimComboID) + { + IAnimComboChanged(hWnd, pb); + return TRUE; + } + else if (IUserCommand(hWnd, pb, cmd, resID)) + return TRUE; + } + break; + } + + return FALSE; +} + +void plMtlAnimProc::IUpdateMtlButton(HWND hWnd, IParamBlock2* pb) +{ + HWND hMtl = GetDlgItem(hWnd, fMtlButtonID); + + // Get the saved material + Mtl *savedMtl = IGetMtl(pb); + + if (savedMtl) + SetWindowText(hMtl, savedMtl->GetName()); + else + SetWindowText(hMtl, "(none)"); + + // Enable the node button if a material is selected + EnableWindow(GetDlgItem(hWnd, fNodeButtonID), (savedMtl != nil)); + + // Update the dependencies of this + IUpdateNodeButton(hWnd, pb); +} + +void plMtlAnimProc::IUpdateNodeButton(HWND hWnd, IParamBlock2* pb) +{ + ISetNodeButtonText(hWnd, pb); + + // Update the dependencies of this + ILoadAnimCombo(hWnd, pb); +} + +#include "plNotetrackAnim.h" + +void plMtlAnimProc::ILoadAnimCombo(HWND hWnd, IParamBlock2* pb) +{ + HWND hAnim = GetDlgItem(hWnd, fAnimComboID); + + ComboBox_ResetContent(hAnim); + int sel = ComboBox_AddString(hAnim, ENTIRE_ANIMATION_NAME); + ComboBox_SetCurSel(hAnim, sel); + + const char* savedName = pb->GetStr(fAnimParamID); + if (!savedName) + savedName = ""; + + Mtl* mtl = IGetMtl(pb); + if (mtl) + { + plNotetrackAnim anim(mtl, nil); + while (const char* animName = anim.GetNextAnimName()) + { + int idx = ComboBox_AddString(hAnim, animName); + ComboBox_SetItemData(hAnim, idx, 1); + if (!strcmp(animName, savedName)) + ComboBox_SetCurSel(hAnim, idx); + } + + EnableWindow(hAnim, TRUE); + } + else + EnableWindow(hAnim, FALSE); + + // Update the dependencies of this + ILoadUser(hWnd, pb); +} + +#include "plPickMaterialMap.h" +#include "../MaxMain/plMtlCollector.h" + +void plMtlAnimProc::IMtlButtonPress(HWND hWnd, IParamBlock2* pb) +{ + // Let the user pick a new material + Mtl* pickedMtl = plPickMaterialMap::PickMaterial(plMtlCollector::kUsedOnly | + plMtlCollector::kPlasmaOnly); + + // Save the mtl in the pb and update the interface + if (pickedMtl != nil) + { + if (pb->GetParameterType(fMtlParamID) == TYPE_REFTARG) + pb->SetValue(fMtlParamID, 0, (ReferenceTarget*)pickedMtl); + else + pb->SetValue(fMtlParamID, 0, pickedMtl); + } + + + // Make sure the current node has the selected material on it (clear it otherwise) + INode* node = pb->GetINode(fNodeParamID); + if (!pickedMtl || !node || node->GetMtl() != pickedMtl) + pb->SetValue(fNodeParamID, 0, (INode*)nil); + + IUpdateMtlButton(hWnd, pb); +} + +void plMtlAnimProc::INodeButtonPress(HWND hWnd, IParamBlock2* pb) +{ + IPickNode(pb); + + IUpdateNodeButton(hWnd, pb); +} + +void plMtlAnimProc::IAnimComboChanged(HWND hWnd, IParamBlock2* pb) +{ + HWND hCombo = GetDlgItem(hWnd, fAnimComboID); + int idx = ComboBox_GetCurSel(hCombo); + + if (idx != CB_ERR) + { + if (ComboBox_GetItemData(hCombo, idx) == 0) + pb->SetValue(fAnimParamID, 0, ""); + else + { + // Get the name of the animation and save it + char buf[256]; + ComboBox_GetText(hCombo, buf, sizeof(buf)); + pb->SetValue(fAnimParamID, 0, buf); + } + } + + // Update the dependencies of this + ILoadUser(hWnd, pb); +} + +Mtl* plMtlAnimProc::IGetMtl(IParamBlock2* pb) +{ + if (pb->GetParameterType(fMtlParamID) == TYPE_REFTARG) + return (Mtl*)pb->GetReferenceTarget(fMtlParamID); + else + return pb->GetMtl(fMtlParamID); +} + + +#include "plPickNodeBase.h" + +static const char* kUserTypeAll = "(All)"; + +class plPickAllMtlNode : public plPickMtlNode +{ +protected: + void IAddUserType(HWND hList) + { + int idx = ListBox_AddString(hList, kUserTypeAll); + if (!fPB->GetINode(fNodeParamID)) + ListBox_SetCurSel(hList, idx); + } + + void ISetUserType(plMaxNode* node, const char* userType) + { + if (hsStrEQ(userType, kUserTypeAll)) + ISetNodeValue(nil); + } + +public: + plPickAllMtlNode(IParamBlock2* pb, int nodeParamID, Mtl* mtl) : + plPickMtlNode(pb, nodeParamID, mtl) + { + } +}; + +void plMtlAnimProc::IPickNode(IParamBlock2* pb) +{ + plPickAllMtlNode pick(pb, fNodeParamID, IGetMtl(pb)); + pick.DoPick(); +} + +void plMtlAnimProc::ISetNodeButtonText(HWND hWnd, IParamBlock2* pb) +{ + HWND hNode = GetDlgItem(hWnd, fNodeButtonID); + + INode* node = pb->GetINode(fNodeParamID); + if (node) + SetWindowText(hNode, node->GetName()); + else + SetWindowText(hNode, kUserTypeAll); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimCompProc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimCompProc.h new file mode 100644 index 00000000..212ce9c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimCompProc.h @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAnimCompProc_h_inc +#define plAnimCompProc_h_inc + +#include "max.h" +#include "iparamm2.h" + +class plComponentBase; +class plMaxNode; + +class plAnimCompProc : public ParamMap2UserDlgProc +{ +protected: + int fCompButtonID; + int fCompParamID; + int fNodeButtonID; + int fNodeParamID; + + virtual void IPickComponent(IParamBlock2* pb)=0; + virtual void IPickNode(IParamBlock2* pb, plComponentBase* comp); + + virtual void ILoadUser(HWND hWnd, IParamBlock2* pb)=0; + virtual bool IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID)=0; + + plMaxNode* IGetNode(IParamBlock2* pb); + void IClearNode(IParamBlock2* pb); + plComponentBase* IGetComp(IParamBlock2* pb); + + void ICompButtonPress(HWND hWnd, IParamBlock2* pb); + void INodeButtonPress(HWND hWnd, IParamBlock2* pb); + + void IUpdateCompButton(HWND hWnd, IParamBlock2* pb); + virtual void IUpdateNodeButton(HWND hWnd, IParamBlock2* pb); + +public: + plAnimCompProc(); + void DeleteThis() {} + + BOOL DlgProc(TimeValue t, IParamMap2* pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + + bool GetCompAndNode(IParamBlock2* pb, plComponentBase*& comp, plMaxNode*& node); +}; + + +/////////////////////////////////////////////////////////////////////////////////////////// + + +class plMtlAnimProc : public ParamMap2UserDlgProc +{ +protected: + int fMtlButtonID; + int fMtlParamID; + int fNodeButtonID; + int fNodeParamID; + int fAnimComboID; + int fAnimParamID; + + Mtl* IGetMtl(IParamBlock2* pb); + + virtual void IOnInitDlg(HWND hWnd, IParamBlock2* pb) {} + virtual void ILoadUser(HWND hWnd, IParamBlock2* pb)=0; + virtual bool IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID)=0; + + virtual void IPickNode(IParamBlock2* pb); + + virtual void ISetNodeButtonText(HWND hWnd, IParamBlock2* pb); + +public: + plMtlAnimProc(); + void DeleteThis() {} + + BOOL DlgProc(TimeValue t, IParamMap2* pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +private: + void IMtlButtonPress(HWND hWnd, IParamBlock2* pb); + void INodeButtonPress(HWND hWnd, IParamBlock2* pb); + void IAnimComboChanged(HWND hWnd, IParamBlock2* pb); + + void IUpdateMtlButton(HWND hWnd, IParamBlock2* pb); + void IUpdateNodeButton(HWND hWnd, IParamBlock2* pb); + void ILoadAnimCombo(HWND hWnd, IParamBlock2* pb); +}; + +#endif // plAnimCompProc_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimComponent.cpp new file mode 100644 index 00000000..083729ac --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimComponent.cpp @@ -0,0 +1,1241 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "resource.h" +#include "hsUtils.h" +#include "plAnimComponent.h" +#include "plComponentProcBase.h" +#include "plPhysicalComponents.h" +#include "plMiscComponents.h" +#include "../MaxMain/plPhysicalProps.h" + +#include "../pnSceneObject/plSceneObject.h" + +#include "../plInterp/plController.h" +#include "plNotetrackAnim.h" +#include "hsResMgr.h" +#include "../plAvatar/plAGModifier.h" +#include "../plAvatar/plAGChannel.h" +#include "../plAvatar/plAGAnim.h" +#include "../plAvatar/plAGMasterMod.h" +#include "../plAvatar/plMatrixChannel.h" +#include "../plAvatar/plPointChannel.h" +#include "../plAvatar/plScalarChannel.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../MaxPlasmaMtls/Materials/plPassMtlBase.h" + +#include "../pnKeyedObject/plUoid.h" +#include "plMaxAnimUtils.h" + +#include "../MaxPlasmaLights/plRealTimeLightBase.h" +#include "../pfAnimation/plLightModifier.h" +#include "../pnKeyedObject/plMsgForwarder.h" + +#include "../plSDL/plSDL.h" +#include "../plSDL/plSDLDescriptor.h" + +#include "plPickNodeBase.h" + + +// For material animations +#include "../MaxPlasmaMtls/Materials/plAnimStealthNode.h" + +// So that the linker won't throw this code away, since it doesn't appear to be used +void DummyCodeIncludeFunc() {} + +bool HasPhysicalComponent(plMaxNodeBase *node, bool searchChildren) +{ + int i; + for (i = 0; i < node->NumAttachedComponents(); i++) + { + if (node->GetAttachedComponent(i)->CanConvertToType(PHYSICS_BASE_CID)) + return true; + } + + if (searchChildren) + { + for (i = 0; i < node->NumberOfChildren(); i++) + if (HasPhysicalComponent((plMaxNodeBase *)node->GetChildNode(i), searchChildren)) + return true; + } + return false; +} + +bool HasPhysicalComponent(plComponentBase *comp) +{ + int i; + for (i = 0; i < comp->NumTargets(); i++) + { + plMaxNodeBase *node = comp->GetTarget(i); + if (node && HasPhysicalComponent(node, true)) + return true; + } + return false; +} + +bool plAnimComponentBase::GetAnimKey( plMaxNode *node, hsTArray &outKeys ) +{ + plComponentBase *comp = node->ConvertToComponent(); + if( comp != nil ) + { + if( IsAnimComponent( comp ) ) + { + plAnimComponentBase *base = (plAnimComponentBase *)comp; + // Grab this guy's key + } + } +// else if( ) + { + } + return true; +} + +plAnimObjInterface *plAnimComponentBase::GetAnimInterface( INode *inode ) +{ + if( inode == nil ) + return nil; + + plMaxNode *node = (plMaxNode *)inode; + plComponentBase *comp = node->ConvertToComponent(); + if( comp != nil ) + { + if( IsAnimComponent( comp ) ) + { + plAnimComponentBase *base = (plAnimComponentBase *)comp; + return (plAnimObjInterface *)base; + } + } + else + { + plAnimStealthNode *stealth = plAnimStealthNode::ConvertToStealth( node ); + if( stealth != nil ) + return (plAnimObjInterface *)stealth; + } + + return nil; +} + +//This enum is necessary and can only be appended. +//This is used in the ParamBlock2Desc. +enum +{ + kAnimRadio_DEAD, + kAnimAutoStart, // Start the Animation on load (V2) + kAnimLoop, // Start Looping at Begin Location + kAnimBegin_DEAD, + kAnimEnd_DEAD, + kAnimLoopSegCkBx_DEAD, + kAnimLoopSegBeg_DEAD, + kAnimLoopSegEnd_DEAD, + kAnimName, // Name of the notetrack animation to play + kAnimLoopSegBegBox_DEAD, + kAnimLoopSegEndBox_DEAD, + kAnimUseGlobal, + kAnimGlobalName, + kAnimLoopName, // Name of the notetrack specified loop + kAnimEaseInType, + kAnimEaseOutType, + kAnimEaseInLength, + kAnimEaseOutLength, + kAnimEaseInMin, + kAnimEaseInMax, + kAnimEaseOutMin, + kAnimEaseOutMax, + kAnimPhysAnim, +}; + +void plAnimComponentProc::EnableGlobal(HWND hWnd, hsBool enable) +{ + ComboBox_Enable(GetDlgItem(hWnd, IDC_ANIM_GLOBAL_LIST), enable); + ComboBox_Enable(GetDlgItem(hWnd, IDC_ANIM_NAMES), !enable); + ComboBox_Enable(GetDlgItem(hWnd, IDC_LOOP_NAMES), !enable); + Button_Enable(GetDlgItem(hWnd, IDC_COMP_ANIM_AUTOSTART_CKBX), !enable); + Button_Enable(GetDlgItem(hWnd, IDC_COMP_ANIM_LOOP_CKBX), !enable); +} + +void plAnimComponentProc::FillAgeGlobalComboBox(HWND box, char *varName) +{ + plStateDescriptor *sd = plSDLMgr::GetInstance()->FindDescriptor(plPageInfoComponent::GetCurrExportAgeName(), plSDL::kLatestVersion); + if (sd) + { + int i; + for (i = 0; i < sd->GetNumVars(); i++) + { + plVarDescriptor *var = sd->GetVar(i); + if (var->GetType() == plVarDescriptor::kFloat || + var->GetType() == plVarDescriptor::kDouble || + var->GetType() == plVarDescriptor::kTime || + var->GetType() == plVarDescriptor::kAgeTimeOfDay) + { + ComboBox_AddString(box, var->GetName()); + } + } + } + ComboBox_AddString(box, "(none)"); +} + +void plAnimComponentProc::SetBoxToAgeGlobal(HWND box, char *varName) +{ + char buff[512]; + if (!varName || !strcmp(varName, "")) + varName = "(none)"; + + ComboBox_SelectString(box, 0, varName); + ComboBox_GetLBText(box, ComboBox_GetCurSel(box), buff); + if (strcmp(varName, buff)) + { + // Didn't find our variable in the age SDL file... + // Probably just missing the sdl file, + // so we'll force it in there. It'll export fine. + ComboBox_AddString(box, varName); + ComboBox_SelectString(box, 0, varName); + } +} + +BOOL plAnimComponentProc::DlgProc(TimeValue t, IParamMap2 *pMap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWND gWnd = GetDlgItem(hWnd, IDC_ANIM_GLOBAL_LIST); + char buff[512]; + switch (msg) + { + case WM_INITDIALOG: + { + fPB = pMap->GetParamBlock(); + + fNoteTrackDlg.Init(GetDlgItem(hWnd, IDC_ANIM_NAMES), + GetDlgItem(hWnd, IDC_LOOP_NAMES), + kAnimName, + kAnimLoopName, + fPB, + fPB->GetOwner()); + fNoteTrackDlg.Load(); + + EnableWindow(GetDlgItem(hWnd, IDC_LOOP_NAMES), fPB->GetInt(kAnimLoop)); + + FillAgeGlobalComboBox(gWnd, fPB->GetStr(ParamID(kAnimGlobalName))); + SetBoxToAgeGlobal(gWnd, fPB->GetStr(ParamID(kAnimGlobalName))); + EnableGlobal(hWnd, fPB->GetInt(ParamID(kAnimUseGlobal))); + Button_Enable(GetDlgItem(hWnd, IDC_COMP_ANIM_PHYSANIM), + HasPhysicalComponent((plComponentBase*)fPB->GetOwner())); + } + return TRUE; + case WM_COMMAND: + if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_ANIM_NAMES) + { + fNoteTrackDlg.AnimChanged(); + return TRUE; + } + else if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_LOOP_NAMES) + { + // Get the new loop name + fNoteTrackDlg.LoopChanged(); + return TRUE; + } + else if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_ANIM_GLOBAL_LIST) + { + ComboBox_GetLBText(gWnd, ComboBox_GetCurSel(gWnd), buff); + fPB->SetValue(ParamID(kAnimGlobalName), 0, _T(buff)); + } + // Catch loop button updates + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_COMP_ANIM_LOOP_CKBX) + EnableWindow(GetDlgItem(hWnd, IDC_LOOP_NAMES), fPB->GetInt(kAnimLoop)); + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_COMP_ANIM_USE_GLOBAL) + { + EnableGlobal(hWnd, fPB->GetInt(ParamID(kAnimUseGlobal))); + } + break; + } + return false; +} + +void plAnimComponentProc::Update( TimeValue t, Interval &valid, IParamMap2 *pmap ) +{ + HWND hWnd = pmap->GetHWnd(); + IParamBlock2 *pb = pmap->GetParamBlock(); + SetBoxToAgeGlobal(GetDlgItem(hWnd, IDC_ANIM_GLOBAL_LIST), pb->GetStr(ParamID(kAnimGlobalName))); +} + +void plAnimComponentProc::DeleteThis() +{ + fNoteTrackDlg.DeleteCache(); +} + +// For the paramblock below. +static plAnimComponentProc gAnimCompProc; + +#define WM_ROLLOUT_OPEN WM_USER+1 + +class plAnimEaseComponentProc : public ParamMap2UserDlgProc +{ +protected: + void EnableStopPoints(IParamMap2 *pm, bool enable) + { + pm->Enable(kAnimEaseInMin, enable); + pm->Enable(kAnimEaseInMax, enable); + pm->Enable(kAnimEaseOutMin, enable); + pm->Enable(kAnimEaseOutMax, enable); + } + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + + // Enable the min and max controls (that are only valid with stop points) + // if at least one of the targets has a stop point + plAnimComponent *comp = (plAnimComponent*)pb->GetOwner(); + int num = comp->NumTargets(); + bool stopPoints = false; + for (int i = 0; i < num; i++) + { + if (DoesHaveStopPoints(comp->GetTarget(i))) + { + stopPoints = true; + break; + } + } + EnableStopPoints(map, stopPoints); + + // If we're doing an ease, set the ease rollup to open + if (pb->GetInt(kAnimEaseInType) != plAnimEaseTypes::kNoEase || + pb->GetInt(kAnimEaseOutType) != plAnimEaseTypes::kNoEase) + PostMessage(hWnd, WM_ROLLOUT_OPEN, 0, 0); + } + return TRUE; + + // Max doesn't know about the rollup until after WM_CREATE, so we get + // around it by posting a message + case WM_ROLLOUT_OPEN: + { + IRollupWindow *rollup = GetCOREInterface()->GetCommandPanelRollup(); + int idx = rollup->GetPanelIndex(hWnd); + rollup->SetPanelOpen(idx, TRUE); + } + return TRUE; + } + return FALSE; + } + + void DeleteThis() {} +}; +// For the paramblock below. +static plAnimEaseComponentProc gAnimEaseCompProc; + +/* +// Make sure min is less than normal, which is less than max +class EaseAccessor : public PBAccessor +{ +protected: + bool fDoingUpdate; + + void AdjustMin(IParamBlock2 *pb, ParamID minID, ParamID normalID, ParamID maxID, float value) + { + if (value > pb->GetFloat(normalID)) + { + pb->SetValue(normalID, 0, value); + if (value > pb->GetFloat(maxID)) + pb->SetValue(maxID, 0, value); + } + } + void AdjustNormal(IParamBlock2 *pb, ParamID minID, ParamID normalID, ParamID maxID, float value) + { + if (value < pb->GetFloat(minID)) + pb->SetValue(minID, 0, value); + if (value > pb->GetFloat(maxID)) + pb->SetValue(maxID, 0, value); + } + void AdjustMax(IParamBlock2 *pb, ParamID minID, ParamID normalID, ParamID maxID, float value) + { + if (value < pb->GetFloat(normalID)) + { + pb->SetValue(normalID, 0, value); + if (value < pb->GetFloat(minID)) + pb->SetValue(minID, 0, value); + } + } + +public: + EaseAccessor() : fDoingUpdate(false) {} + + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (fDoingUpdate) + return; + fDoingUpdate = true; + + plAnimComponent *comp = (plAnimComponent*)owner; + IParamBlock2 *pb = comp->GetParamBlockByID(plComponentBase::kBlkComp); + + if (id == kAnimEaseInMin) + AdjustMin(pb, kAnimEaseInMin, kAnimEaseInLength, kAnimEaseInMin, v.f); + else if (id == kAnimEaseInLength) + AdjustNormal(pb, kAnimEaseInMin, kAnimEaseInLength, kAnimEaseInMax, v.f); + else if (id == kAnimEaseInMax) + AdjustMax(pb, kAnimEaseInMin, kAnimEaseInLength, kAnimEaseInMax, v.f); + else if (id == kAnimEaseOutMin) + AdjustMin(pb, kAnimEaseOutMin, kAnimEaseOutLength, kAnimEaseOutMax, v.f); + else if (id == kAnimEaseOutLength) + AdjustNormal(pb, kAnimEaseOutMin, kAnimEaseOutLength, kAnimEaseOutMax, v.f); + else if (id == kAnimEaseOutMax) + AdjustMax(pb, kAnimEaseOutMin, kAnimEaseOutLength, kAnimEaseOutMax, v.f); + + fDoingUpdate = false; + } +}; +*/ +static plEaseAccessor gAnimCompEaseAccessor(plComponentBase::kBlkComp, + kAnimEaseInMin, kAnimEaseInMax, kAnimEaseInLength, + kAnimEaseOutMin, kAnimEaseOutMax, kAnimEaseOutLength); + +CLASS_DESC(plAnimComponent, gAnimDesc, "Animation", "Animation", COMP_TYPE_MISC, ANIM_COMP_CID) +CLASS_DESC(plAnimGroupedComponent, gAnimGroupedDesc, "Animation Grouped", "AnimGrouped", COMP_TYPE_MISC, ANIM_GROUP_COMP_CID) + +plAnimComponentBase::IsAnimComponent(plComponentBase *comp) +{ + return (comp->ClassID() == ANIM_COMP_CID || + comp->ClassID() == ANIM_GROUP_COMP_CID); +} + +enum { kAnimMain, kAnimEase }; + +ParamBlockDesc2 gAnimBlock +( + plComponent::kBlkComp, _T("animation"), 0, &gAnimDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + // map rollups + 2, + kAnimMain, IDD_COMP_ANIM, IDS_COMP_ANIM, 0, 0, &gAnimCompProc, + kAnimEase, IDD_COMP_ANIM_EASE, IDS_COMP_ANIM_EASE, 0, APPENDROLL_CLOSED, &gAnimEaseCompProc, + + // Anim Main rollout + kAnimAutoStart, _T("autoStart"), TYPE_BOOL, 0, 0, + p_ui, kAnimMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_AUTOSTART_CKBX, + p_default, FALSE, + end, + kAnimLoop, _T("loop"), TYPE_BOOL, 0, 0, + p_ui, kAnimMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_LOOP_CKBX, + p_default, FALSE, + end, + kAnimName, _T("animName"), TYPE_STRING, 0, 0, + end, + kAnimUseGlobal, _T("UseGlobal"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, kAnimMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_USE_GLOBAL, + end, + kAnimGlobalName, _T("GlobalName"), TYPE_STRING, 0, 0, + p_default, _T(""), + end, + kAnimLoopName, _T("loopName"), TYPE_STRING, 0, 0, + end, + kAnimPhysAnim, _T("PhysAnim"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, kAnimMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_PHYSANIM, + end, + + // Anim Ease rollout + kAnimEaseInType, _T("easeInType"), TYPE_INT, 0, 0, + p_ui, kAnimEase, TYPE_RADIO, 3, IDC_COMP_ANIM_EASE_IN_NONE, IDC_COMP_ANIM_EASE_IN_CONST_ACCEL, IDC_COMP_ANIM_EASE_IN_SPLINE, + p_vals, plAnimEaseTypes::kNoEase, plAnimEaseTypes::kConstAccel, plAnimEaseTypes::kSpline, + p_default, plAnimEaseTypes::kNoEase, + end, + kAnimEaseInLength, _T("easeInLength"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_IN_TIME, IDC_COMP_ANIM_EASE_IN_TIME_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + kAnimEaseInMin, _T("easeInMin"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_IN_MIN, IDC_COMP_ANIM_EASE_IN_MIN_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + kAnimEaseInMax, _T("easeInMax"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_IN_MAX, IDC_COMP_ANIM_EASE_IN_MAX_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + + kAnimEaseOutType, _T("easeOutType"), TYPE_INT, 0, 0, + p_ui, kAnimEase, TYPE_RADIO, 3, IDC_COMP_ANIM_EASE_OUT_NONE, IDC_COMP_ANIM_EASE_OUT_CONST_ACCEL, IDC_COMP_ANIM_EASE_OUT_SPLINE, + p_vals, plAnimEaseTypes::kNoEase, plAnimEaseTypes::kConstAccel, plAnimEaseTypes::kSpline, + p_default, plAnimEaseTypes::kNoEase, + end, + kAnimEaseOutLength, _T("easeOutLength"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_OUT_TIME, IDC_COMP_ANIM_EASE_OUT_TIME_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + kAnimEaseOutMin, _T("easeOutMin"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_OUT_MIN, IDC_COMP_ANIM_EASE_OUT_MIN_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + kAnimEaseOutMax, _T("easeOutMax"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, kAnimEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_OUT_MAX, IDC_COMP_ANIM_EASE_OUT_MAX_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + + end +); + +ParamBlockDesc2 gAnimGroupedBlock +( + plComponent::kBlkComp, _T("animGrouped"), 0, &gAnimGroupedDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + // map rollups + 2, + kAnimMain, IDD_COMP_ANIM, IDS_COMP_ANIM_GROUPED, 0, 0, &gAnimCompProc, + kAnimEase, IDD_COMP_ANIM_EASE, IDS_COMP_ANIM_EASE, 0, APPENDROLL_CLOSED, &gAnimEaseCompProc, + + // use params from existing descriptor + &gAnimBlock, + + end +); + +plAnimComponent::plAnimComponent() +{ + fClassDesc = &gAnimDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plKey plAnimComponent::GetModKey(plMaxNode *node) +{ + if (fMods.find(node) != fMods.end()) + return fMods[node]->GetKey(); + + return nil; +} + +hsBool plAnimComponent::GetKeyList( INode *restrictedNode, hsTArray &outKeys ) +{ + if( restrictedNode != nil ) + { + if( fMods.find( (plMaxNode *)restrictedNode ) != fMods.end() ) + { + outKeys.Append( fMods[ (plMaxNode *)restrictedNode ]->GetKey() ); + return true; + } + return false; + } + else + { + hsAssert( false, "DO SOMETHING!" ); + return false; + } +} + +plAnimGroupedComponent::plAnimGroupedComponent() : fForward(nil) +{ + fClassDesc = &gAnimGroupedDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plKey plAnimGroupedComponent::GetModKey(plMaxNode *node) +{ + if( fForward ) + return fForward->GetKey(); + return nil; +} + +hsBool plAnimGroupedComponent::GetKeyList( INode *restrictedNode, hsTArray &outKeys ) +{ + if( fForward ) + { + outKeys.Append( fForward->GetKey() ); + return true; + } + return false; +} + + +#include "../pnMessage/plNodeRefMsg.h" + +hsBool plAnimGroupedComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + bool needSetMaster = fNeedReset; + if (fNeedReset) + { + fForward = TRACKED_NEW plMsgForwarder; + plKey forwardKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), fForward, node->GetLocation()); + + plNodeRefMsg *refMsg = TRACKED_NEW plNodeRefMsg(node->GetRoomKey(), plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric); + hsgResMgr::ResMgr()->AddViaNotify(forwardKey, refMsg, plRefFlags::kActiveRef); + } + + hsBool ret = plAnimComponentBase::PreConvert(node, pErrMsg); + + plAGMasterMod *mod = fMods[node]; + + if (needSetMaster) + mod->SetIsGroupMaster(true, fForward); + mod->SetIsGrouped(true); + + fForward->AddForwardKey(mod->GetKey()); + + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////////// + +plAnimComponentBase::plAnimComponentBase() : fNeedReset(true) +{ +} + + +const char *plAnimComponentBase::GetAnimName() +{ + const char *name = fCompPB->GetStr(kAnimName); + if (!name || name[0] == '\0') + return nil; + return name; +} + +bool IsSubworld(plMaxNode* node) +{ + UInt32 numComps = node->NumAttachedComponents(); + for (int i = 0; i < numComps; i++) + { + plComponentBase* comp = node->GetAttachedComponent(i); + if (comp && comp->ClassID() == PHYS_SUBWORLD_CID) + return true; + } + + return false; +} + +void SetPhysAnimRecurse(plMaxNode *node, plErrorMsg *pErrMsg) +{ + // If we hit a subworld, stop. The subworld may be animated, but the + // physicals in it aren't. + if (IsSubworld(node)) + return; + + if (HasPhysicalComponent(node, false)) + { char* debugName = node->GetName(); + node->GetPhysicalProps()->SetPhysAnim(true, node, pErrMsg); + } + int i; + for (i = 0; i < node->NumberOfChildren(); i++) + SetPhysAnimRecurse((plMaxNode *)node->GetChildNode(i), pErrMsg); +} + +hsBool plAnimComponentBase::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (node->IsTMAnimated()) + { + node->SetMovable(true); + node->SetForceLocal(true); + + // + // forceLocal on our parent (since keys work in local space) + // + plMaxNode *parent = (plMaxNode *)node->GetParentNode(); + if (!parent->IsRootNode()) + { + parent->SetForceLocal(true); + + //char str[512]; + //sprintf(str, "Forcing local on '%s' because of animated child '%s'\n",parent->GetName(),node->GetName() ); + //OutputDebugString(str); + } + } + + if (fCompPB->GetInt(ParamID(kAnimPhysAnim))) + SetPhysAnimRecurse(node, pErrMsg); + + /* + int childCount = node->NumberOfChildren(); + for (int i = 0; i < childCount; i++) + { + SetupProperties((plMaxNode *)node->GetChildNode(i), pErrMsg); + } + */ + return true; +} + +hsBool plAnimComponentBase::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + // If this is the first time in the preconvert, reset the map + if (fNeedReset) + { + fNeedReset = false; + } + + // If this node is animated, create it's modifier and key now so we can give + // it out to anyone that needs it +// if (node->IsTMAnimated() || node->IsAnimatedLight()) +// { + const char *name = node->GetName(); + + plAGMasterMod *mod = node->GetAGMasterMod(); + if (mod == nil) + { + if (!node->HasAGMod()) // Need to add this before the MasterMod, if it doesn't have one already. + { + node->AddModifier(new plAGModifier(node->GetName()), IGetUniqueName(node)); + } + mod = TRACKED_NEW plAGMasterMod(); + + plKey modKey = node->AddModifier(mod, IGetUniqueName(node)); + } + fMods[node] = mod; +// } + + + // Small change here. We're setting up the timing specs on the + // plAGAnim object during preconvert, so that the info is available + // when actually converting the anim (and for other components + // that need it, but may or may not actually convert before us.) + + // Note: if the component uses the "(Entire Animation)" segment for + // the main start/end, the start/end times won't be valid until + // we've added all keys during convert. Some cleanup might + // be necessary in this case. + + char *animName = fCompPB->GetStr(kAnimName); + if (animName == nil || !strcmp(animName, "")) + animName = ENTIRE_ANIMATION_NAME; + + if (fCompPB->GetInt(ParamID(kAnimUseGlobal))) + { + plAgeGlobalAnim *ageAnim = TRACKED_NEW plAgeGlobalAnim(animName, 0, 0); + ageAnim->SetGlobalVarName(fCompPB->GetStr(ParamID(kAnimGlobalName))); + + fAnims[node] = ageAnim; + } + else + { + plATCAnim *ATCAnim = TRACKED_NEW plATCAnim(animName, 0, 0); + plNotetrackAnim noteAnim(node, pErrMsg); + plAnimInfo info = noteAnim.GetAnimInfo(animName); + ATCAnim->SetAutoStart(fCompPB->GetInt(kAnimAutoStart)); + + hsScalar start = info.GetAnimStart(); + hsScalar end = info.GetAnimEnd(); + hsScalar initial = info.GetAnimInitial(); + if (start != -1) + ATCAnim->SetStart(start); + if (end != -1) + ATCAnim->SetEnd(end); + if (initial != -1) + ATCAnim->SetInitial(initial); + + if (fCompPB->GetInt(kAnimLoop)) + { + ATCAnim->SetLoop(true); + char *loopName = fCompPB->GetStr(kAnimLoopName); + hsScalar loopStart = info.GetLoopStart(loopName); + hsScalar loopEnd = info.GetLoopEnd(loopName); + + ATCAnim->SetLoopStart(loopStart == -1 ? ATCAnim->GetStart() : loopStart); + ATCAnim->SetLoopEnd(loopEnd == -1 ? ATCAnim->GetEnd() : loopEnd); + } + + while (const char *loop = info.GetNextLoopName()) + ATCAnim->AddLoop(loop, info.GetLoopStart(loop), info.GetLoopEnd(loop)); + + while (const char *marker = info.GetNextMarkerName()) + ATCAnim->AddMarker(marker, info.GetMarkerTime(marker)); + + float stopPoint = -1; + while ((stopPoint = info.GetNextStopPoint()) != -1) + ATCAnim->AddStopPoint(stopPoint); + + ATCAnim->SetEaseInType(fCompPB->GetInt(kAnimEaseInType)); + ATCAnim->SetEaseOutType(fCompPB->GetInt(kAnimEaseOutType)); + ATCAnim->SetEaseInLength(fCompPB->GetFloat(kAnimEaseInLength)); + ATCAnim->SetEaseInMin(fCompPB->GetFloat(kAnimEaseInMin)); + ATCAnim->SetEaseInMax(fCompPB->GetFloat(kAnimEaseInMax)); + ATCAnim->SetEaseOutLength(fCompPB->GetFloat(kAnimEaseOutLength)); + ATCAnim->SetEaseOutMin(fCompPB->GetFloat(kAnimEaseOutMin)); + ATCAnim->SetEaseOutMax(fCompPB->GetFloat(kAnimEaseOutMax)); + + fAnims[node] = ATCAnim; + } + return true; +} + + +hsBool plAnimComponentBase::IAddTMToAnim(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg) +{ + hsBool result = false; + + // Get the affine parts and the TM Controller + plSceneObject *obj = node->GetSceneObject(); + hsAffineParts * parts = TRACKED_NEW hsAffineParts; + plController* tmc; + + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + tmc = hsControlConverter::Instance().ConvertTMAnim(obj, node, parts); + else + tmc = hsControlConverter::Instance().ConvertTMAnim(obj, node, parts, anim->GetStart(), anim->GetEnd()); + + if (tmc) + { + plMatrixChannelApplicator *app = TRACKED_NEW plMatrixChannelApplicator(); + app->SetChannelName(node->GetName()); + plMatrixControllerChannel *channel = TRACKED_NEW plMatrixControllerChannel(tmc, parts); + app->SetChannel(channel); + anim->AddApplicator(app); + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + anim->ExtendToLength(tmc->GetLength()); + result = true; + } + + delete parts; // We copy this over, so no need to keep it around + return result; +} + +hsBool plAnimComponentBase::IAddLightToAnim(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg) +{ + if (!node->IsAnimatedLight()) + return false; + + Object *obj = node->GetObjectRef(); + Class_ID cid = obj->ClassID(); + + IParamBlock2 *pb = nil; + if (cid == RTSPOT_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTLightBase::kBlkSpotLight); + else if (cid == RTOMNI_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTLightBase::kBlkOmniLight); + else if (cid == RTDIR_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTLightBase::kBlkTSpotLight); + else if (cid == RTPDIR_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTLightBase::kBlkMain); + + node->GetRTLightColAnim(pb, anim); + + if (cid == RTSPOT_LIGHT_CLASSID || cid == RTOMNI_LIGHT_CLASSID) + node->GetRTLightAttenAnim(pb, anim); + + if (cid == RTSPOT_LIGHT_CLASSID) + node->GetRTConeAnim(pb, anim); + + return true; +} + +hsBool plAnimComponentBase::IConvertNodeSegmentBranch(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg) +{ + hsBool madeAnim = false; + int i; + + if (IAddTMToAnim(node, anim, pErrMsg)) + madeAnim = true; + if (IAddLightToAnim(node, anim, pErrMsg)) + madeAnim = true; + + for (i = 0; i < node->NumAttachedComponents(); i++) + { + if (node->GetAttachedComponent(i)->AddToAnim(anim, node)) + madeAnim = true; + } + + if (madeAnim) + { + // It has an animation, we're going to need a plAGMod when loading the anim + if (!node->HasAGMod()) + { + node->AddModifier(new plAGModifier(node->GetName()), IGetUniqueName(node)); + } + madeAnim = true; + } +/* + // let's see if the children have any segments specified... + int childCount = node->NumberOfChildren(); + for (int i = 0; i < childCount; i++) + { + if (IConvertNodeSegmentBranch((plMaxNode *)(node->GetChildNode(i)), anim, pErrMsg)) + madeAnim = true; + } +*/ + return madeAnim; +} + +hsBool plAnimComponentBase::IMakePersistent(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg) +{ + // anims made by this component are private to the specific AGMasterMod, so we attach them there. + plAGMasterMod *mod = plAGMasterMod::ConvertNoRef(fMods[node]); + hsAssert(mod != nil, "No MasterMod to make animation persistent!"); + + char buffer[256]; + sprintf(buffer, "%s_%s_anim_%d", node->GetName(), anim->GetName(), mod->GetNumPrivateAnimations()); + plLocation nodeLoc = node->GetLocation(); + plKey animKey = hsgResMgr::ResMgr()->NewKey(buffer, anim, nodeLoc); + + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(mod->GetKey(), plRefMsg::kOnCreate, 0, 0); + hsgResMgr::ResMgr()->AddViaNotify(animKey, refMsg, plRefFlags::kActiveRef); + + return true; +} + + +hsBool plAnimComponentBase::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fNeedReset = true; + + if (!IConvertNodeSegmentBranch(node, fAnims[node], pErrMsg)) + { + // Either we delete it here, or we make it persistent below and the resMgr handles it + delete fAnims[node]; + fAnims[node] = nil; + return false; + } + + if (fCompPB->GetInt(ParamID(kAnimUseGlobal))) + { + ((plAgeGlobalAnim *)fAnims[node])->SetGlobalVarName(fCompPB->GetStr(ParamID(kAnimGlobalName))); + } + else // It's an ATCAnim + { + // If we're on an "(Entire Animation)" segment. The loops won't know their lengths until + // after the nodes have been converted and added. So we adjust them here if necessary. + ((plATCAnim *)fAnims[node])->CheckLoop(); + } + + IMakePersistent(node, fAnims[node], pErrMsg); + return true; +} + +hsBool plAnimComponentBase::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fMods.clear(); + fLightMods.clear(); + fAnims.clear(); + return true; +} + +void plAnimComponentBase::SetupCtl( plAGAnim *anim, plController *ctl, plAGApplicator *app, plMaxNode *node ) +{ + plScalarControllerChannel *channel = TRACKED_NEW plScalarControllerChannel(ctl); + app->SetChannel(channel); + anim->AddApplicator(app); + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + anim->ExtendToLength(ctl->GetLength()); +} + +//// Picker Dialog for Restricted Animation Components ////////////////////////////////////////// + +class plPickAnimCompNode : public plPickCompNode +{ +protected: + ParamID fTypeID; + + void IAddUserType(HWND hList) + { + int type = fPB->GetInt(fTypeID); + + int idx = ListBox_AddString(hList, kUseParamBlockNodeString); + if (type == plAnimObjInterface::kUseParamBlockNode && !fPB->GetINode(fNodeParamID)) + ListBox_SetCurSel(hList, idx); + + + idx = ListBox_AddString(hList, kUseOwnerNodeString); + if (type == plAnimObjInterface::kUseOwnerNode) + ListBox_SetCurSel(hList, idx); + } + + void ISetUserType(plMaxNode* node, const char* userType) + { + if (hsStrEQ(userType, kUseParamBlockNodeString)) + { + ISetNodeValue(nil); + fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseParamBlockNode); + } + else if (hsStrEQ(userType, kUseOwnerNodeString)) + { + ISetNodeValue(nil); + fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseOwnerNode); + } + else + fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseParamBlockNode); + } + +public: + plPickAnimCompNode(IParamBlock2* pb, ParamID nodeParamID, ParamID typeID, plComponentBase *comp) : + plPickCompNode(pb, nodeParamID, comp), fTypeID(typeID) + { + } +}; + +void plAnimComponentBase::PickTargetNode( IParamBlock2 *destPB, ParamID destParamID, ParamID destTypeID ) +{ + plPickAnimCompNode pick( destPB, destParamID, destTypeID, (plComponentBase *)this ); + pick.DoPick(); +} + +const char *plAnimComponentBase::GetIfaceSegmentName( hsBool allowNil ) +{ + const char *name = GetAnimName(); + if( allowNil || name != nil ) + return name; + return ENTIRE_ANIMATION_NAME; +} + +//// Hit Callback for Animations ///////////////////////////////////////////// + +class plPlasmaAnimHitCallback : public HitByNameDlgCallback +{ +protected: + IParamBlock2* fPB; + ParamID fParamID; + TCHAR fTitle[ 128 ]; + +public: + plPlasmaAnimHitCallback( IParamBlock2 *pb, ParamID paramID, TCHAR *title = nil ) + : fPB( pb ), fParamID( paramID ) + + { + strcpy( fTitle, title ); + } + + virtual TCHAR *dialogTitle() { return fTitle; } + virtual TCHAR *buttonText() { return "OK"; } + + virtual int filter( INode *node ) + { + plComponentBase *comp = ( (plMaxNodeBase *)node )->ConvertToComponent(); + if( comp != nil && plAnimComponentBase::IsAnimComponent( comp ) ) + { + // Make sure it won't create a cyclical reference (Max doesn't like those) + if( comp->TestForLoop( FOREVER, fPB ) == REF_FAIL ) + return FALSE; + + return TRUE; + } + else + { + plAnimStealthNode *stealth = plAnimStealthNode::ConvertToStealth( node ); + if( stealth != nil ) + { + if( stealth->TestForLoop( FOREVER, fPB ) == REF_FAIL ) + return FALSE; + + if( !stealth->IsParentUsedInScene() ) + return FALSE; + + return TRUE; + } + } + + return FALSE; + } + + virtual void proc( INodeTab &nodeTab ) + { + fPB->SetValue( fParamID, (TimeValue)0, nodeTab[ 0 ] ); + } + + virtual BOOL showHiddenAndFrozen() { return TRUE; } + virtual BOOL singleSelect() { return TRUE; } +}; + +//// Dialog Proc For Anim Selection ///////////////////////////////////////////////////////////// + +plPlasmaAnimSelectDlgProc::plPlasmaAnimSelectDlgProc( ParamID paramID, int dlgItem, TCHAR *promptTitle, ParamMap2UserDlgProc *chainedDlgProc ) +{ + fParamID = paramID; + fDlgItem = dlgItem; + fUseNode = false; + strcpy( fTitle, promptTitle ); + fChain = chainedDlgProc; +} + +plPlasmaAnimSelectDlgProc::plPlasmaAnimSelectDlgProc( ParamID paramID, int dlgItem, ParamID nodeParamID, ParamID typeParamID, int nodeDlgItem, + TCHAR *promptTitle, ParamMap2UserDlgProc *chainedDlgProc ) +{ + fParamID = paramID; + fDlgItem = dlgItem; + fUseNode = true; + fNodeParamID = nodeParamID; + fTypeParamID = typeParamID; + fNodeDlgItem = nodeDlgItem; + strcpy( fTitle, promptTitle ); + fChain = chainedDlgProc; +} + +void plPlasmaAnimSelectDlgProc::SetThing( ReferenceTarget *m ) +{ + if( fChain != nil ) + fChain->SetThing( m ); +} + +void plPlasmaAnimSelectDlgProc::Update( TimeValue t, Interval &valid, IParamMap2 *pmap ) +{ + if( fChain != nil ) + fChain->Update( t, valid, pmap ); +} + +void plPlasmaAnimSelectDlgProc::IUpdateNodeBtn( HWND hWnd, IParamBlock2 *pb ) +{ + if( fUseNode ) + { + int type = pb->GetInt( fTypeParamID ); + if( type == plAnimObjInterface::kUseOwnerNode ) + ::SetWindowText( ::GetDlgItem( hWnd, fNodeDlgItem ), kUseOwnerNodeString ); + else + { + INode *node = pb->GetINode( fNodeParamID ); + TSTR newName( node ? node->GetName() : kUseParamBlockNodeString ); + ::SetWindowText( ::GetDlgItem( hWnd, fNodeDlgItem ), newName ); + } + + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( pb->GetINode( fParamID ) ); + if( iface == nil || !iface->IsNodeRestricted() ) + ::EnableWindow( ::GetDlgItem( hWnd, fNodeDlgItem ), false ); + else + { + ::EnableWindow( ::GetDlgItem( hWnd, fNodeDlgItem ), true ); + } + } +} + +BOOL plPlasmaAnimSelectDlgProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch ( msg ) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = pmap->GetParamBlock(); + + INode *node = pb->GetINode( fParamID ); + TSTR newName( node ? node->GetName() : "Pick" ); + ::SetWindowText( ::GetDlgItem( hWnd, fDlgItem ), newName ); + + IUpdateNodeBtn( hWnd, pb ); + } + break; + + case WM_COMMAND: + if( ( HIWORD( wParam ) == BN_CLICKED ) ) + { + if( LOWORD( wParam ) == fDlgItem ) + { + IParamBlock2 *pb = pmap->GetParamBlock(); + plPlasmaAnimHitCallback hitCB( pb, fParamID, fTitle ); + GetCOREInterface()->DoHitByNameDialog( &hitCB ); + + INode *node = pb->GetINode( fParamID ); + TSTR newName( node ? node->GetName() : "Pick" ); + ::SetWindowText( ::GetDlgItem(hWnd, fDlgItem ), newName ); + pmap->Invalidate( fParamID ); + ::InvalidateRect( hWnd, NULL, TRUE ); + + IUpdateNodeBtn( hWnd, pb ); + return true; + } + else if( fUseNode && LOWORD( wParam ) == fNodeDlgItem ) + { + IParamBlock2 *pb = pmap->GetParamBlock(); + + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( pb->GetINode( fParamID ) ); + iface->PickTargetNode( pb, fNodeParamID, fTypeParamID ); + + IUpdateNodeBtn( hWnd, pb ); + return true; + } + } + break; + } + + if( fChain != nil ) + return fChain->DlgProc( t, pmap, hWnd, msg, wParam, lParam ); + + return false; +} + +void plPlasmaAnimSelectDlgProc::DeleteThis() +{ +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +CLASS_DESC(plAnimCompressComp, gAnimCompressDesc, "Anim Compress", "AnimCompress", COMP_TYPE_MISC, ANIM_COMPRESS_COMP_CID) + +ParamBlockDesc2 gAnimCompressBk +( + plComponent::kBlkComp, _T("AnimCompress"), 0, &gAnimCompressDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + IDD_COMP_ANIM_COMPRESS, IDS_COMP_ANIM_COMPRESS_ROLL, 0, 0, nil, + + plAnimCompressComp::kAnimCompressLevel, _T("compressLevel"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_COMP_ANIM_COMPRESS_NONE, IDC_COMP_ANIM_COMPRESS_LOW, IDC_COMP_ANIM_COMPRESS_HIGH, + p_vals, plAnimCompressComp::kCompressionNone, plAnimCompressComp::kCompressionLow, plAnimCompressComp::kCompressionHigh, + p_default, plAnimCompressComp::kCompressionLow, + end, + + plAnimCompressComp::kAnimCompressThreshold, _T("Threshold"), TYPE_FLOAT, 0, 0, + p_default, 0.01, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_COMPRESS_THRESHOLD, IDC_COMP_ANIM_COMPRESS_THRESHOLD_SPIN, 0.001, + end, + + end +); + +plAnimCompressComp::plAnimCompressComp() +{ + fClassDesc = &gAnimCompressDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plAnimCompressComp::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetAnimCompress(fCompPB->GetInt(ParamID(kAnimCompressLevel))); + + // We use Max's key reduction code, which doesn't seem to match up with its own UI. + // Manually using Max's "Reduce Keys" option with a threshold of .01 seems to give + // approximately the same results as calling the function ApplyKeyReduction with + // a threshold of .0002. I want the UI to appear consistent to the artist, so we + // shrug our shoulders and scale down by 50. + node->SetKeyReduceThreshold(fCompPB->GetFloat(ParamID(kAnimCompressThreshold)) / 50.f); + return true; +} + +hsBool plAnimCompressComp::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimComponent.h new file mode 100644 index 00000000..b302b69a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimComponent.h @@ -0,0 +1,198 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAnimComponent_inc +#define plAnimComponent_inc + +#include +#include "plComponent.h" +#include "plComponentReg.h" +#include "../pnKeyedObject/plKey.h" +#include "hsTemplates.h" +#include "plAnimObjInterface.h" +#include "plNoteTrackDlgComp.h" + +#define ANIM_COMP_CID Class_ID(0x32e77ab, 0x28a80383) +#define ANIM_GROUP_COMP_CID Class_ID(0x341a57fc, 0x4cda6c64) +#define ANIM_COMPRESS_COMP_CID Class_ID(0x116d3175, 0x4e465807) + +class plComponentBase; +class plMaxNode; +class plSimpleTMModifier; +class plLightModifier; +class plAGMasterMod; +class plAGAnim; +class plMsgForwarder; +class plController; +class plAGApplicator; + +class plAnimComponentBase : public plComponent, public plAnimObjInterface +{ +protected: + std::map fMods; + std::map fLightMods; + bool fNeedReset; + +public: + plAnimComponentBase(); + void DeleteThis() { delete this; } + + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual plKey GetModKey(plMaxNode *node)=0; + const char *GetAnimName(); + static IsAnimComponent(plComponentBase *comp); + + std::map fAnims; + + // Static function for setting up scalar controllers + static void SetupCtl( plAGAnim *anim, plController *ctl, plAGApplicator *app, plMaxNode *node ); + + // Static function to grab the animation key given the INode pointing to either a) an anim component or b) a material stealth node + static bool GetAnimKey( plMaxNode *node, hsTArray &outKeys ); + + // Static function to grab the animObjInterface for a given INode, regardless of type + static plAnimObjInterface *GetAnimInterface( INode *node ); + + // plAnimObjInterface functions + virtual void PickTargetNode( IParamBlock2 *destPB, ParamID destParamID, ParamID typeID ); + virtual hsBool IsNodeRestricted( void ) { return true; } + virtual const char *GetIfaceSegmentName( hsBool allowNil ); + +protected: + hsBool IAddTMToAnim(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg); + hsBool IAddLightToAnim(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg); + hsBool IConvertNodeSegmentBranch(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg); + hsBool IMakePersistent(plMaxNode *node, plAGAnim *anim, plErrorMsg *pErrMsg); +}; + +class plAnimComponent : public plAnimComponentBase +{ +public: + plAnimComponent(); + plKey GetModKey(plMaxNode *node); + virtual hsBool GetKeyList( INode *restrictedNode, hsTArray &outKeys ); +}; + +class plAnimGroupedComponent : public plAnimComponentBase +{ +protected: + plMsgForwarder *fForward; + +public: + plAnimGroupedComponent(); + + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + + plKey GetModKey(plMaxNode *node); + + virtual hsBool IsNodeRestricted( void ) { return false; } + virtual hsBool GetKeyList( INode *restrictedNode, hsTArray &outKeys ); +}; + +//// Dialog Proc For Anim Selection ///////////////////////////////////////////////////////////// +// Derive from this guy to handle selection of an animation. Also, you can pass a pointer in +// to another dialog proc to chain procs together (this proc will execute before the chained +// one). + +class plPlasmaAnimSelectDlgProc : public ParamMap2UserDlgProc +{ +protected: + ParamID fParamID; + int fDlgItem; + + bool fUseNode; + ParamID fNodeParamID; + ParamID fTypeParamID; + int fNodeDlgItem; + + TCHAR fTitle[ 128 ]; + + ParamMap2UserDlgProc *fChain; + + void IUpdateNodeBtn( HWND hWnd, IParamBlock2 *pb ); + +public: + + int GetHandledDlgItem( void ) const { return fDlgItem; } + + // No node restriction version + plPlasmaAnimSelectDlgProc( ParamID paramID, int dlgItem, TCHAR *promptTitle, ParamMap2UserDlgProc *chainedDlgProc = nil ); + + // Node restricted version + plPlasmaAnimSelectDlgProc( ParamID paramID, int dlgItem, ParamID nodeParamID, ParamID typeParamID, int nodeDlgItem, TCHAR *promptTitle, ParamMap2UserDlgProc *chainedDlgProc = nil ); + + virtual void SetThing( ReferenceTarget *m ); + virtual void Update( TimeValue t, Interval &valid, IParamMap2 *map ); + virtual BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); + + void DeleteThis(); +}; + +class plAnimComponentProc : public ParamMap2UserDlgProc +{ +protected: + plComponentNoteTrackDlg fNoteTrackDlg; + IParamBlock2 *fPB; + + void EnableGlobal(HWND hWnd, hsBool enable); + +public: + static void FillAgeGlobalComboBox(HWND box, char *varName); + static void SetBoxToAgeGlobal(HWND box, char *varName); + + virtual BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + virtual void Update(TimeValue t, Interval &valid, IParamMap2 *map); + void DeleteThis(); +}; + +class plAnimCompressComp : public plComponent +{ +public: + plAnimCompressComp(); + void DeleteThis() { delete this; } + + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + //virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kCompressionNone, + kCompressionLow, + kCompressionHigh, + }; + + enum + { + kAnimCompressLevel, + kAnimCompressThreshold, + }; +}; + +#endif // plAnimComponent_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimEventComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimEventComponent.cpp new file mode 100644 index 00000000..4a05a628 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimEventComponent.cpp @@ -0,0 +1,615 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plAnimEventComponent.h" +#include "plComponentReg.h" +#include "resource.h" +#include "../MaxMain/plMaxNode.h" + +#include "plAnimComponent.h" +#include "plNotetrackAnim.h" + +#include "plAnimCompProc.h" +#include "plPickNode.h" + +#include "../plModifier/plAnimEventModifier.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "hsResMgr.h" +#include "../pnMessage/plRefMsg.h" + +void DummyCodeIncludeFuncAnimDetector() {} + +CLASS_DESC(plAnimEventComponent, gAnimEventDesc, "Anim Event", "AnimEvent", COMP_TYPE_DETECTOR, ANIMEVENT_CID) + + +class plAnimEventProc : public plAnimCompProc +{ +protected: + virtual void IPickComponent(IParamBlock2* pb); + virtual void ILoadUser(HWND hWnd, IParamBlock2* pb); + virtual bool IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID); + +public: + plAnimEventProc(); +}; +static plAnimEventProc gAnimEventProc; + + +enum +{ + kAnimComp, + kAnimNode, + kAnimEvent_DEAD, + kAnimName_DEAD, + + kAnimBegin, + kAnimEnd, + kAnimMarkers, +}; + +enum +{ + kAnimEventBegin, + kAnimEventEnd, + kAnimEventMarker, +}; + +ParamBlockDesc2 gAnimEventBlock +( + plComponent::kBlkComp, _T("animEvent"), 0, &gAnimEventDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_DETECTOR_ANIM, IDS_COMP_DETECTOR_ANIM, 0, 0, &gAnimEventProc, + + kAnimComp, _T("animComp"), TYPE_INODE, 0, 0, + end, + + kAnimNode, _T("animNode"), TYPE_INODE, 0, 0, + end, + + kAnimBegin, _T("animBegin"), TYPE_BOOL, 0, 0, + end, + kAnimEnd, _T("animEnd"), TYPE_BOOL, 0, 0, + end, + kAnimMarkers, _T("animMarkers"), TYPE_STRING_TAB, 0, 0, 0, + end, + + end +); + + +plAnimEventComponent::plAnimEventComponent() : fCanExport(false) +{ + fClassDesc = &gAnimEventDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plAnimEventComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plComponentBase* animComp; + plMaxNode* animNode; + if (!gAnimEventProc.GetCompAndNode(fCompPB, animComp, animNode)) + { + pErrMsg->Set(true, + "Anim Event Component", + "Component %s does not have a valid anim component and node selected.\n" + "It will not be exported.", + GetINode()->GetName()).Show(); + pErrMsg->Set(false); + fCanExport = false; + } + else + fCanExport = true; + + return plActivatorBaseComponent::SetupProperties(node, pErrMsg); +} + +hsBool plAnimEventComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if (!fCanExport) + return false; + + plAnimEventModifier* mod = TRACKED_NEW plAnimEventModifier; + plKey modKey = node->AddModifier(mod, IGetUniqueName(node)); + + fLogicModKeys[node] = modKey; + + return true; +} + +plEventCallbackMsg* CreateCallbackMsg(plAnimCmdMsg* animMsg, plKey modKey) +{ + plEventCallbackMsg *eventMsg = TRACKED_NEW plEventCallbackMsg; + eventMsg->AddReceiver(modKey); + eventMsg->fRepeats = -1; + + animMsg->AddCallback(eventMsg); + hsRefCnt_SafeUnRef(eventMsg); // AddCallback adds it's own ref, so remove ours (the default of 1) + + return eventMsg; +} + +hsBool plAnimEventComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (!fCanExport) + return false; + + plKey modKey = fLogicModKeys[node]; + plAnimEventModifier* mod = plAnimEventModifier::ConvertNoRef(modKey->GetObjectPtr()); + + plAnimComponentBase* animComp; + plMaxNode* animNode; + gAnimEventProc.GetCompAndNode(fCompPB, (plComponentBase*&)animComp, animNode); + + // + // Create and setup the callback message + // + plKey animKey = animComp->GetModKey(animNode); + const char* animName = animComp->GetAnimName(); + + plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg; + animMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); + animMsg->SetSender(modKey); + animMsg->SetAnimName(animName); + animMsg->AddReceiver(animKey); + + if (fCompPB->GetInt(kAnimBegin)) + { + plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); + eventMsg->fEvent = kBegin; + } + if (fCompPB->GetInt(kAnimEnd)) + { + plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); + eventMsg->fEvent = kEnd; + } + if (fCompPB->Count(kAnimMarkers) > 0) + { + plNotetrackAnim anim(animComp, nil); + plAnimInfo info = anim.GetAnimInfo(animName); + + int numMarkers = fCompPB->Count(kAnimMarkers); + for (int i = 0; i < numMarkers; i++) + { + const char* marker = fCompPB->GetStr(kAnimMarkers, 0, i); + float time = info.GetMarkerTime(marker); + + plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); + eventMsg->fEvent = kTime; + eventMsg->fEventTime = time; + } + } + + mod->SetCallback(animMsg); + + hsTArray receivers; + IGetReceivers(node, receivers); + mod->SetReceivers(receivers); + + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// + + +plAnimEventProc::plAnimEventProc() +{ + fCompButtonID = IDC_ANIM_BUTTON; + fCompParamID = kAnimComp; + fNodeButtonID = IDC_NODE_BUTTON; + fNodeParamID = kAnimNode; +} + +void plAnimEventProc::IPickComponent(IParamBlock2* pb) +{ + std::vector cids; + cids.push_back(ANIM_COMP_CID); + cids.push_back(ANIM_GROUP_COMP_CID); + + plPick::Node(pb, kAnimComp, &cids, true, false); +} + +static int ListBox_AddStringData(HWND hList, const char* text, int data) +{ + int idx = ListBox_AddString(hList, text); + ListBox_SetItemData(hList, idx, data); + return idx; +} + +static bool IsMarkerSelected(IParamBlock2* pb, int paramID, const char* marker, bool remove=false) +{ + int numMarkers = pb->Count(paramID); + for (int i = 0; i < numMarkers; i++) + { + if (hsStrEQ(marker, pb->GetStr(paramID, 0, i))) + { + if (remove) + pb->Delete(paramID, i, 1); + return true; + } + } + + return false; +} + +// +// Remove markers we had saved that aren't in the object's notetrack any more +// +static void RemoveDeadMarkers(IParamBlock2* pb, int paramID, plAnimInfo& info) +{ + int numMarkers = pb->Count(paramID); + for (int i = numMarkers-1; i >= 0; i--) + { + float time = info.GetMarkerTime(pb->GetStr(paramID, 0, i)); + if (time == -1) + { + pb->Delete(paramID, i, 1); + } + } +} + +void plAnimEventProc::ILoadUser(HWND hWnd, IParamBlock2* pb) +{ + HWND hList = GetDlgItem(hWnd, IDC_EVENT_LIST); + ListBox_ResetContent(hList); + + plAnimComponentBase* comp; + plMaxNode* node; + + // + // If we don't have a valid comp and node, we should be disabled + // + if (!GetCompAndNode(pb, (plComponentBase*&)comp, node)) + { + EnableWindow(hList, FALSE); + return; + } + else + EnableWindow(hList, TRUE); + + // + // Load the events + // + int idx; + + idx = ListBox_AddStringData(hList, "(Begin)", kAnimEventBegin); + if (pb->GetInt(kAnimBegin)) + ListBox_SetSel(hList, TRUE, idx); + + idx = ListBox_AddStringData(hList, "(End)", kAnimEventEnd); + if (pb->GetInt(kAnimEnd)) + ListBox_SetSel(hList, TRUE, idx); + + if (comp) + { + // Get the shared animations for all the nodes this component is applied to + plNotetrackAnim anim(comp, nil); + plAnimInfo info = anim.GetAnimInfo(comp->GetAnimName()); + + RemoveDeadMarkers(pb, kAnimMarkers, info); + + // Get all the markers in this animation + while (const char* marker = info.GetNextMarkerName()) + { + idx = ListBox_AddStringData(hList, marker, kAnimEventMarker); + + if (IsMarkerSelected(pb, kAnimMarkers, marker)) + ListBox_SetSel(hList, TRUE, idx); + } + } +} + +bool plAnimEventProc::IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID) +{ + if (cmd == LBN_SELCHANGE && resID == IDC_EVENT_LIST) + { + HWND hList = GetDlgItem(hWnd, IDC_EVENT_LIST); + int idx = ListBox_GetCurSel(hList); + BOOL selected = ListBox_GetSel(hList, idx); + int eventType = ListBox_GetItemData(hList, idx); + + if (eventType == kAnimEventBegin) + pb->SetValue(kAnimBegin, 0, selected); + else if (eventType == kAnimEventEnd) + pb->SetValue(kAnimEnd, 0, selected); + else if (eventType == kAnimEventMarker) + { + char buf[256]; + ListBox_GetText(hList, idx, buf); + if (selected) + { + if (!IsMarkerSelected(pb, kAnimMarkers, buf)) + { + TCHAR* name = buf; + pb->Append(kAnimMarkers, 1, &name); + } + } + else + IsMarkerSelected(pb, kAnimMarkers, buf, true); + } + + return true; + } + + return false; +} + + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + + +CLASS_DESC(plMtlEventComponent, gMtlEventDesc, "Mtl Event", "MtlEvent", COMP_TYPE_DETECTOR, MTLEVENT_CID) + +class plMtlEventProc : public plMtlAnimProc +{ +public: + plMtlEventProc(); + +protected: + virtual void ILoadUser(HWND hWnd, IParamBlock2* pb); + virtual bool IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID); +}; +static plMtlEventProc gMtlEventProc; + +enum +{ + kMtlMtl, + kMtlNode, + kMtlAnim, + + kMtlBegin, + kMtlEnd, + kMtlMarkers, +}; + +ParamBlockDesc2 gMtlEventBlock +( + plComponent::kBlkComp, _T("mtlEvent"), 0, &gMtlEventDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_DETECTOR_MTL, IDS_COMP_DETECTOR_MTL, 0, 0, &gMtlEventProc, + + kMtlMtl, _T("mtl"), TYPE_MTL, 0, 0, + end, + + kMtlNode, _T("node"), TYPE_INODE, 0, 0, + end, + + kMtlAnim, _T("anim"), TYPE_STRING, 0, 0, + end, + + kMtlBegin, _T("animBegin"), TYPE_BOOL, 0, 0, + end, + kMtlEnd, _T("animEnd"), TYPE_BOOL, 0, 0, + end, + kMtlMarkers,_T("markers"), TYPE_STRING_TAB, 0, 0, 0, + end, + + end +); + + +plMtlEventComponent::plMtlEventComponent() : fCanExport(false) +{ + fClassDesc = &gMtlEventDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plMtlEventComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + Mtl* mtl = fCompPB->GetMtl(kMtlMtl); + + if (!mtl) + { + pErrMsg->Set(true, + "Mtl Event Component", + "Component %s does not have a valid material selected.\n" + "It will not be exported.", + GetINode()->GetName()).Show(); + pErrMsg->Set(false); + fCanExport = false; + } + else + fCanExport = true; + + return plActivatorBaseComponent::SetupProperties(node, pErrMsg); +} + +hsBool plMtlEventComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if (!fCanExport) + return false; + + plAnimEventModifier* mod = TRACKED_NEW plAnimEventModifier; + plKey modKey = node->AddModifier(mod, IGetUniqueName(node)); + + fLogicModKeys[node] = modKey; + + return true; +} + +// KLUDGE - The material animation key getter is here, so we have to include all this crap +#include "plResponderMtl.h" + +hsBool plMtlEventComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (!fCanExport) + return false; + + plKey modKey = fLogicModKeys[node]; + plAnimEventModifier* mod = plAnimEventModifier::ConvertNoRef(modKey->GetObjectPtr()); + + Mtl* mtl = fCompPB->GetMtl(kMtlMtl); + plMaxNodeBase* mtlNode = (plMaxNodeBase*)fCompPB->GetINode(kMtlNode); + const char* mtlAnim = fCompPB->GetStr(kMtlAnim); + + // + // Create and setup the callback message + // + hsTArray animKeys; + GetMatAnimModKey(mtl, mtlNode, mtlAnim, animKeys); + + plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg; + animMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); + animMsg->SetSender(modKey); + animMsg->SetAnimName(mtlAnim); + animMsg->AddReceivers(animKeys); + + if (fCompPB->GetInt(kMtlBegin)) + { + plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); + eventMsg->fEvent = kBegin; + } + if (fCompPB->GetInt(kMtlEnd)) + { + plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); + eventMsg->fEvent = kEnd; + } + if (fCompPB->Count(kMtlMarkers) > 0) + { + plNotetrackAnim anim(mtl, nil); + plAnimInfo info = anim.GetAnimInfo(mtlAnim); + + int numMarkers = fCompPB->Count(kMtlMarkers); + for (int i = 0; i < numMarkers; i++) + { + const char* marker = fCompPB->GetStr(kMtlMarkers, 0, i); + float time = info.GetMarkerTime(marker); + + plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); + eventMsg->fEvent = kTime; + eventMsg->fEventTime = time; + } + } + + mod->SetCallback(animMsg); + + hsTArray receivers; + IGetReceivers(node, receivers); + mod->SetReceivers(receivers); + + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// + + +plMtlEventProc::plMtlEventProc() +{ + fMtlButtonID = IDC_MTL_BUTTON; + fMtlParamID = kMtlMtl; + fNodeButtonID = IDC_NODE_BUTTON; + fNodeParamID = kMtlNode; + fAnimComboID = IDC_ANIM_COMBO; + fAnimParamID = kMtlAnim; +} + +void plMtlEventProc::ILoadUser(HWND hWnd, IParamBlock2* pb) +{ + HWND hList = GetDlgItem(hWnd, IDC_EVENT_LIST); + ListBox_ResetContent(hList); + + // + // If we don't have a valid material, we should be disabled + // + Mtl* mtl = pb->GetMtl(kMtlMtl); + if (!mtl) + { + EnableWindow(hList, FALSE); + return; + } + else + EnableWindow(hList, TRUE); + + // + // Load the events + // + int idx; + + idx = ListBox_AddStringData(hList, "(Begin)", kAnimEventBegin); + if (pb->GetInt(kMtlBegin)) + ListBox_SetSel(hList, TRUE, idx); + + idx = ListBox_AddStringData(hList, "(End)", kAnimEventEnd); + if (pb->GetInt(kMtlEnd)) + ListBox_SetSel(hList, TRUE, idx); + + if (mtl) + { + const char* mtlAnim = pb->GetStr(kMtlAnim); + + // Get the shared animations for all the nodes this component is applied to + plNotetrackAnim anim(mtl, nil); + plAnimInfo info = anim.GetAnimInfo(mtlAnim); + + RemoveDeadMarkers(pb, kMtlMarkers, info); + + // Get all the markers in this animation + while (const char* marker = info.GetNextMarkerName()) + { + idx = ListBox_AddStringData(hList, marker, kAnimEventMarker); + + if (IsMarkerSelected(pb, kMtlMarkers, marker)) + ListBox_SetSel(hList, TRUE, idx); + } + } +} + +bool plMtlEventProc::IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID) +{ + if (cmd == LBN_SELCHANGE && resID == IDC_EVENT_LIST) + { + HWND hList = GetDlgItem(hWnd, IDC_EVENT_LIST); + int idx = ListBox_GetCurSel(hList); + BOOL selected = ListBox_GetSel(hList, idx); + int eventType = ListBox_GetItemData(hList, idx); + + if (eventType == kAnimEventBegin) + pb->SetValue(kMtlBegin, 0, selected); + else if (eventType == kAnimEventEnd) + pb->SetValue(kMtlEnd, 0, selected); + else if (eventType == kAnimEventMarker) + { + char buf[256]; + ListBox_GetText(hList, idx, buf); + if (selected) + { + if (!IsMarkerSelected(pb, kMtlMarkers, buf)) + { + TCHAR* name = buf; + pb->Append(kMtlMarkers, 1, &name); + } + } + else + IsMarkerSelected(pb, kMtlMarkers, buf, true); + } + + return true; + } + + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimEventComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimEventComponent.h new file mode 100644 index 00000000..97925668 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimEventComponent.h @@ -0,0 +1,60 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAnimEventComponent_inc +#define plAnimEventComponent_inc + +#include "plActivatorBaseComponent.h" + +#define ANIMEVENT_CID Class_ID(0x32eb34af, 0x62c70002) +#define MTLEVENT_CID Class_ID(0x2984243c, 0x30ea3acb) + +class plAnimEventComponent : public plActivatorBaseComponent +{ +protected: + bool fCanExport; + +public: + plAnimEventComponent(); + + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); +}; + +class plMtlEventComponent : public plActivatorBaseComponent +{ +protected: + bool fCanExport; + +public: + plMtlEventComponent(); + + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); +}; + +#endif // plAnimEventComponent_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimObjInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimObjInterface.h new file mode 100644 index 00000000..37b788e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAnimObjInterface.h @@ -0,0 +1,77 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plAnimObjInterface - Pure virtual interface class for providing a // +// common gateway for accessing and converting // +// animated objects (such as components and materials).// +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plAnimObjInterface_h +#define _plAnimObjInterface_h + +#include "hsTemplates.h" + +class plAnimObjInterface +{ + public: + + // If the following function returns true, then it makes sense to restrict + // the animation conversion to a specific node (i.e. PickTargetNode() makes + // sense) + virtual hsBool IsNodeRestricted( void ) = 0; + + // Allows the user to pick an INode that this animation is applied to + // (ex. as a material or as a component) and stores it in the given ID + // of the given ParamBlock + virtual void PickTargetNode( IParamBlock2 *destPB, ParamID destParamID, ParamID typeID ) = 0; + + // The following is the node type enum for paramBlocks that store the above node-restricted + // info (i.e. the values for the "typeID" param specified above) + enum NodeTypes + { + kUseParamBlockNode, // Use the node stored in the ParamBlock + kUseOwnerNode // Ignore the ParamBlock; use the node applied to (i.e. current node in convert process) + }; + + // Given the optional INode to restrict to, return the list of keys to send messages to for conversion + virtual hsBool GetKeyList( INode *restrictedNode, hsTArray &outKeys ) = 0; + + // Return the name of the segment/animation that this interface references. Pass "false" to get the + // ENTIRE_ANIMATION_NAME string for entire animations, "true" for nil. + virtual const char *GetIfaceSegmentName( hsBool allowNil ) = 0; + + // This animation would require (depending on the node restriction) a separate material (i.e. material anim) + virtual hsBool MightRequireSeparateMaterial( void ) { return false; } +}; + +// Strings for above NodeTypes enums +#define kUseParamBlockNodeString "(All)" +#define kUseOwnerNodeString "(Applied Node)" + + +#endif // _plAnimObjInterface_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAudioComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAudioComponents.cpp new file mode 100644 index 00000000..909198c8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAudioComponents.cpp @@ -0,0 +1,3985 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plComponentProcBase.h" + +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include +#include "plAudioComponents.h" +#include "plMiscComponents.h" +#include "plAnimComponent.h" +#include "../plInterp/plAnimEaseTypes.h" +#include "../plAvatar/plAGAnim.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +#include "../MaxConvert/plConvert.h" +#include "../MaxMain/plPluginResManager.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + + +#include "plgDispatch.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" + + +#include "../plScene/plSceneNode.h" +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../plInterp/plController.h" +#include "../MaxMain/plMaxNode.h" +#include "../pnKeyedObject/plKey.h" + +//Physics Related +//#include "../plHavok1/plHKPhysical.h" //Physics Comp +#include "../pnSceneObject/plSimulationInterface.h" +#include "../MaxMain/plPhysicalProps.h" +#include "../plPhysX/plPXPhysical.h" + +// Sound Related +#include "../plPhysical/plEnvEffectDetector.h" +#include "../pnMessage/plEnvEffectMsg.h" +#include "../PubUtilLib/plAudible/plWinAudible.h" +#include "../pnSceneObject/plAudioInterface.h" + +// Anim Related +#include "plMaxAnimUtils.h" +#include "plMaxWaveUtils.h" +#include "../pfAudio/plRandomSoundMod.h" +#include "../plAudio/plWin32StaticSound.h" +#include "../plAudio/plWin32StreamingSound.h" +#include "../plAudio/plWin32GroupedSound.h" +#include "../plAudioCore/plSoundBuffer.h" +#include "../plFile/plFileUtils.h" + +// Valdez Asset Manager Related +#include "../../AssetMan/PublicInterface/MaxAssInterface.h" +#include + +// Fun soft volume stuff +#include "plSoftVolumeComponent.h" +#include "../plIntersect/plSoftVolume.h" + +// Misc +#include "../MaxMain/plMaxCFGFile.h" +#include "plPickNode.h" + +// EAX stuff +#include "../plAudio/plEAXListenerMod.h" +#include +#include + +#include "../plResMgr/plLocalization.h" +#include "../plPhysical/plPhysicalSndGroup.h" + +// EAX3 values which eax4 no longer defines, but we still need. +// Single window material preset +#define EAX_MATERIAL_SINGLEWINDOW (-2800) +#define EAX_MATERIAL_SINGLEWINDOWLF 0.71f +#define EAX_MATERIAL_SINGLEWINDOWROOMRATIO 0.43f + +// Double window material preset +#define EAX_MATERIAL_DOUBLEWINDOW (-5000) +#define EAX_MATERIAL_DOUBLEWINDOWLF 0.40f +#define EAX_MATERIAL_DOUBLEWINDOWROOMRATIO 0.24f + +// Thin door material preset +#define EAX_MATERIAL_THINDOOR (-1800) +#define EAX_MATERIAL_THINDOORLF 0.66f +#define EAX_MATERIAL_THINDOORROOMRATIO 0.66f + +// Thick door material preset +#define EAX_MATERIAL_THICKDOOR (-4400) +#define EAX_MATERIAL_THICKDOORLF 0.64f +#define EAX_MATERIAL_THICKDOORROOMRATIO 0.27f + +// Wood wall material preset +#define EAX_MATERIAL_WOODWALL (-4000) +#define EAX_MATERIAL_WOODWALLLF 0.50f +#define EAX_MATERIAL_WOODWALLROOMRATIO 0.30f + +// Brick wall material preset +#define EAX_MATERIAL_BRICKWALL (-5000) +#define EAX_MATERIAL_BRICKWALLLF 0.60f +#define EAX_MATERIAL_BRICKWALLROOMRATIO 0.24f + +// Stone wall material preset +#define EAX_MATERIAL_STONEWALL (-6000) +#define EAX_MATERIAL_STONEWALLLF 0.68f +#define EAX_MATERIAL_STONEWALLROOMRATIO 0.20f + +// Curtain material preset +#define EAX_MATERIAL_CURTAIN (-1200) +#define EAX_MATERIAL_CURTAINLF 0.15f +#define EAX_MATERIAL_CURTAINROOMRATIO 1.00f + +void DummyCodeIncludeFuncAudio() {} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +/// Base Sound Emitter Component //////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + +enum +{ + kSoundFileName, + kLoopingChekBx_DEAD, //Removed in v3 + kLoopBegin_DEAD, //Removed in v2 + kLoopEnd_DEAD, //Removed in v2 + kMinFallOffRad_DEAD, // removed in v6 + kMaxFallOffRad_DEAD, // removed in v6 + kSoundAutoStartCkBx, + kSoundLoopCkBx, + kSoundLoopSegCkBx_DEAD, //Removed in v3 + kSoundLoopSegBeg_DEAD, + kSoundLoopSegEnd_DEAD, + kSoundLoopSegBegDDList_DEAD, //Inserted in v3 + kSoundLoopSegEndDDList_DEAD, //Inserted in v3 + kSoundLoopSegBeg2_DEAD, //Inserted in v3 + kSoundLoopSegEnd2_DEAD, //Inserted in v3 + kSFileNameTextField, + kOldSoundVolumeSlider, //Inserted in v4 OBLITERATE + kSoundIConeAngle, //Inserted in v5 + kSoundOConeAngle, //Inserted in v5 + kSoundOConeVolumeSlider, //Inserted in v5 + kMinFallOffRad, + kMaxFallOffRad, + kSoundLoopName, + kSoundConeBool, //Inserted in v6, + kNotSoOldSoundVolumeSlider, + kSndFadeInEnable, + kSndFadeInType, + kSndFadeInLength, + kSndFadeOutEnable, + kSndFadeOutType, + kSndFadeOutLength, + kSndFadedVolume, // Currently unsupported + kSndSoftRegion, + kSndSoftRegionEnable, + kSndVersionCount, // So we can do version upgrading (DAMN YOU MAX!!!) + kSoundVolumeSlider, + kSndDisableLOD, + kSndChannelSelect, + kSndAllowChannelSelect, + kSndIsWMAFile_DEAD, + kSndWMAStartClip_DEAD, + kSndWMAEndClip_DEAD, + kSndEnableCrossfadeCover_DEAD, + kSndCrossfadeCoverFilename_DEAD, + kSndCoverIsWMAFile_DEAD, + kSndCoverWMAStartClip_DEAD, + kSndCoverWMAEndClip_DEAD, + kSndIsLocalOnly, + kSndCategory, + kSndPriority, + kSndIncidental, + kSndStreamCompressed, +}; + +enum +{ + kSndFadeTypeLinear, + kSndFadeTypeLogarithmic, + kSndFadeTypeExponential +}; + +UInt32 plBaseSoundEmitterComponent::fWarningFlags = 0; +//bool plBaseSoundEmitterComponent::fAllowUnhide = false; + +void plBaseSoundEmitterComponent::IShowError( UInt32 type, const char *errMsg, const char *nodeName, plErrorMsg *pErrMsg ) +{ + if( !( fWarningFlags & (1 << type) ) ) + { + if( pErrMsg->Set( true, "Sound Component Error", errMsg, nodeName ).CheckAskOrCancel() ) + fWarningFlags |= (1 << type); + pErrMsg->Set( false ); + } +} + +plBaseSoundEmitterComponent::plBaseSoundEmitterComponent() +{ + fAllowUnhide = false; + fAssetsUpdated = false; + fCreateGrouped = false; + fIndices.clear(); + fValidNodes.clear(); +} + +plBaseSoundEmitterComponent::~plBaseSoundEmitterComponent() +{ +} + + +RefTargetHandle plBaseSoundEmitterComponent::Clone( RemapDir &remap ) +{ + // Do the base clone + plBaseSoundEmitterComponent *obj = (plBaseSoundEmitterComponent *)plComponentBase::Clone( remap ); + + obj->fSoundAssetId = fSoundAssetId; + obj->fCoverSoundAssetID = fCoverSoundAssetID; + + return obj; +} + +void plBaseSoundEmitterComponent::IConvertOldVolume( void ) +{ + int oldVol = fCompPB->GetInt( (ParamID)kOldSoundVolumeSlider, 0 ); + if( oldVol != 4999 ) + { + float v = (float)( oldVol - 5000 ) / 5000.f; + fCompPB->SetValue( (ParamID)kNotSoOldSoundVolumeSlider, 0, v ); + fCompPB->SetValue( (ParamID)kOldSoundVolumeSlider, 0, 4999 ); + } + + // Shut up. + float notSoOldV = fCompPB->GetFloat( (ParamID)kNotSoOldSoundVolumeSlider, 0 ); + if( notSoOldV != -1.f ) + { + float d3dValueReally = -5000.f + ( 5000.f * notSoOldV ); + + float ourNewValue = (float)d3dValueReally / 100.f; + + fCompPB->SetValue( (ParamID)kSoundVolumeSlider, 0, ourNewValue ); + fCompPB->SetValue( (ParamID)kNotSoOldSoundVolumeSlider, 0, -1.f ); + } +} + +float plBaseSoundEmitterComponent::IGetDigitalVolume( void ) const +{ + return (float)pow( 10.f, fCompPB->GetFloat( (ParamID)kSoundVolumeSlider, 0 ) / 20.f ); +} + +#define OLD_MAX_ASS_CHUNK 0x5500 +#define MAX_ASS_CHUNK 0x5501 + +IOResult plBaseSoundEmitterComponent::Save(ISave *isave) +{ + IOResult res = plComponentBase::Save(isave); + if (res != IO_OK) + return res; + + isave->BeginChunk(MAX_ASS_CHUNK); + ULONG nwrite; + + UInt64 id = fSoundAssetId; + res = isave->Write(&id, sizeof(id), &nwrite); + if (res != IO_OK) + return res; + + id = fCoverSoundAssetID; + res = isave->Write(&id, sizeof(id), &nwrite); + if (res != IO_OK) + return res; + + isave->EndChunk(); + + return IO_OK; +} + +IOResult plBaseSoundEmitterComponent::Load(ILoad *iload) +{ + IOResult res = plComponentBase::Load(iload); + if (res != IO_OK) + return res; + + while (IO_OK == (res = iload->OpenChunk())) + { + if (iload->CurChunkID() == OLD_MAX_ASS_CHUNK) + { + VARIANT tempVar; + ULONG nread; + res = iload->Read(&tempVar, sizeof(VARIANT), &nread); + fSoundAssetId = tempVar.decVal.Lo64; + } + // Secret AssMan value used for no good.... + else if (iload->CurChunkID() == MAX_ASS_CHUNK) + { + ULONG nread; + UInt64 id; + res = iload->Read(&id, sizeof(id), &nread); + fSoundAssetId = id; + res = iload->Read(&id, sizeof(id), &nread); + fCoverSoundAssetID = id; + } + iload->CloseChunk(); + if (res != IO_OK) + return res; + } + + return IO_OK; +} + +void plBaseSoundEmitterComponent::SetSoundAssetId( plBaseSoundEmitterComponent::WhichSound which, jvUniqueId assetId, const TCHAR *fileName ) +{ + if( which == kBaseSound ) + { + fSoundAssetId = assetId; + + fCompPB->SetValue( (ParamID)kSoundFileName, 0, (TCHAR *)fileName ); + if( fCompPB->GetMap() ) + fCompPB->GetMap()->Invalidate( (ParamID)kSoundFileName ); + } + else + { + hsAssert( false, "Setting a sound that isn't supported on this component" ); + } +} + +jvUniqueId plBaseSoundEmitterComponent::GetSoundAssetID( plBaseSoundEmitterComponent::WhichSound which ) +{ + if( which == kCoverSound ) + return fCoverSoundAssetID; + else if( which == kBaseSound ) + return fSoundAssetId; + + hsAssert( false, "Getting a sound that isn't supported on this component" ); + return fSoundAssetId; +} + +void plBaseSoundEmitterComponent::IUpdateAssets( void ) +{ + if( fAssetsUpdated ) + return; + + if( !fSoundAssetId.IsEmpty() || !fCoverSoundAssetID.IsEmpty() ) + { + MaxAssInterface *maxAssInterface = GetMaxAssInterface(); + if( !maxAssInterface ) + return; + + // Download the latest version and retrieve the filename + char newfilename[ MAX_PATH ]; + if(maxAssInterface->GetLatestVersionFile( fSoundAssetId, newfilename, MAX_PATH ) ) + { + // AssetID overrides filename + fCompPB->SetValue( (ParamID)kSoundFileName, 0, newfilename ); + } + + fAssetsUpdated = true; + } + else + fAssetsUpdated = true; +} + +TCHAR *plBaseSoundEmitterComponent::GetSoundFileName( plBaseSoundEmitterComponent::WhichSound which ) +{ + IUpdateAssets(); + + if( which == kBaseSound ) + return fCompPB->GetStr( (ParamID)kSoundFileName ); + + hsAssert( false, "Getting a sound that isn't supported on this component" ); + return nil; +} + +hsBool plBaseSoundEmitterComponent::DeInit( plMaxNode *node, plErrorMsg *pErrMsg ) +{ + fCreateGrouped = false; + fIndices.clear(); + fValidNodes.clear(); + return true; +} + +// Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plBaseSoundEmitterComponent::SetupProperties( plMaxNode *pNode, plErrorMsg *pErrMsg ) +{ + IConvertOldVolume(); +/* + for (int i = 0; i < fIndices.Count(); i++) + delete(fIndices[i]); + fIndices.SetCountAndZero(0); +*/ + + return true; +} + +void plBaseSoundEmitterComponent::SetCreateGrouped( plMaxNode *baseNode, int commonSoundIdx ) +{ + fIndices[ baseNode ] = commonSoundIdx; + fCreateGrouped = true; +} + +bool plBaseSoundEmitterComponent::IValidate(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( GetSoundFileName( kBaseSound ) == nil ) + { + pErrMsg->Set(true, "Sound 3D FileName Error", "The Sound 3D component %s is missing a filename.", node->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + + return true; +} + +hsBool plBaseSoundEmitterComponent::PreConvert( plMaxNode *node, plErrorMsg *pErrMsg, Class_ID classToConvert ) +{ + const char* dbgNodeName = node->GetName(); + fValidNodes[node] = IValidate(node, pErrMsg); + if (!fValidNodes[node]) + return false; + + node->SetForceLocal(true); + + const plAudioInterface *ai = node->GetSceneObject()->GetAudioInterface(); + if (!ai) + { + ai = TRACKED_NEW plAudioInterface; + plKey pAiKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), (hsKeyedObject*)ai,node->GetKey()->GetUoid().GetLocation(), node->GetLoadMask()); + hsgResMgr::ResMgr()->AddViaNotify(pAiKey, TRACKED_NEW plObjRefMsg(node->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + } + if (!ai->GetAudible()) + { + plAudible *pAudible = TRACKED_NEW plWinAudible; + // Add a key for it + plKey key = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), pAudible, node->GetKey()->GetUoid().GetLocation(), node->GetLoadMask()); + + plIntRefMsg* pMsg = TRACKED_NEW plIntRefMsg(node->GetKey(), plRefMsg::kOnCreate, 0, plIntRefMsg::kAudible); + hsgResMgr::ResMgr()->AddViaNotify(pAudible->GetKey(), pMsg, plRefFlags::kActiveRef ); + + pAudible->SetSceneNode(node->GetRoomKey()); + } + + if( !fCreateGrouped ) + fIndices[ node ] = node->GetNextSoundIdx(); + + return true; +} + +void plBaseSoundEmitterComponent::IGrabFadeValues( plSound *sound ) +{ + if( fCompPB->GetInt( (ParamID)kSndFadeInEnable, 0 ) != 0 ) + { + // Fade in is enabled; set the params + plSound::plFadeParams::Type type; + + hsScalar len = (hsScalar)fCompPB->GetFloat( (ParamID)kSndFadeInLength, 0 ); + + switch( fCompPB->GetInt( (ParamID)kSndFadeInType, 0 ) ) + { + case kSndFadeTypeLinear: type = plSound::plFadeParams::kLinear; break; + case kSndFadeTypeLogarithmic: type = plSound::plFadeParams::kLogarithmic; break; + case kSndFadeTypeExponential: type = plSound::plFadeParams::kExponential; break; + } + + sound->SetFadeInEffect( type, len ); + } + + if( fCompPB->GetInt( (ParamID)kSndFadeOutEnable, 0 ) != 0 ) + { + // Fade out is enabled; set the params + plSound::plFadeParams::Type type; + + hsScalar len = (hsScalar)fCompPB->GetFloat( (ParamID)kSndFadeOutLength, 0 ); + + switch( fCompPB->GetInt( (ParamID)kSndFadeOutType, 0 ) ) + { + case kSndFadeTypeLinear: type = plSound::plFadeParams::kLinear; break; + case kSndFadeTypeLogarithmic: type = plSound::plFadeParams::kLogarithmic; break; + case kSndFadeTypeExponential: type = plSound::plFadeParams::kExponential; break; + } + + sound->SetFadeOutEffect( type, len ); + } + +// sound->SetFadedVolume( (hsScalar)fCompPB->GetFloat( kSndFadedVolume, 0 ) ); +} + +void plBaseSoundEmitterComponent::IGrabSoftRegion( plSound *sound, plErrorMsg *pErrMsg ) +{ + // Do the soft volume, if there is one + if( fCompPB->GetInt( (ParamID)kSndSoftRegionEnable, 0 ) != 0 ) + { + plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent( fCompPB->GetINode( (ParamID)kSndSoftRegion ) ); + if( softComp != nil ) + { + plKey softKey = softComp->GetSoftVolume(); + if( softKey != nil ) + { + // Make sure we set checkListener on the sucker + plSoftVolume *vol = plSoftVolume::ConvertNoRef( softKey->GetObjectPtr() ); + if( vol != nil ) + { + vol->SetCheckListener(); + hsgResMgr::ResMgr()->AddViaNotify( softKey, TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, 0, plSound::kSoftRegion ), plRefFlags::kActiveRef ); + } + } + } + else + { + pErrMsg->Set(true, "Sound Emitter Error", "The Sound emitter component %s is checked to use a soft region, but no soft region is specified. Ignoring setting.", GetINode()->GetName() ).Show(); + pErrMsg->Set(false); + } + } +} + +UInt32 plBaseSoundEmitterComponent::ICalcSourceBufferFlags( void ) const +{ + UInt32 bufferFlags = 0; + + if( IHasWaveformProps() ) + { + if( fCompPB->GetInt( (ParamID)kSndAllowChannelSelect ) ) + { + if( fCompPB->GetInt( (ParamID)kSndChannelSelect ) ) + bufferFlags = plSoundBuffer::kOnlyRightChannel; + else + bufferFlags = plSoundBuffer::kOnlyLeftChannel; + } + } + + return bufferFlags; +} + +plSoundBuffer *plBaseSoundEmitterComponent::GetSourceBuffer( const char *fileName, plMaxNode *srcNode, UInt32 srcBufferFlags ) +{ + plSoundBuffer* sb = IGetSourceBuffer(fileName, srcNode, srcBufferFlags); + + const char* plasmaDir = plMaxConfig::GetClientPath(); + if (plasmaDir) + { + char sfxPath[MAX_PATH]; + sprintf(sfxPath, "%ssfx\\%s", plasmaDir, plFileUtils::GetFileName(fileName)); + + // Export any localized versions as well + for (int i = 0; i < plLocalization::GetNumLocales(); i++) + { + char localName[MAX_PATH]; + if (plLocalization::ExportGetLocalized(sfxPath, i, localName)) + { + IGetSourceBuffer(localName, srcNode, srcBufferFlags); + } + } + } + + return sb; +} + +plSoundBuffer *plBaseSoundEmitterComponent::IGetSourceBuffer( const char *fileName, plMaxNode *srcNode, UInt32 srcBufferFlags ) +{ + plKey key; + char keyName[ MAX_PATH ]; + char fullPath[ MAX_PATH ]; + + + strcpy( keyName, fileName ); + ::PathStripPath( keyName ); + + // TEMP HACK until we get packed sounds: + // Given the source filename, we check to see if it's in our plasma game directory. If not, or if + // it's out of date, we copy it over. We'll truncate the filename inside plSoundBuffer when we're ready. + + const char *plasmaDir = plMaxConfig::GetClientPath(); + if( plasmaDir != nil ) + { + strcpy( fullPath, plasmaDir ); + strcat( fullPath, "sfx\\" ); + + // Before we finish our path, make sure that directory EXISTS + plFileUtils::CreateDir( fullPath ); + + // Now finish the path... + strcat( fullPath, keyName ); + + // Check filestamp + WIN32_FILE_ATTRIBUTE_DATA oldFileAttrib, newFileAttrib; + BOOL oldOK, newOK; + + oldOK = GetFileAttributesEx( fileName, GetFileExInfoStandard, &oldFileAttrib ); + newOK = GetFileAttributesEx( fullPath, GetFileExInfoStandard, &newFileAttrib ); + + if( oldOK && newOK ) + { + // Only copy if the file is newer + if( ::CompareFileTime( &oldFileAttrib.ftLastWriteTime, &newFileAttrib.ftLastWriteTime ) > 0 ) + { + ::CopyFile( fileName, fullPath, FALSE ); + } + } + else + { + // Can't compare, so either there was an error or the target file doesn't exist. Copy no matter what. + ::CopyFile( fileName, fullPath, FALSE ); + } + + // Point to our new sound file + fileName = fullPath; + } + + // Additional info for the keyName--need some flag mangling, specifically for the left/right channel mangling + if( srcBufferFlags & plSoundBuffer::kOnlyLeftChannel ) + strcat( keyName, ":L" ); + else if( srcBufferFlags & plSoundBuffer::kOnlyRightChannel ) + strcat( keyName, ":R" ); + + key = srcNode->FindPageKey( plSoundBuffer::Index(), keyName ); + if( key != nil ) + return plSoundBuffer::ConvertNoRef( key->GetObjectPtr() ); + + // Not yet created, so make a new one + plSoundBuffer *buffer = TRACKED_NEW plSoundBuffer( fileName, srcBufferFlags ); + if( !buffer->IsValid() ) + { + // Invalid, so delete and return nil + delete buffer; + return nil; + } + + // We'll put it in a location parallel to the age, say, (age,district,"sounds") + plLocation loc = srcNode->GetLocation(); +// plKey roomKey = hsgResMgr::ResMgr()->NameToLoc( loc.GetAge(), loc.GetChapter(), "sounds" ); + // TEMP HACK FOR NOW, until we actually finish implementing this--just hide them in the same file +// plKey roomKey = hsgResMgr::ResMgr()->FindLocation( loc.GetAge(), loc.GetChapter(), loc.GetPage() ); + + // The buffer may be shared across multiple sources. We could or together the LoadMasks of all + // the nodes that use it, or we can just go with the default loadmask of Always load, and + // count on it never getting dragged into memory if nothing that references it does. + hsgResMgr::ResMgr()->NewKey( keyName, buffer, srcNode->GetLocation()); + + return buffer; +} + +//// LookupLatestAsset /////////////////////////////////////////////////////// +// Does a find in AssetMan for the given sound file and makes sure it's +// copied over to the AssetMan directory, returning the path to said asset. +// Returns true if found, false if not. + +hsBool plBaseSoundEmitterComponent::LookupLatestAsset( const char *waveName, char *retPath, plErrorMsg *errMsg ) +{ + MaxAssInterface* assetMan = GetMaxAssInterface(); + if( assetMan == nil ) + return false; // No AssetMan available + + // Try to find it in assetMan + jvUniqueId assetId; + if (assetMan->FindAssetByFilename(waveName, assetId)) + { + // Get the latest version + char assetPath[MAX_PATH]; + if (!assetMan->GetLatestVersionFile(assetId, assetPath, sizeof(assetPath))) + { + errMsg->Set( true, "SoundEmitter Convert Error", + "Unable to update wave file '%s' because AssetMan was unable to get the latest version. Using local copy instead.", waveName ).Show(); + errMsg->Set( false ); + return false; + } + + // Copy the string over and go + hsStrcpy( retPath, assetPath ); + return true; + } + + return false; +} + +plSoundBuffer *plBaseSoundEmitterComponent::IProcessSourceBuffer( plMaxNode *maxNode, plErrorMsg *errMsg ) +{ + char *fileName = GetSoundFileName( kBaseSound ); + if( fileName == nil ) + return nil; + + plSoundBuffer *srcBuffer = GetSourceBuffer( fileName, maxNode, ICalcSourceBufferFlags() ); + if( srcBuffer == nil ) + { + IShowError( kSrcBufferInvalid, "The file specified for the sound 3D component %s is invalid. " + "This emitter will not be exported.", GetINode()->GetName(), errMsg ); + return nil; + } + + return srcBuffer; +} + +void plBaseSoundEmitterComponent::UpdateSoundFileSelection( void ) +{ + plSoundBuffer *baseBuffer = nil; + + + // Attempt to load the sound in + if( GetSoundFileName( kBaseSound ) == nil ) + { + // Disable this feature by default + fCompPB->SetValue( (ParamID)kSndAllowChannelSelect, 0, 0 ); + } + else + { + if( IAllowStereoFiles() ) + { + // We allow stereo files, so we don't want to allow stereo->mono select + fCompPB->SetValue( (ParamID)kSndAllowChannelSelect, 0, 0 ); + } + else + { + baseBuffer = TRACKED_NEW plSoundBuffer( GetSoundFileName( kBaseSound ) ); + if( baseBuffer != nil && baseBuffer->IsValid() ) + { + // Update our stereo channel selection if necessary + if( baseBuffer->GetHeader().fNumChannels == 1 ) + { + fCompPB->SetValue( (ParamID)kSndAllowChannelSelect, 0, 0 ); + } + else + { + fCompPB->SetValue( (ParamID)kSndAllowChannelSelect, 0, 1 ); + } + } + else + // Disable this feature by default + fCompPB->SetValue( (ParamID)kSndAllowChannelSelect, 0, 0 ); + } + } + + if( baseBuffer != nil ) + delete baseBuffer; +} + +hsScalar plBaseSoundEmitterComponent::GetSoundVolume( void ) const +{ + return IGetDigitalVolume(); +} + +//// UpdateCategories /////////////////////////////////////////////////////////////////////////// +// Loads the given combo box with category selections and sets the ParamID for the category parameter. +// Returns false if there are no categories to choose for this component + +hsBool plBaseSoundEmitterComponent::UpdateCategories( HWND dialogBox, int &categoryID, ParamID ¶mID ) +{ + HWND comboBox = GetDlgItem( dialogBox, IDC_SND_CATEGORY ); + char **cats; + int *catEnums; + int i, currCat, idx, currIdx; + + + // Get our list of cats + if( !IGetCategoryList( cats, catEnums ) ) + return false; + + // We get two categories for this one: Background Music (default) and Ambience + ComboBox_ResetContent( comboBox ); + + currCat = fCompPB->GetInt( (ParamID)kSndCategory ); + currIdx = -1; + + for( i = 0; cats[ i ][ 0 ] != 0; i++ ) + { + idx = ComboBox_AddString( comboBox, cats[ i ] ); + ComboBox_SetItemData( comboBox, idx, catEnums[ i ] ); + if( catEnums[ i ] == currCat ) + currIdx = idx; + } + + if( currIdx != -1 ) + ComboBox_SetCurSel( comboBox, currIdx ); + else + { + // Option not found in our list, reset to a valid option + ComboBox_SetCurSel( comboBox, 0 ); + fCompPB->SetValue( (ParamID)kSndCategory, 0, catEnums[ 0 ] ); + } + + // Return info + paramID = (ParamID)kSndCategory; + categoryID = IDC_SND_CATEGORY; + + return true; +} + +SegmentMap *GetCompWaveSegmentMap(const char *file) +{ + if( file == nil ) + return nil; + + return GetWaveSegmentMap( file, nil ); + +/* + const char *path = plMaxConfig::GetClientPath(); + if (file && path) + { + char fullpath[MAX_PATH]; + sprintf(fullpath, "%sSfx\\%s", path, file); + return GetWaveSegmentMap(fullpath, nil); + } + + return nil; +*/ +} + +//// ISetBaseParameters ///////////////////////////////////////////////////////////////////////// +// Sets up parameters on the given sound based on the common paramblock values + +void plBaseSoundEmitterComponent::ISetBaseParameters( plSound *destSound, plErrorMsg *pErrMsg ) +{ + // Make sure our category is valid before we set it + int i, cat = fCompPB->GetInt( (ParamID)kSndCategory ); + char **cats; + int *catEnums; + + if( IGetCategoryList( cats, catEnums ) ) + { + for( i = 0; cats[ i ][ 0 ] != 0; i++ ) + { + if( catEnums[ i ] == cat ) + break; + } + if( cats[ i ][ 0 ] == 0 ) + cat = catEnums[ 0 ]; + } + destSound->SetType( cat ); + + destSound->SetVolume( IGetDigitalVolume() ); + destSound->SetProperty( plSound::kPropAutoStart, fCompPB->GetInt( (ParamID)kSoundAutoStartCkBx ) ); + IGrabFadeValues( destSound ); + if( fCompPB->GetInt( (ParamID)kSoundLoopCkBx ) ) + { + destSound->SetProperty( plSound::kPropLooping, true ); + + const char *loop = fCompPB->GetStr((ParamID)kSoundLoopName); + if (loop && loop[0] != '\0') + { + SegmentMap *segMap = GetCompWaveSegmentMap( GetSoundFileName( kBaseSound ) ); + if (segMap && segMap->find(loop) != segMap->end()) + { + SegmentSpec *spec = (*segMap)[loop]; +// sound->SetLoopPoints(spec->fStart, spec->fEnd); + } + } + } + else + destSound->SetProperty( plSound::kPropLooping, false ); +} + +//// AddToAnim ////////////////////////////////////////////////////////////////////////////////// +// Support for animated volumes + +hsBool plBaseSoundEmitterComponent::AddToAnim( plAGAnim *anim, plMaxNode *node ) +{ + hsBool result = false; + plController *ctl; + hsControlConverter& cc = hsControlConverter::Instance(); + + hsScalar start, end; + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + { + start = end = -1; + } + else + { + start = anim->GetStart(); + end = anim->GetEnd(); + } + + ctl = cc.MakeScalarController( fCompPB->GetController( (ParamID)kSoundVolumeSlider ), node, start, end ); + if( ctl != nil ) + { + // Better only do this when the sound component is applied to only one object... + if( fIndices.size() != 1 ) + { + delete ctl; + return false; + } + + std::map::iterator i = fIndices.begin(); + plSoundVolumeApplicator *app = TRACKED_NEW plSoundVolumeApplicator( (*i).second ); + app->SetChannelName(node->GetName()); + plAnimComponentBase::SetupCtl( anim, ctl, app, node ); + result = true; + } + + return result; +} + +// Class that accesses the paramblock + +struct indexinfo +{ + indexinfo::indexinfo() { pNode = nil; fIndex = -1; } + plMaxNode* pNode; + int fIndex; +}; + +int GetSoundNameAndIdx(plComponentBase *comp, plMaxNodeBase *node, const char*& name) +{ + int idx = -1; + if( ( comp->ClassID() == SOUND_3D_COMPONENT_ID || + comp->ClassID() == BGND_MUSIC_COMPONENT_ID || + comp->ClassID() == GUI_SOUND_COMPONENT_ID ) && node->CanConvert()) + { + idx = ((plBaseSoundEmitterComponent *)comp)->GetSoundIdx((plMaxNode*)node); + } + if(node->CanConvert()) + name = idx < 0 ? nil : comp->GetINode()->GetName(); + else + name = nil; + return idx; +} + +int plAudioComp::GetSoundModIdx(plComponentBase *comp, plMaxNode *node) +{ + if( comp->ClassID() == SOUND_3D_COMPONENT_ID || + comp->ClassID() == BGND_MUSIC_COMPONENT_ID || + comp->ClassID() == GUI_SOUND_COMPONENT_ID ) + return ((plBaseSoundEmitterComponent *)comp)->GetSoundIdx(node); + return -1; +} + +bool plAudioComp::IsLocalOnly( plComponentBase *comp ) +{ + if( comp->ClassID() == SOUND_3D_COMPONENT_ID || + comp->ClassID() == BGND_MUSIC_COMPONENT_ID || + comp->ClassID() == GUI_SOUND_COMPONENT_ID ) + return ((plBaseSoundEmitterComponent *)comp)->IsLocalOnly() ? true : false; + return false; +} + +bool plAudioComp::IsSoundComponent(plComponentBase *comp) +{ + Class_ID id = comp->ClassID(); + + if( id == SOUND_3D_COMPONENT_ID || + id == BGND_MUSIC_COMPONENT_ID || + id == GUI_SOUND_COMPONENT_ID ) + return true; + + return false; +} + +class plAudioBaseComponentProc : public plLCBoxComponentProc +{ + +protected: + + void IConvertOldVolume( IParamBlock2 *pb ) + { + int oldVol = pb->GetInt( (ParamID)kOldSoundVolumeSlider, 0 ); + if( oldVol != 4999 ) + { + float v = (float)( oldVol - 5000 ) / 5000.f; + pb->SetValue( (ParamID)kNotSoOldSoundVolumeSlider, 0, v ); + pb->SetValue( (ParamID)kOldSoundVolumeSlider, 0, 4999 ); + } + + // Shut up. + float notSoOldV = pb->GetFloat( (ParamID)kNotSoOldSoundVolumeSlider, 0 ); + if( notSoOldV != -1.f ) + { + float d3dValueReally = -5000.f + ( 5000.f * notSoOldV ); + + float ourNewValue = (float)d3dValueReally / 100.f; + + pb->SetValue( (ParamID)kSoundVolumeSlider, 0, ourNewValue ); + pb->SetValue( (ParamID)kNotSoOldSoundVolumeSlider, 0, -1.f ); + } + } + + void IGetNewLocalFileName( plBaseSoundEmitterComponent *soundComponent, plBaseSoundEmitterComponent::WhichSound which ) + { + TCHAR fileName[ MAX_PATH ], dirName[ MAX_PATH ], *name; + + + name = soundComponent->GetSoundFileName( which ); + if( name != nil ) + strcpy( fileName, name ); + else + strcpy( fileName, _T( "" ) ); + + strcpy( dirName, fileName ); + ::PathRemoveFileSpec( dirName ); + + OPENFILENAME ofn = {0}; + ofn.lStructSize = sizeof( OPENFILENAME ); + ofn.hwndOwner = GetCOREInterface()->GetMAXHWnd(); + ofn.lpstrFilter = "WAV Files (*.wav)\0*.wav\0Windows Media Audio Files (*.wma)\0*.wma\0OGG Vorbis Files (*.ogg)\0*.ogg\0"; + ofn.lpstrFile = fileName; + ofn.nMaxFile = sizeof( fileName ); + ofn.lpstrInitialDir = dirName; + ofn.lpstrTitle = "Choose a sound file"; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST; + ofn.lpstrDefExt = "wav"; + + if( GetOpenFileName( &ofn ) ) + { + jvUniqueId emptyId; + soundComponent->SetSoundAssetId( which, emptyId, fileName ); + } + } + + void IUpdateSoundButton( plBaseSoundEmitterComponent *soundComponent, HWND hDlg, int dlgBtnItemToSet, plBaseSoundEmitterComponent::WhichSound which ) + { + ICustButton *custButton; + TCHAR fileName[ MAX_PATH ]; + + + custButton = GetICustButton( GetDlgItem( hDlg, dlgBtnItemToSet ) ); + if( custButton != nil ) + { + TCHAR *origName = soundComponent->GetSoundFileName( which ); + + if( origName != nil && strlen( origName ) > 0 ) + { + strcpy( fileName, origName ); + ::PathStripPath( fileName ); + custButton->SetText( fileName ); + } + else + custButton->SetText( _T( "" ) ); + + ReleaseICustButton( custButton ); + } + + soundComponent->UpdateSoundFileSelection(); + } + + void ISelectSoundFile( plBaseSoundEmitterComponent *soundComponent, HWND hDlg, int dlgBtnItemToSet, plBaseSoundEmitterComponent::WhichSound which ) + { + MaxAssInterface* maxAssInterface = GetMaxAssInterface(); + + // if we have the assetman plug-in, then try to use it, unless shift is held down + if( maxAssInterface && !( GetKeyState( VK_SHIFT ) & 0x8000 ) ) + { + jvUniqueId assetId = soundComponent->GetSoundAssetID(which); + + char fileName[MAX_PATH]; + if (maxAssInterface->OpenSoundDlg(assetId, fileName, MAX_PATH)) + { + // Set asset ID and filename + soundComponent->SetSoundAssetId(which, assetId, fileName); + } + } + else + { + IGetNewLocalFileName( soundComponent, which ); + } + + // Update the button now + if( hDlg != nil ) + IUpdateSoundButton( soundComponent, hDlg, dlgBtnItemToSet, which ); + } + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) + { + plBaseSoundEmitterComponent *soundComp = (plBaseSoundEmitterComponent *)map->GetParamBlock()->GetOwner(); + + + switch( msg ) + { + case WM_INITDIALOG: + CheckDlgButton(hWnd, IDC_SND_TRACKVIEW, soundComp->fAllowUnhide ? BST_CHECKED : BST_UNCHECKED ); + return true; + + case WM_COMMAND: + if( LOWORD( wParam ) == IDC_SND_TRACKVIEW ) + { + soundComp->fAllowUnhide = ( IsDlgButtonChecked( hWnd, IDC_SND_TRACKVIEW ) == BST_CHECKED ); + plComponentShow::Update(); + return true; + } + break; + } + + return false; + } +}; + + +//// Helper accessors and dialog procs //// + +static plSingleCompSelProc gSoundSoftVolumeSelProc( kSndSoftRegion, IDC_COMP_SOUNDREGION_CHOOSE_VOLUME, "Select a soft region to use for the sound" ); + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plSoundSoftVolAccessor : public PBAccessor +{ +public: + void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t ) + { + if( id == kSndSoftRegion ) + { + plBaseSoundEmitterComponent *comp = (plBaseSoundEmitterComponent*)owner; + comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED ); + } + } +}; + +static plSoundSoftVolAccessor gSoundSoftVolAccessor; + + +enum +{ + kSndSharedParams, + kS3DBaseParams, + kS3DSoftVolumeParams, + kSoundFadeParams, + kSndWaveformParams, + kSndEAXParams +}; + +//// Shared ParamBlock for All Sounds /////////////////////////////////////////////////////////// + +#define sSoundSharedPBHeader(s) kSndSharedParams, IDD_COMP_SOUNDBASE, s##, 0, 0, &gSoundCompProc, \ + kSoundFadeParams, IDD_COMP_SOUND_FADEPARAMS, IDS_COMP_SOUNDFADEPARAMS, 0, 0, &gSoundFadeParamsProc + +static ParamBlockDesc2 sSoundSharedPB +( + plComponent::kBlkComp + 2, _T("Sound Shared Params"), 0, nil, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + 2, // Number of rollouts + kSndSharedParams, IDD_COMP_SOUNDBASE, IDS_COMP_SOUNDBASE, 0, 0, nil, + kSoundFadeParams, IDD_COMP_SOUND_FADEPARAMS, IDS_COMP_SOUNDFADEPARAMS, 0, 0, nil, + + // params + + /// Version # (currently 0, won't use until we know everyone has paramblocks with this in it) + kSndVersionCount, _T(""), TYPE_INT, 0, 0, + p_range, 0, 10000, + p_default, 0, + end, + + kSoundFileName, _T("fileName"), TYPE_STRING, 0, 0, + end, + + kSoundAutoStartCkBx, _T("autoStart"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, kSndSharedParams, TYPE_SINGLECHEKBOX, IDC_COMP_SOUND3D_AUTOSTART_CKBX, + end, + + kSoundLoopCkBx, _T("loop"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, kSndSharedParams, TYPE_SINGLECHEKBOX, IDC_COMP_SOUND3D_LOOPCHKBOX, + end, + kSoundLoopName, _T("loopName"), TYPE_STRING, 0, 0, + end, + + kSndDisableLOD, _T("disableLOD"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, kSndSharedParams, TYPE_SINGLECHEKBOX, IDC_COMP_SOUND_DISABLELOD, + end, + + kSoundVolumeSlider, _T("volume"), TYPE_FLOAT, P_ANIMATABLE, IDS_SND_VOLUME, + p_ui, kSndSharedParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_COMP_SOUND3D_SLIDERVIEWER, IDC_COMP_SOUND3D_VOLSLIDER, 4, + p_range, -48.0f, 0.f, + p_default, 0.f, + end, + + kNotSoOldSoundVolumeSlider, _T(""), TYPE_FLOAT, 0, 0, + end, + + kOldSoundVolumeSlider, _T(""), TYPE_INT, 0, 0, + end, + + kSndCategory, _T("category"), TYPE_INT, 0, 0, + p_range, plSound::kStartType, plSound::kNumTypes - 1, + p_default, plSound::kSoundFX, + end, + + /// Fade Parameters rollout + kSndFadeInEnable, _T("fadeInEnable"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, kSoundFadeParams, TYPE_SINGLECHEKBOX, IDC_SOUND3D_INENABLE, + end, + + kSndFadeInType, _T("fadeInType"), TYPE_INT, 0, 0, + p_default, 0, + end, + + kSndFadeInLength, _T("fadeInLength"), TYPE_FLOAT, 0,0, + p_ui, kSoundFadeParams, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_SOUND3D_INLENGTH, IDC_SOUND3D_INLENGTHSPIN, 1.0f, + p_default, 1.f, + end, + + kSndFadeOutEnable, _T("fadeOutEnable"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, kSoundFadeParams, TYPE_SINGLECHEKBOX, IDC_SOUND3D_OUTENABLE, + end, + + kSndFadeOutType, _T("fadeOutType"), TYPE_INT, 0, 0, + p_default, 0, + end, + + kSndFadeOutLength, _T("fadeOutLength"), TYPE_FLOAT, 0,0, + p_ui, kSoundFadeParams, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_SOUND3D_OUTLENGTH, IDC_SOUND3D_OUTLENGTHSPIN, 1.0f, + p_default, 1.f, + end, + + end +); + +//// ParamBlock Macros for Waveform Properties Rollout /////////////////////////////////////////// + +#define sSndWaveformPropsHeader kSndWaveformParams, IDD_COMP_SOUNDSRC, IDS_COMP_SOUNDSRC, 0, 0, NULL + +#define sSndWaveformPropsParamTemplate \ + \ + kSndAllowChannelSelect, _T( "allowChannelSelect" ), TYPE_BOOL, 0, 0, \ + p_default, 0, \ + p_ui, kSndWaveformParams, TYPE_SINGLECHEKBOX, IDC_SND_ISSTEREO_HIDDEN, \ + p_enable_ctrls, 1, kSndChannelSelect, \ + end, \ + \ + /* Channel select for stereo sources */ \ + kSndChannelSelect, _T( "sourceChannel" ), TYPE_INT, 0, 0, \ + p_ui, kSndWaveformParams, TYPE_RADIO, 2, IDC_SND_CHANSRC1, IDC_SND_CHANSRC2, \ + p_default, 0, \ + end, \ + \ + kSndPriority, _T("sndPriority"), TYPE_INT, 0, 0, \ + p_range, 0, 10, \ + p_ui, kSndWaveformParams, TYPE_SPINNER, EDITTYPE_INT, IDC_SND_PRIORITY, IDC_SND_PRIORITY_SPIN, 1.f, \ + p_default, 0, \ + end + +//// Enums Source EAX Properties Rollout //////////////////////////////////////////////////////// + +enum EAXRefs +{ + kEAXEnabled = 128, + kEAXRoom, + kEAXRoomHF, + kEAXRoomAuto, + kEAXRoomHFAuto, + kEAXOutsideVolHF, + kEAXAirAbsorptionFact, + kEAXRoomRolloffFact, + kEAXDopplerFact, + kEAXRolloffFact, + + kEAXEnableOcclusion, + kEAXOcclusionRegion, + kEAXStartOcclusion, + kEAXStartOcclusionLFRatio, + kEAXStartOcclusionRoomRatio, + kEAXStartOcclusionDirectRatio, + kEAXEndOcclusion, + kEAXEndOcclusionLFRatio, + kEAXEndOcclusionRoomRatio, + kEAXEndOcclusionDirectRatio, + kEAXWhichOccSwapped, + kEAXTempOcclusion, + kEAXTempOcclusionLFRatio, + kEAXTempOcclusionRoomRatio, + kEAXTempOcclusionDirectRatio, + kEAXTempOccSwapper +}; + +//// DialogProc for Source EAX Properties Rollout /////////////////////////////////////////////// + +class plEAXPropsDlgProc : public plSingleCompSelProc +{ + IParamBlock2 *fLastBlockSwapped; + + void ISwapOutOcclusion( IParamBlock2 *pb ) + { + if( pb == nil ) + return; + + if( pb->GetInt( (ParamID)kEAXWhichOccSwapped ) == 0 ) + { + // Swap out to start values + pb->SetValue( (ParamID)kEAXStartOcclusion, 0, pb->GetInt( (ParamID)kEAXTempOcclusion ) ); + pb->SetValue( (ParamID)kEAXStartOcclusionLFRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionLFRatio ) ); + pb->SetValue( (ParamID)kEAXStartOcclusionRoomRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionRoomRatio ) ); + pb->SetValue( (ParamID)kEAXStartOcclusionDirectRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionDirectRatio ) ); + } + else if( pb->GetInt( (ParamID)kEAXWhichOccSwapped ) == 1 ) + { + // Swap out to end values + pb->SetValue( (ParamID)kEAXEndOcclusion, 0, pb->GetInt( (ParamID)kEAXTempOcclusion ) ); + pb->SetValue( (ParamID)kEAXEndOcclusionLFRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionLFRatio ) ); + pb->SetValue( (ParamID)kEAXEndOcclusionRoomRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionRoomRatio ) ); + pb->SetValue( (ParamID)kEAXEndOcclusionDirectRatio, 0, pb->GetFloat( (ParamID)kEAXTempOcclusionDirectRatio ) ); + } + + // Set to "none swapped" + pb->SetValue( (ParamID)kEAXWhichOccSwapped, 0, (int)-1 ); + + fLastBlockSwapped = nil; + } + + void ISwapInOcclusion( IParamBlock2 *pb, int which ) + { + if( pb == nil ) + return; + + if( which == 0 ) + { + // Swap in from start values + pb->SetValue( (ParamID)kEAXTempOcclusion, 0, pb->GetInt( (ParamID)kEAXStartOcclusion ) ); + pb->SetValue( (ParamID)kEAXTempOcclusionLFRatio, 0, pb->GetFloat( (ParamID)kEAXStartOcclusionLFRatio ) ); + pb->SetValue( (ParamID)kEAXTempOcclusionRoomRatio, 0, pb->GetFloat( (ParamID)kEAXStartOcclusionRoomRatio ) ); + pb->SetValue( (ParamID)kEAXTempOcclusionDirectRatio, 0, pb->GetFloat( (ParamID)kEAXStartOcclusionDirectRatio ) ); + } + else + { + // Swap in from end values + pb->SetValue( (ParamID)kEAXTempOcclusion, 0, pb->GetInt( (ParamID)kEAXEndOcclusion ) ); + pb->SetValue( (ParamID)kEAXTempOcclusionLFRatio, 0, pb->GetFloat( (ParamID)kEAXEndOcclusionLFRatio ) ); + pb->SetValue( (ParamID)kEAXTempOcclusionRoomRatio, 0, pb->GetFloat( (ParamID)kEAXEndOcclusionRoomRatio ) ); + pb->SetValue( (ParamID)kEAXTempOcclusionDirectRatio, 0, pb->GetFloat( (ParamID)kEAXEndOcclusionDirectRatio ) ); + } + + pb->SetValue( (ParamID)kEAXWhichOccSwapped, 0, (int)which ); + if( pb->GetMap() != nil ) + pb->GetMap()->UpdateUI( 0 ); + + fLastBlockSwapped = pb; + } + + class plOccPreset + { + public: + char *fName; + Int16 fOcc; + float fLFRatio; + float fRoomRatio; + }; + + plOccPreset fPresets[ 9 ]; + + void ILoadOccPresets( HWND hDlg ) + { + HWND combo = GetDlgItem( hDlg, IDC_EAX_OCCPRESET ); + int i; + + + ComboBox_ResetContent( combo ); + for( i = 0; i < 9; i++ ) + ComboBox_AddString( combo, fPresets[ i ].fName ); + + ComboBox_SetCurSel( combo, 0 ); + } + +public: + + void FlushSwappedPBs( void ) + { + if( fLastBlockSwapped != nil ) + ISwapOutOcclusion( fLastBlockSwapped ); + } + + plEAXPropsDlgProc() : plSingleCompSelProc( kEAXOcclusionRegion, IDC_EAX_OCCREGION, "Select the soft region to blend these EAX occlusion properties" ) + { + int i; + + // Annoyingly, the EAX headers don't have a convenient array, just some #defines + static char occNames[][ 64 ] = { "Single window", "Double window", "Thin door", "Thick door", + "Wood wall", "Brick wall", "Stone wall", "Curtain" }; + Int16 occValues[] = { EAX_MATERIAL_SINGLEWINDOW, EAX_MATERIAL_DOUBLEWINDOW, EAX_MATERIAL_THINDOOR, + EAX_MATERIAL_THICKDOOR, EAX_MATERIAL_WOODWALL, EAX_MATERIAL_BRICKWALL, + EAX_MATERIAL_STONEWALL, EAX_MATERIAL_CURTAIN }; + float occLFValues[] = { EAX_MATERIAL_SINGLEWINDOWLF, EAX_MATERIAL_DOUBLEWINDOWLF, EAX_MATERIAL_THINDOORLF, + EAX_MATERIAL_THICKDOORLF, EAX_MATERIAL_WOODWALLLF, EAX_MATERIAL_BRICKWALLLF, + EAX_MATERIAL_STONEWALLLF, EAX_MATERIAL_CURTAINLF }; + Int16 occRoomValues[] = { EAX_MATERIAL_SINGLEWINDOWROOMRATIO, EAX_MATERIAL_DOUBLEWINDOWROOMRATIO, EAX_MATERIAL_THINDOORROOMRATIO, + EAX_MATERIAL_THICKDOORROOMRATIO, EAX_MATERIAL_WOODWALLROOMRATIO, EAX_MATERIAL_BRICKWALLROOMRATIO, + EAX_MATERIAL_STONEWALLROOMRATIO, EAX_MATERIAL_CURTAINROOMRATIO }; + + for( i = 1; i < 9; i++ ) + { + fPresets[ i ].fName = occNames[ i - 1 ]; + fPresets[ i ].fOcc = occValues[ i - 1 ]; + fPresets[ i ].fLFRatio = occLFValues[ i - 1 ]; + fPresets[ i ].fRoomRatio = occRoomValues[ i - 1 ]; + } + fPresets[ 0 ].fName = "None"; + fPresets[ 0 ].fOcc = 0; + fPresets[ 0 ].fLFRatio = 0.25f; + fPresets[ 0 ].fRoomRatio = 1.5f; + } + + void DeleteThis() {} + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IParamBlock2 *pblock = map->GetParamBlock(); + + switch( msg ) + { + case WM_INITDIALOG: + pblock->SetValue( (ParamID)kEAXTempOccSwapper, 0, (int)0 ); + ISwapInOcclusion( pblock, 0 ); + ILoadOccPresets( hWnd ); + break; + + case WM_DESTROY: + ISwapOutOcclusion( pblock ); + return 0; + + case WM_SHOWWINDOW: + if( wParam ) + { + pblock->SetValue( (ParamID)kEAXTempOccSwapper, 0, (int)0 ); + ISwapInOcclusion( pblock, 0 ); + } + else + ISwapOutOcclusion( pblock ); + return 0; + + case WM_COMMAND: + if( LOWORD( wParam ) == IDC_EAX_STARTOCC || LOWORD( wParam ) == IDC_EAX_ENDOCC ) + { + // Our radio button to switch between start and end values was hit. So swap out the values + // from the temp ones + ISwapOutOcclusion( pblock ); + ISwapInOcclusion( pblock, ( LOWORD( wParam ) == IDC_EAX_STARTOCC ) ? 0 : 1 ); + return true; + } + else if( LOWORD( wParam ) == IDC_EAX_OCCPRESET && HIWORD( wParam ) == CBN_SELCHANGE ) + { + HWND combo = GetDlgItem( hWnd, IDC_EAX_OCCPRESET ); + int idx = ComboBox_GetCurSel( combo ); + if( idx != CB_ERR ) + { + // Load from presets + pblock->SetValue( (ParamID)kEAXTempOcclusion, 0, (int)fPresets[ idx ].fOcc ); + pblock->SetValue( (ParamID)kEAXTempOcclusionLFRatio, 0, fPresets[ idx ].fLFRatio ); + pblock->SetValue( (ParamID)kEAXTempOcclusionRoomRatio, 0, fPresets[ idx ].fRoomRatio ); + } + return true; + } + break; + } + return plSingleCompSelProc::DlgProc( t, map, hWnd, msg, wParam, lParam ); + } + +}; + +static plEAXPropsDlgProc sEAXPropsDlgProc; + +//// ParamBlock for Source EAX Properties Rollout /////////////////////////////////////////////// +// Note: we can't make this a real ParamBlock and do P_INCLUDE_PARAMS because, in Discreet's +// amazing method of doing things, we can't INCLUDE more than one ParamBlock in any other PB. +// So either we chain them together here (and thus make them dependent on one another, which +// is lame) or we just make the whole damned thing a #define, which is all P_INCLUDE_PARAMS +// really does anyway. + +#define sSndEAXPropsParamHeader kSndEAXParams, IDD_COMP_EAXBUFFER, IDS_COMP_EAXBUFFER, 0, APPENDROLL_CLOSED, &sEAXPropsDlgProc +//static ParamBlockDesc2 sSndEAXPropsParamTemplate +//( + /// Main def +// plComponent::kBlkComp + 1, _T("sndEAXProps"), 0, nil, P_AUTO_UI + P_MULTIMAP + P_AUTO_CONSTRUCT, plComponent::kRefComp, + +// 1, +// kSndEAXParams, IDD_COMP_EAXBUFFER, IDS_COMP_EAXBUFFER, 0, 0, nil, + +#define sSndEAXPropsParamTemplate \ + \ + kEAXEnabled, _T("eaxEnabled"), TYPE_BOOL, 0, 0, \ + p_default, 0, \ + p_ui, kSndEAXParams, TYPE_SINGLECHEKBOX, IDC_EAX_ENABLE, \ + p_enable_ctrls, 10, kEAXRoom, kEAXRoomHF, kEAXRoomAuto, kEAXRoomHFAuto, \ + kEAXOutsideVolHF, kEAXAirAbsorptionFact, kEAXRoomRolloffFact, kEAXDopplerFact, kEAXRolloffFact, \ + kEAXEnableOcclusion, \ + end, \ + \ + kEAXRoom, _T("eaxRoom"), TYPE_INT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_INT, IDC_EAX_ROOM_EDIT, IDC_EAX_ROOM, 8, \ + p_range, -10000, 1000, \ + p_default, 0, \ + end, \ + \ + kEAXRoomHF, _T("eaxRoomHF"), TYPE_INT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_INT, IDC_EAX_ROOMHF_EDIT, IDC_EAX_ROOMHF, 8, \ + p_range, -10000, 0, \ + p_default, 0, \ + end, \ + \ + kEAXRoomAuto, _T( "eaxRoomAuto" ), TYPE_BOOL, 0, 0, \ + p_default, 1, \ + p_ui, kSndEAXParams, TYPE_SINGLECHEKBOX, IDC_EAX_ROOMAUTO, \ + end, \ + \ + kEAXRoomHFAuto, _T( "eaxRoomHFAuto" ), TYPE_BOOL, 0, 0, \ + p_default, 1, \ + p_ui, kSndEAXParams, TYPE_SINGLECHEKBOX, IDC_EAX_ROOMHFAUTO, \ + end, \ + \ + kEAXOutsideVolHF, _T("eaxOutsideVolHF"), TYPE_INT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_INT, IDC_EAX_OUTSIDEVOLHF_EDIT, IDC_EAX_OUTSIDEVOLHF, 8, \ + p_range, -10000, 0, \ + p_default, 0, \ + end, \ + \ + kEAXAirAbsorptionFact, _T("eaxAirAbsorptionFact"), TYPE_FLOAT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_AIRABSORPTFACT_EDIT, IDC_EAX_AIRABSORPTFACT, 8, \ + p_range, 0.f, 10.f, \ + p_default, 1.f, \ + end, \ + \ + kEAXRoomRolloffFact, _T("eaxRoomRolloffFact"), TYPE_FLOAT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_ROOMROLLOFFFACT_EDIT, IDC_EAX_ROOMROLLOFFFACT, 8, \ + p_range, 0.f, 10.f, \ + p_default, 0.f, \ + end, \ + \ + kEAXDopplerFact, _T("eaxDopplerFact"), TYPE_FLOAT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_DOPPLERFACT_EDIT, IDC_EAX_DOPPLERFACT, 8, \ + p_range, 0.f, 10.f, \ + p_default, 0.f, \ + end, \ + \ + kEAXRolloffFact, _T("eaxRolloffFact"), TYPE_FLOAT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_ROLLOFFFACT_EDIT, IDC_EAX_ROLLOFFFACT, 8, \ + p_range, 0.f, 10.f, \ + p_default, 0.f, \ + end, \ + \ + kEAXEnableOcclusion, _T("eaxEnableOcclusion"), TYPE_BOOL, 0, 0, \ + p_default, 0, \ + p_ui, kSndEAXParams, TYPE_SINGLECHEKBOX, IDC_EAX_ENABLEOCCLUSION, \ + p_enable_ctrls, 6, kEAXOcclusionRegion, kEAXTempOcclusion, kEAXTempOcclusionLFRatio, kEAXTempOcclusionRoomRatio, \ + kEAXTempOcclusionDirectRatio, kEAXTempOccSwapper, \ + end, \ + \ + kEAXOcclusionRegion, _T("eaxOcclusionRegion"), TYPE_INODE, 0, 0, \ + end, \ + \ + kEAXStartOcclusion, _T("eaxStartOcclusion"), TYPE_INT, 0, 0, \ + p_range, -10000, 0, p_default, 0, \ + end, \ + \ + kEAXStartOcclusionLFRatio, _T("eaxStartOccLFRatio"), TYPE_FLOAT, 0, 0, \ + p_range, 0.f, 1.f, p_default, 0.25f, \ + end, \ + \ + kEAXStartOcclusionRoomRatio, _T("eaxStartOccRoomRatio"), TYPE_FLOAT, 0, 0, \ + p_range, 0.f, 10.f, p_default, 1.5f, \ + end, \ + \ + kEAXStartOcclusionDirectRatio, _T("eaxStartOccDirectRatio"), TYPE_FLOAT, 0, 0, \ + p_range, 0.f, 10.f, p_default, 1.0f, \ + end, \ + \ + kEAXEndOcclusion, _T("eaxEndOcclusion"), TYPE_INT, 0, 0, \ + p_range, -10000, 0, p_default, 0, \ + end, \ + \ + kEAXEndOcclusionLFRatio, _T("eaxEndOccLFRatio"), TYPE_FLOAT, 0, 0, \ + p_range, 0.f, 1.f, p_default, 0.25f, \ + end, \ + \ + kEAXEndOcclusionRoomRatio, _T("eaxEndOccRoomRatio"), TYPE_FLOAT, 0, 0, \ + p_range, 0.f, 10.f, p_default, 1.5f, \ + end, \ + \ + kEAXEndOcclusionDirectRatio, _T("eaxEndOccDirectRatio"), TYPE_FLOAT, 0, 0, \ + p_range, 0.f, 10.f, p_default, 1.0f, \ + end, \ + \ + kEAXWhichOccSwapped, _T("eaxWhichOccSwapped"), TYPE_INT, 0, 0, \ + end, \ + \ + kEAXTempOccSwapper, _T("eaxOccSwapper"), TYPE_INT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_RADIO, 2, IDC_EAX_STARTOCC, IDC_EAX_ENDOCC, \ + p_default, 0, \ + end, \ + \ + kEAXTempOcclusion, _T("eaxTempOcclusion"), TYPE_INT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_INT, IDC_EAX_OCCLUSION_EDIT, IDC_EAX_OCCLUSION, 8, \ + p_range, -10000, 0, p_default, 0, \ + end, \ + \ + kEAXTempOcclusionLFRatio, _T("eaxTempOccLFRatio"), TYPE_FLOAT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_OCCLFRATIO_EDIT, IDC_EAX_OCCLFRATIO, 8, \ + p_range, 0.f, 1.f, p_default, 0.25f, \ + end, \ + \ + kEAXTempOcclusionRoomRatio, _T("eaxTempOccRoomRatio"), TYPE_FLOAT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_OCCROOMRATIO_EDIT, IDC_EAX_OCCROOMRATIO, 8, \ + p_range, 0.f, 10.f, p_default, 1.5f, \ + end, \ + \ + kEAXTempOcclusionDirectRatio, _T("eaxTempOccDirectRatio"), TYPE_FLOAT, 0, 0, \ + p_ui, kSndEAXParams, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_EAX_OCCDIRECTRATIO_EDIT, IDC_EAX_OCCDIRECTRATIO, 8, \ + p_range, 0.f, 10.f, p_default, 1.0f, \ + end + +// , end +//); + + +void plBaseSoundEmitterComponent::IGrabEAXParams( plSound *sound, plErrorMsg *pErrMsg ) +{ + sEAXPropsDlgProc.FlushSwappedPBs(); + + plEAXSourceSettings &settings = sound->GetEAXSettings(); + + if( fCompPB->GetInt( (ParamID)kEAXEnabled ) ) + { + settings.Enable( true ); + settings.SetRoomParams( fCompPB->GetInt( (ParamID)kEAXRoom ), fCompPB->GetInt( (ParamID)kEAXRoomHF ), + fCompPB->GetInt( (ParamID)kEAXRoomAuto ), fCompPB->GetInt( (ParamID)kEAXRoomHFAuto ) ); + settings.SetOutsideVolHF( fCompPB->GetInt( (ParamID)kEAXOutsideVolHF ) ); + settings.SetFactors( fCompPB->GetFloat( (ParamID)kEAXAirAbsorptionFact ), + fCompPB->GetFloat( (ParamID)kEAXRoomRolloffFact ), + fCompPB->GetFloat( (ParamID)kEAXDopplerFact ), + fCompPB->GetFloat( (ParamID)kEAXRolloffFact ) ); + + if( fCompPB->GetInt( (ParamID)kEAXEnableOcclusion ) ) + { + settings.GetSoftStarts().SetOcclusion( fCompPB->GetInt( (ParamID)kEAXStartOcclusion ), + fCompPB->GetFloat( (ParamID)kEAXStartOcclusionLFRatio ), + fCompPB->GetFloat( (ParamID)kEAXStartOcclusionRoomRatio ), + fCompPB->GetFloat( (ParamID)kEAXStartOcclusionDirectRatio ) ); + settings.GetSoftEnds().SetOcclusion( fCompPB->GetInt( (ParamID)kEAXEndOcclusion ), + fCompPB->GetFloat( (ParamID)kEAXEndOcclusionLFRatio ), + fCompPB->GetFloat( (ParamID)kEAXEndOcclusionRoomRatio ), + fCompPB->GetFloat( (ParamID)kEAXEndOcclusionDirectRatio ) ); + + plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent( fCompPB->GetINode( (ParamID)kEAXOcclusionRegion ) ); + if( softComp != nil ) + { + plKey softKey = softComp->GetSoftVolume(); + if( softKey != nil ) + { + // Make sure we set checkListener on the sucker + plSoftVolume *vol = plSoftVolume::ConvertNoRef( softKey->GetObjectPtr() ); + if( vol != nil ) + { + vol->SetCheckListener(); + hsgResMgr::ResMgr()->AddViaNotify( softKey, TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, 0, plSound::kRefSoftOcclusionRegion ), plRefFlags::kActiveRef ); + } + } + } + else + { + pErrMsg->Set(true, "Sound Emitter Error", "The Sound emitter component %s is checked to use an occlusion soft region, but no soft region is specified. Ignoring setting.", GetINode()->GetName() ).Show(); + pErrMsg->Set(false); + settings.GetSoftStarts().Reset(); + settings.GetSoftEnds().Reset(); + } + } + else + { + settings.GetSoftStarts().Reset(); + settings.GetSoftEnds().Reset(); + } + } + else + settings.Enable( false ); +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// SoundEmitter Component +// +// + +/* +class plBaseComponentProc : public ParamMap2UserDlgProc +{ +protected: + void ILoadComboBox(HWND hComboBox, const char *names[]) + { + SendMessage(hComboBox, CB_RESETCONTENT, 0, 0); + for (int i = 0; names[i]; i++) + SendMessage(hComboBox, CB_ADDSTRING, 0, (LPARAM)names[i]); + SendMessage(hComboBox, CB_SETCURSEL, 0, 0); + } + + void ILoadListBox(HWND hListBox, IParamBlock2 *pb, int param, const char *names[]) + { + SendMessage(hListBox, LB_RESETCONTENT, 0, 0); + for (int i = 0; i < pb->Count(param); i++) + { + int idx = pb->GetInt(param, 0, i); + SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)names[idx]); + } + } + + void IConvertOldVolume( IParamBlock2 *pb ) + { + int oldVol = pb->GetInt( kOldSoundVolumeSlider, 0 ); + if( oldVol != 4999 ) + { + float v = (float)( oldVol - 5000 ) / 5000.f; + pb->SetValue( kSoundVolumeSlider, 0, v ); + pb->SetValue( kOldSoundVolumeSlider, 0, 4999 ); + } + } + +}; +*/ +class plSound3DEmitterComponent : public plBaseSoundEmitterComponent +{ +public: + plSound3DEmitterComponent(); + virtual ~plSound3DEmitterComponent(); + + // Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual hsBool IsLocalOnly( void ) const { if( fCompPB->GetInt( (ParamID)kSndIsLocalOnly ) ) return true; else return false; } + + + virtual hsBool ConvertGrouped( plMaxNode *baseNode, hsTArray &groupArray, plErrorMsg *pErrMsg ); + +protected: + + bool IValidate(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual hsBool IAllowStereoFiles( void ) const { return false; } + + void ISetParameters( plWin32Sound *destSound, plErrorMsg *pErrMsg ); + + virtual hsBool IGetCategoryList( char **&catList, int *&catKonstantList ); +}; + +class plSoundComponentProc : public plAudioBaseComponentProc +{ + hsBool fHandleCategory; + int fCategoryCtrlID; + ParamID fCategoryParamID; + +public: + void DeleteThis() {} + + void ILoadLoops(HWND hLoop, IParamBlock2 *pb) + { + SendMessage(hLoop, CB_RESETCONTENT, 0, 0); + SendMessage(hLoop, CB_ADDSTRING, 0, (LPARAM)"(Entire Sound)"); + + const char *loop = pb->GetStr(kSoundLoopName); + if (!loop) + loop = ""; + + SegmentMap *segMap = GetCompWaveSegmentMap(pb->GetStr(kSoundFileName)); + + if (segMap) + { + for (SegmentMap::iterator it = segMap->begin(); it != segMap->end(); it++) + { + SegmentSpec *spec = it->second; + int idx = SendMessage(hLoop, CB_ADDSTRING, 0, (LPARAM)spec->fName); + SendMessage(hLoop, CB_SETITEMDATA, idx, 1); + + if (!strcmp(spec->fName, loop)) + SendMessage(hLoop, CB_SETCURSEL, idx, 0); + } + + + DeleteSegmentMap(segMap); + } + + if (SendMessage(hLoop, CB_GETCURSEL, 0, 0) == CB_ERR) + SendMessage(hLoop, CB_SETCURSEL, 0, 0); + } + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch( msg ) + { + case WM_INITDIALOG: + { + IParamBlock2 *pblock = map->GetParamBlock(); + + plSound3DEmitterComponent *soundComp = (plSound3DEmitterComponent *)map->GetParamBlock()->GetOwner(); + hsAssert( soundComp != nil, "Problem trying to select a sound file" ); + + IUpdateSoundButton( soundComp, hWnd, IDC_COMP_SOUND3D_FILENAME_BTN, plBaseSoundEmitterComponent::kBaseSound ); + + IConvertOldVolume( pblock ); + + fHandleCategory = soundComp->UpdateCategories( hWnd, fCategoryCtrlID, fCategoryParamID ); + } + + { + ILoadLoops(GetDlgItem(hWnd, IDC_LOOP_COMBO), map->GetParamBlock()); + #if 0 + map->SetTooltip(kSoundFileName, true, _T("A sound file name.")); + map->SetTooltip(kLoopBegin, true, _T("The distance, in feet, at which the sound begins to be less audible.")); + map->SetTooltip(kLoopEnd, true, _T("The distance, in feet, at which the sound is no longer audible.")); + map->SetTooltip(kSoundLoopSegBeg2, true, _T("The distance, in feet, at which the sound begins to be less audible.")); + map->SetTooltip(kSoundLoopSegEnd2, true, _T("The distance, in feet, at which the sound is no longer audible.")); + map->SetTooltip(kSoundAutoStartCkBx, true, _T("Check to play the sound file upon game start.")); + map->SetTooltip(kSoundLoopSegBegDDList, true, _T("The time, keyframe or percentage at which looping is to begin.")); + map->SetTooltip(kSoundLoopSegEndDDList, true, _T("The time, keyframe or percentage at which looping is to end.")); + #endif + + + break; + } + + case WM_COMMAND: + if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_LOOP_COMBO) + { + int idx = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); + if (idx == CB_ERR || SendMessage((HWND)lParam, CB_GETITEMDATA, idx, 0) == 0) + map->GetParamBlock()->SetValue((ParamID)kSoundLoopName, 0, ""); + else + { + char buf[256]; + SendMessage((HWND)lParam, CB_GETLBTEXT, idx, (LPARAM)buf); + map->GetParamBlock()->SetValue((ParamID)kSoundLoopName, 0, buf); + } + return true; + } + else if( LOWORD( wParam ) == IDC_COMP_SOUND3D_FILENAME_BTN ) + { + plSound3DEmitterComponent *soundComp = (plSound3DEmitterComponent *)map->GetParamBlock()->GetOwner(); + hsAssert( soundComp != nil, "Problem trying to select a sound file" ); + + ISelectSoundFile( soundComp, hWnd, IDC_COMP_SOUND3D_FILENAME_BTN, plBaseSoundEmitterComponent::kBaseSound ); + } + else if( fHandleCategory && LOWORD( wParam ) == fCategoryCtrlID ) + { + HWND ctrl = GetDlgItem( hWnd, fCategoryCtrlID ); + int idx = ComboBox_GetCurSel( ctrl ); + if( idx != CB_ERR ) + { + int cat = ComboBox_GetItemData( ctrl, idx ); + map->GetParamBlock()->SetValue( (ParamID)fCategoryParamID, 0, cat ); + } + else + map->GetParamBlock()->SetValue( (ParamID)fCategoryParamID, 0, (int)0 ); + } + break; + } + + return plAudioBaseComponentProc::DlgProc( t, map, hWnd, msg, wParam, lParam ); + } + +}; + +class plSoundFadeParamsDlgProc : public plAudioBaseComponentProc +{ + public: + void DeleteThis() {} + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) + { + const char *types[] = { "Linear", "Logarithmic", "Exponential", NULL }; + IParamBlock2 *pb = map->GetParamBlock(); + BOOL enable; + + + switch( msg ) + { + case WM_INITDIALOG: + // Load fade types + + ILoadComboBox( GetDlgItem( hWnd, IDC_SOUND3D_INTYPE ), types ); + ILoadComboBox( GetDlgItem( hWnd, IDC_SOUND3D_OUTTYPE ), types ); + + SendDlgItemMessage( hWnd, IDC_SOUND3D_INTYPE, CB_SETCURSEL, (WPARAM)pb->GetInt( (ParamID)kSndFadeInType, 0 ), 0 ); + SendDlgItemMessage( hWnd, IDC_SOUND3D_OUTTYPE, CB_SETCURSEL, (WPARAM)pb->GetInt( (ParamID)kSndFadeOutType, 0 ), 0 ); + + enable = pb->GetInt( (ParamID)kSndFadeInEnable, 0 ) ? TRUE : FALSE; + EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_INTYPE ), enable ); + EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_INLENGTH ), enable ); + + enable = pb->GetInt( (ParamID)kSndFadeOutEnable, 0 ) ? TRUE : FALSE; + EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_OUTTYPE ), enable ); + EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_OUTLENGTH ), enable ); + + break; + + case WM_COMMAND: + if( HIWORD( wParam ) == CBN_SELCHANGE ) + { + if( LOWORD( wParam ) == IDC_SOUND3D_INTYPE ) + pb->SetValue( (ParamID)kSndFadeInType, 0, (int)SendDlgItemMessage( hWnd, IDC_SOUND3D_INTYPE, CB_GETCURSEL, 0, 0 ) ); + else if( LOWORD( wParam ) == IDC_SOUND3D_OUTTYPE ) + pb->SetValue( (ParamID)kSndFadeOutType, 0, (int)SendDlgItemMessage( hWnd, IDC_SOUND3D_OUTTYPE, CB_GETCURSEL, 0, 0 ) ); + } + + else if( LOWORD( wParam ) == IDC_SOUND3D_INENABLE ) + { + // Enable/disable controls manually + enable = pb->GetInt( (ParamID)kSndFadeInEnable, 0 ) ? TRUE : FALSE; + EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_INTYPE ), enable ); + EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_INLENGTH ), enable ); + } + else if( LOWORD( wParam ) == IDC_SOUND3D_OUTENABLE ) + { + // Enable/disable controls manually + enable = pb->GetInt( (ParamID)kSndFadeOutEnable, 0 ) ? TRUE : FALSE; + EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_OUTTYPE ), enable ); + EnableWindow( GetDlgItem( hWnd, IDC_SOUND3D_OUTLENGTH ), enable ); + } + break; + + } + + return false; + } +}; + +// For the paramblock below. +static plSoundComponentProc gSoundCompProc; +static plSoundFadeParamsDlgProc gSoundFadeParamsProc; + +//Max desc stuff necessary below. +CLASS_DESC(plSound3DEmitterComponent, gSound3DEmitterDesc, "Sound 3D", "Sound3D", COMP_TYPE_AUDIO, SOUND_3D_COMPONENT_ID) + +ParamBlockDesc2 gSound3DEmitterBk +( + plComponent::kBlkComp, _T("3D Sound"), 0, &gSound3DEmitterDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 6, // Number of rollouts + sSoundSharedPBHeader(IDS_COMP_SOUNDBASE), + kS3DBaseParams, IDD_COMP_SOUND3D, IDS_COMP_SOUND3DS, 0, 0, &gSoundCompProc, + kS3DSoftVolumeParams, IDD_COMP_SOUND_SOFTPARAMS, IDS_COMP_SOUNDSOFTPARAMS, 0, 0, &gSoundSoftVolumeSelProc, + sSndWaveformPropsHeader, + sSndEAXPropsParamHeader, + + // Included paramblock + &sSoundSharedPB, + + // Waveform props define + sSndWaveformPropsParamTemplate, + + // params + + kSndIsLocalOnly, _T("noNetworkSynch"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, kS3DBaseParams, TYPE_SINGLECHEKBOX, IDC_SND_LOCALONLY, + end, + + + kMinFallOffRad, _T("minFalloff"), TYPE_INT, 0, 0, + p_range, 1, 1000000000, + p_default, 1, + p_ui, kS3DBaseParams, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_COMP_SOUND3D_EDIT3, IDC_COMP_SOUND3D_SPIN3, SPIN_AUTOSCALE, + end, + + kMaxFallOffRad, _T("maxFalloff"), TYPE_INT, 0, 0, + p_range, 1, 1000000000, + p_default, 1000000000, + p_ui, kS3DBaseParams, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_COMP_SOUND3D_EDIT4, IDC_COMP_SOUND3D_SPIN4, SPIN_AUTOSCALE, + end, + + kSoundConeBool, _T("SoundCone"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_enable_ctrls, 3, kSoundIConeAngle, kSoundOConeAngle, kSoundOConeVolumeSlider, + p_ui, kS3DBaseParams, TYPE_SINGLECHEKBOX, IDC_COMP_SOUND3D_CONEEFFECT_CKBX, + end, + + + kSoundIConeAngle, _T("insideConeAngle"), TYPE_INT, 0, 0, + p_range, 0, 360, + p_default, 360, + p_ui, kS3DBaseParams, TYPE_SPINNER, EDITTYPE_INT, + IDC_COMP_SOUND3D_ICONEANGLE_EDIT, IDC_COMP_SOUND3D_ICONEANGLE_SPIN, 1.0f, + end, + + kSoundOConeAngle, _T("outsideConeAngle"), TYPE_INT, 0, 0, + p_range, 0, 360, + p_default, 360, + p_ui, kS3DBaseParams, TYPE_SPINNER, EDITTYPE_INT, + IDC_COMP_SOUND3D_OCONEANGLE_EDIT, IDC_COMP_SOUND3D_OCONEANGLE_SPIN, 1.0f, + end, + + kSoundOConeVolumeSlider, _T("outsideConeVolSlider"), TYPE_INT, 0, 0, + p_ui, kS3DBaseParams, TYPE_SLIDER, EDITTYPE_INT, IDC_COMP_SOUND3D_SLIDERVIEWER2, IDC_COMP_SOUND3D_VOLSLIDER2, 4, + p_range, 5000, 10000, + p_default, 5000, + end, + + /// Soft Region/Volume Parameters rollout + kSndSoftRegionEnable, _T( "enableSoftRegion" ), TYPE_BOOL, 0, 0, + p_ui, kS3DSoftVolumeParams, TYPE_SINGLECHEKBOX, IDC_SOUND_SOFTENABLE, + p_default, FALSE, + end, + + kSndSoftRegion, _T("softRegion"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_SOUNDSOFTSELECT, + p_accessor, &gSoundSoftVolAccessor, + end, + + kSndIncidental, _T("isIncidental"), TYPE_INT, 0, 0, + p_default, FALSE, + p_ui, kS3DBaseParams, TYPE_SINGLECHEKBOX, IDC_SND_INCIDENTAL, + end, + + sSndEAXPropsParamTemplate, // it's a #define + + end +); + + +plSound3DEmitterComponent::plSound3DEmitterComponent() +{ + fClassDesc = &gSound3DEmitterDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plSound3DEmitterComponent::~plSound3DEmitterComponent() +{ +} + +//// IGetCategoryList /////////////////////////////////////////////////////////////////////////// +// Returns a list of the categories and konstants supported for this type of sound + +hsBool plSound3DEmitterComponent::IGetCategoryList( char **&catList, int *&catKonstantList ) +{ + static char *cats[] = { "Background Music", "Ambience", "Sound FX", "GUI", "NPC Voice", "" }; + static int catEnums[] = { plSound::kBackgroundMusic, plSound::kAmbience, plSound::kSoundFX, plSound::kGUISound, plSound::kNPCVoices }; + + catList = cats; + catKonstantList = catEnums; + + return true; +} + +// Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plSound3DEmitterComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return plBaseSoundEmitterComponent::SetupProperties( pNode, pErrMsg ); +} + +bool plSound3DEmitterComponent::IValidate(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plBaseSoundEmitterComponent::IValidate( node, pErrMsg ); +} + +hsBool plSound3DEmitterComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plBaseSoundEmitterComponent::PreConvert( node, pErrMsg, SOUND_3D_COMPONENT_ID ); +} + +void plSound3DEmitterComponent::ISetParameters( plWin32Sound *destSound, plErrorMsg *pErrMsg ) +{ + ISetBaseParameters( destSound, pErrMsg ); + + int min = fCompPB->GetInt( (ParamID)kMinFallOffRad ); + int max = fCompPB->GetInt( (ParamID)kMaxFallOffRad ); + float Vol = IGetDigitalVolume(); + + int OutVol, innerCone, outerCone; + + if( fCompPB->GetInt( (ParamID)kSoundConeBool ) ) + { + OutVol = fCompPB->GetInt( (ParamID)kSoundOConeVolumeSlider ); + innerCone = fCompPB->GetInt( (ParamID)kSoundIConeAngle ); + outerCone = fCompPB->GetInt( (ParamID)kSoundOConeAngle ); + } + else + { + OutVol = 0; + innerCone = 360; + outerCone = 360; + } + + destSound->SetMax(max); + destSound->SetMin(min); + destSound->SetOuterVolume(OutVol - 10000); + destSound->SetConeAngles(innerCone, outerCone); + destSound->SetProperty( plSound::kPropLocalOnly, fCompPB->GetInt( (ParamID)kSndIsLocalOnly ) ? true : false ); + destSound->SetPriority( fCompPB->GetInt( (ParamID)kSndPriority ) ); + + if( fCompPB->GetInt( (ParamID)kSndIncidental ) ) + { + destSound->SetProperty( plSound::kPropIncidental, true ); + + // Refactor the priority, since incidental priorities are a different range + int pri = fCompPB->GetInt( (ParamID)kSndPriority ); + pri = pri < 1 ? 1 : pri; + destSound->SetPriority( pri ); + } + + if( fCompPB->GetInt( (ParamID)kSndDisableLOD ) ) + { + // Force LOD off on this sound + destSound->SetProperty( plSound::kPropDisableLOD, true ); + } + + if( fCompPB->GetInt( (ParamID)kSndChannelSelect ) ) + destSound->SetChannelSelect( plWin32Sound::kRightChannel ); + else + destSound->SetChannelSelect( plWin32Sound::kLeftChannel ); + + IGrabSoftRegion( destSound, pErrMsg ); + IGrabEAXParams( destSound, pErrMsg ); +} + +hsBool plSound3DEmitterComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (!fValidNodes[node]) + return false; + + if( fCreateGrouped ) + return true; + + char* fileName = GetSoundFileName( kBaseSound ); + + int fIndex = -1; + if (fIndices.find(node) != fIndices.end()) + fIndex = fIndices[node]; + + plSoundBuffer *srcBuffer = IProcessSourceBuffer( node, pErrMsg ); + if( srcBuffer == nil ) + return false; + + const plAudioInterface* ai = node->GetSceneObject()->GetAudioInterface(); + plWinAudible* pAudible = (plWinAudible*)ai->GetAudible(); + + + char keyName[ 256 ]; + sprintf( keyName, "%s", GetINode()->GetName()); + + plWin32Sound *sound = nil; + + if (!strcmp(node->GetName(), "LinkSoundSource")) + sound = TRACKED_NEW plWin32LinkSound; + else + { +#if 0 + sound = TRACKED_NEW plWin32StaticSound; +#else + /// New method, still in testing: any sounds over 4 seconds get made into streaming sounds + if( srcBuffer->GetDataLengthInSecs() > 4.f ) + sound = TRACKED_NEW plWin32StreamingSound; + else + sound = TRACKED_NEW plWin32StaticSound; + } +#endif + + hsgResMgr::ResMgr()->NewKey(keyName, sound, node->GetLocation(), node->GetLoadMask()); + hsgResMgr::ResMgr()->AddViaNotify( srcBuffer->GetKey(), TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, -1, plSound::kRefDataBuffer ), plRefFlags::kActiveRef ); + + if( pAudible->AddSound( sound, fIndex, true ) ) + { + ISetParameters( sound, pErrMsg ); + } + + return true; +} + +// Converts an array of components into a single grouped sound +hsBool plSound3DEmitterComponent::ConvertGrouped( plMaxNode *baseNode, hsTArray &groupArray, plErrorMsg *pErrMsg ) +{ + char keyName[ 256 ]; + + + if( !fValidNodes[ baseNode ] || !fCreateGrouped ) + return false; + + + // First, we need to grab the sound buffers from ALL the components and merge them into one big buffer. + // Also build up an array of positions to feed to our groupedSound later. + // Also also assign all the components the audioInterface index (will be the same one, so we need to + // allocate it here). + // Also also also build up a volume array parallel to startPoses that represents the individual volume + // setting for each sound in the group + hsTArray startPoses; + hsTArray volumes; + hsLargeArray mergedData; + int i; + plWAVHeader mergedHeader; + + for( i = 0; i < groupArray.GetCount(); i++ ) + { + // Make sure they're all 3D sounds... + if( groupArray[ i ]->ClassID() != SOUND_3D_COMPONENT_ID ) + { + char msg[ 512 ]; + sprintf( msg, "The sound component %s isn't a 3D sound, which is necessary for making grouped sounds. " + "Make sure all the sounds in this group are 3D sounds.", groupArray[ i ]->GetINode()->GetName() ); + IShowError( kSrcBufferInvalid, msg, baseNode->GetName(), pErrMsg ); + + // Attempt to recover + startPoses.Append( mergedData.GetCount() ); + volumes.Append( 1.f ); + continue; + } + + // Grab the buffer for this sound directly from the original source + char *fileName = groupArray[ i ]->GetSoundFileName( kBaseSound ); + + plSoundBuffer *buffer = TRACKED_NEW plSoundBuffer( fileName ); + if( !buffer->IsValid() || !buffer->EnsureInternal() ) + { + // OK, because some *cough* machines are completely stupid and don't load AssetMan scenes with + // AssetMan plugins, we need to do a really stupid fallback search to the current exporting directory. + const char *plasmaDir = plMaxConfig::GetClientPath(); + bool worked = false; + if( plasmaDir != nil ) + { + char newPath[ MAX_PATH ], *c; + strcpy( newPath, plasmaDir ); + strcat( newPath, "sfx\\" ); + + c = strrchr( fileName, '\\' ); + if( c == nil ) + c = strrchr( fileName, '/' ); + if( c == nil ) + c = fileName; + else + c++; + strcat( newPath, c ); + + // Got a path to try, so try it! + delete buffer; + buffer = TRACKED_NEW plSoundBuffer( newPath ); + if( buffer->IsValid() && buffer->EnsureInternal() ) + worked = true; + } + + if( !worked ) + { + char msg[ 512 ]; + sprintf( msg, "The sound file %s cannot be loaded for component %s.", fileName, groupArray[ i ]->GetINode()->GetName() ); + IShowError( kSrcBufferInvalid, msg, baseNode->GetName(), pErrMsg ); + delete buffer; + + // Attempt to recover + startPoses.Append( mergedData.GetCount() ); + volumes.Append( 1.f ); + continue; + } + } + + // Get a header (they should all be the same) + if( i == 0 ) + mergedHeader = buffer->GetHeader(); + else + { + if( memcmp( &mergedHeader, &buffer->GetHeader(), sizeof( mergedHeader ) ) != 0 ) + { + char msg[ 512 ]; + sprintf( msg, "The format for sound file %s does not match the format for the other grouped sounds on node %s. " + "Make sure the sounds are all the same format.", fileName, baseNode->GetName() ); + IShowError( kMergeSourceFormatMismatch, msg, baseNode->GetName(), pErrMsg ); + delete buffer; + + // Attempt to recover + startPoses.Append( mergedData.GetCount() ); + volumes.Append( 1.f ); + continue; + } + } + + // Grab the data from this buffer and merge it + // HACK: SetCount() won't copy the old data over, Expand() won't up the use count, so do + // an expand-and-setCount combo. + UInt32 pos = mergedData.GetCount(); + startPoses.Append( pos ); + mergedData.Expand( pos + buffer->GetDataLength() ); + mergedData.SetCount( pos + buffer->GetDataLength() ); + memcpy( &mergedData[ pos ], buffer->GetData(), buffer->GetDataLength() ); + + delete buffer; + + // Also keep track of what the volume should be for this particular sound + volumes.Append( groupArray[ i ]->GetSoundVolume() ); + } + + /// We got a merged buffer, so make a plSoundBuffer from it + int index = -1; + if( fIndices.find( baseNode ) != fIndices.end() ) + index = fIndices[ baseNode ]; + + sprintf( keyName, "%s_MergedSound", GetINode()->GetName() ); + + plKey buffKey = baseNode->FindPageKey( plSoundBuffer::Index(), keyName ); + if( buffKey != nil ) + plPluginResManager::ResMgr()->NukeKeyAndObject( buffKey ); + + // Create a new one... + plSoundBuffer *mergedBuffer = TRACKED_NEW plSoundBuffer(); + mergedBuffer->SetInternalData( mergedHeader, mergedData.GetCount(), mergedData.AcquireArray() ); + mergedData.Reset(); + // The buffer may be shared across multiple sources. We could or together the LoadMasks of all + // the nodes that use it, or we can just go with the default loadmask of Always load, and + // count on it never getting dragged into memory if nothing that references it does. + hsgResMgr::ResMgr()->NewKey( keyName, mergedBuffer, baseNode->GetLocation() ); + + + /// We got the sound buffer, now just create a groupedSound for it + + const plAudioInterface* ai = baseNode->GetSceneObject()->GetAudioInterface(); + plWinAudible* pAudible = (plWinAudible*)ai->GetAudible(); + + sprintf( keyName, "%s", GetINode()->GetName()); + plWin32GroupedSound *sound = TRACKED_NEW plWin32GroupedSound; + sound->SetPositionArray( startPoses.GetCount(), startPoses.AcquireArray(), volumes.AcquireArray() ); + sound->SetProperty( plSound::kPropLoadOnlyOnCall, true ); + + hsgResMgr::ResMgr()->NewKey( keyName, sound, baseNode->GetLocation(), baseNode->GetLoadMask() ); + hsgResMgr::ResMgr()->AddViaNotify( mergedBuffer->GetKey(), TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, -1, plSound::kRefDataBuffer ), plRefFlags::kActiveRef ); + + if( pAudible->AddSound( sound, index, true ) ) + { + // Just use the first component + plSound3DEmitterComponent *grpComp = (plSound3DEmitterComponent *)groupArray[ 0 ]; + grpComp->ISetParameters( sound, pErrMsg ); + } + + /// All done! + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Background Music Component +// + +class plBackgroundMusicComponent : public plBaseSoundEmitterComponent +{ +public: + plBackgroundMusicComponent(); + virtual ~plBackgroundMusicComponent(); + + // Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual hsBool IsLocalOnly( void ) const { if( fCompPB->GetInt( (ParamID)kSndIsLocalOnly ) ) return true; else return false; } + +protected: + virtual UInt32 ICalcSourceBufferFlags() const; + + bool IValidate(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool IGetCategoryList( char **&catList, int *&catKonstantList ); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plBackgroundMusicComponent, gBgndMusicEmitterDesc, "Nonspatial Sound", "NonspatSound", COMP_TYPE_AUDIO, BGND_MUSIC_COMPONENT_ID) + + +ParamBlockDesc2 gBgndMusicEmitterBk +( + plComponent::kBlkComp, _T("Bgnd Music"), 0, &gBgndMusicEmitterDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 5, // Number of rollouts + sSoundSharedPBHeader(IDS_COMP_SOUNDBASE), + kS3DBaseParams, IDD_COMP_SOUNDBGND, IDS_COMP_SOUNDBGND, 0, 0, &gSoundCompProc, + sSndWaveformPropsHeader, + kS3DSoftVolumeParams, IDD_COMP_SOUND_SOFTPARAMS, IDS_COMP_SOUNDSOFTPARAMS, 0, 0, &gSoundSoftVolumeSelProc, + + // Included paramblock + &sSoundSharedPB, + + // Waveform props define + sSndWaveformPropsParamTemplate, + + // params + + kSndIsLocalOnly, _T("noNetworkSynch"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, kS3DBaseParams, TYPE_SINGLECHEKBOX, IDC_SND_LOCALONLY, + end, + + kSndStreamCompressed, _T("stream"), TYPE_BOOL, 0, 0, + p_ui, kS3DBaseParams, TYPE_SINGLECHEKBOX, IDC_CHECK_STREAM, + end, + + /// Soft Region/Volume Parameters rollout + kSndSoftRegionEnable, _T( "enableSoftRegion" ), TYPE_BOOL, 0, 0, + p_ui, kS3DSoftVolumeParams, TYPE_SINGLECHEKBOX, IDC_SOUND_SOFTENABLE, + p_default, FALSE, + end, + + kSndSoftRegion, _T("softRegion"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_SOUNDSOFTSELECT, + p_accessor, &gSoundSoftVolAccessor, + end, + + end +); + + +plBackgroundMusicComponent::plBackgroundMusicComponent() +{ + fClassDesc = &gBgndMusicEmitterDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plBackgroundMusicComponent::~plBackgroundMusicComponent() +{ +} + +// Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plBackgroundMusicComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return plBaseSoundEmitterComponent::SetupProperties( pNode, pErrMsg ); +} + +UInt32 plBackgroundMusicComponent::ICalcSourceBufferFlags() const +{ + UInt32 ourFlags = 0; + if (fCompPB->GetInt(kSndStreamCompressed)) + ourFlags |= plSoundBuffer::kStreamCompressed; + + return plBaseSoundEmitterComponent::ICalcSourceBufferFlags() | ourFlags; +} + +bool plBackgroundMusicComponent::IValidate(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plBaseSoundEmitterComponent::IValidate( node, pErrMsg ); +} + +hsBool plBackgroundMusicComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plBaseSoundEmitterComponent::PreConvert( node, pErrMsg, BGND_MUSIC_COMPONENT_ID ); +} + +hsBool plBackgroundMusicComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (!fValidNodes[node]) + return false; + + char* fileName = GetSoundFileName( kBaseSound ); + + int fIndex = -1; + if (fIndices.find(node) != fIndices.end()) + fIndex = fIndices[node]; + + const plAudioInterface* ai = node->GetSceneObject()->GetAudioInterface(); + plWinAudible* pAudible = (plWinAudible*)ai->GetAudible(); + + plSoundBuffer *srcBuffer = IProcessSourceBuffer( node, pErrMsg ); + if( srcBuffer == nil ) + return false; + + char keyName[ 256 ]; + sprintf( keyName, "%s_Win32BgndSnd", GetINode()->GetName() ); + plWin32Sound *sound = nil; + + if( srcBuffer->GetDataLengthInSecs() > 4.f ) + sound = TRACKED_NEW plWin32StreamingSound; + else + sound = TRACKED_NEW plWin32StaticSound; + + hsgResMgr::ResMgr()->NewKey(keyName, sound, node->GetLocation(), node->GetLoadMask()); + + srcBuffer->SetFlag( plSoundBuffer::kAlwaysExternal ); + hsgResMgr::ResMgr()->AddViaNotify( srcBuffer->GetKey(), TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, -1, plSound::kRefDataBuffer ), plRefFlags::kActiveRef ); + + if (pAudible->AddSound( sound, fIndex, false)) + { + ISetBaseParameters( sound, pErrMsg ); + + sound->SetProperty( plSound::kPropLocalOnly, fCompPB->GetInt( (ParamID)kSndIsLocalOnly ) ? true : false ); + sound->SetPriority( fCompPB->GetInt( (ParamID)kSndPriority ) ); + + if( fCompPB->GetInt( (ParamID)kSndDisableLOD ) ) + { + // Force LOD off on this sound + sound->SetProperty( plSound::kPropDisableLOD, true ); + } + + IGrabSoftRegion( sound, pErrMsg ); + sound->GetEAXSettings().Enable( false ); + } + + return true; +} + +//// IGetCategoryList /////////////////////////////////////////////////////////////////////////// +// Returns a list of the categories and konstants supported for this type of sound + +hsBool plBackgroundMusicComponent::IGetCategoryList( char **&catList, int *&catKonstantList ) +{ + static char *cats[] = { "Background Music", "Ambience", "Sound FX", "GUI", "NPC Voice", "" }; + static int catEnums[] = { plSound::kBackgroundMusic, plSound::kAmbience, plSound::kSoundFX, plSound::kGUISound, plSound::kNPCVoices }; + + catList = cats; + catKonstantList = catEnums; + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// GUI Sound Component +// + +class plGUISoundComponent : public plBaseSoundEmitterComponent +{ +public: + plGUISoundComponent(); + virtual ~plGUISoundComponent(); + + // Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void UpdateSoundFileSelection( void ) { ; } + +protected: + + bool IValidate(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual hsBool IGetCategoryList( char **&catList, int *&catKonstantList ); + + virtual hsBool IHasWaveformProps( void ) const { return false; } +}; + +//Max desc stuff necessary below. +CLASS_DESC(plGUISoundComponent, gGUISoundEmitterDesc, "GUI Sound", "GUISound", COMP_TYPE_AUDIO, GUI_SOUND_COMPONENT_ID) + + +ParamBlockDesc2 gGUISoundEmitterBk +( + plComponent::kBlkComp, _T("GUI Sound"), 0, &gGUISoundEmitterDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 2, // Number of rollouts + sSoundSharedPBHeader(IDS_COMP_SOUNDGUI), + + // Included paramblock + &sSoundSharedPB, + + end +); + + +plGUISoundComponent::plGUISoundComponent() +{ + fClassDesc = &gGUISoundEmitterDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plGUISoundComponent::~plGUISoundComponent() +{ +} + +//// IGetCategoryList /////////////////////////////////////////////////////////////////////////// +// Returns a list of the categories and konstants supported for this type of sound + +hsBool plGUISoundComponent::IGetCategoryList( char **&catList, int *&catKonstantList ) +{ + static char *cats[] = { "GUI", "" }; + static int catEnums[] = { plSound::kGUISound }; + + catList = cats; + catKonstantList = catEnums; + + return true; +} + +// Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUISoundComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return plBaseSoundEmitterComponent::SetupProperties( pNode, pErrMsg ); +} + +bool plGUISoundComponent::IValidate(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plBaseSoundEmitterComponent::IValidate( node, pErrMsg ); +} + +hsBool plGUISoundComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plBaseSoundEmitterComponent::PreConvert( node, pErrMsg, GUI_SOUND_COMPONENT_ID ); +} + +hsBool plGUISoundComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (!fValidNodes[node]) + return false; + + char* fileName = GetSoundFileName( kBaseSound ); + + int fIndex = -1; + if (fIndices.find(node) != fIndices.end()) + fIndex = fIndices[node]; + + const plAudioInterface* ai = node->GetSceneObject()->GetAudioInterface(); + plWinAudible* pAudible = (plWinAudible*)ai->GetAudible(); + + plSoundBuffer *srcBuffer = GetSourceBuffer( fileName, node, ICalcSourceBufferFlags() ); + if( srcBuffer == nil ) + { + pErrMsg->Set( true, node->GetName(), "The file specified for the sound 3D component %s is invalid. This emitter will not be exported.", GetINode()->GetName() ).Show(); + pErrMsg->Set( false ); + return false; + } + + char keyName[ 256 ]; + sprintf( keyName, "%s_Win32GUISound", GetINode()->GetName() ); + + plWin32StaticSound *sound = TRACKED_NEW plWin32StaticSound; + hsgResMgr::ResMgr()->NewKey(keyName, sound, node->GetLocation(), node->GetLoadMask()); + + hsgResMgr::ResMgr()->AddViaNotify( srcBuffer->GetKey(), TRACKED_NEW plGenRefMsg( sound->GetKey(), plRefMsg::kOnCreate, -1, plSound::kRefDataBuffer ), plRefFlags::kActiveRef ); + + if (pAudible->AddSound( sound, fIndex, false)) + { + ISetBaseParameters( sound, pErrMsg ); + + sound->SetProperty( plSound::kPropLocalOnly, true ); // GUI sounds are always local-only + + if( fCompPB->GetInt( (ParamID)kSndDisableLOD ) ) + { + // Force LOD off on this sound + sound->SetProperty( plSound::kPropDisableLOD, true ); + } + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// EAX Listener Soft Region component +// + +class plEAXListenerComponent : public plComponent +{ +public: + enum + { + kRefSoftRegion, + kRefWhichSettings, + kRefPreset, + kRefCustFile, + + // The following are the parameters for the listener as defined in eax.h, minus the panning + kRefEnvironmentSize, // float + kRefEnvironmentDiffusion, // float + kRefRoom, // long + kRefRoomHF, // long + kRefRoomLF, // long + kRefDecayTime, // float + kRefDecayHFRatio, // float + kRefDecayLFRatio, // float + kRefReflections, // long + kRefReflectionsDelay, // float + // panning goes here + kRefReverb, // long + kRefReverbDelay, // float + // Reverb pan + kRefEchoTime, // float + kRefEchoDepth, + kRefModulationTime, + kRefModulationDepth, + kRefAirAbsorptionHF, + kRefHFReference, + kRefLFReference, + kRefRoomRolloffFactor, + kRefFlags, // unsigned long + }; + +public: + plEAXListenerComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *errMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *errMsg); + + const char *GetCustFileName( void ) const; + void SetCustFile( const char *path ); +}; + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plEAXListenerAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (id == plEAXListenerComponent::kRefSoftRegion ) + { + plEAXListenerComponent *comp = (plEAXListenerComponent*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +static plEAXListenerAccessor gEAXListenerAccessor; + +//// DialogProc for EAXListenerComponent //////////////////////////////////////////////////////// + +class plEAXListenerDlgProc : public plSingleCompSelProc +{ +protected: + + hsBool IGetCustFileName( plEAXListenerComponent *listenerComp ) + { + TCHAR fileName[ MAX_PATH ], dirName[ MAX_PATH ]; + + + const char *name = listenerComp->GetCustFileName(); + if( name != nil ) + strcpy( fileName, name ); + else + strcpy( fileName, _T( "" ) ); + + strcpy( dirName, fileName ); + ::PathRemoveFileSpec( dirName ); + + OPENFILENAME ofn = {0}; + ofn.lStructSize = sizeof( OPENFILENAME ); + ofn.hwndOwner = GetCOREInterface()->GetMAXHWnd(); + ofn.lpstrFilter = "EAX Preset Files (*.eax)\0*.eax\0All Files\0*.*\0"; + ofn.lpstrFile = fileName; + ofn.nMaxFile = sizeof( fileName ); + ofn.lpstrInitialDir = dirName; + ofn.lpstrTitle = "Choose a sound file"; + ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST; + ofn.lpstrDefExt = "eax"; + + if( GetOpenFileName( &ofn ) ) + { + listenerComp->SetCustFile( fileName ); + return true; + } + else + return false; + } +public: + + plEAXListenerDlgProc() + : plSingleCompSelProc( plEAXListenerComponent::kRefSoftRegion, IDC_EAX_SOFTREGION, "Select the soft region to apply these EAX listener properties to" ) + { + } + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) + { + int i; + IParamBlock2 *pb = map->GetParamBlock(); + + + switch ( msg ) + { + case WM_INITDIALOG: + { + + // Load the preset combo with preset names + HWND comboBox = GetDlgItem( hWnd, IDC_EAX_PRESET_COMBO ); + ComboBox_ResetContent( comboBox ); + + for( i = 0; i < /*sizeof( EAX30_ORIGINAL_PRESETS ) + / sizeof( EAXLISTENERPROPERTIES )*/26 ; i++ ) + ComboBox_AddString( comboBox, EAX30_ORIGINAL_PRESET_NAMES[ i ] ); + + ComboBox_SetCurSel( comboBox, pb->GetInt( (ParamID)plEAXListenerComponent::kRefPreset ) ); + + ICustButton *custButton = GetICustButton( GetDlgItem( hWnd, IDC_EAX_CUSTFILE ) ); + if( custButton != nil ) + { + custButton->SetText( pb->GetStr( (ParamID)plEAXListenerComponent::kRefCustFile ) ); + ReleaseICustButton( custButton ); + } + } + break; + + case WM_COMMAND: + if( LOWORD( wParam ) == IDC_EAX_PRESET_COMBO ) + { + int sel = SendDlgItemMessage( hWnd, IDC_EAX_PRESET_COMBO, CB_GETCURSEL, 0, 0 ); + if( sel != CB_ERR ) + pb->SetValue( (ParamID)plEAXListenerComponent::kRefPreset, 0, sel ); + return true; + } + if( ( HIWORD( wParam ) == BN_CLICKED ) && LOWORD( wParam ) == IDC_EAX_CUSTFILE ) + { + // Get the file to load + plEAXListenerComponent *comp = (plEAXListenerComponent *)map->GetParamBlock()->GetOwner(); + if( IGetCustFileName( comp ) ) + { + ICustButton *custButton = GetICustButton( GetDlgItem( hWnd, IDC_EAX_CUSTFILE ) ); + if( custButton != nil ) + { + custButton->SetText( pb->GetStr( (ParamID)plEAXListenerComponent::kRefCustFile ) ); + ReleaseICustButton( custButton ); + } + } + } + break; + } + + return plSingleCompSelProc::DlgProc( t, map, hWnd, msg, wParam, lParam ); + } + + void DeleteThis() {} +}; + +static plEAXListenerDlgProc gEAXListenerDlgProc; + +//Max desc stuff necessary below. +CLASS_DESC(plEAXListenerComponent, gEAXListenerDesc, "EAX Listener", "EAXListener", COMP_TYPE_AUDIO, EAX_LISTENER_COMPONENT_ID) + +ParamBlockDesc2 gEAXListenerBlk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("EAXListener"), 0, &gEAXListenerDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_EAXLISTENER, IDS_COMP_EAXLISTENER, 0, 0, &gEAXListenerDlgProc, + + plEAXListenerComponent::kRefSoftRegion, _T("SoftRegion"), TYPE_INODE, 0, 0, + p_accessor, &gEAXListenerAccessor, + end, + + plEAXListenerComponent::kRefWhichSettings, _T("whichSettings"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 2, IDC_EAX_PRESET, IDC_EAX_CUSTOM, + p_default, 0, + end, + + plEAXListenerComponent::kRefPreset, _T("preset"), TYPE_INT, 0, 0, + p_default, 0, + end, + + // This is just a label for now, so the users know what file the presets came from + plEAXListenerComponent::kRefCustFile, _T("custFile"), TYPE_STRING, 0, 0, + p_default, _T(""), + end, + + // EAX listener params (should be private) + plEAXListenerComponent::kRefEnvironmentSize, _T(""), TYPE_FLOAT, 0, 0, end, // float + plEAXListenerComponent::kRefEnvironmentDiffusion, _T(""), TYPE_FLOAT, 0, 0, end,// float + plEAXListenerComponent::kRefRoom, _T(""), TYPE_INT, 0, 0, end,// long + plEAXListenerComponent::kRefRoomHF, _T(""), TYPE_INT, 0, 0, end,// long + plEAXListenerComponent::kRefRoomLF, _T(""), TYPE_INT, 0, 0, end,// long + plEAXListenerComponent::kRefDecayTime, _T(""), TYPE_FLOAT, 0, 0, end,// float + plEAXListenerComponent::kRefDecayHFRatio, _T(""), TYPE_FLOAT, 0, 0, end,// float + plEAXListenerComponent::kRefDecayLFRatio, _T(""), TYPE_FLOAT, 0, 0, end,// float + plEAXListenerComponent::kRefReflections, _T(""), TYPE_INT, 0, 0, end,// long + plEAXListenerComponent::kRefReflectionsDelay, _T(""), TYPE_FLOAT, 0, 0, end,// float + // panning goes here + plEAXListenerComponent::kRefReverb, _T(""), TYPE_INT, 0, 0, end,// long + plEAXListenerComponent::kRefReverbDelay, _T(""), TYPE_FLOAT, 0, 0, end,// float + // Reverb pan + plEAXListenerComponent::kRefEchoTime, _T(""), TYPE_FLOAT, 0, 0, end,// float + plEAXListenerComponent::kRefEchoDepth, _T(""), TYPE_FLOAT, 0, 0, end, + plEAXListenerComponent::kRefModulationTime, _T(""), TYPE_FLOAT, 0, 0, end, + plEAXListenerComponent::kRefModulationDepth, _T(""), TYPE_FLOAT, 0, 0, end, + plEAXListenerComponent::kRefAirAbsorptionHF, _T(""), TYPE_FLOAT, 0, 0, end, + plEAXListenerComponent::kRefHFReference, _T(""), TYPE_FLOAT, 0, 0, end, + plEAXListenerComponent::kRefLFReference, _T(""), TYPE_FLOAT, 0, 0, end, + plEAXListenerComponent::kRefRoomRolloffFactor, _T(""), TYPE_FLOAT, 0, 0, end, + plEAXListenerComponent::kRefFlags, _T(""), TYPE_INT, 0, 0, end,// unsigned long + + end +); + +plEAXListenerComponent::plEAXListenerComponent() +{ + fClassDesc = &gEAXListenerDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plEAXListenerComponent::Convert(plMaxNode *node, plErrorMsg *errMsg) +{ + if( !fCompPB->GetINode((ParamID)kRefSoftRegion) ) + return true; + + plSceneObject* sceneObj = node->GetSceneObject(); + if( !sceneObj ) + return true; +/* + plLightInfo* li = plLightInfo::ConvertNoRef(sceneObj->GetGenericInterface(plLightInfo::Index())); + if( !li ) + return true; +*/ + plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent( fCompPB->GetINode( (ParamID)kRefSoftRegion ) ); + if( !softComp ) + return true; + + plKey softKey = softComp->GetSoftVolume(); + if( !softKey ) + return true; + + // Create a listener mod to handle these things + plEAXListenerMod *listener = TRACKED_NEW plEAXListenerMod(); + node->AddModifier( listener, IGetUniqueName(node) ); + + // Add the soft region + hsgResMgr::ResMgr()->AddViaNotify( softKey, TRACKED_NEW plGenRefMsg( listener->GetKey(), plRefMsg::kOnCreate, 0, plEAXListenerMod::kRefSoftRegion ), plRefFlags::kActiveRef ); + + // Set up the parameters of the listener mod + EAXLISTENERPROPERTIES *listenerProps = listener->GetListenerProps(); + if( fCompPB->GetInt( (ParamID)kRefWhichSettings ) == 0 ) + { + // Set params based on a preset + listener->SetFromPreset( fCompPB->GetInt( (ParamID)kRefPreset ) ); + } + else + { + // Get the raw params + listenerProps->flEnvironmentSize = fCompPB->GetFloat( (ParamID)kRefEnvironmentSize ); + listenerProps->flEnvironmentDiffusion = fCompPB->GetFloat( (ParamID)kRefEnvironmentDiffusion ); + listenerProps->lRoom = fCompPB->GetInt( (ParamID)kRefRoom ); + listenerProps->lRoomHF = fCompPB->GetInt( (ParamID)kRefRoomHF ); + listenerProps->lRoomLF = fCompPB->GetInt( (ParamID)kRefRoomLF ); + listenerProps->flDecayTime = fCompPB->GetFloat( (ParamID)kRefDecayTime ); + listenerProps->flDecayHFRatio = fCompPB->GetFloat( (ParamID)kRefDecayHFRatio ); + listenerProps->flDecayLFRatio = fCompPB->GetFloat( (ParamID)kRefDecayLFRatio ); + listenerProps->lReflections = fCompPB->GetInt( (ParamID)kRefReflections ); + listenerProps->flReflectionsDelay = fCompPB->GetFloat( (ParamID)kRefReflectionsDelay ); + //listenerProps->vReflectionsPan; // early reflections panning vector + listenerProps->lReverb = fCompPB->GetInt( (ParamID)kRefReverb ); // late reverberation level relative to room effect + listenerProps->flReverbDelay = fCompPB->GetFloat( (ParamID)kRefReverbDelay ); + //listenerProps->vReverbPan; // late reverberation panning vector + listenerProps->flEchoTime = fCompPB->GetFloat( (ParamID)kRefEchoTime ); + listenerProps->flEchoDepth = fCompPB->GetFloat( (ParamID)kRefEchoDepth ); + listenerProps->flModulationTime = fCompPB->GetFloat( (ParamID)kRefModulationTime ); + listenerProps->flModulationDepth = fCompPB->GetFloat( (ParamID)kRefModulationDepth ); + listenerProps->flAirAbsorptionHF = fCompPB->GetFloat( (ParamID)kRefAirAbsorptionHF ); + listenerProps->flHFReference = fCompPB->GetFloat( (ParamID)kRefHFReference ); + listenerProps->flLFReference = fCompPB->GetFloat( (ParamID)kRefLFReference ); + listenerProps->flRoomRolloffFactor = fCompPB->GetFloat( (ParamID)kRefRoomRolloffFactor ); + listenerProps->ulFlags = fCompPB->GetInt( (ParamID)kRefFlags ); + } + + return true; +} + +hsBool plEAXListenerComponent::PreConvert(plMaxNode *pNode, plErrorMsg *errMsg) +{ + + return true; +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plEAXListenerComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg) +{ + + return true; +} + +const char *plEAXListenerComponent::GetCustFileName( void ) const +{ + return (const char *)fCompPB->GetStr( (ParamID)kRefCustFile ); +} + +void plEAXListenerComponent::SetCustFile( const char *path ) +{ + char file[ MAX_PATH ]; + int i; + hsUNIXStream presetFile; + + // Map of PB values to file entries + struct FilePBMap + { + char *fKeyword; + ParamID fParamID; + UInt8 fType; // 0 is int, 1 is float for now + } myMap[] = { + { "flEnvironmentSize", kRefEnvironmentSize, 1 }, + { "flEnvironmentDiffusion", kRefEnvironmentDiffusion, 1 }, + { "lRoom", kRefRoom, 0 }, + { "lRoomHF", kRefRoomHF, 0 }, + { "lRoomLF", kRefRoomLF, 0 }, + { "flDecayTime", kRefDecayTime, 1 }, + { "flDecayHFRatio", kRefDecayHFRatio, 1 }, + { "flDecayLFRatio", kRefDecayLFRatio, 1 }, + { "lReflections", kRefReflections, 0 }, + { "flReflectionsDelay", kRefReflectionsDelay, 1 }, + { "lReverb", kRefReverb, 0 }, + { "flReverbDelay", kRefReverbDelay, 1 }, + { "flEchoTime", kRefEchoTime, 1 }, + { "flEchoDepth", kRefEchoDepth, 1 }, + { "flModulationTime", kRefModulationTime, 1 }, + { "flModulationDepth", kRefModulationDepth, 1 }, + { "flAirAbsorptionHF", kRefAirAbsorptionHF, 1 }, + { "flHFReference", kRefHFReference, 1 }, + { "flLFReference", kRefLFReference, 1 }, + { "flRoomRolloffFactor", kRefRoomRolloffFactor, 1 }, + { "dwFlags", kRefFlags, 0 }, + { nil, 0, 0 } }; + + // Read the file and set settings from it + if( !presetFile.Open( path, "rt" ) ) + { + // Oops + hsAssert( false, "can't open file" ); + return; + } + + // Loop and find our keywords + for( i = 0; myMap[ i ].fKeyword != nil && !presetFile.AtEnd(); ) + { + char line[ 512 ]; + + // Read a line from the file until we find our keyword + presetFile.ReadLn( line, sizeof( line ) ); + if( strstr( line, myMap[ i ].fKeyword ) == nil ) + continue; + + // Read the next line, with our value + presetFile.ReadLn( line, sizeof( line ) ); + float value = atof( line ); + if( myMap[ i ].fType == 0 ) + fCompPB->SetValue( myMap[ i ].fParamID, 0, (int)value ); + else + fCompPB->SetValue( myMap[ i ].fParamID, 0, (float)value ); + + i++; + } + + if( myMap[ i ].fKeyword != nil ) + { + hsAssert( false, "Couldn't find all of the keywords in the settings file. Oh well" ); + } + + // All done! + presetFile.Close(); + + // Update our helper reminder string + _splitpath( path, nil, nil, file, nil ); + fCompPB->SetValue( (ParamID)kRefCustFile, 0, file ); +} + +/// Obsolete SFX components (made obsolete by the new EAX support) + +OBSOLETE_CLASS(plSoundReverbComponent, gSoundReverbDesc, "Audio Region", "AudioRegion", COMP_TYPE_AUDIO, Class_ID(0x50507200, 0x48651c4c)) +OBSOLETE_CLASS(plSoundChorusModComponent,gSoundChorusModDesc , "Chorus Effect", "ChorusEffect", COMP_TYPE_AUDIO, Class_ID(0x10f91101, 0x28cb21b9)) +OBSOLETE_CLASS(plSoundCompressorModComponent,gSoundCompressorModDesc , "Compressor Effect", "CompressEffect", COMP_TYPE_AUDIO, Class_ID(0x443d2167, 0x4ca42eb)) +OBSOLETE_CLASS(plSoundDistortModComponent,gSoundDistortModDesc , "Distort Effect", "DistortEffect", COMP_TYPE_AUDIO, Class_ID(0x7cb45868, 0x61220227)) +OBSOLETE_CLASS(plSoundEchoModComponent,gSoundEchoModDesc , "Echo Effect", "EchoEffect", COMP_TYPE_AUDIO,Class_ID(0x2948347e, 0x30ba0be3)) +OBSOLETE_CLASS(plSoundFlangerModComponent,gSoundFlangerModDesc , "Flanger Effect", "FlangerEffect", COMP_TYPE_AUDIO, Class_ID(0x25034090, 0x361a08d7) ) +OBSOLETE_CLASS(plSoundGargleModComponent,gSoundGargleModDesc , "Gargle Effect", "GargleEffect", COMP_TYPE_AUDIO, Class_ID(0x639b6a41, 0x24da2462)) +OBSOLETE_CLASS(plSoundReverbModComponent,gSoundReverbModDesc , "Reverb Effect", "ReverbEffect", COMP_TYPE_AUDIO, Class_ID(0x1bef33fc, 0x5c763858)) + + + +#if 1 // Waiting... mf +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// RandomSound Component +// +// + +plKey plAudioComp::GetRandomSoundKey(plComponentBase *comp, plMaxNode *node) +{ + if (comp->ClassID() == RANDOM_SOUND_COMPONENT_ID) + { + plRandomSoundComponent *rndSnd = (plRandomSoundComponent*)comp; + if (rndSnd->fSoundMods.find(node) != rndSnd->fSoundMods.end()) + return rndSnd->fSoundMods[node]->GetKey(); + } + + return nil; +} + +///////////////////////////////////////////////////////////////////////////////////////// +enum +{ + kAutoStart, + kSelectMode, + kDelayMode, + kMinDelay, + kMaxDelay, + kUseAll, + kGroupIdx, + kSoundList, + kGroupTotals, + kLastPick, + kCombineSounds +}; + +enum +{ + kNormal = 0, + kNoRepeats, + kFullSetRepeat, + kFullSetStop, + kSequential +}; + +enum +{ + kDelayFromStart = 0, + kDelayFromEnd, + kDelayInfinite +}; + +enum +{ + kRandomSoundMain, + kRandomSoundGroup, +}; + +static const kMaxGroups = 10; + +class plRandomSoundComponentProc : public ParamMap2UserDlgProc +{ +public: + plRandomSoundComponentProc() {} + + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + + void DeleteThis() {} + void UpdateDisplay(IParamMap2 *pm); + virtual void Update(TimeValue t, Interval& valid, IParamMap2* pmap) { UpdateDisplay(pmap); } +}; + +static plRandomSoundComponentProc gRandomSoundComponentProc; + +void plRandomSoundComponentProc::UpdateDisplay(IParamMap2 *pm) +{ + HWND hWnd = pm->GetHWnd(); + HWND hList = GetDlgItem(hWnd, IDC_COMP_RS_GROUPLIST); + IParamBlock2 *pb = pm->GetParamBlock(); + plRandomSoundComponent *comp = (plRandomSoundComponent *)pb->GetOwner(); + + ListBox_ResetContent(hList); + int group = comp->GetCurGroupIdx(); + int startIdx = comp->GetStartIndex(group); + int endIdx = comp->GetEndIndex(group); + + while (startIdx < endIdx) + { + INode *curNode = pb->GetINode(ParamID(kSoundList), 0, startIdx); + if (curNode == nil) + { + comp->RemoveSound(startIdx); + endIdx--; + continue; + } + ListBox_AddString(hList, curNode->GetName()); + startIdx++; + } +} + +BOOL plRandomSoundComponentProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + IParamBlock2 *pb = pm->GetParamBlock(); + HWND hList = GetDlgItem(hWnd, IDC_COMP_RS_GROUPLIST); + plRandomSoundComponent *comp = (plRandomSoundComponent *)pb->GetOwner(); + + switch (msg) + { + case WM_INITDIALOG: + //UpdateDisplay(pm); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + if (LOWORD(wParam) == IDC_COMP_RS_GROUP_ADD) + { + std::vector cids; + cids.push_back(SOUND_3D_COMPONENT_ID); + if (plPick::NodeRefKludge(pb, kLastPick, &cids, true, false)) + comp->AddSelectedSound(); + + return TRUE; + } + // Remove the currently selected material + else if (LOWORD(wParam) == IDC_COMP_RS_GROUP_REMOVE) + { + int curSel = ListBox_GetCurSel(hList); + if (curSel >= 0) + comp->RemoveSound(curSel); + + return TRUE; + } + } + } + + return FALSE; +} + +//Max desc stuff necessary below. +CLASS_DESC(plRandomSoundComponent, gRandomSoundDesc, "Random Sound", "RandomSound", COMP_TYPE_AUDIO, RANDOM_SOUND_COMPONENT_ID) + +// +// Block not necessary, kept for backwards compat. +// + +ParamBlockDesc2 gRandomSoundBk +( + plComponent::kBlkComp, _T("RandomSound"), 0, &gRandomSoundDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + 2, + kRandomSoundMain, IDD_COMP_RANDOMSOUND, IDS_COMP_RANDOMSOUNDS, 0, 0, NULL, + kRandomSoundGroup, IDD_COMP_RANDOMSOUND_GROUPS, IDS_COMP_RANDOMSOUNDS_GROUPS, 0, APPENDROLL_CLOSED, &gRandomSoundComponentProc, + + // Main rollout + kAutoStart, _T("AutoStart"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, kRandomSoundMain, TYPE_SINGLECHEKBOX, IDC_COMP_RS_AUTOSTART, + end, + + kSelectMode, _T("SelectMode"), TYPE_INT, 0, 0, + p_ui, kRandomSoundMain, TYPE_RADIO, 5, IDC_RADIO_RS_NORMAL, IDC_RADIO_RS_NOREP, IDC_RADIO_RS_FSREP, IDC_RADIO_RS_FSSTOP, IDC_RADIO_RS_SEQ, + end, + + kDelayMode, _T("DelayMode"), TYPE_INT, 0, 0, + p_ui, kRandomSoundMain, TYPE_RADIO, 3, IDC_RADIO_RS_DELAYSTART, IDC_RADIO_RS_DELAYEND, IDC_RADIO_RS_DELAYNEVER, + end, + + kMinDelay, _T("MinDelay"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -500.0, 1000.0, + p_ui, kRandomSoundMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_RS_DELAYMIN, IDC_COMP_RS_DELAYMIN_SPIN, 1.0, + end, + + kMaxDelay, _T("MaxDelay"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -500.0, 1000.0, + p_ui, kRandomSoundMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_RS_DELAYMAX, IDC_COMP_RS_DELAYMAX_SPIN, 0.1, + end, + + // Group rollout + kUseAll, _T("UseAll"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, kRandomSoundGroup, TYPE_SINGLECHEKBOX, IDC_COMP_RS_USEALL, + end, + + kGroupIdx, _T("GroupIndex"), TYPE_INT, 0, 0, + p_default, 1, + p_range, 1, kMaxGroups, + p_ui, kRandomSoundGroup, TYPE_SPINNER, EDITTYPE_INT, + IDC_COMP_RS_GROUP, IDC_COMP_RS_GROUP_SPIN, 1.f, + end, + + kSoundList, _T("Sounds"), TYPE_INODE_TAB, 0, 0, 0, + end, + + kGroupTotals, _T("Totals"), TYPE_INT_TAB, kMaxGroups, 0, 0, + p_default, 0, + end, + + kLastPick, _T("LastPick"), TYPE_INODE, 0, 0, // Temp storage space for the comp picker + end, + + kCombineSounds, _T("combineSounds"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, kRandomSoundGroup, TYPE_SINGLECHEKBOX, IDC_RAND_COMBINESOUNDS, + end, + + end +); + +plRandomSoundComponent::plRandomSoundComponent() +{ + fClassDesc = &gRandomSoundDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +int plRandomSoundComponent::GetCurGroupIdx() +{ + return fCompPB->GetInt(ParamID(kGroupIdx)) - 1; +} + +int plRandomSoundComponent::GetStartIndex(int group) +{ + int result = 0; + int i; + for (i = 0; i < group; i++) + result += fCompPB->GetInt(ParamID(kGroupTotals), 0, i); + + return result; +} + +int plRandomSoundComponent::GetEndIndex(int group) +{ + return GetStartIndex(group) + fCompPB->GetInt(ParamID(kGroupTotals), 0, group); +} + +void plRandomSoundComponent::AddSelectedSound() +{ + int group = GetCurGroupIdx(); + int soundIdx = GetEndIndex(group); + + INode *node = fCompPB->GetINode(ParamID(kLastPick)); + fCompPB->Insert(ParamID(kSoundList), soundIdx, 1, &node); + + fCompPB->SetValue(ParamID(kGroupTotals), 0, fCompPB->GetInt(ParamID(kGroupTotals), 0, group) + 1, group); +} + +void plRandomSoundComponent::RemoveSound(int index) +{ + int group = GetCurGroupIdx(); + int soundIdx = GetStartIndex(group) + index; + + fCompPB->Delete(ParamID(kSoundList), soundIdx, 1); + fCompPB->SetValue(ParamID(kGroupTotals), 0, fCompPB->GetInt(ParamID(kGroupTotals), 0, group) - 1, group); +} + +hsBool plRandomSoundComponent::ICheckForSounds(plMaxNode* node) +{ + if (!node->CanConvert()) + return false; + + int nSounds = 0; + UInt32 numComp = node->NumAttachedComponents(false); + for(int i = 0; i < numComp; i++) + { + plComponentBase* comp = node->GetAttachedComponent(i); + if (plAudioComp::IsSoundComponent(comp)) + nSounds++; + } + + return nSounds > 0; +} + +hsBool plRandomSoundComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !ICheckForSounds(node) ) + { + // Warning that there's no sounds to be played? + return true; + } + + plRandomSoundMod* mod = fSoundMods[node]; + plSound *pSound = nil; + const plAudioInterface* ai = nil; + plWinAudible* pAudible = nil; + + + if( fCompPB->GetInt((ParamID)kAutoStart) ) + mod->SetState(0); + else + mod->SetState(plRandomSoundMod::kStopped); + + UInt8 mode = plRandomSoundMod::kNormal; + switch( fCompPB->GetInt((ParamID)kSelectMode) ) + { + // random, repeats okay, play until stopped - Normal + case kNormal: + mode = plRandomSoundMod::kNormal; + break; + // random, no repeats, play until stopped - NoRepeats + case kNoRepeats: + mode = plRandomSoundMod::kNoRepeats; + break; + // random, play full cycle before repeating - FullSetRepeat + case kFullSetRepeat: + mode = plRandomSoundMod::kCoverall | plRandomSoundMod::kNoRepeats; + break; + // random, play full cycle, then stop - FullSetStop + case kFullSetStop: + mode = plRandomSoundMod::kCoverall | plRandomSoundMod::kOneCycle | plRandomSoundMod::kNoRepeats; + break; + case kSequential: + mode = plRandomSoundMod::kSequential; + break; + } + + switch( fCompPB->GetInt((ParamID)kDelayMode) ) + { + case kDelayFromStart: + break; + case kDelayFromEnd: + mode |= plRandomSoundMod::kDelayFromEnd; + break; + case kDelayInfinite: + mode |= plRandomSoundMod::kOneCmd; + break; + } + + mod->SetMode(mode); + + float minDel = fCompPB->GetFloat((ParamID)kMinDelay); + float maxDel = fCompPB->GetFloat((ParamID)kMaxDelay); + if( minDel > maxDel ) + { + float t = maxDel; + maxDel = minDel; + minDel = t; + } + + mod->SetMinDelay(minDel); + mod->SetMaxDelay(maxDel); + + node->AddModifier(mod, IGetUniqueName(node)); + + + if (!fCompPB->GetInt(ParamID(kUseAll))) // Actually using separate groups + { + ai = node->GetSceneObject()->GetAudioInterface(); + pAudible = (plWinAudible*)ai->GetAudible(); + hsTArray comps; + + plRandomSoundModGroup *groups = TRACKED_NEW plRandomSoundModGroup[kMaxGroups]; + int i; + int numSoFar = 0; + for (i = 0; i < kMaxGroups; i++) + { + int numSounds = fCompPB->GetInt(ParamID(kGroupTotals), 0, i); + if( numSounds == 0 ) + { + groups[i].fGroupedIdx = -1; + groups[i].fNumSounds = 0; + groups[i].fIndices = nil; + continue; + } + + groups[i].fIndices = TRACKED_NEW UInt16[numSounds]; + + hsTArray indices; + int j; + + if( !fCompPB->GetInt( (ParamID)kCombineSounds ) ) + { + for (j = 0; j < numSounds; j++) + { + plMaxNode *compNode = (plMaxNode*)fCompPB->GetINode(ParamID(kSoundList), 0, numSoFar + j); + if (compNode) + { + plBaseSoundEmitterComponent *comp = (plBaseSoundEmitterComponent *)compNode->ConvertToComponent(); + int idx = comp->GetSoundIdx((plMaxNode*)node); + if (idx >= 0) + { + indices.Append(idx); + } + } + } + groups[i].fNumSounds = indices.GetCount(); + for (j = 0; j < indices.GetCount(); j++) + { + groups[i].fIndices[j] = indices[j]; + } + } + else + { + // Build array of components to give to ConvertGrouped() + for (j = 0; j < numSounds; j++) + { + plMaxNode *compNode = (plMaxNode*)fCompPB->GetINode(ParamID(kSoundList), 0, numSoFar + j); + if (compNode) + { + plBaseSoundEmitterComponent *comp = (plBaseSoundEmitterComponent *)compNode->ConvertToComponent(); + comps.Append( comp ); + // Stupid, i know. Leave me alone, PG is playing. + indices.Append( comps.GetCount() - 1 ); + } + } + + // Get index from first (should be the same for all of 'em) + groups[i].fGroupedIdx = comps[ 0 ]->GetSoundIdx( (plMaxNode *)node ); + groups[i].fNumSounds = indices.GetCount(); + for (j = 0; j < indices.GetCount(); j++) + { + groups[i].fIndices[j] = indices[ j ]; + } + } + + numSoFar += groups[i].fNumSounds; + } + mod->SetGroupInfo(kMaxGroups, groups); + + if( fCompPB->GetInt( (ParamID)kCombineSounds ) ) + { + // Convert (use pointer to first comp so we get the virtual call) + if( !comps[ 0 ]->ConvertGrouped( node, comps, pErrMsg ) ) + { + return false; + } + } + } + + // Non-grouped random sounds - give priority to each sound + else + { + ai = node->GetSceneObject()->GetAudioInterface(); + pAudible = (plWinAudible*)ai->GetAudible(); + int numSounds = pAudible->GetNumSounds(); + + if(numSounds == 0) return true; + + pSound = pAudible->GetSound(0); // Get sound ptr + int highestPriority = pSound->GetPriority(); + + // Distance to lowest priority + int distToLowest = 9 - highestPriority; + if( distToLowest <= 0) distToLowest = 1; // just incase + + for( int i = 0; i < numSounds; i++) + { + pSound = pAudible->GetSound(i); // Get sound ptr + + // Give the first random sound highest priority + if(i == 0) + pSound->SetPriority(highestPriority); + + else + { + pSound->SetPriority(highestPriority+((i-1)%distToLowest)+1); + } + } + } + + return true; +} + +hsBool plRandomSoundComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + if (ICheckForSounds(pNode)) + { + plRandomSoundMod* mod = TRACKED_NEW plRandomSoundMod; + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), mod, pNode->GetLocation()); + fSoundMods[pNode] = mod; + } + + return true; +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plRandomSoundComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + fSoundMods.clear(); + + // Tell some (all?) of the sound components we point to that they're going to be + // grouped sounds instead + if( fCompPB->GetInt( (ParamID)kCombineSounds ) ) + { + if (!fCompPB->GetInt(ParamID(kUseAll))) // Actually using separate groups + { + // Get a sound index to assign to all the components, since they get the same one as a grouped sound + int idx = pNode->GetNextSoundIdx(); + + int i, numSoFar = 0; + for (i = 0; i < kMaxGroups; i++) + { + int numSounds = fCompPB->GetInt(ParamID(kGroupTotals), 0, i); + + if( numSounds <= 0 ) + continue; + + int j; + for (j = 0; j < numSounds; j++) + { + plMaxNode *compNode = (plMaxNode*)fCompPB->GetINode(ParamID(kSoundList), 0, numSoFar + j); + if (compNode) + { + plBaseSoundEmitterComponent *comp = (plBaseSoundEmitterComponent *)compNode->ConvertToComponent(); + comp->SetCreateGrouped( pNode, idx ); + } + } + numSoFar += numSounds; + } + } + } + + return true; +} + + + +#endif // Waiting... mf + + + + +///////////////////////////////////////////////////////////////////////////////////////////////// +/// Physics Sound Group Component /////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + +class plPhysicsSndGroupCompProc; +class plPhysicsSndGroupComp : public plComponent +{ +protected: + + friend class plPhysicsSndGroupCompProc; + +public: + plPhysicsSndGroupComp(); + void DeleteThis() { delete this; } + + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum Refs + { + kRefGroup, + kRefImpactSoundsOld, + kRefSlideSoundsOld, + kRefDummyPickNode, + kRefImpactSounds, + kRefSlideSounds, + }; +}; + +class plPhysicsSndGroupCompProc : public ParamMap2UserDlgProc +{ +public: + plPhysicsSndGroupCompProc() {} + + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + + void DeleteThis() {} + virtual void Update(TimeValue t, Interval& valid, IParamMap2* pmap) { } + +protected: + + void IInitList( HWND hList, int currSel, hsBool allowAll ) + { + int i, toSet = -1; + struct plSndGrp + { + char name[ 64 ]; + int group; + } groups[] = { { "Metal", plPhysicalSndGroup::kMetal }, + { "Grass", plPhysicalSndGroup::kGrass }, + { "Wood", plPhysicalSndGroup::kWood }, + { "Stone", plPhysicalSndGroup::kWood + 1 }, + { "Water", plPhysicalSndGroup::kWood + 2 }, + { "Bone", plPhysicalSndGroup::kWood + 3 }, + { "Dirt", plPhysicalSndGroup::kWood + 4 }, + { "Rug", plPhysicalSndGroup::kWood + 5 }, + { "Cone", plPhysicalSndGroup::kWood + 6 }, + { "User 1", plPhysicalSndGroup::kWood + 7 }, + { "User 2", plPhysicalSndGroup::kWood + 8 }, + { "User 3", plPhysicalSndGroup::kWood + 9 }, + { "", plPhysicalSndGroup::kNone } }; + + SendMessage( hList, CB_RESETCONTENT, 0, 0 ); + + if( allowAll ) + { + int idx = SendMessage( hList, CB_ADDSTRING, 0, (LPARAM)"* All *" ); + SendMessage( hList, CB_SETITEMDATA, idx, (LPARAM)-1 ); + if( currSel == -1 ) + toSet = idx; + } + + for( i = 0; groups[ i ].group != plPhysicalSndGroup::kNone; i++ ) + { + int idx = SendMessage( hList, CB_ADDSTRING, 0, (LPARAM)groups[ i ].name ); + SendMessage( hList, CB_SETITEMDATA, idx, (LPARAM)groups[ i ].group ); + + if( groups[ i ].group == currSel ) + toSet = idx; + } + + if( toSet != -1 ) + SendMessage( hList, CB_SETCURSEL, toSet, 0 ); + } + + void IUpdateBtns( HWND hWnd, int idx, plPhysicsSndGroupComp *comp ) + { + // Update da buttons + if( idx == -1 ) + idx = 0; + + INode *impact = IGet( comp->GetParamBlock( 0 ), plPhysicsSndGroupComp::kRefImpactSounds, idx ); + ::SetWindowText( GetDlgItem( hWnd, IDC_SND_IMPACT ), ( impact != nil ) ? impact->GetName() : "" ); + + INode *slide = IGet( comp->GetParamBlock( 0 ), plPhysicsSndGroupComp::kRefSlideSounds, idx ); + ::SetWindowText( GetDlgItem( hWnd, IDC_SND_SLIDE ), ( slide != nil ) ? slide->GetName() : "" ); + } + + void ISet( IParamBlock2 *pb, ParamID which, int idx, INode *node ) + { + if( pb->Count( which ) <= idx ) + { + pb->SetCount( (ParamID)which, idx + 1 ); + pb->Resize( (ParamID)which, idx + 1 ); + } + + if( idx == -1 ) + { + pb->SetCount( (ParamID)which, plPhysicalSndGroup::kWood + 9 ); + pb->Resize( which, plPhysicalSndGroup::kWood + 9 ); + int i; + for( i = 0; i < plPhysicalSndGroup::kWood + 9; i++ ) + pb->SetValue( which, 0, node, i ); + } + else + pb->SetValue( which, 0, node, idx ); + } + + INode *IGet( IParamBlock2 *pb, ParamID which, int idx ) + { + if( pb->Count( which ) <= idx ) + return nil; + + return pb->GetINode( which, 0, idx ); + } +}; + +static plPhysicsSndGroupCompProc gPhysicsSndGroupCompProc; + + +BOOL plPhysicsSndGroupCompProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + IParamBlock2 *pb = pm->GetParamBlock(); + HWND hList = GetDlgItem( hWnd, IDC_SND_GROUP ); + HWND hAgainst = GetDlgItem( hWnd, IDC_SND_AGAINST ); + plPhysicsSndGroupComp *comp = (plPhysicsSndGroupComp *)pb->GetOwner(); + + switch( msg ) + { + case WM_INITDIALOG: + { + IInitList( GetDlgItem( hWnd, IDC_SND_GROUP ), pb->GetInt( plPhysicsSndGroupComp::kRefGroup ), false ); + IInitList( GetDlgItem( hWnd, IDC_SND_AGAINST ), -1, true ); + + int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 ); + if( idx != CB_ERR ) + { + idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 ); + IUpdateBtns( hWnd, idx, comp ); + } + } + + return TRUE; + + case WM_COMMAND: + if( HIWORD( wParam ) == CBN_SELCHANGE ) + { + if( LOWORD( wParam ) == IDC_SND_GROUP ) + { + int idx = SendMessage( hList, CB_GETCURSEL, 0, 0 ); + if( idx != CB_ERR ) + { + pb->SetValue( (ParamID)plPhysicsSndGroupComp::kRefGroup, 0, (int)SendMessage( hList, CB_GETITEMDATA, idx, 0 ) ); + } + return true; + } + else if( LOWORD( wParam ) == IDC_SND_AGAINST ) + { + int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 ); + if( idx != CB_ERR ) + { + idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 ); + IUpdateBtns( hWnd, idx, comp ); + } + } + } + else if( LOWORD( wParam ) == IDC_SND_CLEAR_IMPACT ) + { + int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 ); + if( idx != CB_ERR ) + { + idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 ); + if( idx == -1 ) + { + pb->Resize( (ParamID)plPhysicsSndGroupComp::kRefImpactSounds, 0 ); + } + else + ISet( pb, plPhysicsSndGroupComp::kRefImpactSounds, idx, nil ); + IUpdateBtns( hWnd, idx, comp ); + } + } + else if( LOWORD( wParam ) == IDC_SND_CLEAR_SLIDE ) + { + int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 ); + if( idx != CB_ERR ) + { + idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 ); + if( idx == -1 ) + pb->Resize( (ParamID)plPhysicsSndGroupComp::kRefSlideSounds, 0 ); + else + ISet( pb, plPhysicsSndGroupComp::kRefSlideSounds, idx, nil ); + IUpdateBtns( hWnd, idx, comp ); + } + } + else if( LOWORD( wParam ) == IDC_SND_IMPACT ) + { + int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 ); + if( idx != CB_ERR ) + { + idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 ); + + std::vector cids; + cids.push_back( RANDOM_SOUND_COMPONENT_ID ); + if( plPick::NodeRefKludge( pb, plPhysicsSndGroupComp::kRefDummyPickNode, &cids, true, false ) ) + ISet( comp->GetParamBlock( 0 ), plPhysicsSndGroupComp::kRefImpactSounds, idx, pb->GetINode( plPhysicsSndGroupComp::kRefDummyPickNode ) ); + + IUpdateBtns( hWnd, idx, comp ); + } + } + else if( LOWORD( wParam ) == IDC_SND_SLIDE ) + { + int idx = SendMessage( hAgainst, CB_GETCURSEL, 0, 0 ); + if( idx != CB_ERR ) + { + idx = (int)SendMessage( hAgainst, CB_GETITEMDATA, idx, 0 ); + + std::vector cids; + cids.push_back( RANDOM_SOUND_COMPONENT_ID ); + if( plPick::NodeRefKludge( pb, plPhysicsSndGroupComp::kRefDummyPickNode, &cids, true, false ) ) + ISet( pb, plPhysicsSndGroupComp::kRefSlideSounds, idx, pb->GetINode( plPhysicsSndGroupComp::kRefDummyPickNode ) ); + + IUpdateBtns( hWnd, idx, comp ); + } + } + + } + + return FALSE; +} + +// Simple accessor +class plPhysicsSndGroupAccessor : public PBAccessor +{ +public: + void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t ) + { + if( id == plPhysicsSndGroupComp::kRefImpactSounds || id == plPhysicsSndGroupComp::kRefSlideSounds ) + { + plPhysicsSndGroupComp *comp = (plPhysicsSndGroupComp *)owner; + comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED ); + } + } +}; +static plPhysicsSndGroupAccessor glPhysicsSndGroupAccessor; + +//Max desc stuff necessary below. +CLASS_DESC(plPhysicsSndGroupComp, gPhysSndGrpDesc, "Physics Sound Group", "PhysSndGroup", COMP_TYPE_AUDIO, SOUND_PHYS_COMP_ID) + +ParamBlockDesc2 gPhysSndGrpBk +( + plComponent::kBlkComp, _T("PhysSndGroup"), 0, &gPhysSndGrpDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SOUNDPHYS, IDS_COMP_SOUNDPHYS, 0, 0, &gPhysicsSndGroupCompProc, + + plPhysicsSndGroupComp::kRefGroup, _T("Group"), TYPE_INT, 0, 0, + p_default, (int)plPhysicalSndGroup::kNone, + end, + + plPhysicsSndGroupComp::kRefDummyPickNode, _T( "Dummy" ), TYPE_INODE, 0, 0, + end, + + plPhysicsSndGroupComp::kRefImpactSounds, _T("Impacts"), TYPE_INODE_TAB, 0, 0, 0, +// p_accessor, glPhysicsSndGroupAccessor, + end, + + plPhysicsSndGroupComp::kRefSlideSounds, _T("Slides"), TYPE_INODE_TAB, 0, 0, 0, +// p_accessor, glPhysicsSndGroupAccessor, + end, + + end +); + +plPhysicsSndGroupComp::plPhysicsSndGroupComp() +{ + fClassDesc = &gPhysSndGrpDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plPhysicsSndGroupComp::Convert( plMaxNode *node, plErrorMsg *pErrMsg ) +{ + plMaxNode *pNode; + plKey RandSoundKey; + + // Try to grab the SI from the current scene object. This'll have the pointer we want + plSceneObject *obj = node->GetSceneObject(); + if( obj != nil ) + { + const plSimulationInterface* si = obj->GetSimulationInterface(); + if (si) + { + // Create a new sound group + plPhysicalSndGroup *grp = TRACKED_NEW plPhysicalSndGroup( fCompPB->GetInt( (ParamID)kRefGroup ) ); + hsgResMgr::ResMgr()->NewKey( IGetUniqueName( node ), grp, node->GetLocation(), node->GetLoadMask() ); + + // Convert each sound into a plWin32StaticSound and store onto the sound group + int i; + for( i = 0; i < fCompPB->Count( (ParamID)kRefImpactSounds ); i++ ) + { + plMaxNode *targNode = (plMaxNode *)fCompPB->GetINode( (ParamID)kRefImpactSounds, 0, i ); + if( targNode != nil ) + { + plComponentBase *comp = targNode->ConvertToComponent(); + if( comp != nil ) + { + // Check root node for random sound component + RandSoundKey = plAudioComp::GetRandomSoundKey( comp, node ); + if(RandSoundKey) + grp->AddImpactSound( i, RandSoundKey ); + + // If not in root node check children + else + { + for(int j = 0; j < node->NumChildren(); j++) + { + pNode = (plMaxNode *)node->GetChildNode(j); + RandSoundKey = plAudioComp::GetRandomSoundKey( comp, pNode ); + if(!RandSoundKey) continue; + + grp->AddImpactSound( i, RandSoundKey ); + break; + } + } + } + } + } + + for( i = 0; i < fCompPB->Count( (ParamID)kRefSlideSounds ); i++ ) + { + plMaxNode *targNode = (plMaxNode *)fCompPB->GetINode( (ParamID)kRefSlideSounds, 0, i ); + if( targNode != nil ) + { + plComponentBase *comp = targNode->ConvertToComponent(); + if( comp != nil ) + { + // Check root node for random sound component + RandSoundKey = plAudioComp::GetRandomSoundKey( comp, node ); + if(RandSoundKey) + grp->AddSlideSound( i, RandSoundKey ); + else + { + for(int j = 0; j < node->NumChildren(); j++) + { + pNode = (plMaxNode *)node->GetChildNode(j); + RandSoundKey = plAudioComp::GetRandomSoundKey( comp, pNode ); + if(!RandSoundKey) continue; + + grp->AddSlideSound( i, RandSoundKey ); + break; + } + } + } + } + } + + // Attach the sound group to the physical + hsgResMgr::ResMgr()->AddViaNotify( grp->GetKey(), TRACKED_NEW plGenRefMsg( si->GetPhysical()->GetKey(), plRefMsg::kOnCreate, 0, plPXPhysical::kPhysRefSndGroup ), plRefFlags::kActiveRef ); + } + } + + return true; +} + +hsBool plPhysicsSndGroupComp::PreConvert( plMaxNode *pNode, plErrorMsg *pErrMsg ) +{ + return true; +} + +hsBool plPhysicsSndGroupComp::SetupProperties( plMaxNode *pNode, plErrorMsg *pErrMsg ) +{ + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAudioComponents.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAudioComponents.h new file mode 100644 index 00000000..f98bcc9b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAudioComponents.h @@ -0,0 +1,192 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#define SOUND_3D_COMPONENT_ID Class_ID(0x1be5543f, 0x746f3a97) +#define BGND_MUSIC_COMPONENT_ID Class_ID(0x16b5b2a, 0x33c75095) +#define RANDOM_SOUND_COMPONENT_ID Class_ID(0x35033e37, 0x499568fb) +#define GUI_SOUND_COMPONENT_ID Class_ID(0x446f1ada, 0x6c594a8d) +#define EAX_LISTENER_COMPONENT_ID Class_ID(0x514f4b0a, 0x24672153) +#define SOUND_PHYS_COMP_ID Class_ID(0x29415900, 0x1ade37a5) + +#include "../pnKeyedObject/plKey.h" +#include "../../AssetMan/PublicInterface/AssManBaseTypes.h" +#include "hsTemplates.h" + +class plComponentBase; +class plMaxNode; +class plSoundBuffer; +class plSound; +class plAudioBaseComponentProc; + +namespace plAudioComp +{ + // Can't call these until after PreConvert + int GetSoundModIdx(plComponentBase *comp, plMaxNode *node); + plKey GetRandomSoundKey(plComponentBase *comp, plMaxNode *node); + + bool IsSoundComponent(plComponentBase *comp); + bool IsLocalOnly( plComponentBase *comp ); +} + +class plBaseSoundEmitterComponent : public plComponent +{ + public: + plBaseSoundEmitterComponent(); + virtual ~plBaseSoundEmitterComponent(); + + RefTargetHandle Clone(RemapDir &remap); + + IOResult Save(ISave* isave); + IOResult Load(ILoad* iload); + + enum WhichSound + { + kBaseSound, + kCoverSound + }; + + virtual void SetSoundAssetId( WhichSound which, jvUniqueId assetId, const TCHAR *fileName ); + virtual jvUniqueId GetSoundAssetID( WhichSound which ); + virtual TCHAR *GetSoundFileName( WhichSound which ); + + // Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg, Class_ID classToConvert ); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) = 0; + + hsBool DeInit( plMaxNode *node, plErrorMsg *pErrMsg ); + + virtual hsBool ConvertGrouped( plMaxNode *baseNode, hsTArray &groupArray, plErrorMsg *pErrMsg ) { return false; } + + int GetSoundIdx(plMaxNode *node) + { + if (fIndices.find(node) != fIndices.end()) + return fIndices[node]; + return -1; + } + + static plSoundBuffer *GetSourceBuffer( const char *fileName, plMaxNode *node, UInt32 srcBufferFlags ); + static hsBool LookupLatestAsset( const char *waveName, char *retPath, plErrorMsg *errMsg ); + + virtual void UpdateSoundFileSelection( void ); + + // Loads the given combo box with category selections and sets the ParamID for the category parameter. + // Returns false if there are no categories to choose for this component + virtual hsBool UpdateCategories( HWND dialogBox, int &categoryID, ParamID ¶mID ); + + virtual hsBool IsLocalOnly( void ) const { return true; } + + // Virtuals for handling animated volumes + virtual hsBool AddToAnim( plAGAnim *anim, plMaxNode *node ); + virtual bool AllowUnhide() { return fAllowUnhide; } + + // Flags this component to create a grouped sound instead of a normal sound + void SetCreateGrouped( plMaxNode *baseNode, int commonSoundIdx ); + + // Grabs the current sound volume + virtual hsScalar GetSoundVolume( void ) const; + + protected: + jvUniqueId fSoundAssetId; // used for the AssMan + jvUniqueId fCoverSoundAssetID; + hsBool fAssetsUpdated; + + friend class plAudioBaseComponentProc; + + static UInt32 fWarningFlags; + bool fAllowUnhide; + + hsBool fCreateGrouped; + + enum Warnings + { + kSrcBufferInvalid = 1, + kCoverBufferInvalid, + kCoverBufferWrongFormat, + kMergeSourceFormatMismatch + }; + + void IUpdateAssets( void ); + + static void IShowError( UInt32 type, const char *errMsg, const char *nodeName, plErrorMsg *pErrMsg ); + + std::map fIndices; + std::map fValidNodes; + + bool IValidate(plMaxNode *node, plErrorMsg *pErrMsg); + + void IConvertOldVolume( void ); + float IGetDigitalVolume( void ) const; // In scale 0. to 1., not in db + void IGrabFadeValues( plSound *sound ); + void IGrabSoftRegion( plSound *sound, plErrorMsg *pErrMsg ); + void IGrabEAXParams( plSound *sound, plErrorMsg *pErrMsg ); + + virtual UInt32 ICalcSourceBufferFlags() const; + + static plSoundBuffer *IGetSourceBuffer( const char *fileName, plMaxNode *srcNode, UInt32 srcBufferFlags ); + + plSoundBuffer *IProcessSourceBuffer( plMaxNode *maxNode, plErrorMsg *errMsg ); + + virtual hsBool IAllowStereoFiles( void ) const { return true; } + virtual hsBool IAllowMonoFiles( void ) const { return true; } + virtual hsBool IHasWaveformProps( void ) const { return true; } + + // Returns pointers to arrays defining the names and konstants for the supported categories + // for this component. Name array should have an extra "" entry at the end. Returns false if none supported + virtual hsBool IGetCategoryList( char **&catList, int *&catKonstantList ) { return false; } + + void ISetBaseParameters( plSound *destSound, plErrorMsg *pErrMsg ); +}; + +class plRandomSoundMod; + +class plRandomSoundComponent : public plComponent +{ +protected: + hsBool ICheckForSounds(plMaxNode* node); + +public: + std::map fSoundMods; + + plRandomSoundComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + void RemoveSound(int index); + void AddSelectedSound(); + int GetCurGroupIdx(); + int GetStartIndex(int group); + int GetEndIndex(int group); + +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoComponent.cpp new file mode 100644 index 00000000..afde26fb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoComponent.cpp @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +// Don't delete this, I use it for testing -Colin +#if 0 + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "plAutoUIComp.h" + +#include "plActivatorComponent.h" + +class plAutoComponent : public plComponent +{ +public: + plAutoComponent(); + + hsBool Convert(plMaxNode *node, plErrorMsg *msg); +}; + +AUTO_CLASS_DESC(plAutoComponent, gAutoDesc, "Auto", "Auto", "Test", Class_ID(0x21807fcf, 0x156e2218)) + +plAutoUIComp *gAutoUI; + +void DummyCode() +{ + gAutoUI = TRACKED_NEW plAutoUIComp(&gAutoDesc); + + gAutoUI->AddCheckBox(0, "test", "Test", true); + gAutoUI->AddFloatSpinner(1, "t2", "T2", 0.5, 0.f, 100.f); + gAutoUI->AddEditBox(2, "blah", "Blah", "Test String", 5); + gAutoUI->AddPickNode(3, "pick", "Pick"); + + std::vector cids; + cids.push_back(ACTIVATOR_CID); + cids.push_back(RESPONDER_CID); + gAutoUI->AddPickNode(4, "pick2", "Pick2", &cids); +} + +plAutoComponent::plAutoComponent() +{ + fClassDesc = &gAutoDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plAutoComponent::Convert(plMaxNode *node, plErrorMsg *msg) +{ + hsBool c1 = gAutoUI->GetCheckBox(0, this); + TSTR str = gAutoUI->GetEditBox(2, this); + + for (int i = 0; i < gAutoUI->Count(3, this); i++) + INode *node = gAutoUI->GetPickNode(3, this, i); + + return true; +} + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBase.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBase.cpp new file mode 100644 index 00000000..164b7dac --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBase.cpp @@ -0,0 +1,560 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plAutoUIBase.h" + +#include "max.h" +#include "iparamb2.h" + +#include "plAutoUIParams.h" +#include "hsUtils.h" +#include "resource.h" + +#include "plGUICompClassIDs.h" + +plAutoUIBase::plAutoUIBase() : + fhDlg(nil), fDesc(nil), fPBlock(nil), fName(nil), fhRollup(nil) +{ +} + +plAutoUIBase::~plAutoUIBase() +{ + if (fDesc) + { + // The internal names are just pointers Max keeps to OUR copy of the string. + // We'll free them here to prevent leaks + UInt16 count = fDesc->Count(); + for (UInt16 i = 0; i < count; i++) + { + ParamID id = fDesc->IndextoID(i); + ParamDef& def = fDesc->GetParamDef(id); + + char *name = def.int_name; + def.int_name = nil; + delete [] name; + + if (def.type == TYPE_STRING) + { + char *defVal = def.def.s; + def.def.s = nil; + delete [] defVal; + } + } + + delete fDesc; + fDesc = nil; + } + + UInt32 count = fParams.size(); + for (UInt32 i = 0; i < count; i++) + delete fParams[i]; + fParams.clear(); + + delete [] fName; + fName = nil; +} + +char *plAutoUIBase::IMakeScriptName(const char *fullName) +{ + if (!fullName) + return nil; + + char buf[256]; + buf[0] = '\0'; + char *bufptr = buf; + + while (*fullName != '\0') + { + if (isalpha(*fullName) || isdigit(*fullName)) + { + *bufptr = *fullName; + bufptr++; + } + + fullName++; + } + + *bufptr = '\0'; + + if (buf[0] == '\0') + return nil; + + return hsStrcpy(buf); +} + +//////////////////////////////////////////////////////////////////////////////// +// Setup control +// + +void plAutoUIBase::AddCheckBox(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, hsBool def) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_BOOL, 0, 0, + p_default, def, end, + end); + plAutoUIParam* param = TRACKED_NEW plCheckBoxParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddFloatSpinner(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, hsScalar def, hsScalar min, hsScalar max) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_FLOAT, 0, 0, + p_default, def, + p_range, min, max, + end, + end); + plAutoUIParam* param = TRACKED_NEW plSpinnerParam(id, name, true); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddIntSpinner(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, int def, int min, int max) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INT, 0, 0, + p_default, def, + p_range, min, max, + end, + end); + plAutoUIParam* param = TRACKED_NEW plSpinnerParam(id, name, false); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddEditBox(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, const char *def, int lines) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_STRING, 0, 0, + p_default, def ? hsStrcpy(def) : nil, end, + end); + plAutoUIParam* param = TRACKED_NEW plEditParam(id, name, lines); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickNodeList(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, std::vector* filter) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE_TAB, 0, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickListParam(id, name, filter); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickNodeButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, std::vector* filter, bool canConvertToType) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickButtonParam(id, name, filter, canConvertToType); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickComponentButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, std::vector* filter, bool canConvertToType) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickComponentButtonParam(id, name, filter, canConvertToType); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickComponentList(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, std::vector* filter) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE_TAB, 0, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickComponentListParam(id, name, filter); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickActivatorButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickActivatorButtonParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickActivatorList(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE_TAB, 0, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickActivatorListParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickDynamicTextButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_REFTARG, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickDynamicTextButtonParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickGUIDialogButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickSingleComponentButtonParam(id, name,plAutoUIParam::kTypeGUIDialog,GUI_DIALOG_COMP_CLASS_ID); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickExcludeRegionButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickExcludeRegionButtonParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickWaterComponentButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickWaterComponentButtonParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickSwimCurrentInterfaceButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickSwimCurrentInterfaceButtonParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickClusterComponentButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickClusterComponentButtonParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickAnimationButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickAnimationButtonParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickBehaviorButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickBehaviorButtonParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickMaterialButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_REFTARG, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickMaterialButtonParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickMaterialAnimationButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_REFTARG, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickMaterialAnimationButtonParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickGUIPopUpMenuButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickSingleComponentButtonParam(id, name,plAutoUIParam::kTypeGUIPopUpMenu,GUI_MENUANCHOR_CLASSID); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickGUISkinButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickSingleComponentButtonParam(id, name,plAutoUIParam::kTypeGUISkin,GUI_SKIN_CLASSID); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddDropDownList(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, std::vector* options) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_STRING, 0, 0, + p_default, nil, end, + end); + plAutoUIParam* param = TRACKED_NEW plDropDownListParam(id, name, options); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +void plAutoUIBase::AddPickGrassComponentButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates) +{ + char *scriptNameNew = scriptName ? hsStrcpy(scriptName) : IMakeScriptName(name); + + fDesc->AddParam(id, scriptNameNew, TYPE_INODE, 0, 0, + end, + end); + plAutoUIParam* param = TRACKED_NEW plPickGrassComponentButtonParam(id, name); + param->SetVisInfo(vid, vstates); + fParams.push_back(param); +} + +BOOL CALLBACK plAutoUIBase::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + plAutoUIBase *pthis = NULL; + if (msg == WM_INITDIALOG) + { + SetWindowLong(hDlg, GWL_USERDATA, lParam); + pthis = (plAutoUIBase*)lParam; + } + else + pthis = (plAutoUIBase*)GetWindowLong(hDlg, GWL_USERDATA); + + return pthis->DlgProc(hDlg, msg, wParam, lParam); +} + +#define WM_SIZE_PANEL WM_APP+1 + +BOOL plAutoUIBase::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_INITDIALOG) + { + fhDlg = hDlg; + ICreateControls(); + + PostMessage(fhDlg, WM_SIZE_PANEL, 0, 0); + } + + UInt32 count = fParams.size(); + for (UInt32 i = 0; i < count; i++) + { + if (fParams[i]->IsMyMessage(msg, wParam, lParam, fPBlock)) + { + if (fParams[i]->GetParamType() == plAutoUIParam::kTypeDropDownList && HIWORD(wParam) == CBN_SELENDOK) + { + plDropDownListParam* ddl = (plDropDownListParam*)fParams[i]; + ParamID id = ddl->GetID(); + std::string str = ddl->GetString(fPBlock); + int yOffset = 10; + + // We now have the id and current state of the drop-down list that changed + // so now we need to update the visible state of the controls + for (UInt32 idx = 0; idx < fParams.size(); idx++) + { + plAutoUIParam* par = fParams[idx]; + + if (par->CheckVisibility(id, str)) + { + par->Show(yOffset); + yOffset += par->GetHeight() + 5; + } + else + { + par->Hide(); + } + } + + IRollupWindow *rollup = GetCOREInterface()->GetCommandPanelRollup(); + int index = rollup->GetPanelIndex(fhDlg); + + if (index >= 0) + rollup->SetPageDlgHeight(index, yOffset); + + InvalidateRect(fhDlg, NULL, TRUE); + } + return TRUE; + } + } + + // During init but after everything else we want to update the visibility + if (msg == WM_SIZE_PANEL) + { + // Ok, this sucks but I don't know of a better way at this point + // We need to intialize the visible state of the controls, and the only way to do this + // is by looping through and finding all of the drop-down lists + for (UInt32 i = 0; i < fParams.size(); i++) + { + if (fParams[i]->GetParamType() == plAutoUIParam::kTypeDropDownList) + { + plDropDownListParam* ddl = (plDropDownListParam*)fParams[i]; + ParamID id = ddl->GetID(); + std::string str = ""; + const char* cstr = ddl->GetString(fPBlock); + int yOffset = 10; + + if (cstr) + str = cstr; + + // We now have the id and current state of the drop-down list that changed + // so now we need to update the visible state of the controls + for (UInt32 idx = 0; idx < fParams.size(); idx++) + { + if (fParams[idx]->CheckVisibility(id, str)) + { + fParams[idx]->Show(yOffset); + yOffset += fParams[idx]->GetHeight() + 5; + } + else + { + fParams[idx]->Hide(); + } + } + + IRollupWindow *rollup = GetCOREInterface()->GetCommandPanelRollup(); + int index = rollup->GetPanelIndex(fhDlg); + + if (index >= 0) + rollup->SetPageDlgHeight(index, yOffset); + + InvalidateRect(fhDlg, NULL, TRUE); + } + } + + return TRUE; + } + + return FALSE; +} + +void plAutoUIBase::ICreateControls() +{ + int yOffset = 10; + + UInt32 count = fParams.size(); + for (UInt32 i = 0; i < count; i++) + yOffset = fParams[i]->Create(fhDlg, fPBlock, yOffset)+5; + //yOffset = fParams[i]->CreateControls(fhDlg, fPBlock, yOffset)+5; + + RECT rect; + GetWindowRect(fhDlg, &rect); + MoveWindow(fhDlg, rect.left, rect.top, rect.right - rect.left, yOffset+5, FALSE); +} + +void plAutoUIBase::CreateAutoRollup(IParamBlock2 *pb) +{ + fPBlock = pb; + + // Don't bother putting up a rollup if there are no params + if (pb->NumParams() == 0) + return; + + fhRollup = GetCOREInterface()->AddRollupPage(hInstance, + MAKEINTRESOURCE(IDD_COMP_AUTO), + ForwardDlgProc, + (fName ? fName : (char*)fDesc->cd->ClassName()), + (LPARAM)this); +} + +void plAutoUIBase::DestroyAutoRollup() +{ + if (fhDlg) + { + GetCOREInterface()->DeleteRollupPage(fhDlg); + fhDlg = nil; + } + + fPBlock = nil; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBase.h new file mode 100644 index 00000000..68dd0ff0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBase.h @@ -0,0 +1,123 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAutoUIBase_h_inc +#define plAutoUIBase_h_inc + +//#include "max.h" +//#include "plComponentBase.h" +//#include "plComponentReg.h" + +#include "hsTypes.h" +#include "hsWindows.h" +#include + +class ParamBlockDesc2; +class IParamBlock2; +class Class_ID; + +class plAutoUIParam; +class plAutoUIClassDesc; + +class plAutoUIBase +{ +protected: + HWND fhDlg; + std::vector fParams; + + ParamBlockDesc2 *fDesc; + IParamBlock2 *fPBlock; + char *fName; + + HWND fhRollup; + + plAutoUIBase(); + +public: + virtual ~plAutoUIBase(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Add control (and associated data). These should only be called at DLL load time. + // The id parameter must be unique, and while you can add and delete id's, you can + // never reuse an id. + // + // 'scriptName' is a MaxScript visible name. Here's what the help file says: + // "They should begin with an alpha character, have only alphanumerics, and have no + // spaces, punctuations, etc. The convention for multi-word names is to use + // studly-caps, eg, paintRadius." + // (Note: if this is nil, one will be generated from 'name'.) + // + // 'name' is the name that will show up in the user interface (a copy is made, so you + // can free the pointer after this function returns if it was allocated) + // + void AddCheckBox (Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, + hsBool def=false); + void AddFloatSpinner(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, + hsScalar def=0.f, hsScalar min=0.f, hsScalar max=1.f); + void AddIntSpinner (Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, + int def=1, int min=0, int max=1); + void AddEditBox (Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, + const char *def=nil, int lines=1); + void AddPickNodeList(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, + std::vector* filter=nil); + void AddPickNodeButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, + std::vector* filter=nil, bool canConvertToType=false); + void AddPickComponentButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, + std::vector* filter=nil, bool canConvertToType=false); + void AddPickComponentList(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, + std::vector* filter=nil); + void AddPickActivatorList(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickActivatorButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickDynamicTextButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickGUIDialogButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickExcludeRegionButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickAnimationButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickBehaviorButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickMaterialButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickMaterialAnimationButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickWaterComponentButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickSwimCurrentInterfaceButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickClusterComponentButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + + void AddPickGUIPopUpMenuButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + void AddPickGUISkinButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + + void AddDropDownList(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates, std::vector* options = nil); + void AddPickGrassComponentButton(Int16 id, const char *scriptName, const char *name, int vid, std::vector* vstates); + + void CreateAutoRollup(IParamBlock2 *pb); + void DestroyAutoRollup(); + + const char *GetName() { return fName; } + +protected: + char *IMakeScriptName(const char *fullName); + void ICreateControls(); + + static BOOL CALLBACK ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); +}; + +#endif // plAutoUIBase_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBlock.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBlock.cpp new file mode 100644 index 00000000..a4216aa6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBlock.cpp @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plAutoUIBlock.h" + +#include "max.h" +#include "iparamb2.h" + +#include "plComponentReg.h" +#include "hsUtils.h" +#include "resource.h" + +plAutoUIBlock::plAutoUIBlock(plComponentClassDesc *cd, int blockID, const char *name, int version) +{ + fName = hsStrcpy(name); + fVersion = version; + fDesc = TRACKED_NEW ParamBlockDesc2(blockID, "Auto", IDS_COMP_AUTO, cd, 0, end); + fIsMultiModifier = false; +} + +int plAutoUIBlock::NumParams() +{ + return fParams.size(); +} + +plAutoUIParam *plAutoUIBlock::GetParam(int idx) +{ + if (idx < fParams.size()) + return fParams[idx]; + + hsAssert(0, "Param index out of range"); + return nil; +} + +IParamBlock2 *plAutoUIBlock::CreatePB() +{ + return CreateParameterBlock2(fDesc, nil); +} + +int plAutoUIBlock::GetBlockID() +{ + return fDesc->ID; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBlock.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBlock.h new file mode 100644 index 00000000..88415f34 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIBlock.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAutoUIBase.h" + +class plComponentClassDesc; + +class plAutoUIBlock : public plAutoUIBase +{ +protected: + int fVersion; + bool fIsMultiModifier; + +public: + plAutoUIBlock(plComponentClassDesc *cd, int blockID, const char *name, int version); + + void SetMultiModifierFlag(bool flag) { fIsMultiModifier=flag; } + int NumParams(); + plAutoUIParam *GetParam(int idx); + + IParamBlock2 *CreatePB(); + + int GetBlockID(); + + int GetVersion() { return fVersion; } + + bool IsMultiModifier() { return fIsMultiModifier; } +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIComp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIComp.cpp new file mode 100644 index 00000000..5ca36335 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIComp.cpp @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plAutoUIComp.h" + +#include + +#include "resource.h" +#include "notify.h" + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +plAutoUIComp::plAutoUIComp(plAutoUIClassDesc *cd) +{ + fDesc = TRACKED_NEW ParamBlockDesc2(plComponentBase::kBlkComp, "Auto", IDS_COMP_AUTO, cd, P_AUTO_CONSTRUCT, plComponentBase::kRefComp, end); + cd->autoComp = this; +} + +//////////////////////////////////////////////////////////////////////////////// +// Get value from control +// + +hsBool plAutoUIComp::GetCheckBox(Int16 id, plComponentBase *comp) +{ + IParamBlock2 *pblock = comp->GetParamBlockByID(plComponentBase::kBlkComp); + return pblock->GetInt(id); +} + +hsScalar plAutoUIComp::GetFloatSpinner(Int16 id, plComponentBase *comp) +{ + IParamBlock2 *pblock = comp->GetParamBlockByID(plComponentBase::kBlkComp); + return pblock->GetFloat(id); +} + +int plAutoUIComp::GetIntSpinner(Int16 id, plComponentBase *comp) +{ + IParamBlock2 *pblock = comp->GetParamBlockByID(plComponentBase::kBlkComp); + return pblock->GetInt(id); +} + +TSTR plAutoUIComp::GetEditBox(Int16 id, plComponentBase *comp) +{ + IParamBlock2 *pblock = comp->GetParamBlockByID(plComponentBase::kBlkComp); + return pblock->GetStr(id); +} + +INode *plAutoUIComp::GetPickNode(Int16 id, plComponentBase *comp, int idx) +{ + IParamBlock2 *pblock = comp->GetParamBlockByID(plComponentBase::kBlkComp); + return pblock->GetINode(id, 0, idx); +} + +int plAutoUIComp::Count(Int16 id, plComponentBase *comp) +{ + IParamBlock2 *pblock = comp->GetParamBlockByID(plComponentBase::kBlkComp); + return pblock->Count(id); +} + +// + +void plAutoUIComp::BeginEditParams(IObjParam *ip, ReferenceMaker *obj, ULONG flags, Animatable *prev) +{ + CreateAutoRollup(((plComponentBase*)obj)->GetParamBlockByID(plComponentBase::kBlkComp)); +} + +void plAutoUIComp::EndEditParams(IObjParam *ip, ReferenceMaker *obj, ULONG flags, Animatable *prev) +{ + DestroyAutoRollup(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIComp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIComp.h new file mode 100644 index 00000000..ed9717f2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIComp.h @@ -0,0 +1,102 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plAutoUIBase.h" + +#include "max.h" +#include "plComponentBase.h" +#include "plComponentReg.h" + +class plAutoUIComp : public plAutoUIBase +{ +public: + plAutoUIComp(plAutoUIClassDesc *cd); + + ///////////////////////////////////////////////////////////////////////////////////// + // Get the value of a control. Pass in the id and your 'this' pointer. + // + hsBool GetCheckBox(Int16 id, plComponentBase *comp); + hsScalar GetFloatSpinner(Int16 id, plComponentBase *comp); + int GetIntSpinner(Int16 id, plComponentBase *comp); + TSTR GetEditBox(Int16 id, plComponentBase *comp); + INode* GetPickNode(Int16 id, plComponentBase *comp, int idx); + + // Get the count for a parameter that takes an index + int Count(Int16 id, plComponentBase *comp); + + ///////////////////////////////////////////////////////////////////////////////////// + // Max/internal functions + // + // Called by the ClassDesc. + void BeginEditParams(IObjParam *ip, ReferenceMaker *obj, ULONG flags, Animatable *prev); + void EndEditParams(IObjParam *ip, ReferenceMaker *obj, ULONG flags, Animatable *prev); +}; + +class plAutoUIClassDesc : public plComponentClassDesc +{ +public: + virtual bool IsAutoUI() { return true; } + virtual bool IsObsolete() { return true; } + + plAutoUIComp *autoComp; + void BeginEditParams(IObjParam *ip, ReferenceMaker* obj, ULONG flags, Animatable *prev) + { + ClassDesc2::BeginEditParams(ip, obj, flags, prev); + if (autoComp) autoComp->BeginEditParams(ip, obj, flags, prev); + } + void EndEditParams(IObjParam *ip, ReferenceMaker* obj, ULONG flags, Animatable *prev) + { + if (autoComp) autoComp->EndEditParams(ip, obj, flags, prev); + ClassDesc2::EndEditParams(ip, obj, flags, prev); + } + void CreateAutoRollup(IParamBlock2 *pb) + { + if (autoComp) + autoComp->CreateAutoRollup(pb); + } + void DestroyAutoRollup() + { + if (autoComp) + autoComp->DestroyAutoRollup(); + } +}; + +void plExternalComponentReg(ClassDesc *desc); + +#define AUTO_CLASS_DESC(classname, varname, longname, shortname, category, id) \ +class classname##ClassDesc : public plAutoUIClassDesc \ +{ \ + FUNC_CLASS_DESC(classname, longname, shortname, category, id) \ + classname##ClassDesc() { plExternalComponentReg(this); } \ +}; \ +DECLARE_CLASS_DESC(classname, varname) + +// +void ReleaseGlobals(); + +// +// Categories +// +#define COMP_TYPE_KAHLO "Kahlo" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIParams.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIParams.cpp new file mode 100644 index 00000000..0a4e7d71 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIParams.cpp @@ -0,0 +1,1864 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plAutoUIParams.h" + +#include "../MaxMain/plMaxAccelerators.h" +#include "hsUtils.h" +#include +#include "../MaxMain/plMaxNode.h" +#include "plComponentBase.h" + +plAutoUIParam::plAutoUIParam(ParamID id, const char *name) : + fID(id), fName(hsStrcpy(name)), fVisID(-1), fHeight(0) +{ +} + +plAutoUIParam::~plAutoUIParam() +{ + delete [] fName; +} + +int plAutoUIParam::Create(HWND hDlg, IParamBlock2 *pb, int yOffset) +{ + int size = fControlVec.size(); + if (size > 0) + fControlVec.clear(); + + int initialOffset = yOffset; + int finalOffset = CreateControls(hDlg, pb, yOffset); + + fHeight = finalOffset - initialOffset; + fhDlg = hDlg; + + return finalOffset; +} + +int plAutoUIParam::ISizeControl(HWND hDlg, HWND hControl, int w, int h, int y, int x) +{ + // Convert the dialog units to screen units + RECT rect; + SetRect(&rect, x, 0, w, h); + MapDialogRect(hDlg, &rect); + // Y is already in screen units + rect.top = y; + + // Resize the window + MoveWindow(hControl, rect.left, rect.top, rect.right, rect.bottom, FALSE); + + return rect.bottom; +} + +HWND plAutoUIParam::ICreateControl(HWND hDlg, const char *className, const char *wndName, DWORD style, DWORD exStyle) +{ + HWND hwnd = CreateWindowEx(exStyle, className, wndName, WS_VISIBLE | WS_CHILD | style, + 0, 0, 0, 0, hDlg, 0/*(HMENU)fDlgItemID*/, hInstance, NULL); + + fControlVec.push_back(hwnd); + + return hwnd; +} + +// By default the controls use a font that is too big. This fixes that. +void plAutoUIParam::ISetControlFont(HWND hControl) +{ + SendMessage(hControl, WM_SETFONT, (WPARAM)GetCOREInterface()->GetAppHFont(), TRUE); +} + +int plAutoUIParam::IAddStaticText(HWND hDlg, int y, const char *text) +{ + HWND hStatic = ICreateControl(hDlg, "Static", text, SS_LEFT); + int height = ISizeControl(hDlg, hStatic, 100, 8, y) + 2; + ISetControlFont(hStatic); + + return height; +} + +int plAutoUIParam::GetParamType() +{ + return kTypeNone; +} + +void plAutoUIParam::Show(int yOffset) +{ + HWND tmp; + + int ctrlOffset = 0; + RECT rect; + + for (int i = 0; i < fControlVec.size(); i++) + { + tmp = fControlVec[i]; + GetWindowRect(tmp, &rect); + + SetRect(&rect, 3, 0, (rect.right - rect.left) + 3, rect.bottom - rect.top); + //MapDialogRect(fhDlg, &rect); + // Y is already in screen units + rect.top = yOffset + ctrlOffset; + rect.bottom = rect.bottom + rect.top; + + // Resize the window + MoveWindow(tmp, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); + //MoveWindow(tmp, rect.left, yOffset + ctrlOffset, (rect.right - rect.left), (rect.bottom - rect.top), FALSE); + ctrlOffset += (rect.bottom - rect.top) + 2; + + ShowWindow(tmp, SW_SHOW); + } +} + +void plAutoUIParam::Hide() +{ + HWND tmp; + + for (int i = 0; i < fControlVec.size(); i++) + { + tmp = fControlVec[i]; + ShowWindow(tmp, SW_HIDE); + } +} + +int plAutoUIParam::GetHeight() +{ + return fHeight; +} + +void plAutoUIParam::SetVisInfo(ParamID id, std::vector* states) +{ + fVisID = id; + fVisStates = *states; +} + +bool plAutoUIParam::CheckVisibility(ParamID id, std::string state) +{ + if (fVisStates.size() == 0) + { + return true; + } + else + { + if (fVisID == id) + { + if (std::find(fVisStates.begin(), fVisStates.end(), state) != fVisStates.end()) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } + } +} + +hsBool plAutoUIParam::GetBool(IParamBlock2 *pb) +{ + hsAssert(false, "Parameter is not a bool"); + return false; +} +float plAutoUIParam::GetFloat(IParamBlock2 *pb) +{ + hsAssert(false, "Parameter is not a float"); + return -1; +} +int plAutoUIParam::GetInt(IParamBlock2 *pb) +{ + hsAssert(false, "Parameter is not an int"); + return -1; +} +const char* plAutoUIParam::GetString(IParamBlock2 *pb) +{ + hsAssert(false, "Parameter is not a string"); + return nil; +} +int plAutoUIParam::GetCount(IParamBlock2 *pb) +{ + hsAssert(false, "Parameter is not a key list"); + return -1; +} +plKey plAutoUIParam::GetKey(IParamBlock2 *pb, int idx) +{ + hsAssert(false, "Parameter is not a key"); + return nil; +} +plComponentBase *plAutoUIParam::GetComponent(IParamBlock2 *pb, int idx) +{ + hsAssert(false, "Parameter is not a component"); + return nil; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plCheckBoxParam::plCheckBoxParam(ParamID id, const char *name) : + plAutoUIParam(id, name), fhCheck(nil) +{ +} + +int plCheckBoxParam::CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset) +{ + // Create the checkbox + fhCheck = ICreateControl(hDlg, "Button", fName, BS_AUTOCHECKBOX); + yOffset += ISizeControl(hDlg, fhCheck, 90, 10, yOffset) + 2; + + // Set the check from the current value + SendMessage(fhCheck, BM_SETCHECK, pb->GetInt(fID), 0); + + return yOffset; +} + +bool plCheckBoxParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhCheck) + { + // Get the state + hsBool checked = (SendMessage(fhCheck, BM_GETCHECK, 0, 0) == BST_CHECKED); + // Set the state in the paramblock + pb->SetValue(fID, 0, checked); + return true; + } + } + + return false; +} + +int plCheckBoxParam::GetParamType() +{ + return kTypeBool; +} +hsBool plCheckBoxParam::GetBool(IParamBlock2 *pb) +{ + return pb->GetInt(fID); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plSpinnerParam::plSpinnerParam(ParamID id, const char *name, bool isFloat) : + plAutoUIParam(id, name), fIsFloat(isFloat), fhSpinner(nil) +{ +} + +int plSpinnerParam::CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset) +{ + yOffset += IAddStaticText(hDlg, yOffset, fName); + + // Create the edit box + HWND hEdit = ICreateControl(hDlg, "CustEdit"); + ISizeControl(hDlg, hEdit, 30, 10, yOffset); + + // Create the spinner (beside the edit box) + fhSpinner = ICreateControl(hDlg, "SpinnerControl"); + yOffset += ISizeControl(hDlg, fhSpinner, 8, 10, yOffset, 33) + 2; + + // Initialize the spinner + ISpinnerControl *spin = GetISpinner(fhSpinner); + ParamDef &def = pb->GetParamDef(fID); + if (fIsFloat) + { + spin->SetLimits(def.range_low.f, def.range_high.f, FALSE); + spin->SetValue(pb->GetFloat(fID), FALSE); + spin->SetScale(0.1f); + spin->LinkToEdit(hEdit, EDITTYPE_FLOAT); + } + else + { + spin->SetLimits(def.range_low.i, def.range_high.i, FALSE); + spin->SetValue(pb->GetInt(fID), FALSE); + spin->SetScale(1); + spin->LinkToEdit(hEdit, EDITTYPE_INT); + } + + ReleaseISpinner(spin); + + return yOffset; +} + +void plSpinnerParam::Show(int yOffset) +{ + yOffset += ISizeControl(fhDlg, fControlVec[0], 100, 8, yOffset) + 2; + ISizeControl(fhDlg, fControlVec[1], 30, 10, yOffset); + ISizeControl(fhDlg, fControlVec[2], 8, 10, yOffset, 33); + + ShowWindow(fControlVec[0], SW_SHOW); + ShowWindow(fControlVec[1], SW_SHOW); + ShowWindow(fControlVec[2], SW_SHOW); +} + +bool plSpinnerParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == CC_SPINNER_CHANGE)// && HIWORD(wParam) == FALSE) + { + ISpinnerControl *spin = (ISpinnerControl*)lParam; + HWND hSpinner = spin->GetHwnd(); + if (hSpinner == fhSpinner) + { + // Set the state + if (fIsFloat) + pb->SetValue(fID, 0, spin->GetFVal()); + else + pb->SetValue(fID, 0, spin->GetIVal()); + return true; + } + } + + return false; +} + +int plSpinnerParam::GetParamType() +{ + return fIsFloat ? kTypeFloat : kTypeInt; +} +float plSpinnerParam::GetFloat(IParamBlock2 *pb) +{ + return (fIsFloat ? pb->GetFloat(fID) : -1); +} +int plSpinnerParam::GetInt(IParamBlock2 *pb) +{ + return (!fIsFloat ? pb->GetInt(fID) : -1); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plEditParam::plEditParam(ParamID id, const char *name, int lines) : + plAutoUIParam(id, name), fhEdit(nil), fLines(lines) +{ +} + +int plEditParam::CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset) +{ + yOffset += IAddStaticText(hDlg, yOffset, fName); + + // + // Create the edit box + // + DWORD flags = ES_AUTOHSCROLL | ES_LEFT | WS_BORDER; + // If this edit box has more than one line, add the multiline flags + if (fLines > 1) + flags |= ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN; + + fhEdit = ICreateControl(hDlg, "Edit", nil, flags, WS_EX_CLIENTEDGE); + yOffset += ISizeControl(hDlg, fhEdit, 100, 5 + 8*fLines, yOffset) + 2; + ISetControlFont(fhEdit); + + // Initialize the edit box + SetWindowText(fhEdit, pb->GetStr(fID)); + + return yOffset; +} + +void plEditParam::Show(int yOffset) +{ + yOffset += ISizeControl(fhDlg, fControlVec[0], 100, 8, yOffset) + 2; + ISizeControl(fhDlg, fControlVec[1], 100, 5 + 8*fLines, yOffset); + + ShowWindow(fControlVec[0], SW_SHOW); + ShowWindow(fControlVec[1], SW_SHOW); +} + +bool plEditParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND) + { + // Disable Max accelerators when the edit box gets focus + if (HIWORD(wParam) == EN_SETFOCUS && (HWND)lParam == fhEdit) + { + plMaxAccelerators::Disable(); + return true; + } + else if (HIWORD(wParam) == EN_KILLFOCUS && (HWND)lParam == fhEdit) + { + // Get the text from the edit and store it in the paramblock + int len = GetWindowTextLength(fhEdit)+1; + if (len > 1) + { + char *buf = TRACKED_NEW char[len]; + GetWindowText(fhEdit, buf, len); + pb->SetValue(fID, 0, buf); + delete [] buf; + } + else + pb->SetValue(fID, 0, ""); + + plMaxAccelerators::Enable(); + + return true; + } + } + + return false; +} + +int plEditParam::GetParamType() +{ + return kTypeString; +} +const char* plEditParam::GetString(IParamBlock2 *pb) +{ + return pb->GetStr(fID); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +#include "plPickNode.h" + +plPickListParam::plPickListParam(ParamID id, const char *name, std::vector* filter) : + plAutoUIParam(id, name), fhList(nil), fhAdd(nil), fhRemove(nil) +{ + if (filter) + fCIDs = *filter; +} + +int plPickListParam::CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset) +{ + yOffset += IAddStaticText(hDlg, yOffset, fName) + 2; + + // Create the listbox + fhList = ICreateControl(hDlg, "ListBox", nil, LBS_STANDARD | LBS_NOINTEGRALHEIGHT, WS_EX_CLIENTEDGE); + yOffset += ISizeControl(hDlg, fhList, 100, 5+4*8, yOffset) + 2; + ISetControlFont(fhList); + + // Create the Add button + fhAdd = ICreateControl(hDlg, "Button"); + ISizeControl(hDlg, fhAdd, 48, 14, yOffset, 4); + SetWindowText(fhAdd, "Add..."); + + // Create the Remove button + fhRemove = ICreateControl(hDlg, "Button"); + yOffset += ISizeControl(hDlg, fhRemove, 48, 14, yOffset, 54); + SetWindowText(fhRemove, "Remove"); + + return yOffset; +} + +void plPickListParam::Show(int yOffset) +{ + yOffset += ISizeControl(fhDlg, fControlVec[0], 100, 8, yOffset) + 4; + yOffset += ISizeControl(fhDlg, fControlVec[1], 100, 5+4*8, yOffset) + 2; + ISizeControl(fhDlg, fControlVec[2], 48, 14, yOffset, 4); + ISizeControl(fhDlg, fControlVec[3], 48, 14, yOffset, 54); + + ShowWindow(fControlVec[0], SW_SHOW); + ShowWindow(fControlVec[1], SW_SHOW); + ShowWindow(fControlVec[2], SW_SHOW); + ShowWindow(fControlVec[3], SW_SHOW); +} + +bool plPickListParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_INITDIALOG) + { + IUpdateList(pb); + return false; + } + + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhAdd) + { + plPick::Node(pb, fID, &fCIDs, false, false); + IUpdateList(pb); + return true; + } + if ((HWND)lParam == fhRemove) + { + int sel = SendMessage(fhList, LB_GETCURSEL, 0, 0); + if (sel != -1) + { + pb->Delete(fID, sel, 1); + IUpdateList(pb); + } + return true; + } + } + + return false; +} + +void plPickListParam::IUpdateList(IParamBlock2 *pb) +{ + SendMessage(fhList, LB_RESETCONTENT, 0, 0); + + int count = pb->Count(fID); + for (int i = 0; i < count; i++) + { + INode *node = pb->GetINode(fID, 0, i); + const char *name = node ? node->GetName() : ""; + SendMessage(fhList, LB_ADDSTRING, 0, (LPARAM)name); + } +} + +int plPickListParam::GetParamType() +{ + return kTypeSceneObj; +} +int plPickListParam::GetCount(IParamBlock2 *pb) +{ + return pb->Count(fID); +} +plKey plPickListParam::GetKey(IParamBlock2 *pb, int idx) +{ + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID, 0, idx); + if (node) + return node->GetKey(); + + return nil; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +class plPickButtonParam; + +class PickNodeButtonFilter : public PickNodeCallback +{ +public: + virtual BOOL Filter(INode *node) { return TRUE; } +}; +static PickNodeButtonFilter gPickFilter; + +class PickNodeButtonMode : public PickModeCallback +{ +public: + plPickButtonParam *fParam; + IParamBlock2 *fPB; + + BOOL HitTest(IObjParam *ip, HWND hWnd, ViewExp *vpt, IPoint2 m, int flags) + { + return (ip->PickNode(hWnd,m,&gPickFilter) != NULL); + } + BOOL Pick(IObjParam *ip, ViewExp *vpt); + + PickNodeCallback *GetFilter() { return &gPickFilter; } + BOOL RightClick(IObjParam *ip, ViewExp *vpt); +}; + +static PickNodeButtonMode gPickMode; + +plPickButtonParam::plPickButtonParam(ParamID id, const char *name, std::vector* filter, bool canConvertToType) : + plAutoUIParam(id, name), fButton(nil), fCanConvertToType(canConvertToType) +{ + if (filter) + fCIDs = *filter; +} + +int plPickButtonParam::CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset) +{ + yOffset += IAddStaticText(hDlg, yOffset, fName) + 2; + + // Create the picknode button + HWND button = ICreateControl(hDlg, "CustButton"); + ISizeControl(hDlg, button, 84, 12, yOffset); + + fButton = GetICustButton(button); + + // Setup the button properties + fButton->SetType(CBT_CHECK); + fButton->SetButtonDownNotify(TRUE); + fButton->SetCheckHighlight(TRUE); + fButton->SetHighlightColor(GREEN_WASH); + + INode *node = (INode*)pb->GetReferenceTarget(fID); + if (node) + fButton->SetText(node->GetName()); + else + fButton->SetText("(none)"); + + // Create the Remove button + fhRemove = ICreateControl(hDlg, "Button"); + yOffset += ISizeControl(hDlg, fhRemove, 17, 12, yOffset, 89); + SetWindowText(fhRemove, "Clear"); + + return yOffset; +} + +void plPickButtonParam::Show(int yOffset) +{ + yOffset += ISizeControl(fhDlg, fControlVec[0], 100, 8, yOffset) + 4; + ISizeControl(fhDlg, fControlVec[1], 84, 12, yOffset); + ISizeControl(fhDlg, fControlVec[2], 17, 12, yOffset, 89); + + + ShowWindow(fControlVec[0], SW_SHOW); + ShowWindow(fControlVec[1], SW_SHOW); + ShowWindow(fControlVec[2], SW_SHOW); +} + +bool plPickButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fCIDs.size() > 0) + { + if (fButton->IsChecked()) + { + if (plPick::Node(pb, fID, &fCIDs, true, fCanConvertToType)) + { + INode *node = (INode*)pb->GetReferenceTarget(fID); + if (node) + fButton->SetText(node->GetName()); + } + fButton->SetCheck(FALSE); + } + } + else + { + if (fButton->IsChecked()) + { + gPickMode.fParam = this; + gPickMode.fPB = pb; + GetCOREInterface()->SetPickMode(&gPickMode); + } + else + { + GetCOREInterface()->ClearPickMode(); + } + } + + return true; + } + } + + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + + return false; +} + +void plPickButtonParam::SetPickNode(INode *node, IParamBlock2 *pb) +{ + if (node && pb) + { + pb->SetValue(fID, 0, (ReferenceTarget*)node); + fButton->SetText(node->GetName()); + } + + fButton->SetCheck(FALSE); +} + + +BOOL PickNodeButtonMode::Pick(IObjParam *ip, ViewExp *vpt) +{ + INode *node = vpt->GetClosestHit(); + if (node && fParam && fPB) + fParam->SetPickNode(node, fPB); + fParam = nil; + fPB = nil; + + return TRUE; +} + +BOOL PickNodeButtonMode::RightClick(IObjParam *ip, ViewExp *vpt) +{ + if (fParam && fPB) + fParam->SetPickNode(nil, nil); + fParam = nil; + fPB = nil; + + return TRUE; +} + +int plPickButtonParam::GetParamType() +{ + return kTypeSceneObj; +} +int plPickButtonParam::GetCount(IParamBlock2 *pb) +{ + return (pb->GetReferenceTarget(fID) ? 1 : 0); +} +plKey plPickButtonParam::GetKey(IParamBlock2 *pb, int idx) +{ + hsAssert(idx == 0, "Pick buttons only have one key"); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID); + if (node) + return node->GetKey(); + + return nil; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plPickComponentButtonParam::plPickComponentButtonParam(ParamID id, const char *name, std::vector* filter, bool canConvertToType) : + plPickButtonParam(id, name, filter, canConvertToType) +{ + hsAssert(filter, "Need to have a ClassID filter for pick component buttons"); +} + +int plPickComponentButtonParam::GetParamType() +{ + return kTypeComponent; +} + +plComponentBase* plPickComponentButtonParam::GetComponent(IParamBlock2 *pb, int idx) +{ + hsAssert(idx == 0, "Pick buttons only have one key"); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID); + if (node) + return node->ConvertToComponent(); + + return nil; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plPickComponentListParam::plPickComponentListParam(ParamID id, const char *name, std::vector* filter) : + plPickListParam(id, name, filter) +{ +} + +bool plPickComponentListParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + return plPickListParam::IsMyMessage(msg, wParam, lParam, pb); +} + +int plPickComponentListParam::GetParamType() +{ + return kTypeComponent; +} + +plComponentBase *plPickComponentListParam::GetComponent(IParamBlock2 *pb, int idx) +{ + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID, 0, idx); + if (node) + return node->ConvertToComponent(); + + return nil; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plPickActivatorButtonParam::plPickActivatorButtonParam(ParamID id, const char *name) : + plPickButtonParam(id, name, nil, false) +{ +} + +int plPickActivatorButtonParam::GetParamType() +{ + return kTypeActivator; +} +plComponentBase* plPickActivatorButtonParam::GetComponent(IParamBlock2 *pb, int idx) +{ + hsAssert(idx == 0, "Pick buttons only have one key"); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID); + if (node) + return node->ConvertToComponent(); + + return nil; +} + +bool plPickActivatorButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if (plPick::Activator(pb, fID, true)) + { + INode *node = (INode*)pb->GetReferenceTarget(fID); + if (node) + fButton->SetText(node->GetName()); + } + fButton->SetCheck(FALSE); + } + + return true; + } + } + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plPickActivatorListParam::plPickActivatorListParam(ParamID id, const char *name) : + plPickListParam(id, name, nil) +{ +} + +bool plPickActivatorListParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhAdd) + { + plPick::Activator(pb, fID, false); + IUpdateList(pb); + return true; + } + } + + return plPickListParam::IsMyMessage(msg, wParam, lParam, pb); +} + +int plPickActivatorListParam::GetParamType() +{ + return kTypeActivator; +} + +plComponentBase *plPickActivatorListParam::GetComponent(IParamBlock2 *pb, int idx) +{ + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID, 0, idx); + if (node) + return node->ConvertToComponent(); + + return nil; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +#include "plPickMaterialMap.h" +#include "../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h" +#include "../plSurface/plLayerInterface.h" +#include "../plGImage/plDynamicTextMap.h" + +plPickDynamicTextButtonParam::plPickDynamicTextButtonParam(ParamID id, const char *name) : + plPickButtonParam(id, name, nil, false) +{ +} + +int plPickDynamicTextButtonParam::GetParamType() +{ + return kTypeDynamicText; +} + +// temp hack to get the name of the texture map name +const char* plPickDynamicTextButtonParam::GetString(IParamBlock2 *pb) +{ + // get the plKeys based on the texture map that the DynamicText map is on + Texmap* texmap = (Texmap*)pb->GetReferenceTarget(fID); + return texmap->GetName(); +} + +int plPickDynamicTextButtonParam::GetCount(IParamBlock2 *pb) +{ + + // get the plKeys based on the texture map that the DynamicText map is on + Texmap* texmap = (Texmap*)pb->GetReferenceTarget(fID); + // make sure that there was a texmap set + if ( texmap ) + { + plPlasmaMAXLayer *maxLayer = plPlasmaMAXLayer::GetPlasmaMAXLayer( texmap ); + if( maxLayer != nil ) + { + // It's one of our Plasma layer types, which means most likely it got converted. + + // Note: if the following count is zero, it was never converted, which most likely means + // no visible geometry in the scene uses this layer. So why do you even care about it? + return maxLayer->GetNumConversionTargets(); + } + } + return 0; +} + +plKey plPickDynamicTextButtonParam::GetKey(IParamBlock2 *pb, int idx) +{ + + // get the plKeys based on the texture map that the DynamicText map is on + Texmap* texmap = (Texmap*)pb->GetReferenceTarget(fID); + // make sure that there was a texmap set + if ( texmap ) + { + plPlasmaMAXLayer *maxLayer = plPlasmaMAXLayer::GetPlasmaMAXLayer( texmap ); + if( maxLayer != nil ) + { + // make sure the index is valid + if ( idx >= 0 && idx < maxLayer->GetNumConversionTargets() ) + { + plLayerInterface *convertedLayer = maxLayer->GetConversionTarget(idx); + if ( convertedLayer ) + { + plBitmap* bmap = convertedLayer->GetTexture(); + // make sure there was a bitmap and that it is a DynamicTextMap + if ( bmap ) + return bmap->GetKey(); + } + } + } + } + + // otherwise we didn't find one, because of one of many reasons + return nil; +} + +int plPickDynamicTextButtonParam::CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset) +{ + yOffset += IAddStaticText(hDlg, yOffset, fName) + 2; + + // Create the picknode button + HWND button = ICreateControl(hDlg, "CustButton"); + ISizeControl(hDlg, button, 84, 12, yOffset); + + fButton = GetICustButton(button); + + // Setup the button properties + fButton->SetType(CBT_CHECK); + fButton->SetButtonDownNotify(TRUE); + fButton->SetCheckHighlight(TRUE); + fButton->SetHighlightColor(GREEN_WASH); + + Texmap* texmap = (Texmap*)pb->GetReferenceTarget(fID); + if (texmap) + fButton->SetText(texmap->GetName()); + else + fButton->SetText("(none)"); + + // Create the Remove button + fhRemove = ICreateControl(hDlg, "Button"); + yOffset += ISizeControl(hDlg, fhRemove, 17, 12, yOffset, 89); + SetWindowText(fhRemove, "Clear"); + + return yOffset; +} + +bool plPickDynamicTextButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if ( plPickMaterialMap::PickTexmap(pb, fID) ) + { + Texmap* texmap = (Texmap*)pb->GetReferenceTarget(fID); + if (texmap) + fButton->SetText(texmap->GetName()); + } + fButton->SetCheck(FALSE); + } + + return true; + } + } + + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + + return false; +} + + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + + +plPickSingleComponentButtonParam::plPickSingleComponentButtonParam(ParamID id, const char *name, int myType, Class_ID myClassToPick ) : + plPickButtonParam(id, name, nil, false) +{ + fClassToPick = myClassToPick; + fMyType = myType; +} + +int plPickSingleComponentButtonParam::GetParamType() +{ + return fMyType; +} + +plComponentBase* plPickSingleComponentButtonParam::GetComponent(IParamBlock2 *pb, int idx) +{ + hsAssert(idx == 0, "Pick buttons only have one key"); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID); + if (node) + return node->ConvertToComponent(); + + return nil; +} + + +bool plPickSingleComponentButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if (plPick::GenericClass(pb, fID, true,fClassToPick)) + { + INode *node = (INode*)pb->GetReferenceTarget(fID); + if (node) + fButton->SetText(node->GetName()); + } + fButton->SetCheck(FALSE); + } + + return true; + } + } + + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + + +plPickExcludeRegionButtonParam::plPickExcludeRegionButtonParam(ParamID id, const char *name) : + plPickButtonParam(id, name, nil, false) +{ +} + +int plPickExcludeRegionButtonParam::GetParamType() +{ + return kTypeExcludeRegion; +} + +plComponentBase* plPickExcludeRegionButtonParam::GetComponent(IParamBlock2 *pb, int idx) +{ + hsAssert(idx == 0, "Pick buttons only have one key"); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID); + if (node) + return node->ConvertToComponent(); + + return nil; +} + + +bool plPickExcludeRegionButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if (plPick::ExcludeRegion(pb, fID, true)) + { + INode *node = (INode*)pb->GetReferenceTarget(fID); + if (node) + fButton->SetText(node->GetName()); + } + fButton->SetCheck(FALSE); + } + + return true; + } + } + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + + return false; +} + + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + + +plPickWaterComponentButtonParam::plPickWaterComponentButtonParam(ParamID id, const char *name) : + plPickButtonParam(id, name, nil, false) +{ +} + +int plPickWaterComponentButtonParam::GetParamType() +{ + return kTypeWaterComponent; +} + +plComponentBase* plPickWaterComponentButtonParam::GetComponent(IParamBlock2 *pb, int idx) +{ + hsAssert(idx == 0, "Pick buttons only have one key"); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID); + if (node) + return node->ConvertToComponent(); + + return nil; +} + + +bool plPickWaterComponentButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if (plPick::WaterComponent(pb, fID, true)) + { + INode *node = (INode*)pb->GetReferenceTarget(fID); + if (node) + fButton->SetText(node->GetName()); + } + fButton->SetCheck(FALSE); + } + + return true; + } + } + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plPickSwimCurrentInterfaceButtonParam::plPickSwimCurrentInterfaceButtonParam(ParamID id, const char *name) : + plPickButtonParam(id, name, nil, false) +{ +} + +int plPickSwimCurrentInterfaceButtonParam::GetParamType() +{ + return kTypeSwimCurrentInterface; +} + +plComponentBase* plPickSwimCurrentInterfaceButtonParam::GetComponent(IParamBlock2 *pb, int idx) +{ + hsAssert(idx == 0, "Pick buttons only have one key"); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID); + if (node) + return node->ConvertToComponent(); + + return nil; +} + + +bool plPickSwimCurrentInterfaceButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if (plPick::Swim2DComponent(pb, fID, true)) + { + INode *node = (INode*)pb->GetReferenceTarget(fID); + if (node) + fButton->SetText(node->GetName()); + } + fButton->SetCheck(FALSE); + } + + return true; + } + } + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plPickClusterComponentButtonParam::plPickClusterComponentButtonParam(ParamID id, const char *name) : + plPickButtonParam(id, name, nil, false) +{ +} + +int plPickClusterComponentButtonParam::GetParamType() +{ + return kTypeClusterComponent; +} + +plComponentBase* plPickClusterComponentButtonParam::GetComponent(IParamBlock2 *pb, int idx) +{ + hsAssert(idx == 0, "Pick buttons only have one key"); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID); + if (node) + return node->ConvertToComponent(); + + return nil; +} + +bool plPickClusterComponentButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if (plPick::ClusterComponent(pb, fID, true)) + { + INode *node = (INode*)pb->GetReferenceTarget(fID); + if (node) + fButton->SetText(node->GetName()); + } + fButton->SetCheck(FALSE); + } + return true; + } + } + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plPickAnimationButtonParam::plPickAnimationButtonParam(ParamID id, const char *name) : + plPickButtonParam(id, name, nil, false) +{ +} + +int plPickAnimationButtonParam::GetParamType() +{ + return kTypeAnimation; +} + +plComponentBase* plPickAnimationButtonParam::GetComponent(IParamBlock2 *pb, int idx) +{ + hsAssert(idx == 0, "Pick buttons only have one key"); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID); + if (node) + return node->ConvertToComponent(); + + return nil; +} + + +bool plPickAnimationButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if (plPick::Animation(pb, fID, true)) + { + INode *node = (INode*)pb->GetReferenceTarget(fID); + if (node) + fButton->SetText(node->GetName()); + } + fButton->SetCheck(FALSE); + } + + return true; + } + } + + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + + +plPickBehaviorButtonParam::plPickBehaviorButtonParam(ParamID id, const char *name) : + plPickButtonParam(id, name, nil, false) +{ +} + +int plPickBehaviorButtonParam::GetParamType() +{ + return kTypeBehavior; +} + +plComponentBase* plPickBehaviorButtonParam::GetComponent(IParamBlock2 *pb, int idx) +{ + hsAssert(idx == 0, "Pick buttons only have one key"); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID); + if (node) + return node->ConvertToComponent(); + + return nil; +} + + +bool plPickBehaviorButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if (plPick::Behavior(pb, fID, true)) + { + INode *node = (INode*)pb->GetReferenceTarget(fID); + if (node) + fButton->SetText(node->GetName()); + } + fButton->SetCheck(FALSE); + } + + return true; + } + } + + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plPickMaterialButtonParam::plPickMaterialButtonParam(ParamID id, const char *name) : + plPickButtonParam(id, name, nil, false) +{ +} + +int plPickMaterialButtonParam::GetParamType() +{ + return kTypeMaterial; +} + +// temp hack to get the name of the texture map name +const char* plPickMaterialButtonParam::GetString(IParamBlock2 *pb) +{ + // get the plKeys based on the texture map that the DynamicText map is on + Texmap* texmap = (Texmap*)pb->GetReferenceTarget(fID); + return texmap->GetName(); +} + +int plPickMaterialButtonParam::GetCount(IParamBlock2 *pb) +{ + + // get the plKeys based on the texture map that the Texture map is on + Texmap* texmap = (Texmap*)pb->GetReferenceTarget(fID); + // make sure that there was a texmap set + if ( texmap ) + { + plPlasmaMAXLayer *maxLayer = plPlasmaMAXLayer::GetPlasmaMAXLayer( texmap ); + if( maxLayer != nil ) + { + // It's one of our Plasma layer types, which means most likely it got converted. + + // Note: if the following count is zero, it was never converted, which most likely means + // no visible geometry in the scene uses this layer. So why do you even care about it? + return maxLayer->GetNumConversionTargets(); + } + } + return 0; +} + +plKey plPickMaterialButtonParam::GetKey(IParamBlock2 *pb, int idx) +{ + + // get the plKeys based on the texture map that the Texture map is on + Texmap* texmap = (Texmap*)pb->GetReferenceTarget(fID); + // make sure that there was a texmap set + if ( texmap ) + { + plPlasmaMAXLayer *maxLayer = plPlasmaMAXLayer::GetPlasmaMAXLayer( texmap ); + if( maxLayer != nil ) + { + // make sure the index is valid + if ( idx >= 0 && idx < maxLayer->GetNumConversionTargets() ) + { + plLayerInterface *convertedLayer = maxLayer->GetConversionTarget(idx); + if ( convertedLayer ) + { + plBitmap* bmap = convertedLayer->GetTexture(); + // make sure there was a bitmap + if ( bmap ) + return bmap->GetKey(); + } + } + } + } + + // otherwise we didn't find one, because of one of many reasons + return nil; +} + +int plPickMaterialButtonParam::CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset) +{ + yOffset += IAddStaticText(hDlg, yOffset, fName) + 2; + + // Create the picknode button + HWND button = ICreateControl(hDlg, "CustButton"); + ISizeControl(hDlg, button, 84, 12, yOffset); + + fButton = GetICustButton(button); + + // Setup the button properties + fButton->SetType(CBT_CHECK); + fButton->SetButtonDownNotify(TRUE); + fButton->SetCheckHighlight(TRUE); + fButton->SetHighlightColor(GREEN_WASH); + + Texmap* texmap = (Texmap*)pb->GetReferenceTarget(fID); + if (texmap) + fButton->SetText(texmap->GetName()); + else + fButton->SetText("(none)"); + + // Create the Remove button + fhRemove = ICreateControl(hDlg, "Button"); + yOffset += ISizeControl(hDlg, fhRemove, 17, 12, yOffset, 89); + SetWindowText(fhRemove, "Clear"); + + return yOffset; +} + +bool plPickMaterialButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if ( plPickMaterialMap::PickTexmap(pb, fID) ) + { + Texmap* texmap = (Texmap*)pb->GetReferenceTarget(fID); + if (texmap) + fButton->SetText(texmap->GetName()); + } + fButton->SetCheck(FALSE); + } + + return true; + } + } + + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plPickMaterialAnimationButtonParam::plPickMaterialAnimationButtonParam(ParamID id, const char *name) : + plPickButtonParam(id, name, nil, false) +{ +} + +int plPickMaterialAnimationButtonParam::GetParamType() +{ + return kTypeMaterialAnimation; +} + +const char* plPickMaterialAnimationButtonParam::GetString(IParamBlock2 *pb) +{ + Mtl* texmap = (Mtl*)pb->GetReferenceTarget(fID); + return texmap->GetName(); +} + +int plPickMaterialAnimationButtonParam::GetCount(IParamBlock2 *pb) +{ + return fKeys.Count(); +} + +plKey plPickMaterialAnimationButtonParam::GetKey(IParamBlock2 *pb, int idx) +{ + int kcount = fKeys.Count(); + + if ( idx >= 0 && idx < kcount ) + { + return fKeys[idx]; + } + + return nil; +} + +// this is in plResponderMtl.cpp +extern int GetMatAnimModKey(Mtl* mtl, plMaxNodeBase* node, const char* segName, hsTArray& keys);; + +void plPickMaterialAnimationButtonParam::CreateKeyArray(IParamBlock2* pb) +{ + fKeys.Reset(); + + Mtl* mtl = (Mtl*)pb->GetReferenceTarget(fID); + + int bob = GetMatAnimModKey(mtl, nil, nil, fKeys); +} + +void plPickMaterialAnimationButtonParam::DestroyKeyArray() +{ + fKeys.Reset(); +} + +int plPickMaterialAnimationButtonParam::CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset) +{ + yOffset += IAddStaticText(hDlg, yOffset, fName) + 2; + + // Create the picknode button + HWND button = ICreateControl(hDlg, "CustButton"); + ISizeControl(hDlg, button, 84, 12, yOffset); + + fButton = GetICustButton(button); + + // Setup the button properties + fButton->SetType(CBT_CHECK); + fButton->SetButtonDownNotify(TRUE); + fButton->SetCheckHighlight(TRUE); + fButton->SetHighlightColor(GREEN_WASH); + + Mtl* texmap = (Mtl*)pb->GetReferenceTarget(fID); + if (texmap) + fButton->SetText(texmap->GetName()); + else + fButton->SetText("(none)"); + + // Create the Remove button + fhRemove = ICreateControl(hDlg, "Button"); + yOffset += ISizeControl(hDlg, fhRemove, 17, 12, yOffset, 89); + SetWindowText(fhRemove, "Clear"); + + return yOffset; +} + +bool plPickMaterialAnimationButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if ( Mtl* mtl = plPickMaterialMap::PickMaterial(0) ) + { + pb->SetValue(fID, 0, (ReferenceTarget*)mtl); + fButton->SetText(mtl->GetName()); + } + fButton->SetCheck(FALSE); + } + + return true; + } + } + + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plDropDownListParam::plDropDownListParam(ParamID id, const char *name, std::vector* options) : + plAutoUIParam(id, name), fhList(nil) +{ + if (options) + fOptions = *options; +} + +int plDropDownListParam::CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset) +{ + yOffset += IAddStaticText(hDlg, yOffset, fName) + 2; + + // Create the combobox + fhList = ICreateControl(hDlg, "ComboBox", nil, CBS_DROPDOWNLIST | CBS_NOINTEGRALHEIGHT | WS_VSCROLL, WS_EX_CLIENTEDGE); + ISizeControl(hDlg, fhList, 100, 100, yOffset); + yOffset += 13 + 2; + ISetControlFont(fhList); + + return yOffset + 5; +} + +bool plDropDownListParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_INITDIALOG) + { + IUpdateList(pb); + return false; + } + + if ((HWND)lParam == fhList) + { + if (HIWORD(wParam) == CBN_SELENDOK) + { + int idx = ComboBox_GetCurSel(fhList); + + if (idx >=0 && idx < fOptions.size()) + { + char* buf = TRACKED_NEW char[fOptions[idx].size() + 1]; + strcpy(buf, fOptions[idx].c_str()); + pb->SetValue(fID, 0, buf); + delete [] buf; + } + + return true; + } + } + + return false; +} + +void plDropDownListParam::IUpdateList(IParamBlock2 *pb) +{ + std::string val = ""; + int selection = -1; + + if (pb) + { + char* bob = pb->GetStr(fID); + if (bob) + val = bob; + } + + ComboBox_ResetContent(fhList); + + for (int i = 0; i < fOptions.size(); i++) + { + const char *name = fOptions[i].c_str(); + + ComboBox_AddString(fhList, name); + + if (fOptions[i] == val) + { + selection = i; + } + } + + if (selection >= 0) + { + ComboBox_SetCurSel(fhList, selection); + } +} + +int plDropDownListParam::GetParamType() +{ + return kTypeDropDownList; +} + +int plDropDownListParam::GetCount(IParamBlock2 *pb) +{ + return pb->Count(fID); +} + +const char* plDropDownListParam::GetString(IParamBlock2 *pb) +{ + return pb->GetStr(fID); +} + +void plDropDownListParam::Show(int yOffset) +{ + /* + HWND tmp; + + int ctrlOffset = 0; + RECT rect; + + for (int i = 0; i < fControlVec.size(); i++) + { + tmp = fControlVec[i]; + GetWindowRect(tmp, &rect); + + SetRect(&rect, 3, 0, (rect.right - rect.left) + 3, rect.bottom - rect.top); + //MapDialogRect(fhDlg, &rect); + // Y is already in screen units + rect.top = yOffset + ctrlOffset; + rect.bottom = rect.bottom + rect.top; + + // Resize the window + MoveWindow(tmp, rect.left, rect.top, rect.right - rect.left, (tmp == fhList) ? 100 : rect.bottom - rect.top, TRUE); + //MoveWindow(tmp, rect.left, yOffset + ctrlOffset, (rect.right - rect.left), (rect.bottom - rect.top), FALSE); + ctrlOffset += (rect.bottom - rect.top) + 2; + ShowWindow(tmp, SW_SHOW); + } + */ + + yOffset += ISizeControl(fhDlg, fControlVec[0], 100, 8, yOffset) + 4; + ISizeControl(fhDlg, fhList, 100, 100, yOffset); + + ShowWindow(fControlVec[0], SW_SHOW); + ShowWindow(fControlVec[1], SW_SHOW); +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +plPickGrassComponentButtonParam::plPickGrassComponentButtonParam(ParamID id, const char *name) : +plPickButtonParam(id, name, nil, false) +{ +} + +int plPickGrassComponentButtonParam::GetParamType() +{ + return kTypeGrassComponent; +} + +plComponentBase* plPickGrassComponentButtonParam::GetComponent(IParamBlock2 *pb, int idx) +{ + hsAssert(idx == 0, "Pick buttons only have one key"); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(fID); + if (node) + return node->ConvertToComponent(); + + return nil; +} + + +bool plPickGrassComponentButtonParam::IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) +{ + if (msg == WM_COMMAND && HIWORD(wParam) == BN_BUTTONUP) + { + if ((HWND)lParam == fButton->GetHwnd()) + { + if (fButton->IsChecked()) + { + if (plPick::GrassComponent(pb, fID, true)) + { + INode *node = (INode*)pb->GetReferenceTarget(fID); + if (node) + fButton->SetText(node->GetName()); + } + fButton->SetCheck(FALSE); + } + + return true; + } + } + // check if the reset button is hit + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED) + { + if ((HWND)lParam == fhRemove) + { + pb->SetValue(fID, 0, (ReferenceTarget*)nil); + fButton->SetText("(none)"); + return true; + } + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIParams.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIParams.h new file mode 100644 index 00000000..adfd6fbd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAutoUIParams.h @@ -0,0 +1,407 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "max.h" +#include "iparamb2.h" +#include "hsTypes.h" +#include "hsTemplates.h" +#include + +class plKey; +class plComponentBase; + +class plAutoUIParam +{ +protected: + ParamID fID; + char *fName; + HWND fhDlg; + int fHeight; + + std::vector fControlVec; + + ParamID fVisID; + std::vector fVisStates; + +public: + // Types returned by GetParamType + enum + { + kTypeNone, + + kTypeBool, + kTypeFloat, + kTypeInt, + kTypeString, + + kTypeSceneObj, + kTypeActivator, + kTypeComponent, + kTypeDynamicText, + kTypeGUIDialog, + kTypeExcludeRegion, + kTypeAnimation, + kTypeBehavior, + kTypeMaterial, + kTypeGUIPopUpMenu, + kTypeGUISkin, + kTypeWaterComponent, + kTypeDropDownList, + kTypeSwimCurrentInterface, + kTypeClusterComponent, + kTypeMaterialAnimation, + kTypeGrassComponent, + }; + + plAutoUIParam(ParamID id, const char *name); + virtual ~plAutoUIParam(); + + int Create(HWND hDlg, IParamBlock2 *pb, int yOffset); + virtual int CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset) = 0; + virtual bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb) = 0; + + ParamID GetID() { return fID; } + + virtual void Show(int yOffset); + void Hide(); + int GetHeight(); + + void SetVisInfo(ParamID id, std::vector* states); + bool CheckVisibility(ParamID id, std::string state); + + virtual int GetParamType(); + virtual hsBool GetBool(IParamBlock2 *pb); + virtual float GetFloat(IParamBlock2 *pb); + virtual int GetInt(IParamBlock2 *pb); + virtual const char* GetString(IParamBlock2 *pb); + + virtual int GetCount(IParamBlock2 *pb); + virtual plKey GetKey(IParamBlock2 *pb, int idx=0); + virtual plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); + +protected: + int ISizeControl(HWND hDlg, HWND hControl, int w, int h, int y, int x=3); + HWND ICreateControl(HWND hDlg, const char *className, const char *wndName=nil, DWORD style=0, DWORD exStyle=0); + void ISetControlFont(HWND hControl); + + int IAddStaticText(HWND hDlg, int y, const char *text); +}; + +class plCheckBoxParam : public plAutoUIParam +{ +protected: + HWND fhCheck; + +public: + plCheckBoxParam(ParamID id, const char *name); + int CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset); + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + hsBool GetBool(IParamBlock2 *pb); +}; + +class plSpinnerParam : public plAutoUIParam +{ +protected: + HWND fhSpinner; + bool fIsFloat; // True if this is a float spinner, false if it is an int + +public: + plSpinnerParam(ParamID id, const char *name, bool isFloat); + int CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset); + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + float GetFloat(IParamBlock2 *pb); + int GetInt(IParamBlock2 *pb); + + void Show(int yOffset); +}; + +class plEditParam : public plAutoUIParam +{ +protected: + HWND fhEdit; + int fLines; + +public: + plEditParam(ParamID id, const char *name, int lines); + int CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset); + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + const char* GetString(IParamBlock2 *pb); + void Show(int yOffset); +}; + +class plPickListParam : public plAutoUIParam +{ +protected: + HWND fhList; + HWND fhAdd; + HWND fhRemove; + std::vector fCIDs; + +public: + plPickListParam(ParamID id, const char *name, std::vector* filter); + int CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset); + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + int GetCount(IParamBlock2 *pb); + plKey GetKey(IParamBlock2 *pb, int idx=0); + + void Show(int yOffset); + +protected: + void IUpdateList(IParamBlock2 *pb); +}; + +class plPickButtonParam : public plAutoUIParam +{ +protected: + ICustButton *fButton; + std::vector fCIDs; + bool fCanConvertToType; + HWND fhRemove; + +public: + plPickButtonParam(ParamID id, const char *name, std::vector* filter, bool canConvertToType); + + int CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset); + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + int GetCount(IParamBlock2 *pb); + plKey GetKey(IParamBlock2 *pb, int idx=0); + + void SetPickNode(INode *node, IParamBlock2 *pb); + + void Show(int yOffset); +}; + +class plPickComponentButtonParam : public plPickButtonParam +{ +public: + plPickComponentButtonParam(ParamID id, const char *name, std::vector* filter, bool canConvertToType); + + int GetParamType(); + plComponentBase* GetComponent(IParamBlock2 *pb, int idx=0); +}; + +class plPickComponentListParam : public plPickListParam +{ +public: + plPickComponentListParam(ParamID id, const char *name, std::vector* filter); + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); +}; + +class plPickActivatorButtonParam : public plPickButtonParam +{ +public: + plPickActivatorButtonParam(ParamID id, const char *name); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); +}; + +class plPickActivatorListParam : public plPickListParam +{ +public: + plPickActivatorListParam(ParamID id, const char *name); + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); +}; + +class plPickDynamicTextButtonParam : public plPickButtonParam +{ +public: + plPickDynamicTextButtonParam(ParamID id, const char *name); + + int CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + const char* GetString(IParamBlock2 *pb); + int GetCount(IParamBlock2 *pb); + plKey GetKey(IParamBlock2 *pb, int idx=0); +}; + +class plPickSingleComponentButtonParam : public plPickButtonParam +{ +protected: + int fMyType; + Class_ID fClassToPick; +public: + plPickSingleComponentButtonParam(ParamID id, const char *name, int myType, Class_ID myClassToPick); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); +}; + +class plPickExcludeRegionButtonParam : public plPickButtonParam +{ +public: + plPickExcludeRegionButtonParam(ParamID id, const char *name); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); +}; + +class plPickWaterComponentButtonParam : public plPickButtonParam +{ +public: + plPickWaterComponentButtonParam(ParamID id, const char *name); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); +}; + +class plPickSwimCurrentInterfaceButtonParam : public plPickButtonParam +{ +public: + plPickSwimCurrentInterfaceButtonParam(ParamID id, const char *name); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); +}; + +class plPickClusterComponentButtonParam : public plPickButtonParam +{ +public: + plPickClusterComponentButtonParam(ParamID id, const char *name); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); +}; + +class plPickAnimationButtonParam : public plPickButtonParam +{ +public: + plPickAnimationButtonParam(ParamID id, const char *name); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); +}; + +class plPickBehaviorButtonParam : public plPickButtonParam +{ +public: + plPickBehaviorButtonParam(ParamID id, const char *name); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); +}; + +class plPickMaterialButtonParam : public plPickButtonParam +{ +public: + plPickMaterialButtonParam(ParamID id, const char *name); + + int CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + const char* GetString(IParamBlock2 *pb); + int GetCount(IParamBlock2 *pb); + plKey GetKey(IParamBlock2 *pb, int idx=0); +}; + +class plPickMaterialAnimationButtonParam : public plPickButtonParam +{ +protected: + hsTArray fKeys; + +public: + plPickMaterialAnimationButtonParam(ParamID id, const char *name); + + int CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + const char* GetString(IParamBlock2 *pb); + int GetCount(IParamBlock2 *pb); + plKey GetKey(IParamBlock2 *pb, int idx=0); + + void CreateKeyArray(IParamBlock2* pb); + void DestroyKeyArray(); +}; + +class plDropDownListParam : public plAutoUIParam +{ +protected: + HWND fhList; + std::vector fOptions; + +public: + plDropDownListParam(ParamID id, const char *name, std::vector* options); + int CreateControls(HWND hDlg, IParamBlock2 *pb, int yOffset); + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + int GetCount(IParamBlock2 *pb); + const char* GetString(IParamBlock2 *pb); + + void Show(int yOffset); + +protected: + void IUpdateList(IParamBlock2 *pb); +}; + +class plPickGrassComponentButtonParam : public plPickButtonParam +{ +public: + plPickGrassComponentButtonParam(ParamID id, const char *name); + + bool IsMyMessage(UINT msg, WPARAM wParam, LPARAM lParam, IParamBlock2 *pb); + + int GetParamType(); + plComponentBase *GetComponent(IParamBlock2 *pb, int idx=0); +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAvatarComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAvatarComponent.cpp new file mode 100644 index 00000000..2246ab38 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAvatarComponent.cpp @@ -0,0 +1,1220 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include + +#include "plComponentProcBase.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxConvert/hsConverterUtils.h" + +#include "../pnSceneObject/plSceneObject.h" + +#include "plgDispatch.h" +#include "../plScene/plSceneNode.h" +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../MaxConvert/hsMaterialConverter.h" +#include "../MaxConvert/plBitmapCreator.h" +#include "hsStringTokenizer.h" +#include "../MaxMain/plMaxNode.h" +#include "../pnKeyedObject/plKey.h" + +#include "hsResMgr.h" + +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../plMessage/plMatRefMsg.h" +#include "../plMessage/plLayRefMsg.h" + +#include "plMaxAnimUtils.h" +#include "../plInterp/plController.h" +#include "../plPhysical/plSimDefs.h" +#include "plPhysicsGroups.h" +#include "../plAudible/plWinAudible.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plSurface/plLayerAnimation.h" +#include "../plSurface/hsGMaterial.h" +#include "../plAudio/plWin32StaticSound.h" +#include "../plAudioCore/plSoundBuffer.h" +#include "plAudioComponents.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../plAvatar/plArmatureMod.h" +#include "../plAvatar/plAvBrainHuman.h" +#include "../plAvatar/plAvBrainCritter.h" +#include "../plAvatar/plAvatarClothing.h" +#include "../plAvatar/plArmatureEffects.h" +#include "../plGImage/plMipmap.h" +#include "../plGImage/plLODMipmap.h" + +// Auto generation of shadows here. +#include "plShadowComponents.h" +#include "../plGLight/plShadowCaster.h" + +#include "plAvatarComponent.h" +#include "../../tools/MaxComponent/plPhysicalComponents.h" + +#include "../MaxMain/plPhysicalProps.h" +//#include +//#include + +#include "plPickNode.h" +#include "plPickMaterialMap.h" +#include "../MaxMain/plMtlCollector.h" + +//#define BOB_SORT_AVATAR_FACES + + +// CONSTANTS +float kStdRestitution = 0.5f; +float kStdFriction = 0.1f; + + +void DummyCodeIncludeAvatarFunc() {} + +// PROTOTYPES +class plAvatarComponent; +class plCritterComponent; +class plArmatureComponent; +class plCritterCommands; + +plArmatureMod* plArmatureComponent::IGenerateMyArmMod(plHKPhysical* myHKPhys, plMaxNode* node) +{ + plArmatureMod *avMod = TRACKED_NEW plArmatureMod(); + avMod->SetRootName(node->GetKey()->GetName()); + return avMod; +} + +void plArmatureComponent::ISetupAvatarRenderPropsRecurse(plMaxNode *node) +{ + node->SetNoSpanSort(true); + node->SetNoFaceSort(true); + node->SetNoDeferDraw(true); + + int i; + for (i = 0; i < node->NumberOfChildren(); i++) + { + plMaxNode *pChild = (plMaxNode *)node->GetChildNode(i); + ISetupAvatarRenderPropsRecurse(pChild); + } +} + +//SETUPPROPERTIES +// Tests if IPB2 pointers are healthy and sets up the MaxNode Data +hsBool plArmatureComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + // Global issues + node->SetMovable(true); + node->SetForceLocal(true); + +#ifndef BOB_SORT_AVATAR_FACES + ISetupAvatarRenderPropsRecurse(node); +#endif + + +// float mass, friction, restitution; +// if(ClassID() == AVATAR_CLASS_ID || ClassID() == LOD_AVATAR_CLASS_ID) +// { +// mass = 12; // *** good number from old physical player +// restitution = 0.5f; +// friction = 0.1f; +// } + plMaxNode *animRoot = nil; + + if(ClassID() == AVATAR_CLASS_ID) + animRoot = (plMaxNode *)fCompPB->GetINode(plAvatarComponent::kRootNode); + else if (ClassID() == LOD_AVATAR_CLASS_ID) + animRoot = (plMaxNode *)fCompPB->GetINode(plLODAvatarComponent::kRootNodeAddBtn); + + if(animRoot) + { + const char *nodeName = animRoot->GetName(); + animRoot->SetDrawable(false); // make sure our root bone is invisible + } + + // Ignore all of the old physicals, so old scenes can export + if (ClassID() == AVATAR_CLASS_ID || ClassID() == LOD_AVATAR_CLASS_ID) + { + bool isLOD = ((ClassID() == LOD_AVATAR_CLASS_ID) != 0); + + plMaxNode* ignoreNode = (plMaxNode*)fCompPB->GetINode(isLOD ? plLODAvatarComponent::kPhysicsProxyFeet_DEAD : plAvatarComponent::kPhysicsProxyFeet_DEAD); + if (ignoreNode) + ignoreNode->SetCanConvert(false); + + ignoreNode = (plMaxNode*)fCompPB->GetINode(isLOD ? plLODAvatarComponent::kPhysicsProxyTorso_DEAD : plAvatarComponent::kPhysicsProxyTorso_DEAD); + if (ignoreNode) + ignoreNode->SetCanConvert(false); + + ignoreNode = (plMaxNode*)fCompPB->GetINode(isLOD ? plLODAvatarComponent::kPhysicsProxyHead_DEAD : plAvatarComponent::kPhysicsProxyHead_DEAD); + if (ignoreNode) + ignoreNode->SetCanConvert(false); + } + + return true; +} + +//// ISETARMATURESORECURSE +// Do some strange magic to make sure that we know when all the parts of the avatar are loaded. +void plArmatureComponent::ISetArmatureSORecurse(plMaxNode *node, plSceneObject *so) +{ + if (node->CanConvert()) + node->SetAvatarSO(so); + + int i; + for (i = 0; i < node->NumberOfChildren(); i++) + { + plMaxNode *pChild = (plMaxNode *)node->GetChildNode(i); + ISetArmatureSORecurse(pChild, so); + } +} + + +hsBool plArmatureComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + // add audio interface and record/playback component + pl2WayWinAudible* pAudible = TRACKED_NEW pl2WayWinAudible; + + // Add a key for it + plKey key = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), pAudible, node->GetLocation() ); + + plAudioInterface* ai = TRACKED_NEW plAudioInterface; + plKey pAiKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), (hsKeyedObject*)ai,node->GetLocation()); + + hsgResMgr::ResMgr()->AddViaNotify(pAiKey, TRACKED_NEW plObjRefMsg(node->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + plIntRefMsg* pMsg = TRACKED_NEW plIntRefMsg(node->GetKey(), plRefMsg::kOnCreate, 0, plIntRefMsg::kAudible); + hsgResMgr::ResMgr()->AddViaNotify(pAudible->GetKey(), pMsg, plRefFlags::kActiveRef ); + + ISetArmatureSORecurse(node, node->GetSceneObject()); + + // Uncomment this line to enable a single bone pallete for the entire avatar. + node->SetupBoneHierarchyPalette(); + return true; + +} + +// this is a little gross...the armature component shouldn't know that the subclasses +// actually exist....it's a hard-to-detect implementation detail that breaks new subclasses.... +hsBool plArmatureComponent::Convert(plMaxNode* node, plErrorMsg *pErrMsg) +{ +// plHKPhysical *physical = plHKPhysical::ConvertToPhysical(node->GetSceneObject()); + // physical->SetProperty(plSimulationInterface::kUpright, true); + + IAttachModifiers(node, pErrMsg); + ISetupClothes(node, fArmMod, pErrMsg); + + // ArmatureEffects + plArmatureEffectsMgr *effects = TRACKED_NEW plArmatureEffectsMgr(); + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), effects, node->GetLocation()); + plGenRefMsg *msg = TRACKED_NEW plGenRefMsg(fArmMod->GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(effects->GetKey(), msg, plRefFlags::kActiveRef); // Attach effects + + plSceneObject *obj = node->GetSceneObject(); + node->MakeCharacterHierarchy(pErrMsg); + + const plAGModifier *temp = static_cast(FindModifierByClass(obj, plAGModifier::Index())); + plAGModifier *agMod = const_cast(temp); + + hsAssert(agMod, "Armature root didn't get a agmod. I'll make one for you."); + if( ! agMod) + { + // MakeCharacterHierarchy will attach agmodifiers to all the bones in the hierarchy; + // have to manually add any for non-bone objects... + agMod = TRACKED_NEW plAGModifier("Handle"); // the player root is known as the handle + node->AddModifier(agMod, IGetUniqueName(node)); + } + + agMod->SetChannelName("Handle"); + + // Get the position and radius of the head and torso physicals + if (ClassID() == AVATAR_CLASS_ID || ClassID() == LOD_AVATAR_CLASS_ID) + { + bool isLOD = ((ClassID() == LOD_AVATAR_CLASS_ID) != 0); + float height = fCompPB->GetFloat(isLOD ? plLODAvatarComponent::kPhysicsHeight : plAvatarComponent::kPhysicsHeight); + float width = fCompPB->GetFloat(isLOD ? plLODAvatarComponent::kPhysicsWidth : plAvatarComponent::kPhysicsWidth); + fArmMod->SetPhysicalDims(height, width); + } + +// node->SetupBonesAliasesRecur(node->GetKey()->GetName()); + + return true; +} + + +hsBool plArmatureComponent::IVerifyUsedNode(INode* thisNode, plErrorMsg* pErrMsg, hsBool IsHull) +{ + if(thisNode != NULL) + { + if(((plMaxNode*)thisNode)->CanConvert()) + { + if(IsHull) + ((plMaxNode*)thisNode)->SetDrawable(false); + + }else + { + pErrMsg->Set(true, "Ignored Node Selection", "The object that Node Ptr %s refs was set to be Ignored. Avatar Component failure.", thisNode->GetName()); + pErrMsg->Set(false); + return false; + } + }else{ + + pErrMsg->Set(true, "Empty Node in Avatar Component", "It is imperative that all the node pickers have real values.\n Avatar Component failure.").Show(); + pErrMsg->Set(false); + return false; + } + return true; +} + +void plArmatureComponent::IAttachShadowCastModifiersRecur(plMaxNode* node, plShadowCaster* caster) +{ + if( !node || !caster ) + return; + + // Add test here for whether we want this guy to cast a shadow. + + plShadowCastComponent::AddShadowCastModifier(node, caster); + + int i; + for( i = 0; i < node->NumberOfChildren(); i++ ) + IAttachShadowCastModifiersRecur((plMaxNode*)node->GetChildNode(i), caster); +} + + + +//class AvatarStats +//{ +//public: +// float fFriction; +// float fMaxVel; +// float fAccel; +// float fTurnForce; +// hsBool fUseNewMovement; +// +// AvatarStats(float a, float b, float c, float d, hsBool newMove) +// : fFriction(a), fMaxVel(b), fAccel(c), fTurnForce(d), fUseNewMovement(newMove) {}; +// AvatarStats() : fFriction(-1.0), fMaxVel(-1.0), fAccel(-1.0), fTurnForce(-1.0) {}; +//}; + +enum +{ + kArmMain, + kArmBounce, + kArmReport, +}; + +CLASS_DESC(plAvatarComponent, gAvatarCompDesc, "Avatar", "Avatar", COMP_TYPE_AVATAR, AVATAR_CLASS_ID) + +//Max paramblock2 stuff below. +ParamBlockDesc2 gPAvatarBk +( + plComponent::kBlkComp, _T("Avatar"), 0, &gAvatarCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + //Roll out + 1, + kArmMain, IDD_COMP_AVATAR, IDS_COMP_AVATARS, 0, 0, &gAvatarCompDlgProc, +// kArmBounce, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_BOUNCE, 0, APPENDROLL_CLOSED, &gBounceGroupProc, +// kArmReport, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_REPORT, 0, APPENDROLL_CLOSED, &gReportGroupProc, + + // params + plAvatarComponent::kPhysicsProxyFeet_DEAD, _T("ProxyFeet"), TYPE_INODE, 0, 0, +// p_ui, kArmMain, TYPE_PICKNODEBUTTON, IDC_COMP_AVATAR_PROXY_PICKB, +// p_sclassID, GEOMOBJECT_CLASS_ID, +// p_prompt, IDS_COMP_AVATAR_PROXYS, + end, + + plAvatarComponent::kFriction, _T("Friction"), TYPE_FLOAT, 0, 0, + p_ui, kArmMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_AVATAR_FRICTION_EDIT, IDC_COMP_AVATAR_FRICTION_SPIN, 0.1f, + p_default, 0.9f, + end, + + plAvatarComponent::kRootNode, _T("RootNode"), TYPE_INODE, 0, 0, + p_ui, kArmMain, TYPE_PICKNODEBUTTON, IDC_COMP_AVATAR_ROOT_PICKB, + p_prompt, IDS_COMP_AVATAR_PROXYS, + end, + + plAvatarComponent::kMeshNode, _T("MeshNode"), TYPE_INODE, 0, 0, + p_ui, kArmMain, TYPE_PICKNODEBUTTON, IDC_COMP_AVATAR_MESH_PICKB, + //p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_AVATAR_PROXYS, + end, + + plAvatarComponent::kClothingGroup, _T("ClothingGroup"), TYPE_INT, 0, 0, + p_default, plClothingMgr::kClothingBaseMale, + end, + + plArmatureComponent::kBounceGroups, _T("bounceGroups"), TYPE_INT, 0,0, + p_default, plPhysicsGroups_DEAD::kCreatures | + plPhysicsGroups_DEAD::kStaticSimulated | + plPhysicsGroups_DEAD::kDynamicSimulated | + plPhysicsGroups_DEAD::kAnimated, + end, + + plArmatureComponent::kReportGroups, _T("reportGroups"), TYPE_INT, 0,0, + end, + + plAvatarComponent::kBrainType, _T("Brain"), TYPE_INT, 0, 0, + p_default, plAvatarComponent::kBrainHuman, + end, + + plAvatarComponent::kSkeleton, _T("Skeleton"), TYPE_INT, 0, 0, + p_default, plArmatureMod::kBoneBaseMale, + end, + + plAvatarComponent::kPhysicsProxyTorso_DEAD, _T("ProxyTorso"), TYPE_INODE, 0, 0, +// p_ui, kArmMain, TYPE_PICKNODEBUTTON, IDC_COMP_AVATAR_PROXY_PICKT, +// p_sclassID, GEOMOBJECT_CLASS_ID, +// p_prompt, IDS_COMP_AVATAR_PROXYS, + end, + + plAvatarComponent::kPhysicsProxyHead_DEAD, _T("ProxyHead"), TYPE_INODE, 0, 0, +// p_ui, kArmMain, TYPE_PICKNODEBUTTON, IDC_COMP_AVATAR_PROXY_PICKH, +// p_sclassID, GEOMOBJECT_CLASS_ID, +// p_prompt, IDS_COMP_AVATAR_PROXYS, + end, + + plAvatarComponent::kPhysicsHeight, _T("physHeight"), TYPE_FLOAT, 0, 0, + p_range, 0.1f, 50.0f, + p_default, 5.f, + p_ui, kArmMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_PHYS_HEIGHT_EDIT, IDC_PHYS_HEIGHT_SPIN, SPIN_AUTOSCALE, + end, + + plAvatarComponent::kPhysicsWidth, _T("physWidth"), TYPE_FLOAT, 0, 0, + p_range, 0.1f, 50.0f, + p_default, 2.5f, + p_ui, kArmMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_PHYS_WIDTH_EDIT, IDC_PHYS_WIDTH_SPIN, SPIN_AUTOSCALE, + end, + + plAvatarComponent::kBodyFootstepSoundPage, _T("bodyFootstepPage"), TYPE_STRING, 0, 0, + p_default, "Audio", + p_ui, kArmMain, TYPE_EDITBOX, IDC_BODYFOOTSTEPPAGE_EDIT, + end, + + plAvatarComponent::kAnimationPrefix,_T("animationPrefix"), TYPE_STRING, 0, 0, + p_default, "Male", + p_ui, kArmMain, TYPE_EDITBOX, IDC_ANIMATIONPREFIX_EDIT, + end, + + end +); + +plAvatarComponent::plAvatarComponent() +{ + fClassDesc = &gAvatarCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + +void plAvatarComponent::IAttachModifiers(plMaxNode *node, plErrorMsg *pErrMsg) +{ + const char *name = node->GetKey()->GetName(); + + plMaxNode *meshNode = (plMaxNode *)fCompPB->GetINode(plAvatarComponent::kMeshNode); + plKey meshKey = meshNode->GetSceneObject()->GetKey(); + plMaxNode *animRootNode = (plMaxNode *)fCompPB->GetINode(plAvatarComponent::kRootNode); + plKey animRootKey = animRootNode->GetSceneObject()->GetKey(); + + plSceneObject * bodySO = node->GetSceneObject(); + + plArmatureMod* avMod = TRACKED_NEW plArmatureMod(); + avMod->SetRootName(name); + avMod->AppendMeshKey(meshKey); + int skeletonType = fCompPB->GetInt(ParamID(kSkeleton)); + avMod->SetBodyType( skeletonType ); + + // only make a human brain if we're a human + if (skeletonType == plArmatureMod::kBoneBaseCritter) + avMod->PushBrain(TRACKED_NEW plAvBrainCritter()); + else + avMod->PushBrain(TRACKED_NEW plAvBrainHuman(skeletonType == plArmatureMod::kBoneBaseActor)); + + avMod->SetBodyAgeName(node->GetAgeName()); + avMod->SetBodyFootstepSoundPage(fCompPB->GetStr(ParamID(kBodyFootstepSoundPage))); + avMod->SetAnimationPrefix(fCompPB->GetStr(ParamID(kAnimationPrefix))); + + //AddLinkSound(node, node->GetSceneObject()->GetKey(), pErrMsg ); + + plKey avKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), avMod, node->GetLocation()); + plObjRefMsg *objRefMsg = TRACKED_NEW plObjRefMsg(bodySO->GetKey(), plRefMsg::kOnCreate,-1, plObjRefMsg::kModifier); + hsgResMgr::ResMgr()->AddViaNotify(avKey, objRefMsg, plRefFlags::kActiveRef); + + fArmMod = avMod; +} + +// Helper function for the Avatar and LOD Avatar components +void AddClothingToMod(plMaxNode *node, plArmatureMod *mod, int group, hsGMaterial *mat, plErrorMsg *pErrMsg) +{ + plGenRefMsg *msg; + char keyName[256]; + TSTR sdata; + hsStringTokenizer toker; + + if (mod == nil) + { + hsAssert(false, "Adding clothes to a nil armatureMod."); + return; + } + + plClothingBase *base = TRACKED_NEW plClothingBase(); + if (node->GetUserPropString("layout", sdata)) + { + toker.Reset(sdata, hsConverterUtils::fTagSeps); + base->SetLayoutName(toker.next()); + } + else + base->SetLayoutName("BasicHuman"); + sprintf(keyName, "%s_ClothingBase", node->GetName()); + hsgResMgr::ResMgr()->NewKey(keyName, base, node->GetLocation()); + plClothingOutfit *outfit = TRACKED_NEW plClothingOutfit(); + outfit->fGroup = group; + sprintf(keyName, "%s_outfit", mod->GetKey()->GetName()); + hsgResMgr::ResMgr()->NewKey(keyName, outfit, node->GetLocation()); + + msg = TRACKED_NEW plGenRefMsg(outfit->GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(base->GetKey(), msg, plRefFlags::kActiveRef); // Add clothing base to outfit + msg = TRACKED_NEW plGenRefMsg(mod->GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(outfit->GetKey(), msg, plRefFlags::kActiveRef); // Attach outfit + + + plMipmap *baseTex = nil; + plLayerInterface *li = nil; + + if (mat != nil) + { + li = mat->GetLayer(0)->BottomOfStack(); + baseTex = plMipmap::ConvertNoRef(li->GetTexture()); + hsAssert(li->GetTexture() == baseTex, "Base texture mismatch on avatar construction?"); + } + if (mat != nil && baseTex != nil) + { + // Let's fix up these bitmap flags. Normally, they are set on convert based on + // the contents of the bitmap. But here our contents are subject to change at runtime. + // Fortunately, we've got a pretty good idea what to expect. + // I'm making a subjective decision here on forcing 32bit even when we run in 16 bit, + // because an A4R4G4B4 makes the avatar look like it has a rare skin disease. + baseTex->SetFlags(plMipmap::kAlphaChannelFlag | plMipmap::kForce32Bit | plMipmap::kDontThrowAwayImage); + + // The tex is what the outfit and the material hold onto. It's what + // gets rendered. The base will hang onto the original baseTex. + msg = TRACKED_NEW plGenRefMsg(base->GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->SendRef(baseTex->GetKey(), msg, plRefFlags::kActiveRef); // Set base texture of avatar + plLayRefMsg* layRef = TRACKED_NEW plLayRefMsg(li->GetKey(), plRefMsg::kOnRemove, 0, plLayRefMsg::kTexture); + hsgResMgr::ResMgr()->SendRef(baseTex->GetKey(), layRef, plRefFlags::kActiveRef); // Remove it from the material + + msg = TRACKED_NEW plGenRefMsg(outfit->GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(li->GetKey(), msg, plRefFlags::kActiveRef); // Set outfit's target layer interface + msg = TRACKED_NEW plGenRefMsg(outfit->GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(mat->GetKey(), msg, plRefFlags::kActiveRef); // Outfit needs the material + } + else + { + pErrMsg->Set(outfit->fGroup != plClothingMgr::kClothingBaseNoOptions, node->GetName(), + "This avatar expects clothing options, but has no material on which to place them. " + "It will not be visable at runtime.").CheckAskOrCancel(); + outfit->fGroup = plClothingMgr::kClothingBaseNoOptions; + } +} + +void plAvatarComponent::ISetupClothes(plMaxNode *node, plArmatureMod *mod, plErrorMsg *pErrMsg) +{ + AddClothingToMod(node, mod, fCompPB->GetInt(kClothingGroup), nil, pErrMsg); +} + +hsBool plAvatarComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetItinerant(true); + +// if(!IVerifyUsedNode(fCompPB->GetINode(plAvatarComponent::kPhysicsProxyFeet), pErrMsg, true)) +// return false; +// if(!IVerifyUsedNode(fCompPB->GetINode(plAvatarComponent::kPhysicsProxyTorso), pErrMsg, true)) +// return false; +// if(!IVerifyUsedNode(fCompPB->GetINode(plAvatarComponent::kPhysicsProxyHead), pErrMsg, true)) +// return false; + if(!IVerifyUsedNode(fCompPB->GetINode(plAvatarComponent::kMeshNode), pErrMsg, false)) + return false; + if(!IVerifyUsedNode(fCompPB->GetINode(plAvatarComponent::kRootNode), pErrMsg, false)) + return false; + + plMaxNode *meshNode = (plMaxNode *)fCompPB->GetINode(plAvatarComponent::kMeshNode); + if (meshNode) + { + if (meshNode->GetObjectRef()->ClassID() == Class_ID(DUMMY_CLASS_ID, 0)) + { + meshNode->SetSwappableGeomTarget(plArmatureMod::kSwapTargetShadow); + } + } + + return plArmatureComponent::SetupProperties(node, pErrMsg); +} + +class AvatarCompDlgProc : public ParamMap2UserDlgProc +{ +public: + AvatarCompDlgProc() {} + ~AvatarCompDlgProc() {} + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + int id = LOWORD(wParam); + int code = HIWORD(wParam); + + IParamBlock2 *pb = map->GetParamBlock(); + HWND cbox = NULL; + char* buffer = NULL; + + int selection; + switch (msg) + { + case WM_INITDIALOG: + int j; + for (j = 0; j < plClothingMgr::kMaxGroup; j++) + { + cbox = GetDlgItem(hWnd, IDC_COMP_AVATAR_CLOTHING_GROUP); + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)plClothingMgr::GroupStrings[j]); + } + selection = pb->GetInt(ParamID(plAvatarComponent::kClothingGroup)); + SendMessage(cbox, CB_SETCURSEL, selection, 0); + + for (j = 0; j < plArmatureMod::kMaxBoneBase; j++) + { + cbox = GetDlgItem(hWnd, IDC_COMP_AVATAR_SKELETON); + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)plArmatureMod::BoneStrings[j]); + } + selection = pb->GetInt(ParamID(plAvatarComponent::kSkeleton)); + SendMessage(cbox, CB_SETCURSEL, selection, 0); + + return TRUE; + + case WM_COMMAND: + if (id == IDC_COMP_AVATAR_CLOTHING_GROUP) + { + selection = SendMessage(GetDlgItem(hWnd, id), CB_GETCURSEL, 0, 0); + pb->SetValue(plAvatarComponent::kClothingGroup, t, selection); + return TRUE; + } + if (id == IDC_COMP_AVATAR_SKELETON) + { + selection = SendMessage(GetDlgItem(hWnd, id), CB_GETCURSEL, 0, 0); + pb->SetValue(plAvatarComponent::kSkeleton, t, selection); + return TRUE; + } + + break; + } + return FALSE; + } + void DeleteThis() {} +}; +static AvatarCompDlgProc gAvatarCompDlgProc; + +/////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////// + +class plCompoundCtrlComponent : plComponent +{ +public: + plCompoundCtrlComponent(); + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode* node,plErrorMsg *pErrMsg); +}; + +CLASS_DESC(plCompoundCtrlComponent, gCompoundCtrlCompDesc, "Compound Controller", "CompoundCtrl", COMP_TYPE_AVATAR, Class_ID(0x3f2a790f, 0x30354673)) + +plCompoundCtrlComponent::plCompoundCtrlComponent() +{ + fClassDesc = &gCompoundCtrlCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plCompoundCtrlComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetMovable(true); + node->SetForceLocal(true); + node->SetItinerant(true); + return true; +} + +hsBool plCompoundCtrlComponent::Convert(plMaxNode* node, plErrorMsg *pErrMsg) +{ + const char *name = node->GetKey()->GetName(); + + node->MakeCharacterHierarchy(pErrMsg); + node->SetupBonesAliasesRecur(name); + + + // create and register the player modifier + plAGMasterMod *agMaster = TRACKED_NEW plAGMasterMod(); + node->AddModifier(agMaster, IGetUniqueName(node)); + + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// +// LOD Avatar Stuff below + + +class plLODAvatarComponentProc : public plVSBaseComponentProc +{ +protected: + IParamBlock2 *fPB; + plLODAvatarComponent* fComp; + HWND fMstrDlg; + +public: + plLODAvatarComponentProc() : fComp(nil), fPB(nil) {} + + void UpdateBoneDisplay(IParamMap2 *pm) + { + HWND hWnd = pm->GetHWnd(); + HWND hList = GetDlgItem(hWnd, IDC_COMP_LOD_AVATAR_BONELIST); + IParamBlock2 *pb = pm->GetParamBlock(); + + ListBox_ResetContent(hList); + int group = fComp->GetCurGroupIdx(); + int startIdx = fComp->GetStartIndex(group); + int endIdx = fComp->GetEndIndex(group); + + while (startIdx < endIdx) + { + INode *curNode = pb->GetINode(ParamID(plLODAvatarComponent::kBoneList), 0, startIdx); + if (curNode == nil) + { + fComp->RemoveBone(startIdx); + endIdx--; + continue; + } + ListBox_AddString(hList, curNode->GetName()); + startIdx++; + } + } + + virtual void Update(TimeValue t, Interval& valid, IParamMap2* pmap) { UpdateBoneDisplay(pmap); } + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + int selection; + HWND cbox = NULL; + HWND hList = GetDlgItem(hWnd, IDC_COMP_LOD_AVATAR_BONELIST); + + switch (msg) + { + case WM_INITDIALOG: + { + int LodBeginState = map->GetParamBlock()->GetInt(plLODAvatarComponent::kLODState); + + HWND LODCombo = GetDlgItem(hWnd, IDC_COMP_LOD_AVATAR_STATE); + + fMstrDlg = hWnd; + + fPB = map->GetParamBlock(); + fComp = (plLODAvatarComponent*) fPB->GetOwner(); + + VCharArray Nilptr; + ILoadComboBox(LODCombo, fComp->fLODLevels); + SendMessage(LODCombo, CB_SETCURSEL, LodBeginState, 0); // select the right one + + int i; + for (i = 0; i < plClothingMgr::kMaxGroup; i++) + { + cbox = GetDlgItem(hWnd, IDC_COMP_AVATAR_CLOTHING_GROUP); + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)plClothingMgr::GroupStrings[i]); + } + selection = fPB->GetInt(ParamID(plLODAvatarComponent::kClothingGroup)); + SendMessage(cbox, CB_SETCURSEL, selection, 0); + + for (i = 0; i < plArmatureMod::kMaxBoneBase; i++) + { + cbox = GetDlgItem(hWnd, IDC_COMP_AVATAR_SKELETON); + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)plArmatureMod::BoneStrings[i]); + } + selection = fPB->GetInt(ParamID(plLODAvatarComponent::kSkeleton)); + SendMessage(cbox, CB_SETCURSEL, selection, 0); + + Mtl *mat = fPB->GetMtl(plLODAvatarComponent::kMaterial); + Button_SetText(GetDlgItem(hWnd, IDC_COMP_LOD_AVATAR_MTL), (mat ? mat->GetName() : "(none)")); + + UpdateBoneDisplay(map); + return true; + } + + case WM_COMMAND: + if (LOWORD(wParam) == IDC_COMP_AVATAR_CLOTHING_GROUP) + { + selection = SendMessage(GetDlgItem(hWnd, IDC_COMP_AVATAR_CLOTHING_GROUP), CB_GETCURSEL, 0, 0); + fPB->SetValue(ParamID(plLODAvatarComponent::kClothingGroup), t, selection); + return TRUE; + } + else if (LOWORD(wParam) == IDC_COMP_AVATAR_SKELETON) + { + selection = SendMessage(GetDlgItem(hWnd, IDC_COMP_AVATAR_SKELETON), CB_GETCURSEL, 0, 0); + fPB->SetValue(ParamID(plLODAvatarComponent::kSkeleton), t, selection); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED) + { + if (LOWORD(wParam) == IDC_COMP_LOD_AVATAR_BONE_ADD) + { + std::vector cids; + cids.push_back(Class_ID(TRIOBJ_CLASS_ID, 0)); + cids.push_back(Class_ID(EDITTRIOBJ_CLASS_ID, 0)); + if (plPick::NodeRefKludge(fPB, plLODAvatarComponent::kLastPick, &cids, true, false)) + fComp->AddSelectedBone(); + + return TRUE; + } + // Remove the currently selected material + else if (LOWORD(wParam) == IDC_COMP_LOD_AVATAR_BONE_REMOVE) + { + int curSel = SendMessage(hList, LB_GETCURSEL, 0, 0); + if (curSel >= 0) + fComp->RemoveBone(curSel); + + return TRUE; + } + else if (LOWORD(wParam) == IDC_COMP_LOD_AVATAR_MTL) + { + Mtl *pickedMtl = plPickMaterialMap::PickMaterial(plMtlCollector::kPlasmaOnly); + fPB->SetValue(plLODAvatarComponent::kMaterial, 0, pickedMtl); + Button_SetText(GetDlgItem(hWnd, IDC_COMP_LOD_AVATAR_MTL), (pickedMtl ? pickedMtl->GetName() : "(none)")); + + return TRUE; + } + } + else + { + int LodBeginState = map->GetParamBlock()->GetInt(plLODAvatarComponent::kLODState); + + if(fPB->GetINode(plLODAvatarComponent::kMeshNodeAddBtn,t)) + fPB->SetValue(plLODAvatarComponent::kMeshNodeTab, t, fPB->GetINode(plLODAvatarComponent::kMeshNodeAddBtn,t), LodBeginState); + + if(LOWORD(wParam) == IDC_COMP_LOD_AVATAR_STATE && HIWORD(wParam) == CBN_SELCHANGE) + { + int idx = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); + fPB->SetValue(plLODAvatarComponent::kLODState, 0, idx); + + if(fPB->GetINode(plLODAvatarComponent::kMeshNodeTab, t, idx)) + fPB->SetValue(plLODAvatarComponent::kMeshNodeAddBtn, t, fPB->GetINode(plLODAvatarComponent::kMeshNodeTab,t, idx)); + else + fPB->Reset(plLODAvatarComponent::kMeshNodeAddBtn); + return true; + } + } + + break; + + case WM_CLOSE: + { + int LodBeginState = map->GetParamBlock()->GetInt(plLODAvatarComponent::kLODState); + + if(fPB->GetINode(plLODAvatarComponent::kMeshNodeAddBtn,t)) + fPB->SetValue(plLODAvatarComponent::kMeshNodeTab, t, fPB->GetINode(plLODAvatarComponent::kMeshNodeAddBtn,t), LodBeginState); + + return false; + } + } + + return false; + } + + void DeleteThis() {} +}; + + + + +//! A static variable. +/*! + A static instance used in the ParamBlock2 processing of + the physical Dialog UIs. + + \sa plPhysCoreComponentProc() +*/ +static plLODAvatarComponentProc gLODAvComponentProc; + +class plLODAvAccessor : public PBAccessor +{ +public: + + //! Public Accessor Class, used in ParamBlock2 processing. + /*! + Workhorse for this Accessor Class (derived from Max's PBAccessor). + + When one of our parameters that is a ref changes, send out the component ref + changed message. Normally, messages from component refs are ignored since + they pass along all the messages of the ref, which generates a lot of false + converts. + */ + + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (id == plLODAvatarComponent::kMeshNodeAddBtn) + { + plLODAvatarComponent *comp = (plLODAvatarComponent *)owner; + IParamBlock2 *pb = comp->GetParamBlockByID(plLODAvatarComponent::kBlkComp); + int LodBeginState = pb->GetInt(plLODAvatarComponent::kLODState); + + INode *node = pb->GetINode(plLODAvatarComponent::kMeshNodeAddBtn, t); + if (node) + pb->SetValue(plLODAvatarComponent::kMeshNodeTab, t, node, LodBeginState); + } + } +}; + +plLODAvAccessor gLODAvatarAccessor; + +///////////////////////////////////////////////////////////////////////////////////////// +/////// +// PLLODAVATARCOMPONENT + +CLASS_DESC(plLODAvatarComponent, gLODAvatarCompDesc, "LODAvatar", "LODAvatar", COMP_TYPE_AVATAR, LOD_AVATAR_CLASS_ID) + +//Max paramblock2 stuff below. +ParamBlockDesc2 gPLODAvatarBk +( + plComponent::kBlkComp, _T("Avatar"), 0, &gLODAvatarCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + //Roll out + 1, + kArmMain, IDD_COMP_LOD_AVATAR, IDS_COMP_LOD_AVATARS, 0, 0, &gLODAvComponentProc, +// kArmBounce, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_BOUNCE, 0, APPENDROLL_CLOSED, &gBounceGroupProc, +// kArmReport, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_REPORT, 0, APPENDROLL_CLOSED, &gReportGroupProc, + + // params + plLODAvatarComponent::kPhysicsProxyFeet_DEAD, _T("ProxyFeet"), TYPE_INODE, 0, 0, +// p_ui, kArmMain, TYPE_PICKNODEBUTTON, IDC_COMP_AVATAR_PROXY_PICKB, +// p_sclassID, GEOMOBJECT_CLASS_ID, +// p_prompt, IDS_COMP_AVATAR_PROXYS, + end, + + plLODAvatarComponent::kFriction, _T("Friction"), TYPE_FLOAT, 0, 0, + p_ui, kArmMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_AVATAR_FRICTION_EDIT, IDC_COMP_AVATAR_FRICTION_SPIN, 0.1f, + p_default, 0.9f, + end, + + plLODAvatarComponent::kLODState, _T("LODState"), TYPE_INT, 0, 0, + p_range, 1, plLODAvatarComponent::kMaxNumLODLevels, + end, + + plLODAvatarComponent::kMeshNodeTab, _T("MeshObject"), TYPE_INODE_TAB, plLODAvatarComponent::kMaxNumLODLevels, 0, 0, + p_accessor, &gLODAvatarAccessor, + end, + + plLODAvatarComponent::kRootNodeAddBtn, _T("RtNodePicker"), TYPE_INODE, 0, 0, + p_ui, kArmMain, TYPE_PICKNODEBUTTON, IDC_COMP_LOD_AVATAR_ROOT_PICKB, + p_prompt, IDS_COMP_AVATAR_PROXYS, + end, + + plLODAvatarComponent::kMeshNodeAddBtn, _T("MshNodePicker"), TYPE_INODE, 0, 0, + p_ui, kArmMain, TYPE_PICKNODEBUTTON, IDC_COMP_LOD_AVATAR_MESH_PICKB, + //p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_AVATAR_PROXYS, + end, + + plLODAvatarComponent::kClothingGroup, _T("ClothingGroup"), TYPE_INT, 0, 0, + p_default, plClothingMgr::kClothingBaseMale, + end, + + plArmatureComponent::kBounceGroups, _T("bounceGroups"), TYPE_INT, 0,0, + p_default, plPhysicsGroups_DEAD::kCreatures | + plPhysicsGroups_DEAD::kStaticSimulated | + plPhysicsGroups_DEAD::kDynamicSimulated | + plPhysicsGroups_DEAD::kAnimated, + end, + + plArmatureComponent::kReportGroups, _T("reportGroups"), TYPE_INT, 0,0, + end, + + plLODAvatarComponent::kBrainType, _T("Brain"), TYPE_INT, 0, 0, + p_default, plLODAvatarComponent::kBrainHuman, + end, + + plLODAvatarComponent::kGroupIdx, _T("GroupIndex"), TYPE_INT, 0, 0, + p_default, 0, + p_range, 0, plLODAvatarComponent::kMaxNumLODLevels - 1, + p_ui, kArmMain, TYPE_SPINNER, EDITTYPE_INT, + IDC_COMP_LOD_AVATAR_GROUP, IDC_COMP_LOD_AVATAR_GROUP_SPIN, 1.f, + end, + + plLODAvatarComponent::kBoneList, _T("Bones"), TYPE_INODE_TAB, 0, 0, 0, + end, + + plLODAvatarComponent::kGroupTotals, _T("Totals"), TYPE_INT_TAB, plLODAvatarComponent::kMaxNumLODLevels, 0, 0, + p_default, 0, + end, + + plLODAvatarComponent::kLastPick, _T("LastPick"), TYPE_INODE, 0, 0, // Temp storage space for the bone picker + end, + + plLODAvatarComponent::kSkeleton, _T("Skeleton"), TYPE_INT, 0, 0, + p_default, plArmatureMod::kBoneBaseMale, + end, + + plLODAvatarComponent::kMaterial, _T("Material"), TYPE_MTL, 0, 0, + end, + + plLODAvatarComponent::kPhysicsProxyTorso_DEAD, _T("ProxyTorso"), TYPE_INODE, 0, 0, +// p_ui, kArmMain, TYPE_PICKNODEBUTTON, IDC_COMP_AVATAR_PROXY_PICKT, +// p_sclassID, GEOMOBJECT_CLASS_ID, +// p_prompt, IDS_COMP_AVATAR_PROXYS, + end, +// + plLODAvatarComponent::kPhysicsProxyHead_DEAD, _T("ProxyHead"), TYPE_INODE, 0, 0, +// p_ui, kArmMain, TYPE_PICKNODEBUTTON, IDC_COMP_AVATAR_PROXY_PICKH, +// p_sclassID, GEOMOBJECT_CLASS_ID, +// p_prompt, IDS_COMP_AVATAR_PROXYS, + end, + + plLODAvatarComponent::kPhysicsHeight, _T("physHeight"), TYPE_FLOAT, 0, 0, + p_range, 0.1f, 50.0f, + p_default, 5.f, + p_ui, kArmMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_PHYS_HEIGHT_EDIT, IDC_PHYS_HEIGHT_SPIN, SPIN_AUTOSCALE, + end, + + plLODAvatarComponent::kPhysicsWidth, _T("physWidth"), TYPE_FLOAT, 0, 0, + p_range, 0.1f, 50.0f, + p_default, 2.5f, + p_ui, kArmMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_PHYS_WIDTH_EDIT, IDC_PHYS_WIDTH_SPIN, SPIN_AUTOSCALE, + end, + + plLODAvatarComponent::kBodyFootstepSoundPage, _T("bodyFootstepPage"), TYPE_STRING, 0, 0, + p_default, "Audio", + p_ui, kArmMain, TYPE_EDITBOX, IDC_BODYFOOTSTEPPAGE_EDIT, + end, + + plLODAvatarComponent::kAnimationPrefix,_T("animationPrefix"), TYPE_STRING, 0, 0, + p_default, "Male", + p_ui, kArmMain, TYPE_EDITBOX, IDC_ANIMATIONPREFIX_EDIT, + end, + + end +); + +plLODAvatarComponent::plLODAvatarComponent() : fMaterial(nil) +{ + fClassDesc = &gLODAvatarCompDesc; + fClassDesc->MakeAutoParamBlocks(this); + + fLODLevels.push_back("High"); + fLODLevels.push_back("Medium"); + fLODLevels.push_back("Low"); + +} + +void plLODAvatarComponent::IAttachModifiers( plMaxNode *node, plErrorMsg *pErrMsg) +{ + const char *avatarName = node->GetKey()->GetName(); + plMaxNode *animRoot = (plMaxNode *)fCompPB->GetINode(plLODAvatarComponent::kRootNodeAddBtn); + plKey animRootKey = animRoot->GetSceneObject()->GetKey(); + plArmatureLODMod* avMod = TRACKED_NEW plArmatureLODMod(avatarName); + + int skeletonType = fCompPB->GetInt(ParamID(kSkeleton)); + avMod->SetBodyType( skeletonType ); + if (skeletonType == plArmatureLODMod::kBoneBaseCritter) + avMod->PushBrain(TRACKED_NEW plAvBrainCritter()); + else + avMod->PushBrain(TRACKED_NEW plAvBrainHuman(skeletonType == plArmatureMod::kBoneBaseActor)); + + avMod->SetBodyAgeName(node->GetAgeName()); + avMod->SetBodyFootstepSoundPage(fCompPB->GetStr(ParamID(kBodyFootstepSoundPage))); + avMod->SetAnimationPrefix(fCompPB->GetStr(ParamID(kAnimationPrefix))); + + int iLODCount = fCompPB->Count(plLODAvatarComponent::kMeshNodeTab); + for (int i = 0; i < iLODCount; i++) + { + plMaxNode *meshNode = (plMaxNode *)fCompPB->GetINode(plLODAvatarComponent::kMeshNodeTab, 0, i); + plKey meshKey = meshNode->GetSceneObject()->GetKey(); + avMod->AppendMeshKey(meshKey); + } + + node->AddModifier(avMod, IGetUniqueName(node)); + fArmMod = avMod; + IAttachShadowCastToLODs(node); +} + +hsBool plLODAvatarComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetItinerant(true); + +// if(!IVerifyUsedNode(fCompPB->GetINode(plLODAvatarComponent::kPhysicsProxyFeet), pErrMsg, true)) +// return false; +// if(!IVerifyUsedNode(fCompPB->GetINode(plLODAvatarComponent::kPhysicsProxyTorso), pErrMsg, true)) +// return false; +// if(!IVerifyUsedNode(fCompPB->GetINode(plLODAvatarComponent::kPhysicsProxyHead), pErrMsg, true)) +// return false; + if(!IVerifyUsedNode(fCompPB->GetINode(plLODAvatarComponent::kRootNodeAddBtn), pErrMsg, false)) + return false; + for(int i = 0; i < plLODAvatarComponent::kMaxNumLODLevels; i++) + { + plMaxNode *meshNode = (plMaxNode *)fCompPB->GetINode(plLODAvatarComponent::kMeshNodeTab, 0, i); + if (!IVerifyUsedNode(meshNode, pErrMsg, false)) + { + return false; + } + else + { + if (meshNode->GetObjectRef()->ClassID() == Class_ID(DUMMY_CLASS_ID, 0)) + { + meshNode->SetSwappableGeomTarget(plArmatureMod::kSwapTargetShadow); + } + } + } + + return plArmatureComponent::SetupProperties(node, pErrMsg); +} + +hsBool plLODAvatarComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + hsBool result = plArmatureComponent::PreConvert(node, pErrMsg); + + hsTArray mats; + Mtl *mtl = fCompPB->GetMtl(kMaterial); + if (mtl) + { + hsMaterialConverter::Instance().GetMaterialArray(mtl, node, mats); + fMaterial = (mats.GetCount() > 0 ? mats[0] : nil); + } + return result; +} + +hsBool plLODAvatarComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plArmatureComponent::Convert(node, pErrMsg); + + // Bone LOD stuff + int numSoFar = 0; + int i; + for (i = 0; i < kMaxNumLODLevels; i++) + { + int numBones = fCompPB->GetInt(ParamID(kGroupTotals), 0, i); + plKeyVector *keyVec = TRACKED_NEW plKeyVector; + + int j; + for (j = 0; j < numBones; j++) + { + plMaxNode *compNode = (plMaxNode*)fCompPB->GetINode(ParamID(kBoneList), 0, numSoFar + j); + if (compNode) + { + plAGModifier *agMod = compNode->HasAGMod(); + keyVec->push_back(agMod ? agMod->GetKey() : nil); + } + } + plArmatureLODMod::ConvertNoRef(fArmMod)->AppendBoneVec(keyVec); + + numSoFar += numBones; + } + + return true; +} + +void plLODAvatarComponent::ISetupClothes(plMaxNode *node, plArmatureMod *mod, plErrorMsg* pErrMsg) +{ + AddClothingToMod(node, mod, fCompPB->GetInt(kClothingGroup), fMaterial, pErrMsg); +} + +void plLODAvatarComponent::IAttachShadowCastModifiersRecur(plMaxNode* node, plShadowCaster* caster) +{ + if( !node || !caster ) + return; + + if( !node->GetSwappableGeom() && node->GetObjectRef()->ClassID() != Class_ID(DUMMY_CLASS_ID, 0)) + plShadowCastComponent::AddShadowCastModifier(node, caster); + + int i; + for( i = 0; i < node->NumberOfChildren(); i++ ) + IAttachShadowCastModifiersRecur((plMaxNode*)node->GetChildNode(i), caster); +} + +void plLODAvatarComponent::IAttachShadowCastToLODs(plMaxNode* rootNode) +{ + plShadowCaster* caster = TRACKED_NEW plShadowCaster; + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(rootNode), caster, rootNode->GetLocation()); + caster->SetSelfShadow(true); + + int iLODCount = fCompPB->Count(plLODAvatarComponent::kMeshNodeTab); + + for (int i = 0; i < iLODCount; i++) + { + plMaxNode *meshNode = (plMaxNode *)fCompPB->GetINode(plLODAvatarComponent::kMeshNodeTab, 0, i); + if( meshNode ) + { + plShadowCastComponent::AddShadowCastModifier(meshNode, caster); // The LOD roots are a special case. + IAttachShadowCastModifiersRecur(meshNode, caster); + } + } +} + +int plLODAvatarComponent::GetCurGroupIdx() +{ + return fCompPB->GetInt(ParamID(kGroupIdx)); +} + +int plLODAvatarComponent::GetStartIndex(int group) +{ + int result = 0; + int i; + for (i = 0; i < group; i++) + result += fCompPB->GetInt(ParamID(kGroupTotals), 0, i); + + return result; +} + +int plLODAvatarComponent::GetEndIndex(int group) +{ + return GetStartIndex(group) + fCompPB->GetInt(ParamID(kGroupTotals), 0, group); +} + +void plLODAvatarComponent::AddSelectedBone() +{ + int group = GetCurGroupIdx(); + int boneIdx = GetEndIndex(group); + + INode *node = fCompPB->GetINode(ParamID(kLastPick)); + fCompPB->Insert(ParamID(kBoneList), boneIdx, 1, &node); + + fCompPB->SetValue(ParamID(kGroupTotals), 0, fCompPB->GetInt(ParamID(kGroupTotals), 0, group) + 1, group); +} + +void plLODAvatarComponent::RemoveBone(int index) +{ + int group = GetCurGroupIdx(); + int boneIdx = GetStartIndex(group) + index; + + fCompPB->Delete(ParamID(kBoneList), boneIdx, 1); + fCompPB->SetValue(ParamID(kGroupTotals), 0, fCompPB->GetInt(ParamID(kGroupTotals), 0, group) - 1, group); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAvatarComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAvatarComponent.h new file mode 100644 index 00000000..3b4866fb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plAvatarComponent.h @@ -0,0 +1,227 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __PLAVATARCOMPONENT_H__ +#define __PLAVATARCOMPONENT_H__ + +#include "max.h" +#include "../plAvatar/plCritterCommands.h" +#include "../plAvatar/plPuppetCommands.h" + +#define AVATAR_CLASS_ID Class_ID(0x49247847, 0xd3908fe) +#define LOD_AVATAR_CLASS_ID Class_ID(0x50100640, 0x72f94120) +#define CRITTER_CLASS_ID Class_ID(0x3bd003b6, 0x66a85756) +#define PUPPET_CLASS_ID Class_ID(0x26545a35, 0x2a5000b) + +class plHKPhysical; +class plErrorMsg; +class plArmatureMod; +class plMaxNode; +class plSceneObject; +class plShadowCaster; +class hsGMaterial; + +class AvatarCompDlgProc; +extern AvatarCompDlgProc gAvatarCompDlgProc; + +/** \class plArmatureComponent + Base class for all the components that creature armatures or, in the case of the + compound controller, simply multi-channel animation controllers (plAGMasterMod.) +*/ +class plArmatureComponent : public plComponent +{ +private: + void ISetArmatureSORecurse(plMaxNode *node, plSceneObject *so); + plHKPhysical* IConvertArmaturePhysicsProxy(plMaxNode *node, plMaxNode *proxyNode); + +protected: + plArmatureMod *fArmMod; + + plArmatureComponent() : fArmMod(nil) {} + plArmatureMod* IGenerateMyArmMod(plHKPhysical* myHKPhys, plMaxNode* node); + hsBool IVerifyUsedNode(INode* thisNode, plErrorMsg *pErrMsg, hsBool isHull); + + virtual void IAttachModifiers(plMaxNode *node, plErrorMsg *pErrMsg) = 0; + virtual void ISetupClothes(plMaxNode *node, plArmatureMod *mod, plErrorMsg *pErrMsg) {} + virtual void ISetupAvatarRenderPropsRecurse(plMaxNode *node); + virtual void IAttachShadowCastModifiersRecur(plMaxNode* node, plShadowCaster* caster); // Apply supplied shadowcaster modifier + +public: + enum { + kBounceEventGroupBoolTab_DEAD = 99, + kReportEventGroupBoolTab_DEAD, + kBounceEventGroupChoice_DEAD, + kReportEventGroupChoice_DEAD, + + kBounceGroups, + kReportGroups, + }; + + // Constants for brain selection UI + enum + { + kBrainHuman, + kBrainCritter, + kMaxBrainType, + }; + //static const char *BrainStrings[]; + + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); + + void DeleteThis() { delete this; } +}; + +class plAvatarComponent : public plArmatureComponent +{ + +private: + void IAttachModifiers(plMaxNode *node, plErrorMsg *pErrMsg); + + + +public: + plAvatarComponent(); + + // Do not change any of the entries in this list. Only add at the end. + // All existing ones must be kept for backward compatibility. + enum + { + kWeightRadio, //Insert in v1, removed version 3 (14.07.01) + kAgilityRadio, //Insert in v1, " " " " + kModelRoot, //Insert in v1, removed version 2 (05/09/01) + kPhysicsProxyFeet_DEAD, //Insert in v1, + kFriction, //Insert in v3, + kMaxVelocity, //Insert in v3, + kAcceleration, //Insert in v3, + kTurnForce, //Insert in v3, + kWalkAnim, //Insert in v3, + kRunAnim, //Insert in v3, + kUseAnimationsBool, //Insert in v4, + kRootNode, //Insert in v5, + kMeshNode, //Insert in v5, + kClothingGroup, //Insert in v6, + kBrainType, //Insert in v7, + kSkeleton, //Insert in v8, + kPhysicsProxyTorso_DEAD, + kPhysicsProxyHead_DEAD, + kPhysicsHeight, + kPhysicsWidth, + kBodyAgeName, + kBodyFootstepSoundPage, + kAnimationPrefix, + }; + + virtual void ISetupClothes(plMaxNode *node, plArmatureMod *mod, plErrorMsg *pErrMsg); + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) { return (plArmatureComponent::PreConvert(node, pErrMsg)); } + hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg) { return (plArmatureComponent::Convert(node, pErrMsg)); } + void DeleteThis() { delete this; } + + +}; + + +class plLODAvatarComponent : public plArmatureComponent +{ + +private: + void IAttachModifiers(plMaxNode *node, plErrorMsg *pErrMsg); + +protected: + virtual void ISetupClothes(plMaxNode *node, plArmatureMod *mod, plErrorMsg *pErrMsg); + // Create a single shadowcaster modifier, and apply it to the + // sceneobject hierarchies for each LOD. + virtual void IAttachShadowCastToLODs(plMaxNode* rootNode); + virtual void IAttachShadowCastModifiersRecur(plMaxNode* node, plShadowCaster* caster); + + hsGMaterial *fMaterial; + +public: + VCharArray fLODLevels; //Initialized in the CTR. + + + // Do not change any of the entries in this list. Only add at the end. + // All existing ones must be kept for backward compatibility. + enum + { + kPhysicsProxyFeet_DEAD, //Insert in v1, + kFriction, //Insert in v1, + kMaxVelocity, //Insert in v1, + kAcceleration, //Insert in v1, + kTurnForce, //Insert in v1, + kWalkAnim, //Insert in v1, + kRunAnim, //Insert in v1, + kUseAnimationsBool, //Insert in v1, + kLODLevel, //Insert in v1, + kLODState, //Insert in v1, + kRootNodeTab, //Insert in v1, + kMeshNodeTab, //Insert in v1, + kRootNodeAddBtn, //Insert in v1, + kMeshNodeAddBtn, //Insert in v1, + kClothingGroup, //Insert in v2, + kBrainType, //Insert in v3, + kGroupIdx, //Insert in v4, + kBoneList, //Insert in v4, + kGroupTotals, //Insert in v4, + kLastPick, //Insert in v4, + kSkeleton, //Insert in v5, + kMaterial, //Insert in v6, + kPhysicsProxyTorso_DEAD, + kPhysicsProxyHead_DEAD, + kPhysicsHeight, + kPhysicsWidth, + kBodyFootstepSoundPage, + kAnimationPrefix, + }; + + enum + { + kMaxNumLODLevels = 3, + }; + + plLODAvatarComponent(); + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); + void DeleteThis() { delete this; } + + void RemoveBone(int index); + void AddSelectedBone(); + int GetCurGroupIdx(); + int GetStartIndex(int group); + int GetEndIndex(int group); +}; + +class plAnimatronicComponent : public plLODAvatarComponent +{ + +}; + + + + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBehavioralComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBehavioralComponents.cpp new file mode 100644 index 00000000..962830d5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBehavioralComponents.cpp @@ -0,0 +1,170 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plBehavioralComponents.h" +#include "plComponentReg.h" +#include "resource.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxMain/plPhysicalProps.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plAvatar/plSittingModifier.h" +#include "plResponderComponent.h" + +#include "plPickNode.h" + +void DummyCodeIncludeFuncBehaviors() {} + + +class plSittingComponentProc : public ParamMap2UserDlgProc +{ +protected: + void IUpdateButtonText(HWND hWnd, IParamBlock2 *pb) + { + INode *node = pb->GetINode(plAvBehaviorSittingComponent::kDetector); + SetWindowText(GetDlgItem(hWnd, IDC_DETECTOR), node ? node->GetName() : "(none)"); + } + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + IUpdateButtonText(hWnd, map->GetParamBlock()); + return TRUE; + + case WM_COMMAND: + IParamBlock2 *pb = map->GetParamBlock(); + if (LOWORD(wParam) == IDC_DETECTOR && HIWORD(wParam) == BN_CLICKED) + { + plPick::Activator(pb, plAvBehaviorSittingComponent::kDetector, true); + IUpdateButtonText(hWnd, pb); + return TRUE; + } + break; + } + + return FALSE; + } + void DeleteThis() {} +}; +static plSittingComponentProc gSittingComponentProc; + +CLASS_DESC(plAvBehaviorSittingComponent, gAvBehaviorSittingDesc, "Sitting Behavior", "SitBehavior", COMP_TYPE_AVATAR, BEHAVIORAL_SITTING_CID) + +ParamBlockDesc2 gAvBehavioralSittingBk +( + plComponent::kBlkComp, _T("(sittingBehavior"), 0, &gAvBehaviorSittingDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_BEHAVIOR_SITTING, IDS_COMP_BEHAVIOR_SITTINGS, 0, 0, &gSittingComponentProc, + + // params + plAvBehaviorSittingComponent::kDetector, _T("detector"), TYPE_INODE, 0, 0, + end, + + plAvBehaviorSittingComponent::kApproachFront, _T("ApproachFront"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SIT_APP_FRONT, + p_default, FALSE, + end, + + plAvBehaviorSittingComponent::kApproachLeft, _T("ApproachLeft"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SIT_APP_LEFT, + p_default, TRUE, + end, + + plAvBehaviorSittingComponent::kApproachRight, _T("ApproachRight"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SIT_APP_RIGHT, + p_default, TRUE, + end, + + plAvBehaviorSittingComponent::kDisableForward, _T("DisableForward"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SIT_NOFORWARD, + p_default, TRUE, + end, + + end +); + +plAvBehaviorSittingComponent::plAvBehaviorSittingComponent() +{ + fClassDesc = &gAvBehaviorSittingDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plAvBehaviorSittingComponent::SetupProperties(plMaxNode* node, plErrorMsg *pErrMsg) +{ + plActivatorBaseComponent::SetupProperties(node, pErrMsg); + + // Need a coordinate interface to tell the avatar where to drop trou + node->SetForceLocal(true); + + return true; +} + +hsBool plAvBehaviorSittingComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plMaxNode *detectNode = (plMaxNode*)fCompPB->GetINode(kDetector); + plComponentBase *detectComp = detectNode ? detectNode->ConvertToComponent() : nil; + if (detectComp) + { + bool hasFrontApproach = fCompPB->GetInt(ParamID(kApproachFront)) ? true : false; + bool hasLeftApproach = fCompPB->GetInt(ParamID(kApproachLeft)) ? true : false; + bool hasRightApproach = fCompPB->GetInt(ParamID(kApproachRight)) ? true : false; + + // Create our key here and give it to the detector so it will notify us + plSittingModifier *sitMod = TRACKED_NEW plSittingModifier(hasFrontApproach, hasLeftApproach, hasRightApproach); + if (fCompPB->GetInt(ParamID(kDisableForward))) + sitMod->fMiscFlags |= plSittingModifier::kDisableForward; + + plKey key = node->AddModifier(sitMod, IGetUniqueName(node)); + detectComp->AddReceiverKey(key); + fLogicModKeys[node] = key; + } + return true; +} + +hsBool plAvBehaviorSittingComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plKey logicKey = fLogicModKeys[node]; + if (logicKey) + { + plSittingModifier *sitMod = plSittingModifier::ConvertNoRef(logicKey->GetObjectPtr()); + + // XXX sitMod->SetSeekTime(fCompPB->GetFloat(kSeekTimeFloat)); + + // Get all the keys who want to be notified when the avatar ass hits the seat + hsTArray receivers; + IGetReceivers(node, receivers); + for (int i = 0; i < receivers.Count(); i++) + sitMod->AddNotifyKey(receivers[i]); + } + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBehavioralComponents.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBehavioralComponents.h new file mode 100644 index 00000000..48b3bcce --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBehavioralComponents.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_BEHAVIORAL_COMPONENTS_INC +#define PL_BEHAVIORAL_COMPONENTS_INC + +#include "plActivatorBaseComponent.h" + +class plSittingModifier; + +class plAvBehaviorSittingComponent : public plActivatorBaseComponent +{ +public: + enum + { + kTriggerNode_DEAD, + kSeekTimeFloat_DEAD, + kBoundState_DEAD, + kDetector, + kUseSmartSeek_DEAD, + kApproachFront, + kApproachLeft, + kApproachRight, + kDisableForward, + }; + + plAvBehaviorSittingComponent(); + hsBool SetupProperties(plMaxNode* node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode* node, plErrorMsg* plErrorMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +#define BEHAVIORAL_SITTING_CID Class_ID(0x617e22cc, 0x31ef310d) + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBipedKiller.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBipedKiller.cpp new file mode 100644 index 00000000..8776d9a1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBipedKiller.cpp @@ -0,0 +1,758 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +// BIPEDKILLER + +/////////// +// +// INCLUDES +// +/////////// + +// theirs +#include + +#include "max.h" +#include "resource.h" +#include "CS/bipexp.h" +#include "decomp.h" + +#pragma warning(disable: 4786) // disable warnings about excessive STL symbol name length + +#include +#include +#include "hsStlSortUtils.h" + +// ours +#include "plComponent.h" +#include "plComponentReg.h" +#include "plMiscComponents.h" +#include "../MaxMain/plMaxNodeBase.h" + +#include "../plTransform/hsAffineParts.h" +#include "hsMatrix44.h" + +////////////// +// +// LOCAL TYPES +// +////////////// + +// NODETMINFO +// A local handy thing to remember a matrix and the time we sampled it +struct nodeTMInfo +{ + TimeValue fTime; + Matrix3 fMat3; +}; + +// PLSAMPLEVEC +// A vector of matrix samples +typedef std::vector plSampleVec; + +// PLSAMPLEVECMAP +// A map relating bone names to plSampleVecs +typedef std::map plSampleVecMap; + +///////////// +// +// PROTOTYPES +// +///////////// + +void ProcessNodeRecurse(INode *node, INode *parent, Interface *theInterface); +void ProcessBipedNodeRecurse(INode *bipNode, INode *newParent, Interface *theInterface); +void ProcessNonBipedNodeRecurse(INode *node, INode *parent, Interface *theInterface); + +int LimitTransform(INode* node, Matrix3* nodeTM); +void GetParts(Int32 i, std::vector& mat3Array, hsAffineParts* parts); + +Quat GetRotKey(Int32 i, std::vector& mat3Array, hsAffineParts* parts); +Point3 GetPosKey(Int32 i, std::vector& mat3Array, hsAffineParts* parts); +ScaleValue GetScaleKey(Int32 i, std::vector& mat3Array, hsAffineParts* parts); + +Quat MakeRotKey(INode *node, INode *parent, TimeValue t); +Point3 MakePosKey(INode *node, INode *parent, TimeValue t); +ScaleValue MakeScaleKey(INode *node, INode *parent, TimeValue t); + +AffineParts GetLocalNodeParts(INode *node, INode *parent, TimeValue t); + +bool ExportableAnimationController(INode* node); +bool HasBipController(INode* node); +Quat GetRotKey(Int32 i, std::vector& mat3Array); + + +plSampleVec * SampleNodeMotion(INode* node, INode* parent, int sampleRate, Interface *theInterface); +plSampleVec * SampleNodeMotion(INode * node, INode* parent, int sampleRate, TimeValue start, TimeValue end); +void ReapplyAnimation(INode *node, plSampleVec *samples); +void FreeMotionSamples(plSampleVec *samples); + +///////////////// +// +// IMPLEMENTATION +// +///////////////// + +// REMOVEBIPED +void RemoveBiped(INode *bipRoot, Interface *theInterface) +{ + SuspendAnimate(); + AnimateOn(); + + // remember Max's default controllers (for the user) + ClassDesc* defaultRotCtrl=GetDefaultController(CTRL_ROTATION_CLASS_ID); + ClassDesc* defaultPosCtrl=GetDefaultController(CTRL_POSITION_CLASS_ID); + ClassDesc* defaultScaleCtrl=GetDefaultController(CTRL_SCALE_CLASS_ID); + + // change default controllers to linear to create linear controllers + // since we have no tan info + DllDir* dllDir=&theInterface->GetDllDir(); + ClassDirectory* classDir=&dllDir->ClassDir(); + + ClassDesc* rotCtrl = classDir->FindClass( SClass_ID(CTRL_ROTATION_CLASS_ID), + Class_ID(TCBINTERP_ROTATION_CLASS_ID,0)); // was Class_ID(LININTERP_ROTATION_CLASS_ID,0)); + + ClassDesc* posCtrl = classDir->FindClass( SClass_ID(CTRL_POSITION_CLASS_ID), + Class_ID(LININTERP_POSITION_CLASS_ID, 0)); + + ClassDesc* scaleCtrl = classDir->FindClass( SClass_ID(CTRL_SCALE_CLASS_ID), + Class_ID(LININTERP_SCALE_CLASS_ID, 0)); + + SetDefaultController(CTRL_ROTATION_CLASS_ID, rotCtrl); + SetDefaultController(CTRL_POSITION_CLASS_ID, posCtrl); + SetDefaultController(CTRL_SCALE_CLASS_ID, scaleCtrl); + + ProcessNodeRecurse(bipRoot, nil, theInterface); + + //deinit + ResumeAnimate(); + + // remember Max's default controllers (for the user) + SetDefaultController(CTRL_ROTATION_CLASS_ID, defaultRotCtrl); + SetDefaultController(CTRL_POSITION_CLASS_ID, defaultPosCtrl); + SetDefaultController(CTRL_SCALE_CLASS_ID, defaultScaleCtrl); +} + +// PROCESSNODERECURSE +void ProcessNodeRecurse(INode *node, INode *parent, Interface *theInterface) +{ + if(HasBipController(node)) + { + ProcessBipedNodeRecurse(node, parent, theInterface); + } else { + ProcessNonBipedNodeRecurse(node, parent, theInterface); + } +} + +// PROCESSBIPNODERECURSE +// When we find a Biped-controlled node in our hierarchy, we need to find one non-biped +// child and promote it to the place of the biped node in the hierarchy. The siblings +// of the promoted node will become its children, as will the original children from the +// biped node. +void ProcessBipedNodeRecurse(INode *bipNode, INode *parent, Interface *theInterface) +{ + int numChildren = bipNode->NumberOfChildren(); + char *bipName = bipNode ? bipNode->GetName() : nil; + INode *replacement = nil; + + for (int i = 0; i < numChildren; i++) + { + INode *child = bipNode->GetChildNode(i); + char *childName = child ? child->GetName() : nil; + + if( ! HasBipController(child) ) + { + replacement = child; // this child is going to be our replacement for this bipnode + + // sample the animation (into global space) + plSampleVec *samples = SampleNodeMotion(replacement, bipNode, 1, theInterface); + + // detach from the parent (this blows away the animation) + replacement->Detach(0); + + // attach the node to the biped's parent. + parent->AttachChild(replacement); + + ReapplyAnimation(child, samples); + FreeMotionSamples(samples); + + // we only need one replacement for the bip node + break; + } + } + + if(replacement) + { + // reparent the siblings to the newly promoted replacement node + numChildren = bipNode->NumberOfChildren(); + for (i = 0; i < numChildren; i++) + { + INode *child = bipNode->GetChildNode(i); + + if( HasBipController(child) ) + { + ProcessBipedNodeRecurse(child, replacement, theInterface); + } else { + child->Detach(0); // remove the (non-bip) child from the bip node + replacement->AttachChild(child); // attach it to the non-bip parent + + ProcessNonBipedNodeRecurse(child, replacement, theInterface); + } + } + } else { + // this is an error condition: we've got a bip node that has no non-bip child for us to promote + char buf[256]; + sprintf(buf, "Couldn't find non-bip node to transfer motion to for bip node %s\n", bipNode->GetName()); + hsStatusMessage(buf); + } +} + +// PROCESSNONBIPEDNODERECURSE +// Sample motion for a hierarchy that does not have any Biped controllers in it. +void ProcessNonBipedNodeRecurse(INode *node, INode *parent, Interface *theInterface) +{ + if( ! ExportableAnimationController(node) ) + { + plSampleVec *samples = SampleNodeMotion(node, parent, 2, theInterface); + ReapplyAnimation(node, samples); + FreeMotionSamples(samples); + } + + int numChildren = node->NumberOfChildren(); + for (int i = 0; i < numChildren; i++) + { + INode *child = node->GetChildNode(i); + + ProcessNodeRecurse(child, node, theInterface); + } +} + +// ADJUSTROTKEYS +void AdjustRotKeys(INode *node) +{ + Control *controller = node->GetTMController(); + Control *rotControl = controller->GetRotationController(); + IKeyControl *rotKeyCont = GetKeyControlInterface(rotControl); + int numKeys = rotKeyCont->GetNumKeys(); + + for(int i = 0; i < numKeys; i++) + { + ITCBKey key; + rotKeyCont->GetKey(i, &key); + + key.cont = 0; + rotKeyCont->SetKey(i, &key); + + } + +} + +#define boolTrue = (0 == 0); +#define boolFalse = (0 == 1); + +// *** todo: generalize this for rotation keys as well. +int CompareKeys(ILinPoint3Key &a, ILinPoint3Key &b) +{ + int result = a.val.Equals(b.val, .001); +#if 0 + hsStatusMessageF("COMPAREKEYS(point): (%f %f %f) vs (%f, %f, %f) = %s\n", a.val.x, a.val.y, a.val.z, b.val.x, b.val.y, b.val.z, result ? "yes" : "no"); +#endif + return result; +} + +template +void ReduceKeys(INode *node, IKeyControl *keyCont) +{ + + keyCont->SortKeys(); // ensure the keys are sorted by time + + int to; // the next key we're setting + int from; // the next key we're examining + int origNumKeys = keyCont->GetNumKeys(); + int finalNumKeys = origNumKeys; + + for (to = 1, from = 1; from < origNumKeys - 1; to++, from++) + { + T prevKey, curKey, nextKey; + + keyCont->GetKey(from - 1, &prevKey); + keyCont->GetKey(from, &curKey); + keyCont->GetKey(from + 1, &nextKey); + + if (CompareKeys(curKey, prevKey) && CompareKeys(curKey, nextKey)) + finalNumKeys--; // skip it + else + keyCont->SetKey(to, &curKey); // copy current key + } + // copy the last one without peeking ahead + T lastKey; + keyCont->GetKey(from, &lastKey); + keyCont->SetKey(to, &lastKey); + + keyCont->SetNumKeys(finalNumKeys); + keyCont->SortKeys(); +} + +void EliminateScaleKeys(INode *node, IKeyControl *keyCont) +{ + int numKeys = keyCont->GetNumKeys(); + ILinScaleKey last; + keyCont->GetKey(numKeys - 1, &last); + keyCont->SetKey(1, &last); // move the last to the second + keyCont->SetNumKeys(2); +} + +// REAPPLYANIMATION +// Now that we've reparented a node within the hierarchy, re-apply all its animation. +void ReapplyAnimation(INode *node, plSampleVec *samples) +{ + Control *controller = node->GetTMController(); + + Control *rotControl = NewDefaultRotationController(); // we set the default rotation controller type above in RemoveBiped() + Control *posControl = NewDefaultPositionController(); // '' '' + Control *scaleControl = NewDefaultScaleController(); // '' '' + + controller->SetRotationController(rotControl); + controller->SetPositionController(posControl); + controller->SetScaleController(scaleControl); + + for(int i = 0; i < samples->size(); i++) + { + nodeTMInfo *info = (*samples)[i]; + Matrix3 m = info->fMat3; + TimeValue t = info->fTime; + +#if 1 + node->SetNodeTM(t, m); +#else + AffineParts parts; + + INode *parent = node->GetParentNode(); + Matrix3 parentTM = parent->GetNodeTM(t); + Matrix3 invParentTM = Inverse(parentTM); + m *= invParentTM; + + decomp_affine(m, &parts); + + Quat q(parts.q.x, parts.q.y, parts.q.z, parts.q.w); + Point3 p(parts.t.x, parts.t.y, parts.t.z); + + rotControl->SetValue(t, q); + posControl->SetValue(t, p); +#endif + } + + IKeyControl *posKeyCont = GetKeyControlInterface(posControl); + IKeyControl *scaleKeyCont = GetKeyControlInterface(scaleControl); + + ReduceKeys(node, posKeyCont); + EliminateScaleKeys(node, scaleKeyCont); + // grrrr ReduceKeys(node, scaleKeyCont); +} + +// HASBIPCONTROLLER +bool HasBipController(INode* node) +{ + if (!node) + return false; + Control* c = node->GetTMController(); + if (c && ((c->ClassID()== BIPSLAVE_CONTROL_CLASS_ID) || + (c->ClassID()== BIPBODY_CONTROL_CLASS_ID) || + (c->ClassID()== FOOTPRINT_CLASS_ID)) ) + return true; + return false; + +} + +// EXPORTABLEANIMATIONCONTROLLER +bool ExportableAnimationController(INode* node) +{ + bool result = false; + + if(node) + { + Control *c = node->GetTMController(); + if(c) + { + Class_ID id = c->ClassID(); + if(id == Class_ID(LININTERP_ROTATION_CLASS_ID, 0) + || id == Class_ID(PRS_CONTROL_CLASS_ID, 0) + || id == Class_ID(LININTERP_POSITION_CLASS_ID, 0) + || id == Class_ID(TCBINTERP_FLOAT_CLASS_ID, 0) + || id == Class_ID(TCBINTERP_POSITION_CLASS_ID, 0) + || id == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0) + || id == Class_ID(TCBINTERP_POINT3_CLASS_ID, 0) + || id == Class_ID(TCBINTERP_SCALE_CLASS_ID, 0)) + { + result = true; + } + } + } + return result; +} + +// SAMPLENODEMOTION +// top level function for sampling all the motion on a single node +plSampleVec * SampleNodeMotion(INode* node, INode* parent, int sampleRate, Interface *theInterface) +{ + Interval interval = theInterface->GetAnimRange(); + TimeValue start = interval.Start(); // in ticks + TimeValue end = interval.End(); + + sampleRate *= GetTicksPerFrame(); // convert sample rate to ticks + + return SampleNodeMotion(node, parent, sampleRate, start, end); +} + +// SAMPLENODEMOTION +// sample all the motion on a single node +// intended for use in the context of a full tree traversal +plSampleVec * SampleNodeMotion(INode * node, INode* parent, int sampleRate, TimeValue start, TimeValue end) +{ + plSampleVec *result = TRACKED_NEW plSampleVec; + + bool done = false; + + for(int i = start; ! done; i += sampleRate) + { + if (i > end) i = end; + if (i == end) done = true; + + // Get key time + TimeValue keyTime = i; + int frameNum= keyTime / GetTicksPerFrame(); + + // get localTM + nodeTMInfo * nti = TRACKED_NEW nodeTMInfo; + nti->fTime = keyTime; + Matrix3 localTM = node->GetNodeTM(keyTime); + + nti->fMat3 = localTM; + result->push_back(nti); + } + return result; +} + +// FREEMOTIONSAMPLES +void FreeMotionSamples(plSampleVec *samples) +{ + int count = samples->size(); + for(int i = 0; i < count; i++) + { + delete (*samples)[i]; + } + delete samples; +} + +// LIMITTRANSFORM +// Check if this node is marked as having a constrained transform. +// Meaning ignore part of the transform for this node and push it down to its kids. +int LimitTransform(INode* node, Matrix3* nodeTM) +{ +/* NOT sure if we want to support this functionality: probably eventually. + hsBool32 noRotX=false,noRotY=false,noRotZ=false; + hsBool32 noRot=gUserPropMgr.UserPropExists(node,"BEHNoRot") || MatWrite::HasToken(node->GetName(), "norot"); + if (!noRot) + { + noRotX=gUserPropMgr.UserPropExists(node,"BEHNoRotX") || MatWrite::HasToken(node->GetName(), "norotx"); + noRotY=gUserPropMgr.UserPropExists(node,"BEHNoRotY") || MatWrite::HasToken(node->GetName(), "noroty"); + noRotZ=gUserPropMgr.UserPropExists(node,"BEHNoRotZ") || MatWrite::HasToken(node->GetName(), "norotz"); + } + + hsBool32 noTransX=false,noTransY=false,noTransZ=false; + hsBool32 noTrans=gUserPropMgr.UserPropExists(node,"BEHNoTrans") || MatWrite::HasToken(node->GetName(), "notrans"); + if (!noTrans) + { + noTransX=gUserPropMgr.UserPropExists(node,"BEHNoTransX") || MatWrite::HasToken(node->GetName(), "notransx"); + noTransY=gUserPropMgr.UserPropExists(node,"BEHNoTransY") || MatWrite::HasToken(node->GetName(), "notransy"); + noTransZ=gUserPropMgr.UserPropExists(node,"BEHNoTransZ") || MatWrite::HasToken(node->GetName(), "notransz"); + } + + if (noRot || noTrans || + noRotX || noRotY || noRotZ || + noTransX || noTransY || noTransZ) + { + Matrix3 tm(true); // identity + + Quat q(*nodeTM); // matrix to quat + float eulerAng[3]; + QuatToEuler(q, eulerAng); // to euler + + // rotation + if (!noRot && !noRotX) + tm.RotateX(eulerAng[0]); + if (!noRot && !noRotY) + tm.RotateY(eulerAng[1]); + if (!noRot && !noRotZ) + tm.RotateZ(eulerAng[2]); + + // translation + Point3 trans=nodeTM->GetTrans(); + if (noTrans || noTransX) + trans.x=0; + if (noTrans || noTransY) + trans.y=0; + if (noTrans || noTransZ) + trans.z=0; + tm.Translate(trans); + + // copy back + *nodeTM = tm; + return true; + } +*/ + return false; +} + + +/* +////////// +// ARCHIVE +////////// +// Stuff we're not using but that looks kind of handy and which we might use again at some point. + +///////////////////////////////// +///////////////////////////////// +/// SAMPLETREEMOTION +/// Sample motion for all of the non-bip bones in the heirarchy. +/// Need to sample the motion before rearranging the hierarchy and then +/// apply it after rearranging; hence the intermediate storage format. + +// SAMPLETREEMOTION +// Sample all the (non-bip) motion in the whole tree +plSampleVecMap *SampleTreeMotion(INode* node, INode* parent, int sampleRate, Interface *theInterface) +{ + Interval interval = theInterface->GetAnimRange(); + TimeValue start = interval.Start(); // in ticks + TimeValue end = interval.End(); + plSampleVecMap *ourMap = TRACKED_NEW plSampleVecMap(); + + sampleRate *= GetTicksPerFrame(); // convert sample rate to ticks + + SampleTreeMotionRecurse(node, parent, sampleRate, start, end, ourMap); + + return ourMap; +} + +// SAMPLETREEMOTIONRECURSE +void SampleTreeMotionRecurse(INode * node, INode* parent, int sampleRate, + TimeValue start, TimeValue end, plSampleVecMap *ourMap) +{ + // if it's not a bip, sample the fuck out of it + if(!HasBipController(node)) + { + char *nodeName = node->GetName(); + char *nameCopy = TRACKED_NEW char[strlen(nodeName) + 1]; + strcpy(nameCopy, nodeName); + + plSampleVec *branch = SampleNodeMotion(node, parent, sampleRate, start, end); + (*ourMap)[nameCopy] = branch; + } + + // whether it's a bip or not, paw through its children + for(int i = 0; i < node->NumberOfChildren(); i++) + { + INode *child = node->GetChildNode(i); + SampleTreeMotionRecurse(child, node, sampleRate, start, end, ourMap); + } +} + +// GETPARTS +void GetParts(Int32 i, std::vector& mat3Array, hsAffineParts* parts) +{ + hsAssert(parts, "nil parts"); + + // decomp matrix + gemAffineParts ap; + hsMatrix44 tXform = plMaxNodeBase::Matrix3ToMatrix44(mat3Array[i]->fMat3); + + decomp_affine(tXform.fMap, &ap); + AP_SET((*parts), ap); +} + +// MAKEROTKEY +Quat MakeRotKey(INode *node, INode *parent, TimeValue t) +{ + AffineParts parts = GetLocalNodeParts(node, parent, t); + + Quat q(parts.q.x, parts.q.y, parts.q.z, parts.q.w); + if( parts.f < 0.f ) + { +// q = Quat(parts.q.x, parts.q.y, parts.q.z, -parts.q.w); + } + else + { +// q=Quat(-parts.q.x, -parts.q.y, -parts.q.z, parts.q.w); + } + + return q; +} + +Quat GetRotKey(Int32 i, std::vector& mat3Array) +{ + Matrix3 m = mat3Array[i]->fMat3; + AffineParts parts; + + decomp_affine(m, &parts); + + Quat q(parts.q.x, parts.q.y, parts.q.z, parts.q.w); + + return q; +} + + +// GETROTKEY +Quat GetRotKey(Int32 i, std::vector& mat3Array, hsAffineParts* parts) +{ + hsAffineParts myParts; + if (!parts) + { + parts=&myParts; + GetParts(i, mat3Array, parts); + } + + Quat q; + if( parts->fF < 0.f ) + { + q = Quat(parts->fQ.fX, parts->fQ.fY, parts->fQ.fZ, -parts->fQ.fW); // ??? why are we inverting W? +#if 0 + if( false) + { + Point3 ax; + float ang; + AngAxisFromQ(q, &ang, ax); + //ang -= hsScalarPI; + ax = -ax; + q = QFromAngAxis(ang, ax); + } +#endif + } + else + { + q=Quat(-parts->fQ.fX, -parts->fQ.fY, -parts->fQ.fZ, parts->fQ.fW); + } + + return q; +} + +// MAKEPOSKEY +Point3 MakePosKey(INode *node, INode *parent, TimeValue t) +{ + AffineParts parts = GetLocalNodeParts(node, parent, t); + + return Point3(parts.t.x, parts.t.y, parts.t.z); +} + + +// GETPOSKEY +Point3 GetPosKey(Int32 i, std::vector& mat3Array, hsAffineParts* parts) +{ + hsAffineParts myParts; + if (!parts) + { + parts=&myParts; + GetParts(i, mat3Array, parts); + } + return Point3(parts->fT.fX, parts->fT.fY, parts->fT.fZ); +} + +// MAKESCALEKEY +ScaleValue MakeScaleKey(INode *node, INode *parent, TimeValue t) +{ + Matrix3 m1 = node->GetNodeTM(t); + hsMatrix44 hsM = plMaxNodeBase::Matrix3ToMatrix44(m1); + gemAffineParts ap; + hsAffineParts hsParts; + + decomp_affine(hsM.fMap, &ap); + AP_SET(hsParts, ap); + + Point3 sAx1; + sAx1=Point3(hsParts.fK.fX, hsParts.fK.fY, hsParts.fK.fZ); + if( hsParts.fF < 0.f ) + { + sAx1=-sAx1; + } + Quat sQ1(hsParts.fU.fX, hsParts.fU.fY, hsParts.fU.fZ, hsParts.fU.fW); + +// return ScaleValue(sAx, sQ); + + AffineParts parts = GetLocalNodeParts(node, parent, t); + + Point3 sAx(parts.k.x, parts.k.y, parts.k.z); + if( parts.f < 0.f ) + { + sAx=-sAx; + } + Quat sQ(parts.u.x, parts.u.y, parts.u.z, parts.u.w); + + return ScaleValue(sAx, sQ); +} + +// GETSCALEKEY +ScaleValue GetScaleKey(Int32 i, std::vector& mat3Array, hsAffineParts* parts) +{ + hsAffineParts myParts; + if (!parts) + { + parts=&myParts; + GetParts(i, mat3Array, parts); + } + + Point3 sAx; + sAx=Point3(parts->fK.fX, parts->fK.fY, parts->fK.fZ); + if( parts->fF < 0.f ) + { + sAx=-sAx; + } + Quat sQ(parts->fU.fX, parts->fU.fY, parts->fU.fZ, parts->fU.fW); + + return ScaleValue(sAx, sQ); +} + + +// GETLOCALNODEPARTS +AffineParts GetLocalNodeParts(INode *node, INode *parent, TimeValue t) +{ + Matrix3 localTM = node->GetNodeTM(t); // world transform of source node + + INode *parent2 = node->GetParentNode(); + // localize it + Matrix3 parentTMX = parent->GetNodeTM(t); + Matrix3 parentTM = parent2->GetNodeTM(t); + + Matrix3 invParent = Inverse(parentTM); + localTM *= invParent; + + AffineParts parts; + + decomp_affine(localTM, &parts); + + return parts; +} + + +*/ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBlowComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBlowComponent.cpp new file mode 100644 index 00000000..6b5f9d9a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBlowComponent.cpp @@ -0,0 +1,189 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "meshdlib.h" +#include "dummy.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../MaxMain/plMaxNode.h" + +#include "hsTypes.h" + +#include "plBlowComponent.h" + +#include "../pfAnimation/plBlower.h" +#include "plFlexibilityComponent.h" + +// Blow component first, related Flexibility component at EOF. + +// Preliminary setup bookkeeping +void DummyCodeIncludeFuncBlow() +{ +} + +CLASS_DESC(plBlowComponent, gBlowCompDesc, "Wind Bone", "Blow", COMP_TYPE_DISTRIBUTOR, BLOW_COMP_CID) + +ParamBlockDesc2 gBlowBk +( + plComponent::kBlkComp, _T("Blow"), 0, &gBlowCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_BLOW, IDS_COMP_BLOWS, 0, 0, nil, + + plBlowComponent::kStrength, _T("Strength"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_BLOW_STRENGTH, IDC_COMP_BLOW_STRENGTH_SPIN, 1.0, + end, + + plBlowComponent::kSpeed, _T("Speed"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_BLOW_SPEED, IDC_COMP_BLOW_SPEED_SPIN, 1.0, + end, + + plBlowComponent::kFlutter, _T("Flutter"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_BLOW_FLUTTER, IDC_COMP_BLOW_FLUTTER_SPIN, 1.0, + end, + + plBlowComponent::kConstancy, _T("Constancy"), TYPE_FLOAT, 0, 0, + p_default, 25.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_BLOW_CONSTANCY, IDC_COMP_BLOW_CONSTANCY_SPIN, 1.0, + end, + + end +); + + + +// Component implementation +plBlowComponent::plBlowComponent() +{ + fClassDesc = &gBlowCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plBlowComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetDrawable(false); + node->SetForceLocal(true); + + return true; +} + +hsBool plBlowComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plBlowComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plBlower* pMod = TRACKED_NEW plBlower; + + float strength = fCompPB->GetFloat(kStrength) * 0.01f; + float speed = fCompPB->GetFloat(kSpeed) * 0.01f; + float flutter = fCompPB->GetFloat(kFlutter) * 0.01f; + float constancy = fCompPB->GetFloat(kConstancy) * 0.01f; + + pMod->SetMasterPower(pMod->GetMasterPower() * strength); + pMod->SetMasterFrequency(pMod->GetMasterFrequency() * speed); + pMod->SetImpulseRate(pMod->GetImpulseRate() * flutter); + pMod->SetConstancy(constancy); + + node->AddModifier(pMod, IGetUniqueName(node)); + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Flexibility Component +// +// + +//Class that accesses the paramblock below. +//Max desc stuff necessary below. +CLASS_DESC(plFlexibilityComponent, gFlexibilityDesc, "Flexibility", "Flexibility", COMP_TYPE_DISTRIBUTOR, FLEXIBILITY_COMP_CID) + +ParamBlockDesc2 gFlexibilityBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("Flexibility"), 0, &gFlexibilityDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FLEXIBILITY, IDS_COMP_FLEXIBILITYS, 0, 0, NULL, + + plFlexibilityComponent::kFlexibility, _T("Flexibility"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.f, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FLEX_FLEX, IDC_COMP_FLEX_FLEX_SPIN, 1.0, + end, + + plFlexibilityComponent::kInterRand, _T("InterRand"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.f, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FLEX_INTER, IDC_COMP_FLEX_INTER_SPIN, 1.0, + end, + + plFlexibilityComponent::kIntraRand, _T("IntraRand"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.f, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FLEX_INTRA, IDC_COMP_FLEX_INTRA_SPIN, 1.0, + end, + + end +); + +plFlexibilityComponent::plFlexibilityComponent() +{ + fClassDesc = &gFlexibilityDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +Point3 plFlexibilityComponent::GetFlexibility() const +{ + // 1.e-2f scale goes from percent to fraction. + // returns (in order), + // the flexibility + // the variation between objects (object still moves as unit) + // the variation within an object (different verts are more or less flexible than others). + return Point3(fCompPB->GetFloat(kFlexibility) * 1.e-2f, + fCompPB->GetFloat(kInterRand) * 1.e-2f, // From percent to fraction. + fCompPB->GetFloat(kIntraRand) * 1.e-2f); // From percent to fraction. +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBlowComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBlowComponent.h new file mode 100644 index 00000000..d94c4679 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plBlowComponent.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plBlowComponent_inc +#define plBlowComponent_inc + +const Class_ID BLOW_COMP_CID(0x2908438f, 0x6c272509); + +class plBlowComponent : public plComponent +{ +public: + enum { + kStrength, + kSpeed, + kFlutter, + kConstancy + }; +public: + plBlowComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + +#endif // plBlowComponent_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCAnimParamBlock.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCAnimParamBlock.cpp new file mode 100644 index 00000000..5c88fe23 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCAnimParamBlock.cpp @@ -0,0 +1,158 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plCAnimParamBlock.cpp - Common animation paramblock and associated code // +// // +//// History ///////////////////////////////////////////////////////////////// +// // +// 10.3.02 mcn - Created from the gory destruction of the separated anim // +// component and material animations. // +// // +//// Notes /////////////////////////////////////////////////////////////////// +// // +// This file is shared currently between MaxComponent and MaxPlasmaMtl. // +// Since they are in separate plugins currently, you MUST include this // +// .cpp file in both projects so that they can both compile in the code. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "plCAnimParamBlock.h" + +#include "../plInterp/plAnimEaseTypes.h" +#include "../MaxPlasmaMtl/plPassMtlBase.h" + + +//// Static ParamBlock Template ////////////////////////////////////////////// +// This is the base paramBlock for all animation paramBlocks. The macro that +// creates the latter just includes this one (defined here to keep the .h +// file simple, among other reasons) + +// Since we can't reference plComponent directly, this #define is just so we +// remember what the hell it is. If this changes in plComponentBase.h, change +// it here too. +int plCAnimPB::plPBBaseDec::kPlComponentBlkID = 0; +int plCAnimPB::plPBBaseDec::kPlComponentRefID = 0; + + +// Maybe plEaseAccessor should be moved here? +static plEaseAccessor sAnimCompEaseAccessor( plCAnimPB::plPBBaseDec::kPlComponentBlkID, + plCAnimPB::kAnimEaseInMin, plCAnimPB::kAnimEaseInMax, plCAnimPB::kAnimEaseInLength, + plCAnimPB::kAnimEaseOutMin, plCAnimPB::kAnimEaseOutMax, plCAnimPB::kAnimEaseOutLength); + +ParamBlockDesc2 plCAnimPB::plPBBaseDec::fAnimBlock +( + kPlComponentBlkID, _T( "animation" ), 0, NULL, 0/*P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP*/, kPlComponentRefID, + + // map rollups (don't define Procs, they'll just be overridden in the macro defs anyway) +/* 2, + plCAnimPB::kRollMain, IDD_COMP_ANIM, IDS_COMP_ANIM, 0, 0, nil, + plCAnimPB::kRollEase, IDD_COMP_ANIM_EASE, IDS_COMP_ANIM_EASE, 0, APPENDROLL_CLOSED, nil, +*/ + + // Anim Main rollout + plCAnimPB::kAnimAutoStart, _T("autoStart"), TYPE_BOOL, 0, 0, + p_ui, plCAnimPB::kRollMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_AUTOSTART_CKBX, + p_default, FALSE, + end, + plCAnimPB::kAnimLoop, _T("loop"), TYPE_BOOL, 0, 0, + p_ui, plCAnimPB::kRollMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_LOOP_CKBX, + p_default, FALSE, + end, + plCAnimPB::kAnimName, _T("animName"), TYPE_STRING, 0, 0, + end, + plCAnimPB::kAnimUseGlobal, _T("UseGlobal"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plCAnimPB::kRollMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_USE_GLOBAL, + end, + plCAnimPB::kAnimGlobalName, _T("GlobalName"), TYPE_STRING, 0, 0, + p_default, _T(""), + end, + plCAnimPB::kAnimLoopName, _T("loopName"), TYPE_STRING, 0, 0, + end, + plCAnimPB::kAnimPhysAnim, _T("PhysAnim"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, plCAnimPB::kRollMain, TYPE_SINGLECHEKBOX, IDC_COMP_ANIM_PHYSANIM, + end, + + // Anim Ease rollout + plCAnimPB::kAnimEaseInType, _T("easeInType"), TYPE_INT, 0, 0, + p_ui, plCAnimPB::kRollEase, TYPE_RADIO, 3, IDC_COMP_ANIM_EASE_IN_NONE, IDC_COMP_ANIM_EASE_IN_CONST_ACCEL, IDC_COMP_ANIM_EASE_IN_SPLINE, + p_vals, plAnimEaseTypes::kNoEase, plAnimEaseTypes::kConstAccel, plAnimEaseTypes::kSpline, + p_default, plAnimEaseTypes::kNoEase, + end, + plCAnimPB::kAnimEaseInLength, _T("easeInLength"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, plCAnimPB::kRollEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_IN_TIME, IDC_COMP_ANIM_EASE_IN_TIME_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + plCAnimPB::kAnimEaseInMin, _T("easeInMin"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, plCAnimPB::kRollEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_IN_MIN, IDC_COMP_ANIM_EASE_IN_MIN_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + plCAnimPB::kAnimEaseInMax, _T("easeInMax"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, plCAnimPB::kRollEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_IN_MAX, IDC_COMP_ANIM_EASE_IN_MAX_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + + plCAnimPB::kAnimEaseOutType, _T("easeOutType"), TYPE_INT, 0, 0, + p_ui, plCAnimPB::kRollEase, TYPE_RADIO, 3, IDC_COMP_ANIM_EASE_OUT_NONE, IDC_COMP_ANIM_EASE_OUT_CONST_ACCEL, IDC_COMP_ANIM_EASE_OUT_SPLINE, + p_vals, plAnimEaseTypes::kNoEase, plAnimEaseTypes::kConstAccel, plAnimEaseTypes::kSpline, + p_default, plAnimEaseTypes::kNoEase, + end, + plCAnimPB::kAnimEaseOutLength, _T("easeOutLength"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, kRollEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_OUT_TIME, IDC_COMP_ANIM_EASE_OUT_TIME_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + plCAnimPB::kAnimEaseOutMin, _T("easeOutMin"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, plCAnimPB::kRollEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_OUT_MIN, IDC_COMP_ANIM_EASE_OUT_MIN_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + plCAnimPB::kAnimEaseOutMax, _T("easeOutMax"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, plCAnimPB::kRollEase, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ANIM_EASE_OUT_MAX, IDC_COMP_ANIM_EASE_OUT_MAX_SPIN, 1.0, + p_accessor, &gAnimCompEaseAccessor, + end, + + end +); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCAnimParamBlock.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCAnimParamBlock.h new file mode 100644 index 00000000..130043db --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCAnimParamBlock.h @@ -0,0 +1,130 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plCAnimParamBlock.h - Common animation paramblock and associated code // +// // +//// History ///////////////////////////////////////////////////////////////// +// // +// 10.3.02 mcn - Created from the gory destruction of the separated anim // +// component and material animations. // +// // +//// Notes /////////////////////////////////////////////////////////////////// +// // +// This file is shared currently between MaxComponent and MaxPlasmaMtl. // +// Since the latter links to virtually nothing, this header MUST remain // +// MAX-pure; no reference to components or any other Plasma-specific stuff // +// above basically nucleusLib. Once we merge materials back into the main // +// plugins, this restriction can go away. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plCAnimParamBlock_h +#define _plCAnimParamBlock_h + +#include "Max.h" +#include "iparamb2.h" +#include "iparamm2.h" + + +/// Note: the namespace is so our enums and such don't clash with anything +/// else +namespace plCAnimPB +{ + +//// ParamBlock IDs ////////////////////////////////////////////////////////// +// IDs for the common paramblock def below + +enum Rollouts +{ + kRollMain, + kRollEase +}; + +enum +{ + kAnimRadio_DEAD, + kAnimAutoStart, // Start the Animation on load (V2) + kAnimLoop, // Start Looping at Begin Location + kAnimBegin_DEAD, + kAnimEnd_DEAD, + kAnimLoopSegCkBx_DEAD, + kAnimLoopSegBeg_DEAD, + kAnimLoopSegEnd_DEAD, + kAnimName, // Name of the notetrack animation to play + kAnimLoopSegBegBox_DEAD, + kAnimLoopSegEndBox_DEAD, + kAnimUseGlobal, + kAnimGlobalName, + kAnimLoopName, // Name of the notetrack specified loop + kAnimEaseInType, + kAnimEaseOutType, + kAnimEaseInLength, + kAnimEaseOutLength, + kAnimEaseInMin, + kAnimEaseInMax, + kAnimEaseOutMin, + kAnimEaseOutMax, + kAnimPhysAnim, +}; + +//// Static ParamBlock Accessor ////////////////////////////////////////////// +// So we can ensure we only have one definition of our base paramBlock, but +// can access it multiple times without having to worry about which is the +// first. Defined here so the macro can get to it; don't reference it +// directly! + +class plPBBaseDec +{ + public: + static int kPlComponentBlkID; + static int kPlComponentRefID; + + static ParamBlockDesc2 fAnimBlock; +}; + +//// ParamBlock Macro //////////////////////////////////////////////////////// +// Use this macro to create your paramblock for your animation object. This +// way, all the paramblocks for animation info will be identical! +// +// Ex. Usage: kDefineAnimPB( gAnimBlock, &gAnimDesc, PASS_ANIM, PASS_ANIM_EASE, gAnimMainProc, gAnimEaseProc ); + +#define kDefineAnimPB( blockName, descPtr, mainResIDName, easeResIDName, mainProc, easeProc ) \ + static ParamBlockDesc2 blockName##( \ + \ + plPBBaseDec::kPlComponentBlkID, _T("animation"), 0, descPtr, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plPBBaseDec::kPlComponentRefID, \ + 2, /* # rollouts */ \ + plCAnimPB::kRollMain, IDD_##mainResIDName, IDS_##mainResIDName, 0, 0, mainProc, \ + plCAnimPB::kRollEase, IDD_##easeResIDName, IDS_##easeResIDName, 0, 0, easeProc, \ + &plPBBaseDec::fAnimBlock, /* ParamBlock to include */ \ + end, \ + ); + + + +}; // namespace + +#endif //_plCAnimParamBlock_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponent.cpp new file mode 100644 index 00000000..d42b4c40 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponent.cpp @@ -0,0 +1,162 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plCameraComponent.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plPhysical/plCollisionDetector.h" // MM +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plCameraMsg.h" + +#include "hsResMgr.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxConvert/plConvert.h" + +#include "../MaxMain/plPhysicalProps.h" +#include "../plHavok1/plHKPhysicsGroups.h" + +void DummyCodeIncludeFuncCamera() {} + +OBSOLETE_CLASS_DESC(plCameraCmdComponent, gCameraCmdDesc, "(ex)Camera Command Region", "CameraCmdRegion", COMP_TYPE_MISC, CAMERACMD_CID) + +enum +{ + kCommand, + kOffsetX, + kOffsetY, + kOffsetZ, + kCustomBoundListStuff, + kSmooth, +}; + +class plCameraCmdComponentProc; +extern plCameraCmdComponentProc gCameraCmdComponentProc; + +enum +{ + kCommandSetOffset, + kCommandSetFP, + kCommandSetFixedCam, +}; + +ParamBlockDesc2 gCameraCmdBlock +( + plComponent::kBlkComp, _T("cameraComp"), 0, &gCameraCmdDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_CAMERACMD, IDS_COMP_CAMERACMD, 0, 0, &gCameraCmdComponentProc, + + kCommand, _T("Command"), TYPE_INT, 0, 0, + p_default, kCommandSetFixedCam, + end, + + kOffsetX, _T("X Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX, IDC_CAMERACMD_SPIN_OFFSETX, SPIN_AUTOSCALE, + end, + + kOffsetY, _T("Y Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0f, 50.0f, + p_default, 10.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY, IDC_CAMERACMD_SPIN_OFFSETY, SPIN_AUTOSCALE, + end, + + kOffsetZ, _T("Z Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0f, 50.0f, + p_default, 3.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETZ, IDC_CAMERACMD_SPIN_OFFSETZ, SPIN_AUTOSCALE, + end, + + kCustomBoundListStuff, _T("FixedCamera"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CAMERACMD_PICKSTATE_BASE, + p_sclassID, CAMERA_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + end, + + kSmooth, _T("useCut"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CAMERACMD_CUT, + end, + + + + end +); + +plCameraCmdComponent::plCameraCmdComponent() +{ + fClassDesc = &gCameraCmdDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plCameraCmdComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plCameraCmdComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plCameraCmdComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +class plCameraCmdComponentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + return false; + } + + void DeleteThis() {} + +protected: + void IEnableControls(IParamMap2 *map, int type) + { + } + + void IAddComboItem(HWND hCombo, const char *name, int id) + { + } + void ISetComboSel(HWND hCombo, int type) + { + } +}; +static plCameraCmdComponentProc gCameraCmdComponentProc; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponent.h new file mode 100644 index 00000000..1ecf9e33 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponent.h @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plComponent.h" + +class plMaxNode; + +#define CAMERACMD_CID Class_ID(0x6edb72d1, 0xd8a1f43) + +class plCameraCmdComponent : public plComponent +{ +public: +protected: + +public: + plCameraCmdComponent(); + + // Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *node, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponents.cpp new file mode 100644 index 00000000..ed3d9778 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponents.cpp @@ -0,0 +1,2559 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" //Max Dependencies + +#include "resource.h" //Resource Dependencies +#include "hsResMgr.h" // Ibid + +#include "plComponent.h" //Component Dependencies +#include "plComponentReg.h" +#include "plCameraComponents.h" // Ibid +#include "plAnimComponent.h" // Ibid +#include "../pnSceneObject/plSceneObject.h" // Ibid +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plScene/plSceneNode.h" // Ibid +#include "../pnKeyedObject/plKey.h" // Ibid +#include "../MaxMain/plMaxNode.h" // Ibid +#include "../MaxMain/plMaxNodeData.h" // Ibid + +#include "../MaxConvert/plConvert.h" +#include "../MaxConvert/hsConverterUtils.h" //Conversion Dependencies +#include "../MaxConvert/hsControlConverter.h" // Ibid + +#include "../plPhysical/plSimDefs.h" + +#include "plgDispatch.h" //Message Dependencies +#include "../pnMessage/plObjRefMsg.h" // Ibid +#include "../pnMessage/plIntRefMsg.h" // Ibid +#include "../pnMessage/plNodeRefMsg.h" // Ibid +#include "../pnMessage/plCameraMsg.h" // Ibid +#include "../MaxMain/plPlasmaRefMsgs.h" // Ibid +#include "../pfAnimation/plLineFollowMod.h" +#include "../plPhysical/plCollisionDetector.h" // MM + +#include "../pfCamera/plCameraBrain.h" +#include "../pfCamera/plCameraModifier.h" +#include "plMiscComponents.h" +#include "../MaxMain/plPhysicalProps.h" +#include "plPhysicalComponents.h" + +// Line Follow related +#include "../plInterp/plAnimPath.h" +#include "../plInterp/plController.h" +#include "../pfAnimation/plLineFollowMod.h" +#include "../pfAnimation/plFollowMod.h" + +#include +#include + +// +// DummyCodeIncludeFuncPhys Function. +// Necessary to keep the compiler from throwing away this file. +// No functions within are inherently called otherwise.... +// +// + +void DummyCodeIncludeFuncCameras() +{ +} + + +// struct for storing transition information temporarily: +// used instead of camtrans because we don't have keys yet, +// but need to set up at pre-convert +struct PreTrans +{ + // used when creating default track transitions at runtime + PreTrans(plSceneObject* to) + { + fTransTo = to; + + fAccel = 60.0f; + fDecel = 60.0f; + fVelocity = 60.0f; + fPOADecel = 60.0f; + fPOAAccel = 60.0f; + fPOAVelocity = 60.0f; + + fCutPos = false; + fCutPOA = false; + fIgnore = false; + } + plSceneObject* fTransTo; + + hsBool fCutPos; + hsBool fCutPOA; + hsBool fIgnore; + hsScalar fAccel; + hsScalar fDecel; + hsScalar fVelocity; + hsScalar fPOAAccel; + hsScalar fPOADecel; + hsScalar fPOAVelocity; + + +}; + +/// +/// +/// +/// +/// +/// +/// limit pan component class +/// +/// +/// +/// +/// +/// + +//Max desc stuff necessary. +CLASS_DESC(plLimitPanComponent, gLimitPaneraDesc, "Allow Camera Panning", "Allow Camera Panning", COMP_TYPE_CAMERA, LIMITPAN_CID) + + +enum +{ + kLimitPanX, + kLimitPanZ, + kPanXDeg, + kPanZDeg, + kMouseSensitivity, // obsolete +}; + +//Max paramblock2 stuff below. +ParamBlockDesc2 gLimitPaneraBk +( + 1, _T("camera"), 0, &gLimitPaneraDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_CAMERAPAN, IDS_COMP_ALLOW_PAN, 0, 0, NULL, + + kLimitPanX, _T("Limit X"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_X_AXIS, + end, + + kLimitPanZ, _T("Limit Z"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_Z_AXIS, + end, + + kPanXDeg, _T("X degrees"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0f, 180.0f, + p_default, 90.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX, IDC_CAMERACMD_SPIN_OFFSETX, SPIN_AUTOSCALE, + end, + + kPanZDeg, _T("Z degrees"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0f, 180.0f, + p_default, 90.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY, IDC_CAMERACMD_SPIN_OFFSETY, SPIN_AUTOSCALE, + end, + + end +); + +plLimitPanComponent::plLimitPanComponent() +{ + fClassDesc = &gLimitPaneraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plLimitPanComponent::SetupProperties(plMaxNode* pNode, plErrorMsg *pErrMsg) +{ + return true; +} + + +hsBool plLimitPanComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plLimitPanComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + + +/// +/// +/// +/// +/// +/// +/// Camera Zoom component class +/// +/// +/// +/// +/// +/// + +//Max desc stuff necessary. +CLASS_DESC(plCameraZoomComponent, gCameraZoomeraDesc, "Allow Camera Zoom", "Allow Camera Zoom", COMP_TYPE_CAMERA, CAMERAZOOM_CID) + + +enum +{ + kZoomMaxDeg, + kZoomMinDeg, + kZoomRate, +}; + +//Max paramblock2 stuff below. +ParamBlockDesc2 gCameraZoomeraBk +( + 1, _T("camera"), 0, &gCameraZoomeraDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_CAMERAZOOM, IDS_COMP_CAMERAZOOM, 0, 0, NULL, + + kZoomMaxDeg, _T("max degrees"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0f, 180.0f, + p_default, 120.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX, IDC_CAMERACMD_SPIN_OFFSETX, SPIN_AUTOSCALE, + end, + + kZoomMinDeg, _T("min degrees"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0f, 180.0f, + p_default, 35.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY, IDC_CAMERACMD_SPIN_OFFSETY, SPIN_AUTOSCALE, + end, + + kZoomRate, _T("degrees per sec"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0f, 180.0f, + p_default, 90.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY2, IDC_CAMERACMD_SPIN_OFFSETY2, SPIN_AUTOSCALE, + end, + + end +); + +plCameraZoomComponent::plCameraZoomComponent() +{ + fClassDesc = &gCameraZoomeraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plCameraZoomComponent::SetupProperties(plMaxNode* pNode, plErrorMsg *pErrMsg) +{ + return true; +} + + +hsBool plCameraZoomComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plCameraZoomComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + + +// +// +// +// +// +// +// +// +// +// +// Override transition component +// +// +// +// + +//Max desc stuff necessary. +CLASS_DESC(plTransOverrideComponent, gTransOverrideeraDesc, "Override Transition", "Override Transition", COMP_TYPE_CAMERA, TRANSCAM_CID) + + +enum +{ + kTransitionTo, + kCutTrans, + kTrackTrans, + kTransSpeed, + kTransAccel, + kCutPOA, + kTrackPOA, + kTransPOASpeed, + kTransPOAAccel, + kTransDecel, + kTransPOADecel, + kTransVelocity, + kTrans, + kIgnoreTrans, + kTransPOAVelocity, +}; + +//Max paramblock2 stuff below. +ParamBlockDesc2 gTransOverrideeraBk +( + 1, _T("camera"), 0, &gTransOverrideeraDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_CAMERA_TRANS, IDS_COMP_CAMERATRANS, 0, 0, NULL, + + kTransitionTo, _T("transitionto"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CAMERARGN_PICKSTATE_BASE, + p_sclassID, CAMERA_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + end, + + kCutTrans, _T("cut"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK1, + end, + + kTrackTrans, _T("track"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK2, + p_enable_ctrls, 3, kTransVelocity, kTransAccel, kTransDecel, + end, + + kCutPOA, _T("cutPOA"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK3, + end, + + kTrackPOA, _T("trackPOA"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK4, + p_enable_ctrls, 3, kTransPOASpeed, kTransPOAAccel, kTransPOADecel, + end, + + kTransVelocity, _T("Velocity"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -200.0f, 200.0f, + p_default, 100.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_VEL, IDC_CAMERA_VEL_SPIN, SPIN_AUTOSCALE, + end, + + kTransAccel, _T("Accel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -200.0f, 200.0f, + p_default, 100.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_ACCEL, IDC_CAMERA_ACCEL_SPIN, SPIN_AUTOSCALE, + end, + + kTransDecel, _T("Decel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -200.0f, 200.0f, + p_default, 100.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_DECEL, IDC_CAMERA_DECEL_SPIN, SPIN_AUTOSCALE, + end, + + kTransPOAVelocity, _T("Velocity"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -200.0f, 200.0f, + p_default, 100.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_VEL2, IDC_CAMERA_VEL_SPIN2, SPIN_AUTOSCALE, + end, + + kTransPOAAccel, _T("Accel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -200.0f, 200.0f, + p_default, 100.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_ACCEL2, IDC_CAMERA_ACCEL_SPIN2, SPIN_AUTOSCALE, + end, + + kTransPOADecel, _T("Decel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -200.0f, 200.0f, + p_default, 100.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_DECEL2, IDC_CAMERA_DECEL_SPIN2, SPIN_AUTOSCALE, + end, + + kIgnoreTrans, _T("ignore"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK_IGNORE, + end, + end +); + +plTransOverrideComponent::plTransOverrideComponent() +{ + fClassDesc = &gTransOverrideeraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plTransOverrideComponent::SetupProperties(plMaxNode* pNode, plErrorMsg *pErrMsg) +{ + fTransKeys.clear(); + return true; +} + + +hsBool plTransOverrideComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + // see if there is a camera specified + INode* pCamNode = fCompPB->GetINode(kTransitionTo); + + PreTrans* pTrans = nil; + if (pCamNode) + pTrans = TRACKED_NEW PreTrans(((plMaxNode*)pCamNode)->GetSceneObject()); + else + pTrans = TRACKED_NEW PreTrans(nil); + + if (fCompPB->GetInt(kIgnoreTrans)) + { + pTrans->fIgnore = true; + } + else + if (fCompPB->GetInt(kTrackTrans)) + { + pTrans->fCutPos = false; + pTrans->fAccel = fCompPB->GetFloat(kTransAccel); + pTrans->fDecel = fCompPB->GetFloat(kTransDecel); + pTrans->fVelocity = fCompPB->GetFloat(kTransVelocity); + } + else + { + pTrans->fCutPos = true; + } + if (fCompPB->GetInt(kTrackPOA)) + { + pTrans->fCutPOA = false; + pTrans->fPOAAccel = fCompPB->GetFloat(kTransPOAAccel); + pTrans->fPOADecel = fCompPB->GetFloat(kTransPOADecel); + pTrans->fPOAVelocity = fCompPB->GetFloat(kTransPOAVelocity); + } + else + { + pTrans->fCutPOA = true; + } + fTransKeys[pNode] = pTrans; + + + return true; +} + +hsBool plTransOverrideComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plTransOverrideComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) +{ + TransitionKeys::iterator i = fTransKeys.begin(); + + for( ; i != fTransKeys.end(); i++ ) + { + delete (*i).second; + } + fTransKeys.clear(); + + return plComponent::DeInit( node, pErrMsg ); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// POA components +// +// +// +// +// +// +// +// +// +// +// +// +// avatar POA component... + +//Max desc stuff necessary. +CLASS_DESC(plPOAAvatarComponent, gPOAAvatareraDesc, "Avatar POA", "Avatar POA", COMP_TYPE_CAMERA, AVATAR_POA_CID) + +enum +{ + kAvPOAOffX, + kAvPOAOffY, + kAvPOAOffZ, + kAvPOAWorldspace, + +}; +//Max paramblock2 stuff below. +ParamBlockDesc2 gPOAAvatareraBk +( + 1, _T("camera"), 0, &gPOAAvatareraDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_AVATAR_POA, IDS_AVATARPOA, 0, 0, NULL, + + + kAvPOAOffX, _T("PX Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX5, IDC_CAMERACMD_SPIN_OFFSETX5, SPIN_AUTOSCALE, + end, + + kAvPOAOffY, _T("PY Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY4, IDC_CAMERACMD_SPIN_OFFSETY4, SPIN_AUTOSCALE, + end, + + kAvPOAOffZ, _T("PZ Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 3.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETZ2, IDC_CAMERACMD_SPIN_OFFSETZ2, SPIN_AUTOSCALE, + end, + + kAvPOAWorldspace, _T("POAworldspace"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_POA_WORLDSPACE, + end, + + + end +); + +plPOAAvatarComponent::plPOAAvatarComponent() +{ + fClassDesc = &gPOAAvatareraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// +// +// +// +// +// +// Object POA component +// +// + +//Max desc stuff necessary. +CLASS_DESC(plPOAObjectComponent, gPOAObjecteraDesc, "Object POA", "Object POA", COMP_TYPE_CAMERA, OBJECT_POA_CID) + + +enum +{ + kPOAObject, + kPOAObjOffsetX, + kPOAObjOffsetY, + kPOAObjOffsetZ, + kPOAObjWorldspace, +}; + +//Max paramblock2 stuff below. +ParamBlockDesc2 gPOAObjecteraBk +( + 1, _T("camera"), 0, &gPOAObjecteraDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_OBJECT_POA, IDS_COMP_OBJECTPOA, 0, 0, NULL, + + kPOAObject, _T("objectPOA"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CLICK_PROXY, + p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + end, + + kPOAObjOffsetX, _T("X Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX, IDC_CAMERACMD_SPIN_OFFSETX, SPIN_AUTOSCALE, + end, + + kPOAObjOffsetY, _T("Y Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY, IDC_CAMERACMD_SPIN_OFFSETY, SPIN_AUTOSCALE, + end, + + kPOAObjOffsetZ, _T("Z Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETZ, IDC_CAMERACMD_SPIN_OFFSETZ, SPIN_AUTOSCALE, + end, + + kPOAObjWorldspace, _T("POAworldspace"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_OBJECTPOA_WORLDSPACE, + end, + + end +); + +plPOAObjectComponent::plPOAObjectComponent() +{ + fClassDesc = &gPOAObjecteraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plPOAObjectComponent::SetupProperties(plMaxNode* pNode, plErrorMsg *pErrMsg) +{ + if (fCompPB->GetINode(kPOAObject)) + { + plMaxNode *pPOA = ((plMaxNode*)fCompPB->GetINode(kPOAObject)); + if (pPOA) + { + pPOA->SetForceLocal(true); + return true; + } + } + return false; +} + +hsBool plPOAObjectComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} + +plKey plPOAObjectComponent::GetObjectKey() +{ + if (fCompPB->GetINode(kPOAObject)) + return ((plMaxNode*)(fCompPB->GetINode(kPOAObject)))->GetSceneObject()->GetKey(); + return nil; +} + +// +// +// +// +// +// +// +// Make Default component... +// +// +// +// +// +// +// +// +// +// +//Max desc stuff necessary. +CLASS_DESC(plMakeDefaultCamComponent, gMakeDefaultCameraDesc, "Make Default Camera", "Make Default Camera", COMP_TYPE_CAMERA, DEFAULTCAM_CID) + + +enum +{ + kMakeDefaultCam, +}; + +//Max paramblock2 stuff below. +ParamBlockDesc2 gMakeDefaultCameraBk +( + 1, _T("camera"), 0, &gMakeDefaultCameraDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + + + end +); + +plMakeDefaultCamComponent::plMakeDefaultCamComponent() +{ + fClassDesc = &gMakeDefaultCameraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + +// +// +// +// +// +// +// +// +// +// +// +// base class for all camera components which make a plCameraModifier +// +// +// +// + + +hsBool plCameraBaseComponent::IsValidNodeType(plMaxNode *pNode) +{ + Object *obj = pNode->EvalWorldState(hsConverterUtils::Instance().GetTime(pNode->GetInterface())).obj; + TimeValue Now = hsConverterUtils::Instance().GetTime(pNode->GetInterface()); + if(obj->ConvertToType(Now, Class_ID(LOOKAT_CAM_CLASS_ID, 0))) + return true; + else + return false; +} + +hsBool plCameraBaseComponent::SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + fModKeys.clear(); + hsBool ValidNode = IsValidNodeType(pNode); + if(!ValidNode){ + if(pErrMsg->Set(true, "Invalid Camera Object", "The camera %s is not a 'Max Target Camera type'. This camera will be disabled..\nKill the export?",((INode*)pNode)->GetName()).Ask()) + pErrMsg->Set(true, "", ""); + else + pErrMsg->Set(false); // Don't want to abort + + return false; + } + plMaxNode* node = ((plMaxNode*)pNode->GetTarget()); + if (node) + { + node->SetDrawable(false); + node->SetMovable(true); + node->SetForceLocal(true); + } + + return true; +} + +hsBool plCameraBaseComponent::PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plCameraBaseComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + // check for overriden transitions and special animation commands + int count = node->NumAttachedComponents(); + for (UInt32 x = 0; x < count; x++) + { + plComponentBase *comp = node->GetAttachedComponent(x); + if (comp->ClassID() == TRANSCAM_CID) + { + plTransOverrideComponent* pTrans = (plTransOverrideComponent*)comp; + int idx = 0; + plTransOverrideComponent::TransitionKeys::const_iterator it; + for (it = pTrans->fTransKeys.begin(); it != pTrans->fTransKeys.end(); it++) + { + if (it->first != node) + continue; + PreTrans *trans = it->second; + // convert the pre transition to a camera transition (get the camera modifier key that we are going to) + plSceneObject* pObj = trans->fTransTo; + const plCameraModifier1* pCamMod = nil; + if (pObj) + { + for (int i = 0; i < pObj->GetNumModifiers(); i++) + { + pCamMod = plCameraModifier1::ConvertNoRef(pObj->GetModifier(i)); + if (pCamMod) + break; + } + } + CamTrans* camTrans = nil; + if (!pCamMod) + camTrans = TRACKED_NEW CamTrans(nil); + else + camTrans = TRACKED_NEW CamTrans(pCamMod->GetKey()); + + camTrans->fAccel = trans->fAccel; + camTrans->fDecel = trans->fDecel; + camTrans->fVelocity = trans->fVelocity; + camTrans->fPOAAccel = trans->fPOAAccel; + camTrans->fPOADecel = trans->fPOADecel; + camTrans->fPOAVelocity = trans->fPOAVelocity; + camTrans->fCutPOA = trans->fCutPOA; + camTrans->fCutPos = trans->fCutPos; + camTrans->fIgnore = trans->fIgnore; + + fModKeys[node]->AddTrans(camTrans); + idx++; + } + + } + } + return true; +} + +plCameraModifier1* plCameraBaseComponent::ICreateCameraModifier(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + + GenCamera* theCam = nil; + Object* obj = pNode->EvalWorldState(hsConverterUtils::Instance().GetTime(pNode->GetInterface())).obj; + TimeValue Now = hsConverterUtils::Instance().GetTime(pNode->GetInterface()); + + if (obj->ConvertToType(Now, Class_ID(LOOKAT_CAM_CLASS_ID, 0))) + theCam = (GenCamera *) obj->ConvertToType(Now, Class_ID(LOOKAT_CAM_CLASS_ID, 0)); + else + { + return nil; + } + + pNode->SetDrawable(false); + pNode->SetForceLocal(true); + pNode->SetItinerant(true); + + // create run-time objects + + plCameraModifier1* pMod = TRACKED_NEW plCameraModifier1; + + plKey modifierKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), pMod, pNode->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(modifierKey, TRACKED_NEW plObjRefMsg(pNode->GetSceneObject()->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + + obj = pNode->EvalWorldState(hsConverterUtils::Instance().GetTime(pNode->GetInterface())).obj; + + hsVector3 pt; + + float FOVvalue= 0.0; //Currently in Radians + // radians + FOVvalue = theCam->GetFOV(Now); + // convert + FOVvalue = FOVvalue*(180/3.141592); + int FOVType = theCam->GetFOVType(); + hsScalar wDeg, hDeg; + switch(FOVType) + { + case 0: // FOV_W + { + wDeg = FOVvalue; + hDeg = (wDeg*3)/4; + } + break; + case 1: // FOV_H + { + hDeg = FOVvalue; + wDeg = (hDeg*4)/3; + } + break; + } + pMod->SetFOVw(wDeg); + pMod->SetFOVh(hDeg); + + pNode->AddModifier(pMod, IGetUniqueName(pNode)); + return pMod; +} + + +void plCameraBaseComponent::ISetLimitPan(plMaxNode* pNode, plCameraBrain1* pBrain) +{ + plComponentBase* LimitPanComp = 0; + + for (UInt32 x = 0; x < pNode->NumAttachedComponents(); x++) + { + plComponentBase *comp = pNode->GetAttachedComponent(x); + if (comp->ClassID() == LIMITPAN_CID) + LimitPanComp = comp; + } + if (LimitPanComp) + { + // set this camera to limit panning x degrees + IParamBlock2* pBlk = LimitPanComp->GetParamBlock(plComponentBase::kRefComp); + if ( pBlk && pBlk->GetInt(kLimitPanX) ) + { + hsScalar deg = pBlk->GetFloat(kPanZDeg); + hsScalar rad = hsScalarDegToRad(deg); + pBrain->SetXPanLimit( rad * 0.5f ); + } + if ( pBlk && pBlk->GetInt(kLimitPanZ) ) + { + hsScalar deg = pBlk->GetFloat(kPanXDeg); + hsScalar rad = hsScalarDegToRad(deg); + pBrain->SetZPanLimit( rad * 0.5f ); + } + } +} + +void plCameraBaseComponent::ISetLimitZoom(plMaxNode* pNode, plCameraBrain1* pBrain) +{ + plComponentBase* LimitZoomComp = 0; + + for (UInt32 x = 0; x < pNode->NumAttachedComponents(); x++) + { + plComponentBase *comp = pNode->GetAttachedComponent(x); + if (comp->ClassID() == CAMERAZOOM_CID) + LimitZoomComp = comp; + } + if (LimitZoomComp) + { + // set this camera to limit panning x degrees + IParamBlock2* pBlk = LimitZoomComp->GetParamBlock(plComponentBase::kRefComp); + hsScalar max = pBlk->GetFloat(kZoomMaxDeg); + hsScalar min = pBlk->GetFloat(kZoomMinDeg); + hsScalar rate = pBlk->GetFloat(kZoomRate); + pBrain->SetZoomParams(max / 1.33333333, min / 1.33333333, rate); + } +} + +void plCameraBaseComponent::ISetIgnoreSubworld(plMaxNode* pNode, plCameraBrain1* pBrain) +{ + plComponentBase* subComp = 0; + + for (UInt32 x = 0; x < pNode->NumAttachedComponents(); x++) + { + plComponentBase *comp = pNode->GetAttachedComponent(x); + if (comp->ClassID() == CAM_IGNORE_SUB_CID) + subComp = comp; + } + if (subComp) + { + // set this camera to ignore subworld movement + pBrain->SetFlags(plCameraBrain1::kIgnoreSubworldMovement); + } +} + + + +plCameraModifier1* plCameraBaseComponent::ICreateFocalPointObject(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + plMaxNode* node = ((plMaxNode*)pNode->GetTarget()); + + plCameraModifier1* pPOAMod = TRACKED_NEW plCameraModifier1; + + plKey poaModifierKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), pPOAMod, node->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(poaModifierKey, TRACKED_NEW plObjRefMsg(node->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + + plCameraBrain1* pPOABrain = TRACKED_NEW plCameraBrain1(pPOAMod); + // Give the brain a key + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), pPOABrain, pNode->GetLocation()); + + plGenRefMsg* pGRMsg = TRACKED_NEW plGenRefMsg(pPOAMod->GetKey(), plRefMsg::kOnCreate, -1, 0); + pGRMsg->SetRef( (hsKeyedObject*)pPOABrain ); + plConvert::Instance().AddMessageToQueue(pGRMsg); + + return (pPOAMod); +} + + +hsBool plCameraBaseComponent::ISetPOA(plMaxNode* pNode, plCameraBrain1* pBrain, plErrorMsg* pErrMsg) +{ + // do we want a special POA for this brain + hsBool bResult = false; + hsBool bAvPOA = false; + plComponentBase* POAComp = 0; + hsBool bPOAObject = false; + plComponentBase* objPOAComp = 0; + + for (UInt32 x = 0; x < pNode->NumAttachedComponents(); x++) + { + plComponentBase *comp = pNode->GetAttachedComponent(x); + if (comp->ClassID() == OBJECT_POA_CID) + { + if (objPOAComp != 0 || POAComp != 0) + { + pErrMsg->Set(true, "Export Error - Cameras", + "Object %s : Cameras must have one and only one POA component!\n", + pNode->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + objPOAComp = comp; + bPOAObject = true; + } + if (comp->ClassID() == AVATAR_POA_CID) + { + if (objPOAComp != 0 || POAComp != 0) + { + pErrMsg->Set(true, "Export Error - Cameras", + "Object %s : Cameras must have one and only one POA component!\n", + pNode->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + bAvPOA = true; + POAComp = comp; + } + } + if (bPOAObject) + { + IParamBlock2* pBlk = objPOAComp->GetParamBlock(plComponentBase::kRefComp); + if (!pBlk) + return false; + + plKey fCamKey = ((plPOAObjectComponent*)objPOAComp)->GetObjectKey(); + if (fCamKey) + { + pBrain->SetSubject(plSceneObject::ConvertNoRef(fCamKey->GetObjectPtr())); + if (!plCameraBrain1_Avatar::ConvertNoRef(pBrain)) + { + pBrain->SetFlags(plCameraBrain1::kCutPOA); + pBrain->SetFlags(plCameraBrain1::kCutPos); + } + hsVector3 pt; + pt.fX = pBlk->GetFloat(kPOAObjOffsetX); + pt.fY = pBlk->GetFloat(kPOAObjOffsetY); + pt.fZ = pBlk->GetFloat(kPOAObjOffsetZ); + pBrain->SetPOAOffset(pt); + if (pBlk->GetInt(kPOAObjWorldspace)) + pBrain->SetFlags(plCameraBrain1::kWorldspacePOA); + bResult = true; + } + else + { + pErrMsg->Set(true, "Export Error - Cameras", + "Object POA component of camera %s has no object specified!\n", + pNode->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + } + if (bAvPOA) + { + pBrain->SetFlags(plCameraBrain1::kFollowLocalAvatar); + + IParamBlock2* pBlk = POAComp->GetParamBlock(plComponentBase::kRefComp); + if (!pBlk) + return false; + + hsVector3 pt; + pt.fX = pBlk->GetFloat(kAvPOAOffX); + pt.fY = pBlk->GetFloat(kAvPOAOffY); + pt.fZ = pBlk->GetFloat(kAvPOAOffZ); + if (pBlk->GetInt(kAvPOAWorldspace)) + pBrain->SetFlags(plCameraBrain1::kWorldspacePOA); + pBrain->SetPOAOffset(pt); + bResult = true; + } + return bResult; +} + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +// +// Camera1Component +// +// the fixed camera component for the new camera code.. +// +// +// +// +// +// +// +// +// +// + + + +//Max desc stuff necessary. +CLASS_DESC(plCamera1Component, gCamera1Desc, "FixedCamera", "Fixed Camera", COMP_TYPE_CAMERA, FIXEDCAM_CID) + +//Max paramblock2 stuff below. +ParamBlockDesc2 gCamera1Bk +( + 1, _T("camera"), 0, &gCamera1Desc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + + end +); + + +plCamera1Component::plCamera1Component() +{ + fClassDesc = &gCamera1Desc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plCamera1Component::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + plCameraModifier1* pMod = ICreateCameraModifier(pNode, pErrMsg); + if (!pMod) + return false; + + //this is a fixed camera using the built-in target + plCameraBrain1_Fixed* pBrain = TRACKED_NEW plCameraBrain1_Fixed(pMod); + // Give the brain a key + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), pBrain, pNode->GetLocation()); + + plGenRefMsg* pMsg = TRACKED_NEW plGenRefMsg(pMod->GetKey(), plRefMsg::kOnCreate, -1, 0); + pMsg->SetRef( (hsKeyedObject*)pBrain ); + plConvert::Instance().AddMessageToQueue(pMsg); + + pBrain->SetFlags(plCameraBrain1::kCutPOA); + pBrain->SetFlags(plCameraBrain1::kCutPos); + + if (!ISetPOA(pNode, pBrain, pErrMsg)) + pBrain->SetTargetPoint(ICreateFocalPointObject(pNode, pErrMsg)); + + ISetLimitPan(pNode, pBrain); + ISetLimitZoom(pNode, pBrain); + ISetIgnoreSubworld(pNode, pBrain); + + fModKeys[pNode] = pMod; + return true; +} + + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +// +// IgnoreSubworldComponent +// +// tells the camera to ignore all subworld matrix movement +// +// +// +// +// +// +// +// +// +// + + + +//Max desc stuff necessary. +CLASS_DESC(plCameraIgnoreSub, gCameraIgnoreSubDesc, "IgnoreSubworldMovement", "Ignore Subworld Movement", COMP_TYPE_CAMERA, CAM_IGNORE_SUB_CID) + +//Max paramblock2 stuff below. +ParamBlockDesc2 gCameraIgnoreSubBk +( + 1, _T("camera"), 0, &gCameraIgnoreSubDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + + end +); + + +plCameraIgnoreSub::plCameraIgnoreSub() +{ + fClassDesc = &gCameraIgnoreSubDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +// +// AutoCamComponent +// +// the 3rd person camera component for the new camera code.. +// +// +// +// +// +// +// +// +// +// + + +//Max desc stuff necessary. +CLASS_DESC(plAutoCamComponent, gAutoCameraDesc, "AutoCamera", "Auto Camera", COMP_TYPE_CAMERA, AUTOCAM_CID) + + +enum +{ + kAutoCamOffX, + kAutoCamOffY, + kAutoCamOffZ, + kAutoCamWorldspace, + kAutoCamLOS, + kAutoCamSpeed, + kAutoCamAccel, + kAutoPOAOffX, + kAutoPOAOffY, + kAutoPOAOffZ, + kAutoCamCut, + kAutoCamPOAWorldspace, + kAutoCamPosWorldspace, + kAutoCamDecel, + kAutoCamPOAAccel, + kAutoCamPOADecel, + kAutoCamPOASpeed, + kAutoCamCutPOA, + kAutoCamVelocity, + kAutoCamVerticalInFall, + kAutoCamFastRun, +}; +//Max paramblock2 stuff below. +ParamBlockDesc2 gAutoCameraBk +( + 1, _T("camera"), 0, &gAutoCameraDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_AUTOCAM, IDS_COMP_AUTOCAM, 0, 0, NULL, + + kAutoCamOffX, _T("X Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX, IDC_CAMERACMD_SPIN_OFFSETX, SPIN_AUTOSCALE, + end, + + kAutoCamOffY, _T("Y Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 10.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY, IDC_CAMERACMD_SPIN_OFFSETY, SPIN_AUTOSCALE, + end, + + kAutoCamOffZ, _T("Z Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 3.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETZ, IDC_CAMERACMD_SPIN_OFFSETZ, SPIN_AUTOSCALE, + end, + + kAutoPOAOffX, _T("PX Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX5, IDC_CAMERACMD_SPIN_OFFSETX5, SPIN_AUTOSCALE, + end, + + kAutoPOAOffY, _T("PY Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY4, IDC_CAMERACMD_SPIN_OFFSETY4, SPIN_AUTOSCALE, + end, + + kAutoPOAOffZ, _T("PZ Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 3.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETZ2, IDC_CAMERACMD_SPIN_OFFSETZ2, SPIN_AUTOSCALE, + end, + + kAutoCamLOS, _T("maintainLOS"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_LOS, + end, + + kAutoCamCut, _T("cutPos"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_LOS2, + end, + + kAutoCamCutPOA, _T("cutPOA"), TYPE_BOOL, 0, 0, + p_default, 1, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_LOS3, + end, + + kAutoCamPosWorldspace, _T("PosWorldspace"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_POS_WORLDSPACE, + end, + + kAutoCamPOAWorldspace, _T("POAworldspace"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_POA_WORLDSPACE, + end, + + kAutoCamVelocity, _T("Velocity"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_VEL, IDC_CAMERA_VEL_SPIN, SPIN_AUTOSCALE, + end, + + kAutoCamAccel, _T("Accel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_ACCEL, IDC_CAMERA_ACCEL_SPIN, SPIN_AUTOSCALE, + end, + + kAutoCamDecel, _T("Decel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_DECEL, IDC_CAMERA_DECEL_SPIN, SPIN_AUTOSCALE, + end, + + kAutoCamPOASpeed, _T("Velocity"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_VEL2, IDC_CAMERA_VEL_SPIN2, SPIN_AUTOSCALE, + end, + + kAutoCamPOAAccel, _T("Accel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_ACCEL2, IDC_CAMERA_ACCEL_SPIN2, SPIN_AUTOSCALE, + end, + + kAutoCamPOADecel, _T("Decel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_DECEL2, IDC_CAMERA_DECEL_SPIN2, SPIN_AUTOSCALE, + end, + + kAutoCamVerticalInFall, _T("vertical"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_VERTICAL, + end, + + kAutoCamFastRun, _T("runspeedup"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_RUN, + end, +end +); + +plAutoCamComponent::plAutoCamComponent() +{ + fClassDesc = &gAutoCameraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plAutoCamComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + + plCameraModifier1* pMod = ICreateCameraModifier(pNode, pErrMsg); + if (!pMod) + return false; + + plCameraBrain1_Avatar* pBrain = TRACKED_NEW plCameraBrain1_Avatar(pMod); + // Give the brain a key + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), pBrain, pNode->GetLocation()); + + plGenRefMsg* pMsg = TRACKED_NEW plGenRefMsg(pMod->GetKey(), plRefMsg::kOnCreate, -1, 0); + pMsg->SetRef( (hsKeyedObject*)pBrain ); + plConvert::Instance().AddMessageToQueue(pMsg); + hsVector3 pt; + pt.Set(fCompPB->GetFloat(kAutoCamOffX),fCompPB->GetFloat(kAutoCamOffY),fCompPB->GetFloat(kAutoCamOffZ)); + pBrain->SetOffset(pt); + pt.Set(fCompPB->GetFloat(kAutoPOAOffX),fCompPB->GetFloat(kAutoPOAOffY),fCompPB->GetFloat(kAutoPOAOffZ)); + pBrain->SetPOAOffset(pt); + if (fCompPB->GetInt(kAutoCamPosWorldspace)) + pBrain->SetFlags(plCameraBrain1::kWorldspacePos); + + if (fCompPB->GetInt(kAutoCamPOAWorldspace)) + pBrain->SetFlags(plCameraBrain1::kWorldspacePOA); + + if (fCompPB->GetInt(kAutoCamLOS)) + pBrain->SetFlags(plCameraBrain1::kMaintainLOS); + + if (fCompPB->GetInt(kAutoCamCut)) + pBrain->SetFlags(plCameraBrain1::kCutPos); + + if (fCompPB->GetInt(kAutoCamCutPOA)) + pBrain->SetFlags(plCameraBrain1::kCutPOA); + + if (fCompPB->GetInt(kAutoCamVerticalInFall)) + pBrain->SetFlags(plCameraBrain1::kVerticalWhenFalling); + + if (fCompPB->GetInt(kAutoCamFastRun)) + pBrain->SetFlags(plCameraBrain1::kSpeedUpWhenRunning); + + // We want a local Avatar POA for this brain + pBrain->SetFlags(plCameraBrain1::kFollowLocalAvatar); + + fModKeys[pNode] = pMod; + + // set brain parameters + + ISetLimitPan(pNode, pBrain); + ISetLimitZoom(pNode, pBrain); + ISetIgnoreSubworld(pNode, pBrain); + + pBrain->SetAccel(fCompPB->GetFloat(kAutoCamAccel)); + pBrain->SetDecel(fCompPB->GetFloat(kAutoCamDecel)); + pBrain->SetVelocity(fCompPB->GetFloat(kAutoCamVelocity)); + + pBrain->SetPOAAccel(fCompPB->GetFloat(kAutoCamPOAAccel)); + pBrain->SetPOADecel(fCompPB->GetFloat(kAutoCamPOADecel)); + pBrain->SetPOAVelocity(fCompPB->GetFloat(kAutoCamPOASpeed)); + + return true; +} + + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +// +// FPCamComponent +// +// the first-person camera component for the new camera code.. +// +// +// +// +// +// +// +// +// +// + + +//Max desc stuff necessary. +CLASS_DESC(plFPCamComponent, gFPCameraDesc, "First Person Camera", "First Person Camera", COMP_TYPE_CAMERA, FPCAM_CID) + + +enum +{ + kFPCamOffX, + kFPCamOffY, + kFPCamOffZ, + kFPCamPOAOffX, + kFPCamPOAOffY, + kFPCamPOAOffZ, + kFpCamPOAWorldspace, + kFpCamPosWorldspace, +}; +//Max paramblock2 stuff below. +ParamBlockDesc2 gFPCameraBk +( + 1, _T("camera"), 0, &gFPCameraDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FIRSTPERSON_CAM, IDS_COMP_FIRST_PERSONCAM, 0, 0, NULL, + + kFPCamOffX, _T("X Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX, IDC_CAMERACMD_SPIN_OFFSETX, SPIN_AUTOSCALE, + end, + + kFPCamOffY, _T("Y Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 1.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY, IDC_CAMERACMD_SPIN_OFFSETY, SPIN_AUTOSCALE, + end, + + kFPCamOffZ, _T("Z Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 6.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETZ, IDC_CAMERACMD_SPIN_OFFSETZ, SPIN_AUTOSCALE, + end, + + kFPCamPOAOffX, _T("PX Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX5, IDC_CAMERACMD_SPIN_OFFSETX5, SPIN_AUTOSCALE, + end, + + kFPCamPOAOffY, _T("PY Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY4, IDC_CAMERACMD_SPIN_OFFSETY4, SPIN_AUTOSCALE, + end, + + kFPCamPOAOffZ, _T("PZ Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 6.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETZ2, IDC_CAMERACMD_SPIN_OFFSETZ2, SPIN_AUTOSCALE, + end, + + kFpCamPOAWorldspace, _T("POAworldspace"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_POA_WORLDSPACE, + end, + + kFpCamPosWorldspace, _T("Posworldspace"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_POS_WORLDSPACE, + end, + end +); + +plFPCamComponent::plFPCamComponent() +{ + fClassDesc = &gFPCameraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + +hsBool plFPCamComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + + plCameraModifier1* pMod = ICreateCameraModifier(pNode, pErrMsg); + if (!pMod) + return false; + + plCameraBrain1_FirstPerson* pBrain = TRACKED_NEW plCameraBrain1_FirstPerson(pMod); + + // Give the brain a key + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), pBrain, pNode->GetLocation()); + plGenRefMsg* pMsg2 = TRACKED_NEW plGenRefMsg(pMod->GetKey(), plRefMsg::kOnCreate, -1, 0); + pMsg2->SetRef( (hsKeyedObject*)pBrain ); + plConvert::Instance().AddMessageToQueue(pMsg2); + + hsVector3 pt; + pt.Set(fCompPB->GetFloat(kFPCamOffX),fCompPB->GetFloat(kFPCamOffY),fCompPB->GetFloat(kFPCamOffZ)); + pBrain->SetOffset(pt); + pt.Set(fCompPB->GetFloat(kFPCamPOAOffX),fCompPB->GetFloat(kFPCamPOAOffY),fCompPB->GetFloat(kFPCamPOAOffZ)); + pBrain->SetPOAOffset(pt); + + // We want a local Avatar POA for this brain + pBrain->SetFlags(plCameraBrain1::kFollowLocalAvatar); + // and we don't want any lag at all. + pBrain->SetFlags(plCameraBrain1::kCutPos); + pBrain->SetFlags(plCameraBrain1::kCutPOA); + fModKeys[pNode] = pMod; + + if (fCompPB->GetInt(kFpCamPOAWorldspace)) + pBrain->SetFlags(plCameraBrain1::kWorldspacePOA); + + if (fCompPB->GetInt(kFpCamPosWorldspace)) + pBrain->SetFlags(plCameraBrain1::kWorldspacePos); + + ISetLimitPan(pNode, pBrain); + ISetLimitZoom(pNode, pBrain); + ISetIgnoreSubworld(pNode, pBrain); + + return true; +} + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +// +// RailCameraComponent +// +// the fixed camera component for the new camera code.. +// +// +// +// +// +// +// +// +// +// +enum +{ + kRailCamObj, + kRailFarthest, + kRailSpeed, + kRailAccel, + kRailDecel, + kRailPOASpeed, + kRailPOAAccel, + kRailPOADecel, + kRailVelocity, +}; + + +CLASS_DESC(plRailCameraComponent, gRailCameraDesc, "Rail Camera", "Rail Camera", COMP_TYPE_CAMERA, RAIL_CAM_CID) + + +ParamBlockDesc2 gRailCameraBk +( + plComponent::kBlkComp, _T("RailCamera"), 0, &gRailCameraDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_CAMERA_RAIL, IDS_COMP_RAILCAMERA, 0, 0, 0, + + kRailCamObj, _T("PathObjectChoice"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_LINE_CHOOSE_PATH, + end, + + kRailFarthest, _T("farthest"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK1, + end, + + kRailVelocity, _T("Velocity"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 5.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_VEL, IDC_CAMERA_VEL_SPIN, SPIN_AUTOSCALE, + end, + + kRailAccel, _T("Accel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 5.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_ACCEL, IDC_CAMERA_ACCEL_SPIN, SPIN_AUTOSCALE, + end, + + kRailDecel, _T("Decel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 5.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_DECEL, IDC_CAMERA_DECEL_SPIN, SPIN_AUTOSCALE, + end, + + kRailPOASpeed, _T("Velocity"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_VEL2, IDC_CAMERA_VEL_SPIN2, SPIN_AUTOSCALE, + end, + + kRailPOAAccel, _T("Accel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_ACCEL2, IDC_CAMERA_ACCEL_SPIN2, SPIN_AUTOSCALE, + end, + + kRailPOADecel, _T("Decel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_DECEL2, IDC_CAMERA_DECEL_SPIN2, SPIN_AUTOSCALE, + end, + + end + +); + +plRailCameraComponent::plRailCameraComponent() +: fValid(false), + fLineMod(nil) +{ + fClassDesc = &gRailCameraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plRailCameraComponent::IMakeLineMod(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + + plRailCameraMod* lineMod = TRACKED_NEW plRailCameraMod; + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), lineMod, pNode->GetLocation()); + + lineMod->SetFollowMode(plLineFollowMod::kFollowLocalAvatar); + + plMaxNode* pathNode = (plMaxNode*)fCompPB->GetINode(kRailCamObj); + if(!pathNode) + { + pErrMsg->Set(true, "Path Node Failure", "Path Node %s was set to be Ignored or empty. Path Component ignored.", ((INode*)pathNode)->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + //hsAssert(pathNode, "If valid is true, this must be set"); + + Control* maxTm = pathNode->GetTMController(); + + plCompoundController* tmc = plCompoundController::ConvertNoRef(hsControlConverter::Instance().MakeTransformController(maxTm, pathNode)); + + if( !(tmc && tmc->GetPosController() && !plCompoundController::ConvertNoRef(tmc->GetPosController())) ) + { + delete tmc; + + pErrMsg->Set(true, pNode->GetName(), "Rail Camera Path Node %s has no suitable animation. Rail Camera ignored", pathNode->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + + Matrix3 w2p(true); + Matrix3 l2w = pathNode->GetNodeTM(TimeValue(0)); + if( !pathNode->GetParentNode()->IsRootNode() ) + w2p = Inverse(pathNode->GetParentNode()->GetNodeTM(TimeValue(0))); + hsMatrix44 loc2Par = pathNode->Matrix3ToMatrix44(l2w * w2p); + + gemAffineParts ap; + decomp_affine(loc2Par.fMap, &ap); + hsAffineParts initParts; + AP_SET(initParts, ap); + + plAnimPath* animPath = TRACKED_NEW plAnimPath; + animPath->SetController(tmc); + animPath->InitParts(initParts); + animPath->SetFarthest(fCompPB->GetInt(kRailFarthest)); + + lineMod->SetPath(animPath); + + if( !pathNode->GetParentNode()->IsRootNode() ) + { + plMaxNode* parNode = (plMaxNode*)pathNode->GetParentNode(); + plSceneObject* parObj = parNode->GetSceneObject(); + if( parObj ) + { + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(lineMod->GetKey(), plRefMsg::kOnCreate, 0, plLineFollowMod::kRefParent); + hsgResMgr::ResMgr()->AddViaNotify(parObj->GetKey(), refMsg, plRefFlags::kPassiveRef); + } + } + + plLeafController *controller = plLeafController::ConvertNoRef(tmc->GetPosController()); + + hsPoint3 start, end; + controller->Interp(0, &start); + controller->Interp(controller->GetLength(), &end); + + animPath->SetWrap(start == end); + + lineMod->SetForceToLine(true); + + fLineMod = lineMod; + + return true; +} + +hsBool plRailCameraComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + if (!plCameraBaseComponent::SetupProperties(pNode, pErrMsg)) + return false; + + fValid = false; + fLineMod = nil; + + if( !fCompPB->GetINode(kRailCamObj) ) + { + return true; + } + plMaxNode* pathNode = (plMaxNode*)fCompPB->GetINode(kRailCamObj); + if( !pathNode ) + { + return true; + } + if( !pathNode->IsTMAnimated() ) + { + return true; + } + pathNode->SetCanConvert(false); + + fValid = true; + pNode->SetForceLocal(true); + pNode->SetMovable(true); + + return true; +} + +hsBool plRailCameraComponent::PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + plCameraModifier1* pMod = ICreateCameraModifier(pNode, pErrMsg); + if (!pMod) + return false; + + //this is a fixed camera using the built-in target + plCameraBrain1_Fixed* pBrain = TRACKED_NEW plCameraBrain1_Fixed(pMod); + // Give the brain a key + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), pBrain, pNode->GetLocation()); + + // make the rail cam have pretty slow acceleration + pBrain->SetAccel(5.0f); + pBrain->SetDecel(5.0f); + + plGenRefMsg* pMsg = TRACKED_NEW plGenRefMsg(pMod->GetKey(), plRefMsg::kOnCreate, -1, 0); + pMsg->SetRef( (hsKeyedObject*)pBrain ); + plConvert::Instance().AddMessageToQueue(pMsg); + + pBrain->SetVelocity(fCompPB->GetFloat(kRailVelocity)); + pBrain->SetAccel(fCompPB->GetFloat(kRailAccel)); + pBrain->SetDecel(fCompPB->GetFloat(kRailDecel)); + + // need to cap these in legacy datasets + if (pBrain->GetAccel() > 10.0f) + pBrain->SetAccel(10.0f); + + if (pBrain->GetDecel() > 10.0f) + pBrain->SetDecel(10.0f); + + if (pBrain->GetVelocity() > 10.0f) + pBrain->SetVelocity(10.0f); + + pBrain->SetPOAVelocity(fCompPB->GetFloat(kRailPOASpeed)); + pBrain->SetPOAAccel(fCompPB->GetFloat(kRailPOAAccel)); + pBrain->SetPOADecel(fCompPB->GetFloat(kRailPOADecel)); + + if (!ISetPOA(pNode, pBrain, pErrMsg)) + pBrain->SetTargetPoint(ICreateFocalPointObject(pNode, pErrMsg)); + + ISetLimitPan(pNode, pBrain); + ISetLimitZoom(pNode, pBrain); + ISetIgnoreSubworld(pNode, pBrain); + + fModKeys[pNode] = pMod; + + // rail camera part + plMaxNode* pathNode = (plMaxNode*)fCompPB->GetINode(kRailCamObj); + if( !pathNode ) + { + pErrMsg->Set(true, "Invald Rail Camera", "Rail Camera component on %s. has no path object selected. This component will not be exported.\n", ((INode*)pNode)->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + + if( !fLineMod ) + { + if( !IMakeLineMod(pNode, pErrMsg) ) + { + fValid = false; + return true; + } + } + pNode->AddModifier(fLineMod, IGetUniqueName(pNode)); + pBrain->SetRail(fLineMod); + + return true; +} + + + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +// +// Circle camera Component +// +// a circle camera component for the new camera code.. +// +// +// +// +// +// +// +// +// +// + + +enum +{ + kCircleFarthest, + kCircleSpeed, + kCirclePOASpeed, + kCirclePOAAccel, + kCirclePOADecel, + kCirclePOAVelocity, +}; + + +CLASS_DESC(plCircleCameraComponent, gCircleCameraDesc, "Circle Camera", "Circle Camera", COMP_TYPE_CAMERA, CIRCLE_CAM_CID) + + +ParamBlockDesc2 gCircleCameraBk +( + plComponent::kBlkComp, _T("CircleCamera"), 0, &gCircleCameraDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_CAMERA_CIRCLE, IDS_COMP_CIRCLECAMERA, 0, 0, 0, + + kCircleFarthest, _T("farthest"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK1, + end, + + kCircleSpeed, _T("Lag Scale"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.1f, 1.0f, + p_default, 0.1f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX3, IDC_CAMERACMD_SPIN_OFFSETX3, SPIN_AUTOSCALE, + end, + + kCirclePOAVelocity, _T("Velocity"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_VEL, IDC_CAMERA_VEL_SPIN, SPIN_AUTOSCALE, + end, + + kCirclePOAAccel, _T("Accel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_ACCEL, IDC_CAMERA_ACCEL_SPIN, SPIN_AUTOSCALE, + end, + + kCirclePOADecel, _T("Decel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_DECEL, IDC_CAMERA_DECEL_SPIN, SPIN_AUTOSCALE, + end, + + + end + +); + +plCircleCameraComponent::plCircleCameraComponent() +: fValid(false) +{ + fClassDesc = &gCircleCameraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plCircleCameraComponent::PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + plCameraModifier1* pMod = ICreateCameraModifier(pNode, pErrMsg); + if (!pMod) + return false; + + //this is a circle camera using the built-in target + plCameraBrain1_Circle* pBrain = TRACKED_NEW plCameraBrain1_Circle(pMod); + // Give the brain a key + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), pBrain, pNode->GetLocation()); + + // make the circle cam have pretty slow acceleration + pBrain->SetAccel(10.0f); + pBrain->SetDecel(10.0f); + pBrain->SetVelocity(15.0f); + // Stay away from / close to the local avatar + pBrain->SetFlags(plCameraBrain1::kFollowLocalAvatar); + + if (fCompPB->GetInt(kCircleFarthest)) + pBrain->SetFarCircleCam(true); + + plGenRefMsg* pMsg = TRACKED_NEW plGenRefMsg(pMod->GetKey(), plRefMsg::kOnCreate, -1, 0); + pMsg->SetRef( (hsKeyedObject*)pBrain ); + plConvert::Instance().AddMessageToQueue(pMsg); + + pBrain->SetPOAVelocity(fCompPB->GetFloat(kCirclePOAVelocity)); + pBrain->SetPOAAccel(fCompPB->GetFloat(kCirclePOAAccel)); + pBrain->SetPOADecel(fCompPB->GetFloat(kCirclePOADecel)); + + if (fCompPB->GetFloat(kCircleSpeed)) + pBrain->SetCircumferencePerSec(fCompPB->GetFloat(kCircleSpeed)); + + if (!ISetPOA(pNode, pBrain, pErrMsg)) + pBrain->SetTargetPoint(ICreateFocalPointObject(pNode, pErrMsg)); + else + pBrain->SetCircleFlags(plCameraBrain1_Circle::kCircleLocalAvatar); + + // set radius and center point + hsPoint3 point, point2; + TimeValue Now = hsConverterUtils::Instance().GetTime(pNode->GetInterface()); + Matrix3 ReturnMatrix = pNode->GetTarget()->GetNodeTM(Now); + Point3 ReturnVal = ReturnMatrix.GetRow(3); + + point.fX = ReturnVal.x; + point.fY = ReturnVal.y; + point.fZ = ReturnVal.z; + + ReturnMatrix = pNode->GetLocalToWorld(Now); + ReturnVal = ReturnMatrix.GetRow(3); + + point2.fX = ReturnVal.x; + point2.fY = ReturnVal.y; + point2.fZ = ReturnVal.z; + + hsVector3 vec(point - point2); + pBrain->SetRadius(vec.Magnitude()); + pBrain->SetCenter(&point); + + + ISetLimitPan(pNode, pBrain); + ISetLimitZoom(pNode, pBrain); + ISetIgnoreSubworld(pNode, pBrain); + + fModKeys[pNode] = pMod; + + return true; +} + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +// the detector for triggering camera changes +/////////////////////////////////////////////////////// + +class plCameraDetectorComponent : public plPhysicCoreComponent +{ +public: + + + + plCameraDetectorComponent(); + void DeleteThis() { delete this; } + + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + + + +CLASS_DESC(plCameraDetectorComponent, gCameraDetectorDesc, "Camera Region", "CameraRegion", COMP_TYPE_CAMERA, CAM_REGION_CID) + +enum +{ + kCameraTarget, +}; + +ParamBlockDesc2 gCameraRegionBlock +( + plComponent::kBlkComp, _T("cameraRegion"), 0, &gCameraDetectorDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_CAMERARGN, IDS_CAMERARGN, 0, 0, NULL, + + + kCameraTarget, _T("CameraTarget"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CAMERARGN_PICKSTATE_BASE, + p_sclassID, CAMERA_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + end, + + + + end +); + + +plCameraDetectorComponent::plCameraDetectorComponent() +{ + fClassDesc = &gCameraDetectorDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + +hsBool plCameraDetectorComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + pNode->SetForceLocal(true); + pNode->SetDrawable(false); + + plPhysicalProps *physProps = pNode->GetPhysicalProps(); + + physProps->SetPinned(true, pNode, pErrMsg); + // only if movable will it have mass (then it will keep track of movements in PhysX) + if ( pNode->IsMovable() || pNode->IsTMAnimatedRecur() ) + physProps->SetMass(1.0, pNode, pErrMsg); + physProps->SetFriction(0.0, pNode, pErrMsg); + physProps->SetRestitution(0.0, pNode, pErrMsg); + physProps->SetBoundsType(plSimDefs::kHullBounds, pNode, pErrMsg); + physProps->SetGroup(plSimDefs::kGroupDetector, pNode, pErrMsg); + physProps->SetReportGroup(1<SetAllowLOS(true, pNode, pErrMsg); + + return true; +} + + +hsBool plCameraDetectorComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + + +hsBool plCameraDetectorComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plSceneObject *obj = node->GetSceneObject(); + plLocation loc = node->GetLocation(); + + plCameraRegionDetector *detector = TRACKED_NEW plCameraRegionDetector; + + // Register the detector + plKey detectorKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), detector, loc); + hsgResMgr::ResMgr()->AddViaNotify(detectorKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetBCastFlag(plMessage::kBCastByType); + + // need to get the key for the camera here... + plMaxNode* pCamNode = (plMaxNode*)fCompPB->GetINode(kCameraTarget); + if (pCamNode) + { + if(pCamNode->CanConvert()) + { + pMsg->SetCmd(plCameraMsg::kRegionPushCamera); + pMsg->SetNewCam(((plMaxNode*)pCamNode)->GetSceneObject()->GetKey()); + + int count = ((plMaxNode*)pCamNode)->NumAttachedComponents(); + for (UInt32 x = 0; x < count; x++) + { + plComponentBase *comp = ((plMaxNode*)pCamNode)->GetAttachedComponent(x); + if (comp->ClassID() == DEFAULTCAM_CID) + { + pMsg->SetCmd(plCameraMsg::kSetAsPrimary); + break; + } + } + + } + else + { + pErrMsg->Set(true, "Improper Cam Region Selection", "Cam Choice %s was set to be Ignored. No Camera selected.", ((INode*)pCamNode)->GetName()); + pErrMsg->Set(false); + return false; + } + + } + else + { + pErrMsg->Set(true, "Camera Region", "Camera Region %s has no camera assigned to it.", ((INode*)node)->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + + + detector->AddMessage(pMsg); + + + return true; +} + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +// +// Object Follow cam Component +// +// the 3rd person camera component for objects other than the avatar +// +// +// +// +// +// +// +// +// +// + + +//Max desc stuff necessary. +CLASS_DESC(plFollowCamComponent, gFollowCameraDesc, "FollowCamera", "Object Follow Camera", COMP_TYPE_CAMERA, FOLLOWCAM_CID) + + +enum +{ + kFollowCamOffX, + kFollowCamOffY, + kFollowCamOffZ, + kFollowCamWorldspace, + kFollowCamLOS, + kFollowCamSpeed, + kFollowCamAccel, + kFollowPOAOffX, + kFollowPOAOffY, + kFollowPOAOffZ, + kFollowCamCut, + kFollowCamPOAWorldspace, + kFollowCamPosWorldspace, + kFollowCamDecel, + kFollowCamPOAAccel, + kFollowCamPOADecel, + kFollowCamPOASpeed, + kFollowCamCutPOA, + kFollowCamVelocity, +}; +//Max paramblock2 stuff below. +ParamBlockDesc2 gFollowCameraBk +( + 1, _T("camera"), 0, &gFollowCameraDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_OBJECT_FOLLOWCAM, IDS_COMP_OBJECT_FOLLOWCAM, 0, 0, NULL, + + kFollowCamOffX, _T("X Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX, IDC_CAMERACMD_SPIN_OFFSETX, SPIN_AUTOSCALE, + end, + + kFollowCamOffY, _T("Y Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 10.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY, IDC_CAMERACMD_SPIN_OFFSETY, SPIN_AUTOSCALE, + end, + + kFollowCamOffZ, _T("Z Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 3.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETZ, IDC_CAMERACMD_SPIN_OFFSETZ, SPIN_AUTOSCALE, + end, + + kFollowPOAOffX, _T("PX Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX5, IDC_CAMERACMD_SPIN_OFFSETX5, SPIN_AUTOSCALE, + end, + + kFollowPOAOffY, _T("PY Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY4, IDC_CAMERACMD_SPIN_OFFSETY4, SPIN_AUTOSCALE, + end, + + kFollowPOAOffZ, _T("PZ Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -50.0f, 50.0f, + p_default, 3.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETZ2, IDC_CAMERACMD_SPIN_OFFSETZ2, SPIN_AUTOSCALE, + end, + + kFollowCamLOS, _T("maintainLOS"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_LOS, + end, + + kFollowCamCut, _T("cutPos"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_LOS2, + end, + + kFollowCamCutPOA, _T("cutPOA"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_LOS3, + end, + + kFollowCamPosWorldspace, _T("PosWorldspace"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_POS_WORLDSPACE, + end, + + kFollowCamPOAWorldspace, _T("POAworldspace"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_AUTOCAM_POA_WORLDSPACE, + end, + + kFollowCamVelocity, _T("Velocity"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_VEL, IDC_CAMERA_VEL_SPIN, SPIN_AUTOSCALE, + end, + + kFollowCamAccel, _T("Accel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_ACCEL, IDC_CAMERA_ACCEL_SPIN, SPIN_AUTOSCALE, + end, + + kFollowCamDecel, _T("Decel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_DECEL, IDC_CAMERA_DECEL_SPIN, SPIN_AUTOSCALE, + end, + + kFollowCamPOASpeed, _T("Velocity"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_VEL2, IDC_CAMERA_VEL_SPIN2, SPIN_AUTOSCALE, + end, + + kFollowCamPOAAccel, _T("Accel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_ACCEL2, IDC_CAMERA_ACCEL_SPIN2, SPIN_AUTOSCALE, + end, + + kFollowCamPOADecel, _T("Decel"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, -100.0f, 100.0f, + p_default, 60.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERA_DECEL2, IDC_CAMERA_DECEL_SPIN2, SPIN_AUTOSCALE, + end, + + end +); + +plFollowCamComponent::plFollowCamComponent() +{ + fClassDesc = &gFollowCameraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plFollowCamComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + + plCameraModifier1* pMod = ICreateCameraModifier(pNode, pErrMsg); + if (!pMod) + return false; + + plCameraBrain1_Avatar* pBrain = TRACKED_NEW plCameraBrain1_Avatar(pMod); + // Give the brain a key + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), pBrain, pNode->GetLocation()); + + plGenRefMsg* pMsg = TRACKED_NEW plGenRefMsg(pMod->GetKey(), plRefMsg::kOnCreate, -1, 0); + pMsg->SetRef( (hsKeyedObject*)pBrain ); + plConvert::Instance().AddMessageToQueue(pMsg); + hsVector3 pt; + pt.Set(fCompPB->GetFloat(kFollowCamOffX),fCompPB->GetFloat(kFollowCamOffY),fCompPB->GetFloat(kFollowCamOffZ)); + pBrain->SetOffset(pt); + pt.Set(fCompPB->GetFloat(kFollowPOAOffX),fCompPB->GetFloat(kFollowPOAOffY),fCompPB->GetFloat(kFollowPOAOffZ)); + pBrain->SetPOAOffset(pt); + if (fCompPB->GetInt(kFollowCamPosWorldspace)) + pBrain->SetFlags(plCameraBrain1::kWorldspacePos); + + if (fCompPB->GetInt(kFollowCamPOAWorldspace)) + pBrain->SetFlags(plCameraBrain1::kWorldspacePOA); + + if (fCompPB->GetInt(kFollowCamLOS)) + pBrain->SetFlags(plCameraBrain1::kMaintainLOS); + + if (fCompPB->GetInt(kFollowCamCut)) + pBrain->SetFlags(plCameraBrain1::kCutPos); + + if (fCompPB->GetInt(kFollowCamCutPOA)) + pBrain->SetFlags(plCameraBrain1::kCutPOA); + + fModKeys[pNode] = pMod; + + if (!ISetPOA(pNode, pBrain, pErrMsg)) + { + if(pErrMsg->Set(true, "Invalid Object Follow Camera", "The camera %s does NOT have an Object POA to go with its Object Follow Cam Component. This camera will be disabled..\nKill the export?",((INode*)pNode)->GetName()).Ask()) + pErrMsg->Set(true, "", ""); + else + pErrMsg->Set(false); // Don't want to abort + } + + // set brain parameters + + ISetLimitPan(pNode, pBrain); + ISetLimitZoom(pNode, pBrain); + ISetIgnoreSubworld(pNode, pBrain); + + pBrain->SetAccel(fCompPB->GetFloat(kFollowCamAccel)); + pBrain->SetDecel(fCompPB->GetFloat(kFollowCamDecel)); + pBrain->SetVelocity(fCompPB->GetFloat(kFollowCamVelocity)); + + pBrain->SetPOAAccel(fCompPB->GetFloat(kFollowCamPOAAccel)); + pBrain->SetPOADecel(fCompPB->GetFloat(kFollowCamPOADecel)); + pBrain->SetPOAVelocity(fCompPB->GetFloat(kFollowCamPOASpeed)); + + return true; +} + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +// +// +// animated camera command component - +// does nothing if you haven't put an animation component on the camera this is on +// +// +// +// +// +// +// +// + + +CLASS_DESC(plCameraAnimCmdComponent, gAnimcamCmdDesc, "Animated Camera Commands", "AnimatedCameraCmd", COMP_TYPE_CAMERA, ANIMCAM_CMD_CID) + +enum +{ + kAnimateOnPush, + kStopOnPop, + kResetOnPop, + kIgnoreFOV +}; + +ParamBlockDesc2 gAnimcamCmdBlock +( + plComponent::kBlkComp, _T("animCamCmd"), 0, &gAnimcamCmdDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_ANIMCAM_COMMANDS, IDS_COMP_ANIM_CAM_CMD, 0, 0, NULL, + + kAnimateOnPush, _T("animOnPush"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_BEGINONPUSH, + end, + + kStopOnPop, _T("stopOnPop"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_STOPONPOP, + end, + + kResetOnPop, _T("resetOnPop"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_RESETONPOP, + end, + + kIgnoreFOV, _T("ignoreFOV"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_IGNOREFOV, + end, + end +); + + +plCameraAnimCmdComponent::plCameraAnimCmdComponent() +{ + fClassDesc = &gAnimcamCmdDesc; + fClassDesc->MakeAutoParamBlocks(this); + fIgnoreFOV = false; +} + +plCameraAnimCmdComponent::PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + fIgnoreFOV = fCompPB->GetInt(kIgnoreFOV); + return true; +} +plCameraAnimCmdComponent::Convert(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + plSceneObject* pObj = pNode->GetSceneObject(); + const plCameraModifier1* pCamMod = nil; + if (pObj) + { + for (int i = 0; i < pObj->GetNumModifiers(); i++) + { + pCamMod = plCameraModifier1::ConvertNoRef(pObj->GetModifier(i)); + if (pCamMod) + break; + } + if (!pCamMod) + return false; + // forgive me oh const-ness gods. it is only the exporter, after all... + const_cast(pCamMod)->SetAnimCommands( fCompPB->GetInt(kAnimateOnPush), fCompPB->GetInt(kStopOnPop), fCompPB->GetInt(kResetOnPop) ); + } + return true; +} + +// obsolete camera components: +#define CAMERACMD_CID Class_ID(0x6edb72d1, 0xd8a1f43) +class plCameraCmdComponent : public plComponent +{ +public: +protected: + +public: + plCameraCmdComponent(); + + // Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *node, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + +}; +OBSOLETE_CLASS_DESC(plCameraCmdComponent, gCameraCmdDesc, "(ex)Camera Command Region", "CameraCmdRegion", COMP_TYPE_MISC, CAMERACMD_CID) + +enum +{ + kCommand, + kOffsetX, + kOffsetY, + kOffsetZ, + kCustomBoundListStuff, + kSmooth, +}; + +class plCameraCmdComponentProc; +extern plCameraCmdComponentProc gCameraCmdComponentProc; + +enum +{ + kCommandSetOffset, + kCommandSetFP, + kCommandSetFixedCam, +}; + +ParamBlockDesc2 gCameraCmdBlock +( + plComponent::kBlkComp, _T("cameraComp"), 0, &gCameraCmdDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_CAMERACMD, IDS_COMP_CAMERACMD, 0, 0, &gCameraCmdComponentProc, + + kCommand, _T("Command"), TYPE_INT, 0, 0, + p_default, kCommandSetFixedCam, + end, + + kOffsetX, _T("X Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0f, 50.0f, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETX, IDC_CAMERACMD_SPIN_OFFSETX, SPIN_AUTOSCALE, + end, + + kOffsetY, _T("Y Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0f, 50.0f, + p_default, 10.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETY, IDC_CAMERACMD_SPIN_OFFSETY, SPIN_AUTOSCALE, + end, + + kOffsetZ, _T("Z Offset"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0f, 50.0f, + p_default, 3.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_CAMERACMD_OFFSETZ, IDC_CAMERACMD_SPIN_OFFSETZ, SPIN_AUTOSCALE, + end, + + kCustomBoundListStuff, _T("FixedCamera"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CAMERACMD_PICKSTATE_BASE, + p_sclassID, CAMERA_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + end, + + kSmooth, _T("useCut"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CAMERACMD_CUT, + end, + + + + end +); + +plCameraCmdComponent::plCameraCmdComponent() +{ + fClassDesc = &gCameraCmdDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plCameraCmdComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plCameraCmdComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plCameraCmdComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +class plCameraCmdComponentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + return false; + } + + void DeleteThis() {} + +protected: + void IEnableControls(IParamMap2 *map, int type) + { + } + + void IAddComboItem(HWND hCombo, const char *name, int id) + { + } + void ISetComboSel(HWND hCombo, int type) + { + } +}; +static plCameraCmdComponentProc gCameraCmdComponentProc; + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponents.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponents.h new file mode 100644 index 00000000..867b7af3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plCameraComponents.h @@ -0,0 +1,238 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_CAMERA_COMPONENTS_H +#define PL_CAMERA_COMPONENTS_H + +#define FIXEDCAM_CID Class_ID(0x60f30a2a, 0x32d110e8) +#define AUTOCAM_CID Class_ID(0x65961f03, 0x775b77e6) +#define AVATAR_POA_CID Class_ID(0x1fe2720, 0x43857066) +#define OBJECT_POA_CID Class_ID(0x5c77163c, 0x78711171) +#define DEFAULTCAM_CID Class_ID(0x75bb1256, 0x49bf34c6) +#define TRANSCAM_CID Class_ID(0x5951682f, 0x1c310006) +#define LIMITPAN_CID Class_ID(0x18360ad4, 0x7dc74837) +#define FPCAM_CID Class_ID(0x1efe4e3e, 0x5b7369e4) +#define CAMERAZOOM_CID Class_ID(0x57b061be, 0x7c4f38db) +#define FOLLOWCAM_CID Class_ID(0x227c19a9, 0x55d809d6) +#define ANIMCAM_CMD_CID Class_ID(0x435913bf, 0x116c19fc) +#define CAM_REGION_CID Class_ID(0x60e27303, 0x1cf148a8) +#define CAM_IGNORE_SUB_CID Class_ID(0x33ae4c0d, 0x2b9d6513) + +#include "plComponent.h" +#include + +struct PreTrans; +class plMaxNode; +class plErrorMsg; +class plCameraModifier1; +class plCameraBrain1; +class plRailCameraMod; + +class plLimitPanComponent : public plComponent +{ +public: + plLimitPanComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +class plCameraZoomComponent : public plComponent +{ +public: + plCameraZoomComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +class plTransOverrideComponent : public plComponent +{ +public: + plTransOverrideComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + typedef std::map TransitionKeys; + TransitionKeys fTransKeys; + + const TransitionKeys& GetTransKeys(); +}; + + +class plPOAAvatarComponent : public plComponent +{ +public: + plPOAAvatarComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) { return true; } + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + + +class plPOAObjectComponent : public plComponent +{ +public: + plPOAObjectComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + + plKey GetObjectKey(); +}; + +class plMakeDefaultCamComponent : public plComponent +{ +public: + plMakeDefaultCamComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) { return true; } + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + +class plCameraBaseComponent : public plComponent +{ +public: + plCameraBaseComponent(){;} + + virtual hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + plCameraModifier1* ICreateCameraModifier(plMaxNode* pNode, plErrorMsg* pErrMsg); + plCameraModifier1* ICreateFocalPointObject(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool IsValidNodeType(plMaxNode *pNode); + void ISetLimitPan(plMaxNode* pNode, plCameraBrain1* pBrain); + void ISetLimitZoom(plMaxNode* pNode, plCameraBrain1* pBrain); + void ISetIgnoreSubworld(plMaxNode* pNode, plCameraBrain1* pBrain); + hsBool ISetPOA(plMaxNode* pNode, plCameraBrain1* pBrain, plErrorMsg* pErrMsg); + + typedef std::map ModKeys; + ModKeys fModKeys; + const ModKeys& GetModKeys(); +}; + +class plCamera1Component : public plCameraBaseComponent +{ +public: + plCamera1Component(); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +class plCameraIgnoreSub : public plComponent +{ +public: + plCameraIgnoreSub(); + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) { return true; } + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + +}; + +class plAutoCamComponent : public plCameraBaseComponent +{ +public: + plAutoCamComponent(); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +class plFPCamComponent : public plCameraBaseComponent +{ +public: + plFPCamComponent(); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +class plRailCameraComponent : public plCameraBaseComponent +{ +private: + hsBool fValid; + + plRailCameraMod* fLineMod; + + hsBool IMakeLineMod(plMaxNode* pNode, plErrorMsg* pErrMsg); + +public: + plRailCameraComponent(); + + virtual hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg); +}; + + +class plCircleCameraComponent : public plCameraBaseComponent +{ +private: + hsBool fValid; + +public: + plCircleCameraComponent(); + + virtual hsBool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg); +}; + + +class plFollowCamComponent : public plCameraBaseComponent +{ +public: + plFollowCamComponent(); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +class plCameraAnimCmdComponent : public plComponent +{ + hsBool fIgnoreFOV; +public: + plCameraAnimCmdComponent(); + virtual hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg){ return true; } + virtual hsBool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg); + virtual hsBool Convert(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool IgnoreFOV() { return fIgnoreFOV; } +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickDragComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickDragComponent.cpp new file mode 100644 index 00000000..8e945dab --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickDragComponent.cpp @@ -0,0 +1,605 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plClickDragComponent.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "plAnimComponent.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plPhysical/plCollisionDetector.h" // MM +#include "../plModifier/plLogicModifier.h" +#include "../plModifier/plAxisAnimModifier.h" +#include "../../NucleusLib/pnModifier/plConditionalObject.h" +#include "../plPhysical/plPickingDetector.h" +#include "../pfConditional/plActivatorConditionalObject.h" +#include "../pfConditional/plFacingConditionalObject.h" +#include "../pfConditional/plObjectInBoxConditionalObject.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnMessage/plCursorChangeMsg.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../plMessage/plAnimCmdMsg.h" + +#include "hsResMgr.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxConvert/plConvert.h" + +#include "plResponderComponent.h" +#include "plgDispatch.h" + +#include "../MaxMain/plPhysicalProps.h" +#include "plNotetrackAnim.h" + +#include "../plPhysical/plSimDefs.h" + +void DummyCodeIncludeFuncClickDrag() {} + +CLASS_DESC(plClickDragComponent, gClickDragDesc, "Dragable", "Dragable", COMP_TYPE_DETECTOR, CLICK_DRAG_CID) + + +enum +{ + kClickDragDirectional, + kClickDragDegrees, + kClickDragUseProxy, + kClickDragProxy, + kClickDragUseRegion, + kClickDragProxyRegion, + kClickDragUseX, + kClickDragUseY, + kClickDragAnimX, + kClickDragAnimY, + kClickDragUseLoopX, + kClickDragUseLoopY, + kClickDragLoopAnimX, + kClickDragLoopAnimY, + kClickDragToggle, + kClickDragAllOrNothing, + kClickDragOneShot, + kClikDragBoundsType, + kClikDragEnabled, +}; + + +class plClickDragComponentProc; +extern plClickDragComponentProc gClickDragComponentProc; + +ParamBlockDesc2 gClickDragBlock +( + + plComponent::kBlkComp, _T("ClickDragComp"), 0, &gClickDragDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_DETECTOR_CLICK_DRAGGABLE, IDS_COMP_DETECTOR_CLICK_DRAGGABLE, 0, 0, &gClickDragComponentProc, + + kClickDragDirectional, _T("directional"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CLICK_DRAG_OMNI, + end, + + kClickDragDegrees, _T("degrees"), TYPE_INT, P_ANIMATABLE, 0, + p_range, 1, 180, + p_default, 180, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_COMP_CLICK_DRAG_DEG, IDC_COMP_CLICK_DRAG_DEGSPIN, SPIN_AUTOSCALE, + end, + + kClickDragUseProxy, _T("useProxy"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CLICK_DRAG_USEPROXYPHYS, + p_enable_ctrls, 1, kClickDragProxy, + end, + + kClickDragProxy, _T("proxyPrimitave"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CLICK_DRAG_PROXY, + p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + end, + + kClickDragProxyRegion, _T("proxyRegion"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CLICK_DRAG_PROXYREGION, + p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + end, + + + kClickDragUseX, _T("useXAnim"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CLICK_DRAG_USEX, + end, + + kClickDragAnimX, _T("XanimName"), TYPE_STRING, 0, 0, + end, + + kClickDragUseY, _T("useYAnim"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CLICK_DRAG_USEYANIM, + end, + + kClickDragAnimY, _T("YanimName"), TYPE_STRING, 0, 0, + end, + + kClickDragUseLoopX, _T("useLoopXAnim"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CLICK_DRAG_USE_LOOPX, + end, + + kClickDragUseLoopY, _T("useLoopYAnim"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CLICK_DRAG_USE_LOOPY, + end, + + kClickDragAllOrNothing, _T("allOrNot"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CLICK_DRAG_ALLORNOT, + end, + + kClickDragOneShot, _T("oneshot"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_ONESHOT, + end, + + kClikDragBoundsType, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + kClikDragEnabled, _T("enabled"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_ENABLE, + p_default, TRUE, + end, + + end +); + +plClickDragComponent::plClickDragComponent() +{ + fClassDesc = &gClickDragDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +const plClickDragComponent::LogicKeys& plClickDragComponent::GetAxisKeys() +{ + return fAxisKeys; +} + +plKey plClickDragComponent::GetAxisKey(plMaxNode* node) +{ + LogicKeys::const_iterator it = fAxisKeys.find(node); + if (it != fAxisKeys.end()) + return(it->second); + + return nil; +} + +void plClickDragComponent::CollectNonDrawables(INodeTab& nonDrawables) +{ + INode* boundsNode = fCompPB->GetINode(kClickDragProxy); + if(boundsNode && fCompPB->GetInt(kClickDragUseProxy)) + nonDrawables.Append(1, &boundsNode); + + boundsNode = fCompPB->GetINode(kClickDragProxyRegion); + if(boundsNode ) + nonDrawables.Append(1, &boundsNode); + +} + +// Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plClickDragComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + + plActivatorBaseComponent::SetupProperties(node, pErrMsg); + + // Phys Props for the Clickable itself. + plMaxNode *boundsNode = nil; + boundsNode = (plMaxNode*)fCompPB->GetINode(kClickDragProxy); + if(boundsNode && fCompPB->GetInt(kClickDragUseProxy)) + if(boundsNode->CanConvert()) + { + boundsNode->SetDrawable(false); + plPhysicalProps *physProps = boundsNode->GetPhysicalProps(); + // only if movable will it have mass (then it will keep track of movements in PhysX) + if ( boundsNode->IsMovable() || boundsNode->IsTMAnimatedRecur() ) + physProps->SetMass(1.0, boundsNode, pErrMsg); + physProps->SetGroup( plSimDefs::kGroupStatic, boundsNode, pErrMsg); +// physProps->SetReportGroup( plPhysicsGroups::kLocalAvatars, boundsNode, pErrMsg); + physProps->SetBoundsType(fCompPB->GetInt(kClikDragBoundsType), boundsNode, pErrMsg); + } + else + { + pErrMsg->Set(true, "Clickable Sensor Warning", "The Clickable %s has a Proxy Surface %s that was Ignored.\nThe Sensors geometry will be used instead.", node->GetName(), boundsNode->GetName()).Show(); + pErrMsg->Set(false); + } + else + { + + plPhysicalProps *physProps = node->GetPhysicalProps(); + // only if movable will it have mass (then it will keep track of movements in PhysX) + if ( node->IsMovable() || node->IsTMAnimatedRecur() ) + physProps->SetMass(1.0, node, pErrMsg); + physProps->SetGroup( plSimDefs::kGroupStatic, node, pErrMsg); +// physProps->SetReportGroup( plPhysicsGroups::kLocalAvatars, node, pErrMsg); + //node->GetPhysicalProps()->SetAllCollideGroups(0); + physProps->SetBoundsType(fCompPB->GetInt(kClikDragBoundsType), node, pErrMsg); + } + // Phys Properties for the auto-generated Detector Region... + boundsNode = nil; + boundsNode = (plMaxNode*)fCompPB->GetINode(kClickDragProxyRegion); + if(boundsNode) + { + if(boundsNode->CanConvert()) + { + plPhysicalProps *physPropsDetector = boundsNode->GetPhysicalProps(); +// physPropsDetector->SetAllowLOS(true, boundsNode, pErrMsg); + physPropsDetector->SetProxyNode(boundsNode, node, pErrMsg); + physPropsDetector->SetBoundsType(plSimDefs::kHullBounds, boundsNode, pErrMsg); + // only if movable will it have mass (then it will keep track of movements in PhysX) + if ( boundsNode->IsMovable() || boundsNode->IsTMAnimated() ) + physPropsDetector->SetMass(1.0, boundsNode, pErrMsg); + + physPropsDetector->SetGroup( plSimDefs::kGroupDetector, boundsNode, pErrMsg ); + //boundsNode->GetPhysicalProps()->SetAllCollideGroups(0); + physPropsDetector->SetReportGroup( 1<Set(true, "Clickable Sensor Error", "The Clickable Sensor %s has a Required Region that is missing.\nThe Export will be aborted", node->GetName()).Show(); +// pErrMsg->Set(false); + return false; + } + + fAxisKeys.clear(); + return true; +} + +hsBool plClickDragComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plActivatorBaseComponent::PreConvert(node, pErrMsg); + plLogicModifier *logic = plLogicModifier::ConvertNoRef(fLogicModKeys[node]->GetObjectPtr()); + + if (fCompPB->GetInt(kClickDragOneShot)) + logic->SetFlag(plLogicModBase::kOneShot); + + plLocation loc = node->GetLocation(); + plSceneObject *obj = node->GetSceneObject(); + + // do the same thing for axis animation controllers. + plAxisAnimModifier* pAxis = TRACKED_NEW plAxisAnimModifier; + plKey axisKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), pAxis, loc); + hsgResMgr::ResMgr()->AddViaNotify(axisKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + logic->AddNotifyReceiver(axisKey); + + fAxisKeys[node] = axisKey; + + return true; +} + +hsBool plClickDragComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plLocation loc = node->GetLocation(); + plSceneObject *obj = node->GetSceneObject(); + + plKey logicKey = fLogicModKeys[node]; + plLogicModifier *logic = plLogicModifier::ConvertNoRef(logicKey->GetObjectPtr()); + logic->fMyCursor = plCursorChangeMsg::kCursorOpen; + + // Create the detector + plDetectorModifier *detector = nil; + detector = TRACKED_NEW plPickingDetector; + + // Register the detector + plKey detectorKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), detector, loc); + hsgResMgr::ResMgr()->AddViaNotify(detectorKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + // set up the axis anim controller + + plKey axisKey = fAxisKeys[node]; + plAxisAnimModifier* pAxis = plAxisAnimModifier::ConvertNoRef(axisKey->GetObjectPtr()); + // attach the animation controller to the animation objects: + // find an animation controller: + + hsTArray receivers; + IGetReceivers(node, receivers); + + int i; + for (i = 0; i < receivers.Count(); i++) + pAxis->GetNotify()->AddReceiver(receivers[i]); + + pAxis->SetNotificationKey(logicKey); + UInt32 count = node->NumAttachedComponents(); + hsBool bHasAnim = false; + plAnimComponentBase* pAnim = nil; + + for (i = 0; i < count; i++) + { + plComponentBase *comp = node->GetAttachedComponent(i); + if (comp->ClassID() == ANIM_COMP_CID || comp->ClassID() == ANIM_GROUP_COMP_CID) + { + pAnim = (plAnimComponentBase*)comp; + break; + } + } + if (!pAnim) + { + pErrMsg->Set(true, "WARNING", "Object %s has click-drag component attached but NO animation component!", ((INode*)node)->GetName()).Show(); + pErrMsg->Set(false); + } + else + { + if (fCompPB->GetInt(kClickDragUseX)) + { + pAxis->SetXAnim( pAnim->GetModKey(node) ); + } + else // take out this else when we support multiple channels + if (fCompPB->GetInt(kClickDragUseY)) + { + pAxis->SetYAnim( pAnim->GetModKey(node) ); + } + pAxis->SetAllOrNothing(fCompPB->GetInt(kClickDragAllOrNothing)); + + // add callbacks for beginning and end of animation + plEventCallbackMsg* pCall1 = TRACKED_NEW plEventCallbackMsg; + pCall1->fEvent = kBegin; + pCall1->fRepeats = -1; + pCall1->AddReceiver(axisKey); + + plEventCallbackMsg* pCall2 = TRACKED_NEW plEventCallbackMsg; + pCall2->fEvent = kEnd; + pCall2->fRepeats = -1; + pCall2->AddReceiver(axisKey); + + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + const char *tempAnimName = pAnim->GetAnimName(); + if (tempAnimName == nil) + { + //pMsg->SetAnimName(ENTIRE_ANIMATION_NAME); + pMsg->SetAnimName(pAnim->GetModKey(node)->GetName()); + pAxis->SetAnimLabel(ENTIRE_ANIMATION_NAME); + } + else + { + //pMsg->SetAnimName(tempAnimName); + pMsg->SetAnimName(pAnim->GetModKey(node)->GetName()); + pAxis->SetAnimLabel(tempAnimName); + } + + + pMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); + pMsg->AddCallback(pCall1); + pMsg->AddCallback(pCall2); + + hsRefCnt_SafeUnRef( pCall1 ); + hsRefCnt_SafeUnRef( pCall2 ); + + pMsg->AddReceiver( pAnim->GetModKey(node) ); + plgDispatch::MsgSend(pMsg); + } + + + // is this a using a proxy primitive? + plPickingDetector* det2 = nil; + plKey det2Key = nil; + plMaxNode* pProxyNode = (plMaxNode*)fCompPB->GetINode(kClickDragProxy); + + if (pProxyNode && fCompPB->GetInt(kClickDragUseProxy)) + { + + // verify that there is a physical proxy attached to this scene object: + UInt32 count = ((plMaxNodeBase*)pProxyNode)->NumAttachedComponents(); + hsBool bHasPhys = false; +// for (UInt32 i = 0; i < count; i++) + // { + // plComponentBase *comp = ((plMaxNodeBase*)pProxyNode)->GetAttachedComponent(i); + // if (comp->ClassID() == Class_ID(0x11e81ee4, 0x36b81450)) + // { + // bHasPhys = true; + // break; + // } + // } + // if (!bHasPhys) + // { + // pErrMsg->Set(true, "WARNING", "Object %s listed as draggable component proxy physical for %s but has NO physical component.\n Please attach a proxyTerrain componet!\n Export will continue but this gadget will not function",pProxyNode->GetName(), ((INode*)node)->GetName()).Show(); + // pErrMsg->Set(false); + // } + + + if(pProxyNode->CanConvert()) + { + det2 = TRACKED_NEW plPickingDetector; + // Register the detector + det2Key = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), det2, loc); + hsgResMgr::ResMgr()->AddViaNotify(det2Key, TRACKED_NEW plObjRefMsg(((plMaxNode*)pProxyNode)->GetSceneObject()->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + hsgResMgr::ResMgr()->AddViaNotify(logicKey, TRACKED_NEW plObjRefMsg( det2Key, plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + det2->SetProxyKey(node->GetSceneObject()->GetKey()); + } + else + { + pErrMsg->Set(true, "Unknown Error", "Invalid proxy physical detector set for draggable %s.", ((INode*)pProxyNode)->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + + } + + + + // create and register the CONDITIONS for the DETECTOR's Logic Modifier + plActivatorConditionalObject* activatorCond = TRACKED_NEW plActivatorConditionalObject; + plKey activatorKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), activatorCond, loc); + + // do we have a required region? + plMaxNode* pProxyRegNode = (plMaxNode*)fCompPB->GetINode(kClickDragProxyRegion); + if (pProxyRegNode) + { + // verify that there is a physical detector attached to this scene object: + UInt32 count = ((plMaxNodeBase*)pProxyRegNode)->NumAttachedComponents(); + hsBool bHasPhys = false; +// for (UInt32 i = 0; i < count; i++) + // { + // plComponentBase *comp = ((plMaxNodeBase*)pProxyRegNode)->GetAttachedComponent(i); + // if (comp->ClassID() == Class_ID(0x33b60376, 0x7e5163e0)) + // { + // bHasPhys = true; + // break; + // } + // } + // if (!bHasPhys) + // { + // pErrMsg->Set(true, "WARNING", "Object %s listed as draggable component detector region for %s but has NO physical detector component!\n Please attach a detector componet.\n Export will continue but this gadget will not function",((INode*)pProxyRegNode)->GetName(), ((INode*)node)->GetName()).Show(); + // pErrMsg->Set(false); + // } + + + if(pProxyRegNode->CanConvert()) + { + // need a player in box condition here... + // first a detector-any for the box + plObjectInVolumeDetector* pCDet = TRACKED_NEW plObjectInVolumeDetector(plCollisionDetector::kTypeAny); + plKey cDetKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), pCDet, loc); + hsgResMgr::ResMgr()->AddViaNotify(cDetKey, TRACKED_NEW plObjRefMsg(((plMaxNode*)pProxyRegNode)->GetSceneObject()->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + pCDet->AddLogicObj(logicKey); + // then an object-in-box condition for the logic mod + plObjectInBoxConditionalObject* boxCond = TRACKED_NEW plObjectInBoxConditionalObject; + plKey boxCondKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), boxCond, loc); + logic->AddCondition(boxCond); + } + else + { + pErrMsg->Set(true, "Problem with region", "Can't convert region component on %s. This component will not be exported.\n", ((INode*)pProxyRegNode)->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + } + else + { + pErrMsg->Set(true, "Must specify trigger region", "No required trigger region specified for click-drag component on %s. This component will not be exported.\n", ((INode*)node)->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + + + // How do we feel about player facing + plFacingConditionalObject* facingCond = TRACKED_NEW plFacingConditionalObject; + facingCond->SetDirectional(fCompPB->GetInt(kClickDragDirectional)); + int deg = fCompPB->GetInt(kClickDragDegrees); + if (deg > 180) + deg = 180; + hsScalar rad = hsScalarDegToRad(deg); + facingCond->SetTolerance(hsCosine(rad)); + plKey facingKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), facingCond, loc); + + + // link everything up: + if (det2) // set up the remote detector (if any) + { + activatorCond->SetActivatorKey(det2Key); + det2->AddLogicObj(logicKey); + } + else + { + detector->AddLogicObj(logicKey); // send messages to this logic component + activatorCond->SetActivatorKey(detectorKey); // Tells the activator condition to look for stimulus from the detector + } + logic->AddCondition(activatorCond); // add this activator condition + logic->AddCondition(facingCond); + logic->SetDisabled(fCompPB->GetInt(kClikDragEnabled) == 0); + + + // If this is for the SceneViewer, set the local only flag since the read function will never be called + if (plConvert::Instance().IsForSceneViewer()) + logic->SetLocalOnly(true); + + return true; +} + +hsBool plClickDragComponent::DeInit( plMaxNode *node, plErrorMsg *pErrMsg ) +{ + fAxisKeys.clear(); + return plActivatorBaseComponent::DeInit( node, pErrMsg ); +} + +#include "plNoteTrackDlgComp.h" + +class plClickDragComponentProc : public ParamMap2UserDlgProc +{ +protected: + plComponentNoteTrackDlg fNoteTrackDlgX; + plComponentNoteTrackDlg fNoteTrackDlgY; + IParamBlock2 *fPB; + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + fPB = map->GetParamBlock(); + + fNoteTrackDlgX.Init(GetDlgItem(hWnd, IDC_COMP_CLICK_DRAG_ANIMX), + nil, + kClickDragAnimX, + -1, + fPB, + fPB->GetOwner()); + + fNoteTrackDlgX.Load(); + + EnableWindow(GetDlgItem(hWnd, IDC_COMP_CLICK_DRAG_ANIMX), true); + + fNoteTrackDlgY.Init(GetDlgItem(hWnd, IDC_COMP_CLICK_DRAG_ANIM_Y), + nil, + kClickDragAnimY, + -1, + fPB, + fPB->GetOwner()); + + fNoteTrackDlgY.Load(); + + EnableWindow(GetDlgItem(hWnd, IDC_COMP_CLICK_DRAG_ANIM_Y), true); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_COMP_CLICK_DRAG_ANIMX) + { + fNoteTrackDlgX.AnimChanged(); + return TRUE; + } + if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_COMP_CLICK_DRAG_ANIM_Y) + { + fNoteTrackDlgY.AnimChanged(); + return TRUE; + } + break; + } + return false; + } + + void DeleteThis() + { + fNoteTrackDlgX.DeleteCache(); + fNoteTrackDlgY.DeleteCache(); + } +}; +static plClickDragComponentProc gClickDragComponentProc; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickDragComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickDragComponent.h new file mode 100644 index 00000000..2978a1ad --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickDragComponent.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plClickDragComponent_inc +#define plClickDragComponent_inc + +#include "plClickableComponent.h" + +#define CLICK_DRAG_CID Class_ID(0x61880560, 0x348d72d3) + +class plClickDragComponent : public plClickableComponent +{ +protected: + LogicKeys fAxisKeys; + +public: + plClickDragComponent(); + + // Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *node, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool DeInit(plMaxNode *node, plErrorMsg* pErrMsg); + + virtual plKey GetAxisKey(plMaxNode* node); + const LogicKeys& GetAxisKeys(); + + virtual void CollectNonDrawables(INodeTab& nonDrawables); + +}; + +#endif // plClickDragComponent_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickableComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickableComponent.cpp new file mode 100644 index 00000000..428b64e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickableComponent.cpp @@ -0,0 +1,409 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plClickableComponent.h" +#include "resource.h" +#include "plComponentReg.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plPhysical/plCollisionDetector.h" // MM +#include "../plModifier/plLogicModifier.h" +#include "../../NucleusLib/pnModifier/plConditionalObject.h" +#include "../plPhysical/plPickingDetector.h" +#include "../pfConditional/plActivatorConditionalObject.h" +#include "../pfConditional/plFacingConditionalObject.h" +#include "../pfConditional/plObjectInBoxConditionalObject.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnMessage/plCursorChangeMsg.h" + +#include "hsResMgr.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxConvert/plConvert.h" +#include "../MaxMain/plPhysicalProps.h" +#include "../plPhysical/plSimDefs.h" + +#include "plResponderComponent.h" + +#include "../MaxMain/plPhysicalProps.h" + + +void DummyCodeIncludeFuncClickable() {} + +CLASS_DESC(plClickableComponent, gClickableDesc, "Clickable", "Clickable", COMP_TYPE_DETECTOR, CLICKABLE_CID) + +enum +{ + kClickableDirectional, + kClickableDegrees, + kClickableUseProxy, + kClickableProxy, + kClickableUseRegion, + kClickableProxyRegion, + kClickableToggle_DEAD, + kClickableOneShot, + kClickableBoundsType, + kClickableEnabled, + kClickablePhysical, + kClickableIgnoreProxyRegion, + kClickableFriction, +}; + +ParamBlockDesc2 gClickableBlock +( + plComponent::kBlkComp, _T("clickable"), 0, &gClickableDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_DETECTOR_CLICKABLE, IDS_COMP_DETECTOR_CLICKABLE, 0, 0, NULL, + + kClickableDirectional, _T("directional"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CLICK_OMNI, + end, + + kClickableDegrees, _T("degrees"), TYPE_INT, P_ANIMATABLE, 0, + p_range, 1, 180, + p_default, 180, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_COMP_CLICK_DEG, IDC_COMP_CLICK_DEGSPIN, SPIN_AUTOSCALE, + end, + + kClickableUseProxy, _T("useProxy"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CLICK_USEPROXY, + p_enable_ctrls, 1, kClickableProxy, + end, + + + kClickableProxy, _T("proxyPrimitave"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CLICK_PROXY, +// p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + end, + + kClickableProxyRegion, _T("proxyRegion"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CLICK_PROXYREGION, +// p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + end, + + kClickableOneShot, _T("oneshot"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_ONESHOT, + end, + + kClickableBoundsType, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + kClickableEnabled, _T("enabled"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_ENABLED, + p_default, TRUE, + end, + + kClickablePhysical, _T("physical"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COLLIDABLE_CHECK, + p_default, TRUE, + end, + + kClickableIgnoreProxyRegion, _T("ignoreProxyRegion"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_IGNORE_REGION_CHECK, + p_default, FALSE, + end, + + kClickableFriction, _T("friction"), TYPE_FLOAT, 0, 0, + p_range, 0.0f, FLT_MAX, + p_default, 0.0f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_CLICKABLE_FRIC_EDIT1, IDC_COMP_CLICKABLE_FRIC_SPIN1, SPIN_AUTOSCALE, + end, + end +); + +plClickableComponent::plClickableComponent() +{ + fClassDesc = &gClickableDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +void plClickableComponent::CollectNonDrawables(INodeTab& nonDrawables) +{ + if( fCompPB->GetInt(kClickableUseProxy) ) + { + INode* clickNode = fCompPB->GetINode(kClickableProxy); + if( clickNode ) + nonDrawables.Append(1, &clickNode); + } + INode* detectNode = fCompPB->GetINode(kClickableProxyRegion); + if( detectNode ) + nonDrawables.Append(1, &detectNode); + +} + +hsBool plClickableComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plActivatorBaseComponent::SetupProperties(node, pErrMsg); + + bool physical = (fCompPB->GetInt(kClickablePhysical) != 0); + + // + // Phys Props for the Clickable itself. + // + plMaxNode *clickNode = node; + if (fCompPB->GetInt(kClickableUseProxy)) + { + clickNode = (plMaxNode*)fCompPB->GetINode(kClickableProxy); + if (clickNode) + clickNode->SetDrawable(false); + else + clickNode = node; + } + + if (clickNode) + { + plPhysicalProps *physProps = clickNode->GetPhysicalProps(); + physProps->SetLOSUIItem(true, clickNode, pErrMsg); + if (physical) + { + physProps->SetGroup(plSimDefs::kGroupStatic, clickNode, pErrMsg); + // only if movable will it have mass (then it will keep track of movements in PhysX) + if ( clickNode->IsMovable() || clickNode->IsTMAnimatedRecur() ) + physProps->SetMass(1.0, clickNode, pErrMsg); + physProps->SetFriction(fCompPB->GetFloat(kClickableFriction),clickNode,pErrMsg); + } + else + { + physProps->SetGroup(plSimDefs::kGroupLOSOnly, clickNode, pErrMsg); + if(clickNode->IsMovable() || clickNode->IsTMAnimatedRecur()) + { + physProps->SetMass(1.0, clickNode, pErrMsg); + + } + } + physProps->SetBoundsType(fCompPB->GetInt(kClickableBoundsType), clickNode, pErrMsg); + } + + // + // Phys Properties for the auto-generated Detector Region... + // + plMaxNode* detectNode = (plMaxNode*)fCompPB->GetINode(kClickableProxyRegion); + if (detectNode) + { + plPhysicalProps *physPropsDetector = detectNode->GetPhysicalProps(); +// physPropsDetector->SetAllowLOS(true, detectNode, pErrMsg); + physPropsDetector->SetProxyNode(detectNode, node, pErrMsg); + physPropsDetector->SetBoundsType(plSimDefs::kHullBounds, detectNode, pErrMsg); + // only if movable will it have mass (then it will keep track of movements in PhysX) + if ( detectNode->IsMovable() || detectNode->IsTMAnimatedRecur() ) + physPropsDetector->SetMass(1.0, detectNode, pErrMsg); + + physPropsDetector->SetGroup(plSimDefs::kGroupDetector, detectNode, pErrMsg ); + physPropsDetector->SetReportGroup(1<GetInt(kClickableUseProxy)) + { + clickNode = (plMaxNode*)fCompPB->GetINode(kClickableProxy); + if (clickNode) + clickNode->SetDrawable(false); + else + clickNode = node; + } + + clickNode->SetForceLocal(true); + + plLocation loc = clickNode->GetLocation(); + plSceneObject *obj = clickNode->GetSceneObject(); + + // Create and register the VolumeGadget's logic component + plLogicModifier *logic = TRACKED_NEW plLogicModifier; + plKey logicKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), logic, clickNode->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(logicKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + fLogicModKeys[clickNode] = logicKey; + + + return true; +} + + +hsBool plClickableComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + bool ignoreProxyRegion = (fCompPB->GetInt(kClickableIgnoreProxyRegion) != 0); + + // + // Error checking + // + plMaxNode* clickProxyNode = node; + if (fCompPB->GetInt(kClickableUseProxy)) + { + clickProxyNode = (plMaxNode*)fCompPB->GetINode(kClickableProxy); + if (!clickProxyNode || !clickProxyNode->CanConvert()) + { + pErrMsg->Set(true, + "Clickable Error", + "The Clickable '%s' on node '%s' is set to use a proxy but doesn't have one, or it didn't convert.\n" + "The node the Clickable is attached to will be used instead.", + GetINode()->GetName(), node->GetName()).Show(); + pErrMsg->Set(false); + + clickProxyNode = node; + } + } + + plMaxNode* detectNode = (plMaxNode*)fCompPB->GetINode(kClickableProxyRegion); + if ((!detectNode || !detectNode->CanConvert()) && (!ignoreProxyRegion)) + { + pErrMsg->Set(true, + "Clickable Error", + "The Clickable '%s' on node '%s' has a required region that is missing, or didn't convert.\n" + "The export will be aborted.", + GetINode()->GetName(), node->GetName()).Show(); + return false; + } + + + plLocation loc = clickProxyNode->GetLocation(); + plSceneObject *obj = clickProxyNode->GetSceneObject(); + + plKey logicKey = fLogicModKeys[clickProxyNode]; + plLogicModifier *logic = plLogicModifier::ConvertNoRef(logicKey->GetObjectPtr()); + logic->fMyCursor = plCursorChangeMsg::kCursorPoised; + + if (fCompPB->GetInt(kClickableOneShot)) + logic->SetFlag(plLogicModBase::kOneShot); + + hsTArray receivers; + IGetReceivers(node, receivers); + for (int i = 0; i < receivers.Count(); i++) + logic->AddNotifyReceiver(receivers[i]); + + // Create the detector + plDetectorModifier *detector = nil; + detector = TRACKED_NEW plPickingDetector; + + // Register the detector + plKey detectorKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), detector, loc); + hsgResMgr::ResMgr()->AddViaNotify(detectorKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + // create and register the CONDITIONS for the DETECTOR's Logic Modifier + plActivatorConditionalObject* activatorCond = TRACKED_NEW plActivatorConditionalObject; + plKey activatorKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), activatorCond, loc); + + // + // Create required region + // + // need a player in box condition here... + // first a detector-any for the box + if (!ignoreProxyRegion) + { + plObjectInVolumeDetector* pCDet = TRACKED_NEW plObjectInVolumeDetector(plCollisionDetector::kTypeAny); + + plKey pCDetKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), pCDet, loc); + hsgResMgr::ResMgr()->AddViaNotify(pCDetKey, TRACKED_NEW plObjRefMsg(detectNode->GetSceneObject()->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + pCDet->AddLogicObj(logicKey); + + // then an object-in-box condition for the logic mod + plObjectInBoxConditionalObject* boxCond = TRACKED_NEW plObjectInBoxConditionalObject; + plKey boxCondKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), boxCond, loc); + logic->AddCondition(boxCond); + } + + // + // How do we feel about player facing + // + plFacingConditionalObject* facingCond = TRACKED_NEW plFacingConditionalObject; + facingCond->SetDirectional(fCompPB->GetInt(kClickableDirectional)); + int deg = fCompPB->GetInt(kClickableDegrees); + if (deg > 180) + deg = 180; + hsScalar rad = hsScalarDegToRad(deg); + facingCond->SetTolerance(hsCosine(rad)); + plKey facingKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), facingCond, loc); + + detector->AddLogicObj(logicKey); // send messages to this logic component + activatorCond->SetActivatorKey(detectorKey); // Tells the activator condition to look for stimulus from the detector + + logic->AddCondition(activatorCond); // add this activator condition + logic->AddCondition(facingCond); + + logic->SetDisabled(!fCompPB->GetInt(kClickableEnabled)); + + // If this is for the SceneViewer, set the local only flag since the read function will never be called + if (plConvert::Instance().IsForSceneViewer()) + logic->SetLocalOnly(true); + + return true; +} + + + +// +// special physical you can walk through and click with mouse +// + +class plNoBlkClickableComponent : public plComponent +{ +public: + plNoBlkClickableComponent(); + void DeleteThis() { delete this; } + + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + +OBSOLETE_CLASS_DESC(plNoBlkClickableComponent, gNoBlkClickableDesc, "(ex)Non Physical Clickable Proxy", "(ex)Non Physical Clickable Proxy", COMP_TYPE_PHYSICAL, Class_ID(0x66325afc, 0x253a3760)) + +ParamBlockDesc2 gNoBlkClickableBlock +( + plComponent::kBlkComp, _T("NonPhysicalClickableProxy"), 0, &gNoBlkClickableDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + + end +); + +plNoBlkClickableComponent::plNoBlkClickableComponent() +{ + fClassDesc = &gNoBlkClickableDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plNoBlkClickableComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickableComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickableComponent.h new file mode 100644 index 00000000..305432f1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClickableComponent.h @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plClickableComponent_inc +#define plClickableComponent_inc + +#include "plActivatorBaseComponent.h" + +#define CLICKABLE_CID Class_ID(0x1a5f6892, 0x7b434188) + +class plClickableComponent : public plActivatorBaseComponent +{ +public: + plClickableComponent(); + + hsBool PreConvert(plMaxNode *node, plErrorMsg* pErrMsg); + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables); + +}; + +#endif // plClickableComponent_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClimbComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClimbComponent.cpp new file mode 100644 index 00000000..6eb5451f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClimbComponent.cpp @@ -0,0 +1,403 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +// max includes +#include "plPickNode.h" +#include "resource.h" + +// local +#include "plClimbComponent.h" +#include "plPhysicalComponents.h" // so we can pick terrains +#include "plComponentReg.h" + +// other +#include "../MaxMain/plMaxNode.h" +#include "../plMessage/plClimbMsg.h" +#include "../plPhysical/plCollisionDetector.h" +#include "../MaxMain/plPhysicalProps.h" +#include "../plPhysical/plSimDefs.h" +#include "../pnSceneObject/plSceneObject.h" + +// stl +#include + +///////////////////////////////////////////////////////////////// +// +// THE DUMMY +// +///////////////////////////////////////////////////////////////// +void DummyCodeIncludeFuncClimbTrigger() {} + +///////////////////////////////////////////////////////////////// +// +// SOME ENUMS +// +///////////////////////////////////////////////////////////////// +// CLIMBCOMMANDS +enum Commands +{ + kMount = 0, + kEnableDismount = 1, + kDisableDismount = 2, + kEnableClimb = 3, + kDisableClimb = 4, + kFallOff = 5, + kRelease = 6, + kMaxCommands = 7 +}; + +// these synchronized ^^^^VVVVV +const char * fCommandStrs[] = +{ + "Start Climbing", + "Enable Dismount", + "Disable Dismount", + "Enable Climb", + "Disable Climb", + "Fall Off", + "Let Go" +}; + +enum Directions +{ + kUp = 0, + kDown = 1, + kLeft = 2, + kRight = 3, + kMaxDirections +}; + +const char * fDirectionStrs[] = +{ + "Up", + "Down", + "Left", + "Right" +}; + + +///////////////////////////////////////////////////////////////// +// +// CLASS DESCRIPTOR AND PARAM BLOCK +// +///////////////////////////////////////////////////////////////// + +// CLASS DESCRIPTOR +CLASS_DESC(plClimbTriggerComponent, gClimbTriggerDesc, "Avatar ClimbTrigger", "AvatarClimbTrigger", COMP_TYPE_AVATAR, CLIMB_TRIGGER_COMPONENT_CLASS_ID) + +// FORWARD REFERENCE OT THE COMPONENT DIALOG PROC +static plClimbTriggerComponentProc gClimbTriggerComponentProc; + +// PARAM BLOCK +ParamBlockDesc2 gClimbTriggerBk +( + + plComponent::kBlkComp, _T("ClimbTrigger"), 0, &gClimbTriggerDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_CLIMB_TRIGGER, IDS_COMP_CLIMB_TRIGGER, 0, 0, &gClimbTriggerComponentProc, + + plClimbTriggerComponent::kCommand, _T("Command"), TYPE_INT, 0, 0, + p_default, kMount, + end, + + plClimbTriggerComponent::kDirection, _T("Direction"), TYPE_INT, 0, 0, + p_default, kUp, + end, + + plClimbTriggerComponent::kWallPicker, _T("WallPicker"), TYPE_INODE, 0, 0, + end, + + end +); + +///////////////////////////////////////////////////////////////// +// +// PLCLIMBTRIGGER IMPLEMENTATION +// +///////////////////////////////////////////////////////////////// + +// CTOR +plClimbTriggerComponent::plClimbTriggerComponent() +{ + fClassDesc = &gClimbTriggerDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +extern const plArmatureMod * FindArmatureMod(const plSceneObject *obj); + +// CONVERT +hsBool plClimbTriggerComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plClimbMsg::Command enterCommand; // when entering the region + plClimbMsg::Command exitCommand; // run this command when exiting the region + hsBool enterStatus = false; + hsBool exitStatus = false; + plClimbMsg::Direction direction; // direction is assumed the same for both enter and exit commands + // i.e. enable up, disable up + + int iCommand = fCompPB->GetInt(plClimbTriggerComponent::kCommand); + int iDirection = fCompPB->GetInt(plClimbTriggerComponent::kDirection); + + switch(iCommand) + { + case kMount: + enterCommand = plClimbMsg::kStartClimbing; + exitCommand = plClimbMsg::kNoCommand; + break; + case kEnableClimb: + enterCommand = plClimbMsg::kEnableClimb; + enterStatus = true; + exitCommand = plClimbMsg::kEnableClimb; + exitStatus = false; + break; + case kDisableClimb: + enterCommand = plClimbMsg::kEnableClimb; + enterStatus = false; + exitCommand = plClimbMsg::kEnableClimb; + exitStatus = true; + break; + case kEnableDismount: + enterCommand = plClimbMsg::kEnableDismount; + enterStatus = true; + exitCommand = plClimbMsg::kEnableDismount; + exitStatus = false; + break; + case kDisableDismount: + enterCommand = plClimbMsg::kEnableDismount; + enterStatus = false; + exitCommand = plClimbMsg::kEnableDismount; + exitStatus = true; + break; + case kFallOff: + enterCommand = plClimbMsg::kFallOff; + exitCommand = plClimbMsg::kNoCommand; + break; + case kRelease: + enterCommand = plClimbMsg::kRelease; + exitCommand = plClimbMsg::kNoCommand; + break; + } + + switch(iDirection) + { + case kUp: + direction = plClimbMsg::kUp; + break; + case kDown: + direction = plClimbMsg::kDown; + break; + case kLeft: + direction = plClimbMsg::kLeft; + break; + case kRight: + direction = plClimbMsg::kRight; + break; + } + + plKey nilKey = nil; + plKey target = node->GetSceneObject()->GetKey(); + plClimbMsg *enterMsg = nil; + if(enterCommand != plClimbMsg::kNoCommand) + { + enterMsg = TRACKED_NEW plClimbMsg(nilKey, nilKey, enterCommand, direction, enterStatus, target); + enterMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + enterMsg->SetBCastFlag(plMessage::kNetPropagate); + enterMsg->SetBCastFlag(plMessage::kNetForce); + } + + plClimbMsg *exitMsg = nil; + if(exitCommand != nil) + { + exitMsg = TRACKED_NEW plClimbMsg(nilKey, nilKey, exitCommand, direction, exitStatus, target); + exitMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + exitMsg->SetBCastFlag(plMessage::kNetPropagate); + exitMsg->SetBCastFlag(plMessage::kNetForce); + } + + plSimpleRegionSensor *sensMod = TRACKED_NEW plSimpleRegionSensor(enterMsg, exitMsg); + node->AddModifier(sensMod, IGetUniqueName(node)); + + return true; +} + +hsBool plClimbTriggerComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetForceLocal(true); + node->SetDrawable(false); + + plPhysicalProps *props = node->GetPhysicalProps(); + + // only if movable will it have mass (then it will keep track of movements in PhysX) + if ( node->IsMovable() || node->IsTMAnimatedRecur() ) + props->SetMass(1.0, node, pErrMsg); + props->SetFriction(0.0, node, pErrMsg); + props->SetRestitution(0.0, node, pErrMsg); + props->SetBoundsType(plSimDefs::kExplicitBounds, node, pErrMsg); + props->SetGroup(plSimDefs::kGroupDetector, node, pErrMsg); + props->SetReportGroup(1<SetPinned(true, node, pErrMsg); + + return true; +} + + +///////////////////////////////////////////////////////////////// +// +// DIALOG PROC IMPLEMENTATION +// +///////////////////////////////////////////////////////////////// +BOOL plClimbTriggerComponentProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + IParamBlock2 *pb = pm->GetParamBlock(); + HWND hCommandMenu = GetDlgItem(hWnd, IDC_COMP_CLIMB_COMMAND); + HWND hDirectionMenu = GetDlgItem(hWnd, IDC_COMP_CLIMB_DIRECTION); + HWND hPick = GetDlgItem(hWnd, IDC_COMP_WALL_PICK); + INode *curPick = nil; + int curSurface = 0; + + switch (msg) + { + case WM_INITDIALOG: + { + int i = 0; + // fill out the command menu + for (i = 0; i < kMaxCommands; i++) + ComboBox_AddString(hCommandMenu, fCommandStrs[i]); + // reflect the current selection + ComboBox_SetCurSel(hCommandMenu, pb->GetInt(ParamID(plClimbTriggerComponent::kCommand))); + + // fill out the direction menu + for (i = 0; i < kMaxDirections; i++) + ComboBox_AddString(hDirectionMenu, fDirectionStrs[i]); + // reflect the current selection + ComboBox_SetCurSel(hDirectionMenu, pb->GetInt(ParamID(plClimbTriggerComponent::kDirection))); + + // show the name of the currently picked item + curPick = pb->GetINode(ParamID(plClimbTriggerComponent::kWallPicker)); + Button_SetText(hPick, (curPick == nil ? "None" : curPick->GetName())); + } + return TRUE; + break; + + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + if (LOWORD(wParam) == IDC_COMP_WALL_PICK) + { + // we're picking a new climbing wall + std::vector pickableClasses; + pickableClasses.push_back(PHYSICS_TERRAIN_CID); // allow picking terrains + pickableClasses.push_back(PHYS_CLIMBABLE_CID); // and climbables + if (plPick::NodeRefKludge(pb, plClimbTriggerComponent::kWallPicker, &pickableClasses, true, false)) + { + curPick = pb->GetINode(ParamID(plClimbTriggerComponent::kWallPicker)); + Button_SetText(hPick, (curPick == nil ? "None" : curPick->GetName())); + } + + return TRUE; + } + } + else if (LOWORD(wParam) == IDC_COMP_CLIMB_COMMAND) + { + HWND hSurface = GetDlgItem(hWnd, IDC_COMP_CLIMB_COMMAND); + curSurface = ComboBox_GetCurSel(hSurface); + pb->SetValue(ParamID(plClimbTriggerComponent::kCommand), 0, curSurface); + } + else if (LOWORD(wParam) == IDC_COMP_CLIMB_DIRECTION) + { + HWND hSurface = GetDlgItem(hWnd, IDC_COMP_CLIMB_DIRECTION); + curSurface = ComboBox_GetCurSel(hSurface); + pb->SetValue(ParamID(plClimbTriggerComponent::kDirection), 0, curSurface); + } + } + + return FALSE; +} + + + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// CLIMB BLOCKER COMPONENT +// +///////////////////////////////////////////////////////////////////////////////////////// + +//Class that accesses the paramblock below. +class plClimbBlockerComponent : public plComponent +{ +public: + plClimbBlockerComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plClimbBlockerComponent, gClimbBlockDesc, "Climb Blocker", "ClimbBlocker", COMP_TYPE_AVATAR, Class_ID(0x170000ac, 0x3cee02c5)) + +ParamBlockDesc2 gClimbBlockBk +( + plComponent::kBlkComp, _T("Climb Blocker"), 0, &gClimbBlockDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_CLIMB_BLOCK, IDS_COMP_CLIMB_BLOCKER, 0, 0, NULL, + + end +); + +plClimbBlockerComponent::plClimbBlockerComponent() +{ + fClassDesc = &gClimbBlockDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plClimbBlockerComponent::SetupProperties(plMaxNode *node, plErrorMsg *errMsg) +{ + node->SetDrawable(false); + + plPhysicalProps *props = node->GetPhysicalProps(); + +// props->SetMass(0.0, node, errMsg); +// props->SetFriction(0.0, node, errMsg); +// props->SetRestitution(0.0, node, errMsg); + props->SetBoundsType(plSimDefs::kExplicitBounds, node, errMsg); +// props->SetPinned(true, node, errMsg); + props->SetLOSBlockCustom(true, node, errMsg); + + return true; +} + +hsBool plClimbBlockerComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClimbComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClimbComponent.h new file mode 100644 index 00000000..745e6a0d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClimbComponent.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_CLIMB_COMPONENT_H +#define PL_CLIMB_COMPONENT_H + +#include "plComponent.h" + +class plClimbTriggerComponent : public plComponent +{ +public: + plClimbTriggerComponent(); + + virtual void DeleteThis() { delete this; } + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum // ParamBlock indices + { + kCommand, + kDirection, + kWallPicker + }; + +private: + +}; + +#define CLIMB_TRIGGER_COMPONENT_CLASS_ID Class_ID(0x69857572, 0x671d0236) + +class plClimbTriggerComponentProc : public ParamMap2UserDlgProc +{ +public: + plClimbTriggerComponentProc() {} + + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + + void DeleteThis() {} +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClothingComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClothingComponent.cpp new file mode 100644 index 00000000..18186550 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClothingComponent.cpp @@ -0,0 +1,345 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "resource.h" +//#include "hsUtils.h" +#include "hsTemplates.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plResMgr/plPageInfo.h" +#include "hsResMgr.h" +#include "../MaxMain/plMaxNode.h" +#include "plClothingComponent.h" +#include "plComponentReg.h" +#include "../MaxPlasmaMtls/Materials/plClothingMtl.h" +#include "../pnMessage/plRefMsg.h" +#include "../plAvatar/plAvatarClothing.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../MaxConvert/hsMaterialConverter.h" +#include "../MaxConvert/plMeshConverter.h" +#include "plPickMaterialMap.h" +#include "../MaxMain/plMtlCollector.h" +#include "plAvatarComponent.h" +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "../plDrawable/plSharedMesh.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plMorphSequence.h" +#include "../plScene/plSceneNode.h" +#include "../plDrawable/plGeometrySpan.h" + +void DummyCodeIncludeFuncClothing() +{ +} + +CLASS_DESC(plClothingComponent, gClothingDesc, "Avatar Clothing", "AvatarClothing", COMP_TYPE_MISC, CLOTHING_COMPONENT_CLASS_ID) + +static plClothingComponentProc gClothingComponentProc; + +class plClothingAccessor : public PBAccessor +{ +public: + + //! Public Accessor Class, used in ParamBlock2 processing. + /*! + Workhorse for this Accessor Class (derived from Max's PBAccessor). + + When one of our parameters that is a ref changes, send out the component ref + changed message. Normally, messages from component refs are ignored since + they pass along all the messages of the ref, which generates a lot of false + converts. + */ + + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (id == plClothingComponent::kMeshNodeAddBtn) + { + plClothingComponent *comp = (plClothingComponent *)owner; + IParamBlock2 *pb = comp->GetParamBlockByID(plClothingComponent::kBlkComp); + int state = pb->GetInt(plClothingComponent::kLODState); + + INode *node = pb->GetINode(plClothingComponent::kMeshNodeAddBtn); + if (node) + pb->SetValue(plClothingComponent::kMeshNodeTab, 0, node, state); + } + + if (id == plClothingComponent::kMeshNodeTab) + { + plComponentBase *comp = (plComponentBase*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; + +plClothingAccessor gClothingAccessor; + +ParamBlockDesc2 gClothingBk +( + + plComponent::kBlkComp, _T("Clothing"), 0, &gClothingDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_CLOTHING, IDS_COMP_CLOTHING, 0, 0, &gClothingComponentProc, + + plClothingComponent::kMaterials, _T("ClothingMaterials"), TYPE_MTL_TAB, 0, 0, 0, + end, + + plClothingComponent::kGroup, _T("ClothingGroup"), TYPE_INT, 0, 0, + end, + + plClothingComponent::kType, _T("ClothingType"), TYPE_INT, 0, 0, + end, + + plClothingComponent::kLODState, _T("LODState"), TYPE_INT, 0, 0, + p_default, 0, + end, + + plClothingComponent::kMeshNodeTab, _T("MeshObject"), TYPE_INODE_TAB, plLODAvatarComponent::kMaxNumLODLevels, 0, 0, + p_accessor, &gClothingAccessor, + end, + + plClothingComponent::kMeshNodeAddBtn, _T("MshNodePicker"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_LOD_CLOTHING_MESH_PICKB, + p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_AVATAR_PROXYS, + p_accessor, &gClothingAccessor, + end, + + end +); + +plClothingComponent::plClothingComponent() +{ + fClassDesc = &gClothingDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plClothingComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + int i; + for (i = 0; i < fCompPB->Count(kMeshNodeTab); i++) + { + plMaxNode *LODNode = (plMaxNode *)fCompPB->GetINode(kMeshNodeTab, 0, i); + if (LODNode != nil) + { + char *dbgNodeName = LODNode->GetName(); + //LODNode->SetCanConvert(false); + LODNode->SetDrawable(false); + LODNode->SetForceShadow(true); + LODNode->SetForceMatShade(true); + if (!LODNode->GetSwappableGeom()) + LODNode->SetSwappableGeom(new plSharedMesh); + + //UInt32 targetID = fCompPB->GetInt(kType); + //((plMaxNode *)LODNode->GetParentNode())->SetSwappableGeomTarget(targetID); + } + } + + return true; +} + +hsBool plClothingComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetupBoneHierarchyPalette(); + + return true; +} + +hsBool plClothingComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + int i, j; + hsTArray spanArray; + hsTArray keys; + plMaxNode *LODNode = nil; + plMaxNode *locationNode = nil; + + + if (fCompPB->Count(plClothingComponent::kMaterials) <= 0) + return true; + + for (i = 0; i < fCompPB->Count(kMeshNodeTab); i++) + { + spanArray.Reset(); + //plSharedMesh *mesh = TRACKED_NEW plSharedMesh; + LODNode = (plMaxNode *)fCompPB->GetINode(kMeshNodeTab, 0, i); + if (LODNode != nil) + { + char *dbgNodeName = LODNode->GetName(); + keys.Append(LODNode->GetSwappableGeom()->GetKey()); + locationNode = LODNode; + + if (fCompPB->GetInt(ParamID(kType)) != plClothingMgr::kTypeFace) + { + // We only save state for the face node. + LODNode->GetSwappableGeom()->fFlags |= plSharedMesh::kDontSaveMorphState; + } + else + { + // The face's weight for the first layer (0) is to be applied to all + // meshes on that node. + LODNode->GetSwappableGeom()->fFlags |= plSharedMesh::kLayer0GlobalToMod; + } + } + else + { + keys.Append(nil); + //delete mesh; + } + } + + const plPageInfo* thisInfo = plKeyFinder::Instance().GetLocationInfo(locationNode ? locationNode->GetLocation() : node->GetLocation()); + const plLocation &loc = plKeyFinder::Instance().FindLocation("GlobalClothing", thisInfo->GetPage()); + + for (i = 0; i < fCompPB->Count(plClothingComponent::kMaterials); i++) + { + plClothingMtl *mtl = (plClothingMtl *)fCompPB->GetMtl(ParamID(plClothingComponent::kMaterials), 0, i); + plClothingItem *cloth = hsMaterialConverter::Instance().GenerateClothingItem(mtl, loc); + cloth->fGroup = fCompPB->GetInt(ParamID(kGroup)); + cloth->fType = fCompPB->GetInt(ParamID(kType)); + + plGenRefMsg *refMsg; + for (j = 0; j < keys.GetCount(); j++) + { + if (keys[j] != nil) + { + refMsg = TRACKED_NEW plGenRefMsg(cloth->GetKey(), plRefMsg::kOnCreate, j, -1); + hsgResMgr::ResMgr()->AddViaNotify(keys[j], refMsg, plRefFlags::kActiveRef); + } + } + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +BOOL plClothingComponentProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + IParamBlock2 *pb = pm->GetParamBlock(); + HWND hList = GetDlgItem(hWnd, IDC_CLOTHING_LIST); + HWND hGroup = GetDlgItem(hWnd, IDC_CLOTHING_GROUP); + HWND hType = GetDlgItem(hWnd, IDC_CLOTHING_TYPE); + HWND hLOD = GetDlgItem(hWnd, IDC_COMP_LOD_CLOTHING_STATE); + switch (msg) + { + case WM_INITDIALOG: + { + ListBox_ResetContent(hList); + int i; + for (i = 0; i < pb->Count(plClothingComponent::kMaterials); i++) + ListBox_AddString(hList, pb->GetMtl(ParamID(plClothingComponent::kMaterials), 0, i)->GetName()); + + ListBox_SetCurSel(hList, -1); + + for (i = 0; i < plClothingMgr::kMaxGroup; i++) + ComboBox_AddString(hGroup, plClothingMgr::GroupStrings[i]); + ComboBox_SetCurSel(hGroup, pb->GetInt(plClothingComponent::kGroup)); + + for (i = 0; i < plClothingMgr::kMaxType; i++) + ComboBox_AddString(hType, plClothingMgr::TypeStrings[i]); + ComboBox_SetCurSel(hType, pb->GetInt(plClothingComponent::kType)); + + ComboBox_AddString(hLOD, "High"); + ComboBox_AddString(hLOD, "Medium"); + ComboBox_AddString(hLOD, "Low"); + ComboBox_SetCurSel(hLOD, pb->GetInt(plClothingComponent::kLODState)); + } + return TRUE; + + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + if (LOWORD(wParam) == IDC_CLOTHING_ADD) + { + Mtl *pickedMtl = plPickMaterialMap::PickMaterial(plMtlCollector::kClothingMtlOnly); + if (pickedMtl != nil) + { + LRESULT stringIdx = ListBox_FindStringExact(hList, -1, pickedMtl->GetName()); + if (stringIdx == LB_ERR) // It's not already there, go and add it + { + pb->Append(ParamID(plClothingComponent::kMaterials), 1, &pickedMtl, 0); + ListBox_AddString(hList, pickedMtl->GetName()); + } + } + return TRUE; + } + // Remove the currently selected material + else if (LOWORD(wParam) == IDC_CLOTHING_REMOVE) + { + int sel = ListBox_GetCurSel(hList); + if (sel != LB_ERR) + { + pb->Delete(plClothingComponent::kMaterials, sel, 1); + ListBox_DeleteString(hList, sel); + } + return TRUE; + } + else if( LOWORD( wParam ) == IDC_CLOTHING_CLEARMESH ) + { + int state = pb->GetInt(plClothingComponent::kLODState); + pb->SetValue(plClothingComponent::kMeshNodeTab, 0, (INode*)nil, state ); + pb->Reset(plClothingComponent::kMeshNodeAddBtn); + } + } + else if (LOWORD(wParam) == IDC_CLOTHING_GROUP) + { + int setIdx = ComboBox_GetCurSel(hGroup); + pb->SetValue(plClothingComponent::kGroup, 0, setIdx); + + return TRUE; + } + else if (LOWORD(wParam) == IDC_CLOTHING_TYPE) + { + int setIdx = ComboBox_GetCurSel(hType); + pb->SetValue(plClothingComponent::kType, 0, setIdx); + + return TRUE; + } + else + { + int state = pb->GetInt(plClothingComponent::kLODState); + + INode *node = pb->GetINode(plClothingComponent::kMeshNodeAddBtn); + if (node) + pb->SetValue(plClothingComponent::kMeshNodeTab, 0, node, state); + + if(LOWORD(wParam) == IDC_COMP_LOD_CLOTHING_STATE && HIWORD(wParam) == CBN_SELCHANGE) + { + int idx = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); + pb->SetValue(plClothingComponent::kLODState, 0, idx); + + node = pb->GetINode(plClothingComponent::kMeshNodeTab, 0, idx); + if (node) + pb->SetValue(plClothingComponent::kMeshNodeAddBtn, 0, node); + else + pb->Reset(plClothingComponent::kMeshNodeAddBtn); + + return TRUE; + } + } + } + + return FALSE; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClothingComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClothingComponent.h new file mode 100644 index 00000000..f49cb9a1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClothingComponent.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_CLOTHING_COMPONENT_H +#define PL_CLOTHING_COMPONENT_H + +#include "plComponent.h" + +class plClothingComponent : public plComponent +{ +public: + plClothingComponent(); + + virtual void DeleteThis() { delete this; } + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum // ParamBlock indices + { + kMaterials, + kGroup, + kType, + kLODState, + kMeshNodeTab, + kMeshNodeAddBtn, + }; +}; + +#define CLOTHING_COMPONENT_CLASS_ID Class_ID(0x2df85c56, 0x27bc2a7a) + +class plClothingComponentProc : public ParamMap2UserDlgProc +{ +public: + plClothingComponentProc() {} + + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + + void DeleteThis() {} +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClusterComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClusterComponent.cpp new file mode 100644 index 00000000..a7146d4e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClusterComponent.cpp @@ -0,0 +1,1179 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + + +#include "max.h" +#include "meshdlib.h" +#include "dummy.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../MaxExport/plExportProgressBar.h" +#include "../MaxMain/plMaxNode.h" + +#include "hsTypes.h" + +#include "hsBitVector.h" +#include "../plMath/hsRadixSort.h" +#include "../plMath/plRandom.h" +#include "../pfAnimation/plBlower.h" + +#include "plDicer.h" +#include "plDistribComponent.h" +#include "../MaxConvert/plDistributor.h" +#include "../MaxConvert/plDistTree.h" +#include "plMiscComponents.h" + +#include "plClusterComponent.h" + + +#include "../MaxConvert/plClusterUtil.h" +#include "../plDrawable/plClusterGroup.h" +#include "../plDrawable/plSpanTemplate.h" + +#include +using namespace std; + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// Start with the component bookkeeping song and dance. +// Actual working code follows. +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +void DummyCodeIncludeFuncCluster() +{ + +} + +class plClusterComponentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_COMMAND: + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_CLUSTER_DO_THE_DANCE) ) + { + plClusterComponent* cc = (plClusterComponent*)map->GetParamBlock()->GetOwner(); + cc->Cluster(nil); + + return TRUE; + } + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_CLUSTER_CLEAR) ) + { + plClusterComponent* cc = (plClusterComponent*)map->GetParamBlock()->GetOwner(); + cc->Clear(); + + return TRUE; + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plClusterComponentProc gClusterCompProc; + +//Max desc stuff necessary below. +CLASS_DESC(plClusterComponent, gClusterCompDesc, "Cluster", "Cluster", COMP_TYPE_DISTRIBUTOR, CLUSTER_COMP_CID) + +class plClusterCompAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if( id == plClusterComponent::kWindBones ) + { + plClusterComponent *comp = (plClusterComponent*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +plClusterCompAccessor gClusterCompAccessor; + +ParamBlockDesc2 gClusterBk +( + plComponent::kBlkComp, _T("Cluster"), 0, &gClusterCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_CLUSTER, IDS_COMP_CLUSTERS, 0, 0, &gClusterCompProc, + + plClusterComponent::kClusters, _T("Clusters"), TYPE_INODE_TAB, 0, 0, 0, + end, + + plClusterComponent::kOptimization, _T("Optimization"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_CLUSTERSIZE, IDC_COMP_CLUSTERSIZE_SPIN, 1.0, + end, + + plClusterComponent::kFadeIns, _T("FadeIns"), TYPE_POINT3_TAB, 0, 0, 0, + end, + + plClusterComponent::kFadeOuts, _T("FadeOuts"), TYPE_POINT3_TAB, 0, 0, 0, + end, + + // OBSOLETE + plClusterComponent::kWindBone, _T("WindBone"), TYPE_INODE, 0, 0, +// p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CLUSTER_WINDBONE, +// p_prompt, IDS_COMP_CLUSTER_CHOSE_WINDBONE, + end, + + plClusterComponent::kWindBones, _T("WindBones"), TYPE_INODE_TAB, 0, 0, 0, + end, + + plClusterComponent::kAutoGen, _T("AutoGen"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CLUST_AUTOEXPORT, + end, + + plClusterComponent::kAutoInstance, _T("AutoInstance"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_CLUST_AUTOINSTANCE, + end, + + end +); + + +hsBool plClusterComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fSetupDone = false; + fExported = false; + + int numClust = fCompPB->Count(kClusters); + int i; + for( i = numClust-1; i >= 0; --i ) + { + if( !fCompPB->GetINode(kClusters, TimeValue(0), i) ) + fCompPB->Delete(kClusters, i, 1); + } + + return true; +} + +static int CompTemplNodes(const void *elem1, const void *elem2) +{ + plDistribInstance* distA = (plDistribInstance*)elem1; + plDistribInstance* distB = (plDistribInstance*)elem2; + + plMaxNode* a = (plMaxNode*)distA->fNode; + plMaxNode* b = (plMaxNode*)distB->fNode; + + if( a == b ) + return 0; + + if( a->GetRenderLevel(!a->GetNoDeferDraw()) < b->GetRenderLevel(!b->GetNoDeferDraw()) ) + return -1; + if( a->GetRenderLevel(!a->GetNoDeferDraw()) > b->GetRenderLevel(!b->GetNoDeferDraw()) ) + return 1; + + if( a < b ) + return -1; + + return 1; +} + +hsBool plClusterComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !fSetupDone ) + { + int numClust = fCompPB->Count(kClusters); + int i; + for( i = 0; i < numClust; i++ ) + { + plMaxNodeBase* clust = (plMaxNodeBase*)fCompPB->GetINode(kClusters, TimeValue(0), i); + if( clust ) + { + Box3 fade(fCompPB->GetPoint3(kFadeIns, TimeValue(0), i), fCompPB->GetPoint3(kFadeOuts, TimeValue(0), i)); + + // Deal here is that, although we'd love to properly sort all the time, most of the time we don't + // need to and/or can't afford it, at least with the closeup dense plants. This is a sort of hacky + // way to guess whether we can afford a proper sort. The idea is that anything that is only visible + // from farther than N feet away is probably some cheap imposter kind of representation that we can + // afford to face sort (it's likely to be two sided convex objects too, which means it'll need the sort). + // Can we do better? Not tonight. + const float kMinDistantFadeIn = 70.f; + const float kMaxDistantFadeOut = 10.f; + BOOL faceSort = false; + if( fade.Min()[2] < 0 ) + { + if( fade.Min()[0] > kMinDistantFadeIn ) + faceSort = true; + } + else if( fade.Max()[2] > 0 ) + { + if( fade.Max()[1] < kMaxDistantFadeOut ) + faceSort = true; + } + if( faceSort ) + faceSort = true; + + clust->SetFade(fade); + clust->SetNoDeferDraw(true); + clust->SetNoFaceSort(!faceSort); + clust->SetNormalChan(plDistributor::kNormMapChan); + + if( i < fCompPB->Count(kWindBones) ) + { + plMaxNodeBase* windBone = (plMaxNodeBase*)fCompPB->GetINode(kWindBones, TimeValue(0), i); + // FISHHACK + // BoneUpdate + // Add clust as first bone, windBone as second. + if( windBone && (windBone != clust) ) + clust->AddBone(windBone); + } + } + } + + ISetupRenderDependencies(); + + fClusterGroups.clear(); + + if (fCompPB->GetInt(kAutoInstance)) + { + hsBitVector doneBits; + + IBuildDistribTab(); + if( !fDistribTab.Count() ) + { + fSetupDone = true; + return true; + } + + plDistribInstTab nodes; + + plExportProgressBar bar; + if( IBuildNodeTab(nodes, pErrMsg, bar) ) + { + nodes.Sort(CompTemplNodes); + + plClusterUtil util; + + int i = 0; + while( i < nodes.Count() ) + { + plMaxNode* repNode = (plMaxNode*)nodes[i].fNode; + + int nextNode; + for( nextNode = i+1; (nextNode < nodes.Count()) && (nodes[i].fNode == nodes[nextNode].fNode); nextNode++ ) + {} // intentional, we just want the i value + + // As far as I can tell, we don't actually use the templates generated here, we just use the count + // to know how many groups to create, and then generate the templates again in Convert(). + // Looks like a hack that never got cleaned up. + plSpanTemplTab templs = util.MakeTemplates(repNode); + int j; + for( j = 0; j < templs.Count(); j++ ) + { + fClusterGroups.push_back(util.CreateGroup(repNode, GetINode()->GetName())); + delete templs[j]; + } + + i = nextNode; + } + } + IClearNodeTab(); + IClearDistribTab(); + } + + fSetupDone = true; + } + return true; +} + +hsBool plClusterComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !fExported && (fCompPB->GetInt(kAutoInstance)) ) + { + hsBitVector doneBits; + + IBuildDistribTab(); + if( !fDistribTab.Count() ) + { + fExported = true; + return true; + } + + plDistribInstTab nodes; + + plExportProgressBar bar; + if( IBuildNodeTab(nodes, pErrMsg, bar) ) + { + nodes.Sort(CompTemplNodes); + + plClusterUtil util; + + plDeformVert defVert; + plShadeVert shadeVert; + + int groupIdx = 0; + int i = 0; + while( i < nodes.Count() ) + { + plL2WTab l2wTab; + plMaxNode* repNode = (plMaxNode*)nodes[i].fNode; + + Matrix3 l2w = nodes[i].fObjectTM; + l2wTab.Append(1, &l2w); + + int nextNode; + for( nextNode = i+1; (nextNode < nodes.Count()) && (nodes[i].fNode == nodes[nextNode].fNode); nextNode++ ) + { + l2wTab.Append(1, &nodes[nextNode].fObjectTM); + } + + plSpanTemplTab templs = util.MakeTemplates(repNode); + int j; + for( j = 0; j < templs.Count(); j++ ) + { + util.SetupGroup(fClusterGroups[groupIdx], repNode, templs[j]); + groupIdx++; + + util.AddClusters(l2wTab, nil, nil); + + } + i = nextNode; + } + } + IClearNodeTab(); + IClearDistribTab(); + fExported = true; + } + return true; +} + +plClusterComponent::plClusterComponent() +{ + fClassDesc = &gClusterCompDesc; + fClassDesc->MakeAutoParamBlocks(this); + + fClusterBins = nil; + fSizes[0] = fSizes[1] = fSizes[2] = 0; + + fAutoGen = FALSE; +} + + +void plClusterComponent::ICheckWindBone() +{ +} + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// Working end of the gun follows below this line. +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +BOOL plClusterComponent::AutoGen(plErrorMsg* pErrMsg) +{ + if( !fCompPB->Count(kClusters) && fCompPB->GetInt(kAutoGen) ) + { + fAutoGen = true; + return Cluster(pErrMsg); + } + return false; +} + +void plClusterComponent::AutoClear(plErrorMsg* pErrMsg) +{ + if( fAutoGen ) + { + Clear(); + fAutoGen = false; + } +} + +void plClusterComponent::IBuildDistribTab() +{ + plDistribCompTab& tab = fDistribTab; + tab.ZeroCount(); + // Okay, laziest hackiest sort algorithm in the world follows. + // But it's okay, the number of distributors is small. + plDistribCompTab sortTab; + Tab valTab; + // For each target + int numTarg = NumTargets(); + int i; + for( i = 0; i < numTarg; i++ ) + { + plMaxNodeBase* targ = GetTarget(i); + if( targ ) + { + UInt32 count = targ->NumAttachedComponents(); + int j; + for( j = 0; j < count; j++ ) + // For each DistribComponent + { + plComponentBase *comp = targ->GetAttachedComponent(j); + if( comp && (comp->ClassID() == DISTRIBUTOR_COMP_CID) ) + { + // Add DistribComponent replicants to nodeTab + // making sure we don't add any nodes twice. + plDistribComponent* distComp = (plDistribComponent*)comp; + int k; + for( k = 0; k < sortTab.Count(); k++ ) + { + if( distComp == sortTab[k] ) + break; + } + if( k == sortTab.Count() ) + { + sortTab.Append(1, &distComp); + float val = distComp->GetIsoPriority(); + valTab.Append(1, &val); + } + } + + } + } + } + BitArray gotten(sortTab.Count()); + while( tab.Count() < sortTab.Count() ) + { + float maxVal = -1.f; + int maxIdx = -1; + for( i = 0; i < sortTab.Count(); i++ ) + { + if( !gotten[i] ) + { + if( valTab[i] > maxVal ) + { + maxVal = valTab[i]; + maxIdx = i; + } + } + } + gotten.Set(maxIdx); + tab.Append(1, &sortTab[maxIdx]); + } +} + +void plClusterComponent::IClearDistribTab() +{ + int i; + for( i = 0; i < fDistribTab.Count(); i++ ) + fDistribTab[i]->Done(); + + fDistribTab.ZeroCount(); +} + +BOOL plClusterComponent::IsFlexible() const +{ + int i; + for( i = 0; i < fDistribTab.Count(); i++ ) + { + if( fDistribTab[i]->IsFlexible() ) + return true; + } + return false; +} + + +BOOL plClusterComponent::IBuildNodeTab(plDistribInstTab& nodes, plErrorMsg* pErrMsg, plExportProgressBar& bar) +{ + plDistTree distTree; + nodes.ZeroCount(); + int numDistrib = fDistribTab.Count(); + if( numDistrib ) + { + int progCnt = 0; + int i; + for( i = 0; i < numDistrib; i++ ) + progCnt += fDistribTab[i]->NumTargets(); + if( !progCnt ) + progCnt = 1; + + bar.Start("Compiling", progCnt << 4); + + if( bar.Update(nil, 0) ) + return false; + + for( i = 0; i < numDistrib; i++ ) + { + plDistribInstTab reps; + if( !fDistribTab[i]->Distribute(reps, pErrMsg, bar, &distTree) ) + return false; + if( reps.Count() ) + nodes.Append(reps.Count(), &reps[0]); + } + } + return true; +} + +void plClusterComponent::IClearNodeTab() +{ + int i; + for( i = 0; i < fDistribTab.Count(); i++ ) + fDistribTab[i]->Done(); +} + +void plClusterComponent::Select() +{ + INodeTab nodeTab; + int numClust = fCompPB->Count(kClusters); + int i; + for( i = 0; i < numClust; i++ ) + { + INode* clust = fCompPB->GetINode(kClusters, TimeValue(0), i); + if( clust ) + { + nodeTab.Append(1, &clust); + } + } + GetCOREInterface()->RemoveNamedSelSet(TSTR(GetINode()->GetName())); + GetCOREInterface()->AddNewNamedSelSet(nodeTab, TSTR(GetINode()->GetName())); +} + +void plClusterComponent::Clear() +{ + int numClust = fCompPB->Count(kClusters); + if( !numClust ) + return; + + GetCOREInterface()->DisableSceneRedraw(); + + plExportProgressBar bar; + + const int log2freq = 2; + const int maskfreq = (1 << log2freq)-1; + int totalSteps = numClust >> log2freq; + if( !totalSteps ) + totalSteps = 1; + bar.Start("Deleting", totalSteps); + + bar.Update(nil, 0); + + int i; + for( i = 0; i < numClust; i++ ) + { + plMaxNode* cluster = (plMaxNode*)fCompPB->GetINode(kClusters, TimeValue(0), i); + if( cluster ) + { + // HACK FISH - till we get a real fix for the slowdown caused by + // deleting things with a location component on them. + int numComp = cluster->NumAttachedComponents(); + int j; + for( j = numComp-1; j >= 0; --j ) + { + plComponentBase* comp = cluster->GetAttachedComponent(j); + if( comp ) + { + comp->DeleteTarget(cluster); + } + } + // END HACK FISH + + cluster->Delete(TimeValue(0), true); + } + + if( !(i & maskfreq) ) + bar.Update(nil); + } + + fCompPB->ZeroCount(kClusters); + fCompPB->ZeroCount(kFadeIns); + fCompPB->ZeroCount(kFadeOuts); + fCompPB->ZeroCount(kWindBones); + + GetCOREInterface()->EnableSceneRedraw(); + + GetCOREInterface()->ForceCompleteRedraw(FALSE); +} + +BOOL plClusterComponent::Cluster(plErrorMsg* pErrMsg) +{ + GetCOREInterface()->RedrawViews(GetCOREInterface()->GetTime(), REDRAW_BEGIN); + + Clear(); + + IGetLocation(); + + ICheckWindBone(); + +// fClusterSize = fCompPB->GetFloat(kClusterSize); +// fClusterSize = 75.f; + + plExportProgressBar bar; + + IBuildDistribTab(); + + INodeTab doneNodes; + plBox3Tab fade; + INodeTab bone; + hsBitVector boneIsParent; + hsBitVector doneBits; + + plDistribInstTab nodes; + + BOOL failed = true; + if( IBuildNodeTab(nodes, pErrMsg, bar) ) + { + failed = false; + + if( nodes.Count() ) + { + bar.Start("Optimizing", nodes.Count()); + bar.Update(nil, 0); + } + + int i; + for( i = 0; i < nodes.Count(); i++ ) + { + if( doneBits.IsBitSet(i) ) + continue; + + if( !nodes[i].fNode ) + continue; + + if( !ICanCluster(nodes[i]) ) + continue; + + plDistribInstTab shared; + shared.Append(1, &nodes[i]); + int j; + for( j = 0; j < nodes.Count(); j++ ) + { + if( !doneBits.IsBitSet(j) && ICanCluster(nodes[i], nodes[j]) ) + { + shared.Append(1, &nodes[j]); + doneBits.SetBit(j); + } + } + INodeTab cluster; + failed = !IClusterGroup(shared, cluster, bar); + if( cluster.Count() ) + { + int j; + for( j = 0; j < cluster.Count(); j++ ) + { + fade.Append(1, &shared[0].fFade); + bone.Append(1, &shared[0].fBone); + boneIsParent.SetBit(doneNodes.Count() + j, shared[0].fRigid); + + // Attach every component on the template node to the new node + int k; + plMaxNode *maxNode = (plMaxNode*)nodes[i].fNode; + for (k = 0; k < maxNode->NumAttachedComponents(); k++) + { + plComponentBase *comp = maxNode->GetAttachedComponent(k); + comp->AddTarget((plMaxNode*)cluster[j]); + } + } + + doneNodes.Append(cluster.Count(), &cluster[0]); + + } + if( failed ) + break; + } + + } + + IClearNodeTab(); + + IClearDistribTab(); + + IFinishDoneNodes(doneNodes, fade, bone, boneIsParent); + + if( failed ) + Clear(); + else + Select(); + + GetCOREInterface()->RedrawViews(GetCOREInterface()->GetTime(), REDRAW_END); + + return failed; +} + +void plClusterComponent::IFinishDoneNodes(INodeTab& doneNodes, plBox3Tab& fade, INodeTab& bones, hsBitVector& boneIsParent) +{ + if( !doneNodes.Count() ) + return; + + NameMaker *nn = GetCOREInterface()->NewNameMaker(); + + TSTR nodeName(GetINode()->GetName()); + int i; + for( i = 0; i < doneNodes.Count(); i++ ) + { + if( doneNodes[i] ) + { + nn->MakeUniqueName(nodeName); + doneNodes[i]->SetName(nodeName); + + ISetLocation((plMaxNode*)doneNodes[i]); + } + Point3* p3p; + p3p = &fade[i].pmin; + fCompPB->Append(kFadeIns, 1, &p3p); + p3p = &fade[i].pmax; + fCompPB->Append(kFadeOuts, 1, &p3p); + + INode* nilNode = nil; + if( bones[i] ) + { + if( boneIsParent.IsBitSet(i) ) + { + + bones[i]->AttachChild(doneNodes[i], true); + fCompPB->Append(kWindBones, 1, &nilNode); + } + else + { + fCompPB->Append(kWindBones, 1, &bones[i]); + } + } + else + { + fCompPB->Append(kWindBones, 1, &nilNode); + } + } + + // Add doneNodes to our PB, so we can keep track of who we've created. + fCompPB->Append(kClusters, doneNodes.Count(), &doneNodes[0]); +} + +void plClusterComponent::ISetupRenderDependencies() +{ + hsRadixSort::Elem* listTrav; + hsTArray scratchList; + + int numClust = fCompPB->Count(kClusters); + + if( !numClust ) + return; + + scratchList.SetCount(numClust); + + int i; + for( i = 0; i < numClust; i++ ) + { + listTrav = &scratchList[i]; + listTrav->fBody = (void*)i; + listTrav->fNext = listTrav+1; + + Point3 fadeMax = fCompPB->GetPoint3(kFadeOuts, TimeValue(0), i); + listTrav->fKey.fFloat = fadeMax[2] > 0 ? -fadeMax[0] : -1.e33f; // Negate the distance to get decreasing sort. + } + listTrav->fNext = nil; + + hsRadixSort rad; + hsRadixSort::Elem* sortedList = rad.Sort(scratchList.AcquireArray(), hsRadixSort::kFloat); + + hsRadixSort::Elem* prevStart = nil; + hsRadixSort::Elem* prevEnd = nil; + + listTrav = sortedList; + + float currFade = listTrav->fKey.fFloat; + listTrav = listTrav->fNext; + + while( listTrav ) + { + if( listTrav->fKey.fFloat != currFade ) + { + IAssignRenderDependencies(prevStart, prevEnd, sortedList, listTrav); + + currFade = listTrav->fKey.fFloat; + } + + listTrav = listTrav->fNext; + } + IAssignRenderDependencies(prevStart, prevEnd, sortedList, listTrav); + + + // Sort them by fade.Min()[2] < 0 ? fade.Min()[0] : 0 in decreasing order + // make first sort value group render dependent on all targets + // for each remaining sort value group + // make render dependent on members of previous sort value group. +} + +void plClusterComponent::IAssignRenderDependencies(hsRadixSortElem*& prevStart, hsRadixSortElem*& prevEnd, + hsRadixSortElem*& currStart, hsRadixSortElem*& currEnd) +{ + if( !prevStart ) + { + hsRadixSort::Elem* q; + for( q = currStart; q != currEnd; q = q->fNext ) + { + int iNode = (int)q->fBody; + plMaxNodeBase* clust = (plMaxNodeBase*)fCompPB->GetINode(kClusters, TimeValue(0), iNode); + + if( clust ) + { + int i; + for( i = 0; i < NumTargets(); i++ ) + { + plMaxNodeBase* targ = GetTarget(i); + if( targ ) + clust->AddRenderDependency(targ); + } + } + } + } + else + { + hsRadixSort::Elem* q; + for( q = currStart; q != currEnd; q = q->fNext ) + { + int iNode = (int)q->fBody; + plMaxNodeBase* clust = (plMaxNodeBase*)fCompPB->GetINode(kClusters, TimeValue(0), iNode); + + if( clust ) + { +#if 0 + hsRadixSort::Elem* p; + for( p = prevStart; p != prevEnd; p = p->fNext ) + { + iNode = (int)p->fBody; + plMaxNodeBase* targ = (plMaxNodeBase*)fCompPB->GetINode(kClusters, TimeValue(0), iNode); + clust->AddRenderDependency(targ); + } +#else + iNode = (int)prevStart->fBody; + plMaxNodeBase* targ = (plMaxNodeBase*)fCompPB->GetINode(kClusters, TimeValue(0), iNode); + clust->AddRenderDependency(targ); +#endif + } + } + } + prevStart = currStart; + prevEnd = currEnd; + currStart = currEnd; +} + +BOOL plClusterComponent::ICanCluster(plDistribInstance& node) +{ + if( !node.fNode ) + return false; + + return true; +} + +BOOL plClusterComponent::ICanCluster(plDistribInstance& node0, plDistribInstance& node1) +{ + if( !(node0.fNode && node1.fNode) ) + return false; + + if( !ICanCluster(node1) ) + return false; + + if( node0.fNode->GetMtl() != node1.fNode->GetMtl() ) + return false; + + if( (node0.fFade.Min() != node1.fFade.Min()) + ||(node0.fFade.Max() != node1.fFade.Max()) ) + return false; + + if( node0.fBone != node1.fBone ) + return false; + + return true; +} + +Box3 plClusterComponent::IPartition(plDistribInstTab& nodes) +{ + if( !nodes.Count() ) + return Box3(); + + Box3 retVal; + int i; + for( i = 0; i < nodes.Count(); i++ ) + { + retVal += nodes[i].fNodeTM.GetTrans(); + } + Point3 mins = retVal.Min(); + Point3 maxs = retVal.Max(); + +// mins += Point3(fClusterSize, fClusterSize, fClusterSize) * 0.5f; +// maxs -= Point3(fClusterSize, fClusterSize, fClusterSize) * 1.5f; +// maxs -= Point3(fClusterSize, fClusterSize, fClusterSize) * 1.0f; + + for( i = 0; i < 3; i++ ) + { + if( mins[i] >= maxs[i] ) + { + float mid = (mins[i] + maxs[i]) * 0.5f; + mins[i] = mid - 1.f; + maxs[i] = mid + 1.f; + } + } + retVal = Box3(mins, maxs); + + return retVal; +} + +void plClusterComponent::IClusterBins(plDistribInstTab& nodes, Box3& box) +{ + int i; + for( i = 0; i < 3; i++ ) + { + fSizes[i] = int((box.Max()[i] - box.Min()[i]) / fClusterSize); + if( !fSizes[i] ) + fSizes[i] = 1; + } + + int totSize = IGetBinCount(); + fClusterBins = TRACKED_NEW plDistribInstTab*[totSize]; + + memset(fClusterBins, 0, sizeof(*fClusterBins) * totSize); + + for( i = 0; i < nodes.Count(); i++ ) + { + Matrix3 l2w = nodes[i].fNodeTM; + Point3 loc = l2w.GetTrans(); + + plDistribInstTab* bin = IGetClusterBin(box, loc); + bin->Append(1, &nodes[i]); + } +} + +int plClusterComponent::IGetBinCount() +{ + return fSizes[0] * fSizes[1] * fSizes[2]; +} + +void plClusterComponent::IDeleteClusterBins() +{ + int totSize = IGetBinCount(); + int i; + for( i = 0; i < totSize; i++ ) + delete fClusterBins[i]; + delete [] fClusterBins; + fClusterBins = nil; +} + +plDistribInstTab* plClusterComponent::IGetClusterBin(const Box3& box, const Point3& loc) +{ + int coord[3]; + int j; + for( j = 0; j < 3; j++ ) + { + coord[j] = int((loc[j] - box.Min()[j]) / fClusterSize); + if( coord[j] < 0 ) + coord[j] = 0; + else if( coord[j] >= fSizes[j] ) + coord[j] = fSizes[j] - 1; + } + int idx = coord[0] * fSizes[1] * fSizes[2] + coord[1] * fSizes[2] + coord[2]; + if( !fClusterBins[idx] ) + fClusterBins[idx] = TRACKED_NEW plDistribInstTab; + return fClusterBins[idx]; +} + +BOOL plClusterComponent::IClusterGroup(plDistribInstTab& nodes, INodeTab& clusters, plExportProgressBar& bar) +{ + BOOL retVal = true; + + hsBitVector doneNodes; + + const float kNoOptClusterSize = 100.f; + const float kOptClusterSize = 100.f; // 30.f? + const int kNoOptMaxFaces = 10000; + const int kOptMaxFaces = 200; + + float optim = fCompPB->GetFloat(kOptimization) * 0.01f; + + float minClusterSize = kNoOptClusterSize + optim * (kOptClusterSize - kNoOptClusterSize); + int maxFaces = kNoOptMaxFaces + int(optim * float(kOptMaxFaces - kNoOptMaxFaces)); + + fClusterSize = minClusterSize; + Box3 fade = nodes[0].fFade; + if( fade.Min().z < 0 ) + { + fClusterSize = fade.Min().x; + } + else if( fade.Max().z < 0 ) + { + fClusterSize = fade.Max().x; + } + if( fClusterSize < minClusterSize ) + fClusterSize = minClusterSize; + + Box3 box = IPartition(nodes); + + IClusterBins(nodes, box); + + int totSize = IGetBinCount(); + int i; + for( i = 0; i < totSize; i++ ) + { + if( fClusterBins[i] ) + { + INode* grp = IMakeOne(*fClusterBins[i]); + if( grp ) + { + INodeTab subGrp; + plDicer dicer; + dicer.SetMaxFaces(maxFaces); + dicer.Dice(grp, subGrp); + int j; + for( j = 0; j < subGrp.Count(); j++ ) + { + clusters.Append(1, &subGrp[j]); + } + } + + if( bar.Update(nil, fClusterBins[i]->Count()) ) + { + retVal = false; + break; + } + } + } + IDeleteClusterBins(); + + return retVal; +} + +INode* plClusterComponent::IMakeOne(plDistribInstTab& nodes) +{ + if( !nodes.Count() ) + return nil; + + TriObject* triObj = CreateNewTriObject(); + Mesh* outMesh = &triObj->mesh; + + *outMesh = *nodes[0].fMesh; + + INode *outNode = GetCOREInterface()->CreateObjectNode(triObj); + + Matrix3 l2w = nodes[0].fObjectTM; + Matrix3 w2l = Inverse(l2w); + + MeshDelta meshDelta(*outMesh); + + int i; + for( i = 1; i < nodes.Count(); i++ ) + { + Mesh nextMesh(*nodes[i].fMesh); + + Matrix3 relativeTransform = nodes[i].fObjectTM * w2l; + + // If we've stashed normals on this mesh, they are in the mesh's + // native local space. The transform of the positions is handled + // automatically by meshDelta.AttachMesh (hence passing in the matrix), + // but the meshDelta hasn't a clue that the normal map channel isn't + // just more UVs. No problem, I'll handle it myself. + if( nextMesh.mapVerts(plDistributor::kNormMapChan) ) + { + Point3* norms = nextMesh.mapVerts(plDistributor::kNormMapChan); + int k; + for( k = 0; k < nextMesh.getNumMapVerts(plDistributor::kNormMapChan); k++ ) + { + norms[k] = relativeTransform.VectorTransform(norms[k]); + } + } + + IRandomizeSkinWeights(&nextMesh, nodes[i].fFlex); + + meshDelta.AttachMesh(*outMesh, nextMesh, relativeTransform, 0); + + meshDelta.Apply(*outMesh); + + } + + outNode->SetNodeTM(TimeValue(0), l2w); + outNode->CopyProperties(nodes[0].fNode); + outNode->SetMtl(nodes[0].fNode->GetMtl()); + outNode->SetObjOffsetPos(Point3(0,0,0)); + Quat identQuat; + identQuat.Identity(); + outNode->SetObjOffsetRot(identQuat); + outNode->SetObjOffsetScale(ScaleValue(Point3(1.f, 1.f, 1.f))); + + outNode->Hide(false); + + return outNode; +} + +BOOL plClusterComponent::IGetLocation() +{ + fLocationComp = nil; + int numTarg = NumTargets(); + int i; + for( i = 0; i < numTarg; i++ ) + { + plMaxNodeBase* targ = GetTarget(i); + if( targ ) + { + UInt32 numComp = targ->NumAttachedComponents(false); + int j; + for( j = 0; j < numComp; j++ ) + { + plComponentBase* comp = targ->GetAttachedComponent(j, false); + if( comp && (comp->ClassID() == ROOM_CID || comp->ClassID() == PAGEINFO_CID) ) + { + if( fLocationComp && (fLocationComp != comp) ) + { + fLocationComp = nil; + return false; + } + fLocationComp = comp; + } + } + } + } + return fLocationComp != nil; +} + +void plClusterComponent::ISetLocation(plMaxNode* node) +{ + if( fLocationComp ) + fLocationComp->AddTarget(node); +} + +void plClusterComponent::IRandomizeSkinWeights(Mesh* mesh, const Point3& flex) const +{ + const int iWgtMap = plDistributor::kWgtMapChan; + + UVVert *wgtMap = mesh->mapVerts(iWgtMap); + int numWgtVerts = mesh->getNumMapVerts(iWgtMap); + + static plRandom random; + + float interMeshRandomNess = flex[1]; + float intraMeshRandomNess = flex[2]; + + float r = interMeshRandomNess > 0 ? random.RandRangeF(1.f - interMeshRandomNess, 1.f) : 1.f; + int i; + for( i = 0; i < numWgtVerts; i++ ) + { + UVVert uvw = wgtMap[i]; + float s = r; + if( intraMeshRandomNess > 0 ) + s *= random.RandRangeF(1.f - intraMeshRandomNess, 1.f); + uvw *= s; + mesh->setMapVert(iWgtMap, i, uvw); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClusterComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClusterComponent.h new file mode 100644 index 00000000..5b05bc47 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plClusterComponent.h @@ -0,0 +1,132 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plClusterComponent_inc +#define plClusterComponent_inc + +const Class_ID CLUSTER_COMP_CID(0x508a10f4, 0x70112d59); + +class hsRadixSortElem; +class plExportProgressBar; +class hsBitVector; +class plDistribInstTab; +class plMaxNodeTab; +class hsBitVector; +class plDistribComponent; +class plDistribInstance; +class plClusterGroup; + +#include +using namespace std; + +class plDistribCompTab : public Tab +{ +}; + +class plBox3Tab : public Tab +{ +}; + +class plClusterComponent : public plComponent +{ +public: + enum { + kClusters = 0, + kOptimization, + kFadeIns, + kFadeOuts, + kWindBone, // Obsolete, moved to DistribComponent + kWindBones, + kAutoGen, + kAutoInstance + }; +protected: + float fClusterSize; + + // These are temp, only used during processing. + plDistribInstTab** fClusterBins; + int fSizes[3]; + plDistribCompTab fDistribTab; + plComponentBase* fLocationComp; + + vector fClusterGroups; + + // And more temps used only during Convert + BOOL fSetupDone; + BOOL fAutoGen; + BOOL fExported; + + void ICheckWindBone(); + + BOOL IGetLocation(); + void ISetLocation(plMaxNode* node); + + void ISetupRenderDependencies(); + void IAssignRenderDependencies(hsRadixSortElem*& prevStart, hsRadixSortElem*& prevEnd, + hsRadixSortElem*& currStart, hsRadixSortElem*& currEnd); + + BOOL IBuildNodeTab(plDistribInstTab& nodes, plErrorMsg* pErrMsg, plExportProgressBar& bar); + void IClearNodeTab(); + void IBuildDistribTab(); + void IClearDistribTab(); + + BOOL IsFlexible() const; + void IRandomizeSkinWeights(Mesh* mesh, const Point3& flex) const; + void IFinishDoneNodes(INodeTab& doneNodes, plBox3Tab& fade, INodeTab& bone, hsBitVector& boneIsParent); + BOOL ICanCluster(plDistribInstance& node); + BOOL ICanCluster(plDistribInstance& node0, plDistribInstance& node1); + BOOL IClusterGroup(plDistribInstTab& nodes, INodeTab& clusters, plExportProgressBar& bar); + INode* IMakeOne(plDistribInstTab& nodes); + Box3 IPartition(plDistribInstTab& nodes); + + void IClusterBins(plDistribInstTab& nodes, Box3& box); + int IGetBinCount(); + void IDeleteClusterBins(); + plDistribInstTab* IGetClusterBin(const Box3& box, const Point3& loc); + +public: + plClusterComponent(); + void DeleteThis() { delete this; } + + void Clear(); + BOOL Cluster(plErrorMsg* pErrMsg); + void Select(); + + BOOL AutoGen(plErrorMsg* pErrMsg); + void AutoClear(plErrorMsg* pErrMsg); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + int GetNumGroups() { if (fSetupDone) return fClusterGroups.size(); return 0; } + plClusterGroup *GetGroup(int index) { if (fSetupDone) return fClusterGroups[index]; return nil; } + +}; + +#endif // plClusterComponent_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.cpp new file mode 100644 index 00000000..de9c4db0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.cpp @@ -0,0 +1,31 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plComponent.h" +#include "plComponentReg.h" // Needed for the kTargs enum + +#include "../MaxMain/plMaxNode.h" + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.h new file mode 100644 index 00000000..3f210364 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.h @@ -0,0 +1,246 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_COMPONENT_H +#define PL_COMPONENT_H + +/************************************************************************************************ +** +** Information regarding the development of Components for Plasma 2.0 +** +** Date: 3/2/2001 +** Version: Plasma 2.00 +** Needs: A working knowledge of max sdk (Specifically the ParamBlocks...) +** A working knowledge of C++ +** +** +** As the components are heavily reliant on the use of the MAX paramblock, the following methodology +** should be used in their implementation: +** +** In order for the paramblock to not be thrown away during linking, we have started the use of a +** dummy function such as: +** +** void DummyCodeIncludeStartPointFunc() +** { +** } +** +** This is called within the code for the GlobalUtility function, currently. +** +** Next create a derived class from the plComponent class with three public functions, +** being its constunctor, its custom destructor and the converter. Currently, we have the +** converter returning an hsBool to let you know how successful the conversion process has been +** during export to the .prd format. +** +** After the class above has been declared, the param block stuff follows: +** +** (Note all the imformation specifics regarding how to develop the paramblocks can be found +** in the help included in the maxsdk.) +** +** CLASS_DESC is a macro that Max has created that helps stuff your data into a param block. It +** takes in the following formal parameters: +** +** CLASS_DESC(plStartingPointComponent, gStartPtDesc, "StartingPoint", "StartingPointComp", Class_ID(0x2a127b68, 0xdc7367a)) +** +** 1st parameter : The C++ class that is going to be accessing this function (assumed to be passed by reference) +** 2nd parameter : The Instance of the class descriptor that will be used specifically for +** the following param block. It is type int. +** 3rd parameter : The Short name description string for this paramblock. +** 4th parameter : The long name description string for this paramblock. +** 5th parameter : The unique CLASS_ID created via the included GEN_ID.exe that comes with maxsdk. +** +** Following this macro, an enum array is useful to store the relatively unique ID constants for the paramblock. +** +** The format of the definitions in this shown below, but basically is in the form of a sequence of fixed specs followed by a variable number of tagged optional specs for each parameter. +** +** +** Next, the actual param block is declared such as the following: +** +** ParamBlockDesc2 gStartPtBk +** ( +** 1, _T(""), 0, &gStartPtDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, 0, +** +** //rollout +** IDD_COMP_STARTINGPOINT, IDS_COMP_STARTINGPOINTS, 0, 0, NULL, +** +** // params +** kPlayerStartingPoint, _T("Start Point:"), TYPE_STRING, 0, 0, +** p_ui, TYPE_EDITBOX, IDC_COMP_STARTPOINT, +** end, +** +** end +** ); +** +** First is the name of the descriptor method that is to be used, in this case the ParamBlockDesc2 +** type. Second is the unique name of this this paramblock. Following, a collection of comma delimited +** parameters ensue. The exact details as well as other flags to use herein can be found in the MaxR4 sdk +** Help file. We shall explain the above parameters to help give you an idea of the layout. +** All paramblocks have the following format: +** +** 1. Required block specs & per-block flags, followed by, +** 2. Optional owning object reference number for the block if auto-construct, followed by, +** 3. Optional parameter map specs if auto-ui, followed by, +** 4. Zero or more parameter specs, comprising: +** a. Required parameter specs, followed by, +** b. Zero or more optional parameter specs, each with is own leading tag, +** the list terminated by an 'end' tag, followed by, +** 5. an 'end' tag +** +** THE BLOCK SPECS: +** +** BlockParameter 1: +** "1" is the permanent block ID for the parameter block2. It must be unique +** to the block. Considering that you can have several blocks stored together in the same window, +** you may want to enum this as well. THIS IS A REQUIRED FIELD. +** +** BlockParameter 2: +** The internal name string. This name is not localized. Internal names are meant +** to be parsable as identifiers. As such they should begin with an alpha character, have only +** alphanumerics, and have no spaces, punctuations, etc. The convention for multi-word names +** is to use studly-caps, eg, paintRadius. THIS IS A REQUIRED FIELD. +** +** BlockParameter 3: +** The resource ID of the localized (sub-anim) name string. It is of type int. THIS IS +** A REQUIRED FIELD. +** +** BlockParameter 4: +** Points to the class descriptor2 of the owning class. This is used to add this +** descriptor to the ClassDesc2's table of block descriptors for the class. Note: This value may +** be passed as NULL for situations where the blocks owning ClassDesc2 is not available for static +** initializations (such as in a separate file). Before using the descriptor for any block +** construction, the ClassDesc2* must be initialized with the method: +** +** void ParamBlockDesc2::SetClassDesc(ClassDesc2* cd); +** +** You can only call this method once on a descriptor and then only if it has been +** constructed initially with a NULL cd. +** +** BlockParameter 5: One or more BYTE flags. In this instance, we are only using: +** +** P_AUTO_CONSTRUCT +** Indicates the parameter block2 will be constructed and referenced automatically +** to its owner in the call to ClassDesc2::MakeAutoParamBlocks(). If this flag is +** set, the parameter block's reference number in the owning object should be given +** immediately following the flag word in the descriptor constructor. See +** . +** +** P_AUTO_UI +** Indicates this block supports automatic UI rollout management in calls to +** ClassDesc2::BeginEditParams(), ClassDesc2::EndEditParams(),ClassDesc2::CreateParamDlg(), +** ClassDesc2::CreateParamDialog(), etc. +** +*8 The must be supplied in the descriptor constructor. +** +** BlockParameter 6: Because we used the AUTO_UI the following parameters are REQUIRED. +** +** int dialog_template_ID, +** (in this case IDD_COMP_something from an .rc file) +** int dialog_title_res_ID, +** (in this case IDS_COMP_something from the String Table) +** int flag_mask, +** (This is used by ClassDesc2::BeginEditParams() and ClassDesc2::EndEditParams() +** to determine whether the ParamMap2 shold be created/deleted on this call. +** All the bits in the supplied mask must be on in the Begin/EndEditParams +** flag longword for the action to take place. For this example we have 0.) +** int rollup_flags +** (This flag is used to control rollup creation. You may pass +** APPENDROLL_CLOSED to have the rollup added in the closed (rolled up) state. +** Otherwise pass 0.) +** ParamMap2UserDlgProc* proc +** (If there are controls in the dialog that require special processing this user +** If not used then NULL should be passed.) +** +** THE PARAM SPECS: +** +** Param Parameter 1: ParamID id +** (The permanent, position-independent ID for the parameter. In this case, it is +** the constant kStartingPoint.) +** Param Parameter 2: TCHAR* internal_name +** (The internal name for the parameter. In our case "Start Point:". This is what is written on the +** button, spinner, whizbang, foozle, etc.) +** Param Parameter 3: ParamType type +** (The type of parameter. See List of ParamType Choices. In this case it is TYPE_STRING. It could be +** TYPE_INT, TYPE_FLOAT, etc, etc, etc.) +** Param Parameter 4: [int table_size] +** (If the type is one of the Tab<> types, you need to supply an initial table size +** which can be 0. Which it is in our case above.) +** Param Parameter 5: int flags +** (These flags are ORed together. We don't use any, hence 0. They include useful things such as +** p_SubAnim which allow track view acess...) +** Param Parameter 6: int local_name_res_ID +** (Here lies that relatively unique enum mentioned above. Hence, the kStartingPoint.) +** +** OPTIONAL STUFF: +** +** As param blocks don't necessarily need a visual GUI, any that you include are optional. +** +** p_ui, +** (This declares that a GUI will be instantiated. The parameter hereafter will declare the type.) +** GUI_Type +** (This is the type of GUI that you are going to use. In our case it is a CUSTOM editbox. +** +** NOTE: MAX prefers that you create CUSTOM instances of all GUI resources (such as spinners, +** editboxes, etc) +** ) +** +** ID +** (This is the implementor defined name that this GUI is using. It is the name of the object that +** you created a resource of. We created an IDC_COMP_STARTINGPOINT which was of a CustEdit type.) +** +** end +** (This lets the constructor that makes uses to know that you have finished one of the p_ui param +** blocks.) +** +** +** FINAL REQUISITE STUFF: +** +** end +** ); +** (This is the conclusion of the Block descriptor. Sort of intuitive...) +** +*************************************************************************************************/ + +#include "plComponentBase.h" +#include "../MaxExport/plErrorMsg.h" + + +class plMaxNode; +class plErrorMsg; + +class plComponent : public plComponentBase +{ +public: + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) = 0; + + // DeInit pass--free up any temp memory you might have allocated here + virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.rc new file mode 100644 index 00000000..b4fdcf86 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponent.rc @@ -0,0 +1,7332 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_COMP_ANIM DIALOG 0, 0, 108, 158 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Auto Start",IDC_COMP_ANIM_AUTOSTART_CKBX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,6,6,47,10 + CONTROL "Loop:",IDC_COMP_ANIM_LOOP_CKBX,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,6,47,34,10 + COMBOBOX IDC_ANIM_NAMES,6,29,95,52,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_LOOP_NAMES,6,60,95,54,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + LTEXT "Name:",IDC_STATIC,6,19,22,8 + CONTROL "Animate on Global:",IDC_COMP_ANIM_USE_GLOBAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,6,92,75,10 + COMBOBOX IDC_ANIM_GLOBAL_LIST,6,106,95,54,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "Object Can Move Others",IDC_COMP_ANIM_PHYSANIM,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,6,133,94,10 + LTEXT "(Physicals Only)",IDC_STATIC,25,144,54,8 +END + +IDD_COMP_ACTIVE DIALOG 0, 0, 108, 87 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Type:",IDC_STATIC,4,3,19,8 + COMBOBOX IDC_DET_COMBO,4,13,98,66,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Only Once",IDC_ONESHOT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,5,32,49,10 + LISTBOX IDC_RESPONDERS,4,56,98,27,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + LTEXT "Responders:",IDC_STATIC,4,46,41,8 +END + +IDD_COMP_ALIAS DIALOG 0, 0, 108, 34 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Player Name:",IDC_STATIC,16,5,43,8 + CONTROL "PlayerEditB",IDC_COMP_ALIAS,"CustEdit",WS_TABSTOP,16,16, + 75,10 +END + +IDD_COMP_PLAYERSTART DIALOG 0, 0, 108, 32 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Player Start:",IDC_STATIC,15,5,39,8 + CONTROL "Ignore",IDC_PLAYERSTART,"CustEdit",WS_DISABLED | + WS_TABSTOP,15,16,77,10 +END + +IDD_COMP_STDPORTAL DIALOG 0, 0, 108, 37 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Room 1:",IDC_STATIC,4,7,28,8 + CONTROL "Room1 Name",IDC_COMP_PORTAL_ROOM1,"CustEdit",WS_TABSTOP, + 34,5,68,12 + LTEXT "Room 2:",IDC_STATIC,4,23,28,8 + CONTROL "Room2 Name",IDC_COMP_PORTAL_ROOM2,"CustEdit",WS_TABSTOP, + 34,20,68,12 +END + +IDD_COMP_TARGS DIALOG 0, 0, 108, 59 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST_TARGS,4,4,100,35,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + CONTROL "Pick",IDC_ADD_TARGS,"CustButton",WS_TABSTOP,4,41,49,14 + CONTROL "Delete",IDC_DELETE_TARGS,"CustButton",WS_TABSTOP,55,41, + 49,14 +END + +IDD_COMP_INTEREST DIALOG 0, 0, 108, 31 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Radius of Attention:",IDC_STATIC,5,5,63,8 + LTEXT "Attention Priority:",IDC_STATIC,14,18,54,8 + CONTROL "Custom1",IDC_COMP_INTEREST_EDIT1,"CustEdit",WS_TABSTOP, + 69,4,25,10 + CONTROL "Custom1",IDC_COMP_INTEREST_EDIT2,"CustEdit",WS_TABSTOP, + 69,17,25,10 + CONTROL "Custom2",IDC_COMP_INTEREST_SPIN1,"SpinnerControl", + WS_TABSTOP,95,4,7,10 + CONTROL "Custom2",IDC_COMP_INTEREST_SPIN2,"SpinnerControl", + WS_TABSTOP,95,17,7,10 +END + +IDD_COMP_PHYSICAL DIALOG 0, 0, 108, 238 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Mass:",IDC_STATIC,17,13,20,8 + LTEXT "Bounce:",IDC_STATIC,9,26,28,8 + CONTROL "Custom1",IDC_COMP_PHYSICAL_EDIT1,"CustEdit",WS_TABSTOP, + 40,12,27,10 + CONTROL "Custom1",IDC_COMP_PHYSICAL_EDIT2,"CustEdit",WS_TABSTOP, + 40,25,27,10 + CONTROL "Custom2",IDC_COMP_PHYSICAL_SPIN1,"SpinnerControl", + WS_TABSTOP,68,12,7,10 + CONTROL "Custom2",IDC_COMP_PHYSICAL_SPIN2,"SpinnerControl", + WS_TABSTOP,68,25,7,10 + LTEXT "Friction:",IDC_STATIC,11,39,26,8 + CONTROL "Custom1",IDC_COMP_PHYSICAL_EDIT3,"CustEdit",WS_TABSTOP, + 40,38,27,10 + CONTROL "Custom2",IDC_COMP_PHYSICAL_SPIN3,"SpinnerControl", + WS_TABSTOP,68,38,7,10 + CONTROL "Custom1",IDC_COMP_PHYSICAL_P3SF_EDIT1,"CustEdit", + WS_TABSTOP,17,100,28,10 + CONTROL "Custom1",IDC_COMP_PHYSICAL_P3SF_EDIT2,"CustEdit", + WS_TABSTOP,17,112,28,10 + CONTROL "Custom2",IDC_COMP_PHYSICAL_P3SF_SPIN1,"SpinnerControl", + WS_TABSTOP,46,100,7,10 + CONTROL "Custom2",IDC_COMP_PHYSICAL_P3SF_SPIN2,"SpinnerControl", + WS_TABSTOP,46,112,7,10 + CONTROL "Custom1",IDC_COMP_PHYSICAL_P3SF_EDIT3,"CustEdit", + WS_TABSTOP,17,124,28,10 + CONTROL "Custom2",IDC_COMP_PHYSICAL_P3SF_SPIN3,"SpinnerControl", + WS_TABSTOP,46,124,7,10 + CONTROL "Custom1",IDC_COMP_PHYSICAL_P3ST_EDIT1,"CustEdit", + WS_TABSTOP,62,100,28,10 + CONTROL "Custom1",IDC_COMP_PHYSICAL_P3ST_EDIT2,"CustEdit", + WS_TABSTOP,62,112,28,10 + CONTROL "Custom2",IDC_COMP_PHYSICAL_P3ST_SPIN1,"SpinnerControl", + WS_TABSTOP,91,100,7,10 + CONTROL "Custom2",IDC_COMP_PHYSICAL_P3ST_SPIN2,"SpinnerControl", + WS_TABSTOP,91,112,7,10 + CONTROL "Custom1",IDC_COMP_PHYSICAL_P3ST_EDIT3,"CustEdit", + WS_TABSTOP,62,124,28,10 + CONTROL "Custom2",IDC_COMP_PHYSICAL_P3ST_SPIN3,"SpinnerControl", + WS_TABSTOP,91,124,7,10 + GROUPBOX "Bounding States",IDC_STATIC,1,143,105,95 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,11,165, + 28,10 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON, + 11,155,39,10 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,11, + 175,28,10 + LTEXT "X",IDC_STATIC,8,101,8,8 + LTEXT "Y",IDC_STATIC,8,113,8,8 + LTEXT "Z",IDC_STATIC,8,124,8,8 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 11,185,34,10 + GROUPBOX "Physical Properties",IDC_STATIC,1,0,105,78 + CONTROL "Pick Me!",IDC_COMP_PHYS_PICKSTATE_BASE,"CustButton", + WS_TABSTOP,17,210,78,9 + LTEXT "Force:",IDC_STATIC,17,90,21,8 + LTEXT "Torque:",IDC_STATIC,62,90,26,8 + GROUPBOX "Starting Forces",IDC_STATIC,1,78,105,65 + CONTROL "Use Alternate Shape?",IDC_COMP_PHYS_CUSTOMCHK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,197,91,8 + CONTROL "Align Alternate Shape?",IDC_COMP_PHYS_ALIGN_SHAPE_BOOL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,222,84,13 + CONTROL "Visible?",IDC_COMP_PHYS_VISIBLE_BOOL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,51,36,11 + CONTROL "Pinned?",IDC_COMP_PHYS_PINNED_BOOL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,62,38,11 + CONTROL "LOS?",IDC_COMP_PHYS_LOS_BOOL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,55,51,37,11 +END + +IDD_COMP_AUTO DIALOG 0, 0, 108, 31 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Button1",IDC_BUTTON1,6,8,96,15,NOT WS_VISIBLE +END + +IDD_COMP_SOUND3D DIALOG 0, 0, 108, 180 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom2",IDC_COMP_SOUND3D_EDIT3,"CustEdit",WS_TABSTOP, + 43,50,46,10 + CONTROL "Custom2",IDC_COMP_SOUND3D_SPIN3,"SpinnerControl", + WS_TABSTOP,90,50,7,10 + CONTROL "Custom2",IDC_COMP_SOUND3D_EDIT4,"CustEdit",WS_TABSTOP, + 43,62,46,10 + CONTROL "Custom2",IDC_COMP_SOUND3D_SPIN4,"SpinnerControl", + WS_TABSTOP,90,62,7,10 + CONTROL "Custom1",IDC_COMP_SOUND3D_ICONEANGLE_EDIT,"CustEdit", + WS_TABSTOP,66,106,18,10 + CONTROL "Custom2",IDC_COMP_SOUND3D_ICONEANGLE_SPIN, + "SpinnerControl",WS_TABSTOP,85,106,7,10 + CONTROL "Custom1",IDC_COMP_SOUND3D_OCONEANGLE_EDIT,"CustEdit", + WS_TABSTOP,66,118,18,10 + CONTROL "Custom2",IDC_COMP_SOUND3D_OCONEANGLE_SPIN, + "SpinnerControl",WS_TABSTOP,85,118,7,10 + CONTROL "Custom1",IDC_COMP_SOUND3D_VOLSLIDER2,"SliderControl", + WS_TABSTOP,7,143,92,15 + LTEXT "Begin:",IDC_STATIC,20,50,21,8 + LTEXT "Inaudible:",IDC_STATIC,9,63,32,8 + GROUPBOX "Sound Falloff Distance",IDC_STATIC,3,38,100,38 + GROUPBOX "Sound Cone Properties",IDC_STATIC,3,79,100,97 + LTEXT "0",IDC_STATIC_SLIDER_START,7,160,8,8 + LTEXT "100%",IDC_STATIC_SLIDER_END,81,160,18,8 + LTEXT "Max Vol Outside Fade Angle",IDC_STATIC_SLIDERANGLE,8, + 135,90,8 + LTEXT "Max Vol Angle:",IDC_STATIC_VOLANGLE,15,107,48,8 + LTEXT "Fadeout Angle:",IDC_STATIC_FADEANGLE,15,119,49,8 + CONTROL "Custom2",IDC_COMP_SOUND3D_SLIDERVIEWER2,"CustEdit",NOT + WS_VISIBLE | WS_DISABLED | WS_TABSTOP,29,150,48,10 + CONTROL "Cone Effect",IDC_COMP_SOUND3D_CONEEFFECT_CKBX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,90,53,10 + CONTROL "Do not synch or trigger over the network", + IDC_SND_LOCALONLY,"Button",BS_AUTOCHECKBOX | + BS_MULTILINE | WS_TABSTOP,7,4,94,18 + CONTROL "Incidental sound",IDC_SND_INCIDENTAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,26,68,10 +END + +IDD_COMP_PHYS_TERRAIN DIALOG 0, 0, 108, 145 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Friction:",IDC_STATIC,9,16,26,8 + CONTROL "Custom1",IDC_COMP_PHYS_TERRAIN_EDIT1,"CustEdit", + WS_TABSTOP,37,15,40,10 + CONTROL "Custom2",IDC_COMP_PHYS_TERRAIN_SPIN1,"SpinnerControl", + WS_TABSTOP,79,15,7,10 + LTEXT "Bounce:",IDC_STATIC,9,29,28,8 + CONTROL "Custom1",IDC_COMP_PHYS_TERRAIN_EDIT2,"CustEdit", + WS_TABSTOP,37,29,40,10 + CONTROL "Custom2",IDC_COMP_PHYS_TERRAIN_SPIN2,"SpinnerControl", + WS_TABSTOP,79,29,7,10 + GROUPBOX "Bounding Shape",IDC_STATIC,1,48,105,96 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,11,71, + 30,10 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON, + 11,61,41,10 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,11,81, + 28,10 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 11,91,41,10 + CONTROL "Pick",IDC_COMP_PHYS_PICKSTATE_TERRAIN,"CustButton", + WS_TABSTOP,16,117,80,9 + CONTROL "Use Alternate Shape?",IDC_COMP_PHYS_CUSTOMCHK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,104,91,8 + CONTROL "Align Alternate Shape?",IDC_COMP_PHYS_ALIGN_SHAPE_BOOL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,128,84,12 + GROUPBOX "Physical Properties",IDC_STATIC,1,1,106,45 +END + +IDD_COMP_PHYS_PLAYER DIALOG 0, 0, 108, 149 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Weight:",IDC_STATIC,16,15,26,8 + LTEXT "Agility:",IDC_STATIC,21,27,21,8 + CONTROL "Custom3",IDC_COMP_PHYS_AGILITY_STATUS,"SpinnerControl", + WS_TABSTOP,83,27,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_AGILITY_SLIDER,"CustEdit", + WS_TABSTOP,44,27,38,10 + CONTROL "Custom3",IDC_COMP_PHYS_WEIGHT_SPIN1,"SpinnerControl", + WS_TABSTOP,83,14,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_WEIGHT_SLIDER,"CustEdit", + WS_TABSTOP,44,14,38,10 + GROUPBOX "Bounding Shape",IDC_STATIC,3,49,103,99 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON, + 10,60,39,10 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,10,70, + 28,10 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,10,80, + 28,10 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 10,90,34,10 + GROUPBOX "Properties",IDC_STATIC,3,1,101,44 + CONTROL "Pick",IDC_COMP_PHYS_PICKSTATE_PLAYER,"CustButton", + WS_TABSTOP,10,120,88,9 + CONTROL "Use Alternate Shape?",IDC_COMP_PHYS_CUSTOMCHK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,105,85,8 + CONTROL "Align Alternate Shape?",IDC_COMP_PHYS_ALIGN_SHAPE_BOOL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,132,85,12 +END + +IDD_COMP_CAMERA DIALOG 0, 0, 108, 39 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Camera Type:",IDC_STATIC,2,1,102,35 + CONTROL "Fixed Camera",IDC_RADIO_FIXEDCAM,"Button", + BS_AUTORADIOBUTTON,8,12,59,10 + CONTROL "Fixed Pan Camera",IDC_RADIO_FIXEDPANCAM,"Button", + BS_AUTORADIOBUTTON,8,23,73,10 +END + +IDD_COMP_PHYS_SIMPLE DIALOG 0, 0, 107, 214 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Mass:",IDC_STATIC,12,12,20,10 + LTEXT "Bounce:",IDC_STATIC,4,28,27,10 + LTEXT "Friction:",IDC_STATIC,5,45,25,10 + CONTROL "Custom2",IDC_COMP_PHYS_SIMP_SPIN1,"SpinnerControl", + WS_TABSTOP,89,12,7,10 + CONTROL "Custom1",IDC_COMP_PHYS_SIMP_EDIT1,"CustEdit",WS_TABSTOP, + 32,12,56,10 + CONTROL "Custom1",IDC_COMP_PHYS_SIMP_EDIT2,"CustEdit",WS_TABSTOP, + 32,28,56,10 + CONTROL "Custom1",IDC_COMP_PHYS_SIMP_EDIT3,"CustEdit",WS_TABSTOP, + 32,44,56,10 + CONTROL "Custom2",IDC_COMP_PHYS_SIMP_SPIN2,"SpinnerControl", + WS_TABSTOP,89,28,7,10 + CONTROL "Custom2",IDC_COMP_PHYS_SIMP_SPIN3,"SpinnerControl", + WS_TABSTOP,89,44,7,10 + GROUPBOX "Bounding Shape",IDC_STATIC,3,64,104,105 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,9,92,34, + 10 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON,9, + 80,42,12 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,9,104, + 29,10 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 9,116,42,10 + GROUPBOX "Physical Properties",IDC_STATIC,3,0,104,62 + CONTROL "Pick",IDC_COMP_PHYS_PICKSTATE_SIMP,"CustButton", + WS_TABSTOP,14,143,86,9 + CONTROL "Use Alternate Shape?",IDC_COMP_PHYS_CUSTOMCHK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,130,92,8 + CONTROL "Align Alternate Shape?",IDC_COMP_PHYS_ALIGN_SHAPE_BOOL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,155,86,12 + CONTROL "Don't Synchronize",IDC_PH_NO_SYNC_CHK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,173,95,13 + CONTROL "Start Inactive",IDC_PH_INACTIVE_CHK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,185,99,13 + CONTROL "Use Avatar Push Anims",IDC_PH_AVANIMPUSHABLE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,197,99,13 +END + +IDD_COMP_PHYS_DETECTOR DIALOG 0, 0, 108, 83 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Bounding Shape",IDC_STATIC,2,1,104,81 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON, + 21,12,39,10 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,21,23, + 28,10 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,21,34, + 28,10 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 21,44,34,10 + CONTROL "Pick",IDC_COMP_PHYS_PICKSTATE_DETECTOR,"CustButton", + WS_TABSTOP,15,69,83,9 + CONTROL "Use Alternative Shape?",IDC_COMP_PHYS_CUSTOMCHK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,57,88,8 + CONTROL "Custom1",IDC_CUSTOM1,"",WS_TABSTOP,29,66,50,14 +END + +IDD_COMP_ANIM_AVATAR DIALOG 0, 0, 108, 41 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Shareable",IDC_COMP_ANIM_AVATAR_SHAREBOOL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,9,90,10 + CONTROL "Global",IDC_COMP_ANIM_AVATAR_GLOBALBOOL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,22,90,10 +END + +IDD_COMP_SOUND_REVERB DIALOG 0, 0, 108, 83 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Box",IDC_COMP_SOUND_REVERB_BBOX,"Button", + BS_AUTORADIOBUTTON,17,15,28,10 + CONTROL "Sphere",IDC_COMP_SOUND_REVERB_BSPHERE,"Button", + BS_AUTORADIOBUTTON,17,27,39,10 + CONTROL "Hull",IDC_COMP_SOUND_REVERB_BHULL,"Button", + BS_AUTORADIOBUTTON,17,39,30,10 + COMBOBOX IDC_COMP_SOUND_REVERB_DDLIST,15,65,76,81, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Reverb Preset:",IDC_STATIC,15,55,48,8 + LTEXT "Bounding Shape:",IDC_STATIC,15,5,56,8 +END + +IDD_COMP_AVATAR DIALOGEX 0, 0, 108, 214 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + GROUPBOX "Avatar Properties",IDC_STATIC,2,0,104,209 + LTEXT "Animation Root:",IDC_STATIC,12,49,50,8 + LTEXT "Mesh:",IDC_STATIC,12,73,21,8 + CONTROL "Pick",IDC_COMP_AVATAR_ROOT_PICKB,"CustButton", + WS_TABSTOP,12,59,83,10 + CONTROL "Pick",IDC_COMP_AVATAR_MESH_PICKB,"CustButton", + WS_TABSTOP,12,82,83,10 + LTEXT "Clothing Group:",IDC_STATIC,12,96,50,8 + COMBOBOX IDC_COMP_AVATAR_CLOTHING_GROUP,12,106,83,43,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Skeleton:",IDC_STATIC,12,124,31,8 + COMBOBOX IDC_COMP_AVATAR_SKELETON,12,134,83,43,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Physical Height:",IDC_STATIC,9,17,53,8 + CONTROL "Custom1",IDC_PHYS_HEIGHT_EDIT,"CustEdit",WS_TABSTOP,62, + 16,30,10 + CONTROL "Custom2",IDC_PHYS_HEIGHT_SPIN,"SpinnerControl", + WS_TABSTOP,92,16,7,10 + LTEXT "Physical Width:",IDC_STATIC,9,30,53,8 + CONTROL "Custom1",IDC_PHYS_WIDTH_EDIT,"CustEdit",WS_TABSTOP,62, + 29,30,10 + CONTROL "Custom2",IDC_PHYS_WIDTH_SPIN,"SpinnerControl", + WS_TABSTOP,92,29,7,10 + LTEXT "Footstep Sound Page:",IDC_STATIC,12,153,72,8 + LTEXT "Animation Prefix:",IDC_STATIC,12,180,53,8 + CONTROL "",IDC_BODYFOOTSTEPPAGE_EDIT,"CustEdit",WS_TABSTOP,12, + 164,83,14 + CONTROL "",IDC_ANIMATIONPREFIX_EDIT,"CustEdit",WS_TABSTOP,12,190, + 83,14 +END + +IDD_COMP_LIGHTMAP DIALOG 0, 0, 108, 117 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "UVW Mapping Channel:",IDC_STATIC,4,7,78,8 + CONTROL "Custom1",IDC_COMP_LIGHTMAP_EDIT1,"CustEdit",WS_TABSTOP, + 84,6,9,10 + CONTROL "Custom3",IDC_COMP_LIGHTMAP_SPIN1,"SpinnerControl", + WS_TABSTOP,94,6,7,10 + GROUPBOX "Resolution Level",IDC_STATIC,3,22,100,44 + CONTROL "Custom2",IDC_COMP_LM_DUMMY,"CustEdit",NOT WS_VISIBLE | + WS_DISABLED,25,48,57,10 + CONTROL "Custom2",IDC_COMP_LIGHT_SLIDER,"SliderControl", + WS_TABSTOP,8,44,90,15 + LTEXT "Max",IDC_STATIC,85,33,16,8 + LTEXT "Min",IDC_STATIC,7,34,17,8 + LTEXT "Default",IDC_STATIC,39,33,24,8 + CONTROL "User1",IDC_COMP_LIGHTMAP_COLOR,"ColorSwatch",WS_GROUP, + 66,71,22,13 + LTEXT "Initial map color",IDC_STATIC,15,73,50,8 + CONTROL "Compress",IDC_COMP_LIGHTMAP_COMPRESS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,90,47,10 + CONTROL "Share Lightmap",IDC_COMP_LIGHTMAP_SHARED,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,103,65,10 +END + +IDD_COMP_IGNORE DIALOG 0, 0, 108, 25 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Ignore",IDC_COMP_IGNORE_CKBX,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,35,8,36,10 +END + +IDD_COMP_SOUNDMOD_CHORUS DIALOG 0, 0, 108, 146 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Wet Dry %",IDC_STATIC,25,5,34,8 + CTEXT "Delay Mod %",IDC_STATIC,17,17,42,8 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_WET_SPIN,"SpinnerControl", + WS_TABSTOP,80,4,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_WET_EDIT,"CustEdit", + WS_TABSTOP,61,4,18,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_DELAYMOD_EDIT,"CustEdit", + WS_TABSTOP,61,16,18,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_DELAYMOD_SPIN,"SpinnerControl", + WS_TABSTOP,80,16,7,10 + CTEXT "Feedback %",IDC_STATIC,19,29,40,8 + CTEXT "Low Freq. Oscillator Cutoff (Hz):",IDC_STATIC,9,46,65, + 17 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_FEEDBACK_SPIN,"SpinnerControl", + WS_TABSTOP,80,28,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_FEEDBACK_EDIT,"CustEdit", + WS_TABSTOP,61,28,18,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_LOWFREQ_OSC_CUTOFF_EDIT, + "CustEdit",WS_TABSTOP,77,49,13,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_LOWFREQ_OSC_CUTOFF_SPIN, + "SpinnerControl",WS_TABSTOP,91,49,7,10 + CTEXT "Feedback Delay (ms):",IDC_STATIC,5,71,70,8 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_FEEDBACK_DELAY_EDIT,"CustEdit", + WS_TABSTOP,77,70,14,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_FEEDBACK_DELAY_SPIN, + "SpinnerControl",WS_TABSTOP,92,70,7,10 + COMBOBOX IDC_COMP_SOUNDMOD_LOWFREQ_DDLIST,22,100,63,41, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CTEXT "Low Freq. Oscillator Cutoff (Hz):",IDC_STATIC,4,89,100, + 8 + COMBOBOX IDC_COMP_SOUNDMOD_PHASEDIF_DDLIST,22,128,63,87, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CTEXT "Phase Differential:",IDC_STATIC,24,118,58,8 +END + +IDD_COMP_SOUNDMOD_COMPRESSOR DIALOG 0, 0, 109, 96 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Gain:",IDC_STATIC,41,6,18,8 + CTEXT "Attack:",IDC_STATIC,35,20,24,8 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_GAIN_SPIN,"SpinnerControl", + WS_TABSTOP,89,5,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_GAIN_EDIT,"CustEdit", + WS_TABSTOP,63,5,25,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_ATTACK_EDIT,"CustEdit", + WS_TABSTOP,63,19,25,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_ATTACK_SPIN,"SpinnerControl", + WS_TABSTOP,89,19,7,10 + CTEXT "Release:",IDC_STATIC,30,34,29,8 + RTEXT "Compression Threshold:",IDC_STATIC,17,45,42,16 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_RELEASE_SPIN,"SpinnerControl", + WS_TABSTOP,89,33,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_RELEASE_EDIT,"CustEdit", + WS_TABSTOP,63,33,25,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_COMPTHRESH_EDIT,"CustEdit", + WS_TABSTOP,63,49,25,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_COMPTHRESH_SPIN, + "SpinnerControl",WS_TABSTOP,89,49,7,10 + RTEXT "Compression Ratio:",IDC_STATIC,19,63,40,16 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_COMPRATIO_EDIT,"CustEdit", + WS_TABSTOP,63,66,25,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_COMPRATIO_SPIN, + "SpinnerControl",WS_TABSTOP,89,66,7,10 + CTEXT "Attack Pre-Delay:",IDC_STATIC,3,81,56,8 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_ATTACKPRE_EDIT,"CustEdit", + WS_TABSTOP,63,80,25,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_ATTACKPRE_SPIN, + "SpinnerControl",WS_TABSTOP,89,80,7,10 +END + +IDD_COMP_SOUNDMOD_DISTORT DIALOG 0, 0, 109, 66 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Gain:",IDC_STATIC,44,5,18,8 + CTEXT "Intensity:",IDC_STATIC,33,17,29,8 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_GAIND_SPIN,"SpinnerControl", + WS_TABSTOP,89,4,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_GAIND_EDIT,"CustEdit", + WS_TABSTOP,64,4,23,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_INTENSE_EDIT,"CustEdit", + WS_TABSTOP,64,16,23,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_INTENSE_SPIN,"SpinnerControl", + WS_TABSTOP,89,16,7,10 + CTEXT "Effect Center Hz:",IDC_STATIC,7,29,55,8 + CTEXT "Effect Width Hz:",IDC_STATIC,9,41,53,8 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_EFFECTC_SPIN,"SpinnerControl", + WS_TABSTOP,89,28,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_EFFECTC_EDIT,"CustEdit", + WS_TABSTOP,64,28,23,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_EFFECTW_EDIT,"CustEdit", + WS_TABSTOP,64,40,23,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_EFFECTW_SPIN,"SpinnerControl", + WS_TABSTOP,89,40,7,10 + CTEXT "Low freq cutoff Hz:",IDC_STATIC,1,53,61,8 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_LOWFREQCUT_EDIT,"CustEdit", + WS_TABSTOP,64,52,23,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_LOWFREQCUT_SPIN, + "SpinnerControl",WS_TABSTOP,89,52,7,10 +END + +IDD_COMP_SOUNDMOD_ECHO DIALOG 0, 0, 108, 88 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Wet Dry %",IDC_STATIC,21,6,34,8 + CTEXT "Feedback %",IDC_STATIC,15,18,40,8 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_WET_SPIN,"SpinnerControl", + WS_TABSTOP,74,5,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_WET_EDIT,"CustEdit", + WS_TABSTOP,56,5,17,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_FEEDBACK_EDIT,"CustEdit", + WS_TABSTOP,56,17,17,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_FEEDBACK_SPIN,"SpinnerControl", + WS_TABSTOP,74,17,7,10 + CTEXT "Left:",IDC_STATIC,34,43,15,8 + CTEXT "Right:",IDC_STATIC,29,56,20,8 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_LEFTFEEDB_SPIN, + "SpinnerControl",WS_TABSTOP,76,43,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_LEFTFEEDB_EDIT,"CustEdit", + WS_TABSTOP,51,43,24,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_RIGHTFEEDB_EDIT,"CustEdit", + WS_TABSTOP,51,54,24,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_RIGHTFEEDB_SPIN, + "SpinnerControl",WS_TABSTOP,76,54,7,10 + CONTROL "Swap Left-Right",IDC_COMP_SOUNDMOD_SWAP_DELAY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,20,69,67,10 + GROUPBOX "Feedback Delay",IDC_STATIC,9,30,89,54 +END + +IDD_COMP_SOUNDMOD_FLANGER DIALOG 0, 0, 108, 239 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Delay Mod %",IDC_STATIC,13,18,42,8 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_DELAYMOD_EDIT,"CustEdit", + WS_TABSTOP,56,17,17,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_DELAYMOD_SPIN,"SpinnerControl", + WS_TABSTOP,74,17,7,10 + CTEXT "Low Freq. Oscillator Cutoff Hz:",IDC_STATIC,12,47,84,17 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_LOWFREQ_OSC_CUTOFF_EDIT, + "CustEdit",WS_TABSTOP,22,69,56,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_LOWFREQ_OSC_CUTOFF_SPIN, + "SpinnerControl",WS_TABSTOP,79,69,7,10 + CTEXT "Feedback Delay (ms):",IDC_STATIC,14,81,79,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_FEEDBACK_DELAYF_EDIT, + "CustEdit",WS_TABSTOP,22,95,56,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_FEEDBACK_DELAYF_SPIN, + "SpinnerControl",WS_TABSTOP,79,95,7,10 + COMBOBOX IDC_COMP_SOUNDMOD_LOWFREQ_DDLIST,22,133,63,41, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CTEXT "Low Freq. Oscillator Cutoff Hz:",IDC_STATIC,12,111,86, + 17 + COMBOBOX IDC_COMP_SOUNDMOD_PHASEDIF_DDLIST,23,165,63,41, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CTEXT "Phase Differential:",IDC_STATIC,16,151,79,10 + CTEXT "Wet Dry %",IDC_STATIC,21,6,34,8 + CTEXT "Feedback %",IDC_STATIC,15,30,40,8 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_WET_SPIN,"SpinnerControl", + WS_TABSTOP,74,5,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_WET_EDIT,"CustEdit", + WS_TABSTOP,56,5,17,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_FEEDBACK_EDIT,"CustEdit", + WS_TABSTOP,56,29,17,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_FEEDBACK_SPIN,"SpinnerControl", + WS_TABSTOP,74,29,7,10 +END + +IDD_COMP_SOUNDMOD_GARGLE DIALOG 0, 0, 108, 46 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Modulation Rate:",IDC_STATIC,10,5,55,8 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_MODRATE_SPIN,"SpinnerControl", + WS_TABSTOP,89,4,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_MODRATE_EDIT,"CustEdit", + WS_TABSTOP,68,4,20,10 + COMBOBOX IDC_COMP_SOUNDMOD_WAVESHAPE_DDLIST,30,28,47,41, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CTEXT "Waveform Shape:",IDC_STATIC,25,18,58,8 +END + +IDD_COMP_SOUNDMOD_REVERB DIALOG 0, 0, 108, 44 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Gain:",IDC_STATIC,31,6,18,8 + CTEXT "Mix:",IDC_STATIC,35,19,14,8 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_REVERBGAIN_SPIN, + "SpinnerControl",WS_TABSTOP,71,5,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_REVERBGAIN_EDIT,"CustEdit", + WS_TABSTOP,52,5,18,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_REVERBMIX_EDIT,"CustEdit", + WS_TABSTOP,52,18,18,10 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_REVERBMIX_SPIN, + "SpinnerControl",WS_TABSTOP,71,18,7,10 + CTEXT "Delay:",IDC_STATIC,28,32,21,8 + CONTROL "Custom2",IDC_COMP_SOUNDMOD_REVERBDELAY_SPIN, + "SpinnerControl",WS_TABSTOP,71,31,7,10 + CONTROL "Custom1",IDC_COMP_SOUNDMOD_REVERBDELAY_EDIT,"CustEdit", + WS_TABSTOP,52,31,18,10 +END + +IDD_COMP_OCCLUSION DIALOG 0, 0, 108, 27 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Two Sided",IDC_COMP_OCCLUSION_CKBX,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,28,9,50,10 +END + +IDD_COMP_PHYS_PROXY_TERRAIN DIALOG 0, 0, 108, 169 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Bounding Shape",IDC_STATIC,1,109,105,60 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON,9, + 130,39,8 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,9,142, + 28,8 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,9,154, + 28,8 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 8,119,34,8 + GROUPBOX "Physical Properties",IDC_STATIC,1,2,105,97 + CONTROL "Block Mouse Clicks",IDC_COMP_CHECK_LOS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,46,86,10 + LTEXT "Friction:",IDC_STATIC,5,16,25,10 + CONTROL "Custom1",IDC_COMP_PHYS_TERRAIN_EDIT1,"CustEdit", + WS_TABSTOP,37,16,45,10 + CONTROL "Custom2",IDC_COMP_PHYS_TERRAIN_SPIN1,"SpinnerControl", + WS_TABSTOP,84,16,7,10 + LTEXT "Bounce:",IDC_STATIC,5,30,27,10 + CONTROL "Custom1",IDC_COMP_PHYS_INVIS_BOUNCE_EDIT,"CustEdit", + WS_TABSTOP,37,30,45,10 + CONTROL "Custom2",IDC_COMP_PHYS_INVIS_BOUNCE_SPIN,"SpinnerControl", + WS_TABSTOP,84,30,7,10 + CONTROL "Physically Obstructs Camera",IDC_COMP_CHECK_LOS2,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,63,105,10 + CONTROL "Camera Avoid Box (ex)",IDC_COMP_CHECK_LOS3,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,78,104,10 +END + +IDD_COMP_SOUNDMOD_BIND DIALOG 0, 0, 108, 67 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST_TARGS,3,12,98,35,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + LTEXT "Sound Emitters:",IDC_STATIC,3,2,50,8 + PUSHBUTTON "Add...",IDC_ADD_SOUNDOBJ,3,49,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,53,49,47, + 14 +END + +IDD_COMP_PARTICLE DIALOG 0, 0, 108, 281 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Life (seconds):",IDC_STATIC,6,78,47,8 + LTEXT "min/max:",IDC_STATIC,3,88,30,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_LIFEMIN,"CustEdit", + WS_TABSTOP,35,88,23,10 + CONTROL "Custom1",IDC_COMP_PARTICLE_LIFEMAX,"CustEdit", + WS_TABSTOP,69,88,23,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_LIFEMIN_SPIN,"SpinnerControl", + WS_TABSTOP,59,88,7,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_LIFEMAX_SPIN,"SpinnerControl", + WS_TABSTOP,93,88,7,10 + GROUPBOX "Particle Properties",IDC_STATIC,2,1,103,148 + LTEXT "Cone Angle:",IDC_STATIC,6,39,40,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_CONE,"CustEdit",WS_TABSTOP, + 50,39,24,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_CONE_SPIN,"SpinnerControl", + WS_TABSTOP,75,39,7,10 + LTEXT "min (%):",IDC_STATIC,39,122,25,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_SCALEMIN,"CustEdit", + WS_TABSTOP,73,121,20,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_SCALEMIN_SPIN,"SpinnerControl", + WS_TABSTOP,93,121,7,10 + LTEXT "Velocity (feet/sec):",IDC_STATIC,6,52,60,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_VELMIN,"CustEdit",WS_TABSTOP, + 35,62,23,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_VELMIN_SPIN,"SpinnerControl", + WS_TABSTOP,59,62,7,10 + LTEXT "min/max:",IDC_STATIC,3,63,30,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_PPS,"CustEdit",WS_TABSTOP,66, + 108,26,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_PPS_SPIN,"SpinnerControl", + WS_TABSTOP,93,108,7,10 + CONTROL "Immortal",IDC_COMP_PARTICLE_NODIE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,59,78,41,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_SCALEMAX,"CustEdit", + WS_TABSTOP,73,135,20,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_SCALEMAX_SPIN,"SpinnerControl", + WS_TABSTOP,93,135,7,10 + LTEXT "Scaling:",IDC_STATIC,6,122,29,8 + LTEXT "Generation Type:",IDC_STATIC,7,9,56,8 + COMBOBOX IDC_GEN_TYPE,36,19,66,40,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP + GROUPBOX "Simulation",IDC_STATIC,2,153,103,109 + LTEXT "Gravity (%):",IDC_STATIC,12,166,36,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_GRAVITY,"CustEdit", + WS_TABSTOP,59,166,24,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_GRAVITY_SPIN,"SpinnerControl", + WS_TABSTOP,85,166,7,10 + LTEXT "Drag:",IDC_STATIC,12,192,18,8 + LTEXT "Birthrate (per sec):",IDC_STATIC,6,109,58,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_VELMAX,"CustEdit",WS_TABSTOP, + 69,62,23,10 + CONTROL "Custom1",IDC_COMP_PARTICLE_VELMAX_SPIN,"SpinnerControl", + WS_TABSTOP,93,62,7,10 + CONTROL "Custom1",IDC_COMP_PARTICLE_DRAG,"CustEdit",WS_TABSTOP, + 59,192,24,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_DRAG_SPIN,"SpinnerControl", + WS_TABSTOP,85,192,7,10 + LTEXT "max (%):",IDC_STATIC,39,135,26,8 + LTEXT "Pre-sim (sec):",IDC_STATIC,12,179,43,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_PRESIM,"CustEdit",WS_TABSTOP, + 59,179,24,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_PRESIM_SPIN,"SpinnerControl", + WS_TABSTOP,85,179,7,10 + CONTROL "Show In TrackView",IDC_TRACKVIEW_SHOW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,267,78,10 + LTEXT "Wind %",IDC_STATIC,11,205,25,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_MULT,"CustEdit", + WS_TABSTOP,59,205,24,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_MULT_SPIN, + "SpinnerControl",WS_TABSTOP,85,205,7,10 + LTEXT "Mass Range",IDC_STATIC,11,218,41,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_MASS_RANGE,"CustEdit", + WS_TABSTOP,59,218,24,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_MASS_RANGE_SPIN, + "SpinnerControl",WS_TABSTOP,85,218,7,10 + LTEXT "Rot deg/sec",IDC_STATIC,11,231,41,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_ROT_RANGE,"CustEdit", + WS_TABSTOP,59,231,24,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_ROT_RANGE_SPIN, + "SpinnerControl",WS_TABSTOP,85,231,7,10 + CONTROL "Follow System",IDC_COMP_PARTICLE_FOLLOW_SYSTEM,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,247,61,10 +END + +IDD_COMP_CAMERACMD DIALOG 0, 0, 108, 139 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_COMBO1,8,13,92,441,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + LTEXT "Type:",IDC_STATIC,8,3,19,8 + LTEXT "Offset X:",IDC_STATIC,19,33,28,8 + LTEXT "Offset Z:",IDC_STATIC,19,59,28,8 + LTEXT "Offset Y:",IDC_STATIC,19,46,28,8 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX,"CustEdit",WS_TABSTOP,49, + 32,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX,"SpinnerControl", + WS_TABSTOP,80,32,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY,"CustEdit",WS_TABSTOP,49, + 45,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY,"SpinnerControl", + WS_TABSTOP,80,45,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETZ,"CustEdit",WS_TABSTOP,49, + 58,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETZ,"SpinnerControl", + WS_TABSTOP,80,58,7,10 + LTEXT "Switch To Fixed Camera:",IDC_STATIC,13,75,80,8 + CONTROL "Pick Me!",IDC_COMP_CAMERACMD_PICKSTATE_BASE,"CustButton", + WS_TABSTOP,11,85,84,10 + CONTROL "Smooth Transition",IDC_COMP_CAMERACMD_CUT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,116,73,10 + CONTROL "Offset in Worldspace",IDC_COMP_CAMERACMD_WORLDSPACE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,101,82,10 +END + +IDD_COMP_ROOM DIALOG 0, 0, 108, 71 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Age:",IDC_STATIC,16,6,16,8 + LTEXT "Chapter:",IDC_STATIC,4,21,28,8 + LTEXT "Page:",IDC_STATIC,13,37,20,8 + CONTROL "Custom1",IDC_COMP_ROOM_AGE_TEXTBOX,"CustEdit", + WS_TABSTOP,33,5,70,10 + CONTROL "Custom1",IDC_COMP_ROOM_DISTRICT_TEXTBOX,"CustEdit", + WS_TABSTOP,33,20,70,10 + CONTROL "Custom1",IDC_COMP_ROOM_ROOM_TEXTBOX,"CustEdit", + WS_TABSTOP,33,36,70,10 + LTEXT "Sequence #:",IDC_STATIC,4,52,42,8 + CONTROL "Custom2",IDC_ROOM_SEQEDIT,"CustEdit",WS_TABSTOP,48,51, + 47,10 + CONTROL "Custom2",IDC_ROOM_SEQSPIN,"SpinnerControl",WS_TABSTOP, + 96,51,7,10 +END + +IDD_COMP_PAGEINFO DIALOG 0, 0, 108, 87 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_COMP_LOCATION_AGECOMBO,5,12,98,111,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMP_LOCATION_PAGECOMBO,5,40,98,95,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Age:",IDC_STATIC,6,2,16,8 + LTEXT "Page:",IDC_STATIC,5,30,20,8 + CONTROL "Itinerant ",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,14,68,74,10 + GROUPBOX "!! Programmer Use ONLY !!",IDC_STATIC,4,56,102,29 +END + +IDD_COMP_PARTICLE_VOLUME DIALOG 0, 0, 108, 123 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Mesh to Build Volume From:",IDC_STATIC,4,3,88,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_VOLUME_NODE,"CustButton", + WS_TABSTOP,7,13,94,14 + CONTROL "Default",IDC_COMP_PARTICLE_VOL_DEFAULT,"Button", + BS_AUTORADIOBUTTON,24,43,39,10 + CONTROL "Die",IDC_COMP_PARTICLE_VOL_DIE,"Button", + BS_AUTORADIOBUTTON,24,54,27,10 + CONTROL "Bounce",IDC_COMP_PARTICLE_VOL_BOUNCE,"Button", + BS_AUTORADIOBUTTON,24,65,41,10 + GROUPBOX "On Impact:",IDC_STATIC,7,32,91,80 + LTEXT "Bounce %",IDC_STATIC,15,81,33,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_VOL_BOUNCEAMT,"CustEdit", + WS_TABSTOP,56,81,27,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_VOL_BOUNCEAMT_SPIN, + "SpinnerControl",WS_TABSTOP,84,81,7,10 + LTEXT "Friction %",IDC_STATIC,16,95,31,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_VOL_FRICTIONAMT,"CustEdit", + WS_TABSTOP,56,95,27,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_VOL_FRICTIONAMT_SPIN, + "SpinnerControl",WS_TABSTOP,84,95,7,10 +END + +IDD_COMP_SMOOTH DIALOG 0, 0, 106, 89 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Smooth normals between all attached meshes",IDC_STATIC, + 15,9,83,20 + LTEXT "Max Degrees",IDC_STATIC,11,32,44,8 + CONTROL "Custom1",IDC_COMP_SMOOTH_ANGLE,"CustEdit",WS_TABSTOP,61, + 31,27,10 + CONTROL "Custom2",IDC_COMP_SMOOTH_ANGLE_SPIN,"SpinnerControl", + WS_TABSTOP,89,31,7,10 + CONTROL "Snap Positions",IDC_COMP_SMOOTH_POS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,53,63,10 + CONTROL "Smooth Colors",IDC_COMP_SMOOTH_COLOR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,66,61,10 +END + +IDD_COMP_BARNEY DIALOG 0, 0, 113, 59 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Ignore node and children for everything but offline lighting.", + IDC_STATIC,7,19,84,24 + LTEXT "Barney",IDC_STATIC,41,7,23,8 +END + +IDD_COMP_LINEFOLLOW DIALOG 0, 0, 110, 209 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Listener",IDC_RADIO_LISTENER,"Button", + BS_AUTORADIOBUTTON,21,30,41,10 + CONTROL "Camera",IDC_RADIO_CAMERA,"Button",BS_AUTORADIOBUTTON,21, + 41,40,10 + CONTROL "Object",IDC_RADIO_OBJECT,"Button",BS_AUTORADIOBUTTON,21, + 52,37,10 + CONTROL "Pick",IDC_COMP_LINE_CHOOSE_OBJECT,"CustButton", + WS_TABSTOP,10,68,86,9 + GROUPBOX "Follow Path Settings",IDC_STATIC,5,4,101,82 + CONTROL "Pick",IDC_COMP_LINE_CHOOSE_PATH,"CustButton",WS_TABSTOP, + 10,15,86,9 + GROUPBOX "Offset Settings",IDC_STATIC,1,86,102,79 + CONTROL "Active",IDC_COMP_LINE_OFFSETACTIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,99,36,10 + LTEXT "Offset degrees",IDC_STATIC,7,112,47,8 + CONTROL "Custom1",IDC_COMP_LINE_OFFSETDEGREES,"CustEdit", + WS_TABSTOP,57,112,27,10 + CONTROL "Custom2",IDC_COMP_LINE_OFFSETDEGREES_SPIN, + "SpinnerControl",WS_TABSTOP,85,112,7,10 + CONTROL "Clamp Offset",IDC_COMP_LINE_OFFSETCLAMPACTIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,126,56,10 + LTEXT "Max feet",IDC_STATIC,21,139,31,8 + CONTROL "Custom1",IDC_COMP_LINE_OFFSETCLAMP,"CustEdit", + WS_TABSTOP,57,139,27,10 + CONTROL "Custom2",IDC_COMP_LINE_OFFSETCLAMP_SPIN,"SpinnerControl", + WS_TABSTOP,85,139,7,10 + CONTROL "Force back onto line",IDC_COMP_LINE_FORCETOLINE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,151,81,10 + LTEXT "Max f/s",IDC_STATIC,23,191,28,8 + CONTROL "Custom1",IDC_COMP_LINE_SPEEDCLAMP,"CustEdit",WS_TABSTOP, + 57,191,27,10 + CONTROL "Custom2",IDC_COMP_LINE_SPEEDCLAMP_SPIN,"SpinnerControl", + WS_TABSTOP,85,191,7,10 + CONTROL "Clamp Speed",IDC_COMP_LINE_SPEEDCLAMPACTIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,178,58,10 + GROUPBOX "Speed Settings",IDC_STATIC,5,168,95,37 +END + +IDD_COMP_RESPOND_MTL DIALOG 0, 0, 108, 102 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Material:",IDC_STATIC,6,2,28,8 + COMBOBOX IDC_LOOP_COMBO,5,84,96,135,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Loop:",IDC_LOOP_TEXT,5,75,19,8 + COMBOBOX IDC_ANIM_COMBO,6,60,96,135,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Animation:",IDC_STATIC,6,51,34,8 + PUSHBUTTON "Button1",IDC_MTL_BUTTON,6,11,96,12 + LTEXT "Node:",IDC_STATIC,6,26,20,8 + PUSHBUTTON "Button1",IDC_NODE_BUTTON,6,35,96,12 +END + +IDD_COMP_RESPOND_LINK DIALOGEX 0, 0, 108, 195 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + COMBOBOX IDC_LINKINGRULE,6,9,96,135,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_LINKAGEFILENAME,6,32,96,135,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_LINKAGEINSTANCENAME,"CustEdit",WS_TABSTOP,5,57,96, + 10 + CONTROL "",IDC_LINKSPAWNPOINT,"CustEdit",WS_TABSTOP,5,80,96,10 + LTEXT "Age Filename:",IDC_STATIC,6,24,46,8 + LTEXT "Spawn Point Name:",IDC_AGELINKSPAWNPTLABEL,5,71,96,8 + LTEXT "Linking Rule:",IDC_STATIC,6,1,42,8 + LTEXT "Age Instance Name:",IDC_LINKNAMELABEL,5,48,66,8 + CONTROL "",IDC_LINKSPAWNPOINTTITLE,"CustEdit",WS_TABSTOP,6,104, + 96,10 + LTEXT "Spawn Point Title:",IDC_AGELINKSPAWNPTTITLE,6,94,96,8 + CONTROL "",IDC_LINKAGELINKINANIMNAME,"CustEdit",WS_TABSTOP,5,129, + 96,10 + LTEXT "Link IN Anim (in new age):",IDC_AGELINKSPAWNPTTITLE2,5, + 119,96,8 + LTEXT "Parent Age Filename:",IDC_STATIC,5,144,82,8 + COMBOBOX IDC_PARENTAGEFILENAME,6,154,96,135,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_LINKAGEINSTANCEGUID,"CustEdit",WS_TABSTOP,5,180, + 96,10 + LTEXT "Age Instance GUID:",IDC_AGELINKSPAWNPTTITLE3,5,170,96,8 +END + +IDD_COMP_DETECTOR_CLICKABLE DIALOGEX 0, 0, 108, 261 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + LTEXT "Facing Within",IDC_STATIC,8,14,44,8 + CONTROL "Custom2",IDC_COMP_CLICK_DEGSPIN,"SpinnerControl", + WS_TABSTOP,72,13,7,10 + CONTROL "Custom1",IDC_COMP_CLICK_DEG,"CustEdit",WS_TABSTOP,54,13, + 18,10 + CONTROL "Of Clickable Y-Axis",IDC_COMP_CLICK_OMNI,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,26,75,10 + CONTROL "OneShot",IDC_ONESHOT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,9,109,44,10 + GROUPBOX "Triggering Conditions",IDC_STATIC,1,98,104,35 + GROUPBOX "Bounding Shape",IDC_STATIC,1,137,104,80 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON,9, + 147,39,10 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,9,158, + 28,10 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,9,169, + 28,10 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 9,180,34,10 + CONTROL "Custom1",IDC_COMP_CLICK_PROXYREGION,"CustButton", + WS_TABSTOP,8,65,82,10 + GROUPBOX "Directional Parameteres",IDC_STATIC,1,1,104,38 + LTEXT "Must be in this region:",IDC_STATIC,8,54,69,8 + GROUPBOX "Required Region",IDC_STATIC,1,43,104,50 + CONTROL "Pick",IDC_COMP_CLICK_PROXY,"CustButton",WS_TABSTOP,13, + 203,86,9 + CONTROL "Use Alternate Shape",IDC_COMP_CLICK_USEPROXY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,191,85,10 + CONTROL "Enabled",IDC_ENABLED,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,9,119,42,10 + CONTROL "Bounce Avatar",IDC_COLLIDABLE_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,221,63,10 + LTEXT "Deg.",IDC_STATIC,82,14,16,8 + CONTROL "Ignore",IDC_IGNORE_REGION_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,80,36,10 + CONTROL "Custom1",IDC_COMP_CLICKABLE_FRIC_EDIT1,"CustEdit", + WS_TABSTOP,41,236,45,10 + CONTROL "Custom2",IDC_COMP_CLICKABLE_FRIC_SPIN1,"SpinnerControl", + WS_TABSTOP,90,236,7,10 + LTEXT "Friction:",IDC_STATIC_FRICTION_LABEL,9,237,26,8 +END + +IDD_COMP_RANDOMSOUND DIALOG 0, 0, 109, 169 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Auto Start",IDC_COMP_RS_AUTOSTART,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,7,47,10 + CONTROL "No repeat",IDC_RADIO_RS_NOREP,"Button", + BS_AUTORADIOBUTTON,23,38,47,10 + CONTROL "Normal",IDC_RADIO_RS_NORMAL,"Button",BS_AUTORADIOBUTTON, + 23,28,38,10 + CONTROL "Full Cycle",IDC_RADIO_RS_FSREP,"Button", + BS_AUTORADIOBUTTON,23,48,46,10 + CONTROL "Full Cycle Stop",IDC_RADIO_RS_FSSTOP,"Button", + BS_AUTORADIOBUTTON,23,58,63,10 + GROUPBOX "Select Mode",IDC_STATIC,11,20,86,59 + CONTROL "Custom1",IDC_COMP_RS_DELAYMIN,"CustEdit",WS_TABSTOP,54, + 130,27,10 + CONTROL "Custom2",IDC_COMP_RS_DELAYMIN_SPIN,"SpinnerControl", + WS_TABSTOP,82,130,7,10 + CONTROL "Custom1",IDC_COMP_RS_DELAYMAX,"CustEdit",WS_TABSTOP,55, + 144,27,10 + CONTROL "Custom2",IDC_COMP_RS_DELAYMAX_SPIN,"SpinnerControl", + WS_TABSTOP,83,144,7,10 + LTEXT "Min Delay",IDC_STATIC,17,132,32,8 + LTEXT "Max Delay",IDC_STATIC,17,144,34,8 + GROUPBOX "Delay Mode",IDC_STATIC,12,86,85,75 + CONTROL "From Start",IDC_RADIO_RS_DELAYSTART,"Button", + BS_AUTORADIOBUTTON,24,96,48,10 + CONTROL "From End",IDC_RADIO_RS_DELAYEND,"Button", + BS_AUTORADIOBUTTON,24,106,46,10 + CONTROL "Play only on trig",IDC_RADIO_RS_DELAYNEVER,"Button", + BS_AUTORADIOBUTTON,24,115,65,10 + CONTROL "Sequential",IDC_RADIO_RS_SEQ,"Button", + BS_AUTORADIOBUTTON,23,67,49,10 +END + +IDD_COMP_DETECTOR_CLICK_DRAGGABLE DIALOG 0, 0, 108, 335 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "X Axis Animation",IDC_COMP_CLICK_DRAG_USEX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,110,68,10 + COMBOBOX IDC_COMP_CLICK_DRAG_ANIMX,8,124,91,52,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "Loop X Segment",IDC_COMP_CLICK_DRAG_USE_LOOPX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,140,69,10 + LTEXT "Facing Within x Degrees",IDC_COMP_CLICK_DRAG_TEXT1,8,26, + 83,8 + CONTROL "Custom1",IDC_COMP_CLICK_DRAG_PROXY,"CustButton", + WS_TABSTOP,8,319,91,10 + CONTROL "Custom1",IDC_COMP_CLICK_DRAG_PROXYREGION,"CustButton", + WS_TABSTOP,8,83,91,10 + CONTROL "Custom2",IDC_COMP_CLICK_DRAG_DEGSPIN,"SpinnerControl", + WS_TABSTOP,91,38,7,10 + CONTROL "Custom1",IDC_COMP_CLICK_DRAG_DEG,"CustEdit",WS_TABSTOP, + 8,38,81,10 + CONTROL "Directional",IDC_COMP_CLICK_DRAG_OMNI,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,12,49,10 + CONTROL "Y Axis Animation",IDC_COMP_CLICK_DRAG_USEYANIM,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,154,68,10 + COMBOBOX IDC_COMP_CLICK_DRAG_ANIM_Y,8,168,91,52,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "Loop Y Segment",IDC_COMP_CLICK_DRAG_USE_LOOPY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,184,69,10 + LTEXT "Must be in region:",IDC_STATIC,8,71,76,8 + CONTROL "Use Alternate Shape?",IDC_COMP_CLICK_DRAG_USEPROXYPHYS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,308,82,10 + CONTROL "All or Nothing",IDC_COMP_CLICK_DRAG_ALLORNOT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,224,58,10 + CONTROL "OneShot",IDC_ONESHOT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,8,211,44,10 + GROUPBOX "Directional Parameteres",IDC_STATIC,1,3,104,55 + GROUPBOX "Required Region",IDC_STATIC,2,61,103,37 + GROUPBOX "Triggering Conditions",IDC_STATIC,2,199,104,53 + GROUPBOX "Axis Animation",IDC_STATIC,2,100,104,96 + GROUPBOX "Bounding Shape",IDC_STATIC,2,252,104,81 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON,8, + 264,39,10 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,8,275, + 28,10 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,8,286, + 28,10 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 8,297,34,10 + CONTROL "Enabled",IDC_ENABLE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,8,236,42,10 +END + +IDD_COMP_ONESHOT DIALOG 0, 0, 108, 120 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Anim Name:",IDC_STATIC,9,16,39,8 + CONTROL "Custom1",IDC_COMP_ONESHOT_ANIM_TEXTBOX,"CustEdit", + WS_TABSTOP,9,28,89,10 + CONTROL "Can Play Backwards",IDC_COMP_ONESHOT_PLAY_BACK_BOOL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,42,81,10 + CONTROL "Can Control Speed",IDC_COMP_ONESHOT_CONT_SPEED_BOOL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,54,75,10 + LTEXT "Seek Time:",IDC_STATIC,5,95,42,10 + CONTROL "Custom1",IDC_COMP_ONESHOT_SEEK_FIELD_EDIT,"CustEdit", + WS_TABSTOP,48,95,37,10 + CONTROL "Custom2",IDC_COMP_ONESHOT_SEEK_FIELD_SPIN, + "SpinnerControl",WS_TABSTOP,85,95,7,10 + CONTROL "Use Smart Seek",IDC_SMART_SEEK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,81,67,10 + GROUPBOX "General Properties",IDC_STATIC,3,3,101,111 + CONTROL "Do Not Seek",IDC_NO_SEEK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,69,57,10 +END + +IDD_COMP_PARTICLE_FADE DIALOG 0, 0, 108, 41 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Fade in Z axis",IDC_COMP_PARTICLE_FADEZ,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,26,62,8 + LTEXT "Fade distance:",IDC_STATIC,7,7,48,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_FADE_DIST,"CustEdit", + WS_TABSTOP,59,7,24,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_FADE_DIST_SPIN, + "SpinnerControl",WS_TABSTOP,84,7,7,10 +END + +IDD_COMP_SOUNDBGND DIALOG 0, 0, 108, 42 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Do not synch or trigger over the network", + IDC_SND_LOCALONLY,"Button",BS_AUTOCHECKBOX | + BS_MULTILINE | WS_TABSTOP,2,4,94,18 + CONTROL "Stream from compressed file",IDC_CHECK_STREAM,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,2,25,104,10 +END + +IDD_COMP_FOLLOW DIALOG 0, 0, 110, 129 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_FOLLOW_CHOOSE_OBJECT,"CustButton", + WS_TABSTOP,15,109,74,9 + CONTROL "Local Player",IDC_F_RADIO_PLAYER,"Button", + BS_AUTORADIOBUTTON,21,69,55,10 + CONTROL "Camera",IDC_F_RADIO_CAMERA,"Button",BS_AUTORADIOBUTTON, + 21,79,40,10 + CONTROL "Listener",IDC_F_RADIO_LISTENER,"Button", + BS_AUTORADIOBUTTON,21,88,41,10 + CONTROL "Pick Object",IDC_F_RADIO_OBJECT,"Button", + BS_AUTORADIOBUTTON,21,97,53,10 + GROUPBOX "Leader",IDC_STATIC,10,58,85,64 + CONTROL "X",IDC_COMP_FOLLOW_X,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,14,28,21,10 + CONTROL "Y",IDC_COMP_FOLLOW_Y,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,39,28,21,10 + CONTROL "Z",IDC_COMP_FOLLOW_Z,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,62,29,21,10 + CONTROL "Rotation",IDC_COMP_FOLLOW_ROTATE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,39,43,10 + GROUPBOX "Affects",IDC_STATIC,10,14,84,36 +END + +IDD_COMP_LIGHTINC DIALOG 0, 0, 112, 41 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Include Characters",IDC_COMP_LIGHTINC_CHARS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,10,75,10 + CONTROL "Test",IDC_COMP_LIGHTINC_FILTER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,13,23,30,10 +END + +IDD_COMP_INV_OBJECT DIALOG 0, 0, 109, 85 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Exists in This Age Only", + IDC_COMP_INV_OBJECT_ITINERANTBOOL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,15,90,9 + CONTROL "Respawn at Starting Point", + IDC_COMP_INV_OBJECT_RESPAWNBOOL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,5,28,96,10 + GROUPBOX "Age Specific ",IDC_STATIC,3,4,103,38 + GROUPBOX "Time Specific",IDC_STATIC,1,44,103,38 + CONTROL "Temporary",IDC_COMP_INV_OBJECT_CONSUMABLE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,55,90,9 + CONTROL "Custom1",IDC_COMP_INV_OBJECT_LIFE_EDIT,"CustEdit", + WS_TABSTOP,41,66,38,10 + CONTROL "Custom2",IDC_COMP_INV_OBJECT_LIFE_SPIN,"SpinnerControl", + WS_TABSTOP,81,66,7,10 + RTEXT " LifeSpan:",IDC_STATIC,5,66,32,10 +END + +IDD_COMP_SOFTVOLUME DIALOG 0, 0, 111, 80 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Soft Distance",IDC_STATIC,8,13,47,8 + CONTROL "Custom1",IDC_COMP_SOFTVOL_SOFT,"CustEdit",WS_TABSTOP,58, + 12,27,10 + CONTROL "Custom2",IDC_COMP_SOFTVOL_SOFT_SPIN,"SpinnerControl", + WS_TABSTOP,86,12,7,10 + LTEXT "Power Inside",IDC_STATIC,11,49,47,8 + CONTROL "Custom1",IDC_COMP_SOFTVOL_INSIDE,"CustEdit",WS_TABSTOP, + 61,48,27,10 + CONTROL "Custom2",IDC_COMP_SOFTVOL_INSIDE_SPIN,"SpinnerControl", + WS_TABSTOP,89,48,7,10 + GROUPBOX "",IDC_STATIC,6,42,97,30 + LTEXT "Power Outside",IDC_STATIC,11,60,47,8 + CONTROL "Custom1",IDC_COMP_SOFTVOL_OUTSIDE,"CustEdit",WS_TABSTOP, + 61,59,27,10 + CONTROL "Custom2",IDC_COMP_SOFTVOL_OUTSIDE_SPIN,"SpinnerControl", + WS_TABSTOP,89,59,7,10 + CONTROL "Partial Power",IDC_COMP_SOFTVOL_ENABLEPARTIAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,31,57,10 +END + +IDD_COMP_SOUND_REVERB_PARAMS DIALOG 0, 0, 108, 539 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_COMP_SOUND_REV_ROOMATTEN,"SliderControl", + WS_TABSTOP,11,25,86,15 + LTEXT "-10,000",IDC_STATIC,12,41,25,8 + RTEXT "0",IDC_STATIC,72,41,24,8 + LTEXT "Attenuation (mB):",IDC_STATIC,11,15,55,8 + CONTROL "Custom1",IDC_COMP_SOUND_REV_ROOMHFATTEN,"SliderControl", + WS_TABSTOP,11,63,86,15 + LTEXT "-10,000",IDC_STATIC,12,79,25,8 + RTEXT "0",IDC_STATIC,72,79,24,8 + LTEXT "High-Freq Attenuation (mB):",IDC_STATIC,11,54,88,8 + GROUPBOX "Room",IDC_STATIC,5,3,98,126 + CONTROL "Custom1",IDC_COMP_SOUND_REV_ROOMROLLOFF,"SliderControl", + WS_TABSTOP,12,98,86,15 + LTEXT "0",IDC_STATIC,13,114,25,8 + RTEXT "10",IDC_STATIC,72,114,24,8 + LTEXT "Rolloff Factor:",IDC_STATIC,12,90,45,8 + CONTROL "Custom1",IDC_COMP_SOUND_REV_DECAYTIME,"SliderControl", + WS_TABSTOP,11,152,86,15 + LTEXT "0.1",IDC_STATIC,12,168,25,8 + RTEXT "20",IDC_STATIC,72,168,24,8 + LTEXT "Time (secs):",IDC_STATIC,11,142,39,8 + CONTROL "Custom1",IDC_COMP_SOUND_REV_DECAYRATIO,"SliderControl", + WS_TABSTOP,11,190,86,15 + LTEXT "0.1",IDC_STATIC,12,206,25,8 + RTEXT "2.0",IDC_STATIC,72,206,24,8 + LTEXT "High-Freq Ratio:",IDC_STATIC,11,181,52,8 + GROUPBOX "Decay",IDC_STATIC,5,130,98,90 + CONTROL "Custom1",IDC_COMP_SOUND_REV_REFLECTATTEN,"SliderControl", + WS_TABSTOP,11,244,86,15 + LTEXT "-10,000",IDC_STATIC,12,260,25,8 + RTEXT "1000",IDC_STATIC,72,260,24,8 + LTEXT "Attenuation (mB):",IDC_STATIC,11,234,55,8 + CONTROL "Custom1",IDC_COMP_SOUND_REV_REFLECTDELAY,"SliderControl", + WS_TABSTOP,11,282,86,15 + LTEXT "0",IDC_STATIC,12,298,25,8 + RTEXT "0.3",IDC_STATIC,72,298,24,8 + LTEXT "Delay (secs):",IDC_STATIC,11,273,42,8 + GROUPBOX "Reflections",IDC_STATIC,5,222,98,90 + CONTROL "Custom1",IDC_COMP_SOUND_REV_REVERBATTEN,"SliderControl", + WS_TABSTOP,11,336,86,15 + LTEXT "-10,000",IDC_STATIC,12,352,25,8 + RTEXT "2000",IDC_STATIC,72,352,24,8 + LTEXT "Attenuation (mB):",IDC_STATIC,11,326,55,8 + CONTROL "Custom1",IDC_COMP_SOUND_REV_REVERBDELAY,"SliderControl", + WS_TABSTOP,11,374,86,15 + LTEXT "0",IDC_STATIC,12,390,25,8 + RTEXT "0.1",IDC_STATIC,72,390,24,8 + LTEXT "Delay (secs):",IDC_STATIC,11,365,42,8 + GROUPBOX "Reverb",IDC_STATIC,5,314,98,90 + CONTROL "Custom1",IDC_COMP_SOUND_REV_ECHODENSITY,"SliderControl", + WS_TABSTOP,11,428,86,15 + LTEXT "0%",IDC_STATIC,12,444,25,8 + RTEXT "100%",IDC_STATIC,72,444,24,8 + LTEXT "Echo Density: ",IDC_STATIC,11,419,47,8 + CONTROL "Custom1",IDC_COMP_SOUND_REV_MODALDENSITY,"SliderControl", + WS_TABSTOP,11,467,86,15 + LTEXT "0%",IDC_STATIC,12,483,25,8 + RTEXT "100%",IDC_STATIC,72,483,24,8 + LTEXT "Modal Density:",IDC_STATIC,11,457,48,8 + GROUPBOX "Late Reverberation Decay",IDC_STATIC,5,406,98,90 + CONTROL "Custom1",IDC_COMP_SOUND_REV_REFHF,"SliderControl", + WS_TABSTOP,11,509,86,15 + LTEXT "20",IDC_STATIC,12,526,25,8 + RTEXT "20,000",IDC_STATIC,72,526,24,8 + LTEXT "Reference High-Freq (hz):",IDC_STATIC,11,499,82,8 + CONTROL "Custom2",IDC_COMP_SOUND_REV_ROOMATTEN_EDIT,"CustEdit", + WS_TABSTOP,40,41,30,9 + CONTROL "Custom2",IDC_COMP_SOUND_REV_ROOMHFATTEN_EDIT,"CustEdit", + WS_TABSTOP,40,79,30,9 + CONTROL "Custom2",IDC_COMP_SOUND_REV_ROOMROLLOFF_EDIT,"CustEdit", + WS_TABSTOP,40,114,30,9 + CONTROL "Custom2",IDC_COMP_SOUND_REV_DECAYTIME_EDIT,"CustEdit", + WS_TABSTOP,40,168,30,9 + CONTROL "Custom2",IDC_COMP_SOUND_REV_DECAYRATIO_EDIT,"CustEdit", + WS_TABSTOP,40,206,30,9 + CONTROL "Custom2",IDC_COMP_SOUND_REV_REFLECTATTEN_EDIT,"CustEdit", + WS_TABSTOP,40,260,30,9 + CONTROL "Custom2",IDC_COMP_SOUND_REV_REFLECTDELAY_EDIT,"CustEdit", + WS_TABSTOP,40,298,30,9 + CONTROL "Custom2",IDC_COMP_SOUND_REV_REVERBATTEN_EDIT,"CustEdit", + WS_TABSTOP,40,352,30,9 + CONTROL "Custom2",IDC_COMP_SOUND_REV_REVERBDELAY_EDIT,"CustEdit", + WS_TABSTOP,40,390,30,9 + CONTROL "Custom2",IDC_COMP_SOUND_REV_ECHODENSITY_EDIT,"CustEdit", + WS_TABSTOP,40,444,30,9 + CONTROL "Custom2",IDC_COMP_SOUND_REV_MODALDENSITY_EDIT,"CustEdit", + WS_TABSTOP,40,483,30,9 + CONTROL "Custom2",IDC_COMP_SOUND_REV_REFHF_EDIT,"CustEdit", + WS_TABSTOP,40,525,30,9 +END + +IDD_COMP_DETECTOR_REGION DIALOGEX 0, 0, 108, 355 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "Trigger on entering volume", + IDC_COMP_PHYSGADGET_ENTERBOX,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,5,5,96,10 + CONTROL "When count reaches",IDC_RADIO_ENTRYCOUNT,"Button", + BS_AUTORADIOBUTTON | BS_TOP | BS_MULTILINE,15,28,57,16 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX2,"CustEdit",WS_TABSTOP, + 72,31,18,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX2,"SpinnerControl", + WS_TABSTOP,90,31,7,10 + CONTROL "First exit",IDC_RADIO_FIRSTEXIT,"Button", + BS_AUTORADIOBUTTON,15,59,47,10 + CONTROL "Each exit",IDC_RADIO_EACHEXIT,"Button", + BS_AUTORADIOBUTTON,15,70,47,10 + CONTROL "OneShot",IDC_ONESHOT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,8,172,44,10 + GROUPBOX "Bounding Shape",IDC_STATIC,2,199,104,81 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,11,221, + 28,10 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,11, + 232,28,10 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 11,243,34,10 + CONTROL "Pick",IDC_COMP_PHYS_PICKSTATE_DETECTOR,"CustButton", + WS_TABSTOP,13,266,83,9 + CONTROL "Use Alternative Shape?",IDC_COMP_PHYS_CUSTOMCHK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,254,88,8 + CONTROL "Trigger on exiting volume",IDC_COMP_PHYSGADGET_EXITBOX, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,47,91,10 + CONTROL "Sphere",IDC_RADIO_BSPHERE2,"Button",BS_AUTORADIOBUTTON, + 11,211,60,10 + CONTROL "When count reaches",IDC_RADIO_EXITCOUNT,"Button", + BS_AUTORADIOBUTTON | BS_TOP | BS_MULTILINE,15,81,54,18 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX3,"CustEdit",WS_TABSTOP, + 72,85,18,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX3,"SpinnerControl", + WS_TABSTOP,89,85,7,10 + GROUPBOX "Triggering Conditions",IDC_STATIC,1,161,104,35 + CONTROL "Enabled",IDC_ENABLED,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,8,183,42,10 + CONTROL "Custom2",IDC_COMP_CLICK_DEGSPIN,"SpinnerControl", + WS_TABSTOP,77,123,7,10 + CONTROL "Custom1",IDC_COMP_CLICK_DEG,"CustEdit",WS_TABSTOP,59, + 123,18,10 + CONTROL "Each entry",IDC_RADIO_EACHENTRY,"Button", + BS_AUTORADIOBUTTON | BS_TOP | BS_MULTILINE,15,17,50,10, + WS_EX_TRANSPARENT + CONTROL "Trigger on facing",IDC_TRIGGER_ON_FACING_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,102,69,10 + LTEXT "Object inside volume and",IDC_STATIC,17,114,80,8 + LTEXT "degrees of region Y",IDC_STATIC,17,134,62,8 + LTEXT "facing within",IDC_STATIC,17,124,40,8 + CONTROL "and walking forward",IDC_WALKING_FORWARD_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,17,145,80,10 + GROUPBOX "Report On",IDC_STATIC,1,279,104,50 + CONTROL "Dynamic",IDC_RADIO_REPORT_DYN,"Button", + BS_AUTORADIOBUTTON,11,305,43,10 + CONTROL "Both",IDC_RADIO_REPORT_BOTH,"Button",BS_AUTORADIOBUTTON, + 11,316,31,10 + CONTROL "Avatar",IDC_RADIO_REPORT_AVATAR,"Button", + BS_AUTORADIOBUTTON,11,295,60,10 + CONTROL "Skip Server Arbitration",IDC_ARBITRATION_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,332,96,10 +END + +IDD_COMP_DETECTOR_COLLISION DIALOG 0, 0, 108, 30 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "OneShot",IDC_ONESHOT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,4,44,10 + CONTROL "Enabled",IDC_ENABLED,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,16,42,10 +END + +IDD_COMP_RESPOND DIALOG 0, 0, 108, 227 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST_TARGS,5,10,98,22,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_ACTIVATOR,5,30,47,12 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,55,30,47, + 12 + COMBOBOX IDC_STATE_COMBO,5,95,96,106,CBS_DROPDOWN | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS | WS_VSCROLL | + WS_TABSTOP + LISTBOX IDC_CMD_LIST,5,112,96,56,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_CMD,5,168,47,12 + PUSHBUTTON "Remove",IDC_REMOVE_CMD,54,168,47,12 + CONTROL "Enabled",IDC_ENABLED,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,5,212,42,10 + COMBOBOX IDC_SWITCH_COMBO,5,191,96,95,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "At end, switch to state:",IDC_STATIC,6,181,73,8 + GROUPBOX "State:",IDC_STATIC,1,85,105,124 + GROUPBOX "Detectors:",IDC_STATIC,1,0,106,83 + CONTROL "Trigger",IDC_CHECK_TRIGGER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,8,45,38,10 + CONTROL "Un-Trigger",IDC_CHECK_UNTRIGGER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,49,45,49,10 + CONTROL "Detect only this node",IDC_DETECT_LOCAL_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,56,83,10 + CONTROL "Don't FF Sounds",IDC_CHECK_SKIPFFSOUND,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,68,83,10 +END + +IDD_COMP_RESPOND_WAIT DIALOG 0, 0, 108, 95 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Wait for this command to complete before resetting", + IDC_WAIT_ON_ME_CHECK,"Button",BS_AUTOCHECKBOX | + BS_MULTILINE | WS_TABSTOP,4,3,95,18 + CONTROL "Wait for previous command",IDC_CHECK_WAIT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,23,102,10 + COMBOBOX IDC_WAIT_WHO,5,37,99,88,CBS_DROPDOWNLIST | WS_DISABLED | + WS_VSCROLL | WS_TABSTOP + CONTROL "finish",IDC_RADIO_FINISH,"Button",BS_AUTORADIOBUTTON | + WS_DISABLED | WS_GROUP,5,53,32,10 + CONTROL "marker:",IDC_RADIO_POINT,"Button",BS_AUTORADIOBUTTON | + WS_DISABLED,5,65,39,10 + COMBOBOX IDC_WAIT_POINT,17,77,87,88,CBS_DROPDOWNLIST | + WS_DISABLED | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_MARKER_EDIT,"CustEdit",NOT WS_VISIBLE | + WS_TABSTOP,17,77,87,12 +END + +IDD_COMP_PHYS_WHEEL_CONSTRAINT DIALOG 0, 0, 108, 178 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Wheel Constraint Params:",IDC_STATIC,4,2,99,114 + LTEXT "Rotational Axis:",IDC_STATIC,11,14,51,10 + LTEXT "Suspension Axis:",IDC_STATIC,11,38,59,10 + LTEXT "Steering Axis:",IDC_STATIC,11,62,51,10 + LTEXT "Position:",IDC_STATIC,11,86,51,10 + CONTROL "Using Z Axis",IDC_COMP_PHYS_SUSPENSION_BUTTON, + "CustButton",WS_TABSTOP,11,50,80,10 + CONTROL "Using Z Axis",IDC_COMP_PHYS_ROTATION_BUTTON,"CustButton", + WS_TABSTOP,11,26,80,10 + CONTROL "Using Z Axis",IDC_COMP_PHYS_STEERING_BUTTON,"CustButton", + WS_TABSTOP,11,74,80,10 + CONTROL "Using World Space",IDC_COMP_PHYS_POSITION_BUTTON, + "CustButton",WS_TABSTOP,11,98,80,10 + LTEXT "Parent:",IDC_STATIC,12,130,51,10 + CONTROL "Select Parent",IDC_COMP_PHYS_PARENT,"CustButton", + WS_TABSTOP,12,142,80,10 + CONTROL "Parent pinned down?",IDC_COMP_PHYS_PINNED_STATE_BOOL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,154,85,9 + GROUPBOX "Parent Parameters:",IDC_STATIC,4,117,100,58 +END + +IDD_COMP_PHYS_SS_CONSTRAINT DIALOG 0, 0, 108, 130 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Length:",IDC_STATIC,17,15,25,8 + LTEXT "Rebound:",IDC_STATIC,17,28,32,8 + CONTROL "Custom1",IDC_COMP_PHY_SS_LENGTH_EDIT,"CustEdit", + WS_TABSTOP,50,13,27,10 + CONTROL "Custom1",IDC_COMP_PHYS_SS_REBOUND_EDIT,"CustEdit", + WS_TABSTOP,50,27,27,10 + CONTROL "Custom2",IDC_COMP_PHY_SS_LENGTH_SPIN,"SpinnerControl", + WS_TABSTOP,78,13,7,10 + CONTROL "Custom2",IDC_COMP_PHYS_SS_REBOUND_SPIN,"SpinnerControl", + WS_TABSTOP,78,27,7,10 + LTEXT "Strength:",IDC_STATIC,17,43,30,8 + CONTROL "Custom1",IDC_COMP_PHYS_SS_STRENGTH_EDIT,"CustEdit", + WS_TABSTOP,50,42,27,10 + CONTROL "Custom2",IDC_COMP_PHYS_SS_STRENGTH_SPIN,"SpinnerControl", + WS_TABSTOP,78,42,7,10 + GROUPBOX "Strong Spring Params:",IDC_STATIC,4,2,100,55 + GROUPBOX "Parent Params:",IDC_STATIC,3,59,101,67 + LTEXT "Parent:",IDC_STATIC,14,85,24,10 + CONTROL "Select Parent",IDC_COMP_PHYS_PARENT,"CustButton", + WS_TABSTOP,13,98,80,10 + CONTROL "Parent pinned down?",IDC_COMP_PHYS_PINNED_STATE_BOOL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,112,81,10 + CONTROL "Use Parent?",IDC_COMP_PHYS_USE_PARENT_BOOL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,72,85,9 +END + +IDD_COMP_LIGHTREGION DIALOG 0, 0, 110, 50 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_LIGHTREGION_CHOOSE_VOLUME,"CustButton", + WS_TABSTOP,10,22,86,9 + GROUPBOX "Region",IDC_STATIC,3,12,97,26 +END + +IDD_COMP_SOFTVOLUME_UNION DIALOG 0, 0, 109, 125 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Soft Regions:",IDC_STATIC,3,6,44,8 + LISTBOX IDC_LIST_TARGS,3,16,98,39,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_VOLUME,3,55,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,53,55,47, + 14 + LTEXT "Power Inside",IDC_STATIC,9,92,47,8 + CONTROL "Custom1",IDC_COMP_SOFTUNION_INSIDE,"CustEdit", + WS_TABSTOP,59,91,27,10 + CONTROL "Custom2",IDC_COMP_SOFTUNION_INSIDE_SPIN,"SpinnerControl", + WS_TABSTOP,87,91,7,10 + GROUPBOX "",IDC_STATIC,4,85,97,30 + LTEXT "Power Outside",IDC_STATIC,9,103,47,8 + CONTROL "Custom1",IDC_COMP_SOFTUNION_OUTSIDE,"CustEdit", + WS_TABSTOP,59,102,27,10 + CONTROL "Custom2",IDC_COMP_SOFTUNION_OUTSIDE_SPIN,"SpinnerControl", + WS_TABSTOP,87,102,7,10 + CONTROL "Partial Power",IDC_COMP_SOFTUNION_ENABLEPARTIAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,78,57,10 +END + +IDD_COMP_SOFTVOLUME_ISECT DIALOG 0, 0, 109, 119 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Soft Regions:",IDC_STATIC,3,6,44,8 + LISTBOX IDC_LIST_TARGS,3,16,98,39,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_VOLUME,3,55,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,53,55,47, + 14 + LTEXT "Power Inside",IDC_STATIC,11,87,47,8 + CONTROL "Custom1",IDC_COMP_SOFTISECT_INSIDE,"CustEdit", + WS_TABSTOP,61,86,27,10 + CONTROL "Custom2",IDC_COMP_SOFTISECT_INSIDE_SPIN,"SpinnerControl", + WS_TABSTOP,89,86,7,10 + GROUPBOX "",IDC_STATIC,6,80,97,30 + LTEXT "Power Outside",IDC_STATIC,11,98,47,8 + CONTROL "Custom1",IDC_COMP_SOFTISECT_OUTSIDE,"CustEdit", + WS_TABSTOP,61,97,27,10 + CONTROL "Custom2",IDC_COMP_SOFTISECT_OUTSIDE_SPIN,"SpinnerControl", + WS_TABSTOP,89,97,7,10 + CONTROL "Partial Power",IDC_COMP_SOFTISECT_ENABLEPARTIAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,73,57,10 +END + +IDD_COMP_SOFTVOLUME_NEGATE DIALOG 0, 0, 110, 96 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_SOFTVOLUME_NEGATE_CHOOSE_SUB,"CustButton", + WS_TABSTOP,10,22,86,9 + GROUPBOX "Invert Region",IDC_STATIC,3,12,97,26 + LTEXT "Power Inside",IDC_STATIC,10,59,47,8 + CONTROL "Custom1",IDC_COMP_SOFTNEGATE_INSIDE,"CustEdit", + WS_TABSTOP,60,58,27,10 + CONTROL "Custom2",IDC_COMP_SOFTNEGATE_INSIDE_SPIN,"SpinnerControl", + WS_TABSTOP,88,58,7,10 + GROUPBOX "",IDC_STATIC,5,52,97,30 + LTEXT "Power Outside",IDC_STATIC,10,70,47,8 + CONTROL "Custom1",IDC_COMP_SOFTNEGATE_OUTSIDE,"CustEdit", + WS_TABSTOP,60,69,27,10 + CONTROL "Custom2",IDC_COMP_SOFTNEGATE_OUTSIDE_SPIN, + "SpinnerControl",WS_TABSTOP,88,69,7,10 + CONTROL "Partial Power",IDC_COMP_SOFTNEGATE_ENABLEPARTIAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,45,57,10 +END + +IDD_COMP_RESPOND_ANIM DIALOG 0, 0, 108, 87 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Animation Component:",IDC_COMP_TEXT,6,4,72,8 + COMBOBOX IDC_LOOP_COMBO,5,68,96,135,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Loop:",IDC_LOOP_TEXT,5,58,19,8 + PUSHBUTTON "Button1",IDC_ANIM_BUTTON,5,15,96,12 + LTEXT "Object:",IDC_OBJ_TEXT,6,32,24,8 + PUSHBUTTON "Button1",IDC_OBJ_BUTTON,5,42,96,12 +END + +IDD_COMP_PHYS_HINGE_CONSTRAINT DIALOGEX 0, 0, 108, 248 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + GROUPBOX "Hinge Constraint Params:",IDC_STATIC,4,2,99,98 + LTEXT "Parent:",IDC_STATIC,11,176,51,10 + CONTROL "Select Parent",IDC_COMP_PHYS_PARENT,"CustButton", + WS_TABSTOP,11,188,80,10 + LTEXT "Rotational Axis:",IDC_STATIC,37,14,51,10,0,WS_EX_RIGHT + CONTROL "X Axis",IDC_COMP_PHYS_HINGE_AXIS_RADIO,"Button", + BS_AUTORADIOBUTTON,47,26,34,10,WS_EX_RIGHT + CONTROL "Y Axis",IDC_COMP_PHYS_HINGE_AXIS_RADIO2,"Button", + BS_AUTORADIOBUTTON,48,36,33,10,WS_EX_RIGHT + CONTROL "Z Axis",IDC_COMP_PHYS_HINGE_AXIS_RADIO3,"Button", + BS_AUTORADIOBUTTON,47,46,34,10,WS_EX_RIGHT + CONTROL "Parent pinned down?",IDC_COMP_PHYS_PINNED_STATE_BOOL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,204,85,9 + GROUPBOX "Parent Params:",IDC_STATIC,4,151,100,68 + LTEXT "Upper Limit:",IDC_STATIC,9,60,38,8 + CONTROL "Custom3",IDC_COMP_PHYS_OPENANGLE_SPIN,"SpinnerControl", + WS_TABSTOP,87,59,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_OPENANGLE_EDIT,"CustEdit", + WS_TABSTOP,47,59,38,10 + LTEXT "Lower Limit:",IDC_STATIC,9,73,38,8 + CONTROL "Custom3",IDC_COMP_PHYS_CLOSEANGLE_SPIN,"SpinnerControl", + WS_TABSTOP,87,72,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_CLOSEANGLE_EDIT,"CustEdit", + WS_TABSTOP,47,72,38,10 + GROUPBOX "General Parameters",IDC_STATIC,3,101,101,48 + LTEXT "Strength:",IDC_STATIC,16,128,30,8 + CONTROL "Custom3",IDC_COMP_PHYS_STRENGTH_SPIN,"SpinnerControl", + WS_TABSTOP,87,127,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_STRENGTH_SLIDER,"CustEdit", + WS_TABSTOP,48,127,38,10 + LTEXT "Rebound:",IDC_STATIC,14,116,32,8 + CONTROL "Custom3",IDC_COMP_PHYS_TAU_SPIN,"SpinnerControl", + WS_TABSTOP,87,115,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_TAU_SLIDER,"CustEdit",WS_TABSTOP, + 48,115,38,10 + LTEXT "Friction:",IDC_STATIC,20,87,26,8 + CONTROL "Custom3",IDC_COMP_PHYS_FRICTION_SPIN1,"SpinnerControl", + WS_TABSTOP,87,86,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_FRICTION_SLIDER,"CustEdit", + WS_TABSTOP,48,86,38,10 + CONTROL "Use Parent?",IDC_COMP_PHYS_USE_PARENT_BOOL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,164,85,9 + GROUPBOX "Child Params:",IDC_STATIC,4,221,100,24 + CONTROL "Child pinned down?",IDC_COMP_PHYS_PINNED_STATE_BOOL2, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,231,85,9 +END + +IDD_COMP_PHYS_CRITTER DIALOG 0, 0, 108, 214 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Bounce:",IDC_STATIC,24,85,26,10 + LTEXT "Friction:",IDC_STATIC,26,99,25,8 + CONTROL "Custom1",IDC_COMP_PHYS_SIMP_EDIT2,"CustEdit",WS_TABSTOP, + 56,84,33,10 + CONTROL "Custom1",IDC_COMP_PHYS_SIMP_EDIT3,"CustEdit",WS_TABSTOP, + 56,98,33,10 + CONTROL "Custom2",IDC_COMP_PHYS_SIMP_SPIN2,"SpinnerControl", + WS_TABSTOP,90,84,7,10 + CONTROL "Custom2",IDC_COMP_PHYS_SIMP_SPIN3,"SpinnerControl", + WS_TABSTOP,90,98,7,10 + GROUPBOX "Physical Properties",IDC_STATIC,2,2,104,140 + LTEXT "Turn Force:",IDC_STATIC,14,30,37,8 + CONTROL "Custom3",IDC_COMP_PHYS_AGILITY_STATUS,"SpinnerControl", + WS_TABSTOP,90,29,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_AGILITY_SLIDER,"CustEdit", + WS_TABSTOP,56,29,33,10 + CONTROL "Pick",IDC_COMP_PHYS_PICKHULL_CRIT,"CustButton", + WS_TABSTOP,11,124,84,9 + LTEXT "Critter Hull Shape:",IDC_STATIC,11,113,58,8 + LTEXT "Mass:",IDC_STATIC,32,72,19,10 + CONTROL "Custom2",IDC_COMP_PHYS_SIMP_SPIN1,"SpinnerControl", + WS_TABSTOP,90,71,7,10 + CONTROL "Custom1",IDC_COMP_PHYS_SIMP_EDIT1,"CustEdit",WS_TABSTOP, + 56,71,33,10 + LTEXT "Acceleration:",IDC_STATIC,9,16,42,8 + CONTROL "Custom3",IDC_COMP_PHYS_ACCEL_STATUS,"SpinnerControl", + WS_TABSTOP,90,16,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_ACCEL_SLIDER,"CustEdit", + WS_TABSTOP,56,16,33,10 + GROUPBOX "Animation Properties",IDC_STATIC,2,143,104,71 + COMBOBOX IDC_ANIM_NAMES,7,165,95,52,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + LTEXT "Animation Type:",IDC_STATIC,7,155,52,8 + LTEXT "Animation Name:",IDC_STATIC,9,184,56,8 + CONTROL "Custom1",IDC_COMP_PHYS_CRIT_ANIM_TXT,"CustEdit", + WS_TABSTOP,6,194,95,10 + LTEXT "Turn Rate:",IDC_STATIC,17,44,35,8 + CONTROL "Custom3",IDC_COMP_PHYS_TR_STATUS,"SpinnerControl", + WS_TABSTOP,90,43,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_TR_SLIDER,"CustEdit",WS_TABSTOP, + 56,43,33,10 + LTEXT "Max Velocity:",IDC_STATIC,9,57,42,10 + CONTROL "Custom2",IDC_COMP_PHYS_CRIT_MVEL_SPIN,"SpinnerControl", + WS_TABSTOP,90,57,7,10 + CONTROL "Custom1",IDC_COMP_PHYS_CRIT_MVEL_EDIT,"CustEdit", + WS_TABSTOP,56,57,33,10 +END + +IDD_COMP_RESPOND_ANIMPICK DIALOG 0, 0, 186, 167 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Pick Component and Object" +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_OBJ_LIST,95,13,85,138,LBS_SORT | WS_VSCROLL | + WS_TABSTOP + LISTBOX IDC_COMP_LIST,5,13,85,138,LBS_SORT | WS_VSCROLL | + WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,40,149,50,14 + PUSHBUTTON "Cancel",IDCANCEL,94,149,50,14 + LTEXT "Object:",IDC_STATIC,95,4,24,8 + LTEXT "Component:",IDC_STATIC,5,4,39,8 +END + +IDD_COMP_RESPOND_ONESHOT DIALOG 0, 0, 108, 56 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Button1",IDC_RESPONDER_COMP,3,13,101,12 + LTEXT "Component:",IDC_STATIC,3,3,39,8 + PUSHBUTTON "Button1",IDC_RESPONDER_NODE,3,39,101,12 + LTEXT "Node:",IDC_STATIC,3,30,20,8 +END + +IDD_COMP_SOUND_FADEPARAMS DIALOG 0, 0, 108, 97 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX " ",IDC_STATIC,3,3,100,45 + CONTROL "Fade In",IDC_SOUND3D_INENABLE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,11,3,41,9 + RTEXT "Type:",IDC_STATIC,13,15,28,8 + COMBOBOX IDC_SOUND3D_INTYPE,45,13,52,75,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + RTEXT "Length (s):",IDC_STATIC,5,30,36,8 + CONTROL "Custom2",IDC_SOUND3D_INLENGTH,"CustEdit",WS_TABSTOP,45, + 30,44,10 + CONTROL "Custom2",IDC_SOUND3D_INLENGTHSPIN,"SpinnerControl", + WS_TABSTOP,90,30,7,10 + GROUPBOX " ",IDC_STATIC,3,50,100,43 + CONTROL "Fade Out",IDC_SOUND3D_OUTENABLE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,50,41,9 + RTEXT "Type:",IDC_STATIC,13,62,28,8 + COMBOBOX IDC_SOUND3D_OUTTYPE,45,60,52,75,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + RTEXT "Length (s):",IDC_STATIC,5,77,36,8 + CONTROL "Custom2",IDC_SOUND3D_OUTLENGTH,"CustEdit",WS_TABSTOP,45, + 77,44,10 + CONTROL "Custom2",IDC_SOUND3D_OUTLENGTHSPIN,"SpinnerControl", + WS_TABSTOP,90,77,7,10 +END + +IDD_COMP_SOUND_SOFTPARAMS DIALOG 0, 0, 110, 36 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX " ",IDC_STATIC,4,5,101,27 + CONTROL "Soft Region",IDC_SOUND_SOFTENABLE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,4,53,10 + CONTROL "Pick",IDC_COMP_SOUNDREGION_CHOOSE_VOLUME,"CustButton", + WS_TABSTOP,9,17,91,9 +END + +IDD_COMP_LOD_AVATAR DIALOGEX 0, 0, 108, 352 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + GROUPBOX "Avatar Properties",IDC_STATIC,3,1,101,181 + GROUPBOX "LOD Properties",IDC_STATIC,3,186,101,164 + COMBOBOX IDC_COMP_LOD_AVATAR_STATE,14,232,79,55,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "LOD State:",IDC_STATIC,14,221,36,8 + LTEXT "Animation Root:",IDC_STATIC,14,197,53,8 + LTEXT "Mesh:",IDC_STATIC,14,247,21,8 + CONTROL "Replace",IDC_COMP_LOD_AVATAR_ROOT_PICKB,"CustButton", + WS_TABSTOP,14,207,79,10 + CONTROL "Replace",IDC_COMP_LOD_AVATAR_MESH_PICKB,"CustButton", + WS_TABSTOP,14,257,79,10 + LISTBOX IDC_COMP_LOD_AVATAR_BONELIST,9,284,88,27, + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add",IDC_COMP_LOD_AVATAR_BONE_ADD,9,314,41,14 + PUSHBUTTON "Remove",IDC_COMP_LOD_AVATAR_BONE_REMOVE,56,314,42,14 + LTEXT "LOD level:",IDC_STATIC,9,335,38,8 + CONTROL "",IDC_COMP_LOD_AVATAR_GROUP,"CustEdit",WS_TABSTOP,66, + 334,24,10 + CONTROL "",IDC_COMP_LOD_AVATAR_GROUP_SPIN,"SpinnerControl", + WS_TABSTOP,90,334,7,10 + LTEXT "Unused Bones:",IDC_STATIC,9,274,54,8 + LTEXT "Target Material:",IDC_STATIC,16,96,61,8 + PUSHBUTTON "(none)",IDC_COMP_LOD_AVATAR_MTL,16,106,74,12 + LTEXT "Clothing Group:",IDC_STATIC,12,42,50,8 + COMBOBOX IDC_COMP_AVATAR_CLOTHING_GROUP,12,52,83,43,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Skeleton:",IDC_STATIC,12,70,31,8 + COMBOBOX IDC_COMP_AVATAR_SKELETON,12,80,83,43,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Physical Height:",IDC_STATIC,9,17,53,8 + CONTROL "Custom1",IDC_PHYS_HEIGHT_EDIT,"CustEdit",WS_TABSTOP,62, + 16,30,10 + CONTROL "Custom2",IDC_PHYS_HEIGHT_SPIN,"SpinnerControl", + WS_TABSTOP,92,16,7,10 + LTEXT "Physical Width:",IDC_STATIC,9,30,53,8 + CONTROL "Custom1",IDC_PHYS_WIDTH_EDIT,"CustEdit",WS_TABSTOP,62, + 29,30,10 + CONTROL "Custom2",IDC_PHYS_WIDTH_SPIN,"SpinnerControl", + WS_TABSTOP,92,29,7,10 + LTEXT "Footstep Sound Page:",IDC_STATIC,12,128,72,8 + LTEXT "Animation Prefix:",IDC_STATIC,12,155,53,8 + CONTROL "",IDC_BODYFOOTSTEPPAGE_EDIT,"CustEdit",WS_TABSTOP,12, + 138,83,14 + CONTROL "",IDC_ANIMATIONPREFIX_EDIT,"CustEdit",WS_TABSTOP,12,164, + 83,14 +END + +IDD_COMP_CAMERARGN DIALOG 0, 0, 108, 41 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Switch To Camera:",IDC_STATIC,9,11,61,8 + CONTROL "Pick Me!",IDC_COMP_CAMERARGN_PICKSTATE_BASE,"CustButton", + WS_TABSTOP,9,22,84,10 + GROUPBOX "Camera Properties:",IDC_STATIC,2,1,103,37 +END + +IDD_COMP_FIXEDCAM DIALOG 0, 0, 83, 22 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Fixed Camera",IDC_STATIC,19,7,46,8 +END + +IDD_COMP_AUTOCAM DIALOG 0, 0, 110, 426 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Offset X:",IDC_STATIC,17,64,28,8 + LTEXT "Offset Z:",IDC_STATIC,17,90,28,8 + LTEXT "Offset Y:",IDC_STATIC,17,78,28,8 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX,"CustEdit",WS_TABSTOP,47, + 63,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX,"SpinnerControl", + WS_TABSTOP,78,63,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY,"CustEdit",WS_TABSTOP,47, + 75,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY,"SpinnerControl", + WS_TABSTOP,78,75,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETZ,"CustEdit",WS_TABSTOP,47, + 88,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETZ,"SpinnerControl", + WS_TABSTOP,78,88,7,10 + CONTROL "Offset in Worldspace",IDC_COMP_AUTOCAM_POA_WORLDSPACE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,119,82,10 + LTEXT "Avatar Follow Camera",IDC_STATIC,7,7,81,8 + LTEXT "Offset X:",IDC_STATIC,17,130,28,8 + LTEXT "Offset Z:",IDC_STATIC,17,158,28,8 + LTEXT "Offset Y:",IDC_STATIC,17,144,28,8 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX5,"CustEdit",WS_TABSTOP, + 47,130,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX5,"SpinnerControl", + WS_TABSTOP,78,130,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY4,"CustEdit",WS_TABSTOP, + 47,142,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY4,"SpinnerControl", + WS_TABSTOP,78,142,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETZ2,"CustEdit",WS_TABSTOP, + 47,154,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETZ2,"SpinnerControl", + WS_TABSTOP,78,154,7,10 + CONTROL "Cut to follow pos",IDC_COMP_AUTOCAM_LOS2,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,266,68,10 + CONTROL "Offset in Worldspace",IDC_COMP_AUTOCAM_POS_WORLDSPACE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,52,82,10 + LTEXT "Acceleration ft/s",IDC_STATIC,9,187,53,8 + CONTROL "Custom1",IDC_CAMERA_ACCEL,"CustEdit",WS_TABSTOP,9,195, + 30,10 + CONTROL "Custom2",IDC_CAMERA_ACCEL_SPIN,"SpinnerControl", + WS_TABSTOP,41,195,7,10 + LTEXT "Max Velocity ft/s",IDC_STATIC,9,210,54,8 + CONTROL "Custom1",IDC_CAMERA_VEL,"CustEdit",WS_TABSTOP,9,219,30, + 10 + CONTROL "Custom2",IDC_CAMERA_VEL_SPIN,"SpinnerControl", + WS_TABSTOP,41,219,7,10 + LTEXT "Deceleration ft/s",IDC_STATIC,9,233,54,8 + CONTROL "Custom1",IDC_CAMERA_DECEL,"CustEdit",WS_TABSTOP,9,242, + 30,10 + CONTROL "Custom2",IDC_CAMERA_DECEL_SPIN,"SpinnerControl", + WS_TABSTOP,41,242,7,10 + GROUPBOX "Positional Speeds",IDC_STATIC,7,176,70,84 + LTEXT "Acceleration ft/s",IDC_STATIC,9,294,53,8 + CONTROL "Custom1",IDC_CAMERA_ACCEL2,"CustEdit",WS_TABSTOP,9,302, + 30,10 + CONTROL "Custom2",IDC_CAMERA_ACCEL_SPIN2,"SpinnerControl", + WS_TABSTOP,41,302,7,10 + LTEXT "Max Velocity ft/s",IDC_STATIC,9,318,54,8 + CONTROL "Custom1",IDC_CAMERA_VEL2,"CustEdit",WS_TABSTOP,9,326,30, + 10 + CONTROL "Custom2",IDC_CAMERA_VEL_SPIN2,"SpinnerControl", + WS_TABSTOP,41,326,7,10 + LTEXT "Deceleration ft/s",IDC_STATIC,9,341,54,8 + CONTROL "Custom1",IDC_CAMERA_DECEL2,"CustEdit",WS_TABSTOP,9,350, + 30,10 + CONTROL "Custom2",IDC_CAMERA_DECEL_SPIN2,"SpinnerControl", + WS_TABSTOP,41,350,7,10 + GROUPBOX "POA Speeds",IDC_STATIC,7,283,70,84 + GROUPBOX "POA Offset",IDC_STATIC,7,104,86,66 + GROUPBOX "Position Offset",IDC_STATIC,7,38,85,66 + CONTROL "Lock POA to Avatar",IDC_COMP_AUTOCAM_LOS3,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,371,79,10 + CONTROL "Maintain LOS",IDC_COMP_AUTOCAM_LOS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,24,59,10 + CONTROL "Go Vertical when falling",IDC_COMP_AUTOCAM_VERTICAL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,386,90,10 + CONTROL "Faster when running",IDC_COMP_AUTOCAM_RUN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,402,80,10 +END + +IDD_COMP_CAMERA_TRANS DIALOG 0, 0, 112, 336 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick Me!",IDC_COMP_CAMERARGN_PICKSTATE_BASE,"CustButton", + WS_TABSTOP,13,33,84,10 + LTEXT "Override Transition From (leave blank if always)", + IDC_STATIC,14,13,85,17 + CONTROL "Cut Position",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,8,89,53,10 + CONTROL "Track Position",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,104,61,10 + CONTROL "Cut POA",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,8,212,43,10 + CONTROL "Track POA",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,228,51,10 + LTEXT "Acceleration ft/s",IDC_STATIC,9,132,53,8 + CONTROL "Custom1",IDC_CAMERA_ACCEL,"CustEdit",WS_TABSTOP,9,140, + 30,10 + CONTROL "Custom2",IDC_CAMERA_ACCEL_SPIN,"SpinnerControl", + WS_TABSTOP,41,140,7,10 + LTEXT "Max Velocity ft/s",IDC_STATIC,9,156,54,8 + CONTROL "Custom1",IDC_CAMERA_VEL,"CustEdit",WS_TABSTOP,9,164,30, + 10 + CONTROL "Custom2",IDC_CAMERA_VEL_SPIN,"SpinnerControl", + WS_TABSTOP,41,164,7,10 + LTEXT "Deceleration ft/s",IDC_STATIC,9,178,54,8 + CONTROL "Custom1",IDC_CAMERA_DECEL,"CustEdit",WS_TABSTOP,9,188, + 30,10 + CONTROL "Custom2",IDC_CAMERA_DECEL_SPIN,"SpinnerControl", + WS_TABSTOP,41,188,7,10 + GROUPBOX "Positional Speeds",IDC_STATIC,7,122,70,84 + LTEXT "Acceleration ft/s",IDC_STATIC,9,255,53,8 + CONTROL "Custom1",IDC_CAMERA_ACCEL2,"CustEdit",WS_TABSTOP,9,263, + 30,10 + CONTROL "Custom2",IDC_CAMERA_ACCEL_SPIN2,"SpinnerControl", + WS_TABSTOP,41,263,7,10 + LTEXT "Max Velocity ft/s",IDC_STATIC,9,279,54,8 + CONTROL "Custom1",IDC_CAMERA_VEL2,"CustEdit",WS_TABSTOP,9,287,30, + 10 + CONTROL "Custom2",IDC_CAMERA_VEL_SPIN2,"SpinnerControl", + WS_TABSTOP,41,287,7,10 + LTEXT "Deceleration ft/s",IDC_STATIC,9,301,54,8 + CONTROL "Custom1",IDC_CAMERA_DECEL2,"CustEdit",WS_TABSTOP,9,311, + 30,10 + CONTROL "Custom2",IDC_CAMERA_DECEL_SPIN2,"SpinnerControl", + WS_TABSTOP,41,311,7,10 + GROUPBOX "POA Speeds",IDC_STATIC,7,245,70,84 + CONTROL "",IDC_CHECK_IGNORE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,60,8,10 + LTEXT "Ignore Transition from above camera (transition will NOT trigger)", + IDC_STATIC,21,49,71,33 +END + +IDD_COMP_PHYS_WALKABLE DIALOG 0, 0, 108, 110 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Bounding Shape",IDC_STATIC,1,49,105,60 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON,7, + 61,39,8 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,7,73,28, + 8 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,7,85, + 28,8 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 7,97,34,8 + GROUPBOX "Physical Properties",IDC_STATIC,1,2,105,46 + LTEXT "Friction:",IDC_STATIC,5,16,25,10 + CONTROL "Custom1",IDC_COMP_PHYS_TERRAIN_EDIT1,"CustEdit", + WS_TABSTOP,37,16,45,10 + CONTROL "Custom2",IDC_COMP_PHYS_TERRAIN_SPIN1,"SpinnerControl", + WS_TABSTOP,84,16,7,10 + LTEXT "Bounce:",IDC_STATIC,5,30,27,10 + CONTROL "Custom1",IDC_COMP_PHYS_INVIS_BOUNCE_EDIT,"CustEdit", + WS_TABSTOP,37,30,45,10 + CONTROL "Custom2",IDC_COMP_PHYS_INVIS_BOUNCE_SPIN,"SpinnerControl", + WS_TABSTOP,84,30,7,10 +END + +IDD_COMP_PHYS_CLIMBABLE DIALOG 0, 0, 108, 110 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Bounding Shape",IDC_STATIC,1,49,105,60 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON,7, + 61,39,8 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,7,73,28, + 8 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,7,85, + 28,8 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 7,97,34,8 + GROUPBOX "Physical Properties",IDC_STATIC,1,2,105,46 + LTEXT "Friction:",IDC_STATIC,5,16,25,10 + CONTROL "Custom1",IDC_COMP_PHYS_TERRAIN_EDIT1,"CustEdit", + WS_TABSTOP,37,16,45,10 + CONTROL "Custom2",IDC_COMP_PHYS_TERRAIN_SPIN1,"SpinnerControl", + WS_TABSTOP,84,16,7,10 + LTEXT "Bounce:",IDC_STATIC,5,30,27,10 + CONTROL "Custom1",IDC_COMP_PHYS_INVIS_BOUNCE_EDIT,"CustEdit", + WS_TABSTOP,37,30,45,10 + CONTROL "Custom2",IDC_COMP_PHYS_INVIS_BOUNCE_SPIN,"SpinnerControl", + WS_TABSTOP,84,30,7,10 +END + +IDD_COMP_PHYS_SWIMSURF DIALOGEX 0, 0, 108, 350 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + GROUPBOX "Bounding Shape",IDC_STATIC,5,7,97,57 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON,9, + 19,39,8 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,9,30,28, + 8 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,9,41, + 28,8 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 9,52,34,8 + CONTROL "None",IDC_SWIM_CURRENT_NONE,"Button",BS_AUTORADIOBUTTON, + 9,127,39,8 + CONTROL "Spiral",IDC_SWIM_CURRENT_SPIRAL,"Button", + BS_AUTORADIOBUTTON,9,138,40,8 + GROUPBOX "Water Current",IDC_STATIC,5,117,97,172 + LTEXT "Rotation:",IDC_STATIC,21,211,29,8 + CONTROL "Custom1",IDC_SWIM_CURRENT_ROTATION,"CustEdit", + WS_TABSTOP,55,209,19,10 + CONTROL "Custom2",IDC_SWIM_CURRENT_ROTATION_SPIN,"SpinnerControl", + WS_TABSTOP,75,209,7,10 + LTEXT "Near Dist:",IDC_STATIC,35,158,32,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_SWIM_CURRENT_PULL_NEAR_DIST,"CustEdit", + WS_TABSTOP,69,157,19,10 + CONTROL "Custom2",IDC_SWIM_CURRENT_PULL_NEAR_DIST_SPIN, + "SpinnerControl",WS_TABSTOP,89,157,7,10 + CONTROL "Custom1",IDC_SWIM_DETECTOR_NODE,"CustButton",WS_TABSTOP, + 11,82,82,10 + LTEXT "Box hull for detection:",IDC_STATIC,11,74,70,8 + CONTROL "Custom1",IDC_SWIM_CURRENT_NODE,"CustButton",WS_TABSTOP, + 11,103,82,10 + LTEXT "Dummy box for current:",IDC_STATIC,11,94,77,8 + GROUPBOX "Buoyancy",IDC_STATIC,5,292,97,53 + RTEXT "Rise:",IDC_STATIC,9,318,59,8 + CONTROL "Custom1",IDC_SWIM_BUOYANCY_UP,"CustEdit",WS_TABSTOP,69, + 316,19,10 + CONTROL "Custom2",IDC_SWIM_BUOYANCY_UP_SPIN,"SpinnerControl", + WS_TABSTOP,89,316,7,10 + RTEXT "Max Upward Vel:",IDC_STATIC,9,331,59,8 + CONTROL "Custom1",IDC_SWIM_MAX_UP_VEL,"CustEdit",WS_TABSTOP,69, + 329,19,10 + CONTROL "Custom2",IDC_SWIM_MAX_UP_VEL_SPIN,"SpinnerControl", + WS_TABSTOP,89,329,7,10 + RTEXT "Slowdown (Entry):",IDC_STATIC,9,305,59,8 + CONTROL "Custom1",IDC_SWIM_BUOYANCY_DOWN,"CustEdit",WS_TABSTOP, + 69,303,19,10 + CONTROL "Custom2",IDC_SWIM_BUOYANCY_DOWN_SPIN,"SpinnerControl", + WS_TABSTOP,89,303,7,10 + LTEXT "Far Vel:",IDC_STATIC,38,198,29,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_SWIM_CURRENT_PULL_FAR_VEL,"CustEdit", + WS_TABSTOP,69,196,19,10 + CONTROL "Custom2",IDC_SWIM_CURRENT_PULL_FAR_VEL_SPIN, + "SpinnerControl",WS_TABSTOP,89,196,7,10 + LTEXT "Near Vel:",IDC_STATIC,38,171,29,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_SWIM_CURRENT_PULL_NEAR_VEL,"CustEdit", + WS_TABSTOP,69,170,19,10 + CONTROL "Custom2",IDC_SWIM_CURRENT_PULL_NEAR_VEL_SPIN, + "SpinnerControl",WS_TABSTOP,89,170,7,10 + LTEXT "Far Dist:",IDC_STATIC,38,185,29,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_SWIM_CURRENT_PULL_FAR_DIST,"CustEdit", + WS_TABSTOP,69,183,19,10 + CONTROL "Custom2",IDC_SWIM_CURRENT_PULL_FAR_DIST_SPIN, + "SpinnerControl",WS_TABSTOP,89,183,7,10 + LTEXT "Pull:",IDC_STATIC,22,149,29,8 + CONTROL "Straight (along Y axis):",IDC_SWIM_CURRENT_STRAIGHT, + "Button",BS_AUTORADIOBUTTON,9,227,88,8 + LTEXT "Near Dist:",IDC_STATIC,35,238,32,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_SWIM_CURRENT_STRAIGHT_NEAR_DIST,"CustEdit", + WS_TABSTOP,69,236,19,10 + CONTROL "Custom2",IDC_SWIM_CURRENT_STRAIGHT_NEAR_DIST_SPIN, + "SpinnerControl",WS_TABSTOP,89,236,7,10 + LTEXT "Far Vel:",IDC_STATIC,38,278,29,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_SWIM_CURRENT_STRAIGHT_FAR_VEL,"CustEdit", + WS_TABSTOP,69,276,19,10 + CONTROL "Custom2",IDC_SWIM_CURRENT_STRAIGHT_FAR_VEL_SPIN, + "SpinnerControl",WS_TABSTOP,89,276,7,10 + LTEXT "Near Vel:",IDC_STATIC,38,250,29,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_SWIM_CURRENT_STRAIGHT_NEAR_VEL,"CustEdit", + WS_TABSTOP,69,249,19,10 + CONTROL "Custom2",IDC_SWIM_CURRENT_STRAIGHT_NEAR_VEL_SPIN, + "SpinnerControl",WS_TABSTOP,89,249,7,10 + LTEXT "Far Dist:",IDC_STATIC,38,264,29,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_SWIM_CURRENT_STRAIGHT_FAR_DIST,"CustEdit", + WS_TABSTOP,69,263,19,10 + CONTROL "Custom2",IDC_SWIM_CURRENT_STRAIGHT_FAR_DIST_SPIN, + "SpinnerControl",WS_TABSTOP,89,263,7,10 +END + +IDD_COMP_PHYS_SWIM3D DIALOG 0, 0, 108, 108 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Bounding Shape",IDC_STATIC,1,48,105,60 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON,7, + 60,39,8 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,7,72,28, + 8 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,7,84, + 28,8 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 7,96,34,8 + GROUPBOX "Physical Properties",IDC_STATIC,1,1,105,46 + LTEXT "Friction:",IDC_STATIC,5,15,25,10 + CONTROL "Custom1",IDC_COMP_PHYS_TERRAIN_EDIT1,"CustEdit", + WS_TABSTOP,37,15,45,10 + CONTROL "Custom2",IDC_COMP_PHYS_TERRAIN_SPIN1,"SpinnerControl", + WS_TABSTOP,84,15,7,10 + LTEXT "Bounce:",IDC_STATIC,5,29,27,10 + CONTROL "Custom1",IDC_COMP_PHYS_INVIS_BOUNCE_EDIT,"CustEdit", + WS_TABSTOP,37,29,45,10 + CONTROL "Custom2",IDC_COMP_PHYS_INVIS_BOUNCE_SPIN,"SpinnerControl", + WS_TABSTOP,84,29,7,10 +END + +IDD_COMP_GUITAG DIALOG 0, 0, 106, 113 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Tag Konstant:",IDC_STATIC,6,80,46,8 + COMBOBOX IDC_GUI_TAGCOMBO,5,93,96,108,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP + GROUPBOX "WARNING:",IDC_STATIC,3,2,99,73 + CTEXT "This component tags a given GUI control or dialog with a code constant. It is for use by programmers ONLY. Misuse of this component will result in immediate death through Smeggying.", + IDC_STATIC,6,12,92,59 +END + +IDD_COMP_CAMERA_OCCLUDER DIALOG 0, 0, 108, 61 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Bounding Shape",IDC_STATIC,1,2,105,56 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,11,25, + 30,10 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON, + 11,15,41,10 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,11,35, + 28,10 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 11,45,41,10 +END + +IDD_COMP_PHYS_INVISIBLE DIALOGEX 0, 0, 108, 173 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + GROUPBOX "Bounding Shape",IDC_STATIC,1,69,105,60 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON,7, + 81,39,8 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,7,93,28, + 8 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,7,105, + 28,8 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 7,117,34,8 + GROUPBOX "Physical Properties",IDC_STATIC,1,2,105,66 + CONTROL "Don't Block LOS",IDC_COMP_CHECK_LOS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,49,69,10 + LTEXT "Friction:",IDC_STATIC,5,16,25,10 + CONTROL "Custom1",IDC_COMP_PHYS_TERRAIN_EDIT1,"CustEdit", + WS_TABSTOP,37,16,45,10 + CONTROL "Custom2",IDC_COMP_PHYS_TERRAIN_SPIN1,"SpinnerControl", + WS_TABSTOP,84,16,7,10 + LTEXT "Bounce:",IDC_STATIC,5,30,27,10 + CONTROL "Custom1",IDC_COMP_PHYS_INVIS_BOUNCE_EDIT,"CustEdit", + WS_TABSTOP,37,30,45,10 + CONTROL "Custom2",IDC_COMP_PHYS_INVIS_BOUNCE_SPIN,"SpinnerControl", + WS_TABSTOP,84,30,7,10 + GROUPBOX "Block",IDC_STATIC,2,131,105,39 + CONTROL "Avatars",IDC_RADIO_BLOCK_AVATAR,"Button", + BS_AUTORADIOBUTTON,8,143,40,10 + CONTROL "Dynamics",IDC_RADIO_BLOCK_DYNAMIC,"Button", + BS_AUTORADIOBUTTON,8,155,47,10 +END + +IDD_COMP_AVATAR_POA DIALOG 0, 0, 111, 75 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Offset X:",IDC_STATIC,17,22,28,8 + LTEXT "Offset Z:",IDC_STATIC,17,43,28,8 + LTEXT "Offset Y:",IDC_STATIC,17,32,28,8 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX5,"CustEdit",WS_TABSTOP, + 47,20,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX5,"SpinnerControl", + WS_TABSTOP,79,20,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY4,"CustEdit",WS_TABSTOP, + 47,31,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY4,"SpinnerControl", + WS_TABSTOP,79,31,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETZ2,"CustEdit",WS_TABSTOP, + 47,41,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETZ2,"SpinnerControl", + WS_TABSTOP,79,41,7,10 + LTEXT "Avatar POA Offset",IDC_STATIC,7,7,62,8 + CONTROL "Offset in Worldspace",IDC_COMP_POA_WORLDSPACE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,55,82,10 +END + +IDD_COMP_OBJECT_POA DIALOG 0, 0, 111, 100 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Object to Look At",IDC_STATIC,9,7,82,8 + CONTROL "Custom1",IDC_COMP_CLICK_PROXY,"CustButton",WS_TABSTOP,7, + 19,82,10 + LTEXT "Offset X:",IDC_STATIC,17,42,28,8 + LTEXT "Offset Z:",IDC_STATIC,17,64,28,8 + LTEXT "Offset Y:",IDC_STATIC,17,54,28,8 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX,"CustEdit",WS_TABSTOP,47, + 41,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX,"SpinnerControl", + WS_TABSTOP,78,41,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY,"CustEdit",WS_TABSTOP,47, + 52,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY,"SpinnerControl", + WS_TABSTOP,78,52,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETZ,"CustEdit",WS_TABSTOP,47, + 62,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETZ,"SpinnerControl", + WS_TABSTOP,78,62,7,10 + LTEXT "Position Offset",IDC_STATIC,7,31,62,8 + CONTROL "Offset in Worldspace",IDC_COMP_OBJECTPOA_WORLDSPACE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,76,82,10 +END + +IDD_COMP_CAM_MAKEDEFAULT DIALOG 0, 0, 108, 43 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Make this camera the default camera for the age once it is triggered.", + IDC_STATIC,7,7,94,29 +END + +IDD_COMP_PYTHON_FILE DIALOG 0, 0, 108, 45 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_PYTHON_FILE,6,13,95,117,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + LTEXT "Python File:",IDC_STATIC,6,3,38,8 + CONTROL "Make it global to the age",IDC_PYTHON_GLOBAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,30,91,10 + LTEXT "Version:",IDC_STATIC,61,3,26,8 + LTEXT "99",IDC_VER_TEXT,89,3,12,9,SS_SUNKEN +END + +IDD_COMP_LOS_WIDGET DIALOG 0, 0, 108, 72 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "LOS Additional Properties",IDC_STATIC,1,0,105,70 + COMBOBOX IDC_COMP_LOS_WIDGET_CB,7,11,96,52,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + LISTBOX IDC_COMP_LOS_WIDGET_LB,7,24,96,30,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Remove",IDC_COMP_LOS_WIDGET_REM_BTN,7,56,94,11 +END + +IDD_COMP_GUIBUTTON DIALOG 0, 0, 106, 294 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_GUI_COMPSELBTN,"CustButton",WS_TABSTOP,42,17, + 55,12 + GROUPBOX "When clicked...",IDC_STATIC,5,6,97,104 + CONTROL "Animate",IDC_GUI_ANIMATE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,2,19,40,10 + CONTROL "Pick",IDC_GUI_COMPSELBTN2,"CustButton",WS_TABSTOP,43, + 126,53,12 + GROUPBOX "When highlighted...",IDC_STATIC,5,115,97,99 + CONTROL "Animate",IDC_GUI_MOUSEOVERANIM,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,1,127,41,10 + CONTROL "Pick",IDC_GUI_MDOWNSNDCOMP,"CustButton",WS_TABSTOP,9,64, + 88,12 + CONTROL "Play snd on mouse down",IDC_GUI_MDOWNSND,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,2,53,96,10 + CONTROL "Pick",IDC_GUI_MUPSNDCOMP,"CustButton",WS_TABSTOP,9,90, + 88,12 + CONTROL "Play snd on mouse up",IDC_GUI_MUPSND,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,2,79,96,10 + CONTROL "Pick",IDC_GUI_MOVERSNDCOMP,"CustButton",WS_TABSTOP,9, + 169,88,12 + CONTROL "Play snd on mouse over",IDC_GUI_MOVERSND,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,1,158,96,10 + CONTROL "Pick",IDC_GUI_MOFFSNDCOMP,"CustButton",WS_TABSTOP,9,195, + 88,12 + CONTROL "Play snd on mouse off",IDC_GUI_MOFFSND,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,1,184,96,10 + CONTROL "Pick",IDC_GUI_ANIMNODESEL,"CustButton",WS_TABSTOP,22,32, + 75,12 + LTEXT "on",IDC_STATIC,10,34,9,8 + CONTROL "Pick",IDC_GUI_ANIMNODESEL2,"CustButton",WS_TABSTOP,22, + 140,75,12 + LTEXT "on",IDC_STATIC,10,142,9,8 + CONTROL "Pick",IDC_GUI_DRAGCHILD,"CustButton",WS_TABSTOP,15,230, + 81,12 + GROUPBOX "Draggable child",IDC_STATIC,5,219,97,29 + CONTROL "",IDC_GUI_USEDRAGCHILD,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,2,232,9,8 + COMBOBOX IDC_COMBO_BUTTON_NOTIFYTYPE,12,263,82,50,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + GROUPBOX "Activate On...",IDC_STATIC_BUTTON_NOTIFY,5,251,96,35 +END + +IDD_COMP_DISTRIBUTOR DIALOG 0, 0, 110, 661 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST_TARGS,3,16,98,39,WS_VSCROLL | WS_TABSTOP + LTEXT "Replicants",IDC_STATIC,3,6,34,8 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,57,55,47, + 14 + CONTROL "Add",IDC_ADD_TARGS,"CustButton",WS_TABSTOP,4,55,47,14 + LTEXT "Spacing",IDC_STATIC,8,76,47,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_SPACING,"CustEdit",WS_TABSTOP, + 58,75,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SPACING_SPIN,"SpinnerControl", + WS_TABSTOP,86,75,7,10 + LTEXT "Space Offset %",IDC_STATIC,3,90,50,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_RNDPOSRADIUS,"CustEdit", + WS_TABSTOP,58,89,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_RNDPOSRADIUS_SPIN, + "SpinnerControl",WS_TABSTOP,86,89,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_ALIGNVECX,"CustEdit", + WS_TABSTOP,58,111,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ALIGNVECX_SPIN,"SpinnerControl", + WS_TABSTOP,86,111,7,10 + LTEXT "X",IDC_STATIC,46,112,8,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ALIGNVECY,"CustEdit", + WS_TABSTOP,58,122,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ALIGNVECY_SPIN,"SpinnerControl", + WS_TABSTOP,86,122,7,10 + LTEXT "Y",IDC_STATIC,46,124,8,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ALIGNVECZ,"CustEdit", + WS_TABSTOP,58,133,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ALIGNVECZ_SPIN,"SpinnerControl", + WS_TABSTOP,86,133,7,10 + LTEXT "Z",IDC_STATIC,46,134,8,8 + LTEXT "Norm deg var",IDC_STATIC,7,173,47,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_POLARRANGE,"CustEdit", + WS_TABSTOP,57,172,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_POLARRANGE_SPIN, + "SpinnerControl",WS_TABSTOP,85,172,7,10 + LTEXT "Spin deg var",IDC_STATIC,7,200,47,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_AZIMUTHRANGE,"CustEdit", + WS_TABSTOP,57,199,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_AZIMUTHRANGE_SPIN, + "SpinnerControl",WS_TABSTOP,85,199,7,10 + LTEXT "Overall prob",IDC_STATIC,7,216,47,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_OVERALLPROB,"CustEdit", + WS_TABSTOP,57,215,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_OVERALLPROB_SPIN, + "SpinnerControl",WS_TABSTOP,85,215,7,10 + LTEXT "Norm bunch",IDC_STATIC,6,185,47,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_POLARBUNCH,"CustEdit", + WS_TABSTOP,57,184,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_POLARBUNCH_SPIN, + "SpinnerControl",WS_TABSTOP,85,184,7,10 + GROUPBOX "Alignment",IDC_STATIC,8,103,93,66 + GROUPBOX "Scale Range",IDC_STATIC,2,228,106,61 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALELOX,"CustEdit", + WS_TABSTOP,17,250,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALELOX_SPIN,"SpinnerControl", + WS_TABSTOP,39,250,7,10 + LTEXT "X",IDC_STATIC,5,252,8,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALEHIX,"CustEdit", + WS_TABSTOP,57,250,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALEHIX_SPIN,"SpinnerControl", + WS_TABSTOP,79,250,7,10 + GROUPBOX "Min",IDC_STATIC,14,239,35,50 + GROUPBOX "Max",IDC_STATIC,54,239,38,50 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALELOY,"CustEdit", + WS_TABSTOP,17,261,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALELOY_SPIN,"SpinnerControl", + WS_TABSTOP,39,261,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALELOZ,"CustEdit", + WS_TABSTOP,17,272,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALELOZ_SPIN,"SpinnerControl", + WS_TABSTOP,39,272,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALEHIY,"CustEdit", + WS_TABSTOP,57,261,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALEHIY_SPIN,"SpinnerControl", + WS_TABSTOP,79,261,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALEHIZ,"CustEdit", + WS_TABSTOP,57,272,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALEHIZ_SPIN,"SpinnerControl", + WS_TABSTOP,79,272,7,10 + LTEXT "Weight",IDC_STATIC,21,155,27,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ALIGNWGT,"CustEdit", + WS_TABSTOP,57,154,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ALIGNWGT_SPIN,"SpinnerControl", + WS_TABSTOP,85,154,7,10 + LTEXT "Y",IDC_STATIC,5,262,8,8 + LTEXT "Z",IDC_STATIC,6,272,8,8 + CONTROL "",IDC_COMP_DISTRIB_PROBTEXMAP,"CustButton",WS_TABSTOP,5, + 306,100,13 + GROUPBOX "Probability Texmap",IDC_STATIC,4,298,103,103 + COMBOBOX IDC_COMP_DISTRIB_PROBCOLORCHAN,8,335,92,72,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Channel",IDC_STATIC,38,323,27,8 + CONTROL "Lock Randomization",IDC_COMP_DISTRIB_LOCK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,474,82,10 + GROUPBOX "Remap",IDC_STATIC,6,352,98,45 + LTEXT "Lo",IDC_STATIC,9,370,8,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_REMAPFROMLO,"CustEdit", + WS_TABSTOP,23,370,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_REMAPFROMLO_SPIN, + "SpinnerControl",WS_TABSTOP,51,370,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_REMAPTOLO,"CustEdit", + WS_TABSTOP,64,370,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_REMAPTOLO_SPIN,"SpinnerControl", + WS_TABSTOP,92,370,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_REMAPFROMHI,"CustEdit", + WS_TABSTOP,23,382,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_REMAPFROMHI_SPIN, + "SpinnerControl",WS_TABSTOP,51,382,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_REMAPTOHI,"CustEdit", + WS_TABSTOP,63,382,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_REMAPTOHI_SPIN,"SpinnerControl", + WS_TABSTOP,91,382,7,10 + LTEXT "Hi",IDC_STATIC,10,382,8,8 + LTEXT "From",IDC_STATIC,30,361,16,8 + LTEXT "To",IDC_STATIC,70,360,10,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ANGPROBX,"CustEdit", + WS_TABSTOP,58,409,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ANGPROBX_SPIN,"SpinnerControl", + WS_TABSTOP,86,409,7,10 + LTEXT "X",IDC_STATIC,46,411,8,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ANGPROBY,"CustEdit", + WS_TABSTOP,58,420,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ANGPROBY_SPIN,"SpinnerControl", + WS_TABSTOP,86,420,7,10 + LTEXT "Y",IDC_STATIC,46,423,8,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ANGPROBZ,"CustEdit", + WS_TABSTOP,58,432,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ANGPROBZ_SPIN,"SpinnerControl", + WS_TABSTOP,86,432,7,10 + LTEXT "Z",IDC_STATIC,46,433,8,8 + GROUPBOX "Orientation Dependency",IDC_STATIC,8,401,93,69 + LTEXT "Lots deg",IDC_STATIC,13,445,33,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ANGPROBHI,"CustEdit", + WS_TABSTOP,58,444,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ANGPROBHI_SPIN,"SpinnerControl", + WS_TABSTOP,86,444,7,10 + LTEXT "None deg",IDC_STATIC,14,457,33,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ANGPROBLO,"CustEdit", + WS_TABSTOP,58,456,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ANGPROBLO_SPIN,"SpinnerControl", + WS_TABSTOP,86,456,7,10 + PUSHBUTTON "Clear Preview",IDC_DISTRIB_CLEAR,30,613,47,14 + PUSHBUTTON "Preview",IDC_DISTRIB_PREVIEW,30,631,47,14 + GROUPBOX "Fade Out",IDC_STATIC,5,486,97,57 + LTEXT "Start",IDC_STATIC,19,494,16,8 + LTEXT "End",IDC_STATIC,66,495,14,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_FADEOUTOPAQ,"CustEdit", + WS_TABSTOP,12,503,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_FADEOUTOPAQ_SPIN, + "SpinnerControl",WS_TABSTOP,40,503,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_FADEOUTTRAN,"CustEdit", + WS_TABSTOP,56,503,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_FADEOUTTRAN_SPIN, + "SpinnerControl",WS_TABSTOP,84,503,7,10 + CONTROL "Fade In",IDC_COMP_DISTRIB_FADEINACTIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,515,82,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_FADEINTRAN,"CustEdit", + WS_TABSTOP,11,527,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_FADEINTRAN_SPIN, + "SpinnerControl",WS_TABSTOP,39,527,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_FADEINOPAQ,"CustEdit", + WS_TABSTOP,55,527,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_FADEINOPAQ_SPIN, + "SpinnerControl",WS_TABSTOP,83,527,7,10 + CONTROL "Pick",IDC_COMP_DISTRIB_WINDBONE,"CustButton",WS_TABSTOP, + 10,565,86,9 + GROUPBOX "",IDC_STATIC,4,546,98,34 + CTEXT "L",IDC_STATIC,93,245,10,9 + CONTROL "",IDC_COMP_DISTRIB_LOCKSCALEXY,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,93,256,10,10 + CONTROL "",IDC_COMP_DISTRIB_LOCKSCALEXYZ,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,93,267,10,10 + CONTROL "Wind Bone",IDC_COMP_DISTRIB_WINDBONEACTIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,553,51,10 + LTEXT "Isolation",IDC_STATIC,12,590,27,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ISOLATION,"CustEdit", + WS_TABSTOP,49,589,9,10 + CONTROL "Custom3",IDC_COMP_DISTRIB_ISOLATION_SPIN,"SpinnerControl", + WS_TABSTOP,59,589,7,10 +END + +IDD_COMP_XREGION DIALOG 0, 0, 108, 93 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST_SAFE,4,12,98,32,WS_VSCROLL | WS_TABSTOP + CONTROL "Remove",IDC_DEL_SAFE,"CustButton",WS_TABSTOP,54,40,47, + 14 + LTEXT "Safe Points:",-1,4,2,39,8 + CONTROL "Add",IDC_ADD_SAFE,"CustButton",WS_TABSTOP,4,40,47,14 + CONTROL "Initially cleared",IDC_CLEARED,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,58,62,10 + CONTROL "Use Smart Seek",IDC_SMARTSEEK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,67,83,10 + CONTROL "Block Camera LOS",IDC_CAMERA_LOS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,77,76,10 +END + +IDD_COMP_GUIDRAGGABLE DIALOG 0, 0, 106, 57 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Report while dragging",IDC_GUI_REPORTDRAG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,9,85,10 + CONTROL "Hide cursor while dragging",IDC_GUI_HIDECURSOR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,22,99,10 + CONTROL "Always snap back to start",IDC_GUI_SNAPSTART,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,35,97,10 +END + +IDD_COMP_CLUSTER DIALOGEX 0, 0, 111, 119 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + CONTROL "Custom1",IDC_COMP_CLUSTERSIZE,"CustEdit",WS_TABSTOP,67, + 11,19,10 + CONTROL "Custom3",IDC_COMP_CLUSTERSIZE_SPIN,"SpinnerControl", + WS_TABSTOP,87,11,7,10 + LTEXT "Optimization: \n(Larger value means more clusters of a smaller size)", + IDC_STATIC,1,5,65,34 + PUSHBUTTON "Go!",IDC_CLUSTER_DO_THE_DANCE,30,95,47,14 + PUSHBUTTON "Clear",IDC_CLUSTER_CLEAR,30,73,47,14 + CONTROL "Auto Export",IDC_COMP_CLUST_AUTOEXPORT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,41,53,10 + CONTROL "Auto Instance",IDC_COMP_CLUST_AUTOINSTANCE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,55,60,10 +END + +IDD_COMP_RESPOND_ENABLE DIALOG 0, 0, 108, 34 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Button1",IDC_RESPONDER_BUTTON,9,5,88,12 + CONTROL "Enable",IDC_ENABLE_CHECK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,9,20,38,10 +END + +IDD_COMP_GUILISTBOX DIALOG 0, 0, 106, 227 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_GUI_COMPSELBTN,"CustButton",WS_TABSTOP,7,111, + 91,12 + GROUPBOX " ",IDC_STATIC,3,100,99,27 + CONTROL "Scroll Control",IDC_GUI_SCROLLCTRL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,99,57,10 + CONTROL "Single Selection",IDC_GUI_SINGLESEL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,6,67,10 + CONTROL "Transparent Background",IDC_GUI_XPARENT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,39,95,10 + CONTROL "Drag && Drop Capable",IDC_GUI_DRAGDROPSRC,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,51,83,10 + CONTROL "Disable Key Actions",IDC_GUI_DISABLEKEYS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,63,79,10 + CONTROL "Allow 2D Element Grid",IDC_GUI_ALLOWMULTIROW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,75,86,10 + CONTROL "Scroll Left-to-Right",IDC_GUI_SCROLLL2R,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,87,74,10 + CONTROL "Maintain size across resolutions",IDC_GUI_SCALERES, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,4, + 132,98,17 + CONTROL "Pass clicks through empty parts",IDC_GUI_PASSTHRU, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,4, + 151,89,16 + CONTROL "Enable tree-view behavior",IDC_GUI_ENABLETREE,"Button", + BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,3,170,89,16 + CONTROL "Custom1",IDC_GUI_SKIN,"CustButton",WS_TABSTOP,9,201,88, + 12 + GROUPBOX "Skin",IDC_STATIC,3,190,99,29 + CONTROL "Pretend like ctrl is always down for multi-select", + IDC_GUI_HANDSOFF,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | + WS_TABSTOP,5,18,97,18 +END + +IDD_COMP_GUITEXTBOX DIALOGEX 0, 0, 106, 192 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + LTEXT "Initial Text:",IDC_STATIC,4,72,35,8 + CONTROL "Transparent Background",IDC_GUI_XPARENT,"Button", + BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,6,6,95,10 + CONTROL "Left",IDC_JUSTIFYRADIO,"Button",BS_AUTORADIOBUTTON | + BS_LEFT | BS_VCENTER | WS_GROUP,54,17,46,12 + CONTROL "Center",IDC_JUSTRADIO2,"Button",BS_AUTORADIOBUTTON | + BS_LEFT | BS_VCENTER,54,30,46,10 + CONTROL "Right",IDC_JUSTRADIO3,"Button",BS_AUTORADIOBUTTON | + BS_LEFT | BS_VCENTER,54,41,46,10 + LTEXT "Justification:",IDC_STATIC,6,30,40,8 + CONTROL "Maintain size across resolutions",IDC_GUI_SCALERES, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,6, + 52,94,17 + EDITTEXT IDC_GUI_INITTEXT,6,81,94,51,ES_MULTILINE | + ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL + LTEXT "Language:",IDC_STATIC,3,134,35,8 + COMBOBOX IDC_GUI_LANGUAGE,6,144,94,60,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Use Localization Mgr:",IDC_GUI_USE_LOCALIZATION,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,6,161,94,10 + EDITTEXT IDC_GUI_LOCALIZATION_PATH,6,172,77,14,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_GUI_SELECT_LOC_PATH,83,172,17,14 +END + +IDD_COMP_ANIM_EASE DIALOG 0, 0, 108, 165 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "None",IDC_COMP_ANIM_EASE_IN_NONE,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,5,13,33,10 + CONTROL "Constant Acceleration", + IDC_COMP_ANIM_EASE_IN_CONST_ACCEL,"Button", + BS_AUTORADIOBUTTON,5,23,85,10 + CONTROL "Spline",IDC_COMP_ANIM_EASE_IN_SPLINE,"Button", + BS_AUTORADIOBUTTON,5,33,35,10 + LTEXT "Time:",IDC_STATIC,9,46,18,8 + CONTROL "",IDC_COMP_ANIM_EASE_IN_TIME,"CustEdit",WS_TABSTOP,30, + 45,24,10 + CONTROL "",IDC_COMP_ANIM_EASE_IN_TIME_SPIN,"SpinnerControl", + WS_TABSTOP,54,45,7,10 + LTEXT "Ease In:",IDC_STATIC,5,3,30,8 + LTEXT "(secs)",IDC_STATIC,64,46,20,8 + LTEXT "Min:",IDC_STATIC,13,58,14,8 + CONTROL "",IDC_COMP_ANIM_EASE_IN_MIN,"CustEdit",WS_TABSTOP,30,57, + 24,10 + CONTROL "",IDC_COMP_ANIM_EASE_IN_MIN_SPIN,"SpinnerControl", + WS_TABSTOP,54,57,7,10 + LTEXT "(secs)",IDC_STATIC,64,58,20,8 + LTEXT "Max:",IDC_STATIC,11,70,16,8 + CONTROL "",IDC_COMP_ANIM_EASE_IN_MAX,"CustEdit",WS_TABSTOP,30,69, + 24,10 + CONTROL "",IDC_COMP_ANIM_EASE_IN_MAX_SPIN,"SpinnerControl", + WS_TABSTOP,54,69,7,10 + LTEXT "(secs)",IDC_STATIC,64,70,20,8 + CONTROL "None",IDC_COMP_ANIM_EASE_OUT_NONE,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,5,95,33,10 + CONTROL "Constant Acceleration", + IDC_COMP_ANIM_EASE_OUT_CONST_ACCEL,"Button", + BS_AUTORADIOBUTTON,5,105,85,10 + CONTROL "Spline",IDC_COMP_ANIM_EASE_OUT_SPLINE,"Button", + BS_AUTORADIOBUTTON,5,115,35,10 + LTEXT "Time:",IDC_STATIC,9,128,18,8 + CONTROL "",IDC_COMP_ANIM_EASE_OUT_TIME,"CustEdit",WS_TABSTOP,30, + 127,24,10 + CONTROL "",IDC_COMP_ANIM_EASE_OUT_TIME_SPIN,"SpinnerControl", + WS_TABSTOP,54,127,7,10 + LTEXT "Ease Out:",IDC_STATIC,5,85,32,8 + LTEXT "(secs)",IDC_STATIC,64,128,20,8 + LTEXT "Min:",IDC_STATIC,13,140,14,8 + CONTROL "",IDC_COMP_ANIM_EASE_OUT_MIN,"CustEdit",WS_TABSTOP,30, + 139,24,10 + CONTROL "",IDC_COMP_ANIM_EASE_OUT_MIN_SPIN,"SpinnerControl", + WS_TABSTOP,54,139,7,10 + LTEXT "(secs)",IDC_STATIC,64,140,20,8 + LTEXT "Max:",IDC_STATIC,11,152,16,8 + CONTROL "",IDC_COMP_ANIM_EASE_OUT_MAX,"CustEdit",WS_TABSTOP,30, + 151,24,10 + CONTROL "",IDC_COMP_ANIM_EASE_OUT_MAX_SPIN,"SpinnerControl", + WS_TABSTOP,54,151,7,10 + LTEXT "(secs)",IDC_STATIC,64,152,20,8 +END + +IDD_COMP_IGNORELITE DIALOG 0, 0, 110, 87 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Turn On",IDC_COMP_IGNORELITE_ON,30,9,47,14 + PUSHBUTTON "Turn Off",IDC_COMP_IGNORELITE_OFF,30,27,47,14 + PUSHBUTTON "Toggle",IDC_COMP_IGNORELITE_TOGGLE,30,45,47,14 + CONTROL "Selected only",IDC_COMP_IGNORELITE_SELECTED,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,19,65,59,10 +END + +IDD_COMP_GUIEDITBOX DIALOG 0, 0, 106, 40 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Transparent Background",IDC_GUI_XPARENT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,6,95,10 + CONTROL "Maintain size across resolutions",IDC_GUI_SCALERES, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,5, + 18,98,17 +END + +IDD_COMP_FLEXIBILITY DIALOG 0, 0, 110, 90 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Flex %",IDC_STATIC,8,9,47,8 + CONTROL "Custom1",IDC_COMP_FLEX_FLEX,"CustEdit",WS_TABSTOP,58,8, + 27,10 + CONTROL "Custom2",IDC_COMP_FLEX_FLEX_SPIN,"SpinnerControl", + WS_TABSTOP,86,8,7,10 + LTEXT "Randomness within Mesh",IDC_STATIC,8,26,82,8 + CONTROL "Custom1",IDC_COMP_FLEX_INTRA,"CustEdit",WS_TABSTOP,57, + 38,27,10 + CONTROL "Custom2",IDC_COMP_FLEX_INTRA_SPIN,"SpinnerControl", + WS_TABSTOP,85,38,7,10 + LTEXT "Randomness between Meshes",IDC_STATIC,6,54,98,8 + CONTROL "Custom1",IDC_COMP_FLEX_INTER,"CustEdit",WS_TABSTOP,57, + 67,27,10 + CONTROL "Custom2",IDC_COMP_FLEX_INTER_SPIN,"SpinnerControl", + WS_TABSTOP,85,67,7,10 +END + +IDD_COMP_GUIDIALOG DIALOG 0, 0, 106, 69 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + RTEXT "Name:",IDC_STATIC,6,24,22,8 + CONTROL "Modal",IDC_COMP_GUI_MODAL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,29,38,35,10 + CONTROL "Custom2",IDC_GUI_VERSION,"CustEdit",WS_TABSTOP,33,51,31, + 10 + CONTROL "Custom2",IDC_GUI_VERSION_SPIN,"SpinnerControl", + WS_TABSTOP,65,51,7,10 + LTEXT "Version:",IDC_STATIC,4,52,26,8 + COMBOBOX IDC_GUIDLG_NAME,31,22,70,144,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + RTEXT "Age:",IDC_STATIC,12,8,16,8 + COMBOBOX IDC_GUIDLG_AGE,31,5,70,144,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP +END + +IDD_COMP_CAMERAPAN DIALOG 0, 0, 112, 96 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX,"CustEdit",WS_TABSTOP,14, + 33,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX,"SpinnerControl", + WS_TABSTOP,45,33,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY,"CustEdit",WS_TABSTOP,13, + 67,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY,"SpinnerControl", + WS_TABSTOP,45,67,7,10 + CONTROL "Allow Up-Down Panning",IDC_Z_AXIS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,18,93,10 + CONTROL "Allow Left-Right Panning",IDC_X_AXIS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,52,93,10 + LTEXT "Degrees",IDC_STATIC,56,35,36,8 + LTEXT "Degrees",IDC_STATIC,56,69,36,8 +END + +IDD_COMP_BLOW DIALOG 0, 0, 111, 72 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_COMP_BLOW_STRENGTH,"CustEdit",WS_TABSTOP, + 49,13,31,10 + CONTROL "Custom3",IDC_COMP_BLOW_STRENGTH_SPIN,"SpinnerControl", + WS_TABSTOP,81,13,7,10 + LTEXT "Strength",IDC_STATIC,13,13,28,8 + CONTROL "Custom1",IDC_COMP_BLOW_SPEED,"CustEdit",WS_TABSTOP,49, + 26,31,10 + CONTROL "Custom3",IDC_COMP_BLOW_SPEED_SPIN,"SpinnerControl", + WS_TABSTOP,81,26,7,10 + LTEXT "Speed",IDC_STATIC,12,26,22,8 + CONTROL "Custom1",IDC_COMP_BLOW_FLUTTER,"CustEdit",WS_TABSTOP,49, + 39,31,10 + CONTROL "Custom3",IDC_COMP_BLOW_FLUTTER_SPIN,"SpinnerControl", + WS_TABSTOP,81,39,7,10 + LTEXT "Flutter",IDC_STATIC,13,39,20,8 + CONTROL "Custom1",IDC_COMP_BLOW_CONSTANCY,"CustEdit",WS_TABSTOP, + 49,51,31,10 + CONTROL "Custom3",IDC_COMP_BLOW_CONSTANCY_SPIN,"SpinnerControl", + WS_TABSTOP,81,51,7,10 + LTEXT "Constancy",IDC_STATIC,12,51,34,8 +END + +IDD_PYTHON_FILE_WARN DIALOG 0, 0, 233, 202 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | + WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Python Component Warning" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,91,183,50,14 + LTEXT "Some of the Python File components in this scene didn't load correctly.\n\nThis can be caused by 3 things:\n - A Python file is missing\n - A Python file is out of date\n - A Python file had errors (check PythonLog.txt)\n\nYou should not save this file.", + IDC_STATIC,7,7,169,74 + LTEXT "Bad Components:",IDC_STATIC,7,88,57,8 + CONTROL "List1",IDC_COMP_LIST,"SysListView32",LVS_REPORT | + WS_BORDER | WS_TABSTOP,7,97,215,81 +END + +IDD_COMP_XFORM DIALOG 0, 0, 109, 63 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN +END + +IDD_COMP_FIRSTPERSON_CAM DIALOG 0, 0, 110, 191 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Offset X:",IDC_STATIC,17,76,28,8 + LTEXT "Offset Z:",IDC_STATIC,17,102,28,8 + LTEXT "Offset Y:",IDC_STATIC,17,89,28,8 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX,"CustEdit",WS_TABSTOP,47, + 75,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX,"SpinnerControl", + WS_TABSTOP,78,75,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY,"CustEdit",WS_TABSTOP,47, + 87,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY,"SpinnerControl", + WS_TABSTOP,78,87,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETZ,"CustEdit",WS_TABSTOP,47, + 100,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETZ,"SpinnerControl", + WS_TABSTOP,78,100,7,10 + LTEXT "Position Offset",IDC_STATIC,7,48,62,8 + LTEXT "First Person Camera",IDC_STATIC,22,7,81,8 + LTEXT "Offset X:",IDC_STATIC,18,143,28,8 + LTEXT "Offset Z:",IDC_STATIC,18,170,28,8 + LTEXT "Offset Y:",IDC_STATIC,18,157,28,8 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX5,"CustEdit",WS_TABSTOP, + 48,143,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX5,"SpinnerControl", + WS_TABSTOP,79,143,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY4,"CustEdit",WS_TABSTOP, + 48,155,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY4,"SpinnerControl", + WS_TABSTOP,79,155,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETZ2,"CustEdit",WS_TABSTOP, + 48,167,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETZ2,"SpinnerControl", + WS_TABSTOP,79,167,7,10 + LTEXT "POA Offset",IDC_STATIC,7,117,62,8 + LTEXT "Do not edit offsets for the standard first person settings", + IDC_STATIC,7,20,96,19 + CONTROL "Offset in Worldspace",IDC_COMP_POA_WORLDSPACE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,127,82,10 + CONTROL "Offset in Worldspace",IDC_COMP_POS_WORLDSPACE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,57,82,10 +END + +IDD_COMP_BEHAVIOR_SITTING DIALOG 0, 0, 108, 94 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Detector:",IDC_STATIC,10,6,30,8 + PUSHBUTTON "Button1",IDC_DETECTOR,10,16,88,12 + CONTROL "Approach Front",IDC_SIT_APP_FRONT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,32,65,10 + CONTROL "Approach Left",IDC_SIT_APP_LEFT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,43,61,10 + CONTROL "Approach Right",IDC_SIT_APP_RIGHT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,54,65,10 + CONTROL "Disable Forward Ctrl",IDC_SIT_NOFORWARD,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,74,79,10 +END + +IDD_COMP_PHYS_CORE_GROUP DIALOG 0, 0, 108, 70 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_COMP_PHYS_GROUP_COMBO,6,12,95,195,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LISTBOX IDC_COMP_PHYS_GROUP_LIST,6,25,95,30,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Remove",IDC_COMP_PHYS_GROUP_REMOVE,6,56,95,11 + LTEXT "* these groups",IDC_GROUP_TEXT,6,1,95,8 +END + +IDD_COMP_GUIUDSCROLL DIALOG 0, 0, 106, 111 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_GUI_COMPSELBTN,"CustButton",WS_TABSTOP,7,16, + 91,12 + LTEXT "Up/Increase Control:",IDC_STATIC,8,5,67,8 + CONTROL "Pick",IDC_GUI_COMPSELBTN2,"CustButton",WS_TABSTOP,7,42, + 91,12 + LTEXT "Down/Decrease Control:",IDC_STATIC,8,31,80,8 + LTEXT "Lower:",IDC_STATIC,8,65,22,8 + CONTROL "Custom2",IDC_GUI_LOWER,"CustEdit",WS_TABSTOP,8,75,31,10 + CONTROL "Custom2",IDC_GUI_LOWER_SPIN,"SpinnerControl",WS_TABSTOP, + 40,75,7,10 + CONTROL "Custom2",IDC_GUI_UPPER,"CustEdit",WS_TABSTOP,59,75,31, + 10 + CONTROL "Custom2",IDC_GUI_UPPER_SPIN,"SpinnerControl",WS_TABSTOP, + 91,75,7,10 + LTEXT "Upper:",IDC_STATIC,60,65,22,8 + GROUPBOX "Range",IDC_STATIC,4,56,99,49 + LTEXT "Step:",IDC_STATIC,21,89,18,8 + CONTROL "Custom2",IDC_GUI_STEP,"CustEdit",WS_TABSTOP,41,89,31,10 + CONTROL "Custom2",IDC_GUI_STEP_SPIN,"SpinnerControl",WS_TABSTOP, + 73,89,7,10 +END + +IDD_COMP_NAV_LADDER DIALOG 0, 0, 108, 108 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_COMP_NAV_LADDER_COMBO,39,4,60,52,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "Custom1",IDC_COMP_NAV_LADDER_LOOPS_EDIT,"CustEdit", + WS_TABSTOP,39,21,28,10 + CONTROL "Custom2",IDC_COMP_NAV_LADDER_LOOPS_SPIN,"SpinnerControl", + WS_TABSTOP,67,21,7,10 + CONTROL "Up",IDC_RADIO_UP,"Button",BS_AUTORADIOBUTTON,39,36,25, + 10 + CONTROL "Down",IDC_RADIO_DOWN,"Button",BS_AUTORADIOBUTTON,66,36, + 35,10 + CONTROL "Pick Me!",IDC_COMP_NAV_TRIGGER,"CustButton",WS_TABSTOP, + 6,60,94,9 + CONTROL "Enabled",IDC_ENABLED,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,6,95,42,10 + LTEXT "Type:",IDC_STATIC,6,6,19,8 + LTEXT "Loops:",IDC_STATIC,6,22,22,8 + LTEXT "Trigger:",IDC_STATIC,6,50,25,8 + LTEXT "Direction:",IDC_STATIC,6,36,31,8 + CONTROL "Pick Me!",IDC_COMP_NAV_LADDER,"CustButton",WS_TABSTOP,6, + 82,94,9 + LTEXT "Ladder Bounds:",IDC_STATIC,6,73,51,8 +END + +IDD_COMP_RESPOND_CAMERA DIALOG 0, 0, 108, 51 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Camera:",-1,6,2,27,8 + CONTROL "Pick Me!",IDC_COMP_CAMERARGN_PICKSTATE_BASE,"CustButton", + WS_TABSTOP,5,12,84,10 + CONTROL "Pop this camera",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,6,31,70,10 +END + +IDD_COMP_TEMPLATE DIALOG 0, 0, 108, 47 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_NAME,"CustEdit",WS_TABSTOP,15,14,77,10 + LTEXT "Template Name:",IDC_STATIC,15,3,53,8 + LTEXT "(Name of the object the Clone Template is attached to)", + IDC_STATIC,7,27,94,17 +END + +IDD_COMP_GUIKNOB DIALOG 0, 0, 106, 212 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Lower:",IDC_STATIC,7,65,22,8 + CONTROL "Custom2",IDC_GUI_LOWER,"CustEdit",WS_TABSTOP,7,75,31,10 + CONTROL "Custom2",IDC_GUI_LOWER_SPIN,"SpinnerControl",WS_TABSTOP, + 39,75,7,10 + CONTROL "Custom2",IDC_GUI_UPPER,"CustEdit",WS_TABSTOP,58,75,31, + 10 + CONTROL "Custom2",IDC_GUI_UPPER_SPIN,"SpinnerControl",WS_TABSTOP, + 90,75,7,10 + LTEXT "Upper:",IDC_STATIC,59,65,22,8 + GROUPBOX "Range",IDC_STATIC,3,56,99,49 + LTEXT "Step:",IDC_STATIC,20,89,18,8 + CONTROL "Custom2",IDC_GUI_STEP,"CustEdit",WS_TABSTOP,40,89,31,10 + CONTROL "Custom2",IDC_GUI_STEP_SPIN,"SpinnerControl",WS_TABSTOP, + 72,89,7,10 + CONTROL "Reverse values",IDC_GUI_REVERSE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,109,65,10 + CONTROL "Up/Down",IDC_ORIENTATION_RADIO,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,6,143,47,10 + GROUPBOX "Orientation",IDC_STATIC,3,133,99,24 + CONTROL "Left/Right",IDC_ORIENTATION_RADIO2,"Button", + BS_AUTORADIOBUTTON,53,143,46,10 + CONTROL "Only trigger proc on release",IDC_GUI_TRIGGERONUP, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,121,102,10 + GROUPBOX "Mouse Mapping",IDC_STATIC,3,159,99,48 + CONTROL "Relative",IDC_GUI_MOUSEMAPREL,"Button", + BS_AUTORADIOBUTTON,8,171,42,10 + CONTROL "Map to animation range",IDC_GUI_MOUSEMAPANIM,"Button", + BS_AUTORADIOBUTTON,8,182,90,10 + CONTROL "Map to screen range",IDC_GUI_MOUSEMAPSCRN,"Button", + BS_AUTORADIOBUTTON,8,193,81,10 + CONTROL "Pick",IDC_GUI_COMPSELBTN,"CustButton",WS_TABSTOP,8,17, + 88,12 + CONTROL "Pick",IDC_GUI_ANIMNODESEL,"CustButton",WS_TABSTOP,21,32, + 75,12 + LTEXT "on",IDC_STATIC,9,34,9,8 + GROUPBOX "Animation",IDC_STATIC,3,4,99,49 +END + +IDD_COMP_GUIDRAGBAR DIALOG 0, 0, 106, 57 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN +END + +IDD_COMP_DIST_SPACING DIALOG 0, 0, 110, 81 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Spacing",IDC_STATIC,8,7,47,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_SPACING,"CustEdit",WS_TABSTOP, + 58,6,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SPACING_SPIN,"SpinnerControl", + WS_TABSTOP,86,6,7,10 + LTEXT "Density",IDC_STATIC,7,21,47,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_DENSITY,"CustEdit",WS_TABSTOP, + 58,20,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_DENSITY_SPIN,"SpinnerControl", + WS_TABSTOP,86,20,7,10 + LTEXT "Randomness",IDC_STATIC,3,34,42,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_SPACERND,"CustEdit", + WS_TABSTOP,58,34,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SPACERND_SPIN,"SpinnerControl", + WS_TABSTOP,86,34,7,10 + COMBOBOX IDC_COMP_DISTRIB_SEPARATION,7,59,92,66,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Separation",IDC_STATIC,25,47,35,8 +END + +IDD_COMP_DIST_ORIENT DIALOG 0, 0, 110, 58 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Align to Surface",IDC_STATIC,5,8,51,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ALIGNWGT,"CustEdit", + WS_TABSTOP,63,6,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ALIGNWGT_SPIN,"SpinnerControl", + WS_TABSTOP,91,6,7,10 + LTEXT "Norm range deg",IDC_STATIC,5,20,53,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_POLARRANGE,"CustEdit", + WS_TABSTOP,63,19,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_POLARRANGE_SPIN, + "SpinnerControl",WS_TABSTOP,91,19,7,10 + LTEXT "Spin range deg",IDC_STATIC,5,33,50,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_AZIMUTHRANGE,"CustEdit", + WS_TABSTOP,63,33,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_AZIMUTHRANGE_SPIN, + "SpinnerControl",WS_TABSTOP,91,33,7,10 + CONTROL "Use Surface Face Normals",IDC_COMP_DISTRIB_FACENORMALS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,45,99,10 +END + +IDD_COMP_DIST_ANGPROB DIALOG 0, 0, 109, 59 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Angle from UP (degrees)",IDC_STATIC,10,6,78,8 + LTEXT "Start ",IDC_STATIC,14,18,33,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ANGPROBLO,"CustEdit", + WS_TABSTOP,63,18,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ANGPROBLO_SPIN,"SpinnerControl", + WS_TABSTOP,91,18,7,10 + LTEXT "End ",IDC_STATIC,14,31,33,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ANGPROBHI,"CustEdit", + WS_TABSTOP,63,31,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ANGPROBHI_SPIN,"SpinnerControl", + WS_TABSTOP,91,31,7,10 + LTEXT "Transition",IDC_STATIC,13,45,46,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ANGPROBTRANS,"CustEdit", + WS_TABSTOP,63,44,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ANGPROBTRANS_SPIN, + "SpinnerControl",WS_TABSTOP,91,44,7,10 +END + +IDD_COMP_DIST_ALTPROB DIALOG 0, 0, 111, 60 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Elevation in feet",IDC_STATIC,10,6,52,8 + LTEXT "Start ",IDC_STATIC,14,18,33,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ALTPROBLO,"CustEdit", + WS_TABSTOP,63,18,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ALTPROBLO_SPIN,"SpinnerControl", + WS_TABSTOP,91,18,7,10 + LTEXT "End ",IDC_STATIC,14,31,33,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ALTPROBHI,"CustEdit", + WS_TABSTOP,63,31,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ALTPROBHI_SPIN,"SpinnerControl", + WS_TABSTOP,91,31,7,10 + LTEXT "Transition ",IDC_STATIC,13,45,46,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_ALTPROBTRANS,"CustEdit", + WS_TABSTOP,63,44,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_ALTPROBTRANS_SPIN, + "SpinnerControl",WS_TABSTOP,91,44,7,10 +END + +IDD_COMP_DIST_CONFORM DIALOG 0, 0, 109, 80 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_COMP_DISTRIB_CONFORMTYPE,7,20,92,66,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Max Adjust (ft)",IDC_STATIC,5,37,51,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_CONFORMMAX,"CustEdit", + WS_TABSTOP,63,36,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_CONFORMMAX_SPIN, + "SpinnerControl",WS_TABSTOP,91,36,7,10 + LTEXT "Surf Offset Min",IDC_STATIC,5,53,51,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_OFFSETMIN,"CustEdit", + WS_TABSTOP,63,53,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_OFFSETMIN_SPIN,"SpinnerControl", + WS_TABSTOP,91,53,7,10 + LTEXT "Surf Offset Max",IDC_STATIC,5,65,51,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_OFFSETMAX,"CustEdit", + WS_TABSTOP,63,65,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_OFFSETMAX_SPIN,"SpinnerControl", + WS_TABSTOP,91,65,7,10 + GROUPBOX "Conform to surface",IDC_STATIC,3,7,100,43 +END + +IDD_COMP_DIST_BITMAP DIALOG 0, 0, 110, 118 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_COMP_DISTRIB_PROBTEXMAP,"CustButton",WS_TABSTOP,5, + 16,100,13 + GROUPBOX "Probability Texmap",IDC_STATIC,4,8,103,103 + COMBOBOX IDC_COMP_DISTRIB_PROBCOLORCHAN,8,45,92,72,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Channel",IDC_STATIC,38,33,27,8 + GROUPBOX "Remap",IDC_STATIC,6,62,98,45 + LTEXT "Lo",IDC_STATIC,9,80,8,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_REMAPFROMLO,"CustEdit", + WS_TABSTOP,23,80,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_REMAPFROMLO_SPIN, + "SpinnerControl",WS_TABSTOP,51,80,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_REMAPTOLO,"CustEdit", + WS_TABSTOP,64,80,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_REMAPTOLO_SPIN,"SpinnerControl", + WS_TABSTOP,92,80,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_REMAPFROMHI,"CustEdit", + WS_TABSTOP,23,92,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_REMAPFROMHI_SPIN, + "SpinnerControl",WS_TABSTOP,51,92,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_REMAPTOHI,"CustEdit", + WS_TABSTOP,63,92,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_REMAPTOHI_SPIN,"SpinnerControl", + WS_TABSTOP,91,92,7,10 + LTEXT "Hi",IDC_STATIC,10,92,8,8 + LTEXT "From",IDC_STATIC,30,71,16,8 + LTEXT "To",IDC_STATIC,70,70,10,8 +END + +IDD_COMP_DIST_RANDOMIZE DIALOG 0, 0, 110, 42 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Lock Randomization",IDC_COMP_DISTRIB_LOCK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,7,82,10 + LTEXT "Seed",IDC_STATIC,8,23,23,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_SEED,"CustEdit",WS_TABSTOP,32, + 22,64,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SEED_SPIN,"SpinnerControl", + WS_TABSTOP,99,22,7,10 +END + +IDD_COMP_DIST_FADE DIALOG 0, 0, 106, 68 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Fade Out",IDC_STATIC,5,3,97,57 + LTEXT "Start",IDC_STATIC,19,11,16,8 + LTEXT "End",IDC_STATIC,66,12,14,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_FADEOUTOPAQ,"CustEdit", + WS_TABSTOP,12,20,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_FADEOUTOPAQ_SPIN, + "SpinnerControl",WS_TABSTOP,40,20,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_FADEOUTTRAN,"CustEdit", + WS_TABSTOP,56,20,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_FADEOUTTRAN_SPIN, + "SpinnerControl",WS_TABSTOP,84,20,7,10 + CONTROL "Fade In",IDC_COMP_DISTRIB_FADEINACTIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,32,82,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_FADEINTRAN,"CustEdit", + WS_TABSTOP,11,44,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_FADEINTRAN_SPIN, + "SpinnerControl",WS_TABSTOP,39,44,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_FADEINOPAQ,"CustEdit", + WS_TABSTOP,55,44,27,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_FADEINOPAQ_SPIN, + "SpinnerControl",WS_TABSTOP,83,44,7,10 +END + +IDD_COMP_DIST_WIND DIALOG 0, 0, 105, 35 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_DISTRIB_WINDBONE,"CustButton",WS_TABSTOP, + 10,19,86,9 + CONTROL "Wind Bone",IDC_COMP_DISTRIB_WINDBONEACTIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,7,51,10 +END + +IDD_COMP_DIST_TEMPLATES DIALOG 0, 0, 112, 87 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST_TARGS,5,14,98,45,WS_VSCROLL | WS_TABSTOP + LTEXT "Replicants",IDC_STATIC,6,4,34,8 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,60,62,47, + 14 + CONTROL "Add",IDC_ADD_TARGS,"CustButton",WS_TABSTOP,7,62,47,14 +END + +IDD_COMP_DIST_SCALE DIALOG 0, 0, 110, 73 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Scale Range",IDC_STATIC,2,5,106,61 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALELOX,"CustEdit", + WS_TABSTOP,17,27,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALELOX_SPIN,"SpinnerControl", + WS_TABSTOP,39,27,7,10 + LTEXT "X",IDC_STATIC,5,29,8,8 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALEHIX,"CustEdit", + WS_TABSTOP,57,27,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALEHIX_SPIN,"SpinnerControl", + WS_TABSTOP,79,27,7,10 + GROUPBOX "Min",IDC_STATIC,14,15,35,50 + GROUPBOX "Max",IDC_STATIC,54,15,38,50 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALELOY,"CustEdit", + WS_TABSTOP,17,38,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALELOY_SPIN,"SpinnerControl", + WS_TABSTOP,39,38,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALELOZ,"CustEdit", + WS_TABSTOP,17,49,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALELOZ_SPIN,"SpinnerControl", + WS_TABSTOP,39,49,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALEHIY,"CustEdit", + WS_TABSTOP,57,38,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALEHIY_SPIN,"SpinnerControl", + WS_TABSTOP,79,38,7,10 + CONTROL "Custom1",IDC_COMP_DISTRIB_SCALEHIZ,"CustEdit", + WS_TABSTOP,57,49,21,10 + CONTROL "Custom2",IDC_COMP_DISTRIB_SCALEHIZ_SPIN,"SpinnerControl", + WS_TABSTOP,79,49,7,10 + LTEXT "Y",IDC_STATIC,5,39,8,8 + LTEXT "Z",IDC_STATIC,6,49,8,8 + CTEXT "L",IDC_STATIC,93,22,10,9 + CONTROL "",IDC_COMP_DISTRIB_LOCKSCALEXY,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,93,33,10,10 + CONTROL "",IDC_COMP_DISTRIB_LOCKSCALEXYZ,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,93,44,10,10 +END + +IDD_COMP_DIST_ACTION DIALOG 0, 0, 105, 48 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Clear Preview",IDC_DISTRIB_CLEAR,29,7,47,14 + PUSHBUTTON "Preview",IDC_DISTRIB_PREVIEW,29,25,47,14 +END + +IDD_COMP_GUIRADIOGROUP DIALOG 0, 0, 106, 118 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_GUI_CHECKLIST,6,11,93,51,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_GUI_ADDCHECK,7,66,44,14 + CONTROL "Remove",IDC_GUI_DELCHECK,"CustButton",WS_TABSTOP,54,66, + 45,14 + GROUPBOX "Check Boxes",IDC_STATIC,3,2,99,82 + CONTROL "Allow no selection",IDC_GUI_ALLOWNONE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,4,88,71,10 + LTEXT "Default Selection:",IDC_STATIC,4,102,57,8 + CONTROL "Custom2",IDC_GUI_DEFSEL,"CustEdit",WS_TABSTOP,62,101,31, + 10 + CONTROL "Custom2",IDC_GUI_DEFSEL_SPIN,"SpinnerControl", + WS_TABSTOP,94,101,7,10 +END + +IDD_COMP_GUIPROCROLLOUT DIALOG 0, 0, 106, 165 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Run a console command:",IDC_GUI_CONRADIO,"Button", + BS_AUTORADIOBUTTON,3,7,97,10 + CONTROL "Custom1",IDC_GUI_CONCMD,"CustEdit",WS_TABSTOP,13,21,83, + 10 + CONTROL "Inherit from owning dialog",IDC_GUI_INHERITRADIO,"Button", + BS_AUTORADIOBUTTON,3,36,96,10 + LTEXT "There are certain cases where the settings here can be overridden. For example, dedicated C++ code and Python scripts can set their own handlers on controls, in which case these settings would be meaningless.", + IDC_STATIC,9,82,87,75 + GROUPBOX "Note:",IDC_STATIC,4,73,98,86 + CONTROL "Close owning dialog",IDC_GUI_CLOSERADIO,"Button", + BS_AUTORADIOBUTTON,3,48,79,10 + CONTROL "Do nothing",IDC_GUI_NILRADIO,"Button", + BS_AUTORADIOBUTTON,3,60,51,10 +END + +IDD_COMP_GUIDYNDISPLAY DIALOG 0, 0, 106, 35 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Dynamic Layer",IDC_STATIC,4,3,97,27 + PUSHBUTTON "Pick",IDC_GUI_PICKMAT,7,14,90,13 +END + +IDD_COMP_LODFADE DIALOG 0, 0, 105, 89 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_LODFADE_BASE,"CustButton",WS_TABSTOP,10, + 19,86,9 + CONTROL "Base/Imposter",IDC_COMP_LODFADE_HASBASE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,7,62,10 + LTEXT "Distance (ft)",IDC_STATIC,8,36,47,8 + CONTROL "Custom1",IDC_COMP_LODFADE_DISTANCE,"CustEdit", + WS_TABSTOP,58,35,27,10 + CONTROL "Custom2",IDC_COMP_LODFADE_DISTANCE_SPIN,"SpinnerControl", + WS_TABSTOP,86,35,7,10 + LTEXT "Transition (ft)",IDC_STATIC,7,48,47,8 + CONTROL "Custom1",IDC_COMP_LODFADE_TRANSITION,"CustEdit", + WS_TABSTOP,57,47,27,10 + CONTROL "Custom2",IDC_COMP_LODFADE_TRANSITION_SPIN, + "SpinnerControl",WS_TABSTOP,85,47,7,10 + CONTROL "Fade Out Imposter",IDC_COMP_LODFADE_FADEBASE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,61,74,10 + CONTROL "Imposter is Backdrop",IDC_COMP_LODFADE_BASEFIRST,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,72,82,10 +END + +IDD_COMP_RESPOND_DELAY DIALOG 0, 0, 108, 50 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_DELAY_EDIT,"CustEdit",WS_TABSTOP,37,6,27, + 10 + CONTROL "Custom2",IDC_DELAY_SPIN,"SpinnerControl",WS_TABSTOP,64, + 6,7,10 + LTEXT "Time:",IDC_STATIC,17,7,18,8 + LTEXT "secs",IDC_STATIC,74,7,16,8 + LTEXT "Note: Another command needs to wait on this for it to be useful.", + IDC_STATIC,9,20,88,25 +END + +IDD_PICK_MTL DIALOG 0, 0, 122, 170 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | + WS_VISIBLE | WS_CAPTION +CAPTION "Pick Material" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,8,154,50,14 + LISTBOX IDC_MTL_LIST,4,4,114,152,LBS_SORT | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "Cancel",IDCANCEL,62,154,50,14 +END + +IDD_COMP_CLOTHING DIALOG 0, 0, 108, 209 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_CLOTHING_LIST,5,11,96,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_CLOTHING_ADD,5,52,47,14 + PUSHBUTTON "Remove",IDC_CLOTHING_REMOVE,54,52,47,14 + LTEXT "Clothing Materials:",IDC_STATIC,4,2,58,8 + COMBOBOX IDC_CLOTHING_GROUP,5,84,96,50,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP + LTEXT "Clothing Group:",IDC_STATIC,5,74,50,8 + COMBOBOX IDC_CLOTHING_TYPE,5,113,96,50,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP + LTEXT "Clothing Type:",IDC_STATIC,5,102,46,8 + GROUPBOX "LOD Properties",IDC_STATIC,5,131,96,74 + COMBOBOX IDC_COMP_LOD_CLOTHING_STATE,11,157,83,55, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "LOD State:",IDC_STATIC,11,146,36,8 + LTEXT "Mesh:",IDC_STATIC,11,177,21,8 + CONTROL "Replace",IDC_COMP_LOD_CLOTHING_MESH_PICKB,"CustButton", + WS_TABSTOP,11,188,83,10 + PUSHBUTTON "Clear",IDC_CLOTHING_CLEARMESH,67,175,27,11 +END + +IDD_COMP_RESPOND_CAMVIEW DIALOG 0, 0, 108, 70 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Button1",IDC_RESPONDER_COMP,3,13,101,12 + LTEXT "Component:",IDC_STATIC,3,3,39,8 + PUSHBUTTON "Button1",IDC_RESPONDER_NODE,3,39,101,12 + LTEXT "Node:",IDC_STATIC,3,30,20,8 + CONTROL "Enable",IDC_ENABLE_CHECK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,56,38,10 +END + +IDD_COMP_CAMERA_RAIL DIALOG 0, 0, 110, 225 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Animation path object ",IDC_STATIC,3,5,101,25 + CONTROL "Pick",IDC_COMP_LINE_CHOOSE_PATH,"CustButton",WS_TABSTOP, + 10,15,86,9 + CONTROL "Go to farthest point",IDC_CHECK1,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,34,84,10 + LTEXT "Acceleration ft/s",IDC_STATIC,1,61,53,8 + CONTROL "Custom1",IDC_CAMERA_ACCEL,"CustEdit",WS_TABSTOP,1,69,30, + 10 + CONTROL "Custom2",IDC_CAMERA_ACCEL_SPIN,"SpinnerControl", + WS_TABSTOP,33,69,7,10 + LTEXT "Max Velocity ft/s",IDC_STATIC,1,84,54,8 + CONTROL "Custom1",IDC_CAMERA_VEL,"CustEdit",WS_TABSTOP,1,93,30, + 10 + CONTROL "Custom2",IDC_CAMERA_VEL_SPIN,"SpinnerControl", + WS_TABSTOP,33,93,7,10 + LTEXT "Deceleration ft/s",IDC_STATIC,1,107,54,8 + CONTROL "Custom1",IDC_CAMERA_DECEL,"CustEdit",WS_TABSTOP,1,116, + 30,10 + CONTROL "Custom2",IDC_CAMERA_DECEL_SPIN,"SpinnerControl", + WS_TABSTOP,33,116,7,10 + LTEXT "Acceleration ft/s",IDC_STATIC,2,145,53,8 + CONTROL "Custom1",IDC_CAMERA_ACCEL2,"CustEdit",WS_TABSTOP,2,153, + 30,10 + CONTROL "Custom2",IDC_CAMERA_ACCEL_SPIN2,"SpinnerControl", + WS_TABSTOP,34,153,7,10 + LTEXT "Max Velocity ft/s",IDC_STATIC,2,169,54,8 + CONTROL "Custom1",IDC_CAMERA_VEL2,"CustEdit",WS_TABSTOP,2,177,30, + 10 + CONTROL "Custom2",IDC_CAMERA_VEL_SPIN2,"SpinnerControl", + WS_TABSTOP,34,177,7,10 + LTEXT "Deceleration ft/s",IDC_STATIC,2,190,54,8 + CONTROL "Custom1",IDC_CAMERA_DECEL2,"CustEdit",WS_TABSTOP,2,201, + 30,10 + CONTROL "Custom2",IDC_CAMERA_DECEL_SPIN2,"SpinnerControl", + WS_TABSTOP,34,201,7,10 + GROUPBOX "POA Speeds",IDC_STATIC,0,134,70,84 + GROUPBOX "Rail Position Speeds",IDC_STATIC,0,51,68,81 +END + +IDD_COMP_MULTIBEH_NORMAL DIALOG 0, 0, 108, 279 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_ANIM_NAME,"CustEdit",WS_TABSTOP,7,14,93,10 + CONTROL "Custom1",IDC_NUM_LOOPS_EDIT,"CustEdit",WS_TABSTOP,51,38, + 34,10 + CONTROL "Custom2",IDC_NUM_LOOPS_SPIN,"SpinnerControl",WS_TABSTOP, + 85,38,7,10 + CONTROL "Loop Forever",IDC_LOOP_FOREVER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,10,50,58,10 + COMBOBOX IDC_FORWARD_COMBO,44,68,57,90,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_BACKWARD,44,85,57,90,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_ADVANCE,44,103,57,90,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_REGRESS,44,120,57,90,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Enter",IDC_CHECK_ENTER,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,10,212,33,10 + CONTROL "Loop",IDC_CHECK_LOOP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,10,223,32,10 + CONTROL "Stage Advance",IDC_CHECK_ADVANCE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,234,65,10 + CONTROL "Stage Regress",IDC_CHECK_REGRESS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,245,63,10 + CONTROL "Use global coordinates",IDC_GLOBAL_COORD,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,262,88,10 + LTEXT "Anim Name:",IDC_STATIC,7,4,39,8 + LTEXT "Num Loops:",IDC_STATIC,10,38,39,8 + LTEXT "Forward:",IDC_STATIC,6,70,28,8 + LTEXT "Backward:",IDC_STATIC,6,87,35,8 + LTEXT "Stage Advance:",IDC_STATIC,6,100,33,17 + LTEXT "Stage Regress:",IDC_STATIC,6,118,31,17 + GROUPBOX "Notify",IDC_STATIC,4,202,99,56 + GROUPBOX "Looping",IDC_STATIC,4,27,99,36 + CONTROL "Custom1",IDC_ADVANCE_STAGE_EDIT,"CustEdit",WS_TABSTOP, + 51,150,34,10 + CONTROL "Custom2",IDC_ADVANCE_STAGE_SPIN,"SpinnerControl", + WS_TABSTOP,85,150,7,10 + CONTROL "Advance To:",IDC_DO_ADVANCE_TO,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,10,139,57,10 + CONTROL "Custom1",IDC_REGRESS_STAGE_EDIT,"CustEdit",WS_TABSTOP, + 52,185,34,10 + CONTROL "Custom2",IDC_REGRESS_STAGE_SPIN,"SpinnerControl", + WS_TABSTOP,86,185,7,10 + CONTROL "Regress To:",IDC_DO_REGRESS_TO,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,10,172,55,10 + LTEXT "Stage:",IDC_STATIC,25,150,22,8 + LTEXT "Stage:",IDC_STATIC,25,185,22,8 +END + +IDD_COMP_MULTIBEH DIALOG 0, 0, 108, 133 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "List1",IDC_STAGE_LIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_EDITLABELS | + LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,2,2,104,53 + PUSHBUTTON "Add...",IDC_ADD,2,56,51,14 + PUSHBUTTON "Remove",IDC_REMOVE,55,56,51,14 + CONTROL "Freeze Physical at End",IDC_FREEZE_PHYS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,90,87,10 + CONTROL "Use Smart Seek",IDC_SMART_SEEK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,76,87,10 + CONTROL "Reverse Forward/Back",IDC_MULTI_REVERSE_CTL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,105,87,8 + LTEXT "On Key Release",IDC_STATIC,19,114,75,8 +END + +IDD_COMP_BLENDONTO DIALOG 0, 0, 111, 95 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Blend onto who? (Base nodes)",IDC_STATIC,4,9,98,8 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,59,50,47, + 14 + CONTROL "Add",IDC_ADD_TARGS,"CustButton",WS_TABSTOP,5,50,47,14 + LISTBOX IDC_LIST_TARGS,5,24,98,26,WS_VSCROLL | WS_TABSTOP + CONTROL "Sort Faces",IDC_COMP_BLENDONTO_SORTFACES,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,78,50,10 +END + +IDD_COMP_GUISCHEME DIALOG 0, 0, 106, 192 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Default Colors",IDC_STATIC,4,3,98,42 + LTEXT "Fg:",IDC_STATIC,8,17,11,8 + CONTROL "Custom1",IDC_GUI_FGCOLOR,"ColorSwatch",WS_TABSTOP,20,15, + 29,12 + LTEXT "Bg:",IDC_STATIC,54,17,12,8 + CONTROL "Custom1",IDC_GUI_BGCOLOR,"ColorSwatch",WS_TABSTOP,66,15, + 29,12 + GROUPBOX "Selected Colors",IDC_STATIC,4,47,98,42 + LTEXT "Fg:",IDC_STATIC,8,61,11,8 + CONTROL "Custom1",IDC_GUI_SFGCOLOR,"ColorSwatch",WS_TABSTOP,20, + 59,29,12 + LTEXT "Bg:",IDC_STATIC,54,61,12,8 + CONTROL "Custom1",IDC_GUI_SBGCOLOR,"ColorSwatch",WS_TABSTOP,66, + 59,29,12 + CONTROL "Use Alphas",IDC_GUI_USEALPHAS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,5,92,53,10 + GROUPBOX "Font",IDC_STATIC,4,132,98,55 + COMBOBOX IDC_GUI_FONTFACE,9,143,88,138,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "Custom1",IDC_GUI_FONTSIZE,"CustEdit",WS_TABSTOP,9,161, + 20,10 + CONTROL "Bold",IDC_GUI_FONTBOLD,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,41,161,28,10 + CONTROL "Italic",IDC_GUI_FONTITALIC,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,72,161,28,10 + CONTROL "Custom2",IDC_GUI_FONTSIZE_SPIN,"SpinnerControl", + WS_TABSTOP,30,161,7,10 + CONTROL "",IDC_GUI_FGALPHA,"SliderControl",WS_TABSTOP,8,30,41,10 + CONTROL "",IDC_GUI_BGALPHA,"SliderControl",WS_TABSTOP,54,30,41, + 10 + CONTROL "",IDC_GUI_SFGALPHA,"SliderControl",WS_TABSTOP,8,74,41, + 10 + CONTROL "",IDC_GUI_SBGALPHA,"SliderControl",WS_TABSTOP,54,74,41, + 10 + CONTROL "Custom1",IDC_GUI_FGAEDIT,"CustEdit",NOT WS_VISIBLE | + WS_TABSTOP,64,91,6,10 + CONTROL "Custom1",IDC_GUI_BGAEDIT,"CustEdit",NOT WS_VISIBLE | + WS_TABSTOP,72,91,6,10 + CONTROL "Custom1",IDC_GUI_SFGAEDIT,"CustEdit",NOT WS_VISIBLE | + WS_TABSTOP,80,91,6,10 + CONTROL "Custom1",IDC_GUI_SBGAEDIT,"CustEdit",NOT WS_VISIBLE | + WS_TABSTOP,88,91,6,10 + GROUPBOX "Preview",IDC_STATIC,4,103,98,27 + CONTROL "",IDC_GUI_SCHEMEPREV,"Static",SS_BLACKFRAME | NOT + WS_VISIBLE,8,113,90,13 + CONTROL "Shadowed",IDC_GUI_FONTSHADOWED,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,41,173,50,10 +END + +IDD_COMP_CAMERA_CIRCLE DIALOG 0, 0, 110, 166 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Go to farthest point",IDC_CHECK1,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,1,2,84,10 + LTEXT "Positional Speed (circumference / second)",IDC_STATIC,5, + 110,57,24 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX3,"CustEdit",WS_TABSTOP,6, + 142,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX3,"SpinnerControl", + WS_TABSTOP,38,142,7,10 + LTEXT "Acceleration ft/s",IDC_STATIC,5,28,53,8 + CONTROL "Custom1",IDC_CAMERA_ACCEL,"CustEdit",WS_TABSTOP,5,36,30, + 10 + CONTROL "Custom2",IDC_CAMERA_ACCEL_SPIN,"SpinnerControl", + WS_TABSTOP,37,36,7,10 + LTEXT "Max Velocity ft/s",IDC_STATIC,5,52,54,8 + CONTROL "Custom1",IDC_CAMERA_VEL,"CustEdit",WS_TABSTOP,5,60,30, + 10 + CONTROL "Custom2",IDC_CAMERA_VEL_SPIN,"SpinnerControl", + WS_TABSTOP,37,60,7,10 + LTEXT "Deceleration ft/s",IDC_STATIC,5,74,54,8 + CONTROL "Custom1",IDC_CAMERA_DECEL,"CustEdit",WS_TABSTOP,5,84,30, + 10 + CONTROL "Custom2",IDC_CAMERA_DECEL_SPIN,"SpinnerControl", + WS_TABSTOP,37,84,7,10 + GROUPBOX "POA Speeds",IDC_STATIC,3,18,70,84 +END + +IDD_COMP_CLIMB_BLOCK DIALOG 0, 0, 111, 105 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Avatars using the climbing brain will refuse to climb through this geometry.", + IDC_STATIC,9,19,92,30 + GROUPBOX "Climbing Blocker",IDC_STATIC,5,6,98,87 + LTEXT "Arms and legs may extend through the blocker, but the avatar's bellybutton will never cross it.", + IDC_STATIC,9,51,93,38 +END + +IDD_COMP_GEO_DICE DIALOG 0, 0, 112, 90 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Max Faces",IDC_STATIC,16,40,39,8 + CONTROL "Custom1",IDC_COMP_GEO_DICE_MAXFACES,"CustEdit", + WS_TABSTOP,60,39,27,10 + CONTROL "Custom2",IDC_COMP_GEO_DICE_MAXFACES_SPIN,"SpinnerControl", + WS_TABSTOP,88,39,7,10 + LTEXT "Max Size",IDC_STATIC,16,51,37,8 + CONTROL "Custom1",IDC_COMP_GEO_DICE_MAXSIZE,"CustEdit", + WS_TABSTOP,60,50,27,10 + CONTROL "Custom2",IDC_COMP_GEO_DICE_MAXSIZE_SPIN,"SpinnerControl", + WS_TABSTOP,88,50,7,10 + LTEXT "Min Faces",IDC_STATIC,16,63,36,8 + CONTROL "Custom1",IDC_COMP_GEO_DICE_MINFACES,"CustEdit", + WS_TABSTOP,60,62,27,10 + CONTROL "Custom2",IDC_COMP_GEO_DICE_MINFACES_SPIN,"SpinnerControl", + WS_TABSTOP,88,62,7,10 + CONTROL "Enabled",IDC_COMP_GEO_DICE_ACTIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,7,42,10 + GROUPBOX "Overrides",IDC_STATIC,9,28,94,50 + CONTROL "Override Defaults",IDC_COMP_GEO_DICE_OVERRIDE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,18,71,10 +END + +IDD_COMP_RESPOND_VISIBILITY DIALOG 0, 0, 108, 34 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick Me!",IDC_NODE_BUTTON,"CustButton",WS_TABSTOP,5,7, + 97,10 + CONTROL "Include Children",IDC_CHECK_CHILDREN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,20,66,10 +END + +IDD_COMP_PARTICLE_WIND DIALOG 0, 0, 110, 199 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_SCALEX,"CustEdit", + WS_TABSTOP,32,23,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_SCALEX_SPIN, + "SpinnerControl",WS_TABSTOP,66,23,7,10 + GROUPBOX "Peak2Peak Dist",IDC_STATIC,11,14,70,46 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_SCALEY,"CustEdit", + WS_TABSTOP,32,34,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_SCALEY_SPIN, + "SpinnerControl",WS_TABSTOP,66,34,7,10 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_SCALEZ,"CustEdit", + WS_TABSTOP,32,45,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_SCALEZ_SPIN, + "SpinnerControl",WS_TABSTOP,66,45,7,10 + LTEXT "X",IDC_STATIC,19,24,8,8 + LTEXT "Y",IDC_STATIC,19,35,8,8 + LTEXT "Z",IDC_STATIC,19,45,8,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_SPEED,"CustEdit", + WS_TABSTOP,43,67,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_SPEED_SPIN, + "SpinnerControl",WS_TABSTOP,77,67,7,10 + LTEXT "Speed",IDC_STATIC,6,68,26,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_STRENGTH,"CustEdit", + WS_TABSTOP,43,80,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_STRENGTH_SPIN, + "SpinnerControl",WS_TABSTOP,77,80,7,10 + LTEXT "Strength",IDC_STATIC,5,81,30,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_CONSTANCY,"CustEdit", + WS_TABSTOP,43,93,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_CONSTANCY_SPIN, + "SpinnerControl",WS_TABSTOP,77,93,7,10 + LTEXT "Constancy",IDC_STATIC,4,94,35,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_SWIRL,"CustEdit", + WS_TABSTOP,43,106,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_SWIRL_SPIN, + "SpinnerControl",WS_TABSTOP,77,106,7,10 + LTEXT "Swirl",IDC_STATIC,3,107,35,8 + CONTROL "Horizontal",IDC_COMP_PARTICLE_WIND_HORIZONTAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,121,48,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_CLAMPANG,"CustEdit", + WS_TABSTOP,50,149,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_CLAMPANG_SPIN, + "SpinnerControl",WS_TABSTOP,84,149,7,10 + LTEXT "Dir Clamp",IDC_STATIC,12,150,32,8 + CONTROL "Pick",IDC_COMP_PARTICLE_WIND_REFOBJECT,"CustButton", + WS_TABSTOP,11,174,86,9 + LTEXT "Direction Ref (Y-Axis)",IDC_STATIC,18,163,68,8 + GROUPBOX "Direction Clamping",IDC_STATIC,7,137,94,53 +END + +IDD_COMP_FORCE_RTLIGHT DIALOG 0, 0, 110, 39 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Force Runtime Lighting",IDC_STATIC,14,8,74,8 + LTEXT "and Disable Pre-shading.",IDC_STATIC,11,20,80,8 +END + +IDD_COMP_DETECTOR_ANIM DIALOG 0, 0, 108, 140 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Animation Component:",IDC_STATIC,6,4,72,8 + LTEXT "Events:",IDC_STATIC,5,58,25,8 + PUSHBUTTON "Button1",IDC_ANIM_BUTTON,5,15,96,12 + LTEXT "Node:",IDC_STATIC,6,32,20,8 + PUSHBUTTON "Button1",IDC_NODE_BUTTON,5,42,96,12 + LISTBOX IDC_EVENT_LIST,5,68,96,74,LBS_SORT | LBS_MULTIPLESEL | + WS_VSCROLL | WS_TABSTOP +END + +IDD_COMP_CAMERAZOOM DIALOG 0, 0, 112, 96 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX,"CustEdit",WS_TABSTOP,8, + 15,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX,"SpinnerControl", + WS_TABSTOP,39,15,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY,"CustEdit",WS_TABSTOP,7, + 31,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY,"SpinnerControl", + WS_TABSTOP,39,31,7,10 + LTEXT "Min FOV",IDC_STATIC,50,17,36,8 + LTEXT "Max FOV",IDC_STATIC,50,33,36,8 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY2,"CustEdit",WS_TABSTOP,7, + 49,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY2,"SpinnerControl", + WS_TABSTOP,39,49,7,10 + LTEXT "Zoom Deg / Sec",IDC_STATIC,50,51,55,8 + LTEXT "Min and Max degrees are the HORIZONTAL FOV",IDC_STATIC, + 7,64,98,19 +END + +IDD_COMP_DETECTOR_MTL DIALOG 0, 0, 108, 163 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Material:",IDC_STATIC,6,2,28,8 + COMBOBOX IDC_ANIM_COMBO,6,60,96,135,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Animation:",IDC_STATIC,6,51,34,8 + PUSHBUTTON "Button1",IDC_MTL_BUTTON,6,11,96,12 + LTEXT "Node:",IDC_STATIC,6,26,20,8 + PUSHBUTTON "Button1",IDC_NODE_BUTTON,6,35,96,12 + LTEXT "Events:",IDC_STATIC,6,78,25,8 + LISTBOX IDC_EVENT_LIST,6,89,96,74,LBS_SORT | LBS_MULTIPLESEL | + WS_VSCROLL | WS_TABSTOP +END + +IDD_PICK_NODE DIALOG 0, 0, 165, 178 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +CAPTION "Pick Node" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,57,162,50,14 + LISTBOX IDC_NODE_LIST,4,5,157,161,LBS_SORT | WS_VSCROLL | + WS_TABSTOP +END + +IDD_COMP_BLENDONTOADV DIALOG 0, 0, 111, 114 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Blend onto who? (Base nodes)",IDC_STATIC,4,4,98,8 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,59,45,47, + 14 + CONTROL "Add",IDC_ADD_TARGS,"CustButton",WS_TABSTOP,5,45,47,14 + LISTBOX IDC_LIST_TARGS,5,19,98,26,WS_VSCROLL | WS_TABSTOP + CONTROL "Sort Faces",IDC_COMP_BLENDONTOADV_SORTFACES,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,69,50,10 + CONTROL "Sort Objects",IDC_COMP_BLENDONTOADV_SORTOBJECTS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,80,55,10 + CONTROL "Blend onto blending",IDC_COMP_BLENDONTOADV_ONTOBLENDING, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,90,79,10 +END + +IDD_COMP_VEHICLE DIALOG 0, 0, 108, 264 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_CHASSIS,"CustButton",WS_TABSTOP,3,13,101,12 + LTEXT "Chassis:",IDC_STATIC,3,2,27,8 + CONTROL "",IDC_FL_WHEEL,"CustButton",WS_TABSTOP,3,39,101,12 + LTEXT "Front Left Wheel:",IDC_STATIC,3,28,56,8 + CONTROL "",IDC_FR_WHEEL,"CustButton",WS_TABSTOP,3,91,101,12 + LTEXT "Front Right Wheel:",IDC_STATIC,3,80,60,8 + CONTROL "",IDC_RL_WHEEL,"CustButton",WS_TABSTOP,3,143,101,12 + LTEXT "Rear Left Wheel:",IDC_STATIC,3,132,55,8 + CONTROL "",IDC_RR_WHEEL,"CustButton",WS_TABSTOP,3,195,101,12 + LTEXT "Rear Right Wheel:",IDC_STATIC,3,184,60,8 + LTEXT "Front Left Hardpoint:",IDC_STATIC,3,54,66,8 + CONTROL "",IDC_FL_HARDPOINT,"CustButton",WS_TABSTOP,3,65,101,12 + LTEXT "Front Right Hardpoint:",IDC_STATIC,3,106,70,8 + CONTROL "",IDC_FR_HARDPOINT,"CustButton",WS_TABSTOP,3,117,101,12 + CONTROL "",IDC_RL_HARDPOINT,"CustButton",WS_TABSTOP,3,169,101,12 + LTEXT "Rear Left Hardpoint:",IDC_STATIC,3,158,65,8 + CONTROL "",IDC_RR_HARDPOINT,"CustButton",WS_TABSTOP,3,221,101,12 + LTEXT "Rear Right Hardpoint:",IDC_STATIC,3,210,70,8 + LTEXT "Drive Detector:",IDC_STATIC,3,237,49,8 + PUSHBUTTON "Button1",IDC_DRIVE,3,247,101,14 +END + +IDD_COMP_PARTICLE_UNIWIND DIALOG 0, 0, 112, 164 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_STRENGTH,"CustEdit", + WS_TABSTOP,43,7,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_STRENGTH_SPIN, + "SpinnerControl",WS_TABSTOP,77,7,7,10 + LTEXT "Strength",IDC_STATIC,5,8,30,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_CONSTANCY,"CustEdit", + WS_TABSTOP,43,20,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_CONSTANCY_SPIN, + "SpinnerControl",WS_TABSTOP,77,20,7,10 + LTEXT "Constancy",IDC_STATIC,4,21,35,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_SWIRL,"CustEdit", + WS_TABSTOP,43,33,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_SWIRL_SPIN, + "SpinnerControl",WS_TABSTOP,77,33,7,10 + LTEXT "Swirl",IDC_STATIC,3,34,35,8 + CONTROL "Horizontal",IDC_COMP_PARTICLE_WIND_HORIZONTAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,85,48,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_MINSECS,"CustEdit", + WS_TABSTOP,40,48,20,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_MINSECS_SPIN, + "SpinnerControl",WS_TABSTOP,62,48,7,10 + LTEXT "Range (s)",IDC_STATIC,4,48,35,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_MAXSECS,"CustEdit", + WS_TABSTOP,74,48,20,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_MAXSECS_SPIN, + "SpinnerControl",WS_TABSTOP,96,48,7,10 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_RATE,"CustEdit", + WS_TABSTOP,45,63,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_RATE_SPIN, + "SpinnerControl",WS_TABSTOP,79,63,7,10 + LTEXT "Rate (s)",IDC_STATIC,5,63,35,8 + CONTROL "Custom1",IDC_COMP_PARTICLE_WIND_CLAMPANG2,"CustEdit", + WS_TABSTOP,50,113,32,10 + CONTROL "Custom2",IDC_COMP_PARTICLE_WIND_CLAMPANG_SPIN2, + "SpinnerControl",WS_TABSTOP,84,113,7,10 + LTEXT "Dir Clamp",IDC_STATIC,12,114,32,8 + CONTROL "Pick",IDC_COMP_PARTICLE_WIND_REFOBJECT,"CustButton", + WS_TABSTOP,11,138,86,9 + LTEXT "Direction Ref (Y-Axis)",IDC_STATIC,18,127,68,8 + GROUPBOX "Direction Clamping",IDC_STATIC,7,101,94,53 +END + +IDD_COMP_DISTFADE DIALOG 0, 0, 113, 95 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Start",IDC_STATIC,19,5,16,8 + LTEXT "End",IDC_STATIC,66,5,14,8 + CONTROL "Custom1",IDC_COMP_DISTFADE_INSTART,"CustEdit", + WS_TABSTOP,12,28,27,10 + CONTROL "Custom2",IDC_COMP_DISTFADE_INSTART_SPIN,"SpinnerControl", + WS_TABSTOP,40,28,7,10 + CONTROL "Custom1",IDC_COMP_DISTFADE_INEND,"CustEdit",WS_TABSTOP, + 56,28,27,10 + CONTROL "Custom2",IDC_COMP_DISTFADE_INEND_SPIN,"SpinnerControl", + WS_TABSTOP,84,28,7,10 + CONTROL "Fade Out",IDC_COMP_DISTFADE_OUT_ACTIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,40,82,10 + CONTROL "Custom1",IDC_COMP_DISTFADE_OUTSTART,"CustEdit", + WS_TABSTOP,11,52,27,10 + CONTROL "Custom2",IDC_COMP_DISTFADE_OUTSTART_SPIN,"SpinnerControl", + WS_TABSTOP,39,52,7,10 + CONTROL "Custom1",IDC_COMP_DISTFADE_OUTEND,"CustEdit",WS_TABSTOP, + 55,52,27,10 + CONTROL "Custom2",IDC_COMP_DISTFADE_OUTEND_SPIN,"SpinnerControl", + WS_TABSTOP,83,52,7,10 + CONTROL "Fade In",IDC_COMP_DISTFADE_IN_ACTIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,16,82,10 +END + +IDD_COMP_SOUNDGUI DIALOG 0, 0, 108, 140 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_COMP_SOUND3D_FILENAME_BTN,"CustButton",WS_TABSTOP, + 7,14,93,13 + CONTROL "Auto Start",IDC_COMP_SOUND3D_AUTOSTART_CKBX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,30,47,10 + CONTROL "Loop:",IDC_COMP_SOUND3D_LOOPCHKBOX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,66,30,34,10 + COMBOBOX IDC_LOOP_COMBO,7,43,93,75,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "Custom1",IDC_COMP_SOUND3D_VOLSLIDER,"SliderControl", + WS_TABSTOP,7,83,92,15 + LTEXT "Sound Name:",IDC_SOUND_FILENAME_TEXT,7,5,44,8 + GROUPBOX "Volume",IDC_STATIC,3,72,100,63 + CONTROL "Custom2",IDC_COMP_SOUND3D_SLIDERVIEWER,"CustEdit", + WS_DISABLED | WS_TABSTOP,60,113,28,10 + LTEXT "-48 db",IDC_STATIC,7,99,25,8 + RTEXT "0 db",IDC_STATIC,81,99,15,8 + RTEXT "-12",IDC_STATIC,64,99,11,8 + RTEXT "-24",IDC_STATIC,47,99,12,8 + RTEXT "-36",IDC_STATIC,33,99,10,8 + CONTROL "Disable Load-on-Demand",IDC_COMP_SOUND_DISABLELOD, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,60,94,10 + CONTROL "Show in TrackView",IDC_SND_TRACKVIEW,"Button", + BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,9,110,50,18 + CTEXT "db",IDC_STATIC,89,114,9,8 +END + +IDD_COMP_SHADOW_CAST DIALOG 0, 0, 111, 156 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Self Shadow",IDC_COMP_SHADOW_CAST_SELFSHADOW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,8,56,10 + CONTROL "Blur",IDC_COMP_SHADOW_CAST_BLUR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,73,28,10 + GROUPBOX "",IDC_STATIC,5,67,92,47 + LTEXT "Amount %",IDC_STATIC,14,88,33,8 + CONTROL "Custom1",IDC_COMP_SHADOW_CAST_BLURSCALE,"CustEdit", + WS_TABSTOP,55,87,27,10 + CONTROL "Custom2",IDC_COMP_SHADOW_CAST_BLURSCALE_SPIN, + "SpinnerControl",WS_TABSTOP,83,87,7,10 + CONTROL "Distance Scale",IDC_COMP_SHADOW_CAST_ATTEN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,40,64,10 + GROUPBOX "",IDC_STATIC,5,34,92,35 + LTEXT "Amount %",IDC_STATIC,13,55,33,8 + CONTROL "Custom1",IDC_COMP_SHADOW_CAST_ATTENSCALE,"CustEdit", + WS_TABSTOP,55,54,27,10 + CONTROL "Custom2",IDC_COMP_SHADOW_CAST_ATTENSCALE_SPIN, + "SpinnerControl",WS_TABSTOP,83,54,7,10 + LTEXT "Strength %",IDC_STATIC,13,22,33,8 + CONTROL "Custom1",IDC_COMP_SHADOW_CAST_BOOST,"CustEdit", + WS_TABSTOP,55,22,27,10 + CONTROL "Custom2",IDC_COMP_SHADOW_CAST_BOOST_SPIN,"SpinnerControl", + WS_TABSTOP,83,22,7,10 + COMBOBOX IDC_COMP_SHADOW_QUALITY,7,134,92,66,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Minimum Quality Setting",IDC_STATIC,11,122,76,8 + CONTROL "Limit Resolution",IDC_COMP_SHADOW_CAST_LIMIT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,101,65,10 +END + +IDD_COMP_SHADOW_RCV DIALOG 0, 0, 113, 95 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Force On",IDC_RADIO_FORCE_ON,"Button", + BS_AUTORADIOBUTTON,10,7,45,10 + CONTROL "Force Off",IDC_RADIO_FORCE_OFF,"Button", + BS_AUTORADIOBUTTON,10,18,45,10 +END + +IDD_COMP_SHADOW_LIGHT DIALOG 0, 0, 113, 134 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Falloff Dist",IDC_STATIC,8,9,44,8 + CONTROL "Custom1",IDC_COMP_SHADOW_LIGHT_FALLOFF,"CustEdit", + WS_TABSTOP,58,8,27,10 + CONTROL "Custom2",IDC_COMP_SHADOW_LIGHT_FALLOFF_SPIN, + "SpinnerControl",WS_TABSTOP,86,8,7,10 + LTEXT "Visible Dist",IDC_STATIC,8,21,35,8 + CONTROL "Custom1",IDC_COMP_SHADOW_LIGHT_MAXDIST,"CustEdit", + WS_TABSTOP,58,20,27,10 + CONTROL "Custom2",IDC_COMP_SHADOW_LIGHT_MAXDIST_SPIN, + "SpinnerControl",WS_TABSTOP,86,20,7,10 + LTEXT "Power %",IDC_STATIC,8,34,28,8 + CONTROL "Custom1",IDC_COMP_SHADOW_LIGHT_POWER,"CustEdit", + WS_TABSTOP,58,33,27,10 + CONTROL "Custom2",IDC_COMP_SHADOW_LIGHT_POWER_SPIN, + "SpinnerControl",WS_TABSTOP,86,33,7,10 + CONTROL "Shadow only",IDC_COMP_SHADOW_LIGHT_SHADOWONLY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,50,57,10 + CONTROL "Obey Light Groups",IDC_COMP_SHADOW_LIGHT_OBEYGROUPS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,63,75,10 + CONTROL "Self Shadow",IDC_COMP_SHADOW_LIGHT_SELFSHADOW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,75,56,10 + COMBOBOX IDC_COMP_SHADOW_QUALITY,7,100,92,66,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Minimum Quality Setting",IDC_STATIC,11,88,76,8 +END + +IDD_COMP_SOUNDSRC DIALOG 0, 0, 108, 61 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Left channel only",IDC_SND_CHANSRC1,"Button", + BS_AUTORADIOBUTTON | WS_DISABLED | WS_GROUP,8,32,70,10 + CONTROL "Right channel only",IDC_SND_CHANSRC2,"Button", + BS_AUTORADIOBUTTON | WS_DISABLED,8,43,75,10 + GROUPBOX "Waveform Source",IDC_STATIC,4,21,100,36 + CONTROL "WavSrcEnableCheckBox",IDC_SND_ISSTEREO_HIDDEN,"Button", + BS_AUTOCHECKBOX | NOT WS_VISIBLE,77,20,11,10 + LTEXT "Priority:",IDC_STATIC,15,8,24,8 + CONTROL "Custom2",IDC_SND_PRIORITY,"CustEdit",WS_TABSTOP,41,7,39, + 10 + CONTROL "Custom2",IDC_SND_PRIORITY_SPIN,"SpinnerControl", + WS_TABSTOP,81,7,7,10 +END + +IDD_COMP_PHYS_SUBWORLD DIALOGEX 0, 0, 107, 55 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN +END + +IDD_COMP_RANDOMSOUND_GROUPS DIALOG 0, 0, 109, 138 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "All in one group",IDC_COMP_RS_USEALL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,7,65,10 + LISTBOX IDC_COMP_RS_GROUPLIST,12,21,85,52,WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "Add",IDC_COMP_RS_GROUP_ADD,12,75,41,14 + PUSHBUTTON "Remove",IDC_COMP_RS_GROUP_REMOVE,55,75,42,14 + LTEXT "Group Num:",-1,12,99,38,8 + CONTROL "",IDC_COMP_RS_GROUP,"CustEdit",WS_TABSTOP,66,98,24,10 + CONTROL "",IDC_COMP_RS_GROUP_SPIN,"SpinnerControl",WS_TABSTOP,90, + 98,7,10 + CONTROL "Combine sounds into one buffer",IDC_RAND_COMBINESOUNDS, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,13, + 114,87,17 +END + +IDD_COMP_FOOTSTEP_SOUND DIALOG 0, 0, 108, 73 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Surface:",IDC_STATIC,7,7,28,8 + COMBOBOX IDC_COMP_FOOTSTEP_SOUND_SURFACE,7,17,94,47,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "(none)",IDC_COMP_FOOTSTEP_SOUND_PICK,7,50,94,14 + LTEXT "Random Sound Comp:",IDC_STATIC,7,39,83,8 +END + +IDD_COMP_RESPOND_FOOT_SURFACE DIALOG 0, 0, 108, 38 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Surface:",IDC_STATIC,3,3,28,8 + COMBOBOX IDC_COMP_RESPOND_FOOT_SURFACE,7,17,94,47,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP +END + +IDD_COMP_EAXLISTENER DIALOG 0, 0, 108, 134 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_EAX_CUSTFILE,"CustButton",WS_TABSTOP,7,75,93,13 + COMBOBOX IDC_EAX_PRESET_COMBO,7,46,93,75,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Use EAX preset:",IDC_EAX_PRESET,"Button", + BS_AUTORADIOBUTTON,4,35,68,10 + CONTROL "Use custom settings:",IDC_EAX_CUSTOM,"Button", + BS_AUTORADIOBUTTON,4,63,81,10 + LTEXT "NOTE: The selected file is not source-controlled, so if you change the settings file, you must reload it here.", + IDC_STATIC,9,92,88,33 + CONTROL "Pick",IDC_EAX_SOFTREGION,"CustButton",WS_TABSTOP,10,14, + 87,13 + GROUPBOX "Soft region to use:",IDC_STATIC,4,3,100,30 +END + +IDD_COMP_EAXBUFFER DIALOG 0, 0, 108, 568 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_EAX_ROOM,"SliderControl",WS_TABSTOP,7,25, + 93,15 + LTEXT "-10,000",IDC_STATIC,12,41,25,8 + RTEXT "1000 mB",IDC_STATIC,67,41,29,8 + LTEXT "Room:",IDC_STATIC,8,15,22,8 + CONTROL "Custom1",IDC_EAX_ROOMHF,"SliderControl",WS_TABSTOP,7,68, + 93,15 + LTEXT "-10,000",IDC_STATIC,12,84,25,8 + RTEXT "0 mB",IDC_STATIC,72,84,24,8 + LTEXT "Room HF:",IDC_STATIC,8,58,33,8 + GROUPBOX " ",IDC_STATIC,3,3, + 101,318 + CONTROL "Custom2",IDC_EAX_ROOM_EDIT,"CustEdit",WS_DISABLED | + WS_TABSTOP,65,14,23,9 + CONTROL "Custom2",IDC_EAX_ROOMHF_EDIT,"CustEdit",WS_DISABLED | + WS_TABSTOP,65,57,23,9 + CONTROL "Enable EAX Effects",IDC_EAX_ENABLE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,3,78,10 + CONTROL "Room Auto",IDC_EAX_ROOMAUTO,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,13,99,51,10 + CONTROL "Room HF Auto",IDC_EAX_ROOMHFAUTO,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,111,63,10 + LTEXT "mB",IDC_STATIC,90,15,11,8 + LTEXT "mB",IDC_STATIC,90,58,11,8 + CONTROL "Custom1",IDC_EAX_OUTSIDEVOLHF,"SliderControl", + WS_TABSTOP,7,136,93,15 + LTEXT "-10,000",IDC_STATIC,11,152,25,8 + RTEXT "0 mB",IDC_STATIC,71,152,24,8 + LTEXT "Outside Vol. HF:",IDC_STATIC,8,126,52,8 + CONTROL "Custom2",IDC_EAX_OUTSIDEVOLHF_EDIT,"CustEdit", + WS_DISABLED | WS_TABSTOP,65,125,23,9 + LTEXT "mB",IDC_STATIC,90,126,11,8 + CONTROL "Custom1",IDC_EAX_AIRABSORPTFACT,"SliderControl", + WS_TABSTOP,7,175,93,15 + LTEXT "0",IDC_STATIC,11,191,8,8 + RTEXT "10",IDC_STATIC,71,191,24,8 + LTEXT "Air Absorp. Factor:",IDC_STATIC,8,165,59,8 + CONTROL "Custom2",IDC_EAX_AIRABSORPTFACT_EDIT,"CustEdit", + WS_DISABLED | WS_TABSTOP,77,164,23,9 + CONTROL "Custom1",IDC_EAX_ROOMROLLOFFFACT,"SliderControl", + WS_TABSTOP,7,213,93,15 + LTEXT "0",IDC_STATIC,11,229,8,8 + RTEXT "10",IDC_STATIC,71,229,24,8 + LTEXT "Room Rolloff Factor:",IDC_STATIC,8,203,66,8 + CONTROL "Custom2",IDC_EAX_ROOMROLLOFFFACT_EDIT,"CustEdit", + WS_DISABLED | WS_TABSTOP,77,202,23,9 + CONTROL "Custom1",IDC_EAX_DOPPLERFACT,"SliderControl",WS_TABSTOP, + 7,251,93,15 + LTEXT "0",IDC_STATIC,11,267,8,8 + RTEXT "10",IDC_STATIC,71,267,24,8 + LTEXT "Doppler Factor:",IDC_STATIC,8,241,50,8 + CONTROL "Custom2",IDC_EAX_DOPPLERFACT_EDIT,"CustEdit", + WS_DISABLED | WS_TABSTOP,77,240,23,9 + CONTROL "Custom1",IDC_EAX_ROLLOFFFACT,"SliderControl",WS_TABSTOP, + 7,289,93,15 + LTEXT "0",IDC_STATIC,11,305,8,8 + RTEXT "10",IDC_STATIC,71,305,24,8 + LTEXT "Rolloff Factor:",IDC_STATIC,8,279,45,8 + CONTROL "Custom2",IDC_EAX_ROLLOFFFACT_EDIT,"CustEdit", + WS_DISABLED | WS_TABSTOP,77,278,23,9 + GROUPBOX " ",IDC_STATIC,3,326, + 101,235 + CONTROL "Enable Occlusion",IDC_EAX_ENABLEOCCLUSION,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,326,71,10 + CONTROL "Pick",IDC_EAX_OCCREGION,"CustButton",WS_TABSTOP,8,351, + 91,13 + LTEXT "Soft Region:",IDC_STATIC,8,341,40,8 + LTEXT "Show:",IDC_STATIC,7,367,21,8 + CONTROL "Inside",IDC_EAX_STARTOCC,"Button",BS_AUTORADIOBUTTON,30, + 367,33,10 + CONTROL "Outside",IDC_EAX_ENDOCC,"Button",BS_AUTORADIOBUTTON,63, + 367,38,10 + CONTROL "Custom1",IDC_EAX_OCCLUSION,"SliderControl",WS_TABSTOP,7, + 390,93,15 + LTEXT "-10,000",IDC_STATIC,10,406,25,8 + RTEXT "0 mB",IDC_STATIC,72,406,24,8 + LTEXT "Occlusion:",IDC_STATIC,8,380,34,8 + CONTROL "Custom2",IDC_EAX_OCCLUSION_EDIT,"CustEdit",WS_DISABLED | + WS_TABSTOP,65,379,23,9 + LTEXT "mB",IDC_STATIC,90,380,11,8 + CONTROL "Custom1",IDC_EAX_OCCLFRATIO,"SliderControl",WS_TABSTOP, + 7,428,93,15 + LTEXT "0",IDC_STATIC,11,444,8,8 + RTEXT "1.0",IDC_STATIC,71,444,24,8 + LTEXT "LF Ratio:",IDC_STATIC,8,418,30,8 + CONTROL "Custom2",IDC_EAX_OCCLFRATIO_EDIT,"CustEdit",WS_DISABLED | + WS_TABSTOP,77,417,23,9 + CONTROL "Custom1",IDC_EAX_OCCROOMRATIO,"SliderControl", + WS_TABSTOP,7,465,93,15 + LTEXT "0",IDC_STATIC,11,481,8,8 + RTEXT "10.0",IDC_STATIC,71,481,24,8 + LTEXT "Room Ratio:",IDC_STATIC,8,456,40,8 + CONTROL "Custom2",IDC_EAX_OCCROOMRATIO_EDIT,"CustEdit", + WS_DISABLED | WS_TABSTOP,77,454,23,9 + CONTROL "Custom1",IDC_EAX_OCCDIRECTRATIO,"SliderControl", + WS_TABSTOP,7,502,93,15 + LTEXT "0",IDC_STATIC,11,518,8,8 + RTEXT "10.0",IDC_STATIC,71,518,24,8 + LTEXT "Direct Ratio:",IDC_STATIC,8,493,40,8 + CONTROL "Custom2",IDC_EAX_OCCDIRECTRATIO_EDIT,"CustEdit", + WS_DISABLED | WS_TABSTOP,77,491,23,9 + LTEXT "Load from preset:",IDC_STATIC,8,532,56,8 + COMBOBOX IDC_EAX_OCCPRESET,8,542,91,72,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP +END + +IDD_COMP_FP_FOOTPRINT DIALOG 0, 0, 112, 324 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Scale Wid %",IDC_STATIC,8,23,47,8 + CONTROL "Custom1",IDC_COMP_FP_WIDTH,"CustEdit",WS_TABSTOP,57,22, + 27,10 + CONTROL "Custom2",IDC_COMP_FP_WIDTH_SPIN,"SpinnerControl", + WS_TABSTOP,85,22,7,10 + LTEXT "Scale Len %",IDC_STATIC,7,33,47,8 + CONTROL "Custom1",IDC_COMP_FP_LENGTH,"CustEdit",WS_TABSTOP,57,33, + 27,10 + CONTROL "Custom2",IDC_COMP_FP_LENGTH_SPIN,"SpinnerControl", + WS_TABSTOP,85,33,7,10 + GROUPBOX "Decal Layer",IDC_STATIC,5,69,99,58 + LTEXT "Life Span (s)",IDC_STATIC,7,52,47,8 + CONTROL "Custom1",IDC_COMP_FP_LIFESPAN2,"CustEdit",WS_TABSTOP,57, + 51,27,10 + CONTROL "Custom2",IDC_COMP_FP_LIFESPAN_SPIN2,"SpinnerControl", + WS_TABSTOP,85,51,7,10 + CONTROL "Alpha",IDC_RADIO_ALPHA,"Button",BS_AUTORADIOBUTTON,12, + 96,34,10 + CONTROL "Brighten",IDC_RADIO_MADD,"Button",BS_AUTORADIOBUTTON,12, + 107,42,10 + CONTROL "Add",IDC_RADIO_ADD,"Button",BS_AUTORADIOBUTTON,62,96,29, + 10 + CONTROL "Mult",IDC_RADIO_MULT,"Button",BS_AUTORADIOBUTTON,62,108, + 29,10 + CONTROL "",IDC_COMP_FP_TEXMAP,"CustButton",WS_TABSTOP,11,80,87, + 13 + LTEXT "Wet secs",IDC_STATIC,7,208,47,8 + CONTROL "Custom1",IDC_COMP_FP_DIRTYTIME,"CustEdit",WS_TABSTOP,60, + 208,27,10 + CONTROL "Custom2",IDC_COMP_FP_DIRTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,88,208,7,10 + LISTBOX IDC_LIST_TARGS,4,146,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_NOTIFY,4,189,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,54,189,47, + 14 + GROUPBOX "Muddy/Wet Footprints",IDC_STATIC,3,134,101,88 + LTEXT "Intensity %",IDC_STATIC,8,7,47,8 + CONTROL "Custom1",IDC_COMP_FP_INTENSITY,"CustEdit",WS_TABSTOP,58, + 7,27,10 + CONTROL "Custom2",IDC_COMP_FP_INTENSITY_SPIN,"SpinnerControl", + WS_TABSTOP,86,7,7,10 + GROUPBOX "Particles",IDC_STATIC,3,226,101,90 + LISTBOX IDC_LIST_TARGS2,5,234,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_PARTICLE,5,280,47,14 + CONTROL "Remove",IDC_DEL_TARGS2,"CustButton",WS_TABSTOP,56,280, + 47,14 + LTEXT "Spew secs",IDC_STATIC,11,303,47,8 + CONTROL "Custom1",IDC_COMP_FP_PARTYTIME,"CustEdit",WS_TABSTOP,61, + 302,27,10 + CONTROL "Custom2",IDC_COMP_FP_PARTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,89,302,7,10 +END + +IDD_COMP_FP_RIPPLE DIALOG 0, 0, 112, 304 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Scale %",IDC_STATIC,8,25,47,8 + CONTROL "Custom1",IDC_COMP_FP_WIDTH,"CustEdit",WS_TABSTOP,57,24, + 27,10 + CONTROL "Custom2",IDC_COMP_FP_WIDTH_SPIN,"SpinnerControl", + WS_TABSTOP,85,24,7,10 + GROUPBOX "Decal Layer",IDC_STATIC,5,43,99,55 + CONTROL "Alpha",IDC_RADIO_ALPHA,"Button",BS_AUTORADIOBUTTON,12, + 70,34,10 + CONTROL "Brighten",IDC_RADIO_MADD,"Button",BS_AUTORADIOBUTTON,12, + 81,42,10 + CONTROL "Add",IDC_RADIO_ADD,"Button",BS_AUTORADIOBUTTON,62,70,29, + 10 + CONTROL "Mult",IDC_RADIO_MULT,"Button",BS_AUTORADIOBUTTON,62,82, + 29,10 + CONTROL "",IDC_COMP_FP_TEXMAP,"CustButton",WS_TABSTOP,11,54,87, + 13 + LTEXT "Wet/Dirty secs",IDC_STATIC,10,182,47,8 + CONTROL "Custom1",IDC_COMP_FP_DIRTYTIME,"CustEdit",WS_TABSTOP,63, + 182,27,10 + CONTROL "Custom2",IDC_COMP_FP_DIRTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,91,182,7,10 + LISTBOX IDC_LIST_TARGS,7,118,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_NOTIFY,7,161,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,57,161,47, + 14 + GROUPBOX "Muddy/Wet Footprints",IDC_STATIC,6,108,101,88 + LTEXT "Intensity %",IDC_STATIC,8,7,47,8 + CONTROL "Custom1",IDC_COMP_FP_INTENSITY,"CustEdit",WS_TABSTOP,58, + 7,27,10 + CONTROL "Custom2",IDC_COMP_FP_INTENSITY_SPIN,"SpinnerControl", + WS_TABSTOP,86,7,7,10 + GROUPBOX "Particles",IDC_STATIC,3,203,101,94 + LISTBOX IDC_LIST_TARGS2,5,211,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_PARTICLE,5,257,47,14 + CONTROL "Remove",IDC_DEL_TARGS2,"CustButton",WS_TABSTOP,56,257, + 47,14 + LTEXT "Spew secs",IDC_STATIC,11,279,47,8 + CONTROL "Custom1",IDC_COMP_FP_PARTYTIME,"CustEdit",WS_TABSTOP,61, + 278,27,10 + CONTROL "Custom2",IDC_COMP_FP_PARTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,89,278,7,10 +END + +IDD_COMP_FP_DIRTY DIALOG 0, 0, 113, 114 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST_TARGS,6,20,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_DECAL,6,73,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,56,73,47, + 14 + LTEXT "Muddy/Wet Footprints",IDC_STATIC,7,6,72,8 + LTEXT "Wet secs",IDC_STATIC,12,94,47,8 + CONTROL "Custom1",IDC_COMP_FP_DIRTYTIME,"CustEdit",WS_TABSTOP,65, + 94,27,10 + CONTROL "Custom2",IDC_COMP_FP_DIRTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,93,94,7,10 +END + +IDD_COMP_FP_PRINTSHAPE DIALOG 0, 0, 113, 53 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Width",IDC_STATIC,8,10,47,8 + CONTROL "Custom1",IDC_COMP_WIDTH,"CustEdit",WS_TABSTOP,57,9,27, + 10 + CONTROL "Custom2",IDC_COMP_WIDTH_SPIN,"SpinnerControl", + WS_TABSTOP,85,9,7,10 + LTEXT "Length",IDC_STATIC,7,20,47,8 + CONTROL "Custom1",IDC_COMP_LENGTH,"CustEdit",WS_TABSTOP,57,20,27, + 10 + CONTROL "Custom2",IDC_COMP_LENGTH_SPIN,"SpinnerControl", + WS_TABSTOP,85,20,7,10 + LTEXT "Range",IDC_STATIC,8,37,47,8 + CONTROL "Custom1",IDC_COMP_HEIGHT,"CustEdit",WS_TABSTOP,57,37,27, + 10 + CONTROL "Custom2",IDC_COMP_HEIGHT_SPIN,"SpinnerControl", + WS_TABSTOP,86,37,7,10 +END + +IDD_COMP_FP_PUDDLE DIALOG 0, 0, 111, 291 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Scale %",IDC_STATIC,8,22,47,8 + CONTROL "Custom1",IDC_COMP_FP_WIDTH,"CustEdit",WS_TABSTOP,57,21, + 27,10 + CONTROL "Custom2",IDC_COMP_FP_WIDTH_SPIN,"SpinnerControl", + WS_TABSTOP,85,21,7,10 + GROUPBOX "Decal Layer",IDC_STATIC,5,37,99,55 + CONTROL "Alpha",IDC_RADIO_ALPHA,"Button",BS_AUTORADIOBUTTON,12, + 64,34,10 + CONTROL "Brighten",IDC_RADIO_MADD,"Button",BS_AUTORADIOBUTTON,12, + 75,42,10 + CONTROL "Add",IDC_RADIO_ADD,"Button",BS_AUTORADIOBUTTON,62,64,29, + 10 + CONTROL "Mult",IDC_RADIO_MULT,"Button",BS_AUTORADIOBUTTON,62,76, + 29,10 + CONTROL "",IDC_COMP_FP_TEXMAP,"CustButton",WS_TABSTOP,11,48,87, + 13 + LTEXT "Wet secs",IDC_STATIC,9,171,47,8 + CONTROL "Custom1",IDC_COMP_FP_DIRTYTIME,"CustEdit",WS_TABSTOP,62, + 171,27,10 + CONTROL "Custom2",IDC_COMP_FP_DIRTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,90,171,7,10 + LISTBOX IDC_LIST_TARGS,4,108,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_NOTIFY,4,151,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,54,151,47, + 14 + GROUPBOX "Muddy/Wet Footprints",IDC_STATIC,3,98,101,88 + LTEXT "Intensity %",IDC_STATIC,8,7,47,8 + CONTROL "Custom1",IDC_COMP_FP_INTENSITY,"CustEdit",WS_TABSTOP,58, + 7,27,10 + CONTROL "Custom2",IDC_COMP_FP_INTENSITY_SPIN,"SpinnerControl", + WS_TABSTOP,86,7,7,10 + GROUPBOX "Particles",IDC_STATIC,3,193,101,93 + LISTBOX IDC_LIST_TARGS2,5,201,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_PARTICLE,5,247,47,14 + CONTROL "Remove",IDC_DEL_TARGS2,"CustButton",WS_TABSTOP,56,247, + 47,14 + LTEXT "Spew secs",IDC_STATIC,11,269,47,8 + CONTROL "Custom1",IDC_COMP_FP_PARTYTIME,"CustEdit",WS_TABSTOP,61, + 268,27,10 + CONTROL "Custom2",IDC_COMP_FP_PARTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,89,268,7,10 +END + +IDD_COMP_SUBWORLD_REGION DIALOG 0, 0, 108, 69 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Join subworld:",IDC_STATIC,11,5,46,8 + CONTROL "Pick Me!",IDC_COMP_CAMERARGN_PICKSTATE_BASE,"CustButton", + WS_TABSTOP,11,16,84,10 + CONTROL "Enter",IDC_RADIO_ENTER,"Button",BS_AUTORADIOBUTTON,11, + 42,33,10 + CONTROL "Exit",IDC_RADIO_EXIT,"Button",BS_AUTORADIOBUTTON,11,53, + 27,10 + LTEXT "Join on:",IDC_STATIC,11,30,26,8 +END + +IDD_COMP_GUIMULTILINE DIALOG 0, 0, 106, 70 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Transparent Background",IDC_GUI_XPARENT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,6,95,10 + CONTROL "Maintain size across resolutions",IDC_GUI_SCALERES, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,5, + 18,98,17 + CONTROL "Pick",IDC_GUI_COMPSELBTN,"CustButton",WS_TABSTOP,7,48, + 91,12 + GROUPBOX " ",IDC_STATIC,3,37,99,27 + CONTROL "Scroll Control",IDC_GUI_SCROLLCTRL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,36,57,10 +END + +IDD_COMP_NPC_SPAWN DIALOG 0, 0, 108, 99 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Base Model:",IDC_STATIC,9,13,40,8 + CONTROL "Custom1",IDC_NPC_SPAWN_MODEL_TEXT_BOX,"CustEdit", + WS_TABSTOP,9,25,89,10 + GROUPBOX "General Properties",IDC_STATIC,3,0,101,93 + LTEXT "Account Name:",IDC_STATIC,9,40,50,8 + CONTROL "Custom1",IDC_NPC_SPAWN_ACCOUNT_TEXT_BOX,"CustEdit", + WS_TABSTOP,9,52,89,10 + CONTROL "Spawn Automatically",IDC_NPC_SPAWN_AUTOSPAWN_BOOL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,71,81,10 +END + +IDD_COMP_FP_ACTIVEPRINTSHAPE DIALOG 0, 0, 113, 138 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Width",IDC_STATIC,8,10,47,8 + CONTROL "Custom1",IDC_COMP_WIDTH,"CustEdit",WS_TABSTOP,57,9,27, + 10 + CONTROL "Custom2",IDC_COMP_WIDTH_SPIN,"SpinnerControl", + WS_TABSTOP,85,9,7,10 + LTEXT "Length",IDC_STATIC,7,20,47,8 + CONTROL "Custom1",IDC_COMP_LENGTH,"CustEdit",WS_TABSTOP,57,20,27, + 10 + CONTROL "Custom2",IDC_COMP_LENGTH_SPIN,"SpinnerControl", + WS_TABSTOP,85,20,7,10 + LTEXT "Range",IDC_STATIC,8,37,47,8 + CONTROL "Custom1",IDC_COMP_HEIGHT,"CustEdit",WS_TABSTOP,57,37,27, + 10 + CONTROL "Custom2",IDC_COMP_HEIGHT_SPIN,"SpinnerControl", + WS_TABSTOP,86,37,7,10 + LISTBOX IDC_LIST_TARGS,4,63,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_NOTIFY2,4,106,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,54,106,47, + 14 + GROUPBOX "Active Puddle/Ripples",IDC_STATIC,3,53,101,75 +END + +IDD_COMP_FP_WAKE DIALOG 0, 0, 111, 332 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Scale Wid %",IDC_STATIC,8,23,47,8 + CONTROL "Custom1",IDC_COMP_FP_WIDTH,"CustEdit",WS_TABSTOP,57,22, + 27,10 + CONTROL "Custom2",IDC_COMP_FP_WIDTH_SPIN,"SpinnerControl", + WS_TABSTOP,85,22,7,10 + LTEXT "Scale Len %",IDC_STATIC,7,33,47,8 + CONTROL "Custom1",IDC_COMP_FP_LENGTH,"CustEdit",WS_TABSTOP,57,33, + 27,10 + CONTROL "Custom2",IDC_COMP_FP_LENGTH_SPIN,"SpinnerControl", + WS_TABSTOP,85,33,7,10 + GROUPBOX "Decal Layer",IDC_STATIC,5,69,99,58 + CONTROL "Alpha",IDC_RADIO_ALPHA,"Button",BS_AUTORADIOBUTTON,12, + 96,34,10 + CONTROL "Brighten",IDC_RADIO_MADD,"Button",BS_AUTORADIOBUTTON,12, + 107,42,10 + CONTROL "Add",IDC_RADIO_ADD,"Button",BS_AUTORADIOBUTTON,62,96,29, + 10 + CONTROL "Mult",IDC_RADIO_MULT,"Button",BS_AUTORADIOBUTTON,62,108, + 29,10 + CONTROL "",IDC_COMP_FP_TEXMAP,"CustButton",WS_TABSTOP,11,80,87, + 13 + LTEXT "Wet secs",IDC_STATIC,7,208,47,8 + CONTROL "Custom1",IDC_COMP_FP_DIRTYTIME,"CustEdit",WS_TABSTOP,60, + 208,27,10 + CONTROL "Custom2",IDC_COMP_FP_DIRTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,88,208,7,10 + LISTBOX IDC_LIST_TARGS,4,146,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_NOTIFY,4,189,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,54,189,47, + 14 + GROUPBOX "Muddy/Wet Footprints",IDC_STATIC,3,134,101,88 + LTEXT "Intensity %",IDC_STATIC,8,7,47,8 + CONTROL "Custom1",IDC_COMP_FP_INTENSITY,"CustEdit",WS_TABSTOP,58, + 7,27,10 + CONTROL "Custom2",IDC_COMP_FP_INTENSITY_SPIN,"SpinnerControl", + WS_TABSTOP,86,7,7,10 + GROUPBOX "Particles",IDC_STATIC,3,230,101,92 + LISTBOX IDC_LIST_TARGS2,5,238,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_PARTICLE,5,284,47,14 + CONTROL "Remove",IDC_DEL_TARGS2,"CustButton",WS_TABSTOP,56,284, + 47,14 + LTEXT "Spew secs",IDC_STATIC,11,306,47,8 + CONTROL "Custom1",IDC_COMP_FP_PARTYTIME,"CustEdit",WS_TABSTOP,61, + 305,27,10 + CONTROL "Custom2",IDC_COMP_FP_PARTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,89,305,7,10 +END + +IDD_COMP_GUIPROXY DIALOG 0, 0, 106, 38 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Use better hit testing \n(slower)",IDC_GUI_BETTERHIT, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,9,7, + 86,22 +END + +IDD_COMP_FILTERINHERIT DIALOG 0, 0, 111, 80 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "X",IDC_COMP_FILTER_NOX,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,16,41,21,10 + CONTROL "Y",IDC_COMP_FILTER_NOY,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,41,41,21,10 + CONTROL "Z",IDC_COMP_FILTER_NOZ,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,64,42,21,10 + GROUPBOX "Disable Move Inherit",IDC_STATIC,12,27,84,29 + CONTROL "Disable Rotate Inherit",IDC_COMP_FILTER_ACTIVE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,10,84,10 +END + +IDD_COMP_SOUNDBASE DIALOGEX 0, 0, 108, 168 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + CONTROL "",IDC_COMP_SOUND3D_FILENAME_BTN,"CustButton",WS_TABSTOP, + 7,14,93,13 + CONTROL "Auto Start",IDC_COMP_SOUND3D_AUTOSTART_CKBX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,30,47,10 + CONTROL "Loop:",IDC_COMP_SOUND3D_LOOPCHKBOX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,66,30,34,10 + COMBOBOX IDC_LOOP_COMBO,7,43,93,75,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "Custom1",IDC_COMP_SOUND3D_VOLSLIDER,"SliderControl", + WS_TABSTOP,7,83,92,15 + LTEXT "Sound Name:",IDC_SOUND_FILENAME_TEXT,7,5,44,8 + GROUPBOX "Volume",IDC_STATIC,3,72,100,63 + CONTROL "Custom2",IDC_COMP_SOUND3D_SLIDERVIEWER,"CustEdit", + WS_TABSTOP,60,113,28,10 + LTEXT "-48 db",IDC_STATIC,7,99,25,8 + RTEXT "0 db",IDC_STATIC,81,99,15,8 + RTEXT "-12",IDC_STATIC,64,99,11,8 + RTEXT "-24",IDC_STATIC,47,99,12,8 + RTEXT "-36",IDC_STATIC,33,99,10,8 + CONTROL "Disable Load-on-Demand",IDC_COMP_SOUND_DISABLELOD, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,60,94,10 + CONTROL "Show in TrackView",IDC_SND_TRACKVIEW,"Button", + BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,9,110,50,18 + CTEXT "db",IDC_STATIC,89,114,9,8 + GROUPBOX "Category",IDC_STATIC,3,137,100,27 + COMBOBOX IDC_SND_CATEGORY,9,146,88,82,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP +END + +IDD_COMP_STEREIZE DIALOG 0, 0, 110, 132 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Left Channel",IDC_COMP_STEREIZE_LEFT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,10,56,10 + LTEXT "Ambient dist",IDC_STATIC,8,28,47,8 + CONTROL "Custom1",IDC_COMP_STEREIZE_AMB,"CustEdit",WS_TABSTOP,58, + 28,27,10 + CONTROL "Custom2",IDC_COMP_STEREIZE_AMB_SPIN,"SpinnerControl", + WS_TABSTOP,86,28,7,10 + LTEXT "Transition",IDC_STATIC,7,40,47,8 + CONTROL "Custom1",IDC_COMP_STEREIZE_TRANS,"CustEdit",WS_TABSTOP, + 57,40,27,10 + CONTROL "Custom2",IDC_COMP_STEREIZE_TRANS_SPIN,"SpinnerControl", + WS_TABSTOP,85,40,7,10 + LTEXT "Sep Angle",IDC_STATIC,7,57,47,8 + CONTROL "Custom1",IDC_COMP_STEREIZE_ANG,"CustEdit",WS_TABSTOP,57, + 57,27,10 + CONTROL "Custom2",IDC_COMP_STEREIZE_ANG_SPIN,"SpinnerControl", + WS_TABSTOP,85,57,7,10 + LTEXT "Max Sep Dist",IDC_STATIC,7,75,47,8 + CONTROL "Custom1",IDC_COMP_STEREIZE_MAXDIST,"CustEdit", + WS_TABSTOP,57,75,27,10 + CONTROL "Custom2",IDC_COMP_STEREIZE_MAXDIST_SPIN,"SpinnerControl", + WS_TABSTOP,85,75,7,10 + LTEXT "Min Sep Dist",IDC_STATIC,6,87,47,8 + CONTROL "Custom1",IDC_COMP_STEREIZE_MINDIST,"CustEdit", + WS_TABSTOP,56,87,27,10 + CONTROL "Custom2",IDC_COMP_STEREIZE_MINDIST_SPIN,"SpinnerControl", + WS_TABSTOP,84,87,7,10 +END + +IDD_COMP_CLIMB_TRIGGER DIALOG 0, 0, 108, 90 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Command:",IDC_STATIC,7,6,34,8 + COMBOBOX IDC_COMP_CLIMB_COMMAND,7,16,94,47,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "(none)",IDC_COMP_WALL_PICK,7,70,94,14 + LTEXT "Climbing Wall:",IDC_STATIC,7,60,83,8 + LTEXT "Direction:",IDC_STATIC,7,33,31,8 + COMBOBOX IDC_COMP_CLIMB_DIRECTION,7,43,94,47,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP +END + +IDD_COMP_UNLEASH DIALOG 0, 0, 111, 448 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Use of this component will unleash Satan to wreak havok upon everyone and everything you hold dear.", + IDC_STATIC,11,22,85,35 + GROUPBOX "Warning",IDC_STATIC,5,11,98,160 + LTEXT "Use with discretion.",IDC_STATIC,21,156,62,8 + CTEXT "Finally, it will format your hard drrive and install Windows 3.0.", + IDC_STATIC,10,128,88,25 + CTEXT "It will login as you to Charles Schwab and convert your entire portfolio to Enron stock.", + IDC_STATIC,15,92,76,33 + CTEXT "It will then mail the contents of your hard drive to the IRS and your mother.", + IDC_STATIC,16,58,75,34 +END + +IDD_COMP_SWIVEL DIALOG 0, 0, 111, 263 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Listener",IDC_RADIO_LISTENER,"Button", + BS_AUTORADIOBUTTON,20,33,41,10 + CONTROL "Local Player",IDC_RADIO_PLAYER,"Button", + BS_AUTORADIOBUTTON,20,44,55,10 + CONTROL "Object",IDC_RADIO_OBJECT,"Button",BS_AUTORADIOBUTTON,20, + 55,37,10 + CONTROL "Pick",IDC_COMP_CHOOSE_OBJECT,"CustButton",WS_TABSTOP,12, + 71,86,9 + GROUPBOX "Target Type",IDC_STATIC,7,7,98,82 + CONTROL "Camera",IDC_RADIO_CAMERA,"Button",BS_AUTORADIOBUTTON,20, + 22,40,10 + GROUPBOX "Pivot",IDC_STATIC,7,92,96,32 + GROUPBOX "Offset",IDC_STATIC,9,131,90,59 + CONTROL "Active",IDC_COMP_OFFSETACTIVE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,13,140,36,10 + LTEXT "X",IDC_STATIC,22,155,13,8 + CONTROL "Custom1",IDC_COMP_OFFSETX,"CustEdit",WS_TABSTOP,38,154, + 27,10 + CONTROL "Custom2",IDC_COMP_OFFSETX_SPIN,"SpinnerControl", + WS_TABSTOP,66,154,7,10 + LTEXT "Y",IDC_STATIC,22,166,13,8 + CONTROL "Custom1",IDC_COMP_OFFSETY,"CustEdit",WS_TABSTOP,38,166, + 27,10 + CONTROL "Custom2",IDC_COMP_OFFSETY_SPIN,"SpinnerControl", + WS_TABSTOP,66,166,7,10 + LTEXT "Z",IDC_STATIC,22,177,13,8 + CONTROL "Custom1",IDC_COMP_OFFSETZ,"CustEdit",WS_TABSTOP,38,177, + 27,10 + CONTROL "Custom2",IDC_COMP_OFFSETZ_SPIN,"SpinnerControl", + WS_TABSTOP,66,177,7,10 + CONTROL "Local",IDC_COMP_OFFSETLOCAL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,52,140,33,10 + CONTROL "Pivot on local Y",IDC_COMP_PIVOTY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,18,107,65,10 +END + +IDD_COMP_MORPHLAY DIALOG 0, 0, 110, 144 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST_TARGS,5,15,98,45,WS_VSCROLL | WS_TABSTOP + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,59,63,47, + 14 + CONTROL "Add",IDC_ADD_TARGS,"CustButton",WS_TABSTOP,6,63,47,14 + LTEXT "Delta meshes",IDC_STATIC,13,4,44,8 +END + +IDD_COMP_MORPHSEQ DIALOG 0, 0, 111, 134 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_CHOOSE_OBJECT,"CustButton",WS_TABSTOP,11, + 19,86,9 + LISTBOX IDC_LIST_TARGS,6,46,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_TARGS,6,89,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,56,89,47, + 14 + GROUPBOX "Neutral Mesh",IDC_STATIC,6,9,97,23 + GROUPBOX "Morph Layers",IDC_STATIC,2,35,105,76 +END + +IDD_COMP_OBJECT_FOLLOWCAM DIALOG 0, 0, 110, 397 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Offset X:",IDC_STATIC,17,64,28,8 + LTEXT "Offset Z:",IDC_STATIC,17,90,28,8 + LTEXT "Offset Y:",IDC_STATIC,17,78,28,8 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX,"CustEdit",WS_TABSTOP,47, + 63,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX,"SpinnerControl", + WS_TABSTOP,78,63,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY,"CustEdit",WS_TABSTOP,47, + 75,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY,"SpinnerControl", + WS_TABSTOP,78,75,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETZ,"CustEdit",WS_TABSTOP,47, + 88,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETZ,"SpinnerControl", + WS_TABSTOP,78,88,7,10 + CONTROL "Offset in Worldspace",IDC_COMP_AUTOCAM_POA_WORLDSPACE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,119,82,10 + LTEXT "Object Follow Camera",IDC_STATIC,7,7,81,8 + LTEXT "Offset X:",IDC_STATIC,17,130,28,8 + LTEXT "Offset Z:",IDC_STATIC,17,158,28,8 + LTEXT "Offset Y:",IDC_STATIC,17,144,28,8 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETX5,"CustEdit",WS_TABSTOP, + 47,130,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETX5,"SpinnerControl", + WS_TABSTOP,78,130,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETY4,"CustEdit",WS_TABSTOP, + 47,142,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETY4,"SpinnerControl", + WS_TABSTOP,78,142,7,10 + CONTROL "Custom1",IDC_CAMERACMD_OFFSETZ2,"CustEdit",WS_TABSTOP, + 47,154,30,10 + CONTROL "Custom2",IDC_CAMERACMD_SPIN_OFFSETZ2,"SpinnerControl", + WS_TABSTOP,78,154,7,10 + CONTROL "Cut to follow pos",IDC_COMP_AUTOCAM_LOS2,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,266,68,10 + CONTROL "Offset in Worldspace",IDC_COMP_AUTOCAM_POS_WORLDSPACE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,52,82,10 + LTEXT "Acceleration ft/s",IDC_STATIC,9,187,53,8 + CONTROL "Custom1",IDC_CAMERA_ACCEL,"CustEdit",WS_TABSTOP,9,195, + 30,10 + CONTROL "Custom2",IDC_CAMERA_ACCEL_SPIN,"SpinnerControl", + WS_TABSTOP,41,195,7,10 + LTEXT "Max Velocity ft/s",IDC_STATIC,9,210,54,8 + CONTROL "Custom1",IDC_CAMERA_VEL,"CustEdit",WS_TABSTOP,9,219,30, + 10 + CONTROL "Custom2",IDC_CAMERA_VEL_SPIN,"SpinnerControl", + WS_TABSTOP,41,219,7,10 + LTEXT "Deceleration ft/s",IDC_STATIC,9,233,54,8 + CONTROL "Custom1",IDC_CAMERA_DECEL,"CustEdit",WS_TABSTOP,9,242, + 30,10 + CONTROL "Custom2",IDC_CAMERA_DECEL_SPIN,"SpinnerControl", + WS_TABSTOP,41,242,7,10 + GROUPBOX "Positional Speeds",IDC_STATIC,7,176,70,84 + LTEXT "Acceleration ft/s",IDC_STATIC,9,294,53,8 + CONTROL "Custom1",IDC_CAMERA_ACCEL2,"CustEdit",WS_TABSTOP,9,302, + 30,10 + CONTROL "Custom2",IDC_CAMERA_ACCEL_SPIN2,"SpinnerControl", + WS_TABSTOP,41,302,7,10 + LTEXT "Max Velocity ft/s",IDC_STATIC,9,318,54,8 + CONTROL "Custom1",IDC_CAMERA_VEL2,"CustEdit",WS_TABSTOP,9,326,30, + 10 + CONTROL "Custom2",IDC_CAMERA_VEL_SPIN2,"SpinnerControl", + WS_TABSTOP,41,326,7,10 + LTEXT "Deceleration ft/s",IDC_STATIC,9,341,54,8 + CONTROL "Custom1",IDC_CAMERA_DECEL2,"CustEdit",WS_TABSTOP,9,350, + 30,10 + CONTROL "Custom2",IDC_CAMERA_DECEL_SPIN2,"SpinnerControl", + WS_TABSTOP,41,350,7,10 + GROUPBOX "POA Speeds",IDC_STATIC,7,283,70,84 + GROUPBOX "POA Offset",IDC_STATIC,7,104,86,66 + GROUPBOX "Position Offset",IDC_STATIC,7,38,85,66 + CONTROL "Lock POA to Object",IDC_COMP_AUTOCAM_LOS3,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,371,79,10 + CONTROL "Maintain LOS",IDC_COMP_AUTOCAM_LOS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,24,59,10 +END + +IDD_COMP_EMOTE DIALOG 0, 0, 108, 82 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Body Usage",IDC_STATIC,3,3,102,48 + CONTROL "Unknown",IDC_BODY_UNKNOWN,"Button",BS_AUTORADIOBUTTON,9, + 14,47,10 + CONTROL "Upper Body",IDC_BODY_UPPER,"Button",BS_AUTORADIOBUTTON, + 9,25,53,10 + CONTROL "Full Body",IDC_BODY_FULL,"Button",BS_AUTORADIOBUTTON,9, + 36,45,10 + LTEXT "Fade In",IDC_FADE_IN,8,56,47,8 + CONTROL "Custom1",IDC_EMO_FADEIN,"CustEdit",WS_TABSTOP,57,55,27, + 10 + CONTROL "Custom2",IDC_EMO_FADEIN_SPIN,"SpinnerControl", + WS_TABSTOP,85,55,7,10 + LTEXT "Fade Out",IDC_FADE_OUT,7,66,47,8 + CONTROL "Custom1",IDC_EMO_FADEOUT,"CustEdit",WS_TABSTOP,57,66,27, + 10 + CONTROL "Custom2",IDC_EMO_FADEOUT_SPIN,"SpinnerControl", + WS_TABSTOP,85,66,7,10 +END + +IDD_COMP_W_GEOWATER DIALOG 0, 0, 110, 131 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Min WaveLen",IDC_STATIC,8,13,47,8 + CONTROL "Custom1",IDC_COMP_W_GEO_MINLEN,"CustEdit",WS_TABSTOP,58, + 12,27,10 + CONTROL "Custom2",IDC_COMP_W_GEO_MINLEN_SPIN,"SpinnerControl", + WS_TABSTOP,86,12,7,10 + LTEXT "Max WaveLen",IDC_STATIC,7,26,47,8 + CONTROL "Custom1",IDC_COMP_W_GEO_MAXLEN,"CustEdit",WS_TABSTOP,57, + 25,27,10 + CONTROL "Custom2",IDC_COMP_W_GEO_MAXLEN_SPIN,"SpinnerControl", + WS_TABSTOP,85,25,7,10 + LTEXT "Amp/Len %",IDC_STATIC,7,40,47,8 + CONTROL "Custom1",IDC_COMP_W_GEO_AMPOVERLEN,"CustEdit", + WS_TABSTOP,57,40,27,10 + CONTROL "Custom2",IDC_COMP_W_GEO_AMPOVERLEN_SPIN,"SpinnerControl", + WS_TABSTOP,85,40,7,10 + LTEXT "Choppiness %",IDC_STATIC,7,57,47,8 + CONTROL "Custom1",IDC_COMP_W_GEO_CHOP,"CustEdit",WS_TABSTOP,57, + 57,27,10 + CONTROL "Custom2",IDC_COMP_W_GEO_CHOP_SPIN,"SpinnerControl", + WS_TABSTOP,85,57,7,10 + LTEXT "Spread Deg",IDC_STATIC,7,74,47,8 + CONTROL "Custom1",IDC_COMP_W_GEO_ANGLEDEV,"CustEdit",WS_TABSTOP, + 57,74,27,10 + CONTROL "Custom2",IDC_COMP_W_GEO_ANGLEDEV_SPIN,"SpinnerControl", + WS_TABSTOP,85,74,7,10 + CONTROL "User1",IDC_COMP_W_SPECULARTINT,"ColorSwatch",WS_GROUP, + 57,96,22,13 + CONTROL "Custom1",IDC_COMP_W_GEO_SPECMUTE,"CustEdit",WS_TABSTOP, + 56,112,27,10 + CONTROL "Custom2",IDC_COMP_W_GEO_SPECMUTE_SPIN,"SpinnerControl", + WS_TABSTOP,84,112,7,10 + LTEXT "Tint",IDC_STATIC,25,98,25,8 + LTEXT "Mute",IDC_STATIC,25,112,25,8 + GROUPBOX "Reflection",IDC_STATIC,8,88,92,36 +END + +IDD_COMP_W_ADVWATER DIALOG 0, 0, 111, 109 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Opacity Start",IDC_STATIC,8,11,47,8 + CONTROL "Custom1",IDC_COMP_W_ZEROOPAC,"CustEdit",WS_TABSTOP,58, + 10,27,10 + CONTROL "Custom2",IDC_COMP_W_ZEROOPAC_SPIN,"SpinnerControl", + WS_TABSTOP,86,10,7,10 + LTEXT "Opacity End",IDC_STATIC,7,25,47,8 + CONTROL "Custom1",IDC_COMP_W_DEPTHOPAC,"CustEdit",WS_TABSTOP,57, + 23,27,10 + CONTROL "Custom2",IDC_COMP_W_DEPTHOPAC_SPIN,"SpinnerControl", + WS_TABSTOP,85,23,7,10 + LTEXT "Reflect Start",IDC_STATIC,7,42,47,8 + CONTROL "Custom1",IDC_COMP_W_ZEROREFL,"CustEdit",WS_TABSTOP,57, + 40,27,10 + CONTROL "Custom2",IDC_COMP_W_ZEROREFL_SPIN,"SpinnerControl", + WS_TABSTOP,85,40,7,10 + LTEXT "Reflect End",IDC_STATIC,7,56,47,8 + CONTROL "Custom1",IDC_COMP_W_DEPTHREFL,"CustEdit",WS_TABSTOP,57, + 53,27,10 + CONTROL "Custom2",IDC_COMP_W_DEPTHREFL_SPIN,"SpinnerControl", + WS_TABSTOP,85,53,7,10 + LTEXT "Wave Start",IDC_STATIC,7,70,47,8 + CONTROL "Custom1",IDC_COMP_W_ZEROWAVE,"CustEdit",WS_TABSTOP,57, + 69,27,10 + CONTROL "Custom2",IDC_COMP_W_ZEROWAVE_SPIN,"SpinnerControl", + WS_TABSTOP,85,69,7,10 + LTEXT "Wave End",IDC_STATIC,7,84,47,8 + CONTROL "Custom1",IDC_COMP_W_DEPTHWAVE,"CustEdit",WS_TABSTOP,57, + 82,27,10 + CONTROL "Custom2",IDC_COMP_W_DEPTHWAVE_SPIN,"SpinnerControl", + WS_TABSTOP,85,82,7,10 +END + +IDD_COMP_W_BASICSHORE DIALOG 0, 0, 109, 51 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Wispiness",IDC_STATIC,7,36,47,8 + CONTROL "Custom1",IDC_COMP_W_WISPINESS,"CustEdit",WS_TABSTOP,57, + 36,27,10 + CONTROL "Custom2",IDC_COMP_W_WISPINESS_SPIN,"SpinnerControl", + WS_TABSTOP,85,36,7,10 + CONTROL "User1",IDC_COMP_W_SHORETINT,"ColorSwatch",WS_GROUP,58,4, + 22,12 + LTEXT "Shore Tint",IDC_STATIC,8,6,34,8 + LTEXT "Shore Opac",IDC_STATIC,7,19,47,8 + CONTROL "Custom1",IDC_COMP_W_SHOREOPAC,"CustEdit",WS_TABSTOP,57, + 19,27,10 + CONTROL "Custom2",IDC_COMP_W_SHOREOPAC_SPIN,"SpinnerControl", + WS_TABSTOP,85,19,7,10 +END + +IDD_COMP_W_ADVSHORE DIALOG 0, 0, 112, 81 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Finger Length",IDC_STATIC,8,29,47,8 + CONTROL "Custom1",IDC_COMP_W_FINGER,"CustEdit",WS_TABSTOP,58,28, + 27,10 + CONTROL "Custom2",IDC_COMP_W_FINGER_SPIN,"SpinnerControl", + WS_TABSTOP,86,28,7,10 + LTEXT "Period",IDC_STATIC,8,11,47,8 + CONTROL "Custom1",IDC_COMP_W_PERIOD,"CustEdit",WS_TABSTOP,58,11, + 27,10 + CONTROL "Custom2",IDC_COMP_W_PERIOD_SPIN,"SpinnerControl", + WS_TABSTOP,86,11,7,10 + LTEXT "Edge Opacity",IDC_STATIC,7,46,47,8 + CONTROL "Custom1",IDC_COMP_W_EDGEOPAC,"CustEdit",WS_TABSTOP,57, + 46,27,10 + CONTROL "Custom2",IDC_COMP_W_EDGEOPAC_SPIN,"SpinnerControl", + WS_TABSTOP,85,46,7,10 + LTEXT "Edge Thick",IDC_STATIC,7,58,47,8 + CONTROL "Custom1",IDC_COMP_W_EDGERADIUS,"CustEdit",WS_TABSTOP,57, + 58,27,10 + CONTROL "Custom2",IDC_COMP_W_EDGERADIUS_SPIN,"SpinnerControl", + WS_TABSTOP,85,58,7,10 +END + +IDD_COMP_W_REFOBJECT DIALOG 0, 0, 112, 42 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_W_REFOBJECT,"CustButton",WS_TABSTOP,10, + 25,86,9 + LTEXT "Reference Object",IDC_STATIC,27,10,57,8 +END + +IDD_COMP_SHORE DIALOG 0, 0, 110, 66 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_SHORE_CHOSE,"CustButton",WS_TABSTOP,10, + 18,86,9 + LTEXT "Water Component",IDC_STATIC,23,6,58,8 + GROUPBOX "Vertex Colors",IDC_STATIC,10,34,89,28 + LTEXT "Red = Opacity",IDC_STATIC,18,47,46,8 +END + +IDD_COMP_GUIPROGRESS DIALOG 0, 0, 106, 167 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Lower:",IDC_STATIC,7,65,22,8 + CONTROL "Custom2",IDC_GUI_LOWER,"CustEdit",WS_TABSTOP,7,75,31,10 + CONTROL "Custom2",IDC_GUI_LOWER_SPIN,"SpinnerControl",WS_TABSTOP, + 39,75,7,10 + CONTROL "Custom2",IDC_GUI_UPPER,"CustEdit",WS_TABSTOP,58,75,31, + 10 + CONTROL "Custom2",IDC_GUI_UPPER_SPIN,"SpinnerControl",WS_TABSTOP, + 90,75,7,10 + LTEXT "Upper:",IDC_STATIC,59,65,22,8 + GROUPBOX "Range",IDC_STATIC,3,56,99,49 + LTEXT "Step:",IDC_STATIC,20,89,18,8 + CONTROL "Custom2",IDC_GUI_STEP,"CustEdit",WS_TABSTOP,40,89,31,10 + CONTROL "Custom2",IDC_GUI_STEP_SPIN,"SpinnerControl",WS_TABSTOP, + 72,89,7,10 + CONTROL "Reverse values",IDC_GUI_REVERSE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,109,65,10 + CONTROL "Pick",IDC_GUI_COMPSELBTN,"CustButton",WS_TABSTOP,8,17, + 88,12 + CONTROL "Pick",IDC_GUI_ANIMNODESEL,"CustButton",WS_TABSTOP,21,32, + 75,12 + LTEXT "on",IDC_STATIC,9,34,9,8 + GROUPBOX "Animation",IDC_STATIC,3,4,99,49 + GROUPBOX "Sound",IDC_STATIC,3,120,97,40 + CONTROL "Pick",IDC_GUI_ANIMSNDCOMP,"CustButton",WS_TABSTOP,7,140, + 88,12 + CONTROL "Play snd on animate",IDC_GUI_ANIMSND,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,0,129,96,10 +END + +IDD_COMP_W_ENVMAP DIALOG 0, 0, 111, 98 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_W_ENVOBJECT,"CustButton",WS_TABSTOP,11, + 24,86,9 + LTEXT "Reference Object",IDC_STATIC,25,9,57,8 + LTEXT "EnvMap Size",IDC_STATIC,8,45,47,8 + CONTROL "Custom1",IDC_COMP_W_ENVSIZE,"CustEdit",WS_TABSTOP,58,44, + 27,10 + CONTROL "Custom2",IDC_COMP_W_ENVSIZE_SPIN,"SpinnerControl", + WS_TABSTOP,86,44,7,10 + LTEXT "Radius",IDC_STATIC,8,61,47,8 + CONTROL "Custom1",IDC_COMP_W_ENVRADIUS,"CustEdit",WS_TABSTOP,58, + 60,27,10 + CONTROL "Custom2",IDC_COMP_W_ENVRADIUS_SPIN,"SpinnerControl", + WS_TABSTOP,86,60,7,10 + LTEXT "Refresh secs",IDC_STATIC,7,76,47,8 + CONTROL "Custom1",IDC_COMP_W_ENVREFRESH,"CustEdit",WS_TABSTOP,57, + 75,27,10 + CONTROL "Custom2",IDC_COMP_W_ENVREFRESH_SPIN,"SpinnerControl", + WS_TABSTOP,85,75,7,10 +END + +IDD_COMP_MAINTAINERS_MARKER DIALOG 0, 0, 108, 57 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Broken (random)",IDC_RADIO_BROKEN,"Button", + BS_AUTORADIOBUTTON,5,11,88,8 + CONTROL "Repaired (Zeros)",IDC_RADIO_REPAIRED,"Button", + BS_AUTORADIOBUTTON,6,24,100,8 + CONTROL "Calibrated (working)",IDC_RADIO_CALIBRATED,"Button", + BS_AUTORADIOBUTTON,6,38,97,8 +END + +IDD_COMP_NOSHOW DIALOG 0, 0, 112, 226 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Disable Only because",IDC_COMP_NOSHOW_SHOWABLE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,9,85,15 + CONTROL "Drawables",IDC_COMP_NOSHOW_AFFECTDRAW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,20,148,49,10 + GROUPBOX "Affect:",IDC_STATIC,5,136,98,42 + CONTROL "Physicals",IDC_COMP_NOSHOW_AFFECTPHYS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,20,160,45,10 + CTEXT "I might want to enable this later. So don't discard it completely, Only Disable it.", + IDC_STATIC,14,23,85,28 + CTEXT "Don't do anything that will get in the way of me enabling it later, I want you to Disable it Only.", + IDC_STATIC,16,50,77,35 + CTEXT "If I wasn't going to enable it later, you could just toss it as an optimization, but I will enable it, so you can't, you need to Only Disable it.", + IDC_STATIC,14,85,81,47 +END + +IDD_COMP_GUICLICKMAP DIALOG 0, 0, 106, 26 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Report while dragging",IDC_GUI_REPORTDRAG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,3,9,85,10 +END + +IDD_COMP_VISREGION DIALOG 0, 0, 112, 134 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_VISREGION_CHOOSE_VOLUME,"CustButton", + WS_TABSTOP,10,17,86,9 + GROUPBOX "Region",IDC_STATIC,3,7,97,26 + CONTROL "Drawables",IDC_COMP_VISREGION_DRAW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,20,63,49,10 + GROUPBOX "Affect:",IDC_STATIC,5,51,98,54 + CONTROL "Lights",IDC_COMP_VISREGION_LIGHT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,20,75,35,10 + CONTROL "Occluders",IDC_COMP_VISREGION_OCC,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,20,87,48,10 + CONTROL "Excludes",IDC_COMP_VISREGION_NOT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,38,45,10 + CONTROL "Disable Un-VisRegioned",IDC_COMP_VISREGION_DIS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,112,93,10 +END + +IDD_COMP_SMOOTHAV DIALOG 0, 0, 110, 58 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Distance Tol",IDC_STATIC,11,9,44,8 + CONTROL "Custom1",IDC_COMP_SMOOTHAV_DIST,"CustEdit",WS_TABSTOP, + 61,8,27,10 + CONTROL "Custom2",IDC_COMP_SMOOTHAV_DIST_SPIN,"SpinnerControl", + WS_TABSTOP,89,8,7,10 + LTEXT "Max Degrees",IDC_STATIC,11,25,44,8 + CONTROL "Custom1",IDC_COMP_SMOOTHAV_ANGLE,"CustEdit",WS_TABSTOP, + 61,23,27,10 + CONTROL "Custom2",IDC_COMP_SMOOTHAV_ANGLE_SPIN,"SpinnerControl", + WS_TABSTOP,89,23,7,10 + CONTROL "Snap Positions",IDC_COMP_SMOOTHAV_POS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,40,63,10 +END + +IDD_COMP_SKINEDIT DIALOGEX 0, 0, 406, 273 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "GUI Skin Editor" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Save",IDOK,349,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,349,24,50,14 + GROUPBOX "Elements",IDC_STATIC,7,7,95,259 + LISTBOX IDC_GUI_ELEMENTS,11,19,86,243,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_GUI_PREVIEW,"Static",SS_BLACKFRAME,105,7,230,249, + WS_EX_STATICEDGE + SCROLLBAR IDC_GUI_HORZSCROLL,105,257,229,9 + SCROLLBAR IDC_GUI_VERTSCROLL,337,7,10,248,SBS_VERT + LTEXT "Static",IDC_GUI_INFO,350,63,49,47 + PUSHBUTTON "IDI_ZOOMIN",IDC_GUI_ZIN,358,43,15,14,BS_ICON | + BS_CENTER | BS_VCENTER,WS_EX_CLIENTEDGE + PUSHBUTTON "IDI_ZOOMOUT",IDC_GUI_ZOUT,376,43,15,14,BS_ICON | + BS_CENTER | BS_VCENTER,WS_EX_CLIENTEDGE +END + +IDD_COMP_GUISKIN DIALOG 0, 0, 106, 90 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_GUI_SKINBMAP,"CustButton",WS_TABSTOP,10,14, + 88,12 + GROUPBOX "Bitmap",IDC_STATIC,4,3,99,29 + PUSHBUTTON "Edit Elements",IDC_GUI_EDITELEM,28,37,50,14 + RTEXT "Item Margin:",IDC_STATIC,15,58,40,8 + RTEXT "Border Margin:",IDC_STATIC,8,70,47,8 + CONTROL "Custom2",IDC_GUI_IMARGIN,"CustEdit",WS_TABSTOP,58,58,31, + 10 + CONTROL "Custom2",IDC_GUI_IMARGIN_SPIN,"SpinnerControl", + WS_TABSTOP,90,58,7,10 + CONTROL "Custom2",IDC_GUI_BMARGIN,"CustEdit",WS_TABSTOP,58,71,31, + 10 + CONTROL "Custom2",IDC_GUI_BMARGIN_SPIN,"SpinnerControl", + WS_TABSTOP,90,71,7,10 +END + +IDD_COMP_GUIMENUANCHOR DIALOG 0, 0, 106, 198 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Custom1",IDC_GUI_SKIN,"CustButton",WS_TABSTOP,9,66,88, + 12 + GROUPBOX "Menu Skin",IDC_STATIC,3,55,99,29 + CONTROL "Never close",IDC_GUI_NEVERCLOSE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,91,54,10 + CONTROL "Modal underneath menus",IDC_GUI_MODALOUTSIDE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,104,96,10 + CONTROL "Enable sub-menu hover",IDC_GUI_HOVER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,5,117,91,10 + RTEXT "Name:",IDC_STATIC,6,24,22,8 + CONTROL "Custom2",IDC_GUI_VERSION,"CustEdit",WS_TABSTOP,33,40,31, + 10 + CONTROL "Custom2",IDC_GUI_VERSION_SPIN,"SpinnerControl", + WS_TABSTOP,65,40,7,10 + LTEXT "Version:",IDC_STATIC,4,41,26,8 + COMBOBOX IDC_GUIDLG_NAME,31,22,70,144,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + RTEXT "Age:",IDC_STATIC,12,8,16,8 + COMBOBOX IDC_GUIDLG_AGE,31,5,70,144,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + GROUPBOX "Alignment",IDC_STATIC,4,149,99,41 + CONTROL "Up-Left",IDC_ALIGNRADIO1,"Button",BS_AUTORADIOBUTTON,8, + 161,39,10 + CONTROL "Up-Right",IDC_ALIGNRADIO2,"Button",BS_AUTORADIOBUTTON, + 54,161,44,10 + CONTROL "Dn-Left",IDC_ALIGNRADIO3,"Button",BS_AUTORADIOBUTTON,8, + 175,39,10 + CONTROL "Dn-Right",IDC_ALIGNRADIO4,"Button",BS_AUTORADIOBUTTON, + 54,175,44,10 + CONTROL "Maintain size across resolutions",IDC_GUI_SCALERES, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,5, + 127,95,21 +END + +IDD_COMP_WDECAL DIALOG 0, 0, 111, 221 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_WDECAL_CHOSE,"CustButton",WS_TABSTOP,10, + 21,86,9 + LTEXT "Water Component",IDC_STATIC,23,9,58,8 + CONTROL "Environment Mapped",IDC_COMP_WDECAL_ENV,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,52,83,10 + CTEXT "If you run your ripple texture through the NVidia Photoshop Bumpmap plugin and check the Env Mapped checkbox, you'll get bump reflected ripples instead of a straight texture.", + IDC_STATIC,13,68,81,74 + GROUPBOX "Environment Option",IDC_STATIC,9,38,91,108 + GROUPBOX "Vertex Colors",IDC_STATIC,10,153,89,42 + LTEXT "Red = Opacity",IDC_STATIC,18,166,46,8 + LTEXT "Green = Greyscale Color",IDC_STATIC,16,178,78,8 +END + +IDD_COMP_ANIMCAM_COMMANDS DIALOG 0, 0, 108, 144 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "animated camera commands",IDC_STATIC,0,1,102,139 + CONTROL "",IDC_BEGINONPUSH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 7,15,8,8 + CONTROL "",IDC_RESETONPOP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 7,79,8,8 + LTEXT "Begin animation when transition to this camera completes", + IDC_STATIC,19,13,82,32 + LTEXT "Reset animation to start point on transition away from this camera", + IDC_STATIC,18,78,77,33 + CONTROL "",IDC_STOPONPOP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7, + 46,8,10 + LTEXT "Stop animation when transitioning away from this camera", + IDC_STATIC,20,46,79,29 + LTEXT "Do not change camera FOV when running this animation", + IDC_STATIC,21,110,76,24 + CONTROL "",IDC_IGNOREFOV,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7, + 110,9,8 +END + +IDD_COMP_NETSYNC DIALOG 0, 0, 108, 120 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Local Only",IDC_LOCAL_ONLY_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,6,5,49,10 + CONTROL "Override High Level SDL",IDC_OVERRIDE_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,6,29,95,10 + LTEXT "Allow",IDC_STATIC,38,41,18,8 + LTEXT "Volatile",IDC_STATIC,59,41,24,8 + LTEXT "Deny",IDC_STATIC,86,41,17,8 + LTEXT "Physical:",IDC_STATIC,4,52,29,8 + CONTROL "",IDC_PHYS_ALLOW_RADIO,"Button",BS_AUTORADIOBUTTON,43, + 52,9,8 + CONTROL "",IDC_PHYS_NOSAVE_RADIO,"Button",BS_AUTORADIOBUTTON,67, + 52,9,8 + CONTROL "",IDC_PHYS_DENY_RADIO,"Button",BS_AUTORADIOBUTTON,91,52, + 9,8 + LTEXT "Animation:",IDC_STATIC,4,63,34,8 + CONTROL "",IDC_ANIM_ALLOW_RADIO,"Button",BS_AUTORADIOBUTTON,43, + 63,9,8 + CONTROL "",IDC_ANIM_NOSAVE_RADIO,"Button",BS_AUTORADIOBUTTON,67, + 63,9,8 + CONTROL "",IDC_ANIM_DENY_RADIO,"Button",BS_AUTORADIOBUTTON,91,63, + 9,8 + LTEXT "Sound:",IDC_STATIC,4,74,24,8 + CONTROL "",IDC_SND_ALLOW_RADIO,"Button",BS_AUTORADIOBUTTON,43,74, + 9,8 + CONTROL "",IDC_SND_NOSAVE_RADIO,"Button",BS_AUTORADIOBUTTON,67, + 74,9,8 + CONTROL "",IDC_SND_DENY_RADIO,"Button",BS_AUTORADIOBUTTON,91,74, + 9,8 + LTEXT "Material:",IDC_STATIC,4,85,28,8 + CONTROL "",IDC_MAT_ALLOW_RADIO,"Button",BS_AUTORADIOBUTTON,43,85, + 9,8 + CONTROL "",IDC_MAT_NOSAVE_RADIO,"Button",BS_AUTORADIOBUTTON,67, + 85,9,8 + CONTROL "",IDC_MAT_DENY_RADIO,"Button",BS_AUTORADIOBUTTON,91,85, + 9,8 + LTEXT "Responder:",IDC_STATIC,4,96,38,8 + CONTROL "",IDC_RESP_ALLOW_RADIO,"Button",BS_AUTORADIOBUTTON,43, + 96,9,8 + CONTROL "",IDC_RESP_NOSAVE_RADIO,"Button",BS_AUTORADIOBUTTON,67, + 96,9,8 + CONTROL "",IDC_RESP_DENY_RADIO,"Button",BS_AUTORADIOBUTTON,91,96, + 9,8 + LTEXT "X-Region:",IDC_STATIC,4,107,32,8 + CONTROL "",IDC_XREG_ALLOW_RADIO,"Button",BS_AUTORADIOBUTTON,43, + 107,9,8 + CONTROL "",IDC_XREG_NOSAVE_RADIO,"Button",BS_AUTORADIOBUTTON,67, + 107,9,8 + CONTROL "",IDC_XREG_DENY_RADIO,"Button",BS_AUTORADIOBUTTON,91, + 107,9,8 + GROUPBOX "SDL",IDC_STATIC,1,18,105,101 +END + +IDD_COMP_RELREGION DIALOG 0, 0, 112, 42 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_RELREGION_CHOOSE_VOLUME,"CustButton", + WS_TABSTOP,10,17,86,9 + GROUPBOX "Region",-1,3,7,97,26 +END + +IDD_COMP_REPRESENT DIALOG 0, 0, 111, 90 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_COMP_REPRESENT_QUALITY,7,20,92,66,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Minimum Quality Level",IDC_STATIC,12,7,71,8 +END + +IDD_COMP_REPGROUP DIALOG 0, 0, 112, 151 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST_REPS,3,23,98,59,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_REPS,4,89,47,14 + CONTROL "Remove",IDC_DEL_REPS,"CustButton",WS_TABSTOP,54,89,47, + 14 + PUSHBUTTON "Up",IDC_UP_REPS,4,110,47,14 + PUSHBUTTON "Down",IDC_DOWN_REPS,55,110,47,14 + PUSHBUTTON "Validate",IDC_VAL_REPS,30,130,47,14 +END + +IDD_COMP_PHYS_BRIDGE DIALOG 0, 0, 108, 175 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Bridge Links:",IDC_STATIC,4,2,99,83 + LTEXT "Upper Limit:",IDC_STATIC,9,15,38,8 + CONTROL "Custom3",IDC_COMP_PHYS_OPENANGLE_SPIN,"SpinnerControl", + WS_TABSTOP,82,14,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_OPENANGLE_EDIT,"CustEdit", + WS_TABSTOP,50,14,31,10 + LTEXT "Lower Limit:",IDC_STATIC,9,28,38,8 + CONTROL "Custom3",IDC_COMP_PHYS_CLOSEANGLE_SPIN,"SpinnerControl", + WS_TABSTOP,82,27,7,10 + CONTROL "Custom4",IDC_STIFFNESS_EDIT,"CustEdit",WS_TABSTOP,50,40, + 31,10 + LISTBOX IDC_SECT_LIST,7,90,78,80,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + CONTROL "Custom3",IDC_STIFFNESS_SPIN,"SpinnerControl",WS_TABSTOP, + 82,40,7,10 + CONTROL "Custom4",IDC_COMP_PHYS_CLOSEANGLE_EDIT,"CustEdit", + WS_TABSTOP,50,27,31,10 + LTEXT "Stiffness:",IDC_STATIC,9,41,30,8 + PUSHBUTTON "+",IDC_UP_BUTTON,91,114,12,12,WS_DISABLED + PUSHBUTTON "-",IDC_DN_BUTTON,91,130,12,12,WS_DISABLED + CONTROL "Custom4",IDC_STRENGTH_EDIT,"CustEdit",WS_TABSTOP,50,53, + 31,10 + CONTROL "Custom3",IDC_STRENGTH_SPIN,"SpinnerControl",WS_TABSTOP, + 82,53,7,10 + LTEXT "Strength:",IDC_STATIC,10,54,30,8 + PUSHBUTTON "Default Strength",IDC_DEF_STR_BUTTON,20,67,67,12 +END + +IDD_COMP_SMOOTHBASE DIALOG 0, 0, 110, 62 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Distance Tol",IDC_STATIC,11,9,44,8 + CONTROL "Custom1",IDC_COMP_SMOOTHBASE_DIST,"CustEdit",WS_TABSTOP, + 61,8,27,10 + CONTROL "Custom2",IDC_COMP_SMOOTHBASE_DIST_SPIN,"SpinnerControl", + WS_TABSTOP,89,8,7,10 + LTEXT "Max Degrees",IDC_STATIC,11,25,44,8 + CONTROL "Custom1",IDC_COMP_SMOOTHBASE_ANGLE,"CustEdit", + WS_TABSTOP,61,23,27,10 + CONTROL "Custom2",IDC_COMP_SMOOTHBASE_ANGLE_SPIN,"SpinnerControl", + WS_TABSTOP,89,23,7,10 + CONTROL "Snap Positions",IDC_COMP_SMOOTHBASE_POS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,40,63,10 +END + +IDD_COMP_SMOOTHSNAP DIALOG 0, 0, 111, 90 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick",IDC_COMP_SMOOTH_CHOSE,"CustButton",WS_TABSTOP,12, + 71,86,9 + LTEXT "Distance Tol",IDC_STATIC,11,9,44,8 + CONTROL "Custom1",IDC_COMP_SMOOTHSNAP_DIST,"CustEdit",WS_TABSTOP, + 61,8,27,10 + CONTROL "Custom2",IDC_COMP_SMOOTHSNAP_DIST_SPIN,"SpinnerControl", + WS_TABSTOP,89,8,7,10 + LTEXT "Max Degrees",IDC_STATIC,11,25,44,8 + CONTROL "Custom1",IDC_COMP_SMOOTHSNAP_ANGLE,"CustEdit", + WS_TABSTOP,61,23,27,10 + CONTROL "Custom2",IDC_COMP_SMOOTHSNAP_ANGLE_SPIN,"SpinnerControl", + WS_TABSTOP,89,23,7,10 + CONTROL "Snap Positions",IDC_COMP_SMOOTHSNAP_POS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,40,63,10 + LTEXT "Smoothing comp of base set",IDC_STATIC,9,58,90,8 +END + +IDD_COMP_IMAGELIB DIALOGEX 0, 0, 108, 166 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + LISTBOX IDC_IMAGE_LIST,5,13,96,83,WS_VSCROLL | WS_HSCROLL | + WS_TABSTOP + PUSHBUTTON "Add...",IDC_IMAGE_ADD,5,98,30,14 + PUSHBUTTON "Remove",IDC_IMAGE_REMOVE,68,98,33,14 + LTEXT "Images:",IDC_STATIC,4,4,26,8 + PUSHBUTTON "Edit...",IDC_IMAGE_EDIT,38,98,27,14 + GROUPBOX "Per Image Properties",IDC_STATIC,5,116,96,43 + CONTROL "Use compression",IDC_IL_COMPRESS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,130,70,10 + CONTROL "Will be used as texture",IDC_IL_FORCEPOW2,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,9,143,87,10 +END + +IDD_COMP_W_VTXHELP DIALOG 0, 0, 111, 90 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Vertex Colors",IDC_STATIC,8,10,89,56 + LTEXT "Red = Opacity",IDC_STATIC,14,23,46,8 + LTEXT "Green = Greyscale Color",IDC_STATIC,14,35,78,8 + LTEXT "Blue = Fresnel Strength",IDC_STATIC,14,48,74,8 +END + +IDD_COMP_PARTICLE_FLOCK DIALOGEX 0, 0, 110, 216 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "X:",IDC_STATIC,23,21,8,8 + LTEXT "Z:",IDC_STATIC,23,41,8,8 + LTEXT "Y:",IDC_STATIC,23,31,8,8 + CONTROL "Custom1",IDC_FLOCK_TARGET_OFFSETX,"CustEdit",WS_TABSTOP, + 34,19,30,10 + CONTROL "Custom2",IDC_FLOCK_TARGET_OFFSETX_SPIN,"SpinnerControl", + WS_TABSTOP,66,19,7,10 + CONTROL "Custom1",IDC_FLOCK_TARGET_OFFSETY,"CustEdit",WS_TABSTOP, + 34,29,30,10 + CONTROL "Custom2",IDC_FLOCK_TARGET_OFFSETY_SPIN,"SpinnerControl", + WS_TABSTOP,66,29,7,10 + CONTROL "Custom1",IDC_FLOCK_TARGET_OFFSETZ,"CustEdit",WS_TABSTOP, + 34,39,30,10 + CONTROL "Custom2",IDC_FLOCK_TARGET_OFFSETZ_SPIN,"SpinnerControl", + WS_TABSTOP,66,39,7,10 + LTEXT "Global Offset From Target:",IDC_STATIC,5,7,85,8 + LTEXT "Conformance Dist:",IDC_STATIC,5,57,58,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_FLOCK_CONFORM_DIST,"CustEdit",WS_TABSTOP, + 66,55,30,10 + CONTROL "Custom2",IDC_FLOCK_CONFORM_DIST_SPIN,"SpinnerControl", + WS_TABSTOP,98,55,7,10 + LTEXT "Strength:",IDC_STATIC,5,70,58,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_FLOCK_CONFORM_STR,"CustEdit",WS_TABSTOP,66, + 68,30,10 + CONTROL "Custom2",IDC_FLOCK_CONFORM_STR_SPIN,"SpinnerControl", + WS_TABSTOP,98,68,7,10 + LTEXT "Repel Dist:",IDC_STATIC,5,91,58,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_FLOCK_REPEL_DIST,"CustEdit",WS_TABSTOP,66, + 89,30,10 + CONTROL "Custom2",IDC_FLOCK_REPEL_DIST_SPIN,"SpinnerControl", + WS_TABSTOP,98,89,7,10 + LTEXT "Strength:",IDC_STATIC,5,104,58,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_FLOCK_REPEL_STR,"CustEdit",WS_TABSTOP,66, + 102,30,10 + CONTROL "Custom2",IDC_FLOCK_REPEL_STR_SPIN,"SpinnerControl", + WS_TABSTOP,98,102,7,10 + LTEXT "Desired Goal Dist:",IDC_STATIC,5,126,58,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_FLOCK_GOAL_DIST,"CustEdit",WS_TABSTOP,66, + 124,30,10 + CONTROL "Custom2",IDC_FLOCK_GOAL_DIST_SPIN,"SpinnerControl", + WS_TABSTOP,98,124,7,10 + LTEXT "Goal Orbit Str:",IDC_STATIC,5,167,58,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_FLOCK_GOAL_STR,"CustEdit",WS_TABSTOP,66, + 164,30,10 + CONTROL "Custom2",IDC_FLOCK_GOAL_STR_SPIN,"SpinnerControl", + WS_TABSTOP,98,164,7,10 + LTEXT "Max Chase Speed:",IDC_STATIC,2,188,61,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_FLOCK_MAX_SPEED,"CustEdit",WS_TABSTOP,66, + 186,30,10 + CONTROL "Custom2",IDC_FLOCK_MAX_SPEED_SPIN,"SpinnerControl", + WS_TABSTOP,98,186,7,10 + LTEXT "Goal Chase Str:",IDC_STATIC,5,153,58,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_FLOCK_GOAL_CHASE_STR,"CustEdit",WS_TABSTOP, + 66,151,30,10 + CONTROL "Custom2",IDC_FLOCK_GOAL_CHASE_STR_SPIN,"SpinnerControl", + WS_TABSTOP,98,151,7,10 + LTEXT "Full Chase Dist:",IDC_STATIC,8,139,55,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_FLOCK_FULL_CHASE_DIST,"CustEdit", + WS_TABSTOP,66,137,30,10 + CONTROL "Custom2",IDC_FLOCK_FULL_CHASE_DIST_SPIN,"SpinnerControl", + WS_TABSTOP,98,137,7,10 + LTEXT "Max Orbit Speed:",IDC_STATIC,2,201,61,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_FLOCK_MAX_ORBIT_SPEED,"CustEdit", + WS_TABSTOP,66,199,30,10 + CONTROL "Custom2",IDC_FLOCK_MAX_ORBIT_SPEED_SPIN,"SpinnerControl", + WS_TABSTOP,98,199,7,10 +END + +IDD_COMP_EFFVISSET DIALOG 0, 0, 111, 43 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Hide for Normal Render",IDC_COMP_EFFVISSET_HIDENORMAL, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,12,90,10 +END + +IDD_COMP_ENVMAP DIALOGEX 0, 0, 112, 324 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + LISTBOX IDC_LIST_TARGS,5,165,98,38,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_TARGS,6,205,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,56,205,47, + 14 + GROUPBOX "Effect Vis Sets",IDC_STATIC,2,132,105,186 + LTEXT "Yon",IDC_STATIC,8,14,47,8 + CONTROL "Custom1",IDC_COMP_ENVMAP_YON,"CustEdit",WS_TABSTOP,58, + 13,27,10 + CONTROL "Custom2",IDC_COMP_ENVMAP_YON_SPIN,"SpinnerControl", + WS_TABSTOP,86,13,7,10 + LTEXT "Refresh secs",IDC_STATIC,7,29,47,8 + CONTROL "Custom1",IDC_COMP_ENVMAP_REFRESHRATE,"CustEdit", + WS_TABSTOP,57,28,27,10 + CONTROL "Custom2",IDC_COMP_ENVMAP_REFRESHRATE_SPIN, + "SpinnerControl",WS_TABSTOP,85,28,7,10 + LTEXT "EnvMap Size",IDC_STATIC,7,45,47,8 + CONTROL "Custom1",IDC_COMP_ENVMAP_ENVSIZE,"CustEdit",WS_TABSTOP, + 57,44,27,10 + CONTROL "Custom2",IDC_COMP_ENVMAP_ENVSIZE_SPIN,"SpinnerControl", + WS_TABSTOP,85,44,7,10 + CONTROL "Include Characters",IDC_COMP_ENVMAP_INCCHARS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,149,75,10 + LTEXT "Fog Depth %",IDC_STATIC,7,61,47,8 + CONTROL "Custom1",IDC_COMP_ENVMAP_FOGSTART,"CustEdit",WS_TABSTOP, + 57,60,27,10 + CONTROL "Custom2",IDC_COMP_ENVMAP_FOGSTART_SPIN,"SpinnerControl", + WS_TABSTOP,85,60,7,10 + CONTROL "User1",IDC_COMP_ENVMAP_FOGCOLOR,"ColorSwatch",WS_GROUP, + 57,76,22,13 + LTEXT "Fog Color",IDC_STATIC,8,78,31,8 + CONTROL "Cubic Environment",IDC_COMP_ENVMAP_CUBIC,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,13,108,75,10 + CONTROL "Single Camera",IDC_COMP_ENVMAP_SINGLE_CAM,"Button", + BS_AUTORADIOBUTTON,13,118,61,10 + LTEXT "Map Type:",IDC_STATIC,8,95,35,8 + LISTBOX IDC_COMP_ENVMAP_NAMES_LISTBOX,5,267,97,34,WS_VSCROLL | + WS_TABSTOP + LTEXT "Effect Vis Set names from other max files:",IDC_STATIC, + 7,228,94,15 + CONTROL "Remove",IDC_COMP_ENVMAP_REMOVE_STRING,"CustButton", + WS_TABSTOP,33,301,42,13 + CONTROL "Custom2",IDC_COMP_ENVMAP_ADD_STRING_BOX,"CustEdit", + WS_TABSTOP,6,251,73,12 + CONTROL "Add",IDC_COMP_ENVMAP_ADD_STRING,"CustButton",WS_TABSTOP, + 82,251,21,12 +END + +IDD_COMP_W_TEXWATER DIALOG 0, 0, 112, 177 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Min WaveLen",IDC_STATIC,8,13,47,8 + CONTROL "Custom1",IDC_COMP_W_TEX_MINLEN,"CustEdit",WS_TABSTOP,58, + 12,27,10 + CONTROL "Custom2",IDC_COMP_W_TEX_MINLEN_SPIN,"SpinnerControl", + WS_TABSTOP,86,12,7,10 + LTEXT "Max WaveLen",IDC_STATIC,7,26,47,8 + CONTROL "Custom1",IDC_COMP_W_TEX_MAXLEN,"CustEdit",WS_TABSTOP,57, + 25,27,10 + CONTROL "Custom2",IDC_COMP_W_TEX_MAXLEN_SPIN,"SpinnerControl", + WS_TABSTOP,85,25,7,10 + LTEXT "Amp/Len %",IDC_STATIC,7,40,47,8 + CONTROL "Custom1",IDC_COMP_W_TEX_AMPOVERLEN,"CustEdit", + WS_TABSTOP,57,40,27,10 + CONTROL "Custom2",IDC_COMP_W_TEX_AMPOVERLEN_SPIN,"SpinnerControl", + WS_TABSTOP,85,40,7,10 + LTEXT "Choppiness %",IDC_STATIC,7,57,47,8 + CONTROL "Custom1",IDC_COMP_W_TEX_CHOP,"CustEdit",WS_TABSTOP,57, + 57,27,10 + CONTROL "Custom2",IDC_COMP_W_TEX_CHOP_SPIN,"SpinnerControl", + WS_TABSTOP,85,57,7,10 + LTEXT "Noise %",IDC_STATIC,7,76,47,8 + CONTROL "Custom1",IDC_COMP_W_TEX_NOISE,"CustEdit",WS_TABSTOP,57, + 76,27,10 + CONTROL "Custom2",IDC_COMP_W_TEX_NOISE_SPIN,"SpinnerControl", + WS_TABSTOP,85,76,7,10 + LTEXT "Ripple Scale",IDC_STATIC,7,96,47,8 + CONTROL "Custom1",IDC_COMP_W_TEX_RIPPLESCALE,"CustEdit", + WS_TABSTOP,57,96,27,10 + CONTROL "Custom2",IDC_COMP_W_TEX_RIPPLESCALE_SPIN,"SpinnerControl", + WS_TABSTOP,85,96,7,10 + LTEXT "Spread Deg.",IDC_STATIC,6,113,47,8 + CONTROL "Custom1",IDC_COMP_W_TEX_ANGLEDEV,"CustEdit",WS_TABSTOP, + 56,113,27,10 + CONTROL "Custom2",IDC_COMP_W_TEX_ANGLEDEV_SPIN,"SpinnerControl", + WS_TABSTOP,84,113,7,10 + GROUPBOX "Ripple Falloff",IDC_STATIC,7,129,81,42 + LTEXT "Start",IDC_STATIC,12,142,26,8 + CONTROL "Custom1",IDC_COMP_W_TEX_SPECSTART,"CustEdit",WS_TABSTOP, + 44,142,27,10 + CONTROL "Custom2",IDC_COMP_W_TEX_SPECSTART_SPIN,"SpinnerControl", + WS_TABSTOP,72,142,7,10 + LTEXT "End",IDC_STATIC,12,154,26,8 + CONTROL "Custom1",IDC_COMP_W_TEX_SPECEND,"CustEdit",WS_TABSTOP, + 44,154,27,10 + CONTROL "Custom2",IDC_COMP_W_TEX_SPECEND_SPIN,"SpinnerControl", + WS_TABSTOP,72,154,7,10 +END + +IDD_COMP_SOUNDPHYS DIALOG 0, 0, 108, 129 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Group",IDC_STATIC,2,4,104,27 + COMBOBOX IDC_SND_GROUP,6,13,95,176,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + GROUPBOX "Sounds",IDC_STATIC,2,35,104,86 + COMBOBOX IDC_SND_AGAINST,34,46,67,158,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Against:",IDC_STATIC,7,48,26,8 + LTEXT "Impact:",IDC_STATIC,8,65,24,8 + LTEXT "Slide:",IDC_STATIC,8,91,18,8 + PUSHBUTTON "",IDC_SND_SLIDE,8,100,67,12 + PUSHBUTTON "Clear",IDC_SND_CLEAR_SLIDE,77,100,25,12 +END + +IDD_COMP_SHOOTABLE DIALOG 0, 0, 111, 105 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Use this to tell shooting devices that this object is shootable.", + IDC_STATIC,9,19,92,30 + GROUPBOX "Shootable",IDC_STATIC,5,6,98,87 + LTEXT "This adds this object to the LOS DB for Shootables and makes it a physical object.", + IDC_STATIC,9,51,93,38 +END + +IDD_COMP_FP_BULLET DIALOG 0, 0, 114, 230 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Intensity %",IDC_STATIC,8,7,47,8 + CONTROL "Custom1",IDC_COMP_FP_INTENSITY,"CustEdit",WS_TABSTOP,58, + 7,27,10 + CONTROL "Custom2",IDC_COMP_FP_INTENSITY_SPIN,"SpinnerControl", + WS_TABSTOP,86,7,7,10 + LTEXT "Life Span (s)",IDC_STATIC,7,23,47,8 + CONTROL "Custom1",IDC_COMP_FP_LIFESPAN2,"CustEdit",WS_TABSTOP,57, + 22,27,10 + CONTROL "Custom2",IDC_COMP_FP_LIFESPAN_SPIN2,"SpinnerControl", + WS_TABSTOP,85,22,7,10 + GROUPBOX "Decal Layer",IDC_STATIC,5,64,99,58 + CONTROL "Alpha",IDC_RADIO_ALPHA,"Button",BS_AUTORADIOBUTTON,12, + 91,34,10 + CONTROL "Brighten",IDC_RADIO_MADD,"Button",BS_AUTORADIOBUTTON,12, + 102,42,10 + CONTROL "Add",IDC_RADIO_ADD,"Button",BS_AUTORADIOBUTTON,62,91,29, + 10 + CONTROL "Mult",IDC_RADIO_MULT,"Button",BS_AUTORADIOBUTTON,62,103, + 29,10 + CONTROL "",IDC_COMP_FP_TEXMAP,"CustButton",WS_TABSTOP,11,75,87, + 13 + GROUPBOX "Particles",IDC_STATIC,3,128,101,95 + LISTBOX IDC_LIST_TARGS2,5,136,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_PARTICLE,5,182,47,14 + CONTROL "Remove",IDC_DEL_TARGS2,"CustButton",WS_TABSTOP,56,182, + 47,14 + LTEXT "Scale %",IDC_STATIC,7,38,47,8 + CONTROL "Custom1",IDC_COMP_FP_SCALE,"CustEdit",WS_TABSTOP,57,38, + 27,10 + CONTROL "Custom2",IDC_COMP_FP_SCALE_SPIN,"SpinnerControl", + WS_TABSTOP,85,38,7,10 + LTEXT "Spew secs",IDC_STATIC,11,205,47,8 + CONTROL "Custom1",IDC_COMP_FP_PARTYTIME,"CustEdit",WS_TABSTOP,61, + 204,27,10 + CONTROL "Custom2",IDC_COMP_FP_PARTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,89,204,7,10 +END + +IDD_COMP_FP_TORPEDO DIALOG 0, 0, 111, 297 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Scale %",IDC_STATIC,8,20,47,8 + CONTROL "Custom1",IDC_COMP_FP_WIDTH,"CustEdit",WS_TABSTOP,57,19, + 27,10 + CONTROL "Custom2",IDC_COMP_FP_WIDTH_SPIN,"SpinnerControl", + WS_TABSTOP,85,19,7,10 + GROUPBOX "Decal Layer",IDC_STATIC,5,47,99,55 + CONTROL "Alpha",IDC_RADIO_ALPHA,"Button",BS_AUTORADIOBUTTON,12, + 74,34,10 + CONTROL "Brighten",IDC_RADIO_MADD,"Button",BS_AUTORADIOBUTTON,12, + 85,42,10 + CONTROL "Add",IDC_RADIO_ADD,"Button",BS_AUTORADIOBUTTON,62,74,29, + 10 + CONTROL "Mult",IDC_RADIO_MULT,"Button",BS_AUTORADIOBUTTON,62,86, + 29,10 + CONTROL "",IDC_COMP_FP_TEXMAP,"CustButton",WS_TABSTOP,11,58,87, + 13 + LTEXT "Wet secs",IDC_STATIC,9,177,47,8 + CONTROL "Custom1",IDC_COMP_FP_DIRTYTIME,"CustEdit",WS_TABSTOP,62, + 177,27,10 + CONTROL "Custom2",IDC_COMP_FP_DIRTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,90,177,7,10 + LISTBOX IDC_LIST_TARGS,4,114,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_NOTIFY,4,157,47,14 + CONTROL "Remove",IDC_DEL_TARGS,"CustButton",WS_TABSTOP,54,157,47, + 14 + GROUPBOX "Muddy/Wet Footprints",IDC_STATIC,3,104,101,88 + LTEXT "Intensity %",IDC_STATIC,8,7,47,8 + CONTROL "Custom1",IDC_COMP_FP_INTENSITY,"CustEdit",WS_TABSTOP,57, + 7,27,10 + CONTROL "Custom2",IDC_COMP_FP_INTENSITY_SPIN,"SpinnerControl", + WS_TABSTOP,85,7,7,10 + GROUPBOX "Particles",IDC_STATIC,3,193,101,93 + LISTBOX IDC_LIST_TARGS2,5,201,98,50,WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add...",IDC_ADD_PARTICLE,5,247,47,14 + CONTROL "Remove",IDC_DEL_TARGS2,"CustButton",WS_TABSTOP,56,247, + 47,14 + LTEXT "Spew secs",IDC_STATIC,11,269,47,8 + CONTROL "Custom1",IDC_COMP_FP_PARTYTIME,"CustEdit",WS_TABSTOP,61, + 268,27,10 + CONTROL "Custom2",IDC_COMP_FP_PARTYTIME_SPIN,"SpinnerControl", + WS_TABSTOP,89,268,7,10 + LTEXT "Life Span (s)",IDC_STATIC,7,34,47,8 + CONTROL "Custom1",IDC_COMP_FP_LIFESPAN2,"CustEdit",WS_TABSTOP,57, + 33,27,10 + CONTROL "Custom2",IDC_COMP_FP_LIFESPAN_SPIN2,"SpinnerControl", + WS_TABSTOP,85,33,7,10 +END + +IDD_COMP_PANIC DIALOG 0, 0, 109, 36 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "",-1,5,4,95,25 + CONTROL "Play Link Out Anim",IDC_COMP_PANIC_ANIM,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,15,14,75,10 +END + +IDD_COMP_RESPOND_ENABLE_PHYS DIALOG 0, 0, 108, 34 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Enable",IDC_ENABLE_CHECK,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,9,20,38,10 + CONTROL "Custom1",IDC_NODE_BUTTON,"CustButton",WS_TABSTOP,9,5,88, + 12 +END + +IDD_COMP_RESPOND_CAM_FORCE DIALOG 0, 0, 108, 35 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Force 3rd Person",IDC_RADIO_THIRD,"Button", + BS_AUTORADIOBUTTON,15,6,70,10 + CONTROL "Resume 1st Person",IDC_RADIO_FIRST,"Button", + BS_AUTORADIOBUTTON,15,18,77,10 +END + +IDD_COMP_FORCE_CTT DIALOG 0, 0, 86, 41 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Force Objects to be Clickable for Click To Turn", + IDC_STATIC,11,7,65,32 +END + +IDD_COMP_MARKER DIALOG 0, 0, 108, 198 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Physical (Sphere):",IDC_STATIC,3,3,58,8 + CONTROL "",IDC_MARKER_PHYS,"CustButton",WS_TABSTOP,3,13,101,12 + GROUPBOX "Material Animations",IDC_STATIC,3,30,101,90 + LTEXT "Mtl:",IDC_STATIC,17,44,12,8 + PUSHBUTTON "Button1",IDC_MTL_BUTTON,31,42,69,12 + LTEXT "Node:",IDC_STATIC,9,58,20,8 + PUSHBUTTON "Button1",IDC_MTL_NODE_BUTTON,31,56,69,12 + LTEXT "Red:",IDC_STATIC,13,75,16,8 + COMBOBOX IDC_ANIM_RED_COMBO,31,73,55,135,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Green:",IDC_STATIC,7,89,22,8 + COMBOBOX IDC_ANIM_GREEN_COMBO,31,87,55,135,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Open:",IDC_STATIC,9,104,20,8 + COMBOBOX IDC_ANIM_OPEN_COMBO,31,102,55,135,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Bounce Animation:",IDC_STATIC,3,125,60,8 + PUSHBUTTON "Button1",IDC_BOUNCE_BUTTON,3,134,101,12 + LTEXT "Place Sound:",IDC_STATIC,3,149,44,8 + PUSHBUTTON "Button1",IDC_PLACE_BUTTON,3,158,101,12 + LTEXT "Hit Sound:",IDC_STATIC,3,173,34,8 + PUSHBUTTON "Button1",IDC_HIT_BUTTON,3,182,101,12 +END + +IDD_COMP_LOSFADE DIALOG 0, 0, 111, 83 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Check Bounds Center",IDC_COMP_BOUNDSCENTER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,16,85,10 + GROUPBOX "Def: Check Pivot Point",IDC_STATIC,5,6,97,26 + LTEXT "In",IDC_STATIC,18,45,33,8 + CONTROL "Custom1",IDC_COMP_FADEINTIME,"CustEdit",WS_TABSTOP,58, + 44,27,10 + CONTROL "Custom2",IDC_COMP_FADEINTIME_SPIN,"SpinnerControl", + WS_TABSTOP,86,44,7,10 + GROUPBOX "Fade Times (secs)",IDC_STATIC,5,36,95,36 + LTEXT "Out",IDC_STATIC,18,58,33,8 + CONTROL "Custom1",IDC_COMP_FADEOUTTIME,"CustEdit",WS_TABSTOP,58, + 57,27,10 + CONTROL "Custom2",IDC_COMP_FADEOUTTIME_SPIN,"SpinnerControl", + WS_TABSTOP,86,57,7,10 +END + +IDD_COMP_GZFADE DIALOG 0, 0, 110, 82 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Opaqe up to:",IDC_STATIC,8,10,47,8 + CONTROL "Custom1",IDC_COMP_GZ_OPAQUE,"CustEdit",WS_TABSTOP,58,9, + 27,10 + CONTROL "Custom2",IDC_COMP_GZ_OPAQUE_SPIN,"SpinnerControl", + WS_TABSTOP,86,9,7,10 + LTEXT "Transp past:",IDC_STATIC,7,25,47,8 + CONTROL "Custom1",IDC_COMP_GZ_TRANSP,"CustEdit",WS_TABSTOP,57,25, + 27,10 + CONTROL "Custom2",IDC_COMP_GZ_TRANSP_SPIN,"SpinnerControl", + WS_TABSTOP,85,25,7,10 + LTEXT "Marker is opaque up to first dist",IDC_STATIC,4,41,99,8 + LTEXT "and fades out completely",IDC_STATIC,13,54,80,8 + LTEXT "at second distance.",IDC_STATIC,19,66,63,8 +END + +IDD_COMP_DYNMAT DIALOG 0, 0, 112, 37 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Force material colors treated as dynamic",IDC_STATIC,13, + 9,88,20 +END + +IDD_COMP_SORT_AS_OPAQUE DIALOG 0, 0, 110, 38 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Caution: Use only if you are SURE about this.", + IDC_STATIC,5,11,96,23 +END + +IDD_COMP_RESPOND_SUBWORLD DIALOG 0, 0, 108, 18 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Pick Me!",IDC_NODE_BUTTON,"CustButton",WS_TABSTOP,5,3, + 97,10 + CTEXT "Exit the current subworld",IDC_SUBWORLD_EXIT,4,2,99,14, + SS_CENTERIMAGE +END + +IDD_PICK_LOCALIZATION DIALOGEX 0, 0, 189, 212 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | + WS_CAPTION | WS_SYSMENU +CAPTION "Pick Localization" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,77,191,50,14 + PUSHBUTTON "Cancel",IDCANCEL,132,191,50,14 + CONTROL "",IDC_LOCALIZATIONTREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS | + WS_BORDER | WS_TABSTOP,7,23,175,166,WS_EX_CLIENTEDGE + EDITTEXT IDC_LOCALIZATIONSTRING,7,7,175,14,ES_AUTOHSCROLL | + ES_READONLY | NOT WS_TABSTOP +END + +IDD_COMP_OBJ_FLOCKER DIALOGEX 0, 0, 115, 235 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "Goal Strength:",IDC_STATIC,3,54,62,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_OBJ_FLOCKER_GOAL_STRENGTH,"CustEdit", + WS_TABSTOP,67,51,30,10 + CONTROL "Custom2",IDC_OBJ_FLOCKER_GOAL_STRENGTH_SPIN, + "SpinnerControl",WS_TABSTOP,99,51,7,10 + LTEXT "Wander Strength:",IDC_STATIC,5,67,60,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_OBJ_FLOCKER_WANDER_STRENGTH,"CustEdit", + WS_TABSTOP,67,65,30,10 + CONTROL "Custom2",IDC_OBJ_FLOCKER_WANDER_STRENGTH_SPIN, + "SpinnerControl",WS_TABSTOP,99,65,7,10 + LTEXT "Separation Str:",IDC_STATIC,0,87,58,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_OBJ_FLOCKER_SEP_STRENGTH,"CustEdit", + WS_TABSTOP,67,86,30,10 + CONTROL "Custom2",IDC_OBJ_FLOCKER_SEP_STRENGTH_SPIN, + "SpinnerControl",WS_TABSTOP,99,86,7,10 + LTEXT "Perception Radius:",IDC_STATIC,5,100,60,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_OBJ_FLOCKER_SEP_RADIUS,"CustEdit", + WS_TABSTOP,67,99,30,10 + CONTROL "Custom2",IDC_OBJ_FLOCKER_SEP_RADIUS_SPIN,"SpinnerControl", + WS_TABSTOP,99,99,7,10 + LTEXT "Cohesion Str:",IDC_STATIC,21,123,43,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_OBJ_FLOCKER_COH_STRENGTH,"CustEdit", + WS_TABSTOP,67,121,30,10 + CONTROL "Custom2",IDC_OBJ_FLOCKER_COH_STRENGTH_SPIN, + "SpinnerControl",WS_TABSTOP,99,121,7,10 + LTEXT "Speed Limit Min:",IDC_STATIC,6,181,58,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_OBJ_FLOCKER_SLIMIT_MIN,"CustEdit", + WS_TABSTOP,67,179,30,10 + CONTROL "Custom2",IDC_OBJ_FLOCKER_SLIMIT_MIN_SPIN,"SpinnerControl", + WS_TABSTOP,99,179,7,10 + LTEXT "Speed Limit Max:",IDC_STATIC,9,169,55,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_OBJ_FLOCKER_SLIMIT_MAX,"CustEdit", + WS_TABSTOP,67,166,30,10 + CONTROL "Custom2",IDC_OBJ_FLOCKER_SLIMIT_MAX_SPIN,"SpinnerControl", + WS_TABSTOP,99,166,7,10 + LTEXT "Turn Tightness:",IDC_STATIC,6,156,58,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_OBJ_FLOCKER_MAX_FORCE,"CustEdit", + WS_TABSTOP,67,153,30,10 + CONTROL "Custom2",IDC_OBJ_FLOCKER_MAX_FORCE_SPIN,"SpinnerControl", + WS_TABSTOP,99,153,7,10 + LTEXT "Perception Radius:",IDC_STATIC,4,135,60,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_OBJ_FLOCKER_COH_RADIUS,"CustEdit", + WS_TABSTOP,67,134,30,10 + CONTROL "Custom2",IDC_OBJ_FLOCKER_COH_RADIUS_SPIN,"SpinnerControl", + WS_TABSTOP,99,134,7,10 + LTEXT "Boid:",IDC_OBJ_TEXT,6,4,17,8 + LTEXT "Number of Boids:",IDC_STATIC,0,33,58,8,0,WS_EX_RIGHT + CONTROL "Custom1",IDC_OBJ_FLOCKER_NUM_BOIDS,"CustEdit", + WS_TABSTOP,67,32,30,10 + CONTROL "Custom2",IDC_OBJ_FLOCKER_NUM_BOIDS_SPIN,"SpinnerControl", + WS_TABSTOP,99,32,7,10 + CONTROL "Use Target Rotation:", + IDC_OBJ_FLOCKER_USE_TARGET_ROTATION,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,4,199,99,8 + CONTROL "Random Anim Start:",IDC_OBJ_FLOCKER_RANDOM_ANIM_START, + "Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,8, + 210,95,8 + CONTROL "Custom1",IDC_OBJ_FLOCKER_BOID_BUTTON,"CustButton", + WS_TABSTOP,5,14,101,14 + CONTROL "Hide Target:",IDC_OBJ_FLOCKER_HIDE_TARGET,"Button", + BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,31,221,72,8 +END + +IDD_COMP_GRASS DIALOGEX 0, 0, 108, 124 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + LTEXT "Displacement",IDC_STATIC,3,22,44,8 + CONTROL "Custom1",IDC_GRASS_DIST_X,"CustEdit",WS_TABSTOP,59,22, + 34,10 + CONTROL "Custom2",IDC_GRASS_DIST_X_SPIN,"SpinnerControl", + WS_TABSTOP,95,22,7,10 + LTEXT "Wave:",IDC_STATIC,3,8,22,8 + COMBOBOX IDC_GRASS_WAVE,28,6,31,113,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP + LTEXT "X:",IDC_STATIC,51,23,8,8 + CONTROL "Custom1",IDC_GRASS_DIST_Y,"CustEdit",WS_TABSTOP,59,34, + 34,10 + CONTROL "Custom2",IDC_GRASS_DIST_Y_SPIN,"SpinnerControl", + WS_TABSTOP,95,34,7,10 + LTEXT "Y:",IDC_STATIC,51,34,8,8 + CONTROL "Custom1",IDC_GRASS_DIST_Z,"CustEdit",WS_TABSTOP,59,46, + 34,10 + CONTROL "Custom2",IDC_GRASS_DIST_Z_SPIN,"SpinnerControl", + WS_TABSTOP,95,46,7,10 + LTEXT "Z:",IDC_STATIC,51,47,8,8 + CONTROL "Custom1",IDC_GRASS_DIR_X,"CustEdit",WS_TABSTOP,59,73,34, + 10 + CONTROL "Custom2",IDC_GRASS_DIR_X_SPIN,"SpinnerControl", + WS_TABSTOP,95,73,7,10 + LTEXT "X:",IDC_STATIC,51,74,8,8 + CONTROL "Custom1",IDC_GRASS_DIR_Y,"CustEdit",WS_TABSTOP,59,85,34, + 10 + CONTROL "Custom2",IDC_GRASS_DIR_Y_SPIN,"SpinnerControl", + WS_TABSTOP,95,85,7,10 + LTEXT "Y:",IDC_STATIC,51,86,8,8 + CONTROL "Custom1",IDC_GRASS_SPEED,"CustEdit",WS_TABSTOP,59,109, + 34,10 + CONTROL "Custom2",IDC_GRASS_SPEED_SPIN,"SpinnerControl", + WS_TABSTOP,95,109,7,10 + LTEXT "Speed:",IDC_STATIC,3,109,24,8 + LTEXT "Direction",IDC_STATIC,3,73,29,8 +END + +IDD_COMP_ANIM_COMPRESS DIALOGEX 0, 0, 108, 125 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + CONTROL "None",IDC_COMP_ANIM_COMPRESS_NONE,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,52,23,33,10 + CONTROL "Low",IDC_COMP_ANIM_COMPRESS_LOW,"Button", + BS_AUTORADIOBUTTON,52,33,29,10 + CONTROL "High",IDC_COMP_ANIM_COMPRESS_HIGH,"Button", + BS_AUTORADIOBUTTON,52,44,31,10 + LTEXT "Compression Level: (rotation only)",IDC_STATIC,5,5,64, + 17 + LTEXT "Keyframe Reduction",IDC_STATIC,5,72,80,9 + LTEXT "Threshold:",IDC_STATIC,17,84,33,8 + CONTROL "Custom1",IDC_COMP_ANIM_COMPRESS_THRESHOLD,"CustEdit", + WS_TABSTOP,52,83,24,10 + CONTROL "Custom2",IDC_COMP_ANIM_COMPRESS_THRESHOLD_SPIN, + "SpinnerControl",WS_TABSTOP,77,83,7,10 + LTEXT "(Warning: Exporting with keyframe reduction changes the actual Max file data.)", + IDC_STATIC,5,95,94,25 +END + +IDD_COMP_RIDE_ANIMATED_PHYS DIALOGEX 0, 0, 131, 79 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Bounding Volume",IDC_STATIC,7,7,115,64 + CONTROL "Sphere",IDC_RADIO_BSPHERE,"Button",BS_AUTORADIOBUTTON, + 14,18,39,8 + CONTROL "Box",IDC_RADIO_BBOX,"Button",BS_AUTORADIOBUTTON,14,29, + 39,8 + CONTROL "Hull",IDC_RADIO_BHULL,"Button",BS_AUTORADIOBUTTON,14,40, + 39,8 + CONTROL "Exact",IDC_RADIO_PICKSTATE,"Button",BS_AUTORADIOBUTTON, + 14,51,39,8 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_COMP_ANIM, DIALOG + BEGIN + BOTTOMMARGIN, 155 + END + + IDD_COMP_PHYSICAL, DIALOG + BEGIN + BOTTOMMARGIN, 25 + END + + IDD_COMP_PHYS_TERRAIN, DIALOG + BEGIN + BOTTOMMARGIN, 3 + END + + IDD_COMP_PHYS_SIMPLE, DIALOG + BEGIN + BOTTOMMARGIN, 210 + END + + IDD_COMP_PHYS_DETECTOR, DIALOG + BEGIN + BOTTOMMARGIN, 11 + END + + IDD_COMP_AVATAR, DIALOG + BEGIN + BOTTOMMARGIN, 210 + END + + IDD_COMP_PHYS_PROXY_TERRAIN, DIALOG + BEGIN + BOTTOMMARGIN, 129 + END + + IDD_COMP_PAGEINFO, DIALOG + BEGIN + BOTTOMMARGIN, 85 + END + + IDD_COMP_RESPOND_MTL, DIALOG + BEGIN + BOTTOMMARGIN, 64 + END + + IDD_COMP_DETECTOR_CLICKABLE, DIALOG + BEGIN + RIGHTMARGIN, 107 + BOTTOMMARGIN, 259 + END + + IDD_COMP_DETECTOR_CLICK_DRAGGABLE, DIALOG + BEGIN + RIGHTMARGIN, 107 + BOTTOMMARGIN, 310 + END + + IDD_COMP_ONESHOT, DIALOG + BEGIN + BOTTOMMARGIN, 114 + END + + IDD_COMP_SOUNDBGND, DIALOG + BEGIN + BOTTOMMARGIN, 40 + END + + IDD_COMP_SOFTVOLUME, DIALOG + BEGIN + BOTTOMMARGIN, 77 + END + + IDD_COMP_DETECTOR_REGION, DIALOG + BEGIN + BOTTOMMARGIN, 334 + END + + IDD_COMP_DETECTOR_COLLISION, DIALOG + BEGIN + RIGHTMARGIN, 105 + BOTTOMMARGIN, 8 + END + + IDD_COMP_RESPOND, DIALOG + BEGIN + BOTTOMMARGIN, 226 + END + + IDD_COMP_SOFTVOLUME_NEGATE, DIALOG + BEGIN + RIGHTMARGIN, 107 + TOPMARGIN, 1 + BOTTOMMARGIN, 93 + END + + IDD_COMP_PHYS_HINGE_CONSTRAINT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 241 + END + + IDD_COMP_PHYS_CRITTER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 207 + END + + IDD_COMP_RESPOND_ANIMPICK, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 160 + END + + IDD_COMP_LOD_AVATAR, DIALOG + BEGIN + RIGHTMARGIN, 104 + BOTTOMMARGIN, 350 + END + + IDD_COMP_CAMERARGN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 34 + END + + IDD_COMP_FIXEDCAM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 76 + TOPMARGIN, 7 + BOTTOMMARGIN, 15 + END + + IDD_COMP_AUTOCAM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 103 + TOPMARGIN, 7 + BOTTOMMARGIN, 419 + END + + IDD_COMP_CAMERA_TRANS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 105 + TOPMARGIN, 7 + BOTTOMMARGIN, 329 + END + + IDD_COMP_PHYS_WALKABLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 103 + END + + IDD_COMP_PHYS_CLIMBABLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 103 + END + + IDD_COMP_PHYS_SWIMSURF, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 105 + TOPMARGIN, 7 + BOTTOMMARGIN, 349 + END + + IDD_COMP_PHYS_SWIM3D, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 101 + END + + IDD_COMP_GUITAG, DIALOG + BEGIN + BOTTOMMARGIN, 39 + END + + IDD_COMP_CAMERA_OCCLUDER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 54 + END + + IDD_COMP_PHYS_INVISIBLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 166 + END + + IDD_COMP_AVATAR_POA, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 104 + TOPMARGIN, 7 + END + + IDD_COMP_OBJECT_POA, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 102 + TOPMARGIN, 7 + BOTTOMMARGIN, 93 + END + + IDD_COMP_CAM_MAKEDEFAULT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 36 + END + + IDD_COMP_LOS_WIDGET, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 65 + END + + IDD_COMP_GUIBUTTON, DIALOG + BEGIN + BOTTOMMARGIN, 293 + END + + IDD_COMP_DISTRIBUTOR, DIALOG + BEGIN + BOTTOMMARGIN, 659 + END + + IDD_COMP_CLUSTER, DIALOG + BEGIN + BOTTOMMARGIN, 118 + END + + IDD_COMP_GUILISTBOX, DIALOG + BEGIN + BOTTOMMARGIN, 203 + END + + IDD_COMP_GUITEXTBOX, DIALOG + BEGIN + VERTGUIDE, 6 + VERTGUIDE, 100 + TOPMARGIN, 6 + BOTTOMMARGIN, 186 + END + + IDD_COMP_ANIM_EASE, DIALOG + BEGIN + BOTTOMMARGIN, 162 + END + + IDD_COMP_IGNORELITE, DIALOG + BEGIN + RIGHTMARGIN, 106 + BOTTOMMARGIN, 85 + END + + IDD_COMP_GUIEDITBOX, DIALOG + BEGIN + BOTTOMMARGIN, 22 + END + + IDD_COMP_FLEXIBILITY, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 106 + TOPMARGIN, 3 + BOTTOMMARGIN, 87 + END + + IDD_COMP_GUIDIALOG, DIALOG + BEGIN + BOTTOMMARGIN, 51 + END + + IDD_COMP_CAMERAPAN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 105 + TOPMARGIN, 7 + BOTTOMMARGIN, 89 + END + + IDD_COMP_BLOW, DIALOG + BEGIN + TOPMARGIN, 1 + BOTTOMMARGIN, 69 + END + + IDD_PYTHON_FILE_WARN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 226 + TOPMARGIN, 7 + BOTTOMMARGIN, 195 + END + + IDD_COMP_XFORM, DIALOG + BEGIN + BOTTOMMARGIN, 61 + END + + IDD_COMP_FIRSTPERSON_CAM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 103 + TOPMARGIN, 7 + BOTTOMMARGIN, 184 + END + + IDD_COMP_BEHAVIOR_SITTING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + VERTGUIDE, 10 + TOPMARGIN, 7 + BOTTOMMARGIN, 85 + END + + IDD_COMP_PHYS_CORE_GROUP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 63 + END + + IDD_COMP_NAV_LADDER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 101 + END + + IDD_COMP_TEMPLATE, DIALOG + BEGIN + TOPMARGIN, 1 + BOTTOMMARGIN, 44 + END + + IDD_COMP_DIST_SPACING, DIALOG + BEGIN + RIGHTMARGIN, 108 + BOTTOMMARGIN, 80 + END + + IDD_COMP_DIST_ANGPROB, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 108 + BOTTOMMARGIN, 58 + END + + IDD_COMP_DIST_ALTPROB, DIALOG + BEGIN + RIGHTMARGIN, 108 + BOTTOMMARGIN, 58 + END + + IDD_COMP_DIST_CONFORM, DIALOG + BEGIN + RIGHTMARGIN, 107 + TOPMARGIN, 1 + BOTTOMMARGIN, 79 + END + + IDD_COMP_DIST_BITMAP, DIALOG + BEGIN + RIGHTMARGIN, 108 + TOPMARGIN, 2 + BOTTOMMARGIN, 116 + END + + IDD_COMP_DIST_FADE, DIALOG + BEGIN + RIGHTMARGIN, 104 + BOTTOMMARGIN, 67 + END + + IDD_COMP_DIST_WIND, DIALOG + BEGIN + TOPMARGIN, 2 + BOTTOMMARGIN, 33 + END + + IDD_COMP_DIST_TEMPLATES, DIALOG + BEGIN + RIGHTMARGIN, 110 + TOPMARGIN, 1 + END + + IDD_COMP_DIST_SCALE, DIALOG + BEGIN + TOPMARGIN, 1 + BOTTOMMARGIN, 71 + END + + IDD_COMP_DIST_ACTION, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 104 + BOTTOMMARGIN, 47 + END + + IDD_COMP_LODFADE, DIALOG + BEGIN + RIGHTMARGIN, 104 + END + + IDD_PICK_MTL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 115 + TOPMARGIN, 7 + BOTTOMMARGIN, 163 + END + + IDD_COMP_CLOTHING, DIALOG + BEGIN + BOTTOMMARGIN, 205 + END + + IDD_COMP_CAMERA_RAIL, DIALOG + BEGIN + BOTTOMMARGIN, 133 + END + + IDD_COMP_MULTIBEH_NORMAL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 272 + END + + IDD_COMP_MULTIBEH, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 126 + END + + IDD_COMP_BLENDONTO, DIALOG + BEGIN + RIGHTMARGIN, 110 + BOTTOMMARGIN, 93 + END + + IDD_COMP_GUISCHEME, DIALOG + BEGIN + BOTTOMMARGIN, 182 + END + + IDD_COMP_CAMERA_CIRCLE, DIALOG + BEGIN + BOTTOMMARGIN, 160 + END + + IDD_COMP_CLIMB_BLOCK, DIALOG + BEGIN + RIGHTMARGIN, 109 + VERTGUIDE, 9 + BOTTOMMARGIN, 93 + END + + IDD_COMP_GEO_DICE, DIALOG + BEGIN + BOTTOMMARGIN, 88 + END + + IDD_COMP_FORCE_RTLIGHT, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 108 + BOTTOMMARGIN, 38 + END + + IDD_COMP_CAMERAZOOM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 105 + TOPMARGIN, 7 + BOTTOMMARGIN, 89 + END + + IDD_COMP_DETECTOR_MTL, DIALOG + BEGIN + BOTTOMMARGIN, 125 + END + + IDD_PICK_NODE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 158 + TOPMARGIN, 7 + BOTTOMMARGIN, 171 + END + + IDD_COMP_BLENDONTOADV, DIALOG + BEGIN + RIGHTMARGIN, 110 + END + + IDD_COMP_VEHICLE, DIALOG + BEGIN + BOTTOMMARGIN, 232 + END + + IDD_COMP_PARTICLE_UNIWIND, DIALOG + BEGIN + RIGHTMARGIN, 110 + BOTTOMMARGIN, 163 + END + + IDD_COMP_DISTFADE, DIALOG + BEGIN + RIGHTMARGIN, 110 + TOPMARGIN, 1 + BOTTOMMARGIN, 94 + END + + IDD_COMP_SOUNDGUI, DIALOG + BEGIN + BOTTOMMARGIN, 128 + END + + IDD_COMP_SHADOW_CAST, DIALOG + BEGIN + RIGHTMARGIN, 109 + END + + IDD_COMP_SHADOW_RCV, DIALOG + BEGIN + RIGHTMARGIN, 111 + BOTTOMMARGIN, 94 + END + + IDD_COMP_SHADOW_LIGHT, DIALOG + BEGIN + BOTTOMMARGIN, 131 + END + + IDD_COMP_FOOTSTEP_SOUND, DIALOG + BEGIN + BOTTOMMARGIN, 41 + END + + IDD_COMP_FP_FOOTPRINT, DIALOG + BEGIN + RIGHTMARGIN, 110 + BOTTOMMARGIN, 322 + END + + IDD_COMP_FP_RIPPLE, DIALOG + BEGIN + LEFTMARGIN, 1 + TOPMARGIN, 1 + BOTTOMMARGIN, 303 + END + + IDD_COMP_FP_DIRTY, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 110 + TOPMARGIN, 1 + BOTTOMMARGIN, 111 + END + + IDD_COMP_FP_PRINTSHAPE, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 112 + TOPMARGIN, 1 + END + + IDD_COMP_FP_PUDDLE, DIALOG + BEGIN + RIGHTMARGIN, 109 + TOPMARGIN, 1 + BOTTOMMARGIN, 290 + END + + IDD_COMP_SUBWORLD_REGION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 62 + END + + IDD_COMP_NPC_SPAWN, DIALOG + BEGIN + BOTTOMMARGIN, 93 + END + + IDD_COMP_FP_ACTIVEPRINTSHAPE, DIALOG + BEGIN + LEFTMARGIN, 2 + RIGHTMARGIN, 110 + TOPMARGIN, 1 + BOTTOMMARGIN, 135 + END + + IDD_COMP_FP_WAKE, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 108 + TOPMARGIN, 1 + BOTTOMMARGIN, 329 + END + + IDD_COMP_FILTERINHERIT, DIALOG + BEGIN + RIGHTMARGIN, 109 + TOPMARGIN, 1 + BOTTOMMARGIN, 78 + END + + IDD_COMP_CLIMB_TRIGGER, DIALOG + BEGIN + VERTGUIDE, 7 + BOTTOMMARGIN, 84 + END + + IDD_COMP_UNLEASH, DIALOG + BEGIN + RIGHTMARGIN, 109 + END + + IDD_COMP_SWIVEL, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 105 + TOPMARGIN, 1 + BOTTOMMARGIN, 262 + END + + IDD_COMP_OBJECT_FOLLOWCAM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 103 + TOPMARGIN, 7 + BOTTOMMARGIN, 390 + END + + IDD_COMP_EMOTE, DIALOG + BEGIN + BOTTOMMARGIN, 54 + END + + IDD_COMP_W_GEOWATER, DIALOG + BEGIN + RIGHTMARGIN, 109 + BOTTOMMARGIN, 130 + END + + IDD_COMP_W_ADVSHORE, DIALOG + BEGIN + RIGHTMARGIN, 110 + END + + IDD_COMP_W_REFOBJECT, DIALOG + BEGIN + RIGHTMARGIN, 110 + END + + IDD_COMP_SHORE, DIALOG + BEGIN + BOTTOMMARGIN, 65 + END + + IDD_COMP_W_ENVMAP, DIALOG + BEGIN + RIGHTMARGIN, 110 + END + + IDD_COMP_NOSHOW, DIALOG + BEGIN + RIGHTMARGIN, 110 + BOTTOMMARGIN, 225 + END + + IDD_COMP_VISREGION, DIALOG + BEGIN + BOTTOMMARGIN, 131 + END + + IDD_COMP_SKINEDIT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 399 + TOPMARGIN, 7 + BOTTOMMARGIN, 266 + END + + IDD_COMP_WDECAL, DIALOG + BEGIN + BOTTOMMARGIN, 220 + END + + IDD_COMP_ANIMCAM_COMMANDS, DIALOG + BEGIN + BOTTOMMARGIN, 140 + END + + IDD_COMP_NETSYNC, DIALOG + BEGIN + BOTTOMMARGIN, 67 + END + + IDD_COMP_RELREGION, DIALOG + BEGIN + BOTTOMMARGIN, 39 + END + + IDD_COMP_REPGROUP, DIALOG + BEGIN + RIGHTMARGIN, 110 + END + + IDD_COMP_PHYS_BRIDGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 168 + END + + IDD_COMP_SMOOTHBASE, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 108 + BOTTOMMARGIN, 61 + END + + IDD_COMP_IMAGELIB, DIALOG + BEGIN + BOTTOMMARGIN, 120 + END + + IDD_COMP_PARTICLE_FLOCK, DIALOG + BEGIN + BOTTOMMARGIN, 215 + END + + IDD_COMP_EFFVISSET, DIALOG + BEGIN + RIGHTMARGIN, 109 + END + + IDD_COMP_W_TEXWATER, DIALOG + BEGIN + RIGHTMARGIN, 110 + BOTTOMMARGIN, 175 + END + + IDD_COMP_SHOOTABLE, DIALOG + BEGIN + RIGHTMARGIN, 109 + VERTGUIDE, 9 + BOTTOMMARGIN, 93 + END + + IDD_COMP_FP_BULLET, DIALOG + BEGIN + RIGHTMARGIN, 113 + END + + IDD_COMP_PANIC, DIALOG + BEGIN + RIGHTMARGIN, 107 + END + + IDD_COMP_FORCE_CTT, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 84 + BOTTOMMARGIN, 39 + END + + IDD_COMP_MARKER, DIALOG + BEGIN + BOTTOMMARGIN, 108 + END + + IDD_COMP_LOSFADE, DIALOG + BEGIN + RIGHTMARGIN, 108 + BOTTOMMARGIN, 81 + END + + IDD_COMP_DYNMAT, DIALOG + BEGIN + RIGHTMARGIN, 111 + BOTTOMMARGIN, 35 + END + + IDD_COMP_SORT_AS_OPAQUE, DIALOG + BEGIN + RIGHTMARGIN, 108 + END + + IDD_COMP_RESPOND_SUBWORLD, DIALOG + BEGIN + BOTTOMMARGIN, 16 + END + + IDD_PICK_LOCALIZATION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 182 + TOPMARGIN, 7 + BOTTOMMARGIN, 205 + END + + IDD_COMP_OBJ_FLOCKER, DIALOG + BEGIN + RIGHTMARGIN, 110 + END + + IDD_COMP_ANIM_COMPRESS, DIALOG + BEGIN + BOTTOMMARGIN, 121 + END + + IDD_COMP_RIDE_ANIMATED_PHYS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 122 + TOPMARGIN, 7 + BOTTOMMARGIN, 71 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ZOOMIN ICON "../MaxComponent/icon1.ico" +IDI_ZOOMOUT ICON "../MaxComponent/zoomin1.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_COMP_GUIBUTTON DLGINIT +BEGIN + IDC_COMBO_BUTTON_NOTIFYTYPE, 0x403, 10, 0 +0x7542, 0x7474, 0x6e6f, 0x5520, 0x0070, + IDC_COMBO_BUTTON_NOTIFYTYPE, 0x403, 12, 0 +0x7542, 0x7474, 0x6e6f, 0x4420, 0x776f, 0x006e, + IDC_COMBO_BUTTON_NOTIFYTYPE, 0x403, 19, 0 +0x7542, 0x7474, 0x6e6f, 0x4420, 0x776f, 0x206e, 0x6e61, 0x2064, 0x7055, +"\000" + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_COMP_ANIM "Animation" +END + +STRINGTABLE +BEGIN + IDS_PLASMA_ATTRIB "Plasma Attrib" + IDS_CHECK "x" + IDS_COMP_ACTIVE "Activator" + IDS_COMP_RESPOND "Responder" + IDS_COMP_ALIASS "Player" + IDS_COMP_STDPORTALS "Portal" + IDS_COMP_TARGS "Targets" + IDS_COMP_INTERESTS "Player Attention" + IDS_COMP_FOOTPRINT "Foot Print" + IDS_COMP_AUTO "Auto" + IDS_COMP_VIEWFACES "BillBoard" + IDS_COMP_SOUND3DS "Sound 3D" + IDS_COMP_BARNEYS "Barney" + IDS_COMP_SOUNDGUI "GUI Sound" + IDS_COMP_CAMERAS "Camera" + IDS_COMP_BLENDONTO "Blend onto base node(s)" +END + +STRINGTABLE +BEGIN + IDS_COMP_PHYS_CHOSEN_BASE "" + IDS_COMP_PHYS_CHOSEN_SIMP "" + IDS_COMP_PHYS_CHOSEN_TERRAIN "" + IDS_COMP_PHYS_CHOSEN_PLAYER "" + IDS_COMP_ROOMS "Location" + IDS_COMP_CAMVIEWS "Setup view from camera for cockpit or HUD effects." + IDS_COMP_LODFADE_BASE "Base/Imposter" + IDS_COMP_ANIM_AVATARS "Compound Animation" + IDS_COMP_SOUND_REVERBS "Audio Region" + IDS_COMP_AVATARS "Avatar" + IDS_COMP_AVATAR_MODELS "Avatar (Model)" + IDS_COMP_AVATAR_PROXYS "Avatar (Proxy)" + IDS_COMP_LIGHTMAPS "Auto Light Map" + IDS_COMP_IGNORES "Ignore" + IDS_COMP_FORCE_RTLIGHT "Force RT Lighting" + IDS_COMP_SOUNDMOD_CHORUSS "Chorus Effect" +END + +STRINGTABLE +BEGIN + IDS_COMP_SOUNDMOD_COMPRESSORS "Compressor Effect" + IDS_COMP_SOUNDMOD_DISTORTIONS "Distortion Effect" + IDS_COMP_SOUNDMOD_ECHOS "Echo Effect" + IDS_COMP_SOUNDMOD_FLANGERS "Flanger Effect" + IDS_COMP_SOUNDMOD_GARGLES "Gargle Effect" + IDS_COMP_SOUNDMOD_REVERBS "Reverb Effect" + IDS_COMP_SOUNDMOD_WETS "Wet Dry Percentage" + IDS_COMP_SOUNDMOD_DELAYMODS "Delay Modulation Percentage" + IDS_COMP_SOUNDMOD_FEEDBACKS "Feedback Percentage" + IDS_COMP_SOUNDMOD_FREQ_OSC_CUTOFFS "Low Frequency Oscillator Cutoff Hz" + IDS_COMP_SOUNDMOD_FEEDBACK_DELAYS "Feedback Delay (ms)" + IDS_COMP_SOUNDMOD_GAINS "Gain dB" + IDS_COMP_SOUNDMOD_ATTACKS "Attack ms" + IDS_COMP_SOUNDMOD_RELEASES "Release ms" + IDS_COMP_SOUNDMOD_COMP_RATIOS "Compression Ratio x:1" + IDS_COMP_SOUNDMOD_ATTACK_PREDELAYS "Attack Pre-Delay (ms)" +END + +STRINGTABLE +BEGIN + IDS_COMP_SOUNDMOD_COMPTHRESHS "Compression Threshold dB" + IDS_COMP_SOUNDMOD_INTENSITYS "Intensity Percentage" + IDS_COMP_SOUNDMOD_EFFECTCS "Effects Center Hz" + IDS_COMP_SOUNDMOD_EFFECTWS "Effect Width Hz" + IDS_COMP_SOUNDMOD_LOWFREQ_CUTOFFS "Low Frequency Cutoff Hz" + IDS_COMP_SOUNDMOD_LEFTFBDS "Left Feedback Delay (ms)" + IDS_COMP_SOUNDMOD_RIGHTFBDS "Right Feedback Delay (ms)" + IDS_COMP_SOUNDMOD_SWAPRLS "Swap Right to Left Delay" + IDS_COMP_SOUNDMOD_FBDELAYFS "Feedback Delay (ms) -Flanger-" + IDS_COMP_SOUNDMOD_MODRATES "Modulation Rate" + IDS_COMP_SOUNDMOD_REVERBGAINS "Reverb Gain (dB)" + IDS_COMP_SOUNDMOD_REVERBMIXS "Reverb Mix" + IDS_COMP_SOUNDMOD_REVERBDELAYS "Reverb Delay" + IDS_COMP_SOUNDMOD_GAINDS "Distortion Gain" + IDS_COMP_OCCLUSIONS "Occlusion Component" + IDS_COMP_OCCLUSION_PROXY "Occlusion Proxy" +END + +STRINGTABLE +BEGIN + IDS_COMP_LODFADE "LOD Fade" + IDS_COMP_SOUNDMOD_BINDINGSS "Sound Effects Bound" + IDS_COMP_PARTICLE_ROLL "Particle System" + IDS_COMP_CAMERACMD "Camera Command Region" + IDS_COMP_PAGEINFOS "Page Info" + IDS_COMP_SOUNDBGND "Nonspatial sound" + IDS_COMP_SMOOTHS "Inter-mesh smoothing" + IDS_COMP_LINEFOLLOWS "Object follows path" + IDS_COMP_LINE_CHOSE_OBJECT "Choose object to follow." + IDS_COMP_LINE_CHOSE_PATH "Choose Path to Stick to" + IDS_COMP_DETECTOR_CLICKABLE "Clickable Object" + IDS_COMP_CMD_PARAMS "Command Parameters" + IDS_COMP_RANDOMSOUNDS "Random Sound Emitter" + IDS_COMP_DETECTOR_CLICK_DRAGGABLE "Click-Draggable Object" + IDS_COMP_LIGHTINCS "Limit light effect" + IDS_COMP_LIGHTINC_CHOSE_LIGHT "Select the light" +END + +STRINGTABLE +BEGIN + IDS_COMP_PARTICLE_VOLUME_ROLL "Collision Volume Effect" + IDS_COMP_PARTICLE_FADE_ROLL "Fade Volume Effect" + IDS_COMP_FOLLOWS "Move with another object." + IDS_COMP_INV_OBJECTS "(ex) Inventory Object" + IDS_COMP_SOFTVOLUMES "Soft Region For Effect" + IDS_COMP_PYTHON "Python" + IDS_COMP_SOUND_REVERB_PARAMS "Effect Parameters" + IDS_COMP_DETECTOR_REGION "Region Sensor" + IDS_COMP_UNLEASH "Unleash Satan" + IDS_COMP_DETECTOR_COLLISION "Collision Sensor" + IDS_COMP_SOFTVOLUME_UNION "Region Union" + IDS_COMP_PHYS_WHEEL_CONSTRAINTS "(ex) Wheel Constraint System" + IDS_COMP_PHYS_HINGE_CONSTRAINTS "(ex) Hinge Constraint System" + IDS_COMP_PHYS_SS_CONSTRAINTS "(ex) StrongString Constraint" + IDS_COMP_SOFTVOLUME_ISECT "Region Intersection" + IDS_COMP_SOFTVOLUME_NEGATE "Invert Region" +END + +STRINGTABLE +BEGIN + IDS_COMP_LIGHTREGION "Light Effect Region" + IDS_COMP_GEO_DICE "Terrain Optimizer" + IDS_COMP_ONESHOTS "One Shot" + IDS_COMP_WAIT "Wait" + IDS_COMP_SOUNDFADEPARAMS "Fade Parameters" + IDS_COMP_SOUNDSOFTPARAMS "Soft Region Params" + IDS_COMP_SOUNDSOFTSELECT + "Select a soft region to use with this sound emitter" + IDS_COMP_LOD_AVATARS "(ex) LOD Avatar" + IDS_CAMERARGN "Camera Region" + IDS_COMP_PARTICLE_WIND "Particle wind" + IDS_COMP_BLENDONTOADV "Blend Onto Advanced" + IDS_COMP_PARTICLE_UNIWIND "Uniform Wind" + IDS_COMP_DISTFADE "Distance Fade" + IDS_COMP_SHADOW_CAST "Object Casts Shadows" + IDS_COMP_GUIDIALOG "GUI Dialog" + IDS_CAMERA_OCCLUDERS "(ex) Camera Blocker" +END + +STRINGTABLE +BEGIN + IDS_COMP_SHADOW_RCV "Shadow Receive Props" + IDS_COMP_AUTOCAM "(ex)AutoCamera" + IDS_COMP_GUIBUTTON "GUI Button" + IDS_COMP_DISTRIBUTORS "Geometry Distributor" + IDS_COMP_GUIDRAGGABLE "GUI Draggable" + IDS_COMP_CLUSTERS "Cluster geometry" + IDS_COMP_XREGION "Exclude Region" + IDS_COMP_GUILISTBOX "GUI List Box" + IDS_COMP_GUITEXTBOX "GUI Text Box" + IDS_COMP_ANIM_EASE "Ease In/Out" + IDS_COMP_IGNORELITES "Control Max Light" + IDS_COMP_GUIEDITBOX "GUI Edit Box" + IDS_COMP_FLEXIBILITYS "Flexibility in wind." + IDS_COMP_GUITAG "GUI ID Konstant Tag" + IDS_COMP_BLOWS "Wind Blows" + IDS_COMP_CLUSTER_CHOSE_WINDBONE "Wind Bone" +END + +STRINGTABLE +BEGIN + IDS_COMP_GZ_FADE "GZ Marker Fader" + IDS_COMP_ANIM_GROUPED "Grouped Animation" + IDS_COMP_XFORMS "X-Form" + IDS_COMP_BEHAVIOR_SITTINGS "Sitting Component" + IDS_COMP_GUIUDSCROLL "GUI Up/Down Scroll Pair" + IDS_COMP_GUI_SELECTUDCTRL + "Select a GUI control for this up/down scroll pair" + IDS_COMP_GUI_SELECTSCROLL "Select a GUI control to use for scrolling" + IDS_COMP_PHYS_MEMBER "Member Groups" + IDS_COMP_PHYS_BOUNCE "Bounce Groups" + IDS_COMP_PHYS_REPORT "Report Groups" + IDS_COMP_NAV_LADDERS "Ladder" + IDS_COMP_CLONE_INST "Clone Instance" + IDS_COMP_CLONE_TEMPLATE "Clone Template" + IDS_COMP_GUIKNOB "GUI Knob Control" + IDS_PARTICLE_VELOCITY_MIN "Min Velocity" + IDS_PARTICLE_VELOCITY_MAX "Max Velocity" +END + +STRINGTABLE +BEGIN + IDS_PARTICLE_LIFE_MIN "Min Life" + IDS_PARTICLE_LIFE_MAX "Max Life" + IDS_PARTICLE_SCALE_MIN "Min Scale" + IDS_PARTICLE_SCALE_MAX "Max Scale" + IDS_PARTICLE_PPS "PPS" + IDS_PARTICLE_CONE_ANGLE "Cone Angle" + IDS_COMP_GUIDRAGBAR "GUI Dialog Drag Bar" + IDS_COMP_DIST_TEMPLATES "Replicant Objects" + IDS_COMP_DIST_SPACING "Spacing" + IDS_COMP_DIST_ORIENT "Replicant Orientation" + IDS_COMP_DIST_ANGPROB "Orientation Dependence" + IDS_COMP_DIST_ALTPROB "Altitude Dependence" + IDS_COMP_DIST_CONFORM "Conformity to Surface" + IDS_COMP_DIST_BITMAP "Probability Bitmap " + IDS_COMP_DIST_FADE "LOD Fade" + IDS_COMP_DIST_WIND "Wind Effect" +END + +STRINGTABLE +BEGIN + IDS_COMP_DIST_RANDOMIZE "Randomization" + IDS_COMP_DIST_SCALE "Scaling" + IDS_COMP_GUICHECKBOX "GUI Check Box" + IDS_COMP_GUI_SELECTANIM "Select an animation to use with this control" + IDS_COMP_DIST_ACTION "Actions" + IDS_COMP_GUIRADIOGROUP "GUI Radio Group" + IDS_COMP_GUI_SELECTCHECK "Select a check box to add to this radio group" + IDS_COMP_GUIPROCROLLOUT "Controlling Procedure" + IDS_COMP_GUISCHEME "Color Scheme" + IDS_COMP_PHYS_DEBUG "Physics Debug" + IDS_COMP_PHYS_SIMPLE "Simple" + IDS_COMP_PHYS_TERRAIN "Terrain" + IDS_COMP_PHYS_PROXY_TERRAIN "Proxy Terrain" + IDS_COMP_PHYS_INVISIBLE "Invisible Blocker" + IDS_COMP_PHYS_WALKABLE "Walkable" + IDS_COMP_GUI_SELECTMOUSEOVERANIM + "Select an animation to use when the mouse moves over this control" +END + +STRINGTABLE +BEGIN + IDS_COMP_PHYS_CLIMBABLE "Climbable" + IDS_COMP_PHYS_SWIMSURF "Swimming Surface" + IDS_COMP_PHYS_SWIM3D "Swim 3D" + IDS_COMP_DYNMAT "Dynamic Colors" + IDS_COMP_PHYS_CRITTER "Critter" + IDS_COMP_GUIDYNDISPLAY "GUI Dynamic Display" + IDS_COMP_CLOTHING "Clothing" + IDS_COMP_RAILCAMERA "Rail Camera" + IDS_COMP_CIRCLECAMERA "Circle Camera" + IDS_COMP_ALLOW_PAN "Allow Camera Panning" + IDS_COMP_DETECTOR_ANIM "Anim Event" + IDS_COMP_CAMERAZOOM "Camera Zoom" + IDS_AVATARPOA "Avatar POA component" + IDS_COMP_CAMERATRANS "Camera Transition Override" + IDS_COMP_OBJECTPOA "Object POA component" + IDS_COMP_FIRST_PERSONCAM "First Person Camera" +END + +STRINGTABLE +BEGIN + IDS_COMP_DETECTOR_MTL "Mtl Event" + IDS_COMP_VEHICLE "Vehicle" + IDS_COMP_SHADOW_LIGHT "Light Casts Shadows" + IDS_COMP_SOUNDSRC "Waveform Properties" + IDS_COMP_RANDOMSOUNDS_GROUPS "Sound Groups" + IDS_COMP_FOOTSTEP_SOUND "Footstep Sounds" + IDS_COMP_EAXLISTENER "EAX Listener Properties" + IDS_COMP_EAXBUFFER "Source EAX Properties" + IDS_SND_VOLUME "Volume" + IDS_COMP_FP_SOURCENODE "Pick Node" + IDS_COMP_RIPPLE "Ripple" + IDS_COMP_FP_DIRTY "Dirty/Wet" + IDS_COMP_FP_PRINTSHAPE "Print Shape" + IDS_COMP_PUDDLE "Puddle" + IDS_COMP_SUBWORLD_REGION "Subworld Region" + IDS_COMP_NPC_SPAWNER "NPC Spawner" +END + +STRINGTABLE +BEGIN + IDS_COMP_FP_ACTIVEPRINTSHAPE "Active Shape" + IDS_COMP_GUIMULTILINE "GUI Multi-Line Edit Control" + IDS_COMP_FP_WAKE "Wake" + IDS_COMP_GUIPROXY "GUI Control Proxy" + IDS_COMP_GUIPROXY_PROMPT + "Choose an object to use as the proxy geometry for this GUI control" + IDS_COMP_FILTER "Disable Inheritance" + IDS_COMP_SOUNDBASE "Base Sound Parameters" + IDS_COMP_STEREIZE "Stereo-izer" + IDS_COMP_CLIMB_TRIGGER "Climb Trigger" + IDS_COMP_CLIMB_BLOCKER "Climb Blocker" + IDS_COMP_SWIVEL "Face things" + IDS_COMP_CHOOSE_OBJECT "Choose Object" + IDS_COMP_MORPHLAY "Morph Layer" + IDS_COMP_MORPHSEQ "Morph Sequence" + IDS_COMP_CHOSE_OBJECT "Choose Mesh" + IDS_COMP_OBJECT_FOLLOWCAM "Object Follow Camera" +END + +STRINGTABLE +BEGIN + IDS_COMP_EMOTE "Emote Animation" + IDS_COMP_WATER "Fixed Water" + IDS_COMP_W_REFOBJECT "Reference Object" + IDS_COMP_W_BASICWATER "Core Water Params" + IDS_COMP_W_ADVWATER "Mnor Water Params" + IDS_COMP_W_BASICSHORE "Core Shore Params" + IDS_COMP_W_ADVSHORE "Minor Shore Params" + IDS_COMP_SHORE "Shore Line" + IDS_COMP_SHORE_CHOSE "Chose Water" + IDS_COMP_GUIPROGRESS "GUI Progress Ctrl" + IDS_COMP_W_ENVMAP "Reflection" + IDS_COMP_MAINTAINERS_MARKER "Maintainers Marker" + IDS_COMP_NOSHOW "Make Invisible" + IDS_COMP_GUICLICKMAP "GUI Clickable Map" + IDS_COMP_VISREGION "Visibility Region" + IDS_COMP_SMOOTHAV "Avatar Smoothing" +END + +STRINGTABLE +BEGIN + IDS_COMP_GUISKIN "GUI Skin" + IDS_COMP_GUIMENUANCHOR "GUI Pop-Up Menu Anchor" + IDS_COMP_WDECAL "Water Decal" + IDS_COMP_ANIM_CAM_CMD "Animated Camera Commands" + IDS_COMP_NETSYNC "Net Sync" + IDS_COMP_RELREGION "Relevance Region" + IDS_COMP_REPRESENT "Representation" + IDS_COMP_REPGROUP "Representation Group" + IDS_COMP_PHYS_BRIDGE "Bridge" + IDS_COMP_SMOOTHBASE "Smoothing Base Reference" + IDS_COMP_SMOOTHSNAP "Snap to template" + IDS_COMP_IMAGELIB "Image Lib" + IDS_COMP_W_VTXHELP "Vertex Color Help" + IDS_COMP_PHYS_SUBWORLD "Subworld" + IDS_COMP_PARTICLE_FLOCK "Particle Flock" + IDS_COMP_EFFVISSET "Effect Visibility Set" +END + +STRINGTABLE +BEGIN + IDS_COMP_ENVMAP "Dynamic Environment Map" + IDS_COMP_W_GEOWATER "Geometric Waves" + IDS_COMP_W_TEXWATER "Texture Waves" + IDS_COMP_SHOOTABLE "Shootable" + IDS_COMP_FP_BULLET "Bullet Impact" + IDS_COMP_FP_TORPEDO "Water bullet" + IDS_COMP_PANIC "Panic Region" + IDS_COMP_SOUNDPHYS "Physics Sound Group" + IDS_COMP_FORCE_CTT "Force Click-To-Turnable" + IDS_COMP_MARKER "Game Marker" + IDS_COMP_LOSFADE "LOS Fade" + IDS_COMP_SORT_AS_OPAQUE "Draw After Avatar" + IDS_SWIM_DETECTOR_NODE "Swim Detector" + IDS_SWIM_CURRENT_NODE "Swim Current" + IDS_COMP_OBJ_FLOCKER "Object Flocker" + IDS_COMP_GRASS_ROLL "Grass" +END + +STRINGTABLE +BEGIN + IDS_COMP_ANIM_COMPRESS_ROLL "Anim Compression" + IDS_COMP_RIDE_ANIMATED_PHYS "Ride Animated Physical Region" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentBase.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentBase.cpp new file mode 100644 index 00000000..e342f680 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentBase.cpp @@ -0,0 +1,694 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plComponentBase.h" +#include "plComponentReg.h" +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "../MaxMain/plMaxNodeBase.h" + +#include "plAutoUIComp.h" + +plComponentBase::plComponentBase() : fClassDesc(nil), fCompPB(nil), fTargsPB(nil) +{ +} + +plComponentBase::~plComponentBase() +{ + DeleteAllRefsFromMe(); +} + +CreateMouseCallBack* plComponentBase::GetCreateMouseCallBack() +{ + return NULL; +} + +void plComponentBase::BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev) +{ + fClassDesc->BeginEditParams(ip, this, flags, prev); +} + +void plComponentBase::EndEditParams(IObjParam *ip, ULONG flags, Animatable *next) +{ + fClassDesc->EndEditParams(ip, this, flags, next); +} + +int plComponentBase::NumParamBlocks() +{ + return 2; +} + +IParamBlock2 *plComponentBase::GetParamBlock(int i) +{ + if (i == kRefComp) + return fCompPB; + else if (i == kRefTargs) + return fTargsPB; + + return nil; +} + +IParamBlock2 *plComponentBase::GetParamBlockByID(BlockID id) +{ + if (fCompPB && fCompPB->ID() == id) + return fCompPB; + else if (fTargsPB && fTargsPB->ID() == id) + return fTargsPB; + + return nil; +} + +// So our animatables will show up in the trackview +int plComponentBase::NumSubs() +{ + return 1; +} + +Animatable *plComponentBase::SubAnim(int i) +{ + return fCompPB; +} + +TSTR plComponentBase::SubAnimName(int i) +{ + return fClassDesc->ClassName(); +} + +RefTargetHandle plComponentBase::Clone(RemapDir &remap) +{ + plComponentBase *obj = (plComponentBase*)fClassDesc->Create(false); + // Do the base clone + BaseClone(this, obj, remap); + // Copy our references + if (fCompPB) + obj->ReplaceReference(kRefComp, fCompPB->Clone(remap)); + if (fTargsPB) + obj->ReplaceReference(kRefTargs, fTargsPB->Clone(remap)); + + return obj; +} + +void plComponentBase::BuildMesh(TimeValue t) +{ +} + +void plComponentBase::FreeCaches() +{ +} + +void plComponentBase::GetLocalBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box) +{ + box.MakeCube(Point3(0,0,0), 0); +} + +void plComponentBase::GetWorldBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box) +{ + box.MakeCube(Point3(0,0,0), 0); +} + +int plComponentBase::Display(TimeValue t, INode *node, ViewExp *vpt, int flags) +{ + return 0; +} + +int plComponentBase::HitTest(TimeValue t, INode *node, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt) +{ + return 0; +} + +int plComponentBase::NumRefs() +{ + return 2; +} + +RefTargetHandle plComponentBase::GetReference(int i) +{ + if (i == kRefComp) + return fCompPB; + else if (i == kRefTargs) + return fTargsPB; + + return nil; +} + +void plComponentBase::SetReference(int i, RefTargetHandle rtarg) +{ + if (i == kRefTargs) + fTargsPB = (IParamBlock2*)rtarg; + else if (i == kRefComp) + fCompPB = (IParamBlock2*)rtarg; +} + +RefResult plComponentBase::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) +{ + return REF_SUCCEED; +} + +IOResult plComponentBase::Save(ISave* isave) +{ + return IO_OK; +} + +IOResult plComponentBase::Load(ILoad* iload) +{ + return IO_OK; +} + +void plComponentBase::AddTargetsToList(INodeTab& list) +{ + int i; + for( i = 0; i < NumTargets(); i++ ) + { + INode* targ = GetTarget(i); + if( targ ) + list.Append(1, &targ); + } +} + +UInt32 plComponentBase::NumTargets() +{ + if (fTargsPB) + return fTargsPB->Count(kTargs); + + return 0; +} + +plMaxNodeBase *plComponentBase::GetTarget(UInt32 i) +{ + if (fTargsPB && i < NumTargets()) + return (plMaxNodeBase*)fTargsPB->GetINode(kTargs, 0, i); + + return nil; +} + +void plComponentBase::AddTarget(plMaxNodeBase *target) +{ + if (!target) + return; + + // Make sure we don't already ref this + UInt32 count = fTargsPB->Count(kTargs); + for (UInt32 i = 0; i < count; i++) + { + if (fTargsPB->GetINode(kTargs, 0, i) == target) + return; + } + + // Add it to our list + fTargsPB->Append(kTargs, 1, (INode**)&target); + + NotifyDependents(FOREVER, (PartID)target, REFMSG_USER_TARGET_ADD); +} + +void plComponentBase::DeleteTarget(plMaxNodeBase *target) +{ + if (!target) + return; + + UInt32 count = fTargsPB->Count(kTargs); + for (UInt32 i = 0; i < count; i++) + { + if (fTargsPB->GetINode(kTargs, 0, i) == target) + { + fTargsPB->Delete(kTargs, i, 1); + NotifyDependents(FOREVER, (PartID)target, REFMSG_USER_TARGET_DELETE); + return; + } + } +} + +void plComponentBase::DeleteAllTargets() +{ + while (fTargsPB->Count(kTargs) > 0) + { + INode *node = fTargsPB->GetINode(kTargs); + if (node) + NotifyDependents(FOREVER, (PartID)node, REFMSG_USER_TARGET_DELETE); + + fTargsPB->Delete(kTargs, 0, 1); + } +} + +bool plComponentBase::IsTarget(plMaxNodeBase *node) +{ + if (!node) + return false; + + for (int i = 0; i < fTargsPB->Count(kTargs); i++) + if (fTargsPB->GetINode(kTargs, 0, i) == node) + return true; + + return false; +} + +const char* plComponentBase::IGetUniqueName(plMaxNodeBase* target) +{ + static char nameBuf[256]; + + // Make sure we've actually got multiple *used* targets. (Some could be nil) + int numUsedTargs = 0; + int thisTargIdx = -1; + for (int i = 0; i < fTargsPB->Count(kTargs); i++) + { + plMaxNodeBase* thisTarg = (plMaxNodeBase*)fTargsPB->GetINode(kTargs, 0, i); + + if (thisTarg) + numUsedTargs++; + + if (thisTarg == target) + thisTargIdx = i; + + // If we've figured out whether or not we have multiple targets, and we + // found which target this one is, we can break out early + if (numUsedTargs > 1 && thisTargIdx != -1) + break; + } + + hsAssert(thisTargIdx != -1, "Bad target for IGetUniqueName"); + + if (numUsedTargs > 1) + _snprintf(nameBuf, sizeof(nameBuf), "%s_%d", GetINode()->GetName(), thisTargIdx); + else + strncpy(nameBuf, GetINode()->GetName(), sizeof(nameBuf)); + + return nameBuf; +} + +plMaxNodeBase *plComponentBase::GetINode() +{ + // Go through the reflist looking for RefMakers with a ref to this component. + // There should only be one INode in this list. + RefList &refList = GetRefList(); + RefListItem *item = refList.FirstItem(); + while (item) + { + if (item->maker->SuperClassID() == BASENODE_CLASS_ID) + return (plMaxNodeBase*)item->maker; + + item = item->next; + } + + return nil; +} + +hsBool plComponentBase::IsExternal() +{ + return CanConvertToType(EXT_COMPONENT_CLASSID); +} + +bool plComponentBase::IsCurMsgLocal() +{ + // Was the last notification a message propagated up from a target? + // Ignore it if so. + if (fTargsPB->LastNotifyParamID() != -1) + return false; + + // Was the last notification from a ref in the component PB? If so, assume + // it was a propagated message from the ref. Real changes to that ref + // parameter will be signalled with a user ref message. + if (fCompPB->LastNotifyParamID() != -1) + { + ParamID id = fCompPB->LastNotifyParamID(); + if (is_ref(fCompPB->GetParamDef(id))) + return false; + } + + return true; +} + +bool plComponentBase::IsObsolete() +{ + return ((plComponentClassDesc*)fClassDesc)->IsObsolete(); +} + +bool DoesPBReferenceNode(IParamBlock2* pb, INode* node); + +static bool CheckRef(ReferenceTarget* targ, INode* node) +{ + if (!targ) + return false; + if (targ->SuperClassID() == BASENODE_CLASS_ID) + { + if ((INode*)targ == node) + return true; + } + else if (targ->SuperClassID() == PARAMETER_BLOCK2_CLASS_ID) + { + return DoesPBReferenceNode((IParamBlock2*)targ, node); + } + return false; +} + +static bool DoesPBReferenceNode(IParamBlock2* pb, INode* node) +{ + for (int i = 0; i < pb->NumParams(); i++) + { + ParamID id = pb->IndextoID(i); + ParamType2 type = pb->GetParameterType(id); + if (type == TYPE_INODE) + { + if (pb->GetINode(id) == node) + return true; + } + if (type == TYPE_INODE_TAB) + { + for (int iNode = 0; iNode < pb->Count(id); iNode++) + { + if (pb->GetINode(id, 0, iNode) == node) + return true; + } + } + if (type == TYPE_REFTARG) + { + ReferenceTarget* targ = pb->GetReferenceTarget(id); + bool ret = CheckRef(targ, node); + if (ret) + return true; + } + if (type == TYPE_REFTARG_TAB) + { + for (int iRef = 0; iRef < pb->Count(id); iRef++) + { + ReferenceTarget* targ = pb->GetReferenceTarget(id, 0, iRef); + bool ret = CheckRef(targ, node); + if (ret) + return true; + } + } + } + + return false; +} + +bool plComponentBase::DoReferenceNode(INode* node) +{ + if (!fCompPB) + return false; + + return DoesPBReferenceNode(fCompPB, node); +} + +void plComponentBase::CreateRollups() +{ + if (!fCompPB) + return; + + ParamBlockDesc2 *pd = fCompPB->GetDesc(); + plComponentClassDesc *cd = (plComponentClassDesc*)pd->cd; + + // This is a plAutoUIComp, we need to treat it differently + if (cd->IsAutoUI()) + { + plAutoUIClassDesc *cd = (plAutoUIClassDesc*)pd->cd; + cd->CreateAutoRollup(fCompPB); + } + // We're using a normal param block with auto UI + else if (pd->flags & P_AUTO_UI) + { + if (pd->flags & P_MULTIMAP) + { + int nMaps = pd->map_specs.Count(); + for (int i = 0; i < nMaps; i++) + { + ParamBlockDesc2::map_spec spec = pd->map_specs[i]; + + // Create the rollout + IParamMap2 *map = CreateCPParamMap2(spec.map_id, + fCompPB, + GetCOREInterface(), + hInstance, + MAKEINTRESOURCE(spec.dlg_template), + GetString(spec.title), + spec.rollup_flags, + spec.dlgProc, + NULL, + ROLLUP_CAT_STANDARD); + + // Save the rollout in the paramblock + fCompPB->SetMap(map, spec.map_id); + } + } + else + { + // Create the rollout + IParamMap2 *map = CreateCPParamMap2(0, + fCompPB, + GetCOREInterface(), + hInstance, + MAKEINTRESOURCE(pd->dlg_template), + GetString(pd->title), + pd->flags, + pd->dlgProc, + NULL, + ROLLUP_CAT_STANDARD); + + // Save the rollout in the paramblock + fCompPB->SetMap(map); + } + } + + fCompPB->ReleaseDesc(); +} + +void plComponentBase::DestroyRollups() +{ + if (!fCompPB) + return; + + ParamBlockDesc2 *pd = fCompPB->GetDesc(); + plComponentClassDesc *cd = (plComponentClassDesc*)pd->cd; + + // This is a plAutoUIComp, we need to treat it differently + if (cd->IsAutoUI()) + { + plAutoUIClassDesc *autoCD = (plAutoUIClassDesc*)pd->cd; + autoCD->DestroyAutoRollup(); + } + // We're using a normal param block with auto UI + else if (pd->flags & P_AUTO_UI) + { + if (pd->flags & P_MULTIMAP) + { + int nMaps = pd->map_specs.Count(); + for (int i = 0; i < nMaps; i++) + { + MapID id = pd->map_specs[i].map_id; + // Destroy any parammap saved in the rollup + IParamMap2 *map = fCompPB->GetMap(id); + fCompPB->SetMap(nil, id); + if (map) + DestroyCPParamMap2(map); + } + } + else + { + // Destroy any parammap saved in the rollup + IParamMap2 *map = fCompPB->GetMap(); + fCompPB->SetMap(nil); + if (map) + DestroyCPParamMap2(map); + } + } + + fCompPB->ReleaseDesc(); +} + +//////////////////////////////////////////////////////////////////////////////// + +static bool INodeHasComponent(plMaxNodeBase *node, plMaxNodeBase *compNode) +{ + UInt32 count = node->NumAttachedComponents(); + for (UInt32 i = 0; i < count; i++) + { + if (node->GetAttachedComponent(i)->GetINode() == compNode) + return true; + } + + return false; +} + +int plSharedComponents(INodeTab& nodes, INodeTab& components) +{ + components.ZeroCount(); + + if (nodes.Count() == 0) + return 0; + + plMaxNodeBase *firstNode = (plMaxNodeBase*)nodes[0]; + int num = firstNode->NumAttachedComponents(); + + // Resize the list to it's max size to be more efficient + components.SetCount(num); + + int i; + + // Put all the components on the first node into a list + for (i = 0; i < num; i++) + components[i] = firstNode->GetAttachedComponent(i)->GetINode(); + + // Delete any components that aren't on all the other nodes + for (i = 1; i < nodes.Count(); i++) + { + plMaxNodeBase *node = (plMaxNodeBase*)nodes[i]; + UInt32 count = node->NumAttachedComponents(); + + for (int j = components.Count()-1; j >= 0; j--) + { + if (!INodeHasComponent(node, (plMaxNodeBase*)components[j])) + components.Delete(j, 1); + } + } + + return components.Count(); +} + + +/////////////////////////////////////////////////////////////////////////////////////////// + +#include "notify.h" + +static void UpdateComponentVisibility(plMaxNodeBase *node) +{ + plComponentBase *comp = node->ConvertToComponent(); + if (comp) + { + node->Freeze(TRUE); + node->Hide(!comp->AllowUnhide()); + } + + for (int i = 0; i < node->NumberOfChildren(); i++) + { + plMaxNodeBase *childNode = (plMaxNodeBase*)node->GetChildNode(i); + UpdateComponentVisibility(childNode); + } +} + +static void UnlinkComponents(plMaxNodeBase *node) +{ + plComponentBase *comp = node->ConvertToComponent(); + if (comp) + comp->DeleteAllTargets(); + + for (int i = 0; i < node->NumberOfChildren(); i++) + { + plMaxNodeBase *child = (plMaxNodeBase*)node->GetChildNode(i); + UnlinkComponents(child); + } +} + +static void FindObsoleteComponents(plMaxNodeBase *node, std::vector& obsoleteComps) +{ + plComponentBase *comp = node->ConvertToComponent(); + if (comp && comp->IsObsolete()) + obsoleteComps.push_back(comp); + + for (int i = 0; i < node->NumberOfChildren(); i++) + { + plMaxNodeBase *childNode = (plMaxNodeBase*)node->GetChildNode(i); + FindObsoleteComponents(childNode, obsoleteComps); + } +} + +static bool gUpdatingComponents = false; + +#include +#include "hsUtils.h" + +static void ComponentNotify(void *param, NotifyInfo *info) +{ + if (info->intcode == NOTIFY_NODE_UNHIDE) + { + if (!gUpdatingComponents) + { + plMaxNodeBase *node = (plMaxNodeBase*)info->callParam; + plComponentBase *comp = node ? node->ConvertToComponent() : nil; + if (comp) + { + node->Hide(!comp->AllowUnhide()); + node->Freeze(TRUE); + } + } + } + else if (info->intcode == NOTIFY_FILE_POST_OPEN) + { + plComponentShow::Update(); + GetCOREInterface()->ForceCompleteRedraw(); + // Also checking bitmap silent mode, since the export script uses that + if (!hsMessageBox_SuppressPrompts && !TheManager->SilentMode()) + { + // Get all the obsolete components in the scene + std::vector obsoleteComps; + FindObsoleteComponents((plMaxNodeBase*)GetCOREInterface()->GetRootNode(), obsoleteComps); + + // For now, just get the categories (could get the component names) + std::set names; + for (int i = 0; i < obsoleteComps.size(); i++) + { + const char *name = obsoleteComps[i]->GetObjectName(); + names.insert(name); + } + + if (obsoleteComps.size() > 0) + { + char buf[1024]; + strcpy(buf, "Components of the following obsolete types\nwere found in this scene. Please delete them.\n\n"); + std::set::iterator it = names.begin(); + for (; it != names.end(); it++) + { + strcat(buf, (*it)); + strcat(buf, "\n"); + } + + hsMessageBox(buf, "Obsolete Components", hsMBoxOk); + } + } + } + // WEIRD HACK - If lots of components are linked to objects when a scene is closed + // the closing goes really slow. I assume it is just repeatedly calling some + // unoptimized section of code (it would probably happen for any large number of + // references). So, at this point if the scene was changed the user would have already + // been prompted to save it, so we can modify the scene by deleting all the component + // references. -Colin + else if (info->intcode == NOTIFY_SYSTEM_SHUTDOWN || + info->intcode == NOTIFY_FILE_PRE_OPEN) + { + UnlinkComponents((plMaxNodeBase*)GetCOREInterface()->GetRootNode()); + } +} + +void plComponentShow::Init() +{ + RegisterNotification(ComponentNotify, 0, NOTIFY_FILE_POST_OPEN); + RegisterNotification(ComponentNotify, 0, NOTIFY_NODE_UNHIDE); + RegisterNotification(ComponentNotify, 0, NOTIFY_SYSTEM_SHUTDOWN); + RegisterNotification(ComponentNotify, 0, NOTIFY_FILE_PRE_OPEN); +} + +void plComponentShow::Update() +{ + gUpdatingComponents = true; + UpdateComponentVisibility((plMaxNodeBase*)GetCOREInterface()->GetRootNode()); + gUpdatingComponents = false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentBase.h new file mode 100644 index 00000000..ad83b690 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentBase.h @@ -0,0 +1,387 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_COMPONENT_BASE_H +#define PL_COMPONENT_BASE_H + +#include "Max.h" +#include "iparamb2.h" +#include "iparamm2.h" + +#include "hsTypes.h" +#include "../pnKeyedObject/plKey.h" + +extern TCHAR *GetString(int id); +extern HINSTANCE hInstance; + +#define COMPONENT_CLASSID Class_ID(0x758e5142, 0x66c748a) +#define EXT_COMPONENT_CLASSID Class_ID(0x6c277b63, 0x6c626b3f) + +class plMaxNodeBase; +class plKey; +class plMaxNode; +class plAGAnim; + +class plComponentBase : public HelperObject +{ +protected: + ClassDesc2 *fClassDesc; // Must set in derived classes constructor + + IParamBlock2 *fCompPB; // The derived component's paramblock (optional) + IParamBlock2 *fTargsPB; // Nodes this component is attached to + + // Get a unique name based on this components name and the index of this target + // Return value points to a static, so don't try to hold on to it + const char* IGetUniqueName(plMaxNodeBase* target); + +public: + // Permanent Block ID's + enum + { + kBlkComp, + kBlkTargs = 10, // To avoid any conflicts with legacy blocks + }; + // Ref numbers + enum + { + kRefComp, + kRefTargs, + }; + + plComponentBase(); + virtual ~plComponentBase(); + void DeleteThis() { delete this; } + + UInt32 NumTargets(); + plMaxNodeBase *GetTarget(UInt32 i); + virtual void AddTarget(plMaxNodeBase *target); + virtual void DeleteTarget(plMaxNodeBase *target); + virtual void DeleteAllTargets(); + bool IsTarget(plMaxNodeBase *node); + + plMaxNodeBase *GetINode(); + + virtual void AddReceiverKey(plKey key, plMaxNode* node=nil) {;} + virtual plKey GetLogicKey(plMaxNode* node) {return nil;} + + // Return true if you want to allow yourself to be unhidden. This is for components + // with animatable parameters they want to expose in the TrackView. + virtual bool AllowUnhide() { return false; } + + // Collect up all the non-runtime renderable geometry that's going to wind up + // physical proxies, trigger boxes, soft regions etc. This is normally everything + // that this component is going to call SetDrawable(false) on. The list returned might + // be selected, hidden, have max's renderable property set to false, whatever, you don't + // have to worry about that. All you have to do is, if your component SetDrawable(false)'s + // on anything, try to remember to override this function and add the MaxNodes you're + // making non-drawable to this list when called. Remember, just append to the list. + // If you're going to add everything you're attached to to the list, you can just + // call AddTargetsToList(nonDrawables), so you're overriding function just looks like: + // virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } + virtual void CollectNonDrawables(INodeTab& nonDrawables) { return; } + virtual void AddTargetsToList(INodeTab& list); + + // Return the minimum graphics capability required by objects created by this component. + // Unless a component is generating an object that uses shaders, or has some other + // specific graphics requirement, the default of 0 (meaning no special requirements) + // should not be overriden. + virtual int GetMinCap() { return 0; } + + hsBool IsExternal(); + + // Returns true if the msg that was just sent out was local or from something + // this component ref's. Used by the SceneWatcher to determine if a component + // needs to be reconverted. + virtual bool IsCurMsgLocal(); + + bool IsObsolete(); + + // Returns true if this component has a reference to node (being attached doesn't count) + virtual bool DoReferenceNode(INode* node); + + virtual void CreateRollups(); + virtual void DestroyRollups(); + + /////////////////////////////////////////////////////////////////////////////////////// + // Required Max functions + // + TCHAR* GetObjectName() { return (TCHAR*)fClassDesc->ClassName(); } + void InitNodeName(TSTR& s) { s = fClassDesc->InternalName(); } + void GetClassName(TSTR& s) { s = fClassDesc->ClassName(); } + Class_ID ClassID() { return fClassDesc->ClassID(); } + + RefTargetHandle Clone(RemapDir &remap); + + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + RefResult NotifyRefChanged(Interval changeInt,RefTargetHandle hTarget, PartID& partID, RefMessage message); + + // allow retreival of our paramblock from other plug-ins + // and the max core + int NumParamBlocks(); + IParamBlock2* GetParamBlock(int i); + IParamBlock2* GetParamBlockByID(BlockID id); + + // So our animatables will show up in the trackview + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + virtual hsBool AddToAnim(plAGAnim *anim, plMaxNode *node) { return false; } + + // plug-in mouse creation callback + CreateMouseCallBack* GetCreateMouseCallBack(); + + void BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev); + void EndEditParams(IObjParam *ip, ULONG flags, Animatable *next); + + void BuildMesh(TimeValue t); + void FreeCaches(); + void GetLocalBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box); + void GetWorldBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box); + int Display(TimeValue t, INode *node, ViewExp *vpt, int flags); + int HitTest(TimeValue t, INode *node, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt); + ObjectState Eval(TimeValue t) { return ObjectState(this); } + + IOResult Save(ISave* isave); + IOResult Load(ILoad* iload); + + int CanConvertToType(Class_ID obtype) { return (obtype == COMPONENT_CLASSID) ? 1 : 0; } + + const char *GetCategory() { return fClassDesc->Category(); } +}; + +// Look through the nodes in 'nodes' and find components that they all share. +// Store those in 'components'. +// Returns the number of shared components +int plSharedComponents(INodeTab& nodes, INodeTab& components); + +// Takes care of the dirty little secrets of components +class plComponentShow +{ +public: + static void Init(); + + // Call this to update the visibility of the components in the scene, ie, if you + // changed the return value of your AllowUnhide function + static void Update(); +}; + +#endif + +/************************************************************************************************ +** +** Information regarding the development of Components for Plasma 2.0 +** +** Date: 3/2/2001 +** Version: Plasma 2.00 +** Needs: A working knowledge of max sdk (Specifically the ParamBlocks...) +** A working knowledge of C++ +** +** +** As the components are heavily reliant on the use of the MAX paramblock, the following methodology +** should be used in their implementation: +** +** In order for the paramblock to not be thrown away during linking, we have started the use of a +** dummy function such as: +** +** void DummyCodeIncludeStartPointFunc() +** { +** } +** +** This is called within the code for the GlobalUtility function, currently. +** +** Next create a derived class from the plComponent class with three public functions, +** being its constunctor, its custom destructor and the converter. Currently, we have the +** converter returning an hsBool to let you know how successful the conversion process has been +** during export to the .prd format. +** +** After the class above has been declared, the param block stuff follows: +** +** (Note all the imformation specifics regarding how to develop the paramblocks can be found +** in the help included in the maxsdk.) +** +** CLASS_DESC is a macro that Max has created that helps stuff your data into a param block. It +** takes in the following formal parameters: +** +** CLASS_DESC(plStartingPointComponent, gStartPtDesc, "StartingPoint", "StartingPointComp", Class_ID(0x2a127b68, 0xdc7367a)) +** +** 1st parameter : The C++ class that is going to be accessing this function (assumed to be passed by reference) +** 2nd parameter : The Instance of the class descriptor that will be used specifically for +** the following param block. It is type int. +** 3rd parameter : The Short name description string for this paramblock. +** 4th parameter : The long name description string for this paramblock. +** 5th parameter : The unique CLASS_ID created via the included GEN_ID.exe that comes with maxsdk. +** +** Following this macro, an enum array is useful to store the relatively unique ID constants for the paramblock. +** +** The format of the definitions in this shown below, but basically is in the form of a sequence of fixed specs followed by a variable number of tagged optional specs for each parameter. +** +** +** Next, the actual param block is declared such as the following: +** +** ParamBlockDesc2 gStartPtBk +** ( +** 1, _T(""), 0, &gStartPtDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, 0, +** +** //rollout +** IDD_COMP_STARTINGPOINT, IDS_COMP_STARTINGPOINTS, 0, 0, NULL, +** +** // params +** kPlayerStartingPoint, _T("Start Point:"), TYPE_STRING, 0, 0, +** p_ui, TYPE_EDITBOX, IDC_COMP_STARTPOINT, +** end, +** +** end +** ); +** +** First is the name of the descriptor method that is to be used, in this case the ParamBlockDesc2 +** type. Second is the unique name of this this paramblock. Following, a collection of comma delimited +** parameters ensue. The exact details as well as other flags to use herein can be found in the MaxR4 sdk +** Help file. We shall explain the above parameters to help give you an idea of the layout. +** All paramblocks have the following format: +** +** 1. Required block specs & per-block flags, followed by, +** 2. Optional owning object reference number for the block if auto-construct, followed by, +** 3. Optional parameter map specs if auto-ui, followed by, +** 4. Zero or more parameter specs, comprising: +** a. Required parameter specs, followed by, +** b. Zero or more optional parameter specs, each with is own leading tag, +** the list terminated by an 'end' tag, followed by, +** 5. an 'end' tag +** +** THE BLOCK SPECS: +** +** BlockParameter 1: +** "1" is the permanent block ID for the parameter block2. It must be unique +** to the block. Considering that you can have several blocks stored together in the same window, +** you may want to enum this as well. THIS IS A REQUIRED FIELD. +** +** BlockParameter 2: +** The internal name string. This name is not localized. Internal names are meant +** to be parsable as identifiers. As such they should begin with an alpha character, have only +** alphanumerics, and have no spaces, punctuations, etc. The convention for multi-word names +** is to use studly-caps, eg, paintRadius. THIS IS A REQUIRED FIELD. +** +** BlockParameter 3: +** The resource ID of the localized (sub-anim) name string. It is of type int. THIS IS +** A REQUIRED FIELD. +** +** BlockParameter 4: +** Points to the class descriptor2 of the owning class. This is used to add this +** descriptor to the ClassDesc2's table of block descriptors for the class. Note: This value may +** be passed as NULL for situations where the blocks owning ClassDesc2 is not available for static +** initializations (such as in a separate file). Before using the descriptor for any block +** construction, the ClassDesc2* must be initialized with the method: +** +** void ParamBlockDesc2::SetClassDesc(ClassDesc2* cd); +** +** You can only call this method once on a descriptor and then only if it has been +** constructed initially with a NULL cd. +** +** BlockParameter 5: One or more BYTE flags. In this instance, we are only using: +** +** P_AUTO_CONSTRUCT +** Indicates the parameter block2 will be constructed and referenced automatically +** to its owner in the call to ClassDesc2::MakeAutoParamBlocks(). If this flag is +** set, the parameter block's reference number in the owning object should be given +** immediately following the flag word in the descriptor constructor. See +** . +** +** P_AUTO_UI +** Indicates this block supports automatic UI rollout management in calls to +** ClassDesc2::BeginEditParams(), ClassDesc2::EndEditParams(),ClassDesc2::CreateParamDlg(), +** ClassDesc2::CreateParamDialog(), etc. +** +*8 The must be supplied in the descriptor constructor. +** +** BlockParameter 6: Because we used the AUTO_UI the following parameters are REQUIRED. +** +** int dialog_template_ID, +** (in this case IDD_COMP_something from an .rc file) +** int dialog_title_res_ID, +** (in this case IDS_COMP_something from the String Table) +** int flag_mask, +** (This is used by ClassDesc2::BeginEditParams() and ClassDesc2::EndEditParams() +** to determine whether the ParamMap2 shold be created/deleted on this call. +** All the bits in the supplied mask must be on in the Begin/EndEditParams +** flag longword for the action to take place. For this example we have 0.) +** int rollup_flags +** (This flag is used to control rollup creation. You may pass +** APPENDROLL_CLOSED to have the rollup added in the closed (rolled up) state. +** Otherwise pass 0.) +** ParamMap2UserDlgProc* proc +** (If there are controls in the dialog that require special processing this user +** If not used then NULL should be passed.) +** +** THE PARAM SPECS: +** +** Param Parameter 1: ParamID id +** (The permanent, position-independent ID for the parameter. In this case, it is +** the constant kStartingPoint.) +** Param Parameter 2: TCHAR* internal_name +** (The internal name for the parameter. In our case "Start Point:". This is what is written on the +** button, spinner, whizbang, foozle, etc.) +** Param Parameter 3: ParamType type +** (The type of parameter. See List of ParamType Choices. In this case it is TYPE_STRING. It could be +** TYPE_INT, TYPE_FLOAT, etc, etc, etc.) +** Param Parameter 4: [int table_size] +** (If the type is one of the Tab<> types, you need to supply an initial table size +** which can be 0. Which it is in our case above.) +** Param Parameter 5: int flags +** (These flags are ORed together. We don't use any, hence 0. They include useful things such as +** p_SubAnim which allow track view acess...) +** Param Parameter 6: int local_name_res_ID +** (Here lies that relatively unique enum mentioned above. Hence, the kStartingPoint.) +** +** OPTIONAL STUFF: +** +** As param blocks don't necessarily need a visual GUI, any that you include are optional. +** +** p_ui, +** (This declares that a GUI will be instantiated. The parameter hereafter will declare the type.) +** GUI_Type +** (This is the type of GUI that you are going to use. In our case it is a CUSTOM editbox. +** +** NOTE: MAX prefers that you create CUSTOM instances of all GUI resources (such as spinners, +** editboxes, etc) +** ) +** +** ID +** (This is the implementor defined name that this GUI is using. It is the name of the object that +** you created a resource of. We created an IDC_COMP_STARTINGPOINT which was of a CustEdit type.) +** +** end +** (This lets the constructor that makes uses to know that you have finished one of the p_ui param +** blocks.) +** +** +** FINAL REQUISITE STUFF: +** +** end +** ); +** (This is the conclusion of the Block descriptor. Sort of intuitive...) +** +*************************************************************************************************/ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentExt.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentExt.h new file mode 100644 index 00000000..e115f3c5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentExt.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_COMPONENT_EXT_H +#define PL_COMPONENT_EXT_H + +#include "plComponentBase.h" +#include "plComponentTools.h" +#include "../MaxExport/plErrorMsg.h" + +class plMaxNodeBase; + +class plComponentExt : public plComponentBase +{ +public: + int CanConvertToType(Class_ID obtype) + { return (obtype == EXT_COMPONENT_CLASSID) ? 1 : plComponentBase::CanConvertToType(obtype); } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNodeBase *node, plComponentTools *tools, plErrorMsg *pErrMsg) { return true; } + virtual hsBool PreConvert(plMaxNodeBase *node, plComponentTools *tools, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNodeBase *node, plComponentTools *tools, plErrorMsg *pErrMsg) = 0; + + // DeInit pass--free up any temp memory you might have allocated here + virtual hsBool DeInit(plMaxNodeBase *node, plComponentTools *tools, plErrorMsg *pErrMsg) { return true; } +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentMgr.cpp new file mode 100644 index 00000000..a89276e7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentMgr.cpp @@ -0,0 +1,119 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "iparamb2.h" + +#include + +#include "plComponent.h" +#include "plComponentMgr.h" +#include "plComponentReg.h" + +///////// +class ComponentMgrClassDesc : public ClassDesc +{ +public: + int IsPublic() { return FALSE; } + void* Create(BOOL loading) { return &plComponentMgr::Inst(); } + const TCHAR* ClassName() { return _T("Component Mgr"); } + SClass_ID SuperClassID() { return UTILITY_CLASS_ID; } + Class_ID ClassID() { return COMPONENT_MGR_CID; } + const TCHAR* Category() { return _T(""); } +}; + +static ComponentMgrClassDesc theComponentMgrCD; +ClassDesc* GetComponentMgrDesc() { return &theComponentMgrCD; } + +///////////// + +plComponentMgr &plComponentMgr::Inst() +{ + static plComponentMgr theInstance; + return theInstance; +} + +UInt32 plComponentMgr::Count() +{ + return fDescs.size(); +} + +ClassDesc *plComponentMgr::Get(UInt32 i) +{ + if (i < fDescs.size()) + return fDescs[i]; + else + { + hsAssert(0, "Component index out of range"); + return nil; + } +} + +UInt32 plComponentMgr::FindClassID(Class_ID id) +{ + for (unsigned int i = 0; i < fDescs.size(); i++) + { + if (fDescs[i]->ClassID() == id) + return i; + } + + return -1; +} + +int IDescCompare(ClassDesc *desc1, ClassDesc *desc2); + +void plComponentMgr::Register(ClassDesc *desc) +{ + // Organize desc's by category and name + for (unsigned int i = 0; i < fDescs.size(); i++) + { + if (IDescCompare(desc, fDescs[i]) < 0) + { + fDescs.insert(&fDescs[i], desc); + return; + } + } + + fDescs.push_back(desc); +} + +int IDescCompare(ClassDesc *desc1, ClassDesc *desc2) +{ + const char *cat1 = desc1->Category() ? desc1->Category() : ""; + const char *cat2 = desc2->Category() ? desc2->Category() : ""; + int cmp = strcmp(cat1, cat2); + + // Only compare name if categories are the same + if (cmp == 0) + { + const char *name1 = desc1->ClassName() ? desc1->ClassName() : ""; + const char *name2 = desc2->ClassName() ? desc2->ClassName() : ""; + + return strcmp(name1, name2); + } + + return cmp; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentMgr.h new file mode 100644 index 00000000..d586268a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentMgr.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_COMPONENTMGR_H +#define PL_COMPONENTMGR_H + +#include "hsTypes.h" +#include "max.h" +#include "utilapi.h" + +#include +using namespace std; + +class ClassDesc2; +class Class_ID; + +#define COMPONENT_MGR_CID Class_ID(0x5b870ba2, 0xb7b1da2) + +// +// Manages the ClassDesc's for all components. Each component calls a macro that +// registers it with the component mgr. Then when Max loads the plugin the plugin +// enumeration functions call this to get all the registered ones. This saves us +// from having to go change those functions every time we add a new component. +// +// 8/28/01: Added globals -Colin +// +class hsResMgr; +class plFactory; +class plTimerCallbackManager; +class plTimerShare; + +class plComponentMgr : public UtilityObj +{ +private: + vector fDescs; + + plComponentMgr() {}; + +public: + static plComponentMgr &Inst(); + + // Required Max functions + virtual void BeginEditParams(Interface *ip,IUtil *iu) {} + virtual void EndEditParams(Interface *ip,IUtil *iu) {} + virtual void SelectionSetChanged(Interface *ip,IUtil *iu) {} + virtual void DeleteThis() {} + + virtual UInt32 Count(); + virtual ClassDesc *Get(UInt32 i); + + virtual UInt32 FindClassID(Class_ID id); + + // Registers a component. Only used by the classdesc macro. + virtual void Register(ClassDesc *desc); +}; + +#endif //PL_COMPONENTMGR_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentProcBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentProcBase.h new file mode 100644 index 00000000..f8b9020f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentProcBase.h @@ -0,0 +1,122 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __PLCOMPONENT_PROC_BASE_H__ +#define __PLCOMPONENT_PROC_BASE_H__ + +#pragma warning(disable: 4786) + +#include "Max.h" +#include "iparamm2.h" +#include +#include + +// +// Const Char* [] Version +// +class plBaseComponentProc : public ParamMap2UserDlgProc +{ +protected: + void ILoadComboBox(HWND hComboBox, const char *names[]) + { + SendMessage(hComboBox, CB_RESETCONTENT, 0, 0); + for (int i = 0; names[i]; i++) + SendMessage(hComboBox, CB_ADDSTRING, 0, (LPARAM)names[i]); + SendMessage(hComboBox, CB_SETCURSEL, 0, 0); + } +}; + +class plLCBoxComponentProc : public plBaseComponentProc +{ +protected: + void ILoadListBox(HWND hListBox, IParamBlock2 *pb, int param, const char *names[]) + { + SendMessage(hListBox, LB_RESETCONTENT, 0, 0); + for (int i = 0; i < pb->Count(param); i++) + { + int idx = pb->GetInt(param, 0, i); + SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)names[idx]); + } + } +}; + +// +// VChar Version (Vector) +// + + +typedef std::vector VStringArray; + +class plVSBaseComponentProc : public ParamMap2UserDlgProc +{ +protected: + void ILoadComboBox(HWND hComboBox, VStringArray &names) + { + SendMessage(hComboBox, CB_RESETCONTENT, 0, 0); + + int nNames = 0; + + if(names.empty()) + return; + else + nNames = names.size(); + + if(nNames) + for (int i = 0; i < nNames ; i++) + { + const char * name = names[i].c_str(); + SendMessage(hComboBox, CB_INSERTSTRING, i, (LPARAM)name); + + } + + SendMessage(hComboBox, CB_SETCURSEL, 0, 0); + + + } + + void ILoadListBox(HWND hListBox, VStringArray &names, int* GetItemVals, int NumVals) + { + + SendMessage(hListBox, LB_RESETCONTENT, 0, 0); + for (int i = 0; i < names.size(); i++) + { + const char* c_name = names[i].c_str(); + int idxptr = SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)c_name); + + SendMessage(hListBox, LB_SETITEMDATA, (WPARAM) idxptr, (LPARAM) GetItemVals[i]); + + } + } + + + + +}; + + + + + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentReg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentReg.h new file mode 100644 index 00000000..ca7388ce --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentReg.h @@ -0,0 +1,143 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_COMPONENT_REG_H +#define PL_COMPONENT_REG_H + +#include "plComponentMgr.h" + +class TargetValidator; +extern TargetValidator gTargetValidator; +enum { kTargs }; + +class plComponentClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return 0; } + SClass_ID SuperClassID() { return HELPER_CLASS_ID; } + HINSTANCE HInstance() { return hInstance; } + BOOL NeedsToSave() { return TRUE; } + + virtual bool IsAutoUI() { return false; } + virtual bool IsObsolete() { return false; } +}; + +// +// This creates the class description for a component. +// +// classname - Name of a plComponent derived class (not a string) +// varname - Name for a static instance of this desc that will be declared +// longname - Public name for the modifier +// shortname - Internal name for the modifier. No spaces. Will be the prefix +// for the Max name of components of this type. +// category - Category this component will show up in in the helper panel. This +// is just a string, but the COMP_TYPE #defines below should be used +// to promote sharing +// id - Max ClassID for the component +// +// The ClassID will automatically be registered with the description manager. +// +#define CLASS_DESC(classname, varname, longname, shortname, category, id) \ +class classname##ClassDesc : public plComponentClassDesc \ +{ \ + FUNC_CLASS_DESC(classname, longname, shortname, category, id) \ + CONSTRUCT_DESC(classname) \ +}; \ +DECLARE_CLASS_DESC(classname, varname) + +#define OBSOLETE_CLASS_DESC(classname, varname, longname, shortname, category, id) \ +class classname##ClassDesc : public plComponentClassDesc \ +{ \ + FUNC_CLASS_DESC(classname, longname, shortname, category, id) \ + CONSTRUCT_DESC(classname) \ + virtual bool IsObsolete() { return true; } \ +}; \ +DECLARE_CLASS_DESC(classname, varname) + +#define FUNC_CLASS_DESC(classname, longname, shortname, category, id) \ +public: \ + void* Create(BOOL loading) { return new classname##; } \ + const TCHAR* ClassName() { return _T(longname); } \ + Class_ID ClassID() { return id; } \ + const TCHAR* Category() { return _T(category); } \ + const TCHAR* InternalName() { return _T(shortname); } + +#define CONSTRUCT_DESC(classname) \ + classname##ClassDesc() { plComponentMgr::Inst().Register(this); } + +#define DECLARE_CLASS_DESC(classname, varname) \ +static classname##ClassDesc varname; \ +TARG_BLOCK(classname, varname) + +// +// OBSOLETE_CLASS macro +// Usually, when you're making a component obsolete, you don't want to bother with any +// class defs or paramBlock defs anymore, you just want them to all be empty. This +// macro simplifies that process greatly: just delete everything about your class +// EXCEPT the CLASS_DESC line, then change CLASS_DESC to OBSOLETE_CLASS. This macro +// will handle the rest for you! -mcn +// +#define OBSOLETE_CLASS( classname, descname, strname, sn, type, classid ) class classname : public plComponent { public: classname##(); hsBool Convert( plMaxNode *n, plErrorMsg *e ) { return true; } }; \ + OBSOLETE_CLASS_DESC( classname, descname, strname, sn, type, classid ) \ + classname##::##classname() { fClassDesc = &##descname##; fClassDesc->MakeAutoParamBlocks(this); } \ + ParamBlockDesc2 classname##blk ( plComponent::kBlkComp, _T("##descname##"), 0, &##descname##, P_AUTO_CONSTRUCT, plComponent::kRefComp, end ); + +// +// Creates the targets paramblock for a component +// +#define TARG_BLOCK(classname, varname) \ +static ParamBlockDesc2 g##classname##TargsBlock \ +( \ + plComponentBase::kBlkTargs, _T("targets"), 0, &varname, \ + P_AUTO_CONSTRUCT, plComponentBase::kRefTargs, \ + \ + kTargs, _T("targets"), TYPE_INODE_TAB, 0, 0, 0, \ + end, \ + end \ +); + +// Component categories +#define COMP_TYPE_MISC "Misc" +#define COMP_TYPE_TYPE "Type" +#define COMP_TYPE_LOGIC "Logic" +#define COMP_TYPE_PHYSICAL "Physics" +#define COMP_TYPE_GRAPHICS "Render" +#define COMP_TYPE_WATER "Water" +#define COMP_TYPE_FOOTPRINT "FootPrint" +#define COMP_TYPE_SHADOW "Shadow" +#define COMP_TYPE_DISTRIBUTOR "Distributor" +#define COMP_TYPE_AUDIO "Audio" +#define COMP_TYPE_PARTICLE "Particle System" +#define COMP_TYPE_AVATAR "Avatar" +#define COMP_TYPE_DETECTOR "Detector" +#define COMP_TYPE_VOLUME "Region" +#define COMP_TYPE_IGNORE "Ignore" +#define COMP_TYPE_PHYS_CONSTRAINTS "Physic Constraints" +#define COMP_TYPE_PHYS_TERRAINS "Navigables" +#define COMP_TYPE_GUI "GUI" +#define COMP_TYPE_CAMERA "Camera" +#define COMP_TYPE_SHADERS "Vertex/Pixel Shaders" + +#endif //PL_COMPONENT_REG_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentTools.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentTools.cpp new file mode 100644 index 00000000..5136378b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentTools.cpp @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "../pnModifier/plModifier.h" + +#include "plComponentTools.h" + +plKey plComponentTools::AddModifier(plMaxNodeBase *node, plModifier *mod) +{ + return fAddModFunc(node, mod); +} + +plKey plComponentTools::GetNewKey(const char *name, plModifier *mod, plLocation loc) +{ + return fNewKey(name, mod, loc); +} + +void plComponentTools::SetActivatorKey(plMaxNodeBase *activatorNode, plMaxNodeBase *responderNode, plMaxNodeBase *convertNode, plResponderModifier *responderLogic) +{ + fActivator(activatorNode, responderNode, convertNode, responderLogic); +} + +plKey plComponentTools::GetAnimCompModKey(plComponentBase *comp, plMaxNodeBase *node) +{ + return fAnimKey(comp, node); +} +/* +plKey plComponentTools::GetAnimCompLightModKey(plComponentBase *comp, plMaxNodeBase *node) +{ + return fAnimLightKey(comp, node); +} +*/ +const char *plComponentTools::GetAnimCompAnimName(plComponentBase *comp) +{ + return fAnimName(comp); +} + +int plComponentTools::GetMaterialAnimModKey(Mtl* mtl, plMaxNodeBase* node, const char *segName, hsTArray& keys) +{ + return fMatMod(mtl, node, segName, keys); +} + +int plComponentTools::GetSoundNameAndIndex(plComponentBase* comp, plMaxNodeBase* node, const char*& name) +{ + return fSndNameAndIdx(comp, node, name); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentTools.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentTools.h new file mode 100644 index 00000000..78994099 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plComponentTools.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_COMPONENT_TOOLS_H +#define PL_COMPONENT_TOOLS_H + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" + +class INode; +class plModifier; +class plKey; +class plLocation; +class plResponderModifier; +class plComponentBase; +class plMaxNodeBase; + +typedef plKey (*PAddModFunc) (plMaxNodeBase *, plModifier *); +typedef plKey (*PGetNewKeyFunc) (const char*, plModifier*, plLocation); +typedef void (*PSetActivatorKeyFunc) (plMaxNodeBase*, plMaxNodeBase*, plMaxNodeBase*, plResponderModifier*); +typedef plKey (*PGetAnimModKeyFunc) (plComponentBase*, plMaxNodeBase*); +typedef const char* (*PGetAnimNameFunc) (plComponentBase*); +typedef int (*PGetMaterialAnimModKeyFunc) (Mtl* mtl, plMaxNodeBase* node, const char *segName, hsTArray& keys); +typedef int (*PGetSoundNameAndIndex) (plComponentBase*, plMaxNodeBase* node, const char*& name); + +// +// A "toolbox" for external components to do their conversion with. The idea +// is to give components the functions they need without pulling in every source +// file. +// +class plComponentTools +{ +protected: + PAddModFunc fAddModFunc; + PGetNewKeyFunc fNewKey; + PSetActivatorKeyFunc fActivator; + PGetAnimModKeyFunc fAnimKey; +// PGetAnimModKeyFunc fAnimLightKey; + PGetAnimNameFunc fAnimName; + PGetMaterialAnimModKeyFunc fMatMod; + PGetSoundNameAndIndex fSndNameAndIdx; + + plComponentTools() {} + +public: + plComponentTools(PAddModFunc addMod, + PGetNewKeyFunc NewKey, + PSetActivatorKeyFunc activator, + PGetAnimModKeyFunc animKey, +// PGetAnimModKeyFunc animLightKey, + PGetAnimNameFunc animName, + PGetMaterialAnimModKeyFunc matMod, + PGetSoundNameAndIndex sndNameAndIdx) + : fAddModFunc(addMod), + fNewKey(NewKey), + fActivator(activator), + fAnimKey(animKey), +// fAnimLightKey(animLightKey), + fAnimName(animName), + fMatMod(matMod), + fSndNameAndIdx(sndNameAndIdx) + {} + + plKey AddModifier(plMaxNodeBase *node, plModifier *mod); + plKey GetNewKey(const char *name, plModifier *mod, plLocation loc); + void SetActivatorKey(plMaxNodeBase *activatorNode, plMaxNodeBase *responderNode, plMaxNodeBase *convertNode, plResponderModifier *responderLogic); + + const char *GetAnimCompAnimName(plComponentBase *comp); + plKey GetAnimCompModKey(plComponentBase *comp, plMaxNodeBase *node); +// plKey GetAnimCompLightModKey(plComponentBase *comp, plMaxNodeBase *node); + + int GetMaterialAnimModKey(Mtl* mtl, plMaxNodeBase* node, const char *segName, hsTArray& keys); + + int GetSoundNameAndIndex(plComponentBase* comp, plMaxNodeBase* node, const char*& name); +}; + +#endif //PL_COMPONENT_TOOLS_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDicer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDicer.cpp new file mode 100644 index 00000000..289de9b6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDicer.cpp @@ -0,0 +1,259 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "meshdlib.h" + +#include "plDicer.h" + +static const int kDefFaces = 200; +static const float kDefSize = 1000.f; + +plDicer::plDicer() +: fMaxSize(kDefSize, kDefSize, kDefSize), + fMaxFaces(kDefFaces) +{ + +} + +plDicer::~plDicer() +{ +} + +void plDicer::SetMaxSize(const Point3& size) +{ + fMaxSize = size; +} + +Point3 plDicer::GetMaxSize() const +{ + return fMaxSize; +} + +void plDicer::SetMaxFaces(int n) +{ + fMaxFaces = n; +} + +int plDicer::GetMaxFaces() const +{ + return fMaxFaces; +} + +BOOL plDicer::Dice(INode* node, INodeTab& out) +{ + Object *obj = node->EvalWorldState(TimeValue(0)).obj; + if( !obj ) + { + out.Append(1, &node); + return false; + } + + if( !obj->CanConvertToType( triObjectClassID ) ) + { + out.Append(1, &node); + return false; + } + + // Convert to triMesh object + TriObject *meshObj = (TriObject*)obj->ConvertToType(TimeValue(0), triObjectClassID); + if( !meshObj ) + { + out.Append(1, &node); + return false; + } + + TriObject* newObj = CreateNewTriObject(); + newObj->mesh = meshObj->mesh; + + plTriObjectTab triList; + if( !IDiceIter(newObj, triList) ) + { + delete newObj; + out.Append(1, &node); + return false; + } + + IMakeIntoNodes(node, triList, out); + + node->Delete(TimeValue(0), true); + + return true; +} + +BOOL plDicer::IDetach(TriObject* triObj, BitArray& faces, plTriObjectTab& triList) +{ + TriObject* newObj = CreateNewTriObject(); + Mesh* newMesh = &newObj->mesh; + + MeshDelta meshDelta(triObj->mesh); + + // meshDelta.Detach(Mesh & m, Mesh *out, BitArray fset, BOOL faces, BOOL del, BOOL elem); + meshDelta.Detach(triObj->mesh, newMesh, faces, true, true, false); + + meshDelta.Apply(triObj->mesh); + + if( newObj->mesh.getNumFaces() ) + triList.Append(1, &newObj); + else + delete newObj; + + if( triObj->mesh.getNumFaces() ) + { + newObj = CreateNewTriObject(); + newObj->mesh = triObj->mesh; + triList.Append(1, &newObj); + } + + delete triObj; + + return true; +} + +BOOL plDicer::IHalf(TriObject* triObj, plTriObjectTab& triList) +{ + Mesh& mesh = triObj->mesh; + BitArray faces(mesh.getNumFaces()); + + int iAxis = 0; + float maxDim = mesh.getBoundingBox().Width()[0]; + if( mesh.getBoundingBox().Width()[1] > maxDim ) + { + maxDim = mesh.getBoundingBox().Width()[1]; + iAxis = 1; + } + if( mesh.getBoundingBox().Width()[2] > maxDim ) + { + maxDim = mesh.getBoundingBox().Width()[2]; + iAxis = 2; + } + float middle = mesh.getBoundingBox().Center()[iAxis]; + + int numHi = 0; + int i; + for( i = 0; i < mesh.getNumFaces(); i++ ) + { + Point3 p[3]; + p[0] = mesh.getVert(mesh.faces[i].getVert(0)); + p[1] = mesh.getVert(mesh.faces[i].getVert(1)); + p[2] = mesh.getVert(mesh.faces[i].getVert(2)); + + if( (p[0][iAxis] > middle) + &&(p[1][iAxis] > middle) + &&(p[2][iAxis] > middle) ) + { + numHi++; + faces.Set(i); + } + } + if( !numHi || (numHi == mesh.getNumFaces()) ) + return false; + + return IDetach(triObj, faces, triList); +} + +BOOL plDicer::IDice(TriObject* triObj, plTriObjectTab& triList) +{ + int oneBigger = -1; + int oneSmaller = -1; + BOOL doChop = false; + // First, does he need chopping? + Mesh& mesh = triObj->mesh; + if( mesh.getNumFaces() > GetMaxFaces() ) + { + doChop = true; + } + else + { + Box3 bnd = mesh.getBoundingBox(); + Point3 wid = bnd.Width(); + + if( wid.x > GetMaxSize().x ) + doChop = true; + if( wid.y > GetMaxSize().y ) + doChop = true; + if( wid.z > GetMaxSize().z ) + doChop = true; + } + + if( !doChop ) + return false; + + // Okay, we got to chop. + return IHalf(triObj, triList); +} + +BOOL plDicer::IDiceIter(TriObject* triObj, plTriObjectTab& triList) +{ + triList.ZeroCount(); + + plTriObjectTab inList; + plTriObjectTab cutList; + + inList.Append(1, &triObj); + + while( inList.Count() ) + { + int i; + for( i = 0; i < inList.Count(); i++ ) + { + if( !IDice(inList[i], cutList) ) + { + triList.Append(1, &inList[i]); + } + } + inList = cutList; + cutList.ZeroCount(); + } + + return triList.Count() > 0; +} + +BOOL plDicer::IMakeIntoNodes(INode* node, plTriObjectTab& triList, INodeTab& out) +{ + NameMaker *nn = GetCOREInterface()->NewNameMaker(); + TSTR nodeName(node->GetName()); + + int i; + for( i = 0; i < triList.Count(); i++ ) + { + INode* outNode = GetCOREInterface()->CreateObjectNode(triList[i]); + + outNode->SetNodeTM(TimeValue(0), node->GetNodeTM(TimeValue(0))); + outNode->CopyProperties(node); + outNode->SetMtl(node->GetMtl()); + outNode->SetObjOffsetPos(node->GetObjOffsetPos()); + outNode->SetObjOffsetRot(node->GetObjOffsetRot()); + outNode->SetObjOffsetScale(node->GetObjOffsetScale()); + + nn->MakeUniqueName(nodeName); + outNode->SetName(nodeName); + + out.Append(1, &outNode); + } + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDicer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDicer.h new file mode 100644 index 00000000..75dc01f4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDicer.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDicer_inc +#define plDicer_inc + +class Point3; + +class plTriObjectTab : public Tab +{ +}; + + +class plDicer +{ +protected: + + int fMaxFaces; + Point3 fMaxSize; + + BOOL IDetach(TriObject* triObj, BitArray& faces, plTriObjectTab& meshList); + BOOL IHalf(TriObject* triObj, plTriObjectTab& triList); + + BOOL IDice(TriObject* triObj, plTriObjectTab& triList); + + BOOL IDiceIter(TriObject* triObj, plTriObjectTab& triList); + + BOOL IMakeIntoNodes(INode* node, plTriObjectTab& triList, INodeTab& out); + +public: + plDicer(); + virtual ~plDicer(); + + BOOL Dice(INode* node, INodeTab& out); + + void SetMaxSize(const Point3& size); + Point3 GetMaxSize() const; + + void SetMaxFaces(int n); + int GetMaxFaces() const; +}; + +#endif // plDicer_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent.cpp new file mode 100644 index 00000000..07e89318 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent.cpp @@ -0,0 +1,1266 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "hsWindows.h" +#include + +#include "max.h" +#include "meshdlib.h" +#include "stdmat.h" +#include "bmmlib.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxExport/plExportProgressBar.h" +#include "../MaxExport/plExportErrorMsg.h" + +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" + +#include "hsTypes.h" + +#include "../MaxConvert/plDistributor.h" +#include "../MaxConvert/plDistTree.h" + +#include "plDistribComponent.h" + +void DummyCodeIncludeFuncDistrib() +{ + extern void DummyCodeIncludeFuncDistrib_old(); + DummyCodeIncludeFuncDistrib_old(); +} + +struct plIsoTypeStringValPair +{ + plDistributor::IsoType fValue; + const char* fString; +}; + +static const int kNumSeparations = 4; +static plIsoTypeStringValPair kSeparationStrings[kNumSeparations] = +{ + { plDistributor::kIsoNone, "None" }, + { plDistributor::kIsoLow, "Low" }, + { plDistributor::kIsoMedium, "Medium" }, + { plDistributor::kIsoHigh, "High" } +}; + +struct plConformTypeStringValPair +{ + plDistributor::ConformType fValue; + const char* fString; +}; + +static const int kNumConforms = 5; +static plConformTypeStringValPair kConformStrings[kNumConforms] = +{ + { plDistributor::kConformNone, "None" }, + { plDistributor::kConformCheck, "Check" }, + { plDistributor::kConformAll, "All Verts" }, + { plDistributor::kConformHeight, "Bottom" }, + { plDistributor::kConformBase, "Base" } +}; + +struct plColorChanStringValPair +{ + plDistributor::ColorChan fValue; + const char* fString; +}; + +static const int kNumColorChanOptions = 13; +static plColorChanStringValPair kProbColorChanStrings[kNumColorChanOptions] = +{ + { plDistributor::kRed, "Red" }, + { plDistributor::kGreen, "Green" }, + { plDistributor::kBlue, "Blue" }, + { plDistributor::kAlpha, "Alpha" }, + { plDistributor::kAverageRedGreen, "Avg(R,G)" }, + { plDistributor::kAverageRedGreenTimesAlpha, "Avg(R,G)xA" }, + { plDistributor::kAverage, "Avg(R,G,B)" }, + { plDistributor::kAverageTimesAlpha, "Avg(R,G,B)xA" }, + { plDistributor::kMax, "Max(R,G,B)" }, + { plDistributor::kMaxColor, "Max(R,G,B)" }, + { plDistributor::kMaxColorTimesAlpha, "Max(R,G,B)xA" }, + { plDistributor::kMaxRedGreen, "Max(R,G)" }, + { plDistributor::kMaxRedGreenTimesAlpha, "Max(R,G)xA" } +}; + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +#define WM_ROLLOUT_OPEN WM_USER+1 + +class plRollupMgrProc : public ParamMap2UserDlgProc +{ +public: + plRollupMgrProc(int iRoll) : fRollup(iRoll), fGotState(false) {} +protected: + int fIdx; + BOOL fGotState; + int fRollup; + + BOOL IHandleRollupState(IParamMap2* map, HWND hWnd, UINT msg) + { + return false; + + char buff[256]; + IRollupWindow *rollup = GetCOREInterface()->GetCommandPanelRollup(); + sprintf(buff, "%d\t%x\t%x\n", fRollup, rollup->GetPanelIndex(hWnd), msg); + hsStatusMessage(buff); + + if( msg == 0x18 ) + { + + if( msg == WM_CLOSE ) + { + fGotState = false; + } + else + if( !fGotState ) + { + fGotState = ISetRollupState(map, hWnd); + } + else + { + IRecordRollupState(map, hWnd); + } + + } + return FALSE; + } + + void IRecordRollupState(IParamMap2* map, HWND hWnd) + { + IRollupWindow *rollup = GetCOREInterface()->GetCommandPanelRollup(); + int idx = rollup->GetPanelIndex(hWnd); + if( idx >= 0 ) + { + int state = map->GetParamBlock()->GetInt(plDistribComponent::kRollupState); + if( rollup->IsPanelOpen(idx) ) + state |= (1 << fRollup); + else + state &= ~(1 << fRollup); + map->GetParamBlock()->SetValue(plDistribComponent::kRollupState, TimeValue(0), state); + } + + } + BOOL ISetRollupState(IParamMap2* map, HWND hWnd) + { + IRollupWindow *rollup = GetCOREInterface()->GetCommandPanelRollup(); + int idx = rollup->GetPanelIndex(hWnd); + if( idx >= 0 ) + { + int state = map->GetParamBlock()->GetInt(plDistribComponent::kRollupState); + rollup->SetPanelOpen(idx, 0 != (state & (1 << fRollup))); + + return true; + } + return false; + } +}; + +class plDistCompNilProc : public plRollupMgrProc +{ +public: + plDistCompNilProc(int iRoll) : plRollupMgrProc(iRoll) {} + + BOOL DlgProc(TimeValue t, IParamMap2* map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IHandleRollupState(map, hWnd, msg); + return FALSE; + } + void DeleteThis() {} +}; + +class plDistCompActionProc : public plRollupMgrProc +{ +public: + plDistCompActionProc() : plRollupMgrProc(plDistribComponent::kRollAction) {} + + BOOL DlgProc(TimeValue t, IParamMap2* map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IHandleRollupState(map, hWnd, msg); + + HWND cbox = NULL; + switch (msg) + { + case WM_INITDIALOG: + PostMessage(hWnd, WM_ROLLOUT_OPEN, 0, 0); + break; + + case WM_COMMAND: + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_DISTRIB_CLEAR) ) + { + plDistribComponent* dc = (plDistribComponent*)map->GetParamBlock()->GetOwner(); + dc->Clear(); + + return TRUE; + } + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_DISTRIB_PREVIEW) ) + { + plDistribComponent* dc = (plDistribComponent*)map->GetParamBlock()->GetOwner(); + dc->Preview(); + + return TRUE; + } + } + return FALSE; + } + void DeleteThis() {} +}; +static plDistCompActionProc gDistCompActionProc; + +class plDistCompSpaceProc : public plRollupMgrProc +{ +private: +public: + plDistCompSpaceProc() : plRollupMgrProc(plDistribComponent::kRollSpacing) {} + + BOOL DlgProc(TimeValue t, IParamMap2* map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IHandleRollupState(map, hWnd, msg); + + HWND cbox = NULL; + switch (msg) + { + + case WM_INITDIALOG: + PostMessage(hWnd, WM_ROLLOUT_OPEN, 0, 0); + + cbox = GetDlgItem(hWnd, IDC_COMP_DISTRIB_SEPARATION); + int i; + for( i = 0; i < kNumSeparations; i++ ) + { + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)kSeparationStrings[i].fString); + } + SendMessage(cbox, CB_SETCURSEL, map->GetParamBlock()->GetInt(plDistribComponent::kSeparation), 0); + + return TRUE; + case WM_COMMAND: + switch( LOWORD(wParam) ) + { + case IDC_COMP_DISTRIB_SEPARATION: + { + map->GetParamBlock()->SetValue(plDistribComponent::kSeparation, t, SendMessage(GetDlgItem(hWnd, LOWORD(wParam)), CB_GETCURSEL, 0, 0)); + return TRUE; + } + break; + } + break; + } + return FALSE; + } + void DeleteThis() {} +}; +static plDistCompSpaceProc gDistCompSpaceProc; + +class plDistCompConformProc : public plRollupMgrProc +{ +private: +public: + plDistCompConformProc() : plRollupMgrProc(plDistribComponent::kRollConform) {} + BOOL DlgProc(TimeValue t, IParamMap2* map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IHandleRollupState(map, hWnd, msg); + + HWND cbox = NULL; + switch (msg) + { + + case WM_INITDIALOG: + PostMessage(hWnd, WM_ROLLOUT_OPEN, 0, 0); + + cbox = GetDlgItem(hWnd, IDC_COMP_DISTRIB_CONFORMTYPE); + int i; + for( i = 0; i < kNumConforms; i++ ) + { + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)kConformStrings[i].fString); + } + SendMessage(cbox, CB_SETCURSEL, map->GetParamBlock()->GetInt(plDistribComponent::kConformType), 0); + + return TRUE; + case WM_COMMAND: + switch( LOWORD(wParam) ) + { + case IDC_COMP_DISTRIB_CONFORMTYPE: + { + map->GetParamBlock()->SetValue(plDistribComponent::kConformType, t, SendMessage(GetDlgItem(hWnd, LOWORD(wParam)), CB_GETCURSEL, 0, 0)); + return TRUE; + } + break; + } + break; + } + + return FALSE; + } + void DeleteThis() {} +}; +static plDistCompConformProc gDistCompConformProc; + +class plDistCompScaleProc : public plRollupMgrProc +{ +private: + void ISetupScaleLock(IParamMap2* map, BOOL onInit=false) + { + IParamBlock2 *pb = map->GetParamBlock(); + if( pb->GetInt(plDistribComponent::kLockScaleXYZ) ) + { + map->Enable(plDistribComponent::kLockScaleXY, FALSE); + + map->Enable(plDistribComponent::kScaleLoY, FALSE); + map->Enable(plDistribComponent::kScaleHiY, FALSE); + + map->Enable(plDistribComponent::kScaleLoZ, FALSE); + map->Enable(plDistribComponent::kScaleHiZ, FALSE); + } + else if( pb->GetInt(plDistribComponent::kLockScaleXY) ) + { + map->Enable(plDistribComponent::kLockScaleXY, TRUE); + + map->Enable(plDistribComponent::kScaleLoY, FALSE); + map->Enable(plDistribComponent::kScaleHiY, FALSE); + + map->Enable(plDistribComponent::kScaleLoZ, TRUE); + map->Enable(plDistribComponent::kScaleHiZ, TRUE); + } + else + { + map->Enable(plDistribComponent::kLockScaleXY, TRUE); + + map->Enable(plDistribComponent::kScaleLoY, TRUE); + map->Enable(plDistribComponent::kScaleHiY, TRUE); + + map->Enable(plDistribComponent::kScaleLoZ, TRUE); + map->Enable(plDistribComponent::kScaleHiZ, TRUE); + } + if( onInit ) + { + map->SetTooltip(plDistribComponent::kLockScaleXY, TRUE, "Lock scale in X and Y" ); + map->SetTooltip(plDistribComponent::kLockScaleXYZ, TRUE, "Lock scale in X, Y and Z (uniform scale)" ); + } + } +public: + plDistCompScaleProc() : plRollupMgrProc(plDistribComponent::kRollScale) {} + + BOOL DlgProc(TimeValue t, IParamMap2* map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IHandleRollupState(map, hWnd, msg); + + HWND cbox = NULL; + switch (msg) + { + + case WM_INITDIALOG: + PostMessage(hWnd, WM_ROLLOUT_OPEN, 0, 0); + + ISetupScaleLock(map, true); + + return TRUE; + case WM_COMMAND: + switch( LOWORD(wParam) ) + { + case IDC_COMP_DISTRIB_LOCKSCALEXY: + case IDC_COMP_DISTRIB_LOCKSCALEXYZ: + ISetupScaleLock(map, false); + return TRUE; + } + break; + } + + return FALSE; + } + void DeleteThis() {} +}; +static plDistCompScaleProc gDistCompScaleProc; + +class plDistCompBitmapProc : public plRollupMgrProc +{ +private: +public: + plDistCompBitmapProc() : plRollupMgrProc(plDistribComponent::kRollBitmap) {} + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IHandleRollupState(map, hWnd, msg); + + HWND cbox = NULL; + switch (msg) + { + case WM_INITDIALOG: + PostMessage(hWnd, WM_ROLLOUT_OPEN, 0, 0); + + cbox = GetDlgItem(hWnd, IDC_COMP_DISTRIB_PROBCOLORCHAN); + int i; + for( i = 0; i < kNumColorChanOptions; i++ ) + { + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)kProbColorChanStrings[i].fString); + } + SendMessage(cbox, CB_SETCURSEL, map->GetParamBlock()->GetInt(plDistribComponent::kProbColorChan), 0); + return TRUE; + + case WM_COMMAND: + switch( LOWORD(wParam) ) + { + case IDC_COMP_DISTRIB_PROBCOLORCHAN: + { + map->GetParamBlock()->SetValue(plDistribComponent::kProbColorChan, t, SendMessage(GetDlgItem(hWnd, LOWORD(wParam)), CB_GETCURSEL, 0, 0)); + return TRUE; + } + break; + } + break; + } + return FALSE; + } + void DeleteThis() {} +}; +static plDistCompBitmapProc gDistCompBitmapProc; + +class plDistCompFadeProc : public plRollupMgrProc +{ +private: +public: + plDistCompFadeProc() : plRollupMgrProc(plDistribComponent::kRollFade) {} + BOOL DlgProc(TimeValue t, IParamMap2* map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IHandleRollupState(map, hWnd, msg); + + HWND cbox = NULL; + switch (msg) + { + case WM_INITDIALOG: + PostMessage(hWnd, WM_ROLLOUT_OPEN, 0, 0); + break; + + case WM_COMMAND: + switch( LOWORD(wParam) ) + { + case IDC_COMP_DISTRIB_FADEINACTIVE: + case IDC_COMP_DISTRIB_FADEINTRAN: + case IDC_COMP_DISTRIB_FADEINOPAQ: + case IDC_COMP_DISTRIB_FADEOUTTRAN: + case IDC_COMP_DISTRIB_FADEOUTOPAQ: + case IDC_COMP_DISTRIB_FADEINTRAN_SPIN: + case IDC_COMP_DISTRIB_FADEINOPAQ_SPIN: + case IDC_COMP_DISTRIB_FADEOUTTRAN_SPIN: + case IDC_COMP_DISTRIB_FADEOUTOPAQ_SPIN: + { + plDistribComponent* dc = (plDistribComponent*)map->GetParamBlock()->GetOwner(); + Box3 fade; + if( !dc->IValidateFade(fade) ) + { + map->GetParamBlock()->SetValue(plDistribComponent::kFadeInTran, t, fade.Min()[0]); + map->GetParamBlock()->SetValue(plDistribComponent::kFadeInOpaq, t, fade.Min()[1]); + map->GetParamBlock()->SetValue(plDistribComponent::kFadeOutTran, t, fade.Max()[0]); + map->GetParamBlock()->SetValue(plDistribComponent::kFadeOutOpaq, t, fade.Max()[1]); + + map->Invalidate(plDistribComponent::kFadeInTran); + map->Invalidate(plDistribComponent::kFadeInOpaq); + map->Invalidate(plDistribComponent::kFadeOutTran); + map->Invalidate(plDistribComponent::kFadeOutOpaq); + ShowWindow(hWnd, SW_HIDE); + ShowWindow(hWnd, SW_SHOW); + } + return TRUE; + } + break; + } + break; + + } + return FALSE; + } + void DeleteThis() {} +}; +static plDistCompFadeProc gDistCompFadeProc; + + + +//Max desc stuff necessary below. +CLASS_DESC(plDistribComponent, gDistribCompDesc, "Distributor", "Distributor", COMP_TYPE_DISTRIBUTOR, DISTRIBUTOR_COMP_CID) + +class plDistribCompAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if( id == plDistribComponent::kTemplates ) + { + plDistribComponent *comp = (plDistribComponent*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +plDistribCompAccessor gDistribCompAccessor; + +plDistCompNilProc gDistCompTemplatesProc(plDistribComponent::kRollTemplates); +plDistCompNilProc gDistCompOrientProc(plDistribComponent::kRollOrient); +plDistCompNilProc gDistCompAngProbProc(plDistribComponent::kRollAngProb); +plDistCompNilProc gDistCompAltProbProc(plDistribComponent::kRollAltProb); +plDistCompNilProc gDistCompWindProc(plDistribComponent::kRollWind); +plDistCompNilProc gDistCompRandomizeProc(plDistribComponent::kRollRandomize); + +//const int kROLLUP_START = 0; +const int kROLLUP_START = APPENDROLL_CLOSED | DONTAUTOCLOSE; + +ParamBlockDesc2 gDistributorBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("Distributor"), 0, &gDistribCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + // Roll Out the red carpet. + plDistribComponent::kNumRollups, + plDistribComponent::kRollTemplates, IDD_COMP_DIST_TEMPLATES, IDS_COMP_DIST_TEMPLATES, 0, 0, &gDistCompTemplatesProc, + plDistribComponent::kRollSpacing, IDD_COMP_DIST_SPACING, IDS_COMP_DIST_SPACING, 0, kROLLUP_START, &gDistCompSpaceProc, + plDistribComponent::kRollScale, IDD_COMP_DIST_SCALE, IDS_COMP_DIST_SCALE, 0, kROLLUP_START, &gDistCompScaleProc, + plDistribComponent::kRollOrient, IDD_COMP_DIST_ORIENT, IDS_COMP_DIST_ORIENT, 0, kROLLUP_START, &gDistCompOrientProc, + plDistribComponent::kRollAngProb, IDD_COMP_DIST_ANGPROB, IDS_COMP_DIST_ANGPROB, 0, kROLLUP_START, &gDistCompAngProbProc, + plDistribComponent::kRollAltProb, IDD_COMP_DIST_ALTPROB, IDS_COMP_DIST_ALTPROB, 0, kROLLUP_START, &gDistCompAltProbProc, + plDistribComponent::kRollConform, IDD_COMP_DIST_CONFORM, IDS_COMP_DIST_CONFORM, 0, kROLLUP_START, &gDistCompConformProc, + plDistribComponent::kRollBitmap, IDD_COMP_DIST_BITMAP, IDS_COMP_DIST_BITMAP, 0, kROLLUP_START, &gDistCompBitmapProc, + plDistribComponent::kRollFade, IDD_COMP_DIST_FADE, IDS_COMP_DIST_FADE, 0, kROLLUP_START, &gDistCompFadeProc, + plDistribComponent::kRollWind, IDD_COMP_DIST_WIND, IDS_COMP_DIST_WIND, 0, kROLLUP_START, &gDistCompWindProc, + plDistribComponent::kRollRandomize, IDD_COMP_DIST_RANDOMIZE, IDS_COMP_DIST_RANDOMIZE, 0, kROLLUP_START, &gDistCompRandomizeProc, + plDistribComponent::kRollAction, IDD_COMP_DIST_ACTION, IDS_COMP_DIST_ACTION, 0, kROLLUP_START, &gDistCompActionProc, + + + // TEMPLATES + plDistribComponent::kTemplates, _T("Templates"), TYPE_INODE_TAB, 0, P_CAN_CONVERT, 0, + p_ui, plDistribComponent::kRollTemplates, TYPE_NODELISTBOX, IDC_LIST_TARGS, IDC_ADD_TARGS, 0, IDC_DEL_TARGS, + p_classID, triObjectClassID, + p_accessor, &gDistribCompAccessor, + end, + + // SPACING + plDistribComponent::kSpacing, _T("Spacing"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.1, 100.0, + p_ui, plDistribComponent::kRollSpacing, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SPACING, IDC_COMP_DISTRIB_SPACING_SPIN, 1.0, + end, + + plDistribComponent::kSpaceRnd, _T("Space Randomness"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 100.0, + p_ui, plDistribComponent::kRollSpacing, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SPACERND, IDC_COMP_DISTRIB_SPACERND_SPIN, 1.0, + end, + + plDistribComponent::kDensity, _T("Density"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 1., 100.0, + p_ui, plDistribComponent::kRollSpacing, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_DENSITY, IDC_COMP_DISTRIB_DENSITY_SPIN, 1.0, + end, + + plDistribComponent::kSeparation, _T("Separation"), TYPE_INT, 0, 0, + p_range, 0, 3, + p_default, 1, + end, + + // SCALE + plDistribComponent::kScaleLoX, _T("ScaleMinX"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, plDistribComponent::kRollScale, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALELOX, IDC_COMP_DISTRIB_SCALELOX_SPIN, 1.0, + end, + + plDistribComponent::kScaleLoY, _T("ScaleMinY"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, plDistribComponent::kRollScale, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALELOY, IDC_COMP_DISTRIB_SCALELOY_SPIN, 1.0, + end, + + plDistribComponent::kScaleLoZ, _T("ScaleMinZ"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, plDistribComponent::kRollScale, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALELOZ, IDC_COMP_DISTRIB_SCALELOZ_SPIN, 1.0, + end, + + plDistribComponent::kScaleHiX, _T("ScaleMaxX"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, plDistribComponent::kRollScale, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALEHIX, IDC_COMP_DISTRIB_SCALEHIX_SPIN, 1.0, + end, + + plDistribComponent::kScaleHiY, _T("ScaleMaxY"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, plDistribComponent::kRollScale, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALEHIY, IDC_COMP_DISTRIB_SCALEHIY_SPIN, 1.0, + end, + + plDistribComponent::kScaleHiZ, _T("ScaleMaxZ"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, plDistribComponent::kRollScale, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALEHIZ, IDC_COMP_DISTRIB_SCALEHIZ_SPIN, 1.0, + end, + + plDistribComponent::kLockScaleXY, _T("LockScaleXY"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plDistribComponent::kRollScale, TYPE_SINGLECHEKBOX, IDC_COMP_DISTRIB_LOCKSCALEXY, + end, + + plDistribComponent::kLockScaleXYZ, _T("LockScaleXYZ"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plDistribComponent::kRollScale, TYPE_SINGLECHEKBOX, IDC_COMP_DISTRIB_LOCKSCALEXYZ, + end, + + // ORIENT + plDistribComponent::kAlignWgt, _T("Align Weight"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 100.0, + p_ui, plDistribComponent::kRollOrient, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_ALIGNWGT, IDC_COMP_DISTRIB_ALIGNWGT_SPIN, 1.0, + end, + + plDistribComponent::kPolarRange, _T("Normal Range (Deg)"), TYPE_FLOAT, 0, 0, + p_default, 15.0, + p_range, 0.0, 180.0, + p_ui, plDistribComponent::kRollOrient, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_POLARRANGE, IDC_COMP_DISTRIB_POLARRANGE_SPIN, 1.0, + end, + + plDistribComponent::kAzimuthRange, _T("Normal Range (Deg)"), TYPE_FLOAT, 0, 0, + p_default, 180.0, + p_range, 0.0, 180.0, + p_ui, plDistribComponent::kRollOrient, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_AZIMUTHRANGE, IDC_COMP_DISTRIB_AZIMUTHRANGE_SPIN, 1.0, + end, + + plDistribComponent::kPolarBunch, _T("Normal Bunching"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + end, + + // ANGPROB + plDistribComponent::kAngProbHi, _T("AngProbHi"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 180.0, + p_ui, plDistribComponent::kRollAngProb, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_ANGPROBHI, IDC_COMP_DISTRIB_ANGPROBHI_SPIN, 1.0, + end, + + plDistribComponent::kAngProbLo, _T("AngProbLo"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 180.0, + p_ui, plDistribComponent::kRollAngProb, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_ANGPROBLO, IDC_COMP_DISTRIB_ANGPROBLO_SPIN, 1.0, + end, + + plDistribComponent::kAngProbTrans, _T("AngProbTrans"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 180.0, + p_ui, plDistribComponent::kRollAngProb, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_ANGPROBTRANS, IDC_COMP_DISTRIB_ANGPROBTRANS_SPIN, 1.0, + end, + + // ALTPROB + plDistribComponent::kAltProbHi, _T("AltProbHi"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -1000.0, 1000.0, + p_ui, plDistribComponent::kRollAltProb, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_DISTRIB_ALTPROBHI, IDC_COMP_DISTRIB_ALTPROBHI_SPIN, 1.0, + end, + + plDistribComponent::kAltProbLo, _T("AltProbLo"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -1000.0, 1000.0, + p_ui, plDistribComponent::kRollAltProb, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_DISTRIB_ALTPROBLO, IDC_COMP_DISTRIB_ALTPROBLO_SPIN, 1.0, + end, + + plDistribComponent::kAltProbTrans, _T("AltProbTrans"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 1000.0, + p_ui, plDistribComponent::kRollAltProb, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_ALTPROBTRANS, IDC_COMP_DISTRIB_ALTPROBTRANS_SPIN, 1.0, + end, + + // CONFORM + plDistribComponent::kConformType, _T("ConformType"), TYPE_INT, 0, 0, + p_default, 0, + end, + + plDistribComponent::kConformMax, _T("ConformMax"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 1000.0, + p_ui, plDistribComponent::kRollConform, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_CONFORMMAX, IDC_COMP_DISTRIB_CONFORMMAX_SPIN, 1.0, + end, + + plDistribComponent::kOffsetMin, _T("OffsetMin"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -1000.0, 1000.0, + p_ui, plDistribComponent::kRollConform, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_DISTRIB_OFFSETMIN, IDC_COMP_DISTRIB_OFFSETMIN_SPIN, 1.0, + end, + + plDistribComponent::kOffsetMax, _T("OffsetMax"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -1000.0, 1000.0, + p_ui, plDistribComponent::kRollConform, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_DISTRIB_OFFSETMAX, IDC_COMP_DISTRIB_OFFSETMAX_SPIN, 1.0, + end, + + // BITMAP + plDistribComponent::kProbTexmap, _T("ProbTexmap"), TYPE_TEXMAP, 0, 0, + p_ui, plDistribComponent::kRollBitmap, TYPE_TEXMAPBUTTON, IDC_COMP_DISTRIB_PROBTEXMAP, + end, + + plDistribComponent::kProbColorChan, _T("ProbColorChan"), TYPE_INT, 0, 0, + p_default, 0, + end, + + plDistribComponent::kRemapFromLo, _T("RemapFromLo"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 255.0, + p_ui, plDistribComponent::kRollBitmap, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_REMAPFROMLO, IDC_COMP_DISTRIB_REMAPFROMLO_SPIN, 1.0, + end, + + plDistribComponent::kRemapFromHi, _T("RemapFromHi"), TYPE_FLOAT, 0, 0, + p_default, 255.0, + p_range, 0.0, 255.0, + p_ui, plDistribComponent::kRollBitmap, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_REMAPFROMHI, IDC_COMP_DISTRIB_REMAPFROMHI_SPIN, 1.0, + end, + + plDistribComponent::kRemapToLo, _T("RemapToLo"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 255.0, + p_ui, plDistribComponent::kRollBitmap, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_REMAPTOLO, IDC_COMP_DISTRIB_REMAPTOLO_SPIN, 1.0, + end, + + plDistribComponent::kRemapToHi, _T("RemapToHi"), TYPE_FLOAT, 0, 0, + p_default, 255.0, + p_range, 0.0, 255.0, + p_ui, plDistribComponent::kRollBitmap, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_REMAPTOHI, IDC_COMP_DISTRIB_REMAPTOHI_SPIN, 1.0, + end, + + // FADE + plDistribComponent::kFadeInTran, _T("FadeInTran"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 1000.0, + p_ui, plDistribComponent::kRollFade, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_FADEINTRAN, IDC_COMP_DISTRIB_FADEINTRAN_SPIN, 1.0, + end, + + plDistribComponent::kFadeInOpaq, _T("FadeInOpaq"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 1000.0, + p_ui, plDistribComponent::kRollFade, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_FADEINOPAQ, IDC_COMP_DISTRIB_FADEINOPAQ_SPIN, 1.0, + end, + + plDistribComponent::kFadeOutTran, _T("FadeOutTran"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 1000.0, + p_ui, plDistribComponent::kRollFade, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_FADEOUTTRAN, IDC_COMP_DISTRIB_FADEOUTTRAN_SPIN, 1.0, + end, + + plDistribComponent::kFadeOutOpaq, _T("FadeOutOpaq"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 1000.0, + p_ui, plDistribComponent::kRollFade, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_FADEOUTOPAQ, IDC_COMP_DISTRIB_FADEOUTOPAQ_SPIN, 1.0, + end, + + plDistribComponent::kFadeInActive, _T("FadeInActive"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plDistribComponent::kRollFade, TYPE_SINGLECHEKBOX, IDC_COMP_DISTRIB_FADEINACTIVE, + p_enable_ctrls, 2, plDistribComponent::kFadeInTran, plDistribComponent::kFadeInOpaq, + end, + + // WIND + plDistribComponent::kWindBone, _T("WindBone"), TYPE_INODE, 0, 0, + p_ui, plDistribComponent::kRollWind, TYPE_PICKNODEBUTTON, IDC_COMP_DISTRIB_WINDBONE, + p_prompt, IDS_COMP_CLUSTER_CHOSE_WINDBONE, + end, + + plDistribComponent::kWindBoneActive, _T("WindBoneActive"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plDistribComponent::kRollWind, TYPE_SINGLECHEKBOX, IDC_COMP_DISTRIB_WINDBONEACTIVE, + p_enable_ctrls, 1, plDistribComponent::kWindBone, + end, + + // RANDOMIZE + plDistribComponent::kSeedLocked, _T("Locked"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plDistribComponent::kRollRandomize, TYPE_SINGLECHEKBOX, IDC_COMP_DISTRIB_LOCK, + end, + + plDistribComponent::kSeed, _T("Seed"), TYPE_INT, 0, 0, + p_default, 1, + p_ui, plDistribComponent::kRollRandomize, TYPE_SPINNER, EDITTYPE_INT, + IDC_COMP_DISTRIB_SEED, IDC_COMP_DISTRIB_SEED_SPIN, 1.0, + p_range, 0x80000000, 0x7fffffff, + end, + + plDistribComponent::kNextSeed, _T("NextSeed"), TYPE_INT, 0, 0, + p_default, 1, + end, + + // REPLICANTS (non-input) + plDistribComponent::kReplicants, _T("Replicants"), TYPE_INODE_TAB, 0, 0, 0, + p_accessor, &gDistribCompAccessor, + end, + + plDistribComponent::kRollupState, _T("RollupState"), TYPE_INT, 0, 0, + p_default, 0, + end, + + plDistribComponent::kFaceNormals, _T("FaceNormals"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plDistribComponent::kRollOrient, TYPE_SINGLECHEKBOX, IDC_COMP_DISTRIB_FACENORMALS, + end, + + end +); + +plDistribComponent::plDistribComponent() +{ + fClassDesc = &gDistribCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plDistribComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + int numReps = fCompPB->Count(kTemplates); + int i; + for( i = 0; i < numReps; i++ ) + { +#if 0 + plMaxNodeBase* temp = (plMaxNodeBase*)fCompPB->GetINode(kTemplates, TimeValue(0), i); + if( temp ) + temp->SetCanConvert(false); +#else + plMaxNodeBase* temp = (plMaxNodeBase*)fCompPB->GetINode(kTemplates, TimeValue(0), i); + if( temp ) + { + temp->SetDrawable(false); + temp->SetPhysical(false); + temp->SetForceLocal(true); + temp->SetRunTimeLight(true); + temp->SetNoPreShade(true); +// temp->SetAlphaTestHigh(true); + } +#endif + } + + return true; +} + +BOOL plDistribComponent::Distribute(plDistribInstTab& replicants, plErrorMsg* pErrMsg, plExportProgressBar& bar, plDistTree* distTree) +{ + plExportErrorMsg exportErrorMsg; + if( !pErrMsg ) + pErrMsg = &exportErrorMsg; + + BOOL retVal = true; + + GetCOREInterface()->DisableSceneRedraw(); + + Clear(); + + plDistributor distrib; + + distrib.SetTheInterface(GetCOREInterface()); + + // Spacing + distrib.SetSpacing(fCompPB->GetFloat(kSpacing)); + + distrib.SetSpacingRange(fCompPB->GetFloat(kSpaceRnd) * 0.01f * distrib.GetSpacing() * 0.5f); + + distrib.SetOverallProb(fCompPB->GetFloat(kDensity)); + + distrib.SetIsolation(GetIsolation()); + + distrib.SetDistTree(distTree); + + // Scale + Point3 scaleLo(fCompPB->GetFloat(kScaleLoX), fCompPB->GetFloat(kScaleLoY), fCompPB->GetFloat(kScaleLoZ)); + Point3 scaleHi(fCompPB->GetFloat(kScaleHiX), fCompPB->GetFloat(kScaleHiY), fCompPB->GetFloat(kScaleHiZ)); + distrib.SetScaleRange(scaleLo, scaleHi); + ULONG scaleLock = plDistributor::kLockNone; + if( fCompPB->GetInt(kLockScaleXYZ) ) + scaleLock = plDistributor::kLockX | plDistributor::kLockY | plDistributor::kLockZ; + else if( fCompPB->GetInt(kLockScaleXY) ) + scaleLock = plDistributor::kLockX | plDistributor::kLockY; + distrib.SetScaleLock(scaleLock); + + // Orient + // UI offers alignment to surface, distributor wants alignment to UP. + distrib.SetAlignmentWeight(100.f - fCompPB->GetFloat(kAlignWgt)); + + distrib.SetPolarRange(fCompPB->GetFloat(kPolarRange)); + + distrib.SetAzimuthRange(fCompPB->GetFloat(kAzimuthRange)); + + distrib.SetPolarBunch(fCompPB->GetFloat(kPolarBunch)); + + distrib.SetFaceNormals(fCompPB->GetInt(kFaceNormals)); + + // Angle prob + distrib.SetAngleProbHi(fCompPB->GetFloat(kAngProbHi)); + distrib.SetAngleProbLo(fCompPB->GetFloat(kAngProbLo)); + distrib.SetAngleProbTransition(fCompPB->GetFloat(kAngProbTrans)); + + // Altitude prob + float minAlt = fCompPB->GetFloat(kAltProbLo); + float maxAlt = fCompPB->GetFloat(kAltProbHi); + if( maxAlt < minAlt ) + { + float t = maxAlt; + maxAlt = minAlt; + minAlt = t; + } + distrib.SetMinAltitude(minAlt); + distrib.SetMaxAltitude(maxAlt); + distrib.SetAltitudeTransition(fCompPB->GetFloat(kAltProbTrans)); + + // Conformity + distrib.SetConformity(GetConformity()); + distrib.SetMaxConform(fCompPB->GetFloat(kConformMax)); + distrib.SetMinOffset(fCompPB->GetFloat(kOffsetMin)); + distrib.SetMaxOffset(fCompPB->GetFloat(kOffsetMax)); + + // Bitmap + ISetProbTexmap(distrib); + + distrib.SetProbabilityChan(GetProbabilityChan()); + + distrib.SetProbabilityRemapFromLo(fCompPB->GetFloat(kRemapFromLo)); + distrib.SetProbabilityRemapFromHi(fCompPB->GetFloat(kRemapFromHi)); + distrib.SetProbabilityRemapToLo(fCompPB->GetFloat(kRemapToLo)); + distrib.SetProbabilityRemapToHi(fCompPB->GetFloat(kRemapToHi)); + + // Fade + distrib.SetFade(GetFade()); + + // Wind + if( fCompPB->GetInt(kWindBoneActive) ) + distrib.SetBone(fCompPB->GetINode(kWindBone)); + + distrib.SetRigid(!IsFlexible()); + + // Randomization + if( !fCompPB->GetInt(kSeedLocked) ) + fCompPB->SetValue(kSeed, TimeValue(0), fCompPB->GetInt(kNextSeed)); + + distrib.SetRandSeed(fCompPB->GetInt(kSeed)); + + int numReps = fCompPB->Count(kTemplates); + int i; + for( i = 0; i < numReps; i++ ) + { + INode* temp = fCompPB->GetINode(kTemplates, TimeValue(0), i); + if( temp ) + distrib.AddReplicateNode(temp); + } + + try { + pErrMsg->Set(!distrib.GetNumReplicateNodes(), GetINode()->GetName(), "Distributor %s has nothing to replicate", GetINode()->GetName()).CheckAndAsk(); + + int numTarg = NumTargets(); + for( i = 0; i < numTarg; i++ ) + { + if( GetTarget(i) ) + { + if( !distrib.Distribute(GetTarget(i), replicants, fDistCache, bar) ) + { + break; + } + } + } + } + + catch (...) + { + if( pErrMsg->IsBogus() ) + pErrMsg->Show(); + retVal = false; + } + + fCompPB->SetValue(kNextSeed, TimeValue(0), int(distrib.GetRandSeed())); + + BOOL redrawDissed = GetCOREInterface()->IsSceneRedrawDisabled(); + + GetCOREInterface()->EnableSceneRedraw(); + + GetCOREInterface()->ForceCompleteRedraw(FALSE); + + return retVal; +} + +void plDistribComponent::Done() +{ + int i; + for( i = 0; i < fDistCache.Count(); i++ ) + { + delete fDistCache[i].fMesh; + } + fDistCache.ZeroCount(); +} + +BOOL plDistribComponent::IsFlexible() const +{ + int numReps = fCompPB->Count(kTemplates); + int i; + for( i = 0; i < numReps; i++ ) + { + plMaxNode* temp = (plMaxNode*)fCompPB->GetINode(kTemplates, TimeValue(0), i); + if( temp ) + { + if( temp->GetFlexibility()[0] > 0 ) + return true; + } + } + return false; +} + +void plDistribComponent::ISetProbTexmap(plDistributor& distrib) +{ + distrib.SetProbabilityBitmapTex(nil); + + Texmap* tex = fCompPB->GetTexmap(kProbTexmap); + if( tex ) + { + BitmapTex* bmt = GetIBitmapTextInterface(tex); + if( bmt ) + distrib.SetProbabilityBitmapTex(bmt); + else if( tex->ClassID() == LAYER_TEX_CLASS_ID ) + distrib.SetProbabilityLayerTex((plLayerTex*)tex); + } +} + +float plDistribComponent::GetIsoPriority() const +{ + float pri = fCompPB->GetFloat(kSpacing); + pri *= pri; + pri *= fCompPB->GetFloat(kDensity) * 0.01f; + + return pri; +} + +plDistributor::ColorChan plDistribComponent::GetProbabilityChan() const +{ + return kProbColorChanStrings[fCompPB->GetInt(kProbColorChan)].fValue; +} + +plDistributor::ConformType plDistribComponent::GetConformity() const +{ + return kConformStrings[fCompPB->GetInt(kConformType)].fValue; +} + +plDistributor::IsoType plDistribComponent::GetIsolation() const +{ + return kSeparationStrings[fCompPB->GetInt(kSeparation)].fValue; +} + +void plDistribComponent::Clear() +{ + GetCOREInterface()->DisableSceneRedraw(); + // First, clear out any we've already done. + int numReps = fCompPB->Count(kReplicants); + int i; + for( i = 0; i < numReps; i++ ) + { + INode* rep = fCompPB->GetINode(kReplicants, TimeValue(0), i); + if( rep ) + rep->Delete(TimeValue(0), true); + } + fCompPB->ZeroCount(kReplicants); + + GetCOREInterface()->EnableSceneRedraw(); + GetCOREInterface()->ForceCompleteRedraw(FALSE); +} + +void plDistribComponent::Preview() +{ + if( !NumTargets() ) + return; + + GetCOREInterface()->DisableSceneRedraw(); + + plDistribInstTab replicants; + + plExportProgressBar bar; + bar.Start("Preview", NumTargets() << 4); + + plDistTree distTree; + Distribute(replicants, nil, bar, &distTree); + + IMakeOne(replicants); + + Done(); + + GetCOREInterface()->EnableSceneRedraw(); + GetCOREInterface()->ForceCompleteRedraw(FALSE); +} + +INode* plDistribComponent::IMakeOne(plDistribInstTab& nodes) +{ + if( !nodes.Count() ) + return nil; + + int iStartNode = 0; + + NameMaker *nn = GetCOREInterface()->NewNameMaker(); + + while( iStartNode < nodes.Count() ) + { + TriObject* triObj = CreateNewTriObject(); + Mesh* outMesh = &triObj->mesh; + + *outMesh = *nodes[iStartNode].fMesh; + + INode *outNode = GetCOREInterface()->CreateObjectNode(triObj); + + Matrix3 l2w = nodes[0].fObjectTM; + Matrix3 w2l = Inverse(l2w); + + MeshDelta meshDelta(*outMesh); + + int i; + for( i = iStartNode; i < nodes.Count(); i++ ) + { + Mesh* nextMesh = nodes[i].fMesh; + + Matrix3 relativeTransform = nodes[i].fObjectTM * w2l; + + meshDelta.AttachMesh(*outMesh, *nextMesh, relativeTransform, 0); + + meshDelta.Apply(*outMesh); + + const int kFaceCutoff = 1000; + if( outMesh->getNumFaces() > kFaceCutoff ) + break; + } + iStartNode = i; + + outNode->SetNodeTM(TimeValue(0), l2w); + outNode->CopyProperties(nodes[0].fNode); + outNode->SetMtl(nodes[0].fNode->GetMtl()); + outNode->SetObjOffsetPos(Point3(0,0,0)); + Quat identQuat; + identQuat.Identity(); + outNode->SetObjOffsetRot(identQuat); + outNode->SetObjOffsetScale(ScaleValue(Point3(1.f, 1.f, 1.f))); + + TSTR outName(TSTR("Preview")); + nn->MakeUniqueName(outName); + outNode->SetName(outName); + + fCompPB->Append(kReplicants, 1, &outNode); + } + + return nil; +} + +Box3 plDistribComponent::GetFade() +{ + Point3 pmin; + if( fCompPB->GetInt(kFadeInActive) ) + { + pmin.Set(fCompPB->GetFloat(kFadeInTran), fCompPB->GetFloat(kFadeInOpaq), 0); + if( pmin[0] == pmin[1] ) + pmin[2] = 0; + else if( pmin[0] < pmin[1] ) + pmin[2] = -1.f; + else + pmin[2] = 1.f; + } + else + { + pmin.Set(0.f,0.f,0.f); + } + + Point3 pmax; + pmax.Set(fCompPB->GetFloat(kFadeOutTran), fCompPB->GetFloat(kFadeOutOpaq), 0); + if( pmax[0] == pmax[1] ) + pmax[2] = 0; + else if( pmax[0] < pmax[1] ) + pmax[2] = -1.f; + else + pmax[2] = 1.f; + + return Box3(pmin, pmax); +} + +BOOL plDistribComponent::IValidateFade(Box3& fade) +{ + BOOL retVal = true; + fade = GetFade(); + + if( fCompPB->GetInt(kFadeInActive) ) + { + if( fade.Max()[0] < fade.Max()[1] ) + { + if( fade.Min()[0] > fade.Max()[0] ) + { + fade.pmin[0] = fade.Max()[0]; + retVal = false; + } + + if( fade.Min()[1] > fade.Min()[0] ) + { + fade.pmin[1] = fade.Min()[0]; + retVal = false; + } + } + else if( fade.Max()[0] > fade.Max()[1] ) + { + if( fade.Min()[1] > fade.Max()[1] ) + { + fade.pmin[1] = fade.Max()[1]; + retVal = false; + } + + if( fade.Min()[0] > fade.Min()[1] ) + { + fade.pmin[0] = fade.Min()[1]; + retVal = false; + } + } + } + return retVal; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent.h new file mode 100644 index 00000000..72c93e6f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent.h @@ -0,0 +1,209 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDistribComponent_inc +#define plDistribComponent_inc + +const Class_ID DISTRIBUTOR_COMP_CID(0x78143984, 0x3e7c78ec); + +#include "../MaxConvert/plDistributor.h" + +class plMaxNode; +class plDistributor; +class plDistribInstTab; +class plExportProgressBar; +class plDistTree; + +//Class that accesses the paramblock below. +class plDistribComponent : public plComponent +{ +public: + enum + { + kRollTemplates, + kRollSpacing, + kRollScale, + kRollOrient, + kRollAngProb, + kRollAltProb, + kRollConform, + kRollBitmap, + kRollFade, + kRollWind, + kRollRandomize, + kRollAction, + + kNumRollups + }; + enum + { + kTemplates = 0, + + // Spacing + kSpacing, + kSpaceRnd, + kDensity, + kSeparation, + + // Scale + kScaleLoX, + kScaleLoY, + kScaleLoZ, + + kScaleHiX, + kScaleHiY, + kScaleHiZ, + + kLockScaleXY, + kLockScaleXYZ, + + // Orient + kAlignWgt, + kPolarRange, + kAzimuthRange, + + kPolarBunch, + + // Angle prob + kAngProbHi, + kAngProbLo, + kAngProbTrans, + + // Altitude prob + kAltProbHi, + kAltProbLo, + kAltProbTrans, + + // Surface Conformity + kConformType, + kConformMax, + kOffsetMin, + kOffsetMax, + + // Bitmap + kProbTexmap, + kProbColorChan, + + kRemapFromLo, + kRemapFromHi, + kRemapToLo, + kRemapToHi, + + // Fade + kFadeInTran, + kFadeInOpaq, + kFadeOutTran, + kFadeOutOpaq, + kFadeInActive, + + // Wind + kWindBone, + kWindBoneActive, + + // Randomization + kSeedLocked, + kSeed, + kNextSeed, + + // Etc. + kReplicants, + kRollupState, + + kFaceNormals, + + kNumParams + + }; + + plMeshCacheTab fDistCache; + + void ISetProbTexmap(plDistributor& distrib); + INode* IMakeOne(plDistribInstTab& nodes); + + BOOL IValidateFade(Box3& fade); + +public: + plDistribComponent(); + void DeleteThis() { delete this; } + + + BOOL Distribute(plDistribInstTab& reps, plErrorMsg* pErrMsg, plExportProgressBar& bar, plDistTree* dt=nil); + void Done(); + + void Clear(); + void Preview(); + + // See notes below + Box3 GetFade(); + BOOL IsFlexible() const; + float GetIsoPriority() const; + + plDistributor::IsoType GetIsolation() const; + plDistributor::ColorChan GetProbabilityChan() const; + plDistributor::ConformType GetConformity() const; + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + + + // GetFade() notes. + // Fade returned as follows: + // Box3.Min()[0] == fadeInTransparent + // Box3.Min()[1] == fadeInOpaque + // Box3.Max()[0] == fadeOutTransparent + // Box3.Max()[1] == fadeOutOpaque + // + // Box3.Min()[2] == 0 turns off fadein. + // Box3.Max()[2] == 0 turns off fadeout. + // + // In all cases, max(Min()[0],Min()[1]) <= min(Max()[0], Max()[1]) + // + // Also, either Min()[0] <= Min()[1] && Max()[0] >= Max()[1] + // or Min()[0] >= Min()[1] && Max()[0] <= Max()[1] + // that is, we either start transparent, go to opaque and back to transparent, + // or we start opaque, go transparent, and back to opaque. + // Makes sense if you think about it. + // + // If Min()[0] == Min()[1], there is no fade in, we start transparent or opaque + // as determined by Max()[0] and Max()[1]. + // Same for equal Maxs. + // Naturally, Min()[0] == Min()[1] && Max()[0] == Max()[1] turns the whole thing off. + // + // Also, as a convenience, if the transparent distance is less than the opaque distance + // (signifying a fade out), then the Z component will be -1, else if it's off, zero, else 1. + // In Summario: + // if( box.Min()[0] < box.Min()[1] ) // Transparent less than Opaque + // box.Min()[2] = -1.f; + // else if( box.Min()[0] == box.Min()[1] ) // Tran same as Opaque, disabled + // box.Min()[2] = 0; + // else // Leaves Opaque less than Transparent. + // box.Min()[2] = 1.f; + +#endif // plDistribComponent_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent_old.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent_old.cpp new file mode 100644 index 00000000..3da50a72 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent_old.cpp @@ -0,0 +1,875 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "hsWindows.h" +#include + +#include "max.h" +#include "meshdlib.h" +#include "stdmat.h" +#include "bmmlib.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxExport/plExportProgressBar.h" + +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" + +#include "hsTypes.h" + +#include "../MaxConvert/plDistributor.h" +#include "../MaxConvert/plDistTree.h" // FISH HACK - just testing + +#include "plDistribComponent_old.h" + +static const int kNumColorChanOptions = 13; + +struct plProbChanStringValPair +{ + plDistributor::ColorChan fValue; + const char* fString; +}; + +static plProbChanStringValPair kProbColorChanStrings[kNumColorChanOptions] = +{ + { plDistributor::kRed, "Red" }, + { plDistributor::kGreen, "Green" }, + { plDistributor::kBlue, "Blue" }, + { plDistributor::kAlpha, "Alpha" }, + { plDistributor::kAverageRedGreen, "Avg(R,G)" }, + { plDistributor::kAverageRedGreenTimesAlpha, "Avg(R,G)xA" }, + { plDistributor::kAverage, "Avg(R,G,B)" }, + { plDistributor::kAverageTimesAlpha, "Avg(R,G,B)xA" }, + { plDistributor::kMax, "Max(R,G,B)" }, + { plDistributor::kMaxColor, "Max(R,G,B)" }, + { plDistributor::kMaxColorTimesAlpha, "Max(R,G,B)xA" }, + { plDistributor::kMaxRedGreen, "Max(R,G)" }, + { plDistributor::kMaxRedGreenTimesAlpha, "Max(R,G)xA" } +}; + +void DummyCodeIncludeFuncDistrib_old() +{ + +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + +class plDistribComponentProc_old : public ParamMap2UserDlgProc +{ +private: + void ISetupScaleLock(IParamMap2* map, BOOL onInit=false) + { + IParamBlock2 *pb = map->GetParamBlock(); + if( pb->GetInt(plDistribComponent_old::kLockScaleXYZ) ) + { + map->Enable(plDistribComponent_old::kLockScaleXY, FALSE); + + map->Enable(plDistribComponent_old::kScaleLoY, FALSE); + map->Enable(plDistribComponent_old::kScaleHiY, FALSE); + + map->Enable(plDistribComponent_old::kScaleLoZ, FALSE); + map->Enable(plDistribComponent_old::kScaleHiZ, FALSE); + } + else if( pb->GetInt(plDistribComponent_old::kLockScaleXY) ) + { + map->Enable(plDistribComponent_old::kLockScaleXY, TRUE); + + map->Enable(plDistribComponent_old::kScaleLoY, FALSE); + map->Enable(plDistribComponent_old::kScaleHiY, FALSE); + + map->Enable(plDistribComponent_old::kScaleLoZ, TRUE); + map->Enable(plDistribComponent_old::kScaleHiZ, TRUE); + } + else + { + map->Enable(plDistribComponent_old::kLockScaleXY, TRUE); + + map->Enable(plDistribComponent_old::kScaleLoY, TRUE); + map->Enable(plDistribComponent_old::kScaleHiY, TRUE); + + map->Enable(plDistribComponent_old::kScaleLoZ, TRUE); + map->Enable(plDistribComponent_old::kScaleHiZ, TRUE); + } + if( onInit ) + { + map->SetTooltip(plDistribComponent_old::kLockScaleXY, TRUE, "Lock scale in X and Y" ); + map->SetTooltip(plDistribComponent_old::kLockScaleXYZ, TRUE, "Lock scale in X, Y and Z (uniform scale)" ); + } + } +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + HWND cbox = NULL; + switch (msg) + { + + case WM_INITDIALOG: + cbox = GetDlgItem(hWnd, IDC_COMP_DISTRIB_PROBCOLORCHAN); + int i; + for( i = 0; i < kNumColorChanOptions; i++ ) + { + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)kProbColorChanStrings[i].fString); + } + SendMessage(cbox, CB_SETCURSEL, map->GetParamBlock()->GetInt(plDistribComponent_old::kProbColorChan), 0); + + ISetupScaleLock(map, true); + + return TRUE; + case WM_COMMAND: + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_DISTRIB_CLEAR) ) + { + plDistribComponent_old* dc = (plDistribComponent_old*)map->GetParamBlock()->GetOwner(); + dc->Clear(); + + return TRUE; + } + switch( LOWORD(wParam) ) + { + case IDC_COMP_DISTRIB_PROBCOLORCHAN: + { + map->GetParamBlock()->SetValue(plDistribComponent_old::kProbColorChan, t, SendMessage(GetDlgItem(hWnd, LOWORD(wParam)), CB_GETCURSEL, 0, 0)); + return TRUE; + } + break; + + case IDC_COMP_DISTRIB_FADEINACTIVE: + case IDC_COMP_DISTRIB_FADEINTRAN: + case IDC_COMP_DISTRIB_FADEINOPAQ: + case IDC_COMP_DISTRIB_FADEOUTTRAN: + case IDC_COMP_DISTRIB_FADEOUTOPAQ: + case IDC_COMP_DISTRIB_FADEINTRAN_SPIN: + case IDC_COMP_DISTRIB_FADEINOPAQ_SPIN: + case IDC_COMP_DISTRIB_FADEOUTTRAN_SPIN: + case IDC_COMP_DISTRIB_FADEOUTOPAQ_SPIN: + { + plDistribComponent_old* dc = (plDistribComponent_old*)map->GetParamBlock()->GetOwner(); + Box3 fade; + if( !dc->IValidateFade(fade) ) + { + map->GetParamBlock()->SetValue(plDistribComponent_old::kFadeInTran, t, fade.Min()[0]); + map->GetParamBlock()->SetValue(plDistribComponent_old::kFadeInOpaq, t, fade.Min()[1]); + map->GetParamBlock()->SetValue(plDistribComponent_old::kFadeOutTran, t, fade.Max()[0]); + map->GetParamBlock()->SetValue(plDistribComponent_old::kFadeOutOpaq, t, fade.Max()[1]); + + map->Invalidate(plDistribComponent_old::kFadeInTran); + map->Invalidate(plDistribComponent_old::kFadeInOpaq); + map->Invalidate(plDistribComponent_old::kFadeOutTran); + map->Invalidate(plDistribComponent_old::kFadeOutOpaq); + ShowWindow(hWnd, SW_HIDE); + ShowWindow(hWnd, SW_SHOW); + } + return TRUE; + } + break; +#if 0 // Obsolete, now kRndPosRadius is percentage of kSpacing + case IDC_COMP_DISTRIB_RNDPOSRADIUS: + case IDC_COMP_DISTRIB_RNDPOSRADIUS_SPIN: + { + IParamBlock2 *pb = map->GetParamBlock(); + + float maxRndPosRad = pb->GetFloat(plDistribComponent_old::kSpacing) * 0.5f; + if( pb->GetFloat(plDistribComponent_old::kRndPosRadius) > maxRndPosRad ) + { + pb->SetValue(plDistribComponent_old::kRndPosRadius, t, maxRndPosRad); + map->Invalidate(plDistribComponent_old::kRndPosRadius); + ShowWindow(hWnd, SW_HIDE); + ShowWindow(hWnd, SW_SHOW); + } + + return TRUE; + } +#endif // Obsolete, now kRndPosRadius is percentage of kSpacing + case IDC_COMP_DISTRIB_LOCKSCALEXY: + case IDC_COMP_DISTRIB_LOCKSCALEXYZ: + ISetupScaleLock(map, false); + return TRUE; + + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plDistribComponentProc_old gDistribCompProc_old; + + +//Max desc stuff necessary below. +OBSOLETE_CLASS_DESC(plDistribComponent_old, gDistribCompDesc_old, "Distributor(old)", "Distributor(old)", COMP_TYPE_DISTRIBUTOR, DISTRIBUTOR_COMP_CID_OLD) + +class plDistribCompAccessor_old : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if( id == plDistribComponent_old::kTemplates ) + { + plDistribComponent_old* comp = (plDistribComponent_old*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +plDistribCompAccessor_old gDistribCompAccessor_old; + + +ParamBlockDesc2 gDistributorBk_old +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("Distributor_old"), 0, &gDistribCompDesc_old, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_DISTRIBUTOR, IDS_COMP_DISTRIBUTORS, 0, 0, &gDistribCompProc_old, + + plDistribComponent_old::kTemplates, _T("Templates"), TYPE_INODE_TAB, 0, P_CAN_CONVERT, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, IDC_ADD_TARGS, 0, IDC_DEL_TARGS, + p_classID, triObjectClassID, + p_accessor, &gDistribCompAccessor_old, + end, + + plDistribComponent_old::kSpacing, _T("Spacing"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.1, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SPACING, IDC_COMP_DISTRIB_SPACING_SPIN, 1.0, + end, + + plDistribComponent_old::kRndPosRadius, _T("Space Range"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_RNDPOSRADIUS, IDC_COMP_DISTRIB_RNDPOSRADIUS_SPIN, 1.0, + end, + + plDistribComponent_old::kAlignVecX, _T("AlignX"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -1.f, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_DISTRIB_ALIGNVECX, IDC_COMP_DISTRIB_ALIGNVECX_SPIN, 0.1, + end, + + plDistribComponent_old::kAlignVecY, _T("AlignY"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -1.f, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_DISTRIB_ALIGNVECY, IDC_COMP_DISTRIB_ALIGNVECY_SPIN, 0.1, + end, + + plDistribComponent_old::kAlignVecZ, _T("AlignZ"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, -1.f, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_DISTRIB_ALIGNVECZ, IDC_COMP_DISTRIB_ALIGNVECZ_SPIN, 0.1, + end, + + plDistribComponent_old::kAlignWgt, _T("Align Weight"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_ALIGNWGT, IDC_COMP_DISTRIB_ALIGNWGT_SPIN, 1.0, + end, + + plDistribComponent_old::kPolarRange, _T("Normal Range (Deg)"), TYPE_FLOAT, 0, 0, + p_default, 15.0, + p_range, 0.0, 180.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_POLARRANGE, IDC_COMP_DISTRIB_POLARRANGE_SPIN, 1.0, + end, + + plDistribComponent_old::kAzimuthRange, _T("Normal Range (Deg)"), TYPE_FLOAT, 0, 0, + p_default, 180.0, + p_range, 0.0, 180.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_AZIMUTHRANGE, IDC_COMP_DISTRIB_AZIMUTHRANGE_SPIN, 1.0, + end, + + plDistribComponent_old::kOverallProb, _T("Overall Probability"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 1., 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_OVERALLPROB, IDC_COMP_DISTRIB_OVERALLPROB_SPIN, 1.0, + end, + + plDistribComponent_old::kPolarBunch, _T("Normal Bunching"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_POLARBUNCH, IDC_COMP_DISTRIB_POLARBUNCH_SPIN, 1.0, + end, + + plDistribComponent_old::kScaleLoX, _T("ScaleMinX"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALELOX, IDC_COMP_DISTRIB_SCALELOX_SPIN, 1.0, + end, + + plDistribComponent_old::kScaleLoY, _T("ScaleMinY"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALELOY, IDC_COMP_DISTRIB_SCALELOY_SPIN, 1.0, + end, + + plDistribComponent_old::kScaleLoZ, _T("ScaleMinZ"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALELOZ, IDC_COMP_DISTRIB_SCALELOZ_SPIN, 1.0, + end, + + plDistribComponent_old::kScaleHiX, _T("ScaleMaxX"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALEHIX, IDC_COMP_DISTRIB_SCALEHIX_SPIN, 1.0, + end, + + plDistribComponent_old::kScaleHiY, _T("ScaleMaxY"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALEHIY, IDC_COMP_DISTRIB_SCALEHIY_SPIN, 1.0, + end, + + plDistribComponent_old::kScaleHiZ, _T("ScaleMaxZ"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_SCALEHIZ, IDC_COMP_DISTRIB_SCALEHIZ_SPIN, 1.0, + end, + + plDistribComponent_old::kReplicants, _T("Replicants"), TYPE_INODE_TAB, 0, 0, 0, + p_accessor, &gDistribCompAccessor_old, + end, + + plDistribComponent_old::kProbTexmap, _T("ProbTexmap"), TYPE_TEXMAP, 0, 0, + p_ui, TYPE_TEXMAPBUTTON, IDC_COMP_DISTRIB_PROBTEXMAP, + end, + + plDistribComponent_old::kProbColorChan, _T("ProbColorChan"), TYPE_INT, 0, 0, + p_default, 0, + end, + + plDistribComponent_old::kSeedLocked, _T("Locked"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_DISTRIB_LOCK, + end, + + plDistribComponent_old::kSeed, _T("Seed"), TYPE_INT, 0, 0, + p_default, 1, + end, + + plDistribComponent_old::kNextSeed, _T("NextSeed"), TYPE_INT, 0, 0, + p_default, 1, + end, + + plDistribComponent_old::kRemapFromLo, _T("RemapFromLo"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 255.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_REMAPFROMLO, IDC_COMP_DISTRIB_REMAPFROMLO_SPIN, 1.0, + end, + + plDistribComponent_old::kRemapFromHi, _T("RemapFromHi"), TYPE_FLOAT, 0, 0, + p_default, 255.0, + p_range, 0.0, 255.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_REMAPFROMHI, IDC_COMP_DISTRIB_REMAPFROMHI_SPIN, 1.0, + end, + + plDistribComponent_old::kRemapToLo, _T("RemapToLo"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 255.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_REMAPTOLO, IDC_COMP_DISTRIB_REMAPTOLO_SPIN, 1.0, + end, + + plDistribComponent_old::kRemapToHi, _T("RemapToHi"), TYPE_FLOAT, 0, 0, + p_default, 255.0, + p_range, 0.0, 255.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_REMAPTOHI, IDC_COMP_DISTRIB_REMAPTOHI_SPIN, 1.0, + end, + + plDistribComponent_old::kAngProbX, _T("AngProbX"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -1.f, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_DISTRIB_ANGPROBX, IDC_COMP_DISTRIB_ANGPROBX_SPIN, 0.1, + end, + + plDistribComponent_old::kAngProbY, _T("AngProbY"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -1.f, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_DISTRIB_ANGPROBY, IDC_COMP_DISTRIB_ANGPROBY_SPIN, 0.1, + end, + + plDistribComponent_old::kAngProbZ, _T("AngProbZ"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, -1.f, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_DISTRIB_ANGPROBZ, IDC_COMP_DISTRIB_ANGPROBZ_SPIN, 0.1, + end, + + plDistribComponent_old::kAngProbHi, _T("AngProbHi"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 180.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_ANGPROBHI, IDC_COMP_DISTRIB_ANGPROBHI_SPIN, 1.0, + end, + + plDistribComponent_old::kAngProbLo, _T("AngProbLo"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 180.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_ANGPROBLO, IDC_COMP_DISTRIB_ANGPROBLO_SPIN, 1.0, + end, + + plDistribComponent_old::kFadeInTran, _T("FadeInTran"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_FADEINTRAN, IDC_COMP_DISTRIB_FADEINTRAN_SPIN, 1.0, + end, + + plDistribComponent_old::kFadeInOpaq, _T("FadeInOpaq"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_FADEINOPAQ, IDC_COMP_DISTRIB_FADEINOPAQ_SPIN, 1.0, + end, + + plDistribComponent_old::kFadeOutTran, _T("FadeOutTran"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_FADEOUTTRAN, IDC_COMP_DISTRIB_FADEOUTTRAN_SPIN, 1.0, + end, + + plDistribComponent_old::kFadeOutOpaq, _T("FadeOutOpaq"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTRIB_FADEOUTOPAQ, IDC_COMP_DISTRIB_FADEOUTOPAQ_SPIN, 1.0, + end, + + plDistribComponent_old::kFadeInActive, _T("FadeInActive"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_DISTRIB_FADEINACTIVE, + p_enable_ctrls, 2, plDistribComponent_old::kFadeInTran, plDistribComponent_old::kFadeInOpaq, + end, + + plDistribComponent_old::kWindBone, _T("WindBone"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_DISTRIB_WINDBONE, + p_prompt, IDS_COMP_CLUSTER_CHOSE_WINDBONE, + end, + + plDistribComponent_old::kLockScaleXY, _T("LockScaleXY"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_DISTRIB_LOCKSCALEXY, + end, + + plDistribComponent_old::kLockScaleXYZ, _T("LockScaleXYZ"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_DISTRIB_LOCKSCALEXYZ, + end, + + plDistribComponent_old::kWindBoneActive, _T("WindBoneActive"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_DISTRIB_WINDBONEACTIVE, + p_enable_ctrls, 1, plDistribComponent_old::kWindBone, + end, + + // Make this a combo box in real gig. + plDistribComponent_old::kIsolation, _T("Isolation"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_COMP_DISTRIB_ISOLATION, IDC_COMP_DISTRIB_ISOLATION_SPIN, 0.4, +// p_range, 0, 3, +// p_default, 1, + end, + + end +); + +plDistribComponent_old::plDistribComponent_old() +{ + fClassDesc = &gDistribCompDesc_old; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plDistribComponent_old::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + int numReps = fCompPB->Count(kTemplates); + int i; + for( i = 0; i < numReps; i++ ) + { + plMaxNodeBase* temp = (plMaxNodeBase*)fCompPB->GetINode(kTemplates, TimeValue(0), i); + if( temp ) + temp->SetCanConvert(false); + } + + return true; +} + +BOOL plDistribComponent_old::Distribute(plDistribInstTab& replicants, plExportProgressBar& bar, plDistTree* distTree) +{ + return false; + + BOOL retVal = true; + + GetCOREInterface()->DisableSceneRedraw(); + + Clear(); + + plDistributor distrib; + + distrib.SetTheInterface(GetCOREInterface()); + + distrib.SetSpacing(fCompPB->GetFloat(kSpacing)); + + distrib.SetSpacingRange(fCompPB->GetFloat(kRndPosRadius) * 0.01f * distrib.GetSpacing() * 0.5f); + + Point3 align(fCompPB->GetFloat(kAlignVecX), fCompPB->GetFloat(kAlignVecY), fCompPB->GetFloat(kAlignVecZ)); + align.FNormalize(); + distrib.SetAlignmentVec(align); + + distrib.SetAlignmentWeight(fCompPB->GetFloat(kAlignWgt)); + + distrib.SetPolarRange(fCompPB->GetFloat(kPolarRange)); + + distrib.SetAzimuthRange(fCompPB->GetFloat(kAzimuthRange)); + + distrib.SetOverallProb(fCompPB->GetFloat(kOverallProb)); + + distrib.SetPolarBunch(fCompPB->GetFloat(kPolarBunch)); + + Point3 scaleLo(fCompPB->GetFloat(kScaleLoX), fCompPB->GetFloat(kScaleLoY), fCompPB->GetFloat(kScaleLoZ)); + Point3 scaleHi(fCompPB->GetFloat(kScaleHiX), fCompPB->GetFloat(kScaleHiY), fCompPB->GetFloat(kScaleHiZ)); + distrib.SetScaleRange(scaleLo, scaleHi); + ULONG scaleLock = plDistributor::kLockNone; + if( fCompPB->GetInt(kLockScaleXYZ) ) + scaleLock = plDistributor::kLockX | plDistributor::kLockY | plDistributor::kLockZ; + else if( fCompPB->GetInt(kLockScaleXY) ) + scaleLock = plDistributor::kLockX | plDistributor::kLockY; + distrib.SetScaleLock(scaleLock); + + distrib.SetProbabilityChan(kProbColorChanStrings[fCompPB->GetInt(kProbColorChan)].fValue); + + ISetProbTexmap(distrib); + + // Setup the new params here. FISH... + Point3 probVec(fCompPB->GetFloat(kAngProbX), fCompPB->GetFloat(kAngProbY), fCompPB->GetFloat(kAngProbZ)); + distrib.SetAngleProbVec(probVec); + distrib.SetAngleProbHi(fCompPB->GetFloat(kAngProbHi)); + distrib.SetAngleProbLo(fCompPB->GetFloat(kAngProbLo)); + + distrib.SetProbabilityRemapFromLo(fCompPB->GetFloat(kRemapFromLo)); + distrib.SetProbabilityRemapFromHi(fCompPB->GetFloat(kRemapFromHi)); + distrib.SetProbabilityRemapToLo(fCompPB->GetFloat(kRemapToLo)); + distrib.SetProbabilityRemapToHi(fCompPB->GetFloat(kRemapToHi)); + + if( !fCompPB->GetInt(kSeedLocked) ) + fCompPB->SetValue(kSeed, TimeValue(0), fCompPB->GetInt(kNextSeed)); + + distrib.SetRandSeed(fCompPB->GetInt(kSeed)); + + distrib.SetFade(GetFade()); + + if( fCompPB->GetInt(kWindBoneActive) ) + distrib.SetBone(fCompPB->GetINode(kWindBone)); + + distrib.SetRigid(!IsFlexible()); + + // FISH HACK, get this passed in from Cluster + distrib.SetDistTree(distTree); + distrib.SetIsolation(GetIsolation()); + + int numReps = fCompPB->Count(kTemplates); + int i; + for( i = 0; i < numReps; i++ ) + { + INode* temp = fCompPB->GetINode(kTemplates, TimeValue(0), i); + if( temp ) + distrib.AddReplicateNode(temp); + } + + int numTarg = NumTargets(); + for( i = 0; i < numTarg; i++ ) + { + if( GetTarget(i) ) + { + if( !distrib.Distribute(GetTarget(i), replicants, fDistCache, bar) ) + { + retVal = false; + break; + } + } + } + + fCompPB->SetValue(kNextSeed, TimeValue(0), int(distrib.GetRandSeed())); + + BOOL redrawDissed = GetCOREInterface()->IsSceneRedrawDisabled(); + + GetCOREInterface()->EnableSceneRedraw(); + + GetCOREInterface()->ForceCompleteRedraw(FALSE); + + return retVal; +} + +void plDistribComponent_old::Done() +{ + int i; + for( i = 0; i < fDistCache.Count(); i++ ) + { + delete fDistCache[i].fMesh; + } + fDistCache.ZeroCount(); +} + +BOOL plDistribComponent_old::IsFlexible() const +{ + int numReps = fCompPB->Count(kTemplates); + int i; + for( i = 0; i < numReps; i++ ) + { + plMaxNode* temp = (plMaxNode*)fCompPB->GetINode(kTemplates, TimeValue(0), i); + if( temp ) + { + if( temp->GetFlexibility()[0] > 0 ) + return true; + } + } + return false; +} + +void plDistribComponent_old::ISetProbTexmap(plDistributor& distrib) +{ + distrib.SetProbabilityBitmapTex(nil); + + Texmap* tex = fCompPB->GetTexmap(kProbTexmap); + if( tex ) + { + BitmapTex* bmt = GetIBitmapTextInterface(tex); + if( bmt ) + distrib.SetProbabilityBitmapTex(bmt); + else if( tex->ClassID() == LAYER_TEX_CLASS_ID ) + distrib.SetProbabilityLayerTex((plLayerTex*)tex); + } +} + +float plDistribComponent_old::GetIsoPriority() const +{ + float pri = fCompPB->GetFloat(kSpacing); + pri *= pri; + pri *= fCompPB->GetFloat(kOverallProb) * 0.01f; + + return pri; +} + +plDistributor::IsoType plDistribComponent_old::GetIsolation() const +{ + return (plDistributor::IsoType)fCompPB->GetInt(kIsolation); +} + +void plDistribComponent_old::Clear() +{ + GetCOREInterface()->DisableSceneRedraw(); + // First, clear out any we've already done. + int numReps = fCompPB->Count(kReplicants); + int i; + for( i = 0; i < numReps; i++ ) + { + INode* rep = fCompPB->GetINode(kReplicants, TimeValue(0), i); + if( rep ) + rep->Delete(TimeValue(0), true); + } + fCompPB->ZeroCount(kReplicants); + + GetCOREInterface()->EnableSceneRedraw(); + GetCOREInterface()->ForceCompleteRedraw(FALSE); +} + +void plDistribComponent_old::Preview() +{ + if( !NumTargets() ) + return; + + GetCOREInterface()->DisableSceneRedraw(); + + plDistribInstTab replicants; + + plExportProgressBar bar; + bar.Start("Preview", NumTargets() << 4); + + plDistTree distTree; + Distribute(replicants, bar, &distTree); + + IMakeOne(replicants); + + Done(); + + GetCOREInterface()->EnableSceneRedraw(); + GetCOREInterface()->ForceCompleteRedraw(FALSE); +} + +INode* plDistribComponent_old::IMakeOne(plDistribInstTab& nodes) +{ + if( !nodes.Count() ) + return nil; + + int iStartNode = 0; + + NameMaker *nn = GetCOREInterface()->NewNameMaker(); + + while( iStartNode < nodes.Count() ) + { + TriObject* triObj = CreateNewTriObject(); + Mesh* outMesh = &triObj->mesh; + + *outMesh = *nodes[iStartNode].fMesh; + + INode *outNode = GetCOREInterface()->CreateObjectNode(triObj); + + Matrix3 l2w = nodes[0].fObjectTM; + Matrix3 w2l = Inverse(l2w); + + MeshDelta meshDelta(*outMesh); + + int i; + for( i = iStartNode; i < nodes.Count(); i++ ) + { + Mesh* nextMesh = nodes[i].fMesh; + + Matrix3 relativeTransform = nodes[i].fObjectTM * w2l; + + meshDelta.AttachMesh(*outMesh, *nextMesh, relativeTransform, 0); + + meshDelta.Apply(*outMesh); + + const int kFaceCutoff = 1000; + if( outMesh->getNumFaces() > kFaceCutoff ) + break; + } + iStartNode = i; + + outNode->SetNodeTM(TimeValue(0), l2w); + outNode->CopyProperties(nodes[0].fNode); + outNode->SetMtl(nodes[0].fNode->GetMtl()); + outNode->SetObjOffsetPos(Point3(0,0,0)); + Quat identQuat; + identQuat.Identity(); + outNode->SetObjOffsetRot(identQuat); + outNode->SetObjOffsetScale(ScaleValue(Point3(1.f, 1.f, 1.f))); + + TSTR outName(TSTR("Preview")); + nn->MakeUniqueName(outName); + outNode->SetName(outName); + + fCompPB->Append(kReplicants, 1, &outNode); + } + + return nil; +} + +Box3 plDistribComponent_old::GetFade() +{ + Point3 pmin; + if( fCompPB->GetInt(kFadeInActive) ) + { + pmin.Set(fCompPB->GetFloat(kFadeInTran), fCompPB->GetFloat(kFadeInOpaq), 0); + if( pmin[0] == pmin[1] ) + pmin[2] = 0; + else if( pmin[0] < pmin[1] ) + pmin[2] = -1.f; + else + pmin[2] = 1.f; + } + else + { + pmin.Set(0.f,0.f,0.f); + } + + Point3 pmax; + pmax.Set(fCompPB->GetFloat(kFadeOutTran), fCompPB->GetFloat(kFadeOutOpaq), 0); + if( pmax[0] == pmax[1] ) + pmax[2] = 0; + else if( pmax[0] < pmax[1] ) + pmax[2] = -1.f; + else + pmax[2] = 1.f; + + return Box3(pmin, pmax); +} + +BOOL plDistribComponent_old::IValidateFade(Box3& fade) +{ + BOOL retVal = true; + fade = GetFade(); + + if( fCompPB->GetInt(kFadeInActive) ) + { + if( fade.Max()[0] < fade.Max()[1] ) + { + if( fade.Min()[0] > fade.Max()[0] ) + { + fade.pmin[0] = fade.Max()[0]; + retVal = false; + } + + if( fade.Min()[1] > fade.Min()[0] ) + { + fade.pmin[1] = fade.Min()[0]; + retVal = false; + } + } + else if( fade.Max()[0] > fade.Max()[1] ) + { + if( fade.Min()[1] > fade.Max()[1] ) + { + fade.pmin[1] = fade.Max()[1]; + retVal = false; + } + + if( fade.Min()[0] > fade.Min()[1] ) + { + fade.pmin[0] = fade.Min()[1]; + retVal = false; + } + } + } + return retVal; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent_old.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent_old.h new file mode 100644 index 00000000..510e9a9b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plDistribComponent_old.h @@ -0,0 +1,167 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDistribComponent_inc +#define plDistribComponent_inc + +const Class_ID DISTRIBUTOR_COMP_CID_OLD(0x490b247f, 0x56f60a0e); + +#include "../MaxConvert/plDistributor.h" + +class plMaxNode; +class plDistributor; +class plDistribInstTab; +class plExportProgressBar; +class plDistTree; + +//Class that accesses the paramblock below. +class plDistribComponent_old : public plComponent +{ +public: + enum + { + kTemplates = 0, + kSpacing, + kRndPosRadius, + + kAlignVecX, + kAlignVecY, + kAlignVecZ, + + kAlignWgt, + + kPolarRange, + kAzimuthRange, + + kOverallProb, + + kPolarBunch, + + kScaleLoX, + kScaleLoY, + kScaleLoZ, + + kScaleHiX, + kScaleHiY, + kScaleHiZ, + + kReplicants, + + kProbTexmap, + kProbColorChan, + + kSeedLocked, + kSeed, + kNextSeed, + + kRemapFromLo, + kRemapFromHi, + kRemapToLo, + kRemapToHi, + + kAngProbX, + kAngProbY, + kAngProbZ, + + kAngProbHi, + kAngProbLo, + + kFadeInTran, + kFadeInOpaq, + kFadeOutTran, + kFadeOutOpaq, + kFadeInActive, + + kWindBone, + + kLockScaleXY, + kLockScaleXYZ, + + kWindBoneActive, + + kIsolation, + + kNumParams + + }; + + plMeshCacheTab fDistCache; + + void ISetProbTexmap(plDistributor& distrib); + INode* IMakeOne(plDistribInstTab& nodes); + + BOOL IValidateFade(Box3& fade); + +public: + plDistribComponent_old(); + void DeleteThis() { delete this; } + + + BOOL Distribute(plDistribInstTab& reps, plExportProgressBar& bar, plDistTree* dt=nil); + void Done(); + + void Clear(); + void Preview(); + + // See notes below + Box3 GetFade(); + BOOL IsFlexible() const; + float GetIsoPriority() const; + plDistributor::IsoType GetIsolation() const; + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + + + // GetFade() notes. + // Fade returned as follows: + // Box3.Min()[0] == fadeInTransparent + // Box3.Min()[1] == fadeInOpaque + // Box3.Max()[0] == fadeOutTransparent + // Box3.Max()[1] == fadeOutOpaque + // + // Box3.Min()[2] == 0 turns off fadein. + // Box3.Max()[2] == 0 turns off fadeout. + // + // In all cases, max(Min()[0],Min()[1]) <= min(Max()[0], Max()[1]) + // + // Also, either Min()[0] <= Min()[1] && Max()[0] >= Max()[1] + // or Min()[0] >= Min()[1] && Max()[0] <= Max()[1] + // that is, we either start transparent, go to opaque and back to transparent, + // or we start opaque, go transparent, and back to opaque. + // Makes sense if you think about it. + // + // If Min()[0] == Min()[1], there is no fade in, we start transparent or opaque + // as determined by Max()[0] and Max()[1]. + // Same for equal Maxs. + // Naturally, Min()[0] == Min()[1] && Max()[0] == Max()[1] turns the whole thing off. + // + +#endif // plDistribComponent_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plExcludeRegionComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plExcludeRegionComponent.cpp new file mode 100644 index 00000000..7f1ca13a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plExcludeRegionComponent.cpp @@ -0,0 +1,209 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plExcludeRegionComponent.h" +#include "max.h" + +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxMain/plMaxNode.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plModifier/plExcludeRegionModifier.h" +#include "../plPhysical/plSimDefs.h" + +#include "../MaxMain/plPhysicalProps.h" + +void DummyCodeIncludeFuncExcludeRegion() {} + +CLASS_DESC(plExcludeRegionComponent, gExcludeRegionDesc, "Exclude Region", "ExclRegion", COMP_TYPE_MISC, XREGION_CID) + +enum +{ + kXRegionSafePoints, + kXRegionInitiallyCleared, + kXRegionSmartSeek, + kXRegionBlockCameras, +}; + +ParamBlockDesc2 gExcludeRegionBlock +( + plComponent::kBlkComp, _T("XRegionComp"), 0, &gExcludeRegionDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_XREGION, IDS_COMP_XREGION, 0, 0, NULL, + + kXRegionSafePoints, _T("safePoints"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_SAFE, IDC_ADD_SAFE, 0, IDC_DEL_SAFE, + p_classID, Class_ID(DUMMY_CLASS_ID, 0), + end, + + kXRegionInitiallyCleared, _T("initiallyCleared"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CLEARED, + end, + + kXRegionSmartSeek, _T("smartSeek"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SMARTSEEK , + end, + + kXRegionBlockCameras, _T("blockCameras"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CAMERA_LOS, + end, + end +); + +plExcludeRegionComponent::plExcludeRegionComponent() +{ + fClassDesc = &gExcludeRegionDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plKey plExcludeRegionComponent::GetKey(plMaxNode *node) +{ + XRegionKeys::iterator it = fXRegionKeys.find(node); + if (it != fXRegionKeys.end()) + return it->second; + + return nil; +} + +hsBool plExcludeRegionComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fXRegionKeys.clear(); + + fIsValid = false; + + int count = fCompPB->Count(kXRegionSafePoints); + for (int i = 0; i < count; i++) + { + plMaxNode *safeNode = (plMaxNode*)fCompPB->GetINode(kXRegionSafePoints, 0, i); + if (safeNode) + { + fIsValid = true; + // Force the dummies to local so we get the right coords + safeNode->SetForceLocal(true); + } + } + + if (!fIsValid) + { + pErrMsg->Set(true, "Exclude Region Warning", + "Node %s : No safe points specified, exclude region will not be created.\n", + node->GetName()).Show(); + pErrMsg->Set(false); + + return false; + } + + plPhysicalProps *physProps = node->GetPhysicalProps(); + + physProps->SetBoundsType(plSimDefs::kHullBounds, node, pErrMsg); + // removed letting exclude regions have weight... there is no need and it causes PhysX to crash! + //physProps->SetMass(1.f, node, pErrMsg); + physProps->SetMass(0.f, node, pErrMsg); + physProps->SetPinned(true, node, pErrMsg); + + if (fCompPB->GetInt(kXRegionInitiallyCleared)) + { + physProps->SetGroup(plSimDefs::kGroupStatic, node, pErrMsg); + physProps->SetLOSBlockUI(true, node, pErrMsg); + if (fCompPB->GetInt(kXRegionBlockCameras)) + physProps->SetLOSBlockCamera(true, node, pErrMsg); + } + else + { + physProps->SetGroup(plSimDefs::kGroupDetector, node, pErrMsg); + physProps->SetReportGroup(1<SetDrawable(false); + + return true; +} + +hsBool plExcludeRegionComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (!fIsValid) + return false; + + if (!plPhysicCoreComponent::PreConvert(node, pErrMsg)) + { + fIsValid = false; + return false; + } + + plExcludeRegionModifier *mod = TRACKED_NEW plExcludeRegionModifier; + plKey key = node->AddModifier(mod, IGetUniqueName(node)); + fXRegionKeys[node] = key; + + return true; +} + +hsBool plExcludeRegionComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (!fIsValid) + return false; + + if (!plPhysicCoreComponent::Convert(node, pErrMsg)) + { + fIsValid = false; + return false; + } + + plExcludeRegionModifier *mod = plExcludeRegionModifier::ConvertNoRef(fXRegionKeys[node]->GetObjectPtr()); + + if (fCompPB->GetInt(kXRegionSmartSeek)) + mod->UseSmartSeek(); + + if (fCompPB->GetInt(kXRegionBlockCameras)) + mod->SetBlockCameras(true); + + int count = fCompPB->Count(kXRegionSafePoints); + for (int i = 0; i < count; i++) + { + plMaxNode *safePoint = (plMaxNode*)fCompPB->GetINode(kXRegionSafePoints, 0, i); + if (safePoint) + { + plKey pKey = safePoint->GetKey(); + mod->AddSafePoint(pKey); + } + } + + return true; +} + +hsBool plExcludeRegionComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fXRegionKeys.clear(); + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plExcludeRegionComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plExcludeRegionComponent.h new file mode 100644 index 00000000..0580f0e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plExcludeRegionComponent.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plExcludeRegionComponent_h_inc +#define plExcludeRegionComponent_h_inc + +#include "plPhysicalComponents.h" +#include "../pnKeyedObject/plKey.h" +#include + +class plComponentBase; + +#define XREGION_CID Class_ID(0x75903e2, 0x50ac210a) + +class plExcludeRegionComponent : public plPhysicCoreComponent +{ +protected: + bool fIsValid; + + typedef std::map XRegionKeys; + XRegionKeys fXRegionKeys; + +public: + plExcludeRegionComponent(); + + // Can't call until after PreConvert + plKey GetKey(plMaxNode *node); + + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + +#endif // plExcludeRegionComponent_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFlexibilityComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFlexibilityComponent.h new file mode 100644 index 00000000..56754111 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFlexibilityComponent.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plFlexibilityComponent_inc +#define plFlexibilityComponent_inc + +const Class_ID FLEXIBILITY_COMP_CID(0x6fec783f, 0x705536d3); + +class plFlexibilityComponent : public plComponent +{ +public: + enum { + kFlexibility, + kInterRand, + kIntraRand + }; + +public: + plFlexibilityComponent(); + void DeleteThis() { delete this; } + + Point3 GetFlexibility() const; + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + +#endif // plFlexibilityComponent_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootPrintComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootPrintComponent.cpp new file mode 100644 index 00000000..41786912 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootPrintComponent.cpp @@ -0,0 +1,1794 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "plPickNode.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxExport/plExportProgressBar.h" +#include "../MaxConvert/hsMaterialConverter.h" + +#include "hsTypes.h" +#include "plTweak.h" + +#include "hsResMgr.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plObjRefMsg.h" + +#include "../plDrawable/plDynaFootMgr.h" +#include "../plDrawable/plDynaRippleMgr.h" +#include "../plDrawable/plDynaRippleVSMgr.h" +#include "../plDrawable/plDynaBulletMgr.h" +#include "../plDrawable/plDynaPuddleMgr.h" +#include "../plDrawable/plDynaTorpedoMgr.h" +#include "../plDrawable/plDynaTorpedoVSMgr.h" +#include "../plDrawable/plDynaWakeMgr.h" +#include "../plDrawable/plCutter.h" +#include "../plModifier/plDecalEnableMod.h" +#include "../plDrawable/plPrintShape.h" +#include "../plDrawable/plActivePrintShape.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayer.h" + +#include "plWaterComponent.h" +#include "../plDrawable/plWaveSetBase.h" + +#include "plParticleComponents.h" + +void DummyCodeIncludeFuncFootPrint() +{ +} + + +const Class_ID FOOTPRINT_COMP_CID(0x5d553f57, 0x42344c5e); +const Class_ID RIPPLE_COMP_CID(0x750e1b2a, 0x4ad10f15); +const Class_ID PUDDLE_COMP_CID(0x2f800331, 0x640f4914); +const Class_ID WAKE_COMP_CID(0x714b6b6b, 0xa937ff4); +const Class_ID BULLET_COMP_CID(0x208c05d7, 0x41540df2); +const Class_ID TORPEDO_COMP_CID(0x72b85c86, 0xe2b0b40); + +const Class_ID DIRTY_COMP_CID(0x500f1fd3, 0x508c4486); + + + +class plFootPrintComponent : public plComponent +{ +protected: + plDynaDecalMgr* fDecalMgr; + bool fValid; + + bool fNotifiesSetup; + + hsBool ISetupNotifies(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool ISetupDecalMgr(plMaxNode* node, plErrorMsg* pErrMsg, plDynaDecalMgr* decalMgr); + hsBool ICreateDecalMaterials(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool ISetupColorDecalMaterials(plMaxNode* node, plErrorMsg* pErrMsg); + + hsBool ISetupParticles(plMaxNode* node, plErrorMsg* pErrMsg); + + static plParticleComponent* IGetParticleComp(INode* node); + + virtual void IFakeParams(); +public: + enum + { + kWidth, + kLength, + kFadeIn, + kFadeOut, + kLifeSpan, + kLayer, + kBlend, + kDirtyTime, + kNotifies, + kIntensity, + kParticles, + kPartyTime + }; + enum + { + kAlpha, + kMADD, + kAdd, + kMult + }; + + plFootPrintComponent(); + virtual ~plFootPrintComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + + static plDynaDecalMgr* GetDecalMgr(INode* node); +}; + + +CLASS_DESC(plFootPrintComponent, gFootPrintCompDesc, "Foot Print", "FootPrint", COMP_TYPE_FOOTPRINT, FOOTPRINT_COMP_CID) + + +class plWetProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + } + return true; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ADD_NOTIFY) + { + std::vector cids; + cids.push_back(FOOTPRINT_COMP_CID); + cids.push_back(RIPPLE_COMP_CID); + cids.push_back(PUDDLE_COMP_CID); + cids.push_back(WAKE_COMP_CID); + IParamBlock2 *pb = map->GetParamBlock(); + plPick::Node(pb, plFootPrintComponent::kNotifies, &cids, false, false); + + map->Invalidate(plFootPrintComponent::kNotifies); + return TRUE; + } + else + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ADD_PARTICLE) + { + std::vector cids; + cids.push_back(PARTICLE_SYSTEM_COMPONENT_CLASS_ID); + IParamBlock2 *pb = map->GetParamBlock(); + plPick::Node(pb, plFootPrintComponent::kParticles, &cids, false, false); + + map->Invalidate(plFootPrintComponent::kParticles); + return TRUE; + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plWetProc gWetProc; + +///////////////////////////////////////////////////////////////////////////////////// + + +ParamBlockDesc2 gFootPrintBk +( + plComponent::kBlkComp, _T("FootPrint"), 0, &gFootPrintCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FP_FOOTPRINT, IDS_COMP_FOOTPRINT, 0, 0, &gWetProc, + + plFootPrintComponent::kWidth, _T("Width"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 25.0, 400.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_WIDTH, IDC_COMP_FP_WIDTH_SPIN, 1.0, + end, + + plFootPrintComponent::kLength, _T("Length"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 25.0, 400.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_LENGTH, IDC_COMP_FP_LENGTH_SPIN, 1.0, + end, + + plFootPrintComponent::kFadeOut, _T("FadeOut"), TYPE_FLOAT, 0, 0, +// p_default, 10.0, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_FADEOUT, IDC_COMP_FP_FADEOUT_SPIN, 0.1, + end, + + plFootPrintComponent::kLifeSpan, _T("LifeSpan"), TYPE_FLOAT, 0, 0, + p_default, 30.0, + p_range, 0.0, 300.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_LIFESPAN2, IDC_COMP_FP_LIFESPAN_SPIN2, 0.1, + end, + + plFootPrintComponent::kFadeIn, _T("FadeIn"), TYPE_FLOAT, 0, 0, +// p_default, 0.1, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_RAMPEND, IDC_COMP_FP_RAMPEND_SPIN, 0.1, + end, + + plFootPrintComponent::kLayer, _T("Layer"), TYPE_TEXMAP, 0, 0, + p_ui, TYPE_TEXMAPBUTTON, IDC_COMP_FP_TEXMAP, + end, + + plFootPrintComponent::kBlend, _T("Blend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_RADIO_ALPHA, IDC_RADIO_MADD, IDC_RADIO_ADD, IDC_RADIO_MULT, + p_vals, plFootPrintComponent::kAlpha, plFootPrintComponent::kMADD, plFootPrintComponent::kAdd, plFootPrintComponent::kMult, + p_default, plFootPrintComponent::kAlpha, + end, + + plFootPrintComponent::kDirtyTime, _T("DirtyTime"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.0, 300.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_DIRTYTIME, IDC_COMP_FP_DIRTYTIME_SPIN, 0.1, + end, + + plFootPrintComponent::kNotifies, _T("Notifies"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + end, + + plFootPrintComponent::kIntensity, _T("Intensity"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_INTENSITY, IDC_COMP_FP_INTENSITY_SPIN, 1.0, + end, + + plFootPrintComponent::kParticles, _T("Particles"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS2, 0, 0, IDC_DEL_TARGS2, + end, + + plFootPrintComponent::kPartyTime, _T("PartyTime"), TYPE_FLOAT, 0, 0, + p_default, 0.25, + p_range, 0.1, 5.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_PARTYTIME, IDC_COMP_FP_PARTYTIME_SPIN, 0.1, + end, + + end +); + +hsBool plFootPrintComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + // If we don't have a valid layer, we're screwed. Throw up a warning + // and shutdown. + if( !fCompPB->GetTexmap(kLayer) ) + { + pErrMsg->Set(true, GetINode()->GetName(), "No layer setup. Ignoring Footprint generator").CheckAndAsk(); + pErrMsg->Set(false); + fValid = false; + return true; + } + + fDecalMgr = nil; + + fValid = true; + fNotifiesSetup = false; + + return true; +} + +hsBool plFootPrintComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + // If we haven't already, create our DynaDecalMgr and stash it away. + if( !fDecalMgr ) + { + ISetupDecalMgr(node, pErrMsg, TRACKED_NEW plDynaFootMgr); + } + + return true; +} + +hsBool plFootPrintComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + if( !fNotifiesSetup ) + ISetupNotifies(node, pErrMsg); + + // Add this node's object to our DynaDecalMgr. + hsgResMgr::ResMgr()->AddViaNotify(node->GetKey(), TRACKED_NEW plGenRefMsg(fDecalMgr->GetKey(), plRefMsg::kOnCreate, 0, plDynaDecalMgr::kRefTarget), plRefFlags::kActiveRef); + + return true; +} + +hsBool plFootPrintComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fDecalMgr = nil; + return true; +} + +plFootPrintComponent::plFootPrintComponent() +: fDecalMgr(nil) +{ + fClassDesc = &gFootPrintCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plFootPrintComponent::~plFootPrintComponent() +{ +} + +void plFootPrintComponent::IFakeParams() +{ + fCompPB->SetValue(kFadeIn, TimeValue(0), 0.1f); + fCompPB->SetValue(kFadeOut, TimeValue(0), fCompPB->GetFloat(kLifeSpan) * 0.25f); +} + +hsBool plFootPrintComponent::ISetupDecalMgr(plMaxNode* node, plErrorMsg* pErrMsg, plDynaDecalMgr* decalMgr) +{ + IFakeParams(); + + fDecalMgr = decalMgr; + plKey mgrKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), fDecalMgr, node->GetLocation()); + + float width = fCompPB->GetFloat(kWidth) * 1.e-2f; + float length = fCompPB->GetFloat(kLength) * 1.e-2f; + float wetTime = fCompPB->GetFloat(kDirtyTime); + float fadeIn = fCompPB->GetFloat(kFadeIn); + float fadeOut = fCompPB->GetFloat(kFadeOut); + float lifeSpan = fCompPB->GetFloat(kLifeSpan); + float intensity = fCompPB->GetFloat(kIntensity) * 1.e-2f; + float partyTime = fCompPB->GetFloat(kPartyTime); + + const hsScalar kHeightHack = 1.f; + fDecalMgr->SetScale(hsVector3(width, length, kHeightHack)); + + const float kMinFadeOut = 1.e-2f; + if( fadeOut > lifeSpan - kMinFadeOut) + fadeOut = lifeSpan - kMinFadeOut; + if( fadeIn > lifeSpan - fadeOut ) + fadeIn = lifeSpan - fadeOut; + + fDecalMgr->SetWetLength(wetTime); + fDecalMgr->SetRampEnd(fadeIn); + fDecalMgr->SetDecayStart(lifeSpan - fadeOut); + fDecalMgr->SetLifeSpan(lifeSpan); + fDecalMgr->SetIntensity(intensity); + fDecalMgr->SetPartyTime(partyTime); + + if( !ICreateDecalMaterials(node, pErrMsg) ) + { + delete fDecalMgr; + fDecalMgr = nil; + return fValid = false; + } + + hsgResMgr::ResMgr()->AddViaNotify(mgrKey, TRACKED_NEW plNodeRefMsg(node->GetRoomKey(), plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric), plRefFlags::kActiveRef); + + ISetupParticles(node, pErrMsg); + + return true; +} + +hsBool plFootPrintComponent::ISetupNotifies(plMaxNode* node, plErrorMsg* pErrMsg) +{ + int num = fCompPB->Count(kNotifies); + int i; + for( i = 0; i < num; i++ ) + { + plDynaDecalMgr* slave = GetDecalMgr(fCompPB->GetINode(kNotifies, TimeValue(0), i)); + if( slave ) + { + slave->SetWaitOnEnable(true); + fDecalMgr->AddNotify(slave->GetKey()); + } + } + fNotifiesSetup = true; + + return true; +} + +hsBool plFootPrintComponent::ISetupParticles(plMaxNode* node, plErrorMsg* pErrMsg) +{ + int num = fCompPB->Count(kParticles); + if( !num ) + return true; + + int i; + for( i = 0; i < num; i++ ) + { + plParticleComponent* partyComp = IGetParticleComp(fCompPB->GetINode(kParticles, TimeValue(0), i)); + if( partyComp && partyComp->NumTargets() ) + { + plMaxNodeBase* partyNode = nil; + const int numTarg = partyComp->NumTargets(); + int j; + for( j = 0; j < numTarg; j++ ) + { + partyNode = partyComp->GetTarget(j); + if( partyNode ) + break; + } + if( partyNode ) + { + plSceneObject* obj = partyNode->GetSceneObject(); + if( obj ) + { + hsgResMgr::ResMgr()->AddViaNotify(obj->GetKey(), + new plGenRefMsg(fDecalMgr->GetKey(), plRefMsg::kOnCreate, 0, plDynaDecalMgr::kRefPartyObject), plRefFlags::kPassiveRef); + } + } + plConst(int) kNumEmitters(3); + partyComp->SetEmitterReserve(kNumEmitters); + } + } + + return true; +} + +hsBool plFootPrintComponent::ICreateDecalMaterials(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( fCompPB->GetInt(kBlend) != kAlpha ) + return ISetupColorDecalMaterials(node, pErrMsg); + + hsGMaterial* matRTShade = hsMaterialConverter::Instance().NonAlphaHackPrint(node, fCompPB->GetTexmap(kLayer), hsGMatState::kBlendAlpha); + + if( !matRTShade ) + return fValid = false; + + hsgResMgr::ResMgr()->AddViaNotify(matRTShade->GetKey(), TRACKED_NEW plGenRefMsg(fDecalMgr->GetKey(), plRefMsg::kOnCreate, 0, plDynaDecalMgr::kRefMatRTShade), plRefFlags::kActiveRef); + + hsGMaterial* matPreShade = hsMaterialConverter::Instance().AlphaHackPrint(node, fCompPB->GetTexmap(kLayer), hsGMatState::kBlendAlpha); + + hsgResMgr::ResMgr()->AddViaNotify(matPreShade->GetKey(), TRACKED_NEW plGenRefMsg(fDecalMgr->GetKey(), plRefMsg::kOnCreate, 0, plDynaDecalMgr::kRefMatPreShade), plRefFlags::kActiveRef); + + return true; +} + +hsBool plFootPrintComponent::ISetupColorDecalMaterials(plMaxNode* node, plErrorMsg* pErrMsg) +{ + UInt32 blendFlags = 0; + switch( fCompPB->GetInt(kBlend) ) + { + case kMADD: + blendFlags = hsGMatState::kBlendMADD; + break; + case kAdd: + blendFlags = hsGMatState::kBlendAdd; + break; + case kMult: + blendFlags = hsGMatState::kBlendMult; + break; + default: + hsAssert(false, "Unknown blend mode"); + blendFlags = hsGMatState::kBlendMADD; // cuz it's my fave. + break; + } + hsGMaterial* matRTShade = hsMaterialConverter::Instance().NonAlphaHackPrint(node, fCompPB->GetTexmap(kLayer), blendFlags); + + if( blendFlags & hsGMatState::kBlendMult ) + { + plLayer* layer = plLayer::ConvertNoRef(matRTShade->GetLayer(0)->BottomOfStack()); + if( !layer ) + return fValid = false; + + layer->SetBlendFlags(layer->GetBlendFlags() | hsGMatState::kBlendInvertFinalColor); + } + + hsgResMgr::ResMgr()->AddViaNotify(matRTShade->GetKey(), TRACKED_NEW plGenRefMsg(fDecalMgr->GetKey(), plRefMsg::kOnCreate, 0, plDynaDecalMgr::kRefMatRTShade), plRefFlags::kActiveRef); + + return true; +} + +plParticleComponent* plFootPrintComponent::IGetParticleComp(INode* node) +{ + if( !node ) + return nil; + + plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); + if( comp == nil ) + return nil; + + if( comp->ClassID() == PARTICLE_SYSTEM_COMPONENT_CLASS_ID ) + { + plParticleComponent* party = (plParticleComponent*)comp; + return party; + } + + return nil; +} + +plDynaDecalMgr* plFootPrintComponent::GetDecalMgr(INode* node) +{ + if( !node ) + return nil; + + plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); + if( comp == nil ) + return nil; + + if( (comp->ClassID() == FOOTPRINT_COMP_CID) + || (comp->ClassID() == RIPPLE_COMP_CID) + || (comp->ClassID() == PUDDLE_COMP_CID) + || (comp->ClassID() == WAKE_COMP_CID) + ) + { + plFootPrintComponent* foot = (plFootPrintComponent*)comp; + return foot->fDecalMgr; + } + + return nil; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class plRippleComponent : public plFootPrintComponent +{ +protected: + virtual void IFakeParams(); +public: + + plRippleComponent(); + virtual ~plRippleComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plRippleComponent, gRippleCompDesc, "Ripple", "Ripple", COMP_TYPE_FOOTPRINT, RIPPLE_COMP_CID) + + +///////////////////////////////////////////////////////////////////////////////////// + + +ParamBlockDesc2 gRippleBk +( + plComponent::kBlkComp, _T("Ripple"), 0, &gRippleCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FP_RIPPLE, IDS_COMP_RIPPLE, 0, 0, &gWetProc, + + plFootPrintComponent::kWidth, _T("Width"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 25.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_WIDTH, IDC_COMP_FP_WIDTH_SPIN, 1.0, + end, + + plFootPrintComponent::kLength, _T("Length"), TYPE_FLOAT, 0, 0, +// p_default, 1.0, +// p_range, 0.25, 10.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_LENGTH, IDC_COMP_FP_LENGTH_SPIN, 0.1, + end, + + plFootPrintComponent::kFadeOut, _T("FadeOut"), TYPE_FLOAT, 0, 0, +// p_default, 3.5, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_FADEOUT, IDC_COMP_FP_FADEOUT_SPIN, 0.1, + end, + + plFootPrintComponent::kLifeSpan, _T("LifeSpan"), TYPE_FLOAT, 0, 0, +// p_default, 5.0, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_LIFESPAN2, IDC_COMP_FP_LIFESPAN_SPIN2, 0.1, + end, + + plFootPrintComponent::kFadeIn, _T("FadeIn"), TYPE_FLOAT, 0, 0, +// p_default, 0.25, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_RAMPEND, IDC_COMP_FP_RAMPEND_SPIN, 0.1, + end, + + plFootPrintComponent::kLayer, _T("Layer"), TYPE_TEXMAP, 0, 0, + p_ui, TYPE_TEXMAPBUTTON, IDC_COMP_FP_TEXMAP, + end, + + plFootPrintComponent::kBlend, _T("Blend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_RADIO_ALPHA, IDC_RADIO_MADD, IDC_RADIO_ADD, IDC_RADIO_MULT, + p_vals, plFootPrintComponent::kAlpha, plFootPrintComponent::kMADD, plFootPrintComponent::kAdd, plFootPrintComponent::kMult, + p_default, plFootPrintComponent::kAlpha, + end, + + plFootPrintComponent::kDirtyTime, _T("DirtyTime"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.0, 300.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_DIRTYTIME, IDC_COMP_FP_DIRTYTIME_SPIN, 0.1, + end, + + plFootPrintComponent::kNotifies, _T("Notifies"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + end, + + plFootPrintComponent::kIntensity, _T("Intensity"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_INTENSITY, IDC_COMP_FP_INTENSITY_SPIN, 1.0, + end, + + plFootPrintComponent::kParticles, _T("Particles"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS2, 0, 0, IDC_DEL_TARGS2, + end, + + plFootPrintComponent::kPartyTime, _T("PartyTime"), TYPE_FLOAT, 0, 0, + p_default, 0.25, + p_range, 0.1, 5.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_PARTYTIME, IDC_COMP_FP_PARTYTIME_SPIN, 0.1, + end, + + end +); + +void plRippleComponent::IFakeParams() +{ + fCompPB->SetValue(kLength, TimeValue(0), fCompPB->GetFloat(kWidth)); + fCompPB->SetValue(kFadeIn, TimeValue(0), 0.25f); + fCompPB->SetValue(kLifeSpan, TimeValue(0), fCompPB->GetFloat(kWidth) * 1.e-2f * 0.5f); + fCompPB->SetValue(kFadeOut, TimeValue(0), fCompPB->GetFloat(kLifeSpan) * 0.80f); +} + +hsBool plRippleComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return plFootPrintComponent::SetupProperties(node, pErrMsg); +} + +hsBool plRippleComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + // If we haven't already, create our DynaDecalMgr and stash it away. + if( !fDecalMgr ) + { + plDynaRippleMgr* ripple = nil; + if( node->GetVS() || node->UserPropExists("XXXWaterColor") ) + { + ripple = TRACKED_NEW plDynaRippleVSMgr; + } + else + { + ripple = TRACKED_NEW plDynaRippleMgr; + } + ISetupDecalMgr(node, pErrMsg, ripple); + if( fValid ) + { + ripple->SetUVWAnim(hsVector3(3.f, 3.f, 3.f), hsVector3(1.f, 1.f, 1.f)); + } + } + + return true; +} + +hsBool plRippleComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + if( !fNotifiesSetup ) + { + plWaveSetBase* waveSet = plWaterComponent::GetWaveSetFromNode(node); + if( waveSet ) + { + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(fDecalMgr->GetKey(), plRefMsg::kOnCreate, 0, plDynaRippleVSMgr::kRefWaveSetBase); + hsgResMgr::ResMgr()->AddViaNotify(waveSet->GetKey(), refMsg, plRefFlags::kPassiveRef); + } + + ISetupNotifies(node, pErrMsg); + } + + // Add this node's object to our DynaDecalMgr. + hsgResMgr::ResMgr()->AddViaNotify(node->GetKey(), TRACKED_NEW plGenRefMsg(fDecalMgr->GetKey(), plRefMsg::kOnCreate, 0, plDynaDecalMgr::kRefTarget), plRefFlags::kActiveRef); + + return true; +} + +plRippleComponent::plRippleComponent() +{ + fClassDesc = &gRippleCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plRippleComponent::~plRippleComponent() +{ +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +class plPuddleComponent : public plRippleComponent +{ +protected: +public: + + plPuddleComponent(); + virtual ~plPuddleComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plPuddleComponent, gPuddleCompDesc, "Puddle", "Puddle", COMP_TYPE_FOOTPRINT, PUDDLE_COMP_CID) + + +///////////////////////////////////////////////////////////////////////////////////// + + +ParamBlockDesc2 gPuddleBk +( + plComponent::kBlkComp, _T("Puddle"), 0, &gPuddleCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FP_PUDDLE, IDS_COMP_PUDDLE, 0, 0, &gWetProc, + + plFootPrintComponent::kWidth, _T("Width"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 25.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_WIDTH, IDC_COMP_FP_WIDTH_SPIN, 1.0, + end, + + plFootPrintComponent::kLength, _T("Length"), TYPE_FLOAT, 0, 0, +// p_default, 1.0, +// p_range, 0.25, 10.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_LENGTH, IDC_COMP_FP_LENGTH_SPIN, 0.1, + end, + + plFootPrintComponent::kFadeOut, _T("FadeOut"), TYPE_FLOAT, 0, 0, +// p_default, 3.5, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_FADEOUT, IDC_COMP_FP_FADEOUT_SPIN, 0.1, + end, + + plFootPrintComponent::kLifeSpan, _T("LifeSpan"), TYPE_FLOAT, 0, 0, +// p_default, 5.0, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_LIFESPAN2, IDC_COMP_FP_LIFESPAN_SPIN2, 0.1, + end, + + plFootPrintComponent::kFadeIn, _T("FadeIn"), TYPE_FLOAT, 0, 0, +// p_default, 0.25, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_RAMPEND, IDC_COMP_FP_RAMPEND_SPIN, 0.1, + end, + + plFootPrintComponent::kLayer, _T("Layer"), TYPE_TEXMAP, 0, 0, + p_ui, TYPE_TEXMAPBUTTON, IDC_COMP_FP_TEXMAP, + end, + + plFootPrintComponent::kBlend, _T("Blend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_RADIO_ALPHA, IDC_RADIO_MADD, IDC_RADIO_ADD, IDC_RADIO_MULT, + p_vals, plFootPrintComponent::kAlpha, plFootPrintComponent::kMADD, plFootPrintComponent::kAdd, plFootPrintComponent::kMult, + p_default, plFootPrintComponent::kAlpha, + end, + + plFootPrintComponent::kDirtyTime, _T("DirtyTime"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.0, 300.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_DIRTYTIME, IDC_COMP_FP_DIRTYTIME_SPIN, 0.1, + end, + + plFootPrintComponent::kNotifies, _T("Notifies"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + end, + + plFootPrintComponent::kIntensity, _T("Intensity"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_INTENSITY, IDC_COMP_FP_INTENSITY_SPIN, 1.0, + end, + + plFootPrintComponent::kParticles, _T("Particles"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS2, 0, 0, IDC_DEL_TARGS2, + end, + + plFootPrintComponent::kPartyTime, _T("PartyTime"), TYPE_FLOAT, 0, 0, + p_default, 0.25, + p_range, 0.1, 5.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_PARTYTIME, IDC_COMP_FP_PARTYTIME_SPIN, 0.1, + end, + + end +); + +hsBool plPuddleComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return plRippleComponent::SetupProperties(node, pErrMsg); +} + +hsBool plPuddleComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + // If we haven't already, create our DynaDecalMgr and stash it away. + if( !fDecalMgr ) + { + plDynaRippleMgr* puddle = TRACKED_NEW plDynaPuddleMgr; + + ISetupDecalMgr(node, pErrMsg, puddle); + if( fValid ) + { + puddle->SetUVWAnim(hsVector3(5.f, 5.f, 5.f), hsVector3(1.f, 1.f, 1.f)); + } + } + return true; +} + +hsBool plPuddleComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + if( !fNotifiesSetup ) + ISetupNotifies(node, pErrMsg); + + // Add this node's object to our DynaDecalMgr. + hsgResMgr::ResMgr()->AddViaNotify(node->GetKey(), TRACKED_NEW plGenRefMsg(fDecalMgr->GetKey(), plRefMsg::kOnCreate, 0, plDynaDecalMgr::kRefTarget), plRefFlags::kActiveRef); + + return true; +} + +plPuddleComponent::plPuddleComponent() +{ + fClassDesc = &gPuddleCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plPuddleComponent::~plPuddleComponent() +{ +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +class plBulletComponent : public plFootPrintComponent +{ +protected: + virtual void IFakeParams(); +public: + + plBulletComponent(); + virtual ~plBulletComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plBulletComponent, gBulletCompDesc, "Bullet", "Bullet", COMP_TYPE_FOOTPRINT, BULLET_COMP_CID) + + +///////////////////////////////////////////////////////////////////////////////////// + + +ParamBlockDesc2 gBulletBk +( + plComponent::kBlkComp, _T("Bullet"), 0, &gBulletCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FP_BULLET, IDS_COMP_FP_BULLET, 0, 0, &gWetProc, + + plFootPrintComponent::kWidth, _T("Width"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 25.0, 400.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_SCALE, IDC_COMP_FP_SCALE_SPIN, 1.0, + end, + + plFootPrintComponent::kLength, _T("Length"), TYPE_FLOAT, 0, 0, +// p_default, 100.0, +// p_range, 25.0, 400.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_LENGTH, IDC_COMP_FP_LENGTH_SPIN, 1.0, + end, + + plFootPrintComponent::kFadeOut, _T("FadeOut"), TYPE_FLOAT, 0, 0, +// p_default, 10.0, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_FADEOUT, IDC_COMP_FP_FADEOUT_SPIN, 0.1, + end, + + plFootPrintComponent::kLifeSpan, _T("LifeSpan"), TYPE_FLOAT, 0, 0, + p_default, 15.0, + p_range, 0.0, 300.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_LIFESPAN2, IDC_COMP_FP_LIFESPAN_SPIN2, 0.1, + end, + + plFootPrintComponent::kFadeIn, _T("FadeIn"), TYPE_FLOAT, 0, 0, +// p_default, 0.1, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_RAMPEND, IDC_COMP_FP_RAMPEND_SPIN, 0.1, + end, + + plFootPrintComponent::kLayer, _T("Layer"), TYPE_TEXMAP, 0, 0, + p_ui, TYPE_TEXMAPBUTTON, IDC_COMP_FP_TEXMAP, + end, + + plFootPrintComponent::kBlend, _T("Blend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_RADIO_ALPHA, IDC_RADIO_MADD, IDC_RADIO_ADD, IDC_RADIO_MULT, + p_vals, plFootPrintComponent::kAlpha, plFootPrintComponent::kMADD, plFootPrintComponent::kAdd, plFootPrintComponent::kMult, + p_default, plFootPrintComponent::kAlpha, + end, + + plFootPrintComponent::kDirtyTime, _T("DirtyTime"), TYPE_FLOAT, 0, 0, +// p_default, 10.0, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_DIRTYTIME, IDC_COMP_FP_DIRTYTIME_SPIN, 0.1, + end, + + plFootPrintComponent::kNotifies, _T("Notifies"), TYPE_INODE_TAB, 0, 0, 0, +// p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + end, + + plFootPrintComponent::kIntensity, _T("Intensity"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_INTENSITY, IDC_COMP_FP_INTENSITY_SPIN, 1.0, + end, + + plFootPrintComponent::kParticles, _T("Particles"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS2, 0, 0, IDC_DEL_TARGS2, + end, + + plFootPrintComponent::kPartyTime, _T("PartyTime"), TYPE_FLOAT, 0, 0, + p_default, 0.25, + p_range, 0.1, 5.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_PARTYTIME, IDC_COMP_FP_PARTYTIME_SPIN, 0.1, + end, + + end +); + +void plBulletComponent::IFakeParams() +{ + fCompPB->SetValue(kLength, TimeValue(0), fCompPB->GetFloat(kWidth)); + plFootPrintComponent::IFakeParams(); +} + +hsBool plBulletComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return plFootPrintComponent::SetupProperties(node, pErrMsg); +} + +hsBool plBulletComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + // If we haven't already, create our DynaDecalMgr and stash it away. + if( !fDecalMgr ) + { + plDynaBulletMgr* bullet = TRACKED_NEW plDynaBulletMgr; + + ISetupDecalMgr(node, pErrMsg, bullet); + } + return true; +} + +hsBool plBulletComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + if( !fNotifiesSetup ) + ISetupNotifies(node, pErrMsg); + + // Add this node's object to our DynaDecalMgr. + hsgResMgr::ResMgr()->AddViaNotify(node->GetKey(), TRACKED_NEW plGenRefMsg(fDecalMgr->GetKey(), plRefMsg::kOnCreate, 0, plDynaDecalMgr::kRefTarget), plRefFlags::kActiveRef); + + return true; +} + +plBulletComponent::plBulletComponent() +{ + fClassDesc = &gBulletCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plBulletComponent::~plBulletComponent() +{ +} + + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +class plTorpedoComponent : public plRippleComponent +{ +protected: + virtual void IFakeParams(); +public: + + plTorpedoComponent(); + virtual ~plTorpedoComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plTorpedoComponent, gTorpedoCompDesc, "Water Bullet", "WetBullet", COMP_TYPE_FOOTPRINT, TORPEDO_COMP_CID) + + +///////////////////////////////////////////////////////////////////////////////////// + + +ParamBlockDesc2 gTorpedoBk +( + plComponent::kBlkComp, _T("Torpedo"), 0, &gTorpedoCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FP_TORPEDO, IDS_COMP_FP_TORPEDO, 0, 0, &gWetProc, + + plFootPrintComponent::kWidth, _T("Width"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 25.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_WIDTH, IDC_COMP_FP_WIDTH_SPIN, 1.0, + end, + + plFootPrintComponent::kLength, _T("Length"), TYPE_FLOAT, 0, 0, +// p_default, 1.0, +// p_range, 0.25, 10.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_LENGTH, IDC_COMP_FP_LENGTH_SPIN, 0.1, + end, + + plFootPrintComponent::kFadeOut, _T("FadeOut"), TYPE_FLOAT, 0, 0, +// p_default, 3.5, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_FADEOUT, IDC_COMP_FP_FADEOUT_SPIN, 0.1, + end, + + plFootPrintComponent::kLifeSpan, _T("LifeSpan"), TYPE_FLOAT, 0, 0, + p_default, 5.0, + p_range, 0.0, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_LIFESPAN2, IDC_COMP_FP_LIFESPAN_SPIN2, 0.1, + end, + + plFootPrintComponent::kFadeIn, _T("FadeIn"), TYPE_FLOAT, 0, 0, +// p_default, 0.25, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_RAMPEND, IDC_COMP_FP_RAMPEND_SPIN, 0.1, + end, + + plFootPrintComponent::kLayer, _T("Layer"), TYPE_TEXMAP, 0, 0, + p_ui, TYPE_TEXMAPBUTTON, IDC_COMP_FP_TEXMAP, + end, + + plFootPrintComponent::kBlend, _T("Blend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_RADIO_ALPHA, IDC_RADIO_MADD, IDC_RADIO_ADD, IDC_RADIO_MULT, + p_vals, plFootPrintComponent::kAlpha, plFootPrintComponent::kMADD, plFootPrintComponent::kAdd, plFootPrintComponent::kMult, + p_default, plFootPrintComponent::kAlpha, + end, + + plFootPrintComponent::kDirtyTime, _T("DirtyTime"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.0, 300.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_DIRTYTIME, IDC_COMP_FP_DIRTYTIME_SPIN, 0.1, + end, + + plFootPrintComponent::kNotifies, _T("Notifies"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + end, + + plFootPrintComponent::kIntensity, _T("Intensity"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_INTENSITY, IDC_COMP_FP_INTENSITY_SPIN, 1.0, + end, + + plFootPrintComponent::kParticles, _T("Particles"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS2, 0, 0, IDC_DEL_TARGS2, + end, + + plFootPrintComponent::kPartyTime, _T("PartyTime"), TYPE_FLOAT, 0, 0, + p_default, 0.25, + p_range, 0.1, 5.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_PARTYTIME, IDC_COMP_FP_PARTYTIME_SPIN, 0.1, + end, + + end +); + +void plTorpedoComponent::IFakeParams() +{ + fCompPB->SetValue(kLength, TimeValue(0), fCompPB->GetFloat(kWidth)); + fCompPB->SetValue(kFadeIn, TimeValue(0), 0.25f); + fCompPB->SetValue(kFadeOut, TimeValue(0), fCompPB->GetFloat(kLifeSpan) * 0.80f); +} + +hsBool plTorpedoComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return plRippleComponent::SetupProperties(node, pErrMsg); +} + +hsBool plTorpedoComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + // If we haven't already, create our DynaDecalMgr and stash it away. + if( !fDecalMgr ) + { + plDynaRippleMgr* torpedo; + if( node->GetVS() ) + torpedo = TRACKED_NEW plDynaTorpedoVSMgr; + else + torpedo = TRACKED_NEW plDynaTorpedoMgr; + + ISetupDecalMgr(node, pErrMsg, torpedo); + if( fValid ) + { + torpedo->SetUVWAnim(hsVector3(5.f, 5.f, 5.f), hsVector3(1.f, 1.f, 1.f)); + } + } + return true; +} + +hsBool plTorpedoComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return plRippleComponent::Convert(node, pErrMsg); +} + +plTorpedoComponent::plTorpedoComponent() +{ + fClassDesc = &gTorpedoCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plTorpedoComponent::~plTorpedoComponent() +{ +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class plWakeComponent : public plFootPrintComponent +{ +protected: + virtual void IFakeParams(); +public: + + plWakeComponent(); + virtual ~plWakeComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plWakeComponent, gWakeCompDesc, "Wake", "Wake", COMP_TYPE_FOOTPRINT, WAKE_COMP_CID) + + +///////////////////////////////////////////////////////////////////////////////////// + + +ParamBlockDesc2 gWakeBk +( + plComponent::kBlkComp, _T("Wake"), 0, &gWakeCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FP_WAKE, IDS_COMP_FP_WAKE, 0, 0, &gWetProc, + + plFootPrintComponent::kWidth, _T("Width"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 25.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_WIDTH, IDC_COMP_FP_WIDTH_SPIN, 1.0, + end, + + plFootPrintComponent::kLength, _T("Length"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 25.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_LENGTH, IDC_COMP_FP_LENGTH_SPIN, 0.1, + end, + + plFootPrintComponent::kFadeOut, _T("FadeOut"), TYPE_FLOAT, 0, 0, +// p_default, 3.5, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_FADEOUT, IDC_COMP_FP_FADEOUT_SPIN, 0.1, + end, + + plFootPrintComponent::kLifeSpan, _T("LifeSpan"), TYPE_FLOAT, 0, 0, +// p_default, 5.0, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_LIFESPAN2, IDC_COMP_FP_LIFESPAN_SPIN2, 0.1, + end, + + plFootPrintComponent::kFadeIn, _T("FadeIn"), TYPE_FLOAT, 0, 0, +// p_default, 0.25, +// p_range, 0.0, 300.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_RAMPEND, IDC_COMP_FP_RAMPEND_SPIN, 0.1, + end, + + plFootPrintComponent::kLayer, _T("Layer"), TYPE_TEXMAP, 0, 0, + p_ui, TYPE_TEXMAPBUTTON, IDC_COMP_FP_TEXMAP, + end, + + plFootPrintComponent::kBlend, _T("Blend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_RADIO_ALPHA, IDC_RADIO_MADD, IDC_RADIO_ADD, IDC_RADIO_MULT, + p_vals, plFootPrintComponent::kAlpha, plFootPrintComponent::kMADD, plFootPrintComponent::kAdd, plFootPrintComponent::kMult, + p_default, plFootPrintComponent::kAlpha, + end, + + plFootPrintComponent::kDirtyTime, _T("DirtyTime"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.0, 300.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_DIRTYTIME, IDC_COMP_FP_DIRTYTIME_SPIN, 0.1, + end, + + plFootPrintComponent::kNotifies, _T("Notifies"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + end, + + plFootPrintComponent::kIntensity, _T("Intensity"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_INTENSITY, IDC_COMP_FP_INTENSITY_SPIN, 1.0, + end, + + plFootPrintComponent::kParticles, _T("Particles"), TYPE_INODE_TAB, 0, 0, 0, +// p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS2, 0, 0, IDC_DEL_TARGS2, + end, + + plFootPrintComponent::kPartyTime, _T("PartyTime"), TYPE_FLOAT, 0, 0, +// p_default, 0.25, +// p_range, 0.1, 5.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_FP_PARTYTIME, IDC_COMP_FP_PARTYTIME_SPIN, 0.1, + end, + + end +); + +void plWakeComponent::IFakeParams() +{ + fCompPB->SetValue(kFadeIn, TimeValue(0), 0.25f); + fCompPB->SetValue(kLifeSpan, TimeValue(0), fCompPB->GetFloat(kWidth) * 1.e-2f * 0.5f); + fCompPB->SetValue(kFadeOut, TimeValue(0), fCompPB->GetFloat(kLifeSpan) * 0.80f); +} + +hsBool plWakeComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return plFootPrintComponent::SetupProperties(node, pErrMsg); +} + +hsBool plWakeComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + // If we haven't already, create our DynaDecalMgr and stash it away. + if( !fDecalMgr ) + { + plDynaWakeMgr* wake = TRACKED_NEW plDynaWakeMgr; + ISetupDecalMgr(node, pErrMsg, wake); + if( fValid ) + { + wake->SetUVWAnim(hsVector3(5.f, 5.f, 5.f), hsVector3(1.f, 1.f, 1.f)); + } + } + + return true; +} + +hsBool plWakeComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + if( !fNotifiesSetup ) + ISetupNotifies(node, pErrMsg); + + // Add this node's object to our DynaDecalMgr. + hsgResMgr::ResMgr()->AddViaNotify(node->GetKey(), TRACKED_NEW plGenRefMsg(fDecalMgr->GetKey(), plRefMsg::kOnCreate, 0, plDynaDecalMgr::kRefTarget), plRefFlags::kActiveRef); + + return true; +} + +plWakeComponent::plWakeComponent() +{ + fClassDesc = &gWakeCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plWakeComponent::~plWakeComponent() +{ +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// + +class plDirtyComponent : public plComponent +{ +public: + enum + { + kDecals, + kDirtyTime + }; +protected: +public: + + plDirtyComponent(); + virtual ~plDirtyComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plDirtyComponent, gDirtyCompDesc, "Dirty/Wet Region", "Dirty/Wet", COMP_TYPE_FOOTPRINT, DIRTY_COMP_CID) + + +class plDirtyProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + } + return true; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ADD_DECAL) + { + std::vector cids; + cids.push_back(FOOTPRINT_COMP_CID); + cids.push_back(RIPPLE_COMP_CID); + cids.push_back(PUDDLE_COMP_CID); + cids.push_back(WAKE_COMP_CID); + IParamBlock2 *pb = map->GetParamBlock(); + plPick::Node(pb, plDirtyComponent::kDecals, &cids, false, false); + + map->Invalidate(plDirtyComponent::kDecals); + return TRUE; + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plDirtyProc gDirtyProc; + + +///////////////////////////////////////////////////////////////////////////////////// + + +ParamBlockDesc2 gDirtyBk +( + plComponent::kBlkComp, _T("Dirty"), 0, &gDirtyCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FP_DIRTY, IDS_COMP_FP_DIRTY, 0, 0, &gDirtyProc, + + plDirtyComponent::kDecals, _T("Decals"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + end, + + plDirtyComponent::kDirtyTime, _T("DirtyTime"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.0, 300.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FP_DIRTYTIME, IDC_COMP_FP_DIRTYTIME_SPIN, 0.1, + end, + + end +); + +hsBool plDirtyComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plDirtyComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plDirtyComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + // Check that this node has a physical interface, or all is for nought. + // Should throw up a warning if it doesn't have one, seems an easy thing + // to miss. + if( !node->IsPhysical() ) + { + pErrMsg->Set(true, node->GetName(), "Has no physical component to notify %s Dirty/Wet component", GetINode()->GetName()).CheckAndAsk(); + pErrMsg->Set(false); + return true; + } + + + plDecalEnableMod* enable = TRACKED_NEW plDecalEnableMod; + plKey modKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), enable, node->GetLocation()); + + int numDecals = fCompPB->Count(kDecals); + int i; + for( i = 0; i < numDecals; i++ ) + { + INode* decalNode = fCompPB->GetINode(kDecals, TimeValue(0), i); + + plDynaDecalMgr* decal = plFootPrintComponent::GetDecalMgr(decalNode); + + if( decal ) + { + decal->SetWaitOnEnable(true); + enable->AddDecalKey(decal->GetKey()); + } + } + enable->SetWetLength(fCompPB->GetFloat(kDirtyTime)); + hsgResMgr::ResMgr()->AddViaNotify(modKey, TRACKED_NEW plObjRefMsg(node->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + return true; +} + +plDirtyComponent::plDirtyComponent() +{ + fClassDesc = &gDirtyCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plDirtyComponent::~plDirtyComponent() +{ +} + + + +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// + +const Class_ID PRINTSHAPE_COMP_CID(0x208226a1, 0x2a6e67ba); + +class plPrintShapeComponent : public plComponent +{ +public: + enum + { + kWidth, + kLength, + kHeight + }; +protected: +public: + + plPrintShapeComponent(); + virtual ~plPrintShapeComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plPrintShapeComponent, gPrintShapeCompDesc, "Print Shape", "PrintShape", COMP_TYPE_FOOTPRINT, PRINTSHAPE_COMP_CID) + + + +///////////////////////////////////////////////////////////////////////////////////// + + +ParamBlockDesc2 gPrintShapeBk +( + plComponent::kBlkComp, _T("PrintShape"), 0, &gPrintShapeCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FP_PRINTSHAPE, IDS_COMP_FP_PRINTSHAPE, 0, 0, NULL, + + plPrintShapeComponent::kWidth, _T("Width"), TYPE_FLOAT, 0, 0, + p_default, 0.45, + p_range, 0.1, 30.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_WIDTH, IDC_COMP_WIDTH_SPIN, 0.1, + end, + + plPrintShapeComponent::kLength, _T("Length"), TYPE_FLOAT, 0, 0, + p_default, 0.9, + p_range, 0.1, 30.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_LENGTH, IDC_COMP_LENGTH_SPIN, 0.1, + end, + + plPrintShapeComponent::kHeight, _T("Height"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 30.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_HEIGHT, IDC_COMP_HEIGHT_SPIN, 0.1, + end, + + + + end +); + +hsBool plPrintShapeComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetForceLocal(true); + return true; +} + +hsBool plPrintShapeComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plPrintShapeComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plSceneObject* obj = node->GetSceneObject(); + if( !obj ) + return true; + + plPrintShape* shape = TRACKED_NEW plPrintShape(); + plKey shapeKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), shape, node->GetLocation()); + + shape->SetWidth(fCompPB->GetFloat(kWidth)); + shape->SetLength(fCompPB->GetFloat(kLength)); + shape->SetHeight(fCompPB->GetFloat(kHeight)); + + hsgResMgr::ResMgr()->AddViaNotify(shapeKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + return true; +} + +plPrintShapeComponent::plPrintShapeComponent() +{ + fClassDesc = &gPrintShapeCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plPrintShapeComponent::~plPrintShapeComponent() +{ +} + + + +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// + +const Class_ID ACTIVEPRINTSHAPE_COMP_CID(0x61b52046, 0x787734a5); + +class plActivePrintShapeComponent : public plComponent +{ +public: + enum + { + kWidth, + kLength, + kHeight, + kNotifies + }; +protected: +public: + + plActivePrintShapeComponent(); + virtual ~plActivePrintShapeComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plActivePrintShapeComponent, gActivePrintShapeCompDesc, "Active Shape", "ActiveShape", COMP_TYPE_FOOTPRINT, ACTIVEPRINTSHAPE_COMP_CID) + + +class plActiveProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + } + return true; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ADD_NOTIFY2) + { + std::vector cids; + cids.push_back(FOOTPRINT_COMP_CID); + cids.push_back(RIPPLE_COMP_CID); + cids.push_back(PUDDLE_COMP_CID); + cids.push_back(WAKE_COMP_CID); + IParamBlock2 *pb = map->GetParamBlock(); + plPick::Node(pb, plActivePrintShapeComponent::kNotifies, &cids, false, false); + + map->Invalidate(plActivePrintShapeComponent::kNotifies); + return TRUE; + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plActiveProc gActiveProc; + + +///////////////////////////////////////////////////////////////////////////////////// + + +ParamBlockDesc2 gActivePrintShapeBk +( + plComponent::kBlkComp, _T("ActivePrintShape"), 0, &gActivePrintShapeCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FP_ACTIVEPRINTSHAPE, IDS_COMP_FP_ACTIVEPRINTSHAPE, 0, 0, &gActiveProc, + + plActivePrintShapeComponent::kWidth, _T("Width"), TYPE_FLOAT, 0, 0, + p_default, 0.45, + p_range, 0.1, 30.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_WIDTH, IDC_COMP_WIDTH_SPIN, 0.1, + end, + + plActivePrintShapeComponent::kLength, _T("Length"), TYPE_FLOAT, 0, 0, + p_default, 0.9, + p_range, 0.1, 30.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_LENGTH, IDC_COMP_LENGTH_SPIN, 0.1, + end, + + plActivePrintShapeComponent::kHeight, _T("Height"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 30.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_HEIGHT, IDC_COMP_HEIGHT_SPIN, 0.1, + end, + + plActivePrintShapeComponent::kNotifies, _T("Notifies"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + end, + + end +); + +hsBool plActivePrintShapeComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetForceLocal(true); + return true; +} + +hsBool plActivePrintShapeComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plActivePrintShapeComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plSceneObject* obj = node->GetSceneObject(); + if( !obj ) + return true; + + plActivePrintShape* shape = TRACKED_NEW plActivePrintShape(); + plKey shapeKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), shape, node->GetLocation()); + + shape->SetWidth(fCompPB->GetFloat(kWidth)); + shape->SetLength(fCompPB->GetFloat(kLength)); + shape->SetHeight(fCompPB->GetFloat(kHeight)); + + int num = fCompPB->Count(kNotifies); + int i; + for( i = 0; i < num; i++ ) + { + plDynaDecalMgr* notify = plFootPrintComponent::GetDecalMgr(fCompPB->GetINode(kNotifies, TimeValue(0), i)); + if( notify ) + { + shape->AddDecalKey(notify->GetKey()); + } + } + + + hsgResMgr::ResMgr()->AddViaNotify(shapeKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + return true; +} + +plActivePrintShapeComponent::plActivePrintShapeComponent() +{ + fClassDesc = &gActivePrintShapeCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plActivePrintShapeComponent::~plActivePrintShapeComponent() +{ +} + + + +////////////////////////////////////////////////////////////////////////////////////////////////////// + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootstepComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootstepComponent.cpp new file mode 100644 index 00000000..b4f8abb7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootstepComponent.cpp @@ -0,0 +1,186 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "resource.h" +#include "hsConfig.h" +#include +#include "hsResMgr.h" +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "../MaxMain/plMaxNode.h" + +#include "plFootstepComponent.h" +#include "plAudioComponents.h" +#include "plComponentReg.h" + +#include "../plAvatar/plAvatarClothing.h" +#include "../plAvatar/plArmatureEffects.h" +#include "../plAvatar/plArmatureMod.h" +#include "../pfAudio/plRandomSoundMod.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "plPickNode.h" + +void DummyCodeIncludeFuncFootstepSound() +{ +} + +CLASS_DESC(plFootstepSoundComponent, gFootstepSoundDesc, "Avatar FootstepSound", "AvatarFootstepSound", COMP_TYPE_AVATAR, FOOTSTEP_SOUND_COMPONENT_CLASS_ID) + +static plFootstepSoundComponentProc gFootstepSoundComponentProc; + +ParamBlockDesc2 gFootstepSoundBk +( + + plComponent::kBlkComp, _T("FootstepSound"), 0, &gFootstepSoundDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_FOOTSTEP_SOUND, IDS_COMP_FOOTSTEP_SOUND, 0, 0, &gFootstepSoundComponentProc, + + plFootstepSoundComponent::kSurface, _T("Surface"), TYPE_INT, 0, 0, + p_default, plArmatureEffectsMgr::kFootDirt, + end, + + plFootstepSoundComponent::kSurfaceList, _T("SoundGroups"), TYPE_INODE_TAB, plArmatureEffectsMgr::kMaxSurface, 0, 0, + end, + + plFootstepSoundComponent::kNodePicker, _T("NodePicker"), TYPE_INODE, 0, 0, + end, + + end +); + +plFootstepSoundComponent::plFootstepSoundComponent() +{ + fClassDesc = &gFootstepSoundDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +extern const plArmatureMod * FindArmatureMod(const plSceneObject *obj); +/*{ + int count = obj->GetNumModifiers(); + + for (int i = 0; i < count; i++) + { + const plModifier *mod = obj->GetModifier(i); + const plArmatureMod * avMod = plArmatureMod::ConvertNoRef(mod); + if(avMod) + return avMod; + } + return nil; +}*/ + +hsBool plFootstepSoundComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plGenRefMsg *msg; + plArmatureEffectFootSound *effect = TRACKED_NEW plArmatureEffectFootSound(); + + // Note: MUST be a hard-coded keyname, since we search for same name in plArmatureMod.cpp + hsgResMgr::ResMgr()->NewKey( "FootstepSounds", effect, node->GetLocation()); + + int i; + for (i = 0; i < plArmatureEffectsMgr::kMaxSurface; i++) + { + plMaxNode *compNode = (plMaxNode*)fCompPB->GetINode(ParamID(kSurfaceList), 0, i); + if (compNode) + { + plRandomSoundComponent *rsComp = (plRandomSoundComponent *)compNode->ConvertToComponent(); + if (rsComp) + { + plRandomSoundMod *mod = rsComp->fSoundMods[node]; + if (mod != nil) + { + msg = TRACKED_NEW plGenRefMsg(effect->GetKey(), plRefMsg::kOnCreate, i, -1); + hsgResMgr::ResMgr()->AddViaNotify(mod->GetKey(), msg, plRefFlags::kActiveRef); + } + } + } + } + + // Add it to the scene node's generic list, so that all avatars can access it. + plNodeRefMsg* nodeRefMsg = TRACKED_NEW plNodeRefMsg(node->GetRoomKey(), plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kGeneric); + hsgResMgr::ResMgr()->AddViaNotify(effect->GetKey(), nodeRefMsg, plRefFlags::kActiveRef); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +BOOL plFootstepSoundComponentProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + IParamBlock2 *pb = pm->GetParamBlock(); + HWND hSurface = GetDlgItem(hWnd, IDC_COMP_FOOTSTEP_SOUND_SURFACE); + HWND hPick = GetDlgItem(hWnd, IDC_COMP_FOOTSTEP_SOUND_PICK); + INode *curPick = nil; + int curSurface = 0; + + switch (msg) + { + case WM_INITDIALOG: + { + int i; + for (i = 0; i < plArmatureEffectsMgr::kMaxSurface; i++) + ComboBox_AddString(hSurface, plArmatureEffectsMgr::SurfaceStrings[i]); + + curSurface = pb->GetInt(ParamID(plFootstepSoundComponent::kSurface)); + ComboBox_SetCurSel(hSurface, curSurface); + + curPick = pb->GetINode(ParamID(plFootstepSoundComponent::kSurfaceList), 0, curSurface); + Button_SetText(hPick, (curPick == nil ? "None" : curPick->GetName())); + } + return TRUE; + + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + if (LOWORD(wParam) == IDC_COMP_FOOTSTEP_SOUND_PICK) + { + std::vector cids; + cids.push_back(RANDOM_SOUND_COMPONENT_ID); + if (plPick::NodeRefKludge(pb, plFootstepSoundComponent::kNodePicker, &cids, true, false)) + { + curPick = pb->GetINode(ParamID(plFootstepSoundComponent::kNodePicker)); + curSurface = pb->GetInt(ParamID(plFootstepSoundComponent::kSurface)); + pb->SetValue(ParamID(plFootstepSoundComponent::kSurfaceList), 0, curPick, curSurface); + Button_SetText(hPick, (curPick == nil ? "None" : curPick->GetName())); + } + + return TRUE; + } + } + else if (LOWORD(wParam) == IDC_COMP_FOOTSTEP_SOUND_SURFACE) + { + curSurface = ComboBox_GetCurSel(hSurface); + curPick = pb->GetINode(ParamID(plFootstepSoundComponent::kSurfaceList), 0, curSurface); + pb->SetValue(ParamID(plFootstepSoundComponent::kSurface), 0, curSurface); + Button_SetText(hPick, (curPick == nil ? "None" : curPick->GetName())); + } + } + + return FALSE; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootstepComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootstepComponent.h new file mode 100644 index 00000000..2c6e2bed --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plFootstepComponent.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_FOOTSTEP_COMPONENT_H +#define PL_FOOTSTEP_COMPONENT_H + +#include "plComponent.h" + +class plFootstepSoundComponent : public plComponent +{ +public: + plFootstepSoundComponent(); + + virtual void DeleteThis() { delete this; } + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) { return true; } + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum // ParamBlock indices + { + kSurface, + kSurfaceList, + kNodePicker, + }; +}; + +#define FOOTSTEP_SOUND_COMPONENT_CLASS_ID Class_ID(0x15c93f12, 0x4c3f050f) + +class plFootstepSoundComponentProc : public ParamMap2UserDlgProc +{ +public: + plFootstepSoundComponentProc() {} + + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + + void DeleteThis() {} +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUICompClassIDs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUICompClassIDs.h new file mode 100644 index 00000000..51ca2d27 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUICompClassIDs.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plGUICompClassIDs Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plGUICompClassIDs_h +#define _plGUICompClassIDs_h + +#define GUI_DIALOG_COMP_CLASS_ID Class_ID(0x2421463d, 0x6ae12c78) + +#define GUI_UPDOWNPAIR_CLASSID Class_ID(0x3d313f12, 0x76052516) +#define GUI_BUTTON_CLASSID Class_ID(0x7b9b4e5e, 0x61746c00) +#define GUI_DRAGGABLE_CLASSID Class_ID(0x1fdd3182, 0x4bc76749) +#define GUI_LISTBOX_CLASSID Class_ID(0x173304d2, 0x7d9007b2) +#define GUI_TEXTBOX_CLASSID Class_ID(0x37ec36e4, 0x2d202036) +#define GUI_EDITBOX_CLASSID Class_ID(0x430689d, 0x4a570f9a) +#define GUI_KNOBCTRL_CLASSID Class_ID(0x3072431, 0x4f447a04) +#define GUI_DRAGBAR_CLASSID Class_ID(0x2eca7f27, 0x71e70cd0) +#define GUI_CHECKBOX_CLASSID Class_ID(0x5dba1dd4, 0x7e572d4d) +#define GUI_RADIOGROUP_CLASSID Class_ID(0x641754fe, 0x38d52199) +#define GUI_DYNDISPLAY_CLASSID Class_ID(0x3051b96, 0x3c760bd3) +#define GUI_COLORSCHEME_CLASSID Class_ID(0x2b1401bd, 0x41a35d80) +#define GUI_MULTILINE_CLASSID Class_ID(0x10f749f2, 0x3c6e2fdd) +#define GUI_PROGRESS_CLASSID Class_ID(0x13dc5f4b, 0x73e06e68) +#define GUI_CLICKMAP_CLASSID Class_ID(0x64180f8b, 0x434116df) +#define GUI_SKIN_CLASSID Class_ID(0x6f760c1c, 0x65934ba7) +#define GUI_MENUANCHOR_CLASSID Class_ID(0x3756357e, 0x61573fcc) + + +#endif //_plGUICompClassIDs_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUIComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUIComponents.cpp new file mode 100644 index 00000000..838d4ec3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUIComponents.cpp @@ -0,0 +1,5059 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "resource.h" +#include "hsTemplates.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "plMiscComponents.h" +#include "plAnimComponent.h" +#include "plNotetrackAnim.h" + +#include "plGUIComponents.h" + +#include "plAudioComponents.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plGeometrySpan.h" +#include "../plSurface/plLayerInterface.h" +#include "../plSurface/plLayer.h" +#include "../plSurface/hsGMaterial.h" +#include "../plGImage/plMipmap.h" +#include "../plGImage/plDynamicTextMap.h" + +#include "../plMessage/plLayRefMsg.h" +#include "../plMessage/plMatRefMsg.h" + +#include "../MaxMain/plPluginResManager.h" + + +#include "plgDispatch.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" + + +#include "../plScene/plSceneNode.h" +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../MaxConvert/hsMaterialConverter.h" +#include "../MaxConvert/plLayerConverter.h" +#include "../plInterp/plController.h" +#include "../plInterp/plAnimEaseTypes.h" +#include "../MaxMain/plMaxNode.h" +#include "../pnKeyedObject/plKey.h" + +// GUIDialog component. +#include "../plScene/plPostEffectMod.h" +#include "../pfGameGUIMgr/pfGameGUIMgr.h" +#include "../pfGameGUIMgr/pfGUIDialogMod.h" +#include "../pfGameGUIMgr/pfGUIControlMod.h" +#include "../pfGameGUIMgr/pfGUIControlHandlers.h" +#include "../pfGameGUIMgr/pfGUIButtonMod.h" +#include "../pfGameGUIMgr/pfGUIDraggableMod.h" +#include "../pfGameGUIMgr/pfGUIListBoxMod.h" +#include "../pfGameGUIMgr/pfGUITextBoxMod.h" +#include "../pfGameGUIMgr/pfGUIEditBoxMod.h" +#include "../pfGameGUIMgr/pfGUIUpDownPairMod.h" +#include "../pfGameGUIMgr/pfGUIKnobCtrl.h" +#include "../pfGameGUIMgr/pfGUITagDefs.h" +#include "../pfGameGUIMgr/pfGUIDragBarCtrl.h" +#include "../pfGameGUIMgr/pfGUICheckBoxCtrl.h" +#include "../pfGameGUIMgr/pfGUIRadioGroupCtrl.h" +#include "../pfGameGUIMgr/pfGUIDynDisplayCtrl.h" +#include "../pfGameGUIMgr/pfGUIMultiLineEditCtrl.h" +#include "../pfGameGUIMgr/pfGUIProgressCtrl.h" +#include "../pfGameGUIMgr/pfGUIClickMapCtrl.h" +#include "../pfGameGUIMgr/pfGUIPopUpMenu.h" + +// Location Related +#include "../plAgeDescription/plAgeDescription.h" +#include "../MaxMain/plMaxCFGFile.h" +#include "../MaxMain/plAgeDescInterface.h" +#include "../plFile/hsFiles.h" + +#include "../MaxConvert/plConvert.h" +#include "../MaxPlasmaMtls/Layers/plDynamicTextLayer.h" +#include "../MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h" + +#include "../MaxMain/plMaxAccelerators.h" + +#include "plPickMaterialMap.h" + +#include "../plInterp/plController.h" +#include "../plAvatar/plMatrixChannel.h" + +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" + +#include "pfGUISkinComp.h" + +#include "../plResMgr/plLocalization.h" + +#include "plPickLocalizationDlg.h" + +#include +#include + + +void DummyCodeIncludeFuncGUI() {} + + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// Helper Classes ///////////////////////////////////////////////////////////////////////////// + +//// Hit Callback for GUI Controls ////////////////////////////////////////////////////////////// + +class plGUICtrlHitCallback : public HitByNameDlgCallback +{ +protected: + INode* fOwner; + IParamBlock2* fPB; + ParamID fNodeListID; + BOOL fRestrict; + hsTArray fRestrictedIDs; + TCHAR fTitle[ 128 ]; + BOOL fSingle; + +public: + plGUICtrlHitCallback( INode* owner, IParamBlock2 *pb, ParamID nodeListID, TCHAR *title = nil, + BOOL restricted = FALSE, Class_ID rID = GUI_BUTTON_CLASSID, BOOL single = TRUE ) + : fOwner( owner ), fPB( pb ), fNodeListID( nodeListID ), fRestrict( restricted ), fSingle( single ) + + { + fRestrictedIDs.Append( rID ); + strcpy( fTitle, title ); + } + + plGUICtrlHitCallback( INode* owner, IParamBlock2 *pb, ParamID nodeListID, TCHAR *title, + hsTArray &rID ) + : fOwner( owner ), fPB( pb ), fNodeListID( nodeListID ), fRestrict( true ), fSingle(TRUE) + + { + for( int i = 0; i < rID.GetCount(); i++ ) + fRestrictedIDs.Append( rID[ i ] ); + + strcpy( fTitle, title ); + } + + virtual TCHAR *dialogTitle() { return fTitle; } + virtual TCHAR *buttonText() { return "OK"; } + + virtual int filter(INode *node) + { + if( node == fOwner ) + return FALSE; + + plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); + + // If this is an activator type component + if( comp ) + { + if( ( fRestrict && fRestrictedIDs.Find( comp->ClassID() ) != fRestrictedIDs.kMissingIndex ) + || ( !fRestrict && plGUIControlBase::GetGUIComp( comp ) != nil ) ) + { + + // And this wouldn't create a cyclical reference (Max doesn't like those) + if (comp->TestForLoop(FOREVER, fPB) == REF_FAIL) + return FALSE; + + return TRUE; + } + } + else if( fRestrict && fRestrictedIDs.Find( node->ClassID() ) != fRestrictedIDs.kMissingIndex ) + { + return TRUE; + } + + return FALSE; + } + + virtual void proc(INodeTab &nodeTab) + { + if ( nodeTab.Count() > 0 ) + { + if( fSingle ) + fPB->SetValue( fNodeListID, TimeValue(0), nodeTab[0] ); + else + fPB->Append( fNodeListID, nodeTab.Count(), &nodeTab[0] ); + } + } + + virtual BOOL showHiddenAndFrozen() { return TRUE; } + virtual BOOL singleSelect() { return TRUE; } +}; + +//// Single GUI Control Dialog Proc ///////////////////////////////////////////////////////////// + +class plGUISingleCtrlDlgProc : public ParamMap2UserDlgProc +{ +protected: + ParamID fNodeID; + int fDlgItem; + TCHAR fTitle[ 128 ]; + hsTArray fClassesToSelect; + + ParamMap2UserDlgProc *fProcChain; + +public: + + int GetHandledDlgItem( void ) const { return fDlgItem; } + + static const Class_ID kEndClassList; + + plGUISingleCtrlDlgProc( ParamID nodeID, int dlgItem, TCHAR *title, Class_ID *restrict, ParamMap2UserDlgProc *parentProc = nil ) + { + fNodeID = nodeID; + fDlgItem = dlgItem; + for( int i = 0; restrict[ i ] != kEndClassList; i++ ) + fClassesToSelect.Append( restrict[ i ] ); +// fClassToSelect = restrict; + strcpy( fTitle, title ); + fProcChain = parentProc; + } + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) + { + switch ( msg ) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + + INode *node = pb->GetINode( fNodeID ); + TSTR newName( node ? node->GetName() : "Pick" ); + ::SetWindowText( ::GetDlgItem( hWnd, fDlgItem ), newName ); + } + break; + + case WM_COMMAND: + if( ( HIWORD( wParam ) == BN_CLICKED ) ) + { + if( LOWORD( wParam ) == fDlgItem ) + { + IParamBlock2 *pb = map->GetParamBlock(); + plGUICtrlHitCallback hitCB( (INode *)pb->GetOwner(), pb, fNodeID, fTitle, fClassesToSelect ); + GetCOREInterface()->DoHitByNameDialog( &hitCB ); + + INode* node = pb->GetINode( fNodeID ); + TSTR newName( node ? node->GetName() : "Pick" ); + ::SetWindowText( ::GetDlgItem(hWnd, fDlgItem ), newName ); + map->Invalidate( fNodeID ); + ::InvalidateRect( hWnd, NULL, TRUE ); + return true; + } + } + break; + } + + if( fProcChain ) + fProcChain->DlgProc( t, map, hWnd, msg, wParam, lParam ); + + return false; + } + + void DeleteThis() {} +}; + +const Class_ID plGUISingleCtrlDlgProc::kEndClassList = Class_ID(); + +Class_ID sSkinClassesToSelect[] = { GUI_SKIN_CLASSID, plGUISingleCtrlDlgProc::kEndClassList }; + + +//// Multiple GUI Control Dialog Proc /////////////////////////////////////////////////////////// + +class plGUIMultipleCtrlDlgProc : public ParamMap2UserDlgProc +{ +protected: + hsTArray fSingleProcs; + hsTArray fProcs; + +public: + + plGUIMultipleCtrlDlgProc( plGUISingleCtrlDlgProc **singleProcs, ParamMap2UserDlgProc **procs=nil ) + { + for( int i = 0; singleProcs[ i ] != nil; i++ ) + fSingleProcs.Append( singleProcs[ i ] ); + if ( procs ) + { + for( int i = 0; procs[ i ] != nil; i++ ) + fProcs.Append( procs[ i ] ); + } + } + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) + { + int i; + + + switch ( msg ) + { + case WM_INITDIALOG: + for( i = 0; i < fSingleProcs.GetCount(); i++ ) + fSingleProcs[ i ]->DlgProc( t, map, hWnd, msg, wParam, lParam ); + + for( i = 0; i < fProcs.GetCount(); i++ ) + fProcs[ i ]->DlgProc( t, map, hWnd, msg, wParam, lParam ); + + return true; + + case WM_COMMAND: + for( i = 0; i < fSingleProcs.GetCount(); i++ ) + { + if( fSingleProcs[ i ]->GetHandledDlgItem() == LOWORD( wParam ) ) + { + fSingleProcs[ i ]->DlgProc( t, map, hWnd, msg, wParam, lParam ); + break; + } + } + // and now do the procs that want more control + for( i = 0; i < fProcs.GetCount(); i++ ) + fProcs[ i ]->DlgProc( t, map, hWnd, msg, wParam, lParam ); + + return true; + } + + return false; + } + + void DeleteThis() {} +}; + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUITag Component ///////////////////////////////////////////////////////////////////// +// +// GUI element that can be dragged anywhere in the 2D viewing plane. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +class plGUITagProc : public ParamMap2UserDlgProc +{ +protected: + + void ILoadTags( HWND hWnd, IParamBlock2 *pb ); + +public: + + void DeleteThis() {} + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); +}; + +static plGUITagProc gGUITagProc; + +// Class that accesses the paramblock below. +class plGUITagComponent : public plComponent +{ +public: + plGUITagComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefCurrIDSel = 64 // So we can share it among other components + }; + + static UInt32 GetTagIDOnNode( plMaxNode *node ); +}; + +//Max desc stuff necessary below. +#define kGUITagClassID Class_ID(0x77276e84, 0x24f360c5) +CLASS_DESC(plGUITagComponent, gGUITagDesc, "GUI ID Tag", "GUITag", COMP_TYPE_GUI, kGUITagClassID ) + +ParamBlockDesc2 gGUITagBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUITag"), 0, &gGUITagDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_GUITAG, IDS_COMP_GUITAG, 0, 0, &gGUITagProc, + + plGUITagComponent::kRefCurrIDSel, _T("currSel"), TYPE_INT, 0, 0, + end, + + end +); + +void plGUITagProc::ILoadTags( HWND hWnd, IParamBlock2 *pb ) +{ + int idx, idx2 = 0; + char str[] = "(none)"; + + + SendMessage( hWnd, CB_RESETCONTENT, 0, 0 ); + idx2 = idx = SendMessage( hWnd, CB_ADDSTRING, 0, (LPARAM)str ); + SendMessage( hWnd, CB_SETITEMDATA, (WPARAM)idx, (LPARAM)0 ); + + for( UInt32 i = 0; i < pfGameGUIMgr::GetNumTags(); i++ ) + { + pfGUITag *tag = pfGameGUIMgr::GetTag( i ); + idx = SendMessage( hWnd, CB_ADDSTRING, 0, (LPARAM)tag->fName ); + SendMessage( hWnd, CB_SETITEMDATA, (WPARAM)idx, (LPARAM)tag->fID ); + + if( tag->fID == pb->GetInt( plGUITagComponent::kRefCurrIDSel ) ) + idx2 = idx; + } + + if( idx2 == 0 && pb->GetInt( plGUITagComponent::kRefCurrIDSel ) != 0 ) + { + char str[ 32 ]; + sprintf( str, "%d", pb->GetInt( plGUITagComponent::kRefCurrIDSel ) ); + SendMessage( hWnd, WM_SETTEXT, 0, (LPARAM)str ); + } + else + SendMessage( hWnd, CB_SETCURSEL, idx2, 0 ); +} + +// Callback enum proc for below +BOOL CALLBACK GetEditCtrlEnumProc( HWND hWnd, LPARAM lParam ) +{ + char className[ 128 ]; + + + // ICK + GetClassName( hWnd, className, sizeof( className ) - 1 ); + if( stricmp( className, "EDIT" ) == 0 ) + { + HWND *ptr = (HWND *)lParam; + *ptr = hWnd; + return FALSE; + } + return TRUE; +} + +// Small proc that, given the handle of a combo box, returns the handle of the edit window for it +static HWND GetEditCtrlFromComboBox( HWND combo ) +{ + HWND toReturn; + + EnumChildWindows( combo, GetEditCtrlEnumProc, (LPARAM)&toReturn ); + return toReturn; +} + +// Small proc to only allow numbers in an edit box +static WNDPROC sOriginalProc = nil; +LRESULT CALLBACK SubclassedEditProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch( msg ) + { + case WM_CHAR: + if( !isdigit( (TCHAR)wParam ) ) + return 0; + break; + + case WM_GETDLGCODE: + return DLGC_WANTALLKEYS; + + case WM_KEYDOWN: + if( wParam == VK_RETURN ) + { + // Do the same thing as when we lose focus--check our int value and make + // sure it's big enough (don't worry about setting the paramBlock value, + // that'll happen when the control loses focus) + + char str[ 32 ]; + GetWindowText( hWnd, str, sizeof( str ) - 1 ); + int id = atoi( str ); + + if( id < pfGameGUIMgr::GetHighestTag() + 1 ) + { + id = pfGameGUIMgr::GetHighestTag() + 1; + sprintf( str, "%d", id ); + SetWindowText( hWnd, str ); + } + SendMessage( hWnd, EM_SETSEL, 0, (LPARAM)-1 ); + return 0; + } + break; + } + + return CallWindowProc( sOriginalProc, hWnd, msg, wParam, lParam ); +} + +BOOL plGUITagProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + HWND edit; + BOOL dummy1; + + + switch( msg ) + { + case WM_INITDIALOG: + ILoadTags( GetDlgItem( hWnd, IDC_GUI_TAGCOMBO ), pmap->GetParamBlock() ); + + // Set the edit control of the combo box to only accept number characters + edit = GetEditCtrlFromComboBox( GetDlgItem( hWnd, IDC_GUI_TAGCOMBO ) ); + SetWindowLong( edit, GWL_STYLE, GetWindowLong( edit, GWL_STYLE ) | ES_WANTRETURN ); + sOriginalProc = (WNDPROC)SetWindowLong( edit, GWL_WNDPROC, (DWORD)SubclassedEditProc ); + + return true; + + case WM_DESTROY: + SetWindowLong( GetDlgItem( hWnd, IDC_GUI_TAGCOMBO ), GWL_WNDPROC, (DWORD)sOriginalProc ); + break; + + case WM_COMMAND: + if( LOWORD( wParam ) == IDC_GUI_TAGCOMBO ) + { + if( HIWORD( wParam ) == CBN_SELCHANGE ) + { + int idx = SendDlgItemMessage( hWnd, IDC_GUI_TAGCOMBO, CB_GETCURSEL, 0, 0 ); + if( idx == CB_ERR ) + { + // Must be a custom one + int id = GetDlgItemInt( hWnd, IDC_GUI_TAGCOMBO, &dummy1, false ); + pmap->GetParamBlock()->SetValue( plGUITagComponent::kRefCurrIDSel, 0, id ); + } + else + { + pmap->GetParamBlock()->SetValue( plGUITagComponent::kRefCurrIDSel, 0, + SendDlgItemMessage( hWnd, IDC_GUI_TAGCOMBO, CB_GETITEMDATA, idx, 0 ) ); + } + } + else if( HIWORD( wParam ) == CBN_KILLFOCUS ) + { + plMaxAccelerators::Enable(); + + // Make sure the number inside is valid + if( SendDlgItemMessage( hWnd, IDC_GUI_TAGCOMBO, CB_GETCURSEL, 0, 0 ) == CB_ERR ) + { + int id = GetDlgItemInt( hWnd, IDC_GUI_TAGCOMBO, &dummy1, false ); + if( id < pfGameGUIMgr::GetHighestTag() + 1 ) + { + id = pfGameGUIMgr::GetHighestTag() + 1; + SetDlgItemInt( hWnd, IDC_GUI_TAGCOMBO, id, false ); + } + + pmap->GetParamBlock()->SetValue( plGUITagComponent::kRefCurrIDSel, 0, id ); + } + } + else if( HIWORD( wParam ) == CBN_SETFOCUS ) + { + plMaxAccelerators::Disable(); + } + } + break; + } + return false; +} + +plGUITagComponent::plGUITagComponent() +{ + fClassDesc = &gGUITagDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUITagComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plGUITagComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + + return true; +} + +hsBool plGUITagComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +UInt32 plGUITagComponent::GetTagIDOnNode( plMaxNode *node ) +{ + UInt32 i; + + + for( i = 0; i < node->NumAttachedComponents( false ); i++ ) + { + plComponentBase *comp = node->GetAttachedComponent( i, false ); + if( comp->ClassID() == kGUITagClassID ) + { + plGUITagComponent *tag = (plGUITagComponent *)comp; + return tag->GetParamBlockByID( plComponent::kBlkComp )->GetInt( kRefCurrIDSel ); + } + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIColorScheme Component ///////////////////////////////////////////////////////////// +// +// Defines the color scheme for a single control or an entire dialog +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +class plGUIColorSchemeProc : public ParamMap2UserDlgProc +{ +protected: + + void ILoadFonts( HWND hWnd, IParamBlock2 *pb ); + + static int CALLBACK IMyFontEnumProc( const ENUMLOGFONTEX *logFontData, const NEWTEXTMETRICEX *physFontData, + unsigned long fontType, LPARAM lParam ); + +public: + + void DeleteThis() {} + + virtual void Update( TimeValue t, Interval &valid, IParamMap2 *map ); + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); + +}; + +static plGUIColorSchemeProc gGUIColorSchemeProc; + +// Class that accesses the paramblock below. +class plGUIColorSchemeComp : public plComponent +{ +public: + plGUIColorSchemeComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefForeColor = 128, // So we can share it among other components + kRefBackColor, + kRefSelForeColor, + kRefSelBackColor, + kRefUseAlphas, + kRefFontFace, + kRefFontSize, + kRefFontBold, + kRefFontItalic, + + kRefForeAlpha, + kRefBackAlpha, + kRefSelForeAlpha, + kRefSelBackAlpha, + + kRefFontShadowed + }; + + static void ConvertScheme( IParamBlock2 *pb, pfGUIColorScheme *destScheme, plErrorMsg *pErrMsg ); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plGUIColorSchemeComp, gGUIColorSchemeDesc, "GUI Color Scheme", "GUIColorScheme", COMP_TYPE_GUI, GUI_COLORSCHEME_CLASSID ) + +static ParamBlockDesc2 gGUIColorSchemeBk +( + /// Main def + plComponent::kBlkComp, _T("GUIColorScheme"), 0, &gGUIColorSchemeDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + 1, + plGUIDialogComponent::kSchemeRollout, IDD_COMP_GUISCHEME, IDS_COMP_GUISCHEME, 0, 0, &gGUIColorSchemeProc, + + plGUIColorSchemeComp::kRefForeColor, _T("foreColor"), TYPE_RGBA, 0, 0, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_COLORSWATCH, IDC_GUI_FGCOLOR, + p_default, Color( 1.f, 1.f, 1.f ), + end, + + plGUIColorSchemeComp::kRefBackColor, _T("backColor"), TYPE_RGBA, 0, 0, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_COLORSWATCH, IDC_GUI_BGCOLOR, + p_default, Color( 0.f, 0.f, 0.f ), + end, + + plGUIColorSchemeComp::kRefSelForeColor, _T("selForeColor"), TYPE_RGBA, 0, 0, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_COLORSWATCH, IDC_GUI_SFGCOLOR, + p_default, Color( 1.f, 1.f, 1.f ), + end, + + plGUIColorSchemeComp::kRefSelBackColor, _T("selBackColor"), TYPE_RGBA, 0, 0, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_COLORSWATCH, IDC_GUI_SBGCOLOR, + p_default, Color( 0.f, 0.f, 1.f ), + end, + + + plGUIColorSchemeComp::kRefForeAlpha, _T("foreAlpha"), TYPE_FLOAT, 0, 0, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_GUI_FGAEDIT, IDC_GUI_FGALPHA, 4, + p_range, 0.f, 1.f, + p_default, 1.f, + end, + + plGUIColorSchemeComp::kRefBackAlpha, _T("backAlpha"), TYPE_FLOAT, 0, 0, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_GUI_BGAEDIT, IDC_GUI_BGALPHA, 4, + p_range, 0.f, 1.f, + p_default, 1.f, + end, + + plGUIColorSchemeComp::kRefSelForeAlpha, _T("selForeAlpha"), TYPE_FLOAT, 0, 0, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_GUI_SFGAEDIT, IDC_GUI_SFGALPHA, 4, + p_range, 0.f, 1.f, + p_default, 1.f, + end, + + plGUIColorSchemeComp::kRefSelBackAlpha, _T("selBackAlpha"), TYPE_FLOAT, 0, 0, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SLIDER, EDITTYPE_FLOAT, IDC_GUI_SBGAEDIT, IDC_GUI_SBGALPHA, 4, + p_range, 0.f, 1.f, + p_default, 1.f, + end, + + + plGUIColorSchemeComp::kRefUseAlphas, _T("useAlphas"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SINGLECHEKBOX, IDC_GUI_USEALPHAS, + p_enable_ctrls, 4, plGUIColorSchemeComp::kRefForeAlpha, plGUIColorSchemeComp::kRefBackAlpha, + plGUIColorSchemeComp::kRefSelForeAlpha, plGUIColorSchemeComp::kRefSelBackAlpha, + end, + + + plGUIColorSchemeComp::kRefFontFace, _T("fontFace"), TYPE_STRING, 0, 0, + p_default, _T( "Times New Roman" ), + end, + + plGUIColorSchemeComp::kRefFontSize, _T("fontSize"), TYPE_INT, 0, 0, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_GUI_FONTSIZE, IDC_GUI_FONTSIZE_SPIN, SPIN_AUTOSCALE, + p_default, 10, + end, + + plGUIColorSchemeComp::kRefFontBold, _T("fontBold"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SINGLECHEKBOX, IDC_GUI_FONTBOLD, + end, + + plGUIColorSchemeComp::kRefFontItalic, _T("fontItalic"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SINGLECHEKBOX, IDC_GUI_FONTITALIC, + end, + + plGUIColorSchemeComp::kRefFontShadowed, _T("fontShadowed"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plGUIDialogComponent::kSchemeRollout, TYPE_SINGLECHEKBOX, IDC_GUI_FONTSHADOWED, + end, + + end +); + +int CALLBACK plGUIColorSchemeProc::IMyFontEnumProc( const ENUMLOGFONTEX *logFontData, const NEWTEXTMETRICEX *physFontData, + unsigned long fontType, LPARAM lParam ) +{ + HWND combo = (HWND)lParam; + + + if( SendMessage( combo, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)logFontData->elfLogFont.lfFaceName ) == CB_ERR ) + SendMessage( combo, CB_ADDSTRING, 0, (LPARAM)logFontData->elfLogFont.lfFaceName ); + + return -1; +} + +void plGUIColorSchemeProc::ILoadFonts( HWND hWnd, IParamBlock2 *pb ) +{ + LOGFONT logFont; + + + logFont.lfCharSet = DEFAULT_CHARSET; + strcpy( logFont.lfFaceName, "" ); + logFont.lfPitchAndFamily = 0; + + SendMessage( hWnd, CB_RESETCONTENT, 0, 0 ); + + HDC hDC = GetDC( nil ); + EnumFontFamiliesEx( hDC, &logFont, (FONTENUMPROC)IMyFontEnumProc, (LPARAM)hWnd, 0 ); + ReleaseDC( nil, hDC ); + + SendMessage( hWnd, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)pb->GetStr( plGUIColorSchemeComp::kRefFontFace ) ); +} + +#define MAXTOCOLORREF( max ) RGB( max.r * 255.f, max.g * 255.f, max.b * 255.f ) + +void plGUIColorSchemeProc::Update( TimeValue t, Interval &valid, IParamMap2 *pmap ) +{ +} + +BOOL plGUIColorSchemeProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + char str[ 256 ]; + HWND placeCtrl; + PAINTSTRUCT paintInfo; + RECT previewRect, r; + HBRUSH bgPattBrush = nil; + Color fgColor, bgColor, selFgColor, selBgColor; + Color hatchColor = Color( 0.4f, 0.4f, 0.4f ), blendedColor, whiteColor = Color( 0.7f, 0.7f, 0.7f ); + Color blackColor = Color( 0, 0, 0 ), blendedColor2; + float fgAlpha, bgAlpha, selFgAlpha, selBgAlpha; + char previewString[] = "Preview"; + HFONT font; + + + switch( msg ) + { + case WM_INITDIALOG: + ILoadFonts( GetDlgItem( hWnd, IDC_GUI_FONTFACE ), pmap->GetParamBlock() ); + return true; + + case WM_DESTROY: + break; + + case WM_COMMAND: + if( LOWORD( wParam ) == IDC_GUI_FONTFACE ) + { + if( HIWORD( wParam ) == CBN_SELCHANGE ) + { + int idx = SendDlgItemMessage( hWnd, IDC_GUI_FONTFACE, CB_GETCURSEL, 0, 0 ); + + SendDlgItemMessage( hWnd, IDC_GUI_FONTFACE, CB_GETLBTEXT, idx, (LPARAM)str ); + + pmap->GetParamBlock()->SetValue( plGUIColorSchemeComp::kRefFontFace, 0, str ); + } + } + break; + + case CC_COLOR_CHANGE: + case CC_COLOR_DROP: + placeCtrl = ::GetDlgItem( hWnd, IDC_GUI_SCHEMEPREV ); + ::GetClientRect( placeCtrl, &previewRect ); + ::MapWindowPoints( placeCtrl, hWnd, (POINT *)&previewRect, 2 ); + ::InvalidateRect( hWnd, &previewRect, FALSE ); + break; + + case WM_PAINT: + + fgColor = pmap->GetParamBlock()->GetColor( plGUIColorSchemeComp::kRefForeColor ); + bgColor = pmap->GetParamBlock()->GetColor( plGUIColorSchemeComp::kRefBackColor ); + selFgColor = pmap->GetParamBlock()->GetColor( plGUIColorSchemeComp::kRefSelForeColor ); + selBgColor = pmap->GetParamBlock()->GetColor( plGUIColorSchemeComp::kRefSelBackColor ); + + fgAlpha = pmap->GetParamBlock()->GetFloat( plGUIColorSchemeComp::kRefForeAlpha ); + bgAlpha = pmap->GetParamBlock()->GetFloat( plGUIColorSchemeComp::kRefBackAlpha ); + selFgAlpha = pmap->GetParamBlock()->GetFloat( plGUIColorSchemeComp::kRefSelForeAlpha ); + selBgAlpha = pmap->GetParamBlock()->GetFloat( plGUIColorSchemeComp::kRefSelBackAlpha ); + if( pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefUseAlphas ) == 0 ) + fgAlpha = bgAlpha = selFgAlpha = selBgAlpha = 1.f; + + placeCtrl = ::GetDlgItem( hWnd, IDC_GUI_SCHEMEPREV ); + ::GetClientRect( placeCtrl, &previewRect ); + ::MapWindowPoints( placeCtrl, hWnd, (POINT *)&previewRect, 2 ); + + ::BeginPaint( hWnd, &paintInfo ); + ::SetBkMode( paintInfo.hdc, TRANSPARENT ); + + int weight = pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefFontBold ) ? FW_BOLD : FW_NORMAL; + bool italic = pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefFontItalic ) ? true : false; + int nHeight = -MulDiv( pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefFontSize ), GetDeviceCaps( paintInfo.hdc, LOGPIXELSY ), 72 ); + const char *face = pmap->GetParamBlock()->GetStr( plGUIColorSchemeComp::kRefFontFace ); + + font = ::CreateFont( nHeight, 0, 0, 0, weight, italic, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, face ); + SelectObject( paintInfo.hdc, font ); + + // Left side + r = previewRect; + r.right = ( r.right + r.left ) >> 1; + + blendedColor = bgColor * bgAlpha + ( whiteColor * ( 1.f - bgAlpha ) ); + + // doesn't like the Color to DWORD operator, so duplicating it here + #define ColorToDWORD(color) RGB(FLto255(color.r),FLto255(color.g),FLto255(color.b)) + ::SetBkColor( paintInfo.hdc, ColorToDWORD(blendedColor) ); + + blendedColor = bgColor * bgAlpha + ( hatchColor * ( 1.f - bgAlpha ) ); + bgPattBrush = CreateHatchBrush( HS_DIAGCROSS, MAXTOCOLORREF( blendedColor ) ); + + ::FillRect( paintInfo.hdc, &r, bgPattBrush ); + if( pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefFontShadowed ) ) + { + blendedColor2 = blackColor * fgAlpha + ( blendedColor * ( 1.f - fgAlpha ) ); + ::SetTextColor( paintInfo.hdc, MAXTOCOLORREF( blendedColor2 ) ); + ::OffsetRect( &r, 1, 1 ); + ::DrawText( paintInfo.hdc, previewString, strlen( previewString ), &r, DT_CENTER | DT_VCENTER ); + ::OffsetRect( &r, -1, -1 ); + } + + blendedColor = fgColor * fgAlpha + ( blendedColor * ( 1.f - fgAlpha ) ); + ::SetTextColor( paintInfo.hdc, MAXTOCOLORREF( blendedColor ) ); + ::DrawText( paintInfo.hdc, previewString, strlen( previewString ), &r, DT_CENTER | DT_VCENTER ); + + ::DeleteObject( bgPattBrush ); + + // Right side + r.left = r.right; + r.right = previewRect.right; + + blendedColor = selBgColor * selBgAlpha + ( whiteColor * ( 1.f - selBgAlpha ) ); + ::SetBkColor( paintInfo.hdc, ColorToDWORD(blendedColor) ); + blendedColor = selBgColor * selBgAlpha + ( hatchColor * ( 1.f - selBgAlpha ) ); + bgPattBrush = CreateHatchBrush( HS_DIAGCROSS, MAXTOCOLORREF( blendedColor ) ); + + ::FillRect( paintInfo.hdc, &r, bgPattBrush ); + if( pmap->GetParamBlock()->GetInt( plGUIColorSchemeComp::kRefFontShadowed ) ) + { + blendedColor2 = blackColor * selFgAlpha + ( blendedColor * ( 1.f - selFgAlpha ) ); + ::SetTextColor( paintInfo.hdc, MAXTOCOLORREF( blendedColor2 ) ); + ::OffsetRect( &r, 1, 1 ); + ::DrawText( paintInfo.hdc, previewString, strlen( previewString ), &r, DT_CENTER | DT_VCENTER ); + ::OffsetRect( &r, -1, -1 ); + } + blendedColor = selFgColor * selFgAlpha + ( blendedColor * ( 1.f - selFgAlpha ) ); + ::SetTextColor( paintInfo.hdc, MAXTOCOLORREF( blendedColor ) ); + ::DrawText( paintInfo.hdc, previewString, strlen( previewString ), &r, DT_CENTER | DT_VCENTER ); + + ::DeleteObject( bgPattBrush ); + + ::DeleteObject( font ); + + ::EndPaint( hWnd, &paintInfo ); + + return true; + } + return false; +} + +plGUIColorSchemeComp::plGUIColorSchemeComp() +{ + fClassDesc = &gGUIColorSchemeDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIColorSchemeComp::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plGUIColorSchemeComp::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + + return true; +} + +hsBool plGUIColorSchemeComp::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + pfGUIControlMod *ctrl = plGUIControlBase::GrabControlFromObject( node ); + if( ctrl != nil ) + { + pfGUIColorScheme *cs = TRACKED_NEW pfGUIColorScheme; + ConvertScheme( fCompPB, cs, pErrMsg ); + ctrl->SetColorScheme( cs ); + } + else + { + pErrMsg->Set( true, "GUI Color Scheme Error", "You have applied a GUI color scheme to an object (%s) without a GUI control. This scheme will be ignored.", node->GetName()).Show(); + pErrMsg->Set( false ); + return false; + } + + return true; +} + +void SMaxRGBAToPlasmaRGBA( Color maxRGB, hsColorRGBA &plasmaRGBA ) +{ + plasmaRGBA.Set( maxRGB.r, maxRGB.g, maxRGB.b, 1.f ); +} + +void plGUIColorSchemeComp::ConvertScheme( IParamBlock2 *pb, pfGUIColorScheme *destScheme, plErrorMsg *pErrMsg ) +{ + SMaxRGBAToPlasmaRGBA( pb->GetColor( kRefForeColor ), destScheme->fForeColor ); + SMaxRGBAToPlasmaRGBA( pb->GetColor( kRefBackColor ), destScheme->fBackColor ); + SMaxRGBAToPlasmaRGBA( pb->GetColor( kRefSelForeColor ), destScheme->fSelForeColor ); + SMaxRGBAToPlasmaRGBA( pb->GetColor( kRefSelBackColor ), destScheme->fSelBackColor ); + + destScheme->fForeColor.a = pb->GetFloat( kRefForeAlpha ); + destScheme->fBackColor.a = pb->GetFloat( kRefBackAlpha ); + destScheme->fSelForeColor.a = pb->GetFloat( kRefSelForeAlpha ); + destScheme->fSelBackColor.a = pb->GetFloat( kRefSelBackAlpha ); + + destScheme->fTransparent = pb->GetInt( kRefUseAlphas ) ? true : false; + + destScheme->SetFontFace( pb->GetStr( kRefFontFace ) ); + destScheme->fFontSize = pb->GetInt( kRefFontSize ); + destScheme->fFontFlags = 0; + if( pb->GetInt( kRefFontBold ) ) + destScheme->fFontFlags |= pfGUIColorScheme::kFontBold; + if( pb->GetInt( kRefFontItalic ) ) + destScheme->fFontFlags |= pfGUIColorScheme::kFontItalic; + if( pb->GetInt( kRefFontShadowed ) ) + destScheme->fFontFlags |= pfGUIColorScheme::kFontShadowed; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIProxy Rollout ///////////////////////////////////////////////////////////////////////// +// +// Defines a proxy object to be used when calculating mouse-down locations and dynamic text +// sizing. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +enum plProxyRefs +{ + kRefProxyNode = 196, + kRefHideProxy, + kRefBetterHitTests +}; + +//// DialogProc ///////////////////////////////////////////////////////////////////////////////// + +class plGUIProxyDlgProc : public ParamMap2UserDlgProc +{ +public: + + plGUIProxyDlgProc() + { + } + + void DeleteThis() {} + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IParamBlock2 *pblock = map->GetParamBlock(); + + switch( msg ) + { + case WM_COMMAND: +// if( LOWORD( wParam ) == IDC_GUI_CLEAR ) +// { +// pblock->Reset( (ParamID)kRefProxyNode ); +// return true; +// } + break; + } + return false; + } + +}; + +static plGUIProxyDlgProc sGUIProxyDlgProc; + +//// ParamBlock ///////////////////////////////////////////////////////////////////////////////// +// Note: we can't make this a real ParamBlock and do P_INCLUDE_PARAMS because, in Discreet's +// amazing method of doing things, we can't INCLUDE more than one ParamBlock in any other PB. +// So either we chain them together here (and thus make them dependent on one another, which +// is lame) or we just make the whole damned thing a #define, which is all P_INCLUDE_PARAMS +// really does anyway. + +#define sGUIProxyParamHeader plGUIControlBase::kRollProxy, IDD_COMP_GUIPROXY, IDS_COMP_GUIPROXY, 0, APPENDROLL_CLOSED, &sGUIProxyDlgProc +//static ParamBlockDesc2 sSndEAXPropsParamTemplate +//( + /// Main def +// plComponent::kBlkComp + 1, _T("sndEAXProps"), 0, nil, P_AUTO_UI + P_MULTIMAP + P_AUTO_CONSTRUCT, plComponent::kRefComp, + +// 1, +// kSndEAXParams, IDD_COMP_EAXBUFFER, IDS_COMP_EAXBUFFER, 0, 0, nil, + +#define sGUIProxyParamTemplate \ + \ + kRefBetterHitTests, _T("guiBetterHitTests"), TYPE_BOOL, 0, 0, \ + p_ui, plGUIControlBase::kRollProxy, TYPE_SINGLECHEKBOX, IDC_GUI_BETTERHIT, \ + p_default, false, \ + end + +// , end +//); + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIDialog Component ////////////////////////////////////////////////////////////////////// +// +// Defines a dialog box (i.e. a collection of controls) to be defined with the GUI manager at +// runtime. Acts a lot like a CamView component, but it additionally handles a few other things. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +class plGUIDialogProc : public ParamMap2UserDlgProc +{ +protected: + + void ILoadPages( HWND hWnd, IParamBlock2 *pb ); + +public: + + void DeleteThis() {} + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); +}; +static plGUIDialogProc gGUIDialogProc; + +//Max desc stuff necessary below. +CLASS_DESC(plGUIDialogComponent, gGUIDialogDesc, "GUI Dialog", "GUIDialog", COMP_TYPE_GUI, GUI_DIALOG_COMP_CLASS_ID ) + +ParamBlockDesc2 gGUIDialogBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIDialog"), 0, &gGUIDialogDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 3, + plGUIDialogComponent::kMainRollout, IDD_COMP_GUIDIALOG, IDS_COMP_GUIDIALOG, 0, 0, &gGUIDialogProc, + plGUIDialogComponent::kTagIDRollout, IDD_COMP_GUITAG, IDS_COMP_GUITAG, 0, 0, &gGUITagProc, + plGUIDialogComponent::kSchemeRollout, IDD_COMP_GUISCHEME, IDS_COMP_GUISCHEME, 0, 0, &gGUIColorSchemeProc, + + &gGUIColorSchemeBk, + + plGUIDialogComponent::kRefDialogName, _T("DialogName"), TYPE_STRING, 0, 0, +// p_ui, plGUIDialogComponent::kMainRollout, TYPE_EDITBOX, IDC_GUIDLG_NAME, + end, + + plGUIDialogComponent::kRefAgeName, _T("ageName"), TYPE_STRING, 0, 0, + p_default, _T( "GUI" ), + end, + + plGUIDialogComponent::kRefIsModal, _T("isModal"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plGUIDialogComponent::kMainRollout, TYPE_SINGLECHEKBOX, IDC_COMP_GUI_MODAL, + end, + + plGUIDialogComponent::kRefVersion, _T("version"), TYPE_INT, 0, 0, + p_ui, plGUIDialogComponent::kMainRollout, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_GUI_VERSION, IDC_GUI_VERSION_SPIN, SPIN_AUTOSCALE, + p_default, 0, + end, + + plGUITagComponent::kRefCurrIDSel, _T("currSel"), TYPE_INT, 0, 0, + end, + + end +); + +plGUIDialogComponent::plGUIDialogComponent( hsBool dontInit ) +{ + if( !dontInit ) + { + fClassDesc = &gGUIDialogDesc; + fClassDesc->MakeAutoParamBlocks(this); + } + fDialogMod = nil; + fProcReceiver = nil; +} + +pfGUIDialogMod *plGUIDialogComponent::IMakeDialog( void ) +{ + return TRACKED_NEW pfGUIDialogMod(); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIDialogComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + TimeValue timeVal( 0 ); + Object* obj = node->EvalWorldState( timeVal ).obj; + + fDialogMod = nil; + + if( obj->CanConvertToType( Class_ID( LOOKAT_CAM_CLASS_ID, 0 ) ) || + obj->CanConvertToType( Class_ID( SIMPLE_CAM_CLASS_ID, 0 ) ) ) + { + // We're applied to a camera. Do our camera stuff + node->SetForceLocal( true ); + } + else + { + // We're applied to a normal object. + node->SetNoSpanReSort(true); + node->SetNoSpanSort(true); + } + + /// Either way, we mangle our own location component. None of this user-defined-location stuff. + + char *dialogName = fCompPB->GetStr( kRefDialogName ); + if( dialogName == nil || *dialogName == 0 ) + { + pErrMsg->Set(true, "GUI Dialog Component Error", "No dialog name specified on GUI Dialog component (object: %s)", node->GetName()).Show(); + return false; + } + + char *ageName = fCompPB->GetStr(kRefAgeName); + Int32 seqNum = plPageInfoUtils::GetSeqNumFromAgeDesc( ageName, dialogName ); + Int32 newNum = plPluginResManager::ResMgr()->VerifySeqNumber( seqNum, ageName, dialogName ); + if( newNum != seqNum ) + { + if( !fSeqNumValidated ) + { + plLocation pageLoc = plPluginResManager::ResMgr()->FindLocation( ageName, dialogName ); + Int32 pageSeqNum = pageLoc.GetSequenceNumber(); + char errMsg[ 512 ]; + sprintf( errMsg, "The sequence number stored by the resource manager (0x%X) for page %s, District, %s does not match\n" + "the sequence number stored in the .age file (0x%X). Forcing it to use the one in the .age file", + pageSeqNum, ageName, dialogName, seqNum ); + pErrMsg->Set( true, "PageInfo Convert Error", errMsg ).Show(); + pErrMsg->Set( false ); + fSeqNumValidated = true; + } + // force the component to use the sequence number in the .age file + //seqNum = newNum; + } + + plKey roomKey = plPluginResManager::ResMgr()->NameToLoc( fCompPB->GetStr( kRefAgeName ), fCompPB->GetStr( kRefDialogName ), seqNum ); + if( !roomKey ) + { + pErrMsg->Set( true, "GUI Dialog Component Error", "GUI Dialog Component %s has a Missing Location. Nuke the files in the dat directory and re-export.",((INode*)node)->GetName()).Show(); + return false; + } + + node->SetRoomKey( roomKey ); + + // Also, we make sure this node will never be fogged (affects material convert) + node->SetIsGUI( true ); + + return true; +} + +hsBool plGUIDialogComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + TimeValue timeVal(0); + + Object* obj = node->EvalWorldState(timeVal).obj; + + GenCamera* cam = nil; + if( obj->CanConvertToType(Class_ID(LOOKAT_CAM_CLASS_ID, 0)) ) + cam = (GenCamera *) obj->ConvertToType(timeVal, Class_ID(LOOKAT_CAM_CLASS_ID, 0)); + else + if( obj->CanConvertToType(Class_ID(SIMPLE_CAM_CLASS_ID, 0)) ) + cam = (GenCamera *) obj->ConvertToType(timeVal, Class_ID(SIMPLE_CAM_CLASS_ID, 0)); + + if( !cam ) + { + // Not applied to a camera, so applied to a normal object. Since this is valid (we also act + // as a location component), just return + return true; + } + + plPostEffectMod* mod = TRACKED_NEW plPostEffectMod; + + float hither = cam->GetEnvRange(timeVal, ENV_NEAR_RANGE); + if( hither < 0.5f ) + hither = 0.5f; + float yon = cam->GetEnvRange(timeVal, ENV_FAR_RANGE); + mod->SetHither(hither); + mod->SetYon(yon); + + // radians + float fov = cam->GetFOV(timeVal); + // convert + int FOVType = cam->GetFOVType(); + hsScalar fovX, fovY; + switch(FOVType) + { + case 0: // FOV_W + { + fovX = fov; + fovY = fovX *3.f / 4.f; + } + break; + case 1: // FOV_H + { + fovY = fov; + fovX = fovY * 4.f / 3.f; + } + break; + } + fovX *= 180.f / hsScalarPI; + fovY *= 180.f / hsScalarPI; + mod->SetFovX(fovX); + mod->SetFovY(fovY); + + // Should already be created from SetupProperties... + // Note: can't just grab the node's room key, 'cause we might not be on the right node! + plKey sceneNodeKey = plPluginResManager::ResMgr()->NameToLoc( fCompPB->GetStr( kRefAgeName ), + fCompPB->GetStr( kRefDialogName ), (UInt32)-1 ); + mod->SetNodeKey( sceneNodeKey ); + +// node->AddModifier(mod); + // Note: we do NOT add this to the sceneObject, we don't want it actually associated with + // a sceneObject. Instead, we just grab the LocalToWorld() off the sceneObject, since that's + // all we want + hsMatrix44 l2w = node->GetLocalToWorld44(); + hsMatrix44 w2l = node->GetWorldToLocal44(); + mod->SetWorldToCamera( w2l, l2w ); + + // Add it to the sceneNode as a generic interface, so it gets loaded with the sceneNode + plLocation nodeLoc = sceneNodeKey->GetUoid().GetLocation(); + + plKey modKey = hsgResMgr::ResMgr()->NewKey( fCompPB->GetStr( kRefDialogName ), mod, nodeLoc ); + hsgResMgr::ResMgr()->AddViaNotify( modKey, TRACKED_NEW plNodeRefMsg( sceneNodeKey, plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric ), plRefFlags::kActiveRef ); + + // Also add our dialog mod to the scene node in the same way + hsgResMgr::ResMgr()->AddViaNotify( fDialogMod->GetKey(), TRACKED_NEW plNodeRefMsg( sceneNodeKey, plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric ), plRefFlags::kActiveRef ); + + /// Already created our mod, just gotta fill it out + fDialogMod->SetRenderMod( mod ); + fDialogMod->SetName( fCompPB->GetStr( kRefDialogName ) ); + if( fCompPB->GetInt( kRefIsModal ) ) + fDialogMod->SetFlag( pfGUIDialogMod::kModal ); + fDialogMod->SetProcReceiver(fProcReceiver); + fDialogMod->SetVersion( fCompPB->GetInt( kRefVersion ) ); + + plGUIColorSchemeComp::ConvertScheme( fCompPB, fDialogMod->GetColorScheme(), pErrMsg ); + + return true; +} + +hsBool plGUIDialogComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + TimeValue timeVal(0); + Object* obj = node->EvalWorldState(timeVal).obj; + + if( obj->CanConvertToType(Class_ID(LOOKAT_CAM_CLASS_ID, 0)) + || obj->CanConvertToType(Class_ID(SIMPLE_CAM_CLASS_ID, 0)) ) + { + // Don't do this. -mf +// IMakeEveryoneOpaque(node); + + // Make a blank dialog modifier, which will be filled in on convert. Do + // this as a separate step so the dialog controls can query and get the dialog + // mod to store a ref to + + fDialogMod = IMakeDialog(); + + // Note: can't just grab the node's room key, 'cause we might not be on the right node! + plKey sceneNodeKey = plPluginResManager::ResMgr()->NameToLoc( fCompPB->GetStr( kRefAgeName ), + fCompPB->GetStr( kRefDialogName ), (UInt32)-1 ); + + plLocation nodeLoc = sceneNodeKey->GetUoid().GetLocation(); + plKey dlgKey = hsgResMgr::ResMgr()->NewKey( fCompPB->GetStr( kRefDialogName ), fDialogMod, nodeLoc ); + + fDialogMod->SetSceneNodeKey( sceneNodeKey ); + + // See if there's a tag to be had + UInt32 id = fCompPB->GetInt( plGUITagComponent::kRefCurrIDSel ); + if( id > 0 ) + fDialogMod->SetTagID( id ); + + fProcReceiver = nil; + } + else + { + } + + return true; +} + +void plGUIDialogComponent::IMakeEveryoneOpaque(plMaxNode* node) +{ + plMaxNode* root = (plMaxNode *)node->GetInterface()->GetRootNode(); + + int i; + for( i = 0; i < root->NumberOfChildren(); i++ ) + IMakeEveryoneOpaqueRecur((plMaxNode*)(root->GetChildNode(i))); + +} + +void plGUIDialogComponent::IMakeEveryoneOpaqueRecur(plMaxNode* node) +{ + if( node->CanConvert() ) + { + node->SetNoSpanReSort(true); + node->SetNoSpanSort(true); + + int i; + for( i = 0; i < node->NumberOfChildren(); i++ ) + { + IMakeEveryoneOpaqueRecur((plMaxNode *)(node->GetChildNode(i))); + } + } +} + +plKey plGUIDialogComponent::GetModifierKey( void ) +{ + if( fDialogMod != nil ) + return fDialogMod->GetKey(); + + return nil; +} + +bool plGUIDialogComponent::SetNotifyReceiver( plKey key ) +{ + if( fProcReceiver != nil ) + return false; + + fProcReceiver = key; + return true; +} + +pfGUIDialogMod *plGUIDialogComponent::GetNodeDialog( plMaxNode *childNode ) +{ + UInt32 i, numComp = childNode->NumAttachedComponents( false ); + for( i = 0; i < numComp; i++ ) + { + plComponentBase *comp = childNode->GetAttachedComponent( i ); + if( comp->ClassID() == GUI_DIALOG_COMP_CLASS_ID ) + return ( (plGUIDialogComponent *)comp )->GetModifier(); + } + + return nil; +} + +void plGUIDialogProc::ILoadPages( HWND hWnd, IParamBlock2 *pb ) +{ + plAgeDescription *aged = plPageInfoUtils::GetAgeDesc( pb->GetStr( plGUIDialogComponent::kRefAgeName ) ); + + if( aged == nil ) + return; + + plAgePage *page; + char *selPageName = pb->GetStr( plGUIDialogComponent::kRefDialogName ); + aged->SeekFirstPage(); + ComboBox_ResetContent( hWnd ); + + while( ( page = aged->GetNextPage() ) != nil ) + { + int idx = ComboBox_AddString( hWnd, page->GetName() ); + if( selPageName && stricmp( page->GetName(), selPageName ) == 0 ) + ComboBox_SetCurSel( hWnd, idx ); + } + + delete aged; +} + +BOOL plGUIDialogProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch( msg ) + { + case WM_INITDIALOG: + // Load the age combo box + { + int i, idx, selIdx = 0; + HWND ageCombo = GetDlgItem( hWnd, IDC_GUIDLG_AGE ); + hsTArray ageList; + + plAgeDescInterface::BuildAgeFileList( ageList ); + ComboBox_ResetContent( ageCombo ); + for( i = 0; i < ageList.GetCount(); i++ ) + { + char ageName[ _MAX_FNAME ]; + _splitpath( ageList[ i ], nil, nil, ageName, nil ); + + idx = ComboBox_AddString( ageCombo, ageName ); + if( stricmp( ageName, pmap->GetParamBlock()->GetStr( plGUIDialogComponent::kRefAgeName ) ) == 0 ) + { + selIdx = idx; + } + } + ComboBox_SetCurSel( ageCombo, selIdx ); + } + + ILoadPages( GetDlgItem( hWnd, IDC_GUIDLG_NAME ), pmap->GetParamBlock() ); + return true; + + case WM_DESTROY: + break; + + case WM_COMMAND: + if( HIWORD( wParam ) == CBN_SELCHANGE ) + { + if( LOWORD( wParam ) == IDC_GUIDLG_NAME ) + { + int idx = SendDlgItemMessage( hWnd, IDC_GUIDLG_NAME, CB_GETCURSEL, 0, 0 ); + if( idx != CB_ERR ) + { + char name[ 256 ]; + ComboBox_GetLBText( GetDlgItem( hWnd, IDC_GUIDLG_NAME ), idx, name ); + pmap->GetParamBlock()->SetValue( plGUIDialogComponent::kRefDialogName, 0, name ); + } + } + else if( LOWORD( wParam ) == IDC_GUIDLG_AGE ) + { + int idx = SendDlgItemMessage( hWnd, IDC_GUIDLG_AGE, CB_GETCURSEL, 0, 0 ); + if( idx != CB_ERR ) + { + char name[ 256 ]; + ComboBox_GetLBText( GetDlgItem( hWnd, IDC_GUIDLG_AGE ), idx, name ); + pmap->GetParamBlock()->SetValue( plGUIDialogComponent::kRefAgeName, 0, name ); + } + + ILoadPages( GetDlgItem( hWnd, IDC_GUIDLG_NAME ), pmap->GetParamBlock() ); + } + } + break; + } + return false; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIControl Component Base Class ////////////////////////////////////////////////////////// +// +// Defines a base class for all GUI control components. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +void plGUIControlBase::CollectNonDrawables( INodeTab &nonDrawables ) +{ +/* if( ICanHaveProxy() ) + { + bool hideProxy = fCompPB->GetInt( (ParamID)kRefHideProxy ) ? true : false; + if( hideProxy ) + { + INode *node = fCompPB->GetINode( (ParamID)kRefProxyNode ); + if( node != nil ) + nonDrawables.Append( 1, &node ); + } + } +*/ +} + +pfGUIDialogMod *plGUIControlBase::IGetDialogMod( plMaxNode *node ) +{ + UInt32 i; + + + for( i = 0; i < node->NumAttachedComponents( false ); i++ ) + { + plComponentBase *comp = node->GetAttachedComponent( i, false ); + if( comp->ClassID() == GUI_DIALOG_COMP_CLASS_ID ) + { + // Found it! + pfGUIDialogMod *dlgMod = ((plGUIDialogComponent *)comp)->GetModifier(); + return dlgMod; + } + } + + return nil; +} + +hsBool plGUIControlBase::SetupProperties( plMaxNode *pNode, plErrorMsg *pErrMsg ) +{ + if( INeedsDynamicText() ) + { + // If we're going to be using a dynamic text layer, we need to make sure the material + // is unique for every node we're applied to + pNode->SetForceMaterialCopy( true ); + } + + return true; +} + +hsBool plGUIControlBase::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + // Create a new control + fControl = IGetNewControl(); + + // Add it as a modifier to this node + node->AddModifier( fControl, IGetUniqueName(node) ); + + // Look for any tag IDs + UInt32 id = plGUITagComponent::GetTagIDOnNode( node ); + if( id > 0 ) + fControl->SetTagID( id ); + + // Now add it to our list of converted nodes + UInt32 i = fTargetNodes.Find( node ); + if( i == fTargetNodes.kMissingIndex ) + { + fTargetNodes.Append( node ); + fTargetControls.Append( fControl ); + } + else + { + fTargetControls[ i ] = fControl; + } + + return true; +} + +hsBool plGUIControlBase::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + // Error check--make sure we're in the same room as our parent (can get confusing with the wrong + // parent-child relationships) + if( !node->GetParentNode()->IsRootNode() ) + { + plMaxNode *parent = (plMaxNode *)node->GetParentNode(); + if( parent->GetRoomKey() != node->GetRoomKey() ) + { + pErrMsg->Set( true, "GUI Control Component Error", "The object %s is assigned to a different GUI dialog than its parent. Make sure both this object and its parent belong to the same GUI dialog (this control will be ignored).", node->GetName() ).Show(); + pErrMsg->Set( false ); + return false; + } + } + + pfGUIDialogMod *dialog = IGetDialogMod( node ); + if( dialog == nil ) + { + pErrMsg->Set( true, "GUI Control Component Error", "The object %s has a GUI control applied but not a GUI Dialog Component. Apply a GUI Dialog Component to this object.", node->GetName() ).Show(); + pErrMsg->Set( false ); + return false; + } + + // Grab fControl from the modifier list on the node, since fControl isn't valid + // between PreConvert() and Convert() (it might get called multiple times, once per node applied) + UInt32 i = fTargetNodes.Find( node ); + if( i == fTargetNodes.kMissingIndex ) + { + pErrMsg->Set( true, "GUI Control Component Error", "The object %s somehow skipped the GUI control Pre-convert stage. Inform a programmer immediately and seek shelter.", node->GetName() ).Show(); + pErrMsg->Set( false ); + return false; + } + + fControl = fTargetControls[ i ]; + + dialog->AddControlOnExport( fControl ); + + if( IHasProcRollout() ) + { + // Also common for all controls: process the Procedure rollout--i.e. what kind of control proc do we get? + switch( fCompPB->GetInt( kRefChoice ) ) + { + case 0: + // Console command + fControl->SetHandler( TRACKED_NEW pfGUIConsoleCmdProc( fCompPB->GetStr( kRefConsoleCmd ) ) ); + break; + + case 1: + // Inherit from parent dialog - this is a runtime flag, so we don't bother actually setting + // a handler here, except to ensure it's nil + fControl->SetHandler( nil ); + fControl->SetFlag( pfGUIControlMod::kInheritProcFromDlg ); + break; + + case 2: + fControl->SetHandler( TRACKED_NEW pfGUICloseDlgProc() ); + break; + + case 3: + // Do nothing. Just set a nil proc, but do NOT inherit from the dialog + fControl->SetHandler( nil ); + fControl->ClearFlag( pfGUIControlMod::kInheritProcFromDlg ); + break; + } + } + + if( INeedsDynamicText() ) + { + // We're a control that dynamically creates text, so look for the first dynamic layer + // (and hopefully the ONLY one) and store it on the control + Mtl *maxMaterial = hsMaterialConverter::Instance().GetBaseMtl( node ); + hsTArray *mtlArray = hsMaterialConverter::Instance().CreateMaterialArray( maxMaterial, node, 0 ); + + UInt32 i, j; + plDynamicTextMap *dynText = nil; + plLayerInterface *layerIFace = nil; + + for( i = 0; i < mtlArray->GetCount() && dynText == nil; i++ ) + { + hsGMaterial *plasmaMat = (*mtlArray)[ 0 ].fMaterial; + + for( j = 0; j < plasmaMat->GetNumLayers(); j++ ) + { + layerIFace = plasmaMat->GetLayer( j ); + dynText = plDynamicTextMap::ConvertNoRef( layerIFace->GetTexture() ); + if( dynText != nil ) + break; + } + } + + if( dynText == nil ) + { + pErrMsg->Set( true, "GUI Component Error", "The object %s needs a Plasma Dynamic Text Layer in its material. " + "This control will not function properly until you apply one.", node->GetName() ).Show(); + pErrMsg->Set( false ); + } + else + fControl->SetDynTextMap( layerIFace, dynText ); + + delete mtlArray; + } + + if( ICanHaveProxy() ) + { + // No proxy objects just yet, just options for better hit testing + if( fCompPB->GetInt( kRefBetterHitTests ) ) + fControl->SetFlag( pfGUIControlMod::kBetterHitTesting ); + } + + return true; +} + +pfGUIControlMod *plGUIControlBase::GrabControlFromObject( INode *node ) +{ + UInt32 i; + plMaxNodeBase *maxNode = (plMaxNodeBase *)node; + + + for( i = 0; i < maxNode->NumAttachedComponents( false ); i++ ) + { + plComponentBase *comp = maxNode->GetAttachedComponent( i, false ); + pfGUIControlMod *ctrl = ConvertCompToControl( comp, maxNode ); + if( ctrl != nil ) + return ctrl; + } + + return nil; +} + +// Given an INode, gives you a pointer to the GUI component if it actually is one, nil otherwise +plGUIControlBase *plGUIControlBase::GetGUIComp( INode *node ) +{ + if( node == nil ) + return nil; + + return GetGUIComp( ( ( plMaxNodeBase *)node )->ConvertToComponent() ); +} + +plGUIControlBase *plGUIControlBase::GetGUIComp( plComponentBase *comp ) +{ + if( comp == nil ) + return nil; + + if( comp->ClassID() == GUI_UPDOWNPAIR_CLASSID || + comp->ClassID() == GUI_BUTTON_CLASSID || + comp->ClassID() == GUI_DRAGGABLE_CLASSID || + comp->ClassID() == GUI_LISTBOX_CLASSID || + comp->ClassID() == GUI_TEXTBOX_CLASSID || + comp->ClassID() == GUI_EDITBOX_CLASSID || + comp->ClassID() == GUI_KNOBCTRL_CLASSID || + comp->ClassID() == GUI_DRAGBAR_CLASSID || + comp->ClassID() == GUI_CHECKBOX_CLASSID || + comp->ClassID() == GUI_RADIOGROUP_CLASSID || + comp->ClassID() == GUI_DYNDISPLAY_CLASSID || + comp->ClassID() == GUI_MULTILINE_CLASSID || + comp->ClassID() == GUI_PROGRESS_CLASSID || + comp->ClassID() == GUI_CLICKMAP_CLASSID ) + { + return (plGUIControlBase *)comp; + } + + return nil; +} + +pfGUIControlMod *plGUIControlBase::GrabControlMod( INode *node, INode *sceneObjectNode ) +{ + if( node == nil ) + return nil; + + plComponentBase *comp = ( ( plMaxNodeBase *)node )->ConvertToComponent(); + return ConvertCompToControl( comp, sceneObjectNode ); +} + +pfGUIControlMod *plGUIControlBase::ConvertCompToControl( plComponentBase *comp, INode *sceneObjectNode ) +{ + plGUIControlBase *base = GetGUIComp( comp ); + if( base != nil ) + { + if( sceneObjectNode == nil ) + { + // Not good, but if you select a component like this, it better only be applied to one object, + // hence will only have one fTargetControl + if( base->fTargetControls.GetCount() > 0 ) + return base->fTargetControls[ 0 ]; + } + else + { + UInt32 i = base->fTargetNodes.Find( (plMaxNode *)sceneObjectNode ); + if( i == base->fTargetNodes.kMissingIndex ) + return nil; + + return base->fTargetControls[ i ]; + } + } + + return nil; +} + +const char *plGUIControlBase::ISetSoundIndex( ParamID checkBoxID, ParamID sndCompID, UInt8 guiCtrlEvent, plMaxNode *maxNode ) +{ + if( fCompPB->GetInt( checkBoxID ) ) + { + plMaxNode *sndNode = (plMaxNode *)fCompPB->GetReferenceTarget( sndCompID ); + if( sndNode != nil ) + { + plComponentBase *comp = sndNode->ConvertToComponent(); + if( comp != nil ) + { + int idx = plAudioComp::GetSoundModIdx( comp, maxNode ); + if( idx != -1 ) + { + fControl->SetSoundIndex( guiCtrlEvent, idx ); + return nil; + } + else + return "The selected sound component could not be found on GUI control %s. Make sure you have a sound component on the same object selected."; + } + else + return "The selected sound node on GUI control %s could not be converted to a component. Make sure you have a sound component selected."; + } + else + return "The GUI control %s has a sound event enabled but no sound component selected. Make sure you have a sound component on the same object selected."; + } + + return nil; +} + + +//// ParamBlock for Control Proc Rollout //////////////////////////////////////////////////////// + +static ParamBlockDesc2 sGUIControlProcParamTemplate +( + /// Main def + plGUIControlBase::kBlkProc, _T("GUIControlProc"), 0, nil, P_AUTO_UI + P_MULTIMAP + P_AUTO_CONSTRUCT, plComponent::kRefComp, + + 1, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + plGUIControlBase::kRefChoice, _T("which"), TYPE_INT, 0, 0, + p_ui, plGUIControlBase::kRollProc, TYPE_RADIO, 4, IDC_GUI_CONRADIO, IDC_GUI_INHERITRADIO, IDC_GUI_CLOSERADIO, IDC_GUI_NILRADIO, + p_default, 1, + end, + + plGUIControlBase::kRefConsoleCmd, _T("ConsoleCmd"), TYPE_STRING, 0, 0, + p_ui, plGUIControlBase::kRollProc, TYPE_EDITBOX, IDC_GUI_CONCMD, + end, + + end +); + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIButton Component ////////////////////////////////////////////////////////////////////// +// +// Defines a dialog button to be defined with the GUI manager at runtime. Belongs to exactly +// one dialog, defined by parent-child relationship, also at runtime. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +class plGUIButtonProc : public ParamMap2UserDlgProc +{ +public: + + void DeleteThis() {} + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); +}; + + +static plGUIButtonProc gGUIButtonProc; + + +// Class that accesses the paramblock below. +class plGUIButtonComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIButtonMod; } + virtual bool ICanHaveProxy( void ) { return true; } + +public: + plGUIButtonComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefConCmdRadio, + kRefPythonRadio, + kRefConsoleCmd, + kRefAnimate, + kRefAnimation, + kRefMouseOverAnimate, + kRefMouseOverAnimation, + kRefMouseDownSound, + kRefMouseDownSoundComp, + kRefMouseUpSound, + kRefMouseUpSoundComp, + kRefMouseOverSound, + kRefMouseOverSoundComp, + kRefMouseOffSound, + kRefMouseOffSoundComp, + kRefAnimationNode, + kRefAnimationNodeType, + kRefMouseOverAnimationNode, + kRefMouseOverAnimationNodeType, + kRefDraggableChild, + kRefUseDraggableChild, + kRefNotifyType + }; +}; + +BOOL plGUIButtonProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + + switch( msg ) + { + case WM_INITDIALOG: + SendMessage( GetDlgItem( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE ), CB_RESETCONTENT, 0, 0 ); + SendMessage( GetDlgItem( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE ), CB_ADDSTRING, 0, (LPARAM)"Button Up" ); + SendMessage( GetDlgItem( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE ), CB_ADDSTRING, 0, (LPARAM)"Button Down" ); + SendMessage( GetDlgItem( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE ), CB_ADDSTRING, 0, (LPARAM)"Button Down and Up" ); + SendMessage( GetDlgItem( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE ), CB_SETCURSEL, pmap->GetParamBlock()->GetInt( plGUIButtonComponent::kRefNotifyType ), 0 ); + return true; + + case WM_DESTROY: + break; + + case WM_COMMAND: + if( LOWORD( wParam ) == IDC_COMBO_BUTTON_NOTIFYTYPE ) + { + if( HIWORD( wParam ) == CBN_SELCHANGE ) + { + int idx = SendDlgItemMessage( hWnd, IDC_COMBO_BUTTON_NOTIFYTYPE, CB_GETCURSEL, 0, 0 ); + pmap->GetParamBlock()->SetValue( plGUIButtonComponent::kRefNotifyType, 0, idx ); + } + } + break; + } + return false; +} + +class plGUIButtonAccessor : public PBAccessor +{ +public: + void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t ) + { + if( id == plGUIButtonComponent::kRefAnimation || + id == plGUIButtonComponent::kRefMouseOverAnimation || + id == plGUIButtonComponent::kRefMouseDownSoundComp || + id == plGUIButtonComponent::kRefMouseUpSoundComp || + id == plGUIButtonComponent::kRefMouseOverSoundComp || + id == plGUIButtonComponent::kRefMouseOffSoundComp || + id == plGUIButtonComponent::kRefAnimationNode || + id == plGUIButtonComponent::kRefMouseOverAnimationNode ) + { + plGUIButtonComponent *comp = (plGUIButtonComponent *)owner; + comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED ); + } + } +}; + +Class_ID sBtnClassesToSelect[] = { ANIM_COMP_CID, ANIM_GROUP_COMP_CID, plGUISingleCtrlDlgProc::kEndClassList }; +Class_ID sBtnSndClassesToSelect[] = { GUI_SOUND_COMPONENT_ID, plGUISingleCtrlDlgProc::kEndClassList }; + +Class_ID sBtnDragClassesToSelect[] = { GUI_DRAGGABLE_CLASSID, plGUISingleCtrlDlgProc::kEndClassList }; + +static plGUIButtonAccessor sGUIButtonAccessor; + +static plGUISingleCtrlDlgProc sGUIButtonSndAProc( plGUIButtonComponent::kRefMouseDownSoundComp, IDC_GUI_MDOWNSNDCOMP, + "Select the sound to play when the mouse clicks this button", sBtnSndClassesToSelect ); +static plGUISingleCtrlDlgProc sGUIButtonSndBProc( plGUIButtonComponent::kRefMouseUpSoundComp, IDC_GUI_MUPSNDCOMP, + "Select the sound to play when the mouse lets up on this button", sBtnSndClassesToSelect ); +static plGUISingleCtrlDlgProc sGUIButtonSndCProc( plGUIButtonComponent::kRefMouseOverSoundComp, IDC_GUI_MOVERSNDCOMP, + "Select the sound to play when the mouse moves over this button", sBtnSndClassesToSelect ); +static plGUISingleCtrlDlgProc sGUIButtonSndDProc( plGUIButtonComponent::kRefMouseOffSoundComp, IDC_GUI_MOFFSNDCOMP, + "Select the sound to play when the mouse moves off of this button", sBtnSndClassesToSelect ); + +static plGUISingleCtrlDlgProc sGUIButtonDragChildProc( plGUIButtonComponent::kRefDraggableChild, IDC_GUI_DRAGCHILD, + "Select the draggable to use when the mouse is dragged off of this button", sBtnDragClassesToSelect ); + +static plGUISingleCtrlDlgProc *sGUIButtonSubProcs[] = { &sGUIButtonSndAProc, &sGUIButtonSndBProc, + &sGUIButtonSndCProc, &sGUIButtonSndDProc, + &sGUIButtonDragChildProc, nil }; +static ParamMap2UserDlgProc *sGUIButtonSubSubProcs[] = { &gGUIButtonProc, nil }; + +static plGUIMultipleCtrlDlgProc sGUIButtonSels( sGUIButtonSubProcs, sGUIButtonSubSubProcs ); + +static plPlasmaAnimSelectDlgProc sGUIButtonAnimA( plGUIButtonComponent::kRefAnimation, IDC_GUI_COMPSELBTN, + plGUIButtonComponent::kRefAnimationNode, plGUIButtonComponent::kRefAnimationNodeType, IDC_GUI_ANIMNODESEL, + "Select the animation to play when this button is clicked", &sGUIButtonSels ); +static plPlasmaAnimSelectDlgProc sGUIButtonProc( plGUIButtonComponent::kRefMouseOverAnimation, IDC_GUI_COMPSELBTN2, + plGUIButtonComponent::kRefMouseOverAnimationNode, plGUIButtonComponent::kRefMouseOverAnimationNodeType, IDC_GUI_ANIMNODESEL2, + "Select the animation to play when the mouse moves over this button", &sGUIButtonAnimA ); + + +#define GUI_SOUND_REF( comp, evt, allCapsEvt ) \ + comp##::kRefMouse##evt##Sound, _T( "mouse##evt##Sound" ), TYPE_BOOL, 0, 0, \ + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_M##allCapsEvt##SND, \ + p_default, FALSE, \ + p_enable_ctrls, 1, comp##::kRefMouse##evt##SoundComp, \ + end, \ + comp##::kRefMouse##evt##SoundComp, _T("mouse##evt##SoundComp"), TYPE_INODE, 0, 0, \ + p_accessor, &sGUIButtonAccessor, \ + end + +//Max desc stuff necessary below. +CLASS_DESC(plGUIButtonComponent, gGUIButtonDesc, "GUI Button", "GUIButton", COMP_TYPE_GUI, GUI_BUTTON_CLASSID ) + +ParamBlockDesc2 gGUIButtonBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIButton"), 0, &gGUIButtonDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_INCLUDE_PARAMS + P_MULTIMAP, plComponent::kRefComp, + + 3, + plGUIControlBase::kRollMain, IDD_COMP_GUIBUTTON, IDS_COMP_GUIBUTTON, 0, 0, &sGUIButtonProc, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + sGUIProxyParamHeader, + + &sGUIControlProcParamTemplate, + + plGUIButtonComponent::kRefAnimate, _T( "animate" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ANIMATE, + p_default, FALSE, + p_enable_ctrls, 1, plGUIButtonComponent::kRefAnimation, + end, + + plGUIButtonComponent::kRefAnimation, _T("animation"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_GUI_SELECTANIM, + p_accessor, &sGUIButtonAccessor, + end, + + plGUIButtonComponent::kRefMouseOverAnimate, _T( "mouseOverAnimate" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_MOUSEOVERANIM, + p_default, FALSE, + p_enable_ctrls, 1, plGUIButtonComponent::kRefMouseOverAnimation, + end, + + plGUIButtonComponent::kRefMouseOverAnimation, _T("mouseOverAnimation"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_GUI_SELECTMOUSEOVERANIM, + p_accessor, &sGUIButtonAccessor, + end, + + GUI_SOUND_REF( plGUIButtonComponent, Down, DOWN ), + GUI_SOUND_REF( plGUIButtonComponent, Up, UP ), + GUI_SOUND_REF( plGUIButtonComponent, Over, OVER ), + GUI_SOUND_REF( plGUIButtonComponent, Off, OFF ), + + sGUIProxyParamTemplate, + + plGUIButtonComponent::kRefAnimationNode, _T("animationNode"), TYPE_INODE, 0, 0, + p_accessor, &sGUIButtonAccessor, + end, + + plGUIButtonComponent::kRefAnimationNodeType, _T("animationNodeType"), TYPE_INT, 0, 0, + p_default, plAnimObjInterface::kUseOwnerNode, + end, + + plGUIButtonComponent::kRefMouseOverAnimationNode, _T("moAnimationNode"), TYPE_INODE, 0, 0, + p_accessor, &sGUIButtonAccessor, + end, + + plGUIButtonComponent::kRefMouseOverAnimationNodeType, _T("moAnimationNodeType"), TYPE_INT, 0, 0, + p_default, plAnimObjInterface::kUseOwnerNode, + end, + + plGUIButtonComponent::kRefUseDraggableChild, _T( "useDragChild" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_USEDRAGCHILD, + p_default, FALSE, + p_enable_ctrls, 1, plGUIButtonComponent::kRefDraggableChild, + end, + + plGUIButtonComponent::kRefDraggableChild, _T( "dragChild" ), TYPE_INODE, 0, 0, + p_accessor, &sGUIButtonAccessor, + end, + + plGUIButtonComponent::kRefNotifyType, _T("notifyType"), TYPE_INT, 0, 0, + p_default, pfGUIButtonMod::kNotifyOnUp, + end, + end +); + +plGUIButtonComponent::plGUIButtonComponent() +{ + fClassDesc = &gGUIButtonDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIButtonComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( fCompPB->GetInt( kRefAnimate ) ) + { + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) ); + if( iface != nil && iface->MightRequireSeparateMaterial() ) + { + INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode ) + ? fCompPB->GetINode( kRefAnimationNode ) + : (INode *)node; + + if( restrict != nil ) + { + node->SetForceMaterialCopy( true ); + } + } + } + + if( fCompPB->GetInt( kRefMouseOverAnimate ) ) + { + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefMouseOverAnimation ) ); + if( iface != nil && iface->MightRequireSeparateMaterial() ) + { + INode *restrict = ( fCompPB->GetInt( kRefMouseOverAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode ) + ? fCompPB->GetINode( kRefMouseOverAnimationNode ) + : (INode *)node; + + if( restrict != nil ) + { + node->SetForceMaterialCopy( true ); + } + } + } + + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUIButtonComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUIButtonComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIButtonMod *button = (pfGUIButtonMod *)fControl; + + // set the notify type + button->SetNotifyType(fCompPB->GetInt( kRefNotifyType )); + + if( fCompPB->GetInt( kRefAnimate ) ) + { + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) ); + if( iface != nil ) + { + INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode ) + ? fCompPB->GetINode( kRefAnimationNode ) + : (INode *)node; + + + hsTArray keys; + if( iface->GetKeyList( restrict, keys ) && keys.GetCount() > 0 ) + button->SetAnimationKeys( keys, iface->GetIfaceSegmentName( false ) ); + } + } + + if( fCompPB->GetInt( kRefMouseOverAnimate ) ) + { + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefMouseOverAnimation ) ); + if( iface != nil ) + { + INode *restrict = ( fCompPB->GetInt( kRefMouseOverAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode ) + ? fCompPB->GetINode( kRefMouseOverAnimationNode ) + : (INode *)node; + + + hsTArray keys; + if( iface->GetKeyList( restrict, keys ) && keys.GetCount() > 0 ) + button->SetMouseOverAnimKeys( keys, iface->GetIfaceSegmentName( false ) ); + } + } + + // Do sound stuff + const char *errMsg1 = ISetSoundIndex( kRefMouseDownSound, kRefMouseDownSoundComp, pfGUIButtonMod::kMouseDown, node ); + const char *errMsg2 = ISetSoundIndex( kRefMouseUpSound, kRefMouseUpSoundComp, pfGUIButtonMod::kMouseUp, node ); + const char *errMsg3 = ISetSoundIndex( kRefMouseOverSound, kRefMouseOverSoundComp, pfGUIButtonMod::kMouseOver, node ); + const char *errMsg4 = ISetSoundIndex( kRefMouseOffSound, kRefMouseOffSoundComp, pfGUIButtonMod::kMouseOff, node ); + + const char *errMsg = ( errMsg1 != nil ) ? errMsg1 : ( errMsg2 != nil ) ? errMsg2 : ( errMsg3 != nil ) ? errMsg3 : errMsg4; + if( errMsg != nil ) + { + pErrMsg->Set( true, "GUI Sound Event Error", errMsg, node->GetName() ).Show(); + pErrMsg->Set( false ); + } + + if( fCompPB->GetInt( kRefUseDraggableChild ) ) + { + pfGUIDraggableMod *dragChild = pfGUIDraggableMod::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefDraggableChild ) ) ); + if( dragChild != nil ) + { + hsgResMgr::ResMgr()->AddViaNotify( dragChild->GetKey(), + new plGenRefMsg( button->GetKey(), plRefMsg::kOnCreate, -1, pfGUIButtonMod::kRefDraggable ), plRefFlags::kActiveRef ); + } + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUICheckBox Component ////////////////////////////////////////////////////////////////////// +// +// Defines a dialog button to be defined with the GUI manager at runtime. Belongs to exactly +// one dialog, defined by parent-child relationship, also at runtime. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUICheckBoxComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUICheckBoxCtrl; } + virtual bool ICanHaveProxy( void ) { return true; } + +public: + plGUICheckBoxComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefConCmdRadio, + kRefPythonRadio, + kRefConsoleCmd, + kRefAnimate, + kRefAnimation, + kRefAnimationNode, + kRefAnimationNodeType, + kRefMouseDownSound, + kRefMouseDownSoundComp, + kRefMouseUpSound, + kRefMouseUpSoundComp, + kRefMouseOverSound, + kRefMouseOverSoundComp, + kRefMouseOffSound, + kRefMouseOffSoundComp, + }; +}; + +class plGUICheckBoxAccessor : public PBAccessor +{ +public: + void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t ) + { + if( id == plGUICheckBoxComponent::kRefAnimation || + id == plGUICheckBoxComponent::kRefMouseDownSoundComp || + id == plGUICheckBoxComponent::kRefMouseUpSoundComp || + id == plGUICheckBoxComponent::kRefMouseOverSoundComp || + id == plGUICheckBoxComponent::kRefMouseOffSoundComp ) + { + plGUICheckBoxComponent *comp = (plGUICheckBoxComponent *)owner; + comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED ); + } + } +}; +static plGUICheckBoxAccessor sGUICheckBoxAccessor; + +static plGUISingleCtrlDlgProc sGUICheckSndAProc( plGUICheckBoxComponent::kRefMouseDownSoundComp, IDC_GUI_MDOWNSNDCOMP, + "Select the sound to play when the mouse clicks this button", sBtnSndClassesToSelect ); +static plGUISingleCtrlDlgProc sGUICheckSndBProc( plGUICheckBoxComponent::kRefMouseUpSoundComp, IDC_GUI_MUPSNDCOMP, + "Select the sound to play when the mouse lets up on this button", sBtnSndClassesToSelect ); +static plGUISingleCtrlDlgProc sGUICheckSndCProc( plGUICheckBoxComponent::kRefMouseOverSoundComp, IDC_GUI_MOVERSNDCOMP, + "Select the sound to play when the mouse moves over this button", sBtnSndClassesToSelect ); +static plGUISingleCtrlDlgProc sGUICheckSndDProc( plGUICheckBoxComponent::kRefMouseOffSoundComp, IDC_GUI_MOFFSNDCOMP, + "Select the sound to play when the mouse moves off of this button", sBtnSndClassesToSelect ); + +static plGUISingleCtrlDlgProc *sGUICheckSubProcs[] = { &sGUICheckSndAProc, &sGUICheckSndBProc, + &sGUICheckSndCProc, &sGUICheckSndDProc, nil }; + +static plGUIMultipleCtrlDlgProc sGUICheckSels( sGUICheckSubProcs ); + +static plPlasmaAnimSelectDlgProc sGUICheckBoxProc( plGUICheckBoxComponent::kRefAnimation, IDC_GUI_COMPSELBTN, + plGUICheckBoxComponent::kRefAnimationNode, plGUICheckBoxComponent::kRefAnimationNodeType, IDC_GUI_ANIMNODESEL, + "Select the animation to play when this check box is clicked", &sGUICheckSels ); + +//Max desc stuff necessary below. +CLASS_DESC(plGUICheckBoxComponent, gGUICheckBoxDesc, "GUI CheckBox", "GUICheckBox", COMP_TYPE_GUI, GUI_CHECKBOX_CLASSID ) + +ParamBlockDesc2 gGUICheckBoxBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUICheckBox"), 0, &gGUICheckBoxDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 3, + plGUIControlBase::kRollMain, IDD_COMP_GUIBUTTON, IDS_COMP_GUICHECKBOX, 0, 0, &sGUICheckBoxProc, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + sGUIProxyParamHeader, + + &sGUIControlProcParamTemplate, + + plGUICheckBoxComponent::kRefAnimate, _T( "animate" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ANIMATE, + p_default, FALSE, + p_enable_ctrls, 1, plGUIButtonComponent::kRefAnimation, + end, + + plGUICheckBoxComponent::kRefAnimation, _T("animation"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_GUI_SELECTANIM, + p_accessor, &sGUICheckBoxAccessor, + end, + + sGUIProxyParamTemplate, + + plGUICheckBoxComponent::kRefAnimationNode, _T("animationNode"), TYPE_INODE, 0, 0, + p_accessor, &sGUIButtonAccessor, + end, + + plGUICheckBoxComponent::kRefAnimationNodeType, _T("animationNodeType"), TYPE_INT, 0, 0, + p_default, plAnimObjInterface::kUseOwnerNode, + end, + + GUI_SOUND_REF( plGUICheckBoxComponent, Down, DOWN ), + GUI_SOUND_REF( plGUICheckBoxComponent, Up, UP ), + GUI_SOUND_REF( plGUICheckBoxComponent, Over, OVER ), + GUI_SOUND_REF( plGUICheckBoxComponent, Off, OFF ), + + end +); + +plGUICheckBoxComponent::plGUICheckBoxComponent() +{ + fClassDesc = &gGUICheckBoxDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUICheckBoxComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( fCompPB->GetInt( kRefAnimate ) ) + { + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) ); + if( iface != nil && iface->MightRequireSeparateMaterial() ) + { + INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode ) + ? fCompPB->GetINode( kRefAnimationNode ) + : (INode *)node; + + if( restrict != nil ) + { + node->SetForceMaterialCopy( true ); + } + } + } + + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUICheckBoxComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUICheckBoxComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUICheckBoxCtrl *button = (pfGUICheckBoxCtrl *)fControl; + + if( fCompPB->GetInt( kRefAnimate ) ) + { + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) ); + if( iface != nil ) + { + INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode ) + ? fCompPB->GetINode( kRefAnimationNode ) + : (INode *)node; + + + hsTArray keys; + if( iface->GetKeyList( restrict, keys ) && keys.GetCount() > 0 ) + button->SetAnimationKeys( keys, iface->GetIfaceSegmentName( false ) ); + } + } + + // Do sound stuff + const char *errMsg1 = ISetSoundIndex( kRefMouseDownSound, kRefMouseDownSoundComp, pfGUICheckBoxCtrl::kMouseDown, node ); + const char *errMsg2 = ISetSoundIndex( kRefMouseUpSound, kRefMouseUpSoundComp, pfGUICheckBoxCtrl::kMouseUp, node ); + const char *errMsg3 = ISetSoundIndex( kRefMouseOverSound, kRefMouseOverSoundComp, pfGUICheckBoxCtrl::kMouseOver, node ); + const char *errMsg4 = ISetSoundIndex( kRefMouseOffSound, kRefMouseOffSoundComp, pfGUICheckBoxCtrl::kMouseOff, node ); + + const char *errMsg = ( errMsg1 != nil ) ? errMsg1 : ( errMsg2 != nil ) ? errMsg2 : ( errMsg3 != nil ) ? errMsg3 : errMsg4; + if( errMsg != nil ) + { + pErrMsg->Set( true, "GUI Sound Event Error", errMsg, node->GetName() ).Show(); + pErrMsg->Set( false ); + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIDraggable Component /////////////////////////////////////////////////////////////////// +// +// GUI element that can be dragged anywhere in the 2D viewing plane. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUIDraggableComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIDraggableMod; } + virtual bool ICanHaveProxy( void ) { return true; } + +public: + plGUIDraggableComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefReportDragging, + kRefHideCursor, + kRefAlwaysSnap + }; +}; + +//Max desc stuff necessary below. +CLASS_DESC(plGUIDraggableComponent, gGUIDraggableDesc, "GUI Draggable", "GUIDraggable", COMP_TYPE_GUI, GUI_DRAGGABLE_CLASSID ) + +ParamBlockDesc2 gGUIDraggableBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIDraggable"), 0, &gGUIDraggableDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 3, + plGUIControlBase::kRollMain, IDD_COMP_GUIDRAGGABLE, IDS_COMP_GUIDRAGGABLE, 0, 0, NULL, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + sGUIProxyParamHeader, + + &sGUIControlProcParamTemplate, + + sGUIProxyParamTemplate, + + plGUIDraggableComponent::kRefReportDragging, _T("reportWhileDragging"), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_REPORTDRAG, + p_default, FALSE, + end, + + plGUIDraggableComponent::kRefHideCursor, _T("hideCursorWhileDragging"), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_HIDECURSOR, + p_default, FALSE, + end, + + plGUIDraggableComponent::kRefAlwaysSnap, _T("alwaysSnapBackToStart"), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SNAPSTART, + p_default, FALSE, + end, + + end +); + +plGUIDraggableComponent::plGUIDraggableComponent() +{ + fClassDesc = &gGUIDraggableDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIDraggableComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetForceLocal( true ); + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUIDraggableComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUIDraggableComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIDraggableMod *ctrl = (pfGUIDraggableMod *)fControl; + + if( fCompPB->GetInt( kRefReportDragging ) ) + ctrl->SetFlag( pfGUIDraggableMod::kReportDragging ); + + if( fCompPB->GetInt( kRefHideCursor ) ) + ctrl->SetFlag( pfGUIDraggableMod::kHideCursorWhileDragging ); + + if( fCompPB->GetInt( kRefAlwaysSnap ) ) + ctrl->SetFlag( pfGUIDraggableMod::kAlwaysSnapBackToStart ); + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIKnobCtrl Component /////////////////////////////////////////////////////////////////// +// +// GUI element that can be dragged anywhere in the 2D viewing plane. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUIKnobCtrlComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIKnobCtrl; } + virtual bool ICanHaveProxy( void ) { return true; } + + hsBool IGrabAnimationRange( plMaxNode *node, plErrorMsg *pErrMsg, hsMatrix44 &startL2W, hsMatrix44 &endL2W ); + +public: + plGUIKnobCtrlComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefMinValue, + kRefMaxValue, + kRefStep, + kReverseValues, + kRefOrientation, + kRefMouseMapping, + kRefTriggerOnMouseUp, + kRefAnimation, + kRefAnimationNode, + kRefAnimationNodeType + }; +}; + +static plPlasmaAnimSelectDlgProc sGUIKnobCtrlProc( plGUIKnobCtrlComponent::kRefAnimation, IDC_GUI_COMPSELBTN, + plGUIKnobCtrlComponent::kRefAnimationNode, plGUIKnobCtrlComponent::kRefAnimationNodeType, IDC_GUI_ANIMNODESEL, + "Select the animation to use when displaying this knob control", nil ); + +//Max desc stuff necessary below. +CLASS_DESC(plGUIKnobCtrlComponent, gGUIKnobCtrlDesc, "GUI Knob Control", "GUIKnobCtrl", COMP_TYPE_GUI, GUI_KNOBCTRL_CLASSID ) + +ParamBlockDesc2 gGUIKnobCtrlBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIKnobCtrl"), 0, &gGUIKnobCtrlDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 3, + plGUIControlBase::kRollMain, IDD_COMP_GUIKNOB, IDS_COMP_GUIKNOB, 0, 0, &sGUIKnobCtrlProc, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + sGUIProxyParamHeader, + + &sGUIControlProcParamTemplate, + + plGUIKnobCtrlComponent::kRefMinValue, _T("minValue"), TYPE_FLOAT, 0, 0, + p_default, 0.0f, + p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_LOWER, IDC_GUI_LOWER_SPIN, SPIN_AUTOSCALE, + end, + + plGUIKnobCtrlComponent::kRefMaxValue, _T("maxValue"), TYPE_FLOAT, 0, 0, + p_default, 10.0f, + p_range, -10000.f, 10000.f, // WHY do we even need to specify this? + p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_UPPER, IDC_GUI_UPPER_SPIN, SPIN_AUTOSCALE, + end, + + plGUIKnobCtrlComponent::kRefStep, _T("step"), TYPE_FLOAT, 0, 0, + p_default, 1.0f, + p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_STEP, IDC_GUI_STEP_SPIN, SPIN_AUTOSCALE, + end, + + plGUIKnobCtrlComponent::kReverseValues, _T("reverseValues"), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_REVERSE, + p_default, FALSE, + end, + + plGUIKnobCtrlComponent::kRefOrientation, _T("orientation"), TYPE_INT, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_RADIO, 2, IDC_ORIENTATION_RADIO, IDC_ORIENTATION_RADIO2, + p_default, 0, + end, + + plGUIKnobCtrlComponent::kRefMouseMapping, _T("mouseMapping"), TYPE_INT, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_RADIO, 3, IDC_GUI_MOUSEMAPREL, IDC_GUI_MOUSEMAPANIM, IDC_GUI_MOUSEMAPSCRN, + p_default, 0, + end, + + plGUIKnobCtrlComponent::kRefTriggerOnMouseUp, _T("triggerOnMouseUp"), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_TRIGGERONUP, + p_default, FALSE, + end, + + sGUIProxyParamTemplate, + + plGUIKnobCtrlComponent::kRefAnimation, _T("animation"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_GUI_SELECTANIM, + end, + + plGUIKnobCtrlComponent::kRefAnimationNode, _T("animationNode"), TYPE_INODE, 0, 0, + end, + + plGUIKnobCtrlComponent::kRefAnimationNodeType, _T("animationNodeType"), TYPE_INT, 0, 0, + p_default, plAnimObjInterface::kUseOwnerNode, + end, + + end +); + +plGUIKnobCtrlComponent::plGUIKnobCtrlComponent() +{ + fClassDesc = &gGUIKnobCtrlDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plGUIKnobCtrlComponent::IGrabAnimationRange( plMaxNode *node, plErrorMsg *pErrMsg, hsMatrix44 &startL2W, hsMatrix44 &endL2W ) +{ + hsBool result = false; + + + // Get the affine parts and the TM Controller + plSceneObject *obj = node->GetSceneObject(); + hsAffineParts * parts = TRACKED_NEW hsAffineParts; + plController* tmc = hsControlConverter::Instance().ConvertTMAnim(obj, node, parts); + + if (tmc) + { + plMatrixControllerChannel *channel = TRACKED_NEW plMatrixControllerChannel(tmc, parts); + + hsScalar length = tmc->GetLength(); + + startL2W = channel->Value( 0.f ); + endL2W = channel->Value( length ); + + delete channel; + result = true; + } + + delete parts; // We copy this over, so no need to keep it around + return result; +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIKnobCtrlComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetForceLocal( true ); + + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) ); + if( iface != nil && iface->MightRequireSeparateMaterial() ) + { + INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode ) + ? fCompPB->GetINode( kRefAnimationNode ) + : (INode *)node; + + if( restrict != nil ) + { + node->SetForceMaterialCopy( true ); + } + } + + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUIKnobCtrlComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +// For hackery below (see warning below) +#include "../plAvatar/plAGMasterMod.h" + +hsBool plGUIKnobCtrlComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIKnobCtrl *ctrl = (pfGUIKnobCtrl *)fControl; + + ctrl->SetRange( fCompPB->GetFloat( kRefMinValue ), fCompPB->GetFloat( kRefMaxValue ) ); + ctrl->SetStep( fCompPB->GetFloat( kRefStep ) ); + + if( fCompPB->GetInt( kReverseValues ) ) + ctrl->SetFlag( pfGUIKnobCtrl::kReverseValues ); + + // Get the animation to use + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) ); + if( iface != nil ) + { + INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode ) + ? fCompPB->GetINode( kRefAnimationNode ) + : (INode *)node; + + + hsTArray keys; + if( iface->GetKeyList( restrict, keys ) && keys.GetCount() > 0 ) + ctrl->SetAnimationKeys( keys, iface->GetIfaceSegmentName( false ) ); + } + else + { + // HACKERY WARNING: Old knobs assumed the animation was just the same one applied to our node, + // so to avoid breaking old formats, if we can't grab an animObjInterface, we just grab the key + // of the master mod of our node, like we would've before + plAGMasterMod *master = node->GetAGMasterMod(); + hsTArray keys; + keys.Append( master->GetKey() ); + ctrl->SetAnimationKeys( keys, ENTIRE_ANIMATION_NAME ); + } + + if( fCompPB->GetInt( kRefOrientation ) == 1 ) + ctrl->SetFlag( pfGUIKnobCtrl::kLeftRightOrientation ); + + hsMatrix44 startL2W, endL2W; + switch( fCompPB->GetInt( kRefMouseMapping ) ) + { + case 0: // Default, normal (old) relative behavior + break; + case 1: // Map to the range of animation positions + if( !IGrabAnimationRange( node, pErrMsg, startL2W, endL2W ) ) + { + pErrMsg->Set( true, "Unable to grab animation range for the GUI Knob Control %s. The Map-To-Screen-Range feature will be disabled.", node->GetName() ).Show(); + pErrMsg->Set( false ); + } + else + { + hsPoint3 startPos = startL2W.GetTranslate(); + hsPoint3 endPos = endL2W.GetTranslate(); + + ctrl->SetScreenRange( startPos, endPos ); + ctrl->SetFlag( pfGUIKnobCtrl::kMapToAnimationRange ); + } + break; + case 2: // Map to a range on the screen + ctrl->SetFlag( pfGUIKnobCtrl::kMapToScreenRange ); + break; + } + + if( fCompPB->GetInt( kRefTriggerOnMouseUp ) ) + ctrl->SetFlag( pfGUIKnobCtrl::kTriggerOnlyOnMouseUp ); + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIListBox Component ///////////////////////////////////////////////////////////////////// +// +// GUI element that can be dragged anywhere in the 2D viewing plane. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUIListBoxComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIListBoxMod; } + virtual bool INeedsDynamicText( void ) { return true; } + +public: + plGUIListBoxComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefUseScroll, + kRefScrollCtrl, + kRefSingleSelect, + kRefXparentBgnd, + kRefDragDropSource, + kRefDisableKeys, + kRefAllow2DElementGrid, + kRefScrollLeftToRight, + kRefScaleWithRes, + kRefPassClicksThrough, + kRefEnableTreeBehavior, + kRefSkin, + kRefHandsOffMultiSelect + }; +}; + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plGUIListBoxAccessor : public PBAccessor +{ +public: + void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t ) + { + if( id == plGUIListBoxComponent::kRefScrollCtrl ) + { + plGUIListBoxComponent *comp = (plGUIListBoxComponent *)owner; + comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED ); + } + } +}; + +Class_ID sScrollingClassesToSelect[] = { GUI_UPDOWNPAIR_CLASSID, GUI_KNOBCTRL_CLASSID, plGUISingleCtrlDlgProc::kEndClassList }; + +static plGUIListBoxAccessor sGUIListBoxAccessor; +static plGUISingleCtrlDlgProc sGUIListBoxProc( plGUIListBoxComponent::kRefScrollCtrl, IDC_GUI_COMPSELBTN, + "Select the control to use for scrolling this list box", sScrollingClassesToSelect ); + +static plGUISingleCtrlDlgProc sGUILBSkinSelectProc( plGUIListBoxComponent::kRefSkin, IDC_GUI_SKIN, + "Select the skin to use for this list box", sSkinClassesToSelect, + &sGUIListBoxProc ); + +//Max desc stuff necessary below. +CLASS_DESC(plGUIListBoxComponent, gGUIListBoxDesc, "GUI List Box", "GUIListBox", COMP_TYPE_GUI, GUI_LISTBOX_CLASSID ) + +ParamBlockDesc2 gGUIListBoxBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIListBox"), 0, &gGUIListBoxDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 2, + plGUIControlBase::kRollMain, IDD_COMP_GUILISTBOX, IDS_COMP_GUILISTBOX, 0, 0, &sGUILBSkinSelectProc, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + &sGUIControlProcParamTemplate, + + plGUIListBoxComponent::kRefUseScroll, _T( "enableScrolling" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCROLLCTRL, + p_default, FALSE, + p_enable_ctrls, 1, plGUIListBoxComponent::kRefScrollCtrl, + end, + + plGUIListBoxComponent::kRefScrollCtrl, _T("scrollControl"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_GUI_SELECTSCROLL, + p_accessor, &sGUIListBoxAccessor, + end, + + plGUIListBoxComponent::kRefSingleSelect, _T( "singleSelect" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SINGLESEL, + p_default, FALSE, + end, + + plGUIListBoxComponent::kRefXparentBgnd, _T( "xparentBgnd" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_XPARENT, + p_default, FALSE, + end, + + plGUIListBoxComponent::kRefDragDropSource, _T( "dragDropCapable" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_DRAGDROPSRC, + p_default, FALSE, + end, + + plGUIListBoxComponent::kRefDisableKeys, _T( "disableKeys" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_DISABLEKEYS, + p_default, FALSE, + end, + + plGUIListBoxComponent::kRefAllow2DElementGrid, _T( "allow2DElementGrid" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ALLOWMULTIROW, + p_default, FALSE, + end, + + plGUIListBoxComponent::kRefScrollLeftToRight, _T( "scrollLeftToRight" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCROLLL2R, + p_default, FALSE, + end, + + plGUIListBoxComponent::kRefScaleWithRes, _T( "scaleWithRes" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCALERES, + p_default, FALSE, + end, + + plGUIListBoxComponent::kRefPassClicksThrough, _T( "passClicksThru" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_PASSTHRU, + p_default, FALSE, + end, + + plGUIListBoxComponent::kRefEnableTreeBehavior, _T( "makeLikeATree" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ENABLETREE, + p_default, FALSE, + end, + + plGUIListBoxComponent::kRefSkin, _T("skin"), TYPE_INODE, 0, 0, + end, + + plGUIListBoxComponent::kRefHandsOffMultiSelect, _T( "handsOffMultiSelect" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_HANDSOFF, + p_default, FALSE, + end, + + end +); + +plGUIListBoxComponent::plGUIListBoxComponent() +{ + fClassDesc = &gGUIListBoxDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIListBoxComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUIListBoxComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUIListBoxComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIListBoxMod *ctrl = (pfGUIListBoxMod *)fControl; + + if( fCompPB->GetInt( kRefUseScroll ) ) + { + // Get the scrolling control to use + pfGUIValueCtrl *scroll = pfGUIValueCtrl::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefScrollCtrl ) ) ); + if( scroll != nil ) + { + hsgResMgr::ResMgr()->AddViaNotify( scroll->GetKey(), TRACKED_NEW plGenRefMsg( ctrl->GetKey(), + plRefMsg::kOnCreate, -1, pfGUIListBoxMod::kRefScrollCtrl ), plRefFlags::kActiveRef ); + } + } + + if( fCompPB->GetInt( kRefSingleSelect ) ) + ctrl->SetSingleSelect( true ); + + if( fCompPB->GetInt( kRefXparentBgnd ) ) + ctrl->SetFlag( pfGUIListBoxMod::kXparentBgnd ); + + if( fCompPB->GetInt( kRefDragDropSource ) ) + ctrl->SetFlag( pfGUIListBoxMod::kDragAndDropCapable ); + + if( fCompPB->GetInt( kRefDisableKeys ) ) + ctrl->SetFlag( pfGUIListBoxMod::kDisableKeyActions ); + + if( fCompPB->GetInt( kRefAllow2DElementGrid ) ) + ctrl->SetFlag( pfGUIListBoxMod::kAllowMultipleElementsPerRow ); + + if( fCompPB->GetInt( kRefScrollLeftToRight ) ) + ctrl->SetFlag( pfGUIListBoxMod::kScrollLeftToRight ); + + if( fCompPB->GetInt( kRefScaleWithRes ) ) + ctrl->SetFlag( pfGUIControlMod::kScaleTextWithResolution ); + + if( fCompPB->GetInt( kRefPassClicksThrough ) ) + ctrl->SetFlag( pfGUIListBoxMod::kAllowMousePassThrough ); + + if( fCompPB->GetInt( kRefEnableTreeBehavior ) ) + ctrl->SetFlag( pfGUIListBoxMod::kGrowLeavesAndProcessOxygen ); + + if( fCompPB->GetInt( kRefHandsOffMultiSelect ) ) + ctrl->SetFlag( pfGUIListBoxMod::kHandsOffMultiSelect ); + + INode *sNode = fCompPB->GetINode( kRefSkin ); + if( sNode != nil ) + { + plComponentBase *comp = ( (plMaxNode *)sNode )->ConvertToComponent(); + if( comp != nil ) + { + Class_ID nodeID = comp->ClassID(); + hsAssert( nodeID == GUI_SKIN_CLASSID, "Bad node param in GUIMenu::Convert()" ); + + plGUISkinComp *skin = (plGUISkinComp *)comp; + hsgResMgr::ResMgr()->AddViaNotify( skin->GetConvertedSkin()->GetKey(), TRACKED_NEW plGenRefMsg( ctrl->GetKey(), plRefMsg::kOnCreate, -1, pfGUIControlMod::kRefSkin ), plRefFlags::kActiveRef ); + } + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUITextBox Component ///////////////////////////////////////////////////////////////////// +// +// GUI element that displays a block of wrapped text. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUITextBoxComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUITextBoxMod; } + virtual bool INeedsDynamicText( void ) { return true; } + +public: + plGUITextBoxComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefInitText, + kRefFontSize, + kRefXparentBgnd, + kRefJustify, + kRefScaleWithRes, + kRefUseLocalization, + kRefLocalizationPath + }; +}; + +class plGUITextBoxProc : public ParamMap2UserDlgProc +{ +private: + std::vector fTranslations; + int fCurLanguage; + void ISetTranslation(int lang, std::string text) + { + while (lang >= fTranslations.size()) + fTranslations.push_back(""); + fTranslations[lang] = text; + } +protected: + +public: + + void DeleteThis() {} + + BOOL DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) + { + int i; + switch( msg ) + { + case WM_INITDIALOG: + // make sure there is a string to get + if ( pmap->GetParamBlock()->GetStr( plGUITextBoxComponent::kRefInitText ) ) + { + fTranslations = plLocalization::StringToLocal(pmap->GetParamBlock()->GetStr( plGUITextBoxComponent::kRefInitText ) ); + SetDlgItemText( hWnd, IDC_GUI_INITTEXT, fTranslations[0].c_str() ); + } + else + // if there is no text, then there is nothing to translate + SetDlgItemText( hWnd, IDC_GUI_INITTEXT, pmap->GetParamBlock()->GetStr( plGUITextBoxComponent::kRefInitText ) ); + SendMessage( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), CB_RESETCONTENT, 0, 0 ); + for (i=0; iGetParamBlock()->GetStr( plGUITextBoxComponent::kRefLocalizationPath ) ); + + if ( pmap->GetParamBlock()->GetInt( plGUITextBoxComponent::kRefUseLocalization ) != 0 ) + { + // disable standard text, enable loc path + EnableWindow( GetDlgItem( hWnd, IDC_GUI_INITTEXT ), false ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), false ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_LOCALIZATION_PATH ), true ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_SELECT_LOC_PATH ), true ); + CheckDlgButton( hWnd, IDC_GUI_USE_LOCALIZATION, BST_CHECKED ); + } + else + { + // enable standard text, disable loc path + EnableWindow( GetDlgItem( hWnd, IDC_GUI_INITTEXT ), true ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), true ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_LOCALIZATION_PATH ), false ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_SELECT_LOC_PATH ), false ); + CheckDlgButton( hWnd, IDC_GUI_USE_LOCALIZATION, BST_UNCHECKED ); + } + return true; + + case WM_DESTROY: + break; + + case WM_COMMAND: + if( LOWORD( wParam ) == IDC_GUI_INITTEXT ) + { + if( HIWORD( wParam ) == EN_CHANGE ) + { + int strLen = SendDlgItemMessage( hWnd, IDC_GUI_INITTEXT, WM_GETTEXTLENGTH, 0, 0 ); + if( strLen > 0 ) + { + char *str = TRACKED_NEW char[ strLen + 1 ]; + GetDlgItemText( hWnd, IDC_GUI_INITTEXT, str, strLen + 1 ); + str[ strLen ] = 0; + ISetTranslation(fCurLanguage,str); + delete [] str; + + std::string translation = plLocalization::LocalToString(fTranslations); + str = TRACKED_NEW char[ translation.length() + 1 ]; + strcpy(str,translation.c_str()); + str[translation.length()] = 0; + + pmap->GetParamBlock()->SetValue( plGUITextBoxComponent::kRefInitText, 0, str ); + delete [] str; + } + } + else if( HIWORD( wParam ) == EN_KILLFOCUS ) + { + plMaxAccelerators::Enable(); + } + else if( HIWORD( wParam ) == EN_SETFOCUS ) + { + plMaxAccelerators::Disable(); + } + } + else if( LOWORD( wParam ) == IDC_GUI_LOCALIZATION_PATH ) + { + if( HIWORD( wParam ) == EN_CHANGE ) + { + int strLen = SendDlgItemMessage( hWnd, IDC_GUI_LOCALIZATION_PATH, WM_GETTEXTLENGTH, 0, 0 ); + if( strLen > 0 ) + { + char *str = TRACKED_NEW char[ strLen + 1 ]; + GetDlgItemText( hWnd, IDC_GUI_LOCALIZATION_PATH, str, strLen + 1 ); + str[ strLen ] = 0; + pmap->GetParamBlock()->SetValue( plGUITextBoxComponent::kRefLocalizationPath, 0, str ); + delete [] str; + } + } + else if( HIWORD( wParam ) == EN_KILLFOCUS ) + { + plMaxAccelerators::Enable(); + } + else if( HIWORD( wParam ) == EN_SETFOCUS ) + { + plMaxAccelerators::Disable(); + } + } + else if( LOWORD( wParam ) == IDC_GUI_LANGUAGE ) + { + if( HIWORD( wParam ) == CBN_SELCHANGE ) + { + int idx = SendDlgItemMessage( hWnd, IDC_GUI_LANGUAGE, CB_GETCURSEL, 0, 0 ); + if (idx >= fTranslations.size()) + SetDlgItemText( hWnd, IDC_GUI_INITTEXT, "" ); + else + SetDlgItemText( hWnd, IDC_GUI_INITTEXT, fTranslations[idx].c_str() ); + fCurLanguage = idx; + } + } + else if( LOWORD( wParam ) == IDC_GUI_SELECT_LOC_PATH ) + { + char value[512]; + GetDlgItemText( hWnd, IDC_GUI_LOCALIZATION_PATH, value, 512 ); + plPickLocalizationDlg dlg( value ); + if( dlg.DoPick() ) + { + pmap->GetParamBlock()->SetValue( plGUITextBoxComponent::kRefLocalizationPath, 0, (char*)dlg.GetValue() ); + SetDlgItemText( hWnd, IDC_GUI_LOCALIZATION_PATH, (char*)dlg.GetValue() ); + } + } + else if( LOWORD( wParam ) == IDC_GUI_USE_LOCALIZATION ) + { + // enable/disable the appropriate values + bool useLoc = ( IsDlgButtonChecked( hWnd, IDC_GUI_USE_LOCALIZATION ) == BST_CHECKED ); + pmap->GetParamBlock()->SetValue( plGUITextBoxComponent::kRefUseLocalization, 0, useLoc ? 1 : 0 ); + + if ( useLoc ) + { + // disable standard text, enable loc path + EnableWindow( GetDlgItem( hWnd, IDC_GUI_INITTEXT ), false ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), false ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_LOCALIZATION_PATH ), true ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_SELECT_LOC_PATH ), true ); + } + else + { + // enable standard text, disable loc path + EnableWindow( GetDlgItem( hWnd, IDC_GUI_INITTEXT ), true ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_LANGUAGE ), true ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_LOCALIZATION_PATH ), false ); + EnableWindow( GetDlgItem( hWnd, IDC_GUI_SELECT_LOC_PATH ), false ); + } + } + break; + } + return false; + } +}; +static plGUITextBoxProc gGUITextBoxProc; + +//Max desc stuff necessary below. +CLASS_DESC(plGUITextBoxComponent, gGUITextBoxDesc, "GUI Text Box", "GUITextBox", COMP_TYPE_GUI, GUI_TEXTBOX_CLASSID ) + +ParamBlockDesc2 gGUITextBoxBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUITextBox"), 0, &gGUITextBoxDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 2, + plGUIControlBase::kRollMain, IDD_COMP_GUITEXTBOX, IDS_COMP_GUITEXTBOX, 0, 0, &gGUITextBoxProc, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + &sGUIControlProcParamTemplate, + + plGUITextBoxComponent::kRefInitText, _T("InitText"), TYPE_STRING, 0, 0, +// p_ui, plGUIControlBase::kRollMain, TYPE_EDITBOX, IDC_GUI_INITTEXT, + end, + + plGUITextBoxComponent::kRefXparentBgnd, _T( "xparentBgnd" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_XPARENT, + p_default, FALSE, + end, + + plGUITextBoxComponent::kRefJustify, _T("justify"), TYPE_INT, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_RADIO, 3, IDC_JUSTIFYRADIO, IDC_JUSTRADIO2, IDC_JUSTRADIO3, + p_default, 0, + end, + + plGUITextBoxComponent::kRefScaleWithRes, _T( "scaleWithRes" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCALERES, + p_default, FALSE, + end, + + plGUITextBoxComponent::kRefUseLocalization, _T( "useLocalization" ), TYPE_BOOL, 0, 0, + p_default, FALSE, + end, + + plGUITextBoxComponent::kRefLocalizationPath,_T( "localizationPath" ),TYPE_STRING, 0, 0, + end, + + end +); + +plGUITextBoxComponent::plGUITextBoxComponent() +{ + fClassDesc = &gGUITextBoxDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUITextBoxComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUITextBoxComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUITextBoxComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUITextBoxMod *ctrl = (pfGUITextBoxMod *)fControl; + + ctrl->SetText( fCompPB->GetStr( kRefInitText ) ); + + if( fCompPB->GetInt( kRefXparentBgnd ) ) + ctrl->SetFlag( pfGUITextBoxMod::kXparentBgnd ); + + int just = fCompPB->GetInt( kRefJustify ); + if( just == 1 ) + ctrl->SetFlag( pfGUITextBoxMod::kCenterJustify ); + else if( just == 2 ) + ctrl->SetFlag( pfGUITextBoxMod::kRightJustify ); + + if( fCompPB->GetInt( kRefScaleWithRes ) ) + ctrl->SetFlag( pfGUIControlMod::kScaleTextWithResolution ); + + ctrl->SetUseLocalizationPath( fCompPB->GetInt( kRefUseLocalization ) != 0 ); + ctrl->SetLocalizationPath( fCompPB->GetStr( kRefLocalizationPath ) ); + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIEditBox Component ///////////////////////////////////////////////////////////////////// +// +// GUI element that can be dragged anywhere in the 2D viewing plane. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUIEditBoxComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIEditBoxMod; } + virtual bool INeedsDynamicText( void ) { return true; } + +public: + plGUIEditBoxComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefXparentBgnd, + kRefScaleWithRes + }; +}; + +//Max desc stuff necessary below. +CLASS_DESC(plGUIEditBoxComponent, gGUIEditBoxDesc, "GUI Edit Box", "GUIEditBox", COMP_TYPE_GUI, GUI_EDITBOX_CLASSID ) + +ParamBlockDesc2 gGUIEditBoxBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIEditBox"), 0, &gGUIEditBoxDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 2, + plGUIControlBase::kRollMain, IDD_COMP_GUIEDITBOX, IDS_COMP_GUIEDITBOX, 0, 0, NULL, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + &sGUIControlProcParamTemplate, + + plGUIEditBoxComponent::kRefXparentBgnd, _T( "xparentBgnd" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_XPARENT, + p_default, FALSE, + end, + + plGUIEditBoxComponent::kRefScaleWithRes, _T( "scaleWithRes" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCALERES, + p_default, FALSE, + end, + + end +); + +plGUIEditBoxComponent::plGUIEditBoxComponent() +{ + fClassDesc = &gGUIEditBoxDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIEditBoxComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUIEditBoxComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUIEditBoxComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIEditBoxMod *ctrl = (pfGUIEditBoxMod *)fControl; + + if( fCompPB->GetInt( kRefXparentBgnd ) ) + ctrl->SetFlag( pfGUIEditBoxMod::kXparentBgnd ); + + if( fCompPB->GetInt( kRefScaleWithRes ) ) + ctrl->SetFlag( pfGUIControlMod::kScaleTextWithResolution ); + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIUpDownPair Component ////////////////////////////////////////////////////////////////// +// +// GUI grouping element that uses two buttons to alter a value up and down +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUIUpDownPairComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIUpDownPairMod; } + +public: + plGUIUpDownPairComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefMinValue, + kRefMaxValue, + kRefStep, + kRefUpControl, + kRefDownControl + }; +}; + +//// Dialog proc //////////////////////////////////////////////////////////////////////////////// + +class plGUIUDPairDlgProc : public ParamMap2UserDlgProc +{ +protected: + ParamID fUpNodeID, fDownNodeID; + int fUpDlgItem, fDownDlgItem; + TCHAR fTitle[ 128 ]; + +public: + plGUIUDPairDlgProc( ParamID upNodeID, int upDlgItem, ParamID downNodeID, int downDlgItem, TCHAR *title ) + { + fUpNodeID = upNodeID; + fDownNodeID = downNodeID; + fUpDlgItem = upDlgItem; + fDownDlgItem = downDlgItem; + strcpy( fTitle, title ); + } + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) + { + switch ( msg ) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + + INode *node = pb->GetINode( fUpNodeID ); + TSTR newName( node ? node->GetName() : "Pick" ); + ::SetWindowText( ::GetDlgItem( hWnd, fUpDlgItem ), newName ); + + node = pb->GetINode( fDownNodeID ); + TSTR newName2( node ? node->GetName() : "Pick" ); + ::SetWindowText( ::GetDlgItem( hWnd, fDownDlgItem ), newName2 ); + } + return true; + + case WM_COMMAND: + if( ( HIWORD( wParam ) == BN_CLICKED ) ) + { + if( LOWORD( wParam ) == fUpDlgItem ) + { + IParamBlock2 *pb = map->GetParamBlock(); + plGUICtrlHitCallback hitCB( (INode *)pb->GetOwner(), pb, fUpNodeID, fTitle, true, GUI_BUTTON_CLASSID ); + GetCOREInterface()->DoHitByNameDialog( &hitCB ); + + INode* node = pb->GetINode( fUpNodeID ); + TSTR newName( node ? node->GetName() : "Pick" ); + ::SetWindowText( ::GetDlgItem(hWnd, fUpDlgItem ), newName ); + map->Invalidate( fUpNodeID ); + ::InvalidateRect( hWnd, NULL, TRUE ); + } + else if( LOWORD( wParam ) == fDownDlgItem ) + { + IParamBlock2 *pb = map->GetParamBlock(); + plGUICtrlHitCallback hitCB( (INode *)pb->GetOwner(), pb, fDownNodeID, fTitle, true, GUI_BUTTON_CLASSID ); + GetCOREInterface()->DoHitByNameDialog( &hitCB ); + + INode* node = pb->GetINode( fDownNodeID ); + TSTR newName( node ? node->GetName() : "Pick" ); + ::SetWindowText( ::GetDlgItem(hWnd, fDownDlgItem ), newName ); + map->Invalidate( fDownDlgItem ); + ::InvalidateRect( hWnd, NULL, TRUE ); + } + } + return true; + } + + return false; + } + + void DeleteThis() {} +}; + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plGUIUDAccessor : public PBAccessor +{ +public: + void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t ) + { + if( id == plGUIUpDownPairComponent::kRefUpControl + || id == plGUIUpDownPairComponent::kRefDownControl ) + { + plGUIUpDownPairComponent *comp = (plGUIUpDownPairComponent *)owner; + comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED ); + } + } +}; + +static plGUIUDAccessor sGUIUDAccessor; +static plGUIUDPairDlgProc sGUIUDPairDlgProc( plGUIUpDownPairComponent::kRefUpControl, IDC_GUI_COMPSELBTN, + plGUIUpDownPairComponent::kRefDownControl, IDC_GUI_COMPSELBTN2, + "Select the control to use in this pair" ); + +//Max desc stuff necessary below. +CLASS_DESC(plGUIUpDownPairComponent, gGUIUDPairDesc, "GUI Up/Down Pair", "GUIUDPair", COMP_TYPE_GUI, GUI_UPDOWNPAIR_CLASSID ) + +ParamBlockDesc2 gGUIUDPairBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIUDPair"), 0, &gGUIUDPairDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 2, + plGUIControlBase::kRollMain, IDD_COMP_GUIUDSCROLL, IDS_COMP_GUIUDSCROLL, 0, 0, &sGUIUDPairDlgProc, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + &sGUIControlProcParamTemplate, + + plGUIUpDownPairComponent::kRefUpControl, _T("upControl"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_GUI_SELECTUDCTRL, + p_accessor, &sGUIUDAccessor, + end, + + plGUIUpDownPairComponent::kRefDownControl, _T("downControl"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_GUI_SELECTUDCTRL, + p_accessor, &sGUIUDAccessor, + end, + + plGUIUpDownPairComponent::kRefMinValue, _T("minValue"), TYPE_FLOAT, 0, 0, + p_default, 0.0f, + p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_LOWER, IDC_GUI_LOWER_SPIN, SPIN_AUTOSCALE, + end, + + plGUIUpDownPairComponent::kRefMaxValue, _T("maxValue"), TYPE_FLOAT, 0, 0, + p_default, 10.0f, + p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_UPPER, IDC_GUI_UPPER_SPIN, SPIN_AUTOSCALE, + end, + + plGUIUpDownPairComponent::kRefStep, _T("step"), TYPE_FLOAT, 0, 0, + p_default, 1.0f, + p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_STEP, IDC_GUI_STEP_SPIN, SPIN_AUTOSCALE, + end, + + end +); + +plGUIUpDownPairComponent::plGUIUpDownPairComponent() +{ + fClassDesc = &gGUIUDPairDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIUpDownPairComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUIUpDownPairComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUIUpDownPairComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIUpDownPairMod *ctrl = (pfGUIUpDownPairMod *)fControl; + + // Get the child controls + pfGUIButtonMod *up = pfGUIButtonMod::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefUpControl ) ) ); + pfGUIButtonMod *down = pfGUIButtonMod::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefDownControl ) ) ); + + ctrl->SetControls( up, down ); + + ctrl->SetRange( fCompPB->GetFloat( kRefMinValue ), fCompPB->GetFloat( kRefMaxValue ) ); + ctrl->SetStep( fCompPB->GetFloat( kRefStep ) ); + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIDragBar Component /////////////////////////////////////////////////////////////////// +// +// GUI element that can be dragged anywhere in the 2D viewing plane. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUIDragBarComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIDragBarCtrl; } + virtual bool ICanHaveProxy( void ) { return true; } + +public: + plGUIDragBarComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + }; +}; + +//Max desc stuff necessary below. +CLASS_DESC(plGUIDragBarComponent, gGUIDragBarDesc, "GUI Dialog Drag Bar", "GUIDragBar", COMP_TYPE_GUI, GUI_DRAGBAR_CLASSID ) + +ParamBlockDesc2 gGUIDragBarBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIDragBar"), 0, &gGUIDragBarDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 3, + plGUIControlBase::kRollMain, IDD_COMP_GUIDRAGBAR, IDS_COMP_GUIDRAGBAR, 0, 0, NULL, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + sGUIProxyParamHeader, + + &sGUIControlProcParamTemplate, + + sGUIProxyParamTemplate, + + end +); + +plGUIDragBarComponent::plGUIDragBarComponent() +{ + fClassDesc = &gGUIDragBarDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIDragBarComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetForceLocal( true ); + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUIDragBarComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUIDragBarComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIDragBarCtrl *ctrl = (pfGUIDragBarCtrl *)fControl; + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIRadioGroup Component ////////////////////////////////////////////////////////////////// +// +// GUI grouping element that ensures that only one of a group of check boxes is checked at any +// one time, and takes on the value of whichever one is currently checked, or -1 if none. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUIRadioGroupAccessor; +class plGUIRadioGroupComponent : public plGUIControlBase +{ + friend class plGUIRadioGroupAccessor; + +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIRadioGroupCtrl; } + +public: + plGUIRadioGroupComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefCheckBoxes, + kRefDefaultSel, + kRefAllowNoSel + }; +}; + +//// Dialog proc //////////////////////////////////////////////////////////////////////////////// + +class plGUIRadioGroupProc : public ParamMap2UserDlgProc +{ +protected: + +public: + plGUIRadioGroupProc() + { + } + + void SetSpinnerRange( IParamMap2 *pMap ) + { + if( pMap == nil ) + return; + + HWND hWnd = pMap->GetHWnd(); + if( hWnd == nil ) + return; + + ISpinnerControl *spin = GetISpinner( GetDlgItem( hWnd, IDC_GUI_DEFSEL_SPIN ) ); + + int minValue = pMap->GetParamBlock()->GetInt( plGUIRadioGroupComponent::kRefAllowNoSel ) ? -1 : 0; + int maxValue = pMap->GetParamBlock()->Count( plGUIRadioGroupComponent::kRefCheckBoxes ); + + spin->SetLimits( minValue, maxValue ); + + ReleaseISpinner( spin ); + } + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) + { + switch ( msg ) + { + case WM_INITDIALOG: + { + SetSpinnerRange( map ); + } + return true; + + case WM_COMMAND: + if( ( HIWORD( wParam ) == BN_CLICKED ) ) + { + if( LOWORD( wParam ) == IDC_GUI_ADDCHECK ) + { + IParamBlock2 *pb = map->GetParamBlock(); + + plGUICtrlHitCallback hitCB( (INode *)pb->GetOwner(), pb, plGUIRadioGroupComponent::kRefCheckBoxes, + "Select a check box to add to this radio group", true, GUI_CHECKBOX_CLASSID, false ); + + GetCOREInterface()->DoHitByNameDialog( &hitCB ); + + map->Invalidate( plGUIRadioGroupComponent::kRefCheckBoxes ); + } + } + return true; + } + + return false; + } + + void DeleteThis() {} +}; +static plGUIRadioGroupProc sGUIRadioGroupProc; + +//Max desc stuff necessary below. +CLASS_DESC(plGUIRadioGroupComponent, gGUIRadioGroupDesc, "GUI Radio Group", "GUIRadioGroup", COMP_TYPE_GUI, GUI_RADIOGROUP_CLASSID ) + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plGUIRadioGroupAccessor : public PBAccessor +{ +public: + void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t ) + { + plGUIRadioGroupComponent *comp = (plGUIRadioGroupComponent *)owner; + IParamBlock2 *pBlock = comp->fCompPB; + + if( id == plGUIRadioGroupComponent::kRefCheckBoxes ) + { + comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED ); + sGUIRadioGroupProc.SetSpinnerRange( pBlock->GetMap( plGUIControlBase::kRollMain ) ); + } + else if( id == plGUIRadioGroupComponent::kRefAllowNoSel ) + sGUIRadioGroupProc.SetSpinnerRange( pBlock->GetMap( plGUIControlBase::kRollMain ) ); + } + + void TabChanged( tab_changes changeCode, Tab *tab, ReferenceMaker *owner, ParamID id, int tabIndex, int count ) + { + plGUIRadioGroupComponent *comp = (plGUIRadioGroupComponent *)owner; + IParamBlock2 *pBlock = comp->fCompPB; + + if( id == plGUIRadioGroupComponent::kRefCheckBoxes ) + { + comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED ); + sGUIRadioGroupProc.SetSpinnerRange( pBlock->GetMap( plGUIControlBase::kRollMain ) ); + } + } + +}; + +static plGUIRadioGroupAccessor sGUIRadioGroupAccessor; + +ParamBlockDesc2 gGUIRadioGroupBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIRadioGroup"), 0, &gGUIRadioGroupDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 2, + plGUIControlBase::kRollMain, IDD_COMP_GUIRADIOGROUP, IDS_COMP_GUIRADIOGROUP, 0, 0, &sGUIRadioGroupProc, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + &sGUIControlProcParamTemplate, + + plGUIRadioGroupComponent::kRefCheckBoxes, _T("checkBoxes"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_NODELISTBOX, IDC_GUI_CHECKLIST, 0, 0, IDC_GUI_DELCHECK, + p_accessor, &sGUIRadioGroupAccessor, + end, + + plGUIRadioGroupComponent::kRefDefaultSel, _T("defaultSelection"), TYPE_INT, 0, 0, + p_default, 0, + p_range, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_INT, IDC_GUI_DEFSEL, IDC_GUI_DEFSEL_SPIN, SPIN_AUTOSCALE, + end, + + plGUIRadioGroupComponent::kRefAllowNoSel, _T( "allowNoSel" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ALLOWNONE, + p_default, FALSE, + p_accessor, &sGUIRadioGroupAccessor, + end, + + end +); + +plGUIRadioGroupComponent::plGUIRadioGroupComponent() +{ + fClassDesc = &gGUIRadioGroupDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIRadioGroupComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + + +hsBool plGUIRadioGroupComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUIRadioGroupComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIRadioGroupCtrl *ctrl = (pfGUIRadioGroupCtrl *)fControl; + + int i; + ctrl->ClearControlList(); + for( i = 0; i < fCompPB->Count( kRefCheckBoxes ); i++ ) + { + pfGUICheckBoxCtrl *cb = pfGUICheckBoxCtrl::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefCheckBoxes, 0, i ) ) ); + if( cb != nil ) + ctrl->AddControl( cb ); + } + + if( fCompPB->GetInt( kRefAllowNoSel ) ) + ctrl->SetFlag( pfGUIRadioGroupCtrl::kAllowNoSelection ); + + ctrl->SetDefaultValue( fCompPB->GetInt( kRefDefaultSel ) ); + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIDynDisplay Component ////////////////////////////////////////////////////////////////// +// +// GUI element that can be dragged anywhere in the 2D viewing plane. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUIDynDisplayComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIDynDisplayCtrl; } + virtual bool IHasProcRollout( void ) { return false; } + +public: + plGUIDynDisplayComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefDynLayer + }; +}; + +//// Dialog proc //////////////////////////////////////////////////////////////////////////////// + +class plGUIDynDisplayProc : public ParamMap2UserDlgProc +{ +protected: + +public: + plGUIDynDisplayProc() + { + } + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) + { + switch ( msg ) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + + Texmap *tmap = pb->GetTexmap( plGUIDynDisplayComponent::kRefDynLayer ); + if( tmap != nil ) + SetDlgItemText( hWnd, IDC_GUI_PICKMAT, (const char *)tmap->GetName() ); + else + SetDlgItemText( hWnd, IDC_GUI_PICKMAT, "Pick" ); + } + return true; + + case WM_COMMAND: + if( ( HIWORD( wParam ) == BN_CLICKED ) ) + { + if( LOWORD( wParam ) == IDC_GUI_PICKMAT ) + { + IParamBlock2 *pb = map->GetParamBlock(); + + if( plPickMaterialMap::PickTexmap( pb, plGUIDynDisplayComponent::kRefDynLayer ) ) + { + Texmap *tmap = pb->GetTexmap( plGUIDynDisplayComponent::kRefDynLayer ); + if( tmap != nil ) + SetDlgItemText( hWnd, IDC_GUI_PICKMAT, (const char *)tmap->GetName() ); + else + SetDlgItemText( hWnd, IDC_GUI_PICKMAT, "Pick" ); + + map->Invalidate( plGUIDynDisplayComponent::kRefDynLayer ); + } + } + } + return true; + } + + return false; + } + + void DeleteThis() {} +}; +static plGUIDynDisplayProc sGUIDynDisplayProc; + +//Max desc stuff necessary below. +CLASS_DESC(plGUIDynDisplayComponent, gGUIDynDisplayDesc, "GUI Dynamic Display", "GUIDynDisplay", COMP_TYPE_GUI, GUI_DYNDISPLAY_CLASSID ) + +ParamBlockDesc2 gGUIDynDisplayBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIDynDisplay"), 0, &gGUIDynDisplayDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + 1, + plGUIControlBase::kRollMain, IDD_COMP_GUIDYNDISPLAY, IDS_COMP_GUIDYNDISPLAY, 0, 0, &sGUIDynDisplayProc, + + plGUIDynDisplayComponent::kRefDynLayer, _T("dynLayer"), TYPE_TEXMAP, 0, 0, +// p_ui, plGUIControlBase::kRollMain, TYPE_TEXMAPBUTTON, IDC_GUI_COMPSELBTN, + end, + + end +); + +plGUIDynDisplayComponent::plGUIDynDisplayComponent() +{ + fClassDesc = &gGUIDynDisplayDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIDynDisplayComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUIDynDisplayComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUIDynDisplayComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIDynDisplayCtrl *ctrl = (pfGUIDynDisplayCtrl *)fControl; + + Texmap *tmap = fCompPB->GetTexmap( plGUIDynDisplayComponent::kRefDynLayer ); + plPlasmaMAXLayer *pLayer = plPlasmaMAXLayer::GetPlasmaMAXLayer( tmap ); + if( pLayer == nil /*|| pLayer->ClassID() != DYN_TEXT_LAYER_CLASS_ID */ ) + { + + pErrMsg->Set(true, "GUI Control Component Error", "The texmap selected for the Dynamic Display Control on object \"%s\" is not a Plasma Dynamic Text Layer. Please fix.", node->GetName() ).Show(); + return false; + } + + const hsTArray &materials = hsMaterialConverter::Instance().DoneMaterials(); + + UInt32 i,count = pLayer->GetNumConversionTargets(); + for( i = 0; i < count; i++ ) + { + plLayerInterface *layIface = pLayer->GetConversionTarget( i ); + + ctrl->AddLayer( layIface ); + + plDynamicTextMap *map = plDynamicTextMap::ConvertNoRef( layIface->GetTexture() ); + if( map != nil ) + ctrl->AddMap( map ); + + UInt32 mat; + bool found = false; + for (mat=0; matGetNumLayers(); lay++) + { + if (layIface->BottomOfStack() == curMaterial->GetLayer(lay)) + { + ctrl->AddMaterial(curMaterial); + found = true; + break; + } + } + if (found) + break; + } + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIMultiLineEdit Component /////////////////////////////////////////////////////////////// +// +// GUI element that can be dragged anywhere in the 2D viewing plane. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUIMultiLineEditComp : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIMultiLineEditCtrl; } + virtual bool INeedsDynamicText( void ) { return true; } + +public: + plGUIMultiLineEditComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefXparentBgnd, + kRefScaleWithRes, + kRefUseScroll, + kRefScrollCtrl, + }; +}; + +static plGUISingleCtrlDlgProc sGUIMultiLineProc( plGUIMultiLineEditComp::kRefScrollCtrl, IDC_GUI_COMPSELBTN, + "Select the control to use for scrolling this multi-line edit box", sScrollingClassesToSelect ); + +//Max desc stuff necessary below. +CLASS_DESC(plGUIMultiLineEditComp, gGUIMultiLineEditDesc, "GUI Multi-Line Edit Box", "GUIMultiLineEdit", COMP_TYPE_GUI, GUI_MULTILINE_CLASSID ) + +ParamBlockDesc2 gGUIMultiLineEditBoxBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIMultiLineEdit"), 0, &gGUIMultiLineEditDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 2, + plGUIControlBase::kRollMain, IDD_COMP_GUIMULTILINE, IDS_COMP_GUIMULTILINE, 0, 0, &sGUIMultiLineProc, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + &sGUIControlProcParamTemplate, + + plGUIMultiLineEditComp::kRefXparentBgnd, _T( "xparentBgnd" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_XPARENT, + p_default, FALSE, + end, + + plGUIMultiLineEditComp::kRefScaleWithRes, _T( "scaleWithRes" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCALERES, + p_default, FALSE, + end, + + plGUIMultiLineEditComp::kRefUseScroll, _T( "enableScrolling" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_SCROLLCTRL, + p_default, FALSE, + p_enable_ctrls, 1, plGUIMultiLineEditComp::kRefScrollCtrl, + end, + + plGUIMultiLineEditComp::kRefScrollCtrl, _T("scrollControl"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_GUI_SELECTSCROLL, + p_accessor, &sGUIListBoxAccessor, + end, + + end +); + +plGUIMultiLineEditComp::plGUIMultiLineEditComp() +{ + fClassDesc = &gGUIMultiLineEditDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIMultiLineEditComp::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUIMultiLineEditComp::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUIMultiLineEditComp::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIMultiLineEditCtrl *ctrl = (pfGUIMultiLineEditCtrl *)fControl; + + if( fCompPB->GetInt( kRefXparentBgnd ) ) + ctrl->SetFlag( pfGUIControlMod::kXparentBgnd ); + + if( fCompPB->GetInt( kRefScaleWithRes ) ) + ctrl->SetFlag( pfGUIControlMod::kScaleTextWithResolution ); + + if( fCompPB->GetInt( kRefUseScroll ) ) + { + // Get the scrolling control to use + pfGUIValueCtrl *scroll = pfGUIValueCtrl::ConvertNoRef( GrabControlMod( fCompPB->GetINode( kRefScrollCtrl ) ) ); + if( scroll != nil ) + { + hsgResMgr::ResMgr()->AddViaNotify( scroll->GetKey(), TRACKED_NEW plGenRefMsg( ctrl->GetKey(), + plRefMsg::kOnCreate, -1, pfGUIMultiLineEditCtrl::kRefScrollCtrl ), plRefFlags::kActiveRef ); + } + } + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIProgressCtrl Component /////////////////////////////////////////////////////////////////// +// +// GUI element that can be dragged anywhere in the 2D viewing plane. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUIProgressCtrlComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIProgressCtrl; } + virtual bool ICanHaveProxy( void ) { return false; } + +public: + plGUIProgressCtrlComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefMinValue, + kRefMaxValue, + kRefStep, + kReverseValues, + kRefOrientation, + kRefMouseMapping, + kRefTriggerOnMouseUp, + kRefAnimation, + kRefAnimationNode, + kRefAnimationNodeType, + kRefAnimateSound, + kRefAnimateSoundComp + }; +}; + +class plGUIProgressCtrlAccessor : public PBAccessor +{ +public: + void Set( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t ) + { + if( id == plGUIProgressCtrlComponent::kRefAnimateSoundComp ) + { + plGUIProgressCtrlComponent *comp = (plGUIProgressCtrlComponent *)owner; + comp->NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED ); + } + } +}; + +static plGUIProgressCtrlAccessor sGUIProgressCtrlAccessor; + +class plGUISoundDlgProc : public ParamMap2UserDlgProc +{ +protected: + ParamID fSoundID; + int fSoundItem; + TCHAR fTitle[ 128 ]; + +public: + plGUISoundDlgProc( ParamID soundID, int soundItem, TCHAR *title ) + { + fSoundID = soundID; + fSoundItem = soundItem; + strcpy( fTitle, title ); + } + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) + { + switch ( msg ) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + + INode *node = pb->GetINode( fSoundID ); + TSTR newName( node ? node->GetName() : "Pick" ); + ::SetWindowText( ::GetDlgItem( hWnd, fSoundItem ), newName ); + } + return true; + + case WM_COMMAND: + if( ( HIWORD( wParam ) == BN_CLICKED ) ) + { + if( LOWORD( wParam ) == fSoundItem ) + { + IParamBlock2 *pb = map->GetParamBlock(); + plGUICtrlHitCallback hitCB( (INode *)pb->GetOwner(), pb, fSoundID, fTitle, true, GUI_SOUND_COMPONENT_ID ); + GetCOREInterface()->DoHitByNameDialog( &hitCB ); + + INode* node = pb->GetINode( fSoundID ); + TSTR newName( node ? node->GetName() : "Pick" ); + ::SetWindowText( ::GetDlgItem(hWnd, fSoundItem ), newName ); + map->Invalidate( fSoundID ); + ::InvalidateRect( hWnd, NULL, TRUE ); + return true; + } + } + break; + } + + return false; + } + + void DeleteThis() {} +}; + +static plGUISoundDlgProc sGUIProgressCtrlSndProc( plGUIProgressCtrlComponent::kRefAnimateSoundComp, IDC_GUI_ANIMSNDCOMP, + "Select the sound to play when this control animates" ); + +static plPlasmaAnimSelectDlgProc sGUIProgressCtrlProc( plGUIProgressCtrlComponent::kRefAnimation, IDC_GUI_COMPSELBTN, + plGUIProgressCtrlComponent::kRefAnimationNode, plGUIProgressCtrlComponent::kRefAnimationNodeType, IDC_GUI_ANIMNODESEL, + "Select the animation to use when displaying this knob control", &sGUIProgressCtrlSndProc ); + +//Max desc stuff necessary below. +CLASS_DESC(plGUIProgressCtrlComponent, gGUIProgressCtrlDesc, "GUI Progress Control", "GUIProgressCtrl", COMP_TYPE_GUI, GUI_PROGRESS_CLASSID ) + +ParamBlockDesc2 gGUIProgressCtrlBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIProgressCtrl"), 0, &gGUIProgressCtrlDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 2, + plGUIControlBase::kRollMain, IDD_COMP_GUIPROGRESS, IDS_COMP_GUIPROGRESS, 0, 0, &sGUIProgressCtrlProc, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + &sGUIControlProcParamTemplate, + + plGUIProgressCtrlComponent::kRefMinValue, _T("minValue"), TYPE_FLOAT, 0, 0, + p_default, 0.0f, + p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_LOWER, IDC_GUI_LOWER_SPIN, SPIN_AUTOSCALE, + end, + + plGUIProgressCtrlComponent::kRefMaxValue, _T("maxValue"), TYPE_FLOAT, 0, 0, + p_default, 10.0f, + p_range, -10000.f, 10000.f, // WHY do we even need to specify this? + p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_UPPER, IDC_GUI_UPPER_SPIN, SPIN_AUTOSCALE, + end, + + plGUIProgressCtrlComponent::kRefStep, _T("step"), TYPE_FLOAT, 0, 0, + p_default, 1.0f, + p_ui, plGUIControlBase::kRollMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_GUI_STEP, IDC_GUI_STEP_SPIN, SPIN_AUTOSCALE, + end, + + plGUIProgressCtrlComponent::kReverseValues, _T("reverseValues"), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_REVERSE, + p_default, FALSE, + end, + + plGUIProgressCtrlComponent::kRefAnimation, _T("animation"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_GUI_SELECTANIM, + end, + + plGUIProgressCtrlComponent::kRefAnimationNode, _T("animationNode"), TYPE_INODE, 0, 0, + end, + + plGUIProgressCtrlComponent::kRefAnimationNodeType, _T("animationNodeType"), TYPE_INT, 0, 0, + p_default, plAnimObjInterface::kUseOwnerNode, + end, + + plGUIProgressCtrlComponent::kRefAnimateSound, _T( "animateSound" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ANIMSND, + p_default, FALSE, + p_enable_ctrls, 1, plGUIProgressCtrlComponent::kRefAnimateSoundComp, + end, + + plGUIProgressCtrlComponent::kRefAnimateSoundComp, _T("animateSoundComp"), TYPE_INODE, 0, 0, + p_accessor, &sGUIProgressCtrlAccessor, + end, + + end +); + +plGUIProgressCtrlComponent::plGUIProgressCtrlComponent() +{ + fClassDesc = &gGUIProgressCtrlDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIProgressCtrlComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetForceLocal( true ); + + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) ); + if( iface != nil && iface->MightRequireSeparateMaterial() ) + { + INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode ) + ? fCompPB->GetINode( kRefAnimationNode ) + : (INode *)node; + + if( restrict != nil ) + { + node->SetForceMaterialCopy( true ); + } + } + + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUIProgressCtrlComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +// For hackery below (see warning below) +#include "../plAvatar/plAGMasterMod.h" + +hsBool plGUIProgressCtrlComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIProgressCtrl *ctrl = (pfGUIProgressCtrl *)fControl; + + ctrl->SetRange( fCompPB->GetFloat( kRefMinValue ), fCompPB->GetFloat( kRefMaxValue ) ); + ctrl->SetStep( fCompPB->GetFloat( kRefStep ) ); + + if( fCompPB->GetInt( kReverseValues ) ) + ctrl->SetFlag( pfGUIProgressCtrl::kReverseValues ); + + // Get the animation to use + plAnimObjInterface *iface = plAnimComponentBase::GetAnimInterface( fCompPB->GetINode( kRefAnimation ) ); + if( iface != nil ) + { + INode *restrict = ( fCompPB->GetInt( kRefAnimationNodeType ) == plAnimObjInterface::kUseParamBlockNode ) + ? fCompPB->GetINode( kRefAnimationNode ) + : (INode *)node; + + + hsTArray keys; + if( iface->GetKeyList( restrict, keys ) && keys.GetCount() > 0 ) + ctrl->SetAnimationKeys( keys, iface->GetIfaceSegmentName( false ) ); + } + else + { + // HACKERY WARNING: Old knobs assumed the animation was just the same one applied to our node, + // so to avoid breaking old formats, if we can't grab an animObjInterface, we just grab the key + // of the master mod of our node, like we would've before + plAGMasterMod *master = node->GetAGMasterMod(); + hsTArray keys; + keys.Append( master->GetKey() ); + ctrl->SetAnimationKeys( keys, ENTIRE_ANIMATION_NAME ); + } + + const char *errMsg = ISetSoundIndex( kRefAnimateSound, kRefAnimateSoundComp, pfGUIProgressCtrl::kAnimateSound, node ); + if( errMsg != nil ) + { + pErrMsg->Set( true, "GUI Sound Event Error", errMsg, node->GetName() ).Show(); + pErrMsg->Set( false ); + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIClickMap Component /////////////////////////////////////////////////////////////////// +// +// GUI element that just keeps track of where on its surface (from 0-1) that it was clicked. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// Class that accesses the paramblock below. +class plGUIClickMapComponent : public plGUIControlBase +{ +protected: + + virtual pfGUIControlMod *IGetNewControl( void ) { return TRACKED_NEW pfGUIClickMapCtrl; } + virtual bool ICanHaveProxy( void ) { return false; } + +public: + plGUIClickMapComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum + { + kRefReportDragging + }; +}; + +//Max desc stuff necessary below. +CLASS_DESC(plGUIClickMapComponent, gGUIClickMapDesc, "GUI Clickable Map", "GUIClickMap", COMP_TYPE_GUI, GUI_CLICKMAP_CLASSID ) + +ParamBlockDesc2 gGUIClickMapBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIClickMap"), 0, &gGUIClickMapDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 2, + plGUIControlBase::kRollMain, IDD_COMP_GUICLICKMAP, IDS_COMP_GUICLICKMAP, 0, 0, NULL, + plGUIControlBase::kRollProc, IDD_COMP_GUIPROCROLLOUT, IDS_COMP_GUIPROCROLLOUT, 0, 0, nil, + + &sGUIControlProcParamTemplate, + + plGUIClickMapComponent::kRefReportDragging, _T("reportWhileDragging"), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_REPORTDRAG, + p_default, FALSE, + end, + + end +); + +plGUIClickMapComponent::plGUIClickMapComponent() +{ + fClassDesc = &gGUIClickMapDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIClickMapComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetForceLocal( true ); + return plGUIControlBase::SetupProperties( node, pErrMsg ); +} + +hsBool plGUIClickMapComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return plGUIControlBase::PreConvert( node, pErrMsg ); +} + +hsBool plGUIClickMapComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !plGUIControlBase::Convert( node, pErrMsg ) ) + return false; + + pfGUIClickMapCtrl *ctrl = (pfGUIClickMapCtrl *)fControl; + + if( fCompPB->GetInt( kRefReportDragging ) ) + ctrl->SetFlag( pfGUIClickMapCtrl::kReportDragging ); + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUISkin Component //////////////////////////////////////////////////////////////////// +// +// Defines a skin to use when rendering certain GUI controls (just menus for now) +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +class pfGUISkinProc : public ParamMap2UserDlgProc +{ +protected: + +public: + + void DeleteThis() {} + +// virtual void Update( TimeValue t, Interval &valid, IParamMap2 *map ); + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); +}; + +static pfGUISkinProc gGUISkinProc; + +// Component defined in pfGUISkinProc.h + +#define kDeclSkinRectValues( ref ) (plGUISkinComp::##ref + 0), _T("f##ref##.left"), TYPE_INT, 0, 0, p_default, 0, end, \ + (plGUISkinComp::##ref + 1), _T("f##ref##.top"), TYPE_INT, 0, 0, p_default, 0, end, \ + (plGUISkinComp::##ref + 2), _T("f##ref##.width"), TYPE_INT, 0, 0, p_default, 8, end, \ + (plGUISkinComp::##ref + 3), _T("f##ref##.height"), TYPE_INT, 0, 0, p_default, 8, end + +#define kSetSkinRectValues( pb, ref, l, t, w, h ) { pb->SetValue( ref + 0, 0, (int) l ); \ + pb->SetValue( ref + 1, 0, (int) t ); \ + pb->SetValue( ref + 2, 0, (int) r ); \ + pb->SetValue( ref + 3, 0, (int) b ); } + + +//Max desc stuff necessary below. +CLASS_DESC(plGUISkinComp, gGUISkinDesc, "GUI Skin", "GUISkin", COMP_TYPE_GUI, GUI_SKIN_CLASSID ) + +static ParamBlockDesc2 gGUISkinBk +( + /// Main def + plComponent::kBlkComp, _T("GUISkin"), 0, &gGUISkinDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_GUISKIN, IDS_COMP_GUISKIN, 0, 0, &gGUISkinProc, + + plGUISkinComp::kRefBitmap, _T("bitmap"), TYPE_TEXMAP, 0, 0, + end, + + kDeclSkinRectValues( kRefUpLeftCorner ), + kDeclSkinRectValues( kRefTopSpan ), + kDeclSkinRectValues( kRefUpRightCorner ), + kDeclSkinRectValues( kRefRightSpan ), + kDeclSkinRectValues( kRefLowerRightCorner ), + kDeclSkinRectValues( kRefBottomSpan ), + kDeclSkinRectValues( kRefLowerLeftCorner ), + kDeclSkinRectValues( kRefLeftSpan ), + kDeclSkinRectValues( kRefMiddleFill ), + kDeclSkinRectValues( kRefSelectedFill ), + kDeclSkinRectValues( kRefSubMenuArrow ), + kDeclSkinRectValues( kRefSelectedSubMenuArrow ), + kDeclSkinRectValues( kRefTreeButtonClosed ), + kDeclSkinRectValues( kRefTreeButtonOpen ), + + plGUISkinComp::kRefItemMargin, _T("itemMargin"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_GUI_IMARGIN, IDC_GUI_IMARGIN_SPIN, SPIN_AUTOSCALE, + p_default, 1, + end, + + plGUISkinComp::kRefBorderMargin, _T("borderMargin"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_GUI_BMARGIN, IDC_GUI_BMARGIN_SPIN, SPIN_AUTOSCALE, + p_default, 4, + end, + + end +); + +// Editor proc +extern HINSTANCE hInstance; + +BOOL pfGUISkinProc::DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + IParamBlock2 *pb = pmap->GetParamBlock(); + plGUISkinComp *comp = (plGUISkinComp *)pb->GetOwner(); + PBBitmap *bitmap; + plLayerTex *layer = comp->GetSkinBitmap(); + ICustButton *bmSelectBtn; + + switch( msg ) + { + case WM_INITDIALOG: + // Set projection map bitmap name + bmSelectBtn = GetICustButton( GetDlgItem( hWnd, IDC_GUI_SKINBMAP ) ); + if( bmSelectBtn != nil ) + { + bitmap = ( layer == nil ) ? nil : layer->GetPBBitmap(); + if( bitmap != nil ) + bmSelectBtn->SetText( (TCHAR *)bitmap->bi.Filename() ); + else + bmSelectBtn->SetText( _T( "" ) ); + ReleaseICustButton( bmSelectBtn ); + } + + return true; + + case WM_DESTROY: + break; + + case WM_COMMAND: + if( LOWORD( wParam ) == IDC_GUI_EDITELEM ) + { + bitmap = ( layer == nil ) ? nil : layer->GetPBBitmap(); + if( bitmap != nil ) + { + pfGUISkinEditProc proc( comp ); + DialogBox( hInstance, MAKEINTRESOURCE( IDD_COMP_SKINEDIT ), GetCOREInterface()->GetMAXHWnd(), proc.DlgProc ); + } + } + + else if( LOWORD( wParam ) == IDC_GUI_SKINBMAP ) + { + BOOL selectedNewBitmap = layer->HandleBitmapSelection(); + if( selectedNewBitmap ) + { + bmSelectBtn = GetICustButton( GetDlgItem( hWnd, IDC_GUI_SKINBMAP ) ); + bitmap = layer->GetPBBitmap(); + bmSelectBtn->SetText( bitmap != nil ? (TCHAR *)bitmap->bi.Filename() : ""); + ReleaseICustButton( bmSelectBtn ); + } + return false; + } + break; + + } + return false; +} + +plKey plGUISkinComp::GetConvertedSkinKey( void ) const +{ + if( fConvertedSkin != nil ) + return fConvertedSkin->GetKey(); + + return nil; +} + +UInt32 plGUISkinComp::GetNumMtls( void ) const +{ + return 1; +} + +Texmap *plGUISkinComp::GetMtl( UInt32 idx ) +{ + return (Texmap *)GetSkinBitmap(); +} + +//// GetSkinBitmap /////////////////////////////////////////////////////////// + +plLayerTex *plGUISkinComp::GetSkinBitmap( void ) +{ + // If we don't have one, create one + plLayerTex *layer = (plLayerTex *)fCompPB->GetTexmap( kRefBitmap, 0 ); + if( layer == nil || layer->ClassID() != LAYER_TEX_CLASS_ID ) + { + layer = TRACKED_NEW plLayerTex; + + fCompPB->SetValue( kRefBitmap, 0, (Texmap *)layer ); + } + if( layer ) + { + IParamBlock2* bitmapPB = layer->GetParamBlockByID( plLayerTex::kBlkBitmap ); + if( bitmapPB->GetInt(kBmpScaling) != kScalingNone ) + bitmapPB->SetValue(kBmpScaling, TimeValue(0), kScalingNone); + } + + + return layer; +} + +plGUISkinComp::plGUISkinComp() +{ + fClassDesc = &gGUISkinDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUISkinComp::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fConvertedSkin = nil; + return true; +} + +hsBool plGUISkinComp::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + // Create and assign key here, so other components can grab the key later + if( fConvertedSkin != nil ) + return true; // Only convert once, since we don't care what node we're on + + Texmap *texture = fCompPB->GetTexmap( kRefBitmap ); + if( texture == nil || texture->ClassID() != LAYER_TEX_CLASS_ID || ( (plLayerTex *)texture )->GetPBBitmap() == nil ) + { + pErrMsg->Set( true, "GUI Skin Convert Error", + "The GUI skin component %s doesn't have a mipmap associated with it. This skin will not " + "be exported.", GetINode()->GetName() ).CheckAndAsk(); + pErrMsg->Set( false ); + return true; + } + + fConvertedSkin = TRACKED_NEW pfGUISkin(); + hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), fConvertedSkin, node->GetLocation() ); + + return true; +} + +hsBool plGUISkinComp::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + // Actually do the work of converting all the skin data + if( fConvertedSkin == nil ) + return true; // Eh? + + + fConvertedSkin->SetMargins( fCompPB->GetInt( kRefItemMargin ), fCompPB->GetInt( kRefBorderMargin ) ); + + UInt32 i; + for( i = 0; i < pfGUISkin::kNumElements; i++ ) + { + ParamID id = ( i * 4 ) + kRefUpLeftCorner; + + fConvertedSkin->SetElement( i, fCompPB->GetInt( id + 0 ), fCompPB->GetInt( id + 1 ), + fCompPB->GetInt( id + 2 ), fCompPB->GetInt( id + 3 ) ); + } + + plLayerTex *layer= (plLayerTex *)fCompPB->GetTexmap( kRefBitmap ); + if( layer != nil ) + { + PBBitmap *texture = layer->GetPBBitmap(); + if( texture != nil ) + { + plBitmap *bMap = plLayerConverter::Instance().CreateSimpleTexture( texture->bi.Name(), fConvertedSkin->GetKey()->GetUoid().GetLocation(), 0, plMipmap::kForceNonCompressed | plMipmap::kAlphaChannelFlag | plMipmap::kNoMaxSize ); + if( bMap != nil && plMipmap::ConvertNoRef( bMap ) != nil ) + { + hsgResMgr::ResMgr()->AddViaNotify( bMap->GetKey(), TRACKED_NEW plGenRefMsg( fConvertedSkin->GetKey(), + plRefMsg::kOnCreate, -1, pfGUISkin::kRefMipmap ), plRefFlags::kActiveRef ); + } + } + } + + return true; +} + +hsBool plGUISkinComp::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fConvertedSkin = nil; + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plGUIPopUpMenu Component /////////////////////////////////////////////////////////////////// +// +// Defines a pop-up menu, with an auto-anchor to the sceneObject it's attached to +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +/*class plGUIMenuProc : public ParamMap2UserDlgProc +{ +protected: + + void ILoadPages( HWND hWnd, IParamBlock2 *pb ); + +public: + + void DeleteThis() {} + + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); +}; +static plGUIMenuProc gGUIMenuProc; +*/ + +static plGUISingleCtrlDlgProc sGUISkinSelectProc( plGUIMenuComponent::kRefSkin, IDC_GUI_SKIN, + "Select the skin to use for this pop-up menu", sSkinClassesToSelect, + &gGUIDialogProc ); + +//Max desc stuff necessary below. +CLASS_DESC(plGUIMenuComponent, gGUIMenuDesc, "GUI Menu", "GUIMenu", COMP_TYPE_GUI, GUI_MENUANCHOR_CLASSID ) + +ParamBlockDesc2 gGUIMenuBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("GUIMenu"), 0, &gGUIMenuDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP + P_INCLUDE_PARAMS, plComponent::kRefComp, + + 3, + plGUIMenuComponent::kMainRollout, IDD_COMP_GUIMENUANCHOR, IDS_COMP_GUIMENUANCHOR, 0, 0, &sGUISkinSelectProc, + plGUIMenuComponent::kTagIDRollout, IDD_COMP_GUITAG, IDS_COMP_GUITAG, 0, 0, &gGUITagProc, + plGUIMenuComponent::kSchemeRollout, IDD_COMP_GUISCHEME, IDS_COMP_GUISCHEME, 0, 0, &gGUIColorSchemeProc, + + &gGUIColorSchemeBk, + + plGUIMenuComponent::kRefDialogName, _T("MenuName"), TYPE_STRING, 0, 0, +// p_ui, plGUIMenuComponent::kMainRollout, TYPE_EDITBOX, IDC_GUIDLG_NAME, + end, + + plGUIMenuComponent::kRefAgeName, _T("ageName"), TYPE_STRING, 0, 0, + p_default, _T( "GUI" ), + end, + + plGUIMenuComponent::kRefVersion, _T("version"), TYPE_INT, 0, 0, + p_ui, plGUIMenuComponent::kMainRollout, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_GUI_VERSION, IDC_GUI_VERSION_SPIN, SPIN_AUTOSCALE, + p_default, 0, + end, + + plGUITagComponent::kRefCurrIDSel, _T("currSel"), TYPE_INT, 0, 0, + end, + + plGUIMenuComponent::kRefSkin, _T("skin"), TYPE_INODE, 0, 0, + end, + + plGUIMenuComponent::kRefNeverClose, _T("neverClose"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plGUIMenuComponent::kMainRollout, TYPE_SINGLECHEKBOX, IDC_GUI_NEVERCLOSE, + end, + + plGUIMenuComponent::kRefModalOutside, _T("modalOutside"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plGUIMenuComponent::kMainRollout, TYPE_SINGLECHEKBOX, IDC_GUI_MODALOUTSIDE, + end, + + plGUIMenuComponent::kRefOpenOnHover, _T("openSubsOnHover"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plGUIMenuComponent::kMainRollout, TYPE_SINGLECHEKBOX, IDC_GUI_HOVER, + end, + + plGUIMenuComponent::kRefAlignment, _T("alignment"), TYPE_INT, 0, 0, + p_default, 3, + p_ui, plGUIMenuComponent::kMainRollout, TYPE_RADIO, 4, IDC_ALIGNRADIO1, IDC_ALIGNRADIO2, IDC_ALIGNRADIO3, IDC_ALIGNRADIO4, + end, + + plGUIMenuComponent::kRefScaleWithScreenRes, _T("maintainSizeAcrossRes"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, plGUIMenuComponent::kMainRollout, TYPE_SINGLECHEKBOX, IDC_GUI_SCALERES, + end, + + end +); + +plGUIMenuComponent::plGUIMenuComponent() : plGUIDialogComponent( true ) +{ + fClassDesc = &gGUIMenuDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +pfGUIDialogMod *plGUIMenuComponent::IMakeDialog( void ) +{ + return TRACKED_NEW pfGUIPopUpMenu(); +} + +plKey plGUIMenuComponent::GetConvertedMenuKey( void ) const +{ + if( fConvertedMenu == nil ) + return nil; + + return fConvertedMenu->GetKey(); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGUIMenuComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ +// return plGUIDialogComponent::SetupProperties( node, pErrMsg ); + fConvertedMenu = nil; + return true; +} + +hsBool plGUIMenuComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + pfGUIPopUpMenu *menu = fConvertedMenu; + +// hsBool b = plGUIDialogComponent::Convert( node, pErrMsg ); +// if( b ) + { +// pfGUIPopUpMenu *menu = pfGUIPopUpMenu::ConvertNoRef( fDialogMod ); +// hsAssert( menu != nil, "Somehow got a bad poitner in GUIMenu::Convert()" ); + + INode *sNode = fCompPB->GetINode( kRefSkin ); + if( sNode != nil ) + { + plComponentBase *comp = ( (plMaxNode *)sNode )->ConvertToComponent(); + if( comp != nil ) + { + Class_ID nodeID = comp->ClassID(); + hsAssert( nodeID == GUI_SKIN_CLASSID, "Bad node param in GUIMenu::Convert()" ); + + plGUISkinComp *skin = (plGUISkinComp *)comp; + menu->SetSkin( skin->GetConvertedSkin() ); + } + } + + if( fCompPB->GetInt( kRefNeverClose ) ) + menu->SetFlag( pfGUIPopUpMenu::kStayOpenAfterClick ); + + if( fCompPB->GetInt( kRefModalOutside ) ) + menu->SetFlag( pfGUIPopUpMenu::kModalOutsideMenus ); + + if( fCompPB->GetInt( kRefOpenOnHover ) ) + menu->SetFlag( pfGUIPopUpMenu::kOpenSubMenusOnHover ); + + if( fCompPB->GetInt( kRefScaleWithScreenRes ) ) + menu->SetFlag( pfGUIPopUpMenu::kScaleWithResolution ); + + switch( fCompPB->GetInt( kRefAlignment ) ) + { + case 0: menu->SetAlignment( pfGUIPopUpMenu::kAlignUpLeft ); break; + case 1: menu->SetAlignment( pfGUIPopUpMenu::kAlignUpRight ); break; + case 2: menu->SetAlignment( pfGUIPopUpMenu::kAlignDownLeft ); break; + case 3: menu->SetAlignment( pfGUIPopUpMenu::kAlignDownRight ); break; + } + } + + // Note: we use the owning dialog of our anchor object as the context, i.e. who translates + // our point at runtime into screen coordinates + menu->SetOriginAnchor( node->GetSceneObject(), plGUIDialogComponent::GetNodeDialog( node ) ); + + const plLocation &loc = menu->GetKey()->GetUoid().GetLocation(); + + // Create the rendermod + plPostEffectMod *renderMod = TRACKED_NEW plPostEffectMod; + hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), renderMod, loc ); + + renderMod->SetHither( 0.5f ); + renderMod->SetYon( 200.f ); + renderMod->SetNodeKey( fConvertedNode ); + + float scrnWidth = 20.f; + + // fovX should be such that scrnWidth is the projected width at z=100 + float fovX = atan( scrnWidth / ( 2.f * 100.f ) ) * 2.f; + float fovY = fovX;// * 3.f / 4.f; + + renderMod->SetFovX( fovX * 180.f / hsScalarPI ); + renderMod->SetFovY( fovY * 180.f / hsScalarPI ); + + + hsgResMgr::ResMgr()->AddViaNotify( renderMod->GetKey(), TRACKED_NEW plNodeRefMsg( fConvertedNode, plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric ), plRefFlags::kActiveRef ); + hsgResMgr::ResMgr()->AddViaNotify( fConvertedNode, TRACKED_NEW plGenRefMsg( renderMod->GetKey(), plRefMsg::kOnCreate, 0, plPostEffectMod::kNodeRef ), plRefFlags::kPassiveRef ); + + menu->SetRenderMod( renderMod ); + menu->SetName( fCompPB->GetStr( kRefDialogName ) ); + + // Create the dummy scene object to hold the menu + plSceneObject *newObj = TRACKED_NEW plSceneObject; + hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), newObj, loc ); + + // *#&$(*@&#$ need a coordIface... + plCoordinateInterface *newCI = TRACKED_NEW plCoordinateInterface; + hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), newCI, loc ); + + + hsgResMgr::ResMgr()->AddViaNotify( menu->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); + + hsgResMgr::ResMgr()->AddViaNotify( newCI->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface ), plRefFlags::kActiveRef ); + hsgResMgr::ResMgr()->AddViaNotify( renderMod->GetKey(), TRACKED_NEW plObjRefMsg( newObj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); + + newObj->SetSceneNode( fConvertedNode ); + menu->SetSceneNodeKey( fConvertedNode ); + + { + hsMatrix44 l2w, w2l; + l2w.Reset(); + l2w.GetInverse( &w2l ); + newObj->SetTransform( l2w, w2l ); + } + + // Should be done now... + return true; +} + +hsBool plGUIMenuComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + // Create and assign key here, so other components can grab the key later + if( fConvertedMenu != nil ) + return true; // Only convert once, since we don't care what node we're on + + /// Create an entirely new sceneNode for us + Int32 seqNum = plPageInfoUtils::GetSeqNumFromAgeDesc( fCompPB->GetStr( kRefAgeName ), fCompPB->GetStr( kRefDialogName ) ); + Int32 newNum = plPluginResManager::ResMgr()->VerifySeqNumber( seqNum, fCompPB->GetStr( kRefAgeName ), fCompPB->GetStr( kRefDialogName ) ); + if( newNum != seqNum ) + { + if( !fSeqNumValidated ) + { + char errMsg[ 512 ]; + sprintf( errMsg, "GUI Menu Component %s has an invalid location sequence number (0x%X). Temporarily using a valid one (0x%X).", + node->GetName(), seqNum, newNum ); + pErrMsg->Set( true, "PageInfo Convert Error", errMsg ).Show(); + pErrMsg->Set( false ); + fSeqNumValidated = true; + } + seqNum = newNum; + } + + fConvertedNode = plPluginResManager::ResMgr()->NameToLoc( fCompPB->GetStr( kRefAgeName ), fCompPB->GetStr( kRefDialogName ), seqNum ); + if( !fConvertedNode ) + { + pErrMsg->Set( true, "GUI Menu Component Error", "GUI MenuComponent %s has a Missing Location. Nuke the files in the dat directory and re-export.",((INode*)node)->GetName()).Show(); + return false; + } + + fConvertedMenu = TRACKED_NEW pfGUIPopUpMenu(); + hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), fConvertedMenu, fConvertedNode->GetUoid().GetLocation() ); + + return true; + +// return plGUIDialogComponent::PreConvert( node, pErrMsg ); +} + +hsBool plGUIMenuComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fConvertedMenu = nil; + fConvertedNode = nil; + return true; +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUIComponents.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUIComponents.h new file mode 100644 index 00000000..c19bc5c9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGUIComponents.h @@ -0,0 +1,222 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plGUIComponents Header // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plGUIComponents_h +#define _plGUIComponents_h + +#include "../pnKeyedObject/plKey.h" + +#include "plGUICompClassIDs.h" +#include "plComponent.h" + + +////////////////////////////////////////////////////////////////////////////// +//// Component Class Definitions ///////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// Dialog Component //////////////////////////////////////////////////////// + +class plMaxNode; +class pfGUIDialogMod; +class plErrorMsg; + +class plGUIDialogComponent : public plComponent +{ + protected: + void IMakeEveryoneOpaqueRecur( plMaxNode *node ); + void IMakeEveryoneOpaque( plMaxNode *node ); + + pfGUIDialogMod *fDialogMod; + plKey fProcReceiver; // non-nil means to send out notifys as our proc + hsBool fSeqNumValidated; + + virtual pfGUIDialogMod *IMakeDialog( void ); + + public: + // I believe booleans should always default to false, hence why this is dontInit instead of init. Byte me. + plGUIDialogComponent( hsBool dontInit = false ); + void DeleteThis() { delete this; } + + hsBool SetupProperties( plMaxNode *pNode, plErrorMsg *pErrMsg ); + hsBool PreConvert( plMaxNode *pNode, plErrorMsg *pErrMsg ); + hsBool Convert( plMaxNode *node, plErrorMsg *pErrMsg ); + hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg) { fProcReceiver = nil; return true;} + + pfGUIDialogMod *GetModifier( void ) { return fDialogMod; } + + // For those too lazy to get it from the modifier + // ... or can't trust that just because you have a modifier doesn't mean that you have a key :-) :-) + plKey GetModifierKey( void ); + + // Set this to have the dialog send out notify messages on events. Do it before Convert(). Returns false if it failed. + bool SetNotifyReceiver( plKey key ); + + static pfGUIDialogMod *GetNodeDialog( plMaxNode *childNode ); + + enum + { + kRefDialogName, + kRefIsModal, + kRefVersion, + kRefAgeName, + kRefDerivedStart + }; + + enum + { + kMainRollout, + kTagIDRollout, + kSchemeRollout + }; +}; + +//// Control Component Base Class //////////////////////////////////////////// + +class pfGUIControlMod; +class hsGMaterial; + +class plGUIControlBase : public plComponent +{ + protected: + + pfGUIControlMod *fControl; + + + pfGUIDialogMod *IGetDialogMod( plMaxNode *node ); + + virtual pfGUIControlMod *IGetNewControl( void ) = 0; + virtual bool IHasProcRollout( void ) { return true; } + virtual bool INeedsDynamicText( void ) { return false; } + virtual bool ICanHaveProxy( void ) { return false; } + + const char *ISetSoundIndex( ParamID checkBoxID, ParamID sndCompID, UInt8 guiCtrlEvent, plMaxNode *node ); + + + // When converting, since we get a new instance per component but not per node, + // we need to keep track of which nodes we get PreConverted() on and the controls that + // get created for each. Then, on Convert(), we look up the node in our list and grab + // the right control. A pain, but what are you going to do? + hsTArray fTargetNodes; + hsTArray fTargetControls; + + public: + plGUIControlBase() {} + void DeleteThis() { delete this; } + + hsBool SetupProperties( plMaxNode *pNode, plErrorMsg *pErrMsg ); + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables( INodeTab &nonDrawables ); + + virtual UInt32 GetNumMtls( void ) const { return 0; } + virtual Texmap *GetMtl( UInt32 idx ) { return nil; } + + // Given a maxNode that is really a component, will return a pointer to the GUI control modifier + // created for it at export time. Only valid after PreConvert. If you think the control component + // might be applied to more than one sceneObject, then you better supply the sceneObject you're + // asking for as well to make sure you get the right control. If not, just leave the second + // parameter nil, but that can be VERY dangerous if the component results in more than one + // GUI control. + static pfGUIControlMod *GrabControlMod( INode *node, INode *sceneObjectNode = nil ); + + // Like GrabControlMod, but for when you already have a pointer to some kind of component + static pfGUIControlMod *ConvertCompToControl( plComponentBase *comp, INode *sceneObjectNode ); + + // Given a MAX object node, returns the one (and hopefully only) pfGUIControlMod attached to the scene object. Valid after PreConvert. + static pfGUIControlMod *GrabControlFromObject( INode *node ); + + // Given an INode, gives you a pointer to the GUI component if it actually is one, nil otherwise + static plGUIControlBase *GetGUIComp( INode *node ); + + // Or a plComponentBase... + static plGUIControlBase *GetGUIComp( plComponentBase *base ); + + enum + { + kBlkProc = plComponent::kBlkComp + 1, + kRollMain = 1, + kRollProc = 32, + kRollProxy = 33 + }; + + enum + { + kRefChoice = 32, + kRefConsoleCmd + }; +}; + +//// Pop-Up Menu Class /////////////////////////////////////////////////////// + +class pfGUIPopUpMenu; +class plGUIMenuComponent : public plGUIDialogComponent +{ + protected: + + virtual pfGUIDialogMod *IMakeDialog( void ); + + pfGUIPopUpMenu *fConvertedMenu; + plKey fConvertedNode; + + public: + plGUIMenuComponent(); + void DeleteThis() { delete this; } + + virtual hsBool SetupProperties( plMaxNode *pNode, plErrorMsg *pErrMsg ); + virtual hsBool PreConvert( plMaxNode *pNode, plErrorMsg *pErrMsg ); + virtual hsBool Convert( plMaxNode *node, plErrorMsg *pErrMsg ); + virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + plKey GetConvertedMenuKey( void ) const; + + enum + { + kRefSkin = kRefDerivedStart, + kRefNeverClose, + kRefModalOutside, + kRefOpenOnHover, + kRefAlignment, + kRefScaleWithScreenRes + }; + +/* enum + { + kMainRollout, + kTagIDRollout, + kSchemeRollout + }; +*/ +}; + + + +#endif //_plGUIComponents_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGrassComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGrassComponent.cpp new file mode 100644 index 00000000..418b58e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGrassComponent.cpp @@ -0,0 +1,302 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include + +#include "HeadSpin.h" +#include "max.h" +#include "resource.h" +#include "hsTemplates.h" +#include "hsResMgr.h" +#include "plQuality.h" +#include "../pnMessage/plRefMsg.h" +#include "../../PubUtilLib/plSurface/hsGMaterial.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxConvert/hsMaterialConverter.h" + +#include "plComponent.h" +#include "plComponentReg.h" +#include "plGrassComponent.h" +#include "../../PubUtilLib/plSurface/plGrassShaderMod.h" + +#include "../pnKeyedObject/plUoid.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../MaxMain/plPluginResManager.h" + +void DummyCodeIncludeFuncGrassShader() +{ +} + +class GrassCompDlgProc : public ParamMap2UserDlgProc +{ +public: + GrassCompDlgProc() {} + ~GrassCompDlgProc() {} + +protected: + void ISetToWave(int wave, HWND hWnd, IParamBlock2 *pb, IParamMap2 *map) + { + HWND cbox = GetDlgItem(hWnd, IDC_GRASS_WAVE); + SendMessage(cbox, CB_SETCURSEL, wave, 0); + pb->SetValue(ParamID(plGrassComponent::kWave), 0, wave); + pb->SetValue(ParamID(plGrassComponent::kDistX), 0, pb->GetFloat(ParamID(plGrassComponent::kDistXTab), 0, wave)); + pb->SetValue(ParamID(plGrassComponent::kDistY), 0, pb->GetFloat(ParamID(plGrassComponent::kDistYTab), 0, wave)); + pb->SetValue(ParamID(plGrassComponent::kDistZ), 0, pb->GetFloat(ParamID(plGrassComponent::kDistZTab), 0, wave)); + pb->SetValue(ParamID(plGrassComponent::kDirX), 0, pb->GetFloat(ParamID(plGrassComponent::kDirXTab), 0, wave)); + pb->SetValue(ParamID(plGrassComponent::kDirY), 0, pb->GetFloat(ParamID(plGrassComponent::kDirYTab), 0, wave)); + pb->SetValue(ParamID(plGrassComponent::kSpeed), 0, pb->GetFloat(ParamID(plGrassComponent::kSpeedTab), 0, wave)); + map->Invalidate(); + } + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + int id = LOWORD(wParam); + int code = HIWORD(wParam); + + IParamBlock2 *pb = map->GetParamBlock(); + HWND cbox = NULL; + + int selection; + switch (msg) + { + case WM_INITDIALOG: + cbox = GetDlgItem(hWnd, IDC_GRASS_WAVE); + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)"1"); + SendMessage(cbox, CB_ADDSTRING, 1, (LPARAM)"2"); + SendMessage(cbox, CB_ADDSTRING, 2, (LPARAM)"3"); + SendMessage(cbox, CB_ADDSTRING, 3, (LPARAM)"4"); + + selection = pb->GetInt(plGrassComponent::kWave); + ISetToWave(selection, hWnd, pb, map); + return TRUE; + + case WM_COMMAND: + case CC_SPINNER_CHANGE: + int wave = SendMessage(GetDlgItem(hWnd, IDC_GRASS_WAVE), CB_GETCURSEL, 0, 0); + if (id == IDC_GRASS_WAVE) + { + if (wave != pb->GetInt(ParamID(plGrassComponent::kWave))) + ISetToWave(wave, hWnd, pb, map); + } + else if (id == IDC_GRASS_DIST_X || id == IDC_GRASS_DIST_X_SPIN) + pb->SetValue(plGrassComponent::kDistXTab, 0, pb->GetFloat(ParamID(plGrassComponent::kDistX)), wave); + else if (id == IDC_GRASS_DIST_Y || id == IDC_GRASS_DIST_Y_SPIN) + pb->SetValue(plGrassComponent::kDistYTab, 0, pb->GetFloat(ParamID(plGrassComponent::kDistY)), wave); + else if (id == IDC_GRASS_DIST_Z || id == IDC_GRASS_DIST_Z_SPIN) + pb->SetValue(plGrassComponent::kDistZTab, 0, pb->GetFloat(ParamID(plGrassComponent::kDistZ)), wave); + else if (id == IDC_GRASS_DIR_X || id == IDC_GRASS_DIR_X_SPIN) + pb->SetValue(plGrassComponent::kDirXTab, 0, pb->GetFloat(ParamID(plGrassComponent::kDirX)), wave); + else if (id == IDC_GRASS_DIR_Y || id == IDC_GRASS_DIR_Y_SPIN) + pb->SetValue(plGrassComponent::kDirYTab, 0, pb->GetFloat(ParamID(plGrassComponent::kDirY)), wave); + else if (id == IDC_GRASS_SPEED || id == IDC_GRASS_SPEED_SPIN) + pb->SetValue(plGrassComponent::kSpeedTab, 0, pb->GetFloat(ParamID(plGrassComponent::kSpeed)), wave); + + return TRUE; + } + return FALSE; + } + void DeleteThis() {} +}; +static GrassCompDlgProc gGrassCompDlgProc; + +CLASS_DESC(plGrassComponent, gGrassDesc, "Grass", "Grass", COMP_TYPE_SHADERS, GRASS_COMPONENT_CLASS_ID) + +ParamBlockDesc2 gGrassBk +( + + plComponent::kBlkComp, _T("Grass"), 0, &gGrassDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_GRASS, IDS_COMP_GRASS_ROLL, 0, 0, &gGrassCompDlgProc, + + + plGrassComponent::kWave, _T("Wave"), TYPE_INT, 0, 0, + p_default, 0, + end, + + plGrassComponent::kDistX, _T("DistX"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_GRASS_DIST_X, IDC_GRASS_DIST_X_SPIN, 0.1, + end, + + plGrassComponent::kDistY, _T("DistY"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_GRASS_DIST_Y, IDC_GRASS_DIST_Y_SPIN, 0.1, + end, + + plGrassComponent::kDistZ, _T("DistZ"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_GRASS_DIST_Z, IDC_GRASS_DIST_Z_SPIN, 0.1, + end, + + plGrassComponent::kDirX, _T("DirX"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_GRASS_DIR_X, IDC_GRASS_DIR_X_SPIN, 0.1, + end, + + plGrassComponent::kDirY, _T("DirY"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_GRASS_DIR_Y, IDC_GRASS_DIR_Y_SPIN, 0.1, + end, + + plGrassComponent::kSpeed, _T("Speed"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_GRASS_SPEED, IDC_GRASS_SPEED_SPIN, 0.1, + end, + + plGrassComponent::kDistXTab, _T("DistXTab"), TYPE_FLOAT_TAB, plGrassShaderMod::kNumWaves, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + end, + + plGrassComponent::kDistYTab, _T("DistYTab"), TYPE_FLOAT_TAB, plGrassShaderMod::kNumWaves, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + end, + + plGrassComponent::kDistZTab, _T("DistZTab"), TYPE_FLOAT_TAB, plGrassShaderMod::kNumWaves, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + end, + + plGrassComponent::kDirXTab, _T("DirXTab"), TYPE_FLOAT_TAB, plGrassShaderMod::kNumWaves, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + end, + + plGrassComponent::kDirYTab, _T("DirYTab"), TYPE_FLOAT_TAB, plGrassShaderMod::kNumWaves, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + end, + + plGrassComponent::kSpeedTab, _T("SpeedTab"), TYPE_FLOAT_TAB, plGrassShaderMod::kNumWaves, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + end, + + end +); + +plGrassComponent::plGrassComponent() : fShader(nil) +{ + fClassDesc = &gGrassDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plGrassComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fShader = TRACKED_NEW plGrassShaderMod(); + + plLoadMask loadMask; + int qual = 1; + int cap = plQuality::kPS_1_1; + plLoadMask::ComputeRepMasks(1, &qual, &cap, &loadMask); + plKey modKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), fShader, node->GetLocation(), loadMask); + + int i; + for (i = 0; i < plGrassShaderMod::kNumWaves; i++) + { + fShader->fWaves[i].fDistX = fCompPB->GetFloat(ParamID(kDistXTab), 0, i); + fShader->fWaves[i].fDistY = fCompPB->GetFloat(ParamID(kDistYTab), 0, i); + fShader->fWaves[i].fDistZ = fCompPB->GetFloat(ParamID(kDistZTab), 0, i); + fShader->fWaves[i].fDirX = fCompPB->GetFloat(ParamID(kDirXTab), 0, i); + fShader->fWaves[i].fDirY = fCompPB->GetFloat(ParamID(kDirYTab), 0, i); + fShader->fWaves[i].fSpeed = fCompPB->GetFloat(ParamID(kSpeedTab), 0, i); + } + + // Add a ref to the shader. + fShader->GetKey()->RefObject(); + + return true; +} + +hsBool plGrassComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plObjRefMsg* refMsg = TRACKED_NEW plObjRefMsg(node->GetKey(), plRefMsg::kOnRequest, -1, plObjRefMsg::kModifier); + hsgResMgr::ResMgr()->AddViaNotify(fShader->GetKey(), refMsg, plRefFlags::kActiveRef); + + hsTArray mats; + hsMaterialConverter::Instance().CollectConvertedMaterials(hsMaterialConverter::Instance().GetBaseMtl(node), mats); + hsgResMgr::ResMgr()->SendRef(mats[0]->GetKey(), TRACKED_NEW plGenRefMsg(fShader->GetKey(), plRefMsg::kOnRequest, 0, plGrassShaderMod::kRefMaterial), plRefFlags::kActiveRef); + + return TRUE; +} + +hsBool plGrassComponent::DeInit(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( fShader ) + fShader->GetKey()->UnRefObject(); + fShader = nil; + + return true; +} + +plGrassShaderMod* plGrassComponent::GetShader(INode* node) +{ + if( !node ) + return nil; + + plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); + if( !comp ) + return nil; + + if( comp->ClassID() != GRASS_COMPONENT_CLASS_ID ) + return nil; + + plGrassComponent *shader = (plGrassComponent*)comp; + return shader->fShader; +} + +plGrassShaderMod* plGrassComponent::GetShaderNode(plMaxNode* node) +{ + if( !node ) + return nil; + + int n = node->NumAttachedComponents(); + int i; + for( i = 0; i < n; i++ ) + { + plComponentBase* comp = node->GetAttachedComponent(i); + if( comp && (comp->ClassID() == GRASS_COMPONENT_CLASS_ID) ) + { + plGrassComponent* shader = (plGrassComponent*)comp; + return shader->fShader; + } + } + return nil; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGrassComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGrassComponent.h new file mode 100644 index 00000000..38aebfac --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plGrassComponent.h @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLGRASSCOMPONENT_INC +#define PLGRASSCOMPONENT_INC + +class plGrassShaderMod; + +class plGrassComponent : public plComponent +{ +protected: + plGrassShaderMod *fShader; +public: + + plGrassComponent(); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool DeInit(plMaxNode* node, plErrorMsg* pErrMsg); + virtual void DeleteThis() { delete this; } + + // These only work after PreConvert pass + static plGrassShaderMod* GetShader(INode* node); // Node is the component node + static plGrassShaderMod* GetShaderNode(plMaxNode* node); // node is the component's target + + enum + { + kWave, + kDistX, + kDistY, + kDistZ, + kDirX, + kDirY, + kSpeed, + kDistXTab, + kDistYTab, + kDistZTab, + kDirXTab, + kDirYTab, + kSpeedTab, + }; +}; + +#define GRASS_COMPONENT_CLASS_ID Class_ID(0x1a422bfe, 0xe0e3f07) + + + +#endif // PLGRASSCOMPONENT_INC diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plIgnoreComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plIgnoreComponent.cpp new file mode 100644 index 00000000..e8c254ec --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plIgnoreComponent.cpp @@ -0,0 +1,417 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plMiscComponents.h" +#include "plComponentReg.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "../MaxMain/plMaxNode.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plDrawInterface.h" + +#include "../plMessage/plSimStateMsg.h" +#include "../pnMessage/plEnableMsg.h" + +#include "../MaxMain/plPluginResManager.h" + +void DummyCodeIncludeFuncIgnore() {} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Ignore Component +// +// + +//Class that accesses the paramblock below. +class plIgnoreComponent : public plComponent +{ +public: + plIgnoreComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plIgnoreComponent, gIgnoreDesc, "Ignore", "Ignore", COMP_TYPE_IGNORE, Class_ID(0x48326288, 0x528a3dea)) + +enum +{ + kIgnoreMeCheckBx +}; + +ParamBlockDesc2 gIgnoreBk +( + plComponent::kBlkComp, _T("Ignore"), 0, &gIgnoreDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_IGNORE, IDS_COMP_IGNORES, 0, 0, NULL, + + kIgnoreMeCheckBx, _T("Ignore"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_IGNORE_CKBX, + end, + + end +); + +plIgnoreComponent::plIgnoreComponent() +{ + fClassDesc = &gIgnoreDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +void plIgnoreComponent::CollectNonDrawables(INodeTab& nonDrawables) +{ + if (fCompPB->GetInt(kIgnoreMeCheckBx)) + { + AddTargetsToList(nonDrawables); + } +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plIgnoreComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + if (fCompPB->GetInt(kIgnoreMeCheckBx)) + pNode->SetCanConvert(false); + + return true; +} + +hsBool plIgnoreComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// IgnoreLite Component +// +// + +//Class that accesses the paramblock below. +class plIgnoreLiteComponent : public plComponent +{ +public: + enum { + kSelectedOnly + }; + enum LightState { + kTurnOn, + kTurnOff, + kToggle + }; +public: + plIgnoreLiteComponent(); + + void SetState(LightState s); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) { return true; } + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) { return true; } + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + + +class plIgnoreLiteProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + + case WM_COMMAND: + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_COMP_IGNORELITE_ON) ) + { + plIgnoreLiteComponent* ilc = (plIgnoreLiteComponent*)map->GetParamBlock()->GetOwner(); + ilc->SetState(plIgnoreLiteComponent::kTurnOn); + + return TRUE; + } + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_COMP_IGNORELITE_OFF) ) + { + plIgnoreLiteComponent* ilc = (plIgnoreLiteComponent*)map->GetParamBlock()->GetOwner(); + ilc->SetState(plIgnoreLiteComponent::kTurnOff); + + return TRUE; + } + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_COMP_IGNORELITE_TOGGLE) ) + { + plIgnoreLiteComponent* ilc = (plIgnoreLiteComponent*)map->GetParamBlock()->GetOwner(); + ilc->SetState(plIgnoreLiteComponent::kToggle); + + return TRUE; + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plIgnoreLiteProc gIgnoreLiteProc; + + +//Max desc stuff necessary below. +CLASS_DESC(plIgnoreLiteComponent, gIgnoreLiteDesc, "Control Max Light", "ControlLite", COMP_TYPE_IGNORE, IGNORELITE_CID) + +ParamBlockDesc2 gIgnoreLiteBk +( + plComponent::kBlkComp, _T("IgnoreLite"), 0, &gIgnoreLiteDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_IGNORELITE, IDS_COMP_IGNORELITES, 0, 0, &gIgnoreLiteProc, + + plIgnoreLiteComponent::kSelectedOnly, _T("SelectedOnly"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_IGNORELITE_SELECTED, + end, + + end +); + +plIgnoreLiteComponent::plIgnoreLiteComponent() +{ + fClassDesc = &gIgnoreLiteDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +void plIgnoreLiteComponent::SetState(LightState s) +{ + BOOL selectedOnly = fCompPB->GetInt(kSelectedOnly); + + int numTarg = NumTargets(); + int i; + for( i = 0; i < numTarg; i++ ) + { + plMaxNodeBase* targ = GetTarget(i); + if( targ ) + { + if( selectedOnly && !targ->Selected() ) + continue; + + Object *obj = targ->EvalWorldState(TimeValue(0)).obj; + if (obj && (obj->SuperClassID() == SClass_ID(LIGHT_CLASS_ID))) + { + LightObject* liObj = (LightObject*)obj; + switch( s ) + { + case kTurnOn: + liObj->SetUseLight(true); + break; + case kTurnOff: + liObj->SetUseLight(false); + break; + case kToggle: + liObj->SetUseLight(!liObj->GetUseLight()); + break; + } + } + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Barney Component +// +// + +//Class that accesses the paramblock below. +class plBarneyComponent : public plComponent +{ +public: + plBarneyComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plBarneyComponent, gBarneyDesc, "Barney", "Barney", COMP_TYPE_IGNORE, Class_ID(0x376955dc, 0x2fec50ae)) + +ParamBlockDesc2 gBarneyBk +( + plComponent::kBlkComp, _T("Barney"), 0, &gBarneyDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_BARNEY, IDS_COMP_BARNEYS, 0, 0, NULL, + + end +); + +plBarneyComponent::plBarneyComponent() +{ + fClassDesc = &gBarneyDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plBarneyComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + pNode->SetCanConvert(false); + pNode->SetIsBarney(true); + return true; +} + +hsBool plBarneyComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// NoShow Component +// +// + +//Class that accesses the paramblock below. +class plNoShowComponent : public plComponent +{ +public: + enum + { + kShowable, + kAffectDraw, + kAffectPhys + }; + + +public: + plNoShowComponent(); + + virtual void CollectNonDrawables(INodeTab& nonDrawables); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +const Class_ID COMP_NOSHOW_CID(0x41cb2b85, 0x615932c6); + +//Max desc stuff necessary below. +CLASS_DESC(plNoShowComponent, gNoShowDesc, "NoShow", "NoShow", COMP_TYPE_IGNORE, COMP_NOSHOW_CID) + +ParamBlockDesc2 gNoShowBk +( + plComponent::kBlkComp, _T("NoShow"), 0, &gNoShowDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_NOSHOW, IDS_COMP_NOSHOW, 0, 0, NULL, + + plNoShowComponent::kShowable, _T("Showable"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_NOSHOW_SHOWABLE, + end, + + plNoShowComponent::kAffectDraw, _T("AffectDraw"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_NOSHOW_AFFECTDRAW, + end, + + plNoShowComponent::kAffectPhys, _T("AffectPhys"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_NOSHOW_AFFECTPHYS, + end, + + end +); + +plNoShowComponent::plNoShowComponent() +{ + fClassDesc = &gNoShowDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plNoShowComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + if( !fCompPB->GetInt(kShowable) ) + { + if( fCompPB->GetInt(kAffectDraw) ) + pNode->SetDrawable(false); + if( fCompPB->GetInt(kAffectPhys) ) + pNode->SetPhysical(false); + } + + return true; +} + +hsBool plNoShowComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plSceneObject* obj = node->GetSceneObject(); + if( !obj ) + return true; + + if( fCompPB->GetInt(kShowable) ) + { + if( fCompPB->GetInt(kAffectDraw) ) + { + plEnableMsg* eMsg = TRACKED_NEW plEnableMsg(nil, plEnableMsg::kDisable, plEnableMsg::kDrawable); + eMsg->AddReceiver(obj->GetKey()); + eMsg->Send(); + } + if( fCompPB->GetInt(kAffectPhys) ) + { + hsAssert(0, "Who uses this?"); +// plEventGroupEnableMsg* pMsg = TRACKED_NEW plEventGroupEnableMsg; +// pMsg->SetFlags(plEventGroupEnableMsg::kCollideOff | plEventGroupEnableMsg::kReportOff); +// pMsg->AddReceiver(obj->GetKey()); +// pMsg->Send(); + } +#if 0 + plDrawInterface* di = node->GetDrawInterface(); + if( di && + { + di->SetProperty(plDrawInterface::kDisable, true); + } +#endif + } + return true; +} +void plNoShowComponent::CollectNonDrawables(INodeTab& nonDrawables) +{ + if( fCompPB->GetInt(kAffectDraw) ) + AddTargetsToList(nonDrawables); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plImpactGadgetComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plImpactGadgetComponent.cpp new file mode 100644 index 00000000..a8983451 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plImpactGadgetComponent.cpp @@ -0,0 +1,180 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plImpactGadgetComponent.h" +#include "resource.h" +//#include "plComponent.h" +#include "plComponentReg.h" + + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plPhysical/plCollisionDetector.h" // MM +#include "../plModifier/plLogicModifier.h" +#include "../../NucleusLib/pnModifier/plConditionalObject.h" +#include "../plPhysical/plPickingDetector.h" +#include "../pfConditional/plActivatorConditionalObject.h" +#include "../pfConditional/plFacingConditionalObject.h" +#include "../pfConditional/plObjectInBoxConditionalObject.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnMessage/plCursorChangeMsg.h" + +#include "hsResMgr.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxConvert/plConvert.h" + +#include "plResponderComponent.h" + +#include "../MaxConvert/hsConverterUtils.h" //Conversion Dependencies +#include "plPhysicalComponents.h" +#include "../pnMessage/plIntRefMsg.h" // Ibid +#include "plComponentProcBase.h" + +#include "../MaxMain/plPhysicalProps.h" + +void DummyCodeIncludeFuncImpactGadget() {} + +// enum +// { +// kImpactObject_DEAD, +// kImpactOneShot, +// kUseImpactNode_DEAD, +// kImpactNode_DEAD, +// kImpactBoundsType_DEAD, +// kImpactBounceChoice_DEAD, +// kImpactBounceBoolTab_DEAD, +// kImpactReportChoice_DEAD, +// kImpactReportBoolTab_DEAD, +// kImpactEnabled, +// kImpactBounceGroups_DEAD, +// kImpactReportGroups, +// kImpactUseVelocity_DEAD, +// kImpactVelocity_DEAD, +// }; +// +// #include "plEventGroupUI.h" + +// static plEventGroupProc gReportGroupProc(kImpactReportGroups, "Report collisions with these groups", false); + +OBSOLETE_CLASS(plImpactGadget, gImpactGadgetDesc, "Collision Sensor", "CollisionSensor", COMP_TYPE_DETECTOR, IMPACTGADGET_CID) + +// enum +// { +// kImpactMain, +// kImpactBounce_DEAD, +// kImpactReport, +// }; +// +// ParamBlockDesc2 gImpactGadgetBlock +// ( +// plComponent::kBlkComp, _T("ImpactGadgetComp"), 0, &gImpactGadgetDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, +// +// 2, +// kImpactMain, IDD_COMP_DETECTOR_COLLISION, IDS_COMP_DETECTOR_COLLISION, 0, 0, NULL, +// kImpactReport, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_REPORT, 0, APPENDROLL_CLOSED, &gReportGroupProc, +// +// kImpactOneShot, _T("oneshot"), TYPE_BOOL, 0, 0, +// p_ui, kImpactMain, TYPE_SINGLECHEKBOX, IDC_ONESHOT, +// end, +// +// kImpactReportGroups, _T("reportGroups"), TYPE_INT, 0,0, +// end, +// +// kImpactEnabled, _T("enabled"), TYPE_BOOL, 0, 0, +// p_ui, kImpactMain, TYPE_SINGLECHEKBOX, IDC_ENABLED, +// p_default, TRUE, +// end, +// +// end +// ); +// +// +// plImpactGadget::plImpactGadget() +// { +// fClassDesc = &gImpactGadgetDesc; +// fClassDesc->MakeAutoParamBlocks(this); +// } +// +// hsBool plImpactGadget::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +// { +// plActivatorBaseComponent::SetupProperties(node, pErrMsg); +// +// plPhysicalProps *physProps = node->GetPhysicalProps(); +// physProps->SetReportGroup(plEventGroupProc::GetGroups(fCompPB, kImpactReportGroups), node, pErrMsg); +// +// return true; +// } +// +// hsBool plImpactGadget::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +// { +// plLocation loc = node->GetLocation(); +// plSceneObject *obj = node->GetSceneObject(); +// +// plKey logicKey = fLogicModKeys[node]; +// plLogicModifier *logic = plLogicModifier::ConvertNoRef(logicKey->GetObjectPtr()); +// +// if (fCompPB->GetInt(kImpactOneShot)) +// logic->SetFlag(plLogicModBase::kOneShot); +// +// hsTArray receivers; +// IGetReceivers(node, receivers); +// for (int i = 0; i < receivers.Count(); i++) +// logic->AddNotifyReceiver(receivers[i]); +// +// // Get the physical node (where the mod is going to be put) +// plMaxNode* physNode = node->GetPhysicalProps()->GetProxyNode(); +// if (!physNode) +// physNode = node; +// +// // Create remote detector +// plCollisionDetector* det = TRACKED_NEW plCollisionDetector; +// det->SetType(plCollisionDetector::kTypeBump); +// +// // Register the detector +// plKey detKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), det, loc); +// hsgResMgr::ResMgr()->AddViaNotify(detKey, TRACKED_NEW plObjRefMsg(physNode->GetSceneObject()->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); +// +// // create and register the CONDITIONS for the DETECTOR's Logic Modifier +// plActivatorConditionalObject* activatorCond = TRACKED_NEW plActivatorConditionalObject; +// plKey activatorKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), activatorCond, loc); +// +// // link everything up: +// logic->AddCondition(activatorCond); // add this activator condition +// logic->SetDisabled(fCompPB->GetInt(kImpactEnabled) == 0); +// +// // Set up the remote detector (if any) +// activatorCond->SetActivatorKey(detKey); +// det->AddLogicObj(logicKey); +// +// // If this is for the SceneViewer, set the local only flag since the read function will never be called +// if (plConvert::Instance().IsForSceneViewer()) +// logic->SetLocalOnly(true); +// +// return true; +// } diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plImpactGadgetComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plImpactGadgetComponent.h new file mode 100644 index 00000000..50d22328 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plImpactGadgetComponent.h @@ -0,0 +1,42 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plImpactGadgetComponent_inc +#define plImpactGadgetComponent_inc + +#include "plActivatorBaseComponent.h" + +#define IMPACTGADGET_CID Class_ID(0x6cb343d6, 0x36995c82) + +// class plImpactGadget : public plActivatorBaseComponent +// { +// public: +// plImpactGadget(); +// +// hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); +// hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +// }; + +#endif // plImpactGadgetComponent_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plInventoryObjComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plInventoryObjComponent.cpp new file mode 100644 index 00000000..6a75615a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plInventoryObjComponent.cpp @@ -0,0 +1,261 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plInventoryObjComponent.h" //Inventory Dependencies + +#include "resource.h" //Resource Dependencies +#include "hsResMgr.h" // Ibid + +#include "plComponent.h" //Component Dependencies +#include "plComponentReg.h" // Ibid +#include "../pnSceneObject/plSceneObject.h" // Ibid +#include "../pnKeyedObject/hsKeyedObject.h" // Ibid +#include "../MaxMain/plMaxNode.h" // Ibid +#include "plResponderComponent.h" + + +#include "../plPhysical/plCollisionDetector.h" //Modifiers Dependencies +#include "../plModifier/plLogicModifier.h" // Ibid +#include "../plModifier/plAxisAnimModifier.h" // Ibid +#include "../../NucleusLib/pnModifier/plConditionalObject.h" // Ibid +#include "../plPhysical/plPickingDetector.h" // Ibid +#include "../pfConditional/plActivatorConditionalObject.h" // Ibid +#include "../pfConditional/plFacingConditionalObject.h" // Ibid +#include "../pfConditional/plObjectInBoxConditionalObject.h" // Ibid + +#include "../pnMessage/plObjRefMsg.h" //Message Dependencies +#include "../pnMessage/plNotifyMsg.h" // Ibid +#include "../pnMessage/plCursorChangeMsg.h" // Ibid + + +#include "../MaxConvert/plConvert.h" + + +// +// DummyCodeIncludeFuncInventStuff Function. +// Necessary to keep the compiler from throwing away this file. +// No functions within are inherently called otherwise.... +// +// + + +void DummyCodeIncludeFuncInventStuff() {} + + + + +CLASS_DESC(plInventoryObjComponent, gInventoryObjDesc, "(ex)InventoryObj", "(ex)InventoryObj", COMP_TYPE_LOGIC, INVENTORYOBJCOMP_CID) + + +enum +{ + kClickDragDirectional, + kClickDragDegrees, + kClickDragUseProxy, + kClickDragProxy, + kClickDragUseRegion, + kClickDragProxyRegion, + kClickDragUseX, + kClickDragUseY, + kClickDragAnimX, + kClickDragAnimY, + kClickDragUseLoopX, + kClickDragUseLoopY, + kClickDragLoopAnimX, + kClickDragLoopAnimY, + kAgeSpecificCheckBx, //Added in v1 + kRespawnAfterLostCheckBx, //Added in v1 + kConsumableCheckbx, //Added in v1 + kLifeSpan //Added in v1 +}; + + +class plInventoryObjComponentProc; +extern plInventoryObjComponentProc gInventoryObjComponentProc; + +ParamBlockDesc2 gInventoryObjBlock +( + plComponent::kBlkComp, _T("ClickDragComp"), 0, &gInventoryObjDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_INV_OBJECT, IDS_COMP_INV_OBJECTS, 0, 0, NULL, //&gInventoryObjComponentProc, + + kAgeSpecificCheckBx, _T("AgeSpecificObject"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_enable_ctrls, 1, kRespawnAfterLostCheckBx, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_INV_OBJECT_ITINERANTBOOL, + end, + + kRespawnAfterLostCheckBx, _T("RespawnAtSPObject"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_INV_OBJECT_RESPAWNBOOL, + end, + + kConsumableCheckbx , _T("TemporaryObject"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_enable_ctrls, 1, kLifeSpan, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_INV_OBJECT_CONSUMABLE, + end, + + kLifeSpan, _T("LifeSpan"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, 0.0, 100000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_INV_OBJECT_LIFE_EDIT, IDC_COMP_INV_OBJECT_LIFE_SPIN, 1.0f, + end, + + + + end +); + +plInventoryObjComponent::plInventoryObjComponent() +{ + fClassDesc = &gInventoryObjDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +const plInventoryObjComponent::LogicKeys& plInventoryObjComponent::GetLogicKeys() +{ + return fLogicModKeys; +} + +// Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plInventoryObjComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fLogicModKeys.clear(); + fReceivers.Reset(); + return true; +} + +plKey plInventoryObjComponent::GetLogicKey(plMaxNode* node) +{ + LogicKeys::const_iterator it; + + for (it = fLogicModKeys.begin(); it != fLogicModKeys.end(); it++) + { + if (node == it->first) + return(it->second); + } + return nil; +} + + +hsBool plInventoryObjComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plLocation loc = node->GetLocation(); + plSceneObject *obj = node->GetSceneObject(); + + // Create and register the ClickDrag's logic component + plLogicModifier *logic = TRACKED_NEW plLogicModifier; + char tmpName[256]; + sprintf(tmpName, "%s_%s_LogicModifier", obj->GetKeyName(), GetINode()->GetName()); + plKey logicKey = hsgResMgr::ResMgr()->NewKey(tmpName, logic, node->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(logicKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + fLogicModKeys[node] = logicKey; + + + +return true; +} + +void plInventoryObjComponent::AddReceiverKey(plKey key, plMaxNode* node) +{ + fReceivers.Append(key); +} + +hsBool plInventoryObjComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +#include "plNoteTrackDlgComp.h" + +class plInventoryObjComponentProc : public ParamMap2UserDlgProc +{ +protected: + plComponentNoteTrackDlg fNoteTrackDlgX; + plComponentNoteTrackDlg fNoteTrackDlgY; + IParamBlock2 *fPB; + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: +// fPB = map->GetParamBlock(); +/* +// fNoteTrackDlgX.Init(GetDlgItem(hWnd, IDC_COMP_CLICK_DRAG_ANIMX), +// nil, +/// kClickDragAnimX, +/ nil, + fPB, + fPB->GetOwner()); + + fNoteTrackDlgX.Load(); + + EnableWindow(GetDlgItem(hWnd, IDC_COMP_CLICK_DRAG_ANIMX), true); + + fNoteTrackDlgY.Init(GetDlgItem(hWnd, IDC_COMP_CLICK_DRAG_ANIM_Y), + nil, + kClickDragAnimY, + nil, + fPB, + fPB->GetOwner()); + + fNoteTrackDlgY.Load(); + + EnableWindow(GetDlgItem(hWnd, IDC_COMP_CLICK_DRAG_ANIM_Y), true); +*/ + return TRUE; +/* + case WM_COMMAND: + if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_COMP_CLICK_DRAG_ANIMX) + { + fNoteTrackDlgX.AnimChanged(); + return TRUE; + } + if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_COMP_CLICK_DRAG_ANIM_Y) + { + fNoteTrackDlgY.AnimChanged(); + return TRUE; + } + break; + */ + } + + return false; + } + + void DeleteThis() + { +// fNoteTrackDlgX.DeleteCache(); +// fNoteTrackDlgY.DeleteCache(); + } +}; +static plInventoryObjComponentProc gInventoryObjComponentProc; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plInventoryObjComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plInventoryObjComponent.h new file mode 100644 index 00000000..a926ddd9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plInventoryObjComponent.h @@ -0,0 +1,110 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plInventoryObjComponent_inc +#define plInventoryObjComponent_inc + + + +#include "plClickableComponent.h" +#include "hsTemplates.h" +#include +#include "../pnKeyedObject/plKey.h" + +class plMaxNode; + +#define INVENTORYOBJCOMP_CID Class_ID(0x425e1687, 0x4a126b91) + +//! Inventory Object Component Class + +/*! + This Class is the rudimentary 'Inventoriable' component. It is derived from the Clickable + component in order to access the mouse controllers to allow mouse state changes when the cursor + is over an object that can be taken. + + member functions: + + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *node, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void AddReceiverKey(plKey pKey); + virtual plKey GetLogicKey(plMaxNode* node); + const LogicKeys& GetLogicKeys(); + \sa SetupProperties(), PreConvert(), AddReceiverKey(), GetLogicKey() and GetLogicKeys() +*/ + + + + + + +class plInventoryObjComponent : public plClickableComponent +{ +public: + typedef std::map LogicKeys; +protected: + + hsTArray fReceivers; + LogicKeys fLogicModKeys; + +public: + + //! Constructor function for class + /*! + Herein the ClassDesc2 object that is used extensively by the ParamBlock2 + has gained accessibiltiy. Auto-Creation of the UI is done here as well. + + */ + plInventoryObjComponent(); + + // Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + + //! plInventoryObjComponent PreConvert, takes in two variables and return a hsBool. + /*! + Calls the function MaybeMakeLocal() and Sets Drawable to false. + + Takes in two variables, being: + \param node a plMaxNode ptr. + \param pErrMsg a pErrMsg ptr. + + \return A hsBool expressing the success of the operation. + \sa DeleteThis(), plPhysicalCoreComponent(), Convert(), GetParamVals(), MaybeMakeLocal() and FixUpPhysical() + */ + + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *node, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void AddReceiverKey(plKey key, plMaxNode* node=nil); + virtual plKey GetLogicKey(plMaxNode* node); + const LogicKeys& GetLogicKeys(); + +}; + +#endif // plInventoryObjComponent_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLODFadeComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLODFadeComponent.cpp new file mode 100644 index 00000000..30b490c6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLODFadeComponent.cpp @@ -0,0 +1,789 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxExport/plExportProgressBar.h" + +#include "hsTypes.h" + +#include "plLODFadeComponent.h" + +#include "../pfSurface/plFadeOpacityMod.h" +#include "../pfSurface/plDistOpacityMod.h" + +void DummyCodeIncludeFuncLODFade() +{ +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// LODFadeComponent first, similar and related BlendOnto component further on in file. +CLASS_DESC(plLODFadeComponent, gLODFadeCompDesc, "LOD Blend", "LODBlend", COMP_TYPE_GRAPHICS, LODFADE_COMP_CID) + +ParamBlockDesc2 gLODFadeBk +( + plComponent::kBlkComp, _T("LODFade"), 0, &gLODFadeCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_LODFADE, IDS_COMP_LODFADE, 0, 0, NULL, + + plLODFadeComponent::kHasBase, _T("HasBase"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_LODFADE_HASBASE, + end, + + plLODFadeComponent::kBase, _T("Base"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_LODFADE_BASE, + p_prompt, IDS_COMP_LODFADE_BASE, + end, + + plLODFadeComponent::kDistance, _T("Distance"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_LODFADE_DISTANCE, IDC_COMP_LODFADE_DISTANCE_SPIN, 1.0, + end, + + plLODFadeComponent::kTransition, _T("Transition"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_LODFADE_TRANSITION, IDC_COMP_LODFADE_TRANSITION_SPIN, 1.0, + end, + + plLODFadeComponent::kFadeBase, _T("FadeBase"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_LODFADE_FADEBASE, + p_enable_ctrls, 1, plLODFadeComponent::kBaseFirst, + end, + + plLODFadeComponent::kBaseFirst, _T("BaseFirst"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_LODFADE_BASEFIRST, + end, + + + end +); + +void plLODFadeComponent::ISetToFadeBase(plMaxNode* node, plMaxNode* base, plErrorMsg* pErrMsg) +{ + if( fCompPB->GetInt(kBaseFirst) ) + node->AddRenderDependency(base); + else + base->AddRenderDependency(node); + + Box3 fade = base->GetFade(); + Point3 maxs = fade.Max(); + float fadeInStart = fCompPB->GetFloat(kDistance) - fCompPB->GetFloat(kTransition); + if( fadeInStart < 0 ) + fadeInStart = 0; + float fadeInEnd = fCompPB->GetFloat(kDistance); + Point3 mins(fadeInStart, fadeInEnd, -1.f); + fade = Box3(mins, maxs); + base->SetFade(fade); + + node->SetNoDeferDraw(true); + base->SetNoDeferDraw(true); +} + +hsBool plLODFadeComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( fCompPB->GetInt(kHasBase) ) + { + plMaxNode* base = (plMaxNode*)fCompPB->GetINode(kBase, TimeValue(0)); + if( base ) + { + if( fCompPB->GetInt(kFadeBase) ) + { + ISetToFadeBase(node, base, pErrMsg); + } + else + { + node->AddRenderDependency(base); + node->SetNoDeferDraw(true); + } + } + } + Box3 fade = node->GetFade(); + Point3 mins = fade.Min(); + float fadeOutStart = fCompPB->GetFloat(kDistance); + float fadeOutEnd = fadeOutStart + fCompPB->GetFloat(kTransition); + Point3 maxs(fadeOutEnd, fadeOutStart, 1.f); + fade = Box3(mins, maxs); + node->SetFade(fade); + + return true; +} + +hsBool plLODFadeComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plLODFadeComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +plLODFadeComponent::plLODFadeComponent() +{ + fClassDesc = &gLODFadeCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// BlendOnto component next. +CLASS_DESC(plBlendOntoComponent, gBlendOntoCompDesc, "Blend Onto", "BlendOnto", COMP_TYPE_GRAPHICS, BLENDONTO_COMP_CID) + +ParamBlockDesc2 gBlendOntoBk +( + plComponent::kBlkComp, _T("BlendOnto"), 0, &gBlendOntoCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_BLENDONTO, IDS_COMP_BLENDONTO, 0, 0, NULL, + + plBlendOntoComponent::kBaseNodes, _T("BaseNodes"), TYPE_INODE_TAB, 0, P_CAN_CONVERT, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, IDC_ADD_TARGS, 0, IDC_DEL_TARGS, + p_classID, triObjectClassID, + end, + + plBlendOntoComponent::kSortFaces, _T("SortFaces"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_BLENDONTO_SORTFACES, + end, + + + end +); + +hsBool plBlendOntoComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + hsBool someBase = false; + int numBase = fCompPB->Count(kBaseNodes); + int i; + for( i = 0; i < numBase; i++ ) + { + plMaxNode* base = (plMaxNode*)fCompPB->GetINode(kBaseNodes, TimeValue(0), i); + + if( base ) + { + node->AddRenderDependency(base); + node->SetNoDeferDraw(true); + if( !fCompPB->GetInt(kSortFaces) ) + node->SetNoFaceSort(true); + + someBase = true; + } + } + if( !someBase ) + { + node->SetBlendToFB(true); + } + + return true; +} + +hsBool plBlendOntoComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plBlendOntoComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +plBlendOntoComponent::plBlendOntoComponent() +{ + fClassDesc = &gBlendOntoCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// BlendOntoAdv component next. +CLASS_DESC(plBlendOntoAdvComponent, gBlendOntoAdvCompDesc, "Blend Onto Advanced", "BlendOntoAdv", COMP_TYPE_GRAPHICS, BLENDONTOADV_COMP_CID) + +ParamBlockDesc2 gBlendOntoAdvBk +( + plComponent::kBlkComp, _T("BlendOntoAdv"), 0, &gBlendOntoAdvCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_BLENDONTOADV, IDS_COMP_BLENDONTOADV, 0, 0, NULL, + + plBlendOntoAdvComponent::kBaseNodes, _T("BaseNodes"), TYPE_INODE_TAB, 0, P_CAN_CONVERT, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, IDC_ADD_TARGS, 0, IDC_DEL_TARGS, + p_classID, triObjectClassID, + end, + + plBlendOntoAdvComponent::kSortFaces, _T("SortFaces"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_BLENDONTOADV_SORTFACES, + end, + + plBlendOntoAdvComponent::kSortObjects, _T("SortObjects"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_BLENDONTOADV_SORTOBJECTS, + end, + + plBlendOntoAdvComponent::kOntoBlending, _T("OntoBlending"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_BLENDONTOADV_ONTOBLENDING, + end, + + + end +); + +hsBool plBlendOntoAdvComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + hsBool someBase = false; + int numBase = fCompPB->Count(kBaseNodes); + int i; + for( i = 0; i < numBase; i++ ) + { + plMaxNode* base = (plMaxNode*)fCompPB->GetINode(kBaseNodes, TimeValue(0), i); + + if( base ) + { + node->AddRenderDependency(base); + if( !fCompPB->GetInt(kOntoBlending) ) + node->SetNoDeferDraw(true); + if( !fCompPB->GetInt(kSortFaces) ) + node->SetNoFaceSort(true); + + someBase = true; + } + } + if( !someBase ) + { + node->SetBlendToFB(true); + } + + return true; +} + +hsBool plBlendOntoAdvComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plBlendOntoAdvComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +plBlendOntoAdvComponent::plBlendOntoAdvComponent() +{ + fClassDesc = &gBlendOntoAdvCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// Force drawing before the avatar. + +const Class_ID B4AV_COMP_CID(0x14536d5b, 0x17dc623b); + +class plB4AvComponent : public plComponent +{ +public: + plB4AvComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plB4AvComponent, gB4AvCompDesc, "Draw B4 Avatar", "B4Av", COMP_TYPE_GRAPHICS, B4AV_COMP_CID) + +ParamBlockDesc2 gB4AvBk +( + plComponent::kBlkComp, _T("B4Av"), 0, &gB4AvCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SORT_AS_OPAQUE, IDS_COMP_SORT_AS_OPAQUE, 0, 0, NULL, + + end +); + +hsBool plB4AvComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetSortAsOpaque(true); + return true; +} + +hsBool plB4AvComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plB4AvComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +plB4AvComponent::plB4AvComponent() +{ + fClassDesc = &gB4AvCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// DistFadeComponent - just feeding them a bit more rope. +CLASS_DESC(plDistFadeComponent, gDistFadeCompDesc, "Distance Fade", "DistFade", COMP_TYPE_GRAPHICS, DISTFADE_COMP_CID) + +ParamBlockDesc2 gDistFadeBk +( + plComponent::kBlkComp, _T("DistFade"), 0, &gDistFadeCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_DISTFADE, IDS_COMP_DISTFADE, 0, 0, NULL, + + plDistFadeComponent::kFadeInActive, _T("FadeInActive"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_DISTFADE_IN_ACTIVE, + p_enable_ctrls, 2, plDistFadeComponent::kFadeInStart, plDistFadeComponent::kFadeInEnd, + end, + + plDistFadeComponent::kFadeInStart, _T("FadeInStart"), TYPE_FLOAT, 0, 0, + p_default, 5.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTFADE_INSTART, IDC_COMP_DISTFADE_INSTART_SPIN, 1.0, + end, + + plDistFadeComponent::kFadeInEnd, _T("FadeInEnd"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTFADE_INEND, IDC_COMP_DISTFADE_INEND_SPIN, 1.0, + end, + + plDistFadeComponent::kFadeOutActive, _T("FadeOutActive"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_DISTFADE_OUT_ACTIVE, + p_enable_ctrls, 2, plDistFadeComponent::kFadeOutStart, plDistFadeComponent::kFadeOutEnd, + end, + + plDistFadeComponent::kFadeOutStart, _T("FadeOutStart"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTFADE_OUTSTART, IDC_COMP_DISTFADE_OUTSTART_SPIN, 1.0, + end, + + plDistFadeComponent::kFadeOutEnd, _T("FadeOutEnd"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_DISTFADE_OUTEND, IDC_COMP_DISTFADE_OUTEND_SPIN, 1.0, + end, + + + end +); + +hsBool plDistFadeComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + // If we're turned off, just return. + if( !fCompPB->GetInt(kFadeInActive) && !fCompPB->GetInt(kFadeOutActive) ) + return true; + + Box3 fade; + Point3 mins(0.f, 0.f, 0.f); + Point3 maxs(0.f, 0.f, 0.f); + + + if( fCompPB->GetInt(kFadeInActive) ) + { + mins[0] = fCompPB->GetFloat(kFadeInStart); + mins[1] = fCompPB->GetFloat(kFadeInEnd); + } + if( fCompPB->GetInt(kFadeOutActive) ) + { + maxs[0] = fCompPB->GetFloat(kFadeOutStart); + maxs[1] = fCompPB->GetFloat(kFadeOutEnd); + } + + // We're not really sure how the artist has artistically interpreted + // the parameters. What we want is: + // Nearest point where object starts to fade in == mins[0] + // Nearest point where object is opaque == mins[1] + // Farthest point where object is opaque == maxs[1] + // Farthest point where object fades out completely = maxs[0] + // + // If the artist says they want it to start off opaque, fade out, then + // fade back in once it's far away, we'll explain in person why that's stupid, + // and in the meantime prevent them from doing that by assuming they just + // have the parameter order flipped. + // + // So, they've either given us 2 distances or 2 pairs of distances. + // If they've just given 2 distances, we use as is, + // but if they've given 2 pairs, we have to arrange them to fit the + // above model. + // + // So first thing to do is figure out how many (valid) distances we have. + if( (mins[0] == 0) && (mins[1] == 0) && (maxs[0] == 0) && (maxs[1] == 0) ) + { + // Okay, they gave no valid distances. Just pretend we were never here. + return true; + } + if( (mins[0] == 0) && (mins[1] == 0) ) + { + // maxs must be valid, just go with them. + fade = IFadeFromPoint(maxs); + } + else if( (maxs[0] == 0) && (maxs[1] == 0) ) + { + // mins must be valid, just go with them. + fade = IFadeFromPoint(mins); + } + else + { + // They're both "valid". Give it a shot. + fade = IFadeFromPair(mins, maxs); + } + + node->SetFade(fade); + + return true; +} + +void plDistFadeComponent::ISwap(float& p0, float& p1) +{ + float t = p0; + p0 = p1; + p1 = t; +} + +Box3 plDistFadeComponent::IFadeFromPoint(Point3& mins) +{ + Point3 maxs(0.f, 0.f, 0.f); + if( mins[0] < mins[1] ) + mins[2] = -1.f; + else if( mins[0] > mins[1] ) + mins[2] = 1.f; + else + mins[2] = 0; + + return Box3(mins, maxs); +} + +Box3 plDistFadeComponent::IFadeFromPair(Point3& mins, Point3& maxs) +{ + if( mins[0] > maxs[0] ) + { + ISwap(mins[0], maxs[0]); + ISwap(mins[1], maxs[1]); + } + if( mins[0] > mins[1] ) + { + ISwap(mins[0], mins[1]); + } + if( maxs[0] < maxs[1] ) + { + // Poor confused bastard, take a guess what he wants. + ISwap(maxs[0], maxs[1]); + } + if( mins[0] < mins[1] ) + mins[2] = -1.f; + else + mins[2] = 0; + if( maxs[0] > maxs[1] ) + maxs[2] = 1.f; + else + maxs[2] = 0; + return Box3(mins, maxs); +} + + +hsBool plDistFadeComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plDistFadeComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +plDistFadeComponent::plDistFadeComponent() +{ + fClassDesc = &gDistFadeCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// LOS Fade component next. + +class plLOSFadeComponent : public plComponent +{ +public: + enum + { + kBoundsCenter, + kFadeInTime, + kFadeOutTime + }; + +public: + plLOSFadeComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plLOSFadeComponent, gLOSFadeCompDesc, "LOS Fade", "LOSFade", COMP_TYPE_GRAPHICS, LOSFADE_COMP_CID) + +ParamBlockDesc2 gLOSFadeBk +( + plComponent::kBlkComp, _T("LOSFade"), 0, &gLOSFadeCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_LOSFADE, IDS_COMP_LOSFADE, 0, 0, NULL, + + plLOSFadeComponent::kBoundsCenter, _T("BoundsCenter"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_BOUNDSCENTER, + end, + + plLOSFadeComponent::kFadeInTime, _T("kFadeInTime"), TYPE_FLOAT, 0, 0, + p_default, 0.5, + p_range, 0.0, 5.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FADEINTIME, IDC_COMP_FADEINTIME_SPIN, 1.0, + end, + + + plLOSFadeComponent::kFadeOutTime, _T("kFadeOutTime"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 5.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_FADEOUTTIME, IDC_COMP_FADEOUTTIME_SPIN, 1.0, + end, + + + end +); + +hsBool plLOSFadeComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetForceMatShade(true); + node->SetForceLocal(true); + node->SetForceMaterialCopy(true); + return true; +} + +hsBool plLOSFadeComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plLOSFadeComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plFadeOpacityMod* fade = TRACKED_NEW plFadeOpacityMod; + + if( fCompPB->GetInt(kBoundsCenter) ) + fade->SetFlag(plFadeOpacityMod::kBoundsCenter); + else + fade->ClearFlag(plFadeOpacityMod::kBoundsCenter); + + fade->SetFadeUp(fCompPB->GetFloat(kFadeInTime)); + fade->SetFadeDown(fCompPB->GetFloat(kFadeOutTime)); + + node->AddModifier(fade, node->GetKey()->GetName()); + + return true; +} + +plLOSFadeComponent::plLOSFadeComponent() +{ + fClassDesc = &gLOSFadeCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// GZ Marker Fade component next. + +const Class_ID GZFADE_COMP_CID(0x27173270, 0x4f4486f); + +class plGZFadeComponent : public plComponent +{ +public: + enum + { + kOpaque, + kTransp + }; + +public: + plGZFadeComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plGZFadeComponent, gGZFadeCompDesc, "GZ Fade", "GZFade", COMP_TYPE_GRAPHICS, GZFADE_COMP_CID) + +ParamBlockDesc2 gGZFadeBk +( + plComponent::kBlkComp, _T("GZFade"), 0, &gGZFadeCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_GZFADE, IDS_COMP_GZ_FADE, 0, 0, NULL, + + plGZFadeComponent::kOpaque, _T("kOpaque"), TYPE_FLOAT, 0, 0, + p_default, 15.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_GZ_OPAQUE, IDC_COMP_GZ_OPAQUE_SPIN, 1.0, + end, + + plGZFadeComponent::kTransp, _T("kTransp"), TYPE_FLOAT, 0, 0, + p_default, 20.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_GZ_TRANSP, IDC_COMP_GZ_TRANSP_SPIN, 1.0, + end, + + + end +); + +hsBool plGZFadeComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + float opaq = fCompPB->GetFloat(kOpaque); + float transp = fCompPB->GetFloat(kTransp); + + pErrMsg->Set(transp <= opaq, node->GetName(), "Distance obj goes transparent must be greater than distance it's opaque").CheckAndAsk(); + pErrMsg->Set(false); + + node->SetForceMatShade(true); + node->SetForceLocal(true); + node->SetForceMaterialCopy(true); + return true; +} + +hsBool plGZFadeComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plGZFadeComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plDistOpacityMod* fade = TRACKED_NEW plDistOpacityMod; + + float opaq = fCompPB->GetFloat(kOpaque); + float transp = fCompPB->GetFloat(kTransp); + + fade->SetFarDist(opaq, transp); + + node->AddModifier(fade, node->GetKey()->GetName()); + + return true; +} + +plGZFadeComponent::plGZFadeComponent() +{ + fClassDesc = &gGZFadeCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// Force dynamic material - keeps the material colors from getting burnt into the verts. + +const Class_ID DYNMAT_COMP_CID(0x2ea4671f, 0x163b12ac); + +class plDynMatComponent : public plComponent +{ +public: + enum + { + kOpaque, + kTransp + }; + +public: + plDynMatComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plDynMatComponent, gDynMatCompDesc, "Force Dyn Mat", "DynMat", COMP_TYPE_GRAPHICS, DYNMAT_COMP_CID) + +ParamBlockDesc2 gDynMatBk +( + plComponent::kBlkComp, _T("DynMat"), 0, &gDynMatCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_DYNMAT, IDS_COMP_DYNMAT, 0, 0, NULL, + + end +); + +hsBool plDynMatComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetForceMatShade(true); + return true; +} + +hsBool plDynMatComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plDynMatComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +plDynMatComponent::plDynMatComponent() +{ + fClassDesc = &gDynMatCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLODFadeComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLODFadeComponent.h new file mode 100644 index 00000000..f6770e3b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLODFadeComponent.h @@ -0,0 +1,135 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLODFadeComponent_inc +#define plLODFadeComponent_inc + +const Class_ID LODFADE_COMP_CID(0x31821ca, 0x49432f20); +const Class_ID BLENDONTO_COMP_CID(0x35d66d72, 0x1e141eff); +const Class_ID BLENDONTOADV_COMP_CID(0x84c41c0, 0x2fc62900); +const Class_ID DISTFADE_COMP_CID(0x348865f7, 0x72f81040); +const Class_ID LOSFADE_COMP_CID(0x6308608a, 0x7fa34929); + +class plMaxNode; +class plExportProgressBar; + +class plLODFadeComponent : public plComponent +{ +public: + enum + { + kHasBase, + kBase, + kDistance, + kTransition, + kFadeBase, + kBaseFirst + }; +protected: + void ISetToFadeBase(plMaxNode* node, plMaxNode* base, plErrorMsg* pErrMsg); + +public: + plLODFadeComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +class plBlendOntoComponent : public plComponent +{ +public: + enum + { + kBaseNodes, + kSortFaces + }; + +public: + plBlendOntoComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +class plBlendOntoAdvComponent : public plComponent +{ +public: + enum + { + kBaseNodes, + kSortFaces, + kSortObjects, + kOntoBlending + }; + +public: + plBlendOntoAdvComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +class plDistFadeComponent : public plComponent +{ +public: + enum + { + kFadeInActive, + kFadeInStart, + kFadeInEnd, + kFadeOutActive, + kFadeOutStart, + kFadeOutEnd + }; +protected: + void ISwap(float& p0, float& p1); + Box3 IFadeFromPoint(Point3& mins); + Box3 IFadeFromPair(Point3& mins, Point3& maxs); + +public: + plDistFadeComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +#endif // plLODFadeComponent_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightGrpComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightGrpComponent.cpp new file mode 100644 index 00000000..a1ebe3d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightGrpComponent.cpp @@ -0,0 +1,298 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "../MaxMain/plMaxNode.h" +#include "hsResMgr.h" + + +// LightGroup component +#include "../pnSceneObject/plSceneObject.h" +#include "../plGLight/plLightInfo.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../pnSceneObject/plDrawInterface.h" +#include "../MaxPlasmaLights/plRealTimeLightBase.h" + +#include "plLightGrpComponent.h" + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// LightGroup Component +// +// + +enum +{ + kIncludeChars, + kAffectedLightSel, + kTest +}; + + + + +CLASS_DESC(plLightGrpComponent, gLightGrpDesc, "Light Group", "LightGroup", COMP_TYPE_GRAPHICS, LIGHTGRP_COMP_CID) + + + +ParamBlockDesc2 gLightGrpBk +( + plComponent::kBlkComp, _T("LightGroup"), 0, &gLightGrpDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_LIGHTINC, IDS_COMP_LIGHTINCS, 0, 0, nil, + + kIncludeChars, _T("Include characters"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_LIGHTINC_CHARS, + end, + + kAffectedLightSel, _T("AffectedLightChoice"), TYPE_INODE, 0, 0, + end, + + kTest, _T("TestBox"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_LIGHTINC_FILTER, + end, + + end + +); + +plLightGrpComponent::plLightGrpComponent() +: fValid(false) +{ + fClassDesc = &gLightGrpDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +class plLightGrpPostLoadCallback : public PostLoadCallback +{ +public: + plLightGrpComponent* fLightGrp; + + plLightGrpPostLoadCallback(plLightGrpComponent* lg) : fLightGrp(lg) {} + + void proc(ILoad *iload) + { + IParamBlock2* compPB = fLightGrp->GetParamBlock(plComponentBase::kBlkComp); + INode* light = compPB->GetINode(kAffectedLightSel); + if( light ) + { + fLightGrp->AddTarget((plMaxNodeBase*)light); + compPB->SetValue(kAffectedLightSel, TimeValue(0), (INode*)nil); + } + delete this; + } +}; + +IOResult plLightGrpComponent::Load(ILoad* iLoad) +{ + iLoad->RegisterPostLoadCallback(new plLightGrpPostLoadCallback(this)); + + return plComponent::Load(iLoad); +} + +hsBool plLightGrpComponent::IAddLightsToSpans(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + int i; + for( i = 0; i < fLightInfos.GetCount(); i++ ) + { + if( !fLightInfos[i] ) + continue; + + const plDrawInterface* di = pNode->GetSceneObject()->GetDrawInterface(); + + int iDraw; + for( iDraw = 0; iDraw < di->GetNumDrawables(); iDraw++ ) + { + plDrawableSpans* drawable = plDrawableSpans::ConvertNoRef(di->GetDrawable(iDraw)); + if( drawable ) + { + UInt32 diIndex = di->GetDrawableMeshIndex(iDraw); + + ISendItOff(fLightInfos[i], drawable, diIndex); + } + } + } + return true; +} + +hsBool plLightGrpComponent::ISendItOff(plLightInfo* liInfo, plDrawableSpans* drawable, UInt32 diIndex) +{ + plDISpanIndex spans = drawable->GetDISpans(diIndex); + + if( spans.fFlags & plDISpanIndex::kMatrixOnly ) + return false; + + if( !fCompPB->GetInt(kTest) ) + { + UInt8 liMsgType = liInfo->GetProjection() ? plDrawable::kMsgPermaProjDI : plDrawable::kMsgPermaLightDI; + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(drawable->GetKey(), plRefMsg::kOnCreate, diIndex, liMsgType); + hsgResMgr::ResMgr()->AddViaNotify(liInfo->GetKey(), refMsg, plRefFlags::kPassiveRef); + } + else + { + + hsBitVector litSpans; + liInfo->GetAffectedForced(drawable->GetSpaceTree(), litSpans, false); + + UInt8 liMsgType = liInfo->GetProjection() ? plDrawable::kMsgPermaProj : plDrawable::kMsgPermaLight; + int i; + for( i = 0; i < spans.GetCount(); i++ ) + { + if( litSpans.IsBitSet(spans[i]) ) + { + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(drawable->GetKey(), plRefMsg::kOnCreate, spans[i], liMsgType); + hsgResMgr::ResMgr()->AddViaNotify(liInfo->GetKey(), refMsg, plRefFlags::kPassiveRef); + } + } + } + + return true; +} + +hsBool plLightGrpComponent::IGetLightInfos() +{ + if( !fLightInfos.GetCount() ) + { + // Already checked that lightnodes are cool. just get the light interfaces. + int i; + for( i = 0; i < fLightNodes.GetCount(); i++ ) + { + plMaxNode* lightNode = fLightNodes[i]; + plSceneObject* lightSO = lightNode->GetSceneObject(); + if( !lightSO ) + continue; + + plLightInfo* liInfo = plLightInfo::ConvertNoRef(lightSO->GetGenericInterface(plLightInfo::Index())); + if( !liInfo ) + continue; + + liInfo->SetProperty(plLightInfo::kLPHasIncludes, true); + if( fCompPB->GetInt(kIncludeChars) ) + liInfo->SetProperty(plLightInfo::kLPIncludesChars, true); + fLightInfos.Append(liInfo); + } + } + return fValid = (fLightInfos.GetCount() > 0); +} + +const hsTArray& plLightGrpComponent::GetLightInfos() +{ + IGetLightInfos(); + return fLightInfos; +} + +plLightGrpComponent* plLightGrpComponent::GetComp(plMaxNode* node) +{ + int i; + for( i = 0; i < node->NumAttachedComponents(); i++ ) + { + plComponentBase* comp = node->GetAttachedComponent(i); + if( comp && comp->ClassID() == LIGHTGRP_COMP_CID ) + return (plLightGrpComponent*)comp; + } + return nil; +} + +hsBool plLightGrpComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + const char* dbgNodeName = node->GetName(); + if( !fValid ) + return true; + + if( !IGetLightInfos() ) + return true; + + if( !node->GetDrawable() ) + return true; + + if( !node->GetSceneObject() || !node->GetSceneObject()->GetDrawInterface() ) + return true; + + // If it's shaded as a character, ignore any light groups attached. + if( node->GetItinerant() ) + return true; + + IAddLightsToSpans(node, pErrMsg); + + return true; +} + +hsBool plLightGrpComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + fValid = false; + fLightInfos.Reset(); + fLightNodes.Reset(); + + int i; + for( i = 0; i < NumTargets(); i++ ) + { + plMaxNodeBase* liNode = GetTarget(i); + + if( liNode && liNode->CanConvert() ) + { + Object *obj = liNode->GetObjectRef(); + if( obj ) + { + Class_ID cid = obj->ClassID(); + + if( (cid == RTSPOT_LIGHT_CLASSID) + || (cid == RTOMNI_LIGHT_CLASSID) + || (cid == RTDIR_LIGHT_CLASSID) + || (cid == RTPDIR_LIGHT_CLASSID) ) + { + fLightNodes.Append((plMaxNode*)liNode); + } + } + } + + } + if( !fLightNodes.GetCount() ) + return true; + + fValid = true; + return true; +} + +hsBool plLightGrpComponent::PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + fValid = false; + + fValid = true; + + + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightGrpComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightGrpComponent.h new file mode 100644 index 00000000..2e7268ff --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightGrpComponent.h @@ -0,0 +1,66 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLightGrpComponent_inc +#define plLightGrpComponent_inc + +const Class_ID LIGHTGRP_COMP_CID(0x42d57a80, 0xd3a3745); + +class plMaxNode; +class plLightInfo; +class plErrorMsg; +class plDrawableSpans; + +//Class that accesses the paramblock below. +class plLightGrpComponent : public plComponent +{ +private: + hsBool fValid; + + hsTArray fLightNodes; + hsTArray fLightInfos; + + hsBool IAddLightsToSpans(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool ISendItOff(plLightInfo* liInfo, plDrawableSpans* drawable, UInt32 diIndex); + hsBool IGetLightInfos(); + +public: + plLightGrpComponent(); + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + const hsTArray& GetLightInfos(); + + IOResult Load(ILoad* iload); + + static plLightGrpComponent* GetComp(plMaxNode* node); +}; + + + +#endif // plLightGrpComponent_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightMapComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightMapComponent.cpp new file mode 100644 index 00000000..e886c66a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightMapComponent.cpp @@ -0,0 +1,189 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "../MaxMain/plMaxNode.h" +#include "hsResMgr.h" + +#include "../plDrawable/plGeometrySpan.h" + +#include "plLightMapComponent.h" + +void DummyCodeIncludeFuncLightMap() +{ +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// +// LightMap Component +// +// +// +// + + +//Max desc stuff necessary below. +CLASS_DESC(plLightMapComponent, gLightMapDesc, "Light Map", "LightMap", COMP_TYPE_GRAPHICS, LIGHTMAP_COMP_CID) + +enum +{ + kMapChannel, //Inserted in v1 + kResolutionLevelRadio, //Inserted in v1, removed in v2 + kResSpinControl, //Inserted in v2 + kMapInitColor, + kCompress, + kShared +}; + +ParamBlockDesc2 gLightMapBk +( + plComponent::kBlkComp, _T("lightMap"), 0, &gLightMapDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_LIGHTMAP, IDS_COMP_LIGHTMAPS, 0, 0, NULL, + + kMapChannel, _T("UVW Channel Light Map"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_COMP_LIGHTMAP_EDIT1, IDC_COMP_LIGHTMAP_SPIN1, 0.4, + p_default, 1, + p_range, 1, plGeometrySpan::kMaxNumUVChannels, + end, +// +// kResolutionLevelRadio, _T("Resolution Level"), TYPE_INT, 0, 0, +// p_ui, TYPE_RADIO, 5, IDC_RADIO_LM1, IDC_RADIO_LM2, IDC_RADIO_LM3, IDC_RADIO_LM4, IDC_RADIO_LM5, +// p_vals, 0, 1, 2, 3, 4, +// p_default, 2, +// +// + kResSpinControl, _T("Resolution Spinner"), TYPE_INT, 0, 0, + p_ui, TYPE_SLIDER, EDITTYPE_INT, IDC_COMP_LM_DUMMY, IDC_COMP_LIGHT_SLIDER, 4, + p_range, 0, 4, + p_default, 2, + end, + + kMapInitColor, _T("Initial map color"), TYPE_RGBA, 0, 0, + p_ui, TYPE_COLORSWATCH, IDC_COMP_LIGHTMAP_COLOR, + p_default, Color(0,0,0), + end, + + kCompress, _T("Compress"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_LIGHTMAP_COMPRESS, + end, + + kShared, _T("Shared"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_LIGHTMAP_SHARED, + end, + + end +); + + + + +plLightMapComponent::plLightMapComponent() +: fLightMapKey(nil) +{ + fClassDesc = &gLightMapDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + + + +hsBool plLightMapComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plLightMapComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fLightMapKey = nil; + return true; +} + + +hsBool plLightMapComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} + + +float plLightMapComponent::GetScale() const +{ + int resBut = fCompPB->GetInt(kResSpinControl); + float res = 1.f; + switch( resBut ) + { + case 4: + res = 9.f; + break; + case 3: + res = 3.f; + break; + default: + case 2: + res = 1.f; + break; + case 1: + res = 0.5f; + break; + case 0: + res = 0.25f; + break; + } + + return res; +} + +UInt32 plLightMapComponent::GetUVWSrc() const +{ + return fCompPB->GetInt(kMapChannel)-1; +} + +hsBool plLightMapComponent::GetCompress() const +{ + return fCompPB->GetInt(kCompress); +} + +hsBool plLightMapComponent::GetShared() const +{ + return fCompPB->GetInt(kShared); +} + +hsColorRGBA plLightMapComponent::GetInitColor() const +{ + Color color = fCompPB->GetColor(kMapInitColor); + hsColorRGBA col; + col.Set(color.r, color.g, color.b, 1.f); + return col; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightMapComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightMapComponent.h new file mode 100644 index 00000000..69ce6e7f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLightMapComponent.h @@ -0,0 +1,63 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLightMapComponent_inc +#define plLightMapComponent_inc + +#include "plComponent.h" + +#include "hsColorRGBA.h" + +class plMipmap; + +const Class_ID LIGHTMAP_COMP_CID(0x1b1d0317, 0x3b3821db); + +class plLightMapComponent : public plComponent +{ +protected: + plKey fLightMapKey; + +public: + plLightMapComponent(); + + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + void SetLightMapKey(const plKey& key) { fLightMapKey = key; } + plKey GetLightMapKey() const { return fLightMapKey; } + + float GetScale() const; + UInt32 GetUVWSrc() const; + + hsBool GetCompress() const; + hsBool GetShared() const; + + hsColorRGBA GetInitColor() const; +}; + +#endif // plLightMapComponent_inc + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLineFollowComp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLineFollowComp.cpp new file mode 100644 index 00000000..64724514 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plLineFollowComp.cpp @@ -0,0 +1,842 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../plInterp/plController.h" +#include "../MaxMain/plMaxNode.h" +#include "../pnKeyedObject/plKey.h" + +#include "plgDispatch.h" +#include "../MaxMain/plPluginResManager.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +// Swivel related +#include "../pfAnimation/plViewFaceModifier.h" // ViewFace Comp + +// Line Follow related +#include "../plInterp/plAnimPath.h" +#include "../pfAnimation/plLineFollowMod.h" +#include "../pfAnimation/plFollowMod.h" + +#include "../pnMessage/plRefMsg.h" + +// Stereizer +#include "../pfAnimation/plStereizer.h" + +const Class_ID STEREIZE_COMP_CID(0x15066ec7, 0x64ea7381); +const Class_ID LINEFOLLOW_COMP_CID(0x64ec57f6, 0x292d47f6); +const Class_ID SWIVEL_COMP_CID(0x106a466b, 0x1c1700f7); + +void DummyCodeIncludeFuncLineFollow() +{ +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// LineFollow Component +// +// + +enum +{ + kFollowModeRadio, + kPathObjectSel, + kFollowObjectSel, + kOffsetActive, + kOffsetDegrees, + kOffsetClampActive, + kOffsetClamp, + kForceToLine, + kSpeedClampActive, + kSpeedClamp +}; + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plLineObjAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if( (id == kFollowObjectSel) || (id == kPathObjectSel) ) + { + plComponentBase* comp = (plComponentBase*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +plLineObjAccessor gLineObjAccessor; + + +class plLineFollowComponentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2* map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2* pb = map->GetParamBlock(); + map->SetTooltip(kPathObjectSel, TRUE, "Press the button, & select the path source object in one of the Viewports" ); + map->SetTooltip(kFollowObjectSel, TRUE, "Press the button, & select the object to follow in one of the Viewports" ); + map->SetTooltip(kOffsetDegrees, TRUE, "Positive angle to right, negative to left." ); + if( pb->GetInt(kFollowModeRadio) == plLineFollowMod::kFollowObject ) + map->Enable(kFollowObjectSel, TRUE); + else + map->Enable(kFollowObjectSel, FALSE); + } + return true; + +////////////////// + case WM_COMMAND: + { + if( (LOWORD(wParam) == IDC_RADIO_LISTENER) + || (LOWORD(wParam) == IDC_RADIO_CAMERA) + || (LOWORD(wParam) == IDC_RADIO_OBJECT) ) + { + IParamBlock2* pb = map->GetParamBlock(); + if( pb->GetInt(kFollowModeRadio) == plLineFollowMod::kFollowObject ) + map->Enable(kFollowObjectSel, TRUE); + else + map->Enable(kFollowObjectSel, FALSE); + + return true; + } + } + + } + + return false; + } + void DeleteThis() {} +}; +static plLineFollowComponentProc gLineFollowProc; + + +//Class that accesses the paramblock below. +class plLineFollowComponent : public plComponent +{ +private: + hsBool fValid; + + plLineFollowMod* fLineMod; + + hsBool IMakeLineMod(plMaxNode* pNode, plErrorMsg* pErrMsg); + +public: + plLineFollowComponent(); + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); + + plLineFollowMod* GetLineMod(plErrorMsg* pErrMsg); +}; + + + +CLASS_DESC(plLineFollowComponent, gLineFollowDesc, "LineFollow", "LineFollow", COMP_TYPE_GRAPHICS, LINEFOLLOW_COMP_CID) + + + +ParamBlockDesc2 gLineFollowBk +( + plComponent::kBlkComp, _T("LineFollow"), 0, &gLineFollowDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_LINEFOLLOW, IDS_COMP_LINEFOLLOWS, 0, 0, &gLineFollowProc, + + kFollowModeRadio, _T("FollowMode"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_RADIO_LISTENER, IDC_RADIO_CAMERA, IDC_RADIO_OBJECT, + p_vals, plLineFollowMod::kFollowListener, plLineFollowMod::kFollowCamera, plLineFollowMod::kFollowObject, + p_default, plLineFollowMod::kFollowListener, + end, + + kPathObjectSel, _T("PathObjectChoice"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_LINE_CHOOSE_PATH, + p_prompt, IDS_COMP_LINE_CHOSE_PATH, + p_accessor, &gLineObjAccessor, + end, + + kFollowObjectSel, _T("FollowObjectChoice"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_LINE_CHOOSE_OBJECT, + p_prompt, IDS_COMP_LINE_CHOSE_OBJECT, + p_accessor, &gLineObjAccessor, + end, + + kOffsetActive, _T("OffsetActive"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_LINE_OFFSETACTIVE, + p_enable_ctrls, 4, kOffsetDegrees, kOffsetClampActive, kOffsetClamp, kForceToLine, + end, + + kOffsetDegrees, _T("OffsetDegrees"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -85.0, 85.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_LINE_OFFSETDEGREES, IDC_COMP_LINE_OFFSETDEGREES_SPIN, 1.0, + end, + + kOffsetClampActive, _T("OffsetClampActive"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_LINE_OFFSETCLAMPACTIVE, + p_enable_ctrls, 1, kOffsetClamp, + end, + + kOffsetClamp, _T("OffsetClamp"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_LINE_OFFSETCLAMP, IDC_COMP_LINE_OFFSETCLAMP_SPIN, 1.0, + end, + + kForceToLine, _T("ForceToLine"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_LINE_FORCETOLINE, + end, + + kSpeedClampActive, _T("SpeedClampActive"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_LINE_SPEEDCLAMPACTIVE, + p_enable_ctrls, 1, kSpeedClamp, + end, + + kSpeedClamp, _T("SpeedClamp"), TYPE_FLOAT, 0, 0, + p_default, 30.0, + p_range, 3.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_LINE_SPEEDCLAMP, IDC_COMP_LINE_SPEEDCLAMP_SPIN, 1.0, + end, + + end + +); + +plLineFollowComponent::plLineFollowComponent() +: fValid(false), + fLineMod(nil) +{ + fClassDesc = &gLineFollowDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plLineFollowComponent::IMakeLineMod(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + plLineFollowMod::FollowMode mode = plLineFollowMod::FollowMode(fCompPB->GetInt(kFollowModeRadio)); + + plLineFollowMod* lineMod = TRACKED_NEW plLineFollowMod; + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), lineMod, pNode->GetLocation()); + + if( plLineFollowMod::kFollowObject == mode ) + { + if(fCompPB->GetINode(kFollowObjectSel) != NULL) + + + { + plMaxNode* targNode = (plMaxNode*)fCompPB->GetINode(kFollowObjectSel); + //plMaxNodeData* pMD = targNode->GetMaxNodeData(); + if( targNode->CanConvert() ) + { + plSceneObject* targObj = targNode->GetSceneObject(); + if( targObj ) + { + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(lineMod->GetKey(), plRefMsg::kOnCreate, 0, plLineFollowMod::kRefObject); + hsgResMgr::ResMgr()->AddViaNotify(targObj->GetKey(), refMsg, plRefFlags::kPassiveRef); + + lineMod->SetFollowMode(plLineFollowMod::kFollowObject); + } + } + } + } + else + { + lineMod->SetFollowMode(mode); + } + + plMaxNode* pathNode = (plMaxNode*)fCompPB->GetINode(kPathObjectSel); + if(!pathNode) + { + pErrMsg->Set(true, "Path Node Failure", "Path Node %s was set to be Ignored or empty. Path Component ignored.", ((INode*)pathNode)->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + //hsAssert(pathNode, "If valid is true, this must be set"); + Control* maxTm = pathNode->GetTMController(); + + plCompoundController* tmc = hsControlConverter::Instance().MakeTransformController(maxTm, pathNode); + + Matrix3 w2p(true); + Matrix3 l2w = pathNode->GetNodeTM(TimeValue(0)); + if( !pathNode->GetParentNode()->IsRootNode() ) + w2p = Inverse(pathNode->GetParentNode()->GetNodeTM(TimeValue(0))); + hsMatrix44 loc2Par = pathNode->Matrix3ToMatrix44(l2w * w2p); + + gemAffineParts ap; + decomp_affine(loc2Par.fMap, &ap); + hsAffineParts initParts; + AP_SET(initParts, ap); + + plAnimPath* animPath = TRACKED_NEW plAnimPath; + animPath->SetController(tmc); + animPath->InitParts(initParts); + + lineMod->SetPath(animPath); + + if( !pathNode->GetParentNode()->IsRootNode() ) + { + plMaxNode* parNode = (plMaxNode*)pathNode->GetParentNode(); + plSceneObject* parObj = parNode->GetSceneObject(); + if( parObj ) + { + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(lineMod->GetKey(), plRefMsg::kOnCreate, 0, plLineFollowMod::kRefParent); + hsgResMgr::ResMgr()->AddViaNotify(parObj->GetKey(), refMsg, plRefFlags::kPassiveRef); + } + } + + if( fCompPB->GetInt(kOffsetActive) ) + { + lineMod->SetOffsetDegrees(fCompPB->GetFloat(kOffsetDegrees)); + + if( fCompPB->GetInt(kOffsetClampActive) ) + { + lineMod->SetOffsetClamp(fCompPB->GetFloat(kOffsetClamp)); + } + + lineMod->SetForceToLine(fCompPB->GetInt(kForceToLine)); + } + + if( fCompPB->GetInt(kSpeedClampActive) ) + { + lineMod->SetSpeedClamp(fCompPB->GetFloat(kSpeedClamp)); + } + + fLineMod = lineMod; + + return true; +} + +plLineFollowMod* plLineFollowComponent::GetLineMod(plErrorMsg* pErrMsg) +{ + if( !fValid ) + return nil; + if( !fLineMod ) + { + int i; + for( i = 0; i < NumTargets(); i++ ) + { + plMaxNode* targ = (plMaxNode*)GetTarget(i); + IMakeLineMod(targ, pErrMsg); + if( fLineMod ) + break; + } + } + return fLineMod; +} + +hsBool plLineFollowComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + + if( !fLineMod ) + { + if( !IMakeLineMod(node, pErrMsg) ) + { + fValid = false; + return true; + } + } + + node->AddModifier(fLineMod, IGetUniqueName(node)); + + return true; +} + +hsBool plLineFollowComponent::SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + fValid = false; + fLineMod = nil; + + if( !fCompPB->GetINode(kPathObjectSel) ) + { + return true; + } + plMaxNode* pathNode = (plMaxNode*)fCompPB->GetINode(kPathObjectSel); + if( !pathNode ) + { + return true; + } + if( !pathNode->IsTMAnimated() ) + { + return true; + } + pathNode->SetCanConvert(false); + + if( plLineFollowMod::kFollowObject == fCompPB->GetInt(kFollowModeRadio) ) + { + if( !fCompPB->GetINode(kFollowObjectSel) ) + { + return true; + } + } + fValid = true; + pNode->SetForceLocal(true); + pNode->SetMovable(true); + return true; +} + +hsBool plLineFollowComponent::PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + fValid = false; + + if( plLineFollowMod::kFollowObject == fCompPB->GetInt(kFollowModeRadio) ) + { + plMaxNode* followNode = (plMaxNode*)fCompPB->GetINode(kFollowObjectSel); + if( !followNode->CanConvert() ) + { + return true; + } + } + plMaxNode* pathNode = (plMaxNode*)fCompPB->GetINode(kPathObjectSel); + if( !pathNode->GetParentNode()->IsRootNode() ) + { + if( !((plMaxNode*)pathNode->GetParentNode())->CanConvert() ) + { + return true; + } + } + fValid = true; + + + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// The stereizer component is sort of related to LineFollow. Really. + +class plStereizeComp : public plComponent +{ +public: + enum + { + kLeft, + kAmbientDist, + kTransition, + kSepAngle, + kMaxDist, + kMinDist + }; + + plLineFollowMod* ISetMaster(plStereizer* stereo, plMaxNode* node, plErrorMsg* pErrMsg); + +public: + plStereizeComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); + + hsBool Bail(plMaxNode* node, const char* msg, plErrorMsg* pErrMsg); + +}; + + + +CLASS_DESC(plStereizeComp, gStereizeDesc, "Stereo-ize", "Stereize", COMP_TYPE_AUDIO, STEREIZE_COMP_CID) + +ParamBlockDesc2 gStereizeBk +( + plComponent::kBlkComp, _T("Stereize"), 0, &gStereizeDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_STEREIZE, IDS_COMP_STEREIZE, 0, 0, NULL, + + plStereizeComp::kLeft, _T("Left"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_STEREIZE_LEFT, + end, + + plStereizeComp::kAmbientDist, _T("AmbientDist"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_STEREIZE_AMB, IDC_COMP_STEREIZE_AMB_SPIN, 1.0, + end, + + plStereizeComp::kTransition, _T("Transition"), TYPE_FLOAT, 0, 0, + p_default, 25.0, + p_range, 1.0, 500.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_STEREIZE_TRANS, IDC_COMP_STEREIZE_TRANS_SPIN, 1.0, + end, + + plStereizeComp::kSepAngle, _T("SepAngle"), TYPE_FLOAT, 0, 0, + p_default, 30.0, + p_range, 1.0, 80.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_STEREIZE_ANG, IDC_COMP_STEREIZE_ANG_SPIN, 1.0, + end, + + plStereizeComp::kMaxDist, _T("MaxDist"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 1.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_STEREIZE_MAXDIST, IDC_COMP_STEREIZE_MAXDIST_SPIN, 1.0, + end, + + plStereizeComp::kMinDist, _T("MinDist"), TYPE_FLOAT, 0, 0, + p_default, 5.0, + p_range, 1.0, 50.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_STEREIZE_MINDIST, IDC_COMP_STEREIZE_MINDIST_SPIN, 1.0, + end, + + end +); + +plStereizeComp::plStereizeComp() +{ + fClassDesc = &gStereizeDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plStereizeComp::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetForceLocal(true); + if( !node->GetParentNode()->IsRootNode() ) + ((plMaxNode*)node->GetParentNode())->SetForceLocal(true); + + return true; +} + +hsBool plStereizeComp::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plStereizeComp::Bail(plMaxNode* node, const char* msg, plErrorMsg* pErrMsg) +{ + pErrMsg->Set(true, node->GetName(), msg).CheckAndAsk(); + pErrMsg->Set(false); + return true; +} + +hsBool plStereizeComp::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plStereizer* stereo = TRACKED_NEW plStereizer; + + stereo->SetAmbientDist(fCompPB->GetFloat(kAmbientDist)); + stereo->SetTransition(fCompPB->GetFloat(kTransition)); + + hsScalar ang = fCompPB->GetFloat(kSepAngle); + if( ang > 80.f ) + ang = 80.f; + stereo->SetSepAngle(hsScalarDegToRad(ang)); + + stereo->SetMaxSepDist(fCompPB->GetFloat(kMaxDist)); + stereo->SetMinSepDist(fCompPB->GetFloat(kMinDist)); + + stereo->SetParentInitPos(node->GetLocalToParent44().GetTranslate()); + + stereo->SetAsLeftChannel(fCompPB->GetInt(kLeft)); + + node->AddModifier(stereo, IGetUniqueName(node)); + + // Do this after AddModifier, cuz that's when stereo gets a plKey. + ISetMaster(stereo, node, pErrMsg); + + return true; +} + +plLineFollowMod* plStereizeComp::ISetMaster(plStereizer* stereo, plMaxNode* node, plErrorMsg* pErrMsg) +{ + int numComp = node->NumAttachedComponents(false); + + int i; + for( i = 0; i < numComp; i++ ) + { + plComponentBase *comp = node->GetAttachedComponent(i); + if( comp && (comp->ClassID() == LINEFOLLOW_COMP_CID) ) + { + plLineFollowComponent* lineComp = (plLineFollowComponent*)comp; + plLineFollowMod* lineMod = lineComp->GetLineMod(pErrMsg); + if( lineMod ) + { + lineMod->AddStereizer(stereo->GetKey()); + return lineMod; + } + } + } + return nil; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// The swivel component is also sort of related to LineFollow. And stereizer. Really. + +class plSwivelComp : public plComponent +{ +public: + enum + { + kFaceTypeRadio, + kFaceObjectSel, + kPivotY, + kOffsetActive, + kOffsetX, + kOffsetY, + kOffsetZ, + kOffsetLocal + }; + enum + { + kFaceCamera, + kFaceListener, + kFacePlayer, + kFaceObject + }; + +public: + plSwivelComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); + + hsBool Bail(plMaxNode* node, const char* msg, plErrorMsg* pErrMsg); + +}; + + +class plSwivelComponentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2* map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2* pb = map->GetParamBlock(); + if( pb->GetInt(plSwivelComp::kFaceTypeRadio) == plSwivelComp::kFaceObject ) + map->Enable(plSwivelComp::kFaceObjectSel, TRUE); + else + map->Enable(plSwivelComp::kFaceObjectSel, FALSE); + } + return true; + +////////////////// + case WM_COMMAND: + { + if( (LOWORD(wParam) == IDC_RADIO_LISTENER) + || (LOWORD(wParam) == IDC_RADIO_PLAYER) + || (LOWORD(wParam) == IDC_RADIO_CAMERA) + || (LOWORD(wParam) == IDC_RADIO_OBJECT) ) + { + IParamBlock2* pb = map->GetParamBlock(); + if( pb->GetInt(plSwivelComp::kFaceTypeRadio) == plSwivelComp::kFaceObject ) + map->Enable(plSwivelComp::kFaceObjectSel, TRUE); + else + map->Enable(plSwivelComp::kFaceObjectSel, FALSE); + + return true; + } + } + + } + + return false; + } + void DeleteThis() {} +}; +static plSwivelComponentProc gSwivelProc; + + +CLASS_DESC(plSwivelComp, gSwivelDesc, "Swivel", "Swivel", COMP_TYPE_GRAPHICS, SWIVEL_COMP_CID) + +ParamBlockDesc2 gSwivelBk +( + plComponent::kBlkComp, _T("Swivel"), 0, &gSwivelDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SWIVEL, IDS_COMP_SWIVEL, 0, 0, &gSwivelProc, + + plSwivelComp::kFaceTypeRadio, _T("FaceType"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_RADIO_CAMERA, IDC_RADIO_LISTENER, IDC_RADIO_PLAYER, IDC_RADIO_OBJECT, + p_vals, plSwivelComp::kFaceCamera, plSwivelComp::kFaceListener, plSwivelComp::kFacePlayer, plSwivelComp::kFaceObject, + p_default, plSwivelComp::kFacePlayer, + end, + + plSwivelComp::kFaceObjectSel, _T("FaceObjectChoice"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CHOOSE_OBJECT, + p_prompt, IDS_COMP_CHOOSE_OBJECT, + end, + + plSwivelComp::kPivotY, _T("PivotY"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_PIVOTY, + end, + + plSwivelComp::kOffsetActive, _T("OffsetActive"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_OFFSETACTIVE, + p_enable_ctrls, 4, plSwivelComp::kOffsetX, plSwivelComp::kOffsetY, plSwivelComp::kOffsetZ, plSwivelComp::kOffsetLocal, + end, + + plSwivelComp::kOffsetX, _T("X"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_OFFSETX, IDC_COMP_OFFSETX_SPIN, 1.0, + end, + + plSwivelComp::kOffsetY, _T("Y"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_OFFSETY, IDC_COMP_OFFSETY_SPIN, 1.0, + end, + + plSwivelComp::kOffsetZ, _T("Z"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_OFFSETZ, IDC_COMP_OFFSETZ_SPIN, 1.0, + end, + + plSwivelComp::kOffsetLocal, _T("OffsetLocal"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_OFFSETLOCAL, + end, + + end +); + +plSwivelComp::plSwivelComp() +{ + fClassDesc = &gSwivelDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plSwivelComp::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetForceLocal(true); + node->SetMovable(true); + + return true; +} + +hsBool plSwivelComp::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plSwivelComp::Bail(plMaxNode* node, const char* msg, plErrorMsg* pErrMsg) +{ + pErrMsg->Set(true, node->GetName(), msg).CheckAndAsk(); + pErrMsg->Set(false); + return true; +} + +hsBool plSwivelComp::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plViewFaceModifier* pMod = TRACKED_NEW plViewFaceModifier; + pMod->SetOrigTransform(node->GetLocalToParent44(), node->GetParentToLocal44()); + node->AddModifier(pMod, IGetUniqueName(node)); + + if( fCompPB->GetInt(kPivotY) ) + pMod->SetFlag(plViewFaceModifier::kPivotY); + else + pMod->SetFlag(plViewFaceModifier::kPivotFace); + + switch( fCompPB->GetInt(kFaceTypeRadio) ) + { + case kFaceCamera: + pMod->SetFollowMode(plViewFaceModifier::kFollowCamera); + break; + + case kFaceListener: + pMod->SetFollowMode(plViewFaceModifier::kFollowListener); + break; + + case kFacePlayer: + pMod->SetFollowMode(plViewFaceModifier::kFollowPlayer); + break; + + case kFaceObject: + { + plMaxNode* targNode = (plMaxNode*)fCompPB->GetINode(kFaceObjectSel); + if( targNode && targNode->CanConvert() ) + { + pMod->SetFollowMode(plViewFaceModifier::kFollowObject, targNode->GetKey()); + } + else + { + pErrMsg->Set(true, node->GetName(), "Swivel to look at component %s has no Plasma object to look at.", GetINode()->GetName()).Show(); + pErrMsg->Set(false); + pMod->SetFollowMode(plViewFaceModifier::kFollowCamera); + } + } + break; + + } + + pMod->SetOffsetActive(fCompPB->GetInt(kOffsetActive)); + if( fCompPB->GetInt(kOffsetActive) ) + { + hsVector3 off(fCompPB->GetFloat(kOffsetX), fCompPB->GetFloat(kOffsetY), fCompPB->GetFloat(kOffsetZ)); + pMod->SetOffset(off, fCompPB->GetInt(kOffsetLocal)); + } + + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxAnimUtils.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxAnimUtils.cpp new file mode 100644 index 00000000..5afa20a5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxAnimUtils.cpp @@ -0,0 +1,369 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "Max.h" +#include "notetrck.h" + +#include "hsTypes.h" +#include "hsTemplates.h" + +#include "plMaxAnimUtils.h" +#include "../MaxExport/plErrorMsg.h" + +float TimeValueToGameTime(TimeValue t) +{ + int FR = ::GetFrameRate(); + int TPF = ::GetTicksPerFrame(); + int TPS = TPF*FR; + + return float(t)/float(TPS); +} + +bool GetSegMapAnimTime(const char *animName, SegmentMap *segMap, SegmentSpec::SegType type, float& begin, float& end) +{ + if (segMap) + { + if (animName && segMap->find(animName) != segMap->end()) + { + SegmentSpec *spec = (*segMap)[animName]; + if (spec->fType == type) + { + if (spec->fStart != -1) + begin = spec->fStart; + if (spec->fEnd != -1) + end = spec->fEnd; + return true; + } + } + } + + return false; +} + +SegmentMap *GetSharedAnimSegmentMap(std::vector& anims, plErrorMsg *pErrorMsg) +{ + if (anims.empty()) + return nil; + + SegmentMap *segMap = GetAnimSegmentMap(anims[0], pErrorMsg); + if (!segMap) + return nil; + + int i; + for (i = 1; i < anims.size(); i++) + { + SegmentMap *curSegMap = GetAnimSegmentMap(anims[i], pErrorMsg); + // This node doesn't have a segmap, so we can't have any anims shared among all the nodes. + if (!curSegMap) + { + DeleteSegmentMap(segMap); + return nil; + } + + if (segMap->begin() == segMap->end()) + { + DeleteSegmentMap(segMap); + return nil; + } + + SegmentMap::iterator it = segMap->begin(); + while (it != segMap->end()) + { + if (curSegMap->find(it->second->fName) == curSegMap->end()) + { + SegmentMap::iterator del = it; + it++; + segMap->erase(del->second->fName); + } + else + it++; + } + + DeleteSegmentMap(curSegMap); + } + + return segMap; +} + +SegmentSpec::SegmentSpec(float start, float end, char * name, SegType type) : + fStart(start), fEnd(end), fName(name), fType(type), fInitial(-1) +{ +} + +SegmentSpec::~SegmentSpec() +{ + delete [] fName; +} + +// constants used for parsing the note tracks + +enum NoteType +{ + kNoteStartAnim, + kNoteEndAnim, + kNoteStartLoop, + kNoteEndLoop, + kNoteMarker, + kNoteStopPoint, + kNoteInitial, + kNoteUnknown, + kNoteSuppress, +}; + +SegmentSpec::SegmentSpec() +{ + fStart = -1; + fEnd = -1; + fInitial = -1; + fName = nil; + fType = kAnim; +} + +bool SegmentSpec::Contains(SegmentSpec *spec) +{ + if (!spec) + return false; + + if (spec->fType == kMarker || spec->fType == kStopPoint) + return (spec->fStart >= fStart && spec->fStart <= fEnd); + + if (fStart == -1 || fEnd == -1) + return false; + + if (spec->fStart == -1) + return (fStart < spec->fEnd); + + if (spec->fEnd == -1) + return (fEnd > spec->fStart); + + if (fStart <= spec->fStart && fEnd >= spec->fEnd) + return true; + + return false; +} + +bool DoesHaveStopPoints(Animatable *anim) +{ + if (!anim || !anim->HasNoteTracks()) + return false; + + int numTracks = anim->NumNoteTracks(); + for (int i = 0; i < numTracks; i++) + { + DefNoteTrack *track = (DefNoteTrack *)anim->GetNoteTrack(i); + int numKeys = track->keys.Count(); + + for (int j = 0; j < numKeys; j++) + { + char buf[256]; + strcpy(buf, track->keys[j]->note); + strlwr(buf); + if (strstr(buf, "@stoppoint")) + return true; + } + } + + return false; +} + +void GetSegment(const char *note, float time, SegmentMap *segMap, plErrorMsg *pErrMsg) +{ + char segName[256]; + char segSuffix[256]; + + int matchedFields = sscanf(note, " %[^@] @ %s ", segName, segSuffix); + + if (matchedFields == 2) + { + NoteType type = kNoteUnknown; + + if (!stricmp(segSuffix, "start") || + !stricmp(segSuffix, "begin")) + type = kNoteStartAnim; + else if (!stricmp(segSuffix, "end")) + type = kNoteEndAnim; + else if (!stricmp(segSuffix, "startloop") || + !stricmp(segSuffix, "loopstart") || + !stricmp(segSuffix, "beginloop") || + !stricmp(segSuffix, "loopbegin")) + type = kNoteStartLoop; + else if (!stricmp(segSuffix, "endloop") || + !stricmp(segSuffix, "loopend")) + type = kNoteEndLoop; + else if (!stricmp(segSuffix, "marker")) + type = kNoteMarker; + else if (!stricmp(segSuffix, "stoppoint")) + type = kNoteStopPoint; + else if (!stricmp(segSuffix, "initial")) + type = kNoteInitial; + else if (!stricmp(segSuffix, "suppress")) + type = kNoteSuppress; + + if (type == kNoteUnknown) + { + if (pErrMsg) + { + pErrMsg->Set(true, "NoteTrack Anim Error", "Malformed segment note: %s", segName); + pErrMsg->Show(); + pErrMsg->Set(); + } + } + else + { + SegmentMap::iterator existing = segMap->find(segName); + SegmentSpec *existingSpec = (existing != segMap->end()) ? (*existing).second : nil; + const char *kErrorTitle = "NoteTrack Anim Error"; + + if (existingSpec) + { + // an existing spec, but we're processing a start note? + if (type == kNoteStartAnim && pErrMsg) + { + pErrMsg->Set(true, kErrorTitle, "Got out of order start note. No Start given for %s", segName).Show(); + pErrMsg->Set(); + } + // existing spec, has an end, we're also processing an end? + else if (type == kNoteEndAnim && existingSpec->fEnd != -1 && pErrMsg) + { + pErrMsg->Set(true, kErrorTitle, "Got two ends for the same segment %s", segName).Show(); + pErrMsg->Set(); + } + else if (type == kNoteStartLoop && existingSpec->fStart != -1 && pErrMsg) + { + pErrMsg->Set(true, kErrorTitle, "Got two loop starts for the same segment, %s", segName).Show(); + pErrMsg->Set(); + } + else if (type == kNoteEndLoop && existingSpec->fEnd != -1 && pErrMsg) + { + pErrMsg->Set(true, kErrorTitle, "Got two loop ends for the same segment, %s", segName).Show(); + pErrMsg->Set(); + } + else if (type == kNoteMarker && pErrMsg) + { + pErrMsg->Set(true, kErrorTitle, "Marker has the same name (%s) as another spec in its notetrack", segName).Show(); + pErrMsg->Set(); + } + else if (type == kNoteStopPoint && pErrMsg) + { + pErrMsg->Set(true, kErrorTitle, "Stop point has the same name (%s) as another spec in its notetrack", segName).Show(); + pErrMsg->Set(); + } + + if (type == kNoteEndAnim || type == kNoteEndLoop) + existingSpec->fEnd = time; + else if (type == kNoteStartLoop) + existingSpec->fStart = time; + else if (type == kNoteInitial) + existingSpec->fInitial = time; + } + else + { + if (type == kNoteEndAnim && pErrMsg) + { + pErrMsg->Set(true, kErrorTitle, "Got an end note without a corresponding start. Ignoring %s", segName).Show(); + pErrMsg->Set(); + } + else + { + char *nameCopy = TRACKED_NEW char[strlen(segName)+1]; + strcpy(nameCopy, segName); + + switch (type) + { + case kNoteStartAnim: + (*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(time, -1, nameCopy, SegmentSpec::kAnim); + break; + + case kNoteStartLoop: + (*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(time, -1, nameCopy, SegmentSpec::kLoop); + break; + + case kNoteEndLoop: + (*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(-1, time, nameCopy, SegmentSpec::kLoop); + break; + + case kNoteMarker: + (*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(time, -1, nameCopy, SegmentSpec::kMarker); + break; + + case kNoteStopPoint: + (*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(time, -1, nameCopy, SegmentSpec::kStopPoint); + break; + + case kNoteSuppress: + (*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(-1, -1, nameCopy, SegmentSpec::kSuppress); + break; + + default: + delete [] nameCopy; + break; + } + } + } + } + } +} + +// Read through all the notes in all the note tracks on the given node +// Check the contents of each node for a name like "walk@start", i.e. @[start | end] +// For each match, open a segment specification and p +SegmentMap * GetAnimSegmentMap(Animatable *anim, plErrorMsg *pErrMsg) +{ + if (!anim->HasNoteTracks()) + return nil; + + SegmentMap *segMap = TRACKED_NEW SegmentMap(); + + int numTracks = anim->NumNoteTracks(); + + for (int i = 0; i < numTracks; i++) + { + DefNoteTrack * track = (DefNoteTrack *)anim->GetNoteTrack(i); + int numKeys = track->keys.Count(); + + for (int j = 0; j < numKeys; j++) + { + char *note = track->keys[j]->note; + float time = TimeValueToGameTime(track->keys[j]->time); + GetSegment(note, time, segMap, pErrMsg); + } + } + + return segMap; +} + +void DeleteSegmentMap(SegmentMap *segMap) +{ + // If we have a segment map, delete the memory associated with it + if (segMap) + { + for (SegmentMap::iterator i = segMap->begin(); i != segMap->end(); i++) + delete (*i).second; + + delete segMap; + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxAnimUtils.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxAnimUtils.h new file mode 100644 index 00000000..ce2569ea --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxAnimUtils.h @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLMAXANIMUTILS_H +#define PLMAXANIMUTILS_H + +#include "hsConfig.h" +#include +#include +#include "hsStlSortUtils.h" + +class plErrorMsg; +class Animatable; + +// SEGMENTSPEC +// A simple helper class to hold information captured from the note track about animation segments +class SegmentSpec +{ +public: + enum SegType { kAnim, kLoop, kMarker, kStopPoint, kSuppress }; + + float fStart; // beginning of the segment in game time + float fEnd; // end of the segment in game time + float fInitial; // initial position of the animation (-1 for the start) + char * fName; // name of the segment: controls lifespan of the name + SegType fType; + + SegmentSpec(); + SegmentSpec(float start, float end, char * name, SegType); + ~SegmentSpec(); + + bool Contains(SegmentSpec *spec); +}; + + +// a table mapping segment names to segment spec objects +typedef std::map SegmentMap; + +// You can pass in nil for pErrMsg for silent operation +SegmentMap *GetAnimSegmentMap(Animatable *anim, plErrorMsg *pErrMsg); + +void DeleteSegmentMap(SegmentMap *segMap); + +SegmentMap *GetSharedAnimSegmentMap(std::vector& anims, plErrorMsg *pErrorMsg); + +bool GetSegMapAnimTime(const char *animName, SegmentMap *segMap, SegmentSpec::SegType type, float& begin, float& end); + +// For internal use +void GetSegment(const char *note, float time, SegmentMap *segMap, plErrorMsg *pErrMsg); + +bool DoesHaveStopPoints(Animatable *anim); + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxWaveUtils.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxWaveUtils.cpp new file mode 100644 index 00000000..0f1347d0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxWaveUtils.cpp @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plMaxWaveUtils.h" +#include "../plAudio/plWavFile.h" +#include "hsTypes.h" + +SegmentMap *GetWaveSegmentMap(const char *file, plErrorMsg *pErrMsg) +{ + CWaveFile waveFile; + waveFile.Open(file, nil, WAVEFILE_READ); + int numMarkers = waveFile.GetNumMarkers(); + if (numMarkers == 0) + return nil; + + SegmentMap *segMap = TRACKED_NEW SegmentMap(); + + for (int i = 0; i < waveFile.GetNumMarkers(); i++) + { + plSoundMarker *marker = waveFile.GetSoundMarker(i); + GetSegment(marker->fName, (float)(marker->fOffset), segMap, pErrMsg); + } + + return segMap; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxWaveUtils.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxWaveUtils.h new file mode 100644 index 00000000..c3db78b3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMaxWaveUtils.h @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLMAXWAVEUTILS_H +#define PLMAXWAVEUTILS_H + +#include "plMaxAnimUtils.h" + +SegmentMap *GetWaveSegmentMap(const char *file, plErrorMsg *pErrMsg); + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMiscComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMiscComponents.cpp new file mode 100644 index 00000000..2280f335 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMiscComponents.cpp @@ -0,0 +1,2557 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "resource.h" +#include "plMiscComponents.h" +#include "plComponentReg.h" +#include "../../AssetMan/PublicInterface/MaxAssInterface.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "../MaxMain/plMaxNodeData.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pnSceneObject/plDrawInterface.h" + +#include "../MaxMain/plPluginResManager.h" + + +#include "plgDispatch.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plIntRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" + + +#include "../plScene/plSceneNode.h" +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../plInterp/plController.h" +#include "../MaxMain/plMaxNode.h" +#include "../pnKeyedObject/plKey.h" +#include "../plFile/plFileUtils.h" + +// Follow mod +#include "../plInterp/plAnimPath.h" +#include "../pfAnimation/plFollowMod.h" + +//Player Attention Related +#include "../pfCamera/plInterestingModifier.h" + +//Player Start Position +#include "../plModifier/plSpawnModifier.h" + +// RunTime related (Sprites, Billboards, LightMaps, etc., etc.) +#include "../pfAnimation/plViewFaceModifier.h" // ViewFace Comp + +// Anim Related +#include "plMaxAnimUtils.h" + +// CavView component. +#include "../plScene/plPostEffectMod.h" + +// Location Related +#include "../plAgeDescription/plAgeDescription.h" +#include "../MaxMain/plMaxCFGFile.h" +#include "../MaxMain/plAgeDescInterface.h" +#include "../plFile/hsFiles.h" +#include "../plResMgr/plPageInfo.h" + +#include "../plDrawable/plGeometrySpan.h" + +#include "../MaxConvert/plConvert.h" + +// ImageLib +#include "../plModifier/plImageLibMod.h" +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" +#include "../MaxConvert/plLayerConverter.h" +#include "../plGImage/plBitmap.h" + +void DummyCodeIncludeFuncMisc() +{ + RegisterNotification(plPageInfoComponent::NotifyProc, nil, NOTIFY_FILE_POST_OPEN); + RegisterNotification(plPageInfoComponent::NotifyProc, nil, NOTIFY_SYSTEM_POST_RESET); + RegisterNotification(plPageInfoComponent::NotifyProc, nil, NOTIFY_SYSTEM_POST_NEW); +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Interesting Component +// +// + +//Class that accesses the paramblock below. +class plInterestingComponent : public plComponent +{ +public: + plInterestingComponent(); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plInterestingComponent, gInterestDesc, "Player Attention", "PlayerAttention", COMP_TYPE_MISC, Class_ID(0x6f48a7, 0x7ab86088)) + +enum +{ + kInteresting, kCamInterestRadius, kCamInterestWeight +}; + +ParamBlockDesc2 gInterestBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + 1, _T("Player Attention"), 0, &gInterestDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_INTEREST, IDS_COMP_INTERESTS, 0, 0, NULL, + + // params + kInteresting, _T("interesting"), TYPE_STRING, 0, 0, + end, + + kCamInterestRadius, _T("CamIntersestRadius"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 100.0f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_INTEREST_EDIT1, IDC_COMP_INTEREST_SPIN1, 1.0f, + end, + + kCamInterestWeight, _T("CamIntersestWeight"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_range, 0.0, 1.0, + p_default, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_INTEREST_EDIT2, IDC_COMP_INTEREST_SPIN2, 0.001f, + end, + + end +); + + +plInterestingComponent::plInterestingComponent() +{ + fClassDesc = &gInterestDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plInterestingComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + + plInterestingModifier* pMod = TRACKED_NEW plInterestingModifier; + + hsScalar loader = fCompPB->GetFloat(kCamInterestRadius); + pMod->SetInterestRadius(loader); + loader = fCompPB->GetFloat(kCamInterestWeight); + pMod->SetInterestWeight(loader); + + node->AddModifier(pMod, IGetUniqueName(node)); + return true; +} + + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// PageInfo Component +// +// + + +class plPageInfoComponentProc : public ParamMap2UserDlgProc +{ +protected: + HWND fhDlg; + IParamBlock2 *fPB; + + void ILoadPages() + { + HWND hPageCombo = GetDlgItem(fhDlg, IDC_COMP_LOCATION_PAGECOMBO); + ComboBox_ResetContent(hPageCombo); + + int idx = ComboBox_GetCurSel( GetDlgItem( fhDlg, IDC_COMP_LOCATION_AGECOMBO ) ); + if( idx == CB_ERR ) + return; + char *agePath = (char *)ComboBox_GetItemData( GetDlgItem( fhDlg, IDC_COMP_LOCATION_AGECOMBO ), idx ); + if( agePath == nil ) + return; + + // Get the age description + plAgeDescription aged( agePath ); + + // Set the seqPrefix here. (Where else would you suggest?) + fPB->SetValue( plPageInfoComponent::kInfoSeqPrefix, 0, (int)aged.GetSequencePrefix() ); + + const char *curPage = fPB->GetStr(plPageInfoComponent::kInfoPage); + if (curPage && *curPage == '\0') + curPage = nil; + + // Load the page combo and select the saved page (if it's in there) + plAgePage *page; + aged.SeekFirstPage(); + while( ( page = aged.GetNextPage() ) != nil ) + { + int idx = ComboBox_AddString(hPageCombo, page->GetName() ); + if (curPage && !strcmp(page->GetName(), curPage)) + ComboBox_SetCurSel(hPageCombo, idx); + ComboBox_SetItemData( hPageCombo, idx, (int)page->GetSeqSuffix() ); + } + } + + void IClearAges( HWND combo ) + { + while( ComboBox_GetCount( combo ) > 0 ) + { + char *path = (char *)ComboBox_GetItemData( combo, 0 ); + if( path != nil ) + delete [] path; + ComboBox_DeleteString( combo, 0 ); + } + } + + bool ILoadAges() + { + HWND hAgeCombo = GetDlgItem(fhDlg, IDC_COMP_LOCATION_AGECOMBO); + IClearAges( hAgeCombo ); + + hsTArray ageFiles; + plAgeDescInterface::BuildAgeFileList( ageFiles ); + + const char *curAge = fPB->GetStr(plPageInfoComponent::kInfoAge); + if (!curAge || *curAge == '\0') + curAge = ""; + + for( int i = 0; i < ageFiles.GetCount(); i++ ) + { + char ageName[_MAX_FNAME]; + _splitpath( ageFiles[ i ], nil, nil, ageName, nil ); + + int idx = ComboBox_AddString( hAgeCombo, ageName ); + // Store the pathas the item data for later (so don't free it yet!) + ComboBox_SetItemData( hAgeCombo, idx, (LPARAM)ageFiles[ i ] ); + + if( !strcmp( ageName, curAge ) ) + ComboBox_SetCurSel( hAgeCombo, idx ); + } + + return true; + } + +public: + void DeleteThis() {} + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + fhDlg = hWnd; + fPB = map->GetParamBlock(); + ILoadAges(); + ILoadPages(); + return TRUE; + } + + case WM_DESTROY: + IClearAges( GetDlgItem( hWnd, IDC_COMP_LOCATION_AGECOMBO ) ); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_COMP_LOCATION_AGECOMBO) + { + HWND hAgeCombo = (HWND)lParam; + int idx = ComboBox_GetCurSel(hAgeCombo); + if (idx != CB_ERR) + { + char buf[256]; + ComboBox_GetText(hAgeCombo, buf, sizeof(buf)); + fPB->SetValue(plPageInfoComponent::kInfoAge, 0, buf); + fPB->SetValue(plPageInfoComponent::kInfoPage, 0, ""); + fPB->SetValue(plPageInfoComponent::kInfoSeqSuffix, 0, (int)-1 ); + ILoadPages(); + } + + return TRUE; + } + if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_COMP_LOCATION_PAGECOMBO) + { + HWND hPageCombo = (HWND)lParam; + int idx = ComboBox_GetCurSel(hPageCombo); + if (idx != CB_ERR) + { + char buf[256]; + ComboBox_GetText(hPageCombo, buf, sizeof(buf)); + fPB->SetValue( plPageInfoComponent::kInfoPage, 0, buf ); + fPB->SetValue( plPageInfoComponent::kInfoSeqSuffix, 0, ComboBox_GetItemData( hPageCombo, idx ) ); + } + + return TRUE; + } + return FALSE; + } + + return FALSE; + } +}; + +// For the paramblock below. +static plPageInfoComponentProc gPageInfoCompProc; + +//Max desc stuff necessary. +CLASS_DESC(plPageInfoComponent, gPageInfoDesc, "Page Info", "PageInfo", COMP_TYPE_MISC, PAGEINFO_CID) + +//Max paramblock2 stuff below. +ParamBlockDesc2 gPageInfoCompBk +( + plComponent::kBlkComp, _T("PageInfo"), 0, &gPageInfoDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_PAGEINFO, IDS_COMP_PAGEINFOS, 0, 0, &gPageInfoCompProc, + + plPageInfoComponent::kInfoAge, _T("ageName"), TYPE_STRING, 0, 0, + end, + + plPageInfoComponent::kInfoPage, _T("pageName"), TYPE_STRING, 0, 0, + end, + + plPageInfoComponent::kInfoSeqPrefix, _T("sequencePrefix"), TYPE_INT, 0, 0, + end, + + plPageInfoComponent::kInfoSeqSuffix, _T("sequenceSuffix"), TYPE_INT, 0, 0, + end, + + plPageInfoComponent::kRefVolatile_PageInfoUpdated, _T( "pageInfoUpdated" ), TYPE_BOOL, 0, 0, + p_default, 0, + end, + + plPageInfoComponent::kItinerant, _T("itinerant"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK1, + end, + + + end +); + +char plPageInfoComponent::fCurrExportedAge[ 256 ] = ""; + +plPageInfoComponent::plPageInfoComponent() +{ + fSeqNumValidated = false; + fItinerant = false; + fClassDesc = &gPageInfoDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plPageInfoComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + // Another component already created a location, don't override it + if (pNode->GetRoomKey()) + return false; + + char *age = fCompPB->GetStr(kInfoAge); + char *room = fCompPB->GetStr(kInfoPage); + + if (!age || *age == '\0' || !room || *room == '\0') + { + pErrMsg->Set(true, + "PageInfo Component Error", + "No Label for the Age, Chapter, or Page, on the Location component found on %s", + pNode->GetName()).Show(); + return false; + } + + // If we're only exporting a certain page, and this location isn't it, don't export this node + const char* exportPage = plConvert::Instance().GetConvertSettings()->fExportPage; + if (exportPage && stricmp(room, exportPage)) + { + pNode->SetCanConvert(false); + return true; + } + + // Check to make sure we don't try to export more than one age at a time + if( fCurrExportedAge[ 0 ] == 0 ) + strncpy( fCurrExportedAge, age, sizeof( fCurrExportedAge ) ); + else + { + if( stricmp( fCurrExportedAge, age ) != 0 ) + { + // Our only currently accepted exception (eh?) is GlobalClothing and GlobalAvatars + if( ( stricmp( age, "GlobalAvatars" ) == 0 && stricmp( fCurrExportedAge, "GlobalClothing" ) == 0 ) || + ( stricmp( age, "GlobalClothing" ) == 0 && stricmp( fCurrExportedAge, "GlobalAvatars" ) == 0 ) ) + { + } + else + { + pErrMsg->Set( true, "PageInfo Component Error", + "The scene you are trying to export is attempting to export to both ages %s and" + " %s. You are only allowed to export to one age at a time.", + fCurrExportedAge, age ).Show(); + + // Reset for next time + return false; + } + } + } + + // Make sure our sequence partitions are up-to-date + IUpdateSeqNumbersFromAgeFile( pErrMsg ); + + // Need to re-get our age and page name here, since IUpdate() might change them + age = fCompPB->GetStr( kInfoAge ); + room = fCompPB->GetStr( kInfoPage ); + fItinerant = fCompPB->GetInt(kItinerant); + + // Build our sequence number + Int32 newNum, seqNum; + seqNum = plPageInfoUtils::CombineSeqNum( fCompPB->GetInt( kInfoSeqPrefix ), fCompPB->GetInt( kInfoSeqSuffix ) ); + newNum = plPluginResManager::ResMgr()->VerifySeqNumber( seqNum, age, room ); + if( newNum != seqNum ) + { + if( !fSeqNumValidated && seqNum != 0 ) + { + // What error was it, exactly? + char errMsg[ 1024 ]; + const plPageInfo *lastPage = plPluginResManager::ResMgr()->GetLastVerifyPage(); + + if( plPluginResManager::ResMgr()->GetLastVerifyError() == plPluginResManager::kErrRightPageWrongSeq ) + { + sprintf( errMsg, "The Page Info component for %s>%s applied to object %s is attempting to export with a sequence number " + "different from what is already exported. Further, the already-exported page %s>%s has " + "the same sequence number as this Page Info component now. This is most likely due to the .age " + "files having been changed since the old page was exported. The recommended solution would be to " + "delete the offending page data and export both pages again.\n\n" + "The exporter has assigned a valid temporary sequence number for page %s>%s, but this data should not be used " + "for playing over the network or released for external use.\n\n" + "\t(Original sequence #: 0x%X)\n\t(Temporary sequence #: 0x%X)", + age, room, pNode->GetName(), + lastPage->GetAge(), lastPage->GetPage(), age, room, seqNum, newNum ); + } + else if( plPluginResManager::ResMgr()->GetLastVerifyError() == plPluginResManager::kErrSeqAlreadyTaken ) + { + sprintf( errMsg, "The Page Info component for %s>%s applied to object %s is attempting to export with " + "an identical sequence number to the already-exported page %s>%s. This is usually due to " + "either page having been exported with an invalid or missing .age file. Please verify that both " + "pages have valid .age files and their sequence numbers do not conflict in the Age Description " + "Manager.\n\n" + "The exporter has assigned a valid temporary sequence number for page %s>%s, but this data should not be used " + "for playing over the network or released for external use.\n\n" + "\t(Original sequence #: 0x%X)\n\t(Temporary sequence #: 0x%X)", + age, room, pNode->GetName(), + lastPage->GetAge(), lastPage->GetPage(), age, room, seqNum, newNum ); + } + else if( plPluginResManager::ResMgr()->GetLastVerifyError() == plPluginResManager::kErrCantFindValid ) + { + sprintf( errMsg, "The Page Info component for %s>%s applied to object %s is attempting to export with " + "an invalid sequence number. The exporter could not find a valid, free sequence number to use, so this " + "page cannot be exported. Contact mcn (ext 264) immediately!\n\n" + "\t(Original sequence #: 0x%X)", + age, room, pNode->GetName(), seqNum ); + pErrMsg->Set( true, "PageInfo Convert Error", errMsg ).Show(); + return false; + } + else + { + sprintf( errMsg, "The Page Info component for %s>%s applied to object %s is attempting to export with " + "a sequence number that is invalid for an unknown reason.\n\n" + "The exporter has assigned a valid temporary sequence number, but this data should not be used " + "for playing over the network or released for external use.\n\n" + "\t(Original sequence #: 0x%X)\n\t(Temporary sequence #: 0x%X)", + age, room, pNode->GetName(), seqNum, newNum ); + } + pErrMsg->Set( true, "PageInfo Convert Error", errMsg ).Show(); + pErrMsg->Set( false ); + fSeqNumValidated = true; + } + seqNum = newNum; + } + if (fItinerant) + int i = 0; + plKey roomKey = plPluginResManager::ResMgr()->NameToLoc(age, room, seqNum, fItinerant ); + + if(!roomKey) + { + pErrMsg->Set(true, + "PageInfo Convert Error", + "Location Component %s has a Missing Location. Nuke the files in the dat directory and re-export.", + pNode->GetName()).Show(); + return false; + } + pNode->SetRoomKey(roomKey); + + + if (!strcmp(age, "GlobalClothing")) + ((plSceneNode *)roomKey->GetObjectPtr())->SetFilterGenericsOnly(true); + + return true; +} + +hsBool plPageInfoComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + // Make sure we clear this flag so that the next time around it's clear + fCompPB->SetValue( kRefVolatile_PageInfoUpdated, 0, (int)false ); + fSeqNumValidated = false; + + return true; +} + +hsBool plPageInfoComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plKey snKey = node->GetRoomKey(); + plSceneObject *so = node->GetSceneObject(); + if(so) + { + if (node->GetSwappableGeom()) + { + snKey->Release(so->GetKey()); + node->GetMaxNodeData()->SetSceneObject(nil); + + // Since child refs are now passive, this isn't needed. + /* + plMaxNode *parent = (plMaxNode *)node->GetParentNode(); + if (!parent->IsRootNode() && !parent->GetSwappableGeom()) + { + parent->GetSceneObject()->GetKey()->Release(so->GetKey()); + } + */ + } + } + + return true; +} + +char *plPageInfoComponent::GetAgeName() +{ + return fCompPB->GetStr(ParamID(kInfoAge)); +} + +//// IVerifyLatestAgeAsset /////////////////////////////////////////////////// +// Checks in assetMan to make sure we have the latest .age file to export +// with. + +void plPageInfoComponent::IVerifyLatestAgeAsset( const char *ageName, const char *localPath, plErrorMsg *errMsg ) +{ + char ageFileName[ MAX_PATH ], assetPath[ MAX_PATH ]; + + + MaxAssInterface *assetMan = GetMaxAssInterface(); + if( assetMan == nil ) + return; // No AssetMan available + + // Try to find it in assetMan + sprintf( ageFileName, "%s.age", ageName ); + jvUniqueId assetId; + if (assetMan->FindAssetByFilename(ageFileName, assetId)) + { + // Get the latest version + if (!assetMan->GetLatestVersionFile(assetId, assetPath, sizeof(assetPath))) + { + errMsg->Set( true, "PageInfo Convert Error", + "Unable to update age file for '%s' because AssetMan was unable to get the latest version. Using local copy instead.", ageName ).Show(); + errMsg->Set( false ); + return; + } + + // Got the latest version, just copy over and roll! + plFileUtils::RemoveFile( localPath ); + plFileUtils::FileCopy( assetPath, localPath ); + } + else + { + // Not found, so just assume it's a local one (no error) + } +} + +//// IUpdateSeqNumbersFromAgeFile //////////////////////////////////////////// +// With the new sequence numbers, it's vital that our sequence numbers that +// we use to export are synched up with the latest .age files. This function +// makes sure that we're synched before we start using 'em. + +void plPageInfoComponent::IUpdateSeqNumbersFromAgeFile( plErrorMsg *errMsg ) +{ + // Check to see if we've updated already + if( fCompPB->GetInt( kRefVolatile_PageInfoUpdated ) ) + return; // Already updated this pass! + + // Mark us as updated + fCompPB->SetValue( kRefVolatile_PageInfoUpdated, 0, (int)true ); + + char path[MAX_PATH]; + + const char *ageFolder = plPageInfoUtils::GetAgeFolder(); + if( ageFolder == nil ) + { + errMsg->Set( true, + "PageInfo Convert Error", + "There was a problem converting the PageInfo Component %s (the age folder couldn't be located). " + "The exporter will assign a temporary sequence number to this page, but you'll be lucky if it works at all.", + GetINode()->GetName() ).Show(); + errMsg->Set( false ); + fCompPB->SetValue( kInfoSeqPrefix, 0, 0 ); + fCompPB->SetValue( kInfoSeqSuffix, 0, 0 ); + return; + } + const char *curAge = fCompPB->GetStr( kInfoAge ); + if( !curAge || *curAge == '\0' ) + { + errMsg->Set( true, + "PageInfo Convert Error", + "There was a problem converting the PageInfo Component %s (no age name was selected). " + "The exporter will assign a temporary sequence number to this page, but you'll be lucky if it works at all.", + GetINode()->GetName()).Show(); + errMsg->Set( false ); + fCompPB->SetValue( kInfoSeqPrefix, 0, 0 ); + fCompPB->SetValue( kInfoSeqSuffix, 0, 0 ); + return; + } + sprintf(path, "%s%s.age", ageFolder, curAge); + + IVerifyLatestAgeAsset( curAge, path, errMsg ); + + hsUNIXStream s; + if (!s.Open(path)) + { + errMsg->Set( true, + "PageInfo Convert Error", + "There was a problem converting the PageInfo Component %s (the age name \"%s\" is invalid). " + "The exporter will assign a temporary sequence number to this page, but you'll be lucky if it works at all.", + GetINode()->GetName(), curAge ).Show(); + errMsg->Set( false ); + fCompPB->SetValue( kInfoSeqPrefix, 0, 0 ); + fCompPB->SetValue( kInfoSeqSuffix, 0, 0 ); + return; + } + + // create and read the age desc + plAgeDescription aged; + aged.Read(&s); + s.Close(); + + // Update based on the age file now + fCompPB->SetValue( kInfoSeqPrefix, 0, (int)aged.GetSequencePrefix() ); + + // Find our page + const char *compPBPageName = fCompPB->GetStr( kInfoPage ); + if( compPBPageName == nil ) + { + errMsg->Set( true, + "PageInfo Convert Error", + "There was a problem converting the PageInfo Component %s (no page name was specified). " + "The exporter will assign a temporary sequence number to this page, but you'll be lucky if it works at all.", + GetINode()->GetName() ).Show(); + errMsg->Set( false ); + fCompPB->SetValue( kInfoSeqPrefix, 0, 0 ); + fCompPB->SetValue( kInfoSeqSuffix, 0, 0 ); + return; + } + + plAgePage *page; + aged.SeekFirstPage(); + + while( ( page = aged.GetNextPage() ) != nil ) + { + if( stricmp( page->GetName(), compPBPageName ) == 0 ) + { + fCompPB->SetValue( kInfoSeqSuffix, 0, (int)page->GetSeqSuffix() ); + + // Also re-copy the page name, just to make sure the case is correct + fCompPB->SetValue( kInfoPage, 0, (char *)page->GetName() ); + return; + } + } + + // If we got here, the page name is invalid + char msg[ 512 ]; + sprintf( msg, "There was a problem converting the PageInfo Component %s (the page \"%s\" wasn't found in the age file for age \"%s\"). " + "The exporter will assign a temporary sequence number to this page, but you'll be lucky if it works at all.", + GetINode()->GetName(), compPBPageName, curAge ); + errMsg->Set( true, "PageInfo Convert Error", msg ).Show(); + errMsg->Set( false ); + fCompPB->SetValue( kInfoSeqPrefix, 0, 0 ); + fCompPB->SetValue( kInfoSeqSuffix, 0, 0 ); +} + +const char *plPageInfoUtils::GetAgeFolder() +{ + static char ageFolder[MAX_PATH]; + static bool initialized = false; + + if (!initialized) + { + initialized = true; + ageFolder[0] = '\0'; + + const char *plasmaPath = plMaxConfig::GetClientPath(); + if (!plasmaPath) + return nil; + + strcpy(ageFolder, plasmaPath); + strcat(ageFolder, plAgeDescription::kAgeDescPath); + } + + if (ageFolder[0] != '\0') + return ageFolder; + else + return nil; +} + +Int32 plPageInfoUtils::CombineSeqNum( int prefix, int suffix ) +{ + hsAssert(abs(prefix) < 0xFF, "Sequence prefix must be less then the max 8-bit number"); + hsAssert(suffix <= 0xFFFF, "Sequence suffix must be less then the max 16-bit number"); + hsAssert(suffix >= 0, "Sequence suffix must be unsigned"); + if( prefix < 0 ) + return -( ( ( -prefix ) << 16 ) + suffix ); + else + return ( prefix << 16 ) + suffix; +} + +Int32 plPageInfoUtils::GetCommonSeqNumFromNormal( Int32 normalSeqNumber, int whichCommonPage ) +{ + int prefix; + const int kFirstCommonSeqSuffix = 0xffff; + + hsAssert( whichCommonPage < plAgeDescription::kNumCommonPages, "Invalid common page index in GetCommonSeqNumFromNormal()" ); + + if( normalSeqNumber < 0 ) + { + prefix = -( (-normalSeqNumber) >> 16 ); + } + else + prefix = normalSeqNumber >> 16; + + return CombineSeqNum( prefix, kFirstCommonSeqSuffix - whichCommonPage ); +} + +Int32 plPageInfoUtils::GetSeqNumFromAgeDesc( const char *ageName, const char *pageName ) +{ + int seqPrefix, seqSuffix = 0; + plAgeDescription *aged = GetAgeDesc( ageName ); + if( aged == nil ) + { + // ???? This ain't good...attempt to get the resMgr to give us a temporary seqNum... + return 0; + } + + seqPrefix = aged->GetSequencePrefix(); + + // Find our page + plAgePage *page; + aged->SeekFirstPage(); + while( ( page = aged->GetNextPage() ) != nil ) + { + if( stricmp( pageName, page->GetName() ) == 0 ) + { + seqSuffix = page->GetSeqSuffix(); + break; + } + } + + delete aged; + + return CombineSeqNum( seqPrefix, seqSuffix ); +} + +plAgeDescription *plPageInfoUtils::GetAgeDesc( const char *ageName ) +{ + char path[ MAX_PATH ]; + + const char *ageFolder = plPageInfoUtils::GetAgeFolder(); + if( ageFolder == nil || ageName == nil ) + return nil; + + sprintf( path, "%s%s.age", ageFolder, ageName ); + + hsUNIXStream s; + if( !s.Open( path ) ) + return nil; + + // Create and read the age desc + plAgeDescription *aged = TRACKED_NEW plAgeDescription; + aged->Read( &s ); + s.Close(); + + return aged; +} + +const char* LocCompGetPage(plComponentBase* comp) +{ + if (!comp) + return nil; + + const char* page = nil; + + if (comp->ClassID() == PAGEINFO_CID) + { + IParamBlock2* pb = comp->GetParamBlockByID(plComponentBase::kBlkComp); + page = pb->GetStr(plPageInfoComponent::kInfoPage); + } + + if (page && *page != '\0') + return page; + + return nil; +} + +static char *CheckPageInfoCompsRecur(plMaxNode *node) +{ + plComponentBase *comp = node->ConvertToComponent(); + if (comp && comp->ClassID() == PAGEINFO_CID) + { + plPageInfoComponent* pageComp = (plPageInfoComponent*)comp; + return pageComp->GetAgeName(); + } + + for (int i = 0; i < node->NumberOfChildren(); i++) + { + char *result = CheckPageInfoCompsRecur((plMaxNode*)node->GetChildNode(i)); + if (result) + return result; + } + return nil; +} + +void plPageInfoComponent::NotifyProc(void *param, NotifyInfo *info) +{ + if (info->intcode == NOTIFY_FILE_POST_OPEN) + { + char *ageName = CheckPageInfoCompsRecur((plMaxNode*)GetCOREInterface()->GetRootNode()); + if (ageName != nil) + strncpy( fCurrExportedAge, ageName, sizeof( fCurrExportedAge ) ); + } + else if (info->intcode == NOTIFY_SYSTEM_POST_RESET || + info->intcode == NOTIFY_SYSTEM_POST_NEW) + { + fCurrExportedAge[0] = 0; + } +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Room Component +// +// + +class plRoomComponent : public plComponent +{ +public: + plRoomComponent(); + + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary. +OBSOLETE_CLASS_DESC(plRoomComponent, gRoomDesc, "Location", "Location", COMP_TYPE_MISC, ROOM_CID) + +enum +{ + kLocRoom, + kLocAge, + kLocDistrict, + kLocSeqNumber +}; + +//Max paramblock2 stuff below. +ParamBlockDesc2 gRoomCompBk +( + 1, _T("Location"), 0, &gRoomDesc, P_AUTO_CONSTRUCT+ P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_ROOM, IDS_COMP_ROOMS, 0, 0, NULL, + + // params + kLocAge, _T("Age"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_COMP_ROOM_AGE_TEXTBOX, + end, + + kLocDistrict, _T("District"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_COMP_ROOM_DISTRICT_TEXTBOX, + end, + + kLocRoom, _T("Room"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_COMP_ROOM_ROOM_TEXTBOX, + end, + + kLocSeqNumber, _T("seqNumber"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, IDC_ROOM_SEQEDIT, IDC_ROOM_SEQSPIN, SPIN_AUTOSCALE, + end, + + end +); + +plRoomComponent::plRoomComponent() +{ + fClassDesc = &gRoomDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plRoomComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// View Facing Component +// +// + +//Class that accesses the paramblock below. +class plViewFacingComponent : public plComponent +{ +public: + plViewFacingComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plViewFacingComponent, gViewFacingDesc, "Billboard", "Billboard", COMP_TYPE_GRAPHICS, Class_ID(0x7fab4d1f, 0x30f95438)) + +// +// Block not necessary, kept for backwards compat. +// +enum +{ + kTypeofView, kViewFaceScaleX, kViewFaceScaleY, kViewFaceScaleZ +}; + +ParamBlockDesc2 gViewFacingBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + 1, _T("View Facing"), 0, &gViewFacingDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, +#if 0 + IDD_COMP_VIEWFACE, IDS_COMP_VIEWFACES, 0, 0, NULL, + + kTypeofView, _T("ViewType"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_RADIO_VF1, IDC_RADIO_VF2, IDC_RADIO_VF3, IDC_RADIO_VF4, + end, + + kViewFaceScaleX, _T("ViewFaceScaleX"), TYPE_FLOAT, 0, 0, + p_default, 0.0f, + p_range, 0.0, 1500.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_VFSCALE_EDIT1, IDC_COMP_VFSCALE_SPIN1, 0.1f, + end, + + kViewFaceScaleY, _T("ViewFaceScaleY"), TYPE_FLOAT, 0, 0, + p_default, 0.0f, + p_range, 0.0, 1500.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_VFSCALE_EDIT2, IDC_COMP_VFSCALE_SPIN2, 0.1f, + end, + + kViewFaceScaleZ, _T("ViewFaceScaleZ"), TYPE_FLOAT, 0, 0, + p_default, 0.0f, + p_range, 0.0, 1500.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_VFSCALE_EDIT3, IDC_COMP_VFSCALE_SPIN3, 0.1f, + end, +#endif + end +); + + + + + + +plViewFacingComponent::plViewFacingComponent() +{ + fClassDesc = &gViewFacingDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +static hsBool NodeHasTMAnimation(plMaxNode* node) +{ + if( node->GetUnBounded() ) + return true; + + plPhysicalProps* props = node->GetPhysicalProps(); + if( props && props->IsUsed() ) + { + if( (props->GetMass() > 0) && !props->GetPinned() ) + return true; + } + + return node->GetTMController() && node->GetTMController()->IsAnimated(); +} + +static hsBool FindTMAnimatedChildrenRecur(plMaxNode* node) +{ + if( !node->CanConvert() ) + return false; + + if( NodeHasTMAnimation(node) ) + return true; + + int i; + for( i = 0; i < node->NumChildren(); i++ ) + { + if( FindTMAnimatedChildrenRecur((plMaxNode*)node->GetChildNode(i)) ) + return true; + } + return false; +} + +static void FindRecursiveBounds(plMaxNode* node, hsBounds3Ext& bnd) +{ + if( !node->CanConvert() ) + return; + + int i; + for( i = 0; i < node->NumChildren(); i++ ) + { + FindRecursiveBounds((plMaxNode*)node->GetChildNode(i), bnd); + } + + const TimeValue currTime(0); + + Object *obj = node->EvalWorldState(currTime).obj; + if( !obj ) + return; + + if( obj->CanConvertToType(triObjectClassID) ) + { + TriObject *meshObj = (TriObject *)obj->ConvertToType(currTime, triObjectClassID); + if( !meshObj ) + return; + + Matrix3 l2w = node->GetObjectTM(currTime); + Box3 box = meshObj->mesh.getBoundingBox(&l2w); + + if( !box.IsEmpty() ) + { + bnd.Union(&hsPoint3(box.Min().x, box.Min().y, box.Min().z)); + bnd.Union(&hsPoint3(box.Max().x, box.Max().y, box.Max().z)); + } + + if( meshObj != obj ) + meshObj->DeleteThis(); + } + return; +} + +static hsBool FindMaxBounds(plMaxNode* node, hsBounds3Ext& bnd) +{ + bnd.MakeEmpty(); + + // First, look from the node up to the root. If anything is animated, we can't do this. + plMaxNode* parent = node; + while( !parent->IsRootNode() ) + { + // We shouldn't ever hit this, but whatever. + if( !parent->CanConvert() ) + return false; + + if( NodeHasTMAnimation(parent) ) + return false; + parent = (plMaxNode*)parent->GetParentNode(); + } + + // Second, look down the children. If any of them are animated, we can't do this. + if( FindTMAnimatedChildrenRecur(node) ) + return false; + + // Now find the recursive world space bounds of us and all our children. + FindRecursiveBounds(node, bnd); + + // Translate to local about our pivot + hsMatrix44 l2w = node->GetLocalToWorld44(); + + // Expand them to be symmetric about local origin. + bnd.MakeSymmetric(&l2w.GetTranslate()); + + return true; +} + +hsBool plViewFacingComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + + plViewFaceModifier* pMod = TRACKED_NEW plViewFaceModifier; + + hsBounds3Ext maxBnd; + if( FindMaxBounds(node, maxBnd) ) + pMod->SetMaxBounds(maxBnd); + +// int ChosenType = fCompPB->GetInt(kTypeofView); +// switch(ChosenType) +// { +// case 0: +// pMod->SetFlag(plViewFaceModifier::kPivotFace); +// break; +// case 1: +// pMod->SetFlag(plViewFaceModifier::kPivotFavorY); +// break; +// case 2: + pMod->SetFlag(plViewFaceModifier::kPivotY); +// break; +// case 3: +// pMod->SetFlag(plViewFaceModifier::kPivotTumble); +// break; +// } + +#if 0 + if(fCompPB->GetFloat(kViewFaceScaleX) || fCompPB->GetFloat(kViewFaceScaleY) || fCompPB->GetFloat(kViewFaceScaleZ)) + { + pMod->SetFlag(plViewFaceModifier::kScale); + + hsVector3 scale; + scale.Set(1.f, 1.f, 1.f); + scale.fX = fCompPB->GetFloat(kViewFaceScaleX); + scale.fY = fCompPB->GetFloat(kViewFaceScaleY); + scale.fZ = fCompPB->GetFloat(kViewFaceScaleZ); + pMod->SetScale(scale); + } +#endif + pMod->SetOrigTransform(node->GetLocalToParent44(), node->GetParentToLocal44()); + node->AddModifier(pMod, IGetUniqueName(node)); + return true; +} + +hsBool plViewFacingComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + pNode->SetForceLocal(true); + pNode->SetMovable(true); + return true; +} + + + + + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Sprite Component +// +// + +//Class that accesses the paramblock below. +class plSpriteComponent : public plComponent +{ +public: + plSpriteComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plSpriteComponent, gSpriteDesc, "Sprite", "Sprite", COMP_TYPE_GRAPHICS, Class_ID(0x1e18192b, 0x312f579b)) + +// +// Block not necessary, kept for backwards compat. +// +ParamBlockDesc2 gSpriteBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + 1, _T("Sprite"), 0, &gSpriteDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + + end +); + +plSpriteComponent::plSpriteComponent() +{ + fClassDesc = &gSpriteDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plSpriteComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plViewFaceModifier* pMod = TRACKED_NEW plViewFaceModifier; + + hsBounds3Ext maxBnd; + if( FindMaxBounds(node, maxBnd) ) + pMod->SetMaxBounds(maxBnd); + + pMod->SetFlag(plViewFaceModifier::kPivotFace); + pMod->SetOrigTransform(node->GetLocalToParent44(), node->GetParentToLocal44()); + node->AddModifier(pMod, IGetUniqueName(node)); + return true; +} + +hsBool plSpriteComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + pNode->SetForceLocal(true); + pNode->SetMovable(true); + return true; +} + + + + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Occlusion Component +// +// + + +enum { + kOccTwoSidedChekbox + }; + + +class plOcclusionComponentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + } + return true; + +////////////////// + case WM_COMMAND: + { + if (LOWORD(wParam) == IDC_COMP_OCCLUSION_CKBX) + { + return true; + } + } + + } + + return false; + } + void DeleteThis() {} +}; +static plOcclusionComponentProc gOccProc; + +//Class that accesses the paramblock below. +class plOcclusionComponent : public plComponent +{ +public: + plOcclusionComponent(); + + virtual hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + +CLASS_DESC(plOcclusionComponent, gOcclusionDesc, "Occlusion", "Occlusion", COMP_TYPE_GRAPHICS, Class_ID(0x18c454df, 0x1ecd40f5)) + +ParamBlockDesc2 gOcclusionBk +( + plComponent::kBlkComp, _T("Occlusion"), 0, &gOcclusionDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_OCCLUSION, IDS_COMP_OCCLUSIONS, 0, 0, &gOccProc, + + kOccTwoSidedChekbox, _T("TwoSided"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_OCCLUSION_CKBX, + end, + + end +); + +plOcclusionComponent::plOcclusionComponent() +{ + fClassDesc = &gOcclusionDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plOcclusionComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plOcclusionComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + hsBool twoSided = fCompPB->GetInt(kOccTwoSidedChekbox); + hsBool isHole = false; + return node->ConvertToOccluder(pErrMsg, twoSided, isHole); +} + +hsBool plOcclusionComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + pNode->SetDrawable(false); + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// CamView Component +// +// + +//Class that accesses the paramblock below. +class plCamViewComponent : public plComponent +{ + hsBool fBogus; + + void IMakeEveryoneOpaqueRecur(plMaxNode* node); + void IMakeEveryoneOpaque(plMaxNode* node); +public: + plCamViewComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plCamViewComponent, gCamViewDesc, "Camera View", "CamView", COMP_TYPE_GRAPHICS, Class_ID(0x5e9f0243, 0xe7f2c08)) + +enum +{ + kWhateverCheckBox +}; + +ParamBlockDesc2 gCamViewBk +( + plComponent::kBlkComp, _T("CamView"), 0, &gCamViewDesc, P_AUTO_CONSTRUCT/* + P_AUTO_UI*/, plComponent::kRefComp, + +// IDD_COMP_CAMVIEW, IDS_COMP_CAMVIEWS, 0, 0, NULL, + + end +); + +plCamViewComponent::plCamViewComponent() +: fBogus(false) +{ + fClassDesc = &gCamViewDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plCamViewComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + TimeValue timeVal(0); + + Object* obj = node->EvalWorldState(timeVal).obj; + + GenCamera* cam = nil; + if( obj->CanConvertToType(Class_ID(LOOKAT_CAM_CLASS_ID, 0)) ) + cam = (GenCamera *) obj->ConvertToType(timeVal, Class_ID(LOOKAT_CAM_CLASS_ID, 0)); + else + if( obj->CanConvertToType(Class_ID(SIMPLE_CAM_CLASS_ID, 0)) ) + cam = (GenCamera *) obj->ConvertToType(timeVal, Class_ID(SIMPLE_CAM_CLASS_ID, 0)); + + if( !cam ) + { + hsAssert(false, "Should have checked for the camera in PreConvert"); + return false; + } + + plPostEffectMod* mod = TRACKED_NEW plPostEffectMod; + + float hither = cam->GetEnvRange(timeVal, ENV_NEAR_RANGE); + if( hither < 0.5f ) + hither = 0.5f; + float yon = cam->GetEnvRange(timeVal, ENV_FAR_RANGE); + mod->SetHither(hither); + mod->SetYon(yon); + + // radians + float fov = cam->GetFOV(timeVal); + // convert + int FOVType = cam->GetFOVType(); + hsScalar fovX, fovY; + switch(FOVType) + { + case 0: // FOV_W + { + fovX = fov; + fovY = fovX *3.f / 4.f; + } + break; + case 1: // FOV_H + { + fovY = fov; + fovX = fovY * 4.f / 3.f; + } + break; + } + fovX *= 180.f / hsScalarPI; + fovY *= 180.f / hsScalarPI; + mod->SetFovX(fovX); + mod->SetFovY(fovY); + + plKey sceneNodeKey = node->GetRoomKey(); + + mod->SetNodeKey(sceneNodeKey); + + node->AddModifier(mod, IGetUniqueName(node)); + + return true; +} + +hsBool plCamViewComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + TimeValue timeVal(0); + Object* obj = node->EvalWorldState(timeVal).obj; + + if( obj->CanConvertToType(Class_ID(LOOKAT_CAM_CLASS_ID, 0)) + || obj->CanConvertToType(Class_ID(SIMPLE_CAM_CLASS_ID, 0)) ) + fBogus = false; + else + fBogus = true; + + if( fBogus ) + pErrMsg->Set(true, node->GetName(), "CamView component attached to non-camera").CheckAndAsk(); + + if( !fBogus ) + IMakeEveryoneOpaque(node); + + return true; +} + +void plCamViewComponent::IMakeEveryoneOpaque(plMaxNode* node) +{ + plMaxNode* root = (plMaxNode *)node->GetInterface()->GetRootNode(); + + int i; + for( i = 0; i < root->NumberOfChildren(); i++ ) + IMakeEveryoneOpaqueRecur((plMaxNode*)(root->GetChildNode(i))); + +} + +void plCamViewComponent::IMakeEveryoneOpaqueRecur(plMaxNode* node) +{ + if( node->CanConvert() ) + { + node->SetNoSpanReSort(true); + node->SetNoSpanSort(true); + + int i; + for( i = 0; i < node->NumberOfChildren(); i++ ) + { + IMakeEveryoneOpaqueRecur((plMaxNode *)(node->GetChildNode(i))); + } + } +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plCamViewComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetForceLocal(true); + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Follow Component +// +// + +enum +{ + kAffectX, + kLeaderTypeRadio, + kLeaderObjectSel, + kAffectY, + kAffectZ, + kAffectRotate +}; + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plLeaderObjAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if( (id == kLeaderObjectSel) ) + { + plComponentBase *comp = (plComponentBase*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +plLeaderObjAccessor gLeaderObjAccessor; + +class plFollowComponentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + map->SetTooltip(kLeaderObjectSel, TRUE, "Press the button, & select the object to follow in one of the Viewports" ); + if( pb->GetInt(kLeaderTypeRadio) == Int32(plFollowMod::kObject) ) + map->Enable(kLeaderObjectSel, TRUE); + else + map->Enable(kLeaderObjectSel, FALSE); + } + return true; + +////////////////// + case WM_COMMAND: + { + if( (LOWORD(wParam) == IDC_F_RADIO_PLAYER) + ||(LOWORD(wParam) == IDC_F_RADIO_LISTENER) + || (LOWORD(wParam) == IDC_F_RADIO_CAMERA) + || (LOWORD(wParam) == IDC_F_RADIO_OBJECT) ) + { + IParamBlock2 *pb = map->GetParamBlock(); + if( pb->GetInt(kLeaderTypeRadio) == Int32(plFollowMod::kObject) ) + map->Enable(kLeaderObjectSel, TRUE); + else + map->Enable(kLeaderObjectSel, FALSE); + + return true; + } + } + + } + + return false; + } + void DeleteThis() {} +}; +static plFollowComponentProc gFollowProc; + +//Class that accesses the paramblock below. +class plFollowComponent : public plComponent +{ +private: + hsBool fValid; + + plFollowMod* IMakeFollowMod(plMaxNode* pNode, plErrorMsg* pErrMsg); + +public: + plFollowComponent(); + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + + +CLASS_DESC(plFollowComponent, gFollowDesc, "Follow", "Follow", COMP_TYPE_GRAPHICS, Class_ID(0x44262418, 0x73ee145b)) + + + +ParamBlockDesc2 gFollowBk +( + plComponent::kBlkComp, _T("Follow"), 0, &gFollowDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FOLLOW, IDS_COMP_FOLLOWS, 0, 0, &gFollowProc, + + kAffectX, _T("X"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FOLLOW_X, + end, + + kAffectY, _T("Y"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FOLLOW_Y, + end, + + kAffectZ, _T("Z"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FOLLOW_Z, + end, + + kAffectRotate, _T("Rotate"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FOLLOW_ROTATE, + end, + + kLeaderTypeRadio, _T("LeaderType"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_F_RADIO_PLAYER, IDC_F_RADIO_CAMERA, IDC_F_RADIO_LISTENER, IDC_F_RADIO_OBJECT, + p_vals, plFollowMod::kLocalPlayer, plFollowMod::kCamera, plFollowMod::kListener, plFollowMod::kObject, + p_default, plFollowMod::kLocalPlayer, + end, + + kLeaderObjectSel, _T("ObjectChoice"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_FOLLOW_CHOOSE_OBJECT, + p_prompt, IDS_COMP_LINE_CHOSE_OBJECT, + p_accessor, &gLeaderObjAccessor, + end, + + end + +); + +plFollowComponent::plFollowComponent() +: fValid(false) +{ + fClassDesc = &gFollowDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plFollowMod* plFollowComponent::IMakeFollowMod(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + plFollowMod::FollowLeaderType lType = plFollowMod::FollowLeaderType(fCompPB->GetInt(kLeaderTypeRadio)); + + plFollowMod* follow = TRACKED_NEW plFollowMod; + + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), follow, pNode->GetLocation()); + + if( plFollowMod::kObject == lType ) + { + if(fCompPB->GetINode(kLeaderObjectSel) != NULL) + { + plMaxNode* targNode = (plMaxNode*)fCompPB->GetINode(kLeaderObjectSel); + + if( targNode->CanConvert() ) + { + plSceneObject* targObj = targNode->GetSceneObject(); + if( targObj ) + { + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(follow->GetKey(), plRefMsg::kOnCreate, 0, plFollowMod::kRefLeader); + hsgResMgr::ResMgr()->AddViaNotify(targObj->GetKey(), refMsg, plRefFlags::kPassiveRef); + + follow->SetType(plFollowMod::kObject); + } + } + } + } + else + { + follow->SetType(lType); + } + + UInt32 mode = 0; + if( fCompPB->GetInt(kAffectX) ) + mode |= plFollowMod::kPositionX; + if( fCompPB->GetInt(kAffectY) ) + mode |= plFollowMod::kPositionY; + if( fCompPB->GetInt(kAffectZ) ) + mode |= plFollowMod::kPositionZ; + if( fCompPB->GetInt(kAffectRotate) ) + mode |= plFollowMod::kRotate; + + if( !mode ) + mode = plFollowMod::kFullTransform; + + follow->SetMode(mode); + + return follow; +} + +hsBool plFollowComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if( !fValid ) + return true; + + plFollowMod* follow = IMakeFollowMod(node, pErrMsg); + + if( follow ) + node->AddModifier(follow, IGetUniqueName(node)); + + return true; +} + +hsBool plFollowComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + fValid = false; + + if( plFollowMod::kObject == fCompPB->GetInt(kLeaderTypeRadio) ) + { + if( !fCompPB->GetINode(kLeaderObjectSel) ) + { + return true; + } + } + fValid = true; + pNode->SetForceLocal(true); + pNode->SetMovable(true); + return true; +} + +hsBool plFollowComponent::PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + if( !fValid ) + return true; + fValid = false; + + if( plFollowMod::kObject == fCompPB->GetInt(kLeaderTypeRadio) ) + { + plMaxNode* followNode = (plMaxNode*)fCompPB->GetINode(kLeaderObjectSel); + if( !followNode->CanConvert() ) + { + return true; + } + } + fValid = true; + + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Unleash Component +// +// + +//Class that accesses the paramblock below. +class plUnleashComponent : public plComponent +{ +public: + plUnleashComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plUnleashComponent, gUnleashDesc, "Unleash Satan", "UnleashSatan", COMP_TYPE_GRAPHICS, Class_ID(0x5d937fa8, 0x1001411a)) + +ParamBlockDesc2 gUnleashBk +( + plComponent::kBlkComp, _T("Unleash"), 0, &gUnleashDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_UNLEASH, IDS_COMP_UNLEASH, 0, 0, NULL, + + end +); + +plUnleashComponent::plUnleashComponent() +{ + fClassDesc = &gUnleashDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plUnleashComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + pNode->SetRunTimeLight(true); + + pNode->SetForcePreShade(true); + + return true; +} + +hsBool plUnleashComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// ForceRTLight Component +// +// + +//Class that accesses the paramblock below. +class plForceRTLightComponent : public plComponent +{ +public: + plForceRTLightComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + +//Max desc stuff necessary below. +CLASS_DESC(plForceRTLightComponent, gForceRTLightDesc, "Force RT Light", "ForceRTLight", COMP_TYPE_GRAPHICS, Class_ID(0x1485091b, 0x42852fb5)) + +ParamBlockDesc2 gForceRTLightBk +( + plComponent::kBlkComp, _T("ForceRTLight"), 0, &gForceRTLightDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FORCE_RTLIGHT, IDS_COMP_FORCE_RTLIGHT, 0, 0, NULL, + + end +); + +plForceRTLightComponent::plForceRTLightComponent() +{ + fClassDesc = &gForceRTLightDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plForceRTLightComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + pNode->SetRunTimeLight(true); + pNode->SetNoPreShade(true); + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Terrain Optimise Component (currently just dices). +// +// +//Class that accesses the paramblock below. +class plGeoDiceComponent : public plComponent +{ +public: + enum { + kActive, + kMaxFaces, + kMaxSize, + kMinFaces, + kOverride + }; +public: + plGeoDiceComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + +static const int kDefMaxFaces(1000); +static const float kDefMaxSize(250.f); +static const int kDefMinFaces(300); + +class plGeoDiceComponentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + if( !pb->GetInt(plGeoDiceComponent::kOverride) ) + { + pb->SetValue(plGeoDiceComponent::kMaxFaces, t, kDefMaxFaces); + pb->SetValue(plGeoDiceComponent::kMaxSize, t, kDefMaxSize); + pb->SetValue(plGeoDiceComponent::kMinFaces, t, kDefMinFaces); + + map->Enable(plGeoDiceComponent::kMaxFaces, FALSE); + map->Enable(plGeoDiceComponent::kMaxSize, FALSE); + map->Enable(plGeoDiceComponent::kMinFaces, FALSE); + } + else + { + map->Enable(plGeoDiceComponent::kMaxFaces, TRUE); + map->Enable(plGeoDiceComponent::kMaxSize, TRUE); + map->Enable(plGeoDiceComponent::kMinFaces, TRUE); + } + } + return true; + +////////////////// + case WM_COMMAND: + { + if( LOWORD(wParam) == IDC_COMP_GEO_DICE_OVERRIDE ) + { + IParamBlock2 *pb = map->GetParamBlock(); + if( !pb->GetInt(plGeoDiceComponent::kOverride) ) + { + pb->SetValue(plGeoDiceComponent::kMaxFaces, t, kDefMaxFaces); + pb->SetValue(plGeoDiceComponent::kMaxSize, t, kDefMaxSize); + pb->SetValue(plGeoDiceComponent::kMinFaces, t, kDefMinFaces); + + map->Enable(plGeoDiceComponent::kMaxFaces, FALSE); + map->Enable(plGeoDiceComponent::kMaxSize, FALSE); + map->Enable(plGeoDiceComponent::kMinFaces, FALSE); + } + else + { + map->Enable(plGeoDiceComponent::kMaxFaces, TRUE); + map->Enable(plGeoDiceComponent::kMaxSize, TRUE); + map->Enable(plGeoDiceComponent::kMinFaces, TRUE); + } + + return true; + } + } + + } + + return false; + } + void DeleteThis() {} +}; +static plGeoDiceComponentProc gGeoDiceProc; + + +//Max desc stuff necessary below. +CLASS_DESC(plGeoDiceComponent, gGeoDiceDesc, "Optimize Terrain", "OptimizeTerrain", COMP_TYPE_GRAPHICS, Class_ID(0x6f7a5713, 0x19595142)) + +ParamBlockDesc2 gGeoDiceBk +( + plComponent::kBlkComp, _T("GeoDice"), 0, &gGeoDiceDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_GEO_DICE, IDS_COMP_GEO_DICE, 0, 0, &gGeoDiceProc, + + plGeoDiceComponent::kActive, _T("Active"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_GEO_DICE_ACTIVE, + end, + + plGeoDiceComponent::kMaxFaces, _T("MaxFaces"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_COMP_GEO_DICE_MAXFACES, IDC_COMP_GEO_DICE_MAXFACES_SPIN, 1.f, + p_default, 1000, + p_range, 10, 10000, + end, + + plGeoDiceComponent::kMaxSize, _T("MaxSize"), TYPE_FLOAT, 0, 0, + p_default, 100.0f, + p_range, 0.0, 10000.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_GEO_DICE_MAXSIZE, IDC_COMP_GEO_DICE_MAXSIZE_SPIN, 0.1f, + end, + + plGeoDiceComponent::kMinFaces, _T("MinFaces"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_COMP_GEO_DICE_MINFACES, IDC_COMP_GEO_DICE_MINFACES_SPIN, 1.f, + p_default, 300, + p_range, 0, 5000, + end, + + plGeoDiceComponent::kOverride, _T("Override"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_GEO_DICE_OVERRIDE, + end, + + end +); + +plGeoDiceComponent::plGeoDiceComponent() +{ + fClassDesc = &gGeoDiceDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plGeoDiceComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + if( fCompPB->GetInt(kActive) ) + { + pNode->SetGeoDice(true, fCompPB->GetInt(kMaxFaces), fCompPB->GetFloat(kMaxSize), fCompPB->GetInt(kMinFaces)); + } + return true; +} + + +/// +/// +/// reference point component +/// put this on a dummy for a handy reference point you can use in python +/// +/// + +class plReferencePointComponent : public plComponent +{ +public: + plReferencePointComponent(); + + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); +}; + +CLASS_DESC(plReferencePointComponent, gReferencePointDesc, "Reference Point", "RefPoint", COMP_TYPE_MISC, Class_ID(0x3c9c6f71, 0x5774fc5)) + +//Max paramblock2 stuff below. +ParamBlockDesc2 gReferencePointBk +( + 1, _T("reference"), 0, &gReferencePointDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + + end +); + +plReferencePointComponent::plReferencePointComponent() +{ + fClassDesc = &gReferencePointDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plReferencePointComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + // all we need is a coordinate interface... + pNode->SetForceLocal(true); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// + +#include "../pnNetCommon/plSDLTypes.h" +#include "../MaxConvert/hsMaterialConverter.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" +#include "plCreatableIndex.h" + +class plNetSyncComponent : public plComponent +{ +protected: + void ISetNetSync(plSynchedObject* so); + void ISetSDLType(plSynchedObject* so, int radioVal, const char* sdlName); + void ISetMtl(hsGMaterial* mtl); + +public: + plNetSyncComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* errMsg); + virtual hsBool Convert(plMaxNode* node, plErrorMsg* errMsg) { return true; } + virtual hsBool DeInit(plMaxNode* node, plErrorMsg* errMsg); +}; + +CLASS_DESC(plNetSyncComponent, gNetSyncDesc, "Net Sync", "NetSync", COMP_TYPE_MISC, Class_ID(0x4d1b2d6f, 0x28fe08db)) + +enum +{ + kNetSyncLocalOnly, + kNetSyncOverride, + kNetSyncPhys, + kNetSyncAnim, + kNetSyncSnd, + kNetSyncMat, + kNetSyncResp, + kNetSyncXReg, +}; + +enum +{ + kNetSyncRadioAllow, + kNetSyncRadioNoSave, + kNetSyncRadioDeny, +}; + +class plNetSyncComponentProc : public ParamMap2UserDlgProc +{ +protected: + void IEnableSDL(IParamMap2* map, bool enable) + { + map->Enable(kNetSyncPhys, enable); + map->Enable(kNetSyncAnim, enable); + map->Enable(kNetSyncSnd, enable); + map->Enable(kNetSyncMat, enable); + map->Enable(kNetSyncResp, enable); + map->Enable(kNetSyncXReg, enable); + } + + void IEnableCtrls(IParamMap2* map) + { + IParamBlock2* pb = map->GetParamBlock(); + if (pb->GetInt(kNetSyncLocalOnly)) + { + map->Enable(kNetSyncOverride, FALSE); + IEnableSDL(map, false); + } + else if (pb->GetInt(kNetSyncOverride)) + { + map->Enable(kNetSyncLocalOnly, FALSE); + IEnableSDL(map, true); + } + else + { + map->Enable(kNetSyncOverride, TRUE); + map->Enable(kNetSyncLocalOnly, TRUE); + IEnableSDL(map, false); + } + } + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + IEnableCtrls(map); + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDC_LOCAL_ONLY_CHECK || + LOWORD(wParam) == IDC_OVERRIDE_CHECK) + { + IEnableCtrls(map); + return TRUE; + } + break; + } + + return FALSE; + } + void DeleteThis() {} +}; +static plNetSyncComponentProc gNetSyncProc; + +//Max paramblock2 stuff below. +ParamBlockDesc2 gHighSDLBk +( + plComponent::kBlkComp, _T("NetSync"), 0, &gNetSyncDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_NETSYNC, IDS_COMP_NETSYNC, 0, 0, &gNetSyncProc, + + kNetSyncLocalOnly, _T("LocalOnly"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_LOCAL_ONLY_CHECK, + end, + + kNetSyncOverride, _T("Override"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_OVERRIDE_CHECK, + end, + + kNetSyncPhys, _T("PhysSDL"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_PHYS_ALLOW_RADIO, IDC_PHYS_NOSAVE_RADIO, IDC_PHYS_DENY_RADIO, + p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny, + p_default, kNetSyncRadioAllow, + end, + + kNetSyncAnim, _T("AnimSDL"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_ANIM_ALLOW_RADIO, IDC_ANIM_NOSAVE_RADIO, IDC_ANIM_DENY_RADIO, + p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny, + p_default, kNetSyncRadioAllow, + end, + + kNetSyncSnd, _T("SndSDL"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_SND_ALLOW_RADIO, IDC_SND_NOSAVE_RADIO, IDC_SND_DENY_RADIO, + p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny, + p_default, kNetSyncRadioAllow, + end, + + kNetSyncMat, _T("MatSDL"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_MAT_ALLOW_RADIO, IDC_MAT_NOSAVE_RADIO, IDC_MAT_DENY_RADIO, + p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny, + p_default, kNetSyncRadioAllow, + end, + + kNetSyncResp, _T("RespSDL"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_RESP_ALLOW_RADIO, IDC_RESP_NOSAVE_RADIO, IDC_RESP_DENY_RADIO, + p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny, + p_default, kNetSyncRadioAllow, + end, + + kNetSyncXReg, _T("XRegSDL"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_XREG_ALLOW_RADIO, IDC_XREG_NOSAVE_RADIO, IDC_XREG_DENY_RADIO, + p_vals, kNetSyncRadioAllow, kNetSyncRadioNoSave, kNetSyncRadioDeny, + p_default, kNetSyncRadioAllow, + end, + + end +); + +plNetSyncComponent::plNetSyncComponent() +{ + fClassDesc = &gNetSyncDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plNetSyncComponent::SetupProperties(plMaxNode* node, plErrorMsg* errMsg) +{ + // make all sdl types on this object Volatile + bool override = (fCompPB->GetInt(kNetSyncOverride) != 0); + node->SetOverrideHighLevelSDL(override); + + return true; +} + +void plNetSyncComponent::ISetSDLType(plSynchedObject* so, int radioVal, const char* sdlName) +{ + switch (radioVal) + { + case kNetSyncRadioNoSave: + so->AddToSDLVolatileList(sdlName); // make volatile a type of persistence + break; + + case kNetSyncRadioDeny: + so->AddToSDLExcludeList(sdlName); // disable a type of persistence + break; + } +} + +void plNetSyncComponent::ISetNetSync(plSynchedObject* so) +{ + // For local only, disable everything and exit + bool localOnly = (fCompPB->GetInt(kNetSyncLocalOnly) != 0); + if (localOnly) + { + so->SetLocalOnly(true); + return; + } + + int physVal = fCompPB->GetInt(kNetSyncPhys); + int animVal = fCompPB->GetInt(kNetSyncAnim); + int sndVal = fCompPB->GetInt(kNetSyncSnd); + int matVal = fCompPB->GetInt(kNetSyncMat); + int respVal = fCompPB->GetInt(kNetSyncResp); + int xregVal = fCompPB->GetInt(kNetSyncXReg); + + // If all are not saved, use that optimization + if ( + physVal == kNetSyncRadioNoSave && + animVal == kNetSyncRadioNoSave && + sndVal == kNetSyncRadioNoSave && + matVal == kNetSyncRadioNoSave && + respVal == kNetSyncRadioNoSave && + xregVal == kNetSyncRadioNoSave + ) + { + so->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile); + } + // If all are volatile, use that optimization + else if ( + physVal == kNetSyncRadioDeny && + animVal == kNetSyncRadioDeny && + sndVal == kNetSyncRadioDeny && + matVal == kNetSyncRadioDeny && + respVal == kNetSyncRadioDeny && + xregVal == kNetSyncRadioDeny + ) + { + so->SetSynchFlagsBit(plSynchedObject::kExcludeAllPersistentState); + } + // If it's a mix, we need to set them individually + else + { + ISetSDLType(so, physVal, kSDLPhysical); + ISetSDLType(so, animVal, kSDLAGMaster); + ISetSDLType(so, sndVal, kSDLSound); + ISetSDLType(so, matVal, kSDLLayer); + ISetSDLType(so, respVal, kSDLResponder); + ISetSDLType(so, xregVal, kSDLXRegion); + } +} + +void plNetSyncComponent::ISetMtl(hsGMaterial* mtl) +{ + for (int i = 0; i < mtl->GetNumLayers(); i++) + { + plLayerInterface* layer = mtl->GetLayer(i); + while (layer) + { + if (layer->ClassIndex() == CLASS_INDEX_SCOPED(plLayerAnimation) || + layer->ClassIndex() == CLASS_INDEX_SCOPED(plLayerSDLAnimation)) + { + ISetNetSync(layer); + } + + layer = layer->GetAttached(); + } + } +} + +// We're cheating and using DeInit as an extra pass, since everything should be done at that point +hsBool plNetSyncComponent::DeInit(plMaxNode* node, plErrorMsg* errMsg) +{ + plSceneObject* so = node->GetSceneObject(); + if (!so) + return false; + + // Set sync options on the sceneobject + ISetNetSync(so); + + // Then on the textures... + Mtl* maxMaterial = hsMaterialConverter::Instance().GetBaseMtl(node); + if (maxMaterial) + { + hsTArray matArray; + + // Get the textures from the material converter + if (hsMaterialConverter::Instance().IsMultiMat(maxMaterial)) + { + int numMaterials = maxMaterial->NumSubMtls(); + for (int i = 0; i < numMaterials; i++) + hsMaterialConverter::Instance().GetMaterialArray(maxMaterial->GetSubMtl(i), node, matArray, i); + } + else + hsMaterialConverter::Instance().GetMaterialArray(maxMaterial, node, matArray); + + // Set sync on the textures we found + for (int i = 0; i < matArray.GetCount(); i++) + { + hsGMaterial* mtl = matArray[i]; + if (mtl) + ISetMtl(mtl); + } + } + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Image Lib Component +// +// + +class pfImageLibProc : public ParamMap2UserDlgProc +{ +protected: + + void IRefreshImageList( HWND hDlg, pfImageLibComponent *comp ) + { + HWND ctrl = GetDlgItem( hDlg, IDC_IMAGE_LIST ); + HDC dc = GetDC( ctrl ); + + LONG maxWidth = 0; + SendMessage( ctrl, LB_RESETCONTENT, 0, 0 ); + + for( int i = 0; i < comp->GetNumBitmaps(); i++ ) + { + plLayerTex *layer = comp->GetBitmap( i ); + if( layer != nil ) + { + const char *str = layer->GetPBBitmap()->bi.Filename(); + int idx = SendMessage( ctrl, LB_ADDSTRING, 0, (LPARAM)str ); + SendMessage( ctrl, LB_SETITEMDATA, (WPARAM)idx, (LPARAM)i ); + + SIZE strSize; + GetTextExtentPoint32( dc, str, strlen(str), &strSize ); + if( strSize.cx > maxWidth ) + maxWidth = strSize.cx; + } + } + SendMessage( ctrl, LB_SETHORIZONTALEXTENT, (WPARAM)maxWidth, NULL ); + ReleaseDC( ctrl, dc ); + + EnableWindow( GetDlgItem( hDlg, IDC_IMAGE_EDIT ), false ); + EnableWindow( GetDlgItem( hDlg, IDC_IMAGE_REMOVE ), false ); + + CheckDlgButton(hDlg, IDC_IL_COMPRESS, BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_IL_FORCEPOW2, BST_UNCHECKED); + EnableWindow(GetDlgItem(hDlg, IDC_IL_COMPRESS), FALSE); + EnableWindow(GetDlgItem(hDlg, IDC_IL_FORCEPOW2), FALSE); + } + +public: + + void DeleteThis() {} + +// virtual void Update( TimeValue t, Interval &valid, IParamMap2 *map ); + + BOOL DlgProc( TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) + { + IParamBlock2 *pb = pmap->GetParamBlock(); + pfImageLibComponent *comp = (pfImageLibComponent *)pb->GetOwner(); + + + switch( msg ) + { + case WM_INITDIALOG: + // Fill our list with bitmap filenames + comp->Validate(); + IRefreshImageList( hWnd, comp ); + return true; + + case WM_DESTROY: + break; + + case WM_COMMAND: + if( LOWORD( wParam ) == IDC_IMAGE_ADD ) + { + plLayerTex *newLayer = TRACKED_NEW plLayerTex; + + if( newLayer->HandleBitmapSelection() ) + { + comp->AppendBitmap( newLayer ); + IRefreshImageList( hWnd, comp ); + } + } + else if( LOWORD( wParam ) == IDC_IMAGE_EDIT ) + { + int idx = SendDlgItemMessage( hWnd, IDC_IMAGE_LIST, LB_GETCURSEL, 0, 0 ); + if( idx != LB_ERR ) + { + idx = SendDlgItemMessage( hWnd, IDC_IMAGE_LIST, LB_GETITEMDATA, (WPARAM)idx, 0 ); + plLayerTex *layer = comp->GetBitmap( idx ); + if( layer != nil && layer->HandleBitmapSelection() ) + { + IRefreshImageList( hWnd, comp ); + } + } + } + else if( LOWORD( wParam ) == IDC_IMAGE_REMOVE ) + { + int idx = SendDlgItemMessage( hWnd, IDC_IMAGE_LIST, LB_GETCURSEL, 0, 0 ); + if( idx != LB_ERR ) + { + idx = SendDlgItemMessage( hWnd, IDC_IMAGE_LIST, LB_GETITEMDATA, (WPARAM)idx, 0 ); + comp->RemoveBitmap( idx ); + IRefreshImageList( hWnd, comp ); + } + return false; + } + else if( LOWORD( wParam ) == IDC_IMAGE_LIST && HIWORD( wParam ) == LBN_SELCHANGE ) + { + int idx = SendDlgItemMessage( hWnd, IDC_IMAGE_LIST, LB_GETCURSEL, 0, 0 ); + EnableWindow( GetDlgItem( hWnd, IDC_IMAGE_EDIT ), idx != LB_ERR ); + EnableWindow( GetDlgItem( hWnd, IDC_IMAGE_REMOVE ), idx != LB_ERR ); + + EnableWindow(GetDlgItem(hWnd, IDC_IL_COMPRESS), TRUE); + CheckDlgButton(hWnd, IDC_IL_COMPRESS, comp->GetCompress(idx) ? BST_CHECKED : BST_UNCHECKED); + } + else if (LOWORD(wParam) == IDC_IL_COMPRESS && HIWORD(wParam) == BN_CLICKED) + { + bool checked = (IsDlgButtonChecked(hWnd, IDC_IL_COMPRESS) == BST_CHECKED); + + int sel = ListBox_GetCurSel(GetDlgItem(hWnd, IDC_IMAGE_LIST)); + comp->SetCompress(sel, checked); + } + break; + + } + return false; + } +}; +static pfImageLibProc gImageLibProc; + +//Max desc stuff necessary below. +CLASS_DESC(pfImageLibComponent, gImageLibDesc, "Image Library", "ImageLib", COMP_TYPE_MISC, IMAGE_LIB_CID ) + +ParamBlockDesc2 gImageLibBlock +( // KLUDGE: not the defined block ID, but kept for backwards compat. + 1, _T("Image Lib"), 0, &gImageLibDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_IMAGELIB, IDS_COMP_IMAGELIB, 0, 0, &gImageLibProc, + + // params + pfImageLibComponent::kRefImageList, _T("imageList"), TYPE_TEXMAP_TAB, 0, 0, 0, + end, + + pfImageLibComponent::kCompressImage, _T("compress"), TYPE_BOOL_TAB, 0, 0, 0, + p_default, 1, + end, + + end +); + +pfImageLibComponent::pfImageLibComponent() +{ + fClassDesc = &gImageLibDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +void pfImageLibComponent::Validate() +{ + if (fCompPB->Count(kCompressImage) != fCompPB->Count(kRefImageList)) + fCompPB->SetCount(kCompressImage, fCompPB->Count(kRefImageList)); +} + +int pfImageLibComponent::GetNumBitmaps( void ) const +{ + return fCompPB->Count( (ParamID)kRefImageList ); +} + +plLayerTex *pfImageLibComponent::GetBitmap( int idx ) +{ + // If we don't have one, create one + plLayerTex *layer = (plLayerTex *)fCompPB->GetTexmap( (ParamID)kRefImageList, 0, idx ); + if( layer == nil || layer->ClassID() != LAYER_TEX_CLASS_ID ) + { + layer = TRACKED_NEW plLayerTex; + fCompPB->SetValue( (ParamID)kRefImageList, 0, (Texmap *)layer, idx ); + } + + return layer; +} + +int pfImageLibComponent::AppendBitmap( plLayerTex *tex ) +{ + int idx = GetNumBitmaps(); + fCompPB->Resize( (ParamID)kRefImageList, idx + 1 ); + fCompPB->SetValue( (ParamID)kRefImageList, 0, (Texmap *)tex, idx ); + + fCompPB->Resize(kCompressImage, idx + 1); + + return idx; +} + +void pfImageLibComponent::RemoveBitmap( int idx ) +{ + fCompPB->Delete( (ParamID)kRefImageList, idx, 1 ); + fCompPB->Delete(kCompressImage, idx, 1); +} + +bool pfImageLibComponent::GetCompress(int idx) +{ + return (fCompPB->GetInt(kCompressImage, 0, idx) != 0); +} + +void pfImageLibComponent::SetCompress(int idx, bool compress) +{ + fCompPB->SetValue(kCompressImage, 0, compress, idx); +} + +hsBool pfImageLibComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + Validate(); + return true; +} + +hsBool pfImageLibComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plImageLibMod *lib = TRACKED_NEW plImageLibMod; + node->AddModifier( lib, IGetUniqueName(node) ); + + int i; + for( i = 0; i < GetNumBitmaps(); i++ ) + { + plLayerTex *layer = GetBitmap( i ); + if( layer != nil ) + { + PBBitmap *texture = layer->GetPBBitmap(); + if( texture != nil ) + { + UInt32 flags = plBitmap::kAlphaChannelFlag; + + plBitmap *bMap; + if (fCompPB->GetInt(kCompressImage, 0, i) == 0) + { + flags |= plBitmap::kForceNonCompressed; + bMap = plLayerConverter::Instance().CreateSimpleTexture( texture->bi.Name(), lib->GetKey()->GetUoid().GetLocation(), 0, flags ); + } + else // compress using JPEG compression scheme + bMap = plLayerConverter::Instance().CreateSimpleTexture( texture->bi.Name(), lib->GetKey()->GetUoid().GetLocation(), 0, flags, true ); + if( bMap != nil ) + { + hsgResMgr::ResMgr()->AddViaNotify( bMap->GetKey(), TRACKED_NEW plGenRefMsg( lib->GetKey(), + plRefMsg::kOnCreate, lib->GetNumImages(), plImageLibMod::kRefImage ), plRefFlags::kActiveRef ); + } + } + } + } + + return true; +} + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMiscComponents.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMiscComponents.h new file mode 100644 index 00000000..0bf9bb9f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMiscComponents.h @@ -0,0 +1,119 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMiscComponents_inc +#define plMiscComponents_inc + +#include "plComponent.h" +#include "Notify.h" + +#define ROOM_CID Class_ID(0x70a1570d, 0x472f5647) +#define PAGEINFO_CID Class_ID(0x54ee40f1, 0x4de45acc) +#define IGNORELITE_CID Class_ID(0x6abb5b4b, 0x6ba27af7) +#define RAIL_CAM_CID Class_ID(0x1b097048, 0xc94038b) +#define CIRCLE_CAM_CID Class_ID(0x66f85282, 0x4daa1b8e) +#define IMAGE_LIB_CID Class_ID(0x736c18d3, 0x6a6d5dde) + +class plComponentBase; +class plAgeDescription; + +const char* LocCompGetPage(plComponentBase* comp); + +namespace plPageInfoUtils +{ + const char *GetAgeFolder(); + Int32 GetSeqNumFromAgeDesc( const char *ageName, const char *pageName ); + Int32 CombineSeqNum( int prefix, int suffix ); + Int32 GetCommonSeqNumFromNormal( Int32 normalSeqNumber, int whichCommonPage ); + + plAgeDescription *GetAgeDesc( const char *ageName ); +}; + +// PageInfo component definition, here so other components can get to the static function(s) +class plPageInfoComponent : public plComponent +{ +protected: + hsBool fSeqNumValidated; + hsBool fItinerant; + static char fCurrExportedAge[ 256 ]; + + void IVerifyLatestAgeAsset( const char *ageName, const char *localPath, plErrorMsg *errMsg ); + void IUpdateSeqNumbersFromAgeFile( plErrorMsg *errMsg ); + +public: + plPageInfoComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + char *GetAgeName(); + hsBool GetItinerant() {return fItinerant; } + + enum + { + kInfoAge=3, + kInfoPage, + kInfoSeqPrefix, + kInfoSeqSuffix, + kRefVolatile_PageInfoUpdated, + kItinerant + }; + + static char *GetCurrExportAgeName() { return (char *)&fCurrExportedAge; } + static void NotifyProc(void *param, NotifyInfo *info); +}; + +//Class that accesses the paramblock below. +class plLayerTex; +class pfImageLibComponent : public plComponent +{ +public: + pfImageLibComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + enum ParamIDs + { + kRefImageList, + kCompressImage, + }; + + int GetNumBitmaps( void ) const; + plLayerTex *GetBitmap( int idx ); + int AppendBitmap( plLayerTex *tex ); + void RemoveBitmap( int idx ); + bool GetCompress(int idx); + void SetCompress(int idx, bool compress); + + void Validate(); +}; + +#endif // plMiscComponents_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMorphSeqComp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMorphSeqComp.cpp new file mode 100644 index 00000000..defc89b0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMorphSeqComp.cpp @@ -0,0 +1,362 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "plPickNode.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxExport/plExportProgressBar.h" + +#include "hsTypes.h" +#include "hsResMgr.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../plDrawable/plMorphSequence.h" +#include "../plDrawable/plSharedMesh.h" + +const Class_ID MORPHSEQ_COMP_CID(0x37100f0a, 0x2d1f6b87); +const Class_ID MORPHLAY_COMP_CID(0x138b1d44, 0x6c0a7417); + +void DummyCodeIncludeFuncMorph() +{ +} + +class plMorphLayComp : public plComponent +{ +protected: +public: + enum + { + kDeltas + }; + + plMorphLayComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool SetupLayer(plMorphArray& morphArr, plMaxNode* baseNode, hsTArray* baseSpans, plErrorMsg* pErrMsg); +}; + +CLASS_DESC(plMorphLayComp, gMorphLayCompDesc, "Morph Layer", "MorphLay", COMP_TYPE_AVATAR, MORPHLAY_COMP_CID) + +ParamBlockDesc2 gMorphLayBk +( + plComponent::kBlkComp, _T("MorphLay"), 0, &gMorphLayCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_MORPHLAY, IDS_COMP_MORPHLAY, 0, 0, nil, + + plMorphLayComp::kDeltas, _T("Deltas"), TYPE_INODE_TAB, 0, P_CAN_CONVERT, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, IDC_ADD_TARGS, 0, IDC_DEL_TARGS, + p_classID, triObjectClassID, + end, + + end +); + + +plMorphLayComp::plMorphLayComp() +{ + fClassDesc = &gMorphLayCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plMorphLayComp::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + const int num = fCompPB->Count(kDeltas); + int i; + for( i = 0; i < num; i++ ) + { + plMaxNode* deltaNode = (plMaxNode*)fCompPB->GetINode(kDeltas, TimeValue(0), i); + if( deltaNode ) + { + const char* deltaName = deltaNode->GetName(); + + if( !deltaNode->GetSwappableGeom() ) + deltaNode->SetSwappableGeom(new plSharedMesh); +// deltaNode->SetForceLocal(true); + } + } + + return true; +} + +hsBool plMorphLayComp::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plMorphLayComp::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plMorphLayComp::SetupLayer(plMorphArray& morphArr, plMaxNode* baseNode, hsTArray* baseSpans, plErrorMsg* pErrMsg) +{ + const int num = fCompPB->Count(kDeltas); + int i; + // For each delta + for( i = 0; i < num; i++ ) + { + plMaxNode* deltaNode = (plMaxNode*)fCompPB->GetINode(kDeltas, TimeValue(0), i); + if( !deltaNode ) + continue; + const char* dbgNodeName = deltaNode->GetName(); + + // Get the GeometrySpans. We ensured they would + // be generated in it's SetupProperties, and they were created + // in the MakeMesh pass. + hsTArray* movedSpans = &deltaNode->GetSwappableGeom()->fSpans; + + // We want to move the deltas into the same space as the base mesh verts. + // So the first question is, which space do we want to move them from? + // Answer is, we want to take the deltas from where they are relative to their OTM (pivot point), + // and move them into base node's local space. + // The idea is that we want to assume the pivot points of the two objects are aligned, even though + // we don't require that they are. So we transform delta verts to their pivot space, pretend that is the + // same space as baseNode's pivot space, and transform from that to baseNode's local space. + // This is somewhat arbitrary, but it allows the delta meshes to be moved aside relative to the base + // meshes, as well as aligning the Pivot points to align the deltas and base mesh. + // So: + // From deltaLocal to deltaPivot space = deltaVertToPivot * deltaLocalToVert // vertToPivot = OTM + // From deltaPivot to basePivot = skip (pretend they are the same). + // From basePivot to baseLocal = baseVertToLocal * Inverse(baseVertToPivot) // because baseOTM is in baseVertToLocal + // mf + hsMatrix44 bvert2local = baseNode->GetVertToLocal44() * baseNode->Matrix3ToMatrix44(Inverse(baseNode->GetOTM())); + hsMatrix44 dlocal2vert = deltaNode->GetOTM44() * deltaNode->GetLocalToVert44(); + hsMatrix44 d2b = bvert2local * dlocal2vert; + hsMatrix44 d2bTInv; + d2b.GetInverse(&d2bTInv); + d2bTInv.GetTranspose(&d2bTInv); + // Error check - movedSpans->GetCount() == baseSpans->GetCount(); + if( movedSpans->GetCount() != baseSpans->GetCount() ) + { + pErrMsg->Set(true, deltaNode->GetName(), "Delta mesh mismatch with base").CheckAndAsk(); + pErrMsg->Set(false); + continue; + } + + plMorphDelta delta; + delta.ComputeDeltas(*baseSpans, *movedSpans, d2b, d2bTInv); + morphArr.AddDelta(delta); + } + + return morphArr.GetNumDeltas() > 0; +} + + + +class plMorphSeqComp : public plComponent +{ +protected: + plMorphLayComp* IGetLayerComp(int i); + +public: + enum + { + kLayers, + kBaseNode + }; + + plMorphSeqComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +CLASS_DESC(plMorphSeqComp, gMorphSeqCompDesc, "Morph Sequence", "MorphSeq", COMP_TYPE_AVATAR, MORPHSEQ_COMP_CID) + + +class plMorphSeqProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + } + return true; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ADD_TARGS) + { + std::vector cids; + cids.push_back(MORPHLAY_COMP_CID); + IParamBlock2 *pb = map->GetParamBlock(); + plPick::Node(pb, plMorphSeqComp::kLayers, &cids, false, false); + + map->Invalidate(plMorphSeqComp::kLayers); + return TRUE; + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plMorphSeqProc gMorphSeqProc; + +///////////////////////////////////////////////////////////////////////////////////// + + +ParamBlockDesc2 gMorphSeqBk +( + plComponent::kBlkComp, _T("MorphSeq"), 0, &gMorphSeqCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_MORPHSEQ, IDS_COMP_MORPHSEQ, 0, 0, &gMorphSeqProc, + + plMorphSeqComp::kLayers, _T("Layers"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + end, + + plMorphSeqComp::kBaseNode, _T("BaseNode"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CHOOSE_OBJECT, + p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_CHOSE_OBJECT, + end, + + end +); + +hsBool plMorphSeqComp::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plMaxNode* baseNode = (plMaxNode*)fCompPB->GetINode(kBaseNode); + if( !baseNode ) + { + pErrMsg->Set(true, node->GetName(), "Missing base (neutral) geometry node").CheckAndAsk(); + pErrMsg->Set(false); + return true; + } + if( !baseNode->GetSwappableGeom() ) + baseNode->SetSwappableGeom(new plSharedMesh); +// baseNode->SetForceLocal(true); + + return true; +} + +hsBool plMorphSeqComp::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plMorphSeqComp::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + const char* dbgNodeName = node->GetName(); + + // Make our plMorphSequence modifier + plMorphSequence* morphSeq = const_cast(plMorphSequence::ConvertNoRef(node->GetSceneObject()->GetModifierByType(plMorphSequence::Index()))); + if (!morphSeq) + { + morphSeq = TRACKED_NEW plMorphSequence; + node->AddModifier(morphSeq, IGetUniqueName(node)); + } + + // Get our base geometry. + plMaxNode* baseNode = (plMaxNode*)fCompPB->GetINode(kBaseNode); + plSharedMesh* mesh = baseNode->GetSwappableGeom(); + hsTArray* baseSpans = &mesh->fSpans; + //morphSeq->AddSharedMesh(mesh); + + // Error check we have some base geometry. + + plMorphDataSet *set = TRACKED_NEW plMorphDataSet; + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), set, node->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(set->GetKey(), TRACKED_NEW plGenRefMsg(mesh->GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); + + const int num = fCompPB->Count(kLayers); + int i; + // For each layer that we have + for( i = 0; i < num; i++ ) + { + plMorphLayComp* layComp = IGetLayerComp(i); + if( !layComp ) + continue; + + // Layer is a sequence of geometry deltas. A layer will + // become a plMorphArray. + plMorphArray morphArr; + + if( layComp->SetupLayer(morphArr, baseNode, baseSpans, pErrMsg) ) + { + //morphSeq->AddLayer(morphArr); + set->fMorphs.Append(morphArr); + } + } + + // Error check - should make sure we wound up with something valid, + // as opposed to a million empty deltas or something. + + return true; +} + +hsBool plMorphSeqComp::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +plMorphLayComp* plMorphSeqComp::IGetLayerComp(int i) +{ + plMaxNode* node = (plMaxNode*)fCompPB->GetINode(kLayers, TimeValue(0), i); + if( !node ) + return nil; + + plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); + if( !comp ) + return nil; + + if( comp->ClassID() == MORPHLAY_COMP_CID ) + { + return (plMorphLayComp*)comp; + } + + return nil; +} + +plMorphSeqComp::plMorphSeqComp() +{ + fClassDesc = &gMorphSeqCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageBehComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageBehComponent.cpp new file mode 100644 index 00000000..6d181734 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageBehComponent.cpp @@ -0,0 +1,605 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plMultistageBehComponent.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "plMultistageStage.h" + +#include "hsStream.h" +#include "resource.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxMain/plMaxAccelerators.h" + +#include "../plAvatar/plAnimStage.h" +#include "../plAvatar/plMultistageBehMod.h" +#include "hsResMgr.h" + +#include + + +void DummyCodeIncludeFuncMultistageBeh() {} + +class plBaseStage; + +class plMultistageBehComponent : public plComponent +{ +protected: + typedef std::multimap ReceiverKeys; + typedef std::pair ReceiverKey; + ReceiverKeys fReceivers; + void IGetReceivers(plMaxNode* node, std::vector& receivers); + + std::vector fStages; + bool fFreezePhys; + bool fSmartSeek; + bool fReverseFBOnRelease; + + // Dialog parameters, assume we'll only have one dialog open at a time + static HWND fDlg; + static int fCurStage; + + void IDeleteStages(); + + static BOOL CALLBACK IStaticDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + void IInitDlg(); + void FixStageNames(); + + void ICreateStageDlg(); + void IDestroyStageDlg(); + + std::map fMods; + +public: + plMultistageBehComponent(); + ~plMultistageBehComponent(); + + plKey GetMultiStageBehKey(plMaxNode *node); + + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void AddReceiverKey(plKey pKey, plMaxNode* node=nil); + + virtual void CreateRollups(); + virtual void DestroyRollups(); + + IOResult Save(ISave* isave); + IOResult Load(ILoad* iload); + + RefTargetHandle Clone(RemapDir &remap); +}; + +HWND plMultistageBehComponent::fDlg = NULL; +int plMultistageBehComponent::fCurStage = -1; + +// +// This is the access for other components to get the plKey of the MultiStageBeh modifier +// +plKey MultiStageBeh::GetMultiStageBehKey(plComponentBase *multiStageBehComp, plMaxNodeBase *target) +{ + if (multiStageBehComp->ClassID() == MULTISTAGE_BEH_CID) + { + plMultistageBehComponent *comp = (plMultistageBehComponent*)multiStageBehComp; + return comp->GetMultiStageBehKey((plMaxNode*)target); + } + + return nil; +} + + +CLASS_DESC(plMultistageBehComponent, gMultistageBehDesc, "Multistage Behavior", "MultiBeh", COMP_TYPE_AVATAR, MULTISTAGE_BEH_CID) + +plMultistageBehComponent::plMultistageBehComponent() +: fFreezePhys(false), + fSmartSeek(false), + fReverseFBOnRelease(false) +{ + fClassDesc = &gMultistageBehDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plMultistageBehComponent::~plMultistageBehComponent() +{ + IDeleteStages(); +} + + +hsBool plMultistageBehComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetForceLocal(true); + fReceivers.clear(); + return true; +} + +plKey plMultistageBehComponent::GetMultiStageBehKey(plMaxNode *node) +{ + if (fMods.find(node) != fMods.end()) + return fMods[node]->GetKey(); + + return nil; +} + +void plMultistageBehComponent::AddReceiverKey(plKey pKey, plMaxNode* node) +{ + fReceivers.insert(ReceiverKey(node, pKey)); +} + +void plMultistageBehComponent::IGetReceivers(plMaxNode* node, std::vector& receivers) +{ + // Add the guys who want to be notified by all instances + ReceiverKeys::iterator lowIt = fReceivers.lower_bound(nil); + ReceiverKeys::iterator highIt = fReceivers.upper_bound(nil); + for (; lowIt != highIt; lowIt++) + receivers.push_back(lowIt->second); + + // Add the ones for just this instance + lowIt = fReceivers.lower_bound(node); + highIt = fReceivers.upper_bound(node); + for (; lowIt != highIt; lowIt++) + receivers.push_back(lowIt->second); +} + +// +// PreConvert done below +// +hsBool plMultistageBehComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + //create the modifier here so that other components can find it + plMultistageBehMod *mod = TRACKED_NEW plMultistageBehMod; + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), mod, node->GetLocation()); + fMods[node] = mod; + + return true; +} + +hsBool plMultistageBehComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + // Create the stage vector + plAnimStageVec* animStages = TRACKED_NEW plAnimStageVec; + int numStages = fStages.size(); + animStages->reserve(numStages); + + // Convert the stages and add them to the vector + for (int i = 0; i < numStages; i++) + { + plBaseStage* stage = fStages[i]; + plAnimStage* animStage = stage->CreateStage(); + + animStages->push_back(animStage); + } + + // re-find the mod and attach it + plMultistageBehMod* mod = fMods[node]; + std::vector receivers; + IGetReceivers(node, receivers); + mod->Init(animStages, fFreezePhys, fSmartSeek, fReverseFBOnRelease, &receivers); + node->AddModifier(mod, IGetUniqueName(node)); + + return true; +} + +void plMultistageBehComponent::IDeleteStages() +{ + int numStages = fStages.size(); + for (int i = 0; i < numStages; i++) + { + plBaseStage *stage = fStages[i]; + delete [] stage; + } + fStages.clear(); +} + +void plMultistageBehComponent::ICreateStageDlg() +{ + if (fCurStage == -1) + return; + + hsAssert(fCurStage < fStages.size(), "Current stage out of range"); + fStages[fCurStage]->CreateDlg(); +} + +void plMultistageBehComponent::IDestroyStageDlg() +{ + if (fCurStage == -1) + return; + + hsAssert(fCurStage < fStages.size(), "Current stage out of range"); + fStages[fCurStage]->DestroyDlg(); + + fCurStage = -1; +} + +void plMultistageBehComponent::CreateRollups() +{ + plComponent::CreateRollups(); + + fDlg = GetCOREInterface()->AddRollupPage(hInstance, + MAKEINTRESOURCE(IDD_COMP_MULTIBEH), + IStaticDlgProc, + "Multistage Behavior", + (LPARAM)this); + IInitDlg(); + + ICreateStageDlg(); +} + +void plMultistageBehComponent::DestroyRollups() +{ + IDestroyStageDlg(); + + if (fDlg) + { + GetCOREInterface()->DeleteRollupPage(fDlg); + fDlg = NULL; + } + + plComponent::DestroyRollups(); +} + +int ListView_AddString(HWND hList, const char* str) +{ + LVITEM item = {0}; + item.mask = LVIF_TEXT; + item.pszText = const_cast(str); // F*** you Windows + item.iItem = ListView_GetItemCount(hList); + return ListView_InsertItem(hList, &item); +} + +void plMultistageBehComponent::IInitDlg() +{ + // Add a column. We don't use it (graphically), but it has to be there. + HWND hList = GetDlgItem(fDlg, IDC_STAGE_LIST); + LVCOLUMN lvc; + lvc.mask = LVCF_TEXT; + lvc.pszText = "Blah"; + ListView_InsertColumn(hList, 0, &lvc); + + FixStageNames(); + for (int i = 0; i < fStages.size(); i++) + { + plBaseStage* stage = fStages[i]; + ListView_AddString(hList, stage->GetName()); + } + + // Make sure the column is wide enough + ListView_SetColumnWidth(hList, 0, LVSCW_AUTOSIZE); + + CheckDlgButton(fDlg, IDC_SMART_SEEK, fSmartSeek ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(fDlg, IDC_FREEZE_PHYS, fFreezePhys ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(fDlg, IDC_MULTI_REVERSE_CTL, fReverseFBOnRelease ? BST_CHECKED : BST_UNCHECKED); +} + +// stages used to be named starting with "Stage 1", but in the code +// they're referred to as "Stage 0..n" +// here we're going to look for old stage names and, if present, +// rename them all to start with zero instead. +void plMultistageBehComponent::FixStageNames() +{ + if(fStages.size() > 0) + { + plBaseStage* stage = fStages[0]; + const char * stageName = stage->GetName(); + + if(strcmp(stageName, "Stage 1") == 0) + { + for (int i = 0; i < fStages.size(); i++) + { + plBaseStage* stage = fStages[i]; + char buf[64]; + sprintf(buf, "Stage %d", i); + stage->SetName(buf); + } + } + } +} + + +BOOL plMultistageBehComponent::IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + // Adding a new stage + if (LOWORD(wParam) == IDC_ADD) + { + // Create the new stage and give it a default name. + plBaseStage* stage = TRACKED_NEW plStandardStage; + int count = fStages.size(); + fStages.push_back(stage); + char buf[64]; + sprintf(buf, "Stage %d", count); + stage->SetName(buf); + + // Add the new stage to the list and make sure the list is wide enough + HWND hList = GetDlgItem(fDlg, IDC_STAGE_LIST); + int idx = ListView_AddString(hList, stage->GetName()); + ListView_SetColumnWidth(hList, 0, LVSCW_AUTOSIZE); + ListView_SetItemState(hList, idx, LVIS_SELECTED, LVIS_SELECTED); + + // Put up the new stages dialog + IDestroyStageDlg(); + fCurStage = idx; + ICreateStageDlg(); + + SetSaveRequiredFlag(); + } + // Removing the selected stage + else if (LOWORD(wParam) == IDC_REMOVE) + { + HWND hList = GetDlgItem(fDlg, IDC_STAGE_LIST); + + int sel = ListView_GetNextItem(hList, -1, LVNI_SELECTED); + if (sel != -1) + { + IDestroyStageDlg(); + + plBaseStage* stage = fStages[sel]; + fStages.erase(fStages.begin()+sel); + delete stage; + ListView_DeleteItem(hList, sel); + + SetSaveRequiredFlag(); + } + } + else if (LOWORD(wParam) == IDC_FREEZE_PHYS) + { + fFreezePhys = (Button_GetCheck((HWND)lParam) == BST_CHECKED); + SetSaveRequiredFlag(); + } + else if (LOWORD(wParam) == IDC_SMART_SEEK) + { + fSmartSeek = (Button_GetCheck((HWND)lParam) == BST_CHECKED); + SetSaveRequiredFlag(); + } + else if (LOWORD(wParam) == IDC_MULTI_REVERSE_CTL) + { + fReverseFBOnRelease = (Button_GetCheck((HWND)lParam) == BST_CHECKED); + SetSaveRequiredFlag(); + } + return TRUE; + } + break; + + case WM_NOTIFY: + { + NMHDR *nmhdr = (NMHDR*)lParam; + if (nmhdr->idFrom == IDC_STAGE_LIST) + { + switch (nmhdr->code) + { + // Stop Max from reading keypresses while the list has focus + case NM_SETFOCUS: + plMaxAccelerators::Disable(); + return TRUE; + case NM_KILLFOCUS: + plMaxAccelerators::Enable(); + return TRUE; + + // The edit box this creates kills the focus on the listbox, + // so add an extra disable to ignore it + case LVN_BEGINLABELEDIT: + plMaxAccelerators::Disable(); + return TRUE; + + // Finishing changing the name of a stage + case LVN_ENDLABELEDIT: + { + NMLVDISPINFO *di = (NMLVDISPINFO*)lParam; + const char *name = di->item.pszText; + + // If the name was changed... + if (name && *name != '\0') + { + plBaseStage* stage = fStages[fCurStage]; + stage->SetName(name); + + // Make sure the column is wide enough + int width = ListView_GetStringWidth(nmhdr->hwndFrom, name)+10; + if (width > ListView_GetColumnWidth(nmhdr->hwndFrom, 0)) + { + ListView_SetColumnWidth(nmhdr->hwndFrom, 0, width); + } + + // Return true to keep the changes + SetWindowLong(hDlg, DWL_MSGRESULT, TRUE); + } + + plMaxAccelerators::Enable(); + } + return TRUE; + + case LVN_ITEMCHANGED: + { + int sel = ListView_GetNextItem(nmhdr->hwndFrom, -1, LVNI_SELECTED); + IDestroyStageDlg(); + if (sel != -1 && sel != fCurStage) + { + fCurStage = sel; + ICreateStageDlg(); + } + } + return TRUE; + } + } + } + break; + } + + return FALSE; +} + +// A simple wrapper so the Max save/load stuff can be used with the hsStream interface +class MaxStream : public hsStream +{ +protected: + ISave* fSave; + ILoad* fLoad; + +public: + MaxStream(ISave* isave) : fSave(isave), fLoad(nil) {} + MaxStream(ILoad* iload) : fSave(nil), fLoad(iload) {} + + // Don't support any of this + virtual hsBool Open(const char *, const char * = "rb") { hsAssert(0, "Not supported"); return false; } + virtual hsBool Open(const wchar *, const wchar * = L"rb") { hsAssert(0, "Not supported"); return false; } + virtual hsBool Close() { hsAssert(0, "Not supported"); return false; } + virtual void Skip(UInt32 deltaByteCount) { hsAssert(0, "Not supported"); } + virtual void Rewind() { hsAssert(0, "Not supported"); } + + virtual UInt32 GetEOF() { return fLoad->CurChunkLength(); } + + virtual UInt32 Read(UInt32 byteCount, void * buffer) + { + UInt32 numRead = 0; + hsAssert(fLoad, "No Max ILoad!"); + if (fLoad) + fLoad->Read(buffer, byteCount, &numRead); + fPosition += numRead; + return numRead; + } + virtual UInt32 Write(UInt32 byteCount, const void* buffer) + { + UInt32 numWritten; + hsAssert(fSave, "No Max ISave!"); + if (fSave) + fSave->Write(buffer, byteCount, &numWritten); + return numWritten; + } +}; + + +IOResult plMultistageBehComponent::Save(ISave* isave) +{ + isave->BeginChunk(kMultiStage); + MaxStream multiChunk(isave); + multiChunk.WriteSwap32(3); + multiChunk.Writebool(fFreezePhys); + multiChunk.Writebool(fSmartSeek); + multiChunk.Writebool(fReverseFBOnRelease); + isave->EndChunk(); + + int numStages = fStages.size(); + for (int i = 0; i < numStages; i++) + { + plBaseStage *stage = fStages[i]; + if (stage) + { + isave->BeginChunk(stage->GetType()); + MaxStream stageChunk(isave); + stage->Write(&stageChunk); + isave->EndChunk(); + } + } + + return IO_OK; +} + +IOResult plMultistageBehComponent::Load(ILoad* iload) +{ + IDeleteStages(); + + while (iload->OpenChunk() == IO_OK) + { + plBaseStage *stage = nil; + + switch (iload->CurChunkID()) + { + case kMultiStage: + { + MaxStream multiChunk(iload); + // all versions do this + int version = multiChunk.ReadSwap32(); + fFreezePhys = multiChunk.Readbool(); + + if(version > 1) + // version 1 adds smart seek + fSmartSeek = multiChunk.Readbool(); + else + fSmartSeek = false; + + if(version > 2) + fReverseFBOnRelease = multiChunk.Readbool(); + else + fReverseFBOnRelease = false; + } + break; + + case kStandard: + stage = TRACKED_NEW plStandardStage; + break; + } + + if (stage) + { + MaxStream stageChunk(iload); + stage->Read(&stageChunk); + fStages.push_back(stage); + } + + iload->CloseChunk(); + } + + return IO_OK; +} + +RefTargetHandle plMultistageBehComponent::Clone(RemapDir &remap) +{ + plMultistageBehComponent* clone = (plMultistageBehComponent*)plComponent::Clone(remap); + + clone->fFreezePhys = fFreezePhys; + clone->fSmartSeek = fSmartSeek; + clone->fReverseFBOnRelease = fReverseFBOnRelease; + + int numStages = fStages.size(); + clone->fStages.reserve(numStages); + for (int i = 0; i < numStages; i++) + { + plBaseStage* cloneStage = fStages[i]->Clone(); + clone->fStages.push_back(cloneStage); + } + + return clone; +} + +BOOL plMultistageBehComponent::IStaticDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_INITDIALOG) + SetWindowLong(hDlg, GWL_USERDATA, lParam); + + plMultistageBehComponent *multi = (plMultistageBehComponent*)GetWindowLong(hDlg, GWL_USERDATA); + + if (!multi) + return FALSE; + + return multi->IDlgProc(hDlg, msg, wParam, lParam); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageBehComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageBehComponent.h new file mode 100644 index 00000000..dddd1a58 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageBehComponent.h @@ -0,0 +1,39 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#define MULTISTAGE_BEH_CID Class_ID(0x6c5234ab, 0x6c955a61) + + +#include "max.h" + +class plKey; +class plComponentBase; +class plMaxNodeBase; + + +namespace MultiStageBeh +{ + plKey GetMultiStageBehKey(plComponentBase *multiStageBehComp, plMaxNodeBase *target); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageStage.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageStage.cpp new file mode 100644 index 00000000..7b69b455 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageStage.cpp @@ -0,0 +1,474 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plMultistageStage.h" +#include "max.h" +#include "hsStream.h" +#include "resource.h" +#include "hsUtils.h" + +#include "../plAvatar/plAnimStage.h" + +// We don't want to be subject to any changes to ReadSafeString, so we just keep +// our own version now. Unfortunately, some files were saved with a modified +// version of it, so we need to keep all the backwards compatability BS +char* MyReadSafeString(hsStream* s) +{ + char *name = nil; + UInt16 numChars = s->ReadSwap16(); + + bool oldFormat = !(numChars & 0xf000); + if (oldFormat) + s->ReadSwap16(); + + numChars &= ~0xf000; + hsAssert(numChars <= s->GetSizeLeft(), "Bad string"); + if (numChars > 0) + { + name = TRACKED_NEW char[numChars+1]; + s->Read(numChars, name); + name[numChars] = '\0'; + } + + return name; +} + +void MyWriteSafeString(hsStream* s, const char* str) +{ + int len = hsStrlen(str); + hsAssert(len<0xf000, "String too long"); + s->WriteSwap16(len | 0xf000); + if (len > 0) + s->Write(len, str); +} + +plBaseStage::plBaseStage() +{ + fName = nil; +} + +plBaseStage::~plBaseStage() +{ + delete [] fName; +} + +BOOL plBaseStage::IStaticDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_INITDIALOG) + SetWindowLong(hDlg, GWL_USERDATA, lParam); + + plBaseStage *stage = (plBaseStage*)GetWindowLong(hDlg, GWL_USERDATA); + + if (!stage) + return FALSE; + + return stage->IDlgProc(hDlg, msg, wParam, lParam); +} + +BOOL plBaseStage::IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return FALSE; +} + +HWND plBaseStage::ICreateDlg(int dialogID, char* title) +{ + return GetCOREInterface()->AddRollupPage(hInstance, + MAKEINTRESOURCE(dialogID), + IStaticDlgProc, + title, + (LPARAM)this); +} + +void plBaseStage::IDestroyDlg(HWND hDlg) +{ + if (hDlg) + GetCOREInterface()->DeleteRollupPage(hDlg); +} + +const char* plBaseStage::GetName() +{ + if (!fName) + fName = hsStrcpy("DefaultName"); + return fName; +} + +void plBaseStage::SetName(const char* name) +{ + delete [] fName; + fName = hsStrcpy(name); +} + +void plBaseStage::Read(hsStream *stream) +{ + int version = stream->ReadSwap16(); + delete [] fName; + fName = MyReadSafeString(stream); +} + +void plBaseStage::Write(hsStream *stream) +{ + stream->WriteSwap16(1); + MyWriteSafeString(stream, fName); +} + +void plBaseStage::IBaseClone(plBaseStage* clone) +{ + clone->fName = hsStrcpy(fName); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +HWND plStandardStage::fDlg = NULL; + +plStandardStage::plStandardStage() +{ + fAnimName = nil; + fNumLoops = 0; + fLoopForever = false; + fForward = 0; + fBackward = 0; + fStageAdvance = 0; + fStageRegress = 0; + fNotify = 0; + fUseGlobalCoord = false; + fDoAdvanceTo = false; + fAdvanceTo = 0; + fDoRegressTo = false; + fRegressTo = 0; +} + +plStandardStage::~plStandardStage() +{ + delete [] fAnimName; +} + +void plStandardStage::Read(hsStream *stream) +{ + plBaseStage::Read(stream); + + UInt16 version = stream->ReadSwap16(); + + delete [] fAnimName; + fAnimName = MyReadSafeString(stream); + fNumLoops = stream->ReadSwap32(); + fLoopForever = stream->Readbool(); + fForward = stream->ReadByte(); + fBackward = stream->ReadByte(); + fStageAdvance = stream->ReadByte(); + fStageRegress = stream->ReadByte(); + fNotify = stream->ReadByte(); + fUseGlobalCoord = stream->Readbool(); + if(version > 1) + { + // these guys were added in version 2 + fDoAdvanceTo = stream->Readbool(); + fAdvanceTo = stream->ReadSwap32(); + fDoRegressTo = stream->Readbool(); + fRegressTo = stream->ReadSwap32(); + } +} + +void plStandardStage::Write(hsStream *stream) +{ + plBaseStage::Write(stream); + + stream->WriteSwap16(2); + + MyWriteSafeString(stream, fAnimName); + stream->WriteSwap32(fNumLoops); + stream->Writebool(fLoopForever); + stream->WriteByte(fForward); + stream->WriteByte(fBackward); + stream->WriteByte(fStageAdvance); + stream->WriteByte(fStageRegress); + stream->WriteByte(fNotify); + stream->Writebool(fUseGlobalCoord); + + // these next 4 were added in version 2 + stream->Writebool(fDoAdvanceTo); + stream->WriteSwap32(fAdvanceTo); + stream->Writebool(fDoRegressTo); + stream->WriteSwap32(fRegressTo); +} + +void plStandardStage::CreateDlg() +{ + hsAssert(!fDlg, "Dialog wasn't destroyed"); + fDlg = ICreateDlg(IDD_COMP_MULTIBEH_NORMAL, "Standard Stage"); + + IInitDlg(); +} + +void plStandardStage::DestroyDlg() +{ + // This shitty Max edit box doesn't notify of changes if it has focus during + // shutdown, so we just get it no matter what. + IGetAnimName(); + + IDestroyDlg(fDlg); + fDlg = nil; +} + +#define SetBit(f,b,on) on ? hsSetBits(f,b) : hsClearBits(f,b) + +BOOL plStandardStage::IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_COMMAND: + { + int code = HIWORD(wParam); + int id = LOWORD(wParam); + + // Combo changed + if (code == CBN_SELCHANGE) + { + int sel = ComboBox_GetCurSel((HWND)lParam); + int type = ComboBox_GetItemData((HWND)lParam, sel); + + if (id == IDC_FORWARD_COMBO) + fForward = type; + else if (id == IDC_BACKWARD) + fBackward = type; + else if (id == IDC_ADVANCE) + fStageAdvance = type; + else if (id == IDC_REGRESS) + fStageRegress = type; + + SetSaveRequiredFlag(); + + return TRUE; + } + // Button clicked or checkbox checked + else if (code == BN_CLICKED) + { + bool isChecked = (Button_GetCheck((HWND)lParam) == BST_CHECKED); + if (id == IDC_LOOP_FOREVER) + { + fLoopForever = isChecked; + + ISpinnerControl *spin = GetISpinner(GetDlgItem(fDlg, IDC_NUM_LOOPS_SPIN)); + spin->Enable(!fLoopForever); + } + else if (id == IDC_GLOBAL_COORD) + { + fUseGlobalCoord = isChecked; + } + else if (id == IDC_CHECK_ENTER) + SetBit(fNotify, plAnimStage::kNotifyEnter, isChecked); + else if (id == IDC_CHECK_LOOP) + SetBit(fNotify, plAnimStage::kNotifyLoop, isChecked); + else if (id == IDC_CHECK_ADVANCE) + SetBit(fNotify, plAnimStage::kNotifyAdvance, isChecked); + else if (id == IDC_CHECK_REGRESS) + SetBit(fNotify, plAnimStage::kNotifyRegress, isChecked); + else if (id == IDC_DO_ADVANCE_TO) + { + fDoAdvanceTo = isChecked; + ISpinnerControl *spin = GetISpinner(GetDlgItem(fDlg, IDC_ADVANCE_STAGE_SPIN)); + spin->Enable(fDoAdvanceTo); + } else if (id == IDC_DO_REGRESS_TO) + { + fDoRegressTo = isChecked; + ISpinnerControl *spin = GetISpinner(GetDlgItem(fDlg, IDC_REGRESS_STAGE_SPIN)); + spin->Enable(fDoRegressTo); + } + + SetSaveRequiredFlag(); + + return TRUE; + } + } + break; + + // Num loops spinner changed + case CC_SPINNER_CHANGE: + { + ISpinnerControl* spin = (ISpinnerControl*)lParam; + if (LOWORD(wParam) == IDC_NUM_LOOPS_SPIN) + { + fNumLoops = spin->GetIVal(); + } else if (LOWORD(wParam) == IDC_ADVANCE_STAGE_SPIN) { + fAdvanceTo = spin->GetIVal(); + } else if (LOWORD(wParam) == IDC_REGRESS_STAGE_SPIN) { + fRegressTo = spin->GetIVal(); + } + + SetSaveRequiredFlag(); + return TRUE; + } + break; + + // Anim name changed + case WM_CUSTEDIT_ENTER: + if (LOWORD(wParam) == IDC_ANIM_NAME) + { + IGetAnimName(); + return TRUE; + } + break; + } + return FALSE; +} + +void plStandardStage::IGetAnimName() +{ + ICustEdit* edit = GetICustEdit(GetDlgItem(fDlg, IDC_ANIM_NAME)); + char buf[256]; + edit->GetText(buf, sizeof(buf)); + + if (!hsStrEQ(buf, fAnimName)) + { + delete [] fAnimName; + fAnimName = hsStrcpy(buf); + + SetSaveRequiredFlag(); + } +} + +struct NameType +{ + const char* name; + int type; +}; + +static NameType gForward[] = +{ + { "None", plAnimStage::kForwardNone }, + { "Keyboard", plAnimStage::kForwardKey }, + { "Automatic", plAnimStage::kForwardAuto } +}; + +static NameType gBackward[] = +{ + { "None", plAnimStage::kBackNone }, + { "Keyboard", plAnimStage::kBackKey }, + { "Automatic", plAnimStage::kBackAuto } +}; + +static NameType gAdvance[] = +{ + { "None", plAnimStage::kAdvanceNone }, + { "Auto At End",plAnimStage::kAdvanceAuto } +}; + +static NameType gRegress[] = +{ + { "None", plAnimStage::kRegressNone }, + { "Auto At End",plAnimStage::kRegressAuto } +}; + +static void LoadCombo(HWND hCombo, NameType* nameInt, int size, int curVal) +{ + int num = size / sizeof(NameType); + + for (int i = 0; i < num; i++) + { + int idx = ComboBox_AddString(hCombo, nameInt[i].name); + ComboBox_SetItemData(hCombo, idx, nameInt[i].type); + + if (nameInt[i].type == curVal) + ComboBox_SetCurSel(hCombo, idx); + } +} + +void plStandardStage::IInitDlg() +{ + ICustEdit* edit = GetICustEdit(GetDlgItem(fDlg, IDC_ANIM_NAME)); + edit->SetText(fAnimName); + + HWND hForward = GetDlgItem(fDlg, IDC_FORWARD_COMBO); + LoadCombo(hForward, gForward, sizeof(gForward), fForward); + + HWND hBackward = GetDlgItem(fDlg, IDC_BACKWARD); + LoadCombo(hBackward, gBackward, sizeof(gBackward), fBackward); + + HWND hAdvance = GetDlgItem(fDlg, IDC_ADVANCE); + LoadCombo(hAdvance, gAdvance, sizeof(gAdvance), fStageAdvance); + + HWND hRegress = GetDlgItem(fDlg, IDC_REGRESS); + LoadCombo(hRegress, gRegress, sizeof(gRegress), fStageRegress); + + CheckDlgButton(fDlg, IDC_CHECK_ENTER, (fNotify & plAnimStage::kNotifyEnter) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(fDlg, IDC_CHECK_LOOP, (fNotify & plAnimStage::kNotifyLoop) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(fDlg, IDC_CHECK_ADVANCE, (fNotify & plAnimStage::kNotifyAdvance) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(fDlg, IDC_CHECK_REGRESS, (fNotify & plAnimStage::kNotifyRegress) ? BST_CHECKED : BST_UNCHECKED); + + ISpinnerControl *spin = SetupIntSpinner(fDlg, IDC_NUM_LOOPS_SPIN, IDC_NUM_LOOPS_EDIT, 0, 10000, fNumLoops); + + CheckDlgButton(fDlg, IDC_LOOP_FOREVER, fLoopForever ? BST_CHECKED : BST_UNCHECKED); + if (fLoopForever) + spin->Disable(); + + spin = SetupIntSpinner(fDlg, IDC_ADVANCE_STAGE_SPIN, IDC_ADVANCE_STAGE_EDIT, -1, 100, fAdvanceTo); + CheckDlgButton(fDlg, IDC_DO_ADVANCE_TO, fDoAdvanceTo ? BST_CHECKED : BST_UNCHECKED); + if (! fDoAdvanceTo) + spin->Disable(); + + spin = SetupIntSpinner(fDlg, IDC_REGRESS_STAGE_SPIN, IDC_REGRESS_STAGE_EDIT, -1, 100, fRegressTo); + CheckDlgButton(fDlg, IDC_DO_REGRESS_TO, fDoRegressTo ? BST_CHECKED : BST_UNCHECKED); + if (! fDoRegressTo) + spin->Disable(); + + CheckDlgButton(fDlg, IDC_GLOBAL_COORD, fUseGlobalCoord ? BST_CHECKED : BST_UNCHECKED); +} + +plAnimStage* plStandardStage::CreateStage() +{ + int loopCount = fLoopForever ? -1 : fNumLoops; + plAnimStage* stage = TRACKED_NEW plAnimStage(fAnimName, + fNotify, + (plAnimStage::ForwardType)fForward, + (plAnimStage::BackType)fBackward, + (plAnimStage::AdvanceType)fStageAdvance, + (plAnimStage::RegressType)fStageRegress, + loopCount, + fDoAdvanceTo, + fAdvanceTo, + fDoRegressTo, + fRegressTo); + + return stage; +} + +plBaseStage* plStandardStage::Clone() +{ + plStandardStage* clone = TRACKED_NEW plStandardStage; + clone->fAnimName = hsStrcpy(fAnimName); + clone->fNumLoops = fNumLoops; + clone->fLoopForever = fLoopForever; + clone->fForward = fForward; + clone->fBackward = fBackward; + clone->fStageAdvance = fStageAdvance; + clone->fStageRegress = fStageRegress; + clone->fNotify = fNotify; + clone->fUseGlobalCoord = fUseGlobalCoord; + + IBaseClone(clone); + + return clone; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageStage.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageStage.h new file mode 100644 index 00000000..ec406c5c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plMultistageStage.h @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsWindows.h" + +class hsStream; +class plAnimStage; + +enum StageTypes +{ + // Data for the multistage + kMultiStage, + + // Stage types + kStandard +}; + +class plBaseStage +{ +protected: + char* fName; + + static BOOL CALLBACK IStaticDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + virtual BOOL IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + HWND ICreateDlg(int dialogID, char* title); + void IDestroyDlg(HWND hDlg); + + void IBaseClone(plBaseStage* clone); + +public: + plBaseStage(); + virtual ~plBaseStage(); + + // From StageTypes + virtual int GetType()=0; + + // Derived classes need to call this from their implementation + virtual void Read(hsStream *stream); + virtual void Write(hsStream *stream); + + virtual void CreateDlg()=0; + virtual void DestroyDlg()=0; + + virtual plAnimStage* CreateStage()=0; + + virtual plBaseStage* Clone()=0; + + const char* GetName(); + void SetName(const char* name); +}; + +class plStandardStage : public plBaseStage +{ +protected: + static HWND fDlg; + + char *fAnimName; + UInt32 fNumLoops; + bool fLoopForever; + UInt8 fForward; + UInt8 fBackward; + UInt8 fStageAdvance; + UInt8 fStageRegress; + UInt8 fNotify; + bool fUseGlobalCoord; + bool fDoAdvanceTo; + UInt32 fAdvanceTo; + bool fDoRegressTo; + UInt32 fRegressTo; + + BOOL IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + void IInitDlg(); + + void IGetAnimName(); + +public: + plStandardStage(); + ~plStandardStage(); + + int GetType() { return kStandard; } + + void Read(hsStream *stream); + void Write(hsStream *stream); + + void CreateDlg(); + void DestroyDlg(); + + plAnimStage* CreateStage(); + + plBaseStage* Clone(); +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNPCSpawnComp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNPCSpawnComp.cpp new file mode 100644 index 00000000..0526e3ff --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNPCSpawnComp.cpp @@ -0,0 +1,213 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plNPCSpawnComp.h" + +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "plActivatorBaseComponent.h" + +#include "../MaxMain/plMaxNode.h" + +#include "../plAvatar/plNPCSpawnMod.h" + +#include "../pnMessage/plNotifyMsg.h" + +#include + + +// Keep from getting dead-code-stripped +void DummyCodeIncludFuncNPCSpawn() {} + +// DON'T delete from this enum. Just add _DEAD to the end of the name +enum +{ + kModelName, // v1 + kAccountName, // v1 + kAutoSpawn, // v1 +}; + +/** \class plNPCSpawnComp + Simply creates a plNPCSpawnMod and applies it to the scene node. +*/ +class plNPCSpawnComp : public plActivatorBaseComponent +{ +public: + plNPCSpawnComp(); + + plKey GetNPCSpawnKey(plMaxNode *node); + + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode* node,plErrorMsg *pErrMsg); + +private: + // per-instance registry of all the modifiers that were created by this component + typedef std::map modmap; + modmap fMods; + bool IIsValid(); +}; + +// Max class descriptor. +CLASS_DESC(plNPCSpawnComp, gNPCSpawnDesc, "NPC Spawner", "NPC Spawner", COMP_TYPE_AVATAR, NPC_SPAWN_CLASS_ID ) + +// GETNPCSPAWNKEY +// The idea here is to allow access to all the modifiers that +// were created by a given npc spawner component. +plKey GetNPCSpawnModKey(plComponentBase *npcSpawnComp, plMaxNodeBase *target) +{ + if (npcSpawnComp->ClassID() == NPC_SPAWN_CLASS_ID) + { + plNPCSpawnComp *comp = (plNPCSpawnComp*)npcSpawnComp; + return comp->GetNPCSpawnKey((plMaxNode*)target); + } + + return nil; +} + +// GNPCSPAWNBLOCK +ParamBlockDesc2 gNPCSpawnBlock +( + plComponent::kBlkComp, _T("(ex)One Shot Comp"), 0, &gNPCSpawnDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Rollout data + IDD_COMP_NPC_SPAWN, IDS_COMP_NPC_SPAWNER, 0, 0, NULL, + + //params + kModelName, _T("ModelName"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_NPC_SPAWN_MODEL_TEXT_BOX, + end, + + //params + kAccountName, _T("AccountName"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_NPC_SPAWN_ACCOUNT_TEXT_BOX, + end, + + kAutoSpawn, _T("AutoSpawn"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_NPC_SPAWN_AUTOSPAWN_BOOL, + end, + + end +); + +plNPCSpawnComp::plNPCSpawnComp() +{ + fClassDesc = &gNPCSpawnDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// GETNPCSPAWNKEY +plKey plNPCSpawnComp::GetNPCSpawnKey(plMaxNode *node) +{ + if (fMods.find(node) != fMods.end()) + return fMods[node]->GetKey(); + + return nil; +} + +// ISVALID +bool plNPCSpawnComp::IIsValid() +{ + const char *modelName = fCompPB->GetStr(kModelName); + // account name is optional (for the moment) + //const char *account = fCompPB->GetStr(kAccountName); + return (modelName && *modelName != '\0'); +} + +// SETUPPROPERTIES +hsBool plNPCSpawnComp::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fMods.clear(); // clear out our cache of modifiers (used for interfacing to python & responder) + + if (IIsValid()) + { + node->SetForceLocal(true); + return true; + } + else + { + if (pErrMsg->Set(true, "NPC Spawner", "NPC Spawn component on '%s' has no animation name, and will not be included in the export. Abort this export?", node->GetName()).Ask()) + pErrMsg->Set(true, "", ""); + else + pErrMsg->Set(false); // Don't want to abort + return false; + } +} + +// PRECONVERT +// We actually do the convert here so we're around for responder & python fixup +hsBool plNPCSpawnComp::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (IIsValid()) + { + const char *modelName = fCompPB->GetStr(kModelName); + const char *accountName = fCompPB->GetStr(kAccountName); + bool autoSpawn = fCompPB->GetInt(kAutoSpawn) ? true : false; + + plNPCSpawnMod *mod = TRACKED_NEW plNPCSpawnMod(modelName, accountName, autoSpawn); + fMods[node] = mod; + + // this is used by the python file modifier to figure out which component we're coming from + plKey modKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), mod, node->GetLocation()); + fLogicModKeys[node] = modKey; + } + + return true; +} + +// CONVERT +// We just attach to the scene object here. Not sure why we didn't do it at convert time; +// I'm cribbing from Colin's modifications to the one shot component. +hsBool plNPCSpawnComp::Convert(plMaxNode* node, plErrorMsg *pErrMsg) +{ + modmap::iterator i = fMods.find(node); + + if (i != fMods.end()) + { + plNPCSpawnMod *mod = ((*i).second); + + // let's make a notification message that we'll use to notify interested parties + // when we actually do our spawn. + plNotifyMsg *notify = TRACKED_NEW plNotifyMsg(); + hsTArray receivers; + IGetReceivers(node, receivers); + notify->SetSender(mod->GetKey()); + notify->SetState(1.0f); + for (int i = 0; i < receivers.Count(); i++) + notify->AddReceiver(receivers[i]); + + mod->SetNotify(notify); + + node->AddModifier(mod, IGetUniqueName(node)); + + return true; + } + + return false; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNPCSpawnComp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNPCSpawnComp.h new file mode 100644 index 00000000..34bcc2ed --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNPCSpawnComp.h @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "max.h" +#include "../pnKeyedObject/plKey.h" + +class plComponentBase; +class plMaxNodeBase; + +#define NPC_SPAWN_CLASS_ID Class_ID(0x784e3345, 0x2ed0288c) + +plKey GetNPCSpawnModKey(plComponentBase *npcSpawnComp, plMaxNodeBase *target); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNavigableComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNavigableComponents.cpp new file mode 100644 index 00000000..4f27a173 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNavigableComponents.cpp @@ -0,0 +1,280 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "resource.h" // Resource Dependencies + +#include "../MaxMain/plPhysicalProps.h" + +#include "plComponent.h" //Component Dependencies +#include "plComponentReg.h" // Ibid +#include "../MaxMain/plMaxNode.h" // Ibid +#include "../pnKeyedObject/plKey.h" // Ibid +#include "plComponentProcBase.h" + +#include "plNavigableComponents.h" +#include "plActivatorBaseComponent.h" +#include "plPhysicalComponents.h" + +#include "../MaxConvert/hsConverterUtils.h" //Conversion Dependencies +#include "../MaxConvert/hsControlConverter.h" // Ibid + +#include "../plAvatar/plAvLadderModifier.h" //Modifier Dependencies +#include "../plPhysical/plSimDefs.h" + +#include "plgDispatch.h" //Message Dependencies +#include "../pnMessage/plObjRefMsg.h" // Ibid +#include "../pnMessage/plIntRefMsg.h" // Ibid +#include "../pnMessage/plNodeRefMsg.h" // Ibid +#include "../MaxMain/plPlasmaRefMsgs.h" // Ibid + +void DummyCodeIncludeFuncNavigablesRegion() {} + + +CLASS_DESC(plAvLadderComponent, gAvLadderComponentDesc, "(ex)Ladder Component", "(ex)LadderComp", COMP_TYPE_PHYS_TERRAINS, NAV_LADDER_CID) + +class plAvLadderComponentProc; +extern plAvLadderComponentProc gAvLadderComponentProc; + +enum kAvLadderFields +{ + kTypeCombo, + kLoopsInt, + kTriggerNode, + kDirectionBool, + kBoundsType_DEAD, + kEnabled, + kLadderNode, +}; + +ParamBlockDesc2 gAvLadderComponentBlock +( + plComponent::kBlkComp, _T("(ex)Ladder Component"), 0, &gAvLadderComponentDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_NAV_LADDER, IDS_COMP_NAV_LADDERS, 0, 0, &gAvLadderComponentProc, + + kTypeCombo, _T("Ladder Type"), TYPE_INT, 0,0, + end, + + kDirectionBool, _T("Climbing Direction"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 2, IDC_RADIO_UP, IDC_RADIO_DOWN, + p_vals, true, false, + end, + + kLoopsInt, _T("BigLadderNumLoop"), TYPE_INT, 0, 0, + p_default, 0, + p_range, 0, 500, + p_ui, TYPE_SPINNER, EDITTYPE_INT, + IDC_COMP_NAV_LADDER_LOOPS_EDIT, IDC_COMP_NAV_LADDER_LOOPS_SPIN, 0.4, + end, + + kTriggerNode, _T("Trigger Node"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_NAV_TRIGGER, + //p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + //p_accessor, &gPhysCoreAccessor, + end, + + kLadderNode, _T("ladder"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_NAV_LADDER, + end, + + kEnabled, _T("enabled"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_ENABLED, + p_default, TRUE, + end, + + end +); + +plAvLadderComponent::plAvLadderComponent() +{ + fClassDesc = &gAvLadderComponentDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +void plAvLadderComponent::CollectNonDrawables(INodeTab& nonDrawables) +{ + INode* ladderNode = fCompPB->GetINode(kLadderNode); + if( ladderNode ) + nonDrawables.Append(1, &ladderNode); + + INode* triggerNode = fCompPB->GetINode(kTriggerNode); + if( triggerNode ) + nonDrawables.Append(1, &triggerNode); + + AddTargetsToList(nonDrawables); +} + +hsBool plAvLadderComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fKeys.Reset(); + + // + // Create an invisible blocker for the ladder shape, so the avatar won't fall over the side + // + plMaxNode *ladderNode = (plMaxNode*)fCompPB->GetINode(kLadderNode); + if (ladderNode) + { + plPhysicalProps* ladderPhys = ladderNode->GetPhysicalProps(); +// ladderPhys->SetMass(0, ladderNode, pErrMsg); + ladderPhys->SetBoundsType(plSimDefs::kHullBounds, ladderNode, pErrMsg); + ladderPhys->SetGroup(plSimDefs::kGroupStatic, ladderNode, pErrMsg); +/// ladderPhys->SetAllowLOS(true, ladderNode, pErrMsg); + + ladderNode->SetDrawable(false); + ladderNode->SetForceLocal(true); // Get a coord interface for facing calculations + } + else + { + pErrMsg->Set(true, + "Ladder Warning", + "Ladder component %s doesn't have the ladder node set", + GetINode()->GetName()).Show(); + pErrMsg->Set(false); + return false; + } + + // + // Create a detector region for the node we're attached to + // + plPhysicalProps *physProps = node->GetPhysicalProps(); + + plMaxNode *triggerNode = (plMaxNode*)fCompPB->GetINode(kTriggerNode); + if (triggerNode) + physProps->SetProxyNode(triggerNode, node, pErrMsg); + + physProps->SetGroup(plSimDefs::kGroupDetector, node, pErrMsg); // this is a detector + physProps->SetReportGroup(1<SetPinned(true, node, pErrMsg); + // only if movable will it have mass (then it will keep track of movements in PhysX) + if ( node->IsMovable() || node->IsTMAnimated() ) + physProps->SetMass(1.0f, node, pErrMsg); // detectors don't move + physProps->SetBoundsType(plSimDefs::kHullBounds, node, pErrMsg); + + node->SetForceLocal(true); // force our seek point to be local + node->SetDrawable(false); + + return true; +} + +hsBool plAvLadderComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plMaxNode *ladderNode = (plMaxNode*)fCompPB->GetINode(kLadderNode); + if (!ladderNode) + return false; + + plMaxNode *triggerNode = (plMaxNode*)fCompPB->GetINode(kTriggerNode); + if (!triggerNode) + triggerNode = node; + + // Get a vector pointing from the ladder to the detector, for facing calculations + Point3 ladderViewMax = ladderNode->GetNodeTM(0).GetTrans() - triggerNode->GetNodeTM(0).GetTrans(); + hsVector3 ladderView(ladderViewMax.x, ladderViewMax.y, ladderViewMax.z); + ladderView.fZ = 0; + ladderView.Normalize(); + + bool goingUp = (fCompPB->GetInt(kDirectionBool) != 0); + int loops = fCompPB->GetInt(kLoopsInt); + int ladderType = fCompPB->GetInt(kTypeCombo); + bool enabled = (fCompPB->GetInt(kEnabled) != 0); + + plAvLadderMod* ladMod = TRACKED_NEW plAvLadderMod(goingUp, ladderType, loops, enabled, ladderView); + plKey modKey = node->AddModifier(ladMod, IGetUniqueName(node)); + fKeys.Append(modKey); + + return true; +} + +hsBool plAvLadderComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plAvLadderComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fKeys.Reset(); + return true; +} + +class plAvLadderComponentProc : public ParamMap2UserDlgProc +{ +public: + enum kLadderTypesEnums + { + kReallyBig, + kFourFeet, + kTwoFeet, + }; + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + HWND hLadder = GetDlgItem(hWnd,IDC_COMP_NAV_LADDER_COMBO); + + ComboBox_AddString(hLadder, "Big"); + ComboBox_AddString(hLadder, "4 feet"); + ComboBox_AddString(hLadder, "2 feet"); + + int type = map->GetParamBlock()->GetInt(kTypeCombo); + ComboBox_SetCurSel(hLadder, type); + } + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDC_COMP_NAV_LADDER_COMBO && HIWORD(wParam) == CBN_SELCHANGE) + { + //Util fcn found in plEventGroupRefs files in MaxMain + HWND hLadder = GetDlgItem(hWnd,IDC_COMP_NAV_LADDER_COMBO); + int idx = ComboBox_GetCurSel(hLadder); + map->GetParamBlock()->SetValue(kTypeCombo, 0, idx); +/* + if (idx == kReallyBig) + { + map->Enable(kLoopsInt, TRUE); +// map->Invalidate(kLoopsInt); + } + else + { + map->Enable(kLoopsInt, TRUE); +// map->Invalidate(kLoopsInt); + } +*/ + return TRUE; + } + break; + } + return FALSE; + } + + void DeleteThis() {} +}; +static plAvLadderComponentProc gAvLadderComponentProc; + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNavigableComponents.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNavigableComponents.h new file mode 100644 index 00000000..b1f607cf --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNavigableComponents.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plAvLadderComponent_h_inc +#define plAvLadderComponent_h_inc + +#include "plComponent.h" +#include "../pnKeyedObject/plKey.h" + +class plComponentBase; + + +#define NAV_LADDER_CID Class_ID(0x6b010148, 0x47cc7464) + +class plAvLadderComponent : public plComponent +{ +public: + typedef hsTArray LadderModKeys; + +protected: + LadderModKeys fKeys; + +public: + plAvLadderComponent(); + + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + // Call after PreConvert + const LadderModKeys& GetLadderModKeys() { return fKeys; } + + virtual void CollectNonDrawables(INodeTab& nonDrawables); +}; + +#endif // plAvLadderComponent_h_inc + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNoteTrackDlgComp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNoteTrackDlgComp.cpp new file mode 100644 index 00000000..ef73b2de --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNoteTrackDlgComp.cpp @@ -0,0 +1,44 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plNoteTrackDlgComp.h" +#include "plComponentBase.h" + +void plComponentNoteTrackDlg::ICacheNoteTrack() +{ + DeleteCache(); + + plComponentBase *comp = (plComponentBase*)fOwner; + + std::vector targets; + for (int i = 0; i < comp->NumTargets(); i++) + { + if (comp->GetTarget(i)) + targets.push_back((Animatable*)comp->GetTarget(i)); + } + + fSegMap = GetSharedAnimSegmentMap(targets, nil); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNoteTrackDlgComp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNoteTrackDlgComp.h new file mode 100644 index 00000000..b40ba3e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNoteTrackDlgComp.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLCOMPONENTNOTETRACKDLG_INC +#define PLCOMPONENTNOTETRACKDLG_INC + +#include "plNoteTrackDlg.h" + +// Use this when owner is a component and you want the shared notetracks from all +// the objects it is applied to. (This is in a seperate file because it uses +// plComponentBase and external stuff might want to use plNoteTrackDlg.) +class plComponentNoteTrackDlg : public plNoteTrackDlg +{ + void ICacheNoteTrack(); +}; + + + +#endif // PLCOMPONENTNOTETRACKDLG_INC \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackAnim.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackAnim.cpp new file mode 100644 index 00000000..ce6c5b92 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackAnim.cpp @@ -0,0 +1,250 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plNotetrackAnim.h" +#include "plComponentBase.h" +#include "../MaxMain/plMaxNodeBase.h" + +plNotetrackAnim::plNotetrackAnim() : fSegMap(nil) +{ +} + +plNotetrackAnim::plNotetrackAnim(Animatable *anim, plErrorMsg *pErrMsg) +{ + if (anim->SuperClassID() == HELPER_CLASS_ID && + ((Object*)anim)->CanConvertToType(COMPONENT_CLASSID)) + { + plComponentBase *comp = (plComponentBase*)anim; + std::vector targs; + + for (int i = 0; i < comp->NumTargets(); i++) + { + plMaxNodeBase *node = comp->GetTarget(i); + if (node) + targs.push_back(node); + } + + fSegMap = GetSharedAnimSegmentMap(targs, pErrMsg); + } + else + { + fSegMap = GetAnimSegmentMap(anim, pErrMsg); + } + + if (fSegMap) + fAnimIt = fSegMap->begin(); +} + +plNotetrackAnim::~plNotetrackAnim() +{ + DeleteSegmentMap(fSegMap); +} + +const char *plNotetrackAnim::GetNextAnimName() +{ + if (!fSegMap) + return nil; + + while (fAnimIt != fSegMap->end()) + { + SegmentSpec *spec = fAnimIt->second; + fAnimIt++; + + if (spec->fType == SegmentSpec::kAnim) + return spec->fName; + } + + fAnimIt = fSegMap->begin(); + return nil; +} + +plAnimInfo plNotetrackAnim::GetAnimInfo(const char *animName) +{ + if (!fSegMap) + return plAnimInfo(); + + if (!animName || *animName == '\0' || fSegMap->find(animName) == fSegMap->end()) + return plAnimInfo(fSegMap, nil); + else + return plAnimInfo(fSegMap, animName); + + return plAnimInfo(); +} + +//////////////////////////////////////////////////////////////////////////////// + +plAnimInfo::plAnimInfo(SegmentMap *segMap, const char *animName) +{ + fSegMap = segMap; + fAnimSpec = animName ? (*fSegMap)[animName] : nil; + + if (fSegMap) + { + fLoopIt = fSegMap->begin(); + fMarkerIt = fSegMap->begin(); + fStopPointIt = fSegMap->begin(); + } +} + +const char *plAnimInfo::GetAnimName() +{ + return fAnimSpec ? fAnimSpec->fName : nil; +} + +float plAnimInfo::GetAnimStart() +{ + return fAnimSpec ? fAnimSpec->fStart : -1; +} + +float plAnimInfo::GetAnimEnd() +{ + return fAnimSpec ? fAnimSpec->fEnd : -1; +} + +float plAnimInfo::GetAnimInitial() +{ + return fAnimSpec ? fAnimSpec->fInitial : -1; +} + +const char *plAnimInfo::GetNextLoopName() +{ + if (!fSegMap) + return nil; + + while (fLoopIt != fSegMap->end()) + { + SegmentSpec *spec = fLoopIt->second; + fLoopIt++; + + if (spec->fType == SegmentSpec::kLoop && + (!fAnimSpec || fAnimSpec->Contains(spec))) + return spec->fName; + } + + fLoopIt = fSegMap->begin(); + return nil; +} + +float plAnimInfo::GetLoopStart(const char *loopName) +{ + if (!fSegMap || loopName == nil) + return -1; + + if (fSegMap->find(loopName) != fSegMap->end()) + { + SegmentSpec *spec = (*fSegMap)[loopName]; + if (spec->fStart == -1) + return -1;//GetAnimStart(); + else + return spec->fStart; + } + + return -1; +} + +float plAnimInfo::GetLoopEnd(const char *loopName) +{ + if (!fSegMap || loopName == nil) + return -1; + + if (fSegMap->find(loopName) != fSegMap->end()) + { + SegmentSpec *spec = (*fSegMap)[loopName]; + if (spec->fEnd == -1) + return -1;//GetAnimEnd(); + else + return spec->fEnd; + } + + return -1; +} + +const char *plAnimInfo::GetNextMarkerName() +{ + if (!fSegMap) + return nil; + + while (fMarkerIt != fSegMap->end()) + { + SegmentSpec *spec = fMarkerIt->second; + fMarkerIt++; + + if (spec->fType == SegmentSpec::kMarker && + (!fAnimSpec || fAnimSpec->Contains(spec))) + return spec->fName; + } + + fMarkerIt = fSegMap->begin(); + return nil; +} + +float plAnimInfo::GetMarkerTime(const char *markerName) +{ + if (!fSegMap) + return -1; + + if (fSegMap->find(markerName) != fSegMap->end()) + { + SegmentSpec *spec = (*fSegMap)[markerName]; + return spec->fStart; + } + + return -1; +} + +float plAnimInfo::GetNextStopPoint() +{ + if (!fSegMap) + return -1; + + while (fStopPointIt != fSegMap->end()) + { + SegmentSpec *spec = fStopPointIt->second; + fStopPointIt++; + + if (spec->fType == SegmentSpec::kStopPoint && + (!fAnimSpec || fAnimSpec->Contains(spec))) + return spec->fStart; + } + + fStopPointIt = fSegMap->begin(); + return -1; +} + +bool plAnimInfo::IsSuppressed(const char *animName) +{ + if (!fSegMap || animName == nil) + return false; + + if (fSegMap->find(animName) != fSegMap->end()) + { + SegmentSpec *spec = (*fSegMap)[animName]; + if (spec->fType == SegmentSpec::kSuppress) + return true; + } + + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackAnim.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackAnim.h new file mode 100644 index 00000000..e022cf86 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackAnim.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_NOTETRACK_ANIM +#define PL_NOTETRACK_ANIM + +#include "plMaxAnimUtils.h" + +// WIP wrapper class to make plMaxAnimUtils easier to use. +class plAnimInfo +{ +protected: + SegmentMap *fSegMap; + SegmentSpec *fAnimSpec; + SegmentMap::iterator fLoopIt; + SegmentMap::iterator fMarkerIt; + SegmentMap::iterator fStopPointIt; + +public: + plAnimInfo() : fSegMap(NULL), fAnimSpec(NULL) {} + plAnimInfo(SegmentMap *segMap, const char *animName); + + const char *GetAnimName(); + float GetAnimStart(); + float GetAnimEnd(); + float GetAnimInitial(); + + const char *GetNextLoopName(); + float GetLoopStart(const char *loopName); + float GetLoopEnd(const char *loopName); + + const char *GetNextMarkerName(); + float GetMarkerTime(const char *markerName); + + float GetNextStopPoint(); // Returns -1 on last stop point + bool IsSuppressed(const char *animName); +}; + +class plNotetrackAnim +{ +protected: + SegmentMap *fSegMap; + SegmentMap::iterator fAnimIt; + + plNotetrackAnim(); + +public: + plNotetrackAnim(Animatable *anim, plErrorMsg *pErrMsg); + ~plNotetrackAnim(); + + bool HasNotetracks() { return (fSegMap != NULL); } + + const char *GetNextAnimName(); + plAnimInfo GetAnimInfo(const char *animName); +}; + +#endif //PL_NOTETRACK_ANIM \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackDlg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackDlg.cpp new file mode 100644 index 00000000..3a6e00f3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackDlg.cpp @@ -0,0 +1,183 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plNoteTrackDlg.h" +#include "plNotetrackAnim.h" + +#include "max.h" +#include "iparamb2.h" +#include "../plInterp/plAnimEaseTypes.h" + +plNoteTrackDlg::plNoteTrackDlg() : fhAnim(NULL), fhLoop(NULL), fPB(nil), fAnimID(-1), fLoopID(-1), fSegMap(nil), fOwner(nil) +{ +} + +plNoteTrackDlg::~plNoteTrackDlg() +{ + DeleteCache(); +} + +void plNoteTrackDlg::Init(HWND hAnim, HWND hLoop, int animID, int loopID, IParamBlock2 *pb, Animatable *owner) +{ + fhAnim = hAnim; + fhLoop = hLoop; + fPB = pb; + fAnimID = animID; + fLoopID = loopID; + fOwner = owner; +} + +void plNoteTrackDlg::ICacheNoteTrack() +{ + DeleteCache(); + + fSegMap = GetAnimSegmentMap(fOwner, nil); +} + +void plNoteTrackDlg::DeleteCache() +{ + if (fSegMap) + { + DeleteSegmentMap(fSegMap); + fSegMap = nil; + } +} + +void plNoteTrackDlg::Load() +{ + ICacheNoteTrack(); + + ILoadAnims(); + ILoadLoops(); +} + +void plNoteTrackDlg::AnimChanged() +{ + const char *animName = IGetSel(fhAnim); + fPB->SetValue(fAnimID, 0, (TCHAR*)animName); + + ILoadLoops(); +} + +void plNoteTrackDlg::LoopChanged() +{ + const char *loopName = IGetSel(fhLoop); + fPB->SetValue(fLoopID, 0, (TCHAR*)loopName); +} + +void plNoteTrackDlg::ILoadAnims() +{ + if(fAnimID < 0 || !fhAnim) + return; + + ComboBox_ResetContent(fhAnim); + + // Add the default option + int def = ComboBox_AddString(fhAnim, ENTIRE_ANIMATION_NAME); + ComboBox_SetItemData(fhAnim, def, kDefault); + ComboBox_SetCurSel(fhAnim, def); + + if (!fSegMap) + return; + + const char *savedAnim = fPB->GetStr(fAnimID); + if (!savedAnim) + savedAnim = ""; + + // Add the names of the animations + for (SegmentMap::iterator it = fSegMap->begin(); it != fSegMap->end(); it++) + { + SegmentSpec *spec = it->second; + if (spec->fType == SegmentSpec::kAnim) + { + int idx = ComboBox_AddString(fhAnim, spec->fName); + ComboBox_SetItemData(fhAnim, idx, kName); + + // If this is the saved animation name, select it + if (!strcmp(spec->fName, savedAnim)) + ComboBox_SetCurSel(fhAnim, idx); + } + } +} + +void plNoteTrackDlg::ILoadLoops() +{ + if(fLoopID < 0 || !fhLoop) + return; + + ComboBox_ResetContent(fhLoop); + + // Add the default option + int def = ComboBox_AddString(fhLoop, ENTIRE_ANIMATION_NAME); + ComboBox_SetItemData(fhLoop, def, kDefault); + ComboBox_SetCurSel(fhLoop, def); + + if (fSegMap) + { + // Get the animation segment (or leave it nil if we're using the entire animation) + SegmentSpec *animSpec = nil; + const char *animName = fPB->GetStr(fAnimID); + if (animName && *animName != '\0' && fSegMap->find(animName) != fSegMap->end()) + animSpec = (*fSegMap)[animName]; + + // Get the saved loop name + const char *loopName = fPB->GetStr(fLoopID); + if (!loopName) + loopName = ""; + + for (SegmentMap::iterator i = fSegMap->begin(); i != fSegMap->end(); i++) + { + SegmentSpec *spec = i->second; + + if (spec->fType == SegmentSpec::kLoop) + { + // If the loop is contained by the animation, add it + if (!animSpec || animSpec->Contains(spec)) + { + // Add the name + int idx = ComboBox_AddString(fhLoop, spec->fName); + ComboBox_SetItemData(fhLoop, idx, kName); + + if (!strcmp(loopName, spec->fName)) + ComboBox_SetCurSel(fhLoop, idx); + } + } + } + } +} + +const char *plNoteTrackDlg::IGetSel(HWND hCombo) +{ + int sel = ComboBox_GetCurSel(hCombo); + if (sel != CB_ERR && ComboBox_GetItemData(hCombo, sel) == kName) + { + char buf[256]; + ComboBox_GetText(hCombo, buf, sizeof(buf)); + return (*fSegMap)[buf]->fName; + } + + return ""; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackDlg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackDlg.h new file mode 100644 index 00000000..48f1c538 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plNotetrackDlg.h @@ -0,0 +1,79 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLNOTETRACKDLG_INC +#define PLNOTETRACKDLG_INC + +#include "hsTypes.h" +#include "hsWindows.h" // For HWND +#include "plMaxAnimUtils.h" + +class IParamBlock2; +class Animatable; + +class plNoteTrackDlg +{ +protected: + // Derived class needs to set these + HWND fhAnim; + HWND fhLoop; + IParamBlock2 *fPB; + int fAnimID; + int fLoopID; + Animatable *fOwner; + + SegmentMap *fSegMap; + + enum + { + kDefault, + kName, + }; + +public: + plNoteTrackDlg(); + virtual ~plNoteTrackDlg(); + + void Init(HWND hAnim, HWND hLoop, int animID, int loopID, IParamBlock2 *pb, Animatable *owner); + void Load(); + + void AnimChanged(); + void LoopChanged(); + + // This is done automatically in most cases, but if the dialog is being destroyed + // but you're not deleting the plNoteTrackDlg, call this to free cached memory. + void DeleteCache(); + +protected: + virtual void ICacheNoteTrack(); + + void ILoadAnims(); + void ILoadLoops(); + + const char *IGetSel(HWND hCombo); +}; + + +#endif // PLNOTETRACKDLG_INC \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plObjectFlockerComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plObjectFlockerComponent.cpp new file mode 100644 index 00000000..2e2f2dcb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plObjectFlockerComponent.cpp @@ -0,0 +1,271 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../plMath/plRandom.h" +#include "plObjectFlockerComponent.h" +#include "../pnKeyedObject/plUoid.h" +#include "../MaxMain/plMaxNode.h" + +#include "plPickNode.h" +#include "../pfAnimation/pfObjectFlocker.h" +#include "../MaxMain/plPluginResManager.h" +#include "../pnSceneObject/plSceneObject.h" + +void DummyCodeIncludeFuncObjectFlocker() +{ +} + +class ObjectFlockerDlgProc : public ParamMap2UserDlgProc +{ +public: + ObjectFlockerDlgProc() {} + ~ObjectFlockerDlgProc() {} + + void IUpdateNode(TimeValue t, IParamBlock2* pb, HWND hWnd, ParamID buttonID, int button) + { + INode* node = pb->GetINode(buttonID, t); + HWND hButton = GetDlgItem(hWnd, button); + + if (node) + SetWindowText(hButton, node->GetName()); + else + SetWindowText(hButton, ""); + } + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + int id = LOWORD(wParam); + + IParamBlock2 *pb = map->GetParamBlock(); + + switch (msg) + { + case WM_INITDIALOG: + IUpdateNode(t, pb, hWnd, plObjectFlockerComponent::kBoidObject, IDC_OBJ_FLOCKER_BOID_BUTTON); + + // Disable stuff that the artists shouldn't have to touch + EnableWindow(GetDlgItem(hWnd, IDC_OBJ_FLOCKER_SEP_RADIUS), false); + EnableWindow(GetDlgItem(hWnd, IDC_OBJ_FLOCKER_SEP_RADIUS_SPIN), false); + SetDlgItemText(hWnd, IDC_OBJ_FLOCKER_SEP_RADIUS, "5.0"); + + EnableWindow(GetDlgItem(hWnd, IDC_OBJ_FLOCKER_COH_RADIUS), false); + EnableWindow(GetDlgItem(hWnd, IDC_OBJ_FLOCKER_COH_RADIUS_SPIN), false); + SetDlgItemText(hWnd, IDC_OBJ_FLOCKER_COH_RADIUS, "9.0"); + + return TRUE; + } + return FALSE; + } + void DeleteThis() {} +}; +static ObjectFlockerDlgProc gObjectFlockerDlgProc; + +CLASS_DESC(plObjectFlockerComponent, gObjectFlockerDesc, "Object Flocker", "Object Flocker", COMP_TYPE_MISC, OBJECT_FLOCKER_COMPONENT_CLASS_ID) + +ParamBlockDesc2 gObjectFlockerBk +( + plComponent::kBlkComp, _T("ObjectFlocker"), 0, &gObjectFlockerDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_OBJ_FLOCKER, IDS_COMP_OBJ_FLOCKER, 0, 0, &gObjectFlockerDlgProc, + + plObjectFlockerComponent::kBoidObject, _T("BoidObject"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_OBJ_FLOCKER_BOID_BUTTON, + //p_sclassID, GEOMOBJECT_CLASS_ID, + end, + + plObjectFlockerComponent::kNumBoids, _T("NumBoids"), TYPE_INT, 0, 0, + p_default, 5, + p_range, 2, 30, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_OBJ_FLOCKER_NUM_BOIDS, IDC_OBJ_FLOCKER_NUM_BOIDS_SPIN, 1.0, + end, + + plObjectFlockerComponent::kGoalStrength, _T("GoalStrength"), TYPE_FLOAT, 0, 0, + p_default, 8.0, + p_range, 00.0, 50.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_OBJ_FLOCKER_GOAL_STRENGTH, IDC_OBJ_FLOCKER_GOAL_STRENGTH_SPIN, 1.0, + end, + + plObjectFlockerComponent::kWanderStrength, _T("WanderStrength"), TYPE_FLOAT, 0, 0, + p_default, 12.0, + p_range, 00.0, 50.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_OBJ_FLOCKER_WANDER_STRENGTH, IDC_OBJ_FLOCKER_WANDER_STRENGTH_SPIN, 1.0, + end, + + plObjectFlockerComponent::kSepStrength, _T("SeparationStrength"), TYPE_FLOAT, 0, 0, + p_default, 12.0, + p_range, 00.0, 50.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_OBJ_FLOCKER_SEP_STRENGTH, IDC_OBJ_FLOCKER_SEP_STRENGTH_SPIN, 1.0, + end, + + plObjectFlockerComponent::kSepRadius, _T("SeparationRadius"), TYPE_FLOAT, 0, 0, + p_default, 05.0, + p_range, 00.0, 50.0, + /*p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_OBJ_FLOCKER_SEP_RADIUS, IDC_OBJ_FLOCKER_SEP_RADIUS_SPIN, 1.0,*/ // Commented out so Max doesn't auto-enable these + end, + + plObjectFlockerComponent::kCohStrength, _T("CohesionStrength"), TYPE_FLOAT, 0, 0, + p_default, 08.0, + p_range, 00.0, 50.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_OBJ_FLOCKER_COH_STRENGTH, IDC_OBJ_FLOCKER_COH_STRENGTH_SPIN, 1.0, + end, + + plObjectFlockerComponent::kCohRadius, _T("CohesionRadius"), TYPE_FLOAT, 0, 0, + p_default, 09.0, + p_range, 00.0, 50.0, + /*p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_OBJ_FLOCKER_COH_RADIUS, IDC_OBJ_FLOCKER_COH_RADIUS_SPIN, 1.0,*/ // Commented out so Max doesn't auto-enable these + end, + + plObjectFlockerComponent::kMaxForce, _T("MaxForce"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 00.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_OBJ_FLOCKER_MAX_FORCE, IDC_OBJ_FLOCKER_MAX_FORCE_SPIN, 1.0, + end, + + plObjectFlockerComponent::kMaxSpeed, _T("MaxSpeed"), TYPE_FLOAT, 0, 0, + p_default, 05.0, + p_range, 00.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_OBJ_FLOCKER_SLIMIT_MAX, IDC_OBJ_FLOCKER_SLIMIT_MAX_SPIN, 1.0, + end, + + plObjectFlockerComponent::kMinSpeed, _T("MinSpeed"), TYPE_FLOAT, 0, 0, + p_default, 04.0, + p_range, 00.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_OBJ_FLOCKER_SLIMIT_MIN, IDC_OBJ_FLOCKER_SLIMIT_MIN_SPIN, 1.0, + end, + + plObjectFlockerComponent::kUseTargetRotation, _T("UseTargetRotation"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_OBJ_FLOCKER_USE_TARGET_ROTATION, + end, + + plObjectFlockerComponent::kRandomAnimStart, _T("RandomAnimStart"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_OBJ_FLOCKER_RANDOM_ANIM_START, + end, + + plObjectFlockerComponent::kHideTarget, _T("HideTarget"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_OBJ_FLOCKER_HIDE_TARGET, + end, + + end +); + +plObjectFlockerComponent::plObjectFlockerComponent() +{ + fFlocker = nil; + fClassDesc = &gObjectFlockerDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plObjectFlockerComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetDrawable(!fCompPB->GetInt(ParamID(kHideTarget))); + node->SetForceLocal(true); + + plMaxNode* targNode = (plMaxNode*)fCompPB->GetINode(kBoidObject); + if (targNode) + targNode->SetForceLocal(true); + + return true; +} + +hsBool plObjectFlockerComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (fFlocker) + delete fFlocker; + + fFlocker = TRACKED_NEW pfObjectFlocker; + hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), fFlocker, node->GetLocation(), node->GetLoadMask()); + + fFlocker->SetGoalWeight(fCompPB->GetFloat(ParamID(kGoalStrength))); + fFlocker->SetWanderWeight(fCompPB->GetFloat(ParamID(kWanderStrength))); + + fFlocker->SetSeparationWeight(fCompPB->GetFloat(ParamID(kSepStrength))); + fFlocker->SetSeparationRadius(fCompPB->GetFloat(ParamID(kSepRadius))); + + fFlocker->SetCohesionWeight(fCompPB->GetFloat(ParamID(kCohStrength))); + fFlocker->SetCohesionRadius(fCompPB->GetFloat(ParamID(kCohRadius))); + + fFlocker->SetMaxForce(fCompPB->GetFloat(ParamID(kMaxForce))); + fFlocker->SetMaxSpeed(fCompPB->GetFloat(ParamID(kMaxSpeed))); + fFlocker->SetMinSpeed(fCompPB->GetFloat(ParamID(kMinSpeed))); + + fFlocker->SetUseTargetRotation(fCompPB->GetInt(ParamID(kUseTargetRotation)) != 0); + fFlocker->SetRandomizeAnimStart(fCompPB->GetInt(ParamID(kRandomAnimStart)) != 0); + + fFlocker->SetNumBoids(fCompPB->GetInt(ParamID(kNumBoids))); + + plKey boidKey = nil; + plMaxNode* targNode = (plMaxNode*)fCompPB->GetINode(kBoidObject); + + if( targNode->CanConvert() ) + { + plSceneObject* targObj = targNode->GetSceneObject(); + if( targObj ) + { + boidKey = targObj->GetKey(); + } + } + fFlocker->SetBoidKey(boidKey); + + // Add a ref to the flocker. + fFlocker->GetKey()->RefObject(); + + return true; +} + +hsBool plObjectFlockerComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->AddModifier(fFlocker, nil); + + return true; +} + +hsBool plObjectFlockerComponent::DeInit(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( fFlocker ) + fFlocker->GetKey()->UnRefObject(); + fFlocker = nil; + + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plObjectFlockerComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plObjectFlockerComponent.h new file mode 100644 index 00000000..b7393810 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plObjectFlockerComponent.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef OBJ_FLOCKER_COMPONENT +#define OBJ_FLOCKER_COMPONENT + +class pfObjectFlocker; + +class plObjectFlockerComponent : public plComponent +{ +public: + enum + { + kBoidObject, + kNumBoids, + kGoalStrength, + kWanderStrength, + kSepStrength, + kSepRadius, + kCohStrength, + kCohRadius, + kMaxForce, + kMaxSpeed, + kMinSpeed, + kUseTargetRotation, + kRandomAnimStart, + kHideTarget, + }; + + plObjectFlockerComponent(); + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool DeInit(plMaxNode* node, plErrorMsg* pErrMsg); + +protected: + pfObjectFlocker* fFlocker; + +}; + +const Class_ID OBJECT_FLOCKER_COMPONENT_CLASS_ID(0x79013d96, 0x7dca7842); + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plOneShotComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plOneShotComponent.cpp new file mode 100644 index 00000000..326cb177 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plOneShotComponent.cpp @@ -0,0 +1,257 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plOneShotComponent.h" + +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "../MaxMain/plMaxNode.h" +#if 0 +#include "../MaxConvert/hsConverterUtils.h" + +#include "../pnSceneObject/plSceneObject.h" + +#include "plgDispatch.h" +#include "../plScene/plSceneNode.h" +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../pnKeyedObject/plKey.h" + + +#include "../PubUtilLib/plResMgr/plLoc.h" + + +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plIntRefMsg.h" + +#include "plMaxAnimUtils.h" +#include "tempAnim.h" +#include "../plInterp/plController.h" +#include "../plHavok1/plHKPhysical.h" +#include "../plAvatar/plAvatarMod.h" +#include "../plModifier/plAliasModifier.h" +#include "../plAudible/plWinAudible.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#endif + +#include "hsResMgr.h" +#include "../plAvatar/plOneShotMod.h" + +#include + + +void DummyCodeIncludeFuncSingleSht() {} + +// +// Enum field for OneShot +// Never delete a field in this enum. Order is necessary for backward compatability. +// Append only. +// +enum +{ + kAnimName, // Insert in v1 + kStartPtBool_DEAD, // Insert in v1 // obsolete + kStartPt_DEAD, // Insert in v1 // obsolete + kTriggerVolBool, // Insert in v1 + kTriggerVolume, // Insert in v1 + kPlayBackwardsBool, // Insert in v1 + kControlSpeedBool, // Insert in v1 + kSeekTimeFloat, // Insert in v2 + kSmartSeekBool, // Insert in v3 + kNoSeekBool + +}; + + +// +// OneShot class +// Functions of PreConvert and Convert are currently empty and ready to be filled +// when functionality is in. +// + +class plOneShotComponent : public plComponent +{ +public: + plOneShotComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode* node,plErrorMsg *pErrMsg); + + plKey GetOneShotKey(plMaxNode *node); + +protected: + std::map fMods; + bool IsValid(); +}; + +plKey OneShotComp::GetOneShotKey(plComponentBase *oneShotComp, plMaxNodeBase *target) +{ + if (oneShotComp->ClassID() == ONESHOTCLASS_ID) + { + plOneShotComponent *comp = (plOneShotComponent*)oneShotComp; + return comp->GetOneShotKey((plMaxNode*)target); + } + + return nil; +} + + +// +// Macro for creation of Components. +// +CLASS_DESC(plOneShotComponent, gOneShotDesc, "(ex)One Shot", "OneShot", COMP_TYPE_AVATAR, ONESHOTCLASS_ID) + +// +// OneShot Paramblock2 +// If functionality is no longer necessary, remove it here. Don't touch the +// order of the Enum field mentioned above. +// +ParamBlockDesc2 gOneShotBlock +( + plComponent::kBlkComp, _T("(ex)One Shot Comp"), 0, &gOneShotDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Rollout data + IDD_COMP_ONESHOT, IDS_COMP_ONESHOTS, 0, 0, NULL, + + //params + kAnimName, _T("AnimationName"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_COMP_ONESHOT_ANIM_TEXTBOX, + end, + + kPlayBackwardsBool, _T("PlayBackwardsBool"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_ONESHOT_PLAY_BACK_BOOL, + end, + + kControlSpeedBool, _T("ControlSpeedBool"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_ONESHOT_CONT_SPEED_BOOL, + end, + + kSeekTimeFloat, _T("SeekTimeFloat"), TYPE_FLOAT, 0, 0, + p_default, 1.0f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ONESHOT_SEEK_FIELD_EDIT, IDC_COMP_ONESHOT_SEEK_FIELD_SPIN, .1f, + end, + + kSmartSeekBool, _T("SmartSeekBool"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_SMART_SEEK, + end, + + kNoSeekBool, _T("NoSeekBool"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_NO_SEEK, + end, + end +); + + +plOneShotComponent::plOneShotComponent() +{ + fClassDesc = &gOneShotDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +plKey plOneShotComponent::GetOneShotKey(plMaxNode *node) +{ + if (fMods.find(node) != fMods.end()) + return fMods[node]->GetKey(); + + return nil; +} + +bool plOneShotComponent::IsValid() +{ + const char *animName = fCompPB->GetStr(kAnimName); + return (animName && *animName != '\0'); +} + +hsBool plOneShotComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fMods.clear(); + + if (IsValid()) + { + node->SetForceLocal(true); + return true; + } + else + { + if (pErrMsg->Set(true, "One-Shot", "One-shot component on '%s' has no animation name, and will not be included. Abort this export?", node->GetName()).Ask()) + pErrMsg->Set(true, "", ""); + else + pErrMsg->Set(false); // Don't want to abort + return false; + } +} + +// +// PreConvert done below +// +hsBool plOneShotComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (IsValid()) + { + plOneShotMod *mod = TRACKED_NEW plOneShotMod; + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), mod, node->GetLocation()); + fMods[node] = mod; + } + + return true; +} + +// +// Convert Done below +// +hsBool plOneShotComponent::Convert(plMaxNode* node, plErrorMsg *pErrMsg) +{ + if (fMods.find(node) != fMods.end()) + { + const char *animName = fCompPB->GetStr(kAnimName); + hsBool drivable = fCompPB->GetInt(kControlSpeedBool); + hsBool reversable = fCompPB->GetInt(kPlayBackwardsBool); + float seekDuration = fCompPB->GetFloat(kSeekTimeFloat); + hsBool smartSeek = fCompPB->GetInt(kSmartSeekBool); + hsBool noSeek = fCompPB->GetInt(kNoSeekBool); + + plOneShotMod *mod = fMods[node]; + mod->Init(animName, drivable, reversable, seekDuration, smartSeek, noSeek); + node->AddModifier(mod, IGetUniqueName(node)); + + return true; + } + + return false; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plOneShotComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plOneShotComponent.h new file mode 100644 index 00000000..e4f34c38 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plOneShotComponent.h @@ -0,0 +1,37 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "max.h" +#include "../pnKeyedObject/plKey.h" + +class plComponentBase; +class plMaxNodeBase; + +#define ONESHOTCLASS_ID Class_ID(0x1efd285a, 0x11ba00a2) + +namespace OneShotComp +{ + plKey GetOneShotKey(plComponentBase *oneShotComp, plMaxNodeBase *target); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plParticleComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plParticleComponents.cpp new file mode 100644 index 00000000..4fa74fa9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plParticleComponents.cpp @@ -0,0 +1,1611 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "plParticleComponents.h" +#include "plAnimComponent.h" +#include "plNotetrackAnim.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../plScene/plSceneNode.h" +#include "plgDispatch.h" + +#include "../MaxConvert/plConvert.h" +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/hsMaterialConverter.h" +#include "../MaxConvert/plMeshConverter.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxPlasmaMtls/Materials/plParticleMtl.h" + +#include "../MaxExport/plErrorMsg.h" + +#include "hsResMgr.h" + +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../plInterp/plController.h" +#include "../plInterp/hsInterp.h" +#include "../plInterp/plAnimEaseTypes.h" +#include "../MaxMain/plMaxNode.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plPipeline/plGBufferGroup.h" + +#include "../plParticleSystem/plParticleSystem.h" +#include "../plParticleSystem/plParticleEmitter.h" +#include "../plParticleSystem/plParticleEffect.h" +#include "../plParticleSystem/plParticleGenerator.h" +#include "../plParticleSystem/plParticleApplicator.h" +#include "../plParticleSystem/plConvexVolume.h" +#include "../plParticleSystem/plBoundInterface.h" + +#include "../plAvatar/plScalarChannel.h" +#include "../plAvatar/plAGAnim.h" + +#include "../pnSceneObject/plDrawInterface.h" + +#include "../plGLight/plLightInfo.h" +#include "plLightGrpComponent.h" + +void DummyCodeIncludeFuncParticles() +{ +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// stuff for plParticleComponent + +const char *plParticleCoreComponent::GenStrings[] = // line these up with the generation types enum. +{ + "Point Source", + "Mesh", + "One Per Vertex" +}; + +hsBool plParticleCoreComponent::IsParticleSystemComponent(plComponentBase *comp) +{ + if (comp->ClassID() == PARTICLE_SYSTEM_COMPONENT_CLASS_ID) + return true; + + return false; +} + +hsBool plParticleCoreComponent::NodeHasSystem(plMaxNode *pNode) +{ + int i; + for (i = 0; i < pNode->NumAttachedComponents(); i++) + { + if (plParticleCoreComponent::IsParticleSystemComponent(pNode->GetAttachedComponent(i))) + return true; + } + return false; +} + +hsBool plParticleCoreComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + GetParamVals( pNode ); + pNode->SetForceLocal(true); + pNode->SetDrawable(false); + pNode->SetParticleRelated(true); + + Mtl *maxMaterial = hsMaterialConverter::Instance().GetBaseMtl(pNode); + plConvert &convert = plConvert::Instance(); + if (!hsMaterialConverter::IsParticleMat(maxMaterial)) + { + maxMaterial = nil; + pNode->SetMtl(NULL); + if (pErrMsg->Set(!(convert.fWarned & plConvert::kWarnedBadMaterialOnParticle), pNode->GetName(), + "Only \"Plasma Particle\" materials (not in a multi-material) may be applied to particle system objects." + " Using a default material for now.").CheckAskOrCancel()) + { + convert.fWarned |= plConvert::kWarnedBadMaterialOnParticle; + } + pErrMsg->Set(false); + } + + // Moving this from Convert so the DrawInterface will appear sooner. Other components expect + // the interfaces to be fully set up by the Convert pass. + plSceneNode *sNode = plSceneNode::ConvertNoRef( pNode->GetRoomKey()->GetObjectPtr() ); + plDrawInterface *di = TRACKED_NEW plDrawInterface; + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(pNode), di, pNode->GetLocation(), pNode->GetLoadMask()); + hsgResMgr::ResMgr()->AddViaNotify( di->GetKey(), TRACKED_NEW plObjRefMsg(pNode->GetSceneObject()->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface ), plRefFlags::kActiveRef ); + pNode->SetDISceneNodeSpans(di, true); + + return true; +} + +hsBool plParticleCoreComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + Int32 i, j, k; + + plLocation nodeLoc = node->GetKey()->GetUoid().GetLocation(); + const char *objName = node->GetKey()->GetName(); + + plSceneObject *sObj = node->GetSceneObject(); + plParticleSystem *sys = TRACKED_NEW plParticleSystem(); + + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), sys, nodeLoc, node->GetLoadMask()); + + // Add material and lifespan animated params. + Mtl *maxMaterial = hsMaterialConverter::Instance().GetBaseMtl(node); + hsTArray matArray; + hsMaterialConverter::Instance().GetMaterialArray(maxMaterial, node, matArray); + hsGMaterial* particleMat = matArray[0]; + + plController *ambientCtl = nil; + plController *diffuseCtl = nil; + plController *opacityCtl = nil; + plController *widthCtl = nil; + plController *heightCtl = nil; + hsControlConverter& cc = hsControlConverter::Instance(); + + if (hsMaterialConverter::IsParticleMat(maxMaterial)) // Exporter will yell if this is false, but we have to handle it + { + plParticleMtl *particleMtl = (plParticleMtl *)maxMaterial; // We check beforehand that this is ok + SetParticleStats(particleMtl); + ambientCtl = cc.MakeColorController(particleMtl->GetAmbColorController(), node); + diffuseCtl = cc.MakeColorController(particleMtl->GetColorController(), node); + opacityCtl = cc.MakeScalarController(particleMtl->GetOpacityController(), node); + widthCtl = cc.MakeScalarController(particleMtl->GetWidthController(), node); + heightCtl = cc.MakeScalarController(particleMtl->GetHeightController(), node); + } + + hsScalar genLife = -1; + hsScalar partLifeMin, partLifeMax; + hsScalar pps = fUserInput.fPPS; + hsPoint3 pos(0, 0, 0); + hsScalar pitch = PI; + hsScalar yaw = 0; + hsScalar angleRange = fUserInput.fConeAngle * PI / 180.f; + hsScalar velMin = fUserInput.fVelocityMin; + hsScalar velMax = fUserInput.fVelocityMax; + hsScalar xSize = fUserInput.fHSize; + hsScalar ySize = fUserInput.fVSize; + hsScalar scaleMin = fUserInput.fScaleMin / 100.0f; + hsScalar scaleMax = fUserInput.fScaleMax / 100.0f; + hsScalar gravity = fUserInput.fGravity / 100.0f; + hsScalar drag = fUserInput.fDrag / 100.f; + hsScalar windMult = fUserInput.fWindMult / 100.f; + hsScalar massRange = fUserInput.fMassRange; + hsScalar rotRange = fUserInput.fRotRange * PI / 180.f; + + UInt32 xTiles = fUserInput.fXTiles; + UInt32 yTiles = fUserInput.fYTiles; + UInt32 maxEmitters = 1 + GetEmitterReserve(); + UInt32 maxTotalParticles = 0; + + // Need to do this even when immortal, so that maxTotalParticles is computed correctly. + partLifeMin = fUserInput.fLifeMin; + partLifeMax = fUserInput.fLifeMax; + plLeafController *ppsCtl = cc.MakeScalarController(fCompPB->GetController(ParamID(kPPS)), node); + if (ppsCtl != nil && ppsCtl->GetLength() > 0) + { + // Simulate just the birth across the curve and record the max + hsScalar frameDelta = (1.f / MAX_FRAMES_PER_SEC); + hsScalar avgLife = (partLifeMax + partLifeMin) / 2; + UInt32 count = node->NumAttachedComponents(); + UInt32 lifeTicks = avgLife / frameDelta; + hsScalar *birth = TRACKED_NEW hsScalar[lifeTicks]; + + // Find any anim components attached to the same node. + for (i = 0; i < count; i++) + { + if (!plAnimComponentBase::IsAnimComponent(node->GetAttachedComponent(i))) + continue; + + hsScalar maxAnimParticles = 0; + + plAnimComponentBase *comp = (plAnimComponentBase *)node->GetAttachedComponent(i); + plATCAnim *anim = plATCAnim::ConvertNoRef(comp->fAnims[node]); + + // If it's an ATC anim, we can be aggressive in determining the max + if (anim) + { + hsScalar curAnimParticles = 0; + + hsScalar loopStart, loopEnd; + + for (j = -1; j < (Int32)anim->GetNumLoops(); j++) + { + // Initialize our birth counters + for (k = 0; k < lifeTicks; k++) + birth[k] = 0; + + if (j == -1) + { + loopStart = anim->GetStart(); + loopEnd = anim->GetEnd(); + } + else + anim->GetLoop(j, loopStart, loopEnd); + + hsScalar loopLength = loopEnd - loopStart; + + if (loopLength == 0) // It's the default "(Entire Animation)" + loopLength = ppsCtl->GetLength(); + + UInt32 loopTicks = loopLength * MAX_FRAMES_PER_SEC; + + UInt32 startTick = loopStart * MAX_FRAMES_PER_SEC; + UInt32 tick; + for (tick = 0; tick < loopTicks + lifeTicks; tick++) + { + curAnimParticles -= birth[tick % lifeTicks] * frameDelta; + hsScalar birthStart = 0.f; + hsScalar birthEnd = 0.f; + ppsCtl->Interp(((tick % loopTicks) + startTick) * frameDelta, &birthStart); + ppsCtl->Interp(((tick % loopTicks) + startTick + 1) * frameDelta, &birthEnd); + birth[tick % lifeTicks] = (birthStart + birthEnd) / 2; + curAnimParticles += birth[tick % lifeTicks] * frameDelta; + if (curAnimParticles > maxAnimParticles) + maxAnimParticles = curAnimParticles; + } + } + } + else // No info on the animation. Assume the worst. + { + hsScalar maxPps = 0; + int i; + for (i = 1; i < ppsCtl->GetNumKeys(); i++) + { + hsScalar curVal = 0; + hsScalarKey *key = ppsCtl->GetScalarKey(i); + if (key) + curVal = key->fValue; + + hsBezScalarKey *bezKey = ppsCtl->GetBezScalarKey(i); + if (bezKey) + curVal = bezKey->fValue; + + if( curVal > maxPps ) + maxPps = curVal; + } + maxAnimParticles = maxPps * (partLifeMax - (partLifeMax - partLifeMin) / 2); + } + + if (maxTotalParticles < maxAnimParticles) + maxTotalParticles = (UInt32)maxAnimParticles; + } + delete [] birth; + } + else + { + maxTotalParticles = pps * (partLifeMax - (partLifeMax - partLifeMin) / 2); + } + maxTotalParticles *= maxEmitters; + + delete ppsCtl; + ppsCtl = nil; + + UInt32 maxAllowedParticles = plGBufferGroup::kMaxNumIndicesPerBuffer / 6; + if (maxTotalParticles > maxAllowedParticles) + { + char text[512]; + sprintf(text, "This particle system requires a buffer for %d particles. " + "The max allowed for a single system is %d. Capping this system " + "at the max. If you need more, create a 2nd particle system " + "and balance out the birthrates.", + maxTotalParticles, maxAllowedParticles); + + plConvert &convert = plConvert::Instance(); + if (pErrMsg->Set(!(convert.fWarned & plConvert::kWarnedTooManyParticles), node->GetName(), text).CheckAskOrCancel()) + { + convert.fWarned |= plConvert::kWarnedTooManyParticles; + } + pErrMsg->Set(false); + maxTotalParticles = maxAllowedParticles; + } + + if (fUserInput.fImmortal) + { + partLifeMin = -1.0; + partLifeMax = -1.0; + } + + // Figure out the appropriate generator to add + plParticleGenerator *generator = nil; + UInt32 sources; + hsScalar *pitchArray; + hsScalar *yawArray; + hsPoint3 *pointArray; + hsVector3 *dirArray; + if (fUserInput.fGenType == kGenPoint) + { + sources = 1; + pitchArray = TRACKED_NEW hsScalar[sources]; + yawArray = TRACKED_NEW hsScalar[sources]; + pointArray = TRACKED_NEW hsPoint3[sources]; + pitchArray[0] = pitch; + yawArray[0] = yaw; + pointArray[0].Set(0, 0, 0); + plSimpleParticleGenerator *gen = TRACKED_NEW plSimpleParticleGenerator(); + gen->Init(genLife, partLifeMin, partLifeMax, pps, sources, pointArray, pitchArray, yawArray, angleRange, + velMin, velMax, xSize, ySize, scaleMin, scaleMax, massRange, rotRange); + generator = gen; + } + else if (fUserInput.fGenType == kGenMesh) + { + hsTArray normals; + hsTArray pos; + plMeshConverter::Instance().StuffPositionsAndNormals(node, &pos, &normals); + sources = normals.GetCount(); + pitchArray = TRACKED_NEW hsScalar[sources]; + yawArray = TRACKED_NEW hsScalar[sources]; + pointArray = TRACKED_NEW hsPoint3[sources]; + int i; + for (i = 0; i < sources; i++) + { + plParticleGenerator::ComputePitchYaw(pitchArray[i], yawArray[i], normals.Get(i)); + pointArray[i] = pos.Get(i); + } + plSimpleParticleGenerator *gen = TRACKED_NEW plSimpleParticleGenerator(); + gen->Init(genLife, partLifeMin, partLifeMax, pps, sources, pointArray, pitchArray, yawArray, angleRange, + velMin, velMax, xSize, ySize, scaleMin, scaleMax, massRange, rotRange); + generator = gen; + } + else // One per vertex + { + hsTArray normals; + hsTArray pos; + plMeshConverter::Instance().StuffPositionsAndNormals(node, &pos, &normals); + sources = normals.GetCount(); + + pointArray = TRACKED_NEW hsPoint3[sources]; + dirArray = TRACKED_NEW hsVector3[sources]; + int i; + for (i = 0; i < sources; i++) + { + dirArray[i] = normals.Get(i); + pointArray[i] = pos.Get(i); + } + + plOneTimeParticleGenerator *gen = TRACKED_NEW plOneTimeParticleGenerator(); + gen->Init(sources, pointArray, dirArray, xSize, ySize, scaleMin, scaleMax, rotRange); + generator = gen; + maxTotalParticles = sources; + gravity = 0.f; + } + + // Init and attach to the scene object + sys->Init(xTiles, yTiles, maxTotalParticles, maxEmitters, ambientCtl, diffuseCtl, opacityCtl, + widthCtl, heightCtl); + hsgResMgr::ResMgr()->AddViaNotify( particleMat->GetKey(), TRACKED_NEW plGenRefMsg( sys->GetKey(), plRefMsg::kOnCreate, 0, 0 ), plRefFlags::kActiveRef ); + hsgResMgr::ResMgr()->AddViaNotify( sys->GetKey(), TRACKED_NEW plObjRefMsg( sObj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); + + // Set up normals and orientation + UInt32 miscFlags = 0; + switch(fUserInput.fNormal) + { + case plParticleMtl::kNormalViewFacing: + miscFlags |= plParticleEmitter::kNormalViewFacing; + break; + case plParticleMtl::kNormalUp: + case plParticleMtl::kEmissive: // For emissive, we don't care about the normal. This choice makes us + // not waste time computing one. + miscFlags |= plParticleEmitter::kNormalUp; + break; + case plParticleMtl::kNormalNearestLight: + miscFlags |= plParticleEmitter::kNormalNearestLight; + break; + case plParticleMtl::kNormalFromCenter: + miscFlags |= plParticleEmitter::kNormalFromCenter; + break; + case plParticleMtl::kNormalVelUpVel: + miscFlags |= plParticleEmitter::kNormalVelUpVel; + break; + } + + switch(fUserInput.fOrientation) + { + case plParticleMtl::kOrientVelocity: + miscFlags |= plParticleEmitter::kOrientationVelocityBased; + break; + case plParticleMtl::kOrientUp: + miscFlags |= plParticleEmitter::kOrientationUp; + break; + case plParticleMtl::kOrientVelStretch: + miscFlags |= plParticleEmitter::kOrientationVelocityStretch; + break; + case plParticleMtl::kOrientVelFlow: + miscFlags |= plParticleEmitter::kOrientationVelocityFlow; + break; + } + + if (fUserInput.fGenType == kGenOnePerVertex && + (miscFlags & plParticleEmitter::kOrientationVelocityMask)) + { + char text[256]; + sprintf(text, "This particle system has an orientation that's based on velocity " + "(see the Particle Material), which doesn't work with OnePerVertex " + "generation. No particles from this system will be visible."); + + plConvert &convert = plConvert::Instance(); + if (pErrMsg->Set(!(convert.fWarned & plConvert::kWarnedParticleVelAndOnePer), node->GetName(), text).CheckAskOrCancel()) + { + convert.fWarned |= plConvert::kWarnedParticleVelAndOnePer; + } + pErrMsg->Set(false); + } + if( maxEmitters > 1 ) + miscFlags |= plParticleEmitter::kOnReserve; + + sys->AddEmitter( maxTotalParticles, generator, miscFlags ); + sys->SetGravity(gravity); + sys->SetDrag(drag); + sys->SetWindMult(windMult); + sys->SetPreSim(fUserInput.fPreSim); + + // Finally, any attached effects. + for (i = 0; i < node->NumAttachedComponents(); i++) + { + plComponentBase *comp = node->GetAttachedComponent(i); + if (plParticleEffectComponent::IsParticleEffectComponent(comp)) + ((plParticleEffectComponent *)comp)->AddToParticleSystem(sys, node); + if (comp->ClassID() == LIGHTGRP_COMP_CID) + IHandleLights((plLightGrpComponent*)comp, sys); + } + + if (fCompPB->GetInt(ParamID(kFollowSystem))) + { + plParticleFollowSystemEffect *effect = TRACKED_NEW plParticleFollowSystemEffect; + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), effect, node->GetLocation(), node->GetLoadMask()); + hsgResMgr::ResMgr()->AddViaNotify( effect->GetKey(), TRACKED_NEW plGenRefMsg( sys->GetKey(), plRefMsg::kOnCreate, 0, plParticleSystem::kEffectMisc ), plRefFlags::kActiveRef ); + } + + return true; +} + +void plParticleCoreComponent::IHandleLights(plLightGrpComponent* liGrp, plParticleSystem* sys) +{ + const hsTArray& liInfo = liGrp->GetLightInfos(); + int i; + for( i = 0; i < liInfo.GetCount(); i++ ) + { + sys->AddLight(liInfo[i]->GetKey()); + } +} + +hsBool plParticleCoreComponent::AddToAnim(plAGAnim *anim, plMaxNode *node) +{ + hsBool result = false; + plController *ctl; + hsControlConverter& cc = hsControlConverter::Instance(); + + hsScalar start, end; + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + { + start = end = -1; + } + else + { + start = anim->GetStart(); + end = anim->GetEnd(); + } + + if (fCompPB->GetInt(kGenType) != kGenOnePerVertex) + { + ctl = cc.MakeScalarController(fCompPB->GetController(ParamID(kLifeMin)), node, start, end); + if (ctl != nil) + { + plParticleLifeMinApplicator *app = TRACKED_NEW plParticleLifeMinApplicator(); + app->SetChannelName(node->GetName()); + plAnimComponentBase::SetupCtl(anim, ctl, app, node); + result = true; + } + + ctl = cc.MakeScalarController(fCompPB->GetController(ParamID(kLifeMax)), node, start, end); + if (ctl != nil) + { + plParticleLifeMaxApplicator *app = TRACKED_NEW plParticleLifeMaxApplicator(); + app->SetChannelName(node->GetName()); + plAnimComponentBase::SetupCtl(anim, ctl, app, node); + result = true; + } + + ctl = cc.MakeScalarController(fCompPB->GetController(ParamID(kPPS)), node, start, end); + if (ctl != nil) + { + plParticlePPSApplicator *app = TRACKED_NEW plParticlePPSApplicator(); + app->SetChannelName(node->GetName()); + plAnimComponentBase::SetupCtl(anim, ctl, app, node); + result = true; + } + + ctl = cc.MakeScalarController(fCompPB->GetController(ParamID(kConeAngle)), node, start, end); + if (ctl != nil) + { + plParticleAngleApplicator *app = TRACKED_NEW plParticleAngleApplicator(); + app->SetChannelName(node->GetName()); + plAnimComponentBase::SetupCtl(anim, ctl, app, node); + result = true; + } + + ctl = cc.MakeScalarController(fCompPB->GetController(ParamID(kVelocityMin)), node, start, end); + if (ctl != nil) + { + plParticleVelMinApplicator *app = TRACKED_NEW plParticleVelMinApplicator(); + app->SetChannelName(node->GetName()); + plAnimComponentBase::SetupCtl(anim, ctl, app, node); + result = true; + } + + ctl = cc.MakeScalarController(fCompPB->GetController(ParamID(kVelocityMax)), node, start, end); + if (ctl != nil) + { + plParticleVelMaxApplicator *app = TRACKED_NEW plParticleVelMaxApplicator(); + app->SetChannelName(node->GetName()); + plAnimComponentBase::SetupCtl(anim, ctl, app, node); + result = true; + } + + /* + ctl = cc.MakeScalarController(fCompPB->GetController(ParamID(kGravity)), node, start, end); + if (ctl != nil) + { + plParticleGravityApplicator *app = TRACKED_NEW plParticleGravityApplicator(); + plAnimComponentBase::SetupCtl(anim, ctl, app, node); + result = true; + } + + ctl = cc.MakeScalarController(fCompPB->GetController(ParamID(kDrag)), node, start, end); + if (ctl != nil) + { + plParticleDragApplicator *app = TRACKED_NEW plParticleDragApplicator(); + plAnimComponentBase::SetupCtl(anim, ctl, app, node); + result = true; + } + */ + } + + ctl = cc.MakeScalarController(fCompPB->GetController(ParamID(kScaleMin)), node, start, end); + if (ctl != nil) + { + plParticleScaleMinApplicator *app = TRACKED_NEW plParticleScaleMinApplicator(); + app->SetChannelName(node->GetName()); + plAnimComponentBase::SetupCtl(anim, ctl, app, node); + result = true; + } + + ctl = cc.MakeScalarController(fCompPB->GetController(ParamID(kScaleMax)), node, start, end); + if (ctl != nil) + { + plParticleScaleMaxApplicator *app = TRACKED_NEW plParticleScaleMaxApplicator(); + app->SetChannelName(node->GetName()); + plAnimComponentBase::SetupCtl(anim, ctl, app, node); + result = true; + } + + return result; +} + +void plParticleCoreComponent::SetParticleStats(plParticleMtl *mtl) +{ + IParamBlock2 *pb = mtl->GetParamBlockByID(plParticleMtl::kBlkBasic); + fUserInput.fHSize = pb->GetFloat(plParticleMtl::kWidth); + fUserInput.fVSize = pb->GetFloat(plParticleMtl::kHeight); + fUserInput.fXTiles = pb->GetInt(plParticleMtl::kXTiles); + fUserInput.fYTiles = pb->GetInt(plParticleMtl::kYTiles); + fUserInput.fNormal = pb->GetInt(plParticleMtl::kNormal); + fUserInput.fOrientation = pb->GetInt(plParticleMtl::kOrientation); +} + +CLASS_DESC(plParticleComponent, gParticleDesc, "Particle System", "ParticleSystem", COMP_TYPE_PARTICLE, PARTICLE_SYSTEM_COMPONENT_CLASS_ID) + +ParamBlockDesc2 gParticleBk +( + + plComponent::kBlkComp, _T("Particle"), 0, &gParticleDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_PARTICLE, IDS_COMP_PARTICLE_ROLL, 0, 0, &gParticleCompDlgProc, + + //Particle Properties.... + + plParticleCoreComponent::kGenType, _T("Generation"), TYPE_INT, 0, 0, + p_default, 0, + end, + + plParticleCoreComponent::kConeAngle, _T("ConeAngle"), TYPE_FLOAT, P_ANIMATABLE, IDS_PARTICLE_CONE_ANGLE, + p_default, 45.0, + p_range, 0.0, 180.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_CONE, IDC_COMP_PARTICLE_CONE_SPIN, 1.0, + end, + + plParticleCoreComponent::kVelocityMin, _T("VelocityMin"), TYPE_FLOAT, P_ANIMATABLE, IDS_PARTICLE_VELOCITY_MIN, + p_default, 50.0, + p_range, 0.0, 500.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_VELMIN, IDC_COMP_PARTICLE_VELMIN_SPIN, 1.0, + end, + + plParticleCoreComponent::kVelocityMax, _T("VelocityMax"), TYPE_FLOAT, P_ANIMATABLE, IDS_PARTICLE_VELOCITY_MAX, + p_default, 50.0, + p_range, 0.0, 500.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_VELMAX, IDC_COMP_PARTICLE_VELMAX_SPIN, 1.0, + end, + + plParticleCoreComponent::kLifeMin, _T("LifeMin"), TYPE_FLOAT, P_ANIMATABLE, IDS_PARTICLE_LIFE_MIN, + p_default, 10.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_LIFEMIN, IDC_COMP_PARTICLE_LIFEMIN_SPIN, 1.0, + end, + + plParticleCoreComponent::kLifeMax, _T("LifeMax"), TYPE_FLOAT, P_ANIMATABLE, IDS_PARTICLE_LIFE_MAX, + p_default, 5.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_LIFEMAX, IDC_COMP_PARTICLE_LIFEMAX_SPIN, 1.0, + end, + + plParticleCoreComponent::kImmortal, _T("Immortal"), TYPE_BOOL, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_PARTICLE_NODIE, + end, + + plParticleCoreComponent::kPPS, _T("PPS"), TYPE_FLOAT, P_ANIMATABLE, IDS_PARTICLE_PPS, + p_default, 20.0, + p_range, 0.0, 5000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_PPS, IDC_COMP_PARTICLE_PPS_SPIN, 1.0, + end, + + plParticleCoreComponent::kScaleMin, _T("ScaleMin"), TYPE_INT, P_ANIMATABLE, IDS_PARTICLE_SCALE_MIN, + p_default, 100, + p_range, 1, 1000, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_COMP_PARTICLE_SCALEMIN, IDC_COMP_PARTICLE_SCALEMIN_SPIN, 1.0, + end, + + plParticleCoreComponent::kScaleMax, _T("ScaleMax"), TYPE_INT, P_ANIMATABLE, IDS_PARTICLE_SCALE_MAX, + p_default, 100, + p_range, 1, 1000, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_COMP_PARTICLE_SCALEMAX, IDC_COMP_PARTICLE_SCALEMAX_SPIN, 1.0, + end, + + plParticleCoreComponent::kGravity, _T("Gravity"), TYPE_INT, 0, 0, + p_default, 100, + p_range, -100, 100, + p_ui, TYPE_SPINNER, EDITTYPE_INT, + IDC_COMP_PARTICLE_GRAVITY, IDC_COMP_PARTICLE_GRAVITY_SPIN, 1.0, + end, + + plParticleCoreComponent::kDrag, _T("Drag"), TYPE_INT, 0, 0, + p_default, 0, + p_range, 0, 1000, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_COMP_PARTICLE_DRAG, IDC_COMP_PARTICLE_DRAG_SPIN, 1.0, + end, + + plParticleCoreComponent::kPreSim, _T("PreSim"), TYPE_INT, 0, 0, + p_default, 0, + p_range, 0, 100, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_COMP_PARTICLE_PRESIM, IDC_COMP_PARTICLE_PRESIM_SPIN, 1.0, + end, + + plParticleCoreComponent::kWindMult, _T("WindMult"), TYPE_INT, 0, 0, + p_default, 100, + p_range, 0, 1000, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_COMP_PARTICLE_WIND_MULT, IDC_COMP_PARTICLE_WIND_MULT_SPIN, 1.0, + end, + + plParticleCoreComponent::kMassRange, _T("MassRange"), TYPE_FLOAT, 0, 0, + p_default, 0.f, + p_range, 0.f, 1000.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_MASS_RANGE, IDC_COMP_PARTICLE_MASS_RANGE_SPIN, 1.0, + end, + + plParticleCoreComponent::kRotRange, _T("RotRange"), TYPE_FLOAT, 0, 0, + p_default, 0.f, + p_range, 0.f, 180.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_ROT_RANGE, IDC_COMP_PARTICLE_ROT_RANGE_SPIN, 1.0, + end, + + plParticleCoreComponent::kFollowSystem, _T("FollowSystem"), TYPE_BOOL, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_PARTICLE_FOLLOW_SYSTEM, + end, + + end +); + +bool plParticleComponent::fAllowUnhide = false; + +plParticleComponent::plParticleComponent() +: fEmitterReserve(0) +{ + fClassDesc = &gParticleDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// Gets values from the ParamBlock and passes them to the Convert +hsBool plParticleComponent::GetParamVals(plMaxNode *pNode) +{ + fUserInput.fGenType = fCompPB->GetInt(kGenType); + fUserInput.fConeAngle = fCompPB->GetFloat(kConeAngle); + fUserInput.fVelocityMin = fCompPB->GetFloat(kVelocityMin); + fUserInput.fVelocityMax = fCompPB->GetFloat(kVelocityMax); + fUserInput.fLifeMin = fCompPB->GetFloat(kLifeMin); + fUserInput.fLifeMax = fCompPB->GetFloat(kLifeMax); + fUserInput.fImmortal = fCompPB->GetInt(kImmortal); + fUserInput.fPPS = fCompPB->GetFloat(kPPS); + fUserInput.fScaleMin = fCompPB->GetInt(kScaleMin); + fUserInput.fScaleMax = fCompPB->GetInt(kScaleMax); + + fUserInput.fGravity = fCompPB->GetInt(kGravity); + fUserInput.fDrag = fCompPB->GetInt(kDrag); + fUserInput.fWindMult = fCompPB->GetInt(kWindMult); + fUserInput.fMassRange = fCompPB->GetFloat(kMassRange); + fUserInput.fPreSim = fCompPB->GetInt(kPreSim); + fUserInput.fRotRange = fCompPB->GetFloat(kRotRange); + + return true; +} + +class ParticleCompDlgProc : public ParamMap2UserDlgProc +{ +protected: + void EnableDynGenParams(IParamMap2 *pm, bool enabled) + { + pm->Enable(plParticleComponent::kConeAngle, enabled); + pm->Enable(plParticleComponent::kVelocityMin, enabled); + pm->Enable(plParticleComponent::kVelocityMax, enabled); + pm->Enable(plParticleComponent::kLifeMin, enabled); + pm->Enable(plParticleComponent::kLifeMax, enabled); + pm->Enable(plParticleComponent::kImmortal, enabled); + pm->Enable(plParticleComponent::kPPS, enabled); + pm->Enable(plParticleComponent::kGravity, enabled); + pm->Enable(plParticleComponent::kPreSim, enabled); + pm->Enable(plParticleComponent::kDrag, enabled); + } + +public: + ParticleCompDlgProc() {} + ~ParticleCompDlgProc() {} + + void IValidateSpinners(TimeValue t, IParamBlock2 *pb, IParamMap2 *map, UInt32 id) + { + UInt32 minIndex, maxIndex; + hsBool adjustMin; + switch(id) + { + case IDC_COMP_PARTICLE_VELMIN: + case IDC_COMP_PARTICLE_VELMIN_SPIN: + minIndex = plParticleCoreComponent::kVelocityMin; maxIndex = plParticleCoreComponent::kVelocityMax; adjustMin = false; + break; + case IDC_COMP_PARTICLE_VELMAX: + case IDC_COMP_PARTICLE_VELMAX_SPIN: + minIndex = plParticleCoreComponent::kVelocityMin; maxIndex = plParticleCoreComponent::kVelocityMax; adjustMin = true; + break; + case IDC_COMP_PARTICLE_LIFEMIN: + case IDC_COMP_PARTICLE_LIFEMIN_SPIN: + minIndex = plParticleCoreComponent::kLifeMin; maxIndex = plParticleCoreComponent::kLifeMax; adjustMin = false; + break; + case IDC_COMP_PARTICLE_LIFEMAX: + case IDC_COMP_PARTICLE_LIFEMAX_SPIN: + minIndex = plParticleCoreComponent::kLifeMin; maxIndex = plParticleCoreComponent::kLifeMax; adjustMin = true; + break; + default: + return; + } + + float min, max; + min = pb->GetFloat(minIndex, t); + max = pb->GetFloat(maxIndex, t); + + if (min > max) + { + if (adjustMin) + pb->SetValue(minIndex, t, max); + else + pb->SetValue(maxIndex, t, min); + + map->Invalidate(minIndex); + map->Invalidate(maxIndex); + } + } + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + int id = LOWORD(wParam); + int code = HIWORD(wParam); + + IParamBlock2 *pb = map->GetParamBlock(); + HWND cbox = NULL; + + int selection; + switch (msg) + { + case WM_INITDIALOG: + int j; + for (j = 0; j < plParticleCoreComponent::kGenNumOptions; j++) + { + cbox = GetDlgItem(hWnd, IDC_GEN_TYPE); + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)plParticleCoreComponent::GenStrings[j]); + } + selection = pb->GetInt(plParticleCoreComponent::kGenType); + SendMessage(cbox, CB_SETCURSEL, selection, 0); + EnableDynGenParams(map, selection != plParticleCoreComponent::kGenOnePerVertex); + + CheckDlgButton(hWnd, IDC_TRACKVIEW_SHOW, plParticleComponent::fAllowUnhide ? BST_CHECKED : BST_UNCHECKED); + return TRUE; + + case WM_COMMAND: + if (id == IDC_GEN_TYPE) + { + selection = SendMessage(GetDlgItem(hWnd, id), CB_GETCURSEL, 0, 0); + pb->SetValue(plParticleCoreComponent::kGenType, t, selection); + EnableDynGenParams(map, selection != plParticleCoreComponent::kGenOnePerVertex); + return TRUE; + } + else if (id == IDC_COMP_PARTICLE_VELMIN || id == IDC_COMP_PARTICLE_VELMAX || + id == IDC_COMP_PARTICLE_LIFEMIN || id == IDC_COMP_PARTICLE_LIFEMAX) + { + IValidateSpinners(t, pb, map, id); + return TRUE; + } + else if (id == IDC_TRACKVIEW_SHOW && code == BN_CLICKED) + { + plParticleComponent::fAllowUnhide = (IsDlgButtonChecked(hWnd, IDC_TRACKVIEW_SHOW) == BST_CHECKED); + plComponentShow::Update(); + return TRUE; + } + break; + case CC_SPINNER_CHANGE: + if (id == IDC_COMP_PARTICLE_VELMIN_SPIN || id == IDC_COMP_PARTICLE_VELMAX_SPIN || + id == IDC_COMP_PARTICLE_LIFEMIN_SPIN || id == IDC_COMP_PARTICLE_LIFEMAX_SPIN) + { + IValidateSpinners(t, pb, map, id); + return TRUE; + } + break; + } + return FALSE; + } + void DeleteThis() {} +}; +static ParticleCompDlgProc gParticleCompDlgProc; + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Particle Effects Base class + +// Make sure any new Effect you add is accounted for here, or it won't get converted. +hsBool plParticleEffectComponent::IsParticleEffectComponent(plComponentBase *comp) +{ + if (comp->ClassID() == PARTICLE_FADE_COMPONENT_CLASS_ID || + comp->ClassID() == PARTICLE_VOLUME_COMPONENT_CLASS_ID || + comp->ClassID() == PARTICLE_WIND_COMPONENT_CLASS_ID || + comp->ClassID() == PARTICLE_UNIWIND_COMPONENT_CLASS_ID || + comp->ClassID() == PARTICLE_FLOCK_COMPONENT_CLASS_ID) + return true; + + return false; +} + +hsBool plParticleEffectComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fEffect = nil; + return true; +} + +hsBool plParticleEffectComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + hsBool valid = plParticleCoreComponent::NodeHasSystem(node); + if (!valid) + { + pErrMsg->Set(true, node->GetName(), "Node has a particle effect component, " + "but no particle system to apply it to. Ignoring component.").Show(); + pErrMsg->Set(false); + } + + return valid; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// plParticleFadeComponent + +void plParticleFadeComponent::AddToParticleSystem(plParticleSystem *sys, plMaxNode *node) +{ + plParticleFadeVolumeEffect *effect = nil; + if( !fEffect ) + { + effect = TRACKED_NEW plParticleFadeVolumeEffect(); + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), effect, node->GetLocation(), node->GetLoadMask()); + effect->fLength = (float)fCompPB->GetInt(kFadeDistance); + if (fCompPB->GetInt(kFadeZ)) + effect->fIgnoreZ = false; + + fEffect = effect; + } + else + { + effect = plParticleFadeVolumeEffect::ConvertNoRef(fEffect); + } + hsAssert(effect, "Our effect pointer was wrong type?"); + hsgResMgr::ResMgr()->AddViaNotify( effect->GetKey(), TRACKED_NEW plGenRefMsg( sys->GetKey(), plRefMsg::kOnCreate, 0, plParticleSystem::kEffectMisc ), plRefFlags::kActiveRef ); +} + +CLASS_DESC(plParticleFadeComponent, gParticleFadeDesc, "Fade Volume Effect", "Fade Volume Effect", COMP_TYPE_PARTICLE, PARTICLE_FADE_COMPONENT_CLASS_ID) + +ParamBlockDesc2 gParticleFadeBk +( + + plComponent::kBlkComp, _T("ParticleFade"), 0, &gParticleFadeDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_PARTICLE_FADE, IDS_COMP_PARTICLE_FADE_ROLL, 0, 0, NULL, + + plParticleFadeComponent::kFadeDistance, _T("FadeDistance"), TYPE_INT, P_ANIMATABLE, 0, + p_default, 100, + p_range, 0, 10000, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_COMP_PARTICLE_FADE_DIST, IDC_COMP_PARTICLE_FADE_DIST_SPIN, 1.0, + end, + + plParticleFadeComponent::kFadeZ, _T("FadeInZ"), TYPE_BOOL, P_ANIMATABLE, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_PARTICLE_FADEZ, + end, + + end +); + +plParticleFadeComponent::plParticleFadeComponent() +{ + fClassDesc = &gParticleFadeDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +//////////////////////////////////////////////////////////////////////////// +// +// Convex Volume Component + +void plParticleVolumeComponent::CollectNonDrawables(INodeTab& nonDrawables) +{ + INode* source = fCompPB->GetINode(kSourceNode); + if( source ) + nonDrawables.Append(1, &source); +} + +hsBool plParticleVolumeComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + if (!plParticleEffectComponent::PreConvert(pNode, pErrMsg)) + return false; + + fBound = nil; + + plMaxNode *source = (plMaxNode *)fCompPB->GetINode(kSourceNode); + if (source == nil || !source->CanConvert()) + { + pErrMsg->Set(true, pNode->GetName(), "Particle Convex Volume component has not been assigned a " + "node to build itself from or Volume has Ignore component on it.. Ignoring component.").Show(); + pErrMsg->Set(false); + return false; // No source selected + } + + source->SetForceLocal(true); + source->SetDrawable(false); + source->SetParticleRelated(true); + + return true; +} + +void plParticleVolumeComponent::BuildVolume(plMaxNode *node) +{ + if (fBound != nil) + return; // already converted it + + fBound = TRACKED_NEW plBoundInterface; + hsgResMgr::ResMgr()->NewKey(node->GetName(), fBound, node->GetLocation(), node->GetLoadMask()); + fBound->Init(plMeshConverter::Instance().CreateConvexVolume(node)); + hsgResMgr::ResMgr()->AddViaNotify(fBound->GetKey(), TRACKED_NEW plObjRefMsg(node->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface), plRefFlags::kActiveRef); +} + + +void plParticleVolumeComponent::AddToParticleSystem(plParticleSystem *sys, plMaxNode *node) +{ + plParticleCollisionEffect *effect = nil; + if( !fEffect ) + { + plMaxNode *source = (plMaxNode *)fCompPB->GetINode(kSourceNode); + + if (source == nil || !source->CanConvert()) + return; // No source selected, user has already been warned. + + BuildVolume(source); + switch( fCompPB->GetInt(kOnImpact) ) + { + default: + case kImpDefault: + effect = TRACKED_NEW plParticleCollisionEffectBeat(); + break; + case kImpDie: + effect = TRACKED_NEW plParticleCollisionEffectDie(); + break; + case kImpBounce: + { + plParticleCollisionEffectBounce* bnc = TRACKED_NEW plParticleCollisionEffectBounce(); + bnc->SetBounce(fCompPB->GetFloat(kBounceAmt) * 1.e-2f); + bnc->SetFriction(fCompPB->GetFloat(kFrictionAmt) * 1.e-2f); + effect = bnc; + } + break; + }; + + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), effect, node->GetLocation(), node->GetLoadMask()); + plSceneObject *sObj = source->GetSceneObject(); + hsgResMgr::ResMgr()->AddViaNotify( sObj->GetKey(), TRACKED_NEW plGenRefMsg( effect->GetKey(), plRefMsg::kOnCreate, 0, 0 ), plRefFlags::kPassiveRef ); + + fEffect = effect; + } + else + { + effect = plParticleCollisionEffect::ConvertNoRef(fEffect); + } + hsAssert(effect, "Our effect pointer was wrong type?"); + hsgResMgr::ResMgr()->AddViaNotify( effect->GetKey(), TRACKED_NEW plGenRefMsg( sys->GetKey(), plRefMsg::kOnCreate, 0, plParticleSystem::kEffectConstraint ), plRefFlags::kActiveRef ); +} + +CLASS_DESC(plParticleVolumeComponent, gParticleVolumeDesc, "Collision Volume Effect", "Collision Volume Effect", COMP_TYPE_PARTICLE, PARTICLE_VOLUME_COMPONENT_CLASS_ID) + +ParamBlockDesc2 gParticleVolumeBk +( + plComponent::kBlkComp, _T("ParticleVolume"), 0, &gParticleVolumeDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_PARTICLE_VOLUME, IDS_COMP_PARTICLE_VOLUME_ROLL, 0, 0, NULL, + + plParticleVolumeComponent::kSourceNode, _T("SourceINode"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_PARTICLE_VOLUME_NODE, + p_sclassID, GEOMOBJECT_CLASS_ID, + //p_prompt, IDS_COMP_ONESHOT_STARTS, + //p_accessor, &gOneShotAccessor, + end, + + plParticleVolumeComponent::kOnImpact, _T("OnImpact"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_COMP_PARTICLE_VOL_DEFAULT, IDC_COMP_PARTICLE_VOL_DIE, IDC_COMP_PARTICLE_VOL_BOUNCE, + p_vals, plParticleVolumeComponent::kImpDefault, plParticleVolumeComponent::kImpDie, plParticleVolumeComponent::kImpBounce, + p_default, plParticleVolumeComponent::kImpDefault, + end, + + plParticleVolumeComponent::kBounceAmt, _T("BounceAmt"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_VOL_BOUNCEAMT, IDC_COMP_PARTICLE_VOL_BOUNCEAMT_SPIN, 1.0, + end, + + plParticleVolumeComponent::kFrictionAmt, _T("FrictionAmt"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_VOL_FRICTIONAMT, IDC_COMP_PARTICLE_VOL_FRICTIONAMT_SPIN, 1.0, + end, + + end +); + +plParticleVolumeComponent::plParticleVolumeComponent() +{ + fClassDesc = &gParticleVolumeDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// plParticleWindComponent + +static hsVector3 IGetRefDir(plMaxNode* node, INode* refNode, float clampAngDeg) +{ + clampAngDeg *= 0.5f; + float vecLen = 100.f; + if( clampAngDeg > 1.f ) + { + float rads = hsScalarDegToRad(clampAngDeg); + float sinAng = sinf(rads); + + hsAssert(sinAng > 0.01, "Trig confusion?"); + +#if 0 + float cosAng = cosf(rads); + if( cosAng < 0.01f ) + vecLen = 0; + else + vecLen = cosAng / sinAng; +#else + vecLen = 1.f / sinAng; +#endif + + } + if( !refNode ) + refNode = node; + + Matrix3 nodeTM = refNode->GetNodeTM(TimeValue(0)); + Point3 dir = nodeTM.GetRow(1); + dir = FNormalize(dir); + + hsVector3 refDir(dir.x * vecLen, dir.y * vecLen, dir.z * vecLen); + + return refDir; +} + + +void plParticleWindComponent::AddToParticleSystem(plParticleSystem *sys, plMaxNode *node) +{ + plParticleLocalWind* effect = nil; + if( !fEffect ) + { + effect = TRACKED_NEW plParticleLocalWind(); + effect->SetScale(hsVector3(fCompPB->GetFloat(kScaleX), fCompPB->GetFloat(kScaleY), fCompPB->GetFloat(kScaleZ))); + effect->SetSpeed(fCompPB->GetFloat(kSpeed)); + + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), effect, node->GetLocation(), node->GetLoadMask()); + + effect->SetStrength(fCompPB->GetFloat(kStrength)); + effect->SetSwirl(fCompPB->GetFloat(kSwirl) * 1.e-2f); + effect->SetHorizontal(fCompPB->GetInt(kHorizontal)); + effect->SetConstancy(fCompPB->GetFloat(kConstancy) * 1.e-2f); + + effect->SetRefDirection(IGetRefDir(node, fCompPB->GetINode(kRefObject), fCompPB->GetFloat(kClampAngle))); + + fEffect = effect; + } + else + { + effect = plParticleLocalWind::ConvertNoRef(fEffect); + } + hsAssert(effect, "Our effect pointer was wrong type?"); + hsgResMgr::ResMgr()->AddViaNotify( effect->GetKey(), TRACKED_NEW plGenRefMsg( sys->GetKey(), plRefMsg::kOnCreate, 0, plParticleSystem::kEffectForce ), plRefFlags::kActiveRef ); +} + +CLASS_DESC(plParticleWindComponent, gParticleWindDesc, "Wind Effect", "WindEffect", COMP_TYPE_PARTICLE, PARTICLE_WIND_COMPONENT_CLASS_ID) + +ParamBlockDesc2 gParticleWindBk +( + + plComponent::kBlkComp, _T("ParticleWind"), 0, &gParticleWindDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_PARTICLE_WIND, IDS_COMP_PARTICLE_WIND, 0, 0, NULL, + + plParticleWindComponent::kScaleX, _T("ScaleX"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 25.f, + p_range, 0.f, 1000.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_SCALEX, IDC_COMP_PARTICLE_WIND_SCALEX_SPIN, 1.0, + end, + + plParticleWindComponent::kScaleY, _T("ScaleY"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 25.f, + p_range, 0.f, 1000.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_SCALEY, IDC_COMP_PARTICLE_WIND_SCALEY_SPIN, 1.0, + end, + + plParticleWindComponent::kScaleZ, _T("ScaleZ"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.f, + p_range, 0.f, 1000.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_SCALEZ, IDC_COMP_PARTICLE_WIND_SCALEZ_SPIN, 1.0, + end, + + plParticleWindComponent::kSpeed, _T("Speed"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 32.f, + p_range, -100.f, 100.f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PARTICLE_WIND_SPEED, IDC_COMP_PARTICLE_WIND_SPEED_SPIN, 1.0, + end, + + plParticleWindComponent::kStrength, _T("Strength"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 30.f, + p_range, 0.f, 100.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_STRENGTH, IDC_COMP_PARTICLE_WIND_STRENGTH_SPIN, 1.0, + end, + + plParticleWindComponent::kConstancy, _T("Constancy"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.f, + p_range, -75.f, 300.f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PARTICLE_WIND_CONSTANCY, IDC_COMP_PARTICLE_WIND_CONSTANCY_SPIN, 1.0, + end, + + plParticleWindComponent::kSwirl, _T("Swirl"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 100.f, + p_range, 0.f, 100.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_SWIRL, IDC_COMP_PARTICLE_WIND_SWIRL_SPIN, 1.0, + end, + + plParticleWindComponent::kHorizontal, _T("Horizontal"), TYPE_BOOL, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_PARTICLE_WIND_HORIZONTAL, + end, + + plParticleWindComponent::kLocalize, _T("Localize"), TYPE_BOOL, + p_default, TRUE, + end, + + plParticleWindComponent::kClampAngle, _T("ClampAngle"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 180.f, + p_range, 0.f, 180.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_CLAMPANG, IDC_COMP_PARTICLE_WIND_CLAMPANG_SPIN, 1.0, + end, + + plParticleWindComponent::kRefObject, _T("RefObject"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_PARTICLE_WIND_REFOBJECT, + p_prompt, IDS_COMP_CHOOSE_OBJECT, + end, + + end +); + +plParticleWindComponent::plParticleWindComponent() +{ + fClassDesc = &gParticleWindDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// plParticleUniWindComponent + +void plParticleUniWindComponent::AddToParticleSystem(plParticleSystem *sys, plMaxNode *node) +{ + plParticleUniformWind* effect = nil; + if( !fEffect ) + { + effect = TRACKED_NEW plParticleUniformWind(); + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), effect, node->GetLocation(), node->GetLoadMask()); + + effect->SetStrength(fCompPB->GetFloat(kStrength)); + effect->SetSwirl(fCompPB->GetFloat(kSwirl) * 1.e-2f); + effect->SetHorizontal(fCompPB->GetInt(kHorizontal)); + effect->SetConstancy(fCompPB->GetFloat(kConstancy) * 1.e-2f); + + effect->SetFrequencyRange(fCompPB->GetFloat(kMinSecs), fCompPB->GetFloat(kMaxSecs)); + effect->SetFrequencyRate(fCompPB->GetFloat(kRate)); + + effect->SetRefDirection(IGetRefDir(node, fCompPB->GetINode(kRefObject), fCompPB->GetFloat(kClampAngle))); + + fEffect = effect; + } + else + { + effect = plParticleUniformWind::ConvertNoRef(fEffect); + } + hsAssert(effect, "Our effect pointer was wrong type?"); + hsgResMgr::ResMgr()->AddViaNotify( effect->GetKey(), TRACKED_NEW plGenRefMsg( sys->GetKey(), plRefMsg::kOnCreate, 0, plParticleSystem::kEffectForce ), plRefFlags::kActiveRef ); +} + +CLASS_DESC(plParticleUniWindComponent, gParticleUniWindDesc, "Uniform Wind", "UniWind", COMP_TYPE_PARTICLE, PARTICLE_UNIWIND_COMPONENT_CLASS_ID) + +ParamBlockDesc2 gParticleUniWindBk +( + + plComponent::kBlkComp, _T("ParticleUniWind"), 0, &gParticleUniWindDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_PARTICLE_UNIWIND, IDS_COMP_PARTICLE_UNIWIND, 0, 0, NULL, + + plParticleUniWindComponent::kStrength, _T("Strength"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 30.f, + p_range, 0.f, 100.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_STRENGTH, IDC_COMP_PARTICLE_WIND_STRENGTH_SPIN, 1.0, + end, + + plParticleUniWindComponent::kConstancy, _T("Constancy"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.f, + p_range, -75.f, 300.f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PARTICLE_WIND_CONSTANCY, IDC_COMP_PARTICLE_WIND_CONSTANCY_SPIN, 1.0, + end, + + plParticleUniWindComponent::kSwirl, _T("Swirl"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 100.f, + p_range, 0.f, 100.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_SWIRL, IDC_COMP_PARTICLE_WIND_SWIRL_SPIN, 1.0, + end, + + plParticleUniWindComponent::kHorizontal, _T("Horizontal"), TYPE_BOOL, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_PARTICLE_WIND_HORIZONTAL, + end, + + plParticleUniWindComponent::kMinSecs, _T("MinSecs"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 1.f, + p_range, 0.1f, 20.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_MINSECS, IDC_COMP_PARTICLE_WIND_MINSECS_SPIN, 1.0, + end, + + plParticleUniWindComponent::kMaxSecs, _T("MaxSecs"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 10.f, + p_range, 0.1f, 30.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_MAXSECS, IDC_COMP_PARTICLE_WIND_MAXSECS_SPIN, 1.0, + end, + + plParticleUniWindComponent::kRate, _T("Rate"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 10.f, + p_range, 0.1f, 50.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_RATE, IDC_COMP_PARTICLE_WIND_RATE_SPIN, 1.0, + end, + + plParticleUniWindComponent::kClampAngle, _T("ClampAngle"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 180.f, + p_range, 0.f, 180.f, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PARTICLE_WIND_CLAMPANG2, IDC_COMP_PARTICLE_WIND_CLAMPANG_SPIN2, 1.0, + end, + + plParticleUniWindComponent::kRefObject, _T("RefObject"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_PARTICLE_WIND_REFOBJECT, + p_prompt, IDS_COMP_CHOOSE_OBJECT, + end, + + end +); + +plParticleUniWindComponent::plParticleUniWindComponent() +{ + fClassDesc = &gParticleUniWindDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// plParticleFlockComponent + +class ParticleFlockEffectDlgProc : public ParamMap2UserDlgProc +{ +public: + ParticleFlockEffectDlgProc() {} + ~ParticleFlockEffectDlgProc() {} + + void IValidateSpinners(TimeValue t, IParamBlock2 *pb, IParamMap2 *map, UInt32 id) + { + UInt32 minIndex, maxIndex; + hsBool adjustMin; + switch(id) + { + case IDC_FLOCK_GOAL_DIST: + case IDC_FLOCK_GOAL_DIST_SPIN: + minIndex = plParticleFlockComponent::kGoalDist; maxIndex = plParticleFlockComponent::kFullChaseDist; adjustMin = false; + break; + case IDC_FLOCK_FULL_CHASE_DIST: + case IDC_FLOCK_FULL_CHASE_DIST_SPIN: + minIndex = plParticleFlockComponent::kGoalDist; maxIndex = plParticleFlockComponent::kFullChaseDist; adjustMin = true; + break; + default: + return; + } + + float min, max; + min = pb->GetFloat(minIndex, t); + max = pb->GetFloat(maxIndex, t); + + if (min > max) + { + if (adjustMin) + pb->SetValue(minIndex, t, max); + else + pb->SetValue(maxIndex, t, min); + + map->Invalidate(minIndex); + map->Invalidate(maxIndex); + } + } + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + int id = LOWORD(wParam); + + IParamBlock2 *pb = map->GetParamBlock(); + + switch (msg) + { + case WM_COMMAND: + case CC_SPINNER_CHANGE: + IValidateSpinners(t, pb, map, id); + return TRUE; + } + return FALSE; + } + void DeleteThis() {} +}; +static ParticleFlockEffectDlgProc gParticleFlockEffectDlgProc; + + + +void plParticleFlockComponent::AddToParticleSystem(plParticleSystem *sys, plMaxNode *node) +{ + plParticleFlockEffect* effect = nil; + if( !fEffect ) + { + effect = TRACKED_NEW plParticleFlockEffect(); + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), effect, node->GetLocation(), node->GetLoadMask()); + + hsPoint3 offset(fCompPB->GetFloat(ParamID(kOffsetX)), + fCompPB->GetFloat(ParamID(kOffsetY)), + fCompPB->GetFloat(ParamID(kOffsetZ))); + effect->SetTargetOffset(offset); + effect->SetInfluenceAvgRadius(fCompPB->GetFloat(ParamID(kInfAvgDist))); + effect->SetInfluenceRepelRadius(fCompPB->GetFloat(ParamID(kInfRepDist))); + + hsScalar goalDist = fCompPB->GetFloat(ParamID(kGoalDist)); + hsScalar fcDist = fCompPB->GetFloat(ParamID(kFullChaseDist)); + effect->SetGoalRadius(goalDist); + effect->SetFullChaseRadius(goalDist > fcDist ? goalDist : fcDist); // Fix old data + + effect->SetConformStr(fCompPB->GetFloat(ParamID(kInfAvgStr))); + effect->SetRepelStr(fCompPB->GetFloat(ParamID(kInfRepStr))); + effect->SetGoalOrbitStr(fCompPB->GetFloat(ParamID(kGoalOrbitStr))); + effect->SetGoalChaseStr(fCompPB->GetFloat(ParamID(kGoalChaseStr))); + effect->SetMaxChaseSpeed(fCompPB->GetFloat(ParamID(kMaxChaseSpeed))); + effect->SetMaxOrbitSpeed(fCompPB->GetFloat(ParamID(kMaxOrbitSpeed))); + effect->SetMaxParticles(sys->GetMaxTotalParticles()); + + fEffect = effect; + } + else + { + effect = plParticleFlockEffect::ConvertNoRef(fEffect); + } + hsAssert(effect, "Our effect pointer was wrong type?"); + hsgResMgr::ResMgr()->AddViaNotify( effect->GetKey(), TRACKED_NEW plGenRefMsg( sys->GetKey(), plRefMsg::kOnCreate, 0, plParticleSystem::kEffectForce ), plRefFlags::kActiveRef ); +} + +CLASS_DESC(plParticleFlockComponent, gParticleFlockDesc, "Particle Flock", "Flock", COMP_TYPE_PARTICLE, PARTICLE_FLOCK_COMPONENT_CLASS_ID) + +ParamBlockDesc2 gParticleFlockBk +( + + plComponent::kBlkComp, _T("ParticleFlock"), 0, &gParticleFlockDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_PARTICLE_FLOCK, IDS_COMP_PARTICLE_FLOCK, 0, 0, &gParticleFlockEffectDlgProc, + + plParticleFlockComponent::kOffsetX, _T("OffsetX"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_TARGET_OFFSETX, IDC_FLOCK_TARGET_OFFSETX_SPIN, 1.0, + end, + + plParticleFlockComponent::kOffsetY, _T("OffsetY"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_TARGET_OFFSETY, IDC_FLOCK_TARGET_OFFSETY_SPIN, 1.0, + end, + + plParticleFlockComponent::kOffsetZ, _T("OffsetZ"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_TARGET_OFFSETZ, IDC_FLOCK_TARGET_OFFSETZ_SPIN, 1.0, + end, + + plParticleFlockComponent::kInfAvgDist, _T("InfAvgDist"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_CONFORM_DIST, IDC_FLOCK_CONFORM_DIST_SPIN, 1.0, + end, + + plParticleFlockComponent::kInfRepDist, _T("InfRepDist"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_REPEL_DIST, IDC_FLOCK_REPEL_DIST_SPIN, 1.0, + end, + + plParticleFlockComponent::kGoalDist, _T("GoalDist"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_GOAL_DIST, IDC_FLOCK_GOAL_DIST_SPIN, 1.0, + end, + + plParticleFlockComponent::kInfAvgStr, _T("InfAvgStr"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_CONFORM_STR, IDC_FLOCK_CONFORM_STR_SPIN, 1.0, + end, + + plParticleFlockComponent::kInfRepStr, _T("InfRepStr"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_REPEL_STR, IDC_FLOCK_REPEL_STR_SPIN, 1.0, + end, + + plParticleFlockComponent::kGoalOrbitStr, _T("GoalStr"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_GOAL_STR, IDC_FLOCK_GOAL_STR_SPIN, 1.0, + end, + + plParticleFlockComponent::kMaxChaseSpeed, _T("MaxSpeed"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 999.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_MAX_SPEED, IDC_FLOCK_MAX_SPEED_SPIN, 1.0, + end, + + plParticleFlockComponent::kGoalChaseStr, _T("GoalChaseStr"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_GOAL_CHASE_STR, IDC_FLOCK_GOAL_CHASE_STR_SPIN, 1.0, + end, + + plParticleFlockComponent::kMaxOrbitSpeed, _T("MaxOrbitSpeed"), TYPE_FLOAT, 0, 0, + p_default, 20.0, + p_range, 0.0, 999.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_MAX_ORBIT_SPEED, IDC_FLOCK_MAX_ORBIT_SPEED_SPIN, 1.0, + end, + + plParticleFlockComponent::kFullChaseDist, _T("FullChaseDist"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_FLOCK_FULL_CHASE_DIST, IDC_FLOCK_FULL_CHASE_DIST_SPIN, 1.0, + end, + + end +); + +plParticleFlockComponent::plParticleFlockComponent() +{ + fClassDesc = &gParticleFlockDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plParticleComponents.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plParticleComponents.h new file mode 100644 index 00000000..e7b51296 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plParticleComponents.h @@ -0,0 +1,315 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_PARTICLE_COMPONENTS_H +#define PL_PARTICLE_COMPONENTS_H + +class plParticleMtl; +class plParticleSystem; +class plParticleEffect; +class plBoundInterface; +class plAGAnim; +class plLightGrpComponent; + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// stuff for plParticleComponent + +class ParticleStats +{ + +public: + hsScalar fConeAngle; + hsScalar fVelocityMin; + hsScalar fVelocityMax; + hsScalar fLifeMin; + hsScalar fLifeMax; + hsScalar fPPS; + hsScalar fScaleMin; + hsScalar fScaleMax; + hsScalar fGravity; + hsScalar fDrag; + hsScalar fWindMult; + hsScalar fMassRange; + hsScalar fRotRange; + hsScalar fPreSim; + hsScalar fHSize; + hsScalar fVSize; + UInt32 fGenType; + UInt32 fXTiles; + UInt32 fYTiles; + UInt32 fNormal; + UInt32 fOrientation; + hsBool fImmortal; + + ParticleStats() : fConeAngle(0.5), fVelocityMin(30.0), fVelocityMax(50.0), fLifeMin(5.0), fLifeMax(10.0), fPPS(20.0), + fScaleMin(80), fScaleMax(120), fGravity(100), fDrag(0), fWindMult(1.f), fMassRange(0), fRotRange(0), + fPreSim(0), fGenType(0), + fXTiles(1), fYTiles(1), fHSize(1), fVSize(1), fImmortal(false) {} +}; + +class ParticleCompDlgProc; +extern ParticleCompDlgProc gParticleCompDlgProc; + +class plParticleCoreComponent : public plComponent +{ +protected: +public: + enum // generation types + { + kGenPoint, + kGenMesh, + kGenOnePerVertex, + kGenNumOptions + }; + static const char *GenStrings[]; + + enum + { + kGenType, + kConeAngle, + kVelocityMin, + kVelocityMax, + kLifeMin, + kLifeMax, + kImmortal, + kPPS, + kScaleMin, + kScaleMax, + kGravity, + kDrag, + kPreSim, + kWindMult, + kMassRange, + kRotRange, + kFollowSystem + }; + + virtual void DeleteThis() { delete this; } + static hsBool IsParticleSystemComponent(plComponentBase *comp); + static hsBool NodeHasSystem(plMaxNode *pNode); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool AddToAnim(plAGAnim *anim, plMaxNode *node); + virtual hsBool GetParamVals(plMaxNode *pNode) = 0; + void SetParticleStats(plParticleMtl *mtl); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } + + void IHandleLights(plLightGrpComponent* liGrp, plParticleSystem* sys); + + virtual void SetEmitterReserve(int numEmitters) = 0; + virtual int GetEmitterReserve() const = 0; + + ParticleStats fUserInput; +}; + +class plParticleComponent : public plParticleCoreComponent +{ +protected: + int fEmitterReserve; + +public: + // Umm, don't touch this + static bool fAllowUnhide; + + plParticleComponent(); + hsBool GetParamVals(plMaxNode *pNode); + + virtual bool AllowUnhide() { return fAllowUnhide; } + + virtual void SetEmitterReserve(int numEmitters) { fEmitterReserve = numEmitters; } + virtual int GetEmitterReserve() const { return fEmitterReserve; } +}; + +#define PARTICLE_SYSTEM_COMPONENT_CLASS_ID Class_ID(0x684c5d88, 0x536b1a29) + +////////////////////////////////////////////////////////////////////////////////////////// +// +// Particle Effects Base class + +class plParticleEffectComponent : public plComponent +{ +protected: + plParticleEffect* fEffect; +public: + plParticleEffectComponent() : fEffect(nil) {} + virtual void DeleteThis() { delete this; } + + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return TRUE; }; + + virtual void AddToParticleSystem(plParticleSystem *sys, plMaxNode *node) = 0; + static hsBool IsParticleEffectComponent(plComponentBase *comp); +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// +// plParticleFadeComponent + +class plParticleFadeComponent : public plParticleEffectComponent +{ +public: + enum + { + kFadeDistance, + kFadeZ + }; + + plParticleFadeComponent(); + virtual void DeleteThis() { delete this; } + virtual void AddToParticleSystem(plParticleSystem *sys, plMaxNode *node); +}; + +#define PARTICLE_FADE_COMPONENT_CLASS_ID Class_ID(0x17496d81, 0x3bb14bc4) + +////////////////////////////////////////////////////////////////////////////////////////// +// +// plParticleVolumeComponent + +class plParticleVolumeComponent : public plParticleEffectComponent +{ +public: + enum + { + kSourceNode, + kOnImpact, + kBounceAmt, + kFrictionAmt + }; + // Things to do on impact + enum + { + kImpDefault, + kImpDie, + kImpBounce + }; + + + plParticleVolumeComponent(); + virtual void DeleteThis() { delete this; } + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual void AddToParticleSystem(plParticleSystem *sys, plMaxNode *node); + void BuildVolume(plMaxNode *node); + + virtual void CollectNonDrawables(INodeTab& nonDrawables); + +protected: + plBoundInterface *fBound; +}; + +#define PARTICLE_VOLUME_COMPONENT_CLASS_ID Class_ID(0x46087f90, 0x77625ce1) + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// plParticleWindComponent + +class plParticleWindComponent : public plParticleEffectComponent +{ +public: + enum + { + kScaleX, + kScaleY, + kScaleZ, + kSpeed, + kStrength, + kConstancy, + kSwirl, + kHorizontal, + kLocalize, + kClampAngle, + kRefObject + }; + + plParticleWindComponent(); + virtual void DeleteThis() { delete this; } + virtual void AddToParticleSystem(plParticleSystem *sys, plMaxNode *node); +}; + +const Class_ID PARTICLE_WIND_COMPONENT_CLASS_ID(0x728c40b2, 0x499068b3); + +////////////////////////////////////////////////////////////////////////////////////////// +// +// plParticleUniWindComponent + +class plParticleUniWindComponent : public plParticleEffectComponent +{ +public: + enum + { + kStrength, + kConstancy, + kSwirl, + kHorizontal, + kMinSecs, + kMaxSecs, + kRate, + kClampAngle, + kRefObject + }; + + plParticleUniWindComponent(); + virtual void DeleteThis() { delete this; } + virtual void AddToParticleSystem(plParticleSystem *sys, plMaxNode *node); +}; + +const Class_ID PARTICLE_UNIWIND_COMPONENT_CLASS_ID(0x2e1d4e50, 0x2f925aac); + +////////////////////////////////////////////////////////////////////////////////////////// +// +// plParticleFlockComponent + +class plParticleFlockComponent : public plParticleEffectComponent +{ +public: + enum + { + kOffsetX, + kOffsetY, + kOffsetZ, + kInfAvgDist, + kInfRepDist, + kGoalDist, + kInfAvgStr, + kInfRepStr, + kGoalOrbitStr, + kMaxChaseSpeed, + kGoalChaseStr, + kMaxOrbitSpeed, + kFullChaseDist, + }; + + plParticleFlockComponent(); + virtual void DeleteThis() { delete this; } + virtual void AddToParticleSystem(plParticleSystem *sys, plMaxNode *node); +}; + +const Class_ID PARTICLE_FLOCK_COMPONENT_CLASS_ID(0x3f522d7f, 0x409b66cc); + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysConstraintComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysConstraintComponents.cpp new file mode 100644 index 00000000..76972a20 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysConstraintComponents.cpp @@ -0,0 +1,1092 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +void DummyCodeIncludeFuncPhysConst() +{ +} + + +#include "HeadSpin.h" +#include "plComponent.h" +#include "plComponentReg.h" +#define PHYS_CONST_HINGE_CID Class_ID(0x790b1637, 0x32c94144) +#define PHYS_CONST_WHEEL_CID Class_ID(0x6e2958dc, 0x62e86e87) +#define PHYS_CONST_SS_CID Class_ID(0x14843886, 0x62a24e94) +#define PHYS_CONST_BRIDGE_CID Class_ID(0x5f99392f, 0x3e5a5807) + +OBSOLETE_CLASS(plPhysHingeConstraintComponent, gPhysHingeConstDesc, "(ex)Hinge Constraint", "(ex)Hinge Constraint", COMP_TYPE_PHYS_CONSTRAINTS, PHYS_CONST_HINGE_CID) +OBSOLETE_CLASS(plPhysBridgeComponent, gPhysBridgeConstDesc, "(ex)Bridge", "Bridge", COMP_TYPE_PHYS_CONSTRAINTS, PHYS_CONST_BRIDGE_CID) +OBSOLETE_CLASS(plStrongSpringConstraintComponent, gPhysStrongSpringConstDesc, "(ex)StrongSpring Constraint", "(ex)StrongSpring Constraint", COMP_TYPE_PHYS_CONSTRAINTS, PHYS_CONST_SS_CID) + +#if 0 + + + +#include +#include +#include + +//#include "../plHavok1/plHKConstraintSolver.h" //Got Havok Messiness... Must go before all plasma directories... +//#include "../plHavok1/plHavokConstraintTools.h" + +#include "plPhysicalComponents.h" +#include "plComponentProcBase.h" +#include "../plPhysical/plPhysicsGroups.h" +#include "../MaxMain/plPhysicalProps.h" +//#include "max.h" //Max Dependencies, reffed in plEventGroupDefs + +#include "resource.h" //Resource Dependencies +#include "hsResMgr.h" // Ibid + +#include "plComponent.h" //Component Dependencies +#include "plComponentReg.h" // Ibid +#include "../pnSceneObject/plSceneObject.h" // Ibid +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../plScene/plSceneNode.h" // Ibid +#include "../pnKeyedObject/plKey.h" // Ibid +#include "../MaxMain/plMaxNode.h" // Ibid + +#include "../MaxConvert/hsConverterUtils.h" //Conversion Dependencies +#include "../MaxConvert/hsControlConverter.h" // Ibid + +#include "../plHavok1/plHKPhysical.h" //Havok Dependencies +#include "../MaxMain/plPhysicalProps.h" + +#include "plgDispatch.h" //Message Dependencies +#include "../pnMessage/plObjRefMsg.h" // Ibid +#include "../pnMessage/plIntRefMsg.h" // Ibid +#include "../pnMessage/plNodeRefMsg.h" // Ibid +#include "../MaxMain/plPlasmaRefMsgs.h" // Ibid +#include "../plModifier/plAliasModifier.h" + +// +// DummyCodeIncludeFuncPhys Function. +// Necessary to keep the compiler from throwing away this file. +// No functions within are inherently called otherwise.... +// +// + +class plPhysConstraintAccessor : public PBAccessor +{ +public: + + //! Public Accessor Class, used in ParamBlock2 processing. + /*! + Workhorse for this Accessor Class (derived from Max's PBAccessor). + + When one of our parameters that is a ref changes, send out the component ref + changed message. Normally, messages from component refs are ignored since + they pass along all the messages of the ref, which generates a lot of false + converts. + */ + + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + plComponentBase *comp = (plComponentBase*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + + } +}; + + + +//! Global variable. +plPhysConstraintAccessor gPhysConstraintAccessor; + +//! Physical Hinge Constraint Component Class + +/*! + This Class offers the very limited functionality, specifically wheel constraint + Physical properties. It relies on the Core's Convert functionality. + The function GetParamVals is processed uniquely, to offer transparency into the Core + internal states found in its PhysicalStats member function, fUserInput. + + member functions: + + GetParamVals(plMaxNode *pNode,plErrorMsg *pErrMsg) + PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + + \sa plPhysDetectorComponent(), DeleteThis(), GetParamVals(), PreConvert(), Convert(), MaybeMakeLocal() and FixUpPhysical() +*/ + +class plPhysHingeConstraintComponent : plComponent +{ +public: + enum + { + kPositionPtr, + kParent, + kParentPinnedBool, + kFriction, + kUpperAngle, + kLowerAngle, + kRebound, + kStrength, + kUseParentBool, + kChildPinnedBool, + }; + + enum + { + kYAxis, + kXAxis, + kZAxis, + }; + + //! Constructor function for class + /*! + Herein the ClassDesc2 object that is used extensively by the ParamBlock2 + has gained accessibiltiy. Auto-Creation of the UI is done here as well. + + \sa DeleteThis(), GetParamVals(), PreConvert(), Convert(), MaybeMakeLocal() and FixUpPhysical() + + */ + + plPhysHingeConstraintComponent(); + + //! Detector GetParamVals function for class, with 1 input. + /*! + Partial Transparency. The following Internal states are accessible: + + -Bounce + -Friction + -Collision Reporting + -Bounding Surface State + -Disabling LOS + -Disable Ghost + + \param pNode a plMaxNode ptr, is the only formal parameter. + \sa DeleteThis(), plPhysicalCoreComponent(), Convert(), PreConvert(), MaybeMakeLocal() and FixUpPhysical() + + */ + + hsBool GetParamVals(plMaxNode *pNode, plErrorMsg *pErrMsg); + + //! Detector PreConvert, takes in two variables and return a hsBool. + /*! + Calls the function MaybeMakeLocal() and Sets Drawable to false. + + Takes in two variables, being: + \param node a plMaxNode ptr. + \param pErrMsg a pErrMsg ptr. + + \return A hsBool expressing the success of the operation. + \sa DeleteThis(), plPhysicalCoreComponent(), Convert(), GetParamVals(), MaybeMakeLocal() and FixUpPhysical() + */ + + hsBool SetupProperties(plMaxNode* node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode* node, plErrorMsg* plErrorMsg) { return true;} + + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +// +// plPhysHingeConstraint Component MacroConstructor +// + + +CLASS_DESC(plPhysHingeConstraintComponent, gPhysHingeConstDesc, "(ex)Hinge Constraint", "(ex)Hinge Constraint", COMP_TYPE_PHYS_CONSTRAINTS, PHYS_CONST_HINGE_CID) + + +enum { HackVal = 1}; + + +ParamBlockDesc2 gPhysHingeConstraintBk +( + plComponent::kBlkComp, _T("Hinge Constraint"), 0, &gPhysHingeConstDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_PHYS_HINGE_CONSTRAINT, IDS_COMP_PHYS_HINGE_CONSTRAINTS, 0, 0, NULL,//&gPhysCoreComponentProc, + + // params + + + plPhysHingeConstraintComponent::kPositionPtr, _T("Rotation Axis Conditions"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_COMP_PHYS_HINGE_AXIS_RADIO, IDC_COMP_PHYS_HINGE_AXIS_RADIO2, IDC_COMP_PHYS_HINGE_AXIS_RADIO3, + p_vals, plPhysHingeConstraintComponent::kXAxis, plPhysHingeConstraintComponent::kYAxis, plPhysHingeConstraintComponent::kZAxis, + p_default, plPhysHingeConstraintComponent::kZAxis, + end, + + plPhysHingeConstraintComponent::kUseParentBool, _T("UseParentChkBx"), TYPE_BOOL, P_ANIMATABLE, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_PHYS_USE_PARENT_BOOL, + p_default, TRUE, + p_enable_ctrls, 2, plPhysHingeConstraintComponent::kParent, plPhysHingeConstraintComponent::kParentPinnedBool, + end, + + + plPhysHingeConstraintComponent::kParent, _T("Parent"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_PHYS_PARENT, + p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + p_accessor, &gPhysConstraintAccessor, + end, + + + plPhysHingeConstraintComponent::kParentPinnedBool, _T("ParentPinnedChkBx"), TYPE_BOOL, P_ANIMATABLE, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_PHYS_PINNED_STATE_BOOL, + end, + + + plPhysHingeConstraintComponent::kFriction, _T("Friction"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 1.0, + p_range, 0.0, 50000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_FRICTION_SLIDER, IDC_COMP_PHYS_FRICTION_SPIN1, 1.0, + end, + + plPhysHingeConstraintComponent::kUpperAngle, _T("UpperLimit"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, -360.0, 360.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PHYS_OPENANGLE_EDIT, IDC_COMP_PHYS_OPENANGLE_SPIN, 1.0, + end, + + plPhysHingeConstraintComponent::kLowerAngle, _T("LowerLimit"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 360.0, + p_range, -360.0, 360.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PHYS_CLOSEANGLE_EDIT, IDC_COMP_PHYS_CLOSEANGLE_SPIN, 1.0, + end, + + + + plPhysHingeConstraintComponent::kRebound, _T("Rebound"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.1, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_TAU_SLIDER, IDC_COMP_PHYS_TAU_SPIN, .01f, + end, + + plPhysHingeConstraintComponent::kStrength, _T("Strength"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.5, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_STRENGTH_SLIDER, IDC_COMP_PHYS_STRENGTH_SPIN, .01f, + end, + + plPhysHingeConstraintComponent::kChildPinnedBool, _T("ChildPinnedChkBx"), TYPE_BOOL, P_ANIMATABLE, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_PHYS_PINNED_STATE_BOOL2, + end, + + + end +); + + + + + +// +// PhysHingeConstraint Component Constructor +// +// + + +plPhysHingeConstraintComponent::plPhysHingeConstraintComponent() +{ + fClassDesc = &gPhysHingeConstDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// +// plPhysHingeConstraintComponent GetParamVals Function +// +// + + +hsBool plPhysHingeConstraintComponent::GetParamVals(plMaxNode *node,plErrorMsg *pErrMsg) +{ + return true; +} + + +// +// Physical Wheel Constraint Component PreConvert Function +// +// + + +hsBool plPhysHingeConstraintComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + + /* + plMaxNodeData *pMD = pNode->GetMaxNodeData(); + pMD->SetMovable(true); + pMD->SetForceLocal(true); + + + plMaxNode* ParentNode = (plMaxNode*)fCompPB->GetINode(kParent); + plMaxNodeData *pMD2 = ParentNode->GetMaxNodeData(); + pMD2->SetMovable(true); + pMD2->SetForceLocal(true); + + +*/ +/* + hsBool HasPhysFlag = false; + for(int i = 0; i < pNode->NumAttachedComponents(); i++) + { + plComponentBase* thisComp = pNode->GetAttachedComponent(i); + if(thisComp->ClassID() == PHYSICS_DEBUG_CID && !HasPhysFlag) HasPhysFlag = true; + if(thisComp->ClassID() == PHYSICS_TERRAIN_CID && !HasPhysFlag) HasPhysFlag = true; + if(thisComp->ClassID() == PHYSICS_DETECTOR_CID && !HasPhysFlag) HasPhysFlag = true; + if(thisComp->ClassID() == PHYSICS_INVISIBLE_CID && !HasPhysFlag) HasPhysFlag = true; + if(thisComp->ClassID() == PHYSICS_PLAYER_CID && !HasPhysFlag) HasPhysFlag = true; + if(thisComp->ClassID() == PHYSICS_SIMPLE_CID && !HasPhysFlag) HasPhysFlag = true; + if(HasPhysFlag) break; + }*/ + + + + + GetParamVals(pNode, pErrMsg); + + + + + if(fCompPB->GetINode(kParent) && fCompPB->GetInt(kUseParentBool)) + if(!((plMaxNode*)fCompPB->GetINode(kParent))->CanConvert()) + { + pErrMsg->Set(true, "Ignored Parent Value", "Parent %s was set to be Ignored. No Constraint was used.", (fCompPB->GetINode(kParent)->GetName())).Show(); + pErrMsg->Set(false); + return false; + } + + + + + return true; +} + + +extern PlasmaToHavokQuat(Havok::Quaternion &a, hsQuat &b); + + + +hsBool plPhysHingeConstraintComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plHingeConstraintMod* HMod = TRACKED_NEW plHingeConstraintMod; + + + plMaxNode* ParentNode = (plMaxNode*)fCompPB->GetINode(kParent); + if(ParentNode) + { + if(!ParentNode->GetPhysicalProps()->IsUsed()) + { + pErrMsg->Set(true, "Need a Physical Component", "Object %s has a Physical Constraint but no Physical Component. No Constraint was used.", (node->GetName())).Show(); + pErrMsg->Set(false); + } + + + } + + if(!node->GetPhysicalProps()->IsUsed()) + { + pErrMsg->Set(true, "Need a Physical Component", "Object %s has a Physical Constraint but no Physical Component. No Constraint was used.", (node->GetName())).Show(); + pErrMsg->Set(false); + } + + + + + // Using the MotorTorque field, just to keep from creating yet another field in our bloated base class. + if(fCompPB->GetInt(kParentPinnedBool)) + HMod->SetParentPin(true); + + if(fCompPB->GetInt(kChildPinnedBool)) + HMod->SetChildPin(true); + + + if(fCompPB->GetFloat(kFriction)) + HMod->SetHCFriction(0,fCompPB->GetFloat(kFriction)); + + //Grab the pivot point from the child translate + hsPoint3 PP = node->GetLocalToWorld44().GetTranslate(); + hsVector3 PPVector; + PPVector.Set(PP.fX, PP.fY, PP.fZ); + HMod->SetPP(PPVector); + + + + plKey ParentKey = nil; + if(fCompPB->GetINode(kParent) && fCompPB->GetInt(kUseParentBool)) + if(((plMaxNode*)fCompPB->GetINode(kParent))->CanConvert()) + { + plMaxNode* ParentNode = (plMaxNode*)fCompPB->GetINode(kParent); + ParentKey = ParentNode->GetKey(); + + } + else + { + pErrMsg->Set(true, "Ignored Position Value", "Position %s was set to be Ignored. No Physical Proxy selected.", (fCompPB->GetINode(kPositionPtr)->GetName())); + pErrMsg->Set(false); + return false; + } + + hsVector3 HingeVector; + if(fCompPB->GetInt(kPositionPtr) == kZAxis) + { + HingeVector = node->GetLocalToWorld44().GetAxis(hsMatrix44::kUp); + HMod->SetHCLimits(kZAxis, 1, fCompPB->GetFloat(kUpperAngle)); + HMod->SetHCLimits(kZAxis, 0, fCompPB->GetFloat(kLowerAngle)); + + + } + else if(fCompPB->GetInt(kPositionPtr) == kYAxis) + { + HingeVector = node->GetLocalToWorld44().GetAxis(hsMatrix44::kView); + HMod->SetHCLimits(kYAxis, 0, -1*fCompPB->GetFloat(kUpperAngle)); + HMod->SetHCLimits(kYAxis, 1, -1*fCompPB->GetFloat(kLowerAngle)); + + } + else + { + HingeVector = node->GetLocalToWorld44().GetAxis(hsMatrix44::kRight); + HMod->SetHCLimits(kXAxis, 0, -1*fCompPB->GetFloat(kUpperAngle)); + HMod->SetHCLimits(kXAxis, 1, -1*fCompPB->GetFloat(kLowerAngle)); + + } + HMod->SetRotationAxis(-1*HingeVector); + + HMod->SetRR(fCompPB->GetFloat(kRebound)); + HMod->SetDamp(fCompPB->GetFloat(kStrength)); + + node->AddModifier(HMod, IGetUniqueName(node)); + if(ParentKey) + hsgResMgr::ResMgr()->AddViaNotify( ParentKey, TRACKED_NEW plGenRefMsg( HMod->GetKey(), plRefMsg::kOnCreate, plHavokConstraintsMod::kParentIdx, 0 ), plRefFlags::kPassiveRef ); + hsgResMgr::ResMgr()->AddViaNotify( node->GetKey(), TRACKED_NEW plGenRefMsg( HMod->GetKey(), plRefMsg::kOnCreate, plHavokConstraintsMod::kChildIdx, 0 ), plRefFlags::kPassiveRef ); + + + + + + + + + + return true; + +} + + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + + +class plPhysBridgeComponent : public plComponent +{ +protected: + // Only used during convert + bool fIsValidated; + +public: + enum + { + kSections, + kUpperAngle, + kLowerAngle, + kStiffness, + kStrength, + }; + + plPhysBridgeComponent(); + + void SetDefaultTau(); + void ValidateSections(); + + hsBool SetupProperties(plMaxNode* node, plErrorMsg* errMsg); + hsBool PreConvert(plMaxNode* node, plErrorMsg* errMsg); + hsBool Convert(plMaxNode* node, plErrorMsg* errMsg); +}; + +class plBridgeProc : public ParamMap2UserDlgProc +{ +protected: + void ILoadList(IParamBlock2* pb, HWND hDlg); + void IMoveListSel(bool up, IParamBlock2* pb, HWND hDlg); + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void DeleteThis() {} +}; +static plBridgeProc gBridgeComponentProc; + +CLASS_DESC(plPhysBridgeComponent, gPhysBridgeConstDesc, "(ex)Bridge", "Bridge", COMP_TYPE_PHYS_CONSTRAINTS, PHYS_CONST_BRIDGE_CID) + +ParamBlockDesc2 gPhysBridgeConstraintBk +( + plComponent::kBlkComp, _T("Bridge"), 0, &gPhysBridgeConstDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_PHYS_BRIDGE, IDS_COMP_PHYS_BRIDGE, 0, 0, &gBridgeComponentProc, + + plPhysBridgeComponent::kSections, _T("Sections"), TYPE_INODE_TAB, 0, 0, 0, + end, + + plPhysBridgeComponent::kUpperAngle, _T("UpperLimit"), TYPE_FLOAT, 0, 0, + p_default, 15.0, + p_range, 0.0, 360.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PHYS_OPENANGLE_EDIT, IDC_COMP_PHYS_OPENANGLE_SPIN, 1.0, + end, + + plPhysBridgeComponent::kLowerAngle, _T("LowerLimit"), TYPE_FLOAT, 0, 0, + p_default, -15.0, + p_range, -360.0, 0.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PHYS_CLOSEANGLE_EDIT, IDC_COMP_PHYS_CLOSEANGLE_SPIN, 1.0, + end, + + plPhysBridgeComponent::kStiffness, _T("Stiffness"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_STIFFNESS_EDIT, IDC_STIFFNESS_SPIN, 0.01, + end, + + plPhysBridgeComponent::kStrength, _T("Strength"), TYPE_FLOAT, 0, 0, + p_default, 0.4, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_STRENGTH_EDIT, IDC_STRENGTH_SPIN, 0.01, + end, + + end +); + +plPhysBridgeComponent::plPhysBridgeComponent() : fIsValidated(false) +{ + fClassDesc = &gPhysBridgeConstDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +void plPhysBridgeComponent::SetDefaultTau() +{ + int count = fCompPB->Count(kSections); + float tau = 1.f / sqrt(float(count)); + fCompPB->SetValue(kStrength, 0, tau); +} + +void plPhysBridgeComponent::ValidateSections() +{ + int i; + + // Make sure everything we're attached to is in our list of sections + for (i = 0; i < NumTargets(); i++) + { + INode* node = (INode*)GetTarget(i); + + bool found = false; + int count = fCompPB->Count(kSections); + for (int j = 0; j < count; j++) + { + if (node == fCompPB->GetINode(kSections, 0, j)) + { + found = true; + break; + } + } + + if (!found) + fCompPB->Append(kSections, 1, &node); + } + + // Make sure we're still attached to everything in our list of sections + for (i = fCompPB->Count(kSections)-1; i >= 0; i--) + { + plMaxNodeBase* node = (plMaxNodeBase*)fCompPB->GetINode(kSections, 0, i); + + if (!IsTarget(node)) + fCompPB->Delete(kSections, i, 1); + } +} + +hsBool plPhysBridgeComponent::SetupProperties(plMaxNode* node, plErrorMsg* errMsg) +{ + if (!fIsValidated) + { + ValidateSections(); + fIsValidated = true; + } + + plPhysicalProps* physProps = node->GetPhysicalProps(); + physProps->SetMass(100.f, node, errMsg); + physProps->SetBoundsType(plSimDefs::kHullBounds, node, errMsg); + + // If the parent is the start or end anchor for the bridge, pin it + int numSections = fCompPB->Count(kSections); + if (node == fCompPB->GetINode(kSections, 0, 0) || + (numSections > 0 && node == fCompPB->GetINode(kSections, 0, numSections-1))) + { + physProps->SetPinned(true, node, errMsg); + physProps->SetMemberGroup(plHKPhysicsGroups::kStaticSimulated, node, errMsg); + } + else + { + physProps->SetMemberGroup(plHKPhysicsGroups::kDynamicSimulated, node, errMsg); + } + + return true; +} + +hsBool plPhysBridgeComponent::PreConvert(plMaxNode* node, plErrorMsg* errMsg) +{ + fIsValidated = false; + + return true; +} + +hsBool plPhysBridgeComponent::Convert(plMaxNode* node, plErrorMsg* errMsg) +{ + plMaxNode* parent = nil; + + // Find the parent for this section + int count = fCompPB->Count(kSections); + for (int i = 0; i < count; i++) + { + plMaxNode* curNode = (plMaxNode*)fCompPB->GetINode(kSections, 0, i); + if (curNode == node) + { + if (i < count-1) + parent = (plMaxNode*)fCompPB->GetINode(kSections, 0, i+1); + break; + } + } + + // No parent, must be the end anchor for the bridge + if (!parent) + return false; + + plHingeConstraintMod* mod = TRACKED_NEW plHingeConstraintMod; + +// mod->SetHCFriction(0, 1.f); + mod->SetRR(fCompPB->GetFloat(kStrength));//1.f / sqrt(float(count))); + mod->SetDamp(fCompPB->GetFloat(kStiffness)); + + // Grab the pivot point from the child translate + hsPoint3 pivot = node->GetLocalToWorld44().GetTranslate(); + hsVector3 pivotVec; + pivotVec.Set(pivot.fX, pivot.fY, pivot.fZ); + mod->SetPP(pivotVec); + + // Cut'n'Paste + enum + { + kYAxis, + kXAxis, + kZAxis, + }; + + hsVector3 hingeVector; + hingeVector = node->GetLocalToWorld44().GetAxis(hsMatrix44::kRight); +// mod->SetHCLimits(kXAxis, 0, -1*fCompPB->GetFloat(kUpperAngle)); +// mod->SetHCLimits(kXAxis, 1, -1*fCompPB->GetFloat(kLowerAngle)); + + mod->SetRotationAxis(-1*hingeVector); + + node->AddModifier(mod, IGetUniqueName(node)); + hsgResMgr::ResMgr()->AddViaNotify(parent->GetKey(), TRACKED_NEW plGenRefMsg(mod->GetKey(), plRefMsg::kOnCreate, plHavokConstraintsMod::kParentIdx, 0), plRefFlags::kPassiveRef); + hsgResMgr::ResMgr()->AddViaNotify(node->GetKey(), TRACKED_NEW plGenRefMsg(mod->GetKey(), plRefMsg::kOnCreate, plHavokConstraintsMod::kChildIdx, 0), plRefFlags::kPassiveRef); + + return true; +} + +/// + +void plBridgeProc::ILoadList(IParamBlock2* pb, HWND hDlg) +{ + HWND hList = GetDlgItem(hDlg, IDC_SECT_LIST); + for (int i = 0; i < pb->Count(plPhysBridgeComponent::kSections); i++) + { + INode* node = pb->GetINode(plPhysBridgeComponent::kSections, 0, i); + ListBox_AddString(hList, node->GetName()); + } +} + +void plBridgeProc::IMoveListSel(bool up, IParamBlock2* pb, HWND hDlg) +{ + HWND hList = GetDlgItem(hDlg, IDC_SECT_LIST); + int idx = ListBox_GetCurSel(hList); + int count = ListBox_GetCount(hList); + + int newIdx = 0; + if (up && idx > 0) + newIdx = idx-1; + else if (!up && idx < count-1) + newIdx = idx+1; + else + return; + + INode* node = pb->GetINode(plPhysBridgeComponent::kSections, 0, idx); + INode* swapNode = pb->GetINode(plPhysBridgeComponent::kSections, 0, newIdx); + + pb->SetValue(plPhysBridgeComponent::kSections, 0, node, newIdx); + pb->SetValue(plPhysBridgeComponent::kSections, 0, swapNode, idx); + + ListBox_ResetContent(hList); + ILoadList(pb, hDlg); + ListBox_SetCurSel(hList, newIdx); +} + +BOOL plBridgeProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + plPhysBridgeComponent* comp = (plPhysBridgeComponent*)pm->GetParamBlock()->GetOwner(); + comp->ValidateSections(); + + ILoadList(pm->GetParamBlock(), hWnd); + } + return TRUE; + + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case BN_CLICKED: + if (LOWORD(wParam) == IDC_UP_BUTTON || LOWORD(wParam) == IDC_DN_BUTTON) + { + IMoveListSel((LOWORD(wParam) == IDC_UP_BUTTON), pm->GetParamBlock(), hWnd); + return TRUE; + } + else if (LOWORD(wParam) == IDC_DEF_STR_BUTTON) + { + plPhysBridgeComponent* comp = (plPhysBridgeComponent*)pm->GetParamBlock()->GetOwner(); + comp->SetDefaultTau(); + pm->Invalidate(plPhysBridgeComponent::kStrength); + return TRUE; + } + break; + case LBN_SELCHANGE: + { + bool hasSelection = (ListBox_GetCurSel(HWND(lParam)) != LB_ERR); + EnableWindow(GetDlgItem(hWnd, IDC_UP_BUTTON), hasSelection); + EnableWindow(GetDlgItem(hWnd, IDC_DN_BUTTON), hasSelection); + } + break; + } + break; + } + + return FALSE; +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + + +class plStrongSpringConstraintComponent : plComponent +{ +public: + + enum + { + kLength = 0, + kRebound, + kStrength, + kParent, + kParentPinnedBool, + }; + + + //! Constructor function for class + /*! + Herein the ClassDesc2 object that is used extensively by the ParamBlock2 + has gained accessibiltiy. Auto-Creation of the UI is done here as well. + + \sa DeleteThis(), GetParamVals(), PreConvert(), Convert(), MaybeMakeLocal() and FixUpPhysical() + + */ + + plStrongSpringConstraintComponent(); + + //! Detector GetParamVals function for class, with 1 input. + /*! + Partial Transparency. The following Internal states are accessible: + + -Bounce + -Friction + -Collision Reporting + -Bounding Surface State + -Disabling LOS + -Disable Ghost + + \param pNode a plMaxNode ptr, is the only formal parameter. + \sa DeleteThis(), plPhysicalCoreComponent(), Convert(), PreConvert(), MaybeMakeLocal() and FixUpPhysical() + + */ + + hsBool GetParamVals(plMaxNode *pNode, plErrorMsg *pErrMsg); + + //! Detector PreConvert, takes in two variables and return a hsBool. + /*! + Calls the function MaybeMakeLocal() and Sets Drawable to false. + + Takes in two variables, being: + \param node a plMaxNode ptr. + \param pErrMsg a pErrMsg ptr. + + \return A hsBool expressing the success of the operation. + \sa DeleteThis(), plPhysicalCoreComponent(), Convert(), GetParamVals(), MaybeMakeLocal() and FixUpPhysical() + */ + + hsBool SetupProperties(plMaxNode* node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode* node, plErrorMsg* plErrorMsg) { return true;} + + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + +}; + + +// +// Hack! Hack! Don't talk back! +// + + +// +// Detector Component MacroConstructor +// + + +CLASS_DESC(plStrongSpringConstraintComponent, gPhysStrongSpringConstDesc, "(ex)StrongSpring Constraint", "(ex)StrongSpring Constraint", COMP_TYPE_PHYS_CONSTRAINTS, PHYS_CONST_SS_CID) + + +// +// Wheel Constraint ParamBlock2 +// +// + + +ParamBlockDesc2 gPhysSSConstraintBk +( + plComponent::kBlkComp, _T("Strong Spring Constraint"), 0, &gPhysStrongSpringConstDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_PHYS_SS_CONSTRAINT, IDS_COMP_PHYS_SS_CONSTRAINTS, 0, 0, NULL,//&gPhysCoreComponentProc, + + // params + + plStrongSpringConstraintComponent::kLength, _T("Length"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, 0.0, 500.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHY_SS_LENGTH_EDIT, IDC_COMP_PHY_SS_LENGTH_SPIN, 1.0, + end, + + plStrongSpringConstraintComponent::kRebound, _T("Rebound"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_SS_REBOUND_EDIT, IDC_COMP_PHYS_SS_REBOUND_SPIN, 0.1, + end, + + plStrongSpringConstraintComponent::kStrength, _T("Strength"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.5, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_SS_STRENGTH_EDIT, IDC_COMP_PHYS_SS_STRENGTH_SPIN, 0.1, + end, + + plStrongSpringConstraintComponent::kParent, _T("Parent"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_PHYS_PARENT, + //p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + p_accessor, &gPhysConstraintAccessor, + end, + + plStrongSpringConstraintComponent::kParentPinnedBool, _T("PinnedChkBx"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_PHYS_USE_PARENT_BOOL, + end, + + + + end +); + + +// +// Detector Component Constructor +// +// + + +plStrongSpringConstraintComponent::plStrongSpringConstraintComponent() +{ + fClassDesc = &gPhysStrongSpringConstDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// +// Detector Component GetParamVals Function +// +// + + +extern PlasmaToHavokQuat(Havok::Quaternion &a, hsQuat &b); + + +hsBool plStrongSpringConstraintComponent::GetParamVals(plMaxNode *node,plErrorMsg *pErrMsg) +{ + + + return true; +} + + +// +// Physical Wheel Constraint Component PreConvert Function +// +// + + +hsBool plStrongSpringConstraintComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + + GetParamVals(pNode, pErrMsg); + + if(fCompPB->GetINode(kParent)) + if(!((plMaxNode*)fCompPB->GetINode(kParent))->CanConvert()) + { + pErrMsg->Set(true, "Ignored Parent Value", "Parent %s was set to be Ignored. No Constraint was used.", (fCompPB->GetINode(kParent)->GetName())); + pErrMsg->Set(false); + return false; + } + else + { + pErrMsg->Set(true, "Bad Parent Value", " Parent %s wasn't selected, all are currently necessary.", (fCompPB->GetINode(kParent)->GetName())); + pErrMsg->Set(false); + return false; + } + + if(500 >= fCompPB->GetFloat(kLength) && fCompPB->GetFloat(kLength) >= 0 ) + { + } + else + { + pErrMsg->Set(true, "Bad Spring Length Value", " Strong Spring Constraint was out of range. Bailing out!."); + pErrMsg->Set(false); + return false; + + } + + if(fCompPB->GetFloat(kRebound) >= 0 && fCompPB->GetFloat(kRebound) <= 1) + { + } + else + { + pErrMsg->Set(true, "Bad Spring Rebound Value", " Strong Spring Constraint was lost. Bailing out!."); + pErrMsg->Set(false); + return false; + + } + + if(fCompPB->GetFloat(kStrength) >= 0 && fCompPB->GetFloat(kStrength) <= 1) + { + } + else + { + pErrMsg->Set(true, "Bad Spring Strength Value", " Strong Spring Constraint was lost. Bailing out!."); + pErrMsg->Set(false); + return false; + + } + + + + + return true; +} + + + +hsBool plStrongSpringConstraintComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plStrongSpringConstraintMod* HMod = TRACKED_NEW plStrongSpringConstraintMod; + + HMod->SetDamp(fCompPB->GetFloat(kStrength)); + HMod->SetRR(fCompPB->GetFloat(kRebound)); + + //No MaximumTorque here, might as well use that field for the Pinned state for the Parent... + HMod->SetParentPin(fCompPB->GetInt(kParentPinnedBool)); + + + Object *obj = fCompPB->GetINode(kParent)->EvalWorldState(0/*hsConverterUtils::Instance().GetTime(GetInterface())*/).obj; + + plKey ParentKey = nil; + if(fCompPB->GetINode(kParent)) + if(((plMaxNode*)fCompPB->GetINode(kParent))->CanConvert() && (obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) || obj->SuperClassID() == GEOMOBJECT_CLASS_ID )) + { + plMaxNode* ParentNode = (plMaxNode*)fCompPB->GetINode(kParent); + ParentKey = ParentNode->GetKey(); + } + else + { + pErrMsg->Set(true, "Ignored Parent Node", "Parent Node %s was set to be Ignored. Bad! Bad!.", (fCompPB->GetINode(kParent)->GetName())); + pErrMsg->Set(false); + return false; + } + else + { + +// pErrMsg->Set(true, "Bad Parent Node", " Parent Node %s wasn't selected. Strong Spring Constraint failed.", (fCompPB->GetINode(kParent)->GetName())); +// pErrMsg->Set(false); +// return false; + } + + + //No motor Angle here, might as well use the field for Length Storage... + HMod->SetFixedLength(fCompPB->GetFloat(kLength)); + + + + node->AddModifier(HMod, IGetUniqueName(node)); + hsgResMgr::ResMgr()->AddViaNotify( ParentKey, TRACKED_NEW plGenRefMsg( HMod->GetKey(), plRefMsg::kOnCreate, plHavokConstraintsMod::kParentIdx, 0 ), plRefFlags::kPassiveRef ); + hsgResMgr::ResMgr()->AddViaNotify( node->GetKey(), TRACKED_NEW plGenRefMsg( HMod->GetKey(), plRefMsg::kOnCreate, plHavokConstraintsMod::kChildIdx, 0 ), plRefFlags::kPassiveRef ); + + + + + + + + + + return true; + +} + + + + + + + + + + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicalComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicalComponents.cpp new file mode 100644 index 00000000..b644236b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicalComponents.cpp @@ -0,0 +1,1999 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +// singular +#include "plPhysicalComponents.h" + +// local +#include "plComponentReg.h" +#include "resource.h" + +// global +#include "hsResMgr.h" + +// other +#include "../plPhysical/plSimDefs.h" +#include "plPhysicsGroups.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxMain/plPhysicalProps.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../plPhysical/plCollisionDetector.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../plMessage/plSwimMsg.h" +#include "../plAvatar/plSwimRegion.h" +#include "../plMessage/plRideAnimatedPhysMsg.h" + +///////////////////////////////////////////////////////////////// +// +// THE DUMMY +// +///////////////////////////////////////////////////////////////// + +// Necessary to keep the compiler from throwing away this file. +void DummyCodeIncludeFuncPhys() +{ +} + +///////////////////////////////////////////////////////////////// +// +// plPhysicCoreComponent +// +///////////////////////////////////////////////////////////////// + +// SetupProperties ----------------------------------------------------------------- +// ---------------- +hsBool plPhysicCoreComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} + +// PreConvert ---------------------------------------------------------------- +// ----------- +hsBool plPhysicCoreComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +// IFixBounds -------------------------- +// ----------- +void plPhysicCoreComponent::IFixBounds() +{ + if (fCompPB->GetInt(kBoundCondRadio) == 0) + { + // zero is a bad value left over from an old version of the GUI: upgrade in place + fCompPB->Reset(kBoundCondRadio); // reset to default + } +} + +// IGetProxy ---------------------------------------------------------------- +// ---------- +hsBool plPhysicCoreComponent::IGetProxy(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (fCompPB->GetInt(kCustomBoundField)) + { + plMaxNode *boundNode = (plMaxNode*)fCompPB->GetINode(kCustomBoundListStuff); + if (boundNode && boundNode->CanConvert()) + return node->GetPhysicalProps()->SetProxyNode(boundNode, node, pErrMsg); + } + + return true; +} + +// Convert ---------------------------------------------------------------- +// -------- +hsBool plPhysicCoreComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + + +//! Global Accessor Class, used in ParamBlock2 processing. +/*! + + When one of our parameters that is a ref changes, send out the component ref + changed message. Normally, messages from component refs are ignored since + they pass along all the messages of the ref, which generates a lot of false + converts. +*/ + +class plPhysCoreAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (id == plPhysicCoreComponent::kCustomBoundListStuff) + { + plComponentBase *comp = (plComponentBase*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +plPhysCoreAccessor gPhysCoreAccessor; +/* +//! Physics Debug Component Class +class plPhysDebugComponent : public plPhysicCoreComponent +{ +public: + plPhysDebugComponent(); + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables); +}; +*/ +OBSOLETE_CLASS(plPhysDebugComponent, gPhysDebugDesc, "Physics Debug", "PhysDebug", COMP_TYPE_PHYSICAL, PHYSICS_DEBUG_CID) + +enum +{ + kPhysMain, + kPhysMember, + kPhysBounce, + kPhysReport, +}; +/* +ParamBlockDesc2 gPhysicalBk +( + plComponent::kBlkComp, _T("physicsDebug"), 0, &gPhysDebugDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + //Roll out + 4, + kPhysMain, IDD_COMP_PHYSICAL, IDS_COMP_PHYS_DEBUG, 0, 0, NULL, + kPhysMember, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_MEMBER, 0, APPENDROLL_CLOSED, &gMemberGroupProc, + kPhysBounce, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_BOUNCE, 0, APPENDROLL_CLOSED, &gBounceGroupProc, + kPhysReport, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_REPORT, 0, APPENDROLL_CLOSED, &gReportGroupProc, + + // params + plPhysicCoreComponent::kMass, _T("Mass"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, 0.0, 500.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYSICAL_EDIT1, IDC_COMP_PHYSICAL_SPIN1, 1.0, + end, + + plPhysicCoreComponent::kBounce, _T("Bounce"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYSICAL_EDIT2, IDC_COMP_PHYSICAL_SPIN2, 0.1, + end, + + plPhysicCoreComponent::kFriction, _T("Friction"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.5, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYSICAL_EDIT3, IDC_COMP_PHYSICAL_SPIN3, 0.1, + end, + + plPhysicCoreComponent::kStartForceX, _T("StartForceX"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0f, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PHYSICAL_P3SF_EDIT1,IDC_COMP_PHYSICAL_P3SF_SPIN1, 0.1f, + end, + + plPhysicCoreComponent::kStartForceY, _T("StartForceY"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0f, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PHYSICAL_P3SF_EDIT2,IDC_COMP_PHYSICAL_P3SF_SPIN2, 0.1f, + end, + + plPhysicCoreComponent::kStartForceZ, _T("StartForceZ"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0f, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PHYSICAL_P3SF_EDIT3,IDC_COMP_PHYSICAL_P3SF_SPIN3, 0.1f, + end, + + plPhysicCoreComponent::kStartTorqueX, _T("StartTorqueX"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0f, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PHYSICAL_P3ST_EDIT1,IDC_COMP_PHYSICAL_P3ST_SPIN1, 0.1f, + end, + + plPhysicCoreComponent::kStartTorqueY, _T("StartTorqueY"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0f, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PHYSICAL_P3ST_EDIT2,IDC_COMP_PHYSICAL_P3ST_SPIN2, 0.1f, + end, + + plPhysicCoreComponent::kStartTorqueZ, _T("StartTorqueZ"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0f, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_PHYSICAL_P3ST_EDIT3,IDC_COMP_PHYSICAL_P3ST_SPIN3, 0.1f, + end, + + plPhysicCoreComponent::kBoundCondRadio, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, kPhysMain, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + plPhysicCoreComponent::kCustomBoundListStuff, _T("UserBoundChoice"), TYPE_INODE, 0, 0, + p_ui, kPhysMain, TYPE_PICKNODEBUTTON, IDC_COMP_PHYS_PICKSTATE_BASE, + p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + p_accessor, &gPhysCoreAccessor, + end, + + plPhysicCoreComponent::kCustomBoundField, _T("UserBoundCheckBx"), TYPE_BOOL, 0, 0, + p_default, false, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_COMP_PHYS_CUSTOMCHK, + p_enable_ctrls, 1, plPhysicCoreComponent::kCustomBoundListStuff, + end, + + plPhysicCoreComponent::kLOSChkBx, _T("LOSChkBx"), TYPE_BOOL, P_ANIMATABLE, 0, + p_default, FALSE, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_COMP_CHECK_LOS, + end, + + plPhysicCoreComponent::kAlignProxyShape, _T("AlignShapeChkBx"), TYPE_BOOL, P_ANIMATABLE, 0, + p_default, FALSE, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_COMP_PHYS_ALIGN_SHAPE_BOOL, + end, + + // Event Groups + plPhysicCoreComponent::kMemberGroups_DEAD, _T("memberGroups"), TYPE_INT, 0,0, + end, + + plPhysicCoreComponent::kBounceGroups_DEAD, _T("bounceGroups"), TYPE_INT, 0,0, + end, + + plPhysicCoreComponent::kReportGroups, _T("memberGroups"), TYPE_INT, 0,0, + end, + + plPhysicCoreComponent::kGroup, _T("group"), TYPE_INT, 0,0, + end, + + end +); + +plPhysDebugComponent::plPhysDebugComponent() +{ + fClassDesc = &gPhysDebugDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +void plPhysDebugComponent::CollectNonDrawables(INodeTab& nonDrawables) +{ + if (fCompPB->GetInt(kCustomBoundField)) + { + INode* boundsNode = fCompPB->GetINode(kCustomBoundListStuff); + if( boundsNode ) + nonDrawables.Append(1, &boundsNode); + } +} + + +hsBool plPhysDebugComponent::SetupProperties(plMaxNode *node, plErrorMsg *errMsg) +{ + IFixBounds(); + + plPhysicalProps *physProps = node->GetPhysicalProps(); + + physProps->SetMass(fCompPB->GetFloat(kMass), node, errMsg); + + physProps->SetRestitution(fCompPB->GetFloat(kBounce), node, errMsg); + physProps->SetFriction(fCompPB->GetFloat(kFriction), node, errMsg); + physProps->SetBoundsType(fCompPB->GetInt(kBoundCondRadio), node, errMsg); + if(fCompPB->GetInt(kLOSChkBx)) + physProps->SetLOSBlockCamera(true, node, errMsg); // *** we really need to have four check boxes now.... + physProps->SetPinned(false, node, errMsg); + physProps->SetAlignToOwner(fCompPB->GetInt(kAlignProxyShape) != 0, node, errMsg); + + + physProps->SetMemberGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kMemberGroups_DEAD), node, errMsg); + physProps->SetBounceGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kBounceGroups_DEAD), node, errMsg); + physProps->SetReportGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kReportGroups), node, errMsg); + + return IGetProxy(node, errMsg); +} +*/ + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plPhysTerrainComponent +// +///////////////////////////////////////////////////////////////////////////////////////// + +class plPhysTerrainComponent : public plPhysicCoreComponent +{ +public: + plPhysTerrainComponent(); + hsBool SetupProperties(plMaxNode* node,plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables); +}; + + +CLASS_DESC(plPhysTerrainComponent, gPhysTerrainDesc, "Terrain", "Terrain", COMP_TYPE_PHYSICAL, PHYSICS_TERRAIN_CID) + + +ParamBlockDesc2 gPhysTerrainBk +( + plComponent::kBlkComp, _T("Terrain"), 0, &gPhysTerrainDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + //Roll out + 1, + kPhysMain, IDD_COMP_PHYS_TERRAIN, IDS_COMP_PHYS_TERRAIN, 0, 0, NULL, +// kPhysMember, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_MEMBER, 0, APPENDROLL_CLOSED, &gMemberGroupProc, +// kPhysBounce, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_BOUNCE, 0, APPENDROLL_CLOSED, &gBounceGroupProc, + + // params + + plPhysicCoreComponent::kFriction, _T("Friction"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.5, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_TERRAIN_EDIT1, IDC_COMP_PHYS_TERRAIN_SPIN1, 0.0001f, + end, + + plPhysicCoreComponent::kBounce, _T("Bounce"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_TERRAIN_EDIT2, IDC_COMP_PHYS_TERRAIN_SPIN2, 0.0001f, + end, + + plPhysicCoreComponent::kBoundCondRadio, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, kPhysMain, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + plPhysicCoreComponent::kCustomBoundListStuff, _T("UserBoundChoice"), TYPE_INODE, 0, 0, + p_ui, kPhysMain, TYPE_PICKNODEBUTTON, IDC_COMP_PHYS_PICKSTATE_TERRAIN, + p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_TERRAIN, + p_accessor, &gPhysCoreAccessor, + end, + + plPhysicCoreComponent::kCustomBoundField, _T("UserBoundCheckBx"), TYPE_BOOL, 0, 0, + p_default, false, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_COMP_PHYS_CUSTOMCHK, + p_enable_ctrls, 1, plPhysicCoreComponent::kCustomBoundListStuff, + end, + + plPhysicCoreComponent::kAlignProxyShape, _T("AlignShapeChkBx"), TYPE_BOOL, P_ANIMATABLE, 0, + p_default, FALSE, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_COMP_PHYS_ALIGN_SHAPE_BOOL, + end, + + // Event Groups + plPhysicCoreComponent::kMemberGroups_DEAD, _T("memberGroups"), TYPE_INT, 0,0, + p_default, plPhysicsGroups_DEAD::kStaticSimulated, + end, + + plPhysicCoreComponent::kBounceGroups_DEAD, _T("bounceGroups"), TYPE_INT, 0,0, + end, + + end +); + +plPhysTerrainComponent::plPhysTerrainComponent() +{ + fClassDesc = &gPhysTerrainDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +void plPhysTerrainComponent::CollectNonDrawables(INodeTab& nonDrawables) +{ + if (fCompPB->GetInt(kCustomBoundField)) + { + INode* boundsNode = fCompPB->GetINode(kCustomBoundListStuff); + if( boundsNode ) + nonDrawables.Append(1, &boundsNode); + } +} + +void ValidateGroups(IParamBlock2* pb, int memberID, int bounceID, plComponent* comp, plErrorMsg* pErrMsg) +{ + UInt32 defMember = pb->GetParamDef(memberID).def.i; + UInt32 member = pb->GetInt(memberID); + + UInt32 defCollide = pb->GetParamDef(bounceID).def.i; + UInt32 collide = pb->GetInt(bounceID); + + if (defMember != member || defCollide != collide) + { + pErrMsg->Set(true, + "Physics Conflict", + "The legacy physical component \"%s\" has non-default member or collide groups.\nPlease recreate it.", + comp->GetINode()->GetName()).Show(); + pErrMsg->Set(false); + } +} + +hsBool plPhysTerrainComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + IFixBounds(); + + plPhysicalProps *physProps = pNode->GetPhysicalProps(); + + physProps->SetMass(0, pNode, pErrMsg); + + physProps->SetFriction(fCompPB->GetFloat(kFriction), pNode, pErrMsg); + physProps->SetRestitution(fCompPB->GetFloat(kBounce), pNode, pErrMsg); + physProps->SetBoundsType(fCompPB->GetInt(kBoundCondRadio), pNode, pErrMsg); + physProps->SetAlignToOwner(fCompPB->GetInt(kAlignProxyShape) != 0, pNode, pErrMsg); + + ValidateGroups(fCompPB, plPhysicCoreComponent::kMemberGroups_DEAD, plPhysicCoreComponent::kBounceGroups_DEAD, this, pErrMsg); +// physProps->SetMemberGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kMemberGroups), pNode, pErrMsg); +// physProps->SetBounceGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kBounceGroups), pNode, pErrMsg); + physProps->SetGroup(plSimDefs::kGroupStatic, pNode, pErrMsg); + + physProps->SetLOSBlockCamera(true, pNode, pErrMsg); + physProps->SetLOSBlockUI(true, pNode, pErrMsg); + physProps->SetLOSAvatarWalkable(true, pNode, pErrMsg); + + return IGetProxy(pNode, pErrMsg); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plProxyTerrainComponent +// +///////////////////////////////////////////////////////////////////////////////////////// + +class plProxyTerrainComponent : public plPhysicCoreComponent +{ +public: + plProxyTerrainComponent(); + hsBool SetupProperties(plMaxNode* node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + +CLASS_DESC(plProxyTerrainComponent, gProxyTerrainDesc, "Proxy Terrain", "ProxyTerrain", COMP_TYPE_PHYSICAL, PHYSICS_INVISIBLE_CID) + +ParamBlockDesc2 gPhysInvisibleBk +( + plComponent::kBlkComp, _T("ProxyTerrain"), 0, &gProxyTerrainDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + //Roll out + 1, + kPhysMain, IDD_COMP_PHYS_PROXY_TERRAIN, IDS_COMP_PHYS_PROXY_TERRAIN, 0, 0, NULL, +// kPhysMember, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_MEMBER, 0, APPENDROLL_CLOSED, &gMemberGroupProc, +// kPhysBounce, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_BOUNCE, 0, APPENDROLL_CLOSED, &gBounceGroupProc, + + // params + plPhysicCoreComponent::kBoundCondRadio, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, kPhysMain, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + plPhysicCoreComponent::kUILOSChkBx, _T("UILOSChkBx"), TYPE_BOOL, P_ANIMATABLE, 0, + p_default, FALSE, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_COMP_CHECK_LOS, + end, + + plPhysicCoreComponent::kCamLOSChkBx, _T("CamLOSChkBx"), TYPE_BOOL, P_ANIMATABLE, 0, + p_default, FALSE, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_COMP_CHECK_LOS2, + end, + + plPhysicCoreComponent::kCamAvoidChkBx, _T("CamLOSChkBx"), TYPE_BOOL, P_ANIMATABLE, 0, + p_default, FALSE, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_COMP_CHECK_LOS3, + end, + + plPhysicCoreComponent::kFriction, _T("Friction"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.5, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_TERRAIN_EDIT1, IDC_COMP_PHYS_TERRAIN_SPIN1, 0.0001f, + end, + + plPhysicCoreComponent::kBounce, _T("Bounce"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_INVIS_BOUNCE_EDIT, IDC_COMP_PHYS_INVIS_BOUNCE_SPIN, 0.0001f, + end, + + // Event Groups + plPhysicCoreComponent::kMemberGroups_DEAD, _T("memberGroups"), TYPE_INT, 0,0, + p_default, plPhysicsGroups_DEAD::kStaticSimulated, + end, + plPhysicCoreComponent::kBounceGroups_DEAD, _T("bounceGroups"), TYPE_INT, 0,0, + end, + + end +); + +plProxyTerrainComponent::plProxyTerrainComponent() +{ + fClassDesc = &gProxyTerrainDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plProxyTerrainComponent::SetupProperties(plMaxNode *node, plErrorMsg *errMsg) +{ + IFixBounds(); + node->SetDrawable(false); + + plPhysicalProps *physProps = node->GetPhysicalProps(); + + physProps->SetMass(0, node, errMsg); + + if (fCompPB->GetInt(kUILOSChkBx) || fCompPB->GetInt(kCamLOSChkBx)) + { +// XXX physProps->SetAllowLOS(false, pNode, pErrMsg); + + + if (fCompPB->GetInt(kUILOSChkBx)) + physProps->SetLOSBlockUI(true, node, errMsg); + if (fCompPB->GetInt(kCamLOSChkBx)) + physProps->SetLOSBlockCamera(true, node, errMsg); + if (fCompPB->GetInt(kCamAvoidChkBx)) + physProps->SetCameraAvoidFlag(true, node, errMsg); + } + + physProps->SetLOSAvatarWalkable(true, node, errMsg); + + physProps->SetFriction(fCompPB->GetFloat(kFriction), node, errMsg); + physProps->SetBoundsType(fCompPB->GetInt(kBoundCondRadio), node, errMsg); + + physProps->SetGroup(plSimDefs::kGroupStatic, node, errMsg); + ValidateGroups(fCompPB, plPhysicCoreComponent::kMemberGroups_DEAD, plPhysicCoreComponent::kBounceGroups_DEAD, this, errMsg); +// physProps->SetMemberGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kMemberGroups), node, errMsg); +// physProps->SetBounceGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kBounceGroups), node, errMsg); + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plCameraBlockerComponent +// +///////////////////////////////////////////////////////////////////////////////////////// +class plCameraBlockerComponent : public plPhysicCoreComponent +{ +public: + plCameraBlockerComponent(); + + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + +CLASS_DESC(plCameraBlockerComponent, gCameraBlockerDesc, "(ex) Camera Blocker", "CameraBlocker", COMP_TYPE_PHYSICAL, PHYS_CAMERA_BLOCK_CID) + +ParamBlockDesc2 gCameraOccludeBlock +( + plComponent::kBlkComp, _T("cameraBlocker"), 0, &gCameraBlockerDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + //Roll out + 1, + kPhysMain, IDD_COMP_CAMERA_OCCLUDER, IDS_CAMERA_OCCLUDERS, 0, 0, NULL, +// kPhysMember, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_MEMBER, 0, APPENDROLL_CLOSED, &gMemberGroupProc, +// kPhysBounce, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_BOUNCE, 0, APPENDROLL_CLOSED, &gBounceGroupProc, + + plPhysicCoreComponent::kBoundCondRadio, _T("BlockerBounds"), TYPE_INT, 0, 0, + p_ui, kPhysMain, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + // Event Groups + plPhysicCoreComponent::kMemberGroups_DEAD, _T("memberGroups"), TYPE_INT, 0,0, + end, + + plPhysicCoreComponent::kBounceGroups_DEAD, _T("bounceGroups"), TYPE_INT, 0,0, + end, + + end +); + +plCameraBlockerComponent::plCameraBlockerComponent() +{ + fClassDesc = &gCameraBlockerDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plCameraBlockerComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + IFixBounds(); + + pNode->SetDrawable(false); + + plPhysicalProps *physProps = pNode->GetPhysicalProps(); + + physProps->SetMass(0, pNode, pErrMsg); + physProps->SetFriction(0, pNode, pErrMsg); +// physProps->SetAllowLOS(false, pNode, pErrMsg); + physProps->SetLOSBlockCamera(true, pNode, pErrMsg); +// physProps->SetUILOSFlag(true, pNode, pErrMsg); + physProps->SetBoundsType(fCompPB->GetInt(kBoundCondRadio), pNode, pErrMsg); + + physProps->SetGroup(plSimDefs::kGroupLOSOnly, pNode, pErrMsg); + ValidateGroups(fCompPB, plPhysicCoreComponent::kMemberGroups_DEAD, plPhysicCoreComponent::kBounceGroups_DEAD, this, pErrMsg); +// physProps->SetMemberGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kMemberGroups), pNode, pErrMsg); +// physProps->SetBounceGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kBounceGroups), pNode, pErrMsg); + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// plPhysSimpleComponent +// +///////////////////////////////////////////////////////////////////////////////////////// +class plPhysSimpleComponent : public plPhysicCoreComponent +{ +public: + plPhysSimpleComponent(); + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables); +}; + +CLASS_DESC(plPhysSimpleComponent, gPhysSimpleDesc, "Simple", "Simple", COMP_TYPE_PHYSICAL, PHYSICS_SIMPLE_CID) + +ParamBlockDesc2 gPhysSimpleBk +( + plComponent::kBlkComp, _T("Simple"), 0, &gPhysSimpleDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + //Roll out + 1, + kPhysMain, IDD_COMP_PHYS_SIMPLE, IDS_COMP_PHYS_SIMPLE, 0, 0, NULL, +// kPhysMember, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_MEMBER, 0, APPENDROLL_CLOSED, &gMemberGroupProc, +// kPhysBounce, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_BOUNCE, 0, APPENDROLL_CLOSED, &gBounceGroupProc, + + // params + plPhysicCoreComponent::kMass, _T("Mass"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 1.0, + p_range, 0.001, 500.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_SIMP_EDIT1, IDC_COMP_PHYS_SIMP_SPIN1, 1.0, + end, + + plPhysicCoreComponent::kBounce, _T("Bounce"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_SIMP_EDIT2, IDC_COMP_PHYS_SIMP_SPIN2, 0.1, + end, + + plPhysicCoreComponent::kFriction, _T("Friction"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.5, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_SIMP_EDIT3, IDC_COMP_PHYS_SIMP_SPIN3, 0.1, + end, + + plPhysicCoreComponent::kBoundCondRadio, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, kPhysMain, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + plPhysicCoreComponent::kCustomBoundListStuff, _T("UserBoundChoice"), TYPE_INODE, 0, 0, + p_ui, kPhysMain, TYPE_PICKNODEBUTTON, IDC_COMP_PHYS_PICKSTATE_SIMP, + p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_SIMP, + p_accessor, &gPhysCoreAccessor, + end, + + plPhysicCoreComponent::kCustomBoundField, _T("UserBoundCheckBx"), TYPE_BOOL, 0, 0, + p_default, false, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_COMP_PHYS_CUSTOMCHK, + p_enable_ctrls, 1, plPhysicCoreComponent::kCustomBoundListStuff, + end, + + plPhysicCoreComponent::kAlignProxyShape, _T("AlignShapeChkBx"), TYPE_BOOL, P_ANIMATABLE, 0, + p_default, FALSE, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_COMP_PHYS_ALIGN_SHAPE_BOOL, + end, + + plPhysicCoreComponent::kNoSynchronize, _T("DontSynchronizeChkBx"), TYPE_BOOL, 0, 0, + p_default, false, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_PH_NO_SYNC_CHK, + end, + + plPhysicCoreComponent::kStartInactive, _T("StartInactiveChkBx"), TYPE_BOOL, 0, 0, + p_default, true, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_PH_INACTIVE_CHK, + end, + + // Event Groups + plPhysicCoreComponent::kMemberGroups_DEAD, _T("memberGroups"), TYPE_INT, 0,0, + p_default, plPhysicsGroups_DEAD::kDynamicSimulated, + end, + plPhysicCoreComponent::kBounceGroups_DEAD, _T("bounceGroups"), TYPE_INT, 0,0, + p_default, plPhysicsGroups_DEAD::kStaticSimulated | + plPhysicsGroups_DEAD::kDynamicSimulated | + plPhysicsGroups_DEAD::kAnimated, + end, + + plPhysicCoreComponent::kAvAnimPushable, _T("AvAnimPushable"), TYPE_BOOL, 0, 0, + p_default, false, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_PH_AVANIMPUSHABLE, + end, + + end +); + +plPhysSimpleComponent::plPhysSimpleComponent() +{ + fClassDesc = &gPhysSimpleDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +void plPhysSimpleComponent::CollectNonDrawables(INodeTab& nonDrawables) +{ + if (fCompPB->GetInt(kCustomBoundField)) + { + INode* boundsNode = fCompPB->GetINode(kCustomBoundListStuff); + if( boundsNode ) + nonDrawables.Append(1, &boundsNode); + } +} + +hsBool plPhysSimpleComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + IFixBounds(); + + plPhysicalProps *physProps = pNode->GetPhysicalProps(); + + physProps->SetMass(fCompPB->GetFloat(kMass), pNode, pErrMsg); + physProps->SetRestitution(fCompPB->GetFloat(kBounce), pNode, pErrMsg); + physProps->SetFriction(fCompPB->GetFloat(kFriction), pNode, pErrMsg); + physProps->SetBoundsType(fCompPB->GetInt(kBoundCondRadio), pNode, pErrMsg); + physProps->SetAlignToOwner(fCompPB->GetInt(kAlignProxyShape) != 0, pNode, pErrMsg); + + ValidateGroups(fCompPB, plPhysicCoreComponent::kMemberGroups_DEAD, plPhysicCoreComponent::kBounceGroups_DEAD, this, pErrMsg); + physProps->SetGroup(plSimDefs::kGroupDynamic, pNode, pErrMsg); +// physProps->SetMemberGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kMemberGroups), pNode, pErrMsg); +// physProps->SetBounceGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kBounceGroups), pNode, pErrMsg); + + physProps->SetNoSynchronize(fCompPB->GetInt(kNoSynchronize)); + physProps->SetStartInactive(fCompPB->GetInt(kStartInactive)); + physProps->SetAvAnimPushable(fCompPB->GetInt(kAvAnimPushable)); + + if( !pNode->IsAnimated() ) + pNode->SetItinerant(true); + + return IGetProxy(pNode, pErrMsg); +} + + +//////////////////////////////////////////////////////////////////////////////// +//! Invisible Blocker Component Class + +class plPhysBlockerComponent : public plPhysicCoreComponent +{ +public: + plPhysBlockerComponent(); + hsBool SetupProperties(plMaxNode* node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + +CLASS_DESC(plPhysBlockerComponent, gPhysBlockerDesc, "(ex) Invisible Blocker", "InvisBlocker", COMP_TYPE_PHYSICAL, PHYSICS_BLOCKER_CID) + +ParamBlockDesc2 gPhysBlockerBk +( + plComponent::kBlkComp, _T("invisibleBlocker"), 0, &gPhysBlockerDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + //Roll out + 1, + kPhysMain, IDD_COMP_PHYS_INVISIBLE, IDS_COMP_PHYS_INVISIBLE, 0, 0, NULL, +// kPhysMember, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_MEMBER, 0, APPENDROLL_CLOSED, &gMemberGroupProc, +// kPhysBounce, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_BOUNCE, 0, APPENDROLL_CLOSED, &gBounceGroupProc, + + // params + plPhysicCoreComponent::kBoundCondRadio, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, kPhysMain, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + plPhysicCoreComponent::kLOSChkBx, _T("LOSChkBx"), TYPE_BOOL, P_ANIMATABLE, 0, + p_default, FALSE, + p_ui, kPhysMain, TYPE_SINGLECHEKBOX, IDC_COMP_CHECK_LOS, + end, + + plPhysicCoreComponent::kFriction, _T("Friction"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.5, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_TERRAIN_EDIT1, IDC_COMP_PHYS_TERRAIN_SPIN1, 0.0001f, + end, + + plPhysicCoreComponent::kBounce, _T("Bounce"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_INVIS_BOUNCE_EDIT, IDC_COMP_PHYS_INVIS_BOUNCE_SPIN, 0.0001f, + end, + + // Event Groups + plPhysicCoreComponent::kMemberGroups_DEAD, _T("memberGroups"), TYPE_INT, 0,0, + p_default, plPhysicsGroups_DEAD::kStaticSimulated, + end, + plPhysicCoreComponent::kBounceGroups_DEAD, _T("bounceGroups"), TYPE_INT, 0,0, + end, + + plPhysicCoreComponent::kGroup, _T("group"), TYPE_INT, 0, 0, + p_ui, kPhysMain, TYPE_RADIO, 2, IDC_RADIO_BLOCK_AVATAR, IDC_RADIO_BLOCK_DYNAMIC, + p_vals, plSimDefs::kGroupAvatarBlocker, plSimDefs::kGroupDynamicBlocker, + p_default, plSimDefs::kGroupAvatarBlocker, + end, + + end +); + +plPhysBlockerComponent::plPhysBlockerComponent() +{ + fClassDesc = &gPhysBlockerDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plPhysBlockerComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + IFixBounds(); + + pNode->SetDrawable(false); + + plPhysicalProps *physProps = pNode->GetPhysicalProps(); + + physProps->SetMass(0, pNode, pErrMsg); + + if(fCompPB->GetInt(kLOSChkBx)) + { + physProps->SetLOSBlockUI(true, pNode, pErrMsg); + physProps->SetLOSBlockCamera(true, pNode, pErrMsg); + } + + physProps->SetFriction(fCompPB->GetFloat(kFriction), pNode, pErrMsg); + + physProps->SetGroup(fCompPB->GetInt(kGroup), pNode, pErrMsg); + ValidateGroups(fCompPB, plPhysicCoreComponent::kMemberGroups_DEAD, plPhysicCoreComponent::kBounceGroups_DEAD, this, pErrMsg); +// physProps->SetMemberGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kMemberGroups), pNode, pErrMsg); +// physProps->SetBounceGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kBounceGroups), pNode, pErrMsg); + + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Walkable + +class plPhysWalkableComponent : public plPhysicCoreComponent +{ +public: + plPhysWalkableComponent(); + hsBool SetupProperties(plMaxNode* node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + +CLASS_DESC(plPhysWalkableComponent, gPhysWalkableDesc, "Walkable", "Walkable", COMP_TYPE_PHYS_TERRAINS, PHYS_WALKABLE_CID) + +ParamBlockDesc2 gPhysWalkableBk +( + plComponent::kBlkComp, _T("Walkable"), 0, &gPhysWalkableDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + //Roll out + 1, + kPhysMain, IDD_COMP_PHYS_INVISIBLE, IDS_COMP_PHYS_WALKABLE, 0, 0, NULL, +// kPhysMember, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_MEMBER, 0, APPENDROLL_CLOSED, &gMemberGroupProc, +// kPhysBounce, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_BOUNCE, 0, APPENDROLL_CLOSED, &gBounceGroupProc, + + // params + plPhysicCoreComponent::kBoundCondRadio, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, kPhysMain, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + + plPhysicCoreComponent::kFriction, _T("Friction"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.5, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_TERRAIN_EDIT1, IDC_COMP_PHYS_TERRAIN_SPIN1, 0.0001f, + end, + + plPhysicCoreComponent::kBounce, _T("Bounce"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_INVIS_BOUNCE_EDIT, IDC_COMP_PHYS_INVIS_BOUNCE_SPIN, 0.0001f, + end, + + // Event Groups + plPhysicCoreComponent::kMemberGroups_DEAD, _T("memberGroups"), TYPE_INT, 0,0, + p_default, plPhysicsGroups_DEAD::kStaticSimulated, + end, + plPhysicCoreComponent::kBounceGroups_DEAD, _T("bounceGroups"), TYPE_INT, 0,0, + end, + + end +); + +plPhysWalkableComponent::plPhysWalkableComponent() +{ + fClassDesc = &gPhysWalkableDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plPhysWalkableComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + IFixBounds(); + + pNode->SetDrawable(false); + + plPhysicalProps *physProps = pNode->GetPhysicalProps(); + + physProps->SetMass(0, pNode, pErrMsg); + physProps->SetFriction(fCompPB->GetFloat(kFriction), pNode, pErrMsg); + physProps->SetRestitution(fCompPB->GetFloat(kBounce), pNode, pErrMsg); + + ValidateGroups(fCompPB, plPhysicCoreComponent::kMemberGroups_DEAD, plPhysicCoreComponent::kBounceGroups_DEAD, this, pErrMsg); +// physProps->SetMemberGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kMemberGroups), pNode, pErrMsg); +// physProps->SetBounceGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kBounceGroups), pNode, pErrMsg); + physProps->SetGroup(plSimDefs::kGroupStatic, pNode, pErrMsg); +// physProps->SetReportGroup(1<MakeAutoParamBlocks(this); +} + +hsBool plPhysClimbableComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + IFixBounds(); + + pNode->SetDrawable(false); + + plPhysicalProps *physProps = pNode->GetPhysicalProps(); + + physProps->SetMass(0, pNode, pErrMsg); + physProps->SetFriction(fCompPB->GetFloat(kFriction), pNode, pErrMsg); + physProps->SetRestitution(fCompPB->GetFloat(kBounce), pNode, pErrMsg); + + ValidateGroups(fCompPB, plPhysicCoreComponent::kMemberGroups_DEAD, plPhysicCoreComponent::kBounceGroups_DEAD, this, pErrMsg); + physProps->SetGroup(plSimDefs::kGroupStatic, pNode, pErrMsg); +// physProps->SetMemberGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kMemberGroups), pNode, pErrMsg); +// physProps->SetBounceGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kBounceGroups), pNode, pErrMsg); +// physProps->SetReportGroup(1<GetFloat(minIndex, t); + max = pb->GetFloat(maxIndex, t); + + if (min > max) + { + if (adjustMin) + pb->SetValue(minIndex, t, max); + else + pb->SetValue(maxIndex, t, min); + + map->Invalidate(minIndex); + map->Invalidate(maxIndex); + } + } + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + int id = LOWORD(wParam); + + IParamBlock2 *pb = map->GetParamBlock(); + + switch (msg) + { + case WM_COMMAND: + case CC_SPINNER_CHANGE: + IValidateSpinners(t, pb, map, id); + return TRUE; + } + return FALSE; + } + void DeleteThis() {} +}; +static Swim2DDlgProc gSwim2DDlgProc; + +CLASS_DESC(plSwim2DComponent, gPhysSwimSurfaceDesc, "Swim 2D", "Swim 2D", COMP_TYPE_PHYS_TERRAINS, PHYS_SWIMSURFACE_CID) + +ParamBlockDesc2 gPhysSwimSurfaceBk +( + plComponent::kBlkComp, _T("Swim 2D"), 0, &gPhysSwimSurfaceDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + //Roll out + 1, + kPhysMain, IDD_COMP_PHYS_SWIMSURF, IDS_COMP_PHYS_SWIMSURF, 0, 0, &gSwim2DDlgProc, +// kPhysMember, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_MEMBER, 0, APPENDROLL_CLOSED, &gMemberGroupProc, +// kPhysBounce, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_BOUNCE, 0, APPENDROLL_CLOSED, &gBounceGroupProc, + + // params + plPhysicCoreComponent::kBoundCondRadio, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, kPhysMain, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + // Event Groups + plPhysicCoreComponent::kMemberGroups_DEAD, _T("memberGroups"), TYPE_INT, 0,0, + end, + plPhysicCoreComponent::kBounceGroups_DEAD, _T("bounceGroups"), TYPE_INT, 0,0, + end, + + plPhysicCoreComponent::kSwimCurrentType, _T("CurrentType"), TYPE_INT, 0, 0, + p_ui, kPhysMain, TYPE_RADIO, 3, IDC_SWIM_CURRENT_NONE, IDC_SWIM_CURRENT_SPIRAL, IDC_SWIM_CURRENT_STRAIGHT, + p_vals, plSwim2DComponent::kCurrentNone, plSwim2DComponent::kCurrentSpiral, plSwim2DComponent::kCurrentStraight, + p_default, plSwim2DComponent::kCurrentNone, + end, + + plPhysicCoreComponent::kSwimCurrentRotation, _T("SwimRotation"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, -100.0, 100.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_SWIM_CURRENT_ROTATION, IDC_SWIM_CURRENT_ROTATION_SPIN, 1.0, + end, + + plPhysicCoreComponent::kSwimDetectorNode, _T("swimDetector"), TYPE_INODE, 0, 0, + p_ui, kPhysMain, TYPE_PICKNODEBUTTON, IDC_SWIM_DETECTOR_NODE, + p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_SWIM_DETECTOR_NODE, + end, + + plPhysicCoreComponent::kSwimCurrentNode, _T("swimCurrentNode"), TYPE_INODE, 0, 0, + p_ui, kPhysMain, TYPE_PICKNODEBUTTON, IDC_SWIM_CURRENT_NODE, + //p_sclassID, DUMMY_CLASS_ID, + p_prompt, IDS_SWIM_CURRENT_NODE, + end, + + plPhysicCoreComponent::kSwimBuoyancyDown, _T("BuoyancyDown"), TYPE_FLOAT, 0, 0, + p_default, 3.0, + p_range, 0.0, 100.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_SWIM_BUOYANCY_DOWN, IDC_SWIM_BUOYANCY_DOWN_SPIN, 0.1, + end, + + plPhysicCoreComponent::kSwimBuoyancyUp, _T("BuoyancyUp"), TYPE_FLOAT, 0, 0, + p_default, 0.05, + p_range, 0.0, 100.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_SWIM_BUOYANCY_UP, IDC_SWIM_BUOYANCY_UP_SPIN, 0.1, + end, + + plPhysicCoreComponent::kSwimMaxUpVel, _T("MaxUpVel"), TYPE_FLOAT, 0, 0, + p_default, 3.0, + p_range, 0.0, 100.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_SWIM_MAX_UP_VEL, IDC_SWIM_MAX_UP_VEL_SPIN, 0.1, + end, + + plPhysicCoreComponent::kSwimCurrentPullNearDist, _T("PullNearDist"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 10000.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_SWIM_CURRENT_PULL_NEAR_DIST, IDC_SWIM_CURRENT_PULL_NEAR_DIST_SPIN, 1.0, + end, + + plPhysicCoreComponent::kSwimCurrentPullNearVel, _T("PullNearVel"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -100.0, 100.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_SWIM_CURRENT_PULL_NEAR_VEL, IDC_SWIM_CURRENT_PULL_NEAR_VEL_SPIN, 1.0, + end, + + plPhysicCoreComponent::kSwimCurrentPullFarDist, _T("PullFarDist"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 10000.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_SWIM_CURRENT_PULL_FAR_DIST, IDC_SWIM_CURRENT_PULL_FAR_DIST_SPIN, 1.0, + end, + + plPhysicCoreComponent::kSwimCurrentPullFarVel, _T("PullFarVel"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -100.0, 100.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_SWIM_CURRENT_PULL_FAR_VEL, IDC_SWIM_CURRENT_PULL_FAR_VEL_SPIN, 1.0, + end, + + plPhysicCoreComponent::kSwimCurrentStraightNearDist, _T("StraightNearDist"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, -10000.0, 10000.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_SWIM_CURRENT_STRAIGHT_NEAR_DIST, IDC_SWIM_CURRENT_STRAIGHT_NEAR_DIST_SPIN, 1.0, + end, + + plPhysicCoreComponent::kSwimCurrentStraightNearVel, _T("StraightNearVel"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -100.0, 100.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_SWIM_CURRENT_STRAIGHT_NEAR_VEL, IDC_SWIM_CURRENT_STRAIGHT_NEAR_VEL_SPIN, 1.0, + end, + + plPhysicCoreComponent::kSwimCurrentStraightFarDist, _T("StraightFarDist"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, -10000.0, 10000.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_SWIM_CURRENT_STRAIGHT_FAR_DIST, IDC_SWIM_CURRENT_STRAIGHT_FAR_DIST_SPIN, 1.0, + end, + + plPhysicCoreComponent::kSwimCurrentStraightFarVel, _T("StraightFarVel"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -100.0, 100.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_SWIM_CURRENT_STRAIGHT_FAR_VEL, IDC_SWIM_CURRENT_STRAIGHT_FAR_VEL_SPIN, 1.0, + end, + + end + ); + +// plSwim2DComponent ----------------- +// ------------------ +plSwim2DComponent::plSwim2DComponent() +{ + fClassDesc = &gPhysSwimSurfaceDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetUpProperties ----------------------------------------------------------- +// ---------------- +hsBool plSwim2DComponent::SetupProperties(plMaxNode *node, plErrorMsg *errMsg) +{ + IFixBounds(); + plPhysicalProps *physProps = nil; + plMaxNode *detectorNode = (plMaxNode *)fCompPB->GetINode(kSwimDetectorNode); + if (detectorNode) + { + detectorNode->SetDrawable(false); + physProps = detectorNode->GetPhysicalProps(); + physProps->SetMass(0, detectorNode, errMsg); + ValidateGroups(fCompPB, plPhysicCoreComponent::kMemberGroups_DEAD, plPhysicCoreComponent::kBounceGroups_DEAD, this, errMsg); + physProps->SetGroup(plSimDefs::kGroupDetector, node, errMsg); +// physProps->SetMemberGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kMemberGroups), detectorNode, errMsg); +// physProps->SetBounceGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kBounceGroups), detectorNode, errMsg); + physProps->SetReportGroup(1<SetBoundsType(plSimDefs::kBoxBounds, detectorNode, errMsg); + } + + physProps = node->GetPhysicalProps(); + physProps->SetMass(0, node, errMsg); + physProps->SetBoundsType(fCompPB->GetInt(kBoundCondRadio), node, errMsg); + physProps->SetLOSSwimRegion(true, node, errMsg); + + plMaxNode *currentNode = (plMaxNode *)fCompPB->GetINode(kSwimCurrentNode); + if (currentNode) + currentNode->SetForceLocal(true); + + return true; +} + +hsBool plSwim2DComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plSwimRegionInterface *swimInt = nil; + int type = fCompPB->GetInt(ParamID(kSwimCurrentType)); + if (type != kCurrentNone && fCompPB->GetINode(ParamID(kSwimCurrentNode)) == nil) + { + pErrMsg->Set(true, node->GetName(), "No dummy box set to define current. Forcing current to \"none\"").Show(); + type = kCurrentNone; + } + + switch (type) + { + case kCurrentSpiral: + { + fSwimRegions[node] = TRACKED_NEW plSwimCircularCurrentRegion(); + hsgResMgr::ResMgr()->NewKey(node->GetName(), fSwimRegions[node], node->GetLocation(), node->GetLoadMask()); + break; + } + case kCurrentStraight: + { + fSwimRegions[node] = TRACKED_NEW plSwimStraightCurrentRegion(); + hsgResMgr::ResMgr()->NewKey(node->GetName(), fSwimRegions[node], node->GetLocation(), node->GetLoadMask()); + break; + } + default: + { + fSwimRegions[node] = TRACKED_NEW plSwimRegionInterface(); + hsgResMgr::ResMgr()->NewKey(node->GetName(), fSwimRegions[node], node->GetLocation(), node->GetLoadMask()); + break; + } + } + return true; +} + +// Convert ------------------------------------------------------------ +// -------- +hsBool plSwim2DComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plMaxNode *detectorNode = (plMaxNode *)fCompPB->GetINode(kSwimDetectorNode); + if (detectorNode && detectorNode->GetSceneObject()) + { + if (!detectorNode->GetSceneObject()->GetModifierByType(plSwimDetector::Index())) + { + plKey nilKey; + plSwimMsg *enterMsg = TRACKED_NEW plSwimMsg(detectorNode->GetKey(), nilKey, true, nil); + plSwimMsg *exitMsg = TRACKED_NEW plSwimMsg(detectorNode->GetKey(), nilKey, false, nil); + enterMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + exitMsg->SetBCastFlag(plMessage::kPropagateToModifiers); + plSwimDetector *swimMod = TRACKED_NEW plSwimDetector(enterMsg, exitMsg); + detectorNode->AddModifier(swimMod, IGetUniqueName(node)); + + // the mod doesn't have a valid key until AddModifier is called, so this comes last. + enterMsg->fSwimRegionKey = swimMod->GetKey(); + exitMsg->fSwimRegionKey = swimMod->GetKey(); + } + } + + int type = fCompPB->GetInt(ParamID(kSwimCurrentType)); + switch (type) + { + case kCurrentSpiral: + { + plSwimCircularCurrentRegion *circInt = plSwimCircularCurrentRegion::ConvertNoRef(fSwimRegions[node]); + circInt->fRotation = fCompPB->GetFloat(ParamID(kSwimCurrentRotation)); + circInt->fPullNearDistSq = fCompPB->GetFloat(ParamID(kSwimCurrentPullNearDist)); + circInt->fPullNearDistSq *= circInt->fPullNearDistSq; + circInt->fPullNearVel = fCompPB->GetFloat(ParamID(kSwimCurrentPullNearVel)); + circInt->fPullFarDistSq = fCompPB->GetFloat(ParamID(kSwimCurrentPullFarDist)); + circInt->fPullFarDistSq *= circInt->fPullFarDistSq; + circInt->fPullFarVel = fCompPB->GetFloat(ParamID(kSwimCurrentPullFarVel)); + + plMaxNode *currentNode = (plMaxNode *)fCompPB->GetINode(ParamID(kSwimCurrentNode)); + if (currentNode) + { + plGenRefMsg *msg= TRACKED_NEW plGenRefMsg(circInt->GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(currentNode->GetSceneObject()->GetKey(), msg, plRefFlags::kActiveRef); + } + break; + } + case kCurrentStraight: + { + plSwimStraightCurrentRegion *strInt = plSwimStraightCurrentRegion::ConvertNoRef(fSwimRegions[node]); + strInt->fNearDist = fCompPB->GetFloat(ParamID(kSwimCurrentStraightNearDist)); + strInt->fNearVel = fCompPB->GetFloat(ParamID(kSwimCurrentStraightNearVel)); + strInt->fFarDist = fCompPB->GetFloat(ParamID(kSwimCurrentStraightFarDist)); + strInt->fFarVel = fCompPB->GetFloat(ParamID(kSwimCurrentStraightFarVel)); + + plMaxNode *currentNode = (plMaxNode *)fCompPB->GetINode(ParamID(kSwimCurrentNode)); + if (currentNode) + { + plGenRefMsg *msg= TRACKED_NEW plGenRefMsg(strInt->GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(currentNode->GetSceneObject()->GetKey(), msg, plRefFlags::kActiveRef); + } + break; + } + default: + { + // Already done all the work in PreConvert + break; + } + } + + fSwimRegions[node]->fDownBuoyancy = fCompPB->GetFloat(ParamID(kSwimBuoyancyDown)) + 1; + fSwimRegions[node]->fUpBuoyancy = fCompPB->GetFloat(ParamID(kSwimBuoyancyUp)) + 1; + fSwimRegions[node]->fMaxUpwardVel = fCompPB->GetFloat(ParamID(kSwimMaxUpVel)); + + hsgResMgr::ResMgr()->AddViaNotify(fSwimRegions[node]->GetKey(), TRACKED_NEW plObjRefMsg(node->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Swim 3D + +class plPhysSwim3DComponent : public plPhysicCoreComponent +{ +public: + plPhysSwim3DComponent(); + hsBool SetupProperties(plMaxNode* node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + +CLASS_DESC(plPhysSwim3DComponent, gPhysSwim3DDesc, "Swim 3D", "Swim 3D", COMP_TYPE_PHYS_TERRAINS, PHYS_SWIM3D_CID) + +ParamBlockDesc2 gPhysSwim3DBk +( + plComponent::kBlkComp, _T("Swim 3D"), 0, &gPhysSwim3DDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + //Roll out + 1, + kPhysMain, IDD_COMP_PHYS_SWIM3D, IDS_COMP_PHYS_SWIM3D, 0, 0, NULL, +// kPhysMember, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_MEMBER, 0, APPENDROLL_CLOSED, &gMemberGroupProc, +// kPhysBounce, IDD_COMP_PHYS_CORE_GROUP, IDS_COMP_PHYS_BOUNCE, 0, APPENDROLL_CLOSED, &gBounceGroupProc, + + // params + plPhysicCoreComponent::kBoundCondRadio, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, kPhysMain, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + plPhysicCoreComponent::kFriction, _T("Friction"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.5, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_TERRAIN_EDIT1, IDC_COMP_PHYS_TERRAIN_SPIN1, 0.0001f, + end, + + plPhysicCoreComponent::kBounce, _T("Bounce"), TYPE_FLOAT, P_ANIMATABLE, 0, + p_default, 0.0, + p_range, 0.0, 1.0, + p_ui, kPhysMain, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_PHYS_INVIS_BOUNCE_EDIT, IDC_COMP_PHYS_INVIS_BOUNCE_SPIN, 0.0001f, + end, + + // Event Groups + plPhysicCoreComponent::kMemberGroups_DEAD, _T("memberGroups"), TYPE_INT, 0,0, + p_default, plPhysicsGroups_DEAD::kStaticSimulated, + end, + plPhysicCoreComponent::kBounceGroups_DEAD, _T("bounceGroups"), TYPE_INT, 0,0, + end, + + end +); + +plPhysSwim3DComponent::plPhysSwim3DComponent() +{ + fClassDesc = &gPhysSwim3DDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plPhysSwim3DComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + IFixBounds(); + + pNode->SetDrawable(false); + + plPhysicalProps *physProps = pNode->GetPhysicalProps(); + + physProps->SetMass(0, pNode, pErrMsg); + physProps->SetFriction(fCompPB->GetFloat(kFriction), pNode, pErrMsg); + physProps->SetRestitution(fCompPB->GetFloat(kBounce), pNode, pErrMsg); + + ValidateGroups(fCompPB, plPhysicCoreComponent::kMemberGroups_DEAD, plPhysicCoreComponent::kBounceGroups_DEAD, this, pErrMsg); + physProps->SetGroup(plSimDefs::kGroupStatic, pNode, pErrMsg); +// physProps->SetMemberGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kMemberGroups), pNode, pErrMsg); +// physProps->SetBounceGroup(plEventGroupProc::GetGroups(fCompPB, plPhysicCoreComponent::kBounceGroups), pNode, pErrMsg); +// physProps->SetReportGroup(plPhysicsGroups_DEAD::kAvatars, pNode, pErrMsg); + + return true; +} + + + +///////////////////////////////////////////////////////////////////////////////////////// +// +// DEAD +// +///////////////////////////////////////////////////////////////////////////////////////// +class plPhysPlayerComponent : public plPhysicCoreComponent +{ +public: + plPhysPlayerComponent(); +}; + +OBSOLETE_CLASS_DESC(plPhysPlayerComponent, gPhysPlayerDesc, "Player", "Player", COMP_TYPE_PHYSICAL, PHYSICS_PLAYER_CID) + +ParamBlockDesc2 gPhysPlayerBk +( + plComponent::kBlkComp, _T("Player"), 0, &gPhysPlayerDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + + end +); + +plPhysPlayerComponent::plPhysPlayerComponent() +{ + fClassDesc = &gPhysPlayerDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +///// + +class plPhysBoundBlockerComponent : public plPhysicCoreComponent +{ +public: + plPhysBoundBlockerComponent(); +}; + +OBSOLETE_CLASS_DESC(plPhysBoundBlockerComponent, gPhysBoundBlockerDesc, "(ex) Boundary Blocker", "BoundaryBlocker", COMP_TYPE_PHYSICAL, PHYSICS_BOUND_BLOCKER_CID) + +ParamBlockDesc2 gPhysBoundBlockerBk +( + plComponent::kBlkComp, _T("(ex)Boundary Blocker"), 0, &gPhysBoundBlockerDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + + end +); + +plPhysBoundBlockerComponent::plPhysBoundBlockerComponent() +{ + fClassDesc = &gPhysBoundBlockerDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +///// + +class plPhysDetectorComponent : public plPhysicCoreComponent +{ +public: + plPhysDetectorComponent(); +}; + +OBSOLETE_CLASS_DESC(plPhysDetectorComponent, gPhysDetectorDesc, "Detector", "Detector", COMP_TYPE_PHYSICAL, PHYSICS_DETECTOR_CID) + +ParamBlockDesc2 gPhysDetectorBk +( + plComponent::kBlkComp, _T("Detector"), 0, &gPhysDetectorDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + + end +); + +plPhysDetectorComponent::plPhysDetectorComponent() +{ + fClassDesc = &gPhysDetectorDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +//////////////////////////////////////////////////////////////////////////////// +//! Physical Simple Component Class + +class plPhysSubWorldComponent : public plPhysicCoreComponent +{ +public: + plPhysSubWorldComponent(); + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + void IAddChildren(plMaxNode *node, plMaxNode* worldKey); +}; + +CLASS_DESC(plPhysSubWorldComponent, gPhysSubWorldDesc, "Subworld", "Subworld", COMP_TYPE_PHYSICAL, PHYS_SUBWORLD_CID) + +ParamBlockDesc2 gPhysSubWorldBk +( + plComponent::kBlkComp, _T("Subworld "), 0, &gPhysSubWorldDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //Roll out + IDD_COMP_PHYS_SUBWORLD, IDS_COMP_PHYS_SUBWORLD, 0, 0, NULL, + + end +); + +plPhysSubWorldComponent::plPhysSubWorldComponent() +{ + fClassDesc = &gPhysSubWorldDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plPhysSubWorldComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + IAddChildren(node, node); + + node->SetForceLocal(true); + node->SetMovable(true); + + return true; +} + +hsBool plPhysSubWorldComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +void plPhysSubWorldComponent::IAddChildren(plMaxNode* node, plMaxNode* subworld) +{ + int numChildren = node->NumberOfChildren(); + for (int i = 0; i < numChildren; i++) + { + plMaxNode* child = (plMaxNode*)node->GetChildNode(i); + const char* childName = child->GetName(); + + bool hasSubworld = false; + + UInt32 numComps = child->NumAttachedComponents(); + for (int j = 0; j < numComps; j++) + { + plComponentBase* comp = child->GetAttachedComponent(j); + if (comp && comp->ClassID() == PHYS_SUBWORLD_CID) + { + hasSubworld = true; + break; + } + } + + // If we've reached another subworld, terminate our descent so we don't file + // children in our subworld instead. + // This has to happen *after* we've converted this node, because it may be a physical + // in the parent subworld. + if (!hasSubworld) + { + plPhysicalProps* props = child->GetPhysicalProps(); + if (props) + props->SetSubworld(subworld); + IAddChildren(child, subworld); + } + } +} + + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +// the detector for triggering subworld changes +/////////////////////////////////////////////////////// + +class plSubworldDetectorComponent : public plPhysicCoreComponent +{ +public: + plSubworldDetectorComponent(); + + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + + + +CLASS_DESC(plSubworldDetectorComponent, gSubworldDetectorDesc, "Subworld Region", "SubworldRegion", COMP_TYPE_PHYSICAL, SUBWORLD_REGION_CID) + +enum +{ + kSubworldTarget, + kInclusive_DEAD, + kSubworldTriggerOn, +}; + +enum +{ + kSubTriggerOnEnter, + kSubTriggerOnExit, +}; + +ParamBlockDesc2 gSubworldRegionBlock +( + plComponent::kBlkComp, _T("subworldRegion"), 0, &gSubworldDetectorDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SUBWORLD_REGION, IDS_COMP_SUBWORLD_REGION, 0, 0, NULL, + + kSubworldTarget, _T("SubworldTarget"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CAMERARGN_PICKSTATE_BASE, + p_sclassID, HELPER_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + end, + + kSubworldTriggerOn, _T("triggerOn"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 2, IDC_RADIO_ENTER, IDC_RADIO_EXIT, + p_vals, kSubTriggerOnEnter, kSubTriggerOnExit, + p_default, kSubTriggerOnEnter, + end, + + end +); + +plSubworldDetectorComponent::plSubworldDetectorComponent() +{ + fClassDesc = &gSubworldDetectorDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plSubworldDetectorComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + pNode->SetForceLocal(true); + pNode->SetDrawable(false); + + plPhysicalProps *physProps = pNode->GetPhysicalProps(); + + physProps->SetMass(1.0, pNode, pErrMsg); + physProps->SetFriction(0.0, pNode, pErrMsg); + physProps->SetRestitution(0.0, pNode, pErrMsg); + //physProps->SetBoundsType(plSimDefs::kExplicitBounds, pNode, pErrMsg); + physProps->SetBoundsType(plSimDefs::kHullBounds, pNode, pErrMsg); + physProps->SetGroup(plSimDefs::kGroupDetector, pNode, pErrMsg); + physProps->SetReportGroup(1<GetSceneObject(); + plLocation loc = node->GetLocation(); + + plSubworldRegionDetector *detector = TRACKED_NEW plSubworldRegionDetector; + + // Register the detector + plKey detectorKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), detector, loc); + hsgResMgr::ResMgr()->AddViaNotify(detectorKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + // need to get the key for the camera here... + plMaxNode* pSubNode = (plMaxNode*)fCompPB->GetINode(kSubworldTarget); + if (pSubNode) + { + if(pSubNode->CanConvert()) + { + detector->SetSubworldKey(pSubNode->GetKey()); + } + } + else + { + detector->SetSubworldKey(nil); + } + + bool onExit = (fCompPB->GetInt(kSubworldTriggerOn) == kSubTriggerOnExit); + detector->SetTriggerOnExit(onExit); + + return true; +} + +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +/////////////////////////////////////////////////////// +// the detector for triggering panic links +/////////////////////////////////////////////////////// + +class plPanicLinkDetectorComponent : public plPhysicCoreComponent +{ +public: + enum + { + kPlayAnim, + }; + + plPanicLinkDetectorComponent(); + void DeleteThis() { delete this; } + + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + + +CLASS_DESC(plPanicLinkDetectorComponent, gPanicLinkDetectorDesc, "Panic Link Region", "PanicLinkRegion", COMP_TYPE_PHYSICAL, PANIC_LINK_REGION_CID) + +ParamBlockDesc2 gPanicLinkRegionBlock +( + plComponent::kBlkComp, _T("panicLinkRegion"), 0, &gPanicLinkDetectorDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_PANIC, IDS_COMP_PANIC, 0, 0, NULL, + + plPanicLinkDetectorComponent::kPlayAnim, _T("PlayAnim"), TYPE_BOOL, 0, 0, + p_default, true, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_PANIC_ANIM, + end, + + end +); + + +plPanicLinkDetectorComponent::plPanicLinkDetectorComponent() +{ + fClassDesc = &gPanicLinkDetectorDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plPanicLinkDetectorComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + pNode->SetForceLocal(true); + pNode->SetDrawable(false); + + plPhysicalProps *physProps = pNode->GetPhysicalProps(); + + physProps->SetMass(1.0, pNode, pErrMsg); + physProps->SetFriction(0.0, pNode, pErrMsg); + physProps->SetRestitution(0.0, pNode, pErrMsg); + physProps->SetBoundsType(plSimDefs::kHullBounds, pNode, pErrMsg); + physProps->SetGroup(plSimDefs::kGroupDetector, pNode, pErrMsg); + physProps->SetReportGroup(1<GetSceneObject(); + plLocation loc = node->GetLocation(); + + plPanicLinkRegion *detector = TRACKED_NEW plPanicLinkRegion; + detector->fPlayLinkOutAnim = fCompPB->GetInt(ParamID(kPlayAnim)); + + // Register the detector + plKey detectorKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), detector, loc); + hsgResMgr::ResMgr()->AddViaNotify(detectorKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// +// Shootable COMPONENT +// +///////////////////////////////////////////////////////////////////////////////////////// + +//Class that accesses the paramblock below. +class plShootableComponent : public plComponent +{ +public: + plShootableComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plShootableComponent, gShootableDesc, "Shootable", "Shootable", COMP_TYPE_PHYSICAL, Class_ID(0x77b94f4a, 0x6d3851fc)) + +ParamBlockDesc2 gShootableBk +( +// 1, _T(""), 0, &gShootableDesc, P_AUTO_CONSTRUCT, 0, + plComponent::kBlkComp, _T("Shootable"), 0, &gShootableDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SHOOTABLE, IDS_COMP_SHOOTABLE, 0, 0, NULL, + + end +); + +plShootableComponent::plShootableComponent() +{ + fClassDesc = &gShootableDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plShootableComponent::SetupProperties(plMaxNode *node, plErrorMsg *errMsg) +{ + plPhysicalProps *props = node->GetPhysicalProps(); + + props->SetMass(0.0, node, errMsg); + props->SetFriction(0.0, node, errMsg); + props->SetRestitution(0.0, node, errMsg); + props->SetBoundsType(plSimDefs::kExplicitBounds, node, errMsg); + props->SetGroup(plSimDefs::kGroupLOSOnly, node, errMsg); + props->SetPinned(true, node, errMsg); + props->SetLOSShootable(true, node, errMsg); + + return true; +} + +hsBool plShootableComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} +class plRideAnimatedPhysicalComponent : public plPhysicCoreComponent +{ +public: + plRideAnimatedPhysicalComponent(); + void DeleteThis(){delete this;} + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; +CLASS_DESC(plRideAnimatedPhysicalComponent , gRideAnimatedPhysicalComponent, "RideAnimPhysReg", "RideAnimatedPhysicalRegion", COMP_TYPE_PHYSICAL, Class_ID(0xaf305963, 0x63a246df)); +ParamBlockDesc2 gSRideAnimatedPhysBk +( +plComponent::kBlkComp, _T("rideAnimated"), 0, &gRideAnimatedPhysicalComponent, P_AUTO_CONSTRUCT + P_AUTO_UI , plComponent::kRefComp, +//Roll out + +IDD_COMP_RIDE_ANIMATED_PHYS, IDS_COMP_RIDE_ANIMATED_PHYS, 0, 0, NULL, + +plPhysicCoreComponent::kBoundCondRadio, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_RADIO_BSPHERE, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kExplicitBounds, + p_default, plSimDefs::kHullBounds, + end, + end +); +plRideAnimatedPhysicalComponent::plRideAnimatedPhysicalComponent() +{ + fClassDesc = &gRideAnimatedPhysicalComponent; + fClassDesc->MakeAutoParamBlocks(this); +} +hsBool plRideAnimatedPhysicalComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + pNode->SetForceLocal(true); + pNode->SetDrawable(false); + + plPhysicalProps *physProps = pNode->GetPhysicalProps(); + + physProps->SetMass(1.0, pNode, pErrMsg); + physProps->SetFriction(0.0, pNode, pErrMsg); + physProps->SetRestitution(0.0, pNode, pErrMsg); + physProps->SetGroup(plSimDefs::kGroupDetector, pNode, pErrMsg); + physProps->SetReportGroup(1<SetBoundsType(fCompPB->GetInt(kBoundCondRadio), pNode, pErrMsg); + return true; +} +hsBool plRideAnimatedPhysicalComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plRideAnimatedPhysicalComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plSceneObject *obj = node->GetSceneObject(); + plLocation loc = node->GetLocation(); + plRideAnimatedPhysMsg* enter = TRACKED_NEW plRideAnimatedPhysMsg(obj->GetKey(), nil, true, nil); + enter->SetBCastFlag(plMessage::kPropagateToModifiers); + plRideAnimatedPhysMsg* exit = TRACKED_NEW plRideAnimatedPhysMsg(obj->GetKey(), nil, false, nil); + exit->SetBCastFlag(plMessage::kPropagateToModifiers); + plRidingAnimatedPhysicalDetector *detector = TRACKED_NEW plRidingAnimatedPhysicalDetector(enter, exit); + // Register the detector + //node->AddModifier(detector, IGetUniqueName(node)); + plKey detectorKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), detector, loc); + hsgResMgr::ResMgr()->AddViaNotify(detectorKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicalComponents.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicalComponents.h new file mode 100644 index 00000000..f7272ca7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicalComponents.h @@ -0,0 +1,181 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPhysicalComponents_h_inc +#define plPhysicalComponents_h_inc + +#include "max.h" //Max Dependencies +#include "plComponent.h" + +#define PHYSICS_BASE_CID Class_ID(0x610f187a, 0x25824341) +#define PHYSICS_DEBUG_CID Class_ID(0x24131a19, 0x3ef26119) +#define PHYSICS_TERRAIN_CID Class_ID(0x5a526841, 0x11d00083) +#define PHYSICS_DETECTOR_CID Class_ID(0x33b60376, 0x7e5163e0) +#define PHYSICS_INVISIBLE_CID Class_ID(0x11e81ee4, 0x36b81450) +#define PHYSICS_PLAYER_CID Class_ID(0x48703458, 0x4c3a4f39) +#define PHYSICS_SIMPLE_CID Class_ID(0x1e1d2ec5, 0x37f612d6) +#define PHYSICS_BLOCKER_CID Class_ID(0xc9a44be, 0x33c87ffb) +#define PHYSICS_BOUND_BLOCKER_CID Class_ID(0x1beb2bc6, 0x49cd7e64) +#define PHYS_WALKABLE_CID Class_ID(0xcc56fb9, 0x716e0810) +#define PHYS_CLIMBABLE_CID Class_ID(0x74602123, 0x7505f97) +#define PHYS_SWIMSURFACE_CID Class_ID(0x1cde28dd, 0x3c792b48) +#define PHYS_SWIM3D_CID Class_ID(0x12040f17, 0x496913c4) +#define PHYS_CAMERA_BLOCK_CID Class_ID(0x32c729fc, 0x606e79d9) +#define PHYS_SUBWORLD_CID Class_ID(0x157e2138, 0x272540c1) +#define SUBWORLD_REGION_CID Class_ID(0x50d13e25, 0x5c4d139b) +#define PANIC_LINK_REGION_CID Class_ID(0x8d1242, 0x4c283d12) + +#include +#include "plPhysical.h" +#include "../plAvatar/plSwimRegion.h" + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Physical Component CORE +// +// + + +//! A Class + +/*! + As all physical parameters are shared and accessed by the Core Component and + its Convert() function, this class holds all the current physical parameters. + It is accessed by the individual components via their GetParamVals() functions. + \sa GetParamVals() +*/ + +class plPhysicCoreComponent : public plComponent +{ +protected: +// plHKPhysical* fMyHKPhysical; + +public: + //! Enum Global for Physical Components. \c Append \c only! + + /*! + Enum Global for Physical Components. \c Append \c only! This enum is essential + for backward compatability issues. Max uses it to maintain versioning for the + ParamBlock2s that are extensively used. As long as vals are never deleted, + full backward compatability is assured. Just append your new vals to the enum + as versions increase. + */ + enum + { + kMass, /*!< Mass Enum value, inserted in v1.*/ + kBounce, /*!< Bounce Enum value, inserted in v1.*/ + kFriction, /*!< Friction Enum value, inserted in v1.*/ + kGhostChkBx, /*!< Ghost Enum value, inserted in v1.*/ + kNotifyCollisionsChkBx_DEAD, /*!< Collision Report Enum value, inserted in v1.*/ + kStartForceX, /*!< Start Force X Enum value, inserted in v1.*/ + kStartForceY, /*!< Start Force Y Enum value,inserted in v1.*/ + kStartForceZ, /*!< Start Force Z Enum value,inserted in v1.*/ + kStartTorqueX, /*!< Start Torque X Enum value,inserted in v1.*/ + kStartTorqueY, /*!< Start Torque Y Enum value,inserted in v1.*/ + kStartTorqueZ, /*!< Start Torque Z Enum value,inserted in v1.*/ + kBoundCondRadio, /*!< Bounding State Enum value, inserted in v1.*/ + kDynamicChkBx, /*!< Dynamic State Enum value, inserted in v1, Removed in v2.*/ + kWeight, /*!< Weight Enum value, inserted in v1.*/ + kTurnForce, /*!< TurnForce Enum value, inserted in v1.*/ + kInvisibleChkBx, /*!< Invisible Enum value, inserted in v1.*/ + kCustomBoundListStuff, /*!< Bounding Proxy Enum value, inserted in v2.*/ + kCustomBoundField, /*!< Bounding String Enum value, inserted in v2, Removed in v2.*/ + kLOSChkBx, /*!< LOS Enum value, inserted in v3. */ + kConstPinChkBx, /*!< Constraint Pin Enum value, inserted in v4. Removed in v5*/ + kAcceleration, /*!< Accel Enum value, inserted in v5. */ + kAlignProxyShape, /*!< Alignment of Proxy Shape Enum value, inserted in v6. */ + kMemberOfEventGroupBoolTab_DEAD, /*!< Designates which Event Groups this is a member of, inserted in v6 */ + kBounceEventGroupBoolTab_DEAD, /*!< Designates which Event Groups this reacts to, inserted in v6 */ + kReportEventGroupBoolTab_DEAD, /*!< Designates which Event Groups this reacts to, inserted in v6 */ + kMemEventGroupChoice_DEAD, + kBounceEventGroupChoice_DEAD, + kReportEventGroupChoice_DEAD, + kCamLOSChkBx, + kUILOSChkBx, + kMemberGroups_DEAD, + kBounceGroups_DEAD, + kReportGroups, + kCamAvoidChkBx, + kGravityX, + kGravityY, + kGravityZ, + kNoSynchronize, // Don't do any network synchronization for this physical + kStartInactive_DEAD, + kStartInactive, // Deactive this when first adding to the sim + kSwimCurrentType, + kSwimCurrentRotation, + kSwimCurrentPull, // Obsolete + kSwimDetectorNode, + kSwimCurrentNode, + kSwimBuoyancyDown, + kSwimBuoyancyUp, + kSwimMaxUpVel, + kSwimCurrentPullFarDist, + kSwimCurrentPullFarVel, + kSwimCurrentPullNearDist, + kSwimCurrentPullNearVel, + kSwimCurrentStraightNearDist, + kSwimCurrentStraightNearVel, + kSwimCurrentStraightFarDist, + kSwimCurrentStraightFarVel, + kAvAnimPushable, // Is this a big object that the avatar should use push anims on? + kGroup, + }; + + virtual void DeleteThis() { delete this; } + + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + int CanConvertToType(Class_ID obtype) + { return (obtype == PHYSICS_BASE_CID) ? 1 : plComponent::CanConvertToType(obtype); } + +protected: + void IFixBounds(); + hsBool IGetProxy(plMaxNode *node, plErrorMsg *pErrMsg); + + UInt32 IGetEventGroup(ParamID paramID); +}; + +class plSwim2DComponent : public plPhysicCoreComponent +{ +public: + plSwim2DComponent(); + hsBool SetupProperties(plMaxNode* node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + std::map fSwimRegions; + + enum + { + kCurrentNone, + kCurrentSpiral, + kCurrentStraight, + }; +}; + +#endif // plPhysicalComponents_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicsGroups.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicsGroups.h new file mode 100644 index 00000000..a6cb5c82 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPhysicsGroups.h @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPhysicsGroups_inc +#define plPhysicsGroups_inc + +// These are dead now, and are only around for old Max files. New physicals use +// plSimDefs::Group. +// +// Note: These bits are actually saved in the Max file. If you swap them around +// the plEventGroupProc code will have to be changed to map between the Max version +// and Plasma version. To make things easier, just add new ones and don't reuse old +// ones that are removed. +namespace plPhysicsGroups_DEAD +{ + enum + { + kUserGroup1 = 1<<1, + kUserGroup2 = 1<<2, + kUserGroup3 = 1<<3, + kUserGroup4 = 1<<4, + kUserGroup5 = 1<<5, + kUserGroup6 = 1<<6, + kUserGroup7 = 1<<7, + kUserGroup8 = 1<<8, + kUserGroup9 = 1<<9, + kUserGroup10 = 1<<10, + kUserGroup11 = 1<<11, + kUserGroup12 = 1<<12, + kUserGroup13 = 1<<13, + kUserGroup14 = 1<<14, + kUserGroup15 = 1<<15, + kUserGroup16 = 1<<16, + + kAvatars = 1<<17, + kCreatures = 1<<18, + kAnimated = 1<<23, + kDynamicSimulated = 1<<24, + kStaticSimulated = 1<<25, + kDetectors = 1<<26, + // Local avatars are included in kAvatars, you only need to use this if + // you want to only do something with local avatars. + kLocalAvatars = 1<<27, + }; +}; + +#endif // plPhysicsGroups_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickLocalizationDlg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickLocalizationDlg.cpp new file mode 100644 index 00000000..54154a9d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickLocalizationDlg.cpp @@ -0,0 +1,249 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPickLocalizationDlg.h" +#include "../pfLocalizationMgr/pfLocalizationDataMgr.h" + +#include "../MaxMain/plMaxCFGFile.h" +#include "../MaxMain/plMaxAccelerators.h" + +#include "hsUtils.h" +#include "hsStringTokenizer.h" + +#include "resource.h" + +#include + +//////////////////////////////////////////////////////////////////// + +bool plPickLocalizationDlg::DoPick() +{ + plMaxAccelerators::Disable(); + + BOOL ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PICK_LOCALIZATION), + GetCOREInterface()->GetMAXHWnd(), IDlgProc, (LPARAM)this); + + plMaxAccelerators::Enable(); + + return (ret != 0); +} + +hsStringTokenizer locIzer; +char locToken[200]; + +bool plPickLocalizationDlg::IInitDlg(HWND hDlg) +{ + if (!pfLocalizationDataMgr::InstanceValid()) + { + MessageBox(hDlg, "Localization data manger is not initialized! (BTW, this is BAD)", "Error", MB_ICONERROR + MB_OK); + return false; + } + + fTree = GetDlgItem(hDlg, IDC_LOCALIZATIONTREE); + TreeView_DeleteAllItems(fTree); + + std::string ageName = "", setName = "", itemName = ""; + locIzer.Reset(fPath.c_str(), "."); + if (locIzer.Next(locToken, 200)) + ageName = locToken; + if (locIzer.Next(locToken, 200)) + setName = locToken; + if (locIzer.Next(locToken, 200)) + itemName = locToken; + + IAddLocalizations(ageName, setName, itemName); + IUpdateValue(hDlg); + + return true; +} + +std::string WStringToString(std::wstring val) +{ + std::string retVal; + char *buff = hsWStringToString(val.c_str()); + retVal = buff; + delete [] buff; + return retVal; +} + +HTREEITEM plPickLocalizationDlg::IAddVar(std::string name, std::string match, HTREEITEM hParent) +{ + TVINSERTSTRUCT tvi = {0}; + tvi.hParent = hParent; + tvi.hInsertAfter = TVI_LAST; + tvi.item.mask = TVIF_TEXT | TVIF_PARAM; + tvi.item.pszText = (char*)name.c_str(); + tvi.item.cchTextMax = name.length(); + tvi.item.lParam = (LPARAM)nil; + + HTREEITEM hItem = TreeView_InsertItem(fTree, &tvi); + + if (name == match) + { + TreeView_SelectItem(fTree, hItem); + TreeView_EnsureVisible(fTree, hItem); + } + + return hItem; +} + +void plPickLocalizationDlg::IAddLocalizations(std::string ageName, std::string setName, std::string itemName) +{ + std::vector ages = pfLocalizationDataMgr::Instance().GetAgeList(); + + for (int curAge = 0; curAge < ages.size(); curAge++) + { + HTREEITEM hAgeItem = IAddVar(WStringToString(ages[curAge]), ageName, TVI_ROOT); + + std::vector sets = pfLocalizationDataMgr::Instance().GetSetList(ages[curAge]); + for (int curSet = 0; curSet < sets.size(); curSet++) + { + std::vector elements = pfLocalizationDataMgr::Instance().GetElementList(ages[curAge], sets[curSet]); + + HTREEITEM hSetItem = IAddVar(WStringToString(sets[curSet]), setName, hAgeItem); + for (int curElement = 0; curElement < elements.size(); curElement++) + IAddVar(WStringToString(elements[curElement]), itemName, hSetItem); + } + } +} + +void plPickLocalizationDlg::IUpdateValue(HWND hDlg) +{ + fPath = ""; + + HTREEITEM hItem = TreeView_GetSelection(fTree); + + std::vector path; + while (hItem) + { + char s[200]; + TVITEM tvi = {0}; + tvi.hItem = hItem; + tvi.mask = TVIF_TEXT; + tvi.pszText = s; + tvi.cchTextMax = 200; + TreeView_GetItem(fTree, &tvi); + path.push_back(tvi.pszText); + hItem = TreeView_GetParent(fTree, hItem); + } + + while (!path.empty()) + { + fPath.append(path.back()); + path.pop_back(); + if (!path.empty()) + fPath.append("."); + } + + SetDlgItemText(hDlg, IDC_LOCALIZATIONSTRING, fPath.c_str()); + + IUpdateOkBtn(hDlg); +} + +void plPickLocalizationDlg::IUpdateOkBtn(HWND hDlg) +{ + HWND hOk = GetDlgItem(hDlg, IDOK); + + char s[512]; + GetDlgItemText(hDlg, IDC_LOCALIZATIONSTRING, s, 511); + + EnableWindow(hOk, strlen(s)>0 && IValidatePath()); +} + +bool plPickLocalizationDlg::IValidatePath() +{ + std::string ageName = "", setName = "", itemName = ""; + locIzer.Reset(fPath.c_str(), "."); + if (locIzer.Next(locToken, 200)) + ageName = locToken; + if (locIzer.Next(locToken, 200)) + setName = locToken; + if (locIzer.Next(locToken, 200)) + itemName = locToken; + + if (ageName == "") + return false; // no age, so not valid + if (setName == "") + return false; // no set, so not valid + if (itemName == "") + return false; // no item, so not valid + return true; +} + +BOOL CALLBACK plPickLocalizationDlg::IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static plPickLocalizationDlg* pthis = nil; + + switch (msg) + { + case WM_INITDIALOG: + pthis = (plPickLocalizationDlg*)lParam; + if (!pthis->IInitDlg(hDlg)) + EndDialog(hDlg, 0); + return FALSE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK) + { + EndDialog(hDlg, 1); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, 0); + return TRUE; + } + break; + + case WM_SYSCOMMAND: + switch (wParam) + { + case SC_CLOSE: + EndDialog(hDlg, 0); + return TRUE; + } + break; + + case WM_NOTIFY: + NMHDR *nmhdr = (NMHDR*)lParam; + if (nmhdr->idFrom == IDC_LOCALIZATIONTREE) + { + switch (nmhdr->code) + { + case TVN_SELCHANGED: + pthis->IUpdateValue(hDlg); + return TRUE; + + case NM_DBLCLK: + if (pthis->IValidatePath()) // only close the dialog if it's a valid path + EndDialog(hDlg, 1); + return TRUE; + } + } + break; + } + + return FALSE; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickLocalizationDlg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickLocalizationDlg.h new file mode 100644 index 00000000..441c980b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickLocalizationDlg.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPickLocalizationDlg_h +#define plPickLocalizationDlg_h + +#include "hsTypes.h" +#include "max.h" +#include + +class plPickLocalizationDlg +{ + static BOOL CALLBACK IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + bool IInitDlg(HWND hDlg); + void IUpdateValue(HWND hDlg); + void IUpdateOkBtn(HWND hDlg); + bool IValidatePath(); // returns true if the current path is a valid subtitle path + HTREEITEM IAddVar(std::string name, std::string match, HTREEITEM hParent); + void IAddLocalizations(std::string ageName, std::string setName, std::string itemName); + + std::string fPath; + HWND fTree; + +public: + plPickLocalizationDlg(const char *path): fPath(path?path:""), fTree(nil) {} + + bool DoPick(); // returns true if [Ok] clicked, false otherwise. + const char *GetValue() const {return fPath.c_str();} +}; + +#endif // plPickLocalizationDlg_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickMaterialMap.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickMaterialMap.cpp new file mode 100644 index 00000000..fa59653e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickMaterialMap.cpp @@ -0,0 +1,179 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plPickMaterialMap.h" +#include "max.h" + +#include "../MaxMain/plMaxNode.h" + +// MAXR4 HACK +// Coming in the backdoor... +#include "hsThread.h" +class hsHackWinFindThread : public hsThread +{ +protected: + enum + { + kOK = 1, + + kMtlLibrary = 0x9c6a, + kMtlEditor, + kActiveSlot, + kSelected, + kScene, + kNew, + }; + +public: + hsError Run() + { + while (1) + { + HWND hMtlDlg = FindWindow(NULL, "Material/Map Browser"); + if (hMtlDlg && IsWindowVisible(GetDlgItem(hMtlDlg, kOK))) + { + SendMessage(GetDlgItem(hMtlDlg, kScene), BM_CLICK, 0, 0); + EnableWindow(GetDlgItem(hMtlDlg, kMtlLibrary), FALSE); + EnableWindow(GetDlgItem(hMtlDlg, kMtlEditor), FALSE); + EnableWindow(GetDlgItem(hMtlDlg, kActiveSlot), FALSE); + EnableWindow(GetDlgItem(hMtlDlg, kNew), FALSE); + return 1; + } + } + } +}; + +static bool IPickMaterial(IParamBlock2 *pb, int id, int flags) +{ + BOOL newMat, cancel; + + hsHackWinFindThread winFind; + winFind.Start(); + + // Get a material + MtlBase *mtl = GetCOREInterface()->DoMaterialBrowseDlg( + GetCOREInterface()->GetMAXHWnd(), + flags | BROWSE_INSTANCEONLY, + newMat, + cancel); + + winFind.Stop(); + + if (!cancel) + pb->SetValue(id, 0, (ReferenceTarget*)mtl); + + return !cancel; +} + +bool plPickMaterialMap::PickTexmap(IParamBlock2 *pb, int id) +{ + return IPickMaterial(pb, id, BROWSE_MAPSONLY); +} + +#include "../MaxMain/plMtlCollector.h" +#include "resource.h" + +static bool GetPickedMtl(HWND hDlg, Mtl** mtl) +{ + HWND hList = GetDlgItem(hDlg, IDC_MTL_LIST); + int sel = ListBox_GetCurSel(hList); + if (sel != LB_ERR) + { + *mtl = (Mtl*)ListBox_GetItemData(hList, sel); + return true; + } + + return false; +} + +static BOOL CALLBACK PickMtlProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static plPickMaterialInfo* info; + + if (msg == WM_INITDIALOG) + { + info = (plPickMaterialInfo*)lParam; + + MtlSet mtls; + plMtlCollector::GetMtls(&mtls, nil, info->fFlags); + + HWND hList = GetDlgItem(hDlg, IDC_MTL_LIST); + + MtlSet::iterator it = mtls.begin(); + for (; it != mtls.end(); it++) + { + int idx = ListBox_AddString(hList, (*it)->GetName()); + ListBox_SetItemData(hList, idx, (*it)); + } + + return TRUE; + } + else if (msg == WM_COMMAND) + { + if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) + { + if (LOWORD(wParam) == IDOK) + { + if (GetPickedMtl(hDlg, &info->fMtl)) + { + EndDialog(hDlg, 1); + return TRUE; + } + } + + EndDialog(hDlg, 0); + return TRUE; + } + else if (HIWORD(wParam) == LBN_DBLCLK && LOWORD(wParam) == IDC_MTL_LIST) + { + if (GetPickedMtl(hDlg, &info->fMtl)) + EndDialog(hDlg, 1); + return TRUE; + } + } + + return FALSE; +} + +Mtl *plPickMaterialMap::PickMaterial(unsigned int flags) +{ + plPickMaterialInfo info; + info.fMtl = NULL; + info.fFlags = flags; + + //Mtl *mtl = NULL; + int ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PICK_MTL), GetCOREInterface()->GetMAXHWnd(), PickMtlProc, (LPARAM)&info); + + if (ret == 1) + { + //pb->SetValue(id, 0, (ReferenceTarget*)mtl); + //return true; + return info.fMtl; + } + + return nil; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickMaterialMap.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickMaterialMap.h new file mode 100644 index 00000000..1df451ac --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickMaterialMap.h @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +class plMessage; +class IParamBlock2; +class ClassDesc2; +class plMaxNode; +class plErrorMsg; +class plComponentBase; +class Mtl; +class plMaxNodeBase; +template class hsTArray; + +//int GetMatAnimModKey(Mtl* mtl, plMaxNodeBase* node, float begin, float end, hsTArray& keys); + +namespace plPickMaterialMap +{ + bool PickTexmap(IParamBlock2 *pb, int id); + Mtl *PickMaterial(unsigned int flags); +}; + +struct plPickMaterialInfo +{ + Mtl *fMtl; + unsigned int fFlags; +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNode.cpp new file mode 100644 index 00000000..192ccd68 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNode.cpp @@ -0,0 +1,331 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plPickNode.h" +#include "iparamb2.h" +#include + +#include "plActivatorBaseComponent.h" +#include "plPythonFileComponent.h" +#include "plBehavioralComponents.h" +#include "plNavigableComponents.h" +#include "plPhysicalComponents.h" +#include "plCameraComponents.h" + +class plPickNodeMax : public HitByNameDlgCallback +{ +protected: + bool fSingle; + bool fCanConvertToType; + IParamBlock2 *fPB; + int fNodeListID; + std::vector fCIDs; + bool fRefKludge; + plComponentBase *fComp; + Mtl* fMtl; + + bool CanConvertToType(Object *obj); + +public: + plPickNodeMax(IParamBlock2 *pb, int nodeListID, std::vector* cids, bool single, bool canConvertToType); + + virtual TCHAR *dialogTitle(); + virtual TCHAR *buttonText() { return "OK"; } + + virtual int filter(INode *node); + + virtual void proc(INodeTab &nodeTab); + + virtual BOOL showHiddenAndFrozen() { return TRUE; } + virtual BOOL singleSelect() { return fSingle; } + + void SetRefKludge(bool on) { fRefKludge = on; } + + void SetComponent(plComponentBase *comp) { fComp = comp; } + void SetMtl(Mtl* mtl) { fMtl = mtl; } +}; + +bool plPickNodeMax::CanConvertToType(Object *obj) +{ + for (int i = 0; i < fCIDs.size(); i++) + { + if (obj->CanConvertToType(fCIDs[i])) + return true; + } + + return false; +} + +plPickNodeMax::plPickNodeMax(IParamBlock2 *pb, int nodeListID, std::vector* cids, bool single, bool canConvertToType) : + fPB(pb), fNodeListID(nodeListID), fSingle(single), fCanConvertToType(canConvertToType), fRefKludge(false), fComp(nil), fMtl(nil) +{ + if (cids) + fCIDs = *cids; +} + +TCHAR *plPickNodeMax::dialogTitle() +{ + return fSingle ? "Select Node" : "Select Nodes"; +} + +int plPickNodeMax::filter(INode *node) +{ + if (node && node->GetObjectRef()) + { + // Filtering by nodes a component is attached to + if (fComp) + { + for (int i = 0; i < fComp->NumTargets(); i++) + { + if (fComp->GetTarget(i) == (plMaxNodeBase*)node) + return TRUE; + } + + return FALSE; + } + + // Filtering by nodes with a specific material on them + if (fMtl) + { + if (node->GetMtl() == fMtl) + return TRUE; + return FALSE; + } + + // Not filtering by ClassID and node is hidden or frozen + if (fCIDs.size() == 0 && (node->IsHidden() || node->IsFrozen())) + return FALSE; + + // We aren't filtering by ClassID or we are and we found a match + if (fCIDs.size() == 0 || + (fCanConvertToType && CanConvertToType(node->GetObjectRef()) || + std::find(fCIDs.begin(), fCIDs.end(), node->GetObjectRef()->ClassID()) != fCIDs.end())) + { + if (fSingle) + { + if (fPB->GetReferenceTarget(fNodeListID) == (ReferenceTarget*)node) + return FALSE; + } + else + { + // Make sure we don't already ref this node + for (int i = 0; i < fPB->Count(fNodeListID); i++) + { + if (fPB->GetReferenceTarget(fNodeListID, 0, i) == (ReferenceTarget*)node) + return FALSE; + } + } + + // Don't allow a ref to ourselves (a cyclical reference) + if (fPB->GetOwner() == node) + return FALSE; + + // Approved and not in the list, add it + return TRUE; + } + } + + return FALSE; +} + +void plPickNodeMax::proc(INodeTab &nodeTab) +{ + if (nodeTab.Count() > 0) + { + if (!fRefKludge) + { + if (fSingle) + fPB->SetValue(fNodeListID, 0, nodeTab[0]); + else + fPB->Append(fNodeListID, nodeTab.Count(), &nodeTab[0]); + } + // Have to be a little wacky here since some of the params that use this are + // ReferenceTargets while some are actually INodes (like they're supposed to be). + else + { + if (fSingle) + fPB->SetValue(fNodeListID, 0, (ReferenceTarget*)nodeTab[0]); + else + fPB->Append(fNodeListID, nodeTab.Count(), (ReferenceTarget**)&nodeTab[0]); + } + } +} + +#include "plPickNodeBase.h" + +bool plPick::Node(IParamBlock2 *pb, int paramID, std::vector* cids, bool single, bool canConvertToType) +{ + plPickNodeMax pick(pb, paramID, cids, single, canConvertToType); + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +bool plPick::NodeRefKludge(IParamBlock2 *pb, int paramID, std::vector* cids, bool single, bool canConvertToType) +{ + plPickNodeMax pick(pb, paramID, cids, single, canConvertToType); + pick.SetRefKludge(true); + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +bool plPick::CompTargets(IParamBlock2 *pb, int paramID, plComponentBase *comp) +{ + plPickCompNode pick(pb, paramID, comp); + return pick.DoPick(); +} + +bool plPick::MtlNodes(IParamBlock2* pb, int paramID, Mtl* mtl) +{ + plPickMtlNode pick(pb, paramID, mtl); + return pick.DoPick(); +} + +#include "plMultistageBehComponent.h" + +bool plPick::Activator(IParamBlock2 *pb, int paramID, bool single) +{ + std::vector cid; + cid.push_back(ACTIVATOR_BASE_CID); + cid.push_back(PYTHON_FILE_CID); + cid.push_back(BEHAVIORAL_SITTING_CID); + cid.push_back(MULTISTAGE_BEH_CID); + cid.push_back(SUBWORLD_REGION_CID); + plPickNodeMax pick(pb, paramID, &cid, single, true); + + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +bool plPick::DetectorEnable(IParamBlock2 *pb, int paramID, bool single) +{ + std::vector cid; + cid.push_back(ACTIVATOR_BASE_CID); + cid.push_back(PYTHON_FILE_CID); + cid.push_back(BEHAVIORAL_SITTING_CID); + cid.push_back(NAV_LADDER_CID); + cid.push_back(NAV_LADDER_CID); + cid.push_back(CAM_REGION_CID); + plPickNodeMax pick(pb, paramID, &cid, single, true); + + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +#include "plGUIComponents.h" + +bool plPick::GUIDialog(IParamBlock2 *pb, int paramID, bool single) +{ + std::vector cid; + cid.push_back(GUI_DIALOG_COMP_CLASS_ID); + plPickNodeMax pick(pb, paramID, &cid, single, true); + + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +bool plPick::GenericClass(IParamBlock2 *pb, int paramID, bool single, Class_ID classIDToPick ) +{ + std::vector cid; + cid.push_back(classIDToPick); + plPickNodeMax pick(pb, paramID, &cid, single, true); + + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +#include "plExcludeRegionComponent.h" + +bool plPick::ExcludeRegion(IParamBlock2 *pb, int paramID, bool single) +{ + std::vector cid; + cid.push_back(XREGION_CID); + plPickNodeMax pick(pb, paramID, &cid, single, true); + + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +#include "plWaterComponent.h" + +bool plPick::WaterComponent(IParamBlock2 *pb, int paramID, bool single) +{ + std::vector cid; + cid.push_back(WATER_COMP_CID); + plPickNodeMax pick(pb, paramID, &cid, single, true); + + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +#include "plPhysicalComponents.h" + +bool plPick::Swim2DComponent(IParamBlock2 *pb, int paramID, bool single) +{ + std::vector cid; + cid.push_back(PHYS_SWIMSURFACE_CID); + plPickNodeMax pick(pb, paramID, &cid, single, true); + + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +#include "plClusterComponent.h" + +bool plPick::ClusterComponent(IParamBlock2 *pb, int paramID, bool single) +{ + std::vector cid; + cid.push_back(CLUSTER_COMP_CID); + plPickNodeMax pick(pb, paramID, &cid, single, true); + + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +#include "plAnimComponent.h" + +bool plPick::Animation(IParamBlock2 *pb, int paramID, bool single) +{ + std::vector cid; + cid.push_back(ANIM_COMP_CID); + cid.push_back(ANIM_GROUP_COMP_CID); + plPickNodeMax pick(pb, paramID, &cid, single, true); + + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +#include "plOneShotComponent.h" +#include "plMultistageBehComponent.h" + +bool plPick::Behavior(IParamBlock2 *pb, int paramID, bool single) +{ + std::vector cid; + cid.push_back(ONESHOTCLASS_ID); + cid.push_back(MULTISTAGE_BEH_CID); + plPickNodeMax pick(pb, paramID, &cid, single, true); + + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} + +#include "plGrassComponent.h" + +bool plPick::GrassComponent(IParamBlock2 *pb, int paramID, bool single) +{ + std::vector cid; + cid.push_back(GRASS_COMPONENT_CLASS_ID); + plPickNodeMax pick(pb, paramID, &cid, single, true); + + return (GetCOREInterface()->DoHitByNameDialog(&pick) != 0); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNode.h new file mode 100644 index 00000000..7556c7ea --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNode.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPickNode_h_inc +#define plPickNode_h_inc + +#include "hsConfig.h" +#include "max.h" +#include + +class plComponentBase; + +namespace plPick +{ + bool Node(IParamBlock2 *pb, int paramID, std::vector* cids, bool single, bool canConvertToType); + bool Activator(IParamBlock2 *pb, int paramID, bool single); + bool GUIDialog(IParamBlock2 *pb, int paramID, bool single); + bool ExcludeRegion(IParamBlock2 *pb, int paramID, bool single); + bool WaterComponent(IParamBlock2 *pb, int paramID, bool single); + bool Swim2DComponent(IParamBlock2 *pb, int paramID, bool single); + bool ClusterComponent(IParamBlock2 *pb, int paramID, bool single); + bool Animation(IParamBlock2 *pb, int paramID, bool single); + bool Behavior(IParamBlock2 *pb, int paramID, bool single); + bool GenericClass(IParamBlock2 *pb, int paramID, bool single, Class_ID classIDToPick ); + bool GrassComponent(IParamBlock2 *pb, int paramID, bool single); + + // Basically the same as activator, but includes other things with built in detectors (ladder) + // that you can enable/disable but shouldn't be triggering off of + bool DetectorEnable(IParamBlock2 *pb, int paramID, bool single); + + // Can only pick a target of comp + bool CompTargets(IParamBlock2 *pb, int paramID, plComponentBase *comp); + // Can only pick a node this material is applied to + bool MtlNodes(IParamBlock2* pb, int paramID, Mtl* mtl); + + bool NodeRefKludge(IParamBlock2 *pb, int paramID, std::vector* cids, bool single, bool canConvertToType); +} + +#endif // plPickNode_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeBase.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeBase.cpp new file mode 100644 index 00000000..7b7201de --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeBase.cpp @@ -0,0 +1,253 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plPickNodeBase.h" + +#include "../MaxMain/plMaxNode.h" +#include "plComponentBase.h" +#include "resource.h" +#include "../MaxMain/plMaxAccelerators.h" + +#include + +plPickNodeBase::plPickNodeBase(IParamBlock2* pb, int nodeParamID) : fPB(pb), fNodeParamID(nodeParamID) +{ +} + + +bool plPickNodeBase::DoPick() +{ + plMaxAccelerators::Disable(); + + // Create Dlg + BOOL ret = DialogBoxParam(hInstance, + MAKEINTRESOURCE(IDD_PICK_NODE), + GetCOREInterface()->GetMAXHWnd(), + IDlgProc, + (LPARAM)this); + + plMaxAccelerators::Enable(); + + return (ret != 0); +} + +void plPickNodeBase::IInitDlg(HWND hDlg) +{ + HWND hList = GetDlgItem(hDlg, IDC_NODE_LIST); + +// int idx = ListBox_AddString(hList, kUserTypeNone); +// ListBox_SetCurSel(hList, idx); + + IAddUserType(hList); + +// LONG style = GetWindowLong(hList, GWL_STYLE); +// SetWindowLong(hList, GWL_STYLE, style | LBS_MULTIPLESEL); + + plMaxNode* curSelNode = nil; + ParamType2 type = fPB->GetParameterType(fNodeParamID); + if (type == TYPE_REFTARG) + curSelNode = (plMaxNode*)fPB->GetReferenceTarget(fNodeParamID); + else if (type == TYPE_INODE) + curSelNode = (plMaxNode*)fPB->GetINode(fNodeParamID); + + IGetNodesRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), hList, curSelNode); +} + +BOOL plPickNodeBase::IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static plPickNodeBase* pthis = nil; + + switch (msg) + { + case WM_INITDIALOG: + pthis = (plPickNodeBase*)lParam; + pthis->IInitDlg(hDlg); + return TRUE; + + case WM_COMMAND: + // For 'OK' or double-click, get the selected node and exit + if ((HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK) || + (HIWORD(wParam) == LBN_DBLCLK && LOWORD(wParam) == IDC_NODE_LIST)) + { + pthis->IGetSelNode(GetDlgItem(hDlg, IDC_NODE_LIST)); + EndDialog(hDlg, 1); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, 0); + return TRUE; + } + break; + } + + return FALSE; +} + +void plPickNodeBase::IGetNodesRecur(plMaxNode* node, HWND hList, plMaxNode* curSelNode) +{ + if (node) + { + if (ICheckNode(node)) + { + int idx = ListBox_AddString(hList, node->GetName()); + ListBox_SetItemData(hList, idx, node); + + if (node == curSelNode) + ListBox_SetCurSel(hList, idx); + } + + int numChildren = node->NumberOfChildren(); + for (int i = 0; i < numChildren; i++) + { + IGetNodesRecur((plMaxNode*)node->GetChildNode(i), hList, curSelNode); + } + } +} + +void plPickNodeBase::ISetNodeValue(plMaxNode* node) +{ + ParamType2 type = fPB->GetParameterType(fNodeParamID); + if (type == TYPE_REFTARG) + { + fPB->SetValue(fNodeParamID, 0, (ReferenceTarget*)node); + } +// else if (type == TYPE_REFTARG_TAB) +// { +// } + else if (type == TYPE_INODE) + { + fPB->SetValue(fNodeParamID, 0, (INode*)node); + } +// else if (type == TYPE_INODE_TAB) +// { +// } +} + +void plPickNodeBase::IGetSelNode(HWND hList) +{ + int sel = ListBox_GetCurSel(hList); + if (sel == LB_ERR) + return; + + plMaxNode* node = (plMaxNode*)ListBox_GetItemData(hList, sel); + + if (node) + { + ISetNodeValue(node); + ISetUserType(node, nil); + } + else + { + int len = ListBox_GetTextLen(hList, sel); + char* buf = TRACKED_NEW char[len+1]; + ListBox_GetText(hList, sel, buf); + +/* if (!strcmp(buf, kUserTypeNone)) + { + ISetNodeValue(nil); + ISetUserType(nil, nil); + } + else +*/ ISetUserType(nil, buf); + + delete [] buf; + } +} + +///////////////////////////////////////////////////////////////// + +plPickNode::plPickNode(IParamBlock2* pb, int nodeParamID) : + plPickNodeBase(pb, nodeParamID) +{ +} + +bool plPickNode::ICanConvertToType(Object *obj) +{ + for (int i = 0; i < fCIDs.size(); i++) + { + if (obj->CanConvertToType(fCIDs[i])) + return true; + } + + return false; +} + +bool plPickNode::ICheckNode(plMaxNode* node) +{ + if (node->GetObjectRef()) + { + // Not filtering by ClassID and node is hidden or frozen + if (fCIDs.size() == 0 && (node->IsHidden() || node->IsFrozen())) + return false; + + // We aren't filtering by ClassID or we are and we found a match + if (fCIDs.size() == 0 || + (fCanConvertToType && ICanConvertToType(node->GetObjectRef()) || + std::find(fCIDs.begin(), fCIDs.end(), node->GetObjectRef()->ClassID()) != fCIDs.end())) + { + // Don't allow a ref to ourselves (a cyclical reference) + if (fPB->GetOwner() == node) + return false; + + // Approved and not in the list, add it + return true; + } + } + + return false; +} + +/////////////////////////////////////////////////////////////// + +plPickMtlNode::plPickMtlNode(IParamBlock2* pb, int nodeParamID, Mtl* mtl) : + plPickNodeBase(pb, nodeParamID), fMtl(mtl) +{ +} + +bool plPickMtlNode::ICheckNode(plMaxNode* node) +{ + // Filtering by nodes with a specific material on them + if (fMtl && node->GetMtl() == fMtl) + return true; + return false; +} + +///////////////////////////////////////////////////// + +plPickCompNode::plPickCompNode(IParamBlock2* pb, int nodeParamID, plComponentBase* comp) : + plPickNodeBase(pb, nodeParamID), fComp(comp) +{ +} + +bool plPickCompNode::ICheckNode(plMaxNode* node) +{ + // Filtering by nodes a component is attached to + if (fComp && fComp->IsTarget((plMaxNodeBase*)node)) + return true; + + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeBase.h new file mode 100644 index 00000000..ba35a164 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeBase.h @@ -0,0 +1,104 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPickNodeBase_h_inc +#define plPickNodeBase_h_inc + +#include "max.h" +#include + +class IParamBlock2; +class plMaxNode; +class plComponentBase; + +class plPickNodeBase +{ +protected: + IParamBlock2* fPB; + int fNodeParamID; + + static BOOL CALLBACK IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + void IInitDlg(HWND hDlg); + + void IGetNodesRecur(plMaxNode* node, HWND hList, plMaxNode* curSelNode); + + void ISetNodeValue(plMaxNode* node); + void IGetSelNode(HWND hList); + + virtual bool ICheckNode(plMaxNode* node)=0; + + virtual void IAddUserType(HWND hList) {} + virtual void ISetUserType(plMaxNode* node, const char* userType) {} + +public: + plPickNodeBase(IParamBlock2* pb, int nodeParamID); + virtual ~plPickNodeBase() {} + + bool DoPick(); +}; + +class plPickNode : public plPickNodeBase +{ +protected: + std::vector fCIDs; + bool fCanConvertToType; + + bool ICanConvertToType(Object *obj); + virtual bool ICheckNode(plMaxNode* node); + +public: + plPickNode(IParamBlock2* pb, int nodeParamID); +}; + +// +// Pick one of the nodes a material is attached to +// +class plPickMtlNode : public plPickNodeBase +{ +protected: + Mtl* fMtl; + + bool ICheckNode(plMaxNode* node); + +public: + plPickMtlNode(IParamBlock2* pb, int nodeParamID, Mtl* mtl); +}; + +// +// Pick one of the nodes a component is attached to +// +class plPickCompNode : public plPickNodeBase +{ +protected: + plComponentBase* fComp; + + bool ICheckNode(plMaxNode* node); + +public: + plPickCompNode(IParamBlock2* pb, int nodeParamID, plComponentBase* comp); +}; + +#endif // plPickNodeBase_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeComp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeComp.cpp new file mode 100644 index 00000000..16c6df9d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPickNodeComp.cpp @@ -0,0 +1,252 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPickNodeBase.h" + +#include "../MaxMain/plMaxNode.h" +#include "plComponentBase.h" +#include "resource.h" +#include "../MaxMain/plMaxAccelerators.h" + +#include + +plPickNodeBase::plPickNodeBase(IParamBlock2* pb, int nodeParamID) : fPB(pb), fNodeParamID(nodeParamID) +{ +} + + +bool plPickNodeBase::DoPick() +{ + plMaxAccelerators::Disable(); + + // Create Dlg + BOOL ret = DialogBoxParam(hInstance, + MAKEINTRESOURCE(IDD_PICK_NODE), + GetCOREInterface()->GetMAXHWnd(), + IDlgProc, + (LPARAM)this); + + plMaxAccelerators::Enable(); + + return (ret != 0); +} + +void plPickNodeBase::IInitDlg(HWND hDlg) +{ + HWND hList = GetDlgItem(hDlg, IDC_NODE_LIST); + +// int idx = ListBox_AddString(hList, kUserTypeNone); +// ListBox_SetCurSel(hList, idx); + + IAddUserType(hList); + +// LONG style = GetWindowLong(hList, GWL_STYLE); +// SetWindowLong(hList, GWL_STYLE, style | LBS_MULTIPLESEL); + + plMaxNode* curSelNode = nil; + ParamType2 type = fPB->GetParameterType(fNodeParamID); + if (type == TYPE_REFTARG) + curSelNode = (plMaxNode*)fPB->GetReferenceTarget(fNodeParamID); + else if (type == TYPE_INODE) + curSelNode = (plMaxNode*)fPB->GetINode(fNodeParamID); + + IGetNodesRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), hList, curSelNode); +} + +BOOL plPickNodeBase::IDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static plPickNodeBase* pthis = nil; + + switch (msg) + { + case WM_INITDIALOG: + pthis = (plPickNodeBase*)lParam; + pthis->IInitDlg(hDlg); + return TRUE; + + case WM_COMMAND: + // For 'OK' or double-click, get the selected node and exit + if ((HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK) || + (HIWORD(wParam) == LBN_DBLCLK && LOWORD(wParam) == IDC_NODE_LIST)) + { + pthis->IGetSelNode(GetDlgItem(hDlg, IDC_NODE_LIST)); + EndDialog(hDlg, 1); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, 0); + return TRUE; + } + break; + } + + return FALSE; +} + +void plPickNodeBase::IGetNodesRecur(plMaxNode* node, HWND hList, plMaxNode* curSelNode) +{ + if (node) + { + if (ICheckNode(node)) + { + int idx = ListBox_AddString(hList, node->GetName()); + ListBox_SetItemData(hList, idx, node); + + if (node == curSelNode) + ListBox_SetCurSel(hList, idx); + } + + int numChildren = node->NumberOfChildren(); + for (int i = 0; i < numChildren; i++) + { + IGetNodesRecur((plMaxNode*)node->GetChildNode(i), hList, curSelNode); + } + } +} + +void plPickNodeBase::ISetNodeValue(plMaxNode* node) +{ + ParamType2 type = fPB->GetParameterType(fNodeParamID); + if (type == TYPE_REFTARG) + { + fPB->SetValue(fNodeParamID, 0, (ReferenceTarget*)node); + } +// else if (type == TYPE_REFTARG_TAB) +// { +// } + else if (type == TYPE_INODE) + { + fPB->SetValue(fNodeParamID, 0, (INode*)node); + } +// else if (type == TYPE_INODE_TAB) +// { +// } +} + +void plPickNodeBase::IGetSelNode(HWND hList) +{ + int sel = ListBox_GetCurSel(hList); + if (sel == LB_ERR) + return; + + plMaxNode* node = (plMaxNode*)ListBox_GetItemData(hList, sel); + + if (node) + { + ISetNodeValue(node); + ISetUserType(node, nil); + } + else + { + int len = ListBox_GetTextLen(hList, sel); + char* buf = new char[len+1]; + ListBox_GetText(hList, sel, buf); + +/* if (!strcmp(buf, kUserTypeNone)) + { + ISetNodeValue(nil); + ISetUserType(nil, nil); + } + else +*/ ISetUserType(nil, buf); + + delete [] buf; + } +} + +///////////////////////////////////////////////////////////////// + +plPickNode::plPickNode(IParamBlock2* pb, int nodeParamID) : + plPickNodeBase(pb, nodeParamID) +{ +} + +bool plPickNode::ICanConvertToType(Object *obj) +{ + for (int i = 0; i < fCIDs.size(); i++) + { + if (obj->CanConvertToType(fCIDs[i])) + return true; + } + + return false; +} + +bool plPickNode::ICheckNode(plMaxNode* node) +{ + if (node->GetObjectRef()) + { + // Not filtering by ClassID and node is hidden or frozen + if (fCIDs.size() == 0 && (node->IsHidden() || node->IsFrozen())) + return false; + + // We aren't filtering by ClassID or we are and we found a match + if (fCIDs.size() == 0 || + (fCanConvertToType && ICanConvertToType(node->GetObjectRef()) || + std::find(fCIDs.begin(), fCIDs.end(), node->GetObjectRef()->ClassID()) != fCIDs.end())) + { + // Don't allow a ref to ourselves (a cyclical reference) + if (fPB->GetOwner() == node) + return false; + + // Approved and not in the list, add it + return true; + } + } + + return false; +} + +/////////////////////////////////////////////////////////////// + +plPickMtlNode::plPickMtlNode(IParamBlock2* pb, int nodeParamID, Mtl* mtl) : + plPickNodeBase(pb, nodeParamID), fMtl(mtl) +{ +} + +bool plPickMtlNode::ICheckNode(plMaxNode* node) +{ + // Filtering by nodes with a specific material on them + if (fMtl && node->GetMtl() == fMtl) + return true; + return false; +} + +///////////////////////////////////////////////////// + +plPickCompNode::plPickCompNode(IParamBlock2* pb, int nodeParamID, plComponentBase* comp) : + plPickNodeBase(pb, nodeParamID), fComp(comp) +{ +} + +bool plPickCompNode::ICheckNode(plMaxNode* node) +{ + // Filtering by nodes a component is attached to + if (fComp && fComp->IsTarget((plMaxNodeBase*)node)) + return true; + + return false; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPythonFileComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPythonFileComponent.cpp new file mode 100644 index 00000000..f653218d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPythonFileComponent.cpp @@ -0,0 +1,1311 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plPythonFileComponent.h" + +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxMain/plMaxNode.h" +#include "hsUtils.h" +#include "plAutoUIBlock.h" +#include "plAutoUIParams.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../MaxMain/plPluginResManager.h" +#include "../pnMessage/plObjRefMsg.h" +#include "plVolumeGadgetComponent.h" +#include "plGUIComponents.h" +#include "pfGUISkinComp.h" +#include "plExcludeRegionComponent.h" +#include "plAnimComponent.h" +#include "plNotetrackAnim.h" +#include "plOneShotComponent.h" +#include "plMultistageBehComponent.h" +#include "../plInterp/plAnimEaseTypes.h" +#include "../plAgeDescription/plAgeDescription.h" +#include "plWaterComponent.h" +#include "../plDrawable/plWaveSetBase.h" +#include "plClusterComponent.h" +#include "../plDrawable/plClusterGroup.h" +#include "plPhysicalComponents.h" +//#include "../plHavok1/plHKPhysical.h" +#include "../plAvatar/plSwimRegion.h" +#include "../../PubUtilLib/plSurface/plGrassShaderMod.h" +#include "plGrassComponent.h" + +#include "notify.h" + +#include +#include +#include +#include + +#include "../pfPython/plPythonFileMod.h" +#include "../pfPython/plPythonParameter.h" + +// for DynamicText hack to get the plKeys (could probably be removed later) +#include "../plGImage/plDynamicTextMap.h" + +//// plCommonPythonLib /////////////////////////////////////////////////////// +// Derived class for our global python fileMods, since they go in to the +// BuiltIn page +// 2.4.03 mcn - Added sceneObjects to the list, so that we can have an +// associated sceneObject for our mod + +#include "../MaxMain/plCommonObjLib.h" +#include "../plSDL/plSDL.h" + +class plCommonPythonLib : public plCommonObjLib +{ + public: + virtual hsBool IsInteresting( const plKey &objectKey ) + { + if( objectKey->GetUoid().GetClassType() == plPythonFileMod::Index() ) + return true; + if( objectKey->GetUoid().GetClassType() == plSceneObject::Index() && + strcmp( objectKey->GetUoid().GetObjectName(), plSDL::kAgeSDLObjectName ) == 0 ) + return true; + return false; + } +}; + +static plCommonPythonLib sCommonPythonLib; + + + +static void NotifyProc(void *param, NotifyInfo *info); + +// Notify us on file open so we can check for bad Python components +void DummyCodeIncludePythonFileFunc() +{ + RegisterNotification(NotifyProc, nil, NOTIFY_FILE_POST_OPEN); + RegisterNotification(NotifyProc, nil, NOTIFY_FILE_PRE_SAVE); + RegisterNotification(NotifyProc, nil, NOTIFY_SYSTEM_POST_RESET); + RegisterNotification(NotifyProc, nil, NOTIFY_SYSTEM_POST_NEW); + RegisterNotification(NotifyProc, nil, NOTIFY_SYSTEM_SHUTDOWN2); +} + +// Param block ID's +enum +{ + kPythonFileType_DEAD, + kPythonFilePB, + kPythonVersion, + kPythonFileIsGlobal +}; + +class plPythonFileComponent : public plComponent +{ +public: + typedef std::map PythonKeys; + + // When we're auto-exporting and can't pop up an error dialog, we cache our + // errors here and blab about it at export time + static std::string fPythonError; + +protected: + PythonKeys fModKeys; + hsTArray fOthersKeys; + +public: + plPythonFileComponent(); + + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + virtual void AddReceiverKey(plKey key, plMaxNode* node=nil) { fOthersKeys.Append(key); } + + // Returns false if the Python file for this component wasn't loaded, and clears + // the data in the PB to prevent Max from crashing. + enum Validate + { + kPythonOk, + kPythonNoFile, + kPythonBadVer, + kPythonNoVer, + }; + Validate ValidateFile(); + + const char* GetPythonName(); + + virtual PythonKeys& GetKeys() { return fModKeys; } + + virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg) + { + fModKeys.clear(); + fOthersKeys.Reset(); + + return plComponent::DeInit(node, pErrMsg); + } +}; + +CLASS_DESC(plPythonFileComponent, gPythonFileComponentDesc, "Python File", "PythonFile", COMP_TYPE_LOGIC, PYTHON_FILE_CID); + +std::string plPythonFileComponent::fPythonError; + +//////////////////////////////////////////////////////////////////////////////// +// Called by the Python File manager +// + +// Storage for all the registered Python file types +static std::vector gAutoUIBlocks; + +void PythonFile::AddAutoUIBlock(plAutoUIBlock *block) +{ + for (int i = 0; i < gAutoUIBlocks.size(); i++) + { + if (gAutoUIBlocks[i]->GetBlockID() == block->GetBlockID()) + { + char buf[256]; + sprintf(buf, + "Duplicate Python File ID\n\n" + "%s and %s use ID %d\n\n" + "%s will be ignored.", + gAutoUIBlocks[i]->GetName(), + block->GetName(), + block->GetBlockID(), + block->GetName()); + hsMessageBox(buf, "Warning", hsMBoxOk); + return; + } + } + + gAutoUIBlocks.push_back(block); +} + +plComponentClassDesc *PythonFile::GetClassDesc() +{ + return &gPythonFileComponentDesc; +} + +static plAutoUIBlock *FindAutoUI(IParamBlock2 *pb) +{ + if (!pb) + return nil; + + for (int i = 0; i < gAutoUIBlocks.size(); i++) + { + if (gAutoUIBlocks[i]->GetBlockID() == pb->ID()) + return gAutoUIBlocks[i]; + } + + return nil; +} + +//////////////////////////////////////////////////////////////////////////////// + + +plPythonFileComponent::plPythonFileComponent() +{ + fClassDesc = &gPythonFileComponentDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plPythonFileComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (fPythonError.length() > 0) + { + pErrMsg->Set(true, "Python Error", fPythonError.c_str()).Show(); + pErrMsg->Set(); + fPythonError = ""; + } + + fModKeys.clear(); + return true; +} + +hsBool plPythonFileComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB); + plAutoUIBlock *block = FindAutoUI(pb); + + if (!block) + return false; + + // if this is a multi-modifier python file component then is this the main (or first) maxnode + bool mainMultiModierNode = false; + + plPythonFileMod *mod = TRACKED_NEW plPythonFileMod; + + // create the modifier key ourselves so that we can get the name of the modifier in the name + plSceneObject *obj = node->GetSceneObject(); + plKey modKey; + if( fCompPB->GetInt( kPythonFileIsGlobal ) ) + { + // Do we already have this guy? + plPythonFileMod *existingOne = plPythonFileMod::ConvertNoRef( sCommonPythonLib.FindObject( plPythonFileMod::kGlobalNameKonstant ) ); + if( existingOne != nil ) + { + // This component already exists, which can happen since it's in a common page. So we need + // to nuke the key and its object so we can reuse it. + modKey = existingOne->GetKey(); + + // But first detach it from its target sceneObject + if( existingOne->GetTarget( 0 ) != nil ) + existingOne->GetTarget( 0 )->GetKey()->Release( modKey ); + + if( !sCommonPythonLib.RemoveObjectAndKey( modKey ) ) + { + pErrMsg->Set( true, "Python File Component Error", + "The global Python File Component %s is attempting to export over an already " + "existing component of the same name, and the exporter is unable to delete the " + "old object to replace it. This would be a good time to call mcn for help.", GetINode()->GetName() ).Show(); + pErrMsg->Set( false ); + } + + // Pointer is invalid now anyways... + existingOne = nil; + } + + // Also make sure we have an age SDL object to attach to (currently only used for python, hence why it's safe here) + obj = plSceneObject::ConvertNoRef( sCommonPythonLib.FindObject( plSDL::kAgeSDLObjectName ) ); + if( obj != nil ) + { + plKey foo = obj->GetKey(); + if( !sCommonPythonLib.RemoveObjectAndKey( foo ) ) + { + pErrMsg->Set( true, "Python File Component Error", + "The global Python File Component %s is attempting to export over an already " + "existing component of the same name, and the exporter is unable to delete the " + "old sceneObject to replace it. This would be a good time to call mcn for help.", GetINode()->GetName() ).Show(); + pErrMsg->Set( false ); + } + + // Pointer is invalid now anyways... + obj = nil; + } + + // Create us a new sceneObject to attach to + obj = TRACKED_NEW plSceneObject; + + const plLocation &globalLoc = plPluginResManager::ResMgr()->GetCommonPage( node->GetLocation(), plAgeDescription::kGlobal ); + hsAssert( globalLoc.IsValid(), "Invalid common page location!!!" ); + modKey = hsgResMgr::ResMgr()->NewKey(plPythonFileMod::kGlobalNameKonstant, mod, globalLoc ); + + // Make a key for our special sceneObject too + plKey sdlObjectKey = hsgResMgr::ResMgr()->NewKey( plSDL::kAgeSDLObjectName, obj, globalLoc ); + plPluginResManager::ResMgr()->AddLooseEnd(sdlObjectKey); + } + else + { + if (block->IsMultiModifier()) + { + // yup, then only create one modifier that will all the objects will attach to + // in other words, one python module with many attached sceneobjects + if ( fModKeys.empty()) + { + // its empty so create the first and only one + modKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), mod, node->GetLocation()); + mainMultiModierNode = true; + } + else + { + // else we just take the first one (should be the only one) + PythonKeys::iterator pos; + pos = fModKeys.begin(); + modKey = pos->second; + + // Guess we won't be using that modifier we new'd up there. Does that mean we should delete it? Sure. + delete mod; + mod = nil; + } + } + else // else, nope... create a modifier for each object its attached to + { + modKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), mod, node->GetLocation()); + } + } + hsgResMgr::ResMgr()->AddViaNotify(modKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + fModKeys[node] = modKey; + + // only let non-multimodifier or multimodifier then only the main node register for notifies + if ( !block->IsMultiModifier() || mainMultiModierNode ) + { + int nParams = block->NumParams(); + for (int i = 0; i < nParams; i++) + { + plAutoUIParam *param = block->GetParam(i); + + if (param->GetParamType() == plAutoUIParam::kTypeActivator) + { + // Register the modifier to recieve notifies from this activator. + // We'll let the modifier know the activator key when it's available, + // in the convert pass. + int numcomps = param->GetCount(pb); + for (int j=0; j< numcomps; j++ ) + { + plComponentBase *comp = param->GetComponent(pb,j); + if (comp) + comp->AddReceiverKey(modKey); + } + } + + if (param->GetParamType() == plAutoUIParam::kTypeGUIDialog) + { + // Register the modifier to recieve notifies from this activator. + // We'll let the modifier know the activator key when it's available, + // in the convert pass. + int numcomps = param->GetCount(pb); + for (int j=0; j< numcomps; j++ ) + { + plComponentBase *comp = param->GetComponent(pb,j); + if (comp && comp->ClassID() == GUI_DIALOG_COMP_CLASS_ID ) + { + // convert the comp to a GUIDialog component, so we can talk to it + plGUIDialogComponent *dialog_comp = (plGUIDialogComponent*)comp; + dialog_comp->SetNotifyReceiver(modKey); + } + } + } + } + } + + return true; +} + +#include "plActivatorBaseComponent.h" +#include "../pnKeyedObject/plKey.h" +#include "plResponderComponent.h" + +hsBool plPythonFileComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB); + plAutoUIBlock *block = FindAutoUI(pb); + + if (!block) + return false; + + plKey modKey = fModKeys[node]; + plPythonFileMod *mod = plPythonFileMod::ConvertNoRef(modKey->GetObjectPtr()); + + // add in all the receivers that want to be notified from the Python coded script + int j; + for (j = 0; j < fOthersKeys.Count(); j++) + { + mod->AddToNotifyList(fOthersKeys[j]); + } + // remove all the Keys we know about to be re-built on the next convert + fOthersKeys.Reset(); + + // set the name of the source file + mod->SetSourceFile(block->GetName()); + + int nParams = block->NumParams(); + for (int i = 0; i < nParams; i++) + { + plAutoUIParam *param = block->GetParam(i); + + plPythonParameter pyParam; + pyParam.fID = param->GetID(); + + // Get the data for the param + switch (param->GetParamType()) + { + case plAutoUIParam::kTypeBool: + pyParam.SetToBoolean(param->GetBool(pb)); + mod->AddParameter(pyParam); + break; + + case plAutoUIParam::kTypeInt: + pyParam.SetToInt(param->GetInt(pb)); + mod->AddParameter(pyParam); + break; + + case plAutoUIParam::kTypeFloat: + pyParam.SetToFloat(param->GetFloat(pb)); + mod->AddParameter(pyParam); + break; + + case plAutoUIParam::kTypeString: + pyParam.SetToString(param->GetString(pb)); + mod->AddParameter(pyParam); + break; + + case plAutoUIParam::kTypeSceneObj: + { + int numKeys = param->GetCount(pb); + hsBool found_atleast_one_good_one = false; + for (int i = 0; i < numKeys; i++) + { + plKey skey = param->GetKey(pb, i); + if ( skey != nil ) + { + pyParam.SetToSceneObject(skey, true); + // make sure that there really was a sceneobject + mod->AddParameter(pyParam); + found_atleast_one_good_one = true; + } + } + if ( !found_atleast_one_good_one ) + { + char buf[512]; + sprintf(buf,"The sceneobject attribute (ID=%d) that was selected in %s PythonFile, somehow does not exist!?", + pyParam.fID,this->GetINode()->GetName()); + pErrMsg->Set(true, "PythonFile Warning", buf).Show(); + pErrMsg->Set(false); + } + } + break; + + case plAutoUIParam::kTypeComponent: + { + int count = param->GetCount(pb); + hsBool found_atleast_one_good_one = false; + for (int i = 0; i < count; i++) + { + plComponentBase *comp = param->GetComponent(pb, i); + if (comp) + { + for (int j = 0; j < comp->NumTargets(); j++) + { + plKey responderKey = Responder::GetKey(comp, comp->GetTarget(j)); + if ( responderKey != nil ) + { + pyParam.SetToResponder(responderKey); + mod->AddParameter(pyParam); + found_atleast_one_good_one = true; + } + } + if ( !found_atleast_one_good_one ) + { + char buf[512]; + sprintf(buf,"The responder attribute %s that was selected in %s PythonFile, somehow does not exist!?", + comp->GetINode()->GetName(),this->GetINode()->GetName()); + pErrMsg->Set(true, "PythonFile Warning", buf).Show(); + pErrMsg->Set(false); + } + } + } + } + break; + + case plAutoUIParam::kTypeActivator: + { + int count = param->GetCount(pb); + for (int i = 0; i < count; i++) + { + plComponentBase *comp = param->GetComponent(pb, i); + // make sure we found a comp and then see if it is an activator type + if (comp && comp->CanConvertToType(ACTIVATOR_BASE_CID)) + { + plActivatorBaseComponent *activator = (plActivatorBaseComponent*)comp; + const plActivatorBaseComponent::LogicKeys& logicKeys = activator->GetLogicKeys(); + plActivatorBaseComponent::LogicKeys::const_iterator it; + for (it = logicKeys.begin(); it != logicKeys.end(); it++) + { + pyParam.SetToActivator(it->second); + mod->AddParameter(pyParam); + } + // do special stuff for Volume sensors because they are stupid turds + if (comp->ClassID() == VOLUMEGADGET_CID) + { + plVolumeGadgetComponent* pClick = (plVolumeGadgetComponent*)comp; + const plVolumeGadgetComponent::LogicKeys &logicKeys2 = pClick->GetLogicOutKeys(); + plVolumeGadgetComponent::LogicKeys::const_iterator VGit; + for (VGit = logicKeys2.begin(); VGit != logicKeys2.end(); VGit++) + { + pyParam.SetToActivator(VGit->second); + mod->AddParameter(pyParam); + } + } + } + // now see if it is a PythonFile kinda activator thingy + else if (comp && comp->ClassID() == PYTHON_FILE_CID) + { + plPythonFileComponent *pyfact = (plPythonFileComponent*)comp; + const plPythonFileComponent::PythonKeys& pythonKeys = pyfact->GetKeys(); + plPythonFileComponent::PythonKeys::const_iterator it; + for (it = pythonKeys.begin(); it != pythonKeys.end(); it++) + { + pyParam.SetToActivator(it->second); + mod->AddParameter(pyParam); + } + } + } + } + break; + + case plAutoUIParam::kTypeDynamicText: + { + int numKeys = param->GetCount(pb); + for (int i = 0; i < numKeys; i++) + { + plKey key = param->GetKey(pb, i); + // make sure we got a key and that it is a DynamicTextMap + if (key && plDynamicTextMap::ConvertNoRef(key->GetObjectPtr()) ) + { + pyParam.SetToDynamicText(key); + mod->AddParameter(pyParam); + } + } + } + break; + + case plAutoUIParam::kTypeGUIDialog: + { + int count = param->GetCount(pb); + for (int i = 0; i < count; i++) + { + plComponentBase *comp = param->GetComponent(pb, i); + if (comp) + { + if (comp && comp->ClassID() == GUI_DIALOG_COMP_CLASS_ID ) + { + // convert the comp to a GUIDialog component, so we can talk to it + plGUIDialogComponent *dialog_comp = (plGUIDialogComponent*)comp; + plKey dialogKey = dialog_comp->GetModifierKey(); + pyParam.SetToGUIDialog(dialogKey); + if ( pyParam.fObjectKey == nil ) + { + char buf[512]; + sprintf(buf,"The GUIDialog attribute %s that was selected in %s PythonFile, somehow does not exist!?", + comp->GetINode()->GetName(),this->GetINode()->GetName()); + pErrMsg->Set(true, "PythonFile Warning", buf).Show(); + pErrMsg->Set(false); + } + else + mod->AddParameter(pyParam); + } + } + } + } + break; + + case plAutoUIParam::kTypeGUIPopUpMenu: + { + int count = param->GetCount(pb); + for (int i = 0; i < count; i++) + { + plComponentBase *comp = param->GetComponent(pb, i); + if (comp) + { + if (comp && comp->ClassID() == GUI_MENUANCHOR_CLASSID ) + { + // convert the comp to a GUIPopUpMenu component, so we can talk to it + plGUIMenuComponent *guiComp = (plGUIMenuComponent*)comp; + plKey key = guiComp->GetConvertedMenuKey(); + pyParam.SetToGUIPopUpMenu( key ); + if ( pyParam.fObjectKey == nil ) + { + char buf[512]; + sprintf(buf,"The GUIPopUpMenu attribute %s that was selected in %s PythonFile, somehow does not exist!?", + comp->GetINode()->GetName(),this->GetINode()->GetName()); + pErrMsg->Set(true, "PythonFile Warning", buf).Show(); + pErrMsg->Set(false); + } + else + mod->AddParameter(pyParam); + } + } + } + } + break; + + case plAutoUIParam::kTypeGUISkin: + { + int count = param->GetCount(pb); + for (int i = 0; i < count; i++) + { + plComponentBase *comp = param->GetComponent(pb, i); + if (comp) + { + if (comp && comp->ClassID() == GUI_SKIN_CLASSID ) + { + // convert the comp to a GUISkin component, so we can talk to it + plGUISkinComp *guiComp = (plGUISkinComp *)comp; + plKey key = guiComp->GetConvertedSkinKey(); + pyParam.SetToGUISkin( key ); + if ( pyParam.fObjectKey == nil ) + { + char buf[512]; + sprintf(buf,"The GUISkin attribute %s that was selected in %s PythonFile, somehow does not exist!?", + comp->GetINode()->GetName(),this->GetINode()->GetName()); + pErrMsg->Set(true, "PythonFile Warning", buf).Show(); + pErrMsg->Set(false); + } + else + mod->AddParameter(pyParam); + } + } + } + } + break; + + case plAutoUIParam::kTypeExcludeRegion: + { + int count = param->GetCount(pb); + int number_of_real_targets_found = 0; + for (int i = 0; i < count; i++) + { + plComponentBase *comp = param->GetComponent(pb, i); + if (comp && comp->ClassID() == XREGION_CID ) + { + for (int j = 0; j < comp->NumTargets(); j++) + { + plExcludeRegionComponent *excomp = (plExcludeRegionComponent*)comp; + plKey exKey = excomp->GetKey((plMaxNode*)(comp->GetTarget(j))); + if ( exKey != nil ) + { + // only get one real target, just count the rest + if ( number_of_real_targets_found == 0 ) + { + pyParam.SetToExcludeRegion(exKey); + mod->AddParameter(pyParam); + } + number_of_real_targets_found += 1; + } + } + if ( number_of_real_targets_found != 1 ) + { + // there is zero or more than one node attached to this exclude region + char buf[512]; + if ( number_of_real_targets_found == 0 ) + sprintf(buf,"The ExcludeRegion %s that was selected as an attribute in %s PythonFile, has no scene nodes attached.", + comp->GetINode()->GetName(),this->GetINode()->GetName()); + else + sprintf(buf,"The ExcludeRegion %s that was selected as an attribute in %s PythonFile, has more than one scene node attached (using first one found).", + comp->GetINode()->GetName(),this->GetINode()->GetName()); + + pErrMsg->Set(true, "PythonFile Warning", buf).Show(); + pErrMsg->Set(false); + } + } + } + } + break; + + case plAutoUIParam::kTypeWaterComponent: + { + plComponentBase* comp = param->GetComponent(pb, 0); + plWaveSetBase* wsb = nil; + + if (comp) + { + wsb = plWaterComponent::GetWaveSet(comp->GetINode()); + + if (wsb != nil) + { + plKey waterKey = wsb->GetKey(); + if ( waterKey != nil ) + { + pyParam.SetToWaterComponent(waterKey); + mod->AddParameter(pyParam); + } + } + } + } + break; + + case plAutoUIParam::kTypeSwimCurrentInterface: + { + plComponentBase* comp = param->GetComponent(pb, 0); + plSwimRegionInterface* sri = nil; + + if (comp && comp->ClassID() == PHYS_SWIMSURFACE_CID) + { + plSwim2DComponent* swimcomp = (plSwim2DComponent*)comp; + std::map::const_iterator containsNode; + plMaxNode* mnode = nil; + + for (int i = 0; i < swimcomp->NumTargets(); i++) + { + mnode = (plMaxNode*)swimcomp->GetTarget(i); + containsNode = swimcomp->fSwimRegions.find(mnode); + if ( containsNode != swimcomp->fSwimRegions.end() ) + { + sri = swimcomp->fSwimRegions[mnode]; + break; + } + } + + if (sri != nil) + { + plKey swimKey = sri->GetKey(); + if ( swimKey != nil ) + { + pyParam.SetToSwimCurrentInterface(swimKey); + mod->AddParameter(pyParam); + } + } + } + } + break; + + case plAutoUIParam::kTypeClusterComponent: + { + plComponentBase* comp = param->GetComponent(pb, 0); + plClusterGroup* clusterGroup = nil; + + if (comp && comp->ClassID() == CLUSTER_COMP_CID) + { + plClusterComponent* clusterComp = (plClusterComponent*)comp; + int numGroups = clusterComp->GetNumGroups(); + int i; + for (i=0; iGetGroup(i); + plKey groupKey = group->GetKey(); + if (groupKey != nil) + { + pyParam.SetToClusterComponent(groupKey); + mod->AddParameter(pyParam); + } + } + } + } + break; + + case plAutoUIParam::kTypeAnimation: + { + int count = param->GetCount(pb); + for (int i = 0; i < count; i++) + { + plComponentBase *comp = param->GetComponent(pb, i); + if (comp && ( comp->ClassID() == ANIM_COMP_CID || comp->ClassID() == ANIM_GROUP_COMP_CID ) ) + { + plAnimComponentBase *animcomp = (plAnimComponentBase*)comp; + // save out the animation name first + const char *tempAnimName = animcomp->GetAnimName(); + if (tempAnimName == nil) + pyParam.SetToAnimationName(ENTIRE_ANIMATION_NAME); + else + pyParam.SetToAnimationName(tempAnimName); + mod->AddParameter(pyParam); + // gather up all the modkeys for all the targets attached to this animation component + int j; + for ( j=0; jNumTargets(); j++ ) + { + pyParam.SetToAnimation(animcomp->GetModKey((plMaxNode*)(comp->GetTarget(j)))); + mod->AddParameter(pyParam); + } + } + } + } + break; + + case plAutoUIParam::kTypeBehavior: + { + // The Behavior attribute is One-Shots and Multi-stage behaviors + // For Python usage: we will only allow using behaviors that are + // attached to one position node. In other words, Python will only + // be used for special cases. + int count = param->GetCount(pb); + int number_of_real_targets_found = 0; + for (int i = 0; i < count; i++) + { + plComponentBase *comp = param->GetComponent(pb, i); + if (comp && comp->ClassID() == ONESHOTCLASS_ID ) + { + // gather up all the modkeys for all the targets attached to this animation component + int j; + for ( j=0; jNumTargets(); j++ ) + { + plKey behKey = OneShotComp::GetOneShotKey(comp,(plMaxNode*)(comp->GetTarget(j))); + if ( behKey != nil ) + { + // only get one real target, just count the rest + if ( number_of_real_targets_found == 0 ) + { + pyParam.SetToBehavior(behKey); + mod->AddParameter(pyParam); + } + number_of_real_targets_found += 1; + } + } + } + else if (comp && comp->ClassID() == MULTISTAGE_BEH_CID ) + { + // gather up all the modkeys for all the targets attached to this animation component + int j; + for ( j=0; jNumTargets(); j++ ) + { + plKey behKey = MultiStageBeh::GetMultiStageBehKey(comp,(plMaxNode*)(comp->GetTarget(j))); + if ( behKey != nil ) + { + // only get one real target, just count the rest + if ( number_of_real_targets_found == 0 ) + { + pyParam.SetToBehavior(behKey); + mod->AddParameter(pyParam); + } + number_of_real_targets_found += 1; + } + } + } + if ( number_of_real_targets_found != 1 ) + { + // there is zero or more than one node attached to this exclude region + char buf[512]; + if ( number_of_real_targets_found == 0 ) + sprintf(buf,"The Behavior component %s that was selected as an attribute in %s PythonFile, has no scene nodes attached.", + comp->GetINode()->GetName(),this->GetINode()->GetName()); + else + sprintf(buf,"The Behavior component %s that was selected as an attribute in %s PythonFile, has more than one scene node attached (using first one found).", + comp->GetINode()->GetName(),this->GetINode()->GetName()); + + pErrMsg->Set(true, "PythonFile Warning", buf).Show(); + pErrMsg->Set(false); + } + + } + } + break; + + case plAutoUIParam::kTypeMaterial: + { + int numKeys = param->GetCount(pb); + for (int i = 0; i < numKeys; i++) + { + plKey key = param->GetKey(pb, i); + // make sure we got a key and that it is a plMipmap + if (key && plMipmap::ConvertNoRef(key->GetObjectPtr()) ) + { + pyParam.SetToMaterial(key); + mod->AddParameter(pyParam); + } + } + } + break; + + case plAutoUIParam::kTypeMaterialAnimation: + { + plPickMaterialAnimationButtonParam* matAnim = (plPickMaterialAnimationButtonParam*)param; + matAnim->CreateKeyArray(pb); + + int numKeys = param->GetCount(pb); + for (int i = 0; i < numKeys; i++) + { + plKey key = param->GetKey(pb, i); + + if ( key ) + { + pyParam.SetToMaterialAnimation(key); + mod->AddParameter(pyParam); + } + } + matAnim->DestroyKeyArray(); + + } + break; + + case plAutoUIParam::kTypeDropDownList: + pyParam.SetToString(param->GetString(pb)); + mod->AddParameter(pyParam); + break; + + case plAutoUIParam::kTypeGrassComponent: + { + plComponentBase* comp = param->GetComponent(pb, 0); + plGrassShaderMod* shader = nil; + + if (comp) + { + shader = plGrassComponent::GetShader(comp->GetINode()); + + if (shader != nil) + { + plKey shaderKey = shader->GetKey(); + if ( shaderKey != nil ) + { + pyParam.SetToGrassShaderComponent(shaderKey); + mod->AddParameter(pyParam); + } + } + } + } + break; + + } + } + + return true; +} + +plPythonFileComponent::Validate plPythonFileComponent::ValidateFile() +{ + // Make sure the type is in the valid range + IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB); + plAutoUIBlock *block = FindAutoUI(pb); + if (!block || fCompPB->GetInt(kPythonVersion) > block->GetVersion()) + { + // Bad type, clear out the PB so this won't blow up during a save + fCompPB->SetValue(kPythonFilePB, 0, (ReferenceTarget*)nil); + + if (!block) + return kPythonNoFile; + else + return kPythonBadVer; + } + + // Got a newer version of the Python file, update our version + if (fCompPB->GetInt(kPythonVersion) < block->GetVersion()) + fCompPB->SetValue(kPythonVersion, 0, block->GetVersion()); + + if (block->GetVersion() == 0) + return kPythonNoVer; + + return kPythonOk; +} + +const char* plPythonFileComponent::GetPythonName() +{ + // Make sure the type is in the valid range + IParamBlock2 *pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kPythonFilePB); + plAutoUIBlock *block = FindAutoUI(pb); + if (block) + return block->GetName(); + + return "(unknown)"; +} + +//////////////////////////////////////////////////////////////////////////////// +// Verify that all Python File components in the scene are OK, and warn the user +// if any aren't +// + +class plPythonError +{ +public: + plMaxNode* node; + const char* pythonName; + plPythonFileComponent::Validate error; + + bool operator< (const plPythonError& rhs) const { return rhs.node < node; } +}; + +typedef std::set ErrorSet; + +static void CheckPythonFileCompsRecur(plMaxNode *node, ErrorSet& badNodes) +{ + plComponentBase *comp = node->ConvertToComponent(); + if (comp && comp->ClassID() == PYTHON_FILE_CID) + { + plPythonFileComponent* pyComp = (plPythonFileComponent*)comp; + + const char* pythonName = pyComp->GetPythonName(); + + plPythonFileComponent::Validate valid = pyComp->ValidateFile(); + if (valid != plPythonFileComponent::kPythonOk) + { + plPythonError err; + err.node = node; + err.pythonName = pythonName; + err.error = valid; + badNodes.insert(err); + } + } + + for (int i = 0; i < node->NumberOfChildren(); i++) + CheckPythonFileCompsRecur((plMaxNode*)node->GetChildNode(i), badNodes); +} + +static BOOL CALLBACK WarnDialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_INITDIALOG) + { + HWND hList = GetDlgItem(hDlg, IDC_COMP_LIST); + + LVCOLUMN lvc; + lvc.mask = LVCF_TEXT; + lvc.pszText = "Component"; + ListView_InsertColumn(hList, 0, &lvc); + lvc.pszText = "Python File"; + ListView_InsertColumn(hList, 1, &lvc); + lvc.pszText = "Error"; + ListView_InsertColumn(hList, 2, &lvc); + + ErrorSet *badNodes = (ErrorSet*)lParam; + ErrorSet::iterator it = badNodes->begin(); + for (; it != badNodes->end(); it++) + { + plPythonError& err = *it; + + plMaxNode* node = err.node; + plPythonFileComponent* comp = (plPythonFileComponent*)node->ConvertToComponent(); + + LVITEM lvi; + lvi.mask = LVIF_TEXT; + lvi.pszText = (char*)node->GetName(); + lvi.iItem = 0; + lvi.iSubItem = 0; + int idx = ListView_InsertItem(hList, &lvi); + + ListView_SetItemText(hList, idx, 1, (char*)err.pythonName); + + switch (err.error) + { + case plPythonFileComponent::Validate::kPythonBadVer: + ListView_SetItemText(hList, idx, 2, "Old Version"); + break; + case plPythonFileComponent::Validate::kPythonNoVer: + ListView_SetItemText(hList, idx, 2, "No Version"); + break; + case plPythonFileComponent::Validate::kPythonNoFile: + ListView_SetItemText(hList, idx, 2, "No File/Python Error"); + break; + } + } + + ListView_SetColumnWidth(hList, 0, LVSCW_AUTOSIZE); + ListView_SetColumnWidth(hList, 1, LVSCW_AUTOSIZE); + ListView_SetColumnWidth(hList, 2, LVSCW_AUTOSIZE); + + return TRUE; + } + else if (msg == WM_COMMAND) + { + if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) + { + DestroyWindow(hDlg); + return TRUE; + } + } + + return FALSE; +} + +static void WriteBadPythonText(ErrorSet& badNodes) +{ + ErrorSet::iterator it = badNodes.begin(); + for (; it != badNodes.end(); it++) + { + plPythonError& err = *it; + + const char* compName = err.node->GetName(); + const char* pythonFile = err.pythonName; + const char* errorText = ""; + switch (err.error) + { + case plPythonFileComponent::kPythonBadVer: + errorText = "Old Version"; + break; + case plPythonFileComponent::Validate::kPythonNoVer: + errorText = "No Version"; + break; + case plPythonFileComponent::Validate::kPythonNoFile: + errorText = "No File/Python Error"; + break; + } + + std::string& pythonError = plPythonFileComponent::fPythonError; + pythonError += "Python component "; + pythonError += compName; + pythonError += " (file "; + pythonError += pythonFile; + pythonError += ") is bad. Reason: "; + pythonError += errorText; + pythonError += "\n"; + } +} + +static void NotifyProc(void *param, NotifyInfo *info) +{ + static bool gotBadPython = false; + + if (info->intcode == NOTIFY_FILE_POST_OPEN) + { + ErrorSet badNodes; + CheckPythonFileCompsRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), badNodes); + + if (badNodes.size() > 0) + { + gotBadPython = true; + + if (hsMessageBox_SuppressPrompts) + WriteBadPythonText(badNodes); + else + CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_PYTHON_FILE_WARN), GetCOREInterface()->GetMAXHWnd(), WarnDialogProc, (LPARAM)&badNodes); + } + else + gotBadPython = false; + } + else if (info->intcode == NOTIFY_FILE_PRE_SAVE) + { + if (gotBadPython) + { + hsMessageBox("This file has bad Python components in it, you REALLY shouldn't save it.", + "Python Component Warning", + hsMBoxOk); + } + } + else if (info->intcode == NOTIFY_SYSTEM_POST_RESET || + info->intcode == NOTIFY_SYSTEM_POST_NEW) + { + gotBadPython = false; + } + // Have to do this at system shutdown 2 (really final shutdown) because it deletes the + // descriptor, which Max may still try to use in between shutdown 1 and 2. + else if (info->intcode == NOTIFY_SYSTEM_SHUTDOWN2) + { + int count = gAutoUIBlocks.size(); + for (int i = 0; i < count; i++) + { + delete gAutoUIBlocks[i]; + } + gAutoUIBlocks.clear(); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +class plPythonFileComponentProc : public ParamMap2UserDlgProc +{ +public: + plPythonFileComponentProc() : fAutoUI(nil) {} + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void DeleteThis() { DestroyAutoUI(); } + +protected: + plAutoUIBlock *fAutoUI; + + void CreateAutoUI(plAutoUIBlock *autoUI, IParamBlock2 *pb); + void DestroyAutoUI(); +}; +static plPythonFileComponentProc gPythonFileProc; + +ParamBlockDesc2 gPythonFileBlk +( + plComponent::kBlkComp, _T("PythonFile"), 0, &gPythonFileComponentDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + //rollout + IDD_COMP_PYTHON_FILE, IDS_COMP_PYTHON, 0, 0, &gPythonFileProc, + + kPythonFilePB, _T("pb"), TYPE_REFTARG, 0, 0, + end, + + kPythonVersion, _T("version"), TYPE_INT, 0, 0, + end, + + kPythonFileIsGlobal, _T("isGlobal"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_PYTHON_GLOBAL, + p_default, FALSE, + end, + + end +); + +#define WM_LOAD_AUTO_UI WM_APP+1 + +BOOL plPythonFileComponentProc::DlgProc(TimeValue t, IParamMap2 *pmap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = pmap->GetParamBlock(); + + HWND hCombo = GetDlgItem(hWnd, IDC_PYTHON_FILE); + ComboBox_ResetContent(hCombo); + + SetDlgItemText(hWnd, IDC_VER_TEXT, ""); + + IParamBlock2 *pythonPB = (IParamBlock2*)pb->GetReferenceTarget(kPythonFilePB); + plAutoUIBlock *pythonBlock = FindAutoUI(pythonPB); + + int numPythonFiles = gAutoUIBlocks.size(); + for (int i = 0; i < numPythonFiles; i++) + { + plAutoUIBlock *block = gAutoUIBlocks[i]; + const char *name = block->GetName(); + + int idx = ComboBox_AddString(hCombo, name); + ComboBox_SetItemData(hCombo, idx, i); + if (block == pythonBlock) + { + ComboBox_SetCurSel(hCombo, idx); + SetDlgItemInt(hWnd, IDC_VER_TEXT, block->GetVersion(), TRUE); + } + } + + // Crappy hack, see WM_LOAD_AUTO_UI + PostMessage(hWnd, WM_LOAD_AUTO_UI, 0, 0); + } + return TRUE; + + // Crappy hack. If we put up the python file UI before returning from WM_INITDIALOG + // it will show up ABOVE the main UI. To get around this we post a message that won't + // get processed until after the main UI is put up. + case WM_LOAD_AUTO_UI: + { + IParamBlock2 *pb = pmap->GetParamBlock(); + + IParamBlock2 *pythonPB = (IParamBlock2*)pb->GetReferenceTarget(kPythonFilePB); + plAutoUIBlock *pythonBlock = FindAutoUI(pythonPB); + + if (pythonBlock && pythonPB) + CreateAutoUI(pythonBlock, pythonPB); + } + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_PYTHON_FILE) + { + HWND hCombo = (HWND)lParam; + int sel = ComboBox_GetCurSel(hCombo); + + int type = ComboBox_GetItemData(hCombo, sel); + + plAutoUIBlock *block = gAutoUIBlocks[type]; + IParamBlock2 *autoPB = block->CreatePB(); + + CreateAutoUI(block, autoPB); + + IParamBlock2 *pb = pmap->GetParamBlock(); + pb->SetValue(kPythonFilePB, 0, (ReferenceTarget*)autoPB); + + SetDlgItemInt(hWnd, IDC_VER_TEXT, block->GetVersion(), TRUE); + + return TRUE; + } + break; + } + + return FALSE; +} + +void plPythonFileComponentProc::CreateAutoUI(plAutoUIBlock *autoUI, IParamBlock2 *pb) +{ + DestroyAutoUI(); + + if (autoUI && pb) + { + fAutoUI = autoUI; + autoUI->CreateAutoRollup(pb); + } +} + +void plPythonFileComponentProc::DestroyAutoUI() +{ + if (fAutoUI) + { + fAutoUI->DestroyAutoRollup(); + fAutoUI = nil; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPythonFileComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPythonFileComponent.h new file mode 100644 index 00000000..33cad69b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plPythonFileComponent.h @@ -0,0 +1,35 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +class plComponentClassDesc; +class plAutoUIBlock; + +#define PYTHON_FILE_CID Class_ID(0x670d3629, 0x559e4f11) + +namespace PythonFile +{ + plComponentClassDesc *GetClassDesc(); + void AddAutoUIBlock(plAutoUIBlock *block); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plRepComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plRepComponent.cpp new file mode 100644 index 00000000..cb066603 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plRepComponent.cpp @@ -0,0 +1,479 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "dummy.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "plMiscComponents.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxExport/plExportErrorMsg.h" + +#include "hsResMgr.h" + +#include "plLoadMask.h" + +#include "plPickNode.h" + + +void DummyCodeIncludeFuncRepComp() +{ +} + +const Class_ID REPCOMP_CID(0x157c29f3, 0x18fa54cd); +const Class_ID REPGROUP_CID(0x20ce74a2, 0x471b2386); + +static const int kNumQualities = 4; +static const char* kQualityStrings[kNumQualities] = { + "Low", + "Medium", + "High", + "Ultra" +}; + +class plRepresentComp : public plComponent +{ +public: + enum { + kQuality + }; +public: + plRepresentComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + int GetQuality(); + int GetCapability(); + + void SetLoadMask(const plLoadMask& m); + + static plRepresentComp* GetComp(INode* node); +}; + +#define WM_ROLLOUT_OPEN WM_USER+1 + +class plRepresentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + PostMessage(hWnd, WM_ROLLOUT_OPEN, 0, 0); + + HWND cbox = GetDlgItem(hWnd, IDC_COMP_REPRESENT_QUALITY); + int i; + for( i = 0; i < kNumQualities; i++ ) + { + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)kQualityStrings[i]); + } + SendMessage(cbox, CB_SETCURSEL, map->GetParamBlock()->GetInt(plRepresentComp::kQuality), 0); + + } + return true; + + case WM_COMMAND: + switch( LOWORD(wParam) ) + { + case IDC_COMP_REPRESENT_QUALITY: + map->GetParamBlock()->SetValue(plRepresentComp::kQuality, t, SendMessage(GetDlgItem(hWnd, LOWORD(wParam)), CB_GETCURSEL, 0, 0)); + return TRUE; + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plRepresentProc gRepresentProc; + + +CLASS_DESC(plRepresentComp, gRepresentDesc, "Representation", "Rep", COMP_TYPE_GRAPHICS, REPCOMP_CID) + +ParamBlockDesc2 gRepresentBk +( + plComponent::kBlkComp, _T("Represent"), 0, &gRepresentDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_REPRESENT, IDS_COMP_REPRESENT, 0, 0, &gRepresentProc, + + plRepresentComp::kQuality, _T("Quality"), TYPE_INT, 0, 0, + p_default, 0, + end, + + end +); + +plRepresentComp::plRepresentComp() +{ + fClassDesc = &gRepresentDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plRepresentComp::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plRepresentComp::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plRepresentComp::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plRepresentComp::DeInit(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +int plRepresentComp::GetQuality() +{ + return fCompPB->GetInt(kQuality); +} + +int plRepresentComp::GetCapability() +{ + int maxCap = 0; + int numTarg = NumTargets(); + int iTarg; + for( iTarg = 0; iTarg < numTarg; iTarg++ ) + { + plMaxNodeBase* node = GetTarget(iTarg); + if( node ) + { + const char* name = node->GetName(); + int numComp = node->NumAttachedComponents(); + int iComp; + for( iComp = 0; iComp < numComp; iComp++ ) + { + plComponentBase* comp = node->GetAttachedComponent(iComp); + if( comp ) + { + const char* compName = comp->GetINode()->GetName(); + int cap = comp->GetMinCap(); + if( cap > maxCap ) + maxCap = cap; + } + } + } + } + return maxCap; +} + +void plRepresentComp::SetLoadMask(const plLoadMask& m) +{ + int numTarg = NumTargets(); + int iTarg; + for( iTarg = 0; iTarg < numTarg; iTarg++ ) + { + plMaxNodeBase* node = GetTarget(iTarg); + if( node ) + { + const char* nodeName = node->GetName(); + node->AddLoadMask(m); + plLoadMask x = node->GetLoadMask(); + x |= m; + } + } +} + +plRepresentComp* plRepresentComp::GetComp(INode* node) +{ + if( node == nil ) + return nil; + + plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); + if( comp == nil ) + return nil; + + if( comp->ClassID() == REPCOMP_CID ) + return (plRepresentComp*) comp; + + return nil; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class plRepGroupComp : public plComponent +{ +public: + enum { + kReps + }; + + void IGetQC(int quals[], int caps[]); + + hsBool ComputeAndValidate(plErrorMsg* pErrMsg, int quals[], int caps[], plLoadMask masks[]); + + void CleanDeadNodes(); +public: + plRepGroupComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + hsBool Validate(plErrorMsg* pErrMsg); +}; + +void plRepGroupComp::CleanDeadNodes() +{ + int i = fCompPB->Count(kReps) - 1; + while( i >= 0 ) + { + if( !fCompPB->GetINode(kReps, TimeValue(0), i) ) + fCompPB->Delete(kReps, i, 1); + i--; + } +} + +class plRepGroupProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + } + return true; + + case WM_COMMAND: + if( HIWORD(wParam) == BN_CLICKED ) + { + switch( LOWORD(wParam) ) + { + case IDC_ADD_REPS: + { + std::vector cids; + cids.push_back(REPCOMP_CID); + IParamBlock2 *pb = map->GetParamBlock(); + plPick::Node(pb, plRepGroupComp::kReps, &cids, false, false); + + map->Invalidate(plRepGroupComp::kReps); + } + return TRUE; + case IDC_UP_REPS: + { + HWND hNode = GetDlgItem(hWnd, IDC_LIST_REPS); + int idx = ListBox_GetCurSel(hNode); + if( (idx != LB_ERR) && (idx > 0) ) + { + IParamBlock2 *pb = map->GetParamBlock(); + INode* node = pb->GetINode(plRepGroupComp::kReps, TimeValue(0), idx); + pb->Delete(plRepGroupComp::kReps, idx, 1); + if( node ) + pb->Insert(plRepGroupComp::kReps, idx-1, 1, &node); + ListBox_SetCurSel(hNode, idx-1); + + map->Invalidate(plRepGroupComp::kReps); + } + } + return TRUE; + case IDC_DOWN_REPS: + { + HWND hNode = GetDlgItem(hWnd, IDC_LIST_REPS); + IParamBlock2 *pb = map->GetParamBlock(); + int idx = ListBox_GetCurSel(hNode); + if( (idx != LB_ERR) && (idx < pb->Count(plRepGroupComp::kReps)-1) ) + { + INode* node = pb->GetINode(plRepGroupComp::kReps, TimeValue(0), idx); + pb->Delete(plRepGroupComp::kReps, idx, 1); + if( node ) + pb->Insert(plRepGroupComp::kReps, idx+1, 1, &node); + ListBox_SetCurSel(hNode, idx+1); + + map->Invalidate(plRepGroupComp::kReps); + } + } + return TRUE; + case IDC_VAL_REPS: + { + plRepGroupComp* repGroup = (plRepGroupComp*)map->GetParamBlock()->GetOwner(); + plExportErrorMsg errMsg; + repGroup->Validate(&errMsg); + } + return TRUE; + } + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plRepGroupProc gRepGroupProc; + +CLASS_DESC(plRepGroupComp, gRepGroupDesc, "Representation Group", "RepGroup", COMP_TYPE_GRAPHICS, REPGROUP_CID) + +ParamBlockDesc2 gRepGroupBk +( + plComponent::kBlkComp, _T("RepGroup"), 0, &gRepGroupDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_REPGROUP, IDS_COMP_REPGROUP, 0, 0, &gRepGroupProc, + + plRepGroupComp::kReps, _T("Reps"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_REPS, 0, 0, IDC_DEL_REPS, + end, + + end +); + +plRepGroupComp::plRepGroupComp() +{ + fClassDesc = &gRepGroupDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +void plRepGroupComp::IGetQC(int quals[], int caps[]) +{ + const int numReps = fCompPB->Count(kReps); + int i; + for( i = 0; i < numReps; i++ ) + { + plRepresentComp* rep = plRepresentComp::GetComp(fCompPB->GetINode(kReps, TimeValue(0), i)); + quals[i] = rep->GetQuality(); + caps[i] = rep->GetCapability(); + } +} +hsBool plRepGroupComp::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + const int numReps = fCompPB->Count(kReps); + hsTArray quals(numReps); + hsTArray caps(numReps); + hsTArray masks(numReps); + + IGetQC(quals.AcquireArray(), caps.AcquireArray()); + + ComputeAndValidate(pErrMsg, quals.AcquireArray(), caps.AcquireArray(), masks.AcquireArray()); + + int i; + for( i = 0; i < numReps; i++ ) + { + plRepresentComp* rep = plRepresentComp::GetComp(fCompPB->GetINode(kReps, TimeValue(0), i)); + rep->SetLoadMask(masks[i]); + } + + return true; +} + +hsBool plRepGroupComp::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plRepGroupComp::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plRepGroupComp::DeInit(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plRepGroupComp::ComputeAndValidate(plErrorMsg* pErrMsg, int quals[], int caps[], plLoadMask masks[]) +{ + const int numReps = fCompPB->Count(kReps); + UInt32 preVal = plLoadMask::ValidateReps(numReps, quals, caps); + + if( preVal ) + { + int i; + for( i = 0; i < 32; i++ ) + { + if( preVal & (1 << i) ) + { + char buff[256]; + INode* rep = fCompPB->GetINode(kReps, TimeValue(0), i); + sprintf(buff, "Rep %d - %s is obscured by an earlier representation in preVal", i, rep ? rep->GetName() : "Unknown"); + pErrMsg->Set(true, GetINode()->GetName(), buff).Show(); + pErrMsg->Set(false); + } + } + } + + hsBool val = plLoadMask::ComputeRepMasks(numReps, quals, caps, masks); + + UInt32 postVal = plLoadMask::ValidateMasks(numReps, masks); + + if( postVal ) + { + int i; + for( i = 0; i < 32; i++ ) + { + if( !(preVal & (1 << i)) && (postVal & (1 << i)) ) + { + char buff[256]; + INode* rep = fCompPB->GetINode(kReps, TimeValue(0), i); + sprintf(buff, "Rep %d - %s is obscured by an earlier representation in postVal", i, rep ? rep->GetName() : "Unknown"); + pErrMsg->Set(true, GetINode()->GetName(), buff).Show(); + pErrMsg->Set(false); + } + } + } + + return !(preVal || val || postVal); +} + +hsBool plRepGroupComp::Validate(plErrorMsg* pErrMsg) +{ + CleanDeadNodes(); + + const int numReps = fCompPB->Count(kReps); + hsTArray quals(numReps); + hsTArray caps(numReps); + hsTArray masks(numReps); + + IGetQC(quals.AcquireArray(), caps.AcquireArray()); + + return ComputeAndValidate(pErrMsg, quals.AcquireArray(), caps.AcquireArray(), masks.AcquireArray()); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderAnim.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderAnim.cpp new file mode 100644 index 00000000..a2e8fb2d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderAnim.cpp @@ -0,0 +1,774 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plResponderAnim.h" +#include "plResponderComponentPriv.h" +#include "resource.h" +#include "max.h" + +#include "../MaxMain/plMaxNode.h" + +#include "plAnimComponent.h" +#include "plAudioComponents.h" + +#include "plMaxAnimUtils.h" +#include + +// Needed for anim msg creation +#include "../pnKeyedObject/plKey.h" +#include "../plMessage/plAnimCmdMsg.h" +#include "plNotetrackAnim.h" + +// Needed for sound msg creation +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "plAudible.h" +#include "../pnMessage/plSoundMsg.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" + +enum +{ + kAnimComp, + kAnimLoop, + kAnimType, + kAnimOwner, + kAnimObject, + kAnimObjectType, +}; + +class plResponderAnimProc; +extern plResponderAnimProc gResponderAnimProc; + +ParamBlockDesc2 gResponderAnimBlock +( + kResponderAnimBlk, _T("animCmd"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_ANIM, IDS_COMP_CMD_PARAMS, 0, 0, &gResponderAnimProc, + + kAnimComp, _T("comp"), TYPE_REFTARG, 0, 0, + end, + + kAnimObject, _T("object"), TYPE_REFTARG, 0, 0, + end, + + kAnimLoop, _T("loop"), TYPE_STRING, 0, 0, + end, + + kAnimType, _T("type"), TYPE_INT, 0, 0, + end, + + kAnimObjectType, _T("objType"), TYPE_INT, 0, 0, + end, + + end +); + +enum AnimObjectType +{ + kNodePB, // Use the node in the PB + kNodeResponder // Use the node the responder is attached to +}; + +plResponderCmdAnim& plResponderCmdAnim::Instance() +{ + static plResponderCmdAnim theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdAnim::GetDesc() +{ + return &gResponderAnimBlock; +} + +// Use the old types, for backwards compatibility +enum +{ + kRespondPlayAnim, + kRespondStopAnim, + kRespondToggleAnim, + kRespondLoopAnimOn, + kRespondLoopAnimOff, + kRespondSetForeAnim=7, + kRespondSetBackAnim, + + kRespondPlaySound, + kRespondStopSound, + kRespondToggleSound, + kRespondSyncedPlaySound, + + kRespondPlayRndSound=19, + kRespondStopRndSound, + kRespondToggleRndSound, + + kRespondRewindAnim, + kRespondRewindSound, + kRespondFastForwardAnim, + + kNumTypes = 16 +}; + +int plResponderCmdAnim::NumTypes() +{ + return kNumTypes; +} + +static int IndexToOldType(int idx) +{ + static int oldTypes[] = + { + kRespondPlayAnim, + kRespondStopAnim, + kRespondToggleAnim, + kRespondLoopAnimOn, + kRespondLoopAnimOff, + kRespondSetForeAnim, + kRespondSetBackAnim, + + kRespondPlaySound, + kRespondStopSound, + kRespondToggleSound, + kRespondSyncedPlaySound, + + kRespondPlayRndSound, + kRespondStopRndSound, + kRespondToggleRndSound, + + kRespondRewindAnim, + kRespondRewindSound, + kRespondFastForwardAnim, + }; + + hsAssert(idx < kNumTypes, "Bad index"); + return oldTypes[idx]; +} + +const char *plResponderCmdAnim::GetCategory(int idx) +{ + int type = IndexToOldType(idx); + + switch (type) + { + case kRespondPlayAnim: + case kRespondStopAnim: + case kRespondToggleAnim: + case kRespondLoopAnimOn: + case kRespondLoopAnimOff: + case kRespondSetForeAnim: + case kRespondSetBackAnim: + case kRespondRewindAnim: + case kRespondFastForwardAnim: + return "Animation"; + + case kRespondPlaySound: + case kRespondStopSound: + case kRespondToggleSound: + case kRespondRewindSound: + case kRespondSyncedPlaySound: + return "Sound"; + + case kRespondPlayRndSound: + case kRespondStopRndSound: + case kRespondToggleRndSound: + return "Random Sound"; + } + + return nil; +} + +const char *plResponderCmdAnim::GetName(int idx) +{ + int type = IndexToOldType(idx); + + switch (type) + { + case kRespondPlayAnim: + case kRespondPlaySound: + case kRespondPlayRndSound: + return "Play"; + + case kRespondStopAnim: + case kRespondStopSound: + case kRespondStopRndSound: + return "Stop"; + + case kRespondToggleAnim: + case kRespondToggleSound: + case kRespondToggleRndSound: + return "Toggle"; + + case kRespondLoopAnimOn: + return "Set Looping On"; + case kRespondLoopAnimOff: + return "Set Looping Off"; + case kRespondSetForeAnim: + return "Set Forwards"; + case kRespondSetBackAnim: + return "Set Backwards"; + + case kRespondRewindAnim: + case kRespondRewindSound: + return "Rewind"; + case kRespondFastForwardAnim: + return "Fast Forward"; + case kRespondSyncedPlaySound: + return "Synched Play"; + } + + return nil; +} + +static const char *GetShortName(int type) +{ + switch (type) + { + case kRespondPlayAnim: return "Anim Play"; + case kRespondStopAnim: return "Anim Stop"; + case kRespondToggleAnim: return "Anim Toggle"; + case kRespondLoopAnimOn: return "Anim Loop On"; + case kRespondLoopAnimOff: return "Anim Loop Off"; + case kRespondSetForeAnim: return "Anim Set Fore"; + case kRespondSetBackAnim: return "Anim Set Back"; + case kRespondPlaySound: return "Snd Play"; + case kRespondSyncedPlaySound: return "Snd Synched Play"; + case kRespondStopSound: return "Snd Stop"; + case kRespondToggleSound: return "Snd Toggle"; + case kRespondPlayRndSound: return "Rnd Snd Play"; + case kRespondStopRndSound: return "Rnd Snd Stop"; + case kRespondToggleRndSound: return "Rnd Snd Toggle"; + case kRespondRewindAnim: return "Anim Rewind"; + case kRespondRewindSound: return "Snd Rewind"; + case kRespondFastForwardAnim: return "Anim FFwd"; + } + + return nil; +} +const char *plResponderCmdAnim::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + const char *shortName = GetShortName(pb->GetInt(kAnimType)); + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(kAnimComp); + sprintf(name, "%s (%s)", shortName, node ? node->GetName() : "none"); + + return name; +} + +static bool IsSoundMsg(int type) +{ + if (type == kRespondPlaySound || + type == kRespondSyncedPlaySound || + type == kRespondStopSound || + type == kRespondToggleSound || + type == kRespondPlayRndSound || + type == kRespondStopRndSound || + type == kRespondToggleRndSound || + type == kRespondRewindSound) + return true; + return false; +} + +IParamBlock2 *plResponderCmdAnim::CreatePB(int idx) +{ + int type = IndexToOldType(idx); + + // Create the paramblock and save it's type + IParamBlock2 *pb = CreateParameterBlock2(&gResponderAnimBlock, nil); + pb->SetValue(kAnimType, 0, type); + + return pb; +} + +plComponentBase *plResponderCmdAnim::GetComponent(IParamBlock2 *pb) +{ + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(kAnimComp); + if (node) + return node->ConvertToComponent(); + else + return nil; +} + +plMessage *plResponderCmdAnim::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + if (IsSoundMsg(pb->GetInt(kAnimType))) + return ICreateSndMsg(node, pErrMsg, pb); + else + return ICreateAnimMsg(node, pErrMsg, pb); +} + +bool GetCompAndNode(IParamBlock2* pb, plMaxNode* node, plComponentBase*& comp, plMaxNode*& targNode) +{ + plMaxNode *compNode = (plMaxNode*)pb->GetReferenceTarget(kAnimComp); + if (!compNode) + return false; + + comp = compNode->ConvertToComponent(); + + // KLUDGE: Anim group components don't need target nodes + if (comp->ClassID() == ANIM_GROUP_COMP_CID) + return true; + + if (pb->GetInt(kAnimObjectType) == kNodeResponder) + targNode = node; + else + targNode = (plMaxNode*)pb->GetReferenceTarget(kAnimObject); + + if (!targNode) + return false; + + if (!comp->IsTarget(targNode)) + return false; + + return true; +} + +plMessage *plResponderCmdAnim::ICreateAnimMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plAnimComponentBase *comp = nil; + plMaxNode *targNode = nil; + if (!GetCompAndNode(pb, node, (plComponentBase*&)comp, targNode)) + throw "A valid animation component and node were not found"; + + // Get the anim modifier keys for all nodes this comp is attached to + plKey animKey = comp->GetModKey(targNode); + if (!animKey) + throw "Animation component didn't convert"; + + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg; + msg->AddReceiver(animKey); + + const char *tempAnimName = comp->GetAnimName(); + msg->SetAnimName(tempAnimName); + + // Create and initialize a message for the command + switch (pb->GetInt(kAnimType)) + { + case kRespondPlayAnim: + msg->SetCmd(plAnimCmdMsg::kContinue); + break; + case kRespondStopAnim: + msg->SetCmd(plAnimCmdMsg::kStop); + break; + case kRespondToggleAnim: + msg->SetCmd(plAnimCmdMsg::kToggleState); + break; + case kRespondSetForeAnim: + msg->SetCmd(plAnimCmdMsg::kSetForewards); + break; + case kRespondSetBackAnim: + msg->SetCmd(plAnimCmdMsg::kSetBackwards); + break; + case kRespondLoopAnimOn: + { + msg->SetCmd(plAnimCmdMsg::kSetLooping); + // KLUDGE - We send the loop to play by name here, so anim grouped components + // could have loops with different begin and end points. However, apparently + // that functionality was never implemented, whoops. So, we'll take out the + // stuff that actually tries to set the begin and end points for now, so that + // anims with a loop set in advance will actually work with this. -Colin +// msg->SetCmd(plAnimCmdMsg::kSetLoopBegin); +// msg->SetCmd(plAnimCmdMsg::kSetLoopEnd); + const char *loopName = pb->GetStr(kAnimLoop); + msg->SetLoopName(loopName); + } + break; + case kRespondLoopAnimOff: + msg->SetCmd(plAnimCmdMsg::kUnSetLooping); + break; + + case kRespondRewindAnim: + msg->SetCmd(plAnimCmdMsg::kGoToBegin); + break; + case kRespondFastForwardAnim: + msg->SetCmd(plAnimCmdMsg::kGoToEnd); + break; + } + + return msg; +} + +plMessage* plResponderCmdAnim::ICreateSndMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plComponentBase *comp; + plMaxNode *targNode; + if (!GetCompAndNode(pb, node, comp, targNode)) + throw "A valid sound component and node were not found"; + + int type = pb->GetInt(kAnimType); + switch (type) + { + case kRespondPlaySound: + case kRespondStopSound: + case kRespondToggleSound: + case kRespondRewindSound: + case kRespondSyncedPlaySound: + { + if (!targNode->GetSceneObject()) + throw "Sound emitter didn't export"; + + int soundIdx = plAudioComp::GetSoundModIdx(comp, targNode); + + // Changed 8.26.2001 mcn - The audioInterface should be the message receiver, + // not the audible itself. + const plAudioInterface *ai = targNode->GetSceneObject()->GetAudioInterface(); + plKey key = ai->GetKey(); + + plSoundMsg* msg = TRACKED_NEW plSoundMsg; + msg->AddReceiver(key); + msg->fIndex = soundIdx; + + if (type == kRespondPlaySound) + msg->SetCmd(plSoundMsg::kPlay); + else if (type == kRespondStopSound) + msg->SetCmd(plSoundMsg::kStop); + else if (type == kRespondToggleSound) + msg->SetCmd(plSoundMsg::kToggleState); + else if (type == kRespondRewindSound) + { + msg->fTime = 0; + msg->SetCmd(plSoundMsg::kGoToTime); + } + else if(type == kRespondSyncedPlaySound) + msg->SetCmd(plSoundMsg::kSynchedPlay); + + if( plAudioComp::IsLocalOnly( comp ) ) + msg->SetCmd( plSoundMsg::kIsLocalOnly ); + + return msg; + } + + case kRespondPlayRndSound: + case kRespondStopRndSound: + case kRespondToggleRndSound: + { + plKey key = plAudioComp::GetRandomSoundKey(comp, targNode); + if (key) + { + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg; + msg->AddReceiver(key); + + if (type == kRespondPlayRndSound) + msg->SetCmd(plAnimCmdMsg::kContinue); + else if (type == kRespondStopRndSound) + msg->SetCmd(plAnimCmdMsg::kStop); + else if (type == kRespondToggleRndSound) + msg->SetCmd(plAnimCmdMsg::kToggleState); + + return msg; + } + } + } + + throw "Unknown sound command"; +} + +bool plResponderCmdAnim::IsWaitable(IParamBlock2 *pb) +{ + int type = pb->GetInt(kAnimType); + if (type == kRespondPlayAnim || + type == kRespondToggleAnim || + type == kRespondStopAnim || + type == kRespondPlaySound || + type == kRespondSyncedPlaySound || + type == kRespondToggleSound) + return true; + + return false; +} + +void plResponderCmdAnim::GetWaitPoints(IParamBlock2 *pb, WaitPoints& waitPoints) +{ + int type = pb->GetInt(kAnimType); + + // Don't try and get points for the stop anim, it can only stop at a stop point + if (type == kRespondStopAnim || IsSoundMsg(type)) + return; + + plAnimComponent *animComp = (plAnimComponent*)GetComponent(pb); + const char *animName = animComp->GetAnimName(); + + if (animComp) + { + plNotetrackAnim notetrackAnim(animComp, nil); + plAnimInfo info = notetrackAnim.GetAnimInfo(animName); + while (const char *marker = info.GetNextMarkerName()) + waitPoints.push_back(marker); + } +} + +void plResponderCmdAnim::CreateWait(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2 *pb, ResponderWaitInfo& waitInfo) +{ + plAnimCmdMsg *animMsg = plAnimCmdMsg::ConvertNoRef(waitInfo.msg); + if (animMsg) + animMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); + + plSoundMsg *soundMsg = plSoundMsg::ConvertNoRef(waitInfo.msg); + if (soundMsg) + soundMsg->SetCmd(plSoundMsg::kAddCallbacks); + + plEventCallbackMsg *eventMsg = TRACKED_NEW plEventCallbackMsg; + eventMsg->AddReceiver(waitInfo.receiver); + eventMsg->fRepeats = 0; + eventMsg->fUser = waitInfo.callbackUser; + + if (waitInfo.point) + { + // FIXME COLIN - Error checking here? + plAnimComponent *animComp = (plAnimComponent*)GetComponent(pb); + const char *animName = animComp->GetAnimName(); + + plNotetrackAnim notetrackAnim(animComp, nil); + plAnimInfo info = notetrackAnim.GetAnimInfo(animName); + + eventMsg->fEvent = kTime; + eventMsg->fEventTime = info.GetMarkerTime(waitInfo.point); + } + else + eventMsg->fEvent = kStop; + + plMessageWithCallbacks *callbackMsg = plMessageWithCallbacks::ConvertNoRef(waitInfo.msg); + callbackMsg->AddCallback(eventMsg); + // AddCallback adds it's own ref, so remove ours (the default of 1) + hsRefCnt_SafeUnRef(eventMsg); +} + +#include "plAnimCompProc.h" +#include "plPickNode.h" +#include "plResponderGetComp.h" + +class plResponderAnimProc : public plAnimCompProc +{ +public: + plResponderAnimProc(); + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +protected: + virtual void IPickComponent(IParamBlock2* pb); + virtual void IPickNode(IParamBlock2* pb, plComponentBase* comp); + + virtual void ILoadUser(HWND hWnd, IParamBlock2* pb); + virtual bool IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID); + + virtual void IUpdateNodeButton(HWND hWnd, IParamBlock2* pb); +}; +static plResponderAnimProc gResponderAnimProc; + +plResponderAnimProc::plResponderAnimProc() +{ + fCompButtonID = IDC_ANIM_BUTTON; + fCompParamID = kAnimComp; + fNodeButtonID = IDC_OBJ_BUTTON; + fNodeParamID = kAnimObject; +} + +BOOL plResponderAnimProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = pm->GetParamBlock(); + + int type = pb->GetInt(kAnimType); + + // Only show the loop control if this is a loop command + int show = (type == kRespondLoopAnimOn) ? SW_SHOW : SW_HIDE; + ShowWindow(GetDlgItem(hWnd, IDC_LOOP_COMBO), show); + ShowWindow(GetDlgItem(hWnd, IDC_LOOP_TEXT), show); + // Resize the dialog if we're not using the loop control + if (type != kRespondLoopAnimOn) + { + RECT itemRect, clientRect; + GetWindowRect(GetDlgItem(hWnd, IDC_LOOP_TEXT), &itemRect); + GetWindowRect(hWnd, &clientRect); + SetWindowPos(hWnd, NULL, 0, 0, clientRect.right-clientRect.left, + itemRect.top-clientRect.top, SWP_NOMOVE | SWP_NOZORDER); + } + + if (IsSoundMsg(type)) + SetDlgItemText(hWnd, IDC_COMP_TEXT, "Sound Component"); + } + break; + } + + return plAnimCompProc::DlgProc(t, pm, hWnd, msg, wParam, lParam); +} + +bool plResponderAnimProc::IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID) +{ + if (cmd == CBN_SELCHANGE && resID == IDC_LOOP_COMBO) + { + HWND hCombo = GetDlgItem(hWnd, IDC_LOOP_COMBO); + int sel = ComboBox_GetCurSel(hCombo); + + // If this is an actual loop (not the entire animation) get its name and save it + if (sel != CB_ERR) + { + if (ComboBox_GetItemData(hCombo, sel) == 1) + { + char buf[256]; + ComboBox_GetText(hCombo, buf, sizeof(buf)); + pb->SetValue(kAnimLoop, 0, buf); + } + else + pb->SetValue(kAnimLoop, 0, ""); + } + + return true; + } + + return false; +} + +void plResponderAnimProc::IPickComponent(IParamBlock2* pb) +{ + std::vector cids; + + int type = pb->GetInt(kAnimType); + if (type == kRespondPlaySound || + type == kRespondStopSound || + type == kRespondToggleSound || + type == kRespondRewindSound || + type == kRespondSyncedPlaySound) + { + cids.push_back(SOUND_3D_COMPONENT_ID); + cids.push_back(BGND_MUSIC_COMPONENT_ID); + cids.push_back(GUI_SOUND_COMPONENT_ID); + } + else if (type == kRespondPlayRndSound || + type == kRespondStopRndSound || + type == kRespondToggleRndSound) + { + cids.push_back(RANDOM_SOUND_COMPONENT_ID); + } + else + { + cids.push_back(ANIM_COMP_CID); + cids.push_back(ANIM_GROUP_COMP_CID); + } + + plPick::NodeRefKludge(pb, kAnimComp, &cids, true, false); +} + +#include "plPickNodeBase.h" + +static const char* kResponderNodeName = "(Responder Node)"; + +class plPickRespNode : public plPickCompNode +{ +protected: + int fTypeID; + + void IAddUserType(HWND hList) + { + int idx = ListBox_AddString(hList, kResponderNodeName); + + int type = fPB->GetInt(fTypeID); + if (type == kNodeResponder) + ListBox_SetCurSel(hList, idx); + } + + void ISetUserType(plMaxNode* node, const char* userType) + { + if (userType && !strcmp(userType, kResponderNodeName)) + { + ISetNodeValue(nil); + fPB->SetValue(fTypeID, 0, kNodeResponder); + } + else + fPB->SetValue(fTypeID, 0, kNodePB); + } + +public: + plPickRespNode(IParamBlock2* pb, int nodeParamID, int typeID, plComponentBase* comp) : + plPickCompNode(pb, nodeParamID, comp), fTypeID(typeID) + { + } +}; + +void plResponderAnimProc::IPickNode(IParamBlock2* pb, plComponentBase* comp) +{ + plPickRespNode pick(pb, kAnimObject, kAnimObjectType, comp); + pick.DoPick(); +} + +#include "plNotetrackAnim.h" + +void plResponderAnimProc::ILoadUser(HWND hWnd, IParamBlock2 *pb) +{ + // Premptive strike. If this isn't a loop, don't bother! + int type = pb->GetInt(kAnimType); + if (type != kRespondLoopAnimOn) + return; + + HWND hLoop = GetDlgItem(hWnd, IDC_LOOP_COMBO); + + const char *savedName = pb->GetStr(kAnimLoop); + if (!savedName) + savedName = ""; + + // Reset the combo and add the default selection + ComboBox_ResetContent(hLoop); + int sel = ComboBox_AddString(hLoop, ENTIRE_ANIMATION_NAME); + ComboBox_SetCurSel(hLoop, sel); + + // FIXME + plComponentBase *comp = plResponderCmdAnim::Instance().GetComponent(pb); + if (comp && comp->ClassID() == ANIM_COMP_CID) + { + const char *animName = ((plAnimComponent*)comp)->GetAnimName(); + + // Get the shared animations for all the nodes this component is applied to + plNotetrackAnim anim(comp, nil); + plAnimInfo info = anim.GetAnimInfo(animName); + // Get all the loops in this animation + while (const char *loopName = info.GetNextLoopName()) + { + int idx = ComboBox_AddString(hLoop, loopName); + ComboBox_SetItemData(hLoop, idx, 1); + + if (!strcmp(loopName, savedName)) + ComboBox_SetCurSel(hLoop, idx); + } + + EnableWindow(hLoop, TRUE); + } + else + { + EnableWindow(hLoop, FALSE); + } +} + +void plResponderAnimProc::IUpdateNodeButton(HWND hWnd, IParamBlock2* pb) +{ + if (pb->GetInt(kAnimObjectType) == kNodeResponder) + { + HWND hButton = GetDlgItem(hWnd, IDC_OBJ_BUTTON); + SetWindowText(hButton, kResponderNodeName); + } + else + plAnimCompProc::IUpdateNodeButton(hWnd, pb); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderAnim.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderAnim.h new file mode 100644 index 00000000..2b9c53cd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderAnim.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plResponderCmd.h" +#include "../pnKeyedObject/plKey.h" + +class plComponentBase; + +class plResponderCmdAnim : public plResponderCmd +{ +public: + static plResponderCmdAnim& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes(); + virtual const char *GetCategory(int idx); + virtual const char *GetName(int idx); + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual IParamBlock2 *CreatePB(int idx); + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2* pb); + + virtual bool IsWaitable(IParamBlock2 *pb); + virtual void GetWaitPoints(IParamBlock2 *pb, WaitPoints& waitPoints); + virtual void CreateWait(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2 *pb, ResponderWaitInfo& waitInfo); + + plComponentBase *GetComponent(IParamBlock2 *pb); + +protected: + plMessage *ICreateAnimMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); + plMessage *ICreateSndMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderCmd.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderCmd.h new file mode 100644 index 00000000..32e80e35 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderCmd.h @@ -0,0 +1,78 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plResponderCmd_h_inc +#define plResponderCmd_h_inc + +#include "hsConfig.h" +#include +#include +#include "../pnKeyedObject/plKey.h" + +class ParamBlockDesc2; +class IParamBlock2; +class plMessage; +class plErrorMsg; +class plMaxNode; + +class ResponderWaitInfo +{ +public: + const char* responderName; // For error messages + + plMessage *msg; // Message created by the responder command + plKey receiver; // Key to send the callback message to + const char *point; // Marker name to wait on (nil for end) + int callbackUser; // Value to set the callback user value to +}; + +class plResponderCmd +{ +public: + static plResponderCmd *Find(IParamBlock2 *pb); + + virtual ParamBlockDesc2 *GetDesc()=0; + + virtual int NumTypes()=0; + virtual const char *GetCategory(int idx)=0; + virtual const char *GetName(int idx)=0; + virtual const char *GetInstanceName(IParamBlock2 *pb)=0; + + virtual IParamBlock2 *CreatePB(int idx); + + // In case the command needs to set any properties on a node + virtual void SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2* pb) {} + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2* pb)=0; + + typedef std::vector WaitPoints; + // Can other commands wait on you? + virtual bool IsWaitable(IParamBlock2 *pb) { return false; } + // The names of the points commands can wait on (or leave empty for only 'end') + virtual void GetWaitPoints(IParamBlock2 *pb, WaitPoints& waitPoints) {} + // Take your message and modify it to send a callback to 'receiver', with fUser set to callbackUser + virtual void CreateWait(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2 *pb, ResponderWaitInfo& waitInfo) {} +}; + +#endif // plResponderCmd_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponent.cpp new file mode 100644 index 00000000..86dff535 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponent.cpp @@ -0,0 +1,1328 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plResponderComponentPriv.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnNetCommon/plSDLTypes.h" + +#include "../plModifier/plResponderModifier.h" +#include "../plModifier/plLogicModifier.h" +#include "../plModifier/plAxisAnimModifier.h" +#include "../pfConditional/plActivatorConditionalObject.h" +#include "../pfConditional/plORConditionalObject.h" + +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNotifyMsg.h" + +#include "hsResMgr.h" +#include "../MaxMain/plMaxNode.h" + +#include "plPickNode.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "plResponderLink.h" +#include "plResponderAnim.h" +#include "plResponderMtl.h" +#include "plResponderWait.h" + +#include "../MaxMain/plMaxAccelerators.h" + +IParamBlock2 *CreateWaitBlk(); + +int ResponderGetActivatorCount(plComponentBase *comp) +{ + if (comp->ClassID() == RESPONDER_CID) + return comp->GetParamBlockByID(plComponentBase::kBlkComp)->Count(kResponderActivators); + + return -1; +} + +plComponentBase *ResponderGetActivator(plComponentBase *comp, int idx) +{ + if (comp->ClassID() == RESPONDER_CID) + { + IParamBlock2 *pb = comp->GetParamBlockByID(plComponentBase::kBlkComp); + plMaxNode *activatorNode = (plMaxNode*)pb->GetINode(kResponderActivators, 0, idx); + return activatorNode->ConvertToComponent(); + } + + return nil; +} + +plKey Responder::GetKey(plComponentBase *comp, plMaxNodeBase *node) +{ + if (comp->ClassID() != RESPONDER_CID) + return nil; + + plResponderComponent *responder = (plResponderComponent*)comp; + if (responder->fModKeys.find((plMaxNode*)node) != responder->fModKeys.end()) + return responder->fModKeys[(plMaxNode*)node]; + + return nil; +} + +CLASS_DESC(plResponderComponent, gResponderDesc, "Responder", "Responder", COMP_TYPE_LOGIC, RESPONDER_CID) + +class plResponderProc; +extern plResponderProc gResponderComponentProc; + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plResponderAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (id == kResponderActivators || id == kResponderState) + { + plResponderComponent *comp = (plResponderComponent*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +static plResponderAccessor gResponderAccessor; + +ParamBlockDesc2 gResponderBlock +( + plComponent::kBlkComp, _T("responderComp"), 0, &gResponderDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_RESPOND, IDS_COMP_RESPOND, 0, 0, &gResponderComponentProc, + + kResponderState, _T("state"), TYPE_REFTARG_TAB, 0, 0, 0, + p_accessor, &gResponderAccessor, + end, + kResponderStateName, _T("stateName"), TYPE_STRING_TAB, 0, 0, 0, + end, + + kResponderActivators, _T("activators"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + p_accessor, &gResponderAccessor, + end, + + kResponderStateDef, _T("defState"), TYPE_INT, 0, 0, + end, + + kResponderEnabled, _T("enabled"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_ENABLED, + end, + + kResponderTrigger, _T("trigger"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK_TRIGGER, + end, + + kResponderUnTrigger, _T("unTrigger"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK_UNTRIGGER, + end, + + kResponderLocalDetect, _T("localDetect"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_DETECT_LOCAL_CHECK, + end, + + kResponderSkipFFSound, _T("skipFFSound"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK_SKIPFFSOUND, + end, + + end +); + +ParamBlockDesc2 gStateBlock +( + kResponderStateBlk, _T("responderState"), 0, &gResponderDesc, 0, + + kStateCmdParams, _T("cmdParam"), TYPE_REFTARG_TAB, 0, 0, 0, + end, + + kStateCmdWait, _T("cmdWait"), TYPE_REFTARG_TAB, 0, 0, 0, + end, + + kStateCmdSwitch, _T("cmdSwitch"), TYPE_INT, 0, 0, + end, + + kStateCmdEnabled, _T("enabled"), TYPE_BOOL_TAB, 0, 0, 0, + p_default, TRUE, + end, + + end +); + +std::vector gResponderCmds; + +plResponderCmd *plResponderCmd::Find(IParamBlock2 *pb) +{ + if (!pb) + return nil; + + ParamBlockDesc2 *pbDesc = pb->GetDesc(); + + for (int i = 0; i < gResponderCmds.size(); i++) + { + if (gResponderCmds[i]->GetDesc() == pbDesc) + return gResponderCmds[i]; + } + + return nil; +} + +IParamBlock2* plResponderCmd::CreatePB(int idx) +{ + hsAssert(NumTypes() == 1, "Can't auto-create the pb for a cmd with multiple types"); + IParamBlock2 *pb = CreateParameterBlock2(GetDesc(), nil); + return pb; +} + +void DummyCodeIncludeFuncResponder() +{ + gResponderCmds.push_back(&(plResponderCmdVisibility::Instance())); + gResponderCmds.push_back(&(plResponderCmdLink::Instance())); + gResponderCmds.push_back(&(plResponderCmdEnable::Instance())); + gResponderCmds.push_back(&(plResponderCmdXRegion::Instance())); + gResponderCmds.push_back(&(plResponderCmdOneShot::Instance())); + gResponderCmds.push_back(&(plResponderCmdNotify::Instance())); + gResponderCmds.push_back(&(plResponderCmdDetectorEnable::Instance())); + gResponderCmds.push_back(&(plResponderCmdCamTransition::Instance())); + gResponderCmds.push_back(&(plResponderCmdCamForce::Instance())); + gResponderCmds.push_back(&(plResponderCmdAnim::Instance())); + gResponderCmds.push_back(&(plResponderCmdMtl::Instance())); + gResponderCmds.push_back(&(plResponderCmdDelay::Instance())); + gResponderCmds.push_back(&(plResponderCmdFootSurface::Instance())); + gResponderCmds.push_back(&(plResponderCmdMultistage::Instance())); + gResponderCmds.push_back(&(plResponderCmdPhysEnable::Instance())); + gResponderCmds.push_back(&(plResponderCmdSubWorld::Instance())); + + for (int i = 0; i < gResponderCmds.size(); i++) + gResponderCmds[i]->GetDesc()->SetClassDesc(&gResponderDesc); + + ResponderWait::SetDesc(&gResponderDesc); +} + +plResponderComponent::plResponderComponent() +{ + fClassDesc = &gResponderDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plResponderComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + int numStates = fCompPB->Count(kResponderState); + for (int i = 0; i < numStates; i++) + { + IParamBlock2 *statePB = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, i); + + for (int j = 0; j < statePB->Count(kStateCmdParams); j++) + { + IParamBlock2 *cmdPB = (IParamBlock2*)statePB->GetReferenceTarget(kStateCmdParams, 0, j); + plResponderCmd *cmd = plResponderCmd::Find(cmdPB); + cmd->SetupProperties(node, pErrMsg, cmdPB); + } + } + + return true; +} + +hsBool plResponderComponent::PreConvert(plMaxNode *node,plErrorMsg *pErrMsg) +{ + plSceneObject* rObj = node->GetSceneObject(); + plLocation loc = node->GetLocation(); + + // Create and register the RESPONDER's logic component + plResponderModifier *responder = TRACKED_NEW plResponderModifier; + plKey responderKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), responder, loc); + hsgResMgr::ResMgr()->AddViaNotify(responderKey, TRACKED_NEW plObjRefMsg(rObj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + // Tell all the activators to notify us + for (int i = 0; i < fCompPB->Count(kResponderActivators); i++) + { + plMaxNode *activatorNode = (plMaxNode*)fCompPB->GetINode(kResponderActivators, 0, i); + plComponentBase *comp = activatorNode ? activatorNode->ConvertToComponent() : nil; + if (comp) + { + if (fCompPB->GetInt(kResponderLocalDetect)) + comp->AddReceiverKey(responderKey, node); + else + comp->AddReceiverKey(responderKey); + } + } + + fModKeys[node] = responderKey; + + return true; +} + +plResponderModifier* plResponderComponent::IGetResponderMod(plMaxNode* node) +{ + plKey responderKey = fModKeys[node]; + return plResponderModifier::ConvertNoRef(responderKey->GetObjectPtr()); +} + +hsBool plResponderComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + IFixOldPB(); + + // Create the commands for each state + int numStates = fCompPB->Count(kResponderState); + + plResponderModifier *responder = IGetResponderMod(node); + responder->fStates.SetCount(numStates); + + for (int i = 0; i < numStates; i++) + { + CmdIdxs cmdIdxs; + + IConvertCmds(node, pErrMsg, i, cmdIdxs); + + int numCallbacks = 0; + ISetupDefaultWait(node, pErrMsg, i, cmdIdxs, numCallbacks); + IConvertCmdWaits(node, pErrMsg, i, cmdIdxs, numCallbacks); + + IParamBlock2 *statePB = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, i); + responder->fStates[i].fNumCallbacks = numCallbacks; + responder->fStates[i].fSwitchToState = statePB->GetInt(kStateCmdSwitch); + } + + // Set the initial state + responder->fCurState = fCompPB->GetInt(kResponderStateDef); + responder->fEnabled = fCompPB->GetInt(kResponderEnabled) != 0; + + if (fCompPB->GetInt(kResponderTrigger)) + responder->fFlags |= plResponderModifier::kDetectTrigger; + if (fCompPB->GetInt(kResponderUnTrigger)) + responder->fFlags |= plResponderModifier::kDetectUnTrigger; + if (fCompPB->GetInt(kResponderSkipFFSound)) + responder->fFlags |= plResponderModifier::kSkipFFSound; + + // Unless it's been overridden somewhere else, don't save our state on the server + if (!node->GetOverrideHighLevelSDL()) + responder->AddToSDLExcludeList(kSDLResponder); + + return true; +} + +hsBool plResponderComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fModKeys.clear(); + return true; +} + +void plResponderComponent::IConvertCmds(plMaxNode* node, plErrorMsg* pErrMsg, int state, CmdIdxs& cmdIdxs) +{ + IParamBlock2 *statePB = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, state); + plResponderModifier *responder = IGetResponderMod(node); + + // Add the messages to the logic modifier + for (int i = 0; i < statePB->Count(kStateCmdParams); i++) + { + plMessage *msg = nil; + + BOOL enabled = statePB->GetInt(kStateCmdEnabled, 0, i); + if (!enabled) + continue; + + IParamBlock2 *cmdPB = (IParamBlock2*)statePB->GetReferenceTarget(kStateCmdParams, 0, i); + + try + { + plResponderCmd *cmd = plResponderCmd::Find(cmdPB); + if (cmd) + msg = cmd->CreateMsg(node, pErrMsg, cmdPB); + } + catch (char *reason) + { + char buf[512]; + + char stateName[128]; + const char *curStateName = fCompPB->GetStr(kResponderStateName, 0, state); + if (curStateName && *curStateName != '\0') + strcpy(stateName, fCompPB->GetStr(kResponderStateName, 0, state)); + else + sprintf(stateName, "State %d", state+1); + + sprintf(buf, + "A responder command failed to export.\n\nResponder:\t%s\nState:\t\t%s\nCommand:\t%d\n\nReason: %s", + GetINode()->GetName(), stateName, i+1, reason); + + pErrMsg->Set(true, "Responder Warning", buf).Show(); + pErrMsg->Set(false); + } + + if (msg) + { + msg->SetSender(responder->GetKey()); + responder->AddCommand(msg, state); + int idx = responder->fStates[state].fCmds.Count()-1; + cmdIdxs[i] = idx; + } + } +} + +static IParamBlock2 *GetWaitBlk(IParamBlock2 *state, int idx) +{ + return (IParamBlock2*)state->GetReferenceTarget(kStateCmdWait, 0, idx); +} + +void plResponderComponent::ISetupDefaultWait(plMaxNode* node, plErrorMsg* pErrMsg, + int state, CmdIdxs& cmdIdxs, int &numCallbacks) +{ + IParamBlock2 *statePB = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, state); + plResponderModifier *responder = IGetResponderMod(node); + hsTArray& cmds = responder->fStates[state].fCmds; + + int numCmds = cmds.Count(); + for (int i = 0; i < numCmds; i++) + { + IParamBlock2 *waitPB = GetWaitBlk(statePB, i); + ResponderWait::FixupWaitBlock(waitPB); + + // If we're supposed to wait for this command, and it converted, create a callback + if (ResponderWait::GetWaitOnMe(waitPB) && cmdIdxs.find(i) != cmdIdxs.end()) + { + int convertedIdx = cmdIdxs[i]; + + ResponderWaitInfo waitInfo; + waitInfo.responderName = GetINode()->GetName(); + waitInfo.receiver = responder->GetKey(); + waitInfo.callbackUser = numCallbacks++; + waitInfo.msg = cmds[convertedIdx].fMsg; + waitInfo.point = nil; + + IParamBlock2 *pb = (IParamBlock2*)statePB->GetReferenceTarget(kStateCmdParams, 0, i); + plResponderCmd *cmd = plResponderCmd::Find(pb); + + cmd->CreateWait(node, pErrMsg, pb, waitInfo); + } + } +} + +void plResponderComponent::IConvertCmdWaits(plMaxNode* node, plErrorMsg* pErrMsg, + int state, CmdIdxs& cmdIdxs, int &numCallbacks) +{ + IParamBlock2 *statePB = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, state); + plResponderModifier *responder = IGetResponderMod(node); + hsTArray& cmds = responder->fStates[state].fCmds; + + int numWaits = statePB->Count(kStateCmdWait); + for (int i = 0; i < numWaits; i++) + { + IParamBlock2 *waitPB = GetWaitBlk(statePB, i); + + int wait = ResponderWait::GetWaitingOn(waitPB); + + // If the waiter and waitee both converted, create the callback + if (cmdIdxs.find(wait) != cmdIdxs.end() && cmdIdxs.find(i) != cmdIdxs.end()) + { + int convertedIdx = cmdIdxs[wait]; + + ResponderWaitInfo waitInfo; + waitInfo.responderName = GetINode()->GetName(); + waitInfo.receiver = responder->GetKey(); + waitInfo.callbackUser = numCallbacks++; + waitInfo.msg = cmds[convertedIdx].fMsg; + waitInfo.point = ResponderWait::GetWaitPoint(waitPB); + + responder->AddCallback(state, convertedIdx, waitInfo.callbackUser); + cmds[cmdIdxs[i]].fWaitOn = waitInfo.callbackUser; + + IParamBlock2 *pb = (IParamBlock2*)statePB->GetReferenceTarget(kStateCmdParams, 0, wait); + plResponderCmd *cmd = plResponderCmd::Find(pb); + + cmd->CreateWait(node, pErrMsg, pb, waitInfo); + } + } +} + +void plResponderComponent::IFixOldPB() +{ + if (fCompPB) + { + if (fCompPB->Count(kResponderState) == 0) + { + IParamBlock2 *pb = CreateParameterBlock2(&gStateBlock, nil); + int idx = fCompPB->Append(kResponderState, 1, (ReferenceTarget**)&pb); + pb->SetValue(kStateCmdSwitch, 0, idx); + } + if (fCompPB->Count(kResponderStateName) == 0) + { + char *name = ""; + fCompPB->Append(kResponderStateName, 1, &name); + } + + // Make sure there is an enabled value for each command in the state + for (int i = 0; i < fCompPB->Count(kResponderState); i++) + { + IParamBlock2* pb = (IParamBlock2*)fCompPB->GetReferenceTarget(kResponderState, 0, i); + if (pb->Count(kStateCmdEnabled) != pb->Count(kStateCmdParams)) + pb->SetCount(kStateCmdEnabled, pb->Count(kStateCmdParams)); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// + +#define CUSTOM_DRAW + +enum +{ + kStateName, + kStateAdd, + kStateRemove, + kStateDefault, + kStateCopy, +}; + +class plResponderProc : public ParamMap2UserDlgProc +{ +protected: + HWND fhDlg; + IParamBlock2 *fPB; + IParamBlock2 *fStatePB; + int fCurState; + + plResponderComponent *fComp; + + IParamMap2 *fCmdMap; + IParamMap2 *fWaitMap; + + int fCmdIdx; + + typedef std::map NameID; + NameID fNames; + + HMENU fhMenu; + typedef std::pair CmdID; + typedef std::map MenuCmd; + MenuCmd fMenuCmds; + + HWND fhList; + + bool fIgnoreNextDrop; + +public: + plResponderProc(); + + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void DeleteThis() { IRemoveCmdRollups(); } + +protected: + void ICreateMenu(); + void IAddMenuItem(HMENU hMenu, int id); + + void ICmdRightClick(HWND hCmdList); + + // Add and remove command rollups + void ICreateCmdRollups(); + void IRemoveCmdRollups(); + IParamMap2 *ICreateMap(IParamBlock2 *pb); // Helper + + const char* GetCommandName(int cmdIdx); + void LoadList(); + + BOOL DragListProc(HWND hWnd, DRAGLISTINFO *info); + +#ifdef CUSTOM_DRAW + void IDrawComboItem(DRAWITEMSTRUCT *dis); +#endif + + void LoadState(); + + void AddCommand(); + void RemoveCurCommand(); + void MoveCommand(int oldIdx, int newIdx); + + // Takes a freshly created state PB and adds it as a new state, then returns its index + int AddState(IParamBlock2 *pb); +}; +static plResponderProc gResponderComponentProc; + +void plResponderProc::IAddMenuItem(HMENU hMenu, int id) +{ + AppendMenu(hMenu, MF_STRING, id+1, fNames[id]); +} + +#include "hsSTLSortUtils.h" + +void plResponderProc::ICreateMenu() +{ + fhMenu = CreatePopupMenu(); + + std::map menus; + int cmdID = 0; + + for (int i = 0; i < gResponderCmds.size(); i++) + { + plResponderCmd *cmd = gResponderCmds[i]; + for (int j = 0; j < cmd->NumTypes(); j++) + { + HMENU hParent = fhMenu; + + const char *category = cmd->GetCategory(j); + if (category) + { + // Menu for this category hasn't been created yet, make one + if (menus.find(category) == menus.end()) + { + hParent = CreatePopupMenu(); + menus[category] = hParent; + InsertMenu(fhMenu, 0, MF_BYPOSITION | MF_POPUP, (UINT)hParent, category); + } + else + hParent = menus[category]; + } + + const char *name = cmd->GetName(j); + + cmdID++; + fMenuCmds[cmdID] = CmdID(cmd, j); + AppendMenu(hParent, MF_STRING, cmdID, name); + } + } +} + +plResponderProc::plResponderProc() : fCmdMap(nil), fCmdIdx(-1), fCurState(0), fhMenu(nil), fIgnoreNextDrop(false) +{ +} + +const char* plResponderProc::GetCommandName(int cmdIdx) +{ + static char buf[256]; + + if (fStatePB->Count(kStateCmdParams) > cmdIdx) + { + buf[0] = '\0'; + + BOOL enabled = fStatePB->GetInt(kStateCmdEnabled, 0, cmdIdx); + if (!enabled) + strcat(buf, "[D]"); + + IParamBlock2 *cmdPB = (IParamBlock2*)fStatePB->GetReferenceTarget(kStateCmdParams, 0, cmdIdx); + plResponderCmd *cmd = plResponderCmd::Find(cmdPB); + + IParamBlock2 *waitPB = (IParamBlock2*)fStatePB->GetReferenceTarget(kStateCmdWait, 0, cmdIdx); + int waitingOn = ResponderWait::GetWaitingOn(waitPB); + if (waitingOn != -1) + { + char num[10]; + sprintf(num, "(%d)", waitingOn+1); + strcat(buf, num); + } + + strcat(buf, cmd->GetInstanceName(cmdPB)); + + return buf; + } + + hsAssert(0, "Bad index to GetCommandName"); + return nil; +} + +void plResponderProc::LoadList() +{ + ListBox_ResetContent(fhList); + + for (int i = 0; i < fStatePB->Count(kStateCmdParams); i++) + { + const char* name = GetCommandName(i); + ListBox_AddString(fhList, name); + } + + ListBox_SetCurSel(fhList, -1); +} + +void plResponderProc::AddCommand() +{ + RECT rect; + GetWindowRect(GetDlgItem(fhDlg, IDC_ADD_CMD), &rect); + + // Create the popup menu and get the option the user selects + SetForegroundWindow(fhDlg); + int type = TrackPopupMenu(fhMenu, TPM_RIGHTALIGN | TPM_NONOTIFY | TPM_RETURNCMD, rect.left, rect.top, 0, fhDlg, NULL); + PostMessage(fhDlg, WM_USER, 0, 0); + + if (type == 0) + return; + + CmdID& cmdID = fMenuCmds[type]; + plResponderCmd *cmd = cmdID.first; + int cmdIdx = cmdID.second; + + IParamBlock2 *cmdPB = cmd->CreatePB(cmdIdx); + fStatePB->Append(kStateCmdParams, 1, (ReferenceTarget**)&cmdPB); + + IParamBlock2 *waitPB = ResponderWait::CreatePB(); + fStatePB->Append(kStateCmdWait, 1, (ReferenceTarget**)&waitPB); + + BOOL enabled = TRUE; + fStatePB->Append(kStateCmdEnabled, 1, &enabled); + + const char* name = GetCommandName(fStatePB->Count(kStateCmdParams)-1); + int idx = ListBox_AddString(fhList, name); + ListBox_SetCurSel(fhList, idx); + + ICreateCmdRollups(); +} + +void plResponderProc::RemoveCurCommand() +{ + int idx = ListBox_GetCurSel(fhList); + if (idx == LB_ERR) + return; + + // Destroy the current rollup, since it's this guy + IRemoveCmdRollups(); + + // Delete all traces of this command + fStatePB->Delete(kStateCmdParams, idx, 1); + fStatePB->Delete(kStateCmdWait, idx, 1); + fStatePB->Delete(kStateCmdEnabled, idx, 1); + ListBox_DeleteString(fhList, idx); + + // Patch the wait commands + ResponderWait::CmdRemoved(fStatePB, idx); + + fCmdIdx = -1; +} + +void plResponderProc::IRemoveCmdRollups() +{ + if (fCmdMap) + { + DestroyCPParamMap2(fCmdMap); + fCmdMap = nil; + } + if (fWaitMap) + { + DestroyCPParamMap2(fWaitMap); + fWaitMap = nil; + } +} + +IParamMap2 *plResponderProc::ICreateMap(IParamBlock2 *pb) +{ + ParamBlockDesc2 *pd = pb->GetDesc(); + + // Don't show anything if there isn't a UI + if (pd->Count() < 1) + { + pb->ReleaseDesc(); + return nil; + } + + // Create the rollout + IParamMap2 *map = CreateCPParamMap2(0, + pb, + GetCOREInterface(), + hInstance, + MAKEINTRESOURCE(pd->dlg_template), + GetString(pd->title), + pd->flags, + pd->dlgProc, + NULL, + ROLLUP_CAT_STANDARD); + + // Save the rollout in the paramblock + pb->SetMap(map); + pb->ReleaseDesc(); + + return map; +} + +void plResponderProc::ICreateCmdRollups() +{ + // Get the index of the current command + HWND hCmds = GetDlgItem(fhDlg, IDC_CMD_LIST); + int cmdIdx = ListBox_GetCurSel(hCmds); + + if (cmdIdx != LB_ERR && cmdIdx != fCmdIdx) + { + fCmdIdx = cmdIdx; + fIgnoreNextDrop = true; + + // Save the current scroll position and reset it at the end, so the panels + // won't always jerk back up to the top + IRollupWindow *rollup = GetCOREInterface()->GetCommandPanelRollup(); + int scrollPos = rollup->GetScrollPos(); + + // Destroy the last command's rollups + IRemoveCmdRollups(); + + // Create the rollup for the current command + IParamBlock2 *pb = (IParamBlock2*)fStatePB->GetReferenceTarget(kStateCmdParams, 0, fCmdIdx); + fCmdMap = ICreateMap(pb); + + ResponderWait::InitDlg(fStatePB, fCmdIdx, GetDlgItem(fhDlg, IDC_CMD_LIST)); + pb = (IParamBlock2*)fStatePB->GetReferenceTarget(kStateCmdWait, 0, fCmdIdx); + fWaitMap = ICreateMap(pb); + + rollup->SetScrollPos(scrollPos); + } +} + +BOOL plResponderProc::DragListProc(HWND hWnd, DRAGLISTINFO *info) +{ + static int oldIdx = -1; + + int curIdx = LBItemFromPt(info->hWnd, info->ptCursor, TRUE); + + switch (info->uNotification) + { + // Allow the drag + case DL_BEGINDRAG: + // When you click on an item in the listbox, the rollups are changed and Max can + // shift the position of dialog you were just clicking in. Since this happens + // before you let go of the mouse button, the listbox thinks you are dragging. + // To get around it, we don't allow a selection change and a drag in the same click. + if (fIgnoreNextDrop) + { + SetWindowLong(hWnd, DWL_MSGRESULT, FALSE); + } + else + { + oldIdx = curIdx; + SetWindowLong(hWnd, DWL_MSGRESULT, TRUE); + } + return TRUE; + + case DL_DRAGGING: + { + if (curIdx < oldIdx) + DrawInsert(hWnd, info->hWnd, curIdx); + else if (curIdx > oldIdx && ListBox_GetCount(info->hWnd) > curIdx+1) + DrawInsert(hWnd, info->hWnd, curIdx+1); + else + DrawInsert(hWnd, info->hWnd, -1); + } + return TRUE; + + case DL_CANCELDRAG: + // Clear drag arrow + DrawInsert(hWnd, info->hWnd, -1); + return TRUE; + + case DL_DROPPED: + { + if (fIgnoreNextDrop) + { + fIgnoreNextDrop = false; + return TRUE; + } + + // Clear drag arrow + DrawInsert(hWnd, info->hWnd, -1); + + if (curIdx != -1 && oldIdx != -1 && curIdx != oldIdx) + { + // Make sure this won't mess up any wait commands, or at least + // that the user approves if it does. + if (!ResponderWait::ValidateCmdMove(fStatePB, oldIdx, curIdx)) + return TRUE; + + MoveCommand(oldIdx, curIdx); + } + + return TRUE; + } + } + + return FALSE; +} + +#ifdef CUSTOM_DRAW +void plResponderProc::IDrawComboItem(DRAWITEMSTRUCT *dis) +{ + if (dis->itemID == -1) // empty item + return; + + // The colors depend on whether the item is selected. + COLORREF clrForeground = SetTextColor(dis->hDC, + GetSysColor(dis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)); + + COLORREF clrBackground = SetBkColor(dis->hDC, + GetSysColor(dis->itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW)); + + // Calculate the vertical and horizontal position. + TEXTMETRIC tm; + GetTextMetrics(dis->hDC, &tm); + int y = (dis->rcItem.bottom + dis->rcItem.top - tm.tmHeight) / 2; + int x = LOWORD(GetDialogBaseUnits()) / 4; + + // If this is a command, not a state, make it bold + HFONT oldFont = nil; + if (dis->itemData != kStateName) + { + LOGFONT lf; + memset(&lf, 0, sizeof(lf)); + lf.lfHeight = tm.tmHeight; + lf.lfWeight = FW_BOLD; + GetTextFace(dis->hDC, LF_FACESIZE, lf.lfFaceName); + HFONT boldFont = CreateFontIndirect(&lf); + oldFont = SelectFont(dis->hDC, boldFont); + } + + // Get and display the text for the list item. + char buf[256]; + ComboBox_GetLBText(dis->hwndItem, dis->itemID, buf); + if (fPB->GetInt(kResponderStateDef) == dis->itemID) + { + char buf2[256]; + sprintf(buf2, "* %s", buf); + strcpy(buf, buf2); + } + + ExtTextOut(dis->hDC, x, y, ETO_CLIPPED | ETO_OPAQUE, &dis->rcItem, buf, strlen(buf), NULL); + + // Restore the previous colors. + SetTextColor(dis->hDC, clrForeground); + SetBkColor(dis->hDC, clrBackground); + + if (oldFont) + DeleteFont(SelectFont(dis->hDC, oldFont)); + + // If the item has the focus, draw focus rectangle. + if (dis->itemState & ODS_FOCUS) + DrawFocusRect(dis->hDC, &dis->rcItem); +} +#endif + +void plResponderProc::LoadState() +{ + fStatePB = (IParamBlock2*)fPB->GetReferenceTarget(kResponderState, 0, fCurState); + + IRemoveCmdRollups(); + LoadList(); + + HWND hSwitchCombo = GetDlgItem(fhDlg, IDC_SWITCH_COMBO); + ComboBox_SetCurSel(hSwitchCombo, fStatePB->GetInt(kStateCmdSwitch)); +} + +// THE MAGICAL TURDFEST. Max's default RemapDir tries to always clone your referenced +// objects. So when we reference an INode it wants to make a clone of that INode +// (and fails for some reason). To get around this I just check if the object to be cloned +// is a paramblock, and actually clone it if it is. Otherwise, I just return the object. +// +// UPDATE: Looks like it's only with ResponderComponents. Probably the parentless PB's (which +// exist due to another bug). Who cares, this works. +class MyRemapDir : public RemapDir +{ +public: + RefTargetHandle CloneRef(RefTargetHandle oldTarg) + { + if (oldTarg == NULL) + return NULL; + else if (oldTarg->SuperClassID() == PARAMETER_BLOCK2_CLASS_ID) + return oldTarg->Clone(*this); + else + return oldTarg; + } + + void PatchPointer(RefTargetHandle* patchThis, RefTargetHandle oldTarg) { hsAssert(0, "shit"); } + RefTargetHandle FindMapping(RefTargetHandle from) { hsAssert(0, "shit"); return NULL; } + void AddEntry(RefTargetHandle hfrom, RefTargetHandle hto) { hsAssert(0, "shit"); } + void Backpatch() { hsAssert(0, "shit"); } + void Clear() { hsAssert(0, "shit"); } + void DeleteThis() { hsAssert(0, "shit"); } + void AddPostPatchProc(PostPatchProc* proc, bool toDelete) { hsAssert(0, "shit"); } +}; +// Even turdier - I had to define this to compile +RefTargetHandle RemapDir::CloneRef(RefTargetHandle oldTarg) { return NULL; } +static MyRemapDir gMyRemapDir; + +RefTargetHandle plResponderComponent::Clone(RemapDir &remap) +{ + plComponentBase *obj = (plComponentBase*)fClassDesc->Create(false); + // Do the base clone + BaseClone(this, obj, remap); + // Copy our references + obj->ReplaceReference(kRefComp, fCompPB->Clone(gMyRemapDir)); + obj->ReplaceReference(kRefTargs, fTargsPB->Clone(remap)); + + return obj; +} + +BOOL plResponderProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static UINT dragListMsg = 0; + + if (dragListMsg != 0 && msg == dragListMsg) + if (DragListProc(hWnd, (DRAGLISTINFO*)lParam)) + return TRUE; + + switch (msg) + { + case WM_INITDIALOG: + { + if (!fhMenu) + ICreateMenu(); + + fhDlg = hWnd; + fhList = GetDlgItem(fhDlg, IDC_CMD_LIST); + fCurState = 0; + fCmdIdx = -1; + + fPB = pm->GetParamBlock(); + fComp = (plResponderComponent*)fPB->GetOwner(); + + fComp->IFixOldPB(); + + LoadState(); + + // Make it so the user can drag commands to different positions + dragListMsg = RegisterWindowMessage(DRAGLISTMSGSTRING); + MakeDragList(GetDlgItem(hWnd, IDC_CMD_LIST)); + + // Setup the State Name combo + HWND hStateName = GetDlgItem(hWnd, IDC_STATE_COMBO); + ComboBox_LimitText(hStateName, 256); + +// I give up, Windows doesn't want to tell me the real font size +#if 0//def CUSTOM_DRAW + // TEMP + HDC hDC = GetDC(hStateName); + HFONT sysFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); + HFONT oldFont = SelectFont(hDC, sysFont); + + TEXTMETRIC tm; + GetTextMetrics(hDC, &tm); + ComboBox_SetItemHeight(hStateName, 0, tm.tmHeight+2); + + DeleteFont(SelectFont(hDC, oldFont)); + ReleaseDC(hStateName, hDC); +#endif + + // Add the commands + int idx = ComboBox_AddString(hStateName, "Add State"); + ComboBox_SetItemData(hStateName, idx, kStateAdd); + idx = ComboBox_AddString(hStateName, "Remove Current State"); + ComboBox_SetItemData(hStateName, idx, kStateRemove); + idx = ComboBox_AddString(hStateName, "Set Current as Default"); + ComboBox_SetItemData(hStateName, idx, kStateDefault); + idx = ComboBox_AddString(hStateName, "Copy Current State"); + ComboBox_SetItemData(hStateName, idx, kStateCopy); + + HWND hSwitchCombo = GetDlgItem(hWnd, IDC_SWITCH_COMBO); + + int numStates = fPB->Count(kResponderStateName); + for (int i = 0; i < numStates; i++) + { + const char *stateName = fPB->GetStr(kResponderStateName, 0, i); + char buf[128]; + if (!stateName || *stateName == '\0') + { + sprintf(buf, "State %d", i+1); + stateName = buf; + } + ComboBox_InsertString(hStateName, i, stateName); + ComboBox_AddString(hSwitchCombo, stateName); + } + + ComboBox_SetCurSel(hStateName, fCurState); + + ComboBox_SetCurSel(hSwitchCombo, fStatePB->GetInt(kStateCmdSwitch)); + } + return TRUE; + +#ifdef CUSTOM_DRAW + case WM_DRAWITEM: + if (wParam == IDC_STATE_COMBO) + { + IDrawComboItem((DRAWITEMSTRUCT*)lParam); + return TRUE; + } + break; +#endif + + case WM_SETCURSOR: + { + if (HIWORD(lParam) == WM_RBUTTONDOWN && HWND(wParam) == GetDlgItem(hWnd, IDC_CMD_LIST)) + { + ICmdRightClick(HWND(wParam)); + return TRUE; + } + } + break; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + if (LOWORD(wParam) == IDC_ADD_ACTIVATOR) + { + // Adding an activator. Set it and refresh the UI to show it in our list. + plPick::Activator(fPB, kResponderActivators, false); + pm->Invalidate(kResponderActivators); + return TRUE; + } + else if (LOWORD(wParam) == IDC_ADD_CMD) + { + AddCommand(); + return TRUE; + } + // Remove the currently selected condition + else if (LOWORD(wParam) == IDC_REMOVE_CMD) + { + RemoveCurCommand(); + return TRUE; + } + } + else if (HIWORD(wParam) == LBN_SELCHANGE && LOWORD(wParam) == IDC_CMD_LIST) + { + ICreateCmdRollups(); + return TRUE; + } + else if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_SWITCH_COMBO) + { + int sel = ComboBox_GetCurSel((HWND)lParam); + if (sel != CB_ERR) + fStatePB->SetValue(kStateCmdSwitch, 0, sel); + } + else if (LOWORD(wParam) == IDC_STATE_COMBO) + { + HWND hCombo = (HWND)lParam; + int code = HIWORD(wParam); + + // Disable accelerators when the combo has focus, so that new names can be typed in + if (code == CBN_SETFOCUS) + { + plMaxAccelerators::Disable(); + return TRUE; + } + else if (code == CBN_KILLFOCUS) + { + plMaxAccelerators::Enable(); + return TRUE; + } + // State name changed, save it in the PB + else if (code == CBN_EDITCHANGE) + { + char buf[256]; + ComboBox_GetText(hCombo, buf, sizeof(buf)); + const char *curName = fPB->GetStr(kResponderStateName, 0, fCurState); + if (!curName || strcmp(buf, curName)) + { + HWND hSwitch = GetDlgItem(hWnd, IDC_SWITCH_COMBO); + int sel = ComboBox_GetCurSel(hSwitch); + ComboBox_DeleteString(hSwitch, fCurState); + ComboBox_InsertString(hSwitch, fCurState, buf); + ComboBox_SetCurSel(hSwitch, sel); + + fPB->SetValue(kResponderStateName, 0, buf, fCurState); + ComboBox_DeleteString(hCombo, fCurState); + ComboBox_InsertString(hCombo, fCurState, buf); +// ComboBox_SetCurSel(hCombo, fCurState); + } + + return TRUE; + } + else if (code == CBN_SELCHANGE) + { + int sel = ComboBox_GetCurSel(hCombo); + int type = ComboBox_GetItemData(hCombo, sel); + + if (type == kStateAdd) + { + IParamBlock2 *pb = CreateParameterBlock2(&gStateBlock, nil); + fCurState = AddState(pb); + fCmdIdx = -1; + } + else if (type == kStateRemove) + { + int count = fPB->Count(kResponderState); + // Don't let the user remove the last state + if (count == 1) + { + hsMessageBox("You must have at least one state.", "Error", hsMessageBoxNormal); + ComboBox_SetCurSel(hCombo, fCurState); + return TRUE; + } + // Verify that the user really wants to delete the state + else + { + int ret = hsMessageBox("Are you sure you want to remove this state?", "Verify Remove", hsMessageBoxYesNo); + if (ret == hsMBoxNo) + { + ComboBox_SetCurSel(hCombo, fCurState); + return TRUE; + } + } + + fPB->Delete(kResponderState, fCurState, 1); + fPB->Delete(kResponderStateName, fCurState, 1); + + ComboBox_DeleteString(hCombo, fCurState); + ComboBox_SetCurSel(hCombo, 0); + + HWND hSwitch = GetDlgItem(hWnd, IDC_SWITCH_COMBO); + ComboBox_DeleteString(hSwitch, fCurState); + + // If the deleted state was the default, set the default to the first + int defState = fPB->GetInt(kResponderStateDef); + if (fCurState == defState) + fPB->SetValue(kResponderStateDef, 0, 0); + else if (fCurState < defState) + fPB->SetValue(kResponderStateDef, 0, defState-1); + + // Patch up the switch commands + for (int i = fCurState; i < fPB->Count(kResponderState); i++) + { + IParamBlock2 *pb = (IParamBlock2*)fPB->GetReferenceTarget(kResponderState, 0, i); + + int switchState = pb->GetInt(kStateCmdSwitch); + // TODO: might want to warn about this + if (switchState == fCurState) + pb->SetValue(kStateCmdSwitch, 0, 0); + else if (switchState > fCurState) + pb->SetValue(kStateCmdSwitch, 0, switchState-1); + } + + fCurState = 0; + fCmdIdx = -1; + } + else if (type == kStateDefault) + { + // Set the current state as the default + fPB->SetValue(kResponderStateDef, 0, fCurState); + ComboBox_SetCurSel(hCombo, fCurState); + } + else if (type == kStateCopy) + { + // Clone the state PB + IParamBlock2 *origPB = (IParamBlock2*)fPB->GetReferenceTarget(kResponderState, 0, fCurState); + IParamBlock2 *copyPB = (IParamBlock2*)origPB->Clone(gMyRemapDir); + fCurState = AddState(copyPB); + fCmdIdx = -1; + } + else + { + fCurState = sel; + fCmdIdx = -1; + } + + LoadState(); + + return TRUE; + } + } + } + + return FALSE; +} + +void plResponderProc::ICmdRightClick(HWND hCmdList) +{ + // Get the position of the cursor in screen and tree client coords + POINT point, localPoint; + GetCursorPos(&point); + localPoint = point; + ScreenToClient(hCmdList, &localPoint); + + LRESULT res = SendMessage(hCmdList, LB_ITEMFROMPOINT, 0, MAKELPARAM(localPoint.x, localPoint.y)); + WORD index = LOWORD(res); + if (index == WORD(LB_ERR)) + return; + + RECT rect; + SendMessage(hCmdList, LB_GETITEMRECT, index, (LPARAM)&rect); + + // Make sure we're actually ON an item, LB_ITEMFROMPOINT get the closest instead of exact + if (localPoint.y >= rect.top && localPoint.y <= rect.bottom) + { + BOOL enabled = fStatePB->GetInt(kStateCmdEnabled, 0, index); + + HMENU hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING, 1, enabled ? "Disable" : "Enable"); + + SetForegroundWindow(fhDlg); + int sel = TrackPopupMenu(hMenu, TPM_NONOTIFY | TPM_RETURNCMD, point.x, point.y, 0, fhDlg, NULL); + if (sel == 1) + { + fStatePB->SetValue(kStateCmdEnabled, 0, !enabled, index); + + ListBox_DeleteString(hCmdList, index); + ListBox_InsertString(hCmdList, index, GetCommandName(index)); + } + + DestroyMenu(hMenu); + } +} + +int plResponderProc::AddState(IParamBlock2 *pb) +{ + int idx = fPB->Append(kResponderState, 1, (ReferenceTarget**)&pb); + pb->SetValue(kStateCmdSwitch, 0, idx); + + char *name = ""; + fPB->Append(kResponderStateName, 1, &name); + + HWND hCombo = GetDlgItem(fhDlg, IDC_STATE_COMBO); + char buf[128]; + sprintf(buf, "State %d", idx+1); + ComboBox_InsertString(hCombo, idx, buf); + ComboBox_SetCurSel(hCombo, idx); + + HWND hSwitch = GetDlgItem(fhDlg, IDC_SWITCH_COMBO); + ComboBox_AddString(hSwitch, buf); + + return idx; +} + +void plResponderProc::MoveCommand(int oldIdx, int newIdx) +{ + // Move data + int insertIdx = (newIdx > oldIdx) ? newIdx+1 : newIdx; + int deleteIdx = (newIdx < oldIdx) ? oldIdx+1 : oldIdx; + + ReferenceTarget *targ = fStatePB->GetReferenceTarget(kStateCmdParams, 0, oldIdx); + fStatePB->Insert(kStateCmdParams, insertIdx, 1, &targ); + fStatePB->Delete(kStateCmdParams, deleteIdx, 1); + + ReferenceTarget *wait = fStatePB->GetReferenceTarget(kStateCmdWait, 0, oldIdx); + fStatePB->Insert(kStateCmdWait, insertIdx, 1, &wait); + fStatePB->Delete(kStateCmdWait, deleteIdx, 1); + + BOOL oldEnabled = fStatePB->GetInt(kStateCmdEnabled, 0, oldIdx); + BOOL newEnabled = fStatePB->GetInt(kStateCmdEnabled, 0, newIdx); + fStatePB->SetValue(kStateCmdEnabled, 0, oldEnabled, newIdx); + fStatePB->SetValue(kStateCmdEnabled, 0, newEnabled, oldIdx); + + ResponderWait::CmdMoved(fStatePB, oldIdx, newIdx); + + LoadList(); + + // Reselect item + // (This doesn't send the LBN_SELCHANGE message so we do that manually) + ListBox_SetCurSel(fhList, newIdx); + ICreateCmdRollups(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponent.h new file mode 100644 index 00000000..2ee7c167 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponent.h @@ -0,0 +1,48 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plComponentExt.h" +#include "../pnKeyedObject/plKey.h" + +#define RESPONDER_CID Class_ID(0x46b83f3e, 0x7d5e5d17) + +class plResponderComponentExt : public plComponentExt +{ +public: + // All classes derived from plResponderComponent can be picked from the activator component, + // because they can convert to the responder type. + int CanConvertToType(Class_ID obtype) + { return (obtype == RESPONDER_CID) ? 1 : plComponentExt::CanConvertToType(obtype); } +}; + +int ResponderGetActivatorCount(plComponentBase *comp); +plComponentBase *ResponderGetActivator(plComponentBase *comp, int idx); + +namespace Responder +{ + // Pass in a responder component and a node it is attached to, and you will + // get the key to the responder modifier + plKey GetKey(plComponentBase *comp, plMaxNodeBase *node); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponentPriv.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponentPriv.h new file mode 100644 index 00000000..8caac3a5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderComponentPriv.h @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Private header for the responder component. Anyone else should include plResponderComponent.h +#include "plResponderComponent.h" +#include "plComponent.h" +#include "../pnKeyedObject/plKey.h" + +#include + +class plResponderComponent : public plComponent +{ +public: + std::map fModKeys; + + plResponderComponent(); + + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode *node,plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node,plErrorMsg *pErrMsg); + hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + // All classes derived from plResponderComponent can be picked from the activator component, + // because they can convert to the responder type. + int CanConvertToType(Class_ID obtype) { return (obtype == RESPONDER_CID) ? 1 : plComponent::CanConvertToType(obtype); } + + RefTargetHandle Clone(RemapDir &remap); + +protected: + // Old responders won't have their tables set up correctly. Call this to fix them + // before export or opening a dialog. + void IFixOldPB(); + + typedef std::map CmdIdxs; + + plResponderModifier* IGetResponderMod(plMaxNode* node); + + void IConvertCmds(plMaxNode* node, plErrorMsg* pErrMsg, int state, CmdIdxs& cmdIdxs); +// void IConvertWait(int state, plResponderModifier *responder); + + void ISetupDefaultWait(plMaxNode* node, plErrorMsg* pErrMsg, int state, CmdIdxs& cmdIdxs, int &numCallbacks); + void IConvertCmdWaits (plMaxNode* node, plErrorMsg* pErrMsg, int state, CmdIdxs& cmdIdxs, int &numCallbacks); + + friend class plResponderProc; +}; + +// Param ID's for the responder comp PB +enum +{ + kResponderActivators=1, + kResponderState=7, + kResponderStateName, + kResponderStateDef, + kResponderEnabled, + kResponderTrigger, + kResponderUnTrigger, + kResponderLocalDetect, + kResponderSkipFFSound +}; + +// Param ID's for the responder state PB +enum +{ + kStateCmdType_DEAD, + kStateCmdParams, + kStateCmdWait, + kStateCmdSwitch, + kStateCmdWaitOnMe, + kStateCmdEnabled, +}; + +// Block ID's for responder command PB's +enum +{ + kResponderAnimBlk = 100, + kResponderMtlBlk, + kResponderSndBlk, + kResponderLnkBlk, + kResponderWaitBlk, + kResponderStateBlk, + kResponderEnableMsgBlk, + kResponderOneShotMsgBlk, + kResponderNotifyMsgBlk, + kResponderActivatorEnableBlk, + kResponderXRegionBlk, + kResponderCameraTransitionBlk, + kResponderDelayBlk, + kResponderVisibilityBlk, + kResponderFootSurfaceBlk, + kResponderMultistageBlk, + kResponderPhysEnableBlk, + kResponderCameraForceBlk, + kResponderSubWorldBlk, +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderGetComp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderGetComp.cpp new file mode 100644 index 00000000..8ed96807 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderGetComp.cpp @@ -0,0 +1,341 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plResponderGetComp.h" + +#include + +#include "../MaxMain/plMaxNodeBase.h" +#include "resource.h" +#include "plComponentBase.h" +#include "../MaxMain/plMaxAccelerators.h" + +plResponderGetComp& plResponderGetComp::Instance() +{ + static plResponderGetComp theInstance; + return theInstance; +} + +bool plResponderGetComp::GetComp(IParamBlock2 *pb, int nodeID, int compID, ClassIDs *classIDs) +{ + fPB = pb; + fNodeID = nodeID; + fCompID = compID; + fClassIDs = classIDs; + + plMaxAccelerators::Disable(); + + int ret = DialogBox(hInstance, + MAKEINTRESOURCE(IDD_COMP_RESPOND_ANIMPICK), + GetCOREInterface()->GetMAXHWnd(), + ForwardDlgProc); + + plMaxAccelerators::Enable(); + + return (ret != 0); +} + +plComponentBase *plResponderGetComp::GetSavedComp(IParamBlock2 *pb, int nodeID, int compID, bool convertTime) +{ + plMaxNodeBase *node = (plMaxNodeBase*)pb->GetReferenceTarget(nodeID); + // This value could be whack, only use if node is valid + plMaxNodeBase *comp = (plMaxNodeBase*)pb->GetReferenceTarget(compID); + + if (!node || (convertTime && !node->CanConvert()) || !comp) + return nil; + + int numComps = node->NumAttachedComponents(); + for (int i = 0; i < numComps; i++) + { + plComponentBase *thisComp = node->GetAttachedComponent(i); + if (thisComp->GetINode() == comp) + return thisComp; + } + + return nil; +} +void plResponderGetComp::IFindCompsRecur(plMaxNodeBase *node, NodeSet& nodes) +{ + plComponentBase *comp = node->ConvertToComponent(); + if (comp) + { + // If we're not filtering, or we are and this component is in our accepted list, add it + if (!fClassIDs || + std::find(fClassIDs->begin(), fClassIDs->end(), comp->ClassID()) != fClassIDs->end()) + { + nodes.insert(node); + } + } + + for (int i = 0; i < node->NumberOfChildren(); i++) + IFindCompsRecur((plMaxNodeBase*)node->GetChildNode(i), nodes); +} + +void plResponderGetComp::ILoadNodes(plMaxNodeBase *compNode, HWND hDlg) +{ + HWND hNodes = GetDlgItem(hDlg, IDC_OBJ_LIST); + ListBox_ResetContent(hNodes); + + plComponentBase *comp = compNode ? compNode->ConvertToComponent() : nil; + if (!comp) + return; + + plMaxNodeBase *savedNode = (plMaxNodeBase*)fPB->GetReferenceTarget(fNodeID); + + for (int i = 0; i < comp->NumTargets(); i++) + { + plMaxNodeBase *node = comp->GetTarget(i); + if (node) + { + int idx = ListBox_AddString(hNodes, node->GetName()); + ListBox_SetItemData(hNodes, idx, node); + + if (savedNode == node) + ListBox_SetCurSel(hNodes, idx); + } + } +} + +BOOL CALLBACK plResponderGetComp::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return Instance().DlgProc(hDlg, msg, wParam, lParam); +} + +BOOL plResponderGetComp::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + NodeSet nodes; + IFindCompsRecur((plMaxNodeBase*)GetCOREInterface()->GetRootNode(), nodes); + + HWND hComps = GetDlgItem(hDlg, IDC_COMP_LIST); + + plMaxNodeBase *node = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID); + + for (NodeSet::iterator it = nodes.begin(); it != nodes.end(); it++) + { + int idx = ListBox_AddString(hComps, (*it)->GetName()); + ListBox_SetItemData(hComps, idx, *it); + + if (*it == node) + ListBox_SetCurSel(hComps, idx); + } + + ILoadNodes(node, hDlg); + } + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, 0); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK) + { + // Get the selected node + HWND hNode = GetDlgItem(hDlg, IDC_OBJ_LIST); + int idx = ListBox_GetCurSel(hNode); + plMaxNodeBase *node = nil; + if (idx != LB_ERR) + node = (plMaxNodeBase*)ListBox_GetItemData(hNode, idx); + + // Get the selected component + HWND hComp = GetDlgItem(hDlg, IDC_COMP_LIST); + idx = ListBox_GetCurSel(hComp); + plMaxNodeBase *comp = nil; + if (idx != LB_ERR) + comp = (plMaxNodeBase*)ListBox_GetItemData(hComp, idx); + + // If both were selected, save them in the PB and as the last setting + if (node && comp) + { +#if 0 + if (fType == kFindAnim) + { + fLastAnimObj = obj->GetHandle(); + fLastAnimComp = comp->GetHandle(); + } + else if (fType == kFindSound) + { + fLastSoundObj = obj->GetHandle(); + fLastSoundComp = comp->GetHandle(); + } +#endif + fPB->SetValue(fNodeID, 0, (ReferenceTarget*)node); + fPB->SetValue(fCompID, 0, (ReferenceTarget*)comp); + + EndDialog(hDlg, 1); + } + else + EndDialog(hDlg, 0); + + return TRUE; + } + else if (HIWORD(wParam) == LBN_SELCHANGE) + { + if (LOWORD(wParam) == IDC_COMP_LIST) + { + int idx = ListBox_GetCurSel((HWND)lParam); + plMaxNodeBase *node = (plMaxNodeBase*)ListBox_GetItemData((HWND)lParam, idx); + ILoadNodes(node, hDlg); + } + } + break; + } + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////// + +#include "plPickNode.h" + +void plResponderCompNode::Init(IParamBlock2 *pb, int compID, int nodeID, int compResID, int nodeResID, ClassIDs *compCIDs) +{ + fPB = pb; + fCompID = compID; + fNodeID = nodeID; + fCompResID = compResID; + fNodeResID = nodeResID; + + if (compCIDs) + fCompCIDs = *compCIDs; +} + +void plResponderCompNode::InitDlg(HWND hWnd) +{ + IValidate(); + + IUpdateNodeButton(hWnd); + IUpdateCompButton(hWnd); +} + +bool plResponderCompNode::IValidate() +{ + plMaxNodeBase *savedComp = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID); + plComponentBase *comp = savedComp ? savedComp->ConvertToComponent() : nil; + plMaxNodeBase *node = (plMaxNodeBase*)fPB->GetReferenceTarget(fNodeID); + if (comp && node) + { + // Make sure the selected comp has the correct CID + if (fCompCIDs.size() > 0) + { + Class_ID compCID = comp->ClassID(); + bool foundCID = false; + for (int i = 0; i < fCompCIDs.size(); i++) + { + if (fCompCIDs[i] == compCID) + foundCID = true; + } + + if (!foundCID) + { + fPB->SetValue(fCompID, 0, (INode*)nil); + fPB->SetValue(fNodeID, 0, (INode*)nil); + return false; + } + } + + // Make sure the comp is really attached to the node + if (comp->IsTarget(node)) + return true; + else + fPB->SetValue(fNodeID, 0, (INode*)nil); + } + + return false; +} + +void plResponderCompNode::CompButtonPress(HWND hWnd) +{ + plPick::Node(fPB, fCompID, &fCompCIDs, true, false); + + IUpdateCompButton(hWnd); + IUpdateNodeButton(hWnd); +} + +void plResponderCompNode::NodeButtonPress(HWND hWnd) +{ + plMaxNodeBase *node = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID); + plComponentBase *comp = node ? node->ConvertToComponent() : nil; + if (comp) + plPick::CompTargets(fPB, fNodeID, comp); + + IUpdateNodeButton(hWnd); +} + +void plResponderCompNode::IUpdateCompButton(HWND hWnd) +{ + HWND hComp = GetDlgItem(hWnd, fCompResID); + + plMaxNodeBase *savedComp = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID); + + if (savedComp) + SetWindowText(hComp, savedComp->GetName()); + else + SetWindowText(hComp, "(none)"); +} + +void plResponderCompNode::IUpdateNodeButton(HWND hWnd) +{ + HWND hNode = GetDlgItem(hWnd, fNodeResID); + + // If there is no component, disable the node button + plMaxNodeBase *node = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID); + plComponentBase *comp = node ? node->ConvertToComponent() : nil; + if (!comp) + { + EnableWindow(hNode, FALSE); + SetWindowText(hNode, "(none)"); + return; + } + EnableWindow(hNode, TRUE); + + plMaxNodeBase *objNode = (plMaxNodeBase*)fPB->GetReferenceTarget(fNodeID); + if (objNode && comp->IsTarget(objNode)) + SetWindowText(hNode, objNode->GetName()); + else + SetWindowText(hNode, "(none)"); +} + +bool plResponderCompNode::GetCompAndNode(plComponentBase*& comp, plMaxNodeBase*& node) +{ + if (!IValidate()) + { + comp = nil; + node = nil; + return false; + } + + plMaxNodeBase *savedComp = (plMaxNodeBase*)fPB->GetReferenceTarget(fCompID); + comp = savedComp ? savedComp->ConvertToComponent() : nil; + node = (plMaxNodeBase*)fPB->GetReferenceTarget(fNodeID); + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderGetComp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderGetComp.h new file mode 100644 index 00000000..00cac2a7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderGetComp.h @@ -0,0 +1,111 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "max.h" + +#include +#include + +class plComponentBase; +class IParamBlock2; +class plMaxNodeBase; + +class plResponderGetComp +{ +public: + typedef std::vector ClassIDs; + + static plResponderGetComp& Instance(); + + // Get a comp and node it is applied to from the user and store it in the PB + // + // pb - Your param block + // nodeID - ParamID of a reftarg for the node to be put in + // compID - ParamID of a reftarg for the comp to be put in + // classIDs - Optional list of component Class_ID's to allow (nil for all) + bool GetComp(IParamBlock2 *pb, int nodeID, int compID, ClassIDs *classIDs); + + // Get the comp stored in your PB + // Set convertTime to true to verify that the node can convert (ie, only use it after PreConvert) + plComponentBase *GetSavedComp(IParamBlock2 *pb, int nodeID, int compID, bool convertTime=false); + +protected: + IParamBlock2 *fPB; + int fNodeID; + int fCompID; + ClassIDs *fClassIDs; + + typedef std::set NodeSet; + +#if 0 + ULONG fLastAnimComp; + ULONG fLastAnimObj; + ULONG fLastSoundComp; + ULONG fLastSoundObj; +#endif + + void IFindCompsRecur(plMaxNodeBase *node, NodeSet& nodes); + void ILoadNodes(plMaxNodeBase *compNode, HWND hDlg); + + static BOOL CALLBACK ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); +}; + +class plResponderCompNode +{ +public: + typedef std::vector ClassIDs; + +protected: + IParamBlock2 *fPB; + int fNodeID; + int fCompID; + int fCompResID; + int fNodeResID; + ClassIDs fCompCIDs; + +// typedef std::set NodeSet; + void IUpdateCompButton(HWND hWnd); + void IUpdateNodeButton(HWND hWnd); + + bool IValidate(); + +public: + + // Get a comp and node it is applied to from the user and store it in the PB + // + // pb - Your param block + // nodeID - ParamID of a reftarg for the node to be put in + // compID - ParamID of a reftarg for the comp to be put in + // classIDs - Optional list of component Class_ID's to allow (nil for all) + void Init(IParamBlock2 *pb, int compID, int nodeID, int compResID, int nodeResID, ClassIDs *compCIDs); + void InitDlg(HWND hWnd); + + void CompButtonPress(HWND hWnd); + void NodeButtonPress(HWND hWnd); + + bool GetCompAndNode(plComponentBase*& comp, plMaxNodeBase*& node); +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderLink.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderLink.cpp new file mode 100644 index 00000000..02ffa0b9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderLink.cpp @@ -0,0 +1,1765 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plResponderLink.h" +#include "plResponderComponentPriv.h" +#include "resource.h" +#include "max.h" +#include "../MaxMain/plMaxNode.h" +#include "hsResMgr.h" +#include "plMiscComponents.h" +// Needed for the dialog +#include "../MaxMain/plMaxCFGFile.h" +#include "../plFile/hsFiles.h" +#include "../plAgeDescription/plAgeDescription.h" + +// Needed to create the message +#include "../plMessage/plLinkToAgeMsg.h" +#include "../pnNetCommon/pnNetCommon.h" +#include "../pnKeyedObject/plFixedKey.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plNetCommon/plNetCommon.h" +#include "../plNetCommon/plSpawnPointInfo.h" + +enum +{ + kLinkAge_DEAD, + kLinkSpawn_DEAD, + kLinkType_DEAD, + kLinkNexusLinkSpawn_DEAD, + kLinkAddToPersonalLinks_DEAD, + kLinkAddToNexusLinks_DEAD, + kLinkName_DEAD, + + kLinkingRule, + kLinkAgeFilename, + kLinkAgeInstanceName, + kLinkAgeSpawnPointTitle, + kLinkAgeSpawnPointName, + kLinkAgeLinkInAnimName, + kLinkParentAgeFilename, + kLinkAgeInstanceGuid, +}; + +#define kDefaultLinkInAnimName "LinkOut" + +class plResponderLinkProc; +extern plResponderLinkProc gResponderLinkProc; + + +ParamBlockDesc2 gResponderLinkBlock +( + kResponderLnkBlk, _T("linkCmd"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_LINK, IDS_COMP_CMD_PARAMS, 0, 0, &gResponderLinkProc, + + kLinkingRule, _T("linkingRule"), TYPE_INT, 0, 0, + p_default, plNetCommon::LinkingRules::kBasicLink, + end, + + kLinkAgeFilename, _T("ageFilename"), TYPE_STRING, 0, 0, + end, + + kLinkAgeInstanceName, _T("ageInstanceName"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_LINKAGEINSTANCENAME, + end, + + kLinkAgeSpawnPointName, _T("ageSpawnPoint"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_LINKSPAWNPOINT, + p_default, kDefaultSpawnPtName, + end, + + kLinkAgeSpawnPointTitle, _T("ageSpawnPointTitle"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_LINKSPAWNPOINTTITLE, + p_default, kDefaultSpawnPtTitle, + end, + + kLinkAgeLinkInAnimName, _T("ageLinkInAnimName"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_LINKAGELINKINANIMNAME, + p_default, kDefaultLinkInAnimName, + end, + + kLinkParentAgeFilename, _T("parentageFilename"), TYPE_STRING, 0, 0, + end, + + kLinkAgeInstanceGuid, _T("ageInstanceGUID"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_LINKAGEINSTANCEGUID, + end, + + end +); + +plResponderCmdLink& plResponderCmdLink::Instance() +{ + static plResponderCmdLink theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdLink::GetDesc() +{ + return &gResponderLinkBlock; +} + +const char *plResponderCmdLink::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + const char *ageName = pb->GetStr(kLinkAgeFilename); + sprintf(name, "Link (%s)", (ageName && *ageName != '\0') ? ageName : "none"); + + return name; +} + +void plResponderCmdLink::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2* pb) +{ + char * spawnPtName = pb->GetStr( kLinkAgeSpawnPointName ); + if ( !spawnPtName ) + { + // set defaults + pb->SetValue( kLinkAgeSpawnPointName, 0, kDefaultSpawnPtName ); + pb->SetValue( kLinkAgeSpawnPointTitle, 0, kDefaultSpawnPtTitle ); + } + else + { + char * spawnPtTitle = pb->GetStr( kLinkAgeSpawnPointTitle ); + if ( !spawnPtTitle ) + { + // set default title, or make same as name. + if ( strcmp( spawnPtName, kDefaultSpawnPtName )==0 ) + pb->SetValue( kLinkAgeSpawnPointTitle, 0, kDefaultSpawnPtTitle ); + else + pb->SetValue( kLinkAgeSpawnPointTitle, 0, spawnPtName ); + } + } +} + +plMessage *plResponderCmdLink::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + int linkingRule = pb->GetInt( kLinkingRule ); + + const char *ageFilename = pb->GetStr(kLinkAgeFilename); + const char *ageInstanceName = pb->GetStr(kLinkAgeInstanceName); + const char *ageSpawnPtName = pb->GetStr(kLinkAgeSpawnPointName); + const char *ageSpawnPtTitle = pb->GetStr(kLinkAgeSpawnPointTitle); + const char *ageLinkInAnimName = pb->GetStr(kLinkAgeLinkInAnimName); + const char *parentageFilename = pb->GetStr(kLinkParentAgeFilename); + const char *ageInstanceGuid = pb->GetStr(kLinkAgeInstanceGuid); + + if ( !ageFilename ) + throw "Must specify Age Filename"; + if ( !ageInstanceName ) + ageInstanceName=ageFilename; + if ( !ageSpawnPtName ) + { + ageSpawnPtName = kDefaultSpawnPtName; + if ( !ageSpawnPtTitle ) + ageSpawnPtTitle = kDefaultSpawnPtTitle; + } + if ( !ageSpawnPtTitle ) + ageSpawnPtTitle = ageSpawnPtName; + if ( !ageLinkInAnimName ) + ageLinkInAnimName = kDefaultLinkInAnimName; + + plLinkToAgeMsg *msg = TRACKED_NEW plLinkToAgeMsg; + msg->GetAgeLink()->SetLinkingRules( linkingRule ); + msg->GetAgeLink()->SetSpawnPoint( plSpawnPointInfo( ageSpawnPtTitle, ageSpawnPtName ) ); + msg->GetAgeLink()->GetAgeInfo()->SetAgeFilename( ageFilename ); + msg->GetAgeLink()->GetAgeInfo()->SetAgeInstanceName( ageInstanceName ); + if (ageInstanceGuid && strlen(ageInstanceGuid) > 0) + msg->GetAgeLink()->GetAgeInfo()->SetAgeInstanceGuid( &plUUID(ageInstanceGuid) ); + msg->SetLinkInAnimName( ageLinkInAnimName ); + if (parentageFilename) + { + if (strcmp(parentageFilename, "") != 0) // is our special string to denote no parent age + msg->GetAgeLink()->SetParentAgeFilename( parentageFilename ); + } + + return msg; +} + +class plResponderLinkProc : public ParamMap2UserDlgProc +{ +protected: + void ILoadLinkingRulesCombo(HWND hWnd, IParamBlock2* pb); + void ILoadAgeFilenamesCombo(HWND hWnd, IParamBlock2 *pb); + void ILoadParentAgeFilenamesCombo(HWND hWnd, IParamBlock2 *pb); + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + ILoadLinkingRulesCombo(hWnd, pm->GetParamBlock()); + ILoadAgeFilenamesCombo(hWnd, pm->GetParamBlock()); + ILoadParentAgeFilenamesCombo(hWnd, pm->GetParamBlock()); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + int sel = ComboBox_GetCurSel((HWND)lParam); + if (sel != CB_ERR) + { + if (LOWORD(wParam) == IDC_LINKINGRULE) + { + int data = ComboBox_GetItemData((HWND)lParam, sel); + pm->GetParamBlock()->SetValue(kLinkingRule, 0, data); + return TRUE; + } + else if (LOWORD(wParam) == IDC_LINKAGEFILENAME) + { + char buf[256]; + SendMessage((HWND)lParam, CB_GETLBTEXT, sel, (LPARAM)buf); + pm->GetParamBlock()->SetValue(kLinkAgeFilename, 0, buf); + return TRUE; + } + else if (LOWORD(wParam) == IDC_PARENTAGEFILENAME) + { + char buf[256]; + SendMessage((HWND)lParam, CB_GETLBTEXT, sel, (LPARAM)buf); + pm->GetParamBlock()->SetValue(kLinkParentAgeFilename, 0, buf); + return TRUE; + } + } + } + break; + } + return FALSE; + } + void DeleteThis() {} +}; +static plResponderLinkProc gResponderLinkProc; + +static int ComboBox_AddStringData(HWND hCombo, const char* str, int data) +{ + int idx = ComboBox_AddString(hCombo, str); + ComboBox_SetItemData(hCombo, idx, data); + return idx; +} + +void plResponderLinkProc::ILoadLinkingRulesCombo(HWND hWnd, IParamBlock2* pb) +{ + HWND hType = GetDlgItem(hWnd, IDC_LINKINGRULE); + + ComboBox_ResetContent(hType); + + int type = pb->GetInt(kLinkingRule); + + using namespace plNetCommon::LinkingRules; + + ComboBox_AddStringData(hType, LinkingRuleStr(kBasicLink), kBasicLink); + ComboBox_AddStringData(hType, LinkingRuleStr(kOriginalBook), kOriginalBook); + ComboBox_AddStringData(hType, LinkingRuleStr(kSubAgeBook), kSubAgeBook); + ComboBox_AddStringData(hType, LinkingRuleStr(kOwnedBook), kOwnedBook); + ComboBox_AddStringData(hType, LinkingRuleStr(kVisitBook), kVisitBook); + ComboBox_AddStringData(hType, LinkingRuleStr(kChildAgeBook), kChildAgeBook); + + int count = ComboBox_GetCount(hType); + for (int i = 0; i < count; i++) + { + if (type == ComboBox_GetItemData(hType, i)) + { + ComboBox_SetCurSel(hType, i); + break; + } + } +} + +void plResponderLinkProc::ILoadAgeFilenamesCombo(HWND hWnd, IParamBlock2 *pb) +{ + HWND hAge = GetDlgItem(hWnd, IDC_LINKAGEFILENAME); + + // Reset the combo and add the default option + SendMessage(hAge, CB_RESETCONTENT, 0, 0); + + // Get the path to the description folder + char agePath[MAX_PATH]; + const char *plasmaPath = plMaxConfig::GetClientPath(); + if (!plasmaPath) + return; + strcpy(agePath, plasmaPath); + strcat(agePath, plAgeDescription::kAgeDescPath); + + const char *savedName = pb->GetStr(kLinkAgeFilename); + if (!savedName) + savedName = ""; + + // Iterate through the age descriptions + hsFolderIterator ageFolder(agePath); + while (ageFolder.NextFileSuffix(".age")) + { + char ageFile[MAX_PATH]; + ageFolder.GetPathAndName(ageFile); + + char name[_MAX_FNAME]; + _splitpath(ageFile, nil, nil, name, nil); + + int idx = SendMessage(hAge, CB_ADDSTRING, 0, (LPARAM)name); + + if (hsStrEQ(name, savedName)) + SendMessage(hAge, CB_SETCURSEL, idx, 0); + } +} + +void plResponderLinkProc::ILoadParentAgeFilenamesCombo(HWND hWnd, IParamBlock2 *pb) +{ + HWND hAge = GetDlgItem(hWnd, IDC_PARENTAGEFILENAME); + + // Reset the combo and add the default option + SendMessage(hAge, CB_RESETCONTENT, 0, 0); + SendMessage(hAge, CB_ADDSTRING, 0, (LPARAM)""); + + // Get the path to the description folder + char agePath[MAX_PATH]; + const char *plasmaPath = plMaxConfig::GetClientPath(); + if (!plasmaPath) + return; + strcpy(agePath, plasmaPath); + strcat(agePath, plAgeDescription::kAgeDescPath); + + const char *savedName = pb->GetStr(kLinkParentAgeFilename); + if (!savedName) + savedName = ""; + + // Iterate through the age descriptions + hsFolderIterator ageFolder(agePath); + while (ageFolder.NextFileSuffix(".age")) + { + char ageFile[MAX_PATH]; + ageFolder.GetPathAndName(ageFile); + + char name[_MAX_FNAME]; + _splitpath(ageFile, nil, nil, name, nil); + + int idx = SendMessage(hAge, CB_ADDSTRING, 0, (LPARAM)name); + + if (hsStrEQ(name, savedName)) + SendMessage(hAge, CB_SETCURSEL, idx, 0); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +#include "plComponentBase.h" + +// Needed for message creation +#include "../plModifier/plResponderModifier.h" + +enum +{ + kEnable, + kEnableNode, + kEnableResponder, +}; + +class plResponderEnableProc; +extern plResponderEnableProc gResponderEnableProc; + +ParamBlockDesc2 gResponderEnableBlock +( + kResponderEnableMsgBlk, _T("enableCmd"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_ENABLE, IDS_COMP_CMD_PARAMS, 0, 0, &gResponderEnableProc, + + kEnable, _T("enable"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_ENABLE_CHECK, + p_default, TRUE, + end, + + kEnableNode, _T("node"), TYPE_REFTARG, 0, 0, + end, + + kEnableResponder, _T("responder"), TYPE_REFTARG, P_NO_REF, 0, + end, + + end +); + +plResponderCmdEnable& plResponderCmdEnable::Instance() +{ + static plResponderCmdEnable theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdEnable::GetDesc() +{ + return &gResponderEnableBlock; +} + +#include "plResponderGetComp.h" + +const char *plResponderCmdEnable::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + plComponentBase *comp = plResponderGetComp::Instance().GetSavedComp(pb, kEnableNode, kEnableResponder, true); + sprintf(name, "Responder Enable (%s)", comp ? comp->GetINode()->GetName() : "none"); + + return name; +} + +plMessage *plResponderCmdEnable::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plComponentBase *comp = plResponderGetComp::Instance().GetSavedComp(pb, kEnableNode, kEnableResponder, true); + if (!comp) + throw "No responder component specified"; + + BOOL enable = pb->GetInt(kEnable); + + plResponderEnableMsg *msg = TRACKED_NEW plResponderEnableMsg; + msg->fEnable = (enable != false); + + plMaxNodeBase *respondNode = (plMaxNodeBase*)pb->GetReferenceTarget(kEnableNode); + plKey responderKey = Responder::GetKey(comp, respondNode); + msg->AddReceiver(responderKey); + + return msg; +} + +class plResponderEnableProc : public ParamMap2UserDlgProc +{ +protected: + void IUpdateButton(HWND hWnd, IParamBlock2 *pb) + { + HWND hComp = GetDlgItem(hWnd, IDC_RESPONDER_BUTTON); + plComponentBase *comp = plResponderGetComp::Instance().GetSavedComp(pb, kEnableNode, kEnableResponder); + if (comp) + SetWindowText(hComp, comp->GetINode()->GetName()); + else + SetWindowText(hComp, "(none)"); + } + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = pm->GetParamBlock(); + + IUpdateButton(hWnd, pb); + } + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_RESPONDER_BUTTON) + { + IParamBlock2 *pb = pm->GetParamBlock(); + + // If the responder component is hosed, remove it so the plResponderGetComp won't get bogus info + if (!plResponderGetComp::Instance().GetSavedComp(pb, kEnableNode, kEnableResponder)) + { + ReferenceTarget *empty = nil; + pb->SetValue(kEnableResponder, 0, empty); + } + + plResponderGetComp::ClassIDs cids; + cids.push_back(RESPONDER_CID); + plResponderGetComp::Instance().GetComp(pb, kEnableNode, kEnableResponder, &cids); + IUpdateButton(hWnd, pb); + } + break; + } + return FALSE; + } + void DeleteThis() {} +}; +static plResponderEnableProc gResponderEnableProc; + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +#include "../pnMessage/plEnableMsg.h" + +enum +{ + kEnablePhys, + kEnablePhysNode, +}; + +ParamBlockDesc2 gPhysicalEnableBlock +( + kResponderPhysEnableBlk, _T("physEnableCmd"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_ENABLE_PHYS, IDS_COMP_CMD_PARAMS, 0, 0, NULL, + + kEnablePhys, _T("enable"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_ENABLE_CHECK, + p_default, TRUE, + end, + + kEnablePhysNode, _T("node"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_NODE_BUTTON, + end, + + end +); + +plResponderCmdPhysEnable& plResponderCmdPhysEnable::Instance() +{ + static plResponderCmdPhysEnable theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdPhysEnable::GetDesc() +{ + return &gPhysicalEnableBlock; +} + +const char *plResponderCmdPhysEnable::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + INode *node = pb->GetINode(kEnablePhysNode); + sprintf(name, "Phys Enable (%s)", node ? node->GetName() : "none"); + + return name; +} + +#include "../plMessage/plSimStateMsg.h" +plMessage *plResponderCmdPhysEnable::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plMaxNode* physNode = (plMaxNode*)pb->GetINode(kEnablePhysNode); + if (!physNode) + throw "No physical selected"; + + BOOL enable = pb->GetInt(kEnable); + + plEnableMsg* enableMsg = TRACKED_NEW plEnableMsg; + enableMsg->SetCmd(plEnableMsg::kPhysical); + enableMsg->SetCmd(enable ? plEnableMsg::kEnable : plEnableMsg::kDisable); + enableMsg->AddReceiver(physNode->GetKey()); + + return enableMsg; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +enum +{ + kOneShotComp, + kOneShotNode, +}; + +class plResponderOneShotProc; +extern plResponderOneShotProc gResponderOneShotProc; + +ParamBlockDesc2 gResponderOneShotBlock +( + kResponderOneShotMsgBlk, _T("oneShotCmd"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_ONESHOT, IDS_COMP_CMD_PARAMS, 0, 0, &gResponderOneShotProc, + + kOneShotComp, _T("oneShotComp"), TYPE_REFTARG, 0, 0, + end, + + kOneShotNode, _T("oneShotNode"), TYPE_REFTARG, 0, 0, + end, + + end +); + +plResponderCmdOneShot& plResponderCmdOneShot::Instance() +{ + static plResponderCmdOneShot theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdOneShot::GetDesc() +{ + return &gResponderOneShotBlock; +} + +const char *plResponderCmdOneShot::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + plMaxNode *node = (plMaxNode*)pb->GetReferenceTarget(kOneShotComp); + sprintf(name, "One Shot (%s)", node ? node->GetName() : "none"); + + return name; +} + +#include "../plMessage/plOneShotMsg.h" +#include "plOneShotComponent.h" + +plMessage *plResponderCmdOneShot::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plResponderCompNode compNode; + plResponderCompNode::ClassIDs cids; + cids.push_back(ONESHOTCLASS_ID); + compNode.Init(pb, kOneShotComp, kOneShotNode, IDC_RESPONDER_COMP, IDC_RESPONDER_NODE, &cids); + + plComponentBase *comp; + plMaxNodeBase *targNode; + if (compNode.GetCompAndNode(comp, targNode)) + { + plKey oneShotKey = OneShotComp::GetOneShotKey(comp, targNode); + if (!oneShotKey) + throw "One-shot component didn't convert"; + + plOneShotMsg *msg = TRACKED_NEW plOneShotMsg; + msg->AddReceiver(oneShotKey); + return msg; + } + else + throw "No one-shot component specified"; +} + +#include "../plMessage/plOneShotCallbacks.h" + +void plResponderCmdOneShot::CreateWait(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2 *pb, ResponderWaitInfo& waitInfo) +{ + plOneShotMsg *oneShotMsg = plOneShotMsg::ConvertNoRef(waitInfo.msg); + hsAssert(oneShotMsg, "Bad One-Shot message"); + if (oneShotMsg) + oneShotMsg->fCallbacks->AddCallback(waitInfo.point, waitInfo.receiver, waitInfo.callbackUser); +} + +class plResponderOneShotProc : public ParamMap2UserDlgProc +{ +protected: + plResponderCompNode fCompNode; + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = pm->GetParamBlock(); + + plResponderCompNode::ClassIDs cids; + cids.push_back(ONESHOTCLASS_ID); + fCompNode.Init(pb, kOneShotComp, kOneShotNode, IDC_RESPONDER_COMP, IDC_RESPONDER_NODE, &cids); + + fCompNode.InitDlg(hWnd); + } + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_RESPONDER_COMP) + { + fCompNode.CompButtonPress(hWnd); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_RESPONDER_NODE) + { + fCompNode.NodeButtonPress(hWnd); + return TRUE; + } + break; + } + + return FALSE; + } + void DeleteThis() {} +}; +static plResponderOneShotProc gResponderOneShotProc; + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +ParamBlockDesc2 gResponderNotifyBlock +( + kResponderNotifyMsgBlk, _T("notifyCmd"), 0, NULL, 0, + + end +); + +plResponderCmdNotify& plResponderCmdNotify::Instance() +{ + static plResponderCmdNotify theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdNotify::GetDesc() +{ + return &gResponderNotifyBlock; +} + +#include "../pnMessage/plNotifyMsg.h" + +plMessage *plResponderCmdNotify::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plNotifyMsg *msg = TRACKED_NEW plNotifyMsg; + msg->SetBCastFlag(plMessage::kNetPropagate, 0); + msg->SetState(1.0); // set to positive state + msg->AddCallbackEvent(1); // create an event record with callback + return msg; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +#include "plCameraComponents.h" + +class plResponderActivatorEnableProc; +extern plResponderActivatorEnableProc gResponderActivatorEnableProc; + +enum { kActivatorComp, kActivatorEnable }; + +ParamBlockDesc2 gResponderActivatorEnableBlock +( + kResponderActivatorEnableBlk, _T("detectorEnable"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_ENABLE, IDS_COMP_CMD_PARAMS, 0, 0, &gResponderActivatorEnableProc, + + kActivatorComp, _T("activatorComp"), TYPE_INODE, 0, 0, + end, + + kActivatorEnable, _T("enable"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_ENABLE_CHECK, + p_default, TRUE, + end, + + end +); + +plResponderCmdDetectorEnable& plResponderCmdDetectorEnable::Instance() +{ + static plResponderCmdDetectorEnable theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdDetectorEnable::GetDesc() +{ + return &gResponderActivatorEnableBlock; +} + +const char *plResponderCmdDetectorEnable::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + plMaxNode *node = (plMaxNode*)pb->GetINode(kActivatorComp); + sprintf(name, "Enable Detector (%s)", node ? node->GetName() : "none"); + + return name; +} + +#include "../pnMessage/plEnableMsg.h" +#include "plActivatorBaseComponent.h" +#include "plVolumeGadgetComponent.h" +#include "plNavigableComponents.h" + +plMessage *plResponderCmdDetectorEnable::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plMaxNode *detectNode = (plMaxNode*)pb->GetINode(kActivatorComp); + if (!detectNode) + throw "No detector component specified"; + + plComponentBase *comp = detectNode->ConvertToComponent(); + if (!comp || (comp->CanConvertToType(ACTIVATOR_BASE_CID) == 0 && comp->ClassID() != NAV_LADDER_CID && comp->ClassID() != CAM_REGION_CID)) + throw "Not a detector component"; + + BOOL enable = pb->GetInt(kActivatorEnable); + + // Just stuffing this in here because I'm lazy + if (comp->ClassID() == CAM_REGION_CID) + { + plEnableMsg* enableMsg = TRACKED_NEW plEnableMsg; + enableMsg->SetCmd(plEnableMsg::kPhysical); + enableMsg->SetCmd(enable ? plEnableMsg::kEnable : plEnableMsg::kDisable); + + for (int i = 0; i < comp->NumTargets(); i++) + enableMsg->AddReceiver(comp->GetTarget(i)->GetKey()); + + return enableMsg; + } + + plEnableMsg *msg = TRACKED_NEW plEnableMsg; + msg->SetCmd(enable ? plEnableMsg::kEnable : plEnableMsg::kDisable); + + hsTArray keys; + + if (comp->CanConvertToType(ACTIVATOR_BASE_CID)) + { + // Add each activator mod to the receiver list + plActivatorBaseComponent *activatorComp = (plActivatorBaseComponent*)comp; + const plActivatorBaseComponent::LogicKeys& logicKeys = activatorComp->GetLogicKeys(); + plActivatorBaseComponent::LogicKeys::const_iterator it; + for (it = logicKeys.begin(); it != logicKeys.end(); it++) + { + plKey key = it->second; + keys.Append(key); + } + // check to see if this is a region sensor and if so if it has exit and / or enter activators + if (activatorComp->HasLogicOut()) + { + plVolumeGadgetComponent *volComp = (plVolumeGadgetComponent*)comp; + const plActivatorBaseComponent::LogicKeys& logicKeys = volComp->GetLogicOutKeys(); + plActivatorBaseComponent::LogicKeys::const_iterator it; + for (it = logicKeys.begin(); it != logicKeys.end(); it++) + { + plKey key = it->second; + keys.Append(key); + } + } + } + else if (comp->ClassID() == NAV_LADDER_CID) + { + plAvLadderComponent *ladderComp = (plAvLadderComponent*)comp; + keys = ladderComp->GetLadderModKeys(); + } + + msg->AddReceivers(keys); + + return msg; +} + +#include "plPickNode.h" + +class plResponderActivatorEnableProc : public ParamMap2UserDlgProc +{ +protected: + void IUpdateButton(IParamBlock2 *pb, HWND hWnd) + { + INode *node = pb->GetINode(kActivatorComp); + if (node) + SetDlgItemText(hWnd, IDC_RESPONDER_BUTTON, node->GetName()); + else + SetDlgItemText(hWnd, IDC_RESPONDER_BUTTON, "(none)"); + } + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + IUpdateButton(pm->GetParamBlock(), hWnd); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_RESPONDER_BUTTON) + { + if (plPick::DetectorEnable(pm->GetParamBlock(), kActivatorComp, true)) + IUpdateButton(pm->GetParamBlock(), hWnd); + return TRUE; + } + break; + } + + return FALSE; + } + void DeleteThis() {} +}; +static plResponderActivatorEnableProc gResponderActivatorEnableProc; + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +class plResponderXRegionProc; +extern plResponderXRegionProc gResponderXRegionProc; + +enum { kXRegionComp, kXRegionType, kXRegionNode }; + +ParamBlockDesc2 gResponderXRegionBlock +( + kResponderXRegionBlk, _T("xRegion"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_ONESHOT, IDS_COMP_CMD_PARAMS, 0, 0, &gResponderXRegionProc, + + kXRegionComp, _T("xRegionComp"), TYPE_INODE, 0, 0, + end, + + kXRegionNode, _T("xRegionNode"), TYPE_INODE, 0, 0, + end, + + kXRegionType, _T("type"), TYPE_INT, 0, 0, + end, + + end +); + +// Old types kept for backwards compatibility +enum +{ + kRespondXRegionClear=25, + kRespondXRegionRelease, +}; + +plResponderCmdXRegion& plResponderCmdXRegion::Instance() +{ + static plResponderCmdXRegion theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdXRegion::GetDesc() +{ + return &gResponderXRegionBlock; +} + +IParamBlock2 *plResponderCmdXRegion::CreatePB(int idx) +{ + IParamBlock2 *pb = CreateParameterBlock2(&gResponderXRegionBlock, nil); + + int type = kRespondXRegionClear; + if (idx == 1) + type = kRespondXRegionRelease; + + pb->SetValue(kXRegionType, 0, type); + return pb; +} + +int plResponderCmdXRegion::NumTypes() +{ + return 2; +} + +const char *plResponderCmdXRegion::GetCategory(int idx) +{ + return "Exclude Region"; +} + +const char *plResponderCmdXRegion::GetName(int idx) +{ + if (idx == 0) + return "Clear"; + else + return "Release"; +} + +const char *plResponderCmdXRegion::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + int type = pb->GetInt(kXRegionType); + INode *node = pb->GetINode(kXRegionComp); + + if (type == kRespondXRegionClear) + sprintf(name, "XRegion Clear (%s)", node ? node->GetName() : "none"); + else + sprintf(name, "XRegion Release (%s)", node ? node->GetName() : "none"); + + return name; +} + +#include "../plMessage/plExcludeRegionMsg.h" +#include "plExcludeRegionComponent.h" + +plMessage *plResponderCmdXRegion::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plResponderCompNode compNode; + plResponderCompNode::ClassIDs cids; + cids.push_back(XREGION_CID); + compNode.Init(pb, kXRegionComp, kXRegionNode, IDC_RESPONDER_COMP, IDC_RESPONDER_NODE, &cids); + + plComponentBase *comp; + plMaxNodeBase *targNode; + if (compNode.GetCompAndNode(comp, targNode)) + { + plExcludeRegionComponent *xComp = (plExcludeRegionComponent*)comp; + + plExcludeRegionMsg *msg = TRACKED_NEW plExcludeRegionMsg; + + int type = pb->GetInt(kXRegionType); + switch (type) + { + case kRespondXRegionClear: + msg->SetCmd(plExcludeRegionMsg::kClear); + break; + case kRespondXRegionRelease: + msg->SetCmd(plExcludeRegionMsg::kRelease); + break; + } + + msg->AddReceiver(xComp->GetKey((plMaxNode*)targNode)); + + return msg; + } + else + throw "No exclude region component specified"; +} + +class plResponderXRegionProc : public ParamMap2UserDlgProc +{ +protected: + plResponderCompNode fCompNode; + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = pm->GetParamBlock(); + + plResponderCompNode::ClassIDs cids; + cids.push_back(XREGION_CID); + fCompNode.Init(pb, kXRegionComp, kXRegionNode, IDC_RESPONDER_COMP, IDC_RESPONDER_NODE, &cids); + fCompNode.InitDlg(hWnd); + } + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_RESPONDER_COMP) + { + fCompNode.CompButtonPress(hWnd); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_RESPONDER_NODE) + { + fCompNode.NodeButtonPress(hWnd); + return TRUE; + } + break; + } + + return FALSE; + } + void DeleteThis() {} +}; +static plResponderXRegionProc gResponderXRegionProc; + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +class plResponderCameraTransitionProc; +extern plResponderCameraTransitionProc gResponderCameraTransitionProc; + +enum +{ kCameraObj, + kPopCamera, +}; + +ParamBlockDesc2 gResponderCameraTransitionBlock +( + kResponderCameraTransitionBlk, _T("camera"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_CAMERA, IDS_COMP_CMD_PARAMS, 0, 0, NULL, + + kCameraObj, _T("CameraObj"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_COMP_CAMERARGN_PICKSTATE_BASE, + p_sclassID, CAMERA_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_BASE, + end, + + kPopCamera, _T("enable"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK1, + p_default, FALSE, + end, + + end +); + +plResponderCmdCamTransition& plResponderCmdCamTransition::Instance() +{ + static plResponderCmdCamTransition theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdCamTransition::GetDesc() +{ + return &gResponderCameraTransitionBlock; +} + +const char *plResponderCmdCamTransition::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + INode *node = pb->GetINode(kCameraObj); + sprintf(name, "Cam Trans (%s)", node ? node->GetName() : "none"); + + return name; +} + +#include "../pnMessage/plCameraMsg.h" +#include "plCameraComponents.h" + +plMessage *plResponderCmdCamTransition::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plMaxNode *pCamNode = (plMaxNode*)pb->GetINode(kCameraObj); + if (!pCamNode) + throw "No Camera Specified"; + hsBool fail = true; + int count = pCamNode->NumAttachedComponents(); + for (UInt32 x = 0; x < count; x++) + { + plComponentBase *comp = ((plMaxNode*)pCamNode)->GetAttachedComponent(x); + if (comp->ClassID() == AUTOCAM_CID || + comp->ClassID() == FPCAM_CID || + comp->ClassID() == FIXEDCAM_CID || + comp->ClassID() == CIRCLE_CAM_CID || + comp->ClassID() == RAIL_CAM_CID || + comp->ClassID() == FOLLOWCAM_CID ) + { + fail = false; + break; + } + } + if (fail) + throw "Invalid Camera Specified"; + + plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; + pMsg->SetBCastFlag(plMessage::kBCastByType); + + if(pCamNode->CanConvert()) + { + if (!pb->GetInt(kPopCamera)) + pMsg->SetCmd(plCameraMsg::kResponderTrigger); + + pMsg->SetCmd(plCameraMsg::kRegionPushCamera); + pMsg->SetNewCam(((plMaxNode*)pCamNode)->GetSceneObject()->GetKey()); + + int count = ((plMaxNode*)pCamNode)->NumAttachedComponents(); + for (UInt32 x = 0; x < count; x++) + { + plComponentBase *comp = ((plMaxNode*)pCamNode)->GetAttachedComponent(x); + if (comp->ClassID() == DEFAULTCAM_CID) + { + pMsg->SetCmd(plCameraMsg::kSetAsPrimary); + break; + } + } + } + + return pMsg; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +class plResponderCameraForceProc; +extern plResponderCameraForceProc gResponderCameraForceProc; + +enum +{ + kCamForce, +}; + +ParamBlockDesc2 gResponderCameraForceBlock +( + kResponderCameraForceBlk, _T("cameraForce"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_CAM_FORCE, IDS_COMP_CMD_PARAMS, 0, 0, NULL, + + kCamForce, _T("force"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 2, IDC_RADIO_THIRD, IDC_RADIO_FIRST, + p_vals, plResponderCmdCamForce::kForce3rd, plResponderCmdCamForce::kResume1st, + p_default, plResponderCmdCamForce::kForce3rd, + end, + + end +); + +plResponderCmdCamForce& plResponderCmdCamForce::Instance() +{ + static plResponderCmdCamForce theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdCamForce::GetDesc() +{ + return &gResponderCameraForceBlock; +} + +const char *plResponderCmdCamForce::GetInstanceName(IParamBlock2 *pb) +{ + if (pb->GetInt(kCamForce) == kForce3rd) + return "Cam Force 3rd"; + else + return "Cam Resume 1st"; +} + +plMessage *plResponderCmdCamForce::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plCameraMsg* msg = TRACKED_NEW plCameraMsg; + msg->SetBCastFlag(plMessage::kBCastByType); + msg->SetBCastFlag(plMessage::kNetPropagate, false); + + if (pb->GetInt(kCamForce) == kForce3rd) + msg->SetCmd(plCameraMsg::kResponderSetThirdPerson); + else + msg->SetCmd(plCameraMsg::kResponderUndoThirdPerson); + + return msg; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +enum { kDelayTime }; + +ParamBlockDesc2 gResponderDelayBlock +( + kResponderDelayBlk, _T("delay"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_DELAY, IDS_COMP_CMD_PARAMS, 0, 0, NULL, + + kDelayTime, _T("delay"), TYPE_FLOAT, 0, 0, + p_default, 1.0f, + p_range, 0.01f, 500.0f, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_DELAY_EDIT, IDC_DELAY_SPIN, .1f, + end, + + end +); + +plResponderCmdDelay& plResponderCmdDelay::Instance() +{ + static plResponderCmdDelay theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdDelay::GetDesc() +{ + return &gResponderDelayBlock; +} + +const char *plResponderCmdDelay::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + sprintf(name, "Delay (%.1f sec)", pb->GetFloat(kDelayTime)); + return name; +} + +#include "../plMessage/plTimerCallbackMsg.h" + +plMessage *plResponderCmdDelay::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + float time = pb->GetFloat(kDelayTime); + + plTimerCallbackMsg *msg = TRACKED_NEW plTimerCallbackMsg; + msg->fTime = time; + msg->fID = UInt32(-1); + + return msg; +} + +void plResponderCmdDelay::CreateWait(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2 *pb, ResponderWaitInfo& waitInfo) +{ + plTimerCallbackMsg *timerMsg = plTimerCallbackMsg::ConvertNoRef(waitInfo.msg); + hsAssert(timerMsg, "Somebody is crazy"); + + if (timerMsg->fID != UInt32(-1)) + { + pErrMsg->Set(true, + "Responder Delay", + "A delay command in responder '%s' on node '%s' has\n" + "more than one command waiting on it. That doesn't work.\n\n" + "However, you don't actually need two commands to wait on\n" + "the same command since the first command will automatically\n" + "delay any commands further down the list", + waitInfo.responderName, node->GetName()).Show(); + pErrMsg->Set(false); + } + else + { + timerMsg->fID = waitInfo.callbackUser; + timerMsg->AddReceiver(waitInfo.receiver); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +enum { kVisibilityNode, kVisibilityType, kVisibilityChildren }; + +ParamBlockDesc2 gResponderVisibilityBlock +( + kResponderVisibilityBlk, _T("Visibility"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_VISIBILITY, IDS_COMP_CMD_PARAMS, 0, 0, NULL, + + kVisibilityNode, _T("VisibilityNode"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_NODE_BUTTON, + end, + + kVisibilityType, _T("type"), TYPE_INT, 0, 0, + end, + + kVisibilityChildren, _T("children"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_CHECK_CHILDREN, + end, + + end +); + +enum +{ + kRespondVisibilityOn, + kRespondVisibilityOff, +}; + +plResponderCmdVisibility& plResponderCmdVisibility::Instance() +{ + static plResponderCmdVisibility theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdVisibility::GetDesc() +{ + return &gResponderVisibilityBlock; +} + +IParamBlock2 *plResponderCmdVisibility::CreatePB(int idx) +{ + IParamBlock2 *pb = CreateParameterBlock2(&gResponderVisibilityBlock, nil); + + int type = kRespondVisibilityOn; + if (idx == 1) + type = kRespondVisibilityOff; + + pb->SetValue(kVisibilityType, 0, type); + return pb; +} + +int plResponderCmdVisibility::NumTypes() +{ + return 2; +} + +const char *plResponderCmdVisibility::GetCategory(int idx) +{ + return "Visibility"; +} + +const char *plResponderCmdVisibility::GetName(int idx) +{ + if (idx == 0) + return "Visible"; + else + return "Invisible"; +} + +const char *plResponderCmdVisibility::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + int type = pb->GetInt(kVisibilityType); + INode *node = pb->GetINode(kVisibilityNode); + + if (type == kRespondVisibilityOn) + sprintf(name, "Visible (%s)", node ? node->GetName() : "none"); + else + sprintf(name, "Invisible (%s)", node ? node->GetName() : "none"); + + return name; +} + +#include "../pnMessage/plEnableMsg.h" + +static void AddChildKeysRecur(plMaxNode* node, plMessage* msg) +{ + if (!node) + return; + + plKey key = node->GetKey(); + if (key) + msg->AddReceiver(key); + + for (int i = 0; i < node->NumberOfChildren(); i++) + AddChildKeysRecur((plMaxNode*)node->GetChildNode(i), msg); +} + +plMessage *plResponderCmdVisibility::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plMaxNode* visNode = (plMaxNode*)pb->GetINode(kVisibilityNode); + if (visNode) + { + plEnableMsg* msg = TRACKED_NEW plEnableMsg; + msg->SetCmd(plEnableMsg::kDrawable); + + int type = pb->GetInt(kVisibilityType); + switch (type) + { + case kRespondVisibilityOn: + msg->SetCmd(plEnableMsg::kEnable); + break; + case kRespondVisibilityOff: + msg->SetCmd(plEnableMsg::kDisable); + break; + } + + if (pb->GetInt(kVisibilityChildren)) + { +// msg->SetBCastFlag(plMessage::kPropagateToChildren); + AddChildKeysRecur(visNode, msg); + } + else + msg->AddReceiver(visNode->GetKey()); + + return msg; + } + else + throw "No node chosen"; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +enum { kSubWorldNode, kSubWorldType }; + +class plResponderSubWorldProc; +extern plResponderSubWorldProc gResponderSubWorldProc; + +ParamBlockDesc2 gResponderSubWorldBlock +( + kResponderSubWorldBlk, _T("SubWorld"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_SUBWORLD, IDS_COMP_CMD_PARAMS, 0, 0, &gResponderSubWorldProc, + + kSubWorldNode, _T("SubWorldNode"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_NODE_BUTTON, + end, + + kSubWorldType, _T("type"), TYPE_INT, 0, 0, + end, + + end +); + +enum +{ + kRespondSubWorldEnter, + kRespondSubWorldExit, +}; + +plResponderCmdSubWorld& plResponderCmdSubWorld::Instance() +{ + static plResponderCmdSubWorld theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdSubWorld::GetDesc() +{ + return &gResponderSubWorldBlock; +} + +IParamBlock2 *plResponderCmdSubWorld::CreatePB(int idx) +{ + IParamBlock2 *pb = CreateParameterBlock2(&gResponderSubWorldBlock, nil); + + int type = kRespondSubWorldEnter; + if (idx == 1) + type = kRespondSubWorldExit; + + pb->SetValue(kSubWorldType, 0, type); + return pb; +} + +int plResponderCmdSubWorld::NumTypes() +{ + return 2; +} + +const char *plResponderCmdSubWorld::GetCategory(int idx) +{ + return "Local Avatar"; +} + +const char *plResponderCmdSubWorld::GetName(int idx) +{ + if (idx == 0) + return "Subworld Enter"; + else + return "Subworld Exit"; +} + +const char *plResponderCmdSubWorld::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + int type = pb->GetInt(kSubWorldType); + INode *node = pb->GetINode(kSubWorldNode); + + if (type == kRespondSubWorldEnter) + sprintf(name, "Subworld Enter (%s)", node ? node->GetName() : "none"); + else + sprintf(name, "Subworld Exit"); + //sprintf(name, "SubWorld Exit (%s)", node ? node->GetName() : "none"); + + return name; +} + +plMessage *plResponderCmdSubWorld::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plMaxNode* swNode = (plMaxNode*)pb->GetINode(kSubWorldNode); + int type = pb->GetInt(kSubWorldType); + + plKey worldKey; + plKey nilKey; + plKey nilKey2; + + switch (type) + { + case kRespondSubWorldEnter: + if (swNode) + { + worldKey = swNode->GetKey(); + } + else + throw "No node chosen"; + + break; + case kRespondSubWorldExit: + // worldKey is already nil key so leave it that way + break; + } + + plSubWorldMsg * swMsg = TRACKED_NEW plSubWorldMsg(nilKey, nilKey2, worldKey); + + return swMsg; +} + +class plResponderSubWorldProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = pm->GetParamBlock(); + int type = pb->GetInt(kSubWorldType); + + HWND nButton = GetDlgItem(hWnd, IDC_NODE_BUTTON); + //HWND sEnterText = GetDlgItem(hWnd, IDC_SUBWORLD_ENTER); + HWND sExitText = GetDlgItem(hWnd, IDC_SUBWORLD_EXIT); + + BOOL isEnter = (type == kRespondSubWorldEnter) ? TRUE : FALSE; + + ShowWindow(nButton, (isEnter) ? SW_SHOW : SW_HIDE); + //ShowWindow(sEnterText,(isEnter) ? SW_SHOW : SW_HIDE); + ShowWindow(sExitText, (isEnter) ? SW_HIDE : SW_SHOW); + } + return TRUE; + + case WM_COMMAND: + break; + } + + return FALSE; + } + void DeleteThis() {} +}; + +static plResponderSubWorldProc gResponderSubWorldProc; +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +#include "../pfMessage/plArmatureEffectMsg.h" +#include "../plAvatar/plArmatureEffects.h" + +class plResponderFootSurfaceProc; +extern plResponderFootSurfaceProc gResponderFootSurfaceProc; + +enum +{ + kSurface, +}; + +ParamBlockDesc2 gResponderFootSurfaceBlock +( + kResponderFootSurfaceBlk, _T("FootSurface"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_FOOT_SURFACE, IDS_COMP_CMD_PARAMS, 0, 0, &gResponderFootSurfaceProc, + + kSurface, _T("Surface"), TYPE_INT, 0, 0, + p_default, plArmatureEffectsMgr::kFootDirt, + end, + + end +); + +plResponderCmdFootSurface& plResponderCmdFootSurface::Instance() +{ + static plResponderCmdFootSurface theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdFootSurface::GetDesc() +{ + return &gResponderFootSurfaceBlock; +} + +const char *plResponderCmdFootSurface::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + sprintf(name, "Foot Surface (%s)", plArmatureEffectsMgr::SurfaceStrings[pb->GetInt(ParamID(kSurface))]); + return name; +} + +plMessage *plResponderCmdFootSurface::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plArmatureEffectStateMsg* msg = TRACKED_NEW plArmatureEffectStateMsg; + msg->SetBCastFlag(plMessage::kPropagateToModifiers); + msg->SetBCastFlag(plMessage::kNetPropagate); + msg->fSurface = pb->GetInt(kSurface); + + return msg; +} + +class plResponderFootSurfaceProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IParamBlock2 *pb = pm->GetParamBlock(); + HWND hCB = GetDlgItem(hWnd, IDC_COMP_RESPOND_FOOT_SURFACE); + int i; + + switch (msg) + { + case WM_INITDIALOG: + for (i = 0; i < plArmatureEffectsMgr::kMaxSurface; i++) + ComboBox_AddString(hCB, plArmatureEffectsMgr::SurfaceStrings[i]); + + ComboBox_SetCurSel(hCB, pb->GetInt(ParamID(kSurface))); + + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_COMP_RESPOND_FOOT_SURFACE) + pb->SetValue(ParamID(kSurface), 0, ComboBox_GetCurSel(hCB)); + } + return FALSE; + } + void DeleteThis() {} +}; +static plResponderFootSurfaceProc gResponderFootSurfaceProc; + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +#include "plMultistageBehComponent.h" + +class plResponderMultistageProc; +extern plResponderMultistageProc gResponderMultistageProc; + +enum { kMultistageComp, kMultistageNode }; + +ParamBlockDesc2 gResponderMultistageBlock +( + kResponderMultistageBlk, _T("multistage"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_ONESHOT, IDS_COMP_CMD_PARAMS, 0, 0, &gResponderMultistageProc, + + kMultistageComp, _T("comp"), TYPE_INODE, 0, 0, + end, + + kMultistageNode, _T("node"), TYPE_INODE, 0, 0, + end, + + end +); + +plResponderCmdMultistage& plResponderCmdMultistage::Instance() +{ + static plResponderCmdMultistage theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdMultistage::GetDesc() +{ + return &gResponderMultistageBlock; +} + +const char *plResponderCmdMultistage::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + INode *node = pb->GetINode(kMultistageComp); + sprintf(name, "Multistage (%s)", node ? node->GetName() : "none"); + return name; +} + +plMessage *plResponderCmdMultistage::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + plResponderCompNode compNode; + plResponderCompNode::ClassIDs cids; + cids.push_back(MULTISTAGE_BEH_CID); + compNode.Init(pb, kMultistageComp, kMultistageNode, IDC_RESPONDER_COMP, IDC_RESPONDER_NODE, &cids); + + plComponentBase *comp; + plMaxNodeBase *targNode; + if (compNode.GetCompAndNode(comp, targNode)) + { + plNotifyMsg* msg = TRACKED_NEW plNotifyMsg; + msg->SetState(1.f); + + // Will actually be set to the player key at runtime + plKey playerKey, self; + msg->AddCollisionEvent(true, playerKey, self); + + plKey multiKey = MultiStageBeh::GetMultiStageBehKey(comp, targNode); + msg->AddReceiver(multiKey); + + return msg; + } + else + throw "No Multistage component specified"; +} + +class plResponderMultistageProc : public ParamMap2UserDlgProc +{ +protected: + plResponderCompNode fCompNode; + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = pm->GetParamBlock(); + + plResponderCompNode::ClassIDs cids; + cids.push_back(MULTISTAGE_BEH_CID); + fCompNode.Init(pb, kMultistageComp, kMultistageNode, IDC_RESPONDER_COMP, IDC_RESPONDER_NODE, &cids); + fCompNode.InitDlg(hWnd); + } + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_RESPONDER_COMP) + { + fCompNode.CompButtonPress(hWnd); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_RESPONDER_NODE) + { + fCompNode.NodeButtonPress(hWnd); + return TRUE; + } + break; + } + + return FALSE; + } + void DeleteThis() {} +}; +static plResponderMultistageProc gResponderMultistageProc; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderLink.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderLink.h new file mode 100644 index 00000000..5dca7abc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderLink.h @@ -0,0 +1,236 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plResponderCmd.h" +#include "../pnKeyedObject/plKey.h" + +class plResponderCmdLink : public plResponderCmd +{ +public: + static plResponderCmdLink& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes() { return 1; } + virtual const char *GetCategory(int idx) { return nil; } + virtual const char *GetName(int idx) { return "Link"; } + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual void SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2* pb); + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + +class plResponderCmdEnable : public plResponderCmd +{ +public: + static plResponderCmdEnable& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes() { return 1; } + virtual const char *GetCategory(int idx) { return "Enable/Disable"; } + virtual const char *GetName(int idx) { return "Responder"; } + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + +class plResponderCmdPhysEnable : public plResponderCmd +{ +public: + static plResponderCmdPhysEnable& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes() { return 1; } + virtual const char *GetCategory(int idx) { return "Enable/Disable"; } + virtual const char *GetName(int idx) { return "Physical"; } + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + +class plResponderCmdOneShot : public plResponderCmd +{ +public: + static plResponderCmdOneShot& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes() { return 1; } + virtual const char *GetCategory(int idx) { return nil; } + virtual const char *GetName(int idx) { return "One Shot"; } + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); + + virtual bool IsWaitable(IParamBlock2 *pb) { return true; } + virtual void CreateWait(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2 *pb, ResponderWaitInfo& waitInfo); +}; + +class plResponderCmdNotify : public plResponderCmd +{ +public: + static plResponderCmdNotify& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes() { return 1; } + virtual const char *GetCategory(int idx) { return nil; } + virtual const char *GetName(int idx) { return "Notify Triggerer"; } + virtual const char *GetInstanceName(IParamBlock2 *pb) { return GetName(0); } + + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + +class plResponderCmdDetectorEnable : public plResponderCmd +{ +public: + static plResponderCmdDetectorEnable& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes() { return 1; } + virtual const char *GetCategory(int idx) { return "Enable/Disable"; } + virtual const char *GetName(int idx) { return "Detector"; } + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + +class plResponderCmdXRegion : public plResponderCmd +{ +public: + static plResponderCmdXRegion& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes(); + virtual const char *GetCategory(int idx); + virtual const char *GetName(int idx); + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual IParamBlock2 *CreatePB(int idx); + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + +class plResponderCmdCamTransition : public plResponderCmd +{ +public: + static plResponderCmdCamTransition& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes() { return 1; } + virtual const char *GetCategory(int idx) { return nil; } + virtual const char *GetName(int idx) { return "Camera Transition"; } + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + +class plResponderCmdCamForce : public plResponderCmd +{ +public: + enum { kForce3rd, kResume1st }; + + static plResponderCmdCamForce& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes() { return 1; } + virtual const char *GetCategory(int idx) { return nil; } + virtual const char *GetName(int idx) { return "Camera Force 3rd"; } + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + +class plResponderCmdDelay : public plResponderCmd +{ +public: + static plResponderCmdDelay& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes() { return 1; } + virtual const char *GetCategory(int idx) { return nil; } + virtual const char *GetName(int idx) { return "Delay"; } + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); + + virtual bool IsWaitable(IParamBlock2 *pb) { return true; } + virtual void CreateWait(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2 *pb, ResponderWaitInfo& waitInfo); +}; + +class plResponderCmdVisibility : public plResponderCmd +{ +public: + static plResponderCmdVisibility& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes(); + virtual const char *GetCategory(int idx); + virtual const char *GetName(int idx); + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual IParamBlock2 *CreatePB(int idx); + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + +class plResponderCmdSubWorld : public plResponderCmd +{ +public: + static plResponderCmdSubWorld& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes(); + virtual const char *GetCategory(int idx); + virtual const char *GetName(int idx); + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual IParamBlock2 *CreatePB(int idx); + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + +class plResponderCmdFootSurface : public plResponderCmd +{ +public: + static plResponderCmdFootSurface& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes() { return 1; } + virtual const char *GetCategory(int idx) { return nil; } + virtual const char *GetName(int idx) { return "Footstep Surface"; } + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; + +class plResponderCmdMultistage : public plResponderCmd +{ +public: + static plResponderCmdMultistage& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes() { return 1; } + virtual const char *GetCategory(int idx) { return nil; } + virtual const char *GetName(int idx) { return "Trigger Multistage"; } + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual plMessage *CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb); +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderMtl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderMtl.cpp new file mode 100644 index 00000000..e585769b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderMtl.cpp @@ -0,0 +1,637 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plResponderMtl.h" +#include "plResponderComponentPriv.h" +#include "resource.h" +#include "max.h" + +#include "../MaxMain/plMaxNode.h" + +#include "../MaxPlasmaMtls/Materials/plDecalMtl.h" +#include "../MaxPlasmaMtls/Materials/plPassMtl.h" + +#include "../MaxConvert/plConvert.h" +#include "../MaxConvert/hsMaterialConverter.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerAnimation.h" + +#include "plMaxAnimUtils.h" +#include "plNotetrackAnim.h" + +#include "plPickMaterialMap.h" +#include "../MaxMain/plMtlCollector.h" +#include "plPickNode.h" + +// Needed for convert +#include "../plMessage/plAnimCmdMsg.h" + +#include +#include +#include + +#include "../MaxMain/plPlasmaRefMsgs.h" + +enum +{ + kMtlRef, + kMtlAnim, + kMtlLoop, + kMtlType, + kMtlOwner_DEAD, + kMtlNode, + kMtlNodeType, +}; + +enum MtlNodeType +{ + kNodePB, // Use the node in the PB + kNodeResponder // Use the node the responder is attached to +}; + +class plResponderMtlProc; +extern plResponderMtlProc gResponderMtlProc; + +ParamBlockDesc2 gResponderMtlBlock +( + kResponderMtlBlk, _T("mtlCmd"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_MTL, IDS_COMP_CMD_PARAMS, 0, 0, &gResponderMtlProc, + + kMtlRef, _T("mtl"), TYPE_REFTARG, 0, 0, + end, + + kMtlAnim, _T("anim"), TYPE_STRING, 0, 0, + end, + + kMtlLoop, _T("loop"), TYPE_STRING, 0, 0, + end, + + kMtlType, _T("type"), TYPE_INT, 0, 0, + end, + + kMtlNode, _T("node"), TYPE_INODE, 0, 0, + end, + + kMtlNodeType, _T("nodeType"), TYPE_INT, 0, 0, + end, + + end +); + +plResponderCmdMtl& plResponderCmdMtl::Instance() +{ + static plResponderCmdMtl theInstance; + return theInstance; +} + +ParamBlockDesc2 *plResponderCmdMtl::GetDesc() +{ + return &gResponderMtlBlock; +} + +// Use old types for backward compatibility +enum +{ + kRespondPlayMat=12, + kRespondStopMat, + kRespondToggleMat, + kRespondLoopMatOn, + kRespondLoopMatOff, + kRespondSetForeMat, + kRespondSetBackMat, + kRespondRewindMat, + + kNumTypes=8 +}; + +static int IndexToOldType(int idx) +{ + static int oldTypes[] = + { + kRespondPlayMat, + kRespondStopMat, + kRespondToggleMat, + kRespondLoopMatOn, + kRespondLoopMatOff, + kRespondSetForeMat, + kRespondSetBackMat, + kRespondRewindMat + }; + + hsAssert(idx < kNumTypes, "Bad index"); + return oldTypes[idx]; +} + +int plResponderCmdMtl::NumTypes() +{ + return kNumTypes; +} + +const char *plResponderCmdMtl::GetCategory(int idx) +{ + return "Material"; +} + +const char *plResponderCmdMtl::GetName(int idx) +{ + int type = IndexToOldType(idx); + + switch (type) + { + case kRespondPlayMat: return "Play"; + case kRespondStopMat: return "Stop"; + case kRespondToggleMat: return "Toggle"; + case kRespondLoopMatOn: return "Set Looping On"; + case kRespondLoopMatOff:return "Set Looping Off"; + case kRespondSetForeMat:return "Set Forwards"; + case kRespondSetBackMat:return "Set Backwards"; + case kRespondRewindMat: return "Rewind"; + } + + return nil; +} + +static const char *GetShortName(int type) +{ + switch (type) + { + case kRespondPlayMat: return "Mat Play"; + case kRespondStopMat: return "Mat Stop"; + case kRespondToggleMat: return "Mat Toggle"; + case kRespondLoopMatOn: return "Mat Loop On"; + case kRespondLoopMatOff:return "Mat Loop Off"; + case kRespondSetForeMat:return "Mat Set Fore"; + case kRespondSetBackMat:return "Mat Set Back"; + case kRespondRewindMat: return "Mat Rewind"; + } + + return nil; +} +const char *plResponderCmdMtl::GetInstanceName(IParamBlock2 *pb) +{ + static char name[256]; + + const char *shortName = GetShortName(pb->GetInt(kMtlType)); + + Mtl *mtl = (Mtl*)pb->GetReferenceTarget(kMtlRef); + sprintf(name, "%s (%s)", shortName, mtl ? mtl->GetName() : "none"); + + return name; +} + +IParamBlock2 *plResponderCmdMtl::CreatePB(int idx) +{ + int type = IndexToOldType(idx); + + // Create the paramblock and save it's type + IParamBlock2 *pb = CreateParameterBlock2(&gResponderMtlBlock, nil); + pb->SetValue(kMtlType, 0, type); + + return pb; +} + +Mtl *plResponderCmdMtl::GetMtl(IParamBlock2 *pb) +{ + return (Mtl*)pb->GetReferenceTarget(kMtlRef); +} + +const char *plResponderCmdMtl::GetAnim(IParamBlock2 *pb) +{ + return pb->GetStr(kMtlAnim); +} + +void ISearchLayerRecur(plLayerInterface *layer, const char *segName, hsTArray& keys) +{ + if (!layer) + return; + + plLayerAnimation *animLayer = plLayerAnimation::ConvertNoRef(layer); + if (animLayer) + { + char *ID = animLayer->GetSegmentID(); + if (ID == nil) + ID = ""; + if (!strcmp(ID, segName)) + { + if( keys.kMissingIndex == keys.Find(animLayer->GetKey()) ) + keys.Append(animLayer->GetKey()); + } + } + + ISearchLayerRecur(layer->GetAttached(), segName, keys); +} + +int ISearchLayerRecur(hsGMaterial* mat, const char *segName, hsTArray& keys) +{ + if (segName == nil) + segName = ""; + int i; + for( i = 0; i < mat->GetNumLayers(); i++ ) + ISearchLayerRecur(mat->GetLayer(i), segName, keys); + return keys.GetCount(); +} + +int GetMatAnimModKey(Mtl* mtl, plMaxNodeBase* node, const char* segName, hsTArray& keys) +{ + int retVal = 0; + + int i; + + //if( begin < 0 ) + // begin = 0; + + if( mtl->ClassID() == Class_ID(MULTI_CLASS_ID,0) ) + { + for( i = 0; i < mtl->NumSubMtls(); i++ ) + retVal += GetMatAnimModKey(mtl->GetSubMtl(i), node, segName, keys); + } + else + { + hsTArray matList; + + if (node) + hsMaterialConverter::Instance().GetMaterialArray(mtl, (plMaxNode*)node, matList); + else + hsMaterialConverter::Instance().CollectConvertedMaterials(mtl, matList); + + for( i = 0; i < matList.GetCount(); i++ ) + { + retVal += ISearchLayerRecur(matList[i], segName, keys); + } + } + + return retVal; +} + +void plResponderCmdMtl::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2* pb) +{ + plMaxNode* mtlNode; + if (pb->GetInt(kMtlNodeType) == kNodeResponder) + mtlNode = node; + else + mtlNode = (plMaxNode*)pb->GetINode(kMtlNode); + + if (mtlNode) + mtlNode->SetForceMaterialCopy(true); +} + +plMessage *plResponderCmdMtl::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb) +{ + Mtl *maxMtl = (Mtl*)pb->GetReferenceTarget(kMtlRef); + if (!maxMtl) + throw "No material specified"; + + const char *animName = pb->GetStr(kMtlAnim); + hsScalar begin=-1.f; + hsScalar end = -1.f; + + SegmentMap *segMap = GetAnimSegmentMap(maxMtl, pErrMsg); + + hsTArray keys; + + if( segMap ) + { + GetSegMapAnimTime(animName, segMap, SegmentSpec::kAnim, begin, end); + } + + plMaxNode* mtlNode; + if (pb->GetInt(kMtlNodeType) == kNodeResponder) + mtlNode = node; + else + mtlNode = (plMaxNode*)pb->GetINode(kMtlNode); + + GetMatAnimModKey(maxMtl, mtlNode, animName, keys); + + const char *loopName = nil; + loopName = pb->GetStr(kMtlLoop); + if (segMap && loopName) + GetSegMapAnimTime(loopName, segMap, SegmentSpec::kLoop, begin, end); + + DeleteSegmentMap(segMap); + + if (!keys.GetCount()) + { + // We need the check here because "physicals only" export mode means that + // most of the materials won't be there, so we should ignore this warning. -Colin + if (plConvert::Instance().GetConvertSettings()->fPhysicalsOnly) + return nil; + else + throw "Material animation key(s) not found"; + } + + plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg; + msg->AddReceivers(keys); + + switch (pb->GetInt(kMtlType)) + { + case kRespondPlayMat: + msg->SetCmd(plAnimCmdMsg::kContinue); + break; + case kRespondStopMat: + msg->SetCmd(plAnimCmdMsg::kStop); + break; + case kRespondToggleMat: + msg->SetCmd(plAnimCmdMsg::kToggleState); + break; + + case kRespondLoopMatOn: + msg->SetCmd(plAnimCmdMsg::kSetLooping); + + // KLUDGE - We send the loop to play by name here, so anim grouped components + // could have loops with different begin and end points. However, apparently + // that functionality was never implemented, whoops. So, we'll take out the + // stuff that actually tries to set the begin and end points for now, so that + // anims with a loop set in advance will actually work with this. -Colin + // + // This KLUDGE has been copied from where Colin kludged it in plResponderAnim + // in the spirit of consistent hackage. Maybe when one gets unkludged, the + // other one will too. -mf +// msg->SetCmd(plAnimCmdMsg::kSetLoopBegin); +// msg->fLoopBegin = begin; + +// msg->SetCmd(plAnimCmdMsg::kSetLoopEnd); +// msg->fLoopEnd = end; + break; + + case kRespondLoopMatOff: + msg->SetCmd(plAnimCmdMsg::kUnSetLooping); + break; + + case kRespondSetForeMat: + msg->SetCmd(plAnimCmdMsg::kSetForewards); + break; + + case kRespondSetBackMat: + msg->SetCmd(plAnimCmdMsg::kSetBackwards); + break; + + case kRespondRewindMat: + msg->SetCmd(plAnimCmdMsg::kGoToBegin); + break; + + default: + delete msg; + throw "Unknown material command"; + } + + return msg; +} + +bool plResponderCmdMtl::IsWaitable(IParamBlock2 *pb) +{ + int type = pb->GetInt(kMtlType); + if (type == kRespondPlayMat || + type == kRespondToggleMat) + return true; + + return false; +} + +void plResponderCmdMtl::GetWaitPoints(IParamBlock2 *pb, WaitPoints& waitPoints) +{ + Mtl *mtl = GetMtl(pb); + const char *animName = GetAnim(pb); + + if (mtl) + { + plNotetrackAnim notetrackAnim(mtl, nil); + plAnimInfo info = notetrackAnim.GetAnimInfo(animName); + while (const char *marker = info.GetNextMarkerName()) + waitPoints.push_back(marker); + } +} + +void plResponderCmdMtl::CreateWait(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2 *pb, ResponderWaitInfo& waitInfo) +{ + plAnimCmdMsg *animMsg = plAnimCmdMsg::ConvertNoRef(waitInfo.msg); + if (animMsg) + animMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); + + plEventCallbackMsg *eventMsg = TRACKED_NEW plEventCallbackMsg; + eventMsg->AddReceiver(waitInfo.receiver); + eventMsg->fRepeats = 0; + eventMsg->fEvent = kStop; + eventMsg->fUser = waitInfo.callbackUser; + + if (waitInfo.point) + { + // FIXME COLIN - Error checking here? + Mtl *mtl = GetMtl(pb); + const char *animName = GetAnim(pb); + + plNotetrackAnim notetrackAnim(mtl, nil); + plAnimInfo info = notetrackAnim.GetAnimInfo(animName); + + eventMsg->fEvent = kTime; + eventMsg->fEventTime = info.GetMarkerTime(waitInfo.point); + } + else + { + eventMsg->fEvent = kStop; + } + + plMessageWithCallbacks *callbackMsg = plMessageWithCallbacks::ConvertNoRef(waitInfo.msg); + callbackMsg->AddCallback(eventMsg); + hsRefCnt_SafeUnRef( eventMsg ); +} + +//////////////////////////////////////////////////////////////////////////////// + +#include "plAnimCompProc.h" + +class plResponderMtlProc : public plMtlAnimProc +{ +public: + plResponderMtlProc(); + +protected: + virtual void IOnInitDlg(HWND hWnd, IParamBlock2* pb); + virtual void ILoadUser(HWND hWnd, IParamBlock2* pb); + virtual bool IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID); + + virtual void IPickNode(IParamBlock2* pb); + + virtual void ISetNodeButtonText(HWND hWnd, IParamBlock2* pb); +}; +static plResponderMtlProc gResponderMtlProc; + +plResponderMtlProc::plResponderMtlProc() +{ + fMtlButtonID = IDC_MTL_BUTTON; + fMtlParamID = kMtlRef; + fNodeButtonID = IDC_NODE_BUTTON; + fNodeParamID = kMtlNode; + fAnimComboID = IDC_ANIM_COMBO; + fAnimParamID = kMtlAnim; +} + +void plResponderMtlProc::IOnInitDlg(HWND hWnd, IParamBlock2* pb) +{ + int type = pb->GetInt(kMtlType); + + // Show the loop control only if this is a loop + int show = (type == kRespondLoopMatOn) ? SW_SHOW : SW_HIDE; + ShowWindow(GetDlgItem(hWnd, IDC_LOOP_COMBO), show); + ShowWindow(GetDlgItem(hWnd, IDC_LOOP_TEXT), show); + + // Resize the dialog if we're not using the loop control + if (type != kRespondLoopMatOn) + { + RECT itemRect, clientRect; + GetWindowRect(GetDlgItem(hWnd, IDC_LOOP_TEXT), &itemRect); + GetWindowRect(hWnd, &clientRect); + SetWindowPos(hWnd, NULL, 0, 0, clientRect.right-clientRect.left, + itemRect.top-clientRect.top, SWP_NOMOVE | SWP_NOZORDER); + } +} + +void plResponderMtlProc::ILoadUser(HWND hWnd, IParamBlock2 *pb) +{ + HWND hLoop = GetDlgItem(hWnd, IDC_LOOP_COMBO); + + const char *savedName = pb->GetStr(kMtlLoop); + if (!savedName) + savedName = ""; + + ComboBox_ResetContent(hLoop); + int sel = ComboBox_AddString(hLoop, ENTIRE_ANIMATION_NAME); + ComboBox_SetCurSel(hLoop, sel); + + // Get the NoteTrack animations off the selected material + Mtl *mtl = (Mtl*)pb->GetReferenceTarget(kMtlRef); + if (!mtl) + { + ComboBox_Enable(hLoop, FALSE); + return; + } + + ComboBox_Enable(hLoop, TRUE); + + plNotetrackAnim anim(mtl, nil); + const char *animName = pb->GetStr(kMtlAnim); + plAnimInfo info = anim.GetAnimInfo(animName); + + while (const char *loopName = info.GetNextLoopName()) + { + sel = ComboBox_AddString(hLoop, loopName); + if (!strcmp(loopName, savedName)) + ComboBox_SetCurSel(hLoop, sel); + } +} + +bool plResponderMtlProc::IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID) +{ + if (cmd == CBN_SELCHANGE && resID == IDC_LOOP_COMBO) + { + HWND hCombo = GetDlgItem(hWnd, IDC_LOOP_COMBO); + int idx = ComboBox_GetCurSel(hCombo); + + if (idx != CB_ERR) + { + if (ComboBox_GetItemData(hCombo, idx) == 0) + pb->SetValue(kMtlLoop, 0, ""); + else + { + // Get the name of the animation and save it + char buf[256]; + ComboBox_GetText(hCombo, buf, sizeof(buf)); + pb->SetValue(kMtlLoop, 0, buf); + } + } + + return true; + } + + return false; +} + + +#include "plPickNodeBase.h" + +static const char* kUserTypeAll = "(All)"; +static const char* kResponderNodeName = "(Responder Node)"; + +class plPickRespMtlNode : public plPickMtlNode +{ +protected: + int fTypeID; + + void IAddUserType(HWND hList) + { + int type = fPB->GetInt(fTypeID); + + int idx = ListBox_AddString(hList, kUserTypeAll); + if (type == kNodePB && !fPB->GetINode(fNodeParamID)) + ListBox_SetCurSel(hList, idx); + + + idx = ListBox_AddString(hList, kResponderNodeName); + if (type == kNodeResponder) + ListBox_SetCurSel(hList, idx); + } + + void ISetUserType(plMaxNode* node, const char* userType) + { + if (hsStrEQ(userType, kUserTypeAll)) + { + ISetNodeValue(nil); + fPB->SetValue(fTypeID, 0, kNodePB); + } + else if (hsStrEQ(userType, kResponderNodeName)) + { + ISetNodeValue(nil); + fPB->SetValue(fTypeID, 0, kNodeResponder); + } + else + fPB->SetValue(fTypeID, 0, kNodePB); + } + +public: + plPickRespMtlNode(IParamBlock2* pb, int nodeParamID, int typeID, Mtl* mtl) : + plPickMtlNode(pb, nodeParamID, mtl), fTypeID(typeID) + { + } +}; + +void plResponderMtlProc::IPickNode(IParamBlock2* pb) +{ + plPickRespMtlNode pick(pb, kMtlNode, kMtlNodeType, IGetMtl(pb)); + pick.DoPick(); +} + +void plResponderMtlProc::ISetNodeButtonText(HWND hWnd, IParamBlock2* pb) +{ + int type = pb->GetInt(kMtlNodeType); + HWND hButton = GetDlgItem(hWnd, IDC_NODE_BUTTON); + + if (type == kNodeResponder) + SetWindowText(hButton, kResponderNodeName); + else if (type == kNodePB && !pb->GetINode(kMtlNode)) + SetWindowText(hButton, kUserTypeAll); + else + plMtlAnimProc::ISetNodeButtonText(hWnd, pb); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderMtl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderMtl.h new file mode 100644 index 00000000..93154766 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderMtl.h @@ -0,0 +1,64 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plResponderCmd.h" +#include "../pnKeyedObject/plKey.h" + +class plMessage; +class IParamBlock2; +class ClassDesc2; +class plMaxNode; +class plErrorMsg; +class plComponentBase; +class Mtl; +class plMaxNodeBase; +template class hsTArray; + +int GetMatAnimModKey(Mtl* mtl, plMaxNodeBase* node, const char *segName, hsTArray& keys); + +class plResponderCmdMtl : public plResponderCmd +{ +public: + static plResponderCmdMtl& Instance(); + virtual ParamBlockDesc2 *GetDesc(); + + virtual int NumTypes(); + virtual const char *GetCategory(int type); + virtual const char *GetName(int type); + virtual const char *GetInstanceName(IParamBlock2 *pb); + + virtual IParamBlock2 *CreatePB(int type); + virtual void SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2* pb); + virtual plMessage* CreateMsg(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2* pb); + + virtual bool IsWaitable(IParamBlock2 *pb); + virtual void GetWaitPoints(IParamBlock2 *pb, WaitPoints& waitPoints); + virtual void CreateWait(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2 *pb, ResponderWaitInfo& waitInfo); + + Mtl *GetMtl(IParamBlock2 *pb); + const char *GetAnim(IParamBlock2 *pb); +}; + +int GetMatAnimModKey(Mtl* mtl, plMaxNodeBase* node, const char* segName, hsTArray& keys); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderWait.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderWait.cpp new file mode 100644 index 00000000..abb8f78c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderWait.cpp @@ -0,0 +1,470 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plResponderWait.h" +#include "plResponderComponentPriv.h" +#include "resource.h" +#include "../plModifier/plResponderModifier.h" + +#include "plResponderLink.h" + +class plResponderWaitProc; +extern plResponderWaitProc gResponderWaitProc; + +enum +{ + kWaitWhoOld, + kWaitPointOld, + kWaitMe, + kWaitWho, + kWaitPoint, +}; + +ParamBlockDesc2 gResponderWaitBlock +( + kResponderWaitBlk, _T("waitCmd"), 0, NULL, P_AUTO_UI, + + IDD_COMP_RESPOND_WAIT, IDS_COMP_WAIT, 0, 0, &gResponderWaitProc, + + kWaitWhoOld, _T("whoOld"), TYPE_INT_TAB, 0, 0, 0, + end, + + kWaitPointOld, _T("pointOld"), TYPE_STRING_TAB, 0, 0, 0, + end, + + kWaitMe, _T("me"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_WAIT_ON_ME_CHECK, + end, + + kWaitWho, _T("who"), TYPE_INT, 0, 0, + p_default, -1, + end, + + kWaitPoint, _T("point"), TYPE_STRING, 0, 0, + end, + + end +); +void ResponderWait::SetDesc(ClassDesc2 *desc) { gResponderWaitBlock.SetClassDesc(desc); } + +void ResponderWait::FixupWaitBlock(IParamBlock2 *waitPB) +{ + if (waitPB->Count(kWaitWhoOld) > 0) + { + int who = waitPB->GetInt(kWaitWhoOld, 0, 0); + waitPB->SetValue(kWaitWho, 0, who); + waitPB->Delete(kWaitWhoOld, 0, 1); + } + + if (waitPB->Count(kWaitPointOld) > 0) + { + TCHAR* point = waitPB->GetStr(kWaitPointOld, 0, 0); + waitPB->SetValue(kWaitPoint, 0, point); + waitPB->Delete(kWaitPointOld, 0, 1); + } +} + +IParamBlock2 *ResponderWait::CreatePB() +{ + return CreateParameterBlock2(&gResponderWaitBlock, nil); +} + +bool ResponderWait::GetWaitOnMe(IParamBlock2* waitPB) +{ + return (waitPB->GetInt(kWaitMe) != 0); +} + +int ResponderWait::GetWaitingOn(IParamBlock2* waitPB) +{ + return waitPB->GetInt(kWaitWho); +} + +const char* ResponderWait::GetWaitPoint(IParamBlock2* waitPB) +{ + const char* point = waitPB->GetStr(kWaitPoint); + if (point && *point == '\0') + return nil; + return point; +} + +class plResponderWaitProc : public ParamMap2UserDlgProc +{ +protected: + IParamBlock2 *fStatePB; + IParamBlock2 *fWaitPB; + + int fCurCmd; + HWND fhDlg; + HWND fhList; + +public: + void Init(IParamBlock2 *curStatePB, int curCmd, HWND hList) { fStatePB = curStatePB; fCurCmd = curCmd; fhList = hList; } + + BOOL DlgProc(TimeValue t, IParamMap2 *pm, HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + void DeleteThis() {} + +protected: + void LoadWho(bool setDefault=false); + void LoadPoint(bool force=false); + + IParamBlock2 *GetCmdParams(int cmdIdx); +}; +static plResponderWaitProc gResponderWaitProc; + +void ResponderWait::InitDlg(IParamBlock2 *curStatePB, int curCmd, HWND hList) +{ + gResponderWaitProc.Init(curStatePB, curCmd, hList); +} + +IParamBlock2 *plResponderWaitProc::GetCmdParams(int cmdIdx) +{ + return (IParamBlock2*)fStatePB->GetReferenceTarget(kStateCmdParams, 0, cmdIdx); +} + +BOOL plResponderWaitProc::DlgProc(TimeValue t, IParamMap2 *pm, HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + fhDlg = hDlg; + + fWaitPB = pm->GetParamBlock(); + + ResponderWait::FixupWaitBlock(fWaitPB); + + IParamBlock2 *pb = GetCmdParams(fCurCmd); + plResponderCmd *cmd = plResponderCmd::Find(pb); + pm->Enable(kWaitMe, cmd->IsWaitable(pb)); + + LoadWho(); + LoadPoint(); + return TRUE; + } + + case WM_CUSTEDIT_ENTER: + if (wParam == IDC_MARKER_EDIT) + { + ICustEdit *edit = GetICustEdit((HWND)lParam); + char buf[256]; + edit->GetText(buf, sizeof(buf)); + fWaitPB->SetValue(kWaitPoint, 0, buf); + + return TRUE; + } + break; + + case WM_COMMAND: + { + int code = HIWORD(wParam); + int id = LOWORD(wParam); + + if (id == IDC_CHECK_WAIT && code == BN_CLICKED) + { + BOOL checked = (IsDlgButtonChecked(hDlg, IDC_CHECK_WAIT) == BST_CHECKED); + if (!checked) + { + fWaitPB->SetValue(kWaitWho, 0, -1); + fWaitPB->SetValue(kWaitPoint, 0, ""); + + LoadPoint(); + + HWND hWho = GetDlgItem(hDlg, IDC_WAIT_WHO); + EnableWindow(hWho, FALSE); + ComboBox_ResetContent(hWho); + } + else + { + LoadWho(true); + LoadPoint(); + } + + return TRUE; + } + else if (id == IDC_WAIT_WHO && code == CBN_SELCHANGE) + { + HWND hWho = (HWND)lParam; + int who = ComboBox_GetCurSel(hWho); + int idx = ComboBox_GetItemData(hWho, who); + fWaitPB->SetValue(kWaitWho, 0, idx); + + LoadPoint(); + return TRUE; + } + else if (id == IDC_RADIO_FINISH && code == BN_CLICKED) + { + fWaitPB->SetValue(kWaitPoint, 0, ""); + LoadPoint(); + return TRUE; + } + else if (id == IDC_RADIO_POINT && code == BN_CLICKED) + { + LoadPoint(true); + return TRUE; + } + else if (id == IDC_WAIT_POINT && code == CBN_SELCHANGE) + { + HWND hPoint = (HWND)lParam; + if (ComboBox_GetCurSel(hPoint) != CB_ERR) + { + char buf[256]; + ComboBox_GetText(hPoint, buf, sizeof(buf)); + fWaitPB->SetValue(kWaitPoint, 0, buf); + } + return TRUE; + } + break; + } + } + + return FALSE; +} + +void plResponderWaitProc::LoadWho(bool setDefault) +{ + HWND hWho = GetDlgItem(fhDlg, IDC_WAIT_WHO); + int who = fWaitPB->GetInt(kWaitWho); + + ComboBox_ResetContent(hWho); + + int numFound = 0; + + // Copy all the commands before this one to the 'who' combo box + for (int i = 0; i < fCurCmd; i++) + { + IParamBlock2 *pb = GetCmdParams(i); + plResponderCmd *cmd = plResponderCmd::Find(pb); + + if (cmd->IsWaitable(pb)) + { + int idx = ComboBox_AddString(hWho, cmd->GetInstanceName(pb)); + ComboBox_SetItemData(hWho, idx, i); + + // If the saved 'who' is valid, select it and check the wait checkbox + if (who == i) + { + ComboBox_SetCurSel(hWho, idx); + CheckDlgButton(fhDlg, IDC_CHECK_WAIT, BST_CHECKED); + EnableWindow(hWho, TRUE); + } + + numFound++; + } + } + + // Pick the last item in the who combo as the default + if (setDefault && numFound > 0) + { + HWND hWho = GetDlgItem(fhDlg, IDC_WAIT_WHO); + int idx = ComboBox_GetItemData(hWho, numFound-1); + fWaitPB->SetValue(kWaitWho, 0, idx); + + ComboBox_SetCurSel(hWho, numFound-1); + CheckDlgButton(fhDlg, IDC_CHECK_WAIT, BST_CHECKED); + EnableWindow(hWho, TRUE); + } + + // Disable the wait checkbox if there are no waitable commands behind this one + EnableWindow(GetDlgItem(fhDlg, IDC_CHECK_WAIT), (numFound > 0)); +} + +void plResponderWaitProc::LoadPoint(bool force) +{ + int who = fWaitPB->GetInt(kWaitWho); + const char *point = fWaitPB->GetStr(kWaitPoint); + if (point && *point == '\0') + point = nil; + + CheckRadioButton(fhDlg, IDC_RADIO_FINISH, IDC_RADIO_POINT, point || force ? IDC_RADIO_POINT : IDC_RADIO_FINISH); + + BOOL enableAll = (who != -1); + EnableWindow(GetDlgItem(fhDlg, IDC_RADIO_FINISH), enableAll); + EnableWindow(GetDlgItem(fhDlg, IDC_RADIO_POINT), enableAll); + + BOOL enablePoint = ((point != nil) || force) && enableAll; + EnableWindow(GetDlgItem(fhDlg, IDC_WAIT_POINT), enablePoint); + ComboBox_ResetContent(GetDlgItem(fhDlg, IDC_WAIT_POINT)); + + if (enableAll) + { + IParamBlock2 *pb = (IParamBlock2*)fStatePB->GetReferenceTarget(kStateCmdParams, 0, who); + plResponderCmd *cmd = plResponderCmd::Find(pb); + + // KLUDGE - stupid one-shot needs editable box + if (cmd == &(plResponderCmdOneShot::Instance())) + { + ShowWindow(GetDlgItem(fhDlg, IDC_WAIT_POINT), SW_HIDE); + + HWND hEdit = GetDlgItem(fhDlg, IDC_MARKER_EDIT); + ShowWindow(hEdit, SW_SHOW); + ICustEdit *custEdit = GetICustEdit(hEdit); + custEdit->SetText(point ? (char*)point : ""); + } + else + { + ShowWindow(GetDlgItem(fhDlg, IDC_WAIT_POINT), SW_SHOW); + + HWND hEdit = GetDlgItem(fhDlg, IDC_MARKER_EDIT); + ShowWindow(hEdit, SW_HIDE); + + plResponderCmd::WaitPoints waitPoints; + cmd->GetWaitPoints(pb, waitPoints); + + HWND hCombo = GetDlgItem(fhDlg, IDC_WAIT_POINT); + ComboBox_ResetContent(hCombo); + + if (waitPoints.size() == 0) + { + EnableWindow(GetDlgItem(fhDlg, IDC_RADIO_POINT), FALSE); + EnableWindow(GetDlgItem(fhDlg, IDC_WAIT_POINT), FALSE); + } + else + { + for (int i = 0; i < waitPoints.size(); i++) + { + const char *marker = waitPoints[i].c_str(); + int idx = ComboBox_AddString(hCombo, marker); + if (point && !strcmp(point, marker)) + ComboBox_SetCurSel(hCombo, idx); + } + } + } + } +} + +static IParamBlock2 *GetWaitBlk(IParamBlock2 *state, int idx) +{ + return (IParamBlock2*)state->GetReferenceTarget(kStateCmdWait, 0, idx); +} + +void ResponderWait::CmdRemoved(IParamBlock2 *state, int delIdx) +{ + int numCmds = state->Count(kStateCmdParams); + for (int i = delIdx; i < numCmds; i++) + { + IParamBlock2 *pb = GetWaitBlk(state, i); + + int who = pb->GetInt(kWaitWho); + + if (who == delIdx) + pb->SetValue(kWaitWho, 0, -1); + if (who > delIdx) + pb->SetValue(kWaitWho, 0, who-1); + } +} + +// A command was moved from oldIdx to newIdx. Fix all the wait commands. +void ResponderWait::CmdMoved(IParamBlock2 *state, int oldIdx, int newIdx) +{ + int numCmds = state->Count(kStateCmdParams); + + // Moved forward + if (oldIdx < newIdx) + { + // Patch up the commands that were ahead of this one + for (int i = oldIdx; i < numCmds; i++) + { + if (i == newIdx) + continue; + + IParamBlock2 *pb = GetWaitBlk(state, i); + + int who = pb->GetInt(kWaitWho); + + // If the command was waiting on the moved one, and is now behind it, invalidate it + if (who == oldIdx && i < newIdx) + pb->SetValue(kWaitWho, 0, -1); + // + else if (who == oldIdx) + pb->SetValue(kWaitWho, 0, newIdx); + // If it was waiting on one ahead of it, correct the index + else if (who > oldIdx && who <= newIdx) + pb->SetValue(kWaitWho, 0, who-1); + } + } + // Moved backward + else + { + // If this command was waiting on any of the commands now ahead of it, invalidate it + IParamBlock2 *movedPB = GetWaitBlk(state, newIdx); + int who = movedPB->GetInt(kWaitWho); + if (who >= newIdx) + movedPB->SetValue(kWaitWho, 0, -1); + + for (int i = newIdx+1; i < numCmds; i++) + { + // Is this command waiting on any of the commands it is moving in back of? + IParamBlock2 *pb = GetWaitBlk(state, i); + int who = pb->GetInt(kWaitWho); + if (who == oldIdx) + pb->SetValue(kWaitWho, 0, newIdx); + if (who >= newIdx && who < oldIdx) + pb->SetValue(kWaitWho, 0, who+1); + } + } +} + +// Determine if any of the wait commands will be invalidated by this move +bool ResponderWait::ValidateCmdMove(IParamBlock2 *state, int oldIdx, int newIdx) +{ + // Moving forward + if (oldIdx < newIdx) + { + // Are any of the commands ahead of this one waiting on it? + for (int i = oldIdx+1; i <= newIdx; i++) + { + IParamBlock2 *pb = GetWaitBlk(state, i); + + if (pb->GetInt(kWaitWho) == oldIdx) + { + int ret = hsMessageBox("You are moving this command ahead of another command that waits on it.\nAre you sure you want to do that?", "Warning", hsMessageBoxYesNo); + if (ret == hsMBoxYes) + return true; + else + return false; + } + } + } + // Moving backward + else + { + // Is this command waiting on any of the commands it is moving in back of? + IParamBlock2 *pb = GetWaitBlk(state, oldIdx); + + int who = pb->GetInt(kWaitWho); + if (who >= newIdx && who < oldIdx) + { + int ret = hsMessageBox("You are moving this command behind another command that it is waiting on.\nAre you sure you want to do that?", "Warning", hsMessageBoxYesNo); + if (ret == hsMBoxYes) + return true; + else + return false; + } + } + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderWait.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderWait.h new file mode 100644 index 00000000..88bfd38d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plResponderWait.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsWindows.h" // Damn, had to pass in a HWND +#include +class ClassDesc2; +class IParamBlock2; +class plKey; +class plMessage; +class plResponderModifier; + +namespace ResponderWait +{ + void SetDesc(ClassDesc2 *desc); + + void InitDlg(IParamBlock2 *curStatePB, int curCmd, HWND hList); + + IParamBlock2 *CreatePB(); + void FixupWaitBlock(IParamBlock2 *waitPB); + + bool ValidateCmdMove(IParamBlock2 *state, int oldIdx, int newIdx); + void CmdMoved(IParamBlock2 *state, int oldIdx, int newIdx); + void CmdRemoved(IParamBlock2 *state, int delIdx); + + bool GetWaitOnMe(IParamBlock2* waitPB); + int GetWaitingOn(IParamBlock2* waitPB); + const char* GetWaitPoint(IParamBlock2* waitPB); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSeekPoint.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSeekPoint.cpp new file mode 100644 index 00000000..65e3ae8d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSeekPoint.cpp @@ -0,0 +1,120 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "../MaxMain/plMaxNode.h" + +#include "../pnSceneObject/plSceneObject.h" + +#include "hsResMgr.h" + +#include "../plScene/plSceneNode.h" +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/plConvert.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../MaxMain/plMaxNode.h" +#include "hsGeometry3.h" + +#include "../plAvatar/plSeekPointMod.h" + +//Necessary Empty function. Otherwise Linker throws the Paramblock away as extraneous. +void DummyCodeIncludeFuncSeekPoint() +{ +} + +// SeekPoint Component +// Denotes a special location in the world that the avatar can "magically" move to. + + +//Class that accesses the paramblock below. +class plSeekPointComponent : public plComponent +{ +public: + plSeekPointComponent(); + void DeleteThis() { delete this; } + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + //hsBool IsValidNodeType(plMaxNode *pNode); +}; + +//Max desc stuff necessary. +CLASS_DESC(plSeekPointComponent, gSeekPtDesc, "Seek Point", "Seek", COMP_TYPE_TYPE, Class_ID(0x597c7970, 0x13df349f)) + +// MAX paramblock describing the user interface. +// In this case, there isn't really a user interface. +ParamBlockDesc2 gSeekPtBk +( + 1, _T(""), 0, &gSeekPtDesc, P_AUTO_CONSTRUCT, 0, + + end +); + +// CTOR (default) +plSeekPointComponent::plSeekPointComponent() +{ + fClassDesc = &gSeekPtDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// CONVERT +hsBool plSeekPointComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + const char *objName = node->GetName(); + char *name = TRACKED_NEW char[strlen(objName) + 1]; + + strcpy(name, objName); + + plSeekPointMod* pointMod = TRACKED_NEW plSeekPointMod(name); + node->AddModifier(pointMod, IGetUniqueName(node)); + return true; +} + +// PRECONVERT +hsBool plSeekPointComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + pNode->SetForceLocal(true); + return true; +} + +/*hsBool IsValidNodeType(plMaxNode *pNode) +{ + Object *obj = pNode->EvalWorldState(hsConverterUtils::Instance().GetTime(pNode->GetInterface())).obj; + if(obj->SuperClassID() == CAMERA_CLASS_ID) + return true; + else + return false; + + +} +*/ + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plShadowComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plShadowComponents.cpp new file mode 100644 index 00000000..cdae2788 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plShadowComponents.cpp @@ -0,0 +1,448 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxExport/plExportProgressBar.h" + +#include "hsTypes.h" + +#include "plShadowComponents.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plObjRefMsg.h" + +#include "../plGLight/plLightInfo.h" +#include "../plGLight/plShadowCaster.h" +#include "../plGLight/plPointShadowMaster.h" +#include "../plGLight/plDirectShadowMaster.h" + +#include "hsResMgr.h" + +void DummyCodeIncludeFuncShadow() +{ +} + +static UInt16 QualityBitToMask(int q) { return ~((1 << q) - 1); } + +#define WM_ROLLOUT_OPEN WM_USER+1 +static const int kNumQualities = 4; +static const char* kQualityStrings[kNumQualities] = { + "Low", + "Medium", + "High", + "Ultra" +}; + +template class plQualityProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + PostMessage(hWnd, WM_ROLLOUT_OPEN, 0, 0); + + HWND cbox = GetDlgItem(hWnd, IDC_COMP_SHADOW_QUALITY); + int i; + for( i = 0; i < kNumQualities; i++ ) + { + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)kQualityStrings[i]); + } + SendMessage(cbox, CB_SETCURSEL, map->GetParamBlock()->GetInt(T::kQuality), 0); + + } + return true; + + case WM_COMMAND: + switch( LOWORD(wParam) ) + { + case IDC_COMP_SHADOW_QUALITY: + map->GetParamBlock()->SetValue(T::kQuality, t, SendMessage(GetDlgItem(hWnd, LOWORD(wParam)), CB_GETCURSEL, 0, 0)); + return TRUE; + } + break; + } + + return false; + } + void DeleteThis() {} +}; + + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Contains (in order) +// ShadowCast +// ShadowRcv +// ShadowLight + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ShadowCast + +CLASS_DESC(plShadowCastComponent, gShadowCastDesc, "Shadow Caster", "ShadowCast", COMP_TYPE_SHADOW, SHADOWCAST_COMP_CID) + + +static plQualityProc gCastQualityProc; + +ParamBlockDesc2 gShadowCastBk +( + plComponent::kBlkComp, _T("ShadowCast"), 0, &gShadowCastDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SHADOW_CAST, IDS_COMP_SHADOW_CAST, 0, 0, &gCastQualityProc, + + plShadowCastComponent::kSelfShadow, _T("SelfShadow"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SHADOW_CAST_SELFSHADOW, + end, + + plShadowCastComponent::kBlur, _T("Blur"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SHADOW_CAST_BLUR, + p_enable_ctrls, 1, plShadowCastComponent::kBlurScale, + end, + + plShadowCastComponent::kBlurScale, _T("BlurScale"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SHADOW_CAST_BLURSCALE, IDC_COMP_SHADOW_CAST_BLURSCALE_SPIN, 1.0, + end, + + plShadowCastComponent::kAtten, _T("Atten"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SHADOW_CAST_ATTEN, + p_enable_ctrls, 1, plShadowCastComponent::kAttenScale, + end, + + plShadowCastComponent::kAttenScale, _T("AttenScale"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 25.0, 1000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SHADOW_CAST_ATTENSCALE, IDC_COMP_SHADOW_CAST_ATTENSCALE_SPIN, 1.0, + end, + + plShadowCastComponent::kBoost, _T("Boost"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 5000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SHADOW_CAST_BOOST, IDC_COMP_SHADOW_CAST_BOOST_SPIN, 1.0, + end, + + plShadowCastComponent::kQuality, _T("Quality"), TYPE_INT, 0, 0, + p_default, 0, + end, + + plShadowCastComponent::kLimitRes, _T("Limit"), TYPE_INT, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SHADOW_CAST_LIMIT, + end, + + end + +); + +plShadowCastComponent::plShadowCastComponent() +: fCaster(nil) +{ + fClassDesc = &gShadowCastDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plShadowCastComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plSceneObject* so = node->GetSceneObject(); + if( !so ) + return true; + + const float kBlurPercentToAbs = 1.e-2f * 1.5f; + const float kAttenPercentToAbs = 1.e-2f; + const float kBoostPercentToAbs = 1.e-2f; + if( !fCaster ) + { + fCaster = TRACKED_NEW plShadowCaster; + plLoadMask lm(QualityBitToMask(fCompPB->GetInt(kQuality)), QualityBitToMask(0)); + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), fCaster, node->GetLocation(), lm); + fCaster->SetSelfShadow(fCompPB->GetInt(kSelfShadow)); + if( fCompPB->GetInt(kBlur) ) + fCaster->SetBlurScale(fCompPB->GetFloat(kBlurScale) * kBlurPercentToAbs); + if( fCompPB->GetInt(kAtten) ) + fCaster->SetAttenScale(fCompPB->GetFloat(kAttenScale) * kAttenPercentToAbs); + fCaster->SetBoost(fCompPB->GetFloat(kBoost) * kBoostPercentToAbs); + if( fCompPB->GetInt(kLimitRes) ) + fCaster->SetLimitRes(true); + } + + AddShadowCastModifier(so, fCaster); + + return true; +} + +hsBool plShadowCastComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + fCaster = nil; + return true; +} + +hsBool plShadowCastComponent::PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plShadowCastComponent::AddShadowCastModifier(plMaxNode* pNode, plShadowCaster* caster) +{ + if( !pNode->CanConvert() ) + return false; + + plSceneObject* so = pNode->GetSceneObject(); + if( !so ) + return false; + + return plShadowCastComponent::AddShadowCastModifier(so, caster); +} + +hsBool plShadowCastComponent::AddShadowCastModifier(plSceneObject* so, plShadowCaster* caster) +{ + // First off, ensure that we NEVER NEVER NEVER have more than one shadowcaster on an object. + // That would be BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD. + int i; + for( i = 0; i < so->GetNumModifiers(); i++ ) + { + if( plShadowCaster::ConvertNoRef(so->GetModifier(i)) ) + return false; + } + + // Okay, we're clear, just add via notify. + hsgResMgr::ResMgr()->AddViaNotify(caster->GetKey(), TRACKED_NEW plObjRefMsg(so->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ShadowRcv + +CLASS_DESC(plShadowRcvComponent, gShadowRcvDesc, "Shadow Receiver", "ShadowRcv", COMP_TYPE_SHADOW, SHADOWRCV_COMP_CID) + + + +ParamBlockDesc2 gShadowRcvBk +( + plComponent::kBlkComp, _T("ShadowRcv"), 0, &gShadowRcvDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SHADOW_RCV, IDS_COMP_SHADOW_RCV, 0, 0, nil, + + plShadowRcvComponent::kForceRadio, _T("ForceShadow"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 2, IDC_RADIO_FORCE_ON, IDC_RADIO_FORCE_OFF, + p_vals, plShadowRcvComponent::kForceOn, plShadowRcvComponent::kForceOff, + p_default, plShadowRcvComponent::kForceOff, + end, + end + +); + +plShadowRcvComponent::plShadowRcvComponent() +{ + fClassDesc = &gShadowRcvDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plShadowRcvComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plShadowRcvComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + if( fCompPB->GetInt(kForceRadio) == kForceOn ) + { + pNode->SetForceShadow(true); + pNode->SetNoShadow(false); + } + else if( fCompPB->GetInt(kForceRadio) == kForceOff ) + { + pNode->SetForceShadow(false); + pNode->SetNoShadow(true); + } + return true; +} + +hsBool plShadowRcvComponent::PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ShadowLight + +CLASS_DESC(plShadowLightComponent, gShadowLightDesc, "Shadow Light", "ShadowLight", COMP_TYPE_SHADOW, SHADOWLIGHT_COMP_CID) + + +static plQualityProc gLightQualityProc; + +ParamBlockDesc2 gShadowLightBk +( + plComponent::kBlkComp, _T("ShadowLight"), 0, &gShadowLightDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SHADOW_LIGHT, IDS_COMP_SHADOW_LIGHT, 0, 0, &gLightQualityProc, + + plShadowLightComponent::kFalloff, _T("Falloff"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 5.0, 50.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SHADOW_LIGHT_FALLOFF, IDC_COMP_SHADOW_LIGHT_FALLOFF_SPIN, 1.0, + end, + + plShadowLightComponent::kMaxDist, _T("MaxDist"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 500.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SHADOW_LIGHT_MAXDIST, IDC_COMP_SHADOW_LIGHT_MAXDIST_SPIN, 1.0, + end, + + plShadowLightComponent::kPower, _T("Power"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 200.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SHADOW_LIGHT_POWER, IDC_COMP_SHADOW_LIGHT_POWER_SPIN, 1.0, + end, + + plShadowLightComponent::kShadowOnly, _T("ShadowOnly"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SHADOW_LIGHT_SHADOWONLY, + end, + + plShadowLightComponent::kObeyGroups, _T("ObeyGroups"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SHADOW_LIGHT_OBEYGROUPS, + end, + + plShadowLightComponent::kSelfShadow, _T("SelfShadow"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SHADOW_LIGHT_SELFSHADOW, + end, + + plShadowLightComponent::kQuality, _T("Quality"), TYPE_INT, 0, 0, + p_default, 0, + end, + + end + +); + +plShadowLightComponent::plShadowLightComponent() +{ + fClassDesc = &gShadowLightDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plShadowLightComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plSceneObject* so = node->GetSceneObject(); + if( !so ) + return true; + + plLightInfo* liInfo = plLightInfo::ConvertNoRef(so->GetGenericInterface(plLightInfo::Index())); + if( !liInfo ) + return true; + + if( fCompPB->GetInt(kShadowOnly) ) + liInfo->SetProperty(plLightInfo::kLPShadowOnly, true); + + if( fCompPB->GetInt(kObeyGroups) ) + liInfo->SetProperty(plLightInfo::kLPShadowLightGroup, true); + + plDirectionalLightInfo* dirLiInfo = plDirectionalLightInfo::ConvertNoRef(liInfo); + if( dirLiInfo ) + return IAddDirectMaster(node, so); + + plOmniLightInfo* omniLiInfo = plOmniLightInfo::ConvertNoRef(liInfo); + if( omniLiInfo ) + return IAddPointMaster(node, so); + + plSpotLightInfo* spotLiInfo = plSpotLightInfo::ConvertNoRef(liInfo); + if( spotLiInfo ) + return IAddPointMaster(node, so); + + return true; +} + +hsBool plShadowLightComponent::IAddDirectMaster(plMaxNode* node, plSceneObject* so) +{ + plDirectShadowMaster* directMaster = TRACKED_NEW plDirectShadowMaster; + + plLoadMask lm(QualityBitToMask(fCompPB->GetInt(kQuality)), QualityBitToMask(0)); + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), directMaster, node->GetLocation(), lm); + + directMaster->SetAttenDist(fCompPB->GetFloat(kFalloff)); + + directMaster->SetMaxDist(fCompPB->GetFloat(kMaxDist)); + + directMaster->SetPower(fCompPB->GetFloat(kPower) * 1.e-2f); + + directMaster->SetProperty(plShadowMaster::kSelfShadow, fCompPB->GetInt(kSelfShadow)); + + hsgResMgr::ResMgr()->AddViaNotify(directMaster->GetKey(), TRACKED_NEW plObjRefMsg(so->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + return true; +} + +hsBool plShadowLightComponent::IAddPointMaster(plMaxNode* node, plSceneObject* so) +{ + plPointShadowMaster* pointMaster = TRACKED_NEW plPointShadowMaster; + + plLoadMask lm(QualityBitToMask(fCompPB->GetInt(kQuality)), QualityBitToMask(0)); + hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), pointMaster, node->GetLocation(), lm); + + pointMaster->SetAttenDist(fCompPB->GetFloat(kFalloff)); + + pointMaster->SetMaxDist(fCompPB->GetFloat(kMaxDist)); + + pointMaster->SetPower(fCompPB->GetFloat(kPower) * 1.e-2f); + + pointMaster->SetProperty(plShadowMaster::kSelfShadow, fCompPB->GetInt(kSelfShadow)); + + hsgResMgr::ResMgr()->AddViaNotify(pointMaster->GetKey(), TRACKED_NEW plObjRefMsg(so->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + return true; +} + +hsBool plShadowLightComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plShadowLightComponent::PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg) +{ + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plShadowComponents.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plShadowComponents.h new file mode 100644 index 00000000..c91f995f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plShadowComponents.h @@ -0,0 +1,112 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plShadowComponents_inc +#define plShadowComponents_inc + +const Class_ID SHADOWCAST_COMP_CID(0x4f447666, 0x73a07cc6); +const Class_ID SHADOWRCV_COMP_CID(0x1d3009ca, 0x4d28537f); +const Class_ID SHADOWLIGHT_COMP_CID(0x2a996151, 0x4f4d1ae1); + +class plMaxNode; +class plErrorMsg; +class plPointShadowMaster; +class plDirectShadowMaster; +class plShadowCaster; + +class plShadowCastComponent : public plComponent +{ +public: +enum +{ + kSelfShadow, + kBlur, + kBlurScale, + kAtten, + kAttenScale, + kBoost, + kQuality, + kLimitRes +}; +protected: + plShadowCaster* fCaster; +public: + plShadowCastComponent(); + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + + static hsBool AddShadowCastModifier(plMaxNode* pNode, plShadowCaster* caster); + static hsBool AddShadowCastModifier(plSceneObject* so, plShadowCaster* caster); +}; + +class plShadowRcvComponent : public plComponent +{ +public: +enum +{ + kForceRadio +}; +enum +{ + kForceOn, + kForceOff +}; +public: + plShadowRcvComponent(); + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +class plShadowLightComponent : public plComponent +{ +public: + enum { + kFalloff, + kMaxDist, + kPower, + kShadowOnly, + kObeyGroups, + kSelfShadow, + kQuality + }; +protected: + hsBool IAddDirectMaster(plMaxNode* node, plSceneObject* so); + hsBool IAddPointMaster(plMaxNode* node, plSceneObject* so); + +public: + plShadowLightComponent(); + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode* pNode, plErrorMsg* pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +#endif // plShadowComponents_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSmoothComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSmoothComponent.cpp new file mode 100644 index 00000000..dfb0a98b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSmoothComponent.cpp @@ -0,0 +1,789 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "../MaxMain/plMaxNode.h" + +#include "hsTypes.h" + +#include "../plDrawable/plDrawableSpans.h" + +// The AccMeshSmooth now does everything the InterMeshSmooth and AvMeshSmooth +// components did, only better and with fewer bugs. +//#include "../plDrawable/plInterMeshSmooth.h" +#include "../plDrawable/plAvMeshSmooth.h" + +#include "../plDrawable/plAccMeshSmooth.h" +#include "../plDrawable/plGeometrySpan.h" +#include "../plDrawable/plSharedMesh.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plDrawInterface.h" + + +const Class_ID CID_SMOOTHCOMP(0x7f926cbc, 0x58df5a44); +const Class_ID CID_SMOOTHAV(0xaf37a4f, 0x8c00991); +const Class_ID CID_SMOOTHBASE(0xebd3ccd, 0x8e85ea6); +const Class_ID CID_SMOOTHSNAP(0x7768074c, 0x65197b77); + + +void DummyCodeIncludeFuncSmooth() +{ + +} + + +//Class that accesses the paramblock below. +class plSmoothComponent : public plComponent +{ +public: + enum { + kSmoothAngle, + kSmoothPos, + kSmoothColor + }; +protected: + hsBool fDoneThis; + hsBool IDoSmooth(plErrorMsg* pErrMsg, hsTArray& spans); + hsBool IGetSpans(plErrorMsg* pErrMsg, hsTArray& spans); + hsBool IReShade(plErrorMsg* pErrMsg); + hsBool ISmoothAll(plErrorMsg* pErrMsg); +public: + plSmoothComponent(); + void DeleteThis() { delete this; } + + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) { fDoneThis = false; return true; } + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { fDoneThis = false; return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plSmoothComponent, gSmoothDesc, "Smooth", "Smooth", COMP_TYPE_GRAPHICS, CID_SMOOTHCOMP) + + +ParamBlockDesc2 gSmoothBk +( + plComponent::kBlkComp, _T("Smooth"), 0, &gSmoothDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SMOOTH, IDS_COMP_SMOOTHS, 0, 0, NULL, + + plSmoothComponent::kSmoothAngle, _T("SmoothAngle"), TYPE_FLOAT, 0, 0, + p_default, 75.0f, + p_range, 0.0, 180.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_SMOOTH_ANGLE, IDC_COMP_SMOOTH_ANGLE_SPIN, 1.f, + end, + + plSmoothComponent::kSmoothPos, _T("SmoothPos"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SMOOTH_POS, + end, + + plSmoothComponent::kSmoothColor, _T("SmoothColor"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SMOOTH_COLOR, + end, + + end +); + +plSmoothComponent::plSmoothComponent() +{ + fClassDesc = &gSmoothDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plSmoothComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + ISmoothAll(pErrMsg); + + return true; +} + +hsBool plSmoothComponent::ISmoothAll(plErrorMsg* pErrMsg) +{ + if( !fDoneThis ) + { + hsTArray spans; + + IGetSpans(pErrMsg, spans); + + IDoSmooth(pErrMsg, spans); + + fDoneThis = true; + } + return true; +} + +hsBool plSmoothComponent::IGetSpans(plErrorMsg* pErrMsg, hsTArray& spans) +{ + spans.SetCount(0); + + UInt32 count = NumTargets(); + UInt32 i; + for( i = 0; i < count; i++ ) + { + plMaxNode *node = (plMaxNode*)GetTarget(i); + if( !(node && node->CanConvert() && node->GetDrawable()) ) + continue; + + plSceneObject* obj = node->GetSceneObject(); + if( !obj ) + continue; + + const plDrawInterface* di = obj->GetDrawInterface(); + if( !di ) + continue; + + UInt8 iDraw; + for( iDraw = 0; iDraw < di->GetNumDrawables(); iDraw++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(iDraw)); + if( !dr ) + continue; + + plDISpanIndex disi = dr->GetDISpans(di->GetDrawableMeshIndex(iDraw)); + + int i; + for( i = 0; i < disi.fIndices.GetCount(); i++ ) + { + spans.Append(dr->GetSourceSpans()[disi.fIndices[i]]); + } + } + } + + return true; +} + +hsBool plSmoothComponent::IDoSmooth(plErrorMsg* pErrMsg, hsTArray& spans) +{ +// if( spans.GetCount() > 1 ) + { + plAccMeshSmooth smoother; + smoother.SetAngle(fCompPB->GetFloat(kSmoothAngle)); + smoother.SetFlags(plAccMeshSmooth::kSmoothNorm); + if( fCompPB->GetInt(kSmoothPos) ) + smoother.SetFlags(smoother.GetFlags() | plAccMeshSmooth::kSmoothPos); + if( fCompPB->GetInt(kSmoothColor) ) + smoother.SetFlags(smoother.GetFlags() | plAccMeshSmooth::kSmoothDiffuse); + smoother.Smooth(spans); + } + + return true; +} + +hsBool plSmoothComponent::IReShade(plErrorMsg* pErrMsg) +{ + UInt32 count = NumTargets(); + UInt32 i; + for( i = 0; i < count; i++ ) + { + plMaxNode *node = (plMaxNode*)GetTarget(i); + if( node ) + node->ShadeMesh(pErrMsg, nil); + } + return true; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +// Now an avatar specific version +/////////////////////////////////////////////////////////////////////////////////////////////// + +//Class that accesses the paramblock below. +class plSmoothAvComponent : public plComponent +{ +public: + enum { + kSmoothAngle, + kDistTol, + kSmoothPos + }; +protected: + hsBool fDoneThis; + hsBool IDoSmooth(plErrorMsg* pErrMsg, hsTArray& spans); + hsBool IGetSpans(plErrorMsg* pErrMsg, hsTArray& spans); + hsBool IReShade(plErrorMsg* pErrMsg); + hsBool ISmoothAll(plErrorMsg* pErrMsg); +public: + plSmoothAvComponent(); + void DeleteThis() { delete this; } + + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) { fDoneThis = false; return true; } + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { fDoneThis = false; return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plSmoothAvComponent, gSmoothAvDesc, "Avatar Smooth", "AvSmooth", COMP_TYPE_GRAPHICS, CID_SMOOTHAV) + + +ParamBlockDesc2 gSmoothAvBk +( + plComponent::kBlkComp, _T("SmoothAv"), 0, &gSmoothAvDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SMOOTHAV, IDS_COMP_SMOOTHAV, 0, 0, NULL, + + plSmoothAvComponent::kSmoothAngle, _T("SmoothAngle"), TYPE_FLOAT, 0, 0, + p_default, 75.0f, + p_range, 0.0, 180.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_SMOOTHAV_ANGLE, IDC_COMP_SMOOTHAV_ANGLE_SPIN, 1.f, + end, + + plSmoothAvComponent::kDistTol, _T("DistTol"), TYPE_FLOAT, 0, 0, + p_default, 0.001f, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_SMOOTHAV_DIST, IDC_COMP_SMOOTHAV_DIST_SPIN, 0.01f, + end, + + plSmoothAvComponent::kSmoothPos, _T("SmoothPos"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SMOOTHAV_POS, + end, + + end +); + +plSmoothAvComponent::plSmoothAvComponent() +{ + fClassDesc = &gSmoothAvDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plSmoothAvComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + ISmoothAll(pErrMsg); + + return true; +} + +hsBool plSmoothAvComponent::ISmoothAll(plErrorMsg* pErrMsg) +{ + if( !fDoneThis ) + { + hsTArray spans; + + IGetSpans(pErrMsg, spans); + + IDoSmooth(pErrMsg, spans); + + fDoneThis = true; + } + return true; +} + +hsBool plSmoothAvComponent::IGetSpans(plErrorMsg* pErrMsg, hsTArray& spans) +{ + spans.SetCount(0); + + UInt32 count = NumTargets(); + UInt32 i; + for( i = 0; i < count; i++ ) + { + plMaxNode *node = (plMaxNode*)GetTarget(i); + if( !node ) + continue; + + plSharedMesh* sharedMesh = node->GetSwappableGeom(); + if( !sharedMesh ) + continue; + + int j; + for( j = 0; j < sharedMesh->fSpans.GetCount(); j++ ) + { + if( sharedMesh->fSpans[j] ) + spans.Append(sharedMesh->fSpans[j]); + } + } + + return true; +} + +hsBool plSmoothAvComponent::IDoSmooth(plErrorMsg* pErrMsg, hsTArray& spans) +{ + if( spans.GetCount() > 1 ) + { + plAccMeshSmooth smoother; + smoother.SetAngle(fCompPB->GetFloat(kSmoothAngle)); + smoother.SetDistTol(fCompPB->GetFloat(kDistTol)); + smoother.SetFlags(plAccMeshSmooth::kSmoothNorm); + if( fCompPB->GetInt(kSmoothPos) ) + smoother.SetFlags(smoother.GetFlags() | plAccMeshSmooth::kSmoothPos); + smoother.Smooth(spans); + } + + return true; +} + +hsBool plSmoothAvComponent::IReShade(plErrorMsg* pErrMsg) +{ + UInt32 count = NumTargets(); + UInt32 i; + for( i = 0; i < count; i++ ) + { + plMaxNode *node = (plMaxNode*)GetTarget(i); + if( node ) + node->ShadeMesh(pErrMsg, nil); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Tiny component to reference a bunch of meshes. These meshes will be used for reference, +// but not actually exported (unless someone else grabs them too.) +/////////////////////////////////////////////////////////////////////////////////////////////////// + +class plSmoothBaseComponent : public plComponent +{ +protected: + hsTArray fSpans; + +public: + enum { + kSmoothAngle, + kDistTol, + kSmoothPos + }; + + plSmoothBaseComponent(); + void DeleteThis() { delete this; } + + hsTArray& GetSpans(plErrorMsg* pErrMsg); + + float GetSmoothAngle() const { return fCompPB->GetFloat(kSmoothAngle); } + float GetDistTol() const { return fCompPB->GetFloat(kDistTol); } + bool SmoothPosition() const { return 0 != fCompPB->GetInt(kSmoothPos); } + + static plSmoothBaseComponent* GetSmoothBaseComp(INode* node); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary below. +CLASS_DESC(plSmoothBaseComponent, gSmoothBaseDesc, "Smoothing Base", "SmoothBase", COMP_TYPE_GRAPHICS, CID_SMOOTHBASE) + +ParamBlockDesc2 gSmoothBaseBk +( + plComponent::kBlkComp, _T("SmoothBase"), 0, &gSmoothBaseDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SMOOTHBASE, IDS_COMP_SMOOTHBASE, 0, 0, NULL, + + plSmoothBaseComponent::kSmoothAngle, _T("SmoothAngle"), TYPE_FLOAT, 0, 0, + p_default, 75.0f, + p_range, 0.0, 180.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_SMOOTHBASE_ANGLE, IDC_COMP_SMOOTHBASE_ANGLE_SPIN, 1.f, + end, + + plSmoothBaseComponent::kDistTol, _T("DistTol"), TYPE_FLOAT, 0, 0, + p_default, 0.001f, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_SMOOTHBASE_DIST, IDC_COMP_SMOOTHBASE_DIST_SPIN, 0.01f, + end, + + plSmoothBaseComponent::kSmoothPos, _T("SmoothPos"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SMOOTHBASE_POS, + end, + + end +); + +plSmoothBaseComponent::plSmoothBaseComponent() +{ + fClassDesc = &gSmoothBaseDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plSmoothBaseComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + node->SetDrawable(false); + if (!node->GetSwappableGeom()) + node->SetSwappableGeom(new plSharedMesh); + + fSpans.SetCount(0); + + return true; +} + +hsBool plSmoothBaseComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + GetSpans(pErrMsg); + return true; +} + +plSmoothBaseComponent* plSmoothBaseComponent::GetSmoothBaseComp(INode* node) +{ + if( !node ) + return nil; + + plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); + if( comp == nil ) + return nil; + + if( comp->ClassID() == CID_SMOOTHBASE ) + return (plSmoothBaseComponent*)comp; + + return nil; +} + + +// Only valid after the MakeMesh phase. +hsTArray& plSmoothBaseComponent::GetSpans(plErrorMsg* pErrMsg) +{ + if( !fSpans.GetCount() ) + { + UInt32 count = NumTargets(); + UInt32 i; + hsTArray spans; + + for( i = 0; i < count; i++ ) + { + plMaxNode *node = (plMaxNode*)GetTarget(i); + if( !node ) + continue; + + plSharedMesh* sharedMesh = node->GetSwappableGeom(); + if( !sharedMesh ) + continue; + + int j; + for( j = 0; j < sharedMesh->fSpans.GetCount(); j++ ) + { + if( sharedMesh->fSpans[j] ) + { + spans.Append(sharedMesh->fSpans[j]); + plAvMeshSmooth::XfmSpan xfmSpan; + xfmSpan.fSpan = sharedMesh->fSpans[j]; + + xfmSpan.fSpanToNeutral = node->GetOTM44() * node->GetLocalToVert44(); + xfmSpan.fSpanToNeutral.GetInverse(&xfmSpan.fNeutralToSpan); + + xfmSpan.fSpanToNeutral.GetTranspose(&xfmSpan.fNormNeutralToSpan); + xfmSpan.fNeutralToSpan.GetTranspose(&xfmSpan.fNormSpanToNeutral); + + fSpans.Append(xfmSpan); + } + } + } + if( spans.GetCount() > 1 ) + { + plAccMeshSmooth smoother; + smoother.SetAngle(fCompPB->GetFloat(kSmoothAngle)); + smoother.SetDistTol(fCompPB->GetFloat(kDistTol)); + smoother.SetFlags(plAccMeshSmooth::kSmoothNorm); + if( fCompPB->GetInt(kSmoothPos) ) + smoother.SetFlags(smoother.GetFlags() | plAccMeshSmooth::kSmoothPos); + smoother.Smooth(spans); + } + } + + return fSpans; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +// Now another avatar specific version +/////////////////////////////////////////////////////////////////////////////////////////////// + +//Class that accesses the paramblock below. +class plSmoothSnapComponent : public plComponent +{ +public: + enum { + kSmoothBase, + kSmoothAngle, + kDistTol, + kSmoothPos + }; +protected: + hsBool fDoneThis; + + hsTArray& IGetSrcSpans(plErrorMsg* pErrMsg); + + hsBool IDoSmooth(plErrorMsg* pErrMsg, hsTArray& srcSpans, hsTArray& dstSpans); + hsBool IGetDstSpans(plErrorMsg* pErrMsg, hsTArray& spans); + hsBool IReShade(plErrorMsg* pErrMsg); + hsBool ISmoothAll(plErrorMsg* pErrMsg); +public: + plSmoothSnapComponent(); + void DeleteThis() { delete this; } + + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) { fDoneThis = false; return true; } + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { fDoneThis = false; return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + + +class plSmoothBaseSelProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void DeleteThis() { } +}; + +#include "plPickNode.h" + +BOOL plSmoothBaseSelProc::DlgProc(TimeValue t, IParamMap2 *paramMap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = paramMap->GetParamBlock(); + INode* node = pb->GetINode(plSmoothSnapComponent::kSmoothBase); + TSTR newName(node ? node->GetName() : "Pick"); + ::SetWindowText(::GetDlgItem(hWnd, IDC_COMP_SMOOTH_CHOSE), newName); + } + return true; + + case WM_COMMAND: + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_COMP_SMOOTH_CHOSE) ) + { + IParamBlock2 *pb = paramMap->GetParamBlock(); + std::vector cids; + cids.push_back(CID_SMOOTHBASE); + if( plPick::Node(pb, plSmoothSnapComponent::kSmoothBase, &cids, true, true) ) + { + INode* node = pb->GetINode(plSmoothSnapComponent::kSmoothBase); + TSTR newName(node ? node->GetName() : "Pick"); + ::SetWindowText(::GetDlgItem(hWnd, IDC_COMP_SMOOTH_CHOSE), newName); + paramMap->Invalidate(plSmoothSnapComponent::kSmoothBase); + ShowWindow(hWnd, SW_HIDE); + ShowWindow(hWnd, SW_SHOW); + } + + return false; + } + return true; + } + + return false; +} + +plSmoothBaseSelProc gSmoothBaseSelProc; + +//Max desc stuff necessary below. +CLASS_DESC(plSmoothSnapComponent, gSmoothSnapDesc, "Snap to Base", "SnapTo", COMP_TYPE_GRAPHICS, CID_SMOOTHSNAP) + + +ParamBlockDesc2 gSmoothSnapBk +( + plComponent::kBlkComp, _T("SmoothSnap"), 0, &gSmoothSnapDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SMOOTHSNAP, IDS_COMP_SMOOTHSNAP, 0, 0, &gSmoothBaseSelProc, + + plSmoothSnapComponent::kSmoothBase, _T("SmoothBase"), TYPE_INODE, 0, 0, + end, + + plSmoothSnapComponent::kSmoothAngle, _T("SmoothAngle"), TYPE_FLOAT, 0, 0, + p_default, 75.0f, + p_range, 0.0, 180.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_SMOOTHSNAP_ANGLE, IDC_COMP_SMOOTHSNAP_ANGLE_SPIN, 1.f, + end, + + plSmoothSnapComponent::kDistTol, _T("DistTol"), TYPE_FLOAT, 0, 0, + p_default, 0.01f, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_SMOOTHSNAP_DIST, IDC_COMP_SMOOTHSNAP_DIST_SPIN, 0.01f, + end, + + plSmoothSnapComponent::kSmoothPos, _T("SmoothPos"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SMOOTHSNAP_POS, + end, + + end +); + +plSmoothSnapComponent::plSmoothSnapComponent() +{ + fClassDesc = &gSmoothSnapDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plSmoothSnapComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + ISmoothAll(pErrMsg); + + return true; +} + +hsBool plSmoothSnapComponent::ISmoothAll(plErrorMsg* pErrMsg) +{ + if( !fDoneThis ) + { + fDoneThis = true; + + hsTArray& srcSpans = IGetSrcSpans(pErrMsg); + + if( !srcSpans.GetCount() ) + return true; + + hsTArray dstSpans; + + if( !IGetDstSpans(pErrMsg, dstSpans) ) + return true; + + IDoSmooth(pErrMsg, srcSpans, dstSpans); + + } + return true; +} + +hsTArray& plSmoothSnapComponent::IGetSrcSpans(plErrorMsg* pErrMsg) +{ + static hsTArray emptySpans; + + plSmoothBaseComponent* baseComp = plSmoothBaseComponent::GetSmoothBaseComp(fCompPB->GetINode(kSmoothBase, 0, 0)); + if( !baseComp ) + return emptySpans; + + return baseComp->GetSpans(pErrMsg); +} + +hsBool plSmoothSnapComponent::IGetDstSpans(plErrorMsg* pErrMsg, hsTArray& spans) +{ + hsTArray geoSpans; + spans.SetCount(0); + + UInt32 count = NumTargets(); + UInt32 i; + for( i = 0; i < count; i++ ) + { + plMaxNode *node = (plMaxNode*)GetTarget(i); + if( !node ) + continue; + + plSharedMesh* sharedMesh = node->GetSwappableGeom(); + if( !sharedMesh ) + continue; + + int j; + for( j = 0; j < sharedMesh->fSpans.GetCount(); j++ ) + { + if( sharedMesh->fSpans[j] ) + { + geoSpans.Append(sharedMesh->fSpans[j]); + + plAvMeshSmooth::XfmSpan xfmSpan; + xfmSpan.fSpan = sharedMesh->fSpans[j]; + + xfmSpan.fSpanToNeutral = node->GetOTM44() * node->GetLocalToVert44(); + xfmSpan.fSpanToNeutral.GetInverse(&xfmSpan.fNeutralToSpan); + + xfmSpan.fSpanToNeutral.GetTranspose(&xfmSpan.fNormNeutralToSpan); + xfmSpan.fNeutralToSpan.GetTranspose(&xfmSpan.fNormSpanToNeutral); + + spans.Append(xfmSpan); + } + } + } + + // Smooth them with themselves before we pass them off to be snapped to the base. + // We'll use the base component's parameters, because ours will be sloppier to + // ensure proper snapping. + if( geoSpans.GetCount() ) + { + plSmoothBaseComponent* baseComp = plSmoothBaseComponent::GetSmoothBaseComp(fCompPB->GetINode(kSmoothBase, 0, 0)); + if( !baseComp ) + return 0; + + plAccMeshSmooth smoother; + smoother.SetAngle(baseComp->GetSmoothAngle()); + smoother.SetDistTol(baseComp->GetDistTol()); + smoother.SetFlags(plAccMeshSmooth::kSmoothNorm); + if( baseComp->SmoothPosition() ) + smoother.SetFlags(smoother.GetFlags() | plAccMeshSmooth::kSmoothPos); + smoother.Smooth(geoSpans); + } + + return spans.GetCount(); +} + +hsBool plSmoothSnapComponent::IDoSmooth(plErrorMsg* pErrMsg, hsTArray& srcSpans, hsTArray& dstSpans) +{ + if( srcSpans.GetCount() && dstSpans.GetCount() ) + { + plAvMeshSmooth smoother; + smoother.SetAngle(fCompPB->GetFloat(kSmoothAngle)); + smoother.SetDistTol(fCompPB->GetFloat(kDistTol)); + smoother.SetFlags(plAccMeshSmooth::kSmoothNorm); + if( fCompPB->GetInt(kSmoothPos) ) + smoother.SetFlags(smoother.GetFlags() | plAccMeshSmooth::kSmoothPos); + smoother.Smooth(srcSpans, dstSpans); + } + + return true; +} + +hsBool plSmoothSnapComponent::IReShade(plErrorMsg* pErrMsg) +{ + UInt32 count = NumTargets(); + UInt32 i; + for( i = 0; i < count; i++ ) + { + plMaxNode *node = (plMaxNode*)GetTarget(i); + if( node ) + node->ShadeMesh(pErrMsg, nil); + } + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSoftVolumeComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSoftVolumeComponent.cpp new file mode 100644 index 00000000..ff6500bb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSoftVolumeComponent.cpp @@ -0,0 +1,1594 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "dummy.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "plMiscComponents.h" +#include "plSoftVolumeComponent.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "../MaxMain/plMaxNode.h" + +#include "../plIntersect/plSoftVolumeTypes.h" +#include "../plIntersect/plVolumeIsect.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plObjRefMsg.h" +#include "hsResMgr.h" + +#include "../plGLight/plLightInfo.h" +#include "../plScene/plOccluder.h" + +#include "../pnSceneObject/plDrawInterface.h" +#include "../plScene/plVisRegion.h" +#include "../plScene/plRelevanceRegion.h" + +void DummyCodeIncludeFuncSoftVolume() {} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +class plVolumeHitCallback : public HitByNameDlgCallback +{ +protected: + INode* fOwner; + IParamBlock2* fPB; + ParamID fNodeListID; + BOOL fSingleSel; + TCHAR fTitle[ 128 ]; + +public: + plVolumeHitCallback(INode* owner, IParamBlock2 *pb, ParamID nodeListID, TCHAR *title = nil, BOOL singleSel=false ); + + virtual TCHAR *dialogTitle() { return fTitle; } + virtual TCHAR *buttonText() { return "OK"; } + virtual int filter(INode *node); + virtual void proc(INodeTab &nodeTab); + virtual BOOL showHiddenAndFrozen() { return TRUE; } + virtual BOOL singleSelect() { return fSingleSel; } +}; + +plVolumeHitCallback::plVolumeHitCallback(INode* owner, IParamBlock2 *pb, ParamID nodeListID, TCHAR *title, BOOL singleSel) +: fOwner(owner), + fPB(pb), + fNodeListID(nodeListID), + fSingleSel(singleSel) +{ + strcpy( fTitle, title ); +} + +int plVolumeHitCallback::filter(INode *node) +{ + if( node == fOwner ) + return FALSE; + + plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); + + // If this is an activator type component + if( comp ) + { + if( (comp->ClassID() == SOFTVOLUME_CID) + || (comp->ClassID() == SOFTVOLUME_UNION_CID) + || (comp->ClassID() == SOFTVOLUME_ISECT_CID) + || (comp->ClassID() == SOFTVOLUME_NEGATE_CID) ) + { + + if( !fSingleSel ) + { + // And we don't already reference it + int i; + for( i = 0; i < fPB->Count(fNodeListID); i++ ) + { + if( fPB->GetINode(fNodeListID, 0, i) == node ) + return FALSE; + } + } + + // And this wouldn't create a cyclical reference (Max doesn't like those) + if (comp->TestForLoop(FOREVER, fPB) == REF_FAIL) + return FALSE; + + return TRUE; + } + } + + return FALSE; +} + +void plVolumeHitCallback::proc(INodeTab &nodeTab) +{ + if( fSingleSel ) + { + if( nodeTab.Count() ) + fPB->SetValue(fNodeListID, TimeValue(0), nodeTab[0]); + else + fPB->SetValue(fNodeListID, TimeValue(0), (INode*)nil); + } + else + fPB->Append(fNodeListID, nodeTab.Count(), &nodeTab[0]); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +//// plSingleCompSelProc Functions ////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + +plSingleCompSelProc::plSingleCompSelProc(ParamID nodeID, int dlgItem, TCHAR *title) : fNodeID(nodeID), fDlgItem(dlgItem) +{ + strcpy( fTitle, title ); +} + +BOOL plSingleCompSelProc::DlgProc(TimeValue t, IParamMap2 *paramMap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = paramMap->GetParamBlock(); + INode* node = pb->GetINode(fNodeID); + TSTR newName(node ? node->GetName() : "Pick"); + ::SetWindowText(::GetDlgItem(hWnd, fDlgItem), newName); + } + return true; + + case WM_COMMAND: + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == fDlgItem) ) + { + // Adding a volume. Set it and refresh the UI to show it in our list. + IParamBlock2 *pb = paramMap->GetParamBlock(); + plVolumeHitCallback hitCB((INode*)pb->GetOwner(), pb, fNodeID, fTitle, true ); + GetCOREInterface()->DoHitByNameDialog(&hitCB); + INode* node = pb->GetINode(fNodeID); + TSTR newName(node ? node->GetName() : "Pick"); + ::SetWindowText(::GetDlgItem(hWnd, fDlgItem), newName); + paramMap->Invalidate(fNodeID); + ShowWindow(hWnd, SW_HIDE); + ShowWindow(hWnd, SW_SHOW); + + return false; + } + return true; + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + +// Moved class declaration to .h -mcn + + +hsBool plSoftVolBaseComponent::SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg) +{ + fSoftKey = nil; + fValid = false; + return true; +} + +plKey plSoftVolBaseComponent::GetSoftVolume() +{ + if( !fSoftKey ) + ICreateSoftVolume(); + return fSoftKey; +} + +plSoftVolBaseComponent* plSoftVolBaseComponent::GetSoftComponent(INode* node) +{ + if( node == nil ) + return nil; + + plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); + if( comp == nil ) + return nil; + + return GetSoftComponent( comp ); +} + +plSoftVolBaseComponent* plSoftVolBaseComponent::GetSoftComponent(plComponentBase *comp) +{ + if( comp != nil && + (comp->ClassID() == SOFTVOLUME_CID) + || (comp->ClassID() == SOFTVOLUME_UNION_CID) + || (comp->ClassID() == SOFTVOLUME_ISECT_CID) + || (comp->ClassID() == SOFTVOLUME_NEGATE_CID) ) + { + return (plSoftVolBaseComponent*)comp; + } + return nil; +} + +void plSoftVolBaseComponent::IAddSubVolume(plKey masterKey, plKey subKey) +{ + if( masterKey && subKey ) + hsgResMgr::ResMgr()->AddViaNotify(subKey, TRACKED_NEW plGenRefMsg(masterKey, plRefMsg::kOnCreate, 0, plSoftVolume::kSubVolume), plRefFlags::kActiveRef); +} + +plKey plSoftVolBaseComponent::ISetVolumeKey(plSoftVolume* vol) +{ + int i; + for( i = 0; i < NumTargets(); i++ ) + { + if( GetTarget(i) ) + break; + } + hsAssert(i < NumTargets(), "We're not attached to anything?"); + plKey key = hsgResMgr::ResMgr()->NewKey(GetINode()->GetName(), vol, GetTarget(i)->GetLocation()); + + return key; +} + +plKey plSoftVolBaseComponent::IInvertVolume(plKey subKey) +{ + if( !subKey ) + return nil; + + plSoftVolumeInvert* invert = TRACKED_NEW plSoftVolumeInvert; + plKey invertKey = ISetVolumeKey(invert); + + IAddSubVolume(invertKey, subKey); + + return invertKey; +} + +hsBool plSoftVolBaseComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg) +{ + fSoftKey = nil; + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// SoftVolume Component +// +//Class that accesses the paramblock below. +class plSoftVolComponent : public plSoftVolBaseComponent +{ +public: + enum { + kSoftDistance, + kPartialEnabled, + kInsidePower, + kOutsidePower + }; + +private: + + plKey ISetFromIsect(plMaxNodeBase* pNode, plVolumeIsect* isect); + plKey ICreateSoftVolume(); + plKey ICreateFromNode(plMaxNodeBase* pNode); + plKey ICreateFromDummyObject(plMaxNodeBase* pNode, Object* obj); + plKey ICreateFromTriObject(plMaxNodeBase* pNode, Object* obj); + +protected: + void ICreateVolume(); +public: + plSoftVolComponent(); + void DeleteThis() { delete this; } + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg); + + virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); } +}; + +// +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plSoftVolObjAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + } +}; +plSoftVolObjAccessor gSoftVolObjAccessor; + + +class plSoftVolComponentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + map->SetTooltip(plSoftVolComponent::kSoftDistance, TRUE, "Distance effect fades in and out." ); + } + return true; + + } + + return false; + } + void DeleteThis() {} +}; +static plSoftVolComponentProc gSoftVolProc; + + + +CLASS_DESC(plSoftVolComponent, gSoftVolDesc, "Soft Region", "SoftRegion", COMP_TYPE_VOLUME, SOFTVOLUME_CID) + + + +ParamBlockDesc2 gSoftVolBk +( + plComponent::kBlkComp, _T("SoftRegion"), 0, &gSoftVolDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SOFTVOLUME, IDS_COMP_SOFTVOLUMES, 0, 0, &gSoftVolProc, + + plSoftVolComponent::kSoftDistance, _T("Soft Distance"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 500.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SOFTVOL_SOFT, IDC_COMP_SOFTVOL_SOFT_SPIN, 1.0, + end, + + plSoftVolComponent::kPartialEnabled, _T("EnablePartial"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SOFTVOL_ENABLEPARTIAL, + p_enable_ctrls, 2, plSoftVolComponent::kInsidePower, plSoftVolComponent::kOutsidePower, + end, + + plSoftVolComponent::kInsidePower, _T("PowerInside"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SOFTVOL_INSIDE, IDC_COMP_SOFTVOL_INSIDE_SPIN, 1.0, + end, + + plSoftVolComponent::kOutsidePower, _T("PowerOutside"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SOFTVOL_OUTSIDE, IDC_COMP_SOFTVOL_OUTSIDE_SPIN, 1.0, + end, + + end + +); + +plSoftVolComponent::plSoftVolComponent() +{ + fClassDesc = &gSoftVolDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plSoftVolComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg) +{ + if( !plSoftVolBaseComponent::SetupProperties(pNode, errMsg) ) + return false; + + int i; + for( i = 0; i < pNode->NumAttachedComponents(); i++ ) + { + plComponentBase* comp = pNode->GetAttachedComponent(i); + if( comp + && (comp != this) + && (comp->ClassID() == ClassID()) ) + { + errMsg->Set(true, pNode->GetName(), "Multiple SoftRegion components attached, there can be only one").CheckAndAsk(); + errMsg->Set(false); + + fValid = false; + return true; + } + } + // + // The node must either point to a Mesh or a dummy box, otherwise skip it. + // + Object *obj = pNode->EvalWorldState(TimeValue(0)).obj; + if( !obj ) + { + return true; + } + + if( obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) ) + { + fValid = true; + } + else if( obj->CanConvertToType(triObjectClassID) ) + { + fValid = true; + } + pNode->SetDrawable(false); + + return true; +} + +plKey plSoftVolComponent::ICreateSoftVolume() +{ + if( !fValid ) + return nil; + + if( !NumTargets() ) + return nil; + + if( NumTargets() < 2 ) + { + return fSoftKey = ICreateFromNode(GetTarget(0)); + } + + plSoftVolumeUnion* compound = TRACKED_NEW plSoftVolumeUnion; + fSoftKey = ISetVolumeKey(compound); + + int i; + for( i = 0; i < NumTargets(); i++ ) + { + if( GetTarget(i) ) + { + plKey subKey = ICreateFromNode(GetTarget(i)); + IAddSubVolume(fSoftKey, subKey); + } + } + if( !compound->GetNumSubs() ) + { + delete compound; + compound = nil; + fSoftKey = nil; + } + + return fSoftKey; +} + +plKey plSoftVolComponent::ICreateFromNode(plMaxNodeBase* pNode) +{ + if( !pNode ) + return nil; + + if( !fValid ) + return nil; + + if( !pNode->GetSceneObject() ) + return nil; + + // Go ahead and make it here, so it'll be available for aggregaters in the Convert pass + Object *obj = pNode->EvalWorldState(TimeValue(0)).obj; + + if( obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) ) + { + return ICreateFromDummyObject(pNode, obj); + } + else if( obj->CanConvertToType(triObjectClassID) ) + { + return ICreateFromTriObject(pNode, obj); + } + + return nil; +} + +plKey plSoftVolComponent::ICreateFromDummyObject(plMaxNodeBase* pNode, Object* obj) +{ + DummyObject* dummy = (DummyObject*)obj; + Box3 bnd = dummy->GetBox(); + + plParallelIsect* isect = TRACKED_NEW plParallelIsect; + isect->SetNumPlanes(3); + + hsMatrix44 v2l = pNode->GetVertToLocal44(); + hsMatrix44 l2v = pNode->GetLocalToVert44(); + + hsPoint3 corner(bnd.pmin.x, bnd.pmin.y, bnd.pmin.z); + hsVector3 axis(bnd.pmax.x - bnd.pmin.x, bnd.pmax.y - bnd.pmin.y, bnd.pmax.z - bnd.pmin.z); + + int i; + for( i = 0; i < 3; i++ ) + { + hsPoint3 bot = v2l * corner; + hsPoint3 top = corner; + top[i] += axis[i]; + top = v2l * top; + + isect->SetPlane(i, bot, top); + } + + return ISetFromIsect(pNode, isect); +} + +plKey plSoftVolComponent::ICreateFromTriObject(plMaxNodeBase* pNode, Object* obj) +{ + TriObject *meshObj = (TriObject *)obj->ConvertToType(TimeValue(0), triObjectClassID); + + Mesh* mesh = &meshObj->mesh; + + hsMatrix44 v2l = pNode->GetVertToLocal44(); + hsMatrix44 l2v = pNode->GetLocalToVert44(); + + plConvexIsect* isect = TRACKED_NEW plConvexIsect; + int i; + for( i = 0; i < mesh->getNumFaces(); i++ ) + { + Face *maxFace = &mesh->faces[ i ]; + + Point3 v0 = mesh->verts[ maxFace->v[ 0 ] ]; + Point3 v1 = mesh->verts[ maxFace->v[ 1 ] ]; + Point3 v2 = mesh->verts[ maxFace->v[ 2 ] ]; + + hsPoint3 p0(v0.x, v0.y, v0.z); + hsPoint3 p1(v1.x, v1.y, v1.z); + hsPoint3 p2(v2.x, v2.y, v2.z); + + p0 = v2l * p0; + p1 = v2l * p1; + p2 = v2l * p2; + + hsVector3 n = hsVector3(&p1, &p0) % hsVector3(&p2, &p0); + + isect->AddPlane(n, p0); + } + + plKey retVal = ISetFromIsect(pNode, isect); + + if( meshObj != obj ) + meshObj->DeleteThis(); + + return retVal; +} + +plKey plSoftVolComponent::ISetFromIsect(plMaxNodeBase* pNode, plVolumeIsect* isect) +{ + isect->SetTransform(pNode->GetLocalToWorld44(), pNode->GetWorldToLocal44()); + + plSoftVolumeSimple* simple = TRACKED_NEW plSoftVolumeSimple; + simple->SetVolume(isect); + simple->SetDistance(fCompPB->GetFloat(kSoftDistance)); + + if( fCompPB->GetInt(kPartialEnabled) ) + { + simple->SetInsideStrength(fCompPB->GetFloat(kInsidePower) * 0.01f); + simple->SetOutsideStrength(fCompPB->GetFloat(kOutsidePower) * 0.01f); + } + + plSceneObject* sceneObj = pNode->GetSceneObject(); + + plKey retVal = ISetVolumeKey(simple); + + hsgResMgr::ResMgr()->AddViaNotify(simple->GetKey(), TRACKED_NEW plObjRefMsg(sceneObj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + return retVal; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +// Union of volumes +///////////////////////////////////////////////////////////////////////////////////////////////// + +//Class that accesses the paramblock below. +class plSoftVolUnionComponent : public plSoftVolBaseComponent +{ +public: + enum { + kSubVolumes, + kPartialEnabled, + kInsidePower, + kOutsidePower + }; +protected: + + plKey ICreateSoftVolume(); + +public: + plSoftVolUnionComponent(); + void DeleteThis() { delete this; } + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg); +}; + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plSoftVolUnionAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (id == plSoftVolUnionComponent::kSubVolumes) + { + plSoftVolUnionComponent *comp = (plSoftVolUnionComponent*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +plSoftVolUnionAccessor gSoftVolUnionAccessor; + + +class plSoftVolUnionComponentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + map->SetTooltip(plSoftVolUnionComponent::kSubVolumes, TRUE, "Select sub-volumes to combine into larger." ); + } + return true; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ADD_VOLUME) + { + // Adding a volume. Set it and refresh the UI to show it in our list. + plVolumeHitCallback hitCB((INode*)map->GetParamBlock()->GetOwner(), map->GetParamBlock(), plSoftVolUnionComponent::kSubVolumes, "Select sub-volumes"); + GetCOREInterface()->DoHitByNameDialog(&hitCB); + map->Invalidate(plSoftVolUnionComponent::kSubVolumes); + return TRUE; + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plSoftVolUnionComponentProc gSoftVolUnionProc; + + + +CLASS_DESC(plSoftVolUnionComponent, gSoftVolUnionDesc, "Soft Region Union", "SoftRegionUnion", COMP_TYPE_VOLUME, SOFTVOLUME_UNION_CID) + + + +ParamBlockDesc2 gSoftVolUnionBk +( + plComponent::kBlkComp, _T("SoftRegionUnion"), 0, &gSoftVolUnionDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SOFTVOLUME_UNION, IDS_COMP_SOFTVOLUME_UNION, 0, 0, &gSoftVolUnionProc, + + plSoftVolUnionComponent::kSubVolumes, _T("SubRegions"), TYPE_INODE_TAB, 0, P_CAN_CONVERT, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + p_classID, SOFTVOLUME_BASE_CID, + p_accessor, &gSoftVolUnionAccessor, + end, + + plSoftVolUnionComponent::kPartialEnabled, _T("EnablePartial"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SOFTUNION_ENABLEPARTIAL, + p_enable_ctrls, 2, plSoftVolUnionComponent::kInsidePower, plSoftVolUnionComponent::kOutsidePower, + end, + + plSoftVolUnionComponent::kInsidePower, _T("PowerInside"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SOFTUNION_INSIDE, IDC_COMP_SOFTUNION_INSIDE_SPIN, 1.0, + end, + + plSoftVolComponent::kOutsidePower, _T("PowerOutside"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SOFTUNION_OUTSIDE, IDC_COMP_SOFTUNION_OUTSIDE_SPIN, 1.0, + end, + + end + +); + +plSoftVolUnionComponent::plSoftVolUnionComponent() +{ + fClassDesc = &gSoftVolUnionDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plSoftVolUnionComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg) +{ + return plSoftVolBaseComponent::SetupProperties(pNode, errMsg); +} + +plKey plSoftVolUnionComponent::ICreateSoftVolume() +{ + int numSubs = fCompPB->Count(kSubVolumes); + if( numSubs < 0 ) + return nil; + + if( numSubs < 2 ) + return fSoftKey = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSubVolumes, 0, 0))->GetSoftVolume(); + + plSoftVolumeUnion* compound = TRACKED_NEW plSoftVolumeUnion; + fSoftKey = ISetVolumeKey(compound); + + int i; + for( i = 0; i < numSubs; i++ ) + { + plSoftVolBaseComponent *comp = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSubVolumes, 0, i)); + if (comp) + { + plKey subKey = comp->GetSoftVolume(); + IAddSubVolume(fSoftKey, subKey); + } + } + + if( fCompPB->GetInt(kPartialEnabled) ) + { + compound->SetInsideStrength(fCompPB->GetFloat(kInsidePower) * 0.01f); + compound->SetOutsideStrength(fCompPB->GetFloat(kOutsidePower) * 0.01f); + } + + return fSoftKey; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +// Intersection of volumes +///////////////////////////////////////////////////////////////////////////////////////////////// +//Class that accesses the paramblock below. +class plSoftVolIsectComponent : public plSoftVolBaseComponent +{ +public: + enum { + kSubVolumes, + kPartialEnabled, + kInsidePower, + kOutsidePower + }; +protected: + + plKey ICreateSoftVolume(); + +public: + plSoftVolIsectComponent(); + void DeleteThis() { delete this; } + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg); +}; + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plSoftVolIsectAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (id == plSoftVolIsectComponent::kSubVolumes) + { + plSoftVolIsectComponent *comp = (plSoftVolIsectComponent*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +plSoftVolIsectAccessor gSoftVolIsectAccessor; + + +class plSoftVolIsectComponentProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + map->SetTooltip(plSoftVolIsectComponent::kSubVolumes, TRUE, "Select sub-volumes to combine into larger." ); + } + return true; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ADD_VOLUME) + { + // Adding a volume. Set it and refresh the UI to show it in our list. + plVolumeHitCallback hitCB((INode*)map->GetParamBlock()->GetOwner(), map->GetParamBlock(), plSoftVolIsectComponent::kSubVolumes, "Select sub-volumes"); + GetCOREInterface()->DoHitByNameDialog(&hitCB); + map->Invalidate(plSoftVolIsectComponent::kSubVolumes); + return TRUE; + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plSoftVolIsectComponentProc gSoftVolIsectProc; + + + +CLASS_DESC(plSoftVolIsectComponent, gSoftVolIsectDesc, "Soft Region Intersection", "SoftRegionIsect", COMP_TYPE_VOLUME, SOFTVOLUME_ISECT_CID) + + + +ParamBlockDesc2 gSoftVolIsectBk +( + plComponent::kBlkComp, _T("SoftRegionIsect"), 0, &gSoftVolIsectDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SOFTVOLUME_ISECT, IDS_COMP_SOFTVOLUME_ISECT, 0, 0, &gSoftVolIsectProc, + + plSoftVolIsectComponent::kSubVolumes, _T("SubRegions"), TYPE_INODE_TAB, 0, P_CAN_CONVERT, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + p_classID, SOFTVOLUME_BASE_CID, + p_accessor, &gSoftVolIsectAccessor, + end, + + plSoftVolIsectComponent::kPartialEnabled, _T("EnablePartial"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SOFTISECT_ENABLEPARTIAL, + p_enable_ctrls, 2, plSoftVolIsectComponent::kInsidePower, plSoftVolIsectComponent::kOutsidePower, + end, + + plSoftVolIsectComponent::kInsidePower, _T("PowerInside"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SOFTISECT_INSIDE, IDC_COMP_SOFTISECT_INSIDE_SPIN, 1.0, + end, + + plSoftVolIsectComponent::kOutsidePower, _T("PowerOutside"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SOFTISECT_OUTSIDE, IDC_COMP_SOFTISECT_OUTSIDE_SPIN, 1.0, + end, + + end + +); + +plSoftVolIsectComponent::plSoftVolIsectComponent() +{ + fClassDesc = &gSoftVolIsectDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plSoftVolIsectComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg) +{ + return plSoftVolBaseComponent::SetupProperties(pNode, errMsg); +} + +plKey plSoftVolIsectComponent::ICreateSoftVolume() +{ + int numSubs = fCompPB->Count(kSubVolumes); + if( numSubs < 0 ) + return nil; + + if( numSubs < 2 ) + return fSoftKey = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSubVolumes, 0, 0))->GetSoftVolume(); + + plSoftVolumeIntersect* compound = TRACKED_NEW plSoftVolumeIntersect; + fSoftKey = ISetVolumeKey(compound); + + int i; + for( i = 0; i < numSubs; i++ ) + { + plKey subKey = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSubVolumes, 0, i))->GetSoftVolume(); + IAddSubVolume(fSoftKey, subKey); + } + + if( fCompPB->GetInt(kPartialEnabled) ) + { + compound->SetInsideStrength(fCompPB->GetFloat(kInsidePower) * 0.01f); + compound->SetOutsideStrength(fCompPB->GetFloat(kOutsidePower) * 0.01f); + } + + return fSoftKey; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +// Inversion of a (possibly complex) volume +///////////////////////////////////////////////////////////////////////////////////////////////// +//Class that accesses the paramblock below. +class plSoftVolNegateComponent : public plSoftVolBaseComponent +{ +public: + enum { + kSubVolume, + kPartialEnabled, + kInsidePower, + kOutsidePower + }; +protected: + + plKey ICreateSoftVolume(); + +public: + plSoftVolNegateComponent(); + void DeleteThis() { delete this; } + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg); +}; + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plSoftVolNegateAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (id == plSoftVolNegateComponent::kSubVolume) + { + plSoftVolNegateComponent *comp = (plSoftVolNegateComponent*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +plSoftVolNegateAccessor gSoftVolNegateAccessor; + + +static plSingleCompSelProc gSoftVolNegateSingleSel(plSoftVolNegateComponent::kSubVolume, IDC_COMP_SOFTVOLUME_NEGATE_CHOOSE_SUB, "Select sub-volumes"); + + + +CLASS_DESC(plSoftVolNegateComponent, gSoftVolNegateDesc, "Soft Region Inverted", "SoftRegionInvert", COMP_TYPE_VOLUME, SOFTVOLUME_NEGATE_CID) + + + +ParamBlockDesc2 gSoftVolNegateBk +( + plComponent::kBlkComp, _T("SoftRegionNegate"), 0, &gSoftVolNegateDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SOFTVOLUME_NEGATE, IDS_COMP_SOFTVOLUME_NEGATE, 0, 0, &gSoftVolNegateSingleSel, + + plSoftVolNegateComponent::kSubVolume, _T("SubRegion"), TYPE_INODE, 0, 0, + p_prompt, IDS_COMP_SOFTVOLUME_NEGATE, + p_accessor, &gSoftVolNegateAccessor, + end, + + plSoftVolNegateComponent::kPartialEnabled, _T("EnablePartial"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SOFTNEGATE_ENABLEPARTIAL, + p_enable_ctrls, 2, plSoftVolComponent::kInsidePower, plSoftVolComponent::kOutsidePower, + end, + + plSoftVolNegateComponent::kInsidePower, _T("PowerInside"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SOFTNEGATE_INSIDE, IDC_COMP_SOFTNEGATE_INSIDE_SPIN, 1.0, + end, + + plSoftVolNegateComponent::kOutsidePower, _T("PowerOutside"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 100.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_SOFTNEGATE_OUTSIDE, IDC_COMP_SOFTNEGATE_OUTSIDE_SPIN, 1.0, + end, + + + end + +); + +plSoftVolNegateComponent::plSoftVolNegateComponent() +{ + fClassDesc = &gSoftVolNegateDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plSoftVolNegateComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg) +{ + return plSoftVolBaseComponent::SetupProperties(pNode, errMsg); +} + +plKey plSoftVolNegateComponent::ICreateSoftVolume() +{ + if( NumTargets() < 1 ) + return nil; + + INode* subNode = fCompPB->GetINode(kSubVolume); + if( subNode ) + { + plKey subKey = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSubVolume))->GetSoftVolume(); + if( subKey ) + { + fSoftKey = IInvertVolume(subKey); + + if( fCompPB->GetInt(kPartialEnabled) ) + { + plSoftVolumeInvert* invert = plSoftVolumeInvert::ConvertNoRef(fSoftKey->GetObjectPtr()); + if( invert ) + { + invert->SetInsideStrength(fCompPB->GetFloat(kInsidePower) * 0.01f); + invert->SetOutsideStrength(fCompPB->GetFloat(kOutsidePower) * 0.01f); + } + } + return fSoftKey; + } + } + return nil; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +// Soft Light Region +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + +//Class that accesses the paramblock below. +class plLightRegionComponent : public plComponent +{ +public: + enum { + kSoftVolume + }; +public: + plLightRegionComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *errMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *errMsg); +}; + +// When one of our parameters that is a ref changes, send out the component ref +// changed message. Normally, messages from component refs are ignored since +// they pass along all the messages of the ref, which generates a lot of false +// converts. +class plLightRegionAccessor : public PBAccessor +{ +public: + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (id == plLightRegionComponent::kSoftVolume) + { + plLightRegionComponent *comp = (plLightRegionComponent*)owner; + comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED); + } + } +}; +plLightRegionAccessor gLightRegionAccessor; + + +static plSingleCompSelProc gLightRegionSingleSel(plLightRegionComponent::kSoftVolume, IDC_COMP_LIGHTREGION_CHOOSE_VOLUME, "Select soft region for light"); + + +//Max desc stuff necessary below. +CLASS_DESC(plLightRegionComponent, gLightRegionDesc, "Light Region", "LightRegion", COMP_TYPE_VOLUME, LIGHTREGION_CID) + + +ParamBlockDesc2 gLightRegionBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("LightRegion"), 0, &gLightRegionDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_LIGHTREGION, IDS_COMP_LIGHTREGION, 0, 0, &gLightRegionSingleSel, + + plLightRegionComponent::kSoftVolume, _T("Region"), TYPE_INODE, 0, 0, + p_accessor, &gLightRegionAccessor, + end, + end +); + +plLightRegionComponent::plLightRegionComponent() +{ + fClassDesc = &gLightRegionDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plLightRegionComponent::Convert(plMaxNode *node, plErrorMsg *errMsg) +{ + if( !fCompPB->GetINode(kSoftVolume) ) + return true; + + plSceneObject* sceneObj = node->GetSceneObject(); + if( !sceneObj ) + return true; + + plLightInfo* li = plLightInfo::ConvertNoRef(sceneObj->GetGenericInterface(plLightInfo::Index())); + if( !li ) + return true; + + plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSoftVolume)); + if( !softComp ) + return true; + + plKey softKey = softComp->GetSoftVolume(); + if( !softKey ) + return true; + + hsgResMgr::ResMgr()->AddViaNotify(softKey, TRACKED_NEW plGenRefMsg(li->GetKey(), plRefMsg::kOnCreate, 0, plLightInfo::kSoftVolume), plRefFlags::kActiveRef); + + return true; +} + +hsBool plLightRegionComponent::PreConvert(plMaxNode *pNode, plErrorMsg *errMsg) +{ + + return true; +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plLightRegionComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg) +{ + + return true; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +// Soft Visibility Region +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + +//Class that accesses the paramblock below. +class plVisRegionSingleSel : public plSingleCompSelProc +{ +public: + plVisRegionSingleSel(ParamID nodeID, int dlgItem, TCHAR *title) + : plSingleCompSelProc(nodeID, dlgItem, title) + { + } + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + if( pb->GetInt(plVisRegionComponent::kExcludes) ) + { + map->Enable(plVisRegionComponent::kDisableNormal, FALSE); + map->Enable(plVisRegionComponent::kAffectDraw, TRUE); + map->Enable(plVisRegionComponent::kAffectLight, TRUE); + map->Enable(plVisRegionComponent::kAffectOcc, TRUE); + } + else + { + map->Enable(plVisRegionComponent::kDisableNormal, TRUE); + if( pb->GetInt(plVisRegionComponent::kDisableNormal) ) + { + map->Enable(plVisRegionComponent::kAffectDraw, FALSE); + map->Enable(plVisRegionComponent::kAffectLight, FALSE); + map->Enable(plVisRegionComponent::kAffectOcc, FALSE); + } + else + { + map->Enable(plVisRegionComponent::kAffectDraw, TRUE); + map->Enable(plVisRegionComponent::kAffectLight, TRUE); + map->Enable(plVisRegionComponent::kAffectOcc, TRUE); + } + } + } + break; + case WM_COMMAND: + { + if( (LOWORD(wParam) == IDC_COMP_VISREGION_NOT) || (LOWORD(wParam) == IDC_COMP_VISREGION_DIS) ) + { + IParamBlock2 *pb = map->GetParamBlock(); + if( pb->GetInt(plVisRegionComponent::kExcludes) ) + { + map->Enable(plVisRegionComponent::kDisableNormal, FALSE); + map->Enable(plVisRegionComponent::kAffectDraw, TRUE); + map->Enable(plVisRegionComponent::kAffectLight, TRUE); + map->Enable(plVisRegionComponent::kAffectOcc, TRUE); + } + else + { + map->Enable(plVisRegionComponent::kDisableNormal, TRUE); + if( pb->GetInt(plVisRegionComponent::kDisableNormal) ) + { + map->Enable(plVisRegionComponent::kAffectDraw, FALSE); + map->Enable(plVisRegionComponent::kAffectLight, FALSE); + map->Enable(plVisRegionComponent::kAffectOcc, FALSE); + } + else + { + map->Enable(plVisRegionComponent::kAffectDraw, TRUE); + map->Enable(plVisRegionComponent::kAffectLight, TRUE); + map->Enable(plVisRegionComponent::kAffectOcc, TRUE); + } + } + return TRUE; + } + } + break; + } + return plSingleCompSelProc::DlgProc(t, map, hWnd, msg, wParam, lParam); + } +}; + +static plVisRegionSingleSel gVisRegionSingleSel(plVisRegionComponent::kSoftVolume, IDC_COMP_VISREGION_CHOOSE_VOLUME, "Select region for visibility"); + + +//Max desc stuff necessary below. +CLASS_DESC(plVisRegionComponent, gVisRegionDesc, "Visibility Region", "VisRegion", COMP_TYPE_VOLUME, VISREGION_CID) + + +ParamBlockDesc2 gVisRegionBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("VisRegion"), 0, &gVisRegionDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_VISREGION, IDS_COMP_VISREGION, 0, 0, &gVisRegionSingleSel, + + plVisRegionComponent::kSoftVolume, _T("Region"), TYPE_INODE, 0, 0, + p_accessor, nil, + end, + + plVisRegionComponent::kAffectDraw, _T("AffectDraw"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_VISREGION_DRAW, + end, + + plVisRegionComponent::kAffectLight, _T("AffectLight"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_VISREGION_LIGHT, + end, + + plVisRegionComponent::kAffectOcc, _T("AffectOcc"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_VISREGION_OCC, + end, + + plVisRegionComponent::kExcludes, _T("Excludes"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_VISREGION_NOT, + end, + + plVisRegionComponent::kDisableNormal, _T("DisableNormal"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_VISREGION_DIS, + end, + + end +); + +plVisRegionComponent::plVisRegionComponent() +{ + fClassDesc = &gVisRegionDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +void plVisRegionComponent::ICheckVisRegion(const plLocation& loc) +{ + if( !fVisReg ) + { + if( !fCompPB->GetINode(kSoftVolume) ) + return; + + plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSoftVolume)); + if( !softComp ) + return; + + plKey softKey = softComp->GetSoftVolume(); + if( !softKey ) + return; + + fVisReg = TRACKED_NEW plVisRegion; + plKey key = hsgResMgr::ResMgr()->NewKey(GetINode()->GetName(), fVisReg, loc); + + + hsBool excludes = fCompPB->GetInt(kExcludes); + hsBool disableNormal = excludes ? false : fCompPB->GetInt(kDisableNormal); + + fVisReg->SetProperty(plVisRegion::kIsNot, excludes); + fVisReg->SetProperty(plVisRegion::kReplaceNormal, true); + fVisReg->SetProperty(plVisRegion::kDisableNormal, disableNormal); + + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(fVisReg->GetKey(), plRefMsg::kOnCreate, 0, plVisRegion::kRefRegion); + hsgResMgr::ResMgr()->SendRef(softKey, refMsg, plRefFlags::kActiveRef); + } +} + +hsBool plVisRegionComponent::Convert(plMaxNode *node, plErrorMsg *errMsg) +{ + const char* dbgNodeName = node->GetName(); + plSceneObject* obj = node->GetSceneObject(); + if( !obj ) + return true; + + hsBool excludes = fCompPB->GetInt(kExcludes); + hsBool disableNormal = excludes ? false : fCompPB->GetInt(kDisableNormal); + hsBool affectDraw = disableNormal ? true : fCompPB->GetInt(kAffectDraw); + hsBool affectOcc = disableNormal ? true : fCompPB->GetInt(kAffectOcc); + hsBool affectLight = disableNormal ? true : fCompPB->GetInt(kAffectLight); + + const plDrawInterface* di = affectDraw ? obj->GetDrawInterface() : nil; + plOccluder* occ = affectOcc ? (plOccluder*)obj->GetGenericInterface(plOccluder::Index()) : nil; + plLightInfo* li = affectLight ? (plLightInfo*)obj->GetGenericInterface(plLightInfo::Index()) : nil; + if( !(disableNormal || di || occ || li) ) + return true; + + ICheckVisRegion(node->GetLocation()); + if( !fVisReg ) + return true; + + if( di ) + hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(di->GetKey(), plRefMsg::kOnCreate, 0, plDrawInterface::kRefVisRegion), plRefFlags::kActiveRef); + + if( occ ) + { + hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(occ->GetKey(), plRefMsg::kOnCreate, 0, plOccluder::kRefVisRegion), plRefFlags::kActiveRef); + } + + if( li ) + { + hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(li->GetKey(), plRefMsg::kOnCreate, 0, plLightInfo::kVisRegion), plRefFlags::kActiveRef); + } + + if( !(di || occ || li) ) + { + hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + } + + return true; +} + +hsBool plVisRegionComponent::PreConvert(plMaxNode *pNode, plErrorMsg *errMsg) +{ + fVisReg = nil; + + return true; +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plVisRegionComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg) +{ + + return true; +} + +void plVisRegionComponent::CollectRegions(plMaxNode* node, hsTArray& regions) +{ + int i; + for( i = 0; i < node->NumAttachedComponents(); i++ ) + { + plComponentBase* comp = node->GetAttachedComponent(i); + if( comp && comp->ClassID() == VISREGION_CID ) + { + plVisRegionComponent* regComp = (plVisRegionComponent*)comp; + if( regComp ) + { + regComp->ICheckVisRegion(node->GetLocation()); + if( regComp->fVisReg ) + regions.Append(regComp->fVisReg); + } + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +// Relevance Region +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + +//Class that accesses the paramblock below. +class plRelevanceRegionComponent : public plComponent +{ +public: + enum { + kSoftVolume, + kName, + }; +protected: + plRelevanceRegion* fRegion; + +public: + plRelevanceRegionComponent(); + void DeleteThis() { delete this; } + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *errMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *errMsg); +}; + + +static plSingleCompSelProc gRelevanceRegionSingleSel(plRelevanceRegionComponent::kSoftVolume, IDC_COMP_RELREGION_CHOOSE_VOLUME, "Select region"); + + +//Max desc stuff necessary below. +CLASS_DESC(plRelevanceRegionComponent, gRelevanceRegionDesc, "Relevance Region", "RelevanceRegion", COMP_TYPE_VOLUME, RELREGION_CID) + + +ParamBlockDesc2 gRelevanceRegionBk +( + plComponent::kBlkComp, _T("RelevanceRegion"), 0, &gRelevanceRegionDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_RELREGION, IDS_COMP_RELREGION, 0, 0, &gRelevanceRegionSingleSel, + + plRelevanceRegionComponent::kSoftVolume, _T("Region"), TYPE_INODE, 0, 0, + p_accessor, nil, + end, + + end +); + +plRelevanceRegionComponent::plRelevanceRegionComponent() +{ + fClassDesc = &gRelevanceRegionDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plRelevanceRegionComponent::Convert(plMaxNode *node, plErrorMsg *errMsg) +{ + const char* dbgNodeName = node->GetName(); + plSceneObject* obj = node->GetSceneObject(); + if( !obj ) + return true; + + if( !fRegion ) + { + if( !fCompPB->GetINode(kSoftVolume) ) + return true; + + plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSoftVolume)); + if( !softComp ) + return true; + + plKey softKey = softComp->GetSoftVolume(); + if( !softKey ) + return true; + + fRegion = TRACKED_NEW plRelevanceRegion; + plKey key = hsgResMgr::ResMgr()->NewKey(GetINode()->GetName(), fRegion, node->GetLocation()); + + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(fRegion->GetKey(), plRefMsg::kOnCreate, 0, 0); + hsgResMgr::ResMgr()->SendRef(softKey, refMsg, plRefFlags::kActiveRef); + } + + hsgResMgr::ResMgr()->AddViaNotify(fRegion->GetKey(), TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + return true; +} + +hsBool plRelevanceRegionComponent::PreConvert(plMaxNode *pNode, plErrorMsg *errMsg) +{ + fRegion = nil; + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +// Special Effects Visibility Set +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// + + + +//Max desc stuff necessary below. +CLASS_DESC(plEffVisSetComponent, gEffVisSetDesc, "Effect Vis Set", "EffVisSet", COMP_TYPE_VOLUME, EFFVISSET_CID) + + +ParamBlockDesc2 gEffVisSetBk +( // KLUDGE: not the defined block ID, but kept for backwards compat. + plComponent::kBlkComp, _T("EffVisSet"), 0, &gEffVisSetDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_EFFVISSET, IDS_COMP_EFFVISSET, 0, 0, NULL, + + plEffVisSetComponent::kHideNormal, _T("HideNormal"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_EFFVISSET_HIDENORMAL, + end, + + end +); + +plEffVisSetComponent::plEffVisSetComponent() +{ + fClassDesc = &gEffVisSetDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plEffVisSetComponent::Convert(plMaxNode *node, plErrorMsg *errMsg) +{ + const char* dbgNodeName = node->GetName(); + plSceneObject* obj = node->GetSceneObject(); + if( !obj ) + return true; + + const plDrawInterface* di = obj->GetDrawInterface(); + plOccluder* occ = (plOccluder*)obj->GetGenericInterface(plOccluder::Index()); + plLightInfo* li = (plLightInfo*)obj->GetGenericInterface(plLightInfo::Index()); + if( !(di || occ || li) ) + return true; + + if( !GetVisRegion(node) ) + return false; + + if( di ) + hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(di->GetKey(), plRefMsg::kOnCreate, 0, plDrawInterface::kRefVisRegion), plRefFlags::kActiveRef); + + if( occ ) + { + hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(occ->GetKey(), plRefMsg::kOnCreate, 0, plOccluder::kRefVisRegion), plRefFlags::kActiveRef); + } + + if( li ) + { + hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(li->GetKey(), plRefMsg::kOnCreate, 0, plLightInfo::kVisRegion), plRefFlags::kActiveRef); + } + + return true; +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plEffVisSetComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg) +{ + fVisReg = nil; + + return true; +} + +plVisRegion* plEffVisSetComponent::GetVisRegion(plMaxNode* node) +{ + if( !fVisReg ) + { + fVisReg = TRACKED_NEW plVisRegion; + plKey key = hsgResMgr::ResMgr()->NewKey(GetINode()->GetName(), fVisReg, node->GetLocation()); + + fVisReg->SetProperty(plVisRegion::kIsNot, false); + fVisReg->SetProperty(plVisRegion::kReplaceNormal, fCompPB->GetInt(kHideNormal)); + fVisReg->SetProperty(plVisRegion::kDisable, true); + } + + return fVisReg; +} + + +plEffVisSetComponent* plEffVisSetComponent::ConvertToEffVisSetComponent(plMaxNode* node) +{ + if( !node ) + return nil; + + plComponentBase *comp = node->ConvertToComponent(); + + // If this is an activator type component + if( comp ) + { + if( comp->ClassID() == EFFVISSET_CID ) + { + return (plEffVisSetComponent*)comp; + } + } + return nil; +} + +void plEffVisSetComponent::CollectRegions(plMaxNode* node, hsTArray& regions) +{ + int i; + for( i = 0; i < node->NumAttachedComponents(); i++ ) + { + plComponentBase* comp = node->GetAttachedComponent(i); + if( comp && comp->ClassID() == EFFVISSET_CID ) + { + plEffVisSetComponent* regComp = (plEffVisSetComponent*)comp; + if( regComp ) + { + if( regComp->fVisReg ) + regions.Append(regComp->fVisReg); + } + } + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSoftVolumeComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSoftVolumeComponent.h new file mode 100644 index 00000000..5658c97b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plSoftVolumeComponent.h @@ -0,0 +1,146 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plSoftVolumeComponent_h +#define _plSoftVolumeComponent_h + +#include "../pnKeyedObject/plKey.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/plUoid.h" + +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////// +const Class_ID SOFTVOLUME_BASE_CID(0x3d9e214e, 0xadb41c9); +const Class_ID SOFTVOLUME_CID(0x39612efc, 0x2a23293b); +const Class_ID SOFTVOLUME_UNION_CID(0x54666594, 0xf543eea); +const Class_ID SOFTVOLUME_ISECT_CID(0x28755c38, 0x5f63259); +const Class_ID SOFTVOLUME_NEGATE_CID(0xe681c08, 0x7102cec); + +const Class_ID LIGHTREGION_CID(0x5c17303, 0x315f2d51); +const Class_ID VISREGION_CID(0x5b144089, 0x5dc870a2); +const Class_ID RELREGION_CID(0x318e0a6d, 0x364a6a35); +const Class_ID EFFVISSET_CID(0x30141876, 0x3ffb3fda); + +//// plSoftVolBaseComponent Declaration ///////////////////////////////////////////////////////// + +class plSoftVolume; +class plVisRegion; + +class plSoftVolBaseComponent : public plComponent +{ +protected: + hsBool fValid; + plKey fSoftKey; + + void IAddSubVolume(plKey masterKey, plKey subKey); + plKey ISetVolumeKey(plSoftVolume* vol); + plKey IInvertVolume(plKey subKey); + virtual plKey ICreateSoftVolume() = 0; +public: + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg); + hsBool PreConvert(plMaxNode* pNode, plErrorMsg* errMsg) { return true; } + hsBool Convert(plMaxNode *node, plErrorMsg *errMsg) { return true; } + + plKey GetSoftVolume(); + + virtual hsBool DeInit(plMaxNode *node, plErrorMsg *pErrMsg); + + static plSoftVolBaseComponent* GetSoftComponent(INode* node); + static plSoftVolBaseComponent* GetSoftComponent(plComponentBase *comp); +}; + +//// plSingleCompSelProc Declaration //////////////////////////////////////////////////////////// +// Utilitiy class for any dialog that consists of a single button that is used to select +// a soft volume. +// Just declare like this: +// static plSingleCompSelProc gSoftVolNegateSingleSel( paramID, buttonCtrlID, promptString ); +// and use it as the dialog proc for your dialog's ParamMap. + +class plSingleCompSelProc : public ParamMap2UserDlgProc +{ +protected: + ParamID fNodeID; + int fDlgItem; + TCHAR fTitle[ 128 ]; + +public: + plSingleCompSelProc(ParamID nodeID, int dlgItem, TCHAR *title); + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void DeleteThis() {} +}; + +class plVisRegionComponent : public plComponent +{ +public: + enum { + kSoftVolume, + kAffectDraw, + kAffectLight, + kAffectOcc, + kExcludes, + kDisableNormal + }; +protected: + plVisRegion* fVisReg; + + void ICheckVisRegion(const plLocation& loc); +public: + plVisRegionComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg); + + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *errMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *errMsg); + + static void CollectRegions(plMaxNode* node, hsTArray& regions); +}; + +class plEffVisSetComponent : public plComponent +{ +public: + enum { + kHideNormal + }; +protected: + plVisRegion* fVisReg; +public: + plEffVisSetComponent(); + void DeleteThis() { delete this; } + + hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *errMsg); + + plVisRegion* GetVisRegion(plMaxNode* node); + + static plEffVisSetComponent* ConvertToEffVisSetComponent(plMaxNode* node); + static void CollectRegions(plMaxNode* node, hsTArray& regions); +}; + +#endif //_plSoftVolumeComponent_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plTemplateComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plTemplateComponent.cpp new file mode 100644 index 00000000..8d54109f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plTemplateComponent.cpp @@ -0,0 +1,217 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plComponent.h" +#include "plComponentReg.h" + +#include "hsTemplates.h" +#include "../MaxMain/plPluginResManager.h" +#include "../MaxMain/plMaxNode.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnKeyedObject/plKey.h" + +#include "plMiscComponents.h" + +#include "resource.h" + +void DummyCodeIncludeFuncTemplate() +{ +} + +static const char *GetPBString(IParamBlock2 *pb, ParamID id) +{ + const char *str = pb->GetStr(id, 0); + if (str && *str == '\0') + return nil; + return str; +} + +class plTemplateComponent : public plComponent +{ +protected: + const char* IGetAgeName(plMaxNode *node); + +public: + plTemplateComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +CLASS_DESC(plTemplateComponent, gTemplateDesc, "Template", "CloneTemplate", "Clone", Class_ID(0x6742590b, 0x14fd2135)) + +enum +{ + kTemplateName +}; + +ParamBlockDesc2 gTemplateBlk +( + plComponent::kBlkComp, _T("Template"), 0, &gTemplateDesc, P_AUTO_CONSTRUCT, plComponent::kRefComp, + + end +); + + +plTemplateComponent::plTemplateComponent() +{ + fClassDesc = &gTemplateDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +const char* plTemplateComponent::IGetAgeName(plMaxNode *node) +{ + UInt32 numComps = node->NumAttachedComponents(); + for (UInt32 i = 0; i < numComps; i++) + { + plComponentBase* comp = node->GetAttachedComponent(i); + if (comp->ClassID() == PAGEINFO_CID) + { + plPageInfoComponent* pageInfo = (plPageInfoComponent*)comp; + return pageInfo->GetAgeName(); + } + } + + return nil; +} + +#include "../MaxMain/plMaxNodeData.h" + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plTemplateComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + const char* ageName = IGetAgeName(node); + if (!ageName) + return false; + +#if 0 + const char *templateName = node->GetName(); + plKey roomKey = plPluginResManager::ResMgr()->NameToLoc(ageName, "District", "BuiltIn", (UInt32)-1); + + // Set this object and all its children to be in the special template age + node->SetRoomKey(roomKey); + for (int i = 0; i < node->NumberOfChildren(); i++) + { + plMaxNode* childNode = (plMaxNode*)node->GetChildNode(i); + childNode->SetRoomKey(roomKey); + } +#endif + + // We need a coordinate interface so we can move to the clone position + node->SetForceLocal(true); + + node->GetMaxNodeData()->SetItinerant(true); + + return true; +} + +hsBool plTemplateComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (node->GetSceneObject()) + node->GetSceneObject()->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile); + + for (int i = 0; i < node->NumberOfChildren(); i++) + { + plMaxNode* childNode = (plMaxNode*)node->GetChildNode(i); + + if (childNode->GetSceneObject()) + childNode->GetSceneObject()->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile); + } + + return true; +} + + +//////////////////////////////////////////////////////////////////////////////// + +#include "../plModifier/plCloneSpawnModifier.h" + +class plSpawnComponent : public plComponent +{ +public: + plSpawnComponent(); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +CLASS_DESC(plSpawnComponent, gSpawnDesc, "Instance", "CloneInst", "Clone", Class_ID(0x5702450d, 0x2c636131)) + +ParamBlockDesc2 gSpawnBlk +( + plComponent::kBlkComp, _T("Spawn"), 0, &gSpawnDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_TEMPLATE, IDS_COMP_CLONE_INST, 0, 0, NULL, + + kTemplateName, _T("name"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_NAME, + end, + + end +); + + +plSpawnComponent::plSpawnComponent() +{ + fClassDesc = &gSpawnDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plSpawnComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (!GetPBString(fCompPB, kTemplateName)) + { + pErrMsg->Set(true, "Clone Instance Component", "Clone Instance component on node %s can't convert because it doesn't have a name", node->GetName()); + pErrMsg->Set(false); + return false; + } + + // We need a coordinate interface to find the point to warp the clone to. + node->SetForceLocal(true); + + return true; +} + +hsBool plSpawnComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + const char *templateName = GetPBString(fCompPB, kTemplateName); + if (!templateName) + return false; + + plCloneSpawnModifier* mod = TRACKED_NEW plCloneSpawnModifier; + mod->SetExportTime(); + mod->SetTemplateName(templateName); + node->AddModifier(mod, IGetUniqueName(node)); + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plTypesComponents.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plTypesComponents.cpp new file mode 100644 index 00000000..8018ab31 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plTypesComponents.cpp @@ -0,0 +1,832 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../pnSceneObject/plSceneObject.h" + +#include "../pfCamera/plInterestingModifier.h" +#include "../plModifier/plSpawnModifier.h" +#include "plgDispatch.h" + + + +#include "hsResMgr.h" + +#include "../plScene/plSceneNode.h" +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/plConvert.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../MaxMain/plMaxNode.h" +#include "hsGeometry3.h" +#include "../plPhysical/plSimDefs.h" + +#include "../pnSceneObject/plCoordinateInterface.h" + +//Necessary Empty function. Otherwise Linker throws the Paramblock away as extraneous. +void DummyCodeIncludeFuncTypes() +{ +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// StartPoint Component +// +// + +//Class that accesses the paramblock below. +class plStartingPointComponent : public plComponent +{ +public: + plStartingPointComponent(); + void DeleteThis() { delete this; } + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + //hsBool IsValidNodeType(plMaxNode *pNode); +}; + +//Max desc stuff necessary. +CLASS_DESC(plStartingPointComponent, gStartPtDesc, "Starting Point", "StartPoint", COMP_TYPE_TYPE, Class_ID(0x2a127b68, 0xdc7367a)) + +//The MAX paramblock stuff below +ParamBlockDesc2 gStartPtBk +( + 1, _T(""), 0, &gStartPtDesc, P_AUTO_CONSTRUCT, 0, + + end +); + +plStartingPointComponent::plStartingPointComponent() +{ + fClassDesc = &gStartPtDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plStartingPointComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetForceLocal(true); + return true; +} +hsBool plStartingPointComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plSpawnModifier* pSpawn = TRACKED_NEW plSpawnModifier; + node->AddModifier(pSpawn, IGetUniqueName(node)); + return true; +} + +hsBool plStartingPointComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "plPickNode.h" + +class plVehicleModifier; + +class plVehicleComponent : public plComponent +{ +protected: + plVehicleModifier* fMod; + + bool IIsValid(); + +public: + plVehicleComponent(); + + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +CLASS_DESC(plVehicleComponent, gVehicleDesc, "(ex)Vehicle", "Vehicle", COMP_TYPE_MISC, Class_ID(0x75903e2, 0x50ac210b)) + +enum +{ + kVehicleChassis, + kVehicleWheelFR, + kVehicleWheelFL, + kVehicleWheelRR, + kVehicleWheelRL, + kVehicleHardpointFR, + kVehicleHardpointFL, + kVehicleHardpointRR, + kVehicleHardpointRL, + kVehicleDriveDet, +}; + +class plVehicleComponentProc : public ParamMap2UserDlgProc +{ +protected: + void IUpdateButtonText(HWND hWnd, IParamBlock2 *pb) + { + INode *node = pb->GetINode(kVehicleDriveDet); + SetWindowText(GetDlgItem(hWnd, IDC_DRIVE), node ? node->GetName() : "(none)"); + } + +public: + BOOL DlgProc(TimeValue t, IParamMap2* pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + IUpdateButtonText(hWnd, pm->GetParamBlock()); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + if (LOWORD(wParam) == IDC_DRIVE) + { + // Adding an activator. Set it and refresh the UI to show it in our list. + plPick::Activator(pm->GetParamBlock(), kVehicleDriveDet, true); + IUpdateButtonText(hWnd, pm->GetParamBlock()); + return TRUE; + } + } + break; + } + + return FALSE; + } + void DeleteThis() {} +}; +static plVehicleComponentProc gVehicleComponentProc; + +ParamBlockDesc2 gVehicleBlock +( + plComponent::kBlkComp, _T("vehicleComp"), 0, &gVehicleDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_VEHICLE, IDS_COMP_VEHICLE, 0, 0, &gVehicleComponentProc, + + kVehicleChassis, _T("chassis"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_CHASSIS, + end, + + kVehicleWheelFR, _T("wheelFR"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_FR_WHEEL, + end, + kVehicleWheelFL, _T("wheelFL"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_FL_WHEEL, + end, + kVehicleWheelRR, _T("wheelRR"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_RR_WHEEL, + end, + kVehicleWheelRL, _T("wheelRL"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_RL_WHEEL, + end, + + kVehicleHardpointFR, _T("hardpointFR"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_FR_HARDPOINT, + end, + kVehicleHardpointFL, _T("hardpointFL"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_FL_HARDPOINT, + end, + kVehicleHardpointRR, _T("hardpointRR"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_RR_HARDPOINT, + end, + kVehicleHardpointRL, _T("hardpointRL"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_RL_HARDPOINT, + end, + + kVehicleDriveDet, _T("driveDet"), TYPE_INODE, 0, 0, + end, + + end +); + +plVehicleComponent::plVehicleComponent() : fMod(nil) +{ + fClassDesc = &gVehicleDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +#include "../MaxMain/plPhysicalProps.h" +#include "../pnSceneObject/plSimulationInterface.h" +//#include "../plHavok1/plVehicleModifier.h" +//#include "../plHavok1/plPhysicsGroups.h" +/* +void SetupVehiclePhys(plMaxNode* physNode, plMaxNode* node, plErrorMsg* pErrMsg, bool chassis=false) +{ + plPhysicalProps* physProps = physNode->GetPhysicalProps(); + physProps->SetMass(1.0, node, pErrMsg); + physProps->SetRestitution(0.0, node, pErrMsg); + physProps->SetFriction(0.5, node, pErrMsg); + physProps->SetBoundsType(plSimDefs::kHullBounds, node, pErrMsg); + + if (chassis) + { + physProps->SetMemberGroup(plPhysicsGroups::kDynamicSimulated, node, pErrMsg); + physProps->SetBounceGroup( plPhysicsGroups::kStaticSimulated | + plPhysicsGroups::kDynamicSimulated | + plPhysicsGroups::kAnimated, + node, pErrMsg); + physProps->SetPinned(true, node, pErrMsg); + } + + physNode->SetMovable(true); + physNode->SetForceLocal(true); +} +*/ +bool plVehicleComponent::IIsValid() +{ + return + ( + fCompPB->GetINode(kVehicleChassis) && + fCompPB->GetINode(kVehicleWheelFR) && + fCompPB->GetINode(kVehicleWheelFL) && + fCompPB->GetINode(kVehicleWheelRR) && + fCompPB->GetINode(kVehicleWheelRL) && + fCompPB->GetINode(kVehicleHardpointFR) && + fCompPB->GetINode(kVehicleHardpointFL) && + fCompPB->GetINode(kVehicleHardpointRR) && + fCompPB->GetINode(kVehicleHardpointRL) && + fCompPB->GetINode(kVehicleDriveDet) + ); +} + +hsBool plVehicleComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return false; +#if 0 + if (!IIsValid()) + return false; + + plMaxNode* chassis = (plMaxNode*)fCompPB->GetINode(kVehicleChassis); + plMaxNode* wheelFR = (plMaxNode*)fCompPB->GetINode(kVehicleWheelFR); + plMaxNode* wheelFL = (plMaxNode*)fCompPB->GetINode(kVehicleWheelFL); + plMaxNode* wheelRR = (plMaxNode*)fCompPB->GetINode(kVehicleWheelRR); + plMaxNode* wheelRL = (plMaxNode*)fCompPB->GetINode(kVehicleWheelRL); + + plMaxNode* hardpointFR = (plMaxNode*)fCompPB->GetINode(kVehicleHardpointFR); + plMaxNode* hardpointFL = (plMaxNode*)fCompPB->GetINode(kVehicleHardpointFL); + plMaxNode* hardpointRR = (plMaxNode*)fCompPB->GetINode(kVehicleHardpointRR); + plMaxNode* hardpointRL = (plMaxNode*)fCompPB->GetINode(kVehicleHardpointRL); + + chassis->SetDrawable(false); + + hardpointFR->SetForceLocal(true); + hardpointFL->SetForceLocal(true); + hardpointRR->SetForceLocal(true); + hardpointRL->SetForceLocal(true); + + SetupVehiclePhys(chassis, node, pErrMsg, true); + SetupVehiclePhys(wheelFR, node, pErrMsg); + SetupVehiclePhys(wheelFL, node, pErrMsg); + SetupVehiclePhys(wheelRR, node, pErrMsg); + SetupVehiclePhys(wheelRL, node, pErrMsg); + + return true; +#endif +} + +hsBool plVehicleComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return false; +#if 0 + if (!IIsValid()) + return false; + + fMod = TRACKED_NEW plVehicleModifier; + plKey modKey = pNode->AddModifier(fMod, IGetUniqueName(pNode)); + + plMaxNode* detectorNode = (plMaxNode*)fCompPB->GetINode(kVehicleDriveDet); + plComponentBase* comp = detectorNode ? detectorNode->ConvertToComponent() : nil; + if (comp) + comp->AddReceiverKey(modKey); + + return true; +#endif +} + +#if 0 + +void GetSuspensionProps(plMaxNode* hardPoint, plMaxNode* wheel, hsPoint3& chassisPoint, + plVehicleModifier::WheelProps& props) +{ + props.wheelKey = wheel->GetKey(); + + hsPoint3 hardPos = hardPoint->GetLocalToWorld44().GetTranslate(); + hsPoint3 wheelPos = wheel->GetLocalToWorld44().GetTranslate(); + + // Get position of the hardpoint relative to the chassis + props.pos = hardPos - chassisPoint; + + // Get a vector from the hardpoint to the wheel + hsVector3 dir(wheelPos - hardPos); + + // Get the length of the suspension (hardpoint to wheel) + props.len = hsPoint3(dir).Magnitude(); + + // Get the direction of the suspension + dir.Normalize(); + props.dir = dir; +} + +#endif + +hsBool plVehicleComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return false; +#if 0 + if (!IIsValid()) + return false; + + plMaxNode* chassis = (plMaxNode*)fCompPB->GetINode(kVehicleChassis); + plMaxNode* wheelFR = (plMaxNode*)fCompPB->GetINode(kVehicleWheelFR); + plMaxNode* wheelFL = (plMaxNode*)fCompPB->GetINode(kVehicleWheelFL); + plMaxNode* wheelRR = (plMaxNode*)fCompPB->GetINode(kVehicleWheelRR); + plMaxNode* wheelRL = (plMaxNode*)fCompPB->GetINode(kVehicleWheelRL); + + plMaxNode* hardpointFR = (plMaxNode*)fCompPB->GetINode(kVehicleHardpointFR); + plMaxNode* hardpointFL = (plMaxNode*)fCompPB->GetINode(kVehicleHardpointFL); + plMaxNode* hardpointRR = (plMaxNode*)fCompPB->GetINode(kVehicleHardpointRR); + plMaxNode* hardpointRL = (plMaxNode*)fCompPB->GetINode(kVehicleHardpointRL); + + hsPoint3 chassisPos = chassis->GetLocalToWorld44().GetTranslate(); + + plVehicleModifier::WheelProps wheelPropsFR, wheelPropsFL, wheelPropsRR, wheelPropsRL; + + GetSuspensionProps(hardpointFR, wheelFR, chassisPos, wheelPropsFR); + GetSuspensionProps(hardpointFL, wheelFL, chassisPos, wheelPropsFL); + GetSuspensionProps(hardpointRR, wheelRR, chassisPos, wheelPropsRR); + GetSuspensionProps(hardpointRL, wheelRL, chassisPos, wheelPropsRL); + + fMod->Setup(chassis->GetKey(), + wheelPropsFR, + wheelPropsFL, + wheelPropsRR, + wheelPropsRL); + + return true; +#endif +} +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Maintainers Marker Component +// +// + +#include "../plModifier/plMaintainersMarkerModifier.h" + +enum +{ + kCalibrated, +}; +//Class that accesses the paramblock below. +class plMaintainersMarkerComponent : public plComponent +{ +public: + plMaintainersMarkerComponent(); + void DeleteThis() { delete this; } + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +//Max desc stuff necessary. +CLASS_DESC(plMaintainersMarkerComponent, gMaintainersDesc, "Maintainers Marker", "MaintainersMarker", COMP_TYPE_TYPE, Class_ID(0x7d7f1f72, 0x405355f5)) + +//The MAX paramblock stuff below +ParamBlockDesc2 gMaintainersBk +( + + 1, _T("maintainersMarker"), 0, &gMaintainersDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + IDD_COMP_MAINTAINERS_MARKER, IDS_COMP_MAINTAINERS_MARKER, 0, 0, NULL, + + kCalibrated, _T("Calibrated"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_RADIO_BROKEN, IDC_RADIO_REPAIRED, IDC_RADIO_CALIBRATED, + p_vals, plMaintainersMarkerModifier::kBroken, plMaintainersMarkerModifier::kRepaired, plMaintainersMarkerModifier::kCalibrated, + end, + end +); + +plMaintainersMarkerComponent::plMaintainersMarkerComponent() +{ + fClassDesc = &gMaintainersDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plMaintainersMarkerComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetForceLocal(true); + return true; +} +hsBool plMaintainersMarkerComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plMaintainersMarkerModifier* pSpawn = TRACKED_NEW plMaintainersMarkerModifier; + pSpawn->SetCalibrated(fCompPB->GetInt(kCalibrated)); + node->AddModifier(pSpawn, IGetUniqueName(node)); + return true; +} + +hsBool plMaintainersMarkerComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Game Marker Component +// + +#include "../plModifier/plGameMarkerModifier.h" +#include "plNotetrackAnim.h" +#include "plPickMaterialMap.h" +#include "../MaxMain/plMtlCollector.h" +#include "plResponderMtl.h" +#include "plResponderGetComp.h" +#include "plAnimComponent.h" +#include "plAudioComponents.h" + +class plGameMarkerComponent : public plComponent +{ +protected: + plKey IGetMtlAnimKey(int paramID, plMaxNode* node); + plKey IGetAnimKey(int nodeID, int compID); + +public: + plGameMarkerComponent(); + hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + hsBool PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); +}; + +CLASS_DESC(plGameMarkerComponent, gGameMarkerDesc, "Game Marker", "GameMarker", COMP_TYPE_TYPE, Class_ID(0x4a15029a, 0x350f7258)) + +enum +{ + kMarkerPhys, + kMarkerMtl, + kMarkerGreenAnim, + kMarkerRedAnim, + kMarkerOpenAnim, + kMarkerEditAnim_DEAD, + kMarkerBounceNode, + kMarkerBounceComp, + kMarkerMtlNode, + kMarkerSndPlace, + kMarkerSndHit, +}; + +class plGameMarkerComponentProc : public ParamMap2UserDlgProc +{ +protected: + void IComboChanged(HWND hWnd, IParamBlock2 *pb, int id) + { + char buf[256]; + GetDlgItemText(hWnd, id, buf, sizeof(buf)); + int paramID = 0; + switch (id) + { + case IDC_ANIM_RED_COMBO: paramID = kMarkerRedAnim; break; + case IDC_ANIM_GREEN_COMBO: paramID = kMarkerGreenAnim; break; + case IDC_ANIM_OPEN_COMBO: paramID = kMarkerOpenAnim; break; + } + + pb->SetValue(paramID, 0, buf); + } + + void ILoadCombo(HWND hWnd, int ctrlID, int paramID, IParamBlock2* pb, plNotetrackAnim& anim) + { + const char* savedName = pb->GetStr(paramID); + HWND hCombo = GetDlgItem(hWnd, ctrlID); + ComboBox_ResetContent(hCombo); + + while (const char* animName = anim.GetNextAnimName()) + { + int sel = ComboBox_AddString(hCombo, animName); + if (hsStrEQ(animName, savedName)) + ComboBox_SetCurSel(hCombo, sel); + } + } + + void IInit(HWND hWnd, IParamBlock2* pb) + { + Mtl* mtl = pb->GetMtl(kMarkerMtl); + + if (mtl) + { + SetDlgItemText(hWnd, IDC_MTL_BUTTON, mtl->GetName()); + + plNotetrackAnim anim(mtl, nil); + ILoadCombo(hWnd, IDC_ANIM_RED_COMBO, kMarkerRedAnim, pb, anim); + ILoadCombo(hWnd, IDC_ANIM_GREEN_COMBO, kMarkerGreenAnim, pb, anim); + ILoadCombo(hWnd, IDC_ANIM_OPEN_COMBO, kMarkerOpenAnim, pb, anim); + } + + if (pb->GetINode(kMarkerMtlNode)) + SetDlgItemText(hWnd, IDC_MTL_NODE_BUTTON, pb->GetINode(kMarkerMtlNode)->GetName()); + else + SetDlgItemText(hWnd, IDC_MTL_NODE_BUTTON, "(none)"); + + if (pb->GetINode(kMarkerBounceNode)) + SetDlgItemText(hWnd, IDC_BOUNCE_BUTTON, pb->GetINode(kMarkerBounceNode)->GetName()); + else + SetDlgItemText(hWnd, IDC_BOUNCE_BUTTON, "(none)"); + + if (pb->GetINode(kMarkerSndPlace)) + SetDlgItemText(hWnd, IDC_PLACE_BUTTON, pb->GetINode(kMarkerSndPlace)->GetName()); + else + SetDlgItemText(hWnd, IDC_PLACE_BUTTON, "(none)"); + + if (pb->GetINode(kMarkerSndHit)) + SetDlgItemText(hWnd, IDC_HIT_BUTTON, pb->GetINode(kMarkerSndHit)->GetName()); + else + SetDlgItemText(hWnd, IDC_HIT_BUTTON, "(none)"); + } + +public: + BOOL DlgProc(TimeValue t, IParamMap2* pm, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + IInit(hWnd, pm->GetParamBlock()); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_MTL_BUTTON) + { + Mtl* pickedMtl = plPickMaterialMap::PickMaterial(plMtlCollector::kUsedOnly | + plMtlCollector::kPlasmaOnly); + if (pickedMtl) + { + pm->GetParamBlock()->SetValue(kMarkerMtl, 0, pickedMtl); + IInit(hWnd, pm->GetParamBlock()); + } + + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_MTL_NODE_BUTTON) + { + if (plPick::MtlNodes(pm->GetParamBlock(), kMarkerMtlNode, pm->GetParamBlock()->GetMtl(kMarkerMtl))) + IInit(hWnd, pm->GetParamBlock()); + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_BOUNCE_BUTTON) + { + plResponderGetComp::ClassIDs ids; + ids.push_back(ANIM_COMP_CID); + if (plResponderGetComp::Instance().GetComp(pm->GetParamBlock(), kMarkerBounceNode, kMarkerBounceComp, &ids)) + IInit(hWnd, pm->GetParamBlock()); + + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDC_PLACE_BUTTON || LOWORD(wParam) == IDC_HIT_BUTTON)) + { + std::vector cids; + cids.push_back(SOUND_3D_COMPONENT_ID); + + int paramID = (LOWORD(wParam) == IDC_PLACE_BUTTON) ? kMarkerSndPlace : kMarkerSndHit; + + if (plPick::Node(pm->GetParamBlock(), paramID, &cids, true, false)) + IInit(hWnd, pm->GetParamBlock()); + return TRUE; + } + else if (HIWORD(wParam) == CBN_SELCHANGE) + { + IComboChanged(hWnd, pm->GetParamBlock(), LOWORD(wParam)); + return TRUE; + } + break; + } + + return FALSE; + } + void DeleteThis() {} +}; +static plGameMarkerComponentProc gGameMarkerComponentProc; + +ParamBlockDesc2 gGameMarkerBlk +( + plComponent::kBlkComp, _T("gameMarker"), 0, &gGameMarkerDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + IDD_COMP_MARKER, IDS_COMP_MARKER, 0, 0, &gGameMarkerComponentProc, + + kMarkerPhys, _T("physical"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_MARKER_PHYS, + end, + + kMarkerMtl, _T("mtl"), TYPE_MTL, 0, 0, + end, + + kMarkerGreenAnim, _T("green"), TYPE_STRING, 0, 0, + end, + kMarkerRedAnim, _T("red"), TYPE_STRING, 0, 0, + end, + kMarkerOpenAnim, _T("open"), TYPE_STRING, 0, 0, + end, + + kMarkerBounceNode, _T("bounceNode"), TYPE_INODE, 0, 0, + end, + kMarkerBounceComp, _T("bounceComp"), TYPE_INODE, 0, 0, + end, + + kMarkerMtlNode, _T("mtlNode"), TYPE_INODE, 0, 0, + end, + + kMarkerSndPlace, _T("sndPlace"), TYPE_INODE, 0, 0, + end, + + kMarkerSndHit, _T("sndHit"), TYPE_INODE, 0, 0, + end, + + end +); + +plGameMarkerComponent::plGameMarkerComponent() +{ + fClassDesc = &gGameMarkerDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plGameMarkerComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plMaxNode* proxy = (plMaxNode*)fCompPB->GetINode(kMarkerPhys); + proxy->SetCanConvert(false); + node->SetForceLocal(true); + node->SetItinerant(true); + + plPhysicalProps* physProps = node->GetPhysicalProps(); + physProps->SetBoundsType(plSimDefs::kSphereBounds, node, pErrMsg); + physProps->SetPinned(true, node, pErrMsg); + // only if movable will it have mass (then it will keep track of movements in PhysX) + if ( node->IsMovable() || node->IsTMAnimated() ) + physProps->SetMass(1.0, node, pErrMsg); + physProps->SetGroup(plSimDefs::kGroupDetector, node, pErrMsg); + physProps->SetReportGroup(1<SetProxyNode(proxy, node, pErrMsg); + + return true; +} + +hsBool plGameMarkerComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} + +plKey plGameMarkerComponent::IGetMtlAnimKey(int paramID, plMaxNode* node) +{ + Mtl* mtl = fCompPB->GetMtl(kMarkerMtl); + plMaxNode* mtlNode = (plMaxNode*)fCompPB->GetINode(kMarkerMtlNode); + hsTArray keys; + const char* anim = fCompPB->GetStr(paramID); + GetMatAnimModKey(mtl, mtlNode, anim, keys); + hsAssert(keys.Count() == 1, "Wrong number of keys"); + return keys[0]; +} + +plKey plGameMarkerComponent::IGetAnimKey(int nodeID, int compID) +{ + plMaxNode* animComp = (plMaxNode*)fCompPB->GetINode(compID); + plMaxNode* animNode = (plMaxNode*)fCompPB->GetINode(nodeID); + if (animComp && animNode) + { + plAnimComponent* comp = (plAnimComponent*)animComp->ConvertToComponent(); + return comp->GetModKey(animNode); + } + + return nil; +} + +hsBool plGameMarkerComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plGameMarkerModifier* markerMod = TRACKED_NEW plGameMarkerModifier; + + plKey greenKey = IGetMtlAnimKey(kMarkerGreenAnim, node); + plKey redKey = IGetMtlAnimKey(kMarkerRedAnim, node); + plKey openKey = IGetMtlAnimKey(kMarkerOpenAnim, node); + + plKey bounceKey = IGetAnimKey(kMarkerBounceNode, kMarkerBounceComp); + + plMaxNode* sndPlaceComp = (plMaxNode*)fCompPB->GetINode(kMarkerSndPlace); + int sndPlaceIdx = plAudioComp::GetSoundModIdx(sndPlaceComp->ConvertToComponent(), node); + + plMaxNode* sndHitComp = (plMaxNode*)fCompPB->GetINode(kMarkerSndHit); + int sndHitIdx = plAudioComp::GetSoundModIdx(sndHitComp->ConvertToComponent(), node); + + markerMod->ExportInit(greenKey, redKey, openKey, bounceKey, sndPlaceIdx, sndHitIdx); + + node->AddModifier(markerMod, IGetUniqueName(node)); + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Camera Component +// +// + + +//Class that accesses the paramblock below. + +class plCameraComponent : public plComponent +{ +public: + plCameraComponent(); + void DeleteThis() { delete this; } + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool IsValidNodeType(plMaxNode *pNode); + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *pErrMsg); + +}; + +//Max desc stuff necessary. +OBSOLETE_CLASS_DESC(plCameraComponent, gCameraDesc, "Camera", "Camera", COMP_TYPE_TYPE, Class_ID(0x75926577, 0x1cbe49b6)) + +// +// Block not necessary, kept for backwards compat. +// +enum +{ + kCamera, + kCameraV2 +}; + +//Max paramblock2 stuff below. +ParamBlockDesc2 gCameraBk +( + 1, _T("camera"), 0, &gCameraDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_CAMERA, IDS_COMP_CAMERAS, 0, 0, NULL, + + // params + kCamera, _T("Animation"), TYPE_INT, 0, 0, + p_default, 0, + p_ui, TYPE_RADIO, 2, /*IDC_RADIO_DEFAULT,*/ IDC_RADIO_FIXEDCAM, IDC_RADIO_FIXEDPANCAM, + end, + //kCamera, _T("CamType"), TYPE_INT, 0, 0, + /// p_ui, TYPE_RADIO, 2, IDC_RADIO_FIXEDCAM, IDC_RADIO_FIXEDPANCAM, + // end, + + + + end +); + +plCameraComponent::plCameraComponent() +{ + fClassDesc = &gCameraDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plCameraComponent::SetupProperties(plMaxNode* pNode, plErrorMsg *pErrMsg) +{ + return true; +} + + +hsBool plCameraComponent::PreConvert(plMaxNode *pNode, plErrorMsg *pErrMsg) +{ + return true; +} + +hsBool plCameraComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + return true; +} + + +hsBool plCameraComponent::IsValidNodeType(plMaxNode *pNode) +{ + return false; +} + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plVolumeGadgetComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plVolumeGadgetComponent.cpp new file mode 100644 index 00000000..fbf76084 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plVolumeGadgetComponent.cpp @@ -0,0 +1,482 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plVolumeGadgetComponent.h" +#include "resource.h" +#include "plComponentReg.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plKey.h" + +#include "../plPhysical/plCollisionDetector.h" // MM +#include "../plModifier/plLogicModifier.h" +#include "../../NucleusLib/pnModifier/plConditionalObject.h" +#include "../plPhysical/plPickingDetector.h" +#include "../pfConditional/plActivatorConditionalObject.h" +#include "../pfConditional/plFacingConditionalObject.h" +#include "../pfConditional/plObjectInBoxConditionalObject.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNotifyMsg.h" +#include "../pnMessage/plCursorChangeMsg.h" + +#include "hsResMgr.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxConvert/plConvert.h" + +#include "plResponderComponent.h" + +// Physics Dependencies below +#include "../MaxConvert/hsConverterUtils.h" //Conversion Dependencies +#include "plPhysicalComponents.h" +#include "../pnMessage/plIntRefMsg.h" +#include "plComponentProcBase.h" + +#include "../MaxMain/plPhysicalProps.h" + +#include "../plPhysical/plSimDefs.h" + +void DummyCodeIncludeFuncVolumeGadget() {} + + + +CLASS_DESC(plVolumeGadgetComponent, gVolumeGadgetDesc, "Region Sensor", "RegionSensor", COMP_TYPE_DETECTOR, VOLUMEGADGET_CID) + +enum +{ + kVolumeGadgetInside_DEAD, // removed + kVolumeGadgetEnter, + kVolumeGadgetExit, + kVolumeUnEnter, + kVolumeUnExit, + kVolumeOneShot, + kVolumeEnterType, + kVolumeExitType, + kVolumeExitNum, + kVolumeEnterNum, + kUseVolumeNode, + kVolumeNode, + kVolumeBoundsType, + kVolumeReportChoice_DEAD, + kVolumeReportBoolTab_DEAD, + kVolumeEnabled, + kVolumeReportGroups_DEAD, + kVolumeDirectional, + kVolumeDegrees, + kVolumeTriggerOnFacing, + kVolumeWalkingForward, + kVolumeReportOn, + kSkipServerArbitration, +}; + +enum +{ + kVolumeMain, + kVolumeReport, +}; + +enum +{ + kEnterTypeEach, + kEnterTypeCount, +}; + +enum +{ + kExitTypeEach, + kExitTypeFirst, + kExitTypeCount, +}; + +class VolumeDlgProc : public ParamMap2UserDlgProc +{ +protected: + // Because there is no p_disable_ctrls + void IEnable(HWND hWnd, bool value) + { + #define Enable_Item(id, val) EnableWindow(GetDlgItem(hWnd, id), val) + + Enable_Item(IDC_COMP_PHYSGADGET_ENTERBOX, !value); + } + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_TRIGGER_ON_FACING_CHECK) + IEnable(hWnd, Button_GetCheck((HWND)lParam) == BST_CHECKED); + else if (msg == WM_INITDIALOG) + IEnable(hWnd, (map->GetParamBlock()->GetInt(kVolumeTriggerOnFacing) != 0)); + return FALSE; + } + + void DeleteThis() {} +}; +static VolumeDlgProc gVolumeDlgProc; + +ParamBlockDesc2 gVolumeGadgetBlock +( + plComponent::kBlkComp, _T("RegionGadgetComp"), 0, &gVolumeGadgetDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + 1, + kVolumeMain, IDD_COMP_DETECTOR_REGION, IDS_COMP_DETECTOR_REGION, 0, 0, &gVolumeDlgProc, + + kVolumeGadgetEnter, _T("Enter"), TYPE_BOOL, 0, 0, + p_ui, kVolumeMain, TYPE_SINGLECHEKBOX, IDC_COMP_PHYSGADGET_ENTERBOX, + p_enable_ctrls, 2, kVolumeEnterType, kVolumeEnterNum, + end, + + kVolumeGadgetExit, _T("Exit"), TYPE_BOOL, 0, 0, + p_ui, kVolumeMain, TYPE_SINGLECHEKBOX, IDC_COMP_PHYSGADGET_EXITBOX, + p_enable_ctrls, 2, kVolumeExitType, kVolumeExitNum, + end, + + kVolumeTriggerOnFacing, _T("triggerOnFacing"), TYPE_BOOL, 0, 0, + p_ui, kVolumeMain, TYPE_SINGLECHEKBOX, IDC_TRIGGER_ON_FACING_CHECK, + p_enable_ctrls, 2, kVolumeDegrees, kVolumeWalkingForward, + end, + + kVolumeOneShot, _T("oneshot"), TYPE_BOOL, 0, 0, + p_ui, kVolumeMain, TYPE_SINGLECHEKBOX, IDC_ONESHOT, + end, + kVolumeEnterType, _T("enterType"), TYPE_INT, 0, 0, + p_ui, kVolumeMain, TYPE_RADIO, 2, IDC_RADIO_EACHENTRY, IDC_RADIO_ENTRYCOUNT, + p_vals, kEnterTypeEach, kEnterTypeCount, + end, + + kVolumeExitType, _T("exitType"), TYPE_INT, 0, 0, + p_ui, kVolumeMain, TYPE_RADIO, 3, IDC_RADIO_EACHEXIT, IDC_RADIO_FIRSTEXIT, IDC_RADIO_EXITCOUNT, + p_vals, kExitTypeEach, kExitTypeFirst, kExitTypeCount, + end, + + kVolumeExitNum, _T("exitNum"), TYPE_INT, P_ANIMATABLE, 0, + p_range, 0, 100, + p_default, 1, + p_ui, kVolumeMain, TYPE_SPINNER, EDITTYPE_INT, + IDC_CAMERACMD_OFFSETX3, IDC_CAMERACMD_SPIN_OFFSETX3, SPIN_AUTOSCALE, + end, + + kVolumeEnterNum, _T("nterNum"), TYPE_INT, P_ANIMATABLE, 0, + p_range, 0, 100, + p_default, 1, + p_ui, kVolumeMain, TYPE_SPINNER, EDITTYPE_INT, + IDC_CAMERACMD_OFFSETX2, IDC_CAMERACMD_SPIN_OFFSETX2, SPIN_AUTOSCALE, + end, + + + + kUseVolumeNode, _T("UseVolumeNode"), TYPE_BOOL, 0, 0, + p_ui, kVolumeMain, TYPE_SINGLECHEKBOX, IDC_COMP_PHYS_CUSTOMCHK, + end, + + kVolumeNode, _T("UserBoundChoice"), TYPE_INODE, 0, 0, + p_ui, kVolumeMain, TYPE_PICKNODEBUTTON, IDC_COMP_PHYS_PICKSTATE_DETECTOR, + p_sclassID, GEOMOBJECT_CLASS_ID, + p_prompt, IDS_COMP_PHYS_CHOSEN_SIMP, + //p_accessor, &gPhysCoreAccessor, + end, + + kVolumeBoundsType, _T("BoundingConditions"), TYPE_INT, 0, 0, + p_ui, kVolumeMain, TYPE_RADIO, 4, IDC_RADIO_BSPHERE2, IDC_RADIO_BBOX, IDC_RADIO_BHULL, IDC_RADIO_PICKSTATE, + p_vals, plSimDefs::kSphereBounds, plSimDefs::kBoxBounds, plSimDefs::kHullBounds, plSimDefs::kProxyBounds, + p_default, plSimDefs::kHullBounds, + end, + + kVolumeReportGroups_DEAD, _T("reportGroups"), TYPE_INT, 0,0, + end, + + kVolumeEnabled, _T("enabled"), TYPE_BOOL, 0, 0, + p_ui, kVolumeMain, TYPE_SINGLECHEKBOX, IDC_ENABLED, + p_default, TRUE, + end, + + kVolumeDegrees, _T("degrees"), TYPE_INT, 0, 0, + p_range, 1, 180, + p_default, 45, + p_ui, kVolumeMain, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_COMP_CLICK_DEG, IDC_COMP_CLICK_DEGSPIN, SPIN_AUTOSCALE, + end, + + kVolumeWalkingForward, _T("walkingForward"), TYPE_BOOL, 0, 0, + p_ui, kVolumeMain, TYPE_SINGLECHEKBOX, IDC_WALKING_FORWARD_CHECK, + end, + + kVolumeReportOn, _T("reportOn"), TYPE_INT, 0, 0, + p_ui, kVolumeMain, TYPE_RADIO, 3, IDC_RADIO_REPORT_AVATAR, IDC_RADIO_REPORT_DYN, IDC_RADIO_REPORT_BOTH, + p_vals, 1<MakeAutoParamBlocks(this); +} + +plKey plVolumeGadgetComponent::GetLogicOutKey(plMaxNode* node) +{ + LogicKeys::const_iterator it = fLogicModOutKeys.find(node); + if (it != fLogicModOutKeys.end()) + return it->second; + + + return nil; +} + +void plVolumeGadgetComponent::CollectNonDrawables(INodeTab& nonDrawables) +{ + if(fCompPB->GetInt(kUseVolumeNode)) + { + INode* boundsNode = fCompPB->GetINode(kVolumeNode); + if( boundsNode ) + nonDrawables.Append(1, &boundsNode); + } + + AddTargetsToList(nonDrawables); +} + +// Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plVolumeGadgetComponent::SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg) +{ + + fLogicModKeys.clear(); + fLogicModOutKeys.clear(); + + node->SetForceLocal(true); + node->SetDrawable(false); + + plPhysicalProps *physProps = node->GetPhysicalProps(); + + physProps->SetBoundsType(fCompPB->GetInt(kVolumeBoundsType), node, pErrMsg); + if(fCompPB->GetInt(kUseVolumeNode)) + { + plMaxNode *boundNode = (plMaxNode*)fCompPB->GetINode(kVolumeNode); + if (boundNode) + if(boundNode->CanConvert()) + physProps->SetProxyNode(boundNode, node, pErrMsg); + else + { + pErrMsg->Set(true, "Volume Sensor Warning", "The Volume Sensor %s has a Proxy Surface %s that was Ignored.\nThe Sensors geometry will be used instead.", node->GetName(), boundNode->GetName()).Show(); + pErrMsg->Set(false); + physProps->SetProxyNode(nil, node, pErrMsg); + } + } + + // only if movable will it have mass (then it will keep track of movements in PhysX) + if ( node->IsMovable() || node->IsTMAnimatedRecur() ) + physProps->SetMass(1.0, node, pErrMsg); +// physProps->SetAllowLOS(true, node, pErrMsg); + physProps->SetGroup(plSimDefs::kGroupDetector, node, pErrMsg); + + UInt32 reportOn = fCompPB->GetInt(kVolumeReportOn); + physProps->SetReportGroup(reportOn, node, pErrMsg); + + return true; +} + +hsBool plVolumeGadgetComponent::PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + plLocation loc = node->GetLocation(); + plSceneObject *obj = node->GetSceneObject(); + + + // Create and register the VolumeGadget's logic component + if(fCompPB->GetInt(kVolumeGadgetEnter) || fCompPB->GetInt(kVolumeTriggerOnFacing)) + { + plLogicModifier *logic = TRACKED_NEW plLogicModifier; + char tmpName[256]; + sprintf(tmpName, "%s_Enter", IGetUniqueName(node)); + plKey logicKey = hsgResMgr::ResMgr()->NewKey(tmpName, logic, node->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(logicKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + fLogicModKeys[node] = logicKey; + if (fCompPB->GetInt(kVolumeOneShot)) + logic->SetFlag(plLogicModBase::kOneShot); + logic->SetFlag(plLogicModBase::kMultiTrigger); + } + + + if(fCompPB->GetInt(kVolumeGadgetExit)) + { + plLogicModifier *logic = TRACKED_NEW plLogicModifier; + char tmpName[256]; + sprintf(tmpName, "%s_Exit", IGetUniqueName(node)); + plKey logicKey = hsgResMgr::ResMgr()->NewKey(tmpName, logic, node->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(logicKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + fLogicModOutKeys[node] = logicKey; + if (fCompPB->GetInt(kVolumeOneShot)) + logic->SetFlag(plLogicModBase::kOneShot); + logic->SetFlag(plLogicModBase::kMultiTrigger); + } + + return true; +} + +void plVolumeGadgetComponent::ICreateConditions(plMaxNode* node, plErrorMsg* errMsg, bool enter) +{ + bool disabled = (fCompPB->GetInt(kVolumeEnabled) == 0); + + plLocation loc = node->GetLocation(); + plSceneObject *obj = node->GetSceneObject(); + char tmpName[256]; + + plKey logicKey; + if (enter) + logicKey = fLogicModKeys[node]; + else + logicKey = fLogicModOutKeys[node]; + + plLogicModifier *logic = plLogicModifier::ConvertNoRef(logicKey->GetObjectPtr()); + + hsTArray receivers; + IGetReceivers(node, receivers); + for (int i = 0; i < receivers.Count(); i++) + logic->AddNotifyReceiver(receivers[i]); + + + // Create the detector + plDetectorModifier* detector = nil; + if (enter && fCompPB->GetInt(kVolumeTriggerOnFacing)) + { + plObjectInVolumeAndFacingDetector* newDetector = TRACKED_NEW plObjectInVolumeAndFacingDetector; + + int deg = fCompPB->GetInt(kVolumeDegrees); + if (deg > 180) + deg = 180; + newDetector->SetFacingTolerance(deg); + + bool walkingForward = (fCompPB->GetInt(kVolumeWalkingForward) != 0); + newDetector->SetNeedWalkingForward(walkingForward); + + detector = newDetector; + } + else + detector = TRACKED_NEW plObjectInVolumeDetector; + + const char* prefix = "Exit"; + if (enter) + prefix = "Enter"; + + // Register the detector + sprintf(tmpName, "%s_%s", IGetUniqueName(node), prefix); + plKey detectorKey = hsgResMgr::ResMgr()->NewKey(tmpName, detector, loc); + hsgResMgr::ResMgr()->AddViaNotify(detectorKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + plVolumeSensorConditionalObject* boxCond=nil; + if((fCompPB->GetInt(kSkipServerArbitration)==0)) + {//we want server arbitration + boxCond = TRACKED_NEW plVolumeSensorConditionalObject; + } + else + { + boxCond = TRACKED_NEW plVolumeSensorConditionalObjectNoArbitration; + } + sprintf(tmpName, "%s_%s", IGetUniqueName(node), prefix); + plKey boxKey = hsgResMgr::ResMgr()->NewKey(tmpName, boxCond, loc); + + if (enter) + boxCond->SetType(plVolumeSensorConditionalObject::kTypeEnter); + else + boxCond->SetType(plVolumeSensorConditionalObject::kTypeExit); + + if (enter && !fCompPB->GetInt(kVolumeTriggerOnFacing)) + { + int trigType = fCompPB->GetInt(kVolumeEnterType); + switch (trigType) + { + case kEnterTypeEach: + break; + + case kEnterTypeCount: + { + int count = fCompPB->GetInt(kVolumeEnterNum); + boxCond->SetTrigNum(count); + break; + } + } + } + else if (!enter) + { + int trigType = fCompPB->GetInt(kVolumeExitType); + switch (trigType) + { + case kExitTypeEach: + break; + + case kExitTypeFirst: + boxCond->SetFirst(true); + break; + + case kExitTypeCount: + { + int count = fCompPB->GetInt(kVolumeExitNum); + boxCond->SetTrigNum(count); + break; + } + } + } + + // link everything up: + detector->AddLogicObj(boxKey); // This MUST be first!! + detector->AddLogicObj(logicKey); // send messages to this logic component + logic->AddCondition(boxCond); + logic->SetDisabled(disabled); + + // If this is for the SceneViewer, set the local only flag since the read function will never be called + if (plConvert::Instance().IsForSceneViewer()) + logic->SetLocalOnly(true); +} + +hsBool plVolumeGadgetComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) +{ + if (fCompPB->GetInt(kVolumeTriggerOnFacing)) + ICreateConditions(node, pErrMsg, true); + else if (fCompPB->GetInt(kVolumeGadgetEnter)) + ICreateConditions(node, pErrMsg, true); + + if (fCompPB->GetInt(kVolumeGadgetExit)) + ICreateConditions(node, pErrMsg, false); + + return true; +} + +hsBool plVolumeGadgetComponent::DeInit( plMaxNode *node, plErrorMsg *pErrMsg ) +{ + fLogicModOutKeys.clear(); + return plActivatorBaseComponent::DeInit( node, pErrMsg ); +} + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plVolumeGadgetComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plVolumeGadgetComponent.h new file mode 100644 index 00000000..5ece3123 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plVolumeGadgetComponent.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plVolumeGadgetComponent_inc +#define plVolumeGadgetComponent_inc + +#include "plActivatorBaseComponent.h" +#include "../pnKeyedObject/plKey.h" + +#define VOLUMEGADGET_CID Class_ID(0x1f77b56, 0x209a09d8) + +class plVolumeGadgetComponent : public plActivatorBaseComponent +{ +protected: + plActivatorBaseComponent::LogicKeys fLogicModOutKeys; + + void ICreateConditions(plMaxNode* node, plErrorMsg* errMsg, bool enter); + +public: + plVolumeGadgetComponent(); + + hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg); + hsBool PreConvert(plMaxNode *node, plErrorMsg* pErrMsg); + hsBool DeInit(plMaxNode *node, plErrorMsg* pErrMsg); + + virtual bool HasLogicOut() { return true; } + + const LogicKeys& GetLogicOutKeys() { return fLogicModOutKeys; } + virtual plKey GetLogicOutKey(plMaxNode* node); + + virtual void CollectNonDrawables(INodeTab& nonDrawables); +}; + +#endif // plVolumeGadgetComponent_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plWaterComponent.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plWaterComponent.cpp new file mode 100644 index 00000000..b41bb55e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plWaterComponent.cpp @@ -0,0 +1,1379 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "meshdlib.h" +#include "dummy.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../MaxMain/plMaxNode.h" + +#include "plWaterComponent.h" +#include "plSoftVolumeComponent.h" + +#include "hsTypes.h" +#include "plTweak.h" + +#include "../plDrawable/plWaveSetBase.h" +#include "../plDrawable/plWaveSet7.h" +#include "../plDrawable/plFixedWaterState7.h" + +#include "../plPipeline/plDynamicEnvMap.h" + +#include "../MaxMain/plPluginResManager.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../pnMessage/plObjRefMsg.h" + +#include "../plScene/plVisRegion.h" + +static const float kPercentToFrac(1.e-2f); +static const float kDegreeToRad(hsScalarPI/180.f); + + +// Preliminary setup bookkeeping +void DummyCodeIncludeFuncWater() +{ +} + +CLASS_DESC(plWaterComponent, gWaterCompDesc, "Large Water", "Water", COMP_TYPE_WATER, WATER_COMP_CID) + +ParamBlockDesc2 gWaterBk +( + plComponent::kBlkComp, _T("Water"), 0, &gWaterCompDesc, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plComponent::kRefComp, + + plWaterComponent::kNumRollups, + plWaterComponent::kRef, IDD_COMP_W_REFOBJECT, IDS_COMP_W_REFOBJECT, 0, 0, 0, +// plWaterComponent::kBasicWater, IDD_COMP_W_BASICWATER, IDS_COMP_W_BASICWATER, 0, 0, 0, + plWaterComponent::kGeoWater, IDD_COMP_W_GEOWATER, IDS_COMP_W_GEOWATER, 0, 0, 0, + plWaterComponent::kTexWater, IDD_COMP_W_TEXWATER, IDS_COMP_W_TEXWATER, 0, 0, 0, + plWaterComponent::kAdvWater, IDD_COMP_W_ADVWATER, IDS_COMP_W_ADVWATER, 0, 0, 0, + plWaterComponent::kEnvMap, IDD_COMP_W_ENVMAP, IDS_COMP_W_ENVMAP, 0, 0, 0, + plWaterComponent::kVtxHelp, IDD_COMP_W_VTXHELP, IDS_COMP_W_VTXHELP, 0, 0, 0, + plWaterComponent::kBasicShore, IDD_COMP_W_BASICSHORE, IDS_COMP_W_BASICSHORE, 0, 0, 0, + plWaterComponent::kAdvShore, IDD_COMP_W_ADVSHORE, IDS_COMP_W_ADVSHORE, 0, 0, 0, + + plWaterComponent::kRefObject, _T("RefObject"), TYPE_INODE, 0, 0, + p_ui, plWaterComponent::kRef, TYPE_PICKNODEBUTTON, IDC_COMP_W_REFOBJECT, + p_prompt, IDS_COMP_CHOOSE_OBJECT, + end, + + // WATER BASIC + plWaterComponent::kWindSpeed, _T("WindSpeed"), TYPE_FLOAT, 0, 0, +// p_default, 30.0, +// p_range, -1.0, 50.0, +// p_ui, plWaterComponent::kBasicWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_W_WINDSPEED, IDC_COMP_W_WINDSPEED_SPIN, 1.0, + end, + + plWaterComponent::kWaterTint, _T("WaterTint"), TYPE_RGBA, 0, 0, +// p_default, Color(0.1, 0.2, 0.2), +// p_ui, plWaterComponent::kBasicWater, TYPE_COLORSWATCH, IDC_COMP_W_WATERTINT, + end, + + plWaterComponent::kWaterOpac, _T("WaterOpac"), TYPE_FLOAT, 0, 0, +// p_default, 100.0, +// p_range, 0.0, 100.0, +// p_ui, plWaterComponent::kBasicWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_W_WATEROPAC, IDC_COMP_W_WATEROPAC_SPIN, 1.0, + end, + + plWaterComponent::kSpecularTint, _T("SpecularTint"), TYPE_RGBA, 0, 0, + p_default, Color(1.0, 1.0, 1.0), + p_ui, plWaterComponent::kGeoWater, TYPE_COLORSWATCH, IDC_COMP_W_SPECULARTINT, + end, + + plWaterComponent::kRippleScale, _T("RippleScale"), TYPE_FLOAT, 0, 0, + p_default, 25.0, + p_range, 5.0, 1000.0, + p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_TEX_RIPPLESCALE, IDC_COMP_W_TEX_RIPPLESCALE_SPIN, 1.0, + end, + + plWaterComponent::kDispersion, _T("Dispersion"), TYPE_FLOAT, 0, 0, +// p_default, 0.0, +// p_range, 0.0, 100.0, +// p_ui, plWaterComponent::kBasicWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_W_DISPERSION, IDC_COMP_W_DISPERSION_SPIN, 1.0, + end, + + // WATER ADVANCED + plWaterComponent::kDepthOpac, _T("DepthOpac"), TYPE_FLOAT, 0, 0, + p_default, 3.0, + p_range, 0.5, 20.0, + p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_DEPTHOPAC, IDC_COMP_W_DEPTHOPAC_SPIN, 1.0, + end, + + plWaterComponent::kDepthRefl, _T("DepthRefl"), TYPE_FLOAT, 0, 0, + p_default, 3.0, + p_range, 0.5, 20.0, + p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_DEPTHREFL, IDC_COMP_W_DEPTHREFL_SPIN, 1.0, + end, + + plWaterComponent::kDepthWave, _T("DepthWave"), TYPE_FLOAT, 0, 0, + p_default, 4.0, + p_range, 0.5, 20.0, + p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_DEPTHWAVE, IDC_COMP_W_DEPTHWAVE_SPIN, 1.0, + end, + + plWaterComponent::kZeroOpac, _T("ZeroOpac"), TYPE_FLOAT, 0, 0, + p_default, -1.0, + p_range, -10.0, 10.0, + p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_W_ZEROOPAC, IDC_COMP_W_ZEROOPAC_SPIN, 1.0, + end, + + plWaterComponent::kZeroRefl, _T("ZeroRefl"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_W_ZEROREFL, IDC_COMP_W_ZEROREFL_SPIN, 1.0, + end, + + plWaterComponent::kZeroWave, _T("ZeroWave"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, -10.0, 10.0, + p_ui, plWaterComponent::kAdvWater, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_COMP_W_ZEROWAVE, IDC_COMP_W_ZEROWAVE_SPIN, 1.0, + end, + + // SHORE BASIC + plWaterComponent::kShoreTint, _T("ShoreTint"), TYPE_RGBA, 0, 0, + p_default, Color(0.2, 0.4, 0.4), + p_ui, plWaterComponent::kBasicShore, TYPE_COLORSWATCH, IDC_COMP_W_SHORETINT, + end, + + plWaterComponent::kShoreOpac, _T("ShoreOpac"), TYPE_FLOAT, 0, 0, + p_default, 40.0, + p_range, 0.0, 100.0, + p_ui, plWaterComponent::kBasicShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_SHOREOPAC, IDC_COMP_W_SHOREOPAC_SPIN, 1.0, + end, + + plWaterComponent::kWispiness, _T("Wispiness"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 200.0, + p_ui, plWaterComponent::kBasicShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_WISPINESS, IDC_COMP_W_WISPINESS_SPIN, 1.0, + end, + + // SHORE ADVANCED + plWaterComponent::kPeriod, _T("Period"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 50.0, 200.0, + p_ui, plWaterComponent::kAdvShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_PERIOD, IDC_COMP_W_PERIOD_SPIN, 1.0, + end, + + plWaterComponent::kFinger, _T("Finger"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 50.0, 300.0, + p_ui, plWaterComponent::kAdvShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_FINGER, IDC_COMP_W_FINGER_SPIN, 1.0, + end, + + plWaterComponent::kEnvObject, _T("EnvObject"), TYPE_INODE, 0, 0, + p_ui, plWaterComponent::kEnvMap, TYPE_PICKNODEBUTTON, IDC_COMP_W_ENVOBJECT, + p_prompt, IDS_COMP_CHOOSE_OBJECT, + end, + + plWaterComponent::kEnvSize, _T("EnvSize"), TYPE_INT, 0, 0, + p_default, 256, + p_range, 32, 512, + p_ui, plWaterComponent::kEnvMap, TYPE_SPINNER, EDITTYPE_INT, + IDC_COMP_W_ENVSIZE, IDC_COMP_W_ENVSIZE_SPIN, 1.f, + end, + + plWaterComponent::kEnvRadius, _T("EnvRadius"), TYPE_FLOAT, 0, 0, + p_default, 500.0, + p_range, 5.0, 10000.0, + p_ui, plWaterComponent::kEnvMap, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_ENVRADIUS, IDC_COMP_W_ENVRADIUS_SPIN, 1.0, + end, + + plWaterComponent::kEdgeOpac, _T("EdgeOpac"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 0.0, 100.0, + p_ui, plWaterComponent::kAdvShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_EDGEOPAC, IDC_COMP_W_EDGEOPAC_SPIN, 1.0, + end, + + plWaterComponent::kEdgeRadius, _T("EdgeRadius"), TYPE_FLOAT, 0, 0, + p_default, 100.0, + p_range, 50.0, 300.0, + p_ui, plWaterComponent::kAdvShore, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_EDGERADIUS, IDC_COMP_W_EDGERADIUS_SPIN, 1.0, + end, + + + plWaterComponent::kEnvRefresh, _T("EnvRefresh"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 3600.0, + p_ui, plWaterComponent::kEnvMap, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_ENVREFRESH, IDC_COMP_W_ENVREFRESH_SPIN, 10.0, + end, + + plWaterComponent::kGeoMinLen, _T("GeoMinLen"), TYPE_FLOAT, 0, 0, + p_default, 4.0, + p_range, 0.1, 50.0, + p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_GEO_MINLEN, IDC_COMP_W_GEO_MINLEN_SPIN, 1.0, + end, + + plWaterComponent::kGeoMaxLen, _T("GeoMaxLen"), TYPE_FLOAT, 0, 0, + p_default, 8.0, + p_range, 0.1, 50.0, + p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_GEO_MAXLEN, IDC_COMP_W_GEO_MAXLEN_SPIN, 1.0, + end, + + plWaterComponent::kGeoAmpOverLen, _T("GeoAngleDev"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.0, 100.0, + p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_GEO_AMPOVERLEN, IDC_COMP_W_GEO_AMPOVERLEN_SPIN, 1.0, + end, + + plWaterComponent::kGeoAngleDev, _T("GeoAngleDev"), TYPE_FLOAT, 0, 0, + p_default, 20.0, + p_range, 0.0, 180.0, + p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_GEO_ANGLEDEV, IDC_COMP_W_GEO_ANGLEDEV_SPIN, 10.0, + end, + + plWaterComponent::kGeoChop, _T("GeoChop"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 500.0, + p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_GEO_CHOP, IDC_COMP_W_GEO_CHOP_SPIN, 1.0, + end, + + plWaterComponent::kTexMinLen, _T("TexMinLen"), TYPE_FLOAT, 0, 0, + p_default, 0.1, + p_range, 0.01, 4.0, + p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_TEX_MINLEN, IDC_COMP_W_TEX_MINLEN_SPIN, 1.0, + end, + + plWaterComponent::kTexMaxLen, _T("TexMaxLen"), TYPE_FLOAT, 0, 0, + p_default, 4.0, + p_range, 0.1, 50.0, + p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_TEX_MAXLEN, IDC_COMP_W_TEX_MAXLEN_SPIN, 1.0, + end, + + plWaterComponent::kTexAmpOverLen, _T("TexAngleDev"), TYPE_FLOAT, 0, 0, + p_default, 10.0, + p_range, 0.0, 100.0, + p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_TEX_AMPOVERLEN, IDC_COMP_W_TEX_AMPOVERLEN_SPIN, 1.0, + end, + + plWaterComponent::kTexAngleDev, _T("TexAngleDev"), TYPE_FLOAT, 0, 0, + p_default, 20.0, + p_range, 0.0, 180.0, + p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_TEX_ANGLEDEV, IDC_COMP_W_TEX_ANGLEDEV_SPIN, 1.0, + end, + + plWaterComponent::kNoise, _T("Noise"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 300.0, + p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_TEX_NOISE, IDC_COMP_W_TEX_NOISE_SPIN, 1.0, + end, + + plWaterComponent::kTexChop, _T("TexChop"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 500.0, + p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_TEX_CHOP, IDC_COMP_W_TEX_CHOP_SPIN, 1.0, + end, + + plWaterComponent::kSpecStart, _T("SpecStart"), TYPE_FLOAT, 0, 0, + p_default, 50.0, + p_range, 0.0, 1000.0, + p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_TEX_SPECSTART, IDC_COMP_W_TEX_SPECSTART_SPIN, 10.0, + end, + + plWaterComponent::kSpecEnd, _T("SpecEnd"), TYPE_FLOAT, 0, 0, + p_default, 1000.0, + p_range, 0.0, 10000.0, + p_ui, plWaterComponent::kTexWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_TEX_SPECEND, IDC_COMP_W_TEX_SPECEND_SPIN, 10.0, + end, + + plWaterComponent::kSpecularMute, _T("SpecularMute"), TYPE_FLOAT, 0, 0, + p_default, 30.0, + p_range, 0.0, 100.0, + p_ui, plWaterComponent::kGeoWater, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_W_GEO_SPECMUTE, IDC_COMP_W_GEO_SPECMUTE_SPIN, 1.0, + end, + + end +); + + +class plWaterCompPostLoadCallback : public PostLoadCallback +{ +public: + plWaterComponent* fWaterComp; + + plWaterCompPostLoadCallback(plWaterComponent* wc) : fWaterComp(wc) {} + + void proc(ILoad *iload) + { + fWaterComp->CheckForObsoleteParams(); + + delete this; + } +}; + +IOResult plWaterComponent::Load(ILoad* iLoad) +{ + iLoad->RegisterPostLoadCallback(new plWaterCompPostLoadCallback(this)); + + return plComponent::Load(iLoad); +} + +void plWaterComponent::CheckForObsoleteParams() +{ + if( (fCompPB->GetFloat(kDispersion) >= 0) + ||(fCompPB->GetFloat(kWindSpeed) >= 0) ) + { + // Okay, these are old. Need to set some default values based + // on the old obsolete ones. Basically, we need to go from: + // + // kDispersion => + // GeoAngleDev + // TexAngleDev + // + // kWindSpeed + // GeoMinLen + // GeoMaxLen + // GeoAmpOverLen = 0.1 + // + // TexMinLen + // TexMaxLen + // TexAmpOverLen + // + // Noise + // + // kEnvRadius + // SpecStart = kEnvRadius / 2.f + // SpecEnd = kEnvRadius * 2.f + + // Okay, here we go. + hsScalar dispersion = fCompPB->GetFloat(kDispersion) / 100.f; + plConst(hsScalar) kMinAng(5.f); + plConst(hsScalar) kMaxAng(180.f); + + hsScalar angleDev = kMinAng + dispersion * (kMaxAng - kMinAng); + fCompPB->SetValue(kGeoAngleDev, TimeValue(0), angleDev); + fCompPB->SetValue(kTexAngleDev, TimeValue(0), angleDev); + + hsScalar windSpeed = fCompPB->GetFloat(kWindSpeed); + const hsScalar kGravConst(32.f); // ft/s^2 + hsScalar waveLen = windSpeed * windSpeed / kGravConst; + waveLen /= 2.f; + if( waveLen < 1.f ) + waveLen = 1.f; + fCompPB->SetValue(kGeoMinLen, TimeValue(0), waveLen/2.f); + fCompPB->SetValue(kGeoMaxLen, TimeValue(0), waveLen*2.f); + fCompPB->SetValue(kGeoAmpOverLen, TimeValue(0), 10.f); + + hsScalar rippleScale = fCompPB->GetFloat(kRippleScale); + fCompPB->SetValue(kTexMinLen, TimeValue(0), 4.f / 256.f * rippleScale); + fCompPB->SetValue(kTexMaxLen, TimeValue(0), 32.f / 256.f * rippleScale); + hsScalar amp = 0.01f; + hsScalar specMute = 0.5f; + if( windSpeed < 15.f ) + { + hsScalar p = windSpeed / 15.f; + amp += p * (0.1f - 0.01f); + + specMute += (1-p)* 0.5f; + } + fCompPB->SetValue(kTexAmpOverLen, TimeValue(0), amp*100.f); + fCompPB->SetValue(kSpecularMute, TimeValue(0), specMute*100.f); + + fCompPB->SetValue(kNoise, TimeValue(0), 50.f); + + hsScalar envRad = fCompPB->GetFloat(kEnvRadius); + hsScalar specStart = envRad / 2.f; + hsScalar specEnd = envRad * 2.f; + fCompPB->SetValue(kSpecStart, TimeValue(0), specStart); + fCompPB->SetValue(kSpecEnd, TimeValue(0), specEnd); + + // Set them negative so we don't keep doing this. + fCompPB->SetValue(kDispersion, TimeValue(0), -1.f); + fCompPB->SetValue(kWindSpeed, TimeValue(0), -1.f); + } +} + + +// Component implementation +plWaterComponent::plWaterComponent() +: fWaveSet(nil) +{ + fClassDesc = &gWaterCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plWaterComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetRunTimeLight(true); + node->SetForceMaterialCopy(true); + node->SetForceShadow(false); + node->SetNoShadow(true); + node->SetSmoothAll(true); + node->SetForceSortable(true); + node->SetNoSpanSort(true); + node->SetNoPreShade(true); + + // Turn on calculation of edge lengths XXX + node->SetCalcEdgeLens(true); + + // Make a note that we're vertex shaded (at least ripplecomponent needs to know).XXX + node->SetVS(true); + + // Turn off the convexity test. We want to be sorted.XXX + node->SetConcave(true); + + node->SetReverseSort(true); + + node->SetWaterHeight(IGetWaterHeight()); + + return true; +} + +hsBool plWaterComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fWaveSet ) + IMakeWaveSet(node, pErrMsg); + + // Do it again in case some idiot is trying to override.XXX + + pErrMsg->Set(!node->HasLoadMask(), node->GetName(), "PS water has no representation component").CheckAndAsk(); + pErrMsg->Set(false); + + return true; +} + +hsBool plWaterComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fWaveSet ) + return true; + + plObjRefMsg* refMsg = TRACKED_NEW plObjRefMsg(node->GetKey(), plRefMsg::kOnRequest, -1, plObjRefMsg::kModifier); + hsgResMgr::ResMgr()->AddViaNotify(fWaveSet->GetKey(), refMsg, plRefFlags::kActiveRef); + + return true; +} + +hsBool plWaterComponent::DeInit(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( fWaveSet ) + fWaveSet->GetKey()->UnRefObject(); + fWaveSet = nil; + + return true; +} + +hsBool plWaterComponent::IReadRefObject(plMaxNodeBase* node, plFixedWaterState7& ws) +{ + INode* ref = fCompPB->GetINode(kRefObject); + if( !ref ) + { + ref = node; + } + if( !ref ) + return false; + + Matrix3 xfm = ref->GetNodeTM(TimeValue(0)); + ws.fWaterHeight = xfm.GetTrans().z; + + Point3 y = xfm.GetRow(1); + hsVector3 dir(-y.x, -y.y, 0); + dir.Normalize(); + ws.fWindDir = dir; + + return true; +} + +hsBool plWaterComponent::IReadEnvObject(plMaxNode* node, plErrorMsg* pErrMsg, plFixedWaterState7& ws) +{ + INode* ref = fCompPB->GetINode(kEnvObject); + if( !ref ) + { + ref = node; + } + plDynamicEnvMap* env = plEnvMapComponent::GetEnvMap((plMaxNode*)ref); + if( !env ) + { + UInt32 size = fCompPB->GetInt(kEnvSize); + UInt32 i; + for( i = 9; i > 5; i-- ) + { + if( (1UL << i) <= size ) + break; + } + size = UInt32(1 << i); + + env = TRACKED_NEW plDynamicEnvMap(size, size, 32); + hsgResMgr::ResMgr()->NewKey(ref->GetName(), env, node->GetLocation(), node->GetLoadMask()); + + Point3 pos = ref->GetNodeTM(TimeValue(0)).GetTrans(); + env->SetPosition(hsPoint3(pos.x, pos.y, pos.z)); + env->SetYon(10000.f); + env->SetRefreshRate(fCompPB->GetFloat(kEnvRefresh)); + } + if( !env ) + return false; + + ws.fEnvCenter = env->GetPosition(); + ws.fEnvRefresh = env->GetRefreshRate(); + + fWaveSet->SetEnvSize(env->GetWidth()); + + + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(fWaveSet->GetKey(), plRefMsg::kOnCreate, -1, plWaveSet7::kRefEnvMap); + hsgResMgr::ResMgr()->SendRef(env->GetKey(), refMsg, plRefFlags::kActiveRef); + + ws.fEnvRadius = fCompPB->GetFloat(kEnvRadius); + + + return true; +} + +hsBool plWaterComponent::IGetRefObject(plMaxNode* node) +{ + plMaxNode* ref = (plMaxNode*)fCompPB->GetINode(kRefObject); + if( (ref != node) // We have an exterior reference node + && ref->CanConvert() // it's being exported + && ref->IsTMAnimated() ) + { + plSceneObject* refObj = ref->GetSceneObject(); + + fWaveSet->SetRefObject(refObj); + + return true; + } + + return false; +} + +hsBool plWaterComponent::IMakeWaveSet(plMaxNode* node, plErrorMsg* pErrMsg) +{ + // Go ahead and create the WaveSet modifier. There will be just + // one created by this component, everyone has to share. + fWaveSet = TRACKED_NEW plWaveSet7; + hsgResMgr::ResMgr()->NewKey( IGetUniqueName(node), fWaveSet, node->GetLocation(), node->GetLoadMask()); + + // Set up the parameters + plFixedWaterState7 ws; + + // Things we get off the reference (plane) object + // First we look to see if it's an animated runtime object we need to keep track of. + // Either way, we just grab the static info we need off of it, for init or forever. + IGetRefObject(node); + IReadRefObject(node, ws); + + // Things we get from our paramblock + plFixedWaterState7::WaveState& geoState = ws.fGeoState; + geoState.fMaxLength = fCompPB->GetFloat(kGeoMaxLen); + geoState.fMinLength = fCompPB->GetFloat(kGeoMinLen); + geoState.fAmpOverLen = fCompPB->GetFloat(kGeoAmpOverLen) * kPercentToFrac; + geoState.fChop = fCompPB->GetFloat(kGeoChop) * kPercentToFrac; + geoState.fAngleDev = fCompPB->GetFloat(kGeoAngleDev) * kDegreeToRad; + + plFixedWaterState7::WaveState& texState = ws.fTexState; + texState.fMaxLength = fCompPB->GetFloat(kTexMaxLen); + texState.fMinLength = fCompPB->GetFloat(kTexMinLen); + texState.fAmpOverLen = fCompPB->GetFloat(kTexAmpOverLen) * kPercentToFrac; + texState.fChop = fCompPB->GetFloat(kTexChop) * kPercentToFrac; + texState.fAngleDev = fCompPB->GetFloat(kTexAngleDev) * kDegreeToRad; + + hsVector3 specVec; + specVec[ws.kNoise] = fCompPB->GetFloat(kNoise) * kPercentToFrac; + specVec[ws.kSpecStart] = fCompPB->GetFloat(kSpecStart); + specVec[ws.kSpecEnd] = fCompPB->GetFloat(kSpecEnd); + ws.fSpecVec = specVec; + + ws.fWispiness = fCompPB->GetFloat(kWispiness) * kPercentToFrac; + ws.fPeriod = fCompPB->GetFloat(kPeriod) * kPercentToFrac; + ws.fRippleScale = fCompPB->GetFloat(kRippleScale); + ws.fFingerLength = fCompPB->GetFloat(kFinger) * kPercentToFrac; + + ws.fDepthFalloff = hsVector3(fCompPB->GetFloat(kDepthOpac), fCompPB->GetFloat(kDepthRefl), fCompPB->GetFloat(kDepthWave)); + ws. fWaterOffset = hsVector3(-fCompPB->GetFloat(kZeroOpac), -fCompPB->GetFloat(kZeroRefl), -fCompPB->GetFloat(kZeroWave)); + ws.fMaxAtten = hsVector3(1.f, 1.f, 1.f); + ws.fMinAtten = hsVector3(0, 0, 0); + + IReadEnvObject(node, pErrMsg, ws); + + // Some colors + ws.fWaterTint = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f); + + Color specTint = fCompPB->GetColor(kSpecularTint); + float specMute = fCompPB->GetFloat(kSpecularMute) * kPercentToFrac; + ws.fSpecularTint = hsColorRGBA().Set(specTint.r, specTint.g, specTint.b, specMute); + + ws.fMaxColor = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f); // Always white/opaque? + Color shoreTint = fCompPB->GetColor(kShoreTint); + float shoreOpac = fCompPB->GetFloat(kShoreOpac) * kPercentToFrac; + ws.fMinColor = hsColorRGBA().Set(shoreTint.r, shoreTint.g, shoreTint.b, shoreOpac); + + // Not really used. + ws.fShoreTint = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f); + + ws.fEdgeOpac = fCompPB->GetFloat(kEdgeOpac) * kPercentToFrac; + ws.fEdgeRadius = fCompPB->GetFloat(kEdgeRadius) * kPercentToFrac; + + fWaveSet->SetState(ws, 0.f); + + // Add a ref to the waveset. + fWaveSet->GetKey()->RefObject(); + + return true; +} + +hsScalar plWaterComponent::IGetWaterHeight() +{ + plMaxNodeBase* node = nil; + + int i; + for( i = 0; i < NumTargets(); i++ ) + { + node = GetTarget(i); + if( node ) + break; + } + + plFixedWaterState7 ws; + IReadRefObject(node, ws); + + return ws.fWaterHeight; +} + +hsScalar plWaterComponent::GetWaterHeight(INode* node) +{ + if( !node ) + return 0.f; + + plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); + if( !comp ) + return 0.f; + + if( comp->ClassID() != WATER_COMP_CID ) + return 0.f; + + plWaterComponent* water = (plWaterComponent*)comp; + return water->IGetWaterHeight(); +} + + +plWaveSetBase* plWaterComponent::GetWaveSet(INode* node) +{ + if( !node ) + return nil; + + plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent(); + if( !comp ) + return nil; + + if( comp->ClassID() != WATER_COMP_CID ) + return nil; + + plWaterComponent* water = (plWaterComponent*)comp; + return water->IGetWaveSet(); +} + +plWaveSetBase* plWaterComponent::GetWaveSetFromNode(plMaxNode* node) +{ + if( !node ) + return nil; + + int n = node->NumAttachedComponents(); + int i; + for( i = 0; i < n; i++ ) + { + plComponentBase* comp = node->GetAttachedComponent(i); + if( comp && (comp->ClassID() == WATER_COMP_CID) ) + { + plWaterComponent* water = (plWaterComponent*)comp; + return water->IGetWaveSet(); + } + } + return nil; +} + +static void ISetWaterDependencies(plMaxNode* node, INode* waterNode) +{ + if( !waterNode ) + return; + + plComponentBase *comp = ((plMaxNodeBase*)waterNode)->ConvertToComponent(); + if( !comp ) + return; + + if( comp->ClassID() != WATER_COMP_CID ) + return; + + INodeTab nodeList; + comp->AddTargetsToList(nodeList); + int i; + for( i = 0; i < nodeList.Count(); i++ ) + { + if( nodeList[i] ) + node->AddRenderDependency((plMaxNodeBase*)nodeList[i]); + } +} + + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +class plShoreCompSelProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void DeleteThis() { } +}; + +#include "plPickNode.h" + +BOOL plShoreCompSelProc::DlgProc(TimeValue t, IParamMap2 *paramMap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = paramMap->GetParamBlock(); + INode* node = pb->GetINode(plShoreComponent::kWaveSet); + TSTR newName(node ? node->GetName() : "Pick"); + ::SetWindowText(::GetDlgItem(hWnd, IDC_COMP_SHORE_CHOSE), newName); + } + return true; + + case WM_COMMAND: + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_COMP_SHORE_CHOSE) ) + { + IParamBlock2 *pb = paramMap->GetParamBlock(); + std::vector cids; + cids.push_back(WATER_COMP_CID); + if( plPick::Node(pb, plShoreComponent::kWaveSet, &cids, true, true) ) + { + INode* node = pb->GetINode(plShoreComponent::kWaveSet); + TSTR newName(node ? node->GetName() : "Pick"); + ::SetWindowText(::GetDlgItem(hWnd, IDC_COMP_SHORE_CHOSE), newName); + paramMap->Invalidate(plShoreComponent::kWaveSet); + ShowWindow(hWnd, SW_HIDE); + ShowWindow(hWnd, SW_SHOW); + } + + return false; + } + return true; + } + + return false; +} + +plShoreCompSelProc gShoreCompSelProc; + +CLASS_DESC(plShoreComponent, gShoreCompDesc, "Shore Line", "Shore", COMP_TYPE_WATER, SHORE_COMP_CID) + + + +ParamBlockDesc2 gShoreCompBk +( + plComponent::kBlkComp, _T("Shore"), 0, &gShoreCompDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_SHORE, IDS_COMP_SHORE, 0, 0, &gShoreCompSelProc, + + plShoreComponent::kWaveSet, _T("WaveSet"), TYPE_INODE, 0, 0, + end, + + end +); + +plShoreComponent::plShoreComponent() +{ + fClassDesc = &gShoreCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plShoreComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetRunTimeLight(true); + node->SetForceMaterialCopy(true); + node->SetForceShadow(false); + node->SetNoShadow(true); + node->SetSmoothAll(true); + node->SetForceSortable(true); + node->SetNoSpanSort(true); + node->SetNoPreShade(true); + + ISetWaterDependencies(node, fCompPB->GetINode(kWaveSet, 0, 0)); + + // Turn on calculation of edge lengths XXX + node->SetCalcEdgeLens(true); + + // Make a note that we're vertex shaded (at least ripplecomponent needs to know).XXX + node->SetVS(true); + + // Turn off the convexity test. We want to be sorted.XXX + node->SetConcave(true); + + node->SetReverseSort(true); + + node->SetWaterHeight(plWaterComponent::GetWaterHeight(fCompPB->GetINode(kWaveSet, 0, 0))); + + return true; +} + +hsBool plShoreComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + pErrMsg->Set(!node->HasLoadMask(), node->GetName(), "PS shore has no representation component").CheckAndAsk(); + pErrMsg->Set(false); + + + return true; +} + +hsBool plShoreComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plWaveSetBase* waveSet = plWaterComponent::GetWaveSet(fCompPB->GetINode(kWaveSet, 0, 0)); + if( waveSet ) + { + plSceneObject* obj = node->GetSceneObject(); + if( obj ) + { + waveSet->AddShore(obj->GetKey()); + } + else + { + pErrMsg->Set(true, node->GetName(), "Invalid object selected for shore. Ignoring").CheckAndAsk(); + pErrMsg->Set(false); + } + } + else + { + pErrMsg->Set(true, node->GetName(), "No Water Component selected for shore. Ignoring").CheckAndAsk(); + pErrMsg->Set(false); + } + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +class plWDecalCompSelProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void DeleteThis() { } +}; + +#include "plPickNode.h" + +BOOL plWDecalCompSelProc::DlgProc(TimeValue t, IParamMap2 *paramMap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = paramMap->GetParamBlock(); + INode* node = pb->GetINode(plWDecalComponent::kWaveSet); + TSTR newName(node ? node->GetName() : "Pick"); + ::SetWindowText(::GetDlgItem(hWnd, IDC_COMP_WDECAL_CHOSE), newName); + } + return true; + + case WM_COMMAND: + if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == IDC_COMP_WDECAL_CHOSE) ) + { + IParamBlock2 *pb = paramMap->GetParamBlock(); + std::vector cids; + cids.push_back(WATER_COMP_CID); + if( plPick::Node(pb, plWDecalComponent::kWaveSet, &cids, true, true) ) + { + INode* node = pb->GetINode(plWDecalComponent::kWaveSet); + TSTR newName(node ? node->GetName() : "Pick"); + ::SetWindowText(::GetDlgItem(hWnd, IDC_COMP_WDECAL_CHOSE), newName); + paramMap->Invalidate(plWDecalComponent::kWaveSet); + ShowWindow(hWnd, SW_HIDE); + ShowWindow(hWnd, SW_SHOW); + } + + return false; + } + return true; + } + + return false; +} + +plWDecalCompSelProc gWDecalCompSelProc; + +CLASS_DESC(plWDecalComponent, gWDecalCompDesc, "Water Decal", "WDecal", COMP_TYPE_WATER, WDECAL_COMP_CID) + + + +ParamBlockDesc2 gWDecalCompBk +( + plComponent::kBlkComp, _T("WDecal"), 0, &gWDecalCompDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_WDECAL, IDS_COMP_WDECAL, 0, 0, &gWDecalCompSelProc, + + plWDecalComponent::kWaveSet, _T("WaveSet"), TYPE_INODE, 0, 0, + end, + + plWDecalComponent::kEnv, _T("Env"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_WDECAL_ENV, + end, + + end +); + +plWDecalComponent::plWDecalComponent() +{ + fClassDesc = &gWDecalCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plWDecalComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetRunTimeLight(true); + node->SetForceMaterialCopy(true); + node->SetForceShadow(false); + node->SetNoShadow(true); + node->SetSmoothAll(true); + node->SetForceSortable(true); + node->SetNoSpanSort(true); + node->SetNoPreShade(true); + + // This should be optional. + if( fCompPB->GetInt(kEnv) ) + node->SetWaterDecEnv(true); + + ISetWaterDependencies(node, fCompPB->GetINode(kWaveSet, 0, 0)); + + // Turn on calculation of edge lengths XXX + node->SetCalcEdgeLens(true); + + // Make a note that we're vertex shaded (at least ripplecomponent needs to know).XXX + node->SetVS(true); + + // Turn off the convexity test. We want to be sorted.XXX + node->SetConcave(true); + + node->SetReverseSort(true); + + node->SetWaterHeight(plWaterComponent::GetWaterHeight(fCompPB->GetINode(kWaveSet, 0, 0))); + + return true; +} + +hsBool plWDecalComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + pErrMsg->Set(!node->HasLoadMask(), node->GetName(), "PS water decal has no representation component").CheckAndAsk(); + pErrMsg->Set(false); + + + return true; +} + +hsBool plWDecalComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + plWaveSetBase* waveSet = plWaterComponent::GetWaveSet(fCompPB->GetINode(kWaveSet, 0, 0)); + if( waveSet ) + { + plSceneObject* obj = node->GetSceneObject(); + if( obj ) + { + waveSet->AddDecal(obj->GetKey()); + } + else + { + pErrMsg->Set(true, node->GetName(), "Invalid object selected for decal. Ignoring").CheckAndAsk(); + pErrMsg->Set(false); + } + } + else + { + pErrMsg->Set(true, node->GetName(), "No Water Component selected for decal. Ignoring").CheckAndAsk(); + pErrMsg->Set(false); + } + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +class plEnvMapCompSelProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + for(int i = 0; i < map->GetParamBlock()->Count(plEnvMapComponent::kVisSetNames); i++ ) + { + HWND hList = GetDlgItem(hWnd, IDC_COMP_ENVMAP_NAMES_LISTBOX); + ListBox_AddString(hList, map->GetParamBlock()->GetStr(plEnvMapComponent::kVisSetNames, 0, i)); + } + } + return true; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ADD_TARGS) + { + std::vector cids; + cids.push_back(EFFVISSET_CID); + IParamBlock2 *pb = map->GetParamBlock(); + plPick::Node(pb, plEnvMapComponent::kVisSets, &cids, false, false); + + map->Invalidate(plEnvMapComponent::kVisSets); + return TRUE; + } + else if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_COMP_ENVMAP_ADD_STRING) + { + char str[256]; + char *pStr = str; + ICustEdit *custEdit = GetICustEdit(GetDlgItem(hWnd, IDC_COMP_ENVMAP_ADD_STRING_BOX)); + custEdit->GetText(str, 256); + custEdit->SetText(""); // clear text box + + if(!strcmp(str, "")) // don't allow empty strings + return TRUE; + + HWND hList = GetDlgItem(hWnd, IDC_COMP_ENVMAP_NAMES_LISTBOX); + ListBox_AddString(hList, pStr); + map->GetParamBlock()->Append(plEnvMapComponent::kVisSetNames, 1, &pStr); + return TRUE; + } + else if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_COMP_ENVMAP_REMOVE_STRING) + { + HWND hList = GetDlgItem(hWnd, IDC_COMP_ENVMAP_NAMES_LISTBOX); + int curSel = ((int)(DWORD)SNDMSG((hList), LB_GETCURSEL, 0L, 0L)); + ListBox_DeleteString(hList, curSel); + if (curSel >= 0) + map->GetParamBlock()->Delete(ParamID(plEnvMapComponent::kVisSetNames), curSel, 1); + + return TRUE; + } + break; + } + + return false; + } + void DeleteThis() {} +}; +static plEnvMapCompSelProc gEnvMapCompSelProc; + +CLASS_DESC(plEnvMapComponent, gEnvMapCompDesc, "Environment Map", "EnvMap", COMP_TYPE_WATER, ENVMAP_COMP_CID) + + + +ParamBlockDesc2 gEnvMapCompBk +( + plComponent::kBlkComp, _T("EnvMap"), 0, &gEnvMapCompDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_ENVMAP, IDS_COMP_ENVMAP, 0, 0, &gEnvMapCompSelProc, + + plEnvMapComponent::kVisSets, _T("VisSets"), TYPE_INODE_TAB, 0, 0, 0, + p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS, + end, + + plEnvMapComponent::kHither, _T("Hither"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.0, 500.0, +// p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, +// IDC_COMP_ENVMAP_HITHER, IDC_COMP_ENVMAP_HITHER_SPIN, 1.0, + end, + + plEnvMapComponent::kYon, _T("Yon"), TYPE_FLOAT, 0, 0, + p_default, 1000.0, + p_range, 10.0, 50000.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ENVMAP_YON, IDC_COMP_ENVMAP_YON_SPIN, 1.0, + end, + + plEnvMapComponent::kFogEnable, _T("FogEnable"), TYPE_BOOL, 0, 0, + p_default, FALSE, +// p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_ENVMAP_FOGENABLE, +// p_enable_ctrls, 2, plEnvMapComponent::kFogStart, plEnvMapComponent::kFogColor, + end, + + plEnvMapComponent::kFogStart, _T("FogStart"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 200.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ENVMAP_FOGSTART, IDC_COMP_ENVMAP_FOGSTART_SPIN, 1.0, + end, + + + plEnvMapComponent::kFogColor, _T("FogColor"), TYPE_RGBA, 0, 0, + p_ui, TYPE_COLORSWATCH, IDC_COMP_ENVMAP_FOGCOLOR, + p_default, Color(0,0,0), + end, + + plEnvMapComponent::kRefreshRate, _T("RefreshRate"), TYPE_FLOAT, 0, 0, + p_default, 0.0, + p_range, 0.0, 3600.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_COMP_ENVMAP_REFRESHRATE, IDC_COMP_ENVMAP_REFRESHRATE_SPIN, 1.0, + end, + + plEnvMapComponent::kEnvSize, _T("EnvSize"), TYPE_INT, 0, 0, + p_default, 256, + p_range, 32, 1024, + p_ui, TYPE_SPINNER, EDITTYPE_INT, + IDC_COMP_ENVMAP_ENVSIZE, IDC_COMP_ENVMAP_ENVSIZE_SPIN, 1.f, + end, + + plEnvMapComponent::kIncChars, _T("IncChars"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_ENVMAP_INCCHARS, + end, + + plEnvMapComponent::kMapType, _T("mapType"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 2, IDC_COMP_ENVMAP_CUBIC, IDC_COMP_ENVMAP_SINGLE_CAM, + p_vals, plEnvMapComponent::kMapCubic, plEnvMapComponent::kMapSingle, + p_default, plEnvMapComponent::kMapCubic, + end, + + plEnvMapComponent::kVisSetNames, _T("VisSetNames"), TYPE_STRING_TAB, 0, 0, 0, + end, + + end +); + +plEnvMapComponent::plEnvMapComponent() +{ + fClassDesc = &gEnvMapCompDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading +// of properties on the MaxNode, as it's still indeterminant. +hsBool plEnvMapComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + fMap = nil; + + node->SetForceLocal(true); + + return true; +} + +hsBool plEnvMapComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + + return true; +} + +hsBool plEnvMapComponent::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + // If we make a handler that will update the EnvMap's position when this object + // moves, we can put it's creation here. Otherwise, there's nought to do, since + // we generate the envmap on demand. + return true; +} + +plDynamicEnvMap* plEnvMapComponent::GetEnvMap() +{ + return plDynamicEnvMap::ConvertNoRef(IGetMap()); +} + +plDynamicCamMap* plEnvMapComponent::GetCamMap() +{ + return plDynamicCamMap::ConvertNoRef(IGetMap()); +} + +plRenderTarget* plEnvMapComponent::IGetMap() +{ + plMaxNode* firstTarg = nil; + int numTarg = NumTargets(); + int i; + for( i = 0; i < numTarg; i++ ) + { + if( GetTarget(i) ) + { + firstTarg = (plMaxNode*)GetTarget(i); + break; + } + } + if( !firstTarg ) + return nil; + + if( !fMap ) + { + UInt32 size = fCompPB->GetInt(kEnvSize); + for( i = 9; i > 5; i-- ) + { + if( (1UL << UInt32(i)) <= size ) + break; + } + size = 1 << UInt32(i); + + plDynamicEnvMap* env = nil; + plDynamicCamMap* cam = nil; + fMap = nil; + if (fCompPB->GetInt((ParamID(kMapType))) == kMapCubic) + fMap = env = TRACKED_NEW plDynamicEnvMap(size, size, 32); + else if (fCompPB->GetInt((ParamID(kMapType))) == kMapSingle) + fMap = cam = TRACKED_NEW plDynamicCamMap(size, size, 32); + + // Need to assign the key before we call all the setup functions. + hsgResMgr::ResMgr()->NewKey(GetINode()->GetName(), fMap, firstTarg->GetLocation(), firstTarg->GetLoadMask()); + + + if (fCompPB->GetInt((ParamID(kMapType))) == kMapCubic) + { + Point3 pos = firstTarg->GetNodeTM(TimeValue(0)).GetTrans(); + env->SetPosition(hsPoint3(pos.x, pos.y, pos.z)); + env->SetRefreshRate(fCompPB->GetFloat(kRefreshRate)); + env->SetHither(fCompPB->GetFloat(kHither)); + env->SetYon(fCompPB->GetFloat(kYon)); + env->SetFogStart(fCompPB->GetFloat(kFogStart) * kPercentToFrac); + Color fogColor = fCompPB->GetColor(kFogColor); + env->SetColor(hsColorRGBA().Set(fogColor.r, fogColor.g, fogColor.b, 1.f)); + } + else if (fCompPB->GetInt((ParamID(kMapType))) == kMapSingle) + { + cam->SetRefreshRate(fCompPB->GetFloat(ParamID(kRefreshRate))); + cam->fHither = fCompPB->GetFloat(ParamID(kHither)); + cam->fYon = fCompPB->GetFloat(ParamID(kYon)); + cam->fFogStart = fCompPB->GetFloat(ParamID(kFogStart)) * kPercentToFrac; + Color fogColor = fCompPB->GetColor(kFogColor); + cam->fColor.Set(fogColor.r, fogColor.g, fogColor.b, 1.f); + } + if (!fMap) + return nil; + + int visGot = 0; + int numVis = fCompPB->Count(kVisSets); + for( i = 0; i < numVis; i++ ) + { + plEffVisSetComponent* effComp = plEffVisSetComponent::ConvertToEffVisSetComponent((plMaxNode*)fCompPB->GetINode(kVisSets, 0, i)); + if( effComp ) + { + plVisRegion* effReg = effComp->GetVisRegion(firstTarg); + if( effReg ) + { + plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(fMap->GetKey(), plRefMsg::kOnCreate, -1, plDynamicEnvMap::kRefVisSet); + hsgResMgr::ResMgr()->SendRef(effReg->GetKey(), refMsg, plRefFlags::kPassiveRef); + + visGot++; + } + } + } + + // This allows you to enter the name of an effect vis set(key name), from another max file and use it + // as if it we're in the same max file. + int numVisNames = fCompPB->Count(kVisSetNames); + for( i = 0; i < numVisNames; i++) + { + fMap->SetVisRegionName(fCompPB->GetStr(kVisSetNames, 0, i)); + } + + if (visGot) + { + if (env) + env->SetIncludeCharacters(fCompPB->GetInt(ParamID(kIncChars)) != 0); + if (cam) + cam->SetIncludeCharacters(fCompPB->GetInt(ParamID(kIncChars)) != 0); + } + + // Right now, the envMap doesn't use this, but I plan to make it do so, so I'm + // going ahead and adding the ref regardless of which type of map we made. + UInt8 refType = cam ? plDynamicCamMap::kRefRootNode : plDynamicEnvMap::kRefRootNode; + hsgResMgr::ResMgr()->AddViaNotify(firstTarg->GetSceneObject()->GetKey(), TRACKED_NEW plGenRefMsg(fMap->GetKey(), plRefMsg::kOnCreate, -1, refType), plRefFlags::kPassiveRef); + } + return fMap; +} + +plDynamicEnvMap* plEnvMapComponent::GetEnvMap(plMaxNode* node) +{ + plEnvMapComponent* envComp = GetEnvMapComponent(node); + if (envComp) + return envComp->GetEnvMap(); + + return nil; +} + +plDynamicCamMap* plEnvMapComponent::GetCamMap(plMaxNode *node) +{ + plEnvMapComponent *envComp = GetEnvMapComponent(node); + if (envComp) + return envComp->GetCamMap(); + + return nil; +} + +plEnvMapComponent *plEnvMapComponent::GetEnvMapComponent(plMaxNode *node) +{ + if (!node) + return nil; + + int n = node->NumAttachedComponents(); + int i; + for (i = 0; i < n; i++) + { + plComponentBase *comp = node->GetAttachedComponent(i); + if (comp && (comp->ClassID() == ENVMAP_COMP_CID)) + { + return (plEnvMapComponent*)comp; + } + } + return nil; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plWaterComponent.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plWaterComponent.h new file mode 100644 index 00000000..a44d228f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plWaterComponent.h @@ -0,0 +1,242 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + + +#ifndef plWaterComponent_inc +#define plWaterComponent_inc + +#include "plQuality.h" + +const Class_ID WATER_COMP_CID(0x1d7c20cc, 0x68106c16); +const Class_ID SHORE_COMP_CID(0x74613f05, 0x326d74ac); +const Class_ID WDECAL_COMP_CID(0x1fe01c50, 0x3d16276f); +const Class_ID ENVMAP_COMP_CID(0x59b7e1c, 0x22780bb7); + +class plWaveSetBase; +class plFixedWaterState7; +class plRenderTarget; +class plDynamicEnvMap; +class plDynamicCamMap; + +class plWaveSet7; + +class plWaterComponent : public plComponent +{ +public: + enum { + kRefObject, + + kWindSpeed, + kWaterTint, // Color + kWaterOpac, // Scalar + kSpecularTint, // Color + kRippleScale, // determines ripple scale + + kDepthOpac, // Depth at which overall opacity reaches 100% in feet + kDepthRefl, // Depth at which reflection strength reaches 100% in feet + kDepthWave, // Depth at which geometric wave height reaches 100% in feet + kZeroOpac, // Offset, positive pulls full-on in + kZeroRefl, // Offset, positive pulls full-on in + kZeroWave, // Offset, positive pulls full-on in + + kShoreTint, // Color => MinColor.rgb + kShoreOpac, + kWispiness, + + kPeriod, // ??? + kFinger, // Finger length [0..100]% + + kDispersion, // How rough (non-viscous) the surface ripples look. + + kEnvObject, + kEnvSize, + kEnvRadius, + + kEdgeOpac, + kEdgeRadius, + + kEnvRefresh, + + kGeoAngleDev, + kTexAngleDev, + + kGeoMinLen, + kGeoMaxLen, + kGeoAmpOverLen, + + kTexMinLen, + kTexMaxLen, + kTexAmpOverLen, + + kNoise, + + kSpecStart, + kSpecEnd, + + kGeoChop, + kTexChop, + + kSpecularMute, + + kNumParams + }; + enum { + kRef, +// kBasicWater, + kAdvWater, + kBasicShore, + kAdvShore, + kEnvMap, + kVtxHelp, + kGeoWater, + kTexWater, + kNumRollups + }; +protected: + plWaveSet7* fWaveSet; + + hsBool IGetRefObject(plMaxNode* node); + hsBool IReadEnvObject(plMaxNode* node, plErrorMsg* pErrMsg, plFixedWaterState7& ws); + hsBool IReadRefObject(plMaxNodeBase* node, plFixedWaterState7& ws); + hsBool IMakeWaveSet(plMaxNode* node, plErrorMsg* pErrMsg); + + plWaveSetBase* IGetWaveSet() const { return (plWaveSetBase*)fWaveSet; } // fWaveSet set in SetupProperties pass. + + hsScalar IGetWaterHeight(); + +public: + plWaterComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool DeInit(plMaxNode* node, plErrorMsg* pErrMsg); + + virtual int GetMinCap() { return plQuality::kPS_1_1; } + + // This works anytime. + static hsScalar GetWaterHeight(INode* node); // node is component node. + + // These only work after PreConvert pass + static plWaveSetBase* GetWaveSet(INode* node); // Node is the component node + static plWaveSetBase* GetWaveSetFromNode(plMaxNode* node); // node is the component's target + + // These just deal with old data with obsolete parameters. + void CheckForObsoleteParams(); + IOResult Load(ILoad* iLoad); +}; + +class plShoreComponent : public plComponent +{ +public: + enum { + kWaveSet + }; +protected: +public: + plShoreComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); + + virtual int GetMinCap() { return plQuality::kPS_1_1; } +}; + +class plWDecalComponent : public plComponent +{ +public: + enum { + kWaveSet, + kEnv + }; +protected: +public: + plWDecalComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); + + virtual int GetMinCap() { return plQuality::kPS_1_1; } +}; + +class plEnvMapComponent : public plComponent +{ +public: + enum { + kVisSets, + kHither, + kYon, + kFogEnable, + kFogStart, + kFogColor, + kRefreshRate, + kEnvSize, + kIncChars, + kMapType, + kVisSetNames, + }; + + // Map types + enum + { + kMapCubic, + kMapSingle, + }; + +protected: + plRenderTarget* fMap; // Will be a plDynamicEnvMap or plDynamicCamMap + + plRenderTarget* IGetMap(); + +public: + plEnvMapComponent(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); + + plDynamicEnvMap* GetEnvMap(); + plDynamicCamMap* GetCamMap(); + static plDynamicEnvMap* GetEnvMap(plMaxNode* node); + static plDynamicCamMap* GetCamMap(plMaxNode* node); + static plEnvMapComponent* GetEnvMapComponent(plMaxNode* node); +}; + +#endif // plBlowComponent_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plXImposter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plXImposter.cpp new file mode 100644 index 00000000..e5240186 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plXImposter.cpp @@ -0,0 +1,448 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "resource.h" +#include "plComponent.h" +#include "plComponentReg.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxExport/plExportProgressBar.h" + +#include "plXImposter.h" + +#include "../pfAnimation/plFilterCoordInterface.h" + +#include "../pnSceneObject/plSimulationInterface.h" +#include "plPhysical.h" + +const Class_ID FILTERINHERIT_COMP_CID(0x263928d8, 0x548456da); + +void DummyCodeIncludeFuncXImposter() +{ + +} + +//Class that accesses the paramblock below. +////////////////////////////////////////////////////////////////////////////////////////////////////// + +//Max desc stuff necessary below. +CLASS_DESC(plXImposterComp, gXImposterDesc, "X-Form", "X-Form", COMP_TYPE_DISTRIBUTOR, XIMPOSTER_COMP_CID) + +ParamBlockDesc2 gXImposterBk +( + plComponent::kBlkComp, _T("X-Form"), 0, &gXImposterDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_XFORM, IDS_COMP_XFORMS, 0, 0, NULL, + + end +); + +plXImposterComp::plXImposterComp() +{ + fClassDesc = &gXImposterDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plXImposterComp::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetRadiateNorms(true); + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// +const Class_ID FORCE_CTT_COMP_CID(0x30ee73b7, 0x4cdd551b); + +class plForceCTTComp : public plComponent +{ +public: + plForceCTTComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + +//Max desc stuff necessary below. +CLASS_DESC(plForceCTTComp, gForceCTTDesc, "ForceClick2Turn", "ForceCTT", COMP_TYPE_MISC, FORCE_CTT_COMP_CID) + +ParamBlockDesc2 gForceCTTBk +( + plComponent::kBlkComp, _T("ForceCTT"), 0, &gForceCTTDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FORCE_CTT, IDS_COMP_FORCE_CTT, 0, 0, NULL, + + end +); + +plForceCTTComp::plForceCTTComp() +{ + fClassDesc = &gForceCTTDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plForceCTTComp::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + node->SetForceVisLOS(true); + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// The filter inheritance component doesn't have anything to do with the XImposter, but there's +// plenty of space here, so blow me. + +class plFilterInheritComp : public plComponent +{ +public: + enum + { + kActive, + kNoX, + kNoY, + kNoZ + }; +public: + plFilterInheritComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg); + virtual hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg); + + hsBool SetMaxInherit(); + hsBool SetMaxInherit(plMaxNodeBase* targ); + hsBool KillMaxInherit(); + hsBool KillMaxInherit(plMaxNodeBase* targ); + + hsBool Bail(plMaxNode* node, const char* msg, plErrorMsg* pErrMsg); + + virtual void AddTarget(plMaxNodeBase *target); + virtual void DeleteTarget(plMaxNodeBase *target); + virtual void DeleteAllTargets(); +}; + +class FilterInheritCompDlgProc : public ParamMap2UserDlgProc +{ +protected: + void ISetTransEnable(IParamMap2* map) + { + IParamBlock2* pb = map->GetParamBlock(); + if( !pb->GetInt(plFilterInheritComp::kActive) ) + { + map->Enable(plFilterInheritComp::kNoX, FALSE); + map->Enable(plFilterInheritComp::kNoY, FALSE); + map->Enable(plFilterInheritComp::kNoZ, FALSE); + } + else + { + map->Enable(plFilterInheritComp::kNoX, TRUE); + map->Enable(plFilterInheritComp::kNoY, TRUE); + map->Enable(plFilterInheritComp::kNoZ, TRUE); + } + plFilterInheritComp* comp = (plFilterInheritComp*)map->GetParamBlock()->GetOwner(); + comp->SetMaxInherit(); + } +public: + FilterInheritCompDlgProc() {} + ~FilterInheritCompDlgProc() {} + + BOOL DlgProc(TimeValue t, IParamMap2* map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + ISetTransEnable(map); + break; + case WM_COMMAND: + ISetTransEnable(map); + break; + } + return FALSE; + } + void DeleteThis() {} +}; +static FilterInheritCompDlgProc gFilterInheritCompDlgProc; + + + +CLASS_DESC(plFilterInheritComp, gFilterInheritDesc, "Filter Inherit", "FiltInherit", COMP_TYPE_MISC, FILTERINHERIT_COMP_CID) + +ParamBlockDesc2 gFilterInheritBk +( + plComponent::kBlkComp, _T("FilterInherit"), 0, &gFilterInheritDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, + + IDD_COMP_FILTERINHERIT, IDS_COMP_FILTER, 0, 0, &gFilterInheritCompDlgProc, + + plFilterInheritComp::kActive, _T("Active"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FILTER_ACTIVE, + end, + + plFilterInheritComp::kNoX, _T("NoX"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FILTER_NOX, + end, + + plFilterInheritComp::kNoY, _T("NoY"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FILTER_NOY, + end, + + plFilterInheritComp::kNoZ, _T("NoZ"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FILTER_NOZ, + end, + + end +); + +plFilterInheritComp::plFilterInheritComp() +{ + fClassDesc = &gFilterInheritDesc; + fClassDesc->MakeAutoParamBlocks(this); +} + +hsBool plFilterInheritComp::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fCompPB->GetInt(kActive) ) + return true; + + if( node->GetParentNode()->IsRootNode() ) + return true; + + node->SetFilterInherit(true); + node->SetForceLocal(true); + plMaxNode* parent = (plMaxNode*)node->GetParentNode(); + parent->SetForceLocal(true); + + // Okay, everything works fine as long as you set up your heirarchy, and THEN + // add this component. But if you put this component on the as yet unlinked child, + // and THEN link the child to the parent, Max starts reporting bogus local TM info. + // If you turn off the component (uncheck the disable rotation checkbox), and turn + // it back on, Max is happy again. + // However, if I just turn off the component and turn it right back on again here, + // (a KillMaxInherit() followed by a SetMaxInherit()), apparently Max hasn't had + // enough time to think in between, and so stays confused. + // Soooo, here at the last minute before conversion, we kill all those inherit + // checkboxes for this node, then after we've finished converting, we turn them + // back on. Note that we don't currently give a rat's patooties whether the check + // boxes are set or not, we just want to turn them off and turn them back on + // sometime before export, with enough time in between for Max to get on to the fact. + // See the matching SetMaxInherit() at the end of Convert(). + KillMaxInherit(node); + + return true; +} + +hsBool plFilterInheritComp::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + return true; +} + +hsBool plFilterInheritComp::Bail(plMaxNode* node, const char* msg, plErrorMsg* pErrMsg) +{ + pErrMsg->Set(true, node->GetName(), msg).CheckAndAsk(); + pErrMsg->Set(false); + return true; +} + +hsBool plFilterInheritComp::Convert(plMaxNode* node, plErrorMsg* pErrMsg) +{ + if( !fCompPB->GetInt(kActive) ) + return true; + + if( node->GetParentNode()->IsRootNode() ) + return true; + + plSceneObject* so = node->GetSceneObject(); + if( !so ) + { + return Bail(node, "Error finding scene object for filtered inheritance", pErrMsg); + } + + const plCoordinateInterface* co = so->GetCoordinateInterface(); + if( !co ) + { + return Bail(node, "Error setting filtered inheritance - no coordinate interface", pErrMsg); + } + + plFilterCoordInterface* filt = plFilterCoordInterface::ConvertNoRef(const_cast(co)); + if( !filt ) + { + return Bail(node, "Error setting filtered inheritance - wrong coordinate interface", pErrMsg); + } + + const plSimulationInterface* si = so->GetSimulationInterface(); + if (si) + { + plPhysical* phys = si->GetPhysical(); + // tell the physical not to send transforms back -- they'll be wrong if it tries to compose w/a subworld + // this rules out using transform filters on dynamically simulated objects.... + phys->SetProperty(plSimulationInterface::kPassive, true); + } + + UInt32 mask = plFilterCoordInterface::kNoRotation; + if( fCompPB->GetInt(kNoX) ) + mask |= plFilterCoordInterface::kNoTransX; + if( fCompPB->GetInt(kNoY) ) + mask |= plFilterCoordInterface::kNoTransY; + if( fCompPB->GetInt(kNoZ) ) + mask |= plFilterCoordInterface::kNoTransZ; + + plMaxNode* parent = (plMaxNode*)node->GetParentNode(); + hsMatrix44 parL2W = parent->GetLocalToWorld44(TimeValue(0)); + + filt->SetFilterMask(mask); + filt->SetRefLocalToWorld(parL2W); + + // See the matching KillMaxInherit() in SetupProperties(). + SetMaxInherit(node); + + return true; +} + +hsBool plFilterInheritComp::SetMaxInherit(plMaxNodeBase* targ) +{ + if( !fCompPB->GetInt(kActive) ) + return KillMaxInherit(targ); + + DWORD mask = INHERIT_ROT_X + | INHERIT_ROT_Y + | INHERIT_ROT_Z + | INHERIT_SCL_X + | INHERIT_SCL_Y + | INHERIT_SCL_Z; + + if( fCompPB->GetInt(kNoX) ) + mask |= INHERIT_POS_X; + if( fCompPB->GetInt(kNoY) ) + mask |= INHERIT_POS_Y; + if( fCompPB->GetInt(kNoZ) ) + mask |= INHERIT_POS_Z; + + // Max documentation is a big fat liar + mask = ~mask; + + if( targ ) + { + targ->GetTMController()->SetInheritanceFlags(mask, true); + targ->GetTMController()->NotifyDependents(FOREVER,0,REFMSG_CHANGE); + } + + return true; +} + +hsBool plFilterInheritComp::SetMaxInherit() +{ + if( !fCompPB->GetInt(kActive) ) + return KillMaxInherit(); + + DWORD mask = INHERIT_ROT_X + | INHERIT_ROT_Y + | INHERIT_ROT_Z + | INHERIT_SCL_X + | INHERIT_SCL_Y + | INHERIT_SCL_Z; + + if( fCompPB->GetInt(kNoX) ) + mask |= INHERIT_POS_X; + if( fCompPB->GetInt(kNoY) ) + mask |= INHERIT_POS_Y; + if( fCompPB->GetInt(kNoZ) ) + mask |= INHERIT_POS_Z; + + // Max documentation is a big fat liar + mask = ~mask; + + int i; + for( i = 0; i < NumTargets(); i++ ) + { + plMaxNodeBase* targ = GetTarget(i); + if( targ ) + { + targ->GetTMController()->SetInheritanceFlags(mask, true); + targ->GetTMController()->NotifyDependents(FOREVER,0,REFMSG_CHANGE); + } + } + return true; +} + +hsBool plFilterInheritComp::KillMaxInherit(plMaxNodeBase* targ) +{ + // Max documentation is a big fat liar + DWORD mask = ~0; + + targ->GetTMController()->SetInheritanceFlags(mask, true); + targ->GetTMController()->NotifyDependents(FOREVER,0,REFMSG_CHANGE); + + return true; +} + +hsBool plFilterInheritComp::KillMaxInherit() +{ + int i; + for( i = 0; i < NumTargets(); i++ ) + { + plMaxNodeBase* targ = GetTarget(i); + if( targ ) + { + KillMaxInherit(targ); + } + } + return true; +} + +void plFilterInheritComp::AddTarget(plMaxNodeBase *target) +{ + plComponentBase::AddTarget(target); + + SetMaxInherit(); +} + +void plFilterInheritComp::DeleteTarget(plMaxNodeBase *target) +{ + plComponentBase::DeleteTarget(target); + + KillMaxInherit(target); +} + +void plFilterInheritComp::DeleteAllTargets() +{ + KillMaxInherit(); + + plComponentBase::DeleteAllTargets(); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plXImposter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plXImposter.h new file mode 100644 index 00000000..ab3c7da8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/plXImposter.h @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plXImposter_inc +#define plXImposter_inc + +#include "plComponent.h" + +class plMaxNode; +class plErrorMsg; + +const Class_ID XIMPOSTER_COMP_CID(0x68c9300f, 0x7c5428af); + +class plXImposterComp : public plComponent +{ +public: + plXImposterComp(); + void DeleteThis() { delete this; } + + // SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading + // of properties on the MaxNode, as it's still indeterminant. + virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg); + virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } + virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; } +}; + +#endif // plXImposter_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/resource.h new file mode 100644 index 00000000..3b67139a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/resource.h @@ -0,0 +1,1893 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by plComponent.rc +// +#define IDS_COMP_ANIM 31 +#define IDS_PLASMA_ATTRIB 32 +#define IDS_CHECK 33 +#define IDS_COMP_ACTIVE 34 +#define IDS_COMP_RESPOND 35 +#define IDS_COMP_ALIASS 36 +#define IDS_COMP_STDPORTALS 37 +#define IDS_COMP_TARGS 38 +#define IDS_COMP_INTERESTS 39 +#define IDS_COMP_FOOTPRINT 40 +#define IDS_COMP_AUTO 41 +#define IDS_COMP_VIEWFACES 42 +#define IDS_COMP_SOUND3DS 43 +#define IDS_COMP_BARNEYS 44 +#define IDS_COMP_SOUNDGUI 45 +#define IDS_COMP_CAMERAS 46 +#define IDS_COMP_BLENDONTO 47 +#define IDS_COMP_PHYS_CHOSEN_BASE 48 +#define IDS_COMP_PHYS_CHOSEN_SIMP 49 +#define IDS_COMP_PHYS_CHOSEN_TERRAIN 50 +#define IDS_COMP_PHYS_CHOSEN_PLAYER 51 +#define IDS_COMP_ROOMS 52 +#define IDS_COMP_CAMVIEWS 53 +#define IDS_COMP_LODFADE_BASE 54 +#define IDS_COMP_ANIM_AVATARS 55 +#define IDS_COMP_SOUND_REVERBS 56 +#define IDS_COMP_AVATARS 57 +#define IDS_COMP_AVATAR_MODELS 58 +#define IDS_COMP_AVATAR_PROXYS 59 +#define IDS_COMP_LIGHTMAPS 60 +#define IDS_COMP_IGNORES 61 +#define IDS_COMP_FORCE_RTLIGHT 62 +#define IDS_COMP_SOUNDMOD_CHORUSS 63 +#define IDS_COMP_SOUNDMOD_COMPRESSORS 64 +#define IDS_COMP_SOUNDMOD_DISTORTIONS 65 +#define IDS_COMP_SOUNDMOD_ECHOS 66 +#define IDS_COMP_SOUNDMOD_FLANGERS 67 +#define IDS_COMP_SOUNDMOD_GARGLES 68 +#define IDS_COMP_SOUNDMOD_REVERBS 69 +#define IDS_COMP_SOUNDMOD_WETS 70 +#define IDS_COMP_SOUNDMOD_DELAYMODS 71 +#define IDS_COMP_SOUNDMOD_FEEDBACKS 72 +#define IDS_COMP_SOUNDMOD_FREQ_OSC_CUTOFFS 73 +#define IDS_COMP_SOUNDMOD_FEEDBACK_DELAYS 74 +#define IDS_COMP_SOUNDMOD_GAINS 75 +#define IDS_COMP_SOUNDMOD_ATTACKS 76 +#define IDS_COMP_SOUNDMOD_RELEASES 77 +#define IDS_COMP_SOUNDMOD_COMP_RATIOS 78 +#define IDS_COMP_SOUNDMOD_ATTACK_PREDELAYS 79 +#define IDS_COMP_SOUNDMOD_COMPTHRESHS 80 +#define IDS_COMP_SOUNDMOD_INTENSITYS 81 +#define IDS_COMP_SOUNDMOD_EFFECTCS 82 +#define IDS_COMP_SOUNDMOD_EFFECTWS 83 +#define IDS_COMP_SOUNDMOD_LOWFREQ_CUTOFFS 84 +#define IDS_COMP_SOUNDMOD_LEFTFBDS 85 +#define IDS_COMP_SOUNDMOD_RIGHTFBDS 86 +#define IDS_COMP_SOUNDMOD_SWAPRLS 87 +#define IDS_COMP_SOUNDMOD_FBDELAYFS 88 +#define IDS_COMP_SOUNDMOD_MODRATES 89 +#define IDS_COMP_SOUNDMOD_REVERBGAINS 90 +#define IDS_COMP_SOUNDMOD_REVERBMIXS 91 +#define IDS_COMP_SOUNDMOD_REVERBDELAYS 92 +#define IDS_COMP_SOUNDMOD_GAINDS 93 +#define IDS_COMP_OCCLUSIONS 94 +#define IDS_COMP_OCCLUSION_PROXY 95 +#define IDS_COMP_LODFADE 96 +#define IDS_COMP_SOUNDMOD_BINDINGSS 97 +#define IDS_COMP_PARTICLE_ROLL 98 +#define IDS_COMP_CAMERACMD 99 +#define IDS_COMP_PAGEINFOS 100 +#define IDS_COMP_SOUNDBGND 101 +#define IDS_COMP_SMOOTHS 102 +#define IDS_COMP_LINEFOLLOWS 103 +#define IDS_COMP_LINE_CHOSE_OBJECT 104 +#define IDS_COMP_LINE_CHOSE_PATH 105 +#define IDS_COMP_DETECTOR_CLICKABLE 106 +#define IDS_COMP_CMD_PARAMS 107 +#define IDS_COMP_RANDOMSOUNDS 108 +#define IDS_COMP_DETECTOR_CLICK_DRAGGABLE 109 +#define IDS_COMP_LIGHTINCS 110 +#define IDS_COMP_LIGHTINC_CHOSE_LIGHT 111 +#define IDS_COMP_PARTICLE_VOLUME_ROLL 112 +#define IDS_COMP_PARTICLE_FADE_ROLL 113 +#define IDS_COMP_FOLLOWS 114 +#define IDS_COMP_INV_OBJECTS 115 +#define IDS_COMP_SOFTVOLUMES 116 +#define IDS_COMP_PYTHON 117 +#define IDS_COMP_SOUND_REVERB_PARAMS 118 +#define IDS_COMP_DETECTOR_REGION 119 +#define IDS_COMP_UNLEASH 120 +#define IDS_COMP_DETECTOR_COLLISION 121 +#define IDS_COMP_SOFTVOLUME_UNION 122 +#define IDS_COMP_PHYS_WHEEL_CONSTRAINTS 123 +#define IDS_COMP_PHYS_HINGE_CONSTRAINTS 124 +#define IDS_COMP_PHYS_SS_CONSTRAINTS 125 +#define IDS_COMP_SOFTVOLUME_ISECT 126 +#define IDS_COMP_SOFTVOLUME_NEGATE 127 +#define IDS_COMP_LIGHTREGION 128 +#define IDS_COMP_GEO_DICE 129 +#define IDS_COMP_ONESHOTS 130 +#define IDS_COMP_WAIT 131 +#define IDS_COMP_SOUNDFADEPARAMS 132 +#define IDS_COMP_SOUNDSOFTPARAMS 133 +#define IDS_COMP_SOUNDSOFTSELECT 134 +#define IDS_COMP_LOD_AVATARS 135 +#define IDS_CAMERARGN 136 +#define IDS_COMP_PARTICLE_WIND 137 +#define IDS_COMP_BLENDONTOADV 138 +#define IDS_COMP_PARTICLE_UNIWIND 139 +#define IDS_COMP_DISTFADE 140 +#define IDS_COMP_SHADOW_CAST 141 +#define IDS_COMP_GUIDIALOG 142 +#define IDS_CAMERA_OCCLUDERS 143 +#define IDS_COMP_SHADOW_RCV 144 +#define IDS_COMP_AUTOCAM 145 +#define IDS_COMP_GUIBUTTON 146 +#define IDS_COMP_DISTRIBUTORS 147 +#define IDS_COMP_GUIDRAGGABLE 148 +#define IDS_COMP_CLUSTERS 149 +#define IDS_COMP_XREGION 150 +#define IDS_COMP_GUILISTBOX 151 +#define IDS_COMP_GUITEXTBOX 152 +#define IDS_COMP_ANIM_EASE 153 +#define IDS_COMP_IGNORELITES 154 +#define IDS_COMP_GUIEDITBOX 155 +#define IDS_COMP_FLEXIBILITYS 156 +#define IDS_COMP_GUITAG 157 +#define IDS_COMP_BLOWS 158 +#define IDS_COMP_CLUSTER_CHOSE_WINDBONE 159 +#define IDS_COMP_PHYS_PUPPET_ROOT 160 +#define IDS_COMP_GZ_FADE 160 +#define IDS_COMP_ANIM_GROUPED 161 +#define IDS_COMP_XFORMS 162 +#define IDS_COMP_BEHAVIOR_SITTINGS 163 +#define IDS_COMP_GUIUDSCROLL 164 +#define IDS_COMP_GUI_SELECTUDCTRL 165 +#define IDS_COMP_GUI_SELECTSCROLL 166 +#define IDS_COMP_PHYS_MEMBER 167 +#define IDS_COMP_PHYS_BOUNCE 168 +#define IDS_COMP_PHYS_REPORT 169 +#define IDS_COMP_NAV_LADDERS 170 +#define IDS_COMP_CLONE_INST 171 +#define IDS_COMP_CLONE_TEMPLATE 172 +#define IDS_COMP_GUIKNOB 173 +#define IDS_PARTICLE_VELOCITY_MIN 174 +#define IDS_PARTICLE_VELOCITY_MAX 175 +#define IDS_PARTICLE_LIFE_MIN 176 +#define IDS_PARTICLE_LIFE_MAX 177 +#define IDS_PARTICLE_SCALE_MIN 178 +#define IDS_PARTICLE_SCALE_MAX 179 +#define IDS_PARTICLE_PPS 180 +#define IDS_PARTICLE_CONE_ANGLE 181 +#define IDS_COMP_GUIDRAGBAR 182 +#define IDS_COMP_DIST_TEMPLATES 183 +#define IDS_COMP_DIST_SPACING 184 +#define IDS_COMP_DIST_ORIENT 185 +#define IDS_COMP_DIST_ANGPROB 186 +#define IDS_COMP_DIST_ALTPROB 187 +#define IDS_COMP_DIST_CONFORM 188 +#define IDS_COMP_DIST_BITMAP 189 +#define IDS_COMP_DIST_FADE 190 +#define IDS_COMP_DIST_WIND 191 +#define IDS_COMP_DIST_RANDOMIZE 192 +#define IDS_COMP_DIST_SCALE 193 +#define IDS_COMP_GUICHECKBOX 194 +#define IDS_COMP_GUI_SELECTANIM 195 +#define IDS_COMP_DIST_ACTION 196 +#define IDS_COMP_GUIRADIOGROUP 197 +#define IDS_COMP_GUI_SELECTCHECK 198 +#define IDS_COMP_GUIPROCROLLOUT 199 +#define IDS_COMP_GUISCHEME 200 +#define IDS_COMP_PHYS_DEBUG 201 +#define IDS_COMP_PHYS_SIMPLE 202 +#define IDS_COMP_PHYS_TERRAIN 203 +#define IDS_COMP_PHYS_PROXY_TERRAIN 204 +#define IDS_COMP_PHYS_INVISIBLE 205 +#define IDS_COMP_PHYS_WALKABLE 206 +#define IDS_COMP_GUI_SELECTMOUSEOVERANIM 207 +#define IDS_COMP_PHYS_CLIMBABLE 208 +#define IDS_COMP_PHYS_SWIMSURF 209 +#define IDS_COMP_PHYS_SWIM3D 210 +#define IDS_COMP_PHYS_PUPPET 211 +#define IDS_COMP_DYNMAT 211 +#define IDS_COMP_PHYS_CRITTER 212 +#define IDS_COMP_GUIDYNDISPLAY 213 +#define IDS_COMP_CLOTHING 214 +#define IDS_COMP_RAILCAMERA 215 +#define IDS_COMP_CIRCLECAMERA 216 +#define IDS_COMP_ALLOW_PAN 217 +#define IDS_COMP_DETECTOR_ANIM 218 +#define IDS_COMP_CAMERAZOOM 219 +#define IDS_AVATARPOA 220 +#define IDS_COMP_CAMERATRANS 221 +#define IDS_COMP_OBJECTPOA 222 +#define IDS_COMP_FIRST_PERSONCAM 223 +#define IDS_COMP_DETECTOR_MTL 224 +#define IDS_COMP_VEHICLE 225 +#define IDS_COMP_SHADOW_LIGHT 226 +#define IDS_COMP_SOUNDSRC 227 +#define IDS_COMP_RANDOMSOUNDS_GROUPS 228 +#define IDS_COMP_FOOTSTEP_SOUND 229 +#define IDS_COMP_EAXLISTENER 230 +#define IDS_COMP_EAXBUFFER 231 +#define IDS_SND_VOLUME 232 +#define IDS_COMP_FP_SOURCENODE 233 +#define IDS_COMP_RIPPLE 234 +#define IDS_COMP_FP_DIRTY 235 +#define IDS_COMP_FP_PRINTSHAPE 236 +#define IDS_COMP_PUDDLE 237 +#define IDS_COMP_SUBWORLD_REGION 238 +#define IDS_COMP_NPC_SPAWNER 239 +#define IDS_COMP_FP_ACTIVEPRINTSHAPE 240 +#define IDS_COMP_GUIMULTILINE 241 +#define IDS_COMP_FP_WAKE 242 +#define IDS_COMP_GUIPROXY 243 +#define IDS_COMP_GUIPROXY_PROMPT 244 +#define IDS_COMP_FILTER 245 +#define IDS_COMP_SOUNDBASE 246 +#define IDS_COMP_STEREIZE 247 +#define IDS_COMP_CLIMB_TRIGGER 248 +#define IDS_COMP_CLIMB_BLOCKER 249 +#define IDS_COMP_SWIVEL 250 +#define IDS_COMP_CHOOSE_OBJECT 251 +#define IDS_COMP_MORPHLAY 252 +#define IDS_COMP_MORPHSEQ 253 +#define IDS_COMP_CHOSE_OBJECT 254 +#define IDS_COMP_OBJECT_FOLLOWCAM 255 +#define IDS_COMP_EMOTE 256 +#define IDS_COMP_WATER 257 +#define IDS_COMP_W_REFOBJECT 258 +#define IDS_COMP_W_BASICWATER 259 +#define IDS_COMP_W_ADVWATER 260 +#define IDS_COMP_W_BASICSHORE 261 +#define IDS_COMP_W_ADVSHORE 262 +#define IDS_COMP_SHORE 263 +#define IDS_COMP_SHORE_CHOSE 264 +#define IDS_COMP_GUIPROGRESS 265 +#define IDS_COMP_W_ENVMAP 266 +#define IDS_COMP_MAINTAINERS_MARKER 267 +#define IDS_COMP_NOSHOW 268 +#define IDS_COMP_GUICLICKMAP 269 +#define IDS_COMP_VISREGION 270 +#define IDS_COMP_SMOOTHAV 271 +#define IDS_COMP_GUISKIN 272 +#define IDS_COMP_GUIMENUANCHOR 273 +#define IDS_COMP_WDECAL 274 +#define IDS_COMP_ANIM_CAM_CMD 275 +#define IDS_COMP_NETSYNC 276 +#define IDS_COMP_RELREGION 277 +#define IDS_COMP_REPRESENT 278 +#define IDS_COMP_REPGROUP 279 +#define IDS_COMP_PHYS_BRIDGE 280 +#define IDS_COMP_SMOOTHBASE 281 +#define IDS_COMP_SMOOTHSNAP 282 +#define IDS_COMP_IMAGELIB 283 +#define IDS_COMP_W_VTXHELP 284 +#define IDS_COMP_PHYS_SUBWORLD 285 +#define IDS_COMP_PARTICLE_FLOCK 286 +#define IDS_COMP_EFFVISSET 287 +#define IDS_COMP_ENVMAP 288 +#define IDS_COMP_W_GEOWATER 289 +#define IDS_COMP_W_TEXWATER 290 +#define IDS_COMP_SHOOTABLE 291 +#define IDS_COMP_FP_BULLET 292 +#define IDS_COMP_FP_TORPEDO 293 +#define IDS_COMP_PANIC 294 +#define IDS_COMP_SOUNDPHYS 295 +#define IDS_COMP_FORCE_CTT 296 +#define IDS_COMP_MARKER 297 +#define IDS_COMP_LOSFADE 298 +#define IDS_COMP_SORT_AS_OPAQUE 299 +#define IDD_COMP_ROOM 300 +#define IDS_SWIM_DETECTOR_NODE 300 +#define IDS_SWIM_CURRENT_NODE 301 +#define IDD_COMP_PHYS_DETECTOR 302 +#define IDS_COMP_OBJ_FLOCKER 302 +#define IDD_COMP_ANIM 303 +#define IDS_COMP_GRASS_ROLL 303 +#define IDS_COMP_ANIM_COMPRESS_ROLL 304 +#define IDS_COMP_RIDE_ANIMATED_PHYS 305 +#define IDD_COMP_ACTIVE 306 +#define IDD_COMP_RESPOND 307 +#define IDD_COMP_ALIAS 308 +#define IDD_COMP_STDPORTAL 312 +#define IDD_COMP_PLAYERSTART 313 +#define IDD_COMP_TARGS 314 +#define IDD_COMP_INTEREST 315 +#define IDD_COMP_AUTO 316 +#define IDD_COMP_PHYSICAL 317 +#define IDD_COMP_SOUND3D 318 +#define IDD_COMP_PHYS_TERRAIN 319 +#define IDD_COMP_PHYS_PLAYER 320 +#define IDD_COMP_CAMERA 321 +#define IDD_COMP_PHYS_SIMPLE 322 +#define IDD_COMP_ANIM_AVATAR 325 +#define IDD_COMP_SOUND_REVERB 326 +#define IDD_COMP_AVATAR 327 +#define IDD_COMP_LIGHTMAP 328 +#define IDD_COMP_IGNORE 329 +#define IDD_COMP_SOUNDMOD_COMPRESSOR 330 +#define IDD_COMP_SOUNDMOD_DISTORT 331 +#define IDD_COMP_SOUNDMOD_ECHO 332 +#define IDD_COMP_SOUNDMOD_FLANGER 333 +#define IDD_COMP_SOUNDMOD_GARGLE 334 +#define IDD_COMP_SOUNDMOD_REVERB 335 +#define IDD_COMP_SOUNDMOD_CHORUS 336 +#define IDD_COMP_OCCLUSION 337 +#define IDD_COMP_PHYS_PROXY_TERRAIN 338 +#define IDD_COMP_SOUNDMOD_BIND 339 +#define IDD_COMP_PARTICLE 340 +#define IDD_COMP_CAMERACMD 341 +#define IDD_COMP_PAGEINFO 342 +#define IDD_COMP_PARTICLE_VOLUME 343 +#define IDD_COMP_SMOOTH 344 +#define IDD_COMP_BARNEY 345 +#define IDD_COMP_LINEFOLLOW 346 +#define IDD_COMP_RESPOND_ANIM 347 +#define IDD_COMP_RESPOND_MTL 348 +#define IDD_COMP_RESPOND_LINK 349 +#define IDD_COMP_DETECTOR_CLICKABLE 350 +#define IDD_COMP_RANDOMSOUND 352 +#define IDD_COMP_DETECTOR_CLICK_DRAGGABLE 353 +#define IDD_COMP_ONESHOT 354 +#define IDD_COMP_PARTICLE_FADE 355 +#define IDD_COMP_SOUNDBGND 356 +#define IDD_COMP_FOLLOW 357 +#define IDD_COMP_LIGHTINC 358 +#define IDD_COMP_INV_OBJECT 359 +#define IDD_COMP_SOFTVOLUME 360 +#define IDD_COMP_SOUND_REVERB_PARAMS 362 +#define IDD_COMP_DETECTOR_REGION 363 +#define IDD_COMP_DETECTOR_COLLISION 365 +#define IDD_COMP_RESPOND_WAIT 367 +#define IDD_COMP_PHYS_WHEEL_CONSTRAINT 368 +#define IDD_COMP_PHYS_HINGE_CONSTRAINT 369 +#define IDD_COMP_PHYS_SS_CONSTRAINT 370 +#define IDD_COMP_LIGHTREGION 371 +#define IDD_COMP_SOFTVOLUME_UNION 372 +#define IDD_COMP_SOFTVOLUME_ISECT 373 +#define IDD_COMP_SOFTVOLUME_NEGATE 374 +#define IDD_COMP_PHYS_CRITTER 376 +#define IDD_COMP_RESPOND_ANIMPICK 377 +#define IDD_COMP_RESPOND_ENABLE 378 +#define IDD_COMP_RESPOND_ONESHOT 379 +#define IDD_COMP_SOUND_FADEPARAMS 380 +#define IDD_COMP_SOUND_SOFTPARAMS 381 +#define IDD_COMP_LOD_AVATAR 382 +#define IDD_COMP_CAMERARGN 383 +#define IDD_COMP_FIXEDCAM 384 +#define IDD_COMP_AUTOCAM 385 +#define IDD_COMP_CAMERA_TRANS 389 +#define IDD_COMP_PHYS_CLIMBABLE 390 +#define IDD_COMP_PHYS_SWIMSURF 391 +#define IDD_COMP_PHYS_SWIM3D 392 +#define IDD_COMP_PHYS_WALKABLE 393 +#define IDD_COMP_PHYS_PUPPET 394 +#define IDD_COMP_CAMERA_OCCLUDER 395 +#define IDD_COMP_GUIDIALOG 396 +#define IDD_COMP_PHYS_INVISIBLE 397 +#define IDD_COMP_AVATAR_POA 398 +#define IDD_COMP_OBJECT_POA 399 +#define IDD_COMP_CAM_MAKEDEFAULT 400 +#define IDD_COMP_PYTHON_FILE 401 +#define IDD_COMP_LOS_WIDGET 402 +#define IDD_COMP_GUIBUTTON 403 +#define IDD_COMP_DISTRIBUTOR 404 +#define IDD_COMP_GUIDRAGGABLE 405 +#define IDD_COMP_CLUSTER 406 +#define IDD_COMP_XREGION 407 +#define IDD_COMP_GUILISTBOX 409 +#define IDD_COMP_GUITEXTBOX 410 +#define IDD_COMP_ANIM_EASE 411 +#define IDD_COMP_IGNORELITE 412 +#define IDD_COMP_GUIEDITBOX 413 +#define IDD_COMP_FLEXIBILITY 414 +#define IDD_COMP_GUITAG 415 +#define IDD_COMP_CAMERAPAN 417 +#define IDD_COMP_BLOW 418 +#define IDD_PYTHON_FILE_WARN 419 +#define IDD_COMP_XFORM 420 +#define IDD_COMP_FIRSTPERSON_CAM 421 +#define IDD_COMP_BEHAVIOR_SITTING 422 +#define IDD_COMP_GUIUDSCROLL 423 +#define IDD_COMP_PHYS_CORE_GROUP 424 +#define IDD_COMP_NAV_LADDER 425 +#define IDD_COMP_RESPOND_CAMERA 426 +#define IDD_COMP_TEMPLATE 427 +#define IDD_COMP_GUIKNOB 428 +#define IDD_COMP_GUIDRAGBAR 429 +#define IDD_COMP_DIST_SPACING 430 +#define IDD_COMP_DIST_ORIENT 431 +#define IDD_COMP_DIST_ANGPROB 432 +#define IDD_COMP_DIST_ALTPROB 433 +#define IDD_COMP_DIST_CONFORM 434 +#define IDD_COMP_DIST_BITMAP 435 +#define IDD_COMP_DIST_RANDOMIZE 436 +#define IDD_COMP_DIST_FADE 437 +#define IDD_COMP_DIST_WIND 438 +#define IDD_COMP_DIST_TEMPLATES 439 +#define IDD_COMP_DIST_SCALE 440 +#define IDD_COMP_DIST_ACTION 441 +#define IDD_COMP_GUIRADIOGROUP 442 +#define IDD_COMP_GUIPROCROLLOUT 443 +#define IDD_COMP_LODFADE 444 +#define IDD_COMP_GUIDYNDISPLAY 445 +#define IDD_COMP_RESPOND_DELAY 446 +#define IDD_PICK_MTL 447 +#define IDD_COMP_CLOTHING 448 +#define IDD_COMP_RESPOND_CAMVIEW 449 +#define IDD_COMP_CAMERA_RAIL 450 +#define IDD_COMP_MULTIBEH_NORMAL 451 +#define IDD_COMP_MULTIBEH 452 +#define IDD_COMP_BLENDONTO 453 +#define IDD_COMP_GUISCHEME 454 +#define IDD_COMP_CAMERA_CIRCLE 455 +#define IDD_COMP_UNLEASH 456 +#define IDD_COMP_GEO_DICE 457 +#define IDD_COMP_RESPOND_VISIBILITY 458 +#define IDD_COMP_PARTICLE_WIND 459 +#define IDD_COMP_FORCE_RTLIGHT 460 +#define IDD_COMP_DETECTOR_ANIM 461 +#define IDD_COMP_CAMERAZOOM 462 +#define IDD_COMP_DETECTOR_MTL 463 +#define IDD_PICK_NODE 464 +#define IDD_COMP_BLENDONTOADV 465 +#define IDD_COMP_VEHICLE 466 +#define IDD_COMP_PARTICLE_UNIWIND 467 +#define IDD_COMP_DISTFADE 468 +#define IDD_COMP_SOUNDGUI 469 +#define IDD_COMP_SHADOW_CAST 473 +#define IDD_COMP_SHADOW_RCV 474 +#define IDD_COMP_SHADOW_LIGHT 475 +#define IDD_COMP_SOUNDSRC 476 +#define IDD_COMP_PHYS_SUBWORLD 478 +#define IDD_COMP_RANDOMSOUND_GROUPS 479 +#define IDD_COMP_FOOTSTEP_SOUND 480 +#define IDD_COMP_RESPOND_FOOT_SURFACE 481 +#define IDD_COMP_EAXLISTENER 482 +#define IDD_COMP_EAXBUFFER 483 +#define IDD_COMP_FP_FOOTPRINT 484 +#define IDD_COMP_FP_RIPPLE 485 +#define IDD_COMP_FP_DIRTY 486 +#define IDD_COMP_FP_PRINTSHAPE 487 +#define IDD_COMP_FP_PUDDLE 488 +#define IDD_COMP_SUBWORLD_REGION 489 +#define IDD_COMP_NPC_SPAWN 490 +#define IDD_COMP_GUIMULTILINE 491 +#define IDD_COMP_FP_ACTIVEPRINTSHAPE 494 +#define IDD_COMP_FP_WAKE 495 +#define IDD_COMP_GUIPROXY 496 +#define IDD_COMP_FILTERINHERIT 497 +#define IDD_COMP_SOUNDBASE 498 +#define IDD_COMP_STEREIZE 499 +#define IDD_COMP_CLIMB_TRIGGER 500 +#define IDD_COMP_CLIMB_BLOCK 501 +#define IDD_COMP_SWIVEL 502 +#define IDD_COMP_MORPHLAY 503 +#define IDD_COMP_MORPHSEQ 504 +#define IDD_COMP_OBJECT_FOLLOWCAM 505 +#define IDD_COMP_EMOTE 506 +#define IDD_COMP_W_GEOWATER 507 +#define IDD_COMP_W_ADVWATER 508 +#define IDD_COMP_W_BASICSHORE 509 +#define IDD_COMP_W_ADVSHORE 510 +#define IDD_COMP_W_REFOBJECT 511 +#define IDD_COMP_SHORE 512 +#define IDD_COMP_GUIPROGRESS 513 +#define IDD_COMP_W_ENVMAP 514 +#define IDD_COMP_MAINTAINERS_MARKER 515 +#define IDD_COMP_NOSHOW 516 +#define IDD_COMP_GUICLICKMAP 517 +#define IDD_COMP_VISREGION 518 +#define IDD_COMP_SMOOTHAV 519 +#define IDD_COMP_SKINEDIT 520 +#define IDI_ZOOMIN 520 +#define IDD_COMP_GUISKIN 521 +#define IDI_ZOOMOUT 521 +#define IDD_COMP_GUIMENUANCHOR 522 +#define IDD_COMP_WDECAL 523 +#define IDD_COMP_ANIMCAM_COMMANDS 524 +#define IDD_COMP_NETSYNC 525 +#define IDD_COMP_RELREGION 526 +#define IDD_COMP_REPRESENT 527 +#define IDD_COMP_REPGROUP 528 +#define IDD_COMP_PHYS_BRIDGE 529 +#define IDD_COMP_SMOOTHBASE 530 +#define IDD_COMP_SMOOTHSNAP 531 +#define IDD_COMP_IMAGELIB 532 +#define IDD_COMP_W_VTXHELP 533 +#define IDD_COMP_PARTICLE_FLOCK 534 +#define IDD_COMP_EFFVISSET 535 +#define IDD_COMP_ENVMAP 536 +#define IDD_COMP_W_TEXWATER 537 +#define IDD_COMP_SOUNDPHYS 538 +#define IDD_COMP_SHOOTABLE 539 +#define IDD_COMP_FP_BULLET 540 +#define IDD_COMP_FP_TORPEDO 541 +#define IDD_COMP_PANIC 542 +#define IDD_COMP_RESPOND_ENABLE_PHYS 543 +#define IDD_COMP_RESPOND_CAM_FORCE 544 +#define IDD_COMP_FORCE_CTT 545 +#define IDD_COMP_MARKER 546 +#define IDD_COMP_LOSFADE 547 +#define IDD_COMP_GZFADE 548 +#define IDD_COMP_DYNMAT 549 +#define IDD_COMP_SORT_AS_OPAQUE 550 +#define IDD_COMP_RESPOND_SUBWORLD 552 +#define IDD_PICK_LOCALIZATION 553 +#define IDD_COMP_OBJ_FLOCKER 555 +#define IDD_COMP_GRASS 556 +#define IDD_COMP_ANIM_COMPRESS 557 +#define IDD_COMP_RIDE_ANIMATED_PHYS 558 +#define IDC_LIST_TARGS 1011 +#define IDC_LIST_TARGS2 1012 +#define IDC_COMP_DISTRIB_PROBTEXMAP 1018 +#define IDC_ADD_TARGS 1027 +#define IDC_DELETE_TARGS 1028 +#define IDC_DET_COMBO 1031 +#define IDC_ONESHOT 1036 +#define IDC_ADD_CMD 1038 +#define IDC_REMOVE_CMD 1039 +#define IDC_ANIM_COMBO 1039 +#define IDC_CMD_LIST 1040 +#define IDC_COMP_LIGHTMAP_COLOR 1040 +#define IDC_COMP_ALIAS 1042 +#define IDC_ANIM_GREEN_COMBO 1042 +#define IDC_ANIM_OPEN_COMBO 1043 +#define IDC_PLAYERSTART 1046 +#define IDC_COMP_PORTAL_ROOM1 1047 +#define IDC_COMP_PORTAL_ROOM2 1048 +#define IDC_COMP_INTEREST_EDIT1 1051 +#define IDC_COMP_INTEREST_EDIT2 1052 +#define IDC_COMP_INTEREST_SPIN1 1053 +#define IDC_COMP_INTEREST_SPIN2 1054 +#define IDC_COMP_PHYSICAL_EDIT1 1054 +#define IDC_COMP_PHYSICAL_EDIT2 1055 +#define IDC_COMP_PHYSICAL_SPIN1 1056 +#define IDC_COMP_PHYSICAL_SPIN2 1057 +#define IDC_COMP_PHYSICAL_EDIT3 1058 +#define IDC_COMP_PHYSICAL_SPIN3 1059 +#define IDC_COMP_PHYSICAL_P3SF_EDIT1 1062 +#define IDC_COMP_PHYSICAL_P3SF_EDIT2 1063 +#define IDC_COMP_PHYSICAL_P3SF_SPIN1 1064 +#define IDC_COMP_PHYSICAL_P3SF_SPIN2 1065 +#define IDC_COMP_PHYSICAL_P3SF_EDIT3 1066 +#define IDC_COMP_PHYSICAL_P3SF_SPIN3 1067 +#define IDC_COMP_PHYSICAL_P3ST_EDIT1 1068 +#define IDC_COMP_NAV_LADDER_LOOPS_EDIT 1068 +#define IDC_COMP_PHYSICAL_P3ST_EDIT2 1069 +#define IDC_COMP_PHYSICAL_P3ST_SPIN1 1070 +#define IDC_COMP_NAV_LADDER_LOOPS_SPIN 1070 +#define IDC_COMP_PHYSICAL_P3ST_SPIN2 1071 +#define IDC_COMP_PHYSICAL_P3ST_SPIN3 1073 +#define IDC_RADIO_BBOX 1074 +#define IDC_RADIO_DOWN 1074 +#define IDC_RADIO_BSPHERE 1075 +#define IDC_RADIO_UP 1075 +#define IDC_RADIO_REPORT_DYN 1075 +#define IDC_RADIO_BHULL 1076 +#define IDC_RADIO_PICKSTATE 1077 +#define IDC_RADIO_BSPHERE2 1078 +#define IDC_COMP_PHYSICAL_P3ST_EDIT3 1079 +#define IDC_RADIO_REPORT_BOTH 1079 +#define IDC_RADIO_BLOCK_DYNAMICS 1079 +#define IDC_RADIO_BLOCK_DYNAMIC 1079 +#define IDC_RADIO_REPORT_AVATAR 1080 +#define IDC_COMP_PARTICLE_PPS 1084 +#define IDC_COMP_PARTICLE_PPS_SPIN 1085 +#define IDC_COMP_SOUND3D_FILENAME_BTN 1090 +#define IDC_GUI_BGALPHA 1091 +#define IDC_GUI_SFGALPHA 1092 +#define IDC_GUI_SBGALPHA 1093 +#define IDC_COMP_SOUND3D_EDIT3 1096 +#define IDC_COMP_SOUND3D_SPIN3 1097 +#define IDC_COMP_SOUND3D_EDIT4 1098 +#define IDC_COMP_SOUND3D_SPIN4 1099 +#define IDC_GUI_STEP 1100 +#define IDC_GUI_STEP_SPIN 1101 +#define IDC_COMP_PHYS_TERRAIN_EDIT1 1105 +#define IDC_COMP_PHYS_TERRAIN_SPIN1 1106 +#define IDC_COMP_PHYS_TERRAIN_EDIT2 1107 +#define IDC_COMP_ANIM_LOOP_CKBX 1108 +#define IDC_COMP_PHYS_TERRAIN_SPIN2 1108 +#define IDC_COMP_SOUND3D_LOOPCHKBOX 1108 +#define IDC_COMP_ANIM_AUTOSTART_CKBX 1109 +#define IDC_COMP_ANIM_USE_GLOBAL 1110 +#define IDC_COMP_SOUND3D_AUTOSTART_CKBX 1111 +#define IDC_COMP_ANIM_PHYSANIM 1111 +#define IDC_COMP_PHYS_AGILITY_STATUS 1112 +#define IDC_COMP_SOUND3D_CONEEFFECT_CKBX 1112 +#define IDC_COMP_PHYS_WEIGHT_SPIN1 1113 +#define IDC_COMP_PHYS_ACCEL_STATUS 1113 +#define IDC_COMP_PHYS_TR_STATUS 1114 +#define IDC_COMP_PHYS_AGILITY_SLIDER 1115 +#define IDC_COMP_PHYS_WEIGHT_SLIDER 1116 +#define IDC_RADIO_FIXEDCAM 1116 +#define IDC_COMP_PHYS_ACCEL_SLIDER 1116 +#define IDC_RADIO_FIXEDPANCAM 1117 +#define IDC_COMP_PHYS_SIMP_EDIT1 1117 +#define IDC_COMP_PHYS_SIMP_SPIN1 1118 +#define IDC_COMP_PHYS_CRIT_MVEL_SPIN 1119 +#define IDC_COMP_PHYS_CRIT_MVEL_EDIT 1120 +#define IDC_COMP_PHYS_SIMP_EDIT2 1121 +#define IDC_COMP_PHYS_SIMP_EDIT3 1122 +#define IDC_COMP_PHYS_SIMP_SPIN2 1123 +#define IDC_COMP_PHYS_PICKSTATE_BASE 1123 +#define IDC_COMP_PHYS_SIMP_SPIN3 1124 +#define IDC_COMP_PHYS_PICKSTATE_SIMP 1125 +#define IDC_COMP_PHYS_PICKSTATE_TERRAIN 1125 +#define IDC_COMP_PHYS_TR_SLIDER 1125 +#define IDC_COMP_PHYS_PICKSTATE_PLAYER 1126 +#define IDC_COMP_ROOM_AGE_TEXTBOX 1129 +#define IDC_COMP_ROOM_DISTRICT_TEXTBOX 1130 +#define IDC_COMP_ROOM_ROOM_TEXTBOX 1131 +#define IDC_ANIM_NAMES 1142 +#define IDC_SOUND_FILENAME_TEXT 1143 +#define IDC_LOOP_NAMES 1145 +#define IDC_ANIM_GLOBAL_LIST 1146 +#define IDC_ADD_ACTIVATOR 1149 +#define IDC_DEL_TARGS 1151 +#define IDC_COMP_SOUND_REVERB_BBOX 1152 +#define IDC_DEL_TARGS2 1152 +#define IDC_COMP_SOUND_REVERB_BSPHERE 1153 +#define IDC_COMP_SOUND_REVERB_BHULL 1154 +#define IDC_COMP_SOUND_REVERB_DDLIST 1155 +#define IDC_COMP_AVATAR_PROXY_PICKH 1156 +#define IDC_COMP_AVATAR_PROXY_PICKT 1157 +#define IDC_COMP_DISTRIB_PROBCOLORCHAN 1162 +#define IDC_COMP_AVATAR_PROXY_PICKB 1163 +#define IDC_COMP_LIGHTMAP_EDIT1 1163 +#define IDC_COMP_LOD_AVATAR_ROOT_PICKB 1164 +#define IDC_COMP_LIGHTMAP_SPIN1 1165 +#define IDC_COMP_LOD_AVATAR_MESH_PICKB 1165 +#define IDC_COMP_IGNORE_CKBX 1167 +#define IDC_COMP_PHYS_PICKSTATE_DETECTOR 1168 +#define IDC_COMP_AVATAR_PROXY_PICKH2 1170 +#define IDC_COMP_AVATAR_PROXY_PICKT2 1171 +#define IDC_COMP_LM_DUMMY 1172 +#define IDC_COMP_LIGHT_SLIDER 1173 +#define IDC_COMP_SOUND3D_VOLSLIDER 1175 +#define IDC_COMP_SOUND3D_SLIDERVIEWER 1176 +#define IDC_COMP_SOUND_REV_ROOMHFATTEN 1176 +#define IDC_RESPONDERS 1177 +#define IDC_COMP_SOUND3D_VOLSLIDER2 1177 +#define IDC_COMP_SOUND_REV_ROOMROLLOFF 1177 +#define IDC_COMP_SOUND3D_SLIDERVIEWER2 1178 +#define IDC_COMP_SOUND_REV_DECAYRATIO 1178 +#define IDC_COMP_SOUND3D_ICONEANGLE_EDIT 1179 +#define IDC_COMP_SOUND_REV_REFLECTDELAY 1179 +#define IDC_COMP_SOUND3D_ICONEANGLE_SPIN 1180 +#define IDC_COMP_SOUND_REV_REVERBDELAY 1180 +#define IDC_COMP_SOUND3D_OCONEANGLE_EDIT 1181 +#define IDC_COMP_SOUND_REV_MODALDENSITY 1181 +#define IDC_COMP_SOUND3D_OCONEANGLE_SPIN 1182 +#define IDC_COMP_SOUND_REV_REFHF 1182 +#define IDC_COMP_SOUNDMOD_FEEDBACK_SPIN 1183 +#define IDC_COMP_SOUND_REV_ROOMATTEN_EDIT 1183 +#define IDC_COMP_SOUNDMOD_FEEDBACK_EDIT 1184 +#define IDC_COMP_SOUND_REV_ROOMHFATTEN_EDIT 1184 +#define IDC_COMP_SOUNDMOD_LOWFREQ_OSC_CUTOFF_EDIT 1185 +#define IDC_COMP_SOUND_REV_ROOMROLLOFF_EDIT 1185 +#define IDC_COMP_SOUNDMOD_LOWFREQ_OSC_CUTOFF_SPIN 1186 +#define IDC_COMP_SOUND_REV_DECAYTIME_EDIT 1186 +#define IDC_CLOTHING_GROUP 1186 +#define IDC_COMP_SOUNDMOD_FEEDBACK_DELAY_EDIT 1187 +#define IDC_COMP_SOUND_REV_DECAYRATIO_EDIT 1187 +#define IDC_CLOTHING_TYPE 1187 +#define IDC_COMP_SOUNDMOD_FEEDBACK_DELAY_SPIN 1188 +#define IDC_COMP_SOUND_REV_REFLECTATTEN_EDIT 1188 +#define IDC_COMP_SOUNDMOD_WET_EDIT 1189 +#define IDC_COMP_SOUND_REV_REFLECTDELAY_EDIT 1189 +#define IDC_COMP_SOUNDMOD_WET_SPIN 1190 +#define IDC_COMP_SOUND_REV_REVERBATTEN_EDIT 1190 +#define IDC_COMP_SOUNDMOD_DELAYMOD_EDIT 1191 +#define IDC_COMP_SOUND_REV_REVERBDELAY_EDIT 1191 +#define IDC_COMP_SOUNDMOD_DELAYMOD_SPIN 1192 +#define IDC_COMP_SOUND_REV_ECHODENSITY_EDIT 1192 +#define IDC_COMP_SOUNDMOD_LOWFREQ_DDLIST 1193 +#define IDC_COMP_SOUND_REV_MODALDENSITY_EDIT 1193 +#define IDC_COMP_SOUNDMOD_GAIN_EDIT 1194 +#define IDC_COMP_SOUND_REV_REFHF_EDIT 1194 +#define IDC_COMP_SOUNDMOD_GAIN_SPIN 1195 +#define IDC_COMP_SOUNDMOD_ATTACK_EDIT 1196 +#define IDC_COMP_SOUNDMOD_ATTACK_SPIN 1197 +#define IDC_COMP_SOUNDMOD_RELEASE_EDIT 1198 +#define IDC_COMP_SOUNDMOD_RELEASE_SPIN 1199 +#define IDC_COMP_SOUNDMOD_COMPTHRESH_EDIT 1200 +#define IDC_COMP_SOUNDMOD_COMPTHRESH_SPIN 1201 +#define IDC_COMP_SOUNDMOD_COMPRATIO_EDIT 1202 +#define IDC_COMP_SOUNDMOD_COMPRATIO_SPIN 1203 +#define IDC_COMP_SOUNDMOD_ATTACKPRE_EDIT 1204 +#define IDC_COMP_SOUNDMOD_GAIND_EDIT 1204 +#define IDC_COMP_SOUNDMOD_ATTACKPRE_SPIN 1205 +#define IDC_COMP_SOUNDMOD_GAIND_SPIN 1205 +#define IDC_COMP_SOUNDMOD_INTENSE_EDIT 1206 +#define IDC_COMP_SOUNDMOD_INTENSE_SPIN 1207 +#define IDC_COMP_SOUNDMOD_EFFECTC_EDIT 1208 +#define IDC_COMP_SOUNDMOD_EFFECTC_SPIN 1209 +#define IDC_COMP_SOUNDMOD_EFFECTW_EDIT 1210 +#define IDC_COMP_SOUNDMOD_EFFECTW_SPIN 1211 +#define IDC_COMP_SOUNDMOD_LOWFREQCUT_EDIT 1212 +#define IDC_COMP_SOUNDMOD_LOWFREQCUT_SPIN 1213 +#define IDC_COMP_SOUNDMOD_SWAP_DELAY 1214 +#define IDC_COMP_SOUNDMOD_LEFTFEEDB_EDIT 1215 +#define IDC_COMP_SOUNDMOD_LEFTFEEDB_SPIN 1216 +#define IDC_COMP_SOUNDMOD_RIGHTFEEDB_EDIT 1217 +#define IDC_COMP_SOUNDMOD_RIGHTFEEDB_SPIN 1218 +#define IDC_COMP_SOUNDMOD_FEEDBACK_DELAYF_EDIT 1219 +#define IDC_COMP_SOUNDMOD_FEEDBACK_DELAYF_SPIN 1220 +#define IDC_COMP_SOUNDMOD_MODRATE_EDIT 1221 +#define IDC_COMP_SOUNDMOD_MODRATE_SPIN 1222 +#define IDC_COMP_SOUNDMOD_WAVESHAPE_DDLIST 1223 +#define IDC_COMP_SOUNDMOD_REVERBGAIN_EDIT 1224 +#define IDC_COMP_SOUNDMOD_REVERBGAIN_SPIN 1225 +#define IDC_COMP_SOUNDMOD_REVERBMIX_EDIT 1226 +#define IDC_COMP_SOUNDMOD_REVERBMIX_SPIN 1227 +#define IDC_COMP_SOUNDMOD_REVERBDELAY_EDIT 1228 +#define IDC_COMP_SOUNDMOD_REVERBDELAY_SPIN 1229 +#define IDC_COMP_SOUNDMOD_PHASEDIF_DDLIST 1231 +#define IDC_COMP_OCCLUSION_CKBX 1232 +#define IDC_COMP_PHYS_CUSTOMCHK 1234 +#define IDC_ADD_SOUNDOBJ 1235 +#define IDC_COMP_PARTICLE_NODIE 1235 +#define IDC_COMP_CHECK_LOS 1236 +#define IDC_COMP_PHYS_ALIGN_SHAPE_BOOL 1237 +#define IDC_COMP_CHECK_LOS2 1237 +#define IDC_COMP_CHECK_LOS3 1238 +#define IDC_LOOP_COMBO 1239 +#define IDC_STATIC_VOLANGLE 1251 +#define IDC_STATIC_FADEANGLE 1252 +#define IDC_STATIC_SLIDERANGLE 1253 +#define IDC_STATIC_SLIDER_START 1254 +#define IDC_STATIC_SLIDER_END 1255 +#define IDC_COMBO1 1256 +#define IDC_CAMERACMD_OFFSETX 1258 +#define IDC_CAMERACMD_SPIN_OFFSETX 1259 +#define IDC_CAMERACMD_OFFSETY 1260 +#define IDC_PHYS_WIDTH_EDIT 1260 +#define IDC_CAMERACMD_SPIN_OFFSETY 1261 +#define IDC_PHYS_WIDTH_SPIN 1261 +#define IDC_CAMERACMD_OFFSETZ 1262 +#define IDC_CAMERACMD_OFFSETY2 1262 +#define IDC_CAMERACMD_SPIN_OFFSETZ 1263 +#define IDC_CAMERACMD_SPIN_OFFSETY2 1263 +#define IDC_CAMERACMD_OFFSETX2 1264 +#define IDC_CAMERACMD_SPIN_OFFSETX2 1265 +#define IDC_COMP_CAMERACMD_PICKSTATE_BASE 1266 +#define IDC_CAMERACMD_OFFSETX3 1266 +#define IDC_COMP_LOCATION_AGECOMBO 1267 +#define IDC_CAMERACMD_SPIN_OFFSETX3 1267 +#define IDC_COMP_LOCATION_PAGECOMBO 1268 +#define IDC_CAMERACMD_OFFSETX5 1268 +#define IDC_CAMERACMD_SPIN_OFFSETX5 1269 +#define IDC_CAMERACMD_OFFSETY4 1270 +#define IDC_CAMERACMD_SPIN_OFFSETY4 1271 +#define IDC_CAMERACMD_OFFSETZ2 1272 +#define IDC_CAMERACMD_SPIN_OFFSETZ2 1273 +#define IDC_FLOCK_CONFORM_DIST 1274 +#define IDC_RADIO_LISTENER 1275 +#define IDC_FLOCK_CONFORM_DIST_SPIN 1275 +#define IDC_RADIO_CAMERA 1276 +#define IDC_RADIO_OBJECT 1277 +#define IDC_COMP_LINE_CHOOSE_OBJECT 1278 +#define IDC_FLOCK_CONFORM_STR 1278 +#define IDC_COMP_LINE_CHOOSE_PATH 1279 +#define IDC_FLOCK_CONFORM_STR_SPIN 1279 +#define IDC_LINKSPAWNPOINT 1280 +#define IDC_FLOCK_REPEL_DIST 1280 +#define IDC_LINKAGEFILENAME 1281 +#define IDC_FLOCK_REPEL_DIST_SPIN 1281 +#define IDC_LINKINGRULE 1282 +#define IDC_FLOCK_REPEL_STR 1282 +#define IDC_LOOP_TEXT 1283 +#define IDC_LINKSPAWNPOINTTITLE 1283 +#define IDC_FLOCK_REPEL_STR_SPIN 1283 +#define IDC_LINKAGEINSTANCENAME 1284 +#define IDC_FLOCK_GOAL_DIST 1284 +#define IDC_FLOCK_GOAL_DIST_SPIN 1285 +#define IDC_LINKAGELINKINANIMNAME 1285 +#define IDC_COMP_CLICK_USEPROXY 1286 +#define IDC_FLOCK_GOAL_STR 1286 +#define IDC_PARENTAGEFILENAME 1286 +#define IDC_COMP_CLICK_PROXY 1287 +#define IDC_FLOCK_GOAL_STR_SPIN 1287 +#define IDC_LINKAGELINKINANIMNAME2 1287 +#define IDC_LINKAGEINSTANCEGUID 1287 +#define IDC_FLOCK_MAX_SPEED 1288 +#define IDC_COMP_CLICK_PROXYREGION 1289 +#define IDC_FLOCK_MAX_SPEED_SPIN 1289 +#define IDC_COMP_CLICK_DEG 1290 +#define IDC_FLOCK_GOAL_CHASE_STR 1290 +#define IDC_COMP_CLICK_DEGSPIN 1291 +#define IDC_FLOCK_GOAL_CHASE_STR_SPIN 1291 +#define IDC_FLOCK_FULL_CHASE_DIST 1292 +#define IDC_COMP_RS_AUTOSTART 1293 +#define IDC_FLOCK_FULL_CHASE_DIST_SPIN 1293 +#define IDC_COMP_CLICK_OMNI 1294 +#define IDC_RADIO_RS_NORMAL 1294 +#define IDC_FLOCK_MAX_ORBIT_SPEED 1294 +#define IDC_RADIO_RS_NOREP 1295 +#define IDC_FLOCK_MAX_ORBIT_SPEED_SPIN 1295 +#define IDC_RADIO_RS_FSREP 1296 +#define IDC_RADIO_RS_FSSTOP 1297 +#define IDC_RADIO_RS_SEQ 1298 +#define IDC_COMP_RS_DELAYMIN 1299 +#define IDC_COMP_RS_DELAYMIN_SPIN 1300 +#define IDC_COMP_RS_DELAYMAX 1301 +#define IDC_COMP_CLICK_DRAG_USEX 1301 +#define IDC_COMP_RS_DELAYMAX_SPIN 1302 +#define IDC_COMP_CLICK_DRAG_USE_LOOPX 1302 +#define IDC_COMP_CLICK_DRAG_ANIMX 1303 +#define IDC_COMP_CLICK_DRAG_OMNI 1305 +#define IDC_COMP_CLICK_DRAG_TEXT1 1306 +#define IDC_COMP_CLICK_DRAG_DEG 1307 +#define IDC_RADIO_RS_DELAYSTART 1308 +#define IDC_COMP_CLICK_DRAG_DEGSPIN 1308 +#define IDC_RADIO_RS_DELAYEND 1309 +#define IDC_COMP_CLICK_DRAG_USEPROXYPHYS 1309 +#define IDC_RADIO_RS_DELAYNEVER 1310 +#define IDC_COMP_CLICK_DRAG_PROXY 1310 +#define IDC_COMP_CLICK_DRAG_ALLORNOT 1311 +#define IDC_COMP_CLICK_DRAG_PROXYREGION 1312 +#define IDC_COMP_AVATAR_FRICTION_SPIN 1313 +#define IDC_COMP_CLICK_DRAG_USEYANIM 1313 +#define IDC_COMP_CLICK_DRAG_ANIM_Y 1314 +#define IDC_ENABLE 1315 +#define IDC_COMP_CLICK_DRAG_USE_LOOPY 1316 +#define IDC_COMP_AVATAR_FRICTION_EDIT 1322 +#define IDC_COMP_ONESHOT_ANIM_TEXTBOX 1325 +#define IDC_COMP_ONESHOT_PLAY_BACK_BOOL 1326 +#define IDC_COMP_ONESHOT_CONT_SPEED_BOOL 1327 +#define IDC_SMART_SEEK 1328 +#define IDC_SIT_APP_LEFT 1329 +#define IDC_NO_SEEK 1329 +#define IDC_COMP_ONESHOT_SEEK_FIELD_EDIT 1330 +#define IDC_SIT_APP_RIGHT 1330 +#define IDC_COMP_ONESHOT_SEEK_FIELD_SPIN 1331 +#define IDC_SIT_NOFORWARD 1331 +#define IDC_COMP_PARTICLE_CONE 1332 +#define IDC_COMP_PARTICLE_CONE_SPIN 1333 +#define IDC_COMP_PARTICLE_VELMIN 1334 +#define IDC_COMP_PARTICLE_VELMIN_SPIN 1335 +#define IDC_COMP_PARTICLE_VELMAX 1336 +#define IDC_COMP_PARTICLE_VELMAX_SPIN 1337 +#define IDC_COMP_PARTICLE_LIFEMIN 1338 +#define IDC_COMP_PARTICLE_LIFEMIN_SPIN 1339 +#define IDC_COMP_PARTICLE_LIFEMAX 1340 +#define IDC_COMP_PARTICLE_LIFEMAX_SPIN 1341 +#define IDC_COMP_PARTICLE_SCALEMIN 1342 +#define IDC_COMP_PARTICLE_SCALEMIN_SPIN 1343 +#define IDC_COMP_PARTICLE_SCALEMAX 1344 +#define IDC_COMP_PARTICLE_SCALEMAX_SPIN 1345 +#define IDC_GEN_TYPE 1346 +#define IDC_COMP_PARTICLE_GRAVITY 1347 +#define IDC_COMP_PARTICLE_GRAVITY_SPIN 1348 +#define IDC_COMP_PARTICLE_DRAG 1349 +#define IDC_COMP_PARTICLE_DRAG_SPIN 1350 +#define IDC_COMP_PARTICLE_VOLUME_NODE 1351 +#define IDC_COMP_PARTICLE_PRESIM 1351 +#define IDC_COMP_PARTICLE_FADEZ 1352 +#define IDC_COMP_PARTICLE_PRESIM_SPIN 1352 +#define IDC_COMP_PARTICLE_FADE_DIST 1353 +#define IDC_COMP_PARTICLE_WIND_MULT 1353 +#define IDC_COMP_PARTICLE_FADE_DIST_SPIN 1354 +#define IDC_COMP_PARTICLE_WIND_MULT_SPIN 1354 +#define IDC_COMP_CAMERACMD_CUT 1355 +#define IDC_COMP_PARTICLE_MASS_RANGE 1355 +#define IDC_COMP_CAMERACMD_WORLDSPACE 1356 +#define IDC_COMP_PARTICLE_MASS_RANGE_SPIN 1356 +#define IDC_COMP_PARTICLE_ROT_RANGE 1357 +#define IDC_COMP_ANIM_AVATAR_SHAREBOOL 1358 +#define IDC_COMP_PARTICLE_ROT_RANGE_SPIN 1358 +#define IDC_COMP_ANIM_AVATAR_GLOBALBOOL 1359 +#define IDC_COMP_FOLLOW_CHOOSE_OBJECT 1360 +#define IDC_F_RADIO_PLAYER 1361 +#define IDC_F_RADIO_CAMERA 1363 +#define IDC_F_RADIO_LISTENER 1364 +#define IDC_F_RADIO_OBJECT 1365 +#define IDC_COMP_LIGHTINC_CHARS 1365 +#define IDC_COMP_FOLLOW_X 1366 +#define IDC_COMP_LIGHTINC_FILTER 1366 +#define IDC_COMP_FOLLOW_Y 1367 +#define IDC_COMP_INV_OBJECT_ITINERANTBOOL 1367 +#define IDC_COMP_FOLLOW_Z 1368 +#define IDC_COMP_INV_OBJECT_RESPAWNBOOL 1368 +#define IDC_COMP_FOLLOW_ROTATE 1369 +#define IDC_COMP_INV_OBJECT_CONSUMABLE 1369 +#define IDC_COMP_FILTER_ACTIVE 1369 +#define IDC_COMP_INV_OBJECT_LIFE_EDIT 1370 +#define IDC_COMP_INV_OBJECT_LIFE_SPIN 1371 +#define IDC_COMP_SOFTVOL_SOFT 1372 +#define IDC_COMP_SOFTVOL_SOFT_SPIN 1373 +#define IDC_COMP_DISTRIB_RNDPOSRADIUS 1374 +#define IDC_COMP_SOFTVOL_INSIDE 1374 +#define IDC_CODE 1375 +#define IDC_COMP_DISTRIB_RNDPOSRADIUS_SPIN 1375 +#define IDC_COMP_SOFTVOL_INSIDE_SPIN 1375 +#define IDC_BUTTON1 1376 +#define IDC_COMP_SOFTVOL_OUTSIDE 1376 +#define IDC_COMP_SOUND_REV_ROOMATTEN 1377 +#define IDC_COMP_SOFTVOL_OUTSIDE_SPIN 1377 +#define IDC_GUI_ZOUT 1377 +#define IDC_COMP_SOUND_REV_DECAYTIME 1378 +#define IDC_COMP_SOUND_REV_REFLECTATTEN 1379 +#define IDC_COMP_PHYSGADGET_ENTERBOX 1380 +#define IDC_CHECK_WAIT 1381 +#define IDC_COMP_PHYS_SUSPENSION_BUTTON 1382 +#define IDC_COMP_SOUND_REV_REVERBATTEN 1383 +#define IDC_COMP_PHYSGADGET_EXITBOX 1384 +#define IDC_COMP_PHYS_ROTATION_BUTTON 1385 +#define IDC_RADIO_FINISH 1386 +#define IDC_COMP_SOUND_REV_ECHODENSITY 1387 +#define IDC_RADIO_POINT 1388 +#define IDC_WAIT_WHO 1390 +#define IDC_WAIT_POINT 1391 +#define IDC_COMP_TEXT 1392 +#define IDC_COMP_PHYS_STEERING_BUTTON 1393 +#define IDC_OBJ_TEXT 1393 +#define IDC_COMP_PHYS_POSITION_BUTTON 1394 +#define IDC_COMP_PHYS_PARENT 1395 +#define IDC_COMP_PHYS_HINGE_AXIS_RADIO 1396 +#define IDC_COMP_PHYS_HINGE_AXIS_RADIO2 1397 +#define IDC_COMP_PHYS_HINGE_AXIS_RADIO3 1398 +#define IDC_COMP_PHY_SS_LENGTH_EDIT 1399 +#define IDC_COMP_PHY_SS_LENGTH_SPIN 1400 +#define IDC_COMP_PHYS_SS_REBOUND_EDIT 1401 +#define IDC_COMP_PHYS_SS_REBOUND_SPIN 1402 +#define IDC_COMP_PHYS_SS_STRENGTH_EDIT 1403 +#define IDC_COMP_PHYS_SS_STRENGTH_SPIN 1404 +#define IDC_COMP_PHYS_PINNED_STATE_BOOL 1405 +#define IDC_STATE_COMBO 1406 +#define IDC_COMP_PHYS_USE_PARENT_BOOL 1406 +#define IDC_COMP_LIGHTREGION_CHOOSE_VOLUME 1407 +#define IDC_COMP_PHYS_PINNED_STATE_BOOL2 1407 +#define IDC_ADD_VOLUME 1408 +#define IDC_SWITCH_COMBO 1408 +#define IDC_COMP_SOFTVOLUME_NEGATE_CHOOSE_SUB 1409 +#define IDC_UP_REPS 1409 +#define IDC_COMP_PHYS_FRICTION_SLIDER 1410 +#define IDC_DOWN_REPS 1410 +#define IDC_COMP_PHYS_FRICTION_SPIN1 1411 +#define IDC_VAL_REPS 1411 +#define IDC_COMP_PHYS_OPENANGLE_SPIN 1412 +#define IDC_COMP_PHYS_OPENANGLE_EDIT 1413 +#define IDC_COMP_PHYS_CLOSEANGLE_SPIN 1414 +#define IDC_COMP_PHYS_CLOSEANGLE_EDIT 1415 +#define IDC_COMP_PHYS_STRENGTH_SPIN 1416 +#define IDC_STIFFNESS_SPIN 1416 +#define IDC_COMP_PHYS_STRENGTH_SLIDER 1417 +#define IDC_MTL_BUTTON 1417 +#define IDC_STRENGTH_SPIN 1417 +#define IDC_COMP_PHYS_TAU_SPIN 1418 +#define IDC_OBJ_LIST 1418 +#define IDC_COMP_PHYS_PICKHULL_CRIT 1418 +#define IDC_NODE_BUTTON 1418 +#define IDC_BOUNCE_BUTTON 1418 +#define IDC_COMP_PHYS_TAU_SLIDER 1419 +#define IDC_COMP_LIST 1419 +#define IDC_COMP_PHYS_PUPPET_ROOT_PICKB 1419 +#define IDC_MTL_NODE_BUTTON 1419 +#define IDC_ANIM_BUTTON 1420 +#define IDC_COMP_PHYS_CRIT_ANIM_TXT 1420 +#define IDC_PLACE_BUTTON 1420 +#define IDC_ENABLE_CHECK 1421 +#define IDC_OBJ_BUTTON 1421 +#define IDC_HIT_BUTTON 1421 +#define IDC_RESPONDER_BUTTON 1422 +#define IDC_ENABLED 1423 +#define IDC_RESPONDER_NODE 1423 +#define IDC_WAIT_ON_ME_CHECK 1424 +#define IDC_SOUND3D_INENABLE 1425 +#define IDC_SOUND3D_INLENGTH 1426 +#define IDC_SOUND3D_INLENGTHSPIN 1427 +#define IDC_SOUND3D_INTYPE 1428 +#define IDC_SOUND3D_OUTENABLE 1431 +#define IDC_SOUND3D_OUTTYPE 1432 +#define IDC_SOUND3D_OUTLENGTH 1433 +#define IDC_SOUND3D_OUTLENGTHSPIN 1434 +#define IDC_COMP_SOUNDREGION_CHOOSE_VOLUME 1435 +#define IDC_SOUND_SOFTENABLE 1436 +#define IDC_MARKER_EDIT 1437 +#define IDC_COMP_LOD_AVATAR_STATE 1438 +#define IDC_COMP_AVATAR_ROOT_PICKB 1447 +#define IDC_COMP_AVATAR_MESH_PICKB 1448 +#define IDC_COMP_CAMERARGN_PICKSTATE_BASE 1450 +#define IDC_COMP_AUTOCAM_LOS 1451 +#define IDC_COMP_AUTOCAM_POA_WORLDSPACE 1452 +#define IDC_COMP_POA_WORLDSPACE 1452 +#define IDC_COMP_OBJECTPOA_WORLDSPACE 1452 +#define IDC_COMP_AUTOCAM_LOS2 1453 +#define IDC_COMP_POS_WORLDSPACE 1453 +#define IDC_COMP_AUTOCAM_POS_WORLDSPACE 1454 +#define IDC_COMP_NAV_LADDER_COMBO 1455 +#define IDC_COMP_AUTOCAM_LOS3 1455 +#define IDC_COMP_AUTOCAM_VERTICAL 1456 +#define IDC_COMP_AUTOCAM_RUN 1457 +#define IDC_COMP_PHYS_INVIS_BOUNCE_EDIT 1461 +#define IDC_COMP_PHYS_INVIS_BOUNCE_SPIN 1462 +#define IDC_COMP_ANIM_EASE_IN_NONE 1463 +#define IDC_GUI_CONRADIO 1463 +#define IDC_COMP_ANIM_EASE_IN_CONST_ACCEL 1464 +#define IDC_COMP_ANIM_EASE_IN_SPLINE 1465 +#define IDC_COMP_PHYS_PUPPET_ANIM_TXT 1465 +#define IDC_GUI_INHERITRADIO 1465 +#define IDC_COMP_ANIM_EASE_IN_TIME 1466 +#define IDC_GUI_CLOSERADIO 1466 +#define IDC_COMP_ANIM_EASE_IN_TIME_SPIN 1467 +#define IDC_GUI_NILRADIO 1467 +#define IDC_COMP_ANIM_EASE_OUT_NONE 1468 +#define IDC_COMP_ANIM_EASE_OUT_CONST_ACCEL 1469 +#define IDC_COMP_ANIM_EASE_OUT_SPLINE 1470 +#define IDC_COMP_ANIM_EASE_OUT_TIME 1471 +#define IDC_COMP_ANIM_EASE_OUT_TIME_SPIN 1472 +#define IDC_GUIDLG_NAME 1473 +#define IDC_COMP_ANIM_EASE_IN_MIN 1473 +#define IDC_GUI_CONCMD 1474 +#define IDC_COMP_ANIM_EASE_IN_MIN_SPIN 1474 +#define IDC_GUIDLG_AGE 1474 +#define IDC_PYTHON_FILE 1475 +#define IDC_COMP_ANIM_EASE_IN_MAX 1475 +#define IDC_GUI_BGCOLOR 1475 +#define IDC_CHECK1 1476 +#define IDC_COMP_ANIM_EASE_IN_MAX_SPIN 1476 +#define IDC_GUI_SFGCOLOR 1476 +#define IDC_CHECK2 1477 +#define IDC_COMP_ANIM_EASE_OUT_MIN 1477 +#define IDC_GUI_SBGCOLOR 1477 +#define IDC_COMP_LOS_WIDGET_CB 1478 +#define IDC_COMP_ANIM_EASE_OUT_MIN_SPIN 1478 +#define IDC_CHECK3 1478 +#define IDC_COMP_LOS_WIDGET_LB 1479 +#define IDC_COMP_ANIM_EASE_OUT_MAX 1479 +#define IDC_CHECK4 1479 +#define IDC_COMP_LOS_WIDGET_REM_BTN 1480 +#define IDC_COMP_ANIM_EASE_OUT_MAX_SPIN 1480 +#define IDC_COMP_SOUND_DISABLELOD 1482 +#define IDC_COMP_DISTRIB_SPACING 1483 +#define IDC_COMP_DISTRIB_SPACING_SPIN 1484 +#define IDC_COMP_DISTRIB_ALIGNVECX 1485 +#define IDC_COMP_DISTRIB_ALIGNVECX_SPIN 1486 +#define IDC_COMP_DISTRIB_ALIGNVECY 1487 +#define IDC_COMP_DISTRIB_ALIGNVECY_SPIN 1488 +#define IDC_COMP_DISTRIB_ALIGNVECZ 1489 +#define IDC_COMP_DISTRIB_ALIGNVECZ_SPIN 1490 +#define IDC_COMP_DISTRIB_POLARRANGE 1491 +#define IDC_COMP_DISTRIB_POLARRANGE_SPIN 1492 +#define IDC_COMP_DISTRIB_AZIMUTHRANGE 1493 +#define IDC_COMP_DISTRIB_AZIMUTHRANGE_SPIN 1494 +#define IDC_COMP_DISTRIB_ANGPROBX 1495 +#define IDC_ADD_SAFE 1496 +#define IDC_COMP_DISTRIB_ANGPROBX_SPIN 1496 +#define IDC_COMP_DISTRIB_OVERALLPROB 1497 +#define IDC_DEL_SAFE 1497 +#define IDC_COMP_DISTRIB_OVERALLPROB_SPIN 1498 +#define IDC_LIST_SAFE 1498 +#define IDC_COMP_DISTRIB_POLARBUNCH 1499 +#define IDC_COMP_DISTRIB_POLARBUNCH_SPIN 1500 +#define IDC_COMP_DISTRIB_SCALELOX 1501 +#define IDC_COMP_DISTRIB_SCALELOX_SPIN 1502 +#define IDC_COMP_DISTRIB_SCALEHIX 1503 +#define IDC_COMP_DISTRIB_SCALEHIX_SPIN 1504 +#define IDC_COMP_DISTRIB_SCALELOY 1505 +#define IDC_COMP_DISTRIB_SCALELOY_SPIN 1506 +#define IDC_COMP_DISTRIB_SCALELOZ 1507 +#define IDC_COMP_DISTRIB_SCALELOZ_SPIN 1508 +#define IDC_COMP_DISTRIB_SCALEHIY 1509 +#define IDC_COMP_DISTRIB_SCALEHIY_SPIN 1510 +#define IDC_COMP_DISTRIB_SCALEHIZ 1511 +#define IDC_COMP_DISTRIB_SCALEHIZ_SPIN 1512 +#define IDC_COMP_DISTRIB_ALIGNWGT 1513 +#define IDC_COMP_DISTRIB_ALIGNWGT_SPIN 1514 +#define IDC_COMP_DISTRIB_ANGPROBY 1515 +#define IDC_COMP_DISTRIB_ANGPROBY_SPIN 1516 +#define IDC_COMP_GUI_MODAL 1517 +#define IDC_COMP_DISTRIB_REMAPFROMLO 1517 +#define IDC_COMP_CLUSTERSIZE 1518 +#define IDC_COMP_DISTRIB_FADEOUTOPAQ 1518 +#define IDC_COMP_DISTRIB_REMAPFROMLO_SPIN 1519 +#define IDC_COMP_BLOW_SPEED 1519 +#define IDC_COMP_CLUSTERSIZE_SPIN 1520 +#define IDC_COMP_DISTRIB_FADEOUTOPAQ_SPIN 1520 +#define IDC_COMP_DISTRIB_REMAPTOLO 1521 +#define IDC_COMP_BLOW_SPEED_SPIN 1521 +#define IDC_CLUSTER_CLEAR 1522 +#define IDC_COMP_DISTRIB_FADEOUTTRAN 1522 +#define IDC_COMP_BLOW_FLUTTER 1522 +#define IDC_COMP_DISTRIB_REMAPTOLO_SPIN 1523 +#define IDC_COMP_BLOW_FLUTTER_SPIN 1523 +#define IDC_COMP_DISTRIB_FADEOUTTRAN_SPIN 1524 +#define IDC_COMP_BLOW_CONSTANCY 1524 +#define IDC_COMP_DISTRIB_REMAPFROMHI 1525 +#define IDC_COMP_BLOW_CONSTANCY_SPIN 1525 +#define IDC_CLUSTER_DO_THE_DANCE 1526 +#define IDC_COMP_DISTRIB_FADEINTRAN 1526 +#define IDC_COMP_DISTRIB_REMAPFROMHI_SPIN 1527 +#define IDC_COMP_DISTRIB_LOCK 1528 +#define IDC_COMP_DISTRIB_REMAPTOHI 1529 +#define IDC_COMP_DISTRIB_FADEINACTIVE 1530 +#define IDC_COMP_DISTRIB_REMAPTOHI_SPIN 1531 +#define IDC_COMP_DISTFADE_IN_ACTIVE 1531 +#define IDC_RADIO_EACHENTRY 1531 +#define IDC_RADIO_ENTRYCOUNT 1532 +#define IDC_COMP_DISTRIB_FADEINTRAN_SPIN 1532 +#define IDC_COMP_DISTRIB_ANGPROBZ 1533 +#define IDC_RADIO_FIRSTEXIT 1534 +#define IDC_COMP_DISTRIB_FADEINOPAQ 1534 +#define IDC_COMP_DISTRIB_ANGPROBZ_SPIN 1535 +#define IDC_RADIO_EXITCOUNT 1536 +#define IDC_COMP_DISTRIB_FADEINOPAQ_SPIN 1536 +#define IDC_COMP_DISTRIB_ANGPROBHI 1537 +#define IDC_RADIO_EACHEXIT 1538 +#define IDC_COMP_DISTRIB_LOCKSCALEXY 1538 +#define IDC_COMP_DISTRIB_ANGPROBTRANS 1538 +#define IDC_COMP_DISTRIB_ANGPROBHI_SPIN 1539 +#define IDC_COMP_DISTRIB_ANGPROBLO 1540 +#define IDC_COMP_DISTRIB_ANGPROBLO_SPIN 1541 +#define IDC_GUI_INITTEXT 1542 +#define IDC_COMP_DISTRIB_LOCKSCALEXYZ 1542 +#define IDC_COMP_DISTRIB_ANGPROBTRANS_SPIN 1542 +#define IDC_GUI_FONTSIZE 1543 +#define IDC_COMP_DISTRIB_WINDBONEACTIVE 1543 +#define IDC_GUI_FGAEDIT 1544 +#define IDC_DISTRIB_CLEAR 1545 +#define IDC_GUI_BGAEDIT 1545 +#define IDC_DISTRIB_PREVIEW 1546 +#define IDC_COMP_LINE_OFFSETACTIVE 1546 +#define IDC_GUI_SFGAEDIT 1546 +#define IDC_COMP_LINE_OFFSETDEGREES 1547 +#define IDC_GUI_SBGAEDIT 1547 +#define IDC_COMP_LINE_OFFSETDEGREES_SPIN 1548 +#define IDC_COMP_LINE_OFFSETCLAMPACTIVE 1549 +#define IDC_COMP_LINE_OFFSETCLAMP 1550 +#define IDC_COMP_IGNORELITE_ON 1550 +#define IDC_COMP_LINE_OFFSETCLAMP_SPIN 1551 +#define IDC_COMP_IGNORELITE_OFF 1551 +#define IDC_COMP_LINE_FORCETOLINE 1552 +#define IDC_COMP_IGNORELITE_TOGGLE 1552 +#define IDC_COMP_IGNORELITE_SELECTED 1553 +#define IDC_COMP_LINE_SPEEDCLAMP 1553 +#define IDC_COMP_FLEX_FLEX 1554 +#define IDC_COMP_LINE_SPEEDCLAMP_SPIN 1554 +#define IDC_COMP_FLEX_FLEX_SPIN 1555 +#define IDC_COMP_LINE_SPEEDCLAMPACTIVE 1555 +#define IDC_GUI_TAGCOMBO 1556 +#define IDC_COMP_FLEX_INTRA 1556 +#define IDC_Z_AXIS 1557 +#define IDC_COMP_FLEX_INTRA_SPIN 1557 +#define IDC_X_AXIS 1558 +#define IDC_COMP_FLEX_INTER 1558 +#define IDC_COMP_BLOW_STRENGTH 1559 +#define IDC_COMP_FLEX_INTER_SPIN 1559 +#define IDC_COMP_BLOW_STRENGTH_SPIN 1560 +#define IDC_COMP_SOFTVOL_ENABLEPARTIAL 1562 +#define IDC_COMP_SOFTUNION_ENABLEPARTIAL 1563 +#define IDC_COMP_SOFTUNION_INSIDE 1564 +#define IDC_COMP_SOFTUNION_INSIDE_SPIN 1565 +#define IDC_COMP_SOFTUNION_OUTSIDE 1566 +#define IDC_COMP_SOFTUNION_OUTSIDE_SPIN 1567 +#define IDC_COMP_SOFTNEGATE_ENABLEPARTIAL 1568 +#define IDC_COMP_SOFTNEGATE_INSIDE 1569 +#define IDC_COMP_SOFTNEGATE_INSIDE_SPIN 1570 +#define IDC_COMP_SOFTNEGATE_OUTSIDE 1571 +#define IDC_COMP_SOFTNEGATE_OUTSIDE_SPIN 1572 +#define IDC_COMP_SOFTISECT_ENABLEPARTIAL 1573 +#define IDC_COMP_SOFTISECT_INSIDE 1574 +#define IDC_COMP_SOFTISECT_INSIDE_SPIN 1575 +#define IDC_COMP_SOFTISECT_OUTSIDE 1576 +#define IDC_COMP_SOFTISECT_OUTSIDE_SPIN 1577 +#define IDC_COMP_DISTRIB_WINDBONE 1578 +#define IDC_RESPONDER_COMP 1580 +#define IDC_COMP_PHYS_VISIBLE_BOOL 1581 +#define IDC_CUSTOM1 1582 +#define IDC_COMP_PHYS_PINNED_BOOL 1585 +#define IDC_COMP_PHYS_LOS_BOOL 1586 +#define IDC_GUI_COMPSELBTN 1587 +#define IDC_GROUP_TEXT 1587 +#define IDC_GUI_SCROLLCTRL 1588 +#define IDC_GUI_COMPSELBTN2 1588 +#define IDC_COMP_PHYS_GROUP_COMBO 1588 +#define IDC_GUI_LOWER 1589 +#define IDC_COMP_PHYS_GROUP_LIST 1589 +#define IDC_GUI_MDOWNSNDCOMP 1589 +#define IDC_GUI_LOWER_SPIN 1590 +#define IDC_COMP_PHYS_GROUP_REMOVE 1590 +#define IDC_GUI_MUPSNDCOMP 1590 +#define IDC_GUI_UPPER 1591 +#define IDC_GUI_MOVERSNDCOMP 1591 +#define IDC_GUI_UPPER_SPIN 1592 +#define IDC_GUI_MOFFSNDCOMP 1592 +#define IDC_COMP_NAV_TRIGGER 1593 +#define IDC_GUI_ANIMNODESEL 1593 +#define IDC_GUI_SINGLESEL 1594 +#define IDC_COMP_NAV_LADDER 1594 +#define IDC_GUI_ANIMNODESEL2 1594 +#define IDC_GUI_ANIMSNDCOMP 1594 +#define IDC_GUI_XPARENT 1595 +#define IDC_GUI_DRAGCHILD 1595 +#define IDC_GUI_DRAGDROPSRC 1596 +#define IDC_NAME 1597 +#define IDC_GUI_DISABLEKEYS 1597 +#define IDC_GUI_ALLOWMULTIROW 1598 +#define IDC_CLEARED 1599 +#define IDC_GUI_SCROLLL2R 1599 +#define IDC_GUI_SCALERES 1600 +#define IDC_TRACKVIEW_SHOW 1601 +#define IDC_GUI_PASSTHRU 1601 +#define IDC_COMP_DISTRIB_ISOLATION 1602 +#define IDC_GUI_ENABLETREE 1602 +#define IDC_COMP_PARTICLE_FOLLOW_SYSTEM 1602 +#define IDC_COMP_DISTRIB_ISOLATION_SPIN 1603 +#define IDC_GUI_HANDSOFF 1603 +#define IDC_COMP_DISTRIB_DENSITY 1604 +#define IDC_COMP_DISTRIB_DENSITY_SPIN 1605 +#define IDC_COMP_DISTRIB_SPACERND 1606 +#define IDC_COMP_DISTRIB_SPACERND_SPIN 1607 +#define IDC_COMP_DISTRIB_SEPARATION 1608 +#define IDC_COMP_DISTRIB_ALTPROBLO 1609 +#define IDC_COMP_DISTRIB_ALTPROBLO_SPIN 1610 +#define IDC_COMP_DISTRIB_ALTPROBHI 1611 +#define IDC_COMP_DISTRIB_ALTPROBHI_SPIN 1612 +#define IDC_COMP_DISTRIB_ALTPROBTRANS 1613 +#define IDC_COMP_DISTRIB_ALTPROBTRANS_SPIN 1614 +#define IDC_COMP_DISTRIB_CONFORMMAX 1615 +#define IDC_COMP_DISTRIB_CONFORMMAX_SPIN 1616 +#define IDC_COMP_DISTRIB_OFFSETMIN 1617 +#define IDC_COMP_DISTRIB_SEED 1617 +#define IDC_COMP_DISTRIB_OFFSETMIN_SPIN 1618 +#define IDC_COMP_CLUST_AUTOEXPORT 1618 +#define IDC_COMP_DISTRIB_OFFSETMAX 1619 +#define IDC_COMP_DISTRIB_SEED_SPIN 1619 +#define IDC_COMP_CLUST_AUTOINSTANCE 1619 +#define IDC_COMP_DISTRIB_OFFSETMAX_SPIN 1620 +#define IDC_DETECTOR 1621 +#define IDC_COMP_DISTRIB_CONFORMTYPE 1621 +#define IDC_GUI_ANIMATE 1622 +#define IDC_GUI_ADDCHECK 1622 +#define IDC_GUI_DELCHECK 1623 +#define IDC_GUI_MOUSEOVERANIM 1623 +#define IDC_GUI_CHECKLIST 1624 +#define IDC_GUI_MDOWNSND 1624 +#define IDC_COMP_DISTRIB_FACENORMALS 1625 +#define IDC_GUI_MUPSND 1625 +#define IDC_COMP_LODFADE_HASBASE 1626 +#define IDC_GUI_MOVERSND 1626 +#define IDC_COMP_LODFADE_BASE 1627 +#define IDC_GUI_MOFFSND 1627 +#define IDC_COMP_LODFADE_DISTANCE 1628 +#define IDC_COMP_LODFADE_DISTANCE_SPIN 1629 +#define IDC_COMP_LODFADE_TRANSITION 1630 +#define IDC_COMP_FADEOUTTIME 1630 +#define IDC_COMP_LODFADE_TRANSITION_SPIN 1631 +#define IDC_COMP_FADEOUTTIME_SPIN 1631 +#define IDC_COMP_LODFADE_FADEBASE 1632 +#define IDC_COMP_LODFADE_BASEFIRST 1633 +#define IDC_GUI_PICKMAT 1634 +#define IDC_DELAY_EDIT 1635 +#define IDC_DELAY_SPIN 1636 +#define IDC_MTL_LIST 1637 +#define IDC_GUI_REVERSE 1638 +#define IDC_CLOTHING_LIST 1639 +#define IDC_CLOTHING_ADD 1640 +#define IDC_GUI_TRIGGERONUP 1640 +#define IDC_CLOTHING_REMOVE 1641 +#define IDC_GUI_ALLOWNONE 1642 +#define IDC_GUI_DEFSEL 1643 +#define IDC_GUI_DEFSEL_SPIN 1644 +#define IDC_GUI_VERSION 1645 +#define IDC_GUI_VERSION_SPIN 1646 +#define IDC_ANIM_NAME 1647 +#define IDC_LOOP_FOREVER 1648 +#define IDC_FORWARD_COMBO 1649 +#define IDC_BACKWARD 1650 +#define IDC_DO_ADVANCE_TO 1651 +#define IDC_GLOBAL_COORD 1652 +#define IDC_NUM_LOOPS_EDIT 1653 +#define IDC_NUM_LOOPS_SPIN 1654 +#define IDC_REGRESS 1655 +#define IDC_ADD 1656 +#define IDC_ADVANCE 1656 +#define IDC_REMOVE 1657 +#define IDC_ADVANCE_STAGE_EDIT 1657 +#define IDC_FREEZE_PHYS 1658 +#define IDC_ADVANCE_STAGE_SPIN 1658 +#define IDC_REGRESS_STAGE_EDIT 1659 +#define IDC_MULTI_REVERSE_CTL 1659 +#define IDC_STAGE_LIST 1660 +#define IDC_REGRESS_STAGE_SPIN 1660 +#define IDC_CHECK_ENTER 1661 +#define IDC_CHECK_LOOP 1662 +#define IDC_CHECK_ADVANCE 1663 +#define IDC_CHECK_REGRESS 1664 +#define IDC_COMP_BLENDONTO_SORTFACES 1665 +#define IDC_DO_REGRESS_TO 1665 +#define IDC_GUI_FONTBOLD 1666 +#define IDC_GUI_FONTITALIC 1667 +#define IDC_GUI_FGCOLOR 1668 +#define IDC_GUI_USEALPHAS 1669 +#define IDC_GUI_FONTFACE 1670 +#define IDC_GUI_FONTSIZE_SPIN 1671 +#define IDC_GUI_FGALPHA 1672 +#define IDC_GUI_SCHEMEPREV 1673 +#define IDC_COMP_GEO_DICE_MAXFACES 1674 +#define IDC_COMP_GEO_DICE_MAXFACES_SPIN 1675 +#define IDC_COMP_GEO_DICE_MAXSIZE 1676 +#define IDC_COMP_GEO_DICE_MAXSIZE_SPIN 1677 +#define IDC_COMP_GEO_DICE_MINFACES 1678 +#define IDC_COMP_GEO_DICE_MINFACES_SPIN 1679 +#define IDC_COMP_GEO_DICE_ACTIVE 1680 +#define IDC_GUI_FONTSHADOWED 1681 +#define IDC_COMP_GEO_DICE_OVERRIDE 1681 +#define IDC_COMP_AVATAR_CLOTHING_GROUP 1682 +#define IDC_COMP_AVATAR_SKELETON 1683 +#define IDC_COMP_SMOOTH_ANGLE 1684 +#define IDC_COMP_SMOOTH_ANGLE_SPIN 1685 +#define IDC_COMP_PARTICLE_WIND_SCALEX 1686 +#define IDC_COMP_SMOOTHAV_ANGLE 1686 +#define IDC_COMP_PARTICLE_WIND_SCALEX_SPIN 1687 +#define IDC_COMP_SMOOTHAV_ANGLE_SPIN 1687 +#define IDC_COMP_PARTICLE_WIND_SCALEY 1688 +#define IDC_CHECK_CHILDREN 1688 +#define IDC_COMP_PARTICLE_WIND_SCALEY_SPIN 1689 +#define IDC_CHECK_TRIGGER 1689 +#define IDC_COMP_PARTICLE_WIND_SCALEZ 1690 +#define IDC_CHECK_UNTRIGGER 1690 +#define IDC_COMP_PARTICLE_WIND_SCALEZ_SPIN 1691 +#define IDC_COMP_PARTICLE_WIND_SPEED 1692 +#define IDC_EVENT_LIST 1692 +#define IDC_COMP_PARTICLE_WIND_SPEED_SPIN 1693 +#define IDC_COMP_PARTICLE_WIND_STRENGTH 1694 +#define IDC_COMP_PARTICLE_VOL_DEFAULT 1694 +#define IDC_NODE_LIST 1694 +#define IDC_COMP_PARTICLE_WIND_STRENGTH_SPIN 1695 +#define IDC_COMP_PARTICLE_VOL_DIE 1696 +#define IDC_COMP_PARTICLE_WIND_CONSTANCY 1696 +#define IDC_COMP_PARTICLE_VOL_BOUNCE 1697 +#define IDC_COMP_PARTICLE_WIND_CONSTANCY_SPIN 1697 +#define IDC_COMP_PARTICLE_VOL_BOUNCEAMT 1698 +#define IDC_COMP_PARTICLE_WIND_SWIRL 1698 +#define IDC_COMP_PARTICLE_VOL_BOUNCEAMT_SPIN 1699 +#define IDC_COMP_PARTICLE_WIND_SWIRL_SPIN 1699 +#define IDC_COMP_PARTICLE_VOL_FRICTIONAMT 1700 +#define IDC_COMP_PARTICLE_WIND_MINSECS 1700 +#define IDC_COMP_PARTICLE_WIND_CLAMPANG 1700 +#define IDC_COMP_BLENDONTOADV_SORTFACES 1701 +#define IDC_COMP_PARTICLE_WIND_MINSECS_SPIN 1701 +#define IDC_COMP_PARTICLE_WIND_CLAMPANG_SPIN 1701 +#define IDC_COMP_PARTICLE_VOL_FRICTIONAMT_SPIN 1702 +#define IDC_COMP_PARTICLE_WIND_MAXSECS 1702 +#define IDC_COMP_BLENDONTOADV_SORTOBJECTS 1703 +#define IDC_COMP_PARTICLE_WIND_MAXSECS_SPIN 1703 +#define IDC_COMP_BLENDONTOADV_ONTOBLENDING 1704 +#define IDC_COMP_PARTICLE_WIND_RATE 1704 +#define IDC_ROOM_SEQEDIT 1705 +#define IDC_COMP_PARTICLE_WIND_RATE_SPIN 1705 +#define IDC_ROOM_SEQSPIN 1706 +#define IDC_COMP_PARTICLE_WIND_CLAMPANG2 1706 +#define IDC_CHASSIS 1707 +#define IDC_COMP_PARTICLE_WIND_HORIZONTAL 1707 +#define IDC_FL_WHEEL 1708 +#define IDC_COMP_PARTICLE_WIND_CLAMPANG_SPIN2 1708 +#define IDC_FR_WHEEL 1709 +#define IDC_RL_WHEEL 1710 +#define IDC_RR_WHEEL 1711 +#define IDC_FL_HARDPOINT 1712 +#define IDC_FR_HARDPOINT 1713 +#define IDC_RL_HARDPOINT 1714 +#define IDC_RR_HARDPOINT 1715 +#define IDC_COMP_DISTFADE_OUT_ACTIVE 1716 +#define IDC_COMP_DISTFADE_OUTSTART 1717 +#define IDC_COMP_LOD_CLOTHING_STATE 1717 +#define IDC_COMP_DISTFADE_OUTSTART_SPIN 1718 +#define IDC_COMP_LOD_CLOTHING_MESH_PICKB 1718 +#define IDC_COMP_DISTFADE_OUTEND 1719 +#define IDC_COMP_DISTFADE_OUTEND_SPIN 1720 +#define IDC_COMP_DISTFADE_INSTART 1721 +#define IDC_COMP_DISTFADE_INSTART_SPIN 1722 +#define IDC_COMP_DISTFADE_INEND 1723 +#define IDC_COMP_DISTFADE_INEND_SPIN 1724 +#define IDC_JUSTIFYRADIO 1725 +#define IDC_JUSTRADIO2 1726 +#define IDC_ORIENTATION_RADIO 1726 +#define IDC_JUSTRADIO3 1727 +#define IDC_ORIENTATION_RADIO2 1727 +#define IDC_GUI_MOUSEMAPREL 1728 +#define IDC_GUI_MOUSEMAPANIM 1729 +#define IDC_RADIO_FORCE_ON 1729 +#define IDC_GUI_MOUSEMAPSCRN 1730 +#define IDC_RADIO_FORCE_OFF 1730 +#define IDC_COMP_SHADOW_LIGHT_FALLOFF 1731 +#define IDC_COMP_SHADOW_LIGHT_FALLOFF_SPIN 1732 +#define IDC_COMP_SHADOW_LIGHT_MAXDIST 1733 +#define IDC_SND_CHANSRC1 1733 +#define IDC_COMP_SHADOW_LIGHT_MAXDIST_SPIN 1734 +#define IDC_SND_CHANSRC2 1734 +#define IDC_COMP_SHADOW_LIGHT_POWER 1735 +#define IDC_COMP_SHADOW_LIGHT_POWER_SPIN 1736 +#define IDC_COMP_SHADOW_LIGHT_SHADOWONLY 1737 +#define IDC_COMP_SHADOW_LIGHT_OBEYGROUPS 1738 +#define IDC_COMP_SHADOW_LIGHT_SELFSHADOW 1739 +#define IDC_SND_ISSTEREO_HIDDEN 1749 +#define IDC_COMP_SHADOW_CAST_SELFSHADOW 1752 +#define IDC_SND_PRIORITY 1752 +#define IDC_COMP_PHYS_SUBW_EDIT1 1753 +#define IDC_SND_PRIORITY_SPIN 1753 +#define IDC_COMP_SHADOW_CAST_BLUR 1753 +#define IDC_COMP_PHYS_SUBW_EDIT2 1754 +#define IDC_COMP_SHADOW_CAST_ATTEN 1754 +#define IDC_COMP_PHYS_SUBW_EDIT3 1755 +#define IDC_COMP_SHADOW_CAST_LIMIT 1755 +#define IDC_SND_LOCALONLY 1756 +#define IDC_SND_CATEGORY 1758 +#define IDC_COMP_LIGHTMAP_COMPRESS 1759 +#define IDC_COMP_LIGHTMAP_SHARED 1760 +#define IDC_COMP_RS_USEALL 1760 +#define IDC_COMP_RS_GROUPLIST 1761 +#define IDC_COMP_RS_GROUP_ADD 1762 +#define IDC_COMP_RS_GROUP_REMOVE 1763 +#define IDC_COMP_RS_GROUP_SPIN 1764 +#define IDC_COMP_RS_GROUP 1765 +#define IDC_COMP_FOOTSTEP_SOUND_SURFACE 1766 +#define IDC_COMP_FOOTSTEP_SOUND_PICK 1767 +#define IDC_COMP_RESPOND_FOOT_SURFACE 1768 +#define IDC_PYTHON_GLOBAL 1769 +#define IDC_EAX_PRESET 1770 +#define IDC_EAX_CUSTOM 1771 +#define IDC_EAX_CUSTFILE 1772 +#define IDC_EAX_PRESET_COMBO 1773 +#define IDC_EAX_SOFTREGION 1774 +#define IDC_EAX_ENABLE 1775 +#define IDC_EAX_ROOMAUTO 1776 +#define IDC_EAX_ROOMHFAUTO 1777 +#define IDC_EAX_ROOMHF 1778 +#define IDC_EAX_ROOMHF_EDIT 1779 +#define IDC_EAX_ROOM 1780 +#define IDC_EAX_ROOM_EDIT 1781 +#define IDC_EAX_OUTSIDEVOLHF 1782 +#define IDC_SND_TRACKVIEW 1782 +#define IDC_EAX_OUTSIDEVOLHF_EDIT 1783 +#define IDC_EAX_AIRABSORPTFACT 1784 +#define IDC_COMP_FP_WIDTH 1784 +#define IDC_EAX_AIRABSORPTFACT_EDIT 1785 +#define IDC_COMP_FP_WIDTH_SPIN 1785 +#define IDC_EAX_ROOMROLLOFFFACT 1786 +#define IDC_COMP_FP_LENGTH 1786 +#define IDC_TRIGGER_ON_FACING_CHECK 1786 +#define IDC_EAX_ROOMROLLOFFFACT_EDIT 1787 +#define IDC_COMP_FP_LENGTH_SPIN 1787 +#define IDC_WALKING_FORWARD_CHECK 1787 +#define IDC_EAX_DOPPLERFACT 1788 +#define IDC_COMP_FP_INTENSITY 1788 +#define IDC_EAX_DOPPLERFACT_EDIT 1789 +#define IDC_COMP_FP_INTENSITY_SPIN 1789 +#define IDC_EAX_ROLLOFFFACT 1790 +#define IDC_EAX_ROLLOFFFACT_EDIT 1791 +#define IDC_COMP_FP_LIFESPAN2 1792 +#define IDC_EAX_ENABLEOCCLUSION 1792 +#define IDC_COMP_FP_LIFESPAN_SPIN2 1793 +#define IDC_EAX_STARTOCC 1793 +#define IDC_EAX_ENDOCC 1794 +#define IDC_COMP_FP_DIRTYTIME 1794 +#define IDC_COMP_FP_SCALE 1794 +#define IDC_EAX_OCCLUSION 1795 +#define IDC_COMP_FP_DIRTYTIME_SPIN 1795 +#define IDC_COMP_FP_SCALE_SPIN 1795 +#define IDC_EAX_OCCLUSION_EDIT 1796 +#define IDC_RADIO_ALPHA 1796 +#define IDC_EAX_OCCLFRATIO 1797 +#define IDC_RADIO_MADD 1797 +#define IDC_EAX_OCCLFRATIO_EDIT 1798 +#define IDC_RADIO_ADD 1798 +#define IDC_EAX_OCCROOMRATIO 1799 +#define IDC_RADIO_MULT 1799 +#define IDC_EAX_OCCROOMRATIO_EDIT 1800 +#define IDC_COMP_FP_TEXMAP 1800 +#define IDC_EAX_OCCDIRECTRATIO 1801 +#define IDC_COMP_FP_PARTYTIME 1801 +#define IDC_EAX_OCCDIRECTRATIO_EDIT 1802 +#define IDC_COMP_FP_PARTYTIME_SPIN 1802 +#define IDC_EAX_OCCREGION 1803 +#define IDC_EAX_OCCPRESET 1804 +#define IDC_ADD_DECAL 1805 +#define IDC_COMP_WIDTH 1806 +#define IDC_COMP_WIDTH_SPIN 1807 +#define IDC_COMP_LENGTH 1808 +#define IDC_ADD_NOTIFY 1808 +#define IDC_COMP_LENGTH_SPIN 1809 +#define IDC_ADD_PARTICLE 1809 +#define IDC_COMP_HEIGHT 1810 +#define IDC_COMP_HEIGHT_SPIN 1811 +#define IDC_ADD_NOTIFY2 1812 +#define IDC_DETECT_LOCAL_CHECK 1814 +#define IDC_LINKNAMELABEL 1815 +#define IDC_CHECK_SKIPFFSOUND 1815 +#define IDC_NPC_SPAWN_MODEL_TEXT_BOX 1816 +#define IDC_NPC_SPAWN_ACCOUNT_TEXT_BOX 1817 +#define IDC_NPC_SPAWN_AUTOSPAWN_BOOL 1818 +#define IDC_COMP_LOD_AVATAR_BONELIST 1819 +#define IDC_COMP_LOD_AVATAR_BONE_ADD 1820 +#define IDC_COMP_LOD_AVATAR_BONE_REMOVE 1821 +#define IDC_COMP_LOD_AVATAR_GROUP 1822 +#define IDC_COMP_LOD_AVATAR_GROUP_SPIN 1823 +#define IDC_CLOTHING_CLEARMESH 1824 +#define IDC_GUI_BETTERHIT 1828 +#define IDC_RAND_COMBINESOUNDS 1829 +#define IDC_PH_NO_SYNC_CHK 1830 +#define IDC_PH_INACTIVE_CHK 1831 +#define IDC_COMP_FILTER_NOX 1832 +#define IDC_PH_AVANIMPUSHABLE 1832 +#define IDC_COMP_FILTER_NOY 1833 +#define IDC_COMP_FILTER_NOZ 1834 +#define IDC_SMARTSEEK 1835 +#define IDC_COMP_STEREIZE_LEFT 1836 +#define IDC_COMP_STEREIZE_AMB 1837 +#define IDC_COMP_STEREIZE_AMB_SPIN 1838 +#define IDC_COMP_STEREIZE_TRANS 1839 +#define IDC_COMP_STEREIZE_TRANS_SPIN 1840 +#define IDC_COMP_STEREIZE_ANG 1841 +#define IDC_COMP_STEREIZE_ANG_SPIN 1842 +#define IDC_COMP_STEREIZE_MAXDIST 1843 +#define IDC_COMP_STEREIZE_MAXDIST_SPIN 1844 +#define IDC_COMP_STEREIZE_MINDIST 1845 +#define IDC_COMP_STEREIZE_MINDIST_SPIN 1846 +#define IDC_COMP_CLIMB_COMMAND 1847 +#define IDC_COMP_CLIMB_DIRECTION 1848 +#define IDC_COMP_WALL_PICK 1849 +#define IDC_CAMERA_VEL_SPIN 1850 +#define IDC_CAMERA_DECEL 1851 +#define IDC_CAMERA_DECEL_SPIN 1852 +#define IDC_CAMERA_ACCEL2 1853 +#define IDC_CAMERA_ACCEL_SPIN2 1854 +#define IDC_CAMERA_VEL2 1855 +#define IDC_CAMERA_VEL_SPIN2 1856 +#define IDC_CAMERA_DECEL2 1857 +#define IDC_CAMERA_DECEL_SPIN2 1858 +#define IDC_CAMERA_ACCEL 1859 +#define IDC_CAMERA_ACCEL_SPIN 1860 +#define IDC_CAMERA_VEL 1861 +#define IDC_VER_TEXT 1862 +#define IDC_CHECK_IGNORE 1863 +#define IDC_RADIO_PLAYER 1864 +#define IDC_COMP_CHOOSE_OBJECT 1865 +#define IDC_COMP_OFFSETACTIVE 1870 +#define IDC_COMP_OFFSETX 1871 +#define IDC_COMP_OFFSETX_SPIN 1872 +#define IDC_COMP_OFFSETY 1873 +#define IDC_BODY_UNKNOWN 1873 +#define IDC_DRIVE 1873 +#define IDC_COMP_OFFSETY_SPIN 1874 +#define IDC_BODY_UPPER 1874 +#define IDC_COMP_OFFSETZ 1875 +#define IDC_BODY_FULL 1875 +#define IDC_COMP_OFFSETZ_SPIN 1876 +#define IDC_COMP_OFFSETLOCAL 1877 +#define IDC_FADE_IN 1877 +#define IDC_COMP_PIVOTY 1878 +#define IDC_FADE_OUT 1878 +#define IDC_EMO_FADEIN 1879 +#define IDC_EMO_FADEIN_SPIN 1880 +#define IDC_EMO_FADEOUT_SPIN 1881 +#define IDC_EMO_FADEOUT 1882 +#define IDC_COMP_W_GEO_MINLEN 1883 +#define IDC_COMP_W_GEO_MINLEN_SPIN 1884 +#define IDC_COMP_W_ZEROOPAC 1885 +#define IDC_COMP_W_GEO_MAXLEN 1885 +#define IDC_COMP_W_ZEROOPAC_SPIN 1886 +#define IDC_COMP_W_GEO_MAXLEN_SPIN 1886 +#define IDC_COMP_W_DEPTHOPAC 1887 +#define IDC_COMP_W_SHORETINT 1887 +#define IDC_COMP_W_DEPTHOPAC_SPIN 1888 +#define IDC_COMP_W_SHOREOPAC 1888 +#define IDC_COMP_W_ZEROREFL 1889 +#define IDC_COMP_W_SHOREOPAC_SPIN 1889 +#define IDC_COMP_W_GEO_AMPOVERLEN 1889 +#define IDC_COMP_W_ZEROREFL_SPIN 1890 +#define IDC_COMP_W_WISPINESS 1890 +#define IDC_COMP_W_FINGER 1890 +#define IDC_COMP_W_GEO_AMPOVERLEN_SPIN 1890 +#define IDC_COMP_W_DEPTHREFL 1891 +#define IDC_COMP_W_WISPINESS_SPIN 1891 +#define IDC_COMP_W_FINGER_SPIN 1891 +#define IDC_COMP_W_GEO_CHOP 1891 +#define IDC_COMP_W_DEPTHREFL_SPIN 1892 +#define IDC_COMP_W_PERIOD 1892 +#define IDC_COMP_W_REFOBJECT 1892 +#define IDC_COMP_W_GEO_CHOP_SPIN 1892 +#define IDC_COMP_W_ZEROWAVE 1893 +#define IDC_COMP_W_PERIOD_SPIN 1893 +#define IDC_COMP_SHORE_CHOSE 1893 +#define IDC_COMP_W_GEO_ANGLEDEV 1893 +#define IDC_COMP_W_ZEROWAVE_SPIN 1894 +#define IDC_COMP_W_ENVOBJECT 1894 +#define IDC_COMP_W_EDGEOPAC 1894 +#define IDC_COMP_W_GEO_ANGLEDEV_SPIN 1894 +#define IDC_COMP_W_DEPTHWAVE 1895 +#define IDC_COMP_W_ENVSIZE 1895 +#define IDC_COMP_W_EDGEOPAC_SPIN 1895 +#define IDC_COMP_W_GEO_SPECMUTE 1895 +#define IDC_COMP_W_DEPTHWAVE_SPIN 1896 +#define IDC_COMP_W_ENVSIZE_SPIN 1896 +#define IDC_COMP_W_EDGERADIUS 1896 +#define IDC_COMP_W_GEO_SPECMUTE_SPIN 1896 +#define IDC_COMP_W_ENVRADIUS 1897 +#define IDC_COMP_W_EDGERADIUS_SPIN 1897 +#define IDC_COMP_W_ENVRADIUS_SPIN 1898 +#define IDC_AGELINKSPAWNPTLABEL 1899 +#define IDC_COMP_W_ENVREFRESH 1899 +#define IDC_RADIO_BROKEN 1900 +#define IDC_COMP_W_ENVREFRESH_SPIN 1900 +#define IDC_AGELINKSPAWNPTTITLE 1900 +#define IDC_RADIO_CALIBRATED 1901 +#define IDC_AGELINKSPAWNPTTITLE2 1901 +#define IDC_RADIO_REPAIRED 1902 +#define IDC_AGELINKSPAWNPTTITLE3 1902 +#define IDC_GUI_REPORTDRAG 1903 +#define IDC_GUI_HIDECURSOR 1904 +#define IDC_GUI_USEDRAGCHILD 1904 +#define IDC_GUI_SNAPSTART 1905 +#define IDC_COMP_NOSHOW_SHOWABLE 1905 +#define IDC_COMP_VISREGION_CHOOSE_VOLUME 1906 +#define IDC_COMP_VISREGION_DRAW 1907 +#define IDC_COMP_VISREGION_LIGHT 1908 +#define IDC_COMP_VISREGION_OCC 1909 +#define IDC_COMP_NOSHOW_AFFECTPHYS 1909 +#define IDC_COMP_NOSHOW_AFFECTDRAW 1910 +#define IDC_COMP_VISREGION_NOT 1910 +#define IDC_COMP_SMOOTHAV_DIST 1911 +#define IDC_COMP_VISREGION_DIS 1911 +#define IDC_COMP_SMOOTHAV_DIST_SPIN 1912 +#define IDC_GUI_ELEMENTS 1913 +#define IDC_GUI_HORZSCROLL 1914 +#define IDC_GUI_VERTSCROLL 1915 +#define IDC_GUI_INFO 1916 +#define IDC_GUI_PREVIEW 1917 +#define IDC_GUI_EDITELEM 1918 +#define IDC_GUI_ZIN 1919 +#define IDC_GUI_IMARGIN 1920 +#define IDC_GUI_IMARGIN_SPIN 1921 +#define IDC_GUI_BMARGIN 1922 +#define IDC_GUI_BMARGIN_SPIN 1923 +#define IDC_GUI_SKIN 1924 +#define IDC_GUI_NEVERCLOSE 1925 +#define IDC_GUI_MODALOUTSIDE 1926 +#define IDC_GUI_HOVER 1927 +#define IDC_ALIGNRADIO1 1929 +#define IDC_ALIGNRADIO2 1930 +#define IDC_COMP_WDECAL_CHOSE 1930 +#define IDC_ALIGNRADIO3 1931 +#define IDC_BEGINONPUSH 1931 +#define IDC_ALIGNRADIO4 1932 +#define IDC_STOPONPOP 1932 +#define IDC_RESETONPOP 1933 +#define IDC_COMP_SMOOTHAV_POS 1934 +#define IDC_COMP_SMOOTH_POS 1935 +#define IDC_COMP_SMOOTH_COLOR 1936 +#define IDC_LOCAL_ONLY_CHECK 1936 +#define IDC_OVERRIDE_CHECK 1939 +#define IDC_PHYS_ALLOW_RADIO 1943 +#define IDC_PHYS_NOSAVE_RADIO 1944 +#define IDC_PHYS_DENY_RADIO 1945 +#define IDC_ANIM_ALLOW_RADIO 1946 +#define IDC_ANIM_NOSAVE_RADIO 1947 +#define IDC_ANIM_DENY_RADIO 1948 +#define IDC_SND_ALLOW_RADIO 1949 +#define IDC_SND_NOSAVE_RADIO 1950 +#define IDC_SND_DENY_RADIO 1951 +#define IDC_MAT_ALLOW_RADIO 1952 +#define IDC_MAT_NOSAVE_RADIO 1953 +#define IDC_MAT_DENY_RADIO 1954 +#define IDC_RESP_ALLOW_RADIO 1955 +#define IDC_RESP_NOSAVE_RADIO 1956 +#define IDC_RESP_DENY_RADIO 1957 +#define IDC_XREG_ALLOW_RADIO 1958 +#define IDC_XREG_NOSAVE_RADIO 1959 +#define IDC_XREG_DENY_RADIO 1960 +#define IDC_COMP_LOD_AVATAR_MTL 1961 +#define IDC_SIT_APP_FRONT 1962 +#define IDC_COMP_RELREGION_CHOOSE_VOLUME 1963 +#define IDC_COMP_SHADOW_CAST_BLURSCALE 1967 +#define IDC_COMP_SHADOW_CAST_BLURSCALE_SPIN 1968 +#define IDC_COMP_SHADOW_CAST_ATTENSCALE 1969 +#define IDC_COMP_REPRESENT_QUALITY 1969 +#define IDC_COMP_SHADOW_CAST_ATTENSCALE_SPIN 1970 +#define IDC_ADD_REPS 1970 +#define IDC_COMP_SHADOW_CAST_BOOST 1971 +#define IDC_LIST_REPS 1971 +#define IDC_COMP_SHADOW_CAST_BOOST_SPIN 1972 +#define IDC_DEL_REPS 1972 +#define IDC_COMP_WDECAL_ENV 1973 +#define IDC_COMP_SHADOW_QUALITY 1973 +#define IDC_SECT_LIST 1974 +#define IDC_STIFFNESS_EDIT 1975 +#define IDC_UP_BUTTON 1976 +#define IDC_DN_BUTTON 1977 +#define IDC_STRENGTH_EDIT 1978 +#define IDC_DEF_STR_BUTTON 1979 +#define IDC_COMP_SMOOTHBASE_DIST 1980 +#define IDC_COMP_SMOOTHBASE_DIST_SPIN 1981 +#define IDC_COMP_SMOOTHBASE_ANGLE 1982 +#define IDC_COMP_SMOOTHBASE_ANGLE_SPIN 1983 +#define IDC_COMP_SMOOTHBASE_POS 1984 +#define IDC_COMP_SMOOTHSNAP_DIST 1985 +#define IDC_COMP_SMOOTHSNAP_DIST_SPIN 1986 +#define IDC_COMP_SMOOTHSNAP_ANGLE 1987 +#define IDC_COMP_SMOOTHSNAP_ANGLE_SPIN 1988 +#define IDC_COMP_SMOOTHSNAP_POS 1989 +#define IDC_COMP_SMOOTH_CHOSE 1990 +#define IDC_IMAGE_LIST 1991 +#define IDC_IMAGE_ADD 1992 +#define IDC_IMAGE_REMOVE 1993 +#define IDC_IMAGE_EDIT 1994 +#define IDC_IL_COMPRESS 1995 +#define IDC_IL_FORCEPOW2 1996 +#define IDC_SND_INCIDENTAL 1997 +#define IDC_COMP_PARTICLE_WIND_REFOBJECT 1998 +#define IDC_FLOCK_TARGET_OFFSETX 2001 +#define IDC_FLOCK_TARGET_OFFSETX_SPIN 2002 +#define IDC_FLOCK_TARGET_OFFSETY 2003 +#define IDC_FLOCK_TARGET_OFFSETZ 2004 +#define IDC_FLOCK_TARGET_OFFSETY_SPIN 2005 +#define IDC_FLOCK_TARGET_OFFSETZ_SPIN 2006 +#define IDC_COMP_EFFVISSET_HIDENORMAL 2007 +#define IDC_COMP_ENVMAP_YON 2008 +#define IDC_COMP_ENVMAP_YON_SPIN 2009 +#define IDC_COMP_ENVMAP_REFRESHRATE 2010 +#define IDC_COMP_ENVMAP_REFRESHRATE_SPIN 2011 +#define IDC_COMP_ENVMAP_ENVSIZE 2012 +#define IDC_COMP_ENVMAP_ENVSIZE_SPIN 2013 +#define IDC_COMP_ENVMAP_INCCHARS 2014 +#define IDC_COMP_W_TEX_MINLEN 2015 +#define IDC_COMP_ENVMAP_FOGSTART 2015 +#define IDC_COMP_W_TEX_MINLEN_SPIN 2016 +#define IDC_COMP_ENVMAP_FOGSTART_SPIN 2016 +#define IDC_COMP_W_TEX_MAXLEN 2017 +#define IDC_COMP_W_TEX_MAXLEN_SPIN 2018 +#define IDC_COMP_W_TEX_AMPOVERLEN 2019 +#define IDC_COMP_W_TEX_AMPOVERLEN_SPIN 2020 +#define IDC_COMP_W_TEX_CHOP 2021 +#define IDC_COMP_W_TEX_CHOP_SPIN 2022 +#define IDC_COMP_W_TEX_NOISE 2023 +#define IDC_COMP_W_TEX_NOISE_SPIN 2024 +#define IDC_STATIC_BUTTON_NOTIFY 2024 +#define IDC_COMP_W_TEX_RIPPLESCALE 2025 +#define IDC_COMBO_BUTTON_NOTIFYTYPE 2025 +#define IDC_COMP_W_TEX_RIPPLESCALE_SPIN 2026 +#define IDC_COMP_W_TEX_ANGLEDEV 2027 +#define IDC_COMP_W_TEX_ANGLEDEV_SPIN 2028 +#define IDC_COMP_W_TEX_SPECSTART 2029 +#define IDC_COMP_W_TEX_SPECSTART_SPIN 2030 +#define IDC_COMP_W_TEX_SPECEND 2031 +#define IDC_COMP_W_TEX_SPECEND_SPIN 2032 +#define IDC_SND_GROUP 2033 +#define IDC_SND_AGAINST 2034 +#define IDC_SND_IMPACT 2035 +#define IDC_SND_CLEAR_IMPACT 2036 +#define IDC_SND_SLIDE 2037 +#define IDC_SND_CLEAR_SLIDE 2038 +#define IDC_GUI_LANGUAGE 2039 +#define IDC_COMP_PANIC_ANIM 2041 +#define IDC_RADIO_THIRD 2042 +#define IDC_RADIO_FIRST 2043 +#define IDC_CHECK_STREAM 2044 +#define IDC_CAMERA_LOS 2045 +#define IDC_IGNOREFOV 2047 +#define IDC_COMP_W_SPECULARTINT 2048 +#define IDC_RADIO_ENTER 2049 +#define IDC_RADIO_EXIT 2050 +#define IDC_COLLIDABLE_CHECK 2051 +#define IDC_MARKER_PHYS 2052 +#define IDC_ANIM_RED_COMBO 2053 +#define IDC_SWIM_CURRENT_NONE 2054 +#define IDC_COMP_BOUNDSCENTER 2054 +#define IDC_SWIM_CURRENT_SPIRAL 2055 +#define IDC_COMP_FADEINTIME 2055 +#define IDC_SWIM_CURRENT_ROTATION 2056 +#define IDC_COMP_FADEINTIME_SPIN 2056 +#define IDC_SWIM_CURRENT_ROTATION_SPIN 2057 +#define IDC_COMP_GZ_OPAQUE 2057 +#define IDC_SWIM_CURRENT_PULL 2058 +#define IDC_COMP_GZ_OPAQUE_SPIN 2058 +#define IDC_SWIM_CURRENT_PULL_NEAR_DIST 2058 +#define IDC_SWIM_CURRENT_PULL_SPIN 2059 +#define IDC_COMP_GZ_TRANSP 2059 +#define IDC_SWIM_CURRENT_PULL_NEAR_DIST_SPIN 2059 +#define IDC_COMP_GZ_TRANSP_SPIN 2060 +#define IDC_SWIM_DETECTOR_NODE 2060 +#define IDC_SWIM_CURRENT_NODE 2061 +#define IDC_SUBWORLD_ENTER 2061 +#define IDC_SUBWORLD_EXIT 2062 +#define IDC_SWIM_CURRENT_PULL_FAR_VEL 2062 +#define IDC_COMP_ENVMAP_FOGCOLOR 2063 +#define IDC_SWIM_CURRENT_PULL_FAR_VEL_SPIN 2063 +#define IDC_SWIM_BUOYANCY_UP 2064 +#define IDC_GUI_ANIMSND 2064 +#define IDC_SWIM_BUOYANCY_UP_SPIN 2065 +#define IDC_GUI_USE_LOCALIZATION 2065 +#define IDC_SWIM_MAX_UP_VEL 2066 +#define IDC_GUI_LOCALIZATION_PATH 2066 +#define IDC_SWIM_MAX_UP_VEL_SPIN 2067 +#define IDC_GUI_SELECT_LOC_PATH 2067 +#define IDC_SWIM_BUOYANCY_DOWN 2068 +#define IDC_LOCALIZATIONTREE 2068 +#define IDC_SWIM_BUOYANCY_DOWN_SPIN 2069 +#define IDC_EDIT1 2069 +#define IDC_LOCALIZATIONSTRING 2069 +#define IDC_BODYAGENAME_EDIT 2069 +#define IDC_SWIM_CURRENT_PULL_NEAR_VEL 2070 +#define IDC_OBJ_FLOCKER_USE_TARGET_ROTATION 2070 +#define IDC_SWIM_CURRENT_PULL_NEAR_VEL_SPIN 2071 +#define IDC_COMP_ANIM_COMPRESS_NONE 2071 +#define IDC_SWIM_CURRENT_PULL_FAR_DIST 2072 +#define IDC_COMP_ANIM_COMPRESS_LOW 2072 +#define IDC_SWIM_CURRENT_PULL_FAR_DIST_SPIN 2073 +#define IDC_COMP_ANIM_COMPRESS_HIGH 2073 +#define IDC_SWIM_CURRENT_STRAIGHT 2074 +#define IDC_COMP_ANIM_COMPRESS_THRESHOLD 2074 +#define IDC_SWIM_CURRENT_STRAIGHT_NEAR_DIST 2075 +#define IDC_COMP_ANIM_COMPRESS_THRESHOLD_SPIN 2075 +#define IDC_SWIM_CURRENT_STRAIGHT_NEAR_DIST_SPIN 2076 +#define IDC_PHYS_HEIGHT_EDIT 2076 +#define IDC_SWIM_CURRENT_STRAIGHT_FAR_VEL 2077 +#define IDC_PHYS_HEIGHT_SPIN 2077 +#define IDC_SWIM_CURRENT_STRAIGHT_FAR_VEL_SPIN 2078 +#define IDC_RADIO_BLOCK_AVATAR 2078 +#define IDC_SWIM_CURRENT_STRAIGHT_NEAR_VEL 2079 +#define IDC_IGNORE_REGION_CHECK 2079 +#define IDC_SWIM_CURRENT_STRAIGHT_NEAR_VEL_SPIN 2080 +#define IDC_BODYFOOTSTEPPAGE_EDIT 2080 +#define IDC_SWIM_CURRENT_STRAIGHT_FAR_DIST 2081 +#define IDC_ANIMATIONPREFIX_EDIT 2081 +#define IDC_SWIM_CURRENT_STRAIGHT_FAR_DIST_SPIN 2082 +#define IDC_STATIC_FRICTION_LABEL 2082 +#define IDC_COMP_CLICKABLE_FRIC_EDIT1 2083 +#define IDC_COMP_CLICKABLE_FRIC_SPIN1 2084 +#define IDC_ARBITRATION_CHECK 2085 +#define IDC_COMP_ENVMAP_ENVSIZ 2312 +#define IDC_GRASS_DIR_X 2854 +#define IDC_GRASS_DIR_X_SPIN 2855 +#define IDC_GRASS_DIR_Y 2856 +#define IDC_GRASS_DIR_Y_SPIN 2857 +#define IDC_GRASS_DIST_X 2858 +#define IDC_GRASS_DIST_X_SPIN 2859 +#define IDC_GRASS_DIST_Y 2860 +#define IDC_GRASS_DIST_Y_SPIN 2861 +#define IDC_GRASS_DIST_Z 2862 +#define IDC_GRASS_DIST_Z_SPIN 2863 +#define IDC_GRASS_SPEED 2864 +#define IDC_GRASS_SPEED_SPIN 2865 +#define IDC_GRASS_WAVE 2866 +#define IDC_OBJ_FLOCKER_BOID_BUTTON 3019 +#define IDC_OBJ_FLOCKER_SEP_RADIUS 3022 +#define IDC_OBJ_FLOCKER_SEP_RADIUS_SPIN 3023 +#define IDC_OBJ_FLOCKER_SEP_STRENGTH 3024 +#define IDC_OBJ_FLOCKER_SEP_STRENGTH_SPIN 3025 +#define IDC_OBJ_FLOCKER_SLIMIT_MIN 3038 +#define IDC_OBJ_FLOCKER_SLIMIT_MIN_SPIN 3039 +#define IDC_OBJ_FLOCKER_HIDE_TARGET 3042 +#define IDC_OBJ_FLOCKER_SLIMIT_MAX 3045 +#define IDC_OBJ_FLOCKER_WANDER_STRENGTH 3046 +#define IDC_OBJ_FLOCKER_WANDER_STRENGTH_SPIN 3047 +#define IDC_OBJ_FLOCKER_GOAL_STRENGTH 3048 +#define IDC_OBJ_FLOCKER_GOAL_STRENGTH_SPIN 3050 +#define IDC_OBJ_FLOCKER_COH_RADIUS 3051 +#define IDC_OBJ_FLOCKER_COH_RADIUS_SPIN 3052 +#define IDC_OBJ_FLOCKER_COH_STRENGTH 3053 +#define IDC_OBJ_FLOCKER_COH_STRENGTH_SPIN 3054 +#define IDC_OBJ_FLOCKER_NUM_BOIDS 3055 +#define IDC_OBJ_FLOCKER_NUM_BOIDS_SPIN 3056 +#define IDC_OBJ_FLOCKER_RANDOM_ANIM_START 3057 +#define IDC_OBJ_FLOCKER_MAX_FORCE 3063 +#define IDC_OBJ_FLOCKER_MAX_FORCE_SPIN 3064 +#define IDC_OBJ_FLOCKER_SLIMIT_MAX_SPIN 3066 +#define IDC_GUI_SKINBMAP 3070 +#define IDC_COMP_ENVMAP_CUBIC 3319 +#define IDC_COMP_ENVMAP_SINGLE_CAM 3320 +#define IDC_COMP_ENVMAP_REMOVE_STRING 3358 +#define IDC_COMP_ENVMAP_ADD_STRING 3359 +#define IDC_COMP_ENVMAP_ADD_STRING_BOX 3360 +#define IDC_COMP_ENVMAP_NAMES_LISTBOX 3361 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 559 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 2086 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/zoomin1.ico b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxComponent/zoomin1.ico new file mode 100644 index 0000000000000000000000000000000000000000..ca7ff8065c7691f16a6d2665625bb0d8016b2bde GIT binary patch literal 1078 zcmd^;F$%&!5Ji7furs-Vsl;38k?hva1sjjG9>va5v9KK9L|07^v=Ib<@@MAnOd!kt zl?wG#*OeUmo-`p>)m2XmY1EgdtkrN13k`@;WcD>8O*%1N6OmFWC9*fHd1dAm4S11wRESia|nz)U>z+xJet2%;eiiuVrB=oaBX%Rnx)b# zj^PX!kuzO8(~3EJ3-`G@zlPc8iTX7n9;bt+n?$7ilFx6R$zHj6Bzq%!BI~;c?q}aV h^X9&txN|$Wg=^_J. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// StringTokenizer.cpp + +#include "StringTokenizer.h" +#include "string.h" + +// String Tokenizer routines +StringTokenizer::StringTokenizer() { + qAsTok = true; + inQuote = false; + this->string = this->seps = 0; +} +StringTokenizer::StringTokenizer(const char *string, const char *seps) { + qAsTok = true; + inQuote = false; + this->string = TRACKED_NEW char[strlen(string)+1]; + strcpy(this->string,string); + numSeps = strlen(seps); + this->seps = TRACKED_NEW char[numSeps+1]; + strcpy(this->seps,seps); + this->tok = this->string; + if (isSep(*tok)) next(); +}; +StringTokenizer::~StringTokenizer() { + delete string; + delete seps; +} +hsBool StringTokenizer::hasMoreTokens() { + return (*tok != '\0'); +}; +char *StringTokenizer::next() { + if (*tok == '\0') return NULL; + char *cur = tok; + while (*tok != '\0' && !isSep(*tok)) tok++; + if (*tok != '\0') { + *tok = '\0'; + tok++; + } + while (*tok != '\0' && isSep(*tok)) tok++; + return cur; +}; +hsBool StringTokenizer::isSep(char c) { + if (!qAsTok || !inQuote) { + for (Int32 i=0; istring) delete this->string; + this->string = TRACKED_NEW char[strlen(string)+1]; + strcpy(this->string,string); + if (this->seps) delete this->seps; + numSeps = strlen(seps); + this->seps = TRACKED_NEW char[numSeps+1]; + strcpy(this->seps,seps); + this->tok = this->string; + if (isSep(*tok)) next(); +} + +void StringTokenizer::ParseQuotes(hsBool qAsTok) { + this->qAsTok = qAsTok; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/StringTokenizer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/StringTokenizer.h new file mode 100644 index 00000000..3c867b69 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/StringTokenizer.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// StringTokenizer.h +#ifndef _STRING_TOKENIZER_H_ +#define _STRING_TOKENIZER_H_ + +#include "Headspin.h" + +class StringTokenizer { +private: + char *seps; + char *tok; + hsBool isSep(char c); + Int32 numSeps; + hsBool qAsTok; + hsBool inQuote; +public: + StringTokenizer(); + StringTokenizer(const char *string, const char *seps); + ~StringTokenizer(); + char *next(); + hsBool hasMoreTokens(); + void reset(const char *string, const char *seps); + void ParseQuotes(hsBool qAsTok); + char *string; +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/UserPropMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/UserPropMgr.cpp new file mode 100644 index 00000000..a815ac4c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/UserPropMgr.cpp @@ -0,0 +1,869 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// UserPropMgr.cpp + +#include "hsTypes.h" +#include "UserPropMgr.h" +#include "hsStringTokenizer.h" +#include "hsUtils.h" +#include "hsHashTable.h" + +#define REFMSG_USERPROP (REFMSG_USER + 1) + +const UInt32 UserPropMgr::kQuickSize = 150001;//199999; + +UserPropMgr gUserPropMgr(GetCOREInterface()); + +UserPropMgr::UserPropMgr(Interface *ip) : +nm(0) +{ + this->ip = ip; + + fQuickTable = nil; + fQuickNode = nil; + + vProps = false; +} + +UserPropMgr::~UserPropMgr() +{ + CloseQuickTable(); +} + +void UserPropMgr::SetUserPropFlag(INode *node, const char *name, const BOOL setFlag, const Int32 hFlag) +{ + if (setFlag) SetUserProp(node,name,NULL,hFlag); + else ClearUserProp(node,name,hFlag); +} + +void UserPropMgr::ClearUserPropALL(const char *name, const Int32 hFlag) +{ + for (int i=0; iSelectNode(node,false); + } else node = ip->GetRootNode(); + + for (int i=0; iNumberOfChildren(); i++) { + SelectUserPropFlagALL(node->GetChildNode(i),name,flag); + } +} + + +void UserPropMgr::DeSelectWithOut(const char *name, const char *value) { + BOOL oldProps = vProps; + vProps=false; + TSTR val; + INode *nodes[1]; + INodeTab nodeTab; + for (int i=0; i 0) ip->SelectNodeTab(nodeTab,false,false); + +} + +void UserPropMgr::RecursiveSelectAll(INode *node) { + if (node) { + if (!node->Selected()) ip->SelectNode(node,false); + } else node = ip->GetRootNode(); + + for (int i=0; iNumberOfChildren(); i++) { + RecursiveSelectAll(node->GetChildNode(i)); + } +} + + +void UserPropMgr::DeSelectUnAlike(INode *node) { +theHold.Begin(); + +ip->ThawSelection(); + + RecursiveSelectAll(); + + TSTR buf; + GetUserPropBuffer(node,buf); + + hsStringTokenizer toker(buf," \r\n"); + toker.ParseQuotes(TRUE); + char *tok; + TSTR name; + bool isName = true; + while (tok=toker.next()) { + if (isName) { + if (*tok != '=') { + name = tok; + tok = toker.next(); + if (tok && *tok == '=') { + tok = toker.next(); + } else tok = NULL; + DeSelectWithOut(name,tok); + } else isName = false; + } else { + isName = true; + } + } + + + TSTR undostr; undostr.printf("Select"); + theHold.Accept(undostr); + + ip->FreezeSelection(); + + ip->RedrawViews(ip->GetTime()); +} + + + +int UserPropMgr::RecursiveCountAlike(INode *node, BOOL MatchAll) { + int count=0; + if (node) { + if (!node->IsNodeHidden() && !node->IsFrozen() && IsAlike(node,MatchAll)) count++; + } else node = ip->GetRootNode(); + + for (int i=0; iNumberOfChildren(); i++) { + count += RecursiveCountAlike(node->GetChildNode(i),MatchAll); + } + return count; +} + + +int UserPropMgr::CountAlike(BOOL MatchAll) { + return RecursiveCountAlike(NULL, MatchAll); +} + +BOOL UserPropMgr::IsMatch(const char *val1, const char *val2) { + if (!stricmp(val1,val2)) return true; + hsStringTokenizer toker(val1," ,@"); + char *tok; + + while (tok=toker.next()) { + hsStringTokenizer toker2(val2," ,@"); + BOOL found = false; + char *tok2; + while ((tok2=toker2.next()) && !found) { + if (tok[0] >= '1' && tok[0] <= '0') { + if (!stricmp(tok,tok2)) found = true; + } else if (toker.HasMoreTokens()) { + if (!stricmp(tok,tok2)) found = true;if (!stricmp(tok,tok2)) found = true; + } else { + if (!strnicmp(tok,tok2,strlen(tok))) found = true; + } + } + if (!found) return false; + } + return true; +} + + +BOOL UserPropMgr::IsAlike(INode *node, BOOL MatchAll) { + TSTR buf; + GetUserPropBuffer(node,buf); + + BOOL oldProps = vProps; + vProps=false; + + hsStringTokenizer toker(buf," \r\n"); + toker.ParseQuotes(TRUE); + char *tok; + TSTR name; + TSTR value; + TSTR tval; + BOOL match = MatchAll; + bool isName = true; + tok = toker.next(); + while (tok && (match==MatchAll)) { + if (isName) { + if (*tok != '=') { + name = tok; + tok = toker.next(); + if (tok && *tok == '=') { + tok = toker.next(); + if (tok) value = tok; + else value = ""; + tok = toker.next(); + } else value = ""; + if (GetUserProp(node,name,tval)) match = IsMatch(value,tval); + else match = false; + continue; + } else isName = false; + } else { + isName = true; + } + tok=toker.next(); + } + + if (match==MatchAll) { + if (!vname.isNull()) match = IsMatch(vname,node->GetName()); + } + + vProps = oldProps; + return match; +} + +int UserPropMgr::GetUserPropCount(INode *node) { + TSTR buf; + + GetUserPropBuffer(node,buf); + + int numProps = 0; + + hsStringTokenizer toker(buf," \r\n"); + toker.ParseQuotes(TRUE); + char *tok; + bool isName = true; + while (tok=toker.next()) { + if (isName) { + if (*tok != '=') { + numProps++; + } else isName = false; + } else { + isName = true; + } + } + + return numProps; +} + +void UserPropMgr::GetUserPropBuffer(INode *node, TSTR &buf) { + if (vProps) buf = vbuf; + else if (node) node->GetUserPropBuffer(buf); + else buf = ""; +} + +void UserPropMgr::SetUserPropBuffer(INode *node, const TSTR &buf) +{ + // QuickTable invalidate + if (node && node == fQuickNode) + { + fQuickNode = nil; + } + + if (vProps) + { + vbuf = buf; + } + else if (node) + { + node->SetUserPropBuffer(buf); + node->NotifyDependents(FOREVER, PART_ALL, REFMSG_USERPROP); + } +} + +void UserPropMgr::SetUserPropFlagALL(const char *name, const BOOL setFlag, const Int32 hFlag) +{ + for (int i=0; iIsRootNode() || node->GetParentNode()->IsRootNode())) + node = node->GetParentNode(); + } + else + if (hFlag == kRoot) + { + while (!(node->IsRootNode() || node->GetParentNode()->IsRootNode())) + node = node->GetParentNode(); + } + return node; +} + + +void UserPropMgr::ClearUserProp(INode *node, const char *name, const Int32 hFlag) +{ + node = GetAncestorIfNeeded(node,hFlag); + + // QuickTable invalidate + if (node && node == fQuickNode) + { + fQuickNode = nil; + } + + TSTR buf; + GetUserPropBuffer(node,buf); + + hsStringTokenizer toker(buf," \r\n"); + toker.ParseQuotes(TRUE); + char *tok; + bool isName = true; + while (tok=toker.next()) + { + if (isName) + { + if (*tok != '=') + { + if (!stricmp(tok,name)) + { + char *tok2 = toker.next(); + if (tok2) + { + if (*tok2 == '=') + { + tok2 = toker.next(); + if (tok2) + { + tok2 = toker.next(); + if (tok2) + { + buf.remove(tok-toker.fString,tok2-tok); + } + else + { + buf.remove(tok-toker.fString); + } + } + else + { + buf.remove(tok-toker.fString); + } + } + else + { + buf.remove(tok-toker.fString,tok2-tok); + } + } + else + { + buf.remove(tok-toker.fString); + } + break; + } + } + else + { + isName = false; + } + } + else + { + isName = true; + } + } + if (vProps) + { + vbuf = buf; + } + else + { + node->SetUserPropBuffer(buf); + node->NotifyDependents(FOREVER, PART_ALL, REFMSG_USERPROP); + } +}; + +BOOL UserPropMgr::GetUserProp(INode *node, const char *name, TSTR &value, const Int32 hFlag) +{ + node = GetAncestorIfNeeded(node,hFlag); + + // QuickTable lookup + if (node && fQuickTable) + { + if (node != fQuickNode) + IBuildQuickTable(node); + return ICheckQuickEntry(name,value); + } + + TSTR buf; + GetUserPropBuffer(node,buf); + + hsStringTokenizer toker(buf," \r\n"); + toker.ParseQuotes(TRUE); + char *tok; + bool isName = true; + while (tok=toker.next()) + { + if (isName) + { + if (*tok != '=') + { + if (!stricmp(tok,name)) + { + tok = toker.next(); + if (tok && *tok == '=') + { + tok = toker.next(); + if (tok) value = tok; + else value = ""; + return true; + } + else + { + value = ""; + return true; + } + } + } + else + isName = false; + } + else + { + isName = true; + } + } + return false; +} + +void UserPropMgr::SetUserProp(INode *node, const char *name, const char *value, const Int32 hFlag) +{ + node = GetAncestorIfNeeded(node,hFlag); + + // QuickTable invalidate + if (node && node == fQuickNode) + { + fQuickNode = nil; + } + + TSTR buf; + GetUserPropBuffer(node,buf); + + hsStringTokenizer toker(buf," \r\n"); + toker.ParseQuotes(TRUE); + char *tok; + bool isName = true; + while (tok=toker.next()) + { + if (isName) + { + if (*tok != '=') + { + if (!stricmp(tok,name)) + { + char *tok2 = toker.next(); + if (tok2) + { + if (*tok2 == '=') + { + tok2 = toker.next(); + if (tok2) + { + tok2 = toker.next(); + if (tok2) + { + buf.remove(tok-toker.fString,tok2-tok); + } + else + { + buf.remove(tok-toker.fString); + } + } + else + { + buf.remove(tok-toker.fString); + } + } + else + { + buf.remove(tok-toker.fString,tok2-tok); + } + } + else + { + buf.remove(tok-toker.fString); + } + break; + } + } + else + { + isName = false; + } + } + else + { + isName = true; + } + } + if (buf.last('\n') < buf.length()-1) + { + // better start with a separator + buf += "\r\n"; + } + buf += name; + if (value && *value) + { + buf += " = "; + if (strchr(value,' ')) + { + buf += "\""; + buf += value; + buf += "\""; + } + else + { + buf += value; + } + } + buf += "\r\n"; + if (vProps) + { + vbuf = buf; + } + else + { + node->SetUserPropBuffer(buf); + node->NotifyDependents(FOREVER, PART_ALL, REFMSG_USERPROP); + } +} + + +BOOL UserPropMgr::GetUserPropString(INode *node, const char *name, TSTR &value, const Int32 hFlag) +{ + return GetUserProp(node,name,value,hFlag); +} +void UserPropMgr::SetUserPropString(INode *node, const char *name, const char *value, const Int32 hFlag) +{ + SetUserProp(node,name,value,hFlag); +} +BOOL UserPropMgr::GetUserPropFloat(INode *node, const char *name, float &value, const Int32 hFlag) +{ + TSTR valStr; + if (GetUserProp(node,name,valStr,hFlag)) + { + value = (float)atof(valStr); + return TRUE; + } + return FALSE; +} +void UserPropMgr::SetUserPropFloat(INode *node, const char *name, const float value, const Int32 hFlag) +{ + char valStr[50]; + if (sprintf(valStr,"%g",value)) SetUserProp(node,name,valStr,hFlag); +} +BOOL UserPropMgr::GetUserPropInt(INode *node, const char *name, int &value, const Int32 hFlag) +{ + TSTR valStr; + if (GetUserProp(node,name,valStr,hFlag)) { + value = atoi(valStr); + return TRUE; + } + return FALSE; +} +void UserPropMgr::SetUserPropInt(INode *node, const char *name, const int value, const Int32 hFlag) +{ + char valStr[50]; + if (sprintf(valStr,"%d",value)) SetUserProp(node,name,valStr,hFlag); +} + +BOOL UserPropMgr::UserPropExists(INode *node, const char *name, const Int32 hFlag) +{ + TSTR value; + return GetUserProp(node,name,value,hFlag); +} + +BOOL UserPropMgr::GetUserPropStringList(INode *node, const char *name, int &num, TSTR list[]) { + TSTR sdata; + if (UserPropMgr::GetUserPropString(node,name,sdata)) { + num=0; + hsStringTokenizer toker(sdata,", "); + char *tok; + while ( tok = toker.next() ) { + list[num] = tok; + num++; + } + return true; + } else return false; +} + +BOOL UserPropMgr::GetUserPropIntList(INode *node, const char *name, int &num, int list[]) { + TSTR sdata; + if (UserPropMgr::GetUserPropString(node,name,sdata)) { + num=0; + hsStringTokenizer toker(sdata,", "); + char *tok; + while ( tok = toker.next() ) { + list[num] = atoi(tok); + num++; + } + return true; + } else return false; +} + +BOOL UserPropMgr::GetUserPropFloatList(INode *node, const char *name, int &num, float list[]) { + TSTR sdata; + if (UserPropMgr::GetUserPropString(node,name,sdata)) { + num=0; + hsStringTokenizer toker(sdata,", "); + char *tok; + while ( tok = toker.next() ) { + list[num] = (float)atof(tok); + num++; + } + return true; + } else return false; +} + +BOOL UserPropMgr::GetUserPropStringALL(const char *name, TSTR &value, const Int32 hFlag) +{ + BOOL propSet = UserPropMgr::GetUserPropString(GetSelNode(0),name,value,hFlag); + + TSTR tvalue; + int i=1; + BOOL propMixed = FALSE; + while (i < GetSelNodeCount() && !propMixed) { + if (propSet ^ UserPropMgr::GetUserPropString(GetSelNode(i),name,tvalue,hFlag)) propMixed = TRUE; + propMixed = (!(value == tvalue)); + i++; + } + + return (!propMixed); +} +void UserPropMgr::SetUserPropStringALL(const char *name, const char *value, const Int32 hFlag) +{ + for (int i=0; iGetSelNodeCount() == 1) name = ip->GetSelNode(0)->GetName(); + else return false; + + return true; +} + +void UserPropMgr::SetNodeNameALL(const char *name) { + if (vProps) { + vname = name; + } else { + if (ip->GetSelNodeCount() > 1) { + TSTR uName; + for (int i=0; iGetSelNodeCount(); i++) { + uName = name; + ip->MakeNameUnique(uName); + ip->GetSelNode(i)->SetName(uName); + } + } else ip->GetSelNode(0)->SetName((char*)name); + } +} + + +void UserPropMgr::LoadVirtualProps(BOOL reset) { + if (reset) + { + vbuf = ""; + vname = ""; + } + vProps = true; +} +void UserPropMgr::DestroyVirtualProps() { + vProps = false; +} +BOOL UserPropMgr::IsVirtual() { + return vProps; +} + +int UserPropMgr::GetSelNodeCount() { + if (vProps) return 1; + else return ip->GetSelNodeCount(); +} +INode *UserPropMgr::GetSelNode(int i) { + if (vProps) return NULL; + else return ip->GetSelNode(i); +} + + +void UserPropMgr::OpenQuickTable() +{ + if (!fQuickTable) + { + fQuickTable = TRACKED_NEW hsHashTable(kQuickSize); + } + fQuickNode = nil; +} + +void UserPropMgr::CloseQuickTable() + { +#ifdef HS_DEBUGGING + if (fQuickNode && fQuickTable) + { + char str[256]; + sprintf(str,"%d Hash Collisions reported\n",fQuickTable->CollisionCount()); + hsStatusMessage(str); + } +#endif + + delete fQuickTable; + fQuickTable = nil; + fQuickNode = nil; + QuickPair::SetBuffer(nil); + } + +void UserPropMgr::IBuildQuickTable(INode* node) +{ + if (fQuickTable && fQuickNode != node) + { + fQuickNode = node; + + // clear old QuickTable + fQuickTable->clear(); + + // build new one + TSTR buf; + GetUserPropBuffer(node,buf); + + hsStringTokenizer toker(buf," \r\n"); + toker.ParseQuotes(TRUE); + + char *tok; + bool inName = false; + bool isName = true; + while ( inName || (tok=toker.next()) ) + { + if (isName) + { + if (*tok != '=') + { + QuickPair qPair; + qPair.SetKey(tok); + + tok = toker.next(); + if (tok && *tok == '=') + { + tok = toker.next(); + qPair.SetVal(tok); + + inName = false; + } + else + { + qPair.SetVal(nil); + inName = (tok != 0); + } + + fQuickTable->insert(qPair); + } + else + { + isName = false; + } + } + else + { + isName = true; + } + } + + // QuickPair owns the tok'd buffer now + QuickPair::SetBuffer(toker.fString); + toker.fString = nil; + } +} + +BOOL UserPropMgr::ICheckQuickEntry(const char *key, TSTR &value) +{ + QuickPair q; + q.SetKey(key); + hsHashTableIterator it = fQuickTable->find(q); + return it->GetVal(value); + } + + +char* UserPropMgr::QuickPair::fBuffer = nil; + +void UserPropMgr::QuickPair::SetBuffer(char* buf) +{ + delete [] fBuffer; + fBuffer = buf; + } + +UInt32 UserPropMgr::QuickPair::GetHash() const +{ + const char * k = fKey; + int len = k ? strlen(k) : 0; + int h; + for (h=len; len--;) + { + h = ((h<<5)^(h>>27))^tolower(*k++); + } + return h; +} + +hsBool UserPropMgr::QuickPair::GetVal(TSTR& value) + { + if (fKey) + { + value = fVal ? fVal : ""; + return true; + } + else + { + return false; + } + } + +bool UserPropMgr::QuickPair::operator==(const QuickPair& other) const +{ + return !_stricmp(fKey,other.fKey); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/UserPropMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/UserPropMgr.h new file mode 100644 index 00000000..9b6677fa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/UserPropMgr.h @@ -0,0 +1,143 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// UserPropMgr + +#ifndef _USERPROPMGR_H_ +#define _USERPROPMGR_H_ + +#include "Max.h" +#include "hsTypes.h" + +template class hsHashTable; + +class UserPropMgr { +public: + enum + { + kMe = 0, + kParent, + kRoot + }; + + UserPropMgr(); // No Default Constructor! + UserPropMgr(Interface *ip); + ~UserPropMgr(); + + NameMaker *nm; + + void SetUserPropFlag(INode *node, const char *name, const BOOL setFlag, const Int32 hFlag=kMe); + + void SelectUserPropFlagALL(INode *node, const char *name, const BOOL flag); + void ClearUserProp(INode *node, const char *name, const Int32 hFlag=kMe); + void ClearUserPropALL(const char *name, const Int32 hFlag=kMe); + void SetUserPropFlagALL(const char *name, const BOOL setFlag, const Int32 hFlag=kMe); + BOOL GetUserPropFlagALL(const char *name, BOOL &isSet, const Int32 hFlag=kMe); + + BOOL GetUserProp(INode *node, const char *name, TSTR &value, const Int32 hFlag=kMe); + void SetUserProp(INode *node, const char *name, const char *value, const Int32 hFlag=kMe); + BOOL UserPropExists(INode *node, const char *name, const Int32 hFlag=kMe); + + BOOL GetUserPropString(INode *node, const char *name, TSTR &value, const Int32 hFlag=kMe); + void SetUserPropString(INode *node, const char *name, const char *value, const Int32 hFlag=kMe); + BOOL GetUserPropFloat(INode *node, const char *name, float &value, const Int32 hFlag=kMe); + void SetUserPropFloat(INode *node, const char *name, const float value, const Int32 hFlag=kMe); + BOOL GetUserPropInt(INode *node, const char *name, int &value, const Int32 hFlag=kMe); + void SetUserPropInt(INode *node, const char *name, const int value, const Int32 hFlag=kMe); + BOOL GetUserPropStringList(INode *node, const char *name, int &num, TSTR list[]); + BOOL GetUserPropIntList(INode *node, const char *name, int &num, int list[]); + BOOL GetUserPropFloatList(INode *node, const char *name, int &num, float list[]); + + BOOL GetUserPropStringALL(const char *name, TSTR &value, const Int32 hFlag=kMe); + void SetUserPropStringALL(const char *name, const char *value, const Int32 hFlag=kMe); + BOOL GetUserPropStringListALL(const char *name, int &num, TSTR list[]); + BOOL GetUserPropIntListALL(const char *name, int &num, int *list); + BOOL GetUserPropFloatListALL(const char *name, int &num, float *list); + + BOOL GetNodeNameALL(TSTR &name); + void SetNodeNameALL(const char *name); + + void LoadVirtualProps(BOOL reset=true); + void DestroyVirtualProps(); + BOOL IsVirtual(); + + int GetSelNodeCount(); + INode *GetSelNode(int i); + + int GetUserPropCount(INode *node); + void GetUserPropBuffer(INode *node, TSTR &buf); + void SetUserPropBuffer(INode *node, const TSTR &buf); + + BOOL IsAlike(INode *node, BOOL MatchAll=true); + int CountAlike(BOOL MatchAll=true); + void DeSelectUnAlike(INode *node=NULL); + + Interface *GetInterface() { return ip; } + + void OpenQuickTable(); + void CloseQuickTable(); + +private: + INode* GetAncestorIfNeeded(INode* node, const Int32 hFlag); + void DeSelectWithOut(const char *name, const char *value); + void RecursiveSelectAll(INode *node = NULL); + int RecursiveCountAlike(INode *node = NULL, BOOL MatchAll=true); + BOOL IsMatch(const char *val1, const char *val2); + BOOL vProps; + TSTR vbuf; + TSTR vname; + + class QuickPair + { + public: + static void SetBuffer(char* buf); + protected: + static char* fBuffer; + const char* fKey; + const char* fVal; + public: + QuickPair() : fKey(nil), fVal(nil) { } + ~QuickPair() { } + + void SetKey(const char* k) { fKey = k; } + void SetVal(const char* v) { fVal = v; } + + UInt32 GetHash() const; + + hsBool GetVal(TSTR& value); + + bool operator==(const QuickPair& other) const; + }; + hsHashTable* fQuickTable; + static const UInt32 kQuickSize; + INode* fQuickNode; + void IBuildQuickTable(INode* node); + BOOL ICheckQuickEntry(const char *key, TSTR &value); + + Interface *ip; + +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsControlConverter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsControlConverter.cpp new file mode 100644 index 00000000..da63b077 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsControlConverter.cpp @@ -0,0 +1,2153 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsWindows.h" +#include +#include + +//#include "Max.h" +#include "../MaxMain/plMaxNode.h" +#include "stdmat.h" +#include "bmmlib.h" +#include "istdplug.h" +#include "texutil.h" +#include "iparamb2.h" +#include "modstack.h" +#include "keyreduc.h" + +#include "HeadSpin.h" + +#include "hsMaxLayerBase.h" +#include "../../../Sources/Plasma/PubUtilLib/plInterp/plController.h" +#include "../../../Sources/Plasma/PubUtilLib/plInterp/hsInterp.h" +#include "../MaxExport/plErrorMsg.h" +#include "UserPropMgr.h" +#include "hsConverterUtils.h" +#include "hsControlConverter.h" +#include "hsMaterialConverter.h" +#include "hsExceptionStack.h" +#include "../MaxExport/plErrorMsg.h" +#include "../../Tools/MaxComponent/plNoteTrackAnim.h" +#include "../MaxComponent/plCameraComponents.h" +#include "../MaxComponent/plAnimComponent.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../pnSceneObject/plCoordinateInterface.h" + +extern UserPropMgr gUserPropMgr; + +hsControlConverter& hsControlConverter::Instance() +{ + static hsControlConverter the_instance; + + return the_instance; +} + +hsControlConverter::hsControlConverter() : fConverterUtils(hsConverterUtils::Instance()) +{ +} + +void hsControlConverter::Init(plErrorMsg* msg) +{ + hsGuardBegin("hsControlConverter::Init"); + + fInterface = GetCOREInterface(); + fErrorMsg = msg; + + fTicksPerFrame = ::GetTicksPerFrame(); /*160*/ + fFrameRate = ::GetFrameRate(); /*30*/ + fTicksPerSec = fTicksPerFrame*fFrameRate; + + Interval interval = fInterface->GetAnimRange(); + fStartFrame = interval.Start()/fTicksPerFrame; + fEndFrame = interval.End()/fTicksPerFrame; + fNumFrames = fEndFrame-fStartFrame+1; + fAnimLength = (float)(fNumFrames-1)/fFrameRate; + + fWarned = false; + + fForceLocal = false; + + fSegStart = fSegEnd = -1; + + hsGuardEnd; +} + +void hsControlConverter::DeInit() +{ +} + +// dummy class that ApplyKeyReduction needs +class KRStatus : public KeyReduceStatus +{ + void Init(int total) {} + int Progress(int p) { return KEYREDUCE_CONTINUE; } +}; + +void hsControlConverter::ReduceKeys(Control *control, hsScalar threshold) +{ + if (control == nil || threshold <= 0) + return; + + KRStatus status; + if (control->IsLeaf()) + { + if (control->IsKeyable()) + { + IKeyControl *keyCont = GetKeyControlInterface(control); + if (keyCont->GetNumKeys() > 2) + { + IKey *key1 = (IKey*)TRACKED_NEW UInt8[keyCont->GetKeySize()]; + IKey *key2 = (IKey*)TRACKED_NEW UInt8[keyCont->GetKeySize()]; + keyCont->GetKey(0, key1); + keyCont->GetKey(keyCont->GetNumKeys() - 1, key2); + + // We want the interval to be one frame past the start and one frame + // before the end, to guarantee we leave the first and last keys + // alone. This will make sure a looping anim still lines up, and + // also prevents us from removing the controller entirely and thinking + // this channel just isn't animated at all. + // + // Also, I think this is a Max bug (since we're using Max's key reduce + // function, and the same error happens without our plugins), but if + // your range is only one frame short of the end of the anim, some + // bones get flipped on that 2nd-to-last frame. So you get a single + // frame with something like your arm pointing in the opposite + // direction at the elbow. + TimeValue start = key1->time + GetTicksPerFrame(); + TimeValue end = key2->time - 2 * GetTicksPerFrame(); + if (start < end) + { + Interval interval(start, end); + ApplyKeyReduction(control, interval, threshold, GetTicksPerFrame(), &status); + } + delete [] (UInt8*)key1; + delete [] (UInt8*)key2; + } + } + } + else + { + int i; + for (i = 0; i < control->NumSubs(); i++) + ReduceKeys((Control*)control->SubAnim(i), threshold); + } +} + +plController *hsControlConverter::ConvertTMAnim(plSceneObject *obj, plMaxNode *node, hsAffineParts *parts, + hsScalar start /* = -1 */, hsScalar end /* = -1 */) +{ + Control* maxTm = node->GetTMController(); + plController *tmc = hsControlConverter::Instance().MakeTransformController(maxTm, node, start, end); + + if (tmc) + { + const plCoordinateInterface *ci = obj->GetCoordinateInterface(); + if(ci) + { + const hsMatrix44& loc2Par = ci->GetLocalToParent(); + + gemAffineParts ap; + decomp_affine(loc2Par.fMap, &ap); + + AP_SET((*parts), ap); + } + } + + return tmc; +} + +hsBool hsControlConverter::HasKeyTimes(Control* ctl) +{ + hsGuardBegin("hsControlConverter::HasKeyTimes"); + + if( !ctl ) + { + return false; + } + + return (ctl->NumKeys() > 1); + hsGuardEnd; +} + + +plLeafController* hsControlConverter::MakeMatrix44Controller(StdUVGen* uvGen, const char* nodeName) +{ + hsGuardBegin("hsControlConverter::MakeMatrix44Controller"); + + if( !uvGen ) + return nil; + + ISetSegRange(-1, -1); + + Tab kTimes; + kTimes.ZeroCount(); + Control* uScaleCtl = nil; + Control* vScaleCtl = nil; + Control* uOffCtl= nil; + Control* vOffCtl = nil; + Control* rotCtl = nil; + GetControllerByName(uvGen, TSTR("U Offset"), uOffCtl); + GetControllerByName(uvGen, TSTR("V Offset"), vOffCtl); + GetControllerByName(uvGen, TSTR("U Tiling"), uScaleCtl); + GetControllerByName(uvGen, TSTR("V Tiling"), vScaleCtl); + GetControllerByName(uvGen, TSTR("Angle"), rotCtl); + + // new with Max R2, replacing "Angle", but it doesn't hurt to look... + Control* uAngCtl = nil; + Control* vAngCtl = nil; + Control* wAngCtl = nil; + GetControllerByName(uvGen, TSTR("U Angle"), uAngCtl); + GetControllerByName(uvGen, TSTR("V Angle"), vAngCtl); + GetControllerByName(uvGen, TSTR("W Angle"), wAngCtl); + + int i; + + CompositeKeyTimes(uOffCtl, kTimes); + CompositeKeyTimes(vOffCtl, kTimes); + CompositeKeyTimes(uScaleCtl, kTimes); + CompositeKeyTimes(vScaleCtl, kTimes); + CompositeKeyTimes(rotCtl, kTimes); + CompositeKeyTimes(uAngCtl, kTimes); + CompositeKeyTimes(vAngCtl, kTimes); + CompositeKeyTimes(wAngCtl, kTimes); + + const float kMaxRads = 30.f * hsScalarPI / 180.f; + MaxSampleAngles(nodeName, uAngCtl, kTimes, kMaxRads); + MaxSampleAngles(nodeName, vAngCtl, kTimes, kMaxRads); + MaxSampleAngles(nodeName, wAngCtl, kTimes, kMaxRads); + + if( kTimes.Count()<2 ) + { + return nil; + } + + plLeafController* ctrl = TRACKED_NEW plLeafController; + ctrl->AllocKeys(kTimes.Count(), hsKeyFrame::kMatrix44KeyFrame); + TimeValue resetTime = fConverterUtils.GetTime(fInterface); + for( i=0; i < kTimes.Count(); i++) + { + Interval v; + uvGen->Update(kTimes[i], v); + + // Get key + float secs = (float)kTimes[i]/fTicksPerSec; + int frameNum= kTimes[i]/fTicksPerFrame; + hsAssert(frameNum <= hsKeyFrame::kMaxFrameNumber, "Anim is too long."); + + fErrorMsg->Set((frameNum < fStartFrame || frameNum > fEndFrame), nodeName, + "Warning: Skipping keyframes outside of animation interval").CheckAndAsk(); + + hsMatrix44Key *key = ctrl->GetMatrix44Key(i); + StdUVGenToHsMatrix44(&key->fValue, uvGen, true); + key->fFrame = frameNum; + } + + return ctrl; + hsGuardEnd; +} + +plLeafController* hsControlConverter::MakeMatrix44Controller(Control* prsControl) +{ + hsGuardBegin("hsControlConverter::MakeMatrix44Controller"); + + ISetSegRange(-1, -1); + + Tab kTimes; + kTimes.ZeroCount(); + + Control* posCtl = nil; + Control* scaleCtl = nil; + Control* rotCtl = nil; + posCtl = prsControl->GetPositionController(); + rotCtl = prsControl->GetRotationController(); + scaleCtl = prsControl->GetScaleController(); + int i; + + CompositeKeyTimes(posCtl, kTimes); + CompositeKeyTimes(scaleCtl, kTimes); + CompositeKeyTimes(rotCtl, kTimes); + + if( kTimes.Count()<2 ) + { + return nil; + } + + plLeafController* ctrl = TRACKED_NEW plLeafController; + ctrl->AllocKeys(kTimes.Count(), hsKeyFrame::kMatrix44KeyFrame); + TimeValue resetTime = fConverterUtils.GetTime(fInterface);; + for( i=0; i < kTimes.Count(); i++) + { + // Get key + float secs = (float)kTimes[i]/fTicksPerSec; + int frameNum= kTimes[i]/fTicksPerFrame; + hsAssert(frameNum <= hsKeyFrame::kMaxFrameNumber, "Anim is too long."); + + Matrix3 maxXform; + maxXform.IdentityMatrix(); + Interval valid = FOREVER; + prsControl->GetValue(fConverterUtils.GetTime(fInterface), &maxXform, valid, CTRL_RELATIVE); + + hsMatrix44Key *key = ctrl->GetMatrix44Key(i); + Matrix3ToHsMatrix44(&maxXform, &key->fValue); + key->fFrame = frameNum; + } + + return ctrl; + hsGuardEnd; +} + + +// +// Create a plScalarController and store the nodes parm behavior in it. +// +plLeafController* hsControlConverter::MakeScalarController(Control* control, plMaxNode* node, + hsScalar start /* = -1 */, hsScalar end /* = -1 */) +{ + hsGuardBegin("hsControlConverter::MakeScalarController"); + + if (control == NULL) + return NULL; + + ISetSegRange(start, end); + + return ICreateScalarController(node, control); + + hsGuardEnd; +} + +plController* hsControlConverter::MakeColorController(Control* control, plMaxNode* node, + hsScalar start /* = -1 */, hsScalar end /* = -1 */) +{ + return MakePosController(control, node, start, end); +} +// +// Create a plPosController and store the nodes parm behavior in it. +// +plController* hsControlConverter::MakePosController(Control* control, plMaxNode* node, + hsScalar start /* = -1 */, hsScalar end /* = -1 */) +{ + hsGuardBegin("hsControlConverter::MakePosController"); + + if (control == NULL) + return NULL; + + ISetSegRange(start, end); + + plController* hsCont; + + if (control->IsLeaf()) + { + hsCont = ICreateSimplePosController(node, control); + } + else + { + hsCont = TRACKED_NEW plCompoundController; + fErrorMsg->Set(control->NumSubs()!=3, node->GetName(), "compound should have 3 subs").Check(); + + if (control->ClassID() == Class_ID(POSITIONNOISE_CONTROL_CLASS_ID,0) ) + { + MessageBox(GetActiveWindow(), node->GetName(), + "Warning: Noise position controller not supported. Ignoring.", MB_OK); + return hsCont; + } + + hsBool keep = false; + for (int i=0; i<3; i++) + { + Control* sub = (Control*)control->SubAnim(i); + plLeafController* sc = ICreateScalarController(node, sub); + ((plCompoundController*)hsCont)->SetController(i, sc); + if (sc) + { + keep = true; + } + } + if (!keep) + { + delete hsCont; + hsCont = nil; + } + } + + return hsCont; + hsGuardEnd; +} + +plController *hsControlConverter::MakeScaleController(Control *control, plMaxNode* node, + hsScalar start /* = -1 */, hsScalar end /* = -1 */) +{ + ISetSegRange(start, end); + + if (control->IsLeaf()) + { + // Simple scale: linear, bezier, tcb + plLeafController* sc = ICreateSimpleScaleController(node, control); + return sc; + } + else + { + // compound scale: noise + if (control->ClassID() == Class_ID(SCALENOISE_CONTROL_CLASS_ID,0) ) + { + MessageBox(GetActiveWindow(), node->GetName(), + "Warning: Noise scale controller not supported. Ignoring.", MB_OK); + } + } + return NULL; +} + +plController *hsControlConverter::MakeRotController(Control *control, plMaxNode *node, hsBool camRot /* = false */, + hsScalar start /* = -1 */, hsScalar end /* = -1 */) +{ + ISetSegRange(start, end); + + if (control->IsLeaf()) + { + // simple rot: linear, smooth, tcb + plLeafController* rc = ICreateSimpleRotController(node, control, camRot); + return rc; + } + else + { + // compound rot: euler or noise + if (control->NumSubs()) + { + if (control->ClassID() == Class_ID(ROTATIONNOISE_CONTROL_CLASS_ID,0) ) + { + MessageBox(GetActiveWindow(), node->GetName(), + "Warning: Noise rotation controller not supported. Ignoring.", MB_OK); + return nil; + } + if (fErrorMsg->Set(control->ClassID() != Class_ID(EULER_CONTROL_CLASS_ID,0), + node->GetName(), "Expecting euler rot ctrler").CheckAndAsk()) + return nil; + + if (fErrorMsg->Set(control->NumSubs() != 3, node->GetName(), "Rot compound controller should have 3 subcontrollers").CheckAndAsk()) + return nil; + + plCompoundController* rc = TRACKED_NEW plCompoundController; + int i; + for (i=0; i<3; i++) + { + Control* sub = (Control*)control->SubAnim(i); + plLeafController* sc = ICreateScalarController(node, sub); + rc->SetController(i, sc); + } + + // + // Check if we need to fixup euler due to missing subcontrollers + // + int numRotConts; + for(numRotConts=0,i=0; i<3; i++) + if (rc->GetController(i)) + numRotConts++; + if (numRotConts>0 && numRotConts<3) + { + // Someone has deleted 1 or 2 of the subcontrollers + // Add a key at the start and end of the missing tracks + Interval interval = fInterface->GetAnimRange(); + TimeValue startTime = interval.Start(); // in ticks + TimeValue endTime = interval.End(); // in ticks + + hsStatusMessage("Fixing up euler controller due to missing subcontrollers\n"); + for(i=0; i<3; i++) + { + if (!rc->GetController(i)) + { + Control* sub = (Control*)control->SubAnim(i); + if (!sub) + continue; + sub->AddNewKey(startTime, ADDKEY_INTERP); + sub->AddNewKey(endTime, ADDKEY_INTERP); + plLeafController* sc = ICreateScalarController(node, sub); + if (sc) + rc->SetController(i, sc); + else + { + fErrorMsg->Set(true, "Scalar Controller Error", "nil plScalar controller").Show(); + fErrorMsg->Set(); + return rc; + } + + } + } + for(numRotConts=0,i=0; i<3; i++) + if (rc->GetController(i)) + numRotConts++; + + if(numRotConts != 3) + { + fErrorMsg->Set(true, "Euler Fixup Error", "Euler fixup failed.").Show(); + fErrorMsg->Set(); + return rc; + } + } + else if (numRotConts == 0) // No sub controllers, no point in having the compound controller then + { + delete rc; + rc = nil; + } + + return rc; + } + } + return NULL; +} + +void hsControlConverter::ScalePositionController(plController* ctl, hsScalar scale) +{ + plLeafController* simp = plLeafController::ConvertNoRef(ctl); + plCompoundController* comp; + int i; + if( simp ) + { + for( i = 0; i < simp->GetNumKeys(); i++ ) + { + hsPoint3Key* key = simp->GetPoint3Key(i); + if (key) + { + key->fValue *= scale; + } + + hsBezPoint3Key* bezKey = simp->GetBezPoint3Key(i); + if (bezKey) + { + bezKey->fInTan *= scale; + bezKey->fOutTan *= scale; + bezKey->fValue *= scale; + } + } + } + else if( comp = plCompoundController::ConvertNoRef(ctl) ) + { + for( i = 0; i < 3; i++ ) + { + ScalePositionController(comp->GetController(i), scale); + } + } +} + +void hsControlConverter::MaxSampleAngles(const char* nodeName, Control* ctl, Tab& kTimes, hsScalar maxRads) +{ + hsGuardBegin("hsControlConverter::MaxSampleAngles"); + + if( !ctl ) + { + return; + } + + Tab rTimes; + Tab sTimes; + rTimes.ZeroCount(); + IGetControlSampleTimes(ctl, 0, ctl->NumKeys(), rTimes, maxRads); + + int iR; + for( iR = 0; iR < rTimes.Count(); iR++ ) + { + int iK; + for( iK = 0; iK < kTimes.Count(); iK++ ) + { + if( kTimes[iK] >= rTimes[iR] ) + break; + } + if( kTimes[iK] != rTimes[iR] ) + kTimes.Insert(iK, 1, rTimes.Addr(iR)); + } + + hsGuardEnd; +} + +plCompoundController *hsControlConverter::MakeTransformController(Control *control, plMaxNode *node, + hsScalar start /* = -1 */, hsScalar end /* = -1 */) +{ + hsGuardBegin("hsControlConverter::MakeTransformController"); + + if (!control) + return NULL; + + ISetSegRange(start, end); + + Class_ID cid = control->ClassID(); + if (cid == Class_ID(PRS_CONTROL_CLASS_ID,0) || + cid == Class_ID(LOOKAT_CONTROL_CLASS_ID,0)) + { + int n = control->NumSubs(); + if(n != 3) + { + fErrorMsg->Set(true, "Transform Controller Error", "Transform controller doesn't have 3 sub controllers").Show(); + fErrorMsg->Set(); + return NULL; + } + + plCompoundController *tmc = TRACKED_NEW plCompoundController; + for (int i=0; iSubAnim(i); + if (sub) + { + IConvertSubTransform(sub, control->SubAnimName(i), node, tmc, start, end); + } + } + + if (cid == Class_ID(LOOKAT_CONTROL_CLASS_ID,0)) + { + hsTArray kfArray; + IAddPartsKeys(control, &kfArray, node); + hsBool ignoreFOV = false; + for (int i = 0; i < node->NumAttachedComponents(); i++) + { + if (node->GetAttachedComponent(i)->ClassID() == ANIMCAM_CMD_CID) + { + plCameraAnimCmdComponent* pAnimComp = (plCameraAnimCmdComponent*)node->GetAttachedComponent(i); + ignoreFOV = pAnimComp->IgnoreFOV(); + break; + } + } + if (!ignoreFOV) + IExportAnimatedCameraFOV(node, &kfArray); + } + + if (tmc->GetPosController() || tmc->GetRotController() || tmc->GetScaleController()) + return tmc; + else + { + delete tmc; + return NULL; + } + } + return NULL; + + hsGuardEnd; +} + +void hsControlConverter::ISetSegRange(hsScalar start, hsScalar end) +{ + fSegStart = (start >= 0 ? fTicksPerSec * start : fInterface->GetAnimRange().Start()); + fSegEnd = (end >= 0 ? fTicksPerSec * end : fInterface->GetAnimRange().End()); +} + + +void hsControlConverter::IConvertSubTransform(Control *control, char *ctlName, plMaxNode *node, plCompoundController *tmc, + hsScalar start, hsScalar end) +{ + if (control) + { + ControllerType ct = IGetControlType(ctlName); + + switch(ct) + { + case ctrlTypePosition: + { + if(tmc->GetPosController() != nil) + { + fErrorMsg->Set(true, "Position Controller Error", "Non-nil position controller").Show(); + fErrorMsg->Set(); + return; + } + tmc->SetPosController(MakePosController(control, node, start, end)); + } + break; + case ctrlTypeRollAngle: + case ctrlTypeRotation: + { + if(tmc->GetRotController() != nil) + { + fErrorMsg->Set(true, "Position Controller Error", "Non-nil Rotation controller").Show(); + fErrorMsg->Set(); + return; + } + hsBool camRot = (ct == ctrlTypeRollAngle); + tmc->SetRotController(MakeRotController(control, node, camRot, start, end)); + } + break; + case ctrlTypeScale: + { + if(tmc->GetScaleController() != nil) + { + fErrorMsg->Set(true, "Scale Controller Error", "Non-nil Scale Controller").Show(); + fErrorMsg->Set(); + return; + } + tmc->SetScaleController(MakeScaleController(control, node, start, end)); + } + break; + default: + /* + if (plExp.GetLogFile()) + fprintf(plExp.GetLogFile(),"%s unknown ctrl type=%d\n", node->GetName(), (int)ct); + */ + break; + } + } +} + +// +// +// +plLeafController* hsControlConverter::ICreateSimpleRotController(plMaxNode* node, Control* control, hsBool camRot) +{ + hsGuardBegin("hsControlConverter::ICreateSimpleRotController"); + + return ICreateQuatController(node, control, true, camRot); + + hsGuardEnd; +} + +plLeafController* hsControlConverter::ICreateSimpleScaleController(plMaxNode* node, Control* control) +{ + hsGuardBegin("hsControlConverter::ICreateSimpleScaleController"); + + return ICreateScaleValueController(node, control); + + hsGuardEnd; +} + + +// +// +// +plLeafController* hsControlConverter::ICreateQuatController(plMaxNode* node, Control* control, bool rotation, hsBool camRot) +{ + hsGuardBegin("hsControlConverter::ICreateQuatController"); + + Int32 startIdx, endIdx; + IKeyControl* ikeys = GetKeyControlInterface(control); + if ( ikeys && IGetRangeCoverKeyIndices(node ? node->GetName() : nil, control, startIdx, endIdx)>1 ) + { + if(!(control->IsKeyable())) + { + fErrorMsg->Set(true, "Quat Controller Creation Error", "Control is not keyable.").Show(); + fErrorMsg->Set(); + return NULL; + } + + IKey* key=(IKey*)(new byte[ikeys->GetKeySize()]); + plLeafController* pc = TRACKED_NEW plLeafController; + + UInt8 compressLevel = node->GetAnimCompress(); + UInt8 keyType; + if (compressLevel == plAnimCompressComp::kCompressionHigh) + keyType = hsKeyFrame::kCompressedQuatKeyFrame32; + else if (compressLevel == plAnimCompressComp::kCompressionLow) + keyType = hsKeyFrame::kCompressedQuatKeyFrame64; + else + keyType = hsKeyFrame::kQuatKeyFrame; + + pc->AllocKeys(endIdx - startIdx + 1, keyType); + for(int i = startIdx; i <= endIdx; i++) + { + // Get key + ikeys->GetKey(i, key); + const float kMaxRads = hsScalarPI* 0.5f; + Tab kTimes; + kTimes.ZeroCount(); + if( rotation ) + IGetControlSampleTimes(control, i, i, kTimes, kMaxRads); + else + kTimes.Append(1, &key->time); + + int k; + for( k = 0; k < kTimes.Count(); k++ ) + { + if (keyType == hsKeyFrame::kQuatKeyFrame) + { + hsQuatKey *hsKey = pc->GetQuatKey(i - startIdx); + ICreateHSInterpKey(control, key, kTimes[k], hsKey, node, camRot); + } + else if (keyType == hsKeyFrame::kCompressedQuatKeyFrame64) + { + hsQuatKey tempKey; + ICreateHSInterpKey(control, key, kTimes[k], &tempKey, node, camRot); + hsCompressedQuatKey64 *compKey = pc->GetCompressedQuatKey64(i - startIdx); + compKey->fFrame = tempKey.fFrame; + compKey->SetQuat(tempKey.fValue); + } + else + { + hsQuatKey tempKey; + ICreateHSInterpKey(control, key, kTimes[k], &tempKey, node, camRot); + hsCompressedQuatKey32 *compKey = pc->GetCompressedQuatKey32(i - startIdx); + compKey->fFrame = tempKey.fFrame; + compKey->SetQuat(tempKey.fValue); + } + } + } + delete [] key; + + return pc; + } + + return nil; + hsGuardEnd; +} + + +// +// +// +plLeafController* hsControlConverter::ICreateScaleValueController(plMaxNode* node, Control* control) +{ + hsGuardBegin("hsControlConverter::ICreateScaleValueController"); + + //plMaxNode* xformParent = GetXformParent(node); + Int32 startIdx, endIdx; + IKeyControl* ikeys = GetKeyControlInterface(control); + if ( ikeys && IGetRangeCoverKeyIndices(node ? node->GetName() : nil, control, startIdx, endIdx)>1 ) + { + if(!(control->IsKeyable())) + { + fErrorMsg->Set(true, "Scale Value Controller Creation Error", "Control is not keyable").Show(); + fErrorMsg->Set(); + return NULL; + } + + IKey* key=(IKey*)(new byte [ikeys->GetKeySize()]); + plLeafController* pc = TRACKED_NEW plLeafController; + pc->AllocKeys(endIdx - startIdx + 1, GetKeyType(control)); + for(int i = startIdx; i <= endIdx; i++) + { + // Get key + ikeys->GetKey(i, key); + hsScaleKey *hsKey = pc->GetScaleKey(i - startIdx); + if (hsKey) + ICreateHSInterpKey(control, key, key->time, hsKey, node); + + hsBezScaleKey *bezKey = pc->GetBezScaleKey(i - startIdx); + if (bezKey) + ICreateHSInterpKey(control, key, key->time, bezKey, node); + } + delete [] key; + return pc; + } + + return nil; + hsGuardEnd; +} + +// +// +// +plLeafController* hsControlConverter::ICreateScalarController(plMaxNode* node, Control* control) +{ + hsGuardBegin("hsControlConverter::ICreateScalarController"); + + Int32 startIdx, endIdx; + IKeyControl* ikeys = GetKeyControlInterface(control); + if ( ikeys && IGetRangeCoverKeyIndices(node ? node->GetName() : nil, control, startIdx, endIdx)>1 ) + { + if(!(control->IsKeyable())) + { + fErrorMsg->Set(true, "Scale Value Controller Creation Error", "Control is not keyable").Show(); + fErrorMsg->Set(); + return NULL; + } + + IKey* key=(IKey*)(new byte [ikeys->GetKeySize()]); + plLeafController* pc = TRACKED_NEW plLeafController; + pc->AllocKeys(endIdx - startIdx + 1, GetKeyType(control)); + for(int i = startIdx; i <= endIdx; i++) + { + // Get key + ikeys->GetKey(i, key); + hsScalarKey *hsKey = pc->GetScalarKey(i - startIdx); + if (hsKey) + ICreateHSInterpKey(control, key, key->time, hsKey); + + hsBezScalarKey *bezKey = pc->GetBezScalarKey(i - startIdx); + if (bezKey) + ICreateHSInterpKey(control, key, key->time, bezKey); + } + + delete [] key; + return pc; + } + return nil; + hsGuardEnd; +} + + +// +// +// +plLeafController* hsControlConverter::ICreateSimplePosController(plMaxNode* node, Control* control) +{ + hsGuardBegin("hsControlConverter::ICreateSimplePosController"); + + IKeyControl* ikeys = GetKeyControlInterface(control); + Int32 startIdx, endIdx; + if ( ikeys && IGetRangeCoverKeyIndices(node ? node->GetName() : nil, control, startIdx, endIdx)>1 ) + { + if(!(control->IsKeyable())) + { + fErrorMsg->Set(true, "Simple Position Controller Creation Error", "Control is not keyable").Show(); + fErrorMsg->Set(); + return NULL; + } + + IKey* key=(IKey*)(new byte [ikeys->GetKeySize()]); + plLeafController* pc = TRACKED_NEW plLeafController; + pc->AllocKeys(endIdx - startIdx + 1, GetKeyType(control)); + for(int i = startIdx; i <= endIdx; i++) + { + // Get key + ikeys->GetKey(i, key); + hsPoint3Key *hsKey = pc->GetPoint3Key(i - startIdx); + if (hsKey) + ICreateHSInterpKey(control, key, key->time, hsKey); + + hsBezPoint3Key *bezKey = pc->GetBezPoint3Key(i - startIdx); + if (bezKey) + ICreateHSInterpKey(control, key, key->time, bezKey); + } + delete [] key; + return pc; + } + + return nil; + hsGuardEnd; +} + +// +// Create a hsKey and store the nodes LTM in it. +// Recurses along all subcontrollers. +// +int hsControlConverter::IAddPartsKeys(Control* control, + hsTArray * kfArray, + plMaxNode* node) +{ + hsGuardBegin("hsControlConverter::IAddPartsKeys"); + + Int32 startIdx, endIdx; + if (control->IsLeaf()) + { + IKeyControl* ikeys = GetKeyControlInterface(control); + int num = ikeys ? IGetRangeCoverKeyIndices(node ? node->GetName() : nil, control, startIdx, endIdx) : 0; + + if (num<2) + { + return 0; + } + + if(!control->IsKeyable()) + { + fErrorMsg->Set(true, "Add Parts Keys Creation Error", "Control is not keyable").Show(); + fErrorMsg->Set(); + return 0; + } + + int i,j; + + // + // Traverse all keys of controller + // + IKey* key=(IKey*)(new byte [ikeys->GetKeySize()]); + hsBool mb=false; + plMaxNode* xformParent = GetXformParent(node); + for(i = startIdx; i <= endIdx; i++) + { + // Get key + ikeys->GetKey(i, key); + hsScalar frameTime = key->time / GetTicksPerSec(); + int frameNum = key->time / GetTicksPerFrame(); + hsAssert(frameNum <= hsKeyFrame::kMaxFrameNumber, "Anim is too long."); + + // Check if we already have a hsG3dsMaxKey at this frameNum + int found=FALSE; + for(j=0; jGetCount(); j++) + { + hsG3DSMaxKeyFrame* k = &(*kfArray)[j]; + if (k->fFrame == frameNum) + { + found = TRUE; + break; + } + } + if (found==TRUE) + // Skip this key, there's one already there + continue; + + // + // Compute AffineParts + // + hsMatrix44 tXform = node->GetLocalToParent44(key->time); + + gemAffineParts ap; + decomp_affine(tXform.fMap, &ap); + hsAffineParts parts; + AP_SET(parts, ap); + + // Init new keyframe + hsG3DSMaxKeyFrame hKey; + hKey.fParts = parts; + hKey.fFrame = frameNum; + + // Add key to list + kfArray->Append(hKey); + } + delete [] key; + } + else + { + int i; + for (i = 0; i < control->NumSubs(); i++) + IAddPartsKeys((Control *)control->SubAnim(i), kfArray, node); + } + + return kfArray->GetCount(); + hsGuardEnd; +} + +Matrix3 hsControlConverter::StdUVGenToMatrix3(StdUVGen* uvGen) +{ + Matrix3 retVal(true); + if( uvGen ) + uvGen->GetUVTransform(retVal); + + retVal = Inverse(IFlipY()) * retVal * IFlipY(); + + return retVal; +} + +// +// +// returns 0 if identity, 1 otherwise +// takes into account the implicit transform of v -> 1-v in meshconvert:setuvs() +// +bool hsControlConverter::StdUVGenToHsMatrix44(hsMatrix44* hsMat, StdUVGen* uvGen, bool preserveOffset) +{ + hsGuardBegin("hsControlConverter::StdUVGenToHsMatrix44"); + + Matrix3 uvXform; + uvGen->GetUVTransform(uvXform); + + uvXform = Inverse(IFlipY()) * uvXform * IFlipY(); + Matrix3ToHsMatrix44(&uvXform, hsMat); + + if( !preserveOffset ) + { + int i; + for( i = 0; i < 2; i++ ) + { + if( fabsf(hsMat->fMap[i][3]) > 1.f ) + hsMat->fMap[i][3] -= hsScalar(int(hsMat->fMap[i][3])); + } + } + + return ( !hsMat->IsIdentity() ); + hsGuardEnd; +} + +void hsControlConverter::IGetControlSampleTimes(Control* control, int iLo, int iHi, Tab& kTimes, float maxRads) +{ + hsGuardBegin("hsControlConverter::IGetControlSampleTimes"); + + kTimes.ZeroCount(); + + if( !control ) + { + return; + } + + Class_ID cID = control->ClassID(); + SClass_ID sID = control->SuperClassID(); + + if( iLo < 0 ) + iLo = 0; + int num = control->NumKeys(); + iHi++; + if( iHi > num ) + iHi = num; + + + IKeyControl* ikeys = GetKeyControlInterface(control); + IKey* key=(IKey*)(new byte [ikeys->GetKeySize()]); + IKey* lastKey=(IKey*)(new byte [ikeys->GetKeySize()]); + + int i; + for( i = iLo; i < iHi; i++ ) + { + TimeValue t = control->GetKeyTime(i); + + if( !i ) + { + kTimes.Append(1, &t); + continue; + } + int nSamp = 1; + float rads = 0; + // following code will work, except that rotations are stored + // relative to previous key, so we'd need to end off with something + // like for i = 1; i < n; i++ ) + // key[i] = key[i-1] * key[i] + // or pass in the previous key and do it here. + /////////////////////////////////////// + ikeys->GetKey(i-1, lastKey); + ikeys->GetKey(i, key); + if( cID == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0) ) + { + ITCBRotKey* tcbRotKey = (ITCBRotKey*)key; + rads = tcbRotKey->val.angle; + } + else + if( cID == Class_ID(LININTERP_ROTATION_CLASS_ID, 0) ) + { + ILinRotKey* linRotKey = (ILinRotKey*)key; + + Point3 axis; + AngAxisFromQ(linRotKey->val, &rads, axis); + } + else + if( cID == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0) ) + { + IBezQuatKey* bezRotKey = (IBezQuatKey*)key; + + Point3 axis; + AngAxisFromQ(bezRotKey->val, &rads, axis); + } + else + if( cID == Class_ID(TCBINTERP_FLOAT_CLASS_ID, 0) ) + { + ITCBFloatKey* fKey = (ITCBFloatKey*)key; + + rads = fKey->val; + fKey = (ITCBFloatKey*)lastKey; + rads -= fKey->val; + } + else + if( cID == Class_ID(LININTERP_FLOAT_CLASS_ID, 0) ) + { + ILinFloatKey* fKey = (ILinFloatKey*)key; + rads = fKey->val; + fKey = (ILinFloatKey*)lastKey; + rads -= fKey->val; + } + else + if( cID == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID, 0) ) + { + IBezFloatKey* fKey = (IBezFloatKey*)key; + rads = fKey->val; + fKey = (IBezFloatKey*)lastKey; + rads -= fKey->val; + } + + nSamp = int(fabs(rads / maxRads) + 0.9f); + if( nSamp < 2 ) + { + kTimes.Append(1, &t); + continue; + } + + + TimeValue t0 = control->GetKeyTime(i-1); + int j; + for( j = 0; j < nSamp; j++ ) + { + float p = float(j+1) / float(nSamp); + TimeValue ti = t0 + TimeValue(p* (t - t0)); + kTimes.Append(1, &ti); + } + /////////////////////////////////////// + } + + delete [] key; + delete [] lastKey; + + hsGuardEnd; +} + +#if 0 + // following code will work, except that TCB (but not Euler) rotations are stored + // relative to previous key, so we'd need to end off with something + // like for i = 1; i < n; i++ ) + // key[i] = key[i-1]* key[i] + // or pass in the previous key and do it here. + Quat quat; + /////////////////////////////////////// + if( cID == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0) ) + { + ITCBRotKey* tcbRotKey = (ITCBRotKey*)mKey; + quat = QFromAngAxis(tcbRotKey->val.angle, tcbRotKey->val.axis); + } + else if( cID == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0) ) + { + IBezQuatKey* bezRotKey = (IBezQuatKey*)mKey; + quat = bezRotKey->val; + } + else if( cID == Class_ID(LININTERP_ROTATION_CLASS_ID, 0) ) + { + ILinRotKey* linRotKey = (ILinRotKey*)mKey; + quat = linRotKey->val; + } + else if( cID == Class_ID(EULER_CONTROL_CLASS_ID, 0) ) + { + float eul[3]; + + int i; + for( i = 0; i < 3; i++ ) + { + Control* subCntl = (Control*)control->SubAnim(i); + if( fErrorMsg->Set(!(subCntl && (subCntl->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID, 0))), node->GetName(), "Bad sub-controller type for animation").CheckAndAsk() ) + { + eul[i] = 0.f; + continue; + } + + ITCBFloatKey* fKey = (ITCBFloatKey*)mKey; + eul[i] = fKey->val; + } + + EulerToQuat(eul, quat); + } + hbKey->fValue.Set(-quat.x, -quat.y, -quat.z, quat.w); + + /////////////////////////////////////// +#endif // try getting from key + +// +// Create an hsKeyFrame from a 3DSMax key +// +Int32 hsControlConverter::ICreateHSInterpKey(Control* control, IKey* mKey, TimeValue keyTime, hsKeyFrame* baseKey, plMaxNode* node, hsBool rotQuat) +{ + hsGuardBegin("hsControlConverter::ICreateHSInterpKey"); + + Class_ID cID = control->ClassID(); + SClass_ID sID = control->SuperClassID(); + char* nodeName = node ? node->GetName() : nil; + + // BEZ + if (cID == Class_ID(HYBRIDINTERP_POSITION_CLASS_ID,0) || + cID == Class_ID(HYBRIDINTERP_COLOR_CLASS_ID,0) || + cID == Class_ID(HYBRIDINTERP_POINT3_CLASS_ID,0) ) + { + IBezPoint3Key*bKey = (IBezPoint3Key*)mKey; + hsBezPoint3Key* hbKey = (hsBezPoint3Key*)baseKey; + hbKey->fValue.Set(bKey->val.x, bKey->val.y, bKey->val.z); // color should be 0 to 1 + hbKey->fInTan.Set(bKey->intan.x, bKey->intan.y, bKey->intan.z); + hbKey->fOutTan.Set(bKey->outtan.x, bKey->outtan.y, bKey->outtan.z); + } + else if (cID == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID,0)) + { + IBezScaleKey*bKey = (IBezScaleKey*)mKey; + hsBezScaleKey* hbKey = (hsBezScaleKey*)baseKey; + hsMatrix44 tXform; + IGetUnEasedLocalTM(node, control, &tXform, keyTime); + gemAffineParts ap; + decomp_affine(tXform.fMap, &ap); + + hbKey->fValue.fS.Set(ap.k.x, ap.k.y, ap.k.z); + hbKey->fValue.fQ.Set(ap.u.x, ap.u.y, ap.u.z, ap.u.w); + hbKey->fInTan.Set(bKey->intan.x, bKey->intan.y, bKey->intan.z); + hbKey->fOutTan.Set(bKey->outtan.x, bKey->outtan.y, bKey->outtan.z); + } + + else if (cID == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0) && !rotQuat) + { + IBezFloatKey* bKey = (IBezFloatKey*)mKey; + hsBezScalarKey* hbKey = (hsBezScalarKey*)baseKey; + hbKey->fValue = bKey->val; + hbKey->fInTan = bKey->intan; + hbKey->fOutTan= bKey->outtan; + } + + else + // LIN + if (cID == Class_ID(LININTERP_POSITION_CLASS_ID,0)) + { + ILinPoint3Key*bKey = (ILinPoint3Key*)mKey; + hsPoint3Key* hbKey = (hsPoint3Key*)baseKey; + hbKey->fValue.Set(bKey->val.x, bKey->val.y, bKey->val.z); + } + else if (sID == SClass_ID(CTRL_ROTATION_CLASS_ID) || (cID == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0) && rotQuat)) // all rotations + { + hsQuatKey* hbKey = (hsQuatKey*)baseKey; + + // get rot values from Matrix and use quat slerp. + // could try getting rot values from key + hsMatrix44 tXform; + IGetUnEasedLocalTM(node, control, &tXform, keyTime); + gemAffineParts ap; + decomp_affine(tXform.fMap, &ap); + + hbKey->fValue.Set(ap.q.x, ap.q.y, ap.q.z, ap.q.w); + + IEnableEaseCurves(control, true); // re-enable + } + else if (cID == Class_ID(LININTERP_SCALE_CLASS_ID,0) ) + { + ILinScaleKey*bKey = (ILinScaleKey*)mKey; + hsScaleKey* hbKey = (hsScaleKey*)baseKey; + hsMatrix44 tXform; + IGetUnEasedLocalTM(node, control, &tXform, keyTime); + gemAffineParts ap; + decomp_affine(tXform.fMap, &ap); + + hbKey->fValue.fS.Set(ap.k.x, ap.k.y, ap.k.z); + hbKey->fValue.fQ.Set(ap.u.x, ap.u.y, ap.u.z, ap.u.w); + } + else + if (cID == Class_ID(LININTERP_FLOAT_CLASS_ID,0) ) + { + ILinFloatKey* bKey = (ILinFloatKey*)mKey; + hsScalarKey* hbKey = (hsScalarKey*)baseKey; + hbKey->fValue = bKey->val; + } + else + // TCB + if (cID == Class_ID(TCBINTERP_POSITION_CLASS_ID,0) || + cID == Class_ID(TCBINTERP_POINT3_CLASS_ID, 0) ) + { + ITCBPoint3Key*bKey = (ITCBPoint3Key*)mKey; + hsPoint3Key* hbKey = (hsPoint3Key*)baseKey; + hbKey->fValue.Set(bKey->val.x, bKey->val.y, bKey->val.z); + } + else + if (cID == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0) ) + { + ITCBFloatKey* bKey = (ITCBFloatKey*)mKey; + hsScalarKey* hbKey = (hsScalarKey*)baseKey; + hbKey->fValue = bKey->val; + } + else if (cID == Class_ID(TCBINTERP_SCALE_CLASS_ID,0) ) + { + ITCBScaleKey*bKey = (ITCBScaleKey*)mKey; + hsScaleKey* hbKey = (hsScaleKey*)baseKey; + hsMatrix44 tXform; + IGetUnEasedLocalTM(node, control, &tXform, keyTime); + gemAffineParts ap; + decomp_affine(tXform.fMap, &ap); + + hbKey->fValue.fS.Set(ap.k.x, ap.k.y, ap.k.z); + hbKey->fValue.fQ.Set(ap.u.x, ap.u.y, ap.u.z, ap.u.w); + } + else + { + fErrorMsg->Set(true, nodeName, "unknown controller type?").Check(); + return 0; // failed + } + + int frameNum = keyTime / GetTicksPerFrame(); + hsAssert(frameNum <= hsKeyFrame::kMaxFrameNumber, "Anim is too long."); + baseKey->fFrame = frameNum; + + return 1; // did it + hsGuardEnd; +} + +UInt8 hsControlConverter::GetKeyType(Control* control, hsBool rotQuat) +{ + Class_ID cID = control->ClassID(); + SClass_ID sID = control->SuperClassID(); + + if (cID == Class_ID(HYBRIDINTERP_POSITION_CLASS_ID,0) || + cID == Class_ID(HYBRIDINTERP_COLOR_CLASS_ID,0) || + cID == Class_ID(HYBRIDINTERP_POINT3_CLASS_ID,0) ) + { + return hsKeyFrame::kBezPoint3KeyFrame; + } + else if (cID == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID,0)) + { + return hsKeyFrame::kBezScaleKeyFrame; + } + else if (cID == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0) && !rotQuat) + { + return hsKeyFrame::kBezScalarKeyFrame; + } + else if (cID == Class_ID(LININTERP_POSITION_CLASS_ID,0)) + { + return hsKeyFrame::kPoint3KeyFrame; + } + else if (sID == SClass_ID(CTRL_ROTATION_CLASS_ID) || (cID == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0) && rotQuat)) // all rotations + { + return hsKeyFrame::kQuatKeyFrame; + } + else if (cID == Class_ID(LININTERP_SCALE_CLASS_ID,0) ) + { + return hsKeyFrame::kScaleKeyFrame; + } + else if (cID == Class_ID(LININTERP_FLOAT_CLASS_ID,0) ) + { + return hsKeyFrame::kScalarKeyFrame; + } + else if (cID == Class_ID(TCBINTERP_POSITION_CLASS_ID,0) || + cID == Class_ID(TCBINTERP_POINT3_CLASS_ID, 0)) + { + return hsKeyFrame::kPoint3KeyFrame; + } + else if (cID == Class_ID(TCBINTERP_FLOAT_CLASS_ID,0) ) + { + return hsKeyFrame::kScalarKeyFrame; + } + else if (cID == Class_ID(TCBINTERP_SCALE_CLASS_ID,0) ) + { + return hsKeyFrame::kScaleKeyFrame; + } + else + { + return hsKeyFrame::kUnknownKeyFrame; + } +} + +// +// +// +Int32 hsControlConverter::IGetRangeCoverKeyIndices(char* nodeName, Control* cont, Int32 &start, Int32 &end) +{ + hsGuardBegin("hsControlConverter::IGetRangeCoverKeyIndices"); + + if (!cont) + { + return 0; + } + + IKeyControl* keys = GetKeyControlInterface(cont); + int numKeys=keys->GetNumKeys(); + if (numKeys == 0) + return 0; + + IKey* key=(IKey*)(new byte [keys->GetKeySize()]); + + start = numKeys; + for (int i=0; iGetKey(i, key); + if (IIsKeyInRange(key)) + { + if (start > i) + start = i; + end = i; + } + } + + // If the keys aren't on the exact endpoints of our range, we need to include the next or previous + // one so that we have data for any time within our range. + + if (start == numKeys) // No keys inside the range + { + for (int i = 0; i < numKeys; i++) + { + keys->GetKey(i, key); + if (key->time < fSegStart) + start = i; + } + + if ((start == numKeys) || // no keys before the start time + (start == numKeys - 1)) // no keys after end (since the latest key is before start) + { + delete [] key; + return 0; + } + + end = start + 1; + } + else + { + keys->GetKey(start, key); + if (key->time > fSegStart && start > 0) + start -= 1; + + keys->GetKey(end, key); + if (key->time < fSegEnd && end < numKeys - 1) + end += 1; + } + + delete [] key; + + //fErrorMsg->Set(numInRange>1 && numInRange!=numKeys, nodeName ? nodeName : "?", +// "Warning: Object has controller with keyframes outside of animation interval").CheckAndAsk(); + + + return end - start + 1; + hsGuardEnd; +} + +// +// find the closest ancestor (if any) that is animated. +// this node's space will be our local space. +// +plMaxNode* hsControlConverter::GetXformParent(plMaxNode* node) +{ + hsGuardBegin("hsControlConverter::GetXformParent"); + + while( node && (node = (plMaxNode *)node->GetParentNode()) && + !(ForceOrigin(node) || ForceLocal(node) || IsAnimated(node)) ); + + return node; + hsGuardEnd; +} + +// ########################################################################### +// Note that ForceWorldSpace Overrides ForceOrigin which Overrides ForceLocal +// ########################################################################### +hsBool hsControlConverter::ForceWorldSpace(plMaxNode* node) +{ + hsGuardBegin("hsControlConverter::ForceWorldSpace"); + + return false; + + hsGuardEnd; +} + +// ########################################################################### +// Note that ForceWorldSpace Overrides ForceOrigin which Overrides ForceLocal +// ########################################################################### +hsBool hsControlConverter::ForceOrigin(plMaxNode* node) +{ + hsGuardBegin("hsControlConverter::ForceOrigin"); + + char* nn = node->GetName(); + + if (node->IsRootNode()) + { + return false; + } + + if (ForceWorldSpace(node)) + { + return false; + } + + return false; + hsGuardEnd; +} + +// ########################################################################### +// Note that ForceWorldSpace Overrides ForceOrigin which Overrides ForceLocal +// This is significant because things that require ForceLocal because they are +// animated or what-not, are still okay with ForceOrigin, but not v.v. +// ########################################################################### +hsBool hsControlConverter::ForceLocal(plMaxNode* node) +{ + hsGuardBegin("hsControlConverter::ForceLocal"); + + + const char* nn = node->GetName(); + + if( !node->CanConvert() ) + return false; + + if (node->IsRootNode()) + { + return false; + } + + if( node->GetForceLocal() ) + return true; + + if( ISkinNode((plMaxNode*)node->GetParentNode()) ) + { + node->SetForceLocal(true); + return true; + } + + Object* objectRef = node->GetObjectRef(); + if (fConverterUtils.IsInstanced(objectRef) && + gUserPropMgr.UserPropExists(node,"AllowInstancing")) + { + node->SetForceLocal(true); + return true; + } + + + return false; + + hsGuardEnd; +} + + +hsBool hsControlConverter::IsAnimated(plMaxNode* node) +{ + hsGuardBegin("hsControlConverter::IsAnimated"); + + return node->IsAnimated(); + + hsGuardEnd; +} + +hsBool hsControlConverter::OwnsMaterialCopy(plMaxNode* node) +{ + hsGuardBegin("hsControlConverter::OwnsMaterialCopy"); + + return false; + hsGuardEnd; +} + +hsBool hsControlConverter::HasFrameEvents(plMaxNode *node) +{ + hsGuardBegin("hsSceneConverter::HasFrameEvents"); + + if (!node) + { + return false; + } + + TSTR sdata; + if (gUserPropMgr.GetUserPropString(node,"FESound",sdata) || + gUserPropMgr.GetUserPropString(node,"FESoundEmitter",sdata) || + gUserPropMgr.GetUserPropString(node,"FEGrab",sdata) || + gUserPropMgr.GetUserPropString(node,"FEDrop",sdata) || + gUserPropMgr.GetUserPropString(node,"FEEventOn",sdata) || + gUserPropMgr.GetUserPropString(node,"FEEventOnPermanent",sdata) || + gUserPropMgr.GetUserPropString(node,"FEEventOff",sdata) || + gUserPropMgr.GetUserPropString(node,"FEActor",sdata)) + { + return false; + } + + return false; + hsGuardEnd; +} + +hsBool hsControlConverter::GetControllerByName(Animatable* anim, TSTR &name, Control* &ctl) +{ + hsGuardBegin("hsControlConverter::GetControllerByName"); + + if( anim ) + { + int nSub = anim->NumSubs(); + int i; + for( i = 0; i < nSub; i++ ) + { + if (anim->SubAnim(i)==nil) + continue; + TSTR subName = anim->SubAnimName(i); + if( subName == name ) + { + fErrorMsg->Set(!anim->SubAnim(i), name, "Found controller by name, but nobody home").Check(); + ctl = GetControlInterface(anim->SubAnim(i)); + return true; + } + else if( GetControllerByName(anim->SubAnim(i), name, ctl) ) + { + return true; + } + } + } + ctl = nil; + + return false; + hsGuardEnd; +} + +Control *hsControlConverter::GetControllerByID(IParamBlock2 *pblock, int paramID) +{ + hsGuardBegin("hsControlConverter::GetControllerByID"); + + if (pblock) + { + int animIdx = pblock->GetAnimNum(paramID); + if (animIdx != -1) + { + Animatable* anim = pblock->SubAnim(animIdx); + if (anim) + return GetControlInterface(anim); + } + } + + return NULL; + hsGuardEnd; +} + +void hsControlConverter::CompositeKeyTimes(Control* ctl, Tab &time) +{ + hsGuardBegin("hsControlConverter::CompositeKeyTimes"); + + if( !ctl ) + { + return; + } + + int curTime = 0; + int i; + for( i = 0; i < ctl->NumKeys(); i++ ) + { + TimeValue t = ctl->GetKeyTime(i); + // advance times + while( (curTime < time.Count())&&(t > time[curTime]) ) + curTime++; + // if past end, append it + if( curTime >= time.Count() ) + time.Append(1, &t); + else // if less + if( t < time[curTime] ) + time.Insert(curTime++, 1, &t); + // already there, skip + } + + hsGuardEnd; +} + +// +// +// +ControllerType hsControlConverter::IGetControlType(TSTR ctrlName) +{ + hsGuardBegin("hsControlConverter::IGetControlType"); + + ControllerType ct = ctrlTypeUnknown; + if (ctrlName && !strcmp(ctrlName, "Ease Curve")) + { + ct = ctrlTypeEase; + } + else if (ctrlName && !strcmp(ctrlName, "Mult Curve")) + { + ct = ctrlTypeMult; + } + else if (ctrlName && !strcmp(ctrlName, "Position")) + { + ct = ctrlTypePosition; + } + else if (ctrlName && !strcmp(ctrlName, "Rotation")) + { + ct = ctrlTypeRotation; + } + else if (ctrlName && !strcmp(ctrlName, "Scale")) + { + ct = ctrlTypeScale; + } + else if (ctrlName && !strcmp(ctrlName, "Transform")) + { + ct = ctrlTypeTransform; + } + else if (ctrlName && !strcmp(ctrlName, "Roll Angle")) + { + ct = ctrlTypeRollAngle; + } +#if 0 + // biped controllers are good for nothing + else if (ctrlName && !strcmp(ctrlName, "Vertical")) + { + ct = ctrlTypeVert; + } + else if (ctrlName && !strcmp(ctrlName, "Horizontal")) + { + ct = ctrlTypeHoriz; + } + else if (ctrlName && !strcmp(ctrlName, "Turning")) + { + ct = ctrlTypeTurn; + } +#endif + + return ct; + hsGuardEnd; +} + +bool hsControlConverter::IIsKeyTimeInRange(TimeValue time) +{ + hsGuardBegin("hsControlConverter::IIsKeyTimeInRange"); + + Interval interval = fInterface->GetAnimRange(); + TimeValue startTime = interval.Start(); // in ticks + TimeValue endTime = interval.End(); // in ticks + + + return (time >= startTime && time <= endTime) && // Max's range + (time >= fSegStart && time <= fSegEnd); + + hsGuardEnd; +} + +bool hsControlConverter::IIsKeyInRange(IKey* key) +{ + hsGuardBegin("hsControlConverter::IIsKeyInRange"); + + return IIsKeyTimeInRange(key->time); + hsGuardEnd; +} + +void hsControlConverter::IGetUnEasedLocalTM(plMaxNode* node, Control* control, hsMatrix44* out, TimeValue time) +{ + hsGuardBegin("hsControlConverter::IGetUnEasedLocalTM"); + + // disable easing so that GetTM won't give us an eased answer. + // we want the uneased "key" value, so that we can do the easing ourselves + IEnableEaseCurves(control, false); + + // Make scale key match nodeTM + fErrorMsg->Set(!node, "ICreateHSInterpKey", "nil node").Check(); + *out = node->GetLocalToParent44(time); + + IEnableEaseCurves(control, true); // re-enable + + hsGuardEnd; +} + +// +// +// +void hsControlConverter::IEnableEaseCurves(Animatable* control, bool enable) +{ + hsGuardBegin("hsControlConverter::IEnableEaseCurves"); + + if (control) + { + int n = control->NumSubs(); + for (int i=0; iSubAnim(i), enable); + + EaseCurveList* el = GetEaseListInterface(control); + if (el) + { + for(int i=0; iNumEaseCurves(); i++) + { + if (enable) + el->EnableEaseCurve(i); + else + el->DisableEaseCurve(i); + } + } + } + + hsGuardEnd; +} + +// We don't actually use this ID for a plugin, just to keep track of our AppData chunks +#define CONTROL_CONVERTER_CID Class_ID(0xae807d2, 0x523808c7) + +Matrix3 hsControlConverter::IFlipY() +{ + hsGuardBegin("hsControlConverter::IFlipY"); + + Matrix3 xfm = ScaleMatrix(Point3(1.0, -1.0, 1.0)) * TransMatrix(Point3(0.0, 1.0, 0.0)); + + return xfm; + hsGuardEnd; +} + +bool hsControlConverter::ISkinNode(plMaxNode* node) +{ + hsGuardBegin("hsControlConverter::ISkinNode"); + + /* + if( fForceSkinning ) + return true; + if( fForceNoSkinning ) + return false; + */ + if (gUserPropMgr.UserPropExists(node,"MATSkin")) + { + return true; + } + + if (gUserPropMgr.UserPropExists(node,"MATSkinColor")) + { + return true; + } + + if( node && node->GetName() && strstr(node->GetName(), "%skin") ) + { + return true; + } + + return false; + hsGuardEnd; +} + +void hsControlConverter::Matrix3ToHsMatrix44(Matrix3* m3, hsMatrix44* hsM) +{ + hsGuardBegin("hsControlConverter::Matrix3ToHsMatrix44"); + + MRow* m = m3->GetAddr(); + + hsM->Reset(); + hsM->fMap[0][0] = m[0][0]; + hsM->fMap[0][1] = m[1][0]; + hsM->fMap[0][2] = m[2][0]; + hsM->fMap[0][3] = m[3][0]; + + hsM->fMap[1][0] = m[0][1]; + hsM->fMap[1][1] = m[1][1]; + hsM->fMap[1][2] = m[2][1]; + hsM->fMap[1][3] = m[3][1]; + + hsM->fMap[2][0] = m[0][2]; + hsM->fMap[2][1] = m[1][2]; + hsM->fMap[2][2] = m[2][2]; + hsM->fMap[2][3] = m[3][2]; + + hsM->NotIdentity(); + + hsGuardEnd; +} + +//// IGetEditableMeshKeyTimes //////////////////////////////////////////////// +// Moved here after hsMeshConverter was obliterated. Only used in this class +// anyway... + +hsBool hsControlConverter::IGetEditableMeshKeyTimes( plMaxNode *node, Tab × ) +{ + hsGuardBegin( "hsControlConverter::GetEditableMeshKeyTimes" ); + + Animatable *anim; + if( IGetSubAnimByName(node, TSTR("Object (Editable Mesh)"), anim) ) + { + fErrorMsg->Set(!anim, node->GetName(), "First she says yes, then she says no.").Check(); + + int i; + int nSub = anim->NumSubs(); + for( i = 0; i < nSub; i++ ) + { + if( anim->SubAnim(i) ) + { + Control *ctl = GetControlInterface(anim->SubAnim(i)); + hsControlConverter::Instance().CompositeKeyTimes(ctl, times); + } + } + } + return times.Count() > 0; + hsGuardEnd; +} + +//// IGetGeomKeyTimes //////////////////////////////////////////////////////// +// Moved here after hsMeshConverter was obliterated. Only used in this class +// anyway... + +hsBool hsControlConverter::IGetGeomKeyTimes( plMaxNode *node, Tab × ) +{ + hsGuardBegin( "hsControlConverter::GetGeomKeyTimes" ); + + char *dgbNodeName = node->GetName(); + Object *obj = node->GetObjectRef(); + if( !obj ) + return false; + IDerivedObject *derObj = nil; + if( obj->CanConvertToType(derivObjClassID) ) + { + derObj = (IDerivedObject *)obj->ConvertToType(fConverterUtils.GetTime(fInterface), derivObjClassID); + } + else + { + SClass_ID objID = obj->SuperClassID(); + SClass_ID genID(GEN_DERIVOB_CLASS_ID); + if( !(obj->SuperClassID() == SClass_ID(GEN_DERIVOB_CLASS_ID)) ) + return false; + if( objID != genID ) + return false; + derObj = (IDerivedObject *)obj; + } + + int i; + int nKeys = 0; + for( i = 0; i < derObj->NumModifiers(); i++ ) + { + Modifier *mod = derObj->GetModifier(i); + char *dbgModName = mod->GetName(); + if( mod ) + { + ChannelMask mask = mod->ChannelsChanged(); + if( mask & GEOM_CHANNEL ) + { + IGetGeomKeyTimesRecur(mod, times); + } + } + } + + return (times.Count() > 0); + hsGuardEnd; +} + +//// IGetGeomKeyTimesRecur /////////////////////////////////////////////////// +// Moved here after hsMeshConverter was obliterated. Only used in this class +// anyway... + +void hsControlConverter::IGetGeomKeyTimesRecur( Animatable *anim, Tab × ) +{ + hsGuardBegin( "hsControlConverter::IGetGeomKeyTimesRecur" ); + + Control * ctl = GetControlInterface(anim); + hsControlConverter::Instance().CompositeKeyTimes(ctl, times); + + int iSub; + int nSub = anim->NumSubs(); + for( iSub = 0; iSub < nSub; iSub++ ) + { + if( anim->SubAnim(iSub) ) + IGetGeomKeyTimesRecur(anim->SubAnim(iSub), times); + } + + hsGuardEnd; +} + +//// IGetGeomKeyTimesRecur /////////////////////////////////////////////////// +// Moved here after hsMeshConverter was obliterated. Only used in this class +// anyway... + +hsBool hsControlConverter::IGetSubAnimByName( Animatable *anim, TSTR &name, Animatable *&subAnim ) +{ + hsGuardBegin( "hsControlConverter::IGetSubAnimByName" ); + + if( anim ) + { + int nSub = anim->NumSubs(); + int i; + for( i = 0; i < nSub; i++ ) + { + if (anim->SubAnim(i)==nil) + continue; + TSTR subName = anim->SubAnimName(i); + if( subName == name ) + { + fErrorMsg->Set(!anim->SubAnim(i), name, "Found controller by name, but nobody home").Check(); + subAnim = anim->SubAnim(i); + return true; + } + else if( IGetSubAnimByName(anim->SubAnim(i), name, subAnim) ) + { + return true; + } + } + } + subAnim = nil; + return false; + hsGuardEnd; +} + +// bad craziness, isolated here. +#include "plConvert.h" +#include "plgDispatch.h" +#include "../MaxComponent/plAnimComponent.h" +#include "../MaxComponent/plCameraComponents.h" +#include "../../../Sources/Plasma/NucleusLib/pnMessage/plCameraMsg.h" +#include "../../../Sources/Plasma/PubUtilLib/plMessage/plAnimCmdMsg.h" +#include "../../../Sources/Plasma/FeatureLib/pfCamera/plCameraModifier.h" +#include "../../../Sources/Plasma/NucleusLib/pnSceneObject/plSceneObject.h" + +void hsControlConverter::IExportAnimatedCameraFOV(plMaxNode* node, hsTArray * kfArray) +{ + // grab the FOV settings at each keyframe here + // create callback messages for the animation to send to the camera + // to interpolate to the correct FOV at each keyframe + + plAnimComponentBase* pAnim = nil; + int count = node->NumAttachedComponents(); + int i; + for (i = 0; i < count; i++) + { + plComponentBase *comp = node->GetAttachedComponent(i); + if (comp->ClassID() == ANIM_COMP_CID || comp->ClassID() == ANIM_GROUP_COMP_CID) + { + pAnim = (plAnimComponentBase*)comp; + break; + } + } + + plCamera1Component* pCamComp = nil; + for (i = 0; i < count; i++) + { + plComponentBase *comp = node->GetAttachedComponent(i); + if (comp->ClassID() == FIXEDCAM_CID) + { + pCamComp = (plCamera1Component*)comp; + break; + } + } + + const plCameraModifier1* pCamMod = nil; + count = node->GetSceneObject()->GetNumModifiers(); + for (i=0; i < count; i++) + { + pCamMod = plCameraModifier1::ConvertNoRef(node->GetSceneObject()->GetModifier(i)); + if (pCamMod) + break; + + } + plCameraMsg* pCamMsg = TRACKED_NEW plCameraMsg; + pCamMsg->SetCmd(plCameraMsg::kSetAnimated); + pCamMsg->AddReceiver(pCamMod->GetKey()); + plConvert::Instance().AddMessageToQueue(pCamMsg); + Object* obj = node->EvalWorldState(hsConverterUtils::Instance().GetTime(node->GetInterface())).obj; + GenCamera* theCam; + hsTArray fovW; + hsTArray fovH; + for (i=0; i < kfArray->Count(); i++) + { + TimeValue t = TimeValue(GetTicksPerFrame() * (kfArray[0][i].fFrame)); + theCam = (GenCamera *) obj->ConvertToType(t, Class_ID(LOOKAT_CAM_CLASS_ID, 0)); + float FOVvalue= 0.0; //Currently in Radians + // radians + FOVvalue = theCam->GetFOV(t); + // convert + FOVvalue = FOVvalue*(180/3.141592); + int FOVType = theCam->GetFOVType(); + hsScalar wDeg, hDeg; + switch(FOVType) + { + case 0: // FOV_W + { + wDeg = FOVvalue; + hDeg = (wDeg*3)/4; + } + break; + case 1: // FOV_H + { + hDeg = FOVvalue; + wDeg = (hDeg*4)/3; + } + break; + } + fovW.Append(wDeg); + fovH.Append(hDeg); + + } + for (i=0; i < kfArray->Count(); i++) + { + + plCameraMsg* pFOVMsg = TRACKED_NEW plCameraMsg; + plCameraConfig* pCfg = pFOVMsg->GetConfig(); + + if (i == kfArray->Count() - 1) + { + pCfg->fFOVh = fovH[0]; + pCfg->fFOVw = fovW[0]; + pCfg->fAccel = kfArray[0][0].fFrame / MAX_FRAMES_PER_SEC; + } + else + { + pCfg->fFOVh = fovH[i + 1]; + pCfg->fFOVw = fovW[i + 1]; + pCfg->fAccel = kfArray[0][i + 1].fFrame / MAX_FRAMES_PER_SEC; + } + + + pFOVMsg->SetCmd(plCameraMsg::kAddFOVKeyframe); + pFOVMsg->AddReceiver(pCamMod->GetKey()); + + plEventCallbackMsg* pCall = TRACKED_NEW plEventCallbackMsg; + pCall->fEvent = kTime; + pCall->fEventTime = kfArray[0][i].fFrame / MAX_FRAMES_PER_SEC; + pCall->fIndex = i; + pCall->AddReceiver(pCamMod->GetKey()); + plAnimCmdMsg* pMsg = TRACKED_NEW plAnimCmdMsg; + pMsg->AddReceiver(pCamMod->GetKey()); + pMsg->SetSender(pAnim->GetModKey(node)); + pMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); + pMsg->SetAnimName(ENTIRE_ANIMATION_NAME); + pMsg->fTime = kfArray[0][i].fFrame / MAX_FRAMES_PER_SEC; + pMsg->AddCallback(pCall); + hsRefCnt_SafeUnRef(pCall); + plConvert::Instance().AddMessageToQueue(pFOVMsg); + plConvert::Instance().AddMessageToQueue(pMsg); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsControlConverter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsControlConverter.h new file mode 100644 index 00000000..2c084c3f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsControlConverter.h @@ -0,0 +1,185 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __HSCONTROLCONVERTER_H +#define __HSCONTROLCONVERTER_H + +#include +#include + +//#include "Max.h" +#include "stdmat.h" +#include "bmmlib.h" +#include "istdplug.h" +#include "texutil.h" +#include "../../../Sources/Plasma/PubUtilLib/plInterp/hsKeys.h" +#include "hsTemplates.h" + +class plMaxNode; + +enum ControllerType +{ + ctrlTypeUnknown, + ctrlTypeFloat, + ctrlTypePoint3, + ctrlTypePosition, + ctrlTypeRotation, + ctrlTypeRotationX, + ctrlTypeRotationY, + ctrlTypeRotationZ, + ctrlTypeScale, + ctrlTypeTransform, + ctrlTypeEase, + ctrlTypeMult, + ctrlTypeVert, // CS Biped + ctrlTypeHoriz, // CS Biped + ctrlTypeTurn, // CS Biped + ctrlTypeRollAngle, +}; + +class plController; +class plLeafController; +class plCompoundController; +struct hsKeyFrame; +class hsConverterUtils; +class plSceneObject; + +class hsControlConverter +{ +private: + hsControlConverter(); + +public: + ~hsControlConverter() { }; + static hsControlConverter& Instance(); + + void Init(plErrorMsg* msg); + void DeInit(); + + // Used to pick correct controller by lights/materials + hsBool GetControllerByName(Animatable* anim, TSTR &name, Control* &ctl); + Control *GetControllerByID(IParamBlock2 *pblock, int paramID); + + ///////////////////////////////////////////////////////////////////////// + // + // Controller convert functions: + // + // All convert functions must call ISetSegRange(start, end) at the beginning. + // (ISetSegRange(-1, -1) will give you the entire anim.) + plLeafController* MakeMatrix44Controller(StdUVGen* uvGen, const char* nodeName); + plLeafController* MakeMatrix44Controller(Control* prsControl); + plLeafController* MakeScalarController(Control* control, plMaxNode* node, hsScalar start = -1, hsScalar end = -1); + plController* MakeColorController(Control* control, plMaxNode* node, hsScalar start = -1, hsScalar end = -1); + plController* MakePosController(Control* control, plMaxNode* node, hsScalar start = -1, hsScalar end = -1); + plController* MakeScaleController(Control* control, plMaxNode* node, hsScalar start = -1, hsScalar end = -1); + plController* MakeRotController(Control* control, plMaxNode* node, hsBool camRot = false, hsScalar start = -1, hsScalar end = -1); + plCompoundController* MakeTransformController(Control* control, plMaxNode* node, hsScalar start = -1, hsScalar end = -1); + + // This last one was in tempAnim.cpp on its own for some time, apparently created + // as an initial attempt to get anims working in Max. It's still used, so I don't want + // to nuke it, but it made sense to move it here. + plController* ConvertTMAnim(plSceneObject *obj, plMaxNode *node, hsAffineParts *parts, hsScalar start = -1, hsScalar end = -1); + // + // + ////////////////////////////////////////////////////////////////////////// + + void Matrix3ToHsMatrix44(Matrix3* m3, hsMatrix44* hsM); + Matrix3 StdUVGenToMatrix3(StdUVGen* uvGen); + bool StdUVGenToHsMatrix44(hsMatrix44* hsMat, StdUVGen* uvGen, bool preserveOffset=false); + void MaxSampleAngles(const char* nodeName, Control* ctl, Tab& kTimes, hsScalar maxRads); + void ScalePositionController(plController* ctl, hsScalar scale); + + void ReduceKeys(Control *control, hsScalar threshold); + hsBool HasKeyTimes(Control* ctl); + UInt8 GetKeyType(Control* ctl, hsBool rotQuat = false); + + plMaxNode* GetXformParent(plMaxNode* node); + hsBool ForceWorldSpace(plMaxNode* node); + hsBool ForceOrigin(plMaxNode* node); + hsBool ForceLocal(plMaxNode* node); + hsBool IsAnimated(plMaxNode* node); + hsBool OwnsMaterialCopy(plMaxNode* node); + hsBool HasFrameEvents(plMaxNode *node); + + void CompositeKeyTimes(Control* ctl, Tab &time); + + int GetTicksPerFrame() { return fTicksPerFrame; } + int GetFrameRate() { return fFrameRate; } + int GetTicksPerSec() { return fTicksPerSec; } + int GetStartFrame() { return fStartFrame; } + int GetEndFrame() { return fEndFrame; } + int GetNumFrames() { return fNumFrames; } + float GetAnimLength() { return fAnimLength; } + +private: + void ISetSegRange(hsScalar start, hsScalar end); + void IConvertSubTransform(Control *control, char *ctlName, plMaxNode *node, plCompoundController *tmc, hsScalar start, hsScalar end); + + plLeafController* ICreateSimpleRotController(plMaxNode* node, Control* control, hsBool camRot = false); + plLeafController* ICreateSimpleScaleController(plMaxNode* node, Control* control); + plLeafController* ICreateQuatController(plMaxNode* node, Control* control, bool rotation = true, hsBool camRot = false); + plLeafController* ICreateScaleValueController(plMaxNode* node, Control* control); + plLeafController* ICreateScalarController(plMaxNode* node, Control* control); + plLeafController* ICreateSimplePosController(plMaxNode* node, Control* control); + + void IEnableEaseCurves(Animatable* control, bool enable); + void IGetControlSampleTimes(Control* control, int iLo, int iHi, Tab& kTimes, float maxRads); + int IAddPartsKeys(Control* control, hsTArray * kfArray, plMaxNode* node); + Int32 ICreateHSInterpKey(Control* control, IKey* mKey, TimeValue keyTime, hsKeyFrame* baseKey, plMaxNode* node=nil, hsBool rotQuat = false); + Int32 IGetRangeCoverKeyIndices(char* nodeName, Control* cont, Int32 &start, Int32 &end); + ControllerType IGetControlType(TSTR ctrlName); + bool IIsKeyTimeInRange(TimeValue time); + bool IIsKeyInRange(IKey* key); + void IGetUnEasedLocalTM(plMaxNode* node, Control* control, hsMatrix44* out, TimeValue time); + Matrix3 IFlipY(); + bool ISkinNode(plMaxNode* node); + void ISetForceLocal(bool f) { fForceLocal=f; } + + hsBool IGetEditableMeshKeyTimes( plMaxNode *node, Tab × ); + hsBool IGetGeomKeyTimes( plMaxNode *node, Tab × ); + void IGetGeomKeyTimesRecur( Animatable *anim, Tab × ); + hsBool IGetSubAnimByName( Animatable *anim, TSTR &name, Animatable *&subAnim ); + void IExportAnimatedCameraFOV(plMaxNode* node, hsTArray * kfArray); + Interface* fInterface; + + hsConverterUtils& fConverterUtils; + plErrorMsg * fErrorMsg; + + Int32 fTicksPerFrame; + Int32 fFrameRate; + Int32 fTicksPerSec; + Int32 fStartFrame; + Int32 fEndFrame; + Int32 fNumFrames; + hsScalar fAnimLength; + hsBool fWarned; + + hsBool fForceLocal; + + TimeValue fSegStart; + TimeValue fSegEnd; +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsConverterUtils.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsConverterUtils.cpp new file mode 100644 index 00000000..2ef4ec83 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsConverterUtils.cpp @@ -0,0 +1,547 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "hsConverterUtils.h" +#include "hsResMgr.h" + +#if HS_BUILD_FOR_WIN32 + +#include + +#include "hsMaxLayerBase.h" +#include "../plInterp/plController.h" + +#include "../MaxExport/plErrorMsg.h" +#include "UserPropMgr.h" +#include "hsStringTokenizer.h" +//#include "hsDXTDirectXCodec.h" +//#include "hsDXTSoftwareCodec.h" +#include "../plGImage/hsCodecManager.h" +///#include "SwitchUtil.h" +#include "hsExceptionStack.h" +#include "hsHashTable.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/hsKeyedObject.h" + +const char hsConverterUtils::fTagSeps[] = " ,\t\n=:;"; + +extern UserPropMgr gUserPropMgr; + +namespace { + class ObjectInstancedEnumProc : public DependentEnumProc + { + public: + ObjectInstancedEnumProc() : fInstanceCount(0) { } + + int proc(ReferenceMaker *rmaker) + { + hsGuardBegin("ObjectInstancedEnumProc::proc"); + + if (rmaker->SuperClassID()==BASENODE_CLASS_ID) + { + fInstanceCount++; + } + + return 0; + hsGuardEnd; + } + + Int32 GetInstanceCount() { return fInstanceCount; } + + private: + Int32 fInstanceCount; + }; +} + +hsConverterUtils& hsConverterUtils::Instance() +{ + static hsConverterUtils the_instance; + + return the_instance; +} + +hsConverterUtils::hsConverterUtils() : +fInterface(GetCOREInterface()), +fErrorMsg(nil), +fSuppressMangling(false), +fWarned(0) +{ +} + +void hsConverterUtils::Init(hsBool save, plErrorMsg *msg) +{ + hsGuardBegin("hsConverterUtils::Init"); + + fErrorMsg = msg; + + fSuppressMangling = false; + fWarned = 0; + fSave = save; + + hsGuardEnd; +} + +TimeValue hsConverterUtils::GetTime(Interface *gi) +{ + hsGuardBegin("hsConverterUtils::GetTime"); + + return ((TimeValue)0); + hsGuardEnd; + // return gi->GetTime(); + // return theSceneEnum->time; +} + +hsBool hsConverterUtils::IsEnvironHolder(INode *node) +{ + hsGuardBegin("hsConverterUtils::IsEnvironHolder"); + + return (gUserPropMgr.UserPropExists(node, "EnvironMap")); + hsGuardEnd; +} + +hsBool hsConverterUtils::AutoStartDynamics(INode *node) +{ + hsGuardBegin("hsConverterUtils::AutoStartDynamics"); + + return (gUserPropMgr.UserPropExists(node,"AutoStart") || gUserPropMgr.UserPropExists(node,"aud")); + hsGuardEnd; +} + +hsBool hsConverterUtils::RandomStartDynamics(INode *node) +{ + hsGuardBegin("hsConverterUtils::RandomStartDynamics"); + + return (gUserPropMgr.UserPropExists(node,"RandomStart")); + hsGuardEnd; +} + +void hsConverterUtils::StripOffTail(char* path) +{ + hsGuardBegin("hsConverterUtils::StripOffTail"); + + int i = strlen(path)-1; + while(path[i] != '\\') + i--; + path[i+1] = 0; + + hsGuardEnd; +} + +void hsConverterUtils::StripOffPath(char* fileName) +{ + hsGuardBegin("hsConverterUtils::StripOffPath"); + + char tmp[256]; + // Remove preceding path + int i = strlen(fileName)-1; + while(fileName[i] != '\\') + i--; + strcpy(tmp, fileName+i+1); + strcpy(fileName, tmp); + + hsGuardEnd; +} + +// +// static +// +hsBool hsConverterUtils::IsReservedKeyword(const char* nodeName) +{ + hsGuardBegin("hsConverterUtils::IsReservedKeyword"); + + return (nodeName!=nil && + ( !_stricmp(nodeName, "theplayer") + || !_stricmp(nodeName, "the_player") + || !_stricmp(nodeName, "thecamera") + || !_stricmp(nodeName, "the_camera") + || !_stricmp(nodeName, "thedetector") + || !_stricmp(nodeName, "the_detector") + || !_stricmp(nodeName, "themonitor") + || !_stricmp(nodeName, "the_monitor") + || !_stricmp(nodeName, "thedetectorshape") + || !_stricmp(nodeName, "the_detector_shape")) ); + + hsGuardEnd; +} + +char *hsConverterUtils::MangleReference(char *mangName, const char *nodeName, const char* defRoom) +{ + hsGuardBegin("hsConverterUtils::MangleReference"); + + //hsAssert(nodeName, "No node name in hsConverterUtils::MangleReference."); + if(!nodeName) + { + fErrorMsg->Set(true, "Mangle Reference Error", "No node name in hsConverterUtils::MangleReference.").Show(); + fErrorMsg->Set(); + return mangName; + } + if (!*nodeName) + return hsStrcpy(mangName, nodeName); + + // doesn't want to be mangled + if (('.' == nodeName[0])&&('.' == nodeName[1])) + return hsStrcpy(mangName, nodeName + 2); + + // already mangled or reserved + if (strstr(nodeName, "..") || IsReservedKeyword(nodeName)) + return hsStrcpy(mangName, nodeName); + + INode *node = GetINodeByName(nodeName); + + if (!node) + { + // no room so make global + // Default is to make it global, but you can set another default (like same + // room as referencer) with defRoom. + char tempName[256]; + + sprintf(tempName, "%s..%s", defRoom, nodeName); + return hsStrcpy(mangName, tempName); + } + + return MangleReference(mangName, node); + hsGuardEnd; +} + +char *hsConverterUtils::MangleReference(char *mangName, INode *node, const char* defRoom) +{ + hsGuardBegin("hsConverterUtils::MangleReference"); + + if (!node) + return nil; + + char tempName[256]; + + char *nodeName = node->GetName(); + char *roomName = nil; + TSTR sdata; + hsStringTokenizer toker; + if (gUserPropMgr.GetUserPropString(node, "Rooms", sdata)) + { + toker.Reset(sdata, fTagSeps); + roomName = toker.next(); + } + + if (fSuppressMangling) + { + return hsStrcpy(mangName, nodeName); + } + + if (('.' == nodeName[0])&&('.' == nodeName[1])) + hsStrcpy(tempName, nodeName + 2); + else if (!*nodeName + || strstr(nodeName, "..") + || IsReservedKeyword(nodeName) + ) + hsStrcpy(tempName, nodeName); + else if (roomName && *roomName) + sprintf(tempName, "%s..%s", roomName, nodeName); + else + sprintf(tempName, "%s..%s", defRoom, nodeName); + + return hsStrcpy(mangName, tempName); + + hsGuardEnd; +} + +char *hsConverterUtils::MangleRefWithRoom(char *mangName, const char *nodeName, const char* roomName) +{ + hsGuardBegin("hsConverterUtils::MangleRefWithRoom"); + + //hsAssert(nodeName && roomName, "No node or room name in hsConverterUtils::MangleRefWithRoom."); + if(!(nodeName && roomName)) + { + fErrorMsg->Set(true, "Mangle Room Reference Error", "No node or room name in hsConverterUtils::MangleRefWithRoom.").Show(); + fErrorMsg->Set(); + return mangName; + } + + if (!*nodeName) + return hsStrcpy(mangName,nodeName); + + // doesn't want to be mangled + if (('.' == nodeName[0])&&('.' == nodeName[1])) + return hsStrcpy(mangName, nodeName + 2); + + // already mangled or reserved + if (strstr(nodeName, "..") || IsReservedKeyword(nodeName)) + return hsStrcpy(mangName, nodeName); + + sprintf(mangName, "%s..%s", roomName, nodeName); + return mangName; + + hsGuardEnd; +} + + +INode* hsConverterUtils::FindINodeFromMangledName(const char* mangName) +{ + hsGuardBegin("hsConverterUtils::FindINodeFromMangledName"); + + if( !(mangName && *mangName) ) + return nil; + + const char* nodeName = mangName; + + char* p; + while( p = strstr(nodeName, "..") ) + { + nodeName = p + 2; + } + if( !(nodeName && *nodeName) ) + nodeName = mangName; + + return GetINodeByName(nodeName); + hsGuardEnd; +} + +INode* hsConverterUtils::FindINodeFromKeyedObject(hsKeyedObject* obj) +{ + hsGuardBegin("hsConverterUtils::FindINodeFromKeyedObject"); + + INode* retVal = FindINodeFromMangledName(obj->GetKey()->GetName()); + if( retVal ) + return (retVal); + +/* No more other Keys plasma 2.0 + int i; + for( i = 0; i < obj->GetNumOtherKeys(); i++ ) + { + retVal = FindINodeFromMangledName(obj->GetOtherKey(i)->GetName()); + if( retVal ) + return retVal; + } +*/ + return nil; + hsGuardEnd; +} + +// Uses MangleRef so all mangling happens in one place. Compares the name with a mangled version of it +hsBool hsConverterUtils::IsMangled(const char *name) +{ + hsGuardBegin("hsConverterUtils::IsMangled"); + + char mang[255]; + return !strcmp(name,MangleReference(mang,name)); + hsGuardEnd; +} + +// Undoes the process of mangling. This includes taking a "name" back to "..name" +char *hsConverterUtils::UnMangleReference(char *dest, const char *name) +{ + hsGuardBegin("hsConverterUtils::IsMangled"); + + char *u = strstr(name,".."); + if (u) + { + u+=2; + strcpy(dest,u); + } + else if (!IsMangled(name)) + { + strcpy(dest,".."); + strcat(dest,name); + } + else + { + strcpy(dest,name); + } + + return dest; + hsGuardEnd; +} + +// Similar to UnMangle but doesn't take "name" back to "..name" +char* hsConverterUtils::StripMangledReference(char* dest, const char* name) +{ + hsGuardBegin("hsConverterUtils::StripMangledReference"); + + char *u = strstr(name,".."); + if (u) + { + u+=2; + strcpy(dest,u); + } + else + { + strcpy(dest,name); + } + + return dest; + hsGuardEnd; +} + +Int32 hsConverterUtils::FindNamedSelSetFromName(const char *name) +{ + hsGuardBegin("hsConverterUtils::FindNamedSelSetFromName"); + + for (Int32 i=0; iGetNumNamedSelSets(); i++) + { + if (!_stricmp(name, fInterface->GetNamedSelSetName(i))) + return (i); + } + + return (-1); + hsGuardEnd; +} + +hsBool hsConverterUtils::IsInstanced(Object* maxObject) +{ + hsGuardBegin("hsConverterUtils::IsInstanced"); + + if (!maxObject) + { + return false; + } + + ObjectInstancedEnumProc instProc; + maxObject->EnumDependents(&instProc); + + return (instProc.GetInstanceCount() > 1); + hsGuardEnd; +} + + +INode* hsConverterUtils::IGetINodeByNameRecur(INode* node, const char* wantName) +{ + hsGuardBegin("hsConverterUtils::IGetINodeByNameRecur"); + + if (!node || !node->GetName()) + return nil; + + char* nodeName=node->GetName(); + if (!_stricmp(nodeName, wantName)) + return node; + + // Process children + int num = node->NumberOfChildren(); + int i; + for(i=0; iGetChildNode(i), wantName))) + return ret; + } + + return nil; + hsGuardEnd; +} + +// +// Matches name against node's name, case-insensitive, +// +INode* hsConverterUtils::GetINodeByName(const char* name, hsBool caseSensitive) +{ + hsGuardBegin("hsConverterUtils::GetINodeByName"); + + if (!name) + { + return nil; + } + + if (fNodeSearchCache) + { + CacheNode cNode(name); + cNode.SetCaseSensitive(caseSensitive); + hsHashTableIterator it = fNodeSearchCache->find(cNode); + return it->GetNode(); + } + + //hsAssert(fInterface, "nil fInterface in hsConverterUtils::GetINodeByName()"); + if(!fInterface) + { + fErrorMsg->Set(true, "Get INode by Name Error", "nil fInterface in hsConverterUtils::GetINodeByName()").Show(); + fErrorMsg->Set(); + return NULL; + } + + + if (caseSensitive) + { + return fInterface->GetINodeByName(name); + } + + return IGetINodeByNameRecur(fInterface->GetRootNode(), name); + hsGuardEnd; +} + +void hsConverterUtils::CreateNodeSearchCache() +{ + if (!fNodeSearchCache) + { + fNodeSearchCache = TRACKED_NEW hsHashTable(); + } + fNodeSearchCache->clear(); + + IBuildNodeSearchCacheRecur(fInterface->GetRootNode()); +} + +void hsConverterUtils::DestroyNodeSearchCache() +{ + delete fNodeSearchCache; + fNodeSearchCache = nil; +} + +void hsConverterUtils::IBuildNodeSearchCacheRecur(INode* node) +{ + if (!node || !node->GetName()) + return ; + + CacheNode cNode(node); + fNodeSearchCache->insert(cNode); + + // Process children + int num = node->NumberOfChildren(); + int i; + for(i=0; iGetChildNode(i)); + } +} + +UInt32 hsConverterUtils::CacheNode::GetHash() const +{ + const char* k = GetName(); + int len = k ? strlen(k) : 0; + for (int h=len; len--;) + { + h = ((h<<5)^(h>>27))^tolower(*k++); + } + return h; +} + +bool hsConverterUtils::CacheNode::operator==(const CacheNode& other) const +{ + const char* k1 = GetName(); + const char* k2 = other.GetName(); + if (other.fCaseSensitive || fCaseSensitive) + return !strcmp(k1,k2); + else + return !_stricmp(k1,k2); +} + +#endif // HS_BUILD_FOR_WIN32 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsConverterUtils.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsConverterUtils.h new file mode 100644 index 00000000..88bfd806 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsConverterUtils.h @@ -0,0 +1,135 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __HSCONVERTERUTILS_H +#define __HSCONVERTERUTILS_H + +#include "Max.h" +#include "stdmat.h" +#include +#include "bmmlib.h" +#include "istdplug.h" +#include "texutil.h" + +#include "Headspin.h" + +class INode; +class Control; +class Interface; +template class hsHashTable; + +class hsMaxLayerBase; +class plSimplePosController; +class plScalarController; +class plLeafController; +class hsGBitmapClass; +class hsGMipmapClass; +class plErrorMsg; +class hsKeyedObject; +class hsGRenderProcs; +class hsBaseRenderProc; + +class hsConverterUtils +{ +private: + hsConverterUtils(); +public: // MSDEV bug + ~hsConverterUtils() {} +public: + static hsConverterUtils& Instance(); + static hsBool IsReservedKeyword(const char* nodeName); + + void Init(hsBool save, plErrorMsg *msg); + hsBool IsEnvironHolder(INode *node); + hsBool AutoStartDynamics(INode *node); + hsBool RandomStartDynamics(INode *node); + TimeValue GetTime(Interface *gi); + void StripOffTail(char* path); + void StripOffPath(char* fileName); + + INode* FindINodeFromKeyedObject(hsKeyedObject* obj); + INode* FindINodeFromMangledName(const char* mangName); +#if 0 + void MangleRPRefs(hsBaseRenderProc* base, hsGRenderProcs* rp); +#endif + char* MangleReference(char *mangName, const char *nodeName, const char* defRoom="global"); + char* MangleReference(char *mangName, INode *node, const char* defRoom="global"); + char* MangleRefWithRoom(char *mangName, const char *nodeName, const char* roomName); + char* UnMangleReference(char *dest, const char *name); + hsBool IsMangled(const char *name); + Int32 FindNamedSelSetFromName(const char *name); + char* StripMangledReference(char* dest, const char* name); + + hsBool IsInstanced(Object* maxObject); + + void CreateNodeSearchCache(); + void DestroyNodeSearchCache(); + INode* GetINodeByName(const char* name, hsBool caseSensitive=false); + + static const char fTagSeps[]; + +private: + + void IBuildNodeSearchCacheRecur(INode* node); + INode* IGetINodeByNameRecur(INode* node, const char* wantName); + +private: + enum { + kWarnedNoMoreBitmapLoadErr = 0x1 + }; + + Interface *fInterface; + plErrorMsg *fErrorMsg; + + hsBool fSuppressMangling; + UInt32 fWarned; + hsBool fSave; + + struct CacheNode + { + private: + INode* fNode; + const char* fName; + hsBool fCaseSensitive; + public: + CacheNode(INode* node=nil) : fNode(node), fName(nil), fCaseSensitive(false) { } + CacheNode(const char* name) : fName(name), fNode(nil), fCaseSensitive(false) { } + ~CacheNode() { } + + INode* GetNode() { return fNode; } + const char* GetName() const { return fNode ? fNode->GetName() : fName; } + + void SetCaseSensitive(hsBool b) { fCaseSensitive = b; } + hsBool GetCaseSensitive() { return fCaseSensitive; } + + UInt32 GetHash() const; + bool operator==(const CacheNode& other) const; + }; + hsHashTable* fNodeSearchCache; +}; + + +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaterialConverter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaterialConverter.cpp new file mode 100644 index 00000000..9ae33ed4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaterialConverter.cpp @@ -0,0 +1,5147 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsWindows.h" +#include +#include +#include + +#include "Max.h" +#include "stdmat.h" +#include "bmmlib.h" +#include "istdplug.h" +#include "texutil.h" + +#include "hsMaterialConverter.h" +#include "plLayerConverter.h" +#include "../MaxComponent/plMaxAnimUtils.h" +#include "../plResMgr/plKeyFinder.h" +#include "hsResMgr.h" +#include "../pnKeyedObject/plUoid.h" +#include "hsUtils.h" +#include "hsMaxLayerBase.h" +#include "../MaxExport/plErrorMsg.h" +#include "../plSurface/hsGMaterial.h" +#include "../pnSceneObject/plSceneObject.h" +#include "UserPropMgr.h" +#include "../plFile/plFileUtils.h" + +#include "hsConverterUtils.h" +#include "hsControlConverter.h" +#include "../MaxMain/plMaxNode.h" + +#include "../plInterp/plController.h" +#include "hsExceptionStack.h" +#include "hsStringTokenizer.h" +#include "../plSurface/plLayerInterface.h" +#include "../plSurface/plLayer.h" +#include "../plSurface/plLayerAnimation.h" +#include "../plGImage/plMipmap.h" + +#include "plgDispatch.h" + +#include "../pnMessage/plRefMsg.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plKeyImp.h" + +#include "plBitmapCreator.h" + +#include "../plMessage/plMatRefMsg.h" +#include "../plMessage/plLayRefMsg.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pfMessage/plClothingMsg.h" + +#include "../MaxPlasmaMtls/Materials/plMultipassMtlPB.h" +#include "../MaxPlasmaMtls/Materials/plCompositeMtlPB.h" +#include "../MaxPlasmaMtls/Materials/plPassMtl.h" +#include "../MaxPlasmaMtls/Materials/plMultipassMtl.h" +#include "../MaxPlasmaMtls/Materials/plDecalMtl.h" +#include "../MaxPlasmaMtls/Materials/plCompositeMtl.h" +#include "../MaxPlasmaMtls/Materials/plParticleMtl.h" +#include "../MaxPlasmaMtls/Materials/plBumpMtl.h" +#include "../MaxPlasmaMtls/Materials/plPassMtlBase.h" +#include "../MaxPlasmaMtls/Materials/plAnimStealthNode.h" +#include "../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h" +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" +#include "../MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h" + +#include "../pfSurface/plLayerAVI.h" +#include "../pfSurface/plLayerBink.h" + +#include "../MaxComponent/plLightMapComponent.h" +#include "../plDrawable/plGeometrySpan.h" + +#include "../MaxPlasmaMtls/Materials/plClothingMtl.h" +#include "../plAvatar/plAvatarClothing.h" +#include "../plAvatar/plClothingLayout.h" +#include "../plSDL/plSDL.h" +#include "../plSDL/plSDLDescriptor.h" + +extern UserPropMgr gUserPropMgr; + +static const char* kSecretBumpSign = "~~~"; + +namespace { + const int kDefaultDetailBias=5; + + void CopyMaterialLODToTextures(hsGMaterial* mat) + { + Int32 i; + for (i = 0; i < mat->GetNumLayers(); ++i) + { + plLayerInterface* layer = mat->GetLayer(i); + +/* Textures? What textures? + if (layer->GetTexture()) + { + hsGTexture* texture = layer->GetTexture(); + texture->SetLOD(texture->GetLOD() | mat->GetLOD()); + } +*/ + } + } + + const char sWarnBaseTextureMissing[] = "The object \"%s\"'s material has a base layer that is assigned texture \"%s\", but the texture file is missing. " + "This can cause unwanted effects during runtime."; + const char sWarnUpperTextureMissing[] = "The object \"%s\"'s material has an upper layer that is assigned texture \"%s\", but the texture file is missing. " + "This is not supported in the engine, so the upper layer will be ignored."; + const char sWarnNoUpperTexture[] = "The object \"%s\"'s material has an uppper layer that is not assigned a texture. " + "This is not supported in the engine, so the upper layer will be disabled."; +} + +static UInt32 MakeUInt32Color(float r, float g, float b, float a) +{ + return (UInt32(a * 255.9f) << 24) + |(UInt32(r * 255.9f) << 16) + |(UInt32(g * 255.9f) << 8) + |(UInt32(b * 255.9f) << 0); +} + +static bool failedRT = false; +static bool failedNumUV = false; +static bool failedAlphaLayer = false; +static bool failedFade = false; +static int dupCuzRT = 0; +static int dupCuzNumUV = 0; +static int dupCuzAlphaLayer = 0; +static int dupCuzFade = 0; + +hsMaterialConverter& hsMaterialConverter::Instance() +{ + hsGuardBegin("hsMaterialConverter::Instance"); + static hsMaterialConverter the_instance; + + return the_instance; + hsGuardEnd; +} + +hsMaterialConverter::hsMaterialConverter() : +fSave(true), +fNodeName(nil), +fWarned(0), +fInterface(nil), +fConverterUtils(hsConverterUtils::Instance()), +fChangedTimes(false) +{ + hsGuardBegin("hsMaterialConverter::hsMaterialConverter"); + + hsGuardEnd; +} + +hsMaterialConverter::~hsMaterialConverter() +{ + hsGuardBegin("hsMaterialConverter::~hsMaterialConverter"); + hsAssert(fDoneMaterials.Count() == 0, "FreeMaterialCache not called"); + hsGuardEnd; +} + +void hsMaterialConverter::Init(hsBool save, plErrorMsg *msg) +{ + hsGuardBegin("hsMaterialConverter::Init"); + + fInterface = GetCOREInterface(); + fSave = save; + fErrorMsg = msg; + + fSubIndex = -1; + fNodeName = nil; + + fWarned = true; + + fLastMaterial.fHsMaterial = nil; + fLastMaterial.fMaxMaterial = nil; + fLastMaterial.fMaxMaterial = nil; + fLastMaterial.fSubMultiMat = false; + fLastMaterial.fOwnedCopy = false; + + failedRT = false; + failedNumUV = false; + failedAlphaLayer = false; + failedFade = false; + dupCuzRT = 0; + dupCuzNumUV = 0; + dupCuzAlphaLayer = 0; + dupCuzFade = 0; + + hsGuardEnd; +} + +void hsMaterialConverter::FreeMaterialCache(const char* path) +{ + if( path && *path ) + IGenMaterialReport(path); + + for (int i = 0; i < fDoneMaterials.Count(); i++) + fDoneMaterials[i].fHsMaterial->GetKey()->UnRefObject(); + + fDoneMaterials.Reset(); +} + +hsBool hsMaterialConverter::ForceNoUvsFlatten(plMaxNode* node) +{ + hsGuardBegin("hsMaterialConverter::ForceNoUvsFlatten"); + return IsMultiMat(GetBaseMtl(node)); + hsGuardEnd; +} + +hsBool hsMaterialConverter::PreserveUVOffset(Mtl* mtl) +{ + hsGuardBegin("hsMaterialConverter::PreserveUVOffset"); + + if (!mtl || (!IsHsMaxMat(mtl) && !IsDecalMat(mtl))) + return true; + + //IParamBlock2 *pblock = mtl->GetParamBlockByID(plPassMtl::kBlkLayers); + //if (!pblock) + // return true; + + plPassMtlBase *currMtl = (plPassMtlBase *)mtl; + for (int i = 0; i < mtl->NumSubTexmaps(); i++) + { + //if (i == 1 && !pblock->GetInt(kPassLayTopOn)) + if (i == 1 && !currMtl->GetTopLayerOn()) + continue; + + Texmap *texMap = mtl->GetSubTexmap(i); + + if (!texMap || texMap->ClassID() != LAYER_TEX_CLASS_ID) + continue; + + StdUVGen* uvGen = (StdUVGen*)((plLayerTex*)texMap)->GetTheUVGen(); + int tiling = uvGen->GetTextureTiling(); + if( !(tiling & U_WRAP) || !(tiling & V_WRAP) ) + return true; + + if (IHasAnimatedControllers(uvGen)) + return true; +// if (ITextureTransformIsAnimated(texMap)) +// return true; + + } + + return false; + + hsGuardEnd; +} + +void AttachLinkMtlAnims(plMaxNode *node, hsGMaterial *mat) +{ + const int numKeys = 2; + hsScalar times[] = {0.f, 1.5f}; + hsScalar values[numKeys] = {100.f, 0.f}; + hsBool leaving[] = {true, false}; + char *animName = "_link_anim"; + + int k; + for (k = 0; k < mat->GetNumLayers(); k++) + { + plLayerInterface *oldLayer, *currLayer; + oldLayer = currLayer = mat->GetLayer(k); + plLeafController *opaCtl; + plLayerLinkAnimation* animLayer; + + char suff[10]; + sprintf(suff, "%d", k); + + opaCtl = TRACKED_NEW plLeafController; + opaCtl->QuickScalarController(numKeys, times, values, sizeof(hsScalar)); + animLayer = TRACKED_NEW plLayerLinkAnimation; + animLayer->SetLinkKey(node->GetAvatarSO()->GetKey()); + //animLayer->fLeavingAge = leaving[x]; + TSTR fullAnimName = TSTR(oldLayer->GetKeyName()) + TSTR("_") + TSTR(animName) + TSTR("_") + TSTR(suff); + hsgResMgr::ResMgr()->NewKey(fullAnimName, animLayer, node->GetLocation()); + animLayer->SetOpacityCtl(opaCtl); + animLayer->GetTimeConvert().SetBegin(times[0]); + animLayer->GetTimeConvert().SetEnd(times[1]); + animLayer->GetTimeConvert().Stop(true); + animLayer->AttachViaNotify(currLayer); + currLayer = animLayer; + + plMatRefMsg* msg = TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnReplace, k, plMatRefMsg::kLayer); + msg->SetOldRef(oldLayer); + msg->SetRef(currLayer); + hsgResMgr::ResMgr()->AddViaNotify(msg, plRefFlags::kActiveRef); + } +} + +UInt32 hsMaterialConverter::ColorChannelsUseMask(plMaxNode* node, int iSubMtl) +{ + UInt32 usedChan = 0; + hsBool deleteIt = false; + + TriObject* triObj = node->GetTriObject(deleteIt); + if( triObj ) + { + Mesh* mesh = &(triObj->mesh); + + UVVert* alphaMap = mesh->mapVerts(MAP_ALPHA); + int numAlphaVerts = mesh->getNumMapVerts(MAP_ALPHA); + + UVVert* illumMap = mesh->mapVerts(MAP_SHADING); + int numIllumVerts = mesh->getNumMapVerts(MAP_SHADING); + + Point3* colorMap = mesh->vertCol; + int numColorVerts = mesh->numCVerts; + + TVFace* colorFaces = mesh->vcFace; + TVFace* illumFaces = mesh->mapFaces(MAP_SHADING); + TVFace* alphaFaces = mesh->mapFaces(MAP_ALPHA); + + int numFaces = mesh->getNumFaces(); + int i; + for( i = 0; i < numFaces; i++ ) + { + Face *face = &mesh->faces[ i ]; + + if( (iSubMtl >= 0) && (face->getMatID() != iSubMtl) ) + continue; + + if( colorFaces && colorMap ) + { + TVFace* colorFace = colorFaces + i; + usedChan |= ICheckPoints(colorMap[colorFace->getTVert(0)], + colorMap[colorFace->getTVert(1)], + colorMap[colorFace->getTVert(2)], + 0, + kColorRedBlack, kColorRedGrey, kColorRedWhite); + usedChan |= ICheckPoints(colorMap[colorFace->getTVert(0)], + colorMap[colorFace->getTVert(1)], + colorMap[colorFace->getTVert(2)], + 1, + kColorGreenBlack, kColorGreenGrey, kColorGreenWhite); + usedChan |= ICheckPoints(colorMap[colorFace->getTVert(0)], + colorMap[colorFace->getTVert(1)], + colorMap[colorFace->getTVert(2)], + 2, + kColorBlueBlack, kColorBlueGrey, kColorBlueWhite); + } + if( illumFaces && illumMap ) + { + TVFace* illumFace = illumFaces + i; + usedChan |= ICheckPoints(illumMap[illumFace->getTVert(0)], + illumMap[illumFace->getTVert(1)], + illumMap[illumFace->getTVert(2)], + 0, + kIllumRedBlack, kIllumRedGrey, kIllumRedWhite); + usedChan |= ICheckPoints(illumMap[illumFace->getTVert(0)], + illumMap[illumFace->getTVert(1)], + illumMap[illumFace->getTVert(2)], + 1, + kIllumGreenBlack, kIllumGreenGrey, kIllumGreenWhite); + usedChan |= ICheckPoints(illumMap[illumFace->getTVert(0)], + illumMap[illumFace->getTVert(1)], + illumMap[illumFace->getTVert(2)], + 2, + kIllumBlueBlack, kIllumBlueGrey, kIllumBlueWhite); + } + if( alphaFaces && alphaMap ) + { + TVFace* alphaFace = alphaFaces + i; + usedChan |= ICheckPoints(alphaMap[alphaFace->getTVert(0)], + alphaMap[alphaFace->getTVert(1)], + alphaMap[alphaFace->getTVert(2)], + 0, + kAlphaBlack, kAlphaGrey, kAlphaWhite); + } + } + + if( deleteIt ) + triObj->DeleteThis(); + } + + return usedChan; +} + +UInt32 hsMaterialConverter::ICheckPoints(const Point3& p0, const Point3& p1, const Point3& p2, + int chan, + UInt32 mBlack, UInt32 mGrey, UInt32 mWhite) +{ + const float kSmall = 1.e-3f; + if( (p0[chan] < kSmall) && (p1[chan] < kSmall) && (p2[chan] < kSmall) ) + return mBlack; + + if( (1.f - p0[chan] < kSmall) && (1.f - p1[chan] < kSmall) && (1.f - p2[chan] < kSmall) ) + return mWhite; + + return mGrey; +} + +UInt32 hsMaterialConverter::ICheckPoints(const Point3& p0, const Point3& p1, const Point3& p2, const Point3& p3, + int chan, + UInt32 mBlack, UInt32 mGrey, UInt32 mWhite) +{ + const float kSmall = 1.e-3f; + if( (p0[chan] < kSmall) && (p1[chan] < kSmall) && (p2[chan] < kSmall) && (p3[chan] < kSmall) ) + return mBlack; + + if( (1.f - p0[chan] < kSmall) && (1.f - p1[chan] < kSmall) && (1.f - p2[chan] < kSmall) && (1.f - p3[chan] < kSmall) ) + return mWhite; + + return mGrey; +} + +int hsMaterialConverter::NumVertexOpacityChannelsRequired(plMaxNode* node, int iSubMtl) +{ + + UInt32 vtxChanMask = VertexChannelsRequiredMask(node, iSubMtl); + + // Now count the bits. + int numChan; + for( numChan = 0; vtxChanMask; vtxChanMask >>= 1 ) + { + numChan += (vtxChanMask & 0x1); + } + return numChan; +} + +UInt32 hsMaterialConverter::VertexChannelsRequiredMask(plMaxNode* node, int iSubMtl) +{ + Mtl* mtl = node->GetMtl(); + if( !mtl ) + return 0; + + // We use iSubMtl < 0 here as a request for all submtls used by node. + if( IsMultiMat(mtl) && (iSubMtl >= 0) ) + { + if( iSubMtl < 0 ) + iSubMtl = 0; + while( IsMultiMat(mtl) ) + { + if( iSubMtl >= mtl->NumSubMtls() ) + iSubMtl = mtl->NumSubMtls() - 1; + mtl = mtl->GetSubMtl(iSubMtl); + } + } + else + { + iSubMtl = -1; + } + + // Get the channels our materials will look at. These should all + // be of the grey variety, since a solid opaque can be drawn as is, + // and a solid transparent can be pitched. + UInt32 vtxChanReq = VertexChannelsRequestMask(node, iSubMtl, mtl); + + // Or in vtx alpha. If it's been set, we need to respect it, if it hasn't, + // it'll and out to zero anyway when we & with the vtxChanUsed. + vtxChanReq |= kAlphaGrey; + + // Now figure out what's in all the channels, which are solid black, which + // actually have interesting values etc. + UInt32 vtxChanUsed = ColorChannelsUseMask(node, iSubMtl); + + UInt32 vtxChanMask = vtxChanReq & vtxChanUsed; + + return vtxChanMask; +} + +UInt32 hsMaterialConverter::VertexChannelsRequestMask(plMaxNode* node, int iSubMtl, Mtl* mtl) +{ + if( !mtl ) + return 0; + + UInt32 vtxChanMask = 0; + + if( IsMultiMat(mtl) && (iSubMtl >= 0) ) + { + while( IsMultiMat(mtl) ) + { + if( iSubMtl >= mtl->NumSubMtls() ) + iSubMtl = mtl->NumSubMtls() - 1; + mtl = mtl->GetSubMtl(iSubMtl); + } + } + + if( IsMultiMat(mtl) || IsMultipassMat(mtl) ) + { + int i; + + for( i = 0; i < mtl->NumSubMtls(); i++ ) + vtxChanMask |= VertexChannelsRequestMask(node, iSubMtl, mtl->GetSubMtl(i)); + + return vtxChanMask; + } + + if( IsCompositeMat(mtl) ) + { + vtxChanMask |= VertexChannelsRequestMask(node, iSubMtl, mtl->GetSubMtl(0)); + + int i; + for( i = 1; i < mtl->NumSubMtls(); i++ ) + { + plCompositeMtl* comp = (plCompositeMtl*)mtl; + int myBlend = comp->GetBlendStyle(i); + switch( myBlend ) + { + case plCompositeMtl::kCompBlendVertexAlpha: + case plCompositeMtl::kCompBlendInverseVtxAlpha: + vtxChanMask |= kAlphaGrey; + break; + case plCompositeMtl::kCompBlendVertexIllumRed: + case plCompositeMtl::kCompBlendInverseVtxIllumRed: + vtxChanMask |= kIllumRedGrey; + break; + case plCompositeMtl::kCompBlendVertexIllumGreen: + case plCompositeMtl::kCompBlendInverseVtxIllumGreen: + vtxChanMask |= kIllumGreenGrey; + break; + case plCompositeMtl::kCompBlendVertexIllumBlue: + case plCompositeMtl::kCompBlendInverseVtxIllumBlue: + vtxChanMask |= kIllumBlueGrey; + break; + default: + hsAssert(false, "Ooops, new composite blends?"); + break; + } + } + } + + if( IsHsMaxMat(mtl) ) + { + plPassMtl* passMtl = (plPassMtl*)mtl; + if( plPassMtlBase::kBlendAlpha == passMtl->GetOutputBlend() ) + vtxChanMask |= kAlphaGrey; + } + + return vtxChanMask; +} + + +// +// What kind of material am I?? +// +hsBool hsMaterialConverter::IsMultiMat(Mtl *m) +{ + hsGuardBegin("hsMaterialConverter::IsMultiMat"); + + if (m == NULL) + { + return false; + } + + return (m->ClassID() == Class_ID(MULTI_CLASS_ID,0)); + hsGuardEnd; +} + +hsBool hsMaterialConverter::IsHsMaxMat(Mtl *m) +{ + hsGuardBegin("hsMaterialConverter::IsHsMaxMat"); + + if (m==NULL) + { + return false; + } + + return (m->ClassID() == PASS_MTL_CLASS_ID); + hsGuardEnd; +} + +hsBool hsMaterialConverter::IsMultipassMat(Mtl *m) +{ + hsGuardBegin("hsMaterialConverter::IsHsMaxMat"); + + if (m==NULL) + { + return false; + } + + return (m->ClassID() == MULTIMTL_CLASS_ID); + hsGuardEnd; +} + +hsBool hsMaterialConverter::IsDecalMat(Mtl *m) +{ + hsGuardBegin("hsMaterialConverter::IsDecalMat"); + + if (m==NULL) + { + return false; + } + + return (m->ClassID() == DECAL_MTL_CLASS_ID); + hsGuardEnd; +} + +hsBool hsMaterialConverter::IsCompositeMat(Mtl *m) +{ + hsGuardBegin("hsMaterialConverter::IsCompositeMat"); + + if (m==NULL) + { + return false; + } + + return (m->ClassID() == COMP_MTL_CLASS_ID); + hsGuardEnd; +} + +hsBool hsMaterialConverter::IsParticleMat(Mtl *m) +{ + hsGuardBegin("hsMaterialConverter::IsParticleMat"); + + if (m==NULL) + { + return false; + } + + return (m->ClassID() == PARTICLE_MTL_CLASS_ID); + hsGuardEnd; +} + +hsBool hsMaterialConverter::IsClothingMat(Mtl *m) +{ + hsGuardBegin("hsMaterialConverter::IsClothingMat"); + + if (m==NULL) + { + return false; + } + + return (m->ClassID() == CLOTHING_MTL_CLASS_ID); + hsGuardEnd; +} + +hsBool hsMaterialConverter::IsTwoSided(Mtl* m, int iSubMtl) +{ + if( !m ) + return false; + + return 0 != (m->Requirements(iSubMtl) & MTLREQ_2SIDE); +} + +hsBool hsMaterialConverter::HasAnimatedTextures(Texmap* texMap) +{ + hsGuardBegin("hsMaterialConverter::HasAnimatedTextures"); + return false; + hsGuardEnd; +} + +hsBool hsMaterialConverter::IsAVILayer(Texmap* texMap) +{ + hsGuardBegin("hsMaterialConverter::IsAVILayer"); + return false; + hsGuardEnd; +} + +hsBool hsMaterialConverter::IsQTLayer(Texmap* texMap) +{ + hsGuardBegin("hsMaterialConverter::IsQTLayer"); + return false; + hsGuardEnd; +} + +hsBool hsMaterialConverter::IsBinkLayer(Texmap* texMap) +{ + hsGuardBegin("hsMaterialConverter::IsBinkLayer"); + return false; + hsGuardEnd; +} + + + +// MAXR3 broke GetCoordMapping, here's a work around which +// works for everything except "Texture - Planar from Object XYZ" +int hsMaterialConverter::GetCoordMapping(StdUVGen *uvgen) +{ + hsGuardBegin("hsMaterialConverter::GetCoordMapping"); + + // Try Requirements, as StdUV does override this... + ULONG reqs = 0l; + reqs = uvgen->Requirements(0); + if (reqs & MTLREQ_VIEW_DEP || reqs == 0) + { + return (uvgen->GetCoordMapping(0)); + } + else + { + return (UVMAP_EXPLICIT); + } + + hsGuardEnd; +} + +static void IGetNodesByMaterialRecur(plMaxNode* node, Mtl *mtl, hsTArray &out) +{ + if (node) + { + if (node->GetMtl() == mtl) + out.Append(node); + + int numChildren = node->NumberOfChildren(); + for (int i = 0; i < numChildren; i++) + { + IGetNodesByMaterialRecur((plMaxNode*)node->GetChildNode(i), mtl, out); + } + } +} + +void hsMaterialConverter::GetNodesByMaterial(Mtl *mtl, hsTArray &out) +{ + IGetNodesByMaterialRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), mtl, out); +} + +// Ok, Max gives us Multi-materials which it knows are just collections of independent materials. This function +// should never be called with a multi-mat. Instead, the multi-mat should call this for each one of its +// sub-materials, and build up an array of these arrays. +// This function should be called on anything that Max thinks is a single material, even if the exporter +// wants to generate several materials from it (like composites). + +hsTArray * +hsMaterialConverter::CreateMaterialArray(Mtl *maxMaterial, plMaxNode *node, UInt32 multiIndex) +{ + hsTArray *ourMaterials = TRACKED_NEW hsTArray; + + const char* dbgNodeName = node->GetName(); + + int numUVChannels = node->NumUVWChannels(); + hsBool makeAlphaLayer = node->AlphaHackLayersNeeded(multiIndex) > 0; + + hsBool enviro = fConverterUtils.IsEnvironHolder(node); + + TSTR name; + if (maxMaterial) + name = maxMaterial->GetName(); + else + name = "nil"; + + /// Get the material + hsBool isMultiMat = IsMultiMat( maxMaterial ); + if (isMultiMat) + { + if (fErrorMsg->Set(!(fWarned & kWarnedSubMulti), node->GetName(), "Multi-material in CreateMaterialArray (Multi child of multi?) on mat %s. Using the first sub-material instead.", + maxMaterial->GetName()).CheckAskOrCancel() ) + fWarned |= kWarnedSubMulti; + maxMaterial = maxMaterial->GetSubMtl(0); + } + + hsBool isMultipassMat = IsMultipassMat( maxMaterial ); + hsBool isComposite = IsCompositeMat( maxMaterial ); + + int i; + int numMaterials = 1; + int numBlendChannels = 0; + if (isComposite) + { + int numBlendMaterials = maxMaterial->NumSubMtls() - 1; // no blend for the base Mtl + + // This will be one or zero, depending on blend modes, and whether we can use the lighting equation + // with vertex alpha. + int maxBlendChannel = plGeometrySpan::kMaxNumUVChannels - 1 - numUVChannels; + + IParamBlock2 *pb = maxMaterial->GetParamBlockByID(plCompositeMtl::kBlkPasses); + for (i = 0; i < numBlendMaterials; i++) + { + int curr = 0; + if (curr > maxBlendChannel) + { + curr = maxBlendChannel; + } + pb->SetValue(kCompUVChannels, 0, (int)(numUVChannels + curr), i); // label each layer's opacity UV mapping + } + numMaterials = (1 << maxMaterial->NumSubMtls()) - 1; // 2^n - 1 possibilites + + ourMaterials->Reset(); + for( i = 0; i < numMaterials; i++ ) // would be smarter to only create the materials we'll actually use + { + plExportMaterialData emd; + emd.fMaterial = ICreateMaterial(maxMaterial, node, name, i + 1, numUVChannels, makeAlphaLayer); + emd.fNumBlendChannels = (emd.fMaterial != nil && emd.fMaterial->NeedsBlendChannel() ? 1 : 0); + + // A bump layer requires 2 generated uv channels + if( HasBumpLayer(node, maxMaterial) ) + emd.fNumBlendChannels += 2; + + if (numBlendChannels < emd.fNumBlendChannels) + numBlendChannels = emd.fNumBlendChannels; + ourMaterials->Append(emd); + } + } + else // plPassMtl, plDecalMat, plMultiPassMtl, plParticleMtl + { + hsGMaterial *mat = ICreateMaterial(maxMaterial, node, name, -1, numUVChannels, makeAlphaLayer); + int maxLayer = (mat == nil ? 0 : mat->GetNumLayers()); + + numBlendChannels = (mat != nil && mat->NeedsBlendChannel() ? 1 : 0); + + // A bump layer requires 2 generated uv channels + if( HasBumpLayer(node, maxMaterial) ) + numBlendChannels += 2; + + plExportMaterialData emd; + emd.fMaterial = mat; + emd.fNumBlendChannels = numBlendChannels; + ourMaterials->Reset(); + ourMaterials->Append(emd); + } + + // We've already handled it... just letting them know. + if (fErrorMsg->Set( (numUVChannels + numBlendChannels > plGeometrySpan::kMaxNumUVChannels) && !(fWarned & kWarnedTooManyUVs), node->GetName(), + "Material wants %d UV channels for textures and blending, but only %d are available." + " Some layers will have incorrect channels assigned.", + numUVChannels + numBlendChannels, plGeometrySpan::kMaxNumUVChannels).CheckAskOrCancel() ) + fWarned |= kWarnedTooManyUVs; + + return ourMaterials; +} + +int hsMaterialConverter::MaxUsedUVWSrc(plMaxNode* node, Mtl* mtl) +{ + const char* dbgNodeName = node->GetName(); + + if( !mtl ) + return 0; + + if( IsMultiMat(mtl) || IsMultipassMat(mtl) || IsCompositeMat(mtl) ) + { + int i; + + int numUVWs = 0; + for( i = 0; i < mtl->NumSubMtls(); i++ ) + { + int num = MaxUsedUVWSrc(node, mtl->GetSubMtl(i)); + if( num > numUVWs ) + numUVWs = num; + } + return numUVWs; + } + + if( IsParticleMat(mtl) ) + { + return 1; + } + + if( IsHsMaxMat(mtl) || IsDecalMat(mtl) || IsBumpMtl(mtl) ) + { + plPassMtlBase* passMtl = (plPassMtlBase*)mtl; + + int numUVWs = 0; + + Texmap* tex = passMtl->GetBaseLayer(); + if( tex ) + { + if( tex->GetUVWSource() == UVWSRC_EXPLICIT ) + { + if( tex->GetMapChannel() > numUVWs ) + numUVWs = tex->GetMapChannel(); + + if( passMtl->GetTopLayerOn() && passMtl->GetTopLayer() ) + { + tex = passMtl->GetTopLayer(); + if( tex->GetMapChannel() > numUVWs ) + numUVWs = tex->GetMapChannel(); + } + } + + } + return numUVWs; + } + + return 0; +} + +hsGMaterial* hsMaterialConverter::NonAlphaHackPrint(plMaxNode* node, Texmap* baseTex, UInt32 blendFlags) +{ + // Bogus input, I hope they choke on the nil pointer I'm returning. + if( !(baseTex && node) ) + return nil; + + char name[256]; + sprintf(name, "%s_%s_%d", node->GetName(), baseTex->GetName(), 0); + + // Search done materials for it + + hsGMaterial* mat = TRACKED_NEW hsGMaterial; + hsgResMgr::ResMgr()->NewKey(name, mat, node->GetLocation()); + + // If plasmaLayer is nil, the artist has some other wierd (unsupported) layer type in the slot. + // Should warn them here. + plPlasmaMAXLayer* plasmaLayer = plPlasmaMAXLayer::GetPlasmaMAXLayer(baseTex); + if( !plasmaLayer ) + return nil; + + plLayerInterface *layerIFace = plLayerConverter::Instance().ConvertTexmap(baseTex, node, 0, false, false); + + plLayer* baseLay = plLayer::ConvertNoRef(layerIFace->BottomOfStack()); + if( !baseLay ) + return nil; + + baseLay->SetTransform(hsMatrix44::IdentityMatrix()); + baseLay->SetUVWSrc(0); + baseLay->SetBlendFlags(blendFlags); + baseLay->SetZFlags(hsGMatState::kZNoZWrite | hsGMatState::kZIncLayer); + baseLay->SetShadeFlags(0); + baseLay->SetClampFlags(hsGMatState::kClampTexture); + baseLay->SetMiscFlags(0); + + baseLay->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + baseLay->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + baseLay->SetPreshadeColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + + IAddLayerToMaterial(mat, layerIFace); + + return mat; +} + +hsGMaterial* hsMaterialConverter::AlphaHackPrint(plMaxNode* node, Texmap* baseTex, UInt32 blendFlags) +{ + // Bogus input, I hope they choke on the nil pointer I'm returning. + if( !(baseTex && node) ) + return nil; + + char name[256]; + sprintf(name, "%s_%s_%d_AH", node->GetName(), baseTex->GetName(), 0); + + // Search done materials for it + + hsGMaterial* mat = TRACKED_NEW hsGMaterial; + hsgResMgr::ResMgr()->NewKey(name, mat, node->GetLocation()); + + // If plasmaLayer is nil, the artist has some other wierd (unsupported) layer type in the slot. + // Should warn them here. + plPlasmaMAXLayer* plasmaLayer = plPlasmaMAXLayer::GetPlasmaMAXLayer(baseTex); + if( !plasmaLayer ) + return nil; + + plLayerInterface *layerIFace = plLayerConverter::Instance().ConvertTexmap(baseTex, node, 0, false, false); + + plLayer* baseLay = plLayer::ConvertNoRef(layerIFace->BottomOfStack()); + if( !baseLay ) + return nil; + + baseLay->SetTransform(hsMatrix44::IdentityMatrix()); + baseLay->SetUVWSrc(0); + baseLay->SetBlendFlags(blendFlags); + baseLay->SetZFlags(hsGMatState::kZNoZWrite | hsGMatState::kZIncLayer); + baseLay->SetShadeFlags(0); + baseLay->SetClampFlags(hsGMatState::kClampTexture); + baseLay->SetMiscFlags(0); + + baseLay->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + baseLay->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + baseLay->SetPreshadeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + + IAddLayerToMaterial(mat, layerIFace); + + plMipmap *texture = IGetUVTransTexture(node); + IInsertSingleBlendLayer(texture, mat, node, 1, 1); + + return mat; +} + +hsGMaterial* hsMaterialConverter::NonAlphaHackVersion(plMaxNode* node, Mtl* mtl, int subIndex) +{ + if( !mtl ) + return nil; + + if( IsMultiMat(mtl) ) + { + return nil; + } + + char name[256]; + sprintf(name, "%s_%s_%d", node->GetName(), mtl->GetName(), subIndex); + + return ICreateMaterial(mtl, node, name, subIndex, 1, false); +} + +hsGMaterial* hsMaterialConverter::AlphaHackVersion(plMaxNode* node, Mtl* mtl, int subIndex) +{ + if( !mtl ) + return nil; + + if( IsMultiMat(mtl) ) + { + return nil; + } + + char name[256]; + sprintf(name, "%s_%s_%d_AH", node->GetName(), mtl->GetName(), subIndex); + + return ICreateMaterial(mtl, node, name, subIndex, 1, true); +} + +// +// Big kahuna converter function +// (Though meshConverter should be calling CreateMaterialArray instead) +// +hsGMaterial *hsMaterialConverter::ICreateMaterial(Mtl *mtl, plMaxNode *node, const char *name, int subIndex, + int numUVChannels, hsBool makeAlphaLayer) +{ + hsGuardBegin("hsMaterialConverter::ICreateMaterial"); + + char *nodeName = node->GetName(); + fSubIndex = subIndex; + fNodeName = nodeName; + Object *obj = node->EvalWorldState(fConverterUtils.GetTime(fInterface)).obj; + if (obj && (obj->SuperClassID() == SClass_ID(LIGHT_CLASS_ID))) + { + return ICheckForProjectedTexture(node); + } + else + { + hsBool forceCopy = hsControlConverter::Instance().OwnsMaterialCopy(node); + if( !forceCopy ) + forceCopy = node->GetForceMaterialCopy(); + if( !forceCopy ) + forceCopy = IMustBeUniqueMaterial( mtl ); + + hsBool runtimeLit = node->GetRunTimeLight(); + + Mtl *bMtl = GetBaseMtl(node); + hsBool isMultiMat = IsMultiMat(bMtl);// || IsPortalMat(bMtl); + + hsGMaterial *mat; + if (mtl) + { + if (IsMatchingDoneMaterial(&fLastMaterial, mtl, isMultiMat, subIndex, forceCopy, runtimeLit, + node, numUVChannels, makeAlphaLayer)) + { + mat = fLastMaterial.fHsMaterial; + CopyMaterialLODToTextures(mat); + //hsRefCnt_SafeRef(mat); + return mat; + } + + Int32 i; + Int32 index(-1); + for (i = 0; i < fDoneMaterials.Count(); i++) + { + if (IsMatchingDoneMaterial(&fDoneMaterials[i], mtl, isMultiMat, subIndex, forceCopy, runtimeLit, + node, numUVChannels, makeAlphaLayer)) + { + index = i; + break; + } + } + + if (index != -1) + { + mat = fDoneMaterials[index].fHsMaterial; + + fLastMaterial = fDoneMaterials[index]; + + CopyMaterialLODToTextures(mat); + //hsRefCnt_SafeRef(mat); + return mat; + } + } + // Guess we haven't converted this one before. Bummer. + + // Get material + /* // CreateMaterial is not called on MultiMats (if CreateMaterialArray is doing its job) + if (IsMultiMat(mtl)) + { + mat = IProcessMaterial(mtl->GetSubMtl(subIndex), node, numUVChannels); + } + */ + if (IsCompositeMat(mtl)) + { + mat = IProcessMaterial(mtl, node, name, numUVChannels, subIndex); + IInsertCompBlendingLayers(mtl, node, mat, subIndex, numUVChannels, makeAlphaLayer); + } + else if (IsMultipassMat(mtl)) + { + mat = IProcessMaterial(mtl, node, name, numUVChannels); + IInsertMultipassBlendingLayers(mtl, node, mat, numUVChannels, makeAlphaLayer); + } + else if (IsHsMaxMat(mtl) || IsDecalMat(mtl)) + { + mat = IProcessMaterial(mtl, node, name, numUVChannels); + IInsertAlphaBlendingLayers(mtl, node, mat, numUVChannels, makeAlphaLayer); + } + else if (IsClothingMat(mtl)) + { + mat = nil; // clothing materials do not generate an hsGMaterial object + } + else { // Particle materials hit this. No need for blending layers, ever. + mat = IProcessMaterial(mtl, node, name, numUVChannels); + } + if( mat && HasBumpLayer(node, mtl) ) + IInsertBumpLayers(node, mat); + + if (mat) + mat = IInsertDoneMaterial(mtl, mat, node, isMultiMat, forceCopy, runtimeLit, subIndex, numUVChannels, makeAlphaLayer); + + return mat; + } + return nil; + hsGuardEnd; +} + +#include "../MaxPlasmaMtls/Materials/plMultipassMtl.h" + +// +// Handle materials for normal non-light, non-particle nodes. +// +hsGMaterial *hsMaterialConverter::IProcessMaterial(Mtl *mtl, plMaxNode *node, const char *name, + int UVChan, int subMtlFlags /* = 0 */) +{ + hsGuardBegin("hsMaterialConverter::IProcessMaterial"); + + plLocation nodeLoc = node->GetLocation(); + + char *dbgNodeName = node->GetName(); + hsGMaterial *hMat = nil; + fChangedTimes = false; + + if (IsMultiMat(mtl)) + { + if (fErrorMsg->Set(!(fWarned & kWarnedSubMulti), dbgNodeName, "Multi-material in ProcessMaterial (Multi child of multi?) on mat %s.", + mtl->GetName()).CheckAskOrCancel() ) + fWarned |= kWarnedSubMulti; + hMat = IProcessMaterial(mtl->GetSubMtl(0), node, name, UVChan); + } + else if (IsHsMaxMat(mtl) || IsDecalMat(mtl) || IsBumpMtl( mtl ) ) + { + hMat = TRACKED_NEW hsGMaterial; + hsgResMgr::ResMgr()->NewKey(name, hMat,nodeLoc); + IProcessPlasmaMaterial(mtl, node, hMat, hMat->GetKey()->GetName()); + } + else if (mtl && mtl->ClassID() == MULTIMTL_CLASS_ID) + { + hMat = IProcessMultipassMtl(mtl, node, name, UVChan); + } + else if (IsCompositeMat(mtl)) + { + hMat = IProcessCompositeMtl(mtl, node, name, UVChan, subMtlFlags); + } + else if (IsParticleMat(mtl)) + { + hMat = IProcessParticleMtl(mtl, node, name); + } + else + { + hMat = IAddDefaultMaterial(node); + } + + if (hMat) + { + if (node->GetAvatarSO() != nil) + { + AttachLinkMtlAnims(node, hMat); + } + if (hMat->GetNumLayers() == 0) + { + if (fErrorMsg->Set((fWarned & kWarnedNoLayers) == 0, node->GetName(), "Material has no layers. (%s)", mtl->GetName()).CheckAndAsk()) + fWarned |= kWarnedNoLayers; + + plLayer* hLay = TRACKED_NEW plLayer; + hLay->InitToDefault(); + hsgResMgr::ResMgr()->NewKey(TSTR(name) + TSTR("_DefLay"), hLay, nodeLoc); + IAddLayerToMaterial(hMat, hLay); + } + + if( node->UserPropExists("WetMe") && !strstr(hMat->GetKey()->GetName(), "Wet(*)") ) + IAppendWetLayer(node, hMat); +// hsgResMgr::ResMgr()->NewKey(name, hMat,nodeLoc); + } + else + return nil; + + if (hMat->IsDynamic()) + { + fChangedTimes = true; + } + + CopyMaterialLODToTextures(hMat); + + return hMat; + hsGuardEnd; +} + +hsBool hsMaterialConverter::IsMatchingDoneMaterial(DoneMaterialData *dmd, + Mtl *mtl, hsBool isMultiMat, UInt32 subMtlFlags, hsBool forceCopy, hsBool runtimeLit, + plMaxNode *node, int numUVChannels, hsBool makeAlphaLayer) +{ + if (!((dmd->fMaxMaterial == mtl) && + (dmd->fSubMultiMat == isMultiMat) && + (dmd->fSubMtlFlags == subMtlFlags) && + (dmd->fRuntimeLit == runtimeLit) && + (dmd->fNumUVChannels == numUVChannels) && + (dmd->fMakeAlphaLayer == makeAlphaLayer))) + { + if( dmd->fMaxMaterial == mtl ) + { + if( dmd->fRuntimeLit != runtimeLit ) + failedRT = true; + if( dmd->fNumUVChannels != numUVChannels ) + failedNumUV = true; + if( dmd->fMakeAlphaLayer != makeAlphaLayer ) + failedAlphaLayer = true; + } + + return false; + } + if( dmd->fNode->HasFade() != node->HasFade() ) + { + bool realFade = false; + if( dmd->fNode->HasFade() ) + { + Box3 fade = dmd->fNode->GetFade(); + if( (fade.Min()[0] != fade.Min()[1]) || (fade.Max()[0] != fade.Max()[1]) ) + realFade = true; + } + else if( node->HasFade() ) + { + Box3 fade = node->GetFade(); + if( (fade.Min()[0] != fade.Min()[1]) || (fade.Max()[0] != fade.Max()[1]) ) + realFade = true; + } + + if( realFade ) + { + failedFade = true; + return false; + } + } + if( dmd->fNode->HasFade() && node->HasFade() ) + { + Box3 dmdFade = dmd->fNode->GetFade(); + Box3 nodeFade = node->GetFade(); + if( dmdFade.Min() != nodeFade.Min() ) + { + if( (dmdFade.Min()[0] != dmdFade.Min()[1]) || (nodeFade.Min()[0] != nodeFade.Min()[1]) ) + { + failedFade = true; + return false; + } + } + if( dmdFade.Max() != nodeFade.Max() ) + { + if( (dmdFade.Max()[0] != dmdFade.Max()[1]) || (nodeFade.Max()[0] != nodeFade.Max()[1]) ) + { + failedFade = true; + return false; + } + } + } + + return !(forceCopy || dmd->fOwnedCopy) || (dmd->fNode == node); +} + + +hsGMaterial* hsMaterialConverter::IInsertDoneMaterial(Mtl *mtl, hsGMaterial *hMat, plMaxNode *node, hsBool isMultiMat, + hsBool forceCopy, hsBool runtimeLit, UInt32 subMtlFlags, int numUVChannels, + hsBool makeAlphaLayer) +{ + if( failedRT ) + dupCuzRT++; + if( failedNumUV ) + dupCuzNumUV++; + if( failedAlphaLayer ) + dupCuzAlphaLayer++; + if( failedFade ) + dupCuzFade++; + failedRT = failedNumUV = failedAlphaLayer = failedFade = false; + + DoneMaterialData done; + done.fHsMaterial = hMat; +// hsRefCnt_SafeAssign(done.fHsMaterial, hMat); + done.fHsMaterial = hMat; + done.fMaxMaterial = mtl; + done.fNode = node; + done.fSubMultiMat = isMultiMat; + done.fOwnedCopy = forceCopy; + done.fRuntimeLit = runtimeLit; + done.fSubMtlFlags = subMtlFlags; + done.fNumUVChannels = numUVChannels; + done.fMakeAlphaLayer = makeAlphaLayer; + + DoneMaterialData* equivalent = IFindDoneMaterial(done); + + if( equivalent ) + { + plKey matKey = hMat->GetKey(); + matKey->RefObject(); + matKey->UnRefObject(); + ((plKeyImp *)matKey)->SetObjectPtr(nil); + matKey = nil; + + hMat = equivalent->fHsMaterial; + } + else + { + hMat->GetKey()->RefObject(); // Matching unref in hsMaterialConverter::DeInit(); + + fDoneMaterials.Append(done); + } + + return hMat; +} + +hsGMaterial *hsMaterialConverter::IAddDefaultMaterial(plMaxNode *node) +{ + if (!node) + return nil; + + plLocation loc = node->GetLocation(); + + hsGMaterial *hMat = TRACKED_NEW hsGMaterial; + hsgResMgr::ResMgr()->NewKey(TSTR(node->GetName()) + TSTR("_DefMat"), hMat, loc); + + plLayer *layer = TRACKED_NEW plLayer; + layer->InitToDefault(); + hsgResMgr::ResMgr()->NewKey(TSTR(hMat->GetKeyName()) + TSTR("_DefLay"), layer, loc); + + DWORD color = node->GetWireColor(); + hsScalar r = hsScalar(GetRValue(color)) / 255.f; + hsScalar g = hsScalar(GetGValue(color)) / 255.f; + hsScalar b = hsScalar(GetBValue(color)) / 255.f; + layer->SetRuntimeColor(hsColorRGBA().Set(r, g, b, 1.f)); + layer->SetPreshadeColor(hsColorRGBA().Set(r, g, b, 1.f)); + layer->SetOpacity(1.f); + IAddLayerToMaterial(hMat, layer); + + return hMat; +} + +plMipmap *hsMaterialConverter::IGetUVTransTexture(plMaxNode *node, hsBool useU /* = true */) +{ + char* texName = (useU ? "ALPHA_BLEND_FILTER_U2ALPHA_TRANS_64x4" : "ALPHA_BLEND_FILTER_V2ALPHA_TRANS_4x64" ); + + int w = (useU ? 64 : 4); + int h = (useU ? 4 : 64); + + // NOTE: CreateBlankMipmap might return an old mipmap if it was already created, so in those + // cases we wouldn't really need to re-write the texture. However, there's no harm in doing so, + // and since we're close to Alpha, I don't want to shake up the code any more than absolutely + // necessary. -mcn + plMipmap *texture = plBitmapCreator::Instance().CreateBlankMipmap( w, h, plMipmap::kARGB32Config, 1, texName, node->GetLocation() ); + + // set the color data + UInt32* pix = (UInt32*)texture->GetImage(); + int x, y; + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + // Filter either the U or V coordinate + float shade = (useU ? (float)x / (w - 1) : (float)y / (h - 1)); + + // These colors MUST be all white even though we're usuing the kBlendNoColor flag... + // Rad huh? Hooray for nVidia!!! + *pix++ = MakeUInt32Color(1.0, 1.0, 1.0, shade); + } + } + + return texture; +} + +//// IInsertSingleBlendLayer ////////////////////////////////////////////////// +// 10.24.01 mcn - Added kMiscRestartPassHere to the layer below the inserted +// one, to guarantee that the hacked alpha layers draw in their +// own passes on the GF3. Keeping this here until we can properly +// handle the hack layers as piggybacks in the pipeline. + +void hsMaterialConverter::IInsertSingleBlendLayer(plMipmap *texture, hsGMaterial *mat, + plMaxNode *node, int layerIdx, int UVChan) +{ + // Need to tweak a few flags on its corresponding layer + plLayer* underLay = plLayer::ConvertNoRef(mat->GetLayer(layerIdx - 1)->BottomOfStack()); + if( !underLay ) + return; + + // This error means a GeForce 1 or 2 won't do the vertex alpha correctly, and every other card + // won't care. We've been ignoring this warning for years anyway, might as well just take it + // out and stop interrupting the export. +// fErrorMsg->Set((underLay->GetMiscFlags() & hsGMatState::kMiscBindNext) != 0, node->GetName(), +// "Layer %s has its BindNext flag set, which can't be done with a vertex alpha blend. The " +// "resulting material may not blend correctly.", underLay->GetKeyName()).Show(); + underLay->SetMiscFlags(underLay->GetMiscFlags() | hsGMatState::kMiscBindNext + | hsGMatState::kMiscRestartPassHere ); + underLay->SetBlendFlags(underLay->GetBlendFlags() | hsGMatState::kBlendAlpha); + mat->SetCompositeFlags(mat->GetCompositeFlags() | hsGMaterial::kCompNeedsBlendChannel); + + + plLayer* layer = TRACKED_NEW plLayer; + layer->InitToDefault(); + hsgResMgr::ResMgr()->NewKey(TSTR(underLay->GetKeyName()) + TSTR("_AlphaBlend"), layer, node->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(texture->GetKey(), TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef); + layer->SetAmbientColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); +// layer->SetZFlags(hsGMatState::kZNoZWrite | hsGMatState::kZIncLayer); + // The inclayer prop probably wouldn't hurt here, because this layer should only get drawn as + // an upper layer, but I'm nuking it out for consistency. mf + layer->SetZFlags(hsGMatState::kZNoZWrite); + UInt32 blendFlags = hsGMatState::kBlendNoTexColor | hsGMatState::kBlendAlphaMult | hsGMatState::kBlendAlpha; + layer->SetBlendFlags(blendFlags); + layer->SetClampFlags(hsGMatState::kClampTexture); + layer->SetUVWSrc(UVChan); + layer->SetMiscFlags(0); + + // Insert it in the right spot. + hsgResMgr::ResMgr()->AddViaNotify(layer->GetKey(), TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnCreate, + layerIdx, plMatRefMsg::kLayer | plMatRefMsg::kInsert), plRefFlags::kActiveRef); +} + +void hsMaterialConverter::IInsertAlphaBlendingLayers(Mtl *mtl, plMaxNode *node, hsGMaterial *mat, int UVChan, + hsBool makeAlphaLayer) +{ + if ((mat->GetLayer( 0 )->GetState().fBlendFlags & hsGMatState::kBlendAlpha) == 0 || UVChan < 0 || !makeAlphaLayer) + return; // Not blending... false alarm... my bad... enjoy the buffet! + + if (!(UVChan < plGeometrySpan::kMaxNumUVChannels)) + { + if (fErrorMsg->Set(!(fWarned & kWarnedTooManyUVs), node->GetName(), + "Material is already using all available UV channels and thus doesn't have one for alpha " + "blending. Some layers will have incorrect channels assigned. (%s)", + mtl->GetName()).CheckAskOrCancel() ) + fWarned |= kWarnedTooManyUVs; + UVChan = plGeometrySpan::kMaxNumUVChannels - 1; + } + int i; + + hsGMaterial *objMat = mat; + plMipmap *texture = IGetUVTransTexture(node); + int origLayers = objMat->GetNumLayers(); + for (i = 0; i < origLayers; i++) + { + IInsertSingleBlendLayer(texture, objMat, node, 2 * i + 1, UVChan); + } +} + +void hsMaterialConverter::IInsertMultipassBlendingLayers(Mtl *mtl, plMaxNode *node, hsGMaterial *mat, int UVChan, + hsBool makeAlphaLayer) +{ + if (UVChan < 0 || !makeAlphaLayer) + return; // Not blending, no layers to insert. + + if (!(UVChan < plGeometrySpan::kMaxNumUVChannels)) + { + if (fErrorMsg->Set(!(fWarned & kWarnedTooManyUVs), node->GetName(), + "Material is already using all available UV channels and thus doesn't have one for alpha " + "blending. Some layers will have incorrect channels assigned. (%s)", + mtl->GetName()).CheckAskOrCancel() ) + fWarned |= kWarnedTooManyUVs; + UVChan = plGeometrySpan::kMaxNumUVChannels - 1; + } + + int i; + hsGMaterial *objMat = mat; + IParamBlock2 *pb = mtl->GetParamBlockByID(plMultipassMtl::kBlkPasses); + plMipmap *texture = IGetUVTransTexture(node); + int currLayerNum = 0; + for (i = 0; i < mtl->NumSubMtls(); i++) + { + if (!pb->GetInt(kMultOn, 0, i)) // Is the box for this submtl checked? + continue; // No, skip it! + + if ((objMat->GetLayer(currLayerNum)->GetBlendFlags() & hsGMatState::kBlendAlpha) == 0) + { // not alpha blending... skip! + currLayerNum += pb->GetInt(kMultLayerCounts, 0, i); + continue; + } + int j; + + for (j = 0; j < pb->GetInt(kMultLayerCounts, 0, i); j++) + { + IInsertSingleBlendLayer(texture, objMat, node, currLayerNum + 1, UVChan); + currLayerNum += 2; + } + } + return; +} + +void hsMaterialConverter::IInsertCompBlendingLayers(Mtl *mtl, plMaxNode *node, hsGMaterial *mat, int subMtlFlags, + int UVChan, hsBool makeAlphaLayer) +{ + if (mat == nil) + return; + + if (!IsCompositeMat(mtl)) + { + hsAssert(false, "IInsertCompBlendingLayers() called on a non-composite material."); + return; + } + + int i; + hsGMaterial *objMat = mat; + plMipmap *textureU = IGetUVTransTexture(node, true); + plMipmap *textureV = IGetUVTransTexture(node, false); + IParamBlock2 *pb = mtl->GetParamBlockByID(plCompositeMtl::kBlkPasses); + int currLayerNum = 0; + int bitmask; + hsBool firstUsedLayer = true; + hsBool canWriteAlpha = ((plCompositeMtl *)mtl)->CanWriteAlpha() >= 0; + plMipmap *currTexture; + for (i = 0, bitmask = 0x1; i < mtl->NumSubMtls(); i++, bitmask <<= 1) + { + if ((bitmask & subMtlFlags) == 0) // skip it! + continue; + + if (firstUsedLayer) + { + firstUsedLayer = false; + if (i != 0) // it's not the base layer, so it must be covering the base layer up. Thus, it's + { // fully opaque, and we shouldn't blend. + plLayer* underLay = plLayer::ConvertNoRef(objMat->GetLayer(currLayerNum)->BottomOfStack()); + if( underLay ) + underLay->SetBlendFlags(underLay->GetBlendFlags() & ~hsGMatState::kBlendMask); + currLayerNum += pb->GetInt(kCompLayerCounts, 0, i); + continue; + } + } + + if (i == 0 && ((mat->GetLayer( 0 )->GetState().fBlendFlags & hsGMatState::kBlendAlpha) == 0)) + { + currLayerNum += pb->GetInt(kCompLayerCounts, 0, i); + continue; + } + + int method = (i <= 0 ? kCompBlendVertexAlpha : pb->GetInt(kCompBlend, 0, i - 1)); + currTexture = (i == 1 ? textureU : textureV); + int j; + for (j = 0; j < pb->GetInt(kCompLayerCounts, 0, i); j++) + { + if (!makeAlphaLayer && canWriteAlpha) + { + if (((plCompositeMtl *)mtl)->IsInverseBlend(method)) + { + plLayer* underLay = plLayer::ConvertNoRef(objMat->GetLayer(currLayerNum)->BottomOfStack()); + underLay->SetBlendFlags(underLay->GetBlendFlags() | hsGMatState::kBlendInvertVtxAlpha); + } + currLayerNum++; + continue; + } + + IInsertSingleBlendLayer(currTexture, objMat, node, currLayerNum + 1, UVChan); + currLayerNum += 2; + } + } + return; +} + +hsGMaterial *hsMaterialConverter::IProcessCompositeMtl(Mtl *mtl, plMaxNode *node, const char *name, int UVChan, int subMtlFlags) +{ + if (!mtl || mtl->ClassID() != COMP_MTL_CLASS_ID) + return nil; + UInt32 *layerCounts = TRACKED_NEW UInt32[mtl->NumSubMtls()]; + IParamBlock2 *pb = mtl->GetParamBlockByID(plCompositeMtl::kBlkPasses); + char suff[10]; + sprintf(suff, "_%d", subMtlFlags); + hsGMaterial *mat = TRACKED_NEW hsGMaterial; + hsgResMgr::ResMgr()->NewKey(TSTR(name) + TSTR(suff), mat, node->GetLocation()); + + int multiIndex = IFindSubIndex(node, mtl); + hsBool needAlphaHack = node->AlphaHackLayersNeeded(multiIndex) > 0; + hsBool canWriteAlpha = ((plCompositeMtl *)mtl)->CanWriteAlpha() >= 0; + + int bitMask = 1; + int i; + for (i = 0; i < mtl->NumSubMtls(); i++) + { + Mtl *subMtl = nil; + hsBool usingSubMtl = (i == 0 || pb->GetInt(kCompOn, 0, i - 1)); + if ((bitMask & subMtlFlags) != 0 && usingSubMtl) + { + char pref[256]; + sprintf(pref, "%s_%d", mat->GetKey()->GetName(), i); + + subMtl = mtl->GetSubMtl(i); + if (subMtl != nil && subMtl->ClassID() == PASS_MTL_CLASS_ID) + IProcessPlasmaMaterial(subMtl, node, mat, pref); + } + bitMask <<= 1; + layerCounts[i] = mat->GetNumLayers(); + if (i > 0 && mat->GetNumLayers() > 0) + { + hsBool materialIsBad = false; + int j; + if (usingSubMtl) + { + for (j = mat->GetNumLayers() - 1; j >= (int)layerCounts[i - 1]; j--) + { + UInt32 blendFlags = mat->GetLayer(j)->GetBlendFlags(); + if ((blendFlags & hsGMatState::kBlendMask) != hsGMatState::kBlendAlpha) + { + hsBool ignore = fErrorMsg->Set(!(fWarned & kWarnedCompMtlBadBlend), node->GetName(), + "For composite materials, all multi-layered submaterials (except the base)" + " must choose 'alpha' for 'output blending'. %s, which is a sub-material" + " of %s, doesn't and will not be included in the composite material.", + subMtl->GetName(), mtl->GetName()).CheckAskOrCancel(); + + materialIsBad = true; + if (ignore) + fWarned |= kWarnedCompMtlBadBlend; + } + + if ((needAlphaHack || !canWriteAlpha) && + ((blendFlags & hsGMatState::kBlendAlphaMult) || (blendFlags & hsGMatState::kBlendAlphaAdd))) + { + hsBool ignore = fErrorMsg->Set(!(fWarned & kWarnedCompMtlBadBlend), node->GetName(), + "Composite material %s has a submaterial, %s, that requires too many textures in a single pass " + "(for blending effects). To cut this down, try some of the following:\n" + "1. Make sure all multi-layered submaterials (except the base)" + " choose 'alpha' for 'layer blending', and 'base alpha only' for 'layer alpha blending'\n" + "2. Force the object to be runtime lit.\n" + "3. Remove other components on this node that may add extra blending layers.\n" + "For now, the submaterial will not be included in the composite.", + mtl->GetName(), subMtl->GetName()).CheckAskOrCancel(); + + materialIsBad = true; + if (ignore) + fWarned |= kWarnedCompMtlBadBlend; + } + } + } + if (materialIsBad) // Nuke all the layers of this sub material, so the artists don't just ignore the warnings + { + int min = (int)layerCounts[i - 1]; + for (j = mat->GetNumLayers() - 1; j >= min; j--) + { + mat->GetKey()->Release(mat->GetLayer(j)->GetKey()); + } + layerCounts[i] = mat->GetNumLayers(); + } + } + } + + for (i = mtl->NumSubMtls() - 1; i > 0; i--) + layerCounts[i] -= layerCounts[i - 1]; + + for (i = 0; i < mtl->NumSubMtls(); i++) + pb->SetValue(kCompLayerCounts, 0, (int)layerCounts[i], i); + + delete [] layerCounts; + if (mat->GetNumLayers() == 0) + { + // No one ever got a ref, so we ref and unref to make it go away. + mat->GetKey()->RefObject(); + mat->GetKey()->UnRefObject(); + return IAddDefaultMaterial(node); + } + + return mat; +} + +hsGMaterial *hsMaterialConverter::IProcessMultipassMtl(Mtl *mtl, plMaxNode *node, const char *name, int UVChan) +{ + if (!mtl || mtl->ClassID() != MULTIMTL_CLASS_ID) + return nil; + + hsGMaterial *mat = TRACKED_NEW hsGMaterial; + UInt32 *layerCounts = TRACKED_NEW UInt32[mtl->NumSubMtls()]; + hsgResMgr::ResMgr()->NewKey(name, mat, node->GetLocation()); + + IParamBlock2 *pb = mtl->GetParamBlockByID(plMultipassMtl::kBlkPasses); + for (int i = 0; i < mtl->NumSubMtls(); i++) + { + Mtl *subMtl = mtl->GetSubMtl(i); + int check = pb->GetInt(kMultOn, 0, i); + if ( ( subMtl->ClassID() == PASS_MTL_CLASS_ID || + subMtl->ClassID() == BUMP_MTL_CLASS_ID ) && check != 0) + { + IProcessPlasmaMaterial(subMtl, node, mat, mat->GetKey()->GetName()); + } + layerCounts[i] = mat->GetNumLayers(); + } + + for (i = mtl->NumSubMtls() - 1; i > 0; i--) + layerCounts[i] -= layerCounts[i - 1]; + + for (i = 0; i < mtl->NumSubMtls(); i++) + pb->SetValue(kMultLayerCounts, 0, (int)layerCounts[i], i); + + delete [] layerCounts; + return mat; +} + +hsGMaterial *hsMaterialConverter::IProcessParticleMtl(Mtl *mtl, plMaxNode *node, const char *name) +{ + hsGuardBegin("hsMaterialConverter::IProcessParticleMaterial"); + + plLocation nodeLoc = node->GetLocation(); + char* dbgNodeName = node->GetName(); + + hsGMaterial *mat = TRACKED_NEW hsGMaterial; + hsgResMgr::ResMgr()->NewKey(name, mat,nodeLoc); + + + if(!mtl) + { + fErrorMsg->Set(!mtl, "Material Loading Error", "No material in ProcessParticleMaterial!").Show(); + fErrorMsg->Set(); + return nil; + } + + plParticleMtl *particleMtl = (plParticleMtl *)mtl; + IParamBlock2 *basicPB = mtl->GetParamBlockByID(plParticleMtl::kBlkBasic); + // + // Color / Opacity + // + Color amb = basicPB->GetColor(plParticleMtl::kColorAmb);; + Color dif = basicPB->GetColor(plParticleMtl::kColor); + float opac = float(basicPB->GetInt(plParticleMtl::kOpacity)) / 100.0f; + + fErrorMsg->Set(opac < 0.0 || opac > 1.0, dbgNodeName, "Bad opacity on mat %s", name).CheckAndAsk(); + fErrorMsg->Set(dif.r < 0.0 || dif.r > 1.0, dbgNodeName, "Bad color (r) on mat %s", name).CheckAndAsk(); + fErrorMsg->Set(dif.g < 0.0 || dif.g > 1.0, dbgNodeName, "Bad color (g) on mat %s", name).CheckAndAsk(); + fErrorMsg->Set(dif.b < 0.0 || dif.b > 1.0, dbgNodeName, "Bad color (b) on mat %s", name).CheckAndAsk(); + + Color col = dif; + + // + // Blend flags + // + UInt32 blendFlags = 0; + + // + // Shade flags + // + UInt32 shadeFlags = 0; + + if (basicPB->GetInt(plParticleMtl::kNormal) == plParticleMtl::kEmissive) + shadeFlags |= hsGMatState::kShadeEmissive; + if( node->GetIsGUI() ) + { + // We don't want materials on any GUI objects to *ever* be fogged + // (just in case we have some weird GUI particle system...)...4.25.2002 mcn + shadeFlags |= hsGMatState::kShadeReallyNoFog; + } + + // + // Misc flags... + // + UInt32 miscFlags = 0; + + // + // Z flags + // + UInt32 zFlags = 0; + zFlags |= hsGMatState::kZNoZWrite; + + hsBool preserveUVOffset = false;//PreserveUVOffset(mtl); + + // + // Process base layer + // + plLayer* baseLay = nil; + Texmap *baseTex = basicPB->GetTexmap(plParticleMtl::kTexmap); + if (baseTex && baseTex->ClassID() == LAYER_TEX_CLASS_ID) + { + plLayerInterface *layerIFace = plLayerConverter::Instance().ConvertTexmap( baseTex, node, 0, preserveUVOffset, false ); + + baseLay = plLayer::ConvertNoRef(layerIFace->BottomOfStack()); + + fErrorMsg->Set(!baseLay, node->GetName(), "Found no layer at end of IProcessPlasmaLayer()").Check(); + + baseLay->SetBlendFlags(baseLay->GetBlendFlags() | blendFlags); + baseLay->SetShadeFlags(baseLay->GetShadeFlags() | shadeFlags); + baseLay->SetZFlags(baseLay->GetZFlags() | zFlags); + baseLay->SetMiscFlags(baseLay->GetMiscFlags() | miscFlags); // 3.28.2001 mcn - Who didn't put this in? + + // No specular on particles. Spoot. + baseLay->SetSpecularColor( hsColorRGBA().Set(0,0,0,1.f) ); + baseLay->SetSpecularPower( 0 ); + + baseLay->SetAmbientColor(hsColorRGBA().Set(amb.r, amb.g, amb.b, hsScalar1)); + baseLay->SetRuntimeColor(hsColorRGBA().Set(col.r, col.g, col.b, hsScalar1)); + baseLay->SetPreshadeColor(hsColorRGBA().Set(0.f,0.f,0.f,1.f)); + baseLay->SetOpacity( opac ); // Don't scale the material color by this if we're add blending; do that later + + UInt32 blendType = 0; + switch (basicPB->GetInt(plParticleMtl::kBlend)) + { + case plParticleMtl::kBlendAlpha: blendType |= hsGMatState::kBlendAlpha; break; + case plParticleMtl::kBlendAdd: blendType |= hsGMatState::kBlendAdd; break; + } + baseLay->SetBlendFlags(baseLay->GetBlendFlags() | blendType); + + plLayerInterface *layerAnim = layerIFace; + + IAddLayerToMaterial(mat, layerAnim); + } + + + return mat; + hsGuardEnd; +} + +plLayerAnimation *IConvertNoteTrackAnims(plLayerAnimation *animLayer, SegmentMap *segMap, plMaxNode *node, + const char *name) +{ + plLayerAnimation *prev = animLayer; + + for (SegmentMap::iterator it = segMap->begin(); it != segMap->end(); it++) + { + SegmentSpec *spec = it->second; + if (spec->fType == SegmentSpec::kAnim) + { + plLayerAnimation *noteAnim = TRACKED_NEW plLayerAnimation; + TSTR animName = TSTR(name) + TSTR("_anim_") + TSTR(spec->fName); + hsgResMgr::ResMgr()->NewKey(animName, noteAnim, node->GetLocation()); + + if (animLayer->GetPreshadeColorCtl()) + noteAnim->SetPreshadeColorCtl(animLayer->GetPreshadeColorCtl()); + if (animLayer->GetRuntimeColorCtl()) + noteAnim->SetRuntimeColorCtl(animLayer->GetRuntimeColorCtl()); + if (animLayer->GetAmbientColorCtl()) + noteAnim->SetAmbientColorCtl(animLayer->GetAmbientColorCtl()); + if (animLayer->GetSpecularColorCtl()) + noteAnim->SetSpecularColorCtl(animLayer->GetSpecularColorCtl()); + if (animLayer->GetOpacityCtl()) + noteAnim->SetOpacityCtl(animLayer->GetOpacityCtl()); + + noteAnim->GetTimeConvert().SetBegin(spec->fStart); + noteAnim->GetTimeConvert().SetEnd(spec->fEnd); + + noteAnim->AttachViaNotify(prev); + prev = noteAnim; + } + } + + return prev; +} + +void ISetDefaultAnim(plPassMtlBase* mtl, plAnimTimeConvert& tc, SegmentMap* segMap) +{ + const char *animName = mtl->GetAnimName(); + if( segMap && animName && (segMap->find(animName) != segMap->end()) ) + { + SegmentSpec *spec = (*segMap)[animName]; + tc.SetBegin(spec->fStart); + tc.SetEnd(spec->fEnd); + } + + if (mtl->GetAutoStart()) + tc.Start(0); + else + tc.Stop(true); + + if (mtl->GetLoop()) + { + // Default to the entire anim + hsScalar loopStart = tc.GetBegin(); + hsScalar loopEnd = tc.GetEnd(); + + // If there's a loop, use it + const char *loopName = mtl->GetAnimLoopName(); + if (loopName && *loopName && segMap) + GetSegMapAnimTime(loopName, segMap, SegmentSpec::kLoop, loopStart, loopEnd); + + tc.SetLoopPoints(loopStart, loopEnd); + tc.Loop(true); + } + else + tc.Loop(false); +} + +void StuffStopPoints(SegmentMap *segMap, hsTArray &out) +{ + if (segMap == nil) + return; + + for (SegmentMap::iterator it = segMap->begin(); it != segMap->end(); it++) + { + SegmentSpec *spec = it->second; + if (spec->fType == SegmentSpec::kStopPoint) + { + out.Append(spec->fStart); + } + } +} + +void IProcessMtlAnimEase(plPassMtlBase *mtl, plAnimTimeConvert &tc) +{ + if (mtl->GetEaseInType() != plAnimEaseTypes::kNoEase) + { + tc.SetEase(true, mtl->GetEaseInType(), mtl->GetEaseInMinLength(), + mtl->GetEaseInMaxLength(), mtl->GetEaseInNormLength()); + } + if (mtl->GetEaseOutType() != plAnimEaseTypes::kNoEase) + { + tc.SetEase(false, mtl->GetEaseOutType(), mtl->GetEaseOutMinLength(), + mtl->GetEaseOutMaxLength(), mtl->GetEaseOutNormLength()); + } +} +/* +hsBool hsMaterialConverter::CheckValidityOfSDLVarAnim(plPassMtlBase *mtl, char *varName, plMaxNode *node) +{ + plStateDescriptor *sd = nil; + char *ageName; + ageName = node->GetAgeName(); + if (ageName) + sd = plSDLMgr::GetInstance()->FindDescriptor(ageName, plSDL::kLatestVersion); + if (sd) + { + plVarDescriptor *var = sd->FindVar(varName); + if (var == nil) + { + char buff[512]; + sprintf(buff, "Cannot find variable named \"%s\" used to animate the material " + "\"%s\". This material won't be animated.", varName, mtl->GetName()); + if (fErrorMsg->Set(!(fWarned & kWarnedBadAnimSDLVarName), node->GetName(), buff).CheckAskOrCancel() ) + fWarned |= kWarnedBadAnimSDLVarName; + return false; + } + if (!(var->GetType() == plVarDescriptor::kFloat || + var->GetType() == plVarDescriptor::kDouble || + var->GetType() == plVarDescriptor::kTime || + var->GetType() == plVarDescriptor::kAgeTimeOfDay)) + { + char buff[512]; + sprintf(buff, "Material \"%s\" animates on the age variable \"%s\", which is " + "of type \"%s\". Only the types float/double/time/ageTimeOfDay may be used. " + "Ignoring the animation of this material.", mtl->GetName(), varName, var->GetTypeString()); + if (fErrorMsg->Set(!(fWarned & kWarnedBadAnimSDLVarName), node->GetName(), buff).CheckAskOrCancel()) + fWarned |= kWarnedBadAnimSDLVarName; + return false; + } + return true; + } + return false; +} +*/ + +static plAnimStealthNode* IGetEntireAnimation(plPassMtlBase* mtl) +{ + const int count = mtl->GetNumStealths(); + int i; + for( i = 0; i < count; i++ ) + { + plAnimStealthNode* stealth = mtl->GetStealth(i); + + const char *segName = stealth->GetSegmentName(); + if( !segName || !*segName || !stricmp(segName, ENTIRE_ANIMATION_NAME) ) + return stealth; + + } + return nil; +} + +static plLayerInterface* IProcessLayerMovie(plPassMtlBase* mtl, plLayerTex* layTex, plMaxNode* node, + plLayerInterface* layerIFace) +{ + IParamBlock2* bitmapPB = layTex->GetParamBlockByID( plLayerTex::kBlkBitmap ); + if( !bitmapPB ) + return layerIFace; + + PBBitmap *pbbm = nil; + + if( !bitmapPB->GetInt(kBmpUseBitmap) || !(pbbm = bitmapPB->GetBitmap(kBmpBitmap)) ) + return layerIFace; + + BitmapInfo *bi = &pbbm->bi; + if( !bi || !bi->Name() || !*bi->Name() ) + return layerIFace; + + const char* fileName = bi->Name(); + + plAnimStealthNode* stealth = IGetEntireAnimation(mtl); + + const char* ext = plFileUtils::GetFileExt(fileName); + bool isBink = ext && (stricmp(ext, "bik") == 0); + bool isAvi = ext &&(stricmp(ext, "avi") == 0); + + if (isBink || isAvi) + { + char movieName[256]; + sprintf(movieName, "avi/%s", plFileUtils::GetFileName(fileName)); + + plLayerMovie* movieLayer = nil; + const char* moviePostfix = nil; + + if (isBink) + { + movieLayer = TRACKED_NEW plLayerBink; + moviePostfix = "_bink"; + } + else if (isAvi) + { + movieLayer = TRACKED_NEW plLayerAVI; + moviePostfix = "_avi"; + } + + TSTR movieKeyName = TSTR(layerIFace->GetKeyName()) + moviePostfix; + hsgResMgr::ResMgr()->NewKey(movieKeyName, movieLayer, node->GetLocation()); + + movieLayer->SetMovieName(movieName); + movieLayer->Eval(0,0,0); + + plAnimTimeConvert& tc = movieLayer->GetTimeConvert(); + + if (stealth) + { + if (stealth->GetAutoStart()) + tc.Start(0); + else + tc.Stop(true); + + tc.Loop(stealth->GetLoop()); + } + tc.SetLoopPoints(0, movieLayer->GetLength()); + tc.SetBegin(0); + tc.SetEnd(movieLayer->GetLength()); + + movieLayer->AttachViaNotify(layerIFace); + layerIFace = movieLayer; + } + + return layerIFace; +} + +plLayerInterface* IProcessLayerAnimation(plPassMtlBase* mtl, plLayerTex* layTex, plMaxNode* node, + const char *name, plLayerInterface* layerIFace) +{ + hsControlConverter& cc = hsControlConverter::Instance(); + + // + // Look for animations. These will get tacked onto the base pass layer + StdUVGen *uvGen = (StdUVGen*)layTex->GetTheUVGen(); + plLeafController* xfmCtl = cc.MakeMatrix44Controller(uvGen, node->GetName()); + + if( !xfmCtl ) + return layerIFace; + + if( mtl->GetUseGlobal() ) + { + plLayerSDLAnimation *SDLLayer = TRACKED_NEW plLayerSDLAnimation; + TSTR animName = TSTR(name) + TSTR("_anim_") + TSTR(mtl->GetGlobalVarName()); + hsgResMgr::ResMgr()->NewKey(animName, SDLLayer, node->GetLocation()); + + SDLLayer->SetVarName(mtl->GetGlobalVarName()); + SDLLayer->SetTransformCtl(xfmCtl); + SDLLayer->AttachViaNotify(layerIFace); + node->CheckSynchOptions(SDLLayer); + + return SDLLayer; + } + + // Don't need anymore, really was just for validation above + delete xfmCtl; + + // If no valid animation track is chosen, return entire animation. + int i, count = mtl->GetNumStealths(); + for( i = count - 1; i >= 0; i-- ) + { + plAnimStealthNode *stealth = mtl->GetStealth( i ); + + plLayerAnimation *noteAnim = TRACKED_NEW plLayerAnimation; + node->CheckSynchOptions(noteAnim); + + const char *segName = stealth->GetSegmentName(); + bool isDefault = ( segName == nil || strcmp( segName, ENTIRE_ANIMATION_NAME ) == 0 ) ? true : false; + + TSTR animName = TSTR( name ) + ( ( isDefault ) ? TSTR( "_LayerAnim_" ) : ( TSTR("_LayerAnim") + TSTR( segName ) ) ); + hsgResMgr::ResMgr()->NewKey( animName, noteAnim, node->GetLocation() ); + + StdUVGen *uvGen = (StdUVGen *)layTex->GetTheUVGen(); + plLeafController *segXfmCtl = cc.MakeMatrix44Controller( uvGen, node->GetName() ); + noteAnim->SetTransformCtl( segXfmCtl ); + + // ATC conversion stuff + stealth->StuffToTimeConvert( noteAnim->GetTimeConvert(), segXfmCtl->GetLength() ); + + // Set segment name if we're not the default + if( !isDefault ) + noteAnim->SetSegmentID( (char *)segName ); + + // And attach! + noteAnim->AttachViaNotify( layerIFace ); + + // So the next time will attach to this layer... + layerIFace = noteAnim; + } + + return layerIFace; + +} + +plLayerInterface* IProcessAnimation(plPassMtlBase *mtl, plMaxNode *node, const char *name, + plLayerInterface *layerIFace) +{ + hsControlConverter& cc = hsControlConverter::Instance(); + + hsScalar maxLength = 0; + // + // Look for animations. These will get tacked onto the base pass layer + Control *maxColCtl = mtl->GetPreshadeColorController(); + plController *colCtl = cc.MakeColorController(maxColCtl, node); + + Control *maxRunColCtl = nil; + plController *runColCtl = nil; + if( mtl->GetDiffuseColorLock() ) + maxRunColCtl = maxColCtl; + else + maxRunColCtl = mtl->GetRuntimeColorController(); + + runColCtl = cc.MakeColorController(maxRunColCtl, node); + + Control *maxAmbCtl = mtl->GetAmbColorController(); + plController *ambCtl = cc.MakeColorController(maxAmbCtl, node); + + Control *maxOpaCtl = mtl->GetOpacityController(); + plLeafController *opaCtl = cc.MakeScalarController(maxOpaCtl, node); + + Control *maxSpecCtl = mtl->GetSpecularColorController(); + plController *specCtl = cc.MakeColorController(maxSpecCtl, node); + + // If there are no animations, return + if (!colCtl && !ambCtl && !opaCtl && !runColCtl && !specCtl) + return layerIFace; + if( colCtl ) + maxLength = colCtl->GetLength(); + if( ambCtl && (ambCtl->GetLength() > maxLength) ) + maxLength = ambCtl->GetLength(); + if( opaCtl && (opaCtl->GetLength() > maxLength) ) + maxLength = opaCtl->GetLength(); + if( runColCtl && (runColCtl->GetLength() > maxLength) ) + maxLength = runColCtl->GetLength(); + if( specCtl && (specCtl->GetLength() > maxLength) ) + maxLength = specCtl->GetLength(); + + if( mtl->GetUseGlobal() ) + { + //if (!hsMaterialConverter::Instance().CheckValidityOfSDLVarAnim(mtl, mtl->GetGlobalVarName(), node)) + // return layerIFace; + + plLayerSDLAnimation *SDLLayer = TRACKED_NEW plLayerSDLAnimation; + TSTR animName = TSTR(name) + TSTR("_anim_") + TSTR(mtl->GetGlobalVarName()); + hsgResMgr::ResMgr()->NewKey(animName, SDLLayer, node->GetLocation()); + + SDLLayer->SetVarName(mtl->GetGlobalVarName()); + node->CheckSynchOptions(SDLLayer); + + if (colCtl) + SDLLayer->SetPreshadeColorCtl(colCtl); + if (ambCtl) + SDLLayer->SetAmbientColorCtl(ambCtl); + if (opaCtl) + SDLLayer->SetOpacityCtl(opaCtl); + if( runColCtl ) + SDLLayer->SetRuntimeColorCtl( runColCtl ); + if( specCtl ) + SDLLayer->SetSpecularColorCtl( specCtl ); + + SDLLayer->AttachViaNotify(layerIFace); + + return SDLLayer; + } + + // Delete these, since they're no longer needed + delete colCtl; + delete ambCtl; + delete opaCtl; + delete runColCtl; + delete specCtl; + + // Loop through the stealths, since each one represents a segment + int i, count = mtl->GetNumStealths(); + for( i = count - 1; i >= 0; i-- ) + { + plAnimStealthNode *stealth = mtl->GetStealth( i ); + + plLayerAnimation *noteAnim = TRACKED_NEW plLayerAnimation; + node->CheckSynchOptions(noteAnim); + + const char *segName = stealth->GetSegmentName(); + bool isDefault = ( segName == nil || strcmp( segName, ENTIRE_ANIMATION_NAME ) == 0 ) ? true : false; + + TSTR animName = TSTR( name ) + ( ( isDefault ) ? TSTR( "_anim" ) : ( TSTR("_anim_") + TSTR( segName ) ) ); + hsgResMgr::ResMgr()->NewKey( animName, noteAnim, node->GetLocation() ); + + plController *noteColCtl = cc.MakeColorController( maxColCtl, node ); + plController *noteAmbCtl = cc.MakeColorController( maxAmbCtl, node ); + plController *noteOpaCtl = cc.MakeScalarController( maxOpaCtl, node ); + plController *noteSpecCtl = cc.MakeColorController( maxSpecCtl, node ); + plController *noteRunColCtl = cc.MakeColorController( maxColCtl, node ); + + if( noteColCtl ) + noteAnim->SetPreshadeColorCtl( noteColCtl ); + if( noteAmbCtl ) + noteAnim->SetAmbientColorCtl( noteAmbCtl ); + if( noteOpaCtl ) + noteAnim->SetOpacityCtl( noteOpaCtl ); + if( noteRunColCtl ) + noteAnim->SetRuntimeColorCtl( noteRunColCtl ); + if( noteSpecCtl ) + noteAnim->SetSpecularColorCtl( noteSpecCtl ); + + // ATC conversion stuff + stealth->StuffToTimeConvert( noteAnim->GetTimeConvert(), maxLength ); + + // Set segment name if we're not the default + if( !isDefault ) + noteAnim->SetSegmentID( (char *)segName ); + + // And attach! + noteAnim->AttachViaNotify( layerIFace ); + + // So the next time will attach to this layer... + layerIFace = noteAnim; + } + + // Cleanup + return layerIFace; + +} + + +hsBool hsMaterialConverter::IHasSubMtl(Mtl* base, Mtl* sub) +{ + if( !(base && sub) ) + return false; + + if( base == sub ) + return true; + + int nSub = base->NumSubMtls(); + int i; + for( i = 0; i < nSub; i++ ) + { + if( IHasSubMtl(base->GetSubMtl(i), sub) ) + return true; + } + return false; +} + +int hsMaterialConverter::IFindSubIndex(plMaxNode* node, Mtl* mtl) +{ + Mtl* baseMtl = node->GetMtl(); + if( !IsMultiMat(baseMtl) ) + return 0; + + int i; + for( i = 0; i < mtl->NumSubMtls(); i++ ) + { + if( IHasSubMtl(mtl->GetSubMtl(i), mtl) ) + return i; + } + return -1; +} + +// +// Converters for different kinds of materials (Plasma, Environ, Standard) +// + +// Now handles both plPassMtl and plDecalMtl which derive from plPassMtlBase +hsBool hsMaterialConverter::IProcessPlasmaMaterial(Mtl *mtl, plMaxNode *node, hsGMaterial *mat, const char* name) +{ + hsGuardBegin("hsMaterialConverter::IProcessPlasmaMaterial"); + + plLocation nodeLoc= node->GetLocation(); + char* dbgNodeName = node->GetName(); + + int initNumLayers = mat->GetNumLayers(); + + if(!mtl) + { + fErrorMsg->Set(!mtl, "Material Loading Error", "No material in ProcessPlasmaMaterial!").Show(); + //hsAssert(mtl, "No material in ProcessPlasmaMaterial!"); + + fErrorMsg->Set(); + return false; + } + + int multiIndex = IFindSubIndex(node, mtl); + hsBool needAlphaHack = node->AlphaHackLayersNeeded(multiIndex) > 0; + + plPassMtlBase *passBase = (plPassMtlBase *)mtl; + // + // Color / Opacity + // + Color amb = passBase->GetAmbColor(); + Color dif = passBase->GetColor(); + Color runDif = passBase->GetRuntimeColor(); + if( passBase->GetDiffuseColorLock() ) + runDif = dif; + + float opac = float(passBase->GetOpacity()) / 100.0f; + + fErrorMsg->Set(opac < 0.0 || opac > 1.0, dbgNodeName, "Bad opacity on mat %s", name).CheckAndAsk(); + fErrorMsg->Set(dif.r < 0.0 || dif.r > 1.0, dbgNodeName, "Bad color (r) on mat %s", name).CheckAndAsk(); + fErrorMsg->Set(dif.g < 0.0 || dif.g > 1.0, dbgNodeName, "Bad color (g) on mat %s", name).CheckAndAsk(); + fErrorMsg->Set(dif.b < 0.0 || dif.b > 1.0, dbgNodeName, "Bad color (b) on mat %s", name).CheckAndAsk(); + fErrorMsg->Set(runDif.r < 0.0 || runDif.r > 1.0, dbgNodeName, "Bad runtime color (r) on mat %s", name).CheckAndAsk(); + fErrorMsg->Set(runDif.g < 0.0 || runDif.g > 1.0, dbgNodeName, "Bad runtime color (g) on mat %s", name).CheckAndAsk(); + fErrorMsg->Set(runDif.b < 0.0 || runDif.b > 1.0, dbgNodeName, "Bad runtime color (b) on mat %s", name).CheckAndAsk(); + +// Color col = dif - amb; + Color col = dif; + + if( passBase->GetDiffuseColorLock() ) + runDif = dif; + + // If we've got No Preshading explicitly set, then set the + // PreshadeColor to black (so anything in the vertex color + // will get ignored at runtime). + if( node->GetRunTimeLight() ) + col.Black(); + // + // Blend flags + // + UInt32 blendFlags = 0; + if( node->GetAlphaTestHigh() || passBase->GetAlphaTestHigh() ) + blendFlags |= hsGMatState::kBlendAlphaTestHigh; + + // Z Flag + if (passBase->GetZOnly()) + blendFlags |= hsGMatState::kBlendNoColor; + + // + // Shade flags + // + UInt32 shadeFlags = 0; + if (passBase->GetSoftShadow()) + shadeFlags |= hsGMatState::kShadeSoftShadow; + if (passBase->GetNoProj()) + shadeFlags |= hsGMatState::kShadeNoProjectors; + if (passBase->GetVertexShade()) + shadeFlags |= hsGMatState::kShadeVertexShade; + if (passBase->GetNoShade()) + shadeFlags |= hsGMatState::kShadeNoShade; + if (passBase->GetNoFog()) + shadeFlags |= hsGMatState::kShadeReallyNoFog; + if (passBase->GetWhite()) + shadeFlags |= hsGMatState::kShadeWhite; + /// Emissive flag, from basic parameters + if( passBase->GetEmissive() ) + { + shadeFlags |= hsGMatState::kShadeEmissive; + col.r = col.g = col.b = 0.0f; + runDif = col; + } + if( node->GetIsGUI() ) + { + // We don't want materials on any GUI objects to *ever* be fogged...4.25.2002 mcn + shadeFlags |= hsGMatState::kShadeReallyNoFog; + } + + // Specular flags + float shine = 0.0; + Color specColor = Color(0,0,0); + + if (passBase->GetUseSpec()) + { + shadeFlags |= hsGMatState::kShadeSpecular; + shine = passBase->GetShine(); + specColor = passBase->GetSpecularColor(); + } + + // + // Misc flags... + // + UInt32 miscFlags = 0; + if (passBase->GetBasicWire()) + miscFlags |= hsGMatState::kMiscWireFrame; + if (passBase->GetMeshOutlines()) + miscFlags |= hsGMatState::kMiscDrawMeshOutlines; + if( !node->GetDup2Sided() && passBase->GetTwoSided() ) + miscFlags |= hsGMatState::kMiscTwoSided; + + // + // Z flags + // + UInt32 zFlags = 0; + if (passBase->GetZClear()) + zFlags |= hsGMatState::kZClearZ; + if (passBase->GetZNoRead()) + zFlags |= hsGMatState::kZNoZRead; + if (passBase->GetZNoWrite()) + zFlags |= hsGMatState::kZNoZWrite; + if (passBase->GetZInc()) + zFlags |= hsGMatState::kZIncLayer; + + // Decal flag settings + if (IsDecalMat(mtl)) + { + zFlags |= hsGMatState::kZIncLayer | hsGMatState::kZNoZWrite; + mat->SetCompositeFlags(mat->GetCompositeFlags() | hsGMaterial::kCompDecal); + } + + + // We're not using specular highlights? + if (shadeFlags & hsGMatState::kShadeSpecularHighlight) + { + shadeFlags &= ~(hsGMatState::kShadeSpecular + | hsGMatState::kShadeSpecularHighlight + | hsGMatState::kShadeSpecularAlpha + | hsGMatState::kShadeSpecularColor); + } + + hsBool preserveUVOffset = PreserveUVOffset(mtl); + + // + // Process base layer + // + plLayer* baseLay = nil; + Texmap *baseTex = passBase->GetBaseLayer(); + plPlasmaMAXLayer *plasmaLayer; + if (baseTex && ( plasmaLayer = plPlasmaMAXLayer::GetPlasmaMAXLayer( baseTex ) ) != nil ) + { + plLayerInterface *layerIFace = plLayerConverter::Instance().ConvertTexmap( baseTex, node, 0, preserveUVOffset, false ); + + baseLay = plLayer::ConvertNoRef(layerIFace->BottomOfStack()); + + fErrorMsg->Set(!baseLay, node->GetName(), "Found no layer at end of IProcessPlasmaLayer()").Check(); + + // This sucks. If we're adding a base layer on top of layers whose emissive flags don't match, + // we have to set kMiscRestartPassHere on this layer + if( mat->GetNumLayers() > 0 ) + { + UInt32 lastShades = mat->GetLayer( mat->GetNumLayers() - 1 )->GetShadeFlags(); + + if( ( lastShades ^ shadeFlags ) & hsGMatState::kShadeEmissive ) + baseLay->SetMiscFlags( baseLay->GetMiscFlags() | hsGMatState::kMiscRestartPassHere ); + } + + baseLay->SetBlendFlags(baseLay->GetBlendFlags() | blendFlags); + baseLay->SetShadeFlags(baseLay->GetShadeFlags() | shadeFlags); + baseLay->SetZFlags(baseLay->GetZFlags() | zFlags); + baseLay->SetMiscFlags(baseLay->GetMiscFlags() | miscFlags); // 3.28.2001 mcn - Who didn't put this in? + + if (baseLay->GetShadeFlags() & hsGMatState::kShadeSpecular) + { + baseLay->SetSpecularColor( hsColorRGBA().Set( specColor.r, specColor.g, specColor.b, hsScalar1 ) ); + baseLay->SetSpecularPower(shine); + } + + baseLay->SetAmbientColor(hsColorRGBA().Set(amb.r, amb.g, amb.b, hsScalar1)); + baseLay->SetPreshadeColor(hsColorRGBA().Set(col.r, col.g, col.b, hsScalar1)); + baseLay->SetRuntimeColor(hsColorRGBA().Set(runDif.r, runDif.g, runDif.b, hsScalar1)); + baseLay->SetOpacity( opac ); // Don't scale the material color by this if we're add blending; do that later + + UInt32 blendType = 0; + switch (passBase->GetOutputBlend()) + { + case plPassMtlBase::kBlendAlpha: blendType |= hsGMatState::kBlendAlpha; break; + case plPassMtlBase::kBlendAdd: blendType |= hsGMatState::kBlendAdd; break; + } + baseLay->SetBlendFlags(baseLay->GetBlendFlags() | blendType); + if( (opac < 1.f) && (blendType & hsGMatState::kBlendAlpha) ) + baseLay->SetMiscFlags( baseLay->GetMiscFlags() | hsGMatState::kMiscRestartPassHere ); + + plLayerInterface *layerAnim = IProcessAnimation(passBase, node, layerIFace->GetKeyName(), layerIFace); + + layerAnim = IProcessLayerAnimation(passBase, (plLayerTex*)baseTex, node, layerIFace->GetKeyName(), layerAnim); + + layerAnim = IProcessLayerMovie(passBase, (plLayerTex*)baseTex, node, layerAnim); + + layerAnim->Eval(0,0,0); + + IAddLayerToMaterial(mat, layerAnim); + + } + + // If we don't have a base layer, there's not much left to talk about. + if( !baseLay ) + return true; + + if( baseLay && ( IsBumpLayer(baseTex) || IsBumpMtl( mtl ) ) ) + baseLay->SetMiscFlags(baseLay->GetMiscFlags() | hsGMatState::kMiscBumpLayer); + if( baseLay && node->GetWaterDecEnv() ) + baseLay->SetShadeFlags(baseLay->GetShadeFlags() | hsGMatState::kShadeEnvironMap); + + // If the top layer is on, and there is a valid base layer + if (passBase->GetTopLayerOn() && baseLay) + { + Texmap *texMap = passBase->GetTopLayer(); + plPlasmaMAXLayer *plasmaTopLayer; + if (texMap && ( plasmaTopLayer = plPlasmaMAXLayer::GetPlasmaMAXLayer( baseTex ) ) != nil ) + { + // Blend flags (do this first so we can pass it to IProcessPlasmaLayer()) + UInt32 blendIndex = passBase->GetLayerBlend(); + if( needAlphaHack + &&(passBase->GetOutputBlend() == plPassMtlBase::kBlendAlpha) + &&(blendIndex == plPassMtlBase::kBlendAdd || blendIndex == plPassMtlBase::kBlendMult) ) + { + blendIndex = plPassMtlBase::kBlendAlpha; + if (fErrorMsg->Set(!(fWarned & kWarnedAlphaAddCombo), node->GetName(), + "Material \"%s\" has Output Blending set to Alpha, but Layer Blending is not. " + "This combination requires RunTime Lighting or give up vertex alpha and opacity animation." + "Using a layer blend of alpha for now.", + passBase->GetName()).CheckAskOrCancel() ) + fWarned |= kWarnedAlphaAddCombo; + } + + UInt32 blendType = 0; + switch (blendIndex) + { + case plPassMtlBase::kBlendAlpha: + blendType |= hsGMatState::kBlendAlpha; + break; + case plPassMtlBase::kBlendAdd: + blendType |= hsGMatState::kBlendAdd; + break; + case plPassMtlBase::kBlendMult: + blendType |= hsGMatState::kBlendMult; + break; + } + + plLayerInterface *layerIFace = plLayerConverter::Instance().ConvertTexmap( texMap, node, blendType, preserveUVOffset, true ); + + if( layerIFace != nil ) + { + if( (baseLay->GetBlendFlags() | layerIFace->GetBlendFlags()) & (hsGMatState::kBlendNoTexColor | hsGMatState::kBlendNoTexAlpha) ) + baseLay->SetMiscFlags(baseLay->GetMiscFlags() | hsGMatState::kMiscBindNext); + + if( blendIndex == plPassMtlBase::kBlendMult ) + baseLay->SetMiscFlags(baseLay->GetMiscFlags() | hsGMatState::kMiscBindNext); + + plLayer* hLay = plLayer::ConvertNoRef(layerIFace->BottomOfStack()); + + fErrorMsg->Set(!hLay, node->GetName(), "Found no layer at end of IProcessPlasmaLayer()").Check(); + + hLay->SetBlendFlags(hLay->GetBlendFlags() | blendType | blendFlags ); + hLay->SetShadeFlags(hLay->GetShadeFlags() | shadeFlags); + // 5.29.2001 mcn - Upper layers are *supposed* to have this set on them +// hLay->SetZFlags( hLay->GetZFlags() | zFlags | hsGMatState::kZNoZWrite | hsGMatState::kZIncLayer ); + // 2/28 2002 - mf - upper layers don't need ZIncLayer (because we no longer clip them). + hLay->SetZFlags( hLay->GetZFlags() | zFlags | hsGMatState::kZNoZWrite ); + + if (hLay->GetShadeFlags() & hsGMatState::kShadeSpecular) + { + hLay->SetSpecularColor( hsColorRGBA().Set( specColor.r, specColor.g, specColor.b, hsScalar1 ) ); + hLay->SetSpecularPower(shine); + } + + hLay->SetAmbientColor(hsColorRGBA().Set(amb.r, amb.g, amb.b, hsScalar1)); + hLay->SetPreshadeColor(hsColorRGBA().Set(col.r, col.g, col.b, hsScalar1)); + hLay->SetRuntimeColor(hsColorRGBA().Set(runDif.r, runDif.g, runDif.b, hsScalar1)); + hLay->SetOpacity( opac ); + + if( IsBumpLayer(texMap) || IsBumpMtl( mtl ) ) + hLay->SetMiscFlags(hLay->GetMiscFlags() | hsGMatState::kMiscBumpLayer); + + hsBool canFunk = !needAlphaHack; + // Base layer fixups + // Yet another attempt to protect the artists from themselves. Setting the + // alpha combine to mult or add, when the output blend is opaque is at best meaningless + // and can be dangerous. Just ignore them. + if( baseLay->GetBlendFlags() & hsGMatState::kBlendAlpha ) + { + switch (passBase->GetOutputAlpha()) + { + case plPassMtlBase::kAlphaMultiply: + hLay->SetBlendFlags(hLay->GetBlendFlags() | hsGMatState::kBlendAlphaMult); + baseLay->SetMiscFlags(baseLay->GetMiscFlags() | hsGMatState::kMiscBindNext); + canFunk = false; + break; + case plPassMtlBase::kAlphaAdd: + hLay->SetBlendFlags(hLay->GetBlendFlags() | hsGMatState::kBlendAlphaAdd); + baseLay->SetMiscFlags(baseLay->GetMiscFlags() | hsGMatState::kMiscBindNext); + canFunk = false; + break; + } + } + + if( canFunk && IHasFunkyOpacity(node, baseTex) ) + { + IAppendFunkyLayer(node, baseTex, mat); + // warn if requested but can't? + } + + layerIFace = IProcessLayerAnimation(passBase, (plLayerTex*)texMap, node, layerIFace->GetKeyName(), layerIFace); + + layerIFace = IProcessLayerMovie(passBase, (plLayerTex*)baseTex, node, layerIFace); + + layerIFace->Eval(0,0,0); + + IAddLayerToMaterial(mat, layerIFace); + + if( canFunk && IHasFunkyOpacity(node, texMap) ) + { + IAppendFunkyLayer(node, texMap, mat); + // warn if requested but can't? + } + } + } + } + else + { + if( IHasFunkyOpacity(node, baseTex) ) + { + if( !needAlphaHack ) + { + IAppendFunkyLayer(node, baseTex, mat); + } + else + { + // warn? + } + } + } + + return true; + hsGuardEnd; +} + +void hsMaterialConverter::IAddLayerToMaterial(hsGMaterial *mat, plLayerInterface *layer) +{ + plMatRefMsg* msg = TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnCreate, -1, plMatRefMsg::kLayer); + hsgResMgr::ResMgr()->AddViaNotify(layer->GetKey(), msg, plRefFlags::kActiveRef); +} + +// +// Functions called by the converters up above... +// +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" + +//// IMustBeUniqueMaterial //////////////////////////////////////////////////// +// Fun stuff here. If one of the layers of the material is a dynamic EnvMap, +// and its anchor is set to nil (i.e. "self"), then the layer must be unique +// for each object its applied to. However, that means the material must +// *ALSO* be unique. So this function will tell us whether we have to make +// a unique material. + +hsBool hsMaterialConverter::IMustBeUniqueMaterial( Mtl *mtl ) +{ + hsGuardBegin( "hsMaterialConverter::IMustBeUniqueMaterial" ); + + if( !mtl ) + return false; + + const char *dbgName = mtl->GetName(); + + if( IsMultiMat( mtl ) || IsMultipassMat( mtl ) || IsCompositeMat( mtl ) ) + { + int iMtl; + for( iMtl = 0; iMtl < mtl->NumSubMtls(); iMtl++ ) + { + if( IMustBeUniqueMaterial( mtl->GetSubMtl( iMtl ) ) ) + return true; + } + return false; + } + else if( IsParticleMat( mtl ) ) + return false; + else if( mtl->ClassID() == PASS_MTL_CLASS_ID ) + { + // It is std material. does not have any submaterials + plPassMtlBase *passMtl = (plPassMtlBase *)mtl; + + if( IMustBeUniqueLayer( passMtl->GetBaseLayer() ) ) + return true; + + if( passMtl->GetTopLayerOn() && passMtl->GetTopLayer() && + IMustBeUniqueLayer( passMtl->GetTopLayer() ) ) + return true; + + return false; + } + + return false; + + hsGuardEnd; +} + +//// IMustBeUniqueLayer /////////////////////////////////////////////////////// +// Check for a single layer (see IMustBeUniqueMaterial()) + +hsBool hsMaterialConverter::IMustBeUniqueLayer( Texmap *layer ) +{ + plPlasmaMAXLayer *plasmaLayer = plPlasmaMAXLayer::GetPlasmaMAXLayer( layer ); + if( plasmaLayer != nil ) + return plasmaLayer->MustBeUnique(); + + return false; +} + +hsBool hsMaterialConverter::IUVGenHasDynamicScale(plMaxNode* node, StdUVGen *uvGen) +{ + hsGuardBegin("hsMaterialConverter::IUVGenHasDynamicScale"); + + Control *ctl = nil; + + hsControlConverter::Instance().GetControllerByName(uvGen, TSTR("U Tiling"), ctl); + if( ctl && (ctl->NumKeys() > 1) ) + return true; + hsControlConverter::Instance().GetControllerByName(uvGen, TSTR("V Tiling"), ctl); + if( ctl && (ctl->NumKeys() > 1) ) + return true; + + return false; + hsGuardEnd; +} + +void hsMaterialConverter::IScaleLayerOpacity(plLayer* hLay, hsScalar scale) +{ + hsGuardBegin("hsMaterialConverter::IScaleLayerOpacity"); + + if( scale < 1.f ) + { + if( !(hLay->GetBlendFlags() & ~(hsGMatState::kBlendAlpha | hsGMatState::kBlendAntiAlias)) ) + { + hLay->SetBlendFlags(hLay->GetBlendFlags() | hsGMatState::kBlendAlpha); + } + + hsScalar opac = hLay->GetOpacity(); + opac *= scale; + hLay->SetOpacity(scale); + } + hsGuardEnd; +} + +hsGMaterial *hsMaterialConverter::ICheckForProjectedTexture(plMaxNode *node) +{ + hsGuardBegin("hsMaterialConverter::ICheckForProjectedTexture"); + + char *nodeName = node->GetName(); + Object *obj = node->EvalWorldState(fConverterUtils.GetTime(fInterface)).obj; + LightObject *light = (LightObject*)obj->ConvertToType(fConverterUtils.GetTime(fInterface), + Class_ID(SPOT_LIGHT_CLASS_ID,0)); + if( !light ) + light = (LightObject*)obj->ConvertToType(fConverterUtils.GetTime(fInterface), + Class_ID(FSPOT_LIGHT_CLASS_ID,0)); + if( !light ) + light = (LightObject*)obj->ConvertToType(fConverterUtils.GetTime(fInterface), + Class_ID(OMNI_LIGHT_CLASS_ID,0)); + + if( !light ) + light = (LightObject*)obj->ConvertToType(fConverterUtils.GetTime(fInterface), + Class_ID(DIR_LIGHT_CLASS_ID,0)); + + if( !light ) + light = (LightObject*)obj->ConvertToType(fConverterUtils.GetTime(fInterface), + Class_ID(TDIR_LIGHT_CLASS_ID,0)); + + if( light && light->GetProjector() ) + { + Texmap *projMap; + projMap = light->GetProjMap(); + return IWrapTextureInMaterial(projMap, node); + } + + return nil; + hsGuardEnd; +} + +hsGMaterial *hsMaterialConverter::IWrapTextureInMaterial(Texmap *texMap, plMaxNode *node) +{ + hsGuardBegin("hsMaterialConverter::IWrapTextureInMaterial"); + plLocation nodeLoc = node->GetLocation(); + // + // Add material to list + // + int found=FALSE; + char *nodeName = node->GetName(); + CStr className; + texMap->GetClassName(className); + + // We want to keep it. Handle appropriately. + BitmapTex *bitmapTex = (BitmapTex *)texMap; + char txtFileName[256]; + strcpy(txtFileName, bitmapTex->GetMapName()); + +// hsRegistryKey* key = hsgResMgr::ResMgr()->FindKey(txtFileName, hsGMaterial::Index()); + plKey key = node->FindPageKey( hsGMaterial::Index(), txtFileName ); + + hsGMaterial *hMat = key ? hsGMaterial::ConvertNoRef(key->GetObjectPtr()) : nil; + if( hMat ) + { + CopyMaterialLODToTextures(hMat); + return hMat; + } + + hMat = TRACKED_NEW hsGMaterial; + + plLayer* hLay = TRACKED_NEW plLayer; + hLay->InitToDefault(); + + hsgResMgr::ResMgr()->NewKey(txtFileName, hLay,nodeLoc); + + if (texMap->ClassID() == hsMaxLayerClassID) + { +// IProcessPlasmaLayer(hLay, nil, texMap, node, 0); + } + else + { + // + // Create hsGBitmap texture file from texture. + // Add texture name to list if not already there. + // + + // fill in benign values for our material + hLay->SetAmbientColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + hLay->SetRuntimeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + + // If we've got No Preshading explicitly set, then set the + // PreshadeColor to black (so anything in the vertex color + // will get ignored at runtime). + if( node->GetRunTimeLight() ) + hLay->SetPreshadeColor(hsColorRGBA().Set(0.f, 0.f, 0.f, 1.f)); + else + hLay->SetPreshadeColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + hLay->SetOpacity(1.f); + + UInt32 autoStart = 0; + char *nodeName = node->GetName(); + char *texName = bitmapTex->GetName(); + // BEGIN OVERRIDE + if (ALPHA_NONE == bitmapTex->GetAlphaSource()) + { + // TEMP + // This should be a blend modifier, not a load flag - mf + // hLay->SetLoadFlags(hLay->GetLoadFlags() | hsGLayer::kLoadNoAlpha); + } + else if (!(hLay->GetBlendFlags() & (hsGMatState::kBlendMask & ~hsGMatState::kBlendAntiAlias))) + { + if (ALPHA_FILE == bitmapTex->GetAlphaSource()) + hLay->SetBlendFlags(hLay->GetBlendFlags() | hsGMatState::kBlendAlpha); + } +#if 0 + // Texture map info + if (texMap != NULL) + { + IInitAttrTexture(node, nil, hLay, texMap, nodeName); + } +#endif + } + + key = hsgResMgr::ResMgr()->NewKey(txtFileName, hMat,nodeLoc); + + IAddLayerToMaterial(hMat, hLay); + + CopyMaterialLODToTextures(hMat); + + return hMat; + hsGuardEnd; +} + +Mtl* hsMaterialConverter::GetBaseMtl(Mtl* mtl) +{ + hsGuardBegin("hsMaterialConverter::GetBaseMtl"); + + return (mtl); + hsGuardEnd; +} + +Mtl* hsMaterialConverter::GetBaseMtl(plMaxNode* node) +{ + return GetBaseMtl(node->GetMtl()); +} + +enum plFunkyNess +{ + kFunkyNone = 0x0, + kFunkyDistance = 0x1, + kFunkyNormal = 0x2, + kFunkyReflect = 0x4, + kFunkyUp = 0x8, + kFunkyMask = kFunkyDistance + | kFunkyNormal + | kFunkyReflect + | kFunkyUp, + kFunkyAdd +}; + +static float IClampToRange(float v, float lo, float hi) +{ + if( v < lo ) + v = lo; + else if( v > hi ) + v = hi; + + return v; +} + +UInt32 hsMaterialConverter::IGetOpacityRanges(plMaxNode* node, Texmap* texMap, hsScalar& tr0, hsScalar& op0, hsScalar& op1, hsScalar& tr1) +{ + if( node->HasFade() ) + { + Box3 fade = node->GetFade(); + tr0 = fade.Min()[0]; + op0 = fade.Min()[1]; + op1 = fade.Max()[1]; + tr1 = fade.Max()[0]; + + UInt32 funkyType = kFunkyDistance; + + if( (tr0 == op0) && (op1 == tr1) ) + { + funkyType = kFunkyNone; + } + // See if we're going opaque to transparent to opaque. Then we need the Additive LUT + else + if( (tr0 > op0) && (op1 > tr1) ) + { + funkyType |= kFunkyAdd; + } + return funkyType; + } + + + if( !(texMap && texMap->GetName() && (strlen(texMap->GetName()) > 3)) ) + return kFunkyNone; + + const char* field = texMap->GetName()+3; + float f0, f1, f2, f3; + int code = sscanf(field, "%g,%g,%g,%g", &f0, &f1, &f2, &f3); + if( !code || (code == EOF) ) + { + return kFunkyNone; + } + switch( code ) + { + case 1: + tr1 = f0; + tr0 = op0 = op1 = 0; + break; + case 2: + op1 = f0; + tr1 = f1; + tr0 = op0 = 0; + break; + case 3: + tr0 = 0; + op0 = f0; + op1 = f1; + tr1 = f2; + break; + case 4: + tr0 = f0; + op0 = f1; + op1 = f2; + tr1 = f3; + break; + + default: + hsAssert(false, "Whoa, am i confused!!!"); + return kFunkyNone; + } + + UInt32 funkyType = IGetFunkyType(texMap); + switch( funkyType & kFunkyMask ) + { + case kFunkyDistance: + tr0 = IClampToRange(tr0, 0, 1.e33f); + op0 = IClampToRange(op0, 0, 1.e33f); + op1 = IClampToRange(op1, 0, 1.e33f); + tr1 = IClampToRange(tr1, 0, 1.e33f); + break; + case kFunkyNormal: + case kFunkyUp: + tr0 = IClampToRange(tr0, 0, 180.f); + op0 = IClampToRange(op0, 0, 180.f); + op1 = IClampToRange(op1, 0, 180.f); + tr1 = IClampToRange(tr1, 0, 180.f); + break; + case kFunkyReflect: + tr0 = IClampToRange(tr0, 0, 180.f); + op0 = IClampToRange(op0, 0, 180.f); + op1 = IClampToRange(op1, 0, 180.f); + tr1 = IClampToRange(tr1, 0, 180.f); + break; + } + if( tr0 > tr1 ) + { + hsScalar t; + t = tr0; + tr0 = tr1; + tr1 = t; + + t = op0; + op0 = op1; + op1 = t; + } + // Check for degenerate ranges. + if( (tr0 == op0) && (op1 == tr1) ) + { + funkyType = kFunkyNone; + } + // See if we're going opaque to transparent to opaque. Then we need the Additive LUT + else + if( (tr0 > op0) && (op1 > tr1) ) + { + funkyType |= kFunkyAdd; + } + switch( funkyType & kFunkyMask ) + { + case kFunkyDistance: + break; + case kFunkyNormal: + case kFunkyUp: + tr0 = hsCosine(hsScalarDegToRad(tr0)); + op0 = hsCosine(hsScalarDegToRad(op0)); + op1 = hsCosine(hsScalarDegToRad(op1)); + tr1 = hsCosine(hsScalarDegToRad(tr1)); + break; + case kFunkyReflect: + tr0 = hsCosine(hsScalarDegToRad(tr0)); + op0 = hsCosine(hsScalarDegToRad(op0)); + op1 = hsCosine(hsScalarDegToRad(op1)); + tr1 = hsCosine(hsScalarDegToRad(tr1)); + break; + } + return funkyType; +} + +UInt32 hsMaterialConverter::IGetFunkyType(Texmap* texMap) +{ + if( texMap && texMap->GetName() && *texMap->GetName() ) + { + // Distance opacity + if( !_strnicmp(texMap->GetName(), "%%%", 3) ) + return kFunkyDistance; + // Angle opacity - normal + if( !_strnicmp(texMap->GetName(), "@@@", 3) ) + return kFunkyNormal; + // Angle opacity - reflection + if( !_strnicmp(texMap->GetName(), "$$$", 3) ) + return kFunkyReflect; + if( !_strnicmp(texMap->GetName(), "!!!", 3) ) + return kFunkyUp; + } + return kFunkyNone; +} + +hsBool hsMaterialConverter::IHasFunkyOpacity(plMaxNode* node, Texmap* texMap) +{ + float tr0, cp0, cp1, tr1; + return IGetOpacityRanges(node, texMap, tr0, cp0, cp1, tr1) != kFunkyNone; +} + +void hsMaterialConverter::IAppendFunkyLayer(plMaxNode* node, Texmap* texMap, hsGMaterial* mat) +{ + plLayer* prevLay = plLayer::ConvertNoRef(mat->GetLayer(mat->GetNumLayers()-1)->BottomOfStack()); + hsAssert(prevLay, "Lost our base layer"); + if( prevLay ) + prevLay->SetMiscFlags(prevLay->GetMiscFlags() | hsGMatState::kMiscBindNext); + + float tr0, op0, op1, tr1; + UInt32 funkyType = IGetOpacityRanges(node, texMap, tr0, op0, op1, tr1); + + if( funkyType == kFunkyNone ) + return; + + hsMatrix44 uvwXfm; + uvwXfm.Reset(); + uvwXfm.fMap[0][0] = uvwXfm.fMap[1][1] = uvwXfm.fMap[2][2] = 0; + uvwXfm.NotIdentity(); + + if( op0 != tr0 ) + { + uvwXfm.fMap[0][2] = -1.f / (tr0 - op0); + uvwXfm.fMap[0][3] = uvwXfm.fMap[0][2] * -tr0; + } + else + { + uvwXfm.fMap[0][3] = 1.f; + } + + if( op1 != tr1 ) + { + uvwXfm.fMap[1][2] = -1.f / (tr1 - op1); + uvwXfm.fMap[1][3] = uvwXfm.fMap[1][2] * -tr1; + } + else + { + uvwXfm.fMap[1][3] = 1.f; + } + + plBitmap* funkRamp = IGetFunkyRamp(node, funkyType); + + char name[512]; + sprintf(name, "%s_funkRamp", prevLay->GetKey()->GetName()); + + plLayer* layer = TRACKED_NEW plLayer; + layer->InitToDefault(); + hsgResMgr::ResMgr()->NewKey(name, layer, node->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(funkRamp->GetKey(), TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef); + + layer->SetAmbientColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + layer->SetPreshadeColor(hsColorRGBA().Set(0, 0, 0, 1.f)); + layer->SetRuntimeColor(hsColorRGBA().Set(0, 0, 0, 1.f)); + + layer->SetZFlags(hsGMatState::kZNoZWrite); + UInt32 blendFlags = hsGMatState::kBlendAlpha | hsGMatState::kBlendNoTexColor | hsGMatState::kBlendAlphaMult; + layer->SetBlendFlags(blendFlags); + layer->SetClampFlags(hsGMatState::kClampTexture); + + layer->SetTransform(uvwXfm); + + switch( funkyType & kFunkyMask ) + { + case kFunkyDistance: + layer->SetUVWSrc(plLayerInterface::kUVWPosition); + layer->SetMiscFlags(layer->GetMiscFlags() | hsGMatState::kMiscNoShadowAlpha); + break; + case kFunkyNormal: + layer->SetUVWSrc(plLayerInterface::kUVWNormal); + break; + case kFunkyUp: + layer->SetUVWSrc(plLayerInterface::kUVWNormal); + layer->SetMiscFlags(layer->GetMiscFlags() | hsGMatState::kMiscOrthoProjection); + break; + case kFunkyReflect: + layer->SetUVWSrc(plLayerInterface::kUVWReflect); + break; + } + + hsgResMgr::ResMgr()->AddViaNotify(layer->GetKey(), TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnCreate, + -1, plMatRefMsg::kLayer), plRefFlags::kActiveRef); + +} + +plBitmap* hsMaterialConverter::IGetFunkyRamp(plMaxNode* node, UInt32 funkyType) +{ + const char* funkName = funkyType & kFunkyAdd ? "FunkyRampAdd" : "FunkyRampMult"; + + const int kLUTWidth = 16; + const int kLUTHeight = 16; + + // NOTE: CreateBlankMipmap might return an old mipmap if it was already created, so in those + // cases we wouldn't really need to re-write the texture. However, there's no harm in doing so, + // and since we're close to Alpha, I don't want to shake up the code any more than absolutely + // necessary. -mcn + plMipmap *texture = plBitmapCreator::Instance().CreateBlankMipmap( kLUTWidth, kLUTHeight, plMipmap::kARGB32Config, 1, funkName, node->GetLocation() ); + + UInt32* pix = (UInt32*)texture->GetImage(); + + if( funkyType & kFunkyAdd ) + { + int i; + for( i = 0; i < kLUTHeight; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + float y = float(i) / (kLUTHeight-1); + if( x < y ) + x = y; + *pix++ = MakeUInt32Color(1.f, 1.f, 1.f, x); + } + } + } + else + { + int i; + for( i = 0; i < kLUTHeight; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + float y = float(i) / (kLUTHeight-1); + *pix++ = MakeUInt32Color(1.f, 1.f, 1.f, x*y); + } + } + } + + return texture; +} + +void hsMaterialConverter::IAppendWetLayer(plMaxNode* node, hsGMaterial* mat) +{ + // Find the "wet" environment map + int i; + for( i = 0; i < 24; i++ ) + { + MtlBase* mtl = fInterface->GetMtlSlot(i); + if( mtl && (mtl->GetName() == TSTR("Wet(*)")) ) + { + if( mtl->SuperClassID() != MATERIAL_CLASS_ID ) + continue; + + hsTArray matList; + if( !GetMaterialArray((Mtl*)mtl, node, matList, 0) ) + return; // oh well, thanks for playing... + + // Okay, got one (at least one, hopefully just one, but this is trash code anyway, right? + plLayerInterface* envLay = matList[0]->GetLayer(0); + + // Append it to the material + hsgResMgr::ResMgr()->AddViaNotify(envLay->GetKey(), + new plMatRefMsg(mat->GetKey(), plRefMsg::kOnCreate, -1, plMatRefMsg::kLayer), plRefFlags::kActiveRef); + + break; + } + } + // Badly set up here + if( i == 24 ) + return; + + // Now throw on the FunkyUp map. Wait here while I pull some parameters out of my + + plLayer* prevLay = plLayer::ConvertNoRef(mat->GetLayer(mat->GetNumLayers()-1)->BottomOfStack()); + hsAssert(prevLay, "Lost our base layer"); + if( prevLay ) + prevLay->SetMiscFlags(prevLay->GetMiscFlags() | hsGMatState::kMiscBindNext | hsGMatState::kMiscRestartPassHere); + + hsMatrix44 uvwXfm; + uvwXfm.Reset(); + uvwXfm.fMap[0][0] = uvwXfm.fMap[1][1] = uvwXfm.fMap[2][2] = 0; + uvwXfm.NotIdentity(); + + uvwXfm.fMap[0][3] = 1.f; + + float op = 0.8f; + float tr = 0.5f; + uvwXfm.fMap[1][2] = -1.f / (tr - op); + uvwXfm.fMap[1][3] = uvwXfm.fMap[1][2] * -tr; + + char name[512]; + sprintf(name, "%s_funkRamp", prevLay->GetKey()->GetName()); + + plLayer* layer = nil; + plKey key = node->FindPageKey( plLayer::Index(), name ); + if( key ) + layer = plLayer::ConvertNoRef(key->GetObjectPtr()); + if( !layer ) + { + layer = TRACKED_NEW plLayer; + layer->InitToDefault(); + + hsgResMgr::ResMgr()->NewKey(name, layer, node->GetLocation()); + + plBitmap* funkRamp = IGetFunkyRamp(node, kFunkyUp); + hsgResMgr::ResMgr()->AddViaNotify(funkRamp->GetKey(), TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef); + } + + layer->SetAmbientColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + layer->SetPreshadeColor(hsColorRGBA().Set(0, 0, 0, 1.f)); + layer->SetRuntimeColor(hsColorRGBA().Set(0, 0, 0, 1.f)); + + layer->SetZFlags(hsGMatState::kZNoZWrite); + UInt32 blendFlags = hsGMatState::kBlendAlpha | hsGMatState::kBlendNoTexColor | hsGMatState::kBlendAlphaMult; + layer->SetBlendFlags(blendFlags); + layer->SetClampFlags(hsGMatState::kClampTexture); + + layer->SetTransform(uvwXfm); + + layer->SetUVWSrc(plLayerInterface::kUVWNormal); + layer->SetMiscFlags(layer->GetMiscFlags() | hsGMatState::kMiscOrthoProjection); + + hsgResMgr::ResMgr()->AddViaNotify(layer->GetKey(), TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnCreate, + -1, plMatRefMsg::kLayer), plRefFlags::kActiveRef); + +} + +hsBool hsMaterialConverter::HasVisDists(plMaxNode* node, int iSubMtl, hsScalar& minDist, hsScalar& maxDist) +{ + const char* dbgNodeName = node->GetName(); + const char* dbgMatName = node->GetMtl() ? node->GetMtl()->GetName() : "Dunno"; + + if( node->HasFade() ) + { + const hsScalar kMaxMaxDist = 1.e10f; + Box3 fade = node->GetFade(); + minDist = maxDist = 0; + if( fade.Min()[2] < 0 ) + { + minDist = fade.Min()[0]; + maxDist = kMaxMaxDist; + } + + if( fade.Max()[2] > 0 ) + maxDist = fade.Max()[0]; + + return maxDist > minDist; + } + + Mtl* mtl = node->GetMtl(); + if( IsMultiMat(mtl) ) + { + if( iSubMtl >= mtl->NumSubMtls() ) + iSubMtl = 0; + mtl = mtl->GetSubMtl(iSubMtl); + } + + return HasVisDists(node, mtl, minDist, maxDist); +} + +hsBool hsMaterialConverter::HasVisDists(plMaxNode* node, Mtl* mtl, hsScalar& minDist, hsScalar& maxDist) +{ + const char* dbgNodeName = node->GetName(); + const char* dbgMatName = node->GetMtl() ? node->GetMtl()->GetName() : "Dunno"; + + if( node->HasFade() ) + { + Box3 fade = node->GetFade(); + if( fade.Min()[2] < 0 ) + minDist = fade.Min()[0]; + else + minDist = 0; + + if( fade.Max()[2] > 0 ) + maxDist = fade.Max()[0]; + else + maxDist = 0; + return maxDist > minDist; + } + + minDist = -1.f; + maxDist = 1.e33f; + + if( IsHsMaxMat(mtl) ) + { + hsBool baseFunky = false; + hsBool topFunky = true; + plPassMtl* passMtl = (plPassMtl*)mtl; + hsScalar tr0, op0, op1, tr1; + UInt32 funkyType = IGetOpacityRanges(node, mtl->GetSubTexmap(0), tr0, op0, op1, tr1); + + if( kFunkyDistance == (funkyType & kFunkyMask) ) + { + if( tr0 < op0 ) + { + minDist = tr0; + baseFunky = true; + } + if( tr1 > op1 ) + { + maxDist = tr1; + baseFunky = true; + } + } + + if( passMtl->GetTopLayerOn() ) + { + topFunky = false; + funkyType = IGetOpacityRanges(node, mtl->GetSubTexmap(1), tr0, op0, op1, tr1); + if( kFunkyDistance == (funkyType & kFunkyMask) ) + { + if( tr0 < op0 ) + { + if( minDist > tr0 ) + minDist = tr0; + topFunky = true; + } + if( tr1 > op1 ) + { + if( maxDist < tr1 ) + maxDist = tr1; + topFunky = true; + } + } + } + return baseFunky && topFunky; + } + + if( IsMultipassMat(mtl) ) + { + minDist = 1.e33f; + maxDist = -1.f; + int i; + for( i = 0; i < mtl->NumSubMtls(); i++ ) + { + hsScalar minD, maxD; + if( !HasVisDists(node, mtl->GetSubMtl(i), minD, maxD) ) + return false; + + if( minDist > minD ) + minDist = minD; + if( maxDist < maxD ) + maxDist = maxD; + } + return true; + } + + return false; +} + + +hsBool hsMaterialConverter::IsBumpLayer(Texmap* texMap) +{ + if( texMap + && (texMap->ClassID() == LAYER_TEX_CLASS_ID) + && !strncmp(texMap->GetName(), kSecretBumpSign, strlen(kSecretBumpSign)) ) + { + return true; + } + return false; +} + +hsBool hsMaterialConverter::IsBumpMtl(Mtl* mtl) +{ + if( mtl == nil ) + return false; // Well, gee, I guess it can't be a bumpmap then... + + const char* dbgMtlName = mtl->GetName(); + + if( mtl->ClassID() == BUMP_MTL_CLASS_ID ) + return true; + + return false; +} + +hsBool hsMaterialConverter::HasBumpLayer(plMaxNode* node, Mtl* mtl) +{ + if( !mtl ) + return false; + + // Multi-sub or a multi-pass, recurse on down + if( IsMultiMat(mtl) || IsMultipassMat(mtl) ) + { + int i; + for( i = 0; i < mtl->NumSubMtls(); i++ ) + { + if( HasBumpLayer(node, mtl->GetSubMtl(i)) ) + return true; + } + return false; + } + + // Composite, screw it, just say no! + if( IsCompositeMat(mtl) ) + return false; + + // Particle? Give me a break. + if( IsParticleMat(mtl) ) + return false; + +#ifdef BUMP_EXCLUDE_DECAL + // Decal? Maybe someday, but not today. + if( IsDecalMat(mtl) ) + return false; +#endif // BUMP_EXCLUDE_DECAL + + // A closer look at the material. + if( IsBumpMtl(mtl) ) + return true; + + if( IsHsMaxMat(mtl) || IsDecalMat(mtl) ) + { + plPassMtlBase *passMtl = (plPassMtlBase *)mtl; +#ifdef BUMP_EXCLUDE_MULTILAYER + if( passMtl->GetTopLayerOn() ) + return false; +#else // BUMP_EXCLUDE_MULTILAYER + if( passMtl->GetTopLayerOn() && IsBumpLayer(mtl->GetSubTexmap(1)) ) + return true; +#endif // BUMP_EXCLUDE_MULTILAYER + + if( IsBumpLayer(mtl->GetSubTexmap(0)) ) + return true; + } + return false; +} + +BitmapTex* hsMaterialConverter::GetBumpLayer(plMaxNode* node, Mtl* mtl) +{ + hsAssert(!IsMultiMat(mtl), "Material passed in here should be per face"); + + if( IsMultipassMat(mtl) ) + { + int i; + for( i = 0; i < mtl->NumSubMtls(); i++ ) + { + BitmapTex* retVal = GetBumpLayer(node, mtl->GetSubMtl(i)); + if( retVal ) + return retVal; + } + return nil; + } + if( IsBumpMtl(mtl) ) + { + Texmap* texMap = mtl->GetSubTexmap(0); + + if( texMap && (texMap->ClassID() == LAYER_TEX_CLASS_ID) ) + { + return (BitmapTex*)texMap; + } + return nil; + } + if( HasBumpLayer(node, mtl) ) + { + Texmap* texMap = nil; + +#ifndef BUMP_EXCLUDE_MULTILAYER + // Safe cast, because only PassMtl (and derivatives) are BumpMtls. + plPassMtl* passMtl = (plPassMtl*)mtl; + + if( passMtl->GetTopLayerOn() && IsBumpLayer(mtl->GetSubTexmap(1)) ) + texMap = mtl->GetSubTexmap(1); +#endif // BUMP_EXCLUDE_MULTILAYER + + if( !texMap ) + texMap = mtl->GetSubTexmap(0); + + if( texMap && (texMap->ClassID() == LAYER_TEX_CLASS_ID) ) + { + return (BitmapTex*)texMap; + } + } + return nil; +} + +plMipmap *hsMaterialConverter::IGetBumpLutTexture(plMaxNode *node) +{ + const char* texName = "BumpLutTexture"; + +//#define FUNKYBUMP +#ifndef FUNKYBUMP + const int kLUTWidth = 16; + const int kLUTHeight = 16; + + // NOTE: CreateBlankMipmap might return an old mipmap if it was already created, so in those + // cases we wouldn't really need to re-write the texture. However, there's no harm in doing so, + // and since we're close to Alpha, I don't want to shake up the code any more than absolutely + // necessary. -mcn + plMipmap *texture = plBitmapCreator::Instance().CreateBlankMipmap( kLUTWidth, kLUTHeight, plMipmap::kARGB32Config, 1, texName, node->GetLocation() ); + + // THIS IS UNTESTED FOR ANY HEIGHT OTHER THAN 16. BEWARE THE DREAD OFF-BY-ONE!!! + int delH = (kLUTHeight-1) / 5; + int startH = delH / 2 + 1; + int doneH = 0; + + // set the color data + UInt32* pix = (UInt32*)texture->GetImage(); + int i; + + // Red ramps, one with G,B = 0,0, one with G,B = 127,127 + for( i = 0; i < startH; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + *pix++ = MakeUInt32Color(x, 0, 0, 1.f); + } + } + doneH = i; + for( i = i; i < doneH + delH; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + *pix++ = MakeUInt32Color(x, 0.5f, 0.5f, 1.f); + } + } + doneH = i; + + // Green ramps, one with R,B = 0,0, one with R,B = 127,127 + for( i = i; i < doneH + delH; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + *pix++ = MakeUInt32Color(0, x, 0, 1.f); + } + } + doneH = i; + for( i = i; i < doneH + delH; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + *pix++ = MakeUInt32Color(0.5f, x, 0.5f, 1.f); + } + } + doneH = i; + + // Blue ramps, one with R,G = 0,0, one with R,G = 127,127 + for( i = i; i < doneH + delH; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + *pix++ = MakeUInt32Color(0, 0, x, 1.f); + } + } + for( i = i; i < kLUTHeight; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); +// *pix++ = MakeUInt32Color(0.25f, 0.25f, x, 1.f); + *pix++ = MakeUInt32Color(0.5f, 0.5f, x, 1.f); + } + } +#else // FUNKYBUMP + const int kLUTWidth = 16; + const int kLUTHeight = 16; + + // NOTE: CreateBlankMipmap might return an old mipmap if it was already created, so in those + // cases we wouldn't really need to re-write the texture. However, there's no harm in doing so, + // and since we're close to Alpha, I don't want to shake up the code any more than absolutely + // necessary. -mcn + plMipmap *texture = plBitmapCreator::CreateBlankMipmap( kLUTWidth, kLUTHeight, plMipmap::kARGB32Config, 1, texName, node->GetLocation() ); + + // THIS IS UNTESTED FOR ANY HEIGHT OTHER THAN 16. BEWARE THE DREAD OFF-BY-ONE!!! + int delH = (kLUTHeight-1) / 5; + int startH = delH / 2 + 1; + int doneH = 0; + + // set the color data + UInt32* pix = (UInt32*)texture->GetImage(); + int i; + + const float kWScale = 1.f; + // Red ramps, one with G,B = 0,0, one with G,B = 127,127 + for( i = 0; i < startH; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + *pix++ = MakeUInt32Color(x, 0, 0, 1.f); + } + } + doneH = i; + for( i = i; i < doneH + delH; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + if( x > 0.5f ) + *pix++ = MakeUInt32Color(x, 0.5f, (x-0.5f) * kWScale + 0.5f, 1.f); + else + *pix++ = MakeUInt32Color(x, 0.5f, (1.f - x - 0.5f) * kWScale + 0.5f, 1.f); + } + } + doneH = i; + + // Green ramps, one with R,B = 0,0, one with R,B = 127,127 + for( i = i; i < doneH + delH; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + *pix++ = MakeUInt32Color(0, x, 0, 1.f); + } + } + doneH = i; + for( i = i; i < doneH + delH; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + if( x > 0.5f ) + *pix++ = MakeUInt32Color(0.5f, x, (x-0.5f) * kWScale + 0.5f, 1.f); + else + *pix++ = MakeUInt32Color(0.5f, x, (1.f - x - 0.5f) * kWScale + 0.5f, 1.f); + } + } + doneH = i; + + // Blue ramps, one with R,G = 0,0, one with R,G = 127,127 + for( i = i; i < doneH + delH; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + *pix++ = MakeUInt32Color(0, 0, x, 1.f); + } + } + for( i = i; i < kLUTHeight; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + *pix++ = MakeUInt32Color(0.25f, 0.25f, x, 1.f); + } + } +#endif // FUNKYBUMP + + return texture; +} + +plLayer* hsMaterialConverter::IMakeBumpLayer(plMaxNode* node, const char* nameBase, hsGMaterial* mat, UInt32 miscFlag) +{ + char name[256]; + switch( miscFlag & hsGMatState::kMiscBumpChans ) + { + case hsGMatState::kMiscBumpDu: + sprintf(name, "%s_DU_BumpLut", nameBase); + break; + case hsGMatState::kMiscBumpDv: + sprintf(name, "%s_DV_BumpLut", nameBase); + break; + case hsGMatState::kMiscBumpDw: + sprintf(name, "%s_DW_BumpLut", nameBase); + break; + default: + hsAssert(false, "Bogus flag input to MakeBumpLayer"); + return nil; + } + + plMipmap* bumpLutTexture = IGetBumpLutTexture(node); + + plLayer* layer = TRACKED_NEW plLayer; + layer->InitToDefault(); + hsgResMgr::ResMgr()->NewKey(name, layer, node->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(bumpLutTexture->GetKey(), TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef); + + layer->SetAmbientColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + layer->SetPreshadeColor(hsColorRGBA().Set(0, 0, 0, 1.f)); + layer->SetRuntimeColor(hsColorRGBA().Set(0, 0, 0, 1.f)); + + layer->SetZFlags(hsGMatState::kZNoZWrite); + UInt32 blendFlags = miscFlag & hsGMatState::kMiscBumpDu ? hsGMatState::kBlendMADD : hsGMatState::kBlendAdd; + layer->SetBlendFlags(blendFlags); + layer->SetClampFlags(hsGMatState::kClampTexture); + layer->SetMiscFlags(miscFlag); + layer->SetMiscFlags(layer->GetMiscFlags() | hsGMatState::kMiscBindNext); + + if( miscFlag & hsGMatState::kMiscBumpDu ) + layer->SetMiscFlags(layer->GetMiscFlags() | hsGMatState::kMiscRestartPassHere); + + int i; + int uvChan = -1; + + // Find our UVW channel. If there's another layer wanting to use the same kind of + // channel, just grab that. Otherwise we need to reserve one ourselves. + for( i = 0; i < mat->GetNumLayers(); i++ ) + { + if( (mat->GetLayer(i)->GetMiscFlags() & hsGMatState::kMiscBumpChans) == (miscFlag & hsGMatState::kMiscBumpChans) ) + uvChan = mat->GetLayer(i)->GetUVWSrc(); + } + + if( uvChan < 0 ) + { + uvChan = 0; + for( i = 0; i < mat->GetNumLayers(); i++ ) + { + if( uvChan <= int(mat->GetLayer(i)->GetUVWSrc() & plLayerInterface::kUVWIdxMask) ) + uvChan = int(mat->GetLayer(i)->GetUVWSrc() & plLayerInterface::kUVWIdxMask) + 1; + } + // Lightmap layers haven't been created and inserted yet, but they have reserved their + // UVW channel. Just don't take it. + if( node->IsLegalDecal() ) + node = (plMaxNode*)node->GetParentNode(); + if( node->GetLightMapComponent() ) + { + if( uvChan == node->GetLightMapComponent()->GetUVWSrc() ) + uvChan++; + } + if( miscFlag & hsGMatState::kMiscBumpDv ) + uvChan++; + if( miscFlag & hsGMatState::kMiscBumpDw ) + uvChan |= plLayerInterface::kUVWNormal; + } + + layer->SetUVWSrc(uvChan); + + return layer; +} + +void hsMaterialConverter::IInsertBumpLayers(plMaxNode* node, hsGMaterial* mat, int bumpLayerIdx) +{ + // First let's monkey with the incoming layer, just to confuse things. + // This doesn't actually do anything useful, it's just to spite anyone trying to read this code. + hsBool isAdd = 0 != (mat->GetLayer(bumpLayerIdx)->GetBlendFlags() & hsGMatState::kBlendAdd); + plLayer* bumpLay = plLayer::ConvertNoRef(mat->GetLayer(bumpLayerIdx)->BottomOfStack()); + if( bumpLay ) + bumpLay->SetBlendFlags( + (bumpLay->GetBlendFlags() & ~hsGMatState::kBlendMask) + | hsGMatState::kBlendDot3); + + const char* name = mat->GetLayer(bumpLayerIdx)->GetKey()->GetName(); + + plLayer* layerDu = IMakeBumpLayer(node, name, mat, hsGMatState::kMiscBumpDu); + plLayer* layerDv = IMakeBumpLayer(node, name, mat, hsGMatState::kMiscBumpDv); + plLayer* layerDw = IMakeBumpLayer(node, name, mat, hsGMatState::kMiscBumpDw); + + if( isAdd ) + layerDu->SetBlendFlags((layerDu->GetBlendFlags() & ~hsGMatState::kBlendMask) | hsGMatState::kBlendAdd); + + // Insert it in the right spot. + hsgResMgr::ResMgr()->AddViaNotify(layerDv->GetKey(), TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnCreate, + bumpLayerIdx, plMatRefMsg::kLayer | plMatRefMsg::kInsert), plRefFlags::kActiveRef); + hsgResMgr::ResMgr()->AddViaNotify(layerDw->GetKey(), TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnCreate, + bumpLayerIdx, plMatRefMsg::kLayer | plMatRefMsg::kInsert), plRefFlags::kActiveRef); + hsgResMgr::ResMgr()->AddViaNotify(layerDu->GetKey(), TRACKED_NEW plMatRefMsg(mat->GetKey(), plRefMsg::kOnCreate, + bumpLayerIdx, plMatRefMsg::kLayer | plMatRefMsg::kInsert), plRefFlags::kActiveRef); +} + +void hsMaterialConverter::IInsertBumpLayers(plMaxNode* node, hsGMaterial* mat) +{ + int bumpLayerIdx = -1; + + // Look for a bump layer. If there aren't any, there's nothing to do, just return. + // Currently only look for the first (and hopefully only) bump layer. + // BUMPTODO - Either correctly handle multiple bump layers (stupid) or detect and + // slap the production wrist for trying it. + // Okay, decided there actually are times it kind of makes sense to have multiple bump + // layers (hint: what's wet and wavy). + int i; + for( i = 0; i < mat->GetNumLayers(); i++ ) + { + if( mat->GetLayer(i)->GetMiscFlags() & hsGMatState::kMiscBumpLayer ) + { + IInsertBumpLayers(node, mat, i); + // Just inserted 3 layers before this one, adjust "i" to still be + // pointed at this layer. + i += 3; + } + } + +} + + +// +// +// +Texmap *hsMaterialConverter::GetUVChannelBase(plMaxNode *node, Mtl* mtl, int which) +{ + hsGuardBegin("hsMaterialConverter::GetUVChannelBase"); + + int i; + + // Forcing no flatten to support multiple uv channels + return nil; + + // Forcing no flatten until flattening with MultiMat resolved. + if( !GetBaseMtl(node->GetMtl())) // || MatWrite::IsMultiMat(node->GetMtl()) ) + return nil; + + if ( ForceNoUvsFlatten(node) ) + return nil; + + if ( !node ) + return nil; + + if ( !mtl ) + return nil; + + for (i = 0; i < mtl->NumSubTexmaps(); i++) { + if (!((StdMat *)mtl)->MapEnabled(i)) + continue; + + Texmap *texMap = mtl->GetSubTexmap(i); + + if (!texMap) + continue; + + if ( !(texMap->Requirements(-1) & MTLREQ_UV) && + !(texMap->Requirements(-1) & MTLREQ_UV2) ) + continue; + + if (which==0) { + if (texMap->GetUVWSource() != UVWSRC_EXPLICIT) + continue; + } else { + if (texMap->GetUVWSource() != UVWSRC_EXPLICIT2) + continue; + } + + if (ITextureTransformIsAnimated(texMap)) + continue; + + return (texMap); + } + + return (0); + hsGuardEnd; +} + +hsBool hsMaterialConverter::ClearDoneMaterials(plMaxNode* node) +{ + hsGuardBegin("hsMaterialConverter::ClearDoneMaterials"); + + Mtl *mtl = GetBaseMtl(node); + int isMultiMat = IsMultiMat(mtl); + + if (isMultiMat) + { + hsBool retVal = false; + Int32 i; + for (i = 0; i < mtl->NumSubMtls(); i++) + { + retVal = retVal || IClearDoneMaterial(mtl->GetSubMtl(i), node); + } + + return (retVal); + } + else + { + return (IClearDoneMaterial(mtl, node)); + } + + hsGuardEnd; +} + +hsBool hsMaterialConverter::IClearDoneMaterial(Mtl* mtl, plMaxNode* node) +{ + hsGuardBegin("hsMaterialConverter::IClearDoneMaterial"); + + hsBool doneSomething = false; + if (fLastMaterial.fMaxMaterial == mtl) + { + fLastMaterial.fHsMaterial = nil; + fLastMaterial.fMaxMaterial = nil; + fLastMaterial.fMaxMaterial = nil; + fLastMaterial.fSubMultiMat = false; + fLastMaterial.fOwnedCopy = false; + } + + Int32 i; + for (i = fDoneMaterials.Count() - 1; i >= 0; --i) + { + if (fDoneMaterials[i].fMaxMaterial == mtl) + { + fDoneMaterials[i].fHsMaterial->GetKey()->UnRefObject(); + fDoneMaterials.Remove(i); + doneSomething = true; + } + } + + return (doneSomething); + hsGuardEnd; +} + +#define VIEW_UP 0 +#define VIEW_DN 1 +#define VIEW_LF 2 +#define VIEW_RT 3 +#define VIEW_FR 4 +#define VIEW_BK 5 + + +static BMM_Color_64 green64 = {0,(1<<16)-1,0,(1<<16)-1}; + +BMM_Color_64 hsMaterialConverter::ICubeSample(Bitmap *bitmap[6], double phi, double theta) +{ + hsGuardBegin("hsMaterialConverter::ICubeSample"); + + theta = fmod(theta, (double)TWOPI); + if( theta < 0 )theta += TWOPI; + if( phi < 0 )phi = 0; + else if( phi > PI )phi = PI; + + Bitmap *map = nil; + + double sinPhi = sin(phi); + double cosPhi = cos(phi); + double sinThe = sin(theta); + double cosThe = cos(theta); + const double sqrt2 = sqrt(2.0); + const double oo_sqrt2 = 1.0 / sqrt2; + const double oo_sqrt3 = 1.0 / sqrt(3.0); + double sinPhiSinThe = sinPhi * sinThe; + double sinPhiCosThe = sinPhi * cosThe; + + double x, y, z; + double xMap, yMap; + + x = sinPhiSinThe; + y = sinPhiCosThe; + z = cosPhi; + if( (z*z > x*x)&&(z*z > y*y) ) + { + if( z > 0 ) + { + map = bitmap[VIEW_UP]; + xMap = -x / z; + yMap = -y / z; + } + else + { + map = bitmap[VIEW_DN]; + xMap = x / z; + yMap = -y / z; + } + } + else + { + if( (theta <= (hsScalarPI / 2.0 - hsScalarPI/4.0)) + ||(theta >= (hsScalarPI * 2.0 - hsScalarPI/4.0)) ) + { + map = bitmap[VIEW_FR]; + xMap = x / y; + yMap = -z / y; + } + else + if( theta <= (hsScalarPI - hsScalarPI/4.0) ) + { + map = bitmap[VIEW_LF]; + xMap = -y / x; + yMap = -z / x; + } + else + if( theta <= (hsScalarPI * 3.0/2.0 - hsScalarPI/4.0) ) + { + map = bitmap[VIEW_BK]; + xMap = x / y; + yMap = z / y; + } + else + { + map = bitmap[VIEW_RT]; + xMap = -y / x; + yMap = z / x; + } + } + xMap += 1.0; + yMap += 1.0; + xMap *= 0.5; + yMap *= 0.5; + int iMap, jMap; + iMap = (int)(xMap * (map->Width()-1)); + jMap = (int)(yMap * (map->Height()-1)); + + fErrorMsg->Set(!map, "CubeSample", "Bad fallthrough in spherefromcube").Check(); + BMM_Color_64 c; + map->GetLinearPixels(iMap,jMap,1,&c); + return c; + + hsGuardEnd; +} + +void hsMaterialConverter::IBuildSphereMap(Bitmap *bitmap[6], Bitmap *bm) +{ + hsGuardBegin("hsMaterialConverter::IBuildSphereMap"); + + int i, j; + double delPhi = PI / bm->Height(); + double delThe = TWOPI / bm->Width(); + PixelBuf l64(bm->Width()); + BMM_Color_64 *pb=l64.Ptr(); + for( j = 0; j < bm->Height(); j++ ) + { + for( i = 0; i < bm->Width(); i++ ) + { + double phi, theta; // phi is up/down + + phi = (0.5 + j) * delPhi; + theta = PI - (0.5 + i) * delThe; + + pb[i] = ICubeSample(bitmap, phi, theta); + } + bm->PutPixels(0,j, bm->Width(), pb); + } + + hsGuardEnd; +} + +hsBool hsMaterialConverter::ITextureTransformIsAnimated(Texmap *texmap) +{ + hsGuardBegin("hsMaterialConverter::IProcessAnimMaterial"); + + if( !texmap ) + return false; + +#if 0 + StdUVGen *uvGen = ((BitmapTex *)texmap)->GetUVGen(); + if( IsAnimatedByName(uvGen, TSTR("U Offset")) ) + return true; + if( IsAnimatedByName(uvGen, TSTR("V Offset")) ) + return true; + if( IsAnimatedByName(uvGen, TSTR("U Tiling")) ) + return true; + if( IsAnimatedByName(uvGen, TSTR("V Tiling")) ) + return true; + if( IsAnimatedByName(uvGen, TSTR("Angle")) ) + return true; + + if( IsAnimatedByName(uvGen, TSTR("U Angle")) ) + return true; + if( IsAnimatedByName(uvGen, TSTR("V Angle")) ) + return true; + if( IsAnimatedByName(uvGen, TSTR("W Angle")) ) + return true; + + return false; +#else + CStr className; + texmap->GetClassName(className); + if( strcmp(className,"Bitmap") && strcmp(className,"Plasma Layer") && strcmp(className,"Plasma Layer Dbg.")) + return false; + return (IHasAnimatedControllers(((BitmapTex *)texmap)->GetUVGen())); +#endif + hsGuardEnd; +} + +hsBool hsMaterialConverter::IHasAnimatedControllers(Animatable* anim) +{ + hsGuardBegin("hsMaterialConverter::IHasAnimatedControllers"); + + if( anim ) + { + Control* ctl = GetControlInterface(anim); + if (hsControlConverter::Instance().HasKeyTimes(ctl)) + return true; + + int nSub = anim->NumSubs(); + int i; + for (i = 0; i < nSub; i++) + { + if (anim->SubAnim(i)==nil) + continue; + + if( IHasAnimatedControllers(anim->SubAnim(i)) ) + return true; + } + } + + return false; + hsGuardEnd; +} + + +hsBool hsMaterialConverter::IIsAnimatedTexmap(Texmap* texmap) +{ + hsGuardBegin("hsMaterialConverter::IIsAnimatedTexmap"); + + if (!texmap) + return false; + + Control *ctl=nil; + if (hsControlConverter::Instance().GetControllerByName(texmap, TSTR("Ambient"), ctl)) + return true; + if (hsControlConverter::Instance().GetControllerByName(texmap, TSTR("Diffuse"), ctl)) + return true; + if (hsControlConverter::Instance().GetControllerByName(texmap, TSTR("Color"), ctl)) + return true; + if (hsControlConverter::Instance().GetControllerByName(texmap, TSTR("Opacity"), ctl)) + return true; + + if (HasAnimatedTextures(texmap) || IsAVILayer(texmap) || IsBinkLayer(texmap)|| IsQTLayer(texmap) || ITextureTransformIsAnimated(texmap)) + return true; + + return false; + hsGuardEnd; +} + +// +// returns true if this material is animated +// +hsBool hsMaterialConverter::IsAnimatedMaterial(Mtl* mtl) +{ + hsGuardBegin("hsMaterialConverter::IsAnimatedMaterial"); + + if (!mtl) + return false; + + if (IsMultiMat(mtl)) + { + int iMtl; + for (iMtl = 0; iMtl < mtl->NumSubMtls(); iMtl++) + { + if (IsAnimatedMaterial(mtl->GetSubMtl(iMtl))) + return true; + } + return false; + } + else +// if (IsStdMat(mtl) || mtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0)) + { + // It is std material. does not have any submaterials + StdMat* std = (StdMat *)mtl; + int i; + for(i=0;iNumSubTexmaps();i++) + { + if (IIsAnimatedTexmap(std->GetSubTexmap(i))) + return true; + } + return false; + } + + hsGuardEnd; +} + +void hsMaterialConverter::GetUsedMaterials(plMaxNode* node, hsBitVector& used) +{ + Mtl* mtl = GetBaseMtl(node); + + used.Clear(); + if( !IsMultiMat(mtl) ) + { + used.SetBit(0); + return; + } + + hsBool deleteIt = false; + TriObject* triObj = node->GetTriObject(deleteIt); + if( triObj ) + { + Mesh* mesh = &(triObj->mesh); + + int numFaces = mesh->getNumFaces(); + int i; + for( i = 0; i < numFaces; i++ ) + { + Face *face = &mesh->faces[ i ]; + + used.SetBit(face->getMatID()); + } + } + return; +} + +//// HasMaterialDiffuseOrOpacityAnimation //////////////////////////////////// +// Returns true if the material or any of its submaterials has a diffuse +// animation controller. Handy for those times when you need to decide on +// a lighting model... +// UPDATE 8.26 mcn - Now checks for opacity controllers as well + +hsBool hsMaterialConverter::HasMaterialDiffuseOrOpacityAnimation(plMaxNode* node, Mtl* mtl) +{ + hsGuardBegin( "hsMaterialConverter::HasMaterialDiffuseOrOpacityAnimation" ); + + if( !mtl ) + mtl = GetBaseMtl(node); + + if( !mtl ) + return false; + + const char *dbgName = mtl->GetName(); + + // mf + // Inserting this test here. The glaring omission was that if you had several + // passes, each using a different material opacity, then you can't bake the + // alpha into the vertices, so you can't go to kLiteVtxNonPreshaded (which is + // what this function really tests to see if you can do). + // More specifically, we can't bake material opacity into the verts if: + // Two or more passes use alpha blending AND have different opacity values. + if( IsMultipassMat(mtl) ) + { + float baseOpac = -1.f; + Mtl* subMtl = mtl->GetSubMtl(0); + if( subMtl->ClassID() == PASS_MTL_CLASS_ID ) + { + plPassMtlBase* passMtl = (plPassMtlBase*)subMtl; + if( plPassMtlBase::kBlendAlpha == passMtl->GetOutputBlend() ) + baseOpac = passMtl->GetOpacity(); + } + int iMtl; + for( iMtl = 1; iMtl < mtl->NumSubMtls(); iMtl++ ) + { + if( subMtl->ClassID() == PASS_MTL_CLASS_ID ) + { + plPassMtlBase* passMtl = (plPassMtlBase*)subMtl; + if( (plPassMtlBase::kBlendAlpha == passMtl->GetOutputBlend()) + &&(baseOpac != passMtl->GetOpacity()) ) + { + if( baseOpac >= 0 ) + return true; + baseOpac = passMtl->GetOpacity(); + } + } + } + } + + if( IsMultiMat( mtl ) ) + { + hsBitVector usedSubs; + GetUsedMaterials(node, usedSubs); + + int iMtl; + for( iMtl = 0; iMtl < mtl->NumSubMtls(); iMtl++ ) + { + // We have to check for nil here, because HasMaterialDif... assumes that when you pass in nil, + // it should just grab the material on the node. In this case that's the multimat we're looping + // through. Hellooooooo infinite loop. + if( mtl->GetSubMtl(iMtl) && usedSubs.IsBitSet(iMtl) && HasMaterialDiffuseOrOpacityAnimation( node, mtl->GetSubMtl(iMtl) ) ) + return true; + } + return false; + } + else if( IsMultipassMat( mtl ) || IsCompositeMat( mtl ) ) + { + int iMtl; + for( iMtl = 0; iMtl < mtl->NumSubMtls(); iMtl++ ) + { + if( mtl->GetSubMtl(iMtl) && HasMaterialDiffuseOrOpacityAnimation( node, mtl->GetSubMtl(iMtl) ) ) + return true; + } + return false; + } + else if ( IsParticleMat( mtl ) ) + { + plParticleMtl *partMtl = (plParticleMtl *)mtl; + return partMtl->GetColorController() != nil; + } + else if( IsHsMaxMat(mtl) || IsDecalMat(mtl) ) + { + // It is std material. does not have any submaterials + StdMat *std = (StdMat *)mtl; + plPassMtlBase *passMtl = (plPassMtlBase *)mtl; + Control *ctl = nil; + int i; + + if( passMtl->GetPreshadeColorController() != nil ) + return true; + if( passMtl->GetRuntimeColorController() != nil ) + return true; + if( passMtl->GetOpacityController() != nil ) + return true; + + for( i = 0; i < std->NumSubTexmaps(); i++ ) + { + if( hsControlConverter::Instance().GetControllerByName( std->GetSubTexmap( i ), TSTR( "Diffuse" ), ctl ) ) + return true; + if( hsControlConverter::Instance().GetControllerByName( std->GetSubTexmap( i ), TSTR( "Color" ), ctl ) ) + return true; + if( hsControlConverter::Instance().GetControllerByName( std->GetSubTexmap( i ), TSTR( "Opacity" ), ctl ) ) + return true; + } + return false; + } + + return false; + + hsGuardEnd; +} + +//// HasEmissiveLayer //////////////////////////////////////////////////////// +// Returns true if the any of the layers of any of the submaterials or +// the main material are emissive. + +hsBool hsMaterialConverter::HasEmissiveLayer(plMaxNode* node, Mtl* mtl) +{ + hsGuardBegin( "hsMaterialConverter::HasEmissiveLayer" ); + + if( !mtl ) + mtl = GetBaseMtl(node); + + if( !mtl ) + return false; + + const char *dbgName = mtl->GetName(); + + if( IsMultiMat( mtl ) ) + { + hsBitVector usedSubs; + GetUsedMaterials(node, usedSubs); + + int iMtl; + for( iMtl = 0; iMtl < mtl->NumSubMtls(); iMtl++ ) + { + if( usedSubs.IsBitSet(iMtl) && HasEmissiveLayer( node, mtl->GetSubMtl(iMtl) ) ) + return true; + } + return false; + } + else if( IsMultipassMat( mtl ) || IsCompositeMat( mtl ) ) + { + int iMtl; + for( iMtl = 0; iMtl < mtl->NumSubMtls(); iMtl++ ) + { + if( HasEmissiveLayer( node, mtl->GetSubMtl(iMtl) ) ) + return true; + } + return false; + } + else if ( IsParticleMat( mtl ) ) + { + plParticleMtl *partMtl = (plParticleMtl *)mtl; + if( partMtl->GetParamBlockByID( plParticleMtl::kRefBasic )->GetInt( plParticleMtl::kNormal ) == plParticleMtl::kEmissive ) + return true; + } + else if( mtl->ClassID() == PASS_MTL_CLASS_ID ) + { + // It is std material. does not have any submaterials + plPassMtlBase *passMtl = (plPassMtlBase *)mtl; + + if( passMtl->GetEmissive() ) + return true; + } + + return false; + + hsGuardEnd; +} + +// +// returns true if the material onthis node is animated +// +hsBool hsMaterialConverter::HasAnimatedMaterial(plMaxNode* node) +{ + hsGuardBegin("hsMaterialConverter::HasAnimatedMaterial"); + + return (node ? IsAnimatedMaterial(node->GetMtl()) : false); + hsGuardEnd; +} + +Mtl* hsMaterialConverter::FindSubMtlByName(TSTR& name, Animatable* anim) +{ + hsGuardBegin("hsMaterialConverter::FindSubMtlByName"); + + if( !anim || !IsMtl(anim) ) + return nil; + + Mtl* mtl = (Mtl*)anim; + + if( mtl->GetName() == name ) + return mtl; + + if( IsMultiMat(mtl) ) + { + int i; + for( i = 0; i < mtl->NumSubs(); i++ ) + { + Mtl* retVal; + if( retVal = FindSubMtlByName(name, mtl->SubAnim(i)) ) + return retVal; + } + } + + return nil; + hsGuardEnd; +} + +Mtl* hsMaterialConverter::FindSceneMtlByName(TSTR& name) +{ + hsGuardBegin("hsMaterialConverter::FindSceneMtlByName"); + + ReferenceTarget *scene = fInterface->GetScenePointer(); + + // First look through the editor slots + ReferenceTarget* mtlEdit; + mtlEdit = scene->GetReference(0); + + int i; + for( i = 0; i < mtlEdit->NumSubs(); i++ ) + { + Mtl* mtl = FindSubMtlByName(name, mtlEdit->SubAnim(i)); + if( mtl ) + return (mtl); + } + + + // Now look through the rest of the scene + MtlBaseLib& mtlLib = *(MtlBaseLib*)scene->GetReference(1); + for( i = 0; i < mtlLib.Count(); i++ ) + { + Mtl* mtl = FindSubMtlByName(name, mtlLib[i]); + if( mtl ) + return (mtl); + } + + return nil; + hsGuardEnd; +} + +int hsMaterialConverter::GetMaterialArray(Mtl *mtl, plMaxNode* node, hsTArray& out, UInt32 multiIndex /* = 0 */) +{ + hsTArray* arGh = CreateMaterialArray(mtl, node, multiIndex); + int i; + for( i = 0; i < arGh->GetCount(); i++ ) + { + out.Append(arGh->Get(i).fMaterial); + } + + delete arGh; + + return out.GetCount(); +} + +static void GetMtlNodes(Mtl *mtl, INodeTab& nodes) +{ + if (!mtl) + return; + + RefList& refs = mtl->GetRefList(); + RefListItem *item = refs.FirstItem(); + while (item) + { + if (item->maker->SuperClassID() == BASENODE_CLASS_ID) + { + INode *node = (INode*)item->maker; + if (node->GetMtl() == mtl) + nodes.Append(1, &node); + } + + item = item->next; + } +} + +int hsMaterialConverter::GetMaterialArray(Mtl *mtl, hsTArray& out, UInt32 multiIndex /* = 0 */) +{ + INodeTab nodes; + GetMtlNodes(mtl, nodes); + + for (int i = 0; i < nodes.Count(); i++) + { + hsTArray tempOut; + GetMaterialArray(mtl, (plMaxNode*)nodes[i], tempOut, multiIndex); + + for (int j = 0; j < tempOut.GetCount(); j++) + { + if (out.Find(tempOut[j]) == out.kMissingIndex) + out.Append(tempOut[j]); + } + } + + return out.GetCount(); +} + +// Grab all the hsGMaterials that have been created as a result of converting mtl +void hsMaterialConverter::CollectConvertedMaterials(Mtl *mtl, hsTArray& out) +{ + int i; + for (i = 0; i < fDoneMaterials.GetCount(); i++) + { + const DoneMaterialData &dmd = fDoneMaterials.Get(i); + if (dmd.fMaxMaterial == mtl) + out.Append(dmd.fHsMaterial); + } +} + +plClothingItem *hsMaterialConverter::GenerateClothingItem(plClothingMtl *mtl, const plLocation &loc) +{ + char clothKeyName[256]; + plClothingItem *cloth = TRACKED_NEW plClothingItem(); + cloth->SetName(mtl->GetName()); + cloth->fSortOrder = (mtl->GetDefault() ? 0 : 1); + + char *accName = mtl->GetForcedAccessoryName(); + if (accName && strcmp(accName, "")) + cloth->fAccessoryName = hsStrcpy(accName); + + Color tint1 = mtl->GetDefaultTint1(); + Color tint2 = mtl->GetDefaultTint2(); + cloth->fDefaultTint1[0] = tint1.r * 255; + cloth->fDefaultTint1[1] = tint1.g * 255; + cloth->fDefaultTint1[2] = tint1.b * 255; + cloth->fDefaultTint2[0] = tint2.r * 255; + cloth->fDefaultTint2[1] = tint2.g * 255; + cloth->fDefaultTint2[2] = tint2.b * 255; + + sprintf(clothKeyName, "CItm_%s", cloth->fName); + hsgResMgr::ResMgr()->NewKey(clothKeyName, cloth, loc); + + plNodeRefMsg* nodeRefMsg = TRACKED_NEW plNodeRefMsg(plKeyFinder::Instance().FindSceneNodeKey(loc), + plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kGeneric); + hsgResMgr::ResMgr()->AddViaNotify(cloth->GetKey(), nodeRefMsg, plRefFlags::kActiveRef); + + mtl->InitTilesets(); + cloth->fTileset = mtl->GetTilesetIndex(); + plClothingTileset *tileset = mtl->fTilesets.Get(cloth->fTileset); + int i, j; + for (i = 0; i < tileset->fElements.GetCount(); i++) + { + for (j = 0; j < plClothingElement::kLayerMax; j++) + { + UInt32 clipLevels; + UInt32 startWidth; + char *elementName = tileset->fElements.Get(i)->fName; + plPlasmaMAXLayer *layer = (plPlasmaMAXLayer *)mtl->GetTexmap(i, j); + if (layer == nil || layer->GetPBBitmap() == nil) + continue; + + const char *texName = layer->GetPBBitmap()->bi.Name(); + + for (clipLevels = 0, startWidth = layer->GetPBBitmap()->bi.Width(); + startWidth > tileset->fElements.Get(i)->fWidth; + clipLevels++, startWidth >>= 1); + + plMipmap *tex = plMipmap::ConvertNoRef(plLayerConverter::Instance().CreateSimpleTexture(texName, loc, clipLevels)); + if (tex == nil) + { + if (fErrorMsg->Set(!(fWarned & kWarnedMissingClothingTexture), mtl->GetName(), + "Unable to create texture %s. This clothing item won't look right.", + texName).CheckAskOrCancel() ) + { + fWarned |= kWarnedMissingClothingTexture; + } + continue; + } + plElementRefMsg *eMsg = TRACKED_NEW plElementRefMsg(cloth->GetKey(), plRefMsg::kOnCreate, i, -1, elementName, j); + hsgResMgr::ResMgr()->AddViaNotify(tex->GetKey(), eMsg, plRefFlags::kActiveRef); + } + } + mtl->ReleaseTilesets(); + + plPlasmaMAXLayer *layer = (plPlasmaMAXLayer *)mtl->GetThumbnail(); + plMipmap *thumbnail = nil; + PBBitmap *pbbm = nil; + if (layer != nil) + { + char texName[ 512 ]; + if( layer->GetBitmapFileName( texName, sizeof( texName ) ) ) + thumbnail = plMipmap::ConvertNoRef(plLayerConverter::Instance().CreateSimpleTexture(texName, loc, 0, plBitmap::kForceOneMipLevel)); + } + if (thumbnail != nil) + { + plGenRefMsg *msg= TRACKED_NEW plGenRefMsg(cloth->GetKey(), plRefMsg::kOnCreate, -1, -1); + hsgResMgr::ResMgr()->AddViaNotify(thumbnail->GetKey(), msg, plRefFlags::kActiveRef); + } + cloth->fDescription = hsStrcpy(mtl->GetDescription()); + cloth->fCustomText = hsStrcpy(mtl->GetCustomText()); + + return cloth; +} + +static int ICompareBaseLayerTexture(const hsMaterialConverter::DoneMaterialData* one, const hsMaterialConverter::DoneMaterialData* two) +{ + const plLayerInterface* oneLay = one->fHsMaterial->GetLayer(0); + const plLayerInterface* twoLay = two->fHsMaterial->GetLayer(0); + const plBitmap* oneTex = oneLay->GetTexture(); + const plBitmap* twoTex = twoLay->GetTexture(); + + if( !oneTex && !twoTex ) + return 0; + if( oneTex && !twoTex ) + return 1; + if( !oneTex && twoTex ) + return -1; + + return _stricmp(oneTex->GetKey()->GetName(), twoTex->GetKey()->GetName()); +} + +static int IIsAnimatedLayer(const plLayerInterface* lay) +{ + return nil == plLayer::ConvertNoRef(lay); +} + +static int ICompareColors(const hsColorRGBA& one, const hsColorRGBA& two) +{ + int oneR = int(one.r * 256.f); + int oneG = int(one.g * 256.f); + int oneB = int(one.b * 256.f); + int twoR = int(two.r * 256.f); + int twoG = int(two.g * 256.f); + int twoB = int(two.b * 256.f); + + int powerOne = oneR + oneG + oneB; + int powerTwo = twoR + twoG + twoB; + + if( powerOne < powerTwo ) + return -1; + if( powerOne > powerTwo ) + return 1; + + if( oneR < twoR ) + return -1; + if( oneR > twoR ) + return 1; + + if( oneG < twoG ) + return -1; + if( oneG > twoG ) + return 1; + + if( oneB < twoB ) + return -1; + if( oneB > twoB ) + return 1; + + return 0; +} + +static int ICompareDoneLayers(const plLayerInterface* one, const plLayerInterface* two) +{ + int retVal; + + if( one == two ) + return 0; + + if( one->GetTexture() && !two->GetTexture() ) + return 1; + if( !one->GetTexture() && two->GetTexture() ) + return -1; + + if( one->GetTexture() && two->GetTexture() ) + { + retVal = _stricmp(one->GetTexture()->GetKey()->GetName(), two->GetTexture()->GetKey()->GetName()); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + } + + retVal = Int32(one->GetBlendFlags()) - Int32(two->GetBlendFlags()); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + + retVal = Int32(one->GetZFlags()) - Int32(two->GetZFlags()); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + + retVal = Int32(one->GetClampFlags()) - Int32(two->GetClampFlags()); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + + retVal = Int32(one->GetMiscFlags()) - Int32(two->GetMiscFlags()); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + + retVal = Int32(one->GetShadeFlags()) - Int32(two->GetShadeFlags()); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + + retVal = ICompareColors(one->GetAmbientColor(), two->GetAmbientColor()); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + + retVal = ICompareColors(one->GetPreshadeColor(), two->GetPreshadeColor()); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + + retVal = ICompareColors(one->GetRuntimeColor(), two->GetRuntimeColor()); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + + retVal = ICompareColors(one->GetSpecularColor(), two->GetSpecularColor()); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + + if( one->GetSpecularPower() < two->GetSpecularPower() ) + return -1; + if( one->GetSpecularPower() > two->GetSpecularPower() ) + return 1; + + if( one->GetLODBias() < two->GetLODBias() ) + return -1; + if( one->GetLODBias() > two->GetLODBias() ) + return 1; + + if( one->GetOpacity() < two->GetOpacity() ) + return -1; + if( one->GetOpacity() > two->GetOpacity() ) + return 1; + + if( one->GetUVWSrc() < two->GetUVWSrc() ) + return -1; + if( one->GetUVWSrc() > two->GetUVWSrc() ) + return 1; + + if( one->GetTexture() && two->GetTexture() ) + { + if( one->GetTransform() != two->GetTransform() ) + { + // Okay, they're not equal. Greater/Lesser doesn't make much + // sense in this context, so we just need some arbitrary but + // consistent comparison. So if the transforms aren't equal, + // the one on the layer with a larger pointer value is greater. + if( one > two ) + return 1; + else + return -1; + } + } + + if( IIsAnimatedLayer(one) || IIsAnimatedLayer(two) ) + { + // Same deal as transform. If either is animated, then for + // the purposes here (whether one can replace the other), they + // aren't interchangeable. Even if they have the same animation, + // they need to stay independently playable. + if( one > two ) + return 1; + else + return -1; + } + + return 0; +} + +static int ICompareDoneMats(const void *arg1, const void *arg2) +{ + const hsMaterialConverter::DoneMaterialData* one = *(const hsMaterialConverter::DoneMaterialData**)arg1; + const hsMaterialConverter::DoneMaterialData* two = *(const hsMaterialConverter::DoneMaterialData**)arg2; + + hsGMaterial* oneMat = one->fHsMaterial; + hsGMaterial* twoMat = two->fHsMaterial; + + // compare the base layers + // First compare the textures. If those are the same, these two materials are very much alike. + // This will (quickly) weed out 99% of the non-equivalent materials before the more expensive checks. + plLayerInterface* oneLay = oneMat->GetLayer(0); + plLayerInterface* twoLay = twoMat->GetLayer(0); + int retVal = ICompareBaseLayerTexture(one, two); + if( retVal > 0 ) + return 1; + if( retVal < 0 ) + return -1; + + // Check for lightmap compatible-ness. + // The case we're looking for is if: + // two different nodes are using the same material, + // && both are lightmapped + // && either + // they are different lightmap components + // || the lightmap component doesn't want to share. + // If true, we want to ensure these two materials don't get combined. + // Since this function is used for material sorting, we'll (arbitrarily) + // return the comparison of owner node pointers as our consistent result. + plMaxNode* oneNode = one->fNode; + plMaxNode* twoNode = two->fNode; + if( oneNode && twoNode && (oneNode != twoNode) ) + { + plLightMapComponent* oneLM = oneNode->GetLightMapComponent(); + plLightMapComponent* twoLM = twoNode->GetLightMapComponent(); + if( oneLM != twoLM ) + { + return oneNode > twoNode ? 1 : -1; + } + if( oneLM ) + { + if( !oneLM->GetShared() ) // and therefore twoLM, since they're equal + { + return oneNode > twoNode ? 1 : -1; + } + } + } + + if( oneMat == twoMat ) + return 0; + + // Now compare everything else about the base layer. + retVal = ICompareDoneLayers(oneMat->GetLayer(0), twoMat->GetLayer(0)); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + + // base layers the same, go up a layer at a time. Non-existence of a layer is < any existent layer + int i; + for( i = 1; i < oneMat->GetNumLayers(); i++ ) + { + if( twoMat->GetNumLayers() <= i ) + return 1; + + retVal = ICompareDoneLayers(oneMat->GetLayer(i), twoMat->GetLayer(i)); + if( retVal < 0 ) + return -1; + else if( retVal > 0 ) + return 1; + } + if( oneMat->GetNumLayers() < twoMat->GetNumLayers() ) + return -1; + + return 0; +} + +void hsMaterialConverter::IPrintDoneMat(hsStream* stream, const char* prefix, DoneMaterialData* doneMat) +{ + if( doneMat->fOwnedCopy ) + stream->WriteString("Unique "); + stream->WriteString(prefix); + + char buff[512]; + sprintf(buff, "%s\n", doneMat->fMaxMaterial ? doneMat->fMaxMaterial->GetName() : "BLANK"); + stream->WriteString(buff); + + sprintf(buff, "\t\t%d Layers\n", doneMat->fHsMaterial->GetNumLayers()); + stream->WriteString(buff); + + int i; + for( i = 0; i < doneMat->fHsMaterial->GetNumLayers(); i++ ) + { + const plLayerInterface* layer = doneMat->fHsMaterial->GetLayer(i); + + const char* blendMode = "error"; + switch(layer->GetBlendFlags() & hsGMatState::kBlendMask) + { + case hsGMatState::kBlendAlpha: + blendMode = "Alpha"; + break; + case hsGMatState::kBlendMult: + blendMode = "Mult"; + break; + case hsGMatState::kBlendAdd: + blendMode = "Add"; + break; + case hsGMatState::kBlendAddColorTimesAlpha: + blendMode = "AddColorTimesAlpha"; + break; + case hsGMatState::kBlendDetail: + blendMode = "Detail"; + break; + case hsGMatState::kBlendMADD: + blendMode = "MADD"; + break; + case hsGMatState::kBlendDot3: + blendMode = "Dot3"; + break; + default: + blendMode = "Opaque"; + break; + } + + sprintf(buff, "\t\tLayer %d [%s]\n", i, IIsAnimatedLayer(layer) ? "Animated" : "Static"); + stream->WriteString(buff); + + sprintf(buff, "\t\t\t%s [B%#08x Z%#08x C%#08x M%#08x S%08x]\n", blendMode, + layer->GetBlendFlags(), + layer->GetZFlags(), + layer->GetClampFlags(), + layer->GetMiscFlags(), + layer->GetShadeFlags()); + stream->WriteString(buff); + + sprintf(buff, "\t\t\tAmbient(%f,%f,%f) Preshade(%f,%f,%f)\n", + layer->GetAmbientColor().r, + layer->GetAmbientColor().g, + layer->GetAmbientColor().b, + layer->GetPreshadeColor().r, + layer->GetPreshadeColor().g, + layer->GetPreshadeColor().b); + stream->WriteString(buff); + + sprintf(buff, "\t\t\tColor(%f,%f,%f) Opacity(%f) UVWSrc(%x)\n", + layer->GetRuntimeColor().r, + layer->GetRuntimeColor().g, + layer->GetRuntimeColor().b, + layer->GetOpacity(), + layer->GetUVWSrc()); + stream->WriteString(buff); + + sprintf(buff, "\t\t\tSpec(%f,%f,%f) Power(%f) LODBias(%f)\n", + layer->GetSpecularColor().r, + layer->GetSpecularColor().g, + layer->GetSpecularColor().b, + layer->GetSpecularPower(), + layer->GetLODBias()); + stream->WriteString(buff); + + sprintf(buff, "\t\t\tTexture %s\n", layer->GetTexture() && layer->GetTexture()->GetKey() && layer->GetTexture()->GetKey()->GetName() + ? layer->GetTexture()->GetKey()->GetName() + : "None"); + stream->WriteString(buff); + + if( layer->GetTransform().fFlags & hsMatrix44::kIsIdent ) + { + sprintf(buff, "\t\t\tXForm = None\n"); + stream->WriteString(buff); + } + else + { + sprintf(buff, "\t\t\tXForm = \t{ {%f,%f,%f,%f}, \n\t\t\t\t\t{%f,%f,%f,%f}, \n\t\t\t\t\t{%f,%f,%f,%f} }\n", + layer->GetTransform().fMap[0][0], + layer->GetTransform().fMap[0][1], + layer->GetTransform().fMap[0][2], + layer->GetTransform().fMap[0][3], + + layer->GetTransform().fMap[1][0], + layer->GetTransform().fMap[1][1], + layer->GetTransform().fMap[1][2], + layer->GetTransform().fMap[1][3], + + layer->GetTransform().fMap[2][0], + layer->GetTransform().fMap[2][1], + layer->GetTransform().fMap[2][2], + layer->GetTransform().fMap[2][3]); + stream->WriteString(buff); + } + } +} + +hsBool hsMaterialConverter::IEquivalent(DoneMaterialData* one, DoneMaterialData* two) +{ + if( one->fOwnedCopy || two->fOwnedCopy ) + return false; + + return ICompareDoneMats(&one, &two) == 0; +} + +void hsMaterialConverter::ISortDoneMaterials(hsTArray& doneMats) +{ + doneMats.SetCount(fDoneMaterials.GetCount()); + int i; + for( i = 0; i < fDoneMaterials.GetCount(); i++ ) + doneMats[i] = &fDoneMaterials[i]; + + + void* arr = doneMats.AcquireArray(); + qsort((void*)arr, doneMats.GetCount(), sizeof(DoneMaterialData*), ICompareDoneMats); +} + +void hsMaterialConverter::IGenMaterialReport(const char* path) +{ + hsTArray doneMats; + ISortDoneMaterials(doneMats); + + IPrintDoneMaterials(path, doneMats); +} + +void hsMaterialConverter::IPrintDoneMaterials(const char* path, hsTArray& doneMats) +{ + TSTR maxFileTstr = GetCOREInterface()->GetCurFileName(); + char maxFile[256]; + hsStrncpy(maxFile, maxFileTstr, 128); + char *dot = strrchr(maxFile, '.'); + if (dot) + *dot = 0; + + char fileName[512]; + if( path[hsStrlen(path)-1] == '\\' ) + { + sprintf(fileName, "%slog\\mat_%s.log", path, maxFile); + } + else + { + sprintf(fileName, "%s\\log\\mat_%s.log", path, maxFile); + } + + hsUNIXStream stream; + if( !stream.Open(fileName, "wt") ) + { + // We may not have a \log folder. If that failed, try + // putting it in the \dat folder. If that doesn't work, + // just quietly give up. + if( path[hsStrlen(path)-1] == '\\' ) + { + sprintf(fileName, "%sdat\\mat_%s.log", path, maxFile); + } + else + { + sprintf(fileName, "%s\\dat\\mat_%s.log", path, maxFile); + } + if( !stream.Open(fileName, "wt") ) + return; + } + + stream.WriteString(maxFile); + stream.WriteString("\n===============================================\n===============================================\n"); + + if( !doneMats.GetCount() ) + { + char buff[256]; + sprintf(buff, ""); + stream.WriteString("No Materials Generated\n"); + stream.Close(); + return; + } + + char pref[32]; + sprintf(pref, "%d\t", 0); + IPrintDoneMat(&stream, pref, doneMats[0]); + + bool lastWasDup = false; + int dupSets = 0; + int duplicates = 0; + int uniques = 0; + int i; + for( i = 1; i < doneMats.GetCount(); i++ ) + { + if( IEquivalent(doneMats[i], doneMats[i-1]) ) + { + if( !lastWasDup ) + { + dupSets++; + lastWasDup = true; + } + duplicates++; + sprintf(pref, "==%d\t", i); + } + else if( !ICompareBaseLayerTexture(doneMats[i], doneMats[i-1]) ) + { + sprintf(pref, "~~%d\t", i); + lastWasDup = false; + } + else + { + sprintf(pref, "%d\t", i); + lastWasDup = false; + } + if( doneMats[i]->fOwnedCopy ) + uniques++; + + IPrintDoneMat(&stream, pref, doneMats[i]); + + } + char buff[256]; + sprintf(buff, "\n===================================================================\n"); + stream.WriteString(buff); + sprintf(buff, "%d sets of duplicates, %d total duplicate count\n", dupSets, duplicates); + stream.WriteString(buff); + + sprintf(buff, "System generated duplicates:\n"); + stream.WriteString(buff); + + sprintf(buff, "Gameplay forced unique - %d\n", uniques); + stream.WriteString(buff); + + sprintf(buff, "RT:%d, UV:%d, AL:%d, FD:%d\n", dupCuzRT, dupCuzNumUV, dupCuzAlphaLayer, dupCuzFade); + stream.WriteString(buff); + + sprintf(buff, "\nThank you, and have a lovely day.\n"); + stream.WriteString(buff); + + stream.Close(); +} + +hsMaterialConverter::DoneMaterialData* hsMaterialConverter::IFindDoneMaterial(DoneMaterialData& done) +{ + int i; + for( i = 0; i < fDoneMaterials.GetCount(); i++ ) + { + if( IEquivalent(&fDoneMaterials[i], &done) ) + { + return &fDoneMaterials[i]; + } + } + return nil; +} + +plMipmap *hsMaterialConverter::GetStaticColorTexture(Color c, plLocation &loc) +{ + char texName[256]; + UInt32 colorHex = MakeUInt32Color(c.r, c.g, c.b, 1.f); + sprintf(texName, "StaticColorTex_4x4_%X", colorHex); + + int w = 4; + int h = 4; + + plMipmap *texture = plBitmapCreator::Instance().CreateBlankMipmap(w, h, plMipmap::kARGB32Config, 1, texName, loc ); + + // set the color data + UInt32* pix = (UInt32*)texture->GetImage(); + int x, y; + + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + *pix++ = colorHex; + } + } + + return texture; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaterialConverter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaterialConverter.h new file mode 100644 index 00000000..4b2999a5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaterialConverter.h @@ -0,0 +1,351 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __HSMATERIALCONVERTER_H +#define __HSMATERIALCONVERTER_H + +#include "HeadSpin.h" +#include "hsTemplates.h" + +#include "Max.h" + +class Interface; + +class hsStream; +class hsScene; +class hsGMaterial; +class plLayer; +class plLayerInterface; +class hsGBitmapClass; +class hsGQTLayer; +class hsGAVILayer; +class hsGBinkLayer; +class hsConverterUtils; +class hsGAnimLayer; +class plBitmap; +class plMipmap; +class plErrorMsg; + +class Mtl; +class Texmap; +class plMaxNode; +class StdUVGen; +class BitmapTex; +class StdMat; +class TSTR; +class Animatable; +class Bitmap; +class plLocation; + +class plLayerTex; +class plBitmapData; +class plCubicRenderTarget; +class plStaticEnvLayer; +class plDynamicEnvLayer; +class plDynamicTextLayer; +class plPassMtlBase; +class plClothingItem; +class plClothingMtl; +class hsBitVector; + +class plExportMaterialData +{ +public: + UInt32 fNumBlendChannels; + hsGMaterial *fMaterial; +}; + +class hsMaterialConverter +{ +private: + hsMaterialConverter(); + +public: + ~hsMaterialConverter(); + static hsMaterialConverter& Instance(); + + void Init(hsBool save, plErrorMsg *msg); + + void FreeMaterialCache(const char* path); + + static void GetUsedMaterials(plMaxNode* node, hsBitVector& used); + static hsBool IsTwoSided(Mtl* m, int iFace); + static hsBool PreserveUVOffset(Mtl* m); + static hsBool IsMultiMat(Mtl *m); + static hsBool IsMultipassMat(Mtl *m); + static hsBool IsHsMaxMat(Mtl *m); + static hsBool IsDecalMat(Mtl *m); + static hsBool IsCompositeMat(Mtl *m); + static hsBool IsParticleMat(Mtl *m); + static hsBool IsHsEnvironMapMat(Mtl *m); + static hsBool IsClothingMat(Mtl *m); +// static hsBool IsPortalMat(Mtl *m); + static hsBool HasAnimatedTextures(Texmap* texMap); + static hsBool HasAnimatedMaterial(plMaxNode* node); + static hsBool IsAnimatedMaterial(Mtl* mtl); + static hsBool HasMaterialDiffuseOrOpacityAnimation(plMaxNode* node, Mtl* mtl=nil); + static hsBool HasEmissiveLayer(plMaxNode* node, Mtl* mtl=nil); + static hsBool IsWaterLayer(plMaxNode* node, Texmap* texMap); + static hsBool IsFireLayer(plMaxNode* node, Texmap* texMap); + static hsBool IsAVILayer(Texmap* texMap); + static hsBool IsQTLayer(Texmap* texMap); + static hsBool IsBinkLayer(Texmap* texMap); +// static hsBool IsEnvironMapped(plMaxNode *node); +// static hsBool IsPortal(plMaxNode* node); + static hsBool ForceNoUvsFlatten(plMaxNode* node); +// static hsBool IsRenderProc(Mtl* mtl); + static Mtl* GetBaseMtl(Mtl* mtl); + static Mtl* GetBaseMtl(plMaxNode* node); + static int GetCoordMapping(StdUVGen *uvgen); + static void GetNodesByMaterial(Mtl *mtl, hsTArray &out); + + static UInt32 VertexChannelsRequestMask(plMaxNode* node, int iSubMtl, Mtl* mtl); + static UInt32 VertexChannelsRequiredMask(plMaxNode* node, int iSubMtl); + static int NumVertexOpacityChannelsRequired(plMaxNode* node, int iSubMtl); + static UInt32 ColorChannelsUseMask(plMaxNode* node, int iSubMtl); + static int MaxUsedUVWSrc(plMaxNode* node, Mtl* mtl); + + + static hsBool IsBumpLayer(Texmap* texMap); + static hsBool IsBumpMtl(Mtl* mtl); + static hsBool HasBumpLayer(plMaxNode* node, Mtl* mtl); + static BitmapTex* GetBumpLayer(plMaxNode* node, Mtl* mtl); + + static hsBool HasVisDists(plMaxNode* node, Mtl* subMtl, hsScalar& minDist, hsScalar& maxDist); + static hsBool HasVisDists(plMaxNode* node, int iSubMtl, hsScalar& minDist, hsScalar& maxDist); + + static hsBool IMustBeUniqueMaterial( Mtl *mtl ); + static hsBool IMustBeUniqueLayer( Texmap *layer ); + + static Mtl* FindSubMtlByName(TSTR& name, Animatable* anim); + Mtl* FindSceneMtlByName(TSTR& name); + + hsTArray *CreateMaterialArray(Mtl *maxMaterial, plMaxNode *node, UInt32 multiIndex); + + // true if last material creation changed MAX time, invalidating current mesh + hsBool ChangedTimes() { return fChangedTimes; } + + Texmap *GetUVChannelBase(plMaxNode *node, Mtl* mtl, int which); + + hsBool ClearDoneMaterials(plMaxNode* node); + + int GetMaterialArray(Mtl *mtl, plMaxNode* node, hsTArray& out, UInt32 multiIndex = 0 ); + int GetMaterialArray(Mtl *mtl, hsTArray& out, UInt32 multiIndex = 0); + void CollectConvertedMaterials(Mtl *mtl, hsTArray &out); + + plClothingItem *GenerateClothingItem(plClothingMtl *mtl, const plLocation &loc); + hsGMaterial* AlphaHackVersion(plMaxNode* node, Mtl* mtl, int subIndex); // used by DynamicDecals + hsGMaterial* NonAlphaHackVersion(plMaxNode* node, Mtl* mtl, int subIndex); + hsGMaterial* AlphaHackPrint(plMaxNode* node, Texmap* baseTex, UInt32 blendFlags); + hsGMaterial* NonAlphaHackPrint(plMaxNode* node, Texmap* baseTex, UInt32 blendFlags); + + plMipmap* GetStaticColorTexture(Color c, plLocation &loc); // Creates a 4x4 texture of the specified solid color; + +enum { + kColorRedBlack = 0x1, + kColorRedGrey = 0x2, + kColorRedWhite = 0x4, + kColorRed = kColorRedBlack | kColorRedGrey | kColorRedWhite, + + kColorGreenBlack = 0x8, + kColorGreenGrey = 0x10, + kColorGreenWhite = 0x20, + kColorGreen = kColorGreenBlack | kColorGreenGrey | kColorGreenWhite, + + kColorBlueBlack = 0x40, + kColorBlueGrey = 0x80, + kColorBlueWhite = 0x100, + kColorBlue = kColorBlueBlack | kColorBlueGrey | kColorBlueWhite, + + kColor = kColorRed | kColorGreen | kColorBlue, + + kIllumRedBlack = 0x200, + kIllumRedGrey = 0x400, + kIllumRedWhite = 0x800, + kIllumRed = kIllumRedBlack | kIllumRedGrey | kIllumRedWhite, + + kIllumGreenBlack = 0x1000, + kIllumGreenGrey = 0x2000, + kIllumGreenWhite = 0x4000, + kIllumGreen = kIllumGreenBlack | kIllumGreenGrey | kIllumGreenWhite, + + kIllumBlueBlack = 0x8000, + kIllumBlueGrey = 0x10000, + kIllumBlueWhite = 0x20000, + kIllumBlue = kIllumBlueBlack | kIllumBlueGrey | kIllumBlueWhite, + + kIllum = kIllumRed | kIllumGreen | kIllumBlue, + + kAlphaBlack = 0x40000, + kAlphaGrey = 0x80000, + kAlphaWhite = 0x100000, + kAlpha = kAlphaBlack | kAlphaGrey | kAlphaWhite, + + kAllChannels = kColor | kIllum | kAlpha // Adjust if more channels added. +}; + + // All this to catch duplicate mats with same name. Sigh. + struct DoneMaterialData + { + DoneMaterialData() : fHsMaterial(nil), fMaxMaterial(nil), fNode(nil), + fSubMultiMat(false), fOwnedCopy(false) { } + + hsGMaterial *fHsMaterial; + Mtl *fMaxMaterial; + plMaxNode *fNode; + hsBool fSubMultiMat; + hsBool fOwnedCopy; + hsBool fRuntimeLit; + UInt32 fSubMtlFlags; + int fNumUVChannels; + hsBool fMakeAlphaLayer; + }; + +private: + enum { + kWarnedNoMoreDub = 0x1, + kWarnedNoMoreMult = 0x2, + kWarnedNoMoreBitmapLoadErr = 0x4, + kWarnedSubMulti = 0x8, + kWarnedCompMtlBadBlend = 0x10, + kWarnedNoLayers = 0x20, + kWarnedTooManyUVs = 0x40, + kWarnedAlphaAddCombo = 0x80, + kWarnedNoBaseTexture = 0x100, + kWarnedNoUpperTexture = 0x200, + kWarnedUpperTextureMissing = 0x400, + kWarnedMissingClothingTexture = 0x800, + kWarnedBadAnimSDLVarName = 0x1000, + }; + + DoneMaterialData* IFindDoneMaterial(DoneMaterialData& done); + hsBool IClearDoneMaterial(Mtl* mtl, plMaxNode* node); + + hsGMaterial *IAddDefaultMaterial(plMaxNode *node); + plMipmap *IGetUVTransTexture(plMaxNode *node, hsBool useU = true); + void IInsertSingleBlendLayer(plMipmap *texture, hsGMaterial *mat, plMaxNode *node, + int layerIdx, int UVChan); + + hsGMaterial *ICreateMaterial(Mtl *mtl, plMaxNode *node, const char *name, int subIndex, int numUVChannels, hsBool makeAlphaLayer); + hsGMaterial *IProcessMaterial(Mtl *mtl, plMaxNode *node, const char *name, int UVChan, int subMtlFlags = 0); + + // ... calls one of: + hsGMaterial *IProcessMultipassMtl(Mtl *mtl, plMaxNode *node, const char *name, int UVChan); + hsGMaterial *IProcessCompositeMtl(Mtl *mtl, plMaxNode *node, const char *name, int UVChan, int subMtlFlags); + hsGMaterial *IProcessParticleMtl(Mtl *mtl, plMaxNode *node, const char *name); + hsBool IProcessPlasmaMaterial(Mtl *mtl, plMaxNode *node, hsGMaterial *mat, const char* namePrefix); + + hsGMaterial* IInsertDoneMaterial(Mtl *mtl, hsGMaterial *hMat, plMaxNode *node, hsBool isMultiMat, + hsBool forceCopy, hsBool runtimeLit, UInt32 subMtlFlags, int numUVChannels, hsBool makeAlphaLayer); + + void IInsertBumpLayers(plMaxNode* node, hsGMaterial* mat, int bumpLayerIdx); + void IInsertBumpLayers(plMaxNode* node, hsGMaterial* mat); + plLayer* IMakeBumpLayer(plMaxNode* node, const char* nameBase, hsGMaterial* mat, UInt32 miscFlag); + plMipmap* IGetBumpLutTexture(plMaxNode* node); + + hsBool IHasSubMtl(Mtl* base, Mtl* sub); + int IFindSubIndex(plMaxNode* node, Mtl* mtl); + + // NOTE: each insert function potentially modifies the layers, + // so make sure you own the material copy before calling these + void IInsertAlphaBlendingLayers(Mtl *mtl, plMaxNode *node, hsGMaterial *mat, int UVChan, + hsBool makeAlphaLayer); + void IInsertMultipassBlendingLayers(Mtl *mtl, plMaxNode *node, hsGMaterial *mat, int UVChan, + hsBool makeAlphaLayer); + void IInsertCompBlendingLayers(Mtl *mtl, plMaxNode *node, hsGMaterial *mat, int subMtlFlags, + int UVChan, hsBool makeAlphaLayer); + + + void IAddLayerToMaterial(hsGMaterial *mat, plLayerInterface *layer); +#if 0 // Out for now... + void IInitAttrSurface(hsGLayer *hLay, StdMat *stdMtl, plMaxNode *node); + void IInitAttrTexture(plMaxNode *node, Mtl *mtl, hsGLayer* hLay, Texmap *texMap, char *nodeName); + void IInitAttrLayer(hsGLayer* hLay, Mtl *mtl, Texmap* layer, plMaxNode* node); +#endif + // ... and so forth + hsBool IUVGenHasDynamicScale(plMaxNode* node, StdUVGen *uvGen); + void IScaleLayerOpacity(plLayer* hLay, hsScalar scale); + + hsGMaterial *ICheckForProjectedTexture(plMaxNode *node); + hsGMaterial *IWrapTextureInMaterial(Texmap *texMap, plMaxNode *node); + + BMM_Color_64 ICubeSample(Bitmap *bitmap[6], double phi, double theta); + void IBuildSphereMap(Bitmap *bitmap[6], Bitmap *bm); +#if 0 // DEFER_ANIM_MAT + void IProcessAnimMaterial(BitmapTex *bitmapTex, hsGAnimLayer* at, UInt32 texFlags, UInt32 procFlags); +#endif // DEFER_ANIM_MAT + static hsBool ITextureTransformIsAnimated(Texmap *texmap); + static hsBool IHasAnimatedControllers(Animatable* anim); + static hsBool IIsAnimatedTexmap(Texmap* texmap); + + static UInt32 ICheckPoints(const Point3& p0, const Point3& p1, const Point3& p2, const Point3& p3, + int chan, + UInt32 mBlack, UInt32 mGrey, UInt32 mWhite); + static UInt32 ICheckPoints(const Point3& p0, const Point3& p1, const Point3& p2, + int chan, + UInt32 mBlack, UInt32 mGrey, UInt32 mWhite); + + void IAppendWetLayer(plMaxNode* node, hsGMaterial* mat); + static plBitmap* IGetFunkyRamp(plMaxNode* node, UInt32 funkyType); + static void IAppendFunkyLayer(plMaxNode* node, Texmap* texMap, hsGMaterial* mat); + static hsBool IHasFunkyOpacity(plMaxNode* node, Texmap* texMap); + static UInt32 IGetFunkyType(Texmap* texMap); + static UInt32 IGetOpacityRanges(plMaxNode* node, Texmap* texMap, hsScalar& tr0, hsScalar& op0, hsScalar& op1, hsScalar& tr1); + + Interface *fInterface; + hsConverterUtils& fConverterUtils; + hsBool fSave; + plErrorMsg *fErrorMsg; + + Int32 fSubIndex; + hsBool fChangedTimes; + + char *fNodeName; + UInt32 fWarned; + + + DoneMaterialData fLastMaterial; + hsTArray fDoneMaterials; + + hsBool IsMatchingDoneMaterial(DoneMaterialData *dmd, + Mtl *mtl, hsBool isMultiMat, UInt32 subMtlFlags, hsBool forceCopy, hsBool runtimeLit, + plMaxNode *node, int numUVChannels, hsBool makeAlphaLayer); + + void ISortDoneMaterials(hsTArray& doneMats); + hsBool IEquivalent(DoneMaterialData* one, DoneMaterialData* two); + void IPrintDoneMat(hsStream* stream, const char* prefix, DoneMaterialData* doneMat); + void IPrintDoneMaterials(const char* path, hsTArray& doneMats); + void IGenMaterialReport(const char* path); + +public: + // Apologies all around, but I need this list for dumping some export warnings. mf + const hsTArray& DoneMaterials() { return fDoneMaterials; } + + //hsBool CheckValidityOfSDLVarAnim(plPassMtlBase *mtl, char *varName, plMaxNode *node); +}; + +extern hsMaterialConverter gMaterialConverter; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaxLayerBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaxLayerBase.h new file mode 100644 index 00000000..2fba1b7d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsMaxLayerBase.h @@ -0,0 +1,188 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __HSMAXLAYERBASE_H +#define __HSMAXLAYERBASE_H + +#include "stdmat.h" + +#define HSMAX_LAYER_CLASS_ID 0x41990fe7 + +const Class_ID hsMaxLayerClassID(HSMAX_LAYER_CLASS_ID, 0x72404998); +const Class_ID hsMaxMtlClassID(0x2f335902, 0x111d2ea7); +const Class_ID hsEnvironMapMtlClassID(0x98777b3, 0x5eb270dd); + +class hsMaxLayerBase : public BitmapTex { +public: + enum hsMatBlendFlags { + kBlendTest = 0x1, // dev + // Rest of blends are mutually exclusive + kBlendAlpha = 0x2, // dev + kBlendMult = 0x4, // dev + kBlendAdd = 0x8, // dev + kBlendAddColorTimesAlpha = 0x10, // dev + kBlendAntiAlias = 0x20, + kBlendDetail = 0x40, + kBlendDetailAdd = 0x80, + kBlendMask = kBlendAlpha + | kBlendMult + | kBlendAdd + | kBlendAddColorTimesAlpha + | kBlendAntiAlias + | kBlendDetail + | kBlendDetailAdd, + kBlendInvertAlpha = 0x1000, // dev + kBlendInvertColor = 0x2000, // dev + kBlendAlphaMult = 0x4000, + kBlendAlphaAdd = 0x8000, + kBlendNoColor = 0x10000, + kBlendNoVtxAlpha = 0x20000 + }; + + enum hsMatZFlags { + kZIncLayer = 0x1, // dev + kZOnlyZ = 0x2, // dev + kZClearZ = 0x4, // dev + kZNoZRead = 0x8, // dev + kZNoZWrite = 0x10, + kZMask = kZNoZWrite | kZClearZ | kZNoZRead, + kZLODBias = 0x20 + }; + + enum hsMatShadeFlags { + kShadeSoftShadow = 0x1, // view, dev + kShadeNoProjectors = 0x2, // projector + kShadeVertexShade = 0x20, // dev + kShadeNoShade = 0x40, // view,dev + kShadeBlack = kShadeNoShade, + kShadeSpecular = 0x80, // view, dev + kShadeNoFog = 0x100, // dev + kShadeWhite = 0x200, + kShadeSpecularAlpha = 0x400, + kShadeSpecularColor = 0x800, + kShadeSpecularHighlight = 0x1000, + kShadeVertColShade = 0x2000, + kShadeInherit = 0x4000 + }; + + enum hsMatMiscFlags { + kMiscWireFrame = 0x1, // dev (running out of bits) + kMiscDrawMeshOutlines = 0x2, // dev, currently unimplemented + kMiscTwoSided = 0x4, // view,dev + kMiscDrawAsSplats = 0x8, // dev? bwt + kMiscMipMap = 0x10, + kMiscUseBitmap = 0x20, + kMiscIntensityOnly = 0x40, + kMiscAutoStart = 0x80, + kMiscDetailBias = 0x100, // obsolete... + kMiscDetailMax = 0x200, // obsolete... + kMiscExplicitMipmap = 0x400, + kMiscAdjustPlane = 0x800, + kMiscAdjustCylinder = 0x1000, + kMiscAdjustSphere = 0x2000, + kMiscTroubledLoner = 0x4000, + kMiscBindSkip = 0x8000, + kMiscBindMask = 0x10000, + kMiscForceNonCompressed = 0x20000, + kMiscNoMaxSize = 0x40000, + kMiscHalfSize = 0x80000, + kMiscBindNext = 0x100000, + kMiscBindPrev = 0x200000, + kMiscReserved = 0x400000 + }; + + enum ProcType { + kProcTypeDefault, + kProcTypeWater + }; + + enum hsMatUsage { + kUseNone = 0x0, + kUseBase = 0x1, + kUseDetail = 0x2, + kUseGrime = 0x4, + kUseTransition = 0x8, + kUseHighlight = 0x10, + kUseMask = 0x20, + kUseShadowLight = 0x40, + kUseHelper = 0x80, + + kUseGuess = 0x10000000 + }; + +public: + // For hsMaxMtl... Special case for higher layers. Sigh. + virtual void SetDirty(BOOL state) = 0; + virtual void SetBlendFlag(int i, BOOL state) = 0; + virtual void SetZFlag(int flag, BOOL state) = 0; + virtual void SetShadeFlag(int flag, BOOL state) = 0; + virtual void SetMiscFlag(int flag, BOOL state) = 0; + virtual void SetProcType(ProcType type) = 0; + virtual void SetUsage(hsMatUsage use) = 0; + virtual void GuessUsage() = 0; + + // For interactive renderer + virtual Color GetAmbient(int mtlNum=0, BOOL backFace=FALSE) = 0; + virtual Color GetColor(int mtlNum=0, BOOL backFace=FALSE) = 0; + + virtual float GetShininess(int mtlNum=0, BOOL backFace=FALSE) = 0; + virtual float GetShinStr(int mtlNum=0, BOOL backFace=FALSE) = 0; + virtual float GetOpacity(int mtlNum=0, BOOL backFace=FALSE) = 0; + + // For exporter + virtual Color GetAmbient(TimeValue t) const = 0; + virtual Color GetColor(TimeValue t) const = 0; + + virtual float GetShininess(TimeValue t) const = 0; + virtual float GetShinStr(TimeValue t) const = 0; + virtual float GetMapPercent(TimeValue t) const = 0; + virtual float GetOpacity(TimeValue t) const = 0; + virtual float GetMipMapBlur(TimeValue t) const = 0; + virtual float GetLODBias(TimeValue t) const = 0; + virtual float GetDetailDropoffStart(TimeValue t) const = 0; + virtual float GetDetailDropoffStop(TimeValue t) const = 0; + virtual float GetDetailMax(TimeValue t) const = 0; + virtual float GetDetailMin(TimeValue t) const = 0; + virtual int GetEnvironMapSize(TimeValue t) const = 0; + + virtual BOOL GetDirty() const = 0; + virtual ULONG GetBlendFlags() const = 0; + virtual ULONG GetZFlags() const = 0; + virtual ULONG GetShadeFlags() const = 0; + virtual ULONG GetMiscFlags() const = 0; + virtual ProcType GetProcType() const = 0; + virtual hsMatUsage GetUsage() const = 0; + + virtual int GetNumExplicitMipmaps() const = 0; + virtual TCHAR *GetExplicitMipmapName(int i) const = 0; + virtual BOOL ExplicitMipmapEnabled(int i) const = 0; + virtual int GetExplicitMipmapLevel(int i) const = 0; + + // KLUDGE - Had to do this to compile under MAX4 beta + virtual void fnReload() {}; + virtual void fnViewImage() {}; +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsVertexShader.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsVertexShader.cpp new file mode 100644 index 00000000..5d1f19bb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsVertexShader.cpp @@ -0,0 +1,441 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// hsVertexShader Class Functions // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// 5.9.2001 mcn - Updated to reflect the new (temporary) vertex color/ // +// lighting model. // +// // +////////////////////////////////////////////////////////////////////////////// + + +#include "HeadSpin.h" + +#include "Max.h" +#include "stdmat.h" +#include "istdplug.h" +#include "dummy.h" +#include "notetrck.h" + +#include "../MaxMain/plMaxNode.h" +#include "hsBitVector.h" + +#include "hsMatrix44.h" +#include "hsTemplates.h" +#include "../plSurface/hsGMaterial.h" + +#include "UserPropMgr.h" +#include "hsMaxLayerBase.h" +#include "hsVertexShader.h" + +#include "hsConverterUtils.h" +#include "hsControlConverter.h" +#include "hsExceptionStack.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" + +#include "../plDrawable/plGeometrySpan.h" + +#include "plMaxLightContext.h" +#include "plRenderGlobalContext.h" +#include "plLightMapGen.h" + +#define MF_NATIVE_MAX_LIGHT +#define MF_NO_RAY_SHADOW + +extern UserPropMgr gUserPropMgr; +extern TimeValue GetTime(Interface *gi); + +//=========================================================================== +// hsVertexShader +//=========================================================================== + +hsVertexShader::hsVertexShader() : + fConverterUtils(hsConverterUtils::Instance()), + fInterface(nil), + fLightMapGen(nil), + fShaded(0) +{ + hsGuardBegin("hsVertexShader::hsVertexShader"); + fLocalToWorld.Reset(); + hsGuardEnd; +} + +hsVertexShader::~hsVertexShader() +{ + hsGuardBegin("hsVertexShader::~hsVertexShader"); + hsGuardEnd; +} + +hsVertexShader& hsVertexShader::Instance() +{ + hsGuardBegin("hsVertexShader::Instance"); + static hsVertexShader instance; + return instance; + hsGuardEnd; +} + +void hsVertexShader::Open() +{ + hsGuardBegin("hsVertexShader::InitLights"); + + fLocalToWorld.Reset(); + + fInterface = ::GetCOREInterface(); + + fLightMapGen = &plLightMapGen::Instance(); + + hsGuardEnd; +} + +void hsVertexShader::Close() +{ + hsGuardBegin("hsVertexShader::DeInitLights"); + + fLightMapGen = nil; + + hsGuardEnd; +} + +//// ShadeNode /////////////////////////////////////////////////////////////// +// Same as the other ShadeNode, only this shades an array of plGeometrySpans. + +void hsVertexShader::ShadeNode(INode* node, hsMatrix44& l2w, hsMatrix44& w2l, hsTArray &spans) +{ + // If we're flagged for WaterColor, our vertex colors are already done. + if( ((plMaxNodeBase*)node)->GetCalcEdgeLens() || node->UserPropExists("XXXWaterColor") ) + return; + + fLightMapGen->InitNode(node); + + fLocalToWorld = l2w; + hsMatrix44 tempMatrix = w2l; // l2w's inverse + tempMatrix.GetTranspose( &fNormalToWorld ); // Inverse-transpose of the fLocalToWorld matrix, + + int i; + for( i = 0; i < spans.GetCount(); i++ ) + IShadeSpan( spans[ i ], node); + + fLightMapGen->DeInitNode(); + + fShaded++; +} + +//// IShadeSpan ////////////////////////////////////////////////////////////// +// Shades a single plGeometrySpan. +// 5.9.2001 mcn - Updated to support the new (temporary) vertex color/lighting +// method. + +void hsVertexShader::IShadeSpan( plGeometrySpan *span, INode* node ) +{ + hsColorRGBA preDiffuse, rtDiffuse, matAmbient; + hsBitVector dirtyVector; + int i; + hsBool translucent, shadeIt, addingIt; + plLayerInterface *layer = nil; + + + hsGuardBegin("hsVertexShader::ShadeSpan"); + + const char* dbgNodeName = node->GetName(); + + if( span->fNumVerts == 0 ) + return; + + fShadeColorTable = TRACKED_NEW hsColorRGBA[ span->fNumVerts ]; + fIllumColorTable = TRACKED_NEW hsColorRGBA[ span->fNumVerts ]; + + translucent = IsTranslucent( span->fMaterial ); + + /// Get material layer #0 + addingIt = false; + shadeIt = !( span->fProps & plGeometrySpan::kPropNoPreShade ); + + if( span->fMaterial->GetNumLayers() != 0 ) + { + layer = span->fMaterial->GetLayer( 0 ); + if( layer->GetShadeFlags() & hsGMatState::kShadeNoShade ) + shadeIt = false; + if( layer->GetBlendFlags() & hsGMatState::kBlendAdd ) + addingIt = true; + } + float opacity = 1.f; + for( i = 0; i < span->fMaterial->GetNumLayers(); i++ ) + { + plLayerInterface* lay = span->fMaterial->GetLayer(i); + if( (lay->GetBlendFlags() & hsGMatState::kBlendAlpha) + && + ( + !i + || + (lay->GetMiscFlags() & hsGMatState::kMiscRestartPassHere) + ) + ) + { + opacity = span->fMaterial->GetLayer(i)->GetOpacity(); + } + } + + /// Generate color table + if( shadeIt ) + IShadeVertices( span, &dirtyVector, node, translucent ); + else + { + for( i = 0; i < span->fNumVerts; i++ ) + { + /// This is good for the old way, but not sure about the new way. Test once new way is in again -mcn +// fShadeColorTable[ i ].Set( 1, 1, 1, 1 ); +// fIllumColorTable[ i ].Set( 0, 0, 0, 1 ); + hsPoint3 position; + hsVector3 normal; + hsColorRGBA color, illum; + + span->ExtractVertex( i, &position, &normal, &color, &illum ); + span->ExtractInitColor( i, &color, &illum ); + fShadeColorTable[ i ].Set( color.r, color.g, color.b, color.a ); + fIllumColorTable[ i ].Set( illum.r, illum.g, illum.b, 1 ); + } + } + + /// Get mat colors to modulate by + if( layer == nil ) + { + preDiffuse.Set( 1, 1, 1, 1 ); + rtDiffuse.Set( 1, 1, 1, 1 ); + matAmbient.Set( 0, 0, 0, 0 ); + } + else + { + if( layer->GetShadeFlags() & hsGMatState::kShadeWhite ) + { + preDiffuse.Set( 1, 1, 1, 1 ); + rtDiffuse.Set( 1, 1, 1, 1 ); + matAmbient.Set( 0, 0, 0, 0 ); + } + else + { + preDiffuse = layer->GetPreshadeColor(); // This is for vertex-based lighting, which basically ignores preshading + rtDiffuse = layer->GetRuntimeColor(); // This is for vertex-based lighting, which basically ignores preshading + matAmbient = layer->GetAmbientColor(); + matAmbient.a = 0; + } + preDiffuse.a = opacity; + rtDiffuse.a = opacity; + } +#if 0 + + /// Multiply by the material color, and scale by opacity if we're additive blending + /// Apply colors now, multiplying by the material color as we go + for( i = 0; i < span->fNumVerts; i++ ) + { + fShadeColorTable[ i ] *= matDiffuse; + fShadeColorTable[ i ] += matAmbient; + fIllumColorTable[ i ] *= matDiffuse; + fIllumColorTable[ i ] += matAmbient; + } + + if( addingIt ) + { + for( i = 0; i < span->fNumVerts; i++ ) + { + float opacity = fShadeColorTable[ i ].a; + fShadeColorTable[ i ] *= opacity; + fIllumColorTable[ i ] *= opacity; + } + } +#else + /// Combine shade and illum together into the diffuse color + if( ( span->fProps & plGeometrySpan::kLiteMask ) != plGeometrySpan::kLiteMaterial ) + { + /// The two vertex lighting formulas take in a vetex color pre-processed, i.e. in + /// the form of: vtxColor = ( maxVtxColor * materialDiffuse + maxIllumColor ) + span->fProps |= plGeometrySpan::kDiffuseFoldedIn; + if( !shadeIt ) + { + for( i = 0; i < span->fNumVerts; i++ ) + { + fIllumColorTable[ i ].a = 0; + fShadeColorTable[ i ] = (fShadeColorTable[ i ] * rtDiffuse) + fIllumColorTable[ i ]; + fIllumColorTable[ i ].Set( 0, 0, 0, 0 ); + } + } + else + { + for( i = 0; i < span->fNumVerts; i++ ) + { + fIllumColorTable[ i ].a = 1.f; + // Following needs to be changed to allow user input vertex colors to modulate + // the runtime light values. +// fShadeColorTable[ i ] = fIllumColorTable[ i ] * rtDiffuse; + fShadeColorTable[ i ] = fShadeColorTable[ i ] * fIllumColorTable[ i ] * rtDiffuse; + fIllumColorTable[ i ].Set( 0, 0, 0, 0 ); + } + } + } + else + { + if( !shadeIt ) + { + // Not shaded, so runtime lit, so we want BLACK vertex colors + for( i = 0; i < span->fNumVerts; i++ ) + { + fShadeColorTable[ i ].Set( 0, 0, 0, 0 ); + fIllumColorTable[ i ].Set( 0, 0, 0, 0 ); + } + } + else + { + for( i = 0; i < span->fNumVerts; i++ ) + { + fShadeColorTable[ i ] *= fIllumColorTable[ i ]; + fIllumColorTable[ i ].Set( 0, 0, 0, 0 ); + } + } + } +#endif + + /// Loop and stuff + for( i = 0; i < span->fNumVerts; i++ ) + span->StuffVertex( i, fShadeColorTable + i, fIllumColorTable + i ); + + delete [] fShadeColorTable; + delete [] fIllumColorTable; + + hsGuardEnd; +} + +//// IShadeVertices ////////////////////////////////////////////////////////// +// Shades an array of vertices from a plGeometrySpan. +// 5.9.2001 mcn - Updated for the new lighting model. Now on runtime, we +// want the following properties on each vertex: +// diffuseColor = vertexColor * matDiffuse + matAmbient (including alpha) +// specularColor = ( illumniation + pre-shading ) * matDiffuse + matAmbient +// We do the mat modulation outside of this function, so we +// just gotta make sure the two arrays get the right values. + +void hsVertexShader::IShadeVertices( plGeometrySpan *span, hsBitVector *dirtyVector, INode* node, hsBool translucent ) +{ + hsGuardBegin( "hsVertexShader::IShadeVertices" ); + + plMaxNode* maxNode = (plMaxNode*)node; + if( maxNode->CanConvert() && (nil != maxNode->GetLightMapComponent()) ) + return; + + int index; + + hsPoint3 position; + hsVector3 normal; + hsColorRGBA color, illum; + plTmpVertex3 *vertices; + + + /// Allocate temp vertex array + vertices = TRACKED_NEW plTmpVertex3[ span->fNumVerts ]; + for( index = 0; index < span->fNumVerts; index++ ) + { + span->ExtractVertex( index, &position, &normal, &color, &illum ); + span->ExtractInitColor( index, &color, &illum ); + + /// fShadeColorTable is the shaded portion. fIllumColorTable is the illuminated portion; + /// for more and less confusing details, see above. + fShadeColorTable[ index ].Set( color.r, color.g, color.b, color.a ); + fIllumColorTable[ index ].Set( illum.r, illum.g, illum.b, 1 ); + + position = fLocalToWorld * position; + normal = fNormalToWorld * normal; + + vertices[ index ].fLocalPos = position; + vertices[ index ].fNormal = normal; + vertices[ index ].fNormal.Normalize(); + } + + const char* dbgNodeName = node->GetName(); + + TimeValue t = fInterface->GetTime(); + Box3 bbox; + node->EvalWorldState(t).obj->GetDeformBBox(t, bbox, &node->GetObjectTM(t)); + plMaxLightContext ctx(bbox, t); + + for( index = 0; index < span->fNumVerts; index++ ) + INativeShadeVtx(fIllumColorTable[index], ctx, vertices[ index ], translucent); + + // Delete temp arrays + delete [] vertices; + + hsGuardEnd; +} + +void hsVertexShader::INativeShadeVtx(hsColorRGBA& shade, plMaxLightContext& ctx, const plTmpVertex3& vtx, hsBool translucent) +{ + ctx.SetPoint(vtx.fLocalPos, vtx.fNormal); + + Color color = fLightMapGen->ShadePoint(ctx); + shade.r += color.r; + shade.g += color.g; + shade.b += color.b; + + // To handle two-sided translucency here, we should compute the shade on each side and sum them. + if( translucent ) + { + ctx.SetPoint(vtx.fLocalPos, -vtx.fNormal); + color = fLightMapGen->ShadePoint(ctx); + shade.r += color.r; + shade.g += color.g; + shade.b += color.b; + } +} + +void hsVertexShader::INativeShadowVtx(hsColorRGBA& shade, plMaxLightContext& ctx, const plTmpVertex3& vtx, hsBool translucent) +{ + ctx.SetPoint(vtx.fLocalPos, vtx.fNormal); + + Color color = fLightMapGen->ShadowPoint(ctx); + shade.r += color.r; + shade.g += color.g; + shade.b += color.b; +} + +hsBool hsVertexShader::IsTranslucent( hsGMaterial *material ) +{ + hsGuardBegin("hsVertexShader::IsTranslucent"); + + if( material ) + { + plLayerInterface* layer = material->GetLayer(0); + if( layer && ( layer->GetShadeFlags() & hsGMatState::kShadeSoftShadow ) ) + { + return true; + } + } + + return false; + hsGuardEnd; +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsVertexShader.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsVertexShader.h new file mode 100644 index 00000000..e93246b0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/hsVertexShader.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __HSVERTEXSHADER_H +#define __HSVERTEXSHADER_H + +class hsConverterUtils; +class hsBitVector; + +class plMaxLightContext; +class plGeometrySpan; +struct hsColorRGBA; +class plLightMapGen; + +class hsVertexShader +{ +private: + hsVertexShader(); +public: + virtual ~hsVertexShader(); + + static hsVertexShader& Instance(); + + void ShadeNode(INode* node, hsMatrix44& l2w, hsMatrix44& w2l, hsTArray &spans); + + void Open(); + void Close(); + +private: + + /// Temporary vertex class + class plTmpVertex3 + { + public: + hsPoint3 fLocalPos; + hsVector3 fNormal; + }; + + hsBool ILightIncludesNode(LightObject* light, INode* node); + + void INativeShadeVtx(hsColorRGBA& shade, plMaxLightContext& ctx, const plTmpVertex3& vtx, hsBool translucent); + void INativeShadowVtx(hsColorRGBA& shade, plMaxLightContext& ctx, const plTmpVertex3& vtx, hsBool translucent); + + hsBool IsTranslucent( hsGMaterial *material ); + + void IShadeSpan( plGeometrySpan *span, INode* node ); + void IShadeVertices( plGeometrySpan *span, hsBitVector *dirtyVector, INode* node, hsBool translucent ); + +private: + Interface *fInterface; + hsConverterUtils &fConverterUtils; + + plLightMapGen* fLightMapGen; + + hsMatrix44 fLocalToWorld, fNormalToWorld; // fN2W is inv-transpose of fL2W + + int fShaded; // just record-keeping + + hsColorRGBA *fShadeColorTable; + hsColorRGBA *fIllumColorTable; +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plBitmapCreator.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plBitmapCreator.cpp new file mode 100644 index 00000000..31b9ad63 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plBitmapCreator.cpp @@ -0,0 +1,684 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "max.h" +#include +#include "bmmlib.h" + +#include "../../Plasma/PubUtilLib/plGImage/plMipmap.h" +#include "hsExceptionStack.h" +#include "../../Plasma/PubUtilLib/plGImage/hsCodecManager.h" + +#include "plBitmapCreator.h" + +#include "../MaxMain/plPluginResManager.h" +#include "../MaxExport/plErrorMsg.h" +#include "../MaxPlasmaMtls/Layers/plStaticEnvLayer.h" + +#include "../plGImage/plMipmap.h" +#include "../plGImage/plDynamicTextMap.h" +#include "../plGImage/plCubicEnvironmap.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plUoid.h" +#include "../plResMgr/plRegistryHelpers.h" +#include "../plResMgr/plLocalization.h" +#include "../plAgeDescription/plAgeDescription.h" + +//// plCommonBitmapLib /////////////////////////////////////////////////////// +// Derived class for our textures, since they all go in a common page +// (namely, "Textures") + +#include "../MaxMain/plCommonObjLib.h" + +class plCommonBitmapLib : public plCommonObjLib +{ + public: + virtual hsBool IsInteresting( const plKey &objectKey ) + { + if( objectKey->GetUoid().GetClassType() == plCubicEnvironmap::Index() || + objectKey->GetUoid().GetClassType() == plMipmap::Index() ) + { + return true; + } + return false; + } +}; + +static plCommonBitmapLib sCommonBitmapLib; + + +plBitmapCreator::plBitmapCreator() +{ + fErrorMsg = nil; +} +plBitmapCreator::~plBitmapCreator() +{ +} + +plBitmapCreator &plBitmapCreator::Instance() +{ + static plBitmapCreator fInstance; + return fInstance; +} + +void plBitmapCreator::Init( hsBool save, plErrorMsg *msg ) +{ + fErrorMsg = msg; +} + +void plBitmapCreator::DeInit( void ) +{ + CleanUpMaps(); +} + +void plBitmapCreator::CleanUpMaps( void ) +{ + sCommonBitmapLib.ClearObjectList(); +} + +void plBitmapCreator::DeleteExportedBitmap( const plKey &constKey ) +{ + plKey key = constKey; + sCommonBitmapLib.RemoveObjectAndKey( key ); +} + +// +// Create Bitmap +// +plMipmap *plBitmapCreator::ICreateBitmap(plBitmapData *bd) +{ + hsGuardBegin("hsConverterUtils::CreateBitmap"); + + // Load the bitmap + BitmapInfo bi; + bi.SetName(bd->fileName); + +#if 0 // This isn't really an issue since the textures are packed -Colin + const int kMaxFileNameLength = 30; + if (strlen(bi.Filename()) > kMaxFileNameLength) + { + // Allow to continue, But make it painful + char errStr[256]; + sprintf(errStr, "File name longer than %d, won't burn to CD (%s)", kMaxFileNameLength, bi.Filename());//bitmapTex->GetName()); + MessageBox(GetActiveWindow(), errStr, bd->fileName, MB_OK|MB_ICONEXCLAMATION); + } +#endif + + hsBool notMipped = (bd->texFlags & plMipmap::kForceOneMipLevel) != 0; + float sigma = bd->sig; + + // Load the bitmap + Bitmap *bm = TheManager->Load(&bi); + if (!bm) + { + // FIXME + /* + if (fErrorMsg->Set(!(fWarned & kWarnedNoMoreBitmapLoadErr), + "Error loading bitmap", pathName).CheckAskOrCancel()) + { + fWarned |= kWarnedNoMoreBitmapLoadErr; + } + */ + return nil; + } + BitmapStorage *storage = bm->Storage(); + BitmapInfo *bInfo = &storage->bi; + + ICheckOutBitmap(bInfo, bm, bd->fileName); + + // + // Create a plMipmap + // + plMipmap *hBitmap = TRACKED_NEW plMipmap; + if( (bm->Width() ^ (bm->Width() & -bm->Width())) + ||(bm->Height() ^ (bm->Height() & -bm->Height())) ) + { + IResampBitmap(bm, *hBitmap); + } + else if( ((bm->Width() >> 3) > bm->Height())||((bm->Height() >> 3) > bm->Width()) ) + { + IResampBitmap(bm, *hBitmap); + } + else + { + ICopyBitmap(bm, *hBitmap); + } + bm->DeleteThis(); + + if( bd->invertAlpha ) + IInvertAlpha(*hBitmap); + + // Do it + plMipmap *hMipmap = nil; + if (sigma > 0.f) + { + hMipmap = TRACKED_NEW plMipmap(hBitmap, sigma, bd->createFlags, bd->detailDropoffStart, + bd->detailDropoffStop, bd->detailMax, bd->detailMin); + } + else + { + hMipmap = TRACKED_NEW plMipmap(hBitmap, -1.f, bd->createFlags, bd->detailDropoffStart, + bd->detailDropoffStop, bd->detailMax, bd->detailMin); + } + delete hBitmap; + + /// Clamp the border if we're using clamping + if( bd->clampFlags != 0 ) + { + hMipmap->EnsureKonstantBorder( ( bd->clampFlags & plBitmapData::kClampU ) ? true : false, + ( bd->clampFlags & plBitmapData::kClampV ) ? true : false ); + } + + /// Cut this down to whatever size we were told to :) + if( bd->maxDimension != 0 ) + hMipmap->ClipToMaxSize( bd->maxDimension ); + + if( notMipped ) + { + // Done AFTER ClipToMaxSize() so we still get the export size specified + hMipmap->RemoveMipping(); + } + + hBitmap = hMipmap; + + UInt32 flagsToSet = 0; + + if( bd->texFlags & plMipmap::kNoMaxSize ) + flagsToSet |= plMipmap::kNoMaxSize; + if( bd->texFlags & plMipmap::kHalfSize ) + flagsToSet |= plMipmap::kHalfSize; + if( bd->texFlags & plMipmap::kDontThrowAwayImage ) + flagsToSet |= plMipmap::kDontThrowAwayImage; + + hBitmap->SetFlags( hBitmap->GetFlags() | flagsToSet ); + if (bd->useJPEG) + hBitmap->fCompressionType = plMipmap::kJPEGCompression; + + // FIXME + if (/*fSave &&*/ !(bd->texFlags & plMipmap::kForceNonCompressed) && !bd->useJPEG) + { + // Are we on? Check Plasma Util panel +// SwitchUtil *pu = (SwitchUtil *)CreateInstance(UTILITY_CLASS_ID, PlasmaUtilClassID); +// if (!pu || pu->TextureCompressionEnabled()) + { + plMipmap *compressed = hsCodecManager::Instance().CreateCompressedMipmap(plMipmap::kDirectXCompression, hBitmap); +// hsDXTSoftwareCodec::Instance().CreateCompressedBitmap(hBitmap); +// hsDXTDirectXCodec::Instance().CreateCompressedBitmap(hBitmap); + + if (compressed) + { + delete hBitmap; + hBitmap = compressed; + hBitmap->SetFlags( hBitmap->GetFlags() | flagsToSet ); + } + } + } + + return hBitmap; + hsGuardEnd; +} + + + + +// +// Verify that bitmap is the correct type/size +// +void plBitmapCreator::ICheckOutBitmap(BitmapInfo* bInfo, Bitmap* bm, const char *fileName) +{ + hsGuardBegin("hsConverterUtils::ICheckOutBitmap"); + + // Check out bitmap + if (bm->Flags() & MAP_FLIPPED) + MessageBox(GetActiveWindow(), "Bitmap is flipped horizontally", fileName, MB_OK); + if (bm->Flags() & MAP_INVERTED) + MessageBox(GetActiveWindow(), "Bitmap is inverted vertically", fileName, MB_OK); + + if (bInfo->Flags() & MAP_FLIPPED) + MessageBox(GetActiveWindow(), "BI:Bitmap is flipped horizontally", fileName, MB_OK); + if (bInfo->Flags() & MAP_INVERTED) + MessageBox(GetActiveWindow(), "BI:Bitmap is inverted vertically", fileName, MB_OK); + + hsGuardEnd; +} + +int plBitmapCreator::IResampBitmap(Bitmap *bm, plMipmap &hBitmap) +{ + hsGuardBegin("hsConverterUtils::IResampBitmap"); + + BitmapStorage *storage = bm->Storage(); + BitmapInfo *bInfo = &storage->bi; + + int dbgW = bm->Width(), dbgH = bm->Height(); + int it; + for( it = 1; it <= bm->Width(); it <<= 1 ); + it >>= 1; + hBitmap.fWidth = it; + for( it = 1; it <= bm->Height(); it <<= 1 ); + it >>= 1; + hBitmap.fHeight = it; + if( (hBitmap.fHeight >> 3) > hBitmap.fWidth ) + hBitmap.fHeight = hBitmap.fWidth << 3; + else + if( (hBitmap.fWidth >> 3) > hBitmap.fHeight ) + hBitmap.fWidth = hBitmap.fHeight << 3; + + hBitmap.fPixelSize = 32; + hBitmap.fRowBytes = hBitmap.fWidth * hBitmap.fPixelSize >> 3; + hBitmap.fNumLevels = 1; + + hBitmap.fImage = HSMemory::New(hBitmap.fRowBytes * hBitmap.fHeight); + +#ifdef COLOR_BLACK_WHITE + hBitmap.fFlags |= plMipmap::kColorWhite | plMipmap::kColorBlack; +#endif // COLOR_BLACK_WHITE + hBitmap.fFlags &= ~( plMipmap::kAlphaBitFlag | plMipmap::kAlphaChannelFlag ); + + int y,x; + float scaleY, scaleX; + hsRGBAColor32 *dstColor; + dstColor = (hsRGBAColor32*)hBitmap.fImage; + + scaleX = ((float)bm->Width())/(float)hBitmap.fWidth; + scaleY = ((float)bm->Height())/(float)hBitmap.fHeight; + for (y = 0; y < hBitmap.fHeight; y++) + { + for (x = 0; x < hBitmap.fWidth; x++) + { + BMM_Color_64 c64_00, c64_01, c64_10, c64_11; + int ix, iy; + float fracX, fracY; + float t; + t = x * scaleX; + ix = (int)t; + fracX = t-ix; + t = y * scaleY; + iy = (int)t; + fracY = t-iy; + + int ret = storage->GetPixels(ix,iy,1,&c64_00); +// FIXME +// fErrorMsg->Set(ret == 0, "ResampBitmap", "Failure getting pixels %dX%d", x, y).Check(); + ret = storage->GetPixels(ix+1,iy,1,&c64_10); + ret = storage->GetPixels(ix,iy+1,1,&c64_01); + ret = storage->GetPixels(ix+1,iy+1,1,&c64_11); + + dstColor->r = (unsigned char)(255.0 / 65535.0 + * ( c64_00.r * (1.f - fracX) * (1.f - fracY) + + c64_10.r * ( fracX) * (1.f - fracY) + + c64_01.r * (1.f - fracX) * ( fracY) + + c64_11.r * ( fracX) * ( fracY) )); + dstColor->g = (unsigned char)(255.0 / 65535.0 + * ( c64_00.g * (1.f - fracX) * (1.f - fracY) + + c64_10.g * ( fracX) * (1.f - fracY) + + c64_01.g * (1.f - fracX) * ( fracY) + + c64_11.g * ( fracX) * ( fracY) )); + dstColor->b = (unsigned char)(255.0 / 65535.0 + * ( c64_00.b * (1.f - fracX) * (1.f - fracY) + + c64_10.b * ( fracX) * (1.f - fracY) + + c64_01.b * (1.f - fracX) * ( fracY) + + c64_11.b * ( fracX) * ( fracY) )); + dstColor->a = (unsigned char)(255.0 / 65535.0 + * ( c64_00.a * (1.f - fracX) * (1.f - fracY) + + c64_10.a * ( fracX) * (1.f - fracY) + + c64_01.a * (1.f - fracX) * ( fracY) + + c64_11.a * ( fracX) * ( fracY) )); + + +#ifdef COLOR_BLACK_WHITE + if( dstColor->r | dstColor->g | dstColor->b ) + hBitmap.fFlags &= ~plMipmap::kColorBlack; + if( ~(dstColor->r & dstColor->g & dstColor->b) ) + hBitmap.fFlags &= ~plMipmap::kColorWhite; +#endif // COLOR_BLACK_WHITE + + if( dstColor->a < 255 ) + { + hBitmap.fFlags |= plMipmap::kAlphaBitFlag; + if( dstColor->a > 0 ) + hBitmap.fFlags |= plMipmap::kAlphaChannelFlag; + } + dstColor++; + } + } + if( hBitmap.fFlags & plMipmap::kAlphaChannelFlag ) + hBitmap.fFlags &= ~plMipmap::kAlphaBitFlag; + + return 0; + hsGuardEnd; +} + +int plBitmapCreator::ICopyBitmap(Bitmap *bm, plMipmap &hBitmap) +{ + hsGuardBegin("hsConverterUtils::ICopyBitmap"); + + BitmapStorage *storage = bm->Storage(); + BitmapInfo *bInfo = &storage->bi; + + hBitmap.fWidth = bm->Width(); + hBitmap.fHeight = bm->Height(); + hBitmap.fPixelSize = 32; + hBitmap.fRowBytes = bm->Width()*hBitmap.fPixelSize/8; + hBitmap.fNumLevels = 1; + + hBitmap.fImage = HSMemory::New(hBitmap.fRowBytes * hBitmap.fHeight); + +#ifdef COLOR_BLACK_WHITE + hBitmap.fFlags |= plMipmap::kColorWhite | plMipmap::kColorBlack; +#endif // COLOR_BLACK_WHITE + hBitmap.fFlags &= ~( plMipmap::kAlphaBitFlag | plMipmap::kAlphaChannelFlag ); + + int y,x; + hsRGBAColor32 *dstColor; + dstColor = (hsRGBAColor32*)hBitmap.fImage; + + for (y = 0; y < hBitmap.fHeight; y++) + { + for (x = 0; x < hBitmap.fWidth; x++) + { + BMM_Color_64 c64; + int ret = storage->GetPixels(x,y,1,&c64); +// FIXME +// fErrorMsg->Set(ret == 0, "CopyBitmap", "Failure getting pixels %dX%d", x, y).Check(); + + // Convert from 16 bits to 8 bits + dstColor->r = (char)(255.0*c64.r/65535.0); + dstColor->g = (char)(255.0*c64.g/65535.0); + dstColor->b = (char)(255.0*c64.b/65535.0); + dstColor->a = (char)(255.0*c64.a/65535.0); + +#ifdef COLOR_BLACK_WHITE + if( dstColor->r | dstColor->g | dstColor->b ) + hBitmap.fFlags &= ~plMipmap::kColorBlack; + if( ~(dstColor->r & dstColor->g & dstColor->b) ) + hBitmap.fFlags &= ~plMipmap::kColorWhite; +#endif // COLOR_BLACK_WHITE + + if( dstColor->a < 255 ) + { + hBitmap.fFlags |= plMipmap::kAlphaBitFlag; + if( dstColor->a > 0 ) + hBitmap.fFlags |= plMipmap::kAlphaChannelFlag; + } + dstColor++; + } + } + if( hBitmap.fFlags & plMipmap::kAlphaChannelFlag ) + hBitmap.fFlags &= ~plMipmap::kAlphaBitFlag; + + return 0; + hsGuardEnd; +} + +int plBitmapCreator::IInvertAlpha(plMipmap& hBitmap) +{ + hsGuardBegin("hsConverterUtils::ICopyBitmap"); + + hsAssert(hBitmap.fPixelSize == 32, "Only RGBA32 implemented"); + if( hBitmap.fPixelSize != 32 ) + return -1; + + hsRGBAColor32* dstColor = (hsRGBAColor32*)hBitmap.fImage; + int n = hBitmap.GetWidth() * hBitmap.GetHeight(); + int i; + for( i = 0; i < n; i++ ) + { + dstColor->a = 255 - dstColor->a; + dstColor++; + } + + return 0; + hsGuardEnd; +} + +plBitmap *plBitmapCreator::CreateTexture(plBitmapData *bd, const plLocation &loc, int clipID) +{ + plBitmap* bm = ICreateTexture(bd, loc, clipID); + + for (int i = 0; i < plLocalization::GetNumLocales(); i++) + { + char localName[MAX_PATH]; + if (plLocalization::ExportGetLocalized(bd->fileName, i, localName)) + { + const char* oldName = bd->fileName; + bd->fileName = localName; + ICreateTexture(bd, loc, clipID); + bd->fileName = oldName; + } + } + + return bm; +} + +//// ICreateTexture //////////////////////////////////////////////////////////// +// Plasma texture creator. Pass it a completed bitmapdata structure and it +// returns a registered texture pointer, or nil if something goes wrong. +// +// 9.14.2001 mcn - clipID added to uniquely identify mipmaps that have been +// rescaled differently (approximately represents how many powers of 2 it was +// scaled down from the original source). +// +// 3.29.2002 mcn - Moved to plBitmapCreator, where it really belongs, and +// added code to handle tracking/cleaning up all materials that are exported. + +plBitmap *plBitmapCreator::ICreateTexture( plBitmapData *bd, const plLocation &loc, int clipID ) +{ + hsGuardBegin( "plBitmapCreator::CreateTexture" ); + + const plLocation &textureLoc = plPluginResManager::ResMgr()->GetCommonPage( loc, plAgeDescription::kTextures ); + + if( !bd ) + { + fErrorMsg->Set( true, "Bitmap Error", "No bitmap data" ).Show(); + fErrorMsg->Set(); + return nil; + } + + if( bd->fileName == nil || bd->fileName[ 0 ] == 0 ) + { + fErrorMsg->Set( true, "Bitmap Error", "Material texture has null bitmap name." ).Show(); + fErrorMsg->Set(); + return nil; + } + + // Get and mangle key name + char name[ 256 ], temp[ 256 ]; + _splitpath(bd->fileName, NULL, NULL, temp, NULL); + + // Somehow, sometimes, we get the same file in with different cases. So we need to force the + // case identical all the time, else the patching process for dat files will think they're + // "different" when they're really not + strlwr( temp ); + + /// Mangle name for detail textures, so we don't end up overwriting settings elsewhere + if( bd->createFlags & plMipmap::kCreateDetailMask ) + { + // Mangle of the form: name@dropStart&dropStop&max&min + if( clipID != -1 ) + sprintf( name, "%s*%x#%d@%s&%3.2f&%3.2f&%3.2f&%3.2f", temp, bd->texFlags, clipID, + bd->createFlags & plMipmap::kCreateDetailAlpha ? "al" : ( bd->createFlags & plMipmap::kCreateDetailAdd ? "ad" : "mu" ), + bd->detailDropoffStart, bd->detailDropoffStop, bd->detailMax, bd->detailMin ); + else + sprintf( name, "%s*%x@%s&%3.2f&%3.2f&%3.2f&%3.2f", temp, bd->texFlags, + bd->createFlags & plMipmap::kCreateDetailAlpha ? "al" : ( bd->createFlags == plMipmap::kCreateDetailAdd ? "ad" : "mu" ), + bd->detailDropoffStart, bd->detailDropoffStop, bd->detailMax, bd->detailMin ); + } + else if( clipID != -1 ) + sprintf( name, "%s*%x#%d", temp, bd->texFlags, clipID ); + else + sprintf( name, "%s*%x", temp, bd->texFlags ); + if( bd->invertAlpha ) + strcat( name, "_inva" ); + strcat( name, ".hsm" ); + + + // Has this texture been used before? + plKey key; + + plBitmap *texture = plBitmap::ConvertNoRef( sCommonBitmapLib.FindObject( name, ( bd->isStaticCubicEnvMap ) ? plCubicEnvironmap::Index() : plMipmap::Index() ) ); + //hsAssert( texture == nil || texture->GetKey()->GetUoid().GetLocation() == textureLoc, "Somehow our texture objectLib has a texture not in the right page? Should be harmless tho..." ); + + // Texture reuse optimization + if( texture ) + { + WIN32_FILE_ATTRIBUTE_DATA fileAttrib; + GetFileAttributesEx(bd->fileName, GetFileExInfoStandard, &fileAttrib); + FILETIME &fileTime = fileAttrib.ftLastWriteTime; + + // If this texture has been modified since the last export, delete the old version but reuse the key + if (!texture->IsSameModifiedTime(fileTime.dwLowDateTime, fileTime.dwHighDateTime)) + { + DeleteExportedBitmap( texture->GetKey() ); + texture = nil; + key = nil; + } + } + + if( texture ) + { + // If it's in the registry, great, use it. + if( bd->texFlags & plMipmap::kNoMaxSize ) + texture->SetFlags( texture->GetFlags() | plMipmap::kNoMaxSize ); + + if( bd->texFlags & plMipmap::kHalfSize ) + texture->SetFlags( texture->GetFlags() | plMipmap::kHalfSize ); + } + else + { + // If it hasn't been used before, make a new texture + if( bd->isStaticCubicEnvMap ) + { + plCubicEnvironmap *cubic = TRACKED_NEW plCubicEnvironmap; + + plMipmap *face; + + /// Build and set the faces + bd->fileName = bd->faceNames[ plStaticEnvLayer::kTopFace ]; + face = ICreateBitmap( bd ); + if( face == nil ) return nil; + cubic->CopyToFace( face, plCubicEnvironmap::kTopFace ); + + bd->fileName = bd->faceNames[ plStaticEnvLayer::kBottomFace ]; + face = ICreateBitmap( bd ); + if( face == nil ) return nil; + cubic->CopyToFace( face, plCubicEnvironmap::kBottomFace ); + + bd->fileName = bd->faceNames[ plStaticEnvLayer::kLeftFace ]; + face = ICreateBitmap( bd ); + if( face == nil ) return nil; + cubic->CopyToFace( face, plCubicEnvironmap::kLeftFace ); + + bd->fileName = bd->faceNames[ plStaticEnvLayer::kRightFace ]; + face = ICreateBitmap( bd ); + if( face == nil ) return nil; + cubic->CopyToFace( face, plCubicEnvironmap::kRightFace ); + + /// NOTE: For whatever reason, MAX decided that the front and back faces should be' + /// switched, literally. It's as if the cube for the cube map starts at the back face + /// and then wraps around, instead of starting at the front face. Since we do things + /// the RIGHT way (or rather, the front way :) on client-side, we need to flip the + /// two here. If you convert this to the real MAX UI, make sure the faces are still + /// flipped!!!!!!!! + + bd->fileName = bd->faceNames[ plStaticEnvLayer::kBackFace ]; + face = ICreateBitmap( bd ); + if( face == nil ) return nil; + cubic->CopyToFace( face, plCubicEnvironmap::kFrontFace ); + + bd->fileName = bd->faceNames[ plStaticEnvLayer::kFrontFace ]; + face = ICreateBitmap( bd ); + if( face == nil ) return nil; + cubic->CopyToFace( face, plCubicEnvironmap::kBackFace ); + + + key = hsgResMgr::ResMgr()->NewKey( name, cubic, textureLoc ); + + texture = (plBitmap *)cubic; + } + else + { + plMipmap *mipmap = ICreateBitmap(bd); + if (!mipmap) + return nil; + + key = hsgResMgr::ResMgr()->NewKey( name, mipmap, textureLoc ); + + texture = (plBitmap *)mipmap; + } + + // Texture reuse optimization + WIN32_FILE_ATTRIBUTE_DATA fileAttrib; + GetFileAttributesEx(bd->fileName, GetFileExInfoStandard, &fileAttrib); + FILETIME &fileTime = fileAttrib.ftLastWriteTime; + texture->SetModifiedTime(fileTime.dwLowDateTime, fileTime.dwHighDateTime); + + // Add to our list of created textures and ref, since we have a hold of them + IAddBitmap( texture ); + } + + return texture; + + hsGuardEnd; +} + +//// IAddBitmap /////////////////////////////////////////////////////////////// + +void plBitmapCreator::IAddBitmap( plBitmap *bitmap, hsBool dontRef ) +{ + sCommonBitmapLib.AddObject( bitmap ); +} + +//// CreateBlankMipmap //////////////////////////////////////////////////////// +// Simple mipmap creator, but importantly, it also adds the mipmap to the list +// of "converted" maps to clean up at the end of export. + +plMipmap *plBitmapCreator::CreateBlankMipmap( UInt32 width, UInt32 height, unsigned config, UInt8 numLevels, + const char *keyName, const plLocation &keyLocation ) +{ + hsGuardBegin( "plBitmapCreator::CreateBlankMipmap" ); + + // Get our real location + const plLocation &textureLoc = plPluginResManager::ResMgr()->GetCommonPage( keyLocation, plAgeDescription::kTextures ); + + // Is it already created? + plKey key = hsgResMgr::ResMgr()->FindKey( plUoid( textureLoc, plMipmap::Index(), keyName ) ); + if( key != nil ) + return plMipmap::ConvertNoRef( key->GetObjectPtr() ); + + // Create + plMipmap *mip = TRACKED_NEW plMipmap( width, height, config, numLevels ); + + // Assign key + hsgResMgr::ResMgr()->NewKey( keyName, mip, textureLoc ); + + // Add to our list + IAddBitmap( mip ); + + return mip; + + hsGuardEnd; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plBitmapCreator.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plBitmapCreator.h new file mode 100644 index 00000000..adc25da0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plBitmapCreator.h @@ -0,0 +1,113 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "../pnKeyedObject/plKey.h" + +class BitmapInfo; +class Bitmap; +class plBitmap; +class plMipmap; +class hsMaxLayerBase; +class plLocation; +class plErrorMsg; + + +class plBitmapData +{ +public: + enum + { + kClampU = 0x01, + kClampV = 0x02 + }; + + const char *fileName; + UInt32 texFlags; + UInt32 createFlags; + hsScalar detailDropoffStart; + hsScalar detailDropoffStop; + hsScalar detailMax; + hsScalar detailMin; + float sig; + hsBool isStaticCubicEnvMap; + hsBool invertAlpha; + const char *faceNames[ 6 ]; + UInt32 maxDimension; + UInt8 clampFlags; + bool useJPEG; + + plBitmapData() + { + fileName = nil; + texFlags = 0; + createFlags = 0; + detailDropoffStart = detailDropoffStop = 0.f; + detailMax = detailMin = 0.f; + sig = 0; + isStaticCubicEnvMap = false; + invertAlpha = false; + faceNames[ 0 ] = faceNames[ 1 ] = faceNames[ 2 ] = faceNames[ 3 ] = faceNames[ 4 ] = faceNames[ 5 ] = nil; + maxDimension = 0; + clampFlags = 0; + useJPEG = false; + } +}; + +class plRegistryKeyIterator; +class plBitmapCreator +{ + public: + + static plBitmapCreator &Instance(); + + plBitmap *CreateTexture( plBitmapData *bd, const plLocation &loc, int clipID = -1 ); + plMipmap *CreateBlankMipmap( UInt32 width, UInt32 height, unsigned config, UInt8 numLevels, const char *keyName, const plLocation &keyLocation ); + + void Init( hsBool save, plErrorMsg *msg ); + void DeInit( void ); + void CleanUpMaps( void ); + + ~plBitmapCreator(); + + // This will also set the key you pass in to nil, so be careful + void DeleteExportedBitmap( const plKey &key ); + + protected: + + plErrorMsg *fErrorMsg; + + plBitmapCreator(); + + plBitmap *ICreateTexture( plBitmapData *bd, const plLocation &loc, int clipID = -1 ); + plMipmap *ICreateBitmap( plBitmapData *bd ); + + void ICheckOutBitmap( BitmapInfo *bInfo, Bitmap *bm, const char *fileName ); + int IResampBitmap( Bitmap *bm, plMipmap &hBitmap ); + int ICopyBitmap( Bitmap *bm, plMipmap &hBitmap ); + int IInvertAlpha( plMipmap &hBitmap ); + + void IAddBitmap( plBitmap *bitmap, hsBool dontRef = false ); +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plClusterUtil.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plClusterUtil.cpp new file mode 100644 index 00000000..915d55c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plClusterUtil.cpp @@ -0,0 +1,787 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsWindows.h" +#include + +#include "Max.h" +#include "stdmat.h" +#include "bmmlib.h" +#include "iparamb2.h" +#include "meshdlib.h" + +#include "hsTypes.h" + +#include +#include + +#include "../MaxMain/plMaxNode.h" +#include "../MaxComponent/plComponent.h" +#include "../MaxComponent/plLightGrpComponent.h" +#include "../MaxComponent/plSoftVolumeComponent.h" + +#include "plClusterUtil.h" + +#include "../plDrawable/plClusterGroup.h" +#include "../plDrawable/plCluster.h" +#include "../plDrawable/plSpanTemplate.h" +#include "../plDrawable/plSpanInstance.h" +#include "../plDrawable/plGeometrySpan.h" + +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayer.h" +#include "../plScene/plVisRegion.h" +#include "../plGLight/plLightInfo.h" + +#include "plMeshConverter.h" +#include "hsVertexShader.h" +#include "plLightMapGen.h" + +#include "hsResMgr.h" +#include "../pnKeyedObject/plUoid.h" + +#include "../pnMessage/plNodeRefMsg.h" + +#include "plTweak.h" + +plConst(int) kDefMinFaces(200); +plConst(int) kDefMaxFaces(1000); +plConst(hsScalar) kDefMinSize(50.f); + +plClusterUtil::plClusterUtil() +: fGroup(nil), + fTemplNode(nil), + fTemplate(nil), + fMinFaces(kDefMinFaces), + fMaxFaces(kDefMaxFaces), + fMinSize(kDefMinSize), + fIdx(0) +{ +} + +plClusterUtil::~plClusterUtil() +{ +} + +plClusterGroup* plClusterUtil::CreateGroup(plMaxNode* templNode, const char* name) +{ + plClusterGroup* retVal = TRACKED_NEW plClusterGroup; + + char buff[256]; + sprintf(buff, "%s_%s_%d", name, templNode->GetName(), fIdx++); + hsgResMgr::ResMgr()->NewKey(buff, retVal, templNode->GetLocation(), templNode->GetLoadMask()); + + plKey sceneNode = templNode->GetRoomKey(); + retVal->SetSceneNode(sceneNode); + + plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(sceneNode, plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric); + hsgResMgr::ResMgr()->AddViaNotify(retVal->GetKey(), refMsg, plRefFlags::kActiveRef); + + return retVal; +} + +plClusterGroup* plClusterUtil::SetupGroup(plClusterGroup *group, plMaxNode* templNode, plSpanTemplateB* templ) +{ + fTemplNode = templNode; + fGroup = group; + fTemplate = templ; + + fGroup->fTemplate = templ; + fGroup->ISendToSelf(plClusterGroup::kRefMaterial, templ->fMaterial); + + fGroup->fRenderLevel = templ->fRenderLevel; + + fMinInsts = fMinFaces / templ->NumTris(); + fMaxInsts = fMaxFaces / templ->NumTris(); + if( fMinInsts < 1 ) + fMinInsts = 1; + if( fMaxInsts <= fMinInsts ) + fMaxInsts = fMinInsts+1; + + // STUB + // Finish setting up the group here (lights, visregions, LOD), extracting all info + // from the template node. + ISetupGroupFromTemplate(templNode); + + return fGroup; +} + +void plClusterUtil::ISetupGroupFromTemplate(plMaxNode* templ) +{ + plLightGrpComponent* liGrp = plLightGrpComponent::GetComp(templ); + if( liGrp ) + { + const hsTArray& lights = liGrp->GetLightInfos(); + int i; + for( i = 0; i < lights.GetCount(); i++ ) + { + fGroup->ISendToSelf(plClusterGroup::kRefLight, lights[i]); + } + } + if( templ->HasFade() ) + { + hsScalar maxDist = 0; + hsScalar minDist = 0; + + Box3 fade = templ->GetFade(); + const hsScalar kMaxMaxDist = 1.e10f; + if( fade.Min()[2] < 0 ) + { + minDist = fade.Min()[0]; + maxDist = kMaxMaxDist; + } + + if( fade.Max()[2] > 0 ) + maxDist = fade.Max()[0]; + + if( maxDist > minDist ) + { + fGroup->fLOD.Set(minDist, maxDist); + } + } + hsTArray regions; + plVisRegionComponent::CollectRegions(templ, regions); + plEffVisSetComponent::CollectRegions(templ, regions); + if( regions.GetCount() ) + { + int i; + for( i = 0; i < regions.GetCount(); i++ ) + { + fGroup->ISendToSelf(plClusterGroup::kRefRegion, regions[i]); + } + } +} + +class sortData +{ +public: + UInt16 fIdx0; + UInt16 fIdx1; + UInt16 fIdx2; + hsScalar fDist; + + sortData() {} + sortData(UInt16 idx0, UInt16 idx1, UInt16 idx2, hsScalar dist) + : fIdx0(idx0), fIdx1(idx1), fIdx2(idx2), fDist(dist) + { + } + + bool operator<(const sortData& ot) const { return fDist < ot.fDist; } + bool operator>(const sortData& ot) const { return fDist > ot.fDist; } + bool operator==(const sortData& ot) const { return fDist == ot.fDist; } + bool operator!=(const sortData& ot) const { return fDist != ot.fDist; } +}; + +void plClusterUtil::ISortTemplate(plSpanTemplateB* templ) const +{ + UInt16* indexData = templ->fIndices; + const int numTris = templ->NumTris(); + typedef std::vector sortVec; + sortVec vec; + vec.resize(numTris); + sortVec::iterator iter; + for( iter = vec.begin(); iter != vec.end(); iter++ ) + { + iter->fIdx0 = indexData[0]; + iter->fIdx1 = indexData[1]; + iter->fIdx2 = indexData[2]; + hsPoint3 pos; + pos = *templ->Position(indexData[0]); + float dist0 = pos.fX * pos.fX + pos.fY * pos.fY; + pos = *templ->Position(indexData[1]); + float dist1 = pos.fX * pos.fX + pos.fY * pos.fY; + pos = *templ->Position(indexData[2]); + float dist2 = pos.fX * pos.fX + pos.fY * pos.fY; + + iter->fDist = dist0 > dist1 + ? (dist0 > dist2 + ? dist0 + : dist2) + : (dist1 > dist2 + ? dist1 + : dist2); + + indexData += 3; + } + + std::sort(vec.begin(), vec.end(), std::less()); + + indexData = templ->fIndices; + for( iter = vec.begin(); iter != vec.end(); iter++ ) + { + indexData[0] = iter->fIdx0; + indexData[1] = iter->fIdx1; + indexData[2] = iter->fIdx2; + + indexData += 3; + } +} + +void plClusterUtil::ITemplateFromGeo(plSpanTemplateB* templ, plGeometrySpan* geo) +{ + UInt16 format = plSpanTemplate::MakeFormat( + true, // hasColor + geo->GetNumUVs(), // UVW count + geo->fFormat & plGeometrySpan::kSkinIndices, // hasWgtIdx + (geo->fFormat & plGeometrySpan::kSkinWeightMask) >> 4, // NumWeights + true, // hasNorm + true // hasPos; + ); + + + UInt32 numVerts = geo->fNumVerts; + UInt32 numTris = geo->fNumIndices / 3; + + // Alloc it. + templ->Alloc(format, numVerts, numTris); + templ->AllocColors(); + + UInt32 numPos = templ->NumPos(); + UInt32 numNorm = templ->NumNorm(); + UInt32 numUVWs = templ->NumUVWs(); + UInt32 numWeights = templ->NumWeights(); + UInt32 numColor = templ->NumColor(); + UInt32 numColor2 = templ->NumColor2(); + UInt32 numWgtIdx = templ->NumWgtIdx(); + + // Fill in the data. + memcpy(templ->fIndices, geo->fIndexData, templ->IndexSize()); + + int i; + for( i = 0; i < templ->NumVerts(); i++ ) + { + float wgt[4]; + UInt32 wgtIdx; + + geo->ExtractInitColor(i, templ->MultColor(i), templ->AddColor(i)); + + hsColorRGBA color; + geo->ExtractVertex(i, templ->Position(i), templ->Normal(i), &color); + + if( templ->NumColor() ) + *templ->Color(i) = color.ToARGB32(); + if( templ->NumColor2() ) + *templ->Color2(i) = 0; + + int k; + for( k = 0; k < templ->NumUVWs(); k++ ) + { + geo->ExtractUv(i, k, templ->UVWs(i, k)); + } + + if( templ->NumWeights() ) + { + geo->ExtractWeights(i, wgt, &wgtIdx); + int j; + for( j = 0; j < templ->NumWeights(); j++ ) + *templ->Weight(i, j) = wgt[j]; + if( templ->NumWgtIdx() ) + *templ->WgtIdx(i) = wgtIdx; + } + } + + // Compute the local bounds. + templ->ComputeBounds(); + + ISortTemplate(templ); +} + +plSpanTemplateB* plClusterUtil::IAddTemplate(plMaxNode* templNode, plGeometrySpan* geo) +{ + // Shade our mesh. + // STUB + + // Create our blank template + plSpanTemplateB* templ = TRACKED_NEW plSpanTemplateB(templNode); + + templ->fRenderLevel = templNode->GetRenderLevel(!templNode->GetNoDeferDraw()); + + ITemplateFromGeo(templ, geo); + + return templ; +} + +void plClusterUtil::IAddTemplates(plMaxNode* templNode, plSpanTemplTab& templs) +{ + // STUB + // Get the Mesh + + // Figure the format and total number of verts. + // If we're lazy or pressed for time we could use MeshConverter. + // But we'd probably spend more time undoing MeshConverter hacks than if we start + // from scratch. + // But, here we go descending cheerully into hell. + // At least with this interface we can bail and do it right later without to much + // bloodshed. + hsTArray spanArray; + if( !plMeshConverter::Instance().CreateSpans(templNode, spanArray, false) ) + return; + + plLightMapGen::Instance().Open(::GetCOREInterface(), ::GetCOREInterface()->GetTime(), false); + hsVertexShader::Instance().Open(); + + hsVertexShader::Instance().ShadeNode(templNode, + templNode->GetLocalToWorld44(), templNode->GetWorldToLocal44(), + spanArray); + + plLightMapGen::Instance().Close(); + hsVertexShader::Instance().Close(); + + int i; + for( i = 0; i < spanArray.GetCount(); i++ ) + { + plSpanTemplateB* templ = IAddTemplate(templNode, spanArray[i]); + templs.Append(1, &templ); + templ->fMaterial = spanArray[i]->fMaterial; + + delete spanArray[i]; + } +} + +Box3 plClusterUtil::IBound(const plL2WTab& src) const +{ + Box3 box; + int i; + for( i = 0; i < src.Count(); i++ ) + { + box += src[i].GetTrans(); + } + return box; +} + +Point3 plClusterUtil::ILength(const plL2WTab& src) const +{ + Box3 box = IBound(src); + return box.Max() - box.Min(); +} + +int plClusterUtil::ISelectAxis(const plL2WTab& src) const +{ + Box3 box = IBound(src); + Point3 del = box.Max() - box.Min(); + if( del.x > del.y ) + { + if( del.x > del.z ) + return 0; + else + return 2; + } + if( del.y > del.z ) + return 1; + + return 2; +} + +static int sortAxis = 0; +static int cmp(const void *elem1, const void *elem2) +{ + Matrix3* m1 = (Matrix3*) elem1; + Matrix3* m2 = (Matrix3*) elem2; + + float d1 = m1->GetTrans()[sortAxis]; + float d2 = m2->GetTrans()[sortAxis]; + + if( d1 < d2 ) + return -1; + else if( d1 > d2 ) + return 1; + + return 0; +} + + +hsBool plClusterUtil::ISplitCluster(plSpanTemplateB* templ, plL2WTab& src, plL2WTab& lo, plL2WTab& hi) +{ + // Tried this, seems to work pretty well, but a more even grid is probably wiser at + // this point. +#if 0 // MAX_SEP + if( src.Count() <= fMinInsts) + return false; + + // Pick an axis + sortAxis = ISelectAxis(src); + + if( src.Count() < fMaxInsts) + { + Point3 len = ILength(src); + if( len[sortAxis] < fMinSize ) + return false; + } + + // Sort by that axis + src.Sort(cmp); + + // Find the biggest gap + float maxDist = 0; + int pivot = 0; + int i; + for( i = 1; i < src.Count(); i++ ) + { + float dist = src[i].GetTrans()[sortAxis] - src[i-1].GetTrans()[sortAxis]; + if( dist > maxDist ) + { + maxDist = dist; + pivot = i; + } + } + hsAssert((pivot > 0) && (pivot < src.Count()), "Invalid pivot found"); + + // Put everyone above it in hi, below it in lo + lo.Append(pivot, src.Addr(0)); + hi.Append(src.Count()-pivot, src.Addr(pivot)); + +#else // MAX_SEP + + if( src.Count() <= fMinInsts ) + return false; + + // Pick an axis + sortAxis = ISelectAxis(src); + + if( src.Count() < fMaxInsts) + { + Point3 len = ILength(src); + if( len[sortAxis] < fMinSize ) + return false; + } + + // Sort by that axis + src.Sort(cmp); + + int pivot = src.Count() >> 1; + lo.Append(pivot, src.Addr(0)); + hi.Append(src.Count()-pivot, src.Addr(pivot)); +#endif // MAX_SEP + + return true; +} + +void plClusterUtil::IFindClustersRecur(plSpanTemplateB* templ, plL2WTab& src, plL2WTabTab& dst) +{ + plL2WTab lo; + plL2WTab hi; + + if( ISplitCluster(templ, src, lo, hi) ) + { + // Keep going + IFindClustersRecur(templ, lo, dst); + IFindClustersRecur(templ, hi, dst); + } + else + { + plL2WTab* tab = TRACKED_NEW plL2WTab(src); + dst.Append(1, &tab); + } +} + +void plClusterUtil::IFreeClustersRecur(plL2WTabTab& dst) const +{ + int i; + for( i = 0; i < dst.Count(); i++ ) + delete dst[i]; +} + +inline hsScalar inlGetAlpha(UInt32* color) +{ + return hsScalar(*color >> 24) / 255.99f; +} + +plSpanEncoding plClusterUtil::ISelectEncoding(plPoint3TabTab& delPosTab, plColorTabTab& colorsTab) +{ + hsBool hasColor = false; + hsBool hasAlpha = false; + hsScalar maxLenSq = 0; + hsScalar maxX = 0; + hsScalar maxY = 0; + hsScalar maxZ = 0; + int i; + for( i = 0; i < delPosTab.Count(); i++ ) + { + int j; + if( delPosTab[i] ) + { + plPoint3Tab& delPos = *delPosTab[i]; + for( j = 0; j < delPos.Count(); j++ ) + { + hsScalar lenSq = delPos[j].MagnitudeSquared(); + if( lenSq > maxLenSq ) + maxLenSq = lenSq; + hsScalar d = fabs(delPos[j].fX); + if( d > maxX ) + maxX = d; + d = fabs(delPos[j].fY); + if( d > maxY ) + maxY = d; + d = fabs(delPos[j].fZ); + if( d > maxZ ) + maxZ = d; + + } + } + + if( colorsTab[i] ) + { + plColorTab& color = *colorsTab[i]; + for( j = 0; j < color.Count(); j++ ) + { + UInt32 col = color[j]; + if( (col & 0x00ffffff) != 0x00ffffff ) + hasColor = true; + if( (col & 0xff000000) != 0xff000000 ) + hasAlpha = true; + } + } + } + + UInt32 code = 0; + hsScalar posScale = 1.f; + + if( hasColor && hasAlpha ) + code |= plSpanEncoding::kColAI88; + else if( hasColor ) + code |= plSpanEncoding::kColI8; + else if( hasAlpha ) + code |= plSpanEncoding::kColA8; + + plConst(hsScalar) kPosQuantum(0.5 / 12.f); // 1/2 inch. + hsScalar maxLen = hsSquareRoot(maxLenSq); + if( maxLen > kPosQuantum ) + { + if( (maxX < kPosQuantum) && (maxY < kPosQuantum) ) + { + code |= plSpanEncoding::kPos008; + posScale = maxLen / 255.9f; + } + else if( (maxLen / 255.9f) < kPosQuantum ) + { + code |= plSpanEncoding::kPos888; + posScale = maxLen / 255.9f; + } + else if( (maxLen / hsScalar(1 << 10)) < kPosQuantum ) + { + code |= plSpanEncoding::kPos101010; + posScale = maxLen / hsScalar(1 << 10); + } + else + { + code |= plSpanEncoding::kPos161616; + posScale = maxLen / hsScalar(1 << 16); + } + } + return plSpanEncoding(code, posScale); +} + +static int CompTemplates(const void *elem1, const void *elem2) +{ + plSpanTemplateB* templA = *((plSpanTemplateB**)elem1); + plSpanTemplateB* templB = *((plSpanTemplateB**)elem2); + + hsScalar hA = templA->GetLocalBounds().GetMaxs().fZ; + hsScalar hB = templB->GetLocalBounds().GetMaxs().fZ; + + if( hA < hB ) + return -1; + + if( hA > hB ) + return 1; + + return 0; +} + +void plClusterUtil::ISortTemplates(plSpanTemplTab& templs) const +{ + templs.Sort(CompTemplates); + + float maxZ = -1.e33f; + + int i; + for( i = 1; i < templs.Count(); i++ ) + { + templs[i]->fRenderLevel.Set(templs[i-1]->fRenderLevel.Level() + 1); + } +} + +plSpanTemplTab plClusterUtil::MakeTemplates(INode* templNode) +{ + plSpanTemplTab templs; + IAddTemplates((plMaxNode*)templNode, templs); + ISortTemplates(templs); + return templs; +} + +void plClusterUtil::AddClusters(plL2WTab& insts, plDeformVert* def, plShadeVert* shade) +{ + plPoint3TabTab delPos; + plColorTabTab colors; + + plL2WTabTab clusters; + IFindClustersRecur(fTemplate, insts, clusters); + + int j; + for( j = 0; j < clusters.Count(); j++ ) + { + // Create a plCluster to hold them all. + plCluster* cluster = fGroup->IAddCluster(); + + // Get the delPositions and colors for all the instances + IAllocPosAndColor(fTemplate, *clusters[j], delPos, colors); + + IDelPosAndColor(fTemplate, + *clusters[j], + def, shade, + delPos, colors); + + + // Look through the results and pick out a proper encoding + plSpanEncoding code = ISelectEncoding(delPos, colors); + cluster->SetEncoding(code); + + // Now create, encode and add all the insts to the cluster. + IAddInstsToCluster(cluster, fTemplate, *clusters[j], delPos, colors); + + IFreePosAndColor(delPos, colors); + } + + IFreeClustersRecur(clusters); +} + +void plClusterUtil::IAddInstsToCluster(plCluster* cluster, plSpanTemplateB* templ, + const plL2WTab& insts, + plPoint3TabTab& delPos, + plColorTabTab& colors) +{ + int i; + for( i = 0; i < insts.Count(); i++ ) + { + plSpanInstance* span = TRACKED_NEW plSpanInstance; + span->Alloc(cluster->GetEncoding(), templ->NumVerts()); + + span->SetLocalToWorld(plMaxNodeBase::Matrix3ToMatrix44(insts[i])); + + span->Encode(cluster->GetEncoding(), templ->NumVerts(), + delPos[i] ? delPos[i]->Addr(0) : nil, + colors[i] ? colors[i]->Addr(0) : nil); + + cluster->IAddInst(span); + } +} + + +void plClusterUtil::IAllocPosAndColor(plSpanTemplateB* templ, const plL2WTab& insts, + plPoint3TabTab& delPos, plColorTabTab& colors) +{ + delPos.SetCount(insts.Count()); + colors.SetCount(insts.Count()); + + const int numVerts = templ->NumVerts(); + int i; + for( i = 0; i < insts.Count(); i++ ) + { + delPos[i] = nil; + colors[i] = nil; + } +} + +void plClusterUtil::IFreePosAndColor(plPoint3TabTab& delPos, plColorTabTab& colors) const +{ + int i; + for( i = 0; i < delPos.Count(); i++ ) + delete delPos[i]; + for( i = 0; i < colors.Count(); i++ ) + delete colors[i]; +} + +void plClusterUtil::IDelPosAndColor(plSpanTemplateB* templ, + const plL2WTab& insts, plDeformVert* def, plShadeVert* shade, + plPoint3TabTab& delPos, plColorTabTab& colors) +{ + + hsBool doDef = def != nil; + hsBool doCol = shade != nil; + // For each inst + int i; + for( i = 0; i < insts.Count(); i++ ) + { + hsBounds3Ext wBnd = templ->GetLocalBounds(); + hsMatrix44 l2w = plMaxNodeBase::Matrix3ToMatrix44(insts[i]); + hsMatrix44 w2l; + l2w.GetInverse(&w2l); + hsMatrix44 w2lT; + w2l.GetTranspose(&w2lT); + + wBnd.Transform(&l2w); + + if( doDef ) + { + def->Begin(templ->GetSrcNode(), wBnd); + + delPos[i] = TRACKED_NEW plPoint3Tab; + delPos[i]->SetCount(templ->NumVerts()); + int j; + for( j = 0; j < templ->NumVerts(); j++ ) + { + hsPoint3 p = l2w * *templ->Position(j); + plPoint3Tab& dp = *delPos[i]; + dp[j] = def->GetDel(p); + dp[j] = w2l * dp[j]; + } + + def->End(); + } + + + + // Make the stored colors the actual output UInt32. + // templ has the mult and add colors, apply them here. + if( doCol ) + { + shade->Begin(templ->GetSrcNode(), wBnd); + + colors[i] = TRACKED_NEW plColorTab; + colors[i]->SetCount(templ->NumVerts()); + int j; + for( j = 0; j < templ->NumVerts(); j++ ) + { + hsPoint3 pos = *templ->Position(j); + pos += (*delPos[i])[j]; + pos = l2w * pos; + + hsVector3 norm = *templ->Normal(j); + norm = w2lT * norm; + + Color rgb = shade->GetShade(pos, norm); + + rgb *= Color(templ->MultColor(j)->r, templ->MultColor(j)->g, templ->MultColor(j)->b); + rgb += Color(templ->AddColor(j)->r, templ->AddColor(j)->g, templ->AddColor(j)->b); + + (*colors[i])[j] = hsColorRGBA().Set(rgb.r, rgb.g, rgb.b, templ->MultColor(j)->a).ToARGB32(); + } + shade->End(); + } + + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plClusterUtil.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plClusterUtil.h new file mode 100644 index 00000000..e37c010a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plClusterUtil.h @@ -0,0 +1,134 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plClusterUtil_inc +#define plClusterUtil_inc + +#include "hsGeometry3.h" +#include "hsColorRGBA.h" + +#include "../plDrawable/plSpanInstance.h" + +class plMaxNode; +class plClusterGroup; +class plSpanTemplateB; +class hsBounds3Ext; +class plCluster; +class hsGMaterial; + +class plDeformVert +{ +public: + + // Begin returns true if it's actually planning to do anything. + // If it returns false, End will still get called, but GetDel probably won't. + virtual hsBool Begin(INode* templNode, const hsBounds3Ext& wBnd) { return false; } + virtual void End() {} + + hsVector3 GetDel(const hsPoint3& p) { return GetDel(Point3(p.fX, p.fY, p.fZ)); } + virtual hsVector3 GetDel(const Point3& p) { return hsVector3(0,0,0); } +}; + +class plShadeVert +{ +public: + virtual hsBool Begin(INode* templNode, const hsBounds3Ext& wBnd) { return false; } + virtual void End() {} + + Color GetShade(const hsPoint3& p, const hsVector3& n) { return GetShade(Point3(p.fX, p.fY, p.fZ), Point3(n.fX, n.fY, n.fZ)); } + virtual Color GetShade(const Point3& p, const Point3& n) { return Color(0,0,0); } +}; + +class plL2WTab : public Tab {}; +class plL2WTabTab : public Tab {}; + +class plPoint3Tab : public Tab {}; +class plPoint3TabTab : public Tab {}; + +class plColorTab : public Tab {}; +class plColorTabTab : public Tab {}; + +class plSpanTemplTab : public Tab {}; + +class plClusterUtil +{ +protected: + UInt32 fIdx; + plClusterGroup* fGroup; + plMaxNode* fTemplNode; + plSpanTemplateB* fTemplate; + + int fMinFaces; + int fMaxFaces; + hsScalar fMinSize; + + int fMinInsts; + int fMaxInsts; + + + plSpanEncoding ISelectEncoding(plPoint3TabTab& delPos, plColorTabTab& colors); + void IAllocPosAndColor(plSpanTemplateB* templ, const plL2WTab& insts, + plPoint3TabTab& delPos, plColorTabTab& colors); + void IDelPosAndColor(plSpanTemplateB* templ, + const plL2WTab& insts, plDeformVert* def, plShadeVert* shade, + plPoint3TabTab& delPos, plColorTabTab& colors); + void IAddInstsToCluster(plCluster* cluster, plSpanTemplateB* templ, + const plL2WTab& insts, + plPoint3TabTab& delPos, + plColorTabTab& colors); + void IFreePosAndColor(plPoint3TabTab& delPos, plColorTabTab& colors) const; + + void IFreeClustersRecur(plL2WTabTab& dst) const; + void IFindClustersRecur(plSpanTemplateB* templ, plL2WTab& src, plL2WTabTab& dst); + hsBool ISplitCluster(plSpanTemplateB* templ, plL2WTab& src, plL2WTab& lo, plL2WTab& hi); + int ISelectAxis(const plL2WTab& src) const; + Box3 IBound(const plL2WTab& src) const; + Point3 ILength(const plL2WTab& src) const; + + void ISortTemplate(plSpanTemplateB* templ) const; + plSpanTemplateB* IAddTemplate(plMaxNode* templNode, plGeometrySpan* geo); + void ITemplateFromGeo(plSpanTemplateB* templ, plGeometrySpan* geo); + + void ISortTemplates(plSpanTemplTab& templs) const; + void IAddTemplates(plMaxNode* templNode, plSpanTemplTab& templs); + + void ISetupGroupFromTemplate(plMaxNode* templ); + +public: + plClusterUtil(); + ~plClusterUtil(); + + plSpanTemplTab MakeTemplates(INode* templNode); + + plClusterGroup* CreateGroup(plMaxNode* node, const char* name); + plClusterGroup* SetupGroup(plClusterGroup* group, plMaxNode* node, plSpanTemplateB* templ); + plClusterGroup* GetGroup() const { return fGroup; } + + void AddClusters(plL2WTab& insts, plDeformVert* def, plShadeVert* shade); + +}; + +#endif // plClusterUtil_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plConvert.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plConvert.cpp new file mode 100644 index 00000000..ab1f281e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plConvert.cpp @@ -0,0 +1,498 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +// +// 3DSMax HeadSpin exporter +// +#include "hsTypes.h" +#include "Max.h" +#include "istdplug.h" +#include "Notify.h" +#include +#include "bmmlib.h" +#include "INode.h" + +#include "plConvert.h" +#include "hsResMgr.h" +#include "hsTemplates.h" + +#include "hsConverterUtils.h" +#include "hsControlConverter.h" +#include "plMeshConverter.h" +#include "hsMaterialConverter.h" +#include "plLayerConverter.h" +#include "UserPropMgr.h" +#include "hsStringTokenizer.h" +#include "../MaxExport/plErrorMsg.h" +#include "hsVertexShader.h" +#include "plLightMapGen.h" +#include "plBitmapCreator.h" +#include "plgDispatch.h" + +#include "../pnMessage/plTimeMsg.h" +#include "../MaxComponent/plComponent.h" +#include "../MaxMain/plMaxNode.h" +#include "../plMessage/plNodeCleanupMsg.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../MaxComponent/plClusterComponent.h" + +#include "../plPhysX/plSimulationMgr.h" +#include "../MaxMain/plPhysXCooking.h" +#include "../MaxExport/plExportProgressBar.h" +#include "hsUtils.h" + +#include "../MaxMain/plGetLocationDlg.h" + +#ifdef HS_DEBUGGING +#define HS_NO_TRY +#endif + +plConvert::plConvert() : fWarned(0) +{ +} + +plConvert& plConvert::Instance() +{ + static plConvert theInstance; + return theInstance; +} + +hsBool plConvert::IOK() +{ + return (!fQuit && !fpErrorMsg->IsBogus() ) ? true: false; +} + +hsBool plConvert::Convert() +{ +#ifndef HS_NO_TRY + try +#endif + { + fSettings->fReconvert = false; + fWarned = 0; + + fInterface->SetIncludeXRefsInHierarchy(TRUE); + + plMaxNode *pNode = (plMaxNode *)fInterface->GetRootNode(); + AddMessageToQueue(new plTransformMsg(nil, nil, nil, nil)); + AddMessageToQueue(new plDelayedTransformMsg(nil, nil, nil, nil)); + + IFindDuplicateNames(); + + plExportProgressBar bar; + hsBool retVal = true; // sometime, we might look at this + + if( !IAutoClusterRecur(pNode) ) + { + fQuit = true; + } + + if(IOK()) + { + bar.Start("Clear Old Data"); + retVal = pNode->DoAllRecur( plMaxNode::ClearData, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { + bar.Start("Convert Validate"); + retVal = pNode->DoRecur( plMaxNode::ConvertValidate, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { + bar.Start("Components Initialize"); + retVal = pNode->DoRecur( plMaxNode::SetupPropertiesPass, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { + bar.Start("Prepare for skinning"); + retVal = pNode->DoRecur( plMaxNode::PrepareSkin, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { + bar.Start("Make Scene Object"); + retVal = pNode->DoRecur( plMaxNode::MakeSceneObject, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { + bar.Start("Make Physical"); + plPhysXCooking::Init(); + retVal = pNode->DoRecur( plMaxNode::MakePhysical, fpErrorMsg, fSettings, &bar ); + plPhysXCooking::Shutdown(); + } + if(IOK()) + { + bar.Start("Component Preconvert"); + retVal = pNode->DoRecur( plMaxNode::FirstComponentPass, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { + bar.Start("Make Controller"); + retVal = pNode->DoRecur( plMaxNode::MakeController, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { // must be before mesh + bar.Start("Make Coord Interface"); + retVal = pNode->DoRecur( plMaxNode::MakeCoordinateInterface, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { // must be after coord interface but before pool data is created. + bar.Start("Make Connections"); + retVal = pNode->DoRecur( plMaxNode::MakeParentOrRoomConnection, fpErrorMsg, fSettings, &bar ); + } + + if(IOK()) + { // must be before simulation + bar.Start("Make Mesh"); + retVal = pNode->DoRecur( plMaxNode::MakeMesh, fpErrorMsg, fSettings, &bar ); + } + + if(IOK()) + { // doesn't matter when + bar.Start("Make Light"); + retVal = pNode->DoRecur( plMaxNode::MakeLight, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { // doesn't matter when + bar.Start("Make Occluder"); + retVal = pNode->DoRecur( plMaxNode::MakeOccluder, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { // must be after mesh + bar.Start("Make Modifiers"); + retVal = pNode->DoRecur( plMaxNode::MakeModifiers, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { + bar.Start("Convert Components"); + retVal = pNode->DoRecur( plMaxNode::ConvertComponents, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { + // do this after convert + bar.Start("Set Up Interface References"); + retVal = pNode->DoRecur( plMaxNode::MakeIfaceReferences, fpErrorMsg, fSettings, &bar ); + } + + if(IOK() && fSettings->fDoPreshade) + { + // These need to be opened after the components have had a chance to flag the MaxNodes + plLightMapGen::Instance().Open(fInterface, fInterface->GetTime(), fSettings->fDoLightMap); + hsVertexShader::Instance().Open(); + + bar.Start("Preshade Geometry"); + retVal = pNode->DoRecur( plMaxNode::ShadeMesh, fpErrorMsg, fSettings, &bar ); + + plLightMapGen::Instance().Close(); + hsVertexShader::Instance().Close(); + } + + if(IOK()) + { + // Do this next-to-last--allows all the components to free up any temp data they kept around + bar.Start("Component DeInit"); + retVal = pNode->DoRecur( plMaxNode::DeInitComponents, fpErrorMsg, fSettings, &bar ); + } + if(IOK()) + { + // Do this very last--it de-inits and frees all the maxNodeDatas lying around + bar.Start("Clear MaxNodeDatas"); + retVal = pNode->DoAllRecur( plMaxNode::ClearMaxNodeData, fpErrorMsg, fSettings, &bar ); + } +// fpErrorMsg->Set(); + + DeInit(); + + fInterface->SetIncludeXRefsInHierarchy(FALSE); + + return IOK(); + } +#ifndef HS_NO_TRY + catch(plErrorMsg& err) + { + DeInit(); + fInterface->SetIncludeXRefsInHierarchy(FALSE); + err.Show(); + return false; + } + catch(...) + { + DeInit(); + fInterface->SetIncludeXRefsInHierarchy(FALSE); + fpErrorMsg->Set(true, "plConvert", "Unknown error during convert\n"); + fpErrorMsg->Show(); + return false; + } +#endif +} + +//#include "../MaxMain/plMaxNodeData.h" +//#include + +hsBool ConvertList(hsTArray& nodes, PMaxNodeFunc p, plErrorMsg *errMsg, plConvertSettings *settings) +{ + for (int i = 0; i < nodes.Count(); i++) + { + (nodes[i]->*p)(errMsg, settings); + + if (errMsg && errMsg->IsBogus()) + return false; + } + + return true; +} + +hsBool plConvert::Convert(hsTArray& nodes) +{ +#ifndef HS_NO_TRY + try +#endif + { + fSettings->fReconvert = true; + + hsBool retVal = true; + + if (IOK()) + retVal = ConvertList(nodes, plMaxNode::ClearData, fpErrorMsg, fSettings); + + if(IOK()) + retVal = ConvertList(nodes, plMaxNode::ConvertValidate, fpErrorMsg, fSettings); + if(IOK()) + retVal = ConvertList(nodes, plMaxNode::SetupPropertiesPass, fpErrorMsg, fSettings); + if(IOK()) + retVal = ConvertList(nodes, plMaxNode::PrepareSkin, fpErrorMsg, fSettings); + if(IOK()) + retVal = ConvertList(nodes, plMaxNode::MakeSceneObject, fpErrorMsg, fSettings); + if(IOK()) + retVal = ConvertList(nodes, plMaxNode::FirstComponentPass, fpErrorMsg, fSettings); + if(IOK()) + retVal = ConvertList(nodes, plMaxNode::MakeController, fpErrorMsg,fSettings); + if(IOK()) + retVal = ConvertList(nodes, plMaxNode::MakeCoordinateInterface, fpErrorMsg, fSettings);// must be before mesh + if(IOK()) + retVal = ConvertList(nodes, plMaxNode::MakeParentOrRoomConnection, fpErrorMsg, fSettings); // after coord, before mesh (or any other pool data). + + // These shouldn't be opened until the components have had a chance to flag the MaxNodes + plLightMapGen::Instance().Open(fInterface, fInterface->GetTime(), fSettings->fDoLightMap); + hsVertexShader::Instance().Open(); + + if(IOK()) + retVal = ConvertList(nodes, plMaxNode::MakeMesh, fpErrorMsg, fSettings); // must be before simulation + + if(IOK()) // doesn't matter when + retVal = ConvertList(nodes, plMaxNode::MakeLight, fpErrorMsg, fSettings); + if(IOK()) // doesn't matter when + retVal = ConvertList(nodes, plMaxNode::MakeOccluder, fpErrorMsg, fSettings); + if(IOK()) // must be after mesh + retVal = ConvertList(nodes, plMaxNode::MakeModifiers, fpErrorMsg, fSettings); + if(IOK()) + retVal = ConvertList(nodes, plMaxNode::ConvertComponents, fpErrorMsg, fSettings); + if(IOK()) + retVal = ConvertList(nodes, plMaxNode::ShadeMesh, fpErrorMsg, fSettings); + + // These may be used by components, so don't close them till the end. + plLightMapGen::Instance().Close(); + hsVertexShader::Instance().Close(); + + plgDispatch::MsgSend(new plTransformMsg(nil, nil, nil, nil)); + plgDispatch::MsgSend(new plDelayedTransformMsg(nil, nil, nil, nil)); + DeInit(); + + return IOK(); + } +#ifndef HS_NO_TRY + catch(plErrorMsg& err) + { + err.Show(); + return false; + } + catch(...) + { + hsMessageBox("Unknown error during convert", "plConvert", hsMessageBoxNormal); + return false; + } +#endif +} + +hsBool plConvert::Init(Interface *ip, plErrorMsg* msg, plConvertSettings *settings) +{ + fInterface = ip; + fpErrorMsg = msg; + fSettings = settings; + + // Move us to time 0, so that things like initial transforms are always consistent with the 0th frame. + // This saves our asses from things like the patch-generation process later + ip->SetTime( 0, false ); + + hsConverterUtils::Instance().Init(true, fpErrorMsg); + plBitmapCreator::Instance().Init(true, fpErrorMsg); + hsMaterialConverter::Instance().Init(true, fpErrorMsg); + hsControlConverter::Instance().Init(fpErrorMsg); + plMeshConverter::Instance().Init(true, fpErrorMsg); + plLayerConverter::Instance().Init(true, fpErrorMsg); + + plGetLocationDlg::Instance().ResetDefaultLocation(); + + fQuit = false; + + return true; +} + +void plConvert::DeInit() +{ + // Undo any autogenerated clusters. + IAutoUnClusterRecur(fInterface->GetRootNode()); + + // clear out the message queue + for (int i = 0; i < fMsgQueue.Count(); i++) + plgDispatch::MsgSend(fMsgQueue[i]); + + fMsgQueue.Reset(); + + hsControlConverter::Instance().DeInit(); + plMeshConverter::Instance().DeInit(); + plLayerConverter::Instance().DeInit(); + // Moving this to the end of writing the files out. Yes, this means that any unused mipmaps still get + // written to disk, including ones loaded on preload, but it's the only way to get shared texture pages + // to work without loading in the entire age worth of reffing objects. - 5.30.2002 mcn +// plBitmapCreator::Instance().DeInit(); + + plNodeCleanupMsg *clean = TRACKED_NEW plNodeCleanupMsg(); + plgDispatch::MsgSend( clean ); +} + +void plConvert::AddMessageToQueue(plMessage* msg) +{ + fMsgQueue.Append(msg); +} + +void plConvert::SendEnvironmentMessage(plMaxNode* pNode, plMaxNode* efxRegion, plMessage* msg, hsBool ignorePhysicals ) +{ + for (int i = 0; i < pNode->NumberOfChildren(); i++) + SendEnvironmentMessage((plMaxNode *)pNode->GetChildNode(i), efxRegion, msg, ignorePhysicals ); + + // don't call ourself... + if (pNode == efxRegion) + return; + + // send the scene object this message: + if (efxRegion->Contains( ((INode*)pNode)->GetNodeTM(hsConverterUtils::Instance().GetTime(pNode->GetInterface())).GetRow(3)) && + pNode->GetSceneObject() && ( !ignorePhysicals || !pNode->IsPhysical() ) ) + msg->AddReceiver( pNode->GetSceneObject()->GetKey() ); +} + +plMaxNode* plConvert::GetRootNode() +{ + return (plMaxNode *)fInterface->GetRootNode(); +} + +BOOL plConvert::IAutoClusterRecur(INode* node) +{ + plMaxNode* maxNode = (plMaxNode*)node; + plComponentBase* comp = maxNode->ConvertToComponent(); + + if( comp && (comp->ClassID() == CLUSTER_COMP_CID) ) + { + plClusterComponent* clust = (plClusterComponent*)comp; + // Cluster decides if it needs autogen + if( clust->AutoGen(fpErrorMsg) ) + return false; + } + int i; + for( i = 0; i < node->NumberOfChildren(); i++ ) + { + if( !IAutoClusterRecur(node->GetChildNode(i)) ) + return false; + } + return true; +} + +BOOL plConvert::IAutoUnClusterRecur(INode* node) +{ + plMaxNode* maxNode = (plMaxNode*)node; + plComponentBase* comp = maxNode->ConvertToComponent(); + + if( comp && (comp->ClassID() == CLUSTER_COMP_CID) ) + { + plClusterComponent* clust = (plClusterComponent*)comp; + // Cluster remembers whether it was autogen'd. + clust->AutoClear(fpErrorMsg); + } + int i; + for( i = 0; i < node->NumberOfChildren(); i++ ) + { + IAutoUnClusterRecur(node->GetChildNode(i)); + } + return true; +} + +bool plConvert::IFindDuplicateNames() +{ + INode *node = fInterface->GetRootNode(); + const char *name = ISearchNames(node, node); + + if (!name) + return false; + + fpErrorMsg->Set(true, + "Error in Conversion of Scene Objects", + "Two objects in the scene share the name '%s'.\nUnique names are necessary during the export process.\n", + name + ); + fpErrorMsg->Show(); + + return true; +} + +// Recursivly search nodes for duplicate names, and return when one is found +const char *plConvert::ISearchNames(INode *node, INode *root) +{ + int count = ICountNameOccurances(root, node->GetName()); + if (count > 1) + return node->GetName(); + + for (int i = 0; i < node->NumberOfChildren(); i++) + { + const char *name = ISearchNames(node->GetChildNode(i), root); + if (name) + return name; + } + + return nil; +} + +// Recursivly search nodes for this name, and return the number of times found +int plConvert::ICountNameOccurances(INode *node, const char *name) +{ + int count = 0; + + if (!stricmp(name, node->GetName())) + count++; + + for (int i = 0; i < node->NumberOfChildren(); i++) + count += ICountNameOccurances(node->GetChildNode(i), name); + + return count; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plConvert.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plConvert.h new file mode 100644 index 00000000..e8d581e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plConvert.h @@ -0,0 +1,115 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plConvert_inc +#define plConvert_inc + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "../pnKeyedObject/plKey.h" + +class plErrorMsg; +class plLocation; +class plMaxNode; +class plMessage; +class plLightMapGen; +class hsVertexShader; + +class plConvertSettings +{ +public: + plConvertSettings() : fSceneViewer(false), fReconvert(false), fDoPreshade(true), fDoLightMap(true), + fLightMapGen(nil), fVertexShader(nil), fPhysicalsOnly(false), fExportPage(nil) {} + + bool fSceneViewer; // Are we converting this for the SceneViewer? + bool fReconvert; // Don't need to set, will be done internally by plConvert + bool fDoPreshade; // Doesn't do preshading if false (flat shades) + bool fDoLightMap; // Reuses available old lightmaps if false, else always generates fresh. + bool fPhysicalsOnly;// Only solid physicals get meshes + const char* fExportPage; // If this isn't nil, only export objects in this page + + plLightMapGen* fLightMapGen; + hsVertexShader* fVertexShader; +}; + +class plConvert +{ +protected: + hsBool fQuit; + plErrorMsg* fpErrorMsg; + Interface* fInterface; + plConvertSettings* fSettings; + hsTArray fMsgQueue; + + plConvert(); + hsBool IMakeSceneObject(INode* node); + plKey IGetRoomKey(INode* node); + plKey INewRoom(INode* node, char roomName[]); + hsBool IOK(); + +public: + static plConvert& Instance(); + UInt32 fWarned; + enum { + kWarnedDecalOnBlendObj = 0x1, + kWarnedBadMaterialOnParticle = 0x2, + kWarnedBadParticle = 0x4, + kWarnedDecalAndNonDecal = 0x8, + kWarnedWrongProj = 0x10, + kWarnedMissingProj = 0x20, + kWarnedDecalOnNonDrawable = 0x40, + kWarnedTooManyParticles = 0x80, + kWarnedParticleVelAndOnePer = 0x100, + kWarnedPhysics = 0x200, + }; + + // Init the converter. Only good for one call of Convert. + hsBool Init(Interface *ip, plErrorMsg* msg, plConvertSettings *settings); + void DeInit(); + + hsBool Convert(); + hsBool Convert(hsTArray& nodes); // Convert a set of nodes (for SceneViewer update) + + plMaxNode* GetRootNode(); + void SendEnvironmentMessage(plMaxNode* pNode, plMaxNode* efxRegion, plMessage* msg, hsBool ignorePhysicals = false); // iterates through scene to find nodes contained by the efxRegion + void AddMessageToQueue(plMessage* msg); + + // Because components don't get the convert settings (too much work to retrofit all of them) + plConvertSettings* GetConvertSettings() { return fSettings; } + bool IsForSceneViewer() { return fSettings->fSceneViewer; } + + // Search for nodes with the same name. Returns true if any are found and stops the export + bool IFindDuplicateNames(); + // IFindDuplicateNames helper functions + const char *ISearchNames(INode *node, INode *root); + int ICountNameOccurances(INode *node, const char *name); + // Does any pre-export generation necessary for distributors, then cleans up after export. + BOOL IAutoClusterRecur(INode* node); + BOOL IAutoUnClusterRecur(INode* node); +}; + + +#endif // plSimpleConvert_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistTree.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistTree.cpp new file mode 100644 index 00000000..8924ae9c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistTree.cpp @@ -0,0 +1,301 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "Max.h" + + +#include "plDistTree.h" + +plDistTree::plDistTree() +: fRoot(-1) +{ +} + +plDistTree::~plDistTree() +{ +} + +void plDistTree::Reset() +{ + fRoot = -1; + fNodes.Reset(); +} + +void plDistTree::AddBoxIData(const Box3& box, const Box3& fade, UInt32 iData) +{ + fRoot = IAddNodeRecur(fRoot, box, fade, iData); +} + +BOOL plDistTree::BoxClear(const Box3& box, const Box3& fade) const +{ + return IBoxClearRecur(fRoot, box, fade); +} + +BOOL plDistTree::PointClear(const Point3& pt, const Box3& fade) const +{ + return IPointClearRecur(fRoot, pt, fade); +} + +BOOL plDistTree::IFadesClear(const Box3& fade0, const Box3& fade1) const +{ + // Only two ways fade can come out non-overlapping. + // Either fade0 fades out before fade1 fades in, or v.v. + + // First case, does fade0 fade out? + if( fade0.Max()[2] > 0 ) + { + // does fade1 fade in? + if( fade1.Min()[2] < 0 ) + { + // Okay they do, does fade0 fade out before fade1 fades in? + if( fade0.Max()[0] <= fade1.Min()[0] ) + return true; + } + } + // Second case, same thing but reversed order + if( fade1.Max()[2] > 0 ) + { + // does fade0 fade in? + if( fade0.Min()[2] < 0 ) + { + // Okay they do, does fade1 fade out before fade0 fades in? + if( fade1.Max()[0] <= fade0.Min()[0] ) + return true; + } + } + return false; +} + +BOOL plDistTree::IBox0ContainsBox1(const Box3& box0, const Box3& box1, const Box3& fade0, const Box3& fade1) const +{ +#ifdef MAX_CONTAINS_WORKS + if( !box0.Contains(box1) ) + return false; +#else MAX_CONTAINS_WORKS + if( (box0.Min()[0] > box1.Min()[0]) + ||(box0.Min()[1] > box1.Min()[1]) + ||(box0.Min()[2] > box1.Min()[2]) + ||(box0.Max()[0] < box1.Max()[0]) + ||(box0.Max()[1] < box1.Max()[1]) + ||(box0.Max()[2] < box1.Max()[2]) ) + return false; +#endif // MAX_CONTAINS_WORKS + + if( IFadesClear(fade0, fade1) ) + return false; + + return true; +} + +BOOL plDistTree::IBoxesClear(const Box3& box0, const Box3& box1) const +{ + return (box0.Min()[0] > box1.Max()[0]) + ||(box0.Max()[0] < box1.Min()[0]) + + ||(box0.Min()[1] > box1.Max()[1]) + ||(box0.Max()[1] < box1.Min()[1]) + + ||(box0.Min()[2] > box1.Max()[2]) + ||(box0.Max()[2] < box1.Min()[2]); +} + +BOOL plDistTree::IBoxClearRecur(Int32 iNode, const Box3& box, const Box3& fade) const +{ + if( iNode < 0 ) + return true; + + if( IBoxesClear(fNodes[iNode].fBox, box) ) + return true; + + if( IFadesClear(fNodes[iNode].fFade, fade) ) + return true; + + if( fNodes[iNode].IsLeaf() ) + return false; + + int i; + for( i = 0; i < 8; i++ ) + { + if( !IBoxClearRecur(fNodes[iNode].fChildren[i], box, fade) ) + return false; + } + return true; +} + +BOOL plDistTree::IPointClearRecur(Int32 iNode, const Point3& pt, const Box3& fade) const +{ + if( iNode < 0 ) + return true; + + if( !fNodes[iNode].fBox.Contains(pt) ) + return true; + + if( IFadesClear(fNodes[iNode].fFade, fade) ) + return true; + + if( fNodes[iNode].IsLeaf() ) + return false; + + int i; + for( i = 0; i < 8; i++ ) + { + if( !IPointClearRecur(fNodes[iNode].fChildren[i], pt, fade) ) + return false; + } + return true; +} + + +Int32 plDistTree::IAddNodeRecur(Int32 iNode, const Box3& box, const Box3& fade, UInt32 iData) +{ + // if iNode < 0, make a node for box and return that. + if( iNode < 0 ) + { + return INextNode(box, fade, iData); + } + + // if the box is contained + // if this node is a leaf, pitch the box + // + // else + // recur on one of 8 children, based on + // box's center relative to node center. + + // if the box doesn't intercect this node, + // replace this node with a node of combined boxes. + // this node becomes sibling of box. + + // if the box does intercect, but isn't contained + // same thing. + + +#if 0 + if( IBox0ContainsBox1(fNodes[iNode].fBox, box, fNodes[iNode].fFade, fade) ) + { + if( !fNodes[iNode].IsLeaf() ) +#else + if( !fNodes[iNode].IsLeaf() && IBox0ContainsBox1(fNodes[iNode].fBox, box, fNodes[iNode].fFade, fade) ) + { +#endif + { + Int32 iChild = IGetChild(fNodes[iNode].fBox, box); + + Int32 iChildNode = IAddNodeRecur(fNodes[iNode].fChildren[iChild], box, fade, iData); + fNodes[iNode].fChildren[iChild] = iChildNode; + + fNodes[iNode].fBox += fNodes[fNodes[iNode].fChildren[iChild]].fBox; + } + return iNode; + } + else + { + return IMergeNodes(iNode, box, fade, iData); + } +} + +Int32 plDistTree::IMergeNodes(Int32 iNode, const Box3& box, const Box3& fade, UInt32 iData) +{ + Box3 parBox = box; + parBox += fNodes[iNode].fBox; + + Int32 pNode = INextNode(parBox, NonFade(), UInt32(-1)); + Int32 iChild = IGetChild(parBox, box); + + Int32 cNode = INextNode(box, fade, iData); + + fNodes[pNode].fChildren[iChild] = cNode; + + // Put the original node in the opposite quadrant from the child. + // This handles the case where one of the bounds completely contains + // the other. The octant structure of the tree isn't relied on, it + // only helps balance the tree. So being wrong here won't hurt anything. + iChild = iChild ^ 0x7; + + fNodes[pNode].fChildren[iChild] = iNode; + + fNodes[pNode].SetIsLeaf(false); + + return pNode; +} + +Int32 plDistTree::IGetChild(const Box3& parent, const Box3& child) const +{ + Point3 parCenter = parent.Center(); + Point3 chiCenter = child.Center(); + + Int32 idx = ((parCenter[0] < chiCenter[0]) << 0) + | ((parCenter[1] < chiCenter[1]) << 1) + | ((parCenter[2] < chiCenter[2]) << 2); + return idx; +} + +Int32 plDistTree::INextNode(const Box3& box, const Box3& fade, UInt32 iData) +{ + Int32 iNode = fNodes.GetCount(); + + fNodes.Push(); + + fNodes[iNode].fFlags = plDistNode::kIsLeaf; + fNodes[iNode].fBox = box; + fNodes[iNode].fFade = fade; + fNodes[iNode].fIData = iData; + fNodes[iNode].fChildren[0] + = fNodes[iNode].fChildren[1] + = fNodes[iNode].fChildren[2] + = fNodes[iNode].fChildren[3] + = fNodes[iNode].fChildren[4] + = fNodes[iNode].fChildren[5] + = fNodes[iNode].fChildren[6] + = fNodes[iNode].fChildren[7] = -1; + + return iNode; +} + +void plDistTree::HarvestBox(const Box3& box, Tab& out) const +{ + IHarvestBoxRecur(fRoot, box, out); +} + +void plDistTree::IHarvestBoxRecur(Int32 iNode, const Box3& box, Tab& out) const +{ + if( iNode < 0 ) + return; + + if( IBoxesClear(fNodes[iNode].fBox, box) ) + return; + + if( fNodes[iNode].IsLeaf() ) + { + out.Append(1, &iNode); + } + else + { + int i; + for( i = 0; i < 8; i++ ) + IHarvestBoxRecur(fNodes[iNode].fChildren[i], box, out); + } +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistTree.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistTree.h new file mode 100644 index 00000000..5f1bfed5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistTree.h @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDistTree_inc +#define plDistTree_inc + +#include "hsTemplates.h" + +class plDistNode +{ +public: + enum { + kIsLeaf = 0x1 + }; + UInt32 fFlags; + + Int32 fChildren[8]; + Box3 fBox; + Box3 fFade; + + union + { + void* fPData; + UInt32 fIData; + }; + + const Box3& GetBox() const { return fBox; } + + BOOL IsLeaf() const { return 0 != (fFlags & kIsLeaf); } + void SetIsLeaf(BOOL on) { if(on)fFlags |= kIsLeaf; else fFlags &= ~kIsLeaf; } +}; + +class plDistTree +{ +protected: + + Int32 fRoot; + + hsLargeArray fNodes; + + Int32 IAddNodeRecur(Int32 iNode, const Box3& box, const Box3& fade, UInt32 iData); + + Int32 IMergeNodes(Int32 iNode, const Box3& box, const Box3& fade, UInt32 iData); + Int32 INextNode(const Box3& box, const Box3& fade, UInt32 iData); + + Int32 IGetChild(const Box3& parent, const Box3& child) const; + + inline BOOL IBoxesClear(const Box3& box0, const Box3& box1) const; + inline BOOL IFadesClear(const Box3& fade0, const Box3& fade1) const; + + BOOL IBox0ContainsBox1(const Box3& box0, const Box3& box1, const Box3& fade0, const Box3& fade1) const; + + BOOL IBoxClearRecur(Int32 iNode, const Box3& box, const Box3& fade) const; + BOOL IPointClearRecur(Int32 iNode, const Point3& pt, const Box3& fade) const; + + void IHarvestBoxRecur(Int32 iNode, const Box3& box, Tab& out) const; + +public: + plDistTree(); + virtual ~plDistTree(); + + void Reset(); + + void AddBoxPData(const Box3& box, const Box3& fade, void* pData=nil) { AddBoxIData(box, fade, UInt32(pData)); } + void AddBoxIData(const Box3& box, const Box3& fade, UInt32 iData=0); + void AddBox(const Box3& box, const Box3& fade=NonFade()) { AddBoxIData(box, fade, 0); } + + BOOL BoxClear(const Box3& box, const Box3& fade) const; + BOOL PointClear(const Point3& pt, const Box3& fade) const; + + BOOL IsEmpty() const { return fRoot < 0; } + + static Box3 NonFade() { return Box3(Point3(0,0,0), Point3(0,0,0)); } + + void HarvestBox(const Box3& box, Tab& out) const; + + const plDistNode& GetBox(Int32 i) const { return fNodes[i]; } +}; + +#endif // plDistTree_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistributor.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistributor.cpp new file mode 100644 index 00000000..28dc4eda --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistributor.cpp @@ -0,0 +1,1467 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsWindows.h" +#include + +#include "Max.h" +#include "stdmat.h" +#include "bmmlib.h" +#include "iparamb2.h" +#include "meshdlib.h" + +#include "../MaxExport/plExportProgressBar.h" +#include "../MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h" +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" + +#include "hsMaterialConverter.h" + +#include "hsTypes.h" +#include "hsGeometry3.h" +#include "hsUtils.h" +#include "../plMath/plTriUtils.h" + +#include "plDistributor.h" +#include "../MaxMain/plMaxNode.h" + +#include "plDistTree.h" + +static const float kMaxHeight = 10.f; + +// Inputs: +// seed mesh - geometric entity to replicate over the surface +// surf mesh - surface to cover with seed data +// spacing - nominal distance between replicants (before randomizing). +// rnd pos radius - replicants spread uniformly about nominal spacing by +- radius +// alignment vector- preferred up direction for replicants +// alignment weight- weight between alignment vector and interpolated face normal +// normal range - amount to randomize the weighted normal +// normal bunch - amount randomized normals bunch around input normal +// rotation range - amount to randomize the azimuthal rotation about the randomized normal +// prob bitmap - describes probability that a seed planted here on the surface will sprout +// prob uvws - mapping from verts to prob bitmap, one per src span vertex +// overall rnd - overall probability that a candidate seed point (passing all other criteria) will be used +// + +static inline hsPoint3& hsP3(const Point3& p) { return *(hsPoint3*)&p; } +static inline hsVector3& hsV3(const Point3& p) { return *(hsVector3*)&p; } +static inline Matrix3 Transpose(const Matrix3& m) +{ + return Matrix3(m.GetColumn3(0), m.GetColumn3(1), m.GetColumn3(2), Point3(0,0,0)); +} + +plDistributor::plDistributor() +{ + IClear(); + fRand.SetSeed(UInt32(this)); +} + +plDistributor::~plDistributor() +{ + Reset(); +} + +void plDistributor::Reset() +{ + // Let stuff go... + if( fSurfObjToDelete ) + fSurfObjToDelete->DeleteThis(); + + // Then clear + IClear(); +} + +void plDistributor::IClear() +{ + fInterface = nil; + + fDistTree = nil; + + fMeshTree.Reset(); + + fSurfNode = nil; + fSurfMesh = nil; + fSurfObjToDelete = nil; + + fRepNodes.ZeroCount(); + + fIsolation = kIsoLow; + fConformity = kConformNone; + fFaceNormals = false; + + fMaxConform = 1.f; + + fSpacing = 5.f; + fRndPosRadius = 2.5f; + + fAlignVec.Set(0,0,1.f); + fAlignWgt = 0; + + fOffsetMin = fOffsetMax = 0; + + fPolarRange = 0; + fTanPolarRange = 0; + fAzimuthRange = hsScalarPI; + + fOverallProb = 1.f; + + fPolarBunch = 0; + + fScaleLock = kLockNone; + fScaleLo.Set(1.f,1.f,1.f); + fScaleHi.Set(1.f,1.f,1.f); + + fProbBitmapTex = nil; + fProbLayerTex = nil; + fProbColorChan = kRed; + + fProbRemapFromLo = 0; + fProbRemapFromHi = 1.f; + fProbRemapToLo = 0; + fProbRemapToHi = 1.f; + + fAngProbVec.Set(0.f, 0.f, 1.f); + fAngProbHi = fAngProbLo = 0; + fAngProbTrans = 0; + + fAltProbHi = fAltProbLo = 0; + fAltProbTrans = 0; + + fFade.pmin = fFade.pmax = Point3(0,0,0); + + fBone = nil; +} + +BOOL plDistributor::IGetMesh(INode* node, TriObject*& objToDelete, Mesh*& retMesh) const +{ + if( objToDelete ) + objToDelete->DeleteThis(); + + retMesh = nil; + objToDelete = nil; + + // Get da object + Object *obj = node->EvalWorldState(TimeValue(0)).obj; + if( !obj ) + return false; + + if( !obj->CanConvertToType( triObjectClassID ) ) + return false; + + // Convert to triMesh object + TriObject *meshObj = (TriObject*)obj->ConvertToType(TimeValue(0), triObjectClassID); + if( !meshObj ) + return false; + + if( meshObj != obj ) + objToDelete = meshObj; + + // Get the mesh + Mesh* mesh = &(meshObj->mesh); + if( !mesh->getNumFaces() ) + { + if( objToDelete ) + objToDelete->DeleteThis(); + objToDelete = nil; + return false; + } + + retMesh = mesh; + retMesh->checkNormals(true); + + return true; +} + +void plDistributor::ISetAngProbCosines() const +{ + if( fAngProbHi == fAngProbLo ) + return; + + float maxAng, minAng; + if( fAngProbHi > fAngProbLo ) + { + maxAng = hsScalarDegToRad(fAngProbHi); + minAng = hsScalarDegToRad(fAngProbLo); + } + else + { + maxAng = hsScalarDegToRad(fAngProbLo); + minAng = hsScalarDegToRad(fAngProbHi); + } + + float transAng = hsScalarDegToRad(fAngProbTrans); + if( transAng > (maxAng - minAng) * 0.5f ) + transAng = (maxAng - minAng) * 0.5f; + + float transAngMax = maxAng < hsScalarPI ? transAng : 0; + float transAngMin = minAng > 0 ? transAng : 0; + + fCosAngProbHi = hsCosine(minAng); + fCosAngProbLo = hsCosine(maxAng); + fCosAngProbHiTrans = hsCosine(minAng + transAngMin); + fCosAngProbLoTrans = hsCosine(maxAng - transAngMax); +} + +BOOL plDistributor::ISetSurfaceNode(INode* surfNode) const +{ + if( !IGetMesh(surfNode, fSurfObjToDelete, fSurfMesh) ) + { + return false; + } + fSurfNode = surfNode; + + fSurfToWorld = surfNode->GetObjectTM(TimeValue(0)); + fWorldToSurf = Inverse(fSurfToWorld); + + fSurfToWorldVec = Transpose(fWorldToSurf); + fWorldToSurfVec = Transpose(fSurfToWorld); + + fSurfAngProbVec = FNormalize(fWorldToSurfVec * fAngProbVec); + // This doesn't have anything to do with the surface node, but it + // does have to do with the SurfAngProbVec, and this is as good a + // place as any to do it. + ISetAngProbCosines(); + + fSurfAlignVec = FNormalize(fWorldToSurfVec * fAlignVec); + + if( INeedMeshTree() ) + IMakeMeshTree(); + + return true; +} + +BOOL plDistributor::INeedMeshTree() const +{ + switch( fConformity ) + { + case kConformAll: + case kConformHeight: + case kConformCheck: + case kConformBase: + return true; + } + return false; +} + +void plDistributor::IMakeMeshTree() const +{ + fMeshTree.Reset(); + + const Box3 nonFade = fMeshTree.NonFade(); + int i; + for( i = 0; i < fSurfMesh->getNumFaces(); i++ ) + { + Point3 p0 = fSurfMesh->getVert(fSurfMesh->faces[i].getVert(0)) * fSurfToWorld; + Point3 p1 = fSurfMesh->getVert(fSurfMesh->faces[i].getVert(1)) * fSurfToWorld; + Point3 p2 = fSurfMesh->getVert(fSurfMesh->faces[i].getVert(2)) * fSurfToWorld; + + Box3 box(p0, p0); + box += p1; + box += p2; + + fMeshTree.AddBoxIData(box, nonFade, i); + + } +} + +void plDistributor::IFindFaceSet(const Box3& box, Tab& faces) const +{ + Tab distNodes; + fMeshTree.HarvestBox(box, distNodes); + int i; + for( i = 0; i < distNodes.Count(); i++ ) + { + Int32 iFace = Int32(fMeshTree.GetBox(distNodes[i]).fIData); + faces.Append(1, &iFace); + } +} + +BOOL plDistributor::IValidateSettings(INode* surfNode, plMeshCacheTab& cache) const +{ + if( !fInterface ) + return false; + + if( !ISetSurfaceNode(surfNode) ) + return false; + + if( !IReadyRepNodes(cache) ) + return false; + + return true; +} + +BOOL plDistributor::Distribute(INode* surfNode, plDistribInstTab& reps, plMeshCacheTab& cache, plExportProgressBar& bar) const +{ + // Validate current settings. + if( !IValidateSettings(surfNode, cache) ) + return false; + + + return IDistributeOverMesh(reps, cache, bar); +} + +BOOL plDistributor::IDistributeOverMesh(plDistribInstTab& reps, plMeshCacheTab& cache, plExportProgressBar& bar) const +{ + int iUpdate = (fSurfMesh->getNumFaces() >> 4) + 1; + int i; + for( i = 0; i < fSurfMesh->getNumFaces(); i++ ) + { + IDistributeOverFace(i, reps, cache); + + if( ((i / iUpdate) * iUpdate) == i ) + { + if( bar.Update(nil) ) + return false; + } + } + return true; +} + +Point3& plDistributor::IPerturbPoint(Point3& pt) const +{ + pt.x += fRand.RandMinusOneToOne() * fRndPosRadius; + pt.y += fRand.RandMinusOneToOne() * fRndPosRadius; + pt.z += fRand.RandMinusOneToOne() * fRndPosRadius; + return pt; +} + +Box3 plDistributor::ISetupGrid(const Point3& p0, const Point3& p1, const Point3& p2) const +{ + // Add half spacing to max's to protect against precision errors. + Box3 box(p0, p0); + box += p1; + box += p2; + + Point3 mins, maxs; + int i; + for( i = 0; i < 3; i++ ) + { + hsScalar t = box.Min()[i]; + t /= fSpacing; + t = hsFloor(t); + t *= fSpacing; + mins[i] = t; + + t = box.Max()[i]; + t /= fSpacing; + t = hsCeil(t); + t *= fSpacing; + maxs[i] = t + fSpacing*0.5f; + } + box = Box3(mins, maxs); + + return box; +} + +hsBool plDistributor::IFailsProbBitmap(int iFace, const Point3& bary) const +{ + // If we don't have a probability map, or we don't have + // valid coordinates into it, just return false. That is, + // with no valid probability map, everything goes. + int uvwChan = 1; + Matrix3 uvtrans(true); + Bitmap* bm = nil; + UINT filtType = BMM_FILTER_PYRAMID; + if( fProbBitmapTex ) + { + uvwChan = fProbBitmapTex->GetMapChannel(); + fProbBitmapTex->GetUVTransform(uvtrans); + + bm = fProbBitmapTex->GetBitmap(TimeValue(0)); + + if( bm && !bm->HasFilter() ) + { + switch( fProbBitmapTex->GetFilterType() ) + { + default: + case FILTER_PYR: + filtType = BMM_FILTER_PYRAMID; + break; + case FILTER_SAT: + filtType = BMM_FILTER_SUM; + break; + case FILTER_NADA: + filtType = BMM_FILTER_NONE; + break; + } + } + } + else if( fProbLayerTex ) + { + uvwChan = fProbLayerTex->GetMapChannel(); + fProbLayerTex->GetUVTransform(uvtrans); + + bm = fProbLayerTex->GetBitmap(TimeValue(0)); + + } + + if( !bm ) + return false; + + if( !bm->HasFilter() ) + bm->SetFilter(filtType); + + bm->PrepareGChannels(&bm->Storage()->bi); + + if( !fSurfMesh->mapSupport(uvwChan) ) + return false; + + if( !fSurfMesh->mapFaces(uvwChan) || !fSurfMesh->mapVerts(uvwChan) ) + return false; + + // Lookup the appropriate texel value + Point3 uvw; + uvw = fSurfMesh->mapVerts(uvwChan)[fSurfMesh->mapFaces(uvwChan)[iFace].getTVert(0)] * bary[0]; + uvw += fSurfMesh->mapVerts(uvwChan)[fSurfMesh->mapFaces(uvwChan)[iFace].getTVert(1)] * bary[1]; + uvw += fSurfMesh->mapVerts(uvwChan)[fSurfMesh->mapFaces(uvwChan)[iFace].getTVert(2)] * bary[2]; + + uvw = uvw * uvtrans; + + float fu = uvw.x - int(uvw.x); + if( fu < 0 ) + fu += 1.f; + float fv = 1.0f - (uvw.y - int(uvw.y)); + if( fv < 0 ) + fv += 1.f; + float du = 1.f / bm->Width(); + float dv = 1.f / bm->Height(); + + BMM_Color_fl evCol; + bm->GetFiltered(fu, fv, du, dv, &evCol); + + float frac; + switch( fProbColorChan ) + { + case kRed: + frac = evCol.r; + break; + case kGreen: + frac = evCol.g; + break; + case kBlue: + frac = evCol.b; + break; + case kAlpha: + frac = evCol.a; + break; + case kAverageRedGreen: + frac = (evCol.r + evCol.g) / 2.f; + break; + case kAverageRedGreenTimesAlpha: + frac = (evCol.r + evCol.g) / 2.f * evCol.a; + break; + case kAverage: + frac = (evCol.r + evCol.g + evCol.b ) / 3.f; + break; + case kAverageTimesAlpha: + frac = (evCol.r + evCol.g + evCol.b ) / 3.f * evCol.a; + break; + case kMax: + case kMaxColor: + frac = hsMaximum(evCol.r, hsMaximum(evCol.g, evCol.b)); + break; + case kMaxColorTimesAlpha: + frac = hsMaximum(evCol.r, hsMaximum(evCol.g, evCol.b)) * evCol.a; + break; + case kMaxRedGreen: + frac = hsMaximum(evCol.r, evCol.g); + break; + case kMaxRedGreenTimesAlpha: + frac = hsMaximum(evCol.r, evCol.g) * evCol.a; + break; + } + + if( fProbRemapFromHi != fProbRemapFromLo ) + frac = fProbRemapToLo + (frac - fProbRemapFromLo) / (fProbRemapFromHi - fProbRemapFromLo) * (fProbRemapToHi - fProbRemapToLo); + else + frac = frac > fProbRemapFromHi ? fProbRemapToHi : fProbRemapToLo; + + return frac < fRand.RandZeroToOne(); +} + +Point3 plDistributor::IGetSurfaceNormal(int iFace, const Point3& bary) const +{ + fSurfMesh->checkNormals(true); + + if( !fFaceNormals ) + { + Face& face = fSurfMesh->faces[iFace]; + Point3 norm = FNormalize(fSurfMesh->getNormal(face.getVert(0))) * bary[0]; + norm += FNormalize(fSurfMesh->getNormal(face.getVert(1))) * bary[1]; + norm += FNormalize(fSurfMesh->getNormal(face.getVert(2))) * bary[2]; + + return norm; + } + + Point3 faceNorm = fSurfMesh->getFaceNormal(iFace); + return FNormalize(faceNorm); +} + +hsBool plDistributor::IFailsAngProb(int iFace, const Point3& bary) const +{ + if( fAngProbLo == fAngProbHi ) + return false; + + Point3 norm = IGetSurfaceNormal(iFace, bary); + + float dot = DotProd(norm, fSurfAngProbVec); + + if( dot > fCosAngProbHi ) + return true; + if( dot < fCosAngProbLo ) + return true; + + if( dot > fCosAngProbHiTrans ) + { + float prob = fCosAngProbHi - dot; + prob /= fCosAngProbHi - fCosAngProbHiTrans; + return fRand.RandZeroToOne() >= prob; + } + + if( dot < fCosAngProbLoTrans ) + { + float prob = dot - fCosAngProbLo; + prob /= fCosAngProbLoTrans - fCosAngProbLo; + return fRand.RandZeroToOne() >= prob; + } + + return false; + +} + +hsBool plDistributor::IFailsAltProb(int iFace, const Point3& bary) const +{ + if( fAltProbLo == fAltProbHi ) + return false; + + Face& face = fSurfMesh->faces[iFace]; + Point3 pos = fSurfMesh->getVert(fSurfMesh->faces[iFace].getVert(0)) * bary[0]; + pos += fSurfMesh->getVert(fSurfMesh->faces[iFace].getVert(1)) * bary[1]; + pos += fSurfMesh->getVert(fSurfMesh->faces[iFace].getVert(2)) * bary[2]; + + pos = pos * fSurfToWorld; + + if( pos.z > fAltProbHi ) + return true; + if( pos.z < fAltProbLo ) + return true; + if( pos.z < fAltProbLo + fAltProbTrans ) + { + float prob = (pos.z - fAltProbLo) / fAltProbTrans; + return fRand.RandZeroToOne() >= prob; + } + if( pos.z > fAltProbHi - fAltProbTrans ) + { + float prob = (fAltProbHi - pos.z) / fAltProbTrans; + return fRand.RandZeroToOne() >= prob; + } + + return false; + +} + +// If |projGridPt - GridPt| < gridCubeRadius +// and probBitmap->GetPixel(src->UVW(bary)) < RandomZeroToOne() +// Also a generic random factor. +hsBool plDistributor::IProbablyDoIt(int iFace, Point3& del, const Point3& bary) const +{ + if( fRand.RandZeroToOne() >= fOverallProb ) + { + return false; + } + + if( (kIsoNone == fIsolation) || (kIsoLow == fIsolation) ) + { + if( LengthSquared(del) >= fSpacing*fSpacing ) + { + return false; + } + + Point3 faceNorm = fSurfMesh->FaceNormal(iFace, FALSE); + if( DotProd(del, faceNorm) < 0 ) + { + return false; + } + } + + if( IFailsAngProb(iFace, bary) ) + { + return false; + } + + if( IFailsAltProb(iFace, bary) ) + { + return false; + } + + if( IFailsProbBitmap(iFace, bary) ) + { + return false; + } + + return true; +} + +Point3 plDistributor::IPerpAxis(const Point3& p) const +{ + const hsScalar kMinLengthSquared = 1.e-1f; + + int minAx = p.MinComponent(); + Point3 ax(0,0,0); + ax[minAx] = 1.f; + + Point3 perp = p ^ ax; + if( perp.LengthSquared() < kMinLengthSquared ) + { + // hmm, think we might be screwed, but this shouldn't happen. + } + + return perp = perp.FNormalize(); +} + +// Generate local to world from face info (pos, normal, etc) +Matrix3 plDistributor::IGenerateTransform(int iRepNode, int iFace, const Point3& pt, const Point3& bary) const +{ + const float kMinVecLengthSq = 1.e-6f; + Matrix3 l2w(true); + + // First, set the scale + Point3 scale; + switch( fScaleLock ) + { + case kLockX | kLockY: + scale.x = fRand.RandRangeF(fScaleLo.x, fScaleHi.x); + scale.y = scale.x; + scale.z = fRand.RandRangeF(fScaleLo.z, fScaleHi.z); + break; + case kLockX | kLockY | kLockZ: + scale.x = fRand.RandRangeF(fScaleLo.x, fScaleHi.x); + scale.y = scale.z = scale.x; + break; + default: + scale.x = fRand.RandRangeF(fScaleLo.x, fScaleHi.x); + scale.y = fRand.RandRangeF(fScaleLo.y, fScaleHi.y); + scale.z = fRand.RandRangeF(fScaleLo.z, fScaleHi.z); + break; + } + + l2w.Scale(scale); + + // Next up, get the rotation. + // First we'll randomly rotate about local Z + float azimRot = fRand.RandMinusOneToOne() * fAzimuthRange; + Matrix3 azimMat; + azimMat.SetRotateZ(azimRot); + + l2w = l2w * azimMat; + + // Now align with the surface. + // Get the interpolated surface normal. + Point3 surfNorm = IGetSurfaceNormal(iFace, bary); + + Matrix3 repNodeTM = fRepNodes[iRepNode]->GetNodeTM(TimeValue(0)); + + Point3 alignVec = repNodeTM.GetRow(2); + alignVec = alignVec * fWorldToSurfVec; + alignVec = FNormalize(alignVec); + + Point3 norm = surfNorm + (alignVec - surfNorm) * fAlignWgt; + // The norm can come out of this zero length, if the surace normal + // is directly opposite the "natural" up direction and the weight + // is 50% (for example). In that case, this is just a bad place + // to drop this replicant. + if( norm.LengthSquared() < kMinVecLengthSq ) + { + l2w.IdentityMatrix(); + return l2w; + } + norm = norm.Normalize(); + + // Randomize through the cone around that. + Point3 rndNorm = norm; + Point3 rndDir = IPerpAxis(norm); + Point3 rndOut = rndDir ^ norm; + rndDir *= fRand.RandMinusOneToOne(); + float len = hsSquareRoot(1.f - rndDir.LengthSquared()); + rndOut *= len; + if( fRand.RandMinusOneToOne() < 0 ) + rndOut *= -1.f; + Point3 rndPol = rndDir + rndOut; + + float polScale = fRand.RandZeroToOne() * fTanPolarRange; + + // Combine using the bunching factor + polScale = polScale * (1.f - fPolarBunch) + polScale * polScale * fPolarBunch; + + rndPol *= polScale; + rndNorm += rndPol; + norm = rndNorm.Normalize(); + + // Have "up" alignment, now just generate random dir vector perpindicular to up + Point3 dir = repNodeTM.GetRow(1); + dir = dir * fWorldToSurfVec; + Point3 out = dir ^ norm; + if( out.LengthSquared() < kMinVecLengthSq ) + { + if( fAzimuthRange < hsScalarPI * 0.5f ) + { + l2w.IdentityMatrix(); + return l2w; + } + else + { + dir = IPerpAxis(norm); + out = dir ^ norm; + } + } + out = FNormalize(out); + dir = norm ^ out; + + // If our "up" direction points into the surface, return an "up" direction + // tangent to the surface. Also, make the "dir" direction point out from + // the surface. So if the alignVec/fAlignWgt turns the replicant around + // to penetrate the surface, it just lies down instead. + // + // There's an early out here, for the case where the surface normal is + // exactly opposed to the destination normal. This usually means the + // surface normal is directly opposite the alignVec. In that + // case, we just want to bag it. + if( DotProd(norm, surfNorm) < 0 ) + { + dir = surfNorm; + dir = dir.Normalize(); + out = dir ^ norm; + if( out.LengthSquared() < kMinVecLengthSq ) + { + l2w.IdentityMatrix(); + return l2w; + } + out = out.Normalize(); + norm = out ^ dir; + } + + Matrix3 align; + align.Set(out, dir, norm, Point3(0,0,0)); + + l2w = l2w * align; + + + // Lastly, set the position. + Point3 pos = pt; + const float offset = fRand.RandRangeF(fOffsetMin, fOffsetMax); + pos += norm * offset; + l2w.Translate(pos); + + l2w = l2w * fSurfNode->GetObjectTM(TimeValue(0)); + + return l2w; +} + +int plDistributor::ISelectRepNode() const +{ + if( fRepNodes.Count() < 2 ) + return 0; + + int i = fRand.RandRangeI(0, fRepNodes.Count()-1); + + return i; +} + +BOOL plDistributor::ISetupNormals(plMaxNode* node, Mesh* mesh, BOOL radiateNorm) const +{ + const char* dbgNodeName = node->GetName(); + + UVVert *normMap = mesh->mapVerts(kNormMapChan); + int numNormVerts = mesh->getNumMapVerts(kNormMapChan); + if( !mesh->mapSupport(kNormMapChan) || !mesh->mapVerts(kNormMapChan) || !mesh->mapFaces(kNormMapChan) ) + { + mesh->setMapSupport(kNormMapChan); + + mesh->setNumMapVerts(kNormMapChan, mesh->getNumVerts()); + mesh->setNumMapFaces(kNormMapChan, mesh->getNumFaces()); + } + + int i; + if( radiateNorm ) + { + Matrix3 otm = node->GetOTM(); + Matrix3 invOtm = Inverse(otm); + invOtm.SetTrans(Point3(0,0,0)); + invOtm.ValidateFlags(); + + for( i = 0; i < mesh->getNumVerts(); i++ ) + { + Point3 pos = mesh->getVert(i) * otm; + pos = pos * invOtm; + + mesh->setMapVert(kNormMapChan, i, pos); + } + } + else + { + mesh->checkNormals(true); + + for( i = 0; i < mesh->getNumVerts(); i++ ) + { + Point3 norm = mesh->getNormal(i); + + mesh->setMapVert(kNormMapChan, i, norm); + } + } + + TVFace* mapFaces = mesh->mapFaces(kNormMapChan); + Face* faces = mesh->faces; + for( i = 0; i < mesh->getNumFaces(); i++ ) + { + mapFaces[i].setTVerts(faces[i].getVert(0), faces[i].getVert(1), faces[i].getVert(2)); + } + + return true; +} + + +BOOL plDistributor::ISetupSkinWeights(plMaxNode* node, Mesh* mesh, const Point3& flex) const +{ + const char* dbgNodeName = node->GetName(); + + Matrix3 otm = node->GetOTM(); + + Box3 bnd = mesh->getBoundingBox() * otm; + + float meshHeight = bnd.Max().z; + float maxHeight = kMaxHeight; + if( meshHeight > maxHeight ) + maxHeight = meshHeight; + float maxNorm = meshHeight / maxHeight; + + float flexibility = flex[0]; + + UVVert *wgtMap = mesh->mapVerts(kWgtMapChan); + int numWgtVerts = mesh->getNumMapVerts(kWgtMapChan); + if( !mesh->mapSupport(kWgtMapChan) || !mesh->mapVerts(kWgtMapChan) || !mesh->mapFaces(kWgtMapChan) ) + { + mesh->setMapSupport(kWgtMapChan); + + mesh->setNumMapVerts(kWgtMapChan, mesh->getNumVerts()); + mesh->setNumMapFaces(kWgtMapChan, mesh->getNumFaces()); + } + + int i; + for( i = 0; i < mesh->getNumVerts(); i++ ) + { + Point3 pos = mesh->getVert(i) * otm; + float wgt = pos.z / meshHeight; + wgt *= wgt > 0 ? wgt : 0; + wgt *= maxNorm; + wgt *= flexibility; + + pos.x = wgt; + pos.y = wgt; + pos.z = wgt; + + mesh->setMapVert(kWgtMapChan, i, pos); + } + + TVFace* mapFaces = mesh->mapFaces(kWgtMapChan); + Face* faces = mesh->faces; + for( i = 0; i < mesh->getNumFaces(); i++ ) + { + mapFaces[i].setTVerts(faces[i].getVert(0), faces[i].getVert(1), faces[i].getVert(2)); + } + + return true; +} + +BOOL plDistributor::IDuplicate2Sided(plMaxNode* node, Mesh* mesh) const +{ + Mtl* mtl = node->GetMtl(); + + BitArray faces(mesh->getNumFaces()); + + int num2Sided = 0; + + int origNumFaces = mesh->getNumFaces(); + + int i; + for( i = 0; i < mesh->getNumFaces(); i++ ) + { + if( hsMaterialConverter::IsTwoSided(mtl, mesh->faces[i].getMatID()) ) + { + num2Sided++; + faces.Set(i); + } + } + + if( !num2Sided ) + return false; + + MeshDelta meshDelta(*mesh); + meshDelta.CloneFaces(*mesh, faces); + meshDelta.Apply(*mesh); + + BitArray verts(mesh->getNumVerts()); + verts.SetAll(); + const float kWeldThresh = 0.1f; + meshDelta.WeldByThreshold(*mesh, verts, kWeldThresh); + meshDelta.Apply(*mesh); + + hsAssert(origNumFaces + num2Sided == mesh->getNumFaces(), "Whoa, lost or gained, unexpected"); + + for( i = origNumFaces; i < mesh->getNumFaces(); i++ ) + { + meshDelta.FlipNormal(*mesh, i); + } + meshDelta.Apply(*mesh); + + return true; +} + +BOOL plDistributor::IReadyRepNodes(plMeshCacheTab& cache) const +{ + int i; + for( i = 0; i < fRepNodes.Count(); i++ ) + { + Mesh* mesh = nil; + TriObject* obj = nil; + if( IGetMesh(fRepNodes[i], obj, mesh) ) + { + plMaxNode* repNode = (plMaxNode*)fRepNodes[i]; + + int iCache = cache.Count(); + cache.SetCount(iCache + 1); + + cache[iCache].fMesh = TRACKED_NEW Mesh(*mesh); + cache[iCache].fFlex = repNode->GetFlexibility(); + + if( obj ) + obj->DeleteThis(); + + BOOL hasXImp = nil != repNode->GetXImposterComp(); + + ISetupNormals(repNode, cache[iCache].fMesh, hasXImp); + + ISetupSkinWeights(repNode, cache[iCache].fMesh, cache[iCache].fFlex); + } + else + { + fRepNodes.Delete(i, 1); + i--; + } + } + + return fRepNodes.Count() > 0; +} + +void plDistributor::ClearReplicateNodes() +{ + fRepNodes.ZeroCount(); +} + +void plDistributor::AddReplicateNode(INode* node) +{ + fRepNodes.Append(1, &node); +} + +BOOL plDistributor::IProjectVertex(const Point3& pt, const Point3& dir, float maxDist, Tab&faces, Point3& projPt) const +{ + BOOL retVal = false; + plTriUtils triUtil; + int i; + for( i = 0; i < faces.Count(); i++ ) + { + int iFace = faces[i]; + const hsPoint3& p0 = hsP3(fSurfMesh->getVert(fSurfMesh->faces[iFace].getVert(0)) * fSurfToWorld); + const hsPoint3& p1 = hsP3(fSurfMesh->getVert(fSurfMesh->faces[iFace].getVert(1)) * fSurfToWorld); + const hsPoint3& p2 = hsP3(fSurfMesh->getVert(fSurfMesh->faces[iFace].getVert(2)) * fSurfToWorld); + + Point3 plnPt = pt; + if( triUtil.ProjectOntoPlaneAlongVector(p0, p1, p2, hsV3(dir), hsP3(plnPt)) ) + { + Point3 bary = plnPt; + plTriUtils::Bary baryVal = triUtil.ComputeBarycentric(p0, p1, p2, hsP3(plnPt), hsP3(bary)); + if( (plTriUtils::kOutsideTri != baryVal) && (plTriUtils::kDegenerateTri != baryVal) ) + { + float dist = DotProd((pt - plnPt), dir); + if( (dist <= maxDist) && (dist >= -maxDist) ) + { + projPt = plnPt; + maxDist = dist >= 0 ? dist : -dist; + retVal = true; + } + } + } + + } + return retVal; +} + +BOOL plDistributor::IConformCheck(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const +{ + Matrix3 OTM = IOTM(iRepNode); + Mesh* mesh = cache[iRepNode].fMesh; + + Point3 dir = l2w.VectorTransform(Point3(0.f, 0.f, 1.f)); + dir = FNormalize(dir); + + const float kOneOverSqrt2 = 0.707107f; + Point3 scalePt(kOneOverSqrt2, kOneOverSqrt2, 0.f); + scalePt = l2w.VectorTransform(scalePt); + float maxScaledDist = fMaxConform * scalePt.Length(); + + Box3 bnd = mesh->getBoundingBox() * OTM; + bnd = Box3(Point3(bnd.Min().x, bnd.Min().y, -bnd.Max().z), bnd.Max()); + bnd = bnd * l2w; + Tab faces; + IFindFaceSet(bnd, faces); + + int i; + for( i = 0; i < mesh->getNumVerts(); i++ ) + { + Point3 pt = mesh->getVert(i) * OTM; + pt.z = 0; + + pt = pt * l2w; + + Point3 projPt; + if( !IProjectVertex(pt, dir, maxScaledDist, faces, projPt) ) + return false; + } + return true; +} + +BOOL plDistributor::IConformAll(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const +{ + Matrix3 OTM = IOTM(iRepNode); + Mesh* mesh = cache[iRepNode].fMesh; + + Point3 dir = l2w.VectorTransform(Point3(0.f, 0.f, 1.f)); + dir = FNormalize(dir); + + const float kOneOverSqrt2 = 0.707107f; + Point3 scalePt(kOneOverSqrt2, kOneOverSqrt2, 0.f); + scalePt = l2w.VectorTransform(scalePt); + float maxScaledDist = fMaxConform * scalePt.Length(); + + Box3 bnd = mesh->getBoundingBox() * OTM; + bnd = Box3(Point3(bnd.Min().x, bnd.Min().y, -bnd.Max().z), bnd.Max()); + bnd = bnd * l2w; + Tab faces; + IFindFaceSet(bnd, faces); + + // l2w, iRepNode, cache, &iCache, maxScaledDist, dir + iCache = cache.Count(); + cache.SetCount(iCache + 1); + cache[iCache] = cache[iRepNode]; + cache[iCache].fMesh = TRACKED_NEW Mesh(*mesh); + + mesh = cache[iCache].fMesh; + + Matrix3 v2w = OTM * l2w; + Matrix3 w2v = Inverse(v2w); + + BOOL retVal = true; + int i; + for( i = 0; i < mesh->getNumVerts(); i++ ) + { + Point3 pt = mesh->getVert(i) * OTM; + pt.z = 0; + + pt = pt * l2w; + + Point3 projPt; + if( !IProjectVertex(pt, dir, maxScaledDist, faces, projPt) ) + { + retVal = false; + break; + } + + Point3 del = w2v.VectorTransform(projPt - pt); + mesh->getVert(i) += del; + } + if( !retVal ) + { +// delete cache[iCache].fMesh; + delete mesh; + cache.SetCount(iCache); + iCache = iRepNode; + } + return retVal; +} + +BOOL plDistributor::IConformHeight(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const +{ + Matrix3 OTM = IOTM(iRepNode); + Mesh* mesh = cache[iRepNode].fMesh; + + Point3 dir = l2w.VectorTransform(Point3(0.f, 0.f, 1.f)); + dir = FNormalize(dir); + + const float kOneOverSqrt2 = 0.707107f; + Point3 scalePt(kOneOverSqrt2, kOneOverSqrt2, 0.f); + scalePt = l2w.VectorTransform(scalePt); + float maxScaledDist = fMaxConform * scalePt.Length(); + + Box3 bnd = mesh->getBoundingBox() * OTM; + bnd = Box3(Point3(bnd.Min().x, bnd.Min().y, -bnd.Max().z), bnd.Max()); + bnd = bnd * l2w; + Tab faces; + IFindFaceSet(bnd, faces); + + // l2w, iRepNode, cache, &iCache, maxScaledDist, dir + iCache = cache.Count(); + cache.SetCount(iCache + 1); + cache[iCache] = cache[iRepNode]; + cache[iCache].fMesh = TRACKED_NEW Mesh(*mesh); + + mesh = cache[iCache].fMesh; + + Matrix3 v2w = OTM * l2w; + Matrix3 w2v = Inverse(v2w); + + + float maxZ = (mesh->getBoundingBox() * OTM).Max().z; + + BOOL retVal = true; + int i; + for( i = 0; i < mesh->getNumVerts(); i++ ) + { + Point3 pt = mesh->getVert(i) * OTM; + + float conScale = 1.f - pt.z / maxZ; + if( conScale > 1.f ) + conScale = 1.f; + else if( conScale < 0 ) + conScale = 0; + + pt.z = 0; + + pt = pt * l2w; + + Point3 projPt; + if( !IProjectVertex(pt, dir, maxScaledDist, faces, projPt) ) + { + retVal = false; + break; + } + + Point3 del = w2v.VectorTransform(projPt - pt); + del *= conScale; + mesh->getVert(i) += del; + } + if( !retVal ) + { + delete cache[iCache].fMesh; + cache.SetCount(iCache); + iCache = iRepNode; + } + return retVal; +} + +BOOL plDistributor::IConformBase(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const +{ + Matrix3 OTM = IOTM(iRepNode); + Mesh* mesh = cache[iRepNode].fMesh; + + Point3 dir = l2w.VectorTransform(Point3(0.f, 0.f, 1.f)); + dir = FNormalize(dir); + + const float kOneOverSqrt2 = 0.707107f; + Point3 scalePt(kOneOverSqrt2, kOneOverSqrt2, 0.f); + scalePt = l2w.VectorTransform(scalePt); + float maxScaledDist = fMaxConform * scalePt.Length(); + + Box3 bnd = mesh->getBoundingBox() * OTM; + bnd = Box3(Point3(bnd.Min().x, bnd.Min().y, -bnd.Max().z), bnd.Max()); + bnd = bnd * l2w; + Tab faces; + IFindFaceSet(bnd, faces); + + // l2w, iRepNode, cache, &iCache, maxScaledDist, dir + iCache = cache.Count(); + cache.SetCount(iCache + 1); + cache[iCache] = cache[iRepNode]; + cache[iCache].fMesh = TRACKED_NEW Mesh(*mesh); + + mesh = cache[iCache].fMesh; + + Matrix3 v2w = OTM * l2w; + Matrix3 w2v = Inverse(v2w); + + + float maxZ = (mesh->getBoundingBox() * OTM).Max().z; + + BOOL retVal = true; + int i; + for( i = 0; i < mesh->getNumVerts(); i++ ) + { + Point3 pt = mesh->getVert(i) * OTM; + + const float kMaxConformZ = 0.5f; + if( pt.z < kMaxConformZ ) + { + pt.z = 0; + + pt = pt * l2w; + + Point3 projPt; + if( !IProjectVertex(pt, dir, maxScaledDist, faces, projPt) ) + { + retVal = false; + break; + } + + Point3 del = w2v.VectorTransform(projPt - pt); + mesh->getVert(i) += del; + } + } + if( !retVal ) + { + delete cache[iCache].fMesh; + cache.SetCount(iCache); + iCache = iRepNode; + } + return retVal; +} + +BOOL plDistributor::IConform(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const +{ + iCache = iRepNode; + switch( fConformity ) + { + case kConformAll: + return IConformAll(l2w, iRepNode, cache, iCache); + case kConformHeight: + return IConformHeight(l2w, iRepNode, cache, iCache); + case kConformCheck: + return IConformCheck(l2w, iRepNode, cache, iCache); + case kConformBase: + return IConformBase(l2w, iRepNode, cache, iCache); + } + return true; +} + + +// Clone the replicant and set its transform appropriately. Should set the plMaxNode property +// that the node's spans are collapseable? No, we'll make a separate component for that. +void plDistributor::IReplicate(Matrix3& l2w, int iRepNode, plDistribInstTab& reps, plMeshCache& mCache) const +{ + INode* repNode = fRepNodes[iRepNode]; + plDistribInstance inst; + inst.fNode = repNode; + inst.fNodeTM = l2w; + inst.fObjectTM = repNode->GetObjectTM(TimeValue(0)) * Inverse(repNode->GetNodeTM(TimeValue(0))) * l2w; + + inst.fMesh = mCache.fMesh; + + inst.fFlex = mCache.fFlex; + + inst.fFade = fFade; + + inst.fBone = fBone; + + inst.fRigid = fRigid; + + reps.Append(1, &inst); +} + +void plDistributor::IDistributeOverFace(int iFace, plDistribInstTab& reps, plMeshCacheTab& cache) const +{ + Point3 p0 = fSurfMesh->getVert(fSurfMesh->faces[iFace].getVert(0)); + Point3 p1 = fSurfMesh->getVert(fSurfMesh->faces[iFace].getVert(1)); + Point3 p2 = fSurfMesh->getVert(fSurfMesh->faces[iFace].getVert(2)); + + Box3 grid = ISetupGrid(p0, p1, p2); + + hsScalar delta = fSpacing; + + hsScalar x, y, z; + for( x = grid.Min().x; x < grid.Max().x; x += delta ) + { + for( y = grid.Min().y; y < grid.Max().y; y += delta ) + { + for( z = grid.Min().z; z < grid.Max().z; z += delta ) + { + Point3 pt(x, y, z); + + pt = IPerturbPoint(pt); + + Point3 plnPt = pt; + Point3 bary; + + // Get Barycentric coord of projection of grid pt onto face + plTriUtils triUtil; + plTriUtils::Bary baryVal = triUtil.ComputeBarycentricProjection(hsP3(p0), hsP3(p1), hsP3(p2), hsP3(plnPt), hsP3(bary)); + if( !(baryVal & (plTriUtils::kOutsideTri | plTriUtils::kDegenerateTri)) ) + { + int iRepNode = ISelectRepNode(); + + Matrix3 l2w = IGenerateTransform(iRepNode, iFace, plnPt, bary); + + // l2w as ident means this position turned out to be not so good afterall. + if( l2w.IsIdentity() ) + { + continue; + } + + Box3 clearBox; + if( !ISpaceClear(iRepNode, l2w, clearBox, cache) ) + { + continue; + } + + int iCacheNode = iRepNode; + if( !IConform(l2w, iRepNode, cache, iCacheNode) ) + { + continue; + } + + // If |projGridPt - GridPt| < gridCubeRadius + // and probBitmap->GetPixel(src->UVW(bary)) < RandomZeroToOne() + // Also a generic random factor. + if( IProbablyDoIt(iFace, pt - plnPt, bary) ) + { + IReplicate(l2w, iRepNode, reps, cache[iCacheNode]); + + IReserveSpace(clearBox); + } + } + } + } + } +} + +Matrix3 plDistributor::IInvOTM(int iRepNode) const +{ + // objectTM = otm * nodeTM + // invOTM * objectTM = nodeTM + // invOTM = nodeTM * invObjectTM + INode* repNode = fRepNodes[iRepNode]; + Matrix3 objectTM = repNode->GetObjectTM(TimeValue(0)); + Matrix3 nodeTM = repNode->GetNodeTM(TimeValue(0)); + Matrix3 invOTM = nodeTM * Inverse(objectTM); + return invOTM; +} + +Matrix3 plDistributor::IOTM(int iRepNode) const +{ + // objectTM = otm * nodeTM + // objectTM * Inverse(nodeTM) = otm + INode* repNode = fRepNodes[iRepNode]; + Matrix3 objectTM = repNode->GetObjectTM(TimeValue(0)); + Matrix3 nodeTM = repNode->GetNodeTM(TimeValue(0)); + Matrix3 OTM = objectTM * Inverse(nodeTM); + return OTM; +} + +BOOL plDistributor::ISpaceClear(int iRepNode, const Matrix3& l2w, Box3& clearBox, plMeshCacheTab& cache) const +{ + if( !fDistTree ) + return true; + + // If we have high isolation, + // clearBox = Box3(Point3(-fSpacing*0.5f, -fSpacing*0.5f, 0), Point3(fSpacing*0.5f, fSpacing*0.5f, fSpacing)); + // Else if we have medium isolation + // clearBox = cache[iRepNode]->getBoundingBox(); // The mesh's bounds + // Else if we have low isolation or None isolation + // clearBox = Box3(Point3(-kSmallSpace, -kSmallSpace, 0), Point3(kSmallSpace, kSmallSpace, kSmallSpace)); // kSmallSpace ~= 0.5f or one or something + + // We want to set up the box (for high, low and none) in Post OTM space. So instead of multiplying + // by l2w, we want to multiply box = box * invOTM * l2w (because l2w already has OTM folded in). When using + // the mesh bounds (Medium), l2w is the right transform. + // objectTM = otm * nodeTM + // invOTM * objectTM = nodeTM + // invOTM = nodeTM * invObjectTM + const float kSmallSpace = 0.5f; + switch( fIsolation ) + { + case kIsoHigh: + { + INode* repNode = fRepNodes[iRepNode]; + Matrix3 objectTM = repNode->GetObjectTM(TimeValue(0)); + Matrix3 nodeTM = repNode->GetNodeTM(TimeValue(0)); + Matrix3 invOTM = nodeTM * Inverse(objectTM); + clearBox = Box3(Point3(-fSpacing*0.5f, -fSpacing*0.5f, 0.f), Point3(fSpacing*0.5f, fSpacing*0.5f, fSpacing)); + clearBox = clearBox * invOTM; + } + break; + case kIsoMedium: + clearBox = cache[iRepNode].fMesh->getBoundingBox(); // The mesh's bounds + break; + case kIsoLow: + case kIsoNone: + default: + { + INode* repNode = fRepNodes[iRepNode]; + Matrix3 objectTM = repNode->GetObjectTM(TimeValue(0)); + Matrix3 nodeTM = repNode->GetNodeTM(TimeValue(0)); + Matrix3 invOTM = nodeTM * Inverse(objectTM); + clearBox = Box3(Point3(-kSmallSpace, -kSmallSpace, 0.f), Point3(kSmallSpace, kSmallSpace, kSmallSpace)); + clearBox = clearBox * invOTM; + } + break; + } + + clearBox = clearBox * l2w; + + return fDistTree->BoxClear(clearBox, fFade); +} + +void plDistributor::IReserveSpace(const Box3& clearBox) const +{ + // if isolation isn't None, add the box. + if( fDistTree && (fIsolation != kIsoNone) ) + fDistTree->AddBox(clearBox, fFade); +} + +UInt32 plDistributor::GetRandSeed() const +{ + return fRand.GetSeed(); +} + +void plDistributor::SetRandSeed(int seed) +{ + fRand.SetSeed(seed); +} + +void plDistributor::SetPolarRange(float deg) +{ + fPolarRange = hsScalarDegToRad(deg); + fTanPolarRange = tan(fPolarRange); +} + +void plDistributor::SetProbabilityBitmapTex(BitmapTex* t) +{ + fProbLayerTex = nil; + fProbBitmapTex = t; +} + +void plDistributor::SetProbabilityLayerTex(plLayerTex* t) +{ + fProbBitmapTex = nil; + fProbLayerTex = t; +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistributor.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistributor.h new file mode 100644 index 00000000..627f2040 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plDistributor.h @@ -0,0 +1,405 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plDistributor_inc +#define plDistributor_inc + +#include "../plMath/plRandom.h" +#include "plDistTree.h" + +class INode; +class Mesh; +class TriObject; + +class BitmapTex; +class plLayerTex; +class plExportProgressBar; +class plMaxNode; +class plDistTree; + +// To Use: +// +// First you must set the interface +// Second, you must set the node(s) to be replicated. +// If multiple replicants, they will be randomly selected at each plant site. +// Set the spacing. Default is 10 or so, but shouldn't be counted on. +// Set the spacing range. Set it to less than half the spacing to prevent +// replicants piling up on each other. +// Rather than increasing the spacing range (which causes pileup), you can increase +// the randomness of placement with the overall probability factor. Reducing it +// means fewer grid points will be used, so placement seems more random +// +// Options: +// PolarRange - defines the cone about the surface normal to randomly fill for up direction +// PolarBunch - defines tendency to bunch around the center of PolarRange cone, +// with zero being uniform distribution, and one being all on cone axis. +// AlignmentVector - defines a world space preferred up orientation. +// AlignmentWeight - blend factor between random normal (from polar stuff) and +// above AlignmentVector. +// ScaleRange - defines range of non-uniform scale to apply to each replicant. +// ProbabilityTexmap - A BitmapTex which maps the probability of an instance +// taking root at any point on the surface. Various interpretations of the +// map available (see ProbabilityChan). If the surface mesh doesn't have +// the appropriate uvw mapping for the map, the map will be ignored. +// ProbabilityChan - defines interpretation of texel value from ProbTexmap +// into a probability. ColorChan enum types are pretty self-explanatory. +// In all cases, a higher texel channel value means more likely of a +// replicant taking root there. +// +// Finally, call Distrubute() with a node containing a surface to be populated. +// +// Modifying. Were actually creating the new node instances, but then we just +// want to re-pack them into clusters anyway. So all we really need is to +// know which template (fRepNodes) and what transform to use for it. We can +// use that to make our clusters, without bogging Max down with adding and +// deleting a gazillion INodes. + +class plDistribInstance +{ +public: + INode* fNode; + Matrix3 fNodeTM; + Matrix3 fObjectTM; + + INode* fBone; + BOOL fRigid; + + Box3 fFade; + + Point3 fFlex; + + Mesh* fMesh; +}; + +class plDistribInstTab : public Tab +{ +}; + +class plMeshCache +{ +public: + Mesh* fMesh; + Point3 fFlex; + + plMeshCache() {} +}; + +class plMeshCacheTab : public Tab +{ +}; + +class plDistributor +{ +public: + enum ColorChan + { + kRed = 0x1, + kGreen = 0x2, + kBlue = 0x4, + kAlpha = 0x8, + kAverageRedGreen = kRed | kGreen, + kAverageRedGreenTimesAlpha = kRed | kGreen | kAlpha, + kAverage = kRed | kGreen | kBlue, + kAverageTimesAlpha = kAverage | kAlpha, + kMax = 0x100, + kMaxColor = kMax | kRed | kGreen | kBlue, + kMaxColorTimesAlpha = kMaxColor | kAlpha, + kMaxRedGreen = kMax | kRed | kGreen, + kMaxRedGreenTimesAlpha = kMaxRedGreen | kAlpha + }; + enum + { + kLockNone = 0x0, + kLockX = 0x1, + kLockY = 0x2, + kLockZ = 0x4 + }; + enum + { + kWgtMapChan = 66, + kNormMapChan = 67 + }; + enum IsoType + { + kIsoNone, + kIsoLow, + kIsoMedium, + kIsoHigh, + + kIsoMax = kIsoHigh + }; + enum ConformType + { + kConformNone, + kConformAll, + kConformHeight, + kConformCheck, + kConformBase + }; +protected: + mutable INode* fSurfNode; + mutable Mesh* fSurfMesh; + mutable TriObject* fSurfObjToDelete; + + mutable INodeTab fRepNodes; + + mutable plDistTree* fDistTree; + mutable plDistTree fMeshTree; + + Interface* fInterface; + + IsoType fIsolation; + ConformType fConformity; + BOOL fFaceNormals; + + float fSpacing; + float fRndPosRadius; + + Point3 fAlignVec; + float fAlignWgt; + + float fOffsetMin; + float fOffsetMax; + + Point3 fAngProbVec; + float fAngProbLo; + float fAngProbHi; + float fAngProbTrans; + + float fAltProbLo; + float fAltProbHi; + float fAltProbTrans; + + float fPolarRange; + float fTanPolarRange; + float fAzimuthRange; + + float fOverallProb; + + float fPolarBunch; + + ULONG fScaleLock; + Point3 fScaleLo; + Point3 fScaleHi; + + BitmapTex* fProbBitmapTex; + plLayerTex* fProbLayerTex; + ColorChan fProbColorChan; + + float fProbRemapFromLo; + float fProbRemapFromHi; + float fProbRemapToLo; + float fProbRemapToHi; + + float fMaxConform; // in feet + + Box3 fFade; + INode* fBone; + BOOL fRigid; + + // Temps used during processing. + mutable Matrix3 fSurfToWorld; + mutable Matrix3 fWorldToSurf; + mutable Matrix3 fSurfToWorldVec; + mutable Matrix3 fWorldToSurfVec; + mutable Point3 fSurfAlignVec; + mutable Point3 fSurfAngProbVec; + mutable plRandom fRand; + mutable float fCosAngProbHi; + mutable float fCosAngProbHiTrans; + mutable float fCosAngProbLo; + mutable float fCosAngProbLoTrans; + + void ISetAngProbCosines() const; + BOOL ISetSurfaceNode(INode* node) const; + BOOL IGetMesh(INode* node, TriObject*& objToDelete, Mesh*& retMesh) const; + + BOOL INeedMeshTree() const; + void IMakeMeshTree() const; + void IFindFaceSet(const Box3& box, Tab& faces) const; + BOOL IProjectVertex(const Point3& pt, const Point3& dir, float maxDist, Tab&faces, Point3& projPt) const; + BOOL IConform(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const; + BOOL IConformHeight(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const; + BOOL IConformAll(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const; + BOOL IConformCheck(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const; + BOOL IConformBase(Matrix3& l2w, int iRepNode, plMeshCacheTab& cache, int& iCache) const; + + Matrix3 IOTM(int iRepNode) const; + Matrix3 IInvOTM(int iRepNode) const; + + Matrix3 IGenerateTransform(int iRepNode, int iFace, const Point3& pt, const Point3& bary) const; + hsBool IProbablyDoIt(int iFace, Point3& del, const Point3& bary) const; + hsBool IFailsAltProb(int iFace, const Point3& bary) const; + hsBool IFailsAngProb(int iFace, const Point3& bary) const; + hsBool IFailsProbBitmap(int iFace, const Point3& bary) const; + Box3 ISetupGrid(const Point3& p0, const Point3& p1, const Point3& p2) const; + Point3& IPerturbPoint(Point3& pt) const; + int ISelectRepNode() const; + Point3 IPerpAxis(const Point3& p) const; + Point3 IGetSurfaceNormal(int iFace, const Point3& bary) const; + + BOOL ISpaceClear(int iRepNode, const Matrix3& l2w, Box3& clearBox, plMeshCacheTab& cache) const; + void IReserveSpace(const Box3& clearBox) const; + + void IReplicate(Matrix3& l2w, int iRep, plDistribInstTab& reps, plMeshCache& mCache) const; + void IDistributeOverFace(int iFace, plDistribInstTab& reps, plMeshCacheTab& cache) const; + BOOL IDistributeOverMesh(plDistribInstTab& reps, plMeshCacheTab& cache, plExportProgressBar& bar) const; + + void IClear(); + BOOL IValidateSettings(INode* surfNode, plMeshCacheTab& cache) const; + + BOOL ISetupNormals(plMaxNode* node, Mesh* mesh, BOOL radiateNorm) const; + BOOL IDuplicate2Sided(plMaxNode* node, Mesh* mesh) const; + BOOL ISetupSkinWeights(plMaxNode* node, Mesh* mesh, const Point3& flex) const; + BOOL IReadyRepNodes(plMeshCacheTab& cache) const; +public: + + plDistributor(); + virtual ~plDistributor(); + + void SetTheInterface(Interface* i) { fInterface = i; } + Interface* GetTheInterface() const { return fInterface; } + + BOOL Distribute(INode* surfNode, plDistribInstTab& replicants, plMeshCacheTab& cache, plExportProgressBar& bar) const; + + void Reset(); + UInt32 GetRandSeed() const; + void SetRandSeed(int seed); + + void ClearReplicateNodes(); + void AddReplicateNode(INode* node); + int GetNumReplicateNodes() const { return fRepNodes.Count(); } + INode* GetReplicateNode(int i) const { return fRepNodes[i]; } + + INode* GetSurfaceNode() const { return fSurfNode; } + + void SetSpacing(float f) { fSpacing = f; } + float GetSpacing() const { return fSpacing; } + + void SetSpacingRange(float f) { fRndPosRadius = f; } + float GetSpacingRange() const { return fRndPosRadius; } + + void SetAlignmentVec(const Point3& v) { fAlignVec = v; } + Point3 GetAlignmentVec() const { return fAlignVec; } + + void SetAlignmentWeight(float w) { fAlignWgt = w / 100.f; } + float GetAlignmentWeight() const { return fAlignWgt * 100.f; } + + void SetPolarRange(float deg); + float GetPolarRange() const { return hsScalarRadToDeg(fPolarRange); } + + void SetAzimuthRange(float deg) { fAzimuthRange = hsScalarDegToRad(deg); } + float GetAzimuthRange() const { return hsScalarRadToDeg(fAzimuthRange); } + + void SetOverallProb(float percent) { fOverallProb = percent/100.f; } + float GetOverallProb() const { return fOverallProb * 100.f; } + + void SetAngleProbVec(const Point3& v) { fAngProbVec = v; } + Point3 GetAngleProbVec() const { return fAngProbVec; } + + void SetAngleProbHi(float deg) { fAngProbHi = deg; } + float GetAngleProbHi() const { return fAngProbHi; } + + void SetAngleProbLo(float deg) { fAngProbLo = deg; } + float GetAngleProbLo() const { return fAngProbLo; } + + void SetAngleProbTransition(float deg) { fAngProbTrans = deg; } + float GetAngleProbTransition() const { return fAngProbTrans; } + + void SetMinAltitude(float feet) { fAltProbLo = feet; } + float GetMinAltitude() const { return fAltProbLo; } + + void SetMaxAltitude(float feet) { fAltProbHi = feet; } + float GetMaxAltitude() const { return fAltProbHi; } + + void SetAltitudeTransition(float feet) { fAltProbTrans = feet; } + float GetAltitudeTransition() const { return fAltProbTrans; } + + void SetPolarBunch(float b) { fPolarBunch = b/100.f; } + float GetPolarBunch() const { return fPolarBunch * 100.f; } + + void SetScaleRange(const Point3& lo, const Point3& hi) { fScaleLo = lo; fScaleHi = hi; } + Point3 GetScaleRangeMin() const { return fScaleLo; } + Point3 GetScaleRangeMax() const { return fScaleHi; } + + void SetProbabilityBitmapTex(BitmapTex* t); + BitmapTex* GetProbabilityBitmapTex() const { return fProbBitmapTex; } + + void SetProbabilityLayerTex(plLayerTex* t); + plLayerTex* GetProbabilityLayerTex() const { return fProbLayerTex; } + + void SetProbabilityChan(ColorChan c) { fProbColorChan = c; } + ColorChan GetProbabilityChan() const { return fProbColorChan; } + + void SetProbabilityRemapFromLo(float f) { fProbRemapFromLo = f / 255.f; } + float GetProbabilityRemapFromLo() const { return fProbRemapFromLo * 255.f; } + + void SetProbabilityRemapFromHi(float f) { fProbRemapFromHi = f / 255.f; } + float GetProbabilityRemapFromHi() const { return fProbRemapFromHi * 255.f; } + + void SetProbabilityRemapToLo(float f) { fProbRemapToLo = f / 255.f; } + float GetProbabilityRemapToLo() const { return fProbRemapToLo * 255.f; } + + void SetProbabilityRemapToHi(float f) { fProbRemapToHi = f / 255.f; } + float GetProbabilityRemapToHi() const { return fProbRemapToHi * 255.f; } + + // We don't really know what fades are, they're just something we're handed that + // we stamp on every distribInstance we generate. See plDistribComponent.h. + void SetFade(const Box3& fade) { fFade = fade; } + Box3 GetFade() const { return fFade; } + + void SetBone(INode* b) { fBone = b; } + INode* GetBone() const { return fBone; } + + void SetRigid(BOOL b) { fRigid = b; } + BOOL GetRigid() const { return fRigid; } + + void SetScaleLock(ULONG f) { fScaleLock = f; } + ULONG GetScaleLock() const { return fScaleLock; } + + void SetDistTree(plDistTree* dt) { fDistTree = dt; } + plDistTree* GetDistTree() const { return fDistTree; } + + void SetIsolation(IsoType t) { fIsolation = t; } + IsoType GetIsolation() const { return fIsolation; } + + void SetConformity(ConformType t) { fConformity = t; } + ConformType GetConformity() const { return fConformity; } + + void SetMaxConform(float feet) { fMaxConform = feet; } + float GetMaxConform() const { return fMaxConform; } + + void SetMinOffset(float feet) { fOffsetMin = feet; } + float GetMinOffset() const { return fOffsetMin; } + + void SetMaxOffset(float feet) { fOffsetMax = feet; } + float GetMaxOffset() const { return fOffsetMax; } + + void SetFaceNormals(BOOL on=true) { fFaceNormals = on; } + BOOL GetFaceNormals() const { return fFaceNormals; } +}; + +#endif // plDistributor_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLayerConverter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLayerConverter.cpp new file mode 100644 index 00000000..9cd23d3b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLayerConverter.cpp @@ -0,0 +1,1118 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plLayerConverter - Utility class that converts plPlasmaMAXLayers into // +// other stuff. // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 1.13.2002 mcn - Created. // +// // +//// Notes //////////////////////////////////////////////////////////////////// +// // +// If you add a new PlasmaMAXLayer type, you need to add the appropriate // +// test in ConvertTexmap() as well as your own function to do the // +// conversion. Messy, but thanks to the dependencies of the convert process,// +// we can't do it the nice, pretty, OOP way. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsWindows.h" +#include "Max.h" +#include "stdmat.h" +#include "istdplug.h" +#include "iparamb2.h" +#include "iparamm2.h" + +#include "hsUtils.h" +#include "hsExceptionStack.h" + +#include "plLayerConverter.h" + +#include "../MaxConvert/hsMaxLayerBase.h" +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../MaxConvert/hsMaterialConverter.h" +#include "../MaxConvert/plBitmapCreator.h" +#include "hsResMgr.h" +#include "../MaxExport/plErrorMsg.h" +#include "../MaxMain/plMaxNode.h" + +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnSceneObject/plSceneObject.h" +#include "../plSurface/plLayerInterface.h" +#include "../plSurface/plLayer.h" +#include "../pnMessage/plRefMsg.h" +#include "../pnMessage/plObjRefMsg.h" +#include "../plMessage/plLayRefMsg.h" +#include "../plDrawable/plGeometrySpan.h" + +#include "../plGImage/plMipmap.h" +#include "../plGImage/plDynamicTextMap.h" +#include "../plGImage/plCubicEnvironmap.h" +#include "../plPipeline/plCubicRenderTarget.h" +#include "../plPipeline/plCubicRenderTargetModifier.h" +#include "../plPipeline/plDynamicEnvMap.h" + +#include "../pfSurface/plLayerAVI.h" +#include "../pfSurface/plLayerBink.h" + +#include "../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h" +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" +#include "../MaxPlasmaMtls/Layers/plLayerTexBasicPB.h" +#include "../MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h" +#include "../MaxPlasmaMtls/Layers/plStaticEnvLayer.h" +#include "../MaxPlasmaMtls/Layers/plDynamicEnvLayer.h" +#include "../MaxPlasmaMtls/Layers/plDynamicTextLayer.h" +#include "../MaxPlasmaMtls/Layers/plAngleAttenLayer.h" +#include "../MaxPlasmaMtls/Layers/plMAXCameraLayer.h" + +#include "../MaxComponent/plComponent.h" +#include "../MaxComponent/plWaterComponent.h" +#include "../pfCamera/plCameraModifier.h" + +//// Local Static Stuff /////////////////////////////////////////////////////// + +namespace +{ + enum { + kWarnedTooManyUVs = 0x01, + kWarnedNoBaseTexture = 0x02, + kWarnedUpperTextureMissing = 0x04, + kWarnedNoUpperTexture = 0x08, + }; + + const char sWarnBaseTextureMissing[] = "The object \"%s\"'s material has a base layer that is assigned texture \"%s\", but the texture file is missing. " + "This can cause unwanted effects during runtime."; + const char sWarnUpperTextureMissing[] = "The object \"%s\"'s material has an upper layer that is assigned texture \"%s\", but the texture file is missing. " + "This is not supported in the engine, so the upper layer will be ignored."; + const char sWarnNoUpperTexture[] = "The object \"%s\"'s material has an uppper layer that is not assigned a texture. " + "This is not supported in the engine, so the upper layer will be disabled."; +} + + +//// Constructor/Destructor /////////////////////////////////////////////////// + +plLayerConverter::plLayerConverter() : + fInterface( nil ), + fConverterUtils( hsConverterUtils::Instance() ) +{ + fErrorMsg = nil; + fWarned = 0; + fSaving = false; +} + +plLayerConverter::~plLayerConverter() +{ +} + +plLayerConverter &plLayerConverter::Instance( void ) +{ + hsGuardBegin( "plLayerConverter::Instance" ); + + static plLayerConverter instance; + + return instance; + + hsGuardEnd; +} + +void plLayerConverter::Init( hsBool save, plErrorMsg *msg ) +{ + fSaving = save; + fErrorMsg = msg; + fWarned = 0; + fInterface = GetCOREInterface(); +} + +void plLayerConverter::DeInit( void ) +{ + int i; + for( i = 0; i < fConvertedLayers.GetCount(); i++ ) + { + if( fConvertedLayers[ i ] != nil ) + fConvertedLayers[ i ]->IClearConversionTargets(); + } + fConvertedLayers.Reset(); +} + +//// Mute/Unmute Warnings ///////////////////////////////////////////////////// + +void plLayerConverter::MuteWarnings( void ) +{ + fSavedWarned = fWarned; + fWarned |= kWarnedNoBaseTexture | kWarnedUpperTextureMissing; +} + +void plLayerConverter::UnmuteWarnings( void ) +{ + fWarned = fSavedWarned; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Main Switcheroo ////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// ConvertTexmap //////////////////////////////////////////////////////////// + +plLayerInterface *plLayerConverter::ConvertTexmap( Texmap *texmap, + plMaxNode *maxNode, + UInt32 blendFlags, hsBool preserveUVOffset, + hsBool upperLayer ) +{ + hsGuardBegin( "plLayerConverter::ConvertTexmap" ); + + fDbgNodeName = maxNode->GetName(); + + // We only convert plPlasmaMAXLayers + plPlasmaMAXLayer *layer = plPlasmaMAXLayer::GetPlasmaMAXLayer( texmap ); + if( layer == nil ) + { + fErrorMsg->Set( true, "Plasma Layer Error", "Cannot convert layer '%s'--unrecognized MAX layer type", texmap->GetName() ); + fErrorMsg->Show(); + fErrorMsg->Set(); + return nil; + } + + // KLUDGE - Some things don't set the name for their layers (ie projected + // runtime lights). So that we don't end up with an empty keyname, set the + // name to the nodes name if there isn't one. -Colin + const char* layerName = layer->GetName(); + if (!layerName || layerName[0] == '\0') + layer->SetName(maxNode->GetName()); + + // Switch on the class ID + plLayerInterface *plasmaLayer = nil; + + if( layer->ClassID() == LAYER_TEX_CLASS_ID ) + plasmaLayer = IConvertLayerTex( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); + + else if( layer->ClassID() == STATIC_ENV_LAYER_CLASS_ID ) + plasmaLayer = IConvertStaticEnvLayer( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); + + else if( layer->ClassID() == DYNAMIC_ENV_LAYER_CLASS_ID ) + plasmaLayer = IConvertDynamicEnvLayer( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); + + else if( layer->ClassID() == DYN_TEXT_LAYER_CLASS_ID ) + plasmaLayer = IConvertDynamicTextLayer( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); + + else if( layer->ClassID() == ANGLE_ATTEN_LAYER_CLASS_ID ) + plasmaLayer = IConvertAngleAttenLayer( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); + + else if( layer->ClassID() == MAX_CAMERA_LAYER_CLASS_ID ) + plasmaLayer = IConvertCameraLayer( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); + + + IRegisterConversion( layer, plasmaLayer ); + + return plasmaLayer; + + hsGuardEnd; +} + +//// IRegisterConversion ////////////////////////////////////////////////////// + +void plLayerConverter::IRegisterConversion( plPlasmaMAXLayer *origLayer, plLayerInterface *convertedLayer ) +{ + if( convertedLayer == nil ) + return; + + // Add this to our list of converted layers (so we can clean them up later) + if( fConvertedLayers.Find( origLayer ) == fConvertedLayers.kMissingIndex ) + fConvertedLayers.Append( origLayer ); + + // Now add the converted layer to that layer's list of conversion targets. + // (easier than us keeping a huge lookup table, since this is *acting* + // as that lookup table more or less) + origLayer->IAddConversionTarget( convertedLayer ); +} + +/////////////////////////////////////////////////////////////////////////////// +//// Main Processing Functions //////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// IConvertLayerTex ///////////////////////////////////////////////////////// + +plLayerInterface *plLayerConverter::IConvertLayerTex( plPlasmaMAXLayer *layer, + plMaxNode *maxNode, UInt32 blendFlags, + hsBool preserveUVOffset, hsBool upperLayer ) +{ + hsGuardBegin( "plLayerConverter::IConvertLayerTex" ); + + IParamBlock2 *bitmapPB; + plLocation loc; + + + loc = maxNode->GetLocation(); + bitmapPB = layer->GetParamBlockByID( plLayerTex::kBlkBitmap ); + + if( !bitmapPB ) + { + fErrorMsg->Set( !bitmapPB, "Plasma Layer Error", "Bitmap paramblock for Plasma Layer not found" ).Show(); + fErrorMsg->Set(); + return nil; + } + + // Get a new layer to play with + plLayer *plasmaLayer = ICreateLayer( layer->GetName(), upperLayer, loc ); + + // We're using a texture, try and get its info + PBBitmap *pbbm = nil; + BitmapInfo *bi = nil; + + if( bitmapPB->GetInt( kBmpUseBitmap ) ) + { + if( bitmapPB ) + pbbm = bitmapPB->GetBitmap( kBmpBitmap ); + if( pbbm ) + bi = &pbbm->bi; + } + + // If the texture had bad info, assert and return the empty layer + if( !bi || !bi->Name() || !strcmp(bi->Name(), "") ) + { + if( upperLayer ) + { + if( fErrorMsg->Set( !( fWarned & kWarnedNoUpperTexture ), "Plasma Export Error", sWarnNoUpperTexture, maxNode->GetName() ).CheckAskOrCancel() ) + fWarned |= kWarnedNoUpperTexture; + fErrorMsg->Set( false ); + + delete plasmaLayer; + return nil; + } + else + { + return (plLayerInterface *)plasmaLayer; + } + } + + // Setup the texture creation parameters + plBitmapData bd; + bd.fileName = bi->Name(); + + // Create texture and add it to list if unique + Int32 texFlags = 0;//hsGTexture::kMipMap; + + // Texture Alpha/Color + if( bitmapPB->GetInt( kBmpInvertColor ) ) + plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendInvertColor ); + if( bitmapPB->GetInt( kBmpDiscardColor ) ) + plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendNoTexColor ); + if( bitmapPB->GetInt( kBmpDiscardAlpha ) ) + plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendNoTexAlpha ); + if( bitmapPB->GetInt( kBmpInvertAlpha ) ) + bd.invertAlpha = true; + + // Texture quality + if( bitmapPB->GetInt( kBmpNonCompressed ) ) + texFlags |= plBitmap::kForceNonCompressed; + + if( bitmapPB->GetInt( kBmpNoDiscard ) ) + texFlags |= plBitmap::kDontThrowAwayImage; + + switch( bitmapPB->GetInt( kBmpScaling ) ) + { + case kScalingHalf: texFlags |= plBitmap::kHalfSize; break; + case kScalingNone: texFlags |= plBitmap::kNoMaxSize; break; + } + + // Mip map filtering. + if( bitmapPB->GetInt( kBmpNoFilter ) ) + texFlags |= plBitmap::kForceOneMipLevel; + if( bitmapPB->GetInt( kBmpMipBias ) ) + { + plasmaLayer->SetZFlags( plasmaLayer->GetZFlags() | hsGMatState::kZLODBias ); + plasmaLayer->SetLODBias( bitmapPB->GetFloat( kBmpMipBiasAmt, fConverterUtils.GetTime( fInterface ) ) ); + } + float sig = bitmapPB->GetFloat( kBmpMipBlur ); + + bd.texFlags = texFlags; + bd.sig = sig; + + // Get detail parameters + if( bitmapPB->GetInt( kBmpUseDetail ) ) + { // TODO: be smarter + if( blendFlags & hsGMatState::kBlendAdd ) + bd.createFlags |= plMipmap::kCreateDetailAdd; + else if( blendFlags & hsGMatState::kBlendMult ) + bd.createFlags |= plMipmap::kCreateDetailMult; + else + bd.createFlags |= plMipmap::kCreateDetailAlpha; + bd.detailDropoffStart = float(bitmapPB->GetInt(kBmpDetailStartSize)) / 100.f; + bd.detailDropoffStop = float(bitmapPB->GetInt(kBmpDetailStopSize)) / 100.f; + bd.detailMax = float(bitmapPB->GetInt(kBmpDetailStartOpac)) / 100.f; + bd.detailMin = float(bitmapPB->GetInt(kBmpDetailStopOpac)) / 100.f; + } + + // Get max export dimension (since the function we eventually call + // expects the max of the two dimensions, we figure that out here and + // pass it on) + bd.maxDimension = bitmapPB->GetInt( kBmpExportWidth ); + int expHt = bitmapPB->GetInt( kBmpExportHeight ); + if( bd.maxDimension < expHt ) + bd.maxDimension = expHt; + int clipID = 0, w; + for( clipID = 0, w = bi->Width(); w > bd.maxDimension; w >>= 1, clipID++ ); + + // Do the UV gen (before we do texture, since it could modify the bitmapData struct) + IProcessUVGen( layer, plasmaLayer, &bd, preserveUVOffset ); + + // Create the texture. If it works, assign it to the layer + if( ( plasmaLayer = IAssignTexture( &bd, maxNode, plasmaLayer, upperLayer, clipID ) ) == nil ) + return nil; + + // All done! + return (plLayerInterface *)plasmaLayer; + + hsGuardEnd; +} + +//// IConvertStaticEnvLayer /////////////////////////////////////////////////// + +plLayerInterface *plLayerConverter::IConvertStaticEnvLayer( plPlasmaMAXLayer *layer, + plMaxNode *maxNode, UInt32 blendFlags, + hsBool preserveUVOffset, hsBool upperLayer ) +{ + hsGuardBegin( "plLayerConverter::IConvertStaticEnvLayer" ); + + IParamBlock2 *bitmapPB; + plLocation loc; + + loc = maxNode->GetLocation(); + bitmapPB = layer->GetParamBlockByID( plStaticEnvLayer::kBlkBitmap ); + + if( !bitmapPB ) + { + fErrorMsg->Set( !bitmapPB, "Plasma Layer Error", "Bitmap paramblock for Plasma Layer not found" ).Show(); + fErrorMsg->Set(); + return nil; + } + + // Get a new layer to play with + plLayer *plasmaLayer = ICreateLayer( layer->GetName(), upperLayer, loc ); + + // Get the texture info + PBBitmap *pbbm = bitmapPB->GetBitmap( plStaticEnvLayer::kBmpFrontBitmap + 0 ); + BitmapInfo *bi = nil; + if( pbbm ) + bi = &pbbm->bi; + + // If the texture had bad info, assert and return the empty layer + if (!bi || !bi->Name() || !strcmp(bi->Name(), "")) + { + // Or don't assert since it can get annoying when you are using someone + // elses file and don't have all the textures. + return (plLayerInterface *)plasmaLayer; + } + + // Setup the texture creation parameters + plBitmapData bd; + bd.fileName = bi->Name(); + + // Create texture and add it to list if unique + Int32 texFlags = 0; + + // Texture Alpha/Color + if( bitmapPB->GetInt( plStaticEnvLayer::kBmpInvertColor ) ) + plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendInvertColor ); + if( bitmapPB->GetInt( plStaticEnvLayer::kBmpDiscardColor ) ) + plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendNoTexColor ); + if( bitmapPB->GetInt( kBmpDiscardAlpha ) ) + plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendNoTexAlpha ); + if( bitmapPB->GetInt( plStaticEnvLayer::kBmpInvertAlpha ) ) + bd.invertAlpha = true; + + // Texture quality + if( bitmapPB->GetInt( plStaticEnvLayer::kBmpNonCompressed ) ) + texFlags |= plBitmap::kForceNonCompressed; + + switch( bitmapPB->GetInt( plStaticEnvLayer::kBmpScaling ) ) + { + case plStaticEnvLayer::kScalingHalf: texFlags |= plBitmap::kHalfSize; break; + case plStaticEnvLayer::kScalingNone: texFlags |= plBitmap::kNoMaxSize; break; + } + + bd.texFlags = texFlags; + bd.isStaticCubicEnvMap = true; + for( int i = 0; i < 6; i++ ) + { + PBBitmap *face = bitmapPB->GetBitmap( plStaticEnvLayer::kBmpFrontBitmap + i ); + if( !face ) + return (plLayerInterface *)plasmaLayer; + bd.faceNames[ i ] = face->bi.Name(); + } + + // Get detail parameters + if( bitmapPB->GetInt( plStaticEnvLayer::kBmpUseDetail ) ) + { // TODO: be smarter + if( blendFlags & hsGMatState::kBlendAdd ) + bd.createFlags = plMipmap::kCreateDetailAdd; + else if( blendFlags & hsGMatState::kBlendMult ) + bd.createFlags = plMipmap::kCreateDetailMult; + else + bd.createFlags = plMipmap::kCreateDetailAlpha; + + bd.detailDropoffStart = float( bitmapPB->GetInt( plStaticEnvLayer::kBmpDetailStartSize ) ) / 100.f; + bd.detailDropoffStop = float( bitmapPB->GetInt( plStaticEnvLayer::kBmpDetailStopSize ) ) / 100.f; + bd.detailMax = float( bitmapPB->GetInt( plStaticEnvLayer::kBmpDetailStartOpac ) ) / 100.f; + bd.detailMin = float( bitmapPB->GetInt( plStaticEnvLayer::kBmpDetailStopOpac ) ) / 100.f; + } + + /// Since we're a cubic environMap, we don't care about the UV transform nor the uvwSrc + plasmaLayer->SetUVWSrc( 0 ); + plasmaLayer->SetUVWSrc( plasmaLayer->GetUVWSrc() | plLayerInterface::kUVWReflect ); + + // Create the texture. If it works, assign it to the layer + if( ( plasmaLayer = IAssignTexture( &bd, maxNode, plasmaLayer, upperLayer ) ) == nil ) + return nil; + + // Tag this layer as reflective cubic environmentmapping + if( bitmapPB->GetInt(plStaticEnvLayer::kBmpRefract) ) + plasmaLayer->SetMiscFlags( plasmaLayer->GetMiscFlags() | hsGMatState::kMiscUseRefractionXform ); + else + plasmaLayer->SetMiscFlags( plasmaLayer->GetMiscFlags() | hsGMatState::kMiscUseReflectionXform ); + + return (plLayerInterface *)plasmaLayer; + + hsGuardEnd; +} + +//// IConvertDynamicEnvLayer ////////////////////////////////////////////////// + +plLayerInterface *plLayerConverter::IConvertDynamicEnvLayer( plPlasmaMAXLayer *layer, + plMaxNode *maxNode, UInt32 blendFlags, + hsBool preserveUVOffset, hsBool upperLayer ) +{ + hsGuardBegin( "plLayerConverter::IConvertDynamicEnvLayer" ); + + IParamBlock2 *bitmapPB; + plLocation loc; + + loc = maxNode->GetLocation(); + bitmapPB = layer->GetParamBlockByID( plDynamicEnvLayer::kBlkBitmap ); + + if( !bitmapPB ) + { + fErrorMsg->Set( !bitmapPB, "Plasma Layer Error", "Bitmap paramblock for Plasma Layer not found" ).Show(); + fErrorMsg->Set(); + return nil; + } + + // Get a new layer to play with + plLayer *plasmaLayer = ICreateLayer( layer->GetName(), upperLayer, loc ); + + // Get the anchor node + plMaxNode *anchor = (plMaxNode *)bitmapPB->GetINode( plDynamicEnvLayer::kBmpAnchorNode ); + if( anchor == nil ) + // Default to self as the anchor--just make sure we make unique versions of this material! + anchor = maxNode; + + if( !anchor->CanConvert() || !( anchor->GetForceLocal() || anchor->GetDrawable() ) ) + { + fErrorMsg->Set( true, "Plasma Layer Error", "The dynamic envMap material %s has an invalid anchor specified. Please specify a valid Plasma scene object as an anchor.", plasmaLayer->GetKeyName() ).Show(); + fErrorMsg->Set(); + return (plLayerInterface *)plasmaLayer; + } + + // Create texture and add it to list if unique + Int32 texFlags = 0; + + /// Since we're a cubic environMap, we don't care about the UV transform nor the uvwSrc + plasmaLayer->SetUVWSrc( 0 ); + plasmaLayer->SetUVWSrc( plasmaLayer->GetUVWSrc() | plLayerInterface::kUVWReflect ); + + // Create the texture. If it works, assign it to the layer + char texName[ 256 ]; + if( anchor == maxNode ) + { + // Self-anchoring material, make sure the name is unique via the nodeName + sprintf( texName, "%s_cubicRT@%s", plasmaLayer->GetKeyName(), maxNode->GetName() ); + } + else + sprintf( texName, "%s_cubicRT", plasmaLayer->GetKeyName() ); + + plBitmap *texture = (plBitmap *)IMakeCubicRenderTarget( texName, maxNode, anchor ); + if( texture ) + hsgResMgr::ResMgr()->AddViaNotify( texture->GetKey(), TRACKED_NEW plLayRefMsg( plasmaLayer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture ), plRefFlags::kActiveRef ); + + // Tag this layer as reflective cubic environmentmapping + if( bitmapPB->GetInt(plDynamicEnvLayer::kBmpRefract) ) + plasmaLayer->SetMiscFlags( plasmaLayer->GetMiscFlags() | hsGMatState::kMiscUseRefractionXform ); + else + plasmaLayer->SetMiscFlags( plasmaLayer->GetMiscFlags() | hsGMatState::kMiscUseReflectionXform ); + + return (plLayerInterface *)plasmaLayer; + + hsGuardEnd; +} + +plLayerInterface *plLayerConverter::IConvertCameraLayer(plPlasmaMAXLayer *layer, + plMaxNode *maxNode, UInt32 blendFlags, + hsBool preserveUVOffset, hsBool upperLayer) +{ + hsGuardBegin( "plLayerConverter::IConvertCameraLayer" ); + + IParamBlock2 *pb; + plLocation loc; + + loc = maxNode->GetLocation(); + pb = layer->GetParamBlockByID(plMAXCameraLayer::kBlkMain); + + if (!pb) + { + fErrorMsg->Set(!pb, "Plasma Layer Error", "Paramblock for Plasma Camera Layer not found" ).Show(); + fErrorMsg->Set(); + return nil; + } + + plLayer *plasmaLayer = ICreateLayer (layer->GetName(), upperLayer, loc); + + plMaxNode *rootNode = (plMaxNode*)pb->GetINode(ParamID(plMAXCameraLayer::kRootNode)); + plDynamicCamMap *map = plEnvMapComponent::GetCamMap(rootNode ? rootNode : maxNode); + if (map) + { + Int32 texFlags = 0; + if (!pb->GetInt(ParamID(plMAXCameraLayer::kExplicitCam))) + { + plasmaLayer->SetUVWSrc(plLayerInterface::kUVWPosition); + plasmaLayer->SetMiscFlags(hsGMatState::kMiscCam2Screen | hsGMatState::kMiscPerspProjection); + hsgResMgr::ResMgr()->AddViaNotify(rootNode->GetSceneObject()->GetKey(), TRACKED_NEW plGenRefMsg(map->GetKey(), plRefMsg::kOnCreate, -1, plDynamicCamMap::kRefRootNode), plRefFlags::kActiveRef); + hsgResMgr::ResMgr()->AddViaNotify(plasmaLayer->GetKey(), TRACKED_NEW plGenRefMsg(map->GetKey(), plRefMsg::kOnCreate, -1, plDynamicCamMap::kRefMatLayer), plRefFlags::kActiveRef); + if (!pb->GetInt(ParamID(plMAXCameraLayer::kForce))) + { + plBitmap *disableTexture = hsMaterialConverter::Instance().GetStaticColorTexture(pb->GetColor(ParamID(plMAXCameraLayer::kDisableColor)), loc); + hsgResMgr::ResMgr()->AddViaNotify(disableTexture->GetKey(), TRACKED_NEW plGenRefMsg(map->GetKey(), plRefMsg::kOnCreate, -1, plDynamicCamMap::kRefDisableTexture), plRefFlags::kActiveRef); + } + } + else + { + plMaxNode *camNode = (plMaxNode*)pb->GetINode(ParamID(plMAXCameraLayer::kCamera)); + if (camNode) + { + const plCameraModifier1 *mod = plCameraModifier1::ConvertNoRef(camNode->GetSceneObject()->GetModifierByType(plCameraModifier1::Index())); + if (mod) + hsgResMgr::ResMgr()->AddViaNotify(mod->GetKey(), TRACKED_NEW plGenRefMsg(map->GetKey(), plRefMsg::kOnCreate, -1, plDynamicCamMap::kRefCamera), plRefFlags::kActiveRef); + } + + plasmaLayer->SetUVWSrc(pb->GetInt(ParamID(plMAXCameraLayer::kUVSource))); + } + + hsTArray nodeList; + hsMaterialConverter::GetNodesByMaterial(maxNode->GetMtl(), nodeList); + int i; + for (i = 0; i < nodeList.GetCount(); i++) + { + hsgResMgr::ResMgr()->AddViaNotify(nodeList[i]->GetSceneObject()->GetKey(), TRACKED_NEW plGenRefMsg(map->GetKey(), plRefMsg::kOnCreate, -1, plDynamicCamMap::kRefTargetNode), plRefFlags::kActiveRef); + } + hsgResMgr::ResMgr()->AddViaNotify(map->GetKey(), TRACKED_NEW plLayRefMsg(plasmaLayer->GetKey(), plRefMsg::kOnCreate, -1, plLayRefMsg::kTexture), plRefFlags::kActiveRef); + + } + + return plasmaLayer; + + hsGuardEnd; +} + +//// IConvertDynamicTextLayer ///////////////////////////////////////////////// + +plLayerInterface *plLayerConverter::IConvertDynamicTextLayer( plPlasmaMAXLayer *layer, + plMaxNode *maxNode, UInt32 blendFlags, + hsBool preserveUVOffset, hsBool upperLayer ) +{ + hsGuardBegin( "plLayerConverter::IConvertDynamicTextLayer" ); + + plDynamicTextLayer *maxLayer; + IParamBlock2 *bitmapPB; + plLocation loc; + + maxLayer = (plDynamicTextLayer *)layer; + loc = maxNode->GetLocation(); + bitmapPB = maxLayer->GetParamBlockByID( plDynamicTextLayer::kBlkBitmap ); + + if( !bitmapPB ) + { + fErrorMsg->Set( !bitmapPB, "Plasma Layer Error", "Bitmap paramblock for Plasma Layer not found" ).Show(); + fErrorMsg->Set(); + return nil; + } + + // Get a new layer to play with + plLayer *plasmaLayer = ICreateLayer( maxLayer->GetName(), upperLayer, loc ); + + + /// UV Gen + IProcessUVGen( maxLayer, plasmaLayer, nil, preserveUVOffset ); + + // Create the "texture" + plDynamicTextMap *texture = ICreateDynTextMap( plasmaLayer->GetKeyName(), + bitmapPB->GetInt( plDynamicTextLayer::kBmpExportWidth ), + bitmapPB->GetInt( plDynamicTextLayer::kBmpExportHeight ), + bitmapPB->GetInt( plDynamicTextLayer::kBmpIncludeAlphaChannel ), + maxNode ); + + // Set the initial bitmap if necessary + UInt32 *initBuffer = IGetInitBitmapBuffer( maxLayer ); + if( initBuffer != nil ) + { + texture->SetInitBuffer( initBuffer ); + delete [] initBuffer; + } + + // Add the texture in + hsgResMgr::ResMgr()->AddViaNotify( texture->GetKey(), TRACKED_NEW plLayRefMsg( plasmaLayer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture ), plRefFlags::kActiveRef ); + + // All done! + return (plLayerInterface *)plasmaLayer; + + hsGuardEnd; +} + +//// IGetInitBitmapBuffer ///////////////////////////////////////////////////// +// Called to get a 32-bit uncompressed ARGB version of the init bitmap of +// a dynamic text layer + +UInt32 *plLayerConverter::IGetInitBitmapBuffer( plDynamicTextLayer *layer ) const +{ + UInt32 *buffer; + hsRGBAColor32 *buffPtr; + UInt16 width, height; + + + IParamBlock2 *bitmapPB = layer->GetParamBlockByID( plDynamicTextLayer::kBlkBitmap ); + Bitmap *initBitmap = layer->GetBitmap( TimeValue( 0 ) ); + + if( bitmapPB->GetInt( (ParamID)plDynamicTextLayer::kBmpUseInitImage ) == 0 || initBitmap == nil ) + return nil; + + width = bitmapPB->GetInt( (ParamID)plDynamicTextLayer::kBmpExportWidth ); + height = bitmapPB->GetInt( (ParamID)plDynamicTextLayer::kBmpExportHeight ); + + buffer = TRACKED_NEW UInt32[ width * height ]; + if( buffer == nil ) + return nil; + + // Fill buffer from the MAX bitmap + PixelBuf l64( width ); + BMM_Color_64 *p64 = l64.Ptr(); + + buffPtr = (hsRGBAColor32 *)buffer; + for( int y = 0; y < height; y++ ) + { + hsRGBAColor32 color; + + if( !initBitmap->GetLinearPixels( 0, y, width, p64 ) ) + { + delete [] buffer; + return nil; + } + + for( int x = 0; x < width; x++ ) + { + color.SetARGB( p64[ x ].a * 255.f / 65535.f, + p64[ x ].r * 255.f / 65535.f, + p64[ x ].g * 255.f / 65535.f, + p64[ x ].b * 255.f / 65535.f ); + buffPtr[ x ] = color; + } + + buffPtr += width; + } + + return buffer; +} + +static UInt32 MakeUInt32Color(float r, float g, float b, float a) +{ + return (UInt32(a * 255.9f) << 24) + |(UInt32(r * 255.9f) << 16) + |(UInt32(g * 255.9f) << 8) + |(UInt32(b * 255.9f) << 0); +} + +plBitmap* plLayerConverter::IGetAttenRamp(plMaxNode *node, BOOL isAdd, int loClamp, int hiClamp) +{ + char funkName[512]; + sprintf(funkName, "%s_%d_%d", + isAdd ? "AttenRampAdd" : "AttenRampMult", + loClamp, + hiClamp); + + float range = float(hiClamp - loClamp) * 1.e-2f; + float lowest = float(loClamp) * 1.e-2f; + const int kLUTWidth = 16; + const int kLUTHeight = 16; + + // NOTE: CreateBlankMipmap might return an old mipmap if it was already created, so in those + // cases we wouldn't really need to re-write the texture. However, there's no harm in doing so, + // and since we're close to Alpha, I don't want to shake up the code any more than absolutely + // necessary. -mcn + plMipmap *texture = plBitmapCreator::Instance().CreateBlankMipmap( kLUTWidth, kLUTHeight, plMipmap::kARGB32Config, 1, funkName, node->GetLocation() ); + + UInt32* pix = (UInt32*)texture->GetImage(); + + if( isAdd ) + { + int i; + for( i = 0; i < kLUTHeight; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + float y = float(i) / (kLUTHeight-1); + if( x < y ) + x = y; + x *= range; + x += lowest; + *pix++ = MakeUInt32Color(1.f, 1.f, 1.f, x); + } + } + } + else + { + int i; + for( i = 0; i < kLUTHeight; i++ ) + { + int j; + for( j = 0; j < kLUTWidth; j++ ) + { + float x = float(j) / (kLUTWidth-1); + float y = float(i) / (kLUTHeight-1); + float val = x * y; + val *= range; + val += lowest; + *pix++ = MakeUInt32Color(1.f, 1.f, 1.f, val); + } + } + } + + return texture; +} + + +plLayer* plLayerConverter::ICreateAttenuationLayer(const char* name, plMaxNode *node, int uvwSrc, + float tr0, float op0, float tr1, float op1, + int loClamp, int hiClamp) +{ + hsMatrix44 uvwXfm; + uvwXfm.Reset(); + uvwXfm.fMap[0][0] = uvwXfm.fMap[1][1] = uvwXfm.fMap[2][2] = 0; + uvwXfm.NotIdentity(); + + if( op0 != tr0 ) + { + uvwXfm.fMap[0][2] = -1.f / (tr0 - op0); + uvwXfm.fMap[0][3] = uvwXfm.fMap[0][2] * -tr0; + } + else + { + uvwXfm.fMap[0][3] = 1.f; + } + + if( op1 != tr1 ) + { + uvwXfm.fMap[1][2] = -1.f / (tr1 - op1); + uvwXfm.fMap[1][3] = uvwXfm.fMap[1][2] * -tr1; + } + else + { + uvwXfm.fMap[1][3] = 1.f; + } + + BOOL chanAdd = false; + if( op0 < tr0 ) + { + if( (op0 < op1) && (tr1 < op1) ) + chanAdd = true; + } + else if( op0 > tr0 ) + { + if( (op0 > op1) && (tr1 > op1) ) + chanAdd = true; + } + plBitmap* funkRamp = IGetAttenRamp(node, chanAdd, loClamp, hiClamp); + + plLayer* layer = TRACKED_NEW plLayer; + layer->InitToDefault(); + hsgResMgr::ResMgr()->NewKey(name, layer, node->GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(funkRamp->GetKey(), TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef); + + layer->SetAmbientColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + layer->SetPreshadeColor(hsColorRGBA().Set(0, 0, 0, 1.f)); + layer->SetRuntimeColor(hsColorRGBA().Set(0, 0, 0, 1.f)); + + layer->SetZFlags(hsGMatState::kZNoZWrite); + UInt32 blendFlags = hsGMatState::kBlendAlpha | hsGMatState::kBlendNoTexColor | hsGMatState::kBlendAlphaMult; + layer->SetBlendFlags(blendFlags); + layer->SetClampFlags(hsGMatState::kClampTexture); + + layer->SetTransform(uvwXfm); + + layer->SetUVWSrc(uvwSrc); + + return layer; +} + +plLayerInterface* plLayerConverter::IConvertAngleAttenLayer(plPlasmaMAXLayer *layer, + plMaxNode *maxNode, UInt32 blendFlags, + hsBool preserveUVOffset, hsBool upperLayer) +{ + hsGuardBegin( "plPlasmaMAXLayer::IConvertAngleAttenLayer" ); + if( !upperLayer ) + { + fErrorMsg->Set(true, maxNode->GetName(), "Angle Attenuation layers can only be used as a top layer").Show(); + fErrorMsg->Set(); + return nil; + } + plAngleAttenLayer* aaLay = (plAngleAttenLayer*)layer; + Box3 fade = aaLay->GetFade(); + float tr0 = cosf(DegToRad(180.f - fade.Min().x)); + float op0 = cosf(DegToRad(180.f - fade.Min().y)); + float tr1 = cosf(DegToRad(180.f - fade.Max().x)); + float op1 = cosf(DegToRad(180.f - fade.Max().y)); + + int loClamp = aaLay->GetLoClamp(); + int hiClamp = aaLay->GetHiClamp(); + + int uvwSrc = aaLay->Reflect() ? plLayerInterface::kUVWReflect : plLayerInterface::kUVWNormal; + + plLayer* lut = ICreateAttenuationLayer(layer->GetName(), maxNode, uvwSrc, tr0, op0, tr1, op1, loClamp, hiClamp); + + return lut; + + hsGuardEnd; +} +//// ICreateLayer ///////////////////////////////////////////////////////////// + +plLayer *plLayerConverter::ICreateLayer( const char *name, hsBool upperLayer, plLocation &loc ) +{ + hsGuardBegin( "plPlasmaMAXLayer::ICreateLayer" ); + + plLayer *layer = TRACKED_NEW plLayer; + layer->InitToDefault(); + + hsgResMgr::ResMgr()->NewKey( name, layer, loc ); + + return layer; + + hsGuardEnd; +} + +//// IProcessUVGen //////////////////////////////////////////////////////////// + +void plLayerConverter::IProcessUVGen( plPlasmaMAXLayer *srcLayer, plLayer *destLayer, + plBitmapData *bitmapData, hsBool preserveUVOffset ) +{ + hsGuardBegin( "plPlasmaMAXLayer::IProcessUVGen" ); + + StdUVGen *uvGen = (StdUVGen *)srcLayer->GetTheUVGen(); + + int tiling = uvGen->GetTextureTiling(); + + // If set this indicates the texture map is tiled in U + if (!(tiling & U_WRAP)) + { + destLayer->SetClampFlags( destLayer->GetClampFlags() | hsGMatState::kClampTextureU ); + if( bitmapData != nil ) + bitmapData->clampFlags |= plBitmapData::kClampU; + } + + // If set this indicates the texture map is tiled in V + if (!(tiling & V_WRAP)) + { + destLayer->SetClampFlags( destLayer->GetClampFlags() | hsGMatState::kClampTextureV ); + if( bitmapData != nil ) + bitmapData->clampFlags |= plBitmapData::kClampV; + } + + // UVW Src + Int32 uvwSrc = srcLayer->GetMapChannel() - 1; + + if( fErrorMsg->Set( !( fWarned & kWarnedTooManyUVs ) && + ( ( uvwSrc < 0 ) || ( uvwSrc >= plGeometrySpan::kMaxNumUVChannels ) ), + destLayer->GetKeyName(), "Only %d UVW channels (1-%d) currently supported", + plGeometrySpan::kMaxNumUVChannels, plGeometrySpan::kMaxNumUVChannels).CheckAskOrCancel() ) + fWarned |= kWarnedTooManyUVs; + fErrorMsg->Set( false ); + + destLayer->SetUVWSrc( uvwSrc ); + + // Get the actual texture transform + hsMatrix44 hsTopX; + if (uvGen && (hsControlConverter::Instance().StdUVGenToHsMatrix44(&hsTopX, uvGen, preserveUVOffset == true))) + destLayer->SetTransform( hsTopX ); + + // All done! + hsGuardEnd; +} + +//// ICreateDynTextMap //////////////////////////////////////////////////////// + +plDynamicTextMap *plLayerConverter::ICreateDynTextMap( const char *layerName, UInt32 width, UInt32 height, + hsBool includeAlphaChannel, plMaxNode *node ) +{ + hsGuardBegin( "plPlasmaMAXLayer::ICreateDynTextMap" ); + + char texName[ 256 ]; + plKey key; + plDynamicTextMap *map = nil; + + + // Need a unique key name for every layer that uses one. We could also key + // off of width and height, but layerName should be more than plenty + sprintf( texName, "%s_dynText", layerName ); + + // Does it already exist? + key = node->FindPageKey( plDynamicTextMap::Index(), texName ); + if( key != nil ) + { + map = plDynamicTextMap::ConvertNoRef( key->GetObjectPtr() ); + if( map != nil ) + return map; + } + + // Create + map = TRACKED_NEW plDynamicTextMap(); + map->SetNoCreate( width, height, includeAlphaChannel ); + + /// Add a key for it + key = hsgResMgr::ResMgr()->NewKey( texName, map, node->GetLocation() ); + + // All done! + return map; + + hsGuardEnd; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Texture/Bitmap Management //////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +plBitmap *plLayerConverter::CreateSimpleTexture(const char *fileName, const plLocation &loc, + UInt32 clipID /* = 0 */, UInt32 texFlags /* = 0 */, bool useJPEG /* = false */) +{ + plBitmapData bd; + bd.fileName = fileName; + bd.texFlags = texFlags; + bd.createFlags = 0; + bd.detailDropoffStart = 0; + bd.detailDropoffStop = 0; + bd.detailMax = 0; + bd.detailMin = 0; + bd.sig = 0; + bd.isStaticCubicEnvMap = false; + bd.invertAlpha = false; + bd.clampFlags = 0; + bd.useJPEG = useJPEG; + + return plBitmapCreator::Instance().CreateTexture(&bd, loc, clipID); +} + +//// IAssignTexture /////////////////////////////////////////////////////////// +// Create a texture and assign it to the layer given. Returns the layer again, +// or nil if there was an error and it got deleted. + +plLayer *plLayerConverter::IAssignTexture( plBitmapData *bd, plMaxNode *maxNode, plLayer *destLayer, hsBool upperLayer, int clipID ) +{ + plBitmap *texture = plBitmapCreator::Instance().CreateTexture( bd, maxNode->GetLocation(), clipID ); + if( texture == nil ) + { + if( upperLayer ) + { + if( fErrorMsg->Set( !( fWarned & kWarnedUpperTextureMissing ), "Plasma Export Error", sWarnUpperTextureMissing, maxNode->GetName(), bd->fileName ).CheckAskOrCancel() ) + fWarned |= kWarnedUpperTextureMissing; + fErrorMsg->Set( false ); + + delete destLayer; + return nil; + } + else + { + if( fErrorMsg->Set( !( fWarned & kWarnedNoBaseTexture ), "Plasma Export Error", sWarnBaseTextureMissing, maxNode->GetName(), bd->fileName ).CheckAskOrCancel() ) + fWarned |= kWarnedNoBaseTexture; + fErrorMsg->Set( false ); + + return destLayer; + } + } + else + hsgResMgr::ResMgr()->AddViaNotify( texture->GetKey(), TRACKED_NEW plLayRefMsg( destLayer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture ), plRefFlags::kActiveRef ); + + return destLayer; +} + +//// IMakeCubicRenderTarget /////////////////////////////////////////////////// +// Makes a plCubicRenderTarget as a texture. Also constructs the associated +// modifier and attaches it to the necessary object (hacked for now) + +plCubicRenderTarget *plLayerConverter::IMakeCubicRenderTarget( const char *name, plMaxNode *node, plMaxNode *anchor ) +{ + plDynamicEnvMap* env = plEnvMapComponent::GetEnvMap(anchor); + if( env ) + return env; + + char modName[ 256 ]; + plCubicRenderTarget *cubic = nil; + + + plKey key; + + key = node->FindPageKey( plCubicRenderTarget::Index(), name ); + if( key != nil ) + { + plCubicRenderTarget *cubic = plCubicRenderTarget::ConvertNoRef( key->GetObjectPtr() ); + if( cubic != nil ) + return cubic; + } + + /// Get the key from the anchor + if( anchor == nil || anchor->GetSceneObject() == nil ) + return nil; + + plKey sObjKey = anchor->GetSceneObject()->GetKey(); + if( sObjKey == nil ) + return nil; + + /// Create + cubic = TRACKED_NEW plCubicRenderTarget( plRenderTarget::kIsTexture, 256, 256, 32 ); + hsAssert( cubic != nil, "Cannot create cubic render target!" ); + + /// Add a key + key = hsgResMgr::ResMgr()->NewKey( name, cubic, node->GetLocation() ); + + /// Now make a modifier + plCubicRenderTargetModifier *mod = TRACKED_NEW plCubicRenderTargetModifier(); + sprintf( modName, "%s_mod", name ); + + hsgResMgr::ResMgr()->NewKey( modName, mod, node->GetLocation() ); + hsgResMgr::ResMgr()->AddViaNotify( cubic->GetKey(), TRACKED_NEW plGenRefMsg( mod->GetKey(), plRefMsg::kOnCreate, 0, 0 ), plRefFlags::kPassiveRef ); + hsgResMgr::ResMgr()->AddViaNotify( mod->GetKey(), TRACKED_NEW plObjRefMsg( sObjKey, plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); + + return cubic; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLayerConverter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLayerConverter.h new file mode 100644 index 00000000..8ef5e98d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLayerConverter.h @@ -0,0 +1,123 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plLayerConverter - Utility class that converts plPlasmaMAXLayers into // +// other stuff. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 1.13.2002 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plLayerConverter_h +#define _plLayerConverter_h + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "Max.h" + + +//// Class Definition ///////////////////////////////////////////////////////// + +class plErrorMsg; +class plLayerInterface; +class plMaxNode; +class plPlasmaMAXLayer; +class plLayer; +class plLocation; +class plBitmapData; +class plDynamicTextMap; +class plBitmap; +class plCubicRenderTarget; +class hsConverterUtils; +class plDynamicTextLayer; + +class plLayerConverter +{ + private: + + plLayerConverter(); + + protected: + + static const Class_ID fDerivedTypes[]; + + public: + + ~plLayerConverter(); + static plLayerConverter &Instance( void ); + + void Init( hsBool save, plErrorMsg *msg ); + void DeInit( void ); + + plLayerInterface *ConvertTexmap( Texmap *texmap, plMaxNode *maxNode, + UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer ); + plBitmap *CreateSimpleTexture(const char *fileName, const plLocation &loc, UInt32 clipID = 0, UInt32 texFlags = 0, bool useJPEG = false); + + void MuteWarnings( void ); + void UnmuteWarnings( void ); + + protected: + + plErrorMsg *fErrorMsg; + UInt32 fWarned, fSavedWarned; + hsBool fSaving; + + Interface *fInterface; + hsConverterUtils &fConverterUtils; + + const char *fDbgNodeName; + + hsTArray fConvertedLayers; + + + plLayer *ICreateLayer( const char *name, hsBool upperLayer, plLocation &loc ); + void IProcessUVGen( plPlasmaMAXLayer *srcLayer, plLayer *destLayer, plBitmapData *bitmapData, hsBool preserveUVOffset ); + plDynamicTextMap *ICreateDynTextMap( const char *layerName, UInt32 width, UInt32 height, hsBool includeAlpha, plMaxNode *node ); + + plLayer *IAssignTexture( plBitmapData *bd, plMaxNode *maxNode, plLayer *destLayer, hsBool upperLayer, int clipID = -1 ); + plCubicRenderTarget *IMakeCubicRenderTarget( const char *name, plMaxNode *maxNode, plMaxNode *anchor ); + + // Add your function to process your layer type here + plLayerInterface *IConvertLayerTex( plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer ); + plLayerInterface *IConvertStaticEnvLayer( plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer ); + plLayerInterface *IConvertDynamicEnvLayer( plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer ); + plLayerInterface *IConvertCameraLayer( plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer ); + plLayerInterface *IConvertDynamicTextLayer( plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer ); + + plBitmap* IGetAttenRamp( plMaxNode *maxNode, BOOL isAdd, int loClamp, int hiClamp); + plLayer* ICreateAttenuationLayer(const char* name, plMaxNode *maxNode, int uvwSrc, float tr0, float op0, float tr1, float op1, int loClamp, int hiClamp); + plLayerInterface* IConvertAngleAttenLayer(plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer); + + void IRegisterConversion( plPlasmaMAXLayer *origLayer, plLayerInterface *convertedLayer ); + + UInt32 *IGetInitBitmapBuffer( plDynamicTextLayer *layer ) const; + +}; + +#endif // _plLayerConverter_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLightMapGen.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLightMapGen.cpp new file mode 100644 index 00000000..4aa28bbc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLightMapGen.cpp @@ -0,0 +1,1614 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +//#define MF_NEW_RGC + +#include "hsTypes.h" +#include "Max.h" +#include "dummy.h" +#include "notify.h" + +#include "plLightMapGen.h" +#include "../plGImage/plMipmap.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxExport/plErrorMsg.h" +#include "plRenderGlobalContext.h" +#include "plMaxLightContext.h" +#include "../plSurface/plLayer.h" +#include "../plSurface/hsGMaterial.h" +#include "../MaxMain/plPluginResManager.h" +#include "../plDrawable/plGeometrySpan.h" +#include "hsFastMath.h" +#include "hsControlConverter.h" +#include "plBitmapCreator.h" +#include "../pnKeyedObject/plKey.h" +#include "../plResMgr/plKeyFinder.h" +#include "../plResMgr/plPageInfo.h" + +#include "../plMessage/plLayRefMsg.h" +#include "../plMessage/plMatRefMsg.h" + +#include "../MaxComponent/plLightMapComponent.h" + +#include "../plGImage/hsCodecManager.h" +#include "../plAgeDescription/plAgeDescription.h" + + +static plLightMapGen theLMG; + +static const float kBlurMapRange = 20.f; + +#ifdef MF_NEW_RGC +void getRGC(void* param, NotifyInfo* info) +{ + if( info->intcode == NOTIFY_PRE_RENDERFRAME ) + { + plLightMapGen* lmg = (plLightMapGen*)param; + RenderGlobalContext* rgc = (RenderGlobalContext*)info->callParam; + lmg->SetRGC(rgc); + } +} +#endif // MF_NEW_RGC + +#ifndef MF_NEW_RGC +#define MF_NO_RAY_SHADOW +#endif // MF_NEW_RGC + +#define MF_NO_SHADOW_BLUR +#if defined(MF_NEW_RGC) && !defined(MF_NO_SHADOW_BLUR) +#define MF_NO_SHADOW_BLUR +#endif // MF_NEW_RGC + +class LMGScanPoint +{ +public: + hsScalar fU; + hsPoint3 fBary; +}; + +class LMGScanlineData +{ +public: + LMGScanlineData() : fEmpty(true) {} + + hsBool fEmpty; + LMGScanPoint fNear; + LMGScanPoint fFar; +}; + +static int kDefaultSize = 64; + +static UInt32 MakeUInt32Color(float r, float g, float b, float a) +{ + return (UInt32(a * 255.9f) << 24) + |(UInt32(r * 255.9f) << 16) + |(UInt32(g * 255.9f) << 8) + |(UInt32(b * 255.9f) << 0); +} + +plLightMapGen& plLightMapGen::Instance() +{ + return theLMG; +} + +#ifdef MF_NEW_RGC +// Don't call this ever ever ever. I mean really. Never. +void plLightMapGen::SetRGC(RenderGlobalContext* rgc) +{ + fRGC = rgc; +} +#endif // MF_NEW_RGC + +plLightMapGen::plLightMapGen() +: fWidth(64), + fHeight(64), + fScale(1.f), + fUVWSrc(-1), + fMapRange(-1.f), + fInterface(nil), + fRenderer(nil), + fRecalcLightMaps(true), + fRGC(nil), + fRP(nil) +{ + fWidth = kDefaultSize; + fHeight = kDefaultSize; +} + +plLightMapGen::~plLightMapGen() +{ + Close(); +} + +// Set up the structures we'll need to compute the lighting. +// You could turn off shadows by commenting out the call to +// MakeRenderInstances, since those are the guys that will +// cast shadows. Modify which lights contribute by changing +// the criteria in IFindLightsRecur. +hsBool plLightMapGen::Open(Interface* ip, TimeValue t, bool forceRegen) +{ + if( !fInterface && ip ) + { + fInterface = ip; + fTime = t; + + fRP = TRACKED_NEW RendParams; + fRP->SetRenderElementMgr(fInterface->GetRenderElementMgr(RS_Production)); + +#ifdef MF_NEW_RGC + RegisterNotification( + getRGC, + this, + NOTIFY_PRE_RENDERFRAME + ); + + fRenderer = (Renderer*)CreateInstance(RENDERER_CLASS_ID, Class_ID(SREND_CLASS_ID,0)); + + ViewParams vp; + vp.prevAffineTM = Matrix3(true); + vp.affineTM = Matrix3(true); + vp.projType = PROJ_PERSPECTIVE; + vp.hither = 1.f; + vp.yon = 30.f; + vp.distance = 1.f; + vp.zoom = 1.f; + vp.fov = hsScalarPI / 4.f; + vp.nearRange = 1.f; + vp.farRange = 30.f; + + fRenderer->Open(fInterface->GetRootNode(), + nil, + &vp, + *fRP, + fInterface->GetMAXHWnd()); + + FrameRendParams frp; + frp.ambient.Black(); + frp.background.Black(); + frp.globalLightLevel.Black(); + frp.frameDuration = 1.f; + frp.relSubFrameDuration = 1.f; + frp.regxmin = 0; + frp.regxmax = 1; + frp.regymin = 0; + frp.regymax = 1; + frp.blowupCenter = Point2(0.5f,0.5f); + frp.blowupFactor = Point2(1.f, 1.f); + + BitmapInfo bminfo; + bminfo.SetType(BMM_TRUE_32); + bminfo.SetWidth(2); + bminfo.SetHeight(2); + bminfo.SetCustWidth(1); + bminfo.SetCustHeight(1); + if( !bminfo.Validate() ) + { + // oops! + return false; + } + + Bitmap* tobm = TheManager->Create(&bminfo); + + + fRenderer->Render(fTime, + tobm, + frp, + fInterface->GetMAXHWnd() + ); + + tobm->DeleteThis(); + +#else MF_NEW_RGC + + fRGC = TRACKED_NEW plRenderGlobalContext(fInterface, fTime); + fRGC->MakeRenderInstances((plMaxNode*)fInterface->GetRootNode(), fTime); + +#endif // MF_NEW_RGC + + fPreppedMipmaps.SetCount(0); + fCreatedLayers.SetCount(0); + fNewMaps.SetCount(0); + + fAllLights.SetCount(0); + fActiveLights.SetCount(0); + IFindLightsRecur((plMaxNode*)fInterface->GetRootNode()); + } + fRecalcLightMaps = forceRegen; + + return fAllLights.GetCount() > 0; +} + +hsBool plLightMapGen::Close() +{ + // HACK to get rid of keys held by the lightmap components, because + // we can't delete the bitmaps in ICompressLightMaps unless these + // refs are gone + for (int i = 0; i < fSharedComponents.size(); i++) + { + if (fSharedComponents[i]->GetLightMapKey()) // if it has a key + fSharedComponents[i]->SetLightMapKey(nil); // nil it out + } + fSharedComponents.clear(); + + ICompressLightMaps(); + +#ifndef MF_NEW_RGC + delete fRGC; +#else // MF_NEW_RGC + if( fRenderer ) + fRenderer->Close(fInterface->GetMAXHWnd()); + fRenderer = nil; +#endif // MF_NEW_RGC + fRGC = nil; + delete fRP; + fRP = nil; + + fPreppedMipmaps.SetCount(0); + fCreatedLayers.SetCount(0); + fNewMaps.SetCount(0); + + IReleaseActiveLights(); + IReleaseAllLights(); + + fInterface = nil; + + return true; +} + +//#define MIPMAP_LOG + +#ifdef MIPMAP_LOG +void DumpMipmap(plMipmap* mipmap, const char* prefix) +{ + hsUNIXStream dump; + char buf[256]; + sprintf(buf, "log\\%s.txt", prefix); + dump.Open(buf, "wt"); + + for (int i = 0; i < mipmap->GetNumLevels(); i++) + { + mipmap->SetCurrLevel(i); + + UInt32 width = mipmap->GetCurrWidth(); + UInt32 height = mipmap->GetCurrHeight(); + + sprintf(buf, "----- Level %d (%dx%d) -----\n", i, width, height); + dump.WriteString(buf); + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + UInt32 color = *(mipmap->GetAddr32(x, y)); + UInt8 r = ((UInt8)((color)>>16)); + UInt8 g = ((UInt8)((color)>>8)); + UInt8 b = ((UInt8)((color)>>0)); + UInt8 a = ((UInt8)((color)>>24)); + sprintf(buf, "[%3d,%3d,%3d,%3d]", r, g, b, a); + dump.WriteString(buf); + } + + dump.WriteString("\n"); + } + } + + dump.Close(); +} +#endif // MIPMAP_LOG + +hsBool plLightMapGen::ICompressLightMaps() +{ + int i; + for( i = 0; i < fPreppedMipmaps.GetCount(); i++ ) + { + plMipmap* orig = fPreppedMipmaps[i]; + + if( orig ) + { + const hsScalar kFilterSigma = 1.0f; + + if( IsFresh(orig) ) + { + hsAssert(!orig->IsCompressed(), "How did we just generate a compressed texture?"); + orig->Filter(kFilterSigma); + } + + if( !orig->IsCompressed() && !(orig->GetFlags() & plMipmap::kForceNonCompressed) ) + { +#ifdef MIPMAP_LOG + DumpMipmap(orig, orig->GetKeyName()); +#endif // MIPMAP_LOG + + plMipmap *compressed = + hsCodecManager::Instance().CreateCompressedMipmap(plMipmap::kDirectXCompression, orig); + + if( compressed ) + { + const plLocation &textureLoc = plPluginResManager::ResMgr()->GetCommonPage(orig->GetKey()->GetUoid().GetLocation(), + plAgeDescription::kTextures ); + char name[512]; + sprintf(name, "%s_DX", orig->GetKey()->GetName()); + + plKey compKey = hsgResMgr::ResMgr()->FindKey(plUoid(textureLoc, plMipmap::Index(), name)); + if( compKey ) + plBitmapCreator::Instance().DeleteExportedBitmap(compKey); + + hsgResMgr::ResMgr()->NewKey( name, compressed, textureLoc ); + + int j; + for( j = 0; j < fCreatedLayers.GetCount(); j++ ) + { + if( orig == fCreatedLayers[j]->GetTexture() ) + { + fCreatedLayers[j]->GetKey()->Release(orig->GetKey()); + hsgResMgr::ResMgr()->AddViaNotify(compressed->GetKey(), TRACKED_NEW plLayRefMsg(fCreatedLayers[j]->GetKey(), plRefMsg::kOnReplace, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef); + } + } + + plBitmapCreator::Instance().DeleteExportedBitmap(orig->GetKey()); + } + } + } + } + return true; +} + +hsBool plLightMapGen::MakeMaps(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, hsTArray &spans, plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + const char* dbgNodeName = node->GetName(); + + plLightMapComponent* lmapComp = node->GetLightMapComponent(); + if( !lmapComp ) + return false; + + SetUVWSrc(lmapComp->GetUVWSrc()); + SetScale(lmapComp->GetScale()); + + // If we don't want maps here, don't bother. + if( !IWantsMaps(node) ) + { + pErrMsg->Set(true, node->GetName(), "Lightmap generation requested on bogus object").CheckAndAsk(); + return false; + } + + if( !IValidateUVWSrc(spans) ) + { + pErrMsg->Set(true, node->GetName(), "Lightmap generation requested but UVW src bogus. Check mapping.").CheckAndAsk(); + return false; + } + + // If there aren't any lights, don't bother + if( !InitNode(node, false) ) + { + pErrMsg->Set(true, node->GetName(), "Lightmap generation requested but no lights on object. Kind of wasteful.").CheckAndAsk(); + return true; + } + + // If we have trouble getting a bitmap size, there's probably something wrong with the geometry + if( !ISelectBitmapDimension(node, l2w, w2l, spans) ) + { + pErrMsg->Set(true, node->GetName(), "Lightmap generation failure determining bitmap size, probably geometry problem.").CheckAndAsk(); + return false; + } + + // Okay, we're going to do it. The lights are + // set up for this guy so we just need some geometry. + // Find the drawable and which spans correspond + // to this node, and feed them through. + // + // IShadeGeometrySpans() and lower return whether any light was actually found. + if( !IShadeGeometrySpans(node, l2w, w2l, spans) ) + { + pErrMsg->Set(true, node->GetName(), "Lightmap generation requested but no light found on object. Kind of wasteful.").CheckAndAsk(); + } + + DeInitNode(); + + return true; +} + +// The next couple of functions don't do anything interesting except +// get us down to the face level where we can work. +hsBool plLightMapGen::IShadeGeometrySpans(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, hsTArray &spans) +{ + hsBool retVal = false; + int i; + for( i = 0; i < spans.GetCount(); i++ ) + { + retVal |= IShadeSpan(node, l2w, w2l, *spans[i]); + } + return retVal; +} + +hsBool plLightMapGen::IsFresh(plBitmap* map) const +{ + return fRecalcLightMaps || fNewMaps.Find(map) != fNewMaps.kMissingIndex; +} + +hsBool plLightMapGen::IShadeSpan(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, plGeometrySpan& span) +{ + // This will look for a suitable lightmap layer and return that. If there + // isn't one already, it will set one up for us. + plLayerInterface* lay = IGetLightMapLayer(node, span); + // This next check should never happen, since we've created the layer ourselves. + if( !lay || !lay->GetTexture() )//|| !lay->GetTexture()->GetBitmap() ) + return false; + + int i; + + if( !(span.fProps & plGeometrySpan::kDiffuseFoldedIn) ) + { + hsBool foldin = 0 != (span.fProps & plGeometrySpan::kLiteVtxNonPreshaded); + hsScalar opacity = 1.f; + hsColorRGBA dif = hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f); + if( foldin ) + { + // Find the opacity to fold in. + // There should be one (or less) layers in this material using layer opacity, + // or else we should have put this span as kLiteMaterial instead of kLiteVtxNonPreshaded, + // so we're safe just getting the first opacity on a blended layer. + // Likewise we're safe getting the first diffuse color that's not on an + // emissive layer (since emissive layers ignore lightmapping). + // If we are using kLiteMaterial, we still need to copy from InitColor to Stuff, + // just don't do the modulate. + for( i = 0; i < span.fMaterial->GetNumLayers(); i++ ) + { + if( span.fMaterial->GetLayer(i)->GetBlendFlags() & hsGMatState::kBlendAlpha ) + { + opacity = span.fMaterial->GetLayer(i)->GetOpacity(); + break; + } + } + for( i = 0; i < span.fMaterial->GetNumLayers(); i++ ) + { + if( !(span.fMaterial->GetLayer(i)->GetShadeFlags() & hsGMatState::kShadeEmissive) ) + { + dif = span.fMaterial->GetLayer(i)->GetRuntimeColor(); + break; + } + } + } + for( i = 0; i < span.fNumVerts; i++ ) + { + hsColorRGBA multColor, addColor; + span.ExtractInitColor( i, &multColor, &addColor); + if( foldin ) + { + multColor *= dif; // We like to use kVertexNonPreshaded for lightmapped objects, which needs the runtime diffuse folded in + multColor.a *= opacity; + } + addColor.Set(0,0,0,0); + span.StuffVertex(i, &multColor, &addColor); + } + if( span.fInstanceRefs ) + { + int j; + for( j = 0; j < span.fInstanceRefs->GetCount(); j++ ) + { + plGeometrySpan* inst = (*span.fInstanceRefs)[j]; + inst->fProps |= plGeometrySpan::kDiffuseFoldedIn; + } + } + } + else + { + for( i = 0; i < span.fNumVerts; i++ ) + { + hsColorRGBA multColor, addColor; + span.ExtractInitColor( i, &multColor, &addColor); + addColor.Set(0,0,0,0); + span.StuffVertex(i, &multColor, &addColor); + } + return true; + } + + // If we aren't recalculating all our lightmaps, then we only want to compute lightmaps + // which have a creation time of now. + if( !IsFresh(lay->GetTexture()) ) + return true; + + plMipmap* accum = IMakeAccumBitmap(lay); + + hsBool retVal = false; + int nFaces = span.fNumIndices / 3; + for( i = 0; i < nFaces; i++ ) + { + retVal |= IShadeFace(node, l2w, w2l, span, i, accum); + } + + IAddToLightMap(lay, accum); + + return retVal; +} + +plMipmap* plLightMapGen::IMakeAccumBitmap(plLayerInterface* lay) const +{ + plMipmap* dst = plMipmap::ConvertNoRef( lay->GetTexture() );//->GetBitmap(); + hsAssert( dst != nil, "nil mipmap in IMakeAccumBitmap()" ); + + int width = dst->GetWidth(); + int height = dst->GetHeight(); + + // Temporary mipmap here, so we don't have to worry about using plBitmapCreator + plMipmap* bitmap = TRACKED_NEW plMipmap( width, height, plMipmap::kRGB32Config, 1 ); + HSMemory::Clear(bitmap->GetImage(), bitmap->GetHeight() * bitmap->GetRowBytes() ); + + return bitmap; +} + +hsBool plLightMapGen::IAddToLightMap(plLayerInterface* lay, plMipmap* src) const +{ + plMipmap* dst = plMipmap::ConvertNoRef( lay->GetTexture() );//->GetBitmap(); + hsAssert( dst != nil, "nil mipmap in IAddToLightMap()" ); + + src->SetCurrLevel( 0 ); + dst->SetCurrLevel( 0 ); + + // BLURLATER +// static hsScalar kFilterSigma = 0.5f; +// src->Filter(kFilterSigma); + + // What we really want to do here is antialias our rasterization, so we can + // just sum in contributions of lighting at the boarder between spans sharing + // a light map. A quick hackaround is to use the max between existing color + // (in dst) and current spans illumination contribution (in src). + int i, j; + for( j = 0; j < dst->GetHeight(); j++ ) + { + for( i = 0; i < dst->GetWidth(); i++ ) + { + UInt32 srcRed = (*src->GetAddr32(i, j) >> 16) & 0xff; + UInt32 dstRed = (*dst->GetAddr32(i, j) >> 16) & 0xff; +// dstRed += srcRed; + if( dstRed < srcRed ) + dstRed = srcRed; + if( dstRed > 0xff ) + dstRed = 0xff; + + UInt32 srcGreen = (*src->GetAddr32(i, j) >> 8) & 0xff; + UInt32 dstGreen = (*dst->GetAddr32(i, j) >> 8) & 0xff; +// dstGreen += srcGreen; + if( dstGreen < srcGreen ) + dstGreen = srcGreen; + if( dstGreen > 0xff ) + dstGreen = 0xff; + + UInt32 srcBlue = (*src->GetAddr32(i, j) >> 0) & 0xff; + UInt32 dstBlue = (*dst->GetAddr32(i, j) >> 0) & 0xff; +// dstBlue += srcBlue; + if( dstBlue < srcBlue ) + dstBlue = srcBlue; + if( dstBlue > 0xff ) + dstBlue = 0xff; + + *dst->GetAddr32(i, j) = 0xff000000 + | (dstRed << 16) + | (dstGreen << 8) + | (dstBlue << 0); + } + } + dst->MakeDirty(); + + delete src; + + return true; +} + +hsBool plLightMapGen::IShadeFace(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, plGeometrySpan& span, int iFace, plMipmap* bitmap) +{ + // Okay, here's where the metal hits the road, whatever that means. + // We're going to get our bitmap, and step along the face texel by texel, + // summing up the light at each texel and stuffing it in the bitmap. + + // Set up a light context for the shading below. + Box3 bbox; + node->EvalWorldState(fTime).obj->GetDeformBBox(fTime, bbox, &node->GetObjectTM(fTime)); + plMaxLightContext ctx(bbox, fTime); + + // First, get the face info we'll be using. + + // This will look for a suitable lightmap layer and return that. + // There should be one there already, because we called this + // in IShadeSpan + plLayerInterface* lay = IGetLightMapLayer(node, span); + int iOurUv = IGetUVWSrc(); + // A little late to be checking this, but whatever... + if( iOurUv < 0 ) + return false; + + int width = bitmap->GetWidth(); + int height = bitmap->GetHeight(); + + hsMatrix44 norml2w; + hsMatrix44 temp; + + l2w.GetInverse( &temp); + temp.GetTranspose( &norml2w ); + + hsPoint3 pt[3]; + hsVector3 norm[3]; + hsPoint3 uv[3]; + + int i; + for( i = 0; i < 3; i++ ) + { + hsColorRGBA trash; + + span.ExtractVertex(span.fIndexData[iFace*3 + i], &pt[i], &norm[i], &trash); + span.ExtractUv(span.fIndexData[iFace*3 + i], iOurUv, &uv[i]); + + pt[i] = l2w * pt[i]; + + norm[i] = norml2w * norm[i]; + + uv[i] = lay->GetTransform() * uv[i]; + + uv[i].fX *= width-1; + uv[i].fX += 0.5f; + uv[i].fY *= height-1; + uv[i].fY += 0.5f; + + } + + Color amb(0,0,0); + + return IShadeVerts(ctx, amb, pt, norm, uv, bitmap); +} + +hsBool plLightMapGen::IShadeVerts(plMaxLightContext& ctx, const Color& amb, const hsPoint3 pt[3], const hsVector3 norm[3], const hsPoint3 uv[3], plMipmap* bitmap) +{ + int width = bitmap->GetWidth(); + int height = bitmap->GetHeight(); + bitmap->SetCurrLevel( 0 ); + + hsTArray scanline; + scanline.SetCount(height); + + int lowestV = height; + int highestV = 0; + int i0, i1, i2; + for( i0 = 0; i0 < 3; i0++ ) + { + i1 = i0 == 2 ? 0 : i0+1; + i2 = i1 == 2 ? 0 : i1+1; + + hsScalar v0 = uv[i0].fY; + hsScalar v1 = uv[i1].fY; + + int vStart = int(v0); + int vEnd = int(v1); + if( vStart == vEnd ) + continue; + + int vStep = vStart < vEnd ? 1 : -1; + int vMid; + for( vMid = vStart; vMid != vEnd + vStep; vMid += vStep ) + { + // This shouldn't really happen, but might with some slop. + if( (vMid < 0) || (vMid >= height) ) + continue; + + hsPoint3 bary; + bary[i0] = (v1 - float(vMid)) / (v1 - v0); + bary[i1] = 1.f - bary[i0]; + bary[i2] = 0; + hsScalar u = uv[i0].fX * bary[i0] + + uv[i1].fX * bary[i1]; + if( scanline[vMid].fEmpty ) + { + scanline[vMid].fNear.fU = u; + scanline[vMid].fNear.fBary = bary; + scanline[vMid].fFar = scanline[vMid].fNear; + + scanline[vMid].fEmpty = false; + if( vMid < lowestV ) + lowestV = vMid; + if( vMid > highestV ) + highestV = vMid; + } + else + { + if( u < scanline[vMid].fNear.fU ) + { + scanline[vMid].fNear.fU = u; + scanline[vMid].fNear.fBary = bary; + } + else if( u > scanline[vMid].fFar.fU ) + { + scanline[vMid].fFar.fU = u; + scanline[vMid].fFar.fBary = bary; + } + } + } + } + int i; + for( i = lowestV; i <= highestV; i++ ) + { + if( !scanline[i].fEmpty ) + { + int uStart = int(scanline[i].fNear.fU); + if( uStart < 0 ) + uStart = 0; + int uEnd = int(scanline[i].fFar.fU); + if( uEnd >= width ) + uEnd = width - 1; + if( uStart == uEnd ) + continue; + int uMid; + for( uMid = uStart; uMid <= uEnd; uMid++ ) + { + hsScalar t = (scanline[i].fFar.fU - float(uMid)) / (scanline[i].fFar.fU - scanline[i].fNear.fU); + hsPoint3 bary = scanline[i].fNear.fBary * t; + bary += scanline[i].fFar.fBary * (1.f - t); + + hsPoint3 p = pt[0] * bary[0] + pt[1] * bary[1] + pt[2] * bary[2]; + hsVector3 n = norm[0] * bary[0] + norm[1] * bary[1] + norm[2] * bary[2]; + + hsFastMath::NormalizeAppr(n); + + UInt32 color = IShadePoint(ctx, amb, p, n); + *bitmap->GetAddr32(uMid, i) = color; + + } + } + } + + return true; +} + +hsBool plLightMapGen::IGetLight(INode* node) +{ + if( node->UserPropExists("RunTimeLight") ) + return false; + + Object *obj = node->EvalWorldState(fTime).obj; + + if (obj && (obj->SuperClassID() == SClass_ID(LIGHT_CLASS_ID))) + { + plLightMapInfo* liInfo = fAllLights.Push(); + + LightObject* liObj = (LightObject*)obj; + + liInfo->fResetShadowType = 0; + liInfo->fResetMapRange = -1.f; + liInfo->fMapRange = -1.f; + + liInfo->fLiNode = node; + liInfo->fObjLiDesc = nil; + liInfo->fNewRender = true; + + return true; + } + + return false; +} + +hsBool plLightMapGen::Update(TimeValue t) +{ + fTime = t; + +#ifndef MF_NEW_RGC + if( fRGC ) + fRGC->Update(t); +#endif // MF_NEW_RGC + + return fAllLights.GetCount() != 0; +} + +hsBool plLightMapGen::IFindLightsRecur(INode* node) +{ + IGetLight(node); + + int i; + for( i = 0; i < node->NumberOfChildren(); i++ ) + IFindLightsRecur(node->GetChildNode(i)); + + return fAllLights.GetCount() > 0; +} + +hsBool plLightMapGen::InitNode(INode* node, hsBool softShadow) +{ + fActiveLights.SetCount(0); + + plMaxNode* maxNode = (plMaxNode*)node; + if( !maxNode->CanConvert() ) + return false; + + if( maxNode->GetNoPreShade() ) + return false; + +#ifndef MF_NO_SHADOW_BLUR + fMapRange = softShadow ? kBlurMapRange : -1.f; +#endif // MF_NO_SHADOW_BLUR + + IFindActiveLights((plMaxNode*)node); + + return fActiveLights.GetCount() > 0; +} + +hsBool plLightMapGen::DeInitNode() +{ + IReleaseActiveLights(); + + return true; +} + +hsBounds3Ext plLightMapGen::IGetBoundsLightSpace(INode* node, INode* liNode) +{ + TimeValue currTime(0); + + hsBounds3Ext bnd; + bnd.MakeEmpty(); + Object *obj = node->EvalWorldState(currTime).obj; + if( !obj ) + return bnd; + + Box3 box; + + if( obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) ) + { + DummyObject* dummy = (DummyObject*)obj; + box = dummy->GetBox(); + } + else + if( obj->CanConvertToType(triObjectClassID) ) + { + TriObject *meshObj = (TriObject *)obj->ConvertToType(currTime, triObjectClassID); + if( !meshObj ) + return bnd; + + Mesh& mesh = meshObj->mesh; + box = mesh.getBoundingBox(); + + if( meshObj != obj ) + meshObj->DeleteThis(); + } + + bnd.Union(&hsPoint3(box.pmin.x, box.pmin.y, box.pmin.z)); + bnd.Union(&hsPoint3(box.pmax.x, box.pmax.y, box.pmax.z)); + + Matrix3 maxL2W = node->GetObjectTM(currTime); + Matrix3 maxW2Light = Inverse(liNode->GetObjectTM(currTime)); + Matrix3 maxL2Light = maxL2W * maxW2Light; + hsMatrix44 l2l; + hsControlConverter::Instance().Matrix3ToHsMatrix44(&maxL2Light, &l2l); + + + bnd.Transform(&l2l); + + return bnd; +} + +hsBool plLightMapGen::IDirAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node) +{ + hsBounds3Ext bnd = IGetBoundsLightSpace(node, liInfo->fLiNode); + + if( bnd.GetType() != kBoundsNormal ) + return false; + + if( bnd.GetMins().fZ > 0 ) + return false; + + LightState ls; + liObj->EvalLightState(TimeValue(0), FOREVER, &ls); + + hsScalar radX = ls.fallsize; + hsScalar radY = radX; + if( ls.shape == RECT_LIGHT ) + radY /= ls.aspect; + + if( bnd.GetMins().fX > radX ) + return false; + if( bnd.GetMaxs().fX < -radX ) + return false; + + if( bnd.GetMins().fY > radY ) + return false; + if( bnd.GetMaxs().fY < -radY ) + return false; + + if( !ls.useAtten ) + return true; + + if( bnd.GetMaxs().fZ < -ls.attenEnd ) + return false; + + return true; +} + +hsBool plLightMapGen::ISpotAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node) +{ + hsBounds3Ext bnd = IGetBoundsLightSpace(node, liInfo->fLiNode); + + if( bnd.GetType() != kBoundsNormal ) + return false; + + if( bnd.GetMins().fZ > 0 ) + return false; + + LightState ls; + liObj->EvalLightState(TimeValue(0), FOREVER, &ls); + + hsScalar coneRad[2]; + coneRad[0] = ls.fallsize * hsScalarPI / 180.f; + coneRad[1] = coneRad[0]; + if( ls.shape == RECT_LIGHT ) + coneRad[1] /= ls.aspect; + + hsPoint3 corners[8]; + bnd.GetCorners(corners); + + int numPos[4] = { 0, 0, 0, 0 }; + int j; + for( j = 0; j < 8; j++ ) + { + hsScalar rad; + rad = hsScalar(atan2(corners[j].fX, -corners[j].fZ)); + if( rad > coneRad[0] ) + numPos[0]++; + if( rad < -coneRad[0] ) + numPos[2]++; + rad = hsScalar(atan2(corners[j].fY, -corners[j].fZ)); + if( rad > coneRad[1] ) + numPos[1]++; + if( rad < -coneRad[1] ) + numPos[3]++; + } + for( j = 0; j < 4; j++ ) + { + if( numPos[j] >= 8 ) + return false; + } + + if( ls.useAtten ) + { + if( bnd.GetMaxs().fZ < -ls.attenEnd ) + return false; + } + + return true; +} + +hsBool plLightMapGen::IOmniAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node) +{ + LightState ls; + liObj->EvalLightState(TimeValue(0), FOREVER, &ls); + + if( !ls.useAtten ) + return true; + + hsBounds3Ext bnd = IGetBoundsLightSpace(node, liInfo->fLiNode); + + if( bnd.GetType() != kBoundsNormal ) + return false; + + hsScalar radius = ls.attenEnd; + + int i; + for( i = 0; i < 3; i++ ) + { + if( bnd.GetMins()[i] > radius ) + return false; + if( bnd.GetMaxs()[i] < -radius ) + return false; + } + + return true; +} + +hsBool plLightMapGen::ILightAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node) +{ + const char* liName = liInfo->fLiNode->GetName(); + const char* nodeName = node->GetName(); + + LightState ls; + liObj->EvalLightState(TimeValue(0), FOREVER, &ls); + + hsBool excluded = false; + if( !liObj->GetUseLight() ) + { + excluded = true; + } + if( !excluded && liObj->GetExclList() && liObj->GetExclList()->TestFlag(NT_AFFECT_ILLUM) ) + { + hsBool inExc = -1 != liObj->GetExclList()->FindNode(node); + if( (!inExc) ^ (!liObj->GetExclList()->TestFlag(NT_INCLUDE)) ) + excluded = true; + } + if( excluded ) + return false; + + switch( ls.type ) + { + case OMNI_LGT: + return IOmniAffectsNode(liInfo, liObj, node); + case SPOT_LGT: + return ISpotAffectsNode(liInfo, liObj, node); + case DIRECT_LGT: + return IDirAffectsNode(liInfo, liObj, node); + default: + case AMBIENT_LGT: + return true; + } + return false; +} + +hsBool plLightMapGen::IPrepLight(plLightMapInfo* liInfo, INode* node) +{ + const char* liName = liInfo->fLiNode->GetName(); + const char* nodeName = node->GetName(); + + INode* liNode = liInfo->fLiNode; + LightObject* liObj = (LightObject*)liNode->EvalWorldState(fTime).obj; + + // redundant check, if it doesn't have a light object it shouldn't be in the list + if( liObj ) + { + hsBool affectsNode = ILightAffectsNode(liInfo, liObj, node); + if( affectsNode ) + { + +#ifdef MF_NO_RAY_SHADOW + // for reasons known only to god and someone deep in the bowels of kinetix, + // the lighting is (sometimes) barfing if the shadow type is ray-traced. + // until i can track that down, i'll force shadow mapped shadows. + liInfo->fResetShadowType = liObj->GetShadowType(); + if( liInfo->fResetShadowType > 0 ) + { + liObj->SetShadowType(0); + liInfo->fNewRender = true; + } +#endif MF_NO_RAY_SHADOW + if( fMapRange > 0 ) + { + if( liInfo->fMapRange != fMapRange ) + { + liInfo->fResetMapRange = liObj->GetMapRange(fTime); + liObj->SetMapRange(fTime, fMapRange); + liInfo->fMapRange = fMapRange; + liInfo->fNewRender = true; + } + } + else if( liInfo->fResetMapRange > 0 ) + { + if( liInfo->fMapRange != liInfo->fResetMapRange ) + { + liObj->SetMapRange(fTime, liInfo->fResetMapRange); + liInfo->fMapRange = liInfo->fResetMapRange; + liInfo->fNewRender = true; + } + } + + ObjLightDesc* objLiDesc = liInfo->fObjLiDesc; + if( !objLiDesc ) + objLiDesc = liObj->CreateLightDesc(liNode); + + plMaxRendContext rc; + objLiDesc->Update(fTime, rc, fRGC, node->RcvShadows(), liInfo->fNewRender); + objLiDesc->UpdateViewDepParams(Matrix3(true)); + + liInfo->fNewRender = false; + + liInfo->fObjLiDesc = objLiDesc; + + fActiveLights.Append(liInfo); + } + } + + return true; +} + +hsBool plLightMapGen::IFindActiveLights(plMaxNode* node) +{ + fActiveLights.SetCount(0); + int i; + for( i = 0; i < fAllLights.GetCount(); i++ ) + { + IPrepLight(&fAllLights[i], node); + } + + return fActiveLights.GetCount() > 0; +} + +hsBool plLightMapGen::IReleaseAllLights() +{ + int i; + for( i = 0; i < fAllLights.GetCount(); i++ ) + { + if( fAllLights[i].fResetMapRange > 0 ) + { + LightObject* liObj = (LightObject*)fAllLights[i].fLiNode->EvalWorldState(fTime).obj; + liObj->SetMapRange(fTime, fAllLights[i].fResetMapRange); + + } +#ifdef MF_NO_RAY_SHADOW + // Fix the shadow method back. + if( fAllLights[i].fResetShadowType > 0 ) + { + LightObject* liObj = (LightObject*)fAllLights[i].fLiNode->EvalWorldState(fTime).obj; + liObj->SetShadowType(fAllLights[i].fResetShadowType); + } +#endif // MF_NO_RAY_SHADOW + + if( fAllLights[i].fObjLiDesc ) + fAllLights[i].fObjLiDesc->DeleteThis(); + + fAllLights[i].fObjLiDesc = nil; + } + fAllLights.SetCount(0); + + return true; +} + +hsBool plLightMapGen::IReleaseActiveLights() +{ + fActiveLights.SetCount(0); + + return true; +} + +hsBool plLightMapGen::IWantsMaps(plMaxNode* node) +{ + if( !(node->CanConvert() && node->GetDrawable()) ) + return false; + + return nil != node->GetLightMapComponent(); +} + +hsBool plLightMapGen::IValidateUVWSrc(hsTArray& spans) const +{ + int i; + for( i = 0; i < spans.GetCount(); i++ ) + { + int numUVWs = spans[i]->GetNumUVs(); + if( IGetUVWSrc() >= numUVWs ) + return false; + } + return true; +} + +void plLightMapGen::IInitBitmapColor(plMipmap* bitmap, const hsColorRGBA& col) const +{ + UInt32 initColor = MakeUInt32Color(col.r, col.g, col.b, col.a); + UInt32* pix = (UInt32*)bitmap->GetImage(); + UInt32* pixEnd = ((UInt32*)bitmap->GetImage()) + bitmap->GetWidth() * bitmap->GetHeight(); + while( pix < pixEnd ) + *pix++ = initColor; +} + +plLayerInterface* plLightMapGen::IGetLightMapLayer(plMaxNode* node, plGeometrySpan& span) +{ + plLayerInterface* lay = IMakeLightMapLayer(node, span); + + plMipmap* mip = plMipmap::ConvertNoRef(lay->GetTexture()); + hsAssert(mip, "This should have been a mipmap we created ourselves."); + if( !mip ) + return nil; + if( fPreppedMipmaps.Find(mip) == fPreppedMipmaps.kMissingIndex ) + { + if( IsFresh(mip) ) + { + hsColorRGBA initColor = node->GetLightMapComponent()->GetInitColor(); + // Get this off the node, where the lightmap component has stashed it. + IInitBitmapColor(mip, initColor); + } + + fPreppedMipmaps.Append(mip); + } + if( fCreatedLayers.Find(lay) == fCreatedLayers.kMissingIndex ) + { + fCreatedLayers.Append(lay); + } + return lay; +} + +plLayerInterface* plLightMapGen::IMakeLightMapLayer(plMaxNode* node, plGeometrySpan& span) +{ + hsGMaterial* mat = span.fMaterial; + + int i; + for( i = 0; i < mat->GetNumPiggyBacks(); i++ ) + { + if( mat->GetPiggyBack(i)->GetMiscFlags() & hsGMatState::kMiscLightMap ) + return mat->GetPiggyBack(i); + } + + char newMatName[256]; + sprintf(newMatName, "%s_%s_LIGHTMAPGEN", mat->GetKey()->GetName(), node->GetName()); + plLocation nodeLoc = node->GetLocation(); + + plKey matKey = hsgResMgr::ResMgr()->FindKey(plUoid(nodeLoc, hsGMaterial::Index(), newMatName)); + if( matKey ) + { + mat = hsGMaterial::ConvertNoRef(matKey->ObjectIsLoaded()); + for( i = 0; i < mat->GetNumPiggyBacks(); i++ ) + { + if( mat->GetPiggyBack(i)->GetMiscFlags() & hsGMatState::kMiscLightMap ) + { + span.fMaterial = mat; + return mat->GetPiggyBack(i); + } + } + hsAssert(false, "Something not a light map material registered with our name?"); + } + hsGMaterial* objMat = nil; + + hsBool sharemaps = node->GetLightMapComponent()->GetShared(); + if( sharemaps ) + { + objMat = mat; + } + else + { + objMat = TRACKED_NEW hsGMaterial; + hsgResMgr::ResMgr()->NewKey(newMatName, objMat, nodeLoc); + + for( i = 0; i < mat->GetNumLayers(); i++ ) + hsgResMgr::ResMgr()->AddViaNotify(mat->GetLayer(i)->GetKey(), TRACKED_NEW plMatRefMsg(objMat->GetKey(), plRefMsg::kOnCreate, -1, plMatRefMsg::kLayer), plRefFlags::kActiveRef); + } + + objMat->SetCompositeFlags(objMat->GetCompositeFlags() | hsGMaterial::kCompIsLightMapped); + + // Make sure layer (and mip) name are unique across pages by putting the page name in + const plPageInfo* pageInfo = plKeyFinder::Instance().GetLocationInfo(node->GetLocation()); + + char layName[256]; + sprintf(layName, "%s_%s_LIGHTMAPGEN", pageInfo->GetPage(), node->GetName()); + + plKey layKey = node->FindPageKey(plLayer::Index(), layName); + + + if( !layKey ) + { + int w = fWidth; + int h = fHeight; + + plKey mipKey; + if( node->GetLightMapComponent()->GetLightMapKey() ) + { + mipKey = node->GetLightMapComponent()->GetLightMapKey(); + } + else + { + char mipmapName[ 256 ]; + sprintf( mipmapName, "%s_mip", layName ); + + // Deleted the NOTE here because it was incorrect in every meaningful sense of the word. - mf + + const plLocation &textureLoc = plPluginResManager::ResMgr()->GetCommonPage( nodeLoc, plAgeDescription::kTextures ); + + mipKey = hsgResMgr::ResMgr()->FindKey(plUoid(textureLoc, plMipmap::Index(), mipmapName)); + + if( !mipKey && !fRecalcLightMaps ) + { + char compressedName[512]; + sprintf(compressedName, "%s_DX", mipmapName); + + plKey compKey = hsgResMgr::ResMgr()->FindKey(plUoid(textureLoc, plMipmap::Index(), compressedName)); + + if( compKey ) + mipKey = compKey; + } + + if( mipKey ) + { + plBitmap* bitmap = plBitmap::ConvertNoRef(mipKey->ObjectIsLoaded()); + if( bitmap ) + { + if( node->GetLightMapComponent()->GetCompress() != bitmap->IsCompressed() ) + { + // make sure the lightmap component isn't holding a key, + // it will get assigned one a few lines later anyway + if (node->GetLightMapComponent()->GetLightMapKey()) + node->GetLightMapComponent()->SetLightMapKey(nil); + + plBitmapCreator::Instance().DeleteExportedBitmap(mipKey); + } + } + } + + if( !mipKey ) + { + plMipmap* bitmap = plBitmapCreator::Instance().CreateBlankMipmap(w, h, plMipmap::kRGB32Config, 1, mipmapName, nodeLoc); + mipKey = bitmap->GetKey(); + fNewMaps.Append(bitmap); + + if( !node->GetLightMapComponent()->GetCompress() ) + bitmap->SetFlags(bitmap->GetFlags() | plMipmap::kForceNonCompressed); + } + if( node->GetLightMapComponent()->GetShared() ) + { + // HACK since we are setting the key, save the pointer to the light map + // component so we can get rid of the key it holds later + fSharedComponents.push_back(node->GetLightMapComponent()); + + node->GetLightMapComponent()->SetLightMapKey(mipKey); + } + } + + plLayer* layer = TRACKED_NEW plLayer; + layer->InitToDefault(); + layKey = hsgResMgr::ResMgr()->NewKey(layName, layer, nodeLoc); + hsgResMgr::ResMgr()->AddViaNotify(mipKey, TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef); + layer->SetAmbientColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); + layer->SetZFlags(hsGMatState::kZNoZWrite); + layer->SetBlendFlags(hsGMatState::kBlendMult); + layer->SetClampFlags(hsGMatState::kClampTexture); + layer->SetUVWSrc(IGetUVWSrc()); + layer->SetMiscFlags(hsGMatState::kMiscLightMap); + } + + hsgResMgr::ResMgr()->AddViaNotify(layKey, TRACKED_NEW plMatRefMsg(objMat->GetKey(), plRefMsg::kOnCreate, -1, plMatRefMsg::kPiggyBack), plRefFlags::kActiveRef); + + span.fMaterial = objMat; + + + return plLayerInterface::ConvertNoRef(layKey->GetObjectPtr()); + +} + +// Like ShadePoint, but only computes the amount of light striking the surface, +// so ignoring the N dot L term. +Color plLightMapGen::ShadowPoint(plMaxLightContext& ctx) +{ + ctx.globContext = fRGC; + + Color accum; + accum.Black(); + int i; + for( i = 0; i < fActiveLights.GetCount(); i++ ) + { + const char* dbgLiName = fActiveLights[i]->fLiNode->GetName(); + + Color color; + Point3 liDir; + float dot_nl, diffuseCoef; + BOOL hit = fActiveLights[i]->fObjLiDesc->Illuminate(ctx, ctx.Normal(), color, liDir, dot_nl, diffuseCoef); + if( hit ) + { + accum += color; + } + } + + return accum; +} + +Color plLightMapGen::ShadePoint(plMaxLightContext& ctx) +{ + ctx.globContext = fRGC; + + Color accum; + accum.Black(); + int i; + for( i = 0; i < fActiveLights.GetCount(); i++ ) + { + Color color; + Point3 liDir; + float dot_nl, diffuseCoef; + BOOL hit = fActiveLights[i]->fObjLiDesc->Illuminate(ctx, ctx.Normal(), color, liDir, dot_nl, diffuseCoef); + if( hit ) + { + accum += color * diffuseCoef; + } + } + + return accum; +} + +Color plLightMapGen::ShadePoint(plMaxLightContext& ctx, const Point3& p, const Point3& n) +{ + ctx.SetPoint(p, n); + + return ShadePoint(ctx); + +} + +Color plLightMapGen::ShadePoint(plMaxLightContext& ctx, const hsPoint3& p, const hsVector3& n) +{ + ctx.SetPoint(p, n); + + return ShadePoint(ctx); + +} + +UInt32 plLightMapGen::IShadePoint(plMaxLightContext& ctx, const Color& amb, const hsPoint3& p, const hsVector3& n) +{ + ctx.globContext = fRGC; + ctx.SetPoint(p, n); + + Color accum = ShadePoint(ctx); + accum += amb; + accum.ClampMinMax(); + + UInt32 retVal; + + retVal = MakeUInt32Color(accum.r, accum.g, accum.b, 1.f); + + return retVal; +} + +hsBool plLightMapGen::ISelectBitmapDimension(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, hsTArray& spans) +{ + float duDr = 0; + float dvDr = 0; + + float totFaces = 0; + + int i; + for( i = 0; i < spans.GetCount(); i++ ) + { + plGeometrySpan *span = spans[i]; + + int nFaces = span->fNumIndices / 3; + int j; + for( j = 0; j < nFaces; j++ ) + { + hsPoint3 pt[3]; + hsPoint3 uv[3]; + + int k; + for( k = 0; k < 3; k++ ) + { + hsVector3 vTrash; + hsColorRGBA cTrash; + + span->ExtractVertex(span->fIndexData[j*3 + k], &pt[k], &vTrash, &cTrash); + + pt[k] = l2w * pt[k]; + + span->ExtractUv(span->fIndexData[j*3 + k], IGetUVWSrc(), &uv[k]); + } + + if( (uv[0].fX >= 1.f) + &&(uv[1].fX >= 1.f) + &&(uv[2].fX >= 1.f) ) + continue; + + if( (uv[0].fY >= 1.f) + &&(uv[1].fY >= 1.f) + &&(uv[2].fY >= 1.f) ) + continue; + + if( (uv[0].fX <= 0) + &&(uv[1].fX <= 0) + &&(uv[2].fX <= 0) ) + continue; + + if( (uv[0].fY <= 0) + &&(uv[1].fY <= 0) + &&(uv[2].fY <= 0) ) + continue; + + float magDU[2]; + magDU[0] = fabsf(uv[1].fX - uv[0].fX); + magDU[1] = fabsf(uv[2].fX - uv[0].fX); + if( magDU[0] > magDU[1] ) + { + float dist = hsVector3(pt+1, pt+0).Magnitude(); + + if( dist > 1.e-3f ) + duDr += magDU[0] / dist; + } + else + { + float dist = hsVector3(pt+2, pt+0).Magnitude(); + + if( dist > 1.e-3f ) + duDr += magDU[1] / dist; + } + + float magDV[2]; + magDV[0] = fabsf(uv[1].fY - uv[0].fY); + magDV[1] = fabsf(uv[2].fY - uv[0].fY); + if( magDV[0] > magDV[1] ) + { + float dist = hsVector3(pt+1, pt+0).Magnitude(); + + if( dist > 1.e-3f ) + dvDr += magDV[0] / dist; + } + else + { + float dist = hsVector3(pt+2, pt+0).Magnitude(); + + if( dist > 1.e-3f ) + dvDr += magDV[1] / dist; + } + + totFaces++; + } + } + + if( totFaces < 1.f ) + return false; + + duDr /= totFaces; + dvDr /= totFaces; + + const int kMaxSize = 256; + const int kMinSize = 32; + const int kMaxAspect = 8; + + const float kTexPerFoot = 1.f; + + if( duDr > 0 ) + { + fWidth = kTexPerFoot / duDr; + + if( fWidth > kMaxSize ) + fWidth = kMaxSize; + if( fWidth < kMinSize ) + fWidth = kMinSize; + } + else + { + fWidth = kMinSize; + } + fWidth *= fScale; + fWidth = IPowerOfTwo(fWidth); + + if( dvDr > 0 ) + { + fHeight = kTexPerFoot / duDr; + + if( fHeight > kMaxSize ) + fHeight = kMaxSize; + if( fHeight < kMinSize ) + fHeight = kMinSize; + } + else + { + fHeight = kMinSize; + } + fHeight *= fScale; + fHeight = IPowerOfTwo(fHeight); + + if( fHeight / fWidth > kMaxAspect ) + fWidth = fHeight / kMaxAspect; + if( fWidth / fHeight > kMaxAspect ) + fHeight = fWidth / kMaxAspect; + + if( fWidth > 512 ) + fWidth = 512; + if( fHeight > 512 ) + fHeight = 512; + + return true; +} + +int plLightMapGen::IPowerOfTwo(int sz) const +{ + int i = 0; + while( (1 << i) < sz ) + i++; + + int p2sz = 1 << i; + + if( p2sz - sz > sz - (p2sz >> 1) ) + p2sz >>= 1; + + return p2sz; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLightMapGen.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLightMapGen.h new file mode 100644 index 00000000..f2ce7939 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plLightMapGen.h @@ -0,0 +1,174 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLightMapGen_inc +#define plLightMapGen_inc + +#include "hsTemplates.h" + +#include + +class plMaxLightContext; +class plRenderGlobalContext; +class plLayerInterface; +class plMaxNode; +class hsGMaterial; +class plGeometrySpan; +class plMipmap; +class plBitmap; +struct hsColorRGBA; +struct hsPoint3; +struct hsVector3; +struct hsMatrix44; +class plErrorMsg; +class plConvertSettings; +class hsBounds3Ext; +class plLightMapComponent; + +class plLightMapInfo +{ +public: + ObjLightDesc* fObjLiDesc; + INode* fLiNode; + int fResetShadowType; + float fResetMapRange; + float fMapRange; + hsBool fNewRender; +}; + +class plLightMapGen +{ +protected: + Interface* fInterface; + TimeValue fTime; + + int fUVWSrc; + float fScale; + + float fMapRange; + + int fWidth; + int fHeight; + + bool fRecalcLightMaps; + + Renderer* fRenderer; +#ifdef MF_NEW_RGC + RenderGlobalContext* fRGC; +#else // MF_NEW_RGC + plRenderGlobalContext* fRGC; +#endif // MF_NEW_RGC + RendParams* fRP; + + hsTArray fAllLights; + hsTArray fActiveLights; + + mutable hsTArray fCreatedLayers; + mutable hsTArray fPreppedMipmaps; + + mutable hsTArray fNewMaps; // Mipmaps created this session (not loaded from disk) + + std::vector fSharedComponents; // HACK so we can get rid of key refs before deleting bitmaps + + hsBounds3Ext IGetBoundsLightSpace(INode* node, INode* liNode); + hsBool IDirAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node); + hsBool ISpotAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node); + hsBool IOmniAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node); + hsBool ILightAffectsNode(plLightMapInfo* liInfo, LightObject* liObj, INode* node); + + hsBool IPrepLight(plLightMapInfo* liInfo, INode* node); + hsBool IGetLight(INode* node); + hsBool IFindLightsRecur(INode* node); + hsBool IFindActiveLights(plMaxNode* node); + hsBool IReleaseActiveLights(); + hsBool IReleaseAllLights(); + + int IPowerOfTwo(int sz) const; + hsBool ISelectBitmapDimension(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, hsTArray &spans); + hsBool ICompressLightMaps(); + + hsBool IsFresh(plBitmap* map) const; + + hsBool IAddToLightMap(plLayerInterface* lay, plMipmap* src) const; + plMipmap* IMakeAccumBitmap(plLayerInterface* lay) const; + void IInitBitmapColor(plMipmap* bitmap, const hsColorRGBA& col) const; + plLayerInterface* IGetLightMapLayer(plMaxNode* node, plGeometrySpan& span); + plLayerInterface* IMakeLightMapLayer(plMaxNode* node, plGeometrySpan& span); + int IGetUVWSrc() const { return fUVWSrc; } + + UInt32 IShadePoint(plMaxLightContext& ctx, const Color& amb, const hsPoint3& p, const hsVector3& n); + hsBool IShadeVerts(plMaxLightContext& ctx, const Color& amb, const hsPoint3 pt[3], const hsVector3 norm[3], const hsPoint3 uv[3], plMipmap* bitmap); + hsBool IShadeFace(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, plGeometrySpan& span, int iFace, plMipmap* bitmap); + hsBool IShadeSpan(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, plGeometrySpan& spans); + hsBool IShadeGeometrySpans(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, hsTArray &spans); + + hsBool IWantsMaps(plMaxNode* node); + hsBool IValidateUVWSrc(hsTArray &spans) const; + +public: + plLightMapGen(); + virtual ~plLightMapGen(); + +#ifdef MF_NEW_RGC + void SetRGC(RenderGlobalContext* rgc); // Don't call this ever ever ever +#endif // MF_NEW_RGC + + hsBool Open(Interface* ip, TimeValue t, bool forceRegen=true); + hsBool InitNode(INode* node, hsBool softShadow=true); // unnecessary when using MakeMaps() + hsBool Update(TimeValue t); + + void SetUVWSrc(int i) { fUVWSrc = i; } + int GetUVWSrc() const { return fUVWSrc; } + + void SetScale(float f) { fScale = f; } + float GetScale() const { return fScale; } + + // Calls to either the global or single must be wrapped in + // a call to Open and a call to close. That is, you must first + // call Open(), then you can make maps all day, but at the end + // of the day, you need to call Close(). Also, if the scene + // lighting changes, you need to call Close() and then Open() again. + // With the possibility of lights getting deleted from the scene, + // you're best off calling Open(), making as many maps as you want + // for now, call Close(), and if you decide later you want more, + // re-open. There's no protection in here from a user deleting + // a light (or any other node) while the shader is Open. For your + // own safety and the safety of your fellow passengers, don't + // return control to the user until the system is Closed. + hsBool MakeMaps(plMaxNode* node, const hsMatrix44& l2w, const hsMatrix44& w2l, hsTArray& spans, plErrorMsg *pErrMsg, plConvertSettings *settings); + + Color ShadowPoint(plMaxLightContext& ctx); + Color ShadePoint(plMaxLightContext& ctx); // ctx already contains pos & norm + Color ShadePoint(plMaxLightContext& ctx, const Point3& p, const Point3& n); + Color ShadePoint(plMaxLightContext& ctx, const hsPoint3& p, const hsVector3& n); + + hsBool DeInitNode(); + hsBool Close(); + + static plLightMapGen& Instance(); +}; + +#endif // plLightMapGen_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMaxLightContext.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMaxLightContext.h new file mode 100644 index 00000000..bff68587 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMaxLightContext.h @@ -0,0 +1,124 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMaxLightContext_inc +#define plMaxLightContext_inc + +#include "hsGeometry3.h" + +class plMaxLightContext : public ShadeContext +{ +protected: + TimeValue fCurrentTime; + + Box3 fBounds; + + Point3 fPos; + Point3 fNorm; + Point3 fPertNorm; + +public: + plMaxLightContext(const Box3& b, TimeValue t=0) + { + fBounds = b; + fCurrentTime = t; + + doMaps = true; + filterMaps = true; + backFace = false; + xshadeID = 0; + mtlNum = 0; + } + + void SetPoint(const Point3& p, const Point3& n) { fPos = p; fNorm = fPertNorm = n; } // Must be world space coming in. + void SetPoint(const hsPoint3& p, const hsVector3& n) { fPos = Point3(p.fX,p.fY,p.fZ); fNorm = fPertNorm = Point3(n.fX,n.fY,n.fZ); } // Must be world space coming in. + + virtual BOOL InMtlEditor() { return false; } // is this rendering the mtl editor sample sphere? + + virtual LightDesc* Light(int n) { return nil; } // get the nth light. + + virtual TimeValue CurTime() { return fCurrentTime; } // current time value + + virtual int FaceNumber() { return 0; } + virtual Point3 Normal() { return fPertNorm; } // interpolated surface normal, in cameara coords: affected by SetNormal() + virtual void SetNormal(Point3 p) { fPertNorm = p; } // used for perturbing normal + virtual Point3 OrigNormal() { return fNorm; } // original surface normal: not affected by SetNormal(); + virtual Point3 GNormal() { return fNorm; } // geometric (face) normal + + virtual Point3 V() { return Point3(0.f,1.f,0.f); } // Unit view vector: from camera towards P + virtual void SetView(Point3 p) { } // Set the view vector + + virtual Point3 ReflectVector() { return V(); } // reflection vector + virtual Point3 RefractVector(float ior) { return V(); } // refraction vector + + virtual Point3 CamPos() { return Point3(0,0,0); } // camera position + + virtual Point3 P() { return fPos; } // point to be shaded; + virtual Point3 DP() { return Point3(0,0,0); } // deriv of P, relative to pixel, for AA + + virtual Point3 PObj() { return P(); } // point in obj coords + virtual Point3 DPObj() { return DP(); } // deriv of PObj, rel to pixel, for AA + virtual Box3 ObjectBox() { return fBounds; } // Object extents box in obj coords + virtual Point3 PObjRelBox(); // Point rel to obj box [-1 .. +1 ] + virtual Point3 DPObjRelBox() { return Point3(0,0,0); } // deriv of Point rel to obj box [-1 .. +1 ] + virtual void ScreenUV(Point2& uv, Point2 &duv) {uv.Set(0,0); duv.Set(0,0); } // screen relative uv (from lower left) + virtual IPoint2 ScreenCoord() { return IPoint2(0,0); } // integer screen coordinate (from upper left) + + virtual Point3 UVW(int channel=0) { return Point3(0,0,0); } // return UVW coords for point + virtual Point3 DUVW(int channel=0) { return Point3(0,0,0); } // return UVW derivs for point + virtual void DPdUVW(Point3 dP[3],int channel=0) { dP[0] = dP[1] = dP[2] = Point3(0,0,0); } // Bump vectors for UVW (camera space) + + virtual void GetBGColor(Color &bgcol, Color& transp, BOOL fogBG=TRUE) { bgcol.Black(); transp.Black(); } // returns Background color, bg transparency + + virtual Point3 PointTo(const Point3& p, RefFrame ito) { return p; } + virtual Point3 PointFrom(const Point3& p, RefFrame ifrom) { return p; } + virtual Point3 VectorTo(const Point3& p, RefFrame ito) { return p; } + virtual Point3 VectorFrom(const Point3& p, RefFrame ifrom) { return p; } +}; + +inline Point3 plMaxLightContext::PObjRelBox(void) +{ + Point3 q; + Point3 p = PObj(); + Box3 b = ObjectBox(); + q.x = 2.0f*(p.x-b.pmin.x)/(b.pmax.x-b.pmin.x) - 1.0f; + q.y = 2.0f*(p.y-b.pmin.y)/(b.pmax.y-b.pmin.y) - 1.0f; + q.z = 2.0f*(p.z-b.pmin.z)/(b.pmax.z-b.pmin.z) - 1.0f; + return q; +} + +class plMaxRendContext : public RendContext +{ +public: + Matrix3 WorldToCam() const { return Matrix3(1); } + Color GlobalLightLevel() const { return Color(1.f, 1.f, 1.f); } + int Progress(int done, int total) { + return 1; + } +}; + + +#endif // plMaxLightContext_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMeshConverter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMeshConverter.cpp new file mode 100644 index 00000000..90f20020 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMeshConverter.cpp @@ -0,0 +1,2543 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plMeshConverter Class Functions // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// Created 4.18.2001 mcn // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "Max.h" +#include "iparamb2.h" +#include "modstack.h" +#include "ISkin.h" +#include "meshdlib.h" + + +#include "HeadSpin.h" +#include "../CoreLib/hsBitVector.h" +#include "plMeshConverter.h" +#include "hsResMgr.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxExport/plErrorMsg.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerInterface.h" +#include "../plDrawable/plGeometrySpan.h" +#include "hsConverterUtils.h" +#include "hsMaterialConverter.h" +#include "hsControlConverter.h" +#include "hsExceptionStack.h" +#include "../MaxPlasmaMtls/Materials/plCompositeMtl.h" +#include "../MaxPlasmaMtls/Materials/plPassMtl.h" +#include "../MaxPlasmaMtls/Materials/plCompositeMtlPB.h" +#include "../MaxPlasmaMtls/Materials/plPassMtlBasicPB.h" +#include "../plPipeline/plGBufferGroup.h" +#include "../plParticleSystem/plConvexVolume.h" +#include "../plDrawable/plGeoSpanDice.h" + +#include "../plDrawable/plAccessGeometry.h" +#include "../plDrawable/plAccessSpan.h" +#include "../plDrawable/plAccessTriSpan.h" +#include "../plDrawable/plAccessVtxSpan.h" + +#include "../plStatusLog/plStatusLog.h" + +#include "plTweak.h" + +//// Static Members ////////////////////////////////////////////////////////// + +hsBool plMeshConverter::fWarnBadNormals = true; +char plMeshConverter::fWarnBadNormalsMsg[] = "Bad normal autogeneration - please deliver Max file to QA"; + +hsBool plMeshConverter::fWarnBadUVs = true; +char plMeshConverter::fWarnBadUVsMsg[] = "The object \"%s\" does not have enough valid UVW mapping channels \ +for the material(s) applied to it. This might produce unwanted rendering artifacts at runtime"; + +hsBool plMeshConverter::fWarnSuspiciousUVs = true; +char plMeshConverter::fWarnSuspiciousUVsMsg[] = "The object \"%s\" has suspicious UVW coordinates on it. \ +You should apply an Unwrap UVW modifier to it."; + +char plMeshConverter::fTooManyVertsMsg[] = "The mesh \"%s\" has too many vertices to fit into a single buffer. \ +Please break up the mesh into pieces with no more than %u vertices each\ +or apply optimize terrain."; + +char plMeshConverter::fTooManyFacesMsg[] = "The mesh \"%s\" has too many faces to fit into a single buffer. \ +Please break up the mesh into pieces with no more than %u faces each\ +or apply optimize terrain."; + + +//// Local Helper Class Definitions ////////////////////////////////////////// + +class TempWeightInfo +{ + public: + float fWeights[ 4 ]; + UInt32 fIndices; +}; + + +class plMAXVertexAccNode +{ + public: + hsPoint3 fPoint; // Inefficient space-wise, I know, but it makes this a lot simpler... + hsVector3 fNormal; + hsColorRGBA fColor, fIllum; + UInt32 fIndex; + hsPoint3 fUVs[ plGeometrySpan::kMaxNumUVChannels ]; + UInt32 fNumChannels; + + plMAXVertexAccNode *fNext; + + plMAXVertexAccNode( const hsPoint3 *point, const hsVector3 *normal, const hsColorRGBA& color, const hsColorRGBA& illum, int numChannels, const hsPoint3 *uvs, UInt32 index ); + + hsBool IsEqual( const hsVector3 *normal, const hsColorRGBA& color, const hsColorRGBA& illum, const hsPoint3 *uvs ); +}; + +typedef plMAXVertexAccNode *plMAXVertexAccNodePtr; + +class plMAXVertexAccumulator +{ + protected: + + int fNumPoints, fNumChannels, fNumVertices; + plMAXVertexAccNode **fPointList; + + hsTArray fIndices; + hsTArray fInverseVertTable; + + void IFindSkinWeights( ISkinContextData *skinData, int vertex, float *weights, UInt32 *indices ); + void IFindUserSkinWeights( plMaxNode* node, Mesh* mesh, int vertex, float *weights, UInt32 *indices ); + void IFindAllUserSkinWeights( plMaxNode* node, Mesh* mesh, TempWeightInfo weights[]); + public: + + plMAXVertexAccumulator( int numOrigPoints, int numChannels ); + ~plMAXVertexAccumulator(); + + void AddVertex( int index, hsPoint3 *point, hsVector3 *normal, const hsColorRGBA& color, const hsColorRGBA& illum, hsPoint3 uvs[ plGeometrySpan::kMaxNumUVChannels ] ); + + void StuffMyData( plMaxNode* node, plGeometrySpan *span, Mesh* mesh, ISkinContextData* skinData ); + + int GetVertexCount(); + UInt32 GetIndexCount( void ) { return fIndices.GetCount(); } +}; + +class plMAXVertNormal +{ + public: + Point3 fNormal; + DWORD fSmGroup; + bool fInited; + + plMAXVertNormal *fNext; + + plMAXVertNormal() { fSmGroup = 0; fNext = nil; fInited = false; fNormal = Point3( 0, 0, 0 ); } + plMAXVertNormal( Point3 &n, DWORD s ) { fNext = nil; fInited = true; fNormal = n; fSmGroup = s; } + ~plMAXVertNormal() { /*delete fNext; */} + void DestroyChain( void ) { if( fNext != nil ) fNext->DestroyChain(); delete fNext; fNext = nil; } + + // Adding normalization of input n. Input is usually just crossproduct of face edges. Non-normalized, + // large faces will overwhelm small faces on summation, which is the opposite of what we want, since + // curvature is most accurately captured in the small faces. mf. + void AddNormal( Point3 &n, DWORD s ) + { + if( !( s & fSmGroup ) && fInited ) + { + if( fNext ) + fNext->AddNormal( n, s ); + else + fNext = TRACKED_NEW plMAXVertNormal( ::Normalize(n), s ); + } + else + { + fNormal += ::Normalize(n); + fSmGroup |= s; + fInited = true; + } + } + + Point3 &GetNormal( DWORD s ) + { + if( ( fSmGroup & s ) || ( fNext == nil ) ) + return fNormal; + else + return fNext->GetNormal( s ); + } + + /// Relies on static variable, values will be destroyed between calls + hsVector3 &GetPlNormal( DWORD s ) + { + Point3 n = GetNormal( s ); + static hsVector3 pt; + pt.Set( n.x, n.y, n.z ); + return pt; + } + + void Normalize( void ) + { + plMAXVertNormal *ptr = fNext, *prev = this; + + + while( ptr != nil ) + { + if( ptr->fSmGroup & fSmGroup ) + { + fNormal += ptr->fNormal; + prev->fNext = ptr->fNext; + delete ptr; + ptr = prev->fNext; + } + else + { + prev = ptr; + ptr = ptr->fNext; + } + } + + fNormal = ::Normalize( fNormal ); + if( fNext ) + fNext->Normalize(); + } +}; + +//// Instance and Constructor/Destructor ///////////////////////////////////// + +plMeshConverter& plMeshConverter::Instance() +{ + static plMeshConverter the_instance; + return the_instance; +} + +plMeshConverter::plMeshConverter() : + fInterface(nil), + fConverterUtils(hsConverterUtils::Instance()), + fIsInitialized(false) +{ + hsGuardBegin("plMeshConverter::plMeshConverter"); + hsGuardEnd; +} + +plMeshConverter::~plMeshConverter() +{ + hsGuardBegin("plMeshConverter::~plMeshConverter"); + hsGuardEnd; +} + +//// Init and DeInit ///////////////////////////////////////////////////////// + +void plMeshConverter::Init( hsBool save, plErrorMsg *msg ) +{ + hsGuardBegin( "plMeshConverter::Init" ); + + if( fIsInitialized ) + DeInit( false ); + + fIsInitialized = true; + fInterface = GetCOREInterface(); + fErrorMsg = msg; + fWarnBadUVs = true; + fWarnSuspiciousUVs = true; + fWarnBadNormals = true; + + hsGuardEnd; +} + +void plMeshConverter::DeInit( hsBool deInitLongRecur ) +{ + hsGuardBegin( "plMeshConverter::DeInit" ); + + fIsInitialized = false; + + hsGuardEnd; +} + +void plMeshConverter::StuffPositionsAndNormals(plMaxNode *node, hsTArray *pos, hsTArray *normals) +{ + hsGuardBegin( "plMeshConverter::BuildNormalsArray" ); + + const char* dbgNodeName = node->GetName(); + Mesh *mesh; + Int32 numVerts; + hsMatrix44 l2wMatrix, vert2LMatrix, vertInvTransMatrix, tempMatrix; + + /// Get da mesh + mesh = IGetNodeMesh( node ); + if( mesh == nil ) + return ; + + numVerts = mesh->getNumVerts(); + + /// Get transforms + l2wMatrix = node->GetLocalToWorld44(); + vert2LMatrix = node->GetVertToLocal44(); // vert2LMatrix is the transform we apply + // now to the verts, to get into *our* object-local-space + vert2LMatrix.GetInverse( &tempMatrix ); + tempMatrix.GetTranspose( &vertInvTransMatrix ); // Inverse-transpose of the vert2Local matrix, + // for xforming the normals + mesh->buildNormals(); + + normals->SetCount(numVerts); + pos->SetCount(numVerts); + int i; + for (i = 0; i < numVerts; i++) + { + // positions + hsPoint3 currPos; + currPos.Set(mesh->verts[i].x, mesh->verts[i].y, mesh->verts[i].z); + pos->Set(i, vert2LMatrix * currPos); + + // normals + RVertex &rv = mesh->getRVert(i); + Point3& norm = rv.rn.getNormal(); + hsVector3 currNorm(norm.x, norm.y, norm.z); + currNorm.Normalize(); + normals->Set(i, vertInvTransMatrix * currNorm); + } + + IDeleteTempGeometry(); + hsGuardEnd; +} + +plConvexVolume *plMeshConverter::CreateConvexVolume(plMaxNode *node) +{ + hsGuardBegin( "plMeshConverter::CreateConvexVolume" ); + + const char* dbgNodeName = node->GetName(); + Mesh *mesh; + Int32 numFaces, i, j, numVerts; + hsMatrix44 l2wMatrix, vert2LMatrix, vertInvTransMatrix, tempMatrix; + hsBool flipOrder, checkForOverflow = false; + + /// Get da mesh + mesh = IGetNodeMesh( node ); + if( mesh == nil ) + return nil; + + numFaces = mesh->getNumFaces(); + numVerts = mesh->getNumVerts(); + + plConvexVolume *bounds = TRACKED_NEW plConvexVolume(); + + /// Get transforms + l2wMatrix = node->GetLocalToWorld44(); + vert2LMatrix = node->GetVertToLocal44(); + flipOrder = vert2LMatrix.GetParity(); // vert2LMatrix is the transform we apply + // now to the verts, to get into *our* object-local-space + vert2LMatrix.GetInverse( &tempMatrix ); + tempMatrix.GetTranspose( &vertInvTransMatrix ); // Inverse-transpose of the vert2Local matrix, + // for xforming the normals + + + //mesh->buildNormals(); + + for( i = 0; i < numFaces; i++ ) + { + Face *maxFace = &mesh->faces[ i ]; + TVFace *maxColorFace = ( mesh->vcFace != nil ) ? &mesh->vcFace[ i ] : nil; + hsPoint3 pos[ 3 ]; + hsPoint3 testPt1, testPt2, testPt3; + hsVector3 normal; + UInt32 vertIdx[ 3 ]; + + /// Add the 3 vertices to the correct vertex accumulator object + + // Get positions + if( flipOrder ) + { + for( j = 0; j < 3; j++ ) + { + vertIdx[ j ] = maxFace->getVert( 2 - j ); + pos[ j ].fX = mesh->verts[ vertIdx[ j ] ].x; + pos[ j ].fY = mesh->verts[ vertIdx[ j ] ].y; + pos[ j ].fZ = mesh->verts[ vertIdx[ j ] ].z; + } + } + else + { + for( j = 0; j < 3; j++ ) + { + vertIdx[ j ] = maxFace->getVert( j ); + pos[ j ].fX = mesh->verts[ vertIdx[ j ] ].x; + pos[ j ].fY = mesh->verts[ vertIdx[ j ] ].y; + pos[ j ].fZ = mesh->verts[ vertIdx[ j ] ].z; + } + } + + // Look for degenerate triangles (why MAX even gives us these I have no clue...) + testPt1 = pos[ 1 ] - pos[ 0 ]; + testPt2 = pos[ 2 ] - pos[ 0 ]; + testPt3 = pos[ 2 ] - pos[ 1 ]; + if( ( testPt1.fX == 0.0f && testPt1.fY == 0.0f && testPt1.fZ == 0.0f ) || + ( testPt2.fX == 0.0f && testPt2.fY == 0.0f && testPt2.fZ == 0.0f ) || + ( testPt3.fX == 0.0f && testPt3.fY == 0.0f && testPt3.fZ == 0.0f ) ) + { + continue; + } + + // Translate to local space + for( j = 0; j < 3; j++ ) + pos[ j ] = vert2LMatrix * pos[ j ]; + + // Calculate normal for face + hsVector3 v1, v2; + v1.Set( &pos[ 1 ], &pos[ 0 ] ); + v2.Set( &pos[ 2 ], &pos[ 0 ] ); + normal = (v1 % v2); + normal.Normalize(); + + hsPlane3 plane(&normal, normal.InnerProduct(pos[0])); + bounds->AddPlane(plane); // auto-checks for redundant planes. + } + + IDeleteTempGeometry(); + + return bounds; + hsGuardEnd; +} + +// +// Sometimes objects can have faces without UV coordinates. These faces will +// export random UV values each time we export, changing the data and forcing +// patches when they aren't necessary. To detect these, we put a Unwrap UVW mod +// on the object and see if it changes the UV values. +// +bool plMeshConverter::IValidateUVs(plMaxNode* node) +{ + if (node->GetObjectRef()->SuperClassID() != GEN_DERIVOB_CLASS_ID) + return true; + + Mesh* mesh = IGetNodeMesh(node); + if (!mesh) + return true; + + if (mesh->getNumMaps() < 2) + return true; + + // Cache the original UV verts + int numVerts = mesh->getNumMapVerts(1); + int vertBufSize = sizeof(UVVert)*numVerts; + UVVert* origVerts = TRACKED_NEW UVVert[vertBufSize]; + memcpy(origVerts, mesh->mapVerts(1), vertBufSize); + + IDeleteTempGeometry(); + + // Add an Unwrap UVW mod onto the stack + IDerivedObject* derivedObject = (IDerivedObject*)node->GetObjectRef(); + #define UNWRAP_UVW_CID Class_ID(0x02df2e3a, 0x72ba4e1f) + Modifier* mod = (Modifier*)GetCOREInterface()->CreateInstance(OSM_CLASS_ID, UNWRAP_UVW_CID); + derivedObject->AddModifier(mod); + + mesh = IGetNodeMesh(node); + + bool uvsAreBad = false; + + UVVert* newVerts = mesh->mapVerts(1); + for (int i = 0; i < numVerts; i++) + { + UVVert uvDiff = newVerts[i] - origVerts[i]; + float diff = uvDiff.Length(); + if (diff > 0.01) + { + uvsAreBad = true; + break; + } + } + + delete [] origVerts; + IDeleteTempGeometry(); + + derivedObject->DeleteModifier(); + + if (uvsAreBad) + { + TSTR logfile = "UV_"; + logfile += GetCOREInterface()->GetCurFileName(); + logfile += ".log"; + plStatusLog::AddLineS(logfile, "%s has suspicious UVs", node->GetName()); + + + if (fWarnSuspiciousUVs) + { + /// We're missing some UV channels on our object. We'll handle it later; warn the user here + if (fErrorMsg->Set(true, "UVW Warning", fWarnSuspiciousUVsMsg, node->GetName()).CheckAskOrCancel()) + fWarnSuspiciousUVs = false; + fErrorMsg->Set(false); + } + } + + return uvsAreBad; +} + +//// CreateSpans ///////////////////////////////////////////////////////////// +// Main function. Takes a maxNode's object and creates geometrySpans from it +// suitable for drawing as ice. + +hsBool plMeshConverter::CreateSpans( plMaxNode *node, hsTArray &spanArray, bool doPreshading ) +{ + hsGuardBegin( "plMeshConverter::CreateSpans" ); + + const char* dbgNodeName = node->GetName(); + Mesh *mesh; + Int32 numFaces, i, j, k, numVerts, maxNumBones, maxUVWSrc; + Int32 numMaterials = 1, numSubMaterials = 1; + hsMatrix44 l2wMatrix, vert2LMatrix, vertInvTransMatrix, tempMatrix; + Mtl *maxMaterial = nil; + hsBool isComposite, isMultiMat, flipOrder, checkForOverflow = false, includesComp; + UInt8 ourFormat, numChannels, maxBlendChannels; + hsColorRGBA *colorArray = nil; + hsColorRGBA *illumArray = nil; + UInt32 sharedSpanProps = 0; + hsBitVector usedSubMtls; + hsBool makeAlphaLayer = node->VtxAlphaNotAvailable(); + + ISkinContextData *skinData; + + hsTArray *> ourMaterials; + hsTArray *> ourAccumulators; + + hsTArray vertNormalCache; + hsTArray* vertDPosDuCache = nil; + hsTArray* vertDPosDvCache = nil; + + //// Setup /////////////////////////////////////////////////////////////// + plLocation nodeLoc = node->GetLocation(); + + TimeValue timeVal = fConverterUtils.GetTime(fInterface); + Class_ID cid = node->EvalWorldState(timeVal).obj->ClassID(); + if( node->EvalWorldState(timeVal).obj->ClassID() == BONE_OBJ_CLASSID ) + return false; + + IValidateUVs(node); + + /// Get da mesh + mesh = IGetNodeMesh( node ); + if( mesh == nil ) + return false; + numFaces = mesh->getNumFaces(); + numVerts = mesh->getNumVerts(); + + /// Get the material + maxMaterial = hsMaterialConverter::Instance().GetBaseMtl( node ); + isMultiMat = hsMaterialConverter::Instance().IsMultiMat( maxMaterial ); + + const hsBool smoothAll = node->GetSmoothAll(); + + includesComp = false; + if (isMultiMat) + { + for (i = 0; i < numFaces; i++) + { + int index = mesh->faces[i].getMatID(); + if (index >= maxMaterial->NumSubMtls()) + index = 0; + + usedSubMtls.SetBit(index); + if (hsMaterialConverter::Instance().IsCompositeMat(maxMaterial->GetSubMtl(index))) + includesComp = true; + } + } + else + includesComp = hsMaterialConverter::Instance().IsCompositeMat(maxMaterial); + + try + { + + /// Check vert count + if( numVerts >= plGBufferGroup::kMaxNumVertsPerBuffer || numFaces * 3 >= plGBufferGroup::kMaxNumIndicesPerBuffer ) + { + /// Possible overflow, but not sure. Only check for overflow if this is set + checkForOverflow = true; + } + + /// Get transforms + l2wMatrix = node->GetLocalToWorld44(); + vert2LMatrix = node->GetVertToLocal44(); + flipOrder = vert2LMatrix.GetParity(); // vert2LMatrix is the transform we apply + // now to the verts, to get into *our* object-local-space + vert2LMatrix.GetInverse( &tempMatrix ); + tempMatrix.GetTranspose( &vertInvTransMatrix ); // Inverse-transpose of the vert2Local matrix, + // for xforming the normals + + // OTM used in generating normals. + Matrix3 otm = node->GetOTM(); + Matrix3 invOtm = Inverse(otm); + invOtm.SetTrans(Point3(0,0,0)); + invOtm.ValidateFlags(); + + // If we use a composite on this object, we don't want to export the illumination channel. + UVVert *illumMap = mesh->mapVerts(MAP_SHADING); + int numIllumVerts = mesh->getNumMapVerts(MAP_SHADING); + + UVVert *alphaMap = mesh->mapVerts(MAP_ALPHA); + int numAlphaVerts = mesh->getNumMapVerts(MAP_ALPHA); + + if( node->GetRunTimeLight() ) + { + sharedSpanProps |= plGeometrySpan::kPropRunTimeLight; + } + if( node->GetNoPreShade() ) + { + sharedSpanProps |= plGeometrySpan::kPropNoPreShade; + } + hsScalar waterHeight = 0; + if( node->GetHasWaterHeight() ) + { + sharedSpanProps |= plGeometrySpan::kWaterHeight; + waterHeight = node->GetWaterHeight(); + } + /// Which lighting equation? + if( node->NonVtxPreshaded() ) + { + /// OK, we can go with kLiteVtxNonPreshaded, so we get vertex alpha. Yipee!!! + sharedSpanProps |= plGeometrySpan::kLiteVtxNonPreshaded; + } + + //// Vertex Colors / Illumination //////////////////////////////////////// + + /// If there are colors, pre-convert them + hsColorRGBA white, black; + white.Set(1.f, 1.f, 1.f, 1.f); + black.Set(0, 0, 0, 1.f); + hsBool allWhite = true, allBlack = true; + + if( mesh->numCVerts > 0) + { + if (mesh->vertCol != nil) + { + colorArray = TRACKED_NEW hsColorRGBA[ mesh->numCVerts ]; + for( i = 0; i < mesh->numCVerts; i++ ) + { + colorArray[i].Set(mesh->vertCol[ i ].x, mesh->vertCol[ i ].y, mesh->vertCol[ i ].z, 1.f); + if (colorArray[ i ] != black) + allBlack = false; + } + + // XXX Sometimes 3DS reports that all colors have been set black (when they haven't been touched). + // We set them white here, so that they don't affect the shader when multiplied in. + // (Sometimes it reports them as all white too, but hey, that's the value we'd use anyway...) + if (allBlack) + for( i = 0; i < mesh->numCVerts; i++ ) + colorArray[ i ] = white; + } + } + + if (illumMap != nil) + { + // MF_HORSE CARNAGE + illumArray = TRACKED_NEW hsColorRGBA[numIllumVerts]; + for( i = 0; i < numIllumVerts; i++ ) + { + illumArray[i].Set(illumMap[ i ].x, illumMap[ i ].y, illumMap[ i ].z, 1.f); + if (illumArray[ i ] != white) + allWhite = false; + } + + // XXX Same hack as with colorArray above, except illumination values are added in, so we set them black + // in order to not affect the shader. + if (allWhite) + for( i = 0; i < numIllumVerts; i++ ) + illumArray[ i ] = black; + // MF_HORSE CARNAGE + } + + //// Materials / Mapping Channels Setup ////////////////////////////////// + + numChannels = node->NumUVWChannels(); + + maxBlendChannels = 0; + + if( isMultiMat ) + { + numMaterials = maxMaterial->NumSubMtls(); + + ourMaterials.SetCountAndZero( numMaterials ); + for( i = 0; i < numMaterials; i++ ) + { + if (usedSubMtls.IsBitSet(i)) // Only export the sub materials actually used + ourMaterials[i] = hsMaterialConverter::Instance().CreateMaterialArray( maxMaterial->GetSubMtl(i), node, i); + else + ourMaterials[i] = nil; + } + } + else // plPassMtl, plDecalMat, plMultiPassMtl + { + numMaterials = 1; + + ourMaterials.Reset(); + ourMaterials.Append(hsMaterialConverter::Instance().CreateMaterialArray( maxMaterial, node, 0 )); + } + + /// UV check on the layers + for( i = 0, maxUVWSrc = -1; i < numMaterials; i++ ) + { + hsTArray *subMats = ourMaterials[i]; + if (subMats == nil) + continue; + for( j = 0; j < subMats->GetCount(); j++ ) + { + plExportMaterialData currData = subMats->Get(j); + if (currData.fMaterial == nil) + continue; + + for( k = 0; k < currData.fMaterial->GetNumLayers(); k++ ) + { + plLayerInterface *layer = currData.fMaterial->GetLayer( k ); + + int uvwSrc = layer->GetUVWSrc() & plLayerInterface::kUVWIdxMask; + + if( maxUVWSrc < uvwSrc && layer->GetTexture() != nil ) + maxUVWSrc = uvwSrc; + if( maxBlendChannels < currData.fNumBlendChannels) + maxBlendChannels = currData.fNumBlendChannels; + } + } + } + // If this node is a water decal set to environment map, then there's only 1 layer, but + // we'll need an extra 2 uvw channels for the tangent space basis vectors. + if( node->GetWaterDecEnv() ) + maxUVWSrc = 2; + + if( numChannels + maxBlendChannels < ( maxUVWSrc + 1 ) && fWarnBadUVs ) + { + /// We're missing some UV channels on our object. We'll handle it later; warn the user here + if( fErrorMsg->Set( true, "UVW Channel Warning", fWarnBadUVsMsg, node->GetName() ).CheckAskOrCancel() ) + fWarnBadUVs = false; + fErrorMsg->Set( false ); + } + else if( numChannels > ( maxUVWSrc + 1 ) ) + { + // Make sure we allocate enough for all the channel data, even if the materials don't use them (yet...) + // (trick is, make sure those extra channels are valid first) + for( i = maxUVWSrc + 1; i < numChannels; i++ ) + { + if( mesh->mapFaces( i + 1 ) == nil ) + { + numChannels = i; + break; + } + } + + maxUVWSrc = numChannels - 1; + } + + //maxUVWSrc += maxBlendChannels; + if (maxUVWSrc > plGeometrySpan::kMaxNumUVChannels - 1) maxUVWSrc = plGeometrySpan::kMaxNumUVChannels - 1; + + /// Our buffer format... +/* ourFormat = ( maxUVWSrc == -1 ) ? plGeometrySpan::kNoUVChannels : + ( maxUVWSrc == 0 ) ? plGeometrySpan::k1UVChannel : + ( maxUVWSrc == 1 ) ? plGeometrySpan::k2UVChannels : + ( maxUVWSrc == 2 ) ? plGeometrySpan::k3UVChannels : + plGeometrySpan::k4UVChannels; +*/ + ourFormat = plGeometrySpan::UVCountToFormat( maxUVWSrc + 1 ); + /// NOW allocate our accumulators, since maxUVWSrc was just calculated... + ourAccumulators.SetCount( numMaterials ); + for( i = 0; i < numMaterials; i++ ) + { + if (ourMaterials[i] == nil) + { + ourAccumulators[i] = nil; + continue; + } + + hsTArray *currAccum = TRACKED_NEW hsTArray; + int currNumSubMtls = ourMaterials[i]->GetCount(); + currAccum->Reset(); + ourAccumulators[i] = currAccum; + for (j = 0; j < currNumSubMtls; j++) + { + currAccum->Append(new plMAXVertexAccumulator( mesh->getNumVerts(), maxUVWSrc + 1 )); + } + } + + + //// Skinning //////////////////////////////////////////////////////////// + + /// Check for skinning + ISkin* skin = node->FindSkinModifier(); + if( skin ) + { + skinData = skin->GetContextInterface(node); + int skinNumPoints = skinData->GetNumPoints(); + if(skinNumPoints != numVerts) + { + fErrorMsg->Set(true, "Skinning Error", "Invalid point count on ISkin data on node %s", dbgNodeName ).Show(); + fErrorMsg->Set(); + throw (hsBool)false; + //hsAssert( skinData->GetNumPoints() == numVerts, "Invalid point count on ISkin data" ); + + } + + + /// Loop through the skin verts and find the max # of bones + for( i = 0, maxNumBones = 0; i < numVerts; i++ ) + { + if( skinData->GetNumAssignedBones( i ) > maxNumBones ) + maxNumBones = skinData->GetNumAssignedBones( i ); + } + maxNumBones++; + if( maxNumBones > 4 ) + maxNumBones = 4; + //hsAssert( maxNumBones >= 2, "Invalid skin (not enough bones)" ); + if( maxNumBones < 2) + { + fErrorMsg->Set(true, "Skinning Error", "Invalid skin (no bones) on node %s", dbgNodeName ).Show(); + fErrorMsg->Set(); + throw (hsBool)false; + } + + + if (node->GetBoneMap() && maxNumBones == 2) + maxNumBones++; + + /// Change format to match + ourFormat |= ( maxNumBones == 2 ) ? plGeometrySpan::kSkin1Weight : + ( maxNumBones == 3 ) ? plGeometrySpan::kSkin2Weights : plGeometrySpan::kSkin3Weights; + + if( skin->GetNumBones() > 1 || node->GetBoneMap()) + ourFormat |= plGeometrySpan::kSkinIndices; + } + else + { + skinData = nil; + + if( node->NumBones() ) + { + maxNumBones = 2; + ourFormat |= plGeometrySpan::kSkin1Weight; + } + } + + + //// Build Vertex Normal Cache /////////////////////////////////////////// + + vertNormalCache.SetCount( mesh->getNumVerts() ); + for( i = 0; i < mesh->getNumFaces(); i++ ) + { + Face *maxFace = &mesh->faces[ i ]; + Point3 v0, v1, v2, norm; + + UInt32 smGroup = smoothAll ? 1 : maxFace->getSmGroup(); + + v0 = mesh->verts[ maxFace->v[ 0 ] ]; + v1 = mesh->verts[ maxFace->v[ 1 ] ]; + v2 = mesh->verts[ maxFace->v[ 2 ] ]; + + norm = ( v1 - v0 ) ^ ( v2 - v1 ); + for( j = 0; j < 3; j++ ) + vertNormalCache[ maxFace->v[ j ] ].AddNormal( norm, maxFace->smGroup ); + } + for( i = 0; i < vertNormalCache.GetCount(); i++ ) + vertNormalCache[ i ].Normalize(); + + vertDPosDuCache = TRACKED_NEW hsTArray[numMaterials]; + vertDPosDvCache = TRACKED_NEW hsTArray[numMaterials]; + + hsTArray bumpLayIdx; + hsTArray bumpLayChan; + hsTArray bumpDuChan; + hsTArray bumpDvChan; + ISetBumpUvSrcs(ourMaterials, bumpLayIdx, bumpLayChan, bumpDuChan, bumpDvChan); + if( node->GetWaterDecEnv() ) + ISetWaterDecEnvUvSrcs(ourMaterials, bumpLayIdx, bumpLayChan, bumpDuChan, bumpDvChan); + + ISmoothUVGradients(node, mesh, ourMaterials, bumpLayIdx, bumpLayChan, vertDPosDuCache, vertDPosDvCache); + + //// Main Conversion Loop //////////////////////////////////////////////// + + // Loop through the faces and stuff them into spans + spanArray.Reset(); + + mesh->buildNormals(); + + for( i = 0; i < numFaces; i++ ) + { + Face *maxFace = &mesh->faces[ i ]; + TVFace *maxColorFace = ( mesh->vcFace != nil ) ? &mesh->vcFace[ i ] : nil; + hsPoint3 pos[ 3 ]; + hsPoint3 testPt1, testPt2, testPt3; + hsVector3 normals[ 3 ]; + hsColorRGBA colors[ 3 ], illums[ 3 ]; + UInt32 smGroup, vertIdx[ 3 ]; + hsPoint3 uvs1[ plGeometrySpan::kMaxNumUVChannels + 1]; + hsPoint3 uvs2[ plGeometrySpan::kMaxNumUVChannels + 1]; + hsPoint3 uvs3[ plGeometrySpan::kMaxNumUVChannels + 1]; + hsPoint3 temp; + Mtl *currMaxMtl; + + // The main index is how a multi-material keeps track of which sub material is involved. The sub material + // may actually create multiple materials (like composites), hence the second index, but it most cases it + // will be zero as well. + + int mainMatIndex = 0; + int subMatIndex = 0; + + // Get span index + if( isMultiMat ) + { + mainMatIndex = maxFace->getMatID(); + if( mainMatIndex >= numMaterials ) + mainMatIndex = 0; + currMaxMtl = maxMaterial->GetSubMtl(mainMatIndex); + } + else + currMaxMtl = maxMaterial; + + int numBlendChannels = 0; + hsTArray *subMtls = ourMaterials[mainMatIndex]; + hsAssert(subMtls != nil, "Face is assigned a material that we think is unused."); + + for (j = 0; j < subMtls->GetCount(); j++) + { + int currBlend = subMtls->Get(j).fNumBlendChannels; + if (numBlendChannels < currBlend) + numBlendChannels = currBlend; + } + isComposite = hsMaterialConverter::Instance().IsCompositeMat( currMaxMtl ); + + /// Add the 3 vertices to the correct vertex accumulator object + + // Get positions + if( flipOrder ) + { + for( j = 0; j < 3; j++ ) + { + vertIdx[ j ] = maxFace->getVert( 2 - j ); + pos[ j ].fX = mesh->verts[ vertIdx[ j ] ].x; + pos[ j ].fY = mesh->verts[ vertIdx[ j ] ].y; + pos[ j ].fZ = mesh->verts[ vertIdx[ j ] ].z; + } + } + else + { + for( j = 0; j < 3; j++ ) + { + vertIdx[ j ] = maxFace->getVert( j ); + pos[ j ].fX = mesh->verts[ vertIdx[ j ] ].x; + pos[ j ].fY = mesh->verts[ vertIdx[ j ] ].y; + pos[ j ].fZ = mesh->verts[ vertIdx[ j ] ].z; + } + } + + // Look for degenerate triangles (why MAX even gives us these I have no clue...) + testPt1 = pos[ 1 ] - pos[ 0 ]; + testPt2 = pos[ 2 ] - pos[ 0 ]; + testPt3 = pos[ 2 ] - pos[ 1 ]; + if( ( testPt1.fX == 0.0f && testPt1.fY == 0.0f && testPt1.fZ == 0.0f ) || + ( testPt2.fX == 0.0f && testPt2.fY == 0.0f && testPt2.fZ == 0.0f ) || + ( testPt3.fX == 0.0f && testPt3.fY == 0.0f && testPt3.fZ == 0.0f ) ) + { + continue; + } + + // If we're expanding the UVW channel list, fill out the rest with zeros + for( j = numChannels; j < maxUVWSrc; j++ ) + { + uvs1[ j ].Set( 0, 0, 0 ); + uvs2[ j ].Set( 0, 0, 0 ); + uvs3[ j ].Set( 0, 0, 0 ); + } + + // Now for each vertex, get the UVs, calc color, and add + if( numChannels > 0 ) + { + // Just go ahead and always generate the opacity into the uvs, because we're + // going to look for it there on composites whether they actually use the + // alpha hack texture or not. + IGenerateUVs( node, currMaxMtl, mesh, i, numChannels, + 1, uvs1, uvs2, uvs3 ); + if( flipOrder ) + { + for( j = 0; j < 3; j++ ) + { + temp = uvs1[ j ]; + uvs1[ j ] = uvs3[ j ]; + uvs3[ j ] = temp; + } + } + } + + // Handle colors + if( maxColorFace == nil ) + { + colors[2] = colors[1] = colors[0] = white; + } + else + { + colors[ 0 ] = colorArray[ maxColorFace->t[ flipOrder ? 2 : 0 ] ]; + colors[ 1 ] = colorArray[ maxColorFace->t[ flipOrder ? 1 : 1 ] ]; + colors[ 2 ] = colorArray[ maxColorFace->t[ flipOrder ? 0 : 2 ] ]; + } + + // Don't want to write illum values to the vertex for composite materials + if (illumArray == nil || includesComp) + { + illums[ 0 ] = illums[ 1 ] = illums[ 2 ] = black; + } + else + { + // MF_HORSE CARNAGE + TVFace* tvFace = &mesh->mapFaces(MAP_SHADING)[i]; + for( j = 0; j < 3; j++ ) + illums[j] = illumArray[ tvFace->getTVert(flipOrder ? 2 - j : j) ]; + // MF_HORSE CARNAGE + } + + if (alphaMap != nil) // if it IS nil, then alpha values are all at the default 1.0 + { + // MF_HORSE CARNAGE + TVFace* tvFace = &mesh->mapFaces(MAP_ALPHA)[i]; + for (j = 0; j < 3; j++) + colors[j].a = alphaMap[ tvFace->getTVert(flipOrder ? 2 - j : j) ].x; + // MF_HORSE CARNAGE + } + + if (isComposite && !makeAlphaLayer) + { + int index = ((plCompositeMtl *)currMaxMtl)->CanWriteAlpha(); + int j; + TVFace* tvFaces = mesh->mapFaces(MAP_SHADING); + for (j = 0; j < 3; j++) + { + switch(index) + { + case plCompositeMtl::kCompBlendVertexAlpha: + break; + case plCompositeMtl::kCompBlendVertexIllumRed: + colors[j].a = (tvFaces != nil ? illumMap[tvFaces[i].getTVert(flipOrder ? 2 - j : j)].x : 1.0f); + break; + case plCompositeMtl::kCompBlendVertexIllumGreen: + colors[j].a = (tvFaces != nil ? illumMap[tvFaces[i].getTVert(flipOrder ? 2 - j : j)].y : 1.0f); + break; + case plCompositeMtl::kCompBlendVertexIllumBlue: + colors[j].a = (tvFaces != nil ? illumMap[tvFaces[i].getTVert(flipOrder ? 2 - j : j)].z : 1.0f); + break; + default: // Different channels, thus we flush the alpha to 100 and do alpha through a 2nd layer. + colors[j].a = 1.0f; + break; + } + } + } + // Calculate normal for face + if( node->HasNormalChan() ) + { + // Someone has stuffed a requested normal into a map channel. + // Ignore common sense and use it as is. + int normChan = node->GetNormalChan(); + TVFace* mapFaces = mesh->mapFaces(normChan); + if( mapFaces ) + { + TVFace* normFace = mapFaces + i; + int ii; + for( ii = 0; ii < 3; ii++ ) + { + Point3 norm = mesh->mapVerts(normChan)[normFace->getTVert(ii)]; + normals[ii].Set(norm.x, norm.y, norm.z); + } + } + else + { + if( fErrorMsg->Set(fWarnBadNormals, node->GetName(), fWarnBadNormalsMsg).CheckAskOrCancel() ) + fWarnBadNormals = false; + fErrorMsg->Set( false ); + normals[0].Set(0,0,1.f); + normals[1] = normals[2] = normals[0]; + } + + } + else if( node->GetRadiateNorms() ) + { + int ii; + for( ii = 0; ii < 3; ii++ ) + { + Point3 pos = mesh->getVert(vertIdx[ii]) * otm; + pos = pos * invOtm; + + normals[ii].Set(pos.x, pos.y, pos.z); + } + } + else + { + smGroup = smoothAll ? 1 : maxFace->getSmGroup(); + if( smGroup == 0 ) + { + hsVector3 v1, v2; + v1.Set( &pos[ 1 ], &pos[ 0 ] ); + v2.Set( &pos[ 2 ], &pos[ 1 ] ); // Hey, MAX does it...see normalCache building above + // Note: if flipOrder is set, we have to reverse the order of the cross product, since + // we already flipped the order of the points, to match what MAX would get + normals[ 0 ] = normals[ 1 ] = normals[ 2 ] = flipOrder ? ( v2 % v1 ) : ( v1 % v2 ); + } + else + { + normals[ 0 ] = vertNormalCache[ vertIdx[ 0 ] ].GetPlNormal( smGroup ); + normals[ 1 ] = vertNormalCache[ vertIdx[ 1 ] ].GetPlNormal( smGroup ); + normals[ 2 ] = vertNormalCache[ vertIdx[ 2 ] ].GetPlNormal( smGroup ); + } + } + normals[ 0 ] = vertInvTransMatrix * normals[ 0 ]; + normals[ 1 ] = vertInvTransMatrix * normals[ 1 ]; + normals[ 2 ] = vertInvTransMatrix * normals[ 2 ]; + + // Adding normalization here, because we're going to compare them when searching for + // this vertex to share. mf. + normals[0].Normalize(); + normals[1].Normalize(); + normals[2].Normalize(); + + // The above section of code has just set any bump uv channels incorrectly, + // but at least they are there. Now we just need to correct the values. + if( bumpLayIdx[mainMatIndex] >= 0 ) + { + TVFace* tvFace = mesh->mapFaces(bumpLayChan[mainMatIndex]+1) + i; + ISetBumpUvs(bumpDuChan[mainMatIndex], vertDPosDuCache[mainMatIndex], tvFace, smGroup, uvs1, uvs2, uvs3); + ISetBumpUvs(bumpDvChan[mainMatIndex], vertDPosDvCache[mainMatIndex], tvFace, smGroup, uvs1, uvs2, uvs3); + } + + // Do this here, cause if we do it before we calculate the normals on smoothing group #0, + // the normals will be wrong + for( j = 0; j < 3; j++ ) + pos[ j ] = vert2LMatrix * pos[ j ]; + +/* We already compute the index, this looks like redundant code - 7/26/01 Bob + // Get span index + if( isMultiMat ) + { + mainMatIndex = maxFace->getMatID(); + if( mainMatIndex >= numMaterials ) + mainMatIndex = 0; + } +*/ + if (isComposite) + { + // I don't care about flipOrder here... it doesn't affect the index + float opac[][2] = {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + + opac[0][0] = uvs1[numChannels].fX; + opac[1][0] = uvs2[numChannels].fX; + opac[2][0] = uvs3[numChannels].fX; + opac[0][1] = uvs1[numChannels].fY; + opac[1][1] = uvs2[numChannels].fY; + opac[2][1] = uvs3[numChannels].fY; + + subMatIndex = ((plCompositeMtl *)currMaxMtl)->ComputeMaterialIndex(opac, 3) - 1; + } + // Add! + hsAssert(ourAccumulators[mainMatIndex] != nil, "Trying to add a face with an unused sub-material."); + ourAccumulators[ mainMatIndex ]->Get(subMatIndex)->AddVertex( vertIdx[ 0 ], &pos[ 0 ], &normals[ 0 ], colors[ 0 ], illums[ 0 ], uvs1 ); + ourAccumulators[ mainMatIndex ]->Get(subMatIndex)->AddVertex( vertIdx[ 1 ], &pos[ 1 ], &normals[ 1 ], colors[ 1 ], illums[ 1 ], uvs2 ); + ourAccumulators[ mainMatIndex ]->Get(subMatIndex)->AddVertex( vertIdx[ 2 ], &pos[ 2 ], &normals[ 2 ], colors[ 2 ], illums[ 2 ], uvs3 ); + + } + + + /// Now go through each accumulator, take any data created and stuff it into a new span + for( i = 0; i < numMaterials; i++ ) + { + hsTArray *subMats = ourMaterials[i]; + // A sub material of a MultiMat that never gets used will have a nil value, signifying no spans to export + if (subMats == nil) + continue; + for( j = 0; j < subMats->GetCount(); j++) + { + plMAXVertexAccumulator *accum = ourAccumulators[i]->Get(j); + + // With composite materials, not every accumulator will have faces. Only create spans for the ones that do. + if (accum->GetVertexCount() == 0) + continue; + + plGeometrySpan *span = TRACKED_NEW plGeometrySpan; + + span->BeginCreate( subMats->Get(j).fMaterial, l2wMatrix, ourFormat ); + span->fLocalToOBB = node->GetLocalToOBB44(); + span->fOBBToLocal = node->GetOBBToLocal44(); + + accum->StuffMyData( node, span, mesh, skinData ); + span->fProps |= sharedSpanProps; + + span->fWaterHeight = waterHeight; + + if( (bumpDuChan[i] >= 0) && (bumpDvChan[i] > 0) ) + span->fLocalUVWChans = (bumpDuChan[i] << 8) | bumpDvChan[i]; + + if( (span->fMaterial != nil) + && (span->fMaterial->GetNumLayers() > 0) + && (span->fMaterial->GetLayer( 0 )->GetState().fBlendFlags & hsGMatState::kBlendMask) ) + { + span->fProps |= plGeometrySpan::kRequiresBlending; + } + if( node->GetForceSortable() ) + span->fProps |= plGeometrySpan::kRequiresBlending; + + span->EndCreate(); + + hsScalar minDist, maxDist; + if( hsMaterialConverter::HasVisDists(node, i, minDist, maxDist) ) + { + span->fMinDist = (minDist); + span->fMaxDist = (maxDist); + } + + // If we're not doing preshading later, make sure everything is illuminated so you can see + if (doPreshading) + { + hsColorRGBA gray; + gray.Set(0.5, 0.5, 0.5, 0.0); + for( int iVert = 0; iVert < span->fNumVerts; iVert++ ) + span->StuffVertex( iVert, &white, &gray ); + } + + if( span->fNumVerts > 0 ) + spanArray.Append( span ); + else + delete span; + } + } + + void SetWaterColor(hsTArray& spans); + + // A bit of test hack here. Remind me to nuke it. + if( node->GetCalcEdgeLens() || node->UserPropExists("XXXWaterColor") ) + SetWaterColor(spanArray); + + + // Now that we have our nice spans, see if they need to be diced up a bit + int maxFaces, minFaces; + float maxSize; + if( node->GetGeoDice(maxFaces, maxSize, minFaces) ) + { + plGeoSpanDice dice; + dice.SetMaxFaces(maxFaces); + dice.SetMaxSize(hsPoint3(maxSize, maxSize, maxSize)); + dice.SetMinFaces(minFaces); + dice.Dice(spanArray); + } + + /// Check for overflow (only do it if our initial tests showed there could be overflow; this + /// is so these tests don't slow down the loop unless absolutely needed). + // We're going to go ahead and quietly break up the mesh if it needs breaking, + // because we can probably do a better job of it than production anyway. + if( checkForOverflow ) + { + hsBool needMoreDicing = false; + int i; + for( i = 0; i < spanArray.GetCount(); i++ ) + { + plAccessGeometry accGeom; + plAccessSpan accSpan; + accGeom.AccessSpanFromGeometrySpan(accSpan, spanArray[i]); + hsBool destroySpan = false; + if( accSpan.HasAccessVtx() ) + { + if( accSpan.AccessVtx().VertCount() >= plGBufferGroup::kMaxNumVertsPerBuffer ) + { + needMoreDicing = true; + } + } + if( accSpan.HasAccessTri() ) + { + if( accSpan.AccessTri().TriCount() * 3 >= plGBufferGroup::kMaxNumIndicesPerBuffer ) + { + needMoreDicing = true; + } + } + accGeom.Close(accSpan); + } + if( needMoreDicing ) + { + // Could just dice the ones that need it, but whatever. mf. + plConst(int) kAutoMaxFaces(5000); + plConst(float) kAutoMaxSize(10000.f); + plConst(int) kAutoMinFaces(1000); + plGeoSpanDice dice; + dice.SetMaxFaces(kAutoMaxFaces); + dice.SetMaxSize(hsPoint3(kAutoMaxSize,kAutoMaxSize,kAutoMaxSize)); + dice.SetMinFaces(kAutoMinFaces); + dice.Dice(spanArray); + } + } + throw (hsBool)true; + } + catch( hsBool retVal ) + { + /// Cleanup! + for( i = 0; i < vertNormalCache.GetCount(); i++ ) + vertNormalCache[ i ].DestroyChain(); + + for( i = 0; i < numMaterials; i++ ) + { + if( vertDPosDuCache != nil ) + { + for( j = 0; j < vertDPosDuCache[i].GetCount(); j++ ) + vertDPosDuCache[i][j].DestroyChain(); + } + if( vertDPosDvCache != nil ) + { + for( j = 0; j < vertDPosDvCache[i].GetCount(); j++ ) + vertDPosDvCache[i][j].DestroyChain(); + } + + if (ourAccumulators[i] == nil) + continue; + for( j = 0; j < ourAccumulators[ i ]->GetCount(); j++ ) + { + delete ourAccumulators[ i ]->Get(j); + } + delete ourMaterials[ i ]; + delete ourAccumulators[ i ]; + } + delete [] vertDPosDuCache; + delete [] vertDPosDvCache; + delete [] colorArray; + delete [] illumArray; + + IDeleteTempGeometry(); + + return retVal; + } + + return true; + hsGuardEnd; +} + +//// ICreateHexColor ///////////////////////////////////////////////////////// + +UInt32 plMeshConverter::ICreateHexColor( float r, float g, float b ) +{ + UInt32 ru, gu, bu, au; + + + au = 0xff000000; + ru = r * 255.0f; + gu = g * 255.0f; + bu = b * 255.0f; + return au | ( ru << 16 ) | ( gu << 8 ) | ( bu ); +} + +UInt32 plMeshConverter::ISetHexAlpha( UInt32 color, float alpha) +{ + UInt32 alphaBits = alpha * 255; + alphaBits <<= 24; + return color & 0x00ffffff | alphaBits; +} + +// Delete temp geo stuff allocated (either directly or indirectly) +// via IGetNodeMesh(). +void plMeshConverter::IDeleteTempGeometry() +{ + if( fTriObjToDelete != nil ) + { + fTriObjToDelete->DeleteMe(); + fTriObjToDelete = nil; + } + if( fMeshToDelete ) + { + delete fMeshToDelete; + fMeshToDelete = nil; + } +} + +//// IGetNodeMesh //////////////////////////////////////////////////////////// +// Get the Mesh object attached to a node. Returns nil if the node +// is not a triMesh object + +Mesh *plMeshConverter::IGetNodeMesh( plMaxNode *node ) +{ + hsGuardBegin( "plMeshConverter::IGetNodeMesh" ); + + const char* dbgNodeName = node->GetName(); + + fTriObjToDelete = nil; + fMeshToDelete = nil; + + // Get da object + Object *obj = node->EvalWorldState( fConverterUtils.GetTime( fInterface ) ).obj; + if( obj == nil ) + return nil; + + if( !obj->CanConvertToType( triObjectClassID ) ) + return nil; + + // Convert to triMesh object + TriObject *meshObj = (TriObject *)obj->ConvertToType( fConverterUtils.GetTime( fInterface ), triObjectClassID ); + if( meshObj == nil ) + return nil; + + if( meshObj != obj ) + fTriObjToDelete = meshObj; + + // Get the mesh + Mesh *mesh = &(meshObj->mesh); + if( mesh->getNumFaces() == 0 ) + return nil; + + if( node->GetDup2Sided() ) + { + mesh = IDuplicate2Sided(node, mesh); + + IDeleteTempGeometry(); + + fMeshToDelete = mesh; + } + + return mesh; + hsGuardEnd; +} + +Mesh* plMeshConverter::IDuplicate2Sided(plMaxNode* node, Mesh* mesh) +{ + mesh = TRACKED_NEW Mesh(*mesh); + + Mtl* mtl = node->GetMtl(); + + BitArray faces(mesh->getNumFaces()); + + int num2Sided = 0; + + int origNumFaces = mesh->getNumFaces(); + + int i; + for( i = 0; i < mesh->getNumFaces(); i++ ) + { + if( hsMaterialConverter::IsTwoSided(mtl, mesh->faces[i].getMatID()) ) + { + num2Sided++; + faces.Set(i); + } + } + + if( !num2Sided ) + return mesh; + + MeshDelta meshDelta(*mesh); + meshDelta.CloneFaces(*mesh, faces); + meshDelta.Apply(*mesh); + + BitArray verts(mesh->getNumVerts()); + verts.SetAll(); + const float kWeldThresh = 0.1f; + meshDelta.WeldByThreshold(*mesh, verts, kWeldThresh); + meshDelta.Apply(*mesh); + + hsAssert(origNumFaces + num2Sided == mesh->getNumFaces(), "Whoa, lost or gained, unexpected"); + + for( i = origNumFaces; i < mesh->getNumFaces(); i++ ) + { + meshDelta.FlipNormal(*mesh, i); + } + meshDelta.Apply(*mesh); + + return mesh; +} + +//// IGenerateUVs //////////////////////////////////////////////////////////// +// Generates the UV coordinates for the three vertices of a given face. +// Returns the number of UV channels. Ripped off of SetUVs() from the old +// hsMeshConverter. + +int plMeshConverter::IGenerateUVs( plMaxNode *node, Mtl *maxMtl, Mesh *mesh, int faceIdx, int numChan, int numBlend, + hsPoint3 *uvs1, hsPoint3 *uvs2, hsPoint3 *uvs3 ) +{ + hsGuardBegin( "plMeshConverter::IGenerateUVs" ); + + if( !( maxMtl && ( hsMaterialConverter::Instance().IsMultiMat( maxMtl ) || ( maxMtl->Requirements(-1) & MTLREQ_UV ) ) ) ) + { + return 0; + } + + + // To avoid transforming the shared UVs while rendering, we will + // sometimes pretransform them and not share. + + int j, k; + Face *face = &mesh->faces[ faceIdx ]; + + + + if( hsMaterialConverter::Instance().IsMultiMat( maxMtl ) ) + { + int faceMtlIndex = face->getMatID(); + if( faceMtlIndex >= maxMtl->NumSubMtls() ) // we'll warn in createtrimeshrecur + faceMtlIndex = 0; + } + + hsBool firstWarn = true; + hsPoint3 pt; + + /// Loop through the vertices + for( j = 0; j < 3; j++ ) + { + int chan; + for( chan = 0; chan < numChan; chan++ ) + { + if( mesh->mapFaces( chan + 1 ) ) + { + TVFace* tvFace = &mesh->mapFaces( chan + 1 )[ faceIdx ]; + UVVert* tVerts = mesh->mapVerts( chan + 1 ); + + if( firstWarn && fErrorMsg->Set( !tvFace, node->GetName(), "Check mapping on textured objects" ).CheckAndAsk() ) + { + firstWarn = false; + } + fErrorMsg->Set( false ); + if( !tvFace ) + { + continue; + } + + int tvIdx = tvFace->getTVert( j ); + + if( tvIdx >= mesh->getNumMapVerts( chan + 1 ) ) + { + static int muteWarn = false; + if( !muteWarn ) + { + muteWarn = fErrorMsg->Set( true, node->GetName(), "Check mapping on channel %d!!!", chan + 1 ).CheckAskOrCancel(); + fErrorMsg->Set( false ); + } + tvIdx = 0; + } + + UVVert uv = tVerts[tvIdx]; + + // The artists set the 3rd coordinate to help create the mapping, + // but we never need it at runtime, so let's set it to zero on + // export and then the vert coder can detect that it doesn't + // even need to write it. + pt.Set( uv.x, 1.0f - uv.y, 0.f ); + + if( _isnan( (double)pt.fX ) || _isnan( (double)pt.fY ) || _isnan( (double)pt.fZ ) ) + pt.Set( 0, 0, 0 ); + + switch( j ) + { + case 0: uvs1[ chan ] = pt; break; + case 1: uvs2[ chan ] = pt; break; + case 2: uvs3[ chan ] = pt; break; + } + } + + } + + // That takes care of the UVs MAX gives us. Do we need some leftover channels to store our blending info? + for (k = numChan; k < numChan + numBlend; k++) + { + UVVert *alphas = mesh->mapVerts(MAP_ALPHA); + UVVert *illums = mesh->mapVerts(MAP_SHADING); + UVVert pt; + pt.z = 0.0f; + + TVFace* alphaFace = mesh->mapFaces(MAP_ALPHA) ? &mesh->mapFaces(MAP_ALPHA)[faceIdx] : nil; + TVFace* illumFace = mesh->mapFaces(MAP_SHADING) ? &mesh->mapFaces(MAP_SHADING)[faceIdx] : nil; + + if (hsMaterialConverter::Instance().IsCompositeMat( maxMtl )) + { + plCompositeMtl *compMtl = (plCompositeMtl *)maxMtl; + IParamBlock2 *pb = maxMtl->GetParamBlockByID(kCompPasses); + + // MF_HORSE CARNAGE + + + compMtl->SetOpacityVal(&pt.x, + (alphas == nil + ? nil + : &alphas[ alphaFace->getTVert(j) ]), + (illums == nil + ? nil + : &illums[ illumFace->getTVert(j) ]), + pb->GetInt(kCompBlend, 0, 0)); + + compMtl->SetOpacityVal(&pt.y, + (alphas == nil + ? nil + : &alphas[ alphaFace->getTVert(j) ]), + (illums == nil + ? nil + : &illums[ illumFace->getTVert(j) ]), + pb->GetInt(kCompBlend, 0, 1)); + // MF_HORSE CARNAGE + } + else // all other materials + { + pt.y = 0.0; + pt.z = 1.0; + if (alphas == nil || alphaFace == nil) + pt.x = 1.0; + else + pt.x = alphas[alphaFace->getTVert(j)].x; + } + + switch( j ) + { + case 0: uvs1[ k ].fX = pt.x; uvs1[ k ].fY = pt.y; uvs1[ k ].fZ = pt.z; break; + case 1: uvs2[ k ].fX = pt.x; uvs2[ k ].fY = pt.y; uvs2[ k ].fZ = pt.z; break; + case 2: uvs3[ k ].fX = pt.x; uvs3[ k ].fY = pt.y; uvs3[ k ].fZ = pt.z; break; + } + } + } + + return numChan; + hsGuardEnd; +} + +void plMeshConverter::ISetWaterDecEnvUvSrcs(hsTArray *>& ourMaterials, + hsTArray& bumpLayIdx, + hsTArray& bumpLayChan, + hsTArray& bumpDuChan, + hsTArray& bumpDvChan) +{ + bumpLayIdx.SetCount(ourMaterials.GetCount()); + bumpLayChan.SetCount(ourMaterials.GetCount()); + bumpDuChan.SetCount(ourMaterials.GetCount()); + bumpDvChan.SetCount(ourMaterials.GetCount()); + + int i; + for( i = 0; i < ourMaterials.GetCount(); i++ ) + { + bumpLayIdx[i] = i; + bumpLayChan[i] = 0; + bumpDuChan[i] = 1; + bumpDvChan[i] = 2; + } +} + +void plMeshConverter::ISetBumpUvSrcs(hsTArray *>& ourMaterials, + hsTArray& bumpLayIdx, + hsTArray& bumpLayChan, + hsTArray& bumpDuChan, + hsTArray& bumpDvChan) +{ + bumpLayIdx.SetCount(ourMaterials.GetCount()); + bumpLayChan.SetCount(ourMaterials.GetCount()); + bumpDuChan.SetCount(ourMaterials.GetCount()); + bumpDvChan.SetCount(ourMaterials.GetCount()); + + int i; + for( i = 0; i < ourMaterials.GetCount(); i++ ) + { + bumpLayIdx[i] = -1; + bumpLayChan[i] = -1; + bumpDuChan[i] = -1; + bumpDvChan[i] = -1; + + // The following two lines pretty much rule out composites with bump maps. + if( !ourMaterials[i] ) + continue; + + if( ourMaterials[i]->GetCount() != 1 ) + continue; + + + hsGMaterial* ourMat = ourMaterials[i]->Get(0).fMaterial; + int j; + for( j = 0; j < ourMat->GetNumLayers(); j++ ) + { + if( ourMat->GetLayer(j)->GetMiscFlags() & hsGMatState::kMiscBumpLayer ) + { + bumpLayIdx[i] = j; + bumpLayChan[i] = ourMat->GetLayer(j)->GetUVWSrc(); + } + + if( ourMat->GetLayer(j)->GetMiscFlags() & hsGMatState::kMiscBumpDu ) + bumpDuChan[i] = ourMat->GetLayer(j)->GetUVWSrc(); + + if( ourMat->GetLayer(j)->GetMiscFlags() & hsGMatState::kMiscBumpDv ) + bumpDvChan[i] = ourMat->GetLayer(j)->GetUVWSrc(); + } + } +} + +void plMeshConverter::ISetBumpUvs(Int16 uvChan, hsTArray& vertDPosDuvCache, TVFace* tvFace, UInt32 smGroup, + hsPoint3* uvs1, hsPoint3* uvs2, hsPoint3* uvs3) +{ + if( uvChan < 0 ) + return; + + uvs1[uvChan] = *(hsPoint3*)&vertDPosDuvCache[tvFace->getTVert(0)].GetPlNormal(smGroup); + uvs2[uvChan] = *(hsPoint3*)&vertDPosDuvCache[tvFace->getTVert(1)].GetPlNormal(smGroup); + uvs3[uvChan] = *(hsPoint3*)&vertDPosDuvCache[tvFace->getTVert(2)].GetPlNormal(smGroup); +} + +// Determine if we're going to need a uv gradient channel. +// If we do need one, determine which uvw channel needs the gradient. +// Finally, make the gradients, smoothing according to smooth groups (just like vertex normals). +// +// If we decided we needed them, they are in the output arrays, otherwise the output arrays are made empty. +void plMeshConverter::ISmoothUVGradients(plMaxNode* node, Mesh* mesh, + hsTArray *>& ourMaterials, + hsTArray& bumpLayIdx, hsTArray& bumpLayChan, + hsTArray* vertDPosDuCache, hsTArray* vertDPosDvCache) +{ + const char* dbgNodeName = node->GetName(); + + Mtl* mainMtl = hsMaterialConverter::Instance().GetBaseMtl( node ); + + hsBool needsGradientUvs = hsMaterialConverter::Instance().HasBumpLayer(node, mainMtl) || node->GetWaterDecEnv(); + + if( needsGradientUvs ) + { + int matIdx; + for( matIdx = 0; matIdx < ourMaterials.GetCount(); matIdx++ ) + { + if( bumpLayIdx[matIdx] >= 0 ) + { + UInt32 uvwSrc = bumpLayChan[matIdx]; + if( mesh->getNumMapVerts(uvwSrc+1) && mesh->mapVerts(uvwSrc+1) ) + { + vertDPosDuCache[matIdx].SetCount(mesh->getNumMapVerts(uvwSrc+1)); + vertDPosDvCache[matIdx].SetCount(mesh->getNumMapVerts(uvwSrc+1)); + } + else + { + // Ooops. This is probably an error somewhere. + hsAssert(false, "Thought we had a valid bump map, but we don't."); + bumpLayIdx[matIdx] = -1; + } + } + } + + hsBool isMultiMat = hsMaterialConverter::Instance().IsMultiMat(mainMtl); + int i; + for( i = 0; i < mesh->getNumFaces(); i++ ) + { + const plLayerInterface* layer = nil; + if( isMultiMat ) + { + int index = mesh->faces[i].getMatID(); + if (index >= mainMtl->NumSubMtls()) + index = 0; + + matIdx = index; + if( bumpLayIdx[index] >= 0 ) + layer = ourMaterials[index]->Get(0).fMaterial->GetLayer(bumpLayIdx[index]); + } + else + { + matIdx = 0; + if( bumpLayIdx[0] >= 0 ) + layer = ourMaterials[0]->Get(0).fMaterial->GetLayer(bumpLayIdx[0]); + } + if( layer ) + { + Point3 dPosDu = IGetUvGradient(node, layer->GetTransform(), layer->GetUVWSrc(), + mesh, i, + 0); + Point3 dPosDv = IGetUvGradient(node, layer->GetTransform(), layer->GetUVWSrc(), + mesh, i, + 1); + +// #define MF_BUMP_CHECK_DUXDV +#ifdef MF_BUMP_CHECK_DUXDV + Point3 duXdv = ::Normalize(dPosDu) ^ ::Normalize(dPosDv); + + Point3 v0 = mesh->verts[ mesh->faces[i].v[ 0 ] ]; + Point3 v1 = mesh->verts[ mesh->faces[i].v[ 1 ] ]; + Point3 v2 = mesh->verts[ mesh->faces[i].v[ 2 ] ]; + + Point3 norm = ::Normalize(( v1 - v0 ) ^ ( v2 - v1 )); + + Point3 diff = duXdv - norm; + + static int doAgain = false; + if( doAgain ) + { + dPosDu = IGetUvGradient(node, layer->GetTransform(), layer->GetUVWSrc(), + mesh, i, + 0); + dPosDv = IGetUvGradient(node, layer->GetTransform(), layer->GetUVWSrc(), + mesh, i, + 1); + } +#endif // MF_BUMP_CHECK_DUXDV + + if( node->GetWaterDecEnv() ) + { + dPosDu.z = dPosDv.z = 0.f; + } + + // Flip the direction of dPosDv, because we flip textures about V for histerical reasons. + dPosDv = -dPosDv; + + TVFace* tvFace = &mesh->mapFaces(layer->GetUVWSrc() + 1)[i]; + int j; + for( j = 0; j < 3; j++ ) + { + vertDPosDuCache[matIdx][tvFace->getTVert(j)].AddNormal(dPosDu, mesh->faces[i].smGroup); + vertDPosDvCache[matIdx][tvFace->getTVert(j)].AddNormal(dPosDv, mesh->faces[i].smGroup); + } + } + } + for( matIdx = 0; matIdx < ourMaterials.GetCount(); matIdx++ ) + { + for( i = 0; i < vertDPosDuCache[matIdx].GetCount(); i++ ) + { + vertDPosDuCache[matIdx][i].Normalize(); + vertDPosDvCache[matIdx][i].Normalize(); + } + } + } +} + +// Get dPos/du into uvws[0], and dPos/dv into uvws[1]. dPos should be in the object's local space. +Point3 plMeshConverter::IGetUvGradient(plMaxNode* node, + const hsMatrix44& uvXform44, Int16 bmpUvwSrc, // Transform and uvwSrc of layer to gradient + Mesh *mesh, int faceIdx, + int iUV) // 0 = uvw.x, 1 = uv2.y +{ + Point3 uvwOut(0,0,0); + if( bmpUvwSrc < 0 ) + return uvwOut; // Not Error. + + if( bmpUvwSrc >= mesh->getNumMaps() ) + return uvwOut; // Error? + + TVFace* tvFace = &mesh->mapFaces(bmpUvwSrc + 1)[faceIdx]; + UVVert* tVerts = mesh->mapVerts(bmpUvwSrc + 1); + if( !tvFace ) + return uvwOut; // Error? + if( !tVerts ) + return uvwOut; // Error? + + Matrix3 v2l = node->GetVertToLocal(); + + hsBool flipOrder = v2l.Parity(); + int vtxIdx = 0; + int vtxNext = flipOrder ? 2 : 1; + int vtxLast = flipOrder ? 1 : 2; + + // Get the three verts, v0-v2, where v0 is the corner in question. + Face* face = &mesh->faces[faceIdx]; + Point3 v0 = v2l * mesh->verts[face->getVert(vtxIdx)]; + Point3 v1 = v2l * mesh->verts[face->getVert(vtxNext)]; + Point3 v2 = v2l * mesh->verts[face->getVert(vtxLast)]; + + // Get the three uvs, uv0-uv2, matching above verts. + if( tvFace->getTVert(vtxIdx) >= mesh->getNumMapVerts(bmpUvwSrc + 1) ) + return uvwOut; // Error? + if( tvFace->getTVert(vtxNext) >= mesh->getNumMapVerts(bmpUvwSrc + 1) ) + return uvwOut; // Error? + if( tvFace->getTVert(vtxLast) >= mesh->getNumMapVerts(bmpUvwSrc + 1) ) + return uvwOut; // Error? + + Matrix3 uvwXform = plMaxNodeBase::Matrix44ToMatrix3(uvXform44); + + Point3 uv0 = uvwXform * tVerts[tvFace->getTVert(vtxIdx)]; + Point3 uv1 = uvwXform * tVerts[tvFace->getTVert(vtxNext)]; + Point3 uv2 = uvwXform * tVerts[tvFace->getTVert(vtxLast)]; + + + const float kRealSmall = 1.e-6f; + // First, look for degenerate cases. + // If (uvn - uvm)[!iUV] == 0 + // then (vn - vm) is tangent in iUV dimension + // Just be careful about direction, since (vn-vm) may be opposite direction from + // increasing iUV. + int iNotUV = !iUV; + float del = uv0[iNotUV] - uv1[iNotUV]; + if( fabs(del) < kRealSmall ) + { + if( uv0[iUV] - uv1[iUV] < 0 ) + uvwOut = v1 - v0; + else + uvwOut = v0 - v1; + return uvwOut; + } + del = uv2[iNotUV] - uv1[iNotUV]; + if( fabs(del) < kRealSmall ) + { + if( uv2[iUV] - uv1[iUV] < 0 ) + uvwOut = v1 - v2; + else + uvwOut = v2 - v1; + return uvwOut; + } + del = uv2[iNotUV] - uv0[iNotUV]; + if( fabs(del) < kRealSmall ) + { + if( uv2[iUV] - uv0[iUV] < 0 ) + uvwOut = v0 - v2; + else + uvwOut = v2 - v0; + return uvwOut; + } + + // Okay, none of the edges are along the dU gradient. That's good, because + // it means we don't have to worry about divides by zero in what we're about + // to do. + del = uv0[iNotUV] - uv1[iNotUV]; + del = 1.f / del; + Point3 v0Mv1 = v0 - v1; + v0Mv1 *= del; + float v0uv = (uv0[iUV] - uv1[iUV]) * del; + + del = uv2[iNotUV] - uv1[iNotUV]; + del = 1.f / del; + Point3 v2Mv1 = v2 - v1; + v2Mv1 *= del; + float v2uv = (uv2[iUV] - uv1[iUV]) * del; + + if( v0uv > v2uv ) + uvwOut = v0Mv1 - v2Mv1; + else + uvwOut = v2Mv1 - v0Mv1; + + return uvwOut; +} + +//// IGetUVTransform ///////////////////////////////////////////////////////// +// Gets the UV transform matrix for the given channel. + +void plMeshConverter::IGetUVTransform( plMaxNode *node, Mtl *mtl, Matrix3 *uvTransform, int which ) +{ + hsGuardBegin( "plMeshConverter::IGetUVTransform" ); + + uvTransform->IdentityMatrix(); + + if( !mtl ) + return; + + Texmap* texMap = hsMaterialConverter::Instance().GetUVChannelBase(node, mtl, which); + + if( !texMap ) + return; + + BitmapTex *bitmapTex = (BitmapTex *)texMap; + +#ifndef NDEBUG + CStr className; + texMap->GetClassName(className); + if( strcmp(className,"Bitmap") && strcmp(className,"Plasma Layer") && strcmp(className,"Plasma Layer Dbg.")) + return; + + char txtFileName[256]; + strcpy(txtFileName, bitmapTex->GetMapName()); +#endif // NDEBUG + + StdUVGen *uvGen = bitmapTex->GetUVGen(); + + uvGen->GetUVTransform(*uvTransform); + + // We're going to munge the internals of the matrix here, but we won't need to worry about + // the flags (ident etc.) because we're not changing the character of the matrix (unless + // it's a pure translation by an integer amount, which is a no-op). + // We also don't have to worry about preserving unit offsets for animation, because the + // output of this function is used as a static matrix (for uv generation). We could check + // and not do this on animated transforms or something. mf + // Note that we can only do this if the texture wraps (not clamps) + if( !hsMaterialConverter::Instance().PreserveUVOffset(mtl) ) + { + MRow* data = uvTransform->GetAddr(); + int i; + for( i = 0; i < 2; i++ ) + { + if( fabsf(data[3][i]) >= 1.f ) + { + data[3][i] -= float(int(data[3][i])); + } + } + } + hsGuardEnd; +} + +////////////////////////////////////////////////////////////////////////////// +//// Helper Class Functions ////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// plMAXVertexAccNode Constructor ////////////////////////////////////////// + +plMAXVertexAccNode::plMAXVertexAccNode( const hsPoint3 *point, const hsVector3 *normal, const hsColorRGBA& color, const hsColorRGBA& illum, int numChannels, const hsPoint3 *uvs, UInt32 index ) +{ + int i; + + + fPoint = *point; + fNormal = *normal; + fColor = color; + fIllum = illum; + for( i = 0; i < numChannels; i++ ) + fUVs[ i ] = uvs[ i ]; + fNumChannels = numChannels; + fIndex = index; + + fNext = nil; +} + +//// IsEqual ///////////////////////////////////////////////////////////////// +// Determines whether the node matches the values given. + +hsBool plMAXVertexAccNode::IsEqual( const hsVector3 *normal, const hsColorRGBA& color, const hsColorRGBA& illum, const hsPoint3 *uvs ) +{ + int i; + + + if( color != fColor || !( *normal == fNormal ) || illum != fIllum ) + return false; + + for( i = 0; i < fNumChannels; i++ ) + { + if( !( uvs[ i ] == fUVs[ i ] ) ) + return false; + } + + return true; +} + +//// plMAXVertexAccumulator Constructor & Destructor ///////////////////////// + +plMAXVertexAccumulator::plMAXVertexAccumulator( int numOrigPoints, int numChannels ) +{ + fNumPoints = numOrigPoints; + fNumChannels = numChannels; + + fPointList = TRACKED_NEW plMAXVertexAccNodePtr[ fNumPoints ]; + memset( fPointList, 0, sizeof( plMAXVertexAccNode * ) * fNumPoints ); + + fIndices.Reset(); + fInverseVertTable.Reset(); + fNumVertices = 0; +} + +plMAXVertexAccumulator::~plMAXVertexAccumulator() +{ + int i; + plMAXVertexAccNode *node; + + + for( i = 0; i < fNumPoints; i++ ) + { + while( fPointList[ i ] != nil ) + { + node = fPointList[ i ]->fNext; + delete fPointList[ i ]; + fPointList[ i ] = node; + } + } + + delete [] fPointList; +} + +//// AddVertex /////////////////////////////////////////////////////////////// +// Adds a vertex to this accumulator. If it already exists, adds the normals +// together and stores the old index; if not, creates a new node and adds +// an index for it. In the end, fIndices will be our final index buffer, +// while fInverseVertTable is basically an array telling us where to get +// our vertices when building the vertex buffer. + +void plMAXVertexAccumulator::AddVertex( int index, hsPoint3 *point, hsVector3 *normal, const hsColorRGBA& color, const hsColorRGBA& illum, hsPoint3 *uvs ) +{ + plMAXVertexAccNode *node; + + + // See if one exists in this list + for( node = fPointList[ index ]; node != nil; node = node->fNext ) + { + if( node->IsEqual( normal, color, illum, uvs ) ) + { + // Found match! Accumulate + fIndices.Append( node->fIndex ); + return; + } + } + + + /// Adding new + node = TRACKED_NEW plMAXVertexAccNode( point, normal, color, illum, fNumChannels, uvs, fNumVertices ); + fInverseVertTable.Append( index ); + fIndices.Append( fNumVertices++ ); + + node->fNext = fPointList[ index ]; + fPointList[ index ] = node; +} + +//// StuffMyData ///////////////////////////////////////////////////////////// +// Stuffs the data into the given span. Assumes the span has already begun +// creation. + +void plMAXVertexAccumulator::StuffMyData( plMaxNode* maxNode, plGeometrySpan *span, Mesh* mesh, ISkinContextData* skinData ) +{ + int i, j, origIdx; + plMAXVertexAccNode *node; + hsPoint3 *uvs[ plGeometrySpan::kMaxNumUVChannels ]; + + const char* dbgNodeName = maxNode->GetName(); + TempWeightInfo *weights = nil; + + /// Precalculate the weights if necessary + weights = TRACKED_NEW TempWeightInfo[ fNumPoints ]; + if( skinData != nil ) + { + for( i = 0; i < fNumPoints; i++ ) + { + IFindSkinWeights( skinData, i, weights[ i ].fWeights, &weights[ i ].fIndices ); + + /// Debug checking stuff, for testing only + // Wrong. Geometry span depends on this + switch( span->fFormat & plGeometrySpan::kSkinWeightMask ) + { + case plGeometrySpan::kSkin1Weight: + weights[ i ].fWeights[1] = -1.f; + break; + case plGeometrySpan::kSkin2Weights: + weights[ i ].fWeights[2] = -1.f; + break; + case plGeometrySpan::kSkin3Weights: + weights[ i ].fWeights[3] = -1.f; + break; + default: + hsAssert(false, "Shouldn't have gotten here"); + + break; + } + } + } + else if( maxNode->NumBones() ) + { + IFindAllUserSkinWeights(maxNode, mesh, weights); + } + else + { + for( i = 0; i < fNumPoints; i++ ) + { + weights[ i ].fWeights[ 0 ] = weights[ i ].fWeights[ 1 ] = + weights[ i ].fWeights[ 2 ] = weights[ i ].fWeights[ 3 ] = -1.0f; + weights[ i ].fIndices = 0; + } + } + + plMaxBoneMap *boneMap = maxNode->GetBoneMap(); + if (boneMap) + { + for (i = 0; i < fNumPoints; i++) + { + UInt8 indices[4]; + indices[0] = (weights[i].fIndices) & 0xff; + indices[1] = (weights[i].fIndices >> 8) & 0xff; + indices[2] = (weights[i].fIndices >> 16) & 0xff; + indices[3] = (weights[i].fIndices >> 24) & 0xff; + + for (j = 0; j < 4; j++) + { + //if (weights[i].fWeights[j] >= 0) + //{ + if (indices[j] != 0) + { + plMaxNodeBase *bone = maxNode->GetBone(indices[j] - 1); + char *dbgBoneName = bone->GetName(); + indices[j] = boneMap->GetIndex(bone) + 1; + } + //} + } + weights[i].fIndices = (indices[0]) | + (indices[1] << 8) | + (indices[2] << 16) | + (indices[3] << 24); + } + } + + hsScalar maxWgt = 0; + hsScalar penWgt = 0; + Int16 maxIdx = -1; + Int16 penIdx = -1; + // Find the highest two weighted bones. We'll use just these two to calculate our bounds. + for( i = 0; i < fNumPoints; i++ ) + { + if( weights[i].fIndices ) + { + for( j = 0; j < 4; j++ ) + { + if( weights[i].fWeights[j] < 0 ) + break; + if( weights[i].fWeights[j] > maxWgt ) + { + penWgt = maxWgt; + penIdx = maxIdx; + + maxWgt = weights[i].fWeights[j]; + maxIdx = (weights[i].fIndices >> (j*8)) & 0xff; + } + else + if( weights[i].fWeights[j] > penWgt ) + { + penWgt = weights[i].fWeights[j]; + penIdx = (weights[i].fIndices >> (j*8)) & 0xff; + } + } + } + } + if( maxIdx < 0 ) + maxIdx = 0; + if( penIdx < 0 ) + penIdx = maxIdx; + span->fMaxBoneIdx = maxIdx; + span->fPenBoneIdx = penIdx; + + /// Stuff the verts + for( i = 0; i < plGeometrySpan::kMaxNumUVChannels; i++) + uvs[ i ] = nil; + + for( i = 0; i < fNumVertices; i++ ) + { + origIdx = fInverseVertTable[ i ]; + + // origIdx gets us the list, but we need to know which node in the list + for( node = fPointList[ origIdx ]; node != nil; node = node->fNext ) + { + if( node->fIndex == i ) + { + // Found it! output this one + hsPoint3 normal; + + node->fNormal.Normalize(); + normal.Set( node->fNormal.fX, node->fNormal.fY, node->fNormal.fZ ); + for( j = 0; j < fNumChannels; j++ ) + uvs[ j ] = &node->fUVs[ j ]; + + /// Add! + span->AddVertex( &node->fPoint, &normal, node->fColor, node->fIllum, uvs, + weights[ origIdx ].fWeights[ 0 ], weights[ origIdx ].fWeights[ 1 ], + weights[ origIdx ].fWeights[ 2 ], weights[ origIdx ].fIndices ); + break; + } + } + hsAssert( node != nil, "Invalid accumulator table when stuffing buffers!" ); + } + + /// Now stuff the indices + for( i = 0; i < fIndices.GetCount(); i++ ) + span->AddIndex( fIndices[ i ] ); + + if( weights != nil ) + delete [] weights; +} + +// IFindAllUserSkinWeights +// Like IFindSkinWeights, but doesn't use Max's native skinning (e.g. ISkinContextData). +// Rather, someone has put a bone (currently only support one) on this plMaxNode, +// and told us what vertex channel to find the weight for that bone in. +void plMAXVertexAccumulator::IFindAllUserSkinWeights( plMaxNode* node, Mesh* mesh, TempWeightInfo weights[]) +{ + const char* dbgNodeName = node->GetName(); + + int iMap = MAP_ALPHA; // FISH HACK, till we stuff the src channel into the max node. + iMap = 66; + + int iChan = 1; // FISH HACK, get this one stuffed too. Could probably or them into the same + //thing or something, but who cares. Gotta stop with the ethers. + + UVVert *wgtMap = mesh->mapVerts(iMap); + int numWgtVerts = mesh->getNumMapVerts(iMap); + + TVFace* mapFaces = mesh->mapFaces(iMap); + + if( wgtMap && mapFaces ) + { + Face* faces = mesh->faces; + + int i; + for( i = 0; i < mesh->getNumFaces(); i++ ) + { + int j; + for( j = 0; j < 3; j++ ) + { + int iVtx = faces[i].getVert(j); + int iTvtx = mapFaces[i].getTVert(j); + + weights[iVtx].fWeights[2] = weights[iVtx].fWeights[3] = 0; + weights[iVtx].fWeights[0] = 1.f - wgtMap[iTvtx][iChan]; + if( weights[iVtx].fWeights[0] > 1.f ) + weights[iVtx].fWeights[0] = 1.f; + else if( weights[iVtx].fWeights[0] < 0 ) + weights[iVtx].fWeights[0] = 0; + weights[iVtx].fWeights[1] = 1.f - weights[iVtx].fWeights[0]; + weights[iVtx].fIndices = 1 << 8; + } + } + } + else + { + int i; + for( i = 0; i < mesh->getNumVerts(); i++ ) + { + weights[i].fWeights[1] = weights[i].fWeights[2] = weights[i].fWeights[3] = 0; + weights[i].fWeights[0] = 1.f; + weights[i].fIndices = 1 << 8; + } + } +} + +//// IFindSkinWeights //////////////////////////////////////////////////////// +// Finds the biggest weights (up to 4) for the given vertex index and returns +// them, along with a dword specifying the indices for each. + +void plMAXVertexAccumulator::IFindSkinWeights( ISkinContextData *skinData, + int vertex, + float *weights, UInt32 *indices ) +{ + float tempWs[ 4 ], tempW, t; + UInt32 idxs[ 4 ], tempIdx, tI; + int i, j; + + tempWs[ 0 ] = tempWs[ 1 ] = tempWs[ 2 ] = tempWs[ 3 ] = 0; + idxs[ 0 ] = idxs[ 1 ] = idxs[ 2 ] = idxs[ 3 ] = 0; + + int boneCount = skinData->GetNumAssignedBones( vertex); + + if( boneCount ) + { + hsScalar defWgt = 1.f; + for( i = 0; i < boneCount; i++ ) + { + /// Grab the weight and index for this bone + tempW = skinData->GetBoneWeight( vertex, i ); + defWgt -= tempW; + + // GetAssignedBone will assert unpredictably if the weight is 0.0f (exactly) + // It will usually then return 0 for the bone index, but sometimes 1 + // In any case, the bone index should not matter at that point. + // Without walking through all the downstream code, seems to work ok. + if(tempW > 0.0f) + tempIdx = skinData->GetAssignedBone( vertex, i ) + 1; + else + tempIdx = 0; + + // float hi = skinData->GetBoneWeight( vertex, tempIdx ); + + /// Slide it in to our list + for( j = 0; j < 4; j++ ) + { + if( tempWs[ j ] < tempW ) + { + t = tempWs[ j ]; + tempWs[ j ] = tempW; + tempW = t; + + tI = idxs[ j ]; + idxs[ j ] = tempIdx; + tempIdx = tI; + } + } + } + + // This isn't really what we want. If the weights add up to less than + // 1.f, the remainder is the un-skinned, un-boned Transform. + // If the weights add up to more than 1.f, someone probably screwed up, + // but we'll deal with it gracefully by normalizing. + if( defWgt > 0 ) + { + tempW = defWgt; + tempIdx = 0; + + /// Slide it in to our list + for( j = 0; j < 4; j++ ) + { + if( tempWs[ j ] < tempW ) + { + t = tempWs[ j ]; + tempWs[ j ] = tempW; + tempW = t; + + tI = idxs[ j ]; + idxs[ j ] = tempIdx; + tempIdx = tI; + } + } + } + + t = tempWs[ 0 ] + tempWs[ 1 ] + tempWs[ 2 ] + tempWs[ 3 ]; + t = 1.0f / t; + + weights[ 0 ] = tempWs[ 0 ] * t; + weights[ 1 ] = tempWs[ 1 ] * t; + weights[ 2 ] = tempWs[ 2 ] * t; + weights[ 3 ] = tempWs[ 3 ] * t; + } + else + { + weights[ 0 ] = 1.f; + idxs[ 0 ] = 0; + + weights[1] = weights[2] = weights[3] = 0; + idxs[1] = idxs[2] = idxs[3] = 0; + } + + if( skinData->GetNumAssignedBones( vertex ) < 2 ) + { + if( idxs[0] ) + { + float tWgt = weights[0]; + int tIdx = idxs[0]; + + weights[0] = weights[1]; + idxs[0] = idxs[1]; + + weights[1] = tWgt; + idxs[1] = tIdx; + } + } + + *indices = ( idxs[ 0 ] & 0xff ) | ( ( idxs[ 1 ] & 0xff ) << 8 ) | + ( ( idxs[ 2 ] & 0xff ) << 16 ) | ( ( idxs[ 3 ] & 0xff ) << 24 ); +} + +int plMAXVertexAccumulator::GetVertexCount() +{ +// return fIndices.GetCount(); + return fNumVertices; +} + +void SetWaterColor(plGeometrySpan* span) +{ + plAccessGeometry accGeom; + // First, set up our access, iterators and that mess. + plAccessSpan acc; + accGeom.AccessSpanFromGeometrySpan(acc, span); + if( !acc.HasAccessTri() ) + { + plAccessGeometry::Instance()->Close(acc); + return; + } + plAccessTriSpan& tri = acc.AccessTri(); + plAccTriIterator triIter(&tri); + + const int nVerts = tri.VertCount(); + // Now, set up our accumulators + hsTArray lens; + lens.SetCount(nVerts); + memset(lens.AcquireArray(), 0, nVerts * sizeof(hsScalar)); + hsTArray wgts; + wgts.SetCount(nVerts); + memset(wgts.AcquireArray(), 0, nVerts * sizeof(hsScalar)); + + // For each triangle + for( triIter.Begin(); triIter.More(); triIter.Advance() ) + { + // This area thing seems like a good robust idea, but it doesn't really + // take into account the fact that the sampling frequency is really determined + // by the weakest link, or in this case the longest link. Experimenting + // with alternatives. + // Actually, I just realized that the area way kind of sucks, because, + // as a parallelogram gets less and less rectangular, the area goes down + // even as the longest edge (the diagonal) gets longer. + hsScalar lenSq20 = hsVector3(&triIter.Position(2), &triIter.Position(0)).MagnitudeSquared(); + hsScalar lenSq10 = hsVector3(&triIter.Position(1), &triIter.Position(0)).MagnitudeSquared(); + hsScalar lenSq21 = hsVector3(&triIter.Position(2), &triIter.Position(1)).MagnitudeSquared(); + hsScalar len = lenSq20; + if( len < lenSq10 ) + len = lenSq10; + if( len < lenSq21 ) + len = lenSq21; + len = hsSquareRoot(len); + + lens[triIter.RawIndex(0)] += len; + wgts[triIter.RawIndex(0)] += 1.f; + + lens[triIter.RawIndex(1)] += len; + wgts[triIter.RawIndex(1)] += 1.f; + + lens[triIter.RawIndex(2)] += len; + wgts[triIter.RawIndex(2)] += 1.f; + + } + // For each vert + int iVert; + for( iVert = 0; iVert < nVerts; iVert++ ) + { + if( wgts[iVert] > 0.f ) + lens[iVert] /= wgts[iVert]; + + wgts[iVert] = 0.f; // We'll use them again on smoothing. + } + // Now we might want to smooth this out some + // This can be repeated for any degree of smoothing + hsTArray smLens; + smLens.SetCount(nVerts); + memset(smLens.AcquireArray(), 0, nVerts * sizeof(hsScalar)); + // For each triangle + for( triIter.Begin(); triIter.More(); triIter.Advance() ) + { + int i; + // For each edge + for( i = 0; i < 3; i++ ) + { + int iVert = triIter.RawIndex(i); + int iVertNext = triIter.RawIndex(i < 2 ? i+1 : 0); + int iVertLast = triIter.RawIndex(i ? i-1 : 2); + smLens[iVert] += lens[iVert]; + wgts[iVert] += 1.f; + + const hsScalar kSmooth(8.f); + smLens[iVertNext] += lens[iVert] * kSmooth; + wgts[iVertNext] += kSmooth; + + smLens[iVertLast] += lens[iVert] * kSmooth; + wgts[iVertLast] += kSmooth; + } + } + lens.Swap(smLens); + // For each vert + for( iVert = 0; iVert < nVerts; iVert++ ) + { + if( wgts[iVert] > 0.f ) + lens[iVert] /= wgts[iVert]; + + wgts[iVert] = 0.f; // We'll use them again on smoothing. + } + + plConst(hsScalar) kNumLens(4.f); + // Okay, we have smoothed lengths. We just need to + // iterate over the vertices and stuff 1/len into the alpha channel + // For each vert + plAccDiffuseIterator colIter(&tri); + for( iVert = 0, colIter.Begin(); colIter.More(); iVert++, colIter.Advance() ) + { + hsColorRGBA multCol; + hsColorRGBA addCol; + span->ExtractInitColor(iVert, &multCol, &addCol); + + // Get the vert color + hsColorRGBA col = colIter.DiffuseRGBA(); + col = multCol; + + col.a = lens[iVert] > 0.f ? 1.f / (kNumLens * lens[iVert]) : 1.f; + + // Stuff color back in. + *colIter.Diffuse32() = col.ToARGB32(); + } + + // Close up the access span. + accGeom.Close(acc); +} + +void SetWaterColor(hsTArray& spans) +{ + int i; + for( i = 0; i < spans.GetCount(); i++ ) + SetWaterColor(spans[i]); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMeshConverter.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMeshConverter.h new file mode 100644 index 00000000..cfdc9245 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plMeshConverter.h @@ -0,0 +1,129 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plMeshConverter Class Header // +// Static class that converts a Max triMesh object into the geometrySpans // +// necessary for a plDrawableIce object. // +// // +//// Version History ///////////////////////////////////////////////////////// +// // +// Created 4.18.2001 mcn // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plMeshConverter_h +#define _plMeshConverter_h + +#include "Max.h" +#include "HeadSpin.h" +#include "hsTemplates.h" + +class plMaxNode; +class plErrorMsg; +class hsConverterUtils; +class plMaxNode; +class plGeometrySpan; +struct hsPoint3; +struct hsVector3; +struct hsMatrix44; +class ISkin; +class plConvexVolume; +class plMAXVertNormal; +class BitmapTex; +class hsGMaterial; +class plExportMaterialData; + + + +//// Class Definition //////////////////////////////////////////////////////// + +class plMeshConverter +{ +private: + plMeshConverter(); + + static hsBool fWarnBadNormals; + static char fWarnBadNormalsMsg[]; + static hsBool fWarnBadUVs; + static char fWarnBadUVsMsg[]; + static hsBool fWarnSuspiciousUVs; + static char fWarnSuspiciousUVsMsg[]; + static char fTooManyVertsMsg[]; + static char fTooManyFacesMsg[]; + +public: + ~plMeshConverter(); + static plMeshConverter& Instance(); + + void Init( hsBool save, plErrorMsg *msg ); + void DeInit( hsBool deInitLongRecur = true ); + + void StuffPositionsAndNormals(plMaxNode *node, hsTArray *pos, hsTArray *normals); + plConvexVolume *CreateConvexVolume( plMaxNode *node ); + // doPreshading - If true, do crappy flat shading now (since we won't do any shading later) + hsBool CreateSpans( plMaxNode *node, hsTArray &spanArray, bool doPreshading ); + +private: + bool IValidateUVs(plMaxNode* node); + + void ISetBumpUvs(Int16 uvChan, hsTArray& vertDPosDuvCache, TVFace* tvFace, UInt32 smGroup, + hsPoint3* uvs1, hsPoint3* uvs2, hsPoint3* uvs3); + void ISetBumpUvSrcs(hsTArray *>& ourMaterials, + hsTArray& bumpLayIdx, hsTArray& bumpLayChan, hsTArray& bumpDuChan, hsTArray& bumpDvChan); + void ISetWaterDecEnvUvSrcs(hsTArray *>& ourMaterials, + hsTArray& bumpLayIdx, hsTArray& bumpLayChan, hsTArray& bumpDuChan, hsTArray& bumpDvChan); + void ISmoothUVGradients(plMaxNode* node, Mesh* mesh, + hsTArray *>& ourMaterials, + hsTArray& bumpLayIdx, hsTArray& bumpLayChan, + hsTArray* vertDPosDuCache, hsTArray* vertDPosDvCache); + Point3 IGetUvGradient(plMaxNode* node, const hsMatrix44& uvXform44, Int16 bmpUvwSrc, + Mesh *mesh, int faceIdx, + int iUV); + + int IGenerateUVs( plMaxNode *node, Mtl *maxMtl, Mesh *mesh, int faceIdx, int numChan, int numBlend, + hsPoint3 *uvs1, hsPoint3 *uvs2, hsPoint3 *uvs3 ); + void IGetUVTransform( plMaxNode *node, Mtl *mtl, Matrix3 *uvTransform, int which ); + + UInt32 ICreateHexColor( float r, float g, float b ); + UInt32 ISetHexAlpha( UInt32 color, float alpha ); + + Mesh* IGetNodeMesh(plMaxNode *node); + void IDeleteTempGeometry(); + Mesh* IDuplicate2Sided(plMaxNode *node, Mesh* mesh); + + Interface *fInterface; + hsConverterUtils& fConverterUtils; + plErrorMsg *fErrorMsg; + hsBool fIsInitialized; + + // Non-nil if we converted the MAX object and have to delete it when we're done + TriObject *fTriObjToDelete; + // Non-nil if we made a copy to mess with that we need to delete when we're done + Mesh *fMeshToDelete; +}; + +#endif // _plMeshConverter_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderGlobalContext.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderGlobalContext.cpp new file mode 100644 index 00000000..20095592 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderGlobalContext.cpp @@ -0,0 +1,205 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "HeadSpin.h" +#include "Max.h" + +#include "../MaxMain/plMaxNode.h" +#include "plRenderGlobalContext.h" + +plRenderGlobalContext::plRenderGlobalContext(Interface* ip, TimeValue t) +{ + fInterface = ip; + + renderer = ip->GetProductionRenderer(); + projType = PROJ_PERSPECTIVE; + devWidth = 640; + devHeight = 480; + xscale = 1.6f; + yscale = -1.6f; + xc = 320.f; + yc = 240.f; + antialias = 1; + camToWorld.IdentityMatrix(); + worldToCam.IdentityMatrix(); + nearRange = 0; + farRange = 10000.f; + devAspect = 1.f; // PIXEL aspect ratio of device pixel H/W + frameDur = 1.f; + envMap = nil; + globalLightLevel.White(); + atmos = nil; + pToneOp = nil; // The tone operator, may be NULL + time = t; + wireMode = false; // wire frame render mode? + wire_thick = 1.f; // global wire thickness + force2Side = false; // is force two-sided rendering enabled + inMtlEdit = false; // rendering in mtl editor? + fieldRender = false; // are we rendering fields + first_field = 1; // is this the first field or the second? + field_order = 1; // which field is first: 0->even first, 1->odd first + + objMotBlur = true; // is object motion blur enabled + nBlurFrames = 10; // number of object motion blur time slices + + SetRenderElementMgr(ip->GetRenderElementMgr(RS_Production)); + +} + +plRenderGlobalContext::~plRenderGlobalContext() +{ + int i; + for( i = 0; i < fInstList.GetCount(); i++ ) + { + fInstList[i].Cleanup(); + } +} + +void plRenderGlobalContext::Update(TimeValue t) +{ + time = t; + + int i; + for( i = 0; i < fInstList.GetCount(); i++ ) + fInstList[i].Update(time); +} + +void plRenderGlobalContext::MakeRenderInstances(plMaxNode* root, TimeValue t) +{ + time = t; + int i; + for( i = 0; i < root->NumberOfChildren(); i++ ) + IMakeRenderInstances((plMaxNode*)root->GetChildNode(i), t, false); + + for( i = 0; i < fInstList.GetCount() - 1; i++ ) + fInstList[i].SetNext(&fInstList[i+1]); +} + +void plRenderGlobalContext::IMakeRenderInstances(plMaxNode* node, TimeValue t, hsBool isBarney) +{ + const char* dbgNodeName = node->GetName(); + if( !isBarney ) + isBarney = node->GetIsBarney(); + + hsBool doMe = isBarney || (node->CanConvert() && node->GetDrawable()); + + if( !doMe ) + return; + + int idx = fInstList.GetCount(); + + plRenderInstance* inst = fInstList.Push(); + + if( !inst->GetFromNode(node, t, idx) ) + fInstList.Pop(); + + int i; + for( i = 0; i < node->NumberOfChildren(); i++ ) + IMakeRenderInstances((plMaxNode *)node->GetChildNode(i), t, isBarney); +} + +void plRenderGlobalContext::IntersectRay(RenderInstance *inst, Ray& origRay, ISect &isct, ISectList &xplist, BOOL findExit) +{ + const float kFarAway = 1.e5f; + Ray ray; + if( findExit ) + { + ray.p = origRay.p + origRay.dir * kFarAway; + ray.dir = -ray.dir; + } + else + { + ray = origRay; + } + float at; + Point3 norm; + DWORD faceIdx; + Point3 bary; + int hit = inst->mesh->IntersectRay(ray, at, norm, faceIdx, bary); + if( hit ) + { + ISect thisHit; + thisHit.t = findExit ? kFarAway - at : at; + thisHit.exit = findExit; + thisHit.backFace = findExit; + thisHit.inst = inst; + thisHit.fnum = faceIdx; + thisHit.bc = bary; + Point3 worldP = (ray.p + at * ray.dir); + thisHit.p = inst->GetINode()->GetObjectTM(time) * worldP; + thisHit.pc = worldToCam * thisHit.p; + + thisHit.mtlNum = inst->mesh->faces[faceIdx].getMatID(); + Mtl* mtl = inst->GetINode()->GetMtl(); + thisHit.matreq = mtl ? mtl->Requirements(thisHit.mtlNum) : 0; + + thisHit.next = nil; + + if( thisHit.matreq & (MTLREQ_TRANSP | MTLREQ_ADDITIVE_TRANSP) ) + { + // advance the ray and try again. This one goes in the xplist. + ISect* xHit = GetNewISect(); + *xHit = thisHit; + xplist.Add(xHit); + + const float kAdvanceHack = 0.5f; + Ray newRay; + newRay.p = origRay.p + origRay.dir * (thisHit.t + kAdvanceHack); + newRay.dir = origRay.dir; + IntersectRay(inst, newRay, isct, xplist, findExit); + } + else + { + xplist.Prune(thisHit.t); + isct = thisHit; + } + } +} + +BOOL plRenderGlobalContext::IntersectWorld(Ray &ray, int skipID, ISect &hit, ISectList &xplist, int blurFrame) +{ + hit.t = -1.f; + xplist.Init(); + int i; + for( i = 0; i < fInstList.GetCount(); i++ ) + { + if( skipID != i ) + { + ISect thisHit; + hit.t = -1.f; + IntersectRay(&fInstList[i], ray, thisHit, xplist, false); + if( thisHit.t >= 0 ) + { + if( (hit.t < 0) || (thisHit.t < hit.t) ) + { + // grab our new winner. + hit = thisHit; + } + } + } + } + return (hit.t >= 0) || !xplist.IsEmpty(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderGlobalContext.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderGlobalContext.h new file mode 100644 index 00000000..8b919d76 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderGlobalContext.h @@ -0,0 +1,57 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRenderGlobalContext_inc +#define plRenderGlobalContext_inc + +#include "hsTemplates.h" +#include "plRenderInstance.h" + +class plRenderGlobalContext : public RenderGlobalContext +{ +protected: + Interface* fInterface; + + hsTArray fInstList; + + void IMakeRenderInstances(plMaxNode* node, TimeValue t, hsBool isBarney); + +public: + plRenderGlobalContext(Interface* ip, TimeValue t); + ~plRenderGlobalContext(); + + void Update(TimeValue t); + + void MakeRenderInstances(plMaxNode* root, TimeValue t); + + virtual int NumRenderInstances() { return fInstList.GetCount(); } + virtual RenderInstance* GetRenderInstance( int i ) { return i < fInstList.GetCount() ? &fInstList[i] : nil; } + + virtual void IntersectRay(RenderInstance *inst, Ray& ray, ISect &isct, ISectList &xpList, BOOL findExit); + virtual BOOL IntersectWorld(Ray &ray, int skipID, ISect &hit, ISectList &xplist, int blurFrame = NO_MOTBLUR); +}; + +#endif // plRenderGlobalContext_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderInstance.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderInstance.cpp new file mode 100644 index 00000000..6ffbf51b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderInstance.cpp @@ -0,0 +1,300 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "hsTypes.h" +#include "Max.h" + +#include "plRenderInstance.h" + +class plNilView : public View +{ + +public: + + Point2 ViewToScreen(Point3 p) { return Point2(p.x,p.y); } + + plNilView() + { + projType = 1; + fov = hsScalarPI * 0.25f; + pixelSize = 1.f; + affineTM.IdentityMatrix(); + worldToView.IdentityMatrix(); + screenW=640.0f; screenH = 480.0f; + } + +}; + +static plNilView nilView; + +plRenderInstance::plRenderInstance() +: fNext(nil), + fNode(nil), + fObject(nil), + fDeleteMesh(false) +{ + mtl = nil; + mesh = nil; + flags = 0; + wireSize = 1.f; + vis = 1.f; + nodeID = 0; + objMotBlurFrame = 0; + objBlurID = 0; + objToWorld.IdentityMatrix(); + objToCam.IdentityMatrix(); + normalObjToCam.IdentityMatrix(); + camToObj.IdentityMatrix(); + obBox.Init(); + center = Point3(0,0,0); + radsq = 0; +} + +plRenderInstance::~plRenderInstance() +{ +} + +void plRenderInstance::Cleanup() +{ + if( mesh && fDeleteMesh ) + { + mesh->DeleteThis(); + mesh = nil; + fDeleteMesh = false; + } +} + +BOOL plRenderInstance::Update(TimeValue& t) +{ + fObject = fNode->EvalWorldState(t).obj; + if( !fObject ) + return false; + + // this shouldn't happen, we shouldn't be trying to make + // renderinstances from non GEOMOBJECT's + if( fObject->SuperClassID() != GEOMOBJECT_CLASS_ID ) + return false; + + if( mesh && fDeleteMesh ) + { + mesh->DeleteThis(); + mesh = nil; + } + fDeleteMesh = false; + mesh = ((GeomObject*)fObject)->GetRenderMesh(t, fNode, nilView, fDeleteMesh); + if( !mesh ) + return false; + + vis = fNode->GetVisibility(t); + if( vis < 0.0f ) + { + vis = 0.0f; + SetFlag(INST_HIDE, 1); + return false; + } + if (vis > 1.0f) vis = 1.0f; + SetFlag(INST_HIDE, 0); + + objMotBlurFrame = NO_MOTBLUR; + objBlurID = 0; + + objToWorld = fNode->GetObjectTM(t); + objToCam = objToWorld; + camToObj = Inverse(objToCam); + + normalObjToCam.IdentityMatrix(); + Matrix3 inv = camToObj; + int i; + for( i = 0; i < 3; i++ ) + normalObjToCam.SetRow(i, inv.GetColumn3(i)); + + obBox = mesh->getBoundingBox(nil); + center = obBox.Center(); + radsq = LengthSquared(obBox.Width()); + + return true; +} + +BOOL plRenderInstance::GetFromNode(INode* node, TimeValue& t, int idx) +{ + fNext = nil; + + fValid = Interval(t, t); + + fNode = node; + mtl = node->GetMtl(); + if( mtl ) + { + wireSize = mtl->WireSize(); + } + + nodeID = idx; + + ClearLights(); + + return Update(t); +} + +BOOL plRenderInstance::CastsShadowsFrom(const ObjLightDesc& constLt) +{ + ObjLightDesc& lt = const_cast(constLt); + if( !fNode->CastShadows() ) + return false; + + if( !lt.ls.shadow ) + return false; + + if( lt.GetExclList() && lt.GetExclList()->TestFlag(NT_AFFECT_SHADOWCAST) ) + { + int idx = lt.GetExclList()->FindNode(fNode); + BOOL isInc = lt.GetExclList()->TestFlag(NT_INCLUDE); + + if( idx >= 0 ) + { + return isInc; + } + else + { + return !isInc; + } + } + + return true; +} + +Point3 plRenderInstance::GetFaceNormal(int fnum) +{ + Face* f = &mesh->faces[fnum]; + Point3 a = GetCamVert(f->v[1]) - GetCamVert(f->v[0]); + Point3 b = GetCamVert(f->v[2]) - GetCamVert(f->v[0]); + + Point3 n = CrossProd(a, b).Normalize(); + return n; +} + +Point3 plRenderInstance::GetFaceVertNormal(int fnum, int vertNum) +{ + Point3 retNorm; + + int smGroup=0; + + Face* f = &mesh->faces[fnum]; + + // Get the rendered vertex. Don't use the device position, fPos. + RVertex &rv = mesh->getRVert(f->v[vertNum]); + + // Number of normals at the vertex + int numNormalsAtVert = (rv.rFlags & NORCT_MASK); + + // Specified normal ? + int specNrml = (rv.rFlags & SPECIFIED_NORMAL); + + if (specNrml || (numNormalsAtVert == 1)) + { + // The normal case is one normal per vertex (a vertex not beng shared by more than one smoothing group) + // We'll assign vertex normals here. + // If the object is faceted, this will be the same as the face normal. + retNorm = rv.rn.getNormal(); + } + else + { + int found = 0; + for(int j=0;jgetSmGroup(); + if ((smGroup & faceSmGroup) == faceSmGroup) + { + retNorm = rv.ern[j].getNormal(); + found++; + + // NOTE: Remove this to really check smoothing groups + break; + } + } + } + retNorm = retNorm * normalObjToCam; + retNorm.Normalize(); + return retNorm; +} + +Point3 plRenderInstance::GetCamVert(int vertnum) +{ + return objToCam*mesh->verts[vertnum]; +} + +void plRenderInstance::GetObjVerts(int fnum, Point3 obp[3]) +{ + Face* f = &mesh->faces[fnum]; + obp[0] = mesh->verts[f->v[0]]; + obp[1] = mesh->verts[f->v[1]]; + obp[2] = mesh->verts[f->v[2]]; +} + +void plRenderInstance::GetCamVerts(int fnum, Point3 cp[3]) +{ + Face* f = &mesh->faces[fnum]; + cp[0] = objToCam*mesh->verts[f->v[0]]; + cp[1] = objToCam*mesh->verts[f->v[1]]; + cp[2] = objToCam*mesh->verts[f->v[2]]; +} + +Mtl* plRenderInstance::GetMtl(int fnum) +{ + if( !mtl ) + return nil; + + if( TestFlag(INST_MTL_BYFACE) ) + { + if( mtl->ClassID() != Class_ID(MULTI_CLASS_ID,0) ) + return mtl; + + Face* f = &mesh->faces[fnum]; + int matIndex = f->getMatID(); + return mtl->GetSubMtl(matIndex); + + } + else + { + return mtl; + } +} + +ULONG plRenderInstance::MtlRequirements(int mtlNum, int faceNum) +{ + if( !mtl ) + return 0; + + if( TestFlag(INST_MTL_BYFACE) ) + { + Mtl* faceMtl = GetMtl(faceNum); + return faceMtl ? faceMtl->Requirements(mtlNum) : 0; + } + return mtl->Requirements(mtlNum); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderInstance.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderInstance.h new file mode 100644 index 00000000..f7397827 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxConvert/plRenderInstance.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plRenderInstance_inc +#define plRenderInstance_inc + +#include "hsTemplates.h" + +class plRenderInstance : public RenderInstance +{ +protected: + plRenderInstance* fNext; + Interval fValid; + + INode* fNode; + Object* fObject; + + BOOL fDeleteMesh; + + hsTArray fLights; +public: + plRenderInstance(); + virtual ~plRenderInstance(); + + BOOL GetFromNode(INode* node, TimeValue& t, int idx); + BOOL Update(TimeValue& t); + void SetNext(plRenderInstance* n) { fNext = n; } + void Cleanup(); + + virtual RenderInstance *Next() { return fNext; } // next in list + + virtual Interval MeshValidity() { return fValid; } + + virtual int NumLights() { return fLights.GetCount(); } + virtual LightDesc *Light(int n) { return fLights[n]; } + virtual void AddLight(LightDesc* l) { fLights.Append(l); } + virtual void ClearLights() { fLights.SetCount(0); } + + virtual BOOL CastsShadowsFrom(const ObjLightDesc& lt); // is lt shadowed by this instance? + + virtual INode *GetINode() { return fNode; } // get INode for instance + virtual Object *GetEvalObject() { return fObject; } // evaluated object for instance + + virtual Point3 GetFaceNormal(int faceNum); // geometric normal in camera coords + virtual Point3 GetFaceVertNormal(int faceNum, int vertNum); // camera coords + virtual void GetFaceVertNormals(int faceNum, Point3 n[3]) // camera coords + { + n[0] = GetFaceVertNormal(faceNum, 0); + n[1] = GetFaceVertNormal(faceNum, 1); + n[2] = GetFaceVertNormal(faceNum, 2); + } + virtual Point3 GetCamVert(int vertnum); // coord for vertex in camera coords + virtual void GetObjVerts(int fnum, Point3 obp[3]); // vertices of face in object coords + virtual void GetCamVerts(int fnum, Point3 cp[3]); // vertices of face in camera(view) coords + + // Material-by-face access + // Objects can provide a material as a function of face number via the IChkMtlAPI interface (chkmtlapi.h). + // This method will return RenderInstance::mtl if flag INST_MTL_BYFACE is not set. If INST_MTL_BYFACE is + // set it will return the proper by-face mtl. // DS 4/3/00 + virtual Mtl *GetMtl(int faceNum); + virtual ULONG MtlRequirements(int mtlNum, int faceNum); // node's mtl requirements. DS 3/31/00: added faceNum to support mtl-per-face objects +}; + +#endif // plRenderInstance_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/SimpleExport.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/SimpleExport.cpp new file mode 100644 index 00000000..2286ac0d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/SimpleExport.cpp @@ -0,0 +1,393 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// +// 3DSMax HeadSpin exporter +// + +#include "HeadSpin.h" +#include "SimpleExport.h" +#include "notify.h" + +#include "plExportErrorMsg.h" +#include "plExportLogErrorMsg.h" + +#include "../MaxConvert/UserPropMgr.h" +#include "hsExceptionStack.h" +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/plBitmapCreator.h" +#include "../pfPython/plPythonFileMod.h" + +#include "../MaxMain/plPluginResManager.h" +#include "../plResMgr/plRegistryHelpers.h" +#include "../plResMgr/plRegistryNode.h" +#include "hsStream.h" +#include "../MaxConvert/plConvert.h" +#include "../MaxConvert/hsMaterialConverter.h" + +#include "../plPhysX/plSimulationMgr.h" +#include "../plSDL/plSDL.h" +#include "../MaxMain/plMaxCFGFile.h" + +// For texture export/cleanup +#include "../MaxMain/plTextureExportLog.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plUoid.h" +#include "../plGImage/plCubicEnvironmap.h" +#include "../plGImage/plDynamicTextMap.h" +#include "../plGImage/plMipmap.h" +#include "../plScene/plSceneNode.h" + +#include "plExportDlg.h" + +#include "../plStatusLog/plStatusLog.h" +#include "../plFile/plFileUtils.h" + +#include "../plAvatar/plAvatarMgr.h" + +extern UserPropMgr gUserPropMgr; + +#ifdef HS_DEBUGGING +#define HS_NO_TRY +#endif + +// +// .MSH export module functions follow: +// + +HSExport2::HSExport2() +{ +} + +HSExport2::~HSExport2() +{ +} + +int HSExport2::ExtCount() +{ + return 2; +} + +// +// Extensions supported for import/export modules +// +const TCHAR *HSExport2::Ext(int n) +{ +static char str[64]; + switch(n) + { + case 0: + return ""; + case 1: + return "prd"; + } + return _T(""); +} + +// +// Long ASCII description (i.e. "Targa 2.0 Image File") +// +const TCHAR *HSExport2::LongDesc() +{ + return "Plasma 2.0"; +} + +// +// Short ASCII description (i.e. "Targa") +// +const TCHAR *HSExport2::ShortDesc() +{ +#ifdef HS_DEBUGGING + return "Plasma 2.0 Debug"; +#else + return "Plasma 2.0"; +#endif +} + +// +// ASCII Author name +// +const TCHAR *HSExport2::AuthorName() +{ + return "Billy Bob"; +} + +// +// ASCII Copyright message +// +const TCHAR *HSExport2::CopyrightMessage() +{ + return "Copyright 1997 HeadSpin Technology Inc."; +} + +// +// Other message #1 +// +const TCHAR *HSExport2::OtherMessage1() +{ + return _T(""); +} + +// +// Other message #2 +// +const TCHAR *HSExport2::OtherMessage2() +{ + return _T(""); +} + +// +// Version number * 100 (i.e. v3.01 = 301) +// +unsigned int HSExport2::Version() +{ + return 100; +} + +// +// Optional +// +void HSExport2::ShowAbout(HWND hWnd) +{ +} + +void IGetPath(const char* name, char* path) +{ + int i; + // find the last backslash in the full path + for ( i=strlen(name)-1; i>=0 ; i-- ) + { + if ( name[i] == '\\' ) + break; + } + if ( i >= 0 && i < 256) // if either we couldn't the backslash or the path was too big + { + strncpy(path,name,i+1); + path[i+1] = '\0'; //null terminate string (cause strncpy might not) + } + else + path[0] = '\0'; // otherwise just make it a null string +} + +// Another little helper class to help write out a list of textures to a log file +class plTextureLoggerCBack : public plRegistryKeyIterator +{ +protected: + plTextureExportLog* fTELog; + +public: + plTextureLoggerCBack(plTextureExportLog* teLog) { fTELog = teLog; } + + virtual hsBool EatKey(const plKey& key) + { + plBitmap* bmap = plBitmap::ConvertNoRef(key->ObjectIsLoaded()); + if (bmap != nil) + fTELog->AddTexture(bmap); + return true; // Always continue + } +}; + +// Yet another key iterator, this one to call OptimizeDrawables() on each sceneNode +class plOptimizeIterator : public plRegistryKeyIterator +{ +public: + virtual hsBool EatKey(const plKey& key) + { + if (key->GetUoid().GetClassType() == plSceneNode::Index()) + { + plSceneNode* sn = plSceneNode::ConvertNoRef(key->ObjectIsLoaded()); + if (sn != nil) + sn->OptimizeDrawables(); + } + return true; // Always continue + } +}; + +// +// +// +int HSExport2::DoExport(const TCHAR *name,ExpInterface *ei,Interface *gi, BOOL suppressPrompts, DWORD options) +{ + BOOL backupEnabled = gi->AutoBackupEnabled(); + gi->EnableAutoBackup(FALSE); + + BOOL bmmSilentMode = TheManager->SilentMode(); + TheManager->SetSilentMode(TRUE); + + bool mbSuppressPrompts = hsMessageBox_SuppressPrompts; + hsMessageBox_SuppressPrompts = (suppressPrompts)?true:false; + + // Disable save so we don't crash in export or + // otherwise screw database. + SimpleExportExitCallback exitCB; + gi->RegisterExitMAXCallback(&exitCB); + + gUserPropMgr.OpenQuickTable(); + hsConverterUtils::Instance().CreateNodeSearchCache(); + + BroadcastNotification(NOTIFY_PRE_EXPORT); + + // get just the path (not the file) of where we are going to export to + char out_path[256]; + IGetPath(name, out_path); + // Apparently this was implied by the open dialog, but not if you call Max's ExportToFile() func + SetCurrentDirectory(out_path); + + // + // Setup ErrorMsg + // + // Needs to be outside try/catch so it doesn't stack unwind... + plExportErrorMsg hituser_errorMessage; // This is the errorMessage that slaps user + + TSTR filename = gi->GetCurFileName(); + hsStrncpy(fName, filename, 128); + char *dot = strrchr(fName, '.'); + if (dot) + *dot = 0; + char ErrorLogName[512]; + sprintf(ErrorLogName, "%s%s.err", out_path, fName); + plExportLogErrorMsg logonly_errorMessage(ErrorLogName); // This errorMessage just writes it all to a file + + // now decide which errorMessage object to use + plErrorMsg* errorMessage; + if (suppressPrompts) + errorMessage = &logonly_errorMessage; + else + errorMessage = &hituser_errorMessage; + + // For export time stats + DWORD exportTime = timeGetTime(); + _SYSTEMTIME tm; + GetSystemTime(&tm); + + // + // Let's get cracking! Convert the scene... + // + plConvertSettings settings; + + if (plExportDlg::Instance().IsExporting()) + { + settings.fDoPreshade = plExportDlg::Instance().GetDoPreshade(); + settings.fPhysicalsOnly = plExportDlg::Instance().GetPhysicalsOnly(); + settings.fDoLightMap = plExportDlg::Instance().GetDoLightMap(); + settings.fExportPage = plExportDlg::Instance().GetExportPage(); + } + + plConvert::Instance().Init(gi, errorMessage, &settings); + + // We want to incorporate any SDL changes since the last export, so we DeInit() + // and re-initialize. + char buf[MAX_PATH]; + strcpy(buf, plMaxConfig::GetClientPath()); + strcat(buf, "sdl"); + plSDLMgr::GetInstance()->SetSDLDir(buf); + plSDLMgr::GetInstance()->DeInit(); + plSDLMgr::GetInstance()->Init(); + + // Add disk source for writing + char datPath[MAX_PATH]; + strcpy(datPath, out_path); + plFileUtils::AddSlash(datPath); + strcat(datPath, "dat\\"); + CreateDirectory(datPath, NULL); + plPluginResManager::ResMgr()->SetDataPath(datPath); + + if (hsgResMgr::Reset()) + { + plSimulationMgr::Init(); + plAvatarMgr::GetInstance(); + + // Verify the pages here manually, since it's a separate step now + plPluginResManager::ResMgr()->VerifyPages(); + + plPythonFileMod::SetAtConvertTime(); + + // Convert!!! + hsBool convertOK = plConvert::Instance().Convert(); + + // Free the material cache. This will delete unused materials. + hsMaterialConverter::Instance().FreeMaterialCache(out_path); + + if (convertOK) + { + // Optimize the drawables + plOptimizeIterator optIterator; + plPluginResManager::ResMgr()->IterateKeys( &optIterator ); + + // And save. + plPluginResManager::ResMgr()->WriteAllPages(); + + // Write out a texture log file + char textureLog[MAX_PATH]; + sprintf(textureLog, "log\\exportedTextures_%s.log", fName); + plTextureExportLog textureExportLog( textureLog ); + plTextureLoggerCBack loggerCallback( &textureExportLog ); + + plPluginResManager::ResMgr()->IterateKeys( &loggerCallback ); + + textureExportLog.Write(); + + // Moving this to the end of writing the files out. Yes, this means that any unused mipmaps still get + // written to disk, including ones loaded on preload, but it's the only way to get shared texture pages + // to work without loading in the entire age worth of reffing objects. - 5.30.2002 mcn + plBitmapCreator::Instance().DeInit(); + } + + // Have the resMgr clean up after export. This includes paging out any converted pages + plPluginResManager::ResMgr()->EndExport(); + + plSimulationMgr::Shutdown(); + plAvatarMgr::ShutDown(); + + // Reset the resmgr so we free all the memory it allocated + hsgResMgr::Reset(); + } + + + //---------------------------------------------- + // Write a log entry to the Db file name for now + //---------------------------------------------- + hsUNIXStream dbLog; + dbLog.Open(name,"at"); + char str[256]; + exportTime = (timeGetTime() - exportTime) / 1000; + sprintf(str,"Export from Max File \"%s\" on %02d/%02d/%4d took %d:%02d\n",filename,tm.wMonth,tm.wDay,tm.wYear, exportTime/60, exportTime%60); + dbLog.WriteString(str); + dbLog.Close(); + + // Allow plugins to clean up after export + BroadcastNotification(NOTIFY_POST_EXPORT); + + hsConverterUtils::Instance().DestroyNodeSearchCache(); + gUserPropMgr.CloseQuickTable(); + gi->UnRegisterExitMAXCallback(&exitCB); + hsMessageBox_SuppressPrompts = mbSuppressPrompts; + TheManager->SetSilentMode(bmmSilentMode); + gi->EnableAutoBackup(backupEnabled); + + MessageBeep(MB_ICONASTERISK); + + return 1; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/SimpleExport.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/SimpleExport.h new file mode 100644 index 00000000..611575a8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/SimpleExport.h @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __SIMPLEEXPORT_H +#define __SIMPLEEXPORT_H + +#include "hsWindows.h" +#include "../MaxMain/resource.h" +#include "Max.h" +#include "HeadSpin.h" + +// +// Inlines +// +extern HINSTANCE hInstance; +extern TCHAR *GetString(int id); + +//////////////////////////////////////////////////////// +//////////////////////////////////////////////////////// +// +// Header file for headSpin 3dsMax exporter +// + +class HSExport2 : public SceneExport +{ +public: + HSExport2(); + ~HSExport2(); + int ExtCount(); // Number of extensions supported + const TCHAR * Ext(int n); // Extension #n (i.e. "HS") + const TCHAR * LongDesc(); // Long ASCII description (i.e. "Autodesk 3D Studio File") + const TCHAR * ShortDesc(); // Short ASCII description (i.e. "3D Studio") + const TCHAR * AuthorName(); // ASCII Author name + const TCHAR * CopyrightMessage(); // ASCII Copyright message + const TCHAR * OtherMessage1(); // Other message #1 + const TCHAR * OtherMessage2(); // Other message #2 + unsigned int Version(); // Version number * 100 (i.e. v3.01 = 301) + void ShowAbout(HWND hWnd); // Show DLL's "About..." box + virtual int DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE, DWORD options=0); + + const char* GetName() { return fName; } + +private: + static hsBool IProgressCallback(hsScalar percent); + static DWORD WINAPI IProgressDummyFunc(LPVOID arg); + + char fName[128]; +}; + +//------------------------------------------------------ + +class HSClassDesc2 : public ClassDesc +{ +public: + int IsPublic() { return 1; } + void * Create(BOOL loading = FALSE) { return TRACKED_NEW HSExport2; } + const TCHAR * ClassName() { return "Plasma 2.0 Scene Exporter"; } + SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } +#ifdef HS_DEBUGGING + Class_ID ClassID() { return Class_ID(0x547962c7, 0x520a702d); } +#else + Class_ID ClassID() { return Class_ID(0x717f791f, 0x79412447); } +#endif + const TCHAR* Category() { return "Plasma Export..."; } +}; + + + +class SimpleExportExitCallback : public ExitMAXCallback +{ +public: + BOOL Exit(HWND hWnd) { return false; } +}; + +// +// Externs +// +extern int controlsInit; +extern HSClassDesc2 HSDesc; + +// +// Defines +// +#define no_RAM() Alert(IDS_TH_OUTOFMEMORY) + +#endif // __SIMPLEEXPORT_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plErrorMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plErrorMsg.cpp new file mode 100644 index 00000000..cf423fa5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plErrorMsg.cpp @@ -0,0 +1,209 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plErrorMsg.h" +#include "hsTypes.h" +#include "hsExceptions.h" +#include "hsUtils.h" + +#include "plErrorMsg.h" + +static plErrorMsg nullMsg; + + +////////////////////////////////////////////////////////////////////////////////////////////////// +// Useful null object, to assign when I don't want to worry about checking for nil everywhere... +////////////////////////////////////////////////////////////////////////////////////////////////// +plErrorMsg *plErrorMsg::GetNull() +{ + return &nullMsg; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// Non-destructive error handling +////////////////////////////////////////////////////////////////////////////////////////////////// +plErrorMsg::plErrorMsg(const char* label, const char* msg) +{ + Set(label, msg); +} + +plErrorMsg::plErrorMsg(hsBool bogus) +{ + Set(bogus); +} + +plErrorMsg::plErrorMsg(hsBool bogus, const char* label, const char* msg) +{ + Set(bogus, label, msg); +} + +plErrorMsg::plErrorMsg(hsBool bogus, const char* label, const char* format, const char* str) +{ + Set(bogus, label, format, str); +} + +plErrorMsg::plErrorMsg(hsBool bogus, const char* label, const char* format, const char* str1, const char* str2) +{ + Set(bogus, label, format, str1, str2); +} + +plErrorMsg::plErrorMsg(hsBool bogus, const char* label, const char* format, int n) +{ + Set(bogus, label, format, n); +} + +plErrorMsg::plErrorMsg(hsBool bogus, const char* label, const char* format, int n, int m) +{ + Set(bogus, label, format, n, m); +} + +plErrorMsg::plErrorMsg(hsBool bogus, const char* label, const char* format, float f) +{ + Set(bogus, label, format, f); +} + +plErrorMsg &plErrorMsg::Set(const char* label, const char* msg) +{ + fBogus = true; + if( label ) + hsStrncpy(fLabel, label, 256); + else + *fLabel = 0; + if( msg ) + { + hsAssert(strlen(msg) < PL_ERR_MSG_MAX_MSG, "Too long of an error message for plErrorMsg."); + hsStrncpy(fMsg, msg, PL_ERR_MSG_MAX_MSG); + } + else + *fMsg = 0; + hsThrow( *this ); + + return *this; +} + +plErrorMsg &plErrorMsg::Set(hsBool bogus) +{ + fBogus = bogus; + fLabel[0] = 0; + fMsg[0] = 0; + + return *this; +} + +plErrorMsg &plErrorMsg::Set(hsBool bogus, const char* label, const char* msg) +{ + if( fBogus = bogus ) + { + if( label ) + hsStrncpy(fLabel, label, 256); + else + *fLabel = 0; + if( msg ) + { + hsAssert(strlen(msg) < PL_ERR_MSG_MAX_MSG, "Too long of an error message for plErrorMsg."); + hsStrncpy(fMsg, msg, PL_ERR_MSG_MAX_MSG); + } + else + *fMsg = 0; + } + + return *this; +} + +plErrorMsg &plErrorMsg::Set(hsBool bogus, const char* label, const char* format, const char* str) +{ + if( fBogus = bogus ) + { + if( label ) + hsStrncpy(fLabel, label, 256); + else + *fLabel = 0; + int length = sprintf(fMsg, format, str); + hsAssert(length < PL_ERR_MSG_MAX_MSG, "Too long of an error message for plErrorMsg."); + } + + return *this; +} + +plErrorMsg &plErrorMsg::Set(hsBool bogus, const char* label, const char* format, const char* str1, const char* str2) +{ + if( fBogus = bogus ) + { + if( label ) + hsStrncpy(fLabel, label, 256); + else + *fLabel = 0; + int length = sprintf(fMsg, format, str1, str2); + hsAssert(length < PL_ERR_MSG_MAX_MSG, "Too long of an error message for plErrorMsg."); + } + + return *this; +} + +plErrorMsg &plErrorMsg::Set(hsBool bogus, const char* label, const char* format, int n) +{ + if( fBogus = bogus ) + { + if( label ) + hsStrncpy(fLabel, label, 256); + else + *fLabel = 0; + int length = sprintf(fMsg, format, n); + hsAssert(length < PL_ERR_MSG_MAX_MSG, "Too long of an error message for plErrorMsg."); + } + + return *this; +} + +plErrorMsg &plErrorMsg::Set(hsBool bogus, const char* label, const char* format, int n, int m) +{ + if( fBogus = bogus ) + { + if( label ) + hsStrncpy(fLabel, label, 256); + else + *fLabel = 0; + int length = sprintf(fMsg, format, n, m); + hsAssert(length < PL_ERR_MSG_MAX_MSG, "Too long of an error message for plErrorMsg."); + } + + return *this; +} + +plErrorMsg &plErrorMsg::Set(hsBool bogus, const char* label, const char* format, float f) +{ + if( fBogus = bogus ) + { + if( label ) + hsStrncpy(fLabel, label, 256); + else + *fLabel = 0; + int length = sprintf(fMsg, format, f); + hsAssert(length < PL_ERR_MSG_MAX_MSG, "Too long of an error message for plErrorMsg."); + } + + return *this; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plErrorMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plErrorMsg.h new file mode 100644 index 00000000..577dd005 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plErrorMsg.h @@ -0,0 +1,97 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __plErrorMsg_h +#define __plErrorMsg_h + +#include "hsTypes.h" + +#define PL_ERR_MSG_MAX_MSG 2048 + +class plErrorMsg +{ +public: + static plErrorMsg *GetNull(); + + plErrorMsg(const char* label, const char* msg); + plErrorMsg(hsBool bogus = false); + plErrorMsg(hsBool bogus, const char* label, const char* msg); + plErrorMsg(hsBool bogus, const char* label, const char* format, const char* str); + plErrorMsg(hsBool bogus, const char* label, const char* format, const char* str1, const char* str2); + plErrorMsg(hsBool bogus, const char* label, const char* format, int n); + plErrorMsg(hsBool bogus, const char* label, const char* format, int n, int m); + plErrorMsg(hsBool bogus, const char* label, const char* format, float f); + virtual ~plErrorMsg() { } + + plErrorMsg &Set(const char* label, const char* msg); + plErrorMsg &Set(hsBool bogus = false); + plErrorMsg &Set(hsBool bogus, const char* label, const char* msg); + plErrorMsg &Set(hsBool bogus, const char* label, const char* format, const char* str); + plErrorMsg &Set(hsBool bogus, const char* label, const char* format, const char* str1, const char* str2); + plErrorMsg &Set(hsBool bogus, const char* label, const char* format, int n); + plErrorMsg &Set(hsBool bogus, const char* label, const char* format, int n, int m); + plErrorMsg &Set(hsBool bogus, const char* label, const char* format, float f); + + hsBool IsBogus() { return GetBogus(); } + // Ask - If condition is true and user says yes to displayed query, return true, else false + virtual hsBool Ask() { return false; } + + // CheckAndAsk - If condition is true and user says YES, throw self. Only asks if condition is true. + // Returns true if condition is true but user says no, else false. + virtual hsBool CheckAndAsk() { return false; } + + // CheckAskOrCancel - If condition is true ( if YES, throw, else if NO return 0, else (CANCEL) return 1 + virtual hsBool CheckAskOrCancel() { return false; } + + // Show - If condition is true, displays message, returns true + virtual hsBool Show() { return false; } + + // CheckAndShow - If condition is true, shows message box then throws self, else return false + virtual hsBool CheckAndShow() { return false; } + + // Check - If condition was true, throws self, else return false + virtual hsBool Check() { return false; } + + // Quit - If condition, quietly just throw with no message + virtual void Quit() { } + +protected: + void SetBogus(hsBool b) { fBogus = b; } + + hsBool GetBogus() { return fBogus; } + char *GetLabel() { if (!fBogus) *fLabel = 0; return fLabel; } + char *GetMsg() { if (!fBogus) *fMsg = 0; return fMsg; } + +private: + hsBool fBogus; + char fLabel[256]; + char fMsg[PL_ERR_MSG_MAX_MSG]; + +private: + // No assignment operator + plErrorMsg &operator=(const plErrorMsg &msg); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportDlg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportDlg.cpp new file mode 100644 index 00000000..91f22969 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportDlg.cpp @@ -0,0 +1,518 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "hsTypes.h" +#include "plExportDlg.h" +#include "../MaxMain/resource.h" +#include "max.h" + +#include "../MaxMain/plMaxCFGFile.h" + +#include +#include +using std::vector; +using std::string; + +extern HINSTANCE hInstance; + +class plExportDlgImp : public plExportDlg +{ +protected: + HWND fDlg; // Handle to the setup dialog + bool fPreshade; + bool fPhysicalsOnly; + bool fLightMap; + char fExportPage[256]; + char fExportSourceDir[MAX_PATH]; + bool fExporting; + bool fAutoExporting; + bool fExportFile; + + int fXPos, fYPos; + + DWORD fLastExportTime; + + static BOOL CALLBACK ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + void IDestroy(); + + void IExportCurrentFile(const char* exportPath); + void IDoExport(); + + void IInitDlg(HWND hDlg); + void IGetRadio(HWND hDlg); + +public: + plExportDlgImp(); + ~plExportDlgImp(); + + virtual void Show(); + + virtual bool IsExporting() { return fExporting; } + virtual bool IsAutoExporting() { return fAutoExporting; } + + virtual bool GetDoPreshade() { return fPreshade; } + virtual bool GetPhysicalsOnly() { return fPhysicalsOnly; } + virtual bool GetDoLightMap() { return fLightMap; } + virtual const char* GetExportPage(); + + virtual void StartAutoExport(); +}; + +plExportDlgImp::plExportDlgImp() : fDlg(NULL), fPreshade(true), fPhysicalsOnly(false), fLightMap(true), fLastExportTime(0), fExporting(false), fAutoExporting(false) +{ + const char* path = plMaxConfig::GetPluginIni(); + fXPos = GetPrivateProfileInt("Export", "X", 0, path); + fYPos = GetPrivateProfileInt("Export", "Y", 30, path); + + GetPrivateProfileString("Export", "Dir", "", fExportSourceDir, sizeof(fExportSourceDir), path); + + memset(fExportPage, 0, sizeof(fExportPage)); +} + +BOOL WritePrivateProfileInt(LPCSTR lpAppName, LPCSTR lpKeyName, int val, LPCSTR lpFileName) +{ + char buf[30]; + itoa(val, buf, 10); + + return WritePrivateProfileString(lpAppName, lpKeyName, buf, lpFileName); +} + +plExportDlgImp::~plExportDlgImp() +{ + const char* path = plMaxConfig::GetPluginIni(); + WritePrivateProfileInt("Export", "X", fXPos, path); + WritePrivateProfileInt("Export", "Y", fYPos, path); + + WritePrivateProfileString("Export", "Dir", fExportSourceDir, path); +} + +plExportDlg& plExportDlg::Instance() +{ + static plExportDlgImp theInstance; + return theInstance; +} + +BOOL plExportDlgImp::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return ((plExportDlgImp&)Instance()).DlgProc(hDlg, msg, wParam, lParam); +} + +const char* plExportDlgImp::GetExportPage() +{ + if (fExportPage[0] == '\0') + return nil; + else + return fExportPage; +} + +#include "../MaxComponent/plComponentBase.h" +#include "../MaxComponent/plMiscComponents.h" +#include "../MaxMain/plMaxNode.h" +#include "hsStlSortUtils.h" +#include + +typedef std::set CompSet; + +static void GetPagesRecur(plMaxNode* node, CompSet& comps) +{ + if (!node) + return; + + plComponentBase* comp = node->ConvertToComponent(); + if (comp && (comp->ClassID() == ROOM_CID || comp->ClassID() == PAGEINFO_CID)) + { + comps.insert(comp); + } + + for (int i = 0; i < node->NumberOfChildren(); i++) + GetPagesRecur((plMaxNode*)node->GetChildNode(i), comps); +} + +static const char* kAllPages = "(All Pages)"; + +void plExportDlgImp::IGetRadio(HWND hDlg) +{ + fExportFile = (IsDlgButtonChecked(hDlg, IDC_RADIO_FILE) == BST_CHECKED); + + EnableWindow(GetDlgItem(hDlg, IDC_PAGE_COMBO), fExportFile); + +// EnableWindow(GetDlgItem(hDlg, IDC_EXPORT_PATH), !fExportFile); + EnableWindow(GetDlgItem(hDlg, IDC_BROWSE_EXPORT), !fExportFile); +} + +void plExportDlgImp::IInitDlg(HWND hDlg) +{ + // Set the client path + const char* path = plMaxConfig::GetClientPath(false, true); + SetDlgItemText(hDlg, IDC_CLIENT_PATH, path); + + // Set the preshade button + CheckDlgButton(hDlg, IDC_PRESHADE_CHECK, fPreshade ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_PHYSICAL_CHECK, fPhysicalsOnly ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hDlg, IDC_LIGHTMAP_CHECK, fLightMap ? BST_CHECKED : BST_UNCHECKED); + + char buf[256]; + sprintf(buf, "Last export took %d:%02d", fLastExportTime/60, fLastExportTime%60); + SetDlgItemText(hDlg, IDC_LAST_EXPORT, buf); + + SetWindowPos(hDlg, NULL, fXPos, fYPos, 0, 0, SWP_NOSIZE | SWP_NOZORDER); + + // + // Get the names of all the pages in this scene and put them in the combo + // + HWND hPages = GetDlgItem(hDlg, IDC_PAGE_COMBO); + ComboBox_AddString(hPages, kAllPages); + + bool foundPage = false; + + CompSet comps; + GetPagesRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), comps); + for (CompSet::iterator it = comps.begin(); it != comps.end(); it++) + { + const char* page = LocCompGetPage(*it); + if (page) + { + int idx = ComboBox_AddString(hPages, page); + if (!strcmp(page, fExportPage)) + { + foundPage = true; + ComboBox_SetCurSel(hPages, idx); + } + } + } + + if (!foundPage) + { + fExportPage[0] = '\0'; + ComboBox_SetCurSel(hPages, 0); + } + + CheckRadioButton(hDlg, IDC_RADIO_FILE, IDC_RADIO_DIR, IDC_RADIO_FILE); + IGetRadio(hDlg); + + SetDlgItemText(hDlg, IDC_EXPORT_PATH, fExportSourceDir); +} + +#include "../plFile/plBrowseFolder.h" + +BOOL plExportDlgImp::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + IInitDlg(hDlg); + return TRUE; + + case WM_COMMAND: + { + int cmd = HIWORD(wParam); + int resID = LOWORD(wParam); + + if (cmd == BN_CLICKED) + { + if (resID == IDCANCEL) + { + IDestroy(); + return TRUE; + } + else if (resID == IDC_EXPORT) + { + IDoExport(); + return TRUE; + } + else if (resID == IDC_PRESHADE_CHECK) + { + fPreshade = (IsDlgButtonChecked(hDlg, IDC_PRESHADE_CHECK) == BST_CHECKED); + return TRUE; + } + else if (resID == IDC_PHYSICAL_CHECK) + { + fPhysicalsOnly = (IsDlgButtonChecked(hDlg, IDC_PHYSICAL_CHECK) == BST_CHECKED); + return TRUE; + } + else if (resID == IDC_LIGHTMAP_CHECK) + { + fLightMap = (IsDlgButtonChecked(hDlg, IDC_LIGHTMAP_CHECK) == BST_CHECKED); + return TRUE; + } + else if (resID == IDC_DIR) + { + // Get a new client path + const char* path = plMaxConfig::GetClientPath(true); + if (path) + SetDlgItemText(hDlg, IDC_CLIENT_PATH, path); + return TRUE; + } + else if (resID == IDC_RADIO_FILE || resID == IDC_RADIO_DIR) + { + IGetRadio(hDlg); + return TRUE; + } + else if (resID == IDC_BROWSE_EXPORT) + { + plBrowseFolder::GetFolder(fExportSourceDir, + fExportSourceDir, + "Choose the source directory", + hDlg); + SetDlgItemText(hDlg, IDC_EXPORT_PATH, fExportSourceDir); + return TRUE; + } + } + else if (cmd == CBN_SELCHANGE && resID == IDC_PAGE_COMBO) + { + int sel = ComboBox_GetCurSel((HWND)lParam); + // If the user selected a page, save it + if (sel != 0 && sel != CB_ERR) + ComboBox_GetText((HWND)lParam, fExportPage, sizeof(fExportPage)); + // Else, clear it (export all pages) + else + fExportPage[0] = '\0'; + return TRUE; + } + } + break; + } + + return FALSE; +} + +void plExportDlgImp::IExportCurrentFile(const char* exportPath) +{ + // Delete the old prd so we don't get the stupid overwrite warning + DeleteFile(exportPath); + + GetCOREInterface()->ExportToFile(exportPath); +} + +#include "../plFile/hsFiles.h" + +void plExportDlgImp::IDoExport() +{ + fExporting = true; + + // Hide the window, since we don't get control back until the export is done + ShowWindow(fDlg, SW_HIDE); + + // Do the export + char exportPath[MAX_PATH]; + GetDlgItemText(fDlg, IDC_CLIENT_PATH, exportPath, sizeof(exportPath)); + strcat(exportPath, "Export.prd"); + + // For export time stats + DWORD exportTime = timeGetTime(); + + if (fExportFile) + IExportCurrentFile(exportPath); + else + { + hsFolderIterator sourceDir(fExportSourceDir); + while (sourceDir.NextFileSuffix(".max")) + { + char exportFile[MAX_PATH]; + sourceDir.GetPathAndName(exportFile); + + if (GetCOREInterface()->LoadFromFile(exportFile)) + IExportCurrentFile(exportPath); + } + } + + fLastExportTime = (timeGetTime() - exportTime) / 1000; + + IDestroy(); + + fExporting = false; +} + +void plExportDlgImp::IDestroy() +{ + if (fDlg) + { + // Save the window pos + RECT rect; + GetWindowRect(fDlg, &rect); + fXPos = rect.left; + fYPos = rect.top; + + DestroyWindow(fDlg); + fDlg = NULL; + } +} + +void plExportDlgImp::Show() +{ + if (!fDlg) + fDlg = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_EXPORT), GetCOREInterface()->GetMAXHWnd(), ForwardDlgProc); +} + +static bool IsExcluded(const char* fileName, vector& excludeFiles) +{ + for (int i = 0; i < excludeFiles.size(); i++) + { + if (!strcmp(fileName, excludeFiles[i].c_str())) + return true; + } + + return false; +} + +static bool AutoExportDir(const char* inputDir, const char* outputDir, const char* groupFiles, vector& excludeFiles) +{ + bool exportedFile = false; + + char outputFileName[MAX_PATH]; + sprintf(outputFileName, "%s\\Export.prd", outputDir); + + char outputLog[MAX_PATH]; + sprintf(outputLog, "%s\\AutoExport.log", outputDir); + + char doneDir[MAX_PATH]; + sprintf(doneDir, "%s\\Done\\", inputDir); + CreateDirectory(doneDir, NULL); + + // Don't give missing bitmap warnings + TheManager->SetSilentMode(TRUE); + + hsFolderIterator sourceDir(inputDir); + while (sourceDir.NextFileSuffix(".max")) + { + char exportFile[MAX_PATH]; + sourceDir.GetPathAndName(exportFile); + + if (IsExcluded(sourceDir.GetFileName(), excludeFiles)) + continue; + + // If we're doing grouped files, and this isn't one, keep looking + if (groupFiles && strncmp(sourceDir.GetFileName(), groupFiles, strlen(groupFiles)) != 0) + continue; + + hsUNIXStream log; + if (log.Open(outputLog, "ab")) + { + log.WriteFmt("%s\r\n", sourceDir.GetFileName()); + log.Close(); + } + + if (GetCOREInterface()->LoadFromFile(exportFile)) + { + sprintf(doneDir, "%s\\Done\\%s", inputDir, sourceDir.GetFileName()); + MoveFileEx(exportFile, doneDir, MOVEFILE_REPLACE_EXISTING); + + GetCOREInterface()->ExportToFile(outputFileName, TRUE); + exportedFile = true; + + // If we're not doing grouped files, this is it, we exported our one file + if (!groupFiles) + break; + } + } + + return exportedFile; +} + +// I'm sure there's a better way to do this but I can't find it in the docs +static void ShutdownMax() +{ + // If we're auto-exporting, write out a file to let the build scripts know + // we're done writing to disk, and if we don't exit soon we probably crashed + if (plExportDlg::Instance().IsAutoExporting()) + { + hsUNIXStream s; + s.Open("log\\AutoExportDone.txt", "wb"); + s.Close(); + } + GetCOREInterface()->FlushUndoBuffer(); + SetSaveRequiredFlag(FALSE); + PostMessage(GetCOREInterface()->GetMAXHWnd(), WM_CLOSE, 0, 0); +} + +static void GetStringSection(const char* configFile, const char* keyName, vector& strings) +{ + char source[256]; + GetPrivateProfileString("Settings", keyName, "", source, sizeof(source), configFile); + + char* seps = ","; + char* token = strtok(source, seps); + while (token != NULL) + { + strings.push_back(token); + token = strtok(NULL, seps); + } +} + +void plExportDlgImp::StartAutoExport() +{ + char configFile[MAX_PATH]; + strcpy(configFile, GetCOREInterface()->GetDir(APP_PLUGCFG_DIR)); + strcat(configFile, "\\AutoExport.ini"); + + char inputDir[MAX_PATH]; + GetPrivateProfileString("Settings", "MaxInputDir", "", inputDir, sizeof(inputDir), configFile); + + char outputDir[MAX_PATH]; + GetPrivateProfileString("Settings", "MaxOutputDir", "", outputDir, sizeof(outputDir), configFile); + + if (inputDir[0] == '\0' || outputDir == '\0') + return; + + fAutoExporting = true; + + // If we're doing an autoexport, suppress prompts now + hsMessageBox_SuppressPrompts = true; + + // Files to ignore + vector excludeFiles; + GetStringSection(configFile, "ExcludeFiles", excludeFiles); + + // + // Get the file substrings to export in one session + // + vector groupedFiles; + GetStringSection(configFile, "GroupedFiles", groupedFiles); + + for (int i = 0; i < groupedFiles.size(); i++) + { + if (AutoExportDir(inputDir, outputDir, groupedFiles[i].c_str(), excludeFiles)) + { + ShutdownMax(); + fAutoExporting = false; + return; + } + } + + if (AutoExportDir(inputDir, outputDir, NULL, excludeFiles)) + { + ShutdownMax(); + fAutoExporting = false; + return; + } + + DeleteFile(configFile); + + fAutoExporting = false; + ShutdownMax(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportDlg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportDlg.h new file mode 100644 index 00000000..04f9e471 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportDlg.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plExportDlg_h_inc +#define plExportDlg_h_inc + +class plExportDlg +{ +public: + static plExportDlg& Instance(); + + virtual void Show()=0; + + // Returns true if the export dialog is doing the export + virtual bool IsExporting()=0; + // Returns true if an autoexport is running (GetValidateUVs is valid) + virtual bool IsAutoExporting()=0; + + virtual bool GetDoLightMap()=0; + virtual bool GetDoPreshade()=0; + virtual bool GetPhysicalsOnly()=0; + virtual const char* GetExportPage()=0; + + virtual void StartAutoExport()=0; +}; + +#endif // plExportDlg_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportErrorMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportErrorMsg.cpp new file mode 100644 index 00000000..fe42ddd7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportErrorMsg.cpp @@ -0,0 +1,126 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "hsWindows.h" +#include "hsTypes.h" +#include "plExportErrorMsg.h" +#include "hsExceptions.h" +#include "hsUtils.h" + + + +hsBool plExportErrorMsg::Show() +{ + // If bogus, and we have something to show, show it + if( GetBogus() && (GetMsg()[0] != 0 || GetLabel()[0] != 0)) + { + hsMessageBox(GetMsg(), GetLabel(), hsMessageBoxNormal/*|hsMessageBoxIconError*/); + } + return GetBogus(); +} +hsBool plExportErrorMsg::Ask() +{ + if( GetBogus() ) + { + return IDYES == hsMessageBox(GetMsg(), GetLabel(), hsMessageBoxYesNo/*|hsMessageBoxIconExclamation*/); + } + return false; +} + +hsBool plExportErrorMsg::CheckAndAsk() +{ + if( GetBogus() ) + { + strncat(GetMsg(), " - File corruption possible - ABORT?", 255); + if( Ask() ) + { + sprintf(GetMsg(), "!Abort at user response to error!"); + Check(); + } + } + return GetBogus(); +} + +hsBool plExportErrorMsg::CheckAskOrCancel() +{ + if( GetBogus() ) + { + strncat(GetMsg(), " - ABORT? (Cancel to mute warnings)", 255); + int ret = hsMessageBox(GetMsg(), GetLabel(), hsMessageBoxYesNoCancel/*|hsMessageBoxIconExclamation*/); + if( IDYES == ret ) + { + sprintf(GetMsg(), "!Abort at user response to error!"); + Check(); + } + else if( IDCANCEL == ret ) + return 1; + } + return false; +} + +hsBool plExportErrorMsg::CheckAndShow() +{ + if ( GetBogus() ) + { + Show(); + Check(); + } + + return GetBogus(); +} + +hsBool plExportErrorMsg::Check() +{ + if( GetBogus() ) + { + strncat(GetMsg(), " !Output File Corrupt!", 255); + IDebugThrow(); + } + + return false; +} + +void plExportErrorMsg::Quit() +{ + if( GetBogus() ) + { + SetBogus(false); + hsThrow( *this ); + } +} + +void plExportErrorMsg::IDebugThrow() +{ + try { +#if HS_BUILD_FOR_WIN32 + DebugBreak(); +#endif // HS_BUILD_FOR_WIN32 + } + catch(...) + { + hsThrow( *this ); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportErrorMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportErrorMsg.h new file mode 100644 index 00000000..dd003af2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportErrorMsg.h @@ -0,0 +1,133 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plExportErrorMsg_inc +#define plExportErrorMsg_inc + +#include +#include "plErrorMsg.h" + +#if !HS_BUILD_FOR_WIN32 +#define PL_NULL_ERRMSG +#endif !HS_BUILD_FOR_WIN32 + +#ifndef PL_NULL_ERRMSG + +class plExportErrorMsg : public plErrorMsg { +public: + plExportErrorMsg(const char* label, const char* msg) : plErrorMsg(label, msg) { } + plExportErrorMsg(hsBool bogus = false) : plErrorMsg(bogus) { } + plExportErrorMsg(hsBool bogus, const char* label, const char* msg) + : plErrorMsg(bogus, label, msg) { } + plExportErrorMsg(hsBool bogus, const char* label, const char* format, const char* str) + : plErrorMsg(bogus, label, format, str) { } + plExportErrorMsg(hsBool bogus, const char* label, const char* format, const char* str1, const char* str2) + : plErrorMsg(bogus, label, format, str1, str2) { } + plExportErrorMsg(hsBool bogus, const char* label, const char* format, int n) + : plErrorMsg(bogus, label, format, n) { } + plExportErrorMsg(hsBool bogus, const char* label, const char* format, int n, int m) + : plErrorMsg(bogus, label, format, n, m) { } + plExportErrorMsg(hsBool bogus, const char* label, const char* format, float f) + : plErrorMsg(bogus, label, format, f) { } + + virtual hsBool Ask(); // if b is true and user says yes to displayed query, return true, else false + virtual hsBool CheckAndAsk(); // if b is true and user says YES, throw self. only asks if b is true. returns true if b is true but user says no, else false + virtual hsBool CheckAskOrCancel(); // if b is true ( if YES, throw, else if NO return 0, else (CANCEL) return 1 + virtual hsBool Show(); // if b is true, displays message, returns true + virtual hsBool Check(); // if b was true, throws self, else return false + virtual hsBool CheckAndShow(); // if b was true, shows message box then throws self, else return false + virtual void Quit(); // if b, quietly just throw with no message + +private: + void IDebugThrow(); +}; +#else // PL_NULL_ERRMSG + +class plExportErrorMsg : public plErrorMsg { +public: + plExportErrorMsg(const char* label, const char* msg) : plErrorMsg() { } + plExportErrorMsg(hsBool bogus = false) : plErrorMsg() { } + plExportErrorMsg(hsBool bogus, const char* label, const char* msg) + : plErrorMsg() { } + plExportErrorMsg(hsBool bogus, const char* label, const char* format, const char* str) + : plErrorMsg() { } + plExportErrorMsg(hsBool bogus, const char* label, const char* format, const char* str1, const char* str2) + : plErrorMsg() { } + plExportErrorMsg(hsBool bogus, const char* label, const char* format, int n) + : plErrorMsg() { } + plExportErrorMsg(hsBool bogus, const char* label, const char* format, int n, int m) + : plErrorMsg() { } + plExportErrorMsg(hsBool bogus, const char* label, const char* format, float f) + : plErrorMsg() { } +}; +#endif // PL_NULL_ERRMSG + +// Compile out error messages labeled debug +// #define PL_ERR_CHECK_DEGUG_ONLY +#if defined(NDEBUG) && defined(PL_ERR_CHECK_DEGUG_ONLY) + +class plExportErrorDbg : public plExportErrorMsg { +public: + plExportErrorDbg(const char* label, const char* msg) : plExportErrorMsg() { } + plExportErrorDbg(hsBool bogus = false) : plExportErrorMsg() { } + plExportErrorDbg(hsBool bogus, const char* label, const char* msg) : plExportErrorMsg() { } + plExportErrorDbg(hsBool bogus, const char* label, const char* format, const char* str) : plExportErrorMsg() { } + plExportErrorDbg(hsBool bogus, const char* label, const char* format, const char* str1, const char* str2) : plExportErrorMsg() { } + plExportErrorDbg(hsBool bogus, const char* label, const char* format, int n) : plExportErrorMsg() { } + plExportErrorDbg(hsBool bogus, const char* label, const char* format, int n, int m) : plExportErrorMsg() { } + plExportErrorDbg(hsBool bogus, const char* label, const char* format, float f) : plExportErrorMsg() { } + + hsBool Ask() { return false; } + hsBool CheckAndAsk() { return false; } + hsBool CheckAskOrCancel(); + hsBool Show() { return false; } + hsBool Check() { return false; } + hsBool CheckAndShow() { return false; } + void Quit() { } +}; + +#else // keep them as exactly the same as errormessage + +class plExportErrorDbg : public plExportErrorMsg { +public: + plExportErrorDbg(const char* label, const char* msg) : plExportErrorMsg(label, msg) { } + plExportErrorDbg(hsBool bogus = true) : plExportErrorMsg(bogus) { } + plExportErrorDbg(hsBool bogus, const char* label, const char* msg) + : plExportErrorMsg(bogus, label, msg) { } + plExportErrorDbg(hsBool bogus, const char* label, const char* format, const char* str) + : plExportErrorMsg(bogus, label, format, str) { } + plExportErrorDbg(hsBool bogus, const char* label, const char* format, const char* str1, const char* str2) + : plExportErrorMsg(bogus, label, format, str1, str2) { } + plExportErrorDbg(hsBool bogus, const char* label, const char* format, int n) + : plExportErrorMsg(bogus, label, format, n) { } + plExportErrorDbg(hsBool bogus, const char* label, const char* format, int n, int m) + : plExportErrorMsg(bogus, label, format, n, m) { } + plExportErrorDbg(hsBool bogus, const char* label, const char* format, float f) + : plExportErrorMsg(bogus, label, format, f) { } +}; + +#endif // keep them as exactly the same as errormessage + +#endif // plErrMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportLogErrorMsg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportLogErrorMsg.cpp new file mode 100644 index 00000000..c8e9215b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportLogErrorMsg.cpp @@ -0,0 +1,187 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "Max.h" +#include +#include +#include +#include "hsWindows.h" +#include "hsTypes.h" +#include "plExportLogErrorMsg.h" +#include "hsExceptions.h" +#include "hsUtils.h" + +// +// On our way out, be sure to close the error file +// ... and remind them of how many errors they created +// +plExportLogErrorMsg::~plExportLogErrorMsg() +{ + if ( fErrfile ) + { + fprintf(fErrfile, "\n%d total number of error!!!! ", fNumberErrors); + if ( fNumberErrors > 10 ) + if ( fNumberErrors > 20 ) + if ( fNumberErrors > 50 ) + fprintf(fErrfile, "(CRISIS CRISIS!)"); + else + fprintf(fErrfile, "(which is a disaster!)"); + else + fprintf(fErrfile, "(which is way too many!)"); + fclose(fErrfile); + } +#ifdef ERRORLOG_ALWAYS_WRITE_SOMETHING + else + { + fErrfile = hsFopen(fErrfile_name, "wt"); + setbuf(fErrfile, nil); + fprintf(fErrfile, "No errors found! Good job."); + fclose(fErrfile); + } +#endif // ERRORLOG_ALWAYS_WRITE_SOMETHING +} + + +hsBool plExportLogErrorMsg::Show() +{ + if( GetBogus() ) + { + IWriteErrorFile(GetLabel(),GetMsg()); + } + return GetBogus(); +} +hsBool plExportLogErrorMsg::Ask() +{ + if( GetBogus() ) + { + IWriteErrorFile(GetLabel(),GetMsg()); + } + return false; +} + +hsBool plExportLogErrorMsg::CheckAndAsk() +{ + if( GetBogus() ) + { + strncat(GetMsg(), " - File corruption possible!", 255); + IWriteErrorFile(GetLabel(),GetMsg()); + } + return GetBogus(); +} + +hsBool plExportLogErrorMsg::CheckAskOrCancel() +{ + if( GetBogus() ) + { + IWriteErrorFile(GetLabel(),GetMsg()); + } + return false; +} + +hsBool plExportLogErrorMsg::CheckAndShow() +{ + if ( GetBogus() ) + { + Show(); + Check(); + } + + return false; +} + + +hsBool plExportLogErrorMsg::Check() +{ + if( GetBogus() ) + { + // ... how many ways can you say something is bad? + strncat(GetMsg(), " !Output File Corrupt!", 255); + IWriteErrorFile(GetLabel(),GetMsg()); + IDebugThrow(); + } + + return false; +} + +// +// Not sure what to do here... it must be really bad if someone wants to Quit() +// +void plExportLogErrorMsg::Quit() +{ + if( GetBogus() ) + { + strncat(GetMsg(), " -- Quit! (must be real bad!)", 255); + IWriteErrorFile(GetLabel(),GetMsg()); + SetBogus(false); + hsThrow( *this ); + } +} + +// +// Write a string to the Error Log File, be sure its open before using +// +void plExportLogErrorMsg::IWriteErrorFile(const char* label, const char* msg) +{ + //make sure that there is a filename + if (fErrfile_name[0] != '\0') + { + // do we have it open, yet? + if ( !fErrfile ) + { + // must be the first write... open the error file + fErrfile = hsFopen(fErrfile_name, "wt"); + setbuf(fErrfile, nil); + fNumberErrors = 0; + } + fprintf(fErrfile, "%s: %s\n", label, msg); + fNumberErrors++; // oh, boy... another error to count + } + + // Check to see if we are running an export server + // If so, then pass the update on to the export server + GUP* exportServerGup = OpenGupPlugIn(Class_ID(470000004,99)); + if(exportServerGup) + { + exportServerGup->Control(-5); // means next control will be error msg + char buf[1024]; + sprintf(buf, "%s: %s", label, msg); + exportServerGup->Control((DWORD)buf); + exportServerGup->Control(-7); // signal that we're done sending this update sequence + } +} + +void plExportLogErrorMsg::IDebugThrow() +{ + try { +#if HS_BUILD_FOR_WIN32 + DebugBreak(); +#endif // HS_BUILD_FOR_WIN32 + } + catch(...) + { + hsThrow( *this ); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportLogErrorMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportLogErrorMsg.h new file mode 100644 index 00000000..30ccd66b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportLogErrorMsg.h @@ -0,0 +1,156 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plExportLogErrorMsg_inc +#define plExportLogErrorMsg_inc + +// +// This plErrorMsg derivation will just log the error to a text file, instead of +// asking the user (because this used for unattended exporting). +// If there is no errors then there should be no file created. +// + +#include +#include "plErrorMsg.h" + +#if !HS_BUILD_FOR_WIN32 +#define PL_NULL_ERRMSG +#endif !HS_BUILD_FOR_WIN32 + +#ifndef PL_NULL_ERRMSG + +#define ERROR_LOGFILE_NAME_LEN 512 +class plExportLogErrorMsg : public plErrorMsg { +public: + plExportLogErrorMsg(const char* efile, const char* label, const char* msg) : plErrorMsg(label, msg) + { strncpy(fErrfile_name,efile,ERROR_LOGFILE_NAME_LEN-1); fErrfile=nil; } + plExportLogErrorMsg(const char* efile, hsBool bogus = false) : plErrorMsg(bogus) + { strncpy(fErrfile_name,efile,ERROR_LOGFILE_NAME_LEN-1); fErrfile=nil; } + plExportLogErrorMsg(const char* efile, hsBool bogus, const char* label, const char* msg) + : plErrorMsg(bogus, label, msg) + { strncpy(fErrfile_name,efile,ERROR_LOGFILE_NAME_LEN-1); fErrfile=nil; } + plExportLogErrorMsg(const char* efile, hsBool bogus, const char* label, const char* format, const char* str) + : plErrorMsg(bogus, label, format, str) + { strncpy(fErrfile_name,efile,ERROR_LOGFILE_NAME_LEN-1); fErrfile=nil; } + plExportLogErrorMsg(const char* efile, hsBool bogus, const char* label, const char* format, const char* str1, const char* str2) + : plErrorMsg(bogus, label, format, str1, str2) + { strncpy(fErrfile_name,efile,ERROR_LOGFILE_NAME_LEN-1); fErrfile=nil; } + plExportLogErrorMsg(const char* efile, hsBool bogus, const char* label, const char* format, int n) + : plErrorMsg(bogus, label, format, n) + { strncpy(fErrfile_name,efile,ERROR_LOGFILE_NAME_LEN-1); fErrfile=nil; } + plExportLogErrorMsg(const char* efile, hsBool bogus, const char* label, const char* format, int n, int m) + : plErrorMsg(bogus, label, format, n, m) + { strncpy(fErrfile_name,efile,ERROR_LOGFILE_NAME_LEN-1); fErrfile=nil; } + plExportLogErrorMsg(const char* efile, hsBool bogus, const char* label, const char* format, float f) + : plErrorMsg(bogus, label, format, f) + { strncpy(fErrfile_name,efile,ERROR_LOGFILE_NAME_LEN-1); fErrfile=nil; } + ~plExportLogErrorMsg(); + + virtual hsBool Ask(); // if b is true and user says yes to displayed query, return true, else false + virtual hsBool CheckAndAsk(); // if b is true and user says YES, throw self. only asks if b is true. returns true if b is true but user says no, else false + virtual hsBool CheckAskOrCancel(); // if b is true ( if YES, throw, else if NO return 0, else (CANCEL) return 1 + virtual hsBool Show(); // if b is true, displays message, returns true + virtual hsBool Check(); // if b was true, throws self, else return false + virtual hsBool CheckAndShow(); // if b was true, shows message box then throws self, else return false + virtual void Quit(); // if b, quietly just throw with no message + +protected: + virtual void IWriteErrorFile(const char* label, const char* msg); +private: + FILE *fErrfile; // the error file to write the nasties + char fErrfile_name[ERROR_LOGFILE_NAME_LEN]; // the name of the error file + Int32 fNumberErrors; + +private: + void IDebugThrow(); +}; +#else // PL_NULL_ERRMSG + +class plExportLogErrorMsg : public plErrorMsg { +public: + plExportLogErrorMsg(const char* label, const char* msg) : plErrorMsg() { } + plExportLogErrorMsg(hsBool bogus = false) : plErrorMsg() { } + plExportLogErrorMsg(hsBool bogus, const char* label, const char* msg) + : plErrorMsg() { } + plExportLogErrorMsg(hsBool bogus, const char* label, const char* format, const char* str) + : plErrorMsg() { } + plExportLogErrorMsg(hsBool bogus, const char* label, const char* format, const char* str1, const char* str2) + : plErrorMsg() { } + plExportLogErrorMsg(hsBool bogus, const char* label, const char* format, int n) + : plErrorMsg() { } + plExportLogErrorMsg(hsBool bogus, const char* label, const char* format, int n, int m) + : plErrorMsg() { } + plExportLogErrorMsg(hsBool bogus, const char* label, const char* format, float f) + : plErrorMsg() { } +}; +#endif // PL_NULL_ERRMSG + +// Compile out error messages labeled debug +// #define PL_ERR_CHECK_DEGUG_ONLY +#if defined(NDEBUG) && defined(PL_ERR_CHECK_DEGUG_ONLY) + +class plExportLogErrorDbg : public plExportLogErrorMsg { +public: + plExportLogErrorDbg(const char* label, const char* msg) : plExportLogErrorMsg("") { } + plExportLogErrorDbg(hsBool bogus = false) : plExportLogErrorMsg("") { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* msg) : plExportLogErrorMsg("") { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* format, const char* str) : plExportLogErrorMsg("") { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* format, const char* str1, const char* str2) : plExportLogErrorMsg("") { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* format, int n) : plExportLogErrorMsg("") { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* format, int n, int m) : plExportLogErrorMsg("") { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* format, float f) : plExportLogErrorMsg("") { } + + hsBool Ask() { return false; } + hsBool CheckAndAsk() { return false; } + hsBool CheckAskOrCancel(); + hsBool Show() { return false; } + hsBool Check() { return false; } + hsBool CheckAndShow() { return false; } + void Quit() { } +}; + +#else // keep them as exactly the same as errormessage + +class plExportLogErrorDbg : public plExportLogErrorMsg { +public: + plExportLogErrorDbg(const char* label, const char* msg) : plExportLogErrorMsg("",label, msg) { } + plExportLogErrorDbg(hsBool bogus = true) : plExportLogErrorMsg("",bogus) { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* msg) + : plExportLogErrorMsg("",bogus, label, msg) { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* format, const char* str) + : plExportLogErrorMsg("",bogus, label, format, str) { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* format, const char* str1, const char* str2) + : plExportLogErrorMsg("",bogus, label, format, str1, str2) { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* format, int n) + : plExportLogErrorMsg("",bogus, label, format, n) { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* format, int n, int m) + : plExportLogErrorMsg("",bogus, label, format, n, m) { } + plExportLogErrorDbg(hsBool bogus, const char* label, const char* format, float f) + : plExportLogErrorMsg("",bogus, label, format, f) { } +}; + +#endif // keep them as exactly the same as errormessage + +#endif // plExportLogErrorMsg_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportProgressBar.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportProgressBar.cpp new file mode 100644 index 00000000..a0307d23 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportProgressBar.cpp @@ -0,0 +1,122 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "Max.h" +#include "plExportProgressBar.h" +#include +#include +#include + +namespace { + DWORD WINAPI ProgressDummyFunc(LPVOID arg) + { + return(0); + } +} + +plExportProgressBar::plExportProgressBar() : + fTotalSteps(0), + fCurStep(0) +{ + fInterface = GetCOREInterface(); +} + +plExportProgressBar::~plExportProgressBar() +{ + fInterface->ProgressEnd(); +} + +void plExportProgressBar::Start(char *name, UInt32 steps) +{ + fTotalSteps = steps; + fCurStep = 0; + + fInterface->ProgressEnd(); + fInterface->ProgressStart(name, TRUE, ProgressDummyFunc, nil); + + GUP* exportServerGup = OpenGupPlugIn(Class_ID(470000004,99)); + if(exportServerGup && name) + { + exportServerGup->Control(-3); // means next control will be progress task + exportServerGup->Control((DWORD)name); + } +} + +bool plExportProgressBar::Update(char *name, UInt32 inc) +{ + fCurStep += inc; + + // Check to see if we are running an export server + // If so, then pass the update on to the export server + GUP* exportServerGup = OpenGupPlugIn(Class_ID(470000004,99)); + if(exportServerGup) + { + exportServerGup->Control(-2); // means next control will be progress pct + exportServerGup->Control((int)((fCurStep * 100) / fTotalSteps)); // send pct + if(name) + { + exportServerGup->Control(-4); // means next control will be progress subtask + exportServerGup->Control((DWORD)name); + } + exportServerGup->Control(-6); // signal that we're done sending this update sequence + } + + fInterface->ProgressUpdate((int)((fCurStep * 100) / fTotalSteps), FALSE, name); + + if (fInterface->GetCancel()) + { + int retval = MessageBox(fInterface->GetMAXHWnd(), _T("Really Cancel?"), + _T("Question"), MB_ICONQUESTION | MB_YESNO); + if (retval == IDYES) + { + return true; + } + else if (retval == IDNO) + { + fInterface->SetCancel(FALSE); + } + } + + return false; +} + +UInt32 plExportProgressBar::CountNodes() +{ + return INodeCount(GetCOREInterface()->GetRootNode()); +} + +UInt32 plExportProgressBar::INodeCount(INode *node) +{ + UInt32 count = 1; + + for (int i = 0; i < node->NumberOfChildren(); i++) + { + INode *child = node->GetChildNode(i); + count += INodeCount(child); + } + + return count; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportProgressBar.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportProgressBar.h new file mode 100644 index 00000000..3c4cc42c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plExportProgressBar.h @@ -0,0 +1,53 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plExportProgressBar_inc +#define plExportProgressBar_inc + +#include "hsTypes.h" + +class INode; + +class plExportProgressBar +{ +public: + plExportProgressBar(); + ~plExportProgressBar(); + + void Start(char *name, UInt32 steps=CountNodes()); + bool Update(char *name=nil, UInt32 inc=1); + + static UInt32 CountNodes(); + +private: + static UInt32 INodeCount(INode *node); + + Interface* fInterface; + + UInt32 fTotalSteps; + UInt32 fCurStep; +}; + +#endif // plExportProgressBar_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plProgressBar.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plProgressBar.h new file mode 100644 index 00000000..ba053d69 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxExport/plProgressBar.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// plProgressBar.h +#ifndef plProgressBar_inc +#define plProgressBar_inc + +#include "hsTypes.h" +#include "hsScalar.h" + +// The progress bar displays an amount that's *fraction* of the distance between min and max. +// i.e., if min is 0.2 and max is 0.7, the bar will run the gamut from min to max; at fraction +// 0.8, it will display 0.6 ( = 0.2 + 0.8 * (0.7 - 0.2)). + +// The bar runs from 0 to 1 and all values should be in that range. +class plProgressBar +{ +public: + plProgressBar(hsScalar min, hsScalar max) : fMin(min), fMax(max) + { + hsAssert(min >= 0, "Min too small."); + hsAssert(min <= 1, "Min too big."); + hsAssert(max >= 0, "Max too small."); + hsAssert(max <= 1, "Max too big."); + hsAssert(min <= max, "Min and max out of order."); + } + + virtual hsBool32 Update(hsScalar fraction) = 0; + + hsScalar GetTotalFraction(hsScalar f) const { return fMin + f * (fMax - fMin); } + +private: + hsScalar fMin; + hsScalar fMax; +}; + +#endif // plProgressBar_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/GlobalUtility.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/GlobalUtility.cpp new file mode 100644 index 00000000..47fb4886 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/GlobalUtility.cpp @@ -0,0 +1,282 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "GlobalUtility.h" + +#include "hsResMgr.h" +#include "plMaxNode.h" +#include "../MaxSceneViewer/SceneSync.h" + +#include "../MaxComponent/ComponentDummies.h" +#include "plActionTableMgr.h" +#include "plMaxMenu.h" +#include "../MaxComponent/plComponentBase.h" +#include "../MaxSceneViewer/plMaxFileData.h" +#include "../pfPython/cyPythonInterface.h" +#include "../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h" + +#include "plMaxCFGFile.h" +#include "../pfLocalizationMgr/pfLocalizationMgr.h" + +extern plActionTableMgr theActionTableMgr; +extern HINSTANCE hInstance; + +/*===========================================================================*\ + | Class Descriptor +\*===========================================================================*/ + +static PlasmaMax gPlasmaMax; + +class PlasmaMaxClassDesc : public ClassDesc +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return &gPlasmaMax; } + const TCHAR* ClassName() { return _T("PlasmaMax"); } + SClass_ID SuperClassID() { return GUP_CLASS_ID; } + Class_ID ClassID() { return PLASMA_MAX_CLASSID; } + const TCHAR* Category() { return _T(""); } + + // ************************* action table + // The following 2 lines are added for action tables and menu work + int NumActionTables() { return theActionTableMgr.NumActionTables(); } + ActionTable* GetActionTable(int i) { return theActionTableMgr.GetActionTable(i); } +}; + +static PlasmaMaxClassDesc PlasmaMaxCD; +ClassDesc* GetGUPDesc() { return &PlasmaMaxCD; } + +////////////////////////////////////////// + +// This function is from the console. This dummy version is here so that plNetLinkingMgr will build. +plKey FindSceneObjectByName(const char* name, const char* ageName, char* statusStr, bool subString) +{ + return nil; +} + + +/*===========================================================================*\ + | Global Utility interface (start/stop/control) +\*===========================================================================*/ +PlasmaMax::PlasmaMax() +{ +} + +void DoAllRecur(PMaxNodeFunc p, plMaxNode *node) +{ + (node->*p)(nil, nil); + + for (int i = 0; i < node->NumberOfChildren(); i++) + { + plMaxNode *child = (plMaxNode*)node->GetChildNode(i); + DoAllRecur(p, child); + } +} + +//#include "../MaxComponent/plComponentBase.h" + + +#include "../MaxExport/plExportErrorMsg.h" +#include "../MaxExport/plExportDlg.h" + +static void NotifyProc(void *param, NotifyInfo *info) +{ + if (info->intcode == NOTIFY_FILE_POST_OPEN) + { + plMaxNode *pNode = (plMaxNode*)GetCOREInterface()->GetRootNode(); + DoAllRecur(plMaxNode::ClearData, pNode); + } + else if (info->intcode == NOTIFY_SYSTEM_STARTUP) + { + int type; + float scale; + GetMasterUnitInfo(&type, &scale); + if (type != UNITS_FEET || scale != 1.f) + { + hsMessageBoxWithOwner(GetCOREInterface()->GetMAXHWnd(), + "Please set your system units to 1 unit = 1 foot.\n\n" + "Customize -> Units Setup... -> System Unit Setup", + "Plasma Units Error", hsMessageBoxNormal); + } + plExportDlg::Instance().StartAutoExport(); + } +} + +// Activate and Stay Resident +// The GUPRESULT_KEEP tells MAX that we want to remain loaded in the system +// Check the SDK Help File for other returns, to change the behavior +DWORD PlasmaMax::Start() +{ + // Make sure component code isn't thrown away by the linker + DummyCodeIncludeFunc(); //Anim Comps + DummyCodeIncludeFuncActive(); //Activators + DummyCodeIncludeFuncResponder(); //Responders + DummyCodeIncludeFuncAudio(); //Audio Files + DummyCodeIncludeAvatarFunc(); //Avatar Comp + DummyCodeIncludeFuncTypes(); //Type (Portal, StartPoint, etc..) + DummyCodeIncludeFuncMisc(); //Misc (Room, PageInfo, Interesting, etc..) + DummyCodeIncludeFuncPhys(); //Phys Comps + DummyCodeIncludeFuncParticles(); //Particle Comps + DummyCodeIncludeFuncSmooth(); //Smooth Comp + DummyCodeIncludeFuncSeekPoint(); //Avatar SeekPoint Comp + DummyCodeIncludeFuncClickable(); //Clickable Comp + DummyCodeIncludeFuncSingleSht(); //OneShot Comp + DummyCodeIncludeFuncAGComp(); + DummyCodeIncludeFuncClickDrag(); //Click-Draggable comp + DummyCodeIncludeFuncInventStuff(); //Inventory Object comp + DummyCodeIncludeFuncVolumeGadget(); // inside/enter/exit phys volume activator +// DummyCodeIncludeFuncActivatorGadget(); // activator activator + DummyCodeIncludeFuncImpactGadget(); // collision activator + DummyCodeIncludeFuncSoftVolume(); // Soft Volumes + DummyCodeIncludeFuncPhysConst(); // Phys Constraints + DummyCodeIncludeFuncCameras(); // new camera code + DummyCodeIncludePythonFileFunc(); + DummyCodeIncludeFuncDistrib(); // Geometry distribution functions + DummyCodeIncludeFuncExcludeRegion(); + DummyCodeIncludeFuncCluster(); // Geometry clustering functions + DummyCodeIncludeFuncGUI(); // User Interface components + DummyCodeIncludeFuncIgnore(); // Things to ignore completely or partially + DummyCodeIncludeFuncBlow(); // Procedural wind modifier + DummyCodeIncludeFuncWater(); // All things wet. + DummyCodeIncludeFuncLightMap(); // LightMap + DummyCodeIncludeFuncXImposter(); // Like a billboard, but exier. + DummyCodeIncludeFuncRepComp(); // Different representations + DummyCodeIncludeFuncLineFollow(); // Things that follow a line + DummyCodeIncludeFuncMorph(); // Like putty in my hands + DummyCodeIncludeFuncLODFade(); // Alpha blending lod transition + DummyCodeIncludeFuncBehaviors(); // Av Behaviors, like Sitting + DummyCodeIncludeFuncNavigablesRegion(); + DummyCodeIncludeFuncTemplate(); + DummyCodeIncludeFuncClothing(); + DummyCodeIncludeFuncMultistageBeh(); + DummyCodeIncludeFuncAnimDetector(); + DummyCodeIncludeFuncShadow(); // Components controlling shadow generation. + DummyCodeIncludeFuncFootstepSound(); + DummyCodeIncludeFuncFootPrint(); // dynamic decals + DummyCodeIncludFuncNPCSpawn(); + DummyCodeIncludeFuncClimbTrigger(); + DummyCodeIncludeFuncObjectFlocker(); + DummyCodeIncludeFuncGrassShader(); + + // Register the SceneViewer with Max + SceneSync::Instance(); + + plComponentShow::Init(); + + plCreateMenu(); + + RegisterNotification(NotifyProc, 0, NOTIFY_FILE_POST_OPEN); + + RegisterNotification(NotifyProc, 0, NOTIFY_SYSTEM_STARTUP); + + InitMaxFileData(); + + // Setup the localization mgr + std::string clientPath = plMaxConfig::GetClientPath(false, true); + clientPath += "dat"; + pfLocalizationMgr::Initialize(clientPath); + + return GUPRESULT_KEEP; +} + +void PlasmaMax::Stop() +{ + UnRegisterNotification(NotifyProc, 0, NOTIFY_FILE_POST_OPEN); + + pfLocalizationMgr::Shutdown(); + + PythonInterface::WeAreInShutdown(); + PythonInterface::finiPython(); + hsgResMgr::Shutdown(); +} + +#include "plMtlCollector.h" +#include "../../AssetMan/PublicInterface/AssManBaseTypes.h" + +void TextureSet(Texmap* texmap, int iBmp, UInt64 assetId) +{ + plPlasmaMAXLayer* layer = plPlasmaMAXLayer::GetPlasmaMAXLayer(texmap); + if (layer) + { + int numBitmaps = layer->GetNumBitmaps(); + if (iBmp < numBitmaps) + layer->SetBitmapAssetId(jvUniqueId(assetId), iBmp); + } +} + +DWORD PlasmaMax::Control(DWORD parameter) +{ + if (parameter == kGetTextures) + { + TexSet texmaps; + plMtlCollector::GetMtls(nil, &texmaps); + + std::vector texInfo; + + TexSet::iterator texIt = texmaps.begin(); + for (; texIt != texmaps.end(); texIt++) + { + Texmap* texmap = (*texIt); + plPlasmaMAXLayer* layer = plPlasmaMAXLayer::GetPlasmaMAXLayer(texmap); + if (layer) + { + int numBitmaps = layer->GetNumBitmaps(); + for (int iBmp = 0; iBmp < numBitmaps; iBmp++) + { + // UPDATE: If someone merges in a material from another AssetMan + // controled scene the texture ID will already be set, when it + // shouldn't be. Because of that, we redo all the texture ID's + // every time. + // - Colin + // if (assetId.IsEmpty()) + { + char fileName[MAX_PATH]; + if (layer->GetBitmapFileName(fileName, sizeof(fileName), iBmp)) + { + int texIdx = texInfo.size(); + texInfo.resize(texIdx+1); + texInfo[texIdx].texmap = texmap; + texInfo[texIdx].iBmp = iBmp; + texInfo[texIdx].texName = fileName; + } + } + } + } + } + + jvArray* textures = TRACKED_NEW jvArray(texInfo.size()); + for (int i = 0; i < texInfo.size(); i++) + (*textures)[i] = texInfo[i]; + + return DWORD(textures); + } + else if (parameter == kGetTextureSetFunc) + { + return DWORD(&TextureSet); + } + + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/GlobalUtility.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/GlobalUtility.h new file mode 100644 index 00000000..7c1750d6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/GlobalUtility.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLASMA_MAX_H +#define PLASMA_MAX_H + +class BitmapManager; +class Texmap; + +#include "max.h" +#include "guplib.h" +#include "hsTypes.h" +#include + +#define PLASMA_MAX_CLASSID Class_ID(0x3d494269, 0x103c5c5f) + +extern ClassDesc* GetGUPDesc(); + +typedef void (*TextureSetFunc)(Texmap* texmap, int iBmp, UInt64 assetId); + +struct TexInfo +{ + Texmap* texmap; + int iBmp; + std::string texName; +}; + +class PlasmaMax : public GUP +{ +public: + PlasmaMax(); + ~PlasmaMax() {} + + // GUP Methods + DWORD Start(); + void Stop(); + + enum ControlVals + { + // Pass this to Control and get back a jvArray* of all the textures in the scene + kGetTextures, + // Pass this to Control and get back a pointer to the TextureSetFunc + kGetTextureSetFunc, + }; + DWORD Control(DWORD parameter); +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/MaxAllocDll.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/MaxAllocDll.cpp new file mode 100644 index 00000000..92d773de --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/MaxAllocDll.cpp @@ -0,0 +1,98 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "max.h" +#include "MaxAllocDll.h" + +typedef void* (*MAXMALLOC) (size_t size); +typedef void (*MAXFREE) (void *memblock); + +static MAXMALLOC maxMalloc = NULL; +static MAXFREE maxFree = NULL; +static HINSTANCE maxAllocDll = NULL; + +void LoadAllocDll() +{ + if (!maxAllocDll) + { + // Search through all the Max plugin paths for MaxAlloc.dll + Interface *ip = GetCOREInterface(); + for (int i = 0; i < ip->GetPlugInEntryCount(); i++) + { + const char *dir = ip->GetPlugInDir(i); + + char path[MAX_PATH]; + sprintf(path, "%sMaxAlloc.dll", dir); + + maxAllocDll = LoadLibrary(path); + + if (maxAllocDll) + return; + } + + maxAllocDll = LoadLibrary("MaxAlloc.dll"); + + if (!maxAllocDll) + { + ::MessageBox(NULL, "Couldn't load MaxAlloc.dll", "Error", MB_OK); + exit(0); + } + } +} + +void *plMaxMalloc(size_t size) +{ + if (!maxMalloc) + { + LoadAllocDll(); + maxMalloc = (MAXMALLOC)GetProcAddress(maxAllocDll, "MaxMalloc"); + + if (!maxMalloc) + { + ::MessageBox(NULL, "Couldn't find MaxMalloc in MaxAlloc.dll", "Error", MB_OK); + exit(0); + } + } + + return maxMalloc(size); +} + +void plMaxFree(void *memblock) +{ + if (!maxFree) + { + LoadAllocDll(); + maxFree = (MAXFREE)GetProcAddress(maxAllocDll, "MaxFree"); + + if (!maxFree) + { + ::MessageBox(NULL, "Couldn't find MaxFree in MaxAlloc.dll", "Error", MB_OK); + exit(0); + } + + } + + maxFree(memblock); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/MaxAllocDll.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/MaxAllocDll.h new file mode 100644 index 00000000..94655ef5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/MaxAllocDll.h @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// +// The functions allocate and free memory using functions contained in another +// Dll. This gives us the advantage of compiling against whatever runtime +// library we want and still being able to allocate memory and give it to Max +// to free. +// +void *plMaxMalloc(size_t size); +void plMaxFree(void *memblock); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/SimpleExport.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/SimpleExport.rc new file mode 100644 index 00000000..ad2884fa --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/SimpleExport.rc @@ -0,0 +1,538 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""../MaxComponent/plComponent.rc""\r\n" + "#include ""../MaxPlasmaMtls/MaxPlasmaMtls.rc""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_COMP_PANEL DIALOGEX 0, 0, 108, 100 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "List1",IDC_COMPLIST,"SysListView32",LVS_REPORT | + LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | + LVS_EDITLABELS | LVS_NOCOLUMNHEADER | WS_BORDER | + WS_TABSTOP,4,4,100,81,WS_EX_CLIENTEDGE + CTEXT "",IDC_NUM_TARGS,4,88,19,9,SS_SUNKEN + PUSHBUTTON "<",IDC_BACK,25,87,12,11,BS_LEFT | BS_BOTTOM | + WS_DISABLED + PUSHBUTTON ">",IDC_FORWARD,39,87,12,11,BS_CENTER | BS_BOTTOM | + WS_DISABLED + PUSHBUTTON "Ref'd By...",IDC_REF_BY_BUTTON,58,87,45,11 +END + +IDD_SCENEVIEWER DIALOG 0, 0, 222, 146 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | + WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "SceneViewer Options" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Update every",IDC_UPDATE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,103,55,10 + CONTROL "",IDC_EDIT,"CustEdit",WS_TABSTOP,65,103,18,10 + CONTROL "",IDC_SPINNER,"SpinnerControl",WS_TABSTOP,83,103,7,10 + PUSHBUTTON "Reshade",IDC_RESHADE,7,117,46,13,WS_DISABLED + PUSHBUTTON "Browse...",IDC_DIR,173,23,42,12 + LTEXT "seconds",IDC_STATIC,92,103,28,11 + LTEXT "Client Path:",IDC_STATIC,7,13,37,8 + CONTROL "Custom1",IDC_CLIENT_PATH,"CustEdit",WS_TABSTOP,7,23,163, + 12 + CONTROL "Reuse data from previous run",IDC_REUSE_DATA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,55,109,10 + GROUPBOX "Update",IDC_STATIC,1,91,218,51 + GROUPBOX "Setup",IDC_STATIC,1,2,218,84 + COMBOBOX IDC_EXE,66,39,50,54,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "Start",IDC_START,87,68,46,13 + LTEXT "Client Executable:",IDC_STATIC,7,41,58,8 +END + +IDD_GET_LOCATION DIALOG 0, 0, 112, 125 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Set A Location" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,4,107,50,14 + PUSHBUTTON "Cancel",IDCANCEL,57,107,50,14 + LISTBOX IDC_LIST_LOC,4,48,103,43,LBS_SORT | WS_VSCROLL | + WS_TABSTOP + LTEXT "a\na\na\na\na",IDC_PROMPT,4,5,103,41 + CONTROL "Use selection for any other objects without a location", + IDC_CHECK_DEFAULT,"Button",BS_AUTOCHECKBOX | + BS_MULTILINE | WS_TABSTOP,7,85,97,20 +END + +IDD_COMP_MAIN DIALOGEX 0, 0, 111, 95 +STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CLIPCHILDREN | + WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +CAPTION "Component Manager" +MENU IDR_COMP_MENU +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "Tree1",IDC_TREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | + TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,4,3,102,32, + WS_EX_CLIENTEDGE + PUSHBUTTON "Attach to Selected Object(s)",IDC_ATTACH,4,37,102,14, + WS_DISABLED + LTEXT "Component Comments:",IDC_COMMENT_TEXT,4,53,74,8 + EDITTEXT IDC_COMMENTS,4,63,102,28,ES_MULTILINE | ES_AUTOVSCROLL | + ES_READONLY | ES_WANTRETURN | WS_VSCROLL +END + +IDD_AGE_DESC DIALOGEX 0, 0, 316, 283 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Age Description Manager" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + PUSHBUTTON "Create New...",IDC_AGE_NEW,12,238,51,13 + PUSHBUTTON "New...",IDC_PAGE_NEW,269,38,32,13 + CONTROL "DateTimePicker1",IDC_DATE,"SysDateTimePick32", + DTS_RIGHTALIGN | DTS_UPDOWN | WS_TABSTOP,170,161,53,12 + CONTROL "DateTimePicker2",IDC_TIME,"SysDateTimePick32", + DTS_RIGHTALIGN | DTS_UPDOWN | WS_TABSTOP | 0x8,226,161, + 56,12 + CONTROL "",IDC_DAYLEN_EDIT,"CustEdit",WS_TABSTOP,226,179,18,10 + CONTROL "",IDC_DAYLEN_SPINNER,"SpinnerControl",WS_TABSTOP,244, + 179,7,10 + DEFPUSHBUTTON "Close",IDOK,120,262,50,14 + LTEXT "Start Date and Time (in Current Timezone):",IDC_STATIC, + 158,152,137,8 + LTEXT "Day Length:",IDC_STATIC,184,179,40,8 + LTEXT "hours",IDC_STATIC,254,179,18,8 + LTEXT "Pages:",IDC_STATIC,153,38,23,8 + GROUPBOX "Age Description",IDC_STATIC,150,140,153,67 + LTEXT "Max Capacity:",IDC_STATIC,178,193,46,8 + CONTROL "",IDC_CAP_EDIT,"CustEdit",WS_TABSTOP,226,192,18,10 + CONTROL "",IDC_CAP_SPINNER,"SpinnerControl",WS_TABSTOP,244,192,7, + 10 + LTEXT "players",IDC_STATIC,254,193,23,8 + PUSHBUTTON "Delete",IDC_PAGE_DEL,269,53,32,13 + LISTBOX IDC_PAGE_LIST,179,38,88,68,LBS_SORT | WS_VSCROLL | + WS_TABSTOP + CONTROL "",IDC_SEQPREFIX_EDIT,"CustEdit",WS_DISABLED | + WS_TABSTOP,214,221,18,10 + CONTROL "",IDC_SEQPREFIX_SPIN,"SpinnerControl",WS_DISABLED | + WS_TABSTOP,232,221,7,10 + LTEXT "Sequence Prefix:",IDC_STATIC,156,222,55,8 + GROUPBOX "Ages",IDC_AGELIST_STATIC,7,36,132,221 + GROUPBOX "Description for Cleft",IDC_AGEDESC,145,7,164,250 + GROUPBOX "Registry Settings",IDC_STATIC,150,210,153,41 + CONTROL "Reserved/Global",IDC_RSVDCHECK,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,157,235,70,10 + PUSHBUTTON "Edit...",IDC_EDITREG,246,226,50,14 + CONTROL "Tree1",IDC_AGE_LIST,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | + TVS_SHOWSELALWAYS | TVS_NOTOOLTIPS | TVS_FULLROWSELECT | + WS_TABSTOP,11,48,124,185,WS_EX_CLIENTEDGE + PUSHBUTTON "Check Out",IDC_AGE_CHECKOUT,150,18,50,14,WS_DISABLED + PUSHBUTTON "Check In",IDC_AGE_CHECKIN,202,18,50,14,WS_DISABLED + PUSHBUTTON "Undo Chk Out",IDC_AGE_UNDOCHECKOUT,254,18,50,14, + WS_DISABLED + CONTROL "Don't load with age",IDC_ADM_DONTLOAD,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,181,100,77,10 + CONTROL "Load if matching age SDL var",IDC_ADM_LOADSDL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,181,111,110,10 + GROUPBOX "Branch",IDC_STATIC,7,7,132,29 + COMBOBOX IDC_BRANCHCOMBO,12,17,122,110,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Local only",IDC_ADM_LOCAL_ONLY,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,181,120,48,10 + CONTROL "Volatile",IDC_ADM_VOLATILE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,181,129,39,10 +END + +IDD_AGE_NAME DIALOG 0, 0, 112, 39 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_AGE_NAME,4,6,103,13,ES_AUTOHSCROLL + DEFPUSHBUTTON "Create",IDOK,4,22,50,14 + PUSHBUTTON "Cancel",IDCANCEL,57,22,50,14 +END + +IDD_UTILS_RES DIALOG 0, 0, 189, 197 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +CAPTION "Resource Browser" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,42,180,50,14 + CONTROL "Tree1",IDC_REG_TREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | WS_BORDER | WS_TABSTOP, + 5,4,179,172 + PUSHBUTTON "Refresh",IDC_REFRESH,97,180,50,14 +END + +IDD_UTILS DIALOG 0, 0, 108, 27 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Resource Browser...",IDC_RES,16,6,76,14 +END + +IDD_FIND_TEXTURE DIALOG 0, 0, 249, 215 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Texture Find and Replace" +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Close",IDCANCEL,100,199,50,14 + EDITTEXT IDC_FIND_EDIT,61,3,87,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "Update Results",IDC_UPDATE_BUTTON,152,3,58,12 + LTEXT "Texture Name:",IDC_STATIC,7,5,48,8 + CONTROL "List2",IDC_TEXTURE_LIST,"SysListView32",LVS_REPORT | + LVS_SORTASCENDING | WS_BORDER | WS_TABSTOP,7,18,235,139 + PUSHBUTTON "Replace All...",IDC_REPLACE_ALL_BUTTON,152,162,58,12, + WS_DISABLED + LTEXT "Replace With:",IDC_STATIC,7,164,46,8 + PUSHBUTTON "(none)",IDC_REPLACE_BUTTON,61,162,87,12,WS_DISABLED + LTEXT "Set Export Size:",IDC_STATIC,7,179,51,8 + PUSHBUTTON "Set All...",IDC_SET_ALL_BUTTON,152,178,58,12, + WS_DISABLED + COMBOBOX IDC_SIZE_COMBO,61,177,87,173,CBS_DROPDOWNLIST | + WS_DISABLED | WS_VSCROLL | WS_TABSTOP +END + +IDD_EXPORT DIALOG 0, 0, 222, 185 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Plasma Export" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_CLIENT_PATH,8,22,159,12,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "Browse...",IDC_DIR,170,22,42,12 + CONTROL "Preshade Geometry",IDC_PRESHADE_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,40,78,10 + CONTROL "Physicals Only",IDC_PHYSICAL_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,102,40,61,10 + CONTROL "Current File",IDC_RADIO_FILE,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,7,85,51,10 + CONTROL "Directory",IDC_RADIO_DIR,"Button",BS_AUTORADIOBUTTON,7, + 114,44,10 + COMBOBOX IDC_PAGE_COMBO,43,97,78,83,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_GROUP | WS_TABSTOP + EDITTEXT IDC_EXPORT_PATH,20,127,147,12,ES_AUTOHSCROLL | + ES_READONLY + PUSHBUTTON "Browse...",IDC_BROWSE_EXPORT,170,127,42,12 + LTEXT "Last Export Took:",IDC_LAST_EXPORT,6,151,209,11, + SS_SUNKEN + DEFPUSHBUTTON "Export",IDC_EXPORT,59,167,50,14 + PUSHBUTTON "Cancel",IDCANCEL,113,167,50,14 + LTEXT "Client Path:",IDC_STATIC,8,12,37,8 + GROUPBOX "Options",IDC_STATIC,2,2,217,66 + LTEXT "Page:",IDC_STATIC,21,99,20,8 + GROUPBOX "Export",IDC_STATIC,2,71,217,75 + CONTROL "Regenerate Lightmaps",IDC_LIGHTMAP_CHECK,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,8,52,87,10 +END + +IDD_REF_BY DIALOG 0, 0, 174, 162 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | + WS_CAPTION +CAPTION "Referenced By" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDC_CLOSE,61,144,50,14 + LISTBOX IDC_REF_LIST,4,21,165,118,LBS_SORT | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + LTEXT "Double-click on a component name to select the nodes it is attached to.", + IDC_STATIC,4,2,155,16 +END + +IDD_AGE_SEQNUM DIALOG 0, 0, 314, 226 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Assigning a New Sequence Number" +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "Age title goes here",IDC_AGEMSG,7,7,296,13 + LTEXT "ADM Manager Message Goes here",IDC_ADMMSG,7,84,289,34 + DEFPUSHBUTTON "Yes, choose a new sequence number for a NORMAL age", + IDYES,43,129,221,16 + PUSHBUTTON "Yes, choose a new sequence number for a GLOBAL age", + IDNO,43,151,221,16 + PUSHBUTTON "No, do not choose a sequence number; I'll take my chances", + IDCANCEL,43,175,221,16 + LTEXT "Info Message Goes Here",IDC_INFOMSG,7,22,296,44 +END + +IDD_AGE_CHECKIN DIALOG 0, 0, 186, 95 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Age is checked out..." +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Save changes and check in",IDYES,24,33,138,14 + PUSHBUTTON "Undo check out and discard changes",IDNO,24,53,138,14 + LTEXT "The current age file you are switching away from is checked out. What do you wish to do?", + IDC_STATIC,7,9,172,18 + DEFPUSHBUTTON "Cancel",IDCANCEL,24,71,138,14 +END + +IDD_AGE_SAVEYESNO DIALOG 0, 0, 186, 61 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Local age has changed..." +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Save",IDYES,16,36,46,14 + PUSHBUTTON "Discard",IDNO,70,36,46,14 + LTEXT "The current age file you are switching away from has been altered. Do you wish to keep your changes?", + IDC_STATIC,7,9,172,18 + DEFPUSHBUTTON "Cancel",IDCANCEL,123,36,46,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_COMP_PANEL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 93 + END + + IDD_SCENEVIEWER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 215 + TOPMARGIN, 7 + BOTTOMMARGIN, 139 + END + + IDD_GET_LOCATION, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 105 + TOPMARGIN, 7 + BOTTOMMARGIN, 118 + END + + IDD_COMP_MAIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 104 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END + + IDD_AGE_DESC, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 309 + TOPMARGIN, 7 + BOTTOMMARGIN, 276 + END + + IDD_AGE_NAME, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 105 + TOPMARGIN, 7 + BOTTOMMARGIN, 32 + END + + IDD_UTILS_RES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 182 + TOPMARGIN, 7 + BOTTOMMARGIN, 190 + END + + IDD_UTILS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 20 + END + + IDD_FIND_TEXTURE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 242 + TOPMARGIN, 7 + BOTTOMMARGIN, 208 + END + + IDD_EXPORT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 215 + TOPMARGIN, 7 + BOTTOMMARGIN, 178 + END + + IDD_REF_BY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 167 + TOPMARGIN, 7 + BOTTOMMARGIN, 155 + END + + IDD_AGE_SEQNUM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 307 + TOPMARGIN, 7 + BOTTOMMARGIN, 219 + END + + IDD_AGE_CHECKIN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END + + IDD_AGE_SAVEYESNO, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 54 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_CURSOR_UP BITMAP "../../Plasma/Apps/plClient/res/cursor_up.bmp" +IDB_CURSOR_DOWN BITMAP "../../Plasma/Apps/plClient/res/cursor_down.bmp" +IDB_CURSOR_RIGHT BITMAP "../../Plasma/Apps/plClient/res/cursor_right.bmp" +IDB_CURSOR_LEFT BITMAP "../../Plasma/Apps/plClient/res/cursor_left.bmp" +IDB_CURSOR_OPEN BITMAP "../../Plasma/Apps/plClient/res/cursor_open.bmp" +IDB_CURSOR_GRAB BITMAP "../../Plasma/Apps/plClient/res/cursor_grab.bmp" +IDB_CURSOR_CLICKED BITMAP "../../Plasma/Apps/plClient/res/cursor_clicked.bmp" +IDB_CURSOR_POISED BITMAP "../../Plasma/Apps/plClient/res/cursor_poised.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_COMP_MENU MENU +BEGIN + POPUP "Tools" + BEGIN + MENUITEM "Remove Unused Components", ID_REMOVE_UNUSED + MENUITEM SEPARATOR + MENUITEM "Refresh View", ID_REFRESH + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ACT1_DESC "Save Selected (with components)" + IDS_ACT1_NAME "Save Selected..." + IDS_ACT2_DESC "Merge File (with components)" + IDS_ACT2_NAME "Merge..." + IDS_ACT3_NAME "Component Manager..." + IDS_ACT4_NAME "Resource Collector..." + IDS_ACT5_NAME "Age Description Manager..." + IDS_ACT6_NAME "Copy Components..." + IDS_ACT7_NAME "SceneViewer..." + IDS_ACT8_DESC "Lock Selected" + IDS_ACT8_NAME "Lock" + IDS_ACT9_DESC "Unlock Selected" + IDS_ACT9_NAME "Unlock" + IDS_ACT10_NAME "Texture Find and Replace..." + IDS_PLASMA_EXPORT "Plasma Export..." +END + +STRINGTABLE +BEGIN + IDS_ACT11_NAME "ResetXform" + IDS_ACT11_DESC "Reset Transform" + IDS_ACT12_NAME "Select NonRend" + IDS_ACT12_DESC "Select NonRenderables" + IDS_ACT_CAT "Plasma" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "../MaxComponent/plComponent.rc" +#include "../MaxPlasmaMtls/MaxPlasmaMtls.rc" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/indexes.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/indexes.cpp new file mode 100644 index 00000000..9e09b557 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/indexes.cpp @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// These take a long time to compile so I'm putting them here so they won't be +// rebuilt unless completely necessary -Colin + +#include "HeadSpin.h" +#include "pnNucleusCreatables.h" +#include "plAllCreatables.h" + +//#include "pfAllCreatables.h" +#include "../pfCharacter/pfCharacterCreatable.h" +#include "../pfCamera/pfCameraCreatable.h" +#include "../pfAnimation/pfAnimationCreatable.h" +#include "../pfConditional/plConditionalObjectCreatable.h" +//#include "../pfConsole/pfConsoleCreatable.h" +#include "../pfSurface/pfSurfaceCreatable.h" +#include "../pfMessage/pfMessageCreatable.h" +#include "../pfAudio/pfAudioCreatable.h" +#include "../pfPython/pfPythonCreatable.h" +#include "../pfGameGUIMgr/pfGameGUIMgrCreatable.h" +#include "../pfCCR/plCCRCreatable.h" +#include "../pfJournalBook/pfJournalBookCreatable.h" +#include "../pfGameMgr/pfGameMgrCreatables.h" diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/main.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/main.cpp new file mode 100644 index 00000000..bb99eb35 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/main.cpp @@ -0,0 +1,276 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "HeadSpin.h" +#include "Max.h" +#include "istdplug.h" +#include "custcont.h" + +#include "HeadSpin.h" + +#include "../MaxExport/SimpleExport.h" + +#include "../MaxComponent/plComponentMgr.h" +#include "../MaxPlasmaMtls/plMtlImport.h" +extern ClassDesc* GetGUPDesc(); +extern ClassDesc* GetComponentUtilDesc(); +extern ClassDesc* GetComponentMgrDesc(); +extern ClassDesc *GetMaxFileDataDesc(); +extern ClassDesc* GetMaxUtilsDesc(); + +static HSClassDesc2 HSDesc; +static int controlsInit = FALSE; +HINSTANCE hInstance = NULL; + +/*inline*/ TCHAR *GetString(int id) +{ + static TCHAR buf[256]; + if (hInstance) + return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL; + return NULL; +} + +// +// return a string to be displayed if the DLL is not found +// +__declspec(dllexport) const TCHAR *LibDescription() +{ + return "Plasma 2.0"; +} + +// +// the number of plugin classes in the dll +// +__declspec(dllexport) int LibNumberClasses() +{ + return 7 + plComponentMgr::Inst().Count() + plPlasmaMtlImport::GetNumMtlDescs(); +} + +// +// return the i'th class descriptor defined by the plugin +// + +// TEMP // +class plGeneralAttribClassDesc; +extern plGeneralAttribClassDesc theGeneralAttribClassDesc; +// TEMP // + +__declspec(dllexport) ClassDesc *LibClassDesc(int i) +{ + switch(i) + { + case 0: + return &HSDesc; + case 1: + return GetGUPDesc(); + case 2: + return (ClassDesc*)&theGeneralAttribClassDesc; + case 3: + return GetComponentUtilDesc(); + case 4: + return GetComponentMgrDesc(); + case 5: + return GetMaxFileDataDesc(); + case 6: + return GetMaxUtilsDesc(); + default: + { + int numMtls = plPlasmaMtlImport::GetNumMtlDescs(); + if( i - 7 < numMtls ) + return plPlasmaMtlImport::GetMtlDesc( i - 7 ); + return plComponentMgr::Inst().Get( i - 7 - numMtls ); + } + } +} + +// +// Return version so can detect obsolete DLLs +// +__declspec(dllexport) ULONG LibVersion() +{ + return VERSION_3DSMAX; +} + +#include "plPythonMgr.h" +#include "plPluginResManager.h" +#include "../plSDL/plSDL.h" +#include "plMaxCFGFile.h" +#include +#include "../plFile/hsFiles.h" + +// +// DLLMAIN +// +BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) +{ + hInstance = hinstDLL; + + if (!controlsInit) + { + controlsInit = TRUE; + + // jaguar controls + InitCustomControls(hInstance); + + // initialize Chicago controls + InitCommonControls(); + + plPythonMgr::Instance().LoadPythonFiles(); + + const char *clientPath = plMaxConfig::GetClientPath(false, true); + if (clientPath) + { + char oldCwd[kFolderIterator_MaxPath]; + _getcwd(oldCwd, sizeof(oldCwd)); + _chdir(clientPath); + plSDLMgr::GetInstance()->Init(); + _chdir(oldCwd); + } + + // Initialize the ResManager + plResManager* pRmgr = TRACKED_NEW plPluginResManager; + hsgResMgr::Init(pRmgr); + } + + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + break; + } + return(TRUE); +} + +////////////////////////////////////////////////////////////////////////////////// +// TEMP +////////////////////////////////////////////////////////////////////////////////// +#include "CustAttrib.h" +#include "ICustAttribContainer.h" +#include "iparamb2.h" + +#define PL_GEN_ATTRIB_CLASS_ID Class_ID(0x24c36e6e, 0x53ec2ce4) + +class plGeneralAttrib : public CustAttrib +{ +public: + ClassDesc2 *fClassDesc; + IParamBlock2 *fPBlock; + + plGeneralAttrib(); + + virtual RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message){return REF_SUCCEED;} + + int NumParamBlocks() { return 1; } // return number of ParamBlocks in this instance + IParamBlock2* GetParamBlock(int i) { return fPBlock; } // return i'th ParamBlock + IParamBlock2* GetParamBlockByID(BlockID id) { return (fPBlock->ID() == id) ? fPBlock : NULL; } // return id'd ParamBlock + + int NumRefs() { return 1;} + virtual RefTargetHandle GetReference(int i) { if(i == 0) return fPBlock; else return NULL; } + virtual void SetReference(int i, RefTargetHandle rtarg) { if(i == 0) fPBlock = (IParamBlock2 *)rtarg; } + + virtual int NumSubs() { return 1; } + virtual Animatable* SubAnim(int i) { return fPBlock; } + virtual TSTR SubAnimName(int i){ return fClassDesc->ClassName();} + + + void BeginEditParams(IObjParam *ip,ULONG flags,Animatable *prev); + void EndEditParams(IObjParam *ip, ULONG flags, Animatable *next); + SClass_ID SuperClassID() {return CUST_ATTRIB_CLASS_ID;} + Class_ID ClassID() {return fClassDesc->ClassID();} + + ReferenceTarget *Clone(RemapDir &remap = NoRemap()); + virtual bool CheckCopyAttribTo(ICustAttribContainer *to) { return true; } + + TCHAR* GetName() { return (TCHAR*)fClassDesc->ClassName(); } + void DeleteThis() { delete this; } +}; + + +class plGeneralAttribClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return 1; } + void* Create(BOOL loading) { return TRACKED_NEW plGeneralAttrib; } + const TCHAR* ClassName() { return _T("Plasma Attrib"); } + SClass_ID SuperClassID() { return CUST_ATTRIB_CLASS_ID; } + Class_ID ClassID() { return PL_GEN_ATTRIB_CLASS_ID; } + const TCHAR* Category() { return _T(""); } + const TCHAR* InternalName() { return _T("PlasmaAttrib"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plGeneralAttribClassDesc theGeneralAttribClassDesc; + +// +// Parameter Block Description +// +enum +{ + kRoomName = 0 +}; + +ParamBlockDesc2 generalAttribBlock +( + 1, _T("GeneralAttribs"), 0, &theGeneralAttribClassDesc, P_AUTO_CONSTRUCT, 0, + + // params + kRoomName, _T("roomName"), TYPE_STRING, 0, 0, + p_default, "", + end, + + end +); + +plGeneralAttrib::plGeneralAttrib() : fClassDesc(&theGeneralAttribClassDesc), fPBlock(NULL) +{ + fClassDesc->MakeAutoParamBlocks(this); +} + +void plGeneralAttrib::BeginEditParams(IObjParam *ip,ULONG flags,Animatable *prev) +{ + fClassDesc->BeginEditParams(ip,this,flags,prev); +} + +void plGeneralAttrib::EndEditParams(IObjParam *ip, ULONG flags, Animatable *next) +{ + fClassDesc->EndEditParams(ip,this,flags,next); +} + +ReferenceTarget *plGeneralAttrib::Clone(RemapDir &remap) +{ + plGeneralAttrib *pnew = (plGeneralAttrib*) fClassDesc->Create(false); + pnew->MakeRefByID(FOREVER,0,remap.CloneRef(fPBlock)); + BaseClone(this, pnew, remap); + return pnew; +} +////////////////////////////////////////////////////////////////////////////////// +// TEMP +////////////////////////////////////////////////////////////////////////////////// diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/main.def b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/main.def new file mode 100644 index 00000000..564b0a2e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/main.def @@ -0,0 +1,8 @@ +LIBRARY +EXPORTS + LibDescription @1 + LibNumberClasses @2 + LibClassDesc @3 + LibVersion @4 +SECTIONS + .data READ WRITE diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plActionTableMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plActionTableMgr.cpp new file mode 100644 index 00000000..885473cd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plActionTableMgr.cpp @@ -0,0 +1,141 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/****************************************************************************** + plActionTableMgr.cpp + + Eric Ellis +******************************************************************************/ + +#include "HeadSpin.h" +#include "plActionTableMgr.h" + + +plActionTableMgr::plActionTableMgr(ActionTableInfo& actionTable, ActionCallbackFunc cbFunc) +{ + AddActionTable(actionTable, cbFunc); + + RegisterNotification(SysStartup, this, NOTIFY_SYSTEM_STARTUP); + RegisterNotification(SysShutdown, this, NOTIFY_SYSTEM_SHUTDOWN); +} + + +plActionTableMgr::~plActionTableMgr() +{ + UnRegisterNotification(SysStartup, this, NOTIFY_SYSTEM_STARTUP); + UnRegisterNotification(SysShutdown, this, NOTIFY_SYSTEM_SHUTDOWN); +} + + +void plActionTableMgr::AddActionTable(ActionTableInfo& actionTable, ActionCallbackFunc cbFunc) +{ + actionTable.ActionCB = new ActionTableMgrCB(cbFunc); + fActionTables.push_back(&actionTable); +} + +CoreExport void *__cdecl MAX_new(size_t size); +CoreExport void __cdecl MAX_delete(void* mem); + +class plActionTable : public ActionTable +{ +public: + plActionTable(ActionTableId id, ActionContextId contextId, TSTR& name, HACCEL hDefaults, int numIds, ActionDescription* pOps, HINSTANCE hInst) + : ActionTable(id, contextId, name, hDefaults, numIds, pOps, hInst) {} + plActionTable(ActionTableId id, ActionContextId contextId, TSTR& name) + : ActionTable(id, contextId, name) {} + + void *operator new (size_t) + { + return MAX_new(sizeof(plActionTable)); + } + void operator delete (void * mem) + { + MAX_delete(mem); + } +}; + +ActionTable* plActionTableMgr::GetActionTable(int i) +{ + if(i > this->NumActionTables()) + { + return NULL; + } + + ActionTableInfo* pTableInfo = fActionTables[i]; + + if(pTableInfo->Created) + { + return GetCOREInterface()->GetActionManager()->FindTable(pTableInfo->TableId); + } + + + ActionTable *pTab = new plActionTable( + pTableInfo->TableId, + pTableInfo->ContextId, + pTableInfo->Name, NULL, + pTableInfo->Actions.size(), + &pTableInfo->Actions[0], + hInstance + ); + + // register the action table with the system before we hand it off + GetCOREInterface()->GetActionManager()->RegisterActionContext(pTableInfo->ContextId, pTableInfo->Name); + + pTableInfo->Created = true; + + return pTab; +} + + +void plActionTableMgr::SysStartup(void *param, NotifyInfo *info) +{ + plActionTableMgr* pActionTableMgr = (plActionTableMgr*)param; + +// ((MenuTestUtil*)param)->CreateMenu(); //setup menus + + IActionManager* pActionMgr = GetCOREInterface()->GetActionManager(); + + for(int i = 0; i < pActionTableMgr->NumActionTables(); i++) + { + ActionTableInfo* pTableInfo = pActionTableMgr->fActionTables[i]; + + pActionMgr->ActivateActionTable(pTableInfo->ActionCB, pTableInfo->TableId); + } +} + + +void plActionTableMgr::SysShutdown(void *param, NotifyInfo *info) +{ + plActionTableMgr* pActionTableMgr = (plActionTableMgr*)param; + + IActionManager* pActionMgr = GetCOREInterface()->GetActionManager(); + + for(int i = 0; i < pActionTableMgr->NumActionTables(); i++) + { + ActionTableInfo* pTableInfo = pActionTableMgr->fActionTables[i]; + + pActionMgr->DeactivateActionTable(pTableInfo->ActionCB, pTableInfo->TableId); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plActionTableMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plActionTableMgr.h new file mode 100644 index 00000000..d3343fbd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plActionTableMgr.h @@ -0,0 +1,126 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/****************************************************************************** + plActionTableMgr.h + + Eric Ellis +******************************************************************************/ + +#ifndef __PLACTIONTABLEMGR_H +#define __PLACTIONTABLEMGR_H + +#include "Max.h" +#include "notify.h" +#include +#include "hsTypes.h" + +typedef bool(*ActionCallbackFunc)(int); + + +/****************************************************************************** + Helper classes for plActionTableMgr +******************************************************************************/ + +class ActionTableMgrCB : public ActionCallback +{ + ActionCallbackFunc fCallbackFunc; + +public: + ActionTableMgrCB(ActionCallbackFunc cbFunc) {fCallbackFunc = cbFunc;} + + BOOL ExecuteAction(int id) { return fCallbackFunc(id) ? TRUE : FALSE; } +}; + + +class ActionTableInfo +{ + friend class plActionTableMgr; + +private: + ActionTableMgrCB* ActionCB; + bool Created; + +public: + ActionTableId TableId; + ActionContextId ContextId; + TSTR Name; + + std::vector Actions; + + ActionTableInfo(ActionTableId actionId, TCHAR* name, ActionDescription actions[], int numActions) + { + TableId = actionId; + ContextId = actionId; + Name = name; + Created = false; + ActionCB = NULL; + + for(int i = 0; i < numActions; i++) + { + Actions.push_back(actions[i]); + } + } + + ActionTableInfo() + { + TableId = 0; + ContextId = 0; + Created = false; + ActionCB = NULL; + } + + virtual ~ActionTableInfo() + { + delete ActionCB; + } +}; + + + +/****************************************************************************** + plActionTableMgr class defintion +******************************************************************************/ + +class plActionTableMgr +{ + std::vector fActionTables; + +public: + plActionTableMgr(ActionTableInfo& actionTable, ActionCallbackFunc cbFunc); + virtual ~plActionTableMgr(); + + void AddActionTable(ActionTableInfo& actionTable, ActionCallbackFunc cbFunc); + + int NumActionTables() { return fActionTables.size(); } + ActionTable* GetActionTable(int i); + +private: + static void SysStartup(void *param, NotifyInfo *info); + static void SysShutdown(void *param, NotifyInfo *info); +}; + + +#endif __PLACTIONTABLEMGR_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plAgeDescInterface.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plAgeDescInterface.cpp new file mode 100644 index 00000000..de5d057d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plAgeDescInterface.cpp @@ -0,0 +1,1331 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plAgeDescInterface.h" +#include "max.h" +#include "resource.h" +#include "../plFile/hsFiles.h" +#include "../plAgeDescription/plAgeDescription.h" +#include "plMaxCFGFile.h" +#include "hsStream.h" +#include "hsUtils.h" +#include "../../AssetMan/PublicInterface/MaxAssInterface.h" +#include "plMaxAccelerators.h" + +#include +using std::string; + +extern HINSTANCE hInstance; + +//// Tree Data Wrapper Class ////////////////////////////////////////////////// + +class plAgeFile +{ +protected: + void IGetAgeName(const char* path) + { + char name[_MAX_FNAME]; + _splitpath(path, nil, nil, name, nil); + fAgeName = name; + } + +public: + jvUniqueId fAssetID; + string fPath; + string fAgeName; + + enum Types + { + kAssetFile, + kLocalFile + }; + Types fType; + + plAgeFile(Types type) : fType(type), fPath(nil) { } + plAgeFile(Types type, const char *path) : fType(type) + { + fPath = path; + IGetAgeName(path); + } + plAgeFile(Types type, const char *path, jvUniqueId& id) : fType(type), fAssetID(id) + { + fPath = path; + IGetAgeName(path); + } +}; + +//// Static Tree Helpers ////////////////////////////////////////////////////// + +static HTREEITEM SAddTreeItem( HWND hTree, HTREEITEM hParent, const char *label, int userData ); +static int SGetTreeData( HWND tree, HTREEITEM item ); + +static void RemovePageItem( HWND listBox, int item ) +{ + plAgePage *page = (plAgePage *)ListBox_GetItemData( listBox, item ); + delete page; + ListBox_DeleteString( listBox, item ); +} + +//// Dummy Dialog Proc //////////////////////////////////////////////////////// + +BOOL CALLBACK DumbDialogProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch( msg ) + { + case WM_COMMAND: + EndDialog( hDlg, LOWORD( wParam ) ); + return TRUE; + } + return FALSE; +} + + +//// Constructor/Destructor /////////////////////////////////////////////////// + +plAgeDescInterface::plAgeDescInterface() : fhDlg(nil), fDirty(false), fSpin(nil), fCurAge(-1) +{ + fCurrAgeCheckedOut = false; + fBoldFont = nil; + fAssetManIface = nil; + + // Make sure the date/time picker controls we need are initialized + INITCOMMONCONTROLSEX icc; + icc.dwSize = sizeof(INITCOMMONCONTROLSEX); + icc.dwICC = ICC_DATE_CLASSES; + InitCommonControlsEx(&icc); +} + +plAgeDescInterface::~plAgeDescInterface() +{ + IClearAgeFiles(fAgeFiles); + + DeleteObject( fBoldFont ); + DeleteObject( fHiliteBrush ); + fBoldFont = nil; + + delete fAssetManIface; + fAssetManIface = nil; +} + +plAgeDescInterface& plAgeDescInterface::Instance() +{ + static plAgeDescInterface theInstance; + return theInstance; +} + +void plAgeDescInterface::Open() +{ + if (!fhDlg) + { + CreateDialog(hInstance, + MAKEINTRESOURCE(IDD_AGE_DESC), + GetCOREInterface()->GetMAXHWnd(), + ForwardDlgProc); + + GetCOREInterface()->RegisterDlgWnd(fhDlg); + ShowWindow(fhDlg, SW_SHOW); + } +} + +BOOL CALLBACK plAgeDescInterface::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return Instance().DlgProc(hDlg, msg, wParam, lParam); +} + +BOOL plAgeDescInterface::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + fhDlg = hDlg; + + if( fAssetManIface == nil ) + fAssetManIface = TRACKED_NEW MaxAssBranchAccess(); + + // Make our bold font by getting the normal font and bolding + if( fBoldFont == nil ) + { + HFONT origFont = (HFONT)SendMessage( hDlg, WM_GETFONT, 0, 0 ); + LOGFONT origInfo, newInfo; + GetObject( origFont, sizeof( origInfo ), &origInfo ); + memcpy( &newInfo, &origInfo, sizeof( newInfo ) ); + newInfo.lfWeight = FW_BOLD; + + fBoldFont = CreateFontIndirect( &newInfo ); + } + + if( fHiliteBrush == nil ) + fHiliteBrush = CreateSolidBrush( RGB( 255, 0, 0 ) ); + + IInitControls(); + IFillAgeTree(); + return TRUE; + + case WM_DESTROY: + delete fAssetManIface; + fAssetManIface = nil; + return TRUE; + + // Day length spinner changed + case CC_SPINNER_CHANGE: + if (LOWORD(wParam) == IDC_DAYLEN_SPINNER || + LOWORD(wParam) == IDC_CAP_SPINNER || + LOWORD(wParam) == IDC_SEQPREFIX_SPIN ) + { + fDirty = true; + return TRUE; + } + break; + + case WM_CLOSE: + ::SendMessage( fhDlg, WM_COMMAND, IDOK, 0 ); + return true; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + case IDCANCEL: + if( IMakeSureCheckedIn() ) + { + IUpdateCurAge(); + DestroyWindow(fhDlg); + fhDlg = nil; + fDirty = false; + fCurAge = -1; + fSpin = nil; + } + return TRUE; + +// case IDC_AGE_LIST: +// if (HIWORD(wParam) == LBN_SELCHANGE) +// { +// IUpdateCurAge(); +// return TRUE; +// } +// break; + + case IDC_AGE_CHECKOUT: + ICheckOutCurrentAge(); + return TRUE; + + case IDC_AGE_CHECKIN: + ICheckInCurrentAge(); + return TRUE; + + case IDC_AGE_UNDOCHECKOUT: + IUndoCheckOutCurrentAge(); + return TRUE; + + case IDC_AGE_NEW: + if (HIWORD(wParam) == BN_CLICKED) + { + INewAge(); + return TRUE; + } + break; + + case IDC_PAGE_NEW: + if (HIWORD(wParam) == BN_CLICKED) + { + INewPage(); + return TRUE; + } + break; + + case IDC_PAGE_DEL: + if (HIWORD(wParam) == BN_CLICKED) + { + HWND hPage = GetDlgItem(hDlg, IDC_PAGE_LIST); + int sel = ListBox_GetCurSel(hPage); + if (sel != LB_ERR) + { + RemovePageItem( hPage, sel ); + fDirty = true; + } + return TRUE; + } + break; + + case IDC_PAGE_LIST: + if( HIWORD( wParam ) == LBN_SELCHANGE ) + { + // Sel change + HWND list = GetDlgItem( hDlg, IDC_PAGE_LIST ); + int sel = ListBox_GetCurSel( list ); + if( sel != LB_ERR ) + { + IEnablePageControls(true); + + plAgePage *page = (plAgePage *)ListBox_GetItemData( list, sel ); + CheckDlgButton( hDlg, IDC_ADM_DONTLOAD, ( page->GetFlags() & plAgePage::kPreventAutoLoad ) ? TRUE : FALSE ); + CheckDlgButton( hDlg, IDC_ADM_LOADSDL, ( page->GetFlags() & plAgePage::kLoadIfSDLPresent ) ? TRUE : FALSE ); + CheckDlgButton( hDlg, IDC_ADM_LOCAL_ONLY, ( page->GetFlags() & plAgePage::kIsLocalOnly ) ? TRUE : FALSE ); + CheckDlgButton( hDlg, IDC_ADM_VOLATILE, ( page->GetFlags() & plAgePage::kIsVolatile ) ? TRUE : FALSE ); + } + else + IEnablePageControls(false); + } + break; + + case IDC_ADM_DONTLOAD: + case IDC_ADM_LOADSDL: + case IDC_ADM_LOCAL_ONLY: + case IDC_ADM_VOLATILE: + ICheckedPageFlag(LOWORD(wParam)); + break; + + case IDC_EDITREG: + // Ask the user to make sure they really want to do this + if( GetAsyncKeyState( VK_SHIFT ) & (~1) ) + { + if( MessageBox( hDlg, "Are you sure you wish to reassign the sequence prefix for this age?", "WARNING", MB_YESNO | MB_ICONEXCLAMATION ) == IDYES ) + { + Int32 prefix = (Int32)IGetNextFreeSequencePrefix( IsDlgButtonChecked( hDlg, IDC_RSVDCHECK ) ); + fSeqPrefixSpin->SetValue( ( prefix >= 0 ) ? prefix : -prefix, false ); + fDirty = true; + } + } + else + { + if( MessageBox( hDlg, "Editing the registry data for an age can be extremely dangerous and " + "can cause instabilities and crashes, particularly with multiplayer. " + "Are you sure you want to do this?", "WARNING", MB_YESNO | MB_ICONEXCLAMATION ) == IDYES ) + { + // Enable the controls + EnableWindow( GetDlgItem( hDlg, IDC_RSVDCHECK ), TRUE ); + EnableWindow( GetDlgItem( hDlg, IDC_SEQPREFIX_EDIT ), TRUE ); + EnableWindow( GetDlgItem( hDlg, IDC_SEQPREFIX_SPIN ), TRUE ); + } + } + return TRUE; + + case IDC_RSVDCHECK: + fDirty = true; + return FALSE; // Still process as normal + + case IDC_BRANCHCOMBO: + if( HIWORD( wParam ) == CBN_SELCHANGE ) + { + int idx = SendDlgItemMessage( hDlg, IDC_BRANCHCOMBO, CB_GETCURSEL, 0, 0 ); + if( idx != CB_ERR ) + { + int id = SendDlgItemMessage( hDlg, IDC_BRANCHCOMBO, CB_GETITEMDATA, idx, 0 ); + + fAssetManIface->SetCurrBranch( id ); + IFillAgeTree(); + } + } + return TRUE; + } + break; + + case WM_PAINT: + PAINTSTRUCT paintInfo; + + BeginPaint( hDlg, &paintInfo ); + + if( fCurrAgeCheckedOut ) + { + RECT r; + HWND dummy = GetDlgItem( hDlg, IDC_AGEDESC ); + GetClientRect( dummy, &r ); + MapWindowPoints( dummy, hDlg, (POINT *)&r, 2 ); + + for( int i = 0; i < 3; i++ ) + { + InflateRect( &r, -1, -1 ); + FrameRect( paintInfo.hdc, &r, fHiliteBrush ); + } + } + + EndPaint( hDlg, &paintInfo ); + return TRUE; + + case WM_NOTIFY: + { + NMHDR *hdr = (NMHDR*)lParam; + + // Message from the start date/time controls + if (hdr->idFrom == IDC_DATE || hdr->idFrom == IDC_TIME) + { + if (hdr->code == NM_KILLFOCUS) + { + plMaxAccelerators::Enable(); + return TRUE; + } + else if (hdr->code == NM_SETFOCUS) + { + plMaxAccelerators::Disable(); + return TRUE; + } + // Time or date changed, set dirty + else if (hdr->code == DTN_DATETIMECHANGE) + { + fDirty = true; + return TRUE; + } + } + else if( hdr->idFrom == IDC_AGE_LIST ) + { + if( hdr->code == NM_CUSTOMDRAW ) + { + // Custom draw notifications for our treeView control + LPNMTVCUSTOMDRAW treeNotify = (LPNMTVCUSTOMDRAW)lParam; + + if( treeNotify->nmcd.dwDrawStage == CDDS_PREPAINT ) + { + // Sent at the start of redraw, lets us request more specific notifys + SetWindowLong( hDlg, DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW ); + return TRUE; + } + else if( treeNotify->nmcd.dwDrawStage == CDDS_ITEMPREPAINT ) + { + // Prepaint on an item. We get to change item font and color here + int idx = SGetTreeData( hdr->hwndFrom, (HTREEITEM)treeNotify->nmcd.dwItemSpec ); +/* if( item == nil || item->fType != plAgeFile::kBranch ) + { + // Default drawing, with default font and such + SetWindowLong( hDlg, DWL_MSGRESULT, CDRF_DODEFAULT ); + return TRUE; + } +*/ + // Color change (only if the item isn't selected) + if( (HTREEITEM)treeNotify->nmcd.dwItemSpec != TreeView_GetSelection( hdr->hwndFrom ) ) + { + treeNotify->clrText = GetColorManager()->GetColor( kText ); + treeNotify->clrTextBk = GetColorManager()->GetColor( kWindow ); + } + + if (idx == -1) + { + // Set a bold font for the branch headers + if( fBoldFont != nil ) + SelectObject( treeNotify->nmcd.hdc, fBoldFont ); + } + + // Let Windows know we changed the font + SetWindowLong( hDlg, DWL_MSGRESULT, CDRF_NEWFONT ); + return TRUE; + } + else + // Let the default handle it + return FALSE; + } + else if( hdr->code == TVN_SELCHANGING ) + { + SetWindowLong( hDlg, DWL_MSGRESULT, !IMakeSureCheckedIn() ); + return TRUE; + } + else if( hdr->code == TVN_SELCHANGED ) + { + // Update the viewing age + IUpdateCurAge(); + return TRUE; + } + } + } + break; + } + + return FALSE; +} + +void plAgeDescInterface::ICheckedPageFlag(int ctrlID) +{ + HWND hList = GetDlgItem(fhDlg, IDC_PAGE_LIST); + int sel = ListBox_GetCurSel(hList); + if (sel == LB_ERR) + return; + + plAgePage* page = (plAgePage*)ListBox_GetItemData(hList, sel); + bool isChecked = (IsDlgButtonChecked(fhDlg, ctrlID) == BST_CHECKED); + + if (ctrlID == IDC_ADM_DONTLOAD) + { + if (isChecked) + { + page->SetFlags(plAgePage::kPreventAutoLoad, true); + + // Doesn't make sense to have loadWithSDL checked too + CheckDlgButton(fhDlg, IDC_ADM_LOADSDL, FALSE); + page->SetFlags(plAgePage::kLoadIfSDLPresent, false); + } + else + page->SetFlags(plAgePage::kPreventAutoLoad, false); + } + else if (ctrlID == IDC_ADM_LOADSDL) + { + if (isChecked) + { + page->SetFlags(plAgePage::kLoadIfSDLPresent, true); + + // Doesn't make sense to have dontLoad checked too + CheckDlgButton(fhDlg, IDC_ADM_DONTLOAD, FALSE); + page->SetFlags(plAgePage::kPreventAutoLoad, false); + } + else + page->SetFlags(plAgePage::kLoadIfSDLPresent, false); + } + else if (ctrlID == IDC_ADM_LOCAL_ONLY) + { + page->SetFlags(plAgePage::kIsLocalOnly, isChecked); + } + else if (ctrlID == IDC_ADM_VOLATILE) + { + page->SetFlags(plAgePage::kIsVolatile, isChecked); + } +} + +void plAgeDescInterface::ICheckOutCurrentAge( void ) +{ + hsAssert( !fCurrAgeCheckedOut, "Trying to re-check out an age!" ); + + plAgeFile *currAge = IGetCurrentAge(); + if( currAge == nil ) + { + hsAssert( false, "How are you checking out an age if none is selected?" ); + return; + } + + if( currAge->fType != plAgeFile::kAssetFile ) + return; + + // Check it out from AssetMan + bool checkOutSuccess = (*fAssetManIface)->CheckOutAsset( currAge->fAssetID, fCheckedOutPath, sizeof( fCheckedOutPath ) ); + if( !checkOutSuccess ) + { + hsMessageBox( "Unable to check out age file from AssetMan. Most likely somebody already has it checked out.", "Error", hsMessageBoxNormal ); + return; + } + fCurrAgeCheckedOut = true; + + // Make sure we loaded the latest version + ILoadAge( fCheckedOutPath, true ); + + IInvalidateCheckOutIndicator(); + IEnableControls( true ); +} + +void plAgeDescInterface::ICheckInCurrentAge( void ) +{ + hsAssert( fCurrAgeCheckedOut, "Trying to check in an age when none is checked out!" ); + + plAgeFile *currAge = IGetCurrentAge(); + if( currAge == nil ) + { + hsAssert( false, "How are you checking in an age if none is selected?" ); + return; + } + + // Save the sucker + ISaveCurAge( fCheckedOutPath ); + + // Check the age file back in to AssetMan + (*fAssetManIface)->CheckInAsset( currAge->fAssetID, fCheckedOutPath, "" ); + fCurrAgeCheckedOut = false; + + IInvalidateCheckOutIndicator(); + IEnableControls( true ); +} + +void plAgeDescInterface::IUndoCheckOutCurrentAge( void ) +{ + hsAssert( fCurrAgeCheckedOut, "Trying to undo check out an age when none is checked out!" ); + + plAgeFile *currAge = IGetCurrentAge(); + if( currAge == nil ) + { + hsAssert( false, "How are you undoing a checkout if no age is selected?" ); + return; + } + + // Check the age file back in to AssetMan + (*fAssetManIface)->UndoCheckOutAsset(currAge->fAssetID); + fCurrAgeCheckedOut = false; + + // Reload the non-saved version + ILoadAge( fCheckedOutPath ); + + IInvalidateCheckOutIndicator(); + IEnableControls( true ); +} + +void plAgeDescInterface::IInvalidateCheckOutIndicator( void ) +{ + RECT r; + HWND dummy = GetDlgItem( fhDlg, IDC_AGEDESC ); + GetClientRect( dummy, &r ); + MapWindowPoints( dummy, fhDlg, (POINT *)&r, 2 ); + + RedrawWindow( fhDlg, &r, nil, RDW_INVALIDATE | RDW_ERASE ); +} + +hsBool plAgeDescInterface::IMakeSureCheckedIn( void ) +{ + int result; + plAgeFile* currAge = IGetCurrentAge(); + if (!currAge) + return true; + + if ( currAge->fType == plAgeFile::kAssetFile && fCurrAgeCheckedOut ) + { + // We're switching away from an age we have checked out--ask what they want to do + result = DialogBox( hInstance, MAKEINTRESOURCE( IDD_AGE_CHECKIN ), + GetCOREInterface()->GetMAXHWnd(), DumbDialogProc ); + if( result == IDCANCEL ) + { + // Got cancelled + return false; + } + else if( result == IDYES ) + { + ICheckInCurrentAge(); + } + else + { + IUndoCheckOutCurrentAge(); + } + } + else if( currAge->fType == plAgeFile::kLocalFile && fDirty ) + { + // Ask if we want to save changes + result = DialogBox( hInstance, MAKEINTRESOURCE( IDD_AGE_SAVEYESNO ), + GetCOREInterface()->GetMAXHWnd(), DumbDialogProc ); + if( result == IDCANCEL ) + { + // Got cancelled + return false; + } + else if( result == IDYES ) + { + ISaveCurAge( currAge->fPath.c_str() ); + } + else + { + // Reload the non-saved version + ILoadAge( currAge->fPath.c_str() ); + } + IEnableControls( true ); + } + return true; +} + +void plAgeDescInterface::IUpdateCurAge( void ) +{ + // Get the current age selection + plAgeFile *currAge = IGetCurrentAge(); + + if (currAge == nil) + { + ISetControlDefaults(); + IEnableControls( false ); + return; + } + + IEnableControls( true ); + + if( currAge->fType == plAgeFile::kAssetFile ) + { + // Gotta get the latest version from assetMan before loading + char localFilename[ MAX_PATH ]; + if( !(*fAssetManIface)->GetLatestVersionFile( currAge->fAssetID, localFilename, sizeof( localFilename ) ) ) + { + hsMessageBox( "Unable to get latest version of age asset from AssetMan. Things are about to get very funky...", "ERROR", hsMessageBoxNormal ); + } + else + { + ILoadAge( localFilename ); + } + } + else + // Load the local age, also check its sequence #s + ILoadAge( currAge->fPath.c_str(), true ); +} + +static const int kDefaultCapacity = 10; + +void plAgeDescInterface::IInitControls() +{ + // Fill the branch combo box + SendDlgItemMessage( fhDlg, IDC_BRANCHCOMBO, CB_RESETCONTENT, 0, 0 ); + const jvTypeArray &branches = (*fAssetManIface)->GetBranches(); + int i, curr = 0; + for( i = 0; i < branches.Size(); i++ ) + { + int idx = SendDlgItemMessage( fhDlg, IDC_BRANCHCOMBO, CB_ADDSTRING, 0, (LPARAM)(const char *)( branches[ i ].Name ) ); + SendDlgItemMessage( fhDlg, IDC_BRANCHCOMBO, CB_SETITEMDATA, idx, (LPARAM)branches[ i ].Id ); + + if( branches[ i ].Id == fAssetManIface->GetCurrBranch() ) + curr = i; + } + SendDlgItemMessage( fhDlg, IDC_BRANCHCOMBO, CB_SETCURSEL, curr, 0 ); + + + fSpin = SetupFloatSpinner(fhDlg, IDC_DAYLEN_SPINNER, IDC_DAYLEN_EDIT, 1.f, 100.f, 24.f); + fCapSpin = SetupIntSpinner(fhDlg, IDC_CAP_SPINNER, IDC_CAP_EDIT, 1, 250, kDefaultCapacity); + fSeqPrefixSpin = SetupIntSpinner(fhDlg, IDC_SEQPREFIX_SPIN, IDC_SEQPREFIX_EDIT, 1, 102400, 1); + + SendDlgItemMessage( fhDlg, IDC_AGELIST_STATIC, WM_SETFONT, (WPARAM)fBoldFont, MAKELPARAM( TRUE, 0 ) ); + SendDlgItemMessage( fhDlg, IDC_AGEDESC, WM_SETFONT, (WPARAM)fBoldFont, MAKELPARAM( TRUE, 0 ) ); + + ISetControlDefaults(); + IEnableControls(false); +} + +void plAgeDescInterface::ISetControlDefaults() +{ + HWND hDate = GetDlgItem(fhDlg, IDC_DATE); + HWND hTime = GetDlgItem(fhDlg, IDC_TIME); + + SYSTEMTIME st = {0}; + st.wDay = 1; + st.wMonth = 1; + st.wYear = 2000; + DateTime_SetSystemtime(hDate, GDT_VALID, &st); + DateTime_SetSystemtime(hTime, GDT_VALID, &st); + + fSpin->SetValue(24.f, FALSE); + fCapSpin->SetValue(kDefaultCapacity, FALSE); + + CheckDlgButton( fhDlg, IDC_ADM_DONTLOAD, FALSE ); + + int i; + HWND ctrl = GetDlgItem( fhDlg, IDC_PAGE_LIST ); + for( i = SendMessage( ctrl, LB_GETCOUNT, 0, 0 ) - 1; i >= 0; i-- ) + RemovePageItem( ctrl, i ); + + SetDlgItemText( fhDlg, IDC_AGEDESC, "Age Description" ); +} + +void plAgeDescInterface::IEnableControls(bool enable) +{ + bool checkedOut = true; + + plAgeFile *currAge = IGetCurrentAge(); + if( currAge != nil && currAge->fType == plAgeFile::kAssetFile ) + checkedOut = fCurrAgeCheckedOut && enable; + else if( currAge != nil && currAge->fType == plAgeFile::kLocalFile ) + checkedOut = enable; + else + checkedOut = false; + + EnableWindow(GetDlgItem(fhDlg, IDC_DATE), checkedOut ); + EnableWindow(GetDlgItem(fhDlg, IDC_TIME), checkedOut ); + EnableWindow(GetDlgItem(fhDlg, IDC_PAGE_LIST), checkedOut ); + EnableWindow(GetDlgItem(fhDlg, IDC_PAGE_NEW), checkedOut ); + EnableWindow(GetDlgItem(fhDlg, IDC_PAGE_DEL), checkedOut ); + EnableWindow(GetDlgItem(fhDlg, IDC_EDITREG), checkedOut ); + + if (!enable || !checkedOut) + IEnablePageControls(false); + + fSpin->Enable(checkedOut ); + fCapSpin->Enable(checkedOut ); + + if( currAge != nil && currAge->fType == plAgeFile::kAssetFile ) + { + EnableWindow( GetDlgItem( fhDlg, IDC_AGE_CHECKIN ), checkedOut ); + EnableWindow( GetDlgItem( fhDlg, IDC_AGE_UNDOCHECKOUT ), checkedOut ); + EnableWindow( GetDlgItem( fhDlg, IDC_AGE_CHECKOUT ), !checkedOut ); + } + else + { + EnableWindow( GetDlgItem( fhDlg, IDC_AGE_CHECKIN ), false ); + EnableWindow( GetDlgItem( fhDlg, IDC_AGE_UNDOCHECKOUT ), false ); + EnableWindow( GetDlgItem( fhDlg, IDC_AGE_CHECKOUT ), false ); + } +} + +void plAgeDescInterface::IEnablePageControls(bool enable) +{ + EnableWindow(GetDlgItem(fhDlg, IDC_ADM_DONTLOAD), enable); + EnableWindow(GetDlgItem(fhDlg, IDC_ADM_LOADSDL), enable); + EnableWindow(GetDlgItem(fhDlg, IDC_ADM_LOCAL_ONLY), enable); + EnableWindow(GetDlgItem(fhDlg, IDC_ADM_VOLATILE), enable); +} + +bool plAgeDescInterface::IGetLocalAgePath(char *path) +{ + // Get the path to the description folder + const char *plasmaPath = plMaxConfig::GetClientPath(); + if (!plasmaPath) + return false; + + strcpy(path, plasmaPath); + strcat(path, plAgeDescription::kAgeDescPath); + + // Make sure the desc folder exists + CreateDirectory(path, NULL); + + return true; +} + +int plAgeDescInterface::IFindAge(const char* ageName, vector& ageFiles) +{ + for (int i = 0; i < ageFiles.size(); i++) + if (ageFiles[i]->fAgeName == ageName) + return i; + + return -1; +} + +void plAgeDescInterface::IGetAgeFiles(vector& ageFiles) +{ + IClearAgeFiles(ageFiles); + + char agePath[MAX_PATH]; + + // Make list of "local" ages. This might contain copies of those in AssetMan, so we make the + // list first and take out the ones that are in AssetMan + char localPath[MAX_PATH]; + if (IGetLocalAgePath(localPath)) + { + hsFolderIterator ageFolder(localPath); + while (ageFolder.NextFileSuffix(".age")) + { + ageFolder.GetPathAndName(agePath); + + plAgeFile* age = TRACKED_NEW plAgeFile(plAgeFile::kLocalFile, agePath); + ageFiles.push_back(age); + } + } + + // Add AssetMan ages, if available (since we're static, go thru the main MaxAss interface) + MaxAssInterface *assetMan = GetMaxAssInterface(); + if( assetMan!= nil ) + { + hsTArray doneAssets; + + jvArray* assets = assetMan->GetAssetsByType(MaxAssInterface::kTypeAge); + for (int i = 0; i < assets->Size(); i++) + { + if( doneAssets.Find( (*assets)[ i ] ) == doneAssets.kMissingIndex ) + { + if (assetMan->GetLatestVersionFile((*assets)[i], agePath, sizeof(agePath))) + { + plAgeFile* age = TRACKED_NEW plAgeFile(plAgeFile::kAssetFile, agePath, (*assets)[i]); + + int existing = IFindAge(age->fAgeName.c_str(), ageFiles); + // Remove it from our "local" list if there, since it's a duplicate + if (existing != -1) + { + delete ageFiles[existing]; + ageFiles[existing] = age; + } + else + ageFiles.push_back(age); + + doneAssets.Append( (*assets)[ i ] ); + } + } + } + assets->DeleteSelf(); + } +} + +void plAgeDescInterface::IClearAgeFiles(vector& ageFiles) +{ + for (int i = 0; i < ageFiles.size(); i++) + delete ageFiles[i]; + ageFiles.clear(); +} + +void plAgeDescInterface::BuildAgeFileList( hsTArray &ageList ) +{ + vector tempAgeFiles; + IGetAgeFiles(tempAgeFiles); + + for (int i = 0; i < tempAgeFiles.size(); i++) + { + ageList.Push(hsStrcpy(tempAgeFiles[i]->fPath.c_str())); + delete tempAgeFiles[ i ]; + } +} + +//// IFillAgeTree ///////////////////////////////////////////////////////////// +// Refreshes/inits the tree view of all ages we have to work with. If +// specified, will also get the latest version of the .age files from assetMan. +void plAgeDescInterface::IFillAgeTree( void ) +{ + HWND ageTree = GetDlgItem( fhDlg, IDC_AGE_LIST ); + + // Clear the tree first and add our two root headers + TreeView_DeleteAllItems(ageTree); + + if( fAssetManIface != nil ) + fAssetManBranch = SAddTreeItem(ageTree, nil, "AssetMan Ages", -1); + else + fAssetManBranch = nil; + fLocalBranch = SAddTreeItem(ageTree, nil, "Local Ages", -1); + + IGetAgeFiles(fAgeFiles); + + // Add the ages to the tree + for (int i = 0; i < fAgeFiles.size(); i++) + { + SAddTreeItem(ageTree, + (fAgeFiles[i]->fType == plAgeFile::kAssetFile) ? fAssetManBranch : fLocalBranch, + fAgeFiles[i]->fAgeName.c_str(), + i); + } + + // Select the first age to view + IUpdateCurAge(); +} + +BOOL CALLBACK NewAgeDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static char *name = nil; + + switch (msg) + { + case WM_INITDIALOG: + name = (char*)lParam; + SetWindowText(hDlg, name); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK) + { + if (GetDlgItemText(hDlg, IDC_AGE_NAME, name, _MAX_FNAME) > 0) + EndDialog(hDlg, 1); + else + EndDialog(hDlg, 0); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, 0); + return TRUE; + } + } + + return FALSE; +} + +BOOL CALLBACK NewSeqNumberProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static char msg1[] = "This age currently does not have a sequence number assigned to it. All ages " + "must have a unique sequence number for multiplayer to work. Unassigned ages " + "will get a temporary random number at export time, but will result in undefined " + "(but probably bad) behavior during multiplayer games. In general, you should " + "always assign a sequence number to an age unless you have a very specific and " + "good reason not to."; + static char msg2[] = "The ADManager can find and assign a new, unique sequence number to this age for " + "you. You can choose to assign a normal or a global/reserved number. Normal ages " + "are ones for gameplay (that you can link to, walk around in, etc.), while " + "global/reserved ages are typically for storing data (such as avatars, GUI dialogs, etc.)"; + char msg3[ 512 ]; + + + switch (msg) + { + case WM_INITDIALOG: + SetDlgItemText( hDlg, IDC_INFOMSG, msg1 ); + SetDlgItemText( hDlg, IDC_ADMMSG, msg2 ); + sprintf( msg3, "Age: %s", (char *)lParam ); + SetDlgItemText( hDlg, IDC_AGEMSG, msg3 ); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED ) + { + EndDialog( hDlg, LOWORD( wParam ) ); + return TRUE; + } + } + + return FALSE; +} + +void plAgeDescInterface::INewAge() +{ + VARIANT assetId; + VariantInit(&assetId); + + bool makeAsset = true; + if( hsMessageBox( "Do you wish to store your new age in AssetMan?", "Make source-controlled?", hsMessageBoxYesNo ) == hsMBoxNo ) + makeAsset = false; + + char newAssetFilename[ MAX_PATH ]; + if (!fAssetManIface) + makeAsset = false; + + if( !IGetLocalAgePath( newAssetFilename ) ) + return; + + char name[_MAX_FNAME]; + strcpy(name, "New Age Name"); + + // Get the name of the new age from the user + int ret = DialogBoxParam(hInstance, + MAKEINTRESOURCE(IDD_AGE_NAME), + GetCOREInterface()->GetMAXHWnd(), + NewAgeDlgProc, + (LPARAM)name); + if (ret != 1) + return; + + strcat(newAssetFilename, name); + strcat(newAssetFilename, ".age"); + + if( !makeAsset ) + fForceSeqNumLocal = true; + ISetControlDefaults(); + ISaveCurAge(newAssetFilename, true); // Check sequence # while we're at it + fForceSeqNumLocal = false; + + if( makeAsset ) + (*fAssetManIface)->AddNewAsset(newAssetFilename); + + // Refresh the tree now + IFillAgeTree(); +} + +void plAgeDescInterface::INewPage() +{ + char name[256]; + strcpy(name, "New Page Name"); + + // Get the name of the new age from the user + int ret = DialogBoxParam(hInstance, + MAKEINTRESOURCE(IDD_AGE_NAME), + GetCOREInterface()->GetMAXHWnd(), + NewAgeDlgProc, + (LPARAM)name); + if (ret != 1) + return; + + HWND hPages = GetDlgItem(fhDlg, IDC_PAGE_LIST); + + // Make sure this page doesn't already exist + int count = ListBox_GetCount(hPages); + for (int i = 0; i < count; i++) + { + char pageName[256]; + ListBox_GetText(hPages, i, pageName); + if (!strcmp(pageName, name)) + return; + } + + // Add the new page and select it + int idx = ListBox_AddString(hPages, name); + + // Choose a new sequence suffix for it + plAgePage *newPage = TRACKED_NEW plAgePage( name, IGetFreePageSeqSuffix( hPages ), 0 ); + ListBox_SetItemData( hPages, idx, (LPARAM)newPage ); + + fDirty = true; +} + +UInt32 plAgeDescInterface::IGetFreePageSeqSuffix( HWND pageCombo ) +{ + int i, count = ListBox_GetCount( pageCombo ); + UInt32 searchSeq = 1; + + do + { + for( i = 0; i < count; i++ ) + { + plAgePage *page = (plAgePage *)ListBox_GetItemData( pageCombo, i ); + if( page != nil && page->GetSeqSuffix() == searchSeq ) + { + searchSeq++; + break; + } + } + + } while( i < count ); + + return searchSeq; +} + +void plAgeDescInterface::ISaveCurAge( const char *path, hsBool checkSeqNum ) +{ + hsUNIXStream s; + if( !s.Open( path, "wt" ) ) + { + hsMessageBox("Unable to open the Age Description file for writing. Updates not saved.", "Error", hsMessageBoxNormal); + return; + } + + plAgeDescription aged; + aged.SetAgeNameFromPath( path ); + + // set the date and time + HWND hDate = GetDlgItem(fhDlg, IDC_DATE); + SYSTEMTIME dst = {0}; + DateTime_GetSystemtime(hDate, &dst); + HWND hTime = GetDlgItem(fhDlg, IDC_TIME); + SYSTEMTIME tst = {0}; + DateTime_GetSystemtime(hTime, &tst); + aged.SetStart(dst.wYear,dst.wMonth,dst.wDay,tst.wHour,tst.wMinute,tst.wSecond); + aged.SetDayLength(fSpin->GetFVal()); + aged.SetMaxCapacity(fCapSpin->GetIVal()); + if( checkSeqNum ) + { + ICheckSequenceNumber( aged ); + } + else if( IsDlgButtonChecked( fhDlg, IDC_RSVDCHECK ) ) + { + // Store reserved sequence prefix + aged.SetSequencePrefix( -fSeqPrefixSpin->GetIVal() ); + } + else + { + aged.SetSequencePrefix( fSeqPrefixSpin->GetIVal() ); + } + + // gather pages + HWND hPages = GetDlgItem(fhDlg, IDC_PAGE_LIST); + int count = ListBox_GetCount(hPages); + if (count != LB_ERR) + { + for (int i = 0; i < count; i++) + { + char pageName[256]; + ListBox_GetText(hPages, i, pageName); + plAgePage *page = (plAgePage *)ListBox_GetItemData( hPages, i ); + aged.AppendPage( pageName, page->GetSeqSuffix(), page->GetFlags() ); + } + } + + // write it all out + aged.Write(&s); + s.Close(); +} + +//// ICheckSequenceNumber ///////////////////////////////////////////////////// +// Checks to make sure the sequence prefix is valid. If not, asks to assign +// a good one. + +void plAgeDescInterface::ICheckSequenceNumber( plAgeDescription &aged ) +{ + if( aged.GetSequencePrefix() == 0 ) // Default of uninitialized + { + // Ask about the sequence # + int ret = DialogBoxParam( hInstance, MAKEINTRESOURCE( IDD_AGE_SEQNUM ), + GetCOREInterface()->GetMAXHWnd(), + NewSeqNumberProc, (LPARAM)aged.GetAgeName() ); + if( ret == IDYES ) + { + aged.SetSequencePrefix( IGetNextFreeSequencePrefix( false ) ); + fDirty = true; + } + else if( ret == IDNO ) + { + aged.SetSequencePrefix( IGetNextFreeSequencePrefix( true ) ); + fDirty = true; + } + } +} + +void plAgeDescInterface::ILoadAge( const char *path, hsBool checkSeqNum ) +{ + ISetControlDefaults(); + + fDirty = false; + + // create and read the age desc + plAgeDescription aged( path ); + + // Get the name of the age + char ageName[_MAX_FNAME]; + _splitpath( path, nil, nil, ageName, nil ); + + // Check the sequence prefix # + if( checkSeqNum ) + ICheckSequenceNumber( aged ); + + char str[ _MAX_FNAME + 30 ]; + sprintf( str, "Description for %s", ageName ); + SetDlgItemText( fhDlg, IDC_AGEDESC, str ); + + // Set up the Dlgs + SYSTEMTIME st; + + HWND hTime = GetDlgItem(fhDlg, IDC_TIME); + memset(&st,0, sizeof(st)); + st.wYear = 2000; + st.wMonth = 1; + st.wDay = 1; + st.wHour = aged.GetStartHour(); + st.wMinute = aged.GetStartMinute(); + st.wSecond = aged.GetStartSecond(); + DateTime_SetSystemtime(hTime, GDT_VALID, &st); + + + HWND hDate = GetDlgItem(fhDlg, IDC_DATE); + memset(&st,0, sizeof(st)); + st.wMonth = aged.GetStartMonth(); + st.wDay = aged.GetStartDay(); + st.wYear = aged.GetStartYear(); + DateTime_SetSystemtime(hDate, GDT_VALID, &st); + + + fSpin->SetValue(aged.GetDayLength(), FALSE); + + int maxCap = aged.GetMaxCapacity(); + if (maxCap == -1) + { + maxCap = kDefaultCapacity; + fDirty = true; + } + fCapSpin->SetValue(maxCap, FALSE); + + Int32 seqPrefix = aged.GetSequencePrefix(); + if( seqPrefix < 0 ) + { + // Reserved prefix + fSeqPrefixSpin->SetValue( (int)( -seqPrefix ), FALSE ); + CheckDlgButton( fhDlg, IDC_RSVDCHECK, BST_CHECKED ); + } + else + { + fSeqPrefixSpin->SetValue( (int)seqPrefix, FALSE ); + CheckDlgButton( fhDlg, IDC_RSVDCHECK, BST_UNCHECKED ); + } + + // Disable the registry controls for now + EnableWindow( GetDlgItem( fhDlg, IDC_RSVDCHECK ), false ); + EnableWindow( GetDlgItem( fhDlg, IDC_SEQPREFIX_EDIT ), false ); + EnableWindow( GetDlgItem( fhDlg, IDC_SEQPREFIX_SPIN ), false ); + + aged.SeekFirstPage(); + plAgePage *page; + + HWND hPage = GetDlgItem(fhDlg, IDC_PAGE_LIST); + while( ( page = aged.GetNextPage() ) != nil ) + { + int idx = ListBox_AddString( hPage, page->GetName() ); + ListBox_SetItemData( hPage, idx, (LPARAM)new plAgePage( *page ) ); + } +} + +UInt32 plAgeDescInterface::IGetNextFreeSequencePrefix( hsBool getReservedPrefix ) +{ + Int32 searchSeq = getReservedPrefix ? -1 : 1; + hsTArray ageList; + int i; + + hsTArray ages; + + + if( fForceSeqNumLocal ) + searchSeq = getReservedPrefix ? -1024 : 1024; + + IGetAgeFiles(fAgeFiles); + + ages.SetCount( fAgeFiles.size() ); + for( i = 0; i < fAgeFiles.size(); i++ ) + { + hsUNIXStream stream; + if( stream.Open( fAgeFiles[ i ]->fPath.c_str(), "rt" ) ) + { + ages[ i ].Read( &stream ); + stream.Close(); + } + } + + do + { + if( getReservedPrefix ) + { + for( i = 0; i < ages.GetCount(); i++ ) + { + if( ages[ i ].GetSequencePrefix() == searchSeq ) + { + searchSeq--; + break; + } + } + } + else + { + for( i = 0; i < ages.GetCount(); i++ ) + { + if( ages[ i ].GetSequencePrefix() == searchSeq ) + { + searchSeq++; + break; + } + } + } + } while( i < ages.GetCount() ); + + return searchSeq; +} + +plAgeFile* plAgeDescInterface::IGetCurrentAge( void ) +{ + HWND ageTree = GetDlgItem( fhDlg, IDC_AGE_LIST ); + fCurrAgeItem = TreeView_GetSelection( ageTree ); + if( fCurrAgeItem == nil ) + return nil; + + int idx = SGetTreeData( ageTree, fCurrAgeItem ); + if (idx == -1) + return nil; + + return fAgeFiles[idx]; +} + +//// SAddTreeItem ///////////////////////////////////////////////////////////// +// Static helper function for adding an item to a treeView + +static HTREEITEM SAddTreeItem( HWND hTree, HTREEITEM hParent, const char *label, int userData ) +{ + TVITEM tvi = {0}; + tvi.mask = TVIF_TEXT | TVIF_PARAM; + tvi.pszText = (char *)label; + tvi.cchTextMax = strlen( label ); + tvi.lParam = (LPARAM)userData; + if( userData == -1 ) + { + tvi.mask |= TVIF_STATE; + tvi.state = tvi.stateMask = TVIS_BOLD | TVIS_EXPANDED; + } + + TVINSERTSTRUCT tvins = {0}; + tvins.item = tvi; + tvins.hParent = hParent; + tvins.hInsertAfter = TVI_LAST; + + return TreeView_InsertItem( hTree, &tvins ); +} + +//// SGetTreeData ///////////////////////////////////////////////////////////// +// Gets the tree data for the given item + +int SGetTreeData( HWND tree, HTREEITEM item ) +{ + TVITEM itemInfo; + itemInfo.mask = TVIF_PARAM | TVIF_HANDLE; + itemInfo.hItem = item; + TreeView_GetItem( tree, &itemInfo ); + return itemInfo.lParam; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plAgeDescInterface.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plAgeDescInterface.h new file mode 100644 index 00000000..df973da3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plAgeDescInterface.h @@ -0,0 +1,121 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsWindows.h" +#include "hsTypes.h" +#include "hsTemplates.h" +#include +#include + +using std::vector; + +class ISpinnerControl; +class plAgeDescription; +class plAgeFile; + +typedef struct _TREEITEM *HTREEITEM; + +class MaxAssBranchAccess; +class plAgeDescInterface +{ +protected: + HWND fhDlg; + bool fDirty; + int fCurAge; + + ISpinnerControl *fSpin; + ISpinnerControl *fCapSpin; + ISpinnerControl *fSeqPrefixSpin; + + HTREEITEM fCurrAgeItem; + bool fCurrAgeCheckedOut; + char fCheckedOutPath[ MAX_PATH ]; + bool fForceSeqNumLocal; + + HTREEITEM fAssetManBranch, fLocalBranch; + + HFONT fBoldFont; + HBRUSH fHiliteBrush; + + vector fAgeFiles; +// vector<_variant_t> fAssetIds; + + MaxAssBranchAccess *fAssetManIface; + + plAgeDescInterface(); + +public: + ~plAgeDescInterface(); + static plAgeDescInterface& Instance(); + + // Open the dialog + void Open(); + + static BOOL CALLBACK ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + static void BuildAgeFileList( hsTArray &ageList ); + +protected: + static int IFindAge(const char* ageName, vector& ageFiles); + static void IGetAgeFiles(vector& ageFiles); + static void IClearAgeFiles(vector& ageFiles); + + void IResetParams(); + + void IInitControls(); + void ISetControlDefaults(); + void IEnableControls(bool enable); + void IEnablePageControls(bool enable); + void ICheckedPageFlag(int ctrlID); + + // Save the settings for the last age and load the settings for the currently one + void IUpdateCurAge(); + void ISaveCurAge( const char *path, hsBool checkSeqNum = false ); + void ILoadAge( const char *path, hsBool checkSeqNum = false ); + + static bool IGetLocalAgePath(char *path); + + // Fill out the age tree view + void IFillAgeTree( void ); + + // Create a new age file and select it in the browser + void INewAge(); + void INewPage(); + + + UInt32 IGetNextFreeSequencePrefix( hsBool getReservedPrefix ); + UInt32 IGetFreePageSeqSuffix( HWND pageCombo ); + + void ICheckOutCurrentAge( void ); + void ICheckInCurrentAge( void ); + void IUndoCheckOutCurrentAge( void ); + hsBool IMakeSureCheckedIn( void ); + + plAgeFile* IGetCurrentAge( void ); + + void IInvalidateCheckOutIndicator( void ); + void ICheckSequenceNumber( plAgeDescription &aged ); +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plCommonObjLib.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plCommonObjLib.cpp new file mode 100644 index 00000000..f5810285 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plCommonObjLib.cpp @@ -0,0 +1,224 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plCommonObjLib - Base class for a library of objects that are stored // +// in common pages (for now, that means Textures or // +// BuiltIn pages). // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "HeadSpin.h" +#include "hsTypes.h" +#include "plCommonObjLib.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnKeyedObject/plUoid.h" +#include "plPluginResManager.h" + + +////////////////////////////////////////////////////////////////////////////// +//// Static Array And Functions ////////////////////////////////////////////// +// Used only by the export resManager, to create and maintain a list of the +// commonObjLibs to be used. +////////////////////////////////////////////////////////////////////////////// + +class plCommonObjLibList +{ + public: + UInt32 fRefCount; + hsTArray fLibs; + + plCommonObjLibList() { fRefCount = 0; } + + void Add( plCommonObjLib *lib ) + { + fLibs.Append( lib ); + fRefCount++; + } + + hsBool Remove( plCommonObjLib *lib ) + { + int idx = fLibs.Find( lib ); + if( idx != fLibs.kMissingIndex ) + fLibs.Remove( idx ); + else + { + hsAssert( false, "Common Object Lib not found in list upon deletion. Are you misusing this class? Tsk tsk!" ); + } + + fRefCount--; + return ( fRefCount == 0 ) ? true : false; + } +}; + +plCommonObjLibList *plCommonObjLib::fLibList = nil; + +UInt32 plCommonObjLib::GetNumLibs( void ) +{ + return ( fLibList != nil ) ? fLibList->fLibs.GetCount() : 0; +} + +plCommonObjLib *plCommonObjLib::GetLib( UInt32 idx ) +{ + if( fLibList == nil ) + return nil; + + if( idx < fLibList->fLibs.GetCount() ) + return fLibList->fLibs[ idx ]; + + return nil; +} + + +////////////////////////////////////////////////////////////////////////////// +//// Constructor/Destructor ////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +plCommonObjLib::plCommonObjLib() +{ + // Make sure we have a list to add ourselves to + if( fLibList == nil ) + fLibList = TRACKED_NEW plCommonObjLibList(); + + // Add ourselves to the list of libs + fLibList->Add( this ); +} + +plCommonObjLib::~plCommonObjLib() +{ + ClearObjectList(); + + // Remove ourselves from the list of libs + if( fLibList->Remove( this ) ) + { + // List is no longer needed + delete fLibList; + fLibList = nil; + } +} + + +////////////////////////////////////////////////////////////////////////////// +//// Base Utility Functions ////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// ClearObjectList ///////////////////////////////////////////////////////// + +void plCommonObjLib::ClearObjectList( void ) +{ + int i; + + + // Unref our object list, so they'll go away properly + for( i = 0; i < fObjects.GetCount(); i++ ) + fObjects[ i ]->GetKey()->UnRefObject(); + fObjects.Reset(); +} + +//// AddObject /////////////////////////////////////////////////////////////// +// Adds the given object to our lib. The object must have a key already. + +void plCommonObjLib::AddObject( hsKeyedObject *object ) +{ + if( object == nil || object->GetKey() == nil ) + { + hsAssert( false, "Trying to add an object to a commonLib that doesn't have a key" ); + return; + } + + // Ref it so it won't go away on us + object->GetKey()->RefObject(); + fObjects.Append( object ); +} + +//// RemoveObjectAndKey ////////////////////////////////////////////////////// +// Given the key to an object, completely nukes the object and the key. After +// this function call, the key should no longer exist in the registry and be +// free to use elsewhere. + +hsBool plCommonObjLib::RemoveObjectAndKey( plKey &key ) +{ + if (!key) + { + hsAssert( false, "Received RemoveObjectAndKey() call for a key that is invalid. Nillifying key anyway." ); + key = nil; + return true; + } + hsKeyedObject *object = hsKeyedObject::ConvertNoRef( key->ObjectIsLoaded() ); + if( object == nil ) + { + hsAssert( false, "Received RemoveObjectAndKey() call for a key that isn't loaded. Nillifying key anyway." ); + key = nil; + return true; + } + + int idx = fObjects.Find( object ); + if( idx == fObjects.kMissingIndex ) + { + hsAssert( false, "Trying to RemoveObjectAndKey() for a common object not in the lib." ); + key = nil; + return true; + } + + // Unref and remove from our list + fObjects[ idx ]->GetKey()->UnRefObject(); + fObjects.Remove( idx ); + + // Nuke out the key and its object + if( !plPluginResManager::ResMgr()->NukeKeyAndObject( key ) ) + { + hsAssert( false, "Trouble nuking out the key for this texture. Problems abound...." ); + return false; + } + + // All done! + return true; +} + +//// FindObject ////////////////////////////////////////////////////////////// +// Given a name and an optional class type, tries to find that object in +// our lib. Returns nil if not found. Use to find out if you already have a +// object of a given name that was previously exported. + +hsKeyedObject *plCommonObjLib::FindObject( const char *name, UInt16 classType /* = -1 */ ) +{ + int i; + + + for( i = 0; i < fObjects.GetCount(); i++ ) + { + const plUoid &uoid = fObjects[ i ]->GetKey()->GetUoid(); + + + if( stricmp( uoid.GetObjectName(), name ) == 0 && + ( classType == (UInt16)-1 || classType == uoid.GetClassType() ) ) + { + return fObjects[ i ]; + } + } + + return nil; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plCommonObjLib.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plCommonObjLib.h new file mode 100644 index 00000000..dc6fb112 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plCommonObjLib.h @@ -0,0 +1,92 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plCommonObjLib - Base class for a library of objects that are stored // +// in common pages (for now, that means Textures or // +// BuiltIn pages). // +// // +//// How To Use ////////////////////////////////////////////////////////////// +// // +// Derive from this class and override the IsInteresting function to // +// filter out which objects you're interested in. Then, create an object // +// of this type (static is fine) before the export process begins. // +// // +// Then, when you are going to create an object in a common page, call // +// this lib to see if the object is already created. If so, it'll already // +// be loaded and ready to go, and you shouldn't create another one. Of // +// course, you always have the option of deleting it and creating another. // +// // +// The single biggest limitation is that objects in common pages can NOT // +// have ANY refs to ANY other objects in ANY other page. Otherwise, their // +// refs will get completely screwed up at export time. So don't do it!!! // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plCommonObjLib_h +#define _plCommonObjLib_h + +#include "hsTemplates.h" + + +//// Class Definition ///////////////////////////////////////////////////////// + +class plCommonObjLibList; +class hsKeyedObject; +class plKey; +class plCommonObjLib +{ + protected: + + hsTArray fObjects; + + public: + + plCommonObjLib(); + virtual ~plCommonObjLib(); + + /// Base utility functions + + void AddObject( hsKeyedObject *object ); + hsBool RemoveObjectAndKey( plKey &key ); + hsKeyedObject *FindObject( const char *name, UInt16 classType = (UInt16)-1 ); + void ClearObjectList( void ); + + /// THIS IS YOUR VIRTUAL HERE. Override this to define which objects you collect + virtual hsBool IsInteresting( const plKey &objectKey ) { return false; } + + + /// Static functions for use only by the export resManager + static UInt32 GetNumLibs( void ); + static plCommonObjLib *GetLib( UInt32 idx ); + + + private: + + static plCommonObjLibList *fLibList; +}; + +#endif //_plCommonObjLib_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentDlg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentDlg.cpp new file mode 100644 index 00000000..33d89332 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentDlg.cpp @@ -0,0 +1,1027 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "iparamb2.h" + +#include "plComponentDlg.h" +#include "../MaxComponent/plComponentBase.h" +#include "../MaxComponent/plComponentMgr.h" +#include "../MaxComponent/plComponentReg.h" +#include "resource.h" +#include "plMaxNode.h" +#include "plComponentPanel.h" +#include "plMaxAccelerators.h" + +#include + +extern HINSTANCE hInstance; + +plComponentDlg::plComponentDlg() : fhDlg(nil), fCompMenu(nil), fTypeMenu(nil), fCommentNode(nil) +{ + fInterface = GetCOREInterface(); + + RegisterNotification(INotify, 0, NOTIFY_FILE_PRE_OPEN); + RegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_NEW); + RegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_RESET); + RegisterNotification(INotify, 0, NOTIFY_FILE_PRE_MERGE); + RegisterNotification(INotify, 0, NOTIFY_PRE_IMPORT); + RegisterNotification(INotify, 0, NOTIFY_FILE_PRE_SAVE); + RegisterNotification(INotify, 0, NOTIFY_FILE_PRE_SAVE_OLD); + + RegisterNotification(INotify, 0, NOTIFY_FILE_POST_OPEN); + RegisterNotification(INotify, 0, NOTIFY_SYSTEM_POST_NEW); + RegisterNotification(INotify, 0, NOTIFY_SYSTEM_POST_RESET); + RegisterNotification(INotify, 0, NOTIFY_FILE_POST_MERGE); + RegisterNotification(INotify, 0, NOTIFY_POST_IMPORT); + + RegisterNotification(INotify, 0, NOTIFY_SYSTEM_SHUTDOWN); +} + +plComponentDlg::~plComponentDlg() +{ + if (fhDlg) + { + fInterface->UnRegisterDlgWnd(fhDlg); + DestroyWindow(fhDlg); + } + if (fCompMenu) + DestroyMenu(fCompMenu); + if (fTypeMenu) + DestroyMenu(fTypeMenu); +} + +plComponentDlg& plComponentDlg::Instance() +{ + static plComponentDlg theInstance; + return theInstance; +} + +void plComponentDlg::Open() +{ + if (!fhDlg) + { + fhDlg = CreateDialog(hInstance, + MAKEINTRESOURCE(IDD_COMP_MAIN), + GetCOREInterface()->GetMAXHWnd(), + ForwardDlgProc); + + GetWindowRect(fhDlg, &fLastRect); + fSmallestSize.x = fLastRect.right - fLastRect.left; + fSmallestSize.y = fLastRect.bottom - fLastRect.top; + + RECT rect; + memcpy(&rect, &fLastRect, sizeof(RECT)); + rect.right = rect.left + 235; + rect.bottom = rect.top + 335; + IPositionControls(&rect, WMSZ_BOTTOM); + SetWindowPos(fhDlg, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOZORDER); + } + + fInterface->RegisterDlgWnd(fhDlg); + ShowWindow(fhDlg, SW_SHOW); + + if (IsIconic(fhDlg)) + ShowWindow(fhDlg, SW_RESTORE); +} + +void plComponentDlg::IPositionControls(RECT *newRect, int edge) +{ + // Get the new width and height + int newW = newRect->right - newRect->left; + int newH = newRect->bottom - newRect->top; + + // If an edge we don't support is being dragged, don't allow the resize. + if (!(edge == WMSZ_BOTTOM || + edge == WMSZ_BOTTOMRIGHT || + edge == WMSZ_RIGHT)) + { + memcpy(newRect, &fLastRect, sizeof(RECT)); + return; + } + + // If the width or height is too small, set it to the minimum + if (newW < fSmallestSize.x) + newRect->right = newRect->left + fSmallestSize.x; + if (newH < fSmallestSize.y) + newRect->bottom = newRect->top + fSmallestSize.y; + + // Calculate the new width and height + int hDiff = (newRect->bottom - newRect->top) - (fLastRect.bottom - fLastRect.top); + int wDiff = (newRect->right - newRect->left) - (fLastRect.right - fLastRect.left); + + // Copy our new rect to the last rect + memcpy(&fLastRect, newRect, sizeof(RECT)); + + // If the size has changed, reposition and resize controls + if (hDiff != 0 || wDiff != 0) + { + IPositionControl(GetDlgItem(fhDlg, IDC_TREE), hDiff, wDiff, kResizeX | kResizeY); + IPositionControl(GetDlgItem(fhDlg, IDC_COMMENT_TEXT), hDiff); + IPositionControl(GetDlgItem(fhDlg, IDC_COMMENTS), hDiff, wDiff, kResizeX | kMoveY); + IPositionControl(GetDlgItem(fhDlg, IDC_ATTACH), hDiff); + } + + InvalidateRect(fhDlg, NULL, TRUE); +} + +void plComponentDlg::IPositionControl(HWND hControl, int hDiff, int wDiff, int flags) +{ + RECT rect; + GetWindowRect(hControl, &rect); + + hsAssert(!((flags & kMoveX) & (flags & kResizeX)), "Moving AND resizing in X in IPositionControl"); + hsAssert(!((flags & kMoveY) & (flags & kResizeY)), "Moving AND resizing in Y in IPositionControl"); + + if (flags & kMoveX || flags & kMoveY) + { + POINT pos = { rect.left, rect.top }; + ScreenToClient(fhDlg, &pos); + if (flags & kMoveX) + pos.x += wDiff; + if (flags & kMoveY) + pos.y += hDiff; + + SetWindowPos(hControl, NULL, pos.x, pos.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOREDRAW); + } + + if (flags & kResizeX || flags & kResizeY) + { + int w = rect.right - rect.left; + int h = rect.bottom - rect.top; + if (flags & kResizeX) + w += wDiff; + if (flags & kResizeY) + h += hDiff; + + SetWindowPos(hControl, NULL, 0, 0, w, h, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW); + } +} + +void plComponentDlg::IGetComment() +{ + if (fCommentNode) + { + // Get the text from the edit and store it in the UserPropBuffer + int len = GetWindowTextLength(GetDlgItem(fhDlg, IDC_COMMENTS))+1; + if (len != 0) + { + char *buf = TRACKED_NEW char[len]; + GetDlgItemText(fhDlg, IDC_COMMENTS, buf, len); + fCommentNode->SetUserPropBuffer(buf); + delete [] buf; + } + else + fCommentNode->SetUserPropBuffer(""); + } +} + +BOOL plComponentDlg::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return Instance().DlgProc(hDlg, msg, wParam, lParam); +} + +#define MENU_ID_START 41000 + +BOOL plComponentDlg::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + fhDlg = hDlg; + IAddComponentsRecur(GetDlgItem(hDlg, IDC_TREE), (plMaxNode*)GetCOREInterface()->GetRootNode()); + + ICreateMenu(); + ICreateRightClickMenu(); + return TRUE; + + case WM_SIZING: + IPositionControls((RECT*)lParam, wParam); + return TRUE; + + case WM_ACTIVATE: + if (LOWORD(wParam) == WA_INACTIVE) + plMaxAccelerators::Enable(); + else + plMaxAccelerators::Disable(); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) + { + ShowWindow(hDlg, SW_HIDE); + fInterface->UnRegisterDlgWnd(hDlg); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ATTACH) + { + IAttachTreeSelection(); + return TRUE; + } + else if (HIWORD(wParam) == EN_KILLFOCUS && LOWORD(wParam) == IDC_COMMENTS) + { + IGetComment(); + return TRUE; + } + // "Refresh" menu item + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_REFRESH) + { + IRefreshTree(); + return TRUE; + } + // "Remove unused components" menu item + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_REMOVE_UNUSED) + { + IRemoveUnusedComps(); + return TRUE; + } + // Item selected from 'New' menu + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) >= MENU_ID_START) + { + ClassDesc *desc = plComponentMgr::Inst().Get(LOWORD(wParam)-MENU_ID_START); + // If this is a component type (not a category) + if (desc) + { + // Create an object of that type and a node to reference it + Object *obj = (Object*)GetCOREInterface()->CreateInstance(desc->SuperClassID(), desc->ClassID()); + INode *node = GetCOREInterface()->CreateObjectNode(obj); + + plComponentBase *comp = (plComponentBase*)obj; + node->Hide(!comp->AllowUnhide()); + node->Freeze(TRUE); + + // Add the new component to the tree + HWND hTree = GetDlgItem(hDlg, IDC_TREE); + HTREEITEM item = IAddComponent(hTree, (plMaxNode*)node); + TreeView_SelectItem(hTree, item); + TreeView_EnsureVisible(hTree, item); + } + } + break; + + case WM_NOTIFY: + NMHDR *nmhdr = (NMHDR*)lParam; + if (nmhdr->idFrom == IDC_TREE) + { + switch (nmhdr->code) + { + case TVN_SELCHANGED: + { + NMTREEVIEW *tv = (NMTREEVIEW*)lParam; + + IGetComment(); + + bool isComponent = IIsComponent(tv->itemNew.lParam); + + // If the new selection is a component, enable the attach button and comment field + EnableWindow(GetDlgItem(hDlg, IDC_ATTACH), isComponent); + SendDlgItemMessage(hDlg, IDC_COMMENTS, EM_SETREADONLY, !isComponent, 0); + + if (isComponent) + { + fCommentNode = (plMaxNode*)tv->itemNew.lParam; + + TSTR buf; + fCommentNode->GetUserPropBuffer(buf); + SetDlgItemText(hDlg, IDC_COMMENTS, buf); + } + else + { + fCommentNode = nil; + SetDlgItemText(hDlg, IDC_COMMENTS, ""); + } + + return TRUE; + } + break; + + case TVN_BEGINLABELEDIT: + // If this isn't a component, don't allow the edit + if (!IIsComponent(((NMTVDISPINFO*)lParam)->item.lParam)) + { + SetWindowLong(hDlg, DWL_MSGRESULT, TRUE); + return TRUE; + } + + // The edit box this creates kills the focus on our window, causing + // accelerators to be enabled. Add an extra disable to counteract that. + plMaxAccelerators::Disable(); + + return TRUE; + + // Finishing changing the name of a component + case TVN_ENDLABELEDIT: + { + NMTVDISPINFO *di = (NMTVDISPINFO*)lParam; + char* text = di->item.pszText; + // If the name was changed... + if (text && *text != '\0') + { + // Update the name of the node + plMaxNode *node = IGetTreeSelection(); + node->SetName(text); + + // Update the name in the panel too + if (plComponentUtil::Instance().IsOpen()) + plComponentUtil::Instance().IUpdateNodeName(node); + + // Make sure Max knows the file was changed + SetSaveRequiredFlag(); + + // Return true to keep the changes + SetWindowLong(hDlg, DWL_MSGRESULT, TRUE); + } + + plMaxAccelerators::Enable(); + } + return TRUE; + + // User double-clicked. Select the objects the selected component is attached to. + case NM_DBLCLK: + ISelectTreeSelection(); + return TRUE; + + case NM_RCLICK: + IOpenRightClickMenu(); + return TRUE; + + case TVN_KEYDOWN: + // User pressed delete + if (((NMTVKEYDOWN*)lParam)->wVKey == VK_DELETE) + { + IDeleteComponent(IGetTreeSelection()); + return TRUE; + } + break; + } + } + break; + } + + return FALSE; +} + +HTREEITEM plComponentDlg::IAddLeaf(HWND hTree, HTREEITEM hParent, const char *text, LPARAM lParam) +{ + TVITEM tvi = {0}; + tvi.mask = TVIF_TEXT | TVIF_PARAM; + tvi.pszText = (char*)text; + tvi.cchTextMax = strlen(text); + tvi.lParam = lParam; + + TVINSERTSTRUCT tvins = {0}; + tvins.item = tvi; + tvins.hParent = hParent; + tvins.hInsertAfter = TVI_SORT; + + return TreeView_InsertItem(hTree, &tvins); +} + +HTREEITEM plComponentDlg::IFindTreeItem(HWND hTree, const char *name, HTREEITEM hParent) +{ + HTREEITEM hChild = TreeView_GetChild(hTree, hParent); + + while (hChild) + { + char buf[256]; + TVITEM tvi; + tvi.mask = TVIF_TEXT; + tvi.hItem = hChild; + tvi.pszText = buf; + tvi.cchTextMax = sizeof(buf); + TreeView_GetItem(hTree, &tvi); + + if (!strcmp(name, tvi.pszText)) + return hChild; + + hChild = TreeView_GetNextSibling(hTree, hChild); + } + + return nil; +} + +HTREEITEM plComponentDlg::IAddComponent(HWND hTree, plMaxNode *node) +{ + plComponentBase *comp = node->ConvertToComponent(); + + // Try and find the component category in the tree + const char *category = comp->GetCategory(); + HTREEITEM hCat = IFindTreeItem(hTree, category, TVI_ROOT); + // If it isn't there yet, add it + if (!hCat) + hCat = IAddLeaf(hTree, TVI_ROOT, category, 0); + + // Try and find the component type in the tree + int idx = plComponentMgr::Inst().FindClassID(comp->ClassID()); + HTREEITEM hType = ISearchTree(hTree, idx+1, hCat); + if (!hType) + { + // If it isn't there yet, add it + TSTR type; + comp->GetClassName(type); + + if (IIsHidden(comp->ClassID())) + type.Append(" (Hidden)"); + + hType = IAddLeaf(hTree, hCat, type, idx+1); + } + + // Add the name of this component to this type + return IAddLeaf(hTree, hType, node->GetName(), (LPARAM)node); +} + +void plComponentDlg::IAddComponentsRecur(HWND hTree, plMaxNode *node) +{ + if (node->IsComponent()) + IAddComponent(hTree, node); + + for (int i = 0; i < node->NumberOfChildren(); i++) + { + plMaxNode *child = (plMaxNode*)node->GetChildNode(i); + IAddComponentsRecur(hTree, child); + } +} + +void plComponentDlg::ICreateMenu() +{ + // Add a refresh option to the system menu, for those rare cases where the manager gets out of sync + HMENU hMenu = GetMenu(fhDlg); + + HMENU hNew = CreatePopupMenu(); + InsertMenu(hMenu, 0, MF_POPUP | MF_STRING | MF_BYPOSITION, (UINT)hNew, "New"); + + const char *lastCat = nil; + HMENU hCurType = nil; + + UInt32 count = plComponentMgr::Inst().Count(); + for (UInt32 i = 0; i < count; i++) + { + plComponentClassDesc *desc = (plComponentClassDesc*)plComponentMgr::Inst().Get(i); + + // Don't put in the create menu if obsolete + if (desc->IsObsolete()) + continue; + + if (!lastCat || strcmp(lastCat, desc->Category())) + { + lastCat = desc->Category(); + + hCurType = CreatePopupMenu(); + AppendMenu(hNew, MF_POPUP | MF_STRING, (UINT)hCurType, lastCat); + } + + AppendMenu(hCurType, MF_STRING, MENU_ID_START+i, desc->ClassName()); + } +} + +// Taking advantage of the fact that the node pointers we store in the lParam +// will certainly be higher than the number of component types +bool plComponentDlg::IIsComponent(LPARAM lParam) +{ + return (lParam > plComponentMgr::Inst().Count()+1); +} + +bool plComponentDlg::IIsType(LPARAM lParam) +{ + return (lParam > 0 && lParam <= plComponentMgr::Inst().Count()+1); +} + +void plComponentDlg::IAttachTreeSelection() +{ + HWND hTree = GetDlgItem(fhDlg, IDC_TREE); + + // Get the current selection from the tree + HTREEITEM hSelected = TreeView_GetSelection(hTree); + TVITEM item; + item.mask = TVIF_PARAM; + item.hItem = hSelected; + TreeView_GetItem(hTree, &item); + + // If the item has a lParam it is a component + if (IIsComponent(item.lParam)) + { + plMaxNode *node = (plMaxNode*)item.lParam; + plComponentBase *comp = node->ConvertToComponent(); + + // Add each of the selected nodes that is not a component to the targets list + int count = fInterface->GetSelNodeCount(); + for (int i = 0; i < count; i++) + { + plMaxNode *target = (plMaxNode*)fInterface->GetSelNode(i); + if (!target->IsComponent()) + comp->AddTarget(target); + } + + // Update the rollups to reflect the new component + if (plComponentUtil::Instance().IsOpen()) + plComponentUtil::Instance().IUpdateRollups(); + } +} + +// Wow, this INodeTab class is very thorough +bool FindNodeInTab(INode *node, INodeTab& nodes) +{ + for (int i = 0; i < nodes.Count(); i++) + { + if (node == nodes[i]) + return true; + } + + return false; +} + +void plComponentDlg::SelectComponentTargs(INodeTab& nodes) +{ + // Make an INode tab with all the targets in it + INodeTab targets; + for (int i = 0; i < nodes.Count(); i++) + { + plComponentBase *comp = ((plMaxNode*)nodes[i])->ConvertToComponent(); + + for (int j = 0; j < comp->NumTargets(); j++) + { + INode *node = comp->GetTarget(j); + if (node && !FindNodeInTab(node, targets)) + targets.Append(1, &node); + } + } + + // If the user is selecting a single component, make sure it is selected in the rollup too + if (plComponentUtil::Instance().IsOpen() && nodes.Count() == 1) + plComponentUtil::Instance().fLastComponent = ((plMaxNode*)nodes[0])->ConvertToComponent(); + + theHold.Begin(); + fInterface->RedrawViews(fInterface->GetTime(), REDRAW_BEGIN); + fInterface->ClearNodeSelection(FALSE); // Deselect current nodes + + // If there is at least one valid target, select it + if (targets.Count() > 0) + fInterface->SelectNodeTab(targets, TRUE, FALSE); + + fInterface->RedrawViews(fInterface->GetTime(), REDRAW_END); + theHold.Accept("Select"); +} + +void plComponentDlg::ISelectTreeSelection() +{ + INodeTab nodes; + + INode *curComponent = (INode*)IGetTreeSelection(); + if (curComponent) + { + nodes.Append(1, &curComponent); + } + else + { + HWND hTree = GetDlgItem(fhDlg, IDC_TREE); + HTREEITEM hRoot = TreeView_GetSelection(hTree); + + IGetComponentsRecur(hTree, hRoot, nodes); + } + + SelectComponentTargs(nodes); +} + +void plComponentDlg::IGetComponentsRecur(HWND hTree, HTREEITEM hItem, INodeTab& nodes) +{ + if (hItem) + { + INode *node = (INode*)ITreeItemToNode(hTree, hItem); + if (node) + nodes.Append(1, &node); + else + { + HTREEITEM hChild = TreeView_GetChild(hTree, hItem); + IGetComponentsRecur(hTree, hChild, nodes); + + while (hChild = TreeView_GetNextSibling(hTree, hChild)) + { + IGetComponentsRecur(hTree, hChild, nodes); + } + } + } +} + +void plComponentDlg::IDeleteComponent(plMaxNode *component) +{ + if (!component) + return; + + // Make sure this components interface isn't showing + if (plComponentUtil::Instance().IsOpen()) + plComponentUtil::Instance().IComponentPreDelete(component->ConvertToComponent()); + + // Delete the component from the scene + theHold.Begin(); + fInterface->DeleteNode(component); + theHold.Accept(_T("Delete Component")); + + // Delete the component from the tree + HWND hTree = GetDlgItem(fhDlg, IDC_TREE); + HTREEITEM hItem = TreeView_GetSelection(hTree); + HTREEITEM hParent = TreeView_GetParent(hTree, hItem); + TreeView_DeleteItem(hTree, hItem); + + // If that was the only component of this type, delete the type too + if (!TreeView_GetChild(hTree, hParent)) + { + HTREEITEM hCategory = TreeView_GetParent(hTree, hParent); + TreeView_DeleteItem(hTree, hParent); + + // If this is the only type in this category, delete the category too! + // Sadly, this is the most we can delete. + if (!TreeView_GetChild(hTree, hCategory)) + TreeView_DeleteItem(hTree, hCategory); + } + + // Update the rollups in case the selected object had this component attached + if (plComponentUtil::Instance().IsOpen()) + plComponentUtil::Instance().IUpdateRollups(); +} + +plMaxNode *plComponentDlg::IGetTreeSelection() +{ + HWND hTree = GetDlgItem(fhDlg, IDC_TREE); + + HTREEITEM hItem = TreeView_GetSelection(hTree); + return ITreeItemToNode(hTree, hItem); +} + +plMaxNode *plComponentDlg::ITreeItemToNode(HWND hTree, HTREEITEM hItem) +{ + if (hItem) + { + TVITEM item; + item.mask = TVIF_PARAM; + item.hItem = hItem; + TreeView_GetItem(hTree, &item); + + if (IIsComponent(item.lParam)) + return (plMaxNode*)item.lParam; + } + + return nil; +} + +enum +{ + // Comp menu + kMenuDelete = 1, + kMenuRename, + kMenuCopy, + + // Type menu + kMenuHide +}; + +void plComponentDlg::ICreateRightClickMenu() +{ + fCompMenu = CreatePopupMenu(); + AppendMenu(fCompMenu, MF_STRING, kMenuDelete, "Delete"); + AppendMenu(fCompMenu, MF_STRING, kMenuRename, "Rename"); + AppendMenu(fCompMenu, MF_STRING, kMenuCopy, "Copy"); + + fTypeMenu = CreatePopupMenu(); + AppendMenu(fTypeMenu, MF_STRING, kMenuHide, "Hide/Show"); +} + +void plComponentDlg::IOpenRightClickMenu() +{ + HWND hTree = GetDlgItem(fhDlg, IDC_TREE); + + // Get the position of the cursor in screen and tree client coords + POINT point, localPoint; + GetCursorPos(&point); + localPoint = point; + ScreenToClient(hTree, &localPoint); + + // Check if there is a tree item at that point + TVHITTESTINFO hitTest; + hitTest.pt = localPoint; + TreeView_HitTest(hTree, &hitTest); + if (!(hitTest.flags & TVHT_ONITEMLABEL)) + return; + + // Check if the tree item has an lParam (is a component) + TVITEM item; + item.mask = TVIF_PARAM; + item.hItem = hitTest.hItem; + TreeView_GetItem(hTree, &item); + + HMENU menu = nil; + if (IIsComponent(item.lParam)) + menu = fCompMenu; + else if (IIsType(item.lParam)) + menu = fTypeMenu; + else + return; + + // Select the item we're working with, so the user isn't confused + TreeView_SelectItem(hTree, item.hItem); + + // Create the popup menu and get the option the user selects + SetForegroundWindow(fhDlg); + int sel = TrackPopupMenu(menu, TPM_NONOTIFY | TPM_RETURNCMD, point.x, point.y, 0, fhDlg, NULL); + switch(sel) + { + case kMenuDelete: + IDeleteComponent((plMaxNode*)item.lParam); + break; + + case kMenuRename: + TreeView_EditLabel(hTree, hitTest.hItem); + break; + + case kMenuCopy: + { + // Component to copy + INode *node = (INode*)item.lParam; + INodeTab tab; + tab.Append(1, &node); + + // Copy + INodeTab copy; + + // Make the copy + fInterface->CloneNodes(tab, Point3(0,0,0), true, NODE_COPY, NULL, ©); + + // Delete the targets for the copy and add it to the tree + plMaxNode *newNode = (plMaxNode*)copy[0]; + newNode->ConvertToComponent()->DeleteAllTargets(); + HTREEITEM hItem = IAddComponent(GetDlgItem(fhDlg, IDC_TREE), newNode); + TreeView_SelectItem(GetDlgItem(fhDlg, IDC_TREE), hItem); + } + break; + + case kMenuHide: + { + ClassDesc *desc = plComponentMgr::Inst().Get(item.lParam-1); + + std::vector::iterator it; + it = std::find(fHiddenComps.begin(), fHiddenComps.end(), desc->ClassID()); + + TSTR name = desc->ClassName(); + if (it == fHiddenComps.end()) + { + fHiddenComps.push_back(desc->ClassID()); + name.Append(" (Hidden)"); + } + else + fHiddenComps.erase(it); + + item.mask = TVIF_TEXT; + item.pszText = name; + TreeView_SetItem(GetDlgItem(fhDlg, IDC_TREE), &item); + + plComponentUtil::Instance().IUpdateRollups(); + } + break; + } + + PostMessage(fhDlg, WM_USER, 0, 0); +} + +HTREEITEM plComponentDlg::ISearchTree(HWND hTree, LPARAM lParam, HTREEITEM hCur) +{ + // Get the param for the current item + TVITEM tvi; + tvi.mask = TVIF_PARAM; + tvi.hItem = hCur; + TreeView_GetItem(hTree, &tvi); + + // If the lParam matches the one searching for, return the handle + if (tvi.lParam == lParam) + return hCur; + + // Do a recursive search on the items children + HTREEITEM hChild = TreeView_GetChild(hTree, hCur); + while (hChild) + { + HTREEITEM hResult = ISearchTree(hTree, lParam, hChild); + if (hResult) + return hResult; + + hChild = TreeView_GetNextSibling(hTree, hChild); + } + + return NULL; +} + +void plComponentDlg::IRefreshTree() +{ + if (fhDlg) + { + fCommentNode = nil; + + HWND hTree = GetDlgItem(fhDlg, IDC_TREE); + TreeView_DeleteAllItems(hTree); + IAddComponentsRecur(hTree, (plMaxNode*)GetCOREInterface()->GetRootNode()); + } +} + +void plComponentDlg::INotify(void *param, NotifyInfo *info) +{ + if (info->intcode == NOTIFY_SYSTEM_SHUTDOWN) + { + UnRegisterNotification(INotify, 0, NOTIFY_FILE_PRE_OPEN); + UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_NEW); + UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_RESET); + UnRegisterNotification(INotify, 0, NOTIFY_FILE_PRE_MERGE); + UnRegisterNotification(INotify, 0, NOTIFY_PRE_IMPORT); + UnRegisterNotification(INotify, 0, NOTIFY_FILE_PRE_SAVE); + UnRegisterNotification(INotify, 0, NOTIFY_FILE_PRE_SAVE_OLD); + + UnRegisterNotification(INotify, 0, NOTIFY_FILE_POST_OPEN); + UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_POST_NEW); + UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_POST_RESET); + UnRegisterNotification(INotify, 0, NOTIFY_FILE_POST_MERGE); + UnRegisterNotification(INotify, 0, NOTIFY_POST_IMPORT); + + UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_SHUTDOWN); + } + // New nodes are coming in, refresh the scene component list + else if (info->intcode == NOTIFY_FILE_POST_OPEN || + info->intcode == NOTIFY_SYSTEM_POST_NEW || + info->intcode == NOTIFY_SYSTEM_POST_RESET || + info->intcode == NOTIFY_FILE_POST_MERGE || + info->intcode == NOTIFY_POST_IMPORT) + { + Instance().IRefreshTree(); + } + // Nodes may be going away, save the comment now + else if (info->intcode == NOTIFY_FILE_PRE_OPEN || + info->intcode == NOTIFY_SYSTEM_PRE_NEW || + info->intcode == NOTIFY_SYSTEM_PRE_RESET || + info->intcode == NOTIFY_FILE_PRE_MERGE || + info->intcode == NOTIFY_PRE_IMPORT || + info->intcode == NOTIFY_FILE_PRE_SAVE || + info->intcode == NOTIFY_FILE_PRE_SAVE_OLD) + { + // This is causing a crash, so for now if you add a comment and don't + // pick another component or close the manager before closing the file, + // you lose the comment -Colin +// Instance().IGetComment(); + } +} + +void plComponentDlg::IUpdateNodeName(plMaxNode *node) +{ + if (!fhDlg) + return; + + // Update the name in the tree too + HWND hTree = GetDlgItem(fhDlg, IDC_TREE); + TVITEM tvi = {0}; + tvi.hItem = ISearchTree(hTree, (LPARAM)node); + tvi.mask = TVIF_TEXT; + tvi.pszText = node->GetName(); + TreeView_SetItem(hTree, &tvi); +} + +void FindUnusedCompsRecur(plMaxNode *node, std::vector& unused) +{ + plComponentBase *comp = node->ConvertToComponent(); + if (comp) + { + bool isAttached = false; + + int num = comp->NumTargets(); + for (int i = 0; i < num; i++) + { + if (comp->GetTarget(i)) + { + isAttached = true; + break; + } + } + + if (!isAttached) + unused.push_back(node); + } + + for (int i = 0; i < node->NumberOfChildren(); i++) + FindUnusedCompsRecur((plMaxNode*)node->GetChildNode(i), unused); +} + +void plComponentDlg::IRemoveUnusedComps() +{ + std::vector unused; + FindUnusedCompsRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), unused); + + for (int i = 0; i < unused.size(); i++) + GetCOREInterface()->DeleteNode(unused[i], FALSE); + + IRefreshTree(); +} + +bool plComponentDlg::IIsHidden(Class_ID& cid) +{ + return (std::find(fHiddenComps.begin(), fHiddenComps.end(), cid) != fHiddenComps.end()); +} + +//////////////////////////////////////////////////////////////////////////////// + +#include "hsUtils.h" + +class plCopyCompCallback : public HitByNameDlgCallback +{ +protected: + Tab fSharedComps; + INodeTab fSelectedNodes; + +public: + bool GetComponents() + { + fSelectedNodes.ZeroCount(); + fSharedComps.ZeroCount(); + + Interface *ip = GetCOREInterface(); + + int nodeCount = ip->GetSelNodeCount(); + if (nodeCount == 0) + return false; + + // Get the components shared among the selected nodes + int i; + fSelectedNodes.SetCount(nodeCount); + for (i = 0; i < nodeCount; i++) + fSelectedNodes[i] = ip->GetSelNode(i); + + INodeTab sharedComps; + if (plSharedComponents(fSelectedNodes, sharedComps) == 0) + return false; + + // Put the shared components in a list + fSharedComps.SetCount(sharedComps.Count()); + for (i = 0; i < sharedComps.Count(); i++) + fSharedComps[i] = ((plMaxNode*)sharedComps[i])->ConvertToComponent(); + + return true; + } + + virtual TCHAR *dialogTitle() { return "Select Nodes"; } + virtual TCHAR *buttonText() { return "Copy"; } + + virtual int filter(INode *node) + { + // Make sure this node doesn't already have the components + for (int i = 0; i < fSelectedNodes.Count(); i++) + { + if (fSelectedNodes[i] == node) + return FALSE; + } + + return TRUE; + } + + virtual void proc(INodeTab &nodeTab) + { + for (int i = 0; i < nodeTab.Count(); i++) + { + for (int j = 0; j < fSharedComps.Count(); j++) + { + fSharedComps[j]->AddTarget((plMaxNodeBase*)nodeTab[i]); + } + } + } +}; +static plCopyCompCallback copyCompCallback; + +void CopyComponents() +{ + if (copyCompCallback.GetComponents()) + GetCOREInterface()->DoHitByNameDialog(©CompCallback); + else + { + int count = GetCOREInterface()->GetSelNodeCount(); + if (count == 0) + hsMessageBox("No object(s) selected", "Component Copy", hsMessageBoxNormal); + else if (count > 1) + hsMessageBox("No components are shared among the selected objects", "Component Copy", hsMessageBoxNormal); + else + hsMessageBox("No components on the selected object", "Component Copy", hsMessageBoxNormal); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentDlg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentDlg.h new file mode 100644 index 00000000..35db69e6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentDlg.h @@ -0,0 +1,106 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "max.h" +#include "notify.h" + +#include + +class plMaxNode; + +class plComponentDlg +{ +protected: + friend class plComponentUtil; + + HWND fhDlg; + HMENU fCompMenu; + HMENU fTypeMenu; + + Interface *fInterface; + POINT fSmallestSize; + RECT fLastRect; + + // The node we're currently editing the comment for + plMaxNode *fCommentNode; + + std::vector fHiddenComps; + +public: + ~plComponentDlg(); + static plComponentDlg &Instance(); + + void Open(); + + void SelectComponentTargs(INodeTab& nodes); + +protected: + plComponentDlg(); + + static BOOL CALLBACK ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + void IPositionControls(RECT *newRect, int edge); + enum { kResizeX = 1, kResizeY = 2, kMoveX = 4, kMoveY = 8 }; + void IPositionControl(HWND hControl, int hDiff, int wDiff=0, int flags=kMoveY); + + HTREEITEM IAddLeaf(HWND hTree, HTREEITEM hParent, const char *text, LPARAM lParam); + // Search for an item in the tree by name, but only in the children of hParent + HTREEITEM IFindTreeItem(HWND hTree, const char *name, HTREEITEM hParent); + HTREEITEM IAddComponent(HWND hTree, plMaxNode *node); + void IAddComponentsRecur(HWND hTree, plMaxNode *node); + void ICreateMenu(); + + bool IIsComponent(LPARAM lParam); + bool IIsType(LPARAM lParam); + + void IAttachTreeSelection(); + void ISelectTreeSelection(); + plMaxNode *IGetTreeSelection(); + plMaxNode *ITreeItemToNode(HWND hTree, HTREEITEM hItem); + void IDeleteComponent(plMaxNode *component); + + void IGetComponentsRecur(HWND hTree, HTREEITEM hItem, INodeTab& nodes); + + void ICreateRightClickMenu(); + void IOpenRightClickMenu(); + + HTREEITEM ISearchTree(HWND hTree, LPARAM lParam, HTREEITEM hCur=TVGN_ROOT); + void IRefreshTree(); + + void IRemoveUnusedComps(); + + static void INotify(void *param, NotifyInfo *info); + + // To syncronize with plComponentUtil when a name is changed + void IUpdateNodeName(plMaxNode *node); + + void IGetComment(); + + bool IIsHidden(Class_ID& cid); +}; + +// Brings up the copy components dialog. Stuck here for no particular reason. +void CopyComponents(); \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentPanel.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentPanel.cpp new file mode 100644 index 00000000..c31f787a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentPanel.cpp @@ -0,0 +1,510 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plComponentPanel.h" +#include "resource.h" + +#include "plMaxNode.h" +#include "../MaxComponent/plComponent.h" +#include "../MaxComponent/plComponentMgr.h" +#include "plComponentDlg.h" +#include "plMaxAccelerators.h" + +extern TCHAR *GetString(int id); + +class ComponentUtilClassDesc : public ClassDesc +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return &plComponentUtil::Instance(); } + const TCHAR* ClassName() { return _T("Component Util"); } + SClass_ID SuperClassID() { return UTILITY_CLASS_ID; } + Class_ID ClassID() { return Class_ID(0xb220659, 0x31015552); } + const TCHAR* Category() { return _T(""); } +}; + +static ComponentUtilClassDesc theComponentUtilCD; +ClassDesc* GetComponentUtilDesc() { return &theComponentUtilCD; } + +plComponentUtil::plComponentUtil() : fInterface(nil), fhPanel(nil), fCurComponent(nil), fLastComponent(nil) +{ +} + +plComponentUtil& plComponentUtil::Instance() +{ + static plComponentUtil theInstance; + return theInstance; +} + +//////////////////////////////////////////////////////////////////////////////// +// Proc for the currently selected object dialog +// +BOOL CALLBACK plComponentUtil::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return Instance().DlgProc(hDlg, msg, wParam, lParam); +} + +BOOL plComponentUtil::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_COMMAND: + // Switch to next or previous target + if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDC_BACK || LOWORD(wParam) == IDC_FORWARD)) + { + INextTarget(LOWORD(wParam) == IDC_FORWARD); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_REF_BY_BUTTON) + { + IShowRefdBy(); + return TRUE; + } + break; + + case WM_NOTIFY: + { + NMHDR *nmhdr = (NMHDR*)lParam; + if (nmhdr->idFrom == IDC_COMPLIST) + { + switch (nmhdr->code) + { + // Stop Max from reading keypresses while the list has focus + case NM_SETFOCUS: + plMaxAccelerators::Disable(); + return TRUE; + case NM_KILLFOCUS: + plMaxAccelerators::Enable(); + return TRUE; + + case LVN_KEYDOWN: + { + NMLVKEYDOWN *kd = (NMLVKEYDOWN*)lParam; + if (kd->wVKey == VK_DELETE) + IDeleteListSelection(); + } + return TRUE; + + // The edit box this creates kills the focus on the listbox, + // so add an extra disable to ignore it + case LVN_BEGINLABELEDIT: + plMaxAccelerators::Disable(); + return TRUE; + + // Finishing changing the name of a component + case LVN_ENDLABELEDIT: + { + NMLVDISPINFO *di = (NMLVDISPINFO*)lParam; + const char *name = di->item.pszText; + + // If the name was changed... + if (name && *name != '\0') + { + // Update the name of the node + plComponentBase* comp = IGetListSelection(); + comp->GetINode()->SetName(di->item.pszText); + + // Make sure the column is wide enough + int width = ListView_GetStringWidth(nmhdr->hwndFrom, di->item.pszText)+10; + if (width > ListView_GetColumnWidth(nmhdr->hwndFrom, 0)) + { + ListView_SetColumnWidth(nmhdr->hwndFrom, 0, width); + InvalidateRect(nmhdr->hwndFrom, NULL, FALSE); + } + + // Update the name in the tree too + plComponentDlg::Instance().IUpdateNodeName((plMaxNode*)comp->GetINode()); + + // Return true to keep the changes + SetWindowLong(hDlg, DWL_MSGRESULT, TRUE); + } + + plMaxAccelerators::Enable(); + } + return TRUE; + + // Selected component has changed. This notification can come + // more than necessary, so IAddRollups doesn't change the rollups + // if the "new" one is the same as the old. + case LVN_ITEMCHANGED: + { + plComponentBase* comp = IGetListSelection(); + IAddRollups(comp); + } + return TRUE; + } + } + } + break; + } + + return FALSE; +} + +void plComponentUtil::IDeleteListSelection() +{ + plComponentBase* comp = IGetListSelection(); + if (comp) + { + // Delete each of the selected nodes from this components target list + int count = fInterface->GetSelNodeCount(); + for (int i = 0; i < count; i++) + { + plMaxNode *curNode = (plMaxNode*)fInterface->GetSelNode(i); + comp->DeleteTarget(curNode); + } + + IUpdateRollups(); + } +} + +plComponentBase* plComponentUtil::IGetListSelection() +{ + HWND hList = GetDlgItem(fhPanel, IDC_COMPLIST); + + int index = ListView_GetNextItem(hList, -1, LVNI_SELECTED); + if (index != -1) + { + LVITEM item; + item.mask = LVIF_PARAM; + item.iItem = index; + item.iSubItem = 0; + if (ListView_GetItem(hList, &item)) + return (plComponentBase*)item.lParam; + } + + return nil; +} + +void plComponentUtil::BeginEditParams(Interface *ip, IUtil *iu) +{ + fInterface = ip; + + fhPanel = fInterface->AddRollupPage(hInstance, MAKEINTRESOURCE(IDD_COMP_PANEL), ForwardDlgProc, "Components (Selected Obj)"); + + // Add a column. We don't use it (graphically), but it has to be there. + HWND hList = GetDlgItem(fhPanel, IDC_COMPLIST); + LVCOLUMN lvc; + lvc.mask = LVCF_TEXT; + lvc.pszText = "Description"; + ListView_InsertColumn(hList, 0, &lvc); + + IUpdateRollups(); +} + +void plComponentUtil::EndEditParams(Interface *ip, IUtil *iu) +{ + IDestroyRollups(); + + GetCOREInterface()->DeleteRollupPage(fhPanel); + fhPanel = nil; + fCurComponent = nil; + fInterface = nil; +} + +void plComponentUtil::SelectionSetChanged(Interface *ip, IUtil *iu) +{ + IUpdateRollups(); +} + +void plComponentUtil::IUpdateRollups() +{ + if (!fhPanel) + return; + + // Destroy any current rollups + IDestroyRollups(); + + HWND hList = GetDlgItem(fhPanel, IDC_COMPLIST); + ListView_DeleteAllItems(hList); + + // Check that something is selected. + int nodeCount = fInterface->GetSelNodeCount(); + if (nodeCount == 0) + { + IAddRollups(nil); + return; + } + + // Get the components shared among the selected nodes + int i; + INodeTab selNodes; + selNodes.SetCount(nodeCount); + for (i = 0; i < nodeCount; i++) + selNodes[i] = fInterface->GetSelNode(i); + + INodeTab sharedComps; + plSharedComponents(selNodes, sharedComps); + + // Add the shared components to the list + for (i = 0; i < sharedComps.Count(); i++) + { + plComponentBase *comp = ((plMaxNode*)sharedComps[i])->ConvertToComponent(); + + if (plComponentDlg::Instance().IIsHidden(comp->ClassID())) + continue; + + IParamBlock2 *pb = comp->GetParamBlockByID(plComponent::kBlkComp); + + LVITEM item = {0}; + item.mask = LVIF_TEXT | LVIF_PARAM; + item.pszText = sharedComps[i]->GetName(); + item.iItem = ListView_GetItemCount(hList); + item.lParam = (LPARAM)comp; + ListView_InsertItem(hList, &item); + } + + // Make sure the column is wide enough + ListView_SetColumnWidth(hList, 0, LVSCW_AUTOSIZE); + + // If there are rollups to show + if (ListView_GetItemCount(hList) > 0) + { + // Try and find the last used rollup + int idx = IFindListItem(fLastComponent); + + // If last one wasn't found, just use the first + if (idx == -1) + idx = 0; + + ListView_SetItemState(hList, idx, LVIS_SELECTED, LVIS_SELECTED); + ListView_EnsureVisible(hList, idx, FALSE); + } + else + IAddRollups(nil); +} + +int plComponentUtil::IFindListItem(plComponentBase* comp) +{ + LVFINDINFO fi; + fi.flags = LVFI_PARAM; + fi.lParam = (LPARAM)comp; + return ListView_FindItem(GetDlgItem(fhPanel, IDC_COMPLIST), -1, &fi); +} + +#include "../MaxComponent/plAutoUIComp.h" + +void plComponentUtil::IAddRollups(plComponentBase* comp) +{ + if (fCurComponent == comp) + return; + + IDestroyRollups(); + fCurComponent = comp; + if (comp) + fLastComponent = comp; + + // + // Update the targets dialog + // + UInt32 numTargs = 0; + if (fCurComponent) + { + // Only count non-nil targets + for (UInt32 i = 0; i < fCurComponent->NumTargets(); i++) + if (fCurComponent->GetTarget(i)) + numTargs++; + } + + // Put the number of targets in the text box + char buf[12]; + itoa(numTargs, buf, 10); + SetWindowText(GetDlgItem(fhPanel, IDC_NUM_TARGS), buf); + + // Enable the forward/back buttons if there are multiple targets + BOOL useButtons = (numTargs > 1); + EnableWindow(GetDlgItem(fhPanel, IDC_BACK), useButtons); + EnableWindow(GetDlgItem(fhPanel, IDC_FORWARD), useButtons); + + // + // Add the component rollups + // + if (fCurComponent) + fCurComponent->CreateRollups(); +} + +void plComponentUtil::IDestroyRollups() +{ + if (fCurComponent) + fCurComponent->DestroyRollups(); +} + +void plComponentUtil::INextTarget(bool forward) +{ +// fCurComponent = IGetListSelection(); +// plComponentBase *comp = fCurComponent->ConvertToComponent(); + + // Loop through the selected component's targets until we find the currently selected node. + // This gives us a starting point to find the next or previous target in this component's list. + plMaxNode *curNode = (plMaxNode*)GetCOREInterface()->GetSelNode(0); + UInt32 count = fCurComponent->NumTargets(); + for (UInt32 i = 0; i < count; i++) + { + if (fCurComponent->GetTarget(i) == curNode) + { + // Got to loop until the target is non-nil here, so we skip over + // any deleted nodes. + UInt32 targIdx = i; + do + { + // Figure out which target to change to + if (forward) + { + if (targIdx == count-1) + targIdx = 0; + else + targIdx = targIdx + 1; + } + else + { + if (targIdx == 0) + targIdx = count-1; + else + targIdx = targIdx - 1; + } + } while (!fCurComponent->GetTarget(targIdx)); + + // Select the new target + theHold.Begin(); + fInterface->RedrawViews(fInterface->GetTime(), REDRAW_BEGIN); + + fInterface->SelectNode(fCurComponent->GetTarget(targIdx)); + + fInterface->RedrawViews(fInterface->GetTime(), REDRAW_END); + theHold.Accept("Select"); + + return; + } + } +} + +void plComponentUtil::IUpdateNodeName(plMaxNode *node) +{ + if (!fhPanel) + return; + + // Update the name in the list + int idx = IFindListItem(node->ConvertToComponent()); + if (idx != -1) + { + HWND hList = GetDlgItem(fhPanel, IDC_COMPLIST); + ListView_SetItemText(hList, idx, 0, node->GetName()); + // Make sure the column is wide enough + ListView_SetColumnWidth(hList, 0, LVSCW_AUTOSIZE); + } +} + +void plComponentUtil::IComponentPreDelete(plComponentBase* comp) +{ + if (fCurComponent == comp) + { + IDestroyRollups(); + fCurComponent = nil; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +#include + +void IGetReferencesRecur(plMaxNode* node, INode* target, std::vector& nodes) +{ + plComponentBase* comp = node->ConvertToComponent(); + if (comp && comp->DoReferenceNode(target)) + { + const char* name = node->GetName(); + nodes.push_back(node); + } + + for (int i = 0; i < node->NumberOfChildren(); i++) + { + IGetReferencesRecur((plMaxNode*)node->GetChildNode(i), target, nodes); + } +} + +BOOL CALLBACK RefDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + if (GetCOREInterface()->GetSelNodeCount() > 0) + { + INode* node = GetCOREInterface()->GetSelNode(0); + + char buf[256]; + sprintf(buf, "%s is Ref'd By", node->GetName()); + SetWindowText(hDlg, buf); + + std::vector nodes; + IGetReferencesRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), node, nodes); + + HWND hList = GetDlgItem(hDlg, IDC_REF_LIST); + for (int i = 0; i < nodes.size(); i++) + { + plMaxNode* node = nodes[i]; + int idx = ListBox_AddString(hList, node->GetName()); + ListBox_SetItemData(hList, idx, node); + } + } + } + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_CLOSE) + { + EndDialog(hDlg, 0); + return TRUE; + } + else if (HIWORD(wParam) == LBN_DBLCLK && LOWORD(wParam) == IDC_REF_LIST) + { + // If the user double clicked on a component, return it so we can + // select the nodes it's attached to + HWND hList = HWND(lParam); + int sel = ListBox_GetCurSel(hList); + LRESULT node = ListBox_GetItemData(hList, sel); + EndDialog(hDlg, node); + return TRUE; + } + break; + } + + return FALSE; +} + +void plComponentUtil::IShowRefdBy() +{ + INode* node = (INode*)DialogBox(hInstance, + MAKEINTRESOURCE(IDD_REF_BY), + GetCOREInterface()->GetMAXHWnd(), + RefDlgProc); + if (node) + { + INodeTab nodes; + nodes.Append(1, &node); + plComponentDlg::Instance().SelectComponentTargs(nodes); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentPanel.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentPanel.h new file mode 100644 index 00000000..77c219b9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plComponentPanel.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "max.h" +#include "utilapi.h" +#include "notify.h" + +class plMaxNode; +class plComponentBase; + +class plComponentUtil : public UtilityObj +{ +protected: + friend class plComponentDlg; + + Interface *fInterface; + HWND fhPanel; + plComponentBase* fCurComponent; + // Used to try and load the last displayed component on reload, just for pointer compares + plComponentBase* fLastComponent; + + plComponentUtil(); + +public: + static plComponentUtil& Instance(); + + void BeginEditParams(Interface *ip, IUtil *iu); + void EndEditParams(Interface *ip, IUtil *iu); + void SelectionSetChanged(Interface *ip, IUtil *iu); + void DeleteThis() {}; + + static BOOL CALLBACK ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + bool IsOpen() { return (fhPanel != NULL); } + +protected: + void IUpdateRollups(); + void IAddRollups(plComponentBase* comp); + void IDestroyRollups(); + + void INextTarget(bool forward); + + plComponentBase *IGetListSelection(); + void IDeleteListSelection(); + int IFindListItem(plComponentBase* comp); + + void IShowRefdBy(); + + // plComponentDlg is about to delete this comp, get rid of it's interface + void IComponentPreDelete(plComponentBase* comp); + // To syncronize with plComponentDlg when a name is changed + void IUpdateNodeName(plMaxNode *node); +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plGetLocationDlg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plGetLocationDlg.cpp new file mode 100644 index 00000000..22574f7e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plGetLocationDlg.cpp @@ -0,0 +1,179 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plGetLocationDlg.h" +#include "plMaxNode.h" +#include "../MaxComponent/plMiscComponents.h" +#include "resource.h" +#include "../MaxExport/plErrorMsg.h" +#include "../MaxComponent/plComponent.h" +#include "hsUtils.h" + +plGetLocationDlg::plGetLocationDlg() : fNode(nil), fErrMsg(nil), fDefaultLocation(nil) +{ +} + +plGetLocationDlg& plGetLocationDlg::Instance() +{ + static plGetLocationDlg theInstance; + return theInstance; +} + +bool plGetLocationDlg::GetLocation(plMaxNode *node, plErrorMsg *errMsg) +{ + fNode = node; + fErrMsg = errMsg; + + // If an XRef doesn't have a location, tell the user and stop the export + if (node->IsXRef()) + { + char buf[256]; + sprintf(buf, "XRef object \"%s\" does not have a location", node->GetName()); + fErrMsg->Set(true, "Convert Error", buf).Show(); + return false; + } + + // If we have a default location, use it and exit + if (fDefaultLocation) + { + ISetLocation(fDefaultLocation); + return true; + } + + // If we're not showing prompts, just fail if there isn't a location + if (hsMessageBox_SuppressPrompts) + { + fErrMsg->Set(true, "Convert Error", "Object %s doesn't have a location component", node->GetName()); + fErrMsg->Show(); + return false; + } + + int ret = DialogBox(hInstance, + MAKEINTRESOURCE(IDD_GET_LOCATION), + GetCOREInterface()->GetMAXHWnd(), + ForwardDlgProc); + + return (ret != 0); +} + +void plGetLocationDlg::ResetDefaultLocation() +{ + fDefaultLocation = nil; +} + +void plGetLocationDlg::IListRooms(plMaxNode *node, HWND hList) +{ + // If node is a room component, add it's name to the list + plComponentBase *comp = node->ConvertToComponent(); + if(comp && (comp->ClassID() == ROOM_CID || comp->ClassID() == PAGEINFO_CID)) + { + int idx = SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)node->GetName()); + SendMessage(hList, LB_SETITEMDATA, idx, (LPARAM)node); + } + + // Recursively add all the nodes children + for (int i = 0; i < node->NumberOfChildren(); i++) + IListRooms((plMaxNode*)node->GetChildNode(i), hList); +} + +void plGetLocationDlg::IAddSelection(HWND hList, bool setDefault) +{ + int sel = SendMessage(hList, LB_GETCURSEL, 0, 0); + if (sel != LB_ERR) + { + // Get the node and component for the selected room component + plMaxNode *node = (plMaxNode*)SendMessage(hList, LB_GETITEMDATA, sel, 0); + ISetLocation(node); + + if (setDefault) + fDefaultLocation = node; + } +} + +void plGetLocationDlg::ISetLocation(plMaxNode *locNode) +{ + plComponent *comp = (plComponent*)locNode->ConvertToComponent(); + + // Add the roomless node to the target list and run the convert pass that gives it a room + comp->AddTarget(fNode); // Might want to fix this... + comp->SetupProperties(fNode, nil); +} + +INT_PTR plGetLocationDlg::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return Instance().DlgProc(hDlg, msg, wParam, lParam); +} + +INT_PTR plGetLocationDlg::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + HWND hList = GetDlgItem(hDlg, IDC_LIST_LOC); + SendMessage(hList, LB_RESETCONTENT, 0, 0); + IListRooms((plMaxNode*)GetCOREInterface()->GetRootNode(), hList); + + // Set the prompt text + char buf[256]; + sprintf(buf, "The object \"%s\" does not have a location. Either pick a location and press OK, or press Cancel to stop the convert.", fNode->GetName()); + SetDlgItemText(hDlg, IDC_PROMPT, buf); + + // No room components found. Tell user to create one and cancel convert. + if (SendMessage(hList, LB_GETCOUNT, 0, 0) == 0) + { + fErrMsg->Set(true, "Convert Error", "No location component found. Create one and convert again."); + fErrMsg->Show(); + EndDialog(hDlg, 0); + } + // Else set the first room as the current selection + else + SendMessage(hList, LB_SETCURSEL, 0, 0); + } + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + { + bool setDefault = (IsDlgButtonChecked(hDlg, IDC_CHECK_DEFAULT) == BST_CHECKED); + IAddSelection(GetDlgItem(hDlg, IDC_LIST_LOC), setDefault); + EndDialog(hDlg, 1); + } + return TRUE; + + case IDCANCEL: + // Stop the convert. No message since they know they canceled. + fErrMsg->Set(true); + EndDialog(hDlg, 0); + return TRUE; + } + break; + } + + return FALSE; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plGetLocationDlg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plGetLocationDlg.h new file mode 100644 index 00000000..c3f23d23 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plGetLocationDlg.h @@ -0,0 +1,54 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsWindows.h" + +class plErrorMsg; +class plMaxNode; + +class plGetLocationDlg +{ +protected: + plMaxNode *fNode; + plErrorMsg *fErrMsg; + plMaxNode *fDefaultLocation; + +public: + static plGetLocationDlg& Instance(); + bool GetLocation(plMaxNode *node, plErrorMsg *errMsg); + + // This should be called at the start of each convert to prevent any + // problems with bad pointers + void ResetDefaultLocation(); + +protected: + plGetLocationDlg(); + void IListRooms(plMaxNode *node, HWND hList); + void IAddSelection(HWND hList, bool setDefault); + void ISetLocation(plMaxNode *locNode); + + static INT_PTR CALLBACK ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + INT_PTR DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxAccelerators.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxAccelerators.cpp new file mode 100644 index 00000000..b1726c55 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxAccelerators.cpp @@ -0,0 +1,46 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "plMaxAccelerators.h" + +int plMaxAccelerators::fNumDisables = 0; + +void plMaxAccelerators::Enable() +{ + fNumDisables--; + + if (fNumDisables <= 0) + EnableAccelerators(); +} + +void plMaxAccelerators::Disable() +{ + fNumDisables++; + + if (fNumDisables > 0) + DisableAccelerators(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxAccelerators.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxAccelerators.h new file mode 100644 index 00000000..a56f06b2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxAccelerators.h @@ -0,0 +1,34 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +class plMaxAccelerators +{ +protected: + static int fNumDisables; + +public: + static void Enable(); + static void Disable(); +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxCFGFile.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxCFGFile.cpp new file mode 100644 index 00000000..5be170e2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxCFGFile.cpp @@ -0,0 +1,103 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plMaxCFGFile.h" +#include "max.h" +#include "hsTypes.h" +#include "../plFile/plBrowseFolder.h" + +const char *plMaxConfig::GetPluginIni() +{ + // Get the plugin CFG dir + static char plugDir[MAX_PATH]; + strcpy(plugDir, GetCOREInterface()->GetDir(APP_PLUGCFG_DIR)); + strcat(plugDir, "\\PlasmaMAX2.ini"); + + return plugDir; +} + +const char *plMaxConfig::GetClientPath(bool getNew, bool quiet) +{ + static char plasmaPath[MAX_PATH]; + plasmaPath[0] = '\0'; + + // Get the plugin CFG dir + const char *plugDir = GetPluginIni(); + + // Get the saved path + UInt32 len = GetPrivateProfileString("SceneViewer", "Directory", "", plasmaPath, MAX_PATH, plugDir); + + // If we didn't find a path, or we want a new one, ask the user for one + if ((len == 0 || getNew) && !quiet) + { + // If the user selects one, save it + if (plBrowseFolder::GetFolder(plasmaPath, plasmaPath, "Specify your client folder")) + WritePrivateProfileString("SceneViewer", "Directory", plasmaPath, plugDir); + } + + // Return the path if we got one + if (plasmaPath[0] != '\0') + { + // Make sure the path ends with a slash + char lastChar = plasmaPath[strlen(plasmaPath)-1]; + if (lastChar != '/' && lastChar != '\\') + strcat(plasmaPath, "\\"); + + return plasmaPath; + } + + return nil; +} + +void plMaxConfig::SetClientPath(const char *path) +{ + const char *plugDir = GetPluginIni(); + WritePrivateProfileString("SceneViewer", "Directory", path, plugDir); +} + +bool plMaxConfig::AssetManInterfaceDisabled() +{ + static bool inited = false; + static bool disabled = false; + + if (!inited) + { + char configstr[MAX_PATH]; + configstr[0] = '\0'; + + const char *plugDir = GetPluginIni(); + UInt32 len = GetPrivateProfileString("AssetMan", "Disable", "", configstr, MAX_PATH, plugDir); + + if (strcmp(configstr, "1") == 0 || stricmp(configstr, "true") == 0) + disabled = true; + else + disabled = false; + + inited = true; + } + + return disabled; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxCFGFile.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxCFGFile.h new file mode 100644 index 00000000..727ef600 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxCFGFile.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +namespace plMaxConfig +{ + // Get the full path to the ini file to write settings to + const char *GetPluginIni(); + + // Gets the path to the Plasma working directory + // If the user hasn't set one before, it prompts them to + // Set getNew to true to force the user to set a new path + // If a path is returned, it will end with a slash + const char *GetClientPath(bool getNew=false, bool quiet=false); + // For the rare case where you need to set the client path manually + void SetClientPath(const char *path); + + // option to disable the plugin's assetman interface + bool AssetManInterfaceDisabled(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMenu.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMenu.cpp new file mode 100644 index 00000000..e2f33efb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMenu.cpp @@ -0,0 +1,431 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "iMenuMan.h" + +#include "plMaxMenu.h" +#include "plActionTableMgr.h" +#include "resource.h" +#include "plSaveSelected.h" +#include "plComponentDlg.h" +#include "plMaxCFGFile.h" +#include "plResCollector.h" +#include "plAgeDescInterface.h" +#include "../MaxSceneViewer/SceneViewer.h" +#include "plNodeLock.h" +#include "plResetXform.h" +#include "plTextureSearch.h" +#include "../MaxExport/plExportDlg.h" + +// ************************* action table +// + +// random id to identify an action table and context +const ActionTableId kActionId = 0x54162b7c; + + +enum +{ + kActionSaveSel, + kActionMerge, + kActionComponent, + kActionResCollect, + kActionAgeDesc, + kActionCompCopy, + kActionSceneViewer, + kActionLock, + kActionUnlock, + kActionTexSearch, + kActionReset, + kActionSelectNonRenderables, + kActionExport, +}; + +static ActionDescription spActions[] = +{ + { + kActionSaveSel, // ActionId identifies action for callback execution (within table) + IDS_ACT1_DESC, // Action description for display in ui customization + IDS_ACT1_NAME, // Action name for display on menu + IDS_ACT_CAT // Action category for ui customization + }, + { + kActionMerge, + IDS_ACT2_DESC, + IDS_ACT2_NAME, + IDS_ACT_CAT + }, + { + kActionComponent, + IDS_ACT3_NAME, + IDS_ACT3_NAME, + IDS_ACT_CAT + }, + { + kActionResCollect, + IDS_ACT4_NAME, + IDS_ACT4_NAME, + IDS_ACT_CAT + }, + { + kActionAgeDesc, + IDS_ACT5_NAME, + IDS_ACT5_NAME, + IDS_ACT_CAT + }, + { + kActionCompCopy, + IDS_ACT6_NAME, + IDS_ACT6_NAME, + IDS_ACT_CAT + }, + { + kActionSceneViewer, + IDS_ACT7_NAME, + IDS_ACT7_NAME, + IDS_ACT_CAT + }, + { + kActionLock, + IDS_ACT8_DESC, + IDS_ACT8_NAME, + IDS_ACT_CAT + }, + { + kActionUnlock, + IDS_ACT9_DESC, + IDS_ACT9_NAME, + IDS_ACT_CAT + }, + { + kActionTexSearch, + IDS_ACT10_NAME, + IDS_ACT10_NAME, + IDS_ACT_CAT + }, + { + kActionReset, + IDS_ACT11_DESC, + IDS_ACT11_NAME, + IDS_ACT_CAT + }, + { + kActionSelectNonRenderables, + IDS_ACT12_DESC, + IDS_ACT12_NAME, + IDS_ACT_CAT + }, + { + kActionExport, + IDS_PLASMA_EXPORT, + IDS_PLASMA_EXPORT, + IDS_ACT_CAT + } +}; + +// callback for action exection +bool DoAction(int id) +{ + switch(id) + { + case kActionSaveSel: + plSaveSelected(); + return true; + + case kActionMerge: + plMerge(); + return true; + + case kActionComponent: + plComponentDlg::Instance().Open(); + return true; + + case kActionResCollect: + plResCollector::Collect(); + return true; + + case kActionAgeDesc: + plAgeDescInterface::Instance().Open(); + return true; + + case kActionCompCopy: + CopyComponents(); + return true; + + case kActionSceneViewer: + SceneViewer::Instance().Show(); + return true; + + case kActionLock: + plNodeLock().Lock(); + return true; + + case kActionUnlock: + plNodeLock().Unlock(); + return true; + + case kActionTexSearch: + plTextureSearch::Instance().Toggle(); + return true; + + case kActionReset: + plResetXform().ResetSelected(); + return true; + + case kActionSelectNonRenderables: + plSelectNonRenderables().SelectNonRenderables(); + return true; + + case kActionExport: + plExportDlg::Instance().Show(); + return true; + } + + return false; +} + +static ActionTableInfo actionInfo(kActionId, "Plasma", spActions, sizeof(spActions) / sizeof(ActionDescription)); + +// static or global declaration of action table manager +plActionTableMgr theActionTableMgr(actionInfo, DoAction); + + +// +// ************************* end action table + +////////////////////////////////////////////////////////////////////////////// +// Menu Creation Junk + +MenuContextId kMyMenuContextId=0xcff95f6c; // +static char *kMenuName = "Plasma"; +static int kMenuVersion = 10; // Increment this number if you add an entry to the menu + +extern TCHAR *GetString(int id); + +void AddPlasmaExportMenu() +{ + IMenuManager* pMenuMan = GetCOREInterface()->GetMenuManager(); + IMenu* fileMenu = pMenuMan->FindMenu("&File"); + + int i; + + bool plasmaExportFound = false; + + // Make sure our action isn't already in the menu + TSTR ourName = GetString(IDS_PLASMA_EXPORT); + for (i = 0; i < fileMenu->NumItems(); i++) + { + IMenuItem* fileMenuItem = fileMenu->GetItem(i); + const TSTR& title = fileMenuItem->GetTitle(); + if (title == ourName) + plasmaExportFound = true; + + // KLUDGE - MaxAss didn't define the file submenu with an accelerator. + // This fixes it. + if (title == "MAX File Operations") + { + fileMenuItem->SetUseCustomTitle(true); + bool custom = fileMenuItem->GetUseCustomTitle(); + fileMenuItem->SetTitle("MAX File Opera&tions"); + + pMenuMan->UpdateMenuBar(); + } + } + + if (!plasmaExportFound) + { + // Menu item isn't there, add it + for (i = 0; i < fileMenu->NumItems(); i++) + { + IMenuItem* fileMenuItem = fileMenu->GetItem(i); + const TSTR& title = fileMenuItem->GetTitle(); + // We want to add it after the "Export Selected" menu item + if (title == "Export Selected...") + { + ActionTable* pActionTable = GetCOREInterface()->GetActionManager()->FindTable(kActionId); + if (!pActionTable) + { + hsAssert(0, "Action table not found"); + return; + } + + IMenuItem* menuItem = GetIMenuItem(); + menuItem->SetActionItem(pActionTable->GetAction(kActionExport)); + fileMenu->AddItem(menuItem, i+1); + + pMenuMan->UpdateMenuBar(); + + return; + } + } + } +} + +void plCreateMenu() +{ + AddPlasmaExportMenu(); + + IMenuManager* pMenuMan = GetCOREInterface()->GetMenuManager(); + bool newlyRegistered = pMenuMan->RegisterMenuBarContext(kMyMenuContextId, kMenuName); + + // Is the Max menu version the most recent? + bool wrongVersion = GetPrivateProfileInt("Menu", "Version", 0, plMaxConfig::GetPluginIni()) < kMenuVersion; + if (wrongVersion) + { + // Delete the old version of the menu + IMenu *oldMenu = pMenuMan->FindMenu(kMenuName); + if (oldMenu) + pMenuMan->UnRegisterMenu(oldMenu); + + // Update the menu version + char buf[30]; + WritePrivateProfileString("Menu", "Version", itoa(kMenuVersion, buf, 10), plMaxConfig::GetPluginIni()); + } + + if (wrongVersion || newlyRegistered) + { + IMenu *pMainMenu = pMenuMan->GetMainMenuBar(); + if (!pMainMenu) + { + hsAssert(0, "Main menu not found"); + return; + } + + // Get our action table + ActionTable* pActionTable = GetCOREInterface()->GetActionManager()->FindTable(kActionId); + if (!pActionTable) + { + hsAssert(0, "Action table not found"); + return; + } + + // Create the Plasma menu + IMenu* pPlasmaMenu = GetIMenu(); + pPlasmaMenu->SetTitle(kMenuName); + + // Register the new menu with the system + pMenuMan->RegisterMenu(pPlasmaMenu, 0); + + ///////////////////////////////////////////////// + // Add the menu items + // + + // Add the save selected action to the menu + IMenuItem* pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionSaveSel)); + pPlasmaMenu->AddItem(pMenuItem); + + // Add the merge action to the menu + pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionMerge)); + pPlasmaMenu->AddItem(pMenuItem); + + // Add the component copy action to the menu + pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionCompCopy)); + pPlasmaMenu->AddItem(pMenuItem); + + // Add a separator + pMenuItem = GetIMenuItem(); + pMenuItem->ActAsSeparator(); + pPlasmaMenu->AddItem(pMenuItem); + + // Add the component manager to the menu + pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionComponent)); + pPlasmaMenu->AddItem(pMenuItem); + + // Add the resource collector to the menu + pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionResCollect)); + pPlasmaMenu->AddItem(pMenuItem); + + // Add the texture search to the menu + pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionTexSearch)); + pPlasmaMenu->AddItem(pMenuItem); + + // Add the age description to the menu + pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionAgeDesc)); + pPlasmaMenu->AddItem(pMenuItem); + + // Add a separator + pMenuItem = GetIMenuItem(); + pMenuItem->ActAsSeparator(); + pPlasmaMenu->AddItem(pMenuItem); + + // Add the SceneViewer to the menu + pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionSceneViewer)); + pPlasmaMenu->AddItem(pMenuItem); + + // Add a separator + pMenuItem = GetIMenuItem(); + pMenuItem->ActAsSeparator(); + pPlasmaMenu->AddItem(pMenuItem); + + // Add the Lock Selected to the menu + pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionLock)); + pPlasmaMenu->AddItem(pMenuItem); + + // Add the Unlock Selected to the menu + pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionUnlock)); + pPlasmaMenu->AddItem(pMenuItem); + + // Add the Reset Selected to the menu + pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionReset)); + pPlasmaMenu->AddItem(pMenuItem); + + // Add the SelectNonRenderables to the menu + pMenuItem = GetIMenuItem(); + pMenuItem->SetActionItem(pActionTable->GetAction(kActionSelectNonRenderables)); + pPlasmaMenu->AddItem(pMenuItem); + + // Create a new menu item to hold the sub-menu + IMenuItem* pSubMenuItem1 = GetIMenuItem(); //menu in menu bar... + pSubMenuItem1->SetSubMenu(pPlasmaMenu); + pMainMenu->AddItem(pSubMenuItem1); + + pMenuMan->UpdateMenuBar(); + + // Save the dang menu, in case Max crashes + const char *uiDir = GetCOREInterface()->GetDir(APP_UI_DIR); + char path[MAX_PATH]; + sprintf(path, "%s\\%s", uiDir, "MaxMenus.mnu"); + + pMenuMan->SaveMenuFile(path); + } + +} + +// End Menu Creation Junk +////////////////////////////////////////////////////////////////////////////// + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMenu.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMenu.h new file mode 100644 index 00000000..b7ba5fb7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMenu.h @@ -0,0 +1,31 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __MENUTESTUTIL__H +#define __MENUTESTUTIL__H + +void plCreateMenu(); + +#endif // __MENUTESTUTIL__H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMeshExtractor.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMeshExtractor.cpp new file mode 100644 index 00000000..716e329b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMeshExtractor.cpp @@ -0,0 +1,222 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plMaxMeshExtractor.h" +#include "Max.h" +#include "plMaxNode.h" +#include "dummy.h" + +static Mesh* ExtractMesh(INode* pNode, TriObject** ppDeleteMe) +{ + Object *obj = pNode->EvalWorldState(0).obj; + Mesh *pRetMesh = nil; + + if( obj ) { + + if( obj->CanConvertToType(triObjectClassID) ) { + + // Convert to triangle object + TriObject *tri = (TriObject*)obj->ConvertToType(0, triObjectClassID); + + if (tri != obj) *ppDeleteMe = tri; // if Convert allocated, pass back so caller can delete. + + if( tri ) { + Mesh *pTMesh = &tri->mesh; + + if( pTMesh->getNumFaces() ) { + pRetMesh = pTMesh; + } + } + } + } + return pRetMesh; +} + +// Return the two points defining the bounding box for the given vertices. +static void MeshMinMax(hsPoint3& min, hsPoint3& max, int numVerts, hsPoint3* pVerts) +{ + for (int i = 0; i < numVerts; i++) + { + hsPoint3* vert = &pVerts[i]; + min.fX = hsMinimum(vert->fX, min.fX); + min.fY = hsMinimum(vert->fY, min.fY); + min.fZ = hsMinimum(vert->fZ, min.fZ); + max.fX = hsMaximum(vert->fX, max.fX); + max.fY = hsMaximum(vert->fY, max.fY); + max.fZ = hsMaximum(vert->fZ, max.fZ); + } +} + +static bool MakeNormalMesh(plMaxNode *node, plMaxMeshExtractor::NeutralMesh& mesh, Matrix3* w2l) +{ + TriObject *pDeleteMe = nil; + Mesh *pMesh = ExtractMesh(node, &pDeleteMe); // allocates *sometimes*; check pDeleteMe + + if (!pMesh) + return false; + + Matrix3 fullTM = node->GetObjectTM(0); + int parity = fullTM.Parity(); + + mesh.fNumVerts = pMesh->numVerts; + mesh.fVerts = TRACKED_NEW hsPoint3[mesh.fNumVerts]; + + for (int i = 0; i < mesh.fNumVerts; i++) + { + // convert the vertex to global coordinates + Point3 newVert = fullTM * pMesh->verts[i]; + // convert the vertex to the new (requested) coordinate system + if (w2l) + newVert = (*w2l) * newVert; + + mesh.fVerts[i].Set(newVert.x, newVert.y, newVert.z); + } + + mesh.fNumFaces = pMesh->numFaces; + mesh.fFaces = TRACKED_NEW UInt16[mesh.fNumFaces*3]; + for (int i = 0; i < mesh.fNumFaces; i++) + { + Face* pFace = &pMesh->faces[i]; + UInt16* pNFace = &mesh.fFaces[i * 3]; + + pNFace[0] = pFace->v[ parity ? 2 : 0 ]; // reverse winding if parity backwards + pNFace[1] = pFace->v[1]; + pNFace[2] = pFace->v[ parity ? 0 : 2 ]; // '' + } + + if (pDeleteMe) + delete pDeleteMe; + + return true; +} + +// BUILDBOXMESH +// Build the minimum bounding box (triangles and all) enclosing the given vertices +// DELETES the given vertex and face array +// ALLOCATES a new vertex and face array +// MODIFIES *all* the input parameters. +static void MakeBoxMesh(plMaxNode* node, plMaxMeshExtractor::NeutralMesh& mesh, hsPoint3& minV, hsPoint3& maxV) +{ + hsPoint3* newVerts = TRACKED_NEW hsPoint3[8]; + UInt16* newFaces = TRACKED_NEW UInt16[12 * 3]; + + newVerts[0].Set(minV.fX, minV.fY, minV.fZ); + newVerts[1].Set(maxV.fX, minV.fY, minV.fZ); + newVerts[2].Set(minV.fX, maxV.fY, minV.fZ); + newVerts[3].Set(maxV.fX, maxV.fY, minV.fZ); + newVerts[4].Set(minV.fX, minV.fY, maxV.fZ); + newVerts[5].Set(maxV.fX, minV.fY, maxV.fZ); + newVerts[6].Set(minV.fX, maxV.fY, maxV.fZ); + newVerts[7].Set(maxV.fX, maxV.fY, maxV.fZ); + + UInt16 standardFaces[] = { 0, 2, 1, + 1, 2, 3, + 0, 1, 4, + 1, 5, 4, + 0, 4, 2, + 2, 4, 6, + 1, 3, 7, + 7, 5, 1, + 3, 2, 7, + 2, 6, 7, + 4, 7, 6, + 4, 5, 7 }; + + memcpy(newFaces, standardFaces, sizeof(standardFaces)); + + delete [] mesh.fVerts; + mesh.fVerts = newVerts; + delete [] mesh.fFaces; + mesh.fFaces = newFaces; + mesh.fNumVerts = 8; + mesh.fNumFaces = 12; +} + +static void MakeDummyMesh(plMaxNode* node, plMaxMeshExtractor::NeutralMesh& mesh) +{ + hsPoint3 minV, maxV; + + Object* thisObj = node->GetObjectRef(); + DummyObject* thisDummy = (DummyObject*)thisObj; + Box3 thisBoundSurface = thisDummy->GetBox(); + minV.fX = thisBoundSurface.Min().x; + minV.fY = thisBoundSurface.Min().y; + minV.fZ = thisBoundSurface.Min().z; + maxV.fX = thisBoundSurface.Max().x; + maxV.fY = thisBoundSurface.Max().y; + maxV.fZ = thisBoundSurface.Max().z; + + MakeBoxMesh(node, mesh, minV, maxV); +} + +// CREATEPLHKPHYSICALFROMMESHEASY +// Convenience function for getting from a max node to a plHKPhysical and the requisite +// Havok objects. +// The node and the scene object don't have to correspond to the same Max object. +// If the sAltNode is supplied, the node will be moved into the coordinate system of the +bool plMaxMeshExtractor::Extract(plMaxMeshExtractor::NeutralMesh& mesh, plMaxNode* node, bool makeAABB, plMaxNode* sOwningNode) +{ + mesh.fNumVerts = mesh.fNumFaces = 0; + mesh.fVerts = nil; + mesh.fFaces = nil; + + // if an alternate node was supplied, get its scene object. otherwise don't... + plMaxNode* masterNode = sOwningNode ? sOwningNode : node; + + mesh.fL2W = masterNode->GetLocalToWorld44(); + + // + // Create the arrays of verts and faces + // + hsBool isDummy = (node->EvalWorldState(0).obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0)); + if (isDummy) + { + hsMatrix44 w2l = masterNode->GetWorldToLocal44(); + MakeDummyMesh(node, mesh); + // Localize the verts + //for (int i = 0; i < mesh.fNumVerts; i++) + // mesh.fVerts[i] = w2l * mesh.fVerts[i]; + } + else + { + // only get the max world-to-local transform if the node is moveable or instanced. otherwise verts stay global. + Matrix3 w2l = masterNode->GetWorldToLocal(); +// Matrix3 *localizer = nil; +// if (masterNode->IsMovable() || masterNode->GetForceLocal() || masterNode->GetInstanced()) +// localizer = &w2l; + + if (!MakeNormalMesh(node, mesh, &w2l)) + return false; + + if (makeAABB) + { + hsPoint3 minV(FLT_MAX, FLT_MAX, FLT_MAX), maxV(-FLT_MAX, -FLT_MAX, -FLT_MAX); + MeshMinMax(minV, maxV, mesh.fNumVerts, mesh.fVerts); + MakeBoxMesh(node, mesh, minV, maxV); + } + } + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMeshExtractor.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMeshExtractor.h new file mode 100644 index 00000000..b5b77bcd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxMeshExtractor.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plMaxMeshExtractor_h_inc +#define plMaxMeshExtractor_h_inc + +#include "hsTypes.h" +#include "hsMatrix44.h" + +class plMaxNode; + +class plMaxMeshExtractor +{ +public: + struct NeutralMesh + { + int fNumVerts; + hsPoint3* fVerts; + int fNumFaces; + UInt16* fFaces; + + hsMatrix44 fL2W; + }; + + // Converts a max node into a position, rotation, and arrays of verts and faces + // relative to those. + static bool Extract(NeutralMesh& mesh, plMaxNode* pNode, bool makeAABB = false, plMaxNode* sOwningNode = nil); +}; + +#endif // plMaxMeshExtractor_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNode.cpp new file mode 100644 index 00000000..ab4fea19 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNode.cpp @@ -0,0 +1,4104 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "iparamm2.h" +#include "iparamb2.h" +#include "ISkin.h" +#include "MNMath.h" + +#include "plMaxNode.h" +//#include "../MaxComponent/resource.h" +#include "GlobalUtility.h" + +#include "plgDispatch.h" +#include "plPluginResManager.h" +#include "plMaxNodeData.h" +#include "hsUtils.h" + +#include "../MaxConvert/plConvert.h" +#include "hsTemplates.h" +#include "hsStringTokenizer.h" + +#include "../MaxConvert/hsConverterUtils.h" +#include "../MaxConvert/hsControlConverter.h" +#include "../MaxConvert/plMeshConverter.h" +#include "../MaxConvert/hsMaterialConverter.h" +#include "../MaxConvert/plLayerConverter.h" +#include "../MaxConvert/UserPropMgr.h" +#include "../MaxExport/plErrorMsg.h" +#include "../MaxConvert/hsVertexShader.h" +#include "../MaxConvert/plLightMapGen.h" +#include "plMaxMeshExtractor.h" +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" + +#include "../pnKeyedObject/plKey.h" + +#include "../pnSceneObject/plSceneObject.h" +#include "../plScene/plSceneNode.h" +#include "../plPhysX/plPXPhysical.h" +#include "../plDrawable/plInstanceDrawInterface.h" +#include "../plDrawable/plSharedMesh.h" +#include "../pnSceneObject/plSimulationInterface.h" +#include "../pnSceneObject/plAudioInterface.h" +#include "../pnSceneObject/plCoordinateInterface.h" +#include "../pfAnimation/plFilterCoordInterface.h" +#include "../plParticleSystem/plBoundInterface.h" +#include "../plPhysical/plPickingDetector.h" +#include "../plModifier/plLogicModifier.h" +#include "../plModifier/plResponderModifier.h" +#include "../plModifier/plInterfaceInfoModifier.h" +#include "../pfAnimation/plLightModifier.h" +#include "../pfCharacter/plPlayerModifier.h" +#include "../plAvatar/plAGModifier.h" +#include "../plAvatar/plAGAnim.h" +#include "../plAvatar/plPointChannel.h" +#include "../plAvatar/plScalarChannel.h" +#include "../plAvatar/plAGMasterMod.h" +#include "../plMessage/plReplaceGeometryMsg.h" +#include "../plGImage/plMipmap.h" +#include "../plModifier/plSpawnModifier.h" +#include "../plInterp/plController.h" +#include "../plInterp/hsInterp.h" +#include "../pnMessage/plTimeMsg.h" +#include "../pfAnimation/plViewFaceModifier.h" // mf horse temp hack testing to be thrown away + +#include "../plScene/plOccluder.h" +#include "hsFastMath.h" + + +#include "../plDrawable/plDrawableSpans.h" +#include "../plDrawable/plGeometrySpan.h" +#include "../plPipeline/plFogEnvironment.h" + +#include "../plGLight/plLightInfo.h" +#include "../plGLight/plLightKonstants.h" +#include "../plSurface/plLayerInterface.h" +#include "../plSurface/plLayer.h" +#include "../plSurface/hsGMaterial.h" + +#include "../pnMessage/plObjRefMsg.h" +#include "../pnMessage/plNodeRefMsg.h" +#include "../pnMessage/plIntRefMsg.h" + +#include "../MaxExport/plExportProgressBar.h" + +#include "../MaxPlasmaMtls/Materials/plDecalMtl.h" + +#include "../MaxComponent/plComponentTools.h" +#include "../MaxComponent/plComponent.h" +#include "../MaxComponent/plComponentExt.h" +#include "../MaxComponent/plFlexibilityComponent.h" +#include "../MaxComponent/plLightMapComponent.h" +#include "../MaxComponent/plXImposter.h" +#include "../MaxComponent/plMiscComponents.h" + +#include "../plParticleSystem/plParticleSystem.h" +#include "../plParticleSystem/plParticleEmitter.h" +#include "../plParticleSystem/plParticleEffect.h" +#include "../plParticleSystem/plParticleGenerator.h" +#include "../plParticleSystem/plConvexVolume.h" + +#include "../MaxPlasmaLights/plRealTimeLightBase.h" +#include "../MaxPlasmaLights/plRTProjDirLight.h" + + +extern UserPropMgr gUserPropMgr; + +hsBool ThreePlaneIntersect(const hsVector3& norm0, const hsPoint3& point0, + const hsVector3& norm1, const hsPoint3& point1, + const hsVector3& norm2, const hsPoint3& point2, hsPoint3& loc); + +// Begin external component toolbox /////////////////////////////////////////////////////////////// +static plKey ExternAddModifier(plMaxNodeBase *node, plModifier *mod) +{ + return nil;//((plMaxNode*)node)->AddModifier(mod); +} + +static plKey ExternGetNewKey(const char *name, plModifier *mod, plLocation loc) +{ + return nil;//hsgResMgr::ResMgr()->NewKey(name, mod, loc); +} + +// In plResponderComponent (for no apparent reason). +int GetMatAnimModKey(Mtl* mtl, plMaxNodeBase* node, const char *segName, hsTArray& keys); +// In plAudioComponents +int GetSoundNameAndIdx(plComponentBase *comp, plMaxNodeBase *node, const char*& name); + +#include "../MaxComponent/plAnimComponent.h" + +static const char *GetAnimCompAnimName(plComponentBase *comp) +{ + if (comp->ClassID() == ANIM_COMP_CID || comp->ClassID() == ANIM_GROUP_COMP_CID) + return ((plAnimComponentBase*)comp)->GetAnimName(); + return nil; +} + +static plKey GetAnimCompModKey(plComponentBase *comp, plMaxNodeBase *node) +{ + if (comp->ClassID() == ANIM_COMP_CID || comp->ClassID() == ANIM_GROUP_COMP_CID) + return ((plAnimComponentBase*)comp)->GetModKey((plMaxNode*)node); + return nil; +} + +plComponentTools gComponentTools(ExternAddModifier, + ExternGetNewKey, + nil, + GetAnimCompModKey, + GetAnimCompAnimName, + GetMatAnimModKey, + GetSoundNameAndIdx); + +// End external component toolbox ////////////////////////////////////////////////////////////////// + +void plMaxBoneMap::AddBone(plMaxNodeBase *bone) +{ + char *dbgNodeName = bone->GetName(); + if (fBones.find(bone) == fBones.end()) + fBones[bone] = fNumBones++; +} + +void plMaxBoneMap::FillBoneArray(plMaxNodeBase **boneArray) +{ + BoneMap::const_iterator boneIt = fBones.begin(); + for (; boneIt != fBones.end(); boneIt++) + boneArray[(*boneIt).second] = (*boneIt).first; +} + +UInt8 plMaxBoneMap::GetIndex(plMaxNodeBase *bone) +{ + hsAssert(fBones.find(bone) != fBones.end(), "Bone missing in remap!"); + return fBones[bone]; +} + +UInt32 plMaxBoneMap::GetBaseMatrixIndex(plDrawable *draw) +{ + if (fBaseMatrices.find(draw) == fBaseMatrices.end()) + return (UInt32)-1; + + return fBaseMatrices[draw]; +} + +void plMaxBoneMap::SetBaseMatrixIndex(plDrawable *draw, UInt32 idx) +{ + fBaseMatrices[draw] = idx; +} + +// Don't call this after you've started assigning indices to spans, or +// you'll be hosed (duh). +void plMaxBoneMap::SortBones() +{ + plMaxNodeBase **tempBones = TRACKED_NEW plMaxNodeBase*[fNumBones]; + FillBoneArray(tempBones); + + // Look ma! An n^2 bubble sort! + // (It's a 1-time thing for an array of less than 100 items. Speed is not essential here) + int i,j; + for (i = 0; i < fNumBones; i++) + { + hsBool swap = false; + for (j = i + 1; j < fNumBones; j++) + { + if (strcmp(tempBones[i]->GetName(), tempBones[j]->GetName()) > 0) + { + plMaxNodeBase *temp = tempBones[i]; + tempBones[i] = tempBones[j]; + tempBones[j] = temp; + swap = true; + } + } + if (!swap) + break; + } + + for (i = 0; i < fNumBones; i++) + fBones[tempBones[i]] = i; + + delete [] tempBones; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +plKey plMaxNode::AddModifier(plModifier *pMod, const char* name) +{ + plKey modKey = pMod->GetKey(); + if (!modKey) + modKey = hsgResMgr::ResMgr()->NewKey(name, pMod, GetLocation()); + hsgResMgr::ResMgr()->AddViaNotify(modKey, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + return modKey; +} + +hsBool plMaxNode::DoRecur(PMaxNodeFunc pDoFunction,plErrorMsg *pErrMsg, plConvertSettings *settings,plExportProgressBar*bar) +{ +#ifdef HS_DEBUGGING + const char *tmpName = GetName(); +#endif + + // If there is a progess bar, update it with the current node + // If the user cancels during the update, set bogus so we'll exit + if (bar && bar->Update(GetName())) + { + pErrMsg->Set(true); + return false; + } + + // If we can't convert (and we aren't the root node) stop recursing. + if (!IsRootNode() && !CanConvert()) + return false; + + (this->*pDoFunction)(pErrMsg, settings); + + for (int i = 0; (!pErrMsg || !pErrMsg->IsBogus()) && i < NumberOfChildren(); i++) + { + plMaxNode *pChild = (plMaxNode *)GetChildNode(i); + pChild->DoRecur(pDoFunction, pErrMsg, settings, bar); + } + return true; +} + +// This is the same as DoRecur except that it ignores the canconvert field. We +// need this for things like clearing the old data, where we need to ignore the old value. +hsBool plMaxNode::DoAllRecur(PMaxNodeFunc pDoFunction,plErrorMsg *pErrMsg, plConvertSettings *settings,plExportProgressBar*bar) +{ +#ifdef HS_DEBUGGING + const char *tmpName = GetName(); +#endif + + // If there is a progess bar, update it with the current node + // If the user cancels during the update, set bogus so we'll exit + if (bar && bar->Update(GetName())) + { + pErrMsg->Set(true); + return false; + } + + (this->*pDoFunction)(pErrMsg, settings); + + for (int i = 0; (!pErrMsg || !pErrMsg->IsBogus()) && i < NumberOfChildren(); i++) + { + plMaxNode *pChild = (plMaxNode *)GetChildNode(i); + pChild->DoAllRecur(pDoFunction, pErrMsg, settings, bar); + } + return true; +} + +hsBool plMaxNode::ConvertValidate(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + TimeValue t = hsConverterUtils::Instance().GetTime(GetInterface()); + Object *obj = EvalWorldState( t ).obj; + const char* dbgName = GetName(); + + // Always want to recalculate if this object can convert at this point. + // In general there won't be any cached flag anyway, but in the SceneViewer + // there can be if we're reconverting. + hsBool canConvert = CanConvert(true); + + plMaxNodeData thisNodeData; // Extra data stored for each node + + if (IsTarget()) + { + plMaxNode* targetNode = ((plMaxNode*)GetLookatNode()); + if (targetNode) + { + Object* targObj = targetNode->EvalWorldState( 0 ).obj; + + if (targObj && targObj->SuperClassID() == CAMERA_CLASS_ID) + canConvert = true; + else + canConvert = false; + } + else + canConvert = false; + } + if (canConvert && obj->SuperClassID() == LIGHT_CLASS_ID) + { + thisNodeData.SetDrawable(false); + thisNodeData.SetRunTimeLight(true); + thisNodeData.SetForceLocal(true); + } + + if (UserPropExists("Occluder")) + { +// thisNodeData.SetDrawable(false); + } + if( UserPropExists("PSRunTimeLight") ) + thisNodeData.SetRunTimeLight(true); + + if (GetParticleRelated()) + thisNodeData.SetForceLocal(true); +// if (UserPropExists("cloth")) +// { +// thisNodeData.SetForceLocal(true); +// } + + // If we want a physicals only world, set everything to not drawable by default. + if (settings->fPhysicalsOnly) + thisNodeData.SetDrawable(false); + + // Remember info in MaxNodeData block for later + thisNodeData.SetCanConvert(canConvert); + + SetMaxNodeData(&thisNodeData); + +#define MF_DISABLE_INSTANCING +#ifndef MF_DISABLE_INSTANCING + // Send this node off to the instance list, to see if we're instanced + if( CanMakeMesh( obj, pErrMsg, settings ) ) + { + hsTArray nodes; + UInt32 numInstances = IBuildInstanceList( GetObjectRef(), t, nodes ); + if( numInstances > 1 ) + { + /// INSTANCED. Make sure to force local on us + SetForceLocal( true ); + SetInstanced( true ); + } + } +#endif // MF_DISABLE_INSTANCING + + // If this is for the SceneViewer, turn off the dirty flags so we won't try + // reconverting this node again. + if (settings->fSceneViewer) + SetDirty(kAllDirty, false); + + return canConvert; +} + +hsBool plMaxNode::ClearMaxNodeData(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + // The right place to delete the boneMap is really in ~plMaxNodeData, but that class + // is only allowed to know about stuff in nucleusLib. + if (GetBoneMap() && GetBoneMap()->fOwner == this) + delete GetBoneMap(); + + if (GetSwappableGeom()) + { + // Ref and unref it, so it goes away if no one kept it around (i.e. we just + // looked at the mesh during export for reference, but don't want to keep it.) + GetSwappableGeom()->GetKey()->RefObject(); + GetSwappableGeom()->GetKey()->UnRefObject(); + } + + SetMaxNodeData( nil ); + return true; +} + +#include "plGetLocationDlg.h" + +// +// Helper for setting synchedObject options, until we have a GUI +// +#include "../plResMgr/plKeyFinder.h" +#include "plMaxCFGFile.h" +#include "../plAgeDescription/plAgeDescription.h" +#include "../plResMgr/plPageInfo.h" +#include "../pnNetCommon/plSDLTypes.h" + +void plMaxNode::CheckSynchOptions(plSynchedObject* so) +{ + if (so) + { + ////////////////////////////////////////////////////////////////////////// + // TEMP - remove + // + TSTR sdata,sdataList[128]; + int i,num; + + // + // check for LocalOnly or DontPersist props + // + if (gUserPropMgr.UserPropExists(this, "LocalOnly")) + so->SetLocalOnly(true); // disable net synching and persistence + else + if (gUserPropMgr.UserPropExists(this, "DontPersistAny")) // disable all types of persistence + so->SetSynchFlagsBit(plSynchedObject::kExcludeAllPersistentState); + else + { + if (gUserPropMgr.GetUserPropStringList(this, "DontPersist", num, sdataList)) + { + for(i=0;iAddToSDLExcludeList(sdataList[i]); // disable a type of persistence + } + } + + // + // Check for Volatile prop + // + if (gUserPropMgr.UserPropExists(this, "VolatileAll")) // make all sdl types on this object Volatile + so->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile); + else + { + if (gUserPropMgr.GetUserPropStringList(this, "Volatile", num, sdataList)) + { + for(i=0;iAddToSDLVolatileList(sdataList[i]); // make volatile a type of persistence + } + } + + bool tempOldOverride = (gUserPropMgr.UserPropExists(this, "OverrideHighLevelSDL") != 0); + + // + // TEMP - remove + ////////////////////////////////////////////////////////////////////////// + + // If this object isn't in a global room, turn off sync flags + if ((!tempOldOverride && !GetOverrideHighLevelSDL()) && !GetLocation().IsReserved()) + { + bool isDynSim = GetPhysicalProps()->GetGroup() == plSimDefs::kGroupDynamic; + bool hasPFC = false; + int count = NumAttachedComponents(); + for (UInt32 x = 0; x < count; x++) + { + plComponentBase *comp = GetAttachedComponent(x); + if (comp->ClassID() == Class_ID(0x670d3629, 0x559e4f11)) + { + hasPFC = true; + break; + } + } + if (!isDynSim && !hasPFC) + { + so->SetSynchFlagsBit(plSynchedObject::kExcludeAllPersistentState); + } + else + { + so->AddToSDLExcludeList(kSDLAGMaster); + so->AddToSDLExcludeList(kSDLResponder); + so->AddToSDLExcludeList(kSDLLayer); + so->AddToSDLExcludeList(kSDLSound); + so->AddToSDLExcludeList(kSDLXRegion); + } + } + } +} + +hsBool plMaxNode::MakeSceneObject(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + const char* dbgName = GetName(); + if (!CanConvert()) + return false; + + plLocation nodeLoc = GetLocation();//GetLocFromStrings(); // After this we can use GetLocation() + if (!nodeLoc.IsValid()) + { + // If we are reconverting, we don't want to bother the user about a room. + // In most cases, if it doesn't have a room we are in the middle of creating + // it. We don't want to pop up a dialog at that point. + if (settings->fReconvert) + { + SetCanConvert(false); + return false; + } + + if (!plGetLocationDlg::Instance().GetLocation(this, pErrMsg)) + return false; + nodeLoc = GetLocation(); + } + + plSceneObject* pso; + plKey objKey; + + // Handle this as a SceneObject + pso = TRACKED_NEW plSceneObject; + objKey = hsgResMgr::ResMgr()->NewKey(GetName(), pso, nodeLoc, GetLoadMask()); + + // Remember info in MaxNodeData block for later + plMaxNodeData *pDat = GetMaxNodeData(); + pDat->SetKey(objKey); + pDat->SetSceneObject(pso); + + CheckSynchOptions(pso); + + return true; +} + +hsBool plMaxNode::PrepareSkin(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + if( !IFindBones(pErrMsg, settings) ) + return false; + + if( !NumBones() ) + return true; + + int i; + for( i = 0; i < NumBones(); i++ ) + { + GetBone(i)->SetForceLocal(true); + GetBone(i)->SetDrawable(false); + } + + return true; +} + +hsBool plMaxNode::IFindBones(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + if( !CanConvert() ) + return false; + + if (UserPropExists("Bone")) + { + AddBone(this); + SetForceLocal(true); + } + + ISkin* skin = FindSkinModifier(); + if( skin && skin->GetNumBones() ) + { + char *dbgNodeName = GetName(); + + // BoneUpdate + //SetForceLocal(true); + int i; + for( i = 0; i < skin->GetNumBones(); i++ ) + { + plMaxNode* bone = (plMaxNode*)skin->GetBone(i); + if( bone ) + { + if( !bone->CanConvert() || !bone->GetMaxNodeData() ) + { + if( pErrMsg->Set(true, GetName(), "Trouble connecting to bone %s - skipping", bone->GetName()).CheckAndAsk() ) + SetDrawable(false); + } + else + { + AddBone(bone); + bone->SetForceLocal(true); + } + } + else + { + if( pErrMsg->Set(true, GetName(), "Trouble finding bone - skipping").CheckAndAsk() ) + SetDrawable(false); + } + } + } + + return true; +} + +#include "plMaxMeshExtractor.h" +#include "plPhysXCooking.h" +#include "../plPhysX/plPXStream.h" +#include "../plPhysX/plSimulationMgr.h" +#include "hsSTLStream.h" + +hsBool plMaxNode::MakePhysical(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + const char* dbgNodeName = GetName(); + + if( !CanConvert() ) + return false; + + if( !GetPhysical() ) + return true; + + plPhysicalProps *physProps = GetPhysicalProps(); + + if (!physProps->IsUsed()) + return true; + + //hsStatusMessageF("Making phys for %s", dbgNodeName); + + plSimDefs::Group group = (plSimDefs::Group)physProps->GetGroup(); + plSimDefs::Bounds bounds = (plSimDefs::Bounds)physProps->GetBoundsType(); + float mass = physProps->GetMass(); + + plMaxNode *proxyNode = physProps->GetProxyNode(); + if (!proxyNode) + proxyNode = this; + + // We want to draw solid physicals only. If it is something avatars bounce off, + // set it to drawable. + if (settings->fPhysicalsOnly) + { + if (group == plSimDefs::kGroupStatic || + group == plSimDefs::kGroupAvatarBlocker || + group == plSimDefs::kGroupDynamic) + proxyNode->SetDrawable(true); + } + + // If mass is zero and we're animated, set the mass to 1 so it will get a rigid + // body. Otherwise PhysX will make assumptions about the physical which will + // fail when it gets moved. + if (physProps->GetPhysAnim() && mass == 0.f) + mass = 1.f; + + TSTR sdata; + plMaxNode* baseNode = this; + while (!baseNode->GetParentNode()->IsRootNode()) + baseNode = (plMaxNode*)baseNode->GetParentNode(); + plKey roomKey = baseNode->GetRoomKey(); + if (!roomKey) + { + pErrMsg->Set(true, "Room Processing Error - Physics" "The Room that physics component %s is attached to should have already been\nprocessed.", GetName()); + return false; + } + + plMaxNode* subworld = physProps->GetSubworld(); + + PhysRecipe recipe; + recipe.mass = mass; + recipe.friction = physProps->GetFriction(); + recipe.restitution = physProps->GetRestitution(); + recipe.bounds = (plSimDefs::Bounds)physProps->GetBoundsType(); + recipe.group = group; + recipe.reportsOn = physProps->GetReportGroup(); + recipe.objectKey = GetKey(); + recipe.sceneNode = roomKey; + recipe.worldKey = subworld ? subworld->GetKey() : nil; + + plMaxMeshExtractor::NeutralMesh mesh; + plMaxMeshExtractor::Extract(mesh, proxyNode, bounds == plSimDefs::kBoxBounds, this); + + if (subworld) + recipe.l2s = subworld->GetWorldToLocal44() * mesh.fL2W; + else + recipe.l2s = mesh.fL2W; + + switch (bounds) + { + case plSimDefs::kBoxBounds: + { + hsPoint3 minV(FLT_MAX, FLT_MAX, FLT_MAX), maxV(-FLT_MAX, -FLT_MAX, -FLT_MAX); + for (int i = 0; i < mesh.fNumVerts; i++) + { + minV.fX = hsMinimum(mesh.fVerts[i].fX, minV.fX); + minV.fY = hsMinimum(mesh.fVerts[i].fY, minV.fY); + minV.fZ = hsMinimum(mesh.fVerts[i].fZ, minV.fZ); + maxV.fX = hsMaximum(mesh.fVerts[i].fX, maxV.fX); + maxV.fY = hsMaximum(mesh.fVerts[i].fY, maxV.fY); + maxV.fZ = hsMaximum(mesh.fVerts[i].fZ, maxV.fZ); + } + hsPoint3 width = maxV - minV; + recipe.bDimensions = width / 2; + recipe.bOffset = minV + (width / 2.f); + } + break; + case plSimDefs::kProxyBounds: + case plSimDefs::kExplicitBounds: + { + // if this is a detector then try to convert to a convex hull first... if that doesn't succeed then do it as an exact + if ( group == plSimDefs::kGroupDetector ) + { + // try converting to a convex hull mesh + recipe.meshStream = plPhysXCooking::CookHull(mesh.fNumVerts, mesh.fVerts,false); + if (recipe.meshStream) + { + plPXStream pxs(recipe.meshStream); + recipe.convexMesh = plSimulationMgr::GetInstance()->GetSDK()->createConvexMesh(pxs); + recipe.bounds = plSimDefs::kHullBounds; + // then test to see if the original mesh was convex (unless they said to skip 'em) +#ifdef WARNINGS_ON_CONCAVE_PHYSX_WORKAROUND + if ( !plPhysXCooking::fSkipErrors ) + { + if ( !plPhysXCooking::TestIfConvex(recipe.convexMesh, mesh.fNumVerts, mesh.fVerts) ) + { + int retStatus = pErrMsg->Set(true, "Physics Warning: PhysX workaround", "Detector region that is marked as exact and is concave but switching to convex hull for PhysX: %s", GetName()).CheckAskOrCancel(); + pErrMsg->Set(); + if ( retStatus == 1 ) // cancel? + plPhysXCooking::fSkipErrors = true; + } + } +#endif // WARNINGS_ON_CONCAVE_PHYSX_WORKAROUND + } + if (!recipe.meshStream) + { + if ( !pErrMsg->Set(true, "Physics Warning", "Detector region exact failed to be made a Hull, trying trimesh: %s", GetName()).Show() ) + pErrMsg->Set(); + recipe.meshStream = plPhysXCooking::CookTrimesh(mesh.fNumVerts, mesh.fVerts, mesh.fNumFaces, mesh.fFaces); + if (!recipe.meshStream) + { + pErrMsg->Set(true, "Physics Error", "Trimesh creation failed for physical %s", GetName()).Show(); + return false; + } + plPXStream pxs(recipe.meshStream); + recipe.triMesh = plSimulationMgr::GetInstance()->GetSDK()->createTriangleMesh(pxs); + } + } + else + { + recipe.meshStream = plPhysXCooking::CookTrimesh(mesh.fNumVerts, mesh.fVerts, mesh.fNumFaces, mesh.fFaces); + if (!recipe.meshStream) + { + pErrMsg->Set(true, "Physics Error", "Trimesh creation failed for physical %s", GetName()).Show(); + return false; + } + plPXStream pxs(recipe.meshStream); + recipe.triMesh = plSimulationMgr::GetInstance()->GetSDK()->createTriangleMesh(pxs); + } + } + break; + case plSimDefs::kSphereBounds: + { + hsPoint3 minV(FLT_MAX, FLT_MAX, FLT_MAX), maxV(-FLT_MAX, -FLT_MAX, -FLT_MAX); + for (int i = 0; i < mesh.fNumVerts; i++) + { + minV.fX = hsMinimum(mesh.fVerts[i].fX, minV.fX); + minV.fY = hsMinimum(mesh.fVerts[i].fY, minV.fY); + minV.fZ = hsMinimum(mesh.fVerts[i].fZ, minV.fZ); + maxV.fX = hsMaximum(mesh.fVerts[i].fX, maxV.fX); + maxV.fY = hsMaximum(mesh.fVerts[i].fY, maxV.fY); + maxV.fZ = hsMaximum(mesh.fVerts[i].fZ, maxV.fZ); + } + hsPoint3 width = maxV - minV; + recipe.radius = hsMaximum(width.fX, hsMaximum(width.fY, width.fZ)); + recipe.radius /= 2.f; + recipe.offset = minV + (width / 2.f); + } + break; + case plSimDefs::kHullBounds: + { + + + + if ( group == plSimDefs::kGroupDynamic ) + { + + + recipe.meshStream = plPhysXCooking::IMakePolytope(mesh); + + + if (!recipe.meshStream) + { + pErrMsg->Set(true, "Physics Error", "polyTope-convexhull failed for physical %s", GetName()).Show(); + return false; + } + } + else + { + recipe.meshStream = plPhysXCooking::CookHull(mesh.fNumVerts, mesh.fVerts,false); + if(!recipe.meshStream) + { + pErrMsg->Set(true, "Physics Error", "Convex hull creation failed for physical %s", GetName()).Show(); + return false; + } + } + plPXStream pxs(recipe.meshStream); + recipe.convexMesh = plSimulationMgr::GetInstance()->GetSDK()->createConvexMesh(pxs); + } + break; + } + + delete [] mesh.fFaces; + delete [] mesh.fVerts; + + // + // Create the physical + // + plPXPhysical* physical = TRACKED_NEW plPXPhysical; + + // add the object to the resource manager, keyed to the new name + plLocation nodeLoc = GetKey()->GetUoid().GetLocation(); + const char *objName = GetKey()->GetName(); + plKey physKey = hsgResMgr::ResMgr()->NewKey(objName, physical, nodeLoc, GetLoadMask()); + + if (!physical->Init(recipe)) + { + pErrMsg->Set(true, "Physics Error", "Physical creation failed for object %s", GetName()).Show(); + physKey->RefObject(); + physKey->UnRefObject(); + return false; + } + + physical->SetProperty(plSimulationInterface::kPinned, physProps->GetPinned()); + physical->SetProperty(plSimulationInterface::kPhysAnim, physProps->GetPhysAnim()); + physical->SetProperty(plSimulationInterface::kNoSynchronize, (physProps->GetNoSynchronize() != 0)); + physical->SetProperty(plSimulationInterface::kStartInactive, (physProps->GetStartInactive() != 0)); + physical->SetProperty(plSimulationInterface::kAvAnimPushable, (physProps->GetAvAnimPushable() != 0)); + + if(physProps->GetLOSBlockCamera()) + physical->AddLOSDB(plSimDefs::kLOSDBCameraBlockers); + if(physProps->GetLOSBlockUI()) + physical->AddLOSDB(plSimDefs::kLOSDBUIBlockers); + if(physProps->GetLOSBlockCustom()) + physical->AddLOSDB(plSimDefs::kLOSDBCustom); + if(physProps->GetLOSUIItem()) + physical->AddLOSDB(plSimDefs::kLOSDBUIItems); + if(physProps->GetLOSShootable()) + physical->AddLOSDB(plSimDefs::kLOSDBShootableItems); + if(physProps->GetLOSAvatarWalkable()) + physical->AddLOSDB(plSimDefs::kLOSDBAvatarWalkable); + if(physProps->GetLOSSwimRegion()) + physical->AddLOSDB(plSimDefs::kLOSDBSwimRegion); + + plSimulationInterface* si = TRACKED_NEW plSimulationInterface; + plKey pSiKey = hsgResMgr::ResMgr()->NewKey(objName, si, nodeLoc, GetLoadMask()); + + // link the simulation interface to the scene object + hsgResMgr::ResMgr()->AddViaNotify(pSiKey, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + // add the physical to the simulation interface + hsgResMgr::ResMgr()->AddViaNotify(physKey , TRACKED_NEW plIntRefMsg(pSiKey, plRefMsg::kOnCreate, 0, plIntRefMsg::kPhysical), plRefFlags::kActiveRef); + + return true; +} + +hsBool plMaxNode::MakeController(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + if (!CanConvert()) + return false; + + hsBool forceLocal = hsControlConverter::Instance().ForceLocal(this); + // Rember the force Local setting + hsBool CurrForceLocal = GetForceLocal(); // dont want to clobber it with false if componentPass made it true + forceLocal = (CurrForceLocal || forceLocal) ? true : false; // if it was set before, or is true now, make it true... + SetForceLocal(forceLocal); + + if( IsTMAnimated() && (!GetParentNode()->IsRootNode()) ) + { + ((plMaxNode*)GetParentNode())->SetForceLocal(true); + } + + return true; +} + +hsBool plMaxNode::MakeCoordinateInterface(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + const char* dbgNodeName = GetName(); + if (!CanConvert()) + return false; + plCoordinateInterface* ci = nil; + + hsBool forceLocal = GetForceLocal(); + + hsBool needCI = (!GetParentNode()->IsRootNode()) + || NumberOfChildren() + || forceLocal; + // If we have a transform, set up a coordinateinterface + if( needCI ) + { + hsMatrix44 loc2Par = GetLocalToParent44(); + hsMatrix44 par2Loc = GetParentToLocal44(); + if( GetFilterInherit() ) + ci = TRACKED_NEW plFilterCoordInterface; + else + ci = TRACKED_NEW plCoordinateInterface; + //------------------------- + // Get data from Node, then its key, then its name + //------------------------- + plKey pNodeKey = GetKey(); + hsAssert(pNodeKey, "Missing key for this Object"); + const char *pName = pNodeKey->GetName(); + plLocation nodeLoc = GetLocation(); + + plKey pCiKey = hsgResMgr::ResMgr()->NewKey(pName, ci,nodeLoc, GetLoadMask()); + ci->SetLocalToParent(loc2Par, par2Loc); + + hsBool usesPhysics = GetPhysicalProps()->IsUsed(); + ci->SetProperty(plCoordinateInterface::kCanEverDelayTransform, !usesPhysics); + ci->SetProperty(plCoordinateInterface::kDelayedTransformEval, !usesPhysics); + + hsgResMgr::ResMgr()->AddViaNotify(pCiKey, TRACKED_NEW plObjRefMsg(pNodeKey, plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + } + return true; +} + +hsBool plMaxNode::MakeModifiers(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + if (!CanConvert()) + return false; + + hsBool forceLocal = GetForceLocal(); + const char *dbgNodeName = GetName(); + + hsBool addMods = (!GetParentNode()->IsRootNode()) + || forceLocal; + + if (addMods) + { + // create / add modifiers + + // mf horse hack testing ViewFace which is already obsolete + if ( UserPropExists("ViewFacing") ) + { + plViewFaceModifier* pMod = TRACKED_NEW plViewFaceModifier; + if( UserPropExists("VFPivotFavorY") ) + pMod->SetFlag(plViewFaceModifier::kPivotFavorY); + else if( UserPropExists("VFPivotY") ) + pMod->SetFlag(plViewFaceModifier::kPivotY); + else if( UserPropExists("VFPivotTumble") ) + pMod->SetFlag(plViewFaceModifier::kPivotTumble); + else + pMod->SetFlag(plViewFaceModifier::kPivotFace); + if( UserPropExists("VFScale") ) + { + pMod->SetFlag(plViewFaceModifier::kScale); + TSTR sdata; + GetUserPropString("VFScale",sdata); + hsStringTokenizer toker; + toker.Reset(sdata, hsConverterUtils::fTagSeps); + int nGot = 0; + char* token; + hsVector3 scale; + scale.Set(1.f,1.f,1.f); + while( (nGot < 3) && (token = toker.next()) ) + { + switch( nGot ) + { + case 0: + scale.fZ = hsScalar(atof(token)); + break; + case 1: + scale.fX = scale.fZ; + scale.fY = hsScalar(atof(token)); + scale.fZ = 1.f; + break; + case 2: + scale.fZ = hsScalar(atof(token)); + break; + } + nGot++; + } + pMod->SetScale(scale); + } + AddModifier(pMod, GetName()); + } + } + return true; + +} + +hsBool plMaxNode::MakeParentOrRoomConnection(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + if (!CanConvert()) + return false; + + char *dbgNodeName = GetName(); + plSceneObject *pso = GetSceneObject(); + if( !GetParentNode()->IsRootNode() ) + { + plKey parKey = GetParentKey(); + plCoordinateInterface* ci = const_cast (pso->GetCoordinateInterface()); + hsAssert(ci,"Missing CI"); + + + plIntRefMsg* msg = TRACKED_NEW plIntRefMsg(parKey, plRefMsg::kOnCreate, -1, plIntRefMsg::kChildObject); + msg->SetRef(pso); + hsgResMgr::ResMgr()->AddViaNotify(msg, plRefFlags::kPassiveRef); + } + + hsgResMgr::ResMgr()->AddViaNotify(pso->GetKey(), TRACKED_NEW plNodeRefMsg(GetRoomKey(), plRefMsg::kOnCreate, -1, plNodeRefMsg::kObject), plRefFlags::kActiveRef); + return true; +} + +void plMaxNode::IWipeBranchDrawable(hsBool b) +{ + SetDrawable(b); + for (int i = 0; i < NumberOfChildren(); i++) + { + plMaxNode *pChild = (plMaxNode *)GetChildNode(i); + pChild->IWipeBranchDrawable(b); + } +} + +//// CanMakeMesh ///////////////////////////////////////////////////////////// +// Returns true if MakeMesh() on this node will result in spans being stored +// on a drawable. Takes in the object pointer to avoid having to do redundant +// work to get it. +// 9.25.2001 mcn - Made public so components can figure out if this node is +// meshable. + +hsBool plMaxNode::CanMakeMesh( Object *obj, plErrorMsg *pErrMsg, plConvertSettings *settings ) +{ + if( obj == nil ) + return false; + + if( UserPropExists( "Plasma2_Camera" ) ) + return false; + + if( !GetSwappableGeom() && !GetDrawable() ) + return false; + + if( GetParticleRelated() ) + return false; + + if( obj->CanConvertToType( triObjectClassID ) ) + return true; + + return false; +} + +void ITestAdjacencyRecur(const hsTArray* vertList, int iVert, hsBitVector& adjVerts) +{ + adjVerts.SetBit(iVert); + + int i; + for( i = 0; i < vertList[iVert].GetCount(); i++ ) + { + if( !adjVerts.IsBitSet(vertList[iVert][i]) ) + { + ITestAdjacencyRecur(vertList, vertList[iVert][i], adjVerts); + } + } +} + +hsBool ITestAdjacency(const hsTArray* vertList, int numVerts) +{ + hsBitVector adjVerts; + ITestAdjacencyRecur(vertList, 0, adjVerts); + + int i; + for( i = 0; i < numVerts; i++ ) + { + if( !adjVerts.IsBitSet(i) ) + return false; + } + return true; +} + +int IsGeoSpanConvexExhaust(const plGeometrySpan* span) +{ + // Brute force, check every point against every face + + UInt16* idx = span->fIndexData; + int numFaces = span->fNumIndices / 3; + + UInt32 stride = span->GetVertexSize(span->fFormat); + + UInt8* vertData = span->fVertexData; + int numVerts = span->fNumVerts; + + hsBool someIn = false; + hsBool someOut = false; + + int i; + for( i = 0; i < numFaces; i++ ) + { + // compute norm and dist for face + hsPoint3* pos[3]; + pos[0] = (hsPoint3*)(vertData + idx[0] * stride); + pos[1] = (hsPoint3*)(vertData + idx[1] * stride); + pos[2] = (hsPoint3*)(vertData + idx[2] * stride); + + hsVector3 edge01(pos[1], pos[0]); + hsVector3 edge02(pos[2], pos[0]); + + hsVector3 faceNorm = edge01 % edge02; + hsFastMath::NormalizeAppr(faceNorm); + hsScalar faceDist = faceNorm.InnerProduct(pos[0]); + + int j; + for( j = 0; j < numVerts; j++ ) + { + hsPoint3* p = (hsPoint3*)(vertData + idx[0] * stride); + + + hsScalar dist = p->InnerProduct(faceNorm) - faceDist; + + const hsScalar kSmall = 1.e-3f; + if( dist < -kSmall ) + someIn = true; + else if( dist > kSmall ) + someOut = true; + + if( someIn && someOut ) + return false; + } + + idx += 3; + } + return true; +} + +int IsGeoSpanConvex(plMaxNode* node, const plGeometrySpan* span) +{ + static int skipTest = false; + if( skipTest ) + return 0; + + // May not be now, but could become. + if( span->fFormat & plGeometrySpan::kSkinWeightMask ) + return 0; + + // May not be now, but could become. + if( node->GetConcave() || node->UserPropExists("XXXWaterColor") ) + return 0; + + if( span->fMaterial && span->fMaterial->GetLayer(0) && (span->fMaterial->GetLayer(0)->GetMiscFlags() & hsGMatState::kMiscTwoSided) ) + return 0; + + int numVerts = span->fNumVerts; + if( !numVerts ) + return 0; + + int numFaces = span->fNumIndices / 3; + if( !numFaces ) + return 0; + + const int kSmallNumFaces = 20; + if( numFaces <= kSmallNumFaces ) + return IsGeoSpanConvexExhaust(span); + + hsTArray* vertList = TRACKED_NEW hsTArray [numVerts]; + + hsTArray* normList = TRACKED_NEW hsTArray [numVerts]; + hsTArray* distList = TRACKED_NEW hsTArray [numVerts]; + + UInt16* idx = span->fIndexData; + + UInt32 stride = span->GetVertexSize(span->fFormat); + + UInt8* vertData = span->fVertexData; + + // For each face + int iFace; + for( iFace = 0; iFace < numFaces; iFace++ ) + { + // compute norm and dist for face + hsPoint3* pos[3]; + pos[0] = (hsPoint3*)(vertData + idx[0] * stride); + pos[1] = (hsPoint3*)(vertData + idx[1] * stride); + pos[2] = (hsPoint3*)(vertData + idx[2] * stride); + + hsVector3 edge01(pos[1], pos[0]); + hsVector3 edge02(pos[2], pos[0]); + + hsVector3 faceNorm = edge01 % edge02; + hsFastMath::NormalizeAppr(faceNorm); + hsScalar faceDist = faceNorm.InnerProduct(pos[0]); + + + // For each vert + int iVtx; + for( iVtx = 0; iVtx < 3; iVtx++ ) + { + int jVtx; + for( jVtx = 0; jVtx < 3; jVtx++ ) + { + if( iVtx != jVtx ) + { + // if idx[jVtx] not in list vertList[idx[iVtx]], add it + if( vertList[idx[iVtx]].kMissingIndex == vertList[idx[iVtx]].Find(idx[jVtx]) ) + vertList[idx[iVtx]].Append(idx[jVtx]); + } + } + normList[idx[iVtx]].Append(faceNorm); + distList[idx[iVtx]].Append(faceDist); + + } + idx += 3; + } + + hsBool someIn = false; + hsBool someOut = false; + int i; + for( i = 0; i < numVerts; i++ ) + { + int k; + for( k = 0; k < normList[i].GetCount(); k++ ) + { + int j; + for( j = 0; j < vertList[i].GetCount(); j++ ) + { + hsPoint3* pos = (hsPoint3*)(vertData + vertList[i][j] * stride); + hsScalar dist = pos->InnerProduct(normList[i][k]) - distList[i][k]; + + const hsScalar kSmall = 1.e-3f; + if( dist < -kSmall ) + someIn = true; + else if( dist > kSmall ) + someOut = true; + + if( someIn && someOut ) + goto cleanUp; + } + } + } + + if( !ITestAdjacency(vertList, numVerts) ) + someIn = someOut = true; + +cleanUp: + delete [] vertList; + delete [] normList; + delete [] distList; + + if( someIn && someOut ) + return 0; + + return someIn ? -1 : 1; +} + +// Returns nil if there isn't a sceneobject and a drawinterface. +plDrawInterface* plMaxNode::GetDrawInterface() +{ + plDrawInterface* di = nil; + plSceneObject* obj = GetSceneObject(); + if( obj ) + { + di = obj->GetVolatileDrawInterface(); + } + return di; +} + +hsBool plMaxNode::MakeMesh(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + hsTArray spanArray; + plDrawInterface *newDI = nil; + + hsBool gotMade = false; + hsBool haveAddedToSceneNode = false; + hsGMesh *myMesh = nil; + UInt32 i, triMeshIndex = (UInt32)-1; + const char *dbgNodeName = GetName(); + TSTR sdata; + hsStringTokenizer toker; + plLocation nodeLoc = GetLocation(); + + if (!GetSwappableGeom()) + { + if (!CanConvert()) + return false; + + if( UserPropExists( "Plasma2_Camera" ) || !GetDrawable() ) + { + SetMesh( nil ); + return true; + } + } + + if( GetSwappableGeomTarget() != (UInt32)-1) + { + // This node has no geometry on export, but will have some added at runtime, + // so it needs a special drawInterface + + plInstanceDrawInterface *newDI = TRACKED_NEW plInstanceDrawInterface; + newDI->fTargetID = GetSwappableGeomTarget(); + plKey pDiKey = hsgResMgr::ResMgr()->NewKey( GetKey()->GetName(), newDI, nodeLoc, GetLoadMask() ); + hsgResMgr::ResMgr()->AddViaNotify(pDiKey, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + plSwapSpansRefMsg *sMsg = TRACKED_NEW plSwapSpansRefMsg(pDiKey, plRefMsg::kOnCreate, -1, -1); + plDrawableSpans *drawable = IGetSceneNodeSpans(IGetDrawableSceneNode(pErrMsg), true, true); + hsgResMgr::ResMgr()->AddViaNotify(drawable->GetKey(), sMsg, plRefFlags::kActiveRef); + + return true; + } + + if( GetInstanced() ) + { + hsTArray nodes; + TimeValue t = hsConverterUtils::Instance().GetTime(GetInterface()); + UInt32 numInstances = IBuildInstanceList( GetObjectRef(), t, nodes, true ); + + /// Instanced, find an iNode in the list that's been converted already + for( i = 0; i < numInstances; i++ ) + { + if( nodes[ i ]->GetSceneObject() && nodes[ i ]->GetSceneObject()->GetDrawInterface() ) + { + /// Found it! + if( !IMakeInstanceSpans( nodes[ i ], spanArray, pErrMsg, settings ) ) + return false; + + gotMade = true; + break; + } + } + /// If we didn't find anything, nothing's got converted yet, so convert the first one + /// like normal + } + + // This has the side effect of calling SetMovable(true) if it should be and + // isn't already. So it needs to be before we make the mesh (and material). + // (Really, whatever makes it movable should do so then, but that has the potential + // to break other stuff, which I don't want to do 2 weeks before we ship). + hsBool movable = IsMovable(); + + if( !gotMade ) + { + if( !plMeshConverter::Instance().CreateSpans( this, spanArray, !settings->fDoPreshade ) ) + return false; + } + if( !spanArray.GetCount() ) + return true; + + for( i = 0; i < spanArray.GetCount(); i++ ) + spanArray[i]->fMaxOwner = GetKey()->GetName(); + + UInt32 shadeFlags = 0; + if( GetNoPreShade() ) + shadeFlags |= plGeometrySpan::kPropNoPreShade; + if( GetRunTimeLight() ) + shadeFlags |= plGeometrySpan::kPropRunTimeLight; + if( GetNoShadow() ) + shadeFlags |= plGeometrySpan::kPropNoShadow; + if( GetForceShadow() || GetAvatarSO() ) + shadeFlags |= plGeometrySpan::kPropForceShadow; + if( GetReverseSort() ) + shadeFlags |= plGeometrySpan::kPropReverseSort; + if( GetForceVisLOS() ) + shadeFlags |= plGeometrySpan::kVisLOS; + if( shadeFlags ) + { + for( i = 0; i < spanArray.GetCount(); i++ ) + spanArray[ i ]->fProps |= shadeFlags; + } + + hsBool DecalMat = false; + hsBool NonDecalMat = false; + + for (i = 0; i < spanArray.GetCount(); i++) + { + if (spanArray[i]->fMaterial->IsDecal()) + DecalMat = true; + else + NonDecalMat = true; + } + if (!(DecalMat ^ NonDecalMat)) + { + for( i = 0; i < spanArray.GetCount(); i++ ) + spanArray[ i ]->ClearBuffers(); + + if (pErrMsg->Set((plConvert::Instance().fWarned & plConvert::kWarnedDecalAndNonDecal) == 0, GetName(), + "This node has both regular and decal materials, and thus will be ignored.").CheckAskOrCancel()) + { + plConvert::Instance().fWarned |= plConvert::kWarnedDecalAndNonDecal; + } + pErrMsg->Set(false); + + return false; + } + + hsBool isDecal = IsLegalDecal(false); // Don't complain about the parent + + /// Get some stuff + hsBool forceLocal = GetForceLocal(); + + hsMatrix44 l2w = GetLocalToWorld44(); + hsMatrix44 w2l = GetWorldToLocal44(); + + /// 4.17.2001 mcn - TEMP HACK to test fog by adding a key to a bogus fogEnviron object to ALL spans +/* plFogEnvironment *myFog = nil; + plKey myFogKey = hsgResMgr::ResMgr()->FindExportAlias( "HACK_FOG", plFogEnvironment::Index() ); + if( myFogKey != nil ) + myFog = plFogEnvironment::ConvertNoRef( myFogKey->GetObjectPtr() ); + else + { + hsColorRGBA color; + color.Set( 0.5, 0.5, 1, 1 ); + + // Exp fog + myFog = TRACKED_NEW plFogEnvironment( plFogEnvironment::kExpFog, 700.f, 1.f, color ); + myFogKey = hsgResMgr::ResMgr()->NewKey( "HACK_FOG", myFog, nodeLoc ); + hsgResMgr::ResMgr()->AddExportAlias( "HACK_FOG", plFogEnvironment::Index(), myFogKey ); + } + + for( int j = 0; j < spanArray.GetCount(); j++ ) + { + spanArray[ j ].fFogEnviron = myFog; + } +*/ /// 4.17.2001 mcn - TEMP HACK end + + + plDrawable* drawable = nil; + plSceneNode* tmpNode = nil; + + /// Find the ice to add it to + + if (GetSwappableGeom()) // We just want to make a geo span, not actually add it to a drawable(interface) + { + plMaxNode *drawableSource = (plMaxNode *)(GetParentNode()->IsRootNode() ? this : GetParentNode()); + plSceneNode *tmpNode = drawableSource->IGetDrawableSceneNode(pErrMsg); + + plDrawableSpans *drawable = IGetSceneNodeSpans(tmpNode, true, true); + ISetupBones(drawable, spanArray, l2w, w2l, pErrMsg, settings); + + hsTArray *swapSpans = &GetSwappableGeom()->fSpans; + for (i = 0; i < spanArray.GetCount(); i++) + swapSpans->Append(spanArray.Get(i)); + + char tmpName[256]; + sprintf(tmpName, "%s_SMsh", GetName()); + hsgResMgr::ResMgr()->NewKey(tmpName, GetSwappableGeom(), GetLocation(), GetLoadMask()); + + return true; + } + + plMaxNode *nonDecalParent = this; + if( GetRoomKey() ) + { + tmpNode = plSceneNode::ConvertNoRef( GetRoomKey()->GetObjectPtr() ); + + if (isDecal) // If we're a decal, we just want to use our parent's drawable + { + plMaxNode *parent = (plMaxNode *)GetParentNode(); + + SetDecalLevel(parent->GetDecalLevel() + 1); + for( i = 0; i < spanArray.GetCount(); i++ ) + spanArray[ i ]->fDecalLevel = GetDecalLevel(); + } + + { + /// Make a new drawInterface (will assign stuff to it later) + newDI = TRACKED_NEW plDrawInterface; + plKey pDiKey = hsgResMgr::ResMgr()->NewKey( GetKey()->GetName(), newDI, nodeLoc, GetLoadMask() ); + hsgResMgr::ResMgr()->AddViaNotify(pDiKey, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + /// Attach the processed spans to the DI (through drawables) + IAssignSpansToDrawables( spanArray, newDI, pErrMsg, settings ); + } + } + + return true; +} + +plSceneNode *plMaxNode::IGetDrawableSceneNode(plErrorMsg *pErrMsg) +{ + plSceneNode *sn = nil; + + sn = plSceneNode::ConvertNoRef( GetRoomKey()->GetObjectPtr() ); + + return sn; +} + +//// IAssignSpansToDrawables ///////////////////////////////////////////////// +// Given a span array, adds it to the node's drawables, creating them if +// necessary. Then it takes the resulting indices and drawable pointers +// and assigns them to the given drawInterface. + +void plMaxNode::IAssignSpansToDrawables( hsTArray &spanArray, plDrawInterface *di, + plErrorMsg *pErrMsg, plConvertSettings *settings ) +{ + hsTArray opaqueArray, blendingArray, sortingArray; + plDrawableSpans *oSpans = nil, *bSpans = nil, *sSpans = nil; + + int sCount, oCount, bCount, i; + plSceneNode *tmpNode = nil; + hsMatrix44 l2w = GetLocalToWorld44(); + hsMatrix44 w2l = GetWorldToLocal44(); + UInt32 oIndex = (UInt32)-1, bIndex = (UInt32)-1, sIndex = UInt32(-1); + + tmpNode = IGetDrawableSceneNode(pErrMsg); +/* + /// Get sceneNode. If we're itinerant and not the parent node, this won't just + /// be GetRoomKey()->GetObjectPtr().... + if( GetItinerant() && !GetParentNode()->IsRootNode() ) + { + /// Step up to the top of the chain + plMaxNode *baseNode = this; + while( !baseNode->GetParentNode()->IsRootNode() ) + baseNode = (plMaxNode *)baseNode->GetParentNode(); + + if( baseNode->GetItinerant() ) + tmpNode = plSceneNode::ConvertNoRef( baseNode->GetRoomKey()->GetObjectPtr() ); + else + { + tmpNode = plSceneNode::ConvertNoRef( GetRoomKey()->GetObjectPtr() ); + + /// Warn, since we should only be itinerant if our parent is as well + pErrMsg->Set( true, "Warning", "Itinerant flag in child '%s' of non-itinerant tree. This should never happen. You should inform a programmer...", GetName() ).Show(); + } + } + else + tmpNode = plSceneNode::ConvertNoRef( GetRoomKey()->GetObjectPtr() ); +*/ + + hsBitVector convexBits; + /// Separate the array into two arrays, one opaque and one blending + for( sCount = 0, oCount = 0, bCount = 0, i = 0; i < spanArray.GetCount(); i++ ) + { + if( spanArray[ i ]->fProps & plGeometrySpan::kRequiresBlending ) + { + hsBool needFaceSort = !GetNoFaceSort() && !IsGeoSpanConvex(this, spanArray[i]); + if( needFaceSort ) + { + sCount++; + } + else + { + convexBits.SetBit(i); + bCount++; + } + } + else + oCount++; + } + + // Done this way, since expanding an hsTArray has the nasty side effect of just copying data, which we don't + // want when we have memory pointers... + opaqueArray.SetCount( oCount ); + blendingArray.SetCount( bCount ); + sortingArray.SetCount( sCount ); + for( sCount = 0, oCount = 0, bCount = 0, i = 0; i < spanArray.GetCount(); i++ ) + { + if( spanArray[ i ]->fProps & plGeometrySpan::kRequiresBlending ) + { + if( convexBits.IsBitSet(i) ) + blendingArray[ bCount++ ] = spanArray[ i ]; + else + sortingArray [ sCount++ ] = spanArray[ i ]; + } + else + opaqueArray[ oCount++ ] = spanArray[ i ]; + } + + /// Get some drawable pointers + if( opaqueArray.GetCount() > 0 ) + oSpans = plDrawableSpans::ConvertNoRef( IGetSceneNodeSpans( tmpNode, false ) ); + if( blendingArray.GetCount() > 0 ) + bSpans = plDrawableSpans::ConvertNoRef( IGetSceneNodeSpans( tmpNode, true, false ) ); + if( sortingArray.GetCount() > 0 ) + sSpans = plDrawableSpans::ConvertNoRef( IGetSceneNodeSpans( tmpNode, true, true ) ); + + if( oSpans != nil ) + IAssignSpan( oSpans, opaqueArray, oIndex, l2w, w2l, pErrMsg, settings ); + if( bSpans != nil ) + IAssignSpan( bSpans, blendingArray, bIndex, l2w, w2l, pErrMsg, settings ); + if( sSpans ) + IAssignSpan( sSpans, sortingArray, sIndex, l2w, w2l, pErrMsg, settings ); + + /// Now assign to the interface + if( oSpans ) + { + UInt8 iDraw = di->GetNumDrawables(); + di->SetDrawable( iDraw, oSpans ); + di->SetDrawableMeshIndex( iDraw, oIndex ); + } + + if( bSpans ) + { + UInt8 iDraw = di->GetNumDrawables(); + di->SetDrawable( iDraw, bSpans ); + di->SetDrawableMeshIndex( iDraw, bIndex ); + } + + if( sSpans ) + { + UInt8 iDraw = di->GetNumDrawables(); + di->SetDrawable( iDraw, sSpans ); + di->SetDrawableMeshIndex( iDraw, sIndex ); + } + +} + +//// IAssignSpan ///////////////////////////////////////////////////////////// +// Small utility function for IAssignSpansToDrawables, just does some of +// the low-down work that's identical for each drawable/spans/etc. + +void plMaxNode::IAssignSpan( plDrawableSpans *drawable, hsTArray &spanArray, UInt32 &index, + hsMatrix44 &l2w, hsMatrix44 &w2l, + plErrorMsg *pErrMsg, plConvertSettings *settings ) +{ + if( NumBones() ) + ISetupBones( drawable, spanArray, l2w, w2l, pErrMsg, settings ); + + // Assign spans to the drawables, plus set the volatile flag on the + // drawables for the SceneViewer, just in case it hasn't been set yet + if( settings->fSceneViewer ) + { + drawable->SetNativeProperty( plDrawable::kPropVolatile, true ); + index = drawable->AppendDISpans( spanArray, index, false ); + } + else + index = drawable->AddDISpans( spanArray, index ); + + if( GetItinerant() ) + drawable->SetNativeProperty(plDrawable::kPropCharacter, true); +} + +// Tiny helper for the function below +void SetSpansBoneInfo(hsTArray &spanArray, UInt32 baseMatrix, UInt32 numMatrices) +{ + int i; + for( i = 0; i < spanArray.GetCount(); i++ ) + { + spanArray[ i ]->fBaseMatrix = baseMatrix; + spanArray[ i ]->fNumMatrices = numMatrices; + } +} + +//// ISetupBones ///////////////////////////////////////////////////////////// +// Adds the given bones to the given drawable, then sets up the given spans +// with the right indices and sets the initial bone positions. +void plMaxNode::ISetupBones(plDrawableSpans *drawable, hsTArray &spanArray, + hsMatrix44 &l2w, hsMatrix44 &w2l, + plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + const char* dbgNodeName = GetName(); + + if( !NumBones() ) + return; + + plMaxBoneMap *boneMap = GetBoneMap(); + if (boneMap && boneMap->GetBaseMatrixIndex(drawable) != (UInt32)-1) + { + SetSpansBoneInfo(spanArray, boneMap->GetBaseMatrixIndex(drawable), boneMap->fNumBones); + return; + } + + int baseMatrix, i; + + UInt8 numBones = (boneMap ? boneMap->fNumBones : NumBones()) + 1; + plMaxNodeBase **boneArray = TRACKED_NEW plMaxNodeBase*[numBones]; + + if (boneMap) + boneMap->FillBoneArray(boneArray); + else + { + for (i = 0; i < NumBones(); i++) + { + boneArray[i] = GetBone(i); + } + } + + hsTArray initialB2W; + hsTArray initialW2B; + initialB2W.SetCount(numBones); + initialW2B.SetCount(numBones); + + hsTArray initialL2B; + hsTArray initialB2L; + initialL2B.SetCount(numBones); + initialB2L.SetCount(numBones); + + initialB2W[0].Reset(); + initialW2B[0].Reset(); + + initialL2B[0].Reset(); + initialB2L[0].Reset(); + + for( i = 1; i < numBones; i++ ) + { + hsMatrix44 b2w; + hsMatrix44 w2b; + hsMatrix44 l2b; + hsMatrix44 b2l; + + plMaxNodeBase *bone = boneArray[i-1]; + const char* dbgBoneName = bone->GetName(); + + Matrix3 localTM = bone->GetNodeTM(TimeValue(0)); + + b2w = Matrix3ToMatrix44(localTM); + b2w.GetInverse(&w2b); + + l2b = w2b * l2w; + b2l = w2l * b2w; + + initialB2W[i] = b2w; + initialW2B[i] = w2b; + + initialL2B[i] = l2b; + initialB2L[i] = b2l; + } + + // First, see if the bones are already set up appropriately. + // Appropriately means: + // a) Associated with the correct drawable (maybe others too, we don't care). + // b) InitialBone transforms match. If we (or another user of the same bone) + // are force localed, Our InitialBone won't match, because it also includes + // our transform as well as the bone's. If we've been flattened into world + // space, our transform is ident and we can share. This is the normal case + // in scene boning. So InitialBones have to match in count and matrix value. + baseMatrix = drawable->FindBoneBaseMatrix(initialL2B, GetSwappableGeom() != nil); + if( baseMatrix != UInt32(-1) ) + { + SetSpansBoneInfo(spanArray, baseMatrix, numBones); + delete [] boneArray; + return; + } + + baseMatrix = drawable->AppendDIMatrixSpans(numBones); + SetSpansBoneInfo(spanArray, baseMatrix, numBones); + if (boneMap) + boneMap->SetBaseMatrixIndex(drawable, baseMatrix); + + for( i = 1; i < numBones; i++ ) + { + plMaxNodeBase *bone = boneArray[i-1]; + plSceneObject* obj = bone->GetSceneObject(); + const char *dbgBoneName = bone->GetName(); + + // Pick which drawable to point the DI to + UInt8 iDraw = 0; + + /// Now create the actual bone DI, or grab it if it's already created + plDrawInterface *di = obj->GetVolatileDrawInterface(); + if( di ) + { + for( iDraw = 0; iDraw < di->GetNumDrawables(); iDraw++ ) + { + if( di->GetDrawable(iDraw) == drawable ) + break; + } + } + else + { + plLocation nodeLoc = bone->GetLocation(); + di = TRACKED_NEW plDrawInterface; + plKey diKey = hsgResMgr::ResMgr()->NewKey(GetKey()->GetName(), di, nodeLoc, GetLoadMask()); + hsgResMgr::ResMgr()->AddViaNotify(diKey, TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + } + + if( di->GetNumDrawables() <= iDraw ) + { + UInt32 diIndex = drawable->NewDIMatrixIndex(); + di->SetDrawableMeshIndex(iDraw, diIndex); + + di->SetDrawable(iDraw, drawable); + } + + + plDISpanIndex& skinIndices = drawable->GetDISpans(di->GetDrawableMeshIndex(iDraw)); + skinIndices.Append(baseMatrix + i); + + drawable->SetInitialBone(baseMatrix + i, initialL2B[i], initialB2L[i]); + di->SetTransform(initialB2W[i], initialW2B[i]); + } + delete [] boneArray; +} + +//// IMakeInstanceSpans ////////////////////////////////////////////////////// +// Given an instance node, instances the geoSpans that the node owns and +// stores them in the given array. + +hsBool plMaxNode::IMakeInstanceSpans( plMaxNode *node, hsTArray &spanArray, + plErrorMsg *pErrMsg, plConvertSettings *settings ) +{ + UInt8 iDraw; + int index, i; + + + plSceneObject *obj = node->GetSceneObject(); + if( !obj ) + return false; + + const plDrawInterface *di = obj->GetDrawInterface(); + if( !di ) + return false; + + hsBool setVisDists = false; + hsScalar minDist, maxDist; + if( hsMaterialConverter::HasVisDists(this, 0, minDist, maxDist) ) + { + setVisDists = true; + } + + index = 0; + spanArray.Reset(); + for( iDraw = 0; iDraw < di->GetNumDrawables(); iDraw++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(iDraw)); + if( !dr ) + continue; + if( di->GetDrawableMeshIndex(iDraw) == (UInt32)-1 ) + continue; + + plDISpanIndex disi = dr->GetDISpans(di->GetDrawableMeshIndex(iDraw)); + + spanArray.ExpandAndZero( spanArray.GetCount() + disi.fIndices.GetCount() ); + for( i = 0; i < disi.fIndices.GetCount(); i++ ) + { + spanArray[ index ] = TRACKED_NEW plGeometrySpan; + spanArray[ index ]->MakeInstanceOf( dr->GetGeometrySpan( disi.fIndices[ i ] ) ); + + if( setVisDists ) + { + spanArray[ index ]->fMinDist = (minDist); + spanArray[ index ]->fMaxDist = (maxDist); + } + + dr->GetGeometrySpan(disi.fIndices[i])->fProps |= plGeometrySpan::kInstanced; + + spanArray[ index++ ]->fProps |= plGeometrySpan::kInstanced; + } + } + + // Now that we have all of our instanced spans, we need to make sure we + // have the right materials. Why? There are some isolated cases (such as when "force + // material copy" is set) where we want to still instance the geometry but we want + // separate materials. In this case, GetMaterialArray() will return the right array of + // materials for our instanced node. However, since we've tossed everything except the + // final plGeometrySpans from MakeMesh(), we have to do a reverse lookup to see what + // materials get assigned to whom. GetMaterialArray() is guaranteed (according to Bob) + // to return materials in the same order for instanced nodes, so what we do is call + // GMA() for the old node and the new node (the old one should just be a lookup), then + // for each geoSpan look its old material up in the old array, find the matching material + // in the new array (i.e. same position) and assign that new material to the span. +#if 1 // Change this to 0 to just always use the same materials on instances (old, incorrect way) + Mtl *newMtl = GetMtl(), *origMtl = node->GetMtl(); + if( newMtl != nil && newMtl == origMtl ) // newMtl should == origMtl, but check just in case + { + hsTArray oldMaterials, newMaterials; + + if( hsMaterialConverter::IsMultiMat( newMtl ) ) + { + for( i = 0; i < newMtl->NumSubMtls(); i++ ) + { + hsMaterialConverter::Instance().GetMaterialArray( origMtl->GetSubMtl( i ), node, oldMaterials ); + hsMaterialConverter::Instance().GetMaterialArray( newMtl->GetSubMtl( i ), this, newMaterials ); + } + } + else + { + hsMaterialConverter::Instance().GetMaterialArray( origMtl, node, oldMaterials ); + hsMaterialConverter::Instance().GetMaterialArray( newMtl, this, newMaterials ); + } + + /// Now we have two arrays to let us map, so walk through our geoSpans and translate them! + /// The good thing is that this is all done before the spans are added to the drawable, + /// so we don't have to worry about reffing or unreffing or any of that messiness; all of + /// that will be done for us as part of the normal AppendDISpans() process. + for( i = 0; i < spanArray.GetCount(); i++ ) + { + int j; + + + // Find the span's original material + for( j = 0; j < oldMaterials.GetCount(); j++ ) + { + if( spanArray[ i ]->fMaterial == oldMaterials[ j ] ) + { + spanArray[ i ]->fMaterial = newMaterials[ j ]; + break; + } + } + + } + } +#endif + + return true; +} + +//// IBuildInstanceList ////////////////////////////////////////////////////// +// For the given object, builds a list of all the iNodes that have that +// object as their object. Returns the total node count + +UInt32 plMaxNode::IBuildInstanceList( Object *obj, TimeValue t, hsTArray &nodes, hsBool beMoreAccurate ) +{ + Object *thisObj = EvalWorldState( t ).obj; + DependentIterator di( obj ); + ReferenceMaker *rm; + plMaxNode *node; + plKey sceneNodeKey = GetRoomKey(); + + + /// Use the DependentIterator to loop through all the dependents of the object, + /// looking for nodes that use it + nodes.Reset(); + while( rm = di.Next() ) + { + if( rm->SuperClassID() == BASENODE_CLASS_ID ) + { + node = (plMaxNode *)rm; + if( node->EvalWorldState( t ).obj == thisObj ) + { + // Note: we CANNOT instance across pages (i.e. sceneNodes), so we need to make sure this + // INode will be in the same page as our master object + + // Also note: RoomKeys will be nil until we've finished the first component pass, so when + // we test this in ConvertValidate(), the keys will be nil and all objects will be "in the + // same room", even though they're not. This is not too bad, though, since the worst that + // could happen is the object gets forced local even when there ends up not being any other + // instances of it in the same page. Ooooh. + + if( sceneNodeKey == node->GetRoomKey() ) + { + // Make sure the materials generated for both of these nodes will be the same + if( IMaterialsMatch( node, beMoreAccurate ) ) + nodes.Append( node ); + } + } + } + } + + return nodes.GetCount(); +} + +//// IMaterialsMatch ///////////////////////////////////////////////////////// +// Given two nodes that are instances of each other, this function determines +// whether the resulting exported materials for both will be the same or not. +// If not, we need to not instance/share the geometry, since the UV channels +// could (and most likely will) be different. +// To test this, all we really need to do is check the return values of +// AlphaHackLayersNeeded(), since all the other material parameters will be +// identical due to these nodes being instances of each other. + +hsBool plMaxNode::IMaterialsMatch( plMaxNode *otherNode, hsBool beMoreAccurate ) +{ + Mtl *mtl = GetMtl(), *otherMtl = otherNode->GetMtl(); + if( mtl != otherMtl ) + return false; // The two objects have different materials, no way we + // can try to instance them now + if( mtl == nil ) + return true; // Both nodes have no material, works for me + + // If we're not told to be accurate, then we just quit here. This is because + // in the early passes, we *can't* be more accurate, since we won't have all + // the info yet, so we don't bother checking it + if( !beMoreAccurate ) + return true; + + if( hsMaterialConverter::IsMultiMat( mtl ) ) + { + int i; + for( i = 0; i < mtl->NumSubMtls(); i++ ) + { + if( AlphaHackLayersNeeded( i ) != otherNode->AlphaHackLayersNeeded( i ) ) + return false; + } + } + else + { + if( AlphaHackLayersNeeded( -1 ) != otherNode->AlphaHackLayersNeeded( -1 ) ) + return false; + } + + // They're close enough! + return true; +} + +hsBool plMaxNode::ShadeMesh(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + const char* dbgNodeName = GetName(); + + hsTArray spanArray; + + if( !(CanConvert() && GetDrawable()) ) + return true; + + plSceneObject* obj = GetSceneObject(); + if( !obj ) + return true; + + const plDrawInterface* di = obj->GetDrawInterface(); + if( !di ) + return true; + + UInt8 iDraw; + for( iDraw = 0; iDraw < di->GetNumDrawables(); iDraw++ ) + { + plDrawableSpans* dr = plDrawableSpans::ConvertNoRef(di->GetDrawable(iDraw)); + if( !dr ) + continue; + if( di->GetDrawableMeshIndex(iDraw) == (UInt32)-1 ) + continue; + + plDISpanIndex disi = dr->GetDISpans(di->GetDrawableMeshIndex(iDraw)); + + int i; + for( i = 0; i < disi.fIndices.GetCount(); i++ ) + { + spanArray.Append( dr->GetGeometrySpan( disi.fIndices[ i ] ) ); + } + + hsMatrix44 l2w = GetLocalToWorld44(); + hsMatrix44 w2l = GetWorldToLocal44(); + + /// Shade the spans now + // Either do vertex shading or generate a light map. + if( GetLightMapComponent() ) + { + plLightMapGen::Instance().MakeMaps(this, l2w, w2l, spanArray, pErrMsg, nil); + + // Since they were already pointers to the geometry spans, we don't have + // to re-stuff them. Horray! + } + else + { + hsVertexShader::Instance().ShadeNode(this, l2w, w2l, spanArray); + } + + if (settings && settings->fSceneViewer) + dr->RefreshDISpans(di->GetDrawableMeshIndex(iDraw)); + } + return true; +} + +hsBool plMaxNode::MakeOccluder(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + if( !UserPropExists("Occluder") ) + return true; + + hsBool twoSided = UserPropExists("OccTwoSided"); + hsBool isHole = UserPropExists("OccHole"); + + return ConvertToOccluder(pErrMsg, twoSided, isHole); +} + +static void IRemoveCollinearPoints(hsTArray& facePts) +{ + int i; + for( i = 0; i < facePts.GetCount(); ) + { + int j = i + 1 >= facePts.GetCount() ? 0 : i + 1; + int k = j + 1 >= facePts.GetCount() ? 0 : j + 1; + Point3 ab = FNormalize(facePts[i] - facePts[j]); + Point3 bc = FNormalize(facePts[j] - facePts[k]); + + const float kDotCutoff = 1.f - 1.e-3f; + float dot = DotProd(ab, bc); + if( (dot < kDotCutoff) && (dot > -kDotCutoff) ) + { + i++; + } + else + { + facePts.Remove(j); + } + } +} + +hsBool plMaxNode::ConvertToOccluder(plErrorMsg* pErrMsg, hsBool twoSided, hsBool isHole) +{ + if( !CanConvert() ) + return false; + + /// Get some stuff + plLocation nodeLoc = GetLocation(); + + hsBool moving = IsMovable(); + if( moving ) + moving++; + + Matrix3 tmp(true); + + Matrix3 maxL2V = GetLocalToVert(TimeValue(0)); + Matrix3 maxV2L = GetVertToLocal(TimeValue(0)); + + hsTArray polys; + + UInt32 polyInitFlags = plCullPoly::kNone; + if( isHole ) + polyInitFlags |= plCullPoly::kHole; + else + if( twoSided ) + polyInitFlags |= plCullPoly::kTwoSided; + + Object *obj = EvalWorldState(TimeValue(0)).obj; + if( obj->CanConvertToType(triObjectClassID) ) + { + TriObject *meshObj = (TriObject *)obj->ConvertToType(TimeValue(0), triObjectClassID); + if( meshObj ) + { + + Mesh mesh(meshObj->mesh); + + const float kNormThresh = hsScalarPI / 20.f; + const float kEdgeThresh = hsScalarPI / 20.f; + const float kBias = 0.1f; + const float kMaxEdge = -1.f; + const DWORD kOptFlags = OPTIMIZE_SAVESMOOTHBOUNDRIES; + + mesh.Optimize( + kNormThresh, // threshold of normal differences to preserve + kEdgeThresh, // When the angle between adjacent surface normals is less than this value the auto edge is performed (if the OPTIMIZE_AUTOEDGE flag is set). This angle is specified in radians. + kBias, // Increasing the bias parameter keeps triangles from becoming degenerate. range [0..1] (0 = no bias). + kMaxEdge, // This will prevent the optimize function from creating edges longer than this value. If this parameter is <=0 no limit is placed on the length of the edges. + kOptFlags, // Let them input using smoothing groups, but nothing else. + NULL); // progress bar + + + MNMesh mnMesh(mesh); + + mnMesh.EliminateCollinearVerts(); + mnMesh.EliminateCoincidentVerts(0.1f); + + // Documentation recommends MakeConvexPolyMesh over MakePolyMesh. Naturally, MakePolyMesh works better. +// mnMesh.MakeConvexPolyMesh(); + mnMesh.MakePolyMesh(); + mnMesh.MakeConvex(); +// mnMesh.MakePlanar(1.f * hsScalarPI / 180.f); // Completely ineffective. Winding up with majorly non-planar polys. + + mnMesh.Transform(maxV2L); + + polys.SetCount(mesh.getNumFaces()); + polys.SetCount(0); + + // Unfortunate problem here. Max is assuming that eventually this will get rendered, and so + // we need to avoid T-junctions. Fact is, T-junctions don't bother us at all, where-as colinear + // verts within a poly do (just as added overhead). + // So, to make this as painless (ha ha) as possible, we could detach each poly as we go to + // its own mnMesh, then eliminate colinear verts on that single poly mesh. Except + // EliminateCollinearVerts doesn't seem to actually do that. So we'll just have to + // manually detect and skip collinear verts. + hsTArray facePts; + int i; + for( i = 0; i < mnMesh.numf; i++ ) + { + MNFace& face = mnMesh.f[i]; + + facePts.SetCount(0); + int j; + for( j = 0; j < face.deg; j++ ) + { + facePts.Append(mnMesh.v[face.vtx[j]].p); + } + IRemoveCollinearPoints(facePts); + + if( facePts.GetCount() < 3 ) + continue; + + int lastAdded = 2; + + plCullPoly* poly = polys.Push(); + poly->fVerts.SetCount(0); + + Point3 p; + hsPoint3 pt; + + p = facePts[0]; + pt.Set(p.x, p.y, p.z); + poly->fVerts.Append(pt); + + p = facePts[1]; + pt.Set(p.x, p.y, p.z); + poly->fVerts.Append(pt); + + p = facePts[2]; + pt.Set(p.x, p.y, p.z); + poly->fVerts.Append(pt); + + for( j = lastAdded+1; j < facePts.GetCount(); j++ ) + { + p = facePts[j]; + pt.Set(p.x, p.y, p.z); + + hsVector3 a = hsVector3(&pt, &poly->fVerts[0]); + hsVector3 b = hsVector3(&poly->fVerts[lastAdded], &poly->fVerts[0]); + hsVector3 c = hsVector3(&poly->fVerts[lastAdded-1], &poly->fVerts[0]); + + hsVector3 aXb = a % b; + hsVector3 bXc = b % c; + + hsFastMath::Normalize(aXb); + hsFastMath::Normalize(bXc); + + + hsScalar dotSq = aXb.InnerProduct(bXc); + dotSq *= dotSq; + + const hsScalar kMinLenSq = 1.e-8f; + const hsScalar kMinDotFracSq = 0.998f * 0.998f; + + hsScalar lenSq = aXb.MagnitudeSquared() * bXc.MagnitudeSquared(); + if( lenSq < kMinLenSq ) + continue; + + // If not planar, move to new poly. + if( dotSq < lenSq * kMinDotFracSq ) + { + poly->InitFromVerts(polyInitFlags); + + poly = polys.Push(); + plCullPoly* lastPoly = &polys[polys.GetCount()-2]; + poly->fVerts.SetCount(0); + poly->fVerts.Append(lastPoly->fVerts[0]); + poly->fVerts.Append(lastPoly->fVerts[lastAdded]); + + lastAdded = 1; + } + + poly->fVerts.Append(pt); + lastAdded++; + } + + poly->InitFromVerts(polyInitFlags); + } + } + } + + if( polys.GetCount() ) + { + plOccluder* occ = nil; + plMobileOccluder* mob = nil; + if( moving ) + { + mob = TRACKED_NEW plMobileOccluder; + occ = mob; + } + else + { + occ = TRACKED_NEW plOccluder; + } + + occ->SetPolyList(polys); + occ->ComputeFromPolys(); + + // Register it. + char tmpName[256]; + if( GetKey() && GetKey()->GetName() && *GetKey()->GetName() ) + { + sprintf(tmpName, "%s_%s", GetKey()->GetName(), "Occluder"); + } + else + { + static int numOcc = 0; + sprintf(tmpName, "%s_%4.4d", "Occluder", numOcc); + } + plKey key = hsgResMgr::ResMgr()->NewKey( tmpName, occ, nodeLoc, GetLoadMask() ); + + hsgResMgr::ResMgr()->AddViaNotify(occ->GetKey(), TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + } + return true; +} + +hsBool plMaxNode::MakeLight(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + + if (!CanConvert()) + return false; + + if (!GetRunTimeLight()) + return true; + + /// Get some stuff + plLocation nodeLoc = GetLocation(); + hsBool forceLocal = GetForceLocal(); + + hsMatrix44 l2w = GetLocalToWorld44(); + hsMatrix44 w2l = GetWorldToLocal44(); + + hsMatrix44 lt2l = GetVertToLocal44(); + hsMatrix44 l2lt = GetLocalToVert44(); + + + plLightInfo* liInfo = nil; + + + liInfo = IMakeLight(pErrMsg, settings); + + if( liInfo ) + { + // 12.03.01 mcn - Um, we want RT lights to affect static objects if they're animated. So + // why wasn't this here a long time ago? :~ + if( IsMovable() || IsAnimatedLight() ) + liInfo->SetProperty(plLightInfo::kLPMovable, true); + + liInfo->SetTransform(l2w, w2l); + liInfo->SetLocalToLight(l2lt, lt2l); + + plKey key = hsgResMgr::ResMgr()->NewKey( GetKey()->GetName(), liInfo, nodeLoc, GetLoadMask() ); + + hsgResMgr::ResMgr()->AddViaNotify(liInfo->GetKey(), TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface), plRefFlags::kActiveRef); + + + // Only support projection for spots and dir lights for now. + if( plLimitedDirLightInfo::ConvertNoRef(liInfo) || plSpotLightInfo::ConvertNoRef(liInfo) ) + { + // Have to do this after the drawable gets a key. + IGetProjection(liInfo, pErrMsg); + + } + } + + return true; +} + +plLightInfo* plMaxNode::IMakeLight(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + plLightInfo* liInfo = nil; + Object *obj = EvalWorldState(timeVal).obj; + if( obj->ClassID() == Class_ID(OMNI_LIGHT_CLASS_ID, 0) ) + liInfo = IMakeOmni(pErrMsg, settings); + else + if( (obj->ClassID() == Class_ID(SPOT_LIGHT_CLASS_ID, 0)) || (obj->ClassID() == Class_ID(FSPOT_LIGHT_CLASS_ID, 0)) ) + liInfo = IMakeSpot(pErrMsg, settings); + else + if( (obj->ClassID() == Class_ID(DIR_LIGHT_CLASS_ID, 0)) || (obj->ClassID() == Class_ID(TDIR_LIGHT_CLASS_ID, 0)) ) + liInfo = IMakeDirectional(pErrMsg, settings); + else + if( obj->ClassID() == RTOMNI_LIGHT_CLASSID ) + liInfo = IMakeRTOmni(pErrMsg, settings); + else + if( (obj->ClassID() == RTSPOT_LIGHT_CLASSID) ) //|| (obj->ClassID() == Class_ID(FSPOT_LIGHT_CLASS_ID, 0)) ) + liInfo = IMakeRTSpot(pErrMsg, settings); + else + if( (obj->ClassID() == RTDIR_LIGHT_CLASSID) ) //|| (obj->ClassID() == Class_ID(FSPOT_LIGHT_CLASS_ID, 0)) ) + liInfo = IMakeRTDirectional(pErrMsg, settings); + else + if( obj->ClassID() == RTPDIR_LIGHT_CLASSID ) + liInfo = IMakeRTProjDirectional( pErrMsg, settings ); + + return liInfo; +} + +void plMaxNode::IGetLightAttenuation(plOmniLightInfo* liInfo, LightObject* light, LightState& ls) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + + hsScalar attenConst, attenLinear, attenQuadratic; + + float intens = ls.intens >= 0 ? ls.intens : -ls.intens; + float attenEnd = ls.attenEnd; + // Decay type 0:None, 1:Linear, 2:Squared + if( ls.useAtten ) + { + switch(((GenLight*)light)->GetDecayType()) + { + case 0: + case 1: + attenConst = 1.f; + attenLinear = (intens * plSillyLightKonstants::GetFarPowerKonst() - 1.f) / attenEnd; + attenQuadratic = 0; + break; + case 2: + attenConst = 1.f; + attenLinear = 0; + attenQuadratic = (intens * plSillyLightKonstants::GetFarPowerKonst() -1.f) / (attenEnd * attenEnd); + break; + case 3: + attenConst = intens; + attenLinear = 0.f; + attenQuadratic = 0.f; + liInfo->SetCutoffAttenuation( ( (GenLight *)light )->GetDecayRadius( timeVal ) ); + break; + } + } + else + { + attenConst = 1.f; + attenLinear = 0.f; + attenQuadratic = 0.f; + } + + liInfo->SetConstantAttenuation(attenConst); + liInfo->SetLinearAttenuation(attenLinear); + liInfo->SetQuadraticAttenuation(attenQuadratic); + +} + +hsBool plMaxNode::IGetRTLightAttenValues(IParamBlock2* ProperPB, hsScalar& attenConst, hsScalar& attenLinear, hsScalar& attenQuadratic, hsScalar &attenCutoff ) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + + float intens = ProperPB->GetFloat(plRTLightBase::kIntensity, timeVal); + if( intens < 0 ) + intens = -intens; + float attenEnd; + + attenEnd = ProperPB->GetFloat(plRTLightBase::kAttenMaxFalloffEdit, timeVal);//ls.attenEnd; + + // Decay Type New == 0 for Linear and 1 for Squared.... OLD and OBSOLETE:Decay type 0:None, 1:Linear, 2:Squared + // Oh, and now 2 = cutoff attenuation + if( ProperPB->GetInt(plRTLightBase::kUseAttenuationBool, timeVal)) + { + switch(ProperPB->GetInt(plRTLightBase::kAttenTypeRadio, timeVal))//((GenLight*)light)->GetDecayType()) + { + case 0: + attenConst = 1.f; + attenLinear = (intens * plSillyLightKonstants::GetFarPowerKonst() - 1.f) / attenEnd; + if( attenLinear < 0 ) + attenLinear = 0; + attenQuadratic = 0; + attenCutoff = attenEnd; + break; + case 1: + attenConst = 1.f; + attenLinear = 0; + attenQuadratic = (intens * plSillyLightKonstants::GetFarPowerKonst() - 1.f) / (attenEnd * attenEnd); + if( attenQuadratic < 0 ) + attenQuadratic = 0; + attenCutoff = attenEnd; + break; + case 2: + attenConst = intens; + attenLinear = 0.f; + attenQuadratic = 0.f; + attenCutoff = attenEnd; + break; + } + return true; + } + else + { + attenConst = 1.f; + attenLinear = 0.f; + attenQuadratic = 0.f; + attenCutoff = 0.f; + return true; + } + + return false; +} + +void plMaxNode::IGetRTLightAttenuation(plOmniLightInfo* liInfo, IParamBlock2* ProperPB) +{ + hsScalar attenConst, attenLinear, attenQuadratic, attenCutoff; + + if( IGetRTLightAttenValues(ProperPB, attenConst, attenLinear, attenQuadratic, attenCutoff) ) + { + liInfo->SetConstantAttenuation(attenConst); + liInfo->SetLinearAttenuation(attenLinear); + liInfo->SetQuadraticAttenuation(attenQuadratic); + liInfo->SetCutoffAttenuation( attenCutoff ); + } +} + +void plMaxNode::IGetLightColors(plLightInfo* liInfo, LightObject* light, LightState& ls) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + + Point3 color = light->GetRGBColor(timeVal); + float intensity = light->GetIntensity(timeVal); + + color *= intensity; + + liInfo->SetAmbient(hsColorRGBA().Set(0,0,0,1.f)); + if( ls.affectDiffuse ) + liInfo->SetDiffuse(hsColorRGBA().Set(color.x, color.y, color.z, intensity)); + else + liInfo->SetDiffuse(hsColorRGBA().Set(0,0,0,intensity)); + if( ls.affectSpecular ) + liInfo->SetSpecular(hsColorRGBA().Set(color.x, color.y, color.z, intensity)); + else + liInfo->SetSpecular(hsColorRGBA().Set(0,0,0,intensity)); + +} + +void plMaxNode::IGetRTLightColors(plLightInfo* liInfo, IParamBlock2* ProperPB) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + + Point3 color = ProperPB->GetPoint3(plRTLightBase::kLightColor, timeVal);//light->GetRGBColor(timeVal); + float intensity = ProperPB->GetFloat(plRTLightBase::kIntensity, timeVal); //light->GetIntensity(timeVal); + + color *= intensity; + + liInfo->SetAmbient(hsColorRGBA().Set(0,0,0,1.f)); + if( ProperPB->GetInt( plRTLightBase::kAffectDiffuse, timeVal ) ) + liInfo->SetDiffuse(hsColorRGBA().Set(color.x, color.y, color.z, intensity)); + else + liInfo->SetDiffuse(hsColorRGBA().Set(0,0,0,intensity)); + if( ProperPB->GetInt(plRTLightBase::kSpec, timeVal)) //ls.affectSpecular ) + { + Color spec = ProperPB->GetColor(plRTLightBase::kSpecularColorSwatch); + liInfo->SetSpecular(hsColorRGBA().Set(spec.r, spec.g, spec.b, intensity)); + } + else + liInfo->SetSpecular(hsColorRGBA().Set(0,0,0,intensity)); +} + +void plMaxNode::IGetCone(plSpotLightInfo* liInfo, LightObject* light, LightState& ls) +{ + + hsScalar inner = hsScalarDegToRad(ls.hotsize); + hsScalar outer = hsScalarDegToRad(ls.fallsize); + + /// 4.26.2001 mcn - MAX gives us full angles, but we want to store half angles + liInfo->SetSpotInner( inner / 2.0f ); + liInfo->SetSpotOuter( outer / 2.0f ); + liInfo->SetFalloff(1.f); +} + +void plMaxNode::IGetRTCone(plSpotLightInfo* liInfo, IParamBlock2* ProperPB) +{ + + //TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + hsScalar inner, outer; + + inner = hsScalarDegToRad(ProperPB->GetFloat(plRTLightBase::kHotSpot, timeVal)); //ls.hotsize); + outer = hsScalarDegToRad(ProperPB->GetFloat(plRTLightBase::kFallOff, timeVal)); //ls.fallsize); + + /// 4.26.2001 mcn - MAX gives us full angles, but we want to store half angles + liInfo->SetSpotInner( inner / 2.0f ); + liInfo->SetSpotOuter( outer / 2.0f ); + liInfo->SetFalloff(1.f); + +} + +plLightInfo* plMaxNode::IMakeSpot(plErrorMsg* pErrMsg, plConvertSettings* settings) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + Object *obj = EvalWorldState(timeVal).obj; + LightObject *light = (LightObject*)obj->ConvertToType(timeVal, Class_ID(SPOT_LIGHT_CLASS_ID,0)); + if( !light ) + light = (LightObject*)obj->ConvertToType(timeVal, Class_ID(FSPOT_LIGHT_CLASS_ID,0)); + + LightState ls; + if (!(REF_SUCCEED == light->EvalLightState(timeVal, Interval(timeVal, timeVal), &ls))) + { + pErrMsg->Set(true, GetName(), "Trouble evaluating light").CheckAndAsk(); + return nil; + } + + plSpotLightInfo* spot = TRACKED_NEW plSpotLightInfo; + + IGetLightColors(spot, light, ls); + + IGetLightAttenuation(spot, light, ls); + + IGetCone(spot, light, ls); + + if( obj != light ) + light->DeleteThis(); + + return spot; +} + +plLightInfo* plMaxNode::IMakeOmni(plErrorMsg* pErrMsg, plConvertSettings* settings) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + + Object *obj = EvalWorldState(timeVal).obj; + LightObject *light = (LightObject*)obj->ConvertToType(timeVal, + Class_ID(OMNI_LIGHT_CLASS_ID,0)); + + LightState ls; + if (!(REF_SUCCEED == light->EvalLightState(timeVal, Interval(timeVal, timeVal), &ls))) + { + pErrMsg->Set(true, GetName(), "Trouble evaluating light").CheckAndAsk(); + return nil; + } + + plOmniLightInfo* omni = TRACKED_NEW plOmniLightInfo; + + IGetLightAttenuation(omni, light, ls); + + IGetLightColors(omni, light, ls); + + if( obj != light ) + light->DeleteThis(); + + return omni; +} + +plLightInfo* plMaxNode::IMakeDirectional(plErrorMsg* pErrMsg, plConvertSettings* settings) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + + Object *obj = EvalWorldState(timeVal).obj; + LightObject *light = (LightObject*)obj->ConvertToType(timeVal, Class_ID(DIR_LIGHT_CLASS_ID,0)); + if( !light ) + light = (LightObject*)obj->ConvertToType(timeVal, Class_ID(TDIR_LIGHT_CLASS_ID,0)); + + LightState ls; + if (!(REF_SUCCEED == light->EvalLightState(timeVal, Interval(timeVal, timeVal), &ls))) + { + pErrMsg->Set(true, GetName(), "Trouble evaluating light").CheckAndAsk(); + return nil; + } + + plLightInfo* plasLight = nil; + if( light->GetProjMap() ) + { + plLimitedDirLightInfo* ldl = TRACKED_NEW plLimitedDirLightInfo; + + float sz = light->GetFallsize(timeVal, FOREVER); + float depth = 1000.f; + ldl->SetWidth(sz); + ldl->SetHeight(sz); + ldl->SetDepth(depth); + + plasLight = ldl; + } + else + { + + plDirectionalLightInfo* direct = TRACKED_NEW plDirectionalLightInfo; + plasLight = direct; + } + + IGetLightColors(plasLight, light, ls); + + if( obj != light ) + light->DeleteThis(); + + return plasLight; +} + +plLightInfo* plMaxNode::IMakeRTSpot(plErrorMsg* pErrMsg, plConvertSettings* settings) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + Object *obj = EvalWorldState(timeVal).obj; + Object *ThisObj = ((INode*)this)->GetObjectRef(); + IParamBlock2* ThisObjPB = ThisObj->GetParamBlockByID(plRTLightBase::kBlkSpotLight); + + + if(!obj->CanConvertToType(RTSPOT_LIGHT_CLASSID)) + { + pErrMsg->Set(true, GetName(), "Trouble evaluating light, improper classID").CheckAndAsk(); + return nil; + + } + + plSpotLightInfo* spot = TRACKED_NEW plSpotLightInfo; + + if(!ThisObjPB->GetInt(plRTLightBase::kLightOn)) + spot->SetProperty(plLightInfo::kDisable, true); + + IGetRTLightColors(spot,ThisObjPB); + + IGetRTLightAttenuation(spot,ThisObjPB); + + IGetRTCone(spot, ThisObjPB); + + //plSpotModifier* liMod = TRACKED_NEW plSpotModifier; + + //GetRTLightColAnim(ThisObjPB, liMod); + //GetRTLightAttenAnim(ThisObjPB, liMod); + //GetRTConeAnim(ThisObjPB, liMod); + + //IAttachRTLightModifier(liMod); + +// if( obj != light ) + // light->DeleteThis(); + + return spot; +} + + +plLightInfo* plMaxNode::IMakeRTOmni(plErrorMsg* pErrMsg, plConvertSettings* settings) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + + Object *obj = EvalWorldState(timeVal).obj; + + Object *ThisObj = ((INode*)this)->GetObjectRef(); + IParamBlock2* ThisObjPB = ThisObj->GetParamBlockByID(plRTLightBase::kBlkOmniLight); + + plOmniLightInfo* omni = TRACKED_NEW plOmniLightInfo; + + if(!ThisObjPB->GetInt(plRTLightBase::kLightOn)) + omni->SetProperty(plLightInfo::kDisable, true); + + IGetRTLightAttenuation(omni, ThisObjPB); + + IGetRTLightColors(omni, ThisObjPB); + + //plOmniModifier* liMod = TRACKED_NEW plOmniModifier; + + //GetRTLightColAnim(ThisObjPB, liMod); + //GetRTLightAttenAnim(ThisObjPB, liMod); + + //IAttachRTLightModifier(liMod); + + +// if( obj != light ) +// light->DeleteThis(); + + return omni; +} + + +plLightInfo* plMaxNode::IMakeRTDirectional(plErrorMsg* pErrMsg, plConvertSettings* settings) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + + Object *obj = EvalWorldState(timeVal).obj; + Object *ThisObj = ((INode*)this)->GetObjectRef(); + IParamBlock2* ThisObjPB = ThisObj->GetParamBlockByID(plRTLightBase::kBlkTSpotLight); + + + plDirectionalLightInfo* direct = TRACKED_NEW plDirectionalLightInfo; + + if(!ThisObjPB->GetInt(plRTLightBase::kLightOn)) + direct->SetProperty(plLightInfo::kDisable, true); + + IGetRTLightColors(direct, ThisObjPB); + + //plLightModifier* liMod = TRACKED_NEW plLightModifier; + + //GetRTLightColAnim(ThisObjPB, liMod); + + //IAttachRTLightModifier(liMod); + + +// if( obj != light ) +// light->DeleteThis(); + + return direct; +} + +//// IMakeRTProjDirectional ////////////////////////////////////////////////// +// Conversion function for RT Projected Directional lights + +plLightInfo *plMaxNode::IMakeRTProjDirectional( plErrorMsg *pErrMsg, plConvertSettings *settings ) +{ + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + + Object *obj = EvalWorldState(timeVal).obj; + Object *ThisObj = ((INode*)this)->GetObjectRef(); + + IParamBlock2 *mainPB = ThisObj->GetParamBlockByID( plRTLightBase::kBlkMain ); + IParamBlock2 *projPB = ThisObj->GetParamBlockByID( plRTProjDirLight::kBlkProj ); + + plLimitedDirLightInfo *light = TRACKED_NEW plLimitedDirLightInfo; + + light->SetWidth( projPB->GetFloat( plRTProjDirLight::kWidth ) ); + light->SetHeight( projPB->GetFloat( plRTProjDirLight::kHeight ) ); + light->SetDepth( projPB->GetFloat( plRTProjDirLight::kRange ) ); + + if( !mainPB->GetInt( plRTLightBase::kLightOn ) ) + light->SetProperty( plLightInfo::kDisable, true ); + + IGetRTLightColors( light, mainPB ); + + //plLightModifier *liMod = TRACKED_NEW plLightModifier; + + //GetRTLightColAnim( mainPB, liMod ); + + //IAttachRTLightModifier(liMod); + + return light; +} + +hsBool plMaxNode::IGetProjection(plLightInfo* li, plErrorMsg* pErrMsg) +{ + hsBool persp = false; + TimeValue timeVal = hsConverterUtils::Instance().GetTime(GetInterface()); + Object *obj = EvalWorldState(timeVal).obj; + LightObject *light = (LightObject*)obj->ConvertToType(timeVal, RTSPOT_LIGHT_CLASSID); + + if( light ) + persp = true; + + if( !light ) + light = (LightObject*)obj->ConvertToType(timeVal, RTPDIR_LIGHT_CLASSID); + + if( !light ) + return false; + + hsBool retVal = false; + Texmap* projMap = light->GetProjMap(); + if( !projMap ) + return false; + + plConvert& convert = plConvert::Instance(); + if( projMap->ClassID() != LAYER_TEX_CLASS_ID ) + { + if( pErrMsg->Set(!(convert.fWarned & plConvert::kWarnedWrongProj), GetName(), + "Only Plasma Layers supported for projection").CheckAskOrCancel() ) + { + convert.fWarned |= plConvert::kWarnedWrongProj; + } + pErrMsg->Set(false); + return false; + } + + IParamBlock2 *pb = nil; + + Class_ID cid = obj->ClassID(); + + // Get the paramblock + if (cid == RTSPOT_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTLightBase::kBlkSpotLight); + else if (cid == RTOMNI_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTLightBase::kBlkOmniLight); + else if (cid == RTDIR_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTLightBase::kBlkTSpotLight); + else if (cid == RTPDIR_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTProjDirLight::kBlkProj); + + // Have the layer converter process this layer directly + plLayerConverter::Instance().MuteWarnings(); + plLayerInterface* proj = plLayerConverter::Instance().ConvertTexmap( projMap, this, 0, true, false ); + plLayerConverter::Instance().UnmuteWarnings(); + if( proj ) + { + plLayer* projLay = plLayer::ConvertNoRef(proj->BottomOfStack()); + if( projLay && projLay->GetTexture() ) + { + if( persp ) + projLay->SetMiscFlags(projLay->GetMiscFlags() | hsGMatState::kMiscPerspProjection); + else + projLay->SetMiscFlags(projLay->GetMiscFlags() | hsGMatState::kMiscOrthoProjection); + projLay->SetUVWSrc(projLay->GetUVWSrc() | plLayerInterface::kUVWPosition); + projLay->SetClampFlags(hsGMatState::kClampTexture); + projLay->SetZFlags(hsGMatState::kZNoZWrite); + + switch( pb->GetInt(plRTLightBase::kProjTypeRadio) ) + { + default: + case plRTLightBase::kIlluminate: + projLay->SetBlendFlags(hsGMatState::kBlendMult); + li->SetProperty(plLightInfo::kLPOverAll, false); + break; + case plRTLightBase::kAdd: + projLay->SetBlendFlags(hsGMatState::kBlendAdd); + li->SetProperty(plLightInfo::kLPOverAll, true); + break; + case plRTLightBase::kMult: + projLay->SetBlendFlags(hsGMatState::kBlendMult | hsGMatState::kBlendInvertColor | hsGMatState::kBlendInvertFinalColor); + li->SetProperty(plLightInfo::kLPOverAll, true); + break; + case plRTLightBase::kMADD: + projLay->SetBlendFlags(hsGMatState::kBlendMADD); + li->SetProperty(plLightInfo::kLPOverAll, true); + break; + } + + hsgResMgr::ResMgr()->AddViaNotify(proj->GetKey(), TRACKED_NEW plGenRefMsg(li->GetKey(), plRefMsg::kOnCreate, 0, 0), plRefFlags::kActiveRef); + + li->SetShadowCaster(false); + + li->SetProperty(plLightInfo::kLPMovable, true); + + + retVal = true; + } + else + { + char buff[256]; + if( projMap && projMap->GetName() && *projMap->GetName() ) + sprintf(buff, "Can't find projected bitmap - %s", projMap->GetName()); + else + sprintf(buff, "Can't find projected bitmap - "); + if( pErrMsg->Set(!(convert.fWarned & plConvert::kWarnedMissingProj), GetName(), + buff).CheckAskOrCancel() ) + convert.fWarned |= plConvert::kWarnedMissingProj; + pErrMsg->Set(false); + retVal = false; + } + } + else + { + if( pErrMsg->Set(!(convert.fWarned & plConvert::kWarnedWrongProj), GetName(), + "Failure to convert projection map - check type.").CheckAskOrCancel() ) + convert.fWarned |= plConvert::kWarnedWrongProj; + pErrMsg->Set(false); + retVal = false; + } + + if( light != obj ) + light->DeleteThis(); + + return retVal; +} +/* +hsBool plMaxNode::IAttachRTLightModifier(plLightModifier* liMod) +{ + if( liMod->HasAnima() ) + { + liMod->DefaultAnimation(); + CreateModifierKey(liMod, "_ANIMA"); + AddModifier(liMod); + } + else + { + delete liMod; + return false; + } + return true; +} +*/ +bool plMaxNode::IsAnimatedLight() +{ + Object *obj = GetObjectRef(); + if (!obj) + return false; + + const char* dbgNodeName = GetName(); + + Class_ID cid = obj->ClassID(); + + if (!(cid == RTSPOT_LIGHT_CLASSID || + cid == RTOMNI_LIGHT_CLASSID || + cid == RTDIR_LIGHT_CLASSID || + cid == RTPDIR_LIGHT_CLASSID)) + return false; + + IParamBlock2 *pb = nil; + + // Get the paramblock + if (cid == RTSPOT_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTLightBase::kBlkSpotLight); + else if (cid == RTOMNI_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTLightBase::kBlkOmniLight); + else if (cid == RTDIR_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTLightBase::kBlkTSpotLight); + else if (cid == RTPDIR_LIGHT_CLASSID) + pb = obj->GetParamBlockByID(plRTLightBase::kBlkMain); + + hsControlConverter& cc = hsControlConverter::Instance(); + + // Is the color animated? + Control *colorCtl = pb->GetController( ParamID( plRTLightBase::kLightColor ) ); + if (colorCtl && cc.HasKeyTimes(colorCtl)) + return true; + + // Is the specularity animated? + Control *specCtl = pb->GetController( ParamID( plRTLightBase::kSpecularColorSwatch ) ); + if (specCtl && cc.HasKeyTimes(specCtl)) + return true; + + // Is the attenuation animated? (Spot and Omni lights only) + if (cid == RTSPOT_LIGHT_CLASSID || cid == RTOMNI_LIGHT_CLASSID) + { + Control *falloffCtl = pb->GetController( ParamID( plRTLightBase::kAttenMaxFalloffEdit ) ); + if (falloffCtl && cc.HasKeyTimes(falloffCtl)) + return true; + } + + // Is the cone animated? (Spot only) + if (cid == RTSPOT_LIGHT_CLASSID) + { + Control *innerCtl = pb->GetController( ParamID( plRTLightBase::kHotSpot ) ); + if (innerCtl && cc.HasKeyTimes(innerCtl)) + return true; + + Control *outerCtl = pb->GetController( ParamID( plRTLightBase::kFallOff ) ); + if (outerCtl && cc.HasKeyTimes(outerCtl)) + return true; + } + + return false; +} + + +void plMaxNode::GetRTLightAttenAnim(IParamBlock2* ProperPB, plAGAnim *anim) +{ + if( ProperPB->GetInt(plRTLightBase::kUseAttenuationBool, TimeValue(0)) ) + { + Control* falloffCtl = ProperPB->GetController(ParamID(plRTLightBase::kAttenMaxFalloffEdit)); + if( falloffCtl ) + { + plLeafController* subCtl; + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + subCtl = hsControlConverter::Instance().MakeScalarController(falloffCtl, this); + else + subCtl = hsControlConverter::Instance().MakeScalarController(falloffCtl, this, + anim->GetStart(), anim->GetEnd()); + + if( subCtl ) + { + if( ProperPB->GetInt(plRTLightBase::kAttenTypeRadio, TimeValue(0)) == 2 ) + { + // Animation of a cutoff attenuation, which only needs a scalar channel + plOmniCutoffApplicator *app = TRACKED_NEW plOmniCutoffApplicator(); + app->SetChannelName(GetName()); + plScalarControllerChannel *chan = TRACKED_NEW plScalarControllerChannel(subCtl); + app->SetChannel(chan); + anim->AddApplicator(app); + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + anim->ExtendToLength(subCtl->GetLength()); + } + else + { + hsBool distSq = ProperPB->GetInt(plRTLightBase::kAttenTypeRadio, TimeValue(0)); + + int i; + for( i = 0; i < subCtl->GetNumKeys(); i++ ) + { + hsScalarKey *key = subCtl->GetScalarKey(i); + if (key) + { + hsScalar attenEnd = key->fValue; + TimeValue tv = key->fFrame * MAX_TICKS_PER_FRAME; + hsScalar intens = ProperPB->GetFloat(plRTLightBase::kIntensity, tv); + hsScalar newVal = (intens * plSillyLightKonstants::GetFarPowerKonst() - 1.f) / attenEnd; + if( distSq ) + newVal /= attenEnd; + + key->fValue = newVal; + } + hsBezScalarKey *bezKey = subCtl->GetBezScalarKey(i); + if (bezKey) + { + hsScalar attenEnd = bezKey->fValue; + TimeValue tv = bezKey->fFrame * MAX_TICKS_PER_FRAME; + hsScalar intens = ProperPB->GetFloat(plRTLightBase::kIntensity, tv); + hsScalar newVal = (intens * plSillyLightKonstants::GetFarPowerKonst() - 1.f) / attenEnd; + if( distSq ) + newVal /= attenEnd; + + /// From the chain rule, fix our tangents. + bezKey->fInTan *= -(intens * plSillyLightKonstants::GetFarPowerKonst() - 1.f) / (attenEnd*attenEnd); + if( distSq ) + bezKey->fInTan *= 2.f / attenEnd; + + bezKey->fOutTan *= -(intens * plSillyLightKonstants::GetFarPowerKonst() - 1.f) / (attenEnd*attenEnd); + if( distSq ) + bezKey->fOutTan *= 2.f / attenEnd; + + bezKey->fValue = newVal; + } + } + plAGApplicator *app; + if (distSq) + app = TRACKED_NEW plOmniSqApplicator; + else + app = TRACKED_NEW plOmniApplicator; + + app->SetChannelName(GetName()); + plScalarControllerChannel *chan = TRACKED_NEW plScalarControllerChannel(subCtl); + app->SetChannel(chan); + anim->AddApplicator(app); + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + anim->ExtendToLength(subCtl->GetLength()); + + hsScalar attenConst, attenLinear, attenQuadratic, attenCutoff; + IGetRTLightAttenValues(ProperPB, attenConst, attenLinear, attenQuadratic, attenCutoff); + + plOmniLightInfo *info = plOmniLightInfo::ConvertNoRef(GetSceneObject()->GetGenericInterface(plOmniLightInfo::Index())); + if (info) + { + hsPoint3 initAtten(attenConst, attenLinear, attenQuadratic); + info->SetConstantAttenuation(attenConst); + info->SetLinearAttenuation(attenLinear); + info->SetQuadraticAttenuation(attenQuadratic); + } + else + hsAssert(false, "Failed to find light info"); + } + } + } + } +} + +void plMaxNode::IAdjustRTColorByIntensity(plController* ctl, IParamBlock2* ProperPB) +{ + plLeafController* simp = plLeafController::ConvertNoRef(ctl); + plCompoundController* comp; + if( simp ) + { + int i; + for( i = 0; i < simp->GetNumKeys(); i++ ) + { + hsPoint3Key* key = simp->GetPoint3Key(i); + if (key) + { + TimeValue tv = key->fFrame * MAX_TICKS_PER_FRAME; + hsScalar intens = ProperPB->GetFloat(plRTLightBase::kIntensity, tv); + key->fValue *= intens; + } + hsBezPoint3Key* bezKey = simp->GetBezPoint3Key(i); + if (bezKey) + { + TimeValue tv = bezKey->fFrame * MAX_TICKS_PER_FRAME; + hsScalar intens = ProperPB->GetFloat(plRTLightBase::kIntensity, tv); + bezKey->fInTan *= intens; + bezKey->fOutTan *= intens; + bezKey->fValue *= intens; + } + } + } + else if( comp = plCompoundController::ConvertNoRef(ctl) ) + { + int j; + for( j = 0; j < 3; j++ ) + { + IAdjustRTColorByIntensity(comp->GetController(j), ProperPB); + } + } +} + +void plMaxNode::GetRTLightColAnim(IParamBlock2* ProperPB, plAGAnim *anim) +{ + Control* ambientCtl = nil; // Ambient not currently supported + Control* colorCtl = ProperPB->GetController(ParamID(plRTLightBase::kLightColor)); + Control* specCtl = ProperPB->GetController(ParamID(plRTLightBase::kSpecularColorSwatch)); + plPointControllerChannel *chan; + + if( ambientCtl ) + { + plController* ctl; + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + ctl = hsControlConverter::Instance().MakeColorController(ambientCtl, this); + else + ctl = hsControlConverter::Instance().MakeColorController(ambientCtl, this, anim->GetStart(), anim->GetEnd()); + + if( ctl ) + { + plLightAmbientApplicator *app = TRACKED_NEW plLightAmbientApplicator(); + app->SetChannelName(GetName()); + chan = TRACKED_NEW plPointControllerChannel(ctl); + app->SetChannel(chan); + anim->AddApplicator(app); + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + anim->ExtendToLength(ctl->GetLength()); + } + } + if( colorCtl ) + { + plController* ctl; + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + ctl = hsControlConverter::Instance().MakeColorController(colorCtl, this); + else + ctl = hsControlConverter::Instance().MakeColorController(colorCtl, this, anim->GetStart(), anim->GetEnd()); + + if( ctl ) + { + IAdjustRTColorByIntensity(ctl, ProperPB); + plLightDiffuseApplicator *app = TRACKED_NEW plLightDiffuseApplicator(); + app->SetChannelName(GetName()); + chan = TRACKED_NEW plPointControllerChannel(ctl); + app->SetChannel(chan); + anim->AddApplicator(app); + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + anim->ExtendToLength(ctl->GetLength()); + } + } + if( specCtl ) + { + plController* ctl; + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + ctl = hsControlConverter::Instance().MakeColorController(specCtl, this); + else + ctl = hsControlConverter::Instance().MakeColorController(specCtl, this, anim->GetStart(), anim->GetEnd()); + + if( ctl ) + { + plLightSpecularApplicator *app = TRACKED_NEW plLightSpecularApplicator(); + app->SetChannelName(GetName()); + chan = TRACKED_NEW plPointControllerChannel(ctl); + app->SetChannel(chan); + anim->AddApplicator(app); + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + anim->ExtendToLength(ctl->GetLength()); + } + } +} + +void plMaxNode::GetRTConeAnim(IParamBlock2* ProperPB, plAGAnim *anim) +{ + Control* innerCtl = ProperPB->GetController(ParamID(plRTLightBase::kHotSpot)); + Control* outerCtl = ProperPB->GetController(ParamID(plRTLightBase::kFallOff)); + plScalarControllerChannel *chan; + + if( innerCtl ) + { + plLeafController* ctl; + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + ctl = hsControlConverter::Instance().MakeScalarController(innerCtl, this); + else + ctl = hsControlConverter::Instance().MakeScalarController(innerCtl, this, anim->GetStart(), anim->GetEnd()); + + if( ctl ) + { + plSpotInnerApplicator *app = TRACKED_NEW plSpotInnerApplicator(); + app->SetChannelName(GetName()); + chan = TRACKED_NEW plScalarControllerChannel(ctl); + app->SetChannel(chan); + anim->AddApplicator(app); + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + anim->ExtendToLength(ctl->GetLength()); + } + } + if( outerCtl ) + { + plController* ctl; + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + ctl = hsControlConverter::Instance().MakeScalarController(outerCtl, this); + else + ctl = hsControlConverter::Instance().MakeScalarController(outerCtl, this, anim->GetStart(), anim->GetEnd()); + + if( ctl ) + { + plSpotOuterApplicator *app = TRACKED_NEW plSpotOuterApplicator(); + app->SetChannelName(GetName()); + chan = TRACKED_NEW plScalarControllerChannel(ctl); + app->SetChannel(chan); + anim->AddApplicator(app); + if (!strcmp(anim->GetName(), ENTIRE_ANIMATION_NAME)) + anim->ExtendToLength(ctl->GetLength()); + } + } +} + +plXImposterComp* plMaxNode::GetXImposterComp() +{ + int count = NumAttachedComponents(); + int i; + for( i = 0; i < count; i++ ) + { + // See if any are a x-imposter component. + plComponentBase *comp = GetAttachedComponent(i); + if( comp && (comp->ClassID() == XIMPOSTER_COMP_CID) ) + { + plXImposterComp* ximp = (plXImposterComp*)comp; + return ximp; + } + } + return nil; +} + +Point3 plMaxNode::GetFlexibility() +{ + UInt32 count = NumAttachedComponents(); + + // Go through all the components attached to this node + for (UInt32 i = 0; i < count; i++) + { + // See if any are a flexibility component. + plComponentBase *comp = GetAttachedComponent(i); + if( comp && (comp->ClassID() == FLEXIBILITY_COMP_CID) ) + { + plFlexibilityComponent* flex = (plFlexibilityComponent*)comp; + return flex->GetFlexibility(); + } + } + return Point3(0.f, 0.f, 0.f); +} + +plLightMapComponent* plMaxNode::GetLightMapComponent() +{ + UInt32 count = NumAttachedComponents(); + + // Go through all the components attached to this node + for (UInt32 i = 0; i < count; i++) + { + // See if any are a flexibility component. + plComponentBase *comp = GetAttachedComponent(i); + if( comp && (comp->ClassID() == LIGHTMAP_COMP_CID) ) + { + plLightMapComponent* lmap = (plLightMapComponent*)comp; + return lmap; + } + } + return nil; +} + +plDrawableCriteria plMaxNode::GetDrawableCriteria(hsBool needBlending, hsBool needSorting) +{ + plRenderLevel level = needBlending ? GetRenderLevel(needBlending) : plRenderLevel::OpaqueRenderLevel(); + + if( GetSortAsOpaque() ) + level.Set(plRenderLevel::kOpaqueMajorLevel, level.Minor()); + + UInt32 crit = 0; + if( needBlending ) + { + if( needSorting && !GetNoFaceSort() ) + crit |= plDrawable::kCritSortFaces; + if( !GetNoSpanSort() ) + crit |= plDrawable::kCritSortSpans; + } + + if( GetItinerant() ) + crit |= plDrawable::kCritCharacter; + + plDrawableCriteria retVal(crit, level, GetLoadMask()); + + if( GetEnviron() ) + retVal.fType |= plDrawable::kEnviron; + if( GetEnvironOnly() ) + retVal.fType &= ~plDrawable::kNormal; + + return retVal; +} + +//// IGetSceneNodeSpans ////////////////////////////////////////////////////// +// Gets the required drawableSpans from a sceneNode. Creates a new one +// if it can't find one. + +plDrawableSpans *plMaxNode::IGetSceneNodeSpans( plSceneNode *node, hsBool needBlending, hsBool needSorting ) +{ + + plDrawableSpans *spans; + char tmpName[ 512 ]; + plLocation nodeLoc = GetLocation(); + + if( !needBlending ) + needSorting = false; + + plDrawableCriteria crit = GetDrawableCriteria(needBlending, needSorting); + + spans = plDrawableSpans::ConvertNoRef( node->GetMatchingDrawable( crit ) ); + + if( spans != nil ) + { + if( GetNoSpanReSort() ) + { + spans->SetNativeProperty(plDrawable::kPropNoReSort, true); + } + return spans; + } + + + /// Couldn't find--create and return it + spans = TRACKED_NEW plDrawableSpans; + if( needBlending ) + { + /// Blending (deferred) spans + spans->SetCriteria( crit ); + sprintf( tmpName, "%s_%8.8x_%xBlendSpans", node->GetKeyName(), crit.fLevel.fLevel, crit.fCriteria); + } + else + { + /// Normal spans + spans->SetCriteria( crit ); + sprintf( tmpName, "%s_%8.8x_%xSpans", node->GetKeyName(), crit.fLevel.fLevel, crit.fCriteria); + } + + if (GetSwappableGeomTarget() != (UInt32)-1 || GetSwappableGeom()) // We intend to swap geometry with this node... flag the drawable as volatile + { + if( GetItinerant() ) + spans->SetNativeProperty(plDrawable::kPropCharacter, true); + + spans->SetNativeProperty( plDrawable::kPropVolatile, true ); + } + + // Add a key for the spans + plKey key = hsgResMgr::ResMgr()->NewKey( tmpName, spans, nodeLoc, GetLoadMask() ); + + spans->SetSceneNode(node->GetKey()); + + /// Created! Return it now... + if( GetNoSpanReSort() ) + spans->SetNativeProperty(plDrawable::kPropNoReSort, true); + + return spans; +} + +hsBool plMaxNode::SetupPropertiesPass(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + // TEMP + if (IsComponent()) + return false; + // End TEMP + + hsBool ret = true; + + UInt32 count = NumAttachedComponents(); + + // Go through all the components attached to this node + for (UInt32 i = 0; i < count; i++) + { + // For each one, call the requested function. If any of the attached components + // return false this function will return false. + plComponentBase *comp = GetAttachedComponent(i); + + if (comp->IsExternal()) + { + if (!((plComponentExt*)comp)->SetupProperties(this, &gComponentTools, pErrMsg)) + ret = false; + } + else + { + if (!((plComponent*)comp)->SetupProperties(this, pErrMsg)) + ret = false; + } + } + + if( ret ) + { + // Now loop through all the plPassMtlBase-derived materials that are applied to this node + Mtl *mtl = GetMtl(); + if( mtl != nil && !GetParticleRelated() ) + { + if( hsMaterialConverter::IsMultiMat( mtl ) || hsMaterialConverter::IsMultipassMat( mtl ) || hsMaterialConverter::IsCompositeMat( mtl ) ) + { + int i; + for (i = 0; i < mtl->NumSubMtls(); i++) + { + plPassMtlBase *pass = plPassMtlBase::ConvertToPassMtl( mtl->GetSubMtl( i ) ); + if( pass != nil ) + { + if( !pass->SetupProperties( this, pErrMsg ) ) + ret = false; + } + } + } + else + { + plPassMtlBase *pass = plPassMtlBase::ConvertToPassMtl( mtl ); + if( pass != nil ) + { + if( !pass->SetupProperties( this, pErrMsg ) ) + ret = false; + } + } + } + } + if( ret ) + { + plMaxNode* parent = (plMaxNode*)GetParentNode(); + if( parent && IsLegalDecal(false) ) + { + AddRenderDependency(parent); + SetNoSpanSort(true); + SetNoFaceSort(true); + SetNoDeferDraw(true); + } + } + + return ret; +} + +hsBool plMaxNode::FirstComponentPass(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + // TEMP + if (IsComponent()) + return false; + // End TEMP + + hsBool ret = true; + + if (!CanConvert()) + return ret; + UInt32 count = NumAttachedComponents(); + + // Go through all the components attached to this node + for (UInt32 i = 0; i < count; i++) + { + // For each one, call the requested function. If any of the attached components + // return false this function will return false. + plComponentBase *comp = GetAttachedComponent(i); + + if (comp->IsExternal()) + { + if (!((plComponentExt*)comp)->PreConvert(this, &gComponentTools, pErrMsg)) + ret = false; + } + else + { + if (!((plComponent*)comp)->PreConvert(this, pErrMsg)) + ret = false; + } + } + + return ret; +} + +hsBool plMaxNode::ConvertComponents(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + // TEMP + if (IsComponent()) + return false; + // End TEMP + + hsBool ret = true; + + char *dbgNodeName = GetName(); + if (!CanConvert()) + return ret; + + UInt32 count = NumAttachedComponents(); + + // Go through all the components attached to this node + for (UInt32 i = 0; i < count; i++) + { + // For each one, call the requested function. If any of the attached components + // return false this function will return false. + plComponentBase *comp = GetAttachedComponent(i); + + if (comp->IsExternal()) + { + if (!((plComponentExt*)comp)->Convert(this, &gComponentTools, pErrMsg)) + ret = false; + } + else + { + if (!((plComponent*)comp)->Convert(this, pErrMsg)) + ret = false; + } + } + + return ret; +} + +hsBool plMaxNode::DeInitComponents(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + // TEMP + if (IsComponent()) + return false; + // End TEMP + + hsBool ret = true; + + char *dbgNodeName = GetName(); + if (!CanConvert()) + return ret; + + UInt32 count = NumAttachedComponents(); + + // Go through all the components attached to this node + for (UInt32 i = 0; i < count; i++) + { + // For each one, call the requested function. If any of the attached components + // return false this function will return false. + plComponentBase *comp = GetAttachedComponent(i); + + if (comp->IsExternal()) + { + if (!((plComponentExt*)comp)->DeInit(this, &gComponentTools, pErrMsg)) + ret = false; + } + else + { + if (!((plComponent*)comp)->DeInit(this, pErrMsg)) + ret = false; + } + } + + if( ret ) + { + // Now loop through all the plPassMtlBase-derived materials that are applied to this node + // So we can call ConvertDeInit() on them + Mtl *mtl = GetMtl(); + if( mtl != nil && !GetParticleRelated() ) + { + if( hsMaterialConverter::IsMultiMat( mtl ) || hsMaterialConverter::IsMultipassMat( mtl ) || hsMaterialConverter::IsCompositeMat( mtl ) ) + { + int i; + for (i = 0; i < mtl->NumSubMtls(); i++) + { + plPassMtlBase *pass = plPassMtlBase::ConvertToPassMtl( mtl->GetSubMtl( i ) ); + if( pass != nil ) + { + if( !pass->ConvertDeInit( this, pErrMsg ) ) + ret = false; + } + } + } + else + { + plPassMtlBase *pass = plPassMtlBase::ConvertToPassMtl( mtl ); + if( pass != nil ) + { + if( !pass->ConvertDeInit( this, pErrMsg ) ) + ret = false; + } + } + } + } + + return ret; +} + +hsBool plMaxNode::ClearData(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + RemoveAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaAgeChunk); + RemoveAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaDistChunk); + RemoveAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaRoomChunk); + + RemoveAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaMaxNodeDataChunk); +// RemoveAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaSceneViewerChunk); + RemoveAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaLightChunk); + + return true; +} + +// HASAGMOD +// Little special-purpose thing to see if a node has an animation graph modifier on it. +plAGModifier *plMaxNode::HasAGMod() +{ + char *name = GetName(); + if (CanConvert()) + { + plSceneObject *SO = GetSceneObject(); + int numMods = SO->GetNumModifiers(); + + for (int i = 0; i < numMods; i++) + { + const plModifier *mod = SO->GetModifier(i); + + if(plAGModifier::ConvertNoRef(mod)) { + return (plAGModifier *)mod; + } + } + } + return nil; +} + +plAGMasterMod *plMaxNode::GetAGMasterMod() +{ + char *name = GetName(); + if (CanConvert()) + { + plSceneObject *SO = GetSceneObject(); + int numMods = SO->GetNumModifiers(); + + for (int i = 0; i < numMods; i++) + { + const plModifier *mod = SO->GetModifier(i); + + if(plAGMasterMod::ConvertNoRef(mod)) { + return (plAGMasterMod *)mod; + } + } + } + return nil; +} + + +// SETUPBONESALIASESRECUR +void plMaxNode::SetupBonesAliasesRecur(const char *rootName) +{ + if(CanConvert()) { + if (!HasAGMod()) { + const char *nameToUse; + + // parse UserPropsBuf for entire BoneName line + char localName[256]; + TSTR propsBuf; + GetUserPropBuffer(propsBuf); + char* start=strstr(propsBuf, "BoneName="); + if (!start) + start=strstr(propsBuf, "bonename="); + const int len = hsStrlen("BoneName="); + if(start && UserPropExists("BoneName")) + { + start+=len; + int i=0; + while(*start != '\n' && *start != '\0' && *start) + { + hsAssert(i<256, "localName overflow"); + localName[i++]=*start++; + } + localName[i]=0; + + nameToUse = localName; + + } + else + { + const char *nodeName = GetName(); + // char str[256]; + // sprintf(str, "Missing 'BoneName=foo' UserProp, on object %s, using node name", nodeName ? nodeName : "?"); + // hsAssert(false, str); + + nameToUse = nodeName; + } + + /* char aliasName[256]; + sprintf(aliasName, "%s_%s", rootName, nameToUse); + + plUoid* uoid = hsgResMgr::ResMgr()->FindAlias(aliasName, plSceneObject::Index()); + if( !uoid ) + { + plAliasModifier* pAlMod = TRACKED_NEW plAliasModifier; + pAlMod->SetAlias(aliasName); + AddModifier(pAlMod); + } + */ + plAGModifier *mod = TRACKED_NEW plAGModifier(nameToUse); + AddModifier(mod, GetName()); + } + } + + int j = 0; + for( j = 0; j < NumberOfChildren(); j++ ) + ((plMaxNode*)GetChildNode(j))->SetupBonesAliasesRecur(rootName); +} + +void plMaxNode::SetDISceneNodeSpans( plDrawInterface *di, hsBool needBlending ) +{ + // This poorly named function is currently only used by ParticleComponent. + // di->GetNumDrawables() will always be zero. In general, particles only + // need a blending drawable, which can always be index zero since it's the only + // one. + di->SetDrawable( di->GetNumDrawables(), + IGetSceneNodeSpans(plSceneNode::ConvertNoRef( GetRoomKey()->GetObjectPtr() ), + needBlending)); +} + +plMaxNode* plMaxNode::GetBonesRoot() +{ + ISkin* skin = FindSkinModifier(); + if( !skin ) + return nil; + + INode* bone = skin->GetBone(0); + + if( !bone ) + return nil; + + while( !bone->GetParentNode()->IsRootNode() ) + bone = bone->GetParentNode(); + + plMaxNode* boneRoot = (plMaxNode*)bone; + + if( !(boneRoot && boneRoot->CanConvert()) ) + return nil; + + return boneRoot; +} + +void plMaxNode::GetBonesRootsRecur(hsTArray& nodes) +{ + plMaxNode* bRoot = GetBonesRoot(); + if( bRoot ) + { + int idx = nodes.Find(bRoot); + if( idx == nodes.kMissingIndex ) + nodes.Append(bRoot); + } + + int i; + for( i = 0; i < NumberOfChildren(); i++ ) + ((plMaxNode*)GetChildNode(i))->GetBonesRootsRecur(nodes); +} + +plSceneObject* plMaxNode::MakeCharacterHierarchy(plErrorMsg *pErrMsg) +{ + plSceneObject* playerRoot = GetSceneObject(); + if( pErrMsg->Set(playerRoot->GetDrawInterface() != nil, GetName(), "Non-helper as player root").CheckAndAsk() ) + return nil; + const char *playerRootName = GetName(); + + hsTArray bonesRoots; + int i; + for( i = 0; i < NumberOfChildren(); i++ ) + ((plMaxNode*)GetChildNode(i))->GetBonesRootsRecur(bonesRoots); + + if( pErrMsg->Set(bonesRoots.GetCount() > 1, playerRootName, "Found multiple bones hierarchies").CheckAndAsk() ) + return nil; + + if( bonesRoots.GetCount() ) + { + bonesRoots[0]->SetupBonesAliasesRecur(playerRootName); + + plSceneObject* boneRootObj = bonesRoots[0]->GetSceneObject(); + + if( pErrMsg->Set(boneRootObj == nil, playerRootName, "No scene object for the bones root").CheckAndAsk() ) + return nil; + + if( boneRootObj != playerRoot ) + hsMessageBox("This avatar's bone hierarchy does not have the avatar root node linked as a parent. " + "This may cause the avatar draw incorrectly.", playerRootName, hsMessageBoxNormal); + } + + return playerRoot; +} + +// Takes all bones found on this node (and any descendents) and sets up a single palette +void plMaxNode::SetupBoneHierarchyPalette(plMaxBoneMap *bones /* = nil */) +{ + const char* dbgNodeName = GetName(); + + if( !CanConvert() ) + return; + + if (GetBoneMap()) + return; + + if (bones == nil) + { + bones = TRACKED_NEW plMaxBoneMap(); + bones->fOwner = this; + } + + if (UserPropExists("Bone")) + bones->AddBone(this); + + int i; + for (i = 0; i < NumBones(); i++) + bones->AddBone(GetBone(i)); + + SetBoneMap(bones); + + for (i = 0; i < NumberOfChildren(); i++) + ((plMaxNode*)GetChildNode(i))->SetupBoneHierarchyPalette(bones); + + // If we were the one to start this whole thing, then sort all the bones now that + // they've been added. + if (bones->fOwner == this) + bones->SortBones(); +} + +hsBool plMaxNode::IsLegalDecal(hsBool checkParent /* = true */) +{ + Mtl *mtl = GetMtl(); + if (mtl == nil || GetParticleRelated()) + return false; + if (hsMaterialConverter::IsMultiMat(mtl)) + { + int i; + for (i = 0; i < mtl->NumSubMtls(); i++) + { + if (!hsMaterialConverter::IsDecalMat(mtl->GetSubMtl(i))) + return false; + } + } + else if (!hsMaterialConverter::IsDecalMat(mtl)) + return false; + + return true; +} + +int plMaxNode::NumUVWChannels() +{ + hsBool deleteIt; + + TriObject* triObj = GetTriObject(deleteIt); + if( triObj ) + { + Mesh* mesh = &(triObj->mesh); + + // note: There's been a bit of a change with UV accounting. There are two variables, numChannels + // and numBlendChannels. The first represents texture UV channels MAX gives us, the latter is + // the number of extra blending channels the current material uses to handle its effects. + + // numMaps includes map #0, which is the vertex colors. So subtract 1 to get the # of uv maps... + int numChannels = mesh->getNumMaps() - 1; + if( numChannels > plGeometrySpan::kMaxNumUVChannels ) + numChannels = plGeometrySpan::kMaxNumUVChannels; + + /// Check the mapping channels. See, MAX likes to tell us we have mapping channels + /// but the actual channel pointer is nil. When MAX tries to render the scene, it says that the + /// object in question has no UV channel. So apparently we have to check numChannels *and* + /// that each mapChannel is non-nil... + int i; + for( i = 0; i < numChannels; i++ ) + { + // i + 1 is exactly what IGenerateUVs uses, so I'm not questioning it... + if( mesh->mapFaces( i + 1 ) == nil ) + { + numChannels = i; + break; + } + } + int numUsed = hsMaterialConverter::MaxUsedUVWSrc(this, GetMtl()); + plLightMapComponent* lmc = GetLightMapComponent(); + if( lmc ) + { + if( (lmc->GetUVWSrc() < numChannels) && (lmc->GetUVWSrc() >= numUsed) ) + numUsed = lmc->GetUVWSrc() + 1; + } + if( numChannels > numUsed ) + numChannels = numUsed; + + if( GetWaterDecEnv() ) + numChannels = 3; + + if( deleteIt ) + triObj->DeleteThis(); + + return numChannels; + } + + return 0; +} + +//// IGet/SetCachedAlphaHackValue //////////////////////////////////////////// +// Pair of functions to handle accessing the TArray cache on plMaxNodeData. +// See AlphaHackLayersNeeded() for details. + +int plMaxNode::IGetCachedAlphaHackValue( int iSubMtl ) +{ + plMaxNodeData *pDat = GetMaxNodeData(); + if( pDat == nil ) + return -1; + + hsTArray *cache = pDat->GetAlphaHackLayersCache(); + if( cache == nil ) + return -1; + + iSubMtl++; + if( iSubMtl >= cache->GetCount() ) + return -1; + + return (*cache)[ iSubMtl ]; +} + +void plMaxNode::ISetCachedAlphaHackValue( int iSubMtl, int value ) +{ + plMaxNodeData *pDat = GetMaxNodeData(); + if( pDat == nil ) + return; + + hsTArray *cache = pDat->GetAlphaHackLayersCache(); + if( cache == nil ) + { + cache = TRACKED_NEW hsTArray; + pDat->SetAlphaHackLayersCache( cache ); + } + + iSubMtl++; + + if( iSubMtl >= cache->GetCount() ) + { + int i = cache->GetCount(); + cache->ExpandAndZero( iSubMtl + 1 ); + for( ; i < cache->GetCount(); i++ ) + (*cache)[ i ] = -1; + } + + (*cache)[ iSubMtl ] = value; +} + +//// AlphaHackLayersNeeded /////////////////////////////////////////////////// +// Updated 8.13.02 mcn - Turns out this function is actually very slow, and +// it also happens to be used a lot in testing instanced objects and whether +// they really can be instanced or not. Since the return value of this +// function will be constant after the SetupProperties() pass (and undefined +// before), we cache the value now after the first time we calculate it. +// Note: mf said that putting long comments in are good so long as most of +// them aren't obscenities, so I'm trying to keep the #*$&(*#$ obscenities +// to a minimum here. + +int plMaxNode::AlphaHackLayersNeeded(int iSubMtl) +{ + const char* dbgNodeName = GetName(); + + int cached = IGetCachedAlphaHackValue( iSubMtl ); + if( cached != -1 ) + return cached; + + int numVtxOpacChanAvail = VtxAlphaNotAvailable() ? 0 : 1; + + int numVtxOpacChanNeeded = hsMaterialConverter::NumVertexOpacityChannelsRequired(this, iSubMtl); + + cached = numVtxOpacChanNeeded - numVtxOpacChanAvail; + ISetCachedAlphaHackValue( iSubMtl, cached ); + return cached; +} + +// Will our lighting pay attention to vertex alpha values? +hsBool plMaxNode::VtxAlphaNotAvailable() +{ + if( NonVtxPreshaded() || GetParticleRelated()) + return false; + + return true; +} + +hsBool plMaxNode::NonVtxPreshaded() +{ + if( GetForceMatShade() ) + return false; + + if( GetAvatarSO() != nil || + hsMaterialConverter::Instance().HasMaterialDiffuseOrOpacityAnimation(this) ) + return false; + + if( GetRunTimeLight() && !hsMaterialConverter::Instance().HasEmissiveLayer(this) ) + return true; + + return( GetLightMapComponent() != nil ); +} + +TriObject* plMaxNode::GetTriObject(hsBool& deleteIt) +{ + // Get da object + Object *obj = EvalWorldState(TimeValue(0)).obj; + if( obj == nil ) + return nil; + + if( !obj->CanConvertToType(triObjectClassID) ) + return nil; + + // Convert to triMesh object + TriObject *meshObj = (TriObject *)obj->ConvertToType(TimeValue(0), triObjectClassID); + if( meshObj == nil ) + return nil; + + deleteIt = meshObj != obj; + + return meshObj; +} + +//// GetNextSoundIdx ///////////////////////////////////////////////////////// +// Starting at 0, returns an incrementing index for each maxNode. Useful for +// assigning indices to sound objects attached to the node. + +UInt32 plMaxNode::GetNextSoundIdx( void ) +{ + UInt32 idx = GetSoundIdxCounter(); + SetSoundIdxCounter( idx + 1 ); + return idx; +} + +//// IsPhysical ////////////////////////////////////////////////////////////// +// Fun temp hack function to tell if a maxNode is physical. Useful after +// preConvert (checks for a physical on the simInterface) + +hsBool plMaxNode::IsPhysical( void ) +{ + if( GetSceneObject() && GetSceneObject()->GetSimulationInterface() && + GetSceneObject()->GetSimulationInterface()->GetPhysical() ) + return true; + + return false; +} + + +plPhysicalProps *plMaxNode::GetPhysicalProps() +{ + plMaxNodeData *pDat = GetMaxNodeData(); + if (pDat) + return pDat->GetPhysicalProps(); + + return nil; +} + +//// FindPageKey ///////////////////////////////////////////////////////////// +// Little helper function. Calls FindKey() in the resManager using the location (page) of this node + +plKey plMaxNode::FindPageKey( UInt16 classIdx, const char *name ) +{ + return hsgResMgr::ResMgr()->FindKey( plUoid( GetLocation(), classIdx, name ) ); +} + +char *plMaxNode::GetAgeName() +{ + int i; + for (i = 0; i < NumAttachedComponents(); i++) + { + plComponentBase *comp = GetAttachedComponent(i); + if (comp->ClassID() == PAGEINFO_CID) + return ((plPageInfoComponent*)comp)->GetAgeName(); + } + return nil; +} + +// create a list of keys used by the run-time interface for things like +// determining cursor changes, what kind of object this is, etc. +// we're doing this here because multiple logic triggers can be attached to a +// single object and tracking down all their run-time counterpart objects (who might +// need a message sent to them) is a huge pain and very ugly. This will capture anything +// important in a single list. + +hsBool plMaxNode::MakeIfaceReferences(plErrorMsg *pErrMsg, plConvertSettings *settings) +{ + hsBool ret = true; + + char *dbgNodeName = GetName(); + if (!CanConvert()) + return ret; + + UInt32 count = GetSceneObject()->GetNumModifiers(); + hsTArray keys; + // Go through all the modifiers attached to this node's scene object + // and grab keys for objects who we would need to send interface messages to + for (UInt32 i = 0; i < count; i++) + { + const plModifier* pMod = GetSceneObject()->GetModifier(i); + // right now all we care about are these, but I guarentee you we will + // care about more as the interface gets more complex + const plPickingDetector* pDet = plPickingDetector::ConvertNoRef(pMod); + const plLogicModifier* pLog = plLogicModifier::ConvertNoRef(pMod); + if( pDet ) + { + for (int j = 0; j < pDet->GetNumReceivers(); j++) + keys.Append(pDet->GetReceiver(j)); + } + else + if( pLog ) + { + keys.Append(pLog->GetKey()); + } + } + // if there is anything there, create an 'interface object modifier' which simply stores + // the list in a handy form + if (keys.Count()) + { + plInterfaceInfoModifier* pMod = TRACKED_NEW plInterfaceInfoModifier; + + plKey modifierKey = hsgResMgr::ResMgr()->NewKey(GetName(), pMod, GetLocation(), GetLoadMask()); + hsgResMgr::ResMgr()->AddViaNotify(modifierKey, TRACKED_NEW plObjRefMsg(GetSceneObject()->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); + + for(int i = 0; i < keys.Count(); i++) + pMod->AddRefdKey(keys[i]); + } + + return ret; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNode.h new file mode 100644 index 00000000..c8127965 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNode.h @@ -0,0 +1,245 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMaxNode_inc +#define plMaxNode_inc + +#include "plMaxNodeBase.h" + +#include "hsTemplates.h" +#include "hsMatrix44.h" +#include "iparamb2.h" +#include "../pnKeyedObject/plKey.h" +#include + +class plMaxNode; +class plErrorMsg; +class plConvertSettings; +class plExportProgressBar; +class plSceneNode; +class plDrawable; +class plDrawInterface; +class plDrawableSpans; +class plLightInfo; +class plSpotLightInfo; +class plOmniLightInfo; +class plGeometrySpan; +class ISkin; +class plSpotModifier; +class plOmniModifier; +class plLtdDirModifier; +class plLightModifier; +class plController; +class plAGModifier; +class plAGMasterMod; +class plAGAnim; +class plRenderLevel; +class plDrawableCriteria; +class plXImposterComp; +class plPhysicalProps; +class plLightMapComponent; +class plPageInfoComponent; +class plMaxBoneMap; +class plSynchedObject; + +typedef hsBool (plMaxNode:: *PMaxNodeFunc) (plErrorMsg *, plConvertSettings *); // Function pointer to a plMaxNode member funtion + +class plMaxNodeTab : public Tab +{ +}; + +//------------------------------------------- +// plMaxNode +//------------------------------------------- +// CAREFUL! This class is different, it is derived from Max's INode (as you can see) +// But we can only add (NON Virtual) functions to plMaxNode directly +// If you want some new Data members, you can add them by adding to the class +// plMaxNodeData This data is stored in each INode through some mechanisms supplied +// It would be nice of you to add GetFunctions for each new data member you add (see below) +//-------------------------------------------- +// NOTE: an INode can be cast to a plMaxNode, but currently it is the MakeSceneObject Pass which +// Adds the plMaxNodeData to the Node + +class plMaxNode : public plMaxNodeBase +{ +public: + hsBool DoRecur(PMaxNodeFunc p,plErrorMsg *, plConvertSettings *, plExportProgressBar*); + hsBool DoAllRecur(PMaxNodeFunc p,plErrorMsg *, plConvertSettings *, plExportProgressBar*); + + // DoRecur takes one of the following functions + hsBool ConvertValidate (plErrorMsg *, plConvertSettings *); + hsBool SetupPropertiesPass (plErrorMsg *, plConvertSettings *); + hsBool MakeSceneObject (plErrorMsg *, plConvertSettings *); + hsBool PrepareSkin (plErrorMsg *, plConvertSettings *); + hsBool MakePhysical (plErrorMsg *, plConvertSettings *); + hsBool FirstComponentPass (plErrorMsg *, plConvertSettings *); + hsBool MakeController (plErrorMsg *, plConvertSettings *); + hsBool MakeCoordinateInterface (plErrorMsg *, plConvertSettings *); + hsBool MakeModifiers (plErrorMsg *, plConvertSettings *); + hsBool MakeParentOrRoomConnection (plErrorMsg *, plConvertSettings *); + hsBool MakeMesh (plErrorMsg *, plConvertSettings *); + hsBool MakeLight (plErrorMsg *, plConvertSettings *); + hsBool MakeOccluder (plErrorMsg *, plConvertSettings *); + hsBool ConvertComponents (plErrorMsg *, plConvertSettings *); + hsBool ClearData (plErrorMsg *, plConvertSettings *); + hsBool ShadeMesh (plErrorMsg *, plConvertSettings *); + hsBool MakeIfaceReferences (plErrorMsg *, plConvertSettings *); + hsBool ClearMaxNodeData (plErrorMsg *, plConvertSettings *); + hsBool DeInitComponents (plErrorMsg *, plConvertSettings *); + + // Does specified function for all components attached to this node + enum { kSetupProperties, kPreConvert, kConvert }; + hsBool DoComponents(int convertType, plErrorMsg *, plConvertSettings *); + + plKey AddModifier(plModifier *pMod, const char* name); + + hsBool ConvertToOccluder (plErrorMsg* pErrMsg, hsBool twoSided, hsBool isHole); + + plDrawableCriteria GetDrawableCriteria(hsBool needBlending, hsBool needSorting); + Point3 GetFlexibility(); // returns Point3(flexibility, interRand, intraRand). + plXImposterComp* GetXImposterComp(); + + int AlphaHackLayersNeeded(int iSubMtl); + int NumUVWChannels(); + hsBool VtxAlphaNotAvailable(); + hsBool NonVtxPreshaded(); + TriObject* GetTriObject(hsBool& deleteIt); + + plAGModifier* HasAGMod(); + plAGMasterMod* GetAGMasterMod(); + plMaxNode* GetBonesRoot(); // Returns the root of my bones hierarchy, if I have any bones, else nil. + void GetBonesRootsRecur(hsTArray& list); + plSceneObject* MakeCharacterHierarchy(plErrorMsg *pErrMsg); + void SetupBonesAliasesRecur(const char *rootName); + void SetupBoneHierarchyPalette(plMaxBoneMap *bones = nil); + + void SetDISceneNodeSpans( plDrawInterface *di, hsBool needBlending ); + + hsBool IsLegalDecal(hsBool checkParent = true); + + bool IsAnimatedLight(); + + // These are public so the animation component can use them, no one else should need them + void GetRTLightColAnim(IParamBlock2* ProperPB, plAGAnim* anim); + void GetRTConeAnim(IParamBlock2* ProperPB, plAGAnim* anim); + void GetRTLightAttenAnim(IParamBlock2* ProperPB, plAGAnim* anim); + + // This is used in the shading pass, where the lightmap component can + // serve as a cache for some shading info. Returns nil if there is no LightMapComponent on this. + plLightMapComponent* GetLightMapComponent(); + // Starting at 0, returns an incrementing index for each maxNode. Useful for assigning + // indices to sound objects attached to the node + UInt32 GetNextSoundIdx( void ); + + hsBool IsPhysical( void ); + + hsBool CanMakeMesh( Object *obj, plErrorMsg *pErrMsg, plConvertSettings *settings ); + plDrawInterface* GetDrawInterface(); // Returns nil if there isn't a sceneobject and a drawinterface. + + // Only call during convert + plPhysicalProps *GetPhysicalProps(); + + // Little helper function. Calls FindKey() in the resManager using the location (page) of this node + plKey FindPageKey( UInt16 classIdx, const char *name ); + char *GetAgeName(); + + void CheckSynchOptions(plSynchedObject* so); + +protected: + INode *GetRootNode() { return GetInterface()->GetRootNode(); } + + plDrawableSpans *IGetSceneNodeSpans( plSceneNode *node, hsBool needBlending, hsBool needSorting=true ); + + plLightInfo* IMakeDirectional(plErrorMsg* pErrMsg, plConvertSettings* settings); + plLightInfo* IMakeOmni(plErrorMsg* pErrMsg, plConvertSettings* settings); + plLightInfo* IMakeSpot(plErrorMsg* pErrMsg, plConvertSettings* settings); + hsBool IGetProjection(plLightInfo* li, plErrorMsg* pErrMsg); + plLightInfo* IMakeRTDirectional(plErrorMsg* pErrMsg, plConvertSettings* settings); + plLightInfo* IMakeRTOmni(plErrorMsg* pErrMsg, plConvertSettings* settings); + plLightInfo* IMakeRTSpot(plErrorMsg* pErrMsg, plConvertSettings* settings); + plLightInfo* IMakeRTProjDirectional( plErrorMsg *pErrMsg, plConvertSettings *settings ); + + void IGetCone(plSpotLightInfo* liInfo, LightObject* light, LightState& ls); + void IGetLightColors(plLightInfo* liInfo, LightObject* light, LightState& ls); + void IGetLightAttenuation(plOmniLightInfo* liInfo, LightObject* light, LightState& ls); + // RunTime Lights versions + void IGetRTCone(plSpotLightInfo* liInfo, IParamBlock2* ProperPB); + void IGetRTLightColors(plLightInfo* liInfo, IParamBlock2* ProperPB); + void IGetRTLightAttenuation(plOmniLightInfo* liInfo, IParamBlock2* ProperPB); + // RunTime Light animation builders + hsBool IGetRTLightAttenValues(IParamBlock2* ProperPB, hsScalar& attenConst, hsScalar& attenLinear, hsScalar& attenQuadratic,hsScalar &attenCutoff); + void IAdjustRTColorByIntensity(plController* ctl, IParamBlock2* ProperPB); + hsBool IAttachRTLightModifier(plLightModifier* liMod); + + plLightInfo* IMakeLight(plErrorMsg *pErrMsg, plConvertSettings *settings); + + plSceneNode* IGetDrawableSceneNode(plErrorMsg *pErrMsg); + void IAssignSpansToDrawables( hsTArray &spanArray, plDrawInterface *di, + plErrorMsg *pErrMsg, plConvertSettings *settings ); + void IAssignSpan( plDrawableSpans *drawable, hsTArray &spanArray, UInt32 &index, + hsMatrix44 &l2w, hsMatrix44 &w2l, + plErrorMsg *pErrMsg, plConvertSettings *settings ); + void ISetupBones( plDrawableSpans *drawable, hsTArray &spanArray, + hsMatrix44 &l2w, hsMatrix44 &w2l, + plErrorMsg *pErrMsg, plConvertSettings *settings ); + hsBool IFindBones(plErrorMsg *pErrMsg, plConvertSettings *settings); + + void IWipeBranchDrawable(hsBool b); + + UInt32 IBuildInstanceList( Object *obj, TimeValue t, hsTArray &nodes, hsBool beMoreAccurate = false ); + hsBool IMakeInstanceSpans( plMaxNode *node, hsTArray &spanArray, + plErrorMsg *pErrMsg, plConvertSettings *settings ); + hsBool IMaterialsMatch( plMaxNode *otherNode, hsBool beMoreAccurate ); + + int IGetCachedAlphaHackValue( int iSubMtl ); + void ISetCachedAlphaHackValue( int iSubMtl, int value ); + +friend class plLocationDlg; +}; + +class plMaxBoneMap +{ +protected: + typedef std::map BoneMap; + BoneMap fBones; + typedef std::map DrawableMap; + DrawableMap fBaseMatrices; + +public: + UInt8 fNumBones; + plMaxNodeBase *fOwner; // Make note of which node created us, so they can delete us. + + plMaxBoneMap() : fNumBones(0), fOwner(nil) {} + + void AddBone(plMaxNodeBase *bone); + UInt8 GetIndex(plMaxNodeBase *bone); + void FillBoneArray(plMaxNodeBase **boneArray); + UInt32 GetBaseMatrixIndex(plDrawable *draw); + void SetBaseMatrixIndex(plDrawable *draw, UInt32 idx); + void SortBones(); +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeBase.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeBase.cpp new file mode 100644 index 00000000..5eaeaf88 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeBase.cpp @@ -0,0 +1,1185 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plMaxNodeBase.h" +#include "plMaxNodeData.h" + +// Max includes +#include "iparamm2.h" +#include "modstack.h" +#include "ISkin.h" +#include "dummy.h" + +//To support the new Plasma Light Objs, the classes are included below +#include "../MaxPlasmaLights/plRealTimeLightBase.h" + + +#include +#include + +#include "GlobalUtility.h" // Only needed for PLASMA_MAX_CLASSID, fix? + +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/plKey.h" + +#include "../pnModifier/plModifier.h" + +#include "../MaxPlasmaMtls/Materials/plDecalMtl.h" + +#include "../MaxComponent/plComponentBase.h" + +CoreExport void *__cdecl MAX_new(size_t size); +CoreExport void __cdecl MAX_delete(void* mem); + +void plMaxNodeBase::SetMaxNodeData(plMaxNodeData * pdat) +{ + const char* dbgNodeName = GetName(); + + // If object is a component, don't add node data + if (IsComponent()) + return; + + AppDataChunk *adc = GetAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaMaxNodeDataChunk); + + // If pointer is nil, remove any data + if (!pdat) + { + if( adc ) + { + plMaxNodeData *pDataChunk = (plMaxNodeData*)adc->data; + pDataChunk->DeInit(); + } + RemoveAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaMaxNodeDataChunk); + return; + } + + if (!adc) + { + // Does not exist, create a new one... + int len = sizeof(plMaxNodeData); + plMaxNodeData *pDataChunk = (plMaxNodeData *)MAX_new(len); + memcpy(pDataChunk, pdat, sizeof(*pdat)); + pDataChunk->Init(); + *pDataChunk = *pdat; + AddAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaMaxNodeDataChunk, len, pDataChunk); + } + else + { + plMaxNodeData *pDataChunk = (plMaxNodeData*)adc->data; + // if someone does a GetMaxNodeData, they get a pointer to the data, + // No need to set it, other wise set the Data chunk from the MaxNodeData passed in + if (pDataChunk != pdat) + *pDataChunk = *pdat; + } +} + +plMaxNodeData *plMaxNodeBase::GetMaxNodeData() +{ + AppDataChunk *adc = GetAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaMaxNodeDataChunk); + if (adc) + return (plMaxNodeData*)adc->data; + + return nil; +} + +#define GetMD plMaxNodeData *pMD = GetMaxNodeData(); //hsAssert(pMD,"Missing MaxNodeData"); // Get MaxNode Data + +plKey plMaxNodeBase::GetKey() { GetMD; return (pMD) ? pMD->GetKey() : nil; } +plSceneObject* plMaxNodeBase::GetSceneObject() { GetMD; return (pMD) ? pMD->GetSceneObject() : nil;} +hsBool plMaxNodeBase::GetForceLocal() { GetMD; return (pMD) ? pMD->GetForceLocal() : nil; } +hsBool plMaxNodeBase::GetReverseSort() { GetMD; return (pMD) ? pMD->GetReverseSort() : nil;} +hsBool plMaxNodeBase::GetSortAsOpaque() { GetMD; return (pMD) ? pMD->GetSortAsOpaque() : nil;} +hsBool plMaxNodeBase::GetVS() { GetMD; return (pMD) ? pMD->GetVS() : nil;} +hsBool plMaxNodeBase::GetHasWaterHeight() { GetMD; return (pMD) ? pMD->GetHasWaterHeight() : nil; } +hsScalar plMaxNodeBase::GetWaterHeight() { GetMD; return (pMD) ? pMD->GetWaterHeight() : nil; } +hsBool plMaxNodeBase::GetSmoothAll() { GetMD; return (pMD) ? pMD->GetSmoothAll() : nil;} +hsBool plMaxNodeBase::GetForceSortable() { GetMD; return (pMD) ? pMD->GetForceSortable() : nil;} +hsBool plMaxNodeBase::GetConcave() { GetMD; return (pMD) ? pMD->GetConcave() : nil;} +hsBool plMaxNodeBase::GetCalcEdgeLens() { GetMD; return (pMD) ? pMD->GetCalcEdgeLens() : nil;} +hsBool plMaxNodeBase::GetRunTimeLight() { GetMD; return (pMD) ? pMD->GetRunTimeLight() : nil;} +hsBool plMaxNodeBase::GetForceMatShade() { GetMD; return (pMD) ? pMD->GetForceMatShade() : nil;} +hsBool plMaxNodeBase::GetForceVisLOS() { GetMD; return (pMD) ? pMD->GetForceVisLOS() : nil;} +hsBool plMaxNodeBase::GetEnviron() { GetMD; return (pMD) ? pMD->GetEnviron() : nil;} +hsBool plMaxNodeBase::GetEnvironOnly() { GetMD; return (pMD) ? pMD->GetEnvironOnly() : nil;} +hsBool plMaxNodeBase::GetWaterDecEnv() { GetMD; return (pMD) ? pMD->GetWaterDecEnv() : nil; } +hsBool plMaxNodeBase::GetNoPreShade() { GetMD; return (pMD) ? pMD->GetNoPreShade() && !pMD->GetForcePreShade() : nil;} +hsBool plMaxNodeBase::GetForcePreShade() { GetMD; return (pMD) ? pMD->GetForcePreShade() : nil;} +plKey plMaxNodeBase::GetRoomKey() { GetMD; return (pMD) ? pMD->GetRoomKey() : nil; } +hsBool plMaxNodeBase::GetDrawable() { GetMD; return (pMD) ? pMD->GetDrawable() : nil; } +hsBool plMaxNodeBase::GetPhysical() { GetMD; return (pMD) ? pMD->GetPhysical() : nil; } +hsBool plMaxNodeBase::GetItinerant() { GetMD; return (pMD) ? pMD->GetItinerant() : nil; } +hsBool plMaxNodeBase::GetUnBounded() { GetMD; return (pMD) ? pMD->GetUnBounded() : nil; } +hsBool plMaxNodeBase::GetDisableNormal() { GetMD; return (pMD) ? pMD->GetDisableNormal() : nil; } +UInt32 plMaxNodeBase::GetDecalLevel() { GetMD; return (pMD) ? pMD->GetDecalLevel() : nil; } +hsBool plMaxNodeBase::GetMovable() { GetMD; return (pMD) ? pMD->GetMovable() : nil; } +hsBool plMaxNodeBase::GetIsBarney() { GetMD; return (pMD) ? pMD->GetIsBarney() : nil; } +hsBool plMaxNodeBase::GetForceShadow() { GetMD; return (pMD) ? pMD->GetForceShadow() : nil; } +hsBool plMaxNodeBase::GetAlphaTestHigh() { GetMD; return (pMD) ? pMD->GetAlphaTestHigh() : nil; } +hsBool plMaxNodeBase::GetFilterInherit() { GetMD; return (pMD) ? pMD->GetFilterInherit() : nil; } +hsBool plMaxNodeBase::GetNoShadow() { GetMD; return (pMD) ? pMD->GetNoShadow() : nil; } +hsBool plMaxNodeBase::GetNoSpanSort() { GetMD; return (pMD) ? pMD->GetNoSpanSort() : nil; } +hsBool plMaxNodeBase::GetNoSpanReSort() { GetMD; return (pMD) ? pMD->GetNoSpanReSort() : nil; } +hsBool plMaxNodeBase::GetNoFaceSort() { GetMD; return (pMD) ? pMD->GetNoFaceSort() : nil; } +hsBool plMaxNodeBase::GetNoDeferDraw() { GetMD; return (pMD) ? pMD->GetNoDeferDraw() : nil; } +hsBool plMaxNodeBase::GetBlendToFB() { GetMD; return (pMD) ? pMD->GetBlendToFB() : nil; } +hsBool plMaxNodeBase::GetForceMaterialCopy() { GetMD; return (pMD) ? pMD->GetForceMaterialCopy() : nil; } +hsBool plMaxNodeBase::GetInstanced() { GetMD; return (pMD) ? pMD->GetInstanced() : nil; } +hsBool plMaxNodeBase::GetParticleRelated() { GetMD; return (pMD) ? pMD->GetParticleRelated() : nil; } +UInt32 plMaxNodeBase::GetSoundIdxCounter() { GetMD; return (pMD) ? pMD->GetSoundIdxCounter() : 0; } +plSceneObject* plMaxNodeBase::GetAvatarSO() { GetMD; return (pMD) ? pMD->GetAvatarSO() : nil; } +BOOL plMaxNodeBase::HasFade() { GetMD; return (pMD) ? pMD->HasFade() : false; } +Box3 plMaxNodeBase::GetFade() { GetMD; return (pMD) ? pMD->GetFade() : Box3(Point3(0,0,0), Point3(0,0,0)); } +hsBool plMaxNodeBase::GetDup2Sided() { GetMD; return (pMD) ? pMD->GetDup2Sided() : false;} +hsBool plMaxNodeBase::GetRadiateNorms() { GetMD; return (pMD) ? pMD->GetRadiateNorms() : false;} +BOOL plMaxNodeBase::HasNormalChan() { GetMD; return (pMD) ? pMD->HasNormalChan() : false; } +int plMaxNodeBase::GetNormalChan() { GetMD; return (pMD) ? pMD->GetNormalChan() : 0; } +hsBool plMaxNodeBase::GetIsGUI() { GetMD; return (pMD) ? pMD->GetIsGUI() : false; } +plSharedMesh* plMaxNodeBase::GetSwappableGeom() { GetMD; return (pMD) ? pMD->GetSwappableGeom() : nil; } +UInt32 plMaxNodeBase::GetSwappableGeomTarget() { GetMD; return (pMD) ? pMD->GetSwappableGeomTarget() : -1; } +plMaxBoneMap* plMaxNodeBase::GetBoneMap() { GetMD; return (pMD) ? pMD->GetBoneMap() : nil; } +hsBool plMaxNodeBase::GetOverrideHighLevelSDL() { GetMD; return (pMD) ? pMD->GetOverrideHighLevelSDL() : false; } +UInt8 plMaxNodeBase::GetAnimCompress() { GetMD; return (pMD) ? pMD->GetAnimCompress() : false; } +hsScalar plMaxNodeBase::GetKeyReduceThreshold() { GetMD; return (pMD) ? pMD->GetKeyReduceThreshold() : 0; } +int plMaxNodeBase::NumRenderDependencies() { GetMD; return (pMD) ? pMD->NumRenderDependencies() : 0; } +plMaxNodeBase* plMaxNodeBase::GetRenderDependency(int i) { GetMD; return (pMD) ? pMD->GetRenderDependency(i) : nil; } + +int plMaxNodeBase::NumBones() { GetMD; return (pMD) ? pMD->NumBones() : 0; } +plMaxNodeBase* plMaxNodeBase::GetBone(int i) { GetMD; return (pMD) ? pMD->GetBone(i) : nil; } + + +//------------------------------ +// Set Data from MaxNodeData +//------------------------------ +void plMaxNodeBase::SetCanConvert(hsBool b) { GetMD; pMD->SetCanConvert(b); } +void plMaxNodeBase::SetMesh(hsGMesh *p) { GetMD; pMD->SetMesh(p); } +void plMaxNodeBase::SetRoomKey(plKey p) { GetMD; pMD->SetRoomKey(p); } +void plMaxNodeBase::SetDrawable(hsBool b) { GetMD; pMD->SetDrawable(b); } +void plMaxNodeBase::SetPhysical(hsBool b) { GetMD; pMD->SetPhysical(b); } +//void plMaxNodeBase::SetItinerant(hsBool b); +void plMaxNodeBase::SetUnBounded(hsBool b) { GetMD; pMD->SetUnBounded(b); } +void plMaxNodeBase::SetDisableNormal(hsBool b) { GetMD; pMD->SetDisableNormal(b); } +void plMaxNodeBase::SetDecalLevel(UInt32 i) { GetMD; pMD->SetDecalLevel(i); } +void plMaxNodeBase::SetMovable(hsBool b) { GetMD; pMD->SetMovable(b); pMD->SetRunTimeLight(b); pMD->SetNoPreShade(b); } +void plMaxNodeBase::SetReverseSort(hsBool b) { GetMD; pMD->SetReverseSort(b); } +void plMaxNodeBase::SetSortAsOpaque(hsBool b) { GetMD; pMD->SetSortAsOpaque(b); } +void plMaxNodeBase::SetVS(hsBool b) { GetMD; pMD->SetVS(b); } +void plMaxNodeBase::SetHasWaterHeight(hsBool b) { GetMD; pMD->SetHasWaterHeight(b); } +void plMaxNodeBase::SetWaterHeight(hsScalar h) { GetMD; pMD->SetWaterHeight(h); } +void plMaxNodeBase::SetSmoothAll(hsBool b) { GetMD; pMD->SetSmoothAll(b); } +void plMaxNodeBase::SetForceSortable(hsBool b) { GetMD; pMD->SetForceSortable(b); } +void plMaxNodeBase::SetConcave(hsBool b) { GetMD; pMD->SetConcave(b); } +void plMaxNodeBase::SetCalcEdgeLens(hsBool b) { GetMD; pMD->SetCalcEdgeLens(b); } +void plMaxNodeBase::SetRunTimeLight(hsBool b) { GetMD; pMD->SetRunTimeLight(b); } +void plMaxNodeBase::SetForceMatShade(hsBool b) { GetMD; pMD->SetForceMatShade(b); } +void plMaxNodeBase::SetForceVisLOS(hsBool b) { GetMD; pMD->SetForceVisLOS(b); } +void plMaxNodeBase::SetEnviron(hsBool b) { GetMD; pMD->SetEnviron(b); } +void plMaxNodeBase::SetEnvironOnly(hsBool b) { GetMD; pMD->SetEnvironOnly(b); } +void plMaxNodeBase::SetWaterDecEnv(hsBool b) { GetMD; pMD->SetWaterDecEnv(b); } +void plMaxNodeBase::SetNoPreShade(hsBool b) { GetMD; pMD->SetNoPreShade(b); } +void plMaxNodeBase::SetForcePreShade(hsBool b) { GetMD; pMD->SetForcePreShade(b); } +void plMaxNodeBase::SetForceLocal(hsBool b) { GetMD; pMD->SetForceLocal(b); } +void plMaxNodeBase::SetIsBarney(hsBool b) { GetMD; pMD->SetIsBarney(b); } +void plMaxNodeBase::SetForceShadow(hsBool b) { GetMD; pMD->SetForceShadow(b); } +void plMaxNodeBase::SetAlphaTestHigh(hsBool b) { GetMD; pMD->SetAlphaTestHigh(b); } +void plMaxNodeBase::SetFilterInherit(hsBool b) { GetMD; pMD->SetFilterInherit(b); } +void plMaxNodeBase::SetNoShadow(hsBool b) { GetMD; pMD->SetNoShadow(b); } +void plMaxNodeBase::SetNoSpanSort(hsBool b) { GetMD; pMD->SetNoSpanSort(b); } +void plMaxNodeBase::SetNoSpanReSort(hsBool b) { GetMD; pMD->SetNoSpanReSort(b); } +void plMaxNodeBase::SetNoFaceSort(hsBool b) { GetMD; pMD->SetNoFaceSort(b); } +void plMaxNodeBase::SetNoDeferDraw(hsBool b) { GetMD; pMD->SetNoDeferDraw(b); } +void plMaxNodeBase::SetBlendToFB(hsBool b) { GetMD; pMD->SetBlendToFB(b); } +void plMaxNodeBase::SetForceMaterialCopy(hsBool b) { GetMD; pMD->SetForceMaterialCopy(b); } +void plMaxNodeBase::SetInstanced(hsBool b) { GetMD; pMD->SetInstanced(b); } +void plMaxNodeBase::SetParticleRelated(hsBool b) { GetMD; pMD->SetParticleRelated(b); } +void plMaxNodeBase::SetSoundIdxCounter(UInt32 ctr) { GetMD; pMD->SetSoundIdxCounter(ctr); } +void plMaxNodeBase::SetAvatarSO(plSceneObject *so) { GetMD; pMD->SetAvatarSO(so); } +void plMaxNodeBase::SetFade(const Box3& b) { GetMD; pMD->SetFade(b); } +void plMaxNodeBase::SetDup2Sided(hsBool b) { GetMD; pMD->SetDup2Sided(b); } +void plMaxNodeBase::SetRadiateNorms(hsBool b) { GetMD; pMD->SetRadiateNorms(b); } +void plMaxNodeBase::SetNormalChan(int n) { GetMD; pMD->SetNormalChan(n); } +void plMaxNodeBase::SetIsGUI(hsBool b) { GetMD; pMD->SetIsGUI(b); } +void plMaxNodeBase::SetSwappableGeom(plSharedMesh *sm) { GetMD; pMD->SetSwappableGeom(sm); } +void plMaxNodeBase::SetSwappableGeomTarget(UInt32 id) { GetMD; pMD->SetSwappableGeomTarget(id); } +void plMaxNodeBase::SetBoneMap(plMaxBoneMap *bones) { GetMD; pMD->SetBoneMap(bones); } +void plMaxNodeBase::SetOverrideHighLevelSDL(hsBool b) { GetMD; pMD->SetOverrideHighLevelSDL(b); } +void plMaxNodeBase::SetAnimCompress(UInt8 v) { GetMD; pMD->SetAnimCompress(v); } +void plMaxNodeBase::SetKeyReduceThreshold(hsScalar v) { GetMD; pMD->SetKeyReduceThreshold(v); } +void plMaxNodeBase::ClearRenderDependencies() { GetMD; pMD->ClearRenderDependencies(); } + +void plMaxNodeBase::AddBone(plMaxNodeBase* m) { GetMD; if(pMD) pMD->AddBone(m); } +void plMaxNodeBase::ClearBones() { GetMD; if(pMD) pMD->ClearBones(); } + +plLocation plMaxNodeBase::GetLocation() +{ + plKey rmKey= GetRoomKey(); + plLocation loc; + loc.Invalidate(); + if (rmKey) + loc = rmKey->GetUoid().GetLocation(); + return loc; +} + +hsBool plMaxNodeBase::GetDirty(UInt8 i) +{ + UInt8 *dirty = IGetSceneViewerChunk(); + return *dirty & i; +} + +void plMaxNodeBase::SetDirty(UInt8 i, hsBool b) +{ + UInt8 *dirty = IGetSceneViewerChunk(); + + if (b) + *dirty |= i; + else + *dirty &= ~i; +} + +hsBool plMaxNodeBase::HasLoadMask() +{ + GetMD; + return pMD->HasLoadMask(); +} + +plLoadMask plMaxNodeBase::GetLoadMask() +{ + GetMD; + return pMD->GetLoadMask(); +} + +void plMaxNodeBase::AddLoadMask(const plLoadMask& m) +{ + GetMD; + pMD->AddLoadMask(m); +} + +hsBool plMaxNodeBase::RenderDependsOn(plMaxNodeBase* m) +{ + if( m == this ) + return true; + + int i; + for( i = 0; i < NumRenderDependencies(); i++ ) + { + if( GetRenderDependency(i)->RenderDependsOn(m) ) + return true; + } + return false; +} + +hsBool plMaxNodeBase::AddRenderDependency(plMaxNodeBase* m) +{ + if( m->RenderDependsOn(this) ) + return false; + GetMD; + pMD->AddRenderDependency(m); + return true; +} + +UInt8 *plMaxNodeBase::IGetSceneViewerChunk() +{ + UInt8 *SVChunk = nil; + + AppDataChunk *adc = GetAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaSceneViewerChunk); + if (adc) + SVChunk = (UInt8*)adc->data; + else + { + // Does not exist, create a new one... + SVChunk = (UInt8*)MAX_new(1); + *SVChunk = 0; + AddAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaSceneViewerChunk, 1, SVChunk); + } + + hsAssert(SVChunk, "SceneViewer chunk not found or created"); + return SVChunk; +} + +hsBool plMaxNodeBase::CanConvert(bool recalculate) +{ + // Try and find a cached return value + plMaxNodeData *md = GetMaxNodeData(); + if (md && !recalculate) + return md->CanConvert(); + + if (UserPropExists("IGNORE")) + return false; + + Object *obj = EvalWorldState(0/*hsConverterUtils::Instance().GetTime(GetInterface())*/).obj; + if (obj) + { + if ( obj->CanConvertToType(triObjectClassID) // MeshObjs are accepted here + || obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) // Dummy boxes are accepted here + || obj->SuperClassID() == CAMERA_CLASS_ID // All Camera types are accepted here + || obj->ClassID() == Class_ID(UTILITY_CLASS_ID, 0) // All Camera targets are accepted here + || ( obj->ClassID() == RTOMNI_LIGHT_CLASSID + || obj->ClassID() == RTSPOT_LIGHT_CLASSID + || obj->ClassID() == RTDIR_LIGHT_CLASSID + || obj->ClassID() == RTPDIR_LIGHT_CLASSID ) + || ( obj->SuperClassID() == LIGHT_CLASS_ID // All run time lights are accepted here + && UserPropExists("RunTimeLight")) + + || IsGroupMember() // Group objects are accepted here + ) + return true; + } + return false; +} + +hsBool plMaxNodeBase::IsTMAnimated() +{ + Control* tmControl = GetTMController(); + return (tmControl && tmControl->IsAnimated()); +} + +//// IsTMAnimatedRecur - test recursively up the chain /////////////////////////////////////////////////////////////// +hsBool plMaxNodeBase::IsTMAnimatedRecur() +{ + const char* dbgNodeName = GetName(); + hsBool shouldBe = false; + + if( !CanConvert() ) + return false; + if( IsTMAnimated() ) + return true; + + return ((plMaxNodeBase*)GetParentNode())->IsTMAnimatedRecur(); +} + +//// IsMovable /////////////////////////////////////////////////////////////// +// Returns whether this node is "animated" (i.e. could move at runtime) +hsBool plMaxNodeBase::IsMovable() +{ + const char* dbgNodeName = GetName(); + hsBool shouldBe = false; + + + if( !CanConvert() ) + return false; + + if( GetMovable() ) + return true; + + if( GetItinerant() ) + shouldBe = true; + else if( FindSkinModifier() ) + shouldBe = true; + + // Moved this to plAnimComponent (so GetMovable() will reveal it) + /* + else if( IsTMAnimated() ) + shouldBe = true; + */ + if( shouldBe ) + { + SetMovable( true ); + return true; + } + + return ((plMaxNodeBase*)GetParentNode())->IsMovable(); +} + +// Recursively set so we don't have to recursively check. +void plMaxNodeBase::SetItinerant(hsBool b) +{ + const char* dbgNodeName = GetName(); + + if( !CanConvert() ) + return; + + GetMD; + pMD->SetItinerant(b); + + int i; + for( i = 0; i < NumChildren(); i++ ) + { + ((plMaxNodeBase*)GetChildNode(i))->SetItinerant(b); + } +} + +//// FindSkinModifier /////////////////////////////////////////////////////// +// Given an INode, gets the ISkin object of that node, or nil if there is +// none. Taken from the Max4 SDK, ISkin.h +ISkin* plMaxNodeBase::FindSkinModifier() +{ + int modStackIndex; + + // Get object from node. Abort if no object. + Object *pObj = GetObjectRef(); + if( pObj == nil ) + return nil; + + // Is derived object ? + while( pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID ) + { + IDerivedObject *pDerObj = (IDerivedObject *)pObj; + + // Iterate over all entries of the modifier stack. + for( modStackIndex = 0; modStackIndex < pDerObj->NumModifiers(); modStackIndex++ ) + { + // Get current modifier. + Modifier *mod = pDerObj->GetModifier( modStackIndex ); + + // Is this Skin ? + if( mod->ClassID() == SKIN_CLASSID ) + { + ISkin* skin = (ISkin*)mod->GetInterface(I_SKIN); + if( skin->GetNumBones() > 0 ) + return skin; + } + } + pObj = pDerObj->GetObjRef(); + } + + // Not found. + return nil; +} + +bool plMaxNodeBase::IsXRef() +{ + // Is this an XRef'd object? + Object *obj = GetObjectRef(); + if (obj->SuperClassID() == SYSTEM_CLASS_ID && obj->ClassID() == Class_ID(XREFOBJ_CLASS_ID,0)) + return true; + + // + // Is this part of an XRef'd scene? + // + // Walk up to our root node + INode *root = GetParentNode(); + while (!root->IsRootNode()) + root = root->GetParentNode(); + // If our root isn't the main root, we're in an XRef'd scene. + if (root != GetCOREInterface()->GetRootNode()) + return true; + + return false; +} + +hsBool plMaxNodeBase::IsComponent(Object *obj) +{ + if (!obj) + obj = GetObjectRef(); + + if (obj && obj->CanConvertToType(COMPONENT_CLASSID)) + return true; + + return false; +} + +hsBool plMaxNodeBase::IsExternComponent(Object *obj) +{ + if (!obj) + obj = GetObjectRef(); + + if (obj && obj->CanConvertToType(EXT_COMPONENT_CLASSID)) + return true; + + return false; +} + +plComponentBase *plMaxNodeBase::ConvertToComponent() +{ + if (IsComponent()) + return (plComponentBase*)GetObjectRef(); + + return nil; +} + +// There isn't an easy way to determine if there are any components attached to a node. +// This method goes through the reflist of a node and backtracks up each ref (using +// IRefMakerToComponent) to determine if it is a component. There are cases though +// where a component can show up as two or more refs to a node, when it has a legitimate +// target ref and some other ref like a proxy object. To catch this case we maintain a +// list of the components found so far and ensure there aren't any duplicates. Maybe it +// would be easier to just go through every component in the scene and check their target +// lists... -Colin +UInt32 plMaxNodeBase::NumAttachedComponents(bool all) +{ + UInt32 numComponents = 0; + std::vector comps; + + // Go through this item's reflist, looking for components + RefList &refList = GetRefList(); + RefListItem *item = refList.FirstItem(); + while (item) + { + plComponentBase *comp = IRefMakerToComponent(item->maker, all); + if (comp && std::find(comps.begin(), comps.end(), comp) == comps.end()) + { + comps.push_back(comp); + numComponents++; + } + + item = item->next; + } + + return numComponents; +} + +plComponentBase *plMaxNodeBase::GetAttachedComponent(UInt32 i, bool all) +{ + UInt32 numComponents = 0; + std::vector comps; + + // Go through this item's reflist, looking for components + RefList &refList = GetRefList(); + RefListItem *item = refList.FirstItem(); + while (item) + { + plComponentBase *comp = IRefMakerToComponent(item->maker, all); + if (comp && std::find(comps.begin(), comps.end(), comp) == comps.end()) + { + if (numComponents == i) + return comp; + + comps.push_back(comp); + numComponents++; + } + + item = item->next; + } + + return nil; +} + +plComponentBase *plMaxNodeBase::IRefMakerToComponent(ReferenceMaker *maker, bool all) +{ + if (!maker) + return nil; + + // Is the refmaker a paramblock? If so, it may be the + // targets block of a component + if (maker->SuperClassID() == PARAMETER_BLOCK2_CLASS_ID) + { + IParamBlock2 *pb = (IParamBlock2*)maker; + ReferenceMaker *pbowner = pb->GetOwner(); + + // Is the owner of the paramblock a helper object (component superclass)? + if (pbowner && pbowner->SuperClassID() == HELPER_CLASS_ID) + { + Object *obj = (Object*)pbowner; + // Is the owner of the paramblock a component? + if (IsComponent(obj)) + { + plComponentBase *comp = (plComponentBase*)obj; + + if (!all) + { + // Does this component actually ref us? (A component can have other + // refs to a node, like a proxy object, so we want to make sure this + // node is actually in the target list.) + for (UInt32 i = 0; i < comp->NumTargets(); i++) + { + if (comp->GetTarget(i) == this) + return comp; + } + } + else + return comp; + } + } + } + + return nil; +} + +hsBool plMaxNodeBase::IRenderLevelSet(hsBool forBlend) +{ + plMaxNodeData* md = GetMaxNodeData(); + if( md ) + return forBlend ? md->BlendLevelSet() : md->OpaqueLevelSet(); + return false; +} + +void plMaxNodeBase::ISetRenderLevel(const plRenderLevel& l, hsBool forBlend) +{ + plMaxNodeData* md = GetMaxNodeData(); + if( md ) + { + if( forBlend ) + md->SetBlendLevel(l); + else + md->SetOpaqueLevel(l); + } +} + +const plRenderLevel& plMaxNodeBase::IGetRenderLevel(hsBool forBlend) +{ + plMaxNodeData* md = GetMaxNodeData(); + if( !md ) + { + static plRenderLevel defRenderLevel; + return defRenderLevel; + } + return forBlend ? md->GetBlendLevel() : md->GetOpaqueLevel(); +} + +UInt32 plMaxNodeBase::IGetMajorRenderLevel(hsBool forBlend) +{ + if( GetBlendToFB() ) + return plRenderLevel::kFBMajorLevel; + + if( GetNoDeferDraw() || GetAvatarSO() ) + forBlend = false; + + int numDep = NumRenderDependencies(); + if( !numDep ) + return forBlend ? plRenderLevel::kBlendRendMajorLevel : plRenderLevel::kDefRendMajorLevel; + + int iMaxDep = 0; + const char* dbgNodeName = GetName(); + + int i; + for( i = 0; i < numDep; i++ ) + { + plMaxNodeBase* dep = GetRenderDependency(i); + if( dep ) + { + const char* depNodeName = dep->GetName(); + int iDep = GetRenderDependency(i)->GetRenderLevel(forBlend).Major(); + if( iDep > iMaxDep ) + iMaxDep = iDep; + } + } + + return iMaxDep; +} + +UInt32 plMaxNodeBase::IGetMinorRenderLevel(hsBool forBlend) +{ + if( GetAvatarSO() ) + return plRenderLevel::kAvatarRendMinorLevel; + + int numDep = NumRenderDependencies(); + if( !numDep ) + return plRenderLevel::kDefRendMinorLevel; + + int iMaxDep = 0; + + const char* dbgNodeName = GetName(); + + int i; + for( i = 0; i < numDep; i++ ) + { + plMaxNodeBase* dep = GetRenderDependency(i); + if( dep ) + { + const char* depNodeName = dep->GetName(); + int iDep = GetRenderDependency(i)->GetRenderLevel(forBlend).Minor(); + if( iDep > iMaxDep ) + iMaxDep = iDep; + } + } + + return iMaxDep + 4; +} + +plRenderLevel plMaxNodeBase::ICalcRenderLevel(hsBool forBlend) +{ + if( GetBlendToFB() ) + return plRenderLevel::kFBMajorLevel; + + if( GetAvatarSO() ) + return plRenderLevel(plRenderLevel::kOpaqueMajorLevel, plRenderLevel::kAvatarRendMinorLevel); + + if( GetNoDeferDraw() ) + forBlend = false; + + int numDep = NumRenderDependencies(); + if( !numDep ) + return forBlend + ? plRenderLevel(plRenderLevel::kBlendRendMajorLevel, plRenderLevel::kDefRendMinorLevel) + : plRenderLevel(plRenderLevel::kDefRendMajorLevel, plRenderLevel::kDefRendMinorLevel); + + plRenderLevel maxLevel(plRenderLevel::kFBMajorLevel, plRenderLevel::kDefRendMinorLevel); + + const char* dbgNodeName = GetName(); + + int i; + for( i = 0; i < numDep; i++ ) + { + plMaxNodeBase* dep = GetRenderDependency(i); + if( dep ) + { + const char* depNodeName = dep->GetName(); + + plRenderLevel depLev = GetRenderDependency(i)->GetRenderLevel(forBlend); + if( depLev > maxLevel ) + maxLevel = depLev; + } + } + maxLevel.Set(maxLevel.Level() + 4); + + return maxLevel; +} + +const plRenderLevel& plMaxNodeBase::GetRenderLevel(hsBool forBlend) +{ + if( !CanConvert() ) + { + static plRenderLevel retVal; + return retVal; + } + + if( !IRenderLevelSet(forBlend) ) + { +#if 0 + + UInt32 major = IGetMajorRenderLevel(forBlend); + UInt32 minor = IGetMinorRenderLevel(forBlend); + + ISetRenderLevel(plRenderLevel(major, minor), forBlend); +#else + ISetRenderLevel(ICalcRenderLevel(forBlend), forBlend); +#endif + } + + return IGetRenderLevel(forBlend); +} + +BOOL plMaxNodeBase::GetGeoDice(int& maxFaces, float& maxSize, int& minFaces) +{ + plMaxNodeData* md = GetMaxNodeData(); + if( md && md->GetGeoDice() ) + { + maxFaces = md->GetGeoDiceMaxFaces(); + maxSize = md->GetGeoDiceMaxSize(); + minFaces = md->GetGeoDiceMinFaces(); + return true; + } + maxFaces = 0; + maxSize = 0; + minFaces = 0; + return false; +} + +void plMaxNodeBase::SetGeoDice(BOOL on, int maxFaces, float maxSize, int minFaces) +{ + plMaxNodeData* md = GetMaxNodeData(); + if( md ) + { + md->SetGeoDice(on); + md->SetGeoDiceMaxFaces(maxFaces); + md->SetGeoDiceMaxSize(maxSize); + md->SetGeoDiceMinFaces(minFaces); + } +} + +hsBool plMaxNodeBase::Contains(const Point3& worldPt) +{ + TimeValue currTime = 0;//hsConverterUtils::Instance().GetTime(GetInterface()); + Object *obj = EvalWorldState(currTime).obj; + if( !obj ) + return false; + + Matrix3 l2w = GetObjectTM(currTime); + Matrix3 w2l = Inverse(l2w); + Point3 pt = w2l * worldPt; + + if( obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) ) + { + DummyObject* dummy = (DummyObject*)obj; + Box3 bnd = dummy->GetBox(); + return bnd.Contains(pt); + } + if( obj->CanConvertToType(triObjectClassID) ) + { + TriObject *meshObj = (TriObject *)obj->ConvertToType(currTime, triObjectClassID); + if( !meshObj ) + return false; + + Mesh& mesh = meshObj->mesh; + Box3 bnd = mesh.getBoundingBox(); + if( !bnd.Contains(pt) ) + { + if( meshObj != obj ) + meshObj->DeleteThis(); + return false; + } + + hsBool retVal = true; + int i; + for( i = 0; i < mesh.getNumFaces(); i++ ) + { + Face& face = mesh.faces[i]; + + Point3 p0 = mesh.verts[face.v[0]]; + Point3 p1 = mesh.verts[face.v[1]]; + Point3 p2 = mesh.verts[face.v[2]]; + + Point3 n = CrossProd(p1 - p0, p2 - p0); + + if( DotProd(pt, n) > DotProd(p0, n) ) + { + retVal = false; + break; + } + } + + if( meshObj != obj ) + meshObj->DeleteThis(); + + return retVal; + } + + // If we can't figure out what it is, the point isn't inside it. + return false; +} + +hsBool plMaxNodeBase::Contains(const Box3& bnd, const Matrix3& l2w) +{ + int i; + for( i = 0; i < 8; i++ ) + { + if( !Contains(l2w * bnd[i]) ) + return false; + } + return true; +} + +hsScalar plMaxNodeBase::BoxVolume(const Box3& bnd, const Matrix3& l2w) +{ + Point3 corner = l2w * bnd[0]; // min, min, min + float len[3]; + + len[0] = Length((l2w * bnd[1]) - corner); // max, min, min + len[1] = Length((l2w * bnd[2]) - corner); // min, max, min + len[2] = Length((l2w * bnd[4]) - corner); // min, min, max + + return len[0] * len[1] * len[2]; +} + +hsScalar plMaxNodeBase::RegionPriority() +{ + TimeValue currTime = 0;//hsConverterUtils::Instance().GetTime(GetInterface()); + Object *obj = EvalWorldState(currTime).obj; + if( !obj ) + return 0; + + Matrix3 l2w = GetObjectTM(currTime); + + if( obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) ) + { + DummyObject* dummy = (DummyObject*)obj; + Box3 bnd = dummy->GetBox(); + + return BoxVolume(bnd, l2w); + } + + if( obj->CanConvertToType(triObjectClassID) ) + { + TriObject *meshObj = (TriObject *)obj->ConvertToType(currTime, triObjectClassID); + if( !meshObj ) + return 0; + + Mesh& mesh = meshObj->mesh; + Box3 bnd = mesh.getBoundingBox(); + + if( meshObj != obj ) + meshObj->DeleteThis(); + + return BoxVolume(bnd, l2w); + } + + // Don't know how to interpret other, it's not contained. + return 0; +} + +hsMatrix44 plMaxNodeBase::GetLocalToParent44(TimeValue t) +{ + Matrix3 m3 = GetLocalToParent(t); + hsMatrix44 m44 = Matrix3ToMatrix44(m3); + return m44; +} + +hsMatrix44 plMaxNodeBase::GetParentToLocal44(TimeValue t) +{ + Matrix3 m3 = GetParentToLocal(t); + hsMatrix44 m44 = Matrix3ToMatrix44(m3); + return m44; +} + +hsMatrix44 plMaxNodeBase::GetLocalToWorld44(TimeValue t) +{ + Matrix3 m3 = GetLocalToWorld(t); + hsMatrix44 m44 = Matrix3ToMatrix44(m3); + return m44; +} + +hsMatrix44 plMaxNodeBase::GetWorldToLocal44(TimeValue t) +{ + Matrix3 m3 = GetWorldToLocal(t); + hsMatrix44 m44 = Matrix3ToMatrix44(m3); + return m44; +} + +hsMatrix44 plMaxNodeBase::GetOTM44(TimeValue t) +{ + Matrix3 m3 = GetOTM(t); + hsMatrix44 m44 = Matrix3ToMatrix44(m3); + return m44; +} + +hsMatrix44 plMaxNodeBase::GetVertToLocal44(TimeValue t) +{ + Matrix3 m3 = GetVertToLocal(t); + hsMatrix44 m44 = Matrix3ToMatrix44(m3); + return m44; +} + +hsMatrix44 plMaxNodeBase::GetLocalToVert44(TimeValue t) +{ + Matrix3 m3 = GetLocalToVert(t); + hsMatrix44 m44 = Matrix3ToMatrix44(m3); + return m44; +} + +hsMatrix44 plMaxNodeBase::GetLocalToOBB44(TimeValue t) +{ + Matrix3 m3 = GetLocalToOBB(t); + hsMatrix44 m44 = Matrix3ToMatrix44(m3); + return m44; +} + +hsMatrix44 plMaxNodeBase::GetOBBToLocal44(TimeValue t) +{ + Matrix3 m3 = GetOBBToLocal(t); + hsMatrix44 m44 = Matrix3ToMatrix44(m3); + return m44; +} + +Matrix3 plMaxNodeBase::GetParentToWorld(TimeValue t) +{ + return Inverse(GetWorldToParent(t)); +} + +Matrix3 plMaxNodeBase::GetWorldToParent(TimeValue t) +{ + // This may look back-ass-ward, but that's only because it + // is. If we've got inheritance filtering on, then our localtoworld + // is no longer parentl2w * l2p, because we'll be ignoring + // some of our parent's transform. More precisely, we'll be + // clobbering parts of the product of our parent's current transform + // and our current local to parent. So we're going to calculate + // a parent to world transform here that would get us to the + // right point and orientation in space, even though it has + // little or nothing to do with our parent's real transform. + // Note that we only go through this charade if we've got + // filtering of inheritance active for this node. + plMaxNodeBase* parent = (plMaxNodeBase*)GetParentNode(); + if( !GetFilterInherit() ) + return parent->GetWorldToLocal(t); + + // l2w = l2p * parentL2W + // l2w * parentW2L = l2p + // parentW2L = w2l * l2p + Point3 pos; + float rot[4]; + ScaleValue scl; + Interval posInv; + Interval rotInv; + Interval sclInv; + + Matrix3Indirect parentMatrix(parent->GetNodeTM(t)); + + TMComponentsArg cmpts(&pos, &posInv, rot, &rotInv, &scl, &sclInv); + GetTMController()->GetLocalTMComponents(t, cmpts, parentMatrix); + + Quat q; + if( cmpts.rotRep == TMComponentsArg::RotationRep::kQuat ) + q = Quat(rot); + else + EulerToQuat(rot, q, cmpts.rotRep); + + Matrix3 l2p(true); + l2p.PreTranslate(pos); + PreRotateMatrix(l2p, q); + l2p.PreScale(scl.s); + PreRotateMatrix(l2p, scl.q); + + Matrix3 w2l = GetWorldToLocal(t); + + return w2l * l2p; +} + +Matrix3 plMaxNodeBase::GetLocalToParent(TimeValue t) +{ + // l2w = l2p * parentL2W + // l2w * Inverse(parentL2W) = l2p + // l2w * parentW2L = l2p + Matrix3 l2w = GetLocalToWorld(t); + Matrix3 w2p(true); + if( !GetParentNode()->IsRootNode() ) + w2p = GetWorldToParent(t); + + Matrix3 l2p = l2w * w2p; + return l2p; +} + +Matrix3 plMaxNodeBase::GetParentToLocal(TimeValue t) +{ + Matrix3 loc2Par = GetLocalToParent(t); + Matrix3 par2Loc = Inverse(loc2Par); + return par2Loc; +} + +Matrix3 plMaxNodeBase::GetLocalToWorld(TimeValue t) +{ + if( GetForceLocal() ) + { + // v2l * l2w = objectTM + // l2w = Inverse(v2l) * objectTM; + // l2w = l2v * objectTM + Matrix3 objectTM = GetObjectTM(t); + Matrix3 l2w = GetLocalToVert(t) * objectTM ; + return l2w; + } + if( !GetParentNode()->IsRootNode() ) + return GetParentToWorld(t); + + return Matrix3(true); +} + +Matrix3 plMaxNodeBase::GetWorldToLocal(TimeValue t) +{ + Matrix3 l2w = GetLocalToWorld(t); + Matrix3 w2l = Inverse(l2w); + return w2l; +} + +Matrix3 plMaxNodeBase::GetOTM(TimeValue t) +{ + // objectTM = otm * nodeTM + // otm = objectTM * Inverse(nodeTM) + Matrix3 objectTM = GetObjectTM(t); + Matrix3 nodeTM = GetNodeTM(t); + Matrix3 otm = objectTM * Inverse(nodeTM); + otm.ValidateFlags(); + return otm; +} + +Matrix3 plMaxNodeBase::GetVertToLocal(TimeValue t) +{ + Matrix3 objectTM; + + // + // If animated or we want forced into local space + // still return OTM to fold into vertices + // + if( GetForceLocal() ) + { + return GetOTM(t); + } + + // otherwise flatten our local to parent into the verts + // note that our parent may have flattened their l2p into + // their v2l as well. + // so + // objectTM = v2l * l2p * parentL2W + // objectTM * Inverse(parentL2W) = v2l * l2p + // objectTM * parentW2L = v2l (w/ l2p folded in) + // + // Objects transformation ObjectTM = OTM * nodeTM + objectTM = GetObjectTM(t); + + Matrix3 w2p(true); + if( !GetParentNode()->IsRootNode() ) + w2p = GetWorldToParent(t); + + Matrix3 v2l = objectTM * w2p; + + return v2l; +} + +Matrix3 plMaxNodeBase::GetLocalToVert(TimeValue t) +{ + Matrix3 v2l = GetVertToLocal(t); + Matrix3 l2v = Inverse(v2l); + return l2v; +} + +Matrix3 plMaxNodeBase::GetLocalToOBB(TimeValue t) +{ + return GetLocalToVert(t) * GetOTM(t); +} + +Matrix3 plMaxNodeBase::GetOBBToLocal(TimeValue t) +{ + return Inverse(GetOTM(t)) * GetVertToLocal(t); +} + +hsMatrix44 plMaxNodeBase::Matrix3ToMatrix44(const Matrix3& m3) +{ + const MRow* m = m3.GetAddr(); + + hsMatrix44 m44; + m44.Reset(); + m44.fMap[0][0] = m[0][0]; + m44.fMap[0][1] = m[1][0]; + m44.fMap[0][2] = m[2][0]; + m44.fMap[0][3] = m[3][0]; + + m44.fMap[1][0] = m[0][1]; + m44.fMap[1][1] = m[1][1]; + m44.fMap[1][2] = m[2][1]; + m44.fMap[1][3] = m[3][1]; + + m44.fMap[2][0] = m[0][2]; + m44.fMap[2][1] = m[1][2]; + m44.fMap[2][2] = m[2][2]; + m44.fMap[2][3] = m[3][2]; + + m44.IsIdentity(); + + return m44; +} + +Matrix3 plMaxNodeBase::Matrix44ToMatrix3(const hsMatrix44& m44) +{ + Matrix3 m3; + + MRow* m = m3.GetAddr(); + + m[0][0] = m44.fMap[0][0]; + m[1][0] = m44.fMap[0][1]; + m[2][0] = m44.fMap[0][2]; + m[3][0] = m44.fMap[0][3]; + + m[0][1] = m44.fMap[1][0]; + m[1][1] = m44.fMap[1][1]; + m[2][1] = m44.fMap[1][2]; + m[3][1] = m44.fMap[1][3]; + + m[0][2] = m44.fMap[2][0]; + m[1][2] = m44.fMap[2][1]; + m[2][2] = m44.fMap[2][2]; + m[3][2] = m44.fMap[2][3]; + + m3.ValidateFlags(); + + return m3; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeBase.h new file mode 100644 index 00000000..12b1742d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeBase.h @@ -0,0 +1,299 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plMaxNodeBase_inc +#define plMaxNodeBase_inc + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "max.h" +#include "hsMatrix44.h" +#include "hsColorRGBA.h" +#include "../pnKeyedObject/plKey.h" +#include "plLoadMask.h" + +class plLocation; +class plSceneObject; +class plModifier; +class plComponentBase; +class ISkin; +class plMaxNodeData; +class hsGMesh; +class plRenderLevel; +class plGeometrySpan; +class plSharedMesh; +class plMaxBoneMap; + +//------------------------------------------- +// plMaxNodeBase +//------------------------------------------- +// CAREFUL! This class is different, it is derived from Max's INode (as you can see) +// But we can only add (NON Virtual) functions to plMaxNodeBase directly +// If you want some new Data members, you can add them by adding to the class +// plMaxNodeData This data is stored in each INode through some mechanisms supplied +// It would be nice of you to add GetFunctions for each new data member you add (see below) +//-------------------------------------------- +// NOTE: an INode can be cast to a plMaxNodeBase, but currently it is the MakeSceneObject Pass which +// Adds the plMaxNodeData to the Node +// +// This class should only reference classes that are in the nucleus. Anything +// that needs more should go into plMaxNode +class plMaxNodeBase : public INode +{ +public: + plMaxNodeData *GetMaxNodeData(); // perhaps with full getters and Setters, we can make this protected + void SetMaxNodeData(plMaxNodeData * pDat); + + //------------------------------ + // Get Data from MaxNodeData + //------------------------------ + // If recalculate is true the cached value is ignored. (Useful in the SceneViewer) + hsBool CanConvert(bool recalculate=false); + plLocation GetLocation(); + plKey GetKey(); + plSceneObject* GetSceneObject(); + hsBool GetForceLocal(); + hsBool GetReverseSort(); + hsBool GetSortAsOpaque(); + hsBool GetRunTimeLight(); + hsBool GetForceMatShade(); + hsBool GetForceVisLOS(); + hsBool GetEnviron(); + hsBool GetEnvironOnly(); + hsBool GetWaterDecEnv(); + hsBool GetVS(); + hsBool GetHasWaterHeight(); + hsScalar GetWaterHeight(); + hsBool GetSmoothAll(); + hsBool GetForceSortable(); + hsBool GetConcave(); + hsBool GetCalcEdgeLens(); + hsBool GetNoPreShade(); + hsBool GetForcePreShade(); + plKey GetRoomKey(); + hsBool GetDrawable(); + hsBool GetPhysical(); + hsBool GetItinerant(); + hsBool GetUnBounded(); + hsBool GetDisableNormal(); + UInt32 GetDecalLevel(); + hsBool GetMovable(); + hsBool GetNoShadow(); + hsBool GetForceShadow(); + hsBool GetAlphaTestHigh(); + hsBool GetFilterInherit(); + hsBool GetIsBarney(); + hsBool GetNoSpanSort(); + hsBool GetNoSpanReSort(); + hsBool GetNoFaceSort(); + hsBool GetNoDeferDraw(); + hsBool GetBlendToFB(); + hsBool GetForceMaterialCopy(); + hsBool GetInstanced(); + hsBool GetParticleRelated(); + UInt32 GetSoundIdxCounter(); + plSceneObject* GetAvatarSO(); + BOOL HasFade(); + Box3 GetFade(); + hsBool GetDup2Sided(); + hsBool GetRadiateNorms(); + BOOL HasNormalChan(); + int GetNormalChan(); + BOOL GetGeoDice(int& maxFaces, float& maxSize, int& minFaces); + hsBool GetIsGUI(); + plSharedMesh* GetSwappableGeom(); + UInt32 GetSwappableGeomTarget(); + plMaxBoneMap* GetBoneMap(); + hsBool GetOverrideHighLevelSDL(); + UInt8 GetAnimCompress(); + hsScalar GetKeyReduceThreshold(); + int NumRenderDependencies(); + plMaxNodeBase* GetRenderDependency(int i); + + int NumBones(); + plMaxNodeBase* GetBone(int i); + + //------------------------------ + // Set Data from MaxNodeData + //------------------------------ + void SetCanConvert(hsBool b); + void SetMesh(hsGMesh *p); + void SetRoomKey(plKey p); + void SetDrawable(hsBool b); + void SetPhysical(hsBool b); + void SetItinerant(hsBool b); + void SetUnBounded(hsBool b); + void SetDisableNormal(hsBool b); + void SetDecalLevel(UInt32 i); + void SetMovable(hsBool b); + void SetNoPreShade(hsBool b); + void SetForcePreShade(hsBool b); + void SetReverseSort(hsBool b); + void SetSortAsOpaque(hsBool b); + void SetVS(hsBool b); + void SetHasWaterHeight(hsBool b); + void SetWaterHeight(hsScalar h); + void SetSmoothAll(hsBool b); + void SetForceSortable(hsBool b); + void SetConcave(hsBool b); + void SetCalcEdgeLens(hsBool b); + void SetRunTimeLight(hsBool b); + void SetForceMatShade(hsBool b); + void SetForceVisLOS(hsBool b); + void SetEnviron(hsBool b); + void SetEnvironOnly(hsBool b); + void SetWaterDecEnv(hsBool b); + void SetForceLocal(hsBool b); + void SetIsBarney(hsBool b); + void SetForceShadow(hsBool b); + void SetAlphaTestHigh(hsBool b); + void SetFilterInherit(hsBool b); + void SetNoShadow(hsBool b); + void SetNoSpanSort(hsBool b); + void SetNoSpanReSort(hsBool b); + void SetNoFaceSort(hsBool b); + void SetNoDeferDraw(hsBool b); + void SetBlendToFB(hsBool b); + void SetForceMaterialCopy(hsBool b); + void SetInstanced(hsBool b); + void SetParticleRelated(hsBool b); + void SetSoundIdxCounter(UInt32 ctr); + void SetAvatarSO(plSceneObject *so); + void SetFade(const Box3& b); + void SetDup2Sided(hsBool b); + void SetRadiateNorms(hsBool b); + void SetNormalChan(int n); + void SetGeoDice(BOOL on, int maxFaces, float maxSize, int minFaces); + void SetIsGUI(hsBool b); + void SetSwappableGeom(plSharedMesh *sm); + void SetSwappableGeomTarget(UInt32 id); + void SetBoneMap(plMaxBoneMap *bones); + void SetOverrideHighLevelSDL(hsBool b); + void SetAnimCompress(UInt8 v); + void SetKeyReduceThreshold(hsScalar v); + hsBool AddRenderDependency(plMaxNodeBase* m); + hsBool RenderDependsOn(plMaxNodeBase* m); + void ClearRenderDependencies(); + + void AddBone(plMaxNodeBase* m); + void ClearBones(); + + // Dirty flags for SceneWatcher use + enum { kGeomDirty = 0x1, kMatDirty = 0x2, kAllDirty = 0xFF }; + hsBool GetDirty(UInt8 i); + void SetDirty(UInt8 i, hsBool b); + + plKey GetParentKey() { plMaxNodeBase *pPar = (plMaxNodeBase*)GetParentNode(); hsAssert(pPar, "No Parent"); return pPar->GetKey(); } + + ISkin* FindSkinModifier(); // Returns the object's skin modifier if it has one, else nil + const plRenderLevel& GetRenderLevel(hsBool forBlend); + + hsBool HasLoadMask(); + plLoadMask GetLoadMask(); + void AddLoadMask(const plLoadMask& m); + + hsBool IsTMAnimated(); + hsBool IsTMAnimatedRecur(); + hsBool IsMovable(); // Checks to see whether this node will ever move in the scene + bool IsXRef(); // Returns true if object is an XRef or part of an XRef'd scene + + //---------- + // Component + //---------- + hsBool IsComponent(Object *obj=nil); // Object pointer is only necessary for internal use, + hsBool IsExternComponent(Object *obj=nil); + plComponentBase *ConvertToComponent(); // Returns nil if node is not a component + + + // Normally you will only want the components that are attached to you + // because you are in their targets list. However, in some cases a + // component will want to know what other components are attached to it. In + // that case, set all to true, so that the attached components won't be + // verified to be in your target list. + UInt32 NumAttachedComponents(bool all=false); + plComponentBase *GetAttachedComponent(UInt32 i, bool all=false); + + hsBool Contains(const Point3& worldPt); // is the world space point inside my (CONVEX) geometry or dummy box? + hsBool Contains(const Box3& bnd, const Matrix3& l2w); // is the box contained entirely inside my (CONVEX) geometry or dummy box? + hsScalar BoxVolume(const Box3& bnd, const Matrix3& l2w); + hsScalar RegionPriority(); // returns a dominance factor. If a point is in more than one environmental + // region, the region with highest priority wins. + + Interface *GetInterface() { return ::GetCOREInterface(); } + + static hsMatrix44 Matrix3ToMatrix44(const Matrix3& m3); + static Matrix3 Matrix44ToMatrix3(const hsMatrix44& m44); + + // Don't use these two functions, they probably don't return what + // you think they do. See code comments. + Matrix3 GetWorldToParent(TimeValue t); + Matrix3 GetParentToWorld(TimeValue t); + + hsMatrix44 GetLocalToParent44(TimeValue t = TimeValue(0)); + hsMatrix44 GetParentToLocal44(TimeValue t = TimeValue(0)); + hsMatrix44 GetLocalToWorld44(TimeValue t = TimeValue(0)); + hsMatrix44 GetWorldToLocal44(TimeValue t = TimeValue(0)); + hsMatrix44 GetOTM44(TimeValue t = TimeValue(0)); + hsMatrix44 GetVertToLocal44(TimeValue t = TimeValue(0)); + hsMatrix44 GetLocalToVert44(TimeValue t = TimeValue(0)); + hsMatrix44 GetOBBToLocal44(TimeValue t = TimeValue(0)); + hsMatrix44 GetLocalToOBB44(TimeValue t = TimeValue(0)); + + Matrix3 GetLocalToParent(TimeValue t = TimeValue(0)); + Matrix3 GetParentToLocal(TimeValue t = TimeValue(0)); + Matrix3 GetLocalToWorld(TimeValue t = TimeValue(0)); + Matrix3 GetWorldToLocal(TimeValue t = TimeValue(0)); + Matrix3 GetOTM(TimeValue t = TimeValue(0)); + Matrix3 GetVertToLocal(TimeValue t = TimeValue(0)); + Matrix3 GetLocalToVert(TimeValue t = TimeValue(0)); + Matrix3 GetOBBToLocal(TimeValue t = TimeValue(0)); + Matrix3 GetLocalToOBB(TimeValue t = TimeValue(0)); + +protected: + // AppDataChunk sub-chunk id's + enum + { + kPlasmaAgeChunk, // No longer in use, but cleared from old files + kPlasmaDistChunk, // No longer in use, but cleared from old files + kPlasmaRoomChunk, // No longer in use, but cleared from old files + kPlasmaMaxNodeDataChunk, + kPlasmaSceneViewerChunk, + kPlasmaLightChunk, // No longer in use, but cleared from old files + }; + + UInt8 *IGetSceneViewerChunk(); + // Attempts to convert a RefMaker pointer to a component. Returns nil if it is not a component. + plComponentBase *IRefMakerToComponent(ReferenceMaker *maker, bool all); + + UInt32 IGetMajorRenderLevel(hsBool forBlend); + UInt32 IGetMinorRenderLevel(hsBool forBlend); + + hsBool IRenderLevelSet(hsBool forBlend); + void ISetRenderLevel(const plRenderLevel& l, hsBool forBlend); + const plRenderLevel& IGetRenderLevel(hsBool forBlend); + plRenderLevel ICalcRenderLevel(hsBool forBlend); +}; + +#endif //plMaxNodeBase_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeData.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeData.h new file mode 100644 index 00000000..7f63505a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxNodeData.h @@ -0,0 +1,435 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plMaxNodeData_inc +#define plMaxNodeData_inc + +#include "hsTypes.h" +#include "hsTemplates.h" +#include "hsColorRGBA.h" +#include "plRenderLevel.h" +#include "plPhysicalProps.h" +#include "hsBitVector.h" +#include "../pnKeyedObject/plKey.h" +#include "plLoadMask.h" + +class plMaxNodeBase; +class plMaxNode; +class plLocation; +class plSceneObject; +class hsGMesh; +class plGeometrySpan; +class plMaxBoneMap; +class plSharedMesh; + +class plMaxNodeBaseTab : public Tab +{ +}; + + +// +// Created hoping to trim some fat so to speak for all the bools that +// we are tossing around. +// + +class DataBF +{ +public: + enum DatBit + { + kCanConvert = 0x0, + kForceLocal, + kDrawable, // Absence of this prop forbids making this drawable + kBlendToFB, + kItinerant, + kRunTimeLight, + kMovable, + kIsBarney, + kForceMatCopy, + kNoPreShade, + kIsInstanced, + kParticleRelated, + kNoSpanReSort, + kHasFade, + kNoFaceSort, + kNoSpanSort, + kNoDeferDraw, + kBLevelSet, + kOLevelSet, + kDup2Sided, + kRadiateNorms, + kGeoDice, + kForcePreShade, + kIsGUI, + kSwappableGeomTarget, + kNoShadow, + kForceShadow, + kFilterInherit, + kReverseSort, + kVS, + kConcave, + kCalcEdgeLens, + kEnviron, + kEnvironOnly, + kPhysical, // Absence of this prop forbids making this physical + kSmoothAll, + kForceSortable, + kOverrideHighLevelSDL, + kDisableNormal, + kHasLoadMask, + kWaterDecEnv, + kWaterHeight, + kAlphaTestHigh, + kForceMatShade, + kUnBounded, + kForceVisLOS, + kSortAsOpaque, + }; + + hsBitVector* fBitVector; + + DataBF() + { + fBitVector = TRACKED_NEW hsBitVector; + fBitVector->SetBit(kDrawable); + fBitVector->SetBit(kPhysical); + } + virtual ~DataBF() { delete fBitVector; } + + DataBF& operator=(const DataBF& ot) + { + *fBitVector = *ot.fBitVector; + return *this; + } + DataBF(const DataBF& ot) + { + fBitVector = TRACKED_NEW hsBitVector; + *fBitVector = *ot.fBitVector; + } + + void Init() + { + fBitVector = TRACKED_NEW hsBitVector; + fBitVector->SetBit(kDrawable); + fBitVector->SetBit(kPhysical); + } + void DeInit() { delete fBitVector; fBitVector = nil; } + + hsBool CanBF(DatBit bitChoice) { return fBitVector->IsBitSet(bitChoice); } + void SetBF(hsBool b, DatBit bitChoice) { fBitVector->SetBit(bitChoice, b); } +}; + + +class plMaxNodeData +{ +public: + plMaxNodeData() : + fpKey(nil), + fpSO(nil) , + fDecalLevel(0), + fpMesh(nil), + fpRoomKey(nil), + fSoundIdxCounter( 0 ), + fAvatarSO(nil), + fFade(Point3(0,0,0), Point3(0,0,0)), + fNormalChan(0), + fWaterHeight(0), + fGDMaxFaces(0), fGDMaxSize(0), fGDMinFaces(0), + fSwapMesh(nil), + fSwapTargetID((UInt32)-1), + fCachedAlphaHackLayerCounts(nil), + fBoneMap(nil), + fAnimCompression(1), // Should be plAnimCompressComp::kCompressionLow, + // but I don't want to include the entire header. + fKeyReduceThreshold(0.0002) + { } + ~plMaxNodeData() + { + fpKey = nil; + fpRoomKey = nil; + fpSO = 0; + fAvatarSO = nil; + delete fCachedAlphaHackLayerCounts; + MaxDatBF.DeInit(); + } + + // Call init on MaxNodeData whose constructor was never called, e.g. if it was malloc'd. + plMaxNodeData& Init() + { + memset( &fpKey, 0, sizeof( plKey ) ); + memset( &fpRoomKey, 0, sizeof( plKey ) ); + fRenderDependencies.Init(); + fBones.Init(); + fCachedAlphaHackLayerCounts = nil; + MaxDatBF.Init(); + return *this; + } + // Ditto + void DeInit( void ) + { + fpKey = nil; + fpRoomKey = nil; + fpSO = 0; fAvatarSO = nil; + delete fCachedAlphaHackLayerCounts; + fCachedAlphaHackLayerCounts = nil; + MaxDatBF.DeInit(); + } + + plKey GetKey() { return fpKey; } + void SetKey(plKey p ) { fpKey = p; } + plSceneObject * GetSceneObject() { return fpSO; } + void SetSceneObject(plSceneObject *p) { fpSO = p; } + + hsBool CanConvert() { return MaxDatBF.CanBF(MaxDatBF.kCanConvert); } + void SetCanConvert(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kCanConvert); } + + hsBool GetForceLocal() { return MaxDatBF.CanBF(MaxDatBF.kForceLocal); } + void SetForceLocal(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kForceLocal); } + + void * GetMesh() { return fpMesh; } // void pointer for header simplicity + void SetMesh(hsGMesh *p) { fpMesh = p; } + + hsBool GetDrawable() {return MaxDatBF.CanBF(MaxDatBF.kDrawable); } + void SetDrawable(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kDrawable); } + + hsBool GetPhysical() {return MaxDatBF.CanBF(MaxDatBF.kPhysical); } + void SetPhysical(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kPhysical); } + + hsBool GetRunTimeLight() {return MaxDatBF.CanBF(MaxDatBF.kRunTimeLight); } + void SetRunTimeLight(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kRunTimeLight); } + + hsBool GetForceMatShade() {return MaxDatBF.CanBF(MaxDatBF.kForceMatShade); } + void SetForceMatShade(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kForceMatShade); } + + hsBool GetForceVisLOS() {return MaxDatBF.CanBF(MaxDatBF.kForceVisLOS); } + void SetForceVisLOS(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kForceVisLOS); } + + hsBool GetEnviron() {return MaxDatBF.CanBF(MaxDatBF.kEnviron); } + void SetEnviron(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kEnviron); } + + hsBool GetEnvironOnly() {return MaxDatBF.CanBF(MaxDatBF.kEnvironOnly); } + void SetEnvironOnly(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kEnvironOnly); } + + hsBool GetWaterDecEnv() { return MaxDatBF.CanBF(MaxDatBF.kWaterDecEnv); } + void SetWaterDecEnv(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kWaterDecEnv); } + + hsBool GetItinerant() {return MaxDatBF.CanBF(MaxDatBF.kItinerant); } + void SetItinerant(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kItinerant); } + + hsBool GetUnBounded() {return MaxDatBF.CanBF(MaxDatBF.kUnBounded); } + void SetUnBounded(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kUnBounded); } + + hsBool GetDisableNormal() {return MaxDatBF.CanBF(MaxDatBF.kDisableNormal); } + void SetDisableNormal(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kDisableNormal); } + + hsBool GetMovable() {return MaxDatBF.CanBF(MaxDatBF.kMovable); } + void SetMovable(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kMovable); } + + hsBool GetNoPreShade() {return MaxDatBF.CanBF(MaxDatBF.kNoPreShade); } + void SetNoPreShade(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kNoPreShade); } + + hsBool GetForcePreShade() {return MaxDatBF.CanBF(MaxDatBF.kForcePreShade); } + void SetForcePreShade(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kForcePreShade); } + + hsBool GetNoShadow() {return MaxDatBF.CanBF(MaxDatBF.kNoShadow); } + void SetNoShadow(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kNoShadow); } + + hsBool GetForceShadow() {return MaxDatBF.CanBF(MaxDatBF.kForceShadow); } + void SetForceShadow(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kForceShadow); } + + hsBool GetAlphaTestHigh() {return MaxDatBF.CanBF(MaxDatBF.kAlphaTestHigh); } + void SetAlphaTestHigh(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kAlphaTestHigh); } + + hsBool GetFilterInherit() {return MaxDatBF.CanBF(MaxDatBF.kFilterInherit); } + void SetFilterInherit(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kFilterInherit); } + + hsBool GetIsBarney() {return MaxDatBF.CanBF(MaxDatBF.kIsBarney); } + void SetIsBarney(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kIsBarney); } + + hsBool GetNoSpanSort() {return MaxDatBF.CanBF(MaxDatBF.kNoSpanSort); } + void SetNoSpanSort(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kNoSpanSort); } + + hsBool GetNoSpanReSort() {return MaxDatBF.CanBF(MaxDatBF.kNoSpanReSort); } + void SetNoSpanReSort(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kNoSpanReSort); } + + hsBool GetNoFaceSort() {return MaxDatBF.CanBF(MaxDatBF.kNoFaceSort); } + void SetNoFaceSort(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kNoFaceSort); } + + hsBool GetReverseSort() {return MaxDatBF.CanBF(MaxDatBF.kReverseSort); } + void SetReverseSort(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kReverseSort); } + + hsBool GetSortAsOpaque() {return MaxDatBF.CanBF(MaxDatBF.kSortAsOpaque); } + void SetSortAsOpaque(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kSortAsOpaque); } + + hsBool GetVS() {return MaxDatBF.CanBF(MaxDatBF.kVS); } + void SetVS(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kVS); } + + hsBool GetHasWaterHeight() { return MaxDatBF.CanBF(MaxDatBF.kWaterHeight); } + void SetHasWaterHeight(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kWaterHeight); } + hsScalar GetWaterHeight() { return fWaterHeight; } + void SetWaterHeight(hsScalar f) { SetHasWaterHeight(true); fWaterHeight = f; } + + hsBool GetSmoothAll() {return MaxDatBF.CanBF(MaxDatBF.kSmoothAll); } + void SetSmoothAll(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kSmoothAll); } + + hsBool GetForceSortable() {return MaxDatBF.CanBF(MaxDatBF.kForceSortable); } + void SetForceSortable(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kForceSortable); } + + hsBool GetConcave() {return MaxDatBF.CanBF(MaxDatBF.kConcave); } + void SetConcave(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kConcave); } + + hsBool GetCalcEdgeLens() {return MaxDatBF.CanBF(MaxDatBF.kCalcEdgeLens); } + void SetCalcEdgeLens(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kCalcEdgeLens); } + + hsBool GetNoDeferDraw() {return MaxDatBF.CanBF(MaxDatBF.kNoDeferDraw); } + void SetNoDeferDraw(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kNoDeferDraw); } + + hsBool GetBlendToFB() {return MaxDatBF.CanBF(MaxDatBF.kBlendToFB); } + void SetBlendToFB(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kBlendToFB); } + + hsBool GetForceMaterialCopy() {return MaxDatBF.CanBF(MaxDatBF.kForceMatCopy); } + void SetForceMaterialCopy(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kForceMatCopy); } + + hsBool GetInstanced() {return MaxDatBF.CanBF(MaxDatBF.kIsInstanced); } + void SetInstanced(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kIsInstanced); } + + hsBool GetParticleRelated() {return MaxDatBF.CanBF(MaxDatBF.kParticleRelated); } + void SetParticleRelated(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kParticleRelated); } + + hsBool GetDup2Sided() {return MaxDatBF.CanBF(MaxDatBF.kDup2Sided); } + void SetDup2Sided(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kDup2Sided); } + + hsBool GetRadiateNorms() {return MaxDatBF.CanBF(MaxDatBF.kRadiateNorms); } + void SetRadiateNorms(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kRadiateNorms); } + + hsBool GetGeoDice() {return MaxDatBF.CanBF(MaxDatBF.kGeoDice); } + void SetGeoDice(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kGeoDice); } + + hsBool GetIsGUI() {return MaxDatBF.CanBF(MaxDatBF.kIsGUI); } + void SetIsGUI(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kIsGUI); } + + plSharedMesh* GetSwappableGeom() { return fSwapMesh;} + void SetSwappableGeom(plSharedMesh *sm) { fSwapMesh = sm; } + + UInt32 GetSwappableGeomTarget() { return fSwapTargetID;} + void SetSwappableGeomTarget(UInt32 id) { fSwapTargetID = id;} + + plMaxBoneMap* GetBoneMap() { return fBoneMap;} + void SetBoneMap(plMaxBoneMap *bones) { fBoneMap = bones;} + + int GetGeoDiceMaxFaces() { return fGDMaxFaces; } + void SetGeoDiceMaxFaces(int f) { fGDMaxFaces = f; } + + float GetGeoDiceMaxSize() { return fGDMaxSize; } + void SetGeoDiceMaxSize(float f) { fGDMaxSize = f; } + + int GetGeoDiceMinFaces() { return fGDMinFaces; } + void SetGeoDiceMinFaces(int f) { fGDMinFaces = f; } + + plKey GetRoomKey() { return fpRoomKey; } + void SetRoomKey(plKey p) { fpRoomKey = p; } + + UInt32 GetDecalLevel() { return fDecalLevel; } + void SetDecalLevel(UInt32 i) { fDecalLevel = i; } + + UInt32 GetSoundIdxCounter() { return fSoundIdxCounter; } + void SetSoundIdxCounter( UInt32 i ) { fSoundIdxCounter = i; } + + plSceneObject * GetAvatarSO() { return fAvatarSO; } + void SetAvatarSO(plSceneObject *so) { fAvatarSO = so; } + + int NumRenderDependencies() { return fRenderDependencies.Count(); } + plMaxNodeBase* GetRenderDependency(int i) { return fRenderDependencies[i]; } + void AddRenderDependency(plMaxNodeBase* m) { fRenderDependencies.Append(1, &m); } + void ClearRenderDependencies() { fRenderDependencies.ZeroCount(); } + + int NumBones() { return fBones.Count(); } + plMaxNodeBase* GetBone(int i) { return fBones[i]; } + void AddBone(plMaxNodeBase* m) { fBones.Append(1, &m); } + void ClearBones() { fBones.ZeroCount(); } + + hsBool HasFade() { return MaxDatBF.CanBF(MaxDatBF.kHasFade); } + void SetFade(const Box3& b) { MaxDatBF.SetBF((b.Min()[2] != 0)||(b.Max()[2] != 0), MaxDatBF.kHasFade); fFade = b; } + Box3 GetFade() { return fFade; } + + hsBool HasLoadMask() { return MaxDatBF.CanBF(MaxDatBF.kHasLoadMask); } + plLoadMask GetLoadMask() { return HasLoadMask() ? fLoadMask : plLoadMask::kAlways; } + void AddLoadMask(const plLoadMask& m) { if( !HasLoadMask() ) { fLoadMask = m; MaxDatBF.SetBF(true, MaxDatBF.kHasLoadMask); }else{ fLoadMask |= m; } } + + hsBool HasNormalChan() { return fNormalChan > 0; } + void SetNormalChan(int n) { fNormalChan = n; } + int GetNormalChan() { return fNormalChan; } + + hsBool BlendLevelSet() { return MaxDatBF.CanBF(MaxDatBF.kBLevelSet); } + void SetBlendLevel(const plRenderLevel& l) { fBlendLevel = l; MaxDatBF.SetBF(true, MaxDatBF.kBLevelSet); } + const plRenderLevel& GetBlendLevel() { return fBlendLevel; } + hsBool OpaqueLevelSet() { return MaxDatBF.CanBF(MaxDatBF.kOLevelSet); } + void SetOpaqueLevel(const plRenderLevel& l) { fOpaqueLevel = l; MaxDatBF.SetBF(true, MaxDatBF.kOLevelSet); } + const plRenderLevel& GetOpaqueLevel() { return fOpaqueLevel; } + + plPhysicalProps* GetPhysicalProps() { return &fPhysicalProps; } + + hsTArray *GetAlphaHackLayersCache( void ) { return fCachedAlphaHackLayerCounts; } + void SetAlphaHackLayersCache( hsTArray *cache ) { fCachedAlphaHackLayerCounts = cache; } + hsBool GetOverrideHighLevelSDL() { return MaxDatBF.CanBF(MaxDatBF.kOverrideHighLevelSDL); } + void SetOverrideHighLevelSDL(hsBool b) { MaxDatBF.SetBF(b, MaxDatBF.kOverrideHighLevelSDL); } + UInt8 GetAnimCompress() { return fAnimCompression; } + void SetAnimCompress(UInt8 v) { fAnimCompression = v; } + hsScalar GetKeyReduceThreshold() { return fKeyReduceThreshold; } + void SetKeyReduceThreshold(hsScalar v) { fKeyReduceThreshold = v; } + +protected: + plKey fpKey; + plSceneObject * fpSO; + // hacking this in here temporarily because everything's about to change in the mesh world... + hsGMesh* fpMesh; + plKey fpRoomKey; + UInt32 fDecalLevel; + Int32 fNormalChan; + UInt32 fSoundIdxCounter; + plSceneObject * fAvatarSO; + plMaxNodeBaseTab fRenderDependencies; + plMaxNodeBaseTab fBones; + Box3 fFade; + int fGDMaxFaces; + float fGDMaxSize; + int fGDMinFaces; + plRenderLevel fBlendLevel; + plRenderLevel fOpaqueLevel; + plPhysicalProps fPhysicalProps; + UInt32 fSwapTargetID; + hsTArray *fCachedAlphaHackLayerCounts; + plSharedMesh *fSwapMesh; + plMaxBoneMap *fBoneMap; + plLoadMask fLoadMask; + hsScalar fWaterHeight; + UInt8 fAnimCompression; + hsScalar fKeyReduceThreshold; + DataBF MaxDatBF; +}; + + +#endif // plSimpleConvert_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxUtils.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxUtils.cpp new file mode 100644 index 00000000..782cb11d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxUtils.cpp @@ -0,0 +1,147 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plMaxUtils.h" +#include "resource.h" + +#include "hsResMgr.h" +#include "../plResMgr/plPageInfo.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnFactory/plFactory.h" + +class MaxUtilsClassDesc : public ClassDesc +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return &plMaxUtils::Instance(); } + const TCHAR* ClassName() { return _T("Plasma Debug Utils"); } + SClass_ID SuperClassID() { return UTILITY_CLASS_ID; } + Class_ID ClassID() { return Class_ID(0x316610ee, 0xebe62c3); } + const TCHAR* Category() { return _T(""); } +}; + +static MaxUtilsClassDesc theMaxUtilsClassDesc; +ClassDesc* GetMaxUtilsDesc() { return &theMaxUtilsClassDesc; } + +plMaxUtils::plMaxUtils() : fhPanel(nil), fhResDlg(nil) +{ +} + +plMaxUtils& plMaxUtils::Instance() +{ + static plMaxUtils theInstance; + return theInstance; +} + +void plMaxUtils::BeginEditParams(Interface *ip, IUtil *iu) +{ + fhPanel = GetCOREInterface()->AddRollupPage(hInstance, + MAKEINTRESOURCE(IDD_UTILS), + ForwardDlgProc, + "Plasma Debug Utils"); +} + +void plMaxUtils::EndEditParams(Interface *ip, IUtil *iu) +{ + GetCOREInterface()->DeleteRollupPage(fhPanel); +} + +BOOL CALLBACK plMaxUtils::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return Instance().DlgProc(hDlg, msg, wParam, lParam); +} + +int ClearTextureIds(); + +BOOL plMaxUtils::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_RES) + { + int numCleared = ClearTextureIds(); + char buf[256]; + sprintf(buf, "Cleared %d texture ids", numCleared); + MessageBox(NULL, buf, "AssetMan Clear", MB_OK); + return TRUE; + } + break; + } + + return FALSE; +} + +#include "plMtlCollector.h" +#include "../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h" +#include "../../AssetMan/PublicInterface/AssManTypes.h" + +int ClearTextureIds() +{ + int numCleared = 0; + + TexSet texmaps; + plMtlCollector::GetMtls(nil, &texmaps); + + TexSet::iterator texIt = texmaps.begin(); + for (; texIt != texmaps.end(); texIt++) + { + Texmap* texmap = (*texIt); + + plPlasmaMAXLayer* layer = plPlasmaMAXLayer::GetPlasmaMAXLayer(texmap); + if (layer) + { + int numBitmaps = layer->GetNumBitmaps(); + for (int i = 0; i < numBitmaps; i++) + { + jvUniqueId assetId; + layer->GetBitmapAssetId(assetId, i); + if (!assetId.IsEmpty()) + { + assetId.SetEmpty(); + layer->SetBitmapAssetId(assetId, i); + numCleared++; + } + } + } + } + + return numCleared; +} +/* +void ClearAssetsRecur(plMaxNode* node) +{ + plComponentBase* comp = node->ConvertToComponent(); + if (comp && plAudioComp::IsSoundComponent(comp)) + { + plBaseSoundEmitterComponent* soundComp = (plBaseSoundEmitterComponent*)comp; + soundComp->SetSoundAssetId(kBaseSound, + kCoverSound +) + } +} +*/ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxUtils.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxUtils.h new file mode 100644 index 00000000..453d9b34 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMaxUtils.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "max.h" +#include "utilapi.h" + +// This a central repository for handy debugging tools for Max that don't fit in anywhere else. +class plMaxUtils : public UtilityObj +{ +protected: + HWND fhPanel; + HWND fhResDlg; + + plMaxUtils(); + +public: + static plMaxUtils& Instance(); + void DeleteThis() {}; + + void BeginEditParams(Interface *ip, IUtil *iu); + void EndEditParams(Interface *ip, IUtil *iu); + + static BOOL CALLBACK ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + static BOOL CALLBACK ResDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMtlCollector.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMtlCollector.cpp new file mode 100644 index 00000000..cf639530 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMtlCollector.cpp @@ -0,0 +1,273 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plMtlCollector.h" + +#include "../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h" + +#include "../MaxPlasmaMtls/Materials/plCompositeMtl.h" +#include "../MaxPlasmaMtls/Materials/plDecalMtl.h" +#include "../MaxPlasmaMtls/Materials/plMultipassMtl.h" +#include "../MaxPlasmaMtls/Materials/plParticleMtl.h" +#include "../MaxPlasmaMtls/Materials/plPassMtl.h" +#include "../MaxPlasmaMtls/Materials/plClothingMtl.h" + +#include "../MaxComponent/plGUIComponents.h" +#include "../MaxComponent/pfGUISkinComp.h" +#include "../MaxComponent/plMiscComponents.h" + +#include "../MaxMain/plMaxNodeBase.h" + +static bool IsPlasmaMtl(Mtl *mtl) +{ + if (mtl->ClassID() == COMP_MTL_CLASS_ID || + mtl->ClassID() == DECAL_MTL_CLASS_ID || + mtl->ClassID() == MULTIMTL_CLASS_ID || + mtl->ClassID() == PARTICLE_MTL_CLASS_ID || + mtl->ClassID() == PASS_MTL_CLASS_ID || + mtl->ClassID() == CLOTHING_MTL_CLASS_ID) + return true; + return false; +} + +static bool IsTexmapOK(Texmap *tex, UInt8 flags) +{ + if (flags & plMtlCollector::kPlasmaOnly && !plPlasmaMAXLayer::GetPlasmaMAXLayer(tex)) + return false; + + return true; +} + +static bool IsMtlOK(Mtl *mtl, UInt8 flags) +{ + if (flags & plMtlCollector::kPlasmaOnly && !IsPlasmaMtl(mtl)) + return false; + + if (flags & plMtlCollector::kNoMultiMtl && mtl->ClassID() == MULTIMTL_CLASS_ID) + return false; + + if (flags & plMtlCollector::kClothingMtlOnly && mtl->ClassID() != CLOTHING_MTL_CLASS_ID) + return false; + + return true; +} + +void GetMtlsRecur(MtlBase *mtlBase, MtlSet* mtls, TexSet* texmaps, UInt32 flags) +{ + if (!mtlBase) + return; + + if (mtlBase->SuperClassID() == TEXMAP_CLASS_ID) + { + Texmap* tex = (Texmap*)mtlBase; + if (texmaps && IsTexmapOK(tex, flags)) + texmaps->insert(tex); + } + else if(mtlBase->SuperClassID() == MATERIAL_CLASS_ID) + { + Mtl* mtl = (Mtl*)mtlBase; + + if (mtls && IsMtlOK(mtl, flags)) + mtls->insert(mtl); + + // Get the bitmaps from all the textures this material contains + int i; + int numTex = mtl->NumSubTexmaps(); + for (i = 0; i < numTex; i++) + { + Texmap *tex = mtl->GetSubTexmap(i); + if (tex) + { + if (texmaps && IsTexmapOK(tex, flags)) + texmaps->insert(tex); + } + } + + // Do the same for any submtls + if (!(flags & plMtlCollector::kNoSubMtls)) + { + int numMtl = mtl->NumSubMtls(); + for (i = 0; i < numMtl; i++) + GetMtlsRecur(mtl->GetSubMtl(i), mtls, texmaps, flags); + } + } + else + { + hsAssert(0, "What kind of material is this?"); + } +} + +static void GetTexmapPBs(Texmap* tex, PBSet& pbs) +{ + if (!tex) + return; + + plPlasmaMAXLayer *plasma = plPlasmaMAXLayer::GetPlasmaMAXLayer( tex ); + if( plasma != nil ) + { + int i; + for( i = 0; i < plasma->GetNumBitmaps(); i++ ) + { + PBBitmap *pbbm = plasma->GetPBBitmap( i ); + if( pbbm != nil ) + pbs.insert( pbbm ); + } + } + else + { + for (int i = 0; i < tex->NumRefs(); i++) + { + ReferenceTarget* r = tex->GetReference(i); + if (r && r->SuperClassID() == PARAMETER_BLOCK2_CLASS_ID) + { + IParamBlock2* pb = (IParamBlock2*)r; + for (int j = 0; j < pb->NumParams(); j++) + { + if (pb->GetParameterType(pb->IndextoID(j)) == TYPE_BITMAP) + { + PBBitmap *pbbm = pb->GetBitmap(j); + + if (pbbm) + pbs.insert(pbbm); + } + } + } + } + } +} + +#include "../MaxPlasmaLights/plRealTimeLightBase.h" + +static void GetNodeMtlsRecur(INode *node, MtlSet* mtls, TexSet* texmaps, UInt32 flags) +{ + Mtl *mtl = node->GetMtl(); + GetMtlsRecur(mtl, mtls, texmaps, flags); + + Object* obj = node->GetObjectRef(); + if (obj && (obj->ClassID() == RTSPOT_LIGHT_CLASSID || obj->ClassID() == RTPDIR_LIGHT_CLASSID)) + { + Texmap* texmap = ((plRTLightBase*)obj)->GetProjMap(); + GetMtlsRecur(texmap, mtls, texmaps, flags); + } + + plGUIControlBase *gui = plGUIControlBase::GetGUIComp( node ); + if( gui != nil ) + { + UInt32 i; + for( i = 0; i < gui->GetNumMtls(); i++ ) + GetMtlsRecur( gui->GetMtl( i ), mtls, texmaps, flags ); + } + else + { + // Skins aren't controls + plGUISkinComp *guiSkin = plGUISkinComp::GetGUIComp( node ); + if( guiSkin != nil ) + { + UInt32 i; + for( i = 0; i < guiSkin->GetNumMtls(); i++ ) + GetMtlsRecur( guiSkin->GetMtl( i ), mtls, texmaps, flags ); + } + else + { + // Um, other components + plComponentBase *base = ( ( plMaxNodeBase *)node )->ConvertToComponent(); + if( base != nil ) + { + if( base->ClassID() == IMAGE_LIB_CID ) + { + pfImageLibComponent *iLib = (pfImageLibComponent *)base; + UInt32 i; + for( i = 0; i < iLib->GetNumBitmaps(); i++ ) + GetMtlsRecur( iLib->GetBitmap( i ), mtls, texmaps, flags ); + } + } + } + } + + for (int i = 0; i < node->NumberOfChildren(); i++) + GetNodeMtlsRecur(node->GetChildNode(i), mtls, texmaps, flags); +} + +static void GetEditorMtls(MtlSet* mtls, TexSet* texmaps, UInt32 flags) +{ + static const int kNumEditorSlots = 24; + + Interface *ip = GetCOREInterface(); + for (int i = 0; i < kNumEditorSlots; i++) + { + MtlBase *mtlBase = ip->GetMtlSlot(i); + GetMtlsRecur(mtlBase, mtls, texmaps, flags); + } +} + +void plMtlCollector::GetMtls(MtlSet* mtls, TexSet* texmaps, UInt32 flags) +{ + Interface *ip = GetCOREInterface(); + + // Make a list of all the textures from the GetSceneMtls() func + MtlBaseLib* sceneMtls = ip->GetSceneMtls(); + for(int i = 0; i < sceneMtls->Count(); i++) + { + GetMtlsRecur((*sceneMtls)[i], mtls, texmaps, flags); + } + + // Add any more we find traversing the node hierarchy + INode *root = ip->GetRootNode(); + GetNodeMtlsRecur(root, mtls, texmaps, flags); + + if (!(flags & kUsedOnly)) + GetEditorMtls(mtls, texmaps, flags); +} + +void plMtlCollector::GetMtlLayers(Mtl *mtl, LayerSet& layers) +{ + TexSet tex; + GetMtlsRecur(mtl, nil, &tex, kPlasmaOnly); + + TexSet::iterator it = tex.begin(); + for (; it != tex.end(); it++) + { + layers.insert((plPlasmaMAXLayer*)*it); + } +} + +void plMtlCollector::GetAllTextures(TexNameSet& texNames) +{ + TexSet tex; + GetMtls(nil, &tex); + + PBSet pbs; + TexSet::iterator it = tex.begin(); + for (; it != tex.end(); it++) + GetTexmapPBs(*it, pbs); + + PBSet::iterator pbIt = pbs.begin(); + for (; pbIt != pbs.end(); pbIt++) + { + PBBitmap* pbbm = *pbIt; + texNames.insert(pbbm->bi.Name()); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMtlCollector.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMtlCollector.h new file mode 100644 index 00000000..ee29949b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plMtlCollector.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plMtlCollector_h_inc +#define plMtlCollector_h_inc + +#include "hsTypes.h" +#include +#include "hsSTLSortUtils.h" + +class Mtl; +class Texmap; +class plPlasmaMAXLayer; +class PBBitmap; + +typedef std::set MtlSet; +typedef std::set TexSet; +typedef std::set LayerSet; +typedef std::set PBSet; +typedef std::set TexNameSet; + +class plMtlCollector +{ +public: + enum + { + kUsedOnly = 0x1, + kPlasmaOnly = 0x2, + kNoMultiMtl = 0x4, + kClothingMtlOnly = 0x8, + kNoSubMtls = 0x10, + }; + + static void GetMtls(MtlSet* mtls, TexSet* texmaps, UInt32 flags=0); + + static void GetMtlLayers(Mtl *mtl, LayerSet& layers); + + // Warning: These pointers are only valid until you return control to Max + // (where a bitmap could be modified). Don't hang on to them! + static void GetAllTextures(TexNameSet& texNames); +}; + +#endif // plMtlCollector_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plNodeLock.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plNodeLock.cpp new file mode 100644 index 00000000..4ae7f5f6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plNodeLock.cpp @@ -0,0 +1,58 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" + +#include "plNodeLock.h" + +void plNodeLock::Lock(BOOL on) +{ + Interface *ip = GetCOREInterface(); + ISetLockRecur(ip->GetRootNode(), on); +} + +void plNodeLock::ISetLockRecur(INode* node, BOOL on) +{ + BOOL isSel = node->Selected(); + if( isSel ) + { + node->SetTransformLock(INODE_LOCKPOS, INODE_LOCK_X, on); + node->SetTransformLock(INODE_LOCKPOS, INODE_LOCK_Y, on); + node->SetTransformLock(INODE_LOCKPOS, INODE_LOCK_Z, on); + + node->SetTransformLock(INODE_LOCKROT, INODE_LOCK_X, on); + node->SetTransformLock(INODE_LOCKROT, INODE_LOCK_Y, on); + node->SetTransformLock(INODE_LOCKROT, INODE_LOCK_Z, on); + + node->SetTransformLock(INODE_LOCKSCL, INODE_LOCK_X, on); + node->SetTransformLock(INODE_LOCKSCL, INODE_LOCK_Y, on); + node->SetTransformLock(INODE_LOCKSCL, INODE_LOCK_Z, on); + } + int i; + for( i = 0; i < node->NumberOfChildren(); i++ ) + ISetLockRecur(node->GetChildNode(i), on); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plNodeLock.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plNodeLock.h new file mode 100644 index 00000000..7d3e1282 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plNodeLock.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plNodeLock_inc +#define plNodeLock_inc + +class plNodeLock +{ +protected: + void ISetLockRecur(INode* node, BOOL on); + +public: + plNodeLock() {} + ~plNodeLock() {} + + void Lock(BOOL on = true); // on = true locks, on = false unlocks + void Unlock() { Lock(false); } +}; + +#endif // plNodeLock_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysXCooking.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysXCooking.cpp new file mode 100644 index 00000000..ebd348e7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysXCooking.cpp @@ -0,0 +1,498 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPhysXCooking.h" +#include "hsGeometry3.h" +#include "../plPhysX/plSimulationMgr.h" +#include "../plPhysX/plPXStream.h" +#include "../plPhysX/plPXConvert.h" +#include "hsSTLStream.h" + +#include "Nx.h" +#include "NxStream.h" +#include "NxPhysics.h" +#include "NxCooking.h" +#include "NxPlane.h" +#include "NxUtilLib.h" +#include "NxMat33.h" +bool plPhysXCooking::fSkipErrors = false; + +NxUtilLib* plPhysXCooking::fUtilLib =nil; +//assumes that the Vectors are normalized +hsBool ThreePlaneIntersect(const NxVec3& norm0, const NxVec3& point0, + const NxVec3& norm1, const NxVec3& point1, + const NxVec3& norm2, const NxVec3& point2, NxVec3& loc) +{ + //need to make sure these planes aren't parallel + hsBool suc=0; + NxVec3 cross=norm1.cross( norm2); + hsScalar denom=norm0.dot(cross); + if(abs(denom)<0.0001) return 0;//basically paralell + // if we are here there must be a point in 3 space + try{ + hsScalar d1,d2,d3; + d1=norm0.dot(point0); + d2=norm1.dot(point1); + d3=norm2.dot(point2); + NxVec3 n1Xn2=norm1.cross(norm2); + NxVec3 n2Xn0=norm2.cross(norm0); + NxVec3 n0Xn1=norm0.cross(norm1); + NxVec3 pos=(d1*n1Xn2+ d2*n2Xn0 + d3*n0Xn1)/(denom); + loc.x=pos.x; + loc.y=pos.y; + loc.z=pos.z; + suc= 1; + } + catch(...) + { + suc=0; + } + + return suc; + +} + +void plPhysXCooking::Init() +{ + NxInitCooking(); + NxUtilLib* fUtilLib=NxGetUtilLib(); + NxCookingParams parms=NxGetCookingParams(); + parms.skinWidth=.05; + NxSetCookingParams(parms); + +} + + +void plPhysXCooking::Shutdown() +{ + NxCloseCooking(); + fUtilLib=nil; + fSkipErrors = false; +} + +hsVectorStream* plPhysXCooking::CookTrimesh(int nVerts, hsPoint3* verts, int nFaces, UInt16* faces) +{ + NxTriangleMeshDesc triDesc; + triDesc.numVertices = nVerts; + triDesc.pointStrideBytes = sizeof(hsPoint3); + triDesc.points = verts; + triDesc.numTriangles = nFaces; + triDesc.triangleStrideBytes = sizeof(UInt16) * 3; + triDesc.triangles = faces; + triDesc.flags = NX_MF_16_BIT_INDICES; + + hsVectorStream* ram = TRACKED_NEW hsVectorStream; + plPXStream buf(ram); + bool status = NxCookTriangleMesh(triDesc, buf); + hsAssert(status, "Trimesh failed to cook"); + + if (status) + { + ram->Rewind(); + return ram; + } + else + { + delete ram; + return nil; + } +} + +bool plPhysXCooking::IsPointInsideHull(hsPlane3* hull, int nPlanes, const hsPoint3& pos) +{ + int i; + for( i = 0; i < nPlanes; i++ ) + { + // add a fudge to the point so not to trip on the ever so slightly concave + // ... so pull the point out in the direction of the normal of the plane we are testing. + hsPoint3 fudgepos = pos + (hull[i].GetNormal()*0.005f); + if (!ITestPlane(fudgepos, hull[i])) + return false; + } + return true; +} + +bool plPhysXCooking::TestIfConvex(NxConvexMesh* convexMesh, int nVerts, hsPoint3* verts) +{ + bool retVal = true; + + // build planes from the convex mesh + NxConvexMeshDesc desc; + convexMesh->saveToDesc(desc); + + hsPlane3* planes = TRACKED_NEW hsPlane3[desc.numTriangles]; + + int i; + for ( i = 0; i < desc.numTriangles; i++) + { + UInt32* triangle = (UInt32*)(((char*)desc.triangles) + desc.triangleStrideBytes*i); + float* vertex1 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[0]); + float* vertex2 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[1]); + float* vertex3 = (float*)(((char*)desc.points) + desc.pointStrideBytes*triangle[2]); + hsPoint3 pt1(vertex1[0],vertex1[1],vertex1[2]); + hsPoint3 pt2(vertex2[0],vertex2[1],vertex2[2]); + hsPoint3 pt3(vertex3[0],vertex3[1],vertex3[2]); + + planes[i] = hsPlane3(&pt1,&pt2,&pt3); + } + // now see if any of the points from the mesh are inside the hull + for (int j=0; jRewind(); + return ram; + } + else + { + delete ram; + return nil; + } +} +/* +NxTriangleMesh* ReadExplicit(hsStream* stream) +{ + const int nVertices = stream->ReadSwap32(); + hsPoint3* pVertices = TRACKED_NEW hsPoint3[nVertices]; + stream->ReadSwapScalar(nVertices*3, (float*)pVertices); + + const int nFaces = stream->ReadSwap32(); + unsigned short* pTriangles = TRACKED_NEW unsigned short[nFaces * 3]; + stream->ReadSwap16(nFaces * 3, pTriangles); + + NxTriangleMeshDesc triDesc; + triDesc.numVertices = nVertices; + triDesc.pointStrideBytes = sizeof(hsPoint3); + triDesc.points = pVertices; + triDesc.numTriangles = nFaces; + triDesc.triangleStrideBytes = sizeof(UInt16) * 3; + triDesc.triangles = pTriangles; + triDesc.flags = NX_MF_16_BIT_INDICES;// | NX_MF_FLIPNORMALS; + + hsRAMStream ram; + plNxStream buf(&ram); + NxInitCooking(); + bool status = NxCookTriangleMesh(triDesc, buf); + hsAssert(status, "Trimesh failed to cook"); + NxCloseCooking(); + + delete[] pVertices; + delete[] pTriangles; + + if (status) + { + ram.Rewind(); + return plSimulationMgr::GetInstance()->GetSDK()->createTriangleMesh(buf); + } + + return nil; +} + +NxConvexMesh* ReadConvexHull(hsStream* stream) +{ + const int nVertices = stream->ReadSwap32(); + hsPoint3* pVertices = TRACKED_NEW hsPoint3[nVertices]; + stream->ReadSwapScalar(nVertices*3, (float*)pVertices); + + NxConvexMeshDesc convexDesc; + convexDesc.numVertices = nVertices; + convexDesc.pointStrideBytes = sizeof(hsPoint3); + convexDesc.points = pVertices; + convexDesc.flags = NX_CF_COMPUTE_CONVEX; + + hsRAMStream ram; + plNxStream buf(&ram); + NxInitCooking(); + bool status = NxCookConvexMesh(convexDesc, buf); + hsAssert(status, "Convex mesh failed to cook"); + NxCloseCooking(); + + delete[] pVertices; + + if (status) + { + ram.Rewind(); + return plSimulationMgr::GetInstance()->GetSDK()->createConvexMesh(buf); + } + + return nil; +} + +void ReadBoxFromHull(hsStream* stream, NxBoxShapeDesc& box) +{ + const int nVertices = stream->ReadSwap32(); + hsPoint3* pVertices = TRACKED_NEW hsPoint3[nVertices]; + stream->ReadSwapScalar(nVertices*3, (float*)pVertices); + + hsScalar minX, minY, minZ, maxX, maxY, maxZ; + minX = minY = minZ = FLT_MAX; + maxX = maxY = maxZ = -FLT_MAX; + for (int i = 0; i < nVertices; i++) + { + hsPoint3& vec = pVertices[i]; + minX = hsMinimum(minX, vec.fX); + minY = hsMinimum(minY, vec.fY); + minZ = hsMinimum(minZ, vec.fZ); + maxX = hsMaximum(maxX, vec.fX); + maxY = hsMaximum(maxY, vec.fY); + maxZ = hsMaximum(maxZ, vec.fZ); + } + + delete[] pVertices; + + float xWidth = maxX - minX; + float yWidth = maxY - minY; + float zWidth = maxZ - minZ; + box.dimensions.x = xWidth / 2; + box.dimensions.y = yWidth / 2; + box.dimensions.z = zWidth / 2; + +// hsMatrix44 mat; +// box.localPose.getRowMajor44(&mat.fMap[0][0]); +// hsPoint3 trans(minX + (xWidth / 2), minY + (yWidth / 2), minY + (yWidth / 2)); +// mat.SetTranslate(&trans); +// box.localPose.setRowMajor44(&mat.fMap[0][0]); +} +*/ +hsBool ProjectPointOnToPlane(const hsVector3& planeNormal,hsScalar& d0, + const hsVector3 pointToProject, hsPoint3& res) +{ + + NxVec3 vec=plPXConvert::Vector(planeNormal); + NxVec3 orig,projected; + orig=plPXConvert::Vector(pointToProject); + NxPlane* pl=new NxPlane(vec,d0); + projected=pl->project(orig); + res.fX=projected.x; + res.fY=projected.y; + res.fZ=projected.z; + return 1; +} +void plPhysXCooking::PCA(const NxVec3* points,int numPoints, NxMat33& out) +{ + NxVec3 mean(0.f,0.f,0.f); + hsScalar Cov[3][3]; + memset(Cov,0,9* sizeof hsScalar); + for(int i=0; i outCloud; + hsPoint3 offset; + int numPlanes=26; + float planeMax[26]; + int indexMax[26]; + hsPoint3 AABBMin(FLT_MAX,FLT_MAX,FLT_MAX); + hsPoint3 AABBMax(-FLT_MAX,-FLT_MAX,-FLT_MAX); + //prep + NxVec3* vectors = TRACKED_NEW NxVec3[26]; + + int curvec=0; + for(int xcomp= -1;xcomp<2;xcomp++) + { + for(int ycomp= -1;ycomp<2;ycomp++) + { + for(int zcomp= -1;zcomp<2;zcomp++) + { + if(!((xcomp==0)&&(ycomp==0)&&(zcomp==0))) + { + vectors[curvec].set((hsScalar)(xcomp),(hsScalar)(ycomp),(hsScalar)(zcomp)); + vectors[curvec].normalize(); + planeMax[curvec]=(-FLT_MAX); + //indexMax[curvec]=0; + curvec++; + } + } + } + } + /* + for(int i=0;i<26;i++) + {//make your max and mins + planeMax[i]=(-FLT_MAX); + } + */ + hsPoint3 centroid(0.0f,0.0f,0.0f); + for(int i=0;i=planeMax[plane]) + { + planeMax[plane]=dist; + indexMax[plane]=i; + } + } + } + for(int i=0;i0.0001) + + { + within=false; + + } + curplane++; + } + while((curplane<26)&&within); + if(within) +// if((res.x>=AABBMin.fX)&&(res.x<=AABBMax.fX)&& +// (res.y>=AABBMin.fY)&&(res.y<=AABBMax.fY)&& +// (res.z>=AABBMin.fZ)&&(res.z<=AABBMax.fZ)) + { + NxVec3 reverted; + reverted=rot*res; + reverted.x=reverted.x +centroid.fX; + reverted.y=reverted.y +centroid.fY; + reverted.z=reverted.z +centroid.fZ; + hsPoint3 out; + out=plPXConvert::Point(reverted); + outCloud.push_back(out); + } + } + } + } + } + + //planes discovered + //this is'nt right + //cleanup + offset=centroid; + + delete[] vectors; + hsPoint3* pointages=TRACKED_NEW hsPoint3[outCloud.size()]; + for(int x=0;x. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPhysXCooking_h_inc +#define plPhysXCooking_h_inc + +#include "hsTypes.h" +#include "hsGeometry3.h" +#include "plMaxMeshExtractor.h" +class hsStream; +class hsVectorStream; +class NxConvexMesh; +class NxUtilLib; +class NxVec3; +class NxMat33; + +class plPhysXCooking +{ +public: + static void Init(); + static void Shutdown(); + + static hsVectorStream* CookTrimesh(int nVerts, hsPoint3* verts, int nFaces, UInt16* faces); + static bool TestIfConvex(NxConvexMesh* convexMesh, int nVerts, hsPoint3* verts); + static hsVectorStream* CookHull(int nVerts, hsPoint3* verts,bool inflate); + static bool IsPointInsideHull(hsPlane3* hull, int nPlanes, const hsPoint3& pos); + static inline bool ITestPlane(const hsPoint3 &pos, const hsPlane3 &plane) + { + hsScalar dis = plane.fN.InnerProduct(pos); + dis += plane.fD; + if( dis > 0.f ) + return false; + + return true; + }; + static void PCA(const NxVec3* points,int numPoints, NxMat33& out); + static NxUtilLib* fUtilLib; + static bool fSkipErrors; + static hsVectorStream* IMakePolytope(const plMaxMeshExtractor::NeutralMesh& inMesh); +}; + +#endif // plPhysXCooking_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysicalProps.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysicalProps.cpp new file mode 100644 index 00000000..8b6a79a8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysicalProps.cpp @@ -0,0 +1,309 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plPhysicalProps.h" +#include "../plPhysical/plSimDefs.h" +// For plBoundsType +#include "../pnSceneObject/plSimulationInterface.h" + +#include "plMaxNode.h" +#include "../MaxExport/plErrorMsg.h" + +/** These enums are used to indicate which parameters are ignorable and can + be overridden. + We can't use the "PhysFlags," below, for this, because those only cover boolean properties. + *** We should use the same enum for both and just not worry that some of the enums aren't + usable as "ignore" flags, -- the readability & simplification gain would be worth it. */ +enum CanIgnore +{ + kMemberGroup = 0x1, + kBounceGroup = 0x2, + kReportGroup = 0x4, + kMass = 0x8, + kFriction = 0x10, + kRestitution = 0x20, + kBoundsType = 0x40, + kProxyNode = 0x80, + kPinned = 0x100, + kAlignToOwner = 0x200, + kCameraAvoid = 0x400, + kPhysAnim = 0x800, + kCanIgnoreLOSBlockCamera = 0x1000, + kCanIgnoreLOSBlockUI = 0x2000, + kCanIgnoreLOSUIItem = 0x4000, + kCanIgnoreLOSBlockCustom = 0x8000, + kCanIgnoreLOSShootable = 0x10000, + kCanIgnoreLOSAvatarWalkable = 0x20000, + kCanIgnoreLOSSwimRegion = 0x40000, + + kAll = 0xffffffff +}; + +enum PhysFlags +{ + kFlagPinned = 0x1, + kFlagAlignToOwner = 0x2, + kFlagCameraAvoid = 0x4, + kFlagPhysAnim = 0x8, + kFlagStartInactive = 0x10, + kFlagNoSynchronize = 0x20, + kFlagLOSBlockCamera = 0x40, + kFlagLOSBlockUI = 0x80, + kFlagLOSUIItem = 0x100, + kFlagLOSBlockCustom = 0x200, + kFlagLOSShootable = 0x400, + kFlagLOSAvatarWalkable = 0x800, + kFlagLOSSwimRegion = 0x1000, +}; + +plPhysicalProps::plPhysicalProps() : + fUsed(false), + fCanIgnore(kAll), + fGroup(0), + fReportGroup(0), + fMass(0), + fFriction(0), + fRestitution(0), + fBoundsType(plSimDefs::kHullBounds), + fProxyNode(nil), + fFlags(0), + fSubworld(nil), + fNoSynchronize(0), + fStartInactive(0), + fAvAnimPushable(0) +{ +} + +bool plPhysicalProps::SetGroup(UInt32 group, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetParam(fGroup, group, kMemberGroup, canIgnore, node, errMsg); +} + +bool plPhysicalProps::SetReportGroup(UInt32 notifyGroup, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetParam(fReportGroup, notifyGroup, kReportGroup, canIgnore, node, errMsg); +} + +bool plPhysicalProps::SetMass(float mass, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + if(mass != 0.0f) + { + if (!GetPinned()) + node->SetMovable(true); + node->SetForceLocal(true); + } + return ISetParam(fMass, mass, kMass, canIgnore, node, errMsg); +} + +bool plPhysicalProps::SetFriction(float friction, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetParam(fFriction, friction, kFriction, canIgnore, node, errMsg); +} + +bool plPhysicalProps::SetRestitution(float restitution, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetParam(fRestitution, restitution, kRestitution, canIgnore, node, errMsg); +} + +bool plPhysicalProps::SetBoundsType(int boundsType, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + hsAssert(boundsType >= 1 && boundsType < plSimDefs::kNumBounds, "Bad bounds type"); + return ISetParam(fBoundsType, boundsType, kBoundsType, canIgnore, node, errMsg); +} + +bool plPhysicalProps::SetProxyNode(plMaxNode *proxyNode, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + if( proxyNode ) + proxyNode->SetDrawable(false); + + return ISetParam(fProxyNode, proxyNode, kProxyNode, canIgnore, node, errMsg); +} + +bool plPhysicalProps::IGetFlagParam(int flagType) +{ + return ((fFlags & flagType) != 0); +} + +bool plPhysicalProps::ISetFlagParam(bool val, int flagType, int type, bool canIgnore, plMaxNode *node, plErrorMsg *errMsg) +{ + bool ourVal = IGetFlagParam(flagType); + if (ISetParam(ourVal, val, type, canIgnore, node, errMsg)) + { + if (ourVal) + fFlags |= flagType; + else + fFlags &= ~flagType; + return true; + } + + return false; +} + +bool plPhysicalProps::SetPinned(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetFlagParam(status, kFlagPinned, kPinned, canIgnore, node, errMsg); +} + +bool plPhysicalProps::SetLOSBlockCamera(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetFlagParam(status, kFlagLOSBlockCamera, kCanIgnoreLOSBlockCamera, canIgnore, node, errMsg); +} + +bool plPhysicalProps::GetLOSBlockCamera() +{ + return IGetFlagParam(kFlagLOSBlockCamera); +} + +bool plPhysicalProps::SetLOSBlockUI(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetFlagParam(status, kFlagLOSBlockUI, kCanIgnoreLOSBlockUI, canIgnore, node, errMsg); +} + +bool plPhysicalProps::GetLOSBlockUI() +{ + return IGetFlagParam(kFlagLOSBlockUI); +} + +bool plPhysicalProps::SetLOSUIItem(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetFlagParam(status, kFlagLOSUIItem, kCanIgnoreLOSUIItem, canIgnore, node, errMsg); +} + +bool plPhysicalProps::GetLOSUIItem() +{ + return IGetFlagParam(kFlagLOSUIItem); +} + +bool plPhysicalProps::SetLOSBlockCustom(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetFlagParam(status, kFlagLOSBlockCustom, kCanIgnoreLOSBlockCustom, canIgnore, node, errMsg); +} + +bool plPhysicalProps::GetLOSBlockCustom() +{ + return IGetFlagParam(kFlagLOSBlockCustom); +} + +bool plPhysicalProps::SetCameraAvoidFlag(bool allowLOS, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetFlagParam(allowLOS, kFlagCameraAvoid, kCameraAvoid, canIgnore, node, errMsg); +} + +bool plPhysicalProps::SetLOSShootable(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetFlagParam(status, kFlagLOSShootable, kCanIgnoreLOSShootable, canIgnore, node, errMsg); +} +bool plPhysicalProps::GetLOSShootable() +{ + return IGetFlagParam(kFlagLOSShootable); +} + +bool plPhysicalProps::SetLOSAvatarWalkable(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetFlagParam(status, kFlagLOSAvatarWalkable, kCanIgnoreLOSAvatarWalkable, canIgnore, node, errMsg); +} + +bool plPhysicalProps::GetLOSAvatarWalkable() +{ + return IGetFlagParam(kFlagLOSAvatarWalkable); +} + +bool plPhysicalProps::SetLOSSwimRegion(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetFlagParam(status, kFlagLOSSwimRegion, kCanIgnoreLOSSwimRegion, canIgnore, node, errMsg); +} + +bool plPhysicalProps::GetLOSSwimRegion() +{ + return IGetFlagParam(kFlagLOSSwimRegion); +} + +//bool plPhysicalProps::SetUILOSFlag(bool allowLOS, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +//{ +// return ISetFlagParam(allowLOS, kFlagUILOS, kUILOS, canIgnore, node, errMsg); +//} + +bool plPhysicalProps::SetAlignToOwner(bool alignToOwner, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetFlagParam(alignToOwner, kFlagAlignToOwner, kAlignToOwner, canIgnore, node, errMsg); +} + +bool plPhysicalProps::SetSubworld(plMaxNode* subworld) +{ + // Note that we do *not* set fUsed, because we don't (necessarily) want a physical for this node. + // We just want to remember that it is a subworld. + fSubworld = subworld; + return true; +} + +bool plPhysicalProps::SetPhysAnim(bool anim, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore) +{ + return ISetFlagParam(anim, kFlagPhysAnim, kPhysAnim, canIgnore, node, errMsg); +} + +bool plPhysicalProps::GetPinned() +{ + return IGetFlagParam(kFlagPinned); +} + +bool plPhysicalProps::GetCameraAvoid() +{ + return IGetFlagParam(kFlagCameraAvoid); +} + +bool plPhysicalProps::GetAlignToOwner() +{ + return IGetFlagParam(kFlagAlignToOwner); +} + +bool plPhysicalProps::GetPhysAnim() +{ + return IGetFlagParam(kFlagPhysAnim); +} + +void plPhysicalProps::SetCanIgnore(UInt32 type, bool canIgnore) +{ + if (canIgnore) + fCanIgnore |= type; + else + fCanIgnore &= ~type; +} + +bool plPhysicalProps::CanIgnore(UInt32 type) +{ + return ((fCanIgnore & type) != 0); +} + +void plPhysicalProps::IDisplayErrorMsg(plMaxNode *node, plErrorMsg *errMsg) +{ + if (!errMsg->IsBogus()) + { + errMsg->Set(true, + "Physics Conflict", + "The node \"%s\" has a conflict in its physical settings.\nMake sure the physical components on it are compatible.", + node->GetName()).Show(); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysicalProps.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysicalProps.h new file mode 100644 index 00000000..88d6f1f8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPhysicalProps.h @@ -0,0 +1,163 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPhysicalProps_h_inc +#define plPhysicalProps_h_inc + +#include "hsTypes.h" +#include "..\..\NucleusLib\pnKeyedObject\plKey.h" + +class plMaxNode; +class plErrorMsg; + +class plPhysicalProps +{ +public: + // + // Set canIgnore to true if it is OK for someone else's setting to override yours + // If any of the Set functions return false, there was a conflict and the export will be aborted. + // + bool SetGroup(UInt32 group, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + bool SetReportGroup(UInt32 notifyGroup, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + bool SetMass(float mass, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + bool SetFriction(float friction, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + bool SetRestitution(float restitution, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + // From plBoundsType + bool SetBoundsType(int boundsType, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + // An alternate node for the physical to be created from + bool SetProxyNode(plMaxNode *proxyNode, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + // If you're going to pin a node and set its mass greater than zero, do the pin first. + // That way it will not be flagged as movable. + bool SetPinned(bool pinned, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + /** Allow line-of-sight-checks to pass through this object. + Set to true if you don't want this object to block LOS probes. + Set to false if you do want this object to block LOS probes. */ + bool SetAlignToOwner(bool alignToOwner, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + + bool SetCameraAvoidFlag(bool allowLOS, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + +// bool SetAllowLOS(bool allowLOS, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); +// bool SetCameraLOSFlag(bool allowLOS, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); +// bool SetUILOSFlag(bool allowLOS, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + + // New LOS Types.... + bool SetLOSBlockCamera(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore = false); + bool SetLOSBlockUI(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore = false); + bool SetLOSUIItem(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore = false); + bool SetLOSBlockCustom(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore = false); + bool SetLOSShootable(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore= false); + bool SetLOSAvatarWalkable(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore= false); + bool SetLOSSwimRegion(bool status, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore = false); + + bool SetSubworld(plMaxNode* subworld); + bool SetPhysAnim(bool anim, plMaxNode *node, plErrorMsg *errMsg, bool canIgnore=false); + void SetStartInactive(int on) { fStartInactive = on; }; + void SetNoSynchronize(int on) { fNoSynchronize = on; }; + void SetAvAnimPushable(int on) { fAvAnimPushable = on; }; + + bool IsUsed() { return fUsed; } + + UInt32 GetGroup() { return fGroup; } + UInt32 GetReportGroup() { return fReportGroup; } + float GetMass() { return fMass; } + float GetFriction() { return fFriction; } + float GetRestitution() { return fRestitution; } + int GetBoundsType() { return fBoundsType; } + plMaxNode* GetProxyNode() { return fProxyNode; } + bool GetPinned(); + bool GetCameraAvoid(); + bool GetAlignToOwner(); + plMaxNode* GetSubworld() { return fSubworld; } + bool GetPhysAnim(); + + + // New LOS Types + bool GetLOSBlockCamera(); + bool GetLOSBlockUI(); + bool GetLOSUIItem(); + bool GetLOSBlockCustom(); + bool GetLOSShootable(); + bool GetLOSAvatarWalkable(); + bool GetLOSSwimRegion(); + + int GetStartInactive() { return fStartInactive; }; + int GetNoSynchronize() { return fNoSynchronize; }; + int GetAvAnimPushable() { return fAvAnimPushable; }; + +protected: + bool fUsed; + UInt32 fCanIgnore; + + UInt32 fGroup; + UInt32 fReportGroup; + float fMass; + float fFriction; + float fRestitution; + int fBoundsType; + plMaxNode *fProxyNode; + UInt32 fFlags; + plMaxNode* fSubworld; + int fStartInactive; + int fNoSynchronize; + int fAvAnimPushable; + + bool IGetFlagParam(int flagType); + bool ISetFlagParam(bool val, int flagType, int type, bool canIgnore, plMaxNode *node, plErrorMsg *errMsg); + + // Because VC++ sucks, this has to be inlined. + template bool ISetParam(T& ourVal, T& theirVal, int type, bool otherCanIgnore, plMaxNode *node, plErrorMsg *errMsg) + { + fUsed = true; + + if (ourVal != theirVal) + { + if (CanIgnore(type)) + { + ourVal = theirVal; + if (!otherCanIgnore) + SetCanIgnore(type, false); + } + else if (!otherCanIgnore) + { + IDisplayErrorMsg(node, errMsg); + fUsed = false; + return false; + } + } + + return true; + } + + void SetCanIgnore(UInt32 type, bool canIgnore); + bool CanIgnore(UInt32 type); + + void IDisplayErrorMsg(plMaxNode *node, plErrorMsg *errMsg); + + // Only plMaxNodeData can create these + plPhysicalProps(); + friend class plMaxNodeData; +}; + +#endif // plPhysicalProps_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPlasmaRefMsgs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPlasmaRefMsgs.h new file mode 100644 index 00000000..060a4205 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPlasmaRefMsgs.h @@ -0,0 +1,38 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// +// This message is used to notify the SceneWatcher of changes in the materials. +// There is a refmsg sent out when material parameters change, but for some +// reason it is not propogated to the node that the material is applied to. +// User refmsgs always make it through though. +// +#define REFMSG_USER_MAT REFMSG_USER+1 + + +#define REFMSG_USER_TARGET_ADD REFMSG_USER+2 +#define REFMSG_USER_TARGET_DELETE REFMSG_USER+3 + +#define REFMSG_USER_COMP_REF_CHANGED REFMSG_USER+4 \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPluginResManager.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPluginResManager.cpp new file mode 100644 index 00000000..5f41d80f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPluginResManager.cpp @@ -0,0 +1,525 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plPluginResManager.h" +#include "hsTypes.h" +#include "hsTemplates.h" +#include "../plResMgr/plRegistryNode.h" +#include "../plResMgr/plRegistryHelpers.h" +#include "../plResMgr/plVersion.h" +#include "../plResMgr/plResMgrSettings.h" +#include "../plScene/plSceneNode.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plKeyImp.h" +#include "../plAgeDescription/plAgeDescription.h" +#include "plgDispatch.h" + +// For our common object libs +#include "plCommonObjLib.h" +#include "../MaxComponent/plMiscComponents.h" + +plKey plPluginResManager::NameToLoc(const char* age, const char* page, Int32 sequenceNumber, hsBool itinerant) +{ + // Hack for now--always prefer paging out sceneNodes first + fPageOutHint = plSceneNode::Index(); + + // Get or create our page + plRegistryPageNode* pageNode = INameToPage(age, page, sequenceNumber, itinerant); + hsAssert(pageNode != nil, "No page returned from INameToPage(), shouldn't be possible"); + + // Go find the sceneNode now, since we know the page exists (go through our normal channels, though) + char keyName[256]; + sprintf(keyName, "%s_%s", age, page); + + plUoid nodeUoid(pageNode->GetPageInfo().GetLocation(), plSceneNode::Index(), keyName); + + plKey snKey = FindKey(nodeUoid); + if (snKey == nil) + { + // Not found, create a new one + plSceneNode *newSceneNode = TRACKED_NEW plSceneNode; + snKey = NewKey(keyName, newSceneNode, pageNode->GetPageInfo().GetLocation()); + + // Call init after it gets a key + newSceneNode->Init(); + + // Add to our list of exported nodes + fExportedNodes.Append(newSceneNode); + newSceneNode->GetKey()->RefObject(); + } + else + { + hsAssert(snKey->ObjectIsLoaded() != nil, "Somehow we still have the key for a sceneNode that hasn't been loaded."); + + // Force load, or attempt to at least + plSceneNode* node = plSceneNode::ConvertNoRef(snKey->VerifyLoaded()); + + // Add to our list if necessary + if (fExportedNodes.Find(node) == fExportedNodes.kMissingIndex) + { + fExportedNodes.Append(node); + node->GetKey()->RefObject(); + } + } + + // Return the key + return snKey; +} + +//// INameToPage ///////////////////////////////////////////////////////////// +// Given the age/chapter/page combo we all know and love, plus an optional +// seqNumber, returns the page for that combo (either by preloading it or +// by creating it). + +plRegistryPageNode* plPluginResManager::INameToPage(const char* age, const char* page, Int32 sequenceNumber, hsBool itinerant) +{ + // Find the location first, to see if it already exists + plRegistryPageNode* pageNode = FindPage(age, page); + if (pageNode == nil) + { + // This page does not yet exist, so create a new page + if (sequenceNumber != UInt32(-1)) + { + const plLocation& newLoc = ICreateLocation(age, page, sequenceNumber, itinerant); + pageNode = CreatePage(newLoc, age, page); + } + else + { + const plLocation& newLoc = ICreateLocation(age, page, itinerant); + pageNode = CreatePage(newLoc, age, page); + } + // Still preload textures on this guy. This should be a no-op for this page since it's new, but won't be + // for the shared textures page + IPreLoadTextures(pageNode, sequenceNumber); + } + else if (!pageNode->IsNewPage()) + { + // Node's already in our registry (i.e. already stored somewhere), so make sure it loads so + // we can update from that + LoadPageKeys(pageNode); + + // Now clear out all the unwanted keys. + IPreLoadTextures(pageNode, sequenceNumber); + } + + return pageNode; +} + +//// plCommonKeyDistributor ////////////////////////////////////////////////// +// Iterator that takes the keys in a common page and distributes them to +// whichever commonObjectLibs are interested in them. + +class plCommonKeyDistributor : public plRegistryKeyIterator +{ + plPluginResManager* fMgr; + +public: + plCommonKeyDistributor(plPluginResManager* mgr) : fMgr(mgr) {} + + virtual hsBool EatKey(const plKey& key) + { + UInt32 count = plCommonObjLib::GetNumLibs(); + + for (UInt32 i = 0; i < count; i++) + { + plCommonObjLib* lib = plCommonObjLib::GetLib(i); + if (lib->IsInteresting(key)) + { + // Lib wants this guy, so load the object and add it + // Note: we want to switch to passive mode here, so any keys read in + // will NOT force a load on whichever page those keys belong to. Otherwise, + // we'd have to load that entire page and ref all the objects all over + // again and I just really don't want to do that... + plResMgrSettings::Get().SetPassiveKeyRead(true); + hsKeyedObject* object = key->VerifyLoaded(); + plResMgrSettings::Get().SetPassiveKeyRead(false); + + lib->AddObject(object); + break; + } + } + + return true; + } +}; + +//// IPreLoadTextures //////////////////////////////////////////////////////// +// Given a page of loaded keys, culls through them and make sure all our +// registered commonObjectLibs get them as if we had just converted them. +// Note: Broken out in a separate function 5.31.2002 mcn to facilitate +// pre-loading textures exported to our special textures page. + +void plPluginResManager::IPreLoadTextures(plRegistryPageNode* pageNode, Int32 origSeqNumber) +{ + // For common pages, we want to kinda-maybe-load all the objects so they don't get wiped when we + // re-export them. However, we don't have a good way of telling whether a page is a common page, + // which is where this hack comes in + bool common = false; + for (int i = 0; i < plAgeDescription::kNumCommonPages; i++) + { + if (hsStrCaseEQ(plAgeDescription::GetCommonPage(i), pageNode->GetPageInfo().GetPage())) + { + common = true; + break; + } + } + + if (common) + { + // Iterate through all the keys in our page, scattering them to various objectLibs if they're + // interested. If nobody likes them, they get unreffed and disappear. + plCommonKeyDistributor distributor(this); + pageNode->IterateKeys(&distributor); + } + + // Clear out all the unwanted keys in the page we just loaded (by implication; they won't clear if we already + // stored the keys via our objectLibs above) + { + class plEmptyIterator : public plRegistryKeyIterator + { + public: + virtual hsBool EatKey(const plKey& key) { return true; } + } empty; + + pageNode->IterateKeys(&empty); + } + + // We've loaded anything we needed from this page now, so set it as new so + // that we won't try loading again + pageNode->SetNewPage(); + + // Get our texture page now, if we're not a texture page + if (!common) + { + // Make sure it's not a global page we're handling either + if (!pageNode->GetPageInfo().GetLocation().IsReserved()) + { + Int32 texSeqNum = -1; + if (origSeqNumber != -1) + texSeqNum = plPageInfoUtils::GetCommonSeqNumFromNormal(origSeqNumber, plAgeDescription::kTextures); + + // Note: INameToPage will turn around and call us again, so no need to do the call twice + plRegistryPageNode* texturePage = INameToPage(pageNode->GetPageInfo().GetAge(), + plAgeDescription::GetCommonPage(plAgeDescription::kTextures), texSeqNum); + hsAssert(texturePage != nil, "Unable to get or create the shared textures page? Shouldn't be possible."); + + // Do the other one + Int32 commonSeqNum = -1; + if (origSeqNumber != -1) + commonSeqNum = plPageInfoUtils::GetCommonSeqNumFromNormal(origSeqNumber, plAgeDescription::kGlobal); + + // Note: INameToPage will turn around and call us again, so no need to do the call twice + plRegistryPageNode* commonPage = INameToPage(pageNode->GetPageInfo().GetAge(), + plAgeDescription::GetCommonPage(plAgeDescription::kGlobal), + commonSeqNum); + hsAssert(commonPage != nil, "Unable to get or create the shared built-in page? Shouldn't be possible."); + } + } +} + +//// GetCommonPage /////////////////////////////////////////////////////////// +// Given a plLocation, finds the texture page that's in the same age and +// returns its location. + +const plLocation& plPluginResManager::GetCommonPage(const plLocation &sisterPage, int whichPage) +{ + if (sisterPage.IsReserved()) + return sisterPage; // Reserved pages have no common pages + + plRegistryPageNode* page = FindPage(sisterPage); + if (page == nil) + { + hsAssert(false, "Trying to find the sister common page to a page that doesn't exist!"); + return sisterPage; + } + + // Find the common page in the same age as this one + plRegistryPageNode* commonPage = FindPage(page->GetPageInfo().GetAge(), + plAgeDescription::GetCommonPage(whichPage)); + if (commonPage == nil) + { + hsAssert(false, "Unable to find sister common page to this page"); + return sisterPage; + } + + return commonPage->GetPageInfo().GetLocation(); +} + +//// IShutdown /////////////////////////////////////////////////////////////// + +void plPluginResManager::IShutdown() +{ + if (!fInited) + return; + + // Loop through all the commonObjLibs and clear their object lists, just + // as a safety measure (the creators of the various libs should really + // be doing it) + for (UInt32 i = 0; i < plCommonObjLib::GetNumLibs(); i++) + plCommonObjLib::GetLib(i)->ClearObjectList(); + + plResManager::IShutdown(); +} + +// Little finder class to, erm, find unused location sequence numbers +class plSeqNumberFinder : public plRegistryPageIterator +{ +protected: + Int32& fSeqNum; + hsBool fWillBeReserved; + +public: + plSeqNumberFinder(Int32& seqNum, hsBool willBeReserved) : fSeqNum(seqNum), fWillBeReserved(willBeReserved) {} + + virtual hsBool EatPage(plRegistryPageNode* page) + { + if (fSeqNum <= page->GetPageInfo().GetLocation().GetSequenceNumber() && + fWillBeReserved == page->GetPageInfo().GetLocation().IsReserved()) + fSeqNum = page->GetPageInfo().GetLocation().GetSequenceNumber() + 1; + return true; + } +}; + + +plLocation plPluginResManager::ICreateLocation(const char* age, const char* page, hsBool itinerant) +{ + Int32 seqNum = VerifySeqNumber(0, age, page); + return ICreateLocation(age, page, seqNum, itinerant); +} + +plLocation plPluginResManager::ICreateLocation(const char* age, const char* page, Int32 seqNum, hsBool itinerant) +{ + hsBool willBeReserved = hsStrCaseEQ(age, "global"); + + Int32 oldNum = seqNum; + seqNum = VerifySeqNumber(seqNum, age, page); + if (seqNum != oldNum) + { + hsAssert(false, "Conflicting page sequence number. Somebody called NameToLoc without verifying their seq# first!"); + } + + if (seqNum < 0) + { + willBeReserved = true; + seqNum = -seqNum; + } + + plLocation newLoc; + if (willBeReserved) + newLoc = plLocation::MakeReserved(seqNum); + else + newLoc = plLocation::MakeNormal(seqNum); + + // Flag common pages + for (int i = 0; i < plAgeDescription::kNumCommonPages; i++) + { + if (hsStrEQ(plAgeDescription::GetCommonPage(i), page)) + { + newLoc.SetFlags(plLocation::kBuiltIn); + break; + } + } + + // If we have an age description file for the age we're creating a location + // for, grab some extra flags from it + plAgeDescription* ageDesc = plPageInfoUtils::GetAgeDesc(age); + plAgePage* agePage = ageDesc ? ageDesc->FindPage(page) : nil; + if (agePage) + { + if (agePage->GetFlags() & plAgePage::kIsLocalOnly) + newLoc.SetFlags(plLocation::kLocalOnly); + + if (agePage->GetFlags() & plAgePage::kIsVolatile) + newLoc.SetFlags(plLocation::kVolatile); + } + if (itinerant) + newLoc.SetFlags(plLocation::kItinerant); + + delete ageDesc; + return newLoc; +} + +class plWritePageIterator : public plRegistryPageIterator +{ +public: + plWritePageIterator() {} + virtual hsBool EatPage(plRegistryPageNode *page) + { + if (page->GetPageInfo().GetLocation() != plLocation::kGlobalFixedLoc) + page->Write(); + return true; + } +}; + +void plPluginResManager::WriteAllPages() +{ + plWritePageIterator iter; + IteratePages(&iter); +} + +//// EndExport /////////////////////////////////////////////////////////////// +// Called after export is done and pages are all written out. Cleans up +// by paging out all the sceneNodes we just created. +void plPluginResManager::EndExport() +{ + for (int i = 0; i < fExportedNodes.GetCount(); i++) + { + if (fExportedNodes[i] != nil) + fExportedNodes[i]->GetKey()->UnRefObject(); + } + fExportedNodes.Reset(); + + for( i = 0; i < fLooseEnds.GetCount(); i++ ) + { + if( fLooseEnds[i] ) + fLooseEnds[i]->UnRefObject(); + } + fLooseEnds.Reset(); + // Flush the message queue, so all the messages for paging out stuff actually get delivered + plgDispatch::Dispatch()->MsgQueueProcess(); +} + +void plPluginResManager::AddLooseEnd(plKey key) +{ + if( key ) + { + key->RefObject(); + fLooseEnds.Append(key); + } +} +// Verifies that the given sequence number belongs to the given string combo and ONLY that combo. Returns a new, unique sequenceNumber if not +Int32 plPluginResManager::VerifySeqNumber(Int32 sequenceNumber, const char* age, const char* page) +{ + hsBool negated = false, willBeReserved = hsStrCaseEQ(age, "global"); + if (sequenceNumber < 0) + { + sequenceNumber = -sequenceNumber; + willBeReserved = negated = true; + } + + fLastVerifyError = kNoVerifyError; + fLastVerifyPage = nil; + + plLocation toCompareTo; + if (willBeReserved) + plLocation::MakeReserved(sequenceNumber); + else + plLocation::MakeNormal(sequenceNumber); + + // Does the page already exist? + plRegistryPageNode* pageNode = FindPage(age, page); + if (pageNode != nil) + { + if (pageNode->GetPageInfo().GetLocation() == toCompareTo) + // Right page, right sequence #. Assume we're smart enough to already have it right + return negated ? -sequenceNumber : sequenceNumber; + + // Right page, wrong seq #...tag our last error field so we can know this later on + fLastVerifyError = kErrRightPageWrongSeq; + } + + // Page doesn't yet exist, check to make sure the seq # isn't used yet + if (sequenceNumber > 0) + { + pageNode = FindPage(toCompareTo); + if (pageNode == nil) + // Safe to use + return negated ? -sequenceNumber : sequenceNumber; + else + { + // If there is no error yet, set the error to "already taken" + if (fLastVerifyError == kNoVerifyError) + fLastVerifyError = kErrSeqAlreadyTaken; + + fLastVerifyPage = &pageNode->GetPageInfo(); + } + } + + // Gotta find a good sequence number to use, so keep searching until we find one + // (but start at a good high number so we won't hopefully ever run into anybody else) + const int kTemporarySequenceStartPrefix = 100; // can't be larger then 0xFE, so well start out at 100 for kicks + sequenceNumber = plPageInfoUtils::CombineSeqNum(kTemporarySequenceStartPrefix, 0); + + Int32 upperLimit = 0xFEFFFF; // largest legal sequence number is a prefix of FE and a suffix of FFFF + for(; sequenceNumber < upperLimit; sequenceNumber++) + { + if (willBeReserved) + toCompareTo = plLocation::MakeReserved(sequenceNumber); + else + toCompareTo = plLocation::MakeNormal(sequenceNumber); + + pageNode = FindPage(toCompareTo); + if (pageNode == nil) + return negated ? -sequenceNumber : sequenceNumber; + } + + hsAssert(false, "Unable to find a valid sequence number to use"); + fLastVerifyError = kErrCantFindValid; + return 0; +} + +//// NukeKeyAndObject //////////////////////////////////////////////////////// +// Given a key, will ref and unref the associated object so it goes away, +// then nukes the key and sets it to nil. The key's UOID should be safe to +// reuse at that point, unless someone else still has a ref, in which case +// this function returns false (returns true if successful). + +bool plPluginResManager::NukeKeyAndObject(plKey& objectKey) +{ + class plPublicRefKey : public plKeyImp + { + public: + UInt16 GetRefCount() const { return fRefCount; } + }; + + plKeyImp* keyData = (plKeyImp*)objectKey; + + // Check the ref count on the object. Nobody should have a ref to it + // except the key + hsKeyedObject* object = objectKey->ObjectIsLoaded(); + if (object != nil) + { + if (keyData->GetActiveRefs()) + // Somebody still has a ref to this object, so we can't nuke it + return false; + } + + // Nobody has a ref to the object, so we're clear to nuke + keyData->SetObjectPtr(nil); + + // Check the key. The refcount should be 1 at this point, for the copy + // we're holding in this function. Nobody else should be holding the key + // now that the object is gone. + if (((plPublicRefKey*)keyData)->GetRefCount() > 1) + return false; + + // Nuke out the key as well + objectKey = nil; + + // All done! + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPluginResManager.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPluginResManager.h new file mode 100644 index 00000000..d3b3ed75 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPluginResManager.h @@ -0,0 +1,98 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plPluginResManager_h_inc +#define plPluginResManager_h_inc + +#include "../plResMgr/plResManager.h" +#include "../pnKeyedObject/plKey.h" +#include "hsTemplates.h" + +class plPageInfo; +class plRegistryPageNode; +class plSceneNode; + +class plPluginResManager : public plResManager +{ +public: + static plPluginResManager* ResMgr() { return (plPluginResManager*)hsgResMgr::ResMgr(); } + + //------------------------ + // Location management + //------------------------ + + // Given a page string combo, returns the key of the sceneNode for that page. If the page does not exist, it creates one. + plKey NameToLoc(const char* age, const char* page, Int32 sequenceNumber, hsBool itinerant = false); + + // Verifies that the given sequence number belongs to the given string combo and ONLY that combo. Returns a new, unique sequenceNumber if not + Int32 VerifySeqNumber(Int32 sequenceNumber, const char* age, const char* page); + + enum VerifyErrors + { + kNoVerifyError, + kErrRightPageWrongSeq, + kErrSeqAlreadyTaken, + kErrCantFindValid + }; + VerifyErrors GetLastVerifyError() const { return fLastVerifyError; } + const plPageInfo* GetLastVerifyPage() const { return fLastVerifyPage; } + + // Write all pages. Duh. + void WriteAllPages(); + + // Given a location, returns the plLocation corresponding to the common page from the same age + const plLocation& GetCommonPage(const plLocation& sisterPage, int whichPage); + + // If we have the key of an object that needs to get written out, but we're too lazy to set up + // a proper reference too (e.g. adding it to a scenenode), we can add it here. On add, it will be RefObject()'d, + // and then after we've written out everything, it will be UnRefObject()'d (at the same time we UnRef all + // our scenenodes in EndExport. + void AddLooseEnd(plKey key); + + // Given a key, will ref and unref the associated object so it goes away, then nukes the key and sets it to nil. + // The key's UOID should be safe to reuse at that point, unless someone else still has a ref, in which case + // this function returns false (returns true if successful). + bool NukeKeyAndObject(plKey& objectKey); + + // Flushes all the created scene nodes out + void EndExport(); + +protected: + plLocation ICreateLocation(const char* age, const char* page, hsBool itinerant); + plLocation ICreateLocation(const char* age, const char* page, Int32 seqNum, hsBool itinerant); + + plRegistryPageNode* INameToPage(const char* age, const char* page, Int32 sequenceNumber, hsBool itinerant = false); + + void IPreLoadTextures(plRegistryPageNode* pageNode, Int32 origSeqNumber); + + void IShutdown(); + + VerifyErrors fLastVerifyError; + const plPageInfo* fLastVerifyPage; + hsTArray fExportedNodes; + hsTArray fLooseEnds; +}; + +#endif // plPluginResManager_h_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPythonMgr.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPythonMgr.cpp new file mode 100644 index 00000000..bfcf94b5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPythonMgr.cpp @@ -0,0 +1,634 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plPythonMgr.h" + +#include "../MaxComponent/plAutoUIBlock.h" +//#include "Python.h" +#include "plMaxCFGFile.h" +#include "../plFile/hsFiles.h" + +#include "plgDispatch.h" +#include "../pfPython/cyPythonInterface.h" +#include "hsUtils.h" + +plPythonMgr::plPythonMgr() +{ +} + +plPythonMgr& plPythonMgr::Instance() +{ + static plPythonMgr theInstance; + return theInstance; +} + +// Python wants char, not const char, LAME! +static char* kGetBlockID = "glue_getBlockID"; +static char* kGetClassName = "glue_getClassName"; +static char* kGetNumParams = "glue_getNumParams"; +static char* kGetParam = "glue_getParam"; +static char* kGetVersion = "glue_getVersion"; +static char* kIsMultiModifier = "glue_isMultiModifier"; +static char* kGetVisInfo = "glue_getVisInfo"; + +bool ICallVoidFunc(PyObject *dict, char *funcName, PyObject*& val) +{ + PyObject *func = PyDict_GetItemString(dict, (char*)funcName); + if (func ) + { + if (PyCallable_Check(func)) + { + val = PyObject_CallFunction(func, NULL); + if (val) + { + // there might have been some message printed, so get it out to the log file + PythonInterface::getOutputAndReset(); + return true; + } + + // There was an error when calling the function + // get the error message + PyErr_Print(); + PyErr_Clear(); + PythonInterface::getOutputAndReset(); + } + } + + return false; +} + +bool ICallIntFunc(PyObject *dict, char *funcName, int& val) +{ + PyObject *obj; + if (ICallVoidFunc(dict, funcName, obj)) + { + if (PyInt_Check(obj)) + { + val = PyInt_AsLong(obj); + Py_DECREF(obj); + return true; + } + } + return false; +} + +bool ICallStrFunc(PyObject *dict, char *funcName, char*& val) +{ + PyObject *obj; + if (ICallVoidFunc(dict, funcName, obj)) + { + if (PyString_Check(obj)) + { + val = hsStrcpy(PyString_AsString(obj)); + Py_DECREF(obj); + return true; + } + } + + return false; +} + + +#include "../MaxComponent/plPythonFileComponent.h" +#include "hsUtils.h" + +enum ParamTypes +{ + // These numbers used in the python/plasma/glue.py code + kTypeUndefined, // 0 + kTypeBool, // 1 + kTypeInt, // 2 + kTypeFloat, // 3 + kTypeString, // 4 + kTypeSceneObj, // 5 + kTypeSceneObjList, // 6 + kTypeActivatorList, // 7 + kTypeActivator, // 8 + kTypeResponder, // 9 + kTypeResponderList, // 10 + kTypeDynamicText, // 11 + kTypeGUIDialog, // 12 + kTypeExcludeRegion, // 13 (x-rude-oh legion-oh) + kTypeAnimation, // 14 (animation component) + kTypeAvatarBehavior, // 15 (avatar behaviors, such as one-shots or multistage behaviors) + kTypeMaterial, // 16 (material type) + kTypeGUIPopUpMenu, // 17 (GUI pop up menu) + kTypeGUISkin, // 18 (Guess) + kTypeWaterComponent, // 19 + kTypeDropDownList, // 20 + kTypeSwimCurrentInterface, // 21 + kTypeClusterComponent, // 22 + kTypeMaterialAnimation, // 23 + kTypeGrassComponent, // 24 +}; + +bool IGetTupleInt(PyObject *tuple, int pos, int& val) +{ + PyObject *param = PyTuple_GetItem(tuple, pos); + if (param && PyInt_Check(param)) + { + val = PyInt_AsLong(param); + return true; + } + + return false; +} + +bool IGetTupleFloat(PyObject *tuple, int pos, float& val) +{ + PyObject *param = PyTuple_GetItem(tuple, pos); + if (param && PyFloat_Check(param)) + { + val = (float)PyFloat_AsDouble(param); + return true; + } + + return false; +} + +bool IGetTupleString(PyObject *tuple, int pos, char*& val) +{ + PyObject *param = PyTuple_GetItem(tuple, pos); + if (param && PyString_Check(param)) + { + val = PyString_AsString(param); + return true; + } + + return false; +} + +void IExtractVisInfo(PyObject* tuple, int* id, std::vector* vec) +{ + PyObject* vid = PyTuple_GetItem(tuple, 0); + PyObject* vstates = PyTuple_GetItem(tuple, 1); + + if (vid && PyInt_Check(vid)) + { + *id = PyInt_AsLong(vid); + } + + if (vstates && PyList_Check(vstates)) + { + PyObject* element; + int lsize = PyList_Size(vstates); + + for (int i = 0; i < lsize; i++) + { + element = PyList_GetItem(vstates, i); + if (element && PyString_Check(element)) + { + std::string str = PyString_AsString(element); + vec->push_back(str); + } + } + } +} + +bool plPythonMgr::IQueryPythonFile(char *fileName) +{ + PyObject *module = PyImport_ImportModule(fileName); + if (module) + { + // attach the glue python code to the end + if ( !PythonInterface::RunString("execfile('.\\python\\plasma\\glue.py')", module) ) + { + // display any output (NOTE: this would be disabled in production) + // get the messages + PythonInterface::getOutputAndReset(); + return false; // if we can't create the instance then there is nothing to do here + } + // Get the dictionary for this module + PyObject *dict = PyModule_GetDict(module); + // set the name of the file for the glue.py code to find + PyObject* pfilename = PyString_FromString(fileName); + PyDict_SetItemString(dict, "glue_name", pfilename); + + // Get the block ID + int blockID = 0; + if (!ICallIntFunc(dict, kGetBlockID, blockID)) + { + Py_DECREF(module); + return false; + } + + // Get the class name + char *className = nil; + if (!ICallStrFunc(dict, kGetClassName, className)) + { + Py_DECREF(module); + return false; + } + + // Get the number of parameters + int numParams = 0; + if (!ICallIntFunc(dict, kGetNumParams, numParams)) + { + Py_DECREF(module); + return false; + } + + // determine if this is a multimodifier + int isMulti = 0; + ICallIntFunc(dict, kIsMultiModifier, isMulti); + + // Get the version + //====================== + // Get version must be the last call that needs a pythonfile class instance + // ... because it delete the instance afterwards + // NOTE: get attribute params doesn't need the pythonfile class instance + int version = 0; + if (!ICallIntFunc(dict, kGetVersion, version)) + { + Py_DECREF(module); + return false; + } + + PyObject *getParamFunc = PyDict_GetItemString(dict, kGetParam); + PyObject *getVisInfoFunc = PyDict_GetItemString(dict, kGetVisInfo); + + if (PyCallable_Check(getParamFunc)) + { + plAutoUIBlock *autoUI = TRACKED_NEW plAutoUIBlock(PythonFile::GetClassDesc(), blockID, className, version); + // test to see if it is a multi-modifier type class + if (isMulti) + autoUI->SetMultiModifierFlag(true); + + for (int i = numParams-1; i >= 0; i--) + { + PyObject *ret = PyObject_CallFunction(getParamFunc, "l", i); + + PyObject *visinfo = nil; + int ddlParamID = -1; + std::vector vec; + + if (PyCallable_Check(getVisInfoFunc)) + { + visinfo = PyObject_CallFunction(getVisInfoFunc, "l", i); + if (visinfo && PyTuple_Check(visinfo)) + { + IExtractVisInfo(visinfo, &ddlParamID, &vec); + } + } + + if (ret) + { + if (PyTuple_Check(ret)) + { + int paramID = -1; + char *paramName = nil; + int paramType = kTypeUndefined; + + // Get the param ID, name, and type + if (IGetTupleInt(ret, 0, paramID) && + IGetTupleString(ret, 1, paramName) && + IGetTupleInt(ret, 2, paramType)) + { + // Get the type specific params and add the param to the AutoUI block + switch (paramType) + { + case kTypeInt: + IAddInt(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeFloat: + IAddFloat(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeString: + IAddString(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeBool: + IAddBool(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeSceneObj: + IAddSceneObj(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeSceneObjList: + IAddSceneObjList(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeActivator: + IAddActivator(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeActivatorList: + IAddActivatorList(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeResponder: + IAddResponder(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeResponderList: + IAddResponderList(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeDynamicText: + IAddDynamicText(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeGUIDialog: + IAddGUIDialog(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeExcludeRegion: + IAddExcludeRegion(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeAnimation: + IAddAnimation(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeAvatarBehavior: + IAddBehavior(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeMaterial: + IAddMaterial(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeGUIPopUpMenu: + IAddGUIPopUpMenu(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + + case kTypeGUISkin: + IAddGUISkin(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + case kTypeWaterComponent: + IAddWaterComponent(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + case kTypeSwimCurrentInterface: + IAddSwimCurrentInterface(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + case kTypeDropDownList: + IAddDropDownList(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + case kTypeClusterComponent: + IAddClusterComponent(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + case kTypeMaterialAnimation: + IAddMaterialAnimation(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + case kTypeGrassComponent: + IAddGrassComponent(autoUI, ret, paramName, paramID, ddlParamID, &vec); + break; + } + } + } + + Py_DECREF(ret); + } + } + + PythonFile::AddAutoUIBlock(autoUI); + } + + delete [] className; + Py_DECREF(module); + } + else + { + // There was an error when importing the module + // get the error message and put it in the log + PyErr_Print(); + PyErr_Clear(); + PythonInterface::getOutputAndReset(); + } + + + return false; +} + +void plPythonMgr::IAddBool(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + hsBool def = false; + IGetTupleInt(tuple, 3, def); + + autoUI->AddCheckBox(id, nil, paramName, vid, vstates, def); +} + +void plPythonMgr::IAddInt(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + int def=0, min=0, max=100; + + IGetTupleInt(tuple, 3, def); + + PyObject *range = PyTuple_GetItem(tuple, 4); + if (range && PyTuple_Check(range)) + { + IGetTupleInt(range, 0, min); + IGetTupleInt(range, 1, max); + } + + autoUI->AddIntSpinner(id, nil, paramName, vid, vstates, def, min, max); +} + +void plPythonMgr::IAddFloat(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + float def=0, min=0, max=1; + + IGetTupleFloat(tuple, 3, def); + + PyObject *range = PyTuple_GetItem(tuple, 4); + if (range && PyTuple_Check(range)) + { + IGetTupleFloat(range, 0, min); + IGetTupleFloat(range, 1, max); + } + + autoUI->AddFloatSpinner(id, nil, paramName, vid, vstates, def, min, max); +} + +void plPythonMgr::IAddString(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + char *def = nil; + IGetTupleString(tuple, 3, def); + + autoUI->AddEditBox(id, nil, paramName, vid, vstates, def); +} + +void plPythonMgr::IAddSceneObj(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickNodeButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddSceneObjList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickNodeList(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddActivator(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickActivatorButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddActivatorList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickActivatorList(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddDynamicText(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickDynamicTextButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddGUIDialog(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickGUIDialogButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddExcludeRegion(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickExcludeRegionButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddWaterComponent(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickWaterComponentButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddSwimCurrentInterface(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickSwimCurrentInterfaceButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddClusterComponent(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickClusterComponentButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddAnimation(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickAnimationButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddBehavior(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickBehaviorButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddMaterial(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickMaterialButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddGUIPopUpMenu(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickGUIPopUpMenuButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddGUISkin(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickGUISkinButton(id, nil, paramName, vid, vstates); +} + +#include "../MaxComponent/plResponderComponent.h" + +void plPythonMgr::IAddResponder(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + std::vector cids; + cids.push_back(RESPONDER_CID); + + autoUI->AddPickComponentButton(id, nil, paramName, vid, vstates, &cids, true); +} + +void plPythonMgr::IAddResponderList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + std::vector cids; + cids.push_back(RESPONDER_CID); + + autoUI->AddPickComponentList(id, nil, paramName, vid, vstates, &cids); +} + +void plPythonMgr::IAddMaterialAnimation(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickMaterialAnimationButton(id, nil, paramName, vid, vstates); +} + +void plPythonMgr::IAddGrassComponent(plAutoUIBlock *autoUI, PyObject *objTuple, std::string paramName, int id, int vid, std::vector* vstates) +{ + autoUI->AddPickGrassComponentButton(id, nil, paramName.c_str(), vid, vstates); +} + +#include + +void plPythonMgr::LoadPythonFiles() +{ + const char *clientPath = plMaxConfig::GetClientPath(false, true); + if (clientPath) + { + char oldCwd[MAX_PATH]; + _getcwd(oldCwd, sizeof(oldCwd)); + _chdir(clientPath); + + // Get the path to the Python subdirectory of the client + char pythonPath[MAX_PATH]; + strcpy(pythonPath, clientPath); + strcat(pythonPath, "Python"); + + PythonInterface::initPython(); + + // Iterate through all the Python files in the folder + hsFolderIterator folder(pythonPath); + while (folder.NextFileSuffix(".py")) + { + // Get the filename without the ".py" (module name) + const char *fullFileName = folder.GetFileName(); + char fileName[_MAX_FNAME]; + _splitpath(fullFileName, NULL, NULL, fileName, NULL); + + IQueryPythonFile(fileName); + } + + PythonInterface::finiPython(); + + _chdir(oldCwd); + } +} + +void plPythonMgr::IAddDropDownList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates) +{ + PyObject *options = PyTuple_GetItem(tuple, 3); + if (options && PyTuple_Check(options)) + { + int size = PyTuple_Size(options); + char* opt = nil; + std::vector optionsVec; + + for (int i = 0; i < size; i++) + { + IGetTupleString(options, i, opt); + std::string str = opt; + optionsVec.push_back(str); + } + + autoUI->AddDropDownList(id, nil, paramName, vid, vstates, &optionsVec); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPythonMgr.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPythonMgr.h new file mode 100644 index 00000000..24250c55 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plPythonMgr.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "max.h" +#include "python.h" +#include + +class plAutoUIBlock; + +class plPythonMgr +{ +protected: + plPythonMgr(); + + bool IQueryPythonFile(char *fileName); + + void IAddBool(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddInt(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddFloat(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddString(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddSceneObj(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddSceneObjList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddActivator(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddActivatorList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddResponder(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddResponderList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddDynamicText(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddGUIDialog(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddExcludeRegion(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddAnimation(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddBehavior(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddMaterial(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddGUIPopUpMenu(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddGUISkin(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddWaterComponent(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddSwimCurrentInterface(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddDropDownList(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddClusterComponent(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddMaterialAnimation(plAutoUIBlock *autoUI, PyObject *tuple, char *paramName, int id, int vid, std::vector* vstates); + void IAddGrassComponent(plAutoUIBlock *autoUI, PyObject *objTuple, std::string paramName, int id, int vid, std::vector* vstates); + +public: + static plPythonMgr& Instance(); + + void LoadPythonFiles(); +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResCollector.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResCollector.cpp new file mode 100644 index 00000000..2e939275 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResCollector.cpp @@ -0,0 +1,92 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plResCollector.h" +#include "plMtlCollector.h" +#include "max.h" +#include "../MaxExport/plExportProgressBar.h" + +void plResCollector::Collect() +{ + Interface *ip = GetCOREInterface(); + + // Get the directory to copy files to + char path[MAX_PATH]; + ip->ChooseDirectory(ip->GetMAXHWnd(), + "Choose a folder to copy the resources to", + path, + NULL); + if (!strcmp(path, "")) + return; + + // Make sure the directory ends with a slash + if (path[strlen(path)-1] != '\\' && path[strlen(path)-1] != '/') + strcat(path, "\\"); + + // Make a list of all the textures + TexNameSet texNames; + plMtlCollector::GetAllTextures(texNames); + + plExportProgressBar bar; + bar.Start("Copy Files", texNames.size()+1); + + // Copy each texture to the output directory + TexNameSet::iterator it = texNames.begin(); + for (; it != texNames.end(); it++) + { + const char *texName = *it; + + char outpath[MAX_PATH], name[_MAX_FNAME+_MAX_EXT], ext[_MAX_EXT]; + _splitpath(texName, NULL, NULL, name, ext); + strcat(name, ext); + + if (bar.Update(name)) + return; + + strcpy(outpath, path); + strcat(outpath, name); + + CopyFile(texName, outpath, TRUE); + } + + // Get the filename to save to + TSTR& maxFile = ip->GetCurFileName(); + TSTR& filePath = ip->GetCurFilePath(); + + if (!strcmp(maxFile, "")) + return; + + if (bar.Update(maxFile)) + return; + + // If we need to save, do it now + if (IsSaveRequired()) + ip->SaveToFile(filePath); + + // Copy the max file to the output directory + strcat(path, maxFile); + CopyFile(filePath, path, TRUE); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResCollector.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResCollector.h new file mode 100644 index 00000000..f9a96e0a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResCollector.h @@ -0,0 +1,29 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +namespace plResCollector +{ + void Collect(); +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResetXform.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResetXform.cpp new file mode 100644 index 00000000..d9c4d5d6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResetXform.cpp @@ -0,0 +1,168 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "max.h" +#include "plResetXform.h" + + +#include "../MaxMain/plMaxNode.h" +#include "../MaxComponent/plComponent.h" + + +void plResetXform::ResetSelected() const +{ + ResetSelectedRecur(GetCOREInterface()->GetRootNode()); +} + +void plResetXform::ResetSelectedRecur(INode* node) const +{ + if( !node ) + return; + + if( node->Selected() ) + { + IResetNode(node); + } + int i; + for( i = 0; i < node->NumberOfChildren(); i++ ) + ResetSelectedRecur(node->GetChildNode(i)); +} + +void plResetXform::IResetNode(INode* node) const +{ + const char* dbgNodeName = node->GetName(); + + BOOL deleteIt = false; + TriObject* oldObj = IGetTriObject(node, deleteIt); + if( !oldObj ) + return; + + Mesh& oldMesh = oldObj->mesh; + + TimeValue t(0); + + Matrix3 nodeTM = node->GetNodeTM(t); + + Matrix3 objectTM = node->GetObjectTM(t); + Matrix3 otm = objectTM * Inverse(nodeTM); + + Point3 u = nodeTM.GetRow(0); + Point3 v = nodeTM.GetRow(1); + Point3 w = CrossProd(u, v); + v = CrossProd(w, u); + + u = Normalize(u); + v = Normalize(v); + w = Normalize(w); + + + Matrix3 newXform(u, v, w, nodeTM.GetTrans()); + Matrix3 vtxXform = otm * nodeTM * Inverse(newXform) * Inverse(otm); + + TriObject* newObj = CreateNewTriObject(); + Mesh& newMesh = newObj->mesh; + + newMesh = oldMesh; + + int i; + for( i = 0; i < newMesh.getNumVerts(); i++ ) + { + Point3 p = vtxXform * newMesh.getVert(i); + newMesh.getVert(i) = p; + } + + if( nodeTM.Parity() ) + { + for( i = 0; i < newMesh.getNumFaces(); i++ ) + { + newMesh.FlipNormal(i); + } + + } + node->SetObjectRef(newObj); + + node->SetNodeTM(t, newXform); + + if( deleteIt ) + oldObj->DeleteThis(); +} + +TriObject* plResetXform::IGetTriObject(INode* node, BOOL& deleteIt) const +{ + Object *obj = node->EvalWorldState(TimeValue(0)).obj; + if( !obj ) + return NULL; + + if( !obj->CanConvertToType( triObjectClassID ) ) + return NULL; + + // Convert to triMesh object + TriObject *meshObj = (TriObject*)obj->ConvertToType(TimeValue(0), triObjectClassID); + if( !meshObj ) + return NULL; + + deleteIt = meshObj != obj; + + return meshObj; +} + +void plSelectNonRenderables::ICollectNonDrawablesRecur(plMaxNode* node, INodeTab& nodeTab) const +{ + plComponentBase* comp = node->ConvertToComponent(); + if( comp ) + comp->CollectNonDrawables(nodeTab); + + int i; + for( i = 0; i < node->NumberOfChildren(); i++ ) + ICollectNonDrawablesRecur((plMaxNode*)node->GetChildNode(i), nodeTab); +} + +void plSelectNonRenderables::SelectNonRenderables() const +{ + INodeTab nodeTab; + + ICollectNonDrawablesRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), nodeTab); + + INodeTab unhidden; + int i; + for( i = 0; i < nodeTab.Count(); i++ ) + { + INode* node = nodeTab[i]; + if( !node->IsHidden() ) + unhidden.Append(1, &node); + } + + theHold.Begin(); + + + TSTR undostr; + undostr.printf("SelNonRend"); + + GetCOREInterface()->SelectNodeTab(unhidden, true, true); + + theHold.Accept(undostr); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResetXform.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResetXform.h new file mode 100644 index 00000000..38318910 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plResetXform.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plResetXform_inc +#define plResetXform_inc + +class Interface; +class INode; +class TriObject; + +class plMaxNode; +class INodeTab; + +class plResetXform +{ +public: + void ResetSelected() const; +protected: + void ResetSelectedRecur(INode* node) const; + void IResetNode(INode* node) const; + TriObject* IGetTriObject(INode* node, BOOL& deleteIt) const; +}; + +class plSelectNonRenderables +{ +public: + void SelectNonRenderables() const; +protected: + void ICollectNonDrawablesRecur(plMaxNode* node, INodeTab& nodeTab) const; +}; + +#endif // plResetXform_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plSaveSelected.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plSaveSelected.cpp new file mode 100644 index 00000000..06234071 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plSaveSelected.cpp @@ -0,0 +1,317 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "max.h" +#include "notify.h" +#include "../MaxComponent/plComponent.h" +#include +#include "plMaxNode.h" + +bool IIsNodeInTab(INodeTab &tab, INode *node) +{ + for (int i = 0; i < tab.Count(); i++) + if (tab[i] == node) + return true; + + return false; +} + +class ComponentRefs +{ +public: + plComponentBase *comp; + INodeTab refs; +}; + +void plSaveSelected() +{ + Interface *ip = GetCOREInterface(); + + // Get the Max filename to save to + char buf[256]; + memset(&buf, 0, sizeof(buf)); + OPENFILENAME ofn = {0}; + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = ip->GetMAXHWnd(); + ofn.lpstrFilter = "3ds max (*.max)\0*.max\0\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFile = buf; + ofn.nMaxFile = sizeof(buf); +// ofn.lpstrInitialDir = ip->GetDir(APP_SCENE_DIR); + ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; + ofn.lpstrDefExt = "max"; + + if (GetSaveFileName(&ofn)) + { + int count = ip->GetSelNodeCount(); + int i; + + // Put all the selected nodes in a list + INodeTab selected; + for (i = 0; i < count; i++) + { + plMaxNode *node = (plMaxNode*)ip->GetSelNode(i); + selected.Append(1, (INode**)&node); + } + + // Put all the components attached to those nodes in a list + INodeTab components; + for (i = 0; i < count; i++) + { + plMaxNode *node = (plMaxNode*)ip->GetSelNode(i); + + UInt32 compCount = node->NumAttachedComponents(); + for (int j = 0; j < compCount; j++) + { + INode *compNode = node->GetAttachedComponent(j)->GetINode();//Node(j); + + if (!IIsNodeInTab(components, compNode)) + components.Append(1, &compNode); + } + } + + // Find the objects that the components are reffing that are not part of the selection. + // Back them up, and delete them out of the components ref list so they won't be saved too. + std::vector out; + for (i = 0; i < components.Count(); i++) + { + plComponentBase *comp = ((plMaxNode*)components[i])->ConvertToComponent(); + + for (int j = comp->NumTargets() - 1; j >= 0; --j) + { + plMaxNodeBase *node = comp->GetTarget(j); + + if (!node) + continue; + + const char *t1 = components[i]->GetName(); + const char *t2 = node->GetName(); + + if (!IIsNodeInTab(selected, node)) + { + UInt32 idx = -1; + for (UInt32 k = 0; k < out.size(); k++) + { + if (out[k].comp == comp) + idx = k; + } + + if (idx == -1) + { + idx = out.size(); + out.resize(idx+1); + out[idx].comp = comp; + } + + out[idx].refs.Append(1, (INode**)&node); + + comp->DeleteTarget(node); + } + } + } + + // Save the selected objects and their components + INodeTab allNodes; + allNodes.Append(selected.Count(), &selected[0]); + allNodes.Append(components.Count(), &components[0]); + + ip->FileSaveNodes(&allNodes, buf); + + // Restore the component refs to objects that weren't selected + for (i = 0; i < out.size(); i++) + { + for (int j = 0; j < out[i].refs.Count(); j++) + out[i].comp->AddTarget((plMaxNode*)out[i].refs[j]); + } + } +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +void IFindComponentsRecur(plMaxNode *node, std::vector &components) +{ + if (node->IsComponent()) + components.push_back(node->ConvertToComponent()); + + for (int i = 0; i < node->NumberOfChildren(); i++) + IFindComponentsRecur((plMaxNode*)node->GetChildNode(i), components); +} + +void IMergeComponents(plComponentBase *to, plComponentBase *from) +{ + for (int i = 0; i < from->NumTargets(); i++) + to->AddTarget(from->GetTarget(i)); + + // Delete the component from the scene + theHold.Begin(); + GetCOREInterface()->DeleteNode(from->GetINode()); + theHold.Accept(_T("Delete Component")); +} + +plMaxNode *IFindComponentRecur(plMaxNode *node, const char *name) +{ + if (!strcmp(node->GetName(), name)) + { + if (node->IsComponent()) + return node; + } + + for (int i = 0; i < node->NumberOfChildren(); i++) + { + plMaxNode *ret = IFindComponentRecur((plMaxNode*)node->GetChildNode(i), name); + if (ret) + return ret; + } + + return nil; +} + +void plMerge() +{ + Interface *ip = GetCOREInterface(); + + // Get the Max filename to merge + char file[MAX_PATH]; + memset(&file, 0, sizeof(file)); + + OPENFILENAME ofn = {0}; + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = ip->GetMAXHWnd(); + ofn.lpstrFilter = "3ds max (*.max)\0*.max\0\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFile = file; + ofn.nMaxFile = sizeof(file); +// ofn.lpstrInitialDir = ip->GetDir(APP_SCENE_DIR); + ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; + ofn.lpstrTitle = "Merge"; + + if (!GetOpenFileName(&ofn)) + return; + + // Don't actually merge yet, just get the names of every node in the file + NameTab nodeNames; + ip->MergeFromFile(file, TRUE, FALSE, FALSE, MERGE_LIST_NAMES, &nodeNames); + + // For each node name, search the current scene for a component with the same name. + // If one is found, append "Merged" to it so its name won't cause a conflict during + // the actual merge. + std::vector renamedNodes; + int i; + for (i = 0; i < nodeNames.Count(); i++) + { + plMaxNode *node = IFindComponentRecur((plMaxNode*)ip->GetRootNode(), nodeNames[i]); + if (node) + { + char buf[256]; + strcpy(buf, node->GetName()); + strcat(buf, "Merged"); + node->SetName(buf); + + renamedNodes.push_back(node); + } + } + + // Do the merge + ip->MergeFromFile(file); + + // Rename the components back to their original names + for (i = 0; i < renamedNodes.size(); i++) + { + char buf[256]; + strcpy(buf, renamedNodes[i]->GetName()); + buf[strlen(buf)-6] = '\0'; + renamedNodes[i]->SetName(buf); + } + + // Put all the components in the scene in a list + std::vector components; + IFindComponentsRecur((plMaxNode*)ip->GetRootNode(), components); + + nodeNames.ZeroCount(); + + // For each component, search the scene for any other components with the same + // name and type. If there are any, merge their target lists and delete one. + for (i = 0; i < renamedNodes.size(); i++) + { + if (!renamedNodes[i]) + continue; + + plComponentBase *oldComp = renamedNodes[i]->ConvertToComponent(); + char *oldCompName = oldComp->GetINode()->GetName(); + + for (int j = 0; j < components.size(); j++) + { + plComponentBase *comp = components[j]; + + if (oldComp == comp) + components[j] = nil; + else if (comp) + { + const char *temp = comp->GetINode()->GetName(); + + if (!strcmp(oldCompName, comp->GetINode()->GetName()) && + comp->ClassID() == comp->ClassID()) + { + IMergeComponents(comp, oldComp); + nodeNames.AddName(oldCompName); + continue; + } + } + } + } + + // Send out merge notifications again, so that the component dialog will be updated + BroadcastNotification(NOTIFY_FILE_PRE_MERGE); + BroadcastNotification(NOTIFY_FILE_POST_MERGE); + +#if 0 + if (nodeNames.Count() == 0) + return; + + // Actually calculate the size of all the merged component names, because + // a static buffer could be too small in large merges + UInt32 size = 0; + for (i = 0; i < nodeNames.Count(); i++) + size += strlen(nodeNames[i]) + 1; + + // Put all the component names in a list and show it to the user + char *buf = TRACKED_NEW char[size+25]; + strcpy(buf, "Components Merged:\n\n"); + + for (i = 0; i < nodeNames.Count(); i++) + { + strcat(buf, nodeNames[i]); + strcat(buf, "\n"); + } + + MessageBox(ip->GetMAXHWnd(), buf, "Components Merged", MB_OK); + + delete [] buf; +#endif +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plSaveSelected.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plSaveSelected.h new file mode 100644 index 00000000..9568db2f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plSaveSelected.h @@ -0,0 +1,27 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +void plSaveSelected(); +void plMerge(); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureExportLog.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureExportLog.cpp new file mode 100644 index 00000000..c3003309 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureExportLog.cpp @@ -0,0 +1,255 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plTextureExportLog - Lil' utility class for collating and writing out // +// a log of all textures exported, or rather, in the // +// resManager, or rather, the ones passed in to this // +// sucker. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "HeadSpin.h" +#include "hsTypes.h" +#include "hsWindows.h" +#include "plTextureExportLog.h" +#include "../plGImage/plCubicEnvironmap.h" +#include "../plGImage/plMipmap.h" +#include "../plGImage/plDynamicTextMap.h" +#include "../plPipeline/plRenderTarget.h" +#include "../plPipeline/plCubicRenderTarget.h" +#include "../pnKeyedObject/plKey.h" +#include "hsUtils.h" +#include "hsStream.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plTextureExportLog::plTextureExportLog( const char *fileName ) +{ + fFileName = hsStrcpy( fileName ); + fNodeList = nil; +} + +plTextureExportLog::~plTextureExportLog() +{ + plBMapNode *node; + + + if( fFileName != nil ) + { + Write(); + delete [] fFileName; + } + + while( fNodeList != nil ) + { + node = fNodeList->fNextNode; + delete fNodeList; + fNodeList = node; + } +} + +//// Other Ones ////////////////////////////////////////////////////////////// + +void plTextureExportLog::IAddBMapNode( UInt32 rank, plBitmap *bMap ) +{ + plBMapNode *node = TRACKED_NEW plBMapNode, **nodeHdl; + + + node->fBitmap = bMap; + node->fRank = rank; + + + for( nodeHdl = &fNodeList; *nodeHdl != nil; ) + { + if( (*nodeHdl)->fRank < rank ) + break; + + nodeHdl = &( (*nodeHdl)->fNextNode ); + } + + node->fNextNode = *nodeHdl; + *nodeHdl = node; +} + +void plTextureExportLog::AddTexture( plBitmap *texture ) +{ + // Rank based on size written to disk + IAddBMapNode( texture->GetTotalSize(), texture ); +} + +void plTextureExportLog::Write( void ) +{ + plBMapNode *node; + hsUNIXStream *stream = TRACKED_NEW hsUNIXStream; + char str[ 128 ]; + UInt32 size; + + + stream->Open( fFileName, "wt" ); + stream->WriteString( "------------------------------------------------------------\n" ); + stream->WriteString( "- Texture Export Data Log -\n" ); + stream->WriteString( "------------------------------------------------------------\n" ); + stream->WriteString( "\n" ); + IWriteTabbedString( stream, "Name", 4 ); + IWriteTabbedString( stream, "Size", 3 ); + IWriteTabbedString( stream, "Type", 4 ); + stream->WriteString( "\n" ); + stream->WriteString( "------------------------------------------------------------\n" ); + + for( node = fNodeList; node != nil; node = node->fNextNode ) + { + plMipmap *mip = plMipmap::ConvertNoRef( node->fBitmap ); + plDynamicTextMap *dynText = plDynamicTextMap::ConvertNoRef( node->fBitmap ); + plCubicEnvironmap *cubic = plCubicEnvironmap::ConvertNoRef( node->fBitmap ); + plCubicRenderTarget* cubeRend = plCubicRenderTarget::ConvertNoRef( node->fBitmap ); + plRenderTarget* rend = plRenderTarget::ConvertNoRef( node->fBitmap ); + + // Name + IWriteTabbedString( stream, node->fBitmap->GetKeyName(), dynText != nil ? 8 : 4 ); + + // Size, formatted + size = node->fBitmap->GetTotalSize(); + if( size < 1024 ) + { + sprintf( str, "%d bytes", size ); + IWriteTabbedString( stream, str, 2 ); + } + else if( size < 1024 * 1024 ) + { + sprintf( str, "%4.1f kb", size / 1024.f ); + IWriteTabbedString( stream, str, 2 ); + } + else + { + sprintf( str, "%4.1f Mb", size / ( 1024.f * 1024.f ) ); + IWriteTabbedString( stream, str, 2 ); + } + + if( dynText != nil ) + { + IWriteTabbedString( stream, "Dynamic text map", 3 ); + + // Dimensions + sprintf( str, "%d by %d", dynText->GetVisibleWidth(), dynText->GetVisibleHeight() ); + IWriteTabbedString( stream, str, 2 ); + + sprintf( str, "%d bpp", dynText->GetPixelSize() ); + IWriteTabbedString( stream, str, 1 ); + + IWriteTabbedString( stream, dynText->IsCompressed() + ? "Compressed" + : dynText->fCompressionType == plBitmap::kJPEGCompression + ? "JPEG" + : "Uncompressed", 2 ); + } + else if( cubic != nil ) + { + IWriteTabbedString( stream, "Cubic EnvironMap", 3 ); + + sprintf( str, "%d pixels square", cubic->GetFace( 0 )->GetWidth() ); + IWriteTabbedString( stream, str, 2 ); + + sprintf( str, "%d bpp", cubic->GetPixelSize() ); + IWriteTabbedString( stream, str, 1 ); + + IWriteTabbedString( stream, cubic->IsCompressed() + ? "Compressed" + : cubic->fCompressionType == plBitmap::kJPEGCompression + ? "JPEG" + : "Uncompressed", 2 ); + } + else if( mip != nil ) + { + IWriteTabbedString( stream, "Mipmap", 3 ); + + // Dimensions & num mip levels + sprintf( str, "%d by %d", mip->GetWidth(), mip->GetHeight() ); + IWriteTabbedString( stream, str, 2 ); + + sprintf( str, "%d bpp", mip->GetPixelSize() ); + IWriteTabbedString( stream, str, 1 ); + + sprintf( str, "%d levels", mip->GetNumLevels() ); + IWriteTabbedString( stream, str, 2 ); + + IWriteTabbedString( stream, mip->IsCompressed() + ? "Compressed" + : mip->fCompressionType == plBitmap::kJPEGCompression + ? "JPEG" + : "Uncompressed", 2 ); + } + else if( cubeRend ) + { + IWriteTabbedString( stream, "CubicRenderTarget", 3 ); + + // Dimensions & num mip levels + sprintf( str, "%d by %d", cubeRend->GetWidth(), cubeRend->GetHeight() ); + IWriteTabbedString( stream, str, 2 ); + } + else if( rend ) + { + IWriteTabbedString( stream, "RenderTarget", 3 ); + + // Dimensions & num mip levels + sprintf( str, "%d by %d", rend->GetWidth(), rend->GetHeight() ); + IWriteTabbedString( stream, str, 2 ); + } + else + { + IWriteTabbedString( stream, "Unknown", 3 ); + } + stream->WriteString( "\n" ); + } + + stream->Close(); + delete stream; + + // HACK: Prevent the destructor from writing out now + delete [] fFileName; + fFileName = nil; +} + +void plTextureExportLog::IWriteTabbedString( hsStream *stream, const char *string, Int8 numTabs ) +{ + static char tabs[ 64 ]; + int i; + + + stream->WriteString( string ); + + // Assumes 8 spaces per tab + numTabs -= strlen( string ) / 8; + if( numTabs < 1 ) + numTabs = 1; + + for( i = 0; i < numTabs; i++ ) + tabs[ i ] = '\t'; + tabs[ i ] = 0; + + stream->WriteString( tabs ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureExportLog.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureExportLog.h new file mode 100644 index 00000000..f3a0a532 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureExportLog.h @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plTextureExportLog - Lil' utility class for collating and writing out // +// a log of all textures exported, or rather, in the // +// resManager, or rather, the ones passed in to this // +// sucker. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plTextureExportLog_h +#define _plTextureExportLog_h + + +//// Class Definition //////////////////////////////////////////////////////// + +class plBitmap; +class hsStream; + +class plTextureExportLog +{ + protected: + + char *fFileName; + + // Tiny linked list helper + class plBMapNode + { + public: + UInt32 fRank; // Sort with biggest first + plBitmap *fBitmap; + plBMapNode *fNextNode; + }; + + plBMapNode *fNodeList; + + + void IAddBMapNode( UInt32 rank, plBitmap *bMap ); + void IWriteTabbedString( hsStream *stream, const char *string, Int8 numTabs ); + + public: + + plTextureExportLog( const char *fileName ); + ~plTextureExportLog(); + + void AddTexture( plBitmap *texture ); + void Write( void ); +}; + + +#endif //_plTextureExportLog_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureSearch.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureSearch.cpp new file mode 100644 index 00000000..baacad6d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureSearch.cpp @@ -0,0 +1,374 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plTextureSearch.h" +#include "resource.h" +#include "hsUtils.h" + +#define PB2Export __declspec( dllexport ) // Because I don't feel like including all the paramblock crap +#include "pbbitmap.h" +#include "bmmlib.h" +#include "IMtlEdit.h" + +#include "plMtlCollector.h" +#include "plMaxAccelerators.h" +#include "../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h" +#include "../../AssetMan/PublicInterface/MaxAssInterface.h" + +// Not a class member so we don't have to make everyone who uses this know about AssetMan +static jvUniqueId gAssetID; + +plTextureSearch::plTextureSearch() : fDlg(NULL) +{ + gAssetID.SetEmpty(); + memset(fFileName, 0, sizeof(fFileName)); +} + +plTextureSearch& plTextureSearch::Instance() +{ + static plTextureSearch theInstance; + return theInstance; +} + +void plTextureSearch::Toggle() +{ + if (!fDlg) + { + fDlg = CreateDialog(hInstance, + MAKEINTRESOURCE(IDD_FIND_TEXTURE), + GetCOREInterface()->GetMAXHWnd(), + ForwardDlgProc); + + HWND hList = GetDlgItem(fDlg, IDC_TEXTURE_LIST); + LVCOLUMN lvc; + lvc.mask = LVCF_TEXT; + lvc.pszText = "Material"; + ListView_InsertColumn(hList, 0, &lvc); + + lvc.pszText = "Layer"; + ListView_InsertColumn(hList, 1, &lvc); + + lvc.pszText = "Texture"; + ListView_InsertColumn(hList, 2, &lvc); + + IUpdateTextures(kUpdateLoadList); + + GetCOREInterface()->RegisterDlgWnd(fDlg); + ShowWindow(fDlg, SW_SHOW); + } + else + { + DestroyWindow(fDlg); + fDlg = NULL; + } +} + + +BOOL plTextureSearch::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return Instance().DlgProc(hDlg, msg, wParam, lParam); +} + +BOOL plTextureSearch::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_ACTIVATE: + if (LOWORD(wParam) == WA_INACTIVE) + plMaxAccelerators::Enable(); + else + plMaxAccelerators::Disable(); + return TRUE; + + case WM_COMMAND: + if (HIWORD(wParam) == BN_CLICKED) + { + int id = LOWORD(wParam); + if (id == IDCANCEL) + { + Toggle(); + return TRUE; + } + else if (id == IDC_UPDATE_BUTTON) + { + IUpdateTextures(kUpdateLoadList); + return TRUE; + } + else if (id == IDC_REPLACE_ALL_BUTTON) + { + if (hsMessageBox("Are you sure?", "Confirmation", hsMessageBoxYesNo) == hsMBoxYes) + { + IUpdateTextures(kUpdateReplace); + } + return TRUE; + } + else if (id == IDC_SET_ALL_BUTTON) + { + if (hsMessageBox("Are you sure?", "Confirmation", hsMessageBoxYesNo) == hsMBoxYes) + { + IUpdateTextures(kUpdateSetSize); + } + return TRUE; + } + else if (id == IDC_REPLACE_BUTTON) + { + IPickReplaceTexture(); + return TRUE; + } + } + else if (HIWORD(wParam) == EN_CHANGE && LOWORD(wParam) == IDC_FIND_EDIT) + { + bool findText = (SendDlgItemMessage(hDlg, IDC_FIND_EDIT, EM_LINELENGTH, 0, 0) > 0); + bool replace = (fFileName[0] != '\0'); + + EnableWindow(GetDlgItem(hDlg, IDC_REPLACE_BUTTON), findText); + EnableWindow(GetDlgItem(hDlg, IDC_REPLACE_ALL_BUTTON), findText && replace); + + return TRUE; + } + break; + + case WM_NOTIFY: + { + NMHDR* nmhdr = (NMHDR*)lParam; + // User double-clicked a material in the texture list + if (nmhdr->idFrom == IDC_TEXTURE_LIST && nmhdr->code == NM_DBLCLK) + { + NMITEMACTIVATE* itema = (NMITEMACTIVATE*)lParam; + if (itema->iItem != -1) + { + // Get the material the user clicked on + HWND hList = GetDlgItem(fDlg, IDC_TEXTURE_LIST); + LV_ITEM item; + item.iItem = itema->iItem; + item.mask = LVIF_PARAM; + ListView_GetItem(hList, &item); + + Mtl* mtl = (Mtl*)item.lParam; + + // Make sure the material is still in the scene (paranoid check) + MtlSet mtls; + plMtlCollector::GetMtls(&mtls, nil, plMtlCollector::kPlasmaOnly | plMtlCollector::kNoMultiMtl); + if (mtls.find(mtl) != mtls.end()) + { + // Put the material in the current slot of the material editor + IMtlEditInterface* mtlInterface = GetMtlEditInterface(); + int slot = mtlInterface->GetActiveMtlSlot(); + mtlInterface->PutMtlToMtlEditor(mtl, slot); + } + } + + return TRUE; + } + } + break; + } + + return FALSE; +} + +static int FloorPow2(int value) +{ + int v; + for (v = 1; v <= value; v <<= 1); + return v >> 1; +} + +void plTextureSearch::IUpdateTextures(plTextureSearch::Update update) +{ + MtlSet mtls; + plMtlCollector::GetMtls(&mtls, nil, plMtlCollector::kPlasmaOnly | plMtlCollector::kNoMultiMtl); + + char searchStr[256]; + GetDlgItemText(fDlg, IDC_FIND_EDIT, searchStr, sizeof(searchStr)); + strlwr(searchStr); + + HWND hList = GetDlgItem(fDlg, IDC_TEXTURE_LIST); + ListView_DeleteAllItems(hList); + + int sizeX = -1, sizeY = -1; + + HWND hCombo = GetDlgItem(fDlg, IDC_SIZE_COMBO); + + // If we're updating the size, get whatever the user selected + if (update == kUpdateSetSize) + { + int sel = ComboBox_GetCurSel(hCombo); + UInt32 data = ComboBox_GetItemData(hCombo, sel); + sizeX = LOWORD(data); + sizeY = HIWORD(data); + } + + MtlSet::iterator it = mtls.begin(); + for (; it != mtls.end(); it++) + { + Mtl *mtl = (*it); + + LayerSet layers; + plMtlCollector::GetMtlLayers(mtl, layers); + + LayerSet::iterator layerIt = layers.begin(); + for (; layerIt != layers.end(); layerIt++) + { + plPlasmaMAXLayer *layer = (*layerIt); + + int numBitmaps = layer->GetNumBitmaps(); + + for (int i = 0; i < numBitmaps; i++) + { + PBBitmap *pbbm = layer->GetPBBitmap(i); + if (pbbm) + { + const char *name = pbbm->bi.Filename(); + if (name && *name != '\0') + { + char buf[256]; + strncpy(buf, name, sizeof(buf)); + strlwr(buf); + + // If we don't have a search string, or we do and it was + // found in the texture name, add the texture to the list. + if (searchStr[0] == '\0' || strstr(buf, searchStr)) + { + if (update == kUpdateLoadList) + { + LVITEM item = {0}; + item.mask = LVIF_TEXT | LVIF_PARAM; + item.pszText = mtl->GetName(); + item.lParam = (LPARAM)mtl; // A little dangerous, since the user could delete this + int idx = ListView_InsertItem(hList, &item); + + ListView_SetItemText(hList, idx, 1, layer->GetName()); + ListView_SetItemText(hList, idx, 2, (char*)name); + + // If size is uninitialized or the same as the last, keep size + if ((sizeX == -1 && sizeY == -1) || (sizeX == pbbm->bi.Width() && sizeY == pbbm->bi.Height())) + { + sizeX = pbbm->bi.Width(); + sizeY = pbbm->bi.Height(); + } + // Otherwise clear it + else + { + sizeX = sizeY = 0; + } + } + else if (update == kUpdateReplace) + { + layer->SetBitmapAssetId(gAssetID, i); + + BitmapInfo info; + info.SetName(fFileName); + layer->SetBitmap(&info, i); + } + else if (update == kUpdateSetSize) + { + layer->SetExportSize(sizeX, sizeY); + } + } + } + } + } + } + } + + if (update == kUpdateLoadList) + { + HWND hButton = GetDlgItem(fDlg, IDC_SET_ALL_BUTTON); + ComboBox_ResetContent(hCombo); + + // If all bitmaps are the same size, enable resizing + if (sizeX != -1 && sizeX != 0) + { + sizeX = FloorPow2(sizeX); + sizeY = FloorPow2(sizeY); + + char buf[256]; + + while (sizeX >= 4 && sizeY >= 4) + { + sprintf(buf, "%d x %d", sizeX, sizeY); + int idx = ComboBox_AddString(hCombo, buf); + ComboBox_SetItemData(hCombo, idx, MAKELPARAM(sizeX, sizeY)); + + sizeX >>= 1; + sizeY >>= 1; + } + + ComboBox_SetCurSel(hCombo, 0); + + EnableWindow(hCombo, TRUE); + EnableWindow(hButton, TRUE); + } + else + { + EnableWindow(hCombo, FALSE); + EnableWindow(hButton, FALSE); + } + } + + int autoSizeType = LVSCW_AUTOSIZE; + if (ListView_GetItemCount(hList) == 0) + autoSizeType = LVSCW_AUTOSIZE_USEHEADER; + + ListView_SetColumnWidth(hList, 0, autoSizeType); + ListView_SetColumnWidth(hList, 1, autoSizeType); + ListView_SetColumnWidth(hList, 2, autoSizeType); +} + +void plTextureSearch::IPickReplaceTexture() +{ + fFileName[0] = '\0'; + + // if we have the assetman plug-in, then try to use it, unless shift is held down + MaxAssInterface* maxAssInterface = GetMaxAssInterface(); + if (maxAssInterface && !(GetKeyState(VK_SHIFT) & 0x8000)) + { + maxAssInterface->OpenBitmapDlg(gAssetID, fFileName, sizeof(fFileName)); + } + else + { + gAssetID.SetEmpty(); + BitmapInfo bi; + TheManager->SelectFileInput(&bi, GetCOREInterface()->GetMAXHWnd(), _T("Select Bitmap Image File")); + strcpy(fFileName, bi.Filename()); + } + + if (fFileName[0] == '\0') + { + SetDlgItemText(fDlg, IDC_REPLACE_BUTTON, "(none)"); + EnableWindow(GetDlgItem(fDlg, IDC_REPLACE_ALL_BUTTON), FALSE); + } + else + { + char fname[_MAX_FNAME+_MAX_EXT], ext[_MAX_EXT]; + _splitpath(fFileName, NULL, NULL, fname, ext); + strcat(fname, ext); + + SetDlgItemText(fDlg, IDC_REPLACE_BUTTON, fname); + EnableWindow(GetDlgItem(fDlg, IDC_REPLACE_ALL_BUTTON), TRUE); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureSearch.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureSearch.h new file mode 100644 index 00000000..84601110 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/plTextureSearch.h @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "max.h" + +class plTextureSearch +{ +protected: + HWND fDlg; + char fFileName[MAX_PATH]; + + plTextureSearch(); + +public: + static plTextureSearch& Instance(); + void Toggle(); + +protected: + enum Update { kUpdateLoadList, kUpdateReplace, kUpdateSetSize }; + void IUpdateTextures(Update update); + void IPickReplaceTexture(); + + static BOOL CALLBACK ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/resource.h new file mode 100644 index 00000000..80964ae3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMain/resource.h @@ -0,0 +1,134 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by SimpleExport.rc +// +#define IDS_ACT1_DESC 1 +#define IDS_ACT1_NAME 2 +#define IDS_ACT2_DESC 3 +#define IDS_ACT2_NAME 4 +#define IDS_ACT3_NAME 5 +#define IDS_ACT4_NAME 6 +#define IDS_ACT5_NAME 7 +#define IDS_ACT6_NAME 8 +#define IDS_ACT7_NAME 9 +#define IDS_ACT8_DESC 10 +#define IDS_ACT8_NAME 11 +#define IDS_ACT9_DESC 12 +#define IDS_ACT9_NAME 13 +#define IDS_ACT10_NAME 14 +#define IDS_PLASMA_EXPORT 15 +#define IDS_ACT11_NAME 16 +#define IDS_ACT11_DESC 17 +#define IDS_ACT12_NAME 18 +#define IDS_ACT12_DESC 19 +#define IDS_ACT_CAT 30 +#define IDD_COMP_PANEL 103 +#define IDD_SCENEVIEWER 105 +#define IDD_GET_LOCATION 106 +#define IDD_COMP_MAIN 107 +#define IDD_AGE_DESC 108 +#define IDD_AGE_NAME 109 +#define IDD_UTILS_RES 110 +#define IDD_UTILS 111 +#define IDB_CURSOR_UP 112 +#define IDD_FIND_TEXTURE 112 +#define IDB_CURSOR_DOWN 113 +#define IDD_EXPORT 113 +#define IDB_CURSOR_RIGHT 114 +#define IDD_REF_BY 114 +#define IDB_CURSOR_LEFT 115 +#define IDB_CURSOR_OPEN 116 +#define IDB_CURSOR_GRAB 117 +#define IDB_CURSOR_CLICKED 118 +#define IDB_CURSOR_POISED 119 +#define IDR_COMP_MENU 120 +#define IDD_AGE_SEQNUM 121 +#define IDD_AGE_CHECKIN 122 +#define IDD_AGE_SAVEYESNO 123 +#define IDC_COMPLIST 1007 +#define IDC_BACK 1009 +#define IDC_FORWARD 1010 +#define IDC_NUM_TARGS 1011 +#define IDC_TREE 1014 +#define IDC_ATTACH 1015 +#define IDC_UPDATE 1017 +#define IDC_SPINNER 1019 +#define IDC_EDIT 1020 +#define IDC_LIST_LOC 1021 +#define IDC_CAP_EDIT 1021 +#define IDC_CHECK_DEFAULT 1022 +#define IDC_CAP_SPINNER 1022 +#define IDC_PROMPT 1023 +#define IDC_DIR 1024 +#define IDC_BROWSE_EXPORT 1025 +#define IDC_COMMENT_TEXT 1026 +#define IDC_COMMENTS 1028 +#define IDC_AGE_NEW 1030 +#define IDC_PAGE_NEW 1032 +#define IDC_PAGE_DEL 1033 +#define IDC_DATE 1034 +#define IDC_TIME 1035 +#define IDC_AGE_NAME 1036 +#define IDC_RESHADE 1037 +#define IDC_DAYLEN_EDIT 1038 +#define IDC_DAYLEN_SPINNER 1039 +#define IDC_REG_TREE 1040 +#define IDC_SEQPREFIX_EDIT 1040 +#define IDC_RES 1041 +#define IDC_SEQPREFIX_SPIN 1041 +#define IDC_REFRESH 1042 +#define IDC_CLIENT_PATH 1044 +#define IDC_REUSE_DATA 1045 +#define IDC_EXPORT_PATH 1045 +#define IDC_EXE 1046 +#define IDC_START 1047 +#define IDC_PAGE_LIST 1048 +#define IDC_FIND_EDIT 1049 +#define IDC_UPDATE_BUTTON 1050 +#define IDC_REPLACE_ALL_BUTTON 1052 +#define IDC_TEXTURE_LIST 1053 +#define IDC_SET_ALL_BUTTON 1054 +#define IDC_REPLACE_BUTTON 1055 +#define IDC_PRESHADE_CHECK 1056 +#define IDC_PHYSICAL_CHECK 1057 +#define IDC_EXPORT 1058 +#define IDC_LAST_EXPORT 1059 +#define IDC_PAGE_COMBO 1060 +#define IDC_LIGHTMAP_CHECK 1061 +#define IDC_REF_BY_BUTTON 1062 +#define IDC_UV_CHECK 1062 +#define IDC_REF_LIST 1063 +#define IDC_CLOSE 1064 +#define IDC_AGE_LIST 1065 +#define IDC_RSVDCHECK 1066 +#define IDC_EDITREG 1067 +#define IDC_AGEMSG 1069 +#define IDC_ADMMSG 1070 +#define IDC_INFOMSG 1071 +#define IDC_AGEDESC 1071 +#define IDC_RADIO_FILE 1072 +#define IDC_RADIO_DIR 1073 +#define IDC_AGE_CHECKOUT 1074 +#define IDC_AGE_CHECKIN 1075 +#define IDC_AGE_UNDOCHECKOUT 1076 +#define IDC_AGELIST_STATIC 1077 +#define IDC_ADM_DONTLOAD 1078 +#define IDC_ADM_LOADSDL 1079 +#define IDC_SIZE_COMBO 1081 +#define IDC_HIGH_SDL_CHECK 1082 +#define IDC_BRANCHCOMBO 1083 +#define IDC_ADM_LOCAL_ONLY 1084 +#define IDC_ADM_VOLATILE 1085 +#define ID_REFRESH 40002 +#define ID_REMOVE_UNUSED 40003 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 123 +#define _APS_NEXT_COMMAND_VALUE 40004 +#define _APS_NEXT_CONTROL_VALUE 1086 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/DllEntry.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/DllEntry.cpp new file mode 100644 index 00000000..43d86af5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/DllEntry.cpp @@ -0,0 +1,91 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/********************************************************************** + *< + FILE: DllEntry.cpp + + DESCRIPTION: Contains the Dll Entry stuff + + CREATED BY: + + HISTORY: + + *> Copyright (c) 2000, All Rights Reserved. + **********************************************************************/ +#include "plMaterialUpdate.h" + +extern ClassDesc2* GetMaterialUpdateDesc(); + +HINSTANCE hInstance; +int controlsInit = FALSE; + + +BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) +{ + hInstance = hinstDLL; // Hang on to this DLL's instance handle. + + if (!controlsInit) { + controlsInit = TRUE; + InitCustomControls(hInstance); // Initialize MAX's custom controls + InitCommonControls(); // Initialize Win95 controls + } + + return (TRUE); +} + +__declspec( dllexport ) const TCHAR* LibDescription() +{ + return GetString(IDS_LIBDESCRIPTION); +} + +//TODO: Must change this number when adding a new class +__declspec( dllexport ) int LibNumberClasses() +{ + return 1; +} + +__declspec( dllexport ) ClassDesc* LibClassDesc(int i) +{ + switch(i) { + case 0: return GetMaterialUpdateDesc(); + default: return 0; + } +} + +__declspec( dllexport ) ULONG LibVersion() +{ + return VERSION_3DSMAX; +} + +TCHAR *GetString(int id) +{ + static TCHAR buf[256]; + + if (hInstance) + return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL; + return NULL; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/OldMat/hsMaxLayer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/OldMat/hsMaxLayer.h new file mode 100644 index 00000000..3ef7d1a3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/OldMat/hsMaxLayer.h @@ -0,0 +1,421 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __HSMAXLAYER_H +#define __HSMAXLAYER_H + +#include +#include "hsMaxLayerBase.h" +//#include "hsMaxMtlRes.h" + +//========================================================================================= +// Flags and constants... +//========================================================================================= + +// ParamBlock entries +#define PB_LAYER_CLIPU 0 +#define PB_LAYER_CLIPV 1 +#define PB_LAYER_CLIPW 2 +#define PB_LAYER_CLIPH 3 +#define PB_LAYER_JITTER 4 +#define PB_LAYER_AMBIENT 5 +#define PB_LAYER_COLOR 6 +#define PB_LAYER_SHININESS 7 +#define PB_LAYER_SHIN_STR 8 +#define PB_LAYER_SELFI 9 +#define PB_LAYER_OPAC 10 +#define PB_LAYER_OPFALL 11 +#define PB_LAYER_FILTER 12 +#define PB_LAYER_WIRESZ 13 +#define PB_LAYER_IOR 14 +#define PB_LAYER_BOUNCE 15 +#define PB_LAYER_STATFRIC 16 +#define PB_LAYER_SLIDFRIC 17 +#define PB_LAYER_DIMLEV 18 +#define PB_LAYER_DIMMULT 19 +#define PB_LAYER_MAPPERCENT 20 +#define PB_LAYER_MIPMAPBLUR 21 +#define PB_LAYER_LODBIAS 22 +#define PB_LAYER_DETAILBIAS 23 +#define PB_LAYER_DETAILMAX 24 +#define PB_LAYER_ENVIRONMAPSIZE 25 +#define LAYER_NPARAMS 26 + +#define HSMAX_LAYER_LOCK_AD 0x1 + +class hsMaxLayer; +class hsMaxLayerDlg; + +static LRESULT CALLBACK HiliteWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ); + +// which edit control enum +enum EditControl {Hc, Sc, Vc, Rc, Gc, Bc}; + +void GetBMName(BitmapInfo& bi, TSTR &fname); + +//========================================================================================= +// BMSampler +//========================================================================================= + +class BMSampler : public MapSampler { + Bitmap *bm; + hsMaxLayer *tex; + int alphaSource; + float u0,v0,u1,v1,ufac,vfac,ujit,vjit; + int bmw,bmh,clipx, clipy, cliph; + float fclipw,fcliph, fbmh, fbmw; +public: + BMSampler() { bm = NULL; } + void Init(hsMaxLayer *bmt); + int PlaceUV(ShadeContext& sc, float &u, float &v, int iu, int iv); + void PlaceUVFilter(ShadeContext& sc, float &u, float &v, int iu, int iv); + AColor Sample(ShadeContext& sc, float u,float v); + AColor SampleFilter(ShadeContext& sc, float u,float v, float du, float dv); + // float SampleMono(ShadeContext& sc, float u,float v); + // float SampleMonoFilter(ShadeContext& sc, float u,float v, float du, float dv); +}; + +//========================================================================================= +// BM\AlphaSampler +//========================================================================================= +class BMAlphaSampler : public MapSampler { + Bitmap *bm; + hsMaxLayer *tex; + float u0,v0,u1,v1,ufac,vfac,ujit,vjit; + int bmw,bmh,clipx, clipy, cliph; + float fclipw,fcliph, fbmh, fbmw; +public: + BMAlphaSampler() { bm = NULL; } + void Init(hsMaxLayer *bmt); + int PlaceUV(ShadeContext &sc, float &u, float &v, int iu, int iv); + void PlaceUVFilter(ShadeContext &sc, float &u, float &v, int iu, int iv); + AColor Sample(ShadeContext& sc, float u,float v) { return AColor(0,0,0,0);} + AColor SampleFilter(ShadeContext& sc, float u,float v, float du, float dv) { return AColor(0,0,0,0);} + float SampleMono(ShadeContext& sc, float u,float v); + float SampleMonoFilter(ShadeContext& sc, float u,float v, float du, float dv); +}; + +//========================================================================================= +// hsMaxLayerNotify... this calls hsMaxLayer::NotifyChanged when a bitmap changes +//========================================================================================= + +class hsMaxLayerNotify : public BitmapNotify { +public: + void SetTex(hsMaxLayer *tx) { tex = tx; } + int Changed(ULONG flags); + +private: + hsMaxLayer *tex; +}; + +//========================================================================================= +// hsMaxLayer: a material layer with (possibly) texture info, blending info and shading info +//========================================================================================= + +class hsMaxLayer : public hsMaxLayerBase { + friend class hsMaxLayerPostLoad; + friend class hsMaxLayerDlg; + friend class BMSampler; + friend class BMAlphaSampler; + friend class BMCropper; +public: + hsMaxLayer(); + ~hsMaxLayer(); + + ParamDlg* CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + void Update(TimeValue t, Interval& valid); + void Reset(); + Interval Validity(TimeValue t) { Interval v; Update(t,v); return ivalid; } + TSTR GetFullName(); + + void SetOutputLevel(TimeValue t, float v) { } + void SetFilterType(int ft); + void SetAlphaSource(int as); + void SetEndCondition(int endcond) { endCond = endcond; } + void SetAlphaAsMono(BOOL onoff) { alphaAsMono = onoff; } + void SetAlphaAsRGB(BOOL onoff) { alphaAsRGB = onoff; } + void SetPremultAlpha(BOOL onoff) { premultAlpha = onoff; } + void SetMapName(TCHAR *name) { + bi.SetName(name); + FreeBitmap(); + if (paramDlg) + ReloadBitmap(); + } + void SetStartTime(TimeValue t) { startTime = t; } + void SetPlaybackRate(float r) { pbRate = r; } + + void SetClipU(TimeValue t, float f) { clipu = f; pblock->SetValue( PB_LAYER_CLIPU, t, f); } + void SetClipV(TimeValue t, float f) { clipv = f; pblock->SetValue( PB_LAYER_CLIPV, t, f); } + void SetClipW(TimeValue t, float f) { clipw = f; pblock->SetValue( PB_LAYER_CLIPW, t, f); } + void SetClipH(TimeValue t, float f) { cliph = f; pblock->SetValue( PB_LAYER_CLIPH, t, f); } + void SetJitter(TimeValue t, float f) { cliph = f; pblock->SetValue( PB_LAYER_JITTER, t, f); } + + int GetFilterType() { return filterType; } + int GetAlphaSource() { return alphaSource; } + int GetEndCondition() { return endCond; } + BOOL GetAlphaAsMono(BOOL onoff) { return alphaAsMono; } + BOOL GetAlphaAsRGB(BOOL onoff) { return alphaAsRGB; } + BOOL GetPremultAlpha(BOOL onoff) { return premultAlpha; } + TCHAR *GetMapName() { return (TCHAR *)bi.Name(); } + TimeValue GetStartTime() { return startTime; } + float GetPlaybackRate() { return pbRate; } + StdUVGen* GetUVGen() { return (StdUVGen*)uvGen; } + TextureOutput* GetTexout() { return 0; } + Bitmap *GetBitmap(TimeValue t) { LoadBitmap(t); return thebm; } + float GetClipU(TimeValue t) { return pblock->GetFloat( PB_LAYER_CLIPU, t); } + float GetClipV(TimeValue t) { return pblock->GetFloat( PB_LAYER_CLIPV, t); } + float GetClipW(TimeValue t) { return pblock->GetFloat( PB_LAYER_CLIPW, t); } + float GetClipH(TimeValue t) { return pblock->GetFloat( PB_LAYER_CLIPH, t); } + float GetJitter(TimeValue t) { return pblock->GetFloat( PB_LAYER_JITTER, t); } + void StuffCropValues(); // stuff new values into the cropping VFB + + void UpdtSampler() { + mysamp.Init(this); + alphasamp.Init(this); + } + + void NotifyChanged(); + Bitmap* BuildBitmap(int size); + void FreeBitmap(); + BMMRES LoadBitmap(TimeValue t); + int CalcFrame(TimeValue t); + void ScaleBitmapBumpAmt(float f); + void ReloadBitmap(); + + // Evaluate the color of map for the context. + RGBA EvalColor(ShadeContext& sc); + float EvalMono(ShadeContext& sc); + Point3 EvalNormalPerturb(ShadeContext& sc); + + void DiscardTexHandle(); + + BOOL SupportTexDisplay() { return TRUE; } + void ActivateTexDisplay(BOOL onoff); + DWORD GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker); + void GetUVTransform(Matrix3 &uvtrans) { uvGen->GetUVTransform(uvtrans); } + int GetTextureTiling() { return uvGen->GetTextureTiling(); } + int GetUVWSource() { return uvGen->GetUVWSource(); } + UVGen *GetTheUVGen() { return uvGen; } +#ifdef MAXR3 + int GetMapChannel () { return uvGen->GetMapChannel(); } +#endif // MAXR3 + + int RenderBegin(TimeValue t, ULONG flags) { + inRender = TRUE; + return 1; + } + int RenderEnd(TimeValue t) { + inRender = FALSE; + return 1; + } + int LoadMapFiles(TimeValue t) { LoadBitmap(t); return 1; } + void RenderBitmap(TimeValue t, Bitmap *bm, float scale3D, BOOL filter); + + Class_ID ClassID() { return hsMaxLayerClassID; } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } +#ifdef HS_DEBUGGING + void GetClassName(TSTR& s) { s= GetString(IDS_DS_LAYER_DEBUG); } +#else + void GetClassName(TSTR& s) { s= "";}//GetString(IDS_DS_LAYER); } +#endif + void DeleteThis() { delete this; } + + // Requirements + ULONG LocalRequirements(int subMtlNum); + + int NumSubs() { return 2; } + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + int SubNumToRefNum(int subNum) { return subNum; } + void InitSlotType(int sType) { if (uvGen) uvGen->InitSlotType(sType); } + + // From ref + int NumRefs() { return 2; } + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + + RefTargetHandle Clone(RemapDir &remap = NoRemap()); + RefResult NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message ); + + // From Animatable + void EnumAuxFiles(NameEnumCallback& nameEnum, DWORD flags) { + bi.EnumAuxFiles(nameEnum,flags); + } + int SetProperty(ULONG id, void *data); + void FreeAllBitmaps() { + FreeBitmap(); + } + + int GetFlag(ULONG f) { return (flags&f)?1:0; } + void SetFlag(ULONG f, ULONG val); + + // from hsMaxLayerBase + BOOL KeyAtTime(int id,TimeValue t) { return pblock->KeyFrameAtTime(id,t); } + Color GetAmbient(int mtlNum=0, BOOL backFace=FALSE); + Color GetColor(int mtlNum=0, BOOL backFace=FALSE); + + float GetShininess(int mtlNum=0, BOOL backFace=FALSE) { return 0.f; } + float GetShinStr(int mtlNum=0, BOOL backFace=FALSE) { return 0.f; } + float GetMapPercent(int mtlNum=0, BOOL backFace=FALSE) { return 0.f; } + float GetOpacity(int mtlNum=0, BOOL backFace=FALSE) { return 0.f; } + float GetMipMapBlur(int mtlNum=0, BOOL backFace=FALSE) { return pblock->GetFloat(PB_LAYER_MIPMAPBLUR, 0); } + float GetLODBias(int mtlNum=0, BOOL backFace=FALSE) { return pblock->GetFloat(PB_LAYER_LODBIAS, 0); } + int GetEnvironMapSize(int mtlNum=0, BOOL backFace=FALSE) { return 0; } + + Color GetAmbient(TimeValue t) const; + Color GetColor(TimeValue t) const; + + float GetShininess(TimeValue t) const; + float GetShinStr(TimeValue t) const; + float GetMapPercent(TimeValue t) const; + float GetOpacity(TimeValue t) const; + float GetMipMapBlur(TimeValue t) const; + float GetLODBias(TimeValue t) const; + float GetDetailDropoffStart(TimeValue t) const; + float GetDetailDropoffStop(TimeValue t) const; + float GetDetailMax(TimeValue t) const; + float GetDetailMin(TimeValue t) const; + int GetEnvironMapSize(TimeValue t) const; + + int GetNumExplicitMipmaps() const { return mipmapInfo.Count(); } + TCHAR *GetExplicitMipmapName(int i) const { return mipmapOn[i] ? (TCHAR *)mipmapInfo[i].Name() : 0; } + BOOL ExplicitMipmapEnabled(int i) const { return mipmapOn[i]; } + int GetExplicitMipmapLevel(int i) const { return mipmapLevel[i]; } + + BOOL GetDirty() const { return dirty; } + ULONG GetBlendFlags() const { return blendFlags; } + ULONG GetZFlags() const { return zFlags; } + ULONG GetShadeFlags() const { return shadeFlags; } + ULONG GetMiscFlags() const { return miscFlags; } + ProcType GetProcType() const { return procType; } + hsMatUsage GetUsage() const { return usageType; } + + // Setting the things in hsMaxLayerBase + void SetShininess(float v, TimeValue t); + void SetShinStr(float v, TimeValue t); + void SetMapPercent(float v, TimeValue t); + void SetOpacity(float v, TimeValue t); + void SetAmbient(Color c, TimeValue t); + void SetColor(Color c, TimeValue t); + void SetMipMapBlur(float f, TimeValue t); + void SetLODBias(float f, TimeValue t); + void SetDetailDropoffStart(float f, TimeValue t); + void SetDetailDropoffStop(float f, TimeValue t); + void SetDetailMax(float f, TimeValue t); + void SetDetailMin(float f, TimeValue t); + void SetEnvironMapSize(int i, TimeValue t); + + void SetNumExplicitMipmaps(int n); + void SetExplicitMipmapName(int i, const char *n) { mipmapInfo[i].SetName(n); } + void EnableExplicitMipmap(int i, BOOL state) { mipmapOn[i] = state; } + void SetExplicitMipmapLevel(int i, int l) { mipmapLevel[i] = l; } + + void SetDirty(BOOL state) { dirty = state; } + void SetBlendFlag(int i, BOOL state); + void SetZFlag(int flag, BOOL state); + void SetShadeFlag(int flag, BOOL state); + void SetMiscFlag(int flag, BOOL state); + void SetProcType(ProcType type); + void SetUsage(hsMatUsage use); + void GuessUsage(); + + // IO + IOResult Save(ISave *isave); + IOResult Load(ILoad *iload); + + // Colin Hack + BOOL GetApplyCrop() { return applyCrop; } + BOOL GetPlaceImage() { return placeImage; } + +private: + UVGen *uvGen; // ref #0 + IParamBlock *pblock; // ref #1 + BitmapInfo bi; + TSTR bmName; // for loading old files only + Bitmap *thebm; + hsMaxLayerNotify bmNotify; + TexHandle *texHandle; + float pbRate; + TimeValue startTime; + Interval ivalid; + + // Samplers + BMSampler mysamp; + BMAlphaSampler alphasamp; + + BOOL applyCrop; + BOOL loadingOld; + BOOL placeImage; + BOOL randPlace; + int filterType; + int alphaSource; + int endCond; + int alphaAsMono; + int alphaAsRGB; + float clipu, clipv, clipw, cliph, jitter; + BOOL premultAlpha; + BOOL isNew; + BOOL loadFailed; + BOOL inRender; + hsMaxLayerDlg *paramDlg; + int texTime; + Interval texValid; + Interval clipValid; + float rumax,rumin,rvmax,rvmin; + + // ADDED + ULONG flags; + + Color ambient; + Color color; + + float opacity; + float shine_str; + float shininess; + float mapPercent; + float mipMapBlur; + float lodBias; + float detailDropoffStart; + float detailDropoffStop; + float detailMax; + float detailMin; + int environMapSize; + + BOOL dirty; + + ULONG blendFlags; + ULONG zFlags; + ULONG shadeFlags; + ULONG miscFlags; + ProcType procType; + hsMatUsage usageType; + + Tab mipmapInfo; // references + Tab mipmapOn; + Tab mipmapLevel; +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/OldMat/hsMaxLayerBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/OldMat/hsMaxLayerBase.h new file mode 100644 index 00000000..731a3a20 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/OldMat/hsMaxLayerBase.h @@ -0,0 +1,190 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __HSMAXLAYERBASE_H +#define __HSMAXLAYERBASE_H + +#include "stdmat.h" + +#define HSMAX_LAYER_CLASS_ID 0x41990fe7 + +const Class_ID hsMaxLayerClassID(HSMAX_LAYER_CLASS_ID, 0x72404998); +const Class_ID hsMaxMtlClassID(0x2f335902, 0x111d2ea7); +const Class_ID hsEnvironMapMtlClassID(0x98777b3, 0x5eb270dd); + +class hsMaxLayerBase : public BitmapTex { +public: + enum hsMatBlendFlags { + kBlendTest = 0x1, // dev + // Rest of blends are mutually exclusive + kBlendAlpha = 0x2, // dev + kBlendMult = 0x4, // dev + kBlendAdd = 0x8, // dev + kBlendMultColorPlusMultAlpha = 0x10, // dev + kBlendAntiAlias = 0x20, + kBlendDetail = 0x40, + kBlendDetailAdd = 0x80, + kBlendMask = kBlendAlpha + | kBlendMult + | kBlendAdd + | kBlendMultColorPlusMultAlpha + | kBlendAntiAlias + | kBlendDetail + | kBlendDetailAdd, + kBlendInvertAlpha = 0x1000, // dev + kBlendInvertColor = 0x2000, // dev + kBlendAlphaMult = 0x4000, + kBlendAlphaAdd = 0x8000, + kBlendNoColor = 0x10000, + kBlendNoVtxAlpha = 0x20000 + }; + + enum hsMatZFlags { + kZIncLayer = 0x1, // dev + kZOnlyZ = 0x2, // dev + kZClearZ = 0x4, // dev + kZNoZRead = 0x8, // dev + kZNoZWrite = 0x10, + kZMask = kZNoZWrite | kZClearZ | kZNoZRead, + kZLODBias = 0x20 + }; + + enum hsMatShadeFlags { + kShadeSoftShadow = 0x1, // view, dev + kShadeNoProjectors = 0x2, // projector + kShadeVertexShade = 0x20, // dev + kShadeNoShade = 0x40, // view,dev + kShadeBlack = kShadeNoShade, + kShadeSpecular = 0x80, // view, dev + kShadeNoFog = 0x100, // dev + kShadeWhite = 0x200, + kShadeSpecularAlpha = 0x400, + kShadeSpecularColor = 0x800, + kShadeSpecularHighlight = 0x1000, + kShadeVertColShade = 0x2000, + kShadeInherit = 0x4000 + }; + + enum hsMatMiscFlags { + kMiscWireFrame = 0x1, // dev (running out of bits) + kMiscDrawMeshOutlines = 0x2, // dev, currently unimplemented + kMiscTwoSided = 0x4, // view,dev + kMiscDrawAsSplats = 0x8, // dev? bwt + kMiscMipMap = 0x10, + kMiscUseBitmap = 0x20, + kMiscIntensityOnly = 0x40, + kMiscAutoStart = 0x80, + kMiscDetailBias = 0x100, // obsolete... + kMiscDetailMax = 0x200, // obsolete... + kMiscExplicitMipmap = 0x400, + kMiscAdjustPlane = 0x800, + kMiscAdjustCylinder = 0x1000, + kMiscAdjustSphere = 0x2000, + kMiscTroubledLoner = 0x4000, + kMiscBindSkip = 0x8000, + kMiscBindMask = 0x10000, + kMiscForceNonCompressed = 0x20000, + kMiscNoMaxSize = 0x40000, + kMiscHalfSize = 0x80000, + kMiscBindNext = 0x100000, + kMiscBindPrev = 0x200000, + kMiscReserved = 0x400000 + }; + + enum ProcType { + kProcTypeDefault, + kProcTypeWater + }; + + enum hsMatUsage { + kUseNone = 0x0, + kUseBase = 0x1, + kUseDetail = 0x2, + kUseGrime = 0x4, + kUseTransition = 0x8, + kUseHighlight = 0x10, + kUseMask = 0x20, + kUseShadowLight = 0x40, + kUseHelper = 0x80, + + kUseGuess = 0x10000000 + }; + +public: + // For hsMaxMtl... Special case for higher layers. Sigh. + virtual void SetDirty(BOOL state) = 0; + virtual void SetBlendFlag(int i, BOOL state) = 0; + virtual void SetZFlag(int flag, BOOL state) = 0; + virtual void SetShadeFlag(int flag, BOOL state) = 0; + virtual void SetMiscFlag(int flag, BOOL state) = 0; + virtual void SetProcType(ProcType type) = 0; + virtual void SetUsage(hsMatUsage use) = 0; + virtual void GuessUsage() = 0; + + // For interactive renderer + virtual Color GetAmbient(int mtlNum=0, BOOL backFace=FALSE) = 0; + virtual Color GetColor(int mtlNum=0, BOOL backFace=FALSE) = 0; + + virtual float GetShininess(int mtlNum=0, BOOL backFace=FALSE) = 0; + virtual float GetShinStr(int mtlNum=0, BOOL backFace=FALSE) = 0; + virtual float GetOpacity(int mtlNum=0, BOOL backFace=FALSE) = 0; + + // For exporter + virtual Color GetAmbient(TimeValue t) const = 0; + virtual Color GetColor(TimeValue t) const = 0; + + virtual float GetShininess(TimeValue t) const = 0; + virtual float GetShinStr(TimeValue t) const = 0; + virtual float GetMapPercent(TimeValue t) const = 0; + virtual float GetOpacity(TimeValue t) const = 0; + virtual float GetMipMapBlur(TimeValue t) const = 0; + virtual float GetLODBias(TimeValue t) const = 0; + virtual float GetDetailDropoffStart(TimeValue t) const = 0; + virtual float GetDetailDropoffStop(TimeValue t) const = 0; + virtual float GetDetailMax(TimeValue t) const = 0; + virtual float GetDetailMin(TimeValue t) const = 0; + virtual int GetEnvironMapSize(TimeValue t) const = 0; + + virtual BOOL GetDirty() const = 0; + virtual ULONG GetBlendFlags() const = 0; + virtual ULONG GetZFlags() const = 0; + virtual ULONG GetShadeFlags() const = 0; + virtual ULONG GetMiscFlags() const = 0; + virtual ProcType GetProcType() const = 0; + virtual hsMatUsage GetUsage() const = 0; + + virtual int GetNumExplicitMipmaps() const = 0; + virtual TCHAR *GetExplicitMipmapName(int i) const = 0; + virtual BOOL ExplicitMipmapEnabled(int i) const = 0; + virtual int GetExplicitMipmapLevel(int i) const = 0; + +#ifdef MAXR4 + // KLUDGE - Had to do this to compile under MAX4 beta + virtual void fnReload() {}; + virtual void fnViewImage() {}; +#endif +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.cpp new file mode 100644 index 00000000..03d6b9d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.cpp @@ -0,0 +1,395 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plMaterialUpdate.h" + +#define MAXR3 +#define MAXR4 + +//#include "OldMat/hsMaxMtl.h" +#include "OldMat/hsMaxLayer.h" + +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" +#include "../MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h" + +#include "../MaxPlasmaMtls/Materials/plPassMtl.h" +#include "../MaxPlasmaMtls/Materials/plPassMtlBase.h" +#include "../MaxPlasmaMtls/Materials/plPassMtlBasicPB.h" +#include "../MaxPlasmaMtls/Materials/plPassMtlAdvPB.h" +#include "../MaxPlasmaMtls/Materials/plPassMtlLayersPB.h" + +#include "../MaxExport/plExportProgressBar.h" + +#define PLMATERIALUPDATE_CLASS_ID Class_ID(0x70acddfe, 0x68f42f3f) + +#include + +class plMaterialUpdate : public UtilityObj +{ +protected: + HWND fhPanel; + Interface *fInterface; + std::map fDoneMaterials; + bool fConvertSecondLayer; + + plMaterialUpdate(); + +public: + static plMaterialUpdate &Instance(); + + ~plMaterialUpdate(); + void DeleteThis() {} + + void BeginEditParams(Interface *ip,IUtil *iu); + void EndEditParams(Interface *ip,IUtil *iu); + + bool ConvertAllMtls(INode *node, plExportProgressBar *bar); + +protected: + static BOOL CALLBACK DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + + void IRenameMtls(MtlBase *oldMtl, MtlBase *newMtl); + + void IConvert(INode *node); + plPassMtl *IConvertMtl(Mtl *mtl, Mtl *multi=NULL, int subNum=-1); + plLayerTex *IConvertLayer(hsMaxLayer *layer); + + void ICopyMtlParams(plPassMtl *mtl, hsMaxLayer *layer); +}; + +class plMaterialUpdateClassDesc:public ClassDesc2 { + public: + int IsPublic() { return TRUE; } + void * Create(BOOL loading = FALSE) { return &plMaterialUpdate::Instance(); } + const TCHAR * ClassName() { return "Plasma Material Converter"; } + SClass_ID SuperClassID() { return UTILITY_CLASS_ID; } + Class_ID ClassID() { return PLMATERIALUPDATE_CLASS_ID; } + const TCHAR* Category() { return GetString(IDS_CATEGORY); } + + const TCHAR* InternalName() { return _T("plMaterialConverter"); } // returns fixed parsable name (scripter-visible name) + HINSTANCE HInstance() { return hInstance; } // returns owning module handle +}; + +static plMaterialUpdateClassDesc plMaterialUpdateDesc; +ClassDesc2* GetMaterialUpdateDesc() { return &plMaterialUpdateDesc; } + +plMaterialUpdate &plMaterialUpdate::Instance() +{ + static plMaterialUpdate theInstance; + return theInstance; +} + +plMaterialUpdate::plMaterialUpdate() : fInterface(NULL), fhPanel(NULL), fConvertSecondLayer(false) +{ +} + +plMaterialUpdate::~plMaterialUpdate() +{ +} + +BOOL plMaterialUpdate::DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_COMMAND: + if (LOWORD(wParam) == IDC_BUTTON1 && HIWORD(wParam) == BN_CLICKED) + { + plMaterialUpdate &p = Instance(); + + plExportProgressBar bar; + bar.Start("Convert Materials"); + + if (IsDlgButtonChecked(hWnd, IDC_CHECK1) == BST_CHECKED) + p.fConvertSecondLayer = true; + + p.ConvertAllMtls(p.fInterface->GetRootNode(), &bar); + + p.fInterface->RedrawViews(p.fInterface->GetTime()); + p.fDoneMaterials.clear(); + return TRUE; + } + break; + } + + return FALSE; +} + +void plMaterialUpdate::BeginEditParams(Interface *ip, IUtil *iu) +{ + fInterface = ip; + fhPanel = fInterface->AddRollupPage( + hInstance, + MAKEINTRESOURCE(IDD_PANEL), + DlgProc, + GetString(IDS_PARAMS), + 0); +} + +void plMaterialUpdate::EndEditParams(Interface *ip, IUtil *iu) +{ + fInterface->DeleteRollupPage(fhPanel); + fInterface = NULL; + fhPanel = NULL; +} + +bool plMaterialUpdate::ConvertAllMtls(INode *node, plExportProgressBar *bar) +{ + IConvert(node); + + bool cancel = bar->Update(); + if (cancel) + return false; + + for (int i = 0; i < node->NumChildren(); i++) + { + if (!ConvertAllMtls(node->GetChildNode(i), bar)) + return false; + } + + return true; +} + +void plMaterialUpdate::IConvert(INode *node) +{ + Mtl *mtl = node->GetMtl(); + if (!mtl) + return; + + if (mtl->ClassID() == hsMaxMtlClassID) + { + plPassMtl *pass = IConvertMtl(mtl); + node->SetMtl(pass); + } + else if (mtl->ClassID() == Class_ID(MULTI_CLASS_ID,0)) + { + for (int i = 0; i < mtl->NumSubMtls(); i++) + { + Mtl *subMtl = mtl->GetSubMtl(i); + if (subMtl->ClassID() == hsMaxMtlClassID) + { + plPassMtl *pass = IConvertMtl(subMtl, mtl, i); + mtl->SetSubMtl(i, pass); + } + } + } +} + +void plMaterialUpdate::IRenameMtls(MtlBase *oldMtl, MtlBase *newMtl) +{ + char buf[256]; + const char *name = oldMtl->GetName(); + newMtl->SetName(name); + strcpy(buf, name); + strcat(buf, " old"); + oldMtl->SetName(buf); +} + +plPassMtl *plMaterialUpdate::IConvertMtl(Mtl *mtl, Mtl *multi, int subNum) +{ + // We've already converted this material, use the new one we already made + if (fDoneMaterials.find(mtl) != fDoneMaterials.end()) + return (plPassMtl*)fDoneMaterials[mtl]; + + plPassMtl *newMtl = (plPassMtl*)CreateInstance(MATERIAL_CLASS_ID, PASS_MTL_CLASS_ID); + IParamBlock2 *layersPB = newMtl->GetParamBlockByID(plPassMtl::kBlkLayers); + + IRenameMtls(mtl, newMtl); + + if (mtl->NumSubTexmaps() > 0) + { + Texmap *map = mtl->GetSubTexmap(0); + if (map->ClassID() == hsMaxLayerClassID) + { + plLayerTex *layer = IConvertLayer((hsMaxLayer*)map); + +// layer->SetMtlFlag(MTL_TEX_DISPLAY_ENABLED, TRUE); +// layer->ActivateTexDisplay(TRUE); + + newMtl->SetSubTexmap(0, layer); +/* newMtl->SetActiveTexmap(layer); + newMtl->SetMtlFlag(MTL_TEX_DISPLAY_ENABLED, TRUE); + + if (multi) + GetCOREInterface()->ActivateTexture(layer, multi, subNum); + else + GetCOREInterface()->ActivateTexture(layer, newMtl); +*/ + ICopyMtlParams(newMtl, (hsMaxLayer*)map); + } + } + + if (mtl->NumSubTexmaps() > 1 && fConvertSecondLayer && mtl->SubTexmapOn(1)) + { + Texmap *map = mtl->GetSubTexmap(1); + if (map->ClassID() == hsMaxLayerClassID) + { + hsMaxLayer *oldLayer = (hsMaxLayer*)map; + plLayerTex *layer = IConvertLayer(oldLayer); + newMtl->SetSubTexmap(1, layer); + + IParamBlock2 *layersPB = newMtl->GetParamBlockByID(plPassMtl::kBlkLayers); + layersPB->SetValue(kPassLayTopOn, 0, TRUE); + + if (oldLayer->GetBlendFlags() & hsMaxLayerBase::kBlendAlpha) + layersPB->SetValue(kPassLayBlend, 0, plPassMtlBase::kBlendAlpha); + else if (oldLayer->GetBlendFlags() & hsMaxLayerBase::kBlendAdd) + layersPB->SetValue(kPassLayBlend, 0, plPassMtlBase::kBlendAdd); + } + } + +//MtlBaseLib& Interface::GetMaterialLibrary() + + // Add this to our converted materials + fDoneMaterials[mtl] = newMtl; + + return newMtl; +} + +plLayerTex *plMaterialUpdate::IConvertLayer(hsMaxLayer *layer) +{ + plLayerTex *newLayer = (plLayerTex*)CreateInstance(TEXMAP_CLASS_ID, LAYER_TEX_CLASS_ID); + IParamBlock2 *bitmapPB = newLayer->GetParamBlockByID(plLayerTex::kBlkBitmap); + + IRenameMtls(layer, newLayer); + + // Copy the bitmap + if (layer->GetMiscFlags() & hsMaxLayerBase::kMiscUseBitmap) + { + bitmapPB->SetValue(kBmpUseBitmap, 0, 1); + + const char *name = layer->GetMapName(); + PBBitmap pbb; + pbb.bi.SetName(name); + + // Disable annoying missing texture warning + BOOL bmmSilentMode = TheManager->SilentMode(); + TheManager->SetSilentMode(TRUE); + + bitmapPB->SetValue(kBmpBitmap, 0, &pbb); + + TheManager->SetSilentMode(bmmSilentMode); + } + + // Copy the UVGen + newLayer->ReplaceReference(plLayerTex::kRefUVGen, layer->GetUVGen()); + + // Copy the cropping + if (layer->GetApplyCrop()) + { + bitmapPB->SetValue(kBmpApply, 0, TRUE); + bitmapPB->SetValue(kBmpCropPlace, 0, layer->GetPlaceImage()); + + bitmapPB->SetValue(kBmpClipU, 0, layer->GetClipU(0)); + bitmapPB->SetValue(kBmpClipV, 0, layer->GetClipV(0)); + bitmapPB->SetValue(kBmpClipW, 0, layer->GetClipW(0)); + bitmapPB->SetValue(kBmpClipH, 0, layer->GetClipH(0)); + } + + // Misc + if (layer->GetBlendFlags() & hsMaxLayerBase::kBlendNoColor) + bitmapPB->SetValue(kBmpDiscardColor, 0, TRUE); + if (layer->GetBlendFlags() & hsMaxLayerBase::kBlendInvertColor) + bitmapPB->SetValue(kBmpInvertColor, 0, TRUE); + if (layer->GetAlphaSource() == 2) + bitmapPB->SetValue(kBmpDiscardAlpha, 0, TRUE); + if (layer->GetBlendFlags() & hsMaxLayerBase::kBlendInvertAlpha) + bitmapPB->SetValue(kBmpInvertAlpha, 0, TRUE); + +/* + // Texture quality + kBmpNonCompressed, + kBmpScaling, +*/ + + // Mipmap + if (layer->GetFilterType() == 2) + bitmapPB->SetValue(kBmpNoFilter, 0, TRUE); + + float blur = layer->GetMipMapBlur(TimeValue(0)); + bitmapPB->SetValue(kBmpMipBlur, 0, blur); + + if (layer->GetZFlags() & hsMaxLayerBase::kZLODBias) + { + bitmapPB->SetValue(kBmpMipBias, 0, TRUE); + bitmapPB->SetValue(kBmpMipBiasAmt, 0, layer->GetLODBias(TimeValue(0))); + } + + // Detail + if (layer->GetBlendFlags() & hsMaxLayerBase::kBlendDetail || + layer->GetBlendFlags() & hsMaxLayerBase::kBlendDetailAdd) + { + bitmapPB->SetValue(kBmpUseDetail, 0, TRUE); + + bitmapPB->SetValue(kBmpDetailStartSize, 0, layer->GetDetailDropoffStart(0)); + bitmapPB->SetValue(kBmpDetailStopSize, 0, layer->GetDetailDropoffStop(0)); + bitmapPB->SetValue(kBmpDetailStartOpac, 0, layer->GetDetailMax(0)); + bitmapPB->SetValue(kBmpDetailStopOpac, 0, layer->GetDetailMin(0)); + } + + return newLayer; +} + +void plMaterialUpdate::ICopyMtlParams(plPassMtl *mtl, hsMaxLayer *layer) +{ + IParamBlock2 *basicPB = mtl->GetParamBlockByID(plPassMtl::kBlkBasic); + IParamBlock2 *layersPB = mtl->GetParamBlockByID(plPassMtl::kBlkLayers); + + basicPB->SetValue(kPassBasColorAmb, 0, layer->GetAmbient()); + basicPB->SetValue(kPassBasColor, 0, layer->GetColor()); + + basicPB->SetValue(kPassBasOpacity, 0, int(layer->GetOpacity(0)*100.f)); + + if (layer->GetBlendFlags() & hsMaxLayerBase::kBlendAlpha) + layersPB->SetValue(kPassLayOutputBlend, 0, plPassMtlBase::kBlendAlpha); + else if (layer->GetBlendFlags() & hsMaxLayerBase::kBlendAdd) + layersPB->SetValue(kPassLayOutputBlend, 0, plPassMtlBase::kBlendAdd); + +/* + kPassAdvUseSpec, + kPassAdvSpecType, + kPassAdvShine, + kPassAdvShineStr, + + // Misc + kPassAdvWire, + kPassAdvMeshOutlines, + kPassAdvTwoSided, + + // Shading + kPassAdvSoftShadow, + kPassAdvNoProj, + kPassAdvVertexShade, + kPassAdvNoShade, + kPassAdvNoFog, + kPassAdvWhite, + + // Z + kPassAdvZOnly, + kPassAdvZClear, + kPassAdvZNoRead, + kPassAdvZNoWrite, + kPassAdvZInc, +*/ +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.def b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.def new file mode 100644 index 00000000..564b0a2e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.def @@ -0,0 +1,8 @@ +LIBRARY +EXPORTS + LibDescription @1 + LibNumberClasses @2 + LibClassDesc @3 + LibVersion @4 +SECTIONS + .data READ WRITE diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.h new file mode 100644 index 00000000..b9d9df9c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.h @@ -0,0 +1,55 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/********************************************************************** + *< + FILE: plMaterialUpdate.h + + DESCRIPTION: Includes for Plugins + + CREATED BY: + + HISTORY: + + *> Copyright (c) 2000, All Rights Reserved. + **********************************************************************/ + +#ifndef __PLMATERIALUPDATE__H +#define __PLMATERIALUPDATE__H + +#include "Max.h" +#include "resource.h" +#include "istdplug.h" +#include "iparamb2.h" +#include "iparamm2.h" + +#include "utilapi.h" + + +extern TCHAR *GetString(int id); + +extern HINSTANCE hInstance; + +#endif // __PLMATERIALUPDATE__H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.rc new file mode 100644 index 00000000..b4cf371c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/plMaterialUpdate.rc @@ -0,0 +1,110 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PANEL DIALOG DISCARDABLE 0, 0, 108, 45 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Convert All",IDC_BUTTON1,29,9,48,15 + CONTROL "Convert 2nd layer",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,18,29,71,10 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_PANEL, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 38 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CLASS_NAME "plMaterialUpdate" + IDS_PARAMS "Parameters" + IDS_SPIN "Spin" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/resource.h new file mode 100644 index 00000000..9d10fab2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxMtlUpdate/resource.h @@ -0,0 +1,28 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by plMaterialUpdate.rc +// +#define IDS_LIBDESCRIPTION 1 +#define IDS_CATEGORY 2 +#define IDS_CLASS_NAME 3 +#define IDS_PARAMS 4 +#define IDS_SPIN 5 +#define IDD_PANEL 101 +#define IDC_CLOSEBUTTON 1000 +#define IDC_DOSTUFF 1000 +#define IDC_BUTTON1 1001 +#define IDC_CHECK1 1002 +#define IDC_COLOR 1456 +#define IDC_EDIT 1490 +#define IDC_SPIN 1496 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1003 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/DLLEntry.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/DLLEntry.cpp new file mode 100644 index 00000000..16015c21 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/DLLEntry.cpp @@ -0,0 +1,96 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plRealTimeLightBase.h" +#include "plRealTimeLights.h" +#include "plRTProjDirLightClassDesc.h" + +#include "../pnMessage/pnMessageCreatable.h" +#include "../pnKeyedObject/pnKeyedObjectCreatable.h" +#include "../pnNetCommon/pnNetCommonCreatable.h" + +#include "../plSurface/plLayerInterface.h" +REGISTER_NONCREATABLE( plLayerInterface ); + + +HINSTANCE hInstance; +int controlsInit = FALSE; + +// This function is called by Windows when the DLL is loaded. This +// function may also be called many times during time critical operations +// like rendering. Therefore developers need to be careful what they +// do inside this function. In the code below, note how after the DLL is +// loaded the first time only a few statements are executed. +BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) +{ + hInstance = hinstDLL; // Hang on to this DLL's instance handle. + + if (!controlsInit) + { + controlsInit = TRUE; + InitCustomControls(hInstance); // Initialize MAX's custom controls + InitCommonControls(); // Initialize Win95 controls + } + + return (TRUE); +} + +__declspec(dllexport) const TCHAR* LibDescription() +{ + return NULL; +} + +__declspec(dllexport) int LibNumberClasses() +{ + return 4; +} + +__declspec(dllexport) ClassDesc* LibClassDesc(int i) +{ + switch (i) + { + case 0: return (ClassDesc*)plRTSpotLightDesc::GetDesc(); + case 1: return (ClassDesc*)plRTOmniLightDesc::GetDesc(); + case 2: return (ClassDesc*)plRTDirLightDesc::GetDesc(); + case 3: return (ClassDesc*)plRTProjDirLightDesc::GetDesc(); + default: return 0; + } +} + +__declspec(dllexport) ULONG LibVersion() +{ + return VERSION_3DSMAX; +} + +TCHAR *GetString(int id) +{ + static TCHAR buf[256]; + + if (hInstance) + return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL; + return NULL; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLightBaseAnimDlgProc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLightBaseAnimDlgProc.h new file mode 100644 index 00000000..cb9ca422 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLightBaseAnimDlgProc.h @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// Animation Rollout Dialog Proc Class Definition // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.6.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#if 0//ndef _plRTLightBaseAnimDlgProc_h +#define _plRTLightBaseAnimDlgProc_h + +#include "../MaxComponent/plNotetrackDlg.h" + +/////////////////////////////////////////////////////////////////////////////// +//// Dialog Proc for Animation Rollout //////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class plRTLightBaseAnimDlgProc : public ParamMap2UserDlgProc +{ + protected: + plNoteTrackDlg fNoteTrackDlg; + + HWND fhWnd; + + static plRTLightBaseAnimDlgProc fInstance; + static const char *kDecalNameNone; + + public: + BOOL DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); + void DeleteThis() { fNoteTrackDlg.DeleteCache(); } + void SetThing( ReferenceTarget *m ); + + static plRTLightBaseAnimDlgProc *Instance() { return &fInstance; } + + protected: + void IInitControls( Animatable *anim, IParamBlock2 *pb ); + +}; + + +#endif //_plRTLightBaseAnimDlgProc_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLightBaseAnimPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLightBaseAnimPBDec.h new file mode 100644 index 00000000..8db9ae18 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLightBaseAnimPBDec.h @@ -0,0 +1,68 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// Animation Rollout ParamBlock for Runtime Lights // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.3.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#if 0//ndef _plRTLightBaseAnimPB_h +#define _plRTLightBaseAnimPB_h + +/////////////////////////////////////////////////////////////////////////////// +//// Animation Rollout ParamBlock ///////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ParamBlockDesc2 plRTLightBaseAnimPB +( + plRTLightBase::kBlkAnim, _T("anim"), IDS_LIGHT_ANIM, nil, // To be added manually + P_AUTO_CONSTRUCT + P_AUTO_UI, plRTLightBase::kRefAnimParams, + + // UI + IDD_LIGHT_ANIM, IDS_LIGHT_ANIM, 0, APPENDROLL_CLOSED, plRTLightBaseAnimDlgProc::Instance(), + + plRTLightBase::kAnimName, _T("animName"), TYPE_STRING, 0, 0, + end, + + plRTLightBase::kAnimAutoStart, _T("autoStart"), TYPE_BOOL, 0, 0, + end, + + plRTLightBase::kAnimLoop, _T("loop"), TYPE_BOOL, 0, 0, + end, + plRTLightBase::kAnimLoopName, _T("loopName"), TYPE_STRING, 0, 0, + end, + + end +); + + +#endif //_plRTLightBaseAnimPB_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLights.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLights.rc new file mode 100644 index 00000000..a8e4a9fd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTLights.rc @@ -0,0 +1,682 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_LIGHT_PARAM DIALOG DISCARDABLE 0, 0, 108, 60 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Diffuse:",IDC_LIGHT_DIFFUSE,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,36,5,38,10 + CONTROL "User1",IDC_LIGHT_COLOR,"ColorSwatch",WS_GROUP,80,4,22, + 12 + LTEXT "Multiplier:",IDC_STATIC,27,39,30,8 + CONTROL "",IDC_LMULT,"CustEdit",WS_GROUP | WS_TABSTOP,59,39,26, + 10 + CONTROL "",IDC_LMULTSPINNER,"SpinnerControl",WS_GROUP,86,39,7,10 + CONTROL "Specular:",IDC_AFFECT_SPECULAR,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,36,20,42,10 + CONTROL "User1",IDC_LIGHT_COLOR_SPECULAR,"ColorSwatch",WS_GROUP, + 80,19,22,12 + CONTROL "On",IDC_LIGHT_ON,"Button",BS_AUTOCHECKBOX | WS_GROUP | + WS_TABSTOP,7,12,24,10 + CONTROL "STUB-Ambient Only",IDC_AMBIENT_ONLY_STUB,"Button", + BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,0,50,32,9 +END + +IDD_SPOTLIGHT DIALOG DISCARDABLE 0, 0, 108, 120 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Light Cone:",IDC_STATIC,2,1,103,75,WS_GROUP + CONTROL "Show Cone ",IDC_SHOW_CONE,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,5,11,50,10 + CONTROL "Overshoot",IDC_OVERSHOOT,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,57,11,45,10 + RTEXT "Hotspot:",ID_STATIC,12,25,33,8 + CONTROL "",IDC_LHOTSIZE,"CustEdit",WS_GROUP | WS_TABSTOP,49,24, + 36,10 + CONTROL "",IDC_LHOTSIZESPINNER,"SpinnerControl",WS_GROUP,86,24,7, + 10 + RTEXT "Falloff:",IDC_STATIC,12,36,33,8 + CONTROL "",IDC_LFALLOFF,"CustEdit",WS_GROUP | WS_TABSTOP,49,35, + 36,10 + CONTROL "",IDC_LFALLOFFSPINNER,"SpinnerControl",WS_GROUP,86,35,7, + 10 + CONTROL "Circle",IDC_CIRCLE_LIGHT,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,11,48,30,10 + CONTROL "Rectangle",IDC_RECT_LIGHT,"Button",BS_AUTORADIOBUTTON, + 49,48,45,10 + RTEXT "Aspect:",IDC_STATIC,4,61,24,8 + CONTROL "",IDC_LASPECT,"CustEdit",WS_GROUP | WS_TABSTOP,29,61,23, + 10 + CONTROL "",IDC_LASPECT_SPIN,"SpinnerControl",WS_GROUP,53,61,7,10 + PUSHBUTTON "Bitmap Fit...",IDC_BITMAP_FIT,62,60,40,12,WS_GROUP + GROUPBOX "Projector Map:",IDC_STATIC,2,78,103,26,WS_GROUP + CONTROL "",IDC_PROJECTOR,"Button",BS_AUTOCHECKBOX | BS_RIGHT | + WS_GROUP | WS_TABSTOP,9,90,9,8 + CONTROL "Custom1",IDC_PROJ_MAPNAME,"CustButton",WS_GROUP | + WS_TABSTOP,21,88,67,12 + LTEXT "Target Distance:",IDC_STATIC,6,108,55,10 + RTEXT "XXXXX",IDC_TARG_DISTANCE,65,108,36,8 +END + +IDD_FREE_SPOTLIGHT DIALOG DISCARDABLE 0, 0, 108, 178 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Light Cone:",IDC_STATIC,2,2,103,39,WS_GROUP + CONTROL "Show Cone ",IDC_SHOW_CONE,"Button",BS_AUTOCHECKBOX | + NOT WS_VISIBLE | WS_GROUP | WS_TABSTOP,6,13,14,10 + RTEXT "Hotspot:",IDC_STATIC,12,14,33,8 + CONTROL "",IDC_LHOTSIZE,"CustEdit",WS_GROUP | WS_TABSTOP,49,14, + 36,10 + CONTROL "",IDC_LHOTSIZESPINNER,"SpinnerControl",WS_GROUP,86,14,7, + 10 + RTEXT "Falloff:",IDC_STATIC,12,25,33,8 + CONTROL "",IDC_LFALLOFF,"CustEdit",WS_GROUP | WS_TABSTOP,49,25, + 36,10 + CONTROL "",IDC_LFALLOFFSPINNER,"SpinnerControl",WS_GROUP,86,25,7, + 10 + GROUPBOX "Projector Map:",IDC_STATIC,1,42,104,132,WS_GROUP + CONTROL "Map:",IDC_PROJECTOR,"Button",BS_AUTOCHECKBOX | BS_RIGHT | + WS_GROUP | WS_TABSTOP,5,52,28,10 + CONTROL "Custom1",IDC_PROJ_MAPNAME,"CustButton",WS_GROUP | + WS_TABSTOP,35,52,68,11 + CONTROL "Illuminate",IDC_ILLUMINATE,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,11,76,48,10 + GROUPBOX "Mode",IDC_STATIC,5,66,92,58 + CONTROL "Add",IDC_ADD,"Button",BS_AUTORADIOBUTTON | WS_GROUP | + WS_TABSTOP,11,87,48,10 + CONTROL "Multiply",IDC_MULT,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,11,98,48,10 + CONTROL "Brighten",IDC_MADD,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,11,109,48,10 + GROUPBOX "Texture",IDC_STATIC,5,126,91,40 + CONTROL "Don't Compress",IDC_PROJ_NOCOMPRESS,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_GROUP | WS_TABSTOP,11,138, + 67,10 + CONTROL "Don't Mip",IDC_PROJ_NOMIP,"Button",BS_AUTOCHECKBOX | + BS_LEFT | WS_GROUP | WS_TABSTOP,11,150,67,10 +END + +IDD_OMNI DIALOG DISCARDABLE 0, 0, 108, 20 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Map:",IDC_PROJECTOR,"Button",BS_AUTOCHECKBOX | BS_RIGHT | + WS_GROUP | WS_TABSTOP,4,5,27,10 + CONTROL "Custom1",IDC_PROJ_MAPNAME,"CustButton",WS_GROUP | + WS_TABSTOP,32,4,73,12 +END + +IDD_LIGHT_ATTEN DIALOG DISCARDABLE 0, 0, 108, 72 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX " ",IDC_STATIC,4,2,100,65, + WS_GROUP + CONTROL "Use Attenuation",IDC_LIGHT_ATTENBOOL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,2,67,10 + LTEXT "Max Distance:",IDC_STATIC,7,16,46,8 + CONTROL "",IDC_LIGHT_ATTEN,"CustEdit",WS_GROUP | WS_TABSTOP,55, + 15,36,10 + CONTROL "",IDC_LIGHT_ATTEN_SPIN,"SpinnerControl",WS_GROUP,92,15, + 7,10 + CONTROL "Linear Falloff",IDC_LIGHT_ATTEN_LINEAR_RADIO,"Button", + BS_AUTORADIOBUTTON,11,30,56,10 + CONTROL "Quadratic Falloff",IDC_LIGHT_ATTEN_QUAD_RADIO,"Button", + BS_AUTORADIOBUTTON,11,41,67,10 + CONTROL "No Falloff",IDC_LIGHT_ATTEN_NONE_RADIO,"Button", + BS_AUTORADIOBUTTON,11,52,46,10 +END + +IDD_LIGHT_SHADOW3 DIALOG DISCARDABLE 0, 0, 108, 212 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Use Global Settings",IDC_GLOBAL_SET,"Button", + BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,5,5,76,10 + GROUPBOX "Shadow Type:",IDC_STATIC,1,17,105,30,WS_GROUP + CONTROL "Shadow Maps",IDC_SHADOW_MAPS,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,6,26,72,10 + CONTROL "Ray-Traced Shadows",IDC_RAY_TRACED_SHADOWS,"Button", + BS_AUTORADIOBUTTON,6,35,87,10 + GROUPBOX "Shadow Maps:",IDC_STATIC,1,51,105,42,WS_GROUP + LTEXT "Bias",IDC_STATIC,9,60,14,8 + CONTROL "",IDC_MAP_BIAS,"CustEdit",WS_GROUP | WS_TABSTOP,4,69,21, + 10 + CONTROL "",IDC_MAP_BIAS_SPIN,"SpinnerControl",WS_GROUP,25,69,8, + 10 + LTEXT "Size",IDC_STATIC,43,60,15,8 + CONTROL "",IDC_MAP_SIZE,"CustEdit",WS_GROUP | WS_TABSTOP,35,69, + 26,10 + CONTROL "",IDC_MAP_SIZE_SPIN,"SpinnerControl",WS_GROUP,61,69,8, + 10 + LTEXT "Smp Range",IDC_STATIC,67,60,38,8 + CONTROL "",IDC_MAP_RANGE,"CustEdit",WS_GROUP | WS_TABSTOP,73,69, + 21,10 + CONTROL "",IDC_MAP_RANGE_SPIN,"SpinnerControl",WS_GROUP,94,69,8, + 10 + CONTROL "Absolute Map Bias",IDC_ABS_MAP_BIAS,"Button", + BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,8,81,72,10 + GROUPBOX "Ray Traced Shadows:",IDC_STATIC,1,97,105,24,WS_GROUP + RTEXT "Bias:",IDC_STATIC,23,109,20,8 + CONTROL "",IDC_RT_BIAS,"CustEdit",WS_GROUP | WS_TABSTOP,47,108, + 21,10 + CONTROL "",IDC_RT_BIAS_SPIN,"SpinnerControl",WS_GROUP,68,108,7, + 10 + GROUPBOX "Shadow Color:",IDC_STATIC,1,125,105,41,WS_GROUP + CONTROL "User1",IDC_SHADOW_COLOR,"ColorSwatch",WS_GROUP,5,140,25, + 12 + RTEXT "Map:",IDC_STATIC,56,131,18,8 + CONTROL "Custom1",IDC_SHADPROJ_MAPNAME,"CustButton",WS_GROUP | + WS_TABSTOP,32,140,69,12 + CONTROL "Light Affects Shadow Color",IDC_LT_EFFECT_SHADCOL, + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,4,154, + 99,10 + GROUPBOX "Atmospheric Shadowing:",IDC_STATIC,1,171,105,38, + WS_GROUP + CONTROL "On",IDC_ATMOS_SHADOWS,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,7,184,27,10 + LTEXT "Opacity:",IDC_STATIC,40,184,27,8 + CONTROL "",IDC_ATM_OPACITY,"CustEdit",WS_GROUP | WS_TABSTOP,68, + 183,27,10 + CONTROL "",IDC_ATM_OPACITY_SPIN,"SpinnerControl",WS_GROUP,95,183, + 8,10 + LTEXT "Color Amount:",IDC_STATIC,23,196,45,8 + CONTROL "",IDC_ATM_COLAMT,"CustEdit",WS_GROUP | WS_TABSTOP,68, + 195,27,10 + CONTROL "",IDC_ATM_COLAMT_SPIN,"SpinnerControl",WS_GROUP,95,195, + 8,10 +END + +IDD_LIGHT_SHADOW2 DIALOG DISCARDABLE 0, 0, 108, 146 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Object Shadows:",IDC_STATIC,1,1,105,97,WS_GROUP + CONTROL "On",IDC_OBJECT_SHADOWS,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,5,11,22,10 + COMBOBOX IDC_SHADOW_TYPE,9,22,91,64,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_GROUP | WS_TABSTOP + CONTROL "Use Global Settings",IDC_GLOBAL_SET,"Button", + BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,9,36,78,10 + RTEXT "Color:",IDC_STATIC,4,55,19,8 + CONTROL "User1",IDC_SHADOW_COLOR,"ColorSwatch",WS_GROUP,24,53,15, + 12 + LTEXT "Dens.",IDC_STATIC,44,55,20,8 + CONTROL "",IDC_SHAD_MULT,"CustEdit",WS_GROUP | WS_TABSTOP,66,54, + 27,10 + CONTROL "",IDC_SHAD_MULT_SPIN,"SpinnerControl",WS_GROUP,94,54,7, + 10 + CONTROL "Map:",IDC_USE_SHAD_COLMAP,"Button",BS_AUTOCHECKBOX | + BS_RIGHT | WS_GROUP | WS_TABSTOP,5,70,30,10 + CONTROL "Custom1",IDC_SHADPROJ_MAPNAME,"CustButton",WS_GROUP | + WS_TABSTOP,35,69,66,12 + CONTROL "Light Affects Shadow Color",IDC_LT_EFFECT_SHADCOL, + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,5,85,98, + 10 + GROUPBOX "Atmosphere Shadows:",IDC_STATIC,1,103,105,40,WS_GROUP + CONTROL "On",IDC_ATMOS_SHADOWS,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,6,113,22,10 + LTEXT "Opacity:",IDC_STATIC,39,117,27,8 + CONTROL "",IDC_ATM_OPACITY,"CustEdit",WS_GROUP | WS_TABSTOP,67, + 116,27,10 + CONTROL "",IDC_ATM_OPACITY_SPIN,"SpinnerControl",WS_GROUP,94,116, + 8,10 + LTEXT "Color Amount:",IDC_STATIC,22,129,45,8 + CONTROL "",IDC_ATM_COLAMT,"CustEdit",WS_GROUP | WS_TABSTOP,67, + 128,27,10 + CONTROL "",IDC_ATM_COLAMT_SPIN,"SpinnerControl",WS_GROUP,94,128, + 8,10 +END + +IDD_LIGHT_SHADOW1 DIALOG DISCARDABLE 0, 0, 108, 142 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "Object Shadows:",IDC_STATIC,1,1,105,94,WS_GROUP + CONTROL "On",IDC_OBJECT_SHADOWS,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,8,12,22,10 + COMBOBOX IDC_SHADOW_TYPE,34,12,69,64,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_GROUP | WS_TABSTOP + CONTROL "Use Global Settings",IDC_GLOBAL_SET,"Button", + BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,8,26,76,10 + LTEXT "Shadow Density:",IDC_STATIC,6,46,54,8 + CONTROL "",IDC_SHAD_MULT,"CustEdit",WS_GROUP | WS_TABSTOP,65,45, + 27,10 + CONTROL "",IDC_SHAD_MULT_SPIN,"SpinnerControl",WS_GROUP,93,45,7, + 10 + RTEXT "Color:",IDC_STATIC,5,56,22,8 + RTEXT "Map:",IDC_STATIC,49,56,18,8 + CONTROL "User1",IDC_SHADOW_COLOR,"ColorSwatch",WS_GROUP,5,65,22, + 12 + CONTROL "Custom1",IDC_SHADPROJ_MAPNAME,"CustButton",WS_GROUP | + WS_TABSTOP,28,65,61,12 + CONTROL "",IDC_USE_SHAD_COLMAP,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,92,66,9,10 + CONTROL "Light Affects Shadow Color",IDC_LT_EFFECT_SHADCOL, + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,5,81,99, + 10 + GROUPBOX "Atmospheric Shadows:",IDC_STATIC,1,99,105,40,WS_GROUP + CONTROL "On",IDC_ATMOS_SHADOWS,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,8,109,22,10 + LTEXT "Opacity:",IDC_STATIC,39,113,27,8 + CONTROL "",IDC_ATM_OPACITY,"CustEdit",WS_GROUP | WS_TABSTOP,67, + 112,27,10 + CONTROL "",IDC_ATM_OPACITY_SPIN,"SpinnerControl",WS_GROUP,94,112, + 8,10 + LTEXT "Color Amount:",IDC_STATIC,22,125,45,8 + CONTROL "",IDC_ATM_COLAMT,"CustEdit",WS_GROUP | WS_TABSTOP,67, + 124,27,10 + CONTROL "",IDC_ATM_COLAMT_SPIN,"SpinnerControl",WS_GROUP,94,124, + 8,10 +END + +IDD_SHADOWMAP_PARAMS DIALOG DISCARDABLE 0, 0, 108, 48 +STYLE WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + RTEXT "Bias:",IDC_STATIC,3,5,17,8 + CONTROL "",IDC_SHADBIAS,"CustEdit",WS_GROUP | WS_TABSTOP,22,4,21, + 10 + CONTROL "",IDC_SHADBIAS_SPIN,"SpinnerControl",WS_GROUP,44,4,7,10 + RTEXT "Size:",IDC_STATIC,53,5,17,8 + CONTROL "",IDC_SHADSIZE,"CustEdit",WS_GROUP | WS_TABSTOP,73,4,21, + 10 + CONTROL "",IDC_SHADSIZE_SPIN,"SpinnerControl",WS_GROUP,95,4,7,10 + RTEXT "Sample Range:",IDC_STATIC,5,20,53,8 + CONTROL "",IDC_SHADSAMPLE_RANGE,"CustEdit",WS_GROUP | WS_TABSTOP, + 61,19,21,10 + CONTROL "",IDC_SHADSAMPLE_RANGE_SPIN,"SpinnerControl",WS_GROUP, + 83,19,7,10 + CONTROL "Absolute Map Bias",IDC_ABSOLUTE_MAPBIAS,"Button", + BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,34,81,10 +END + +IDD_PROJ_DIRECTIONAL DIALOG DISCARDABLE 0, 0, 108, 179 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Width:",ID_STATIC,12,31,22,8 + CONTROL "",IDC_WIDTH,"CustEdit",WS_GROUP | WS_TABSTOP,37,30,36, + 10 + CONTROL "",IDC_WIDTHSPINNER,"SpinnerControl",WS_GROUP,74,30,7,10 + LTEXT "Height:",IDC_STATIC,10,42,24,8 + CONTROL "",IDC_HEIGHT,"CustEdit",WS_GROUP | WS_TABSTOP,37,42,36, + 10 + CONTROL "",IDC_HEIGHTSPINNER,"SpinnerControl",WS_GROUP,74,42,7, + 10 + CONTROL "Custom1",IDC_PROJ_MAPNAME,"CustButton",WS_TABSTOP,9,13, + 88,12 + LTEXT "Range:",IDC_STATIC,10,54,24,8 + CONTROL "",IDC_DEPTH,"CustEdit",WS_GROUP | WS_TABSTOP,37,54,36, + 10 + CONTROL "",IDC_DEPTHSPINNER,"SpinnerControl",WS_GROUP,74,54,7,10 + LTEXT "Projector Map:",IDC_STATIC,9,4,47,8 + CONTROL "Illuminate",IDC_ILLUMINATE,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,11,79,48,10 + GROUPBOX "Mode",IDC_STATIC,5,69,92,58 + CONTROL "Add",IDC_ADD,"Button",BS_AUTORADIOBUTTON | WS_GROUP | + WS_TABSTOP,11,90,48,10 + CONTROL "Multiply",IDC_MULT,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,11,101,48,10 + CONTROL "Brighten",IDC_MADD,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,11,112,48,10 + GROUPBOX "Texture",IDC_STATIC,5,130,91,40 + CONTROL "Don't Compress",IDC_PROJ_NOCOMPRESS,"Button", + BS_AUTOCHECKBOX | BS_LEFT | WS_GROUP | WS_TABSTOP,11,142, + 67,10 + CONTROL "Don't Mip",IDC_PROJ_NOMIP,"Button",BS_AUTOCHECKBOX | + BS_LEFT | WS_GROUP | WS_TABSTOP,11,154,67,10 +END + +IDD_LIGHT_ANIM DIALOG DISCARDABLE 0, 0, 108, 74 +STYLE WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_NAMES,27,7,76,52,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "Auto-start",IDC_AUTO_START,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,5,25,46,10 + CONTROL "Loop",IDC_LOOP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5, + 39,32,10 + COMBOBOX IDC_LOOPS,38,37,65,54,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Refresh",IDC_REFRESH,3,55,35,15 + LTEXT "Name:",IDC_STATIC,4,9,22,8 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 4,0,0,0 + PRODUCTVERSION 4,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "TECH: dan.silva (of AutoDesk) and allred, ryan (of CyanWorlds...)\0" + VALUE "CompanyName", "CyanWorlds.com\0" + VALUE "FileDescription", "plRealTimeLights plugin\0" + VALUE "FileVersion", "4.0.0.0\0" + VALUE "InternalName", "plRTLights\0" + VALUE "LegalCopyright", "Copyright © 2001 CyanWorlds.com\0" + VALUE "LegalTrademarks", "3D Studio MAX, Biped, Character Studio, Heidi, Kinetix and Physique are registered trademarks and 3ds max, combustion, Discreet, DWG Unplugged, DXF, FLI and FLC are trademarks of Autodesk, Inc.\0" + VALUE "OriginalFilename", "plRealTimeLights.dlo\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "3ds max\0" + VALUE "ProductVersion", "4.0.0.0\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_SHADOWMAP_PARAMS, DIALOG + BEGIN + BOTTOMMARGIN, 7 + END + + IDD_LIGHT_ANIM, DIALOG + BEGIN + TOPMARGIN, 7 + BOTTOMMARGIN, 69 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DB_TARGET "Target" + IDS_DB_DOT_TARGET ".Target" + IDS_DB_OMNI "RT Omni" + IDS_DB_TARGET_SPOT "Target Spot" + IDS_DB_DIRECTIONAL "RT Directional" + IDS_DB_FREE_SPOT "RT Spot" + IDS_DB_NONE "None" + IDS_DB_PROJDIR "RT Proj Direct" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DB_OMNI_CLASS "Plasma Omni" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DB_COLOR "Color" + IDS_DB_ASPECT_RATIO "Aspect Ratio" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DB_MULTIPLIER "Multiplier" + IDS_DB_ATTENSTART "Attenuation Far Start" + IDS_DB_ATTENEND "Attenuation Far End" + IDS_DB_HOTSIZE "Hotspot" + IDS_DB_FALLSIZE "Falloff" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DB_TDIST "Target Distance" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DS_EXCLUDE__ "Exclude.." + IDS_DS_INCLUDE__ "Include.." +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DS_LIGHTCOL "Diffuse Color" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DS_TDIRECTIONAL "Target Direct" + IDS_DS_TDIRECT "Direct" + IDS_DS_TDIR_LIGHT "Target Directional Light" + IDS_DS_ATTENSTARTNEAR "Attenuation Near Start" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DB_GENERAL_PARAMS "General Parameters" + IDS_DB_DIR_PARAMS "Directional Parameters" + IDS_DB_SPOT_PARAMS "Spotlight Parameters" + IDS_DB_SHADOW_PARAMS "Shadow Parameters" + IDS_DB_SELECT_TO_FIT " Select Image File to Fit " + IDS_DB_OMNI_LIGHT "Omni Light" + IDS_DB_DIR_LIGHT "Free Directional Light" + IDS_DB_DIRECT "FDirect" + IDS_DB_FSPOT "Fspot" + IDS_DB_SPOT "Spot" + IDS_DB_LIGHT "RTLight" + IDS_DB_TAPE "Tape" + IDS_DB_GENERAL "General" + IDS_DB_TARGET_SPOT_CLASS "Target Spot" + IDS_DB_TAPE_CLASS "Tape" + IDS_LIGHT_ANIM "Animation Parameters" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DS_ATTENENDNEAR "Attenuation Near End" + IDS_PROJ_PARAMS "Projection Parameters" + IDS_DS_CONTRAST "Contrast" + IDS_PROJWIDTH "Projection Width" + IDS_PROJHEIGHT "Projection Height" + IDS_PROJDEPTH "Projection Depth" + IDS_DS_DIFFSOFT "Diffuse Soften" + IDS_DS_SHADCOL "Shadow Color" + IDS_DS_SETTYPE "Set Type" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DB_DIRECTIONAL_CLASS "RT Directional" + IDS_DS_TDIRECTIONAL_CLASS "Target Direct" + IDS_DB_FREE_SPOT_CLASS "RT Spot" + IDS_DB_SLAVECONTROL_CLASS "SlaveControl" + IDS_DB_TARGET_CLASS "Target" + IDS_DB_PROJDIR_CLASS "RT Projected Direct" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_AP_SLICEON "Slice On" + IDS_AP_SLICETO "Slice To" + IDS_AP_SLICEFROM "Slice From" + IDS_DECAY_NONE "None" + IDS_DECAY_INVERSE "Inverse" + IDS_DECAY_INVSQ "Inverse Square" + IDS_ATTEN_PARAM "Attenuation Parameters" + IDS_DECAY_RADIUS "Decay Falloff" + IDS_ATMOS_OPACITY "Atmosphere Opacity" + IDS_ATMOS_COLAMT "Atmosphere Color Amount" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DS_PROJMAP "Projection Map" + IDS_DS_SHADPROJMAP "Shadow Projection Map" + IDS_DS_SHAD_GEN "Shadow Parameters" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DS_SETSHADTYPE "Set Shadow Type" + IDS_PW_CV "CV" + IDS_SHAD_DENSITY "Shadow Density" + IDS_DOF "Depth Of Field" + IDS_DOF_ENABLE "Enable" + IDS_DOF_FSTOP "fStop" + IDS_TH_VIZ_TEXT "VIZ Text" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_RTLIGHT_ON "LightOn" + IDS_LIGHT_GEN_PARAMS "General Parameters" + IDS_LIGHT_ATTEN_PARAMSS "Attenuation Parameters" + IDS_LIGHT_SPOT_PARAMS "Spot Light Parameters" + IDS_DS_HUE "Hue" + IDS_DS_SAT "Saturation" + IDS_DS_VAL "Value" + IDS_DS_DIFFON "Diffuse On" + IDS_DS_SPEC "Specularity" + IDS_DS_AMBI "Ambient Only" + IDS_DS_USENEAR "Use Near Atten" + IDS_DS_SHOWNEARRANGES "Show Near Ranges (Atten)" + IDS_DS_USEFARATTEN "Use Far Atten" + IDS_DS_SHOWFARRANGES "Show Far Ranges (Atten)" + IDS_DB_DECAY_STATE "Decay State" + IDS_DS_SHOWDECAY "Show Decay" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DS_SHOWCONE "Show Cone" + IDS_DS_OVERSHOOT "Overshoot On" + IDS_DS_LIGHTSHAPE_RATIO "Light Shape Ratio" + IDS_DB_LIGHTTYPE "Light Type" + IDS_DS_RED "Red" + IDS_DS_GREEN "Green" + IDS_DS_BLUE "Blue" + IDS_LIGHT_SHADOWS "Shadow Parameters" + IDS_DS_USESHADOWSS "Shadows On" + IDS_DS_USESHADOWGLOBS "Use Shadow Global" + IDS_DS_SHADCOLORS "Shadow Color" + IDS_DS_DENSITYSHADOWS "Shadow Density" + IDS_DS_USESHADOWMAPS "Use Shadow Maps" + IDS_DS_SHADOWMAPBUTTONS "Shadow Map Button" + IDS_DS_LIGHTEFFECTS_SHADS "Light Effect Shadows Bool" + IDS_DS_ATMOS_SHADS "Atmospheric Shadows Bool" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DS_ATMOS_SHAD_OPACS "Atmospheric Shadow Opacity" + IDS_DS_ATMOS_SHAD_COLORS "Atmospheric Shadow Color Amount" + IDS_LIGHT_SHADOWMAP_PARAMSS "Shadow Map Params" + IDS_DS_SHADOWMAP_BIASS "Shadow Map Bias" + IDS_DS_SHADOWMAP_SIZES "Shadow Map Size" + IDS_DS_SHADOWMAP_SAMPLESS "Shadow Map Sample Range" + IDS_DS_SHADOWMAP_ABSOLULTE_BIASS "Absolute Map Bias" + IDS_DS_CASTSHADOWSS "Cast Shadows" + IDS_DS_LIGHT_ATTEN_SLIDER "Atten Slider" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_DS_SPECCOL "Specular Color" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTObjLightDesc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTObjLightDesc.h new file mode 100644 index 00000000..3611dd12 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTObjLightDesc.h @@ -0,0 +1,151 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plRTObjLightDesc.h - Header for the various ObjLightDesc classes // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.2.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plRTObjLightDesc_h +#define _plRTObjLightDesc_h + +#include "plRealTimeLightBase.h" +#include "resource.h" + +class plMaxNode; + +//// AttenRanges Class //////////////////////////////////////////////////////// + +class AttenRanges +{ + public: + float aStart, aEnd; // Attenuation start and end and hot spot scaling for volume shading + //float aNearStart, aNearEnd; // Near Attenuation start and end and hot spot scaling for volume shading + float decayRadius; +}; + +//// BaseObjLight Class /////////////////////////////////////////////////////// + +class BaseObjLight : public ObjLightDesc +{ + public: + Color intensCol; // intens*color + Color shadColor; + float contrast,kA,kB,diffSoft; + int decayType; + BOOL diffSoften; + float decayRadius; + plRTLightBase* gl; + BaseObjLight(INode *n); + void DeleteThis() {delete this;} + int Update(TimeValue t, const RendContext& rc, RenderGlobalContext * rgc, BOOL shadows, BOOL shadowGeomChanged); + void UpdateGlobalLightLevel(Color globLightLevel) { intensCol = ls.intens*ls.color*globLightLevel;} + virtual Color AttenuateIllum(ShadeContext& sc,Point3 p,Color &colStep,Point3 &dp,int filt, float ldp, float &distAtten, AttenRanges &ranges) {return Color(0,0,0);} + virtual BOOL UseAtten()=0; + virtual BOOL IsFacingLight(Point3 &dir) {return FALSE;} + virtual int LightType()=0; + + inline float ContrastFunc(float nl) { + if (diffSoft!=0.0f) { + float p = nl*nl*(2.0f-nl); // based on Hermite interpolant + nl = diffSoft*p + (1.0f-diffSoft)*nl; + } + return (contrast==0.0f)? nl: + nl/(kA*nl+kB); // the "Bias" function described in Graphics Gems IV, pp. 401ff + } + +}; + +//// OmniLight Class ////////////////////////////////////////////////////////// + +class OmniLight : public BaseObjLight +{ + Matrix3 tmCamToLight[6]; + BOOL shadow, doShadows, shadowRay; + Texmap *projMap; + BOOL needMultiple; + BOOL genCanDoOmni; + float zfac, xscale, yscale, fov, sz2,size,sizeClip,sampSize,sampSize2; + public: + OmniLight(INode *inode, BOOL forceShadowBuf ); + ~OmniLight(); + int Update(TimeValue t, const RendContext& rc, RenderGlobalContext *rgc, BOOL shadows, BOOL shadowGeomChanged); + int UpdateViewDepParams(const Matrix3& worldToCam); + BOOL UseAtten() {return TRUE;} + int LightType() { return plRTLightBase::RT_OMNI; } +}; + +//// SpotLight Class ////////////////////////////////////////////////////////// + +class SpotLight: public BaseObjLight +{ + Point3 lightDir; // light direction in render space + BOOL projector; //, shadowRay, overshoot; + float hot_cos, fall_cos, fall_tan, fall_sin; + float hotpct, ihotpct; + float zfac, xscale,yscale, fov, sz2, curve; + float out_range,in_range, range_span; + Point2 rectv0, rectv1; + Texmap* projMap; + public: + SpotLight(INode *inode, BOOL forceShadowBuf ); + ~SpotLight() {} // FreeShadGens(); } + int Update(TimeValue t, const RendContext& rc, RenderGlobalContext *rgc, BOOL shadows, BOOL shadowGeomChanged); + int UpdateViewDepParams(const Matrix3& worldToCam); + BOOL UseAtten() {return ls.useAtten;} + BOOL IsFacingLight(Point3 &dir); + int LightType() { return FSPOT_LIGHT; } +}; + +//// DirLight Class /////////////////////////////////////////////////////////// + +class DirLight : public BaseObjLight +{ + Point3 lightDir; // light direction in render space + //BOOL projector;//,overshoot; + float hotsz, fallsz, fallsq; + float xscale, yscale, sz2, curve; + float out_range,in_range, range_span; + float hotpct,ihotpct; + float aspect; + float sw2, sh2; + Texmap* projMap; + public: + DirLight(INode *inode, BOOL forceShadowBuf ); + ~DirLight() { /* FreeShadGens();*/} + int Update(TimeValue t, const RendContext& rc, RenderGlobalContext *rgc, BOOL shadows, BOOL shadowGeomChanged); + int UpdateViewDepParams(const Matrix3& worldToCam); + BOOL UseAtten() {return FALSE;} + int LightType() { return plRTLightBase::RT_FREE_DIR; } +}; + + +#endif // _plRTObjLightDesc_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTPlasmaLights.def b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTPlasmaLights.def new file mode 100644 index 00000000..564b0a2e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTPlasmaLights.def @@ -0,0 +1,8 @@ +LIBRARY +EXPORTS + LibDescription @1 + LibNumberClasses @2 + LibClassDesc @3 + LibVersion @4 +SECTIONS + .data READ WRITE diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLight.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLight.cpp new file mode 100644 index 00000000..8f961b70 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLight.cpp @@ -0,0 +1,474 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plRTProjDirLight.cpp - Functions for the Projected Directional MAX light // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.2.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "HeadSpin.h" +#include "plRTProjDirLight.h" +#include "plRTProjDirLightClassDesc.h" +#include "iparamm2.h" +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" +#include "../MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h" +#include "../MaxComponent/plMaxAnimUtils.h" +#include "plRTObjLightDesc.h" +#include "plRTLightBaseAnimDlgProc.h" + + +//// Static ClassDesc2 Get Functions ////////////////////////////////////////// + +plRTProjDirLightDesc plRTProjDirLightDesc::fStaticDesc; + + +//// Dialog Proc ////////////////////////////////////////////////////////////// + +class plProjDirDlgProc : public plBaseLightProc +{ + public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IParamBlock2 *pb = map->GetParamBlock(); + plRTProjDirLight *lite = (plRTProjDirLight *)pb->GetOwner(); + PBBitmap *bitmap; + plLayerTex *layer = (plLayerTex *)lite->GetProjMap(); + ICustButton *bmSelectBtn; + + + switch( msg ) + { + case WM_COMMAND: + if( LOWORD( wParam ) == IDC_PROJ_MAPNAME ) + { + BOOL selectedNewBitmap = layer->HandleBitmapSelection(); + if( selectedNewBitmap ) + { + bmSelectBtn = GetICustButton( GetDlgItem( hWnd, IDC_PROJ_MAPNAME ) ); + bitmap = layer->GetPBBitmap(); + bmSelectBtn->SetText( bitmap != nil ? (TCHAR *)bitmap->bi.Filename() : ""); + ReleaseICustButton( bmSelectBtn ); + } + return false; + } + break; + + case WM_INITDIALOG: + + // Set projection map bitmap name + bmSelectBtn = GetICustButton( GetDlgItem( hWnd, IDC_PROJ_MAPNAME ) ); + if( bmSelectBtn != nil ) + { + bitmap = ( layer == nil ) ? nil : layer->GetPBBitmap(); + if( bitmap != nil ) + bmSelectBtn->SetText( (TCHAR *)bitmap->bi.Filename() ); + else + bmSelectBtn->SetText( _T( "" ) ); + ReleaseICustButton( bmSelectBtn ); + } + + break; + } + + return plBaseLightProc::DlgProc( t, map, hWnd, msg, wParam, lParam ); + } + void DeleteThis() {}; +}; +static plProjDirDlgProc gPPDirLiteDlgProc; + +/// Include for the ParamBlock2 definition + +#include "plRTProjDirLightPBDec.h" + + +/////////////////////////////////////////////////////////////////////////////// +//// plRTProjPBAccessor Class Functions /////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +plRTProjPBAccessor plRTProjPBAccessor::fAccessor; + +void plRTProjPBAccessor::Set( PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t ) +{ + plRTProjDirLight *obj = (plRTProjDirLight *)owner; + IParamBlock2 *pb = obj->GetParamBlockByID( plRTProjDirLight::kBlkProj ); + + switch( id ) + { + case plRTProjDirLight::kProjMap: +/* { + if( pb->GetMap() ) + pb->GetMap()->Invalidate( plRTProjDirLight::kProjMap ); + PBBitmap *thisMap = val.bm; + obj->SetProjMap( &thisMap->bi ); + } +*/ break; + default: + break; + } +} + +void plRTProjPBAccessor::Get( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid ) +{ +} + +/////////////////////////////////////////////////////////////////////////////// +//// Projected Directional Light ////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +plRTProjDirLight::plRTProjDirLight() +{ + fIP = nil; + fLightPB = nil; + fProjPB = nil; + fClassDesc = plRTProjDirLightDesc::GetDesc(); + fClassDesc->MakeAutoParamBlocks( this ); + + fLightPB->SetValue(kLightColor, 0, Color(255,255,255)); + SetHSVColor(0, Point3(255, 255, 255)); + + fTex = nil; + meshBuilt = 0; + + IBuildMeshes(true); +} + +ObjLightDesc *plRTProjDirLight::CreateLightDesc( INode *n, BOOL forceShadowBuf ) +{ + return TRACKED_NEW DirLight( n, forceShadowBuf ); +} + +RefTargetHandle plRTProjDirLight::Clone( RemapDir &remap ) +{ + plRTProjDirLight *obj = TRACKED_NEW plRTProjDirLight; + + obj->ReplaceReference( kRefDirLight, fLightPB->Clone( remap ) ); + BaseClone( this, obj, remap ); + + return obj; +} + +//// SubAnim ///////////////////////////////////////////////////////////////// + +Animatable *plRTProjDirLight::SubAnim( int i ) +{ + switch( i ) + { + case 0: + return (Animatable *)fLightPB; + case 1: + return (Animatable *)fProjPB; + default: + return nil; + } +} + +//// SubAnimName ///////////////////////////////////////////////////////////// + +TSTR plRTProjDirLight::SubAnimName( int i ) +{ + switch( i ) + { + case 0: + return fLightPB->GetLocalName(); + case 1: + return fProjPB->GetLocalName(); + default: + return TSTR( "" ); + } +} + +//// GetReference //////////////////////////////////////////////////////////// + +RefTargetHandle plRTProjDirLight::GetReference( int i ) +{ + switch( i ) + { + case kRefMainRollout: + return (RefTargetHandle)fLightPB; + case kRefProjRollout: + return (RefTargetHandle)fProjPB; + default: + return plRTLightBase::GetReference( i ); + } +} + +//// SetReference //////////////////////////////////////////////////////////// + +void plRTProjDirLight::SetReference( int ref, RefTargetHandle rtarg ) +{ + switch( ref ) + { + case kRefMainRollout: + fLightPB = (IParamBlock2 *)rtarg; + break; + case kRefProjRollout: + fProjPB = (IParamBlock2 *)rtarg; + break; + + default: + plRTLightBase::SetReference( ref, rtarg ); + break; + } +} + +//// ParamBlock Functions //////////////////////////////////////////////////// + +int plRTProjDirLight::NumParamBlocks() +{ + return plRTLightBase::NumParamBlocks() + 1; +} + +IParamBlock2 *plRTProjDirLight::GetParamBlock( int i ) +{ + switch ( i ) + { + case 2: return fProjPB; + default: return plRTLightBase::GetParamBlock( i ); + } +} + +IParamBlock2 *plRTProjDirLight::GetParamBlockByID( BlockID id ) +{ + if( fProjPB->ID() == id ) + return fProjPB; + else + return plRTLightBase::GetParamBlockByID( id ); +} + +//// GetProjMap ////////////////////////////////////////////////////////////// + +Texmap *plRTProjDirLight::GetProjMap() +{ + // If we don't have one, create one + plLayerTex *layer = (plLayerTex *)fProjPB->GetTexmap( kTexmap, 0 ); + if( layer == nil || layer->ClassID() != LAYER_TEX_CLASS_ID ) + { + layer = TRACKED_NEW plLayerTex; + fProjPB->SetValue( kTexmap, 0, (Texmap *)layer ); + + IParamBlock2 *bitmapPB = layer->GetParamBlockByID( plLayerTex::kBlkBitmap ); + bitmapPB->SetValue( kBmpUseBitmap, 0, 1 ); + } + + // Backwards compatability here + PBBitmap *bitmap = fProjPB->GetBitmap( kProjMap, 0 ); + if( bitmap != nil ) + { + layer->SetBitmap( &bitmap->bi ); + fProjPB->SetValue( kProjMap, 0, (PBBitmap *)nil ); + } + + if( layer ) + { + const char* dbgTexName = layer->GetName(); + + IParamBlock2 *bitmapPB = layer->GetParamBlockByID(plLayerTex::kBlkBitmap); + hsAssert(bitmapPB, "LayerTex with no param block"); + + int noCompress = fProjPB->GetInt(kProjNoCompress); + int noMip = fProjPB->GetInt(kProjNoMip); + bitmapPB->SetValue(kBmpNonCompressed, TimeValue(0), noCompress); + bitmapPB->SetValue(kBmpNoFilter, TimeValue(0), noMip); + } + return (Texmap *)layer; +} + +//// IBuildMeshes //////////////////////////////////////////////////////////// + +void plRTProjDirLight::IBuildMeshes( BOOL isnew ) +{ + BuildStaticMeshes(); + + fMesh = staticMesh[ plRTLightBase::RT_OMNI + 1 ]; +} + +//// GetLocalBoundBox //////////////////////////////////////////////////////// + +void plRTProjDirLight::GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box ) +{ + Point3 loc = node->GetObjectTM( t ).GetTrans(); + float scaleFactor = vpt->NonScalingObjectSize() * vpt->GetVPWorldWidth( loc ) / 360.0f; + float width, height, depth; + + box = fMesh.getBoundingBox(); + // Because we want to scale about the origin, not the box center, we have to do this funky offset + Point3 boxCenter = box.Center(); + box.Translate( -boxCenter ); + box.Scale( scaleFactor ); + boxCenter *= scaleFactor; + box.Translate( boxCenter ); + + if( ( extDispFlags & EXT_DISP_ONLY_SELECTED ) ) + { + fProjPB->GetValue( kWidth, t, width, FOREVER ); + fProjPB->GetValue( kHeight, t, height, FOREVER ); + fProjPB->GetValue( kRange, t, depth, FOREVER ); + width /= 2.f; + height /= 2.f; + + box += Point3( -width, -height, 0.f ); + box += Point3( width, height, -depth ); + } +} + +//// DrawConeAndLine ///////////////////////////////////////////////////////// + +int plRTProjDirLight::DrawConeAndLine( TimeValue t, INode* inode, GraphicsWindow *gw, int drawing ) +{ + Matrix3 tm = inode->GetObjectTM( t ); + gw->setTransform( tm ); + gw->clearHitCode(); + + if( extDispFlags & EXT_DISP_ONLY_SELECTED ) + DrawCone( t, gw, 500 ); + + return gw->checkHitCode(); +} + +//// DrawCone //////////////////////////////////////////////////////////////// +// Function called by MAX to render the cone shape in the viewport for this +// light. Note that this is the cone, not the actual object itself! + +void plRTProjDirLight::DrawCone( TimeValue t, GraphicsWindow *gw, float dist ) +{ + Point3 nearPts[ 5 ], farPts[ 5 ], u[ 3 ]; + int i; + float width, height; + + + // Get values + fProjPB->GetValue( kWidth, t, width, FOREVER ); + fProjPB->GetValue( kHeight, t, height, FOREVER ); + fProjPB->GetValue( kRange, t, dist, FOREVER ); + + // Draw a rectangle showing the extents of the light (width, height and depth) + IBuildRectangle( width, height, 0, nearPts ); + IBuildRectangle( width, height, -dist, farPts ); + + gw->setColor( LINE_COLOR, GetUIColor( COLOR_HOTSPOT ) ); + + gw->polyline( 4, nearPts, nil, nil, true, nil ); + gw->polyline( 4, farPts, nil, nil, true, nil ); + for( i = 0; i < 4; i++ ) + { + u[ 0 ] = nearPts[ i ]; + u[ 1 ] = farPts[ i ]; + gw->polyline( 2, u, nil, nil, false, nil ); + } +} + +//// IBuildRectangle ///////////////////////////////////////////////////////// + +void plRTProjDirLight::IBuildRectangle( float width, float height, float z, Point3 *pts ) +{ + width /= 2.f; + height /= 2.f; + + pts[ 0 ] = Point3( -width, -height, z ); + pts[ 1 ] = Point3( width, -height, z ); + pts[ 2 ] = Point3( width, height, z ); + pts[ 3 ] = Point3( -width, height, z ); +} + +//// GetFallsize ///////////////////////////////////////////////////////////// +// To get using-light-as-camera-viewport to work + +float plRTProjDirLight::GetFallsize( TimeValue t, Interval &valid ) +{ + if( fProjPB == nil ) + return -1.f; + + return fProjPB->GetFloat( kWidth, t ); +} + +//// GetAspect /////////////////////////////////////////////////////////////// +// To get using-light-as-camera-viewport to work + +float plRTProjDirLight::GetAspect( TimeValue t, Interval &valid ) +{ + if( fProjPB == nil ) + return -1.f; + + return( fProjPB->GetFloat( kHeight, t ) / fProjPB->GetFloat( kWidth, t ) ); +} + +//// GetTDist //////////////////////////////////////////////////////////////// +// To get using-light-as-camera-viewport to work + +float plRTProjDirLight::GetTDist( TimeValue t, Interval &valid ) +{ + return 0.f; + if( fProjPB == nil ) + return -1.f; + + return fProjPB->GetFloat( kRange, t ); +} + +//// SetFallsize ///////////////////////////////////////////////////////////// +// To get using-light-as-camera-viewport to work + +void plRTProjDirLight::SetFallsize( TimeValue time, float f ) +{ + if( fProjPB != nil ) + fProjPB->SetValue( kWidth, time, f ); +} + +//// EvalLightState ////////////////////////////////////////////////////////// +// To get using-light-as-camera-viewport to work + +RefResult plRTProjDirLight::EvalLightState( TimeValue t, Interval& valid, LightState *ls ) +{ + ls->type = DIRECT_LGT; + if( fLightPB->GetInt( kLightOn, t ) == true ) + ls->color = GetRGBColor( t, valid ); + else + ls->color = Color( 0, 0, 0 ); + ls->on = fLightPB->GetInt( kLightOn, t ); + ls->intens = GetIntensity( t, valid ); + + float fall = fProjPB->GetFloat( kWidth, t ); + if( fall < fProjPB->GetFloat( kHeight, t ) ) + fall = fProjPB->GetFloat( kHeight, t ); + + ls->hotsize = ls->fallsize = fall / 2.f; + ls->useNearAtten = false; + ls->useAtten = false; + ls->shape = RECT_LIGHT; + + ls->aspect = fProjPB->GetFloat( kHeight, t ) / fProjPB->GetFloat( kWidth, t ); + ls->overshoot = false; + ls->shadow = false; + ls->ambientOnly = false; + ls->affectDiffuse = fLightPB->GetInt( kAffectDiffuse, t ); + ls->affectSpecular = fLightPB->GetInt( kSpec, t ); + + return REF_SUCCEED; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLight.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLight.h new file mode 100644 index 00000000..79e09f5f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLight.h @@ -0,0 +1,155 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plRTProjDirLight.h - Header for the derived MAX RT projected directional // +// light // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.2.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plRTProjDirLight_h +#define _plRTProjDirLight_h + +#include "plRealTimeLightBase.h" +#include "iparamm2.h" + + +/////////////////////////////////////////////////////////////////////////////// +//// plRTProjPBAccessor /////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class plRTProjPBAccessor : public PBAccessor +{ + public: + void Set( PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t ); + void Get( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid ); + + static plRTProjPBAccessor *Instance( void ) { return &fAccessor; } + + protected: + + static plRTProjPBAccessor fAccessor; +}; + +/////////////////////////////////////////////////////////////////////////////// +//// Projected Directional Light ////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class plRTProjDirLight : public plRTLightBase +{ + public: + + friend class plRTProjPBAccessor; + + enum Blocks + { + kBlkProj = kBlkDerivedStart + }; + + enum References + { + kRefMainRollout = kRefDerivedStart, + kRefProjRollout, + + kNumRefs + }; + + enum ProjRollout + { + kWidth, + kHeight, + kRange, + kShowCone, + kProjMap, + kTexmap, + + // Someone goofed and used this index from the base class in our param block. + // luckily it worked because there was no overlap, but we can't change it to + // clean it up without breaking everything that uses it. So hopefully this + // will at least clarify what's going on and prevent someone from stepping on + // the index later. + kProjTypeRadio = plRTLightBase::kProjTypeRadio, + }; + + plRTProjDirLight(); + + /// Class ID stuff + Class_ID ClassID( void ) { return RTPDIR_LIGHT_CLASSID; } + SClass_ID SuperClassID( void ) { return LIGHT_CLASS_ID; } + + ObjLightDesc *CreateLightDesc( INode *n, BOOL forceShadowBuf = FALSE ); + GenLight *NewLight( int type ) { return TRACKED_NEW plRTProjDirLight(); } + RefTargetHandle Clone( RemapDir &remap ); + + int CanConvertToType( Class_ID obtype ) { return ( obtype == RTPDIR_LIGHT_CLASSID ) ? 1 : 0; } + + virtual void GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box ); + virtual int DrawConeAndLine( TimeValue t, INode* inode, GraphicsWindow *gw, int drawing ); + virtual void DrawCone( TimeValue t, GraphicsWindow *gw, float dist ); + + virtual BOOL IsDir( void ) { return TRUE; } + virtual RefTargetHandle GetReference( int i ); + virtual void SetReference( int ref, RefTargetHandle rtarg ); + virtual int NumRefs() { return kNumRefs; } + + virtual int NumSubs() { return 2; } + virtual TSTR SubAnimName( int i ); + virtual Animatable *SubAnim( int i ); + + virtual int NumParamBlocks(); + virtual IParamBlock2 *GetParamBlock( int i ); + virtual IParamBlock2 *GetParamBlock2() { return fLightPB; } + virtual IParamBlock2 *GetParamBlockByID( BlockID id ); + + virtual Texmap *GetProjMap(); + + virtual void InitNodeName( TSTR &s ) { s = _T( "RTProjDirLight" ); } + + // To get using-light-as-camera-viewport to work + virtual int GetSpotShape( void ) { return RECT_LIGHT; } + virtual float GetAspect( TimeValue t, Interval &valid = Interval(0,0) ); + virtual float GetFallsize( TimeValue t, Interval &valid = Interval(0,0) ); + virtual int Type() { return DIR_LIGHT; } + virtual float GetTDist( TimeValue t, Interval &valid = Interval(0,0) ); + virtual void SetFallsize( TimeValue time, float f ); + + RefResult EvalLightState(TimeValue t, Interval& valid, LightState *ls); + + protected: + + IParamBlock2 *fProjPB; + + virtual void IBuildMeshes( BOOL isNew ); + + void IBuildRectangle( float width, float height, float z, Point3 *pts ); +}; + +#endif // _plRTProjDirLight_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLightClassDesc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLightClassDesc.h new file mode 100644 index 00000000..9d885fc0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLightClassDesc.h @@ -0,0 +1,62 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plRTProjDirLight.h - Header for the derived MAX RT projected directional // +// light // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.2.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plRTProjDirLightClassDesc_h +#define _plRTProjDirLightClassDesc_h + +#include "plRTProjDirLight.h" +#include "iparamm2.h" +#include "resource.h" + +class plRTProjDirLightDesc : public ClassDesc2 +{ + public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return TRACKED_NEW plRTProjDirLight; } + const TCHAR* ClassName() { return GetString(IDS_DB_PROJDIR); } + SClass_ID SuperClassID() { return LIGHT_CLASS_ID; } + Class_ID ClassID() { return RTPDIR_LIGHT_CLASSID; } + const TCHAR* Category() { return _T("Plasma RunTime");} + const TCHAR* InternalName() { return _T("RTProjDir"); } // returns fixed parsable name (scripter-visible name) + HINSTANCE HInstance() { return hInstance; } + + static plRTProjDirLightDesc fStaticDesc; + + static ClassDesc2 *GetDesc( void ) { return &fStaticDesc; } +}; + +#endif // _plRTProjDirLightClassDesc_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLightPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLightPBDec.h new file mode 100644 index 00000000..223d7e77 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRTProjDirLightPBDec.h @@ -0,0 +1,162 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// ParamBlock Type Konstants for plRT*Lights // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.2.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plRTProjDirLightPBDec_h +#define _plRTProjDirLightPBDec_h + + +/////////////////////////////////////////////////////////////////////////////// +//// Projected Directional Lights ///////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ParamBlockDesc2 RTProjDirLightBlk +( + /// Main def + plRTProjDirLight::kBlkMain, _T("RTProjDir"), 0, plRTProjDirLightDesc::GetDesc(), P_AUTO_CONSTRUCT + P_AUTO_UI, + plRTProjDirLight::kRefMainRollout, + + /// Rollout definition + IDD_LIGHT_PARAM, IDS_LIGHT_GEN_PARAMS, 0, 0, &gPPDirLiteDlgProc, + + plRTLightBase::kLightOn, _T("on"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, true, + p_ui, TYPE_SINGLECHEKBOX, IDC_LIGHT_ON, + end, + + plRTLightBase::kAffectDiffuse, _T("AffectDiffuse"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, true, + p_ui, TYPE_SINGLECHEKBOX, IDC_LIGHT_DIFFUSE, + end, + + plRTLightBase::kAmbientOnlyStub, _T("AmbientOnly"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, false, + p_ui, TYPE_SINGLECHEKBOX, IDC_AMBIENT_ONLY_STUB, + end, + + plRTLightBase::kCastShadows, _T("CastShadows"), TYPE_BOOL, 0, IDS_DS_CASTSHADOWSS, +// p_default, false, +// p_ui, TYPE_SINGLECHEKBOX, IDC_CAST_SHADOWS, + end, + + plRTLightBase::kLightColor, _T("LightColor"), TYPE_RGBA, P_ANIMATABLE, IDS_DS_LIGHTCOL, + p_default, Color(255,255,255), + p_ui, TYPE_COLORSWATCH, IDC_LIGHT_COLOR, + end, + + plRTLightBase::kSpec, _T("AffectSpecular"), TYPE_BOOL, P_ANIMATABLE, IDS_DS_SPEC, + p_default, false, + p_ui, TYPE_SINGLECHEKBOX, IDC_AFFECT_SPECULAR, + end, + + plRTLightBase::kSpecularColorSwatch,_T("SpecularColor"),TYPE_RGBA, P_ANIMATABLE, IDS_DS_SPECCOL, + p_default, Color(255,255,255), + p_ui, TYPE_COLORSWATCH, IDC_LIGHT_COLOR_SPECULAR, + end, + + plRTLightBase::kIntensity, _T("IntensityEditSpinner"), TYPE_FLOAT, P_ANIMATABLE, IDS_DB_MULTIPLIER, + p_range, -250.0, 250.0, + p_default, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_LMULT,IDC_LMULTSPINNER, .05f, + end, + + end +); + +/////////////////////////////////////////////////////////////////////////////// +//// Projection Rollout ParamBlock //////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ParamBlockDesc2 plRTProjParamBlock +( + /// Main def + plRTProjDirLight::kBlkProj, _T("RTProjDir_Proj"), 0, plRTProjDirLightDesc::GetDesc(), P_AUTO_CONSTRUCT + P_AUTO_UI, + plRTProjDirLight::kRefProjRollout, + + /// Rollout definition + IDD_PROJ_DIRECTIONAL, IDS_PROJ_PARAMS, 0, 0, &gPPDirLiteDlgProc, + + plRTProjDirLight::kWidth, _T("Width"), TYPE_FLOAT, P_ANIMATABLE, IDS_PROJWIDTH, + p_range, 0.0, 999999999.0, + p_default, 200.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_UNIVERSE, IDC_WIDTH, IDC_WIDTHSPINNER, .05f, + end, + + plRTProjDirLight::kHeight, _T("Height"), TYPE_FLOAT, P_ANIMATABLE, IDS_PROJHEIGHT, + p_range, 0.0, 999999999.0, + p_default, 200.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_UNIVERSE, + IDC_HEIGHT, IDC_HEIGHTSPINNER, .05f, + end, + + plRTProjDirLight::kRange, _T("Range"), TYPE_FLOAT, P_ANIMATABLE, IDS_PROJDEPTH, + p_range, 0.0, 999999999.0, + p_default, 200.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_UNIVERSE, + IDC_DEPTH, IDC_DEPTHSPINNER, .05f, + end, + + // Old way--here for backwards compatability + plRTProjDirLight::kProjMap, _T("ProjMapButton"), TYPE_BITMAP, P_SHORT_LABELS, IDS_DS_PROJMAP, +// p_ui, TYPE_BITMAPBUTTON, IDC_PROJ_MAPNAME, +// p_accessor, plRTProjPBAccessor::Instance(), + end, + + // New way + plRTProjDirLight::kTexmap, _T("texmap"), TYPE_TEXMAP, 0, 0, + end, + + plRTLightBase::kProjTypeRadio, _T("ProjTypeRadio"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_ILLUMINATE, IDC_ADD, IDC_MULT, IDC_MADD, + p_vals, plRTLightBase::kIlluminate, plRTLightBase::kAdd, plRTLightBase::kMult, plRTLightBase::kMADD, + p_default, plRTLightBase::kIlluminate, + end, + + plRTLightBase::kProjNoCompress, _T("NoCompress"), TYPE_BOOL, 0, IDS_DS_PROJ_PARAMS, + p_default, false, + p_ui, TYPE_SINGLECHEKBOX, IDC_PROJ_NOCOMPRESS, + end, + + plRTLightBase::kProjNoMip, _T("NoMip"), TYPE_BOOL, 0, IDS_DS_PROJ_PARAMS, + p_default, false, + p_ui, TYPE_SINGLECHEKBOX, IDC_PROJ_NOMIP, + end, + + end +); + +#endif //_plRTProjDirLightPBDec_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightBase.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightBase.cpp new file mode 100644 index 00000000..c756073a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightBase.cpp @@ -0,0 +1,2087 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "plRealTimeLightBase.h" +#include "iparamm2.h" +#include "resource.h" +#include "decomp.h" +#include "hsv.h" +#include "target.h" +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" +#include "../MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h" + + +static int GetTargetPoint(TimeValue t, INode *inode, Point3& p) +{ + Matrix3 tmat; + if (inode->GetTargetTM(t,tmat)) + { + p = tmat.GetTrans(); + return 1; + } + else + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +//// plBaseLightProc Functions //////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// This class provides the base functionality for all the dialog procs for +// the ParamBlock rollouts for each light + +void plBaseLightProc::ILoadComboBox( HWND hComboBox, const char *names[] ) +{ + SendMessage(hComboBox, CB_RESETCONTENT, 0, 0); + for (int i = 0; names[i]; i++) + SendMessage(hComboBox, CB_ADDSTRING, 0, (LPARAM)names[i]); + SendMessage(hComboBox, CB_SETCURSEL, 0, 0); +} + +void plBaseLightProc::IBuildLightMesh( plRTLightBase *base, float coneSize ) +{ + base->BuildSpotMesh( coneSize ); + base->fMesh = base->spotMesh; +} + +BOOL plBaseLightProc::DlgProc( TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + IParamBlock2 *pb = map->GetParamBlock(); + plRTLightBase *gl = (plRTLightBase *) pb->GetOwner(); + + + switch( msg ) + { + case WM_INITDIALOG: + { + HWND DecayTypeCombo = GetDlgItem(hWnd, IDC_LIGHT_DECAY); + HWND ShadowStateCombo = GetDlgItem(hWnd, IDC_SHADOW_TYPE); + + map->Invalidate(plRTLightBase::kProjMapTexButton); + } + break; + + case WM_COMMAND: + break; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +//// plLightTexPBAccessor Class Functions ///////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +plLightTexPBAccessor plLightTexPBAccessor::fAccessor; + +void plLightTexPBAccessor::Set( PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t ) +{ + plRTLightBase* Obj = (plRTLightBase*)owner; + IParamBlock2 *pb = Obj->fLightPB; + + switch (id) + { + case plRTLightBase::kProjMapTexButton: + if (1) //(val.bm)->bm) + { + if (pb->GetMap()) + pb->GetMap()->Invalidate(plRTLightBase::kProjMapTexButton); + //pb->SetValue(plRTLightBase::kProjMapTexButton, Obj->fIP->GetTime(), val.bm); + PBBitmap* ThisMap = val.bm; + Obj->SetProjMap(&ThisMap->bi); + break; + } + default: + break; + + } +} + +void plLightTexPBAccessor::Get( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid ) +{ +} + + +//////////////////////////////////////////////////////////// +// +// Mouse creation callback class for this plug-in + +class RTLightMouseCallBack : public CreateMouseCallBack +{ + plRTLightBase *ob; +public: + int proc(ViewExp *vpt, int msg, int point, int flags, IPoint2 m, Matrix3 &mat); + void SetObj(plRTLightBase *obj) { ob = obj; } +}; +static RTLightMouseCallBack gRTMouseCallback; + + +int RTLightMouseCallBack::proc(ViewExp *vpt, int msg, int point, int flags, IPoint2 m, Matrix3 &mat) +{ + switch (msg) + { + case MOUSE_POINT: + case MOUSE_MOVE: + switch (point) + { + case 0: + mat.SetTrans(vpt->SnapPoint(m,m,NULL,SNAP_IN_PLANE)); + break; + + case 1: + mat.SetTrans(vpt->SnapPoint(m,m,NULL,SNAP_IN_PLANE)); + if (msg == MOUSE_POINT) + return 0; + break; + } + break; + + case MOUSE_ABORT: + return CREATE_ABORT; + } + + return CREATE_CONTINUE; +} + +static RTLightMouseCallBack sRTBaseLgtCreateCB; + +CreateMouseCallBack* plRTLightBase::GetCreateMouseCallBack() +{ + sRTBaseLgtCreateCB.SetObj(this); + + return &sRTBaseLgtCreateCB; +} +///////////////////////////////////////////////////////////////////////////// + +#if 0 +plRTLightBase::plRTLightBase() : fIP(nil), fClassDesc(nil), fLightPB(nil) +{ + fColor = Color(0.5f, 0.5f, 1.f); +} +#endif + + + +void plRTLightBase::SetHSVColor(TimeValue t, Point3 &hsv) +{ + DWORD rgb = HSVtoRGB ((int)(hsv[0]*255.0f), (int)(hsv[1]*255.0f), (int)(hsv[2]*255.0f)); + Point3 temp = fLightPB->GetPoint3(kLightColor, t); + Point3 rgbf; + rgbf[0] = temp.x / 255.0f; + rgbf[1] = temp.y / 255.0f; + rgbf[2] = temp.z / 255.0f; + SetRGBColor(t, rgbf); +} + + +Point3 plRTLightBase::GetHSVColor(TimeValue t, Interval& b) +{ + int h, s, v; + Point3 rgbf = GetRGBColor(t, b); + DWORD rgb = RGB((int)(rgbf[0]*255.0f), (int)(rgbf[1]*255.0f), (int)(rgbf[2]*255.0f)); + RGBtoHSV (rgb, &h, &s, &v); + return Point3(h/255.0f, s/255.0f, v/255.0f); + + +} + +#define FZ (0.0f) + +#define SET_QUAD(face, v0, v1, v2, v3) \ + staticMesh[RT_OMNI +1].faces[face].setVerts(v0, v1, v2); \ + staticMesh[RT_OMNI +1].faces[face].setEdgeVisFlags(1,1,0); \ + staticMesh[RT_OMNI +1].faces[face+1].setVerts(v0, v2, v3); \ + staticMesh[RT_OMNI +1].faces[face+1].setEdgeVisFlags(0,1,1); + +void plRTLightBase::BuildStaticMeshes() +{ + if(!meshBuilt) { + int nverts = 6; + int nfaces = 8; + // Build a leetle octahedron + staticMesh[RT_OMNI].setNumVerts(nverts); + staticMesh[RT_OMNI].setNumFaces(nfaces); + float s = 8.0f; + staticMesh[RT_OMNI].setVert(0, Point3( FZ,FZ, -s)); + staticMesh[RT_OMNI].setVert(1, Point3( s, FZ, FZ)); + staticMesh[RT_OMNI].setVert(2, Point3( FZ, s, FZ)); + staticMesh[RT_OMNI].setVert(3, Point3(-s, FZ, FZ)); + staticMesh[RT_OMNI].setVert(4, Point3( FZ,-s, FZ)); + staticMesh[RT_OMNI].setVert(5, Point3( FZ,FZ, s)); + staticMesh[RT_OMNI].faces[0].setVerts(0,1,4); + staticMesh[RT_OMNI].faces[1].setVerts(0,4,3); + staticMesh[RT_OMNI].faces[2].setVerts(0,3,2); + staticMesh[RT_OMNI].faces[3].setVerts(0,2,1); + staticMesh[RT_OMNI].faces[4].setVerts(5,1,2); + staticMesh[RT_OMNI].faces[5].setVerts(5,2,3); + staticMesh[RT_OMNI].faces[6].setVerts(5,3,4); + staticMesh[RT_OMNI].faces[7].setVerts(5,4,1); + for (int i=0; iID() ) + return fLightPB; + else + return nil; +} + +IParamBlock2 *plRTLightBase::GetParamBlock( int i ) +{ + switch( i ) + { + case 0: return fLightPB; + default: return nil; + } +} + + +// So our animatables will show up in the trackview +int plRTLightBase::NumSubs() +{ + return 1; +} +Animatable *plRTLightBase::SubAnim(int i) +{ + return (Animatable *)fLightPB; + + switch(i) + { + /* kRefProjMap, + kRefShadowProjMap, + kRefShadowType, + kRefOmniLight, + kRefSpotLight, + kRefTSpotLight, + kRefDirLight, + kRefTDirLight + */ + case kRefOmniLight: + case kRefSpotLight: + case kRefTSpotLight: + case kRefDirLight: + case kRefTDirLight: + case kRefProjDirLight: + return (Animatable*)fLightPB; + case kRefProjMap: + Texmap* MyMap; + return (Animatable*) fLightPB->GetValue(kProjMapTexButton, 0, MyMap, FOREVER); + case kRefShadowType: + return NULL; + default: return NULL; + + } + + //return (Animatable*) fLightPB; +} + +TSTR plRTLightBase::SubAnimName(int i) +{ + return fLightPB->GetLocalName(); + switch(i) + { + + case kRefOmniLight:return _T(""); + case kRefSpotLight: return TSTR(GetString(IDS_DB_FSPOT)); + case kRefTSpotLight:return _T(""); + case kRefDirLight:return _T(""); + case kRefTDirLight:return _T(""); + case kRefProjMap: return TSTR(GetString(IDS_DS_PROJMAP)); + case kRefShadowType: return _T(""); + default: return _T(""); + +/* case PBLOCK_REF: return TSTR(GetString(IDS_RB_PARAMETERS)); + case PROJMAP_REF: return TSTR(GetString(IDS_DS_PROJMAP)); + case SHADPROJMAP_REF: return TSTR(GetString(IDS_DS_SHADPROJMAP)); + case SHADTYPE_REF: return TSTR(GetString(IDS_DS_SHAD_GEN)); + case EMITTER_REF: + if ( IsCompatibleRenderer ()) + return TSTR(GetString(IDS_EMITTER)); + else + return _T(""); + default: return _T(""); +*/ + } +} + +#if 0 +RefTargetHandle plRTSpotLight::Clone(RemapDir &remap) +{ + plRTLightBase *obj = TRACKED_NEW plRTSpotLight; + + obj->GetParamBlock2->SetValue(kLightOn, t, fLightPB->GetInt(kLightOn, t)); +// obj->fLightPB->SetValue(kLightType, t, fLightPB->GetInt(kLightType, t)); + obj->fLightPB->SetValue(kLightColor, t, fLightPB->GetInt(kLightOn, t)); + obj->fLightPB->SetValue(kCastShadows, t, fLightPB->GetInt(kLightOn, t)); + //obj->fLightPB->SetValue(kContrast, t, fLightPB->GetInt(kLightOn, t)); + //obj->fLightPB->SetValue(kDiffSoft, t, fLightPB->GetInt(kLightOn, t)); + //obj->fLightPB->SetValue(kDiffOn, t, fLightPB->GetInt(kLightOn, t)); + obj->fLightPB->SetValue(kSpec, t, fLightPB->GetInt(kLightOn, t)); + obj->fLightPB->SetValue(kSpecularColorSwatch, t, fLightPB->GetInt(kLightOn, t)); + obj->fLightPB->SetValue(kIntensity, t, fLightPB->GetInt(kLightOn, t)); + + if( IHasAttenuation() ) + { + obj->fLightPB->SetValue(kUseAttenuationBool, t, fLightPB->GetInt(kLightType, t)); + obj->fLightPB->SetValue(kAttenMaxFalloffEdit, t, fLightPB->GetInt(kLightOn, t)); + obj->fLightPB->SetValue(kAttenTypeRadio, t, fLightPB->GetInt(kLightOn, t)); + obj->fLightPB->SetValue(kShowConeBool, t, fLightPB->GetInt(kLightOn, t)); + obj->fLightPB->SetValue(kHotSpot, t, fLightPB->GetInt(kLightOn, t)); + obj->fLightPB->SetValue(kAttenMaxFalloffEdit, fLightPB->GetInt(kLightType, t)); + obj->fLightPB->SetValue(kFallOff, t, fLightPB->GetInt(kLightOn, t)); + + obj->fLightPB->SetValue(kUseProjectorBool, t, fLightPB->GetInt(kLightOn, t)); + obj->fLightPB->SetValue(kProjMapTexButton, t, fLightPB->GetInt(kLightOn, t)); + } + obj->ReplaceReference(kRefSpotLight,fLightPB->Clone(remap)); + /* + GeneralLight* newob = TRACKED_NEW GeneralLight(type); + newob->enable = enable; + newob->coneDisplay = coneDisplay; + newob->useLight = useLight; + newob->attenDisplay = attenDisplay; + newob->useAtten = useAtten; + newob->useAttenNear = useAttenNear; + newob->attenNearDisplay = attenNearDisplay; + newob->decayDisplay = decayDisplay; + newob->shape = shape; + newob->shadow = shadow; + newob->shadowType = shadowType; + newob->overshoot = overshoot; + newob->projector = projector; + newob->absMapBias = absMapBias; + newob->exclList = exclList; + newob->softenDiffuse = softenDiffuse; + newob->affectDiffuse = affectDiffuse; + newob->affectSpecular = affectSpecular; + newob->ambientOnly = ambientOnly; + newob->decayType = decayType; + newob->atmosShadows = atmosShadows; + newob->atmosOpacity = atmosOpacity; + newob->atmosColAmt = atmosColAmt; + newob->ReplaceReference(PBLOCK_REF,pblock->Clone(remap)); + if (projMap) newob->ReplaceReference(PROJMAP_REF,projMap->Clone(remap)); + if (shadProjMap) newob->ReplaceReference(SHADPROJMAP_REF,shadProjMap->Clone(remap)); + if (shadType) newob->ReplaceReference(SHADTYPE_REF,shadType->Clone(remap)); + if (emitter) newob->ReplaceReference(EMITTER_REF ,emitter->Clone(remap)); + BaseClone(this, newob, remap); + return(newob); + */ + + //plRTLightBase *obj = (plRTLightBase*) fClassDesc->Create(false); + //obj->ReplaceReference(kRefComp, fLightPB->Clone(remap)); + return obj; +} +#endif + + +void plRTLightBase::FreeCaches() +{ +// fMesh.FreeAll(); +} + + + +void plRTLightBase::BoxCircle(TimeValue t, float r, float d, Box3& box, int extraPt, Matrix3 *tm) +{ + Point3 q[3*NUM_CIRC_PTS]; + int npts; + float asp; + if ( 1 /*Circle Object*/) { npts = NUM_CIRC_PTS+extraPt; asp = -1.0f; } + else { npts = 4+extraPt; asp = -1.0; } + GetConePoints(t, asp , r, d, q); + box.IncludePoints(q,npts,tm); +} + +void plRTLightBase::BoxDirPoints(TimeValue t, float angle, float dist, Box3 &box, Matrix3 *tm) + { + int npts; + Point3 q[3*NUM_CIRC_PTS]; + npts = 1 /*Circle Object*/? GetCirXPoints(t,angle,dist,q): GetRectXPoints(t,angle,dist,q); + box.IncludePoints(q,npts,tm); +} + + +void plRTLightBase::BoxPoints(TimeValue t, float angle, float dist, Box3 &box, Matrix3 *tm) +{ + if (IsDir()) + BoxCircle(t, angle, dist, box, 0,tm); + else + BoxDirPoints(t, angle, dist, box, tm); +} + + +void plRTLightBase::BoxLight(TimeValue t, INode *inode, Box3& box, Matrix3 *tm) +{ + Point3 pt; + float d; + if (GetTargetPoint(t, inode, pt)) + { + Point3 loc = inode->GetObjectTM(t).GetTrans(); + d = FLength(loc - pt) / FLength(inode->GetObjectTM(t).GetRow(2)); + box += tm? (*tm)*Point3(0.0f, 0.0f, -d): Point3(0.0f, 0.0f, -d); + } + else + { + d = GetTDist(t); + // if(fLightPB->GetInt(kLightType) == RT_FREE_DIR) + // d = GetFallsize(t); + if(IsSpot()) + box += tm? (*tm)*Point3(0.0f, 0.0f, -d): Point3(0.0f, 0.0f, -d); + if(IsDir()) + { + d = GetFallsize(t); + box += tm? (*tm)*Point3(0.0f, 0.0f, -d): Point3(0.0f, 0.0f, -d); + + } + } + if( this->ClassID() == RTSPOT_LIGHT_CLASSID ) +// if (fLightPB->GetInt(kLightType) == RT_FREE_SPOT || fLightPB->GetInt(kLightType) == RT_TARGET_SPOT) + if((fLightPB->GetInt(kShowConeBool,t)) || (extDispFlags & EXT_DISP_ONLY_SELECTED)) + { + float rad = MaxF(GetHotspot(t), GetFallsize(t)); + if (IsDir()) + BoxCircle(t,rad,0.0f,box,1,tm); + BoxCircle(t,rad,d,box,1,tm); + } + if( this->ClassID() == RTDIR_LIGHT_CLASSID + || this->ClassID() == RTPDIR_LIGHT_CLASSID ) +// if (fLightPB->GetInt(kLightType) == RT_FREE_DIR || fLightPB->GetInt(kLightType) == RT_TARGET_DIR) + if((extDispFlags & EXT_DISP_ONLY_SELECTED)) + { + float rad = MaxF(GetHotspot(t), GetFallsize(t)); + if (IsDir()) + BoxCircle(t,rad,0.0f,box,1,tm); + BoxCircle(t,rad,2.82841*GetFallsize(t),box,1,tm); //hack, hack. Do 2root2 at corners... + } + BOOL dispAtten = false; + BOOL dispAttenNear = false; + BOOL dispDecay = false; + if( this->ClassID() == RTOMNI_LIGHT_CLASSID || this->ClassID() == RTSPOT_LIGHT_CLASSID ) + { + dispAtten = fLightPB->GetInt(kUseAttenuationBool,t); + dispAttenNear = 0; //attenNearDisplay; + + dispDecay = (GetDecayType()&&(extDispFlags & EXT_DISP_ONLY_SELECTED)); + } + if( dispAtten || dispDecay) + { + if( this->ClassID() == RTOMNI_LIGHT_CLASSID ) + { + Point3 q[3*NUM_CIRC_PTS]; + float rad = 0; + if (dispAtten) + rad = MaxF(GetAtten(t, ATTEN_START), GetAtten(t, ATTEN_END)); + if (dispDecay) rad = MaxF(rad,0.0/*GetDecayRadius(t)*/); + GetAttenPoints(t, rad, q); + box.IncludePoints(q,3*NUM_CIRC_PTS,tm); + } + if( this->ClassID() == RTSPOT_LIGHT_CLASSID ) + { + if (dispAtten) + { + BoxPoints(t, GetFallsize(t), GetAtten(t,ATTEN_END), box, tm); + BoxPoints(t, GetFallsize(t), GetAtten(t,ATTEN_START), box, tm); + } + if (dispDecay) + BoxPoints(t, GetFallsize(t), 0.0/*GetDecayRadius(t)*/, box, tm); + } + } + +} + +int plRTLightBase::GetRectXPoints(TimeValue t, float angle, float dist, Point3 *q) +{ + int i; + if(dist==0.0f) dist = .00001f; + float ang = DegToRad(angle)/2.0f; + float da,sn,cs,x,y,z,a; + float aspect = GetAspect(t); + float w = dist * (float)tan(ang) * (float)sqrt((double)aspect); + float h = w/aspect; + float wang = (float)atan(w/dist); + float hang = (float)atan(h/dist); + float aw = float(atan(w/dist)*cos(hang)); // half-angle of top and bottom arcs + float ah = float(atan(h/dist)*cos(wang)); // half-angle of left and right arcs + int j = 0; + + // draw horizontal and vertical center lines + da = wang/float(NUM_HALF_ARC); + for(i = -NUM_HALF_ARC, a = -wang; i<= NUM_HALF_ARC; i++, a+=da) + q[j++] = Point3(dist*(float)sin(a), 0.0f, -dist*(float)cos(a)); + da = hang/float(NUM_HALF_ARC); + for(i = -NUM_HALF_ARC, a = -hang; i<= NUM_HALF_ARC; i++, a+=da) + q[j++] = Point3(0.0f, dist*(float)sin(a), -dist*(float)cos(a)); + + + // draw top and bottom arcs + da = aw/float(NUM_HALF_ARC); + sn = (float)sin(hang); + cs = (float)cos(hang); + for (i = -NUM_HALF_ARC, a = -aw; i<= NUM_HALF_ARC; i++, a+=da) + { + x = dist*(float)sin(a); + z = -dist*(float)cos(a); + q[j] = Point3(x, z*sn, z*cs); + q[j+NUM_ARC_PTS] = Point3(x,-z*sn, z*cs); + j++; + } + + j+= NUM_ARC_PTS; + + // draw left and right arcs + da = ah/float(NUM_HALF_ARC); + sn = (float)sin(wang); + cs = (float)cos(wang); + for (i = -NUM_HALF_ARC, a = -ah; i<= NUM_HALF_ARC; i++, a+=da) + { + y = dist*(float)sin(a); + z = -dist*(float)cos(a); + q[j] = Point3( z*sn, y, z*cs); + q[j+NUM_ARC_PTS] = Point3(-z*sn, y, z*cs); + j++; + } + + return 6*NUM_ARC_PTS; +} + +int plRTLightBase::GetCirXPoints(TimeValue t, float angle, float dist, Point3 *q) +{ + int i; + float ang = DegToRad(angle)/2.0f; + float da = ang/float(NUM_HALF_ARC); + // first draw circle: + float d = dist*(float)cos(ang); + GetConePoints(t, -1.0f, angle, d, q); + int j=NUM_CIRC_PTS; + // then draw Arc X + float a = -ang; + for(i = -NUM_HALF_ARC; i<= NUM_HALF_ARC; i++, a+=da) + q[j++] = Point3(0.0f, dist*(float)sin(a), -dist*(float)cos(a)); + a = -ang; + for(i = -NUM_HALF_ARC; i<= NUM_HALF_ARC; i++, a+=da) + q[j++] = Point3(dist*(float)sin(a), 0.0f, -dist*(float)cos(a)); + return NUM_CIRC_PTS + 2*NUM_ARC_PTS; +} + +void plRTLightBase::DrawX(TimeValue t, float asp, int npts, float dist, GraphicsWindow *gw, int indx) +{ + Point3 q[3*NUM_CIRC_PTS+1]; + Point3 u[2]; + GetConePoints(t, asp, GetFallsize(t), dist, q); + gw->polyline(npts, q,NULL, NULL, TRUE, NULL); + u[0] = q[0]; u[1] = q[2*indx]; + gw->polyline(2, u,NULL, NULL, FALSE, NULL); + u[0] = q[indx]; u[1] = q[3*indx]; + gw->polyline(2, u,NULL, NULL, FALSE, NULL); +} + + + +void plRTLightBase::GetAttenPoints(TimeValue t, float rad, Point3 *q) +{ + double a; + float sn, cs; + for(int i = 0; i < NUM_CIRC_PTS; i++) + { + a = (double)i * 2.0 * 3.1415926 / (double)NUM_CIRC_PTS; + sn = rad * (float)sin(a); + cs = rad * (float)cos(a); + q[i+0*NUM_CIRC_PTS] = Point3(sn, cs, 0.0f); + q[i+1*NUM_CIRC_PTS] = Point3(sn, 0.0f, cs); + q[i+2*NUM_CIRC_PTS] = Point3(0.0f, sn, cs); + } +} + + +// Draw warped rectangle +void plRTLightBase::DrawWarpRect(TimeValue t, GraphicsWindow *gw, float angle, float dist, Point3 *q) +{ + GetRectXPoints(t, angle,dist,q); + for (int i=0; i<6; i++) + gw->polyline(NUM_ARC_PTS, q+i*NUM_ARC_PTS,NULL, NULL, FALSE, NULL); +} + +void plRTLightBase::DrawCircleX(TimeValue t, GraphicsWindow *gw, float angle, float dist, Point3 *q) +{ + GetCirXPoints(t, angle,dist,q); + gw->polyline(NUM_CIRC_PTS, q,NULL, NULL, TRUE, NULL); // circle + gw->polyline(NUM_ARC_PTS, q+NUM_CIRC_PTS,NULL, NULL, FALSE, NULL); // vert arc + gw->polyline(NUM_ARC_PTS, q+NUM_CIRC_PTS+NUM_ARC_PTS,NULL, NULL, FALSE, NULL); // horiz arc +} + +void plRTLightBase::DrawSphereArcs(TimeValue t, GraphicsWindow *gw, float r, Point3 *q) +{ + GetAttenPoints(t, r, q); + gw->polyline(NUM_CIRC_PTS, q, NULL, NULL, TRUE, NULL); + gw->polyline(NUM_CIRC_PTS, q+NUM_CIRC_PTS, NULL, NULL, TRUE, NULL); + gw->polyline(NUM_CIRC_PTS, q+2*NUM_CIRC_PTS,NULL, NULL, TRUE, NULL); +} + +// + +void plRTLightBase::DrawAttenCirOrRect(TimeValue t, GraphicsWindow *gw, float dist, BOOL froze, int uicol) +{ + if (!froze) gw->setColor( LINE_COLOR, GetUIColor(uicol)); + if (IsDir()) + { + int npts,indx; + float asp; + npts = NUM_CIRC_PTS; asp = -1.0f; indx = SEG_INDEX; + DrawX(t, asp, npts, dist, gw, indx); + } + else + { + Point3 q[3*NUM_CIRC_PTS+1]; + if( this->ClassID() == RTOMNI_LIGHT_CLASSID ) + DrawSphereArcs(t, gw, dist, q); + else + DrawCircleX(t, gw, GetFallsize(t),dist,q); + + } +} + + +int plRTLightBase::DrawAtten(TimeValue t, INode *inode, GraphicsWindow *gw) +{ + BOOL dispAtten = false; + BOOL dispDecay = false; + if( this->ClassID() == RTOMNI_LIGHT_CLASSID || this->ClassID() == RTSPOT_LIGHT_CLASSID ) + { + dispAtten = (fLightPB->GetInt(kUseAttenuationBool,t) && (extDispFlags & EXT_DISP_ONLY_SELECTED)); + //BOOL dispAttenNear = (fLightPB->GetInt(kUseNearAtten) && (extDispFlags & EXT_DISP_ONLY_SELECTED))?TRUE:fLightPB->GetInt(kShowFarAttenRanges); + dispDecay = (GetDecayType() && (extDispFlags & EXT_DISP_ONLY_SELECTED)); + } + if (dispAtten || dispDecay) + { + Matrix3 tm = inode->GetObjectTM(t); + gw->setTransform(tm); + BOOL froze = inode->IsFrozen() && !inode->Dependent(); + if (dispAtten) + { + DrawAttenCirOrRect(t, gw, GetAtten(t,ATTEN_START), froze, COLOR_START_RANGE); + DrawAttenCirOrRect(t, gw, GetAtten(t,ATTEN_END), froze, COLOR_END_RANGE); + } + + if (dispDecay) + { + DrawAttenCirOrRect(t, gw, 0.0 /*DecayRadius() Stuff here */, froze, COLOR_DECAY_RADIUS); + } + } + return 0; +} + +void plRTLightBase::GetLocalBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box) +{ + //BuildMeshes(t); + //box = fMesh.getBoundingBox(); +// int nv; +// Matrix3 tm; +// GetMat(t, node,vpt,tm); +// Point3 loc = tm.GetTrans(); +// nv = fMesh.getNumVerts(); +// box.Init(); +// if(!(extDispFlags & EXT_DISP_ZOOM_EXT)) +// box.IncludePoints(fMesh.verts,nv,&tm); +// else +// box += loc; +// tm = node->GetObjectTM(t); +// BoxLight(t, node, box, &tm); + Point3 loc = node->GetObjectTM(t).GetTrans(); + float scaleFactor = vpt->NonScalingObjectSize()*vpt->GetVPWorldWidth(loc) / 360.0f; + box = fMesh.getBoundingBox(); + box.Scale(scaleFactor); + BoxLight(t, node, box, NULL); + +} + + + +/* +RefTargetHandle Clone(RemapDir &remap = NoRemap()) +{ + + plRTLightBase* newOb = TRACKED_NEW plRTLightBase; + newOb->fLightPB->SetValue(kLightOn, 0, this->fLightPB->GetInt(kLightOn, 0)); + newOb->fLightPB->SetValue(kLightColor, 0, this->fLightPB->GetValue(kLightColor, 0)); + newOb->fLightPB->SetValue(kLightExclude, 0, this->fLightPB->GetValue(kLightExclude, 0)); + newOb->fLightPB->SetValue(kRed, 0, this->fLightPB->GetInt(kRed, 0)); + newOb->fLightPB->SetValue(kGreen, 0, this->fLightPB->GetInt(kGreen, 0)); + newOb->fLightPB->SetValue(kBlue, 0, this->fLightPB->GetInt(kBlue, 0)); + newOb->fLightPB->SetValue(kHue, 0, this->fLightPB->GetInt(kHue, 0)); + newOb->fLightPB->SetValue(kSat, 0, this->fLightPB->GetInt(kSat, 0)); + newOb->fLightPB->SetValue(kVal, 0, this->fLightPB->GetInt(kVal, 0)); + newOb->fLightPB->SetValue(kIntensity, 0, this->fLightPB->GetFloat(kIntensity, 0)); + newOb->fLightPB->SetValue(kContrast, 0, this->fLightPB->GetFloat(kContrast, 0)); + newOb->fLightPB->SetValue(kDiffSoft, 0, this->fLightPB->GetFloat(kDiffSoft, 0)); + newOb->fLightPB->SetValue(kDiffOn, 0, this->fLightPB->GetInt(kDiffOn, 0)); + newOb->fLightPB->SetValue(kStartAttenNear, 0, this->fLightPB->GetFloat(kStartAttenNear, 0)); + newOb->fLightPB->SetValue(kAmbiOnly, 0, this->fLightPB->GetInt(kAmbiOnly, 0)); + newOb->fLightPB->SetValue(kEndAttenNear, 0, this->fLightPB->GetFloat(kEndAttenNear, 0)); + newOb->fLightPB->SetValue(kUseNearAtten, 0, this->fLightPB->GetInt(kUseNearAtten, 0)); + newOb->fLightPB->SetValue(kShowNearAttenRanges, 0, this->fLightPB->GetInt(kShowNearAttenRanges, 0)); + newOb->fLightPB->SetValue(kStartAttenFar, 0, this->fLightPB->GetFloat(kStartAttenFar, 0)); + newOb->fLightPB->SetValue(kEndAttenFar, 0, this->fLightPB->GetFloat(kEndAttenFar, 0)); + + newOb->fLightPB->SetValue(kUseFarAtten, 0, this->fLightPB->GetInt(kUseFarAtten, 0)); + newOb->fLightPB->SetValue(kShowFarAttenRanges, 0, this->fLightPB->GetInt(kShowFarAttenRanges, 0)); + newOb->fLightPB->SetValue(kLightDecay, 0, this->fLightPB->GetInt(kLightDecay, 0)); + newOb->fLightPB->SetValue(kRed, 0, this->fLightPB->GetInt(kRed, 0)); + newOb->fLightPB->SetValue(kGreen, 0, this->fLightPB->GetInt(kGreen, 0)); + newOb->fLightPB->SetValue(kBlue, 0, this->fLightPB->GetInt(kBlue, 0)); + newOb->fLightPB->SetValue(kHue, 0, this->fLightPB->GetInt(kHue, 0)); + newOb->fLightPB->SetValue(kSat, 0, this->fLightPB->GetInt(kSat, 0)); + newOb->fLightPB->SetValue(kVal, 0, this->fLightPB->GetInt(kVal, 0)); + newOb->fLightPB->SetValue(kIntensity, 0, this->fLightPB->GetFloat(kIntensity, 0)); + newOb->fLightPB->SetValue(kContrast, 0, this->fLightPB->GetFloat(kContrast, 0)); + newOb->fLightPB->SetValue(kDiffSoft, 0, this->fLightPB->GetFloat(kDiffSoft, 0)); + newOb->fLightPB->SetValue(kDiffOn, 0, this->fLightPB->GetInt(kDiffOn, 0)); + newOb->fLightPB->SetValue(kStartAttenNear, 0, this->fLightPB->GetFloat(kStartAttenNear, 0)); + newOb->fLightPB->SetValue(kAmbiOnly, 0, this->fLightPB->GetInt(kAmbiOnly, 0)); + newOb->fLightPB->SetValue(kEndAttenNear, 0, this->fLightPB->GetFloat(kEndAttenNear, 0)); + newOb->fLightPB->SetValue(kUseNearAtten, 0, this->fLightPB->GetInt(kUseNearAtten, 0)); + newOb->fLightPB->SetValue(kShowNearAttenRanges, 0, this->fLightPB->GetInt(kShowNearAttenRanges, 0)); + newOb->fLightPB->SetValue(kStartAttenFar, 0, this->fLightPB->GetFloat(kStartAttenFar, 0)); + newOb->fLightPB->SetValue(kEndAttenFar, 0, this->fLightPB->GetFloat(kEndAttenFar, 0)); + +*/ + +/* + + + kLightType, //Inserted in v1 + kLightOn, //Inserted in v1 + kLightColor, //Inserted in v1 + kLightExclude, //Inserted in v1 + kRed, //Inserted in v1 + kGreen, //Inserted in v1 + kBlue, //Inserted in v1 + kHue, //Inserted in v1 + kSat, //Inserted in v1 + kVal, //Inserted in v1 + kIntensity, //Inserted in v1 + kContrast, //Inserted in v1 + kDiffSoft, //Inserted in v1 + kDiffOn, //Inserted in v1 + kSpec, //Inserted in v1 + kAmbiOnly, //Inserted in v1 + kStartAttenNear, //Inserted in v1 + kEndAttenNear, //Inserted in v1 + kUseNearAtten, //Inserted in v1 + kShowNearAttenRanges,//Inserted in v1 + kStartAttenFar, //Inserted in v1 + kEndAttenFar, //Inserted in v1 + kUseFarAtten, //Inserted in v1 + kShowFarAttenRanges, //Inserted in v1 + kLightDecay, //Inserted in v1 + kDecayEdit, //Inserted in v1 + kShowDecay, //Inserted in v1 + kUseProjectBool, //Inserted in v1 + kProjMapTexButton, //Inserted in v1 + kShowConeBool, //Inserted in v1 + kOvershootBool, //Inserted in v1 + kHotSpot, //Inserted in v1 + kFallOff, //Inserted in v1 + kLightShapeRadio, //Inserted in v1 + kAspect, //Inserted in v1 + kUseProjectorBool, //Inserted in v1 + kProjMapTexButton2, //Inserted in v1 + kTargetDist //Inserted in v1 + GeneralLight* newob = TRACKED_NEW GeneralLight(type); + newob->enable = enable; + newob->coneDisplay = coneDisplay; + newob->useLight = useLight; + newob->attenDisplay = attenDisplay; + newob->useAtten = useAtten; + newob->useAttenNear = useAttenNear; + newob->attenNearDisplay = attenNearDisplay; + newob->decayDisplay = decayDisplay; + newob->shape = shape; + newob->shadow = shadow; + newob->shadowType = shadowType; + newob->overshoot = overshoot; + newob->projector = projector; + newob->absMapBias = absMapBias; + newob->exclList = exclList; + newob->softenDiffuse = softenDiffuse; + newob->affectDiffuse = affectDiffuse; + newob->affectSpecular = affectSpecular; + newob->ambientOnly = ambientOnly; + newob->decayType = decayType; + newob->atmosShadows = atmosShadows; + newob->atmosOpacity = atmosOpacity; + newob->atmosColAmt = atmosColAmt; + newob->ReplaceReference(PBLOCK_REF,pblock->Clone(remap)); + if (projMap) newob->ReplaceReference(PROJMAP_REF,projMap->Clone(remap)); + if (shadProjMap) newob->ReplaceReference(SHADPROJMAP_REF,shadProjMap->Clone(remap)); + if (shadType) newob->ReplaceReference(SHADTYPE_REF,shadType->Clone(remap)); + if (emitter) newob->ReplaceReference(EMITTER_REF ,emitter->Clone(remap)); + BaseClone(this, newob, remap); + return(newob); + } +*/ + + + +//} + + + + + +void plRTLightBase::GetWorldBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box) +{ + GetLocalBoundBox( t, node, vpt, box ); + box = box * node->GetObjectTM( t ); +} + +void plRTLightBase::GetDeformBBox(TimeValue t, Box3& box, Matrix3 *tm, BOOL useSel ) +{ + box = fMesh.getBoundingBox(tm); +} + + + +int plRTLightBase::Display(TimeValue t, INode *node, ViewExp *vpt, int flags) +{ + + + Matrix3 m; +// if (!enable) +// return 0; + GraphicsWindow *gw = vpt->getGW(); + GetMat(t,node,vpt,m); + gw->setTransform(m); + DWORD rlim = gw->getRndLimits(); + gw->setRndLimits(GW_WIREFRAME|GW_BACKCULL|(gw->getRndMode() & GW_Z_BUFFER)); + if (node->Selected()) + gw->setColor( LINE_COLOR, GetSelColor()); + else if(!node->IsFrozen() && !node->Dependent()) + { + if(fLightPB->GetInt(kLightOn)) + gw->setColor( LINE_COLOR, GetUIColor(COLOR_LIGHT_OBJ)); + // I un-commented this line DS 6/11/99 + else + gw->setColor( LINE_COLOR, 0.0f, 0.0f, 0.0f); + } + + fMesh.render( gw, gw->getMaterial(), + (flags&USE_DAMAGE_RECT) ? &vpt->GetDammageRect() : NULL, COMP_ALL); + + DrawConeAndLine(t, node, gw, 1); +// DrawAtten(t, node, gw); + gw->setRndLimits(rlim); + return 0; + +} + + + +static void RemoveScaling(Matrix3 &tm) +{ + AffineParts ap; + decomp_affine(tm, &ap); + tm.IdentityMatrix(); + tm.SetRotate(ap.q); + tm.SetTrans(ap.t); +} + +void plRTLightBase::GetMat(TimeValue t, INode* inode, ViewExp *vpt, Matrix3& tm) +{ + tm = inode->GetObjectTM(t); +// tm.NoScale(); + RemoveScaling(tm); + float scaleFactor = vpt->NonScalingObjectSize()*vpt->GetVPWorldWidth(tm.GetTrans())/(float)360.0; + tm.Scale(Point3(scaleFactor,scaleFactor,scaleFactor)); +} + + +void plRTLightBase::GetConePoints(TimeValue t, float aspect, float angle, float dist, Point3 *q) +{ + float ta = (float)tan(0.5*DegToRad(angle)); + if(1 /*fLightPB->GetFloat(kAspect, t) <= 0.0f*/) + { + // CIRCULAR + float rad = dist * ta; + double a; + if(IsDir()) + rad = angle; + int i; + for(i = 0; i < NUM_CIRC_PTS; i++) { + a = (double)i * 2.0 * 3.1415926 / (double)NUM_CIRC_PTS; + q[i] = Point3(rad*(float)sin(a), rad*(float)cos(a), -dist); + } + q[i] = q[0] + Point3(0.0f, 15.0f, 0.0f); + } + else + { + // RECTANGULAR + float w = IsDir()? angle : dist * ta * (float)sqrt((double)aspect); + float h = w / aspect; + q[0] = Point3( w, h,-dist); + q[1] = Point3(-w, h,-dist); + q[2] = Point3(-w,-h,-dist); + q[3] = Point3( w,-h,-dist); + q[4] = Point3( 0.0f, h+15.0f, -dist); + q[5] = Point3( 0.0f, h, -dist); + } +} + +#define HOTCONE 0 +#define FALLCONE 1 + +void plRTLightBase::DrawCone(TimeValue t, GraphicsWindow *gw, float dist) +{ + Point3 q[NUM_CIRC_PTS+1], u[3]; + int dirLight = IsDir(); + int i; + BOOL dispAtten = false; + BOOL dispDecay = false; + if( this->ClassID() == RTOMNI_LIGHT_CLASSID || this->ClassID() == RTSPOT_LIGHT_CLASSID ) + { + dispAtten = (fLightPB->GetInt(kUseAttenuationBool, t) && (extDispFlags & EXT_DISP_ONLY_SELECTED));//attenDisplay; + dispDecay = (/*fLightPB->GetInt(kAttenTypeRadio, t)*/GetDecayType() && (extDispFlags & EXT_DISP_ONLY_SELECTED)); + } + if(!IsDir()) + GetConePoints(t, -1.0f, GetHotspot(t), dist, q); + else + GetConePoints(t, -1.0f, 20.0, dist, q); + + gw->setColor( LINE_COLOR, GetUIColor(COLOR_HOTSPOT)); + if(1 /*Circular Hack*/) { + // CIRCULAR + if(GetHotspot(t) >= GetFallsize(t)) + { + // draw (far) hotspot circle + u[0] = q[0]; + u[1] = q[NUM_CIRC_PTS]; + gw->polyline( 2, u, NULL, NULL, FALSE, NULL); + } + gw->polyline(NUM_CIRC_PTS, q, NULL, NULL, TRUE, NULL); + if (dirLight) + { + // draw 4 axial hotspot lines + for (i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX) + { + u[0] = q[i]; u[1] = q[i]; u[1].z += dist; + gw->polyline( 2, u, NULL, NULL, FALSE, NULL ); + } + GetConePoints(t, -1.0f, 20/*GetHotspot(t)*/, 0.0f, q); + // draw (near) hotspot circle + gw->polyline(NUM_CIRC_PTS, q, NULL, NULL, TRUE, NULL); + } + else + { + // draw 4 axial lines + u[0] = Point3(0,0,0); + for (i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX) + { + u[1] = q[i]; + gw->polyline( 2, u, NULL, NULL, FALSE, NULL ); + } + } + if(!IsDir()) + GetConePoints(t, -1.0f, GetFallsize(t), dist, q); + else + GetConePoints(t, -1.0f, 200.0, dist, q); + gw->setColor( LINE_COLOR, GetUIColor(COLOR_FALLOFF)); + if(GetHotspot(t) < GetFallsize(t)) + { + // draw (far) fallsize circle + u[0] = q[0]; u[1] = q[NUM_CIRC_PTS]; + gw->polyline( 2, u, NULL, NULL, FALSE, NULL); + u[0] = Point3(0,0,0); + } + gw->polyline(NUM_CIRC_PTS, q, NULL, NULL, TRUE, NULL); + if (dirLight) + { + float dfar = q[0].z; + float dnear = 0.0f; + if (dispAtten) + { + dfar = MinF(-GetAtten(t,ATTEN_END),dfar); + /// dnear = MaxF(-GetAtten(t,ATTEN_START),dnear); + } + if (dispDecay) { + dfar = MinF(/*-GetDecayRadius(t)*/ 0.0,dfar); + } + + // draw axial fallsize lines + for (i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX) + { + u[0] = q[i]; u[0].z = dfar; u[1] = q[i]; u[1].z = dnear; + gw->polyline( 2, u, NULL, NULL, FALSE, NULL ); + } + + GetConePoints(t, -1.0f, 10000.0, 0.0f, q); + // draw (near) fallsize circle + gw->polyline(NUM_CIRC_PTS, q, NULL, NULL, TRUE, NULL); + + } + else + { + float cs = (float)cos(DegToRad(GetFallsize(t)*0.5f)); + float dfar = q[0].z; + if (dispAtten) + dfar = MinF(-cs*GetAtten(t,ATTEN_END),dfar); + if (dispDecay) + dfar = MinF(/*-cs*GetDecayRadius(t)*/0.0,dfar); + + for (i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX) + { + u[1] = -q[i]*dfar/dist; + gw->polyline( 2, u, NULL, NULL, FALSE, NULL ); + } + } + } + +} + + +int plRTLightBase::DrawConeAndLine(TimeValue t, INode* inode, GraphicsWindow *gw, int drawing ) + { + if(!IsSpot() && !IsDir()) + return 0; + Matrix3 tm = inode->GetObjectTM(t); + gw->setTransform(tm); + gw->clearHitCode(); + if( 0 ) + { + Point3 pt,v[3]; + if (GetTargetPoint(t, inode, pt)) + { + float den = FLength(tm.GetRow(2)); + float dist = (den!=0) ? FLength(tm.GetTrans()-pt) / den : 0.0f; + fLightPB->SetValue(kAttenMaxFalloffEdit, t, dist); + + //fLightPB->SetValue(kTargetDist, t, dist); + //if (hSpotLight&&(currentEditLight==this)) { + // TCHAR buf[40]; + // _stprintf(buf,_T("%0.3f"),targDist); + // SetWindowText(GetDlgItem(hSpotLight,IDC_TARG_DISTANCE),buf); + // } + + if ((drawing != -1) && (fLightPB->GetInt(kShowConeBool, t) || (extDispFlags & EXT_DISP_ONLY_SELECTED))) + DrawCone(t, gw, dist); + if(!inode->IsFrozen() && !inode->Dependent()) + gw->setColor( LINE_COLOR, GetUIColor(COLOR_TARGET_LINE)); + v[0] = Point3(0,0,0); + v[1] = Point3(0.0f, 0.0f, (drawing == -1)? (-0.9f * dist): -dist); + gw->polyline( 2, v, NULL, NULL, FALSE, NULL ); + } + + } + else if( this->ClassID() == RTSPOT_LIGHT_CLASSID ) + { + if ((drawing != -1) && (fLightPB->GetInt(kShowConeBool, t) || (extDispFlags & EXT_DISP_ONLY_SELECTED))) + DrawCone(t, gw, fLightPB->GetFloat( kAttenMaxFalloffEdit, t ) ); + } + else if( this->ClassID() == RTDIR_LIGHT_CLASSID ) + { + if ((extDispFlags & EXT_DISP_ONLY_SELECTED)) + DrawCone(t, gw, 500/*GetTDist(t)*/); + } + return gw->checkHitCode(); +} + + +//// DrawArrow /////////////////////////////////////////////////////////////// + +void plRTLightBase::DrawArrow( TimeValue t, GraphicsWindow *gw, Point3 &direction, float dist ) +{ + Point3 pts[ 5 ]; + + + pts[ 0 ] = Point3( 0, 0, 0 ); + pts[ 1 ] = direction * dist; + pts[ 3 ] = pts[ 1 ] - direction * 10.f; + pts[ 2 ] = pts[ 3 ] + Point3( direction.y, direction.z, direction.x ) * 5.f; + + gw->polyline( 4, pts, nil, nil, true, nil ); +} + +int plRTLightBase::HitTest(TimeValue t, INode *node, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt) +{ + DWORD savedLimits; + GraphicsWindow *gw = vpt->getGW(); + HitRegion hitRegion; + int res; + Matrix3 m; + MakeHitRegion(hitRegion, type, crossing, 4, p); + Material *mtl = gw->getMaterial(); + + gw->setRndLimits( ((savedLimits = gw->getRndLimits()) | GW_PICK) & ~(GW_ILLUM|GW_BACKCULL)); + GetMat(t,node,vpt,m); + + //BuildMeshes(t); + gw->setTransform(m); + res = fMesh.select( gw, mtl, &hitRegion, flags & HIT_ABORTONHIT); + // if not, check the target line, and set the pair flag if it's hit + if( !res ) + { + // this special case only works with point selection of targeted lights + if((type != HITTYPE_POINT) || !node->GetTarget()) + return 0; + // don't let line be active if only looking at selected stuff and target isn't selected + if((flags & HIT_SELONLY) && !node->GetTarget()->Selected() ) + return 0; + gw->clearHitCode(); + if(res = DrawConeAndLine(t, node, gw, -1)) + node->SetTargetNodePair(1); + } + gw->setRndLimits(savedLimits); + return res; + + //return fMesh.select(gw,node->Mtls(),&hitRegion,flags & HIT_ABORTONHIT,node->NumMtls()); +} +static void GenericSnap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt) +{ + // Make sure the vertex priority is active and at least as important as the best snap so far + if(snap->vertPriority > 0 && snap->vertPriority <= snap->priority) + { + Matrix3 tm = inode->GetObjectTM(t); + GraphicsWindow *gw = vpt->getGW(); + + gw->setTransform(tm); + + Point2 fp = Point2((float)p->x, (float)p->y); + IPoint3 screen3; + Point2 screen2; + Point3 vert(0.0f,0.0f,0.0f); + + gw->wTransPoint(&vert,&screen3); + + screen2.x = (float)screen3.x; + screen2.y = (float)screen3.y; + + // Are we within the snap radius? + int len = (int)Length(screen2 - fp); + if(len <= snap->strength) + { + // Is this priority better than the best so far? + if(snap->vertPriority < snap->priority) + { + snap->priority = snap->vertPriority; + snap->bestWorld = vert * tm; + snap->bestScreen = screen2; + snap->bestDist = len; + } + // Closer than the best of this priority? + else if(len < snap->bestDist) + { + snap->priority = snap->vertPriority; + snap->bestWorld = vert * tm; + snap->bestScreen = screen2; + snap->bestDist = len; + } + } + } +} + +void plRTLightBase::Snap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt) +{ + GenericSnap(t,inode,snap,p,vpt); +} +// This generic snap routine is used to snap to the 0,0,0 point of the given node. For lights, +// this works to snap all types. + + + +RefTargetHandle plRTLightBase::GetReference(int i) +{ + /* + kRefProjMap, + kRefShadowProjMap, + kRefShadowType, + *///Texmap* MyMap; + switch(i) + { + case kRefGeneralLightProp: + return NULL; + case kRefProjMap: + //if(fLightPB->GetTexmap(kProjMapTexButton, 0) != NULL) + //{ + // MyMap = fLightPB->GetTexmap(kProjMapTexButton, 0); + //return (RefTargetHandle) MyMap; + //}else + return NULL; + case kRefShadowProjMap: + return NULL; + case kRefShadowType: + return NULL; + case kRefOmniLight: + case kRefSpotLight: + case kRefTSpotLight: + case kRefDirLight: + case kRefTDirLight: + case kRefProjDirLight: + return (RefTargetHandle)fLightPB; + default: + return NULL; + } + +} + +void plRTLightBase::SetReference(int ref, RefTargetHandle rtarg) +{ + //Texmap* MyMap; + switch(ref) + { + case kRefGeneralLightProp: + return; + case kRefProjMap: + + //MyMap = (Texmap *)rtarg; + //fLightPB->SetValue(kProjMapTexButton, 0, MyMap); + + //if (projMapName) + // projMapName->SetText(projMap?projMap->GetFullName().data():GetString(IDS_DB_NONE)); + return; + + case kRefShadowProjMap: + return; + case kRefShadowType: + return; + case kRefOmniLight: + case kRefSpotLight: + case kRefTSpotLight: + case kRefDirLight: + case kRefTDirLight: + case kRefProjDirLight: + fLightPB = (IParamBlock2*)rtarg; break; + } + //fLightPB = (IParamBlock2*)rtarg; +} + +RefResult plRTLightBase::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) +{ + if( fLightPB ) + { + ParamID param = fLightPB->LastNotifyParamID(); + + if( param == kAmbientOnlyStub ) + { + fLightPB->EnableNotifications( false ); + fLightPB->SetValue( kAmbientOnlyStub, TimeValue( 0 ), false ); + // Assume this was true, since, well, we're IN NotifyRefChanged.... + fLightPB->EnableNotifications( true ); + + return REF_SUCCEED; + } + } + + return REF_SUCCEED; +} + +void plRTLightBase::BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev) +{ + fIP = ip; + fClassDesc->BeginEditParams(ip, this, flags, prev); +} + +void plRTLightBase::EndEditParams(IObjParam *ip, ULONG flags, Animatable *next) +{ + GenLight::EndEditParams( ip, flags, next ); + fIP = NULL; + fClassDesc->EndEditParams(ip, this, flags, next); +} + +RefResult plRTLightBase::EvalLightState(TimeValue t, Interval& valid, LightState *ls) +{ + //t uselight; +//#if 0 //fLightPB->GetInt(kLightOn, t); + if(fLightPB->GetInt(kLightOn, t) == true) + ls->color = GetRGBColor(t,valid); + else + ls->color = Color(0,0,0); + ls->on = fLightPB->GetInt(kLightOn, t); + ls->intens = GetIntensity(t, valid); + if( this->ClassID() == RTSPOT_LIGHT_CLASSID || this->ClassID() == RTOMNI_LIGHT_CLASSID ) + { + ls->hotsize = GetHotspot(t, valid); + ls->fallsize = GetFallsize(t, valid); + ls->useNearAtten = GetUseAttenNear(); + ls->useAtten = GetUseAtten(); + ls->attenStart = GetAtten(t, ATTEN_START, valid); + ls->attenEnd = GetAtten(t, ATTEN_END, valid); + }else + { + ls->hotsize = 20; + ls->fallsize = 10000; + ls->useNearAtten = false; //GetUseAttenNear(); + ls->useAtten = false; //GetUseAtten(); + //ls->attenStart = GetAtten(t, ATTEN_START, valid); + //ls->attenEnd = GetAtten(t, ATTEN_END, valid); + + + } + ls->shape = GW_SHAPE_CIRCULAR; //fLightPB->GetInt(kLightShapeRadio); + + ls->aspect = -1.0;//GetAspect(t, valid); + ls->overshoot = false; //GetOvershoot(); + ls->shadow = GetShadow(); + ls->ambientOnly = false; //fLightPB->GetValue( kAmbiOnly,t, ls->ambientOnly, valid); //ls->ambientOnly = fLightP.AmbiOnly; + ls->affectDiffuse = true; //fLightPB->GetInt(kDiffOn,t); //ls->affectDiffusey = fLightP.AmbiOnly; + ls->affectSpecular = fLightPB->GetInt(kSpec,t); //ls- = fLightP.DiffOn;; + + //ls-> + + if( this->ClassID() == RTOMNI_LIGHT_CLASSID ) + ls->type = OMNI_LGT; + else if( this->ClassID() == RTDIR_LIGHT_CLASSID || this->ClassID() == RTPDIR_LIGHT_CLASSID ) + ls->type = DIRECT_LGT; + else + ls->type = SPOT_LGT; + + return REF_SUCCEED; +} + + +#if 0 +#define PLASMAOBJ_DATA_CHUNK 1 + +IOResult plRTLightBase::Save(ISave* isave) +{ + return IO_OK; +} + +IOResult plRTLightBase::Load(ILoad* iload) +{ + ULONG nb; + IOResult res; + int numrefs = 0; + + while (IO_OK == (res = iload->OpenChunk())) + { + if (iload->CurChunkID() == PLASMAOBJ_DATA_CHUNK) + res = iload->Read(&numrefs, sizeof(int), &nb); + iload->CloseChunk(); + + if (res != IO_OK) + return res; + } + + return IO_OK; +} + +#endif + + +ObjLightDesc *plRTLightBase::CreateLightDesc(INode *n, BOOL forceShadowBuf) +{ + + return NULL; + +} + + +void plRTLightBase::SetHotspot(TimeValue t, float f) +{ + if(!IsSpot()) + return; + if(f < 0.5f) + f = 0.5f; + if(!IsDir() && (f > 179.5f)) + f = 179.5f; + //pblock->SetValue( PB_HOTSIZE, t, f ); + fLightPB->SetValue(kHotSpot, t, f); + + //fLightP.HotSpot = f; + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +} + +float plRTLightBase::GetHotspot(TimeValue t, Interval& valid) +{ + Interval iv; + + if(!IsSpot() && !IsDir()) + return -1.0f; + float f; + //pblock->GetValue( PB_HOTSIZE, t, f, valid ); + if(IsSpot()) + fLightPB->GetValue(kHotSpot, t, f, FOREVER); + else + f = 20.0; + + if(GetFallsize(t, iv) < f ) + return GetFallsize(t, iv) - 2.0; + return f; +} + +void plRTLightBase::SetRGBColor(TimeValue t, Point3& rgb) +{ + //fLightPB->SetValue(plRTLightBase::kRed, t, rgb.x); + //fLightPB->SetValue(plRTLightBase::kGreen, t, rgb.y); + //fLightPB->SetValue(plRTLightBase::kBlue, t, rgb.z); + fLightPB->SetValue(kLightColor, t, rgb ); + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); + + +} + +Point3 plRTLightBase::GetRGBColor(TimeValue t, Interval &valid) +{ + //Point3 Foo = Point3(fLightPB->GetInt(plRTLightBase::kRed, t), fLightPB->GetInt(plRTLightBase::kGreen, t), fLightPB->GetInt(plRTLightBase::kBlue, t)); + Point3 rgb; + fLightPB->GetValue( kLightColor, t, rgb, valid ); + return rgb; +} + + + +void plRTLightBase::SetFallsize(TimeValue t, float f) +{ + if(!IsSpot() ) + return; + if(f < 0.5f) + f = 0.5f; + if(!IsDir() && (f > 179.5f)) + f = 179.5f; + //pblock->SetValue( PB_FALLSIZE, t, f ); + fLightPB->SetValue(kFallOff, t, f); + + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +} + +float plRTLightBase::GetFallsize(TimeValue t, Interval& valid) +{ + if(!IsSpot() && !IsDir()) + return -1.0f; + float f; + + //pblock->GetValue( PB_FALLSIZE, t, f, valid ); + if(IsSpot()) + fLightPB->GetValue(kFallOff, t, f, valid); + else + f = 200.0; + return f; +} + + +void plRTLightBase::SetAtten(TimeValue t, int which, float f) +{ + + if(which != ATTEN_START) + fLightPB->SetValue(kAttenMaxFalloffEdit, t, f); + + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +} + + +float plRTLightBase::GetAtten(TimeValue t, int which, Interval& valid) +{ + float f = 0.0; + //if(fLightPB->GetInt(kEndAttenFar, t) == 1) + //if(LyteType == RT_OMNI) + // fLightPB->GetValue(kStartAttenFar, t, f, FOREVER); + //else + // fLightPB->GetValue(kStartAttenNear, t, f, FOREVER); + if(which == LIGHT_ATTEN_START) + return f; + else + return( fLightPB->GetFloat(kAttenMaxFalloffEdit, t)); + + //return f; +} + + +void plRTLightBase::SetTDist(TimeValue t, float f) +{ +// int i; +// fLightPB->GetInt(kLightType, t, i); + + //pblock->SetValue( PB_TDIST, t, f ); + //To be implemented. + if( IHasAttenuation() ) + fLightPB->SetValue(kAttenMaxFalloffEdit, t, f); + //fLightPB->SetValue(kTargetDist, t, f); + + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +} + +float plRTLightBase::GetTDist(TimeValue t, Interval& valid) +{ + if(!IsSpot()) + return -1.0f; +// int i; + //fLightPB->GetValue(kLightType, t, i, valid); + float f = -1.0; + + fLightPB->GetFloat( kAttenMaxFalloffEdit, t ); + + //pblock->GetValue( PB_TDIST, t, f, valid ); + //fLightPB->GetValue(kTargetDist, t, f, valid); + return f; +} + +void plRTLightBase::SetConeDisplay(int s, int notify) +{ + if(!IsDir()) + fLightPB->SetValue(kShowConeBool, 0, s); + if(notify && IsSpot()) + NotifyDependents(FOREVER, PART_OBJ, REFMSG_CHANGE); +} + +BOOL plRTLightBase::GetConeDisplay(void) +{ + if(!IsDir()) + return fLightPB->GetInt(kShowConeBool); + return + false; +} + + +void plRTLightBase::SetProjMap(BitmapInfo* pmap) +{ + //plLayerTex* MyMap = TRACKED_NEW plLayerTex; + if(!fTex) + fTex = TRACKED_NEW plLayerTex; + fTex->SetBitmap(pmap); + ReplaceReference(kRefProjMap,fTex); + + IParamBlock2 *bitmapPB = fTex->GetParamBlockByID(plLayerTex::kBlkBitmap); + bitmapPB->SetValue(kBmpUseBitmap, 0, 1); + + // This is set in the call to fTex->SetBitmap(pmap) + //PBBitmap pbb(*pmap); + //bitmapPB->SetValue(kBmpBitmap, 0, &pbb); + + //Texmap* MyMap; + //fLightPB->GetValue(kProjMapTexButton, 0, MyMap, FOREVER); + if (fTex) fLightPB->SetValue(kUseProjectorBool, 0, true); + NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED); + if( fLightPB->GetMap() ) + { + fLightPB->GetMap()->Invalidate(kProjMapTexButton); + fLightPB->GetMap()->Invalidate(kUseProjectorBool); + } + +} + + +BOOL plRTLightBase::GetShadow() +{ + return fLightPB->GetInt(kCastShadows); +} + +void plRTLightBase::SetShadow(int a) +{ + fLightPB->SetValue(kCastShadows, 0, 1); +} + +Texmap* plRTLightBase::GetProjMap() +{ + if( !fLightPB->GetInt(kUseProjectorBool) ) + return NULL; + + if( GetTex() ) + { + const char* dbgTexName = GetTex()->GetName(); + + IParamBlock2 *bitmapPB = fTex->GetParamBlockByID(plLayerTex::kBlkBitmap); + hsAssert(bitmapPB, "LayerTex with no param block"); + + int noCompress = fLightPB->GetInt(kProjNoCompress); + int noMip = fLightPB->GetInt(kProjNoMip); + bitmapPB->SetValue(kBmpNonCompressed, TimeValue(0), noCompress); + bitmapPB->SetValue(kBmpNoFilter, TimeValue(0), noMip); + } + + return (Texmap*) GetTex(); +} + +void plRTLightBase::UpdateTargDistance(TimeValue t, INode* inode) +{ + if( this->ClassID() == RTSPOT_LIGHT_CLASSID ) + { + Point3 pt,v[3]; + if (GetTargetPoint(t, inode, pt)) + { + Matrix3 tm = inode->GetObjectTM(t); + float den = FLength(tm.GetRow(2)); + float dist = (den!=0) ? FLength(tm.GetTrans()-pt) / den : 0.0f; + fLightPB->SetValue(kAttenMaxFalloffEdit, t, dist); + + // fLightPB->SetValue(kTargetDist, t, dist); + //TCHAR buf[40]; + //_stprintf(buf,_T("%0.3f"),targDist); + //SetWindowText(GetDlgItem(hSpotLight,IDC_TARG_DISTANCE),buf); + } + } + +} + + + + +/// +// +// +// Target Creation for Targeted Lights... +// +// +// + + +#if 0 +class TSpotCreationManager : public MouseCallBack, ReferenceMaker +{ +private: + CreateMouseCallBack *createCB; + INode *lgtNode,*targNode; + plRTLightBase *lgtObject; + TargetObject *targObject; + int attachedToNode; + IObjCreate *createInterface; + ClassDesc *cDesc; + Matrix3 mat; // the nodes TM relative to the CP + IPoint2 pt0; + int ignoreSelectionChange; + int lastPutCount; + + void CreateNewObject(); + + int NumRefs() { return 1; } + RefTargetHandle GetReference(int i) { return (RefTargetHandle)lgtNode; } + void SetReference(int i, RefTargetHandle rtarg) { lgtNode = (INode *)rtarg; } + + // StdNotifyRefChanged calls this, which can change the partID to new value + // If it doesnt depend on the particular message& partID, it should return + // REF_DONTCARE + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + +public: + void Begin( IObjCreate *ioc, ClassDesc *desc ); + void End(); + + TSpotCreationManager() { ignoreSelectionChange = FALSE; } + int proc( HWND hwnd, int msg, int point, int flag, IPoint2 m ); + BOOL SupportAutoGrid(){return TRUE;} +}; + + +#define CID_TSPOTCREATE CID_USER + 3 + +class TSpotCreateMode : public CommandMode { + TSpotCreationManager proc; +public: + void Begin( IObjCreate *ioc, ClassDesc *desc ) { proc.Begin( ioc, desc ); } + void End() { proc.End(); } + + int Class() { return CREATE_COMMAND; } + int ID() { return CID_TSPOTCREATE; } + MouseCallBack *MouseProc(int *numPoints) { *numPoints = 1000000; return &proc; } + ChangeForegroundCallback *ChangeFGProc() { return CHANGE_FG_SELECTED; } + BOOL ChangeFG( CommandMode *oldMode ) { return (oldMode->ChangeFGProc() != CHANGE_FG_SELECTED); } + void EnterMode() { + } + void ExitMode() { + } + BOOL IsSticky() { return FALSE; } +}; + +static TSpotCreateMode theTSpotCreateMode; + +#endif + +#if 0 +void TSpotCreationManager::Begin( IObjCreate *ioc, ClassDesc *desc ) +{ + createInterface = ioc; + cDesc = desc; + attachedToNode = FALSE; + createCB = NULL; + lgtNode = NULL; + targNode = NULL; + lgtObject = NULL; + targObject = NULL; + CreateNewObject(); +} + +void TSpotCreationManager::End() +{ + if ( lgtObject ) { + lgtObject->EndEditParams( (IObjParam*)createInterface, END_EDIT_REMOVEUI, NULL); + if ( !attachedToNode ) { + // RB 4-9-96: Normally the hold isn't holding when this + // happens, but it can be in certain situations (like a track view paste) + // Things get confused if it ends up with undo... + theHold.Suspend(); + //delete lgtObject; + lgtObject->DeleteThis(); // JBW 11.1.99, this allows scripted plugin lights to delete cleanly + lgtObject = NULL; + theHold.Resume(); + // RB 7/28/97: If something has been put on the undo stack since this object was created, we have to flush the undo stack. + if (theHold.GetGlobalPutCount()!=lastPutCount) { + GetSystemSetting(SYSSET_CLEAR_UNDO); + } + macroRec->Cancel(); // JBW 4/23/99 + } + else if ( lgtNode ) { + // Get rid of the reference. + DeleteReference(0); // sets lgtNode = NULL + } + } +} + +RefResult TSpotCreationManager::NotifyRefChanged( + Interval changeInt, + RefTargetHandle hTarget, + PartID& partID, + RefMessage message) +{ + switch (message) { + + case REFMSG_PRENOTIFY_PASTE: + case REFMSG_TARGET_SELECTIONCHANGE: + if ( ignoreSelectionChange ) { + break; + } + if (lgtObject && lgtNode==hTarget) { + // this will set camNode== NULL; + DeleteReference(0); + goto endEdit; + } + // fall through + + case REFMSG_TARGET_DELETED: + if ( lgtObject && lgtNode==hTarget ) { + endEdit: + lgtObject->EndEditParams( (IObjParam*)createInterface, 0, NULL); + lgtObject = NULL; + lgtNode = NULL; + CreateNewObject(); + attachedToNode = FALSE; + } + else if (targNode==hTarget) { + targNode = NULL; + targObject = NULL; + } + break; + } + return REF_SUCCEED; +} + + +void TSpotCreationManager::CreateNewObject() +{ + lgtObject = (plRTLightBase *)cDesc->Create(); + lastPutCount = theHold.GetGlobalPutCount(); + + macroRec->BeginCreate(cDesc); // JBW 4/23/99 + + // Start the edit params process + if ( lgtObject ) { + lgtObject->BeginEditParams( (IObjParam*)createInterface, BEGIN_EDIT_CREATE, NULL ); + } +} + +static void whoa(){}; + +static BOOL needToss; + +int TSpotCreationManager::proc( + HWND hwnd, + int msg, + int point, + int flag, + IPoint2 m ) +{ + int res; + TSTR targName; + ViewExp *vpx = createInterface->GetViewport(hwnd); + assert( vpx ); + + switch ( msg ) { + case MOUSE_POINT: + switch ( point ) { + case 0: + pt0 = m; + assert( lgtObject ); + vpx->CommitImplicitGrid(m, flag); //KENNY MERGE + if ( createInterface->SetActiveViewport(hwnd) ) { + return FALSE; + } + + if (createInterface->IsCPEdgeOnInView()) { + res = FALSE; + goto done; + } + + // if lights were hidden by category, re-display them + GetCOREInterface()->SetHideByCategoryFlags( + GetCOREInterface()->GetHideByCategoryFlags() & ~HIDE_LIGHTS); + + if ( attachedToNode ) { + // send this one on its way + lgtObject->EndEditParams( (IObjParam*)createInterface, 0, NULL); + macroRec->EmitScript(); // JBW 4/23/99 + + // Get rid of the reference. + if (lgtNode) + DeleteReference(0); + + // new object + CreateNewObject(); // creates lgtObject + } + + needToss = theHold.GetGlobalPutCount()!=lastPutCount; + + theHold.Begin(); // begin hold for undo + mat.IdentityMatrix(); + + // link it up + lgtNode = createInterface->CreateObjectNode( lgtObject); + attachedToNode = TRUE; + assert( lgtNode ); + createCB = lgtObject->GetCreateMouseCallBack(); + createInterface->SelectNode( lgtNode ); + + // Create target object and node + targObject = TRACKED_NEW TargetObject; + assert(targObject); + targNode = createInterface->CreateObjectNode( targObject); + assert(targNode); + targName = lgtNode->GetName(); + targName += GetString(IDS_DB_DOT_TARGET); + targNode->SetName(targName); + + // hook up camera to target using lookat controller. + createInterface->BindToTarget(lgtNode,targNode); + + // Reference the new node so we'll get notifications. + MakeRefByID( FOREVER, 0, lgtNode); + + // Position camera and target at first point then drag. + mat.IdentityMatrix(); + //mat[3] = vpx->GetPointOnCP(m); + #ifdef _3D_CREATE + mat.SetTrans( vpx->SnapPoint(m,m,NULL,SNAP_IN_3D) ); + #else + mat.SetTrans(vpx->SnapPoint(m,m,NULL,SNAP_IN_PLANE)); + #endif + createInterface->SetNodeTMRelConstPlane(lgtNode, mat); + createInterface->SetNodeTMRelConstPlane(targNode, mat); + lgtObject->Enable(1); + + ignoreSelectionChange = TRUE; + createInterface->SelectNode( targNode,0); + ignoreSelectionChange = FALSE; + res = TRUE; + break; + + case 1: + if (Length(m-pt0)<2) + goto abort; + //mat[3] = vpx->GetPointOnCP(m); + #ifdef _3D_CREATE + mat.SetTrans( vpx->SnapPoint(m,m,NULL,SNAP_IN_3D) ); + #else + mat.SetTrans(vpx->SnapPoint(m,m,NULL,SNAP_IN_PLANE)); + #endif + macroRec->Disable(); // JBW 4/23/99 + createInterface->SetNodeTMRelConstPlane(targNode, mat); + macroRec->Enable(); + + ignoreSelectionChange = TRUE; + createInterface->SelectNode( lgtNode); + ignoreSelectionChange = FALSE; + + theHold.Accept(IDS_DS_CREATE); + + createInterface->AddLightToScene(lgtNode); + createInterface->RedrawViews(createInterface->GetTime()); + + res = FALSE; // We're done + break; + } + break; + + case MOUSE_MOVE: + //mat[3] = vpx->GetPointOnCP(m); + #ifdef _3D_CREATE + mat.SetTrans( vpx->SnapPoint(m,m,NULL,SNAP_IN_3D) ); + #else + mat.SetTrans(vpx->SnapPoint(m,m,NULL,SNAP_IN_PLANE)); + #endif + macroRec->Disable(); // JBW 4/23/99 + createInterface->SetNodeTMRelConstPlane(targNode, mat); + macroRec->Enable(); + createInterface->RedrawViews(createInterface->GetTime()); + + macroRec->SetProperty(lgtObject, _T("target"), // JBW 4/23/99 + mr_create, Class_ID(TARGET_CLASS_ID, 0), GEOMOBJECT_CLASS_ID, 1, _T("transform"), mr_matrix3, &mat); + + res = TRUE; + break; + + case MOUSE_FREEMOVE: + SetCursor(LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CROSS_HAIR))); + #ifdef _OSNAP + //Snap Preview + #ifdef _3D_CREATE + vpx->SnapPreview(m,m,NULL, SNAP_IN_3D); + #else + vpx->SnapPreview(m,m,NULL, SNAP_IN_PLANE); + #endif + #endif + vpx->TrackImplicitGrid(m); //KENNY MERGE + break; + + case MOUSE_PROPCLICK: + // right click while between creations + createInterface->RemoveMode(NULL); + break; + + case MOUSE_ABORT: + abort: + assert( lgtObject ); + lgtObject->EndEditParams( (IObjParam*)createInterface,0,NULL); + // Toss the undo stack if param changes have been made + macroRec->Cancel(); // JBW 4/23/99 + theHold.Cancel(); // deletes both the camera and target. + if (needToss) + GetSystemSetting(SYSSET_CLEAR_UNDO); + lgtNode = NULL; + targNode = NULL; + createInterface->RedrawViews(createInterface->GetTime()); + CreateNewObject(); + attachedToNode = FALSE; + res = FALSE; + } + +done: + //KENNY MERGE + if ((res == CREATE_STOP)||(res==CREATE_ABORT)) + vpx->ReleaseImplicitGrid(); + createInterface->ReleaseViewport(vpx); + return res; +} + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightBase.h new file mode 100644 index 00000000..60cb5d49 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightBase.h @@ -0,0 +1,498 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_RTLIGHT_BASE_H +#define PL_RTLIGHT_BASE_H + +// Max related headers +#include "max.h" +#include "iparamb2.h" +#include "iparamm2.h" + +// Our generic headers +#include "hsTypes.h" +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" + + +extern TCHAR *GetString(int id); + + + +extern HINSTANCE hInstance; + +#define RTOMNI_LIGHT_CLASSID Class_ID(0x57cf7089, 0x282e5b71) +#define RTSPOT_LIGHT_CLASSID Class_ID(0x2b263fdd, 0x19c4351f) +#define RTTSPOT_LIGHT_CLASSID Class_ID(0x48cb06ab, 0x3c142832) +#define RTDIR_LIGHT_CLASSID Class_ID(0x5a6d278c, 0x780a78b1) +// Projected Directional Light +#define RTPDIR_LIGHT_CLASSID Class_ID(0x2f611934, 0x3681ff0) + + + +#define FOREVER Interval(TIME_NegInfinity, TIME_PosInfinity) + +#define DEF_TDIST 240.0f // 160.0f + +#define NUM_HALF_ARC 5 +#define NUM_ARC_PTS (2*NUM_HALF_ARC+1) +#define NUM_CIRC_PTS 28 +#define SEG_INDEX 7 + +#define ATTEN_START 2 // far +#define ATTEN_END 3 // far + + +#define WM_SET_TYPE WM_USER + 0x04002 + + +inline float MaxF(float a, float b) { return a>b?a:b; } +inline float MinF(float a, float b) { return aClassName(); } + void GetClassName(TSTR& s) { s = fClassDesc->ClassName(); } + + virtual IParamBlock2 *GetParamBlock( int i ); + virtual IParamBlock2* GetParamBlock2(); + virtual IParamBlock2* GetParamBlockByID(short id); + plLayerTex* GetTex() { return fTex; } + // So our animatables will show up in the trackview + virtual int NumParamBlocks() { return 1; } + virtual int NumSubs(); + virtual Animatable* SubAnim(int i); + virtual TSTR SubAnimName(int i); + + // plug-in mouse creation callback + CreateMouseCallBack* GetCreateMouseCallBack(); + RefTargetHandle Clone(RemapDir &remap = NoRemap()){ plRTLightBase* thisObj = TRACKED_NEW plRTLightBase(); BaseClone(this, thisObj, remap); return thisObj;} + + virtual void BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev); + virtual void EndEditParams(IObjParam *ip, ULONG flags, Animatable *next); + + // main function that will build our mesh + void FreeCaches(); + + // retreives bounding box in object space/world space + void GetLocalBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box); + void GetWorldBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box); + + // main display function for this object + int Display(TimeValue t, INode *node, ViewExp *vpt, int flags); + virtual int DrawConeAndLine(TimeValue t, INode* inode, GraphicsWindow *gw, int drawing ); + void GetConePoints(TimeValue t, float aspect, float angle, float dist, Point3 *q); + virtual void DrawCone(TimeValue t, GraphicsWindow *gw, float dist); + int GetSpotShape(void){ return 0; } + void SetExtendedDisplay(int flags){ extDispFlags = flags; } + void BoxCircle(TimeValue t, float r, float d, Box3& box, int extraPt, Matrix3 *tm); + void BoxDirPoints(TimeValue t, float angle, float dist, Box3 &box, Matrix3 *tm); + void BoxPoints(TimeValue t, float angle, float dist, Box3 &box, Matrix3 *tm); + + void DrawArrow( TimeValue t, GraphicsWindow *gw, Point3 &direction, float dist ); + + void GetAttenPoints(TimeValue t, float rad, Point3 *q); + int GetRectXPoints(TimeValue t, float angle, float dist, Point3 *q); + int GetCirXPoints(TimeValue t, float angle, float dist, Point3 *q); + void DrawSphereArcs(TimeValue t, GraphicsWindow *gw, float r, Point3 *q); + +// + void DrawX(TimeValue t, float asp, int npts, float dist, GraphicsWindow *gw, int indx); + void DrawCircleX(TimeValue t, GraphicsWindow *gw, float angle, float dist, Point3 *q); + void DrawWarpRect(TimeValue t, GraphicsWindow *gw, float angle, float dist, Point3 *q); + void DrawAttenCirOrRect(TimeValue t, GraphicsWindow *gw, float dist, BOOL froze, int uicol); + int DrawAtten(TimeValue t, INode *inode, GraphicsWindow *gw); + + + + + //void SetType(int tp); + + + + // hit testing of this object + int HitTest(TimeValue t, INode *node, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt); + + void Snap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt); + void GetDeformBBox(TimeValue t, Box3& box, Matrix3 *tm, BOOL useSel ); + + + //Internal routines + void BoxLight(TimeValue t, INode *inode, Box3& box, Matrix3 *tm); + void GetMat(TimeValue t, INode* inode, ViewExp *vpt, Matrix3& tm); + + virtual RefTargetHandle GetReference(int i); + virtual void SetReference(int ref, RefTargetHandle rtarg); + virtual int NumRefs() { return kNumRefs;} + + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message); + + // Called to retreive the state of this object at the specified time. + ObjectState Eval(TimeValue t) { return ObjectState(this); } + + const char *GetCategory(){ return fClassDesc->Category(); } + + // + // LightObject Specific Stuff below + // + // + + virtual BOOL IsSpot( void ) { return FALSE; } + virtual BOOL IsDir( void ) { return FALSE; } + + RefResult EvalLightState(TimeValue time, Interval& valid, LightState *ls); + ObjLightDesc *CreateLightDesc(INode *n, BOOL forceShadowBuffer=FALSE); + void SetUseLight(int onOff) { fLightPB->SetValue(kLightOn, 0, onOff); NotifyDependents(FOREVER, PART_OBJ, REFMSG_CHANGE); } + BOOL GetUseLight(void) { BOOL v; fLightPB->GetValue(kLightOn, 0, v, FOREVER); return v; } + void SetHotspot(TimeValue time, float f); + float GetHotspot(TimeValue t, Interval& valid = Interval(0,0)); + void SetFallsize(TimeValue time, float f); + float GetFallsize(TimeValue t, Interval& valid = Interval(0,0)); + void SetAtten(TimeValue time, int which, float f); + float GetAtten(TimeValue t, int which, Interval& valid = Interval(0,0)); + + // TDist funcs needs implementation as of 31/5/01 + void SetTDist(TimeValue time, float f); + float GetTDist(TimeValue t, Interval& valid = Interval(0,0)); + + void SetConeDisplay(int s, int notify=TRUE); + BOOL GetConeDisplay(void); + + void SetRGBColor(TimeValue t, Point3& rgb); //fLightPB->SetValue(kRGB, t, rgb); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);} + Point3 GetRGBColor(TimeValue t, Interval &valid = Interval(0,0)); //return fLightPB->GetPoint3(kRGB, t); } + void SetIntensity(TimeValue t, float f) { fLightPB->SetValue(kIntensity, t, f); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);} + float GetIntensity(TimeValue t, Interval& valid = Interval(0,0)) {return fLightPB->GetFloat(kIntensity, t); } + void SetAspect(TimeValue t, float f) {} + float GetAspect(TimeValue t, Interval& valid = Interval(0,0)) { return 0.0; } + void SetUseAtten(int s){ fLightPB->SetValue(kUseAttenuationBool, 0, s); NotifyDependents(FOREVER, PART_OBJ, REFMSG_CHANGE); } + BOOL GetUseAtten(void) {return fLightPB->GetInt(kUseAttenuationBool, 0);} + void SetUseAttenNear(int s) { } + BOOL GetUseAttenNear(void) {return false;} + void SetAttenDisplay(int s){ } + BOOL GetAttenDisplay(void) {return fLightPB->GetInt(kUseAttenuationBool, 0);} + void SetAttenDisplayNear(int s){ } + BOOL GetAttenDisplayNear(void){return false;} + void Enable(int enab) { fLightPB->SetValue(kLightOn, 0, enab); } + void SetMapBias(TimeValue t, float f) {} + float GetMapBias(TimeValue t, Interval& valid = Interval(0,0)){return 0.0f;} + void SetMapRange(TimeValue t, float f) {} + float GetMapRange(TimeValue t, Interval& valid = Interval(0,0)) {return 0.0f;} + void SetMapSize(TimeValue t, int f) {} + int GetMapSize(TimeValue t, Interval& valid = Interval(0,0)){return 0;} + void SetRayBias(TimeValue t, float f) {} + float GetRayBias(TimeValue t, Interval& valid = Interval(0,0)) {return 0.0;} //{return 0.0f;} + int GetAbsMapBias() {return 0;} + void SetAbsMapBias(int a) {} + int GetOvershoot(){return 0;} + void SetOvershoot(int a){} + virtual int GetProjector() { return 0; } + void SetProjector(int a){fLightPB->SetValue(kUseProjectorBool, 0, a); } + ExclList* GetExclList(){return NULL;} + BOOL Include() {return false;} + virtual Texmap* GetProjMap(); //{ Interval valid = Interval(0,0); Texmap* MyMap; fLightPB->GetValue(kProjMapTexButton, 0, MyMap, valid); return MyMap; } + void SetProjMap(BitmapInfo* pmap); + void UpdateTargDistance(TimeValue t, INode* inode); + + // GenLight Specific Stuff below + + BOOL GetUseGlobal() { return false; } //Global Shadow param + void SetUseGlobal(int a) {} + BOOL GetShadow(); + void SetShadow(int a); + BOOL GetShadowType() { return -1; } //No Shadows generated .... + void SetShadowType(int a) {} //Until implemented.... + GenLight* NewLight(int type) { return NULL;} + int Type() {return -1;} + void SetSpotShape(int a) {} + void SetHSVColor(TimeValue t, class Point3 &b); + Point3 GetHSVColor(TimeValue t, Interval& b); + void SetContrast(int a, float other) {} // fLightPB->SetValue(kContrast, a, other); NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);} + float GetContrast(int a, Interval &valid) {return 0.0;} //float f; f = fLightPB->GetFloat(kContrast, a); return f;} + void SetAttenNearDisplay(int a) {} + BOOL GetAttenNearDisplay() {return false;} + ExclList& GetExclusionList() {return exclList;} + void SetExclusionList(ExclList & a) {} + BOOL SetHotSpotControl(Control* a) {fLightPB->SetController(ParamID(kHotSpot),0, a); return true;} + BOOL SetFalloffControl(Control* a) {fLightPB->SetController(ParamID(kFallOff),0, a); return true;} + Control* GetHotSpotControl() {return fLightPB->GetController(ParamID(kHotSpot));} + Control* GetFalloffControl() {return fLightPB->GetController(ParamID(kFallOff));} + BOOL SetColorControl(Control * a) {fLightPB->SetController(ParamID(kLightColor),0, a); return true;} + Control* GetColorControl() {return fLightPB->GetController(ParamID(kLightColor)); } + + BOOL GetDecayType() { return fLightPB->GetInt(kAttenTypeRadio, 0) + 1;} //Offset for the radio. + void SetDecayType(BOOL onOff) {if (!onOff) return; else {fLightPB->SetValue(kAttenTypeRadio, 0, ((int) onOff - 1)); return;} } + void SetDecayRadius(TimeValue time, float f) { fLightPB->SetValue(kAttenMaxFalloffEdit, time, f); } + float GetDecayRadius(TimeValue t, Interval& valid = Interval(0,0)) {return fLightPB->GetFloat(kAttenMaxFalloffEdit, t); } + + void SetDiffuseSoft(TimeValue time, float f) {}//fLightPB->SetValue(kDiffSoft, time, f);} + float GetDiffuseSoft(TimeValue t, Interval& valid = Interval(0,0)) {return 0.0;} //return fLightPB->GetFloat(kDiffSoft, t);} + void SetAffectDiffuse(BOOL onOff) {}//fLightPB->SetValue(kDiffOn, 0, onOff); } + BOOL GetAffectDiffuse() {return false; } //fLightPB->GetInt(kDiffOn, 0); } + + LRESULT CALLBACK TrackViewWinProc( HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam ){return(0);} +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLights.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLights.cpp new file mode 100644 index 00000000..71835aee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLights.cpp @@ -0,0 +1,890 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plRealTimeLights.cpp - Implementation for some MAX RT lights // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.2.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "HeadSpin.h" +#include "plRealTimeLights.h" +#include "iparamm2.h" +#include "../MaxPlasmaMtls/Layers/plLayerTex.h" +#include "../MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h" +#include "../MaxComponent/plMaxAnimUtils.h" +#include "plRTLightBaseAnimDlgProc.h" +#include "../plGLight/plLightKonstants.h" + +void DummyLightCodeIncludeFunc() {} + + +//// Static ClassDesc2 Get Functions ////////////////////////////////////////// + +plRTOmniLightDesc plRTOmniLightDesc::fStaticDesc; +plRTSpotLightDesc plRTSpotLightDesc::fStaticDesc; +plRTDirLightDesc plRTDirLightDesc::fStaticDesc; + + +//// ParamBlock2 DlgProc Functions //////////////////////////////////////////// + +const char* DecayLevel[] = { + "None", + "Inverse", + "Inverse Square", + NULL + }; + +const char* ShadowState[] = { + "Shadow Map", + NULL + }; + + +class LightDlgProc : public plBaseLightProc +{ +protected: + void IValidateSpinners( TimeValue t, WPARAM wParam, IParamBlock2 *pb, IParamMap2 *map ) + { + /// Make sure falloff is >= hotspot (adjust the one we're not editing) + float hotspot, falloff; + hotspot = pb->GetFloat( plRTLightBase::kHotSpot, t ); + falloff = pb->GetFloat( plRTLightBase::kFallOff, t ); + + if( falloff < hotspot ) + { + if( LOWORD( wParam ) == IDC_LHOTSIZESPINNER ) + pb->SetValue( plRTLightBase::kFallOff, t, hotspot ); + else + pb->SetValue( plRTLightBase::kHotSpot, t, falloff ); + + map->Invalidate( plRTLightBase::kHotSpot ); + map->Invalidate( plRTLightBase::kFallOff ); + } + IBuildLightMesh( (plRTLightBase *)pb->GetOwner(), falloff ); + } + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IParamBlock2 *pb = map->GetParamBlock(); + plRTLightBase *gl = (plRTLightBase *) pb->GetOwner(); + + switch (msg) + { + case WM_COMMAND: + if(LOWORD(wParam) == IDC_PROJ_MAPNAME ) + { + if( gl->ClassID() != RTOMNI_LIGHT_CLASSID ) +// if(pb->GetInt(plRTLightBase::kLightType) != plRTLightBase::RT_OMNI) + + // map->SetValue(plRTSpotLight::kProjMapTexButton, t, + //gl->SetProjMap( + map->Invalidate(plRTSpotLight::kProjMapTexButton); + return false; + } + else if( LOWORD( wParam ) == IDC_LHOTSIZE || LOWORD( wParam ) == IDC_LFALLOFF ) + { + if( HIWORD( wParam ) == EN_CHANGE ) + IValidateSpinners( t, wParam, pb, map ); + } + break; + + case CC_SPINNER_CHANGE: + if( LOWORD( wParam ) == IDC_LHOTSIZESPINNER || LOWORD( wParam ) == IDC_LFALLOFFSPINNER ) + IValidateSpinners( t, wParam, pb, map ); + break; + } + + return plBaseLightProc::DlgProc( t, map, hWnd, msg, wParam, lParam );; + } + void DeleteThis() {}; +}; +static LightDlgProc gLiteDlgProc; + + +#include "plRealTimeLightsPBDec.h" + + +#include "plRTObjLightDesc.h" + + +/////////////////////////////////////////////////////////////////////// +// +// Light Descriptors declared below for the different plRTLights... +// +// +// +// + +//--- Base Light Class derived from the ObjLightDesc + +#define COS_45 0.7071067f +#define COS_45_2X 1.4142136f + +static float stepFactor[] = {50.0f,80.0f,140.0f}; +#define MAXSTEPS 1000 + + +BaseObjLight::BaseObjLight(INode *n) : ObjLightDesc(n) +{ + ObjectState os = n->EvalWorldState(TimeValue(0)); + assert(os.obj->SuperClassID()==LIGHT_CLASS_ID); + gl = (os.obj->GetInterface(I_MAXSCRIPTPLUGIN) != NULL) ? (plRTLightBase*)os.obj->GetReference(0) : (plRTLightBase*)os.obj; // JBW 4/7/99 +} + +static Color blackCol(0,0,0); + +int BaseObjLight::Update(TimeValue t, const RendContext& rc, RenderGlobalContext * rgc, BOOL shadows, BOOL shadowGeomChanged) { + ObjLightDesc::Update(t,rc,rgc,shadows,shadowGeomChanged); + intensCol = ls.intens*ls.color*rc.GlobalLightLevel(); + ObjectState os = inode->EvalWorldState(t); + plRTLightBase* lob = (plRTLightBase *)os.obj; + contrast = 0; //lob->GetParamBlock2()->GetFloat(plRTLightBase::kContrast, t); + diffSoft = 0; //lob->GetDiffuseSoft(t)/100.0f; + + float a = .5; //contrast/204.0f + 0.5f; // so "a" varies from .5 to .99 + kA = (2.0f-1.0f/a); + kB = 1.0f-kA; + diffSoften = false; //lob->GetParamBlock2()->GetInt(plRTLightBase::kDiffOn, t); //GetSoftenDiffuse(); + + return 1; +} + + + + + + +//--- Omni Light ------------------------------------------------ + + +OmniLight::OmniLight(INode *inode, BOOL forceShadowBuf ) : BaseObjLight(inode){ + + //projector = /*doShadows = shadowRay =*/ FALSE; + //projMap = NULL; + needMultiple = FALSE; + +} + +OmniLight::~OmniLight() +{ + +} + + + + +int OmniLight::UpdateViewDepParams(const Matrix3& worldToCam) { + BaseObjLight::UpdateViewDepParams(worldToCam); + return 1; + } + +static Point3 MapToDir(Point3 p, int k) { + switch(k) { + case 0: return Point3( p.z, p.y, -p.x); // +X + case 1: return Point3( -p.z, p.y, p.x); // -X + case 2: return Point3( p.x, p.z, -p.y); // +Y + case 3: return Point3( p.x,-p.z, p.y); // -Y + case 4: return Point3( -p.x, p.y, -p.z); // +Z + case 5: return p; // -Z + } + return p; + } + +static void GetMatrixForDir(Matrix3 &origm, Matrix3 &tm, int k ) { + tm = origm; + switch(k) { + case 0: tm.PreRotateY(-HALFPI); break; // Map 0: +X axis + case 1: tm.PreRotateY( HALFPI); break; // Map 1: -X axis + case 2: tm.PreRotateX( HALFPI); break; // Map 2: +Y axis + case 3: tm.PreRotateX(-HALFPI); break; // Map 3: -Y axis + case 4: tm.PreRotateY( PI ); break; // Map 4: +Z axis + case 5: break; // Map 5: -Z axis + } + } + +static int WhichDir(Point3 &p) { + int j = MaxComponent(p); // the component with the maximum abs value + return (p[j]<0.0f) ? 2*j+1 : 2*j; + } + +int OmniLight::Update(TimeValue t, const RendContext & rc, + RenderGlobalContext *rgc, BOOL shadows, BOOL shadowGeomChanged) +{ + BaseObjLight::Update(t,rc,rgc,shadows,shadowGeomChanged); + + ObjectState os = inode->EvalWorldState(t); + LightObject* lob = (LightObject *)os.obj; + assert(os.obj->SuperClassID()==LIGHT_CLASS_ID); + plRTOmniLight* gl = (lob->GetInterface(I_MAXSCRIPTPLUGIN) != NULL) ? (plRTOmniLight*)lob->GetReference(0) : (plRTOmniLight*)lob; // JBW 4/7/99 + + decayType = gl->GetDecayType(); + decayRadius = gl->GetDecayRadius(t); + + fov = HALFPI; // 90 degree fov + int res=1; + if(gl->GetTex()) + gl->GetTex()->Update(t, FOREVER); + //projector = gl->GetProjector(); + //if (projector){ + // projMap = gl->GetProjMap(); + // if( projMap ) projMap->Update(t,FOREVER); + //} + + return res; +} + + + +////------------------------------------------------------------------ +// +// +// SpotLight descriptors..... +// +// +// +// +// + +SpotLight::SpotLight(INode *inode, BOOL forceShadowBuf ):BaseObjLight(inode) +{ + projMap = NULL; +} + +int SpotLight::Update(TimeValue t, const RendContext &rc, RenderGlobalContext *rgc, BOOL shadows, BOOL shadowGeomChanged) +{ + int res = 1; + BaseObjLight::Update(t,rc,rgc,shadows, shadowGeomChanged); + + float hs = DegToRad(ls.hotsize); + float fs = DegToRad(ls.fallsize); + fall_tan = (float)tan(fs/2.0f); + hot_cos = (float)cos(hs/2.0f); + fall_cos =(float)cos(fs/2.0f); + fall_sin = (float)sin(fs/2.0f); + hotpct = ls.hotsize/ls.fallsize; + ihotpct = 1.0f - hotpct; + + ObjectState os = inode->EvalWorldState(t); + LightObject* lob = (LightObject *)os.obj; + assert(os.obj->SuperClassID()==LIGHT_CLASS_ID); + plRTLightBase* gl = (lob->GetInterface(I_MAXSCRIPTPLUGIN) != NULL) ? (plRTLightBase*)lob->GetReference(0) : (plRTLightBase*)lob; // JBW 4/7/99 + + decayType = gl->GetDecayType(); + decayRadius = gl->GetDecayRadius(t); + + projector = gl->GetProjector(); + fov = hsMaximum(fs,hs); + + float aspect = 1.0f; + + fov = 2.0f* (float)atan(tan(fov*0.5f)*sqrt(aspect)); + zfac = -sz2 /(float)tan(0.5*(double)fov); + xscale = zfac; + yscale = -zfac*aspect; + curve =(float)fabs(1.0f/xscale); + + rectv0.y = fall_sin * (float)sqrt(aspect); + rectv1.y = fall_sin / (float)sqrt(aspect); + + rectv0.x = rectv1.x = fall_cos; + rectv0 = Normalize(rectv0); + rectv1 = Normalize(rectv1); + + Interval v; + if (projector){ + projMap = gl->GetProjMap(); + if( projMap ) projMap->Update(t,v); + } + + return res; +} + +int SpotLight::UpdateViewDepParams(const Matrix3& worldToCam) +{ + BaseObjLight::UpdateViewDepParams(worldToCam); + lightDir = -FNormalize(lightToCam.GetRow(2)); + return 1; +} + +BOOL SpotLight::IsFacingLight(Point3 &dir) +{ + return dir.z>0.0f; +} + + + +//--- Directional Light ------------------------------------------------ + +DirLight::DirLight(INode *inode, BOOL forceShadowBuf ) : BaseObjLight(inode) +{ + projMap = NULL; +} + +int DirLight::Update(TimeValue t, const RendContext &rc, + RenderGlobalContext *rgc, BOOL shadows, BOOL shadowGeomChanged) +{ + int res = 1; + BaseObjLight::Update(t,rc,rgc,shadows,shadowGeomChanged); + hotsz = ls.hotsize; + fallsz = ls.fallsize; + fallsq = fallsz*fallsz; + hotpct = ls.hotsize/ls.fallsize; + ihotpct = 1.0f - hotpct; + + ObjectState os = inode->EvalWorldState(t); + LightObject* lob = (LightObject *)os.obj; + assert(os.obj->SuperClassID()==LIGHT_CLASS_ID); + plRTDirLight* gl = (lob->GetInterface(I_MAXSCRIPTPLUGIN) != NULL) ? (plRTDirLight*)lob->GetReference(0) : (plRTDirLight*)lob; // JBW 4/7/99 + + //projector = gl->GetProjector(); + + aspect = 1.0f; + + //if (projector){ + // projMap = gl->GetProjMap(); + // if( projMap ) projMap->Update(t,FOREVER); + // } + return res; +}; + +int DirLight::UpdateViewDepParams(const Matrix3& worldToCam) { + BaseObjLight::UpdateViewDepParams(worldToCam); + lightDir = FNormalize(lightToCam.GetRow(2)); + + return 1; + } + + + + + + + +//////////////////////////////////////////////////////////////////////////////////// +// +// plRTOmni Stuff below +// +// +// +// +// + +plRTOmniLight::plRTOmniLight() +{ + fIP = NULL; + fLightPB = NULL; + fClassDesc = plRTOmniLightDesc::GetDesc(); + fClassDesc->MakeAutoParamBlocks(this); + + fLightPB->SetValue(kLightColor, 0, Color(255,255,255)); + SetHSVColor(0, Point3(255, 255, 255)); + + fTex = NULL; + + meshBuilt = 0; + + IBuildMeshes(true); +} + +ObjLightDesc *plRTOmniLight::CreateLightDesc(INode *n, BOOL forceShadowBuf) +{ + return TRACKED_NEW OmniLight( n, forceShadowBuf ); +} + + +RefTargetHandle plRTOmniLight::Clone(RemapDir &remap) +{ + + plRTOmniLight *obj = TRACKED_NEW plRTOmniLight;//(plRTLightBase*) fClassDesc->Create(false); + obj->ReplaceReference(kRefSpotLight, fLightPB->Clone(remap)); + BaseClone(this, obj, remap); + + return obj; +} + +void plRTOmniLight::IBuildMeshes( BOOL isnew ) +{ + BuildStaticMeshes(); + fMesh = staticMesh[ plRTLightBase::RT_OMNI ]; +} + +//// DrawConeAndLine ///////////////////////////////////////////////////////// + +int plRTOmniLight::DrawConeAndLine( TimeValue t, INode* inode, GraphicsWindow *gw, int drawing ) +{ + float atOneHalfDist; + Matrix3 tm = inode->GetObjectTM( t ); + + + gw->setTransform( tm ); + gw->clearHitCode(); + + if( ( extDispFlags & EXT_DISP_ONLY_SELECTED ) ) + { + if( GetUseAtten() ) + { + // Draw hotspot as the point at which light is 1/2 intensity (just to help the visual) + gw->setColor( LINE_COLOR, GetUIColor( COLOR_HOTSPOT ) ); + + if( fLightPB->GetInt( kAttenTypeRadio, t ) == 0 ) + atOneHalfDist = GetAtten( t, ATTEN_END ) / ( fLightPB->GetFloat( kIntensity, t ) * plSillyLightKonstants::GetFarPowerKonst() - 1.f ); + else + atOneHalfDist = sqrt( GetAtten( t, ATTEN_END ) * GetAtten( t, ATTEN_END ) / ( fLightPB->GetFloat( kIntensity, t ) * plSillyLightKonstants::GetFarPowerKonst() - 1.f ) ); + + if( atOneHalfDist > 0.0f ) + DrawCone( t, gw, atOneHalfDist ); + + gw->setColor( LINE_COLOR, GetUIColor( COLOR_FALLOFF ) ); + DrawCone( t, gw, GetAtten( t, ATTEN_END ) ); + } + else + DrawArrows( t, gw, 50 ); + } + + return gw->checkHitCode(); +} + +//// DrawCone //////////////////////////////////////////////////////////////// +// Function called by MAX to render the cone shape in the viewport for this +// light. Note that this is the cone, not the actual object itself! + +void plRTOmniLight::DrawCone( TimeValue t, GraphicsWindow *gw, float dist ) +{ + Point3 pts[ NUM_CIRC_PTS * 3 + 1 ]; + + + /// Draw sphere-thingy + GetAttenPoints( t, dist, pts ); + + gw->polyline( NUM_CIRC_PTS, pts, nil, nil, true, nil ); + gw->polyline( NUM_CIRC_PTS, pts + NUM_CIRC_PTS, nil, nil, true, nil ); + gw->polyline( NUM_CIRC_PTS, pts + 2 * NUM_CIRC_PTS, nil, nil, true, nil ); +} + + +//// DrawArrows ////////////////////////////////////////////////////////////// +// Renders some arrows in all directions, to show a radiating, attenuation-less +// omni light. + +void plRTOmniLight::DrawArrows( TimeValue t, GraphicsWindow *gw, float dist ) +{ + Point3 directions[] = { Point3( 1, 0, 0 ), Point3( -1, 0, 0 ), Point3( 0, 1, 0 ), Point3( 0, -1, 0 ), + Point3( 0, 0, 1 ), Point3( 0, 0, -1 ), + Point3( 2, 2, 0 ), Point3( 2, -2, 0 ), Point3( 2, 0, 2 ), Point3( 2, 0, -2 ), + Point3( -2, 2, 0 ), Point3( -2, -2, 0 ), Point3( -2, 0, 2 ), Point3( -2, 0, -2 ), + Point3( 0, 2, 2 ), Point3( 0, 2, -2 ), Point3( 0, -2, 2 ), Point3( 0, -2, -2 ), + Point3( 0, 0, 0 ) }; + Point3 empty( 0, 0, 0 ); + int i; + Point3 pts[ 5 ]; + + + /// Adjust directions + for( i = 0; directions[ i ] != empty; i++ ) + { + if( directions[ i ].x == 2.f ) + directions[ i ].x = 0.7f; + else if( directions[ i ].x == -2.f ) + directions[ i ].x = -0.7f; + + if( directions[ i ].y == 2.f ) + directions[ i ].y = 0.7f; + else if( directions[ i ].y == -2.f ) + directions[ i ].y = -0.7f; + + if( directions[ i ].z == 2.f ) + directions[ i ].z = 0.7f; + else if( directions[ i ].z == -2.f ) + directions[ i ].z = -0.7f; + } + + /// Draw da arrows + gw->setColor( LINE_COLOR, GetUIColor( COLOR_HOTSPOT ) ); + for( i = 0; directions[ i ] != empty; i++ ) + DrawArrow( t, gw, directions[ i ], dist ); +} + +//// GetLocalBoundBox //////////////////////////////////////////////////////// + +void plRTOmniLight::GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box ) +{ + Point3 loc = node->GetObjectTM( t ).GetTrans(); + float scaleFactor = vpt->NonScalingObjectSize() * vpt->GetVPWorldWidth( loc ) / 360.0f; + float width; + + box = fMesh.getBoundingBox(); + // Because we want to scale about the origin, not the box center, we have to do this funky offset + Point3 boxCenter = box.Center(); + box.Translate( -boxCenter ); + box.Scale( scaleFactor ); + boxCenter *= scaleFactor; + box.Translate( boxCenter ); + + // Include points for the spotlight. That means either the attenuated cone or + // our unattenuated cone display + if( ( extDispFlags & EXT_DISP_ONLY_SELECTED ) ) + { + if( GetUseAtten() ) + width = GetAtten( t, ATTEN_END ); + else + width = 50.f; // Include our arrows + + box += Point3( -width, -width, -width ); + box += Point3( width, width, width ); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// +// +// SpotLight Stuff +// +// + + +//// Local Copy of shared Anim PB ///////////////////////////////////////////// + +plRTSpotLight::plRTSpotLight() +{ + fIP = NULL; + fLightPB = NULL; + fClassDesc = plRTSpotLightDesc::GetDesc(); + fClassDesc->MakeAutoParamBlocks(this); + + fLightPB->SetValue(kLightColor, 0, Color(255,255,255)); + SetHSVColor(0, Point3(255, 255, 255)); + + fTex = NULL; + meshBuilt = 0; + + IBuildMeshes(true); +} + +ObjLightDesc *plRTSpotLight::CreateLightDesc(INode *n, BOOL forceShadowBuf) +{ + return TRACKED_NEW SpotLight( n, forceShadowBuf ); +} + + +RefTargetHandle plRTSpotLight::Clone(RemapDir &remap) +{ + + plRTSpotLight *obj = TRACKED_NEW plRTSpotLight;//(plRTLightBase*) fClassDesc->Create(false); + obj->ReplaceReference(kRefSpotLight, fLightPB->Clone(remap)); + BaseClone(this, obj, remap); + return obj; +} + +Texmap *plRTSpotLight::GetProjMap() +{ + if( !fLightPB->GetInt( kUseProjectorBool ) ) + return nil; + + Interval valid = Interval(0,0); + if( !GetTex() ) + { + if( fLightPB->GetInt( kUseProjectorBool ) ) + { + PBBitmap* bitmap = fLightPB->GetBitmap( kProjMapTexButton, 0 ); + SetProjMap( &bitmap->bi ); + } + } + + if( GetTex() ) + { + const char* dbgTexName = GetTex()->GetName(); + + IParamBlock2 *bitmapPB = fTex->GetParamBlockByID(plLayerTex::kBlkBitmap); + hsAssert(bitmapPB, "LayerTex with no param block"); + + int noCompress = fLightPB->GetInt(kProjNoCompress); + int noMip = fLightPB->GetInt(kProjNoMip); + bitmapPB->SetValue(kBmpNonCompressed, TimeValue(0), noCompress); + bitmapPB->SetValue(kBmpNoFilter, TimeValue(0), noMip); + } + + return (Texmap *)GetTex(); +} + +void plRTSpotLight::IBuildMeshes( BOOL isnew ) +{ + float val = fLightPB->GetFloat( kHotSpot, TimeValue(0) ); //Init val of HotSpot + if( isnew ) + { + val = fLightPB->GetFloat(kHotSpot, TimeValue(0)); + SetHotspot( TimeValue(0), val); + //val = 45.0; + val = fLightPB->GetFloat(kFallOff, TimeValue(0)); + SetFallsize( TimeValue(0), val); + val = fLightPB->GetFloat(kAttenMaxFalloffEdit, TimeValue(0)); //fLightPB->GetFloat(kTargetDist, TimeValue(0)); + if(val < 1.0f) + SetTDist( TimeValue(0), DEF_TDIST); + else + SetTDist( TimeValue(0), val); + + val = fLightPB->GetFloat(kHotSpot, TimeValue(0)); + + } + + BuildSpotMesh( val ); + + fMesh = spotMesh; +} + +//// DrawConeAndLine ///////////////////////////////////////////////////////// + +int plRTSpotLight::DrawConeAndLine( TimeValue t, INode* inode, GraphicsWindow *gw, int drawing ) +{ + Matrix3 tm = inode->GetObjectTM( t ); + + + gw->setTransform( tm ); + gw->clearHitCode(); + + if( extDispFlags & EXT_DISP_ONLY_SELECTED ) + DrawCone( t, gw, GetAtten( t, ATTEN_END ) ); + + return gw->checkHitCode(); +} + +//// DrawCone //////////////////////////////////////////////////////////////// +// Function called by MAX to render the cone shape in the viewport for this +// light. Note that this is the cone, not the actual object itself! + +void plRTSpotLight::DrawCone( TimeValue t, GraphicsWindow *gw, float dist ) +{ + int i; + Point3 pts[ NUM_CIRC_PTS + 1 ], u[ 3 ]; + + + if( !GetUseAtten() ) + { + /// Don't use atten, but still want a cone, so draw the cone w/ a dist of 100 + /// and the lines extending past it (thus indicating that it keeps going) + dist = 100; + } + + /// Draw hotspot cone + gw->setColor( LINE_COLOR, GetUIColor( COLOR_HOTSPOT ) ); + GetConePoints( t, -1.0f, GetHotspot( t ), dist, pts ); + gw->polyline( NUM_CIRC_PTS, pts, nil, nil, true, nil ); + + if( GetUseAtten() ) + { + u[ 0 ] = Point3( 0, 0, 0 ); + for( i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX ) + { + u[ 1 ] = pts[ i ]; + gw->polyline( 2, u, nil, nil, true, nil ); + } + } + else + { + for( i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX ) + { + pts[ i ] = pts[ i ].Normalize(); + DrawArrow( t, gw, pts[ i ], dist + 50.f ); + } + } + + /// Draw falloff cone if necessary + if( GetHotspot( t ) < GetFallsize( t ) ) + { + gw->setColor( LINE_COLOR, GetUIColor( COLOR_FALLOFF ) ); + GetConePoints( t, -1.0f, GetFallsize( t ), dist, pts ); + gw->polyline( NUM_CIRC_PTS, pts, nil, nil, true, nil ); + + if( GetUseAtten() ) + { + u[ 0 ] = Point3( 0, 0, 0 ); + for( i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX ) + { + u[ 1 ] = pts[ i ]; + gw->polyline( 2, u, nil, nil, true, nil ); + } + } + else + { + for( i = 0; i < NUM_CIRC_PTS; i += SEG_INDEX ) + { + pts[ i ] = pts[ i ].Normalize(); + DrawArrow( t, gw, pts[ i ], dist + 50.f ); + } + } + } +} + +//// GetLocalBoundBox //////////////////////////////////////////////////////// + +void plRTSpotLight::GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box ) +{ + Point3 loc = node->GetObjectTM( t ).GetTrans(); + float scaleFactor = vpt->NonScalingObjectSize() * vpt->GetVPWorldWidth( loc ) / 360.0f; + float width, depth; + + box = fMesh.getBoundingBox(); + // Because we want to scale about the origin, not the box center, we have to do this funky offset + Point3 boxCenter = box.Center(); + box.Translate( -boxCenter ); + box.Scale( scaleFactor ); + boxCenter *= scaleFactor; + box.Translate( boxCenter ); + + // Include points for the spotlight. That means either the attenuated cone or + // our unattenuated cone display + if( ( extDispFlags & EXT_DISP_ONLY_SELECTED ) ) + { + if( GetUseAtten() ) + depth = GetAtten( t, ATTEN_END ); + else + depth = 100.f + 50.f; // Include arrows + + width = depth * tan( DegToRad( GetFallsize( t ) / 2.f ) ); + + box += Point3( -width, -width, 0.f ); + box += Point3( width, width, -depth ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +//// Directional Light //////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +plRTDirLight::plRTDirLight() +{ + fIP = NULL; + fLightPB = NULL; + fClassDesc = plRTDirLightDesc::GetDesc(); + fClassDesc->MakeAutoParamBlocks(this); + + fLightPB->SetValue(kLightColor, 0, Color(255,255,255)); + SetHSVColor(0, Point3(255, 255, 255)); + + fTex = NULL; + meshBuilt = 0; + + IBuildMeshes(true); +} + +ObjLightDesc *plRTDirLight::CreateLightDesc(INode *n, BOOL forceShadowBuf) +{ + return TRACKED_NEW DirLight( n, forceShadowBuf ); +} + + +RefTargetHandle plRTDirLight::Clone(RemapDir &remap) +{ + + plRTDirLight *obj = TRACKED_NEW plRTDirLight;//(plRTLightBase*) fClassDesc->Create(false); + obj->ReplaceReference(kRefDirLight, fLightPB->Clone(remap)); + BaseClone(this, obj, remap); + return obj; +} + +//// IBuildMeshes //////////////////////////////////////////////////////////// + +void plRTDirLight::IBuildMeshes( BOOL isnew ) +{ + BuildStaticMeshes(); + + fMesh = staticMesh[ plRTLightBase::RT_OMNI + 1 ]; +} + +//// DrawCone //////////////////////////////////////////////////////////////// +// Function called by MAX to render the cone shape in the viewport for this +// light. Note that this is the cone, not the actual object itself! + +void plRTDirLight::DrawCone( TimeValue t, GraphicsWindow *gw, float dist ) +{ + Point3 arrow[ 7 ]; + int i, j, r; + float d; + const float spacing = 20.f; + + + // Draw some funky arrows to represent our direction + dist = 100.f; + gw->setColor( LINE_COLOR, GetUIColor( COLOR_HOTSPOT ) ); + + for( i = -2; i <= 2; i++ ) + { + for( j = -2; j <= 2; j++ ) + { + r = ( i * i ) + ( j * j ); + if( r <= 4 ) + { + d = dist * ( 5 - r ) / 5; + IBuildZArrow( i * spacing, j * spacing, -d, -10.f, arrow ); + gw->polyline( 6, arrow, nil, nil, true, nil ); + } + } + } +} + +//// IBuildZArrow //////////////////////////////////////////////////////////// + +void plRTDirLight::IBuildZArrow( float x, float y, float zDist, float arrowSize, Point3 *pts ) +{ + pts[ 0 ] = Point3( x, y, 0.f ); + pts[ 1 ] = Point3( x, y, zDist ); + pts[ 2 ] = Point3( x + arrowSize / 2.f, y, zDist - arrowSize ); + pts[ 3 ] = Point3( x, y, zDist - arrowSize ); + pts[ 4 ] = Point3( x, y + arrowSize / 2.f, zDist - arrowSize ); + pts[ 5 ] = Point3( x, y, zDist ); +} + +//// GetLocalBoundBox //////////////////////////////////////////////////////// + +void plRTDirLight::GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box ) +{ + Point3 loc = node->GetObjectTM( t ).GetTrans(); + float scaleFactor = vpt->NonScalingObjectSize() * vpt->GetVPWorldWidth( loc ) / 360.0f; + float width, height, depth; + + box = fMesh.getBoundingBox(); + // Because we want to scale about the origin, not the box center, we have to do this funky offset + Point3 boxCenter = box.Center(); + box.Translate( -boxCenter ); + box.Scale( scaleFactor ); + boxCenter *= scaleFactor; + box.Translate( boxCenter ); + + if( ( extDispFlags & EXT_DISP_ONLY_SELECTED ) ) + { + width = 2 * 20.f + ( 10.f / 2.f ); // Add in half arrow size + height = 2 * 20.f + ( 10.f / 2.f ); + depth = 100.f; + + box += Point3( -width, -height, 0.f ); + box += Point3( width, height, -depth ); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLights.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLights.h new file mode 100644 index 00000000..e9054e97 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLights.h @@ -0,0 +1,203 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plRealTimeLights - Header for the derived MAX RT light type plug-ins // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.2.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef PL_RTLIGHT_H +#define PL_RTLIGHT_H + +#include "plRealTimeLightBase.h" +#include "iparamm2.h" +#include "resource.h" + +class plMaxNode; + + + +/////////////////////////////////////////////////////////////////////////////// +//// Omni Light /////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class plRTOmniLight : public plRTLightBase +{ + public: + + Class_ID ClassID() { return RTOMNI_LIGHT_CLASSID; } + SClass_ID SuperClassID() { return LIGHT_CLASS_ID; } + + int CanConvertToType(Class_ID obtype) { return (obtype == RTOMNI_LIGHT_CLASSID ) ? 1 : 0; } + plRTOmniLight(); + + ObjLightDesc *CreateLightDesc(INode *n, BOOL forceShadowBuf= FALSE); + GenLight* NewLight(int type) {return TRACKED_NEW plRTOmniLight();} + RefTargetHandle Clone(RemapDir &remap); + + virtual void InitNodeName( TSTR &s ) { s = _T( "RTOmniLight" ); } + + virtual int DrawConeAndLine( TimeValue t, INode* inode, GraphicsWindow *gw, int drawing ); + virtual void DrawCone( TimeValue t, GraphicsWindow *gw, float dist ); + virtual void DrawArrows( TimeValue t, GraphicsWindow *gw, float dist ); + virtual void GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box ); + + protected: + virtual void IBuildMeshes( BOOL isNew ); + virtual hsBool IHasAttenuation( void ) { return true; } +}; + +class plRTOmniLightDesc : public ClassDesc2 +{ + public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return TRACKED_NEW plRTOmniLight; } + const TCHAR* ClassName() { return GetString(IDS_DB_OMNI); } + SClass_ID SuperClassID() { return LIGHT_CLASS_ID; } + Class_ID ClassID() { return RTOMNI_LIGHT_CLASSID; } + const TCHAR* Category() { return _T("Plasma RunTime");} + const TCHAR* InternalName() { return _T("plRTOmni"); } // returns fixed parsable name (scripter-visible name) + HINSTANCE HInstance() { return hInstance; } + + + static plRTOmniLightDesc fStaticDesc; + + static ClassDesc2 *GetDesc( void ) { return &fStaticDesc; } +}; + + +/////////////////////////////////////////////////////////////////////////////// +//// Spotlight //////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class plRTSpotLight : public plRTLightBase +{ + public: + + plRTSpotLight(BOOL loading); + + plRTSpotLight(); + + Class_ID ClassID() { return RTSPOT_LIGHT_CLASSID; } + SClass_ID SuperClassID() { return LIGHT_CLASS_ID; } + + int CanConvertToType(Class_ID obtype) { return (obtype == RTSPOT_LIGHT_CLASSID ) ? 1 : 0; } + ObjLightDesc *CreateLightDesc(INode *n, BOOL forceShadowBuf= FALSE); + GenLight* NewLight(int type) {return TRACKED_NEW plRTSpotLight();} + RefTargetHandle Clone(RemapDir &remap); + + virtual Texmap *GetProjMap(); + + virtual BOOL IsSpot( void ) { return TRUE; } + virtual int GetProjector() { return fLightPB->GetInt( kUseProjectorBool, 0 ); } + + virtual void InitNodeName( TSTR &s ) { s = _T( "RTSpotLight" ); } + + virtual int DrawConeAndLine( TimeValue t, INode* inode, GraphicsWindow *gw, int drawing ); + virtual void DrawCone( TimeValue t, GraphicsWindow *gw, float dist ); + virtual void GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box ); + + protected: + virtual void IBuildMeshes( BOOL isNew ); + virtual hsBool IHasAttenuation( void ) { return true; } +}; + +class plRTSpotLightDesc : public ClassDesc2 +{ + public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading = FALSE) { return TRACKED_NEW plRTSpotLight; } + const TCHAR* ClassName() { return GetString(IDS_DB_FREE_SPOT); } + SClass_ID SuperClassID() { return LIGHT_CLASS_ID; } + Class_ID ClassID() { return RTSPOT_LIGHT_CLASSID; } + const TCHAR* Category() { return _T("Plasma RunTime"); } + const TCHAR* InternalName() { return _T("RTSpot"); } // returns fixed parsable name (scripter-visible name) + HINSTANCE HInstance() { return hInstance; } + + static plRTSpotLightDesc fStaticDesc; + + static ClassDesc2 *GetDesc( void ) { return &fStaticDesc; } + +}; + + +/////////////////////////////////////////////////////////////////////////////// +//// Directional Light //////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class plRTDirLight : public plRTLightBase +{ + public: + + plRTDirLight(); + + Class_ID ClassID() { return RTDIR_LIGHT_CLASSID; } + SClass_ID SuperClassID() { return LIGHT_CLASS_ID; } + + ObjLightDesc *CreateLightDesc(INode *n, BOOL forceShadowBuf= FALSE); + GenLight* NewLight(int type) {return TRACKED_NEW plRTDirLight();} + RefTargetHandle Clone(RemapDir &remap); + + int CanConvertToType(Class_ID obtype) { return (obtype == RTDIR_LIGHT_CLASSID ) ? 1 : 0; } + + virtual void DrawCone(TimeValue t, GraphicsWindow *gw, float dist); + virtual void GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box ); + + virtual BOOL IsDir( void ) { return TRUE; } + + virtual void InitNodeName( TSTR &s ) { s = _T( "RTDirLight" ); } + + protected: + virtual void IBuildMeshes( BOOL isNew ); + + void IBuildZArrow( float x, float y, float zDist, float arrowSize, Point3 *pts ); +}; + +class plRTDirLightDesc : public ClassDesc2 +{ + public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return TRACKED_NEW plRTDirLight; } + const TCHAR* ClassName() { return GetString(IDS_DB_DIRECTIONAL); } + SClass_ID SuperClassID() { return LIGHT_CLASS_ID; } + Class_ID ClassID() { return RTDIR_LIGHT_CLASSID; } + const TCHAR* Category() { return _T("Plasma RunTime");} + const TCHAR* InternalName() { return _T("RTDir"); } // returns fixed parsable name (scripter-visible name) + HINSTANCE HInstance() { return hInstance; } + + static plRTDirLightDesc fStaticDesc; + + static ClassDesc2 *GetDesc( void ) { return &fStaticDesc; } + +}; + + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightsPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightsPBDec.h new file mode 100644 index 00000000..20666e35 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/plRealTimeLightsPBDec.h @@ -0,0 +1,323 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// ParamBlock Type Konstants for plRT*Lights // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.2.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plRTLightsPB_h +#define _plRTLightsPB_h + +/////////////////////////////////////////////////////////////////////////////// +//// Spotlights /////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ParamBlockDesc2 RTSpotLightBlk +( + /// Main def + plRTSpotLight::kBlkSpotLight, _T("RT Spot Light"), 0, plRTSpotLightDesc::GetDesc(), P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plRTSpotLight::kRefSpotLight, + + /// Rollout definitions + 3, + plRTSpotLight::kLightMap1, IDD_LIGHT_PARAM, IDS_LIGHT_GEN_PARAMS, 0, 0, &gLiteDlgProc, + plRTSpotLight::kLightMap2, IDD_LIGHT_ATTEN, IDS_LIGHT_ATTEN_PARAMSS, 0, 0, &gLiteDlgProc, + plRTSpotLight::kLightMap3, IDD_FREE_SPOTLIGHT, IDS_LIGHT_SPOT_PARAMS, 0, 0, &gLiteDlgProc, + + /// Rollout 1 - General Parameters + plRTSpotLight::kLightOn, _T("on"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, true, + p_ui, plRTSpotLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_LIGHT_ON, + end, + + plRTSpotLight::kAffectDiffuse, _T("AffectDiffuse"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, true, + p_ui, plRTSpotLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_LIGHT_DIFFUSE, + end, + + plRTSpotLight::kAmbientOnlyStub, _T("AmbientOnly"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, false, + p_ui, plRTSpotLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_AMBIENT_ONLY_STUB, + end, + + plRTSpotLight::kCastShadows, _T("CastShadows"), TYPE_BOOL, 0, IDS_DS_CASTSHADOWSS, +// p_default, false, +// p_ui, plRTSpotLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_CAST_SHADOWS, + end, + + plRTSpotLight::kLightColor, _T("LightColor"), TYPE_RGBA, P_ANIMATABLE, IDS_DS_LIGHTCOL, + p_default, Color(255,255,255), + p_ui, plRTSpotLight::kLightMap1, TYPE_COLORSWATCH, IDC_LIGHT_COLOR, + end, + + plRTSpotLight::kSpec, _T("AffectSpecular"), TYPE_BOOL, P_ANIMATABLE, IDS_DS_SPEC, + p_default, false, + //p_enable_ctrls, plRTSpotLight::kSpecularColorSwatch, + p_ui, plRTSpotLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_AFFECT_SPECULAR, + end, + + plRTSpotLight::kSpecularColorSwatch,_T("SpecularColor"), TYPE_RGBA, P_ANIMATABLE, IDS_DS_SPECCOL, + p_default, Color(255,255,255), + p_ui, plRTSpotLight::kLightMap1, TYPE_COLORSWATCH, IDC_LIGHT_COLOR_SPECULAR, + end, + + plRTSpotLight::kIntensity, _T("IntensityEditSpinner"), TYPE_FLOAT, P_ANIMATABLE, IDS_DB_MULTIPLIER, + p_range, -250.0, 250.0, + p_default, 1.0, + p_ui, plRTSpotLight::kLightMap1, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_LMULT,IDC_LMULTSPINNER, .05f, + end, + + /// Rollout 2 - Attenuation Parameters + + plRTSpotLight::kUseAttenuationBool, _T("AttenOnBool"), TYPE_BOOL, 0, IDS_DS_USEFARATTEN, + p_default, TRUE, + p_ui, plRTSpotLight::kLightMap2, TYPE_SINGLECHEKBOX, IDC_LIGHT_ATTENBOOL, + p_enable_ctrls, 2, plRTSpotLight::kAttenMaxFalloffEdit,plRTSpotLight::kAttenTypeRadio, + end, + + plRTSpotLight::kAttenMaxFalloffEdit, _T("MaxFalloffEdit"), TYPE_FLOAT, P_ANIMATABLE, IDS_DS_ATTENSTARTNEAR, + p_range, 0.0, 999999999.0, + p_default, 200.0, + p_ui, plRTSpotLight::kLightMap2, TYPE_SPINNER, EDITTYPE_POS_UNIVERSE, + IDC_LIGHT_ATTEN, IDC_LIGHT_ATTEN_SPIN, .05f, + end, + + plRTSpotLight::kAttenTypeRadio, _T("LightShapeRadio"), TYPE_INT, 0, IDS_DS_LIGHTSHAPE_RATIO, + p_default, 0, + //p_vals, 1, 2, + p_ui, plRTSpotLight::kLightMap2, TYPE_RADIO, 3, + IDC_LIGHT_ATTEN_LINEAR_RADIO, IDC_LIGHT_ATTEN_QUAD_RADIO, IDC_LIGHT_ATTEN_NONE_RADIO, + + end, + + /// Rollout 3 - Spotlight Parameters + + plRTSpotLight::kShowConeBool, _T("ShowConeBool"), TYPE_BOOL, 0, IDS_DS_SHOWCONE, + p_default, false, + p_ui, plRTSpotLight::kLightMap3, TYPE_SINGLECHEKBOX, IDC_SHOW_CONE, + end, + + + plRTSpotLight::kHotSpot, _T("HotSpot"), TYPE_FLOAT, P_ANIMATABLE, IDS_DB_HOTSIZE, + p_range, 0.0, 179.0f, + p_default, 43.0, + p_ui, plRTSpotLight::kLightMap3, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_LHOTSIZE, IDC_LHOTSIZESPINNER, .05f, + end, + + plRTSpotLight::kFallOff, _T("FallOff"), TYPE_FLOAT, P_ANIMATABLE, IDS_DB_FALLSIZE, + p_range, 0.0, 179.0f, + p_default, 45.0, + p_ui, plRTSpotLight::kLightMap3, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_LFALLOFF, IDC_LFALLOFFSPINNER, .05f, + end, + + plRTSpotLight::kUseProjectorBool, _T("UseProjBool"), TYPE_BOOL, 0, IDS_DS_PROJ_PARAMS, + p_default, false, + p_enable_ctrls, 1, plRTSpotLight::kProjMapTexButton, + p_ui, plRTSpotLight::kLightMap3, TYPE_SINGLECHEKBOX, IDC_PROJECTOR, + end, + + plRTSpotLight::kProjMapTexButton, _T("ProjMapButton"), TYPE_BITMAP, P_SHORT_LABELS, IDS_DS_PROJMAP, + p_ui, plRTSpotLight::kLightMap3, TYPE_BITMAPBUTTON, IDC_PROJ_MAPNAME, + p_accessor, plLightTexPBAccessor::Instance(), + end, + + plRTLightBase::kProjTypeRadio, _T("ProjTypeRadio"), TYPE_INT, 0, 0, + p_default, plRTLightBase::kIlluminate, + p_ui, plRTSpotLight::kLightMap3, TYPE_RADIO, 4, IDC_ILLUMINATE, IDC_ADD, IDC_MULT, IDC_MADD, + p_vals, plRTLightBase::kIlluminate, plRTLightBase::kAdd, plRTLightBase::kMult, plRTLightBase::kMADD, + end, + + plRTLightBase::kProjNoCompress, _T("NoCompress"), TYPE_BOOL, 0, IDS_DS_PROJ_PARAMS, + p_default, false, + p_ui, plRTSpotLight::kLightMap3, TYPE_SINGLECHEKBOX, IDC_PROJ_NOCOMPRESS, + end, + + plRTLightBase::kProjNoMip, _T("NoMip"), TYPE_BOOL, 0, IDS_DS_PROJ_PARAMS, + p_default, false, + p_ui, plRTSpotLight::kLightMap3, TYPE_SINGLECHEKBOX, IDC_PROJ_NOMIP, + end, + + + end +); + + +/////////////////////////////////////////////////////////////////////////////// +//// Omni Lights ////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ParamBlockDesc2 RTOmniLightBlk +( + /// Main def + plRTOmniLight::kBlkOmniLight, _T("RT Omni Light"), 0, plRTOmniLightDesc::GetDesc(), P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plRTOmniLight::kRefOmniLight, + + /// Rollout definitions + 2, + plRTOmniLight::kLightMap1, IDD_LIGHT_PARAM, IDS_LIGHT_GEN_PARAMS, 0, 0, &gLiteDlgProc, + plRTOmniLight::kLightMap2, IDD_LIGHT_ATTEN, IDS_LIGHT_ATTEN_PARAMSS, 0, 0, &gLiteDlgProc, + + /// Rollout 1 - General Parameters + plRTOmniLight::kLightOn, _T("on"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, true, + p_ui, plRTOmniLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_LIGHT_ON, + end, + + plRTOmniLight::kAffectDiffuse, _T("AffectDiffuse"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, true, + p_ui, plRTOmniLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_LIGHT_DIFFUSE, + end, + + plRTOmniLight::kAmbientOnlyStub, _T("AmbientOnly"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, false, + p_ui, plRTOmniLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_AMBIENT_ONLY_STUB, + end, + + plRTOmniLight::kCastShadows, _T("CastShadows"), TYPE_BOOL, 0, IDS_DS_CASTSHADOWSS, +// p_default, false, +// p_ui, plRTOmniLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_CAST_SHADOWS, + end, + + plRTOmniLight::kLightColor, _T("LightColor"), TYPE_RGBA, P_ANIMATABLE, IDS_DS_LIGHTCOL, + p_default, Color(255,255,255), + p_ui, plRTOmniLight::kLightMap1, TYPE_COLORSWATCH, IDC_LIGHT_COLOR, + end, + + plRTOmniLight::kSpec, _T("AffectSpecular"), TYPE_BOOL, P_ANIMATABLE, IDS_DS_SPEC, + p_default, false, + p_ui, plRTOmniLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_AFFECT_SPECULAR, + end, + + plRTOmniLight::kSpecularColorSwatch,_T("SpecularColor"), TYPE_RGBA, P_ANIMATABLE, IDS_DS_SPECCOL, + p_default, Color(255,255,255), + p_ui, plRTOmniLight::kLightMap1, TYPE_COLORSWATCH, IDC_LIGHT_COLOR_SPECULAR, + end, + + + plRTOmniLight::kIntensity, _T("IntensityEditSpinner"), TYPE_FLOAT, P_ANIMATABLE, IDS_DB_MULTIPLIER, + p_range, -250.0, 250.0, + p_default, 1.0, + p_ui, plRTOmniLight::kLightMap1, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_LMULT,IDC_LMULTSPINNER, .05f, + end, + + + /// Rollout 2 - Light Attenuation Parameters + + plRTOmniLight::kUseAttenuationBool, _T("AttenOnBool"), TYPE_BOOL, 0, IDS_DS_USEFARATTEN, + p_default, TRUE, + p_ui, plRTOmniLight::kLightMap2, TYPE_SINGLECHEKBOX, IDC_LIGHT_ATTENBOOL, + p_enable_ctrls, 2, plRTOmniLight::kAttenMaxFalloffEdit, plRTOmniLight::kAttenTypeRadio, + end, + + plRTOmniLight::kAttenMaxFalloffEdit, _T("MaxFalloffEdit"), TYPE_FLOAT, P_ANIMATABLE, IDS_DS_ATTENSTARTNEAR, + p_range, 0.0, 999999999.0, + p_default, 200.0, + p_ui, plRTOmniLight::kLightMap2, TYPE_SPINNER, EDITTYPE_POS_UNIVERSE, + IDC_LIGHT_ATTEN, IDC_LIGHT_ATTEN_SPIN, .05f, + end, + + plRTOmniLight::kAttenTypeRadio, _T("LightShapeRadio"), TYPE_INT, 0, IDS_DS_LIGHTSHAPE_RATIO, + p_default, 0, + //p_vals, 1, 2, + p_ui, plRTOmniLight::kLightMap2, TYPE_RADIO, 3, + IDC_LIGHT_ATTEN_LINEAR_RADIO, IDC_LIGHT_ATTEN_QUAD_RADIO, IDC_LIGHT_ATTEN_NONE_RADIO, + end, + + end + +); + + +/////////////////////////////////////////////////////////////////////////////// +//// Directional Lights /////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ParamBlockDesc2 RTTDirLightBlk +( + /// Main def + plRTDirLight::kBlkTSpotLight, _T("RT Spot Light"), 0, plRTDirLightDesc::GetDesc(), P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, plRTDirLight::kRefTSpotLight, + + /// Rollout definitions + 1, + plRTDirLight::kLightMap1, IDD_LIGHT_PARAM, IDS_LIGHT_GEN_PARAMS, 0, 0, &gLiteDlgProc, + + /// Rollout 1 - General Parameters + plRTDirLight::kLightOn, _T("on"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, true, + p_ui, plRTDirLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_LIGHT_ON, + end, + + plRTDirLight::kAffectDiffuse, _T("AffectDiffuse"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, true, + p_ui, plRTDirLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_LIGHT_DIFFUSE, + end, + + plRTDirLight::kAmbientOnlyStub, _T("AmbientOnly"), TYPE_BOOL, 0, IDS_RTLIGHT_ON, + p_default, false, + p_ui, plRTDirLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_AMBIENT_ONLY_STUB, + end, + + plRTDirLight::kCastShadows, _T("CastShadows"), TYPE_BOOL, 0, IDS_DS_CASTSHADOWSS, +// p_default, false, +// p_ui, plRTDirLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_CAST_SHADOWS, + end, + + plRTDirLight::kLightColor, _T("LightColor"), TYPE_RGBA, P_ANIMATABLE, IDS_DS_LIGHTCOL, + p_default, Color(255,255,255), + p_ui, plRTDirLight::kLightMap1, TYPE_COLORSWATCH, IDC_LIGHT_COLOR, + end, + + plRTDirLight::kSpec, _T("AffectSpecular"), TYPE_BOOL, P_ANIMATABLE, IDS_DS_SPEC, + p_default, false, + p_ui, plRTDirLight::kLightMap1, TYPE_SINGLECHEKBOX, IDC_AFFECT_SPECULAR, + end, + + plRTDirLight::kSpecularColorSwatch,_T("SpecularColor"), TYPE_RGBA, P_ANIMATABLE, IDS_DS_SPECCOL, + p_default, Color(255,255,255), + p_ui, plRTDirLight::kLightMap1, TYPE_COLORSWATCH, IDC_LIGHT_COLOR_SPECULAR, + end, + + plRTDirLight::kIntensity, _T("IntensityEditSpinner"), TYPE_FLOAT, P_ANIMATABLE, IDS_DB_MULTIPLIER, + p_range, -250.0, 250.0, + p_default, 1.0, + p_ui, plRTDirLight::kLightMap1, TYPE_SPINNER, EDITTYPE_FLOAT, + IDC_LMULT,IDC_LMULTSPINNER, .05f, + end, + + end +); + + +#endif //_plRTLightsPB_h + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/prim.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/prim.h new file mode 100644 index 00000000..aa0298fe --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/prim.h @@ -0,0 +1,104 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/********************************************************************** + *< + FILE: prim.h + + DESCRIPTION: + + CREATED BY: Dan Silva + + HISTORY: + + *> Copyright (c) 1994, All Rights Reserved. + **********************************************************************/ + +#ifndef __PRIM__H +#define __PRIM__H + +#include "Max.h" +#include "resource.h" + +#ifdef DESIGN_VER //for conversion to amodeler solids +#include "igeomimp.h" +#include "plugapi.h" +#endif + +TCHAR *GetString(int id); + +extern ClassDesc* GetBoxobjDesc(); +extern ClassDesc* GetSphereDesc(); +extern ClassDesc* GetCylinderDesc(); +extern ClassDesc* GetSimpleCamDesc(); +extern ClassDesc* GetOmniLightDesc(); +extern ClassDesc* GetDirLightDesc(); +extern ClassDesc *GetTDirLightDesc(); +extern ClassDesc* GetFSpotLightDesc(); +extern ClassDesc* GetTSpotLightDesc(); +extern ClassDesc* GetLookatCamDesc(); +extern ClassDesc* GetSplineDesc(); +#ifdef DESIGN_VER +extern ClassDesc* GetOrthoSplineDesc(); +#endif +extern ClassDesc* GetNGonDesc(); +extern ClassDesc* GetDonutDesc(); +extern ClassDesc* GetTargetObjDesc(); +extern ClassDesc* GetBonesDesc(); +extern ClassDesc* GetRingMasterDesc(); +extern ClassDesc* GetSlaveControlDesc(); +extern ClassDesc* GetQuadPatchDesc(); +extern ClassDesc* GetTriPatchDesc(); +extern ClassDesc* GetTorusDesc(); +extern ClassDesc* GetMorphObjDesc(); +extern ClassDesc* GetCubicMorphContDesc(); +extern ClassDesc* GetRectangleDesc(); +extern ClassDesc* GetBoolObjDesc(); +extern ClassDesc* GetTapeHelpDesc(); +extern ClassDesc* GetProtHelpDesc(); +extern ClassDesc* GetTubeDesc(); +extern ClassDesc* GetConeDesc(); +extern ClassDesc* GetHedraDesc(); +extern ClassDesc* GetCircleDesc(); +extern ClassDesc* GetEllipseDesc(); +extern ClassDesc* GetArcDesc(); +extern ClassDesc* GetStarDesc(); +extern ClassDesc* GetHelixDesc(); +extern ClassDesc* GetRainDesc(); +extern ClassDesc* GetSnowDesc(); +extern ClassDesc* GetTextDesc(); +extern ClassDesc* GetTeapotDesc(); +extern ClassDesc* GetBaryMorphContDesc(); +#ifdef DESIGN_VER +extern ClassDesc* GetOrthoSplineDesc(); +extern ClassDesc* GetParallelCamDesc(); +#endif +extern ClassDesc* GetGridobjDesc(); +extern ClassDesc* GetNewBonesDesc(); + +extern HINSTANCE hInstance; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/resource.h new file mode 100644 index 00000000..ea2c487a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/resource.h @@ -0,0 +1,1135 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by plRTLights.rc +// +#define IDS_PW_GRID 1 +#define IDD_SPHEREPARAM 101 +#define IDD_SPHEREPARAM2 101 +#define IDB_SPINNER 102 +#define IDD_CYLINDERPARAM 102 +#define IDD_CYLINDERPARAM2 102 +#define IDC_SPINNERCUR 103 +#define IDD_BOXPARAM 103 +#define IDD_BOXPARAM2 103 +#define IDD_SHADOWMAP_PARAMS 103 +#define IDD_WAVEPARAM 104 +#define IDD_SCAMERA 105 +#define IDD_SCAMERAPARAM 105 +#define IDD_LIGHT_PARAM 106 +#define IDD_CYLINDERPARAM1 107 +#define IDD_DIRLIGHTPARAM 108 +#define IDD_DUMMY1 109 +#define IDD_DUMMY2 110 +#define IDD_DUMMY3 111 +#define IDD_DUMMY4 112 +#define IDD_SPHEREPARAM1 113 +#define IDD_LIGHT_ANIM 113 +#define IDD_DUMMY5 114 +#define IDD_DUMMY6 115 +#define IDD_FSPOTLIGHTPARAM 116 +#define IDD_SPOTLIGHT 116 +#define IDD_SPLINEPARAM 117 +#define IDD_SPLINEPARAM1 117 +#define IDD_NGONPARAM2 118 +#define IDD_NGONPARAM1 119 +#define IDD_DONUTPARAM2 120 +#define IDD_DONUTPARAM1 121 +#define IDD_SAMPLEPARAM 122 +#define IDD_FCAMERAPARAM 123 +#define IDD_PATCHGRIDPARAM 124 +#define IDD_TRIPATCHPARAM 125 +#define IDD_PSPHEREPARAM1 126 +#define IDD_PSPHEREPARAM2 127 +#define IDD_TORUSPARAM1 128 +#define IDB_HYBRIDKEYBUTTONS 128 +#define IDD_TORUSPARAM2 129 +#define IDB_MASK_HYBRIDKEYBUTTONS 129 +#define IDD_MORPHPARAM 130 +#define IDD_MORPHPARAM2 130 +#define IDD_MORPHPARAM1 131 +#define IDD_FREE_SPOTLIGHT 132 +#define IDD_LIGHT_SHADOW 134 +#define IDD_LIGHT_SHADOW3 134 +#define IDD_RECTANGLEPARAM 135 +#define IDD_BOOLPARAM1 136 +#define IDD_BOOLPARAM2 137 +#define IDD_TAPEHELPER 138 +#define IDD_SPHEREPARAM3 139 +#define IDD_CYLINDERPARAM3 140 +#define IDD_TORUSPARAM3 141 +#define IDD_TUBEPARAM1 142 +#define IDD_TUBEPARAM2 143 +#define IDD_TUBEPARAM3 144 +#define IDD_BOXPARAM1 145 +#define IDD_BOXPARAM3 146 +#define IDD_CONEPARAM1 147 +#define IDD_CONEPARAM2 148 +#define IDD_CONEPARAM3 149 +#define IDD_HEDRAPARAM 150 +#define IDD_HELIXPARAM1 151 +#define IDD_HELIXPARAM2 152 +#define IDB_TEXT_BUTTONS 152 +#define IDD_DONUTPARAM3 153 +#define IDB_TEXT_MASKBUTTONS 153 +#define IDD_NGONPARAM3 154 +#define IDB_FOV 154 +#define IDD_HELIXPARAM3 156 +#define IDC_CROSS_HAIR 156 +#define IDD_RECTANGLEPARAM3 157 +#define IDD_RECTANGLEPARAM2 158 +#define IDB_FOVMASK 158 +#define IDD_RECTANGLEPARAM1 159 +#define IDB_CI_TORUS32_MASK 159 +#define IDD_CIRCLEPARAM2 160 +#define IDB_CI_SPHERE16_MASK 160 +#define IDD_CIRCLEPARAM1 161 +#define IDB_CI_SPHERE32 161 +#define IDD_ELLIPSEPARAM3 162 +#define IDB_CI_SPHERE32_MASK 162 +#define IDD_ELLIPSEPARAM2 163 +#define IDB_CI_TEAPOT16 163 +#define IDD_ELLIPSEPARAM1 164 +#define IDB_CI_TEAPOT16_MASK 164 +#define IDD_CIRCLEPARAM3 165 +#define IDB_CI_TEAPOT32 165 +#define IDD_ARCPARAM3 166 +#define IDB_CI_TEAPOT32_MASK 166 +#define IDD_ARCPARAM2 167 +#define IDB_CI_TORUS16 167 +#define IDD_ARCPARAM1 168 +#define IDB_CI_TORUS16_MASK 168 +#define IDD_RAINPARAM 169 +#define IDB_CI_TORUS32 169 +#define IDD_STARPARAM2 170 +#define IDB_CI_SPHERE16 170 +#define IDD_STARPARAM1 171 +#define IDD_SNOWPARAM 172 +#define IDD_TEXTPARAM 173 +#define IDD_INTERPPARAMS 174 +#define IDD_SPLINEPARAM2 175 +#define IDD_TEAPOTPARAM1 176 +#define IDD_TEAPOTPARAM2 177 +#define IDD_TEAPOTPARAM3 178 +#define IDD_CUBICMORPH_KEYINFO 179 +#define IDD_BARYMORPH_KEYINFO 180 +#define IDD_OMNI 181 +#define IDD_BONEPARAMS 182 +#define IDD_BONEPARAMS_OLD 182 +#define IDD_PATCHGRIDPARAM2 183 +#define IDD_TRIPATCHPARAM2 184 +#define IDD_PROTHELPER 185 +#define IDD_LIGHT_ATTEN 186 +#define IDD_PCAM_CUSTOM 188 +#define IDD_PCAM_STANDARD 190 +#define IDD_LIGHT_SHADOW1 191 +#define IDB_PUSHPIN 192 +#define IDD_GRIDPARAM3 192 +#define IDB_LOCK_ADJACENT 193 +#define IDD_GRIDPARAM2 193 +#define IDD_GRIDPARAM1 194 +#define IDB_LOCK_SEPERATED 195 +#define IDD_LIGHT_SHADOW2 195 +#define IDD_LIGHT_EMITTER 196 +#define IDD_PROJ_DIRECTIONAL 196 +#define IDD_CAMERA_DOF 197 +#define IDB_SUBOBJTYPES 198 +#define IDD_BONEPARAMS_NEW 198 +#define IDB_MASK_SUBOBJTYPES 199 +#define IDD_NEWBONE 199 +#define IDC_RADIUS 1000 +#define IDC_SEGMENTS 1001 +#define IDC_AMPLITUDE 1001 +#define IDC_PARAM_P 1001 +#define IDC_TI_ARCFROM 1001 +#define IDC_BOOL_BNAME 1001 +#define IDC_CREATEDIAMETER 1002 +#define IDC_CYCLES 1002 +#define IDC_HEMISPHERE 1002 +#define IDC_CAPSEGMENTS 1002 +#define IDC_PARAM_Q 1002 +#define IDC_TI_ARCTO 1002 +#define IDC_CREATERADIUS 1003 +#define IDC_PHASE 1003 +#define IDC_SCALE_P 1003 +#define IDC_SEGSPINNER 1004 +#define IDC_NUMNODES 1004 +#define IDC_SCALE_Q 1004 +#define IDC_RADSPINNER 1005 +#define IDC_CAPSEGSPINNER 1005 +#define IDC_OBSMOOTH 1006 +#define IDC_AMPSPINNER 1006 +#define IDC_PARAM_PSPIN 1006 +#define IDC_TI_ARCFROMSPINNER 1006 +#define IDC_LENGTH 1007 +#define IDC_CYCSPINNER 1007 +#define IDC_HEMISPHERESPINNER 1007 +#define IDC_PARAM_QSPIN 1007 +#define IDC_TI_ARCTOSPINNER 1007 +#define IDC_LENSPINNER 1008 +#define IDC_PHSSPINNER 1008 +#define IDC_SCALE_PSPIN 1008 +#define IDC_SIDES 1009 +#define IDC_NUMSPINNER 1009 +#define IDC_SCALE_QSPIN 1009 +#define IDC_POINTS 1009 +#define IDC_SCALESPINNER 1009 +#define IDC_SIDESPINNER 1010 +#define IDC_HEIGHT 1010 +#define IDC_HEIGHTEDIT 1010 +#define IDC_SCALE_R 1010 +#define IDC_POINTSSPINNER 1010 +#define IDC_WIDTHSPINNER 1011 +#define IDC_PIESLICESPIN1 1011 +#define IDC_SCALE_RSPIN 1011 +#define IDC_DISTORTSPINNER 1011 +#define IDC_HEIGHTSPINNER 1012 +#define IDC_PIESLICESPIN2 1012 +#define IDC_DISTORT 1012 +#define IDC_FILLETSPINNER 1012 +#define IDC_BROWSE 1013 +#define IDC_HSEGSPIN 1013 +#define IDC_RADSPINNER2 1013 +#define IDC_CAPSEGSPIN 1013 +#define IDC_AP_FILLETSPINNER 1013 +#define IDC_OBJFILENAME 1014 +#define IDC_HSEGS 1014 +#define IDC_FOV 1015 +#define IDC_WSEGSPIN 1015 +#define IDC_AP_FILLETSPINNER2 1015 +#define IDC_FOVSPINNER 1016 +#define IDC_WSEGS 1016 +#define IDC_LRED 1017 +#define IDC_LSEGSPIN 1017 +#define IDC_FOVSPINNER2 1017 +#define IDC_LREDSPINNER 1018 +#define IDC_LSEGS 1018 +#define IDC_FOV2 1018 +#define IDC_PROT_PICK_TARGET1 1018 +#define IDC_SHOWCAMCONE 1019 +#define IDC_PROT_PICK_TARGET2 1019 +#define IDC_RENDERSEGSPINNER 1019 +#define IDC_CHECK1 1020 +#define IDC_SHOW_CONE 1020 +#define IDC_BOOL_OPTIMIZE 1020 +#define IDC_SHOWHORZLINE 1020 +#define IDC_SPEC_LEN 1020 +#define IDC_HEMI_RECENTER 1020 +#define IDC_SLICEON 1020 +#define IDC_ARCPIE 1020 +#define IDC_HIDE_EMITTER 1020 +#define IDC_TEXT_MANUAL_UPDATE 1020 +#define IDC_BONES_ASSIGNIK 1020 +#define IDC_BONE_SIDEFINS 1020 +#define IDC_LIGHT_ATTENBOOL 1020 +#define IDC_AMBIENT_ONLY_STUB 1020 +#define IDC_LGREEN 1021 +#define IDC_BUTTON1 1021 +#define IDC_CREATE_MORPHKEY 1021 +#define IDC_15MM 1021 +#define IDC_SCALE_RESET 1021 +#define IDC_REVERSE 1021 +#define IDC_BARYMORPH_100TOTAL 1021 +#define IDC_BONES_ASSIGNIKROOT 1021 +#define IDC_BONE_FRONTFIN 1021 +#define IDC_LGREENSPINNER 1022 +#define IDC_BUTTON2 1022 +#define IDC_PROJ_IMAGE 1022 +#define IDC_20MM 1022 +#define IDC_DELETE_MORPHTARG 1022 +#define IDC_BONES_CREATEENDEFFECTOR 1022 +#define IDC_BONE_BACKFIN 1022 +#define IDC_LBLUE 1023 +#define IDC_RADIO1 1023 +#define IDC_BUTTON3 1023 +#define IDC_TARG_REFERENCE 1023 +#define IDC_RECT_LIGHT 1023 +#define IDC_24MM 1023 +#define IDC_BOOL_UNION 1023 +#define IDC_HEMI_SQUASH 1023 +#define IDC_SMOOTH_ALL 1023 +#define IDC_FAM_TETRA 1023 +#define IDC_HELIX_CW 1023 +#define IDC_PARTICLE_DOTS 1023 +#define IDC_PART_BOTH 1023 +#define IDC_INSCRIBED 1023 +#define IDC_BONE_REFINE 1023 +#define IDC_LIGHT_ATTEN_LINEAR_RADIO 1023 +#define IDC_LBLUESPINNER 1024 +#define IDC_RADIO2 1024 +#define IDC_BUTTON4 1024 +#define IDC_TARG_COPY 1024 +#define IDC_CIRCLE_LIGHT 1024 +#define IDC_28MM 1024 +#define IDC_BOOL_INTERSECTION 1024 +#define IDC_HEMI_CHOP 1024 +#define IDC_SMOOTH_SIDES 1024 +#define IDC_FAM_OCTA 1024 +#define IDC_HELIX_CCW 1024 +#define IDC_PARTICLE_TICKS 1024 +#define IDC_PART_BODY 1024 +#define IDC_CIRCUMSCRIBED 1024 +#define IDC_LIGHT_ATTEN_QUAD_RADIO 1024 +#define IDC_RADIO3 1025 +#define IDC_BUTTON5 1025 +#define IDC_LHOTSIZESPINNER 1025 +#define IDC_TARG_MOVE 1025 +#define IDC_35MM 1025 +#define IDC_MAP_RANGE 1025 +#define IDC_BOOL_DIFFERENCEA 1025 +#define IDC_SMOOTH_NONE 1025 +#define IDC_FAM_DODEC 1025 +#define IDC_PART_LID 1025 +#define IDC_BOOL_DIFFERENCEAB 1025 +#define IDC_LIGHT_ATTEN_NONE_RADIO 1025 +#define IDC_RADIO4 1026 +#define IDC_BUTTON6 1026 +#define IDC_LHOTSIZE 1026 +#define IDC_TARG_INSTANCE 1026 +#define IDC_50MM 1026 +#define IDC_BOOL_DISPRESULT 1026 +#define IDC_FAM_STAR1 1026 +#define IDC_SMOOTH_STRIPES 1026 +#define IDC_ATM_OPACITY 1026 +#define IDC_BUTTON7 1027 +#define IDC_LFALLOFFSPINNER 1027 +#define IDC_85MM 1027 +#define IDC_BOOL_DIFFERENCEB 1027 +#define IDC_BOOL_DIFFERENCEBA 1027 +#define IDC_ATM_COLAMT 1027 +#define IDC_CHECK2 1028 +#define IDC_BUTTON8 1028 +#define IDC_LFALLOFF 1028 +#define IDC_135MM 1028 +#define IDC_TICKMARKS 1028 +#define IDC_BOOL_SHOWHIDDENOPS 1028 +#define IDC_GENTEXTURE 1028 +#define IDC_PARTICLE_CONSTANTRATE 1028 +#define IDC_BONE_AUTOLINK 1028 +#define IDC_BONE_GENMAP 1028 +#define IDC_BUTTON9 1029 +#define IDC_LTDISTSPINNER 1029 +#define IDC_200MM 1029 +#define IDC_BONE_COPYIKPARAMS 1029 +#define IDC_DEPTH 1029 +#define IDC_BUTTON10 1030 +#define IDC_LASPECT_SPIN 1030 +#define IDC_CREATE_VIEW 1030 +#define IDC_BONE_MATCHALIGNMENT 1030 +#define IDC_ENABLE_MP_EFFECT 1030 +#define IDC_DEPTHSPINNER 1030 +#define IDC_BUTTON11 1031 +#define IDC_LASPECT 1031 +#define IDC_PREVIEW_MP_EFFECT 1031 +#define IDC_LIST1 1032 +#define IDC_MORPHTARG_LIST 1032 +#define IDC_LBLUR_SPIN 1032 +#define IDC_BOOL_OPERANDS 1032 +#define IDC_BARYMORPH_TARGLIST 1032 +#define IDC_MP_EFFECT_REFFECT_PER_PASS 1032 +#define IDC_SCROLLBAR2 1033 +#define IDC_LBLUR 1033 +#define IDC_KEYTIME 1033 +#define IDC_KEYTIMESPIN 1034 +#define IDC_SCROLLBAR1 1035 +#define IDC_SCROLLBAR3 1035 +#define IDC_TCB_T 1035 +#define IDC_SCROLLBAR4 1036 +#define IDC_TCB_TSPIN 1036 +#define IDC_EDIT1 1037 +#define IDC_TEXTENTRY 1037 +#define IDC_TCB_C 1037 +#define IDC_TCB_CSPIN 1038 +#define IDC_USER1 1039 +#define IDC_PICK_MORPHTARG 1039 +#define IDC_LIGHT_COLOR 1039 +#define IDC_TCB_B 1039 +#define IDC_ICORNER 1040 +#define IDC_TCB_BSPIN 1040 +#define IDC_SHADOW_COLOR 1040 +#define IDC_LIGHT_COLOR_SPECULAR 1040 +#define IDC_ISMOOTH 1041 +#define IDC_BARYMORPH_PERCENT 1041 +#define IDC_DCORNER 1042 +#define IDC_OBCIRCULAR 1042 +#define IDC_BARYMORPH_PERCENTSPIN 1042 +#define IDC_DSMOOTH 1043 +#define IDC_RADIUS1 1043 +#define IDC_DBEZIER 1044 +#define IDC_RAD1SPINNER 1044 +#define IDC_RADIUS2 1045 +#define IDC_RAD2SPINNER 1046 +#define IDC_TORUS_ROT 1047 +#define IDC_TURNS 1047 +#define IDC_TORUS_ROTSPIN 1048 +#define IDC_TORUS_TWIST 1049 +#define IDC_TURNSSPINNER 1049 +#define IDC_SHOW_RANGES 1050 +#define IDC_TORUS_TWISTSPIN 1050 +#define IDC_TURNSEDIT 1050 +#define IDC_BIASSPINNER 1051 +#define IDC_SHOW_RANGES1 1051 +#define IDC_BIASEDIT 1052 +#define IDC_SHOW_DECAY 1053 +#define IDC_USE_ATTEN 1062 +#define IDC_START_RANGE_SPIN 1063 +#define IDC_START_RANGE 1064 +#define IDC_END_RANGE_SPIN 1065 +#define IDC_END_RANGE 1066 +#define IDC_LMULTSPINNER 1067 +#define IDC_LMULT 1068 +#define IDC_LTDIST 1069 +#define IDC_SSTEPSSPINNER 1069 +#define IDC_USE_ATTEN1 1069 +#define IDC_TDIST 1070 +#define IDC_SHAPESTEPS 1070 +#define IDC_AFFECT_DIFFUSE 1070 +#define IDC_TDISTSPINNER 1071 +#define IDC_AFFECT_SPECULAR 1071 +#define IDC_HITHER_SPIN 1072 +#define IDC_LCONTRAST 1072 +#define IDC_DIFFSOFT 1072 +#define IDC_LVSPINNER 1073 +#define IDC_HITHER 1073 +#define IDC_LV 1074 +#define IDC_YON_SPIN 1074 +#define IDC_LSSPINNER 1075 +#define IDC_YON 1075 +#define IDC_LS 1076 +#define IDC_LHSPINNER 1077 +#define IDC_LH 1078 +#define IDC_LCONTRASTSPIN 1079 +#define IDC_AMBIENT_ONLY 1080 +#define IDC_OVERSHOOT 1081 +#define IDC_DECAY_EDIT 1082 +#define IDC_PROJECTOR 1083 +#define IDC_DECAY_SPIN 1084 +#define IDC_PROJ_NOCOMPRESS 1084 +#define IDC_BITMAP_FIT 1085 +#define IDC_PROJ_NOMIP 1085 +#define IDC_LIGHT_DIFFUSE 1086 +#define IDC_LIGHT_ON 1087 +#define IDC_PREVKEY 1090 +#define IDC_NEXTKEY 1091 +#define IDC_KEYNUM 1092 +#define IDC_NAMES 1142 +#define IDC_LOOP 1143 +#define IDC_AUTO_START 1144 +#define IDC_LOOPS 1145 +#define IDC_REFRESH 1146 +#define IDC_TARGNAME 1157 +#define IDC_LIGHT_ATTEN_SLIDER 1175 +#define IDC_TCB_GRAPH 1179 +#define IDC_SOFTEN_DIFFUSE 1180 +#define IDC_DIFFSOFTSPIN 1181 +#define IDC_LCONTRASTSPIN2 1190 +#define IDC_LCONTRAST2 1191 +#define IDC_ADAPTIVE 1225 +#define IDC_SC_SLICEON 1238 +#define IDC_SC_SLICE1 1239 +#define IDC_SC_SLICE1SPIN 1240 +#define IDC_SC_SLICE2 1241 +#define IDC_SC_SLICE2SPIN 1242 +#define IDC_PROT_TARGET1_NAME 1274 +#define IDC_PROT_TARGET2_NAME 1275 +#define IDC_OPTIMIZE 1500 +#define IDC_LENS 3000 +#define IDC_LENSSPINNER 3001 +#define IDC_MAP_BIAS 3002 +#define IDC_MAP_BIAS_SPIN 3003 +#define IDC_MAP_SIZE 3004 +#define IDC_MAP_SIZE_SPIN 3005 +#define IDC_RT_BIAS 3006 +#define IDC_RT_BIAS_SPIN 3007 +#define IDC_LENGTHEDIT 3009 +#define IDC_WIDTHEDIT 3010 +#define IDC_PICK_BOOLOPERAND 3011 +#define IDC_FILLET 3011 +#define IDC_SCALEEDIT 3011 +#define IDC_BOOL_RECALC 3012 +#define IDC_AP_FILLET2 3012 +#define IDC_RENDERSEGEDIT 3012 +#define IDC_BOOL_EXTRACTOP 3013 +#define IDC_BOOL_DISPOPS 3015 +#define IDC_BOOL_UPDATERENDER 3016 +#define IDC_BOOL_UPDATEMANUAL 3017 +#define IDC_BOOL_UPDATEALWAYS 3018 +#define IDC_BOOL_UPDATESELECT 3019 +#define IDC_TI_POSX 3019 +#define IDC_TI_POSXSPIN 3020 +#define IDC_TI_POSY 3021 +#define IDC_TI_POSYSPIN 3022 +#define IDC_TI_CREATE 3023 +#define IDC_TI_POSZ 3024 +#define IDC_PIESLICE1 3024 +#define IDC_TEXTITALIC 3024 +#define IDC_TI_POSZSPIN 3025 +#define IDC_PIESLICE2 3025 +#define IDC_TEXTUNDERLINE 3025 +#define IDC_TI_CLOSE 3026 +#define IDC_TEXTLEFT 3026 +#define IDC_RADSPINNER1 3027 +#define IDC_TI_FINISH 3027 +#define IDC_TEXTCENTER 3027 +#define IDC_CREATECUBE 3028 +#define IDC_TEXTRIGHT 3028 +#define IDC_CREATEBOX 3029 +#define IDC_TEXTJUSTIFIED 3029 +#define IDC_FAM_STAR2 3030 +#define IDC_VERT_BASIC 3031 +#define IDC_VERT_CENTER 3032 +#define IDC_VERT_SIDES 3033 +#define IDC_RADIUSSPIN 3034 +#define IDC_MANUAL_CLIP 3035 +#define IDC_CREATEEDGE 3036 +#define IDC_VPT_PARTICLES 3036 +#define IDC_CREATECENTER 3037 +#define IDC_VPT_PARTICLESSPIN 3037 +#define IDC_ARCFROM 3038 +#define IDC_PARTICLE_SIZE 3038 +#define IDC_ARCFROMSPINNER 3039 +#define IDC_PARTICLE_SIZESPIN 3039 +#define IDC_ARCTO 3040 +#define IDC_PARTICLE_START 3040 +#define IDC_ARCTOSPINNER 3041 +#define IDC_PARTICLE_STARTSPIN 3041 +#define IDC_CREATE_EEC 3042 +#define IDC_PARTICLE_LIFE 3042 +#define IDC_TEXTFONT 3042 +#define IDC_CREATE_CEE 3043 +#define IDC_PARTICLE_LIFESPIN 3043 +#define IDC_TEXTSIZEEDIT 3043 +#define IDC_EMITTER_WIDTH 3044 +#define IDC_TEXTSIZESPINNER 3044 +#define IDC_EMITTER_WIDTHSPIN 3045 +#define IDC_TEXTBOLD 3045 +#define IDC_TEXTKERNINGEDIT 3045 +#define IDC_EMITTER_HEIGHT 3046 +#define IDC_TEXTKERNINGSPINNER 3046 +#define IDC_EMITTER_HEIGHTSPIN 3047 +#define IDC_TEXTLEADINGEDIT 3047 +#define IDC_PARTICLE_SPEED 3048 +#define IDC_TEXTLEADINGSPINNER 3048 +#define IDC_PARTICLE_SPEEDSPIN 3049 +#define IDC_PARTICLE_VARIATION 3050 +#define IDC_PARTICLE_VARIATIONSPIN 3051 +#define IDC_PARTICLE_CUST 3052 +#define IDC_TEXTEXAMPLE 3053 +#define IDC_PARTICLE_BIRTHRATE 3053 +#define IDC_PARTICLE_BIRTHRATESPIN 3054 +#define IDC_PARTICLE_MAXRATE 3055 +#define IDC_NO_CROSSOVER 3056 +#define IDC_PARTICLE_TUMBLE 3056 +#define IDC_RND_PARTICLES 3056 +#define IDC_FAR_RANGE 3057 +#define IDC_PARTICLE_TUMBLESPIN 3057 +#define IDC_RND_PARTICLESSPIN 3057 +#define IDC_FAR_RANGE_SPIN 3058 +#define IDC_PARTICLE_SCALE 3058 +#define IDC_NEAR_RANGE 3059 +#define IDC_PARTICLE_SCALESPIN 3059 +#define IDC_NEAR_RANGE_SPIN 3060 +#define IDC_REND_PARTICLES 3060 +#define IDC_TI_ADDPOINT 3061 +#define IDC_REND_PARTICLESSPIN 3061 +#define IDC_MAP_RANGE_SPIN 3062 +#define IDC_GLOBAL_SET 3063 +#define IDC_CAST_SHADOWS 3064 +#define IDC_ATM_OPACITY_SPIN 3064 +#define IDC_ABS_MAP_BIAS 3065 +#define IDC_SHADOW_MAPS 3066 +#define IDC_RAY_TRACED_SHADOWS 3067 +#define IDC_LT_EFFECT_SHADCOL 3068 +#define IDC_EXCLUDE_DLG 3069 +#define IDC_PROJ_MAPNAME 3070 +#define IDC_ATM_COLAMT_SPIN 3070 +#define IDC_PARTICLE_REND1 3071 +#define IDC_TEAPART 3071 +#define IDC_PARTICLE_REND2 3072 +#define IDC_PARTICLE_REND3 3073 +#define ID_STATIC 3073 +#define IDC_PARTICLE_TUMBLE_TXT 3074 +#define IDC_TEA_BODY 3074 +#define IDC_PARTICLE_SCALE_TXT 3075 +#define IDC_TEA_HANDLE 3075 +#define IDC_BOOL_ANAME 3075 +#define IDC_PARTICLE_BIRTHRATE_TXT 3076 +#define IDC_TEA_SPOUT 3076 +#define IDC_MORPHTARG_NAME 3076 +#define IDC_TEA_LID 3077 +#define IDC_IS_ORTHO 3080 +#define IDC_AP_FILLET 3081 +#define IDC_BARYMORPH_TARGETLEBEL 3082 +#define IDC_BARYMORPH_PERCENTLABEL 3083 +#define IDC_BARYMORPH_TOTAL 3084 +#define IDC_ATMOS_SHADOWS 3085 +#define IDC_START_RANGE1 4001 +#define IDC_START_RANGE1_SPIN 4002 +#define IDC_END_RANGE1 4003 +#define IDC_END_RANGE1_SPIN 4004 +#define IDC_BOOL_EXTRACT_INTANCE 4005 +#define IDC_BOOL_EXTRACT_COPY 4006 +#define IDC_TARG_DISTANCE 4007 +#define IDC_TEXT_UPDATE 4008 +#define IDC_BONES_AUTOBONE 4009 +#define IDC_FOV_TYPE 4010 +#define IDC_ATT_NONE 4020 +#define IDC_ATT_INV 4021 +#define IDC_ATT_INVSQ 4022 +#define IDC_TAPE_X_AXIS 4030 +#define IDC_TAPE_Y_AXIS 4031 +#define IDC_TAPE_Z_AXIS 4032 +#define IDC_TAPE_XY_PLANE 4033 +#define IDC_TAPE_YZ_PLANE 4034 +#define IDC_TAPE_ZX_PLANE 4035 +#define IDC_PROT_ANGLE 4036 +#define IDC_SHADPROJ_MAPNAME 4037 +#define IDC_CONTRAST_T 4038 +#define IDC_DIFFSOFT_T 4039 +#define IDC_LIGHT_TYPE 4041 +#define IDC_CAM_TYPE 4042 +#define IDC_LIGHT_DECAY 4043 +#define IDC_MP_EFFECT 4043 +#define IDC_PCAM_PLAN 4050 +#define IDC_PCAM_BOTTOM 4051 +#define IDC_PCAM_LEFT 4052 +#define IDC_PCAM_SHOW_VOLUME 4052 +#define IDC_PCAM_RIGHT 4053 +#define IDC_PCAM_SHOW_ENV 4053 +#define IDC_PCAM_FRONT 4054 +#define IDC_PCAM_NEAR_ENV 4054 +#define IDC_PCAM_BACK 4055 +#define IDC_PCAM_NEAR_ENV_SPINNER 4055 +#define IDC_PCAM_QUADRANT1 4056 +#define IDC_PCAM_FAR_ENV 4056 +#define IDC_PCAM_QUADRANT2 4057 +#define IDC_PCAM_FAR_ENV_SPINNER 4057 +#define IDC_PCAM_QUADRANT3 4058 +#define IDC_PCAM_UPDATE 4058 +#define IDC_PCAM_QUADRANT4 4059 +#define IDC_SHADOW_TYPE 4059 +#define IDC_PCAM_DIMETRIC15 4060 +#define IDC_SHAD_MULT 4060 +#define IDC_PCAM_DIMETRIC45 4061 +#define IDC_SHAD_MULT_SPIN 4061 +#define IDC_USE_SHAD_COLMAP 4062 +#define IDC_EMITTER_ENABLE 4063 +#define IDC_TOTALFACES 4063 +#define IDC_EMITTER_CA_PHOTONS_LBL 4064 +#define IDC_EMITTER_DECAY_LBL 4065 +#define IDC_EMITTER_CA_PHOTONS 4066 +#define IDC_EMITTER_CA_PHOTONS_SPIN 4067 +#define IDC_EMITTER_ENERGY 4068 +#define IDC_PCAM_LOCK_X2Z 4069 +#define IDC_EMITTER_ENERGY_SPIN 4069 +#define IDC_PCAM_LOCK_X2Y 4070 +#define IDC_EMITTER_ENERGY_LBL 4070 +#define IDC_PCAM_LOCK_Y2Z 4071 +#define IDC_EMITTER_DECAY 4071 +#define IDC_PCAM_ANGLE_X 4072 +#define IDC_EMITTER_GI_PHOTONS_LBL 4072 +#define IDC_PCAM_ANGLE_X_SPINNER 4073 +#define IDC_EMITTER_GI_PHOTONS 4073 +#define IDC_PCAM_ANGLE_Y 4074 +#define IDC_EMITTER_GI_PHOTONS_SPIN 4074 +#define IDC_PCAM_ANGLE_Y_SPINNER 4075 +#define IDC_PCAM_ANGLE_Z 4076 +#define IDC_PCAM_ANGLE_Z_SPINNER 4077 +#define IDC_PCAM_CLIP_NEAR 4084 +#define IDC_PCAM_CLIP_NEAR_SPINNER 4085 +#define IDC_PCAM_CLIP_FAR 4086 +#define IDC_PCAM_CLIP_FAR_SPINNER 4087 +#define IDC_PCAM_SHOW_SECTION 4088 +#define IDC_PCAM_FIX_HEIGHT 4089 +#define IDC_PCAM_FIX_WIDTH 4090 +#define IDC_PCAM_FIX_ASPECT 4091 +#define IDC_PCAM_HEIGHT 4092 +#define IDC_PCAM_HEIGHT_SPINNER 4093 +#define IDC_PCAM_WIDTH 4094 +#define IDC_PCAM_WIDTH_SPINNER 4095 +#define IDC_PCAM_ASPECT 4096 +#define IDC_PCAM_ASPECT_SPINNER 4097 +#define IDC_OBJECT_SHADOWS 4098 +#define IDC_DOF_FSTOP_LBL 4099 +#define IDC_DOF_ENABLE 4100 +#define IDC_DOF_FSTOP 4101 +#define IDC_DOF_FSTOP_SPIN 4102 +#define IDC_GROUP_CAUSTICS 4103 +#define IDC_GROUP_GLOBALILLUM 4104 +#define IDC_BONE_WIDTH 4105 +#define IDC_BONE_WIDTHSPIN 4106 +#define IDC_BONE_HEIGHTSPIN 4107 +#define IDC_BONE_IKSOLVER 4108 +#define IDC_BONE_HEIGHT 4108 +#define IDC_BONE_TAPERSPIN 4109 +#define IDC_BONE_TAPER 4110 +#define IDC_BONE_SIDEFINS_SIZE 4111 +#define IDC_BONE_SIDEFINS_SIZESPIN 4112 +#define IDC_BONE_SIDEFINS_START_TAPER 4113 +#define IDC_BONE_SIDEFINS_START_TAPERSPIN 4114 +#define IDC_BONE_SIDEFINS_END_TAPER 4115 +#define IDC_BONE_SIDEFINS_END_TAPERSPIN 4116 +#define IDC_BONE_FRONTFIN_SIZE 4117 +#define IDC_BONE_FRONTFIN_START_TAPER 4118 +#define IDC_BONE_FRONTFIN_END_TAPER 4119 +#define IDC_BONE_FRONTFIN_SIZESPIN 4120 +#define IDC_BONE_FRONTFIN_START_TAPERSPIN 4121 +#define IDC_BONE_FRONTFIN_END_TAPERSPIN 4122 +#define IDC_BONE_BACKFIN_SIZE 4123 +#define IDC_BONE_BACKFIN_START_TAPER 4124 +#define IDC_BONE_BACKFIN_END_TAPER 4125 +#define IDC_BONE_BACKFIN_SIZESPIN 4126 +#define IDC_BONE_BACKFIN_START_TAPERSPIN 4127 +#define IDC_BONE_BACKFIN_END_TAPERSPIN 4128 +#define IDC_SHADBIAS 4130 +#define IDC_SHADBIAS_SPIN 4131 +#define IDC_SHADSIZE 4132 +#define IDC_SHADSIZE_SPIN 4133 +#define IDC_SHADSAMPLE_RANGE 4134 +#define IDC_SHADSAMPLE_RANGE_SPIN 4135 +#define IDC_ABSOLUTE_MAPBIAS 4136 +#define IDC_LIGHT_ATTEN 4137 +#define IDC_LIGHT_ATTEN_SPIN 4138 +#define IDC_WIDTH 4139 +#define IDC_ADD 4141 +#define IDC_MULT 4142 +#define IDC_MADD 4143 +#define IDC_ILLUMINATE 4144 +#define IDS_DS_CREATE 10008 +#define IDS_DB_FREE 10009 +#define IDS_DB_BONES 10010 +#define IDS_DB_BASIC 10011 +#define IDS_DB_RING_ARRAY 10012 +#define IDS_DB_SLAVECONTROL 10013 +#define IDS_DB_SLAVE_CONTROL 10014 +#define IDS_DB_TAPEHELPER 10015 +#define IDS_DB_BONES_CLASS 10016 +#define IDS_RB_TEAPOT_CLASS 10017 +#define IDS_TH_TEXT_CLASS 10018 +#define IDS_RB_TORUS_CLASS 10019 +#define IDS_TH_TRI_PATCH_CLASS 10020 +#define IDS_DB_DIRECTIONAL_CLASS 10021 +#define IDS_DS_TDIRECTIONAL_CLASS 10022 +#define IDS_DB_FREE_SPOT_CLASS 10023 +#define IDS_DB_SLAVECONTROL_CLASS 10024 +#define IDS_DB_TARGET_CLASS 10025 +#define IDS_DB_PROTRACTOR 10026 +#define IDS_DB_PROJDIR_CLASS 10026 +#define IDS_DB_PROTHELPER_CLASS 10027 +#define IDS_DB_PROT_CLASS 10028 +#define IDS_DS_RINGARRAYPAR 10037 +#define IDS_DS_RADIUS 10038 +#define IDS_DS_CYCLES 10039 +#define IDS_DS_AMPLITUDE 10040 +#define IDS_DS_PHASE 10041 +#define IDS_CT_SURF 10042 +#define IDS_CT_CAP 10043 +#define IDS_CT_SLICE 10044 +#define IDS_DS_EXCLUDE__ 10122 +#define IDS_DS_INCLUDE__ 10123 +#define IDS_DS_LIGHTCOL 10175 +#define IDS_DS_SPECCOL 10176 +#define IDS_DB_TDIST 20072 +#define IDS_DB_MULTIPLIER 20315 +#define IDS_DB_ATTENSTART 20316 +#define IDS_DB_ATTENEND 20317 +#define IDS_DB_HOTSIZE 20318 +#define IDS_DB_FALLSIZE 20319 +#define IDS_DB_COLOR 20320 +#define IDS_DB_ASPECT_RATIO 20323 +#define IDS_DB_TAPE_LENGTH 20326 +#define IDS_DB_NRANGE 20453 +#define IDS_DB_FRANGE 20454 +#define IDS_RB_OBJECT 30004 +#define IDS_RB_PARAMETERS 30028 +#define IDS_RB_ANGLE 30029 +#define IDS_RB_AMOUNT 30031 +#define IDS_RB_LENGTH 30038 +#define IDS_RB_WIDTH 30039 +#define IDS_RB_HEIGHT 30040 +#define IDS_RB_WSEGS 30041 +#define IDS_RB_HSEGS 30042 +#define IDS_RB_LSEGS 30043 +#define IDS_RB_FOV 30044 +#define IDS_RB_RADIUS 30045 +#define IDS_RB_SEGS 30046 +#define IDS_RB_SIDES 30047 +#define IDS_RB_SMOOTH 30048 +#define IDS_RB_RED 30049 +#define IDS_RB_GREEN 30050 +#define IDS_RB_BLUE 30051 +#define IDS_RB_HOTSIZE 30052 +#define IDS_RB_TRANSFORM 30053 +#define IDS_RB_BASEPARAMS 30054 +#define IDS_DB_TARGET 30055 +#define IDS_DB_DOT_TARGET 30056 +#define IDS_DB_OMNI 30057 +#define IDS_DB_TARGET_SPOT 30058 +#define IDS_DB_DIRECTIONAL 30059 +#define IDS_DB_FREE_SPOT 30060 +#define IDS_DB_NONE 30061 +#define IDS_RB_OBJ_MOD 30062 +#define IDS_DB_PROJDIR 30062 +#define IDS_RB_WORLD_MOD 30063 +#define IDS_DB_GENERAL_PARAMS 30064 +#define IDS_DB_DIR_PARAMS 30065 +#define IDS_DB_SPOT_PARAMS 30066 +#define IDS_DB_SHADOW_PARAMS 30067 +#define IDS_DB_SELECT_TO_FIT 30068 +#define IDS_DB_OMNI_LIGHT 30069 +#define IDS_DB_DIR_LIGHT 30070 +#define IDS_DB_DIRECT 30071 +#define IDS_DB_FSPOT 30072 +#define IDS_DB_SPOT 30073 +#define IDS_DB_LIGHT 30074 +#define IDS_DB_TAPE 30075 +#define IDS_DB_GENERAL 30076 +#define IDS_DB_TARGET_SPOT_CLASS 30077 +#define IDS_DB_TAPE_CLASS 30078 +#define IDS_LIGHT_ANIM 30079 +#define IDS_RB_EDITKEYINFO 30128 +#define IDS_DB_OMNI_CLASS 30129 +#define IDS_RB_MORPH_CLASS 30130 +#define IDS_RB_SNOW_CLASS 30131 +#define IDS_RB_GEOMETERIC 30157 +#define IDS_RB_SHAPES 30158 +#define IDS_RB_LIGHTS 30159 +#define IDS_RB_CAMERAS 30160 +#define IDS_RB_HELPERS 30161 +#define IDS_RB_SPACEWARPS 30162 +#define IDS_RB_SYSTEMS 30163 +#define IDS_DB_CAMERA 30164 +#define IDS_DB_SIMPLECAM 30165 +#define IDS_DB_SIMPLECAM_CLASS 30166 +#define IDS_RB_PICKOPERAND 30263 +#define IDS_RB_PRIMITIVES 30264 +#define IDS_PW_PRIMITIVES 30265 +#define IDS_PW_CREATIONMETHOD 30266 +#define IDS_PW_KEYBOARDENTRY 30267 +#define IDS_PW_PARAMETERS 30268 +#define IDS_PW_LENGTH 30269 +#define IDS_PW_WIDTH 30270 +#define IDS_PW_HEIGHT 30271 +#define IDS_PW_WSEGS 30272 +#define IDS_PW_LSEGS 30273 +#define IDS_PW_HSEGS 30274 +#define IDS_PW_SCALE 30275 +#define IDS_PW_RSEG 30276 +#define IDS_PW_MAPPING 30277 +#define IDS_PW_POS 30278 +#define IDS_PW_LEFTOVER 30279 +#define IDS_RB_BOOLEAN 30475 +#define IDS_RB_COMPOUNDOBJECTS 30476 +#define IDS_RB_INVALIDBOOLEAN 30477 +#define IDS_RB_OPERANDS 30478 +#define IDS_RB_PICKBOOLEAN 30479 +#define IDS_RB_OPERANDA 30480 +#define IDS_RB_OPERANDATRANSFORM 30481 +#define IDS_RB_OPERANDB 30482 +#define IDS_RB_OPERANDBTRANSFORM 30483 +#define IDS_RB_OPERAND 30484 +#define IDS_RB_CUBICMORPHCONTROL 30485 +#define IDS_RB_TARGETNUMBER 30486 +#define IDS_RB_MORPH 30487 +#define IDS_RB_MORPHOBJECT 30488 +#define IDS_RB_PICKTARGETS 30489 +#define IDS_RB_CURRENTTARGETS 30490 +#define IDS_RB_SPRAY 30491 +#define IDS_RB_SNOW 30492 +#define IDS_RB_PARTICLESYSTEMS 30493 +#define IDS_RB_VIEWPARTICLES 30494 +#define IDS_RB_DROPSIZE 30495 +#define IDS_RB_SPEED 30496 +#define IDS_RB_VARIATION 30497 +#define IDS_RB_STARTTIME 30498 +#define IDS_RB_LIFETIME 30499 +#define IDS_RB_BIRTHRATE 30500 +#define IDS_RB_TUMBLE 30501 +#define IDS_RB_TUMBLERATE 30502 +#define IDS_RB_SPHERE 30503 +#define IDS_RB_CREATIONMETHOD 30504 +#define IDS_RB_KEYBOARDENTRY 30505 +#define IDS_RB_HEMISPHERE 30506 +#define IDS_RB_BOX 30507 +#define IDS_RB_CYLINDER 30508 +#define IDS_RB_CIRCLESEGMENTS 30509 +#define IDS_RB_CAPSEGMENTS 30510 +#define IDS_RB_SLICEON 30511 +#define IDS_RB_SLICEFROM 30512 +#define IDS_RB_SLICETO 30513 +#define IDS_RB_GENTEXCOORDS 30514 +#define IDS_RB_CONE 30515 +#define IDS_RB_RADIUS1 30516 +#define IDS_RB_RADIUS2 30517 +#define IDS_RB_TEAPOT 30518 +#define IDS_DB_TARGET_CAM 30519 +#define IDS_RB_TORUS 30520 +#define IDS_RB_ROTATION2 30521 +#define IDS_RB_TWIST 30522 +#define IDS_RB_HEDRA 30523 +#define IDS_RB_FAMILY 30524 +#define IDS_RB_PVALUE 30525 +#define IDS_RB_QVALUE 30526 +#define IDS_RB_PSCALE 30527 +#define IDS_RB_QSCALE 30528 +#define IDS_RB_RSCALE 30529 +#define IDS_RB_TUBE 30530 +#define IDS_RB_SEGMENTS 30531 +#define IDS_DB_FREE_CAM 30532 +#define IDS_RB_BOOLEAN_CLASS 30533 +#define IDS_RB_BOX_CLASS 30534 +#define IDS_DB_FREE_CLASS 30535 +#define IDS_RB_MORPHOBJECT_CLASS 30536 +#define IDS_RB_CUBICMORPHCONTROL_CLASS 30537 +#define IDS_RB_SPHERE2 30543 +#define IDS_RB_CREATIONMETHOD2 30544 +#define IDS_RB_FROM 30545 +#define IDS_RB_TO 30546 +#define IDS_RB_KEYBOARDENTRY2 30547 +#define IDS_RB_HEMISPHERE2 30548 +#define IDS_RB_BOX2 30549 +#define IDS_RB_CYLINDER2 30550 +#define IDS_RB_CIRCLESEGMENTS2 30551 +#define IDS_RB_CREATIONMETHOD3 30552 +#define IDS_RB_KEYBOARDENTRY3 30553 +#define IDS_RB_CIRCLESEGMENTS3 30554 +#define IDS_RB_CREATIONMETHOD4 30555 +#define IDS_RB_BODY 30633 +#define IDS_RB_LID 30634 +#define IDS_RB_SPOUT 30635 +#define IDS_RB_HANDLE 30636 +#define IDS_RB_HEIGHTSEGS 30639 +#define IDS_RB_MAPSIZE 30640 +#define IDS_RB_MAPBIAS 30641 +#define IDS_RB_MAPRANGE 30642 +#define IDS_RB_RAYBIAS 30643 +#define IDS_RB_NEARPLANE 30644 +#define IDS_RB_FARPLANE 30645 +#define IDS_RB_PICKMORPHTARGET 30657 +#define IDS_RB_CREATEMORPHKEY 30658 +#define IDS_RB_BOOLEANABORTED 30661 +#define IDS_RB_BOOLEACOMPLETED 30662 +#define IDS_RB_FLAKESIZE 30676 +#define IDS_TH_RADIUS 31033 +#define IDS_TH_CIRCULAR 31034 +#define IDS_TH_SIDES 31035 +#define IDS_TH_RADIUS1 31036 +#define IDS_TH_RADIUS2 31037 +#define IDS_TH_PARAMETERS 31038 +#define IDS_TH_LINE 31079 +#define IDS_TH_FROM 31161 +#define IDS_TH_TO 31162 +#define IDS_TH_DISTORTION 31163 +#define IDS_TH_STARPOINTS 31164 +#define IDS_TH_TURNS 31165 +#define IDS_TH_BIAS 31166 +#define IDS_TH_NEEDTWOSPLINEPOINTS 31188 +#define IDS_TH_ERRORTWOCONSECUTIVE 31189 +#define IDS_TH_NGON 31259 +#define IDS_TH_SPLINES 31260 +#define IDS_TH_CREATION_METHOD 31261 +#define IDS_TH_KEYBOARD_ENTRY 31262 +#define IDS_TH_TRI_PATCH 31263 +#define IDS_TH_PATCH_GRIDS 31264 +#define IDS_TH_ARC 31265 +#define IDS_TH_CIRCLE 31266 +#define IDS_TH_RECTANGLE 31267 +#define IDS_TH_STAR 31268 +#define IDS_TH_DONUT 31269 +#define IDS_TH_TEXT 31270 +#define IDS_TH_HELIX 31271 +#define IDS_TH_ELLIPSE 31272 +#define IDS_TH_QUAD_PATCH 31273 +#define IDS_TH_MAX_TEXT 31274 +#define IDS_TH_SIZE 31275 +#define IDS_TH_INTERPOLATION 31276 +#define IDS_TH_ARC_CLASS 31277 +#define IDS_TH_HEIGHT 31278 +#define IDS_TH_CIRCLE_CLASS 31279 +#define IDS_TH_DIRECTION 31280 +#define IDS_TH_ARIAL 31281 +#define IDS_TH_QUADPATCH 31282 +#define IDS_TH_QUADPATCHOBJECT 31283 +#define IDS_TH_TRIPATCH 31284 +#define IDS_TH_TRIPATCHOBJECT 31285 +#define IDS_TH_HELIX_CLASS 31286 +#define IDS_RB_CONE_CLASS 31287 +#define IDS_RB_CYLINDER_CLASS 31288 +#define IDS_TH_DONUT_CLASS 31289 +#define IDS_TH_ELLIPSE_CLASS 31290 +#define IDS_RB_HEDRA_CLASS 31291 +#define IDS_DB_LIGHT_CLASS 31292 +#define IDS_TH_NGON_CLASS 31293 +#define IDS_TH_QUADPATCHOBJECT_CLASS 31294 +#define IDS_RB_SPRAY_CLASS 31295 +#define IDS_TH_RECTANGLE_CLASS 31296 +#define IDS_DB_RING_ARRAY_CLASS 31297 +#define IDS_RB_SPHERE_CLASS 31298 +#define IDS_TH_LINE_CLASS 31299 +#define IDS_TH_STAR_CLASS 31300 +#define IDS_DB_TAPEHELPER_CLASS 31301 +#define IDS_TH_TRIPATCHOBJECT_CLASS 31302 +#define IDS_RB_TUBE_CLASS 31303 +#define IDS_TH_QUAD_PATCH_CLASS 31304 +#define IDS_TH_SIMPSPLINE 31315 +#define IDS_DS_PARAMCHG 31316 +#define IDS_RB_DELETEMORPHTARG 31317 +#define IDS_DB_ORTHO_WIDTH 31318 +#define IDS_AP_EXTENDED 31319 +#define IDS_AP_FILLET 31320 +#define IDS_AP_FILLET1 31321 +#define IDS_AP_FILLET2 31322 +#define IDS_RB_BARYMORPHCONTROL 31323 +#define IDS_DS_TDIRECTIONAL 31324 +#define IDS_DS_TDIRECT 31325 +#define IDS_DS_TDIR_LIGHT 31326 +#define IDS_DS_ATTENSTARTNEAR 31327 +#define IDS_DS_ATTENENDNEAR 31328 +#define IDS_DS_PROJ_PARAMS 31329 +#define IDS_PROJ_PARAMS 31329 +#define IDS_RB_EXTRACTOP 31330 +#define IDS_RB_BONEPARAMS 31331 +#define IDS_DS_CONTRAST 31332 +#define IDS_SM_PATCH 31333 +#define IDS_PROJWIDTH 31333 +#define IDS_SM_EDITABLEMESH 31334 +#define IDS_PROJHEIGHT 31334 +#define IDS_SM_NURBS_SURFACE 31335 +#define IDS_PROJDEPTH 31335 +#define IDS_TH_LEADING 31336 +#define IDS_TH_KERNING 31337 +#define IDS_RB_BARYMORPHCONTROL_CLASS 31338 +#define IDS_TH_SCRIBE 31339 +#define IDS_DS_DIFFSOFT 31340 +#define IDS_RB_PRIMLIB 31341 +#define IDS_DS_SHADCOL 31342 +#define IDS_DS_SETTYPE 31343 +#define IDS_DS_SETCAMTYPE 31344 +#define IDS_AP_SLICEON 31345 +#define IDS_AP_SLICETO 31346 +#define IDS_AP_SLICEON3 31347 +#define IDS_AP_SLICEFROM 31347 +#define IDS_DECAY_NONE 31348 +#define IDS_DECAY_INVERSE 31349 +#define IDS_DECAY_INVSQ 31350 +#define IDS_ATTEN_PARAM 31351 +#define IDS_PRS_ORTHOLINE_CLASS 31352 +#define IDS_DECAY_RADIUS 31352 +#define IDS_ATMOS_OPACITY 31353 +#define IDS_ATMOS_COLAMT 31354 +#define IDS_PARRALLEL_CAMERA 31355 +#define IDS_PARALLEL_CAMERA_CLASS 31356 +#define IDS_PCAM_CUSTOM_GROUP 31357 +#define IDS_PCAM_STANDARD_GROUP 31358 +#define IDS_PCAM_AXIS_ANGLE_X 31359 +#define IDS_PCAM_AXIS_ANGLE_Y 31360 +#define IDS_PCAM_AXIS_ANGLE_Z 31361 +#define IDS_PCAM_HEIGHT 31362 +#define IDS_PCAM_WIDTH 31363 +#define IDS_PCAM_ASPECT 31364 +#define IDS_SECTION_UPDATE 31365 +#define IDS_LOCK_X2Z 31366 +#define IDS_LOCK_X2Y 31367 +#define IDS_LOCK_Y2Z 31368 +#define IDS_PCAM_FIX_HEIGHT 31369 +#define IDS_PCAM_FIX_WIDTH 31370 +#define IDS_PCAM_FIX_ASPECT 31371 +#define IDS_PARALLEL_CAMERA 31372 +#define IDS_DS_PROJMAP 31373 +#define IDS_DS_SHADPROJMAP 31374 +#define IDS_DS_SHAD_GEN 31375 +#define IDS_DS_SETSHADTYPE 31376 +#define IDS_PW_CV 31377 +#define IDS_SHAD_DENSITY 31379 +#define IDS_EMITTER 31380 +#define IDS_EMITTER_ENABLE 31381 +#define IDS_EMITTER_ENERGY 31382 +#define IDS_EMITTER_CA_PHOTONS 31383 +#define IDS_DOF 31384 +#define IDS_DOF_ENABLE 31385 +#define IDS_DOF_FSTOP 31386 +#define IDS_TH_VIZ_TEXT 31387 +#define IDS_EMITTER_GI_PHOTONS 31388 +#define IDS_PW_PLANE_ERROR 31389 +#define IDS_PW_PLANE_MEMORY_ERROR 31390 +#define IDS_BONE_WIDTH 31391 +#define IDS_BONE_HEIGHT 31392 +#define IDS_BONE_TAPER 31393 +#define IDS_BONE_LENGTH 31394 +#define IDS_BONE_SIDEFINS 31395 +#define IDS_BONE_SF_SIZE 31396 +#define IDS_BONE_SF_STARTTAPER 31397 +#define IDS_BONE_SF_ENDTAPER 31398 +#define IDS_BONE_FRONTFIN 31399 +#define IDS_BONE_FF_SIZE 31400 +#define IDS_BONE_FF_STARTTAPER 31401 +#define IDS_BONE_FF_ENDTAPER 31402 +#define IDS_BONE_BACKFIN 31403 +#define IDS_BONE_BF_SIZE 31404 +#define IDS_BONE_BF_STARTTAPER 31405 +#define IDS_BONE_BF_ENDTAPER 31406 +#define IDS_BONE_GENMAP 31407 +#define IDS_BONE_IK_ROLLUP_NAME 31408 +#define IDS_BONE_OBJ_NAME 31409 +#define IDS_BONE_IKSOLVER_HD 31410 +#define IDS_BONE_UNDO_DELETE_BONE 31411 +#define IDS_BONE_UNDO_ASSIGN_IK 31412 +#define IDS_MP_EFFECT 31413 +#define IDS_MP_EFFECT_ENABLE 31414 +#define IDS_MP_EFF_REND_EFF_PER_PASS 31416 +#define IDS_BONE_REFINE 31420 +#define IDS_DS_CAMPRESET 31421 +#define IDS_DS_MULTIPASS 31422 +#define IDS_HISOLVER 31423 +#define IDS_RTLIGHT_ON 31424 +#define IDS_LIGHT_GEN_PARAMS 31425 +#define IDS_LIGHT_ATTEN_PARAMSS 31426 +#define IDS_LIGHT_SPOT_PARAMS 31427 +#define IDS_DS_HUE 31428 +#define IDS_DS_SAT 31429 +#define IDS_DS_VAL 31430 +#define IDS_DS_DIFFON 31431 +#define IDS_DS_SPEC 31432 +#define IDS_DS_AMBI 31433 +#define IDS_DS_USENEAR 31434 +#define IDS_DS_SHOWNEARRANGES 31435 +#define IDS_DS_USEFARATTEN 31436 +#define IDS_DS_SHOWFARRANGES 31437 +#define IDS_DB_DECAY_STATE 31438 +#define IDS_DS_SHOWDECAY 31439 +#define IDS_DS_SHOWCONE 31440 +#define IDS_DS_OVERSHOOT 31441 +#define IDS_DS_LIGHTSHAPE_RATIO 31442 +#define IDS_DB_LIGHTTYPE 31443 +#define IDS_DS_RED 31444 +#define IDS_DS_GREEN 31445 +#define IDS_DS_BLUE 31446 +#define IDS_LIGHT_SHADOWS 31447 +#define IDS_DS_USESHADOWSS 31448 +#define IDS_DS_USESHADOWGLOBS 31449 +#define IDS_DS_SHADCOLORS 31450 +#define IDS_DS_DENSITYSHADOWS 31451 +#define IDS_DS_USESHADOWMAPS 31452 +#define IDS_DS_SHADOWMAPBUTTONS 31453 +#define IDS_DS_LIGHTEFFECTS_SHADS 31454 +#define IDS_DS_ATMOS_SHADS 31455 +#define IDS_DS_ATMOS_SHAD_OPACS 31456 +#define IDS_DS_ATMOS_SHAD_COLORS 31457 +#define IDS_LIGHT_SHADOWMAP_PARAMSS 31458 +#define IDS_DS_SHADOWMAP_BIASS 31459 +#define IDS_DS_SHADOWMAP_SIZES 31460 +#define IDS_DS_SHADOWMAP_SAMPLESS 31461 +#define IDS_DS_SHADOWMAP_ABSOLULTE_BIASS 31462 +#define IDS_DS_CASTSHADOWSS 31463 +#define IDS_DS_LIGHT_ATTEN_SLIDER 31464 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 4145 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/target.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/target.h new file mode 100644 index 00000000..6fba954f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaLights/target.h @@ -0,0 +1,108 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/********************************************************************** + *< + FILE: target.h + + DESCRIPTION: Defines a Target Object Class + + CREATED BY: Dan Silva + + HISTORY: created 11 January 1995 + + *> Copyright (c) 1994, All Rights Reserved. + **********************************************************************/ + +#ifndef __TARGET__H__ + +#define __TARGET__H__ + +class TargetObject: public GeomObject { + friend class TargetObjectCreateCallBack; + friend BOOL CALLBACK TargetParamDialogProc( HWND hDlg, UINT message, + WPARAM wParam, LPARAM lParam ); + + // Mesh cache + static HWND hSimpleCamParams; + static IObjParam* iObjParams; + static Mesh mesh; + static int meshBuilt; + + void GetMat(TimeValue t, INode* inode, ViewExp* vpt, Matrix3& tm); + void BuildMesh(); + + // inherited virtual methods for Reference-management + RefResult NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message ); + + public: + TargetObject(); + + // inherited virtual methods: + + // From BaseObject + int HitTest(TimeValue t, INode* inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt); + void Snap(TimeValue t, INode* inode, SnapInfo *snap, IPoint2 *p, ViewExp *vpt); + int Display(TimeValue t, INode* inode, ViewExp *vpt, int flags); + CreateMouseCallBack* GetCreateMouseCallBack(); + void BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev); + void EndEditParams( IObjParam *ip, ULONG flags,Animatable *next); + TCHAR *GetObjectName() { return GetString(IDS_DB_TARGET); } + + // From Object + ObjectState Eval(TimeValue time); + void InitNodeName(TSTR& s) { s = GetString(IDS_DB_TARGET); } + ObjectHandle ApplyTransform(Matrix3& matrix); + int UsesWireColor() { return 0; } + int IsRenderable() { return 0; } + + // From GeomObject + int IntersectRay(TimeValue t, Ray& r, float& at); + ObjectHandle CreateTriObjRep(TimeValue t); // for rendering, also for deformation + void GetWorldBoundBox(TimeValue t, INode *mat, ViewExp *vpt, Box3& box ); + void GetLocalBoundBox(TimeValue t, INode *mat, ViewExp *vpt, Box3& box ); + void GetDeformBBox(TimeValue t, Box3& box, Matrix3 *tm, BOOL useSel ); + + // From Animatable + void DeleteThis() { + delete this; + } + Class_ID ClassID() { return Class_ID(TARGET_CLASS_ID,0); } + void GetClassName(TSTR& s) { s = TSTR(GetString(IDS_DB_TARGET_CLASS)); } + int IsKeyable(){ return 1;} + LRESULT CALLBACK TrackViewWinProc( HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam ){return(0);} + + // From ref.h + RefTargetHandle Clone(RemapDir& remap = NoRemap()); + + // IO + IOResult Save(ISave *isave); + IOResult Load(ILoad *iload); + }; + + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plAngleAttenLayer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plAngleAttenLayer.cpp new file mode 100644 index 00000000..dd0f90f2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plAngleAttenLayer.cpp @@ -0,0 +1,492 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plAngleAttenLayer.h" + +#include "iparamb2.h" +#include "iparamm2.h" +#include "stdmat.h" + +#include "plBMSampler.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +class plAngleAttenLayerClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading = FALSE) { return TRACKED_NEW plAngleAttenLayer(); } + const TCHAR* ClassName() { return GetString(IDS_ANGLE_ATTEN_LAYER); } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + Class_ID ClassID() { return ANGLE_ATTEN_LAYER_CLASS_ID; } + const TCHAR* Category() { return TEXMAP_CAT_COLMOD; } + const TCHAR* InternalName() { return _T("PlasmaAngleAttenLayer"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plAngleAttenLayerClassDesc plAngleAttenLayerDesc; +ClassDesc2* GetAngleAttenLayerDesc() { return &plAngleAttenLayerDesc; } + +/////////////////////////////////////////////////////////////////////////////// +//// ParamBlock Definition //////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const float kDefTransp0 = 60.f; +static const float kDefOpaque0 = 90.f; +static const float kDefTransp1 = 30.f; +static const float kDefOpaque1 = 0.f; + +static ParamBlockDesc2 gAngleAttenParamBlk +( + plAngleAttenLayer::kBlkAngles, _T("angles"), 0, GetAngleAttenLayerDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI, plAngleAttenLayer::kRefAngles, + + IDD_ANGLE_ATTEN_LAYER, IDS_ANGLE_ATTEN_LAYER_PROPS, 0, 0, nil, + + // Texture size + plAngleAttenLayer::kTranspAngle0, _T("transp0"), TYPE_FLOAT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_TRANSP_ANGLE_0, IDC_TRANSP_ANGLE_0_SPIN, SPIN_AUTOSCALE, + p_range, 0.0, 180.0, + p_default, kDefTransp0, + end, + + plAngleAttenLayer::kOpaqueAngle0, _T("opaque0"), TYPE_FLOAT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_OPAQUE_ANGLE_0, IDC_OPAQUE_ANGLE_0_SPIN, SPIN_AUTOSCALE, + p_range, 0.0, 180.0, + p_default, kDefOpaque0, + end, + + plAngleAttenLayer::kDoubleFade, _T("doubleFade"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_DOUBLE_FADE, + p_enable_ctrls, 2, plAngleAttenLayer::kOpaqueAngle1, plAngleAttenLayer::kTranspAngle1, + p_default, false, + end, + + plAngleAttenLayer::kOpaqueAngle1, _T("opaque1"), TYPE_FLOAT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_OPAQUE_ANGLE_1, IDC_OPAQUE_ANGLE_1_SPIN, SPIN_AUTOSCALE, + p_range, 0.0, 180.0, + p_default, kDefTransp1, + end, + + plAngleAttenLayer::kTranspAngle1, _T("transp1"), TYPE_FLOAT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_TRANSP_ANGLE_1, IDC_TRANSP_ANGLE_1_SPIN, SPIN_AUTOSCALE, + p_range, 0.0, 180.0, + p_default, kDefOpaque1, + end, + + plAngleAttenLayer::kReflect, _T("reflect"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_REFLECT, + p_default, false, + end, + + plAngleAttenLayer::kLoClamp, _T("loClamp"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_LO_CLAMP, IDC_LO_CLAMP_SPIN, SPIN_AUTOSCALE, + p_range, 0, 100, + p_default, 0, + end, + + plAngleAttenLayer::kHiClamp, _T("hiClamp"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_HI_CLAMP, IDC_HI_CLAMP_SPIN, SPIN_AUTOSCALE, + p_range, 0, 100, + p_default, 100, + end, + + end +); + +plAngleAttenLayer::plAngleAttenLayer() : + fParmsPB(NULL), + fIValid(NEVER), + fCosTransp0(0), + fCosOpaque0(0), + fCosTransp1(0), + fCosOpaque1(0), + fCosinesCached(false) +{ + plAngleAttenLayerDesc.MakeAutoParamBlocks(this); +} + +plAngleAttenLayer::~plAngleAttenLayer() +{ +} + +//From MtlBase +void plAngleAttenLayer::Reset() +{ + GetAngleAttenLayerDesc()->Reset(this, TRUE); // reset all pb2's + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); + + fIValid.SetEmpty(); +} + +void plAngleAttenLayer::Update(TimeValue t, Interval& valid) +{ + if (!fIValid.InInterval(t)) + { + fIValid.SetInfinite(); + + } + + valid &= fIValid; +} + +Interval plAngleAttenLayer::Validity(TimeValue t) +{ + //TODO: Update fIValid here + + Interval v = FOREVER; + return v; +} + +ParamDlg* plAngleAttenLayer::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + IAutoMParamDlg* masterDlg = plAngleAttenLayerDesc.CreateParamDlgs(hwMtlEdit, imp, this); + + return masterDlg; +} + +BOOL plAngleAttenLayer::SetDlgThing(ParamDlg* dlg) +{ + return FALSE; +} + +int plAngleAttenLayer::NumRefs() +{ + return 1; +} + +//From ReferenceMaker +RefTargetHandle plAngleAttenLayer::GetReference(int i) +{ + switch (i) + { + case kRefAngles: return fParmsPB; + default: return NULL; + } +} + +void plAngleAttenLayer::SetReference(int i, RefTargetHandle rtarg) +{ + Interval garbage; + + switch (i) + { + case kRefAngles: + fParmsPB = (IParamBlock2 *)rtarg; + break; + } +} + +int plAngleAttenLayer::NumParamBlocks() +{ + return 1; +} + +IParamBlock2* plAngleAttenLayer::GetParamBlock(int i) +{ + switch (i) + { + case 0: return fParmsPB; + default: return NULL; + } +} + +IParamBlock2* plAngleAttenLayer::GetParamBlockByID(BlockID id) +{ + if (fParmsPB->ID() == id) + return fParmsPB; + else + return NULL; +} + +//From ReferenceTarget +RefTargetHandle plAngleAttenLayer::Clone(RemapDir &remap) +{ + plAngleAttenLayer *mnew = TRACKED_NEW plAngleAttenLayer(); + *((MtlBase*)mnew) = *((MtlBase*)this); // copy superclass stuff + mnew->ReplaceReference(kRefAngles, remap.CloneRef(fParmsPB)); + BaseClone(this, mnew, remap); + return (RefTargetHandle)mnew; +} + +int plAngleAttenLayer::NumSubs() +{ + return 1; +} + +Animatable* plAngleAttenLayer::SubAnim(int i) +{ + //TODO: Return 'i-th' sub-anim + switch (i) + { + case kRefAngles: return fParmsPB; + default: return NULL; + } +} + +TSTR plAngleAttenLayer::SubAnimName(int i) +{ + switch (i) + { + case kRefAngles: return "Angles"; + default: return ""; + } +} + +RefResult plAngleAttenLayer::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message) +{ + switch (message) + { + case REFMSG_CHANGE: + { + fIValid.SetEmpty(); + + if (hTarget == fParmsPB) + { + // see if this message came from a changing parameter in the pblock, + // if so, limit rollout update to the changing item + ParamID changingParam = fParmsPB->LastNotifyParamID(); + fParmsPB->GetDesc()->InvalidateUI(changingParam); + + if (changingParam != -1) + IChanged(); + } + } + break; + + } + + return REF_SUCCEED; +} + +void plAngleAttenLayer::IChanged() +{ + // Cut and paste insanity from DynamicTextLayer. + // Texture wasn't getting updated in the viewports, and this fixes it. + // Don't know if it's the right way though. + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); + + // And this is so the SceneWatcher gets notified that the material on some of it's + // referenced objects changed. + NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_MAT); + + ICacheCosines(); +} + +void plAngleAttenLayer::ICacheCosines() +{ + fCosTransp0 = cosf(DegToRad(fParmsPB->GetFloat(kTranspAngle0))); + fCosOpaque0 = cosf(DegToRad(fParmsPB->GetFloat(kOpaqueAngle0))); + + if( fParmsPB->GetInt(kDoubleFade) ) + { + fCosTransp1 = cosf(DegToRad(fParmsPB->GetFloat(kTranspAngle1))); + fCosOpaque1 = cosf(DegToRad(fParmsPB->GetFloat(kOpaqueAngle1))); + } + else + { + fCosTransp1 = fCosOpaque1 = 0; + } + fCosinesCached = true; +} + +#define TEX_HDR_CHUNK 0x5000 + +IOResult plAngleAttenLayer::Save(ISave *isave) +{ + IOResult res; + + isave->BeginChunk(TEX_HDR_CHUNK); + res = MtlBase::Save(isave); + if (res != IO_OK) + return res; + isave->EndChunk(); + + return IO_OK; +} + +IOResult plAngleAttenLayer::Load(ILoad *iload) +{ + IOResult res; + while (IO_OK == (res = iload->OpenChunk())) + { + if (iload->CurChunkID() == TEX_HDR_CHUNK) + { + res = MtlBase::Load(iload); + } + iload->CloseChunk(); + if (res != IO_OK) + return res; + } + + return IO_OK; +} + + +AColor plAngleAttenLayer::EvalColor(ShadeContext& sc) +{ + if( !sc.doMaps ) + return AColor(0.0f, 0.0f, 0.0f, 1.0f); + + AColor color; + if (sc.GetCache(this, color)) + return color; + + if( !fCosinesCached ) + ICacheCosines(); + + if (gbufID) + sc.SetGBufferID(gbufID); + + // Evaluate the Bitmap + + Point3 normal = sc.Normal(); + + if( fParmsPB->GetInt(kReflect) ) + { + normal = sc.ReflectVector(); + } + float dotZ = normal.z; + + float alpha = 1.f; + if( fCosTransp0 != fCosOpaque0 ) + { + float a = (dotZ - fCosTransp0) / (fCosOpaque0 - fCosTransp0); + if( a < 0 ) + a = 0; + else if( a > 1.f ) + a = 1.f; + alpha *= a; + } + if( fParmsPB->GetInt(kDoubleFade) && (fCosTransp1 != fCosOpaque1) ) + { + float a = (dotZ - fCosTransp1) / (fCosOpaque1 - fCosTransp1); + if( a < 0 ) + a = 0; + else if( a > 1.f ) + a = 1.f; + if( fCosTransp0 < fCosTransp1 ) + { + if( fCosTransp0 > fCosOpaque0 ) + alpha += a; + else + alpha *= a; + } + else + { + if( fCosTransp0 < fCosOpaque0 ) + alpha += a; + else + alpha *= a; + } + } + color = AColor(1.f, 1.f, 1.f, alpha); + + sc.PutCache(this, color); + return color; +} + +float plAngleAttenLayer::EvalMono(ShadeContext& sc) +{ + return Intens(EvalColor(sc)); +} + +Point3 plAngleAttenLayer::EvalNormalPerturb(ShadeContext& sc) +{ + // Return the perturbation to apply to a normal for bump mapping + return Point3(0, 0, 0); +} + +ULONG plAngleAttenLayer::LocalRequirements(int subMtlNum) +{ + return MTLREQ_VIEW_DEP | MTLREQ_TRANSP; +} + +void plAngleAttenLayer::ActivateTexDisplay(BOOL onoff) +{ +} + +BITMAPINFO *plAngleAttenLayer::GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono, BOOL forceW, BOOL forceH) +{ + return nil; + // FIXME +} + +DWORD plAngleAttenLayer::GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker) +{ + return 0; +} + +const char *plAngleAttenLayer::GetTextureName( int which ) +{ + return NULL; +} + +int plAngleAttenLayer::GetLoClamp() +{ + return fParmsPB->GetInt(kLoClamp); +} + +int plAngleAttenLayer::GetHiClamp() +{ + return fParmsPB->GetInt(kHiClamp); +} + +Box3 plAngleAttenLayer::GetFade() +{ + Point3 pmin, pmax; + + pmin.x = fParmsPB->GetFloat(kTranspAngle0); + pmin.y = fParmsPB->GetFloat(kOpaqueAngle0); + if( pmin.x < pmin.y ) + pmin.z = -1.f; + else if( pmin.x > pmin.y ) + pmin.z = 1.f; + else + pmin.z = 0; + + if( fParmsPB->GetInt(kDoubleFade) ) + { + pmax.x = fParmsPB->GetFloat(kTranspAngle1); + pmax.y = fParmsPB->GetFloat(kOpaqueAngle1); + if( pmax.x < pmax.y ) + pmax.z = -1.f; + else if( pmax.x > pmax.y ) + pmax.z = 1.f; + else + pmax.z = 0; + } + else + { + pmax.x = pmax.y = pmax.z = 0; + } + return Box3(pmin, pmax); + +} + +BOOL plAngleAttenLayer::Reflect() +{ + return fParmsPB->GetInt(kReflect); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plAngleAttenLayer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plAngleAttenLayer.h new file mode 100644 index 00000000..ff1e50db --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plAngleAttenLayer.h @@ -0,0 +1,165 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plAngleAttenLayer_inc +#define plAngleAttenLayer_inc + +#include "Max.h" +#include "../resource.h" +#include "plPlasmaMAXLayer.h" + +class ClassDesc2; +class IParamBlock2; + +ClassDesc2* GetAngleAttenLayerDesc(); + +extern TCHAR *GetString(int id); +extern HINSTANCE hInstance; + + +//// Class Definition ///////////////////////////////////////////////////////// + +class plAngleAttenLayer : public plPlasmaMAXLayer +{ +protected: + // Parameter block + IParamBlock2* fParmsPB; + + Interval fIValid; + + BOOL fCosinesCached; + float fCosTransp0; + float fCosOpaque0; + float fCosTransp1; + float fCosOpaque1; + +public: + // Ref nums + enum + { + kRefAngles + }; + + // Block ID's + enum + { + kBlkAngles + }; + + plAngleAttenLayer(); + ~plAngleAttenLayer(); + void DeleteThis() { delete this; } + + //From MtlBase + ParamDlg* CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + BOOL SetDlgThing(ParamDlg* dlg); + void Update(TimeValue t, Interval& valid); + void Reset(); + Interval Validity(TimeValue t); + ULONG LocalRequirements(int subMtlNum); + + //From Texmap + RGBA EvalColor(ShadeContext& sc); + float EvalMono(ShadeContext& sc); + Point3 EvalNormalPerturb(ShadeContext& sc); + + // For displaying textures in the viewport + BOOL SupportTexDisplay() { return FALSE; } + void ActivateTexDisplay(BOOL onoff); + BITMAPINFO *GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono=FALSE, int forceW=0, int forceH=0); + DWORD GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker); +protected: + void ICacheCosines(); + void IChanged(); + void IDiscardTexHandle(); + +public: + + //TODO: Return anim index to reference index + int SubNumToRefNum(int subNum) { return subNum; } + + virtual BOOL DiscardColor() { return true; } + + // Loading/Saving + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + //From Animatable + Class_ID ClassID() { return ANGLE_ATTEN_LAYER_CLASS_ID; } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + void GetClassName(TSTR& s) { s = GetString(IDS_ANGLE_ATTEN_LAYER); } + + RefTargetHandle Clone( RemapDir &remap ); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + // TODO: Maintain the number or references here + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + + int NumParamBlocks(); // return number of ParamBlocks in this instance + IParamBlock2* GetParamBlock(int i); // return i'th ParamBlock + IParamBlock2* GetParamBlockByID(BlockID id); // return id'd ParamBlock + + const char *GetTextureName( int which ); + + + /// ParamBlock accessors + enum + { + kTranspAngle0, + kOpaqueAngle0, + kOpaqueAngle1, + kTranspAngle1, + kDoubleFade, + kReflect, + kLoClamp, + kHiClamp + }; + + + // Pure virtual accessors for the various bitmap related elements + virtual Bitmap *GetMaxBitmap(int index = 0) { hsAssert( false, "Function call not valid on this type of layer." ); return nil; } + virtual PBBitmap *GetPBBitmap( int index = 0 ) { hsAssert( false, "Function call not valid on this type of layer." ); return nil; } + virtual int GetNumBitmaps( void ) { return 0; } + + // Some specific to processing this layer type into runtime materials. + virtual Box3 GetFade(); + virtual BOOL Reflect(); + virtual int GetLoClamp(); + virtual int GetHiClamp(); + +protected: + virtual void ISetMaxBitmap(Bitmap *bitmap, int index = 0) { hsAssert( false, "Function call not valid on this type of layer." ); } + virtual void ISetPBBitmap( PBBitmap *pbbm, int index = 0 ){ hsAssert( false, "Function call not valid on this type of layer." ); } +}; + +#endif // plAngleAttenLayer_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayer.cpp new file mode 100644 index 00000000..5f0c95d9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayer.cpp @@ -0,0 +1,402 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDynamicEnvLayer - Dynamic EnvironmentMap MAX Layer // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.22.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plDynamicEnvLayer.h" + +#include "iparamb2.h" +#include "iparamm2.h" +#include "stdmat.h" +#include "resource.h" + +#include "plBMSampler.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + + +//// Externs ////////////////////////////////////////////////////////////////// + +extern TCHAR *GetString( int id ); +extern HINSTANCE hInstance; + +//// ClassDesc Definition ///////////////////////////////////////////////////// + +class plDynamicEnvLayerClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading = FALSE) { return TRACKED_NEW plDynamicEnvLayer(); } + const TCHAR* ClassName() { return GetString(IDS_DYNAMIC_ENVMAP_LAYER); } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + Class_ID ClassID() { return DYNAMIC_ENV_LAYER_CLASS_ID; } + const TCHAR* Category() { return TEXMAP_CAT_ENV; } + const TCHAR* InternalName() { return _T("PlasmaDynamicEnvMapLayer"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plDynamicEnvLayerClassDesc plDynamicEnvLayerDesc; +ClassDesc2* GetDynamicEnvLayerDesc() { return &plDynamicEnvLayerDesc; } + +#include "plDynamicEnvLayerBitmapPB.cpp" + +//// Constructor/Destructor /////////////////////////////////////////////////// + +plDynamicEnvLayer::plDynamicEnvLayer() : + fBitmapPB(NULL), + fUVGen(NULL), + fTexHandle(NULL), + fTexTime(0), + fIValid(NEVER) +{ + plDynamicEnvLayerDesc.MakeAutoParamBlocks(this); + ReplaceReference(kRefUVGen, GetNewDefaultUVGen()); +} + +plDynamicEnvLayer::~plDynamicEnvLayer() +{ + IDiscardTexHandle(); +} + +void plDynamicEnvLayer::GetClassName( TSTR& s ) +{ + s = GetString( IDS_DYNAMIC_ENVMAP_LAYER ); +} + +//// Reset //////////////////////////////////////////////////////////////////// + +void plDynamicEnvLayer::Reset() +{ + GetDynamicEnvLayerDesc()->Reset(this, TRUE); // reset all pb2's + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); + + fIValid.SetEmpty(); +} + +//// Update /////////////////////////////////////////////////////////////////// + +void plDynamicEnvLayer::Update(TimeValue t, Interval& valid) +{ + if (!fIValid.InInterval(t)) + { + fIValid.SetInfinite(); + + fUVGen->Update(t,fIValid); + fBitmapPB->GetValidity(t, fIValid); + } + + valid &= fIValid; +} + +//// Validity ///////////////////////////////////////////////////////////////// + +Interval plDynamicEnvLayer::Validity(TimeValue t) +{ + //TODO: Update fIValid here + + // mf horse - Hacking this in just to get animations working. + // No warranty on this not being stupid. + Interval v = FOREVER; + fBitmapPB->GetValidity(t, v); + v &= fUVGen->Validity(t); + return v; +} + +//// CreateParamDlg /////////////////////////////////////////////////////////// + +ParamDlg* plDynamicEnvLayer::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + fIMtlParams = imp; + IAutoMParamDlg* masterDlg = plDynamicEnvLayerDesc.CreateParamDlgs(hwMtlEdit, imp, this); + + return masterDlg; +} + +//// SetDlgThing ////////////////////////////////////////////////////////////// + +BOOL plDynamicEnvLayer::SetDlgThing(ParamDlg* dlg) +{ + return FALSE; +} + +//// Reference Functions ////////////////////////////////////////////////////// + +int plDynamicEnvLayer::NumRefs() +{ + return 2; +} + +RefTargetHandle plDynamicEnvLayer::GetReference( int i ) +{ + switch( i ) + { + case kRefUVGen: return fUVGen; + case kRefBitmap: return fBitmapPB; + default: return NULL; + } +} + +void plDynamicEnvLayer::SetReference( int i, RefTargetHandle rtarg ) +{ + Interval garbage; + + switch( i ) + { + case kRefUVGen: + fUVGen = (UVGen *)rtarg; + if( fUVGen ) + fUVGen->Update( TimeValue( 0 ), garbage ); + break; + case kRefBitmap: + fBitmapPB = (IParamBlock2 *)rtarg; + break; + } +} + +//// ParamBlock Functions ///////////////////////////////////////////////////// + +int plDynamicEnvLayer::NumParamBlocks() +{ + return 1; +} + +IParamBlock2 *plDynamicEnvLayer::GetParamBlock( int i ) +{ + switch( i ) + { + case 0: return fBitmapPB; + default: return NULL; + } +} + +IParamBlock2 *plDynamicEnvLayer::GetParamBlockByID( BlockID id ) +{ + if( fBitmapPB->ID() == id ) + return fBitmapPB; + else + return NULL; +} + +//// Clone //////////////////////////////////////////////////////////////////// + +RefTargetHandle plDynamicEnvLayer::Clone( RemapDir &remap ) +{ + plDynamicEnvLayer *mnew = TRACKED_NEW plDynamicEnvLayer(); + *((MtlBase*)mnew) = *((MtlBase*)this); // copy superclass stuff + mnew->ReplaceReference(kRefBitmap, remap.CloneRef(fBitmapPB)); + mnew->ReplaceReference(kRefUVGen, remap.CloneRef(fUVGen)); + BaseClone(this, mnew, remap); + return (RefTargetHandle)mnew; +} + +//// SubAnim Functions //////////////////////////////////////////////////////// + +int plDynamicEnvLayer::NumSubs() +{ + return 1; +} + +Animatable *plDynamicEnvLayer::SubAnim( int i ) +{ + switch( i ) + { + case kRefBitmap: return fBitmapPB; + default: return NULL; + } +} + +TSTR plDynamicEnvLayer::SubAnimName( int i ) +{ + switch( i ) + { + case kRefBitmap: return fBitmapPB->GetLocalName(); + default: return ""; + } +} + +//// NotifyRefChanged ///////////////////////////////////////////////////////// + +RefResult plDynamicEnvLayer::NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message ) +{ + switch (message) + { + case REFMSG_CHANGE: + { + fIValid.SetEmpty(); + + if (hTarget == fBitmapPB) + { + // see if this message came from a changing parameter in the pblock, + // if so, limit rollout update to the changing item and update any active viewport texture + ParamID changingParam = fBitmapPB->LastNotifyParamID(); + fBitmapPB->GetDesc()->InvalidateUI(changingParam); + } + } + break; + + case REFMSG_UV_SYM_CHANGE: + IDiscardTexHandle(); + break; + } + + return REF_SUCCEED; +} + +//// Save/Load //////////////////////////////////////////////////////////////// + +#define TEX_HDR_CHUNK 0x5000 + +IOResult plDynamicEnvLayer::Save(ISave *isave) +{ + IOResult res; + + isave->BeginChunk(TEX_HDR_CHUNK); + res = MtlBase::Save(isave); + if (res != IO_OK) + return res; + isave->EndChunk(); + + return IO_OK; +} + +IOResult plDynamicEnvLayer::Load(ILoad *iload) +{ + IOResult res; + while (IO_OK == (res = iload->OpenChunk())) + { + if (iload->CurChunkID() == TEX_HDR_CHUNK) + { + res = MtlBase::Load(iload); + } + iload->CloseChunk(); + if (res != IO_OK) + return res; + } + + return IO_OK; +} + +//// EvalColor //////////////////////////////////////////////////////////////// + +inline Point2 CompUV(float x, float y, float z) +{ + return Point2( 0.5f * ( x / z + 1.0f ), 0.5f * ( y / z + 1.0f ) ); +} + +AColor plDynamicEnvLayer::EvalColor(ShadeContext& sc) +{ + if (!sc.doMaps) + return AColor(0.0f, 0.0f, 0.0f, 1.0f); + + AColor color; + if (sc.GetCache(this, color)) + return color; + + if (gbufID) + sc.SetGBufferID(gbufID); + + color.White(); + + sc.PutCache(this, color); + return color; +} + +float plDynamicEnvLayer::EvalMono(ShadeContext& sc) +{ + return Intens(EvalColor(sc)); +} + +Point3 plDynamicEnvLayer::EvalNormalPerturb(ShadeContext& sc) +{ + // Return the perturbation to apply to a normal for bump mapping + return Point3(0, 0, 0); +} + +ULONG plDynamicEnvLayer::LocalRequirements(int subMtlNum) +{ + return MTLREQ_VIEW_DEP; +} + +void plDynamicEnvLayer::IDiscardTexHandle() +{ + if (fTexHandle) + { + fTexHandle->DeleteThis(); + fTexHandle = NULL; + } +} + +void plDynamicEnvLayer::ActivateTexDisplay(BOOL onoff) +{ + if (!onoff) + IDiscardTexHandle(); +} + +BITMAPINFO *plDynamicEnvLayer::GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono, BOOL forceW, BOOL forceH) +{ + return NULL; +} + +DWORD plDynamicEnvLayer::GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker) +{ + // FIXME: ignore validity for now + if (fTexHandle && fIValid.InInterval(t))// && texTime == CalcFrame(t)) + return fTexHandle->GetHandle(); + else + { + IDiscardTexHandle(); + + fTexTime = 0;//CalcFrame(t); + fTexHandle = thmaker.MakeHandle(GetVPDisplayDIB(t, thmaker, fIValid)); + if (fTexHandle) + return fTexHandle->GetHandle(); + else + return 0; + } +} + +//// MustBeUnique ///////////////////////////////////////////////////////////// +// Fun stuff here. If our anchor is set to nil (i.e. "self"), then we must be +// unique for each object we're applied to. However, that means the material +// must *ALSO* be unique. Hence why this function is called by +// hsMaterialConverter::IMustBeUniqueMaterial(). + +bool plDynamicEnvLayer::MustBeUnique( void ) +{ + if( fBitmapPB->GetINode( kBmpAnchorNode ) == nil ) + return true; + + return false; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayer.h new file mode 100644 index 00000000..4773b26b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayer.h @@ -0,0 +1,168 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDynamicEnvLayer - Dynamic EnvironmentMap MAX Layer // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.22.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDynamicEnvLayer_h +#define _plDynamicEnvLayer_h + +#include "Max.h" +#include "plPlasmaMAXLayer.h" + +class ClassDesc2; +class IParamBlock2; + +//// Class Definition ///////////////////////////////////////////////////////// + +class plDynamicEnvLayer : public plPlasmaMAXLayer +{ +protected: + // Parameter block + IParamBlock2 *fBitmapPB; + UVGen *fUVGen; + + IMtlParams *fIMtlParams; + + TexHandle *fTexHandle; + TimeValue fTexTime; + + Interval fIValid; + + friend class DELBitmapDlgProc; + +public: + // Ref nums + enum + { + kRefUVGen, + kRefBitmap, + }; + + // Block ID's + enum + { + kBlkBitmap, + }; + + plDynamicEnvLayer(); + ~plDynamicEnvLayer(); + void DeleteThis() { delete this; } + + //From MtlBase + ParamDlg *CreateParamDlg( HWND hwMtlEdit, IMtlParams *imp ); + BOOL SetDlgThing( ParamDlg* dlg ); + void Update( TimeValue t, Interval& valid ); + void Reset(); + Interval Validity( TimeValue t ); + ULONG LocalRequirements( int subMtlNum ); + + //From Texmap + RGBA EvalColor( ShadeContext& sc ); + float EvalMono( ShadeContext& sc ); + Point3 EvalNormalPerturb( ShadeContext& sc ); + + // For displaying textures in the viewport + BOOL SupportTexDisplay() { return TRUE; } + void ActivateTexDisplay(BOOL onoff); + BITMAPINFO *GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono=FALSE, int forceW=0, int forceH=0); + DWORD GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker); + + virtual bool MustBeUnique( void ); + +protected: + void IDiscardTexHandle(); + +public: + void GetUVTransform(Matrix3 &uvtrans) { fUVGen->GetUVTransform(uvtrans); } + int GetTextureTiling() { return fUVGen->GetTextureTiling(); } + int GetUVWSource() { return fUVGen->GetUVWSource(); } + virtual int GetMapChannel() { return fUVGen->GetMapChannel(); } // only relevant if above returns UVWSRC_EXPLICIT + UVGen *GetTheUVGen() { return fUVGen; } + + //TODO: Return anim index to reference index + int SubNumToRefNum( int subNum ) { return kRefBitmap; /* Only one sub*/ } + + // Loading/Saving + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + //From Animatable + Class_ID ClassID() { return DYNAMIC_ENV_LAYER_CLASS_ID; } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + void GetClassName( TSTR& s ); + + RefTargetHandle Clone( RemapDir &remap ); + RefResult NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message ); + + int NumSubs(); + Animatable *SubAnim(int i); + TSTR SubAnimName(int i); + + int NumRefs(); + RefTargetHandle GetReference( int i ); + void SetReference( int i, RefTargetHandle rtarg ); + + int NumParamBlocks(); // return number of ParamBlocks in this instance + IParamBlock2 *GetParamBlock(int i); // return i'th ParamBlock + IParamBlock2 *GetParamBlockByID(BlockID id); // return id'd ParamBlock + + /// ParamBlock accessors + enum + { + kScalingAny, + kScalingHalf, + kScalingNone + }; + + // Param ID's + enum + { + // General params + kBmpTextureSize, + kBmpAnchorNode, + kBmpLastTextureSize, // Annoying, but necessary to clamp texture sizes to powers of 2 + kBmpRefract + }; + + // Pure virtual accessors for the various bitmap related elements + virtual Bitmap *GetMaxBitmap(int index = 0) { hsAssert( false, "Function call not valid on this type of layer." ); return nil; } + virtual PBBitmap *GetPBBitmap( int index = 0 ) { hsAssert( false, "Function call not valid on this type of layer." ); return nil; } + virtual int GetNumBitmaps( void ) { return 0; } + + protected: + virtual void ISetMaxBitmap(Bitmap *bitmap, int index = 0) { hsAssert( false, "Function call not valid on this type of layer." ); } + virtual void ISetPBBitmap( PBBitmap *pbbm, int index = 0 ){ hsAssert( false, "Function call not valid on this type of layer." ); } +}; + +#endif // _plDynamicEnvLayer_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayerBitmapPB.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayerBitmapPB.cpp new file mode 100644 index 00000000..4dd91647 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicEnvLayerBitmapPB.cpp @@ -0,0 +1,250 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDynamicEnvLayer ParamBlock Functions // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.22.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +//// PickAnchorNode /////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class PickAnchorNode : public PickObjectProc +{ + public: + plDynamicEnvLayer *fLayer; + HWND fHWnd; + int bleah; + + PickAnchorNode() { fLayer = NULL; } + + BOOL Pick( INode *node ) + { + const char *dbgNodeName = node->GetName(); + + if( fLayer ) + fLayer->GetParamBlockByID( plDynamicEnvLayer::kBlkBitmap )->SetValue( plDynamicEnvLayer::kBmpAnchorNode, TimeValue( 0 ), node ); + + return TRUE; + } + + void EnterMode() { ISetButton( TRUE ); } + void ExitMode() { ISetButton( FALSE ); } + + BOOL Filter( INode *node ) + { + Object *obj = node->EvalWorldState( 0 ).obj; + if( obj != NULL ) + { + if( obj->CanConvertToType( triObjectClassID ) || + obj->ClassID() == Class_ID( DUMMY_CLASS_ID, 0 ) ) + return TRUE; + } + return FALSE; + } + + protected: + + void ISetButton( BOOL checkIt ) + { + ICustButton *iBut = GetICustButton( GetDlgItem( fHWnd, IDC_ANCHOR_NODE ) ); + if( iBut ) + { + iBut->SetCheck( checkIt ); + if( fLayer ) + { + if( fLayer->GetParamBlockByID( plDynamicEnvLayer::kBlkBitmap )->GetINode( plDynamicEnvLayer::kBmpAnchorNode ) == NULL ) + iBut->SetText( _T( "" ) ); + } + } + ReleaseICustButton( iBut ); + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +//// ParamBlock Dialog Proc /////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class DELBitmapDlgProc : public ParamMap2UserDlgProc +{ + PickAnchorNode fPickAnchorCallback; + + /// Called to update the controls of the dialog + virtual void Update( TimeValue t, Interval &valid, IParamMap2 *map ) + { + IParamBlock2 *pblock; + int i; + + + ParamMap2UserDlgProc::Update( t, valid, map ); + + pblock = map->GetParamBlock(); + + i = pblock->GetInt( plDynamicEnvLayer::kBmpTextureSize, t ); + pblock->SetValue( plDynamicEnvLayer::kBmpLastTextureSize, t, i ); + + if( pblock->GetINode( plDynamicEnvLayer::kBmpAnchorNode ) == NULL ) + { + ICustButton *bmSelectBtn = GetICustButton( GetDlgItem( pblock->GetMap()->GetHWnd(), IDC_ANCHOR_NODE ) ); + bmSelectBtn->SetText( _T( "" ) ); + ReleaseICustButton( bmSelectBtn ); + } + } + + /// Clamp texture sizes to a power of 2 + void IClampTexSizeSpinner( TimeValue t, IParamMap2 *map ) + { + IParamBlock2 *pblock = map->GetParamBlock(); + + int lastVal = pblock->GetInt( plDynamicEnvLayer::kBmpLastTextureSize, t ); + int tempVal, newVal = pblock->GetInt( plDynamicEnvLayer::kBmpTextureSize, t ); + + if( newVal < lastVal ) + { + lastVal = newVal; + for( tempVal = 1; tempVal < newVal; tempVal <<= 1 ); + newVal = tempVal >> 1; + } + else + { + lastVal = newVal; + for( tempVal = 1; tempVal < newVal; tempVal <<= 1 ); + newVal = tempVal; + } + + pblock->SetValue( plDynamicEnvLayer::kBmpTextureSize, t, newVal ); + pblock->SetValue( plDynamicEnvLayer::kBmpLastTextureSize, t, newVal ); + } + + /// Main message proc + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + break; + + case CC_SPINNER_CHANGE: + if( LOWORD( wParam ) == IDC_TEXSIZE_SPIN ) + IClampTexSizeSpinner( t, map ); + break; + + case WM_COMMAND: + + if( HIWORD( wParam ) == EN_CHANGE && LOWORD( wParam ) == IDC_TEXSIZE_EDIT ) + IClampTexSizeSpinner( t, map ); + + else if( LOWORD( wParam ) == IDC_ANCHOR_NODE ) + { + plDynamicEnvLayer *layer = (plDynamicEnvLayer *)map->GetParamBlock()->GetOwner(); + + layer->fIMtlParams->EndPickMode(); + fPickAnchorCallback.fHWnd = hWnd; + fPickAnchorCallback.fLayer = layer; + + layer->fIMtlParams->SetPickMode( &fPickAnchorCallback ); + break; + } + break; + } + + return FALSE; + } + + void DeleteThis() {}; +}; + +static DELBitmapDlgProc gDELBitmapDlgProc; + +class BleahPBAccessor : public PBAccessor +{ +public: + void Set(PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + plDynamicEnvLayer* layer = (plDynamicEnvLayer *)owner; + IParamBlock2 *pb = layer->GetParamBlockByID( plDynamicEnvLayer::kBlkBitmap ); + + switch (id) + { + case plDynamicEnvLayer::kBmpAnchorNode: + INode *newNode = (INode *)val.r; + if( newNode == NULL ) + { + // Instead of displaying "none", display "", since that's what nil means + // for us + ICustButton *bmSelectBtn = GetICustButton( GetDlgItem( pb->GetMap()->GetHWnd(), IDC_ANCHOR_NODE ) ); + bmSelectBtn->SetText( _T( "" ) ); + ReleaseICustButton( bmSelectBtn ); + } + break; + } + } + void Get(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid) + { + } +}; + +static BleahPBAccessor gBleahPBAccessor; + +/////////////////////////////////////////////////////////////////////////////// +//// ParamBlock Definition //////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ParamBlockDesc2 gBitmapParamBlk +( + plDynamicEnvLayer::kBlkBitmap, _T("bitmap"), 0, GetDynamicEnvLayerDesc(), + P_AUTO_CONSTRUCT + P_AUTO_UI, plDynamicEnvLayer::kRefBitmap, + + IDD_DYNAMIC_ENVMAP_LAYER, IDS_DYNAMIC_ENVMAP_LAYER_TEX, 0, 0, &gDELBitmapDlgProc, + + // General parameters + plDynamicEnvLayer::kBmpTextureSize, _T("textureSize"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_TEXSIZE_EDIT, IDC_TEXSIZE_SPIN, SPIN_AUTOSCALE, + p_range, 4, 512, + p_default, 64, + end, + plDynamicEnvLayer::kBmpAnchorNode, _T("anchorNode"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_ANCHOR_NODE, + p_prompt, IDS_SELECT_ANCHOR, + p_accessor, &gBleahPBAccessor, + end, + plDynamicEnvLayer::kBmpLastTextureSize, _T("lastTextureSize"), TYPE_INT, 0, 0, + end, + + plDynamicEnvLayer::kBmpRefract, _T("refract"),TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_REFRACT, + end, + + end +); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayer.cpp new file mode 100644 index 00000000..93f83796 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayer.cpp @@ -0,0 +1,559 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plDynamicTextLayer.h" + +#include "iparamb2.h" +#include "iparamm2.h" +#include "stdmat.h" + +#include "plBMSampler.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +class plDynamicTextLayerClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading = FALSE) { return TRACKED_NEW plDynamicTextLayer(); } + const TCHAR* ClassName() { return GetString(IDS_DYN_TEXT_LAYER); } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + Class_ID ClassID() { return DYN_TEXT_LAYER_CLASS_ID; } + const TCHAR* Category() { return TEXMAP_CAT_2D; } + const TCHAR* InternalName() { return _T("PlasmaDynamicTextLayer"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plDynamicTextLayerClassDesc plDynamicTextLayerDesc; +ClassDesc2* GetDynamicTextLayerDesc() { return &plDynamicTextLayerDesc; } + +#include "plDynamicTextLayerBitmapPB.cpp" + +ParamDlg* plDynamicTextLayer::fUVGenDlg = NULL; + +plDynamicTextLayer::plDynamicTextLayer() : + fBitmapPB(NULL), + fUVGen(NULL), + fTexHandle(NULL), + fTexTime(0), + fIValid(NEVER) +{ + fInitBitmap = NULL; + + plDynamicTextLayerDesc.MakeAutoParamBlocks(this); + ReplaceReference(kRefUVGen, GetNewDefaultUVGen()); +} + +plDynamicTextLayer::~plDynamicTextLayer() +{ + if( fInitBitmap ) + fInitBitmap->DeleteThis(); + + IDiscardTexHandle(); +} + +//From MtlBase +void plDynamicTextLayer::Reset() +{ + GetDynamicTextLayerDesc()->Reset(this, TRUE); // reset all pb2's + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); + + fIValid.SetEmpty(); +} + +void plDynamicTextLayer::Update(TimeValue t, Interval& valid) +{ + if (!fIValid.InInterval(t)) + { + fIValid.SetInfinite(); + + fUVGen->Update(t,fIValid); + fBitmapPB->GetValidity(t, fIValid); + } + + // Gonna need to do this when we support animated bm's +#if 0 + if (fBM) + { + if (bi.FirstFrame()!=bi.LastFrame()) + ivalid.SetInstant(t); + } +#endif + + valid &= fIValid; +} + +Interval plDynamicTextLayer::Validity(TimeValue t) +{ + //TODO: Update fIValid here + + // mf horse - Hacking this in just to get animations working. + // No warranty on this not being stupid. + Interval v = FOREVER; + fBitmapPB->GetValidity(t, v); + v &= fUVGen->Validity(t); + return v; +} + +ParamDlg* plDynamicTextLayer::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + fIMtlParams = imp; + IAutoMParamDlg* masterDlg = plDynamicTextLayerDesc.CreateParamDlgs(hwMtlEdit, imp, this); + + fUVGenDlg = fUVGen->CreateParamDlg(hwMtlEdit, imp); + masterDlg->AddDlg(fUVGenDlg); + + return masterDlg; +} + +BOOL plDynamicTextLayer::SetDlgThing(ParamDlg* dlg) +{ + if (dlg == fUVGenDlg) + { + fUVGenDlg->SetThing(fUVGen); + return TRUE; + } + + return FALSE; +} + +int plDynamicTextLayer::NumRefs() +{ + return 2; +} + +//From ReferenceMaker +RefTargetHandle plDynamicTextLayer::GetReference(int i) +{ + switch (i) + { + case kRefUVGen: return fUVGen; + case kRefBitmap: return fBitmapPB; + default: return NULL; + } +} + +void plDynamicTextLayer::SetReference(int i, RefTargetHandle rtarg) +{ + Interval garbage; + + switch (i) + { + case kRefUVGen: + fUVGen = (UVGen *)rtarg; + if( fUVGen ) + fUVGen->Update( TimeValue( 0 ), garbage ); + break; + case kRefBitmap: + fBitmapPB = (IParamBlock2 *)rtarg; + // KLUDGE: If the paramblock is being set chances are we are being created or + // loaded. In the case of load, we want to refresh our textures. + if (fBitmapPB) + RefreshBitmaps(); + break; + } +} + +int plDynamicTextLayer::NumParamBlocks() +{ + return 1; +} + +IParamBlock2* plDynamicTextLayer::GetParamBlock(int i) +{ + switch (i) + { + case 0: return fBitmapPB; + default: return NULL; + } +} + +IParamBlock2* plDynamicTextLayer::GetParamBlockByID(BlockID id) +{ + if (fBitmapPB->ID() == id) + return fBitmapPB; + else + return NULL; +} + +//From ReferenceTarget +RefTargetHandle plDynamicTextLayer::Clone(RemapDir &remap) +{ + plDynamicTextLayer *mnew = TRACKED_NEW plDynamicTextLayer(); + *((MtlBase*)mnew) = *((MtlBase*)this); // copy superclass stuff + mnew->ReplaceReference(kRefBitmap, remap.CloneRef(fBitmapPB)); + mnew->ReplaceReference(kRefUVGen, remap.CloneRef(fUVGen)); + BaseClone(this, mnew, remap); + return (RefTargetHandle)mnew; +} + +int plDynamicTextLayer::NumSubs() +{ + return 2; +} + +Animatable* plDynamicTextLayer::SubAnim(int i) +{ + //TODO: Return 'i-th' sub-anim + switch (i) + { + case kRefUVGen: return fUVGen; + case kRefBitmap: return fBitmapPB; + default: return NULL; + } +} + +TSTR plDynamicTextLayer::SubAnimName(int i) +{ + switch (i) + { + case kRefUVGen: return "UVGen"; + case kRefBitmap: return fBitmapPB->GetLocalName(); + default: return ""; + } +} + +RefResult plDynamicTextLayer::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message) +{ + switch (message) + { + case REFMSG_CHANGE: + { + fIValid.SetEmpty(); + + if (hTarget == fBitmapPB) + { + // see if this message came from a changing parameter in the pblock, + // if so, limit rollout update to the changing item and update any active viewport texture + ParamID changingParam = fBitmapPB->LastNotifyParamID(); + fBitmapPB->GetDesc()->InvalidateUI(changingParam); + + if (changingParam != -1) + IChanged(); + } + } + break; + + case REFMSG_UV_SYM_CHANGE: + IDiscardTexHandle(); + break; + } + + return REF_SUCCEED; +} + +void plDynamicTextLayer::IChanged() +{ + IDiscardTexHandle(); + // Texture wasn't getting updated in the viewports, and this fixes it. + // Don't know if it's the right way though. + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); + + // And this is so the SceneWatcher gets notified that the material on some of it's + // referenced objects changed. + NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_MAT); +} + +#define TEX_HDR_CHUNK 0x5000 + +IOResult plDynamicTextLayer::Save(ISave *isave) +{ + IOResult res; + + isave->BeginChunk(TEX_HDR_CHUNK); + res = MtlBase::Save(isave); + if (res != IO_OK) + return res; + isave->EndChunk(); + + return IO_OK; +} + +IOResult plDynamicTextLayer::Load(ILoad *iload) +{ + IOResult res; + while (IO_OK == (res = iload->OpenChunk())) + { + if (iload->CurChunkID() == TEX_HDR_CHUNK) + { + res = MtlBase::Load(iload); + } + iload->CloseChunk(); + if (res != IO_OK) + return res; + } + + return IO_OK; +} + +inline Point2 CompUV(float x, float y, float z) +{ + return Point2( 0.5f * ( x / z + 1.0f ), 0.5f * ( y / z + 1.0f ) ); +} + +Bitmap *plDynamicTextLayer::GetBitmap( TimeValue t ) +{ + return fInitBitmap; +} + +AColor plDynamicTextLayer::EvalColor(ShadeContext& sc) +{ + if (!sc.doMaps) + return AColor(0.0f, 0.0f, 0.0f, 1.0f); + + AColor color; + if (sc.GetCache(this, color)) + return color; + + if (gbufID) + sc.SetGBufferID(gbufID); + + // Evaluate the Bitmap + if( fBitmapPB->GetInt( kBmpUseInitImage ) && fInitBitmap ) + { + plBMSampler mysamp( this, fInitBitmap ); + color = fUVGen->EvalUVMap( sc, &mysamp, TRUE ); + } + else + color.White(); + + // Invert color if specified + if( fBitmapPB->GetInt( kBmpInvertColor ) ) + { + color.r = 1.0f - color.r; + color.g = 1.0f - color.g; + color.b = 1.0f - color.b; + } + // Discard color if specified + if( fBitmapPB->GetInt( kBmpDiscardColor ) ) + color.r = color.g = color.b = 1.0f; + + // Invert alpha if specified + if( fBitmapPB->GetInt( kBmpInvertAlpha ) ) + color.a = 1.0f - color.a; + // Discard alpha if specified + if( fBitmapPB->GetInt( kBmpDiscardAlpha ) ) + color.a = 1.0f; + + sc.PutCache(this, color); + return color; +} + +float plDynamicTextLayer::EvalMono(ShadeContext& sc) +{ + return Intens(EvalColor(sc)); +} + +Point3 plDynamicTextLayer::EvalNormalPerturb(ShadeContext& sc) +{ + // Return the perturbation to apply to a normal for bump mapping + return Point3(0, 0, 0); +} + +ULONG plDynamicTextLayer::LocalRequirements(int subMtlNum) +{ + return fUVGen->Requirements( subMtlNum ); +} + +void plDynamicTextLayer::IDiscardTexHandle() +{ + if (fTexHandle) + { + fTexHandle->DeleteThis(); + fTexHandle = NULL; + } +} + +void plDynamicTextLayer::ActivateTexDisplay(BOOL onoff) +{ + if (!onoff) + IDiscardTexHandle(); +} + +BITMAPINFO *plDynamicTextLayer::GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono, BOOL forceW, BOOL forceH) +{ + // FIXME + fTexTime = 0;//CalcFrame(t); +// texValid = clipValid; + BITMAPINFO *bmi = NULL; + int xflags = 0; + + // Create a bitmap to write into via Windows + BITMAPINFO tempBMI; + memset( &tempBMI.bmiHeader, 0, sizeof( BITMAPINFOHEADER ) ); + tempBMI.bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); + tempBMI.bmiHeader.biWidth = fBitmapPB->GetInt( kBmpExportWidth ); + tempBMI.bmiHeader.biHeight = -(int)fBitmapPB->GetInt( kBmpExportHeight ); + tempBMI.bmiHeader.biPlanes = 1; + tempBMI.bmiHeader.biCompression = BI_RGB; + tempBMI.bmiHeader.biBitCount = 32; + + DWORD *bitmapBits; + HDC winDC = CreateCompatibleDC( nil ); + HBITMAP bitmap = CreateDIBSection( winDC, &tempBMI, DIB_RGB_COLORS, (void **)&bitmapBits, nil, 0 ); + + HBITMAP old = (HBITMAP)SelectObject( winDC, bitmap ); + + // Write into it now + RECT r; + SetRect( &r, 0, 0, fBitmapPB->GetInt( kBmpExportWidth ) - 1, fBitmapPB->GetInt( kBmpExportHeight ) - 1 ); + HBRUSH brush = CreateSolidBrush( RGB( 255, 0, 0 ) ); + FrameRect( winDC, &r, brush ); + DeleteObject( brush ); + + SetMapMode( winDC, MM_TEXT ); + SetBkMode( winDC, TRANSPARENT ); + SetTextAlign( winDC, TA_TOP | TA_LEFT ); + + // Background letters + int nHeight = -MulDiv( 72, GetDeviceCaps( winDC, LOGPIXELSY ), 72 ); + HFONT winFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, "Times New Roman" ); + if( winFont != nil ) + { + HFONT origFont = (HFONT)SelectObject( winDC, winFont ); + SetTextColor( winDC, RGB( 32, 32, 32 ) ); + char str2[] = "ABCDEFG"; + ::TextOut( winDC, 0, 0, str2, strlen( str2 ) ); + SelectObject( winDC, origFont ); + DeleteObject( winFont ); + } + + nHeight = -MulDiv( 8, GetDeviceCaps( winDC, LOGPIXELSY ), 72 ); + winFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, "Arial" ); + if( winFont != nil ) + { + HFONT origFont = (HFONT)SelectObject( winDC, winFont ); + + SetTextColor( winDC, RGB( 255, 255, 255 ) ); + char str[] = "Dynamic Text"; + ::TextOut( winDC, 0, 0, str, strlen( str ) ); + char str3[] = "This is 8 point Arial"; + ::TextOut( winDC, 0, 12, str3, strlen( str3 ) ); + + SelectObject( winDC, origFont ); + DeleteObject( winFont ); + } + + + /// Create a MAX bitmap and copy over the data, the painful way + Bitmap *maxBmp; + BitmapInfo maxInfo; + + maxInfo.SetType( BMM_TRUE_32 ); + maxInfo.SetWidth( fBitmapPB->GetInt( kBmpExportWidth ) ); + maxInfo.SetHeight( fBitmapPB->GetInt( kBmpExportHeight ) ); + maxInfo.SetFlags( MAP_HAS_ALPHA ); + maxInfo.SetCustomFlag( 0 ); + maxBmp = TheManager->Create( &maxInfo ); + + PixelBuf l64( fBitmapPB->GetInt( kBmpExportWidth ) ); + for( int y = 0; y < fBitmapPB->GetInt( kBmpExportHeight ); y++ ) + { + BMM_Color_64 *p64 = l64.Ptr(); + for( int x = 0; x < fBitmapPB->GetInt( kBmpExportWidth ); x++, p64++ ) + { + COLORREF color = GetPixel( winDC, x, y ); + + if( color == RGB( 0, 0, 0 ) ) + { + if( fBitmapPB->GetInt( kBmpUseInitImage ) && fInitBitmap != nil ) + fInitBitmap->GetLinearPixels( x, y, 1, p64 ); + else + p64->r = p64->g = p64->b = 0.f; + } + else + { + p64->r = GetRValue( color ) << 8; + p64->g = GetGValue( color ) << 8; + p64->b = GetBValue( color ) << 8; + } + p64->a = 0xffff; + } + maxBmp->PutPixels( 0, y, fBitmapPB->GetInt( kBmpExportWidth ), l64.Ptr() ); + } + + // Done with these now + SelectObject( winDC, old ); + DeleteObject( bitmap ); + DeleteObject( winDC ); + + // Convert to a BITMAPINFO. Go figure. + bmi = thmaker.BitmapToDIB( maxBmp, 0, xflags, forceW, forceH ); + + return bmi; +} + +DWORD plDynamicTextLayer::GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker) +{ + // FIXME: ignore validity for now + if (fTexHandle && fIValid.InInterval(t))// && texTime == CalcFrame(t)) + return fTexHandle->GetHandle(); + else + { + IDiscardTexHandle(); + + fTexTime = 0;//CalcFrame(t); + fTexHandle = thmaker.MakeHandle(GetVPDisplayDIB(t, thmaker, fIValid)); + if (fTexHandle) + return fTexHandle->GetHandle(); + else + return 0; + } +} + +const char *plDynamicTextLayer::GetTextureName( int which ) +{ + PBBitmap *pbbm = fBitmapPB->GetBitmap( kBmpInitBitmap ); + if( pbbm ) + return pbbm->bi.Name(); + return NULL; +} + +void plDynamicTextLayer::ISetPBBitmap(PBBitmap *pbbm, int index /* = 0 */) +{ + fBitmapPB->SetValue( (ParamID)kBmpInitBitmap, 0, pbbm ); +} + +PBBitmap *plDynamicTextLayer::GetPBBitmap(int index /* = 0 */) +{ + return fBitmapPB->GetBitmap( (ParamID)kBmpInitBitmap ); +} + +//// GetSamplerInfo /////////////////////////////////////////////////////////// +// Virtual function called by plBMSampler to get various things while sampling +// the layer's image + +bool plDynamicTextLayer::GetSamplerInfo( plBMSamplerData *samplerData ) +{ + if( fBitmapPB->GetInt( (ParamID)kBmpDiscardAlpha ) ) + samplerData->fAlphaSource = plBMSamplerData::kDiscard; + else + samplerData->fAlphaSource = plBMSamplerData::kFromTexture; + + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayer.h new file mode 100644 index 00000000..ff3bf3c4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayer.h @@ -0,0 +1,212 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDynamicTextLayer - Dynamic Run-time Text MAX Layer // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 1.13.2002 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDynamicTextLayer_h +#define _plDynamicTextLayer_h + +#include "Max.h" +#include "../resource.h" +#include "plPlasmaMAXLayer.h" + +class ClassDesc2; +class IParamBlock2; +class DTLPBAccessor; + +ClassDesc2* GetDynamicTextLayerDesc(); + +extern TCHAR *GetString(int id); +extern HINSTANCE hInstance; + + +//// Class Definition ///////////////////////////////////////////////////////// + +class plDynamicTextLayer : public plPlasmaMAXLayer +{ +protected: + friend class DTLPBAccessor; + + // Parameter block + IParamBlock2 *fBitmapPB; + UVGen *fUVGen; + + IMtlParams *fIMtlParams; + + TexHandle *fTexHandle; + TimeValue fTexTime; + + Interval fIValid; + + Bitmap *fInitBitmap; + + static ParamDlg *fUVGenDlg; + + friend class DTLBitmapDlgProc; + + +public: + // Ref nums + enum + { + kRefUVGen, + kRefBitmap, + }; + + // Block ID's + enum + { + kBlkBitmap, + }; + + plDynamicTextLayer(); + ~plDynamicTextLayer(); + void DeleteThis() { delete this; } + + //From MtlBase + ParamDlg* CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + BOOL SetDlgThing(ParamDlg* dlg); + void Update(TimeValue t, Interval& valid); + void Reset(); + Interval Validity(TimeValue t); + ULONG LocalRequirements(int subMtlNum); + + //From Texmap + RGBA EvalColor(ShadeContext& sc); + float EvalMono(ShadeContext& sc); + Point3 EvalNormalPerturb(ShadeContext& sc); + + // For displaying textures in the viewport + BOOL SupportTexDisplay() { return TRUE; } + void ActivateTexDisplay(BOOL onoff); + BITMAPINFO *GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono=FALSE, int forceW=0, int forceH=0); + DWORD GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker); +protected: + void IChanged(); + void IDiscardTexHandle(); + +public: + void GetUVTransform(Matrix3 &uvtrans) { fUVGen->GetUVTransform(uvtrans); } + int GetTextureTiling() { return fUVGen->GetTextureTiling(); } + int GetUVWSource() { return fUVGen->GetUVWSource(); } + virtual int GetMapChannel () { return fUVGen->GetMapChannel(); } // only relevant if above returns UVWSRC_EXPLICIT + UVGen *GetTheUVGen() { return fUVGen; } + + //TODO: Return anim index to reference index + int SubNumToRefNum(int subNum) { return subNum; } + + + // Loading/Saving + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + //From Animatable + Class_ID ClassID() { return DYN_TEXT_LAYER_CLASS_ID; } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + void GetClassName(TSTR& s) { s = GetString(IDS_DYN_TEXT_LAYER); } + + RefTargetHandle Clone( RemapDir &remap ); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + // TODO: Maintain the number or references here + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + + int NumParamBlocks(); // return number of ParamBlocks in this instance + IParamBlock2* GetParamBlock(int i); // return i'th ParamBlock + IParamBlock2* GetParamBlockByID(BlockID id); // return id'd ParamBlock + + const char *GetTextureName( int which ); + virtual Bitmap* GetBitmap(TimeValue t); + + + /// ParamBlock accessors + enum + { + kScalingAny, + kScalingHalf, + kScalingNone + }; + + // Param ID's + enum + { + kBmpExportWidth, + kBmpExportHeight, + kBmpExportLastWidth, // Annoying fields, these two, but they're necessary + kBmpExportLastHeight, // for clamping the spinners to powers of 2 + + // Misc + kBmpDiscardColor, + kBmpInvertColor, + kBmpDiscardAlpha, + kBmpInvertAlpha, + + // Static text settings + kBmpMakeStatic, + kBmpText, + kBmpFontFace, + kBmpFontSize, + kBmpLeftMargin, + kBmpTopMargin, + kBmpRightMargin, + kBmpBottomMargin, + + // Initial image settings + kBmpUseInitImage, + kBmpInitBitmap, + + kBmpIncludeAlphaChannel + }; + + // Pure virtual accessors for the various bitmap related elements + virtual Bitmap *GetMaxBitmap(int index = 0) { return fInitBitmap; } + virtual PBBitmap *GetPBBitmap( int index = 0 ); + virtual int GetNumBitmaps( void ) { return 1; } + + // Virtual function called by plBMSampler to get various things while sampling the layer's image + virtual bool GetSamplerInfo( plBMSamplerData *samplerData ); + + protected: + virtual void ISetMaxBitmap( Bitmap *bitmap, int index = 0 ) { fInitBitmap = bitmap; } + virtual void ISetPBBitmap( PBBitmap *pbbm, int index = 0 ); +}; + +#endif // _plDynamicTextLayer_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayerBitmapPB.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayerBitmapPB.cpp new file mode 100644 index 00000000..632b182f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plDynamicTextLayerBitmapPB.cpp @@ -0,0 +1,304 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plDynamicTextLayer.h" + +#include "iparamb2.h" +#include "iparamm2.h" +#include "stdmat.h" + +#include "plBMSampler.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + + +/////////////////////////////////////////////////////////////////////////////// +//// ParamBlock Dialog Proc /////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class DTLBitmapDlgProc : public ParamMap2UserDlgProc +{ + /// Called to update the controls of the dialog + virtual void Update( TimeValue t, Interval &valid, IParamMap2 *map ) + { + IParamBlock2 *pblock; + BitmapInfo bi; + ICustButton *bmSelectBtn; + + + ParamMap2UserDlgProc::Update( t, valid, map ); + + pblock = map->GetParamBlock(); + + plDynamicTextLayer *layer = (plDynamicTextLayer *)map->GetParamBlock()->GetOwner(); + + bmSelectBtn = GetICustButton( GetDlgItem( map->GetHWnd(), IDC_INITIMAGE ) ); + PBBitmap *pbbm = pblock->GetBitmap( plDynamicTextLayer::kBmpInitBitmap ); + if( pbbm ) + bmSelectBtn->SetText( (TCHAR *)pbbm->bi.Filename() ); + else + bmSelectBtn->SetText( _T( "None" ) ); + ReleaseICustButton( bmSelectBtn ); + } + + /// Clamp texture sizes to a power of 2 + void IClampTexSizeSpinner( TimeValue t, IParamMap2 *map ) + { + } + + /// Main message proc + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + + switch (msg) + { + case WM_INITDIALOG: + break; + + /// Note: the following *could* be done in the accessor, except that you end up in an + /// infinite loop updating the values. Not good. + case CC_SPINNER_CHANGE: + + if( LOWORD( wParam ) == IDC_EXPORTWIDTH_SPINNER ) + IClampTexSizeSpinner( t, map, true ); + + else if( LOWORD( wParam ) == IDC_EXPORTHEIGHT_SPINNER ) + IClampTexSizeSpinner( t, map, false ); + + break; + + case WM_COMMAND: + + if( HIWORD( wParam ) == EN_CHANGE && LOWORD( wParam ) == IDC_EXPORTWIDTH ) + IClampTexSizeSpinner( t, map, true ); + + else if( HIWORD( wParam ) == EN_CHANGE && LOWORD( wParam ) == IDC_EXPORTHEIGHT ) + IClampTexSizeSpinner( t, map, false ); + + else if( HIWORD( wParam ) == BN_CLICKED && LOWORD( wParam ) == IDC_INITIMAGE_RELOAD ) + { + // TEMP + IParamBlock2 *pblock = map->GetParamBlock(); + PBBitmap *pbbm = pblock->GetBitmap( plDynamicTextLayer::kBmpInitBitmap ); + if( pbbm ) + { + plDynamicTextLayer *layer = (plDynamicTextLayer *)map->GetParamBlock()->GetOwner(); + layer->RefreshBitmaps(); + layer->IChanged(); + } + return TRUE; + } + + else if( LOWORD( wParam ) == IDC_INITIMAGE ) + { + plPlasmaMAXLayer *layer = (plPlasmaMAXLayer *)map->GetParamBlock()->GetOwner(); + if( layer == nil ) + return FALSE; + BOOL selectedNewBitmap = layer->HandleBitmapSelection(); + if( selectedNewBitmap ) + { + IParamBlock2 *pblock = map->GetParamBlock(); + + ICustButton *bmSelectBtn = GetICustButton( GetDlgItem( hWnd, IDC_INITIMAGE ) ); + PBBitmap *pbbm = layer->GetPBBitmap(); + + bmSelectBtn->SetText( pbbm != nil ? (TCHAR *)pbbm->bi.Filename() : ""); + + ReleaseICustButton( bmSelectBtn ); + } + + return TRUE; + } + break; + } + + return FALSE; + } + void DeleteThis() {}; + +protected: + /// Clamp texture sizes to a power of 2 + void IClampTexSizeSpinner( TimeValue t, IParamMap2 *map, bool clampWidth ) + { + IParamBlock2 *pblock = map->GetParamBlock(); + ParamID clampNew, clampOld; + ParamID otherNew, otherOld; + + if( clampWidth ) + { + clampNew = plDynamicTextLayer::kBmpExportWidth; clampOld = plDynamicTextLayer::kBmpExportLastWidth; + otherNew = plDynamicTextLayer::kBmpExportHeight; otherOld = plDynamicTextLayer::kBmpExportLastHeight; + } + else + { + clampNew = plDynamicTextLayer::kBmpExportHeight; clampOld = plDynamicTextLayer::kBmpExportLastHeight; + otherNew = plDynamicTextLayer::kBmpExportWidth; otherOld = plDynamicTextLayer::kBmpExportLastWidth; + } + + int lastVal = pblock->GetInt( clampOld, t ); + int tempVal, newVal = pblock->GetInt( clampNew, t ); + + if( newVal < lastVal ) + { + lastVal = newVal; + for( tempVal = 1; tempVal <= newVal; tempVal <<= 1 ); + newVal = tempVal >> 1; + } + else + { + lastVal = newVal; + for( tempVal = 1; tempVal < newVal; tempVal <<= 1 ); + newVal = tempVal; + } + + pblock->SetValue( clampNew, t, newVal ); + pblock->SetValue( clampOld, t, newVal ); + } + + int IFloorPow2( int value ) + { + int v; + + + for( v = 1; v <= value; v <<= 1 ); + return v >> 1; + } +}; + +static DTLBitmapDlgProc gDTLBitmapDlgProc; + +/////////////////////////////////////////////////////////////////////////////// +//// Bitmap Accessor ////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class DTLPBAccessor : public PBAccessor +{ +public: + void Set(PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if( !owner ) + return; + + plDynamicTextLayer *layer = (plDynamicTextLayer *)owner; + + IParamBlock2 *pb = owner->GetParamBlockByID( plDynamicTextLayer::kBlkBitmap ); + switch( id ) + { + case plDynamicTextLayer::kBmpInitBitmap: + case plDynamicTextLayer::kBmpUseInitImage: + if (pb->GetMap()) + pb->GetMap()->Invalidate( id ); + + layer->IChanged(); + + break; + } + } + void Get(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid) + { + } +}; +static DTLPBAccessor gDTLPBAccessor; + +/////////////////////////////////////////////////////////////////////////////// +//// ParamBlock Definition //////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ParamBlockDesc2 gBitmapParamBlk +( + plDynamicTextLayer::kBlkBitmap, _T("bitmap"), 0, GetDynamicTextLayerDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI, plDynamicTextLayer::kRefBitmap, + + IDD_DYN_TEXT_LAYER, IDS_DYN_TEXT_LAYER_PROPS, 0, 0, &gDTLBitmapDlgProc, + + // Texture Color/Alpha + plDynamicTextLayer::kBmpDiscardColor, _T("discardColor"), TYPE_BOOL, 0, 0, +// p_ui, TYPE_SINGLECHEKBOX, IDC_BLEND_NO_COLOR, + end, + plDynamicTextLayer::kBmpInvertColor, _T("invertColor"), TYPE_BOOL, 0, 0, +// p_ui, TYPE_SINGLECHEKBOX, IDC_BLEND_INV_COLOR, + end, + plDynamicTextLayer::kBmpDiscardAlpha, _T("discardAlpha"), TYPE_BOOL, 0, 0, +// p_ui, TYPE_SINGLECHEKBOX, IDC_DISCARD_ALPHA, + end, + plDynamicTextLayer::kBmpInvertAlpha, _T("invertAlpha"), TYPE_BOOL, 0, 0, +// p_ui, TYPE_SINGLECHEKBOX, IDC_BLEND_INV_ALPHA, + end, + + // Texture size + plDynamicTextLayer::kBmpExportWidth, _T("exportWidth"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_EXPORTWIDTH, IDC_EXPORTWIDTH_SPINNER, SPIN_AUTOSCALE, + p_range, 4, 2048, + p_default, 512, + end, + plDynamicTextLayer::kBmpExportHeight, _T("exportHeight"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_EXPORTHEIGHT, IDC_EXPORTHEIGHT_SPINNER, SPIN_AUTOSCALE, + p_range, 4, 2048, + p_default, 512, + end, + plDynamicTextLayer::kBmpExportLastWidth, _T("lastExportWidth"), TYPE_INT, 0, 0, + end, + plDynamicTextLayer::kBmpExportLastHeight, _T("lastExportHeight"), TYPE_INT, 0, 0, + end, + + plDynamicTextLayer::kBmpIncludeAlphaChannel, _T("includeAlphaChannel"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_DYNTEXT_ALPHA, + p_default, FALSE, + end, + + // Initial image + plDynamicTextLayer::kBmpUseInitImage, _T("useInitImage"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_USEINITIMAGE, + p_enable_ctrls, 1, plDynamicTextLayer::kBmpInitBitmap, + p_accessor, &gDTLPBAccessor, + end, + plDynamicTextLayer::kBmpInitBitmap, _T("initBitmap"), TYPE_BITMAP, P_SHORT_LABELS, 0, + p_accessor, &gDTLPBAccessor, + end, + +/* plGUIButtonComponent::kRefAnimate, _T( "animate" ), TYPE_BOOL, 0, 0, + p_ui, plGUIControlBase::kRollMain, TYPE_SINGLECHEKBOX, IDC_GUI_ANIMATE, + p_default, FALSE, + p_enable_ctrls, 1, plGUIButtonComponent::kRefAnimation, + end, + + // Static text settings + plDynamicTextLayer::kBmpMakeStatic, _T("makeStatic"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_DYNTEXT_MAKESTATIC, + p_default, FALSE, + p_enable_ctrls, 5, kBmpFontSize, kBmpLeftMargin, kBmpTopMargin, kBmpRightMargin, kBmpBottomMargin, + end, + + plDynamicTextLayer::kBmpText, + plDynamicTextLayer::kBmpFontFace, + plDynamicTextLayer::kBmpFontSize, + plDynamicTextLayer::kBmpLeftMargin, + plDynamicTextLayer::kBmpTopMargin, + plDynamicTextLayer::kBmpRightMargin, + plDynamicTextLayer::kBmpBottomMargin, +*/ + end +); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTex.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTex.cpp new file mode 100644 index 00000000..43c5050f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTex.cpp @@ -0,0 +1,683 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plLayerTex.h" + +#include "iparamb2.h" +#include "iparamm2.h" +#include "stdmat.h" + +#include "plBMSampler.h" +#include "../resource.h" +#include "plLayerTexBitmapPB.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +class plLayerTexClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading = FALSE) { return TRACKED_NEW plLayerTex(); } + const TCHAR* ClassName() { return GetString(IDS_LAYER); } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + Class_ID ClassID() { return LAYER_TEX_CLASS_ID; } + const TCHAR* Category() { return TEXMAP_CAT_2D; } + const TCHAR* InternalName() { return _T("PlasmaLayer"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plLayerTexClassDesc plLayerTexDesc; +ClassDesc2* GetLayerTexDesc() { return &plLayerTexDesc; } + +ParamDlg* plLayerTex::fUVGenDlg = NULL; + +// For initializing paramblock descriptor +//ParamBlockDesc2 *GetBasicBlk(); +ParamBlockDesc2 *GetBitmapBlk(); + +//#include "plLayerTexBasicPB.cpp" +#include "plLayerTexBitmapPB.cpp" + +void plLayerTex::GetClassName( TSTR &s ) +{ + s = GetString( IDS_LAYER ); +} + +plLayerTex::plLayerTex() : + fBitmapPB(NULL), + //fBasicPB(NULL), + fUVGen(NULL), + fTexHandle(NULL), + fTexTime(0), + fBM(NULL), + fIValid(NEVER) +{ +#if 0 + // Initialize the paramblock descriptors only once + static bool descInit = false; + if (!descInit) + { + descInit = true; + //GetBasicBlk()->SetClassDesc(GetLayerTexDesc()); + GetBitmapBlk()->SetClassDesc(GetLayerTexDesc()); + } +#endif + + plLayerTexDesc.MakeAutoParamBlocks(this); + ReplaceReference(kRefUVGen, GetNewDefaultUVGen()); +} + +plLayerTex::~plLayerTex() +{ + if (fBM) + fBM->DeleteThis(); + + IDiscardTexHandle(); +} + +//From MtlBase +void plLayerTex::Reset() +{ + GetLayerTexDesc()->Reset(this, TRUE); // reset all pb2's + SetBitmap(NULL); + + fIValid.SetEmpty(); +} + +void plLayerTex::Update(TimeValue t, Interval& valid) +{ + if (!fIValid.InInterval(t)) + { + fIValid.SetInfinite(); + + fUVGen->Update(t,fIValid); + fBitmapPB->GetValidity(t, fIValid); + +// Interval clipValid; +// clipValid.SetInfinite(); +// float temp; +// fBitmapPB->GetValue(kBmpClipU, t, temp, clipValid); +// fBitmapPB->GetValue(kBmpClipV, t, temp, clipValid); +// fBitmapPB->GetValue(kBmpClipW, t, temp, clipValid); +// fBitmapPB->GetValue(kBmpClipH, t, temp, clipValid); + } + + // Gonna need to do this when we support animated bm's +#if 0 + if (fBM) + { + if (bi.FirstFrame()!=bi.LastFrame()) + ivalid.SetInstant(t); + } +#endif + + valid &= fIValid; +} + +Interval plLayerTex::Validity(TimeValue t) +{ + //TODO: Update fIValid here + + // mf horse - Hacking this in just to get animations working. + // No warranty on this not being stupid. + Interval v = FOREVER; + fBitmapPB->GetValidity(t, v); + //fBasicPB->GetValidity(t, v); + v &= fUVGen->Validity(t); + return v; +} + +ParamDlg* plLayerTex::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + fMtlParams = imp; + + IAutoMParamDlg* masterDlg = plLayerTexDesc.CreateParamDlgs(hwMtlEdit, imp, this); + + fUVGenDlg = fUVGen->CreateParamDlg(hwMtlEdit, imp); + masterDlg->AddDlg(fUVGenDlg); + + return masterDlg; +} + +BOOL plLayerTex::SetDlgThing(ParamDlg* dlg) +{ + if (dlg == fUVGenDlg) + { + fUVGenDlg->SetThing(fUVGen); + return TRUE; + } + + return FALSE; +} + +int plLayerTex::NumRefs() +{ + return 3; +} + +//From ReferenceMaker +RefTargetHandle plLayerTex::GetReference(int i) +{ + switch (i) + { + case kRefUVGen: return fUVGen; + case kRefBitmap: return fBitmapPB; + //case kRefBasic: return fBasicPB; + default: return NULL; + } +} + +void plLayerTex::SetReference(int i, RefTargetHandle rtarg) +{ + Interval garbage; + + switch (i) + { + case kRefUVGen: + fUVGen = (UVGen *)rtarg; + if( fUVGen ) + fUVGen->Update( TimeValue( 0 ), garbage ); + break; + case kRefBitmap: + fBitmapPB = (IParamBlock2 *)rtarg; + // KLUDGE: If the paramblock is being set chances are we are being created or + // loaded. In the case of load, we want to refresh our texture. + if (fBitmapPB) + RefreshBitmaps(); + break; + } +} + +int plLayerTex::NumParamBlocks() +{ + return 2; +} + +IParamBlock2* plLayerTex::GetParamBlock(int i) +{ + switch (i) + { + case 0: return fBitmapPB; + //case 1: return fBasicPB; + default: return NULL; + } +} + +IParamBlock2* plLayerTex::GetParamBlockByID(BlockID id) +{ + if (fBitmapPB->ID() == id) + return fBitmapPB; + //else if (fBasicPB->ID() == id) + // return fBasicPB; + else + return NULL; +} + +//From ReferenceTarget +RefTargetHandle plLayerTex::Clone(RemapDir &remap) +{ + plLayerTex *mnew = TRACKED_NEW plLayerTex(); + *((MtlBase*)mnew) = *((MtlBase*)this); // copy superclass stuff + //mnew->ReplaceReference(kRefBasic, remap.CloneRef(fBasicPB)); + mnew->ReplaceReference(kRefBitmap, remap.CloneRef(fBitmapPB)); + mnew->ReplaceReference(kRefUVGen, remap.CloneRef(fUVGen)); + BaseClone(this, mnew, remap); + return (RefTargetHandle)mnew; +} + +int plLayerTex::NumSubs() +{ + return 3; +} + +Animatable* plLayerTex::SubAnim(int i) +{ + //TODO: Return 'i-th' sub-anim + switch (i) + { + case kRefUVGen: return fUVGen; + case kRefBitmap: return fBitmapPB; + //case kRefBasic: return fBasicPB; + default: return NULL; + } +} + +TSTR plLayerTex::SubAnimName(int i) +{ + switch (i) + { + case kRefUVGen: return "UVGen"; + case kRefBitmap: return fBitmapPB->GetLocalName(); + //case kRefBasic: return fBasicPB->GetLocalName(); + default: return ""; + } +} + +RefResult plLayerTex::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message) +{ + switch (message) + { + case REFMSG_CHANGE: + { + fIValid.SetEmpty(); + + if (hTarget == fBitmapPB) + { + // see if this message came from a changing parameter in the pblock, + // if so, limit rollout update to the changing item and update any active viewport texture + ParamID changingParam = fBitmapPB->LastNotifyParamID(); + fBitmapPB->GetDesc()->InvalidateUI(changingParam); + + if (changingParam != -1) + IChanged(); + } + } + break; + + case REFMSG_UV_SYM_CHANGE: + IDiscardTexHandle(); + break; + } + + return REF_SUCCEED; +} + +BOOL plLayerTex::DiscardColor() +{ + return fBitmapPB->GetInt(kBmpDiscardColor); +} + +BOOL plLayerTex::DiscardAlpha() +{ + return fBitmapPB->GetInt(kBmpDiscardAlpha); +} + +void plLayerTex::IChanged() +{ + IDiscardTexHandle(); + // Texture wasn't getting updated in the viewports, and this fixes it. + // Don't know if it's the right way though. + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); + + // And this is so the SceneWatcher gets notified that the material on some of it's + // referenced objects changed. + NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_MAT); +} + +#define TEX_HDR_CHUNK 0x5000 +#define MAX_ASS_CHUNK 0x5500 + +IOResult plLayerTex::Save(ISave *isave) +{ + IOResult res; + isave->BeginChunk(TEX_HDR_CHUNK); + res = MtlBase::Save(isave); + if (res != IO_OK) + return res; + isave->EndChunk(); + + return IO_OK; +} + +IOResult plLayerTex::Load(ILoad *iload) +{ + IOResult res; + while (IO_OK == (res = iload->OpenChunk())) + { + if (iload->CurChunkID() == TEX_HDR_CHUNK) + { + res = MtlBase::Load(iload); + } + iload->CloseChunk(); + if (res != IO_OK) + return res; + } + + return IO_OK; +} + +bool plLayerTex::HasAlpha() +{ + return (fBM != NULL && fBM->HasAlpha() != 0); +} + +Bitmap* plLayerTex::GetBitmap(TimeValue t) +{ + return fBM; +} + +AColor plLayerTex::EvalColor(ShadeContext& sc) +{ + if (!sc.doMaps) + return AColor(0.0f, 0.0f, 0.0f, 1.0f); + + AColor color; + if (sc.GetCache(this, color)) + return color; + + if (gbufID) + sc.SetGBufferID(gbufID); + + // + // Evaluate the Bitmap + // + if (fBitmapPB->GetInt(kBmpUseBitmap) && fBM) + { + plBMSampler mysamp(this, fBM); + color = fUVGen->EvalUVMap(sc, &mysamp, FALSE); + // We'd like to pass TRUE and actually filter the image, but that seems to be + // tripping an odd crash in Max internals. *shrug* + } + else + color.White(); + + // Invert color if specified + if (fBitmapPB->GetInt(kBmpInvertColor)) + { + color.r = 1.0f - color.r; + color.g = 1.0f - color.g; + color.b = 1.0f - color.b; + } + // Discard color if specified + if (fBitmapPB->GetInt(kBmpDiscardColor)) + color.r = color.g = color.b = 1.0f; + + // Invert alpha if specified + if (fBitmapPB->GetInt(kBmpInvertAlpha)) + color.a = 1.0f - color.a; + // Discard alpha if specified + if (fBitmapPB->GetInt(kBmpDiscardAlpha)) + color.a = 1.0f; + + // If RGB output is set to alpha, show RGB as grayscale of the alpha + if (fBitmapPB->GetInt(kBmpRGBOutput) == 1) + color = AColor(color.a, color.a, color.a, 1.0f); + + sc.PutCache(this, color); + return color; +} + +float plLayerTex::EvalMono(ShadeContext& sc) +{ + if (fBitmapPB->GetInt(kBmpMonoOutput) == 1) + return EvalColor(sc).a; + + return Intens(EvalColor(sc)); +} + +Point3 plLayerTex::EvalNormalPerturb(ShadeContext& sc) +{ + // Return the perturbation to apply to a normal for bump mapping + return Point3(0, 0, 0); +} + +ULONG plLayerTex::LocalRequirements(int subMtlNum) +{ + return fUVGen->Requirements(subMtlNum); +} + +#if 0 +int plLayerTex::ICalcFrame(TimeValue t) +{ + PBBitmap *pbbm = fBitmapPB->GetBitmap(kBmpBitmap); + if (!pbbm || !pbbm->bi) + return 0; + BitmapInfo *bi = pbbm->bi; + + TimeValue tm, dur, td; + int frameStart = bi->FirstFrame(); + int frameEnd = bi->LastFrame(); + int tpf = GetTicksPerFrame(); + tm = TimeValue(float(t - startTime) * pbRate); + dur = (fend-fstart+1)*GetTicksPerFrame(); + + switch (endCond) + { + case END_HOLD: + if (tm <= 0) + return frameStart; + if (tm >= dur) + return frameEnd; + return tm/tpf; + + case END_PINGPONG: + if (((tm >= 0) && ((tm / dur) & 1)) || ((tm < 0) && !(tm / dur))) + { + td = modt(tm, dur); + return frameStart + frameEnd - td / tpf; + } + // else fall through + case END_LOOP: + td = modt(tm, dur); + return td / tpf; + } + + return 0; +} +#endif + +void plLayerTex::IDiscardTexHandle() +{ + if (fTexHandle) + { + fTexHandle->DeleteThis(); + fTexHandle = NULL; + } +} + +void plLayerTex::ActivateTexDisplay(BOOL onoff) +{ + if (!onoff) + IDiscardTexHandle(); +} + +BITMAPINFO *plLayerTex::GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono, BOOL forceW, BOOL forceH) +{ + // FIXME + fTexTime = 0;//CalcFrame(t); +// texValid = clipValid; + BITMAPINFO *bmi = NULL; + int xflags = 0; + + if (fBitmapPB->GetInt(kBmpApply)) + { + float clipu = fBitmapPB->GetFloat(kBmpClipU); + float clipv = fBitmapPB->GetFloat(kBmpClipV); + float clipw = fBitmapPB->GetFloat(kBmpClipW); + float cliph = fBitmapPB->GetFloat(kBmpClipH); + int discardAlpha = fBitmapPB->GetInt(kBmpDiscardAlpha); + int alphaAsRGB = (fBitmapPB->GetInt(kBmpRGBOutput) == 1); + + int w = fBM->Width(); + int h = fBM->Height(); + + Bitmap *newBM; + BitmapInfo bi; + bi.SetName(_T("y8798734")); + bi.SetType(BMM_TRUE_32); + bi.SetFlags(MAP_HAS_ALPHA); + + if (fBitmapPB->GetInt(kBmpCropPlace) == 1) + { + int x0, y0, nw, nh; + int bmw = thmaker.Size(); + int bmh = int(float(bmw)*float(h)/float(w)); + bi.SetWidth(bmw); + bi.SetHeight(bmh); + newBM = TheManager->Create(&bi); + newBM->Fill(0,0,0,0); + nw = int(float(bmw)*clipw); + nh = int(float(bmh)*cliph); + x0 = int(float(bmw-1)*clipu); + y0 = int(float(bmh-1)*clipv); + + if (nw<1) nw = 1; + if (nh<1) nh = 1; + PixelBuf row(nw); + + Bitmap *tmpBM; + BitmapInfo bif2; + bif2.SetName(_T("xxxx67878")); + bif2.SetType(BMM_TRUE_32); + bif2.SetFlags(MAP_HAS_ALPHA); + bif2.SetWidth(nw); + bif2.SetHeight(nh); + tmpBM = TheManager->Create(&bif2); + tmpBM->CopyImage(fBM, COPY_IMAGE_RESIZE_LO_QUALITY, 0); + BMM_Color_64* p1 = row.Ptr(); + for (int y = 0; yGetLinearPixels(0,y, nw, p1); + if (alphaAsRGB) + { + for (int ix =0; ixPutPixels(x0, y+y0, nw, p1); + } + tmpBM->DeleteThis(); + bmi = thmaker.BitmapToDIB(newBM, fUVGen->SymFlags(), xflags, forceW, forceH); + newBM->DeleteThis(); + } + else + { + int x0,y0,nw,nh; + x0 = int(float(w-1)*clipu); + y0 = int(float(h-1)*clipv); + nw = int(float(w)*clipw); + nh = int(float(h)*cliph); + if (nw<1) nw = 1; + if (nh<1) nh = 1; + bi.SetWidth(nw); + bi.SetHeight(nh); + PixelBuf row(nw); + newBM = TheManager->Create(&bi); + BMM_Color_64* p1 = row.Ptr(); + for (int y = 0; yGetLinearPixels(x0,y+y0, nw, p1); + if (alphaAsRGB) + { + for (int ix = 0; ix < nw; ix++) + p1[ix].r = p1[ix].g = p1[ix].b = p1[ix].a; + } + if (discardAlpha) + { + for (int ix = 0; ix < nw; ix++) + p1[ix].a = 0xffff; + } + newBM->PutPixels(0, y, nw, p1); + } + bmi = thmaker.BitmapToDIB(newBM, fUVGen->SymFlags(), xflags, forceW, forceH); + newBM->DeleteThis(); + } + } + else + { + if (fBitmapPB->GetInt(kBmpRGBOutput) == 1) + xflags |= EX_RGB_FROM_ALPHA; + bmi = thmaker.BitmapToDIB(fBM, fUVGen->SymFlags(), xflags, forceW, forceH); + } + + return bmi; +} + +DWORD plLayerTex::GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker) +{ + // FIXME: ignore validity for now + if (fTexHandle && fIValid.InInterval(t))// && texTime == CalcFrame(t)) + return fTexHandle->GetHandle(); + else + { + IDiscardTexHandle(); + + fTexTime = 0;//CalcFrame(t); + fTexHandle = thmaker.MakeHandle(GetVPDisplayDIB(t, thmaker, fIValid)); + if (fTexHandle) + return fTexHandle->GetHandle(); + else + return 0; + } +} + +const char *plLayerTex::GetTextureName() +{ +// if (fBitmapPB->GetInt(kBmpUseBitmap)) + { + PBBitmap *pbbm = fBitmapPB->GetBitmap(kBmpBitmap); + if (pbbm) + return pbbm->bi.Name(); + } + + return NULL; +} + +void plLayerTex::ISetPBBitmap(PBBitmap *pbbm, int index /* = 0 */) +{ + fBitmapPB->SetValue(ParamID(kBmpBitmap), 0, pbbm, index); +} + +PBBitmap *plLayerTex::GetPBBitmap(int index /* = 0 */) +{ + return fBitmapPB->GetBitmap(ParamID(kBmpBitmap)); +} + +//// GetSamplerInfo /////////////////////////////////////////////////////////// +// Virtual function called by plBMSampler to get various things while sampling +// the layer's image + +bool plLayerTex::GetSamplerInfo( plBMSamplerData *samplerData ) +{ + samplerData->fClipU = fBitmapPB->GetFloat( (ParamID)kBmpClipU ); + samplerData->fClipV = fBitmapPB->GetFloat( (ParamID)kBmpClipV ); + samplerData->fClipW = fBitmapPB->GetFloat( (ParamID)kBmpClipW ); + samplerData->fClipH = fBitmapPB->GetFloat( (ParamID)kBmpClipH ); + + samplerData->fEnableCrop = fBitmapPB->GetInt( (ParamID)kBmpApply ) ? true : false; + samplerData->fCropPlacement = fBitmapPB->GetInt( (ParamID)kBmpCropPlace ); + + if( fBitmapPB->GetInt( (ParamID)kBmpDiscardAlpha ) ) + samplerData->fAlphaSource = plBMSamplerData::kDiscard; + else if( fBitmapPB->GetInt( (ParamID)kBmpRGBOutput ) == 1 ) + samplerData->fAlphaSource = plBMSamplerData::kFromRGB; + else + samplerData->fAlphaSource = plBMSamplerData::kFromTexture; + + return true; +} + +void plLayerTex::SetExportSize(int x, int y) +{ + fBitmapPB->SetValue(kBmpExportWidth, 0, x); + fBitmapPB->SetValue(kBmpExportLastWidth, 0, x); + fBitmapPB->SetValue(kBmpExportHeight, 0, y); + fBitmapPB->SetValue(kBmpExportLastHeight, 0, y); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTex.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTex.h new file mode 100644 index 00000000..86afb1c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTex.h @@ -0,0 +1,163 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __PLMAXLAYER__H +#define __PLMAXLAYER__H + +#include "Max.h" +#include "plPlasmaMAXLayer.h" + +class ClassDesc2; +class IParamBlock2; +class Bitmap; + +ClassDesc2* GetLayerTexDesc(); + +extern TCHAR *GetString(int id); +extern HINSTANCE hInstance; + +class plLayerTex : public plPlasmaMAXLayer +{ +protected: + // Parameter block + IParamBlock2 *fBitmapPB; + IParamBlock2 *fBasicPB; + UVGen *fUVGen; + + IMtlParams *fMtlParams; + + TexHandle *fTexHandle; + TimeValue fTexTime; + + Bitmap *fBM; + static ParamDlg *fUVGenDlg; + Interval fIValid; + + friend class BitmapDlgProc; + +public: + // Ref nums + enum + { + kRefUVGen, + kRefBasic, // DEAD, but left in for backwards compatability + kRefBitmap, + }; + + // Block ID's + enum + { + kBlkBasic, // DEAD + kBlkBitmap, + }; + + plLayerTex(); + ~plLayerTex(); + void DeleteThis() { delete this; } + + //From MtlBase + ParamDlg* CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + BOOL SetDlgThing(ParamDlg* dlg); + void Update(TimeValue t, Interval& valid); + void Reset(); + Interval Validity(TimeValue t); + ULONG LocalRequirements(int subMtlNum); + + //From Texmap + RGBA EvalColor(ShadeContext& sc); + float EvalMono(ShadeContext& sc); + Point3 EvalNormalPerturb(ShadeContext& sc); + + // For displaying textures in the viewport + BOOL SupportTexDisplay() { return TRUE; } + void ActivateTexDisplay(BOOL onoff); + BITMAPINFO *GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono=FALSE, int forceW=0, int forceH=0); + DWORD GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker); +protected: + void IChanged(); + void IDiscardTexHandle(); + +public: + void GetUVTransform(Matrix3 &uvtrans) { fUVGen->GetUVTransform(uvtrans); } + int GetTextureTiling() { return fUVGen->GetTextureTiling(); } + int GetUVWSource() { return fUVGen->GetUVWSource(); } + virtual int GetMapChannel () { return fUVGen->GetMapChannel(); } // only relevant if above returns UVWSRC_EXPLICIT + UVGen *GetTheUVGen() { return fUVGen; } + + //TODO: Return anim index to reference index + int SubNumToRefNum(int subNum) { return subNum; } + + virtual BOOL DiscardColor(); + virtual BOOL DiscardAlpha(); + + // Loading/Saving + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + //From Animatable + Class_ID ClassID() { return LAYER_TEX_CLASS_ID; } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + void GetClassName(TSTR& s); + + RefTargetHandle Clone( RemapDir &remap ); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + // TODO: Maintain the number or references here + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + + int NumParamBlocks(); // return number of ParamBlocks in this instance + IParamBlock2* GetParamBlock(int i); // return i'th ParamBlock + IParamBlock2* GetParamBlockByID(BlockID id); // return id'd ParamBlock + + bool HasAlpha(); // Checks if the bitmap for this layer has an alpha channel + virtual Bitmap* GetBitmap(TimeValue t); + + const char *GetTextureName(); + + // Accessors needed by the base class for the various bitmap related elements + virtual Bitmap *GetMaxBitmap(int index = 0) { return fBM; } + virtual PBBitmap *GetPBBitmap( int index = 0 ); + virtual int GetNumBitmaps( void ) { return 1; } + + // Virtual function called by plBMSampler to get various things while sampling the layer's image + virtual bool GetSamplerInfo( plBMSamplerData *samplerData ); + + // Backdoor for the texture find and replace util. Assumes input has the correct aspect ratio and is power of 2. + virtual void SetExportSize(int x, int y); + +protected: + virtual void ISetPBBitmap( PBBitmap *pbbm, int index = 0 ); + virtual void ISetMaxBitmap(Bitmap *bitmap, int index = 0) { fBM = bitmap; } + +}; + +#endif // __PLMAXLAYER__H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBasicPB.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBasicPB.cpp new file mode 100644 index 00000000..f2de824c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBasicPB.cpp @@ -0,0 +1,105 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plLayerTex.h" +#include "plLayerTexBasicPB.h" + +class BasicDlgProc; +extern BasicDlgProc gBasicDlgProc; + +static ParamBlockDesc2 gBasicParamBlk +( + plLayerTex::kBlkBasic, _T("basicLayer"), 0, GetLayerTexDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI, plLayerTex::kRefBasic, + + // UI + IDD_LAYER_BASIC, IDS_LAYER_BASIC, 0, 0, &gBasicDlgProc, + + // Usage + kBasicUsage, _T("usage"), TYPE_INT, 0, 0, + end, + + end +); +ParamBlockDesc2 *GetBasicBlk() { return &gBasicParamBlk; } + +static const char *kUsageTypes[] = +{ + "None", + "Base Texture", + "Detail", + "Grime", + "Map Blend", + "Highlight/Specular", + "Alpha Mask", + "Shadow/Light Map", + "Helper Object", + "Best Guess" +}; + +class BasicDlgProc : public ParamMap2UserDlgProc +{ +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IParamBlock2 *pb = map->GetParamBlock(); + + switch (msg) + { + case WM_INITDIALOG: + { + HWND hUsage = GetDlgItem(hWnd, IDC_USAGE_TYPE); + for (int i = 0; i < kUsageNumTypes; i++) + SendMessage(hUsage, CB_ADDSTRING, 0, (LPARAM)kUsageTypes[i]); + SendMessage(hUsage, CB_SETCURSEL, pb->GetInt(kBasicUsage), 0); + } + break; + + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case CBN_SELCHANGE: + switch (LOWORD(wParam)) + { + case IDC_USAGE_TYPE: + { + int cur = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); + if (LOWORD(wParam) == IDC_USAGE_TYPE) + pb->SetValue(kBasicUsage, t, cur); + return true; + } + break; + } + break; + } + break; + } + + return false; + } + void DeleteThis() {}; +}; +static BasicDlgProc gBasicDlgProc; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBasicPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBasicPB.h new file mode 100644 index 00000000..fa003ad7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBasicPB.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_LAYERTEXBASICPB_H +#define PL_LAYERTEXBASICPB_H + +// Param ID's +enum +{ + kBasicUsage, +}; + +// Usage types +enum +{ + kUsageNone, + kUsageBase, + kUsageDetail, + kUsageGrime, + kUsageTransition, + kUsageHighlight, + kUsageAlphaMask, + kUsageShadowLight, + kUsageHelper, + kUsageGuess, + + kUsageNumTypes +}; + +#endif //PL_LAYERTEXBASICPB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBitmapPB.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBitmapPB.cpp new file mode 100644 index 00000000..70806c2d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBitmapPB.cpp @@ -0,0 +1,751 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plLayerTex.h" +#include "plLayerTexBitmapPB.h" +#include "plDetailCurveCtrl.h" + +#if 1 +class BMTexPBAccessor; +extern BMTexPBAccessor bmtex_accessor; + +class BitmapDlgProc; +extern BitmapDlgProc gBitmapDlgProc; + +static ParamBlockDesc2 gBitmapParamBlk +( + plLayerTex::kBlkBitmap, _T("bitmap"), 0, GetLayerTexDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI, plLayerTex::kRefBitmap, + + IDD_LAYER_TEX, IDS_LAYER_TEX, 0, 0, &gBitmapDlgProc, + + // Bitmap + kBmpUseBitmap, _T("useBitmap"), TYPE_BOOL, 0, 0, + p_default, TRUE, + p_ui, TYPE_SINGLECHEKBOX, IDC_USE_BITMAP, + end, + kBmpBitmap, _T("bitmap"), TYPE_BITMAP, P_SHORT_LABELS, 0, + p_accessor, &bmtex_accessor, + end, + + // Crop/Place + kBmpApply, _T("apply"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_BM_CLIP, + end, + kBmpCropPlace, _T("cropPlace"), TYPE_INT, 0, 0, + p_default, 0, + p_range, 0, 1, + p_ui, TYPE_RADIO, 2, IDC_BM_CROP,IDC_BM_PLACE, + end, + kBmpClipU, _T("clipU"), TYPE_FLOAT, P_ANIMATABLE, IDS_BITMAP_CLIPU, + p_default, 0.0, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_CLIP_X, IDC_CLIP_XSPIN, 0.001f, + p_accessor, &bmtex_accessor, + end, + kBmpClipV, _T("clipV"), TYPE_FLOAT, P_ANIMATABLE, IDS_BITMAP_CLIPV, + p_default, 0.0, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_CLIP_Y, IDC_CLIP_YSPIN, 0.001f, + p_accessor, &bmtex_accessor, + end, + kBmpClipW, _T("clipW"), TYPE_FLOAT, P_ANIMATABLE, IDS_BITMAP_CLIPW, + p_default, 1.0, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_CLIP_W, IDC_CLIP_WSPIN, 0.001f, + p_accessor, &bmtex_accessor, + end, + kBmpClipH, _T("clipH"), TYPE_FLOAT, P_ANIMATABLE, IDS_BITMAP_CLIPH, + p_default, 1.0, + p_range, 0.0, 1.0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_CLIP_H, IDC_CLIP_HSPIN, 0.001f, + p_accessor, &bmtex_accessor, + end, + + // Texture Color/Alpha + kBmpDiscardColor, _T("discardColor"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_BLEND_NO_COLOR, + end, + kBmpInvertColor, _T("invertColor"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_BLEND_INV_COLOR, + end, + kBmpDiscardAlpha, _T("discardAlpha"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_DISCARD_ALPHA, + end, + kBmpInvertAlpha, _T("invertAlpha"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_BLEND_INV_ALPHA, + end, + + // Texture Quality + kBmpNonCompressed, _T("nonCompressed"),TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_FORCE_NONCOMPRESSED, + end, + kBmpScaling, _T("scaling"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_SCALE_ALL, IDC_SCALE_HALF, IDC_SCALE_NONE, + end, + + // Max Only + kBmpMonoOutput, _T("monoOutput"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 2, IDC_HSMAX_LAYER_RGBOUT, IDC_HSMAX_LAYER_ALPHAOUT, + end, + kBmpRGBOutput, _T("rgbOutput"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 2, IDC_HSMAX_LAYER_RGBOUT2, IDC_HSMAX_LAYER_ALPHAOUT2, + end, + + // Mipmap + kBmpNoFilter, _T("noFilter"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_NO_FILTERING, + end, + kBmpMipBlur, _T("mipBlur"), TYPE_FLOAT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_MIPBLUR_EDIT, IDC_MIPBLUR_SPIN, 0.4, + p_range, 0.01f, 100.0f, + p_default, 1.0, + end, + kBmpMipBias, _T("mipBias"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_USE_MIPBIAS, + p_enable_ctrls, 1, kBmpMipBiasAmt, + end, + kBmpMipBiasAmt, _T("mipBiasAmt"), TYPE_FLOAT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_MIPBIAS_EDIT, IDC_MIPBIAS_SPIN, 0.7, + p_range, -100.0, 100.0, + p_default, 1.0, + end, + + // Detail + kBmpUseDetail, _T("useDetail"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_USE_DETAIL, + p_default, FALSE, + p_enable_ctrls, 4, kBmpDetailStartSize, kBmpDetailStopSize, kBmpDetailStartOpac, kBmpDetailStopOpac, + p_accessor, &bmtex_accessor, + end, + + kBmpDetailStartSize,_T("dropOffStart"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_START_SIZE_EDIT, IDC_DETAIL_START_SIZE_SPIN, 0.4, + p_range, 0, 100, + p_default, 0, + p_accessor, &bmtex_accessor, + end, + kBmpDetailStopSize, _T("dropOffStop"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_STOP_SIZE_EDIT, IDC_DETAIL_STOP_SIZE_SPIN, 0.4, + p_range, 0, 100, + p_default, 100, + p_accessor, &bmtex_accessor, + end, + kBmpDetailStartOpac, _T("detailMax"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_START_OPAC_EDIT, IDC_DETAIL_START_OPAC_SPIN, 0.4, + p_range, 0, 100, + p_default, 100, + p_accessor, &bmtex_accessor, + end, + kBmpDetailStopOpac, _T("detailMin"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_STOP_OPAC_EDIT, IDC_DETAIL_STOP_OPAC_SPIN, 0.4, + p_range, 0, 100, + p_default, 0, + p_accessor, &bmtex_accessor, + end, + + kBmpExportWidth, _T("exportWidth"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_EXPORTWIDTH, IDC_EXPORTWIDTH_SPINNER, SPIN_AUTOSCALE, + p_range, 4, 2048, + p_default, 512, + end, + kBmpExportHeight, _T("exportHeight"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_EXPORTHEIGHT, IDC_EXPORTHEIGHT_SPINNER, SPIN_AUTOSCALE, + p_range, 4, 2048, + p_default, 512, + end, + kBmpExportLastWidth, _T("lastExportWidth"), TYPE_INT, 0, 0, + end, + kBmpExportLastHeight, _T("lastExportHeight"), TYPE_INT, 0, 0, + end, + + // Keep a sysmem copy at runtime (for image examination/manipulation). + kBmpNoDiscard, _T("noDiscard"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_NO_DISCARD, + p_default, FALSE, + end, + + end +); +ParamBlockDesc2 *GetBitmapBlk() { return &gBitmapParamBlk; } + +class BMCropper; + +class BMTexPBAccessor : public PBAccessor +{ +public: + void Set(PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + plLayerTex* layer = (plLayerTex*)owner; + + if(layer == NULL) return; + + IParamBlock2 *pb = layer->GetParamBlockByID(plLayerTex::kBlkBitmap); + + switch (id) + { + case kBmpBitmap: + if (pb->GetMap()) + pb->GetMap()->Invalidate(kBmpBitmap); + + // Update the bitmap saved by the layer + //layer->SetBitmap(&val.bm->bi, tabIndex); + break; + +/* + case kBmpFilename: + bmt->SetMapName(val.s); + break; + + case kBmpFiltering: + bmt->filterType = val.i; + if (bmt->thebm) + bmt->thebm->SetFilter(bmFilterType(val.i)); + break; +*/ + case kBmpClipU: + { + float u = val.f; + float w = pb->GetFloat(kBmpClipW, t); + if (u + w > 1.0f) + { + pb->SetValue(kBmpClipW, t, 1.0f-u); + if (pb->GetMap()) + pb->GetMap()->Invalidate(kBmpClipW); + } + break; + } + case kBmpClipW: + { + float w = val.f; + float u = pb->GetFloat(kBmpClipU, t); + if (u + w > 1.0f) + { + pb->SetValue(kBmpClipU, t, 1.0f-w); + if (pb->GetMap()) + pb->GetMap()->Invalidate(kBmpClipU); + } + break; + } + case kBmpClipV: + { + float v = val.f; + float h = pb->GetFloat(kBmpClipH, t); + if (v + h > 1.0f) + { + pb->SetValue(kBmpClipH, t, 1.0f-v); + if (pb->GetMap()) + pb->GetMap()->Invalidate(kBmpClipH); + } + break; + } + case kBmpClipH: + { + float h = val.f; + float v = pb->GetFloat(kBmpClipV, t); + if (v + h > 1.0f) + { + pb->SetValue(kBmpClipV, t, 1.0f-h); + if (pb->GetMap()) + pb->GetMap()->Invalidate(kBmpClipV); + } + break; + } + + case kBmpDetailStartSize: + case kBmpDetailStopSize: + case kBmpDetailStartOpac: + case kBmpDetailStopOpac: + if( pb != NULL ) + { + if( IIsProcSettingDetailValues( pb ) ) + break; // Ignore, since we're the ones setting 'em + + HWND dlg = pb->GetMap()->GetHWnd(); + plDetailCurveCtrl *ctrl = GET_DETAIL_CURVE_CTRL( dlg, IDC_DETAIL_CURVE_CTRL ); + if( ctrl != NULL ) + { + if( id == kBmpDetailStartSize || id == kBmpDetailStartOpac ) + ctrl->SetStartPoint( (float)pb->GetInt( kBmpDetailStartSize, t ) / 100.f, + (float)pb->GetInt( kBmpDetailStartOpac, t ) / 100.f ); + else + ctrl->SetEndPoint( (float)pb->GetInt( kBmpDetailStopSize, t ) / 100.f, + (float)pb->GetInt( kBmpDetailStopOpac, t ) / 100.f ); + } + + // Make sure start is less than end + if( id == kBmpDetailStartSize ) + { + int end = pb->GetInt( kBmpDetailStopSize, t ); + if( val.i > end ) + pb->SetValue( kBmpDetailStopSize, t, val.i ); + } + else if( id == kBmpDetailStopSize ) + { + int start = pb->GetInt( kBmpDetailStartSize, t ); + if( val.i < start ) + pb->SetValue( kBmpDetailStartSize, t, val.i ); + } + + } + break; + + case kBmpUseDetail: + if( pb != NULL ) + { + HWND dlg = pb->GetMap()->GetHWnd(); + EnableWindow( GetDlgItem( dlg, IDC_DETAIL_CURVE_CTRL ), (BOOL)val.i ); + } + break; + } + } + void Get(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid) + { + } + + // Gotta love hacks.... + bool IIsProcSettingDetailValues( IParamBlock2 *pb ); + +}; +static BMTexPBAccessor bmtex_accessor; + + +//========================================================================================= +// BMCropper +//========================================================================================= +class BMCropper : public CropCallback +{ + IParamBlock2 *fPBlock; + +public: + BMCropper(IParamBlock2* pblock) : fPBlock(pblock) {} + + float GetInitU() { return fPBlock->GetFloat(kBmpClipU); } + float GetInitV() { return fPBlock->GetFloat(kBmpClipV); } + float GetInitW() { return fPBlock->GetFloat(kBmpClipW); } + float GetInitH() { return fPBlock->GetFloat(kBmpClipH); } + BOOL GetInitMode() { return fPBlock->GetInt(kBmpCropPlace); } + void SetValues(float u, float v, float w, float h, BOOL md); + void OnClose(); +}; + +void BMCropper::SetValues(float u, float v, float w, float h, BOOL md) +{ + TimeValue t = GetCOREInterface()->GetTime(); + + if (u != fPBlock->GetFloat(kBmpClipU, t)) + { + fPBlock->SetValue(kBmpClipU, t, u); + fPBlock->GetMap()->Invalidate(kBmpClipU); + } + + if (v != fPBlock->GetFloat(kBmpClipV, t)) + { + fPBlock->SetValue(kBmpClipV, t, v); + fPBlock->GetMap()->Invalidate(kBmpClipV); + } + + if (w != fPBlock->GetFloat(kBmpClipW, t)) + { + fPBlock->SetValue(kBmpClipW, t, w); + fPBlock->GetMap()->Invalidate(kBmpClipW); + } + + if (h != fPBlock->GetFloat(kBmpClipH, t)) + { + fPBlock->SetValue(kBmpClipH, t, h); + fPBlock->GetMap()->Invalidate(kBmpClipH); + } + + if (md != fPBlock->GetInt(kBmpCropPlace)) + { + fPBlock->SetValue(kBmpCropPlace, t, md); + fPBlock->GetMap()->Invalidate(kBmpCropPlace); + } +} + +void BMCropper::OnClose() +{ + delete this; +} + +class BitmapDlgProc : public ParamMap2UserDlgProc +{ + friend class BMTexPBAccessor; + + + PBBitmap *fLastBMap; + bool fSettingDetailValues; + + + /// Called to update the controls of the dialog + /// Note: we're bad that we use a static here, but + virtual void Update( TimeValue t, Interval &valid, IParamMap2 *map ) + { + ICustButton *bmSelectBtn; + IParamBlock2 *pblock; + int width, height; + + + ParamMap2UserDlgProc::Update( t, valid, map ); + + if( fSettingDetailValues ) + { + // We're getting an update just because we changed detail values, so we + // know we don't have to do anything ourselves + return; + } + + pblock = map->GetParamBlock(); + + // Update texture map button + bmSelectBtn = GetICustButton( GetDlgItem( map->GetHWnd(), IDC_LAYER_NAME ) ); + PBBitmap *pbbm = pblock->GetBitmap( kBmpBitmap, t ); + if( pbbm ) + { + if( pbbm != fLastBMap ) + { + bmSelectBtn->SetText( (TCHAR *)pbbm->bi.Filename() ); + + // Init values for clamping spinners to powers of 2 + width = IFloorPow2( pbbm->bi.Width() ); + map->SetRange( kBmpExportWidth, 4.f, (float)width ); + + height = IFloorPow2( pbbm->bi.Height() ); + map->SetRange( kBmpExportHeight, 4.f, (float)height ); + + IClampTexSizeSpinner( t, map, true ); + ISetDetailCurveNumLevels( map, t ); + } + } + else if( pbbm != fLastBMap ) + bmSelectBtn->SetText( _T( "None" ) ); + + fLastBMap = pbbm; + + ReleaseICustButton( bmSelectBtn ); + + // Update detail curve control + HWND dlg = map->GetHWnd(); + + plDetailCurveCtrl *ctrl = GET_DETAIL_CURVE_CTRL( dlg, IDC_DETAIL_CURVE_CTRL ); + if( ctrl == NULL ) + { + // The control hasn't been created, so create it already! + HWND basis; + RECT r; + + // Create the detail map control + basis = GetDlgItem( dlg, IDC_DETAIL_SAMPLE ); + GetClientRect( basis, &r ); + MapWindowPoints( basis, dlg, (POINT *)&r, 2 ); + + ctrl = TRACKED_NEW plDetailCurveCtrl( dlg, IDC_DETAIL_CURVE_CTRL, &r ); + } + + EnableWindow( GetDlgItem( dlg, IDC_DETAIL_CURVE_CTRL ), (BOOL)pblock->GetInt( kBmpUseDetail, t ) ); + + if( ctrl != NULL ) + { + ctrl->SetStartPoint( (float)pblock->GetInt( kBmpDetailStartSize, t ) / 100.f, + (float)pblock->GetInt( kBmpDetailStartOpac, t ) / 100.f ); + ctrl->SetEndPoint( (float)pblock->GetInt( kBmpDetailStopSize, t ) / 100.f, + (float)pblock->GetInt( kBmpDetailStopOpac, t ) / 100.f ); + } + + } + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + static ICustButton* bmSelectBtn; + + switch (msg) + { + case WM_INITDIALOG: + fLastBMap = NULL; + fSettingDetailValues = false; + break; + + /// Note: the following *could* be done in the accessor, except that you end up in an + /// infinite loop updating the values. Not good. + case CC_SPINNER_CHANGE: + + if( LOWORD( wParam ) == IDC_EXPORTWIDTH_SPINNER ) + IClampTexSizeSpinner( t, map, true ); + + else if( LOWORD( wParam ) == IDC_EXPORTHEIGHT_SPINNER ) + IClampTexSizeSpinner( t, map, false ); + + break; + + // Message from the detail curve that a point got dragged + case PL_DC_POINT_DRAGGED: + { + plDetailCurveCtrl *ctrl = (plDetailCurveCtrl *)lParam; + IParamBlock2 *pblock = map->GetParamBlock(); + float x, y; + + + fSettingDetailValues = true; + + if( wParam == PL_DC_START_POINT ) + { + ctrl->GetStartPoint( x, y ); + pblock->SetValue( kBmpDetailStartSize, t, (int)( x * 100.f ) ); + pblock->SetValue( kBmpDetailStartOpac, t, (int)( y * 100.f ) ); + } + else + { + ctrl->GetEndPoint( x, y ); + pblock->SetValue( kBmpDetailStopSize, t, (int)( x * 100.f ) ); + pblock->SetValue( kBmpDetailStopOpac, t, (int)( y * 100.f ) ); + } + + map->UpdateUI( t ); + fSettingDetailValues = false; + } + return 0; + + case WM_COMMAND: + if( HIWORD( wParam ) == EN_CHANGE && LOWORD( wParam ) == IDC_EXPORTWIDTH ) + IClampTexSizeSpinner( t, map, true ); + + else if( HIWORD( wParam ) == EN_CHANGE && LOWORD( wParam ) == IDC_EXPORTHEIGHT ) + IClampTexSizeSpinner( t, map, false ); + + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_BM_CROP_IMAGE) + { + IParamBlock2 *pblock = map->GetParamBlock(); + PBBitmap *pbbm = pblock->GetBitmap(kBmpBitmap, t); + if (pbbm) + { + if (!pbbm->bm) + pbbm->bm = TheManager->Load(&pbbm->bi); + + BMCropper *cropper = TRACKED_NEW BMCropper(pblock); + + pbbm->bm->Display("Specify Cropping/Placement", BMM_CN, FALSE, TRUE, cropper); + } + // bm->DeleteThis(); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_LAYER_RELOAD) + { + // TEMP + IParamBlock2 *pblock = map->GetParamBlock(); + PBBitmap *pbbm = pblock->GetBitmap(kBmpBitmap, t); + if (pbbm) + { + plLayerTex *layer = (plLayerTex*)map->GetParamBlock()->GetOwner(); + + layer->RefreshBitmaps(); + + layer->fMtlParams->MtlChanged(); + layer->IChanged(); + } + return TRUE; + } + else if (LOWORD(wParam) == IDC_LAYER_NAME) + { + plPlasmaMAXLayer *layer = (plPlasmaMAXLayer *)map->GetParamBlock()->GetOwner(); + if (layer == nil) + return FALSE; + BOOL selectedNewBitmap = layer->HandleBitmapSelection(); + + if(selectedNewBitmap) + { + IParamBlock2 *pblock = map->GetParamBlock(); + //plLayerTex *layer = (plLayerTex*)map->GetParamBlock()->GetOwner(); + + //layer->SetBitmap(&bi); + //layer->IChanged(); + //BitmapInfo *bi = &layer->GetPBBitmap()->bi; + + bmSelectBtn = GetICustButton(GetDlgItem(hWnd,IDC_LAYER_NAME)); + PBBitmap *pbbm = layer->GetPBBitmap(); + bmSelectBtn->SetText(pbbm != nil ? (TCHAR*)pbbm->bi.Filename() : ""); + ReleaseICustButton(bmSelectBtn); + + if (pbbm != nil) + { + // Init values for clamping spinners to powers of 2 + int width = IFloorPow2( pbbm->bi.Width() ); + map->SetRange( kBmpExportWidth, 4.f, (float)width ); + + int height = IFloorPow2( pbbm->bi.Height() ); + map->SetRange( kBmpExportHeight, 4.f, (float)height ); + + if( width > 512 ) + { + height = (int)( 512.f * (float)( (float)height / (float)width ) ); + width = 512; + } + else if( height > 512 ) + { + width = (int)( 512.f * (float)( (float)width / (float)height ) ); + height = 512; + } + pblock->SetValue( kBmpExportWidth, t, width ); + pblock->SetValue( kBmpExportLastWidth, t, width ); + pblock->SetValue( kBmpExportHeight, t, height ); + pblock->SetValue( kBmpExportLastHeight, t, height ); + + IClampTexSizeSpinner( t, map, true ); + } + return TRUE; + } + else + { + return FALSE; + } + } + break; + } + + return FALSE; + } + void DeleteThis() {}; + + void ISetDetailCurveNumLevels( IParamMap2 *map, TimeValue t ) + { + /// Set the level count on the detail control + plDetailCurveCtrl *ctrl = GET_DETAIL_CURVE_CTRL( map->GetHWnd(), IDC_DETAIL_CURVE_CTRL ); + if( ctrl != NULL ) + { + IParamBlock2 *pblock = map->GetParamBlock(); + int w = pblock->GetInt( kBmpExportWidth, t ); + int h = pblock->GetInt( kBmpExportHeight, t ); + int numLevels = 0; + while( w > 1 && h > 1 ) + { + w >>= 1; + h >>= 1; + numLevels++; + } + ctrl->SetNumLevels( numLevels ); + } + } + + /// Clamp texture sizes to a power of 2 + void IClampTexSizeSpinner( TimeValue t, IParamMap2 *map, bool clampWidth ) + { + IParamBlock2 *pblock = map->GetParamBlock(); + ParamID clampNew, clampOld; + ParamID otherNew, otherOld; + + if( clampWidth ) + { + clampNew = kBmpExportWidth; clampOld = kBmpExportLastWidth; + otherNew = kBmpExportHeight; otherOld = kBmpExportLastHeight; + } + else + { + clampNew = kBmpExportHeight; clampOld = kBmpExportLastHeight; + otherNew = kBmpExportWidth; otherOld = kBmpExportLastWidth; + } + + int lastVal = pblock->GetInt( clampOld, t ); + int tempVal, newVal = pblock->GetInt( clampNew, t ); + + if( newVal < lastVal ) + { + lastVal = newVal; + for( tempVal = 1; tempVal <= newVal; tempVal <<= 1 ); + newVal = tempVal >> 1; + } + else + { + lastVal = newVal; + for( tempVal = 1; tempVal < newVal; tempVal <<= 1 ); + newVal = tempVal; + } + + pblock->SetValue( clampNew, t, newVal ); + pblock->SetValue( clampOld, t, newVal ); + + // And clamp aspect ratio + PBBitmap *pbbm = pblock->GetBitmap( kBmpBitmap, t ); + + if( pbbm != NULL ) + { + int realWidth = pbbm->bi.Width(); + int realHeight = pbbm->bi.Height(); + + float aspect; + if( clampWidth ) + aspect = (float)realHeight / (float)realWidth; + else + aspect = (float)realWidth / (float)realHeight; + + int value = newVal; + value *= aspect; + + if( value < 4 ) + { + // Can't be below 4! + value = 4; + pblock->SetValue( otherNew, t, value ); + pblock->SetValue( otherOld, t, value ); + value = value / aspect; + pblock->SetValue( clampNew, t, value ); + pblock->SetValue( clampOld, t, value ); + } + else + { + pblock->SetValue( otherNew, t, value ); + pblock->SetValue( otherOld, t, value ); + } + } + + ISetDetailCurveNumLevels( map, t ); + } + + int IFloorPow2( int value ) + { + int v; + + + for( v = 1; v <= value; v <<= 1 ); + return v >> 1; + } + +}; + +static BitmapDlgProc gBitmapDlgProc; + + +// Gotta love hacks.... +bool BMTexPBAccessor::IIsProcSettingDetailValues( IParamBlock2 *pb ) +{ + BitmapDlgProc *proc = (BitmapDlgProc *)pb->GetMap()->GetUserDlgProc(); + if( proc != NULL ) + return proc->fSettingDetailValues; + + return false; +} + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h new file mode 100644 index 00000000..70dcb531 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_LAYERTEXBITMAPPB_H +#define PL_LAYERTEXBITMAPPB_H + +// Param ID's +enum +{ + kBmpUseBitmap, + kBmpBitmap, + + // Cropping/Placement + kBmpApply, + kBmpCropPlace, + kBmpClipU, + kBmpClipV, + kBmpClipW, + kBmpClipH, + + // Misc + kBmpDiscardColor, + kBmpInvertColor, + kBmpDiscardAlpha, + kBmpInvertAlpha, + + // Texture quality + kBmpNonCompressed, + kBmpScaling, + + // Mipmap + kBmpNoFilter, + kBmpMipBlur, + kBmpMipBias, + kBmpMipBiasAmt, + + // Max only + kBmpMonoOutput, + kBmpRGBOutput, + + // Detail + kBmpUseDetail, + kBmpDetailStartSize, + kBmpDetailStopSize, + kBmpDetailStartOpac, + kBmpDetailStopOpac, + + // New export size controls + kBmpExportWidth, + kBmpExportHeight, + kBmpExportLastWidth, // Annoying fields, these two, but they're necessary + kBmpExportLastHeight, // for clamping the spinners to powers of 2 + + // Keep a sysmem copy of the texture + kBmpNoDiscard +}; + +enum +{ + kScalingAny, + kScalingHalf, + kScalingNone +}; + +#endif //PL_LAYERTEXBITMAPPB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plMAXCameraLayer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plMAXCameraLayer.cpp new file mode 100644 index 00000000..0396d5b5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plMAXCameraLayer.cpp @@ -0,0 +1,399 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" + +#include "plMAXCameraLayer.h" + +#include "iparamb2.h" +#include "iparamm2.h" +#include "stdmat.h" + +#include "plBMSampler.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +class plMAXCameraLayerClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading = FALSE) { return TRACKED_NEW plMAXCameraLayer(); } + const TCHAR* ClassName() { return GetString(IDS_MAX_CAMERA_LAYER); } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + Class_ID ClassID() { return MAX_CAMERA_LAYER_CLASS_ID; } + const TCHAR* Category() { return TEXMAP_CAT_COLMOD; } + const TCHAR* InternalName() { return _T("PlasmaMAXCameraLayer"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plMAXCameraLayerClassDesc plMAXCameraLayerDesc; +ClassDesc2* GetMAXCameraLayerDesc() { return &plMAXCameraLayerDesc; } + +class MAXCameraLayerDlgProc : public ParamMap2UserDlgProc +{ +public: + MAXCameraLayerDlgProc() {} + ~MAXCameraLayerDlgProc() {} + + void UpdateDisplay(IParamMap2 *pmap) + { + HWND hWnd = pmap->GetHWnd(); + IParamBlock2 *pb = pmap->GetParamBlock(); + HWND cbox; + cbox = GetDlgItem(hWnd, IDC_CAM_LAYER_UV_SRC); + SendMessage(cbox, CB_SETCURSEL, pb->GetInt(plMAXCameraLayer::kUVSource), 0); + hsBool reflect = (pb->GetInt(ParamID(plMAXCameraLayer::kExplicitCam)) == 0); + EnableWindow(GetDlgItem(hWnd, IDC_CAM_LAYER_UV_SRC), !reflect); + } + + virtual void Update(TimeValue t, Interval& valid, IParamMap2* pmap) { UpdateDisplay(pmap); } + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + int id = LOWORD(wParam); + int code = HIWORD(wParam); + + IParamBlock2 *pb = map->GetParamBlock(); + HWND cbox = NULL; + + switch (msg) + { + case WM_INITDIALOG: + int i; + for (i = 0; i < plMAXCameraLayer::kMaxUVSrc; i++) + { + cbox = GetDlgItem(hWnd, IDC_CAM_LAYER_UV_SRC); + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)plMAXCameraLayer::kUVStrings[i]); + } + UpdateDisplay(map); + return TRUE; + + case WM_COMMAND: + if (id == IDC_CAM_LAYER_UV_SRC) + { + pb->SetValue(plMAXCameraLayer::kUVSource, t, SendMessage(GetDlgItem(hWnd, id), CB_GETCURSEL, 0, 0)); + return TRUE; + } + else if (id == IDC_CAM_LAYER_EXPLICIT_CAM) + { + UpdateDisplay(map); + return TRUE; + } + break; + } + return FALSE; + } + void DeleteThis() {} +}; +static MAXCameraLayerDlgProc gMAXCameraLayerDlgProc; + +/////////////////////////////////////////////////////////////////////////////// +//// ParamBlock Definition //////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ParamBlockDesc2 gMAXCameraLayerParamBlk +( + plMAXCameraLayer::kBlkMain, _T("CamLayer"), 0, GetMAXCameraLayerDesc(), + P_AUTO_CONSTRUCT + P_AUTO_UI, plMAXCameraLayer::kRefMain, + IDD_MAX_CAMERA_LAYER, IDS_MAX_CAMERA_LAYER_PROPS, 0, 0, &gMAXCameraLayerDlgProc, + + plMAXCameraLayer::kCamera, _T("camera"), TYPE_INODE, P_CAN_CONVERT, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_CAM_LAYER_CAMERA, + p_classID, Class_ID(LOOKAT_CAM_CLASS_ID, 0), + p_prompt, IDS_CAM_LAYER_CAMERA, + end, + + plMAXCameraLayer::kUVSource, _T("UVSource"), TYPE_INT, 0, 0, + p_default, 0, + end, + + plMAXCameraLayer::kExplicitCam, _T("explicitCam"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CAM_LAYER_EXPLICIT_CAM, + p_default, false, + p_enable_ctrls, 1, plMAXCameraLayer::kCamera, + end, + + plMAXCameraLayer::kRootNode, _T("rootNode"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_CAM_LAYER_ROOT_NODE, + p_prompt, IDS_CAM_LAYER_ROOT_NODE, + end, + + plMAXCameraLayer::kDisableColor, _T("disableColor"), TYPE_RGBA, 0, 0, + p_ui, TYPE_COLORSWATCH, IDC_CAM_LAYER_DISABLE_COLOR, + p_default, Color(0,0,0), + end, + + plMAXCameraLayer::kForce, _T("force"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CAM_LAYER_FORCE, + p_default, false, + end, + + end +); + +///////////////////////////////////////////////////////////////////////////// + +const char *plMAXCameraLayer::kUVStrings[] = { "1", "2", "3", "4", "5", "6", "7", "8" }; +const UInt8 plMAXCameraLayer::kMaxUVSrc = 8; + +plMAXCameraLayer::plMAXCameraLayer() : +fParmsPB(NULL), +fIValid(NEVER) +{ + plMAXCameraLayerDesc.MakeAutoParamBlocks(this); +} + +plMAXCameraLayer::~plMAXCameraLayer() +{ +} + +//From MtlBase +void plMAXCameraLayer::Reset() +{ + GetMAXCameraLayerDesc()->Reset(this, TRUE); // reset all pb2's + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); + + fIValid.SetEmpty(); +} + +void plMAXCameraLayer::Update(TimeValue t, Interval& valid) +{ + if (!fIValid.InInterval(t)) + { + fIValid.SetInfinite(); + } + + valid &= fIValid; +} + +Interval plMAXCameraLayer::Validity(TimeValue t) +{ + Interval v = FOREVER; + return v; +} + +ParamDlg* plMAXCameraLayer::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + IAutoMParamDlg* masterDlg = plMAXCameraLayerDesc.CreateParamDlgs(hwMtlEdit, imp, this); + + return masterDlg; +} + +BOOL plMAXCameraLayer::SetDlgThing(ParamDlg* dlg) +{ + return FALSE; +} + +int plMAXCameraLayer::NumRefs() +{ + return 1; +} + +//From ReferenceMaker +RefTargetHandle plMAXCameraLayer::GetReference(int i) +{ + switch (i) + { + case kRefMain: return fParmsPB; + default: return NULL; + } +} + +void plMAXCameraLayer::SetReference(int i, RefTargetHandle rtarg) +{ + Interval garbage; + + switch (i) + { + case kRefMain: + fParmsPB = (IParamBlock2 *)rtarg; + break; + } +} + +int plMAXCameraLayer::NumParamBlocks() +{ + return 1; +} + +IParamBlock2* plMAXCameraLayer::GetParamBlock(int i) +{ + switch (i) + { + case 0: return fParmsPB; + default: return NULL; + } +} + +IParamBlock2* plMAXCameraLayer::GetParamBlockByID(BlockID id) +{ + if (fParmsPB->ID() == id) + return fParmsPB; + else + return NULL; +} + +//From ReferenceTarget +RefTargetHandle plMAXCameraLayer::Clone(RemapDir &remap) +{ + plMAXCameraLayer *mnew = TRACKED_NEW plMAXCameraLayer(); + *((MtlBase*)mnew) = *((MtlBase*)this); // copy superclass stuff + mnew->ReplaceReference(kRefMain, remap.CloneRef(fParmsPB)); + BaseClone(this, mnew, remap); + return (RefTargetHandle)mnew; +} + +int plMAXCameraLayer::NumSubs() +{ + return 1; +} + +Animatable* plMAXCameraLayer::SubAnim(int i) +{ + switch (i) + { + case kRefMain: return fParmsPB; + default: return NULL; + } +} + +TSTR plMAXCameraLayer::SubAnimName(int i) +{ + switch (i) + { + case kRefMain: return "Main"; + default: return ""; + } +} + +RefResult plMAXCameraLayer::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message) +{ + switch (message) + { + case REFMSG_CHANGE: + { + fIValid.SetEmpty(); + + if (hTarget == fParmsPB) + { + // see if this message came from a changing parameter in the pblock, + // if so, limit rollout update to the changing item + ParamID changingParam = fParmsPB->LastNotifyParamID(); + fParmsPB->GetDesc()->InvalidateUI(changingParam); + + if (changingParam != -1) + IChanged(); + } + } + break; + + } + + return REF_SUCCEED; +} + +void plMAXCameraLayer::IChanged() +{ + // Cut and paste insanity from DynamicTextLayer. + // Texture wasn't getting updated in the viewports, and this fixes it. + // Don't know if it's the right way though. + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); + + // And this is so the SceneWatcher gets notified that the material on some of it's + // referenced objects changed. + NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_MAT); +} + +#define TEX_HDR_CHUNK 0x5000 + +IOResult plMAXCameraLayer::Save(ISave *isave) +{ + IOResult res; + + isave->BeginChunk(TEX_HDR_CHUNK); + res = MtlBase::Save(isave); + if (res != IO_OK) + return res; + isave->EndChunk(); + + return IO_OK; +} + +IOResult plMAXCameraLayer::Load(ILoad *iload) +{ + IOResult res; + while (IO_OK == (res = iload->OpenChunk())) + { + if (iload->CurChunkID() == TEX_HDR_CHUNK) + { + res = MtlBase::Load(iload); + } + iload->CloseChunk(); + if (res != IO_OK) + return res; + } + + return IO_OK; +} + + +AColor plMAXCameraLayer::EvalColor(ShadeContext& sc) +{ + return AColor(0.0f, 0.0f, 0.0f, 1.0f); +} + +float plMAXCameraLayer::EvalMono(ShadeContext& sc) +{ + return Intens(EvalColor(sc)); +} + +Point3 plMAXCameraLayer::EvalNormalPerturb(ShadeContext& sc) +{ + // Return the perturbation to apply to a normal for bump mapping + return Point3(0, 0, 0); +} + +ULONG plMAXCameraLayer::LocalRequirements(int subMtlNum) +{ + return MTLREQ_VIEW_DEP | MTLREQ_TRANSP; +} + +void plMAXCameraLayer::ActivateTexDisplay(BOOL onoff) +{ +} + +BITMAPINFO *plMAXCameraLayer::GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono, BOOL forceW, BOOL forceH) +{ + return nil; +} + +DWORD plMAXCameraLayer::GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker) +{ + return 0; +} + +const char *plMAXCameraLayer::GetTextureName( int which ) +{ + return NULL; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plMAXCameraLayer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plMAXCameraLayer.h new file mode 100644 index 00000000..e7933ba7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plMAXCameraLayer.h @@ -0,0 +1,145 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plMAXCameraLayer_inc +#define plMAXCameraLayer_inc + +#include "Max.h" +#include "../resource.h" +#include "plPlasmaMAXLayer.h" + +class ClassDesc2; +class IParamBlock2; + +ClassDesc2* GetMAXCameraLayerDesc(); + +extern TCHAR *GetString(int id); +extern HINSTANCE hInstance; + +class plMAXCameraLayer : public plPlasmaMAXLayer +{ +protected: + // Parameter block + IParamBlock2* fParmsPB; + Interval fIValid; + +public: + // Ref nums + enum + { + kRefMain + }; + + // Block ID's + enum + { + kBlkMain + }; + + plMAXCameraLayer(); + ~plMAXCameraLayer(); + void DeleteThis() { delete this; } + + //From MtlBase + ParamDlg* CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + BOOL SetDlgThing(ParamDlg* dlg); + void Update(TimeValue t, Interval& valid); + void Reset(); + Interval Validity(TimeValue t); + ULONG LocalRequirements(int subMtlNum); + + //From Texmap + RGBA EvalColor(ShadeContext& sc); + float EvalMono(ShadeContext& sc); + Point3 EvalNormalPerturb(ShadeContext& sc); + + // For displaying textures in the viewport + BOOL SupportTexDisplay() { return FALSE; } + void ActivateTexDisplay(BOOL onoff); + BITMAPINFO *GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono=FALSE, int forceW=0, int forceH=0); + DWORD GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker); + +protected: + void ICacheCosines(); + void IChanged(); + void IDiscardTexHandle(); + +public: + + int SubNumToRefNum(int subNum) { return subNum; } + virtual BOOL DiscardColor() { return true; } + + // Loading/Saving + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + //From Animatable + Class_ID ClassID() { return MAX_CAMERA_LAYER_CLASS_ID; } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + void GetClassName(TSTR& s) { s = GetString(IDS_MAX_CAMERA_LAYER); } + + RefTargetHandle Clone( RemapDir &remap ); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + + int NumParamBlocks(); // return number of ParamBlocks in this instance + IParamBlock2* GetParamBlock(int i); // return i'th ParamBlock + IParamBlock2* GetParamBlockByID(BlockID id); // return id'd ParamBlock + + const char *GetTextureName( int which ); + + // ParamBlock accessors + enum + { + kCamera, + kUVSource, + kExplicitCam, + kRootNode, + kDisableColor, + kForce, + }; + + static const char *kUVStrings[]; + static const UInt8 kMaxUVSrc; + + // Pure virtual accessors for the various bitmap related elements + virtual Bitmap *GetMaxBitmap(int index = 0) { hsAssert(false, "Function call not valid on this type of layer."); return nil; } + virtual PBBitmap *GetPBBitmap(int index = 0) { hsAssert(false, "Function call not valid on this type of layer."); return nil; } + virtual int GetNumBitmaps(void) { return 0; } + +protected: + virtual void ISetMaxBitmap(Bitmap *bitmap, int index = 0) { hsAssert(false, "Function call not valid on this type of layer."); } + virtual void ISetPBBitmap(PBBitmap *pbbm, int index = 0) { hsAssert(false, "Function call not valid on this type of layer."); } +}; + +#endif // plMAXCameraLayer_inc diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plPlasmaMAXLayer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plPlasmaMAXLayer.cpp new file mode 100644 index 00000000..d913bcfd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plPlasmaMAXLayer.cpp @@ -0,0 +1,442 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plPlasmaMAXLayer - MAX Layer type that is the basis for all Plasma layer // +// types // +// Note: All export-side functions are contained in // +// MaxConvert/plPlasmaMaxLayerExport.cpp, for linking purposes. // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 1.13.2002 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plPlasmaMAXLayer.h" + +#include "stdmat.h" +#include "istdplug.h" +#include "iparamb2.h" +#include "iparamm2.h" +#include "resource.h" +#include "../../AssetMan/PublicInterface/MaxAssInterface.h" + +#include "hsUtils.h" +#include "../pnKeyedObject/hsKeyedObject.h" +#include "../pnMessage/plRefMsg.h" +#include "../plSurface/plLayerInterface.h" +#include "hsResMgr.h" + + +//// Derived Types List /////////////////////////////////////////////////////// +// If you create a new Plasma layer type, add a define for the class ID in +// the header and add it to the list here. + +const Class_ID plPlasmaMAXLayer::fDerivedTypes[] = +{ + LAYER_TEX_CLASS_ID, + STATIC_ENV_LAYER_CLASS_ID, + DYNAMIC_ENV_LAYER_CLASS_ID, + DYN_TEXT_LAYER_CLASS_ID, + ANGLE_ATTEN_LAYER_CLASS_ID, + MAX_CAMERA_LAYER_CLASS_ID +}; + +//// Constructor/Destructor /////////////////////////////////////////////////// + +plPlasmaMAXLayer::plPlasmaMAXLayer() +{ + fConversionTargets = nil; +} + +plPlasmaMAXLayer::~plPlasmaMAXLayer() +{ +} + +//// GetPlasmaMAXLayer //////////////////////////////////////////////////////// +// Static function that checks the classID of the given texMap and, if it's a +// valid Plasma MAX Layer, returns a pointer to such. + +plPlasmaMAXLayer *plPlasmaMAXLayer::GetPlasmaMAXLayer( Texmap *map ) +{ + if (!map) + return NULL; + + int i; + + + for( i = 0; i < sizeof( fDerivedTypes ) / sizeof( Class_ID ); i++ ) + { + if( map->ClassID() == fDerivedTypes[ i ] ) + return (plPlasmaMAXLayer *)map; + } + + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Conversion Targets /////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +//// plLayerTargetContainer /////////////////////////////////////////////////// +// This is a helper class that just contains a passive ref list of the layers +// that are our conversion targets at export time. See, it's possible that a +// layer gets converted, added to the target list, then destroyed as the +// parent material is suddenly thrown away. In order to avoid our pointers +// from being trashed (or keeping active refs on the layers when they're not +// actually used), we have a small helper class that just keep passive refs, +// so when one of them goes away, we get a notify about it. + +class plLayerTargetContainer : public hsKeyedObject +{ + static UInt32 fKeyCount; + + public: + hsTArray fLayers; + + virtual hsBool MsgReceive( plMessage *msg ) + { + plGenRefMsg *ref = plGenRefMsg::ConvertNoRef( msg ); + if( ref != nil ) + { + if( ref->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) ) + fLayers[ ref->fWhich ] = plLayerInterface::ConvertNoRef( ref->GetRef() ); + else + fLayers[ ref->fWhich ] = nil; + } + + return hsKeyedObject::MsgReceive( msg ); + } + + plLayerTargetContainer() + { + char str[ 512 ]; + + + sprintf( str, "plLayerTargetContainer-%d", fKeyCount++ ); + hsgResMgr::ResMgr()->NewKey( str, this, plLocation::kGlobalFixedLoc ); + } +}; + +UInt32 plLayerTargetContainer::fKeyCount = 0; + + +void plPlasmaMAXLayer::IAddConversionTarget( plLayerInterface *target ) +{ + if( fConversionTargets == nil ) + { + // Create us a new container + fConversionTargets = TRACKED_NEW plLayerTargetContainer; + fConversionTargets->GetKey()->RefObject(); + } + + fConversionTargets->fLayers.Append( target ); + hsgResMgr::ResMgr()->AddViaNotify( target->GetKey(), + new plGenRefMsg( fConversionTargets->GetKey(), plRefMsg::kOnCreate, + fConversionTargets->fLayers.GetCount() - 1, 0 ), + plRefFlags::kPassiveRef ); +} + +void plPlasmaMAXLayer::IClearConversionTargets( void ) +{ + if( fConversionTargets != nil ) + { + fConversionTargets->GetKey()->UnRefObject(); + fConversionTargets = nil; + } +} + +int plPlasmaMAXLayer::GetNumConversionTargets( void ) +{ + if( fConversionTargets == nil ) + return 0; + + + int i, count = 0; + for( i = 0; i < fConversionTargets->fLayers.GetCount(); i++ ) + { + if( fConversionTargets->fLayers[ i ] != nil ) + count++; + } + return count; +} + +plLayerInterface *plPlasmaMAXLayer::GetConversionTarget( int index ) +{ + if( fConversionTargets == nil ) + return nil; + + int i; + for( i = 0; i < fConversionTargets->fLayers.GetCount(); i++ ) + { + if( fConversionTargets->fLayers[ i ] != nil ) + { + if( index == 0 ) + return fConversionTargets->fLayers[ i ]; + index--; + } + } + + return nil; +} + +/////////////////////////////////////////////////////////////////////////////// +//// Asset Management, and textures /////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void plPlasmaMAXLayer::SetBitmapAssetId(jvUniqueId& assetId, int index /* = 0 */) +{ + PBBitmap *pbbm = GetPBBitmap(index); + if (pbbm && GetMaxAssInterface()) + { + char buf[20]; + GetMaxAssInterface()->UniqueIdToString(assetId, buf); + pbbm->bi.SetDevice(buf); + } +} + +void plPlasmaMAXLayer::GetBitmapAssetId(jvUniqueId& assetId, int index /* = 0 */) +{ + PBBitmap *pbbm = GetPBBitmap(index); + if (pbbm && GetMaxAssInterface()) + assetId = GetMaxAssInterface()->StringToUniqueId(pbbm->bi.Device()); + else + assetId.SetEmpty(); +} + +void plPlasmaMAXLayer::SetBitmap(BitmapInfo *bi, int index) +{ + jvUniqueId targetAssetId; + GetBitmapAssetId(targetAssetId, index); + + Bitmap *BM = GetMaxBitmap(index); + if (BM) + { + BM->DeleteThis(); + BM = NULL; + } + + if (bi) + { + if (!targetAssetId.IsEmpty()) + { + // If this texture has an assetId, we will check the + // asset database and make sure we have the latest version + // of the texture file before loading it + MaxAssInterface* assInterface = GetMaxAssInterface(); + if (assInterface) + { + char buf[20]; + assInterface->UniqueIdToString(targetAssetId, buf); + bi->SetDevice(buf); + + const char* filename = bi->Name(); + // Download the latest version and retrieve the filename + char newfilename[MAX_PATH]; + if (assInterface->GetLatestVersionFile(targetAssetId, newfilename, sizeof(newfilename))) + { + // If the filename has changed, we have to reset the bitmap in the ParamBlock + if(stricmp(filename, newfilename) != 0) + bi->SetName(newfilename); + } + } + } + + BMMRES result; + BM = TheManager->Load(bi, &result); + if (result == BMMRES_SUCCESS) + ISetMaxBitmap(BM, index); + else + ISetMaxBitmap(NULL, index); + + // The load may have failed, but we still want to set the paramblock. We + // don't want to modify the layer if we're just missing the file. + PBBitmap pbBitmap(*bi); + ISetPBBitmap(&pbBitmap, index); + } + else + { + ISetMaxBitmap(NULL, index); + ISetPBBitmap(NULL, index); + } + +/* + Bitmap *BM = GetMaxBitmap(index); + + if (BM) + { + BM->DeleteThis(); + BM = NULL; + } + + if (filename) + { + BitmapInfo bi; + bi.SetName(filename); + + // If this texture has an assetId, get the latest version from AssetMan before loading it + if (assetId && !assetId->IsEmpty()) + { + MaxAssInterface* maxAssInterface = GetMaxAssInterface(); + if (maxAssInterface) + { + // Download the latest version and retrieve the filename + char newfilename[MAX_PATH]; + if (maxAssInterface->GetLatestVersionFile(*assetId, newfilename, sizeof(newfilename))) + { + // If the filename has changed, we have to reset the bitmap in the ParamBlock + if (stricmp(filename, newfilename) != 0) + { + bi.SetName(newfilename); + } + } + } + } + + ISetMaxBitmap(TheManager->Load(&bi)); + + PBBitmap pbBitmap(bi); +// TheManager->LoadInto(&pbBitmap.bi, &pbBitmap.bm, TRUE); + ISetPBBitmap(&pbBitmap, index); + + if (assetId) + SetBitmapAssetId(*assetId, index); + } + else + { + ISetMaxBitmap(NULL, index); + ISetPBBitmap(NULL, index); + } + + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +*/ +} + +//// RefreshBitmaps /////////////////////////////////////////////////////////// +// Makes sure the bitmap asset is the latest from AssetMan, if we're using it. + +void plPlasmaMAXLayer::RefreshBitmaps() +{ + int i, count = GetNumBitmaps(); + + for( i = 0; i < count; i++ ) + { + PBBitmap *pbbm = GetPBBitmap(i); + if (pbbm) + { + SetBitmap(&pbbm->bi, i); + } + } +} + +//// GetBitmapFileName //////////////////////////////////////////////////////// +// Returns the filename of the ith bitmap. Makes sure we have the latest +// version from assetMan as well, if applicable. + +hsBool plPlasmaMAXLayer::GetBitmapFileName( char *destFilename, int maxLength, int index /* = 0 */ ) +{ + jvUniqueId targetAssetId; + GetBitmapAssetId(targetAssetId, index); + + MaxAssInterface* maxAssInterface = GetMaxAssInterface(); + if (maxAssInterface != nil && !targetAssetId.IsEmpty()) + { + // Download the latest version and retrieve the filename + if (maxAssInterface->GetLatestVersionFile(targetAssetId, destFilename, maxLength)) + return true; + } + + // Normal return + if( GetPBBitmap( index ) == nil ) + return false; + + strncpy( destFilename, GetPBBitmap( index )->bi.Name(), maxLength ); + return true; +} + +BOOL plPlasmaMAXLayer::HandleBitmapSelection(int index /* = 0 */) +{ + static ICustButton* bmSelectBtn; + + PBBitmap *pbbm = GetPBBitmap( index ); + + MaxAssInterface* maxAssInterface = GetMaxAssInterface(); + + // If the control key is held, we want to get rid of this texture + if ((GetKeyState(VK_CONTROL) & 0x8000) && pbbm != nil) + { + char msg[512]; + sprintf(msg, "Are you sure you want to change this bitmap from %s to (none)?", pbbm->bi.Name()); + if (hsMessageBox(msg, "Remove texture?", hsMessageBoxYesNo) == hsMBoxYes) + { + SetBitmap(nil, index); + return TRUE; + } + return FALSE; + } + // if we have the assetman plug-in, then try to use it, unless shift is held down + else if(maxAssInterface && !(GetKeyState(VK_SHIFT) & 0x8000)) + { + jvUniqueId assetId; + GetBitmapAssetId(assetId, index); + + char filename[MAX_PATH]; + if (maxAssInterface->OpenBitmapDlg(assetId, filename, sizeof(filename))) + { + SetBitmapAssetId(assetId, index); + + BitmapInfo bi; + bi.SetName(filename); + SetBitmap(&bi, index); + return TRUE; + } + } + else + { + BitmapInfo bi; + if( pbbm != NULL ) + bi.SetName( pbbm->bi.Name() ); + + BOOL selectedNewBitmap = TheManager->SelectFileInput(&bi, + GetCOREInterface()->GetMAXHWnd(), + _T("Select Bitmap Image File")); + + if (selectedNewBitmap) + { + // Set the assetId to empty so our new, unmanaged texture will take + jvUniqueId emptyId; + SetBitmapAssetId(emptyId, index); + + SetBitmap(&bi, index); + return TRUE; + } + } + + return FALSE; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h new file mode 100644 index 00000000..d826618b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h @@ -0,0 +1,149 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plPlasmaMAXLayer - MAX Layer type that is the basis for all Plasma layer // +// types // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 1.13.2002 mcn - Created. // +// // +//// Notes from the Author //////////////////////////////////////////////////// +// // +// This base class is actually a quite-recent addition. As a result, most // +// of the old, non-base-class-structured code is still lying around. This // +// code will be slowly converted over as time goes on; the theory was that // +// this conversion would be far more likely to occur if the base class // +// actually already existed. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plPlasmaMAXLayer_h +#define _plPlasmaMAXLayer_h + +#include "Max.h" +#include "hsTypes.h" + +//// Derived Type Class IDs /////////////////////////////////////////////////// +// If you create a new Plasma layer type, add a define for the class ID here +// and also add the ID to the list in plPlasmaMAXLayer.cpp. + +const Class_ID LAYER_TEX_CLASS_ID( 0x4223c620, 0x183c4868 ); +const Class_ID STATIC_ENV_LAYER_CLASS_ID( 0x379a0a20, 0x3d0b1244 ); +const Class_ID DYNAMIC_ENV_LAYER_CLASS_ID( 0x18205c0f, 0x57ea0e10 ); +const Class_ID DYN_TEXT_LAYER_CLASS_ID( 0x36e3480f, 0x120120bd ); +const Class_ID ANGLE_ATTEN_LAYER_CLASS_ID( 0x6d90918, 0x6160114 ); +const Class_ID MAX_CAMERA_LAYER_CLASS_ID( 0xfaf5ec7, 0x13d90d3f ); + +//// Class Definition ///////////////////////////////////////////////////////// + +class plLayerInterface; +class plMaxNode; +class plErrorMsg; +class plLayer; +class plDynamicTextMap; +class plBitmapData; +class plLocation; +class plLayerConverter; +class plLayerInterface; +class plBMSamplerData; +class jvUniqueId; +class plLayerTargetContainer; +class plPlasmaMAXLayer : public Texmap +{ + friend class plLayerConverter; + + protected: + + static const Class_ID fDerivedTypes[]; + + plLayerTargetContainer *fConversionTargets; + + + void IAddConversionTarget( plLayerInterface *target ); + void IClearConversionTargets( void ); + + public: + + plPlasmaMAXLayer(); + virtual ~plPlasmaMAXLayer(); + + void DeleteThis() { delete this; } + + + // Static that checks the classID of the given texMap and, if it's a valid Plasma MAX Layer, returns a pointer to such + static plPlasmaMAXLayer *GetPlasmaMAXLayer( Texmap *map ); + + // Some layers must be unique for each node they're applied to (i.e. can't be shared among nodes). + // This returns true if the layer must be unique. + virtual bool MustBeUnique( void ) { return false; } + + // These let the material make an informed decision on what to do with + // the color and alpha values coming out of an EvalColor call. Something + // like an InvertColor can be handled within EvalColor, but there needs + // to be a way to tell the caller that the color returned should be completely + // ignored. + virtual BOOL DiscardColor() { return false; } + virtual BOOL DiscardAlpha() { return false; } + + + // Return the number of conversion targets (only valid after the MakeMesh pass) + int GetNumConversionTargets( void ); + + // Get an indexed conversion target + plLayerInterface *GetConversionTarget( int index ); + + virtual BOOL HandleBitmapSelection(int index = 0); + virtual void SetBitmap(BitmapInfo *bi, int index = 0); + virtual void SetBitmapAssetId(jvUniqueId& assetId, int index = 0); + virtual void GetBitmapAssetId(jvUniqueId& assetId, int index = 0); + + // Pure virtual accessors for the various bitmap related elements + virtual Bitmap *GetMaxBitmap(int index = 0) = 0; + virtual PBBitmap *GetPBBitmap( int index = 0 ) = 0; + virtual int GetNumBitmaps( void ) = 0; + + // Makes sure the textures are the latest versions (including getting + // the latest version from AssetMan) + void RefreshBitmaps(); + + hsBool GetBitmapFileName( char *destFilename, int maxLength, int index = 0 ); + + // Virtual function called by plBMSampler to get various things while sampling the layer's image + virtual bool GetSamplerInfo( plBMSamplerData *samplerData ) { return false; } + + // Backdoor for the texture find and replace util. Assumes input has the correct aspect ratio and is power of 2. + virtual void SetExportSize(int x, int y) {} + + protected: + virtual void ISetMaxBitmap(Bitmap *bitmap, int index = 0) = 0; + virtual void ISetPBBitmap( PBBitmap *pbbm, int index = 0 ) = 0; + +}; + +#endif // _plPlasmaMAXLayer_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayer.cpp new file mode 100644 index 00000000..2500e8e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayer.cpp @@ -0,0 +1,696 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plStaticEnvLayer.h" + +#include "iparamb2.h" +#include "iparamm2.h" +#include "stdmat.h" + +#include "plBMSampler.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +class plStaticEnvLayerClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading = FALSE) { return TRACKED_NEW plStaticEnvLayer(); } + const TCHAR* ClassName() { return GetString(IDS_STATIC_ENVMAP_LAYER); } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + Class_ID ClassID() { return STATIC_ENV_LAYER_CLASS_ID; } + const TCHAR* Category() { return TEXMAP_CAT_ENV; } + const TCHAR* InternalName() { return _T("PlasmaStaticEnvMapLayer"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plStaticEnvLayerClassDesc plStaticEnvLayerDesc; +ClassDesc2* GetStaticEnvLayerDesc() { return &plStaticEnvLayerDesc; } + +#include "plStaticEnvLayerBitmapPB.cpp" + +plStaticEnvLayer::plStaticEnvLayer() : + fBitmapPB(NULL), + fUVGen(NULL), + fTexHandle(NULL), + fTexTime(0), + fIValid(NEVER) +{ + int i; + + for( i = 0; i < 6; i++ ) + { + fBitmaps[ i ] = NULL; + } + + plStaticEnvLayerDesc.MakeAutoParamBlocks(this); + ReplaceReference(kRefUVGen, GetNewDefaultUVGen()); +} + +plStaticEnvLayer::~plStaticEnvLayer() +{ + int i; + + + for( i = 0; i < 6; i++ ) + { + if( fBitmaps[ i ] ) + fBitmaps[ i ]->DeleteThis(); + } + + IDiscardTexHandle(); +} + +//From MtlBase +void plStaticEnvLayer::Reset() +{ + GetStaticEnvLayerDesc()->Reset(this, TRUE); // reset all pb2's + for( int i = 0; i < 6; i++ ) + { + SetBitmap( NULL, i ); + } + + fIValid.SetEmpty(); +} + +void plStaticEnvLayer::Update(TimeValue t, Interval& valid) +{ + if (!fIValid.InInterval(t)) + { + fIValid.SetInfinite(); + + fUVGen->Update(t,fIValid); + fBitmapPB->GetValidity(t, fIValid); + } + + // Gonna need to do this when we support animated bm's +#if 0 + if (fBM) + { + if (bi.FirstFrame()!=bi.LastFrame()) + ivalid.SetInstant(t); + } +#endif + + valid &= fIValid; +} + +Interval plStaticEnvLayer::Validity(TimeValue t) +{ + //TODO: Update fIValid here + + // mf horse - Hacking this in just to get animations working. + // No warranty on this not being stupid. + Interval v = FOREVER; + fBitmapPB->GetValidity(t, v); + v &= fUVGen->Validity(t); + return v; +} + +ParamDlg* plStaticEnvLayer::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + fIMtlParams = imp; + IAutoMParamDlg* masterDlg = plStaticEnvLayerDesc.CreateParamDlgs(hwMtlEdit, imp, this); + + SELBitmapDlgProc *paramDlg = (SELBitmapDlgProc *)gBitmapParamBlk.GetUserDlgProc(); + if( paramDlg ) + paramDlg->fMtlParams = imp; + + return masterDlg; +} + +BOOL plStaticEnvLayer::SetDlgThing(ParamDlg* dlg) +{ + return FALSE; +} + +int plStaticEnvLayer::NumRefs() +{ + return 2; +} + +//From ReferenceMaker +RefTargetHandle plStaticEnvLayer::GetReference(int i) +{ + switch (i) + { + case kRefUVGen: return fUVGen; + case kRefBitmap: return fBitmapPB; + default: return NULL; + } +} + +void plStaticEnvLayer::SetReference(int i, RefTargetHandle rtarg) +{ + Interval garbage; + + switch (i) + { + case kRefUVGen: + fUVGen = (UVGen *)rtarg; + if( fUVGen ) + fUVGen->Update( TimeValue( 0 ), garbage ); + break; + case kRefBitmap: + fBitmapPB = (IParamBlock2 *)rtarg; + // KLUDGE: If the paramblock is being set chances are we are being created or + // loaded. In the case of load, we want to refresh our textures. + if (fBitmapPB) + RefreshBitmaps(); + break; + } +} + +int plStaticEnvLayer::NumParamBlocks() +{ + return 1; +} + +IParamBlock2* plStaticEnvLayer::GetParamBlock(int i) +{ + switch (i) + { + case 0: return fBitmapPB; + default: return NULL; + } +} + +IParamBlock2* plStaticEnvLayer::GetParamBlockByID(BlockID id) +{ + if (fBitmapPB->ID() == id) + return fBitmapPB; + else + return NULL; +} + +//From ReferenceTarget +RefTargetHandle plStaticEnvLayer::Clone(RemapDir &remap) +{ + plStaticEnvLayer *mnew = TRACKED_NEW plStaticEnvLayer(); + *((MtlBase*)mnew) = *((MtlBase*)this); // copy superclass stuff + mnew->ReplaceReference(kRefBitmap, remap.CloneRef(fBitmapPB)); + mnew->ReplaceReference(kRefUVGen, remap.CloneRef(fUVGen)); + BaseClone(this, mnew, remap); + return (RefTargetHandle)mnew; +} + +int plStaticEnvLayer::NumSubs() +{ + return 2; +} + +Animatable* plStaticEnvLayer::SubAnim(int i) +{ + //TODO: Return 'i-th' sub-anim + switch (i) + { + case kRefUVGen: return fUVGen; + case kRefBitmap: return fBitmapPB; + default: return NULL; + } +} + +TSTR plStaticEnvLayer::SubAnimName(int i) +{ + switch (i) + { + case kRefUVGen: return "UVGen"; + case kRefBitmap: return fBitmapPB->GetLocalName(); + default: return ""; + } +} + +RefResult plStaticEnvLayer::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message) +{ + switch (message) + { + case REFMSG_CHANGE: + { + fIValid.SetEmpty(); + + if (hTarget == fBitmapPB) + { + // see if this message came from a changing parameter in the pblock, + // if so, limit rollout update to the changing item and update any active viewport texture + ParamID changingParam = fBitmapPB->LastNotifyParamID(); + fBitmapPB->GetDesc()->InvalidateUI(changingParam); + + if (changingParam != -1) + IChanged(); + } + } + break; + + case REFMSG_UV_SYM_CHANGE: + IDiscardTexHandle(); + break; + } + + return REF_SUCCEED; +} + +void plStaticEnvLayer::IChanged() +{ + IDiscardTexHandle(); + // Texture wasn't getting updated in the viewports, and this fixes it. + // Don't know if it's the right way though. + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); + + // And this is so the SceneWatcher gets notified that the material on some of it's + // referenced objects changed. + NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_MAT); +} + +#define TEX_HDR_CHUNK 0x5000 +#define MAX_ASS_CHUNK 0x5500 + +IOResult plStaticEnvLayer::Save(ISave *isave) +{ + IOResult res; + + isave->BeginChunk(TEX_HDR_CHUNK); + res = MtlBase::Save(isave); + if (res != IO_OK) + return res; + isave->EndChunk(); + + return IO_OK; +} + +IOResult plStaticEnvLayer::Load(ILoad *iload) +{ + IOResult res; + while (IO_OK == (res = iload->OpenChunk())) + { + if (iload->CurChunkID() == TEX_HDR_CHUNK) + { + res = MtlBase::Load(iload); + } + iload->CloseChunk(); + if (res != IO_OK) + return res; + } + + return IO_OK; +} + +inline Point2 CompUV(float x, float y, float z) +{ + return Point2( 0.5f * ( x / z + 1.0f ), 0.5f * ( y / z + 1.0f ) ); +} + +AColor plStaticEnvLayer::EvalColor(ShadeContext& sc) +{ + if (!sc.doMaps) + return AColor(0.0f, 0.0f, 0.0f, 1.0f); + + AColor color; + if (sc.GetCache(this, color)) + return color; + + if (gbufID) + sc.SetGBufferID(gbufID); + + // Evaluate the Bitmap + +// Point3 v = sc.VectorTo( sc.V(), REF_OBJECT );//WORLD ); + Point3 v = sc.VectorTo( sc.Normal(), REF_OBJECT ); + float wx,wy,wz; + Color rcol; + Bitmap *refmap = NULL; + Point3 rv; + Point2 uv; + int size; + + wx = (float)fabs( v.x ); + wy = (float)fabs( v.y ); + wz = (float)fabs( v.z ); + if( wx >= wy && wx >= wz ) + { + if( v.x < 0 ) + { + refmap = fBitmaps[ kLeftFace ]; + uv = CompUV( -v.y, -v.z, v.x ); + } + else + { + refmap = fBitmaps[ kRightFace ]; + uv = CompUV( v.y, -v.z, -v.x ); + } + } + else if( wy >= wx && wy >= wz ) + { + if( v.y > 0 ) + { + refmap = fBitmaps[ kBackFace ]; + uv = CompUV( -v.x, -v.z, -v.y ); + } + else + { + refmap = fBitmaps[ kFrontFace ]; + uv = CompUV( v.x, -v.z, v.y ); + } + } + else if( wz >= wx && wz >= wy ) + { + if( v.z < 0 ) + { + refmap = fBitmaps[ kBottomFace ]; + uv = CompUV( -v.x, -v.y, v.z ); + } + else + { + refmap = fBitmaps[ kTopFace ]; + uv = CompUV( -v.x, v.y, -v.z ); + } + } + + if( refmap == NULL ) + color.White(); + else + { + if( uv.x < 0.0f ) + uv.x = 0.0f; + else if( uv.x > 1.0f ) + uv.x = 1.0f; + if( uv.y < 0.0f ) + uv.y = 0.0f; + else if( uv.y > 1.0f ) + uv.y = 1.0f; + size = refmap->Width(); + int x = (int)( uv.x * (float)( size - 1 ) ); + int y = (int)( ( 1.0f - uv.y ) * (float)( size - 1 ) ); + + BMM_Color_64 c; + refmap->GetLinearPixels( x, y, 1, &c ); + color = AColor( c.r / 65535.f, c.g / 65535.f, c.b / 65535.f, c.a / 65535.f ); + } + + // Invert color if specified + if( fBitmapPB->GetInt( kBmpInvertColor ) ) + { + color.r = 1.0f - color.r; + color.g = 1.0f - color.g; + color.b = 1.0f - color.b; + } + // Discard color if specified + if( fBitmapPB->GetInt( kBmpDiscardColor ) ) + color.r = color.g = color.b = 1.0f; + + // Invert alpha if specified + if( fBitmapPB->GetInt( kBmpInvertAlpha ) ) + color.a = 1.0f - color.a; + // Discard alpha if specified + if( fBitmapPB->GetInt( kBmpDiscardAlpha ) ) + color.a = 1.0f; + + // If RGB output is set to alpha, show RGB as grayscale of the alpha + if( fBitmapPB->GetInt( kBmpRGBOutput ) == 1 ) + color = AColor( color.a, color.a, color.a, 1.0f ); + + sc.PutCache(this, color); + return color; +} + +float plStaticEnvLayer::EvalMono(ShadeContext& sc) +{ + if (fBitmapPB->GetInt(kBmpMonoOutput) == 1) + return EvalColor(sc).a; + + return Intens(EvalColor(sc)); +} + +Point3 plStaticEnvLayer::EvalNormalPerturb(ShadeContext& sc) +{ + // Return the perturbation to apply to a normal for bump mapping + return Point3(0, 0, 0); +} + +ULONG plStaticEnvLayer::LocalRequirements(int subMtlNum) +{ + if( fBitmapPB->GetInt( kBmpUseMAXAtmosphere ) ) + return MTLREQ_VIEW_DEP; + + return MTLREQ_VIEW_DEP | MTLREQ_NOATMOS; +} + +void plStaticEnvLayer::IDiscardTexHandle() +{ + if (fTexHandle) + { + fTexHandle->DeleteThis(); + fTexHandle = NULL; + } +} + +void plStaticEnvLayer::ActivateTexDisplay(BOOL onoff) +{ + if (!onoff) + IDiscardTexHandle(); +} + +BITMAPINFO *plStaticEnvLayer::GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono, BOOL forceW, BOOL forceH) +{ + // FIXME + fTexTime = 0;//CalcFrame(t); +// texValid = clipValid; + BITMAPINFO *bmi = NULL; + int xflags = 0; + + if (fBitmapPB->GetInt(kBmpRGBOutput) == 1) + xflags |= EX_RGB_FROM_ALPHA; + bmi = thmaker.BitmapToDIB(fBitmaps[ 0 ], fUVGen->SymFlags(), xflags, forceW, forceH); + + return bmi; +} + +DWORD plStaticEnvLayer::GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker) +{ + // FIXME: ignore validity for now + if (fTexHandle && fIValid.InInterval(t))// && texTime == CalcFrame(t)) + return fTexHandle->GetHandle(); + else + { + IDiscardTexHandle(); + + fTexTime = 0;//CalcFrame(t); + fTexHandle = thmaker.MakeHandle(GetVPDisplayDIB(t, thmaker, fIValid)); + if (fTexHandle) + return fTexHandle->GetHandle(); + else + return 0; + } +} + +const char *plStaticEnvLayer::GetTextureName( int which ) +{ +// if (fBitmapPB->GetInt(kBmpUseBitmap)) + { + PBBitmap *pbbm = fBitmapPB->GetBitmap( kBmpFrontBitmap + which ); + if (pbbm) + return pbbm->bi.Name(); + } + + return NULL; +} + +//// Set/GetBaseFilename ////////////////////////////////////////////////////// + +void plStaticEnvLayer::SetBaseFilename( const TCHAR *name, TimeValue t ) +{ + fBitmapPB->SetValue( kBmpBaseFilename, t, (TCHAR *)name ); +} + +const TCHAR *plStaticEnvLayer::GetBaseFilename( TimeValue t ) +{ + Interval valid; + TCHAR *buffer; + + fBitmapPB->GetValue( kBmpBaseFilename, t, buffer, valid ); + return (const TCHAR *)buffer; +} + +//// IGetViewTM /////////////////////////////////////////////////////////////// + +Matrix3 plStaticEnvLayer::IGetViewTM( int i ) +{ + Matrix3 m; + m.IdentityMatrix(); + switch( i ) + { + case kTopFace: + m.RotateX( -PI ); + break; + case kBottomFace: + break; + case kLeftFace: + m.RotateX( -.5f * PI ); + m.RotateY( -.5f * PI ); + break; + case kRightFace: + m.RotateX( -.5f * PI ); + m.RotateY( +.5f * PI ); + break; + case kFrontFace: + m.RotateX( -.5f * PI ); + m.RotateY( PI ); + break; + case kBackFace: + m.RotateX( -.5f * PI ); + break; + } + return m; +} + +//// IWriteBM ///////////////////////////////////////////////////////////////// + +int plStaticEnvLayer::IWriteBM( BitmapInfo *bi, Bitmap *bm, TCHAR *name ) +{ + bi->SetName( name ); + if( bm->OpenOutput( bi ) == BMMRES_SUCCESS ) + { + if( bm->Write( bi, BMM_SINGLEFRAME ) == BMMRES_SUCCESS ) + { + bm->Close( bi ); + return 1; + } + } + + return 0; +} + +//// RenderCubicMap /////////////////////////////////////////////////////////// +// Generates the 6 faces for a cubic map based on a picked node + +void plStaticEnvLayer::RenderCubicMap( INode *node ) +{ + int res, size; + BOOL success = 0; + TSTR fname, fullname; + Bitmap *bm = NULL; + TSTR path, filename, ext, thisFilename; + BitmapInfo biOutFile; + + static TCHAR suffixes[ 6 ][ 4 ] = { "_FR", "_BK", "_LF", "_RT", "_UP", "_DN" }; + + + Interface *ip = GetCOREInterface(); + size = fBitmapPB->GetInt( kBmpTextureSize, ip->GetTime() ); + if( size <= 0 ) + { + return; + } + + thisFilename = fBitmapPB->GetStr( kBmpBaseFilename, ip->GetTime() ); + if( thisFilename.isNull() ) + { + return; + } + + SplitFilename( thisFilename, &path, &filename, &ext ); + + BOOL wasHid = node->IsNodeHidden(); + node->Hide( TRUE ); + + // Create a blank bitmap + biOutFile.SetWidth( size ); + biOutFile.SetHeight( size ); + biOutFile.SetType( BMM_TRUE_64 ); + biOutFile.SetAspect( 1.0f ); + biOutFile.SetCurrentFrame( 0 ); + bm = TheManager->Create( &biOutFile ); + + Matrix3 nodeTM = node->GetNodeTM( ip->GetTime() ); + Matrix3 tm; + INode *root = ip->GetRootNode(); + bm->Display( GetString( IDS_CUBIC_RENDER_TITLE ) ); + + /// Set up rendering contexts + ViewParams vp; + vp.projType = PROJ_PERSPECTIVE; + vp.hither = .001f; + vp.yon = 1.0e30f; + vp.fov = PI/2.0f; + if( fBitmapPB->GetInt( kBmpUseMAXAtmosphere ) ) + { + vp.nearRange = 0; + vp.farRange = fBitmapPB->GetFloat( kBmpFarDistance ); + } + else + { + vp.nearRange = vp.farRange = 1.0e30f; + } + BOOL saveUseEnvMap = ip->GetUseEnvironmentMap(); + ip->SetUseEnvironmentMap( false ); + + res = ip->OpenCurRenderer( &vp ); + for( int i = 0; i < 6; i++ ) + { + tm = IGetViewTM( i ); + tm.PreTranslate( -nodeTM.GetTrans() ); + vp.affineTM = tm; + + // Construct filename + thisFilename.printf( _T( "%s\\%s%s%s" ), path, filename, suffixes[ i ], ext ); + + res = ip->CurRendererRenderFrame( ip->GetTime(), bm, NULL, 1.0f, &vp ); + if( !res ) + goto fail; + + if( !IWriteBM( &biOutFile, bm, thisFilename ) ) + goto fail; + } + + success = 1; +fail: + ip->CloseCurRenderer(); + ip->SetUseEnvironmentMap( saveUseEnvMap ); + + bm->DeleteThis(); + node->Hide( wasHid ); + if( success ) + { + for(int i = 0; i < 6; i++ ) + { + BitmapInfo bi; + thisFilename.printf( _T( "%s\\%s%s%s" ), path, filename, suffixes[ i ], ext ); + bi.SetName( thisFilename ); + + PBBitmap pbBitmap( bi ); + fBitmapPB->SetValue( kBmpFrontBitmap + i, ip->GetTime(), &pbBitmap ); + } + fBitmapPB->GetMap()->UpdateUI( ip->GetTime() ); + } +} + +PBBitmap *plStaticEnvLayer::GetPBBitmap(int index /* = 0 */) +{ + return fBitmapPB->GetBitmap( ParamID( kBmpFrontBitmap + index ) ); +} + +void plStaticEnvLayer::ISetPBBitmap( PBBitmap *pbbm, int index ) +{ + fBitmapPB->SetValue( ParamID( kBmpFrontBitmap + index ), 0, pbbm ); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayer.h new file mode 100644 index 00000000..13cf3595 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayer.h @@ -0,0 +1,237 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plStaticEnvLayer - Static EnvironmentMap MAX Layer // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.17.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plStaticEnvLayer_h +#define _plStaticEnvLayer_h + +#include "Max.h" +#include "plPlasmaMAXLayer.h" +#include "../resource.h" + +class ClassDesc2; +class IParamBlock2; +class Bitmap; + +ClassDesc2* GetStaticEnvLayerDesc(); + +extern TCHAR *GetString(int id); +extern HINSTANCE hInstance; + + +//// Class Definition ///////////////////////////////////////////////////////// + +class plStaticEnvLayer : public plPlasmaMAXLayer +{ +protected: + // Parameter block + IParamBlock2 *fBitmapPB; + UVGen *fUVGen; + + IMtlParams *fIMtlParams; + + TexHandle *fTexHandle; + TimeValue fTexTime; + + Bitmap *fBitmaps[ 6 ]; + Interval fIValid; + TCHAR fBaseFileName[ MAX_PATH ]; + + friend class SELBitmapDlgProc; + + + Matrix3 IGetViewTM( int i ); + int IWriteBM( BitmapInfo *bi, Bitmap *bm, TCHAR *name ); + +public: + // Ref nums + enum + { + kRefUVGen, + kRefBitmap, + }; + + // Block ID's + enum + { + kBlkBitmap, + }; + + // Faces + enum + { + kFrontFace, + kBackFace, + kLeftFace, + kRightFace, + kTopFace, + kBottomFace + }; + + plStaticEnvLayer(); + ~plStaticEnvLayer(); + void DeleteThis() { delete this; } + + //From MtlBase + ParamDlg* CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + BOOL SetDlgThing(ParamDlg* dlg); + void Update(TimeValue t, Interval& valid); + void Reset(); + Interval Validity(TimeValue t); + ULONG LocalRequirements(int subMtlNum); + + //From Texmap + RGBA EvalColor(ShadeContext& sc); + float EvalMono(ShadeContext& sc); + Point3 EvalNormalPerturb(ShadeContext& sc); + + // For displaying textures in the viewport + BOOL SupportTexDisplay() { return TRUE; } + void ActivateTexDisplay(BOOL onoff); + BITMAPINFO *GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono=FALSE, int forceW=0, int forceH=0); + DWORD GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker); +protected: + void IChanged(); + void IDiscardTexHandle(); + +public: + void GetUVTransform(Matrix3 &uvtrans) { fUVGen->GetUVTransform(uvtrans); } + int GetTextureTiling() { return fUVGen->GetTextureTiling(); } + int GetUVWSource() { return fUVGen->GetUVWSource(); } + virtual int GetMapChannel () { return fUVGen->GetMapChannel(); } // only relevant if above returns UVWSRC_EXPLICIT + UVGen *GetTheUVGen() { return fUVGen; } + + //TODO: Return anim index to reference index + int SubNumToRefNum(int subNum) { return subNum; } + + + // Loading/Saving + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + //From Animatable + Class_ID ClassID() { return STATIC_ENV_LAYER_CLASS_ID; } + SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } + void GetClassName(TSTR& s) { s = GetString(IDS_STATIC_ENVMAP_LAYER); } + + RefTargetHandle Clone( RemapDir &remap ); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + // TODO: Maintain the number or references here + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + + int NumParamBlocks(); // return number of ParamBlocks in this instance + IParamBlock2* GetParamBlock(int i); // return i'th ParamBlock + IParamBlock2* GetParamBlockByID(BlockID id); // return id'd ParamBlock + + const char *GetTextureName( int which ); + + void SetBaseFilename( const TCHAR *name, TimeValue t ); + const TCHAR *GetBaseFilename( TimeValue t ); + + void RenderCubicMap( INode *node ); + + + /// ParamBlock accessors + enum + { + kScalingAny, + kScalingHalf, + kScalingNone + }; + + // Param ID's + enum + { + kBmpFrontBitmap, + kBmpBackBitmap, + kBmpLeftBitmap, + kBmpRightBitmap, + kBmpTopBitmap, + kBmpBottomBitmap, + + // Misc + kBmpDiscardColor, + kBmpInvertColor, + kBmpDiscardAlpha, + kBmpInvertAlpha, + + // Texture quality + kBmpNonCompressed, + kBmpScaling, + + // Max only + kBmpMonoOutput, + kBmpRGBOutput, + + // Detail + kBmpUseDetail, + kBmpDetailStartSize, + kBmpDetailStopSize, + kBmpDetailStartOpac, + kBmpDetailStopOpac, + + // Texture generation + kBmpBaseFilename, + kBmpTextureSize, + kBmpGenerateFaces, + kBmpLastTextureSize, // Annoying, but necessary to clamp texture sizes to powers of 2 + kBmpUseMAXAtmosphere, + kBmpFarDistance, + + // Just a hack to simulate refraction instead of reflection + kBmpRefract + }; + + // Pure virtual accessors for the various bitmap related elements + virtual Bitmap *GetMaxBitmap(int index = 0) { return fBitmaps[ index ]; } + virtual PBBitmap *GetPBBitmap( int index = 0 ); + virtual int GetNumBitmaps( void ) { return 6; } + + protected: + virtual void ISetMaxBitmap(Bitmap *bitmap, int index = 0) { fBitmaps[ index ] = bitmap; } + virtual void ISetPBBitmap( PBBitmap *pbbm, int index = 0 ); + + +}; + +#endif // _plStaticEnvLayer_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayerBitmapPB.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayerBitmapPB.cpp new file mode 100644 index 00000000..eb4cc605 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayerBitmapPB.cpp @@ -0,0 +1,499 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plStaticEnvLayer.h" + + + +/////////////////////////////////////////////////////////////////////////////// +//// Bitmap Accessor ////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class SELBMTexPBAccessor : public PBAccessor +{ +public: + void Set(PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (!owner) + return; + + plStaticEnvLayer* layer = (plStaticEnvLayer*)owner; + IParamBlock2 *pb = layer->GetParamBlockByID(plStaticEnvLayer::kBlkBitmap); + + switch (id) + { + case plStaticEnvLayer::kBmpFrontBitmap: + case plStaticEnvLayer::kBmpBackBitmap: + case plStaticEnvLayer::kBmpLeftBitmap: + case plStaticEnvLayer::kBmpRightBitmap: + case plStaticEnvLayer::kBmpTopBitmap: + case plStaticEnvLayer::kBmpBottomBitmap: + // Set up the enums so these would match... + if (pb->GetMap()) + pb->GetMap()->Invalidate( id ); + // Update the bitmap saved by the layer + //layer->SetBitmap( id, &val.bm->bi ); + break; + + case plStaticEnvLayer::kBmpBaseFilename: + if( pb->GetMap() ) + { + pb->GetMap()->Enable( plStaticEnvLayer::kBmpGenerateFaces, ( val.s == NULL || val.s[ 0 ] == 0 ) ? FALSE : TRUE ); + + ICustButton *bmSelectBtn = GetICustButton( GetDlgItem( pb->GetMap()->GetHWnd(), IDC_GENERATE_FACES ) ); + bmSelectBtn->SetText( _T( "Generate From Node" ) ); + ReleaseICustButton( bmSelectBtn ); + } + break; + } + } + void Get(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid) + { + } +}; +static SELBMTexPBAccessor bmtex_accessor; + + +/////////////////////////////////////////////////////////////////////////////// +//// PickControlNode ////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class PickControlNode : public PickObjectProc +{ + public: + plStaticEnvLayer *fLayer; + HWND fHWnd; + + PickControlNode() { fLayer = NULL; } + + BOOL Pick( INode *node ) + { + if( node && fLayer ) + fLayer->RenderCubicMap( node ); + return TRUE; + } + + void EnterMode() + { + ICustButton *iBut = GetICustButton( GetDlgItem( fHWnd, IDC_GENERATE_FACES ) ); + if( iBut ) + { + iBut->SetCheck( TRUE ); + iBut->SetText( _T( "Generate From Node" ) ); + } + ReleaseICustButton( iBut ); + } + + void ExitMode() + { + ICustButton *iBut = GetICustButton( GetDlgItem( fHWnd, IDC_GENERATE_FACES ) ); + if( iBut ) + { + iBut->SetCheck( FALSE ); + iBut->SetText( _T( "Generate From Node" ) ); + } + ReleaseICustButton( iBut ); + } + + BOOL Filter( INode *node ) { return TRUE; } +}; + + +/////////////////////////////////////////////////////////////////////////////// +//// ParamBlock Dialog Proc /////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class SELBitmapDlgProc : public ParamMap2UserDlgProc +{ + PickControlNode fPickCallback; + +public: + IMtlParams *fMtlParams; + + /// Called to update the controls of the dialog + virtual void Update( TimeValue t, Interval &valid, IParamMap2 *map ) + { + ICustButton *bmSelectBtn; + IParamBlock2 *pblock; + int i; + long buttons[ 6 ] = { IDC_FRONT_NAME, IDC_BACK_NAME, IDC_LEFT_NAME, IDC_RIGHT_NAME, IDC_TOP_NAME, IDC_BOTTOM_NAME }; + BitmapInfo bi; + + + ParamMap2UserDlgProc::Update( t, valid, map ); + + pblock = map->GetParamBlock(); + for( i = plStaticEnvLayer::kBmpFrontBitmap; i <= plStaticEnvLayer::kBmpBottomBitmap; i++ ) + { + bmSelectBtn = GetICustButton( GetDlgItem( map->GetHWnd(), buttons[ i ] ) ); + PBBitmap *pbbm = pblock->GetBitmap( i, t ); + if( pbbm ) + bmSelectBtn->SetText( (TCHAR *)pbbm->bi.Filename() ); + else + bmSelectBtn->SetText( _T( "None" ) ); + ReleaseICustButton( bmSelectBtn ); + } + + plStaticEnvLayer *layer = (plStaticEnvLayer *)map->GetParamBlock()->GetOwner(); + bi.SetName( layer->GetBaseFilename( t ) ); + SetDlgItemText( map->GetHWnd(), IDC_BASE_FILENAME, bi.Filename() ); + map->Enable( plStaticEnvLayer::kBmpGenerateFaces, ( bi.Name() == NULL || bi.Name()[ 0 ] == 0 ) ? FALSE : TRUE ); + + bmSelectBtn = GetICustButton( GetDlgItem( map->GetHWnd(), IDC_GENERATE_FACES ) ); + bmSelectBtn->SetText( _T( "Generate From Node" ) ); + ReleaseICustButton( bmSelectBtn ); + + i = pblock->GetInt( plStaticEnvLayer::kBmpTextureSize, t ); + pblock->SetValue( plStaticEnvLayer::kBmpLastTextureSize, t, i ); + } + + /// Clamp texture sizes to a power of 2 + void IClampTexSizeSpinner( TimeValue t, IParamMap2 *map ) + { + IParamBlock2 *pblock = map->GetParamBlock(); + + int lastVal = pblock->GetInt( plStaticEnvLayer::kBmpLastTextureSize, t ); + int tempVal, newVal = pblock->GetInt( plStaticEnvLayer::kBmpTextureSize, t ); + + if( newVal < lastVal ) + { + lastVal = newVal; + for( tempVal = 1; tempVal < newVal; tempVal <<= 1 ); + newVal = tempVal >> 1; + } + else + { + lastVal = newVal; + for( tempVal = 1; tempVal < newVal; tempVal <<= 1 ); + newVal = tempVal; + } + + pblock->SetValue( plStaticEnvLayer::kBmpTextureSize, t, newVal ); + pblock->SetValue( plStaticEnvLayer::kBmpLastTextureSize, t, newVal ); + } + + /// Main message proc + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + static ICustButton* bmSelectBtn; + long buttons[ 6 ] = { IDC_FRONT_NAME, IDC_BACK_NAME, IDC_LEFT_NAME, IDC_RIGHT_NAME, IDC_TOP_NAME, IDC_BOTTOM_NAME }; + + switch (msg) + { + case WM_INITDIALOG: + break; + + case CC_SPINNER_CHANGE: + if( LOWORD( wParam ) == IDC_TEXSIZE_SPIN ) + IClampTexSizeSpinner( t, map ); + break; + + case WM_COMMAND: + if( HIWORD( wParam ) == EN_CHANGE && LOWORD( wParam ) == IDC_TEXSIZE_EDIT ) + { + IClampTexSizeSpinner( t, map ); + } + else if( HIWORD( wParam ) == BN_CLICKED && LOWORD( wParam ) == IDC_LAYER_RELOAD ) + { + plStaticEnvLayer *layer = (plStaticEnvLayer*)map->GetParamBlock()->GetOwner(); + layer->RefreshBitmaps(); + layer->IChanged(); + + return TRUE; + } + else if( LOWORD( wParam ) == IDC_FRONT_NAME ) + return IDoLayerClicked( LOWORD( wParam ), plStaticEnvLayer::kFrontFace, map, t, hWnd ); + + else if( LOWORD( wParam ) == IDC_BACK_NAME ) + return IDoLayerClicked( LOWORD( wParam ), plStaticEnvLayer::kBackFace, map, t, hWnd ); + + else if( LOWORD( wParam ) == IDC_LEFT_NAME ) + return IDoLayerClicked( LOWORD( wParam ), plStaticEnvLayer::kLeftFace, map, t, hWnd ); + + else if( LOWORD( wParam ) == IDC_RIGHT_NAME ) + return IDoLayerClicked( LOWORD( wParam ), plStaticEnvLayer::kRightFace, map, t, hWnd ); + + else if( LOWORD( wParam ) == IDC_TOP_NAME ) + return IDoLayerClicked( LOWORD( wParam ), plStaticEnvLayer::kTopFace, map, t, hWnd ); + + else if( LOWORD( wParam ) == IDC_BOTTOM_NAME ) + return IDoLayerClicked( LOWORD( wParam ), plStaticEnvLayer::kBottomFace, map, t, hWnd ); + + else if( LOWORD( wParam ) == IDC_LAYER_LOAD_GEN ) + return IDoLoadGenerated( map, t, hWnd ); + + else if( LOWORD( wParam ) == IDC_GENERATE_FACES ) + { + plStaticEnvLayer *layer = (plStaticEnvLayer*)map->GetParamBlock()->GetOwner(); + + fMtlParams->EndPickMode(); + fPickCallback.fHWnd = hWnd; + fPickCallback.fLayer = layer; + + fMtlParams->SetPickMode( &fPickCallback ); + break; + } + + else if( LOWORD( wParam ) == IDC_BASE_FILENAME ) + return IDoSelectBaseFilename( map, t, hWnd ); + + break; + } + + return FALSE; + } + void DeleteThis() {}; + + BOOL IDoSelectBaseFilename( IParamMap2 *map, TimeValue t, HWND hWnd ) + { + BitmapInfo bi; + + + plStaticEnvLayer *layer = (plStaticEnvLayer*)map->GetParamBlock()->GetOwner(); + + /// Select one file + bi.SetName( layer->GetBaseFilename( t ) ); + if( !TheManager->SelectFileOutput( &bi, GetCOREInterface()->GetMAXHWnd(), _T( "Choose the base filename for the rendered faces" ) ) ) + return FALSE; + + /// Just store the name and set the button label as such, too + SetDlgItemText( hWnd, IDC_BASE_FILENAME, bi.Filename() ); + layer->SetBaseFilename( bi.Name(), t ); + return TRUE; + } + + BOOL IDoLoadGenerated( IParamMap2 *map, TimeValue t, HWND hWnd ) + { + BitmapInfo bi; + int i; + TCHAR filename[ MAX_PATH ]; + TCHAR *modPoint, faces[ 6 ][ 4 ] = { "_FR", "_BK", "_LF", "_RT", "_UP", "_DN" }; + + + /// Select one file + PBBitmap *pbbm = map->GetParamBlock()->GetBitmap( plStaticEnvLayer::kBmpFrontBitmap, t ); + if( pbbm != NULL ) + bi.SetName( pbbm->bi.Name() ); + if( !TheManager->SelectFileInput( &bi, GetCOREInterface()->GetMAXHWnd(), _T( "Select one of the generated face bitmaps" ) ) ) + return FALSE; + + /// Copy the name over and get our mod point + strcpy( filename, bi.Filename() ); + modPoint = strstr( filename, "_UP" ); + if( modPoint == NULL ) + modPoint = strstr( filename, "_DN" ); + if( modPoint == NULL ) + modPoint = strstr( filename, "_LF" ); + if( modPoint == NULL ) + modPoint = strstr( filename, "_RT" ); + if( modPoint == NULL ) + modPoint = strstr( filename, "_FR" ); + if( modPoint == NULL ) + modPoint = strstr( filename, "_BK" ); + + /// Load each face + for( i = 0; i < 6; i++ ) + { + memcpy( modPoint, faces[ i ], sizeof( TCHAR ) * 3 ); + if( !ILoadFace( i, filename, map, t, hWnd ) ) + return FALSE; + } + + return TRUE; + } + + BOOL ILoadFace( int whichFace, const TCHAR *fileName, IParamMap2 *map, TimeValue t, HWND hWnd ) + { + long buttons[ 6 ] = { IDC_FRONT_NAME, IDC_BACK_NAME, IDC_LEFT_NAME, IDC_RIGHT_NAME, IDC_TOP_NAME, IDC_BOTTOM_NAME }; + + IParamBlock2 *pblock = map->GetParamBlock(); + plStaticEnvLayer *layer = (plStaticEnvLayer*)map->GetParamBlock()->GetOwner(); + ICustButton *bmSelectBtn; + BitmapInfo bi; + + + if( TheManager->GetImageInfo( &bi, fileName ) != BMMRES_SUCCESS ) + return FALSE; + + layer->SetBitmap( &bi, whichFace - plStaticEnvLayer::kFrontFace ); + + PBBitmap *pbbm = layer->GetPBBitmap( whichFace - plStaticEnvLayer::kFrontFace ); + bmSelectBtn = GetICustButton( GetDlgItem( hWnd, buttons[ whichFace ] ) ); + bmSelectBtn->SetText((TCHAR*)pbbm->bi.Filename()); + ReleaseICustButton( bmSelectBtn ); + + return TRUE; + } + + BOOL IDoLayerClicked( int whichBtn, int whichFace, IParamMap2 *map, TimeValue t, HWND hWnd ) + { + plPlasmaMAXLayer *layer = (plPlasmaMAXLayer *)map->GetParamBlock()->GetOwner(); + if (layer == nil) + return FALSE; + + BOOL selectedNewBitmap = layer->HandleBitmapSelection( whichFace - plStaticEnvLayer::kFrontFace ); + if(selectedNewBitmap) + { + ICustButton* bmSelectBtn; + + PBBitmap *pbbm = layer->GetPBBitmap( whichFace - plStaticEnvLayer::kFrontFace ); + bmSelectBtn = GetICustButton( GetDlgItem( hWnd, whichBtn ) ); + bmSelectBtn->SetText(pbbm != nil ? (TCHAR*)pbbm->bi.Filename() : nil); + ReleaseICustButton(bmSelectBtn); + + return TRUE; + } + else + { + return FALSE; + } + } +}; +static SELBitmapDlgProc gSELBitmapDlgProc; + +/////////////////////////////////////////////////////////////////////////////// +//// ParamBlock Definition //////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ParamBlockDesc2 gBitmapParamBlk +( + plStaticEnvLayer::kBlkBitmap, _T("bitmap"), 0, GetStaticEnvLayerDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI, plStaticEnvLayer::kRefBitmap, + + IDD_STATIC_ENVMAP_LAYER, IDS_STATIC_ENVMAP_LAYER_TEX, 0, 0, &gSELBitmapDlgProc, + + // Bitmaps + plStaticEnvLayer::kBmpFrontBitmap, _T("frontBitmap"), TYPE_BITMAP, P_SHORT_LABELS, 0, + p_accessor, &bmtex_accessor, + end, + plStaticEnvLayer::kBmpBackBitmap, _T("backBitmap"), TYPE_BITMAP, P_SHORT_LABELS, 0, + p_accessor, &bmtex_accessor, + end, + plStaticEnvLayer::kBmpLeftBitmap, _T("leftBitmap"), TYPE_BITMAP, P_SHORT_LABELS, 0, + p_accessor, &bmtex_accessor, + end, + plStaticEnvLayer::kBmpRightBitmap, _T("rightBitmap"), TYPE_BITMAP, P_SHORT_LABELS, 0, + p_accessor, &bmtex_accessor, + end, + plStaticEnvLayer::kBmpTopBitmap, _T("topBitmap"), TYPE_BITMAP, P_SHORT_LABELS, 0, + p_accessor, &bmtex_accessor, + end, + plStaticEnvLayer::kBmpBottomBitmap, _T("bottomBitmap"), TYPE_BITMAP, P_SHORT_LABELS, 0, + p_accessor, &bmtex_accessor, + end, + + // Texture Color/Alpha + plStaticEnvLayer::kBmpDiscardColor, _T("discardColor"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_BLEND_NO_COLOR, + end, + plStaticEnvLayer::kBmpInvertColor, _T("invertColor"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_BLEND_INV_COLOR, + end, + plStaticEnvLayer::kBmpDiscardAlpha, _T("discardAlpha"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_DISCARD_ALPHA, + end, + plStaticEnvLayer::kBmpInvertAlpha, _T("invertAlpha"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_BLEND_INV_ALPHA, + end, + + // Texture Quality + plStaticEnvLayer::kBmpNonCompressed, _T("nonCompressed"),TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_FORCE_NONCOMPRESSED, + end, + plStaticEnvLayer::kBmpScaling, _T("scaling"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_SCALE_ALL, IDC_SCALE_HALF, IDC_SCALE_NONE, + end, + + // Max Only + plStaticEnvLayer::kBmpMonoOutput, _T("monoOutput"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 2, IDC_HSMAX_LAYER_RGBOUT, IDC_HSMAX_LAYER_ALPHAOUT, + end, + plStaticEnvLayer::kBmpRGBOutput, _T("rgbOutput"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 2, IDC_HSMAX_LAYER_RGBOUT2, IDC_HSMAX_LAYER_ALPHAOUT2, + end, + + // Detail + plStaticEnvLayer::kBmpUseDetail, _T("useDetail"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_USE_DETAIL, + p_default, FALSE, + p_enable_ctrls, 4, plStaticEnvLayer::kBmpDetailStartSize, plStaticEnvLayer::kBmpDetailStopSize, + plStaticEnvLayer::kBmpDetailStartOpac, plStaticEnvLayer::kBmpDetailStopOpac, + end, + + plStaticEnvLayer::kBmpDetailStartSize,_T("dropOffStart"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_START_SIZE_EDIT, IDC_DETAIL_START_SIZE_SPIN, 0.4, + p_range, 0, 100, + p_default, 0, + end, + plStaticEnvLayer::kBmpDetailStopSize, _T("dropOffStop"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_STOP_SIZE_EDIT, IDC_DETAIL_STOP_SIZE_SPIN, 0.4, + p_range, 0, 100, + p_default, 100, + end, + plStaticEnvLayer::kBmpDetailStartOpac, _T("detailMax"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_START_OPAC_EDIT, IDC_DETAIL_START_OPAC_SPIN, 0.4, + p_range, 0, 100, + p_default, 8, + end, + plStaticEnvLayer::kBmpDetailStopOpac, _T("detailMin"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_STOP_OPAC_EDIT, IDC_DETAIL_STOP_OPAC_SPIN, 0.4, + p_range, 0, 100, + p_default, 0, + end, + + // Face generation + plStaticEnvLayer::kBmpTextureSize, _T("textureSize"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_TEXSIZE_EDIT, IDC_TEXSIZE_SPIN, SPIN_AUTOSCALE, + p_range, 4, 512, + p_default, 64, + end, + plStaticEnvLayer::kBmpBaseFilename, _T("baseFilename"), TYPE_FILENAME, 0, 0, + p_default, _T( "" ), + p_accessor, &bmtex_accessor, + end, + plStaticEnvLayer::kBmpGenerateFaces, _T("genFaces"), TYPE_INODE, 0, 0, + p_ui, TYPE_PICKNODEBUTTON, IDC_GENERATE_FACES, + p_prompt, IDS_SELECT_NODE, + end, + plStaticEnvLayer::kBmpUseMAXAtmosphere, _T("useMAXAtmos"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_USEMAXFOG, + p_default, FALSE, + p_enable_ctrls, 1, plStaticEnvLayer::kBmpFarDistance, + end, + plStaticEnvLayer::kBmpFarDistance, _T("farDistance"), TYPE_FLOAT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_FARDIST_EDIT, IDC_FARDIST_SPIN, SPIN_AUTOSCALE, + p_range, 0.f, 9999999.f, + p_default, 500.f, + end, + plStaticEnvLayer::kBmpLastTextureSize, _T("lastTextureSize"), TYPE_INT, 0, 0, + end, + + plStaticEnvLayer::kBmpRefract, _T("refract"),TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_REFRACT, + end, + + end +); + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayerBitmapPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayerBitmapPB.h new file mode 100644 index 00000000..630cc35a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Layers/plStaticEnvLayerBitmapPB.h @@ -0,0 +1,72 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// ParamBlock Konstants for Static EnvironmentMap MAX Layers // +// Cyan, Inc. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 8.17.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plStaticEnvLayerBitmapPB_h +#define _plStaticEnvLayerBitmapPB_h + +// Param ID's +enum +{ + kBmpFrontBitmap, + kBmpBackBitmap, + kBmpLeftBitmap, + kBmpRightBitmap, + kBmpTopBitmap, + kBmpBottomBitmap, + + // Misc + kBmpDiscardColor, + kBmpInvertColor, + kBmpDiscardAlpha, + kBmpInvertAlpha, + + // Texture quality + kBmpNonCompressed, + kBmpScaling, + + // Max only + kBmpMonoOutput, + kBmpRGBOutput, + + // Detail + kBmpUseDetail, + kBmpDetailStartSize, + kBmpDetailStopSize, + kBmpDetailStartOpac, + kBmpDetailStopOpac, +}; + +#endif //_plStaticEnvLayerBitmapPB_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthConvert.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthConvert.cpp new file mode 100644 index 00000000..1b8d7a29 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthConvert.cpp @@ -0,0 +1,266 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// Export/convert-specific functionality of plAnimStealthNode // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plAnimStealthNode.h" +#include "plPassMtlBase.h" +#include "resource.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxComponent/plMaxAnimUtils.h" +#include "../MaxConvert/plConvert.h" +#include "../MaxConvert/hsMaterialConverter.h" +#include "../plSurface/hsGMaterial.h" +#include "../plSurface/plLayerAnimation.h" + +#include "iparamm2.h" + + +//// Helpers ///////////////////////////////////////////////////////////////// + +static void ISearchLayerRecur( plLayerInterface *layer, const char *segName, hsTArray& keys ) +{ + if( !layer ) + return; + + plLayerAnimation *animLayer = plLayerAnimation::ConvertNoRef(layer); + if (animLayer) + { + char *ID = animLayer->GetSegmentID(); + if (ID == nil) + ID = ""; + if (!strcmp(ID, segName)) + { + if( keys.kMissingIndex == keys.Find(animLayer->GetKey()) ) + keys.Append(animLayer->GetKey()); + } + } + + ISearchLayerRecur(layer->GetAttached(), segName, keys); +} + +static int ISearchLayerRecur(hsGMaterial* mat, const char *segName, hsTArray& keys) +{ + if (segName == nil || strcmp( segName, ENTIRE_ANIMATION_NAME ) == 0 ) + segName = ""; + int i; + for( i = 0; i < mat->GetNumLayers(); i++ ) + ISearchLayerRecur(mat->GetLayer(i), segName, keys); + return keys.GetCount(); +} + +static int GetMatAnimModKey(Mtl* mtl, plMaxNodeBase* node, const char* segName, hsTArray& keys) +{ + int retVal = 0; + + int i; + + //if( begin < 0 ) + // begin = 0; + + if( mtl->ClassID() == Class_ID(MULTI_CLASS_ID,0) ) + { + for( i = 0; i < mtl->NumSubMtls(); i++ ) + retVal += GetMatAnimModKey(mtl->GetSubMtl(i), node, segName, keys); + } + else + { + hsTArray matList; + + if (node) + hsMaterialConverter::Instance().GetMaterialArray(mtl, (plMaxNode*)node, matList); + else + hsMaterialConverter::Instance().CollectConvertedMaterials(mtl, matList); + + for( i = 0; i < matList.GetCount(); i++ ) + { + retVal += ISearchLayerRecur(matList[i], segName, keys); + } + } + + return retVal; +} + +SegmentSpec *plAnimStealthNode::IGetSegmentSpec( void ) const +{ + if( fCachedSegMap != nil ) + { + const char *name = GetSegmentName(); + if( name != nil ) + { + SegmentMap::iterator i = fCachedSegMap->find( name ); + if( i != fCachedSegMap->end() ) + { + SegmentSpec *spec = i->second; + return spec; + } + } + } + return nil; +} + + +hsScalar plAnimStealthNode::GetSegStart( void ) const +{ + SegmentSpec *spec = IGetSegmentSpec(); + if( spec != nil ) + return spec->fStart; + + return 0.f; +} + +hsScalar plAnimStealthNode::GetSegEnd( void ) const +{ + SegmentSpec *spec = IGetSegmentSpec(); + if( spec != nil ) + return spec->fEnd; + + return 0.f; +} + +void plAnimStealthNode::GetLoopPoints( hsScalar &start, hsScalar &end ) const +{ + start = GetSegStart(); + end = GetSegEnd(); + + const char *loopName = GetLoopName(); + if( loopName != nil && loopName[ 0 ] != 0 && fCachedSegMap != nil ) + GetSegMapAnimTime( loopName, fCachedSegMap, SegmentSpec::kLoop, start, end ); +} + +void plAnimStealthNode::GetAllStopPoints( hsTArray &out ) +{ + if( fCachedSegMap == nil ) + return; + + for (SegmentMap::iterator it = fCachedSegMap->begin(); it != fCachedSegMap->end(); it++) + { + SegmentSpec *spec = it->second; + if( spec->fType == SegmentSpec::kStopPoint ) + { + out.Append( spec->fStart ); + } + } +} + +//// StuffToTimeConvert ////////////////////////////////////////////////////// +// Handles converting all the settings we have that are applicable for an +// animTimeConvert and stuffing it into said ATC. + +void plAnimStealthNode::StuffToTimeConvert( plAnimTimeConvert &convert, hsScalar maxLength ) +{ + const char *segName = GetSegmentName(); + bool isEntire = ( segName == nil || strcmp( segName, ENTIRE_ANIMATION_NAME ) == 0 ) ? true : false; + + if( isEntire ) + { + convert.SetBegin( 0 ); + convert.SetEnd( maxLength ); + } + else + { + SegmentSpec *spec = IGetSegmentSpec(); + convert.SetBegin( ( spec != nil ) ? spec->fStart : 0.f ); + convert.SetEnd( ( spec != nil ) ? spec->fEnd : 0.f ); + } + + // Even if we're not looping, set the loop points. (A responder + // could tell us later to start looping.) + if( isEntire ) + convert.SetLoopPoints( 0, maxLength ); + else + { + hsScalar loopStart, loopEnd; + GetLoopPoints( loopStart, loopEnd ); + convert.SetLoopPoints( loopStart, loopEnd ); + } + convert.Loop( GetLoop() ); + + // Auto-start + if( GetAutoStart() ) + convert.Start( 0 ); + else + convert.Stop( true ); + + // Stuff stop points + GetAllStopPoints( convert.GetStopPoints() ); + + // Ease curve stuff + if( GetEaseInType() != plAnimEaseTypes::kNoEase ) + { + convert.SetEase( true, GetEaseInType(), GetEaseInMin(), GetEaseInMax(), GetEaseInLength() ); + } + if( GetEaseOutType() != plAnimEaseTypes::kNoEase ) + { + convert.SetEase( false, GetEaseOutType(), GetEaseOutMin(), GetEaseOutMax(), GetEaseOutLength() ); + } +} + +//// plAnimObjInterface Functions //////////////////////////////////////////// + +hsBool plAnimStealthNode::GetKeyList( INode *restrictedNode, hsTArray &outKeys ) +{ + if( !fPreppedForConvert ) + { + hsMessageBox( "This messages is to warn you that mcn screwed up in his attempt to create " + "a SetupProperties() pass for materials in this scene. You should probably let him know as soon as " + "possible, and also make a copy of this exact scene so that he can test with it and figure out what " + "is going wrong. Thank you.", "Mathew is Stupid Error", hsMessageBoxNormal ); + } + + GetMatAnimModKey( GetParentMtl(), (plMaxNode *)restrictedNode, GetSegmentName(), outKeys ); + return true; +} + +//// SetupProperties ///////////////////////////////////////////////////////// + +hsBool plAnimStealthNode::SetupProperties( plMaxNode *node, plErrorMsg *pErrMsg ) +{ + fPreppedForConvert = true; + plPassMtlBase *parent = GetParentMtl(); + if( parent != nil && fCachedSegMap == nil ) + { + fCachedSegMap = GetAnimSegmentMap( parent, nil ); + } + return true; +} + +//// ConvertDeInit /////////////////////////////////////////////////////////// + +hsBool plAnimStealthNode::ConvertDeInit( plMaxNode *node, plErrorMsg *pErrMsg ) +{ + fPreppedForConvert = false; + if( fCachedSegMap != nil ) + DeleteSegmentMap( fCachedSegMap ); + fCachedSegMap = nil; + + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthNode.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthNode.cpp new file mode 100644 index 00000000..69ebb2f2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthNode.cpp @@ -0,0 +1,970 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plAnimStealthNode - Stealthy hidden INode that represents a single // +// segment's worth of animation info for a material. // +// Stored as an INode so they can be "selected" // +// by components as targets of animation messages. // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plAnimStealthNode.h" +#include "plPassMtlBase.h" +#include "resource.h" + +#include "../MaxComponent/plMaxAnimUtils.h" +#include "../MaxComponent/plPickNodeBase.h" + +#include "iparamm2.h" + +extern TCHAR *GetString( int id ); +extern HINSTANCE hInstance; + + +//// Stealthy Class Desc ///////////////////////////////////////////////////// + +class plStealthClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return FALSE; } + void* Create(BOOL loading) { return TRACKED_NEW plAnimStealthNode(loading); } + const TCHAR* ClassName() { return GetString( IDS_STEALTH_NAME ); } + SClass_ID SuperClassID() { return HELPER_CLASS_ID; } + Class_ID ClassID() { return ANIMSTEALTH_CLASSID; } + const TCHAR* Category() { return NULL; } + const TCHAR* InternalName() { return _T("PlasmaAnimStealthInfo"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plStealthClassDesc sStealthClassDesc; +ClassDesc2* GetStealthClassDesc() { return &sStealthClassDesc; } + +//// plStealthDlgProc ///////////////////////////////////////////////////////// +// Dialog proc for the anim stealth child dialog + +class plStealthDlgProc : public ParamMap2UserDlgProc +{ +protected: + // Combo itemdata values + enum + { + kName, // Name of an animation/loop + kDefault, // Default combo value + kInvalid, // Invalid entry (couldn't find) + }; + + SegmentMap *fSegMap; + + HWND fhWnd; + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void DeleteThis() { IDeleteSegMap(); } + void SetThing(ReferenceTarget *m); + + void Update( TimeValue t, Interval &valid, IParamMap2 *pmap ); + +protected: + // Set all the controls to their stored value + void IInitControls( plAnimStealthNode *stealth, IParamBlock2 *pb); + + // Deletes all the allocated memory + void IDeleteSegMap(); + + void ILoadLoops(IParamBlock2 *pb); + + void ISetSel(HWND hCombo, const char *name); +}; + +const char *kAnimNameNone = ENTIRE_ANIMATION_NAME; + +static plStealthDlgProc sStealthDlgProc; + + + + + +//// Stealthy ParamBlock Desc //////////////////////////////////////////////// + +static plEaseAccessor sEaseAccessor( plAnimStealthNode::kBlockPB, plAnimStealthNode::kPBEaseInMin, + plAnimStealthNode::kPBEaseInMax, plAnimStealthNode::kPBEaseInLength, + plAnimStealthNode::kPBEaseOutMin, plAnimStealthNode::kPBEaseOutMax, + plAnimStealthNode::kPBEaseOutLength ); + +ParamBlockDesc2 plAnimStealthNode::sAnimStealthPB +( + kBlockPB, _T( "animStealth" ), IDS_STEALTH_NAME, GetStealthClassDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI, kRefParamBlock, + + // UI + IDD_STEALTH_ANIM, IDS_STEALTH_NAME, 0, 0, &sStealthDlgProc, + + kPBName, _T("animName"), TYPE_STRING, 0, 0, + end, + + kPBAutoStart, _T("autoStart"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_AUTO_START, + p_default, FALSE, + end, + + kPBLoop, _T("loop"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_LOOP, + p_default, TRUE, + end, + kPBLoopName, _T("loopName"), TYPE_STRING, 0, 0, + end, + + // Anim Ease + kPBEaseInType, _T("easeInType"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_PASS_ANIM_EASE_IN_NONE, IDC_PASS_ANIM_EASE_IN_CONST_ACCEL, IDC_PASS_ANIM_EASE_IN_SPLINE, + p_vals, plAnimEaseTypes::kNoEase, plAnimEaseTypes::kConstAccel, plAnimEaseTypes::kSpline, + p_default, plAnimEaseTypes::kNoEase, + end, + kPBEaseInLength, _T("easeInLength"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_IN_TIME, IDC_PASS_ANIM_EASE_IN_TIME_SPIN, 1.0, + p_accessor, &sEaseAccessor, + end, + kPBEaseInMin, _T("easeInMin"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_IN_MIN, IDC_PASS_ANIM_EASE_IN_MIN_SPIN, 1.0, + p_accessor, &sEaseAccessor, + end, + kPBEaseInMax, _T("easeInMax"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_IN_MAX, IDC_PASS_ANIM_EASE_IN_MAX_SPIN, 1.0, + p_accessor, &sEaseAccessor, + end, + + kPBEaseOutType, _T("easeOutType"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_PASS_ANIM_EASE_OUT_NONE, IDC_PASS_ANIM_EASE_OUT_CONST_ACCEL, IDC_PASS_ANIM_EASE_OUT_SPLINE, + p_vals, plAnimEaseTypes::kNoEase, plAnimEaseTypes::kConstAccel, plAnimEaseTypes::kSpline, + p_default, plAnimEaseTypes::kNoEase, + end, + kPBEaseOutLength, _T("easeOutLength"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_OUT_TIME, IDC_PASS_ANIM_EASE_OUT_TIME_SPIN, 1.0, + p_accessor, &sEaseAccessor, + end, + kPBEaseOutMin, _T("easeOutMin"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_OUT_MIN, IDC_PASS_ANIM_EASE_OUT_MIN_SPIN, 1.0, + p_accessor, &sEaseAccessor, + end, + kPBEaseOutMax, _T("easeOutMax"), TYPE_FLOAT, 0, 0, + p_default, 1.0, + p_range, 0.1, 99.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_OUT_MAX, IDC_PASS_ANIM_EASE_OUT_MAX_SPIN, 1.0, + p_accessor, &sEaseAccessor, + end, + + end +); + +plAnimStealthNode::plAnimStealthNode( BOOL loading ) : fClassDesc(nil), fParamBlock(nil), fParentMtl(nil) +{ + fCachedSegMap = nil; + fClassDesc = &sStealthClassDesc; + fClassDesc->MakeAutoParamBlocks( this ); +} + +plAnimStealthNode::~plAnimStealthNode() +{ +// DeleteAllRefsFromMe(); +} + +CreateMouseCallBack *plAnimStealthNode::GetCreateMouseCallBack() +{ + return nil; +} + +void plAnimStealthNode::SetParentMtl( plPassMtlBase *parent ) +{ + fParentMtl = parent; +} + +bool plAnimStealthNode::CanConvertToStealth( INode *objNode ) +{ + return ( ConvertToStealth( objNode ) != nil ); +} + +plAnimStealthNode *plAnimStealthNode::ConvertToStealth( INode *objNode ) +{ + if( objNode == nil ) + return nil; + + Object *obj = objNode->GetObjectRef(); + if( obj == nil ) + return nil; + + if( obj->CanConvertToType( ANIMSTEALTH_CLASSID ) ) + return (plAnimStealthNode *)obj; + + return nil; +} + + +const char *plAnimStealthNode::GetSegmentName( void ) const +{ + const char *str = fParamBlock->GetStr( (ParamID)kPBName ); + if( str == nil || str[ 0 ] == 0 ) + return ENTIRE_ANIMATION_NAME; + return str; +} + +void plAnimStealthNode::SetSegment( const char *name ) +{ + if( name == nil || strcmp(name, ENTIRE_ANIMATION_NAME) == 0 || name[ 0 ] == 0 ) + fParamBlock->SetValue( (ParamID)kPBName, 0, "" ); + else + fParamBlock->SetValue( (ParamID)kPBName, 0, (char *)name ); +} + +void plAnimStealthNode::SetNodeName( const char *parentName ) +{ + INode *node = GetINode(); + if( node != nil ) + { + char name[ 512 ], newName[ 512 ]; + sprintf( name, "%s : %s", parentName, GetSegmentName() ); + + if( GetCOREInterface()->GetINodeByName( name ) != nil ) + { + // For whatever reason, MakeNameUnique() doesn't ACTUALLY make a name unique! + // So we just need to more or less do it ourselves... + int i; + for( i = 1; i < 1024; i++ ) + { + sprintf( newName, "%s(%d)", name, i ); + if( GetCOREInterface()->GetINodeByName( newName ) == nil ) + break; + } + if( i == 1024 ) + { + // You've got to be kidding me... + char msg[ 2048 ]; + sprintf( msg, "WARNING: For some reason, we cannot find a unique name for the node '%s'. This" + " will most likely cause export problems. Exactly how many of these do we HAVE??", + name ); + hsMessageBox( msg, "WARNING!", hsMessageBoxNormal ); + } + } + else + strcpy( newName, name ); + + + node->SetName( newName ); + } +} + +int plAnimStealthNode::NumParamBlocks() +{ + return 1; +} + +IParamBlock2 *plAnimStealthNode::GetParamBlock( int i ) +{ + if( i == kRefParamBlock ) + return fParamBlock; + + return nil; +} + +IParamBlock2 *plAnimStealthNode::GetParamBlockByID( BlockID id ) +{ + if( fParamBlock && fParamBlock->ID() == id ) + return fParamBlock; + + return nil; +} + +RefTargetHandle plAnimStealthNode::Clone(RemapDir &remap) +{ + plAnimStealthNode *obj = (plAnimStealthNode *)fClassDesc->Create( false ); + // Do the base clone + BaseClone(this, obj, remap); + // Copy our references + if (fParamBlock) + obj->ReplaceReference( kRefParamBlock, fParamBlock->Clone( remap ) ); + + return obj; +} + +void plAnimStealthNode::BuildMesh(TimeValue t) +{ +} + +void plAnimStealthNode::FreeCaches() +{ +} + +void plAnimStealthNode::GetLocalBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box) +{ + box.MakeCube(Point3(0,0,0), 0); +} + +void plAnimStealthNode::GetWorldBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box) +{ + box.MakeCube(Point3(0,0,0), 0); +} + +int plAnimStealthNode::Display(TimeValue t, INode *node, ViewExp *vpt, int flags) +{ + return 0; +} + +int plAnimStealthNode::HitTest(TimeValue t, INode *node, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt) +{ + return 0; +} + +int plAnimStealthNode::NumRefs() +{ + return 1; +} + +RefTargetHandle plAnimStealthNode::GetReference( int i ) +{ + if( i == kRefParamBlock ) + return fParamBlock; + else if( i == kRefParentMtl ) + return fParentMtl; + + return nil; +} + +void plAnimStealthNode::SetReference( int i, RefTargetHandle rtarg ) +{ + if( i == kRefParamBlock ) + fParamBlock = (IParamBlock2 *)rtarg; + else if( i == kRefParentMtl ) + fParentMtl = (plPassMtlBase *)rtarg; +} + +RefResult plAnimStealthNode::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) +{ + return REF_SUCCEED; +} + +IOResult plAnimStealthNode::Save(ISave* isave) +{ + return IO_OK; +} + +IOResult plAnimStealthNode::Load(ILoad* iload) +{ + return IO_OK; +} + +plPassMtlBase *plAnimStealthNode::GetParentMtl( void ) +{ + return fParentMtl; +} + +class plGetRefs : public DependentEnumProc +{ + public: + + hsTArray fList; + + plGetRefs() { } + + virtual int proc( ReferenceMaker *rmaker ) + { + fList.Append( rmaker ); + return DEP_ENUM_CONTINUE; + } +}; + +hsBool plAnimStealthNode::IsParentUsedInScene( void ) +{ + if( GetParentMtl() == nil ) + return false; + + // There are two possibilities: either a node uses us and thus has a ref to us, + // or a multi-sub uses us that a node has a ref to us. + + // Note: we could do the loop as a helper function, but we only do it twice, + // so it's not *really* worth the effort... + //// NOTE: the following doesn't seem to work, but keeping here in case it ever does. + //// What really actually finds something is the enum dependents loop below + const char *mtlName = GetParentMtl()->GetName(); + + RefList &refList = GetRefList(); + RefListItem *item = refList.FirstItem(); + while( item != nil ) + { + TSTR s; + item->maker->GetClassName( s ); + + if( item->maker->SuperClassID() == BASENODE_CLASS_ID && !CanConvertToStealth( (INode *)( item->maker ) ) ) + return true; // Horray, a node has a ref to us! + + else if( item->maker->ClassID() == Class_ID(MULTI_CLASS_ID,0) ) + { + // Multi-sub, run the refs on that guy (we only go one up) + Mtl *multisub = (Mtl *)item->maker; + RefList &refList2 = multisub->GetRefList(); + RefListItem *item2 = refList.FirstItem(); + while( item2 != nil ) + { + if( item2->maker->SuperClassID() == BASENODE_CLASS_ID ) + return true; // Horray, a node has a ref to us! + item2 = item2->next; + } + + // No go, keep trying + } + else if( item->maker->SuperClassID() == MATERIAL_CLASS_ID ) + { + int q = 0; + } + + item = item->next; + } + + // Enum dependents + int i; + + plGetRefs callback; + GetParentMtl()->EnumDependents( &callback ); + for( i = 0; i < callback.fList.GetCount(); i++ ) + { + ReferenceMaker *maker = callback.fList[ i ]; + + TSTR s; + maker->GetClassName( s ); + + if( maker->SuperClassID() == BASENODE_CLASS_ID && !CanConvertToStealth( (INode *)maker ) ) + return true; // Horray, a node has a ref to us! + } + return false; +} + +INode *plAnimStealthNode::GetINode() +{ + // Go through the reflist looking for RefMakers with a ref to this component. + // There should only be one INode in this list. + RefList &refList = GetRefList(); + RefListItem *item = refList.FirstItem(); + while( item ) + { + if( item->maker->SuperClassID() == BASENODE_CLASS_ID ) + return (INode *)item->maker; + + item = item->next; + } + + return nil; +} + +void plStealthDlgProc::Update(TimeValue t, Interval& valid, IParamMap2* pmap) +{ + // Does the pmap match our pmap? + +} + +//// plStealthMouseOverrideProc ////////////////////////////////////////////// +// Because of wonderful linking problems with the MAX libraries, we can't +// actually use CreateChildMParamMap2 like we should. So instead, we use +// CreateChildCPParamMap2. However, *that* function calls the wrong interface +// to handle untrapped mouse messages, with the result that clicking and +// dragging scrolls the command pane (where components are displayed) instead +// of the material editor pane. +// To override this, we subclass each dialog so that we can capture the mouse +// messages before MAX processes them and then reroute them appropriately. +// Note: because MAX already uses the window long of the given window, we can't +// store the old proc of the window. However, since we always use +// CreateChildCPParamMap2, and because the MAX source code shows us that it +// always uses the same dialog proc for all windows created with that function, +// we can simply store the address of that proc the first time we subclass and +// use it for restoring every time thereafter (see the following DlgProc) + +static WNDPROC sOldStealthDlgProc = nil; + +static INT_PTR CALLBACK plStealthMouseOverrideProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + IParamMap2 *map = (IParamMap2 *)GetWindowLongPtr( hWnd, GWLP_USERDATA ); + + switch( msg ) + { + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MOUSEMOVE: + { + // We don't want the COREInterface to process our mouse messages with RollupMouseMessage; + // rather, we want IMtlParams to do it just like it would if we could actually call + // CreateChildMParamMap2 + IParamBlock2 *pb = map->GetParamBlock(); + if( pb != nil ) + { + plAnimStealthNode *stealth = (plAnimStealthNode *)pb->GetOwner(); + if( stealth != nil ) + { + plPassMtlBase *mtl = (plPassMtlBase *)stealth->GetParentMtl(); + mtl->fIMtlParams->RollupMouseMessage( hWnd, msg, wParam, lParam ); + } + } + return 0; + } + } + + if( sOldStealthDlgProc != nil ) + return CallWindowProc( sOldStealthDlgProc, hWnd, msg, wParam, lParam ); + else + return 0; +} + +BOOL plStealthDlgProc::DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + IParamBlock2 *pb = map->GetParamBlock(); + plAnimStealthNode *stealth = (plAnimStealthNode *)pb->GetOwner(); + + switch (msg) + { + case WM_INITDIALOG: + { + // Install our override proc so we can capture mouse messages ourselves. + // Note that the first time, we grab the old proc so we can restore with that + // one every time after, since they should always be the same proc + WNDPROC old = (WNDPROC)SetWindowLongPtr( hWnd, DWLP_DLGPROC, (LONG_PTR)plStealthMouseOverrideProc ); + if( sOldStealthDlgProc == nil ) + sOldStealthDlgProc = old; + + fhWnd = hWnd; + IInitControls( stealth, pb ); + + return TRUE; + } + + case WM_DESTROY: + // Restore our old proc + SetWindowLongPtr( hWnd, DWLP_DLGPROC, (LONG_PTR)sOldStealthDlgProc ); + break; + + case WM_ENABLE: + // The entire dialog was either enabled or disabled. + break; + + case WM_COMMAND: + // Loop selection changed + if( LOWORD( wParam ) == IDC_LOOPS && HIWORD( wParam ) == CBN_SELCHANGE ) + { + // If a loop is selected, save it + HWND hCombo = (HWND)lParam; + int sel = SendMessage( hCombo, CB_GETCURSEL, 0, 0 ); + if( sel != CB_ERR ) + { + if( SendMessage( hCombo, CB_GETITEMDATA, sel, 0 ) == kName ) + { + char buf[256]; + SendMessage( hCombo, CB_GETLBTEXT, sel, (LPARAM)buf ); + pb->SetValue( (ParamID)plAnimStealthNode::kPBLoopName, 0, buf ); + } + else + pb->SetValue( (ParamID)plAnimStealthNode::kPBLoopName, 0, "" ); + } + + return TRUE; + } + + // Auto-start or loop checkbox checked + if( LOWORD( wParam ) == IDC_LOOP && HIWORD( wParam ) == BN_CLICKED ) + { + BOOL checked = ( SendMessage( (HWND)lParam, BM_GETCHECK, 0, 0 ) == BST_CHECKED ); + + pb->SetValue( plAnimStealthNode::kPBLoop, 0, checked ); + EnableWindow( GetDlgItem( hWnd, IDC_LOOPS ), checked ); + return TRUE; + } + + // Refresh clicked + else if( LOWORD( wParam ) == IDC_REFRESH_ANIMS && HIWORD( wParam ) == BN_CLICKED ) + { + IInitControls( stealth, pb ); + return TRUE; + } + + break; + } + + return FALSE; +} + +void plStealthDlgProc::SetThing(ReferenceTarget *m) +{ + plAnimStealthNode *stealth = (plAnimStealthNode *)m; + IParamBlock2 *pb = stealth->GetParamBlockByID( plAnimStealthNode::kBlockPB ); + + IInitControls( stealth, pb ); +} + +void plStealthDlgProc::IDeleteSegMap() +{ + // If we have a segment map, delete the memory associated with it + DeleteSegmentMap( fSegMap ); + fSegMap = nil; +} + +void plStealthDlgProc::ISetSel(HWND hCombo, const char *name) +{ + // If there is a name, try and set that + if( name && strcmp( name, "" ) ) + { + int idx = SendMessage( hCombo, CB_FINDSTRINGEXACT, -1, (LPARAM)name ); + // If we can't find the saved name add a "not found" entry, so they know what it was + if( idx == -1 ) + { + char buf[256]; + sprintf( buf, "(not found) %s", name ); + idx = SendMessage( hCombo, CB_ADDSTRING, 0, (LPARAM)buf ); + SendMessage( hCombo, CB_SETITEMDATA, idx, kInvalid ); + } + + SendMessage( hCombo, CB_SETCURSEL, idx, 0 ); + } + // No name, set it to none + else + { + int count = SendMessage( hCombo, CB_GETCOUNT, 0, 0 ); + for( int i = 0; i < count; i++ ) + { + if( SendMessage( hCombo, CB_GETITEMDATA, i, 0 ) == kDefault ) + SendMessage( hCombo, CB_SETCURSEL, i, 0 ); + } + } +} + +void plStealthDlgProc::IInitControls( plAnimStealthNode *stealth, IParamBlock2 *pb ) +{ + IDeleteSegMap(); + + if( stealth->GetParentMtl() != nil ) + { + fSegMap = GetAnimSegmentMap( stealth->GetParentMtl(), nil ); + + ILoadLoops( pb ); + } + else + { + // ?? What should we do? + fSegMap = nil; + hsStatusMessage( "No parent material yet in plStealthDlgProc::IInitControls()...not good..." ); + } + + // Enable/disable the loop dropdown + EnableWindow( GetDlgItem( fhWnd, IDC_LOOPS ), pb->GetInt( (ParamID)plAnimStealthNode::kPBLoop ) ); +} + +void plStealthDlgProc::ILoadLoops(IParamBlock2 *pb) +{ + HWND hLoops = GetDlgItem( fhWnd, IDC_LOOPS ); + SendMessage( hLoops, CB_RESETCONTENT, 0, 0 ); + + // Add the default option + int defIdx = SendMessage( hLoops, CB_ADDSTRING, 0, (LPARAM)ENTIRE_ANIMATION_NAME ); + SendMessage( hLoops, CB_SETITEMDATA, defIdx, kDefault ); + + const char *segName = pb->GetStr( (ParamID)plAnimStealthNode::kPBName ); + if( segName == nil || fSegMap == nil ) + { + // Default of "entire animation", no other loop options + SendMessage( hLoops, CB_SETCURSEL, defIdx, 0 ); + return; + } + + SegmentSpec *animSpec = (*fSegMap)[ segName ]; + if( animSpec && fSegMap ) + { + // for each segment we found: + for( SegmentMap::iterator i = fSegMap->begin(); i != fSegMap->end(); i++ ) + { + SegmentSpec *spec = (*i).second; + + if( spec->fType == SegmentSpec::kLoop ) + { + // If the loop is contained by the animation, add it + if( (spec->fStart == -1 || spec->fStart >= animSpec->fStart) && + (spec->fEnd == -1 || spec->fEnd <= animSpec->fEnd) ) + { + // Add the name + int idx = SendMessage( hLoops, CB_ADDSTRING, 0, (LPARAM)spec->fName ); + SendMessage( hLoops, CB_SETITEMDATA, idx, kName ); + } + } + } + } + + ISetSel( hLoops, pb->GetStr( (ParamID)plAnimStealthNode::kPBLoopName ) ); +} + +void plAnimStealthNode::BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev) +{ + fClassDesc->BeginEditParams(ip, this, flags, prev); +} + +void plAnimStealthNode::EndEditParams(IObjParam *ip, ULONG flags, Animatable *next) +{ + fClassDesc->EndEditParams(ip, this, flags, next); +} + +//// ReleaseDlg ////////////////////////////////////////////////////////////// + +void plAnimStealthNode::ReleaseDlg( void ) +{ + IParamMap2 *map = fParamBlock->GetMap(); + fParamBlock->SetMap( nil ); + if( map != nil ) + DestroyChildCPParamMap2( map ); +} + +//// SwitchDlg /////////////////////////////////////////////////////////////// +// Switch underlying objects in the dialog (to avoid unnecessary deletion/ +// recreations) + +void plAnimStealthNode::SwitchDlg( plAnimStealthNode *toSwitchTo ) +{ + IParamMap2 *map = fParamBlock->GetMap(); + + fParamBlock->SetMap( nil ); + toSwitchTo->fParamBlock->SetMap( map ); + + map->SetParamBlock( toSwitchTo->fParamBlock ); + map->SetThing( (ReferenceTarget *)toSwitchTo ); + map->Invalidate(); + map->UpdateUI( 0 ); +} + +//// CreateAndEmbedDlg /////////////////////////////////////////////////////// +// Create the dialog for this object and place it inside the given dialog, +// centering it in the given control if any. + +bool plAnimStealthNode::CreateAndEmbedDlg( IParamMap2 *parentMap, IMtlParams *parentParams, HWND frameCtrl ) +{ + IParamMap2 *map = CreateChildCPParamMap2( fParamBlock, GetCOREInterface(), hInstance, + parentMap, MAKEINTRESOURCE( IDD_STEALTH_ANIM ), + nil, &sStealthDlgProc ); + fParamBlock->SetMap( map ); + + if( frameCtrl != nil ) + { + HWND child = fParamBlock->GetMap()->GetHWnd(); + RECT childFrame, centerFrame; + + ::GetClientRect( child, &childFrame ); + ::GetWindowRect( frameCtrl, ¢erFrame ); + ::MapWindowPoints( nil, parentMap->GetHWnd(), (POINT *)¢erFrame, 2 ); + + int frameWidth = centerFrame.right - centerFrame.left; + int frameHeight = centerFrame.bottom - centerFrame.top; + int childWidth = childFrame.right - childFrame.left; + int childHeight = childFrame.bottom - childFrame.top; + + ::OffsetRect( &childFrame, ( frameWidth - childWidth ) >> 1, ( frameHeight - childHeight ) >> 1 ); + ::OffsetRect( &childFrame, centerFrame.left, centerFrame.top ); + + ::SetWindowPos( child, nil, childFrame.left, childFrame.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER ); + } + + return true; +} + +//// GetWinDlg /////////////////////////////////////////////////////////////// +// Get the actual window handle of the currently active dialog displaying us + +HWND plAnimStealthNode::GetWinDlg( void ) const +{ + IParamMap2 *map = fParamBlock->GetMap(); + if( map != nil ) + return map->GetHWnd(); + + return nil; +} + +//// Picker Dialog for Restricted Animation Components ////////////////////////////////////////// + +class plPickAnimStealthNode : public plPickMtlNode +{ +protected: + ParamID fTypeID; + + void IAddUserType(HWND hList) + { + int type = fPB->GetInt(fTypeID); + + int idx = ListBox_AddString( hList, kUseParamBlockNodeString ); + if (type == plAnimObjInterface::kUseParamBlockNode && !fPB->GetINode(fNodeParamID)) + ListBox_SetCurSel(hList, idx); + + + idx = ListBox_AddString( hList, kUseOwnerNodeString ); + if (type == plAnimObjInterface::kUseOwnerNode) + ListBox_SetCurSel(hList, idx); + } + + void ISetUserType(plMaxNode* node, const char* userType) + { + if( hsStrEQ( userType, kUseParamBlockNodeString ) ) + { + ISetNodeValue(nil); + fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseParamBlockNode); + } + else if( hsStrEQ(userType, kUseOwnerNodeString ) ) + { + ISetNodeValue(nil); + fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseOwnerNode); + } + else + fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseParamBlockNode); + } + +public: + plPickAnimStealthNode(IParamBlock2* pb, ParamID nodeParamID, ParamID typeID, Mtl *mtl) : + plPickMtlNode(pb, nodeParamID, mtl), fTypeID(typeID) + { + } +}; + +//// plAnimObjInterface Functions //////////////////////////////////////////// + +void plAnimStealthNode::PickTargetNode( IParamBlock2 *destPB, ParamID destParamID, ParamID typeID ) +{ + plPickAnimStealthNode pick( destPB, destParamID, typeID, (Mtl *)GetParentMtl() ); + pick.DoPick(); +} + +const char *plAnimStealthNode::GetIfaceSegmentName( hsBool allowNil ) +{ + // When sending messages to material animations, they're already addressed for the right + // layer, no need for a segment name + return nil; +} + +//// Parameter Access Functions ////////////////////////////////////////////// + +#pragma warning( push ) +#pragma warning( disable:4800 ) // Forcing value to bool true or false (go figure, i'm even explicitly casting) +bool plAnimStealthNode::GetAutoStart( void ) const { return (bool)fParamBlock->GetInt( (ParamID)kPBAutoStart ); } +void plAnimStealthNode::SetAutoStart( bool b ) { fParamBlock->SetValue( (ParamID)kPBAutoStart, 0, (int)b ); }; + +bool plAnimStealthNode::GetLoop( void ) const { return fParamBlock->GetInt( (ParamID)kPBLoop ); } +const char *plAnimStealthNode::GetLoopName( void ) const { return fParamBlock->GetStr( (ParamID)kPBLoopName ); } +void plAnimStealthNode::SetLoop( bool b, const char *name ) +{ + fParamBlock->SetValue( (ParamID)kPBLoop, 0, (int)b ); + if( name == nil ) + fParamBlock->SetValue( (ParamID)kPBLoopName, 0, "" ); + else + fParamBlock->SetValue( (ParamID)kPBLoopName, 0, (char *)name ); +} + +UInt8 plAnimStealthNode::GetEaseInType( void ) const { return (UInt8)fParamBlock->GetInt( (ParamID)kPBEaseInType ); } +hsScalar plAnimStealthNode::GetEaseInLength( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseInLength ); } +hsScalar plAnimStealthNode::GetEaseInMin( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseInMin ); } +hsScalar plAnimStealthNode::GetEaseInMax( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseInMax ); } +void plAnimStealthNode::SetEaseIn( UInt8 type, hsScalar length, hsScalar min, hsScalar max ) +{ + fParamBlock->SetValue( (ParamID)kPBEaseInType, 0, (int)type ); + fParamBlock->SetValue( (ParamID)kPBEaseInLength, 0, (float)length ); + fParamBlock->SetValue( (ParamID)kPBEaseInMin, 0, (float)min ); + fParamBlock->SetValue( (ParamID)kPBEaseInMax, 0, (float)max ); +} + +UInt8 plAnimStealthNode::GetEaseOutType( void ) const { return (UInt8)fParamBlock->GetInt( (ParamID)kPBEaseOutType ); } +hsScalar plAnimStealthNode::GetEaseOutLength( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseOutLength ); } +hsScalar plAnimStealthNode::GetEaseOutMin( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseOutMin ); } +hsScalar plAnimStealthNode::GetEaseOutMax( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseOutMax ); } +void plAnimStealthNode::SetEaseOut( UInt8 type, hsScalar length, hsScalar min, hsScalar max ) +{ + fParamBlock->SetValue( (ParamID)kPBEaseOutType, 0, (int)type ); + fParamBlock->SetValue( (ParamID)kPBEaseOutLength, 0, (float)length ); + fParamBlock->SetValue( (ParamID)kPBEaseOutMin, 0, (float)min ); + fParamBlock->SetValue( (ParamID)kPBEaseOutMax, 0, (float)max ); +} +#pragma warning( pop ) // Forcing value to bool true or false (go figure, i'm even explicitly casting) + +//// Parent Accessor Functions /////////////////////////////////////////////// + +plStealthNodeAccessor &plStealthNodeAccessor::GetInstance( void ) +{ + static plStealthNodeAccessor instance; + return instance; +} + +void plStealthNodeAccessor::ISetParent( ReferenceTarget *target, plPassMtlBase *parent ) +{ + if( target != nil && target->ClassID() == ANIMSTEALTH_CLASSID ) + { + ( (plAnimStealthNode *)target )->SetParentMtl( parent ); + } +} + +void plStealthNodeAccessor::TabChanged( tab_changes changeCode, Tab *tab, ReferenceMaker *owner, + ParamID id, int tabIndex, int count ) +{ + if( changeCode == tab_insert || changeCode == tab_append ) + { + if( owner->SuperClassID() != MATERIAL_CLASS_ID ) + return; + plPassMtlBase *mtl = (plPassMtlBase *)owner; + + while( count > 0 ) + { + ISetParent( (*tab)[ tabIndex ].r, mtl ); + tabIndex++; + count--; + } + } + else if( changeCode == tab_delete || changeCode == tab_ref_deleted ) + { + // How are we supposed to handle this if we don't even get a stinkin pointer?? + } +} + +void plStealthNodeAccessor::Set( PB2Value &v, ReferenceMaker *owner, ParamID id, int tabIndex, TimeValue t ) +{ + // Bit of error checking + if( owner->SuperClassID() != MATERIAL_CLASS_ID ) + return; + + plPassMtlBase *mtl = (plPassMtlBase *)owner; + + IParamBlock2 *pb = mtl->fAnimPB; + + // A stealth node paramBlock value just got set. First make sure we + // un-set the old stealth's parent + ISetParent( pb->GetReferenceTarget( id, tabIndex ), nil ); + + // So make sure that the stealth node that was just added gets its parent mtl set properly + ISetParent( v.r, mtl ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthNode.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthNode.h new file mode 100644 index 00000000..2f33dec1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plAnimStealthNode.h @@ -0,0 +1,247 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plAnimStealthNode - Stealthy hidden INode that represents a single // +// segment's worth of animation info for a material. // +// Stored as an INode so they can be "selected" // +// by components as targets of animation messages. // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plAnimStealthNode_h +#define _plAnimStealthNode_h + +#include "Max.h" +#include "iparamb2.h" +#include "iparamm2.h" + +#include "hsTypes.h" +#include "../pnKeyedObject/plKey.h" +#include "../MaxComponent/plAnimObjInterface.h" +#include "../MaxComponent/plMaxAnimUtils.h" + +extern TCHAR *GetString(int id); +extern HINSTANCE hInstance; + +#define ANIMSTEALTH_CLASSID Class_ID(0xd0272f8, 0x750349c9) + +#define REFMSG_NOTETRACK_ADDED REFMSG_USER + 1 + +class plPassMtlBase; +class NoteTrack; +class plMaxNode; +class plErrorMsg; +class plAnimTimeConvert; + +//// Class Def /////////////////////////////////////////////////////////////// + +class plAnimStealthNode : public HelperObject, public plAnimObjInterface +{ +protected: + ClassDesc2 *fClassDesc; + + IParamBlock2 *fParamBlock; + plPassMtlBase *fParentMtl; + + static ParamBlockDesc2 sAnimStealthPB; + + hsBool fPreppedForConvert; + SegmentMap *fCachedSegMap; + + SegmentSpec *IGetSegmentSpec( void ) const; + +public: + + // Some IDs + enum + { + kBlockPB = 0 + }; + + enum Refs + { + kRefParamBlock, + kRefParentMtl + }; + // ParamBlock IDs + enum ParamBlockIDs + { + kPBAutoStart, // Start the Animation on load (V2) + kPBLoop, // Start Looping at Begin Location + kPBName, // Name of the notetrack animation to play + kPBLoopName, // Name of the notetrack specified loop + kPBEaseInType, + kPBEaseOutType, + kPBEaseInLength, + kPBEaseOutLength, + kPBEaseInMin, + kPBEaseInMax, + kPBEaseOutMin, + kPBEaseOutMax + }; + + plAnimStealthNode( BOOL loading ); + virtual ~plAnimStealthNode(); + void DeleteThis() { delete this; } + + INode *GetINode( void ); + plPassMtlBase *GetParentMtl( void ); + void SetParentMtl( plPassMtlBase *parent ); + void SetNodeName( const char *parentName ); + + // Create the dialog for this object and place it inside the given dialog, centering it in the given control if any + bool CreateAndEmbedDlg( IParamMap2 *parentMap, IMtlParams *parentParams, HWND frameCtrl = nil ); + + // Release said dialog + void ReleaseDlg( void ); + + // Switch underlying objects in the dialog (to avoid unnecessary deletion/recreations) + void SwitchDlg( plAnimStealthNode *toSwitchTo ); + + // Get the actual window handle of the currently active dialog displaying us + HWND GetWinDlg( void ) const; + + // Interesting functions + const char *GetSegmentName( void ) const; + void SetSegment( const char *name ); // nil for "entire animation" + + // Conversion from stealth's INode to the actual object + static bool CanConvertToStealth( INode *objNode ); + static plAnimStealthNode *ConvertToStealth( INode *objNode ); + + /////////////////////////////////////////////////////////////////////////////////////// + // Required Max functions + // + TCHAR* GetObjectName() { return (TCHAR*)fClassDesc->ClassName(); } + void InitNodeName(TSTR& s) { s = fClassDesc->InternalName(); } + void GetClassName(TSTR& s) { s = fClassDesc->ClassName(); } + Class_ID ClassID() { return ANIMSTEALTH_CLASSID; } + + RefTargetHandle Clone(RemapDir &remap); + + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + RefResult NotifyRefChanged(Interval changeInt,RefTargetHandle hTarget, PartID& partID, RefMessage message); + + // allow retreival of our paramblock from other plug-ins + // and the max core + int NumParamBlocks(); + IParamBlock2* GetParamBlock(int i); + IParamBlock2* GetParamBlockByID(BlockID id); + + // We override because we don't want to be able to animate this sucker + int NumSubs() { return 0; } + Animatable *SubAnim( int i ) { return nil; } + TSTR SubAnimName( int i ) { return fClassDesc->ClassName(); } + + // plug-in mouse creation callback + CreateMouseCallBack* GetCreateMouseCallBack(); + + void BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev); + void EndEditParams(IObjParam *ip, ULONG flags, Animatable *next); +// void SelectionSetChanged(Interface *ip, IUtil *iu); + + void BuildMesh(TimeValue t); + void FreeCaches(); + void GetLocalBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box); + void GetWorldBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box); + int Display(TimeValue t, INode *node, ViewExp *vpt, int flags); + int HitTest(TimeValue t, INode *node, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt); + ObjectState Eval(TimeValue t) { return ObjectState(this); } + + IOResult Save(ISave* isave); + IOResult Load(ILoad* iload); + + int CanConvertToType( Class_ID obtype ) { return ( obtype == ANIMSTEALTH_CLASSID ) ? 1 : 0; } + + const char *GetCategory() { return fClassDesc->Category(); } + + /// Parameter access + + bool GetAutoStart( void ) const; + void SetAutoStart( bool b ); + + bool GetLoop( void ) const; + const char *GetLoopName( void ) const; + void SetLoop( bool b, const char *name ); + + UInt8 GetEaseInType( void ) const; + hsScalar GetEaseInLength( void ) const; + hsScalar GetEaseInMin( void ) const; + hsScalar GetEaseInMax( void ) const; + void SetEaseIn( UInt8 type, hsScalar length, hsScalar min, hsScalar max ); + + UInt8 GetEaseOutType( void ) const; + hsScalar GetEaseOutLength( void ) const; + hsScalar GetEaseOutMin( void ) const; + hsScalar GetEaseOutMax( void ) const; + void SetEaseOut( UInt8 type, hsScalar length, hsScalar min, hsScalar max ); + + // Conversion stuff + void GetAllStopPoints( hsTArray &out ); + hsScalar GetSegStart( void ) const; + hsScalar GetSegEnd( void ) const; + void GetLoopPoints( hsScalar &start, hsScalar &end ) const; + void StuffToTimeConvert( plAnimTimeConvert &convert, hsScalar maxLength ); + + // plAnimObjInterface functions + virtual void PickTargetNode( IParamBlock2 *destPB, ParamID destParamID, ParamID typeID ); + virtual hsBool IsNodeRestricted( void ) { return true; } + virtual const char *GetIfaceSegmentName( hsBool allowNil ); + virtual hsBool GetKeyList( INode *restrictedNode, hsTArray &outKeys ); + virtual hsBool MightRequireSeparateMaterial( void ) { return true; } + + // Convert time, called on the setupProps pass for each material applied to a node in the scene + virtual hsBool SetupProperties( plMaxNode *node, plErrorMsg *pErrMsg ); + virtual hsBool ConvertDeInit( plMaxNode *node, plErrorMsg *pErrMsg ); + + // Returns true if the parent material is applied to any node in the scene, false otherwise + hsBool IsParentUsedInScene( void ); +}; + +//// Accessor for Parent's ParamBlock //////////////////////////////////////// + +class plStealthNodeAccessor : public PBAccessor +{ + protected: + + void ISetParent( ReferenceTarget *target, plPassMtlBase *parent ); + + void IHandleSet( PB2Value &v, ReferenceMaker *owner, ParamID id, int tabIndex, TimeValue t ); + + public: + + plStealthNodeAccessor() { } + static plStealthNodeAccessor &GetInstance( void ); + + virtual void Set( PB2Value &v, ReferenceMaker *owner, ParamID id, int tabIndex, TimeValue t ); + virtual void TabChanged( tab_changes changeCode, Tab *tab, ReferenceMaker *owner, + ParamID id, int tabIndex, int count ); +}; + +#endif //_plAnimStealthNode_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtl.cpp new file mode 100644 index 00000000..d81d5c4a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtl.cpp @@ -0,0 +1,660 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plBumpMtl.h" +#include "resource.h" +//extern ClassDesc2* GetMaxLayerDesc(); +#include "Shaders.h" + +#include "plBumpMtlBasicPB.h" + +#include "iparamm2.h" + +#include "Layers/plLayerTex.h" +#include "Layers/plStaticEnvLayer.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +extern HINSTANCE hInstance; + +class plBumpMtlClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return TRACKED_NEW plBumpMtl(loading); } + const TCHAR* ClassName() { return GetString(IDS_BUMP_MTL); } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + Class_ID ClassID() { return BUMP_MTL_CLASS_ID; } + const TCHAR* Category() { return NULL; } + const TCHAR* InternalName() { return _T("PlasmaMaterial"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plBumpMtlClassDesc plBumpMtlDesc; +ClassDesc2* GetBumpMtlDesc() { return &plBumpMtlDesc; } + +// For initializing paramblock descriptor +ParamBlockDesc2 *GetBumpBasicPB(); +ParamBlockDesc2 *GetBumpLayersPB(); + +#include "plBumpMtlBasicPBDec.h" +#include "plBumpMtlAnimPBDec.h" + +plBumpMtl::plBumpMtl(BOOL loading) : plPassMtlBase( loading ) +{ + plBumpMtlDesc.MakeAutoParamBlocks( this ); + fBasicPB->SetValue( kBumpBasLayer, 0, TRACKED_NEW plLayerTex ); + + // If we do this later (like, when the dialog loads) something blows up, + // somewhere in Max. It didn't in 4, it does in 7. This seems to fix it. + if (!loading) + IVerifyStealthPresent(ENTIRE_ANIMATION_NAME); +} + +ParamDlg* plBumpMtl::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + fIMtlParams = imp; + IAutoMParamDlg* masterDlg = plBumpMtlDesc.CreateParamDlgs(hwMtlEdit, imp, this); + + return (ParamDlg*)masterDlg; +} + +BOOL plBumpMtl::SetDlgThing(ParamDlg* dlg) +{ + return FALSE; +} + +Interval plBumpMtl::Validity(TimeValue t) +{ +#if 0 // mf horse + Interval valid = FOREVER; + +/* for (int i = 0; i < fSubTexmap.Count(); i++) + { + if (fSubTexmap[i]) + valid &= fSubTexmap[i]->Validity(t); + } +*/ +// float u; +// fPBlock->GetValue(pb_spin,t,u,valid); + return valid; +#else // mf horse + const char* name = GetName(); + + // mf horse - Hacking in something like real validity checking + // to get material animations working. No warranty, this is just + // better than nothing. + Interval v = FOREVER; + fBasicPB->GetValidity(t, v); + + if( fBasicPB->GetTexmap(kBumpBasLayer) ) + v &= fBasicPB->GetTexmap(kBumpBasLayer)->Validity(t); + return v; +#endif // mf horse +} + +//// GetReference //////////////////////////////////////////////////////////// +// Note: need to overload because MAX for some reason writes out the +// references by their INDEX. ARRRRGH! + +RefTargetHandle plBumpMtl::GetReference( int i ) +{ + switch( i ) + { + case kRefBasic: return fBasicPB; + case kRefAnim: return fAnimPB; + } + + return plPassMtlBase::GetReference( i ); +} + +//// SetReference //////////////////////////////////////////////////////////// +// Note: need to overload because MAX for some reason writes out the +// references by their INDEX. ARRRRGH! + +void plBumpMtl::SetReference(int i, RefTargetHandle rtarg) +{ + if (i == kRefBasic) + fBasicPB = (IParamBlock2 *)rtarg; + else if (i == kRefAnim) + fAnimPB = (IParamBlock2 *)rtarg; + else + plPassMtlBase::SetReference( i, rtarg ); +} + +/*===========================================================================*\ + | Subanim & References support +\*===========================================================================*/ + +int plBumpMtl::NumSubs() +{ + return 3; +} + +TSTR plBumpMtl::SubAnimName(int i) +{ + switch (i) + { + case 0: return fBasicPB->GetLocalName(); + case 1: return fAnimPB->GetLocalName(); + case 2: return "Base Layer"; + } + + return ""; +} + +Animatable* plBumpMtl::SubAnim(int i) +{ + switch (i) + { + case 0: return fBasicPB; + case 1: return fAnimPB; + case 2: return fBasicPB->GetTexmap(kBumpBasLayer); + break; + } + + return NULL; +} + +int plBumpMtl::NumParamBlocks() +{ + return 2; +} + +IParamBlock2* plBumpMtl::GetParamBlock(int i) +{ + return (IParamBlock2*)GetReference(i); +} + +IParamBlock2* plBumpMtl::GetParamBlockByID(BlockID id) +{ + if (fBasicPB->ID() == id) + return fBasicPB; + else if (fAnimPB->ID() == id) + return fAnimPB; + + return NULL; +} + +RefResult plBumpMtl::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) +{ + return plPassMtlBase::NotifyRefChanged( changeInt, hTarget, partID, message ); +} + +//////////////////////////////////////////////////////////////////////////////// +// Subtexmap access + +int plBumpMtl::NumSubTexmaps() +{ + return 1; +} + +Texmap* plBumpMtl::GetSubTexmap(int i) +{ + if (i == 0) + return fBasicPB->GetTexmap(kBumpBasLayer); + + return NULL; +} + +void plBumpMtl::SetSubTexmap(int i, Texmap *m) +{ + if (i == 0) + fBasicPB->SetValue(kBumpBasLayer, 0, m); +} + +TSTR plBumpMtl::GetSubTexmapSlotName(int i) +{ + if (i == 0) + return "Base"; + + return ""; +} + +TSTR plBumpMtl::GetSubTexmapTVName(int i) +{ + return GetSubTexmapSlotName(i); +} + +/*===========================================================================*\ + | Updating and cloning +\*===========================================================================*/ + +RefTargetHandle plBumpMtl::Clone(RemapDir &remap) +{ + plBumpMtl *mnew = TRACKED_NEW plBumpMtl(FALSE); + plPassMtlBase::ICloneBase( mnew, remap ); + return (RefTargetHandle)mnew; +} + +void plBumpMtl::ICloneRefs( plPassMtlBase *target, RemapDir &remap ) +{ + target->ReplaceReference(kRefBasic, remap.CloneRef(fBasicPB)); + target->ReplaceReference(kRefAnim, remap.CloneRef(fAnimPB)); +} + +void plBumpMtl::NotifyChanged() +{ + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +} + +void plBumpMtl::Update(TimeValue t, Interval& valid) +{ + // mf horse - Hacking in something like real validity checking + // to get material animations working. No warranty, this is just + // better than nothing. + if (!fIValid.InInterval(t)) + { + fIValid.SetInfinite(); + if( fBasicPB->GetTexmap(kBumpBasLayer) ) + fBasicPB->GetTexmap(kBumpBasLayer)->Update(t, fIValid); + +// fLayersPB->GetValue(kMtlLayLayer1On, t, fMapOn[0], fIValid); +/* + for (int i = 0; i < fSubTexmap.Count(); i++) + { + if (fSubTexmap[i]) + fSubTexmap[i]->Update(t,fIValid); + } +*/ + } + + valid &= fIValid; +} + +/*===========================================================================*\ + | Determine the characteristics of the material +\*===========================================================================*/ + +void plBumpMtl::SetAmbient(Color c, TimeValue t) {} +void plBumpMtl::SetDiffuse(Color c, TimeValue t) {} +void plBumpMtl::SetSpecular(Color c, TimeValue t) {} +void plBumpMtl::SetShininess(float v, TimeValue t) {} + +Color plBumpMtl::GetAmbient(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plBumpMtl::GetDiffuse(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plBumpMtl::GetSpecular(int mtlNum, BOOL backFace) { return Color(0,0,0); } + +float plBumpMtl::GetXParency(int mtlNum, BOOL backFace) +{ + return 0.f; +} + +float plBumpMtl::GetShininess(int mtlNum, BOOL backFace) { return 0.0f; } +float plBumpMtl::GetShinStr(int mtlNum, BOOL backFace) { return 0.0f; } +float plBumpMtl::WireSize(int mtlNum, BOOL backFace) { return 0.0f; } + +///////////////////////////////////////////////////////////////// + +void plBumpMtl::SetupGfxMultiMaps(TimeValue t, Material *mtl, MtlMakerCallback &cb) +{ +#if 0 + if (texHandleValid.InInterval(t)) { + mtl->texture.SetCount(numTexHandlesUsed); + for (int i=0; itexture[i].textHandle = texHandle[i]->GetHandle(); + Texmap *tx = (*maps)[useSubForTex[i]].map; + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[i], tx ); + SetTexOps(mtl,i,texOpsType[i]); + } + } + return; + } +#endif + +#if 0 // WTF?!?!?!? + Texmap *tx[2]; + int diffChan = stdIDToChannel[ ID_DI ]; + int opacChan = stdIDToChannel[ ID_OP ]; + tx[0] = (*maps)[diffChan].IsActive()?(*maps)[diffChan].map:NULL; + tx[1] = (*maps)[opacChan].IsActive()?(*maps)[opacChan].map:NULL; +#endif + + int nsupport = cb.NumberTexturesSupported(); +#if 0 + BITMAPINFO *bmi[NTEXHANDLES]; + + int nmaps=0; + for (int i=0; itexture.SetCount(nmaps); + if (nmaps==0) + return; + for (i=0; itexture[i].textHandle = NULL; + texHandleValid.SetInfinite(); + Interval valid; + BOOL needDecal = FALSE; + int ntx = 0; + int op; + + int forceW = 0; + int forceH = 0; + if (tx[0]) { + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[0], tx[0]); + TextureInfo &ti = mtl->texture[0]; + if (ti.tiling[0]==GW_TEX_NO_TILING||ti.tiling[1]==GW_TEX_NO_TILING) + needDecal = TRUE; + op = needDecal?TXOP_ALPHABLEND:TXOP_MODULATE; + bmi[0] = tx[0]->GetVPDisplayDIB(t,cb,valid,FALSE); + if (bmi[0]) { + texHandleValid &= valid; + useSubForTex[0] = diffChan; + ntx = 1; + forceW = bmi[0]->bmiHeader.biWidth; + forceH = bmi[0]->bmiHeader.biHeight; + } + } + if (tx[1]) { + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[ntx], tx[1]); + if (nsupport>ntx) { + bmi[1] = tx[1]->GetVPDisplayDIB(t,cb,valid,TRUE); + if (bmi[1]) { + texHandleValid &= valid; + StuffAlpha(bmi[1], (*maps)[opacChan].amount, GetOpacity(t),ntx?whiteCol:pShader->GetDiffuseClr(t)); + texHandle[ntx] = cb.MakeHandle(bmi[1]); + bmi[1] = NULL; + mtl->texture[ntx].textHandle = texHandle[ntx]->GetHandle(); + SetTexOps(mtl,ntx,TXOP_OPACITY); + useSubForTex[ntx] = opacChan; + ntx++; + } + } + else { + if (!needDecal) { + TextureInfo ti; +// if (SameUV(mtl->texture[0],mtl->texture[1])) { + // Not really correct to combine channels for different UV's but what the heck. + bmi[1] = tx[1]->GetVPDisplayDIB(t,cb,valid,TRUE, forceW, forceH); + if (bmi[1]) { + texHandleValid &= valid; + StuffAlphaInto(bmi[1], bmi[0], (*maps)[opacChan].amount, GetOpacity(t)); + op = TXOP_OPACITY; + free(bmi[1]); + bmi[1] = NULL; + } +// } + } + } + } + if (bmi[0]) { + texHandle[0] = cb.MakeHandle(bmi[0]); + bmi[0] = NULL; + mtl->texture[0].textHandle = texHandle[0]->GetHandle(); + SetTexOps(mtl,0,op); + } + mtl->texture.SetCount(ntx); + numTexHandlesUsed = ntx; +#endif +} + +/*===========================================================================*\ + | Actual shading takes place +\*===========================================================================*/ + +void plBumpMtl::GetInterpVtxValue(int channel, ShadeContext &sc, Point3 &val) +{ + Mesh *mesh = sc.globContext->GetRenderInstance(sc.NodeID())->mesh; + if (mesh != nil) + { + Face *maxFace = &mesh->faces[ sc.FaceNumber() ]; + UVVert *map = mesh->mapVerts(channel); + if (map != nil) + { + Point3 p0 = map[maxFace->getVert( 0 )]; + Point3 p1 = map[maxFace->getVert( 1 )]; + Point3 p2 = map[maxFace->getVert( 2 )]; + Point3 interp = sc.BarycentricCoords(); + val.x = interp.x * p0.x + interp.y * p1.x + interp.z * p2.x; + val.y = interp.x * p0.y + interp.y * p1.y + interp.z * p2.y; + val.z = interp.x * p0.z + interp.y * p1.z + interp.z * p2.z; + return; + } + } + + // No value defined... set default. + if (channel == MAP_SHADING) + val.x = val.y = val.z = 0.0f; + else + val.x = val.y = val.z = 1.0f; +} + +void plBumpMtl::Shade(ShadeContext& sc) +{ + // Get the background color + Color backColor, backTrans; + sc.GetBGColor(backColor, backTrans); + + ShadeWithBackground(sc, backColor); +} + +//// Requirements //////////////////////////////////////////////////////////// +// Tells MAX what we need to render ourselves properly, such as translucency, +// two-sidedness, etc. Flags are in imtl.h in the MAX SDK. + +ULONG plBumpMtl::Requirements( int subMtlNum ) +{ + ULONG req = 0; + + + req = Mtl::Requirements( subMtlNum ); + + // Uncomment this to get the background color fed to our ShadeWithBackground() + // (slower processing tho) + //req |= MTLREQ_BGCOL; + req |= MTLREQ_UV; + req |= MTLREQ_TRANSP; + + if (req & MTLREQ_FACEMAP) + { + int i = 0; + } + return req; +} + +void plBumpMtl::ShadeWithBackground(ShadeContext &sc, Color background, bool useVtxAlpha /* = true */) +{ +#if 1 + + // old +#if 0 + Color lightCol,rescol, diffIllum0; + RGBA mval; + Point3 N0,P; + BOOL bumped = FALSE; + int i; + + if (gbufID) + sc.SetGBufferID(gbufID); + + if (sc.mode == SCMODE_SHADOW) { + float opac = 0.0; + for (i=0; i < NumSubTexmaps(); i++) { + if (SubTexmapOn(i)) { + hsMaxLayerBase *hsmLay = (hsMaxLayerBase *)GetSubTexmap(i); + opac += hsmLay->GetOpacity(t); + } + } + + float f = 1.0f - opac; + sc.out.t = Color(f,f,f); + return; + } + + N0 = sc.Normal(); + P = sc.P(); +#endif + + TimeValue t = sc.CurTime(); + Color color(0, 0, 0); + float alpha = 0.0; + + // Evaluate Base layer + Texmap *map = fBasicPB->GetTexmap(kBumpBasLayer); + if (map && ( map->ClassID() == LAYER_TEX_CLASS_ID + || map->ClassID() == STATIC_ENV_LAYER_CLASS_ID ) ) + { + plLayerTex *layer = (plLayerTex*)map; + AColor evalColor = layer->EvalColor(sc); + + color = evalColor; + alpha = evalColor.a; + } + +#if 1 + AColor black; + black.Black(); + AColor white; + white.White(); + + + SIllumParams ip; + // + // Shading setup + // + + // Setup the parameters for the shader + ip.amb = black; + ip.diff = white; + ip.diffIllum = black; + ip.specIllum = black; + ip.N = sc.Normal(); + ip.V = sc.V(); + + + // + // Specularity + // + ip.spec = black; + ip.sh_str = 0; + ip.ph_exp = 0; + ip.shine = 0; + ip.softThresh = 0; + + // + + // Do the shading + Shader *myShader = GetShader(SHADER_BLINN); + myShader->Illum(sc, ip); + + ip.diffIllum.ClampMinMax(); + ip.specIllum.ClampMinMax(); + ip.diffIllum = ip.amb * sc.ambientLight + ip.diff * ip.diffIllum; + +// AColor returnColor = AColor(opac * ip.diffIllum + ip.specIllum, opac) +#endif + + float vtxAlpha = 1.0f; + if (useVtxAlpha && GetOutputBlend() == plPassMtlBase::kBlendAlpha) + { + Point3 p; + GetInterpVtxValue(MAP_ALPHA, sc, p); + vtxAlpha = p.x; + } + alpha *= vtxAlpha; + + // MAX will do the additive/alpha/no blending for us based on what Requirements() + // we tell it. However, since MAX's formula is bgnd*sc.out.t + sc.out.c, + // we have to multiply our output color by the alpha. + // If we ever need a more complicated blending function, you can request the + // background color via Requirements() (otherwise it's just black) and then do + // the blending yourself; however, if the transparency isn't set, the shadows + // will be opaque, so be careful. + Color outC = ip.diffIllum + ip.specIllum; + + sc.out.c = ( outC * alpha ); + sc.out.t = Color( 1.f - alpha, 1.f - alpha, 1.f - alpha ); + +#endif +} + +float plBumpMtl::EvalDisplacement(ShadeContext& sc) +{ + return 0.0f; +} + +Interval plBumpMtl::DisplacementValidity(TimeValue t) +{ + Interval iv; + iv.SetInfinite(); + + return iv; +} + +bool plBumpMtl::HasAlpha() +{ + return ((plLayerTex *)fBasicPB->GetTexmap(kBumpBasLayer))->HasAlpha(); +} +// Massive list of inherited accessor functions for ParamBlock data + +// Advanced Block +int plBumpMtl::GetBasicWire() { return 0; } +int plBumpMtl::GetMeshOutlines() { return 0; } +int plBumpMtl::GetTwoSided() { return 0; } +int plBumpMtl::GetSoftShadow() { return 0; } +int plBumpMtl::GetNoProj() { return 0; } +int plBumpMtl::GetVertexShade() { return 0; } +int plBumpMtl::GetNoShade() { return 0; } +int plBumpMtl::GetNoFog() { return 0; } +int plBumpMtl::GetWhite() { return 0; } +int plBumpMtl::GetZOnly() { return 0; } +int plBumpMtl::GetZClear() { return 0; } +int plBumpMtl::GetZNoRead() { return 0; } +int plBumpMtl::GetZNoWrite() { return 0; } +int plBumpMtl::GetZInc() { return 0; } +int plBumpMtl::GetAlphaTestHigh() { return 0; } + +// Animation block +char * plBumpMtl::GetAnimName() { return fAnimPB->GetStr(kPBAnimName); } +int plBumpMtl::GetAutoStart() { return fAnimPB->GetInt(kPBAnimAutoStart); } +int plBumpMtl::GetLoop() { return fAnimPB->GetInt(kPBAnimLoop); } +char * plBumpMtl::GetAnimLoopName() { return fAnimPB->GetStr(kPBAnimLoopName); } + +// Basic block +int plBumpMtl::GetColorLock() { return 0; } +Color plBumpMtl::GetAmbColor() { return Color(0,0,0); } +Color plBumpMtl::GetColor() { return Color(0,0,0); } +int plBumpMtl::GetOpacity() { return 100; } +int plBumpMtl::GetEmissive() { return 0; } +int plBumpMtl::GetUseSpec() { return 0; } +int plBumpMtl::GetShine() { return 0; } +Color plBumpMtl::GetSpecularColor() { return Color(0,0,0); } +int plBumpMtl::GetDiffuseColorLock() { return 0; } +Color plBumpMtl::GetRuntimeColor() { return fBasicPB->GetColor(kBumpBasRunColor); } +Control *plBumpMtl::GetPreshadeColorController() { return nil; } +Control *plBumpMtl::GetAmbColorController() { return nil; } +Control *plBumpMtl::GetOpacityController() { return nil; } +Control *plBumpMtl::GetSpecularColorController() { return nil; } +Control *plBumpMtl::GetRuntimeColorController() { return fBasicPB->GetController(ParamID(kBumpBasRunColor)); } + +// Layer block +Texmap *plBumpMtl::GetBaseLayer() { return fBasicPB->GetTexmap(kBumpBasLayer); } +int plBumpMtl::GetTopLayerOn() { return 0; } +Texmap *plBumpMtl::GetTopLayer() { return nil; } +int plBumpMtl::GetLayerBlend() { return 0; } +int plBumpMtl::GetOutputAlpha() { return 0; } +int plBumpMtl::GetOutputBlend() { return fBasicPB->GetInt( kBumpBasSpecular ) ? plPassMtlBase::kBlendAdd : plPassMtlBase::kBlendAlpha; } diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtl.h new file mode 100644 index 00000000..9e2c632b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtl.h @@ -0,0 +1,178 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_BUMPMTL_H +#define PL_BUMPMTL_H + +#include "Max.h" +#include "iparamb2.h" + +#include "../resource.h" +#include "plPassMtlBase.h" + +#define BUMP_MTL_CLASS_ID Class_ID(0x4dbf7b0a, 0x92226de) + +extern TCHAR *GetString(int id); + +class plBumpMtl : public plPassMtlBase +{ +protected: + + virtual void ICloneRefs( plPassMtlBase *target, RemapDir &remap ); + +public: + + enum RefIDs + { + kRefBasic, + kRefAnim, + }; + enum BlockIDs + { + kBlkBasic, + kBlkAnim, + }; + + plBumpMtl(BOOL loading); + void DeleteThis() { delete this; } + + //From Animatable + Class_ID ClassID() { return BUMP_MTL_CLASS_ID; } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + void GetClassName(TSTR& s) { s = GetString(IDS_BUMP_MTL); } + + ParamDlg *CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + void Update(TimeValue t, Interval& valid); + Interval Validity(TimeValue t); + + void NotifyChanged(); + + BOOL SupportsMultiMapsInViewport() { return FALSE; } + void SetupGfxMultiMaps(TimeValue t, Material *mtl, MtlMakerCallback &cb); + + // Shade and displacement calculation + static void GetInterpVtxValue(int channel, ShadeContext &sc, Point3 &interpVal); + void Shade(ShadeContext& sc); + void ShadeWithBackground(ShadeContext &sc, Color background, bool useVtxAlpha = true); + float EvalDisplacement(ShadeContext& sc); + Interval DisplacementValidity(TimeValue t); + + virtual RefTargetHandle GetReference( int i ); + virtual void SetReference( int i, RefTargetHandle rtarg ); + + // SubTexmap access methods + int NumSubTexmaps(); + Texmap* GetSubTexmap(int i); + void SetSubTexmap(int i, Texmap *m); + TSTR GetSubTexmapSlotName(int i); + TSTR GetSubTexmapTVName(int i); + + BOOL SetDlgThing(ParamDlg* dlg); + + RefTargetHandle Clone( RemapDir &remap ); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + int NumParamBlocks(); + IParamBlock2* GetParamBlock(int i); + IParamBlock2* GetParamBlockByID(BlockID id); + + +// void SetParamDlg(ParamDlg *dlg); + +// void SetNumSubTexmaps(int num); + + // From MtlBase and Mtl + void SetAmbient(Color c, TimeValue t); + void SetDiffuse(Color c, TimeValue t); + void SetSpecular(Color c, TimeValue t); + void SetShininess(float v, TimeValue t); + Color GetAmbient(int mtlNum=0, BOOL backFace=FALSE); + Color GetDiffuse(int mtlNum=0, BOOL backFace=FALSE); + Color GetSpecular(int mtlNum=0, BOOL backFace=FALSE); + float GetXParency(int mtlNum=0, BOOL backFace=FALSE); + float GetShininess(int mtlNum=0, BOOL backFace=FALSE); + float GetShinStr(int mtlNum=0, BOOL backFace=FALSE); + float WireSize(int mtlNum=0, BOOL backFace=FALSE); + + ULONG Requirements( int subMtlNum ); + + virtual bool HasAlpha(); + // Massive list of inherited accessor functions for ParamBlock data + + // Advanced Block + virtual int GetBasicWire(); + virtual int GetMeshOutlines(); + virtual int GetTwoSided(); + virtual int GetSoftShadow(); + virtual int GetNoProj(); + virtual int GetVertexShade(); + virtual int GetNoShade(); + virtual int GetNoFog(); + virtual int GetWhite(); + virtual int GetZOnly(); + virtual int GetZClear(); + virtual int GetZNoRead(); + virtual int GetZNoWrite(); + virtual int GetZInc(); + virtual int GetAlphaTestHigh(); + + // Animation block + virtual char * GetAnimName(); + virtual int GetAutoStart(); + virtual int GetLoop(); + virtual char * GetAnimLoopName(); + + // Basic block + virtual int GetColorLock(); + virtual Color GetAmbColor(); + virtual Color GetColor(); + virtual int GetOpacity(); + virtual int GetEmissive(); + virtual int GetUseSpec(); + virtual int GetShine(); + virtual Color GetSpecularColor(); + virtual Control *GetPreshadeColorController(); + virtual Control *GetAmbColorController(); + virtual Control *GetOpacityController(); + virtual Control *GetSpecularColorController(); + virtual int GetDiffuseColorLock(); + virtual Color GetRuntimeColor(); + virtual Control *GetRuntimeColorController(); + + // Layer block + virtual Texmap *GetBaseLayer(); + virtual int GetTopLayerOn(); + virtual Texmap *GetTopLayer(); + virtual int GetLayerBlend(); + virtual int GetOutputAlpha(); + virtual int GetOutputBlend(); +}; + +#endif //PL_BUMPMTL_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlAnimPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlAnimPB.h new file mode 100644 index 00000000..e1f6e8a7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlAnimPB.h @@ -0,0 +1,37 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_BUMPMTLANIMPB_H +#define PL_BUMPMTLANIMPB_H + +enum +{ + kBumpAnimName, + kBumpAnimAutoStart, + kBumpAnimLoop, + kBumpAnimLoopName, +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlAnimPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlAnimPBDec.h new file mode 100644 index 00000000..613557b8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlAnimPBDec.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plBumpMtl.h" +#include "plPassBaseParamIDs.h" +#include "resource.h" +#include "iparamm2.h" + +#include "plPassAnimDlgProc.h" +#include "plAnimStealthNode.h" + +using namespace plPassBaseParamIDs; + +static ParamBlockDesc2 gBumpAnimPB +( + plBumpMtl::kBlkAnim, _T("anim"), IDS_PASS_ANIM, GetBumpMtlDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI + P_CALLSETS_ON_LOAD, plBumpMtl::kRefAnim, + + // UI + IDD_PASS_ANIM, IDS_PASS_ANIM, 0, 0, &plPassAnimDlgProc::Get(), + +#ifdef MCN_UPGRADE_OLD_ANIM_BLOCKS + // THE FOLLOWING ARE ALL OLD PARAMETERS AND SHOULD NO LONGER BE USED. The only reason + // they're here is so we can convert old paramBlocks into the new plAnimStealthNode format + kPBAnimName, _T("animName"), TYPE_STRING, 0, 0, + end, + + kPBAnimAutoStart, _T("autoStart"), TYPE_BOOL, 0, 0, + end, + + kPBAnimLoop, _T("loop"), TYPE_BOOL, 0, 0, + end, + kPBAnimLoopName, _T("loopName"), TYPE_STRING, 0, 0, + end, + + // Anim Ease + kPBAnimEaseInType, _T("easeInType"), TYPE_INT, 0, 0, + end, + kPBAnimEaseInLength, _T("easeInLength"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseInMin, _T("easeInMin"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseInMax, _T("easeInMax"), TYPE_FLOAT, 0, 0, + end, + + kPBAnimEaseOutType, _T("easeOutType"), TYPE_INT, 0, 0, + end, + kPBAnimEaseOutLength, _T("easeOutLength"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseOutMin, _T("easeOutMin"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseOutMax, _T("easeOutMax"), TYPE_FLOAT, 0, 0, + end, + +#endif // MCN_UPGRADE_OLD_ANIM_BLOCKS + + kPBAnimUseGlobal, _T("UseGlobal"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_MTL_USE_GLOBAL, + end, + kPBAnimGlobalName, _T("GlobalName"), TYPE_STRING, 0, 0, + p_default, _T(""), + end, + + kPBAnimStealthNodes, _T( "testing" ), TYPE_REFTARG_TAB, 0, 0, 0, + p_accessor, &plStealthNodeAccessor::GetInstance(), + end, + + end +); +ParamBlockDesc2 *GetBumpAnimPB() { return &gBumpAnimPB; } diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlBasicPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlBasicPB.h new file mode 100644 index 00000000..8dbf460b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlBasicPB.h @@ -0,0 +1,38 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_BUMPMTLBASICPB_H +#define PL_BUMPMTLBASICPB_H + +// Param ID's +enum +{ + kBumpBasSpecular, + kBumpBasRunColor, + kBumpBasLayer + +}; + +#endif //PL_BUMPMTLBASICPB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlBasicPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlBasicPBDec.h new file mode 100644 index 00000000..b6f0f757 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plBumpMtlBasicPBDec.h @@ -0,0 +1,92 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plBumpMtl.h" +#include "plBumpMtlBasicPB.h" +#include "resource.h" +#include "iparamm2.h" + +class BumpBasicDlgProc; +extern BumpBasicDlgProc gBumpBasicDlgProc; + +static ParamBlockDesc2 gBumpBasicPB +( + plBumpMtl::kBlkBasic, _T("basic"), IDS_PASS_BASIC, GetBumpMtlDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI, plBumpMtl::kRefBasic, + + // UI + IDD_BUMP_BASIC, IDS_PASS_BASIC, 0, 0, &gBumpBasicDlgProc, + + kBumpBasLayer, _T("bumpLayer"), TYPE_TEXMAP, 0, IDS_BASIC_AMB, + p_ui, TYPE_TEXMAPBUTTON, IDC_LAYER1, + p_subtexno, 0, + end, + + kBumpBasRunColor, _T("runtimeColor"), TYPE_RGBA, P_ANIMATABLE, IDS_BASIC_RUNCOLOR, +// p_ui, TYPE_COLORSWATCH, IDC_LAYER_RUNCOLOR, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_INTEN_EDIT, IDC_INTEN_SPIN, + IDC_DUMMY_EDIT1, IDC_DUMMY_SPIN1, IDC_DUMMY_EDIT2, IDC_DUMMY_SPIN2, + SPIN_AUTOSCALE, + p_default, Color(1,0,0), + end, + + // Specularity + kBumpBasSpecular, _T("useSpec"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_SPECULAR, + end, + + end +); +ParamBlockDesc2 *GetBumpBasicPB() { return &gBumpBasicPB; } + +class BumpBasicDlgProc : public ParamMap2UserDlgProc +{ +#if 1 +protected: + +public: + BumpBasicDlgProc() {} + ~BumpBasicDlgProc() { } +#endif + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IParamBlock2 *pb = map->GetParamBlock(); + + switch (msg) + { + case WM_INITDIALOG: + { + } + return TRUE; + } + return FALSE; + } + void DeleteThis() {} +}; +static BumpBasicDlgProc gBumpBasicDlgProc; + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtl.cpp new file mode 100644 index 00000000..abaa4080 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtl.cpp @@ -0,0 +1,699 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsUtils.h" +#include "plClothingMtl.h" +#include "resource.h" +#include "Shaders.h" + +#include "iparamm2.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "plBMSampler.h" +#include "stdmat.h" +#include "Layers/plLayerTex.h" +#include "Layers/plLayerTexBitmapPB.h" +#include "../../Plasma/PubUtilLib/plAvatar/plClothingLayout.h" + +extern HINSTANCE hInstance; + +class plClothingMtlClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return TRACKED_NEW plClothingMtl(loading); } + const TCHAR* ClassName() { return GetString(IDS_CLOTHING_MTL); } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + Class_ID ClassID() { return CLOTHING_MTL_CLASS_ID; } + const TCHAR* Category() { return NULL; } + const TCHAR* InternalName() { return _T("ClothingMaterial"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plClothingMtlClassDesc plClothingMtlDesc; +ClassDesc2* GetClothingMtlDesc() { return &plClothingMtlDesc; } + +// For initializing paramblock descriptor +ParamBlockDesc2 *GetClothingPB(); +#include "plClothingMtlPBDec.h" + +const UINT32 plClothingMtl::ButtonConstants[] = +{ + IDC_CLOTHING_TEXTURE1, + IDC_CLOTHING_TEXTURE2, + IDC_CLOTHING_TEXTURE3, + IDC_CLOTHING_TEXTURE4 +}; + +const UINT32 plClothingMtl::TextConstants[] = +{ + IDC_CLOTHING_TILE1_NAME, + IDC_CLOTHING_TILE1_SIZE, + IDC_CLOTHING_TILE2_NAME, + IDC_CLOTHING_TILE2_SIZE, + IDC_CLOTHING_TILE3_NAME, + IDC_CLOTHING_TILE3_SIZE, + IDC_CLOTHING_TILE4_NAME, + IDC_CLOTHING_TILE4_SIZE +}; + +const char *plClothingMtl::LayerStrings[] = +{ + "Base", + "Skin", + "Skin Blend (1)", + "Skin Blend (2)", + "Skin Blend (3)", + "Skin Blend (4)", + "Skin Blend (5)", + "Skin Blend (6)", + "Tint 1", + "Tint 2" +}; + +const UInt8 plClothingMtl::LayerToPBIdx[] = +{ + kTexmapBase, + kTexmapSkin, + kTexmapSkinBlend1, + kTexmapSkinBlend2, + kTexmapSkinBlend3, + kTexmapSkinBlend4, + kTexmapSkinBlend5, + kTexmapSkinBlend6, + kTexmap, + kTexmap2 +}; + +plClothingMtl::plClothingMtl(BOOL loading) : fBasicPB(NULL) +{ + plClothingMtlDesc.MakeAutoParamBlocks(this); + + Reset(); + int i; + for (i = 0; i < plClothingMtl::kMaxTiles; i++) + { + plLayerTex *tex = TRACKED_NEW plLayerTex; + fBasicPB->SetValue(ParamID(kTexmap), 0, tex, i); + } + fBasicPB->SetValue(ParamID(kThumbnail), 0, TRACKED_NEW plLayerTex); +} + +void plClothingMtl::Reset() +{ + fIValid.SetEmpty(); +} + +ParamDlg* plClothingMtl::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + fIMtlParams = imp; + IAutoMParamDlg* masterDlg = plClothingMtlDesc.CreateParamDlgs(hwMtlEdit, imp, this); + + return (ParamDlg*)masterDlg; +} + +BOOL plClothingMtl::SetDlgThing(ParamDlg* dlg) +{ + return FALSE; +} + +Interval plClothingMtl::Validity(TimeValue t) +{ + // mf horse - Hacking in something like real validity checking + // to get material animations working. No warranty, this is just + // better than nothing. + Interval v = FOREVER; + fBasicPB->GetValidity(t, v); + + return v; +} + +/*===========================================================================*\ + | Subanim & References support +\*===========================================================================*/ + +/* +int plClothingMtl::NumSubs() +{ + return 2; +} + +TSTR plClothingMtl::SubAnimName(int i) +{ + switch (i) + { + case 0: return fBasicPB->GetLocalName(); + case 1: return "Texmap"; + } + + return ""; +} + +Animatable* plClothingMtl::SubAnim(int i) +{ + switch (i) + { + case 0: return fBasicPB; + case 1: return fBasicPB->GetTexmap(kTexmap); + } + + return NULL; +} +*/ + +int plClothingMtl::NumRefs() +{ + return 1; +} + +RefTargetHandle plClothingMtl::GetReference(int i) +{ + switch (i) + { + case kRefBasic: return fBasicPB; + } + + return NULL; +} + +void plClothingMtl::SetReference(int i, RefTargetHandle rtarg) +{ + if (i == kRefBasic) + fBasicPB = (IParamBlock2 *)rtarg; +} + +int plClothingMtl::NumParamBlocks() +{ + return 1; +} + +IParamBlock2* plClothingMtl::GetParamBlock(int i) +{ + return (IParamBlock2*)GetReference(i); +} + +IParamBlock2* plClothingMtl::GetParamBlockByID(BlockID id) +{ + if (fBasicPB->ID() == id) + return fBasicPB; + + return NULL; +} + +RefResult plClothingMtl::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) +{ + switch (message) + { + case REFMSG_CHANGE: + fIValid.SetEmpty(); + + // see if this message came from a changing parameter in the pblock, + // if so, limit rollout update to the changing item + if (hTarget == fBasicPB) + { + IParamBlock2 *pb = (IParamBlock2*)hTarget; + + ParamID changingParam = pb->LastNotifyParamID(); + pb->GetDesc()->InvalidateUI(changingParam); + + // And let the SceneWatcher know that the material on some of it's + // referenced objects changed. + NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_MAT); + } + break; + } + + return REF_SUCCEED; +} + +//////////////////////////////////////////////////////////////////////////////// +// Subtexmap access + +int plClothingMtl::NumSubTexmaps() +{ + return plClothingElement::kLayerMax * kMaxTiles + 1; // add one for the thumbnail +} + +Texmap* plClothingMtl::GetSubTexmap(int i) +{ + if (i >= 0 && i < plClothingElement::kLayerMax * kMaxTiles) + return fBasicPB->GetTexmap(ParamID(LayerToPBIdx[i / kMaxTiles]), 0, i % kMaxTiles); + if (i == plClothingElement::kLayerMax * kMaxTiles) + return fBasicPB->GetTexmap(ParamID(kThumbnail)); + + return NULL; +} + +void plClothingMtl::SetSubTexmap(int i, Texmap *m) +{ + if (i >= 0 && i < plClothingElement::kLayerMax * kMaxTiles) + fBasicPB->SetValue(ParamID(LayerToPBIdx[i / kMaxTiles]), 0, m, i % kMaxTiles); + if (i == plClothingElement::kLayerMax * kMaxTiles) + fBasicPB->SetValue(kThumbnail, 0, m); +} + +TSTR plClothingMtl::GetSubTexmapSlotName(int i) +{ + if (i >= 0 && i < plClothingElement::kLayerMax * kMaxTiles) + return "Texmap"; + if (i == plClothingElement::kLayerMax * kMaxTiles) + return "Thumbnail"; + + return ""; +} + +TSTR plClothingMtl::GetSubTexmapTVName(int i) +{ + return GetSubTexmapSlotName(i); +} + +DllExport Texmap *plClothingMtl::GetTexmap(int index, int layer) +{ + return fBasicPB->GetTexmap(ParamID(LayerToPBIdx[layer]), 0, index); +} + +/*===========================================================================*\ + | Standard IO +\*===========================================================================*/ + +#define MTL_HDR_CHUNK 0x4000 + +IOResult plClothingMtl::Save(ISave *isave) +{ + IOResult res; + isave->BeginChunk(MTL_HDR_CHUNK); + res = MtlBase::Save(isave); + if (res!=IO_OK) return res; + isave->EndChunk(); + + return IO_OK; +} + +IOResult plClothingMtl::Load(ILoad *iload) +{ + IOResult res; + int id; + while (IO_OK==(res=iload->OpenChunk())) + { + switch(id = iload->CurChunkID()) + { + case MTL_HDR_CHUNK: + res = MtlBase::Load(iload); + break; + } + iload->CloseChunk(); + if (res!=IO_OK) + return res; + } + + return IO_OK; +} + +/*===========================================================================*\ + | Updating and cloning +\*===========================================================================*/ + +RefTargetHandle plClothingMtl::Clone(RemapDir &remap) +{ + plClothingMtl *mnew = TRACKED_NEW plClothingMtl(FALSE); + *((MtlBase*)mnew) = *((MtlBase*)this); + mnew->ReplaceReference(kRefBasic, remap.CloneRef(fBasicPB)); + + BaseClone(this, mnew, remap); + mnew->fIValid.SetEmpty(); + + return (RefTargetHandle)mnew; +} + +void plClothingMtl::NotifyChanged() +{ + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +} + +void plClothingMtl::Update(TimeValue t, Interval& valid) +{ + //StdUVGen *gen = (StdUVGen *)fUVGen; + //gen->SetUScl(1.0f, t); + //gen->SetVScl(1.0f, t); + //gen->Update(t, fIValid); + valid &= fIValid; +} + +/*===========================================================================*\ + | Determine the characteristics of the material +\*===========================================================================*/ + +void plClothingMtl::SetAmbient(Color c, TimeValue t) {} +void plClothingMtl::SetDiffuse(Color c, TimeValue t) {} +void plClothingMtl::SetSpecular(Color c, TimeValue t) {} +void plClothingMtl::SetShininess(float v, TimeValue t) {} + +Color plClothingMtl::GetAmbient(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plClothingMtl::GetDiffuse(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plClothingMtl::GetSpecular(int mtlNum, BOOL backFace) { return Color(0,0,0); } + +float plClothingMtl::GetXParency(int mtlNum, BOOL backFace) +{ + /* + int opacity = fBasicPB->GetInt( kOpacity, 0 ); + float alpha = 1.0f - ( (float)opacity / 100.0f ); + + return alpha; + */ + return 0; +} + +float plClothingMtl::GetShininess(int mtlNum, BOOL backFace) { return 0.0f; } +float plClothingMtl::GetShinStr(int mtlNum, BOOL backFace) { return 0.0f; } +float plClothingMtl::WireSize(int mtlNum, BOOL backFace) { return 0.0f; } + +///////////////////////////////////////////////////////////////// + +void plClothingMtl::SetupGfxMultiMaps(TimeValue t, Material *mtl, MtlMakerCallback &cb) +{ +} + +/*===========================================================================*\ + | Actual shading takes place +\*===========================================================================*/ + +void plClothingMtl::Shade(ShadeContext& sc) +{ + // Get the background color + Color backColor, backTrans; + sc.GetBGColor(backColor, backTrans); + + ShadeWithBackground(sc, backColor); +} + +//// Requirements //////////////////////////////////////////////////////////// +// Tells MAX what we need to render ourselves properly, such as translucency, +// two-sidedness, etc. Flags are in imtl.h in the MAX SDK. + +ULONG plClothingMtl::Requirements( int subMtlNum ) +{ + ULONG req = 0; + + + req = Mtl::Requirements( subMtlNum ); + + // Uncomment this to get the background color fed to our ShadeWithBackground() + // (slower processing tho) +// req |= MTLREQ_BGCOL; + + //int blendType = fBasicPB->GetInt( kBlend ); + //if( blendType == kBlendAdd ) + // req |= MTLREQ_ADDITIVE_TRANSP | MTLREQ_TRANSP; + //else if( blendType == kBlendAlpha ) + // req |= MTLREQ_TRANSP; + //else if( fBasicPB->GetInt( kOpacity, 0 ) != 100 ) + // req |= MTLREQ_TRANSP; + + return req; +} + +void plClothingMtl::ShadeWithBackground(ShadeContext &sc, Color background) +{ +#if 1 + TimeValue t = sc.CurTime(); + Color color(0, 0, 0); + float alpha = 0.0; + + // Evaluate Base layer + Texmap *map = fBasicPB->GetTexmap(kTexmap); + if (map && map->ClassID() == LAYER_TEX_CLASS_ID) + { + plLayerTex *layer = (plLayerTex*)map; + AColor evalColor = layer->EvalColor(sc); + + color = evalColor; + alpha = evalColor.a; + } + +#if 1 + AColor black; + black.Black(); + AColor white; + white.White(); + + + SIllumParams ip; + //if( fBasicPB->GetInt( kNormal ) == kEmissive ) + //{ + // Emissive objects don't get shaded + // ip.diffIllum = fBasicPB->GetColor(kColorAmb, t) * color; + // ip.diffIllum.ClampMinMax(); + // ip.specIllum = black; + //} + //else + { + // + // Shading setup + // + + // Setup the parameters for the shader + ip.amb = black; + ip.diff = white; //fBasicPB->GetColor(kColor, t) * color; + ip.spec = white; + ip.diffIllum = black; + ip.specIllum = black; + ip.N = sc.Normal(); + ip.V = sc.V(); + + + // + // Specularity + // + ip.sh_str = 0; + ip.ph_exp = 0; + ip.shine = 0; + + ip.softThresh = 0; + + + + // Do the shading + Shader *myShader = GetShader(SHADER_BLINN); + myShader->Illum(sc, ip); + + ip.diffIllum.ClampMinMax(); + ip.specIllum.ClampMinMax(); + ip.diffIllum = ip.amb * sc.ambientLight + ip.diff * ip.diffIllum; + } + +// AColor returnColor = AColor(opac * ip.diffIllum + ip.specIllum, opac) +#endif + + // Get opacity and combine with alpha + float opac = 1.0f; //float(fBasicPB->GetInt(kOpacity, t)) / 100.0f; + //float opac = 1.0f; + alpha *= opac; + + // MAX will do the additive/alpha/no blending for us based on what Requirements() + // we tell it. However, since MAX's formula is bgnd*sc.out.t + sc.out.c, + // we have to multiply our output color by the alpha. + // If we ever need a more complicated blending function, you can request the + // background color via Requirements() (otherwise it's just black) and then do + // the blending yourself; however, if the transparency isn't set, the shadows + // will be opaque, so be careful. + Color outC = ip.diffIllum + ip.specIllum; + + sc.out.c = ( outC * alpha ); + sc.out.t = Color( 1.f - alpha, 1.f - alpha, 1.f - alpha ); + +#endif +} + +float plClothingMtl::EvalDisplacement(ShadeContext& sc) +{ + return 0.0f; +} + +Interval plClothingMtl::DisplacementValidity(TimeValue t) +{ + Interval iv; + iv.SetInfinite(); + + return iv; +} + +plClothingElement *plClothingMtl::FindElementByName(char *name) +{ + int i; + for (i = 0; i < fElements.GetCount(); i++) + { + if (!strcmp(fElements.Get(i)->fName, name)) + return fElements.Get(i); + } + return nil; +} + +void plClothingMtl::InitTilesets() +{ + hsAssert(fElements.GetCount() == 0, "Tilesets already initialized"); + fElements.Reset(); + fTilesets.SetCountAndZero(plClothingLayout::kMaxTileset); + + plClothingElement::GetElements(fElements); +/* + plClothingTileset *tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("shirt"); + tileset->AddElement(FindElementByName("shirt-chest")); + tileset->AddElement(FindElementByName("shirt-sleeve")); + fTilesets[plClothingLayout::kSetShirt] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("face"); + tileset->AddElement(FindElementByName("face")); + fTilesets[plClothingLayout::kSetFace] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("eye"); + tileset->AddElement(FindElementByName("eyeball")); + fTilesets[plClothingLayout::kSetEye] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("shoe"); + tileset->AddElement(FindElementByName("shoe-top")); + tileset->AddElement(FindElementByName("shoe-bottom")); + fTilesets[plClothingLayout::kSetShoe] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("pants"); + tileset->AddElement(FindElementByName("pants")); + fTilesets[plClothingLayout::kSetPants] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("hand"); + tileset->AddElement(FindElementByName("hand-LOD")); + tileset->AddElement(FindElementByName("hand-square")); + tileset->AddElement(FindElementByName("hand-wide")); + fTilesets[plClothingLayout::kSetHand] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("playerbook"); + tileset->AddElement(FindElementByName("playerbook")); + fTilesets[plClothingLayout::kSetPlayerBook] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("backpack"); + tileset->AddElement(FindElementByName("backpack")); + fTilesets[plClothingLayout::kSetBackpack] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("glasses"); + tileset->AddElement(FindElementByName("glasses-front")); + tileset->AddElement(FindElementByName("glasses-side")); + fTilesets[plClothingLayout::kSetGlasses] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("KI"); + tileset->AddElement(FindElementByName("KI")); + fTilesets[plClothingLayout::kSetKI] = tileset; + */ + plClothingTileset *tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("Torso"); + tileset->AddElement(FindElementByName("Chest")); + tileset->AddElement(FindElementByName("Arm")); + fTilesets[plClothingLayout::kSetShirt] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("Head"); + tileset->AddElement(FindElementByName("Eye")); + tileset->AddElement(FindElementByName("Extra Hair")); + tileset->AddElement(FindElementByName("Face")); + tileset->AddElement(FindElementByName("Hat")); + fTilesets[plClothingLayout::kSetFace] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("Foot"); + tileset->AddElement(FindElementByName("Foot")); + fTilesets[plClothingLayout::kSetShoe] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("Legs"); + tileset->AddElement(FindElementByName("Legs")); + fTilesets[plClothingLayout::kSetPants] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("Hand"); + tileset->AddElement(FindElementByName("Finger")); + tileset->AddElement(FindElementByName("LOD")); + tileset->AddElement(FindElementByName("Palm")); + fTilesets[plClothingLayout::kSetHand] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("Player Book"); + tileset->AddElement(FindElementByName("Player Book")); + fTilesets[plClothingLayout::kSetPlayerBook] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("Glasses"); + tileset->AddElement(FindElementByName("Glasses")); + fTilesets[plClothingLayout::kSetGlasses] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("KI"); + tileset->AddElement(FindElementByName("KI")); + fTilesets[plClothingLayout::kSetKI] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("(unused)"); + fTilesets[plClothingLayout::kSetEye] = tileset; + + tileset = TRACKED_NEW plClothingTileset(); + tileset->SetName("(unused)"); + fTilesets[plClothingLayout::kSetBackpack] = tileset; +} + +void plClothingMtl::ReleaseTilesets() +{ + while (fElements.GetCount() > 0) + delete fElements.Pop(); + while (fTilesets.GetCount() > 0) + delete fTilesets.Pop(); +} + +///////////////////////////////////////////////////////////////////////////////// + +plClothingTileset::plClothingTileset() : fName(nil) +{ + fElements.Reset(); +} + +plClothingTileset::~plClothingTileset() +{ + delete [] fName; +} + +void plClothingTileset::AddElement(plClothingElement *element) +{ + fElements.Append(element); +} + +void plClothingTileset::SetName(char *name) +{ + delete fName; fName = hsStrcpy(name); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtl.h new file mode 100644 index 00000000..ac5d311a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtl.h @@ -0,0 +1,201 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_CLOTHINGMTL_H +#define PL_CLOTHINGMTL_H + +#include "Max.h" +#include "iparamb2.h" +#include "../resource.h" +#include "hsTemplates.h" + +class Bitmap; +class plClothingItem; +class plMaxNode; +class plClothingElement; + +#define CLOTHING_MTL_CLASS_ID Class_ID(0x792c6de4, 0x1f952b65) + +extern TCHAR *GetString(int id); + +class plClothingTileset +{ +public: + char *fName; + hsTArray fElements; + + plClothingTileset(); + ~plClothingTileset(); + + void SetName(char *name); + void AddElement(plClothingElement *element); +}; + +class plClothingMtl : public Mtl +{ +protected: + IParamBlock2 *fBasicPB; + Interval fIValid; + +public: + IMtlParams *fIMtlParams; + + static const char *LayerStrings[]; + static const UInt8 LayerToPBIdx[]; + + enum + { + kRefBasic, + }; + enum + { + kBlkBasic, + }; + + enum // Param block indicies + { + kTileset, + kTexmap, + kDescription, + kThumbnail, + kLayer, + kTexmapSkin, + kTexmap2, + kDefault, + kCustomTextSpecs, + kTexmapBase, + kTexmapSkinBlend1, + kTexmapSkinBlend2, + kTexmapSkinBlend3, + kTexmapSkinBlend4, + kTexmapSkinBlend5, + kTexmapSkinBlend6, + kDefaultTint1, + kDefaultTint2, + kForcedAcc, + }; + + enum + { + kMaxTiles = 4, + }; + + enum + { + kBlendNone, + kBlendAlpha, + //kBlendAdd, + }; + + static const UINT32 ButtonConstants[kMaxTiles]; + static const UINT32 TextConstants[kMaxTiles * 2]; + + hsTArray fTilesets; + hsTArray fElements; + virtual void InitTilesets(); + virtual void ReleaseTilesets(); + plClothingElement *FindElementByName(char *name); + + int GetTilesetIndex() { return fBasicPB->GetInt(ParamID(kTileset)); } + DllExport Texmap *GetTexmap(int index, int layer); + Texmap *GetThumbnail() { return fBasicPB->GetTexmap(ParamID(kThumbnail)); } + char *GetDescription() { return fBasicPB->GetStr(ParamID(kDescription)); } + char *GetCustomText() { return fBasicPB->GetStr(ParamID(kCustomTextSpecs)); } + hsBool GetDefault() { return fBasicPB->GetInt(ParamID(kDefault)) != 0; } + Color GetDefaultTint1() { return fBasicPB->GetColor(plClothingMtl::kDefaultTint1); } + Color GetDefaultTint2() { return fBasicPB->GetColor(plClothingMtl::kDefaultTint2); } + char *GetForcedAccessoryName() { return fBasicPB->GetStr(ParamID(kForcedAcc)); } + + plClothingMtl(BOOL loading); + void DeleteThis() { delete this; } + + //From Animatable + Class_ID ClassID() { return CLOTHING_MTL_CLASS_ID; } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + void GetClassName(TSTR& s) { s = GetString(IDS_CLOTHING_MTL); } + + ParamDlg *CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + void Update(TimeValue t, Interval& valid); + Interval Validity(TimeValue t); + void Reset(); + + void NotifyChanged(); + + BOOL SupportsMultiMapsInViewport() { return FALSE; } + void SetupGfxMultiMaps(TimeValue t, Material *mtl, MtlMakerCallback &cb); + + // Shade and displacement calculation + void Shade(ShadeContext& sc); + void ShadeWithBackground(ShadeContext &sc, Color background); + float EvalDisplacement(ShadeContext& sc); + Interval DisplacementValidity(TimeValue t); + + // SubTexmap access methods + int NumSubTexmaps(); + Texmap* GetSubTexmap(int i); + void SetSubTexmap(int i, Texmap *m); + TSTR GetSubTexmapSlotName(int i); + TSTR GetSubTexmapTVName(int i); + + BOOL SetDlgThing(ParamDlg* dlg); + + // Loading/Saving + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + RefTargetHandle Clone( RemapDir &remap ); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs() { return 0; } + Animatable* SubAnim(int i) { return nil; } + TSTR SubAnimName(int i) { return ""; } + + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + + int NumParamBlocks(); + IParamBlock2* GetParamBlock(int i); + IParamBlock2* GetParamBlockByID(BlockID id); + + + // From MtlBase and Mtl + void SetAmbient(Color c, TimeValue t); + void SetDiffuse(Color c, TimeValue t); + void SetSpecular(Color c, TimeValue t); + void SetShininess(float v, TimeValue t); + Color GetAmbient(int mtlNum=0, BOOL backFace=FALSE); + Color GetDiffuse(int mtlNum=0, BOOL backFace=FALSE); + Color GetSpecular(int mtlNum=0, BOOL backFace=FALSE); + float GetXParency(int mtlNum=0, BOOL backFace=FALSE); + float GetShininess(int mtlNum=0, BOOL backFace=FALSE); + float GetShinStr(int mtlNum=0, BOOL backFace=FALSE); + float WireSize(int mtlNum=0, BOOL backFace=FALSE); + + ULONG Requirements( int subMtlNum ); +}; + +#endif //PL_ClothingMTL_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtlPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtlPBDec.h new file mode 100644 index 00000000..410ff670 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plClothingMtlPBDec.h @@ -0,0 +1,426 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +//#include "plClothingMtl.h" +#include "../../AssetMan/PublicInterface/AssManBaseTypes.h" +#include "max.h" + +class plClothingEditBox +{ +protected: + int fCtrlID; + int fPBEditID; + + plClothingEditBox() {}; + + void IGetText(IParamBlock2 *pb, HWND hCtrl) + { + // Get the previous value + const char *oldVal = pb->GetStr(fPBEditID); + if (!oldVal) + oldVal = ""; + + // Get the text from the edit and store it in the paramblock + int len = GetWindowTextLength(hCtrl)+1; + if (len > 1) + { + char *buf = TRACKED_NEW char[len]; + GetWindowText(hCtrl, buf, len); + + // If the old value is different from the current one, update + if (strcmp(oldVal, buf)) + pb->SetValue(fPBEditID, 0, buf); + + delete [] buf; + } + else + { + // If the old value wasn't empty, update + if (*oldVal != '\0') + pb->SetValue(fPBEditID, 0, ""); + } + } + +public: + plClothingEditBox(int ctrlID, int pbEditID) : fCtrlID(ctrlID), fPBEditID(pbEditID) {} + + void UpdateText(IParamBlock2 *pb, HWND hWnd) + { + const char *str = pb->GetStr(fPBEditID); + SetDlgItemText(hWnd, fCtrlID, (str != nil ? str : "")); + } + + BOOL ProcessMsg(IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + // If this isn't a message we process, return + if (!(msg == WM_INITDIALOG || msg == WM_DESTROY || + (msg == WM_COMMAND && LOWORD(wParam) == fCtrlID))) + return FALSE; + + IParamBlock2 *pb = map->GetParamBlock(); + + // Initializing the dialog, set the text box + if (msg == WM_INITDIALOG) + { + UpdateText(pb, hWnd); + return FALSE; + } + + // Being destroyed, but we may not have gotten the text from the edit box + // (EN_KILLFOCUS seems to be sent after the window isn't recieving any more messages) + if (msg == WM_DESTROY) + { + IGetText(pb, GetDlgItem(hWnd, fCtrlID)); + return FALSE; + } + + int notification = HIWORD(wParam); + HWND hCtrl = (HWND)lParam; + + // Disable Max accelerators when the edit box gets focus + if (notification == EN_SETFOCUS) + { + //plMaxAccelerators::Disable(); + DisableAccelerators(); + return TRUE; + } + // The edit control is losing focus, get it's contents + else if (notification == EN_KILLFOCUS) + { + IGetText(pb, hCtrl); + //plMaxAccelerators::Enable(); + EnableAccelerators(); + return TRUE; + } + + return FALSE; + } +}; + +class ClothingBasicDlgProc; +extern ClothingBasicDlgProc gClothingBasicDlgProc; + +static ParamBlockDesc2 gClothingMtlPB +( + plClothingMtl::kBlkBasic, _T("Clothing"), IDS_PASS_BASIC, GetClothingMtlDesc(), + P_AUTO_CONSTRUCT + P_AUTO_UI + P_CALLSETS_ON_LOAD, plClothingMtl::kRefBasic, + + // UI + IDD_CLOTHING, IDS_PASS_BASIC, 0, 0, &gClothingBasicDlgProc, + + plClothingMtl::kTileset, _T("tileset"), TYPE_INT, 0, 0, + p_default, 0, + end, + + plClothingMtl::kTexmap, _T("texmap"), TYPE_TEXMAP_TAB, plClothingMtl::kMaxTiles, 0, 0, + end, + + plClothingMtl::kDescription, _T("ItemDescription"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_CLOTHING_DESCRIPTION, + end, + + plClothingMtl::kThumbnail, _T("Thumbnail"), TYPE_TEXMAP, 0, 0, + end, + + plClothingMtl::kLayer, _T("Layer"), TYPE_INT, 0, 0, + p_default, plClothingElement::kLayerTint1, + end, + + plClothingMtl::kTexmapSkin, _T("SkinLayer"), TYPE_TEXMAP_TAB, plClothingMtl::kMaxTiles, 0, 0, + end, + + plClothingMtl::kTexmap2, _T("TintLayer2"), TYPE_TEXMAP_TAB, plClothingMtl::kMaxTiles, 0, 0, + end, + + plClothingMtl::kDefault, _T("Default"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_CLOTHING_DEFAULT, + p_default, 0, + end, + + plClothingMtl::kCustomTextSpecs, _T("TextSpecs"), TYPE_STRING, 0, 0, + end, + + plClothingMtl::kTexmapBase, _T("BaseLayer"), TYPE_TEXMAP_TAB, plClothingMtl::kMaxTiles, 0, 0, + end, + + plClothingMtl::kTexmapSkinBlend1, _T("SkinBlend(1)"), TYPE_TEXMAP_TAB, plClothingMtl::kMaxTiles, 0, 0, + end, + + plClothingMtl::kTexmapSkinBlend2, _T("SkinBlend(2)"), TYPE_TEXMAP_TAB, plClothingMtl::kMaxTiles, 0, 0, + end, + + plClothingMtl::kTexmapSkinBlend3, _T("SkinBlend(3)"), TYPE_TEXMAP_TAB, plClothingMtl::kMaxTiles, 0, 0, + end, + + plClothingMtl::kTexmapSkinBlend4, _T("SkinBlend(4)"), TYPE_TEXMAP_TAB, plClothingMtl::kMaxTiles, 0, 0, + end, + + plClothingMtl::kTexmapSkinBlend5, _T("SkinBlend(5)"), TYPE_TEXMAP_TAB, plClothingMtl::kMaxTiles, 0, 0, + end, + + plClothingMtl::kTexmapSkinBlend6, _T("SkinBlend(6)"), TYPE_TEXMAP_TAB, plClothingMtl::kMaxTiles, 0, 0, + end, + + plClothingMtl::kDefaultTint1, _T("DefaultTint1"), TYPE_RGBA, 0, 0, + p_ui, TYPE_COLORSWATCH, IDC_CLOTHING_TINT1, + p_default, Color(1,1,1), + end, + + plClothingMtl::kDefaultTint2, _T("DefaultTint2"), TYPE_RGBA, 0, 0, + p_ui, TYPE_COLORSWATCH, IDC_CLOTHING_TINT2, + p_default, Color(1,1,1), + end, + + plClothingMtl::kForcedAcc, _T("ForcedAcc"), TYPE_STRING, 0, 0, + p_ui, TYPE_EDITBOX, IDC_CLOTHING_FORCED_ACC, + end, + + end +); + +class ClothingBasicDlgProc : public ParamMap2UserDlgProc +{ +protected: + plClothingEditBox fCustomText; + +public: + ClothingBasicDlgProc() : fCustomText(IDC_CLOTHING_CUSTOM_TEXT_SPECS, plClothingMtl::kCustomTextSpecs) {} + ~ClothingBasicDlgProc() {} + + void UpdateDisplay(IParamMap2 *pmap) + { + HWND hWnd = pmap->GetHWnd(); + IParamBlock2 *pb = pmap->GetParamBlock(); + + plClothingMtl *mtl = (plClothingMtl *)pb->GetOwner(); + HWND cbox = NULL; + plPlasmaMAXLayer *layer; + PBBitmap *pbbm; + ICustButton *bmSelectBtn; + char buff[256]; + + // Setup the tiles + int i, j; + int layerSet = pb->GetInt(ParamID(plClothingMtl::kLayer)); + int layerIdx = plClothingMtl::LayerToPBIdx[layerSet]; + for (j = 0; j < plClothingMtl::kMaxTiles; j++) + { + layer = (plPlasmaMAXLayer *)pb->GetTexmap(ParamID(layerIdx), 0, j); + pbbm = (layer == nil ? nil : layer->GetPBBitmap()); + + bmSelectBtn = GetICustButton(GetDlgItem(hWnd, plClothingMtl::ButtonConstants[j])); + bmSelectBtn->SetText(pbbm ? (TCHAR*)pbbm->bi.Filename() : "(none)"); + ReleaseICustButton(bmSelectBtn); + } + + // And the thumbnail... + layer = (plPlasmaMAXLayer *)pb->GetTexmap(ParamID(plClothingMtl::kThumbnail)); + if (layer == nil) + { + layer = TRACKED_NEW plLayerTex; + pb->SetValue(ParamID(plClothingMtl::kThumbnail), 0, layer); + } + pbbm = layer->GetPBBitmap(); + if (pbbm) + { + bmSelectBtn = GetICustButton(GetDlgItem(hWnd, IDC_CLOTHING_THUMBNAIL)); + bmSelectBtn->SetText((TCHAR*)pbbm->bi.Filename()); + ReleaseICustButton(bmSelectBtn); + } + + int setIdx = pb->GetInt(ParamID(plClothingMtl::kTileset)); + ComboBox_SetCurSel(GetDlgItem(hWnd, IDC_CLOTHING_TILESET), setIdx); + ComboBox_SetCurSel(GetDlgItem(hWnd, IDC_CLOTHING_LAYER), pb->GetInt(ParamID(plClothingMtl::kLayer))); + mtl->InitTilesets(); + plClothingTileset *tileset = mtl->fTilesets.Get(setIdx); + for (i = 0; i < tileset->fElements.GetCount(); i++) + { + plClothingElement *element = tileset->fElements.Get(i); + SendMessage(GetDlgItem(hWnd, plClothingMtl::TextConstants[2 * i]), + WM_SETTEXT, NULL, (LPARAM)element->fName); + sprintf(buff, "(%d, %d)", element->fWidth, element->fHeight); + SendMessage(GetDlgItem(hWnd, plClothingMtl::TextConstants[2 * i + 1]), + WM_SETTEXT, NULL, (LPARAM)buff); + + ShowWindow(GetDlgItem(hWnd, plClothingMtl::TextConstants[2 * i]), SW_SHOW); + ShowWindow(GetDlgItem(hWnd, plClothingMtl::TextConstants[2 * i + 1]), SW_SHOW); + ShowWindow(GetDlgItem(hWnd, plClothingMtl::ButtonConstants[i]), SW_SHOW); + } + for (i = tileset->fElements.GetCount(); i < plClothingMtl::kMaxTiles; i++) + { + ShowWindow(GetDlgItem(hWnd, plClothingMtl::TextConstants[2 * i]), SW_HIDE); + ShowWindow(GetDlgItem(hWnd, plClothingMtl::TextConstants[2 * i + 1]), SW_HIDE); + ShowWindow(GetDlgItem(hWnd, plClothingMtl::ButtonConstants[i]), SW_HIDE); + } + mtl->ReleaseTilesets(); + + fCustomText.UpdateText(pb, hWnd); + } + + virtual void Update(TimeValue t, Interval& valid, IParamMap2* pmap) { UpdateDisplay(pmap); } + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + // Check if it is for our edit box + if (fCustomText.ProcessMsg(map, hWnd, msg, wParam, lParam)) + return TRUE; + + int id = LOWORD(wParam); + int code = HIWORD(wParam); + + IParamBlock2 *pb = map->GetParamBlock(); + plClothingMtl *mtl = (plClothingMtl *)pb->GetOwner(); + HWND cbox = NULL; + plPlasmaMAXLayer *layer; + PBBitmap *pbbm; + ICustButton *bmSelectBtn; + int layerIdx = plClothingMtl::LayerToPBIdx[pb->GetInt(ParamID(plClothingMtl::kLayer))]; + + switch (msg) + { + case WM_INITDIALOG: + int j; + mtl->InitTilesets(); + cbox = GetDlgItem(hWnd, IDC_CLOTHING_TILESET); + for (j = 0; j < mtl->fTilesets.GetCount(); j++) + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)mtl->fTilesets.Get(j)->fName); + + mtl->ReleaseTilesets(); + + cbox = GetDlgItem(hWnd, IDC_CLOTHING_LAYER); + for (j = 0; j < plClothingElement::kLayerMax; j++) + ComboBox_AddString(cbox, plClothingMtl::LayerStrings[j]); + + return TRUE; + + case WM_COMMAND: + if (id == IDC_CLOTHING_TILESET) + { + int setIdx = SendMessage(GetDlgItem(hWnd, id), CB_GETCURSEL, 0, 0); + pb->SetValue(plClothingMtl::kTileset, t, setIdx); + return TRUE; + } + + if (id == IDC_CLOTHING_LAYER) + { + + pb->SetValue(plClothingMtl::kLayer, t, ComboBox_GetCurSel(GetDlgItem(hWnd, id))); + return TRUE; + } + + if (id == IDC_CLOTHING_THUMBNAIL) + { + layer = (plPlasmaMAXLayer *)pb->GetTexmap(ParamID(plClothingMtl::kThumbnail)); + if (layer == nil) + return FALSE; + + BitmapInfo bi; + bi.SetName(layer->GetPBBitmap() == nil ? "" : layer->GetPBBitmap()->bi.Name()); + + BOOL selectedNewBitmap = layer->HandleBitmapSelection(); + if (selectedNewBitmap) + { + pbbm = layer->GetPBBitmap(); + bmSelectBtn = GetICustButton(GetDlgItem(hWnd, IDC_CLOTHING_THUMBNAIL)); + bmSelectBtn->SetText(pbbm != nil ? (TCHAR*)pbbm->bi.Filename() : "(none)"); + ReleaseICustButton(bmSelectBtn); + } + return TRUE; + } + + int buttonIdx = -1; + if (id == IDC_CLOTHING_TEXTURE1) buttonIdx = 0; + else if (id == IDC_CLOTHING_TEXTURE2) buttonIdx = 1; + else if (id == IDC_CLOTHING_TEXTURE3) buttonIdx = 2; + else if (id == IDC_CLOTHING_TEXTURE4) buttonIdx = 3; + + if (buttonIdx != -1) + { + layer = (plPlasmaMAXLayer *)pb->GetTexmap(ParamID(layerIdx), 0, buttonIdx); + if (layer == nil) + { // First time we've set a layer on this spot + layer = TRACKED_NEW plLayerTex; + pb->SetValue(ParamID(layerIdx), 0, layer, buttonIdx); + } + + jvUniqueId oldId; + layer->GetBitmapAssetId(oldId); + + BitmapInfo bi; + bi.SetName(layer->GetPBBitmap() == nil ? "" : layer->GetPBBitmap()->bi.Name()); + + BOOL selectedNewBitmap = layer->HandleBitmapSelection(); + if (selectedNewBitmap) + { + // Check if it's ok, and undo if not. + hsBool choiceOk = true; + + pbbm = layer->GetPBBitmap(); + if (pbbm != nil) + { + mtl->InitTilesets(); + + plClothingTileset *tileset = mtl->fTilesets.Get(pb->GetInt(plClothingMtl::kTileset)); + plClothingElement *element = tileset->fElements.Get(buttonIdx); + float targRatio = (float)element->fWidth / (float)element->fHeight; + float ratio = (float)pbbm->bi.Width() / (float)pbbm->bi.Height(); + + if (targRatio != ratio) + { + choiceOk = false; + hsMessageBox("That image's width/height ratio does not match the one for this tile. " + "Restoring the old selection.", "Invalid image", hsMessageBoxNormal); + } + else if (pbbm->bi.Width() < element->fWidth) + { + choiceOk = false; + hsMessageBox("The chosen image is too small for that tile slot. " + "Restoring the old selection.", "Invalid image", hsMessageBoxNormal); + } + + mtl->ReleaseTilesets(); + } + if (!choiceOk) + { + layer->SetBitmapAssetId(oldId); + layer->SetBitmap(&bi); + } + else + { + bmSelectBtn = GetICustButton(GetDlgItem(hWnd, plClothingMtl::ButtonConstants[buttonIdx])); + bmSelectBtn->SetText(pbbm != nil ? (TCHAR*)pbbm->bi.Filename() : "(none)"); + ReleaseICustButton(bmSelectBtn); + } + } + } + return TRUE; + } + + return FALSE; + } + + void DeleteThis() {} +}; +static ClothingBasicDlgProc gClothingBasicDlgProc; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtl.cpp new file mode 100644 index 00000000..937143a4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtl.cpp @@ -0,0 +1,568 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plCompositeMtl.h" +#include "plPassMtl.h" +//#include "plCompositeMtlPB.h" +#include "plCompositeMtlDlg.h" + +class plCompositeClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return TRACKED_NEW plCompositeMtl(loading); } + const TCHAR* ClassName() { return GetString(IDS_COMP_MTL); } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + Class_ID ClassID() { return COMP_MTL_CLASS_ID; } + const TCHAR* Category() { return NULL; } + const TCHAR* InternalName() { return _T("PlasmaComposite"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plCompositeClassDesc plCompositeMtlDesc; +ClassDesc2* GetCompMtlDesc() { return &plCompositeMtlDesc; } + +#include "plCompositeMtlPBDec.h" + +const char *plCompositeMtl::BlendStrings[] = // Make sure these match up in order with the Blend enum (in the header) +{ + "Vertex Alpha", + "Inverse Vtx Alpha", + "Vertex Illum Red", + "Inv. Vtx Illum Red", + "Vertex Illum Green", + "Inv. Vtx Illum Green", + "Vertex Illum Blue", + "Inv. Vtx Illum Blue" +}; + +plCompositeMtl::plCompositeMtl(BOOL loading) : fPassesPB(NULL) +{ + plCompositeMtlDesc.MakeAutoParamBlocks(this); + + if (!loading) + Reset(); + + int i; + for (i = 0; i < NSUBMTLS; i++) + { + plPassMtl *newMtl = TRACKED_NEW plPassMtl(false); + fPassesPB->SetValue(kCompPasses, 0, newMtl, i); + GetCOREInterface()->AssignNewName(fPassesPB->GetMtl(kCompPasses, 0, i)); + } +} + +void plCompositeMtl::Reset() +{ + fIValid.SetEmpty(); +} + +ParamDlg* plCompositeMtl::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + fMtlDlg = TRACKED_NEW plCompositeMtlDlg(hwMtlEdit, imp, this); + + return fMtlDlg; +} + +void plCompositeMtl::SetParamDlg(ParamDlg *dlg) +{ + fMtlDlg = (plCompositeMtlDlg*)dlg; +} + +BOOL plCompositeMtl::SetDlgThing(ParamDlg* dlg) +{ + if (dlg == fMtlDlg) + { + fMtlDlg->SetThing(this); + return TRUE; + } + + return FALSE; +} + +Interval plCompositeMtl::Validity(TimeValue t) +{ + Interval valid = FOREVER; + +/* for (int i = 0; i < fSubTexmap.Count(); i++) + { + if (fSubTexmap[i]) + valid &= fSubTexmap[i]->Validity(t); + } +*/ +// float u; +// fPBlock->GetValue(pb_spin,t,u,valid); + return valid; +} + +// The index for a face on a composite material is really just a bitmask for all the submaterials. This function only +// computes it... The mesh creator will ask it to be created if it's needed. +int plCompositeMtl::ComputeMaterialIndex(float opac[][2], int vertCount) +{ + int index = 0; + int i;//, j; + int bitmask = 1; + for (i = NumSubMtls() - 1, bitmask <<= i; i >= 0; i--, bitmask >>= 1) + { + index |= bitmask; + /* + if (i == 0) + index |= bitmask; // it's not opaqued out, so include the base layer + else if (fPassesPB->GetInt(kCompOn, 0, i - 1)) // is the checkbox ticked? (ie, are we using it?) + { + bool transparent = true; + for (j = 0; j < vertCount; j++) + { + if (opac[j][i - 1] != 0.0) + transparent = false; + } + if (transparent) + continue; // totally transparent for this face, skip it + + index |= bitmask; // include this material + + bool opaque = true; + for (j = 0; j < vertCount; j++) + { + if (opac[j][i - 1] < 1.0) + opaque = false; + } + if (opaque && !((plPassMtlBase *)fPassesPB->GetMtl(kCompPasses, 0, i))->HasAlpha()) + break; // This material is totally opaque, no sense including anything underneath it + } + */ + } + return index; +} + +int plCompositeMtl::GetBlendStyle(int index) +{ + return fPassesPB->GetInt(kCompBlend, 0, index - 1); +} + +// Determines whether all materials are only blending on one channel (and possibly its inverse) and therefore +// it's ok to overwrite the vertex alpha exported for the span using this material. +// Returns: -1 if we use multiple sources, otherwise the source we all agree on +int plCompositeMtl::CanWriteAlpha() +{ + int blend[3]; + blend[0] = ((((plPassMtlBase *)GetSubMtl(0))->GetOutputBlend() == plPassMtlBase::kBlendNone) ? -1 : kCompBlendVertexAlpha); + blend[1] = (fPassesPB->GetInt(kCompOn, 0, 0) ? RemoveInverse(GetBlendStyle(1)) : -1); + blend[2] = (fPassesPB->GetInt(kCompOn, 0, 1) ? RemoveInverse(GetBlendStyle(2)) : -1); + + int source = blend[0]; + int i; + for (i = 1; i < 3; i++) + { + if (source < 0) + { + source = blend[i]; + continue; + } + if (source >= 0 && blend[i] >= 0 && blend[i] != source) + return -1; + } + return source; +} + +/*===========================================================================*\ + | Subanim & References support +\*===========================================================================*/ + +int plCompositeMtl::NumSubs() +{ + return NumSubMtls(); +} + +TSTR plCompositeMtl::SubAnimName(int i) +{ + return GetSubMtlSlotName(i); +} + +Animatable* plCompositeMtl::SubAnim(int i) +{ + return GetSubMtl(i); +} + +int plCompositeMtl::NumRefs() +{ + return 1; +} + +RefTargetHandle plCompositeMtl::GetReference(int i) +{ + if (i == kRefPasses) + return fPassesPB; + + return NULL; +} + +void plCompositeMtl::SetReference(int i, RefTargetHandle rtarg) +{ + if (i == kRefPasses) + fPassesPB = (IParamBlock2 *)rtarg; +} + +int plCompositeMtl::NumParamBlocks() +{ + return 1; +} + +IParamBlock2 *plCompositeMtl::GetParamBlock(int i) +{ + if (i == kRefPasses) + return fPassesPB; + + return NULL; +} + +IParamBlock2 *plCompositeMtl::GetParamBlockByID(BlockID id) +{ + if (fPassesPB->ID() == id) + return fPassesPB; + + return NULL; +} + +RefResult plCompositeMtl::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message ) +{ + switch (message) + { + case REFMSG_CHANGE: + fIValid.SetEmpty(); + if (hTarget == fPassesPB) + { + ParamID changingParam = fPassesPB->LastNotifyParamID(); + fPassesPB->GetDesc()->InvalidateUI(changingParam); + } + break; + } + + return REF_SUCCEED; +} + +//////////////////////////////////////////////////////////////////////////////// +// Subtexmap access + +int plCompositeMtl::NumSubMtls() +{ +// return fPassesPB->GetInt(kMultCount); + return NSUBMTLS; +} + +Mtl *plCompositeMtl::GetSubMtl(int i) +{ + if (i < NumSubMtls()) + return fPassesPB->GetMtl(kCompPasses, 0, i); + + return NULL; +} + +void plCompositeMtl::SetSubMtl(int i, Mtl *m) +{ + if (i < NumSubMtls()) + fPassesPB->SetValue(kCompPasses, 0, m, i); +} + +TSTR plCompositeMtl::GetSubMtlSlotName(int i) +{ + TSTR str; + str.printf("Pass %d", i+1); + return str; +} + +TSTR plCompositeMtl::GetSubMtlTVName(int i) +{ + return GetSubMtlSlotName(i); +} + + +/*===========================================================================*\ + | Standard IO +\*===========================================================================*/ + +#define MTL_HDR_CHUNK 0x4000 + +IOResult plCompositeMtl::Save(ISave *isave) +{ + IOResult res; + isave->BeginChunk(MTL_HDR_CHUNK); + res = MtlBase::Save(isave); + if (res!=IO_OK) return res; + isave->EndChunk(); + + return IO_OK; +} + +IOResult plCompositeMtl::Load(ILoad *iload) +{ + IOResult res; + int id; + while (IO_OK==(res=iload->OpenChunk())) + { + switch(id = iload->CurChunkID()) + { + case MTL_HDR_CHUNK: + res = MtlBase::Load(iload); + break; + } + iload->CloseChunk(); + if (res!=IO_OK) + return res; + } + + return IO_OK; +} + + +/*===========================================================================*\ + | Updating and cloning +\*===========================================================================*/ + +RefTargetHandle plCompositeMtl::Clone(RemapDir &remap) +{ + plCompositeMtl *mnew = TRACKED_NEW plCompositeMtl(FALSE); + *((MtlBase*)mnew) = *((MtlBase*)this); + mnew->ReplaceReference(kRefPasses, remap.CloneRef(fPassesPB)); + + mnew->fIValid.SetEmpty(); + BaseClone(this, mnew, remap); + + return (RefTargetHandle)mnew; +} + +void plCompositeMtl::NotifyChanged() +{ + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +} + +void plCompositeMtl::Update(TimeValue t, Interval& valid) +{ + if (!fIValid.InInterval(t)) + { + fIValid.SetInfinite(); +// fPassesPB->GetValue(kMtlLayLayer1On, t, fMapOn[0], fIValid); + + for (int i = 0; i < NumSubMtls(); i++) + { + if (GetSubMtl(i)) + GetSubMtl(i)->Update(t, fIValid); + } + } + + valid &= fIValid; +} + +/*===========================================================================*\ + | Determine the characteristics of the material +\*===========================================================================*/ +void plCompositeMtl::SetAmbient(Color c, TimeValue t) {} +void plCompositeMtl::SetDiffuse(Color c, TimeValue t) {} +void plCompositeMtl::SetSpecular(Color c, TimeValue t) {} +void plCompositeMtl::SetShininess(float v, TimeValue t) {} + +Color plCompositeMtl::GetAmbient(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plCompositeMtl::GetDiffuse(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plCompositeMtl::GetSpecular(int mtlNum, BOOL backFace) { return Color(0,0,0); } +float plCompositeMtl::GetXParency(int mtlNum, BOOL backFace) { return 0.0f; } +float plCompositeMtl::GetShininess(int mtlNum, BOOL backFace) { return 0.0f; } +float plCompositeMtl::GetShinStr(int mtlNum, BOOL backFace) { return 0.0f; } +float plCompositeMtl::WireSize(int mtlNum, BOOL backFace) { return 0.0f; } + +/*===========================================================================*\ + | Actual shading takes place +\*===========================================================================*/ + +void plCompositeMtl::Shade(ShadeContext& sc) +{ + // Get the background color + Color backColor, backTrans; + //sc.GetBGColor(backColor, backTrans); + backColor.Black(); + backTrans.White(); + + Point3 vtxIllum, vtxAlpha; + plPassMtl::GetInterpVtxValue(MAP_ALPHA, sc, vtxAlpha); + plPassMtl::GetInterpVtxValue(MAP_SHADING, sc, vtxIllum); + int count = NumSubMtls(); + for (int i = 0; i < count; i++) + { + if (i > 0 && fPassesPB->GetInt(kCompOn, 0, i - 1) == 0) // Material is unchecked, skip it. + continue; + + Mtl *mtl = GetSubMtl(i); + if (mtl == NULL || mtl->ClassID() != PASS_MTL_CLASS_ID) + continue; + + float opacity; + if (i == 0) + { + opacity = 1.0f; + } + else + { + int blendMethod = fPassesPB->GetInt(kCompBlend, 0, i - 1); + switch(blendMethod) + { + case kCompBlendVertexAlpha: + case kCompBlendInverseVtxAlpha: + opacity = vtxAlpha.x; + break; + case kCompBlendVertexIllumRed: + case kCompBlendInverseVtxIllumRed: + opacity = vtxIllum.x; + break; + case kCompBlendVertexIllumGreen: + case kCompBlendInverseVtxIllumGreen: + opacity = vtxIllum.y; + break; + case kCompBlendVertexIllumBlue: + case kCompBlendInverseVtxIllumBlue: + opacity = vtxIllum.z; + break; + default: + opacity = 1.0f; + break; + } + if (IsInverseBlend(blendMethod)) + opacity = 1 - opacity; + } + + plPassMtl *passMtl = (plPassMtl*)mtl; + passMtl->ShadeWithBackground(sc, backColor, false); // Don't include the vtx alpha, that's OUR job + float currTrans = (1 - (1 - sc.out.t.r) * opacity); + backTrans *= currTrans; + backColor = backColor * currTrans + sc.out.c * opacity; + } + + sc.out.t = backTrans; + sc.out.c = backColor; +} + +float plCompositeMtl::EvalDisplacement(ShadeContext& sc) +{ + return 0.0f; +} + +Interval plCompositeMtl::DisplacementValidity(TimeValue t) +{ + Interval iv; + iv.SetInfinite(); + + return iv; +} + +/* +void plCompositeMtl::SetNumSubMtls(int num) +{ + TimeValue t = GetCOREInterface()->GetTime(); + int curNum = fPassesPB->GetInt(kMultCount); + + fPassesPB->SetValue(kMultCount, 0, num); + + fPassesPB->SetCount(kMultPasses, num); + fPassesPB->SetCount(kMultOn, num); + + for (int i = curNum; i < num; i++) + { + plPassMtl *newMtl = TRACKED_NEW plPassMtl(false); + fPassesPB->SetValue(kMultPasses, t, newMtl, i); + fPassesPB->SetValue(kMultOn, t, TRUE, i); + GetCOREInterface()->AssignNewName(fPassesPB->GetMtl(kMultPasses, t, i)); + } +} +*/ + + +void plCompositeMtl::SetOpacityVal(float *target, UVVert *alpha, UVVert *illum, int method) +{ + if (method == kCompBlendVertexAlpha || method == kCompBlendInverseVtxAlpha) + { + if (alpha == NULL) + *target = 1.0f; + else + *target = alpha->x; + } + else if (method == kCompBlendVertexIllumRed || method == kCompBlendInverseVtxIllumRed) + { + if (illum == NULL) + *target = 1.0f; + else + *target = illum->x; + } + else if (method == kCompBlendVertexIllumGreen || method == kCompBlendInverseVtxIllumGreen) + { + if (illum == NULL) + *target = 1.0f; + else + *target = illum->y; + } + else if (method == kCompBlendVertexIllumBlue || method == kCompBlendInverseVtxIllumBlue) + { + if (illum == NULL) + *target = 1.0f; + else + *target = illum->z; + } + else + { + *target = 1.0f; + } + + if (method == kCompBlendInverseVtxAlpha || + method == kCompBlendInverseVtxIllumRed || + method == kCompBlendInverseVtxIllumGreen || + method == kCompBlendInverseVtxIllumBlue) + { + *target = 1.0f - *target; + } +} +/* +int plCompositeMtl::UVChannelsNeeded(bool makeAlphaLayer) +{ + if (makeAlphaLayer) + return 1; + + // Otherwise, we do have vertex alpha... can we get by without taking up a UV channel? + int channels = 0; + + int i; + for (i = 0; i < NumSubMtls() - 1; i++) + { + if (!fPassesPB->GetInt(kCompOn, 0, i)) + continue; // The material is unchecked, no need to see if it needs a channel + + int method = fPassesPB->GetInt(kCompBlend, 0, i); + if (!(method == kCompBlendVertexAlpha || method1 == kCompBlendInverseVtxAlpha)) + { + channels = 1; + break; + } + } + + return channels; +} +*/ \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtl.h new file mode 100644 index 00000000..2b580a3d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtl.h @@ -0,0 +1,154 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __PLCOMPMTL__H +#define __PLCOMPMTL__H + +#include "Max.h" +#include "../resource.h" +#include "istdplug.h" +#include "iparamb2.h" +#include "iparamm2.h" + +extern TCHAR *GetString(int id); +extern HINSTANCE hInstance; + +#define COMP_MTL_CLASS_ID Class_ID(0x237c422d, 0x64ab6371) + +class plCompositeMtlDlg; + +class plCompositeMtl : public Mtl +{ +protected: + IParamBlock2 *fPassesPB; + Interval fIValid; + plCompositeMtlDlg *fMtlDlg; + +public: + enum + { + kCompPasses, + kCompOn, + kCompBlend, + kCompUVChannels, + kCompLayerCounts + }; + + // Make sure to pair up each blend mode with an inverse after it + // (This way we check for an inverse blend by doing an odd/even check.) + enum BlendMethod // These should match up in order with the blend strings + { + kCompBlendVertexAlpha, + kCompBlendInverseVtxAlpha, + kCompBlendVertexIllumRed, + kCompBlendInverseVtxIllumRed, + kCompBlendVertexIllumGreen, + kCompBlendInverseVtxIllumGreen, + kCompBlendVertexIllumBlue, + kCompBlendInverseVtxIllumBlue, + + kCompNumBlendMethods + }; + + static const char *BlendStrings[]; + + enum { kRefPasses }; + enum { kBlkPasses }; + + ParamDlg *CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + void Update(TimeValue t, Interval& valid); + Interval Validity(TimeValue t); + void Reset(); + + void NotifyChanged(); + + // From MtlBase and Mtl + void SetAmbient(Color c, TimeValue t); + void SetDiffuse(Color c, TimeValue t); + void SetSpecular(Color c, TimeValue t); + void SetShininess(float v, TimeValue t); + Color GetAmbient(int mtlNum=0, BOOL backFace=FALSE); + Color GetDiffuse(int mtlNum=0, BOOL backFace=FALSE); + Color GetSpecular(int mtlNum=0, BOOL backFace=FALSE); + float GetXParency(int mtlNum=0, BOOL backFace=FALSE); + float GetShininess(int mtlNum=0, BOOL backFace=FALSE); + float GetShinStr(int mtlNum=0, BOOL backFace=FALSE); + float WireSize(int mtlNum=0, BOOL backFace=FALSE); + + // Shade and displacement calculation + void Shade(ShadeContext& sc); + float EvalDisplacement(ShadeContext& sc); + Interval DisplacementValidity(TimeValue t); + + // SubTexmap access methods + int NumSubMtls(); + Mtl* GetSubMtl(int i); + void SetSubMtl(int i, Mtl *m); + TSTR GetSubMtlSlotName(int i); + TSTR GetSubMtlTVName(int i); + + BOOL SetDlgThing(ParamDlg* dlg); + plCompositeMtl(BOOL loading); + + // Loading/Saving + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + //From Animatable + Class_ID ClassID() { return COMP_MTL_CLASS_ID; } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + void GetClassName(TSTR& s) { s = GetString(IDS_COMP_MTL); } + + RefTargetHandle Clone(RemapDir &remap); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + + int NumParamBlocks(); + IParamBlock2* GetParamBlock(int i); + IParamBlock2* GetParamBlockByID(BlockID id); + + void DeleteThis() { delete this; } + + void SetParamDlg(ParamDlg *dlg); + + int ComputeMaterialIndex(float opac[][2], int vertCount); + int GetBlendStyle(int index); + int CanWriteAlpha(); + bool IsInverseBlend(int blend) const { return blend & 1; } + int RemoveInverse(int blend) { return blend - (blend & 1); } + //void SetNumSubMtls(int num); + void SetOpacityVal(float *pt, UVVert *alphas, UVVert *illums, int method); + //DllExport int UVChannelsNeeded(bool makeAlphaLayer); +}; + +#endif // __PLCOMPMTL__H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlDlg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlDlg.cpp new file mode 100644 index 00000000..d939d040 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlDlg.cpp @@ -0,0 +1,277 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "max.h" +#include "MaxIcon.h" + +#include "resource.h" +#include "plCompositeMtl.h" +//#include "plCompositeMtlPB.h" +//#include "plMaxLayer.h" +#include "plCompositeMtlDlg.h" + +struct LayerID +{ + int layerID; + int activeID; + int blendID; +}; +static LayerID kLayerID[] = +{ + { IDC_TEX1, 0, 0 }, + { IDC_TEX2, IDC_TEXON2, IDC_COMBO2 }, + { IDC_TEX3, IDC_TEXON3, IDC_COMBO3 } +}; + +//----------------------------------------------------------------------------- +// Constructor and destructor +//----------------------------------------------------------------------------- + +plCompositeMtlDlg::plCompositeMtlDlg(HWND hwMtlEdit, IMtlParams *imp, plCompositeMtl *m) +{ + fDADMgr.Init(this); + + fhMtlEdit = hwMtlEdit; + fhRollup = NULL; + fMtl = m; + fPBlock = fMtl->GetParamBlockByID(plCompositeMtl::kBlkPasses); + ip = imp; + valid = FALSE; + + for (int i = 0; i < NSUBMTLS; i++) + fLayerBtns[i] = NULL; + + curTime = imp->GetTime(); + + fhRollup = ip->AddRollupPage( + hInstance, + MAKEINTRESOURCE(IDD_COMPOSITE), + ForwardProc, + "Composite Parameters", + (LPARAM)this); +} + +plCompositeMtlDlg::~plCompositeMtlDlg() +{ + fMtl->SetParamDlg(NULL); + for (int i = 0; i < NSUBMTLS; i++) + { + ReleaseICustButton(fLayerBtns[i]); + fLayerBtns[i] = NULL; + } + + SetWindowLong(fhRollup, GWL_USERDATA, NULL); + ip->DeleteRollupPage(fhRollup); + + fhRollup = NULL; +} + +//----------------------------------------------------------------------------- +// Functions inheirited from ParamDlg +//----------------------------------------------------------------------------- + +void plCompositeMtlDlg::SetThing(ReferenceTarget *m) +{ + assert(m->SuperClassID() == MATERIAL_CLASS_ID); + assert(m->ClassID() == COMP_MTL_CLASS_ID); + + // Bad? + if (fMtl) + fMtl->SetParamDlg(NULL); + fMtl = (plCompositeMtl *)m; + if (fMtl) + fMtl->SetParamDlg(this); + + LoadDialog(); + IUpdateMtlDisplay(); +} + +void plCompositeMtlDlg::SetTime(TimeValue t) +{ + if (t != curTime) + { + curTime = t; + Interval v; + fMtl->Update(ip->GetTime(),v); + LoadDialog(); + IUpdateMtlDisplay(); + } +} + +void plCompositeMtlDlg::ReloadDialog() +{ + Interval v; + fMtl->Update(ip->GetTime(), v); + LoadDialog(); +} + +void plCompositeMtlDlg::ActivateDlg(BOOL onOff) +{ +} + +int plCompositeMtlDlg::FindSubMtlFromHWND(HWND hwnd) +{ + for (int i = 0; i < NSUBMTLS; i++) + { + if (hwnd == fLayerBtns[i]->GetHwnd()) + return i; + } + + return -1; +} + +BOOL plCompositeMtlDlg::ForwardProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + plCompositeMtlDlg *theDlg; + if (msg == WM_INITDIALOG) + { + theDlg = (plCompositeMtlDlg*)lParam; + theDlg->fhRollup = hDlg; + SetWindowLong(hDlg, GWL_USERDATA, lParam); + } + else + { + if ((theDlg = (plCompositeMtlDlg *)GetWindowLong(hDlg, GWL_USERDATA)) == NULL) + return FALSE; + } + + return theDlg->LayerPanelProc(hDlg,msg,wParam,lParam); +} + + + +//---------------------------------------------------------------------------- +// Layer panel processor +//---------------------------------------------------------------------------- +BOOL plCompositeMtlDlg::LayerPanelProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + int id = LOWORD(wParam); + int code = HIWORD(wParam); + int i; + + switch (msg) + { + case WM_INITDIALOG: + { + for (i = 0; i < NSUBMTLS; i++) + { + fLayerBtns[i] = GetICustButton(GetDlgItem(hDlg, kLayerID[i].layerID)); + fLayerBtns[i]->SetDADMgr(&fDADMgr); + + if (i > 0) // the first material doesn't get one, nyah nyah! + { + HWND cbox = NULL; + int j; + for (j = 0; j < plCompositeMtl::kCompNumBlendMethods; j++) + { + cbox = GetDlgItem(hDlg, kLayerID[i].blendID); + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)plCompositeMtl::BlendStrings[j]); + } + SendMessage(cbox, CB_SETCURSEL, 0, 0); + } + } + + // TEMP testing + UpdateLayerDisplay(); + + IUpdateMtlDisplay(); + } + return TRUE; + + case WM_DESTROY: + for (i = 0; i < NSUBMTLS; i++) + { + ReleaseICustButton(fLayerBtns[i]); + fLayerBtns[i] = NULL; + } + break; + + case WM_COMMAND: + { + for (i = 0; i < NSUBMTLS; i++) + { + if (id == kLayerID[i].activeID) + { + bool checked = SendMessage(GetDlgItem(hDlg, id), BM_GETCHECK, 0, 0) == BST_CHECKED; + fPBlock->SetValue(plCompositeMtl::kCompOn, curTime, checked, i - 1); + return TRUE; + } + if (id == kLayerID[i].layerID) + { + PostMessage(fhMtlEdit, WM_SUB_MTL_BUTTON, i, (LPARAM)fMtl); + return TRUE; + } + if (id == kLayerID[i].blendID) + { + fPBlock->SetValue(plCompositeMtl::kCompBlend, curTime, SendMessage(GetDlgItem(hDlg, id), CB_GETCURSEL, 0, 0), i - 1); + return TRUE; + } + } + + } +// IUpdateMtlDisplay(); + break; + } + + return FALSE; +} + +void plCompositeMtlDlg::UpdateLayerDisplay() +{ + int i; + for (i = 0; i < NSUBMTLS; i++) + { + Mtl *m = fPBlock->GetMtl(plCompositeMtl::kCompPasses, curTime, i); + TSTR nm; + if (m) + nm = m->GetName(); + else + nm = "None"; + fLayerBtns[i]->SetText(nm.data()); + + ShowWindow(GetDlgItem(fhRollup, kLayerID[i].layerID), SW_SHOW); + ShowWindow(GetDlgItem(fhRollup, kLayerID[i].activeID), SW_SHOW); + if (i > 0) + { + int check = (fPBlock->GetInt(plCompositeMtl::kCompOn, curTime, i - 1) == 0 ? BST_UNCHECKED : BST_CHECKED); + SendMessage(GetDlgItem(fhRollup, kLayerID[i].activeID), BM_SETCHECK, (WPARAM)check, 0); + int selection = fPBlock->GetInt(plCompositeMtl::kCompBlend, curTime, i - 1); + SendMessage(GetDlgItem(fhRollup, kLayerID[i].blendID), CB_SETCURSEL, (WPARAM)selection, 0); + } + } +} + +void plCompositeMtlDlg::LoadDialog() +{ + if (fMtl) + { + fPBlock = fMtl->GetParamBlockByID(plCompositeMtl::kBlkPasses); + + if (fhRollup) + UpdateLayerDisplay(); + } +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlDlg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlDlg.h new file mode 100644 index 00000000..32f02853 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlDlg.h @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_COMPMTLDLG_H +#define PL_COMPMTLDLG_H + +#define NSUBMTLS 3 + +class plCompositeMtlDlg : public ParamDlg +{ + +protected: + IParamBlock2 *fPBlock; + + HWND fhMtlEdit; // Window handle of the materials editor dialog + HWND fhRollup; // Our rollup panel + IMtlParams *ip; + plCompositeMtl *fMtl; // current mtl being edited. + TimeValue curTime; + int isActive; + BOOL valid; +// int offset; + + ICustButton *fLayerBtns[NSUBMTLS]; + + MtlDADMgr fDADMgr; // For drag-drop sub-materials +public: + // Constructor and destructor + plCompositeMtlDlg(HWND hwMtlEdit, IMtlParams *imp, plCompositeMtl *m); + ~plCompositeMtlDlg(); + + // Functions inherited from ParamDLg: + Class_ID ClassID() { return COMP_MTL_CLASS_ID; } + void SetThing(ReferenceTarget *m); + ReferenceTarget* GetThing() { return (ReferenceTarget*)fMtl; } + void SetTime(TimeValue t); + void ReloadDialog(); + void ActivateDlg(BOOL onOff); + void DeleteThis() { delete this; } + int FindSubMtlFromHWND(HWND hw); + + static BOOL CALLBACK ForwardProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL LayerPanelProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + + void UpdateLayerDisplay(); + void LoadDialog(); +/* + // Lower-level crap + void Invalidate(); // Called by ParamMtl + BOOL IsActive() { return isActive; } + +private: + void ClampOffset(); + void SetNumMats(); + + void UpdateLayers(); + void UpdateControlFor(int np); + void VScroll(int code, short int cpos ); +*/ +protected: + void IUpdateMtlDisplay() { if (ip) ip->MtlChanged(); } +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlPB.h new file mode 100644 index 00000000..a27a0e0b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlPB.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_COMPOSITE_MTL_PB_H +#define PL_COMPOSITE_MTL_PB_H + +enum +{ + kCompPasses, + kCompOn, + kCompBlend, + kCompUVChannels, + kCompLayerCounts +}; + +// Make sure to pair up each blend mode with an inverse after it +// (This way we check for an inverse blend by doing an odd/even check.) +enum BlendMethod // These should match up in order with the blend strings +{ + kCompBlendVertexAlpha, + kCompBlendInverseVtxAlpha, + kCompBlendVertexIllumRed, + kCompBlendInverseVtxIllumRed, + kCompBlendVertexIllumGreen, + kCompBlendInverseVtxIllumGreen, + kCompBlendVertexIllumBlue, + kCompBlendInverseVtxIllumBlue, + kCompNumBlendMethods +}; + +static char *BlendStrings[] = // Make sure these match up in order with the Blend enum +{ + "Vertex Alpha", + "Inverse Vtx Alpha", + "Vertex Illum Red", + "Inv. Vtx Illum Red", + "Vertex Illum Green", + "Inv. Vtx Illum Green", + "Vertex Illum Blue", + "Inv. Vtx Illum Blue" +}; + +#endif //PL_COMPOSITE_MTL_PB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlPBDec.h new file mode 100644 index 00000000..18dd81c3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plCompositeMtlPBDec.h @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plCompositeMtl.h" +//#include "plCompositeMtlPB.h" + +ClassDesc2* GetCompMtlDesc(); + +static ParamBlockDesc2 gCompositeMtlPB +( + plCompositeMtl::kBlkPasses, _T("composite"), 0, GetCompMtlDesc(), + P_AUTO_CONSTRUCT, plCompositeMtl::kRefPasses, + + plCompositeMtl::kCompPasses, _T("passes"), TYPE_MTL_TAB, 3, 0, 0, + end, + plCompositeMtl::kCompOn, _T("passOn"), TYPE_BOOL_TAB, 2, 0, 0, + p_default, TRUE, + end, + plCompositeMtl::kCompBlend, _T("BlendMethod"), TYPE_INT_TAB, 2, 0, 0, + p_default, 0, + end, + plCompositeMtl::kCompUVChannels, _T("UVChannels"), TYPE_INT_TAB, 2, 0, 0, + p_default, 0, + end, + plCompositeMtl::kCompLayerCounts, _T("LayerCounts"), TYPE_INT_TAB, 3, 0, 0, + p_default, 0, + end, + end +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtl.cpp new file mode 100644 index 00000000..f6fae448 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtl.cpp @@ -0,0 +1,784 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plDecalMtl.h" +#include "resource.h" +//extern ClassDesc2* GetMaxLayerDesc(); +#include "Shaders.h" +#include "../MaxComponent/plMaxAnimUtils.h" + +#include "plPassBaseParamIDs.h" +#include "plDecalMtlBasicPB.h" +#include "plDecalMtlLayersPB.h" + +#include "iparamm2.h" + +#include "Layers/plLayerTex.h" +#include "Layers/plStaticEnvLayer.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +extern HINSTANCE hInstance; + +class plDecalMtlClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return TRACKED_NEW plDecalMtl(loading); } + const TCHAR* ClassName() { return GetString(IDS_DECAL_MTL); } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + Class_ID ClassID() { return DECAL_MTL_CLASS_ID; } + const TCHAR* Category() { return NULL; } + const TCHAR* InternalName() { return _T("PlasmaMaterial"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plDecalMtlClassDesc plDecalMtlDesc; +ClassDesc2* GetDecalMtlDesc() { return &plDecalMtlDesc; } + +// For initializing paramblock descriptor +ParamBlockDesc2 *GetDecalBasicPB(); +ParamBlockDesc2 *GetDecalAdvPB(); +ParamBlockDesc2 *GetDecalLayersPB(); + +#include "plDecalMtlAdvPBDec.h" +#include "plDecalMtlBasicPBDec.h" +#include "plDecalMtlLayersPBDec.h" +#include "plDecalMtlAnimPBDec.h" + +plDecalMtl::plDecalMtl(BOOL loading) : plPassMtlBase( loading ) +{ + plDecalMtlDesc.MakeAutoParamBlocks( this ); + fLayersPB->SetValue( kDecalLayBase, 0, TRACKED_NEW plLayerTex ); + fLayersPB->SetValue( kDecalLayTop, 0, TRACKED_NEW plLayerTex ); + + // If we do this later (like, when the dialog loads) something blows up, + // somewhere in Max. It didn't in 4, it does in 7. This seems to fix it. + if (!loading) + IVerifyStealthPresent(ENTIRE_ANIMATION_NAME); +} + +ParamDlg* plDecalMtl::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + fIMtlParams = imp; + IAutoMParamDlg* masterDlg = plDecalMtlDesc.CreateParamDlgs(hwMtlEdit, imp, this); + + return (ParamDlg*)masterDlg; +} + +BOOL plDecalMtl::SetDlgThing(ParamDlg* dlg) +{ + return FALSE; +} + +Interval plDecalMtl::Validity(TimeValue t) +{ +#if 0 // mf horse + Interval valid = FOREVER; + +/* for (int i = 0; i < fSubTexmap.Count(); i++) + { + if (fSubTexmap[i]) + valid &= fSubTexmap[i]->Validity(t); + } +*/ +// float u; +// fPBlock->GetValue(pb_spin,t,u,valid); + return valid; +#else // mf horse + const char* name = GetName(); + + // mf horse - Hacking in something like real validity checking + // to get material animations working. No warranty, this is just + // better than nothing. + Interval v = FOREVER; + fBasicPB->GetValidity(t, v); + fAdvPB->GetValidity(t, v); + + if( fLayersPB->GetTexmap(kDecalLayBase) ) + v &= fLayersPB->GetTexmap(kDecalLayBase)->Validity(t); + if( fLayersPB->GetTexmap(kDecalLayTop) ) + v &= fLayersPB->GetTexmap(kDecalLayTop)->Validity(t); + return v; +#endif // mf horse +} + +//// GetReference //////////////////////////////////////////////////////////// +// Note: need to overload because MAX for some reason writes out the +// references by their INDEX. ARRRRGH! + +RefTargetHandle plDecalMtl::GetReference( int i ) +{ + switch( i ) + { + case kRefBasic: return fBasicPB; + case kRefAdv: return fAdvPB; + case kRefLayers: return fLayersPB; + case kRefAnim: return fAnimPB; + } + + return plPassMtlBase::GetReference( i ); +} + +//// SetReference //////////////////////////////////////////////////////////// +// Note: need to overload because MAX for some reason writes out the +// references by their INDEX. ARRRRGH! + +void plDecalMtl::SetReference(int i, RefTargetHandle rtarg) +{ + if (i == kRefBasic) + fBasicPB = (IParamBlock2 *)rtarg; + else if (i == kRefAdv) + fAdvPB = (IParamBlock2 *)rtarg; + else if (i == kRefLayers) + fLayersPB = (IParamBlock2 *)rtarg; + else if (i == kRefAnim) + fAnimPB = (IParamBlock2 *)rtarg; + else + plPassMtlBase::SetReference( i, rtarg ); +} + + +/*===========================================================================*\ + | Subanim & References support +\*===========================================================================*/ + +int plDecalMtl::NumSubs() +{ + return 6; +} + +TSTR plDecalMtl::SubAnimName(int i) +{ + switch (i) + { + case 0: return fBasicPB->GetLocalName(); + case 1: return fAdvPB->GetLocalName(); + case 2: return fLayersPB->GetLocalName(); + case 3: return fAnimPB->GetLocalName(); + case 4: return "Base Layer"; + case 5: return "Top Layer"; + } + + return ""; +} + +Animatable* plDecalMtl::SubAnim(int i) +{ + switch (i) + { + case 0: return fBasicPB; + case 1: return fAdvPB; + case 2: return fLayersPB; + case 3: return fAnimPB; + case 4: return fLayersPB->GetTexmap(kDecalLayBase); + case 5: + if (fLayersPB->GetInt(kDecalLayTopOn)) + return fLayersPB->GetTexmap(kDecalLayTop); + break; + } + + return NULL; +} + +int plDecalMtl::NumParamBlocks() +{ + return 4; +} + +IParamBlock2* plDecalMtl::GetParamBlock(int i) +{ + return (IParamBlock2*)GetReference(i); +} + +IParamBlock2* plDecalMtl::GetParamBlockByID(BlockID id) +{ + if (fBasicPB->ID() == id) + return fBasicPB; + else if (fAdvPB->ID() == id) + return fAdvPB; + else if (fLayersPB->ID() == id) + return fLayersPB; + else if (fAnimPB->ID() == id) + return fAnimPB; + + return NULL; +} + +RefResult plDecalMtl::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) +{ + return plPassMtlBase::NotifyRefChanged( changeInt, hTarget, partID, message ); +} + +//////////////////////////////////////////////////////////////////////////////// +// Subtexmap access + +int plDecalMtl::NumSubTexmaps() +{ + return 2; +} + +Texmap* plDecalMtl::GetSubTexmap(int i) +{ + if (i == 0) + return fLayersPB->GetTexmap(kDecalLayBase); + else if (i == 1) + return fLayersPB->GetTexmap(kDecalLayTop); + + return NULL; +} + +void plDecalMtl::SetSubTexmap(int i, Texmap *m) +{ + if (i == 0) + fLayersPB->SetValue(kDecalLayBase, 0, m); + else if (i == 1) + fLayersPB->SetValue(kDecalLayTop, 0, m); +} + +TSTR plDecalMtl::GetSubTexmapSlotName(int i) +{ + if (i == 0) + return "Base"; + else if (i == 1) + return "Top"; + + return ""; +} + +TSTR plDecalMtl::GetSubTexmapTVName(int i) +{ + return GetSubTexmapSlotName(i); +} + +int plDecalMtl::SubTexmapOn(int i) +{ + if (i == 0) + return 1; + else if (i == 1 && fLayersPB->GetInt(kDecalLayTopOn)) + return 1; + + return 0; +} + + +/*===========================================================================*\ + | Updating and cloning +\*===========================================================================*/ + +RefTargetHandle plDecalMtl::Clone(RemapDir &remap) +{ + plDecalMtl *mnew = TRACKED_NEW plDecalMtl(FALSE); + plPassMtlBase::ICloneBase( mnew, remap ); + return (RefTargetHandle)mnew; +} + +void plDecalMtl::ICloneRefs( plPassMtlBase *target, RemapDir &remap ) +{ + target->ReplaceReference(kRefBasic, remap.CloneRef(fBasicPB)); + target->ReplaceReference(kRefAdv, remap.CloneRef(fAdvPB)); + target->ReplaceReference(kRefLayers, remap.CloneRef(fLayersPB)); + target->ReplaceReference(kRefAnim, remap.CloneRef(fAnimPB)); +} + +void plDecalMtl::NotifyChanged() +{ + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +} + +void plDecalMtl::Update(TimeValue t, Interval& valid) +{ + // mf horse - Hacking in something like real validity checking + // to get material animations working. No warranty, this is just + // better than nothing. + if (!fIValid.InInterval(t)) + { + fIValid.SetInfinite(); + if( fLayersPB->GetTexmap(kDecalLayBase) ) + fLayersPB->GetTexmap(kDecalLayBase)->Update(t, fIValid); + if( fLayersPB->GetTexmap(kDecalLayTop) ) + fLayersPB->GetTexmap(kDecalLayTop)->Update(t, fIValid); + +// fLayersPB->GetValue(kMtlLayLayer1On, t, fMapOn[0], fIValid); +/* + for (int i = 0; i < fSubTexmap.Count(); i++) + { + if (fSubTexmap[i]) + fSubTexmap[i]->Update(t,fIValid); + } +*/ + } + + // Our wonderful way of version handling--if the runtimeColor is (-1,-1,-1), we know it's + // just been initialized, so set it to the static color (this lets us do the right thing for + // loading old paramBlocks) + if( fBasicPB ) + { + Color run = fBasicPB->GetColor( kDecalBasRunColor, 0 ); + if( run == Color(-1,-1,-1) ) + { + fBasicPB->SetValue( kDecalBasRunColor, 0, fBasicPB->GetColor( kDecalBasColor, 0 ) ); + } + + // Also, if shineStr is anything other than -1, then it must be an old paramblock and we need + // to convert to our new specColor (we know this because the original valid range was 0-100) + int shine = fBasicPB->GetInt( kDecalBasShineStr, 0 ); + if( shine != -1 ) + { + fBasicPB->SetValue( kDecalBasSpecColor, 0, Color( (float)shine / 100.f, (float)shine / 100.f, (float)shine / 100.f ) ); + fBasicPB->SetValue( kDecalBasShineStr, 0, (int)-1 ); + } + } + + valid &= fIValid; +} + +/*===========================================================================*\ + | Determine the characteristics of the material +\*===========================================================================*/ + +void plDecalMtl::SetAmbient(Color c, TimeValue t) {} +void plDecalMtl::SetDiffuse(Color c, TimeValue t) {} +void plDecalMtl::SetSpecular(Color c, TimeValue t) {} +void plDecalMtl::SetShininess(float v, TimeValue t) {} + +Color plDecalMtl::GetAmbient(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plDecalMtl::GetDiffuse(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plDecalMtl::GetSpecular(int mtlNum, BOOL backFace) { return Color(0,0,0); } + +float plDecalMtl::GetXParency(int mtlNum, BOOL backFace) +{ + int opacity = fBasicPB->GetInt( kDecalBasOpacity, 0 ); + float alpha = 1.0f - ( (float)opacity / 100.0f ); + + return alpha; +} + +float plDecalMtl::GetShininess(int mtlNum, BOOL backFace) { return 0.0f; } +float plDecalMtl::GetShinStr(int mtlNum, BOOL backFace) { return 0.0f; } +float plDecalMtl::WireSize(int mtlNum, BOOL backFace) { return 0.0f; } + +///////////////////////////////////////////////////////////////// + +void plDecalMtl::SetupGfxMultiMaps(TimeValue t, Material *mtl, MtlMakerCallback &cb) +{ +#if 0 + if (texHandleValid.InInterval(t)) { + mtl->texture.SetCount(numTexHandlesUsed); + for (int i=0; itexture[i].textHandle = texHandle[i]->GetHandle(); + Texmap *tx = (*maps)[useSubForTex[i]].map; + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[i], tx ); + SetTexOps(mtl,i,texOpsType[i]); + } + } + return; + } +#endif + +#if 0 // WTF?!?!?!? + Texmap *tx[2]; + int diffChan = stdIDToChannel[ ID_DI ]; + int opacChan = stdIDToChannel[ ID_OP ]; + tx[0] = (*maps)[diffChan].IsActive()?(*maps)[diffChan].map:NULL; + tx[1] = (*maps)[opacChan].IsActive()?(*maps)[opacChan].map:NULL; +#endif + + int nsupport = cb.NumberTexturesSupported(); +#if 0 + BITMAPINFO *bmi[NTEXHANDLES]; + + int nmaps=0; + for (int i=0; itexture.SetCount(nmaps); + if (nmaps==0) + return; + for (i=0; itexture[i].textHandle = NULL; + texHandleValid.SetInfinite(); + Interval valid; + BOOL needDecal = FALSE; + int ntx = 0; + int op; + + int forceW = 0; + int forceH = 0; + if (tx[0]) { + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[0], tx[0]); + TextureInfo &ti = mtl->texture[0]; + if (ti.tiling[0]==GW_TEX_NO_TILING||ti.tiling[1]==GW_TEX_NO_TILING) + needDecal = TRUE; + op = needDecal?TXOP_ALPHABLEND:TXOP_MODULATE; + bmi[0] = tx[0]->GetVPDisplayDIB(t,cb,valid,FALSE); + if (bmi[0]) { + texHandleValid &= valid; + useSubForTex[0] = diffChan; + ntx = 1; + forceW = bmi[0]->bmiHeader.biWidth; + forceH = bmi[0]->bmiHeader.biHeight; + } + } + if (tx[1]) { + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[ntx], tx[1]); + if (nsupport>ntx) { + bmi[1] = tx[1]->GetVPDisplayDIB(t,cb,valid,TRUE); + if (bmi[1]) { + texHandleValid &= valid; + StuffAlpha(bmi[1], (*maps)[opacChan].amount, GetOpacity(t),ntx?whiteCol:pShader->GetDiffuseClr(t)); + texHandle[ntx] = cb.MakeHandle(bmi[1]); + bmi[1] = NULL; + mtl->texture[ntx].textHandle = texHandle[ntx]->GetHandle(); + SetTexOps(mtl,ntx,TXOP_OPACITY); + useSubForTex[ntx] = opacChan; + ntx++; + } + } + else { + if (!needDecal) { + TextureInfo ti; +// if (SameUV(mtl->texture[0],mtl->texture[1])) { + // Not really correct to combine channels for different UV's but what the heck. + bmi[1] = tx[1]->GetVPDisplayDIB(t,cb,valid,TRUE, forceW, forceH); + if (bmi[1]) { + texHandleValid &= valid; + StuffAlphaInto(bmi[1], bmi[0], (*maps)[opacChan].amount, GetOpacity(t)); + op = TXOP_OPACITY; + free(bmi[1]); + bmi[1] = NULL; + } +// } + } + } + } + if (bmi[0]) { + texHandle[0] = cb.MakeHandle(bmi[0]); + bmi[0] = NULL; + mtl->texture[0].textHandle = texHandle[0]->GetHandle(); + SetTexOps(mtl,0,op); + } + mtl->texture.SetCount(ntx); + numTexHandlesUsed = ntx; +#endif +} + +/*===========================================================================*\ + | Actual shading takes place +\*===========================================================================*/ + +void plDecalMtl::Shade(ShadeContext& sc) +{ + // Get the background color + Color backColor, backTrans; + sc.GetBGColor(backColor, backTrans); + + ShadeWithBackground(sc, backColor); +} + +//// Requirements //////////////////////////////////////////////////////////// +// Tells MAX what we need to render ourselves properly, such as translucency, +// two-sidedness, etc. Flags are in imtl.h in the MAX SDK. + +ULONG plDecalMtl::Requirements( int subMtlNum ) +{ + ULONG req = 0; + + + req = Mtl::Requirements( subMtlNum ); + + // Uncomment this to get the background color fed to our ShadeWithBackground() + // (slower processing tho) +// req |= MTLREQ_BGCOL; + req |= MTLREQ_UV; + + int blendType = fLayersPB->GetInt( kDecalLayOutputBlend ); + if( blendType == kBlendAdd ) + req |= MTLREQ_ADDITIVE_TRANSP | MTLREQ_TRANSP; + else if( blendType == kBlendAlpha ) + req |= MTLREQ_TRANSP; + else if( fBasicPB->GetInt( kDecalBasOpacity, 0 ) != 100 ) + req |= MTLREQ_TRANSP; + + if( fAdvPB->GetInt( kPBAdvTwoSided ) ) + req |= MTLREQ_2SIDE; + + return req; +} + +void plDecalMtl::ShadeWithBackground(ShadeContext &sc, Color background) +{ +#if 1 + + // old +#if 0 + Color lightCol,rescol, diffIllum0; + RGBA mval; + Point3 N0,P; + BOOL bumped = FALSE; + int i; + + if (gbufID) + sc.SetGBufferID(gbufID); + + if (sc.mode == SCMODE_SHADOW) { + float opac = 0.0; + for (i=0; i < NumSubTexmaps(); i++) { + if (SubTexmapOn(i)) { + hsMaxLayerBase *hsmLay = (hsMaxLayerBase *)GetSubTexmap(i); + opac += hsmLay->GetOpacity(t); + } + } + + float f = 1.0f - opac; + sc.out.t = Color(f,f,f); + return; + } + + N0 = sc.Normal(); + P = sc.P(); +#endif + + TimeValue t = sc.CurTime(); + Color color(0, 0, 0); + float alpha = 0.0; + + // Evaluate Base layer + Texmap *map = fLayersPB->GetTexmap(kDecalLayBase); + if (map && ( map->ClassID() == LAYER_TEX_CLASS_ID + || map->ClassID() == STATIC_ENV_LAYER_CLASS_ID ) ) + { + plLayerTex *layer = (plLayerTex*)map; + AColor evalColor = layer->EvalColor(sc); + + color = evalColor; + alpha = evalColor.a; + } + + // Evaluate Top layer, if it's on + if (fLayersPB->GetInt(kDecalLayTopOn)) + { + Texmap *map = fLayersPB->GetTexmap(kDecalLayTop); + if (map && ( map->ClassID() == LAYER_TEX_CLASS_ID + || map->ClassID() == STATIC_ENV_LAYER_CLASS_ID ) ) + { + plLayerTex *layer = (plLayerTex*)map; + AColor evalColor = layer->EvalColor(sc); + + // Blend layers + int blendType = fLayersPB->GetInt(kDecalLayBlend); + switch (blendType) + { + case kBlendAdd: + color += evalColor * evalColor.a; + break; + case kBlendAlpha: + color = (1.0f - evalColor.a) * color + evalColor.a * evalColor; + alpha = 1 - (1 - evalColor.a) * (1 - alpha); + break; + case kBlendMult: + color *= evalColor; + break; + default: // No blend... + color = evalColor; + alpha = 1.0; + break; + } + } + } + +#if 1 + AColor black; + black.Black(); + AColor white; + white.White(); + + + SIllumParams ip; + if (fBasicPB->GetInt(kDecalBasEmissive)) + { + // Emissive objects don't get shaded + ip.diffIllum = fBasicPB->GetColor(kDecalBasColorAmb, t) * color; + ip.diffIllum.ClampMinMax(); + ip.specIllum = black; + } + else + { + // + // Shading setup + // + + // Setup the parameters for the shader + ip.amb = fBasicPB->GetColor(kDecalBasColorAmb, t); + ip.diff = fBasicPB->GetColor(kDecalBasColor, t) * color; + ip.diffIllum = black; + ip.specIllum = black; + ip.N = sc.Normal(); + ip.V = sc.V(); + + + // + // Specularity + // + if (fBasicPB->GetInt(kDecalBasUseSpec, t)) + { + ip.sh_str = 1.f; + ip.spec = fBasicPB->GetColor( kDecalBasSpecColor, t ); + ip.ph_exp = (float)pow(2.0f,float(fBasicPB->GetInt(kDecalBasShine, t)) / 10.0f); + ip.shine = float(fBasicPB->GetInt(kDecalBasShine, t)) / 100.0f; + } + else + { + ip.sh_str = 0; + ip.ph_exp = 0; + ip.shine = 0; + ip.spec = black; + } + ip.softThresh = 0; + + // + + // Do the shading + Shader *myShader = GetShader(SHADER_BLINN); + myShader->Illum(sc, ip); + + // Override shader parameters + if (fAdvPB->GetInt(kPBAdvNoShade)) + { + ip.diffIllum = black; + ip.specIllum = black; + } + if (fAdvPB->GetInt(kPBAdvWhite)) + { + ip.diffIllum = white; + ip.specIllum = black; + } + + ip.diffIllum.ClampMinMax(); + ip.specIllum.ClampMinMax(); + ip.diffIllum = ip.amb * sc.ambientLight + ip.diff * ip.diffIllum; + } + +// AColor returnColor = AColor(opac * ip.diffIllum + ip.specIllum, opac) +#endif + + // Get opacity and combine with alpha + float opac = float(fBasicPB->GetInt(kDecalBasOpacity, t)) / 100.0f; + alpha *= opac; + + // MAX will do the additive/alpha/no blending for us based on what Requirements() + // we tell it. However, since MAX's formula is bgnd*sc.out.t + sc.out.c, + // we have to multiply our output color by the alpha. + // If we ever need a more complicated blending function, you can request the + // background color via Requirements() (otherwise it's just black) and then do + // the blending yourself; however, if the transparency isn't set, the shadows + // will be opaque, so be careful. + Color outC = ip.diffIllum + ip.specIllum; + + sc.out.c = ( outC * alpha ); + sc.out.t = Color( 1.f - alpha, 1.f - alpha, 1.f - alpha ); + +#endif +} + +float plDecalMtl::EvalDisplacement(ShadeContext& sc) +{ + return 0.0f; +} + +Interval plDecalMtl::DisplacementValidity(TimeValue t) +{ + Interval iv; + iv.SetInfinite(); + + return iv; +} + +bool plDecalMtl::HasAlpha() +{ + return ((plLayerTex *)fLayersPB->GetTexmap(kDecalLayBase))->HasAlpha(); +} + +// Massive list of inherited accessor functions for ParamBlock data + +// Advanced Block +int plDecalMtl::GetBasicWire() { return fAdvPB->GetInt(kPBAdvWire); } +int plDecalMtl::GetMeshOutlines() { return fAdvPB->GetInt(kPBAdvMeshOutlines); } +int plDecalMtl::GetTwoSided() { return fAdvPB->GetInt(kPBAdvTwoSided); } +int plDecalMtl::GetSoftShadow() { return fAdvPB->GetInt(kPBAdvSoftShadow); } +int plDecalMtl::GetNoProj() { return fAdvPB->GetInt(kPBAdvNoProj); } +int plDecalMtl::GetVertexShade() { return fAdvPB->GetInt(kPBAdvVertexShade); } +int plDecalMtl::GetNoShade() { return fAdvPB->GetInt(kPBAdvNoShade); } +int plDecalMtl::GetNoFog() { return fAdvPB->GetInt(kPBAdvNoFog); } +int plDecalMtl::GetWhite() { return fAdvPB->GetInt(kPBAdvWhite); } +int plDecalMtl::GetZOnly() { return fAdvPB->GetInt(kPBAdvZOnly); } +int plDecalMtl::GetZClear() { return fAdvPB->GetInt(kPBAdvZClear); } +int plDecalMtl::GetZNoRead() { return fAdvPB->GetInt(kPBAdvZNoRead); } +int plDecalMtl::GetZNoWrite() { return fAdvPB->GetInt(kPBAdvZNoWrite); } +int plDecalMtl::GetZInc() { return fAdvPB->GetInt(kPBAdvZInc); } +int plDecalMtl::GetAlphaTestHigh() { return fAdvPB->GetInt(kPBAdvAlphaTestHigh); } + +// Animation block +char * plDecalMtl::GetAnimName() { return fAnimPB->GetStr(kPBAnimName); } +int plDecalMtl::GetAutoStart() { return fAnimPB->GetInt(kPBAnimAutoStart); } +int plDecalMtl::GetLoop() { return fAnimPB->GetInt(kPBAnimLoop); } +char * plDecalMtl::GetAnimLoopName() { return fAnimPB->GetStr(kPBAnimLoopName); } +int plDecalMtl::GetEaseInType() { return fAnimPB->GetInt(kPBAnimEaseInType); } +float plDecalMtl::GetEaseInNormLength() { return fAnimPB->GetFloat(kPBAnimEaseInLength); } +float plDecalMtl::GetEaseInMinLength() { return fAnimPB->GetFloat(kPBAnimEaseInMin); } +float plDecalMtl::GetEaseInMaxLength() { return fAnimPB->GetFloat(kPBAnimEaseInMax); } +int plDecalMtl::GetEaseOutType() { return fAnimPB->GetInt(kPBAnimEaseOutType); } +float plDecalMtl::GetEaseOutNormLength() { return fAnimPB->GetFloat(kPBAnimEaseOutLength); } +float plDecalMtl::GetEaseOutMinLength() { return fAnimPB->GetFloat(kPBAnimEaseOutMin); } +float plDecalMtl::GetEaseOutMaxLength() { return fAnimPB->GetFloat(kPBAnimEaseOutMax); } +int plDecalMtl::GetUseGlobal() { return fAnimPB->GetInt(ParamID(kPBAnimUseGlobal)); } +char * plDecalMtl::GetGlobalVarName() { return fAnimPB->GetStr(ParamID(kPBAnimGlobalName)); } + +// Basic block +int plDecalMtl::GetColorLock() { return fBasicPB->GetInt(kDecalBasColorLock); } +Color plDecalMtl::GetAmbColor() { return fBasicPB->GetColor(kDecalBasColorAmb); } +Color plDecalMtl::GetColor() { return fBasicPB->GetColor(kDecalBasColor); } +int plDecalMtl::GetOpacity() { return fBasicPB->GetInt(kDecalBasOpacity); } +int plDecalMtl::GetEmissive() { return fBasicPB->GetInt(kDecalBasEmissive); } +int plDecalMtl::GetUseSpec() { return fBasicPB->GetInt(kDecalBasUseSpec); } +int plDecalMtl::GetShine() { return fBasicPB->GetInt(kDecalBasShine); } +Color plDecalMtl::GetSpecularColor() { return fBasicPB->GetColor(kDecalBasSpecColor); } +Control *plDecalMtl::GetPreshadeColorController() { return fBasicPB->GetController(ParamID(kDecalBasColor)); } +Control *plDecalMtl::GetAmbColorController() { return fBasicPB->GetController(ParamID(kDecalBasColorAmb)); } +Control *plDecalMtl::GetOpacityController() { return fBasicPB->GetController(ParamID(kDecalBasOpacity)); } +Control *plDecalMtl::GetSpecularColorController() { return fBasicPB->GetController(ParamID(kDecalBasSpecColor)); } +int plDecalMtl::GetDiffuseColorLock() { return fBasicPB->GetInt(kDecalBasDiffuseLock); } +Color plDecalMtl::GetRuntimeColor() { return fBasicPB->GetColor(kDecalBasRunColor); } +Control *plDecalMtl::GetRuntimeColorController() { return fBasicPB->GetController(ParamID(kDecalBasRunColor)); } + +// Layer block +Texmap *plDecalMtl::GetBaseLayer() { return fLayersPB->GetTexmap(kDecalLayBase); } +int plDecalMtl::GetTopLayerOn() { return fLayersPB->GetInt(kDecalLayTopOn); } +Texmap *plDecalMtl::GetTopLayer() { return fLayersPB->GetTexmap(kDecalLayTop); } +int plDecalMtl::GetLayerBlend() { return fLayersPB->GetInt(kDecalLayBlend); } +int plDecalMtl::GetOutputAlpha() { return fLayersPB->GetInt(kDecalLayOutputAlpha); } +int plDecalMtl::GetOutputBlend() { return fLayersPB->GetInt(kDecalLayOutputBlend); } + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtl.h new file mode 100644 index 00000000..9e9a2b81 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtl.h @@ -0,0 +1,193 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_DECALMTL_H +#define PL_DECALMTL_H + +#include "Max.h" +#include "iparamb2.h" + +#include "../resource.h" +#include "plPassMtlBase.h" + +#define DECAL_MTL_CLASS_ID Class_ID(0x691d2257, 0x419d629e) + +extern TCHAR *GetString(int id); + +class plDecalMtl : public plPassMtlBase +{ +protected: + + virtual void ICloneRefs( plPassMtlBase *target, RemapDir &remap ); + +public: + + enum + { + kRefBasic, + kRefLayers, + kRefAdv, + kRefAnim, + }; + + enum Blocks + { + kBlkBasic, + kBlkLayers, + kBlkAdv, + kBlkAnim, + }; + + plDecalMtl(BOOL loading); + void DeleteThis() { delete this; } + + //From Animatable + Class_ID ClassID() { return DECAL_MTL_CLASS_ID; } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + void GetClassName(TSTR& s) { s = GetString(IDS_DECAL_MTL); } + + ParamDlg *CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + void Update(TimeValue t, Interval& valid); + Interval Validity(TimeValue t); + + void NotifyChanged(); + + BOOL SupportsMultiMapsInViewport() { return FALSE; } + void SetupGfxMultiMaps(TimeValue t, Material *mtl, MtlMakerCallback &cb); + + // Shade and displacement calculation + void Shade(ShadeContext& sc); + void ShadeWithBackground(ShadeContext &sc, Color background); + float EvalDisplacement(ShadeContext& sc); + Interval DisplacementValidity(TimeValue t); + + virtual RefTargetHandle GetReference( int i ); + virtual void SetReference( int i, RefTargetHandle rtarg ); + + // SubTexmap access methods + int NumSubTexmaps(); + Texmap* GetSubTexmap(int i); + void SetSubTexmap(int i, Texmap *m); + TSTR GetSubTexmapSlotName(int i); + TSTR GetSubTexmapTVName(int i); + int SubTexmapOn(int i); + + BOOL SetDlgThing(ParamDlg* dlg); + + RefTargetHandle Clone( RemapDir &remap ); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + int NumParamBlocks(); + IParamBlock2* GetParamBlock(int i); + IParamBlock2* GetParamBlockByID(BlockID id); + + +// void SetParamDlg(ParamDlg *dlg); + +// void SetNumSubTexmaps(int num); + + // From MtlBase and Mtl + void SetAmbient(Color c, TimeValue t); + void SetDiffuse(Color c, TimeValue t); + void SetSpecular(Color c, TimeValue t); + void SetShininess(float v, TimeValue t); + Color GetAmbient(int mtlNum=0, BOOL backFace=FALSE); + Color GetDiffuse(int mtlNum=0, BOOL backFace=FALSE); + Color GetSpecular(int mtlNum=0, BOOL backFace=FALSE); + float GetXParency(int mtlNum=0, BOOL backFace=FALSE); + float GetShininess(int mtlNum=0, BOOL backFace=FALSE); + float GetShinStr(int mtlNum=0, BOOL backFace=FALSE); + float WireSize(int mtlNum=0, BOOL backFace=FALSE); + + ULONG Requirements( int subMtlNum ); + + virtual bool HasAlpha(); + // Massive list of inherited accessor functions for ParamBlock data + + // Advanced Block + virtual int GetBasicWire(); + virtual int GetMeshOutlines(); + virtual int GetTwoSided(); + virtual int GetSoftShadow(); + virtual int GetNoProj(); + virtual int GetVertexShade(); + virtual int GetNoShade(); + virtual int GetNoFog(); + virtual int GetWhite(); + virtual int GetZOnly(); + virtual int GetZClear(); + virtual int GetZNoRead(); + virtual int GetZNoWrite(); + virtual int GetZInc(); + virtual int GetAlphaTestHigh(); + + // Animation block + virtual char * GetAnimName(); + virtual int GetAutoStart(); + virtual int GetLoop(); + virtual char * GetAnimLoopName(); + virtual int GetEaseInType(); + virtual float GetEaseInMinLength(); + virtual float GetEaseInMaxLength(); + virtual float GetEaseInNormLength(); + virtual int GetEaseOutType(); + virtual float GetEaseOutMinLength(); + virtual float GetEaseOutMaxLength(); + virtual float GetEaseOutNormLength(); + virtual int GetUseGlobal(); + virtual char * GetGlobalVarName(); + + // Basic block + virtual int GetColorLock(); + virtual Color GetAmbColor(); + virtual Color GetColor(); + virtual int GetOpacity(); + virtual int GetEmissive(); + virtual int GetUseSpec(); + virtual int GetShine(); + virtual Color GetSpecularColor(); + virtual Control *GetPreshadeColorController(); + virtual Control *GetAmbColorController(); + virtual Control *GetOpacityController(); + virtual Control *GetSpecularColorController(); + virtual int GetDiffuseColorLock(); + virtual Color GetRuntimeColor(); + virtual Control *GetRuntimeColorController(); + + // Layer block + virtual Texmap *GetBaseLayer(); + virtual int GetTopLayerOn(); + virtual Texmap *GetTopLayer(); + virtual int GetLayerBlend(); + virtual int GetOutputAlpha(); + virtual int GetOutputBlend(); +}; + +#endif //PL_DECALMTL_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAdvPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAdvPB.h new file mode 100644 index 00000000..51e17de9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAdvPB.h @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_DECALMTLADVPB_H +#define PL_DECALMTLADVPB_H + +// Param ID's +enum +{ + // Specular + kDecalAdvUseSpec, // Not used anymore, feel free to replace with new fields as necessary + kDecalAdvSpecType, // ditto + kDecalAdvShine, // ... + kDecalAdvShineStr, // ... + + // Misc + kDecalAdvWire, + kDecalAdvMeshOutlines, + kDecalAdvTwoSided, + + // Shading + kDecalAdvSoftShadow, + kDecalAdvNoProj, + kDecalAdvVertexShade, + kDecalAdvNoShade, + kDecalAdvNoFog, + kDecalAdvWhite, + + // Z + kDecalAdvZOnly, + kDecalAdvZClear, + kDecalAdvZNoRead, + kDecalAdvZNoWrite, + kDecalAdvZInc, +}; + +// Specular types +enum +{ + kSpecTypeAlpha, + kSpecTypeColor, + kSpecTypeHighlight +}; + +#endif //PL_DECALMTLADVPB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAdvPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAdvPBDec.h new file mode 100644 index 00000000..1aa0a42a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAdvPBDec.h @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plDecalMtl.h" +#include "plPassBaseParamIDs.h" + +using namespace plPassBaseParamIDs; + +static ParamBlockDesc2 gDecalAdvPB +( + plDecalMtl::kBlkAdv, _T("advanced"), IDS_PASS_ADV, GetDecalMtlDesc(), + P_AUTO_CONSTRUCT + P_AUTO_UI, plDecalMtl::kRefAdv, + + // UI + IDD_PASS_ADV, IDS_PASS_ADV, 0, APPENDROLL_CLOSED, NULL, + + // Misc Properties + kPBAdvWire, _T("basicWire"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_MISC_WIRE, + end, + kPBAdvMeshOutlines, _T("meshOutlines"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_MISC_MESHOUTLINES, + end, + kPBAdvTwoSided, _T("twoSided"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_MISC_TWOSIDED, + end, + + // Shade properties + kPBAdvSoftShadow, _T("softShadow"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_SOFTSHADOW, + end, + kPBAdvNoProj, _T("noProj"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_NO_PROJ, + end, + kPBAdvVertexShade, _T("vertexShade"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_VERTEXSHADE, + end, + kPBAdvNoShade, _T("noShade"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_NOSHADE, + end, + kPBAdvNoFog, _T("noFog"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_NO_FOG, + end, + kPBAdvWhite, _T("white"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_WHITE, + end, + + // Z Properties + kPBAdvZOnly, _T("zOnly"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_Z_ZONLY, + end, + kPBAdvZClear, _T("zClear"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_Z_ZCLEAR, + end, + kPBAdvZNoRead, _T("zNoRead"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_Z_ZNOREAD, + end, + kPBAdvZNoWrite, _T("zNoWrite"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_Z_ZNOWRITE, + p_default, TRUE, + end, + kPBAdvZInc, _T("zInc"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_Z_INC, + p_default, TRUE, + end, + + kPBAdvAlphaTestHigh, _T("aTestHigh"), TYPE_BOOL, 0, 0, + p_default, FALSE, + end, + + end +); +ParamBlockDesc2 *GetDecalAdvPB() { return &gDecalAdvPB; } \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAnimPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAnimPB.h new file mode 100644 index 00000000..2fc0eed3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAnimPB.h @@ -0,0 +1,47 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_DECALMTLANIMPB_H +#define PL_DECALMTLANIMPB_H + +enum +{ + kDecalAnimName, + kDecalAnimAutoStart, + kDecalAnimLoop, + kDecalAnimLoopName, + kDecalEaseInType, // Not used, but reserved + kDecalEaseOutType, // + kDecalEaseInLength, // + kDecalEaseOutLength, // + kDecalEaseInMin, // + kDecalEaseInMax, // + kDecalEaseOutMin, // + kDecalEaseOutMax, // + kDecalAnimUseGlobal, // + kDecalAnimGlobalName, // +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAnimPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAnimPBDec.h new file mode 100644 index 00000000..d2598c7d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlAnimPBDec.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plDecalMtl.h" +#include "plPassBaseParamIDs.h" +#include "resource.h" +#include "iparamm2.h" + +#include "plPassAnimDlgProc.h" +#include "plAnimStealthNode.h" + +using namespace plPassBaseParamIDs; + +static ParamBlockDesc2 gDecalAnimPB +( + plDecalMtl::kBlkAnim, _T("anim"), IDS_PASS_ANIM, GetDecalMtlDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI + P_CALLSETS_ON_LOAD, plDecalMtl::kRefAnim, + + // UI + IDD_PASS_ANIM, IDS_PASS_ANIM, 0, 0, &plPassAnimDlgProc::Get(), + +#ifdef MCN_UPGRADE_OLD_ANIM_BLOCKS + // THE FOLLOWING ARE ALL OLD PARAMETERS AND SHOULD NO LONGER BE USED. The only reason + // they're here is so we can convert old paramBlocks into the new plAnimStealthNode format + kPBAnimName, _T("animName"), TYPE_STRING, 0, 0, + end, + + kPBAnimAutoStart, _T("autoStart"), TYPE_BOOL, 0, 0, + end, + + kPBAnimLoop, _T("loop"), TYPE_BOOL, 0, 0, + end, + kPBAnimLoopName, _T("loopName"), TYPE_STRING, 0, 0, + end, + + // Anim Ease + kPBAnimEaseInType, _T("easeInType"), TYPE_INT, 0, 0, + end, + kPBAnimEaseInLength, _T("easeInLength"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseInMin, _T("easeInMin"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseInMax, _T("easeInMax"), TYPE_FLOAT, 0, 0, + end, + + kPBAnimEaseOutType, _T("easeOutType"), TYPE_INT, 0, 0, + end, + kPBAnimEaseOutLength, _T("easeOutLength"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseOutMin, _T("easeOutMin"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseOutMax, _T("easeOutMax"), TYPE_FLOAT, 0, 0, + end, + +#endif // MCN_UPGRADE_OLD_ANIM_BLOCKS + + kPBAnimUseGlobal, _T("UseGlobal"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_MTL_USE_GLOBAL, + end, + kPBAnimGlobalName, _T("GlobalName"), TYPE_STRING, 0, 0, + p_default, _T(""), + end, + + kPBAnimStealthNodes, _T( "testing" ), TYPE_REFTARG_TAB, 0, 0, 0, + p_accessor, &plStealthNodeAccessor::GetInstance(), + end, + + end +); +ParamBlockDesc2 *GetDecalAnimPB() { return &gDecalAnimPB; } diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlBasicPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlBasicPB.h new file mode 100644 index 00000000..c53601d2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlBasicPB.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_DECALMTLBASICPB_H +#define PL_DECALMTLBASICPB_H + +// Param ID's +enum +{ + kDecalBasColorLock, + kDecalBasColorAmb, + kDecalBasColor, + + kDecalBasOpacity, + + kDecalBasEmissive, + + // Specular + kDecalBasUseSpec, + kDecalBasShine, + kDecalBasShineStr, + + // New color stuff + kDecalBasDiffuseLock, + kDecalBasRunColor, + kDecalBasSpecColor + +}; + +#endif //PL_DECALMTLBASICPB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlBasicPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlBasicPBDec.h new file mode 100644 index 00000000..7128c681 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlBasicPBDec.h @@ -0,0 +1,236 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plDecalMtl.h" +#include "plDecalMtlBasicPB.h" +#include "resource.h" +#include "iparamm2.h" + +class DecalBasicPBAccessor; +extern DecalBasicPBAccessor basicAccessor; + +class DecalBasicDlgProc; +extern DecalBasicDlgProc gDecalBasicDlgProc; + +static ParamBlockDesc2 gDecalBasicPB +( + plDecalMtl::kBlkBasic, _T("basic"), IDS_PASS_BASIC, GetDecalMtlDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI, plDecalMtl::kRefBasic, + + // UI + IDD_PASS_BASIC, IDS_PASS_BASIC, 0, 0, &gDecalBasicDlgProc, + + // Color + kDecalBasColorLock, _T("colorLock"), TYPE_BOOL, 0, 0, + p_ui, TYPE_CHECKBUTTON, IDC_LOCK_AD, + p_accessor, &basicAccessor, + end, + kDecalBasColorAmb, _T("ambColor"), TYPE_RGBA, P_ANIMATABLE, IDS_BASIC_AMB, + p_ui, TYPE_COLORSWATCH, IDC_LAYER_COLOR_AMB, + p_accessor, &basicAccessor, + end, + kDecalBasColor, _T("color"), TYPE_RGBA, P_ANIMATABLE, IDS_BASIC_COLOR, + p_ui, TYPE_COLORSWATCH, IDC_LAYER_COLOR, + p_default, Color(1,1,1), + p_accessor, &basicAccessor, + end, + + kDecalBasRunColor, _T("runtimeColor"), TYPE_RGBA, P_ANIMATABLE, IDS_BASIC_RUNCOLOR, + p_ui, TYPE_COLORSWATCH, IDC_LAYER_RUNCOLOR, + p_default, Color(1,1,1), + p_accessor, &basicAccessor, + end, + kDecalBasDiffuseLock, _T("diffuseLock"), TYPE_BOOL, 0, 0, + p_ui, TYPE_CHECKBUTTON, IDC_LOCK_COLORS, + p_accessor, &basicAccessor, + p_default, TRUE, + end, + + // Opacity + kDecalBasOpacity, _T("opacity"), TYPE_INT, P_ANIMATABLE, IDS_BASIC_OPAC, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_TR_EDIT, IDC_TR_SPIN, 0.4, + p_range, 0, 100, + p_default, 100, + end, + + kDecalBasEmissive, _T("emissive"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_LAYER_EMISSIVE_CB, + end, + + // Specularity + kDecalBasUseSpec, _T("useSpec"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_SPECULAR, + p_enable_ctrls, 2, kDecalBasShine, kDecalBasShineStr, + end, + kDecalBasShine, _T("shine"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_SH_EDIT, IDC_SH_SPIN, 0.4, + p_range, 0, 100, + end, + kDecalBasSpecColor, _T("specularColor"), TYPE_RGBA, P_ANIMATABLE, IDS_BASIC_SPECCOLOR, + p_ui, TYPE_COLORSWATCH, IDC_LAYER_SPECCOLOR, + p_default, Color(0,0,0), + end, + + // OBSOLETE--here so we can upgrade it to color if necessary + kDecalBasShineStr, _T("shineStr"), TYPE_INT, 0, 0, + p_range, -1, 100, + p_default, -1, + end, + + end +); +ParamBlockDesc2 *GetDecalBasicPB() { return &gDecalBasicPB; } + +class DecalBasicPBAccessor : public PBAccessor +{ + bool fColorLocked; + +public: + DecalBasicPBAccessor() : fColorLocked( false ) {} + + void Set(PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + plDecalMtl* mtl = (plDecalMtl*)owner; + IParamBlock2 *pb = mtl->GetParamBlockByID(plDecalMtl::kBlkBasic); + + switch (id) + { + case kDecalBasColorLock: + if (val.i) + pb->SetValue(kDecalBasColor, t, pb->GetColor(kDecalBasColorAmb, t)); + break; + + case kDecalBasDiffuseLock: + if (val.i) + pb->SetValue(kDecalBasRunColor, t, pb->GetColor(kDecalBasColor, t)); + break; + + case kDecalBasColor: + case kDecalBasColorAmb: + case kDecalBasRunColor: + ISyncLockedColors( id, pb, val, t ); + break; + } + } + void Get(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid) + { + } + + void ISyncLockedColors( ParamID settingID, IParamBlock2 *pb, PB2Value &val, TimeValue t ) + { + int i, numToSet = 0; + ParamID toSet[ 2 ]; + + + if( fColorLocked ) + return; + fColorLocked = true; + + if( settingID == kDecalBasColorAmb && pb->GetInt( kDecalBasColorLock, t ) ) + { + toSet[ numToSet++ ] = kDecalBasColor; + if( pb->GetInt( kDecalBasDiffuseLock, t ) ) + toSet[ numToSet++ ] = kDecalBasRunColor; + } + else if( settingID == kDecalBasRunColor && pb->GetInt( kDecalBasDiffuseLock, t ) ) + { + toSet[ numToSet++ ] = kDecalBasColor; + if( pb->GetInt( kDecalBasColorLock, t ) ) + toSet[ numToSet++ ] = kDecalBasColorAmb; + } + else if( settingID == kDecalBasColor ) + { + if( pb->GetInt( kDecalBasColorLock, t ) ) + toSet[ numToSet++ ] = kDecalBasColorAmb; + if( pb->GetInt( kDecalBasDiffuseLock, t ) ) + toSet[ numToSet++ ] = kDecalBasRunColor; + } + + for( i = 0; i < numToSet; i++ ) + { + pb->SetValue( toSet[ i ], t, *val.p ); + pb->GetMap()->Invalidate( toSet[ i ] ); + } + + fColorLocked = false; + } +}; +static DecalBasicPBAccessor basicAccessor; + +class DecalBasicDlgProc : public ParamMap2UserDlgProc +{ +#if 1 +protected: + HIMAGELIST hLockButtons; + + void LoadLockButtons() + { + static bool loaded = false; + if (loaded) + return; + loaded = true; + + HINSTANCE hInst = hInstance; + hLockButtons = ImageList_Create(16, 15, TRUE, 2, 0); + HBITMAP hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BUTTONS)); + HBITMAP hMask = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_MASKBUTTONS)); + ImageList_Add(hLockButtons, hBitmap, hMask); + DeleteObject(hBitmap); + DeleteObject(hMask); + } + void ISetLock(HWND hButton) + { + LoadLockButtons(); + + ICustButton *iBut = GetICustButton(hButton); + iBut->SetImage(hLockButtons,0,1,0,1,16,15); + iBut->SetType(CBT_CHECK); + ReleaseICustButton(iBut); + } + +public: + DecalBasicDlgProc() : hLockButtons(NULL) {} + ~DecalBasicDlgProc() { if (hLockButtons) ImageList_Destroy(hLockButtons); } +#endif + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IParamBlock2 *pb = map->GetParamBlock(); + + switch (msg) + { + case WM_INITDIALOG: + { + ISetLock(GetDlgItem(hWnd, IDC_LOCK_AD)); + ISetLock(GetDlgItem(hWnd, IDC_LOCK_COLORS)); + } + return TRUE; + } + return FALSE; + } + void DeleteThis() {} +}; +static DecalBasicDlgProc gDecalBasicDlgProc; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlLayersPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlLayersPB.h new file mode 100644 index 00000000..818b63ae --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlLayersPB.h @@ -0,0 +1,40 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_DECALMTLLAYERSPB_H +#define PL_DECALMTLLAYERSPB_H + +enum +{ + // Layers + kDecalLayBase, + kDecalLayOutputBlend, + kDecalLayTopOn, + kDecalLayTop, + kDecalLayBlend, + kDecalLayOutputAlpha, +}; + +#endif //PL_DECALMTLLAYERSPB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlLayersPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlLayersPBDec.h new file mode 100644 index 00000000..e2e1f6cc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plDecalMtlLayersPBDec.h @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plDecalMtl.h" +#include "plDecalMtlLayersPB.h" +#include "resource.h" + +#include "iparamm2.h" + +#include "Layers/plLayerTex.h" + +class DecalMtlLayersAccessor; +extern DecalMtlLayersAccessor gLayersAccessor; + +class LayersDlgProc; +extern LayersDlgProc gLayersDlgProc; + +static ParamBlockDesc2 gDecalMtlLayersPB +( + plDecalMtl::kBlkLayers, _T("layers"), IDS_PASS_LAYERS, GetDecalMtlDesc(), + P_AUTO_CONSTRUCT + P_AUTO_UI, plDecalMtl::kRefLayers, + + // UI + IDD_PASS_LAYERS, IDS_PASS_LAYERS, 0, 0, NULL, + + kDecalLayBase, _T("baseLayer"), TYPE_TEXMAP, 0, IDS_BASIC_AMB, + p_ui, TYPE_TEXMAPBUTTON, IDC_LAYER1, + p_subtexno, 0, + end, + + kDecalLayOutputBlend, _T("outputBlend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_OUTPUTB_NONE, IDC_OUTPUTB_ALPHA, IDC_OUTPUTB_ADD, + p_vals, plPassMtlBase::kBlendNone, plPassMtlBase::kBlendAlpha, plPassMtlBase::kBlendAdd, + p_default, plPassMtlBase::kBlendNone, + end, + + kDecalLayTopOn, _T("topLayerOn"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_TOP_ON, + p_default, FALSE, + p_enable_ctrls, 3, kDecalLayTop, kDecalLayBlend, kDecalLayOutputAlpha, + end, + kDecalLayTop, _T("topLayer"), TYPE_TEXMAP, 0, 0, + p_ui, TYPE_TEXMAPBUTTON, IDC_LAYER2, + p_subtexno, 1, + end, + + kDecalLayBlend, _T("layerBlend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_LAYER_ALPHA, IDC_LAYER_ADD, IDC_LAYER_MULTIPLY, + p_vals, plPassMtlBase::kBlendAlpha, plPassMtlBase::kBlendAdd, plPassMtlBase::kBlendMult, + p_default, plPassMtlBase::kBlendAdd, + end, + + kDecalLayOutputAlpha, _T("ouputAlpha"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_OUTPUTA_DISCARD, IDC_OUTPUTA_ADD, IDC_OUTPUTA_MULT, + p_vals, plPassMtlBase::kAlphaDiscard, plPassMtlBase::kAlphaAdd, plPassMtlBase::kAlphaMultiply, + p_default, plPassMtlBase::kAlphaDiscard, + end, + + + end + + + +); +ParamBlockDesc2 *GetDecalLayersPB() { return &gDecalMtlLayersPB; } diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtl.cpp new file mode 100644 index 00000000..145ab24f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtl.cpp @@ -0,0 +1,370 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plMultipassMtl.h" +#include "plPassMtl.h" +#include "plMultipassMtlPB.h" +#include "plMultipassMtlDlg.h" + +class plMultipassClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return TRACKED_NEW plMultipassMtl(loading); } + const TCHAR* ClassName() { return GetString(IDS_MULTI_MTL); } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + Class_ID ClassID() { return MULTIMTL_CLASS_ID; } + const TCHAR* Category() { return NULL; } + const TCHAR* InternalName() { return _T("PlasmaMultipass"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plMultipassClassDesc plMultipassMtlDesc; +ClassDesc2* GetMultiMtlDesc() { return &plMultipassMtlDesc; } + +#include "plMultipassMtlPB.cpp" + +plMultipassMtl::plMultipassMtl(BOOL loading) : fPassesPB(NULL) +{ + plMultipassMtlDesc.MakeAutoParamBlocks(this); + + if (!loading) + Reset(); + + SetNumSubMtls(1); +} + +void plMultipassMtl::Reset() +{ + fIValid.SetEmpty(); +} + +ParamDlg* plMultipassMtl::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + fMtlDlg = TRACKED_NEW plMultipassMtlDlg(hwMtlEdit, imp, this); + + return fMtlDlg; +} + +void plMultipassMtl::SetParamDlg(ParamDlg *dlg) +{ + fMtlDlg = (plMultipassMtlDlg*)dlg; +} + +BOOL plMultipassMtl::SetDlgThing(ParamDlg* dlg) +{ + if (dlg == fMtlDlg) + { + fMtlDlg->SetThing(this); + return TRUE; + } + + return FALSE; +} + +Interval plMultipassMtl::Validity(TimeValue t) +{ + Interval valid = FOREVER; + +/* for (int i = 0; i < fSubTexmap.Count(); i++) + { + if (fSubTexmap[i]) + valid &= fSubTexmap[i]->Validity(t); + } +*/ +// float u; +// fPBlock->GetValue(pb_spin,t,u,valid); + return valid; +} + +/*===========================================================================*\ + | Subanim & References support +\*===========================================================================*/ + +int plMultipassMtl::NumSubs() +{ + return NumSubMtls(); +} + +TSTR plMultipassMtl::SubAnimName(int i) +{ + return GetSubMtlSlotName(i); +} + +Animatable* plMultipassMtl::SubAnim(int i) +{ + return GetSubMtl(i); +} + +int plMultipassMtl::NumRefs() +{ + return 1; +} + +RefTargetHandle plMultipassMtl::GetReference(int i) +{ + if (i == kRefPasses) + return fPassesPB; + + return NULL; +} + +void plMultipassMtl::SetReference(int i, RefTargetHandle rtarg) +{ + if (i == kRefPasses) + fPassesPB = (IParamBlock2 *)rtarg; +} + +int plMultipassMtl::NumParamBlocks() +{ + return 1; +} + +IParamBlock2 *plMultipassMtl::GetParamBlock(int i) +{ + if (i == kRefPasses) + return fPassesPB; + + return NULL; +} + +IParamBlock2 *plMultipassMtl::GetParamBlockByID(BlockID id) +{ + if (fPassesPB->ID() == id) + return fPassesPB; + + return NULL; +} + +RefResult plMultipassMtl::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message ) +{ + switch (message) + { + case REFMSG_CHANGE: + fIValid.SetEmpty(); + if (hTarget == fPassesPB) + { + ParamID changingParam = fPassesPB->LastNotifyParamID(); + fPassesPB->GetDesc()->InvalidateUI(changingParam); + } + break; + } + + return REF_SUCCEED; +} + +//////////////////////////////////////////////////////////////////////////////// +// Subtexmap access + +int plMultipassMtl::NumSubMtls() +{ + return fPassesPB->GetInt(kMultCount); +} + +Mtl *plMultipassMtl::GetSubMtl(int i) +{ + if (i < NumSubMtls()) + return fPassesPB->GetMtl(kMultPasses, 0, i); + + return NULL; +} + +void plMultipassMtl::SetSubMtl(int i, Mtl *m) +{ + if (i < NumSubMtls()) + fPassesPB->SetValue(kMultPasses, 0, m, i); +} + +TSTR plMultipassMtl::GetSubMtlSlotName(int i) +{ + TSTR str; + str.printf("Pass %d", i+1); + return str; +} + +TSTR plMultipassMtl::GetSubMtlTVName(int i) +{ + return GetSubMtlSlotName(i); +} + + +/*===========================================================================*\ + | Standard IO +\*===========================================================================*/ + +#define MTL_HDR_CHUNK 0x4000 + +IOResult plMultipassMtl::Save(ISave *isave) +{ + IOResult res; + isave->BeginChunk(MTL_HDR_CHUNK); + res = MtlBase::Save(isave); + if (res!=IO_OK) return res; + isave->EndChunk(); + + return IO_OK; +} + +IOResult plMultipassMtl::Load(ILoad *iload) +{ + IOResult res; + int id; + while (IO_OK==(res=iload->OpenChunk())) + { + switch(id = iload->CurChunkID()) + { + case MTL_HDR_CHUNK: + res = MtlBase::Load(iload); + break; + } + iload->CloseChunk(); + if (res!=IO_OK) + return res; + } + + return IO_OK; +} + + +/*===========================================================================*\ + | Updating and cloning +\*===========================================================================*/ + +RefTargetHandle plMultipassMtl::Clone(RemapDir &remap) +{ + plMultipassMtl *mnew = TRACKED_NEW plMultipassMtl(FALSE); + *((MtlBase*)mnew) = *((MtlBase*)this); + mnew->ReplaceReference(kRefPasses, remap.CloneRef(fPassesPB)); + + mnew->fIValid.SetEmpty(); + BaseClone(this, mnew, remap); + + return (RefTargetHandle)mnew; +} + +void plMultipassMtl::NotifyChanged() +{ + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +} + +void plMultipassMtl::Update(TimeValue t, Interval& valid) +{ + if (!fIValid.InInterval(t)) + { + fIValid.SetInfinite(); +// fPassesPB->GetValue(kMtlLayLayer1On, t, fMapOn[0], fIValid); + + for (int i = 0; i < NumSubMtls(); i++) + { + if (GetSubMtl(i)) + GetSubMtl(i)->Update(t, fIValid); + } + } + + valid &= fIValid; +} + +/*===========================================================================*\ + | Determine the characteristics of the material +\*===========================================================================*/ +void plMultipassMtl::SetAmbient(Color c, TimeValue t) {} +void plMultipassMtl::SetDiffuse(Color c, TimeValue t) {} +void plMultipassMtl::SetSpecular(Color c, TimeValue t) {} +void plMultipassMtl::SetShininess(float v, TimeValue t) {} + +Color plMultipassMtl::GetAmbient(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plMultipassMtl::GetDiffuse(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plMultipassMtl::GetSpecular(int mtlNum, BOOL backFace) { return Color(0,0,0); } +float plMultipassMtl::GetXParency(int mtlNum, BOOL backFace) { return 0.0f; } +float plMultipassMtl::GetShininess(int mtlNum, BOOL backFace) { return 0.0f; } +float plMultipassMtl::GetShinStr(int mtlNum, BOOL backFace) { return 0.0f; } +float plMultipassMtl::WireSize(int mtlNum, BOOL backFace) { return 0.0f; } + +/*===========================================================================*\ + | Actual shading takes place +\*===========================================================================*/ + +void plMultipassMtl::Shade(ShadeContext& sc) +{ + // Get the background color + Color backColor, backTrans; + backColor.Black(); + backTrans.White(); + + int count = NumSubMtls(); + for (int i = 0; i < count; i++) + { + if (fPassesPB->GetInt(kMultOn, 0, i) == 0) + continue; + + // Call each pass' shade function with the previous color + Mtl *mtl = GetSubMtl(i); + //backTrans = Color(0,0,0); + if (mtl->ClassID() == PASS_MTL_CLASS_ID) + { + plPassMtl *passMtl = (plPassMtl*)mtl; + passMtl->ShadeWithBackground(sc, backColor); + backTrans *= sc.out.t; + backColor = backColor * sc.out.t + sc.out.c; + } + } + + sc.out.t = backTrans; + sc.out.c = backColor; +} + +float plMultipassMtl::EvalDisplacement(ShadeContext& sc) +{ + return 0.0f; +} + +Interval plMultipassMtl::DisplacementValidity(TimeValue t) +{ + Interval iv; + iv.SetInfinite(); + + return iv; +} + +void plMultipassMtl::SetNumSubMtls(int num) +{ + TimeValue t = GetCOREInterface()->GetTime(); + int curNum = fPassesPB->GetInt(kMultCount); + + fPassesPB->SetValue(kMultCount, 0, num); + + fPassesPB->SetCount(kMultPasses, num); + fPassesPB->SetCount(kMultOn, num); + fPassesPB->SetCount(kMultLayerCounts, num); + + for (int i = curNum; i < num; i++) + { + plPassMtl *newMtl = TRACKED_NEW plPassMtl(false); + fPassesPB->SetValue(kMultPasses, t, newMtl, i); + fPassesPB->SetValue(kMultOn, t, TRUE, i); + GetCOREInterface()->AssignNewName(fPassesPB->GetMtl(kMultPasses, t, i)); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtl.h new file mode 100644 index 00000000..b3facc25 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtl.h @@ -0,0 +1,120 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __PLMAXMTL__H +#define __PLMAXMTL__H + +#include "Max.h" +#include "../resource.h" +#include "istdplug.h" +#include "iparamb2.h" +#include "iparamm2.h" + +extern TCHAR *GetString(int id); +extern HINSTANCE hInstance; + +#define MULTIMTL_CLASS_ID Class_ID(0x3f687a, 0x28c62bd7) + +class plMultipassMtlDlg; + +class plMultipassMtl : public Mtl +{ +protected: + IParamBlock2 *fPassesPB; + Interval fIValid; + plMultipassMtlDlg *fMtlDlg; + +public: + enum { kRefPasses }; + enum { kBlkPasses }; + + ParamDlg *CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + void Update(TimeValue t, Interval& valid); + Interval Validity(TimeValue t); + void Reset(); + + void NotifyChanged(); + + // From MtlBase and Mtl + void SetAmbient(Color c, TimeValue t); + void SetDiffuse(Color c, TimeValue t); + void SetSpecular(Color c, TimeValue t); + void SetShininess(float v, TimeValue t); + Color GetAmbient(int mtlNum=0, BOOL backFace=FALSE); + Color GetDiffuse(int mtlNum=0, BOOL backFace=FALSE); + Color GetSpecular(int mtlNum=0, BOOL backFace=FALSE); + float GetXParency(int mtlNum=0, BOOL backFace=FALSE); + float GetShininess(int mtlNum=0, BOOL backFace=FALSE); + float GetShinStr(int mtlNum=0, BOOL backFace=FALSE); + float WireSize(int mtlNum=0, BOOL backFace=FALSE); + + // Shade and displacement calculation + void Shade(ShadeContext& sc); + float EvalDisplacement(ShadeContext& sc); + Interval DisplacementValidity(TimeValue t); + + // SubTexmap access methods + int NumSubMtls(); + Mtl* GetSubMtl(int i); + void SetSubMtl(int i, Mtl *m); + TSTR GetSubMtlSlotName(int i); + TSTR GetSubMtlTVName(int i); + + BOOL SetDlgThing(ParamDlg* dlg); + plMultipassMtl(BOOL loading); + + // Loading/Saving + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + //From Animatable + Class_ID ClassID() { return MULTIMTL_CLASS_ID; } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + void GetClassName(TSTR& s) { s = GetString(IDS_MULTI_MTL); } + + RefTargetHandle Clone(RemapDir &remap); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + + int NumParamBlocks(); + IParamBlock2* GetParamBlock(int i); + IParamBlock2* GetParamBlockByID(BlockID id); + + void DeleteThis() { delete this; } + + void SetParamDlg(ParamDlg *dlg); + + void SetNumSubMtls(int num); +}; + +#endif // __PLMAXMTL__H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlDlg.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlDlg.cpp new file mode 100644 index 00000000..77d75565 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlDlg.cpp @@ -0,0 +1,326 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "max.h" +#include "MaxIcon.h" + +#include "resource.h" +#include "plMultipassMtl.h" +#include "plMultipassMtlPB.h" +//#include "plMaxLayer.h" +#include "plMultipassMtlDlg.h" + +struct LayerID +{ + int layerID; + int activeID; +}; +static LayerID kLayerID[] = +{ + { IDC_TEX1, IDC_TEXON1 }, + { IDC_TEX2, IDC_TEXON2 }, + { IDC_TEX3, IDC_TEXON3 }, + { IDC_TEX4, IDC_TEXON4 }, + { IDC_TEX5, IDC_TEXON5 }, + { IDC_TEX6, IDC_TEXON6 }, + { IDC_TEX7, IDC_TEXON7 }, + { IDC_TEX8, IDC_TEXON8 }, + { IDC_TEX9, IDC_TEXON9 }, + { IDC_TEX10, IDC_TEXON10 }, +}; + +//----------------------------------------------------------------------------- +// Constructor and destructor +//----------------------------------------------------------------------------- + +plMultipassMtlDlg::plMultipassMtlDlg(HWND hwMtlEdit, IMtlParams *imp, plMultipassMtl *m) +{ + fDADMgr.Init(this); + + fhMtlEdit = hwMtlEdit; + fhRollup = NULL; + fMtl = m; + fPBlock = fMtl->GetParamBlockByID(plMultipassMtl::kBlkPasses); + ip = imp; + valid = FALSE; + + for (int i = 0; i < NSUBMTLS; i++) + fLayerBtns[i] = NULL; + + curTime = imp->GetTime(); + + fhRollup = ip->AddRollupPage( + hInstance, + MAKEINTRESOURCE(IDD_MULTIPASS), + ForwardProc, + "Multipass Parameters", + (LPARAM)this); +} + +plMultipassMtlDlg::~plMultipassMtlDlg() +{ + fMtl->SetParamDlg(NULL); + for (int i = 0; i < NSUBMTLS; i++) + { + ReleaseICustButton(fLayerBtns[i]); + fLayerBtns[i] = NULL; + } + + SetWindowLong(fhRollup, GWL_USERDATA, NULL); + ip->DeleteRollupPage(fhRollup); + + fhRollup = NULL; +} + +//----------------------------------------------------------------------------- +// Functions inheirited from ParamDlg +//----------------------------------------------------------------------------- + +void plMultipassMtlDlg::SetThing(ReferenceTarget *m) +{ + assert(m->SuperClassID() == MATERIAL_CLASS_ID); + assert(m->ClassID() == MULTIMTL_CLASS_ID); + + // Bad? + if (fMtl) + fMtl->SetParamDlg(NULL); + fMtl = (plMultipassMtl *)m; + if (fMtl) + fMtl->SetParamDlg(this); + + LoadDialog(); + IUpdateMtlDisplay(); +} + +void plMultipassMtlDlg::SetTime(TimeValue t) +{ + if (t != curTime) + { + curTime = t; + Interval v; + fMtl->Update(ip->GetTime(),v); + LoadDialog(); + IUpdateMtlDisplay(); + } +} + +void plMultipassMtlDlg::ReloadDialog() +{ + Interval v; + fMtl->Update(ip->GetTime(), v); + LoadDialog(); +} + +void plMultipassMtlDlg::ActivateDlg(BOOL onOff) +{ +} + +int plMultipassMtlDlg::FindSubMtlFromHWND(HWND hwnd) +{ + for (int i = 0; i < NSUBMTLS; i++) + { + if (hwnd == fLayerBtns[i]->GetHwnd()) + return i; + } + + return -1; +} + +BOOL plMultipassMtlDlg::ForwardProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + plMultipassMtlDlg *theDlg; + if (msg == WM_INITDIALOG) + { + theDlg = (plMultipassMtlDlg*)lParam; + theDlg->fhRollup = hDlg; + SetWindowLong(hDlg, GWL_USERDATA, lParam); + } + else + { + if ((theDlg = (plMultipassMtlDlg *)GetWindowLong(hDlg, GWL_USERDATA)) == NULL) + return FALSE; + } + + return theDlg->LayerPanelProc(hDlg,msg,wParam,lParam); +} + + + +//---------------------------------------------------------------------------- +// Layer panel processor +//---------------------------------------------------------------------------- +BOOL plMultipassMtlDlg::LayerPanelProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + int id = LOWORD(wParam); + int code = HIWORD(wParam); + int i; + + switch (msg) + { + case WM_INITDIALOG: + { + int nLayers = fPBlock->GetInt(kMultCount); + + for (i = 0; i < NSUBMTLS; i++) + { + fLayerBtns[i] = GetICustButton(GetDlgItem(hDlg, kLayerID[i].layerID)); + fLayerBtns[i]->SetDADMgr(&fDADMgr); + +/* if (i < nLayers) + { + SetCheckBox(hDlg, kLayerID[i].activeID, fPBlock->GetInt(kMtlLayerOn, curTime, i)); + } +*/ + fNumTexSpin = SetupIntSpinner(hDlg, IDC_LAYER_SPIN, IDC_LAYER_EDIT, 1, 10, nLayers); + } + + // TEMP testing + UpdateLayerDisplay(); + + IUpdateMtlDisplay(); + } + return TRUE; + + case WM_DESTROY: + for (i = 0; i < NSUBMTLS; i++) + { + ReleaseICustButton(fLayerBtns[i]); + fLayerBtns[i] = NULL; + } + ReleaseISpinner(fNumTexSpin); + fNumTexSpin = NULL; + break; + + case CC_SPINNER_CHANGE: + if (id == IDC_LAYER_SPIN && !code) + { + IGetSpinnerVal(); + return TRUE; + } + break; + case CC_SPINNER_BUTTONUP: + if (id == IDC_LAYER_SPIN && code) + { + IGetSpinnerVal(); + return TRUE; + } + break; + + case WM_COMMAND: + { + for (i = 0; i < NSUBMTLS; i++) + { + if (id == kLayerID[i].activeID) + { +// fMtl->EnableMap(i,GetCheckBox(hwndDlg, id)); + bool checked = SendMessage(GetDlgItem(hDlg, id), BM_GETCHECK, 0, 0) == BST_CHECKED; + fPBlock->SetValue(kMultOn, curTime, checked, i); + return TRUE; + } + if (id == kLayerID[i].layerID) + { + PostMessage(fhMtlEdit, WM_SUB_MTL_BUTTON, i, (LPARAM)fMtl); + return TRUE; + } + } + + } +// IUpdateMtlDisplay(); + break; + } + + return FALSE; +} + +void plMultipassMtlDlg::UpdateLayerDisplay() +{ + int numlayers = fPBlock->GetInt(kMultCount); + + fNumTexSpin->SetValue(numlayers, FALSE); + + int i; + for (i = 0; i < numlayers && i < NSUBMTLS; i++) + { + Mtl *m = fPBlock->GetMtl(kMultPasses, curTime, i); + TSTR nm; + if (m) + nm = m->GetName(); + else + nm = "None"; + fLayerBtns[i]->SetText(nm.data()); + + ShowWindow(GetDlgItem(fhRollup, kLayerID[i].layerID), SW_SHOW); + ShowWindow(GetDlgItem(fhRollup, kLayerID[i].activeID), SW_SHOW); + SetCheckBox(fhRollup, kLayerID[i].activeID, fPBlock->GetInt(kMultOn, curTime, i)); + } + + for (i = numlayers; i < NSUBMTLS; i++) + { + ShowWindow(GetDlgItem(fhRollup, kLayerID[i].layerID), SW_HIDE); + ShowWindow(GetDlgItem(fhRollup, kLayerID[i].activeID), SW_HIDE); + } +} + +void plMultipassMtlDlg::LoadDialog() +{ + if (fMtl) + { + fPBlock = fMtl->GetParamBlockByID(plMultipassMtl::kBlkPasses); + + if (fhRollup) + UpdateLayerDisplay(); + } +} + +bool plMultipassMtlDlg::ISetNumLayers(int num) +{ + if (num >= 1 && num <= NSUBMTLS) + { + fMtl->SetNumSubMtls(num); + UpdateLayerDisplay(); +// IUpdateMtlDisplay(); + + return true; + } + + return false; +} + +void plMultipassMtlDlg::IGetSpinnerVal() +{ + ISpinnerControl *spin = GetISpinner(GetDlgItem(fhRollup, IDC_LAYER_SPIN)); + if (!spin) + return; + + // If new number of layers is invalid, set to current num + if (!ISetNumLayers(spin->GetIVal())) + { + int nLayers = fPBlock->GetInt(kMultCount); + spin->SetValue(nLayers, FALSE); + } + + ReleaseISpinner(spin); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlDlg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlDlg.h new file mode 100644 index 00000000..7dc26ab0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlDlg.h @@ -0,0 +1,89 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_MAXMTLDLG_H +#define PL_MAXMTLDLG_H + +#define NSUBMTLS 10 + +class plMultipassMtlDlg : public ParamDlg +{ +protected: + IParamBlock2 *fPBlock; + + HWND fhMtlEdit; // Window handle of the materials editor dialog + HWND fhRollup; // Our rollup panel + IMtlParams *ip; + plMultipassMtl *fMtl; // current mtl being edited. + TimeValue curTime; + int isActive; + BOOL valid; +// int offset; + + ISpinnerControl *fNumTexSpin; + ICustButton *fLayerBtns[NSUBMTLS]; + + MtlDADMgr fDADMgr; // For drag-drop sub-materials + +public: + // Constructor and destructor + plMultipassMtlDlg(HWND hwMtlEdit, IMtlParams *imp, plMultipassMtl *m); + ~plMultipassMtlDlg(); + + // Functions inherited from ParamDLg: + Class_ID ClassID() { return MULTIMTL_CLASS_ID; } + void SetThing(ReferenceTarget *m); + ReferenceTarget* GetThing() { return (ReferenceTarget*)fMtl; } + void SetTime(TimeValue t); + void ReloadDialog(); + void ActivateDlg(BOOL onOff); + void DeleteThis() { delete this; } + int FindSubMtlFromHWND(HWND hw); + + static BOOL CALLBACK ForwardProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL LayerPanelProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + + void UpdateLayerDisplay(); + void LoadDialog(); +/* + // Lower-level crap + void Invalidate(); // Called by ParamMtl + BOOL IsActive() { return isActive; } + +private: + void ClampOffset(); + void SetNumMats(); + + void UpdateLayers(); + void UpdateControlFor(int np); + void VScroll(int code, short int cpos ); +*/ +protected: + void IUpdateMtlDisplay() { if (ip) ip->MtlChanged(); } + bool ISetNumLayers(int num); + void IGetSpinnerVal(); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlPB.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlPB.cpp new file mode 100644 index 00000000..efb56fe4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlPB.cpp @@ -0,0 +1,50 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plMultipassMtl.h" +#include "plMultipassMtlPB.h" + +ClassDesc2* GetMultiMtlDesc(); + +static ParamBlockDesc2 gMultipassMtlPB +( + plMultipassMtl::kBlkPasses, _T("multipass"), 0, GetMultiMtlDesc(), + P_AUTO_CONSTRUCT, plMultipassMtl::kRefPasses, + + kMultCount, _T("numPasses"), TYPE_INT, 0, 0, + p_default, 0, + end, + + kMultPasses, _T("passes"), TYPE_MTL_TAB, 0, 0, 0, + end, + kMultOn, _T("passOn"), TYPE_BOOL_TAB, 0, 0, 0, + p_default, TRUE, + end, + kMultLayerCounts, _T("LayerCounts"), TYPE_INT_TAB, 0, 0, 0, + p_default, 0, + end, + end +); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlPB.h new file mode 100644 index 00000000..ea6d5984 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plMultipassMtlPB.h @@ -0,0 +1,37 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_MULTIPASS_MTL_PB_H +#define PL_MULTIPASS_MTL_PB_H + +enum +{ + kMultCount, + kMultPasses, + kMultOn, + kMultLayerCounts +}; + +#endif //PL_MULTIPASS_MTL_PB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plNoteTrackWatcher.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plNoteTrackWatcher.cpp new file mode 100644 index 00000000..cc4d6de2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plNoteTrackWatcher.cpp @@ -0,0 +1,127 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plNoteTrackWatcher - Dummy object to watch for notetrack additions or // +// removals from the main material that owns it. // +// All of this is required because MAX will notify // +// an object's dependents about notetrack actions but // +// NOT the object itself, and the Add/DeleteNoteTrack // +// functions are non-virtual. ARRRGH! // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plNoteTrackWatcher.h" +#include "plPassMtlBase.h" + +#include "iparamm2.h" + +#include "resource.h" + + +//// Watcher Class Desc ////////////////////////////////////////////////////// + +plNoteTrackWatcher::plNoteTrackWatcher( plPassMtlBase *parentMtl ) : fParentMtl(nil) +{ + fNoteTrackCount = parentMtl->NumNoteTracks(); + MakeRefByID( FOREVER, kRefParentMtl, parentMtl ); +} + +plNoteTrackWatcher::~plNoteTrackWatcher() +{ + if( fParentMtl != nil ) + { + fParentMtl->fNTWatcher = nil; + DeleteReference( kRefParentMtl ); + } + DeleteAllRefsFromMe(); +} + +BOOL plNoteTrackWatcher::IsRealDependency( ReferenceTarget *rtarg ) +{ + if( rtarg == fParentMtl ) + return false; + + return true; +} + +int plNoteTrackWatcher::NumRefs() +{ + return 1; +} + +RefTargetHandle plNoteTrackWatcher::GetReference( int i ) +{ + if( i == kRefParentMtl ) + return fParentMtl; + + return nil; +} + +void plNoteTrackWatcher::SetReference( int i, RefTargetHandle rtarg ) +{ + if( i == kRefParentMtl ) + fParentMtl = (plPassMtlBase *)rtarg; +} + +RefResult plNoteTrackWatcher::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) +{ + switch( message ) + { + case REFMSG_SUBANIM_STRUCTURE_CHANGED: + if( hTarget == fParentMtl && fParentMtl != nil ) + { + // Structure of parent material changed--did it gain or lose a notetrack? + int oldCount = fNoteTrackCount; + fNoteTrackCount = fParentMtl->NumNoteTracks(); + if( oldCount != fNoteTrackCount ) + { + // Is it an addition? + if( fNoteTrackCount > oldCount ) + // Yes, notify parent. + fParentMtl->NoteTrackAdded(); + else + // Deletion, also notify parent + fParentMtl->NoteTrackRemoved(); + } + } + break; + + case REFMSG_NODE_NAMECHANGE: + if( hTarget == fParentMtl ) + { + fParentMtl->NameChanged(); + } + break; + + case REFMSG_TARGET_DELETED: + fParentMtl = nil; + break; + } + + return REF_SUCCEED; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plNoteTrackWatcher.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plNoteTrackWatcher.h new file mode 100644 index 00000000..7d1c4e4f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plNoteTrackWatcher.h @@ -0,0 +1,88 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plNoteTrackWatcher - Dummy object to watch for notetrack additions or // +// removals from the main material that owns it. // +// All of this is required because MAX will notify // +// an object's dependents about notetrack actions but // +// NOT the object itself, and the Add/DeleteNoteTrack // +// functions are non-virtual. ARRRGH! // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plNoteTrackWatcher_h +#define _plNoteTrackWatcher_h + +#include "Max.h" +#include "iparamb2.h" +#include "iparamm2.h" + +#include "hsTypes.h" + +extern TCHAR *GetString(int id); +extern HINSTANCE hInstance; + +#define NTWATCHER_CLASSID Class_ID(0x313f531e, 0xa5f132f) + +#define REFMSG_NOTETRACK_ADDED REFMSG_USER + 1 + +class plPassMtlBase; +class NoteTrack; + +//// Class Def /////////////////////////////////////////////////////////////// + +class plNoteTrackWatcher : public ReferenceMaker +{ +protected: + plPassMtlBase *fParentMtl; + + // For tracking notetrack additions to the parent + int fNoteTrackCount; + +public: + + enum Refs + { + kRefParentMtl = 0 + }; + + plNoteTrackWatcher( plPassMtlBase *parentMtl ); + virtual ~plNoteTrackWatcher(); + void DeleteThis() { delete this; } + + Class_ID ClassID() { return NTWATCHER_CLASSID; } + SClass_ID SuperClassID() { return REF_MAKER_CLASS_ID; } + + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + RefResult NotifyRefChanged(Interval changeInt,RefTargetHandle hTarget, PartID& partID, RefMessage message); + + virtual BOOL IsRealDependency( ReferenceTarget *rtarg ); +}; + +#endif //_plNoteTrackWatcher_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtl.cpp new file mode 100644 index 00000000..dd96cbb6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtl.cpp @@ -0,0 +1,620 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plParticleMtl.h" +#include "resource.h" +//extern ClassDesc2* GetMaxLayerDesc(); +#include "Shaders.h" + +#include "iparamm2.h" + +#include "../MaxMain/plPlasmaRefMsgs.h" +#include "plBMSampler.h" +#include "stdmat.h" +#include "Layers/plLayerTex.h" +#include "Layers/plLayerTexBitmapPB.h" + +extern HINSTANCE hInstance; + +class plParticleMtlClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return TRACKED_NEW plParticleMtl(loading); } + const TCHAR* ClassName() { return GetString(IDS_PARTICLE_MTL); } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + Class_ID ClassID() { return PARTICLE_MTL_CLASS_ID; } + const TCHAR* Category() { return NULL; } + const TCHAR* InternalName() { return _T("ParticleMaterial"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plParticleMtlClassDesc plParticleMtlDesc; +ClassDesc2* GetParticleMtlDesc() { return &plParticleMtlDesc; } + +// For initializing paramblock descriptor +ParamBlockDesc2 *GetParticlePB(); +#include "plParticleMtlPBDec.h" + +const char *plParticleMtl::NormalStrings[] = // Make sure these match up in order with the Normal enum (in the header) +{ + "Normal: View Facing", + "Normal: Up", + "Normal: Nearest Light", + "Normal: From Center", + "Normal: Vel x Up x Vel", + "Emissive" +}; + +plParticleMtl::plParticleMtl(BOOL loading) : fBasicPB(NULL)//, fBM(NULL), fUVGen(NULL) +{ +#if 0 // This wasn't working on load + // Initialize the paramblock descriptors only once + static bool descInit = false; + if (!descInit) + { + descInit = true; + GetParticlePB()->SetClassDesc(GetParticleMtlDesc()); + } +#endif + + plParticleMtlDesc.MakeAutoParamBlocks(this); + +// if (!loading) + { + Reset(); + plLayerTex *tex = TRACKED_NEW plLayerTex; + //tex->GetParamBlockByID(kBlkBasic)->SetValue(kBmpUseBitmap, 0, 1); + fBasicPB->SetValue(kTexmap, 0, tex); + + } + //fUVGen = GetNewDefaultUVGen(); +} + +void plParticleMtl::Reset() +{ + fIValid.SetEmpty(); +} + +ParamDlg* plParticleMtl::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + fIMtlParams = imp; + IAutoMParamDlg* masterDlg = plParticleMtlDesc.CreateParamDlgs(hwMtlEdit, imp, this); + + return (ParamDlg*)masterDlg; +} + +BOOL plParticleMtl::SetDlgThing(ParamDlg* dlg) +{ + return FALSE; +} + +Interval plParticleMtl::Validity(TimeValue t) +{ +#if 0 // mf horse + Interval valid = FOREVER; + +/* for (int i = 0; i < fSubTexmap.Count(); i++) + { + if (fSubTexmap[i]) + valid &= fSubTexmap[i]->Validity(t); + } +*/ +// float u; +// fPBlock->GetValue(pb_spin,t,u,valid); + return valid; +#else // mf horse + const char* name = GetName(); + + // mf horse - Hacking in something like real validity checking + // to get material animations working. No warranty, this is just + // better than nothing. + Interval v = FOREVER; + fBasicPB->GetValidity(t, v); + + return v; +#endif // mf horse +} + +/*===========================================================================*\ + | Subanim & References support +\*===========================================================================*/ + +int plParticleMtl::NumSubs() +{ + return 2; +} + +TSTR plParticleMtl::SubAnimName(int i) +{ + switch (i) + { + case 0: return fBasicPB->GetLocalName(); + case 1: return "Texmap"; + } + + return ""; +} + +Animatable* plParticleMtl::SubAnim(int i) +{ + switch (i) + { + case 0: return fBasicPB; + case 1: return fBasicPB->GetTexmap(kTexmap); + } + + return NULL; +} + +int plParticleMtl::NumRefs() +{ + return 1; +} + +RefTargetHandle plParticleMtl::GetReference(int i) +{ + switch (i) + { + case kRefBasic: return fBasicPB; + } + + return NULL; +} + +void plParticleMtl::SetReference(int i, RefTargetHandle rtarg) +{ + if (i == kRefBasic) + fBasicPB = (IParamBlock2 *)rtarg; +} + +int plParticleMtl::NumParamBlocks() +{ + return 1; +} + +IParamBlock2* plParticleMtl::GetParamBlock(int i) +{ + return (IParamBlock2*)GetReference(i); +} + +IParamBlock2* plParticleMtl::GetParamBlockByID(BlockID id) +{ + if (fBasicPB->ID() == id) + return fBasicPB; + + return NULL; +} + +RefResult plParticleMtl::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) +{ + switch (message) + { + case REFMSG_CHANGE: + fIValid.SetEmpty(); + + // see if this message came from a changing parameter in the pblock, + // if so, limit rollout update to the changing item + if (hTarget == fBasicPB) + { + IParamBlock2 *pb = (IParamBlock2*)hTarget; + + ParamID changingParam = pb->LastNotifyParamID(); + pb->GetDesc()->InvalidateUI(changingParam); + + // And let the SceneWatcher know that the material on some of it's + // referenced objects changed. + NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_MAT); + } + break; + } + + return REF_SUCCEED; +} + +//////////////////////////////////////////////////////////////////////////////// +// Subtexmap access + +int plParticleMtl::NumSubTexmaps() +{ + return 1; +} + +Texmap* plParticleMtl::GetSubTexmap(int i) +{ + if (i == 0) + return fBasicPB->GetTexmap(kTexmap); + + return NULL; +} + +void plParticleMtl::SetSubTexmap(int i, Texmap *m) +{ + if (i == 0) + fBasicPB->SetValue(kTexmap, 0, m); +} + +TSTR plParticleMtl::GetSubTexmapSlotName(int i) +{ + if (i == 0) + return "Texmap"; + + return ""; +} + +TSTR plParticleMtl::GetSubTexmapTVName(int i) +{ + return GetSubTexmapSlotName(i); +} + + +/*===========================================================================*\ + | Standard IO +\*===========================================================================*/ + +#define MTL_HDR_CHUNK 0x4000 + +IOResult plParticleMtl::Save(ISave *isave) +{ + IOResult res; + isave->BeginChunk(MTL_HDR_CHUNK); + res = MtlBase::Save(isave); + if (res!=IO_OK) return res; + isave->EndChunk(); + + return IO_OK; +} + +IOResult plParticleMtl::Load(ILoad *iload) +{ + IOResult res; + int id; + while (IO_OK==(res=iload->OpenChunk())) + { + switch(id = iload->CurChunkID()) + { + case MTL_HDR_CHUNK: + res = MtlBase::Load(iload); + break; + } + iload->CloseChunk(); + if (res!=IO_OK) + return res; + } + + return IO_OK; +} + +/*===========================================================================*\ + | Updating and cloning +\*===========================================================================*/ + +RefTargetHandle plParticleMtl::Clone(RemapDir &remap) +{ + plParticleMtl *mnew = TRACKED_NEW plParticleMtl(FALSE); + *((MtlBase*)mnew) = *((MtlBase*)this); + mnew->ReplaceReference(kRefBasic, remap.CloneRef(fBasicPB)); + + BaseClone(this, mnew, remap); + mnew->fIValid.SetEmpty(); + + return (RefTargetHandle)mnew; +} + +void plParticleMtl::NotifyChanged() +{ + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +} + +void plParticleMtl::Update(TimeValue t, Interval& valid) +{ + //StdUVGen *gen = (StdUVGen *)fUVGen; + //gen->SetUScl(1.0f, t); + //gen->SetVScl(1.0f, t); + //gen->Update(t, fIValid); + valid &= fIValid; +} + +/*===========================================================================*\ + | Determine the characteristics of the material +\*===========================================================================*/ + +void plParticleMtl::SetAmbient(Color c, TimeValue t) {} +void plParticleMtl::SetDiffuse(Color c, TimeValue t) {} +void plParticleMtl::SetSpecular(Color c, TimeValue t) {} +void plParticleMtl::SetShininess(float v, TimeValue t) {} + +Color plParticleMtl::GetAmbient(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plParticleMtl::GetDiffuse(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plParticleMtl::GetSpecular(int mtlNum, BOOL backFace) { return Color(0,0,0); } + +float plParticleMtl::GetXParency(int mtlNum, BOOL backFace) +{ + int opacity = fBasicPB->GetInt( kOpacity, 0 ); + float alpha = 1.0f - ( (float)opacity / 100.0f ); + + return alpha; +} + +float plParticleMtl::GetShininess(int mtlNum, BOOL backFace) { return 0.0f; } +float plParticleMtl::GetShinStr(int mtlNum, BOOL backFace) { return 0.0f; } +float plParticleMtl::WireSize(int mtlNum, BOOL backFace) { return 0.0f; } + +///////////////////////////////////////////////////////////////// + +void plParticleMtl::SetupGfxMultiMaps(TimeValue t, Material *mtl, MtlMakerCallback &cb) +{ +#if 0 + if (texHandleValid.InInterval(t)) { + mtl->texture.SetCount(numTexHandlesUsed); + for (int i=0; itexture[i].textHandle = texHandle[i]->GetHandle(); + Texmap *tx = (*maps)[useSubForTex[i]].map; + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[i], tx ); + SetTexOps(mtl,i,texOpsType[i]); + } + } + return; + } +#endif + +#if 0 // WTF?!?!?!? + Texmap *tx[2]; + int diffChan = stdIDToChannel[ ID_DI ]; + int opacChan = stdIDToChannel[ ID_OP ]; + tx[0] = (*maps)[diffChan].IsActive()?(*maps)[diffChan].map:NULL; + tx[1] = (*maps)[opacChan].IsActive()?(*maps)[opacChan].map:NULL; +#endif + + int nsupport = cb.NumberTexturesSupported(); +#if 0 + BITMAPINFO *bmi[NTEXHANDLES]; + + int nmaps=0; + for (int i=0; itexture.SetCount(nmaps); + if (nmaps==0) + return; + for (i=0; itexture[i].textHandle = NULL; + texHandleValid.SetInfinite(); + Interval valid; + BOOL needDecal = FALSE; + int ntx = 0; + int op; + + int forceW = 0; + int forceH = 0; + if (tx[0]) { + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[0], tx[0]); + TextureInfo &ti = mtl->texture[0]; + if (ti.tiling[0]==GW_TEX_NO_TILING||ti.tiling[1]==GW_TEX_NO_TILING) + needDecal = TRUE; + op = needDecal?TXOP_ALPHABLEND:TXOP_MODULATE; + bmi[0] = tx[0]->GetVPDisplayDIB(t,cb,valid,FALSE); + if (bmi[0]) { + texHandleValid &= valid; + useSubForTex[0] = diffChan; + ntx = 1; + forceW = bmi[0]->bmiHeader.biWidth; + forceH = bmi[0]->bmiHeader.biHeight; + } + } + if (tx[1]) { + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[ntx], tx[1]); + if (nsupport>ntx) { + bmi[1] = tx[1]->GetVPDisplayDIB(t,cb,valid,TRUE); + if (bmi[1]) { + texHandleValid &= valid; + StuffAlpha(bmi[1], (*maps)[opacChan].amount, GetOpacity(t),ntx?whiteCol:pShader->GetDiffuseClr(t)); + texHandle[ntx] = cb.MakeHandle(bmi[1]); + bmi[1] = NULL; + mtl->texture[ntx].textHandle = texHandle[ntx]->GetHandle(); + SetTexOps(mtl,ntx,TXOP_OPACITY); + useSubForTex[ntx] = opacChan; + ntx++; + } + } + else { + if (!needDecal) { + TextureInfo ti; +// if (SameUV(mtl->texture[0],mtl->texture[1])) { + // Not really correct to combine channels for different UV's but what the heck. + bmi[1] = tx[1]->GetVPDisplayDIB(t,cb,valid,TRUE, forceW, forceH); + if (bmi[1]) { + texHandleValid &= valid; + StuffAlphaInto(bmi[1], bmi[0], (*maps)[opacChan].amount, GetOpacity(t)); + op = TXOP_OPACITY; + free(bmi[1]); + bmi[1] = NULL; + } +// } + } + } + } + if (bmi[0]) { + texHandle[0] = cb.MakeHandle(bmi[0]); + bmi[0] = NULL; + mtl->texture[0].textHandle = texHandle[0]->GetHandle(); + SetTexOps(mtl,0,op); + } + mtl->texture.SetCount(ntx); + numTexHandlesUsed = ntx; +#endif +} + +/*===========================================================================*\ + | Actual shading takes place +\*===========================================================================*/ + +void plParticleMtl::Shade(ShadeContext& sc) +{ + // Get the background color + Color backColor, backTrans; + sc.GetBGColor(backColor, backTrans); + + ShadeWithBackground(sc, backColor); +} + +//// Requirements //////////////////////////////////////////////////////////// +// Tells MAX what we need to render ourselves properly, such as translucency, +// two-sidedness, etc. Flags are in imtl.h in the MAX SDK. + +ULONG plParticleMtl::Requirements( int subMtlNum ) +{ + ULONG req = 0; + + + req = Mtl::Requirements( subMtlNum ); + + // Uncomment this to get the background color fed to our ShadeWithBackground() + // (slower processing tho) +// req |= MTLREQ_BGCOL; + + int blendType = fBasicPB->GetInt( kBlend ); + if( blendType == kBlendAdd ) + req |= MTLREQ_ADDITIVE_TRANSP | MTLREQ_TRANSP; + else if( blendType == kBlendAlpha ) + req |= MTLREQ_TRANSP; + else if( fBasicPB->GetInt( kOpacity, 0 ) != 100 ) + req |= MTLREQ_TRANSP; + + return req; +} + +void plParticleMtl::ShadeWithBackground(ShadeContext &sc, Color background) +{ +#if 1 + TimeValue t = sc.CurTime(); + Color color(0, 0, 0); + float alpha = 0.0; + + // Evaluate Base layer + Texmap *map = fBasicPB->GetTexmap(kTexmap); + if (map && map->ClassID() == LAYER_TEX_CLASS_ID) + { + plLayerTex *layer = (plLayerTex*)map; + AColor evalColor = layer->EvalColor(sc); + + color = evalColor; + alpha = evalColor.a; + } + +#if 1 + AColor black; + black.Black(); + AColor white; + white.White(); + + + SIllumParams ip; + if( fBasicPB->GetInt( kNormal ) == kEmissive ) + { + // Emissive objects don't get shaded + ip.diffIllum = fBasicPB->GetColor(kColorAmb, t) * color; + ip.diffIllum.ClampMinMax(); + ip.specIllum = black; + } + else + { + // + // Shading setup + // + + // Setup the parameters for the shader + ip.amb = black; + ip.diff = fBasicPB->GetColor(kColor, t) * color; + ip.spec = white; + ip.diffIllum = black; + ip.specIllum = black; + ip.N = sc.Normal(); + ip.V = sc.V(); + + + // + // Specularity + // + ip.sh_str = 0; + ip.ph_exp = 0; + ip.shine = 0; + + ip.softThresh = 0; + + + + // Do the shading + Shader *myShader = GetShader(SHADER_BLINN); + myShader->Illum(sc, ip); + + ip.diffIllum.ClampMinMax(); + ip.specIllum.ClampMinMax(); + ip.diffIllum = ip.amb * sc.ambientLight + ip.diff * ip.diffIllum; + } + +// AColor returnColor = AColor(opac * ip.diffIllum + ip.specIllum, opac) +#endif + + // Get opacity and combine with alpha + float opac = float(fBasicPB->GetInt(kOpacity, t)) / 100.0f; + //float opac = 1.0f; + alpha *= opac; + + // MAX will do the additive/alpha/no blending for us based on what Requirements() + // we tell it. However, since MAX's formula is bgnd*sc.out.t + sc.out.c, + // we have to multiply our output color by the alpha. + // If we ever need a more complicated blending function, you can request the + // background color via Requirements() (otherwise it's just black) and then do + // the blending yourself; however, if the transparency isn't set, the shadows + // will be opaque, so be careful. + Color outC = ip.diffIllum + ip.specIllum; + + sc.out.c = ( outC * alpha ); + sc.out.t = Color( 1.f - alpha, 1.f - alpha, 1.f - alpha ); + +#endif +} + +float plParticleMtl::EvalDisplacement(ShadeContext& sc) +{ + return 0.0f; +} + +Interval plParticleMtl::DisplacementValidity(TimeValue t) +{ + Interval iv; + iv.SetInfinite(); + + return iv; +} + +Control *plParticleMtl::GetAmbColorController() { return fBasicPB->GetController(ParamID(kColorAmb)); } +Control *plParticleMtl::GetColorController() { return fBasicPB->GetController(ParamID(kColor)); } +Control *plParticleMtl::GetOpacityController() { return fBasicPB->GetController(ParamID(kOpacity)); } +Control *plParticleMtl::GetWidthController() { return fBasicPB->GetController(ParamID(kWidth)); } +Control *plParticleMtl::GetHeightController() { return fBasicPB->GetController(ParamID(kHeight)); } diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtl.h new file mode 100644 index 00000000..28a87aa5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtl.h @@ -0,0 +1,182 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_PARTICLEMTL_H +#define PL_PARTICLEMTL_H + +#include "Max.h" +//#include "istdplug.h" +#include "iparamb2.h" +//#include "iparamm2.h" +#include "../resource.h" + +class Bitmap; + +#define PARTICLE_MTL_CLASS_ID Class_ID(0x26df05ff, 0x60660749) + +extern TCHAR *GetString(int id); + +class plParticleMtl : public Mtl +{ +protected: + IParamBlock2 *fBasicPB; + Interval fIValid; + +public: + IMtlParams *fIMtlParams; + + enum + { + kRefBasic, + }; + enum + { + kBlkBasic, + }; + + enum // Param block indicies + { + kOpacity, + kColor, + kWidth, + kHeight, + kXTiles, + kYTiles, + kNormal, + kBlend, + kOrientation, + kBitmap, + kTexmap, + kColorAmb, + kNoFilter + }; + enum + { + kBlendNone, + kBlendAlpha, + kBlendAdd + }; + enum + { + kOrientVelocity, + kOrientUp, + kOrientVelStretch, + kOrientVelFlow + }; + + enum + { + kNormalViewFacing, + kNormalUp, + kNormalNearestLight, + kNormalFromCenter, + kNormalVelUpVel, + kEmissive, + kNumNormalOptions + }; + + static const char *NormalStrings[]; + + plParticleMtl(BOOL loading); + void DeleteThis() { delete this; } + + //From Animatable + Class_ID ClassID() { return PARTICLE_MTL_CLASS_ID; } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + void GetClassName(TSTR& s) { s = GetString(IDS_PARTICLE_MTL); } + + ParamDlg *CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + void Update(TimeValue t, Interval& valid); + Interval Validity(TimeValue t); + void Reset(); + + void NotifyChanged(); + + BOOL SupportsMultiMapsInViewport() { return FALSE; } + void SetupGfxMultiMaps(TimeValue t, Material *mtl, MtlMakerCallback &cb); + + // Shade and displacement calculation + void Shade(ShadeContext& sc); + void ShadeWithBackground(ShadeContext &sc, Color background); + float EvalDisplacement(ShadeContext& sc); + Interval DisplacementValidity(TimeValue t); + + // SubTexmap access methods + int NumSubTexmaps(); + Texmap* GetSubTexmap(int i); + void SetSubTexmap(int i, Texmap *m); + TSTR GetSubTexmapSlotName(int i); + TSTR GetSubTexmapTVName(int i); + + BOOL SetDlgThing(ParamDlg* dlg); + + // Loading/Saving + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + RefTargetHandle Clone( RemapDir &remap ); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + + int NumParamBlocks(); + IParamBlock2* GetParamBlock(int i); + IParamBlock2* GetParamBlockByID(BlockID id); + + +// void SetParamDlg(ParamDlg *dlg); + +// void SetNumSubTexmaps(int num); + + DllExport Control *GetAmbColorController(); + DllExport Control *GetColorController(); + DllExport Control *GetOpacityController(); + DllExport Control *GetWidthController(); + DllExport Control *GetHeightController(); + + // From MtlBase and Mtl + void SetAmbient(Color c, TimeValue t); + void SetDiffuse(Color c, TimeValue t); + void SetSpecular(Color c, TimeValue t); + void SetShininess(float v, TimeValue t); + Color GetAmbient(int mtlNum=0, BOOL backFace=FALSE); + Color GetDiffuse(int mtlNum=0, BOOL backFace=FALSE); + Color GetSpecular(int mtlNum=0, BOOL backFace=FALSE); + float GetXParency(int mtlNum=0, BOOL backFace=FALSE); + float GetShininess(int mtlNum=0, BOOL backFace=FALSE); + float GetShinStr(int mtlNum=0, BOOL backFace=FALSE); + float WireSize(int mtlNum=0, BOOL backFace=FALSE); + + ULONG Requirements( int subMtlNum ); +}; + +#endif //PL_PARTICLEMTL_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtlPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtlPBDec.h new file mode 100644 index 00000000..fc2746f7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plParticleMtlPBDec.h @@ -0,0 +1,234 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plParticleMtl.h" + +class PartMtlPBAccessor; +extern PartMtlPBAccessor partMtl_accessor; + +class ParticleBasicDlgProc; +extern ParticleBasicDlgProc gParticleBasicDlgProc; + +#define PL_PARTICLE_MTL_MIN_TILES 1 +#define PL_PARTICLE_MTL_MAX_TILES 16 + +static ParamBlockDesc2 gParticleMtlPB +( + plParticleMtl::kBlkBasic, _T("particle"), IDS_PASS_BASIC, GetParticleMtlDesc(), + P_AUTO_CONSTRUCT + P_AUTO_UI + P_CALLSETS_ON_LOAD, plParticleMtl::kRefBasic, + + // UI + IDD_PARTICLE, IDS_PASS_BASIC, 0, 0, &gParticleBasicDlgProc, + + plParticleMtl::kOpacity, _T("opacity"), TYPE_INT, P_ANIMATABLE, IDS_PARTICLE_OPACITY, + p_default, 100, + p_range, 0, 100, + p_ui, TYPE_SPINNER, EDITTYPE_INT, + IDC_PARTICLE_OPACITY, IDC_PARTICLE_OPACITY_SPIN, 1.0, + end, + + plParticleMtl::kColorAmb, _T("ambColor"), TYPE_RGBA, P_ANIMATABLE, IDS_PARTICLE_AMB_COLOR, + p_ui, TYPE_COLORSWATCH, IDC_PARTICLE_AMB_COLOR, + p_default, Color(0,0,0), + end, + + plParticleMtl::kColor, _T("color"), TYPE_RGBA, P_ANIMATABLE, IDS_PARTICLE_COLOR, + p_ui, TYPE_COLORSWATCH, IDC_PARTICLE_COLOR, + p_default, Color(1,1,1), + end, + + plParticleMtl::kWidth, _T("width"), TYPE_FLOAT, P_ANIMATABLE, IDS_PARTICLE_WIDTH, + p_default, 1.0, + p_range, 0.01, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_PARTICLE_WIDTH, IDC_PARTICLE_WIDTH_SPIN, 1.0, + end, + + plParticleMtl::kHeight, _T("height"), TYPE_FLOAT, P_ANIMATABLE, IDS_PARTICLE_HEIGHT, + p_default, 1.0, + p_range, 0.01, 10.0, + p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, + IDC_PARTICLE_HEIGHT, IDC_PARTICLE_HEIGHT_SPIN, 1.0, + end, + + plParticleMtl::kXTiles, _T("xTiling"), TYPE_INT, 0, 0, + p_default, 1, + p_range, PL_PARTICLE_MTL_MIN_TILES, PL_PARTICLE_MTL_MAX_TILES, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_PARTICLE_XTILE, IDC_PARTICLE_XTILE_SPIN, 1.0, + p_accessor, &partMtl_accessor, + end, + + plParticleMtl::kYTiles, _T("yTiling"), TYPE_INT, 0, 0, + p_default, 1, + p_range, PL_PARTICLE_MTL_MIN_TILES, PL_PARTICLE_MTL_MAX_TILES, + p_ui, TYPE_SPINNER, EDITTYPE_POS_INT, + IDC_PARTICLE_YTILE, IDC_PARTICLE_YTILE_SPIN, 1.0, + p_accessor, &partMtl_accessor, + end, + + plParticleMtl::kNormal, _T("normal"), TYPE_INT, 0, 0, + p_default, 0, + end, + + plParticleMtl::kBlend, _T("texBlend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 2, IDC_PARTICLE_BLEND_ALPHA, IDC_PARTICLE_BLEND_ADD, + p_vals, plParticleMtl::kBlendAlpha, plParticleMtl::kBlendAdd, + p_default, plParticleMtl::kBlendAlpha, + end, + + plParticleMtl::kOrientation, _T("layerBlend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 4, IDC_PARTICLE_ORIENT_VELOCITY, IDC_PARTICLE_ORIENT_UP, IDC_PARTICLE_ORIENT_VELSTRETCH, IDC_PARTICLE_ORIENT_VELFLOW, + p_vals, plParticleMtl::kOrientVelocity, plParticleMtl::kOrientUp, plParticleMtl::kOrientVelStretch, plParticleMtl::kOrientVelFlow, + p_default, plParticleMtl::kOrientVelocity, + end, + + plParticleMtl::kBitmap, _T("bitmap"), TYPE_BITMAP, P_SHORT_LABELS, 0, + ///p_ui, TYPE_BITMAPBUTTON, IDC_PARTICLE_TEXTURE, + //p_accessor, &partMtl_accessor, + end, + + plParticleMtl::kTexmap, _T("texmap"), TYPE_TEXMAP, 0, 0, +// p_ui, TYPE_TEXMAPBUTTON, IDC_LAYER2, + end, + + plParticleMtl::kNoFilter, _T("noFilter"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_PARTICLE_NOFILTER, + p_default, FALSE, + end, + + end +); + +class PartMtlPBAccessor : public PBAccessor +{ +public: + void Set(PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + plParticleMtl* mtl = (plParticleMtl *)owner; + //plLayerTex *layer; + + switch (id) + { + case plParticleMtl::kBitmap: + break; + case plParticleMtl::kXTiles: + case plParticleMtl::kYTiles: + if (val.i < PL_PARTICLE_MTL_MIN_TILES) + val.i = PL_PARTICLE_MTL_MIN_TILES; + if (val.i > PL_PARTICLE_MTL_MAX_TILES) + val.i = PL_PARTICLE_MTL_MAX_TILES; + break; + } + } + + void Get(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid) + { + } +}; +static PartMtlPBAccessor partMtl_accessor; + + +class ParticleBasicDlgProc : public ParamMap2UserDlgProc +{ +public: + ParticleBasicDlgProc() {} + ~ParticleBasicDlgProc() {} + + void UpdateDisplay(IParamMap2 *pmap) + { + HWND hWnd = pmap->GetHWnd(); + IParamBlock2 *pb = pmap->GetParamBlock(); + HWND cbox = GetDlgItem(hWnd, IDC_PARTICLE_NORMAL); + plPlasmaMAXLayer *layer = (plPlasmaMAXLayer *)pb->GetTexmap(ParamID(plParticleMtl::kTexmap)); + PBBitmap *pbbm; + ICustButton *bmSelectBtn; + + SendMessage(cbox, CB_SETCURSEL, pb->GetInt(plParticleMtl::kNormal), 0); + pbbm = (layer == nil ? nil : layer->GetPBBitmap()); + + bmSelectBtn = GetICustButton(GetDlgItem(hWnd,IDC_PARTICLE_TEXTURE)); + bmSelectBtn->SetText(pbbm ? (TCHAR*)pbbm->bi.Filename() : "(none)"); + ReleaseICustButton(bmSelectBtn); + } + + virtual void Update(TimeValue t, Interval& valid, IParamMap2* pmap) { UpdateDisplay(pmap); } + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + int id = LOWORD(wParam); + int code = HIWORD(wParam); + + IParamBlock2 *pb = map->GetParamBlock(); + HWND cbox = NULL; + plPlasmaMAXLayer *layer = (plPlasmaMAXLayer *)pb->GetTexmap(ParamID(plParticleMtl::kTexmap)); + + switch (msg) + { + case WM_INITDIALOG: + int j; + for (j = 0; j < plParticleMtl::kNumNormalOptions; j++) + { + cbox = GetDlgItem(hWnd, IDC_PARTICLE_NORMAL); + SendMessage(cbox, CB_ADDSTRING, 0, (LPARAM)plParticleMtl::NormalStrings[j]); + } + UpdateDisplay(map); + return TRUE; + + case WM_COMMAND: + if (id == IDC_PARTICLE_NORMAL) + { + pb->SetValue(plParticleMtl::kNormal, t, SendMessage(GetDlgItem(hWnd, id), CB_GETCURSEL, 0, 0)); + return TRUE; + } + else if (id == IDC_PARTICLE_TEXTURE) + { + if (layer == nil) + return FALSE; + layer->HandleBitmapSelection(); + UpdateDisplay(map); + return TRUE; + } + else if (id == IDC_PARTICLE_NOFILTER) + { + if (!layer) + return FALSE; + if( pb->GetInt(plParticleMtl::kNoFilter) ) + { + layer->GetParamBlockByID( plLayerTex::kBlkBitmap )->SetValue(kBmpNoFilter, t, 1); + } + else + { + layer->GetParamBlockByID( plLayerTex::kBlkBitmap )->SetValue(kBmpNoFilter, t, 0); + } + return TRUE; + } + break; + } + return FALSE; + } + void DeleteThis() {} +}; +static ParticleBasicDlgProc gParticleBasicDlgProc; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassAnimDlgProc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassAnimDlgProc.cpp new file mode 100644 index 00000000..318b75ca --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassAnimDlgProc.cpp @@ -0,0 +1,305 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plPassAnimDlgProc - Base Animation Dlg Proc for plPassMtlBase // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plPassAnimDlgProc.h" + +#include "plPassBaseParamIDs.h" +#include "../MaxComponent/plNotetrackAnim.h" +#include "resource.h" +#include "iparamm2.h" + +#include "plAnimStealthNode.h" + +#include "../MaxComponent/plMaxAnimUtils.h" +#include "../MaxComponent/plAnimComponent.h" +#include "../MaxExport/plErrorMsg.h" + +const char *kPassNameNone = ENTIRE_ANIMATION_NAME; + +#include "plAnimStealthNode.h" + +using namespace plPassBaseParamIDs; + +plPassAnimDlgProc::plPassAnimDlgProc() +{ + fCurrParamMap = nil; + fInitingNames = false; +} + +plPassAnimDlgProc::~plPassAnimDlgProc() +{ + if( fCurrParamMap != nil ) + { + plPassMtlBase *mtl = (plPassMtlBase *)( fCurrParamMap->GetParamBlock()->GetOwner() ); + mtl->RegisterChangeCallback( this ); + } +} + +plPassAnimDlgProc &plPassAnimDlgProc::Get( void ) +{ + static plPassAnimDlgProc instance; + return instance; +} + +void plPassAnimDlgProc::Update(TimeValue t, Interval& valid, IParamMap2* pmap) +{ +/* plAnimStealthNode *testStealth = (plAnimStealthNode *)pmap->GetParamBlock()->GetINode( (ParamID)kPBAnimTESTING ); + if( testStealth != nil ) + { + IParamBlock2 *pb = testStealth->GetParamBlockByID( plAnimStealthNode::kBlockPB ); + if( pb && pb->GetMap() && pb->GetMap()->GetUserDlgProc() ) + pb->GetMap()->GetUserDlgProc()->Update( t, valid, pmap ); + } +*/ + + HWND hWnd = pmap->GetHWnd(); + IParamBlock2 *pb = pmap->GetParamBlock(); + plAnimComponentProc::SetBoxToAgeGlobal(GetDlgItem(hWnd, IDC_MTL_GLOBAL_NAME), pb->GetStr(ParamID(kPBAnimGlobalName))); +} + +BOOL plPassAnimDlgProc::DlgProc(TimeValue t, IParamMap2 *pMap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if( fCurrParamMap != pMap ) + { + if( fCurrParamMap != nil ) + { + plPassMtlBase *mtl = (plPassMtlBase *)( fCurrParamMap->GetParamBlock()->GetOwner() ); + mtl->UnregisterChangeCallback( this ); + } + + fCurrParamMap = pMap; + + if( fCurrParamMap != nil ) + { + plPassMtlBase *mtl = (plPassMtlBase *)( fCurrParamMap->GetParamBlock()->GetOwner() ); + mtl->RegisterChangeCallback( this ); + } + } + + IParamBlock2 *pb = pMap->GetParamBlock(); + plPassMtlBase *mtl = (plPassMtlBase*)pb->GetOwner(); + HWND gWnd = GetDlgItem(hWnd, IDC_MTL_GLOBAL_NAME); + char buff[512]; + + switch (msg) + { + case WM_DESTROY: + if( fCurrParamMap != nil ) + { + plPassMtlBase *mtl = (plPassMtlBase *)( fCurrParamMap->GetParamBlock()->GetOwner() ); + mtl->RegisterChangeCallback( this ); + fCurrParamMap = nil; + } + break; + + case WM_INITDIALOG: + { + fhWnd = hWnd; + fCurrStealth = nil; + IInitControls(mtl, pb); + + plAnimComponentProc::FillAgeGlobalComboBox(gWnd, pb->GetStr(ParamID(kPBAnimGlobalName))); + plAnimComponentProc::SetBoxToAgeGlobal(gWnd, pb->GetStr(ParamID(kPBAnimGlobalName))); + IEnableGlobal(hWnd, pb->GetInt( (ParamID)kPBAnimUseGlobal ) ); + + bool stopPoints = false; + if( DoesHaveStopPoints( pb->GetOwner() ) ) + { + stopPoints = true; + break; + } + + IEnableEaseStopPoints( pMap, stopPoints ); + } + return TRUE; + + case WM_COMMAND: + // Anim name selection changed + if (LOWORD(wParam) == IDC_NAMES && HIWORD(wParam) == CBN_SELCHANGE) + { + IUpdateSegmentSel( pMap ); + return TRUE; + } + // Refresh clicked + else if (LOWORD(wParam) == IDC_REFRESH_ANIMS && HIWORD(wParam) == BN_CLICKED) + { + IInitControls(mtl, pb); + return TRUE; + } + else if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_MTL_GLOBAL_NAME) + { + ComboBox_GetLBText(gWnd, ComboBox_GetCurSel(gWnd), buff); + pb->SetValue(ParamID(kPBAnimGlobalName), 0, _T(buff)); + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_MTL_USE_GLOBAL) + { + IEnableGlobal(hWnd, pb->GetInt( (ParamID)kPBAnimUseGlobal ) ); + } + + break; + } + + return FALSE; +} + +void plPassAnimDlgProc::SegmentListChanged( void ) +{ + if( fCurrParamMap != nil ) + ILoadNames( fCurrParamMap->GetParamBlock() ); +} + +void plPassAnimDlgProc::IUpdateSegmentSel( IParamMap2 *thisMap, hsBool clear ) +{ + plAnimStealthNode *newStealth; + HWND hAnims = GetDlgItem( fhWnd, IDC_NAMES ); + + // Get current selection + if( clear ) + newStealth = nil; + else + { + int sel = SendDlgItemMessage( fhWnd, IDC_NAMES, CB_GETCURSEL, 0, 0 ); + if( sel == CB_ERR ) + { + // Somehow we don't have a selection...fine, just destroy + newStealth = nil; + } + else + { + newStealth = (plAnimStealthNode *)SendDlgItemMessage( fhWnd, IDC_NAMES, CB_GETITEMDATA, sel, 0 ); + } + } + + // Did we really not change? + if( newStealth == fCurrStealth ) + return; + + if( fCurrStealth != nil && newStealth != nil ) + { + fCurrStealth->SwitchDlg( newStealth ); + } + else + { + // Destroy the old + if( fCurrStealth != nil ) + fCurrStealth->ReleaseDlg(); + + // Show the new + if( newStealth != nil ) + IExposeStealthNode( newStealth, thisMap ); + } + + // And update! + fCurrStealth = newStealth; +} + + +void plPassAnimDlgProc::IExposeStealthNode( HelperObject *node, IParamMap2 *thisMap ) +{ + if( node->ClassID() != ANIMSTEALTH_CLASSID ) + return; + + // Get our stealth pointer + plAnimStealthNode *stealth = (plAnimStealthNode *)node; + + // Create the paramMap-based dialog for us + IParamBlock2 *pb = thisMap->GetParamBlock(); + plPassMtlBase *mtl = (plPassMtlBase *)pb->GetOwner(); + + if( !stealth->CreateAndEmbedDlg( thisMap, mtl->fIMtlParams, GetDlgItem( fhWnd, IDC_PLACEHOLDER ) ) ) + { + } +} + + +void plPassAnimDlgProc::SetThing(ReferenceTarget *m) +{ + plPassMtlBase *mtl = (plPassMtlBase*)m; + IInitControls(mtl, mtl->fAnimPB ); +} + +void plPassAnimDlgProc::IInitControls(Animatable *anim, IParamBlock2 *pb) +{ + ILoadNames( pb ); + IEnableGlobal( fhWnd, pb->GetInt( ParamID( kPBAnimUseGlobal ) ) ); +} + +void plPassAnimDlgProc::ILoadNames(IParamBlock2 *pb ) +{ + // The following is to prevent IGetNumStealths() from re-calling us + if( fInitingNames ) + return; + fInitingNames = true; + + HWND hAnims = GetDlgItem(fhWnd, IDC_NAMES); + SendMessage(hAnims, CB_RESETCONTENT, 0, 0); + + plPassMtlBase *mtl = (plPassMtlBase *)pb->GetOwner(); + + // Loop through our stealth nodes and add them all to the combo, + // since that's what we're selecting...erm, yeah + int i, count = mtl->IGetNumStealths( true ); + for( i = 0; i < count; i++ ) + { + plAnimStealthNode *stealth = mtl->IGetStealth( i, false ); + if( stealth != nil ) + { + int idx = SendMessage( hAnims, CB_ADDSTRING, 0, (LPARAM)stealth->GetSegmentName() ); + SendMessage( hAnims, CB_SETITEMDATA, idx, (LPARAM)stealth ); + } + } + + SendMessage( hAnims, CB_SETCURSEL, 0, 0 ); + IUpdateSegmentSel( pb->GetMap() ); + + fInitingNames = false; +} + +void plPassAnimDlgProc::IEnableGlobal(HWND hWnd, hsBool enable) +{ + Edit_Enable(GetDlgItem(hWnd, IDC_MTL_GLOBAL_NAME), enable); + ComboBox_Enable(GetDlgItem(hWnd, IDC_NAMES), !enable); + + HWND stealthWnd = ( fCurrStealth != nil ) ? fCurrStealth->GetWinDlg() : nil; + if( stealthWnd != nil ) + EnableWindow( stealthWnd, !enable ); +} + +void plPassAnimDlgProc::IEnableEaseStopPoints( IParamMap2 *pm, bool enable ) +{ + pm->Enable( (ParamID)kPBAnimEaseInMin, enable ); + pm->Enable( (ParamID)kPBAnimEaseInMax, enable ); + pm->Enable( (ParamID)kPBAnimEaseOutMin, enable ); + pm->Enable( (ParamID)kPBAnimEaseOutMax, enable ); +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassAnimDlgProc.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassAnimDlgProc.h new file mode 100644 index 00000000..842914ee --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassAnimDlgProc.h @@ -0,0 +1,84 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plPassAnimDlgProc - Base Animation Dlg Proc for plPassMtlBase // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plPassAnimDlgProc_h +#define _plPassAnimDlgProc_h + +#include "plPassMtlBase.h" +#include "iparamm2.h" + +class plAnimStealthNode; +class IParamMap2; + +class plPassAnimDlgProc : public ParamMap2UserDlgProc, public plMtlChangeCallback +{ + protected: + // Combo itemdata values + enum + { + kName, // Name of an animation/loop + kDefault, // Default combo value + kInvalid, // Invalid entry (couldn't find) + }; + + plAnimStealthNode *fCurrStealth; + IParamMap2 *fCurrParamMap; + + bool fInitingNames; + HWND fhWnd; + + public: + plPassAnimDlgProc(); + virtual ~plPassAnimDlgProc(); + + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void DeleteThis() {} + void SetThing(ReferenceTarget *m); + virtual void Update(TimeValue t, Interval& valid, IParamMap2* pmap); + + void SegmentListChanged( void ); + + static plPassAnimDlgProc &Get( void ); + + protected: + // Set all the controls to their stored value + void IInitControls(Animatable *anim, IParamBlock2 *pb); + void IEnableGlobal(HWND hWnd, hsBool enable); + + void ILoadNames( IParamBlock2 *pb ); + + void IExposeStealthNode( HelperObject *stealth, IParamMap2 *thisMap ); + void IUpdateSegmentSel( IParamMap2 *thisMap, hsBool clear = false ); + + void IEnableEaseStopPoints( IParamMap2 *pm, bool enable ); +}; + +#endif //_plPassAnimDlgProc_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassBaseParamIDs.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassBaseParamIDs.h new file mode 100644 index 00000000..ed73daf2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassBaseParamIDs.h @@ -0,0 +1,100 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plPassBaseParamIDs - Common ParamIDs for all plPassMtlBase-derived mtls // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plPassBaseParamIDs_h +#define _plPassBaseParamIDs_h + +#define MCN_UPGRADE_OLD_ANIM_BLOCKS + +namespace plPassBaseParamIDs +{ + enum AdvancedIDs + { + // Specular + kPBAdvUseSpec, // Not used anymore, feel free to replace with new fields as necessary + kPBAdvSpecType, // ditto + kPBAdvShine, // ... + kPBAdvShineStr, // ... + + // Misc + kPBAdvWire, + kPBAdvMeshOutlines, + kPBAdvTwoSided, + + // Shading + kPBAdvSoftShadow, + kPBAdvNoProj, + kPBAdvVertexShade, + kPBAdvNoShade, + kPBAdvNoFog, + kPBAdvWhite, + + // Z + kPBAdvZOnly, + kPBAdvZClear, + kPBAdvZNoRead, + kPBAdvZNoWrite, + kPBAdvZInc, + + // Alpha test + kPBAdvAlphaTestHigh + }; + + enum SpecularTypes + { + kSpecTypeAlpha, + kSpecTypeColor, + kSpecTypeHighlight + }; + + enum AnimationIDs + { + kPBAnimName, + kPBAnimAutoStart, + kPBAnimLoop, + kPBAnimLoopName, + kPBAnimEaseInType, + kPBAnimEaseOutType, + kPBAnimEaseInLength, + kPBAnimEaseOutLength, + kPBAnimEaseInMin, + kPBAnimEaseInMax, + kPBAnimEaseOutMin, + kPBAnimEaseOutMax, + kPBAnimUseGlobal, + kPBAnimGlobalName, + kPBAnimStealthNodes + }; + + +} + +#endif //_plPassBaseParamIDs_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtl.cpp new file mode 100644 index 00000000..c6c93410 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtl.cpp @@ -0,0 +1,846 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plPassMtl.h" +#include "resource.h" +//extern ClassDesc2* GetMaxLayerDesc(); +#include "Shaders.h" +#include "../MaxComponent/plMaxAnimUtils.h" + +#include "plPassBaseParamIDs.h" +#include "plPassMtlBasicPB.h" +#include "plPassMtlLayersPB.h" + +#include "iparamm2.h" + +#include "Layers/plLayerTex.h" +#include "Layers/plStaticEnvLayer.h" + +#include "hsBitVector.h" + +extern HINSTANCE hInstance; + +class plPassMtlClassDesc : public ClassDesc2 +{ +public: + int IsPublic() { return TRUE; } + void* Create(BOOL loading) { return TRACKED_NEW plPassMtl(loading); } + const TCHAR* ClassName() { return GetString(IDS_PASS_MTL); } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + Class_ID ClassID() { return PASS_MTL_CLASS_ID; } + const TCHAR* Category() { return NULL; } + const TCHAR* InternalName() { return _T("PlasmaMaterial"); } + HINSTANCE HInstance() { return hInstance; } +}; +static plPassMtlClassDesc plPassMtlDesc; +ClassDesc2* GetPassMtlDesc() { return &plPassMtlDesc; } + +// For initializing paramblock descriptor +ParamBlockDesc2 *GetPassBasicPB(); +ParamBlockDesc2 *GetPassAdvPB(); +ParamBlockDesc2 *GetPassLayersPB(); + +#include "plPassMtlAdvPBDec.h" +#include "plPassMtlBasicPBDec.h" +#include "plPassMtlLayersPBDec.h" +#include "plPassMtlAnimPBDec.h" + +#include "plAnimStealthNode.h" + +plPassMtl::plPassMtl(BOOL loading) : plPassMtlBase( loading ) +{ + plPassMtlDesc.MakeAutoParamBlocks( this ); + fLayersPB->SetValue( kPassLayBase, 0, TRACKED_NEW plLayerTex ); + fLayersPB->SetValue( kPassLayTop, 0, TRACKED_NEW plLayerTex ); + + // If we do this later (like, when the dialog loads) something blows up, + // somewhere in Max. It didn't in 4, it does in 7. This seems to fix it. + if (!loading) + IVerifyStealthPresent(ENTIRE_ANIMATION_NAME); +} + +plPassMtl::~plPassMtl() +{ +} + +ParamDlg* plPassMtl::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) +{ + fIMtlParams = imp; + IAutoMParamDlg* masterDlg = plPassMtlDesc.CreateParamDlgs(hwMtlEdit, imp, this); + + return (ParamDlg*)masterDlg; +} + +BOOL plPassMtl::SetDlgThing(ParamDlg* dlg) +{ + return FALSE; +} + +Interval plPassMtl::Validity(TimeValue t) +{ +#if 0 // mf horse + Interval valid = FOREVER; + +/* for (int i = 0; i < fSubTexmap.Count(); i++) + { + if (fSubTexmap[i]) + valid &= fSubTexmap[i]->Validity(t); + } +*/ +// float u; +// fPBlock->GetValue(pb_spin,t,u,valid); + return valid; +#else // mf horse + const char* name = GetName(); + + // mf horse - Hacking in something like real validity checking + // to get material animations working. No warranty, this is just + // better than nothing. + Interval v = FOREVER; + fBasicPB->GetValidity(t, v); + fAdvPB->GetValidity(t, v); + + if( fLayersPB->GetTexmap(kPassLayBase) ) + v &= fLayersPB->GetTexmap(kPassLayBase)->Validity(t); + if( fLayersPB->GetTexmap(kPassLayTop) ) + v &= fLayersPB->GetTexmap(kPassLayTop)->Validity(t); + return v; +#endif // mf horse +} + +//// GetReference //////////////////////////////////////////////////////////// +// Note: need to overload because MAX for some reason writes out the +// references by their INDEX. ARRRRGH! + +RefTargetHandle plPassMtl::GetReference( int i ) +{ + switch( i ) + { + case kRefBasic: return fBasicPB; + case kRefAdv: return fAdvPB; + case kRefLayers: return fLayersPB; + case kRefAnim: return fAnimPB; + } + + return plPassMtlBase::GetReference( i ); +} + +//// SetReference //////////////////////////////////////////////////////////// +// Note: need to overload because MAX for some reason writes out the +// references by their INDEX. ARRRRGH! + +void plPassMtl::SetReference(int i, RefTargetHandle rtarg) +{ + if (i == kRefBasic) + fBasicPB = (IParamBlock2 *)rtarg; + else if (i == kRefAdv) + fAdvPB = (IParamBlock2 *)rtarg; + else if (i == kRefLayers) + fLayersPB = (IParamBlock2 *)rtarg; + else if (i == kRefAnim) + fAnimPB = (IParamBlock2 *)rtarg; + else + plPassMtlBase::SetReference( i, rtarg ); +} + +/*===========================================================================*\ + | Subanim & References support +\*===========================================================================*/ + +int plPassMtl::NumSubs() +{ + return 6; +} + +TSTR plPassMtl::SubAnimName(int i) +{ + switch (i) + { + case 0: return fBasicPB->GetLocalName(); + case 1: return fAdvPB->GetLocalName(); + case 2: return fLayersPB->GetLocalName(); + case 3: return fAnimPB->GetLocalName(); + case 4: return "Base Layer"; + case 5: return "Top Layer"; + } + + return ""; +} + +Animatable* plPassMtl::SubAnim(int i) +{ + switch (i) + { + case 0: return fBasicPB; + case 1: return fAdvPB; + case 2: return fLayersPB; + case 3: return fAnimPB; + case 4: return fLayersPB->GetTexmap(kPassLayBase); + case 5: + if (fLayersPB->GetInt(kPassLayTopOn)) + return fLayersPB->GetTexmap(kPassLayTop); + break; + } + + return NULL; +} + +int plPassMtl::NumParamBlocks() +{ + return 4; +} + +IParamBlock2* plPassMtl::GetParamBlock(int i) +{ + return (IParamBlock2*)GetReference(i); +} + +IParamBlock2* plPassMtl::GetParamBlockByID(BlockID id) +{ + if (fBasicPB->ID() == id) + return fBasicPB; + else if (fAdvPB->ID() == id) + return fAdvPB; + else if (fLayersPB->ID() == id) + return fLayersPB; + else if (fAnimPB->ID() == id) + return fAnimPB; + + return NULL; +} + +RefResult plPassMtl::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) +{ + return plPassMtlBase::NotifyRefChanged( changeInt, hTarget, partID, message ); +} + +//////////////////////////////////////////////////////////////////////////////// +// Subtexmap access + +int plPassMtl::NumSubTexmaps() +{ + return 2; +} + +Texmap* plPassMtl::GetSubTexmap(int i) +{ + if (i == 0) + return fLayersPB->GetTexmap(kPassLayBase); + else if (i == 1) + return fLayersPB->GetTexmap(kPassLayTop); + + return NULL; +} + +void plPassMtl::SetSubTexmap(int i, Texmap *m) +{ + if (i == 0) + fLayersPB->SetValue(kPassLayBase, 0, m); + else if (i == 1) + fLayersPB->SetValue(kPassLayTop, 0, m); +} + +TSTR plPassMtl::GetSubTexmapSlotName(int i) +{ + if (i == 0) + return "Base"; + else if (i == 1) + return "Top"; + + return ""; +} + +TSTR plPassMtl::GetSubTexmapTVName(int i) +{ + return GetSubTexmapSlotName(i); +} + +int plPassMtl::SubTexmapOn(int i) +{ + if (i == 0) + return 1; + else if (i == 1 && fLayersPB->GetInt(kPassLayTopOn)) + return 1; + + return 0; +} + + +/*===========================================================================*\ + | Updating and cloning +\*===========================================================================*/ + +RefTargetHandle plPassMtl::Clone(RemapDir &remap) +{ + plPassMtl *mnew = TRACKED_NEW plPassMtl(FALSE); + plPassMtlBase::ICloneBase( mnew, remap ); + return (RefTargetHandle)mnew; +} + +void plPassMtl::ICloneRefs( plPassMtlBase *target, RemapDir &remap ) +{ + target->ReplaceReference(kRefBasic, remap.CloneRef(fBasicPB)); + target->ReplaceReference(kRefAdv, remap.CloneRef(fAdvPB)); + target->ReplaceReference(kRefLayers, remap.CloneRef(fLayersPB)); + target->ReplaceReference(kRefAnim, remap.CloneRef(fAnimPB)); +} + +void plPassMtl::NotifyChanged() +{ + NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); +} + +void plPassMtl::Update(TimeValue t, Interval& valid) +{ + // mf horse - Hacking in something like real validity checking + // to get material animations working. No warranty, this is just + // better than nothing. + if (!fIValid.InInterval(t)) + { + fIValid.SetInfinite(); + if( fLayersPB->GetTexmap(kPassLayBase) ) + fLayersPB->GetTexmap(kPassLayBase)->Update(t, fIValid); + if( fLayersPB->GetTexmap(kPassLayTop) ) + fLayersPB->GetTexmap(kPassLayTop)->Update(t, fIValid); + +// fLayersPB->GetValue(kMtlLayLayer1On, t, fMapOn[0], fIValid); +/* + for (int i = 0; i < fSubTexmap.Count(); i++) + { + if (fSubTexmap[i]) + fSubTexmap[i]->Update(t,fIValid); + } +*/ + } + + // Our wonderful way of version handling--if the runtimeColor is (-1,-1,-1), we know it's + // just been initialized, so set it to the static color (this lets us do the right thing for + // loading old paramBlocks) + if( fBasicPB ) + { + Color run = fBasicPB->GetColor( kPassBasRunColor, 0 ); + if( run == Color(-1,-1,-1) ) + { + fBasicPB->SetValue( kPassBasRunColor, 0, fBasicPB->GetColor( kPassBasColor, 0 ) ); + } + + // Also, if shineStr is anything other than -1, then it must be an old paramblock and we need + // to convert to our new specColor (we know this because the original valid range was 0-100) + int shine = fBasicPB->GetInt( kPassBasShineStr, 0 ); + if( shine != -1 ) + { + fBasicPB->SetValue( kPassBasSpecColor, 0, Color( (float)shine / 100.f, (float)shine / 100.f, (float)shine / 100.f ) ); + fBasicPB->SetValue( kPassBasShineStr, 0, (int)-1 ); + } + } + + valid &= fIValid; +} + +/*===========================================================================*\ + | Determine the characteristics of the material +\*===========================================================================*/ + +void plPassMtl::SetAmbient(Color c, TimeValue t) {} +void plPassMtl::SetDiffuse(Color c, TimeValue t) {} +void plPassMtl::SetSpecular(Color c, TimeValue t) {} +void plPassMtl::SetShininess(float v, TimeValue t) {} + +Color plPassMtl::GetAmbient(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plPassMtl::GetDiffuse(int mtlNum, BOOL backFace) { return Color(0,0,0); } +Color plPassMtl::GetSpecular(int mtlNum, BOOL backFace) { return Color(0,0,0); } + +float plPassMtl::GetXParency(int mtlNum, BOOL backFace) +{ + int opacity = fBasicPB->GetInt( kPassBasOpacity, 0 ); + float alpha = 1.0f - ( (float)opacity / 100.0f ); + + return alpha; +} + +float plPassMtl::GetShininess(int mtlNum, BOOL backFace) { return 0.0f; } +float plPassMtl::GetShinStr(int mtlNum, BOOL backFace) { return 0.0f; } +float plPassMtl::WireSize(int mtlNum, BOOL backFace) { return 0.0f; } + +///////////////////////////////////////////////////////////////// + +void plPassMtl::SetupGfxMultiMaps(TimeValue t, Material *mtl, MtlMakerCallback &cb) +{ +#if 0 + if (texHandleValid.InInterval(t)) { + mtl->texture.SetCount(numTexHandlesUsed); + for (int i=0; itexture[i].textHandle = texHandle[i]->GetHandle(); + Texmap *tx = (*maps)[useSubForTex[i]].map; + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[i], tx ); + SetTexOps(mtl,i,texOpsType[i]); + } + } + return; + } +#endif + +#if 0 // WTF?!?!?!? + Texmap *tx[2]; + int diffChan = stdIDToChannel[ ID_DI ]; + int opacChan = stdIDToChannel[ ID_OP ]; + tx[0] = (*maps)[diffChan].IsActive()?(*maps)[diffChan].map:NULL; + tx[1] = (*maps)[opacChan].IsActive()?(*maps)[opacChan].map:NULL; +#endif + + int nsupport = cb.NumberTexturesSupported(); +#if 0 + BITMAPINFO *bmi[NTEXHANDLES]; + + int nmaps=0; + for (int i=0; itexture.SetCount(nmaps); + if (nmaps==0) + return; + for (i=0; itexture[i].textHandle = NULL; + texHandleValid.SetInfinite(); + Interval valid; + BOOL needDecal = FALSE; + int ntx = 0; + int op; + + int forceW = 0; + int forceH = 0; + if (tx[0]) { + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[0], tx[0]); + TextureInfo &ti = mtl->texture[0]; + if (ti.tiling[0]==GW_TEX_NO_TILING||ti.tiling[1]==GW_TEX_NO_TILING) + needDecal = TRUE; + op = needDecal?TXOP_ALPHABLEND:TXOP_MODULATE; + bmi[0] = tx[0]->GetVPDisplayDIB(t,cb,valid,FALSE); + if (bmi[0]) { + texHandleValid &= valid; + useSubForTex[0] = diffChan; + ntx = 1; + forceW = bmi[0]->bmiHeader.biWidth; + forceH = bmi[0]->bmiHeader.biHeight; + } + } + if (tx[1]) { + cb.GetGfxTexInfoFromTexmap(t, mtl->texture[ntx], tx[1]); + if (nsupport>ntx) { + bmi[1] = tx[1]->GetVPDisplayDIB(t,cb,valid,TRUE); + if (bmi[1]) { + texHandleValid &= valid; + StuffAlpha(bmi[1], (*maps)[opacChan].amount, GetOpacity(t),ntx?whiteCol:pShader->GetDiffuseClr(t)); + texHandle[ntx] = cb.MakeHandle(bmi[1]); + bmi[1] = NULL; + mtl->texture[ntx].textHandle = texHandle[ntx]->GetHandle(); + SetTexOps(mtl,ntx,TXOP_OPACITY); + useSubForTex[ntx] = opacChan; + ntx++; + } + } + else { + if (!needDecal) { + TextureInfo ti; +// if (SameUV(mtl->texture[0],mtl->texture[1])) { + // Not really correct to combine channels for different UV's but what the heck. + bmi[1] = tx[1]->GetVPDisplayDIB(t,cb,valid,TRUE, forceW, forceH); + if (bmi[1]) { + texHandleValid &= valid; + StuffAlphaInto(bmi[1], bmi[0], (*maps)[opacChan].amount, GetOpacity(t)); + op = TXOP_OPACITY; + free(bmi[1]); + bmi[1] = NULL; + } +// } + } + } + } + if (bmi[0]) { + texHandle[0] = cb.MakeHandle(bmi[0]); + bmi[0] = NULL; + mtl->texture[0].textHandle = texHandle[0]->GetHandle(); + SetTexOps(mtl,0,op); + } + mtl->texture.SetCount(ntx); + numTexHandlesUsed = ntx; +#endif +} + +/*===========================================================================*\ + | Actual shading takes place +\*===========================================================================*/ + +void plPassMtl::GetInterpVtxValue(int channel, ShadeContext &sc, Point3 &val) +{ + Mesh *mesh = sc.globContext->GetRenderInstance(sc.NodeID())->mesh; + if (mesh != nil) + { + Face *maxFace = &mesh->faces[ sc.FaceNumber() ]; + UVVert *map = mesh->mapVerts(channel); + if (map != nil) + { + Point3 p0 = map[maxFace->getVert( 0 )]; + Point3 p1 = map[maxFace->getVert( 1 )]; + Point3 p2 = map[maxFace->getVert( 2 )]; + Point3 interp = sc.BarycentricCoords(); + val.x = interp.x * p0.x + interp.y * p1.x + interp.z * p2.x; + val.y = interp.x * p0.y + interp.y * p1.y + interp.z * p2.y; + val.z = interp.x * p0.z + interp.y * p1.z + interp.z * p2.z; + return; + } + } + + // No value defined... set default. + if (channel == MAP_SHADING) + val.x = val.y = val.z = 0.0f; + else + val.x = val.y = val.z = 1.0f; +} + +void plPassMtl::Shade(ShadeContext& sc) +{ + // Get the background color + Color backColor, backTrans; + sc.GetBGColor(backColor, backTrans); + + ShadeWithBackground(sc, backColor); +} + +//// Requirements //////////////////////////////////////////////////////////// +// Tells MAX what we need to render ourselves properly, such as translucency, +// two-sidedness, etc. Flags are in imtl.h in the MAX SDK. + +ULONG plPassMtl::Requirements( int subMtlNum ) +{ + ULONG req = 0; + + + req = Mtl::Requirements( subMtlNum ); + + // Uncomment this to get the background color fed to our ShadeWithBackground() + // (slower processing tho) + //req |= MTLREQ_BGCOL; + req |= MTLREQ_UV; + + int blendType = fLayersPB->GetInt( kPassLayOutputBlend ); + if( blendType == kBlendAdd ) + req |= MTLREQ_ADDITIVE_TRANSP | MTLREQ_TRANSP; + else if( blendType == kBlendAlpha ) + req |= MTLREQ_TRANSP; + else if( fBasicPB->GetInt( kPassBasOpacity, 0 ) != 100 ) + req |= MTLREQ_TRANSP; + + if( fAdvPB->GetInt( kPBAdvTwoSided ) ) + req |= MTLREQ_2SIDE; + + if (req & MTLREQ_FACEMAP) + { + int i = 0; + } + return req; +} + +void plPassMtl::ShadeWithBackground(ShadeContext &sc, Color background, bool useVtxAlpha /* = true */) +{ +#if 1 + + // old +#if 0 + Color lightCol,rescol, diffIllum0; + RGBA mval; + Point3 N0,P; + BOOL bumped = FALSE; + int i; + + if (gbufID) + sc.SetGBufferID(gbufID); + + if (sc.mode == SCMODE_SHADOW) { + float opac = 0.0; + for (i=0; i < NumSubTexmaps(); i++) { + if (SubTexmapOn(i)) { + hsMaxLayerBase *hsmLay = (hsMaxLayerBase *)GetSubTexmap(i); + opac += hsmLay->GetOpacity(t); + } + } + + float f = 1.0f - opac; + sc.out.t = Color(f,f,f); + return; + } + + N0 = sc.Normal(); + P = sc.P(); +#endif + + TimeValue t = sc.CurTime(); + Color color(0, 0, 0); + float alpha = 0.0; + + // Evaluate Base layer + Texmap *map = fLayersPB->GetTexmap(kPassLayBase); + if (map && ( map->ClassID() == LAYER_TEX_CLASS_ID + || map->ClassID() == STATIC_ENV_LAYER_CLASS_ID ) ) + { + plLayerTex *layer = (plLayerTex*)map; + AColor evalColor = layer->EvalColor(sc); + + color = evalColor; + alpha = evalColor.a; + } + + // Evaluate Top layer, if it's on + if (fLayersPB->GetInt(kPassLayTopOn)) + { + Texmap *map = fLayersPB->GetTexmap(kPassLayTop); + if (map && ( map->ClassID() == LAYER_TEX_CLASS_ID + || map->ClassID() == STATIC_ENV_LAYER_CLASS_ID + || map->ClassID() == ANGLE_ATTEN_LAYER_CLASS_ID) ) + { + plPlasmaMAXLayer *layer = (plPlasmaMAXLayer*)map; + AColor evalColor = layer->EvalColor(sc); + + // Blend layers + if( !layer->DiscardColor() ) + { + int blendType = fLayersPB->GetInt(kPassLayBlend); + switch (blendType) + { + case kBlendAdd: + color += evalColor * evalColor.a; + break; + case kBlendAlpha: + color = (1.0f - evalColor.a) * color + evalColor.a * evalColor; + break; + case kBlendMult: + color *= evalColor; + break; + default: // No blend... + color = evalColor; + break; + } + } + if( !layer->DiscardAlpha() ) + { + int alphaType = fLayersPB->GetInt(kPassLayOutputBlend); + switch( alphaType ) + { + case kAlphaMultiply: + alpha *= evalColor.a; + break; + case kAlphaAdd: + alpha += evalColor.a; + break; + case kAlphaDiscard: + default: + break; + } + } + } + } + +#if 1 + AColor black; + black.Black(); + AColor white; + white.White(); + + + SIllumParams ip; + if (fBasicPB->GetInt(kPassBasEmissive)) + { + // Emissive objects don't get shaded + ip.diffIllum = fBasicPB->GetColor(kPassBasColorAmb, t) * color; + ip.diffIllum.ClampMinMax(); + ip.specIllum = black; + } + else + { + // + // Shading setup + // + + // Setup the parameters for the shader + ip.amb = fBasicPB->GetColor(kPassBasColorAmb, t); + ip.diff = fBasicPB->GetColor(kPassBasColor, t) * color; + ip.diffIllum = black; + ip.specIllum = black; + ip.N = sc.Normal(); + ip.V = sc.V(); + + + // + // Specularity + // + if (fBasicPB->GetInt(kPassBasUseSpec, t)) + { + ip.sh_str = 1.f; + ip.spec = fBasicPB->GetColor( kPassBasSpecColor, t ); + ip.ph_exp = (float)pow(2.0f,float(fBasicPB->GetInt(kPassBasShine, t)) / 10.0f); + ip.shine = float(fBasicPB->GetInt(kPassBasShine, t)) / 100.0f; + } + else + { + ip.spec = black; + ip.sh_str = 0; + ip.ph_exp = 0; + ip.shine = 0; + } + ip.softThresh = 0; + + // + + // Do the shading + Shader *myShader = GetShader(SHADER_BLINN); + myShader->Illum(sc, ip); + + // Override shader parameters + if (fAdvPB->GetInt(kPBAdvNoShade)) + { + ip.diffIllum = black; + ip.specIllum = black; + } + if (fAdvPB->GetInt(kPBAdvWhite)) + { + ip.diffIllum = white; + ip.specIllum = black; + } + + ip.specIllum.ClampMinMax(); + ip.diffIllum = ip.amb * sc.ambientLight + ip.diff * ip.diffIllum; + ip.diffIllum.ClampMinMax(); + } + +// AColor returnColor = AColor(opac * ip.diffIllum + ip.specIllum, opac) +#endif + + // Get opacity and combine with alpha + float opac = float(fBasicPB->GetInt(kPassBasOpacity, t)) / 100.0f; + alpha *= opac; + + float vtxAlpha = 1.0f; + if (useVtxAlpha && GetOutputBlend() == plPassMtlBase::kBlendAlpha) + { + Point3 p; + GetInterpVtxValue(MAP_ALPHA, sc, p); + vtxAlpha = p.x; + } + alpha *= vtxAlpha; + + // MAX will do the additive/alpha/no blending for us based on what Requirements() + // we tell it. However, since MAX's formula is bgnd*sc.out.t + sc.out.c, + // we have to multiply our output color by the alpha. + // If we ever need a more complicated blending function, you can request the + // background color via Requirements() (otherwise it's just black) and then do + // the blending yourself; however, if the transparency isn't set, the shadows + // will be opaque, so be careful. + Color outC = ip.diffIllum + ip.specIllum; + + sc.out.c = ( outC * alpha ); + sc.out.t = Color( 1.f - alpha, 1.f - alpha, 1.f - alpha ); + +#endif +} + +float plPassMtl::EvalDisplacement(ShadeContext& sc) +{ + return 0.0f; +} + +Interval plPassMtl::DisplacementValidity(TimeValue t) +{ + Interval iv; + iv.SetInfinite(); + + return iv; +} + +bool plPassMtl::HasAlpha() +{ + return ((plLayerTex *)fLayersPB->GetTexmap(kPassLayBase))->HasAlpha(); +} +// Massive list of inherited accessor functions for ParamBlock data + +// Advanced Block +int plPassMtl::GetBasicWire() { return fAdvPB->GetInt(kPBAdvWire); } +int plPassMtl::GetMeshOutlines() { return fAdvPB->GetInt(kPBAdvMeshOutlines); } +int plPassMtl::GetTwoSided() { return fAdvPB->GetInt(kPBAdvTwoSided); } +int plPassMtl::GetSoftShadow() { return fAdvPB->GetInt(kPBAdvSoftShadow); } +int plPassMtl::GetNoProj() { return fAdvPB->GetInt(kPBAdvNoProj); } +int plPassMtl::GetVertexShade() { return fAdvPB->GetInt(kPBAdvVertexShade); } +int plPassMtl::GetNoShade() { return fAdvPB->GetInt(kPBAdvNoShade); } +int plPassMtl::GetNoFog() { return fAdvPB->GetInt(kPBAdvNoFog); } +int plPassMtl::GetWhite() { return fAdvPB->GetInt(kPBAdvWhite); } +int plPassMtl::GetZOnly() { return fAdvPB->GetInt(kPBAdvZOnly); } +int plPassMtl::GetZClear() { return fAdvPB->GetInt(kPBAdvZClear); } +int plPassMtl::GetZNoRead() { return fAdvPB->GetInt(kPBAdvZNoRead); } +int plPassMtl::GetZNoWrite() { return fAdvPB->GetInt(kPBAdvZNoWrite); } +int plPassMtl::GetZInc() { return fAdvPB->GetInt(kPBAdvZInc); } +int plPassMtl::GetAlphaTestHigh() { return fAdvPB->GetInt(kPBAdvAlphaTestHigh); } + +// Animation block +char * plPassMtl::GetAnimName() { return fAnimPB->GetStr(kPBAnimName); } +int plPassMtl::GetAutoStart() { return fAnimPB->GetInt(kPBAnimAutoStart); } +int plPassMtl::GetLoop() { return fAnimPB->GetInt(kPBAnimLoop); } +char * plPassMtl::GetAnimLoopName() { return fAnimPB->GetStr(kPBAnimLoopName); } +int plPassMtl::GetEaseInType() { return fAnimPB->GetInt(kPBAnimEaseInType); } +float plPassMtl::GetEaseInNormLength() { return fAnimPB->GetFloat(kPBAnimEaseInLength); } +float plPassMtl::GetEaseInMinLength() { return fAnimPB->GetFloat(kPBAnimEaseInMin); } +float plPassMtl::GetEaseInMaxLength() { return fAnimPB->GetFloat(kPBAnimEaseInMax); } +int plPassMtl::GetEaseOutType() { return fAnimPB->GetInt(kPBAnimEaseOutType); } +float plPassMtl::GetEaseOutNormLength() { return fAnimPB->GetFloat(kPBAnimEaseOutLength); } +float plPassMtl::GetEaseOutMinLength() { return fAnimPB->GetFloat(kPBAnimEaseOutMin); } +float plPassMtl::GetEaseOutMaxLength() { return fAnimPB->GetFloat(kPBAnimEaseOutMax); } +int plPassMtl::GetUseGlobal() { return fAnimPB->GetInt(ParamID(kPBAnimUseGlobal)); } +char * plPassMtl::GetGlobalVarName() { return fAnimPB->GetStr(ParamID(kPBAnimGlobalName)); } + +// Basic block +int plPassMtl::GetColorLock() { return fBasicPB->GetInt(kPassBasColorLock); } +Color plPassMtl::GetAmbColor() { return fBasicPB->GetColor(kPassBasColorAmb); } +Color plPassMtl::GetColor() { return fBasicPB->GetColor(kPassBasColor); } +int plPassMtl::GetOpacity() { return fBasicPB->GetInt(kPassBasOpacity); } +int plPassMtl::GetEmissive() { return fBasicPB->GetInt(kPassBasEmissive); } +int plPassMtl::GetUseSpec() { return fBasicPB->GetInt(kPassBasUseSpec); } +int plPassMtl::GetShine() { return fBasicPB->GetInt(kPassBasShine); } +Color plPassMtl::GetSpecularColor() { return fBasicPB->GetColor(kPassBasSpecColor); } +int plPassMtl::GetDiffuseColorLock() { return fBasicPB->GetInt(kPassBasDiffuseLock); } +Color plPassMtl::GetRuntimeColor() { return fBasicPB->GetColor(kPassBasRunColor); } +Control *plPassMtl::GetPreshadeColorController() { return fBasicPB->GetController(ParamID(kPassBasColor)); } +Control *plPassMtl::GetAmbColorController() { return fBasicPB->GetController(ParamID(kPassBasColorAmb)); } +Control *plPassMtl::GetOpacityController() { return fBasicPB->GetController(ParamID(kPassBasOpacity)); } +Control *plPassMtl::GetSpecularColorController() { return fBasicPB->GetController(ParamID(kPassBasSpecColor)); } +Control *plPassMtl::GetRuntimeColorController() { return fBasicPB->GetController(ParamID(kPassBasRunColor)); } + +// Layer block +Texmap *plPassMtl::GetBaseLayer() { return fLayersPB->GetTexmap(kPassLayBase); } +int plPassMtl::GetTopLayerOn() { return fLayersPB->GetInt(kPassLayTopOn); } +Texmap *plPassMtl::GetTopLayer() { return fLayersPB->GetTexmap(kPassLayTop); } +int plPassMtl::GetLayerBlend() { return fLayersPB->GetInt(kPassLayBlend); } +int plPassMtl::GetOutputAlpha() { return fLayersPB->GetInt(kPassLayOutputAlpha); } +int plPassMtl::GetOutputBlend() { return fLayersPB->GetInt(kPassLayOutputBlend); } diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtl.h new file mode 100644 index 00000000..c6aab79e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtl.h @@ -0,0 +1,197 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_PASSMTL_H +#define PL_PASSMTL_H + +#include "Max.h" +//#include "istdplug.h" +#include "iparamb2.h" +//#include "iparamm2.h" + +#include "../resource.h" +#include "plPassMtlBase.h" +#include "hsTemplates.h" + +#define PASS_MTL_CLASS_ID Class_ID(0x42b6718f, 0x2d579b35) + +extern TCHAR *GetString(int id); + +class plPassMtl : public plPassMtlBase +{ +protected: + + virtual void ICloneRefs( plPassMtlBase *target, RemapDir &remap ); + +public: + + enum RefIDs + { + kRefBasic, + kRefAdv, + kRefLayers, + kRefAnim, + }; + enum Blocks + { + kBlkBasic, + kBlkAdv, + kBlkLayers, + kBlkAnim, + }; + + plPassMtl(BOOL loading); + virtual ~plPassMtl(); + void DeleteThis() { delete this; } + + //From Animatable + Class_ID ClassID() { return PASS_MTL_CLASS_ID; } + SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; } + void GetClassName(TSTR& s) { s = GetString(IDS_PASS_MTL); } + + ParamDlg *CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); + void Update(TimeValue t, Interval& valid); + Interval Validity(TimeValue t); + + void NotifyChanged(); + + BOOL SupportsMultiMapsInViewport() { return FALSE; } + void SetupGfxMultiMaps(TimeValue t, Material *mtl, MtlMakerCallback &cb); + + // Shade and displacement calculation + static void GetInterpVtxValue(int channel, ShadeContext &sc, Point3 &interpVal); + void Shade(ShadeContext& sc); + void ShadeWithBackground(ShadeContext &sc, Color background, bool useVtxAlpha = true); + float EvalDisplacement(ShadeContext& sc); + Interval DisplacementValidity(TimeValue t); + + virtual RefTargetHandle GetReference( int i ); + virtual void SetReference( int i, RefTargetHandle rtarg ); + + // SubTexmap access methods + int NumSubTexmaps(); + Texmap* GetSubTexmap(int i); + void SetSubTexmap(int i, Texmap *m); + TSTR GetSubTexmapSlotName(int i); + TSTR GetSubTexmapTVName(int i); + int SubTexmapOn(int i); + + BOOL SetDlgThing(ParamDlg* dlg); + + RefTargetHandle Clone( RemapDir &remap ); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumSubs(); + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + int NumParamBlocks(); + IParamBlock2* GetParamBlock(int i); + IParamBlock2* GetParamBlockByID(BlockID id); + + +// void SetParamDlg(ParamDlg *dlg); + +// void SetNumSubTexmaps(int num); + + // From MtlBase and Mtl + void SetAmbient(Color c, TimeValue t); + void SetDiffuse(Color c, TimeValue t); + void SetSpecular(Color c, TimeValue t); + void SetShininess(float v, TimeValue t); + Color GetAmbient(int mtlNum=0, BOOL backFace=FALSE); + Color GetDiffuse(int mtlNum=0, BOOL backFace=FALSE); + Color GetSpecular(int mtlNum=0, BOOL backFace=FALSE); + float GetXParency(int mtlNum=0, BOOL backFace=FALSE); + float GetShininess(int mtlNum=0, BOOL backFace=FALSE); + float GetShinStr(int mtlNum=0, BOOL backFace=FALSE); + float WireSize(int mtlNum=0, BOOL backFace=FALSE); + + ULONG Requirements( int subMtlNum ); + + virtual bool HasAlpha(); + // Massive list of inherited accessor functions for ParamBlock data + + // Advanced Block + virtual int GetBasicWire(); + virtual int GetMeshOutlines(); + virtual int GetTwoSided(); + virtual int GetSoftShadow(); + virtual int GetNoProj(); + virtual int GetVertexShade(); + virtual int GetNoShade(); + virtual int GetNoFog(); + virtual int GetWhite(); + virtual int GetZOnly(); + virtual int GetZClear(); + virtual int GetZNoRead(); + virtual int GetZNoWrite(); + virtual int GetZInc(); + virtual int GetAlphaTestHigh(); + + // Animation block + virtual char * GetAnimName(); + virtual int GetAutoStart(); + virtual int GetLoop(); + virtual char * GetAnimLoopName(); + virtual int GetEaseInType(); + virtual float GetEaseInMinLength(); + virtual float GetEaseInMaxLength(); + virtual float GetEaseInNormLength(); + virtual int GetEaseOutType(); + virtual float GetEaseOutMinLength(); + virtual float GetEaseOutMaxLength(); + virtual float GetEaseOutNormLength(); + virtual int GetUseGlobal(); + virtual char * GetGlobalVarName(); + + // Basic block + virtual int GetColorLock(); + virtual Color GetAmbColor(); + virtual Color GetColor(); + virtual int GetOpacity(); + virtual int GetEmissive(); + virtual int GetUseSpec(); + virtual int GetShine(); + virtual Color GetSpecularColor(); + virtual Control *GetPreshadeColorController(); + virtual Control *GetAmbColorController(); + virtual Control *GetOpacityController(); + virtual Control *GetSpecularColorController(); + virtual int GetDiffuseColorLock(); + virtual Color GetRuntimeColor(); + virtual Control *GetRuntimeColorController(); + + // Layer block + virtual Texmap *GetBaseLayer(); + virtual int GetTopLayerOn(); + virtual Texmap *GetTopLayer(); + virtual int GetLayerBlend(); + virtual int GetOutputAlpha(); + virtual int GetOutputBlend(); +}; + +#endif //PL_PASSMTL_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAdvPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAdvPB.h new file mode 100644 index 00000000..43daa640 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAdvPB.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_PASSMTLADVPB_H +#define PL_PASSMTLADVPB_H + +// Param ID's +enum +{ + // Specular + kPassAdvUseSpec, // Not used anymore, feel free to replace with new fields as necessary + kPassAdvSpecType, // ditto + kPassAdvShine, // ... + kPassAdvShineStr, // ... + + // Misc + kPassAdvWire, + kPassAdvMeshOutlines, + kPassAdvTwoSided, + + // Shading + kPassAdvSoftShadow, + kPassAdvNoProj, + kPassAdvVertexShade, + kPassAdvNoShade, + kPassAdvNoFog, + kPassAdvWhite, + + // Z + kPassAdvZOnly, + kPassAdvZClear, + kPassAdvZNoRead, + kPassAdvZNoWrite, + kPassAdvZInc, +}; + +#endif //PL_PASSMTLADVPB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAdvPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAdvPBDec.h new file mode 100644 index 00000000..5fec759e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAdvPBDec.h @@ -0,0 +1,94 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPassMtl.h" +#include "plPassBaseParamIDs.h" + +using namespace plPassBaseParamIDs; + +static ParamBlockDesc2 gPassAdvPB +( + plPassMtl::kBlkAdv, _T("advanced"), IDS_PASS_ADV, GetPassMtlDesc(), + P_AUTO_CONSTRUCT + P_AUTO_UI, plPassMtl::kRefAdv, + + // UI + IDD_PASS_ADV, IDS_PASS_ADV, 0, APPENDROLL_CLOSED, NULL, + + // Misc Properties + kPBAdvWire, _T("basicWire"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_MISC_WIRE, + end, + kPBAdvMeshOutlines, _T("meshOutlines"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_MISC_MESHOUTLINES, + end, + kPBAdvTwoSided, _T("twoSided"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_MISC_TWOSIDED, + end, + + // Shade properties + kPBAdvSoftShadow, _T("softShadow"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_SOFTSHADOW, + end, + kPBAdvNoProj, _T("noProj"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_NO_PROJ, + end, + kPBAdvVertexShade, _T("vertexShade"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_VERTEXSHADE, + end, + kPBAdvNoShade, _T("noShade"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_NOSHADE, + end, + kPBAdvNoFog, _T("noFog"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_NO_FOG, + end, + kPBAdvWhite, _T("white"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_WHITE, + end, + + // Z Properties + kPBAdvZOnly, _T("zOnly"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_Z_ZONLY, + end, + kPBAdvZClear, _T("zClear"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_Z_ZCLEAR, + end, + kPBAdvZNoRead, _T("zNoRead"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_Z_ZNOREAD, + end, + kPBAdvZNoWrite, _T("zNoWrite"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_Z_ZNOWRITE, + end, + kPBAdvZInc, _T("zInc"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_Z_INC, + end, + + kPBAdvAlphaTestHigh, _T("aTestHigh"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_ALPHA_TEST_HIGH, + p_default, FALSE, + end, + + end +); +ParamBlockDesc2 *GetPassAdvPB() { return &gPassAdvPB; } \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAnimPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAnimPB.h new file mode 100644 index 00000000..ff316c9e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAnimPB.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_PASSMTLANIMPB_H +#define PL_PASSMTLANIMPB_H + +enum { kPassAnimMain, kPassAnimEase }; + +#define WM_ROLLOUT_OPEN WM_USER+1 + +class plAnimEaseDlgProc : public ParamMap2UserDlgProc +{ +protected: + void EnableStopPoints(IParamMap2 *pm, bool enable) + { + pm->Enable(kPassEaseInMin, enable); + pm->Enable(kPassEaseInMax, enable); + pm->Enable(kPassEaseOutMin, enable); + pm->Enable(kPassEaseOutMax, enable); + } + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_INITDIALOG: + { + IParamBlock2 *pb = map->GetParamBlock(); + + bool stopPoints = false; + if (DoesHaveStopPoints(pb->GetOwner())) + { + stopPoints = true; + break; + } + + EnableStopPoints(map, stopPoints); + + // If we're doing an ease, set the ease rollup to open + // if (pb->GetInt(kPassEaseInType) != plAnimEaseTypes::kNoEase || + // pb->GetInt(kPassEaseOutType) != plAnimEaseTypes::kNoEase) + // PostMessage(hWnd, WM_ROLLOUT_OPEN, 0, 0); + } + return TRUE; + + // Max doesn't know about the rollup until after WM_CREATE, so we get + // around it by posting a message + //case WM_ROLLOUT_OPEN: + // { + // IRollupWindow *rollup = GetCOREInterface(MTLEDIT_INTERFACE)->GetCommandPanelRollup(); + // int idx = rollup->GetPanelIndex(hWnd); + // rollup->SetPanelOpen(idx, TRUE); + // } + // return TRUE; + } + return FALSE; + } + + void DeleteThis() {} +}; + +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAnimPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAnimPBDec.h new file mode 100644 index 00000000..1526f20e --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlAnimPBDec.h @@ -0,0 +1,95 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPassMtl.h" +#include "plPassBaseParamIDs.h" +#include "resource.h" +#include "iparamm2.h" + +#include "plPassAnimDlgProc.h" +#include "plAnimStealthNode.h" + +using namespace plPassBaseParamIDs; + +static ParamBlockDesc2 gPassAnimPB +( + plPassMtl::kBlkAnim, _T("anim"), IDS_PASS_ANIM, GetPassMtlDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI + P_CALLSETS_ON_LOAD, plPassMtl::kRefAnim, + + // UI + IDD_PASS_ANIM, IDS_PASS_ANIM, 0, 0, &plPassAnimDlgProc::Get(), + +#ifdef MCN_UPGRADE_OLD_ANIM_BLOCKS + // THE FOLLOWING ARE ALL OLD PARAMETERS AND SHOULD NO LONGER BE USED. The only reason + // they're here is so we can convert old paramBlocks into the new plAnimStealthNode format + kPBAnimName, _T("animName"), TYPE_STRING, 0, 0, + end, + + kPBAnimAutoStart, _T("autoStart"), TYPE_BOOL, 0, 0, + end, + + kPBAnimLoop, _T("loop"), TYPE_BOOL, 0, 0, + end, + kPBAnimLoopName, _T("loopName"), TYPE_STRING, 0, 0, + end, + + // Anim Ease + kPBAnimEaseInType, _T("easeInType"), TYPE_INT, 0, 0, + end, + kPBAnimEaseInLength, _T("easeInLength"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseInMin, _T("easeInMin"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseInMax, _T("easeInMax"), TYPE_FLOAT, 0, 0, + end, + + kPBAnimEaseOutType, _T("easeOutType"), TYPE_INT, 0, 0, + end, + kPBAnimEaseOutLength, _T("easeOutLength"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseOutMin, _T("easeOutMin"), TYPE_FLOAT, 0, 0, + end, + kPBAnimEaseOutMax, _T("easeOutMax"), TYPE_FLOAT, 0, 0, + end, + +#endif // MCN_UPGRADE_OLD_ANIM_BLOCKS + + kPBAnimUseGlobal, _T("UseGlobal"), TYPE_BOOL, 0, 0, + p_default, FALSE, + p_ui, TYPE_SINGLECHEKBOX, IDC_MTL_USE_GLOBAL, + end, + kPBAnimGlobalName, _T("GlobalName"), TYPE_STRING, 0, 0, + p_default, _T(""), + end, + + kPBAnimStealthNodes, _T( "testing" ), TYPE_REFTARG_TAB, 0, 0, 0, + p_accessor, &plStealthNodeAccessor::GetInstance(), + end, + + end + +); +ParamBlockDesc2 *GetPassAnimPB() { return &gPassAnimPB; } + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBase.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBase.cpp new file mode 100644 index 00000000..dae1d883 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBase.cpp @@ -0,0 +1,660 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plPassMtlBase - Base class for all Plasma MAX materials // +// // +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsBitVector.h" + +#include "Max.h" +#include "iparamb2.h" +#include "notify.h" +#include "notetrck.h" + +#include "plPassMtlBase.h" +#include "plPassBaseParamIDs.h" +#include "plNoteTrackWatcher.h" +#include "plAnimStealthNode.h" + +#include "../MaxComponent/plMaxAnimUtils.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +// For converting from a MAX Mtl +#include "plPassMtl.h" +#include "plBumpMtl.h" +#include "plDecalMtl.h" + +using namespace plPassBaseParamIDs; + +IMtlParams *plPassMtlBase::fIMtlParams = nil; + +//// plPostLoadHandler /////////////////////////////////////////////////////// +// Small class to keep track of all the materials to update after load + +class plPostLoadHandler +{ + static bool fLoading; + static hsTArray fPostLoads; + + public: + static bool IsLoading() { return fLoading; } + + static void PostLoadFixupFunction( void *param, NotifyInfo *info ) + { + fLoading = false; + + for( int i = 0; i < fPostLoads.GetCount(); i++ ) + fPostLoads[ i ]->PostLoadAnimPBFixup(); + + fPostLoads.Reset(); + UnRegisterNotification( PostLoadFixupFunction, param, NOTIFY_FILE_POST_OPEN ); + UnRegisterNotification( PostLoadFixupFunction, param, NOTIFY_FILE_POST_MERGE ); + } + + static void AddPostLoad( plPassMtlBase *mtl ) + { + fLoading = true; + + if( fPostLoads.GetCount() == 0 ) + { + RegisterNotification( PostLoadFixupFunction, mtl, NOTIFY_FILE_POST_OPEN ); + RegisterNotification( PostLoadFixupFunction, mtl, NOTIFY_FILE_POST_MERGE ); + } + + mtl->SetLoadingFlag( true ); + fPostLoads.Append( mtl ); + } + + static void RemovePostLoad( plPassMtlBase *mtl ) + { + for( int i = 0; i < fPostLoads.GetCount(); i++ ) + { + if( fPostLoads[ i ] == mtl ) + { + fPostLoads.Remove( i ); + break; + } + } + } +}; +hsTArray plPostLoadHandler::fPostLoads; +bool plPostLoadHandler::fLoading = false; + + +plPassMtlBase::plPassMtlBase( BOOL loading ) : fNTWatcher( nil ), fBasicPB(NULL), fAdvPB(NULL), fLayersPB(NULL), fAnimPB(NULL), + fLoading( loading ) +{ + fNTWatcher = TRACKED_NEW plNoteTrackWatcher( this ); + Reset(); +} + +plPassMtlBase::~plPassMtlBase() +{ + if( fLoading ) + plPostLoadHandler::RemovePostLoad( this ); + + // Force the watcher's parent pointer to nil, otherwise the de-ref will attempt to re-delete us + fNTWatcher->SetReference( plNoteTrackWatcher::kRefParentMtl, nil ); + delete fNTWatcher; + fNTWatcher = nil; + + // Manually delete our notetrack refs, otherwise there'll be hell to pay + for( int i = 0; i < fNotetracks.GetCount(); i++ ) + { + if( fNotetracks[ i ] != nil ) + DeleteReference( kRefNotetracks + i ); + } +} + +void plPassMtlBase::Reset( void ) +{ + fIValid.SetEmpty(); +} + +//// Stealth Accessors /////////////////////////////////////////////////////// + +int plPassMtlBase::GetNumStealths( void ) +{ + return IGetNumStealths( true ); +} + +plAnimStealthNode *plPassMtlBase::GetStealth( int index ) +{ + return IGetStealth( index, false ); +} + +int plPassMtlBase::IGetNumStealths( hsBool update ) +{ + if( update ) + IUpdateAnimNodes(); + + return fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); +} + +plAnimStealthNode *plPassMtlBase::IGetStealth( int index, hsBool update ) +{ + if( update ) + IUpdateAnimNodes(); + + return (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, index ); +} + +plAnimStealthNode *plPassMtlBase::IFindStealth( const char *segmentName ) +{ + int i; + + + for( i = 0; i < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); i++ ) + { + plAnimStealthNode *node = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, i ); + const char *name = node->GetSegmentName(); + + if( node != nil && strcmp( name, segmentName ) == 0 ) + { + return node; + } + } + + return nil; +} + +//// IVerifyStealthPresent /////////////////////////////////////////////////// +// Ensures that we have a stealth for the given segment. + +plAnimStealthNode *plPassMtlBase::IVerifyStealthPresent( const char *animName ) +{ + // If we're in the middle of loading, don't check + if (plPostLoadHandler::IsLoading()) + return nil; + + plAnimStealthNode *stealth = IFindStealth( animName ); + if( stealth == nil ) + { + // New segment, add a new stealth node + stealth = (plAnimStealthNode *)GetCOREInterface()->CreateInstance( HELPER_CLASS_ID, ANIMSTEALTH_CLASSID ); + INode *node = GetCOREInterface()->CreateObjectNode( stealth ); + stealth->SetSegment( ( strcmp(animName, ENTIRE_ANIMATION_NAME) != 0 ) ? animName : nil ); + stealth->SetNodeName( GetName() ); + node->Freeze( true ); + + // Skip the attach, since we might not find a valid INode. This will leave the node attached to the scene + // root, which is fine. Since we just care about it being SOMEWHERE in the scene hierarchy + /* + if( fAnimPB->Count( (ParamID)kPBAnimStealthNodes ) > 0 ) + { + plAnimStealthNode *first = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, 0 ); + first->GetINode()->AttachChild( node ); + } + */ + + fAnimPB->Append( (ParamID)kPBAnimStealthNodes, 1, (ReferenceTarget **)&stealth ); + + const char *realName = stealth->GetSegmentName(); + + fStealthsChanged = true; + } + else + { + // Exists already, we're ok + stealth->SetParentMtl( this ); + } + return stealth; +} + +//// Change Callbacks //////////////////////////////////////////////////////// + +void plPassMtlBase::RegisterChangeCallback( plMtlChangeCallback *callback ) +{ + if( fChangeCallbacks.Find( callback ) == fChangeCallbacks.kMissingIndex ) + fChangeCallbacks.Append( callback ); +} + +void plPassMtlBase::UnregisterChangeCallback( plMtlChangeCallback *callback ) +{ + int idx = fChangeCallbacks.Find( callback ); + if( idx != fChangeCallbacks.kMissingIndex ) + fChangeCallbacks.Remove( idx ); +} + +//// IUpdateAnimNodes //////////////////////////////////////////////////////// +// Updates the list of stealth nodes in the anim paramBlock to match our +// list of anim segments. + +void plPassMtlBase::IUpdateAnimNodes( void ) +{ + // Our beautiful hack, to make sure we don't update until we actually are loaded + if( fLoading ) + return; + + SegmentMap *segMap = GetAnimSegmentMap( this, nil ); + + hsTArray goodNodes; + + + // Keep track of whether we change anything + fStealthsChanged = false; + + // Verify one for "entire animation" + plAnimStealthNode *stealth = IVerifyStealthPresent( ENTIRE_ANIMATION_NAME ); + goodNodes.Append( stealth ); + + // Verify segment nodes + if( segMap != nil ) + { + for( SegmentMap::iterator i = segMap->begin(); i != segMap->end(); i++ ) + { + SegmentSpec *spec = (*i).second; + + if( spec->fType == SegmentSpec::kAnim ) + { + plAnimStealthNode *stealth = IVerifyStealthPresent( spec->fName ); + goodNodes.Append( stealth ); + } + } + + DeleteSegmentMap( segMap ); + } + + // Remove nodes that no longer have segments + int idx; + for( idx = 0; idx < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); ) + { + plAnimStealthNode *node = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, idx ); + + if( node != nil && goodNodes.Find( node ) == goodNodes.kMissingIndex ) + { + fAnimPB->Delete( (ParamID)kPBAnimStealthNodes, idx, 1 ); +// GetCOREInterface()->DeleteNode( node->GetINode() ); + fStealthsChanged = true; + } + else + idx++; + } + + if( fStealthsChanged ) + { + // Yup, our list of stealths got updated. Notify everyone of such. + for( idx = 0; idx < fChangeCallbacks.GetCount(); idx++ ) + fChangeCallbacks[ idx ]->SegmentListChanged(); + } +} + +//// NameChanged ///////////////////////////////////////////////////////////// +// Notify from NTWatcher so we can update the names of our stealth nodes + +void plPassMtlBase::NameChanged( void ) +{ + for( int idx = 0; idx < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); idx++ ) + { + plAnimStealthNode *node = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, idx ); + if( node != nil ) + node->SetNodeName( GetName() ); + } +} + +//// NoteTrackAdded/Removed ////////////////////////////////////////////////// +// Notifies from NTWatcher so we can update our list of stealths + +void plPassMtlBase::NoteTrackAdded( void ) +{ + int i; + + + // Make a ref to our new notetrack + for( i = 0; i < NumNoteTracks(); i++ ) + { + NoteTrack *track = GetNoteTrack( i ); + + if( fNotetracks.Find( track ) == fNotetracks.kMissingIndex ) + { + MakeRefByID( FOREVER, kRefNotetracks + fNotetracks.GetCount(), track ); + break; + } + } + + for( i = 0; i < fChangeCallbacks.GetCount(); i++ ) + fChangeCallbacks[ i ]->NoteTrackListChanged(); + IUpdateAnimNodes(); +} + +void plPassMtlBase::NoteTrackRemoved( void ) +{ + int i; + hsBitVector stillThere; + + + // Make a ref to our new notetrack + for( i = 0; i < NumNoteTracks(); i++ ) + { + NoteTrack *track = GetNoteTrack( i ); + + int idx = fNotetracks.Find( track ); + if( idx != fNotetracks.kMissingIndex ) + stillThere.Set( idx ); + } + + for( i = 0; i < fNotetracks.GetCount(); i++ ) + { + if( !stillThere.IsBitSet( i ) && fNotetracks[ i ] != nil ) + { +// DeleteReference( kRefNotetracks + i ); + SetReference( kRefNotetracks + i, nil ); + } + } + + for( i = 0; i < fChangeCallbacks.GetCount(); i++ ) + fChangeCallbacks[ i ]->NoteTrackListChanged(); + IUpdateAnimNodes(); +} + + +////////////////////////////////////////////////////////////////////////////// +//// MAX Ref Stuff /////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// NumRefs ///////////////////////////////////////////////////////////////// + +int plPassMtlBase::NumRefs() +{ + return 4 + fNotetracks.GetCount(); +} + +//// GetReference //////////////////////////////////////////////////////////// + +RefTargetHandle plPassMtlBase::GetReference( int i ) +{ + if( i >= kRefNotetracks && i < kRefNotetracks + fNotetracks.GetCount() ) + return fNotetracks[ i - kRefNotetracks ]; + + return NULL; +} + +//// SetReference //////////////////////////////////////////////////////////// + +void plPassMtlBase::SetReference(int i, RefTargetHandle rtarg) +{ + if( i >= kRefNotetracks ) + { + fNotetracks.ExpandAndZero(i - kRefNotetracks + 1); + fNotetracks[i - kRefNotetracks] = (NoteTrack*)rtarg; + } +} + +//// NotifyRefChanged //////////////////////////////////////////////////////// + +RefResult plPassMtlBase::NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, + PartID &partID, RefMessage message ) +{ + switch( message ) + { + case REFMSG_CHANGE: + fIValid.SetEmpty(); + + // see if this message came from a changing parameter in the pblock, + // if so, limit rollout update to the changing item + if (hTarget == fBasicPB || hTarget == fAdvPB || hTarget == fLayersPB || hTarget == fAnimPB) + { + IParamBlock2 *pb = (IParamBlock2*)hTarget; + + ParamID changingParam = pb->LastNotifyParamID(); + pb->GetDesc()->InvalidateUI( changingParam ); + + // And let the SceneWatcher know that the material on some of it's + // referenced objects changed. + NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_MAT ); + } + else + { + // Was it a notetrack ref? + if( fNotetracks.Find( (NoteTrack *)hTarget ) != fNotetracks.kMissingIndex ) + { + // Yup, so update our notetrack list + IUpdateAnimNodes(); + } + } + + break; + + case REFMSG_TARGET_DELETED: + NoteTrackRemoved(); + break; + + } + + return REF_SUCCEED; +} + +////////////////////////////////////////////////////////////////////////////// +//// Standard IO (or non-standard, as the case may be) /////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// PostLoadAnimPBFixup ///////////////////////////////////////////////////// +// Takes the old version of the anim paramblock and translates it into the +// new version. +// Note that there's an interesting (?) side effect of this: for new materials, +// we'll incorrectly detect them as the "old" format and fix-up them as well. +// This means that we'll end up with the same defaults for (Entire Animation) +// that we had for the old materials. We can easily change the defaults by +// changing the defaults for the old paramblock though. +// Also, we go ahead and re-work the stealth parent pointers here, since it +// appears to be the ONLY time we can do it and have it reliably work. ARRGH! +// +// History of the options that we CAN'T do and why (and hence why this): +// - ParamBlock accessor doesn't work. For some reason, the accessor isn't +// always called on load, even with P_CALLSETS_ON_LOAD specified +// - Doing it on Load() doesn't work, because neither the ParamBlocks are +// guaranteed to be fully loaded (with their tabs filled) nor are the +// notetracks necessarily attached yet +// - Notetracks can also possibly be attached BEFORE load, which doesn't +// do us a damned bit of good, so we need to make sure we're fully +// loaded before we run this function. Unfortunately, the only time +// we're guaranteed THAT is by a FILE_POST_OPEN notify. (post-load +// callbacks don't work because they're called right after our object +// is loaded but not necessarily before the notetracks are attached) + +void plPassMtlBase::PostLoadAnimPBFixup( void ) +{ + SetLoadingFlag( false ); + +#ifdef MCN_UPGRADE_OLD_ANIM_BLOCKS + if( fAnimPB->Count( (ParamID)kPBAnimStealthNodes ) == 0 ) + { + // Yup, old style. So our update process looks like this: + // 1) Create stealths for all our segments as we are now + // 2) Set the parameters on all of them to our old defaults (no autostart, + // loop on entire, no ease). + // 3) Copy the old paramblock values to the single stealth indicated by + // the old PB + + // Step 1... + IUpdateAnimNodes(); + + // Step 2... + for( int i = 0; i < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); i++ ) + { + plAnimStealthNode *node = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, i ); + const char *name = node->GetSegmentName(); + node->SetAutoStart( false ); + node->SetLoop( true, ENTIRE_ANIMATION_NAME ); + node->SetEaseIn( plAnimEaseTypes::kNoEase, 1.f, 1.f, 1.f ); + node->SetEaseOut( plAnimEaseTypes::kNoEase, 1.f, 1.f, 1.f ); + } + + // Step 3... + const char *oldSel = (const char *)fAnimPB->GetStr( (ParamID)kPBAnimName ); + if( oldSel == nil ) + oldSel = ENTIRE_ANIMATION_NAME; + plAnimStealthNode *myNew = IFindStealth( oldSel ); + if( myNew != nil ) + { +#pragma warning( push ) // Forcing value to bool true or false (go figure, i'm even explicitly casting) +#pragma warning( disable:4800 ) // Forcing value to bool true or false (go figure, i'm even explicitly casting) + myNew->SetAutoStart( (bool)fAnimPB->GetInt( (ParamID)kPBAnimAutoStart ) ); + myNew->SetLoop( (bool)fAnimPB->GetInt( (ParamID)kPBAnimLoop ), + (char *)fAnimPB->GetStr( (ParamID)kPBAnimLoopName ) ); + myNew->SetEaseIn( (UInt8)fAnimPB->GetInt( (ParamID)kPBAnimEaseInType ), + (hsScalar)fAnimPB->GetFloat( (ParamID)kPBAnimEaseInLength ), + (hsScalar)fAnimPB->GetFloat( (ParamID)kPBAnimEaseInMin ), + (hsScalar)fAnimPB->GetFloat( (ParamID)kPBAnimEaseInMax ) ); + myNew->SetEaseOut( (UInt8)fAnimPB->GetInt( (ParamID)kPBAnimEaseOutType ), + (hsScalar)fAnimPB->GetFloat( (ParamID)kPBAnimEaseOutLength ), + (hsScalar)fAnimPB->GetFloat( (ParamID)kPBAnimEaseOutMin ), + (hsScalar)fAnimPB->GetFloat( (ParamID)kPBAnimEaseOutMax ) ); +#pragma warning( pop ) + } + } +#endif // MCN_UPGRADE_OLD_ANIM_BLOCKS + + + // Make sure the parent is set tho. Note: we have to do this because, for some *(#$&(* reason, + // when we're loading a file, MAX can somehow add the stealths to our tab list WITHOUT calling + // the accessor for it (and the tab is empty on the CallSetsOnLoad() pass, for some reason). + for( int i = 0; i < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); i++ ) + { + plAnimStealthNode *node = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, i ); + node->SetParentMtl( this ); + } +} + + +#define MTL_HDR_CHUNK 0x4000 + +//// Load //////////////////////////////////////////////////////////////////// +// Our actual MAX load function + +IOResult plPassMtlBase::Load(ILoad *iload) +{ + plPostLoadHandler::AddPostLoad( this ); + + IOResult res; + int id; + while (IO_OK==(res=iload->OpenChunk())) + { + switch(id = iload->CurChunkID()) + { + case MTL_HDR_CHUNK: + res = MtlBase::Load(iload); + break; + } + iload->CloseChunk(); + if (res!=IO_OK) + return res; + } + + return IO_OK; +} + +//// Save //////////////////////////////////////////////////////////////////// +// The MAX flip-side + +IOResult plPassMtlBase::Save(ISave *isave) +{ + IOResult res; + isave->BeginChunk(MTL_HDR_CHUNK); + res = MtlBase::Save(isave); + if (res!=IO_OK) return res; + isave->EndChunk(); + + return IO_OK; +} + +//// ICloneBase ////////////////////////////////////////////////////////////// + +void plPassMtlBase::ICloneBase( plPassMtlBase *target, RemapDir &remap ) +{ + *((MtlBase*)target) = *((MtlBase*)this); + ICloneRefs( target, remap ); + + for( int idx = 0; idx < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); idx++ ) + { + IParamBlock2 *pb = target->fAnimPB; + plAnimStealthNode *stealth = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, idx ); + pb->SetValue( (ParamID)kPBAnimStealthNodes, 0, remap.CloneRef( stealth ), idx ); + + stealth = (plAnimStealthNode *)pb->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, idx ); + INode *node = GetCOREInterface()->CreateObjectNode( stealth ); + stealth->SetNodeName( GetName() ); + node->Freeze( true ); + } + + BaseClone(this, target, remap); + target->fIValid.SetEmpty(); +} + +//// ConvertToPassMtl //////////////////////////////////////////////////////// +// Static convert to our plPassMtlBase type, if possible + +plPassMtlBase *plPassMtlBase::ConvertToPassMtl( Mtl *mtl ) +{ + if( mtl == nil ) + return nil; + + if( mtl->ClassID() == PASS_MTL_CLASS_ID + || mtl->ClassID() == BUMP_MTL_CLASS_ID + || mtl->ClassID() == DECAL_MTL_CLASS_ID ) + { + return (plPassMtlBase *)mtl; + } + + return nil; +} + +//// SetupProperties ///////////////////////////////////////////////////////// + +hsBool plPassMtlBase::SetupProperties( plMaxNode *node, plErrorMsg *pErrMsg ) +{ + hsBool ret = true; + + // Call SetupProperties on all our animStealths if we have any + int i, count = IGetNumStealths(); + for( i = 0; i < count; i++ ) + { + if( !IGetStealth( i, false )->SetupProperties( node, pErrMsg ) ) + ret = false; + } + + return ret; +} + +//// ConvertDeInit /////////////////////////////////////////////////////////// + +hsBool plPassMtlBase::ConvertDeInit( plMaxNode *node, plErrorMsg *pErrMsg ) +{ + hsBool ret = true; + + // Call ConvertDeInit on all our animStealths if we have any + int i, count = IGetNumStealths(); + for( i = 0; i < count; i++ ) + { + if( !IGetStealth( i, false )->ConvertDeInit( node, pErrMsg ) ) + ret = false; + } + + return ret; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBase.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBase.h new file mode 100644 index 00000000..ce67c66f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBase.h @@ -0,0 +1,285 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_PASSMTLBASE_H +#define PL_PASSMTLBASE_H + +#include "hsTemplates.h" +#include "Max.h" +#include "iparamb2.h" +#include "../../PubUtilLib/plInterp/plAnimEaseTypes.h" + +class plNoteTrackWatcher; +class plMtlChangeCallback; +class plAnimStealthNode; +class NoteTrack; +class plPassAnimDlgProc; +class plStealthNodeAccessor; +class plMaxNode; +class plErrorMsg; + +class plPassMtlBase : public Mtl +{ +protected: + + friend class plPassAnimDlgProc; + friend class plStealthNodeAccessor; + friend class plNoteTrackWatcher; + + plNoteTrackWatcher *fNTWatcher; + + IParamBlock2 *fBasicPB; + IParamBlock2 *fAdvPB; + IParamBlock2 *fLayersPB; + IParamBlock2 *fAnimPB; + + Interval fIValid; + + hsBool fLoading; + + hsTArray fNotetracks; + + hsBool fStealthsChanged; + hsTArray fChangeCallbacks; + + void IUpdateAnimNodes( void ); + plAnimStealthNode *IFindStealth( const char *animName ); + plAnimStealthNode *IVerifyStealthPresent( const char *animName ); + + int IGetNumStealths( hsBool update = true ); + plAnimStealthNode *IGetStealth( int index, hsBool update = true ); + + void ICloneBase( plPassMtlBase *target, RemapDir &remap ); + virtual void ICloneRefs( plPassMtlBase *target, RemapDir &remap ) = 0; + +public: + + // mcn note: as far as I can tell, this is always the same pointer passed around to everyone. + // So since we have trouble getting it all the time, just store the first version we get and + // use that forever and ever + static IMtlParams *fIMtlParams; + + plPassMtlBase( BOOL loading ); + virtual ~plPassMtlBase(); + + virtual bool HasAlpha() = 0; + + enum Refs + { + //kRefBasic, // Can't do this, long ago they were different in the "derived" classes, + //kRefAdv, // and even tho MAX says it doesn't write refs out by their index, it does + //kRefLayers, + //kRefAnim, + kRefNotetracks = 4 // MUST BE THE LAST REF ID SPECIFIED + }; + void SetLoadingFlag( hsBool f ) { fLoading = f; } + void PostLoadAnimPBFixup( void ); + + void RegisterChangeCallback( plMtlChangeCallback *callback ); + void UnregisterChangeCallback( plMtlChangeCallback *callback ); + + // Change notifys from our ntWatcher + virtual void NoteTrackAdded( void ); + virtual void NoteTrackRemoved( void ); + virtual void NameChanged( void ); + + // Loading/Saving + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + virtual void Reset( void ); + + int NumRefs(); + virtual RefTargetHandle GetReference( int i ); + virtual void SetReference( int i, RefTargetHandle rtarg ); + RefResult NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, PartID &partID, RefMessage message ); + + // Convert time, called on the setupProps pass for each material applied to a node in the scene + virtual hsBool SetupProperties( plMaxNode *node, plErrorMsg *pErrMsg ); + virtual hsBool ConvertDeInit( plMaxNode *node, plErrorMsg *pErrMsg ); + + int GetNumStealths( void ); + plAnimStealthNode *GetStealth( int index ); + + // Static convert to our plPassMtlBase type, if possible + static plPassMtlBase *ConvertToPassMtl( Mtl *mtl ); + + // Blend types + enum + { + kBlendNone, + kBlendAlpha, + kBlendAdd, + kBlendMult + }; + + // Alpha blend types + enum + { + kAlphaDiscard, + kAlphaMultiply, + kAlphaAdd + }; + + // Advanced Block + virtual int GetBasicWire() = 0; + virtual int GetMeshOutlines() = 0; + virtual int GetTwoSided() = 0; + virtual int GetSoftShadow() = 0; + virtual int GetNoProj() = 0; + virtual int GetVertexShade() = 0; + virtual int GetNoShade() = 0; + virtual int GetNoFog() = 0; + virtual int GetWhite() = 0; + virtual int GetZOnly() = 0; + virtual int GetZClear() = 0; + virtual int GetZNoRead() = 0; + virtual int GetZNoWrite() = 0; + virtual int GetZInc() = 0; + virtual int GetAlphaTestHigh() = 0; + + // Animation block + virtual char * GetAnimName() = 0; + virtual int GetAutoStart() = 0; + virtual int GetLoop() = 0; + virtual char * GetAnimLoopName() = 0; + virtual int GetEaseInType() { return plAnimEaseTypes::kNoEase; } + virtual float GetEaseInMinLength() { return 1; } + virtual float GetEaseInMaxLength() { return 1; } + virtual float GetEaseInNormLength() { return 1; } + virtual int GetEaseOutType() { return plAnimEaseTypes::kNoEase; } + virtual float GetEaseOutMinLength() { return 1; } + virtual float GetEaseOutMaxLength() { return 1; } + virtual float GetEaseOutNormLength() { return 1; } + virtual char * GetGlobalVarName() { return NULL; } + virtual int GetUseGlobal() { return 0; } + + // Basic block + virtual int GetColorLock() = 0; + virtual Color GetAmbColor() = 0; + virtual Color GetColor() = 0; + virtual int GetOpacity() = 0; + virtual int GetEmissive() = 0; + virtual int GetUseSpec() = 0; + virtual int GetShine() = 0; + virtual Color GetSpecularColor() = 0; + virtual int GetDiffuseColorLock() = 0; + virtual Color GetRuntimeColor() = 0; + virtual Control *GetPreshadeColorController() = 0; + virtual Control *GetAmbColorController() = 0; + virtual Control *GetOpacityController() = 0; + virtual Control *GetSpecularColorController() = 0; + virtual Control *GetRuntimeColorController() = 0; + + // Layer block + virtual Texmap *GetBaseLayer() = 0; + virtual int GetTopLayerOn() = 0; + virtual Texmap *GetTopLayer() = 0; + virtual int GetLayerBlend() = 0; + virtual int GetOutputAlpha() = 0; + virtual int GetOutputBlend() = 0; +}; + + +// Make sure min is less than normal, which is less than max +class plEaseAccessor : public PBAccessor +{ +protected: + bool fDoingUpdate; + int fBlockID; + int fEaseInMinID, fEaseInMaxID, fEaseInNormID, fEaseOutMinID, fEaseOutMaxID, fEaseOutNormID; + + void AdjustMin(IParamBlock2 *pb, ParamID minID, ParamID normalID, ParamID maxID, float value) + { + if (value > pb->GetFloat(normalID)) + { + pb->SetValue(normalID, 0, value); + if (value > pb->GetFloat(maxID)) + pb->SetValue(maxID, 0, value); + } + } + void AdjustNormal(IParamBlock2 *pb, ParamID minID, ParamID normalID, ParamID maxID, float value) + { + if (value < pb->GetFloat(minID)) + pb->SetValue(minID, 0, value); + if (value > pb->GetFloat(maxID)) + pb->SetValue(maxID, 0, value); + } + void AdjustMax(IParamBlock2 *pb, ParamID minID, ParamID normalID, ParamID maxID, float value) + { + if (value < pb->GetFloat(normalID)) + { + pb->SetValue(normalID, 0, value); + if (value < pb->GetFloat(minID)) + pb->SetValue(minID, 0, value); + } + } + +public: + plEaseAccessor(int blockID, int easeInMinID, int easeInMaxID, int easeInNormID, + int easeOutMinID, int easeOutMaxID, int easeOutNormID) + { + fDoingUpdate = false; + fBlockID = blockID; + fEaseInMinID = easeInMinID; fEaseInMaxID = easeInMaxID; fEaseInNormID = easeInNormID; + fEaseOutMinID = easeOutMinID; fEaseOutMaxID = easeOutMaxID; fEaseOutNormID = easeOutNormID; + } + + void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + if (fDoingUpdate) + return; + fDoingUpdate = true; + + IParamBlock2 *pb = owner->GetParamBlockByID(fBlockID); + + if (id == fEaseInMinID) + AdjustMin(pb, fEaseInMinID, fEaseInNormID, fEaseInMaxID, v.f); + else if (id == fEaseInNormID) + AdjustNormal(pb, fEaseInMinID, fEaseInNormID, fEaseInMaxID, v.f); + else if (id == fEaseInMaxID) + AdjustMax(pb, fEaseInMinID, fEaseInNormID, fEaseInMaxID, v.f); + else if (id == fEaseOutMinID) + AdjustMin(pb, fEaseOutMinID, fEaseOutNormID, fEaseOutMaxID, v.f); + else if (id == fEaseOutNormID) + AdjustNormal(pb, fEaseOutMinID, fEaseOutNormID, fEaseOutMaxID, v.f); + else if (id == fEaseOutMaxID) + AdjustMax(pb, fEaseOutMinID, fEaseOutNormID, fEaseOutMaxID, v.f); + + fDoingUpdate = false; + } +}; + +//// plMtlChangeCallback ///////////////////////////////////////////////////// +// Interface class for receiving info about when things change on a material. + +class plMtlChangeCallback +{ + public: + virtual void NoteTrackListChanged( void ) { ; } + virtual void SegmentListChanged( void ) { ; } +}; + +#endif // PL_PASSMTLBASE_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBasicPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBasicPB.h new file mode 100644 index 00000000..f8b7fccc --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBasicPB.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_PASSMTLBASICPB_H +#define PL_PASSMTLBASICPB_H + +// Param ID's +enum +{ + kPassBasColorLock, + kPassBasColorAmb, + kPassBasColor, + + kPassBasOpacity, + + kPassBasEmissive, + + // Specular + kPassBasUseSpec, + kPassBasShine, + kPassBasShineStr, + + // New color stuff + kPassBasDiffuseLock, + kPassBasRunColor, + kPassBasSpecColor + +}; + +#endif //PL_PASSMTLBASICPB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBasicPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBasicPBDec.h new file mode 100644 index 00000000..a694918d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlBasicPBDec.h @@ -0,0 +1,240 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPassMtl.h" +#include "plPassMtlBasicPB.h" +#include "resource.h" +#include "iparamm2.h" + +class PassBasicPBAccessor; +extern PassBasicPBAccessor basicAccessor; + +class PassBasicDlgProc; +extern PassBasicDlgProc gPassBasicDlgProc; + +static ParamBlockDesc2 gPassBasicPB +( + plPassMtl::kBlkBasic, _T("basic"), IDS_PASS_BASIC, GetPassMtlDesc(),//NULL, + P_AUTO_CONSTRUCT + P_AUTO_UI, plPassMtl::kRefBasic, + + // UI + IDD_PASS_BASIC, IDS_PASS_BASIC, 0, 0, &gPassBasicDlgProc, + + // Color + kPassBasColorLock, _T("colorLock"), TYPE_BOOL, 0, 0, + p_ui, TYPE_CHECKBUTTON, IDC_LOCK_AD, + p_accessor, &basicAccessor, + end, + kPassBasColorAmb, _T("ambColor"), TYPE_RGBA, P_ANIMATABLE, IDS_BASIC_AMB, + p_ui, TYPE_COLORSWATCH, IDC_LAYER_COLOR_AMB, + p_accessor, &basicAccessor, + end, + kPassBasColor, _T("color"), TYPE_RGBA, P_ANIMATABLE, IDS_BASIC_COLOR, + p_ui, TYPE_COLORSWATCH, IDC_LAYER_COLOR, + p_default, Color(1,1,1), + p_accessor, &basicAccessor, + end, + + kPassBasRunColor, _T("runtimeColor"), TYPE_RGBA, P_ANIMATABLE, IDS_BASIC_RUNCOLOR, + p_ui, TYPE_COLORSWATCH, IDC_LAYER_RUNCOLOR, + p_default, Color(-1,-1,-1), + p_accessor, &basicAccessor, + end, + kPassBasDiffuseLock, _T("diffuseLock"), TYPE_BOOL, 0, 0, + p_ui, TYPE_CHECKBUTTON, IDC_LOCK_COLORS, + p_accessor, &basicAccessor, + p_default, TRUE, + end, + + // Opacity + kPassBasOpacity, _T("opacity"), TYPE_INT, P_ANIMATABLE, IDS_BASIC_OPAC, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_TR_EDIT, IDC_TR_SPIN, 0.4, + p_range, 0, 100, + p_default, 100, + end, + + kPassBasEmissive, _T("emissive"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_LAYER_EMISSIVE_CB, + end, + + // Specularity + kPassBasUseSpec, _T("useSpec"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_SHADE_SPECULAR, + p_enable_ctrls, 2, kPassBasShine, kPassBasSpecColor, + end, + kPassBasShine, _T("shine"), TYPE_INT, 0, 0, + p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_SH_EDIT, IDC_SH_SPIN, 0.4, + p_range, 0, 100, + end, + kPassBasSpecColor, _T("specularColor"), TYPE_RGBA, P_ANIMATABLE, IDS_BASIC_SPECCOLOR, + p_ui, TYPE_COLORSWATCH, IDC_LAYER_SPECCOLOR, + p_default, Color(0,0,0), + end, + + // OBSOLETE--here so we can upgrade it to color if necessary + kPassBasShineStr, _T("shineStr"), TYPE_INT, 0, 0, + p_range, -1, 100, + p_default, -1, + end, + + end +); +ParamBlockDesc2 *GetPassBasicPB() { return &gPassBasicPB; } + +class PassBasicPBAccessor : public PBAccessor +{ + bool fColorLocked; + +public: + PassBasicPBAccessor() : fColorLocked( false ) {} + + void Set(PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t) + { + plPassMtl* mtl = (plPassMtl*)owner; + IParamBlock2 *pb = mtl->GetParamBlockByID(plPassMtl::kBlkBasic); + + switch (id) + { + case kPassBasColorLock: + if (val.i) + pb->SetValue(kPassBasColor, t, pb->GetColor(kPassBasColorAmb, t)); + break; + + case kPassBasDiffuseLock: + if (val.i) + pb->SetValue(kPassBasRunColor, t, pb->GetColor(kPassBasColor, t)); + break; + + case kPassBasColor: + case kPassBasColorAmb: + case kPassBasRunColor: + ISyncLockedColors( id, pb, val, t ); + break; + } + } + void Get(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid) + { + } + + void ISyncLockedColors( ParamID settingID, IParamBlock2 *pb, PB2Value &val, TimeValue t ) + { + int i, numToSet = 0; + ParamID toSet[ 2 ]; + + + if( fColorLocked ) + return; + fColorLocked = true; + + if( settingID == kPassBasColorAmb && pb->GetInt( kPassBasColorLock, t ) ) + { + toSet[ numToSet++ ] = kPassBasColor; + if( pb->GetInt( kPassBasDiffuseLock, t ) ) + toSet[ numToSet++ ] = kPassBasRunColor; + } + else if( settingID == kPassBasRunColor && pb->GetInt( kPassBasDiffuseLock, t ) ) + { + toSet[ numToSet++ ] = kPassBasColor; + if( pb->GetInt( kPassBasColorLock, t ) ) + toSet[ numToSet++ ] = kPassBasColorAmb; + } + else if( settingID == kPassBasColor ) + { + if( pb->GetInt( kPassBasColorLock, t ) ) + toSet[ numToSet++ ] = kPassBasColorAmb; + if( pb->GetInt( kPassBasDiffuseLock, t ) ) + toSet[ numToSet++ ] = kPassBasRunColor; + } + + for( i = 0; i < numToSet; i++ ) + { + pb->SetValue( toSet[ i ], t, *val.p ); + if( pb->GetMap() ) + pb->GetMap()->Invalidate( toSet[ i ] ); + } + + fColorLocked = false; + } +}; +static PassBasicPBAccessor basicAccessor; + +class PassBasicDlgProc : public ParamMap2UserDlgProc +{ +#if 1 +protected: + HIMAGELIST hLockButtons; + + void LoadLockButtons() + { + static bool loaded = false; + if (loaded) + return; + loaded = true; + + HINSTANCE hInst = hInstance; + hLockButtons = ImageList_Create(16, 15, TRUE, 2, 0); + HBITMAP hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BUTTONS)); + HBITMAP hMask = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_MASKBUTTONS)); + ImageList_Add(hLockButtons, hBitmap, hMask); + DeleteObject(hBitmap); + DeleteObject(hMask); + } + void ISetLock(HWND hButton) + { + LoadLockButtons(); + + ICustButton *iBut = GetICustButton(hButton); + iBut->SetImage(hLockButtons,0,1,0,1,16,15); + iBut->SetType(CBT_CHECK); + ReleaseICustButton(iBut); + } + + +public: + PassBasicDlgProc() : hLockButtons(NULL) {} + ~PassBasicDlgProc() { if (hLockButtons) ImageList_Destroy(hLockButtons); } +#endif + +public: + BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + IParamBlock2 *pb = map->GetParamBlock(); + + switch (msg) + { + case WM_INITDIALOG: + { + ISetLock(GetDlgItem(hWnd, IDC_LOCK_AD)); + ISetLock(GetDlgItem(hWnd, IDC_LOCK_COLORS)); + } + return TRUE; + } + return FALSE; + } + void DeleteThis() {} +}; +static PassBasicDlgProc gPassBasicDlgProc; + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlLayersPB.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlLayersPB.h new file mode 100644 index 00000000..edea7c50 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlLayersPB.h @@ -0,0 +1,41 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PL_PASSMTLLAYERSPB_H +#define PL_PASSMTLLAYERSPB_H + +enum +{ + // Layers + kPassLayBase, + kPassLayTopOn, + kPassLayTop, + + kPassLayBlend, + kPassLayOutputBlend, + kPassLayOutputAlpha, +}; + +#endif //PL_PASSMTLLAYERSPB_H \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlLayersPBDec.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlLayersPBDec.h new file mode 100644 index 00000000..70e87415 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Materials/plPassMtlLayersPBDec.h @@ -0,0 +1,86 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPassMtl.h" +#include "plPassMtlLayersPB.h" +#include "resource.h" + +#include "iparamm2.h" + +#include "Layers/plLayerTex.h" + +class PassMtlLayersAccessor; +extern PassMtlLayersAccessor gLayersAccessor; + +class LayersDlgProc; +extern LayersDlgProc gLayersDlgProc; + +static ParamBlockDesc2 gPassMtlLayersPB +( + plPassMtl::kBlkLayers, _T("layers"), IDS_PASS_LAYERS, GetPassMtlDesc(), + P_AUTO_CONSTRUCT + P_AUTO_UI, plPassMtl::kRefLayers, + + // UI + IDD_PASS_LAYERS, IDS_PASS_LAYERS, 0, 0, NULL, + + kPassLayBase, _T("baseLayer"), TYPE_TEXMAP, 0, IDS_BASIC_AMB, + p_ui, TYPE_TEXMAPBUTTON, IDC_LAYER1, + p_subtexno, 0, + end, + + kPassLayTopOn, _T("topLayerOn"), TYPE_BOOL, 0, 0, + p_ui, TYPE_SINGLECHEKBOX, IDC_TOP_ON, + p_default, FALSE, + p_enable_ctrls, 3, kPassLayTop, kPassLayBlend, kPassLayOutputAlpha, + end, + kPassLayTop, _T("topLayer"), TYPE_TEXMAP, 0, 0, + p_ui, TYPE_TEXMAPBUTTON, IDC_LAYER2, + p_subtexno, 1, + end, + + kPassLayBlend, _T("layerBlend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_LAYER_ALPHA, IDC_LAYER_ADD, IDC_LAYER_MULTIPLY, + p_vals, plPassMtlBase::kBlendAlpha, plPassMtlBase::kBlendAdd, plPassMtlBase::kBlendMult, + p_default, plPassMtlBase::kBlendAdd, + end, + + kPassLayOutputAlpha, _T("ouputAlpha"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_OUTPUTA_DISCARD, IDC_OUTPUTA_ADD, IDC_OUTPUTA_MULT, + p_vals, plPassMtlBase::kAlphaDiscard, plPassMtlBase::kAlphaAdd, plPassMtlBase::kAlphaMultiply, + p_default, plPassMtlBase::kAlphaDiscard, + end, + + kPassLayOutputBlend, _T("outputBlend"), TYPE_INT, 0, 0, + p_ui, TYPE_RADIO, 3, IDC_OUTPUTB_NONE, IDC_OUTPUTB_ALPHA, IDC_OUTPUTB_ADD, + p_vals, plPassMtlBase::kBlendNone, plPassMtlBase::kBlendAlpha, plPassMtlBase::kBlendAdd, + p_default, plPassMtlBase::kBlendNone, + end, + + end + + + +); +ParamBlockDesc2 *GetPassLayersPB() { return &gPassMtlLayersPB; } diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/MaxPlasmaMtls.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/MaxPlasmaMtls.rc new file mode 100644 index 00000000..f543b99f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/MaxPlasmaMtls.rc @@ -0,0 +1,1181 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PASS_BASIC DIALOG 0, 0, 217, 84 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "User1",IDC_LOCK_AD,"CustButton",0x0,84,16,12,12 + CONTROL "",IDC_LAYER_COLOR_AMB,"ColorSwatch",0x0,53,8,27,12 + CONTROL "User1",IDC_LAYER_COLOR,"ColorSwatch",0x0,53,23,27,12 + CONTROL "",IDC_TR_EDIT,"CustEdit",WS_TABSTOP,53,53,20,10 + CONTROL "0,255,0",IDC_TR_SPIN,"SpinnerControl",WS_TABSTOP,73,53, + 7,10 + RTEXT "Opacity:",IDC_STATIC,23,53,27,8 + RTEXT "Ambient:",IDC_STATIC,22,10,28,8 + RTEXT "Static Color:",IDC_SCOL_LABEL,11,25,39,8 + CONTROL "Emissive",IDC_LAYER_EMISSIVE_CB,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,33,67,43,10 + CONTROL "Use Specular:",IDC_SHADE_SPECULAR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,118,24,61,10 + CONTROL "",IDC_SH_EDIT,"CustEdit",WS_TABSTOP,162,39,20,10 + CONTROL "0,255,0",IDC_SH_SPIN,"SpinnerControl",WS_TABSTOP,183,39, + 7,10 + RTEXT "Shininess:",IDC_STATIC,124,39,33,8 + GROUPBOX "Specular",IDC_STATIC,102,12,106,58 + CONTROL "User1",IDC_LOCK_COLORS,"CustButton",0x0,84,31,12,12 + CONTROL "User1",IDC_LAYER_RUNCOLOR,"ColorSwatch",0x0,53,37,27,12 + RTEXT "Runtime Color:",IDC_RCOL_LABEL,4,39,47,8 + RTEXT "Color:",IDC_STATIC,138,52,19,8 + CONTROL "User1",IDC_LAYER_SPECCOLOR,"ColorSwatch",0x0,162,51,27, + 12 +END + +IDD_ANGLE_ATTEN_LAYER DIALOG 0, 0, 217, 89 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Second Fade",IDC_DOUBLE_FADE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,107,9,59,10 + GROUPBOX "",IDC_STATIC,105,18,102,42 + LTEXT "Transparent",IDC_STATIC,10,29,43,8 + CONTROL "0,180",IDC_TRANSP_ANGLE_0,"CustEdit",WS_GROUP | + WS_TABSTOP,55,28,28,10 + CONTROL "0,180,0",IDC_TRANSP_ANGLE_0_SPIN,"SpinnerControl",0x0, + 84,28,7,10 + LTEXT "Opaque",IDC_STATIC,13,42,39,8 + CONTROL "0,180",IDC_OPAQUE_ANGLE_0,"CustEdit",WS_GROUP | + WS_TABSTOP,55,42,28,10 + CONTROL "0,180,0",IDC_OPAQUE_ANGLE_0_SPIN,"SpinnerControl",0x0, + 84,42,7,10 + LTEXT "Transparent",IDC_STATIC,113,28,43,8 + CONTROL "0,180",IDC_TRANSP_ANGLE_1,"CustEdit",WS_GROUP | + WS_TABSTOP,159,26,28,10 + CONTROL "0,180,0",IDC_TRANSP_ANGLE_1_SPIN,"SpinnerControl",0x0, + 188,26,7,10 + LTEXT "Opaque",IDC_STATIC,117,41,39,8 + CONTROL "0,180",IDC_OPAQUE_ANGLE_1,"CustEdit",WS_GROUP | + WS_TABSTOP,159,41,28,10 + CONTROL "0,180,0",IDC_OPAQUE_ANGLE_1_SPIN,"SpinnerControl",0x0, + 188,41,7,10 + CONTROL "Use Reflection",IDC_REFLECT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,18,9,63,10 + LTEXT "Min",IDC_STATIC,22,70,15,8 + CONTROL "0,180",IDC_LO_CLAMP,"CustEdit",WS_GROUP | WS_TABSTOP,39, + 70,28,10 + CONTROL "0,180,0",IDC_LO_CLAMP_SPIN,"SpinnerControl",0x0,67,70,7, + 10 + GROUPBOX "Opacity Range",IDC_STATIC,14,60,131,22 + LTEXT "Max",IDC_STATIC,83,70,15,8 + CONTROL "0,180",IDC_HI_CLAMP,"CustEdit",WS_GROUP | WS_TABSTOP, + 105,70,28,10 + CONTROL "0,180,0",IDC_HI_CLAMP_SPIN,"SpinnerControl",0x0,133,70, + 7,10 +END + +IDD_BUMP_ANIM DIALOG 0, 0, 217, 53 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_NAMES,61,5,95,52,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "Auto-start",IDC_AUTO_START,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,61,23,46,10 + CONTROL "Loop",IDC_LOOP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,61, + 36,32,10 + COMBOBOX IDC_LOOPS,94,35,95,54,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Refresh",IDC_REFRESH_ANIMS,168,4,39,14 + LTEXT "Name:",IDC_STATIC,35,7,22,8 +END + +IDD_BUMP_BASIC DIALOG 0, 0, 217, 44 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Specular Bump Map",IDC_SHADE_SPECULAR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,108,8,80,10 + RTEXT "Intensity:",IDC_RCOL_LABEL,39,9,29,8 + CONTROL "",IDC_LAYER1,"CustButton",WS_TABSTOP,72,24,116,13 + RTEXT "Bump Layer:",IDC_STATIC,29,26,40,8 + CONTROL "",IDC_INTEN_EDIT,"CustEdit",WS_TABSTOP,72,9,20,10 + CONTROL "0,255,0",IDC_INTEN_SPIN,"SpinnerControl",WS_TABSTOP,92, + 9,7,10 + CONTROL "",IDC_DUMMY_EDIT1,"CustEdit",NOT WS_VISIBLE | + WS_TABSTOP,4,7,6,10 + CONTROL "0,255,0",IDC_DUMMY_SPIN1,"SpinnerControl",NOT + WS_VISIBLE | WS_TABSTOP,10,7,7,10 + CONTROL "",IDC_DUMMY_EDIT2,"CustEdit",NOT WS_VISIBLE | + WS_TABSTOP,4,15,6,10 + CONTROL "0,255,0",IDC_DUMMY_SPIN2,"SpinnerControl",NOT + WS_VISIBLE | WS_TABSTOP,10,15,7,10 +END + +IDD_CLOTHING DIALOGEX 0, 0, 217, 269 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "Tileset:",IDC_STATIC,7,82,24,8 + COMBOBOX IDC_CLOTHING_TILESET,37,79,92,72,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "1",IDC_CLOTHING_TILE1_NAME,8,131,59,8,0,WS_EX_RIGHT + LTEXT "2",IDC_CLOTHING_TILE2_NAME,8,147,59,8,0,WS_EX_RIGHT + LTEXT "3",IDC_CLOTHING_TILE3_NAME,8,163,59,8,0,WS_EX_RIGHT + LTEXT "4",IDC_CLOTHING_TILE4_NAME,8,179,59,8,0,WS_EX_RIGHT + LTEXT "Tile:",IDC_STATIC,8,118,60,8 + LTEXT "Texture:",IDC_STATIC,108,118,27,8 + CONTROL "",IDC_CLOTHING_TEXTURE1,"CustButton",WS_TABSTOP,108,129, + 102,12 + CONTROL "",IDC_CLOTHING_TEXTURE2,"CustButton",WS_TABSTOP,108,145, + 102,12 + CONTROL "",IDC_CLOTHING_TEXTURE3,"CustButton",WS_TABSTOP,108,161, + 102,12 + CONTROL "",IDC_CLOTHING_TEXTURE4,"CustButton",WS_TABSTOP,108,177, + 102,12 + LTEXT "Static",IDC_CLOTHING_TILE1_SIZE,70,131,34,8 + LTEXT "Static",IDC_CLOTHING_TILE2_SIZE,70,147,34,8 + LTEXT "Static",IDC_CLOTHING_TILE3_SIZE,70,163,34,8 + LTEXT "Static",IDC_CLOTHING_TILE4_SIZE,70,179,34,8 + LTEXT "Thumbnail Image:",IDC_STATIC,7,8,61,8 + CONTROL "",IDC_CLOTHING_THUMBNAIL,"CustButton",WS_TABSTOP,76,7, + 102,12 + LTEXT "Item Description:",IDC_STATIC,7,23,61,8 + CONTROL "Custom1",IDC_CLOTHING_DESCRIPTION,"CustEdit",WS_TABSTOP, + 7,34,203,12 + LTEXT "Layer:",IDC_STATIC,7,100,20,8 + COMBOBOX IDC_CLOTHING_LAYER,37,97,92,104,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + CONTROL "Default",IDC_CLOTHING_DEFAULT,"Button",BS_AUTOCHECKBOX | + BS_LEFTTEXT | WS_TABSTOP,155,67,38,10 + LTEXT "Text Options Sent To Python:",IDC_STATIC,7,195,109,8 + EDITTEXT IDC_CLOTHING_CUSTOM_TEXT_SPECS,7,209,203,54,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL | WS_HSCROLL + CONTROL "",IDC_CLOTHING_TINT1,"ColorSwatch",0x0,171,80,27,12 + LTEXT "Tint 1:",IDC_STATIC,147,82,20,8 + CONTROL "",IDC_CLOTHING_TINT2,"ColorSwatch",0x0,171,97,27,12 + LTEXT "Tint 2:",IDC_STATIC,147,99,21,8 + CONTROL "",IDC_CLOTHING_FORCED_ACC,"CustEdit",WS_TABSTOP,38,64, + 92,10 + LTEXT "Forced Accessory:",IDC_STATIC,7,53,60,8 + GROUPBOX "",IDC_STATIC,141,59,63,58 +END + +IDD_COMPOSITE DIALOG 0, 0, 217, 70 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_TEX1,"CustButton",WS_TABSTOP,23,17,86,13 + CONTROL "",IDC_TEXON2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10, + 35,10,10 + CONTROL "",IDC_TEX2,"CustButton",WS_TABSTOP,23,33,86,13 + CONTROL "",IDC_TEXON3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10, + 50,10,10 + CONTROL "",IDC_TEX3,"CustButton",WS_TABSTOP,23,49,86,13 + LTEXT "Pass:",IDC_STATIC,56,7,18,8 + LTEXT "Use:",IDC_STATIC,7,7,16,8 + COMBOBOX IDC_COMBO2,114,33,96,59,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_COMBO3,114,49,96,62,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP + LTEXT "Vertex Alpha Determined By:",IDC_STATIC,117,7,90,8 + LTEXT "(opaque)",IDC_STATIC,143,19,29,8 +END + +IDD_DECAL_BASIC DIALOG 0, 0, 217, 44 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "User1",IDC_LOCK_AD,"CustButton",0x0,12,15,12,12 + CONTROL "",IDC_LAYER_COLOR_AMB,"ColorSwatch",0x0,56,8,25,12 + CONTROL "User1",IDC_LAYER_COLOR,"ColorSwatch",0x0,56,23,25,12 + CONTROL "",IDC_TR_EDIT,"CustEdit",WS_TABSTOP,158,17,20,10 + CONTROL "0,255,0",IDC_TR_SPIN,"SpinnerControl",WS_TABSTOP,178,17, + 7,10 + LTEXT "Opacity:",IDC_STATIC,128,18,27,8 + LTEXT "Ambient:",IDC_STATIC,28,10,28,8 + LTEXT "Color:",IDC_STATIC,36,24,19,8 +END + +IDD_DECAL_LAYERS DIALOG 0, 0, 217, 60 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_LAYER1,"CustButton",WS_TABSTOP,76,5,116,13 + CONTROL "None",IDC_OUTPUTB_NONE,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,75,25,33,10 + CONTROL "Alpha",IDC_OUTPUTB_ALPHA,"Button",BS_AUTORADIOBUTTON,75, + 35,34,10 + CONTROL "Add",IDC_OUTPUTB_ADD,"Button",BS_AUTORADIOBUTTON,75,45, + 29,8 + LTEXT "Base Layer:",IDC_STATIC,34,6,38,8 + LTEXT "Output Blending:",IDC_BLEND_TEXT3,17,25,54,8 +END + +IDD_DYN_TEXT_LAYER DIALOG 0, 0, 216, 226 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_EXPORTWIDTH,"CustEdit",WS_GROUP | WS_TABSTOP,24, + 15,28,10 + CONTROL "0,255,0",IDC_EXPORTWIDTH_SPINNER,"SpinnerControl",0x0, + 53,15,7,10 + CONTROL "",IDC_EXPORTHEIGHT,"CustEdit",WS_GROUP | WS_TABSTOP,73, + 15,28,10 + CONTROL "0,255,0",IDC_EXPORTHEIGHT_SPINNER,"SpinnerControl",0x0, + 102,15,7,10 + CONTROL "Use",IDC_USEINITIMAGE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,12,57,29,10 + PUSHBUTTON "Reload",IDC_INITIMAGE_RELOAD,76,55,33,12 + CONTROL "",IDC_INITIMAGE,"CustButton",WS_TABSTOP,10,71,100,12 + GROUPBOX "Export Size (in pixels)",IDC_STATIC,6,4,109,38 + LTEXT "W:",IDC_STATIC,12,16,10,8 + LTEXT "H:",IDC_STATIC,64,16,8,8 + GROUPBOX "Static Text",IDC_STATIC,6,90,202,131,WS_DISABLED + CONTROL "Make Static (disables runtime changes)", + IDC_DYNTEXT_MAKESTATIC,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,11,101,140,10 + EDITTEXT IDC_DT_TEXT,14,114,186,22,ES_AUTOHSCROLL | WS_DISABLED + GROUPBOX "Margins (in pixels)",IDC_STATIC,36,173,144,42, + WS_DISABLED + RTEXT "Left:",IDC_STATIC,42,186,21,8,WS_DISABLED + CONTROL "",IDC_DT_LMARGIN,"CustEdit",WS_DISABLED | WS_GROUP | + WS_TABSTOP,65,185,28,10 + CONTROL "0,255,0",IDC_DT_LMARGIN_SPIN,"SpinnerControl", + WS_DISABLED,94,185,7,10 + RTEXT "Top:",IDC_STATIC,109,186,21,8,WS_DISABLED + CONTROL "",IDC_DT_TMARGIN,"CustEdit",WS_DISABLED | WS_GROUP | + WS_TABSTOP,132,185,28,10 + CONTROL "0,255,0",IDC_DT_TMARGIN_SPIN,"SpinnerControl", + WS_DISABLED,161,185,7,10 + RTEXT "Right:",IDC_STATIC,42,199,21,8,WS_DISABLED + CONTROL "",IDC_DT_RMARGIN,"CustEdit",WS_DISABLED | WS_GROUP | + WS_TABSTOP,65,198,28,10 + CONTROL "0,255,0",IDC_DT_RMARGIN_SPIN,"SpinnerControl", + WS_DISABLED,94,198,7,10 + RTEXT "Bottom:",IDC_STATIC,103,199,27,8,WS_DISABLED + CONTROL "",IDC_DT_BMARGIN,"CustEdit",WS_DISABLED | WS_GROUP | + WS_TABSTOP,132,198,28,10 + CONTROL "0,255,0",IDC_DT_BMARGIN_SPIN,"SpinnerControl", + WS_DISABLED,161,198,7,10 + GROUPBOX "Font",IDC_STATIC,14,138,187,28,WS_DISABLED + COMBOBOX IDC_DT_FONTFACE,18,148,137,77,CBS_DROPDOWN | CBS_SORT | + WS_DISABLED | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_DT_FONTSIZE,"CustEdit",WS_DISABLED | WS_GROUP | + WS_TABSTOP,158,150,28,10 + CONTROL "0,255,0",IDC_DT_FONTSIZE_SPIN,"SpinnerControl", + WS_DISABLED,188,150,7,10 + GROUPBOX "Initial Image",IDC_STATIC,6,45,109,43 + CONTROL "Include alpha channel",IDC_DYNTEXT_ALPHA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,11,28,86,10 +END + +IDD_DYNAMIC_ENVMAP_LAYER DIALOG 0, 0, 216, 141 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + GROUPBOX "General",IDC_STATIC,5,5,203,60 + RTEXT "Generate from Node:",IDC_STATIC,8,34,72,8 + CONTROL "",IDC_TEXSIZE_EDIT,"CustEdit",WS_TABSTOP,84,16,25,10 + CONTROL "",IDC_TEXSIZE_SPIN,"SpinnerControl",0x0,110,16,7,10 + RTEXT "Texture Size:",IDC_STATIC,32,16,48,8 + CONTROL "",IDC_ANCHOR_NODE,"CustButton",WS_TABSTOP,84,32,97,12 + CONTROL "Refract",IDC_REFRACT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,16,51,39,10 + GROUPBOX "Notes:",IDC_STATIC,5,75,202,52 + LTEXT "Use ""Generate from Node"" to share env map among objects.", + IDC_STATIC,15,86,178,18 + LTEXT "Use EnvMap component on GenFromNode for more options.", + IDC_STATIC,13,106,184,15 +END + +IDD_LAYER_BASIC DIALOG 0, 0, 216, 41 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_USAGE_TYPE,61,6,93,103,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + PUSHBUTTON "Set Defaults from Purpose",IDC_USAGE_SETUP,61,22,94,14 +END + +IDD_LAYER_TEX DIALOGEX 0, 0, 216, 304 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "Use Texture",IDC_USE_BITMAP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,6,3,55,10 + CONTROL "",IDC_LAYER_NAME,"CustButton",WS_TABSTOP,33,17,139,12 + PUSHBUTTON "Reload",IDC_LAYER_RELOAD,176,17,33,12 + CONTROL "Apply ",IDC_BM_CLIP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,16,74,31,10 + CONTROL "Crop",IDC_BM_CROP,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,27,89,31,10 + CONTROL "Place",IDC_BM_PLACE,"Button",BS_AUTORADIOBUTTON | + WS_TABSTOP,63,89,34,10 + CONTROL "",IDC_CLIP_X,"CustEdit",WS_GROUP | WS_TABSTOP,25,102,23, + 10 + CONTROL "0,255,0",IDC_CLIP_XSPIN,"SpinnerControl",0x0,49,102,7, + 10 + CONTROL "",IDC_CLIP_W,"CustEdit",WS_TABSTOP,72,102,23,10 + CONTROL "0,255,0",IDC_CLIP_WSPIN,"SpinnerControl",0x0,96,102,7, + 10 + CONTROL "",IDC_CLIP_Y,"CustEdit",WS_TABSTOP,25,114,23,10 + CONTROL "0,255,0",IDC_CLIP_YSPIN,"SpinnerControl",0x0,49,114,7, + 10 + CONTROL "",IDC_CLIP_H,"CustEdit",WS_TABSTOP,72,114,23,10 + CONTROL "0,255,0",IDC_CLIP_HSPIN,"SpinnerControl",0x0,96,114,7, + 10 + PUSHBUTTON "Interactive Adjustment...",IDC_BM_CROP_IMAGE,17,130,87, + 14 + CONTROL "Do not compress",IDC_FORCE_NONCOMPRESSED,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,163,69,10 + LTEXT "Game Detail Scaling:",IDC_STATIC,11,178,67,8 + CONTROL "Allow all scaling",IDC_SCALE_ALL,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,11,188,65,10 + CONTROL "No smaller than 1/2 original",IDC_SCALE_HALF,"Button", + BS_AUTORADIOBUTTON,11,198,102,10 + CONTROL "No scaling",IDC_SCALE_NONE,"Button",BS_AUTORADIOBUTTON, + 11,208,49,10 + CONTROL "Discard Color",IDC_BLEND_NO_COLOR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,126,44,58,10 + CONTROL "Invert Color",IDC_BLEND_INV_COLOR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,126,53,52,10 + CONTROL "Discard Alpha",IDC_DISCARD_ALPHA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,126,65,60,10 + CONTROL "Invert Alpha",IDC_BLEND_INV_ALPHA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,126,74,54,10 + CONTROL "No Filtering",IDC_NO_FILTERING,"Button",BS_AUTOCHECKBOX | + WS_GROUP | WS_TABSTOP,126,102,51,10 + CONTROL "Bias:",IDC_USE_MIPBIAS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,126,114,28,10 + CONTROL "",IDC_MIPBIAS_EDIT,"CustEdit",WS_TABSTOP,156,114,25,10 + CONTROL "0,255,0",IDC_MIPBIAS_SPIN,"SpinnerControl",0x0,182,114, + 7,10 + CONTROL "",IDC_MIPBLUR_EDIT,"CustEdit",WS_TABSTOP,156,125,25,10 + CONTROL "0,255,0",IDC_MIPBLUR_SPIN,"SpinnerControl",0x0,182,125, + 7,10 + CONTROL "RGB Intensity",IDC_HSMAX_LAYER_RGBOUT,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,129,164,60,10 + CONTROL "Alpha",IDC_HSMAX_LAYER_ALPHAOUT,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,129,174,60,10 + CONTROL "RGB ",IDC_HSMAX_LAYER_RGBOUT2,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,129,198,60,10 + CONTROL "Alpha as Gray",IDC_HSMAX_LAYER_ALPHAOUT2,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,129,208,60,10 + CONTROL "Use detail",IDC_USE_DETAIL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,11,239,47,10 + CONTROL "",IDC_DETAIL_START_SIZE_EDIT,"CustEdit",WS_TABSTOP,44, + 273,20,10 + CONTROL "",IDC_DETAIL_START_SIZE_SPIN,"SpinnerControl",0x0,65, + 273,7,10 + CONTROL "",IDC_DETAIL_START_OPAC_EDIT,"CustEdit",WS_TABSTOP,84, + 273,20,10 + CONTROL "",IDC_DETAIL_START_OPAC_SPIN,"SpinnerControl",0x0,105, + 273,7,10 + CONTROL "",IDC_DETAIL_STOP_SIZE_EDIT,"CustEdit",WS_TABSTOP,44, + 284,20,10 + CONTROL "",IDC_DETAIL_STOP_SIZE_SPIN,"SpinnerControl",0x0,65,284, + 7,10 + CONTROL "",IDC_DETAIL_STOP_OPAC_EDIT,"CustEdit",WS_TABSTOP,84, + 284,20,10 + CONTROL "",IDC_DETAIL_STOP_OPAC_SPIN,"SpinnerControl",0x0,105, + 284,7,10 + LTEXT "Texture:",IDC_STATIC,4,19,27,8,0,WS_EX_RIGHT + LTEXT "U:",IDC_STATIC,17,103,8,8 + LTEXT "V:",IDC_STATIC,17,114,8,8 + LTEXT "W:",IDC_STATIC,61,103,10,8 + LTEXT "H:",IDC_STATIC,62,114,8,8 + GROUPBOX "Cropping/Placement",IDC_STATIC,6,62,110,87 + GROUPBOX "",IDC_STATIC,120,145,88,79 + GROUPBOX "Texture Color/Alpha",IDC_STATIC,120,33,88,54 + LTEXT "Mono Channel Output:",IDC_STATIC,127,153,72,8 + LTEXT "Color Channel Output:",IDC_STATIC,127,188,70,8 + LTEXT "Blur:",IDC_STATIC,138,125,13,8 + GROUPBOX "Texture Quality",IDC_STATIC,5,150,110,73 + GROUPBOX "Mipmap Properties",IDC_STATIC,120,90,88,52 + LTEXT "Start %",IDC_STATIC,12,273,23,8 + LTEXT "End %",IDC_STATIC,14,284,21,8 + CTEXT "Distance from Camera:",IDC_STATIC,36,255,41,16 + LTEXT "Opacity:",IDC_STATIC,85,259,25,10 + GROUPBOX "Detail",IDC_STATIC,5,227,202,72 + CONTROL "",IDC_DETAIL_SAMPLE,"Static",SS_BLACKFRAME | NOT + WS_VISIBLE,117,238,86,56 + GROUPBOX "Export Size (in pixels)",IDC_STATIC,6,33,109,27 + LTEXT "W:",IDC_STATIC,12,45,10,8 + LTEXT "H:",IDC_STATIC,64,45,8,8 + CONTROL "",IDC_EXPORTWIDTH,"CustEdit",WS_GROUP | WS_TABSTOP,24, + 44,28,10 + CONTROL "0,255,0",IDC_EXPORTWIDTH_SPINNER,"SpinnerControl",0x0, + 53,44,7,10 + CONTROL "",IDC_EXPORTHEIGHT,"CustEdit",WS_GROUP | WS_TABSTOP,73, + 44,28,10 + CONTROL "0,255,0",IDC_EXPORTHEIGHT_SPINNER,"SpinnerControl",0x0, + 102,44,7,10 + CONTROL "Texture Python Readable",IDC_NO_DISCARD,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,108,3,97,10 +END + +IDD_MULTIPASS DIALOG 0, 0, 217, 196 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_LAYER_EDIT,"CustEdit",WS_TABSTOP,129,4,12,10 + CONTROL "",IDC_LAYER_SPIN,"SpinnerControl",WS_TABSTOP,141,4,7,10 + CONTROL "",IDC_TEXON1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,59, + 36,10,10 + CONTROL "",IDC_TEX1,"CustButton",WS_TABSTOP,73,35,86,13 + CONTROL "",IDC_TEXON2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,59, + 52,10,10 + CONTROL "",IDC_TEX2,"CustButton",WS_TABSTOP,73,51,86,13 + CONTROL "",IDC_TEXON3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,59, + 68,10,10 + CONTROL "",IDC_TEX3,"CustButton",WS_TABSTOP,73,67,86,13 + CONTROL "",IDC_TEXON4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,59, + 84,10,10 + CONTROL "",IDC_TEXON10,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,59, + 180,10,10 + CONTROL "",IDC_TEX4,"CustButton",WS_TABSTOP,73,83,86,13 + CONTROL "",IDC_TEXON5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,59, + 100,10,10 + CONTROL "",IDC_TEX5,"CustButton",WS_TABSTOP,73,99,86,13 + CONTROL "",IDC_TEXON6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,59, + 117,10,10 + CONTROL "",IDC_TEX6,"CustButton",WS_TABSTOP,73,115,86,13 + CONTROL "",IDC_TEXON7,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,59, + 132,10,10 + CONTROL "",IDC_TEX7,"CustButton",WS_TABSTOP,73,131,86,13 + CONTROL "",IDC_TEXON8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,59, + 148,10,10 + CONTROL "",IDC_TEX8,"CustButton",WS_TABSTOP,73,147,86,13 + CONTROL "",IDC_TEXON9,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,59, + 164,10,10 + CONTROL "",IDC_TEX9,"CustButton",WS_TABSTOP,73,163,86,13 + CONTROL "",IDC_TEX10,"CustButton",WS_TABSTOP,73,179,86,13 + LTEXT "Pass:",-1,106,25,18,8 + LTEXT "Use:",-1,57,25,16,8 + LTEXT "Number of Passes:",-1,69,5,60,8 +END + +IDD_PARTICLE DIALOGEX 0, 0, 217, 119 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "Height:",IDC_STATIC,18,65,24,8 + CONTROL "Custom1",IDC_PARTICLE_HEIGHT,"CustEdit",WS_TABSTOP,43, + 64,19,10 + CONTROL "Custom2",IDC_PARTICLE_HEIGHT_SPIN,"SpinnerControl", + WS_TABSTOP,63,64,7,10 + LTEXT "Width:",IDC_STATIC,19,51,22,8 + CONTROL "Custom1",IDC_PARTICLE_WIDTH,"CustEdit",WS_TABSTOP,43,51, + 19,10 + CONTROL "Custom2",IDC_PARTICLE_WIDTH_SPIN,"SpinnerControl", + WS_TABSTOP,63,51,7,10 + LTEXT "X:",IDC_STATIC,111,73,8,8 + CONTROL "Custom1",IDC_PARTICLE_XTILE,"CustEdit",WS_TABSTOP,121, + 72,14,10 + CONTROL "Custom2",IDC_PARTICLE_XTILE_SPIN,"SpinnerControl", + WS_TABSTOP,137,72,7,10 + LTEXT "Y:",IDC_STATIC,149,73,8,8 + CONTROL "Custom1",IDC_PARTICLE_YTILE,"CustEdit",WS_TABSTOP,159, + 72,14,10 + CONTROL "Custom2",IDC_PARTICLE_YTILE_SPIN,"SpinnerControl", + WS_TABSTOP,175,72,7,10 + LTEXT "Tiling:",IDC_STATIC,89,73,20,8 + CONTROL "Custom1",IDC_PARTICLE_OPACITY,"CustEdit",WS_TABSTOP,43, + 38,20,10 + CONTROL "0,255,0",IDC_PARTICLE_OPACITY_SPIN,"SpinnerControl", + WS_TABSTOP,63,38,7,10 + RTEXT "Opacity:",IDC_STATIC,13,38,27,8 + CONTROL "User1",IDC_PARTICLE_COLOR,"ColorSwatch",0x0,43,22,27,12 + RTEXT "Color:",IDC_STATIC,21,23,19,8 + LTEXT "Shading:",IDC_STATIC,87,8,29,8 + COMBOBOX IDC_PARTICLE_NORMAL,118,7,92,72,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "Orientation:",IDC_STATIC,79,24,37,8 + CONTROL "",IDC_PARTICLE_TEXTURE,"CustButton",WS_TABSTOP,43,84, + 139,12 + LTEXT "Texture:",IDC_STATIC,14,88,27,8,0,WS_EX_RIGHT + LTEXT "Blend Mode:",IDC_STATIC,163,24,41,8 + CONTROL "Velocity Based",IDC_PARTICLE_ORIENT_VELOCITY,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,88,34,59,8 + CONTROL "Rotatable",IDC_PARTICLE_ORIENT_UP,"Button", + BS_AUTORADIOBUTTON,87,43,59,8 + CONTROL "Alpha",IDC_PARTICLE_BLEND_ALPHA,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,171,34,32,10 + CONTROL "Add",IDC_PARTICLE_BLEND_ADD,"Button",BS_AUTORADIOBUTTON, + 171,46,30,8 + CONTROL "User1",IDC_PARTICLE_AMB_COLOR,"ColorSwatch",0x0,43,7,27, + 12 + RTEXT "Ambient:",IDC_STATIC,14,9,28,8 + CONTROL "Velocity Stretch",IDC_PARTICLE_ORIENT_VELSTRETCH,"Button", + BS_AUTORADIOBUTTON,87,53,64,8 + CONTROL "No Filter",IDC_PARTICLE_NOFILTER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,42,100,39,10 + CONTROL "Velocity Flow",IDC_PARTICLE_ORIENT_VELFLOW,"Button", + BS_AUTORADIOBUTTON,87,63,64,8 +END + +IDD_PASS_ADV DIALOG 0, 0, 217, 150 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Soft Shadow",IDC_SHADE_SOFTSHADOW,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,39,57,10 + CONTROL "No Projectors",IDC_SHADE_NO_PROJ,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,49,59,10 + CONTROL "Vertex Shading",IDC_SHADE_VERTEXSHADE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,10,58,64,10 + CONTROL "No Shading",IDC_SHADE_NOSHADE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,10,68,53,10 + CONTROL "No Fog",IDC_SHADE_NO_FOG,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,10,78,39,10 + CONTROL "White",IDC_SHADE_WHITE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,10,88,35,10 + CONTROL "Z Only",IDC_Z_ZONLY,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,109,39,37,10 + CONTROL "Z Clear",IDC_Z_ZCLEAR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,109,49,39,10 + CONTROL "Z No Read",IDC_Z_ZNOREAD,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,109,59,51,10 + CONTROL "Z No Write",IDC_Z_ZNOWRITE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,109,69,51,10 + CONTROL "Increment Layer",IDC_Z_INC,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,109,79,67,10 + CONTROL "Wireframe",IDC_MISC_WIRE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,109,107,48,10 + CONTROL "Draw Mesh Outlines",IDC_MISC_MESHOUTLINES,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,109,118,79,10 + CONTROL "Two-Sided",IDC_MISC_TWOSIDED,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,109,128,50,10 + GROUPBOX "Shade Properties",IDC_STATIC,4,28,93,73 + GROUPBOX "Draw Properties",IDC_STATIC,103,95,109,48 + GROUPBOX "Z Properties",IDC_STATIC,103,28,109,64 + LTEXT "The options in this panel are for experimental purposes only and are NOT SUPPORTED", + IDC_STATIC,37,5,143,19 + GROUPBOX "Alpha",IDC_STATIC,4,105,92,23 + CONTROL "Test High",IDC_ALPHA_TEST_HIGH,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,10,114,47,10 +END + +IDD_PASS_ANIM_EASE DIALOG 0, 0, 217, 90 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "None",IDC_PASS_ANIM_EASE_IN_NONE,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,13,17,33,10 + CONTROL "Constant Acceleration", + IDC_PASS_ANIM_EASE_IN_CONST_ACCEL,"Button", + BS_AUTORADIOBUTTON,13,26,85,10 + CONTROL "Spline",IDC_PASS_ANIM_EASE_IN_SPLINE,"Button", + BS_AUTORADIOBUTTON,13,36,35,10 + LTEXT "Time:",IDC_STATIC,17,49,18,8 + CONTROL "",IDC_PASS_ANIM_EASE_IN_TIME,"CustEdit",WS_TABSTOP,38, + 49,24,10 + CONTROL "",IDC_PASS_ANIM_EASE_IN_TIME_SPIN,"SpinnerControl", + WS_TABSTOP,62,49,7,10 + LTEXT "Ease In:",IDC_STATIC,13,7,30,8 + LTEXT "(secs)",IDC_STATIC,72,49,20,8 + LTEXT "Min:",IDC_STATIC,21,62,14,8 + CONTROL "",IDC_PASS_ANIM_EASE_IN_MIN,"CustEdit",WS_TABSTOP,38,60, + 24,10 + CONTROL "",IDC_PASS_ANIM_EASE_IN_MIN_SPIN,"SpinnerControl", + WS_TABSTOP,62,60,7,10 + LTEXT "(secs)",IDC_STATIC,72,62,20,8 + LTEXT "Max:",IDC_STATIC,19,73,16,8 + CONTROL "",IDC_PASS_ANIM_EASE_IN_MAX,"CustEdit",WS_TABSTOP,38,73, + 24,10 + CONTROL "",IDC_PASS_ANIM_EASE_IN_MAX_SPIN,"SpinnerControl", + WS_TABSTOP,62,73,7,10 + LTEXT "(secs)",IDC_STATIC,72,73,20,8 + CONTROL "None",IDC_PASS_ANIM_EASE_OUT_NONE,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,111,17,33,10 + CONTROL "Constant Acceleration", + IDC_PASS_ANIM_EASE_OUT_CONST_ACCEL,"Button", + BS_AUTORADIOBUTTON,111,26,85,10 + CONTROL "Spline",IDC_PASS_ANIM_EASE_OUT_SPLINE,"Button", + BS_AUTORADIOBUTTON,111,36,35,10 + LTEXT "Time:",IDC_STATIC,115,49,18,8 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_TIME,"CustEdit",WS_TABSTOP,136, + 49,24,10 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_TIME_SPIN,"SpinnerControl", + WS_TABSTOP,160,49,7,10 + LTEXT "Ease Out:",IDC_STATIC,111,7,32,8 + LTEXT "(secs)",IDC_STATIC,170,49,20,8 + LTEXT "Min:",IDC_STATIC,119,62,14,8 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_MIN,"CustEdit",WS_TABSTOP,136, + 60,24,10 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_MIN_SPIN,"SpinnerControl", + WS_TABSTOP,160,60,7,10 + LTEXT "(secs)",IDC_STATIC,170,62,20,8 + LTEXT "Max:",IDC_STATIC,117,73,16,8 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_MAX,"CustEdit",WS_TABSTOP,136, + 73,24,10 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_MAX_SPIN,"SpinnerControl", + WS_TABSTOP,160,73,7,10 + LTEXT "(secs)",IDC_STATIC,170,73,20,8 +END + +IDD_PASS_LAYERS DIALOG 0, 0, 217, 93 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_LAYER1,"CustButton",WS_TABSTOP,76,5,116,13 + CONTROL "Top Layer:",IDC_TOP_ON,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,21,22,47,10 + CONTROL "",IDC_LAYER2,"CustButton",WS_TABSTOP,76,21,116,13 + CONTROL "Alpha",IDC_LAYER_ALPHA,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,14,56,34,10 + CONTROL "Add",IDC_LAYER_ADD,"Button",BS_AUTORADIOBUTTON,14,66,29, + 10 + CONTROL "Base alpha only",IDC_OUTPUTA_DISCARD,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,71,56,66,10 + CONTROL "Add",IDC_OUTPUTA_ADD,"Button",BS_AUTORADIOBUTTON,71,66, + 29,10 + CONTROL "Multiply",IDC_OUTPUTA_MULT,"Button",BS_AUTORADIOBUTTON, + 71,76,39,10 + CONTROL "None",IDC_OUTPUTB_NONE,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,149,56,33,10 + CONTROL "Alpha",IDC_OUTPUTB_ALPHA,"Button",BS_AUTORADIOBUTTON, + 149,66,34,10 + CONTROL "Add",IDC_OUTPUTB_ADD,"Button",BS_AUTORADIOBUTTON,149,76, + 29,10 + LTEXT "Layer Alpha Blending:",IDC_ALPHA_TEXT,71,45,70,8 + LTEXT "Layer Blending:",IDC_BLEND_TEXT,14,45,50,8 + LTEXT "Base Layer:",IDC_STATIC,34,6,38,8 + LTEXT "Output Blending:",IDC_BLEND_TEXT3,149,45,54,8 + CONTROL "Multiply",IDC_LAYER_MULTIPLY,"Button", + BS_AUTORADIOBUTTON,13,76,39,10 +END + +IDD_STATIC_ENVMAP_LAYER DIALOGEX 0, 0, 216, 352 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "",IDC_FRONT_NAME,"CustButton",WS_TABSTOP,37,15,63,12 + PUSHBUTTON "Reload",IDC_LAYER_RELOAD,139,66,33,12 + CONTROL "Do not compress",IDC_FORCE_NONCOMPRESSED,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,191,69,10 + LTEXT "Game Detail Scaling:",IDC_STATIC,11,205,67,8 + CONTROL "Allow all scaling",IDC_SCALE_ALL,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,11,215,65,10 + CONTROL "No smaller than 1/2 original",IDC_SCALE_HALF,"Button", + BS_AUTORADIOBUTTON,11,225,102,10 + CONTROL "No scaling",IDC_SCALE_NONE,"Button",BS_AUTORADIOBUTTON, + 11,235,49,10 + CONTROL "Discard Color",IDC_BLEND_NO_COLOR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,126,103,58,10 + CONTROL "Invert Color",IDC_BLEND_INV_COLOR,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,126,114,52,10 + CONTROL "Discard Alpha",IDC_DISCARD_ALPHA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,126,130,60,10 + CONTROL "Invert Alpha",IDC_BLEND_INV_ALPHA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,126,141,54,10 + CONTROL "RGB Intensity",IDC_HSMAX_LAYER_RGBOUT,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,129,184,60,10 + CONTROL "Alpha",IDC_HSMAX_LAYER_ALPHAOUT,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,129,194,60,10 + CONTROL "RGB ",IDC_HSMAX_LAYER_RGBOUT2,"Button", + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,129,222,60,10 + CONTROL "Alpha as Gray",IDC_HSMAX_LAYER_ALPHAOUT2,"Button", + BS_AUTORADIOBUTTON | WS_TABSTOP,129,232,60,10 + CONTROL "Use detail",IDC_USE_DETAIL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,11,265,47,10 + CONTROL "",IDC_DETAIL_START_SIZE_EDIT,"CustEdit",WS_TABSTOP,44, + 299,20,10 + CONTROL "",IDC_DETAIL_START_SIZE_SPIN,"SpinnerControl",0x0,65, + 299,7,10 + CONTROL "",IDC_DETAIL_START_OPAC_EDIT,"CustEdit",WS_TABSTOP,84, + 299,20,10 + CONTROL "",IDC_DETAIL_START_OPAC_SPIN,"SpinnerControl",0x0,105, + 299,7,10 + CONTROL "",IDC_DETAIL_STOP_SIZE_EDIT,"CustEdit",WS_TABSTOP,44, + 310,20,10 + CONTROL "",IDC_DETAIL_STOP_SIZE_SPIN,"SpinnerControl",0x0,65,310, + 7,10 + CONTROL "",IDC_DETAIL_STOP_OPAC_EDIT,"CustEdit",WS_TABSTOP,84, + 310,20,10 + CONTROL "",IDC_DETAIL_STOP_OPAC_SPIN,"SpinnerControl",0x0,105, + 310,7,10 + GROUPBOX "",IDC_STATIC,120,160,88,90 + GROUPBOX "Texture Color/Alpha",IDC_STATIC,120,89,88,68 + LTEXT "Mono Channel Output:",IDC_STATIC,127,173,72,8 + LTEXT "Color Channel Output:",IDC_STATIC,127,212,70,8 + GROUPBOX "Texture Quality",IDC_STATIC,5,178,110,72 + LTEXT "Start %",IDC_STATIC,12,299,23,8 + LTEXT "End %",IDC_STATIC,14,310,21,8 + LTEXT "Texture size on screen:",IDC_STATIC,39,281,38,16 + LTEXT "Opacity:",IDC_STATIC,85,285,25,10 + GROUPBOX "Detail",IDC_STATIC,5,253,202,72 + CONTROL "",IDC_DETAIL_SAMPLE,"Static",SS_BLACKFRAME,140,264,60, + 56 + CTEXT "Todo:\nGraphic Doodad",IDC_STATIC,143,281,54,18 + RTEXT "Front:",IDC_STATIC,11,17,22,8,0,WS_EX_RIGHT + CONTROL "",IDC_LEFT_NAME,"CustButton",WS_TABSTOP,37,31,63,12 + RTEXT "Left:",IDC_STATIC,11,33,22,8,0,WS_EX_RIGHT + CONTROL "",IDC_TOP_NAME,"CustButton",WS_TABSTOP,37,47,63,12 + RTEXT "Top:",IDC_STATIC,11,49,22,8,0,WS_EX_RIGHT + CONTROL "",IDC_BACK_NAME,"CustButton",WS_TABSTOP,136,15,63,12 + RTEXT "Back:",IDC_STATIC,110,17,22,8,0,WS_EX_RIGHT + CONTROL "",IDC_RIGHT_NAME,"CustButton",WS_TABSTOP,136,31,63,12 + RTEXT "Right:",IDC_STATIC,110,33,22,8,0,WS_EX_RIGHT + CONTROL "",IDC_BOTTOM_NAME,"CustButton",WS_TABSTOP,136,47,63,12 + RTEXT "Bottom:",IDC_STATIC,103,49,29,8,0,WS_EX_RIGHT + PUSHBUTTON "Load Generated Bitmaps",IDC_LAYER_LOAD_GEN,37,66,94,12 + GROUPBOX "Faces",IDC_STATIC,5,3,202,83 + GROUPBOX "Face Generation",IDC_STATIC,6,89,110,86 + RTEXT "Base Filename:",IDC_STATIC,12,101,48,8 + PUSHBUTTON "",IDC_BASE_FILENAME,63,99,47,12 + CONTROL "",IDC_TEXSIZE_EDIT,"CustEdit",WS_TABSTOP,68,116,25,10 + CONTROL "",IDC_TEXSIZE_SPIN,"SpinnerControl",0x0,94,116,7,10 + RTEXT "Texture Size:",IDC_STATIC,17,116,48,8 + CONTROL "Generate",IDC_GENERATE_FACES,"CustButton",WS_TABSTOP,24, + 157,76,12 + CONTROL "",IDC_FARDIST_EDIT,"CustEdit",WS_TABSTOP,69,142,25,10 + CONTROL "",IDC_FARDIST_SPIN,"SpinnerControl",0x0,95,142,7,10 + CONTROL "Use MAX Fog Settings",IDC_USEMAXFOG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,12,130,87,10 + RTEXT "Far Distance:",IDC_STATIC,17,143,48,8 + CONTROL "Refract",IDC_REFRACT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,12,329,39,10 +END + +IDD_STEALTH_ANIM DIALOG 0, 0, 205, 144 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_NAMES,7,7,34,52,CBS_DROPDOWNLIST | CBS_SORT | NOT + WS_VISIBLE | WS_VSCROLL | WS_TABSTOP + CONTROL "Auto-start",IDC_AUTO_START,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,47,8,46,10 + CONTROL "Loop",IDC_LOOP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,47, + 21,32,10 + COMBOBOX IDC_LOOPS,79,19,95,54,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "None",IDC_PASS_ANIM_EASE_IN_NONE,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,14,60,33,10 + CONTROL "Constant Acceleration", + IDC_PASS_ANIM_EASE_IN_CONST_ACCEL,"Button", + BS_AUTORADIOBUTTON,14,71,85,10 + CONTROL "Spline",IDC_PASS_ANIM_EASE_IN_SPLINE,"Button", + BS_AUTORADIOBUTTON,14,82,35,10 + LTEXT "Time:",IDC_STATIC,18,95,18,8 + CONTROL "",IDC_PASS_ANIM_EASE_IN_TIME,"CustEdit",WS_TABSTOP,39, + 95,24,10 + CONTROL "",IDC_PASS_ANIM_EASE_IN_TIME_SPIN,"SpinnerControl", + WS_TABSTOP,63,95,7,10 + LTEXT "Ease In:",IDC_STATIC,14,50,30,8 + LTEXT "(secs)",IDC_STATIC,73,95,20,8 + LTEXT "Min:",IDC_STATIC,22,107,14,8 + CONTROL "",IDC_PASS_ANIM_EASE_IN_MIN,"CustEdit",WS_TABSTOP,39, + 106,24,10 + CONTROL "",IDC_PASS_ANIM_EASE_IN_MIN_SPIN,"SpinnerControl", + WS_TABSTOP,63,106,7,10 + LTEXT "(secs)",IDC_STATIC,73,107,20,8 + LTEXT "Max:",IDC_STATIC,20,118,16,8 + CONTROL "",IDC_PASS_ANIM_EASE_IN_MAX,"CustEdit",WS_TABSTOP,39, + 118,24,10 + CONTROL "",IDC_PASS_ANIM_EASE_IN_MAX_SPIN,"SpinnerControl", + WS_TABSTOP,63,118,7,10 + LTEXT "(secs)",IDC_STATIC,73,118,20,8 + CONTROL "None",IDC_PASS_ANIM_EASE_OUT_NONE,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,108,60,33,10 + CONTROL "Constant Acceleration", + IDC_PASS_ANIM_EASE_OUT_CONST_ACCEL,"Button", + BS_AUTORADIOBUTTON,108,71,85,10 + CONTROL "Spline",IDC_PASS_ANIM_EASE_OUT_SPLINE,"Button", + BS_AUTORADIOBUTTON,108,82,35,10 + LTEXT "Time:",IDC_STATIC,112,95,18,8 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_TIME,"CustEdit",WS_TABSTOP,133, + 95,24,10 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_TIME_SPIN,"SpinnerControl", + WS_TABSTOP,157,95,7,10 + LTEXT "Ease Out:",IDC_STATIC,108,50,32,8 + LTEXT "(secs)",IDC_STATIC,167,95,20,8 + LTEXT "Min:",IDC_STATIC,116,107,14,8 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_MIN,"CustEdit",WS_TABSTOP,133, + 106,24,10 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_MIN_SPIN,"SpinnerControl", + WS_TABSTOP,157,106,7,10 + LTEXT "(secs)",IDC_STATIC,167,107,20,8 + LTEXT "Max:",IDC_STATIC,114,118,16,8 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_MAX,"CustEdit",WS_TABSTOP,133, + 118,24,10 + CONTROL "",IDC_PASS_ANIM_EASE_OUT_MAX_SPIN,"SpinnerControl", + WS_TABSTOP,157,118,7,10 + LTEXT "(secs)",IDC_STATIC,167,118,20,8 + GROUPBOX "Ease Curve Parameters",IDC_STATIC,7,37,189,100 +END + +IDD_PASS_ANIM DIALOG 0, 0, 217, 193 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_NAMES,58,25,95,52,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "Auto-start",IDC_AUTO_START,"Button",BS_AUTOCHECKBOX | + NOT WS_VISIBLE | WS_TABSTOP,31,166,46,10 + CONTROL "Loop",IDC_LOOP,"Button",BS_AUTOCHECKBOX | NOT + WS_VISIBLE | WS_TABSTOP,20,168,32,10 + COMBOBOX IDC_LOOPS,52,166,95,54,CBS_DROPDOWNLIST | CBS_SORT | NOT + WS_VISIBLE | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Refresh",IDC_REFRESH_ANIMS,160,25,39,12 + LTEXT "Name:",IDC_STATIC,32,27,22,8 + CONTROL "Animate on Global:",IDC_MTL_USE_GLOBAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,19,10,75,10 + CONTROL "",IDC_PLACEHOLDER,"Static",SS_BLACKFRAME | NOT + WS_VISIBLE,7,43,200,143 + COMBOBOX IDC_MTL_GLOBAL_NAME,95,7,95,54,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP +END + +IDD_MAX_CAMERA_LAYER DIALOGEX 0, 0, 217, 130 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + CONTROL "Specify Camera (Instead Of Reflection):", + IDC_CAM_LAYER_EXPLICIT_CAM,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,4,3,140,10 + CONTROL "Pick",IDC_CAM_LAYER_CAMERA,"CustButton",WS_TABSTOP,17, + 25,83,10 + COMBOBOX IDC_CAM_LAYER_UV_SRC,107,24,92,72,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + LTEXT "UV Source:",IDC_STATIC,109,14,43,8 + CONTROL "Pick",IDC_CAM_LAYER_ROOT_NODE,"CustButton",WS_TABSTOP, + 17,83,181,10 + LTEXT "Reference Node (Must have a Water->EnvironmentMap component). For reflections, this node determines the plane of reflection (Z axis of pivot point). For a specified camera, you probably want this to be the camera node.", + IDC_STATIC,9,47,200,32 + LTEXT "Camera Node:",IDC_STATIC,18,14,66,8 + CONTROL "",IDC_CAM_LAYER_DISABLE_COLOR,"ColorSwatch",0x0,107,107, + 27,12 + LTEXT "If reflections are disabled, render this solid color instead:", + IDC_STATIC,9,105,100,16 + CONTROL "Override Disable",IDC_CAM_LAYER_FORCE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,139,108,65,9 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PASS_BASIC, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 210 + VERTGUIDE, 54 + VERTGUIDE, 80 + TOPMARGIN, 7 + BOTTOMMARGIN, 77 + END + + IDD_BUMP_ANIM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 46 + END + + IDD_BUMP_BASIC, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 210 + VERTGUIDE, 72 + VERTGUIDE, 100 + TOPMARGIN, 7 + BOTTOMMARGIN, 37 + END + + IDD_CLOTHING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 210 + TOPMARGIN, 7 + BOTTOMMARGIN, 265 + END + + IDD_COMPOSITE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 210 + TOPMARGIN, 7 + BOTTOMMARGIN, 63 + END + + IDD_DECAL_BASIC, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 37 + END + + IDD_DECAL_LAYERS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 192 + TOPMARGIN, 7 + BOTTOMMARGIN, 53 + END + + IDD_DYN_TEXT_LAYER, DIALOG + BEGIN + RIGHTMARGIN, 214 + BOTTOMMARGIN, 221 + END + + IDD_DYNAMIC_ENVMAP_LAYER, DIALOG + BEGIN + RIGHTMARGIN, 214 + BOTTOMMARGIN, 136 + END + + IDD_LAYER_BASIC, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 209 + TOPMARGIN, 3 + BOTTOMMARGIN, 34 + END + + IDD_LAYER_TEX, DIALOG + BEGIN + RIGHTMARGIN, 214 + BOTTOMMARGIN, 296 + END + + IDD_MULTIPASS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 189 + END + + IDD_PARTICLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 210 + TOPMARGIN, 7 + BOTTOMMARGIN, 115 + END + + IDD_PASS_ADV, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 212 + TOPMARGIN, 7 + BOTTOMMARGIN, 143 + END + + IDD_PASS_ANIM_EASE, DIALOG + BEGIN + LEFTMARGIN, 6 + RIGHTMARGIN, 207 + TOPMARGIN, 7 + BOTTOMMARGIN, 83 + END + + IDD_PASS_LAYERS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 101 + TOPMARGIN, 7 + BOTTOMMARGIN, 86 + END + + IDD_STATIC_ENVMAP_LAYER, DIALOG + BEGIN + RIGHTMARGIN, 214 + BOTTOMMARGIN, 344 + END + + IDD_STEALTH_ANIM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 196 + TOPMARGIN, 7 + BOTTOMMARGIN, 137 + END + + IDD_PASS_ANIM, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 207 + TOPMARGIN, 7 + BOTTOMMARGIN, 186 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_MASKBUTTONS BITMAP "..\\MaxPlasmaMtls\\res\\dmtlmsk.bmp" +IDB_DETAILBGND BITMAP "..\\MaxPlasmaMtls\\res\\detailBgnd8bit.bmp" +IDB_BUTTONS BITMAP "..\\MaxPlasmaMtls\\res\\dmtlbut.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_STEALTH_ANIM DLGINIT +BEGIN + 1142, 0x403, 132, 0 +0x6854, 0x7369, 0x6320, 0x6d6f, 0x6f62, 0x6920, 0x2073, 0x7270, 0x7365, +0x6e65, 0x2074, 0x7562, 0x2074, 0x6968, 0x6464, 0x6e65, 0x7320, 0x206f, +0x6577, 0x6320, 0x6e61, 0x7320, 0x6168, 0x6572, 0x6420, 0x6169, 0x6f6c, +0x2067, 0x7270, 0x636f, 0x2073, 0x6562, 0x7774, 0x6565, 0x206e, 0x6874, +0x7369, 0x6120, 0x646e, 0x7420, 0x6568, 0x6120, 0x696e, 0x206d, 0x6f63, +0x706d, 0x6e6f, 0x6e65, 0x2c74, 0x7720, 0x6968, 0x6863, 0x6e20, 0x6565, +0x7364, 0x662f, 0x6c69, 0x736c, 0x7420, 0x6968, 0x2073, 0x6f63, 0x626d, +0x206f, 0x756f, 0x0074, + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_LAYER "Plasma Layer" + IDS_MAX_CAMERA_LAYER "Plasma Camera Layer" + IDS_PASS_MTL "Plasma Standard" + IDS_PASS_BASIC "Basic Parameters" + IDS_PASS_ADV "Advanced Parameters" + IDS_PASS_LAYERS "Layer Parameters" + IDS_PASS_OUTPUT "Output Parameters" + IDS_LAYER_TEX "Texture Parameters" + IDS_LAYER_BASIC "Purpose/Usage" + IDS_MULTI_MTL "Plasma Multipass" + IDS_DECAL_MTL "Plasma Decal" + IDS_COMP_MTL "Plasma Composite" + IDS_PASS_ANIM "Animation Parameters" + IDS_STATIC_ENVMAP_LAYER "Plasma Static EnvMap" + IDS_PARTICLE_HEIGHT "Height" + IDS_DYNAMIC_ENVMAP_LAYER "Plasma Dynamic EnvMap" +END + +STRINGTABLE +BEGIN + IDS_BITMAP_CLIPU "Clip U" + IDS_BITMAP_CLIPV "Clip V" + IDS_BITMAP_CLIPW "Clip W" + IDS_BITMAP_CLIPH "Clip H" + IDS_STATIC_ENVMAP_LAYER_TEX "Static EnvironmentMap Parameters" + IDS_CUBIC_RENDER_TITLE "Rendering Cubic Faces..." + IDS_BITMAP_TEXSIZE "Texture Size" + IDS_BITMAP_BASENAME "Base Filename" + IDS_SELECT_NODE "Select a MAX node to render the faces from" + IDS_PARTICLE_MTL "Plasma Particle" + IDS_PARTICLE_OPACITY "Opacity" + IDS_PARTICLE_COLOR "Color" + IDS_PARTICLE_WIDTH "Width" + IDS_PARTICLE_AMB_COLOR "Ambient Color" + IDS_SELECT_ANCHOR "Select a MAX node to use as the rendering anchor" + IDS_DYNAMIC_ENVMAP_LAYER_TEX "Dynamic EnvironmentMap Parameters" +END + +STRINGTABLE +BEGIN + IDS_BASIC_AMB "Ambient" + IDS_BASIC_COLOR "Static Color" + IDS_BASIC_OPAC "Opacity" + IDS_PASS_ANIM_EASE "Ease In/Out" + IDS_MAX_CAMERA_LAYER_PROPS "Camera Layer Props" + IDS_CAM_LAYER_CAMERA "Select a Camera" + IDS_CAM_LAYER_ROOT_NODE "Select a Reference Node" + IDS_BASIC_RUNCOLOR "Runtime Color" + IDS_BASIC_SPECCOLOR "Specular Color" + IDS_BUMP_MTL "Plasma Bumpmap" +END + +STRINGTABLE +BEGIN + IDS_DYN_TEXT_LAYER "Plasma Dynamic Text" + IDS_DYN_TEXT_LAYER_PROPS "Dynamic Text Properties" + IDS_CLOTHING_MTL "Plasma Clothing" + IDS_ANGLE_ATTEN_LAYER "Plasma Angle Layer" + IDS_ANGLE_ATTEN_LAYER_PROPS "Angle attenuation properties" + IDS_ANIMSTEALTH "Secret Animation Stealth Info" + IDS_STEALTH_NAME "Animation Parameters" + IDS_NTWATCHER_NAME "NoteTrack Watcher" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Shaders.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Shaders.cpp new file mode 100644 index 00000000..24a0aeb8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Shaders.cpp @@ -0,0 +1,285 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "max.h" +//#include "mtlhdr.h" +#include "Shaders.h" + +//=========================================================================== +// Useful variables... +//=========================================================================== + +static PhongShader phongShader; +static BlinnShader blinnShader; +static MetalShader metalShader; +static hsMaxShader plasmaShader; + +static Shader *shaders[3] = { + &phongShader, + &metalShader, + &blinnShader, +}; + +AColor black(0.0f,0.0f,0.0f,0.0f); + +//=========================================================================== +// Useful functions... +//=========================================================================== + +Shader *GetShader(int s) { return shaders[s]; }; + +#if 1 +// Quadratic +static inline float Soften(float r) { + return r*(2.0f-r); +} +#else +// Cubic +static inline float Soften(float r) { + return r*r*(3.0f-2.0f*r); +} +#endif + +float CompK(float f0) { + return float(2.0*sqrt(f0)/sqrt(1.0-f0)); +} + +float fres_metal(float c, float k) { + float b,rpl, rpp,c2; + b = k*k + 1.0f; + c2 = c*c; + rpl = (b*c2-2*c+1)/(b*c2+2*c+1); + rpp = (b-2*c+c2)/(b+2*c+c2); + return (.5f*(rpl+rpp)); +} + +//=========================================================================== +// Phong shader... don't know if this is ever going to be used +//=========================================================================== + +void PhongShader::Illum(ShadeContext &sc, SIllumParams &ip) { + LightDesc *l; + Color lightCol; + BOOL is_shiny; + Point3 R; + if (is_shiny=(ip.sh_str>0.0f)) + R = sc.ReflectVector(); + + for (int i=0; iIlluminate(sc,ip.N,lightCol,L,NL,diffCoef)) { + // diffuse + if (NL<=0.0f) + continue; + if (l->affectDiffuse) + ip.diffIllum += diffCoef*lightCol; + if (is_shiny&&l->affectSpecular) { + // specular (Phong) + float c = DotProd(L,R); + if (c>0.0f) { + if (ip.softThresh!=0.0&&diffCoef0.0f)) + R = sc.ReflectVector(); + + for (int i=0; iIlluminate(sc,ip.N,lightCol,L,NL,diffCoef)) { + // diffuse + if (NL<=0.0f) + continue; + if (l->affectDiffuse) + ip.diffIllum += diffCoef*lightCol; + if (is_shiny&&l->affectSpecular) { + // specular + float c = DotProd(L,R); + if (c>0.0f) { + c = (float)pow((double)c, (double)ip.ph_exp); // could use table lookup for speed + ip.specIllum += c*ip.sh_str*lightCol; + } + } + } + } + ip.specIllum *= ip.spec; +} + +//=========================================================================== +// Blinn shader... don't know if this is ever going to be used +//=========================================================================== + +void BlinnShader::Illum(ShadeContext &sc, SIllumParams &ip) { + LightDesc *l; + Color lightCol; + + // Blinn style phong + BOOL is_shiny=(ip.sh_str>0.0f)?1:0; + double ph_exp = double(ip.ph_exp)*4.0; // This is to make the hilite compatible with normal phong + for (int i=0; iIlluminate(sc,ip.N,lightCol,L,NL,diffCoef)) { + // diffuse + if (NL<=0.0f) + continue; + + if (l->affectDiffuse) + ip.diffIllum += diffCoef*lightCol; + + // specular (Phong) + if (is_shiny&&l->affectSpecular) { + Point3 H = FNormalize(L-ip.V); + float c = DotProd(ip.N,H); + if (c>0.0f) { + if (ip.softThresh!=0.0&&diffCoef0.0f) { + NV = -DotProd(ip.N,ip.V); // N dot V: view vector is TOWARDS us. + is_shiny = 1; + float r = 1.0f-ip.shine; + if (r==0.0f) r = .00001f; + m2inv = 1.0f/(r*r); + } + else + is_shiny = 0; + + + for (int i=0; iIlluminate(sc,ip.N,lightCol,L,NL,diffCoef)) + continue; + + // diffuse + if (NL>0.0f&&l->affectDiffuse) // TBD is the NL test necessary? + ip.diffIllum += diffCoef*lightCol; + + if (is_shiny&&l->affectSpecular) { // SPECULAR + Color fcol; + float LH,NH,VH; + float sec2; // Was double?? TBD + Point3 H; + + if (NV<0.0f) continue; + + H = FNormalize(L-ip.V); + + LH = DotProd(L,H); // cos(phi) + NH = DotProd(ip.N,H); // cos(alpha) + if (NH==0.0f) continue; + VH = -DotProd(ip.V,H); + + // compute geometrical attenuation factor + float G = (NV0.0f) { + // Compute (approximate) indices of refraction + // this can be factored out for non-texture-mapped mtls + if (!gotKav) { + fav0 = Intens(ip.diff); + if (fav0>=1.0f) fav0 = .9999f; + kav = CompK(fav0); + gotKav = TRUE; + } + + float fav = fres_metal(LH,kav); + float t = (fav-fav0)/(1.0f-fav0); + fcol = (1.0f-t)*ip.diff + Color(t,t,t); + + // Beckman distribution (from Cook-Torrance paper) + sec2 = 1.0f/(NH*NH); // 1/sqr(cos) + float D = (.5f/PI)*sec2*sec2*m2inv*(float)exp((1.0f-sec2)*m2inv); + if (G>1.0f) G = 1.0f; + float Rs = ip.sh_str*D*G/(NV+.05f); + ip.specIllum += fcol*Rs*lightCol; + } + } + } + ip.diffIllum *= 1.0f - ip.sh_str; +} + + +void MetalShader::SetShininess(float shininess, float shineStr) { + float r = 1.0f-shininess; + if (r==0.0f) r = .00001f; + fm2inv = 1.0f/(r*r); + fshin_str = shineStr; +} + +float MetalShader::EvalHilite(float x) { + float c = (float)cos(x*PI); + float sec2 = 1.0f/(c*c); /* 1/sqr(cos) */ + return fshin_str*(.5f/PI)*sec2*sec2*fm2inv*(float)exp((1.0f-sec2)*fm2inv); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Shaders.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Shaders.h new file mode 100644 index 00000000..a79f07c2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/Shaders.h @@ -0,0 +1,140 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef __SHADERS_H +#define __SHADERS_H + +class Shader; + +//=========================================================================== +// Interface tools... +//=========================================================================== + +struct SIllumParams { + unsigned long flags; + float sh_str, ph_exp, shine, softThresh; + Color amb, diff, spec; + Point3 N, V; + Color diffIllum, specIllum; +}; + +const int SHADER_PHONG = 0; +const int SHADER_METAL = 1; +const int SHADER_BLINN = 2; +const int SHADER_PLASMA = 3; + +// Return shader of given type, using indices above... +Shader *GetShader(int s); + +extern AColor black; + +//=========================================================================== +// Abstract shader... +//=========================================================================== + +class Shader { +public: + virtual void Illum(ShadeContext &sc, SIllumParams &ip)=0; + virtual void AffectReflMap(ShadeContext &sc, SIllumParams &ip, Color &rcol)=0; + virtual void SetShininess(float shininess, float shineStr)=0; + virtual float EvalHilite(float x)=0; +}; + +//=========================================================================== +// Phong shader... +//=========================================================================== + +class PhongShader : public Shader { + float fs; + float shin_str; +public: + void Illum(ShadeContext &sc, SIllumParams &ip); + void AffectReflMap(ShadeContext &sc, SIllumParams &ip, Color &rcol) { rcol *= ip.spec; }; + void SetShininess(float shininess, float shineStr) { + fs = (float)pow(2.0,shininess*10.0); + shin_str = shineStr; + } + float EvalHilite(float x) { + return shin_str*(float)pow((double)cos(x*PI),(double)fs); + } +}; + +//=========================================================================== +// Blinn shader... +//=========================================================================== + +class BlinnShader : public Shader { + float fs; + float shin_str; +public: + void Illum(ShadeContext &sc, SIllumParams &ip); + void AffectReflMap(ShadeContext &sc, SIllumParams &ip, Color &rcol) { rcol *= ip.spec; }; + void SetShininess(float shininess, float shineStr) { + fs = (float)pow(2.0,shininess*10.0); + shin_str = shineStr; + } + float EvalHilite(float x) { + return shin_str*(float)pow((double)cos(x*PI),(double)fs); + } +}; + +//=========================================================================== +// Metal shader... +//=========================================================================== + +class MetalShader : public Shader { + float fm2inv, fshin_str; +public: + void Illum(ShadeContext &sc, SIllumParams &ip); + void AffectReflMap(ShadeContext &sc, SIllumParams &ip, Color &rcol) { rcol *= ip.diff; }; + void SetShininess(float shininess, float shineStr); + float EvalHilite(float x); +}; + +//=========================================================================== +// hsMax layer shader... +//=========================================================================== + +class hsMaxShader : public Shader { + float fs; + float shin_str; +public: + void Illum(ShadeContext &sc, SIllumParams &ip); + void AffectReflMap(ShadeContext &sc, SIllumParams &ip, Color &rcol) { rcol *= ip.spec; }; + void SetShininess(float shininess, float shineStr) { + fs = (float)pow(2.0,shininess*10.0); + shin_str = shineStr; + } + float EvalHilite(float x) { + return shin_str*(float)pow((double)cos(x*PI),(double)fs); + } +}; + + + + + +#endif + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plBMSampler.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plBMSampler.cpp new file mode 100644 index 00000000..9ad95c7f --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plBMSampler.cpp @@ -0,0 +1,206 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "max.h" +#include "texutil.h" +#include "iparamb2.h" + +#include +#include "bmmlib.h" +#include "bitmap.h" + +#include "Layers\plPlasmaMAXLayer.h" +#include "plBMSampler.h" + +plBMSampler::plBMSampler(plPlasmaMAXLayer *layer, Bitmap *bm) : fBM(bm), fInitialized(false) +{ + // Get our parameters + if( fBM && layer && layer->GetSamplerInfo( &fData ) ) + { + u1 = fData.fClipU + fData.fClipW; + v1 = fData.fClipV + fData.fClipH; + bmw = fBM->Width(); + bmh = fBM->Height(); + fbmw = float(bmw-1); + fbmh = float(bmh-1); + clipx = int(fData.fClipU*fbmw); + clipy = int(fData.fClipV*fbmh); + fclipw = fData.fClipW*fbmw; + fcliph = fData.fClipH*fbmh; + cliph = fcliph; + + fInitialized = true; + } +} + +int plBMSampler::PlaceUV(ShadeContext &sc, float &u, float &v, int iu, int iv) +{ + if (!fInitialized) + return 1; + + if (uu1||v>v1) + return 0; + + u = (u-fData.fClipU)/fData.fClipW; + v = (v-fData.fClipV)/fData.fClipH; + + return 1; +} + +void plBMSampler::PlaceUVFilter(ShadeContext &sc, float &u, float &v, int iu, int iv) +{ + if (!fInitialized) + return; + + u = (u-fData.fClipU)/fData.fClipW; + v = (v-fData.fClipV)/fData.fClipH; +} + +AColor plBMSampler::Sample(ShadeContext& sc, float u,float v) +{ + AColor none(0.0f, 0.0f, 0.0f, 0.0f); + + if (!fInitialized) + return none; + + BMM_Color_64 c; + int x,y; + float fu,fv; + fu = frac(u); + fv = 1.0f-frac(v); + if (fData.fEnableCrop) + { + if (fData.fCropPlacement) + { + if (!PlaceUV(sc,fu, fv, int(u), int(v))) + return AColor(0,0,0,0); + x = (int)(fu*fbmw+0.5f); + y = (int)(fv*fbmh+0.5f); + } + else + { + x = mod(clipx + (int)(fu*fclipw+0.5f),bmw); + y = mod(clipy + (int)(fv*fcliph+0.5f),bmh); + } + } + else + { + x = (int)(fu*fbmw+0.5f); + y = (int)(fv*fbmh+0.5f); + } + fBM->GetLinearPixels(x,y,1,&c); + switch(fData.fAlphaSource) + { + case plBMSamplerData::kDiscard: + c.a = 0xffff; + break; + case plBMSamplerData::kFromRGB: + c.a = (c.r+c.g+c.b)/3; + break; + // TBD + // XPCOL needs to be handled in bitmap for filtering. + // Need to open a bitmap with this property. + // case ALPHA_XPCOL: break; + } + return c; +} + + +AColor plBMSampler::SampleFilter(ShadeContext& sc, float u,float v, float du, float dv) +{ + AColor none(0.0f, 0.0f, 0.0f, 0.0f); + + if (!fInitialized) + return none; + + fBM->SetFilter(BMM_FILTER_PYRAMID); + + BMM_Color_64 c; + float fu,fv; + fu = frac(u); + fv = 1.0f-frac(v); + if (fData.fEnableCrop) + { + if (fData.fCropPlacement) + { + PlaceUVFilter(sc,fu, fv, int(u), int(v)); + du /= fData.fClipW; + dv /= fData.fClipH; + float du2 = 0.5f*du; + float ua = fu-du2; + float ub = fu+du2; + if (ub<=0.0f||ua>=1.0f) return none; + float dv2 = 0.5f*dv; + float va = fv-dv2; + float vb = fv+du2; + if (vb<=0.0f||va>=1.0f) return none; + BOOL clip = 0; + if (ua<0.0f) { ua=0.0f; clip = 1; } + if (ub>1.0f) { ub=1.0f; clip = 1; } + if (va<0.0f) { va=0.0f; clip = 1; } + if (vb>1.0f) { vb=1.0f; clip = 1; } + fBM->GetFiltered(fu,fv,du,dv,&c); + switch(fData.fAlphaSource) + { + case plBMSamplerData::kDiscard: + c.a = 0xffff; + break; + case plBMSamplerData::kFromRGB: + c.a = (c.r + c.g + c.b)/3; + break; + } + AColor ac(c); + if (clip) + { + float f = ((ub-ua)/du) * ((vb-va)/dv); + ac *= f; + } + return ac; + } + else + { + fu = (fData.fClipU + fData.fClipW*fu); + fv = (fData.fClipV + fData.fClipH*fv); + du *= fData.fClipW; + dv *= fData.fClipH; + fBM->GetFiltered(fu,fv,du,dv,&c); + } + } + else + fBM->GetFiltered(fu,fv,du,dv,&c); + + switch (fData.fAlphaSource) + { + case plBMSamplerData::kDiscard: + c.a = 0xffff; + break; + case plBMSamplerData::kFromRGB: + c.a = (c.r + c.g + c.b)/3; + break; + } + + return c; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plBMSampler.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plBMSampler.h new file mode 100644 index 00000000..ca3b0980 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plBMSampler.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plBMSampler_h +#define _plBMSampler_h + +#include "max.h" +#include "imtl.h" + +class plPlasmaMAXLayer; + +class plBMSamplerData +{ + public: + bool fEnableCrop; + int fCropPlacement; + float fClipU, fClipV; + float fClipW, fClipH; + + enum ASource + { + kFromTexture, + kFromRGB, + kDiscard + }; + + ASource fAlphaSource; + + plBMSamplerData() + { + fEnableCrop = false; + fCropPlacement = 0; + fClipU = fClipV = 0.f; + fClipW = fClipH = 1.f; + fAlphaSource = kFromTexture; + } +}; + +class plBMSampler : public MapSampler +{ +protected: + Bitmap *fBM; + + plBMSamplerData fData; + + float u1,v1; + int bmw,bmh,clipx, clipy, cliph; + float fclipw,fcliph, fbmh, fbmw; + bool fInitialized; + + plBMSampler() {} + +public: + plBMSampler(plPlasmaMAXLayer *layer, Bitmap *bm); + int PlaceUV(ShadeContext& sc, float &u, float &v, int iu, int iv); + void PlaceUVFilter(ShadeContext& sc, float &u, float &v, int iu, int iv); + AColor Sample(ShadeContext& sc, float u,float v); + AColor SampleFilter(ShadeContext& sc, float u,float v, float du, float dv); + // float SampleMono(ShadeContext& sc, float u,float v); + // float SampleMonoFilter(ShadeContext& sc, float u,float v, float du, float dv); +}; + +#endif //_plBMSampler_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDetailCurveCtrl.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDetailCurveCtrl.cpp new file mode 100644 index 00000000..27f65f98 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDetailCurveCtrl.cpp @@ -0,0 +1,680 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDetailCurveCtrl Class Functions // +// Custom Win32 Control class for drawing the detail map opacity curve so // +// the artists can figure out what the hell is going on. // +// Cyan, Inc. // +// // +// To use: // +// 1. Create a new plDetailCurveCtrl, giving it a parent window and a // +// client rect. // +// 2. Set the start and end percentages, along with the start and end // +// opacities. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 10.1.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plDetailCurveCtrl.h" +#include "resource.h" + + +//// Static Stuff ///////////////////////////////////////////////////////////// + +int plDetailCurveCtrl::fClassRefCnt = 0; +HINSTANCE plDetailCurveCtrl::fInstance = NULL; +HBITMAP plDetailCurveCtrl::fBgndImage = NULL; +HFONT plDetailCurveCtrl::fFont = NULL; + +#ifdef MCN_TWO_GRAPH_MODE +HBITMAP plDetailCurveCtrl::fBgndImage2 = NULL; +bool plDetailCurveCtrl::fXAsMipmapLevel = false; +#endif + +const char gCtrlClassName[] = "DetailCurveClass"; + +#define kHiResStep 0.01f + + +void plDetailCurveCtrl::IRegisterCtrl( HINSTANCE instance ) +{ + if( fClassRefCnt == 0 ) + { + fInstance = instance; + + WNDCLASSEX clInfo; + + memset( &clInfo, 0, sizeof( clInfo ) ); + clInfo.cbSize = sizeof( clInfo ); + clInfo.style = CS_OWNDC | CS_NOCLOSE; + clInfo.lpfnWndProc = (WNDPROC)IWndProc; + clInfo.cbClsExtra = 0; + clInfo.cbWndExtra = 0; + clInfo.hInstance = fInstance; + clInfo.hIcon = NULL; + clInfo.hCursor = LoadCursor( NULL, IDC_CROSS ); + clInfo.hbrBackground = NULL; + clInfo.lpszMenuName = NULL; + clInfo.lpszClassName = gCtrlClassName; + clInfo.hIconSm = NULL; + + RegisterClassEx( &clInfo ); + + fBgndImage = (HBITMAP)LoadImage( fInstance, MAKEINTRESOURCE( IDB_DETAILBGND ), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR ); +#ifdef MCN_TWO_GRAPH_MODE + fBgndImage2 = (HBITMAP)LoadImage( fInstance, MAKEINTRESOURCE( IDB_DETAILBGND2 ), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR ); +#endif + + HDC hDC = GetDC( NULL ); + fFont = CreateFont( -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY ), 72 ), 0, 0, 0, 0, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Arial" ); + ReleaseDC( NULL, hDC ); + } + + fClassRefCnt++; +} + +void plDetailCurveCtrl::IUnregisterCtrl( void ) +{ + fClassRefCnt--; + if( fClassRefCnt == 0 ) + { + UnregisterClass( gCtrlClassName, fInstance ); + if( fFont != NULL ) + DeleteObject( fFont ); + } +} + +//// Constructor & Destructor ///////////////////////////////////////////////// + +plDetailCurveCtrl::plDetailCurveCtrl( HWND parentWnd, WPARAM id, RECT *clientRect, HINSTANCE instance ) +{ + // Class init + if( instance == NULL ) + instance = (HINSTANCE)GetWindowLong( parentWnd, GWL_HINSTANCE ); + IRegisterCtrl( instance ); + + // Per-object init + fDblDC = NULL; + fDblBitmap = NULL; + fStartPercent = 0; + fStartOpac = 0; + fEndPercent = 1.f; + fEndOpac = 1.f; + fNumLevels = 8; + fDraggingStart = fDraggingEnd = false; + fCanDragStart = fCanDragEnd = false; + + // Note: we create originally as disabled since the default detail setting is disabled. + // The MAX Update stuff should change this if necessary after we're created + fHWnd = ::CreateWindowEx( WS_EX_CLIENTEDGE, gCtrlClassName, "Detail Curve", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_DISABLED, + clientRect->left, clientRect->top, clientRect->right - clientRect->left, + clientRect->bottom - clientRect->top, + parentWnd, (HMENU)id, instance, 0 ); + if( fHWnd == NULL ) + return; + + SetWindowLong( fHWnd, GWL_USERDATA, (LONG)this ); +} + +plDetailCurveCtrl::~plDetailCurveCtrl() +{ + if( fDblDC != NULL ) + { + SelectObject( fDblDC, (HBITMAP)NULL ); + DeleteObject( fDblBitmap ); + DeleteDC( fDblDC ); + } + + if( fWhiteBrush != NULL ) + DeleteObject( fWhiteBrush ); + if( fBluePen != NULL ) + DeleteObject( fBluePen ); + if( fLiteBluePen != NULL ) + DeleteObject( fLiteBluePen ); + if( fBlueBrush != NULL ) + DeleteObject( fBlueBrush ); + +// DestroyWindow( fHWnd ); + IUnregisterCtrl(); +} + +//// IInitDblBuffer /////////////////////////////////////////////////////////// +// Note: For some strange reason, grabbing the HDC of the window doesn't do +// any good, 'cause it's black-and-white (right, THAT makes sense). Grabbing +// the desktop DC works, however. + +void plDetailCurveCtrl::IInitDblBuffer( void ) +{ + if( fDblDC == NULL ) + { + int width, height; + RECT r; + HDC desk = GetDC( NULL ); + + GetClientRect( fHWnd, &r ); + width = r.right - r.left; + height = r.bottom - r.top; + + fDblDC = CreateCompatibleDC( desk ); + fDblBitmap = CreateCompatibleBitmap( desk/*fDblDC*/, width, height ); + SelectObject( fDblDC, fDblBitmap ); + ReleaseDC( NULL, desk ); + + fWhiteBrush = CreateSolidBrush( RGB( 255, 255, 255 ) ); + fBluePen = CreatePen( PS_SOLID, 1, RGB( 0, 0, 255 ) ); + fLiteBluePen = CreatePen( PS_SOLID, 1, RGB( 127, 127, 255 ) ); + fBlueBrush = CreateSolidBrush( RGB( 0, 0, 255 ) ); + + IRefreshDblBuffer(); + } +} + +//// IRefreshDblBuffer //////////////////////////////////////////////////////// + +void plDetailCurveCtrl::IRefreshDblBuffer( void ) +{ + HDC hBgndDC; + RECT clientRect, r; + SIZE bgndSize; + int width, height, x, y; + HPEN oldPen; + BITMAPINFO bmpInfo; + POINT pt1, pt2; + + + IInitDblBuffer(); + + GetClientRect( fHWnd, &clientRect ); + width = clientRect.right - clientRect.left; + height = clientRect.bottom - clientRect.top; + + if( fDblBitmap != NULL ) + { + FillRect( fDblDC, &clientRect, fWhiteBrush ); + + if( fBgndImage != NULL ) + { + + // Draw bgnd + hBgndDC = CreateCompatibleDC( fDblDC ); +#ifdef MCN_TWO_GRAPH_MODE + SelectObject( hBgndDC, fXAsMipmapLevel ? fBgndImage2 : fBgndImage ); +#else + SelectObject( hBgndDC, fBgndImage ); +#endif + + bmpInfo.bmiHeader.biSize = sizeof( bmpInfo.bmiHeader ); + bmpInfo.bmiHeader.biBitCount = 0; + GetDIBits( hBgndDC, fBgndImage, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS ); + bgndSize.cx = bmpInfo.bmiHeader.biWidth; + bgndSize.cy = bmpInfo.bmiHeader.biHeight; + x = ( width - bgndSize.cx ) >> 1; + y = ( height - bgndSize.cy ) >> 1; + + BitBlt( fDblDC, x, y, bgndSize.cx, bgndSize.cy, hBgndDC, 0, 0, SRCCOPY ); + SelectObject( hBgndDC, (HBITMAP)NULL ); + DeleteDC( hBgndDC ); + + /// Draw graph + if( IsWindowEnabled( fHWnd ) ) + { + bgndSize.cx -= 8; + oldPen = (HPEN)SelectObject( fDblDC, fLiteBluePen ); + + /// This line draws the light blue "actual" curve, which shows what happens + /// when you actually interpolate the curve across the mipmap levels. It's + /// more accurate in that it shows the actual values computed for each level, + /// but less accurate because it doesn't take into account mipmap sampling + /// and such. Given the latter, we leave it out (for now) to avoid confusing + /// the artists. +// IDrawCurve( fDblDC, true, x, y, &bgndSize ); + + SelectObject( fDblDC, fBluePen ); + IDrawCurve( fDblDC, false, x, y, &bgndSize ); + + SelectObject( fDblDC, oldPen ); + + if( fStartPercent == 0.07f && fStartOpac == 0.23f && fEndPercent == 0.19f && fEndOpac == 0.79f ) + { + const char str[] = "\x48\x61\x70\x70\x79\x20\x62\x64\x61\x79\x20\x74\x6f\x20\x6d\x63\x6e\x21"; + SetBkMode( fDblDC, TRANSPARENT ); + SetTextColor( fDblDC, RGB( 0, 0, 255 ) ); + SelectObject( fDblDC, fFont ); + TextOut( fDblDC, x, y + bgndSize.cy - 10, str, strlen( str ) ); + } + + // Draw our two markers + IXlateValuesToClientPt( fStartPercent, fStartOpac, &pt1, x, y, &bgndSize ); + SetRect( &fStartDragPt, pt1.x - 4, pt1.y - 4, pt1.x + 4, pt1.y + 4 ); + r = fStartDragPt; + if( !fCanDragStart ) + InflateRect( &r, -2, -2 ); + FillRect( fDblDC, &r, fBlueBrush ); + + IXlateValuesToClientPt( fEndPercent, fEndOpac, &pt2, x, y, &bgndSize ); + SetRect( &fEndDragPt, pt2.x - 4, pt2.y - 4, pt2.x + 4, pt2.y + 4 ); + r = fEndDragPt; + if( !fCanDragEnd ) + InflateRect( &r, -2, -2 ); + FillRect( fDblDC, &r, fBlueBrush ); + } + } + } +} + +//// IDrawCurve /////////////////////////////////////////////////////////////// +// Draw the damned curve. + +void plDetailCurveCtrl::IDrawCurve( HDC hDC, bool clampToInts, int cornerX, int cornerY, SIZE *bgndSize ) +{ + float dist, penX, penBaseY, penXStep, penYScale; + POINT pt1; + + + // Calc stuff + penX = (float)cornerX; + penBaseY = (float)( cornerY + bgndSize->cy ); + penXStep = (float)bgndSize->cx * kHiResStep; + penYScale = (float)bgndSize->cy; + + // Draw curve + pt1.x = (int)penX; + pt1.y = (int)( penBaseY - penYScale * fStartOpac ); + + float artificialBias = 1.f / (float)fNumLevels; // So we never get a howFar less than 0 + float artificialMaxDist = 1.f - artificialBias; + + for( dist = 0.f; dist <= 1.f; dist += kHiResStep ) + { + float opac = IXlateDistToValue( dist, clampToInts ); + + if( dist == 0.f ) + MoveToEx( hDC, (int)penX, (int)( penBaseY - penYScale * opac ), NULL ); + else + LineTo( hDC, (int)penX, (int)( penBaseY - penYScale * opac ) ); + + penX += penXStep; + } +} + +//// IXlateDistToValue //////////////////////////////////////////////////////// +// I.E. from distance across graph to distance up on graph (percentage-wise) + +float plDetailCurveCtrl::IXlateDistToValue( float dist, bool clampToInts ) +{ + const float artificialBias = 1.f / (float)fNumLevels; // So we never get a howFar less than 0 + const float artificialMaxDist = 1.f - artificialBias; + float howFar, opac; + + + howFar = IXlateDistToX( dist, clampToInts ); + + if( howFar < fStartPercent ) + opac = fStartOpac; + else if( howFar > fEndPercent ) + opac = fEndOpac; + else + opac = ( howFar - fStartPercent ) * ( fEndOpac - fStartOpac ) / ( fEndPercent - fStartPercent ) + fStartOpac; + + return opac; +} + +//// IXlateDistToX //////////////////////////////////////////////////////////// +// I.E. from the distance in percentage across the graph to the actual x +// value on the graph + +float plDetailCurveCtrl::IXlateDistToX( float dist, bool clampToInts ) +{ + const float artificialBias = 1.f / (float)fNumLevels; // So we never get a howFar less than 0 + const float artificialMaxDist = 1.f - artificialBias; + float howFar; + + +#ifdef MCN_TWO_GRAPH_MODE + if( fXAsMipmapLevel ) + { + howFar = dist * (float)fNumLevels; + if( clampToInts ) + howFar = (float)( (int)howFar ); + + howFar /= (float)fNumLevels; + return howFar; + } +#endif + + if( dist == 0.f ) + howFar = 0.f; + else + { + howFar = 1.f - ( ( 1.f - dist ) * artificialMaxDist ); + howFar = ( (float)fNumLevels - 1.f / howFar ); + if( howFar < 0.f ) + howFar = 0.f; + else if( howFar > (float)fNumLevels - 1.f ) + howFar = (float)fNumLevels - 1.f; + + if( clampToInts ) + howFar = (float)( (int)howFar ); + + howFar /= (float)fNumLevels - 1.f; + } + + return howFar; +} + +//// IXlateXToDist //////////////////////////////////////////////////////////// +// I.E. from the actual x value of the graph to the actual distance in +// percentage across the graph. + +float plDetailCurveCtrl::IXlateXToDist( float howFar ) +{ + const float artificialBias = 1.f / (float)fNumLevels; // So we never get a howFar less than 0 + const float artificialMaxDist = 1.f - artificialBias; + float dist; + + +#ifdef MCN_TWO_GRAPH_MODE + if( fXAsMipmapLevel ) + { + return howFar; + } +#endif + + if( howFar == 0.f ) + dist = 0.f; + else + { + howFar *= (float)fNumLevels - 1.f; + howFar = 1.f / ( (float)fNumLevels - howFar ); + howFar = ( ( howFar - 1.f ) / artificialMaxDist ) + 1.f; + } + + return howFar; +} + +//// IXlateValuesToClientPt /////////////////////////////////////////////////// +// I.E. from graph x,y values to client coordinates + +void plDetailCurveCtrl::IXlateValuesToClientPt( float x, float y, POINT *pt, int cornerX, int cornerY, SIZE *bgndSize ) +{ + pt->x = cornerX + (int)( IXlateXToDist( x ) * (float)bgndSize->cx ); + pt->y = cornerY + bgndSize->cy; + + pt->y -= (int)( (float)bgndSize->cy * y ); +} + +//// IMapMouseToValues //////////////////////////////////////////////////////// +// Map mouse x,y coordinates in clientspace to graph values. If the last param +// is true, maps to the start point, else maps to the end point + +void plDetailCurveCtrl::IMapMouseToValues( int x, int y, bool mapToStart ) +{ + BITMAPINFO bmpInfo; + int cX, cY, width, height; + RECT clientRect; + float vX, vY; + SIZE bgndSize; + + + if( fBgndImage == NULL || fDblDC == NULL || !IsWindowEnabled( fHWnd ) ) + return; + + GetClientRect( fHWnd, &clientRect ); + width = clientRect.right - clientRect.left; + height = clientRect.bottom - clientRect.top; + + bmpInfo.bmiHeader.biSize = sizeof( bmpInfo.bmiHeader ); + bmpInfo.bmiHeader.biBitCount = 0; + GetDIBits( fDblDC, fBgndImage, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS ); + bgndSize.cx = bmpInfo.bmiHeader.biWidth; + bgndSize.cy = bmpInfo.bmiHeader.biHeight; + cX = ( width - bgndSize.cx ) >> 1; + cY = ( height - bgndSize.cy ) >> 1; + + bgndSize.cx -= 8; + + // Xlate to graph space and clamp + x -= cX; + y = bgndSize.cy - ( y - cY ); + if( x < 0 ) + x = 0; + else if( x > bgndSize.cx ) + x = bgndSize.cx; + if( y < 0 ) + y = 0; + else if( y > bgndSize.cy ) + y = bgndSize.cy; + + vX = IXlateDistToX( (float)x / (float)bgndSize.cx, false ); + vY = (float)y / (float)bgndSize.cy; + + if( mapToStart ) + { + fStartPercent = vX; + fStartOpac = vY; + if( fEndPercent < fStartPercent ) + { + fEndPercent = fStartPercent; + ISendDraggedMessage( false ); + } + } + else + { + fEndPercent = vX; + fEndOpac = vY; + if( fEndPercent < fStartPercent ) + { + fStartPercent = fEndPercent; + ISendDraggedMessage( true ); + } + } + + IRefreshDblBuffer(); + InvalidateRect( fHWnd, NULL, false ); + RedrawWindow( fHWnd, NULL, NULL, RDW_UPDATENOW ); + + ISendDraggedMessage( mapToStart ); +} + +//// ISendDraggedMessage ////////////////////////////////////////////////////// + +void plDetailCurveCtrl::ISendDraggedMessage( bool itWasTheStartPoint ) +{ + HWND parent = GetParent( fHWnd ); + + + if( parent == NULL ) + return; + + SendMessage( parent, PL_DC_POINT_DRAGGED, itWasTheStartPoint ? PL_DC_START_POINT : PL_DC_END_POINT, + (LPARAM)this ); +} + +//// SetStart/EndPoint //////////////////////////////////////////////////////// + +void plDetailCurveCtrl::SetStartPoint( float percentLevel, float opacity ) +{ + fStartPercent = percentLevel; + fStartOpac = opacity; + IRefreshDblBuffer(); + InvalidateRect( fHWnd, NULL, false ); +} + +void plDetailCurveCtrl::SetEndPoint( float percentLevel, float opacity ) +{ + fEndPercent = percentLevel; + fEndOpac = opacity; + IRefreshDblBuffer(); + InvalidateRect( fHWnd, NULL, false ); +} + +void plDetailCurveCtrl::SetNumLevels( int numLevels ) +{ + fNumLevels = numLevels; + IRefreshDblBuffer(); + InvalidateRect( fHWnd, NULL, false ); +} + +//// IWndProc ///////////////////////////////////////////////////////////////// + +LRESULT CALLBACK plDetailCurveCtrl::IWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + HDC hDC; + RECT clientRect; + int width, height; + PAINTSTRUCT pInfo; + POINT pt; + + + plDetailCurveCtrl *ctrl = (plDetailCurveCtrl *)GetWindowLong( hWnd, GWL_USERDATA ); + GetClientRect( hWnd, &clientRect ); + width = clientRect.right - clientRect.left; + height = clientRect.bottom - clientRect.top; + + switch( msg ) + { + case WM_CREATE: + return 0; + + case WM_ENABLE: + if( ctrl != NULL ) + ctrl->IRefreshDblBuffer(); + return 0; + + case WM_PAINT: + BeginPaint( hWnd, &pInfo ); + hDC = (HDC)pInfo.hdc; + + if( ctrl != NULL ) + { + if( ctrl->fDblDC == NULL ) + ctrl->IInitDblBuffer(); + + BitBlt( hDC, 0, 0, width, height, ctrl->fDblDC, 0, 0, SRCCOPY ); + } + + EndPaint( hWnd, &pInfo ); + return 0; + + case WM_ERASEBKGND: + return TRUE; + + case WM_LBUTTONDOWN: + if( ctrl != NULL && !ctrl->fDraggingStart && !ctrl->fDraggingEnd ) + { + pt.x = LOWORD( lParam ); + pt.y = HIWORD( lParam ); + if( PtInRect( &ctrl->fStartDragPt, pt ) ) + { + if( !ctrl->fCanDragStart && !ctrl->fCanDragEnd ) + SetCapture( hWnd ); + ctrl->fDraggingStart = true; + } + else if( PtInRect( &ctrl->fEndDragPt, pt ) ) + { + if( !ctrl->fCanDragStart && !ctrl->fCanDragEnd ) + SetCapture( hWnd ); + ctrl->fDraggingEnd = true; + } + } + return 0; + + case WM_MOUSEMOVE: + if( ctrl != NULL ) + { + pt.x = LOWORD( lParam ); + pt.y = HIWORD( lParam ); + + if( ctrl->fDraggingStart || ctrl->fDraggingEnd ) + { + ctrl->IMapMouseToValues( (short)LOWORD( lParam ), (short)HIWORD( lParam ), ctrl->fDraggingStart ); + } + else if( PtInRect( &ctrl->fStartDragPt, pt ) ) + { + if( !ctrl->fCanDragStart ) + { + ctrl->fCanDragStart = true; + ctrl->fCanDragEnd = false; + SetCapture( hWnd ); + ctrl->IRefreshDblBuffer(); + InvalidateRect( hWnd, NULL, false ); + } + } + else if( PtInRect( &ctrl->fEndDragPt, pt ) ) + { + if( !ctrl->fCanDragEnd ) + { + ctrl->fCanDragEnd = true; + ctrl->fCanDragStart = false; + SetCapture( hWnd ); + ctrl->IRefreshDblBuffer(); + InvalidateRect( hWnd, NULL, false ); + } + } + else if( ctrl->fCanDragStart || ctrl->fCanDragEnd ) + { + ctrl->fCanDragStart = false; + ctrl->fCanDragEnd = false; + ReleaseCapture(); + ctrl->IRefreshDblBuffer(); + InvalidateRect( hWnd, NULL, false ); + } + } + return 0; + + case WM_LBUTTONUP: + if( ctrl != NULL && ( ctrl->fDraggingStart || ctrl->fDraggingEnd ) ) + { + if( !ctrl->fCanDragStart && !ctrl->fCanDragEnd ) + ReleaseCapture(); + ctrl->fDraggingStart = false; + ctrl->fDraggingEnd = false; + } + return 0; + +#ifdef MCN_TWO_GRAPH_MODE + case WM_RBUTTONDOWN: + fXAsMipmapLevel = !fXAsMipmapLevel; + ctrl->IRefreshDblBuffer(); + InvalidateRect( hWnd, NULL, false ); + return 0; +#endif + + case WM_DESTROY: + delete ctrl; + SetWindowLong( hWnd, GWL_USERDATA, 0 ); + return 0; + + default: + return DefWindowProc( hWnd, msg, wParam, lParam ); + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDetailCurveCtrl.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDetailCurveCtrl.h new file mode 100644 index 00000000..45beaf83 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDetailCurveCtrl.h @@ -0,0 +1,128 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plDetailCurveCtrl Class Header // +// Custom Win32 Control class for drawing the detail map opacity curve so // +// the artists can figure out what the hell is going on. // +// Cyan, Inc. // +// // +// To use: // +// 1. Create a new plDetailCurveCtrl, giving it a parent window and a // +// client rect. // +// 2. Set the start and end percentages, along with the start and end // +// opacities. // +// // +//// Version History ////////////////////////////////////////////////////////// +// // +// 10.1.2001 mcn - Created. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _plDetailCurveCtrl_h +#define _plDetailCurveCtrl_h + +#include "Max.h" +#include "resource.h" + + +#define GET_DETAIL_CURVE_CTRL( dlg, id ) (GetDlgItem( dlg, id ) ? (plDetailCurveCtrl *)GetWindowLong( GetDlgItem( dlg, id ), GWL_USERDATA ) : NULL ) + +// Message to parent to let it know a point got dragged. lParam = pointer to control, wParam = 1 if start point, 0 if end point +#define PL_DC_POINT_DRAGGED WM_USER + 50 +#define PL_DC_START_POINT 1 +#define PL_DC_END_POINT 0 + +/// The following #define was for back when I had two graphs, one was with mipmap level +/// as the X axis, the other was as it is now (distance). Uncomment this define to +/// re-enable the two-graph mode (right-click switches) +//#define MCN_TWO_GRAPH_MODE + +//// Class Definition ///////////////////////////////////////////////////////// + +class plDetailCurveCtrl +{ + protected: + + HWND fHWnd; + + HDC fDblDC; + HBITMAP fDblBitmap; + HBRUSH fWhiteBrush, fBlueBrush; + HPEN fBluePen, fLiteBluePen; + + RECT fStartDragPt, fEndDragPt; + + bool fDraggingStart, fDraggingEnd; + bool fCanDragStart, fCanDragEnd; + + int fNumLevels; + float fStartPercent, fEndPercent; + float fStartOpac, fEndOpac; + + void IInitDblBuffer( void ); + void IRefreshDblBuffer( void ); + void IDrawCurve( HDC hDC, bool clampToInts, int cornerX, int cornerY, SIZE *bgndSize ); + + float IXlateDistToValue( float dist, bool clampToInts ); + float IXlateDistToX( float dist, bool clampToInts ); + float IXlateXToDist( float howFar ); + void IXlateValuesToClientPt( float x, float y, POINT *pt, int cornerX, int cornerY, SIZE *bgndSize ); + void IMapMouseToValues( int x, int y, bool mapToStart ); + + void ISendDraggedMessage( bool itWasTheStartPoint ); + + static HINSTANCE fInstance; + static int fClassRefCnt; + static HBITMAP fBgndImage; + static HFONT fFont; + +#ifdef MCN_TWO_GRAPH_MODE + static HBITMAP fBgndImage2; + static bool fXAsMipmapLevel; +#endif + + static void IRegisterCtrl( HINSTANCE instance ); + static void IUnregisterCtrl( void ); + + static LRESULT CALLBACK IWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); + + + public: + + plDetailCurveCtrl( HWND parentWnd, WPARAM id, RECT *clientRect, HINSTANCE instance = NULL ); + ~plDetailCurveCtrl(); + + void SetStartPoint( float percentLevel, float opacity ); + void SetEndPoint( float percentLevel, float opacity ); + void SetNumLevels( int numLevels ); + + void GetStartPoint( float &percent, float &opacity ) { percent = fStartPercent; opacity = fStartOpac; } + void GetEndPoint( float &percent, float &opacity ) { percent = fEndPercent; opacity = fEndOpac; } + +}; + +#endif // _plDetailCurveCtrl_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDrawCurve.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDrawCurve.cpp new file mode 100644 index 00000000..dc2fa3c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plDrawCurve.cpp @@ -0,0 +1,83 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#if 0 + int ybot, ytop, ylast, i, iy; + HPEN linePen = (HPEN)GetStockObject(WHITE_PEN); + HPEN fgPen = CreatePen(PS_SOLID,0,GetSysColor(COLOR_BTNFACE)); + HPEN bgPen = CreatePen(PS_SOLID,0,GetSysColor(COLOR_BTNSHADOW)); + int width = rect.w() - 4; + int height = rect.h() - 4; + int miplevel = 0; + const float depth = 9; + float detailDropoffStart = theHsMaxLayer->GetDetailDropoffStart(curTime) * depth; + float detailDropoffStop = theHsMaxLayer->GetDetailDropoffStop(curTime) * depth; + float detailMax = theHsMaxLayer->GetDetailMax(curTime); + float detailMin = theHsMaxLayer->GetDetailMin(curTime); + int nextmip = 1; + + ytop = rect.top + 2; + ybot = ytop + height; + ylast = -1; + + for (i=0; i < width; i++) { + if (i==nextmip) { + miplevel++; + nextmip *= 2; + } + + int ix = i + rect.left + 2; + + float alpha = (miplevel - detailDropoffStart) * (detailMin - detailMax) / (detailDropoffStop - detailDropoffStart) + + detailMax; + + if (alpha > detailMax) + alpha = detailMax; + + if (alpha < detailMin) + alpha = detailMin; + + iy = (int)(ybot - alpha * height); + + SelectPen(hdc, fgPen); + VertLine(hdc, ix, ybot, iy); + + if (iy-1 > ytop) { + // Fill in above curve + SelectPen(hdc,bgPen); + VertLine(hdc, ix, ytop, iy-1); + } + if (ylast>=0) { + SelectPen(hdc,linePen); + VertLine(hdc, ix-1, iy-1, ylast); + } + + ylast = iy; + } + SelectObject( hdc, linePen ); + DeleteObject(fgPen); + DeleteObject(bgPen); + WhiteRect3D(hdc, rect, 1); +#endif \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMaterialRefMsg.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMaterialRefMsg.h new file mode 100644 index 00000000..240913b5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMaterialRefMsg.h @@ -0,0 +1,32 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// +// This message is used to notify the SceneWatcher of changes in the materials. +// There is a refmsg sent out when material parameters change, but for some +// reason it is not propogated to the node that the material is applied to. +// User refmsgs always make it through though. +// +#define REFMSG_USER_MAT REFMSG_USER+1 \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMtlImport.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMtlImport.cpp new file mode 100644 index 00000000..5dc7cd53 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMtlImport.cpp @@ -0,0 +1,70 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plMtlImport.h" + +extern ClassDesc2* GetPassMtlDesc(); +extern ClassDesc2* GetLayerTexDesc(); +extern ClassDesc2* GetStaticEnvLayerDesc(); +extern ClassDesc2* GetMultiMtlDesc(); +extern ClassDesc2* GetDecalMtlDesc(); +extern ClassDesc2* GetCompMtlDesc(); +extern ClassDesc2* GetParticleMtlDesc(); +extern ClassDesc2* GetDynamicEnvLayerDesc(); +extern ClassDesc2* GetBumpMtlDesc(); +extern ClassDesc2* GetDynamicTextLayerDesc(); +extern ClassDesc2* GetClothingMtlDesc(); +extern ClassDesc2* GetAngleAttenLayerDesc(); +extern ClassDesc2* GetStealthClassDesc(); +extern ClassDesc2* GetBinkClassDesc(); +extern ClassDesc2* GetMAXCameraLayerDesc(); + +int plPlasmaMtlImport::GetNumMtlDescs( void ) +{ + return 15; +} + +ClassDesc2 *plPlasmaMtlImport::GetMtlDesc( int i ) +{ + switch (i) + { + case 0: return GetPassMtlDesc(); + case 1: return GetLayerTexDesc(); + case 2: return GetMultiMtlDesc(); + case 3: return GetDecalMtlDesc(); + case 4: return GetCompMtlDesc(); + case 5: return GetStaticEnvLayerDesc(); + case 6: return GetParticleMtlDesc(); + case 7: return GetDynamicEnvLayerDesc(); + case 8: return GetBumpMtlDesc(); + case 9: return GetDynamicTextLayerDesc(); + case 10: return GetClothingMtlDesc(); + case 11: return GetAngleAttenLayerDesc(); + case 12: return GetStealthClassDesc(); + case 13: return GetBinkClassDesc(); + case 14: return GetMAXCameraLayerDesc(); + default: return 0; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMtlImport.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMtlImport.h new file mode 100644 index 00000000..ca4c3500 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/plMtlImport.h @@ -0,0 +1,38 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plMtlImport_h +#define _plMtlImport_h + +#include "Max.h" +#include "iparamb2.h" + +namespace plPlasmaMtlImport +{ + int GetNumMtlDescs( void ); + ClassDesc2 *GetMtlDesc( int i ); +}; + +#endif //_plMtlImport_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/res/detailBgnd8bit.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/res/detailBgnd8bit.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ca1bd4759a6e41ce44a3a6708f2292530f8add7c GIT binary patch literal 10678 zcmeI2QABN77{~v!B~2m{i%i!v8qL+nG-sMC)0kYDjAnG{A$lmHO%KsSbcqy6^bkG7 zinuY+Lk~$tH1v>qs3jsQsfQ9NQIbls=D+sdXLs)08qzs?Jv84r-`?wdd+qP{|JL5? zoU`{iZ~yq~H9Y$<4fuww_w;x4&yeB8d%pHopDeW2xVyWgYZfCblIXS`6(Gd<053#?$kG;J;?Ck7ddwUyOTU*%J*ueVwI@Z?Ku(Gm(<>h58 zEiGYjaS;m(3z(mu$K2c;W@l$HJw1)7sVPiOPGVwW0^{T37#kbI=;$a$Mn*6^JdB~C zAq)-6eT4kC@wBWQBe^J3ky+D zP=NgWeB|ZjK?s4|++5`3_X?V?4$bYH+E+)r?VxOARhHD6%w{+zpZ(|H0SPCCO(muFY!=rl&JTO_ESa zfTSi#w&X_1B$8}NHdl1RW}JLUz9gTU=#bjG=(VkgCbq82{s%)?`%s70Ca@U+Ct z5ik{#2WV>0)S#(BQ-h`k?m}lyYS7f6sXM^;x?F*7rR&CN~h?(X8?-~h+R z$2dPf$My9!&TcjS{O|!iJv|&<7Rx7-`718jq-%3d^Az`u?Pps&*CXp}C)mI_6Lw+y z`4(n-sKNB&oiSlraeF@0WQb>bVC`)Vx$&OK!F~dcrx&Ki#Cn6C$-@2%Y*4=W0P(xA zK!e$kJZ>BmX7#v1{#0`B!ExAqSpk=y1?lOY9^8l$L}!sWNKswxZh`rKgh`hy)# zx70NpVXNM^#w?7pE$%PI%;XLy(!&!j$x#t<58|l;| zJ%aejnD@CD9N^SV+Jwwbz%?2IZZrj3hVLU$ zHN<;|48jafLaY;pO|f}EW2 zcb!NkFCmWbxd|g5P1CzdI~Iz d^Z(pP-k|*>YvK5M)mh^{@%GcRyT^Yt`Uf%hh4cUb literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/res/dmtlbut.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/res/dmtlbut.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7806f19eef5424e3a99f078fcd6a1dfac1558d13 GIT binary patch literal 478 zcmaKnF%H5o3`JcDsUz25p_3!fyPd>b>&qOz~Hq kc_Q!0bJ@M)q%51~`%ZN4+yK~I=nYllklWk*D`fjGK5xLmQvd(} literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/res/dmtlmsk.bmp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxPlasmaMtls/res/dmtlmsk.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6ac3f24f0ba7fdbdc1d75fb13d9a17b40eea2995 GIT binary patch literal 478 zcmaiv(G9{d3`AXt|E$3$Gw{zIOu%. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" + +#include "SceneSync.h" +#include "SceneWatcher.h" + +#define MAXPLUGINCODE + +#include "../pnSceneObject/plSceneObject.h" +#include "../MaxMain/plMaxNode.h" +#include "../MaxMain/plPluginResManager.h" +#include "../MaxConvert/plConvert.h" +#include "../MaxConvert/hsMaterialConverter.h" +#include "../MaxComponent/plComponent.h" +#include "hsThread.h" +#include "hsSTLStream.h" +#include "../../../Plasma/Apps/plClient/plClientUpdateFormat.h" + +#include "plMaxFileData.h" + +SceneSync& SceneSync::Instance() +{ + static SceneSync theInstance; + return theInstance; +} + +SceneSync::SceneSync() : fUpdateSignal(nil), fSceneWatcher(nil), fTimerID(0), fUpdateFreq(-1) +{ + // Need to save the current state + RegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_RESET); + RegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_NEW); + RegisterNotification(INotify, 0, NOTIFY_FILE_PRE_OPEN); + RegisterNotification(INotify, 0, NOTIFY_PRE_EXPORT); + + // Need to load the saved state + RegisterNotification(INotify, 0, NOTIFY_FILE_POST_OPEN); + RegisterNotification(INotify, 0, NOTIFY_POST_EXPORT); + RegisterNotification(INotify, 0, NOTIFY_EXPORT_FAILED); + + // Need to save the current state and cleanup + RegisterNotification(INotify, 0, NOTIFY_SYSTEM_SHUTDOWN); +} + +void SceneSync::IShutdown() +{ + UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_RESET); + UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_PRE_NEW); + UnRegisterNotification(INotify, 0, NOTIFY_FILE_PRE_OPEN); + UnRegisterNotification(INotify, 0, NOTIFY_PRE_EXPORT); + + UnRegisterNotification(INotify, 0, NOTIFY_FILE_POST_OPEN); + UnRegisterNotification(INotify, 0, NOTIFY_POST_EXPORT); + UnRegisterNotification(INotify, 0, NOTIFY_EXPORT_FAILED); + + UnRegisterNotification(INotify, 0, NOTIFY_SYSTEM_SHUTDOWN); + + delete fUpdateSignal; + fUpdateSignal = nil; +} + +#include +#include "../MaxMain/plMaxCFGFile.h" +#include "../MaxExport/plExportErrorMsg.h" + +// TEMP +#include + +bool SceneSync::CreateClientData() +{ + char path[MAX_PATH]; + if (!GetOutputDir(path)) + return false; + + char datPath[MAX_PATH]; + sprintf(datPath, "%sdat", path); + + // Setup for the convert + plExportErrorMsg msg; + plConvertSettings settings; + settings.fSceneViewer = true; + plConvert::Instance().Init(GetCOREInterface(), &msg, &settings); + + // Do the convert + plConvert::Instance().Convert(); + + // If convert failed, fail too + if (msg.IsBogus()) + return false; + + // Clear the dirty flags since everything is fresh + IClearDirtyRecur((plMaxNode*)GetCOREInterface()->GetRootNode()); + + // + // Write the converted data out and page out the objects + // + IDeletePath(path); + CreateDirectory(path, NULL); + CreateDirectory(datPath, NULL); + + // TEMP + char oldCWD[MAX_PATH]; + getcwd(oldCWD, MAX_PATH); + chdir(path); + + hsAssert( false, "YOU NEED TO FIX ME" ); +// hsgResMgr::ResMgr()->Write(); + + // TEMP + chdir(oldCWD); + + IWriteNodeMap(path); + + // TEMP + hsMaterialConverter::Instance().FreeMaterialCache(path); + + hsAssert( false, "YOU NEED TO FIX ME" ); +// hsgResMgr::ResMgr()->PageOutConverted(); + hsgResMgr::Reset(); + + return true; +} + +bool SceneSync::IsClientRunning() +{ + return (fUpdateSignal != nil); +} + +void SceneSync::IShutdownClient() +{ + hsNamedPipeStream outStream(hsNamedPipeStream::kThrowOnError, 500); + try + { + if (outStream.Open(fPipeName, "w")) + { + // Signal the Client + fUpdateSignal->Signal(); + + if (outStream.WaitForClientConnect()) + outStream.WriteByte(ClientUpdate::kShutdown); + + outStream.Close(); + } + } + catch (hsNamedPipeStream*) + { + hsAssert(0, "Error writing to pipe"); + outStream.Close(); + } +} + +void SceneSync::SetUpdateFreq(int freq) +{ + fUpdateFreq = freq; + + // If the client is running, change it's update freq + if (IsClientRunning()) + { + // Kill the old timer + if (fTimerID != 0) + { + KillTimer(NULL, fTimerID); + fTimerID = 0; + } + + // Create a new timer + if (fUpdateFreq != -1) + fTimerID = SetTimer(NULL, 0, fUpdateFreq, ITimerProc); + } +} + +bool SceneSync::BeginClientSync(const char *semaphoreName, const char *pipeName) +{ + char path[MAX_PATH]; + if (!GetOutputDir(path)) + return false; + + char datPath[MAX_PATH]; + sprintf(datPath, "%sdat", path); + + // Load the saved rooms and their keys (but not objects) + hsAssert( false, "YOU NEED TO FIX ME" ); +// hsgResMgr::ResMgr()->ForceLoadDirectory(datPath, true/*false*/); // TEMP + + // Set the keys in the plMaxNodes. Also, delete Plasma objects for any + // plMaxNodes that can't be found (must have been deleted). + IReadNodeMap(path); + + if (!fSceneWatcher) + IStartWatching(true); + + if (fUpdateFreq != -1) + fTimerID = SetTimer(NULL, 0, fUpdateFreq, ITimerProc); + +// Update(); + + fUpdateSignal = TRACKED_NEW hsSemaphore(0, semaphoreName); + + fPipeName = pipeName; + + return true; +} + +void SceneSync::EndClientSync(bool abort) +{ + if (fTimerID != 0 || fUpdateSignal) + { + KillTimer(NULL, fTimerID); + fTimerID = 0; + + if (!abort) + { + SaveResMgr(); + IShutdownClient(); + } + else + { + // Delete files so we won't try to run with this possibly corrupted data + char path[MAX_PATH]; + if (GetOutputDir(path)) + IDeletePath(path); + } + + delete fUpdateSignal; + fUpdateSignal = nil; + + hsAssert( false, "YOU NEED TO FIX ME" ); +// hsgResMgr::ResMgr()->PageOutConverted(); +// hsgResMgr::Reset(); + } +} + +#include "../pnKeyedObject/plKey.h" + +void SceneSync::IClearDirtyRecur(plMaxNode *node) +{ + node->SetDirty(plMaxNode::kAllDirty, false); + + for (int i = 0; i < node->NumberOfChildren(); i++) + IClearDirtyRecur((plMaxNode*)node->GetChildNode(i)); +} + +#include "../plFile/hsFiles.h" + +void SceneSync::IDeletePath(const char *path) +{ + // Remove any files in the dat directory + char datPath[MAX_PATH]; + sprintf(datPath, "%sdat\\", path); + hsFolderIterator folder(datPath); + while (folder.NextFile()) + { + char file[MAX_PATH]; + folder.GetPathAndName(file); + DeleteFile(file); + } + + // Remove the dat directory +// RemoveDirectory(datPath); + + // Remove any files in the root dir + folder.SetPath(path); + while (folder.NextFile()) + { + char file[MAX_PATH]; + folder.GetPathAndName(file); + DeleteFile(file); + } +} + +bool SceneSync::SaveResMgr() +{ + // Get the output directory for the current file + char path[MAX_PATH]; + if (!GetOutputDir(path)) + return false; + + IWriteNodeMap(path); + + return true; +} + +bool SceneSync::GetOutputDir(char *buf) +{ + const char *path = plMaxConfig::GetClientPath(); + if (!path) + return false; + + const char *file = GetCOREInterface()->GetCurFileName(); + if (!file || *file == '\0') + return false; + + char filecpy[_MAX_FNAME]; + _splitpath(file, nil, nil, filecpy, nil); + + strcpy(buf, path); + strcat(buf, "SceneViewer\\"); + + // Make sure the SceneViewer directory is created (CreateDirectory sucks) +// CreateDirectory(buf, nil); + + strcat(buf, filecpy); + strcat(buf, "\\"); + + return true; +} + +bool SceneSync::IStartWatching(bool forceWatch) +{ + IStopWatching(); + + // Ref all the nodes in the scene if: + // a) we are being forced to watch (starting SceneViewer) + // b) there is previously saved data for this scene (we need to keep up to date) + if (forceWatch || CanLoadOldResMgr()) + { + fSceneWatcher = TRACKED_NEW SceneWatcher; + } + + return true; +} + +bool SceneSync::IStopWatching() +{ + if (!fSceneWatcher) + return true; + + delete fSceneWatcher; + fSceneWatcher = nil; + + return true; +} + +static const char *kKeysFile = "NodeMap.dat"; + +bool SceneSync::CanLoadOldResMgr() +{ + char path[MAX_PATH]; + if (!GetOutputDir(path)) + return false; + strcat(path, kKeysFile); + + hsUNIXStream s; + if (s.Open(path)) + { + s.Close(); + return true; + } + + return false; +} + +static void IGetNodes(std::vector& nodes, plMaxNode *curNode=nil) +{ + if (!curNode) + curNode = (plMaxNode*)GetCOREInterface()->GetRootNode(); + else + nodes.push_back(curNode); + + for (int i = 0; i < curNode->NumberOfChildren(); i++) + { + plMaxNode *childNode = (plMaxNode*)curNode->GetChildNode(i); + if (childNode) + IGetNodes(nodes, childNode); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// The NodeMap is a mapping from unique node id's to Uoid's. +// It is used to figure out the plKey associated with a particular node, which +// will be needed if the node's data needs to be deleted out of the Plasma scene. +// +bool SceneSync::IWriteNodeMap(const char *dir) +{ + char path[MAX_PATH]; + strcpy(path, dir); + strcat(path, kKeysFile); + + hsUNIXStream s; + if (!s.Open(path, "wb")) + return false; + + int numWritten = 0; + s.WriteSwap32(numWritten); + + std::vector nodes; + IGetNodes(nodes); + + int numNodes = nodes.size(); + for (int i = 0; i < numNodes; i++) + { + plMaxNode *node = nodes[i]; + + if (node->GetKey()) + { + s.WriteSwap32(node->GetHandle()); + node->GetKey()->GetUoid().Write(&s); + + numWritten++; + } + } + + s.Rewind(); + s.WriteSwap32(numWritten); + + s.Close(); + + return true; +} + +#include "../MaxMain/plMaxNodeData.h" + +bool SceneSync::IReadNodeMap(const char *dir) +{ + char path[MAX_PATH]; + strcpy(path, dir); + strcat(path, kKeysFile); + + hsUNIXStream s; + if (!s.Open(path, "rb")) + return false; + + int numWritten = s.ReadSwap32(); + + for (int i = 0; i < numWritten; i++) + { + // Read in the node handle and get the actual node + ULONG handle = s.ReadSwap32(); + plMaxNode *node = (plMaxNode*)GetCOREInterface()->GetINodeByHandle(handle); + + // Read in the Uoid and get the key + plUoid uoid; + uoid.Read(&s); + plKey key = hsgResMgr::ResMgr()->FindKey(uoid); + + // A node with that handle wasn't found, it must have been deleted. + // Delete it from the Plasma scene. + if (!node) + { + hsAssert( false, "YOU NEED TO FIX ME" ); +// hsgResMgr::ResMgr()->RemoveObject(key); + } + else + { + // Save the node's key in the node data + plMaxNodeData *dat = node->GetMaxNodeData(); + // Allocate the node data if it doesn't have any + if (!dat) + { + plMaxNodeData data; + node->SetMaxNodeData(&data); + dat = node->GetMaxNodeData(); + } + dat->SetKey(key); + dat->SetSceneObject(plSceneObject::ConvertNoRef(key->GetObjectPtr())); + node->CanConvert(); + } + } + + s.Close(); + + return true; +} + +#include "plKeyRefSort.h" + +bool SceneSync::Update() +{ + // If there are no dirty nodes, and nothing was deleted, return now + if (!fSceneWatcher || (!fSceneWatcher->AnyDirty() && !fSceneWatcher->AnyDeleted())) + return false; + + std::vector delUoids; + + // If any nodes were deleted, remove them from the ResManager + if (fSceneWatcher->AnyDeleted()) + { + SceneWatcher::KeyList& deleted = fSceneWatcher->GetDeleted(); + + for (int i = 0; i < deleted.size(); i++) + { + delUoids.push_back(deleted[i]->GetUoid()); + hsAssert( false, "YOU NEED TO FIX ME" ); +// hsgResMgr::ResMgr()->RemoveObject(deleted[i]); + } + + deleted.clear(); + } + + hsAssert( false, "YOU NEED TO FIX ME" ); +// hsgResMgr::ResMgr()->SaveNewKeys(true); + + // If any nodes are dirty, reconvert them + if (fSceneWatcher->AnyDirty()) + { + // Go through all the referenced nodes and put all the ones that need to be + // reconverted in a list + SceneWatcher::NodeSet dirtyNodes; + fSceneWatcher->GetDirty(dirtyNodes); + + // Delete the SceneObjects for all the dirty nodes, and put them in a list + // that we can send to the converter + hsTArray nodes; + for (SceneWatcher::NodeSet::iterator it = dirtyNodes.begin(); it != dirtyNodes.end(); it++) + { + // If the material is dirty, tell the material converter to release + // it's ref, so it will be recreated. + if ((*it)->GetDirty(plMaxNode::kMatDirty)) + hsMaterialConverter::Instance().ClearDoneMaterials(*it); + + plKey key = (*it)->GetKey(); + if (key) + { + delUoids.push_back(key->GetUoid()); + hsAssert( false, "YOU NEED TO FIX ME" ); +// hsgResMgr::ResMgr()->RemoveObject(key); + } + + nodes.Append(*it); + } + + // Convert + plExportErrorMsg msg; + plConvertSettings settings; + settings.fSceneViewer = true; + plConvert::Instance().Init(GetCOREInterface(), &msg, &settings); + hsBool ret = plConvert::Instance().Convert(nodes); + + // REMOVE/FIX (COLIN) + hsMaterialConverter::Instance().FreeMaterialCache(nil); + } + + // + // Sort the new keys + // + hsAssert( false, "YOU NEED TO FIX ME" ); +// const plUpdatableResManager::KeyList& keys = hsgResMgr::ResMgr()->GetNewKeys(); + std::vector newKeys;// = keys; + plKeyRefSort::Sort(&newKeys); + +#if 0 + hsStatusMessage("New Keys (Sorted):\n"); + for (int x = 0; x < newKeys.size(); x++) + { + hsStatusMessage(" "); + hsStatusMessage(newKeys[x]->GetName()); + hsStatusMessage("\n"); + } +#endif + + // + // Write out the data to the client + // + hsNamedPipeStream outStream(hsNamedPipeStream::kThrowOnError); + try + { + if (outStream.Open(fPipeName, "w")) + { + // Signal the Client + fUpdateSignal->Signal(); + + if (outStream.WaitForClientConnect()) + { + outStream.WriteByte(ClientUpdate::kUpdate); + + int i; + + // Write out the deleted Uoids + int numUoids = delUoids.size(); + outStream.WriteSwap32(numUoids); + for (i = 0; i < numUoids; i++) + { + delUoids[i].Write(&outStream); + } + + hsAssert( false, "NEED TO FIX ME!" ); +// hsgResMgr::ResMgr()->WriteChangedSpans(&outStream); + + // Write out the new keys (and objects) + int numKeys = newKeys.size(); + outStream.WriteSwap32(numKeys); + for (i = 0; i < numKeys; i++) + { + plKey key = newKeys[i]; + if (key && key->GetObjectPtr()) + hsgResMgr::ResMgr()->WriteCreatable(&outStream, key->GetObjectPtr()); + } + } + + outStream.Close(); + } + } + catch (...) + { + hsAssert(0, "Error writing to pipe"); + outStream.Close(); + + EndClientSync(true); + } + + hsAssert( false, "NEED TO FIX ME!" ); +// hsgResMgr::ResMgr()->SaveNewKeys(false); + + return true; +} + +void SceneSync::INotify(void *param, NotifyInfo *info) +{ + SceneSync &inst = SceneSync::Instance(); + + int code = info->intcode; + + // Need to save the current state + if (code == NOTIFY_SYSTEM_PRE_RESET || + code == NOTIFY_SYSTEM_PRE_NEW || + code == NOTIFY_FILE_PRE_OPEN || + code == NOTIFY_PRE_EXPORT) + { + inst.IStopWatching(); + } + // Need to load the saved state + else if (code == NOTIFY_FILE_POST_OPEN || + code == NOTIFY_POST_EXPORT || + code == NOTIFY_EXPORT_FAILED) + { + inst.IStartWatching(); + } + // Need to save the current state and cleanup + else if (code == NOTIFY_SYSTEM_SHUTDOWN) + { + inst.SaveResMgr(); + inst.IStopWatching(); + inst.IShutdown(); + } +} + +void CALLBACK SceneSync::ITimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + Instance().Update(); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneSync.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneSync.h new file mode 100644 index 00000000..f2980760 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneSync.h @@ -0,0 +1,102 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef SCENE_SYNC_H +#define SCENE_SYNC_H + +#include "Max.h" +#include "notify.h" + +#include +#include + +class plMaxNode; +class hsSemaphore; +class SceneWatcher; +class plSceneNode; + +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/plKey.h" + +class SceneSync +{ +protected: + SceneWatcher *fSceneWatcher; + hsSemaphore *fUpdateSignal; + const char *fPipeName; + int fTimerID; + int fUpdateFreq; + + SceneSync(); + +public: + static SceneSync& Instance(); + + // Get the path where the current Max file will be exported to (not including "dat") + bool GetOutputDir(char *buf); + + bool IsClientRunning(); + + // Is there valid data on disk that we can load into the ResMgr or do we need to reconvert? + bool CanLoadOldResMgr(); + + // Create client data + bool CreateClientData(); + + void SetUpdateFreq(int freq); // In milliseconds + + // Start updating the client + bool BeginClientSync(const char *semaphoreName, const char *pipeName); + // Stop updating the client. If abort is true, don't try saving, something went wrong + void EndClientSync(bool abort); + +protected: + bool SaveResMgr(); + + void IShutdownClient(); + + // Reconvert any dirty nodes to sync the Plasma database and the Max one + bool Update(); + + void AddSceneNodes(std::set& sceneNodes, std::vector& delUoids, std::vector& newKeys); + + bool IStartWatching(bool forceWatch=false); + bool IStopWatching(); + + // Called by open and close scene. + bool IReadNodeMap(const char *dir); + bool IWriteNodeMap(const char *dir); + + void IShutdown(); + + void IDeletePath(const char *path); + void IClearDirtyRecur(plMaxNode *node); + + static void INotify(void *param, NotifyInfo *info); + + static void CALLBACK ITimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); +}; + +#endif //SCENE_SYNC_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneViewer.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneViewer.cpp new file mode 100644 index 00000000..f60cf883 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneViewer.cpp @@ -0,0 +1,280 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "SceneViewer.h" +#include "SceneSync.h" + +#include "../MaxMain/plMaxCFGFile.h" + +#include "../MaxMain/resource.h" + +// For ShellExecute +#include + +static const char *kDebugClientExe = "plClient_dbg.exe"; +static const char *kReleaseClientExe = "plClient.exe"; + +static const char *kSemaphoreName = "PlasmaSceneViewer"; +static const char *kPipeName = "\\\\.\\pipe\\PlasmaSceneViewer"; + +SceneViewer::SceneViewer() : fhDlg(NULL) +{ + // Get the plugin CFG dir + const char *plugFile = plMaxConfig::GetPluginIni(); + + fUpdate = (GetPrivateProfileInt("SceneViewer", "Update", 1, plugFile) != 0); + fUpdateFreq = GetPrivateProfileInt("SceneViewer", "UpdateFreq", 500, plugFile); + fLoadOld = (GetPrivateProfileInt("SceneViewer", "LoadOld", 0, plugFile) != 0); + fReleaseExe = (GetPrivateProfileInt("SceneViewer", "ReleaseExe", 1, plugFile) != 0); +} + +SceneViewer::~SceneViewer() +{ + // Make sure the client is shut down + ISetRunning(false); + + // Get the plugin CFG dir + const char *plugFile = plMaxConfig::GetPluginIni(); + + char buf[20]; + WritePrivateProfileString("SceneViewer", "Update", fUpdate ? "1" : "0", plugFile); + WritePrivateProfileString("SceneViewer", "UpdateFreq", itoa(fUpdateFreq, buf, 10), plugFile); + WritePrivateProfileString("SceneViewer", "LoadOld", fLoadOld ? "1" : "0", plugFile); + WritePrivateProfileString("SceneViewer", "ReleaseExe", fReleaseExe ? "1" : "0", plugFile); +} + +SceneViewer &SceneViewer::Instance() +{ + static SceneViewer theInstance; + return theInstance; +} + +void SceneViewer::Show() +{ + // If the dialog is already created, make sure it is visible + if (fhDlg) + { + if (IsIconic(fhDlg)) + ShowWindow(fhDlg, SW_RESTORE); + } + else + { + fhDlg = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_SCENEVIEWER), GetCOREInterface()->GetMAXHWnd(), ForwardDlgProc); + } +} + +// Toggles the client and SceneSync +bool SceneViewer::IToggleRunning() +{ + fRunning = !fRunning; + return ISetRunning(fRunning); +} + +// Starts/Stops the client and SceneSync +bool SceneViewer::ISetRunning(bool running) +{ + if (running) + { + // The client actually is running, hmmm + if (SceneSync::Instance().IsClientRunning()) + return true; + + // If we're not loading old data, or we are but it's not there, try to create some. + if (!fLoadOld || !SceneSync::Instance().CanLoadOldResMgr()) + { + // If creation fails, fail + if (!SceneSync::Instance().CreateClientData()) + return false; + } + + char path[MAX_PATH]; + SceneSync::Instance().GetOutputDir(path); + strcat(path, "dat\\"); + + // Start the client + char *options = TRACKED_NEW char[strlen(path)+2+strlen(kSemaphoreName)+strlen(kPipeName)+6]; + sprintf(options, "-s %s %s \"%s\"", kSemaphoreName, kPipeName, path); + + int ret = (int)ShellExecute(NULL, + "open", + fReleaseExe ? kReleaseClientExe : kDebugClientExe, + options, + plMaxConfig::GetClientPath(), + SW_SHOWNORMAL); + delete [] options; + + // Client start failed + if (ret < 32) + return false; + + // Start client sync + SceneSync::Instance().SetUpdateFreq(fUpdateFreq); + SceneSync::Instance().BeginClientSync(kSemaphoreName, kPipeName); + + return true; + } + else + { + if (SceneSync::Instance().IsClientRunning()) + SceneSync::Instance().EndClientSync(false); + return true; + } +} + +BOOL SceneViewer::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return Instance().DlgProc(hDlg, msg, wParam, lParam); +} + +BOOL SceneViewer::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + fhDlg = hDlg; + + // Set the exe to use + HWND hExeCombo = GetDlgItem(hDlg, IDC_EXE); + ComboBox_AddString(hExeCombo, "Release"); + ComboBox_AddString(hExeCombo, "Debug"); + ComboBox_SetCurSel(hExeCombo, fReleaseExe ? 0 : 1); + + // Set the client path + const char *path = plMaxConfig::GetClientPath(false, true); + ICustEdit *edit = GetICustEdit(GetDlgItem(hDlg, IDC_CLIENT_PATH)); + edit->SetText((char*)path); + + // Set the "Load old data" checkbox + HWND hLoadOld = GetDlgItem(hDlg, IDC_REUSE_DATA); + Button_SetCheck(hLoadOld, fLoadOld ? BST_CHECKED : BST_UNCHECKED); + Button_Enable(hLoadOld, SceneSync::Instance().CanLoadOldResMgr()); + + // Set the update controls + float val = float(fUpdateFreq) / 1000.f; + ISpinnerControl *spin = SetupFloatSpinner(hDlg, IDC_SPINNER, IDC_EDIT, 0.1, 1.f, val); + spin->Enable(fUpdate); + CheckDlgButton(hDlg, IDC_UPDATE, fUpdate ? BST_CHECKED : BST_UNCHECKED); + + IEnableSetupControls(!SceneSync::Instance().IsClientRunning()); + } + return TRUE; + + case WM_COMMAND: + // Start/Stop SceneViewer + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_START) + { + IToggleRunning(); + IEnableSetupControls(!SceneSync::Instance().IsClientRunning()); + return TRUE; + } + // Close dialog + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL) + { + DestroyWindow(hDlg); + fhDlg = NULL; + return TRUE; + } + // Browse for directory + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_DIR) + { + const char *path = plMaxConfig::GetClientPath(true); + if (path) + { + ICustEdit *edit = GetICustEdit(GetDlgItem(hDlg, IDC_CLIENT_PATH)); + edit->SetText((char*)path); + } + + return TRUE; + } + // "Load old data" selection changed + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_REUSE_DATA) + { + fLoadOld = (Button_GetCheck((HWND)lParam) == BST_CHECKED); + return TRUE; + } + // Release/Debug exe selection changed + else if (HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_EXE) + { + int sel = ComboBox_GetCurSel((HWND)lParam); + fReleaseExe = (sel == 0); + return TRUE; + } + else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_UPDATE) + { + fUpdate = (SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED); + + ISpinnerControl *spin = GetISpinner(GetDlgItem(hDlg, IDC_SPINNER)); + spin->Enable(fUpdate); + ReleaseISpinner(spin); + + // If update was turned on, send out an update message so any dirty objects + // will be reconverted right away + if (fUpdate) + SceneSync::Instance().SetUpdateFreq(fUpdateFreq); + + return TRUE; + } + break; + + // Update frequency changed + case CC_SPINNER_CHANGE: + if (LOWORD(wParam) == IDC_SPINNER) + { + ISpinnerControl *spin = (ISpinnerControl*)lParam; + float val = spin->GetFVal(); + fUpdateFreq = int(val*1000.f); + SceneSync::Instance().SetUpdateFreq(fUpdateFreq); + } + return TRUE; + + // Type in directory + case WM_CUSTEDIT_ENTER: + if (wParam == IDC_CLIENT_PATH) + { + ICustEdit *edit = GetICustEdit((HWND)lParam); + + char path[MAX_PATH]; + edit->GetText(path, sizeof(path)); + plMaxConfig::SetClientPath(path); + } + return TRUE; + } + + return FALSE; +} + +void SceneViewer::IEnableSetupControls(bool enable) +{ + ICustEdit *edit = GetICustEdit(GetDlgItem(fhDlg, IDC_CLIENT_PATH)); + edit->Enable(enable); + + EnableWindow(GetDlgItem(fhDlg, IDC_DIR), enable); + EnableWindow(GetDlgItem(fhDlg, IDC_EXE), enable); + EnableWindow(GetDlgItem(fhDlg, IDC_REUSE_DATA), enable); + + SetWindowText(GetDlgItem(fhDlg, IDC_START), enable ? "Start" : "Stop"); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneViewer.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneViewer.h new file mode 100644 index 00000000..536816cb --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneViewer.h @@ -0,0 +1,61 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef SceneViewer_h_inc +#define SceneViewer_h_inc + +#include "hsWindows.h" + +// The dialog for setting up and starting the SceneViewer. Controls starting the client +// and SceneSync; SceneSync controls the updates though. +class SceneViewer +{ +protected: + bool fUpdate; + int fUpdateFreq; // Update rate (ms), -1 for no update + bool fLoadOld; // Load old data if possible + bool fReleaseExe; // True for release exe, false for debug + bool fRunning; // Do we think the client is currently running (not sure since the + // client can terminate without us knowing). + HWND fhDlg; // Handle to the setup dialog + + SceneViewer(); + + static BOOL CALLBACK ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); + + void IEnableSetupControls(bool enable); + + bool IToggleRunning(); + bool ISetRunning(bool running); + +public: + ~SceneViewer(); + static SceneViewer &Instance(); + + void Show(); +}; + +#endif // SceneViewer_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneWatcher.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneWatcher.cpp new file mode 100644 index 00000000..16f1cf88 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneWatcher.cpp @@ -0,0 +1,352 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" + +#include "SceneWatcher.h" + +#include "../MaxMain/plMaxNode.h" +#include "../MaxComponent/plComponent.h" +#include "../MaxMain/plPlasmaRefMsgs.h" + +#include "../pnKeyedObject/plKey.h" + +SceneWatcher::SceneWatcher() : fDirty(false) +{ + RegisterNotification(INotify, this, NOTIFY_NODE_CREATED); + IAddNodeRecur((plMaxNode*)GetCOREInterface()->GetRootNode()); +} + +SceneWatcher::~SceneWatcher() +{ + DeleteAllRefsFromMe(); + UnRegisterNotification(INotify, this, NOTIFY_NODE_CREATED); +} + +int SceneWatcher::NumRefs() +{ + return fNodes.size(); +} + +RefTargetHandle SceneWatcher::GetReference(int i) +{ + if (i < fNodes.size()) + return fNodes[i]; + + hsAssert(0, "Index out of range"); + return nil; +} + +void SceneWatcher::SetReference(int i, RefTargetHandle rtarg) +{ + if (i < fNodes.size()) + fNodes[i] = (plMaxNode*)rtarg; + else + hsAssert(0, "Index out of range"); +} + +// +// Something in the scene has changed. +// +RefResult SceneWatcher::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message) +{ + plMaxNode *node = (plMaxNode *)hTarget; + +#ifdef HS_DEBUGGING + char *tmp = node->GetName(); +#endif + + if (message == REFMSG_CHANGE) + { + // If the message is from a component, and was not generated locally (ie. + // it came from a ref parameter), ignore it. There is no way to tell if + // one of these messages is an actual change to the component or just a + // change to the referenced object. We'll catch the real changes with + // REFMSG_USER_COMP_REF_CHANGED below. + if (node->IsComponent()) + { + plComponentBase *comp = node->ConvertToComponent(); + if (!comp->IsCurMsgLocal()) + return REF_SUCCEED; + } + + // If this is a static light, ignore it + Object *obj = node->GetObjectRef(); + if (obj && obj->SuperClassID() == LIGHT_CLASS_ID && !node->GetRunTimeLight()) + return REF_SUCCEED; + + node->SetDirty(plMaxNode::kGeomDirty, true); + ISetDirty(); + } + else if (message == REFMSG_TARGET_DELETED) + { + // If the deleted node was a component, dirty everyone it was attached to + if (node->IsComponent()) + { + plComponentBase *comp = node->ConvertToComponent(); + for (UInt32 i = 0; i < comp->NumTargets(); i++) + { + if (comp->GetTarget(i)) + comp->GetTarget(i)->SetDirty(plMaxNode::kGeomDirty, true); + } + } + + fDeleted.push_back(node->GetKey()); + + IRemoveRef(node); + ISetDirty(); + } + else if (message == REFMSG_NODE_MATERIAL_CHANGED || + message == REFMSG_USER_MAT) + { + if (!node->IsComponent()) + { + node->SetDirty(plMaxNode::kMatDirty, true); + ISetDirty(); + } + } + // A node was added to the components target list + else if (message == REFMSG_USER_TARGET_ADD) + { + plMaxNode *target = (plMaxNode*)partID; + target->SetDirty(plMaxNode::kGeomDirty, true); + ISetDirty(); + } + // A node was deleted from the components target list + else if (message == REFMSG_USER_TARGET_DELETE) + { + plMaxNode *target = (plMaxNode*)partID; + target->SetDirty(plMaxNode::kGeomDirty, true); + ISetDirty(); + } + // A ref maintained by a component PB changed (not just a propagated message + // from the ref) + else if (message == REFMSG_USER_COMP_REF_CHANGED) + { + node->SetDirty(plMaxNode::kGeomDirty, true); + ISetDirty(); + } + + return REF_SUCCEED; +} + +bool SceneWatcher::AnyDeleted() +{ + return !fDeleted.empty(); +} + +SceneWatcher::KeyList& SceneWatcher::GetDeleted() +{ + return fDeleted; +} + +const SceneWatcher::NodeList& SceneWatcher::GetWatchNodes() +{ + return fNodes; +} + +bool SceneWatcher::AnyDirty() +{ + return fDirty; +} + +void SceneWatcher::GetDirty(NodeSet& dirtyNodes) +{ + int size = fNodes.size(); + for (int i = 0; i < size; i++) + { + plMaxNode *node = fNodes[i]; + +#ifdef HS_DEBUGGING + const char *tmp = node ? node->GetName() : nil; +#endif + // If any dirty flags are set, add to dirty list + if (node && node->GetDirty(plMaxNode::kAllDirty)) + IGetDependents(node, dirtyNodes); + } + + fDirty = false; +} + +void SceneWatcher::IAddRef(plMaxNode *node) +{ + // Ensure that we don't already ref this node + if (FindRef(node) != -1) + return; + + // Make a ref + int size = fNodes.size(); + fNodes.resize(size+1); + MakeRefByID(FOREVER, size, node); +} + +void SceneWatcher::IRemoveRef(plMaxNode *node) +{ + // Delete the reference if it's in our list + int i = FindRef(node); + + if (i != -1) + { + // Clear the node data, in case it is undeleted later + // (when it will be invalid) + node->ClearData(nil, nil); + node->SetDirty(plMaxNode::kAllDirty, true); + + // We can never really delete this reference because the user may "undo" + // and cause it to come back, which just sets the reference again. + fNodes[i] = nil; + } +} + +void SceneWatcher::IAddNodeRecur(plMaxNode *node) +{ + IAddRef(node); + + // If a node is dirty, make sure to set the dirty flag (since nodes may have + // been dirtied in a previous run but not reconverted yet). + if (node->GetDirty(plMaxNode::kAllDirty)) + fDirty = true; + + for (int i = 0; i < node->NumberOfChildren(); ++i) + { + plMaxNode *childNode = (plMaxNode*)node->GetChildNode(i); + IAddNodeRecur(childNode); + } +} + +void SceneWatcher::INotify(void *param, NotifyInfo *info) +{ + SceneWatcher *inst = (SceneWatcher*)param; + + int code = info->intcode; + + // New node was added to the scene, add it to our refs + if (code == NOTIFY_NODE_CREATED) + { + plMaxNode *node = (plMaxNode*)info->callParam; + + // Add a ref to the node and set it to dirty + inst->IAddRef(node); + node->SetDirty(plMaxNodeBase::kAllDirty, true); + inst->ISetDirty(); + } +} + +void SceneWatcher::ISetDirty() +{ + fDirty = true; +} + +#include "../MaxComponent/plMiscComponents.h" + +#include "../MaxExport/plExportErrorMsg.h" +#include "../MaxExport/plExportProgressBar.h" +#include "../MaxComponent/plResponderComponent.h" // Just need the CID + +void SceneWatcher::IGetLogicDependents(plMaxNode *node, NodeSet& nodes) +{ + int attached = node->NumAttachedComponents(); + for (int i = 0; i < attached; i++) + { + plComponentBase *comp = node->GetAttachedComponent(i); +/* + if (comp->ClassID() == ACTIVATOR_CID) + { + plMaxNodeBase *activatorNode = comp->GetINode(); + int numResponders = activatorNode->NumAttachedComponents(true); + for (int i = 0; i < numResponders; i++) + { + plComponentBase *responderComp = activatorNode->GetAttachedComponent(i, true); + if (responderComp->ClassID() == RESPONDER_CID) + { + for (int j = 0; j < responderComp->NumTargets(); j++) + { + plMaxNode *targ = (plMaxNode*)responderComp->GetTarget(j); + if (targ && nodes.find(targ) == nodes.end()) + { + nodes.insert(targ); + IGetLogicDependents(targ, nodes); + } + } + } + } + } + else +*/ + if (comp->ClassID() == RESPONDER_CID) + { + int activatorCnt = ResponderGetActivatorCount(comp); + for (int i = 0; i < activatorCnt; i++) + { + plComponentBase *activator = ResponderGetActivator(comp, i); + + for (int j = 0; j < activator->NumTargets(); j++) + { + plMaxNode *targ = (plMaxNode*)activator->GetTarget(j); + if (targ && nodes.find(targ) == nodes.end()) + { + nodes.insert(targ); + IGetLogicDependents(targ, nodes); + } + } + } + } + } +} + +void SceneWatcher::IGetDependents(plMaxNode *node, NodeSet& nodes) +{ + NodeSet dependents; + + if (node->IsComponent()) + { + plComponentBase *comp = node->ConvertToComponent(); + + if (comp->ClassID() == ROOM_CID || comp->ClassID() == PAGEINFO_CID) + return; + + for (int i = 0; i < comp->NumTargets(); i++) + { + plMaxNode *targ = (plMaxNode*)comp->GetTarget(i); + if (targ) + { + dependents.insert(targ); + IGetLogicDependents(targ, dependents); + } + } + } + else + { + dependents.insert(node); + + IGetLogicDependents(node, dependents); + } + +// Bug in VC++? +// nodes.insert(dependents.begin(), dependents.end()); + for (NodeSet::iterator i = dependents.begin(); i != dependents.end(); i++) + nodes.insert(*i); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneWatcher.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneWatcher.h new file mode 100644 index 00000000..cbc0a011 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/SceneWatcher.h @@ -0,0 +1,93 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef SCENE_WATCHER_H +#define SCENE_WATCHER_H + +#include "Max.h" +#include "notify.h" + +#include +#include + +#include "../pnKeyedObject/plKey.h" + +class plMaxNode; + +class SceneWatcher : public ReferenceMaker +{ +public: + typedef std::vector NodeList; + typedef std::set NodeSet; + typedef std::vector KeyList; + +protected: + NodeList fNodes; + KeyList fDeleted; + + bool fDirty; + +public: + SceneWatcher(); + ~SceneWatcher(); + + //////////////////////////////////////////////////////////////////////////// + // ReferenceMaker functions + // + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, + PartID& partID, RefMessage message); + + int NumRefs(); + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + + BOOL IsRealDependency(ReferenceTarget *rtarg) { return FALSE; } + + // Get all the nodes we're watching + const NodeList& GetWatchNodes(); + + // Get all the nodes that need to be reconverted + bool AnyDirty(); + void GetDirty(NodeSet& dirtyNodes); + + bool AnyDeleted(); + KeyList& GetDeleted(); + +protected: + void IAddRef(plMaxNode *node); + void IRemoveRef(plMaxNode *node); + + void IAddNodeRecur(plMaxNode *node); + + // Helpers for GetDirtyNodes + void IGetDependents(plMaxNode *node, NodeSet& nodes); + void IGetLogicDependents(plMaxNode *node, NodeSet& nodes); + + void ISetDirty(); + + static void INotify(void *param, NotifyInfo *info); +}; + +#endif //SCENE_WATCHER_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plKeyRefSort.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plKeyRefSort.cpp new file mode 100644 index 00000000..87799060 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plKeyRefSort.cpp @@ -0,0 +1,87 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plKeyRefSort.h" +#include "../pnKeyedObject/plKeyImp.h" + +#include + +std::vector* plKeyRefSort::fKeys = nil; +std::vector plKeyRefSort::fNumRefs; + +int plKeyRefSort::CountRefsRecur(plKey key, std::vector& traversedKeys) +{ + int numRefs = 0; + + if (std::find(traversedKeys.begin(), traversedKeys.end(), key) == traversedKeys.end()) + { + traversedKeys.push_back(key); + + plKeyImp* iKey = (plKeyImp*)key; + for (int i = 0; i < iKey->GetNumRefs(); i++) + { + plKey refKey = iKey->GetRef(i); + if (std::find(fKeys->begin(), fKeys->end(), refKey) != fKeys->end()) + numRefs++; + + numRefs += CountRefsRecur(refKey, traversedKeys); + } + } + + return numRefs; +} + + +class RefComp +{ +public: + bool operator() (plKey key1, plKey key2) const + { + std::vector::iterator it1 = std::find(plKeyRefSort::fNumRefs.begin(), plKeyRefSort::fNumRefs.end(), key1); + std::vector::iterator it2 = std::find(plKeyRefSort::fNumRefs.begin(), plKeyRefSort::fNumRefs.end(), key2); + + return ((*it1).fNumRefs < (*it2).fNumRefs); + } +}; + +void plKeyRefSort::Sort(std::vector* keys) +{ + fKeys = keys; + int numKeys = keys->size(); + fNumRefs.resize(numKeys); + + int i; + for (i = 0; i < numKeys; i++) + { + plKey curKey = (*keys)[i]; + + std::vector traversedKeys; + int numRefs = CountRefsRecur(curKey, traversedKeys); + + fNumRefs[i] = KeyRefs(curKey, numRefs); + } + + std::sort(fKeys->begin(), fKeys->end(), RefComp()); +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plKeyRefSort.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plKeyRefSort.h new file mode 100644 index 00000000..2e0a80a8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plKeyRefSort.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include +#include "../pnKeyedObject/plKey.h" + + +// A really crappy sort of a list of keys from the most referenced key to the least. +// This allows you to write out and read in the keys without having to worry about +// one referencing another that isn't loaded yet (ie, in the SceneViewer) +class plKeyRefSort +{ +protected: + class KeyRefs + { + public: + plKey fKey; + int fNumRefs; + KeyRefs() : fKey(nil), fNumRefs(-1) {} + KeyRefs(plKey key, int numRefs) : fKey(key), fNumRefs(numRefs) {} + hsBool operator== (const plKey key) + { + return (fKey == key); + } + }; + + static std::vector* fKeys; + static std::vector fNumRefs; + + friend class RefComp; + + static int CountRefsRecur(plKey key, std::vector& traversedKeys); + +public: + static void Sort(std::vector* keys); +}; diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plMaxFileData.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plMaxFileData.cpp new file mode 100644 index 00000000..b77fb0f8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plMaxFileData.cpp @@ -0,0 +1,277 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "plMaxFileData.h" +#include "hsUtils.h" + +#include "max.h" +#include "notify.h" +#include "tvnode.h" + +#define PLASMA_FILE_DATA_CID Class_ID(0x255a700a, 0x285279dc) + +// MyControl is derived from StdControl, but has no controller functionality. It simply has some +// membervariables and saves these by Load and Save. +// EditTrackParams and TrackParamsType are responsible for displaying a user interface (RightClick->Properties) +// on the controler. With these functions you can avoid it, having an interface ! +// As you can see, most of these methods are stubbed. Only Load and Save are implemented +// and of course the methods, to access the membervariables. +class plMaxFileDataControl : public StdControl +{ +public: + SYSTEMTIME fCodeBuildTime; + char fBranch[128]; + + plMaxFileDataControl() + { + memset(&fCodeBuildTime, 0, sizeof(SYSTEMTIME)); + memset(&fBranch, 0, sizeof(fBranch)); + } + + // Animatable + virtual void EditTrackParams(TimeValue t, ParamDimensionBase *dim,TCHAR *pname,HWND hParent, IObjParam *ip, DWORD flags){}; + int TrackParamsType() { return TRACKPARAMS_WHOLE; } + virtual void DeleteThis() { delete this; } + + // ReferenceMaker + virtual RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID,RefMessage message) + {return REF_DONTCARE;} + + Class_ID ClassID() { return PLASMA_FILE_DATA_CID; } + SClass_ID SuperClassID() { return CTRL_FLOAT_CLASS_ID; } + void GetClassName(TSTR& s) {s = "blah";} + + // Control methods + RefTargetHandle Clone(RemapDir& remap) { return TRACKED_NEW plMaxFileDataControl(); } + void Copy(Control *from) {} + virtual BOOL IsReplaceable() { return FALSE; } + + // StdControl methods + void GetValueLocalTime(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE){} + void SetValueLocalTime(TimeValue t, void *val, int commit, GetSetMethod method) {} + void Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int type){} + void *CreateTempValue() {return NULL;} + void DeleteTempValue(void *val) {} + void ApplyValue(void *val, void *delta) {} + void MultiplyValue(void *val, float m) {} + + // MyControl methods + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); +}; + +#define MAXFILE_DATA_CHUNK 1001 +static const UInt8 kVersion = 1; + +IOResult plMaxFileDataControl::Load(ILoad *iload) +{ + ULONG nb; + IOResult res; + while (IO_OK==(res=iload->OpenChunk())) + { + if (iload->CurChunkID() == MAXFILE_DATA_CHUNK) + { + UInt8 version = 0; + res = iload->Read(&version, sizeof(UInt8), &nb); + res = iload->Read(&fCodeBuildTime, sizeof(SYSTEMTIME), &nb); + + int branchLen = 0; + iload->Read(&branchLen, sizeof(int), &nb); + iload->Read(&fBranch, branchLen, &nb); + } + + iload->CloseChunk(); + if (res != IO_OK) + return res; + } + + return IO_OK; +} + +IOResult plMaxFileDataControl::Save(ISave *isave) +{ + ULONG nb; + isave->BeginChunk(MAXFILE_DATA_CHUNK); + + isave->Write(&kVersion, sizeof(kVersion), &nb); + isave->Write(&fCodeBuildTime, sizeof(SYSTEMTIME), &nb); + + int branchLen = strlen(fBranch)+1; + isave->Write(&branchLen, sizeof(int), &nb); + isave->Write(&fBranch, branchLen, &nb); + + isave->EndChunk(); + return IO_OK; +} + +class MaxFileDataClassDesc : public ClassDesc +{ +public: + int IsPublic() { return FALSE; } + void* Create(BOOL loading) { return TRACKED_NEW plMaxFileDataControl; } + const TCHAR* ClassName() { return _T("MaxFileData"); } + SClass_ID SuperClassID() { return CTRL_FLOAT_CLASS_ID; } + Class_ID ClassID() { return PLASMA_FILE_DATA_CID; } + const TCHAR* Category() { return _T(""); } +}; +MaxFileDataClassDesc gMaxFileDataClassDesc; +ClassDesc *GetMaxFileDataDesc() { return &gMaxFileDataClassDesc; } + +// This functions searches for Trackviewnode and the Controller and creates one, if none is present. +plMaxFileDataControl *GetMaxFileData(bool& created) +{ + plMaxFileDataControl *pCtrl = NULL; + ITrackViewNode *tvNode = NULL; + ITrackViewNode *tvRoot = GetCOREInterface()->GetTrackViewRootNode(); + + int i = tvRoot->FindItem(PLASMA_FILE_DATA_CID); + if (i < 0) + { + created = true; + + tvNode = CreateITrackViewNode(); + + // This method adds the Node with the specific Title (e.g. "My Settings") + tvRoot->AddNode(tvNode, "Plasma Globals", PLASMA_FILE_DATA_CID); + pCtrl = (plMaxFileDataControl*)CreateInstance(CTRL_FLOAT_CLASS_ID, PLASMA_FILE_DATA_CID); + + TSTR s; + pCtrl->GetClassName(s); + + + // This adds the controller + tvNode->AddController(pCtrl, s, PLASMA_FILE_DATA_CID); + tvNode->HideChildren(TRUE); + } + else + { + created = false; + + tvNode = tvRoot->GetNode(i); + pCtrl = (plMaxFileDataControl*)tvNode->GetController(PLASMA_FILE_DATA_CID); + } + + return pCtrl; +} + +static SYSTEMTIME gThisCodeBuildTime; +static char gThisBranch[128]; + +static void PrintTime(SYSTEMTIME& time, char* buf) +{ + sprintf(buf, "%d/%d/%d %d:%02d %s", time.wMonth, time.wDay, time.wYear, + (time.wHour <= 12) ? time.wHour : time.wHour-12, + time.wMinute, + (time.wHour < 12 || time.wHour == 24) ? "AM" : "PM"); +} + +static void NotifyProc(void *param, NotifyInfo *info) +{ + if (info->intcode == NOTIFY_FILE_POST_OPEN) + { + bool created; + plMaxFileDataControl* data = GetMaxFileData(created); + + if (!created) + { + FILETIME fileTime, pluginTime; + SystemTimeToFileTime(&gThisCodeBuildTime, &pluginTime); + SystemTimeToFileTime(&data->fCodeBuildTime, &fileTime); + + if (CompareFileTime(&fileTime, &pluginTime) > 0) + { + if (hsMessageBox_SuppressPrompts) + return; + + char buf[1024]; + + strcpy(buf, "This file was last saved with plugins stamped:\n\n"); + + char timeBuf[128]; + PrintTime(data->fCodeBuildTime, timeBuf); + strcat(buf, timeBuf); + strcat(buf, "\n"); + strcat(buf, data->fBranch); + + strcat(buf, "\n\nThese plugins are stamped:\n\n"); + + PrintTime(gThisCodeBuildTime, timeBuf); + strcat(buf, timeBuf); + strcat(buf, "\n"); + strcat(buf, gThisBranch); + + strcat(buf, + "\n\nNew features may have been added to the newer plugins,\n" + "so saving this file could cause data to be lost."); + + MessageBox(GetCOREInterface()->GetMAXHWnd(), buf, "Plugin Warning", MB_OK | MB_ICONEXCLAMATION); + } + } + + strcpy(data->fBranch, gThisBranch); + memcpy(&data->fCodeBuildTime, &gThisCodeBuildTime, sizeof(SYSTEMTIME)); + } +} + +static void IGetString(int resID, char *destBuffer, int size) +{ + HRSRC rsrc = ::FindResource(hInstance, MAKEINTRESOURCE(resID), RT_RCDATA); + + if (rsrc != NULL) + { + HGLOBAL handle = ::LoadResource(hInstance, rsrc); + + if (handle != NULL) + { + char* str = (char*)::LockResource(handle); + strncpy(destBuffer, str, size); + UnlockResource(handle); + } + } +} + +void InitMaxFileData() +{ + memset(&gThisCodeBuildTime, 0, sizeof(SYSTEMTIME)); + + // Date + char buf[128]; + IGetString(1000, buf, sizeof(buf) - 1); + sscanf(buf, "%hu/%hu/%hu", &gThisCodeBuildTime.wMonth, &gThisCodeBuildTime.wDay, &gThisCodeBuildTime.wYear); + + // Time + IGetString(1001, buf, sizeof(buf) - 1); + sscanf(buf, "%hu:%hu", &gThisCodeBuildTime.wHour, &gThisCodeBuildTime.wMinute); + + if (strstr(buf, "PM") != nil) + { + gThisCodeBuildTime.wHour += 12; + } + + IGetString(1002, gThisBranch, sizeof(gThisBranch) - 1); + + RegisterNotification(NotifyProc, 0, NOTIFY_FILE_POST_OPEN); +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plMaxFileData.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plMaxFileData.h new file mode 100644 index 00000000..f49485f2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plMaxFileData.h @@ -0,0 +1,26 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +void InitMaxFileData(); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginApp.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginApp.cpp new file mode 100644 index 00000000..6bd2e8e5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginApp.cpp @@ -0,0 +1,51 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPluginClient.h" +#include "plPluginApp.h" +#include "../pnNetCommon/plNetApp.h" + +plClient *plPluginApp::Startup(char *pCmdLine) +{ + // Create the client + fClient = new plPluginClient; + + // disable networking always + plNetClientApp::GetInstance()->SetFlagsBit(plNetClientApp::kDisabled); + // and set local triggers + plNetClientApp::GetInstance()->SetFlagsBit(plNetClientApp::kLocalTriggers); + + return fClient; +} + +void plPluginApp::Shutdown() +{ + // Destroy the client + if (fClient) + { + fClient->Shutdown(); + fClient = nil; + } +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginApp.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginApp.h new file mode 100644 index 00000000..4e685cc5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginApp.h @@ -0,0 +1,43 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "../../Plasma/Apps/plClient/plApp.h" + +class plPluginClient; + +// +// Plugin app class. Creates and destroys the client, leaves the resmanager +// +class plPluginApp : public plApp +{ +protected: + plPluginClient *fClient; + +public: + plPluginApp() : fClient(NULL) {} + + virtual plClient *Startup(char *CmdLine); + virtual void Shutdown(); +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginClient.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginClient.cpp new file mode 100644 index 00000000..6e2decf7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginClient.cpp @@ -0,0 +1,291 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "plPluginClient.h" + +#include "../../../Plasma/PubUtilLib/plPipeline/hsG3DDeviceSelector.h" + +#include "hsThread.h" +#include "hsSTLStream.h" + +#include "../pnKeyedObject/plUoid.h" +#include "../plResMgr/plUpdatableResManager.h" +#include "../plStatusLog/plStatusLog.h" + +// Needed for IEnableProxies +#include "plPipeline.h" +#include "../plDrawable/plDrawableSpans.h" +#include "../pnMessage/plProxyDrawMsg.h" +#include "plgDispatch.h" + +#define LOG_SCENEVIWER + +static plUpdatableResManager *GetResMgr() +{ + return (plUpdatableResManager*)hsgResMgr::ResMgr(); +} + +plUpdatableClient::plUpdatableClient() : fPipeName(nil), fUpdateSignal(nil), fDataPath(nil), fActive(true), fDirty(false) +{ +#ifdef LOG_SCENEVIWER + fLog = plStatusLogMgr::GetInstance().CreateStatusLog(25, "SceneViewer", + plStatusLog::kDontWriteFile | plStatusLog::kFilledBackground | plStatusLog::kAlignToTop); +#endif // LOG_SCENEVIWER +} + +plUpdatableClient::~plUpdatableClient() +{ + if (fUpdateSignal) + { + delete fUpdateSignal; + fUpdateSignal = nil; + } + + if (fLog) + { + delete fLog; + fLog = nil; + } +} + +void plUpdatableClient::InitUpdate(const char *semaphoreName, const char *pipeName, const char *dir) +{ + fUpdateSignal = TRACKED_NEW hsSemaphore(0, semaphoreName); + fPipeName = pipeName; + fDataPath = dir; +} + +hsG3DDeviceModeRecord plUpdatableClient::ILoadDevMode(const char* devModeFile) +{ + // Get the usual devmode + hsG3DDeviceModeRecord dmr = plClient::ILoadDevMode(devModeFile); + + // Override the mode with a windowed one + hsG3DDeviceMode *mode = (hsG3DDeviceMode*)dmr.GetMode(); + mode->SetColorDepth(0); + + return hsG3DDeviceModeRecord(*dmr.GetDevice(), *mode); +} + +#include "../../../Plasma/Apps/plClient/plClientUpdateFormat.h" +#include "../pnKeyedObject/plKey.h" + +void plUpdatableClient::IGetUpdate() +{ + // If the semaphore is signaled an update is ready + if (fUpdateSignal && fUpdateSignal->Wait(0)) + { + hsNamedPipeStream s; + s.Open(fPipeName, "r"); + +#ifdef LOG_SCENEVIWER + fLog->Clear(); + static int numUpdates = 0; + numUpdates++; + fLog->AddLineF(plStatusLog::kBlue, "SceneViewer Update #%d", numUpdates); +#endif // LOG_SCENEVIWER + + UInt8 type = s.ReadByte(); + + if (type == ClientUpdate::kShutdown) + { + #ifdef LOG_SCENEVIWER + fLog->AddLine("Client shutdown"); + #endif // LOG_SCENEVIWER + + PostMessage(GetWindowHandle(), WM_SYSCOMMAND, SC_CLOSE, 0); + } + else if (type == ClientUpdate::kUpdate) + { + fDirty = true; + + IEnableProxies(false); + + int i; + + // + // Delete the deleted keys + // + int numDeleted = s.ReadSwap32(); + std::vector delKeys; + delKeys.reserve(numDeleted); + + for (i = 0; i < numDeleted; i++) + { + plUoid uoid; + uoid.Read(&s); + plKey *key = hsgResMgr::ResMgr()->FindKey(uoid); + hsAssert(key, "Key to delete not found"); + if (key) + { + #ifdef LOG_SCENEVIWER + fLog->AddLineF("Remove: %s", key->GetName()); + #endif // LOG_SCENEVIWER + + GetResMgr()->RemoveObject(key, false); + delKeys.push_back(key); + } + } + + GetResMgr()->DelayLoad(true); + + // + // Read in the changed spans + // + hsStatusMessage("ReadChangedSpans\n"); + GetResMgr()->ReadChangedSpans(&s); + + // + // Read in the new keys and objects + // + int numNew = s.ReadSwap32(); + for (i = 0; i < numNew; i++) + { + plCreatable *cre = GetResMgr()->ReadCreatable(&s); + + hsKeyedObject *ko = hsKeyedObject::ConvertNoRef(cre); + + #ifdef LOG_SCENEVIWER + if (ko) + fLog->AddLineF("Read: %s", ko->GetKey()->GetName()); + else + fLog->AddLine("Read: (null)"); + #endif // LOG_SCENEVIWER + } + + GetResMgr()->DelayLoad(false); + + // Clear out any objects that were never reloaded (really deleted) + for (i = 0; i < delKeys.size(); i++) + { + plKey *key = delKeys[i]; + if (!key->ObjectIsLoaded()) + { + #ifdef LOG_SCENEVIWER + fLog->AddLineF("Key deleted: %s", key->GetName()); + #endif // LOG_SCENEVIWER + + GetResMgr()->RemoveObject(key); + } + } + + IEnableProxies(true); + } + + s.Close(); + } +} + +hsBool plUpdatableClient::Init() +{ + if (plClient::Init()) + { + GetResMgr()->ForceLoadDirectory(fDataPath, true); + // Page in the SceneViewer now that our key is ready + GetResMgr()->PageInSceneViewer(); + return true; + } + + return false; +} + +hsBool plUpdatableClient::MainLoop() +{ + IGetUpdate(); + + if (fActive) + return plClient::MainLoop(); + else + { + Sleep(100); + return true; + } +} + +#include + +hsBool plUpdatableClient::Shutdown() +{ + if (fDirty && fDataPath) + { + char oldCwd[MAX_PATH]; + getcwd(oldCwd, sizeof(oldCwd)); + + // Even bigger hack + char tempCrap[MAX_PATH]; + strcpy(tempCrap, fDataPath); + tempCrap[strlen(tempCrap)-strlen("dat\\")] = '\0'; + chdir(tempCrap); + + GetResMgr()->WriteSceneViewer(); + + chdir(oldCwd); + } + + return plClient::Shutdown(); +} + +void plUpdatableClient::IEnableProxies(bool enable) +{ + if (enable) + { + // switch back on any drawable proxies + if (fPipeline->GetDrawableTypeMask() & plDrawableSpans::kAudibleProxy) + { + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kAudible | plProxyDrawMsg::kCreate); + plgDispatch::MsgSend(msg); + } + if (fPipeline->GetDrawableTypeMask() & plDrawableSpans::kOccluderProxy) + { + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kOccluder | plProxyDrawMsg::kCreate); + plgDispatch::MsgSend(msg); + } + if (fPipeline->GetDrawableTypeMask() & plDrawableSpans::kPhysicalProxy) + { + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kPhysical | plProxyDrawMsg::kCreate); + plgDispatch::MsgSend(msg); + } + if (fPipeline->GetDrawableTypeMask() & plDrawableSpans::kLightProxy) + { + plProxyDrawMsg* msg = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kLight | plProxyDrawMsg::kCreate); + plgDispatch::MsgSend(msg); + } + } + else + { + // notify any and all drawable proxies to stop drawing... + plProxyDrawMsg* nuke = TRACKED_NEW plProxyDrawMsg(plProxyDrawMsg::kAudible + | plProxyDrawMsg::kOccluder + | plProxyDrawMsg::kPhysical + | plProxyDrawMsg::kLight + | plProxyDrawMsg::kDestroy); + plgDispatch::MsgSend(nuke); + } +} + +void plUpdatableClient::WindowActivate(bool active) +{ + fActive = active; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginClient.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginClient.h new file mode 100644 index 00000000..38faf341 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/MaxSceneViewer/plPluginClient.h @@ -0,0 +1,65 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef plUpdatableClient_h_inc +#define plUpdatableClient_h_inc + +#include "hsTypes.h" +#include "../../Plasma/Apps/plClient/plClient.h" + +class hsSemaphore; +class plStatusLog; + +class plUpdatableClient : public plClient +{ +protected: + hsSemaphore *fUpdateSignal; + const char *fPipeName; + const char *fDataPath; + + bool fActive; + bool fDirty; + + plStatusLog *fLog; + + void IEnableProxies(bool enable); + void IGetUpdate(); + +public: + plUpdatableClient(); + virtual ~plUpdatableClient(); + + virtual hsG3DDeviceModeRecord ILoadDevMode(const char* devModeFile); + + virtual hsBool Init(); + virtual hsBool MainLoop(); + virtual hsBool Shutdown(); + + void InitUpdate(const char *semaphoreName, const char *pipeName, const char *dir); + + virtual void WindowActivate(bool active); +}; + +#endif // plUpdatableClient_h_inc \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.cpp new file mode 100644 index 00000000..bb8acf45 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.cpp @@ -0,0 +1,232 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +// Migration.cpp : Defines the entry point for the application. +// +#include +#include +#include +#include +#include "resource.h" +#include "Migration.h" + +HINSTANCE gInstance; +HWND gDlg; +unsigned int gThreadID; + +int gTaskItem = -1; +bool gTasksRunning = false; + +LRESULT CALLBACK DlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow) +{ + InitCommonControls(); + + HWND hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MAINDIALOG), NULL, (DLGPROC) DlgProc); + + gInstance = hInstance; + gDlg = hWnd; + + ShowWindow(hWnd, SW_SHOW); + + // Main message loop: + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + if (!IsWindow(hWnd) || !IsDialogMessage(hWnd, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return msg.wParam; +} + + +// Mesage handler for dlg box. +LRESULT CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + LRESULT ret = FALSE; + switch (message) + { + case WM_INITDIALOG: + { + HWND hListView = GetDlgItem(hDlg,IDC_TASKLIST); + ListView_SetExtendedListViewStyleEx(hListView, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES); + LoadTasks(hListView); + LoadTasks(hDlg); + Button_SetCheck(GetDlgItem(hDlg,IDC_RADIOTEST),BST_CHECKED); + ret = TRUE; + } + break; + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDC_START: + // Start the migration Tasks + SetCursor(LoadCursor(NULL,IDC_WAIT)); + Button_Enable(GetDlgItem(hDlg,IDC_START),FALSE); + Button_Enable(GetDlgItem(hDlg,IDC_STOP),TRUE); + Button_Enable(GetDlgItem(hDlg,IDC_RADIOTEST),FALSE); + Button_Enable(GetDlgItem(hDlg,IDC_RADIOLAST),FALSE); + Button_Enable(GetDlgItem(hDlg,IDC_RADIOBRANCH),FALSE); + _beginthreadex(NULL,0,RunTasks,NULL,0,&gThreadID); + gTasksRunning = true; + ret = TRUE; + break; + case IDC_STOP: + // Stop the migration Tasks + SetCursor(LoadCursor(NULL,IDC_ARROW)); + Button_Enable(GetDlgItem(hDlg,IDC_START),TRUE); + Button_Enable(GetDlgItem(hDlg,IDC_STOP),FALSE); + Button_Enable(GetDlgItem(hDlg,IDC_RADIOTEST),TRUE); + Button_Enable(GetDlgItem(hDlg,IDC_RADIOLAST),TRUE); + Button_Enable(GetDlgItem(hDlg,IDC_RADIOBRANCH),TRUE); + ListBox_SetCurSel(GetDlgItem(hDlg,IDC_TASKLIST),gTaskItem); + gTasksRunning = false; + ret = TRUE; + break; + default: + break; + } + } + break; + case WM_NOTIFY: + { + switch ((int)wParam) + { + case IDC_TASKLIST: + { + NMHDR* hdr = (NMHDR*)lParam; + switch (hdr->code) + { + case LVN_ITEMCHANGED: + { + NMLISTVIEW* note = (NMLISTVIEW*)lParam; + if (note->iItem != -1) + { + Static_SetText(GetDlgItem(hDlg,IDC_DESCRIPTION),(*(MigrationTaskList::GetInstance()->GetList()))[note->iItem]->GetDescription()); + (*(MigrationTaskList::GetInstance()->GetList()))[note->iItem]->SetEnabled(ListView_GetCheckState(hdr->hwndFrom, note->iItem) != 0); + ret = true; + } + } + break; + } + } + break; + } + } + break; + case WM_CLOSE: + PostQuitMessage(-1); + break; + } + return ret; +} + + +MigrationTaskList::MigrationTaskList() +{ + static MigrationTask_Backup backup; + static MigrationTask_CleanUp cleanUp; + static MigrationTask_PatchBuilder patchBuilder; + static MigrationTask_DataMigration dataMigration; + static MigrationTask_InstallClient installClient; + static MigrationTask_GenerateClientManifest generateClientManifest; + static MigrationTask_DropStoredGames dropStoredGames; + static MigrationTask_InstallAges installAges; + static MigrationTask_CopyTestServers copyTestServers; + static MigrationTask_StartLiveServers startLiveServers; + + fList.push_back(&backup); + fList.push_back(&cleanUp); + fList.push_back(&patchBuilder); + fList.push_back(&dataMigration); + fList.push_back(&installClient); + fList.push_back(&generateClientManifest); + fList.push_back(&dropStoredGames); + fList.push_back(&installAges); + fList.push_back(©TestServers); + fList.push_back(&startLiveServers); +} + +MigrationTaskList* MigrationTaskList::GetInstance() +{ + static MigrationTaskList mlist; + return &mlist; +} + +void LoadTasks(HWND hListView) +{ + MigrationTaskList::TaskList* tasktlist = MigrationTaskList::GetInstance()->GetList(); + + MigrationTaskList::TaskList::iterator it = tasktlist->begin(); + int index = 0; + while (it != tasktlist->end()) + { + LVITEM item; + ZeroMemory(&item,sizeof(item)); + item.pszText = (*it)->GetName(); + item.mask = LVIF_TEXT; + item.iItem = index; + ListView_InsertItem(hListView,&item); + it++; index++; + } +} + +unsigned int __stdcall RunTasks(void* args) +{ + gTaskItem = 0; + MigrationTaskList::TaskList* tasktlist = MigrationTaskList::GetInstance()->GetList(); + + while(gTasksRunning && gTaskItem < tasktlist->size()) + { + if ((*tasktlist)[gTaskItem]->GetEnabled()) + { + if (Button_GetCheck(GetDlgItem(gDlg,IDC_RADIOTEST)) == BST_CHECKED) + (*tasktlist)[gTaskItem]->SetServer(MigrationTask::kTest); + if (Button_GetCheck(GetDlgItem(gDlg,IDC_RADIOLAST)) == BST_CHECKED) + (*tasktlist)[gTaskItem]->SetServer(MigrationTask::kLast); + if (Button_GetCheck(GetDlgItem(gDlg,IDC_RADIOBRANCH)) == BST_CHECKED) + (*tasktlist)[gTaskItem]->SetServer(MigrationTask::kBranch); + + ListBox_SetCurSel(GetDlgItem(gDlg,IDC_TASKLIST),gTaskItem); + Static_SetText(GetDlgItem(gDlg,IDC_DESCRIPTION),(*tasktlist)[gTaskItem]->GetDescription()); + gTasksRunning = (*tasktlist)[gTaskItem]->Run(gInstance,gDlg) == 0; + } + gTaskItem++; + } + + gTaskItem = -1; + SendMessage(gDlg,WM_COMMAND,MAKEWPARAM(IDC_STOP,BN_CLICKED),WPARAM(GetDlgItem(gDlg,IDC_STOP))); + + return 0; +} \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.h new file mode 100644 index 00000000..da4fe657 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.h @@ -0,0 +1,49 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef PLASMA_MIGRATION_TOOL +#define PLASMA_MIGRATION_TOOL + +#include "MigrationTask.h" +#include + +void LoadTasks(HWND hListView); +unsigned int __stdcall RunTasks(void* args); + +class MigrationTaskList +{ +public: + typedef std::vector TaskList; +private: + TaskList fList; +public: + MigrationTaskList(); + static MigrationTaskList* GetInstance(); + TaskList* GetList() { return &fList; } +}; + + + +#endif //PLASMA_MIGRATION_TOOL diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.ico b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.ico new file mode 100644 index 0000000000000000000000000000000000000000..386883523bcc032db77b69b047cbc5c15ae3b7fe GIT binary patch literal 1078 zcmeH_K@!3s3`IZHwe$$AoF2oYaWszOk{jR)NR>_(PRDWOZgV)&%Zp6VccBVa2?uQ&sh9LW;!?J w2dwKn!S@jnH5GJS10MR3&V{nkc?%F^XS#jrb=7ItXV>BJ*yg?&H~)SR4}%AClK=n! literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.rc new file mode 100644 index 00000000..7f01c471 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/Migration.rc @@ -0,0 +1,126 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS +#include "resource.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MIGRATION ICON DISCARDABLE "Migration.ICO" +IDI_SMALL ICON DISCARDABLE "SMALL.ICO" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""resource.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAINDIALOG DIALOG DISCARDABLE 0, 0, 290, 221 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Migrate the Test Servers to Live" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Start",IDC_START,207,13,50,14 + PUSHBUTTON "Stop",IDC_STOP,207,30,50,14,WS_DISABLED + LTEXT "Tasks to perform:",IDC_STATIC,19,13,93,8 + LTEXT "Description:",IDC_STATIC,190,51,45,9 + EDITTEXT IDC_DESCRIPTION,192,67,81,98,ES_MULTILINE | + ES_AUTOVSCROLL | ES_READONLY | ES_WANTRETURN + CONTROL "List1",IDC_TASKLIST,"SysListView32",LVS_LIST | + LVS_SINGLESEL | WS_BORDER | WS_TABSTOP,19,26,158,179 + GROUPBOX "From Server:",IDC_STATIC,189,168,94,39 + CONTROL "Test",IDC_RADIOTEST,"Button",BS_AUTORADIOBUTTON,199,176, + 31,13 + CONTROL "Last",IDC_RADIOLAST,"Button",BS_AUTORADIOBUTTON,241,176, + 29,13 + CONTROL "Branch",IDC_RADIOBRANCH,"Button",BS_AUTORADIOBUTTON,217, + 189,38,13 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_MAINDIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 283 + TOPMARGIN, 7 + BOTTOMMARGIN, 214 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/MigrationTask.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/MigrationTask.cpp new file mode 100644 index 00000000..7f0f545b --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/MigrationTask.cpp @@ -0,0 +1,419 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "MigrationTask.h" +#include "OptionalDialog.h" +#include "commctrl.h" +#include "resource.h" +#include "windowsx.h" +#include "shellapi.h" + +#define SHARENAME "\\\\data.dni\\Parable-Root" +#define LIVECLIENTDIR "\\dataservers\\live\\game_Clients\\drcExplorer" +#define LIVEEXPANDEDINSTALLDIR "\\dataservers\\live\\game_Install\\Expanded" + + +int MigrationTask_Backup::Run(HINSTANCE hInst, HWND hDlg) +{ + int ret = 0; + + // SSH In & Run the backup Script... + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + si.dwFlags = STARTF_USECOUNTCHARS | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_MINIMIZE; + si.lpTitle = "Server Backup Running"; + si.dwXCountChars = 80; + si.dwYCountChars = 25; + ZeroMemory( &pi, sizeof(pi) ); + ret = !CreateProcess(NULL,"ssh2 parable@build.bone.cyan.com \"backuplive.sh\"",NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + WaitForSingleObject(pi.hProcess,INFINITE); + + return ret; +} + + +int MigrationTask_CleanUp::Run(HINSTANCE hInst, HWND hDlg) +{ + int ret = 0; + + // Clean up the Test Directory + char* dataloc; + + switch (GetServer()) + { + case kTest: + dataloc = SHARENAME "\\dataservers\\test\\game_Data"; + break; + case kLast: + dataloc = SHARENAME "\\dataservers\\test-last\\game_Data"; + break; + case kBranch: + dataloc = SHARENAME "\\dataservers\\branch\\game_Data"; + break; + } + + + ShellExecute(hDlg,"open",dataloc,NULL,NULL,SW_SHOWNORMAL); + + if (MessageBox(hDlg, "Press OK to Continue...","Continue", MB_OKCANCEL) == IDCANCEL) + ret = -1; + + return ret; +} + + + +int MigrationTask_PatchBuilder::Run(HINSTANCE hInst, HWND hDlg) +{ + // Connect to the Data Servers by mapping the share + + // Run the patch builder on the Test and Live directories + + return 0; +} + + +int MigrationTask_DataMigration::Run(HINSTANCE hInst, HWND hDlg) +{ + int ret = 0; + + // SSH In; Copy the Data to the live + + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + si.dwFlags = STARTF_USECOUNTCHARS | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_MINIMIZE; + si.lpTitle = "Migrate the Data sets"; + si.dwXCountChars = 80; + si.dwYCountChars = 25; + ZeroMemory( &pi, sizeof(pi) ); + + char* migratecmd; + switch (GetServer()) + { + case kTest: + migratecmd = "ssh2 parable@build.bone.cyan.com \"migrate-test-data-live.sh\""; + break; + case kLast: + migratecmd = "ssh2 parable@build.bone.cyan.com \"migrate-test-last-data-live.sh\""; + break; + case kBranch: + migratecmd = "ssh2 parable@build.bone.cyan.com \"migrate-branch-data-live.sh\""; + break; + } + + ret = !CreateProcess(NULL,migratecmd,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + WaitForSingleObject(pi.hProcess,INFINITE); + + return ret; +} + + +int MigrationTask_InstallClient::Run(HINSTANCE hInst, HWND hDlg) +{ + int ret = 0; + + // migrate the client support files + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + si.dwFlags = STARTF_USECOUNTCHARS | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_MINIMIZE; + si.lpTitle = "Migrate Client Support"; + si.dwXCountChars = 80; + si.dwYCountChars = 25; + ZeroMemory( &pi, sizeof(pi) ); + + char* migratecmd; + switch (GetServer()) + { + case kTest: + migratecmd = "ssh2 parable@build.bone.cyan.com \"migrate-test-client-live.sh\""; + break; + case kLast: + migratecmd = "ssh2 parable@build.bone.cyan.com \"migrate-test-last-client-live.sh\""; + break; + case kBranch: + migratecmd = "ssh2 parable@build.bone.cyan.com \"migrate-branch-client-live.sh\""; + break; + } + + ret = CreateProcess(NULL,migratecmd,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + WaitForSingleObject(pi.hProcess,INFINITE); + + // Connect to the Data Servers by mapping the share + NETRESOURCE netres; + memset(&netres,0,sizeof(netres)); + netres.lpRemoteName = SHARENAME; + netres.dwDisplayType = RESOURCETYPE_DISK; + ret = (WNetAddConnection3(hDlg,&netres,NULL,NULL,CONNECT_INTERACTIVE) != NO_ERROR); + + if (ret == 0) + { + char exe[MAX_PATH]; + OPENFILENAME ofn; + SHFILEOPSTRUCT shFileOpt; + // Choose a client to copy across + memset(&exe, 0, sizeof(exe)); + memset(&ofn, 0, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFile = exe; + ofn.nMaxFile = sizeof(exe); + ofn.lpstrFilter = "Executable (*.EXE)\0*.EXE\0All (*.*)\0*.*\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = "Choose client exe to be used as drcExplorer.exe"; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + while(!GetOpenFileName(&ofn)); + + // copy the client across + memset(&shFileOpt,0,sizeof(shFileOpt)); + shFileOpt.hwnd = hDlg; + shFileOpt.wFunc = FO_COPY; + shFileOpt.pFrom = exe; + shFileOpt.pTo = SHARENAME LIVECLIENTDIR "\\drcExplorer.exe\0"; + ret = SHFileOperation(&shFileOpt); + + // Choose a client config to copy across + memset(&exe, 0, sizeof(exe)); + memset(&ofn, 0, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFile = exe; + ofn.nMaxFile = sizeof(exe); + ofn.lpstrFilter = "Executable (*.EXE)\0*.EXE\0All (*.*)\0*.*\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = "Choose client setup exe to be used as drcConfig.exe"; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + while(!GetOpenFileName(&ofn)); + + // copy the client across + memset(&shFileOpt,0,sizeof(shFileOpt)); + shFileOpt.hwnd = hDlg; + shFileOpt.wFunc = FO_COPY; + shFileOpt.pFrom = exe; + shFileOpt.pTo = SHARENAME LIVEEXPANDEDINSTALLDIR "\\drcConfig.exe\0"; + ret = SHFileOperation(&shFileOpt); + + // Choose a patcher to copy across + memset(&exe, 0, sizeof(exe)); + memset(&ofn, 0, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFile = exe; + ofn.nMaxFile = sizeof(exe); + ofn.lpstrFilter = "Executable (*.EXE)\0*.EXE\0All (*.*)\0*.*\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = "Choose patcher exe to be used as drcPatcher.exe"; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + while(!GetOpenFileName(&ofn)); + + // copy the client across + memset(&shFileOpt,0,sizeof(shFileOpt)); + shFileOpt.hwnd = hDlg; + shFileOpt.wFunc = FO_COPY; + shFileOpt.pFrom = exe; + shFileOpt.pTo = SHARENAME LIVEEXPANDEDINSTALLDIR "\\drcPatcher.exe\0"; + ret = SHFileOperation(&shFileOpt); + + // Choose a python.dll to copy across + memset(&exe, 0, sizeof(exe)); + memset(&ofn, 0, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hDlg; + ofn.lpstrFile = exe; + ofn.nMaxFile = sizeof(exe); + ofn.lpstrFilter = "Dynamic Lib (*.DLL)\0*.DLL\0All (*.*)\0*.*\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = "Choose python dll to be used as cypython21.dll"; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + while(!GetOpenFileName(&ofn)); + + // copy the client across + memset(&shFileOpt,0,sizeof(shFileOpt)); + shFileOpt.hwnd = hDlg; + shFileOpt.wFunc = FO_COPY; + shFileOpt.pFrom = exe; + shFileOpt.pTo = SHARENAME LIVEEXPANDEDINSTALLDIR "\\cypython21.dll\0"; + ret = SHFileOperation(&shFileOpt); + } + + return ret; +} + + +int MigrationTask_GenerateClientManifest::Run(HINSTANCE hInst, HWND hDlg) +{ + int ret = 0; + + // Run generate manifest on the directoy on the server + // Connect to the Data Servers by mapping the share + NETRESOURCE netres; + memset(&netres,0,sizeof(netres)); + netres.lpRemoteName = SHARENAME; + netres.dwDisplayType = RESOURCETYPE_DISK; + ret = (WNetAddConnection3(hDlg,&netres,NULL,NULL,CONNECT_INTERACTIVE) != NO_ERROR); + + if (ret == 0) + { + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + ZeroMemory( &pi, sizeof(pi) ); + ret = !CreateProcess(NULL,"plGenClientManifest.exe client=drcExplorer.exe dir=" SHARENAME LIVECLIENTDIR,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + WaitForSingleObject(pi.hProcess,INFINITE); + } + + return ret; +} + + +int MigrationTask_DropStoredGames::Run(HINSTANCE hInst, HWND hDlg) +{ + int ret = 0; + + // SSH In & Run the drop Script... + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + si.dwFlags = STARTF_USECOUNTCHARS | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_MINIMIZE; + si.lpTitle = "Live Game Reset"; + si.dwXCountChars = 80; + si.dwYCountChars = 25; + ZeroMemory( &pi, sizeof(pi) ); + ret = CreateProcess(NULL,"ssh2 parable@build.bone.cyan.com \"reset-live-games.sh\"",NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + WaitForSingleObject(pi.hProcess,INFINITE); + + return ret; +} + + +int MigrationTask_InstallAges::Run(HINSTANCE hInst, HWND hDlg) +{ + int ret = 0; + + // SSH In; Copy the Age Files from the data to the server AgeFiles folder + + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + si.dwFlags = STARTF_USECOUNTCHARS | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_MINIMIZE; + si.lpTitle = "Install Ages"; + si.dwXCountChars = 80; + si.dwYCountChars = 25; + ZeroMemory( &pi, sizeof(pi) ); + ret = !CreateProcess(NULL,"ssh2 parable@build.bone.cyan.com \"install-live-ages.sh\"",NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + WaitForSingleObject(pi.hProcess,INFINITE); + + return ret; +} + + +int MigrationTask_CopyTestServers::Run(HINSTANCE hInst, HWND hDlg) +{ + int ret = 0; + + // SSH In; Delete ~/Servers; copy ~/Servers-Test to ~/Servers + + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + si.dwFlags = STARTF_USECOUNTCHARS | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_MINIMIZE; + si.lpTitle = "Copy Test Servers"; + si.dwXCountChars = 80; + si.dwYCountChars = 25; + ZeroMemory( &pi, sizeof(pi) ); + switch (GetServer()) + { + case kTest: + ret = !CreateProcess(NULL, + "ssh2 parable@build.bone.cyan.com \"rm -frv ~/Servers; cp -rv ~/Servers-Test ~/Servers\"", + NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + break; + case kLast: + ret = !CreateProcess(NULL, + "ssh2 parable@build.bone.cyan.com \"rm -frv ~/Servers; cp -rv ~/Servers-Test-Last ~/Servers\"", + NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + break; + case kBranch: + ret = !CreateProcess(NULL, + "ssh2 parable@build.bone.cyan.com \"rm -frv ~/Servers; cp -rv ~/Servers-Branch ~/Servers\"", + NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + break; + } + WaitForSingleObject(pi.hProcess,INFINITE); + + return ret; +} + + +int MigrationTask_StartLiveServers::Run(HINSTANCE hInst, HWND hDlg) +{ + int ret = 0; + + // SSH In; Start the Live Servers + + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + si.dwFlags = STARTF_USECOUNTCHARS | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_MINIMIZE; + si.lpTitle = "Start Live Servers"; + si.dwXCountChars = 80; + si.dwYCountChars = 25; + ZeroMemory( &pi, sizeof(pi) ); + ret = !CreateProcess(NULL,"ssh2 parable@build.bone.cyan.com \"start-live-servers.sh\"",NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); + WaitForSingleObject(pi.hProcess,INFINITE); + + return ret; +} + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/MigrationTask.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/MigrationTask.h new file mode 100644 index 00000000..41863eed --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/MigrationTask.h @@ -0,0 +1,139 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef MIGRATION_TASK_H +#define MIGRATION_TASK_H + +#include + +class MigrationTask +{ +public: + enum Servers + { + kTest, + kLast, + kBranch + }; +private: + bool fEnabled; + Servers fServer; +public: + MigrationTask() : fEnabled(false) {} + + virtual char* GetName() = 0; + virtual char* GetDescription() = 0; + bool GetEnabled() const { return fEnabled; } + void SetEnabled(bool val) { fEnabled = val; } + Servers GetServer() const { return fServer; } + void SetServer(Servers server) { fServer = server; } + virtual int Run(HINSTANCE hInst, HWND hDlg) = 0; +}; + + +class MigrationTask_Backup : public MigrationTask +{ +public: + char* GetName() { return "Backup Task"; } + char* GetDescription() { return "Backing up Live Data and Live Servers."; } + int Run(HINSTANCE hInst, HWND hDlg); +}; + + +class MigrationTask_CleanUp : public MigrationTask +{ +public: + char* GetName() { return "Test Data Clean-Up"; } + char* GetDescription() { return "Clean up the test data for copying to the live server."; } + int Run(HINSTANCE hInst, HWND hDlg); +}; + + +class MigrationTask_PatchBuilder : public MigrationTask +{ +public: + char* GetName() { return "Patch Building Task"; } + char* GetDescription() { return "Building the patch set to upgrade the client's data."; } + int Run(HINSTANCE hInst, HWND hDlg); +}; + + +class MigrationTask_DataMigration : public MigrationTask +{ +public: + char* GetName() { return "Data Migration Task"; } + char* GetDescription() { return "Copying the data from the Test Server to the Live Server"; } + int Run(HINSTANCE hInst, HWND hDlg); +}; + +class MigrationTask_InstallClient : public MigrationTask +{ +public: + char* GetName() { return "Install the Client Files"; } + char* GetDescription() { return "Installs the Client files from a specified directory."; } + int Run(HINSTANCE hInst, HWND hDlg); +}; + +class MigrationTask_GenerateClientManifest : public MigrationTask +{ +public: + char* GetName() { return "Generate The Client Manifest"; } + char* GetDescription() { return "Generates the Client Manifest from the Client Files on the Server."; } + int Run(HINSTANCE hInst, HWND hDlg); +}; + +class MigrationTask_DropStoredGames : public MigrationTask +{ +public: + char* GetName() { return "Drop Live Stored Games"; } + char* GetDescription() { return "Drops the Stored Games that are on the live server."; } + int Run(HINSTANCE hInst, HWND hDlg); +}; + +class MigrationTask_InstallAges : public MigrationTask +{ +public: + char* GetName() { return "Install the Ages"; } + char* GetDescription() { return "Installs the Ages from the live data into the Lookup Server."; } + int Run(HINSTANCE hInst, HWND hDlg); +}; + +class MigrationTask_CopyTestServers : public MigrationTask +{ +public: + char* GetName() { return "Copy the Test Servers to Live"; } + char* GetDescription() { return "Copy the Test server executables to the Live server."; } + int Run(HINSTANCE hInst, HWND hDlg); +}; + +class MigrationTask_StartLiveServers : public MigrationTask +{ +public: + char* GetName() { return "Start the Live Servers"; } + char* GetDescription() { return "Starts the Live Servers."; } + int Run(HINSTANCE hInst, HWND hDlg); +}; + +#endif //MIGRATION_TASK_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/OptionalDialog.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/OptionalDialog.cpp new file mode 100644 index 00000000..019a8432 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/OptionalDialog.cpp @@ -0,0 +1,106 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "OptionalDialog.h" +#include "resource.h" +#include + +LRESULT CALLBACK OptionalDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + + +bool OptionalDialog(HINSTANCE hInstance, HWND hParent, char* desc, unsigned int timeoutsecs, bool defaultyes) +{ + HWND hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_OPTIONALDIALOG), hParent, (DLGPROC) OptionalDialogProc); + + ShowWindow(hWnd, SW_SHOW); + + if (timeoutsecs > 0) + { + // Set up timer + SetTimer(hWnd,WM_USER+1,timeoutsecs*1000,NULL); + char timeStr[20]; + Static_SetText(GetDlgItem(hWnd,IDC_TIMELEFT),itoa(timeoutsecs,timeStr,10)); + } + + Static_SetText(GetDlgItem(hWnd,IDC_DESCRIPTION),desc); + if (defaultyes) + { + SetWindowLong(GetDlgItem(hWnd,IDC_BUTTON_YES),GWL_STYLE, + GetWindowLong(GetDlgItem(hWnd,IDC_BUTTON_YES),GWL_STYLE) | BS_DEFPUSHBUTTON); + Static_SetText(GetDlgItem(hWnd,IDC_DEFAULT),"Default: Yes"); + } + else + { + SetWindowLong(GetDlgItem(hWnd,IDC_BUTTON_NO),GWL_STYLE, + GetWindowLong(GetDlgItem(hWnd,IDC_BUTTON_NO),GWL_STYLE) | BS_DEFPUSHBUTTON); + Static_SetText(GetDlgItem(hWnd,IDC_DEFAULT),"Default: No"); + } + + // Main message loop: + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + if (!IsWindow(hWnd) || !IsDialogMessage(hWnd, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + DestroyWindow(hWnd); + + return msg.wParam == -1 ? defaultyes : (bool)(msg.wParam); +} + +// Mesage handler for about box. +LRESULT CALLBACK OptionalDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + return TRUE; + + case WM_TIMER: + PostQuitMessage(-1); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_BUTTON_YES: + PostQuitMessage(1); + return TRUE; + + case IDC_BUTTON_NO: + PostQuitMessage(0); + return TRUE; + + default: + return FALSE; + } + break; + } + return FALSE; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/OptionalDialog.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/OptionalDialog.h new file mode 100644 index 00000000..5fcc9289 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/OptionalDialog.h @@ -0,0 +1,33 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef OPTIONAL_DIALOG_H +#define OPTIONAL_DIALOG_H + +#include + +bool OptionalDialog(HINSTANCE hInstance, HWND hParent, char* desc, unsigned int timeoutsecs, bool defaultyes); + +#endif //OPTIONAL_DIALOG_H diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/resource.h new file mode 100644 index 00000000..2c12a9ba --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/resource.h @@ -0,0 +1,30 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Migration.rc +// +#define IDD_MIGRATION_DIALOG 102 +#define IDI_MIGRATION 107 +#define IDI_SMALL 108 +#define IDD_MAINDIALOG 129 +#define IDD_OPTIONALDIALOG 130 +#define IDC_TASKLIST 1005 +#define IDC_START 1008 +#define IDC_STOP 1009 +#define IDC_DESCRIPTION 1010 +#define IDC_BUTTON_YES 1011 +#define IDC_BUTTON_NO 1012 +#define IDC_RADIOTEST 1018 +#define IDC_RADIOLAST 1019 +#define IDC_RADIOBRANCH 1020 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 131 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1020 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/small.ico b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/Migration/small.ico new file mode 100644 index 0000000000000000000000000000000000000000..8f94d9aa8285725af1920f17fa4ba90a7dad97fc GIT binary patch literal 318 zcmbu1u@S&92m|H2^tei$GGj6tBe4N_?3C#ONWzj2Y0z^{b=^ZcTR}S)7&>4n7JrdT ujNG@ttiTl!1hqz0y#czdCNotgVrj}ml}kwpjQ>. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" + +#include "../pnFactory/plCreator.h" + +#include "plAudible.h" +REGISTER_NONCREATABLE( plAudible ); + +#include "plDrawable.h" +REGISTER_NONCREATABLE( plDrawable ); + +#include "plPhysical.h" +REGISTER_NONCREATABLE( plPhysical ); + +#include "plgDispatch.h" +REGISTER_NONCREATABLE( plDispatchBase ); + +#include "../pnDispatch/pnDispatchCreatable.h" +#include "../pnKeyedObject/pnKeyedObjectCreatable.h" +#include "../pnMessage/pnMessageCreatable.h" +#include "../pnModifier/pnModifierCreatable.h" +#include "../pnNetCommon/pnNetCommonCreatable.h" +#include "../plNetMessage/plNetMessageCreatable.h" +#include "../pnTimer/pnTimerCreatable.h" +#include "../plVault/plVaultCreatable.h" +#include "../plNetCommon/plNetCommonCreatable.h" + +#include "../plResMgr/plResMgrCreatable.h" + +#include "../plMessage/plResMgrHelperMsg.h" +REGISTER_CREATABLE(plResMgrHelperMsg); + +#include "../plUnifiedTime/plUnifiedTime.h" +REGISTER_CREATABLE( plUnifiedTime ); diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plDatMerger.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plDatMerger.cpp new file mode 100644 index 00000000..ff09fd61 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plDatMerger.cpp @@ -0,0 +1,429 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plDatMerger - Command line utility app that takes multiple dat files +// and merges them into one +// +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStream.h" +#include "hsUtils.h" +#include "hsTimer.h" +#include "../plFile/hsFiles.h" +#include "plRawResManager.h" +#include "plRawPageAccessor.h" +#include "../plResMgr/plRegistryDiskSource.h" +#include "../plResMgr/plRegistryDiskMergedSourceData.h" +#include "../plResMgr/plRegistryHelpers.h" +#include "../plResMgr/plRegistry.h" +#include "../plResMgr/plRegistryNode.h" +#include "../plResMgr/plResMgrSettings.h" +#include "../plResMgr/plPageInfo.h" +#include "../plAgeDescription/plAgeDescription.h" +#include "../plFile/hsFiles.h" +#include "../plFile/plFileUtils.h" +#include "../pnKeyedObject/plKey.h" + +#include +#include + +//// Tiny String Filter Class //////////////////////////////////////////////// + +class plStrFilter +{ + protected: + + hsBool fWildCardSuffix, fInvert; + char *fStr; + + plStrFilter *fChapFilter, *fPageFilter; + + + hsBool IPass( const char *string ) + { + if( fWildCardSuffix ) + { + if( strnicmp( fStr, string, strlen( fStr ) ) == 0 ) + return true; + } + else + { + if( stricmp( fStr, string ) == 0 ) + return true; + } + return false; + } + + public: + plStrFilter() { fWildCardSuffix = fInvert = false; fStr = nil; fChapFilter = fPageFilter = nil; } + + plStrFilter( const char *initLine ) + { + if( initLine[ 0 ] == '-' ) + { + fInvert = true; + initLine++; + } + else if( initLine[ 0 ] == '+' ) + { + initLine++; + fInvert = false; + } + else + fInvert = false; + + fWildCardSuffix = false; + fStr = hsStrcpy( initLine ); + + fChapFilter = fPageFilter = nil; + char *comma = strchr( fStr, ',' ); + if( comma != nil ) + { + char *next = comma + 1; + *comma = 0; + comma = strchr( next, ',' ); + if( comma != nil ) + { + fPageFilter = new plStrFilter( comma + 1 ); + *comma = 0; + } + + fChapFilter = new plStrFilter( next ); + } + + if( fStr[ strlen( fStr ) - 1 ] == '*' ) + { + fWildCardSuffix = true; + fStr[ strlen( fStr ) - 1 ] = 0; + } + } + + ~plStrFilter() + { + delete [] fStr; + delete fChapFilter; + delete fPageFilter; + } + + hsBool Pass( const char *string ) + { + hsBool ret = IPass( string ); + + if( fInvert ) + ret = !ret; + + return ret; + } + + hsBool Pass( const plPageInfo &page ) + { + hsBool ret = IPass( page.GetAge() ) && + fChapFilter->IPass( page.GetChapter() ) && + fPageFilter->IPass( page.GetPage() ); + if( fInvert ) + ret = !ret; + + return ret; + } + + static hsBool Passes( const char *string, hsTArray &filters ) + { + UInt32 i; + + + for( i = 0; i < filters.GetCount(); i++ ) + { + if( !filters[ i ]->Pass( string ) ) + return false; + } + + return true; + } + + static hsBool Passes( const plPageInfo &page, hsTArray &filters ) + { + UInt32 i; + + + for( i = 0; i < filters.GetCount(); i++ ) + { + if( !filters[ i ]->Pass( page ) ) + return false; + } + + return true; + } +}; + +//// Globals ///////////////////////////////////////////////////////////////// + +plRawResManager *gResManager = nil; + +char gDatDirectory[ kFolderIterator_MaxPath ] = "."; +char gDestFileName[ kFolderIterator_MaxPath ]; + +hsTArray fPageFilters; +hsTArray *fCurrFilterList = nil; + +hsTArray fSourcePages; + +plRegistryDiskMergedSourceData *gDestMergeData = nil; + + +//// PrintHelp /////////////////////////////////////////////////////////////// + +int PrintHelp( void ) +{ + puts( "" ); + puts( "Usage:\tplDatMerger [oldDir] [newDir] [patchDir] |-na| |-np| |-fp| |-lAgeName| |-anewAgeDir|" ); + puts( "Where:" ); + puts( "\toldDir is the directory containing the old data files" ); + puts( "\tnewDir is the directory containing the new data files" ); + puts( "\tpatchDir is the directory where the patch files will go" ); + puts( "\t WARNING: This should point to a 'patches'" ); + puts( "\t subdir under 'newDir'; don't use anything else" ); + puts( "\t unless you REALLY know what you're doing." ); + puts( "\t-na is a flag that keeps the builder from updating the" ); + puts( "\t version numbers in the age files (for generating" ); + puts( "\t previous patch versions, for example." ); + puts( "\t-np is a flag that keeps the builder from actually creating the" ); + puts( "\t patch files. Usually helpful when you want to" ); + puts( "\t only update version numbers in the dat files" ); + puts( "\t and the age files. -na and -np are mutually" ); + puts( "\t exclusive." ); + puts( "\t-fp forces writing of entire objects instead of just difference" ); + puts( "\t buffers, for debugging purposes." ); + puts( "\t-l limits processing to the single age given. Don't put a space between the l." ); + puts( "\t and the age name." ); + puts( "\t-a specifies a different directory to put the modified age files in. If not" ); + puts( "\t specified, age files are overwritten in the newDir." ); + puts( "" ); + + return -1; +} + +hsBool ReadConfig( const char *filename ) +{ + hsUNIXStream config; + + + if( !config.Open( filename, "rt" ) ) + return false; + + char line[ 512 ]; + int lineNum = 1; + + while( config.ReadLn( line, sizeof( line ) ) ) + { + // Switch based on command + if( stricmp( line, "[pageFilters]" ) == 0 ) + fCurrFilterList = &fPageFilters; + else if( fCurrFilterList != nil ) + { + fCurrFilterList->Append( new plStrFilter( line ) ); + } + else + { + + char *tok = strtok( line, " \t=" ); + if( tok != nil ) + { + if( stricmp( tok, "datDir" ) == 0 ) + { + tok = strtok( nil, " \t=" ); + if( tok != nil ) + strcpy( gDatDirectory, tok ); + else + { + printf( "Parse error in init file, line %d", lineNum ); + return false; + } + } + else if( stricmp( tok, "destFile" ) == 0 ) + { + tok = strtok( nil, "\n\r" ); + if( tok == nil ) + { + printf( "Parse error in init file, line %d", lineNum ); + return false; + } + strcpy( gDestFileName, tok ); + } + else + { + printf( "Parse error in init file, line %d", lineNum ); + } + } + } + lineNum++; + } + + config.Close(); + return true; +} + +//// Our Main Page Iterator ////////////////////////////////////////////////// + +class plPageStuffer : public plRegistryPageIterator +{ + public: + + virtual hsBool EatPage( plRegistryPageNode *page ) + { + const plPageInfo &info = page->GetPageInfo(); + if( plStrFilter::Passes( info, fPageFilters ) ) + fSourcePages.Append( page ); + return true; + } +}; + +//// IShutdown /////////////////////////////////////////////////////////////// + +void IShutdown( int retCode ) +{ + UInt32 i; + + for( i = 0; i < fPageFilters.GetCount(); i++ ) + delete fPageFilters[ i ]; + + delete gDestMergeData; + + hsgResMgr::Shutdown(); + + if( retCode == 0 ) + printf( "Finished!\n" ); + else + exit( retCode ); +} + +//// main //////////////////////////////////////////////////////////////////// + +int main( int argc, char *argv[] ) +{ + puts( "-----------------------------------------------------" ); + puts( "plDatMerger - Plasma 2 dat file merging utility" ); + puts( "-----------------------------------------------------" ); + + if( argc < 1 || argc > 8 ) + return PrintHelp(); + + // Read our config + ReadConfig( argv[ 1 ] ); + + plResMgrSettings::Get().SetFilterNewerPageVersions( false ); + plResMgrSettings::Get().SetFilterOlderPageVersions( false ); + + // Init our special resMgr + puts( "Initializing resManager..." ); + gResManager = new plRawResManager; + hsgResMgr::Init( gResManager ); + + // Load the registry in to work with + printf( "Loading registry from directory \"%s\"...\n", gDatDirectory ); + gResManager->AddSource( new plRegistryDiskSource( gDatDirectory ) ); + + // Iterate and collect pages to merge + printf( "Collecting pages...\n" ); + plPageStuffer pageIter; + gResManager->IterateAllPages( &pageIter ); + + if( fSourcePages.GetCount() == 0 ) + { + puts( "ERROR: No source pages found to merge!" ); + IShutdown( -1 ); + } + + + // Create a merged data source to represent our dest page + printf( "Merging %d pages to file(s) %s...\n", fSourcePages.GetCount(), gDestFileName ); + + gDestMergeData = new plRegistryDiskMergedSourceData( gDestFileName ); + gDestMergeData->SetNumEntries( fSourcePages.GetCount() ); + + // Open the dest merged streams and write out our initial, incorrect, entry table so we can get positions right + hsStream *destIdxStream = gDestMergeData->WriteEntries( true ); + hsStream *destDatStream = gDestMergeData->OpenData( (UInt32)-1, "wb" ); + + UInt32 i, bytesRead; + static UInt8 scratchBuffer[ 1024 * 64 ]; // 32k in size + for( i = 0; i < fSourcePages.GetCount(); i++ ) + { + printf( " Merging %s>%s...\n", fSourcePages[ i ]->GetPageInfo().GetAge(), fSourcePages[ i ]->GetPageInfo().GetPage() ); + + // For each page, we open the source streams, read the ENTIRE thing in, front to back, and append it + // to the dest stream. We then update the entry in the mergeData to reflect our info + plMSDEntry &entry = gDestMergeData->GetEntry( i ); + + entry.fIdxOffset = destIdxStream->GetPosition(); + entry.fDatOffset = destDatStream->GetPosition(); + + /// Actually transfer the data + plRegistrySource *srcSource = fSourcePages[ i ]->GetSource(); + + // Idx first + hsStream *srcStream = srcSource->OpenIndexStream( fSourcePages[ i ] ); + UInt32 size = srcStream->GetEOF(); + do + { + bytesRead = srcStream->Read( size > sizeof( scratchBuffer ) ? sizeof( scratchBuffer ) : size, scratchBuffer ); + if( bytesRead > 0 ) + destIdxStream->Write( bytesRead, scratchBuffer ); + size -= bytesRead; + } while( size > 0 && bytesRead > 0 ); + srcSource->CloseIndexStream( fSourcePages[ i ] ); + + // Now dat + srcStream = srcSource->OpenDataStream( fSourcePages[ i ] ); + size = srcStream->GetEOF(); + do + { + bytesRead = srcStream->Read( size > sizeof( scratchBuffer ) ? sizeof( scratchBuffer ) : size, scratchBuffer ); + if( bytesRead > 0 ) + destDatStream->Write( bytesRead, scratchBuffer ); + size -= bytesRead; + } while( size > 0 && bytesRead > 0 ); + srcSource->CloseDataStream( fSourcePages[ i ] ); + + // Update lengths + entry.fIdxLength = destIdxStream->GetPosition() - entry.fIdxOffset; + entry.fDatLength = destDatStream->GetPosition() - entry.fDatOffset; + } + + printf( "Closing destination files...\n" ); + destIdxStream->Close(); + destDatStream->Close(); + + // Re-write the entry table, now that it's correct + printf( "Updating merged table...\n" ); + gDestMergeData->WriteEntries( false ); + + puts( "Shutting down..." ); + IShutdown( 0 ); + return 0; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawKeyedObject.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawKeyedObject.cpp new file mode 100644 index 00000000..c854031d --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawKeyedObject.cpp @@ -0,0 +1,209 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plRawPageAccessor - Dangerous little class that lets you take a +// plRegistryPageNode and load the objects in raw (i.e. +// as block memory buffers). +// This should NOT be used in any normal app, only +// utility apps that don't want to load objects in +// normally (which basically means if you're not mcn, +// don't use this!) +// +//// Why We're Bad /////////////////////////////////////////////////////////// +// +// To store all the raw buffers, we stuff them as pointers into the keys +// themselves. This is Way Bad(tm) because those pointers are expecting +// hsKeyedObjects, and what we're giving them certainly ain't those. +// This is why it's only safe to use this class in a very small, controlled +// environment, one where we know the keys won't be accessed in a normal +// fashion so we know nobody will try to use our pointers in a bad way. +// +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStream.h" +#include "hsResMgr.h" +#include "plRawKeyedObject.h" +#include "../pnKeyedObject/plKeyImp.h" + + +//// Tiny Yet Now Famous Key Hack //////////////////////////////////////////// + +class plPublicKeyImp : public plKeyImp +{ + public: + + void SetObjectPtrDirect( hsKeyedObject *obj ) + { + fObjectPtr = obj; + } + + void SetAsEmpty( void ) + { + fStartPos = (UInt32)-1; + fDataLen = (UInt32)-1; + } + + void SetStartPosFromStream( hsStream *stream ) + { + fStartPos = stream->GetPosition(); + } + + void SetLengthFromStream( hsStream *stream ) + { + fDataLen = stream->GetPosition() - fStartPos; + } +}; + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plRawKeyedObject::plRawKeyedObject() +{ + fSrcKey = nil; + fBuffer = nil; + fBufferSize = 0; +} + +plRawKeyedObject::plRawKeyedObject( const plKey &key, UInt32 size, UInt8 *data ) +{ + fSrcKey = key; + ( (plPublicKeyImp *)(plKeyImp *)key )->SetObjectPtrDirect( this ); + + fBuffer = nil; + fBufferSize = 0; + + SetBuffer( size, data ); +} + +plRawKeyedObject::~plRawKeyedObject() +{ + if( fSrcKey != nil ) + { + ( (plPublicKeyImp *)(plKeyImp *)fSrcKey )->SetObjectPtrDirect( nil ); + fSrcKey = nil; + } + + delete [] fBuffer; +} + +void plRawKeyedObject::SetBuffer( UInt32 size, UInt8 *data ) +{ + delete [] fBuffer; + + if( data == nil ) + { + fBufferSize = 0; + fBuffer = nil; + return; + } + + fBufferSize = size; + fBuffer = new UInt8[ size ]; + memcpy( fBuffer, data, size ); +} + +void plRawKeyedObject::SetKey( plKey k ) +{ + if( fSrcKey != nil ) + { + ( (plPublicKeyImp *)(plKeyImp *)fSrcKey )->SetObjectPtrDirect( nil ); + } + + fSrcKey = k; + if( fSrcKey != nil ) + { + ( (plPublicKeyImp *)(plKeyImp *)fSrcKey )->SetObjectPtrDirect( this ); + } +} + +void plRawKeyedObject::MarkAsEmpty( plKey &key ) +{ + ( (plPublicKeyImp *)(plKeyImp *)key )->SetAsEmpty(); +} + +void plRawKeyedObject::Write( hsStream *stream ) +{ + // BEFORE we write out, somewhere at the top of our buffer is the key to ourselves + // that all hsKeyedObjects write out as part of their Write() function. We need + // to REPLACE that key with our new key, since our location has now changed. Note + // that this will ONLY work if our location changes, NOT if our name changes, + // because we're relying on the fact that the written size of our key is not + // going to change!!! + { + hsWriteOnlyStream replaceStream( fBufferSize, fBuffer ); + + // Here's the part that REALLY sucks, 'cause it assumes our written format will never change!!!! + // It ALSO assumes, VERY dangerously, that ReadSwap16() will ALWAYS read a size UInt16 + + replaceStream.SetPosition( sizeof( UInt16 ) ); // Get past creatable class that resManager writes out + + hsgResMgr::ResMgr()->WriteKey( &replaceStream, fSrcKey, hsResMgr::kWriteNoCheck ); + } + + ( (plPublicKeyImp *)(plKeyImp *)fSrcKey )->SetStartPosFromStream( stream ); + + stream->Write( fBufferSize, fBuffer ); + + ( (plPublicKeyImp *)(plKeyImp *)fSrcKey )->SetLengthFromStream( stream ); +} + + +//// Warning Stubs /////////////////////////////////////////////////////////// + +void plRawKeyedObject::Validate() +{ + hsAssert( false, "Invalid call on plRawKeyedObject" ); +} + +hsBool plRawKeyedObject::IsFinal() +{ + hsAssert( false, "Invalid call on plRawKeyedObject" ); + return false; +} + +void plRawKeyedObject::Read(hsStream *s, hsResMgr *mgr ) +{ + hsAssert( false, "Invalid call on plRawKeyedObject" ); +} + +void plRawKeyedObject::Write(hsStream *s, hsResMgr *mgr ) +{ + hsAssert( false, "Invalid call on plRawKeyedObject" ); +} + +hsBool plRawKeyedObject::MsgReceive( plMessage *msg ) +{ + hsAssert( false, "Invalid call on plRawKeyedObject" ); + return false; +} + +hsKeyedObject *plRawKeyedObject::GetSharedObject() +{ + hsAssert( false, "Invalid call on plRawKeyedObject" ); + return nil; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawKeyedObject.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawKeyedObject.h new file mode 100644 index 00000000..c48f5bed --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawKeyedObject.h @@ -0,0 +1,76 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plRawKeyedObject - A fake keyed object type that really just stores itself +// as a raw data buffer. See plRawPageAccessor for details. +// +// Derived from hsKeyedObject so we can try to put some +// warning asserts in where needed. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plRawKeyedObject_h +#define _plRawKeyedObject_h + +#include "hsTypes.h" +#include "hsStream.h" +#include "../pnKeyedObject/hsKeyedObject.h" + +class plRawKeyedObject : public hsKeyedObject +{ + protected: + + plKey fSrcKey; + UInt32 fBufferSize; + UInt8 *fBuffer; + + public: + + plRawKeyedObject(); + plRawKeyedObject( const plKey &key, UInt32 size, UInt8 *buffer ); + virtual ~plRawKeyedObject(); + + void SetBuffer( UInt32 size, UInt8 *data ); + UInt32 GetBufferSize( void ) const { return fBufferSize; } + UInt8 *GetBuffer( void ) const { return fBuffer; } + + void SetKey( plKey k ); + void Write( hsStream *stream ); + + static void MarkAsEmpty( plKey &key ); + + // None of the following should ever be called (hence our asserts) + virtual void Validate(); + virtual hsBool IsFinal(); + virtual void Read(hsStream *s, hsResMgr *mgr ); + virtual void Write(hsStream *s, hsResMgr *mgr ); + virtual hsBool MsgReceive( plMessage *msg ); + virtual hsKeyedObject *GetSharedObject(); +}; + + +#endif //_plRawKeyedObject_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawPageAccessor.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawPageAccessor.cpp new file mode 100644 index 00000000..17f7e37a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawPageAccessor.cpp @@ -0,0 +1,224 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plRawPageAccessor - Dangerous little class that lets you take a +// plRegistryPageNode and load the objects in raw (i.e. +// as block memory buffers). +// This should NOT be used in any normal app, only +// utility apps that don't want to load objects in +// normally (which basically means if you're not mcn, +// don't use this!) +// +//// Why We're Bad /////////////////////////////////////////////////////////// +// +// To store all the raw buffers, we stuff them as pointers into the keys +// themselves. This is Way Bad(tm) because those pointers are expecting +// hsKeyedObjects, and what we're giving them certainly ain't those. +// This is why it's only safe to use this class in a very small, controlled +// environment, one where we know the keys won't be accessed in a normal +// fashion so we know nobody will try to use our pointers in a bad way. +// +// Also assumes the current global resManager is a plRawResManager! +// +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStream.h" +#include "plRawPageAccessor.h" +#include "plRawResManager.h" +#include "plRawKeyedObject.h" + +#include "hsTemplates.h" +#include "../pnKeyedObject/plKeyImp.h" +#include "../plResMgr/plRegistryNode.h" +#include "../plResMgr/plRegistryHelpers.h" +#include "../plResMgr/plRegistrySource.h" + + +//// Constructor/Destructor ////////////////////////////////////////////////// + +plRawPageAccessor::plRawPageAccessor( plRegistryPageNode *source, hsBool read ) +{ + fSource = source; + if( read ) + ReadFromSource(); +} + +plRawPageAccessor::~plRawPageAccessor() +{ + Release(); +} + +//// Iterators /////////////////////////////////////////////////////////////// + +class plRawReaderIter : public plRegistryKeyIterator +{ + public: + + virtual hsBool EatKey( plKey key ) + { + plRawResManager *mgr = (plRawResManager *)hsgResMgr::ResMgr(); + + UInt32 len; + plKeyImp *imp = (plKeyImp *)key; + UInt8 *buffer = mgr->ReadObjectBuffer( imp, len ); + + // This will also set the object ptr in the key + plRawKeyedObject *obj = new plRawKeyedObject( key, len, buffer ); + delete [] buffer; // rawKeyedObject keeps a copy + + return true; + } +}; + +class plRawWriterIter : public plRegistryKeyIterator +{ + hsStream *fStream; + + public: + + plRawWriterIter( hsStream *stream ) : fStream( stream ) {} + + virtual hsBool EatKey( plKey key ) + { + plRawResManager *mgr = (plRawResManager *)hsgResMgr::ResMgr(); + + plRawKeyedObject *obj = (plRawKeyedObject *)key->ObjectIsLoaded(); + if( obj == nil ) + { + // Mark the key as not written + plRawKeyedObject::MarkAsEmpty( key ); + return true; + } + + obj->Write( fStream ); + return true; + } +}; + +class plRawReleaseIter : public plRegistryKeyIterator +{ + public: + + virtual hsBool EatKey( plKey key ) + { + plRawKeyedObject *obj = (plRawKeyedObject *)key->ObjectIsLoaded(); + delete obj; + + return true; + } +}; + + +//// Various Functions /////////////////////////////////////////////////////// + +void plRawPageAccessor::ReadFromSource( void ) +{ + if( !fSource->IsLoaded() ) + fSource->LoadKeysFromSource(); + + plRawReaderIter iter; + fSource->IterateKeys( &iter ); +} + +void plRawPageAccessor::WriteToSource( void ) +{ + if( fSource->GetSource() == nil ) + { + hsAssert( false, "Unable to write accessor to disk; no source defined!" ); + return; + } + + // Write out objects first + hsStream *stream = fSource->GetSource()->OpenDataStream( fSource, true ); + if( stream == nil ) + return; + + plRawWriterIter writer( stream ); + fSource->IterateKeys( &writer ); + + fSource->GetSource()->CloseDataStream( fSource ); + + // Now write out the keys + fSource->WriteKeysToSource(); +} + +void plRawPageAccessor::Release( void ) +{ + plRawReleaseIter iter; + fSource->IterateKeys( &iter ); + + fSource->ClearKeyLists(); +} + +void plRawPageAccessor::AddCopy( const plKey &origKey ) +{ + plRawResManager *mgr = (plRawResManager *)hsgResMgr::ResMgr(); + plKey newKey; + + + // Get the source object + plRawKeyedObject *srcObj = (plRawKeyedObject *)origKey->ObjectIsLoaded(); + + // Construct a new uoid + plUoid newUoid( fSource->GetPageInfo().GetLocation(), + origKey->GetUoid().GetClassType(), + origKey->GetUoid().GetObjectName() ); + + // Does it already exist? + newKey = mgr->FindKey( newUoid ); + if( newKey != nil ) + { + // Yup, gotta get rid of old object (if there is one) + plRawKeyedObject *obj = (plRawKeyedObject *)newKey->ObjectIsLoaded(); + delete obj; + } + else + { + // Nope, gotta create key first + newKey = mgr->NewBlankKey( newUoid ); + } + + // Force the key's uoid to the right uoid, now that it's in the right page + ( (plKeyImp *)newKey )->SetUoid( origKey->GetUoid() ); + + // Assign a new buffer to the key + if( srcObj != nil ) + { + // Will set obj pointer in key + plRawKeyedObject *obj = new plRawKeyedObject( newKey, srcObj->GetBufferSize(), srcObj->GetBuffer() ); + } +} + +void plRawPageAccessor::UpdateDataVersion( plRegistryPageNode *from ) +{ + plPageInfo &orig = from->GetPageInfo(); + + fSource->GetPageInfo().SetVersion( orig.GetMajorVersion(), orig.GetMinorVersion() ); + fSource->GetPageInfo().SetReleaseVersion( orig.GetReleaseVersion() ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawPageAccessor.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawPageAccessor.h new file mode 100644 index 00000000..437432cd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawPageAccessor.h @@ -0,0 +1,69 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plRawPageAccessor - Dangerous little class that lets you take a +// plRegistryPageNode and load the objects in raw (i.e. +// as block memory buffers). +// This should NOT be used in any normal app, only +// utility apps that don't want to load objects in +// normally (which basically means if you're not mcn, +// don't use this!) +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plRawPageAccessor_h +#define _plRawPageAccessor_h + +#include "hsTypes.h" +#include "hsStream.h" + +class plRegistryPageNode; +class plKey; + +class plRawPageAccessor +{ + protected: + + plRegistryPageNode *fSource; + + public: + + plRawPageAccessor( plRegistryPageNode *source, hsBool read = true ); + virtual ~plRawPageAccessor(); + + void ReadFromSource( void ); + void WriteToSource( void ); + void Release( void ); + + void UpdateDataVersion( plRegistryPageNode *from ); + + void AddCopy( const plKey &origKey ); + +}; + + +#endif //_plRawPageAccessor_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawResManager.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawResManager.cpp new file mode 100644 index 00000000..faa11b51 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawResManager.cpp @@ -0,0 +1,116 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plRawResManager - Small public resManager thingy for reading/writing +// objects raw. +// +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "hsStream.h" +#include "plRawResManager.h" + +#include "../plResMgr/plRegistry.h" +#include "../plResMgr/plPageInfo.h" +#include "../plResMgr/plRegistrySource.h" +#include "../plResMgr/plRegistryNode.h" +#include "../plResMgr/plRegistryHelpers.h" +#include "../pnKeyedObject/plKeyImp.h" + + +plRegistryPageNode *plRawResManager::FindPage( const char *age, const char *chapter, const char *page ) +{ + return fRegistry->FindPage( age, chapter, page ); +} + +plRegistryPageNode *plRawResManager::CreatePage( const plPageInfo &info ) +{ + plRegistryPageNode *page = fRegistry->CreatePage( info.GetLocation(), info.GetAge(), info.GetChapter(), info.GetPage() ); + + if( page != nil ) + { + page->SetLoaded( true ); // We're "loaded", i.e. constructing this at runtime + fIOSources[ 0 ]->AddLocToSource( page ); + } + + return page; +} + +UInt8 *plRawResManager::ReadObjectBuffer( plKeyImp *pKey, UInt32 &retBuffLength ) +{ + UInt8 *buffer = nil; + + + hsAssert( pKey, "Null Key" ); + hsAssert( pKey->GetStartPos() != (UInt32)-1, "Missing StartPos" ); + hsAssert( pKey->GetDataLen() != (UInt32)-1, "Missing Data Length" ); + + if( pKey->GetStartPos() == (UInt32)-1 || pKey->GetDataLen() == (UInt32)-1 ) + { + // Try to recover from this by just not reading an object + retBuffLength = 0; + return nil; + } + + plRegistryDataStream *dataStream = fRegistry->OpenPageDataStream( pKey->GetUoid().GetLocation(), false ); + + if( dataStream != nil && dataStream->GetStream() != nil ) + { + hsStream *stream = dataStream->GetStream(); + + UInt32 oldPos = stream->GetPosition(); + stream->SetPosition( pKey->GetStartPos() ); + + buffer = new UInt8[ pKey->GetDataLen() ]; + if( buffer != nil ) + { + *( (UInt32 *)buffer ) = pKey->GetDataLen(); + stream->Read( pKey->GetDataLen(), (UInt8 *)buffer ); + retBuffLength = pKey->GetDataLen(); + } + else + retBuffLength = 0; + + // Restore old position now + stream->SetPosition( oldPos ); + } + delete dataStream; + + return buffer; +} + +plKey plRawResManager::NewBlankKey( const plUoid &newUoid ) +{ + plKeyImp *newKey = new plKeyImp; + + + newKey->SetUoid( newUoid ); + fRegistry->AddKey( newKey ); + + plKey keyPtr = plKey::Make( newKey ); + return keyPtr; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawResManager.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawResManager.h new file mode 100644 index 00000000..8c7cc980 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plDatMerger/plRawResManager.h @@ -0,0 +1,56 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plRawResManager - Small public resManager thingy for reading/writing +// objects raw. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plRawResManager_h +#define _plRawResManager_h + +#include "hsTypes.h" +#include "hsStream.h" +#include "../plResMgr/plResManager.h" + +class plPageInfo; +class plKeyImp; + +class plRawResManager : public plResManager +{ + public: + + plRegistryPageNode *FindPage( const char *age, const char *chapter, const char *page ); + plRegistryPageNode *CreatePage( const plPageInfo &info ); + + UInt8 *ReadObjectBuffer( plKeyImp *key, UInt32 &retLength ); + + plKey NewBlankKey( const plUoid &newUoid ); +}; + + +#endif //_plRawResManager_h diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/hsCodecManagerStub.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/hsCodecManagerStub.cpp new file mode 100644 index 00000000..b2e99560 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/hsCodecManagerStub.cpp @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsCodecManager.h" +#include "plMipmap.h" + + +hsCodecManager& hsCodecManager::Instance() +{ + static hsCodecManager the_instance; + static hsBool initialized = false; + + if (!initialized) + { + initialized = true; + } + + return the_instance; +} + +hsCodecManager::hsCodecManager() +{ +} + +plMipmap *hsCodecManager::CreateCompressedMipmap(UInt32 compressionFormat, plMipmap *uncompressed) +{ + return nil; +} + +plMipmap *hsCodecManager::CreateUncompressedMipmap(plMipmap *compressed, UInt8 bitDepth) +{ + return nil; + +} + +hsBool hsCodecManager::ColorizeCompMipmap( plMipmap *bMap, const UInt8 *colorMask ) +{ + return false; +} + +hsBool hsCodecManager::Register(hsCodec *codec, UInt32 compressionFormat, hsScalar priority) +{ + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontConverter.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontConverter.cpp new file mode 100644 index 00000000..314c22bd --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontConverter.cpp @@ -0,0 +1,75 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#define CLASSNAME "plFontConverter" // Used in WinInit() +#define WINDOWNAME "plFontConverter" + +#include "HeadSpin.h" +#include "hsTypes.h" +#include +#include "res/resource.h" + +#include "pnAllCreatables.h" +#include "../plResMgr/plResManager.h" +#include "../plResMgr/plResMgrCreatable.h" +#include "../plResMgr/plResMgrSettings.h" +#include "../plMessage/plResMgrHelperMsg.h" +#include "../plUnifiedTime/plUnifiedTimeCreatable.h" +REGISTER_CREATABLE(plResMgrHelperMsg); + +#include "../plGImage/plFont.h" +REGISTER_CREATABLE(plFont); + +#include "../plGImage/plBitmap.h" +#include "../plGImage/plMipmap.h" +REGISTER_NONCREATABLE(plBitmap); +REGISTER_CREATABLE(plMipmap); + + +HINSTANCE gInstance; +char *gCommandLine = nil; +HWND gMainWindow = nil; + +BOOL CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ); + + +int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) +{ + HACCEL accelTable = LoadAccelerators( hInstance, MAKEINTRESOURCE( IDR_ACCELERATOR1 ) ); + + + gCommandLine = (char *)lpCmdLine; + + gInstance = hInstance; + + plResManager *rMgr = new plResManager; + hsgResMgr::Init( rMgr ); + + DialogBox( gInstance, MAKEINTRESOURCE( IDD_MAINDIALOG ), nil, WndProc ); + + hsgResMgr::Shutdown(); + + return 0; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontConverterProc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontConverterProc.cpp new file mode 100644 index 00000000..d1293221 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontConverterProc.cpp @@ -0,0 +1,739 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "HeadSpin.h" +#include "hsTypes.h" +#include +#include "res/resource.h" +#include +#include +#include +#include + +#include "hsStream.h" +#include "hsResMgr.h" +#include "plFontFreeType.h" +#include "../plGImage/plFont.h" +#include "../plGImage/plMipmap.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/plKeyImp.h" + + +extern HINSTANCE gInstance; + +// My global font that i'm working on +plFont *gFont = nil; + +// Preview bitmap +HDC gPreviewHDC = nil; +HBITMAP gPreviewBitmap = nil; + +void IMakeFontGoAway( void ) +{ + if( gFont != nil ) + { + plKeyImp *imp = (plKeyImp *)(gFont->GetKey()); + if( imp != nil ) + imp->SetObjectPtr( nil ); + gFont = nil; + } +} + +void IMakeNewFont( void ) +{ + IMakeFontGoAway(); + gFont = new plFont(); +} + +BOOL CALLBACK AboutDialogProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + if( msg == WM_COMMAND ) + EndDialog( hWnd, 0 ); + return 0; +} + +bool PromptForFile( HWND parent, const char *prompt, const char *filter, char *fileName, int fileNameMax, bool save ) +{ + OPENFILENAME openInfo; + + + memset( &openInfo, 0, sizeof( OPENFILENAME ) ); + openInfo.hInstance = gInstance; + openInfo.hwndOwner = parent; + openInfo.lStructSize = sizeof( OPENFILENAME ); + openInfo.lpstrFile = fileName; + openInfo.nMaxFile = fileNameMax; + openInfo.lpstrFilter = filter; + openInfo.lpstrTitle = prompt; + openInfo.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY; + if( !save ) + openInfo.Flags |= OFN_READONLY; + + if( save ) + return GetSaveFileName( &openInfo ) ? true : false; + + return GetOpenFileName( &openInfo ) ? true : false; +} + +void IUpdateInfo( HWND hDlg ) +{ + const int TEST_STRING_SIZE = 512; + static wchar_t testString[ TEST_STRING_SIZE ] = L"The quick brown fox jumped over the lazy dog! ABCabc012345;,."; + + if( gFont == nil ) + { + SetDlgItemText( hDlg, IDC_FACE, "" ); + SetDlgItemText( hDlg, IDC_FSIZE, "" ); + SetDlgItemText( hDlg, IDC_STARTG, "" ); + SetDlgItemText( hDlg, IDC_GCOUNT, "" ); + + SetDlgItemText( hDlg, IDC_WIDTH, "" ); + SetDlgItemText( hDlg, IDC_HEIGHT, "" ); + SetDlgItemText( hDlg, IDC_BPP, "" ); + + CheckDlgButton( hDlg, IDC_BOLD, false ); + CheckDlgButton( hDlg, IDC_ITALIC, false ); + return; + } + + SetDlgItemText( hDlg, IDC_FACE, gFont->GetFace() ); + SetDlgItemInt( hDlg, IDC_FSIZE, gFont->GetSize(), false ); + SetDlgItemInt( hDlg, IDC_STARTG, gFont->GetFirstChar(), false ); + SetDlgItemInt( hDlg, IDC_GCOUNT, gFont->GetNumChars(), false ); + + SetDlgItemInt( hDlg, IDC_WIDTH, gFont->GetBitmapWidth(), false ); + SetDlgItemInt( hDlg, IDC_HEIGHT, gFont->GetBitmapHeight(), false ); + SetDlgItemInt( hDlg, IDC_BPP, gFont->GetBitmapBPP(), false ); + + CheckDlgButton( hDlg, IDC_BOLD, gFont->IsFlagSet( plFont::kFlagBold ) ); + CheckDlgButton( hDlg, IDC_ITALIC, gFont->IsFlagSet( plFont::kFlagItalic ) ); + + if( gPreviewHDC != nil ) + { + DeleteObject( gPreviewHDC ); + DeleteObject( gPreviewBitmap ); + gPreviewHDC = nil; + gPreviewBitmap = nil; + } + + // Get the size of our preview + RECT r; + GetClientRect( GetDlgItem( hDlg, IDC_PREVIEW ), &r ); + MapWindowPoints( GetDlgItem( hDlg, IDC_PREVIEW ), hDlg, (POINT *)&r, 2 ); + + InvalidateRect( hDlg, &r, false ); + + if( gFont->GetNumChars() == 0 ) + return; + + // Our preview bitmap + HDC deskDC = GetDC( nil ); + gPreviewHDC = CreateCompatibleDC( deskDC ); + gPreviewBitmap = CreateCompatibleBitmap( deskDC, r.right - r.left, r.bottom - r.top ); + SelectObject( gPreviewHDC, gPreviewBitmap ); + ReleaseDC( nil, deskDC ); + + ::GetDlgItemTextW( hDlg, IDC_PREVTEXT, testString, TEST_STRING_SIZE ); + + // Create us a mipmap to render onto, render onto it, then copy that to our DC + plMipmap *mip = new plMipmap( r.right - r.left, r.bottom - r.top, plMipmap::kARGB32Config, 1 ); + memset( mip->GetImage(), 0xff, mip->GetWidth() * mip->GetHeight() * 4 ); + + gFont->SetRenderColor( 0xff000000 ); + gFont->SetRenderFlag( plFont::kRenderClip, true ); + gFont->SetRenderClipRect( 0, 0, (Int16)(r.right - r.left), (Int16)(r.bottom - r.top) ); + UInt16 w, h, a, lastX, lastY; + UInt32 firstCC; + gFont->CalcStringExtents( testString, w, h, a, firstCC, lastX, lastY ); + + int cY = ( ( ( r.bottom - r.top ) - h ) >> 1 ) + a; + + if( cY < 0 ) + cY = 0; + else if( cY > r.bottom - r.top - 1 ) + cY = r.bottom - r.top - 1; + + memset( mip->GetAddr32( 8, cY ), 0xc0, ( r.right - r.left - 8 ) * 4 ); + + gFont->RenderString( mip, 8, cY, testString ); + + int x, y; + for( y = 0; y < r.bottom - r.top; y++ ) + { + for( x = 0; x < r.right - r.left; x++ ) + { + UInt32 color = *mip->GetAddr32( x, y ); + hsColorRGBA rgba; + rgba.FromARGB32( color ); + + if( color != 0xffffffff && color != 0xff000000 ) + { + int q = 0; + } + SetPixel( gPreviewHDC, x, y, RGB( rgba.r * 255.f, rgba.g * 255.f, rgba.b * 255.f) ); + } + } + + delete mip; +} + +class plSetKeyObj : public hsKeyedObject +{ + public: + void SetMyKey( const plKey &key ) + { + SetKey( key ); + } +}; + +class plMyBDFCallback : public plBDFConvertCallback +{ + protected: + HWND fDlg; + clock_t fLastTime; + UInt16 fPoint; + + void IPumpMessageQueue( void ) + { + MSG msg; + while( PeekMessage( &msg, fDlg, 0, 0, PM_REMOVE ) ) + { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + } + + public: + plMyBDFCallback( HWND dlg ) : fDlg( dlg ) {} + + virtual void NumChars( UInt16 chars ) + { + ::SendDlgItemMessage( fDlg, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM( 0, chars ) ); + IPumpMessageQueue(); + fLastTime = clock(); + fPoint = 0; + } + + virtual void CharDone( void ) + { + fPoint++; + if( clock() - fLastTime > CLOCKS_PER_SEC / 16 ) + { + ::SendDlgItemMessage( fDlg, IDC_PROGRESS, PBM_SETPOS, fPoint, 0 ); + IPumpMessageQueue(); + fLastTime = clock(); + } + } +}; + +BOOL CALLBACK ProgressWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + return 0; +} + +void IImportFNT( HWND hWnd, const char *path ) +{ + IMakeNewFont(); + if( !gFont->LoadFromFNT( path ) ) + MessageBox( hWnd, "Failure converting FNT file", "ERROR", MB_OK | MB_ICONEXCLAMATION ); + IUpdateInfo( hWnd ); +} + +void IImportBDF( HWND hWnd, const char *path ) +{ + IMakeNewFont(); + HWND dialog = CreateDialog( gInstance, MAKEINTRESOURCE( IDD_PROGRESS ), hWnd, ProgressWndProc ); + ShowWindow( dialog, SW_SHOW ); + EnableWindow( hWnd, false ); + plMyBDFCallback callback( dialog ); + + if( !gFont->LoadFromBDF( path, &callback ) ) + MessageBox( hWnd, "Failure converting BDF file", "ERROR", MB_OK | MB_ICONEXCLAMATION ); + + DestroyWindow( dialog ); + EnableWindow( hWnd, true ); + + IUpdateInfo( hWnd ); +} + +void IOpenP2F( HWND hWnd, const char *path ) +{ + IMakeNewFont(); + if( !gFont->LoadFromP2FFile( path ) ) + MessageBox( hWnd, "Failure opening P2F file", "ERROR", MB_OK | MB_ICONEXCLAMATION ); + + IUpdateInfo( hWnd ); +} + +struct ResRecord +{ + HRSRC fHandle; + char fName[ 512 ]; + + ResRecord() { fHandle = nil; fName[ 0 ] = 0; } + ResRecord( HRSRC h, const char *n ) { fHandle = h; strncpy( fName, n, sizeof( fName ) ); } +}; + +BOOL CALLBACK ResEnumProc( HMODULE module, LPCTSTR type, LPTSTR name, LONG_PTR lParam ) +{ + HRSRC res = FindResource( module, name, type ); + if( res != nil ) + { + hsTArray *array = (hsTArray *)lParam; + array->Append( new ResRecord( res, name ) ); + } + + return true; +} + +BOOL CALLBACK ResListWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + switch( message ) + { + case WM_INITDIALOG: + { + SendDlgItemMessage( hWnd, IDC_RESLIST, LB_RESETCONTENT, 0, 0 ); + hsTArray *array = (hsTArray *)lParam; + for( UInt32 i = 0; i < array->GetCount(); i++ ) + { + ResRecord *rec = array->Get( i ); + int idx = SendDlgItemMessage( hWnd, IDC_RESLIST, LB_ADDSTRING, 0, (LPARAM)rec->fName ); + SendDlgItemMessage( hWnd, IDC_RESLIST, LB_SETITEMDATA, idx, (LPARAM)rec ); + } + } + return 0; + + case WM_COMMAND: + if( wParam == IDCANCEL ) + EndDialog( hWnd, nil ); + else + { + int idx = SendDlgItemMessage( hWnd, IDC_RESLIST, LB_GETCURSEL, 0, 0 ); + if( idx == LB_ERR ) + EndDialog( hWnd, nil ); + else + { + ResRecord *rec = (ResRecord *)SendDlgItemMessage( hWnd, IDC_RESLIST, LB_GETITEMDATA, idx, 0 ); + EndDialog( hWnd, (int)rec ); + } + } + return true; + } + + return 0; +} + +void IImportFON( HWND hWnd, const char *path ) +{ + // FON files are really just resource modules + IMakeNewFont(); + HMODULE file = LoadLibraryEx( path, nil, LOAD_LIBRARY_AS_DATAFILE | DONT_RESOLVE_DLL_REFERENCES ); + if( file == nil ) + { + char msg[ 512 ], msg2[ 1024 ]; + + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nil, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)msg, sizeof( msg ), nil ); + + sprintf( msg2, "Failure importing FON file: can't open as resource library (%s)", msg ); + MessageBox( hWnd, msg2, "Error", MB_OK | MB_ICONEXCLAMATION ); + } + else + { + hsTArray resList; + + if( EnumResourceNames( file, "Font", ResEnumProc, (LPARAM)&resList ) ) + { + // Put up a list of the resources so the user can choose which one + ResRecord *res = (ResRecord *)DialogBoxParam( gInstance, MAKEINTRESOURCE( IDD_FONCHOOSER ), hWnd, + ResListWndProc, (LPARAM)&resList ); + if( res != nil ) + { + // Load the resource into a ram stream + hsRAMStream stream; + + HGLOBAL glob = LoadResource( file, res->fHandle ); + if( glob != nil ) + { + void *data = LockResource( glob ); + if( data != nil ) + { + stream.Write( SizeofResource( file, res->fHandle ), data ); + stream.Rewind(); + + if( !gFont->LoadFromFNTStream( &stream ) ) + MessageBox( hWnd, "Failure importing FON file: can't parse resource as FNT", + "Error", MB_OK | MB_ICONEXCLAMATION ); + + } + } + } + } + else + { + char msg[ 512 ], msg2[ 1024 ]; + + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nil, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)msg, sizeof( msg ), nil ); + + sprintf( msg2, "Failure importing FON file: can't enumerate resources (%s)", msg ); + MessageBox( hWnd, msg2, "Error", MB_OK | MB_ICONEXCLAMATION ); + } + + UInt32 i; + for( i = 0; i < resList.GetCount(); i++ ) + delete resList[ i ]; + resList.Reset(); + + FreeLibrary( file ); + } + + IUpdateInfo( hWnd ); +} + +BOOL CALLBACK FreeTypeDlgProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + static plFontFreeType::Options *info; + + switch( message ) + { + case WM_INITDIALOG: + info = (plFontFreeType::Options *)lParam; + SetDlgItemInt( hWnd, IDC_PSIZE, info->fSize, false ); + SetDlgItemInt( hWnd, IDC_RES, info->fScreenRes, false ); + SetDlgItemInt( hWnd, IDC_MAXCHAR, info->fMaxCharLimit, false ); + CheckRadioButton( hWnd, IDC_BITDEPTH, IDC_BITDEPTH2, info->fBitDepth == 1 ? IDC_BITDEPTH : IDC_BITDEPTH2 ); + return 0; + + case WM_COMMAND: + if( wParam == IDOK || wParam == IDCANCEL || wParam == IDC_BATCH ) + { + info->fSize = GetDlgItemInt( hWnd, IDC_PSIZE, nil, false ); + info->fScreenRes = GetDlgItemInt( hWnd, IDC_RES, nil, false ); + info->fMaxCharLimit = GetDlgItemInt( hWnd, IDC_MAXCHAR, nil, false ); + + if( IsDlgButtonChecked( hWnd, IDC_BITDEPTH ) ) + info->fBitDepth = 1; + else + info->fBitDepth = 8; + + EndDialog( hWnd, wParam ); + } + return 1; + } + return 0; +} + +void IBatchFreeType( HWND hWnd, const char *path ); + +void IImportFreeType( HWND hWnd, const char *path ) +{ + static plFontFreeType::Options info; + + int ret = DialogBoxParam( gInstance, MAKEINTRESOURCE( IDD_FREETYPE ), hWnd, FreeTypeDlgProc, (LPARAM)&info ); + if( ret == IDCANCEL ) + return; + else if( ret == IDC_BATCH ) + { + IBatchFreeType( hWnd, path ); + return; + } + + IMakeNewFont(); + HWND dialog = CreateDialog( gInstance, MAKEINTRESOURCE( IDD_PROGRESS ), hWnd, ProgressWndProc ); + ShowWindow( dialog, SW_SHOW ); + EnableWindow( hWnd, false ); + plMyBDFCallback callback( dialog ); + + plFontFreeType *ft2Convert = (plFontFreeType *)gFont; + if( !ft2Convert->ImportFreeType( path, &info, &callback ) ) + MessageBox( hWnd, "Failure converting TrueType file", "ERROR", MB_OK | MB_ICONEXCLAMATION ); + + DestroyWindow( dialog ); + EnableWindow( hWnd, true ); + + IUpdateInfo( hWnd ); +} + +static UInt8 sNumSizes = 0; +static UInt8 sSizeArray[ 256 ]; +static char sFontName[ 256 ]; // desired font name for FreeType conversions + +BOOL CALLBACK FreeTypeBatchDlgProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + static plFontFreeType::Options *info; + + switch( message ) + { + case WM_INITDIALOG: + info = (plFontFreeType::Options *)lParam; + SetDlgItemText( hWnd, IDC_PSIZE, "12" ); + SetDlgItemText( hWnd, IDC_FONTNAME, "Untitled" ); + SetDlgItemInt( hWnd, IDC_RES, info->fScreenRes, false ); + SetDlgItemInt( hWnd, IDC_MAXCHAR, info->fMaxCharLimit, false ); + CheckRadioButton( hWnd, IDC_BITDEPTH, IDC_BITDEPTH2, info->fBitDepth == 1 ? IDC_BITDEPTH : IDC_BITDEPTH2 ); + return 0; + + case WM_COMMAND: + if( wParam == IDOK || wParam == IDCANCEL ) + { + sNumSizes = 0; + + char *c, *lastC, str[ 1024 ]; + GetDlgItemText( hWnd, IDC_PSIZE, str, sizeof( str ) ); + lastC = str; + while( ( c = strchr( lastC, ',' ) ) != nil && sNumSizes < 255 ) + { + *c = 0; + sSizeArray[ sNumSizes++ ] = atoi( lastC ); + lastC = c + 1; + } + + sSizeArray[ sNumSizes++ ] = atoi( lastC ); + + info->fScreenRes = GetDlgItemInt( hWnd, IDC_RES, nil, false ); + info->fMaxCharLimit = GetDlgItemInt( hWnd, IDC_MAXCHAR, nil, false ); + + if( IsDlgButtonChecked( hWnd, IDC_BITDEPTH ) ) + info->fBitDepth = 1; + else + info->fBitDepth = 8; + + GetDlgItemText( hWnd, IDC_FONTNAME, sFontName, sizeof(sFontName) ); + + EndDialog( hWnd, wParam ); + } + return 1; + } + return 0; +} + +void IBatchFreeType( HWND hWnd, const char *path ) +{ + static plFontFreeType::Options info; + + if( DialogBoxParam( gInstance, MAKEINTRESOURCE( IDD_FREETYPEBATCH ), hWnd, FreeTypeBatchDlgProc, (LPARAM)&info ) == IDCANCEL ) + return; + + BROWSEINFO bInfo; + LPITEMIDLIST itemList; + LPMALLOC shMalloc; + static char destPath[ MAX_PATH ] = ""; + + memset( &bInfo, 0, sizeof( bInfo ) ); + bInfo.hwndOwner = hWnd; + bInfo.pidlRoot = NULL; + bInfo.pszDisplayName = destPath; + bInfo.lpszTitle = "Select a path to write the P2F fonts to:"; + bInfo.ulFlags = BIF_EDITBOX; + + itemList = SHBrowseForFolder( &bInfo ); + if( itemList != NULL ) + { + SHGetPathFromIDList( itemList, destPath ); + SHGetMalloc( &shMalloc ); + shMalloc->Free( itemList ); + shMalloc->Release(); + } + else + return; + + HWND dialog = CreateDialog( gInstance, MAKEINTRESOURCE( IDD_PROGRESS ), hWnd, ProgressWndProc ); + ShowWindow( dialog, SW_SHOW ); + EnableWindow( hWnd, false ); + plMyBDFCallback callback( dialog ); + + callback.NumChars( sNumSizes ); + UInt8 i; + for( i = 0; i < sNumSizes; i++ ) + { + IMakeNewFont(); + plFontFreeType *ft2Convert = (plFontFreeType *)gFont; + + info.fSize = sSizeArray[ i ]; + if( !ft2Convert->ImportFreeType( path, &info, nil ) ) + { + MessageBox( hWnd, "Failure converting TrueType file", "ERROR", MB_OK | MB_ICONEXCLAMATION ); + continue; + } + + gFont->SetFace(sFontName); + char fileName[ MAX_PATH ]; + sprintf( fileName, "%s\\%s-%d.p2f", destPath, gFont->GetFace(), gFont->GetSize() ); + hsUNIXStream stream; + if( !stream.Open( fileName, "wb" ) ) + MessageBox( hWnd, "Can't open file for writing", "Error", MB_OK | MB_ICONEXCLAMATION ); + else + { + gFont->WriteRaw( &stream ); + stream.Close(); + } + + callback.CharDone(); + } + + DestroyWindow( dialog ); + EnableWindow( hWnd, true ); + IUpdateInfo( hWnd ); +} + +BOOL CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + char fileName[ MAX_PATH ]; + PAINTSTRUCT paintInfo; + HDC myDC; + RECT r; + + + switch( message ) + { + case WM_PAINT: + myDC = BeginPaint( hWnd, &paintInfo ); + + GetClientRect( GetDlgItem( hWnd, IDC_PREVIEW ), &r ); + MapWindowPoints( GetDlgItem( hWnd, IDC_PREVIEW ), hWnd, (POINT *)&r, 2 ); + + if( gPreviewHDC != nil ) + BitBlt( myDC, r.left, r.top, r.right - r.left, r.bottom - r.top, gPreviewHDC, 0, 0, SRCCOPY ); + else + FillRect( myDC, &r, GetSysColorBrush( COLOR_3DFACE ) ); + + DrawEdge( myDC, &r, EDGE_SUNKEN, BF_RECT ); + + EndPaint( hWnd, &paintInfo ); + return 0; + + case WM_INITDIALOG: + SendMessage( hWnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon( gInstance, MAKEINTRESOURCE( IDI_APPICON ) ) ); + SetDlgItemTextW( hWnd, IDC_PREVTEXT, L"The quick brown fox jumped over the lazy dog! ABCabc012345;,." ); + return 0; + + case WM_COMMAND: + if( wParam == ID_FILE_ABOUT ) + { + DialogBox( gInstance, MAKEINTRESOURCE( IDD_ABOUT ), hWnd, AboutDialogProc ); + } + else if( wParam == ID_FILE_EXIT ) + PostQuitMessage( 0 ); + else if( wParam == ID_FILE_FNT ) + { + fileName[ 0 ] = 0; + if( PromptForFile( hWnd, "Choose a FNT file to convert", "Windows FNT files\0*.fnt\0All files\0*.*\0", fileName, sizeof( fileName ), false ) ) + IImportFNT( hWnd, fileName ); + } + else if( wParam == ID_FILE_P2F ) + { + fileName[ 0 ] = 0; + if( PromptForFile( hWnd, "Choose a P2F file to open", "Plasma 2 font files\0*.p2f\0All files\0*.*\0", fileName, sizeof( fileName ), false ) ) + IOpenP2F( hWnd, fileName ); + } + else if( wParam == ID_FILE_FON ) + { + fileName[ 0 ] = 0; + if( PromptForFile( hWnd, "Choose a FON file to convert", "Windows FON files\0*.fon\0All files\0*.*\0", fileName, sizeof( fileName ), false ) ) + IImportFON( hWnd, fileName ); + } + else if( wParam == ID_FILE_TRUETYPE ) + { + fileName[ 0 ] = 0; + if( PromptForFile( hWnd, "Choose a TrueType font to convert", "TrueType files\0*.ttf\0TrueType Collections\0*.ttc\0All files\0*.*\0", fileName, sizeof( fileName ), false ) ) + IBatchFreeType( hWnd, fileName ); + } + else if( wParam == ID_FILE_EXPORT ) + { + // Grab updated values for the font + GetDlgItemText( hWnd, IDC_FACE, fileName, sizeof( fileName ) ); + gFont->SetFace( fileName ); + gFont->SetSize( GetDlgItemInt( hWnd, IDC_FSIZE, nil, false ) ); + gFont->SetFlag( plFont::kFlagBold, IsDlgButtonChecked( hWnd, IDC_BOLD ) == BST_CHECKED ); + gFont->SetFlag( plFont::kFlagItalic, IsDlgButtonChecked( hWnd, IDC_ITALIC ) == BST_CHECKED ); + + // Write out + sprintf( fileName, "%s-%d.p2f", gFont->GetFace(), gFont->GetSize() ); + if( PromptForFile( hWnd, "Specify a file to export to", "Plasma 2 font files\0*.p2f\0", fileName, sizeof( fileName ), true ) ) + { + hsUNIXStream stream; + if( !stream.Open( fileName, "wb" ) ) + MessageBox( hWnd, "Can't open file for writing", "Error", MB_OK | MB_ICONEXCLAMATION ); + else + { +/* sprintf( fileName, "%s-%d", gFont->GetFace(), gFont->GetSize() ); + + if( gFont->GetKey() == nil ) + hsgResMgr::ResMgr()->NewKey( fileName, gFont, plLocation::kGlobalFixedLoc ); + +*/ + gFont->WriteRaw( &stream ); + stream.Close(); + } + } + } + else if( LOWORD( wParam ) == IDC_PREVTEXT && HIWORD( wParam ) == EN_CHANGE ) + { + IUpdateInfo( hWnd ); + } + return true; + + case WM_CLOSE: + PostQuitMessage( 0 ); + return true; + + case WM_DROPFILES: + { + int i, fileCount = DragQueryFile( (HDROP)wParam, -1, nil, nil ); + char path[ MAX_PATH ]; + + + for( i = 0; i < fileCount; i++ ) + { + if( DragQueryFile( (HDROP)wParam, i, path, sizeof( path ) ) > 0 ) + { + char *ext = PathFindExtension( path ); + if( stricmp( ext, ".fnt" ) == 0 ) + IImportFNT( hWnd, path ); + else if( stricmp( ext, ".bdf" ) == 0 ) + IImportBDF( hWnd, path ); + else if( stricmp( ext, ".fon" ) == 0 ) + IImportFON( hWnd, path ); + else if( stricmp( ext, ".exe" ) == 0 ) + IImportFON( hWnd, path ); + else if(( stricmp( ext, ".ttf" ) == 0 ) || ( stricmp( ext, ".ttc" ) == 0 )) + IImportFreeType( hWnd, path ); + else if( stricmp( ext, ".p2f" ) == 0 ) + IOpenP2F( hWnd, path ); + else + // Try using our freeType converter + IImportFreeType( hWnd, path ); + } + } + + IUpdateInfo( hWnd ); + } + break; + + } + return 0;//DefWindowProc( hWnd, message, wParam, lParam ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontFreeType.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontFreeType.cpp new file mode 100644 index 00000000..9c8167ab --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontFreeType.cpp @@ -0,0 +1,249 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plFontFreeType Functions // +// Basically our cheat to allow importing fonts via FreeType2 into our // +// plFont system without having to make plFont.cpp reliant on FreeType2. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "plFontFreeType.h" + +#include "ft2build.h" +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_TYPES_H + +#define kMaxGlyphs 65536 + + + +hsBool plFontFreeType::ImportFreeType( const char *fontPath, Options *options, plBDFConvertCallback *callback ) +{ + FT_Library ftLibrary; + + + IClear(); + + // Init FreeType2 + FT_Error error = FT_Init_FreeType( &ftLibrary ); + if( error ) + { + return false; + } + + try + { + + // Load our font that we're converting + FT_Face ftFace; + error = FT_New_Face( ftLibrary, fontPath, 0, &ftFace ); + if( error == FT_Err_Unknown_File_Format ) + { + // Unsupported inport format + throw false; + } + else if( error ) + { + // Some other error + throw false; + } + + // Set our point size to convert to + error = FT_Set_Char_Size( ftFace, 0, options->fSize * 64, options->fScreenRes, options->fScreenRes ); // Size is in 1/64ths of a point + if( error ) + { + throw false; + } + + // Run through our glyphs, loading them into a temp array and calcing bounding boxes for them + FT_GlyphSlot ftSlot = ftFace->glyph; + FT_ULong ftChar; + FT_UInt ftIndex; + UInt32 numGlyphs = 0, totalHeight = 0, maxChar = 0, i; + FT_Glyph ftGlyphs[ kMaxGlyphs ]; + UInt16 glyphChars[ kMaxGlyphs ]; + FT_Vector ftAdvances[ kMaxGlyphs ]; + FT_BBox ftGlyphBox, ftFontBox; + FT_UInt previous = 0; + hsBool useKerning = false; + + + if( options->fUseKerning ) + useKerning = FT_HAS_KERNING( ftFace ); + + ftFontBox.xMin = ftFontBox.yMin = 32000; + ftFontBox.xMax = ftFontBox.yMax = -32000; + + // Hack for now: if we don't have a charmap active already, just choose the first one + if( ftFace->charmap == nil ) + { + if( ftFace->num_charmaps == 0 ) + throw false; + + FT_Set_Charmap( ftFace, ftFace->charmaps[ 0 ] ); + } + + ftChar = FT_Get_First_Char( ftFace, &ftIndex ); + while( ftIndex != 0 && numGlyphs < kMaxGlyphs ) + { + error = FT_Load_Glyph( ftFace, ftIndex, FT_LOAD_DEFAULT ); + if( !error && ftChar <= options->fMaxCharLimit ) + { + + // Got this glyph, store it and update our bounding box + error = FT_Get_Glyph( ftFace->glyph, &ftGlyphs[ numGlyphs ] ); + if( error ) + throw false; + + // Don't know why this isn't valid per actual glyph... + ftAdvances[ numGlyphs ] = ftSlot->advance; + + FT_Glyph_Get_CBox( ftGlyphs[ numGlyphs ], ft_glyph_bbox_pixels, &ftGlyphBox ); + if( ftGlyphBox.xMin < ftFontBox.xMin ) + ftFontBox.xMin = ftGlyphBox.xMin; + if( ftGlyphBox.yMin < ftFontBox.yMin ) + ftFontBox.yMin = ftGlyphBox.yMin; + if( ftGlyphBox.xMax > ftFontBox.xMax ) + ftFontBox.xMax = ftGlyphBox.xMax; + if( ftGlyphBox.yMax > ftFontBox.yMax ) + ftFontBox.yMax = ftGlyphBox.yMax; + + totalHeight += ftGlyphBox.yMax - ftGlyphBox.yMin + 1; + + if( maxChar < ftChar ) + maxChar = ftChar; + glyphChars[ numGlyphs ] = (UInt16)ftChar; + + numGlyphs++; + } + + ftChar = FT_Get_Next_Char( ftFace, ftChar, &ftIndex ); + } + + // Init some of our font properties + fBPP = options->fBitDepth; + fWidth = ( ftFontBox.xMax - ftFontBox.xMin + 1 ); + if( fBPP == 1 ) + fWidth = ( ( fWidth + 7 ) >> 3 ) << 3; + fHeight = totalHeight; + fBMapData = new UInt8[ ( fWidth * fHeight * fBPP ) >> 3 ]; + memset( fBMapData, 0, ( fWidth * fHeight * fBPP ) >> 3 ); + + // Set the name and size of our font + fSize = options->fSize; + char str[ 512 ]; + + if( ftFace->style_flags & FT_STYLE_FLAG_ITALIC ) + SetFlag( kFlagItalic, true ); + if( ftFace->style_flags & FT_STYLE_FLAG_BOLD ) + SetFlag( kFlagBold, true ); + + if( IsFlagSet( kFlagItalic | kFlagBold ) ) + sprintf( str, "%s %s", ftFace->family_name, ftFace->style_name ); + else + strcpy( str, ftFace->family_name ); + SetFace( str ); + + // # of bytes per row + UInt32 stride = ( fBPP == 1 ) ? ( fWidth >> 3 ) : fWidth; + + // Pre-expand our char list + fCharacters.ExpandAndZero( maxChar + 1 ); + fCharacters.SetCount( 0 ); + if( callback != nil ) + callback->NumChars( (UInt16)(maxChar + 1) ); + + // Now re-run through our stored list of glyphs, converting them to bitmaps + for( i = 0; i < numGlyphs; i++ ) + { + if( ftGlyphs[ i ]->format != ft_glyph_format_bitmap ) + { + FT_Vector origin; + origin.x = 32; // Half a pixel over + origin.y = 0; + + error = FT_Glyph_To_Bitmap( &ftGlyphs[ i ], ( fBPP == 1 ) ? ft_render_mode_mono : ft_render_mode_normal, &origin, 0 ); + if( error ) + throw false; + } + + if( fCharacters.GetCount() < glyphChars[ i ] + 1 ) + fCharacters.SetCount( glyphChars[ i ] + 1 ); + + FT_BitmapGlyph ftBitmap = (FT_BitmapGlyph)ftGlyphs[ i ]; + plCharacter *ch = &fCharacters[ glyphChars[ i ] ]; + + UInt8 *ourBitmap = IGetFreeCharData( ch->fBitmapOff ); + if( ourBitmap == nil ) + throw false; + + if( ch->fBitmapOff > ( ( fWidth * ( fHeight - ftBitmap->bitmap.rows ) * fBPP ) >> 3 ) ) + { + hsAssert( false, "Invalid position found in IGetFreeCharData()" ); + return nil; + } + + // Set these now, since setting them before would've changed the IGetFreeCharData results + ch->fLeftKern = (hsScalar)ftBitmap->left; + ch->fRightKern = (hsScalar)ftAdvances[ i ].x / 64.f - (hsScalar)fWidth - ch->fLeftKern;//ftBitmap->bitmap.width; + ch->fBaseline = ftBitmap->top; + ch->fHeight = ftBitmap->bitmap.rows; + + // Copy data straight through to our bitmap + int y; + UInt8 *srcData = (UInt8 *)ftBitmap->bitmap.buffer; + + UInt32 bytesWide = ( fBPP == 1 ) ? ( ( ftBitmap->bitmap.width + 7 ) >> 3 ) : ftBitmap->bitmap.width; + + for( y = 0; y < ch->fHeight; y++ ) + { + memcpy( ourBitmap, srcData, bytesWide ); + srcData += ftBitmap->bitmap.pitch; + ourBitmap += stride; + } + + FT_Done_Glyph( ftGlyphs[ i ] ); + + if( callback != nil ) + callback->CharDone(); + } + + } + catch( ... ) + { + // Shut down FreeType2 + FT_Done_FreeType( ftLibrary ); + return false; + } + + ICalcFontAscent(); + + // Shut down FreeType2 + FT_Done_FreeType( ftLibrary ); + return true; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontFreeType.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontFreeType.h new file mode 100644 index 00000000..1bb0b991 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/plFontFreeType.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +/////////////////////////////////////////////////////////////////////////////// +// // +// plFontFreeType Functions // +// Basically our cheat to allow importing fonts via FreeType2 into our // +// plFont system without having to make plFont.cpp reliant on FreeType2. // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "plFont.h" + +class plFontFreeType : public plFont +{ + public: + + struct Options + { + UInt8 fSize; + hsBool fUseKerning; + UInt8 fBitDepth; + UInt32 fScreenRes; + UInt32 fMaxCharLimit; + + Options() { fSize = 12; fUseKerning = false; fBitDepth = 1; fScreenRes = 96; fMaxCharLimit = 255; } + }; + + hsBool ImportFreeType( const char *fontPath, Options *options, plBDFConvertCallback *callback ); +}; \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/res/icon1.ico b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/res/icon1.ico new file mode 100644 index 0000000000000000000000000000000000000000..f1a72bb91b0785daca6e005513afdc10ac2ad1db GIT binary patch literal 2998 zcmd6pO=ufO6o8*%vlh#;TKTVGk&7!qX>_vLOL8k4>0|=A9WZ$|!+OOBy*?KiXUXWskf%}7Y3 zBv)i{F%a|oc}3(?=EX&$iTqd=@h3z+<9!kN94W~pqew>V`=1Oao-7+pjJ(S3?k=-& zX!|c(n>U0%V=}`Bdz@8uk;_pEZT`$qvM)2i>c~(Es@3YuL?9#GDJ{)JGeI_W@6c|<)mm1dycFG=sC44r@zYWGW5jmG0l*#%~gqi1#N9tpEEl?*3S z6gk()V2YmW1RPbyb+%PbFWmNREc{=dODcEZ@^c~+`ni^9sAduCSYB zIrbcTj*?@~u`@}5y}({zFEA9?HM&OEg{>ShMkJ3&6tO7cb8E{pL@-1!L@-1!SR@!C z7$O)V7$O)fCJYe_5eyLw5eya`h6siTh6siThDc+BBZ4C`9Go0fmO?QqSQIP@7Q1Cw z6vY*V6iF2nLt($Mict%x zGx|ff?KRzjirP7Gw$;?mVah3*Qy_;ar*@7rr*=;5oZ9v{Y;Ek+ kN-yBg+{hZ$9 zFyt`g^uZh(U9r2z!pvxnkscJ(D_|?&D5z(T)^N~+0*=CPurYhwEGl3rU@3qoC}^($ zECmY-f(i@;Ec_$n;YA7&HtT1`UIT!CogA3K%pD8ioP}(jdgc zpkdH3Xc$P1V244&pkdGeXzCXv(=5~kDRL-u1&6k|O^lLHLCF@dTA5ZY=waX0zGAH(Qyjg_UtX*_+FC*YiE!54zo2814<@(5cTxxg=uGem_te^Tr@3erQBtfHDcNac(-Gz4hY<)PFjCRPb zo6AkN+OE4Tcg$a*BXo&fm+7`n|+40SBJ(lVHVb%>~ngR_(yL*`nikwQz5C<}5+0)#=W8 z7uUy&$&iPB*CyClIbAn4nSi~TduReS*2f%YuLg2&STUf3^%2*ZYcwa@Fw7ayymQ$z q(r6?Z<|=z(_+Gy`2;cYJmtpw6<>xl<@ci{dyx1QLE2k literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/res/plFontConverter.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/res/plFontConverter.rc new file mode 100644 index 00000000..f1b3c803 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/res/plFontConverter.rc @@ -0,0 +1,277 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APPICON ICON DISCARDABLE "icon1.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUT DIALOG DISCARDABLE 0, 0, 247, 57 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About plFontConverter" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,190,7,50,14 + ICON IDI_APPICON,IDC_STATIC,11,8,20,20 + LTEXT "plFontConverter\n\nA simple Plasma 2.0 utility for converting public font formats into our own bitmap font format", + IDC_STATIC,40,7,140,43 +END + +IDD_MAINDIALOG DIALOGEX 100, 100, 241, 170 +STYLE DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_ACCEPTFILES +CAPTION "plFontConverter" +MENU IDR_APPMENU +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + PUSHBUTTON "Export P2F...",ID_FILE_EXPORT,184,7,50,14 + LTEXT "Face:",IDC_STATIC,7,9,19,8 + LTEXT "Size:",IDC_STATIC,10,26,16,8 + EDITTEXT IDC_FACE,27,7,148,14,ES_AUTOHSCROLL + EDITTEXT IDC_FSIZE,27,24,30,14,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Starting glyph:",IDC_STATIC,10,43,46,8 + LTEXT "Glyph count:",IDC_STATIC,87,43,41,8 + LTEXT "0",IDC_STARTG,58,42,22,12,SS_SUNKEN + LTEXT "0",IDC_GCOUNT,130,42,22,12,SS_SUNKEN + LTEXT "Bitmap width:",IDC_STATIC,13,58,43,8 + LTEXT "0",IDC_WIDTH,58,57,22,12,SS_SUNKEN + LTEXT "Bitmap height:",IDC_STATIC,83,58,46,8 + LTEXT "0",IDC_HEIGHT,130,57,22,12,SS_SUNKEN + LTEXT "BPP:",IDC_STATIC,160,57,17,8 + LTEXT "0",IDC_BPP,179,56,22,12,SS_SUNKEN + CONTROL "",IDC_PREVIEW,"Static",SS_WHITEFRAME | SS_SUNKEN | NOT + WS_VISIBLE,7,73,227,73 + CONTROL "Bold",IDC_BOLD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,64, + 26,30,10 + CONTROL "Italic",IDC_ITALIC,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,97,26,31,10 + EDITTEXT IDC_PREVTEXT,7,149,227,14,ES_AUTOHSCROLL +END + +IDD_FONCHOOSER DIALOG DISCARDABLE 0, 0, 166, 161 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "FON Resource List" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,109,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,109,24,50,14 + LTEXT "Choose which resource to import:",IDC_STATIC,7,7,93,18 + LISTBOX IDC_RESLIST,7,27,97,127,LBS_SORT | LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP +END + +IDD_PROGRESS DIALOG DISCARDABLE 0, 0, 230, 25 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Importing font..." +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Progress1",IDC_PROGRESS,"msctls_progress32",PBS_SMOOTH | + WS_BORDER,7,7,216,11 +END + +IDD_FREETYPE DIALOG DISCARDABLE 0, 0, 234, 68 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "FreeType Import Options" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,177,47,50,14 + PUSHBUTTON "Cancel",IDCANCEL,115,47,50,14 + LTEXT "Render Point Size:",IDC_STATIC,7,10,60,8 + EDITTEXT IDC_PSIZE,70,8,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Monochrome",IDC_BITDEPTH,"Button",BS_AUTORADIOBUTTON, + 120,27,57,10 + CONTROL "Grayscale",IDC_BITDEPTH2,"Button",BS_AUTORADIOBUTTON, + 180,27,47,10 + LTEXT "Screen Resolution:",IDC_STATIC,7,28,61,8 + EDITTEXT IDC_RES,70,26,40,14,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Max Character:",IDC_STATIC,132,10,49,8 + EDITTEXT IDC_MAXCHAR,183,8,40,14,ES_AUTOHSCROLL | ES_NUMBER + PUSHBUTTON "Batch...",IDC_BATCH,7,47,50,14 +END + +IDD_FREETYPEBATCH DIALOG DISCARDABLE 0, 0, 234, 114 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "FreeType Import Options" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,177,93,50,14 + PUSHBUTTON "Cancel",IDCANCEL,115,93,50,14 + LTEXT "Render Point Sizes:",IDC_STATIC,7,10,63,8 + EDITTEXT IDC_PSIZE,71,8,156,14,ES_AUTOHSCROLL + CONTROL "Monochrome",IDC_BITDEPTH,"Button",BS_AUTORADIOBUTTON,58, + 58,57,10 + CONTROL "Grayscale",IDC_BITDEPTH2,"Button",BS_AUTORADIOBUTTON, + 118,58,47,10 + LTEXT "Screen Resolution:",IDC_STATIC,117,41,61,8 + EDITTEXT IDC_RES,180,39,40,14,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Max Character:",IDC_STATIC,15,41,49,8 + EDITTEXT IDC_MAXCHAR,67,39,40,14,ES_AUTOHSCROLL | ES_NUMBER + GROUPBOX "Common Settings",IDC_STATIC,7,27,220,45 + LTEXT "Font name:",IDC_STATIC,7,77,36,8 + EDITTEXT IDC_FONTNAME,49,75,178,14,ES_AUTOHSCROLL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 240 + TOPMARGIN, 7 + BOTTOMMARGIN, 50 + END + + IDD_MAINDIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 234 + TOPMARGIN, 7 + BOTTOMMARGIN, 163 + END + + IDD_FONCHOOSER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 159 + TOPMARGIN, 7 + BOTTOMMARGIN, 154 + END + + IDD_PROGRESS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 223 + TOPMARGIN, 7 + BOTTOMMARGIN, 18 + END + + IDD_FREETYPE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 227 + TOPMARGIN, 7 + BOTTOMMARGIN, 61 + END + + IDD_FREETYPEBATCH, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 227 + TOPMARGIN, 8 + BOTTOMMARGIN, 107 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_APPMENU MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "Import FNT...", ID_FILE_FNT + MENUITEM "Import BDF...", ID_FILE_BDF + MENUITEM "Import FON...", ID_FILE_FON + MENUITEM "Open P2F...", ID_FILE_P2F + MENUITEM "Batch Convert TrueType...", ID_FILE_TRUETYPE + MENUITEM SEPARATOR + MENUITEM "Export P2F...", ID_FILE_EXPORT + MENUITEM SEPARATOR + MENUITEM "About...", ID_FILE_ABOUT + MENUITEM "Exit", ID_FILE_EXIT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCELERATOR1 ACCELERATORS DISCARDABLE +BEGIN + "A", ID_FILE_ABOUT, VIRTKEY, CONTROL, NOINVERT +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/res/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/res/resource.h new file mode 100644 index 00000000..4ef7adf6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plFontConverter/res/resource.h @@ -0,0 +1,52 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by plFontConverter.rc +// +#define IDI_APPICON 101 +#define IDR_APPMENU 102 +#define IDD_MAINDIALOG 103 +#define IDD_ABOUT 104 +#define IDD_FONCHOOSER 105 +#define IDR_ACCELERATOR1 106 +#define IDD_PROGRESS 106 +#define IDD_FREETYPE 107 +#define IDD_FREETYPEBATCH 108 +#define IDC_FACE 1002 +#define IDC_FSIZE 1003 +#define IDC_STARTG 1004 +#define IDC_GCOUNT 1005 +#define IDC_WIDTH 1006 +#define IDC_HEIGHT 1007 +#define IDC_BPP 1008 +#define IDC_PREVIEW 1010 +#define IDC_RESLIST 1011 +#define IDC_BOLD 1012 +#define IDC_ITALIC 1013 +#define IDC_PROGRESS 1014 +#define IDC_PREVTEXT 1015 +#define IDC_PSIZE 1016 +#define IDC_BITDEPTH 1017 +#define IDC_BITDEPTH2 1018 +#define IDC_RES 1019 +#define IDC_MAXCHAR 1020 +#define IDC_BATCH 1021 +#define IDC_FONTNAME 1022 +#define ID_FILE_FNT 40000 +#define ID_FILE_EXPORT 40001 +#define ID_FILE_EXIT 40002 +#define ID_FILE_BDF 40003 +#define ID_FILE_ABOUT 40004 +#define ID_FILE_P2F 40005 +#define ID_FILE_FON 40006 +#define ID_FILE_TRUETYPE 40007 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_COMMAND_VALUE 40008 +#define _APS_NEXT_CONTROL_VALUE 1023 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.cpp new file mode 100644 index 00000000..eacf5e3c --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.cpp @@ -0,0 +1,114 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#define CLASSNAME "plResBrowser" // Used in WinInit() +#define WINDOWNAME "plResBrowser" + +#include "HeadSpin.h" +#include "hsTypes.h" +#include +#include "res/resource.h" + +#include "pnAllCreatables.h" +#include "../plResMgr/plResMgrCreatable.h" +#include "../plResMgr/plResManager.h" +#include "../plResMgr/plResMgrSettings.h" +#include "../plMessage/plResMgrHelperMsg.h" +#include "../plUnifiedTime/plUnifiedTimeCreatable.h" +REGISTER_CREATABLE(plResMgrHelperMsg); + + +HINSTANCE gInstance; +char *gCommandLine = nil; +HWND gMainWindow = nil; + +LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ); +BOOL WinInit( HINSTANCE hInst, int nCmdShow ); + + +int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) +{ + MSG msg; + HACCEL accelTable = LoadAccelerators( hInstance, MAKEINTRESOURCE( IDR_ACCELERATOR1 ) ); + + + plResMgrSettings::Get().SetFilterNewerPageVersions( false ); + plResMgrSettings::Get().SetFilterOlderPageVersions( false ); + + gCommandLine = (char *)lpCmdLine; + plResManager *rMgr = TRACKED_NEW plResManager; + hsgResMgr::Init( rMgr ); + + if( !WinInit( hInstance, nCmdShow ) ) + return -1; + + while( GetMessage( &msg, NULL, 0, 0 ) ) + { + if( !TranslateAccelerator( gMainWindow, accelTable, &msg ) ) + { + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + } + + hsgResMgr::Shutdown(); + + return 0; +} + +BOOL WinInit(HINSTANCE hInst, int nCmdShow) +{ + gInstance = hInst; + + // Fill out WNDCLASS info + WNDCLASS wndClass; + wndClass.style = 0; // CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = WndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = hInst; + wndClass.hIcon = LoadIcon( hInst, MAKEINTRESOURCE( IDI_APPICON ) ); + + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = (HBRUSH)GetSysColorBrush( COLOR_3DFACE ); + wndClass.lpszMenuName = MAKEINTRESOURCE( IDR_APPMENU ); + wndClass.lpszClassName = CLASSNAME; + + // can only run one at a time anyway, so just quit if another is running + if (!RegisterClass(&wndClass)) + return FALSE; + + DWORD dwStyle = WS_POPUP | WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE; + DWORD dwExStyle = WS_EX_ACCEPTFILES; + + // Create a window + gMainWindow = CreateWindowEx(dwExStyle, CLASSNAME, WINDOWNAME, + dwStyle, 10, 10, + 800, + 600, + NULL, NULL, hInst, NULL); + + return TRUE; +} diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.dsp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.dsp new file mode 100644 index 00000000..09b806c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.dsp @@ -0,0 +1,175 @@ +# Microsoft Developer Studio Project File - Name="plResBrowser" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=plResBrowser - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "plResBrowser.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "plResBrowser.mak" CFG="plResBrowser - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "plResBrowser - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "plResBrowser - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "plResBrowser - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../Sources/Plasma/NucleusLib/inc" /I "..\..\..\Sources\Plasma\PubUtilLib\inc" /I "..\..\..\Sources\Plasma\CoreLib" /I "../../../Sources/Plasma/FeatureLib/inc" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 ws2_32.lib wininet.lib winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib Shlwapi.lib libcrypto.a /nologo /subsystem:windows /machine:I386 /nodefaultlib:"libc.lib" /libpath:"..\..\..\..\StaticSDKs\Win32\OpenSSL\lib" +# Begin Special Build Tool +TargetPath=.\Release\plResBrowser.exe +SOURCE="$(InputPath)" +PostBuild_Cmds=xcopy /Y $(TargetPath) ..\..\..\tools\ +# End Special Build Tool + +!ELSEIF "$(CFG)" == "plResBrowser - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm- /GX /ZI /Od /I "../../../Sources/Plasma/NucleusLib/inc" /I "..\..\..\Sources\Plasma\PubUtilLib\inc" /I "..\..\..\Sources\Plasma\CoreLib" /I "../../../Sources/Plasma/FeatureLib/inc" /D "WIN32" /D "_DEBUG" /D "_LIB" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ws2_32.lib wininet.lib winmm.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib Shlwapi.lib libcrypto.a /nologo /subsystem:windows /debug /machine:I386 /nodefaultlib:"libc.lib" /pdbtype:sept /libpath:"..\..\..\..\StaticSDKs\Win32\OpenSSL\lib" +# Begin Special Build Tool +TargetPath=.\Debug\plResBrowser.exe +SOURCE="$(InputPath)" +PostBuild_Cmds=xcopy /Y $(TargetPath) ..\..\..\tools\ +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "plResBrowser - Win32 Release" +# Name "plResBrowser - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\plPatchDetail.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Sources\Tools\plResBrowser\plResBrowser.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Sources\Tools\plResBrowser\plResBrowserWndProc.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Sources\Tools\plResBrowser\plResTreeView.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\Sources\Tools\plResBrowser\plWinRegistryTools.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\..\Sources\Tools\plResBrowser\plResTreeView.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\Sources\Tools\plResBrowser\plWinRegistryTools.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\res\dataicon.ico +# End Source File +# Begin Source File + +SOURCE=..\..\..\Sources\Tools\plResBrowser\res\icon1.ico +# End Source File +# Begin Source File + +SOURCE=.\res\icon2.ico +# End Source File +# Begin Source File + +SOURCE=.\res\indexico.ico +# End Source File +# Begin Source File + +SOURCE=.\res\mergedda.ico +# End Source File +# Begin Source File + +SOURCE=.\res\mergedin.ico +# End Source File +# Begin Source File + +SOURCE=..\..\..\Sources\Tools\plResBrowser\res\plResBrowser.rc +# End Source File +# Begin Source File + +SOURCE=..\..\..\Sources\Tools\plResBrowser\res\resource.h +# End Source File +# End Group +# End Target +# End Project diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.vcproj b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.vcproj new file mode 100644 index 00000000..f046ea8a --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowser.vcproj @@ -0,0 +1,292 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowserWndProc.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowserWndProc.cpp new file mode 100644 index 00000000..e4878d80 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResBrowserWndProc.cpp @@ -0,0 +1,424 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsTemplates.h" +#include +#include "afxres.h" +#include "res/resource.h" +#include +#include +#include + +#include "plResTreeView.h" +#include "../plResMgr/plResManager.h" +#include "../plResMgr/plResMgrSettings.h" +#include "plWinRegistryTools.h" +#include "../plFile/hsFiles.h" + +#define IDC_REGTREEVIEW 1000 + +extern HINSTANCE gInstance; +extern char *gCommandLine; + +HWND gTreeView; +HWND gInfoDlg; + +class plWaitCursor +{ + HCURSOR fOrig; + public: + plWaitCursor() + { + fOrig = ::SetCursor( ::LoadCursor( nil, IDC_WAIT ) ); + } + + ~plWaitCursor() + { + ::SetCursor( fOrig ); + } +}; + +void SetWindowTitle( HWND hWnd, char *path ) +{ + char fun[ MAX_PATH + 50 ]; + + + sprintf( fun, "plResBrowser%s%s", path != nil ? " - " : "", path != nil ? path : "" ); + SetWindowText( hWnd, fun ); +} + +BOOL CALLBACK AboutDialogProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + if( msg == WM_COMMAND ) + EndDialog( hWnd, 0 ); + return 0; +} + +LRESULT CALLBACK HandleCommand( HWND hWnd, WPARAM wParam, LPARAM lParam ) +{ + OPENFILENAME openInfo; + char fileName[ MAX_PATH ]; + char path[ MAX_PATH ]; + + static bool filter = true; + + + switch( LOWORD( wParam ) ) + { + case ID_FILE_EXIT: + PostQuitMessage( 0 ); + break; + + case ID_FILE_OPENDIRECTORY: + + BROWSEINFO bInfo; + LPITEMIDLIST itemList; + LPMALLOC shMalloc; + + + memset( &bInfo, 0, sizeof( bInfo ) ); + bInfo.hwndOwner = hWnd; + bInfo.pidlRoot = NULL; + bInfo.pszDisplayName = path; + bInfo.lpszTitle = "Select a Plasma 2 Data Directory:"; + bInfo.ulFlags = BIF_EDITBOX; + + itemList = SHBrowseForFolder( &bInfo ); + if( itemList != NULL ) + { + plWaitCursor myWaitCursor; + + SHGetPathFromIDList( itemList, path ); + SHGetMalloc( &shMalloc ); + shMalloc->Free( itemList ); + shMalloc->Release(); + + hsgResMgr::Reset(); + plResTreeView::ClearTreeView( gTreeView ); + + // Load that source + plResManager *mgr = (plResManager *)hsgResMgr::ResMgr(); + + hsFolderIterator pathIterator(path); + while (pathIterator.NextFileSuffix(".prp")) + { + char fileName[kFolderIterator_MaxPath]; + pathIterator.GetPathAndName(fileName); + mgr->AddSinglePage(fileName); + } + + plResTreeView::FillTreeViewFromRegistry( gTreeView ); + + SetWindowTitle( hWnd, path ); + } + + break; + + case ID_FILE_OPEN: + fileName[ 0 ] = 0; + + memset( &openInfo, 0, sizeof( OPENFILENAME ) ); + openInfo.hInstance = gInstance; + openInfo.hwndOwner = hWnd; + openInfo.lStructSize = sizeof( OPENFILENAME ); + openInfo.lpstrFile = fileName; + openInfo.nMaxFile = sizeof( fileName ); + openInfo.lpstrFilter = "Plasma 2 Pack Files\0*.prp\0All Files\0*.*\0"; + openInfo.lpstrTitle = "Choose a file to browse:"; + openInfo.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + + if( GetOpenFileName( &openInfo ) ) + { + plWaitCursor myWaitCursor; + + hsgResMgr::Reset(); + plResTreeView::ClearTreeView( gTreeView ); + + // Load that source + plResManager *mgr = (plResManager *)hsgResMgr::ResMgr(); + mgr->AddSinglePage(fileName); + plResTreeView::FillTreeViewFromRegistry( gTreeView ); + + SetWindowTitle( hWnd, fileName ); + } + + break; + + case ID_FILE_ABOUT: + DialogBox( gInstance, MAKEINTRESOURCE( IDD_ABOUT ), hWnd, AboutDialogProc ); + break; + + case ID_FILE_FINDOBJECT: + plResTreeView::FindObject( gTreeView ); + break; + + case ID_FILE_FINDNEXT: + plResTreeView::FindNextObject( gTreeView ); + break; + + case ID_FILE_VERIFYPAGE: + plResTreeView::VerifyCurrentPage( gTreeView ); + break; + + case IDC_SHOWASHEX: + plResTreeView::UpdateInfoDlg( gTreeView ); + break; + + case ID_FILE_ONLYLOAD: + filter = !filter; + plResTreeView::FilterLoadables( filter, gTreeView ); + { + HMENU menu = ::GetMenu( hWnd ); + menu = ::GetSubMenu( menu, 0 ); + ::CheckMenuItem( menu, ID_FILE_ONLYLOAD, MF_BYCOMMAND | ( filter ? MF_CHECKED : MF_UNCHECKED ) ); + } + break; + + case ID_FILE_SAVESELECTED: + plResTreeView::SaveSelectedObject(gTreeView); + break; + + default: + return DefWindowProc( hWnd, WM_COMMAND, wParam, lParam ); + } + + return 0; +} + +void SizeControls( HWND parent ) +{ + RECT clientRect, infoRect; + + + GetClientRect( parent, &clientRect ); + GetClientRect( gInfoDlg, &infoRect ); + + SetWindowPos( gTreeView, NULL, 0, 0, clientRect.right - infoRect.right - 4, clientRect.bottom, 0 ); + + OffsetRect( &infoRect, clientRect.right - infoRect.right, ( clientRect.bottom >> 1 ) - ( infoRect.bottom >> 1 ) ); + SetWindowPos( gInfoDlg, NULL, infoRect.left, infoRect.top, 0, 0, SWP_NOSIZE ); +} + +void InitWindowControls( HWND hWnd ) +{ + RECT clientRect; + + + GetClientRect( hWnd, &clientRect ); + + gTreeView = CreateWindowEx( WS_EX_CLIENTEDGE, WC_TREEVIEW, "Tree View", WS_VISIBLE | WS_CHILD | WS_BORDER | + TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_SHOWSELALWAYS, + 0, 0, 0, 0, + hWnd, (HMENU)IDC_REGTREEVIEW, gInstance, NULL ); + + + gInfoDlg = CreateDialog( gInstance, MAKEINTRESOURCE( IDD_INFODLG ), hWnd, plResTreeView::InfoDlgProc ); + + SizeControls( hWnd ); +} + +static bool sFileTypesRegistered = false; + +void RegisterFileTypes( HWND mainWnd ) +{ + if( sFileTypesRegistered ) + return; + + // Make sure our file types are created + char path[ MAX_PATH ]; + + if( ::GetModuleFileName( nil, path, sizeof( path ) ) == 0 ) + return; + + //plWinRegistryTools::AssociateFileType( "PlasmaIdxFile", "Plasma 2 Index File", path, 1 ); + //plWinRegistryTools::AssociateFileType( "PlasmaDatFile", "Plasma 2 Data File", path, 2 ); + //plWinRegistryTools::AssociateFileType( "PlasmaPatchFile", "Plasma 2 Patch File", path, 3 ); + plWinRegistryTools::AssociateFileType( "PlasmaPackFile", "Plasma 2 Packfile", path, 4 ); + + // Check our file extensions + char prpAssoc[ 512 ]; + hsBool needToRegister = true; + if( plWinRegistryTools::GetCurrentFileExtensionAssociation( ".prp", prpAssoc, sizeof( prpAssoc ) ) ) + { + if( strcmp( prpAssoc, "PlasmaPackFile" ) == 0 ) + needToRegister = false; + } + + if( needToRegister ) + { + if( MessageBox( nil, "The Plasma 2 packed data file extension .prp is not currently associated with " + "plResBrowser. Would you like to associate it now?", "plResBrowser File Type Association", + MB_YESNO | MB_ICONQUESTION) == IDYES ) + { + // Associate 'em + plWinRegistryTools::AssociateFileExtension( ".prp", "PlasmaPackFile" ); + } + } + + sFileTypesRegistered = true; +} + +LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + switch( message ) + { + case WM_CREATE: + InitCommonControls(); + InitWindowControls( hWnd ); + RegisterFileTypes( hWnd ); + plResMgrSettings::Get().SetLoadPagesOnInit(false); + + { + plResTreeView::FilterLoadables( true, gTreeView ); + HMENU menu = ::GetMenu( hWnd ); + menu = ::GetSubMenu( menu, 0 ); + ::CheckMenuItem( menu, ID_FILE_ONLYLOAD, MF_BYCOMMAND | MF_CHECKED ); + } + + if( gCommandLine != nil ) + { + plWaitCursor myWaitCursor; + + char path[ MAX_PATH ]; + if( gCommandLine[ 0 ] == '"' ) + { + strcpy( path, gCommandLine + 1 ); + char *c = strchr( path, '"' ); + if( c != nil ) + *c = 0; + } + else + strcpy( path, gCommandLine ); + + if( stricmp( PathFindExtension( path ), ".prp" ) == 0 ) + { + hsgResMgr::Reset(); + plResTreeView::ClearTreeView( gTreeView ); + plResManager *mgr = (plResManager *)hsgResMgr::ResMgr(); + mgr->AddSinglePage(path); + plResTreeView::FillTreeViewFromRegistry( gTreeView ); + + SetWindowTitle( hWnd, path ); + } + } + break; + + case WM_CLOSE: + DestroyWindow( hWnd ); + break; + case WM_DESTROY: + plResTreeView::ClearTreeView( gTreeView ); + PostQuitMessage(0); + break; + + case WM_SIZING: + case WM_SIZE: + SizeControls( hWnd ); + break; + + case WM_NOTIFY: + if( wParam == IDC_REGTREEVIEW ) + { + NMHDR *hdr = (NMHDR *)lParam; + if( hdr->code == TVN_SELCHANGED ) + { + plResTreeView::UpdateInfoDlg( gTreeView ); + //NMTREEVIEW *tv = (NMTREEVIEW *)hdr; + + } + else if( hdr->code == NM_DBLCLK ) + { + plResTreeView::SelectionDblClicked( gTreeView ); + } + } + break; + + case WM_DROPFILES: + { + int i, j, fileCount = DragQueryFile( (HDROP)wParam, -1, nil, nil ); + char path[ MAX_PATH ]; + + plWaitCursor myWaitCursor; + + hsgResMgr::Reset(); + plResTreeView::ClearTreeView( gTreeView ); + plResManager *mgr = (plResManager *)hsgResMgr::ResMgr(); + + if( fileCount == 1 && DragQueryFile( (HDROP)wParam, 0, path, sizeof( path ) ) > 0 && + ( (char *)PathFindExtension( path ) )[ 0 ] == 0 ) + { + // Must be a directory + hsFolderIterator pathIterator(path); + while (pathIterator.NextFileSuffix(".prp")) + { + char fileName[kFolderIterator_MaxPath]; + pathIterator.GetPathAndName(fileName); + mgr->AddSinglePage(fileName); + } + } + else + { + hsTArray filesAdded; + + filesAdded.Reset(); + for( i = 0; i < fileCount; i++ ) + { + if( DragQueryFile( (HDROP)wParam, i, path, sizeof( path ) ) > 0 ) + { + // Check for duplicates + for( j = 0; j < filesAdded.GetCount(); j++ ) + { + if( stricmp( filesAdded[ j ], path ) == 0 ) + break; + } + if( j < filesAdded.GetCount() ) + continue; + + if( stricmp( PathFindExtension( path ), ".prp" ) == 0 ) + { + mgr->AddSinglePage(path); + filesAdded.Append( hsStrcpy( path ) ); + } + } + } + + for( j = 0; j < filesAdded.GetCount(); j++ ) + delete [] filesAdded[ j ]; + } + plResTreeView::FillTreeViewFromRegistry( gTreeView ); + + PathRemoveFileSpec( path ); + SetWindowTitle( hWnd, path ); + } + break; + + case WM_COMMAND: + return HandleCommand( hWnd, wParam, lParam ); + } + + return DefWindowProc( hWnd, message, wParam, lParam ); +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResTreeView.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResTreeView.cpp new file mode 100644 index 00000000..7c645c98 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResTreeView.cpp @@ -0,0 +1,506 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#include "hsTypes.h" +#include "hsWindows.h" +#include "plResTreeView.h" + +#include "../plResMgr/plResManager.h" +#include "../plResMgr/plRegistryHelpers.h" +#include "../plResMgr/plRegistryNode.h" +#include "../plResMgr/plPageInfo.h" +#include "../pnKeyedObject/plUoid.h" +#include "../pnKeyedObject/plKey.h" +#include "../pnKeyedObject/plKeyImp.h" +#include "../pnFactory/plFactory.h" + +#include +#include +#include +#include "res\resource.h" + + +extern HINSTANCE gInstance; +HWND plResTreeView::fInfoDlg = nil; +bool plResTreeView::fFilter = false; + +static char gSearchString[ 512 ]; +static HTREEITEM fFoundItem = nil; + +extern void ViewPatchDetails( plKey &patchKey ); + + +BOOL CALLBACK FindDialogProc( HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch( msg ) + { + case WM_INITDIALOG: + return true; + + case WM_COMMAND: + if( LOWORD( wParam ) == IDOK ) + { + GetDlgItemText( dlg, IDC_SEARCHSTRING, gSearchString, sizeof( gSearchString ) ); + fFoundItem = nil; + EndDialog( dlg, IDOK ); + } + else if( LOWORD( wParam ) == IDCANCEL ) + EndDialog( dlg, IDCANCEL ); + + return -1; + } + + return 0; +} + + +struct plKeyInfo +{ + plKey fKey; + plRegistryPageNode *fPage; + + plKeyInfo( plKey k, plRegistryPageNode *p ) : fKey( k ), fPage( p ) {} +}; + +// How's this for functionality? +class plResDlgLoader : public plRegistryPageIterator, public plRegistryKeyIterator +{ + protected: + + HWND fTree; + HTREEITEM fCurrItem, fCurrTypeItem; + UInt16 fCurrType; + bool fFilter; + + plRegistryPageNode *fCurrPage; + + + HTREEITEM AddLeaf(HWND hTree, HTREEITEM hParent, const char *text, plKeyInfo *info ) + { + TVITEM tvi = {0}; + tvi.mask = TVIF_TEXT | TVIF_PARAM; + tvi.pszText = text ? (char*)text : ""; + tvi.cchTextMax = text ? strlen(text) : 7; + tvi.lParam = (LPARAM)info; + + TVINSERTSTRUCT tvins = {0}; + tvins.item = tvi; + tvins.hParent = hParent; + tvins.hInsertAfter = TVI_SORT; + + return TreeView_InsertItem(hTree, &tvins); + } + + public: + + plResDlgLoader( HWND hTree, bool filter ) + { + fFilter = filter; + fTree = hTree; + ((plResManager *)hsgResMgr::ResMgr())->IterateAllPages( this ); + } + + virtual hsBool EatPage( plRegistryPageNode *page ) + { + char str[ 512 ]; + + + fCurrPage = page; + const plPageInfo &info = page->GetPageInfo(); + sprintf( str, "%s->%s->%s", info.GetAge(), info.GetPage() ); + fCurrItem = AddLeaf( fTree, NULL, str, new plKeyInfo( nil, fCurrPage ) ); + + fCurrType = (UInt16)-1; + page->LoadKeys(); + page->IterateKeys( this ); + return true; + } + + virtual hsBool EatKey( const plKey& key ) + { + if( fCurrType != key->GetUoid().GetClassType() ) + { + fCurrType = key->GetUoid().GetClassType(); + const char *className = plFactory::GetNameOfClass( fCurrType ); + fCurrTypeItem = AddLeaf( fTree, fCurrItem, className != nil ? className : "", nil ); + } + + if( !fFilter ) + AddLeaf( fTree, fCurrTypeItem, key->GetUoid().GetObjectName(), new plKeyInfo( key, fCurrPage ) ); + return true; + } +}; + +void plResTreeView::FillTreeViewFromRegistry( HWND hWnd ) +{ + plResDlgLoader loader( hWnd, fFilter ); +} + +HTREEITEM IGetNextTreeItem( HWND tree, HTREEITEM item ) +{ + // First try child items of this one + HTREEITEM next = TreeView_GetChild( tree, item ); + if( next == nil ) + // If no child items, try next sibling + next = TreeView_GetNextSibling( tree, item ); + if( next == nil ) + { + // If no siblings, go up to the parent and keep searching until we find a parent with a sibling + next = item; + while( true ) + { + next = TreeView_GetParent( tree, next ); + if( next == nil ) + { + // No parent; not found, so stop + break; + } + else if( TreeView_GetNextSibling( tree, next ) != nil ) + { + next = TreeView_GetNextSibling( tree, next ); + break; + } + } + } + + return next; +} + +void plResTreeView::FindNextObject( HWND tree ) +{ + if( fFoundItem == nil ) + FindObject( tree ); + else + { + fFoundItem = IGetNextTreeItem( tree, fFoundItem ); + IFindNextObject( tree ); + } +} + +void plResTreeView::FindObject( HWND tree ) +{ + if( DialogBox( gInstance, MAKEINTRESOURCE( IDD_FINDOBJ ), tree, FindDialogProc ) == IDOK ) + { + fFoundItem = TreeView_GetRoot( tree ); + IFindNextObject( tree ); + } +} + +void plResTreeView::IFindNextObject( HWND tree ) +{ + while( fFoundItem != nil ) + { + // Get the item + TVITEM itemInfo; + itemInfo.mask = TVIF_PARAM | TVIF_HANDLE; + itemInfo.hItem = fFoundItem; + TreeView_GetItem( tree, &itemInfo ); + plKeyInfo *keyInfo = (plKeyInfo *)itemInfo.lParam; + if( keyInfo != nil && keyInfo->fKey != nil ) + { + if( StrStrI( keyInfo->fKey->GetUoid().GetObjectName(), gSearchString ) != nil ) + { + /// FOUND + TreeView_SelectItem( tree, fFoundItem ); + return; + } + } + + // Keep searching. First try child items of this one + fFoundItem = IGetNextTreeItem( tree, fFoundItem ); + } + + MessageBox( tree, "No objects found", "Find Object", MB_OK ); +} + +void IDeleteRecurse( HWND tree, HTREEITEM item ) +{ + while( item != nil ) + { + HTREEITEM child = TreeView_GetChild( tree, item ); + if( child != nil ) + IDeleteRecurse( tree, child ); + + TVITEM itemInfo; + itemInfo.mask = TVIF_PARAM | TVIF_HANDLE; + itemInfo.hItem = item; + TreeView_GetItem( tree, &itemInfo ); + plKeyInfo *keyInfo = (plKeyInfo *)itemInfo.lParam; + if( keyInfo != nil ) + { + delete keyInfo; + itemInfo.lParam = 0; + TreeView_SetItem( tree, &itemInfo ); + } + + item = TreeView_GetNextSibling( tree, item ); + } +} + +void plResTreeView::ClearTreeView( HWND hWnd ) +{ + HTREEITEM root = TreeView_GetRoot( hWnd ); + if( root != nil ) + IDeleteRecurse( hWnd, root ); + + TreeView_DeleteAllItems( hWnd ); +} + +void plResTreeView::SelectionDblClicked( HWND treeCtrl ) +{ + HTREEITEM sel = TreeView_GetSelection( treeCtrl ); + if( sel != nil ) + { + TVITEM item; + + item.mask = TVIF_PARAM | TVIF_HANDLE; + item.hItem = sel; + if( !TreeView_GetItem( treeCtrl, &item ) ) + return; + } +} + +void plResTreeView::FilterLoadables( bool filter, HWND treeCtrl ) +{ + fFilter = filter; + ClearTreeView( treeCtrl ); + FillTreeViewFromRegistry( treeCtrl ); +} + +BOOL CALLBACK plResTreeView::InfoDlgProc( HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam ) +{ + switch( msg ) + { + case WM_INITDIALOG: + fInfoDlg = dlg; + break; + + case WM_COMMAND: + return SendMessage( GetParent( dlg ), msg, wParam, lParam ); + } + + return 0; +} + +void plResTreeView::VerifyCurrentPage( HWND treeCtrl ) +{ + HTREEITEM sel = TreeView_GetSelection( treeCtrl ); + if( sel != nil ) + { + TVITEM item; + + item.mask = TVIF_PARAM | TVIF_HANDLE; + item.hItem = sel; + if( !TreeView_GetItem( treeCtrl, &item ) ) + return; + + plKeyInfo *info = (plKeyInfo *)item.lParam; + if( info != nil ) + { + if( info->fPage != nil ) + { + // TODO: FIXME + /* + /// HACK. Live with it + class plHackResManager : public plResManager + { + public: + plRegistry *GetRegistry( void ) { return IGetRegistry(); } + }; + + plRegistry *registry = ((plHackResManager *)hsgResMgr::ResMgr())->GetRegistry(); + + plRegistry::plPageCond result = registry->VerifyOnePage( info->fPage ); + + char msg[ 512 ]; + if( result == plRegistry::kOK || result == plRegistry::kTooNew ) + strcpy( msg, "Page verifies OK" ); + else if( result == plRegistry::kChecksumInvalid ) + strcpy( msg, "Checksums for page are invalid" ); + else if( result == plRegistry::kOutOfDate ) + strcpy( msg, "Page is older than the current data version" ); + + hsMessageBox( msg, "Verification Results", hsMessageBoxNormal ); + */ + } + } + } +} + +void plResTreeView::UpdateInfoDlg( HWND treeCtrl ) +{ + hsBool showAsHex = (hsBool)IsDlgButtonChecked( fInfoDlg, IDC_SHOWASHEX ); + + SetDlgItemText( fInfoDlg, IDC_NAME, "" ); + SetDlgItemText( fInfoDlg, IDC_CLASS, "" ); + SetDlgItemText( fInfoDlg, IDC_LENGTH, "" ); + SetDlgItemText( fInfoDlg, IDC_STARTPOS, "" ); + EnableWindow( GetDlgItem( fInfoDlg, ID_FILE_VERIFYPAGE ), FALSE ); + + HTREEITEM sel = TreeView_GetSelection( treeCtrl ); + if( sel != nil ) + { + TVITEM item; + + item.mask = TVIF_PARAM | TVIF_HANDLE; + item.hItem = sel; + if( !TreeView_GetItem( treeCtrl, &item ) ) + return; + + plKeyInfo *info = (plKeyInfo *)item.lParam; + if( info != nil ) + { + if( info->fPage != nil ) + { + const plPageInfo &pageInfo = info->fPage->GetPageInfo(); + char tempStr[ 32 ]; + + SetDlgItemText( fInfoDlg, IDC_AGE, pageInfo.GetAge() ); + SetDlgItemText( fInfoDlg, IDC_PAGE, pageInfo.GetPage() ); + + SetDlgItemText( fInfoDlg, IDC_LOCATION, pageInfo.GetLocation().StringIze( tempStr ) ); + + CheckDlgButton(fInfoDlg, IDC_RESERVED, (pageInfo.GetLocation().GetFlags() & plLocation::kReserved) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(fInfoDlg, IDC_BUILTIN, (pageInfo.GetLocation().GetFlags() & plLocation::kBuiltIn) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(fInfoDlg, IDC_VOLATILE, (pageInfo.GetLocation().GetFlags() & plLocation::kVolatile) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(fInfoDlg, IDC_LOCAL_ONLY, (pageInfo.GetLocation().GetFlags() & plLocation::kLocalOnly) ? BST_CHECKED : BST_UNCHECKED); + + sprintf( tempStr, "%d", pageInfo.GetMajorVersion()); + SetDlgItemText( fInfoDlg, IDC_DATAVERSION, tempStr ); + + SetDlgItemText( fInfoDlg, IDC_CHECKSUMTYPE, "Basic (file size)" ); + EnableWindow( GetDlgItem( fInfoDlg, ID_FILE_VERIFYPAGE ), TRUE ); + } + + if( info->fKey != nil ) + { + char str[ 128 ]; + + + SetDlgItemText( fInfoDlg, IDC_NAME, info->fKey->GetUoid().GetObjectName() ); + + const char *name = plFactory::GetNameOfClass( info->fKey->GetUoid().GetClassType() ); + sprintf( str, "%s (%d)", name != nil ? name : "", info->fKey->GetUoid().GetClassType() ); + SetDlgItemText( fInfoDlg, IDC_CLASS, str ); + + plKeyImp *imp = (plKeyImp *)info->fKey; + EnableWindow( GetDlgItem( fInfoDlg, IDC_STARTPOS_LABEL ), true ); + EnableWindow( GetDlgItem( fInfoDlg, IDC_SIZE_LABEL ), true ); + + if( showAsHex ) + sprintf( str, "0x%X", imp->GetStartPos() ); + else + sprintf( str, "%d", imp->GetStartPos() ); + SetDlgItemText( fInfoDlg, IDC_STARTPOS, str ); + + if( imp->GetDataLen() < 1024 ) + sprintf( str, "%d bytes", imp->GetDataLen() ); + else if( imp->GetDataLen() < 1024 * 1024 ) + sprintf( str, "%4.2f kB", imp->GetDataLen() / 1024.f ); + else + sprintf( str, "%4.2f MB", imp->GetDataLen() / 1024.f / 1024.f ); + + SetDlgItemText( fInfoDlg, IDC_LENGTH, str ); + } + } + } +} + +#include "hsStream.h" +#include + +void plResTreeView::SaveSelectedObject(HWND treeCtrl) +{ + // TODO: FIXME + /* + plKey itemKey = nil; + + HTREEITEM sel = TreeView_GetSelection(treeCtrl); + if (sel != nil) + { + TVITEM item; + item.mask = TVIF_PARAM | TVIF_HANDLE; + item.hItem = sel; + if (TreeView_GetItem(treeCtrl, &item)) + { + plKeyInfo *info = (plKeyInfo*)item.lParam; + if (info != nil) + itemKey = info->fKey; + } + } + + if (!itemKey) + return; + + char fileName[MAX_PATH]; + sprintf(fileName, "%s.bin", itemKey->GetName()); + + OPENFILENAME openInfo; + memset( &openInfo, 0, sizeof( OPENFILENAME ) ); +// openInfo.hInstance = gInstance; +// openInfo.hwndOwner = hWnd; + openInfo.lStructSize = sizeof( OPENFILENAME ); + openInfo.lpstrFile = fileName; + openInfo.nMaxFile = sizeof( fileName ); + openInfo.lpstrFilter = "Binary Files\0*.bin\0All Files\0*.*\0"; +// openInfo.lpstrTitle = "Choose a pack index file to browse:"; +// openInfo.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; + + if (GetSaveFileName(&openInfo)) + { + plKeyImp* keyImp = (plKeyImp*)itemKey; + + if (keyImp->GetDataLen() <= 0) + return; + + plResManager* resMgr = (plResManager*)hsgResMgr::ResMgr(); + const plPageInfo& pageInfo = resMgr->FindPage(keyImp->GetUoid().GetLocation())->GetPageInfo(); + + plRegistryDataStream *stream = registry->OpenPageDataStream( keyImp->GetUoid().GetLocation(), false ); + if( stream == nil ) + return; + + hsStream *dataStream = stream->GetStream(); + UInt8 *buffer = TRACKED_NEW UInt8[ keyImp->GetDataLen() ]; + if( buffer != nil ) + { + dataStream->SetPosition( keyImp->GetStartPos() ); + dataStream->Read( keyImp->GetDataLen(), buffer ); + } + delete stream; + + if( buffer == nil ) + return; + + hsUNIXStream outStream; + outStream.Open(fileName, "wb"); + outStream.Write(keyImp->GetDataLen(), buffer); + outStream.Close(); + + delete [] buffer; + } + */ +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResTreeView.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResTreeView.h new file mode 100644 index 00000000..bae070c1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plResTreeView.h @@ -0,0 +1,59 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +#ifndef _plResTreeView_h +#define _plResTreeView_h + +class plResTreeView +{ + protected: + + static HWND fInfoDlg; + static bool fFilter; + + static void IFindNextObject( HWND tree ); + + public: + + static void FindObject( HWND tree ); + static void FindNextObject( HWND tree ); + + static void FillTreeViewFromRegistry( HWND hWnd ); + static void ClearTreeView( HWND hWnd ); + + static BOOL CALLBACK InfoDlgProc( HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam ); + + static void UpdateInfoDlg( HWND treeCtrl ); + + static void VerifyCurrentPage( HWND treeCtrl ); + + static void SelectionDblClicked( HWND treeCtrl ); + + static void FilterLoadables( bool filter, HWND treeCtrl ); + + static void SaveSelectedObject(HWND treeCtrl); +}; + +#endif //_plResTreeView_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plWinRegistryTools.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plWinRegistryTools.cpp new file mode 100644 index 00000000..98d3ca58 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plWinRegistryTools.cpp @@ -0,0 +1,162 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plWinRegistryTools +// Utility class for doing various usefull things in Win32 +// Written by Mathew Burrack +// 4.23.2002 +// +////////////////////////////////////////////////////////////////////////////// + +#include "hsTypes.h" +#include "plWinRegistryTools.h" +#include "hsWindows.h" + + +////////////////////////////////////////////////////////////////////////////// +//// Static Utility Functions //////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// ISetRegKey ////////////////////////////////////////////////////////////// +// Sets the given registry key to the given string value. If valueName = nil, +// sets the (default) value + +static hsBool ISetRegKey( const char *keyName, const char *value, const char *valueName = nil ) +{ + HKEY regKey; + DWORD result; + + + // Create the key (just opens if it already exists) + if( ::RegCreateKeyEx( HKEY_CLASSES_ROOT, keyName, 0, nil, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, + nil, ®Key, &result ) != ERROR_SUCCESS ) + { + hsStatusMessageF( "Warning: Registry database open failed for key '%s'.\n", keyName ); + return false; + } + + // Assign the "default" subkey value + LONG lResult = ::RegSetValueEx( regKey, valueName, 0, REG_SZ, (const BYTE *)value, ( lstrlen( value ) + 1 ) * sizeof( TCHAR ) ); + + if( ::RegCloseKey( regKey ) == ERROR_SUCCESS && lResult == ERROR_SUCCESS ) + return true; + + hsStatusMessageF( "Warning: Registry database update failed for key '%s'.\n", keyName ); + return false; +} + +////////////////////////////////////////////////////////////////////////////// +//// Public Utility Functions //////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +//// AssociateFileType /////////////////////////////////////////////////////// +// Associates a given file type in the Win32 registry with the given +// application. Also assigns a default icon if iconIndex != -1 +// +// To do this, we create a set of keys in the registry under CLASSES_ROOT that +// looks like this: +// fileTypeID (value = fileTypeName) +// | +// |--- DefaultIcon (value = path,index) [omit this one if you don't +// | want a default icon] +// | +// |--- shell +// | +// |--- open +// | +// |--- command (value = command line) +// + +hsBool plWinRegistryTools::AssociateFileType( const char *fileTypeID, const char *fileTypeName, const char *appPath, int iconIndex ) +{ + char keyName[ 512 ], keyValue[ 512 ]; + + + // Root key + if( !ISetRegKey( fileTypeID, fileTypeName ) ) + return false; + + // DefaultIcon key, if we want one + if( iconIndex != -1 ) + { + sprintf( keyName, "%s\\DefaultIcon", fileTypeID ); + sprintf( keyValue, "%s,%d", appPath, iconIndex ); + if( !ISetRegKey( keyName, keyValue ) ) + return false; + } + + // shell/open/command key + sprintf( keyName, "%s\\shell\\open\\command", fileTypeID ); + sprintf( keyValue, "\"%s\" \"%%1\"", appPath ); + if( !ISetRegKey( keyName, keyValue ) ) + return false; + + // Success! + return true; +} + +//// AssociateFileExtension ////////////////////////////////////////////////// +// Assigns a given file extension to a previously registered Win32 file type +// (using the above function) +// +// We do this by creating a key entry under CLASSES_ROOT of the following +// structure: +// +// fileExtension (value = fileTypeID) +// +// where fileExtension includes the leading . and fileTypeID is the same +// typeID registered with the above function + +hsBool plWinRegistryTools::AssociateFileExtension( const char *fileExtension, const char *fileTypeID ) +{ + return ISetRegKey( fileExtension, fileTypeID ); +} + +//// GetCurrentFileExtensionAssociation ////////////////////////////////////// +// Obtains the current fileTypeID associated with the given file extension, +// or a null string if it isn't yet associated. + +hsBool plWinRegistryTools::GetCurrentFileExtensionAssociation( const char *extension, char *buffer, int bufferLen ) +{ + long dataLen; + + + buffer[ 0 ] = 0; + dataLen = bufferLen; + + LONG retVal = ::RegQueryValue( HKEY_CLASSES_ROOT, extension, buffer, &dataLen ); + if( retVal != ERROR_SUCCESS ) + { + char msg[ 512 ]; + FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, 0, retVal, 0, msg, sizeof( msg ), nil ); + hsStatusMessageF( "Error querying registry key '%s' : %s\n", extension, msg ); + return false; + } + + return true; +} + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plWinRegistryTools.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plWinRegistryTools.h new file mode 100644 index 00000000..975894f1 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/plWinRegistryTools.h @@ -0,0 +1,52 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// +// plWinRegistryTools +// Utility class for doing various usefull things in Win32 +// Written by Mathew Burrack +// 4.23.2002 +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plWinRegistryTools_h +#define _plWinRegistryTools_h + +class plWinRegistryTools +{ + public: + + // Associates a given file type in the Win32 registry with the given application. Also assigns a default icon if iconIndex != -1 + static hsBool AssociateFileType( const char *fileTypeID, const char *fileTypeName, const char *appPath, int iconIndex = -1 ); + + // Assigns a given file extension to a previously registered Win32 file type (using the above function) + static hsBool AssociateFileExtension( const char *fileExtension, const char *fileTypeID ); + + // Obtains the current fileTypeID associated with the given file extension, or a null string if it isn't yet associated + static hsBool GetCurrentFileExtensionAssociation( const char *extension, char *buffer, int bufferLen ); +}; + +#endif //_plWinRegistryTools_h \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/dataicon.ico b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/dataicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7a551653a880a9c419e382bce0401e09167f8e67 GIT binary patch literal 2998 zcmcJRJxHWS7=T~zgq20Iv9S>0HWuNmr?SdnFI;17H|>7S@ihUrQsD}N0b#l;l8_W> ztQNM=D#baqHW3aiE{L#Nxv<6LdEf76{|vbl8J(H${eR}2ZC=Gp4^4)bD^a{p*X?sXfDJd?b#;~;sKHEOwcC=JQSa+#sF_j|{h z(_|U-86z!P$SuV4{(Bq>Ssg~_N>$4H$nVvml%GcD=S1`lD|uhd)U0JQk7W|#FyXJ! zefm(e>8W83{az(!^aQAWCFj}bSmj>V0-o}0KOd>0aCpk|tN~=@@Ra3}nL}llhB?&n z+TUz6&TsN@X%_M^vO~RE9i^NVl3g=5{ZNV1`zMiCUor`QHT-Y0g@1#oyT*RU+-Iht zyks_*t@Ik4HuDX0L6%r~jTdcuX6a%?%{y%yE06Y`U;KF?46w8U3(=u-=o~tyI@+!-9=+$mqxa}NdJPNc1Nwjv&~=>Y$JR*49eQXW}`gwxV#Dus3NyG4!Eyn6y_8W-cA3 z?1h7x>kQNONAyKB1uVmi9q=-`%rIwHv}$sO8T)kfynUb!bq3Bj6F)Eqt8IRrS$lIL zgC^>X@1IL~)LX5iiwyPqQ`qsK`vu0F1MzyP+}`sJX4e`8r^YKoT|(=ddBS1Btb7C) zF4aNC(Z;U?#V8IkX}0*vJZRqQYgE_Phmx<1Kj9!#<{7)9Ob_vLOL8k4>0|=A9WZ$|!+OOBy*?KiXUXWskf%}7Y3 zBv)i{F%a|oc}3(?=EX&$iTqd=@h3z+<9!kN94W~pqew>V`=1Oao-7+pjJ(S3?k=-& zX!|c(n>U0%V=}`Bdz@8uk;_pEZT`$qvM)2i>c~(Es@3YuL?9#GDJ{)JGeI_W@6c|<)mm1dycFG=sC44r@zYWGW5jmG0l*#%~gqi1#N9tpEEl?*3S z6gk()V2YmW1RPbyb+%PbFWmNREc{=dODcEZ@^c~+`ni^9sAduCSYB zIrbcTj*?@~u`@}5y}({zFEA9?HM&OEg{>ShMkJ3&6tO7cb8E{pL@-1!L@-1!SR@!C z7$O)V7$O)fCJYe_5eyLw5eya`h6siTh6siThDc+BBZ4C`9Go0fmO?QqSQIP@7Q1Cw z6vY*V6iF2nLt($Mict%x zGx|ff?KRzjirP7Gw$;?mVah3*Qy_;ar*@7rr*=;5oZ9v{Y;Ek+ kN-yBg+{hZ$9 zFyt`g^uZh(U9r2z!pvxnkscJ(D_|?&D5z(T)^N~+0*=CPurYhwEGl3rU@3qoC}^($ zECmY-f(i@;Ec_$n;YA7&HtT1`UIT!CogA3K%pD8ioP}(jdgc zpkdH3Xc$P1V244&pkdGeXzCXv(=5~kDRL-u1&6k|O^lLHLCF@dTA5ZY=waX0zGAH(Qyjg_UtX*_+FC*YiE!54zo2814<@(5cTxxg=uGem_te^Tr@3erQBtfHDcNac(-Gz4hY<)PFjCRPb zo6AkN+OE4Tcg$a*BXo&fm+7`n|+40SBJ(lVHVb%>~ngR_(yL*`nikwQz5C<}5+0)#=W8 z7uUy&$&iPB*CyClIbAn4nSi~TduReS*2f%YuLg2&STUf3^%2*ZYcwa@Fw7ayymQ$z q(r6?Z<|=z(_+Gy`2;cYJmtpw6<>xl<@ci{dyx1QLE2k literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/icon2.ico b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/icon2.ico new file mode 100644 index 0000000000000000000000000000000000000000..7995ecf59a46fb5a2b3e3c130d360a570dd6d380 GIT binary patch literal 2998 zcmchZF=%8*3_#_(h1km{i5pjj+qejEJ(a5q?hV(tb|+F^=O&P?oZ^~5z%bnvNl1z` z#_oTWB35lTapmHI3Ea6`NHKZR-~ZS9zYR8olXqsmq>=Q#W_Gd2iae9a ztdogrBEPPReEMAEE1oBeuNf;cViuXQ_kNk;#50{X3p0;W*EOp8|xL`(igVSX?l`$25|#8(E; zLI=NE@b>e?`&ixW=T%9t(0QydU=1;Mg|s@^@9MOtreCd)m*Ta4^Say5?pnW}Z-`K< zTQ}S;b?a{Rd9<{8n|^tniM;p-<0Un|W0aY!FrG6$VSHAW?>UzrnU0nj7upmj8h1Jt zW*+07*MHUW;m==WG#bh3>Z(*#CF|?!vazuto12@mwY4SN+uO3gzb|`xd$PN`D?2+o zGX1v5t5-#SlnW{UnEY|^UM}9flOKNjokEGBjLhIPT}@00Tf&yy#&^iKD0byRu`71P z?qL~w#-33!_KclH47*`B?0%CBdqFSg#hOygiR6hyiJ-*yHp(+47!nK#h6F<(!H{4` zFeDff41ozlf+4|>U`Q|oIt&Sh1Ve%$!H^0zI1(JmIrxo$Dkzj$VNqBVmUv}Yl;TPu zC8@#^ZNRBKC@cy~v;_`@L*Y<36pm;W914TNpfGsr6b^+!VX*kcvZjU9Y5vfoz1|(D zsGZptt)_N{DN{64Aj6cYopEMrXKH6^$8kh$?9|WH&(!xL%GA&FCc}_n$n?RFjjqJo zV_{|XW26U0J%i2QFzUt8ItM*4ILtYCu{dsm3>JgMfG`Ti6@bMEG&b>#W?=X0;w+qr z!D8NzVDa1Fr+^KH0z-kJz))Z)FvN9&!C)vb6c`K!(lFQ$LxG{dP+%Z613L@_h5|zY zpitjPRtPL?QgSG|!cii(?@{?eewKrS135fAl%t~~IX*s?^Ye2#IXRKb%S*Ysx{~Yb zYq`0(k=xr_nayVM;ol-Zy?x8~cuIHuJKziXLi4gYv|^Q2K5`juFSyi;7&i6zQ47%>r?&B z+k14=#eRG=E&!Si-RqC=XycRn*S!Ax4N>~}<7k@65r_V;IvY;rL45cfu#tQG9v05# zGWYH$F5FG>yRLX%BQ7d?hB5J{t}ev=o#5}e$31`F-SZFJJ^ylTr&zly3#>;xeT4r} MUQpxZL;t7z2OC!FyZ`_I literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/indexico.ico b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/indexico.ico new file mode 100644 index 0000000000000000000000000000000000000000..e8358fde0d4dd2418fd37d16c5b6ac1b0506f3d2 GIT binary patch literal 2998 zcmchZziS*v6vw}^528Cn5;ZD>z!w!Es;5%rpkClcwYn*7cWMH;N^xZt4g}M=NJ3Ji z(O+;?ifC%7i7FKiA`q)PNHO`mx3_zDdos2lc(lh*V|!{L{*_SYln zYv$q5BGQ|ZH#`sN-_qCAW0Xet-rq(Tv5ZFb#K@y2lL@mKihos#J<<7?%7)dJ z+E&|IF@2=0s^3qQQsdvRl2TRmTUkkcrB-eeyeGc7dYjq1GJ%%tAC8AVh%FB|381+Q zet*u}jpzGGRqM}I2{4y=?4v;IEt$z%BNKg5raTq(Y8r7#UcLI^W$TYO^30)*RlS;- zXM3+2b0atAMsC6{&r|8iC+JVf@jbmvwMKtT|BU`cS-$67e%h$F%s7`u7?HTsJ~8s> z_k8l_MDPCiS-oCQ>+9><*x1nK=BBo`wzRXequt$I?d|R9=;%m?hle^iIMDw7zDC~_ zJ$qI()}_kdS-)TYqszB%RsH%Ki2^;+?Id$826O>kKo?BMJ7g{qedHmckLV+MTb9r# z^mbbk`h=cI8GS~d(PtDHeL*hBC41(;7ziE+6j&5^Z#_LjfFZyTUk^Wu{vdmKtvXPV zJK^W0CU=4Tl3Jip%qlcluP+%wk z6!K?+6&4npA~7Ozgrj(D^NIROKk4}RSSKeZIz2tr+1Z&cE-rL_ey*#lD_vh->*nT0 zx3{;tySvkLI@P;>i++6bhWB_xb?qIniFhtqToa21AJv}U(-s$Qzg}t2m+a$id;LKB zU@*3?g(2{G++B~#zUJVbt9G;5QlD4W{||gM-7L^b{u;1Nvtl=Jqo1{|z0o)GL+$nM zZFOD6?s_kr1Jo6|*Y3-`j1R6~z5Uq^5xVjHXzIZbSM9DfHjIob>)mdEb-TxI%iLIB z=Fa`Zgt-pBX!7T6)_G=&Fkrolb2qcM4f_l8UT7VA*F0FZub4abPH^&UFSdoeAAbA< O|52WhwP;=6tzzL#Bx;U*}gkz+J}cHN8^)P ze;kkX@$jg&k1SXpPpbGMEu{a{K5W{m1@^2SbO)B9itij0eQVFmM(iBSYzuDKV)Z)x zTKD{Pw~Aj}kFHMp=k<#EK@3Sv$oSnsrCUAh>4~8|IP9q`Lo>CgPCe=AHHnD_eihFMhv};s;sT8$;QTpY;JDK*4CEn?d{3#?yl_Y?8x@^wv4`2dGki)RIWtt zv;2DXr(AvfDBu133xxtRG7~eGX22Kl1$@DLyeW1h;*V@1{)j)~x3Gji;ZGO|f5Oj2 zGX9J|Zxzt=TIF)5)283L?7(2(G|aYJX~4rG17xf zy$oB1BU8^Gt#QzU3`aH&ZpBOL0v?M#%fEYaWqHk1>HVkicEM-{uD@@Mw@-BcDQzzA|F7QDp7QBU zudR3!7=D10^LiO<8OkK{`jqXyVqC1>cd7$zy~DWI&OL)9Exi)TV=cW-YycSZ@vi#~ z{*!)s%Ka^jH@EhtI@|gDW$(&xJKEAMl=hOuy1ZDoz-f_oypw%euT%FNVm^MB+96u? zXS(lNdh`Cum)ShkSTCu|)RnVwo$6)k9`SUeIZUtaPtm`5D)-Q98GT+xZ;W0`p3~=& Y1G3&6O|XC6e}?~2UQpxJr~Xg*8=HY7oB#j- literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/mergedin.ico b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/mergedin.ico new file mode 100644 index 0000000000000000000000000000000000000000..f20a2190b5a88a2c7016b089f3e0254081e6ddda GIT binary patch literal 2998 zcmchZKTIP>6o(&omW)>@4hY6x!f)HckqANL(6E*?F#AVU~K{6=7;=cWptQS31e2max=$d~;T}`~9=Q=~<;e zo}TJse^A*23)aW8GJc@BbQkS@-B!)9=k=gFwhU!_`#A4fd*(J``*?0!aLpF0*X~w& z%1?L8_|^63@~oe!US4g)kko|uZ;ngd>gE2;Y{k$vPV;YPvJB1Cyp&73l%=0WWma|4 z!&KzOCzvm(F=Xmg)|k(kZDv;&&pC&mF>0mTipvNQH)lHCxbm3J`S_2q@bFL$4i030e_!_Y_GI*0<<%>d@8w!_66Lq+ zk8=ItgM9bPuM~1j+oiyah3W7)d=8&8A1B3@c>K1b9>2%$@mpBHAMgi^fIr~pCJ}$c zAMr;V(M}w;Vrxv59al$kM@7#s`^hJ*xzgTcYzU~n)bOc)#t4h9E< zgCU{A;9zhtI2arZPVvFv;BdylZv<3{!gKYocvw6v>6Kye6!#SJB=xYQHXzyw_po?a zQd{8gaCkU893GC;DmXk09tIDCwa&xgVel}-^otdX7E-79!%pqB?m$KDz`E3GY6qAC zMFRx_Oo7?~F;F{DJ5W1qN2-mV`hog^`nE-Z`hngA7y=A|KG?R=mGt&_xO25_qz93D z5w-|Nq+Z%u2t=e{Is&jn0waqejtC1MCVAMC2urknBo;dj zwhQ=RP#6>jg+XCZ7}9ZqA;O?AC=3w>(lA*MgTkONC=8@#;)g+DP#6>drG6xt5~wWl z#q=vL75dIXRK@^K-enx{`~F3%R+uk=xr_xx2fQ`}=!&czBTM zbSn8@DnGn?$M<+dckMf1H}WOR@|vlV{ipGE#i$2%E*szB*_t7n=ipKNw- z#GAlK3s||V7r~aHNV2TY*d`_8YJJi!7c})YW1{T|gCq^T6pCdHy-I8V7|ZcVPcQgS z`uq31-^zG%Yc|t)T+Uzgt_ZiK4c$U%FG;M+t91*U6=}vNvZwVbb(SEOlufT>^ko^nHnN7iq%S3h Xq*EUYWdCM-g8xxoP~+uO|EK&7OVTU_ literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/plResBrowser.rc b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/plResBrowser.rc new file mode 100644 index 00000000..929bdef0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/plResBrowser.rc @@ -0,0 +1,214 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APPICON ICON "icon1.ico" +IDI_INDEXICON ICON "icon2.ico" +IDI_DATAICON ICON "indexico.ico" +IDI_PATCHICON ICON "dataicon.ico" +IDI_MERGEDDATAICON ICON "mergedda.ico" +IDI_MERGEDINDEXICON ICON "mergedin.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_APPMENU MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Open...", ID_FILE_OPEN + MENUITEM "Open &Directory...", ID_FILE_OPENDIRECTORY + MENUITEM SEPARATOR + MENUITEM "Save Selected Object", ID_FILE_SAVESELECTED + MENUITEM SEPARATOR + MENUITEM "Find Object...\tCtrl+F", ID_FILE_FINDOBJECT + MENUITEM "Find Next\tF3", ID_FILE_FINDNEXT + MENUITEM "Show Only Loadable", ID_FILE_ONLYLOAD + MENUITEM SEPARATOR + MENUITEM "About...", ID_FILE_ABOUT + MENUITEM "Exit", ID_FILE_EXIT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_INFODLG DIALOGEX 0, 0, 186, 281 +STYLE DS_SETFONT | WS_CHILD | WS_VISIBLE +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + GROUPBOX "Object Information",IDC_STATIC,4,168,178,67 + RTEXT "Name:",IDC_STATIC,17,180,22,8 + LTEXT "",IDC_NAME,42,179,129,10,SS_SUNKEN | NOT WS_GROUP + GROUPBOX "Page Information",IDC_STATIC,4,2,178,163 + RTEXT "Age:",IDC_STATIC,45,14,16,8 + LTEXT "",IDC_AGE,63,14,107,10,SS_SUNKEN | NOT WS_GROUP + RTEXT "Page:",IDC_STATIC,41,28,20,8 + LTEXT "",IDC_PAGE,63,28,107,10,SS_SUNKEN | NOT WS_GROUP + RTEXT "Location:",IDC_STATIC,31,43,30,8 + LTEXT "",IDC_LOCATION,64,43,74,10,SS_SUNKEN | NOT WS_GROUP + CONTROL "Reserved",IDC_RESERVED,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,64,66,10,10 + RTEXT "Class:",IDC_STATIC,19,195,20,8 + LTEXT "",IDC_CLASS,42,194,129,10,SS_SUNKEN | NOT WS_GROUP + RTEXT "Start Pos:",IDC_STARTPOS_LABEL,7,221,32,8 + LTEXT "",IDC_STARTPOS,42,220,55,10,SS_SUNKEN | NOT WS_GROUP + RTEXT "Size:",IDC_SIZE_LABEL,103,221,17,8 + LTEXT "",IDC_LENGTH,124,220,47,10,SS_SUNKEN | NOT WS_GROUP + LTEXT "Reserved",IDC_STATIC,76,67,31,8 + PUSHBUTTON "Find",ID_FILE_FINDOBJECT,4,259,50,14 + PUSHBUTTON "Find Next",ID_FILE_FINDNEXT,58,259,50,14 + RTEXT "Data Version:",IDC_STATIC,17,79,44,8 + LTEXT "",IDC_DATAVERSION,64,79,48,10,SS_SUNKEN | NOT WS_GROUP + RTEXT "Checksum:",IDC_STATIC,25,139,36,8 + LTEXT "",IDC_IDXCHECKSUM,64,139,53,10,SS_SUNKEN | NOT WS_GROUP + PUSHBUTTON "Verify Page",ID_FILE_VERIFYPAGE,130,259,50,14 + RTEXT "Checksum type:",IDC_STATIC,9,151,52,8 + LTEXT "",IDC_CHECKSUMTYPE,64,152,89,10,SS_SUNKEN | NOT + WS_GROUP + CONTROL "View values as hex",IDC_SHOWASHEX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,105,241,77,10 + CONTROL "",IDC_LOCAL_ONLY,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,64,57,11,8 + LTEXT "Local Only",IDC_STATIC,76,57,34,8 + CONTROL "",IDC_BUILTIN,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,116,67,11,8 + CONTROL "",IDC_VOLATILE,"Button",BS_AUTOCHECKBOX | WS_DISABLED | + WS_TABSTOP,116,57,11,8 + LTEXT "Built-In",IDC_STATIC,129,67,22,8 + LTEXT "Volatile",IDC_STATIC,129,57,24,8 +END + +IDD_ABOUT DIALOG 0, 0, 247, 57 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About plResBrowser" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,190,7,50,14 + ICON IDI_APPICON,IDC_STATIC,11,8,21,20 + LTEXT "plResBrowser\nA simple Plasma 2.0 packfile browsing utility\nCopyright (C) 2002 Cyan Worlds, Inc.\n\nWho needs log files?", + IDC_STATIC,40,7,140,43 +END + +IDD_FINDOBJ DIALOG 0, 0, 228, 46 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Find Object" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_SEARCHSTRING,13,19,146,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "Find",IDOK,171,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,171,24,50,14 + GROUPBOX "Object Name",IDC_STATIC,7,7,157,32 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 240 + TOPMARGIN, 7 + BOTTOMMARGIN, 50 + END + + IDD_FINDOBJ, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 221 + TOPMARGIN, 7 + BOTTOMMARGIN, 39 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCELERATOR1 ACCELERATORS +BEGIN + "F", ID_FILE_FINDOBJECT, VIRTKEY, CONTROL, NOINVERT + VK_F3, ID_FILE_FINDNEXT, VIRTKEY, NOINVERT +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/resource.h b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/resource.h new file mode 100644 index 00000000..23a1cbf9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Tools/plResBrowser/res/resource.h @@ -0,0 +1,67 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by plResBrowser.rc +// +#define IDI_ICON1 101 +#define IDI_APPICON 101 +#define IDR_APPMENU 102 +#define IDD_INFODLG 103 +#define IDD_ABOUT 104 +#define IDR_ACCELERATOR1 106 +#define IDD_FINDOBJ 107 +#define IDI_INDEXICON 108 +#define IDI_DATAICON 109 +#define IDI_PATCHICON 110 +#define IDI_MERGEDDATAICON 111 +#define IDI_MERGEDINDEXICON 112 +#define IDC_NAME 1000 +#define IDC_AGE 1001 +#define IDC_CHAPTER 1002 +#define IDC_PAGE 1003 +#define IDC_LOCATION 1004 +#define IDC_RESERVED 1005 +#define IDC_CLASS 1006 +#define IDC_STARTPOS 1007 +#define IDC_LENGTH 1008 +#define IDC_CANLOAD 1009 +#define IDC_STARTPOS_LABEL 1010 +#define IDC_SIZE_LABEL 1011 +#define IDC_INTERLEAVED 1012 +#define IDC_RELVERSION 1013 +#define IDC_SEARCHSTRING 1014 +#define IDC_DATAVERSION 1014 +#define IDC_IDXCHECKSUM 1015 +#define IDC_DATACHECKSUM 1016 +#define IDC_CHECKSUMTYPE 1017 +#define IDC_SHOWASHEX 1018 +#define IDC_SCROLLBAR 1019 +#define IDC_PARTIALPATCH 1019 +#define IDC_FRAME 1020 +#define IDC_HEADERPATCH 1020 +#define IDC_COPIED 1021 +#define IDC_NEW 1022 +#define IDC_ZOOM 1023 +#define IDC_ZOOMSLIDER 1024 +#define IDC_SEGINFO 1025 +#define IDC_LOCAL_ONLY 1025 +#define IDC_BUILTIN 1026 +#define IDC_VOLATILE 1027 +#define ID_FILE_EXIT 40002 +#define ID_FILE_OPENDIRECTORY 40003 +#define ID_FILE_ABOUT 40004 +#define ID_FILE_FINDOBJECT 40005 +#define ID_FILE_FINDNEXT 40006 +#define ID_FILE_VERIFYPAGE 40007 +#define ID_FILE_ONLYLOAD 40008 +#define ID_FILE_SAVESELECTED 40009 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 110 +#define _APS_NEXT_COMMAND_VALUE 40010 +#define _APS_NEXT_CONTROL_VALUE 1026 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/IJL/ijlnotes.htm b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/IJL/ijlnotes.htm new file mode 100644 index 00000000..ff3557b8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/IJL/ijlnotes.htm @@ -0,0 +1,157 @@ + + + + +Intel® JPEG Library Release Notes + + + +

    Intel® JPEG Library v1.51

    + +

    Product Release Notes

    +

    Contents

    +
    + +

    Intel® JPEG Library 1.51

    +

    The Intel® JPEG Library (IJL) is a software library for application developers that provides high performance JPEG encoding and decoding of full color and grayscale still +images.

    +

    The IJL was designed for use on Intel® architecture platforms and has been tuned for speed and efficient memory usage. Additionally, the IJL was developed to take advantage of MMX™, +Streaming SIMD Extensions (SSE), and SSE-2 technology if +present.

    +

    The IJL provides an easy-to-use programming interface without sacrificing low-level JPEG control to advanced developers. The IJL also includes a substantial amount of functionality that is not included in the ISO JPEG standard. This added functionality is typically necessary when working with JPEG images, and includes pre-processing and post-processing options like sampling and color space conversions.

    +

    Additional information on this software as well as other libraries is +available at the Intel Performance +Libraries web site.
    +

    +

    System Requirements

    + +

    The Intel JPEG Library requires the following software and hardware:

    +

    Recommended hardware: A Pentium® II,  Pentium III, or Pentium 4 processor-based PC with memory appropriate to the image sizes needed.

    +

    Software Requirements: Microsoft* Windows* 95, 98, ME*, 2000, or Microsoft Windows NT* 3.51 or 4.0.

    +

    Compiler Support

    +

    Intel® C++ Compiler, Borland* C++ 5, or Microsoft* Visual C++* 4.2 or +later. Other compatible compilers have not been tested extensively but might work.

    +

    License Definitions

    + +

    Please see the license file ijllic.htm +for the license definitions and restrictions on the library.

    +

    Installation

    + +

    You can install the IJL package by downloading and running the appropriate self-extracting installation files from the Web site. +Check the Intel JPEG +Library site for updates, when available.

    +

    Library Use

    To get started using the library, the following steps are required:

    + +
      +
    • Include the file "ijl.h" in your program.
    • +
    • Call the IJL interface.
    • +
    • Link to the file ijl.lib in the lib directory.
    • +
    • Put the DLL ijl15.dll in your path before executing your program.
    + +

    What’s New in the Intel JPEG Library Version 1.51

    + +

    New Features:

    + +
      +
    • Microsoft* Visual Basic* example extended to demonstrate how to work with + a memory buffer
    • +
    +

    Fixed Bugs:

    + +
      +
    • Multi-thread safety restored
    • +
    • Now properly auto-detects processor type
    • +
    • Now writing JPEG comments properly
    • +
    • No longer crashing when decoding corrupted JPEG images
    • +
    • Now encoding/decoding progressive JPEG images properly
    • +
    • Now decoding a Region of Interest (ROI) from progressive JPEG images
    • +
    • Rounding error fixed in the Microsoft* Visual Basic* example
    • +
    • JPGView example now stores BMP files in top-down order instead of + bottom-up
    • +
    +

    History

    +

    New in Version 1.5:

    + +
      +
    • Optimizations for the Pentium III and Pentium 4 processors
    • +
    • Support for Microsoft* Visual Basic*
    • +
    • A more accurate DCT
    • +
    • Encoding of progressive images
    • +
    • Reading of embedded thumbnails
    • +
    • Reading/Writing of JPEG comments
    • +
    • Reading/Writing of DPI information
    • +
    • Raw quantization and Huffman tables can be read at decode time
    • +
    • YCbCr (4:2:2) supported as input to encoder and output from decoder
    • +
    • Raw DCT coefficients and raw sampled data supported as input to decoder + and output from decoder
    • +
    +

    Bugs fixed in 1.5: 

    +
      +
    • JPG files are properly truncated of extra data before writing
    • +
    • JPGSizeBytes no longer ignored on ijlWrite
    • +
    • No dynamic buffer overruns if output buffer is too small
    • +
    • No longer misinterpreting coefficients when first AC coefficient is -1 and + first DC coefficient is negative
    • +
    • A single-line distortion problem was fixed
    • +
    • Some  image corruptions at encode time was fixed
    • +
    • Path searching is correct when path contains localized characters/ UNICODE
    • +
    • IJL now recognizes images encoded with a ZORAN* chip
    • +
    +

    New in Version 1.1:

    + + + +

    Known Limitations In Version 1.51

    + +
      +
    • PLSInfo.exe and the Performance Libraries dispatching mechanism are not supported.
    • +
    + +

    Technical Support and Feedback

    +

    All interactive support for the Intel JPEG Library is handled through the Intel® Performance +Libraries section of the Intel® Premier Support web-based support system. In order to access support for this product, you will need an account on Intel Premier Support. Register for an account at the Intel Premier Support Registration Page. 
    +Once you have an account, you can login to the Intel Premier Support Web site. 

    +If you have forgotten your password, please contact quad.support@intel.com. If you have questions pertaining to the +auto accounts registration process, please contact pto.quad@intel.com. For all support questions, please submit an issue at +https://premier.intel.com/.

    +To submit an issue via the Intel Premier Support website: +

      +
    • +Go to https://premier.intel.com/ 
    • +
    • Type in your Premier Support Login and Password.
      +Click the "Submit" button.
    • +
    • Read the Confidentiality Statement and click the "I Accept" button.
    • +
    • Click on the "Submit Issue" link in the left navigation bar.
    • +
    • +Choose "Initiatives, technologies & tools" from the "Product Type" drop-down list.
    • +
    • +Choose "Intel® Performance Libraries" from the "Product Name" drop-down list.
    • +
    • Enter your question and complete the fields in the windows that follow to successfully submit the issue. 
    • +
    +

    +Contact us with your suggestions or problem reports using your Intel Premier Support account. A technical support engineer will respond within one (1) Intel business day. +

    + +

    Intel, the Intel logo, and Pentium are +registered trademarks of Intel Corporation.
    +MMX is a trademark of Intel Corporation.
    +*Other names and brands may be claimed as the property of others.

    +

    Copyright 2001, Intel Corporation, All Rights Reserved.

    +
    + + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/MySQL/ReadMe.txt b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/MySQL/ReadMe.txt new file mode 100644 index 00000000..065f42ce --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/MySQL/ReadMe.txt @@ -0,0 +1 @@ +Not sure this is needed. It was probably for the original servers (before MOUL). diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/PhysX/release_notes.html b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/PhysX/release_notes.html new file mode 100644 index 00000000..fe04e6cd --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/PhysX/release_notes.html @@ -0,0 +1,1042 @@ + + + + Release Notes - AGEIA PhysX SDK 2.6 + + + +
    +

    +
    +

    Release Notes - AGEIA PhysX SDK 2.6.0

    +

    28th September 2006

    +

     

    +

    What's New In AGEIA PhysX 2.6

    +
    +

    Hardware/Software Rigid Bodies

    +
      +
    • There is a new joint projection mode, NX_JPM_LINEAR_MINDIST, that uses only + linear projection for improved performance.
    • +
    • A new stack overflow protection scheme is in place, where too-large stack allocations are replaced by heap allocations when needed. If heap allocations are suddenly causing a slowdown, the threshold for this fallback can be set using NxFoundationSDK::setAllocaThreshold().
    • +
    • Fixed: Raycasts missing on the exact boundary of a heightfield.
    • +
    • Fixed: Filtering does not work correctly with HSM.
    • +
    • Fixed (SW): DistanceJoint will not work properly when attachment points coincide in worldspace.
    • +
    • Fixed: No contacts generated in Capsule-Capsule collision when capsule segments intersect.
    • +
    • Fixed: No contacts generated in capsule-convex collision when capsule penetrates convex.
    • +
    • Fixed: CCD collision response in FW scenes.
    • +
    +

    Hardware Fluids

    +
      +
    • Fluid collision improvements including support for a + collision distance between fluid particles and rigid + body geometry which is maintained during the + simulation + (NxFluidDesc::collisionDistanceMutliplier).
    • +
    • A new method, NxScene::cookFluidMeshHotspot, allows + for preparing static mesh, height field and convex + shapes for fluid collision, preventing on-the-fly + cooking.
    • +
    • More information about particles including particle + flags, IDs and notifications for particle + creations/deletions.
    • +
    • New runtime switches for degrading simulation + fidelity on heavy system load.
    • +
    • Fluids emitters are now attached to shapes not actors.
    • +
    +

    Hardware/Software Cloth

    +
      +
    • Cloth metal - a combination of deformable cloth and + nondeformable rigid body, useful for sheet metal and + similar objects. This mode can be activated by + attaching a piece of cloth to a rigid body via the + NxCloth::attachToCore method.
    • +
    • Cloth self collision is now implemented and can be activated by means of + NxClothFlag::NX_CLF_SELFCOLLISION.
    • +
    +

    Hardware Scene Manager(HSM)

    +
      +
    • The HSM now uses Compartments, allowing the utilization of multiple PhysX cards + in future versions.
    • +
    • The interface of the HSM has been completely revised.
    • +
    +

    Performance

    +
      +
    • The performance of some scenes with many kinematic actors and/or a lot of broadphase activity has been improved.
    • +
    • Fluid collision performance improved, especially for dynamic convex shapes.
    • +
    +

    Serialization

    +
      +
    • A new serialization library has been introduced called NxuStream
    • +
    • This is a source code library with an easy to use interface.
    • +
    • SampleAssetExport and SampleSceneExport demonstrate how to use this library.
    • +
    • It is now possible to serialize to and from an XML file format, as a high speed binary file, and in COLLADA 1.4.1 physics.
    • +
    +

    Documentation

    +
      +
    • Many small corrections and fixes for the user guide and API + reference.
    • +
    • Information in the User's Guide pertaining to hardware has been concentrated in one place.
    • +
    • User guide documentation for: +
        +
      • Compartments
      • +
      • Training programs
      • +
      • Cloth Metal
      • +
      +
    • +
    +

    Installer

    +
      +
    • The client installers have been replaced by a single client installer. This installer is based on the MSI/WISE installer which was distributed in the past. There is no longer an NSIS based installer.
    • +
    +
    +

    Supported Platforms

    +
    +

    Runtime

    +
      +
    • Software Runtime for Microsoft Windows XP (32bit and 64bit + editions; Pentium III / Athlon XP and higher only)
    • +
    • Software Runtime for Microsoft Windows Vista (32bit + edition; Pentium III / Athlon XP and higher only)
    • +
    • AGEIA PhysX Hardware Runtime for Microsoft Windows XP + (32bit and 64bit editions)
    • +
    • AGEIA PhysX Hardware Runtime for Microsoft Windows Vista + (32bit and 64bit editions)
    • +
    • Microsoft XBox360
    • +
    • Sony Play Station 3 (provided by Sony)
    • +
    • Software Runtime for SuSE Enterprise Linux 10.0 (alpha release; 32bit and 64bit + editions; Pentium III / Athlon XP and higher only)
    • +
    • Software Runtime for Red Hat Enterprise Linux WS 4.0 (alpha release; 32bit and 64bit + editions; Pentium III / Athlon XP and higher only)
    • + +
    +

    Development

    +
      +
    • Microsoft Windows XP
    • +
    • Microsoft Visual Studio .NET 2003
    • +
    • Microsoft Visual Studio .NET 2005
    • +
    • Microsoft XBox360 development tools
    • +
    • Sony Playstation 3 development tools
    • +
    • SuSE Enterprise Linux 10.0 (alpha release)
    • +
    • Red Hat Enterprise Linux 4.0 WS (alpha release)
      +
    • +
    +
    +


    + +Known Issues And Limitations

    +
    +

    Hardware/Software Rigid Bodies

    +
      +
    • Objects may not get woken if they lose contact but their bounds still overlap.
    • +
    • Objects may have difficulty falling asleep if they have alternating bounds overlaps with non sleeping bodies.
    • +
    • Joint projection may cause instability in the D6 joint.
    • +
    • joints may pull off each other when the mass relationship is not properly set up
    • +
    • joints' behavior may differ when actors are in very small size.
    • +
    +

    Hardware Fluids

    +
      +
    • Dynamic fluid - mesh shape collision isn't supported anymore.
    • +
    • Dynamic convex shapes are limited to 64 planes for + fluid collision. If a convex consists of more than 64 + planes, only the first 64 planes are taken into consideration for + collision. If this is the case it may happen that particles collide + with the axis aligned bounding box of the convex shape.
    • +
    • If fluid particles are located more than + 32768*NxFluidDesc::kernelRadiusMultiplier/NxFluidDesc::restParticlesPerMeter + units far from the world origin, collision and SPH computations will fail. +
    • +
    • If particles get trapped between two dynamic rigid + bodies, they jitter within the penetration depth of + the rigid bodies.
    • +
    • Switching from no-particle-interaction mode to mixed mode or even sph mode can cause instabilities in the simulation.
    • +
    • Fluids have a limit on the packet number. It's 1024.
    • +
    • Max 4096 new Particles per time step can be added by the function of NxFluid::addParticles().
    • +
    • 16 Mbytes are reserved on the PPUs external memory + for cooked static collision geometry for fluids. If + this is exceeded during runtime collision geometry is + dropped and a debug message + "Hardware heap ran out of memory" is generated. The call + NxScene::cookFluidMeshHotspot can't cause this, since + it only cooks data and doesn't transfer the data to + the PPUs external memory yet.
    • +
    • If more than 8192 static triangles are located in the range + of a fluid packet, triangles will be dropped. + A debug message is generated in this case. +
    • +
    +

    Hardware/Software Cloth

    +
      +
    • A self-collision enabled cloth can still intersect itself due to high relative + velocities of cloth particles, small cloth thickness, multiple collisions with + other objects or user interaction.
    • +
    • Untangling is not yet supported.
    • +
    • Collision handling between different pieces of cloth is not yet supported.
    • +
    • Supported core bodies for cloth metal are boxes, + spheres, capsules and compounds of spheres.
    • +
    • The metal deformation is penetration based. This + means that if the rigid body solver leaves a big + penetration, the result looks more dramatic. Thus, in + some range, the deformation is not predictable. + However, the cloth is moved towards the colliding + body by the penetrationDepth parameter at an impact + so the penetration just varies around this value.
    • +
    • Attaching a low density actor to cloth in NX_CLOTH_ATTACHMENT_TWOWAY mode can cause unstable simulation.
    • +
    • When the density of object collision with cloth is too high, the objects can easily penetrate cloth, even in very low speed.
    • +
    • In some cases a cloth can slip through a concave corner or a valley edge during mesh or terrain collisions. (Workaround: Increase the cloth's thickness or bending stiffness)
    • +
    +

    Hardware Scene Manager(HSM)

    +
      +
    • dynamic triangle meshes are not supported anymore.
    • +
    • Objects in compartments are not visualized.
    • +
    • Hardware objects from different hardware compartments do not interact (i.e., cloth and rigid).
    • +
    +

    Serialization

    +
      +
    • COLLADA does not retain all of the physics data in our SDK. It will not retain heightfields, wheel shapes, CCD information, cloth, fluids, or a number of other items that are not in the COLLADA 1.4.1 specification. +
    • +
    +

    Installers

    +
      +
    • The AGEIA PhysX SDK must be installed to run samples as they + rely on DLLs contained within the main SDK installer.
    • +
    +

    Windows Device Driver

    +
      +
    • No known issues.
    • +
    +

    Tools

    +
      +
    • There is currently no support for content creation tools in the Linux SDK release.
    • +
    +
    + +

    Release Notes - AGEIA PhysX SDK +2.5.1

    + +

    17th August 2006

    + +

     

    + +

    What's New In AGEIA PhysX 2.5.1

    + +
    +

    General

    +
      +
    • The NxPhysicsSDK object is now reference counted, and + thus all NxCreatePhysicsSDK()/NxCreatePhysicsSDKWithID() + calls should be matched with an NxReleasePhysicsSDK() + call.
    • +
    +

    Hardware/Software Rigid Bodies

    +
      +
    • The following HW Rigid Body optimizations have been + introduced.
        +
      • Improved mesh cooking
      • +
      • Optimized narrow phase collision detection
      • +
      • Optimized broad phase collision detection
      • +
      • Optimized dynamics solver
      • +
      +

      These optimizations are expected to improve + performance with no significant change to the + behavior of the simulation.

      +
    • +
    +

    Windows Device Driver

    +
      +
    • Microsoft Windows XP 32bit and 64bit now supported. + Allows for 32bit software which use AGEIA PhysX + hardware to run on Microsoft Windows XP 64bit.
    • +
    +
    + +

    Supported Platforms

    + +
    +

    Runtime

    +
      +
    • Software Runtime for Microsoft Windows XP (32bit and 64bit + editions)
    • +
    • Software Runtime for Microsoft Windows Vista (32bit + edition)
    • +
    • AGEIA PhysX Hardware Runtime for Microsoft Windows XP + (32bit and 64bit editions)
    • +
    • AGEIA PhysX Hardware Runtime for Microsoft Windows Vista + (32bit edition)
    • +
    • Microsoft XBox360
    • +
    • Sony Play Station 3 (provided by Sony)
      +
    • +
    +

    Development

    +
      +
    • Microsoft Windows XP
    • +
    • Microsoft Visual Studio .NET 2003
    • +
    • Microsoft XBox360 development tools (August 2006 XDK + v3529)
    • +
    • Sony Playstation 3 development tools
    • +
    +
    + +

    + +


    +Known Issues And Limitations

    + +


    +Below is a list of new issues and limitations; this is an +addition to the list from 2.5.0.

    + +
    +

    Windows Device Driver

    +
      +
    • Since the AGEIA PhysX Properties Control Panel Applet + is a 32-bit DLL, it does not appear in the Control + Panel automatically under 64-bit Windows. The user + must enable 32-bit Control Panel icons for it to + appear.
    • +
    +

    Hardware/Software Rigid Bodies

    +
      +
    • If shapes are added to an actor containing a single + triangle mesh shape in a HW scene, collisions with + the triangle mesh shape will no longer work. The + problem lies in the transition from single shape + actor to a compound, which means that compound actors + can be created with triangle mesh shapes either by + specifying all shapes directly in the descriptor, or + by making sure that the triangle mesh is never the + only shape in the actor.
    • +
    +
    + +

    Release Notes - AGEIA PhysX SDK 2.5.0

    +

    30th June 2006

    +

     

    +

    What's New In AGEIA PhysX 2.5

    +
    +

    Hardware/Software Rigid Bodies

    +
      +
    • Sweep API - Allows swept shape queries against a scene.
    • +
    • Active Transform Notification - Provides a list of actors which + have been updated.
    • +
    • Sleep Events - Provides a notification when an actor wakes up or + goes to sleep.
    • +
    • Kinetic Energy Based Sleeping - An improved criteria for + deciding if an actor should sleep. This is now the default.
    • +
    • Reduced overhead for sleeping actors.
    • +
    • Contact Modification - A callback is provided which allows the + user to modify contacts between the near phase and solver.
    • +
    • Fine grained threading is now enabled on the PC - parallel near + phase and solver.
    • +
    • Version Controlled Cooking - Cooking is now versioned along with + the runtime.
    • +
    • New/Delete has been cleaned up in many places and all calls + should now go through the user supplied memory allocator.
    • +
    • Adaptive force improvements - The adaptive force optimization is + now only applied to groups of objects which are in contact with a + static object, for example a stack resting on the floor.
    • +
    • The ratio property of a pulley joint is a force ratio rather + than a distance ratio.
    • +
    • The gear ratio of joints take into account the inertial + properties of the bodies involved. This affects the way forces are + propagated.
    • +
    • All joint types and collision primitives have software fall + backs in hardware scenes.
    • +
    • Full contact callbacks and modifiable contacts available in HW + scenes, but with substantial performance overhead.
    • +
    • Up to 64K shapes, 64K bodies, and 64K D6 joints may be created + in hardware scenes (but note the limitation below concerning active + object counts.)
    • +
    +

    Hardware Fluids

    +
      +
    • Fluid surfaces allow triangle meshes surfaces to be generated + for fluids on the PPU. Basic fluid surface feature set includes: +
        +
      • Depth smoothing
      • +
      • Multiple fluid surfaces associated with fluids
        +
      • +
      • User defined particles supported
      • +
      +
    • +
    • Two Way Interaction - Fluids can now exert a force on a rigid + body.
    • +
    • NX_FF_ENABLED flag added - This allows simulation to be enabled + and disabled for fluids. By default the flag is set. When turning the + flag off (on NxFluid), the next fluid step is not carried out. + Particles are not copied to the user buffers
    • +
    • NX_SF_FLUID_DRAIN_INVERTED - Removed for performance reasons. + There are workarounds to get the same effect.
    • +
    +

    Hardware/Software Cloth

    +
      +
    • Basic feature set: +
        +
      • stretching and bending resistance
      • +
      • two way rigid body interaction
      • +
      • attachments
      • +
      • damping and friction
      • +
      • tearing
      • +
      • pressure
      • +
      +
    • +
    +

    Hardware Scene Manager(HSM)

    +
      +
    • Two way hardware rigid body and cloth interaction.
    • +
    • Two way fluid and rigid body interaction.
    • +
    • One way managed rigid body versus primary scene rigid body + interaction, including:
    • +
    • Software material table mirrored to the managed scenes.
    • +
    • Software static meshes are paged to managed scenes.
    • +
    • Group based filtering mirrored to the managed scenes.
    • +
    • Ray cast results include query performed against managed scenes.
    • +
    +

    Performance

    +
      +
    • Sleeping objects are much faster, not consuming any CPU in 2.5
    • +
    • New friction model in 2.5 is somewhat faster, showing up in + stacking scenarios
    • +
    • Anisotropic friction, per-triangle materials and terrain height + fields run somewhat slower than in 2.4
    • +
    • All joint types but the D6 run somewhat slower in 2.5
    • +
    • Hardware rigid body scenes consume more CPU cycles than in 2.4
    • +
    • Running a scene on multiple threads now consumes memory + proportional to the number of threads concurrently within the SDK + rather than the number of threads which have entered the SDK since + creation.
    • +
    • Xbox 360 Optimization: +
        +
      • VMX128 rigid body constraint + solver.
      • +
      • VMX128 cloth solver.
      • +
      • VMX128 optimizations to box-box, + convex-mesh and convex-convex collision detection.
      • +
      • VMX128 midphase optimization for sphere-mesh, capsule-mesh, + OBB-mesh
      • +
      • VMX128 some small optimization for CCD.
        +
      • +
      +
    • +
    • PlayStation3 PPU only version significantly faster due to changes in Vector library.
    • +
    +

    Rocket

    +
      +
    • Updated interface using menu items in addition to onscreen + buttons.
    • +
    • Support for Cloth
    • +
    • Support for Fluids
    • +
    • Support for NxUStream
    • +
    • (For details, see the Rocket documentation included in the Tools + Installer)
    • +
    +

    Installers

    +
      +
    • With the high/low level split in the SDK, the source code + installer will not contain sources for low level components
    • +
    • 2.5.0 includes a tools installer. For beta and beyond, it will + include Rocket, a 3DStudio Max plugin and a Maya plugin.
    • +
    • Tool developers can now specify the version of cooking they want + to use.
    • +
    • Applications no longer need to include the PhysXLoader (that is + now in the System32 folder)
    • +
    +

    Documentation

    +
      +
    • Many small corrections and fixes for the user guide and API + reference.
    • +
    • User guide and API reference documentation for: +
        +
      • Fluid surfaces
      • +
      • Contact modification callback
      • +
      • Sweep tests
      • +
      • Active transform notification
      • +
      • Sleep events
      • +
      • Hardware scene manager
        +
      • +
      • Cloth tearing and pressure
        +
      • +
      • Mesh auto paging
        +
      • +
      +
    • +
    • Updates to the tutorials and samples pages.
    • +
    +

    Windows Device Driver

    +
      +
    • Support for PCIExpress Cards
    • +
    +
    +

    Supported Platforms

    +
    +

    Runtime

    +
      +
    • Software Runtime for Microsoft Windows XP (32bit editions)
    • +
    • AGEIA PhysX Hardware Runtime for Microsoft Windows XP (32bit + editions)
    • +
    • Microsoft XBox360
    • +
    • Sony Play Station 3 (provided by Sony)
      +
    • +
    +

    Development

    +
      +
    • Microsoft Windows XP
    • +
    • Microsoft Visual Studio .NET 2003
    • +
    • Microsoft Visual Studio .NET 2005 (not supported, project files + only)
      +
    • +
    • Microsoft XBox360 development tools
    • +
    • Sony Playstation 3 development tools
      +
    • +
    +
    +


    + +Known Issues And Limitations

    +
    +

    Hardware/Software Rigid Bodies

    +
      +
    • Joint types other than D6, spherical and revolute are partially + simulated in software.
    • +
    • For HW D6 joints, Only angular orientation drive & linear + position drive are supported, i.e. slerp drive & velocity + drive are not supported.
    • +
    • Wheel shape and joint parameters may need tweaking due to solver + changes.
    • +
    • Spherical and revolute joint behavior may differ from 2.4.
    • +
    • All joint break limits are specified as a maximum impulse and + might need to be adjusted.
    • +
    • User break notification now reports an impulse which is clamped + to the break impulse.
    • +
    • The friction model has changed. It should now be somewhat + stiffer. In addition friction is applied as soon as a contact is + created.
    • +
    • Mesh-Mesh collision detection is always performed in software(in + particular PMaps).
    • +
    • Mesh based height field collisions and height field collisions + are performed in software.
    • +
    • Wheel shapes are simulated in software.
    • +
    • Mesh and height field collisions are performed in software if + the mesh/height field has multiple materials defined.
    • +
    • Anisotropic friction is partially performed in software.
    • +
    • User contact reports and contact modification have quite a large + performance hit.
    • +
    • The result of sweep tests which are initially penetrating is + currently undefined.
    • +
    • Only boxes and capsules are supported by the sweep API as swept + objects. (spheres are emulated as capsules of zero length).
    • +
    • Sweep tests against planes, wheel shapes, and height fields are + not supported.
    • +
    • All sweep tests are synchronous(NX_SF_ASYNC is not supported).
    • +
    • Trigger shapes do not take part in CCD (Continuous Collision + Detection).
    • +
    • The methods isSleeping() and isGroupSleeping() now return the + same value.
    • +
    • Contact forces are not reported unless NX_NOTIFY_FORCES is set
    • +
    • Scene statistics no longer report the number and maximum number + of contacts, axis constraints or solver bodies
    • +
    • Hardware rigid body scenes support at most 4K active bodies. The + following additional restrictions are expected to be removed or + significantly alleviated during beta: at most 8K active shapes (shapes + whose pose is linked to active bodies), 4K active joints, and 4K + software fall back constraints. Note: these are counts for active + objects, sleeping objects are not included in the limits.
      +
    • +
    • The hardware broad phase supports at most 4080 actors. Broad + phase runs in hardware only if the NX_SF_RESTRICTED_SCENE flag is set + on the scene. Otherwise broad phase runs in software even in hardware + scenes, and there are no size restrictions.
    • +
    • Joint pose projection is not hardware accelerated
    • +
    • Hardware convexes are limited to 32 vertices and 32 faces. Convexes will fallback + to software above these limits.
    • +
    +

    Hardware Fluids

    +
      +
    • Maximum of 32767 particles per fluid.
    • +
    • Only limited fluid surface depth smoothing is supported in + hardware with filter size = 3.
    • +
    • Silhouette smoothing for fluid surfaces is not supported in + hardware.
    • +
    • Fluid surface resolution is limited to no more than 256x256, so + in the initial state, if the resolution is beyond the limitation, + NxImplicitScreenMeshDesc::isValid() will give an assert error. But if + this resolution is changed during application program running, it will + be automatically clamped to the maximum value.
    • +
    • Quadtree hierarchy mesh is not supported in hardware for fluid + surfaces.
      +
    • +
    • Two way interaction may be unstable with resting + particle-RB-contacts.
    • +
    • If the fluid hits packets which contain a high triangle density, + fluid mesh cooking on the host might become the bottleneck. However + there is logic to prevent detailed cooking when the load is too high. + Also cooking is now taking place in a separate thread, which removes + frame rate hick ups.
    • +
    +

    Hardware/Software Cloth
    +

    +
      +
    • Convexes up to 120 planes
    • +
    • Only cloth particle collision detection (no cloth triangle + collision detection)
    • +
    • < 1024 attached particles per cloth (limit might be removed + before release)
    • +
    • < 1024 colliding shapes (limit might be removed before + release)
    • +
    • Wild movement of the cloth in connection with high bending + stiffness can get the cloth into an entangled state. It looks like the + cloth would locally stick to itself. This is a local self collision + issue. Workaround: reduce bending stiffness.
    • +
    • Squeezing cloth between rigid bodies can cause jittering. + Workaround: reduce cloth thickness.
    • +
    • For small meshes (< 256 vertices) it is more efficient to + merge several of them into one cloth than creating a cloth instance for + each individual mesh. These meshes do not need to be connected (e.g. + multiple leaves of a plant).
    • +
    +

    Hardware Scene Manager(HSM)

    +
      +
    • Only group based collision reports are supported by the HSM.
    • +
    • Only a single managed hardware scene is supported by the HSM.
    • +
    • Effectors between hardware actors are not supported.
    • +
    • Actor and Shape pair flags not supported by the HSM.
    • +
    • Scene stats for hardware objects controlled by the HSM are not + supported.
    • +
    • Hardware rigid bodies do not exhibit two way interaction with + software objects under the HSM.
    • +
    • Hardware objects from separate scenes do not interact(e.g. rigid + body, cloth, fluids) under the HSM.
    • +
    • Dynamic height fields mirrored into the cloth and fluid scenes + will only supply 64 triangles, the rest will be silently dropped.
    • +
    • Shapes mirrored into the slave scene(cloth, rigid body, fluids) + will not be updated until they are mirrored/un-mirrored. This means if + you change a shape property(e.g. sphere radius) the hardware objects + will not be updated.
    • +
    • In the hardware rigid body scene only dynamic objects are + mirrored to the software scene.
    • +
    +

    Rocket

    +
      +
    • No known issues.
    • +
    +

    Installers

    +
      +
    • The AGEIA PhysX SDK must be installed to run samples as they + rely on DLLs contained within the main SDK installer.
    • +
    +

    Windows Device Driver

    +
      +
    • No known issues.
    • +
    +
    +
    +
    +
      +
    +
    +

    Release Notes - AGEIA PhysX SDK 2.4.0

    +

    23rd February 2006

    +

    AGEIA PhysX SDK Build Component

    +

    What's New?

    +
      +
    1. Documentation and Samples for new features.
    2. +
    3. A number of smaller documentation corrections and +clarifications.
    4. +
    +

    Platforms Supported

    +Development: +
      +
    • Microsoft Windows XP (32bit editions)
    • +
    • Microsoft Visual Studio .NET 2003
    • +
    • Microsoft Visual Studio .NET 2003 with the XBox360 development +kit.
    • +
    +Not Supported for Development: +
      +
    • Microsoft Windows XP x64 Edition (and all other 64bit editions)
    • +
    • Microsoft Visual Studio .NET 2005 (project files not provided / +not tested)
    • +
    • Microsoft Visual C++ 6 (project files not provided / not tested) +
    • +
    +

    Known Issues and Limitations

    +
      +
    • The AGEIA PhysX SDK Build Component and AGEIA PhysX Software +Runtime Component must be installed to run samples and training +programs, since they rely on DLLs contained within the runtime +component.
    • +
    +

    Version

    +2.4.0 +

    AGEIA PhysX Software Runtime Component

    +

    What's New?

    +
      +
    1. Dynamic vs Dynamic CCD support
    2. +
    3. Support for heightfield shapes
    4. +
    5. User thread control.
    6. +
    7. Fine grained threading of the simulation(available only on +XBox360)
    8. +
    9. Additional overlap tests for shapes
    10. +
    11. Hardware scene manager, allows fluids to interact with rigid +bodies.
    12. +
    +

    Platforms Supported (Runtime)

    +
      +
    • Microsoft Windows XP (32bit editions)
    • +
    • Microsoft XBox 360
    • +
    • Sony PlayStation3 (provided by Sony)
    • +
    +

    Known Issues and Limitations

    +
      +
    1. The AGEIA PhysX SDK must be installed to run samples as they +rely on DLLs contained within the main SDK installer.
    2. +
    +

    Version

    +2.4.0 +

    Hardware Rigid Bodies (AGEIA PhysX Driver)

    +

    What's New?

    +
      +
    1. Many optimizations and Bug fixes.
    2. +
    3. D6 joint support. The D6 supports the following features: +breaking and signaling, angular orientation drive and gears,
    4. +
    5. The simulation no longer locks up on firmware crashes. Instead a +flag passed to fetchResults () which is set on an error.
    6. +
    7. A new error reporting tool allows the driver to record the +simulation state leading up to a crash to allow analysis and bug +fixing.
    8. +
    +

    Platforms Supported (Runtime)

    +
      +
    • Microsoft Windows XP (32bit editions) with an AGEIA PhysX Card
    • +
    +

    Known Issues and Limitations

    +
      +
    1. There is a limit of 2048 D6 joints per scene.
    2. +
    3. Only angular D6 drives are supported. All other types of drive +are not supported.
    4. +
    5. D6 joint pose projection is disabled.
    6. +
    +

    Version

    +2.4.0 +

    Hardware Fluids (AGEIA PhysX Driver)

    +

    What's New?

    +
      +
    1. The AGEIA PhysX SDK now has a hardware scene manager, which +allows fluids to be created in a software scene. The hardware scene +manager takes care of mesh cooking and mirroring actors into the +hardware scene.
    2. +
    3. Support for more dynamic shapes in a fluid scene: convex mesh, +box, capsule, sphere
    4. +
    5. The following unimplemented API calls have been removed: +
        +
      • NX_FF_MESH_INCLUDE_SEPARATED
      • +
      • void NxFluid::setCollisionGroup(NxCollisionGroup group);
      • +
      • NxU32 NxFluid::getCollisionGroup();
      • +
      • NxImplicitMesh* NxFluid::createSurfaceMesh(const +NxImplicitMeshDesc& mesh);
      • +
      • void NxFluid::releaseSurfaceMesh();
      • +
      • NxImplicitMesh* NxFluid::getSurfaceMesh();
      • +
      • NxFluid::getDynamicActorReactionSmoothing ()
      • +
      • NxFluid::setDynamicActorReactionSmoothing(NxReal smooth)
      • +
      • NxFluid::getDynamicActorReactionScaling()
      • +
      • NxFluid::setDynamicActorReactionScaling(NxReal scale)
      • +
      • NxFluid::setOnSeparationAction (NxFluidParticleAction +action, bool val)
      • +
      • NxFluid::getOnSeparationAction(NxFluidParticleAction action) +
      • +
      • NxFluid::setOnCollision(NxFluidParticleAction action, bool +val)
      • +
      • NxFluid::getOnCollision(NxFluidParticleAction action)
      • +
      • NxFluid::setOnLifetimeExpired(NxFluidParticleAction action, +bool val)
      • +
      • NxFluid::getOnLifetimeExpired(NxFluidParticleAction action)
      • +
      • typedef NxU16 NxFluidGroup;
      • +
      • void NxFluid::setFluidGroup(NxFluidGroup group)
      • +
      • NxU32 NxFluid::getFluidGroup()
      • +
      • NxU32 NxFluidDesc::onSeparation;
      • +
      • NxU32 NxFluidDesc::onCollision;
      • +
      • NxU32 NxFluidDesc::onLifetimeExpired;
      • +
      • NxReal NxFluidDesc::dynamicActorReactionSmoothing ;
      • +
      • NxReal NxFluidDesc::dynamicActorReactionScaling;
      • +
      • NxReal NxFluidDesc::collisionGroup;
      • +
      • class NxImplicitMesh
      • +
      • void NxPhysicsSDK::setFluidGroupPairFlags()
      • +
      • NxPhysicsSDK::getFluidGroupPairFlags()
      • +
      • NxScene::setUserFluidContactReport()
      • +
      • NxScene:: getUserFluidContactReport()
      • +
      • NxImplicitMesh * NxScene::createImplicitMesh();
      • +
      • Void NxScene::releaseImplicitMesh();
      • +
      • NxScene::getNbImplicitMeshes
      • +
      • NxImplicitMesh** NxScene::getImplicitMeshes()
      • +
      • class NxUserFluidContactReport
      • +
      • NxFluidCollisionMethod::NX_F_DYNAMIC_ACTOR_REACTION
      • +
      +
    6. +
    +

    Platforms Supported (Runtime)

    +
      +
    • Microsoft Windows XP (32bit editions) with an AGEIA PhysX Card
    • +
    +

    Known Issues and Limitations

    +
      +
    1. The maximum number of capsules are limited to 1024 in a scene.
    2. +
    3. The maximum number of boxes in a scene is limited to 1024.
    4. +
    5. Spheres are emulated as capsule of zero height, so they share +the same limitation as capsules.
    6. +
    +

    Version

    +2.4.0 +

    Cloth

    +

    What's New?

    +
      +
    1. Software only cloth support (see the user guide for further +details)
    2. +
    3. Collision filtering.
    4. +
    5. Support for tearing and pressure.
    6. +
    +

    Platforms Supported (Runtime)

    +
      +
    • Microsoft Windows XP (32bit editions)
    • +
    • Microsoft XBox 360
    • +
    +

    Known Issues and Limitations

    +
      +
    1. A cloth can bend too much and get stuck in itself. Workaround: +use less bending stiffness or variate the bending stiffness over time.
    2. +
    +

    Version

    +2.4.0 +

    Tools

    +

    What's New?

    +
      +
    1. NxuStream- Support for serialization of the physical scene. +Supported formats include binary, Ascii(write only) and Collada
    2. +
    3. Visual Remote Debugger - View the physical representation used +by a game in real time and gather statistics and profiling information. +
    4. +
    +

    Platforms Supported (Runtime)

    +
      +
    • Microsoft Windows XP (32bit editions)
    • +
    • Microsoft XBox 360
    • +
    • Sony PlayStation3 (provided by Sony)
    • +
    +

    Known Issues and Limitations

    +
      +
    1. NxuStreamdoes not import Collada files created with other +libraries correctly.
    2. +
    3. Visual Remote Debugger connections must be enabled immediately +after initializing the SDK, otherwise the connection is likely to fail +(because object creation data, etc, is lost). For example: +
      // Create Physics SDK ...

      gPhysicsSDK->getFoundationSDK().getRemoteDebugger()->connect ("localhost", 5425);
      +
    4. +
    5. Reading profiler data at the same time the Visual Remote +Debugger is connected is not supported. Profiler data will not be +returned to the user when they call NxScene::readProfileData() if the +debugger is connected.
    6. +
    +

    Version

    +2.4.0 +
    +

    Release Notes - AGEIA PhysX SDK 2.3.2

    +

    6th January 2006

    +

    AGEIA PhysX SDK Build Component

    +

    What's New?

    +
      +
    1. It is no longer possible for end users to load PhysXCore.dll +from the application directory. The correct version is now loaded by +PhysXLoader from "Program Files\Ageia Technologies"
    2. +
    3. Installer improvements.
    4. +
    5. Fixes to sample project files.
    6. +
    7. Foundation.lib included with the XBox 360 installer.
    8. +
    9. Lots of documentation improvements.
    10. +
    +

    Platforms Supported

    +Development: +
      +
    • Microsoft Windows XP (32bit editions)
    • +
    • Microsoft Visual Studio .NET 2003
    • +
    • Microsoft Visual Studio .NET 2003 with the XBox360 development +kit. Not Supported for Development:
    • +
    • Microsoft Windows XP x64 Edition (and all other 64bit editions)
    • +
    • Microsoft Visual Studio .NET 2005 (project files not provided / +not tested)
    • +
    • Microsoft Visual C++ 6 (project files not provided / not tested) +
    • +
    +

    Known Issues and Limitations

    +
      +
    • The AGEIA PhysX SDK Build Component and AGEIA PhysX Software +Runtime Component must be installed to run samples and training +programs, since they rely on DLLs contained within these components.
    • +
    +

    Version

    +2.3.2 +

    AGEIA PhysX Software Runtime Component

    +

    What's New?

    +
      +
    1. Improved convex hull generation algorithm(the old method is +still available using a flag)
    2. +
    3. Character controller fixes.
    4. +
    5. Fix for distance joints not being breakable.
    6. +
    7. Additional overlap/intersection queries.
    8. +
    9. Fix for performance issues related to SSE denormal handling on +some machines.
    10. +
    +

    Platforms Supported (Runtime)

    +
      +
    • Microsoft Windows XP (32bit editions)
    • +
    • Microsoft XBox 360
    • +
    • Sony PlayStation3 (provided by Sony)
    • +
    +

    Known Issues and Limitations

    +
      +
    1. When statically linked(for example on XBox360) calling +NxInitCooking() overwrites parameters set when calling +NxCreatePhysicsSDK(). For example the error stream is reset. This +occurs because the variables are shared when statically linked.
      +Workaround: Supply the same parameters to NxInitCooking() and +NxCreatePhysicsSDK()
    2. +
    3. In some cases convex hull generation can fail for degenerate +input.
      +Workaround: Supply geometry without degenerate sets of points (edges +and planes). Use the legacy convex hull generation(see +NX_CF_USE_LEGACY_COOKER).
    4. +
    +

    Version

    +2.3.2 +

    Hardware Rigid Bodies (AGEIA PhysX Driver)

    +

    What's New?

    +
      +
    1. Many bug fixes and optimizations.
    2. +
    3. Fix getWorldBounds () for triangle mesh shapes.
    4. +
    5. Driver error reporting in fetchResults()
    6. +
    +

    Platforms Supported (Runtime)

    +
      +
    • Microsoft Windows XP (32bit editions) with an AGEIA PhysX Card
    • +
    +

    Known Issues and Limitations

    +
      +
    1. See the API references for details concerning which API +functions are supported.
    2. +
    3. At most 2000 shapes can be present in a scene at once (this +includes both static and dynamic shapes).
    4. +
    5. Precision limitations suggest that physics behavior may not be +stable or correct more than 1000 units from the origin.
    6. +
    7. Rigid body scenes occupy 32MB of memory on the card, so that +absolutely no more than three scenes may be present on the card at +once.
    8. +
    9. The following is an important but non-exhaustive list of SDK +features which are not supported: joints, continuous collision, contact +notification, many debugging visualization, contact notifications, +triggers, per shape skin width.
    10. +
    11. Convex hulls are limited in this release to have at most 32 +vertices and 32 faces.
    12. +
    13. Using terrain meshes on hardware requires making use of the new +mesh paging API. When a mesh is cooked, it is divided into a number of +hardware pages, and these pages must be manually moved to the card by +the application for any collision to take place.
      +The number of pages into which a mesh will be cooked is variable, and +will depend on the degree of local convexity in the mesh. However, at +least 500 triangles should fit into a mesh page, and the allocation of +16MB of mesh data supports 256 pages, which allows for the presence of +more than 100,000 triangles on the card. Note that when a page is +removed from the card, the space occupied by that mesh page is not +freed until after the physics has been stepped. No data yet exists for +the performance of paging meshes on and off the card; however as a +baseline the raw transmission of a mesh page by DMA requires half a +millisecond.
      +As in the software SDK, in the absence of ATT it is easy for thin +objects to tunnel through the mesh; in particular collisions will not +be detected with objects whose center lies beneath the mesh.
    14. +
    15. Due to limitations of the friction model, objects sliding on a +triangle mesh may not have correct frictional behavior. Per-triangle +materials are not supported.
    16. +
    +

    Version

    +2.3.2 +

    Hardware Fluids (AGEIA PhysX Driver)

    +

    What's New?

    +
      +
    1. Many bug fixes and optimizations.
    2. +
    3. Fluid scenes are created in a special hardware scene.
    4. +
    5. Fix to intermittent crash bug when adding a mesh to a fluid +scene.
    6. +
    7. Better input validation for the fluid mesh cooker.
    8. +
    9. Driver error reporting in fetchResults()
    10. +
    11. The Fluid can be configured with the new parameter +"motionLimitMultiplier". The user can define how far a particle can +travel during one timestep. This is important for tuning the memory +size consumed by the static mesh cooked for fluid collision. The +parameter effectively limits the maximal velocity of a particle. See +the user guide for more details.
    12. +
    13. If the user is interested in a more coarse simulation of fluids +which supports sprays and puddles, but works less well on deep pools, +he can choose this new simulation mode which alternates the simulation +between simple particles (without inter particle forces) and SPH +simulation. See NxFluidSimulationMethod::NX_F_MIXED_MODE and +NxFluidDesc::simulationMethod in the documentation.
    14. +
    +

    Platforms Supported (Runtime)

    +
      +
    • Microsoft Windows XP (32bit editions) with an AGEIA PhysX Card
    • +
    +

    Known Issues and Limitations

    +
      +
    1. See the API references for details concerning which API +functions are supported
    2. +
    3. Collision detection only supported with static triangle meshes +and convex meshes.
    4. +
    5. The number of convex shapes interacting at the same time with +one fluid is restricted to 1024 convexes. The user may add more dynamic +convex actors to the fluid hardware scene, though. If more than 1024 +convexes are moving within the AABB of the fluid, some will be ignored +for collision.
    6. +
    7. One dynamic convex is restricted to 64 triangles. If a convex +with more than 64 triangles is added to the fluid hardware scene, some +triangles are ignored for collision.
    8. +
    9. The user can only provide one pre-cooked static mesh per fluid +scene. All fluids added to the scene have to be compatible to the +static mesh structure, i.e. have the same restParticlesPerMeter, +kernelRadiusMultiplier, packetSizeMultiplier.
    10. +
    11. Issue: Max PPU Connections (15): If multiple fluids are created, +the maximal possible number of PPU connections can be reached. One +connection is used for the static mesh interface (per scene), and one +for each fluid created. If the maximum of connections is reached, the +fluid creation returns NULL.
      +Workaround: Don't create too many scenes and fluids. A fluid hardware +scene uses one connection and every fluid uses another one.
    12. +
    13. Crashing call to createFluidHardwareTriangleMesh (): This call +may crash if running out of PPU connections or PPU memory.
      +Workaround: Try to save on hardware rigid body scenes and hardware +fluid scenes, as well as fluids. Try to reduce the maximal number of +particles for the fluids. A main issue might be the cooked fluid static +triangle mesh size.
    14. +
    15. Fluid memory consumption can be approximated as:
      +NxFluid: constant(~2.5Mb) + 11 * maxParticles * 64 Bytes
      +Triangle Mesh: Depends upon:- +
        +
      • Cooked Geometry
      • +
      • restParticlesPerMeter
      • +
      • kernelRadiusMultiplier
      • +
      • motionLimitMultiplier
      • +
      • packetSizeMultiplier
      • +
      +Fluids can be allocated from all of the cards onboard memory. However +when hardware rigid bodies are used they compete for memory.
    16. +
    17. Issue: Slowdown on creating and releasing fluids in a row: The +reason for this issue has not been determined. This doesn't happen when +releasing the scene too.
      +Workaround: Create fluids once and reuse them. Recreation may lead to +fragmentation on the PPU memory, which is bad anyway.
    18. +
    +

    Version

    +2.3.2 +
    +
    Old Release Notes / Changelog

    2.3 - 2005.08.15
    ==================
    - XBOX 360, PS3 support. (may require files separate from windows installer package)
    - improved API reference documentation, doc comments.
    - new static vs. dynamic continuous collision detection based on CCD Skeletons
    - NxConvexShape introduced as independent class of NxTriangleMeshShape
    - NxWheelShape introduced for better raycast car support
    - added API to support selection between hardware and software implementation.
    - NxMaterial::programData API replaced with strongly typed spring member. Behavior is unchanged.
    - debug rendering API simplified. NxUserDebugRenderable removed, NxDebugRenderable interface class replaced by a simple struct.
    - more strict parameter checking in several places
    - removed raycast based CCD
    - removed FPU manipulation API functions.
    - intel assembly implementation of NxMath::sincos().
    - changed implementation of NxMath::rand()
    - replaced buggy TransformPlane() with correct NxPlane::transform() and NxPlane::inverseTransform().
    - added NxQUat::fromAxisAngleFast() and NxQuat::invert().
    - modified implementations for NxQUat::rot, invRot (), and ::transform().
    - removed NX_MF_CONVEX and NX_COMPUTE_CONVEX from NxMeshFlags of NxSimpleTriangleMesh
    - removed NX_NUM_SLEEP_FRAMES macro

    2.2, 2.1.3 - 2005.03.21

    ==================

    - new abstract core implementation for hardware compatibility
    - lots of new features for the D6Joint.
    - materials API completely rewritten
    - new mesh cooking library. Raw meshes must be preprocessed by the cooking library before being handed to the SDK. This makes creation of meshes at runtime much faster.
    - new character motion controller library
    - new extensions library
    - new external utils library
    - Foundation SDK has been merged into the Physics SDK. Specifically:
    - there is no more foundation.dll
    - the user must no longer link to foundation.lib
    - API documentation for the foundation SDK is now part of the Physics SDK API documentation.
    - the function exports of the foundation.dll are now exported from physics.dll
    - the member functions from foundation shape API classes (NxSphere, NxBox, etc.) that were calling foundation exports have been removed. Here is a list with the corresponding replacement export that must be called by the user instead:

    Old Member Function Replacement Function

    NxSphere::NxSphere(unsigned nb_verts, const NxVec3* verts) NxComputeSphere(sphere, nb_verts, verts);
    NxSphere::NxSphere(const NxSphere& sphere0, const NxSphere& sphere1) NxMergeSpheres(sphere, sphere0, sphere1);
    NxSphere::compute(unsigned nb_verts, const NxVec3* verts) NxComputeSphere(sphere, nb_verts, verts);
    NxSphere::fastCompute(unsigned nb_verts, const NxVec3* verts) NxFastComputeSphere(sphere, nb_verts, verts);
    NxBox::containsPoint(const NxVec3& p) NxBoxContainsPoint(box, p);
    NxBox::create(const NxBounds3& aabb, const NxMat34& mat) NxCreateBox(box, aabb, mat);
    NxBox::computePlanes(NxPlane* planes) NxComputeBoxPlanes(box, planes);
    NxBox::computePoints(NxVec3* pts) const NxComputeBoxPoints(box, pts);
    NxBox::computeVertexNormals(NxVec3* pts) const NxComputeBoxVertexNormals(box, pts);
    NxBox::getEdges() NxGetBoxEdges();
    NxBox::getEdgesAxes() NxGetBoxEdgesAxes();
    NxBox::getTriangles() NxGetBoxTriangles();
    NxBox::getLocalEdgeNormals() NxGetBoxLocalEdgeNormals();
    NxBox::computeWorldEdgeNormal(NxU32 edge_index, NxVec3& world_normal) NxComputeBoxWorldEdgeNormal(box, edge_index, world_normal);
    NxBox::computeCapsule(NxCapsule& capsule) NxComputeCapsuleAroundBox(box, capsule);
    NxBox::isInside(const NxBox& box) NxIsBoxAInsideBoxB(box, box);
    NxRay::distanceSquared(const NxVec3& point, NxF32* t = NULL) NxComputeDistanceSquared(ray, point, t);
    NxRay::distance(const NxVec3& point, NxF32* t = NULL) sqrtf(NxComputeDistanceSquared(ray, point, t));
    NxSegment::squareDistance(const NxVec3& point, NxF32* t=NULL) NxComputeSquareDistance(segment, point, t);
    NxSegment::distance(const NxVec3& point, NxF32* t = NULL) sqrtf(NxComputeSquareDistance(segment,point, t));
    NxCapsule::computeOBB(NxBox&box) NxComputeBoxAroundCapsule(capsule, box);
    NxCapsule::contains(const NxVec3& pt) NxComputeSquareDistance(capsule, point) <= radius*radius;
    NxCapsule::contains(const NxSphere& sphere) NxF32 d = radius - sphere.radius; if(d> = 0.0f) return NxComputeSquareDistance(capsule, sphere.center) < = d*d; else return false;
    NxCapsule::contains(const NxCapsule& capsule) contains(NxSphere(capsule.p0, capsule.radius)) && contains(NxSphere(capsule.p1, capsule.radius));

    Users will need to replace any calls to the old member function to the corresponding replacement function.
    - NxProfiler class deleted
    - removed virtual NxProfilingZone * NxFoundationSDK::createProfilingZone(const char * x) = 0;
    - NxArray no longer derives from NxAllocateable
    - removed NxComputeVolumeIntegrals from API
    - removed obsolete Foundation/include/NxBlank.h
    - removed obsolete Foundation/include/NxList.h
    - added NxActor::getMaxAngularVelocity ()
    - added NxActor::get/setSolverIterationCount()
    - added Physics/src/Physics.h: contains #define's from /src/Nxp.h which are just used from inide the SDK and should not be visible from outside
    all files in the /src including Nxp.h now include Physics.h
    - removed is...() (e.g. isBox()) functions from NxShapes and NxJoints
    updated samples: added static castings for on NxShape and NxJoint to the appropriate type instead of calling the is function
    (e.g static_cast(s) instead of s->isBox())
    - added NxPlane NxPlaneShape::getPlane() const;
    - removed methods deprecated in 2.1.2
    - added NxScene &NxActor::getScene() const;
    - added NxPhysicsSDK& NxScene::getPhysicsSDK() const;
    - added NxJoint& NxActor::getScene() const;
    - removed NX_INLINE void NxFPU::NxSinCos(NxF32& c, NxF32& s, NxF32 f);
    - added NX_INLINE static void NxMath::sinCos(NxF32 & c, NxF32 & s, NxF32 f);
    - new NX_ACCELERATION force mode.
    - new getScene() method for objects contained in scene.
    - moved public enums from various classes to Nxp.h
    - new shape flag NX_SF_POINT_CONTACT_FORCE and NxContactStreamIterator::getPointNormalForce() for returning per-point contact forces.
    - improved validation in NxJointLimitSoftDesc::isValid()
    - added function NxGetValue(NxCookingValue).
    - motor for pulley joint
    - added NxU32 NxScene::setThreadAffinityMask(NxU32 mask)
    - replaced MIN_SEPARATION_FOR_PENALTY with NxShape::get/setSkinWidth(), and a global skin width parameter.
    - new 128 bit mask based collision filtering (NxShape::getGroupsMask(), NxGroupsMask)
    - new NxRaycastBit NX_RAYCAST_FACE_NORMAL for retrieving non-smoothed normals.
    - new method NxCapsuleShape::getWorldCapsule()
    - new method NxSphereShape::getWorldSphere() - new NxScene::getNbStaticShapes(), getNbDynamicShapes().
    - new triangleIDs for raycasting.
    - removed support for pmap-less nonconvex nonheightfield mesh-mesh collision detection which was working poorly.
    - removed obsolete parameter NX_MESH_MESH_LEVEL
    - removed obsolete parameter NX_MESH_HINT_SPEED - removed NxActor::setDynamic(). Workaround: Create the initially static actor as a kinematic. Later turn off the kinematic state.

    2.1.2 - 2004.11.08

    ==================

    - more strict error checking
    - multithreaded implementation - shape descriptors in NxActorDesc can now be allocated using a custom allocator.
    - fluid API - NX_MESH_SMOOTH_SPHERE_COLLISIONS now disabled by default
    - made mesh instances default to flat and not smooth sphere mode
    - removed neccesarily reentrant method NxContactPair::setPairNotify(NxU32). Workaround: use any of the
    several different ways to set actor flags.
    - actor group and actor group pair flags
    - raycast car support, including: - New + improved 'multipatch' friction implementation. Fringe benefits are that rare contact situations with multiple contact normals or materials-per-triangle are now handled correctly.
    - Capsule shapes now have a 'swept shape' mode that can be used to get raycast wheel behavior. This is enabled with the new NX_SWEPT_SHAPE flag in NxCapsuleShapeDesc.
    - programData member in NxMaterial
    - NX_MF_SPRING_CONTACT flag of NxMaterial, and the appropriate code to emit suspension-spring-like contacts when this is enabled.
    - SampleRaycast car
    - Reworked applyForce code:
    - removed these methods of NxActor because they were causing user confusion (they were hoping that it did more than just read back what they have previously set...) setForce (), setTorque(), getForce(), getTorque()
    The replacement for setForce/setTorque is calling addForce/addTorque just once (per frame).
    The replacement of getForce ()/getTorque() is to keep track of the forces you add.
    - removed the variables: NxBodyDesc::initialForce, NxBodyDesc::initialTorque. The replacement for initialForce, initialTorque is addForce/addTorque after creating the object.
    - forces act over the entire time step, including all substeps
    - NxMaterial's vectors are now in shape space not in actor space.
    - added 3 new joint types: - Distance - Pulley - Fixed
    - kinematics perform move over a whole step, not a substep
    - added methods to add impulses and velocity changes
    - exception handling in SDK off, was enabled by mistake in 2.1.1
    - implemented more material combine modes
    - fixed a debug rendering crash bug
    - fixed debug rendering extra lines and zero length lines bugs
    - NX_VISUALIZE_COLLISION_NORMALS now defaults to zero.
    - raycasting based approximate continuous collision detection (enable using NX_CONTINUOUS_CD)
    - faster raycasting, now also supports segment queries
    - 'one shot' scene collision queries
    - added access to internal mesh data

    2.1.1 - 2004.07.15
    ==================
    - convex-mesh collision detection
    - convex meshes viewer scene
    - broken joints deletion process cleared up
    - raycasting against compounds fix
    - NX_NOTIFY_ON_START_TOUCH bug fixed
    - bugs related to jointing bodies to kinematics fixed
    - crash fixed when switching body from kinematic to dynamic
    - implemented contact and friction force feedback in contact report
    - joints to kinematic bodies now projected correctly
    - removed reduced simulation mode for joints
    - made it possible to set a different number of solver iterations per body
    - added adaptive force parameter - improved solver: bodies don't pop out of other bodies as fast as before
    - improved solver: non-reduced mode joints work better
    - fixed rotating spheres and longs solver problem
    - fixed solver instability on very thin objects
    - visual C++ .NET project files
    - fixed several box-capsule contact generation bugs
    - added more error messages regarding broken joints
    - removed '#'-s in filenames of monster truck demos
    - updated docs with more information on releasing actors and broken joints
    - added angular projection for revolute joints
    - fixed some bias scaling related bugs in friction and joint setup code
    - fixed uninitialized variable bug in viewer
    - removed obsolete files from foundation
    - extended ODF files to permit specification of joint projection
    - fixed joint projection bug
    - fixed fat lines rendering bug in viewer
    - fixed some doc bugs
    - fixed bug regarding kinematic bodies going to sleep
    - fixed sphere joint limit visualization bug
    - visualization flag for shapes, bodies, and joints
    - added docs on masses of trigger shapes
    - added FAQ section to SDK docs
    - simplified versioning mechanism
    - renamed ShapeDesc::triggerFlags into ShapeDesc::flags.
    - fixed bug regarding deletion of trigger shapes
    - removed personal edition copy protection scheme
    2.1 - 2004.05.15
    =================
    - Pulled enums and typedefs out of classes. For example NxSomeClass::SOME_FLAG is now NX_SOME_FLAG.
    - NxCollisionGroup changed from an enum to an int.
    - NxCollisionGroup 0 no longer has special meaning. Static-static collision detection is automatically not performed.
    - Actors, Shapes and Joints now have setName () getName() methods for debugging convenience.
    - Removed support for dynamic meshes flag
    - Optimized mesh inertia tensor computation
    - Removed dynamic and default mesh classes from Foundation
    - Replaced NxUserTriangleMesh with NxSimpleTriangleMesh, changed the way meshes are provided to the SDK - User's meshes are now copied, not referenced
    - Materials are now stored in a global list and indexed by shapes, not actors.
    - New material per triangles feature (for all mesh-primitive combinations)
    - Added sleep velocity thresholds
    - Changed SLEEP_LIN_VEL_SQUARED to SLEEP_DEFAULT_LIN_VEL_QUARED& also angular
    - Documented NX_MIN_SEPARATION_FOR_PENALTY in manual.
    - Removed quaternion accessor functions and made elements public.
    - Added NxMat34::getColumnMajor44(), NxMat34::setColumnMajor44() for ez 4x4 graphics matrix conversion.
    - Added X get*() methods which return the matrix value instead of copying it to a destination address. void get*(X) methods are now deprecated.
    - Fixed bug with kinematicactors (were generating useless contacts)
    - Fixed bug with raycasting against compounds (was returning internal compound shapes & wasn't testing collision groups)
    - RunFor() timing params are now scene properties.
    - Asynchronous API added. runFor is deprecated.
    - Removed NxUserContactReport::onPairCreated() due to performance concerns! Users must take care to notice that their implementing code no longer does anything / never gets called!! We don't have a full replacement for this mechanism yet. For now NxScene::setActorPairFlags() is the best bet.
    - Added CoreDump mechanism.
    - Maya exporter
    - ConvexMesh-ConvexMesh Collision Detection
    - Four new sample programs: SampleConvex, SampleGameLevel, SampleMaterials, SampleMeshMaterials
    - Got rid of NxActor's localToGlobalSpace, globalToLocalSpace, localToGlobalSpaceDirection, globalToLocalSpaceDirection . because the implementation transformed between the wrong spaces. To get the old behavior, use this code: NxMat34 cmpose; actor->setCMassGlobalPose(cmpose); worldPositon = cmpose * bodyPosition;
    //localToGlobalSpace bodyPositon = cmpose % worldPosition;
    //globalToLocalSpace worldDirection = cmpose.M * bodyDirection;

    //localToGlobalSpaceDirection bodyDirection = cmpose.M % worldDirection;
    //globalToLocalSpaceDirection To get the proper behavior, use this code: worldPositon = actor->getGlobalPoseReference() * bodyPosition;
    //localToGlobalSpace bodyPositon = actor->getGlobalPoseReference() % worldPosition;
    //globalToLocalSpace worldDirection = actor->getGlobalPoseReference().M * bodyDirection;

    //localToGlobalSpaceDirection bodyDirection = actor->getGlobalPoseReference().M % worldDirection;
    //globalToLocalSpaceDirection
    - Removed NxScene::enablePair, isEnabledPair. They are replaced by the new get/setPairFlags (). The old behavior maps to the new calls like so: /**
    enables or disables collision detection between a pair of actors. Initially all pairs are enabled. Collision detection between two shapes a and b occurs if: NxPhysicsSDK::getGroupCollisionFlag(a->getGroup(), b->getGroup()) && isEnabledPair(a->getActor(),b->getActor()) is true. Note: a and b may not refer to the same shape. */ NX_INLINE void enablePair (NxActor&,
    NxActor&,
    bool enable) { if (enable) setPairFlags(a,b, getPairFlags(a,b) | NX_IGNORE_PAIR); else
    setPairFlags(a,b, getPairFlags(a,b) & ~NX_IGNORE_PAIR);
    }
    /** Queries the value set by the above call. */ NX_INLINE bool isEnabledPair(NxActor&a, NxActor&b) = 0 {
    return getPairFlags(a,b) & 1;
    }
    2.03 - 2004.04.08
    =================
    -B

    AM Feb 20 fixed: inertia tensors of meshes are wrong -B
    AM Feb 22 fixed: syntax error in NxQuat template's slerp code -B
    AM Feb 22 fixed: setGlobalAxis/setGlobalAnchor in NxJoint and NxJointDesc transform to body space, while the local frames are documented as being in actor space, as they should be -B
    AM Feb 22 releasing/in any way changing static actor doesn't notify its joints (incl: making it dynamic) == > workaround for now is to use NULL for static actors when jointing
    -B AM Feb 22 fixed: Spellcheck api doc comments
    -B AM Feb 22 new feature: kinematic motion of bodies
    -B AM Mar 5 fixed: Several class doc comments are not immediately followed by the class declaration so doxygen ignores them.
    -B AM Mar 02 fixed: Sleeping code is time step sensitive: With small dt stuff goes to sleep unrealistically fast.
    -B AM Mar 05 fixed: Deleting while in bbox vis mode causes a crash
    -B PT Mar 20 Fixed crash when running empty simulation
    -B AM Mar 20 Improved capsule contact generation for parallell case.
    -B PT Mar 22 Additional parameter in NxActor::wakeUp

    API Changes

    - replaced bitfields with enum flags (impacts aniso friction and joints)

    Other changes

    - removed all templates except NxArray
    - Added 3 new samples
    - fixed various contact stream iterator problems
    - updated docs with kinematic bodies infos
    - added this change log

    2.02 - 2004.02.19
    =================
    Collision SDK and RB SDK Merged
    Substance SDK Discontinued
    Most of the tech completely rewritten - hence no change list.


    1.91 - 2003.07.07
    =================
    Rigid Body SDK: get hinge angle and velocity query, for both artic and lagrange .
    Rigid Body SDK: fixed: penetration correction of lagrange jointed stuff is worse than articulated stuff
    Rigid Body SDK: fixed: dense grid of contacts acts weird (Pierre showed that this is fixed but we have no good repro)
    Rigid Body SDK: changed solver to no sub iters (5x faster), and correct bounce correction
    -- now big stacks go to sleep slower
    -- boxes don't bounce up straight anymore
    Rigid Body SDK: removed obsolete rigid sdk params
    Rigid Body SDK: very redundant contacts are working.
    Rigid Body SDK: stuff can be created in sleeping mode, even if jointed.
    Rigid Body SDK: fixed: Articulations don't fall asleep
    Collision SDK: heightfield mode for meshes
    Foundation SDK: fixed inplace matrix transpose bug
    Collision SDK: test cave configuration for terrains
    Collision SDK: upgrated to opcode 1.3
    Collision SDK: fixed a bug in mesh-mesh that didn't seem to be having any bad effects.
    Foundation SDK: all stl gotten rid of
    Foundation SDK: array class with decent memory management
    Viewer: vs7 build of viewer
    Tools: flexporter was exporting spheres wrong (made them off-center with a nonzero offset)
    Rigid Body SDK: fixed: the asymetry of the fixed and prismatic joint leads to breakage if the bodies are not ordered right (it creates a different response.) Collision SDK: capsules integrated (Simon)
    Rigid Body SDK: new contact solver combines Pierre's work
    Rigid Body SDK: created fixed joint
    Demo: gears demo
    Demo: bride demo
    Rigid Body SDK: added bias for limits.
    Collision SDK: fixed a bug in sphere-sphere penetration computation
    Foundation SDK: Spelling Error in Nx.h: "usually becase you have passed"
    Rigid Body SDK: Spelling Error in NxRigidBodySDK.h: "byt": Larger values may fix exploding systems byt introduces constraint error.
    Collision SDK: fixed incorrect computation of box-sphere penetration in the special case of the sphere center being embedded in the box.
    Rigid Body SDK: fixed a bug regarding the deletion of effectors not removed from scene
    Rigid Body SDK: fixed a bug regarding removal of joints, bodies, and effectors from one of multiple scenes, where they were not added
    Rigid Body SDK: fixed a bug regarding the duplicate addition of effectors to a scene
    Rigid Body SDK: fixed a bug with non-invertable response of fixed articulation
    Viewer: fixed non pow2 texture widths are loaded badly (jpg format)
    Collision SDK: Fixed a bug in mesh vs. line
    Collision SDK: Fixed a bug in heightfield vs. box
    Collision SDK: Fixed a bug in mesh vs. plane
    Rigid Body SDK: fixed explosion on constraining missing featherstone dofs.
    1.9
    All SDKs: Codewarrior compiler compatibility -- renamed overloaded method names
    All SDKs: Professional Edition DLLs
    Rigid Body SDK: Fixed a crash bug related to deleting certain joints or bodies
    Rigid Body SDK: Completely rewritten solver
    Foundation SDK: changed memory allocation in foundation, + other misc stuff so other SDKs don't have to link with foundation

    1.8 - 2003.06.01
    =================
    Rigid Body SDK: gravity applied if group is not sleeping
    Rigid Body SDK: normal forces have different magnitudes, not real normal forces, just an estimate (this is a problem)
    Rigid Body SDK: frict scaling params removed.
    Rigid Body SDK: frict coeffs changed, new ranges
    Rigid Body SDK: FAST linear time contact solver has good friction model!
    Rigid Body SDK: FAST linear time contact solver!
    Rigid Body SDK: add API method to purge limit planes.
    Rigid Body SDK: applyImpulses () should checks f[i], and early outs if it is zero.
    Collision SDK: sweep and prune broad phase, new NxBroadPhase
    Collision SDK: caching of near phase results when there was no movement.
    Collision SDK: redone exact mesh-box
    Collision SDK: redone exact mesh-sphere
    Collision SDK: fixed iterators in scene and shapeSet so that calling getNext () after hasMore() returns zero doesn't result in access violation. Actually, now hasMore () is redundant.
    Viewer: changed default friction and restitution coeffs in viewer.
    Viewer: don't pose change notify unless neccesary
    Viewer: punching of bodies
    Viewer: better mesh visualization

    1.6 - 2003.02.21
    =================
    Viewer: delete bodies in viewer demos by right-dragging a body and then pressing delete.
    Viewer: monster truck can now be driven even if some wheels have been deleted.
    Viewer: tweaked monster truck car controller for torque based driving.
    Viewer: Granny support for articulated characters ('ragdolls') (only available for Rad Game Tools Granny licensees)
    Viewer: inertia tensor can be specified explicitly in the body block of an ods file
    Viewer: Made a real collision veto joint in viewer
    Collision SDK: fixed bug in mesh-box colldet (thanks to Jason Zisk of nFusion)
    Collision SDK: mesh data structure can be visualized by appropriate setting SDK param.
    Collision SDK: removed STL from hashGridSpace + optimized it a bit
    Collision SDK: new contact groups methods, removed corresp. group methods from hashGridSpace.
    Collision SDK: new pairwise contact disabling methods
    Rigid Body SDK: hugely improved performance of hinge and prismatic joints, see the monster truck demo for an example.
    Rigid Body SDK: NxBody::createClone()
    Rigid Body SDK: customizeable damping in NxJoint, works only for chains of hinge and prismatic joints.
    Rigid Body SDK: fixed defect in joint limit plane iteration.
    Rigid Body SDK: fixed implementation bug of bool Scene::hasMoreJoints().
    Rigid Body SDK: joint and body scene removal / addition / deletion error checking.
    Rigid Body SDK: setVelocity / momentum only wakes bodies if the set vel is high.
    More orderly shortcuts in startmenu.
    More demos.
    New licensing scheme (registry stuff goes to HKEY_CURRENT_USER, we don't mail reg files anymore.)
    No more double precision libs shipped in personal edition (ask if you still want them)
    updated docs

    1.51 - 2003.01.09
    =================
    Fixed vsync slowdown problem in viewer
    Frame rate limited to physics rate (usually 50 Hz)
    single precision floating point DLLs for collision SDK and RB SDK(F32 suffix)
    doc bug fixes

    1.5
    =================
    Substance SDK
    userErrorStream has new reportError member
    Foundation SDK as DLL
    substance SDK support in viewer
    breaking joints was broken in 1.45, fixed.
    mesh-box collision detection implemented
    matrix ops of the form Mx::op(A,B) first write to a temp object in case A.op(A,B) is called.
    also same for vector cross product.
    fixed bug regarding edge-edge colldet penetration depth compute.
    improved mesh-sphere
    added fracturable vase and road blocks to monster truck demo

    1.45 - 2002.12.02
    =================
    updated simple demos
    added colldet block and sim params block to demo scripts.
    obstacle support removed
    mass = 0 bodies can have material properties
    getBodies method in NxJoint
    fixed hashspace problem
    solver params settable from scripts
    fixed box-box bug
    stuff stuck in air now wake up
    thin rod spin limiting by max angular velocity
    joint support improved for Juice

    1.44 - 2002.10.30
    =================
    mesh-mesh collision detection added
    mesh-sphere collision detection added
    mesh-line collision detection added
    mesh-plane collision detection added
    internal parameters exposed
    doc bugs fixed
    full screen mode put back
    box-sphere collision penetration depth fix
    isAttachedByJoint typo
    NxSphere, etc. can have release() called on it directly.

    1.431
    =================
    mass adaptive drag force strength in viewer

    1.43 - 2002.10.15
    =================
    additions and fixes in viewer and rigid body documentation (thanks to Nate and Pierre)
    memory leak fix in viewer (thanks to Mete)
    domino demo failure
    included Pierre's new toboggan demo

    1.42 - 2002.10.08
    =================
    fixed installer problem (DLL placement)

    1.41 - 2002.10.03
    =================
    memory manager issues
    readme for max exporter
    shortcuts to viewer doc and projects
    readme file correction

    1.4 - 2002.08.25
    ================
    first public SDK release


    ==EOF==
    +

    +
    Copyright © 2006 AGEIA Technologies Inc, Suite 118, 82 Pioneer Way, +Mountain View, CA 94041 U.S.A. All rights reserved. www.ageia.com + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/PlatformSDK/ReadMe.txt b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/PlatformSDK/ReadMe.txt new file mode 100644 index 00000000..befa375c --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/PlatformSDK/ReadMe.txt @@ -0,0 +1 @@ +Windows Platform SDk diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/License.txt b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/License.txt new file mode 100644 index 00000000..8d286121 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/License.txt @@ -0,0 +1,20 @@ +License for Scintilla and SciTE + +Copyright 1998-2003 by Neil Hodgson + +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation. + +NEIL HODGSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS, IN NO EVENT SHALL NEIL HODGSON BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE +OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/README b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/README new file mode 100644 index 00000000..9775d703 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/README @@ -0,0 +1,69 @@ +README for building of Scintilla and SciTE + +Scintilla can be built by itself. +To build SciTE, Scintilla must first be built. + + +*** GTK+/Linux version *** + +You must first have GTK+ 1.2 or later and GCC (3.1 or better) installed. +GTK+ 1.0x will not work and when it did it was very slow. +Other C++ compilers may work but may require tweaking the make file. + +To build Scintilla, use the makefile located in the scintilla/gtk directory + cd scintilla/gtk + make + cd ../.. + +To build and install SciTE, use the makefile located in the scite/gtk directory + cd scite/gtk + make + make install + +This installs SciTE into $prefix/bin. The value of $prefix is determined from +the location of Gnome if it is installed. This is usually /usr if installed +with Linux or /usr/local if built from source. If Gnome is not installed +/usr/bin is used as the prefix. The prefix can be overridden on the command +line like "make prefix=/opt" but the same value should be used for both make +and make install as this location is compiled into the executable. The global +properties file is installed at $prefix/share/scite/SciTEGlobal.properties. +The language specific properties files are also installed into this directory. + +To build Scintilla for GTK+ 1 rather than the default GTK+ 2, define GTK1 on +the make command line: + make GTK1=1 + +To remove SciTE + make uninstall + +To clean the object files which may be needed to change $prefix + make clean + +The current make file only supports static linking between SciTE and Scintilla. + + +*** Windows version *** + +A C++ compiler is required. Visual Studio .NET 2003 is the development system +used for most development although Mingw32 3.1 and Borland C++ are also +supported. +For older versions of Borland make such as version 5.02, add the -l option. + +To build Scintilla, make in the scintilla/win32 directory + cd scintilla\win32 +GCC: mingw32-make +VS .NET: nmake -f scintilla.mak +VC++ 6: nmake -f scintilla_vc6.mak +Borland: make -fscintilla.mak + cd ..\.. + +To build SciTE, use the makefiles located in the scite/win32 directory + cd scite\win32 +GCC: mingw32-make +VS .NET: nmake -f scite.mak +Borland: make -fscite.mak + +An executable SciTE will now be in scite\bin. + +The Visual C++ 6.0 project (.dsp) files are no longer supported but are left +in the download for people that are prepared to update them. diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.dll b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.dll new file mode 100644 index 0000000000000000000000000000000000000000..cc99cf0e5941d73bf1e302400a2618680accd3d5 GIT binary patch literal 537088 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 z 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%0AJ?Ltn`D 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%0AI?vl$ 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 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.exp b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.exp new file mode 100644 index 0000000000000000000000000000000000000000..5850ef13bbcca2253adb7e1d39edc83a9ec15efb GIT binary patch literal 639 zcmah`!A`AbNUMB}|05Wna_ngE@3&ICX5A&T0jD1WEAJyZt)x2(z zMsbh%A=x)*Q0`DgXJjfK1zBd=@3-X&ov(SUdcF@>1c~r{hjm5F-Q;!^xQPe@C}C3` zQ-Kr2f@C$sJ;~~bMadcx$#;Q_Zm-C`DjXsQsHp06CW7hqRKAWHP$?_8M!Qlda0JED z@ld#NxC|eYTEjfA>3w4Wnj0=6Rd~hH6gC^`_&GoN&H^l73}?XsF0o3Lcpg2`zI3-t zNnLcuOnDx~IQZsPsT)m<7F=%?BydXb6?u3=wsT}`*NWD<0>;E5V*gnB!|}+pS+{51 inEkOmGRZH07WNwxU1xtB@S0gtgMBwK2CUn*+wcjJ4R-nf literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.lib b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.lib new file mode 100644 index 0000000000000000000000000000000000000000..a90f04bebcb35281b4068189249ecd82a19f404a GIT binary patch literal 1814 zcmcIlPj3=I6#wl4MaiZT+iP{3^iVxum%<9hn60Hm(k*RZdo&|1Xg8tI;5PB*7w{wW z?uYQwZ{Wd$w_f}p^}S^Vwzg0(b&{Q#_nS8}@Av-9uD)yy{G(TyXXe#rS;gmep=^~l zui_h0wCs|cy~_Yd0onzST%>6gSUBTPh}z!4e!~-$o#u9|2A%rEZ5IBEJ7(ZD*<;F;xpue9>~D)|ps-)og{`%`T3V*b5Bh#fAVH&pjdQ7IOBj?s z(C9v;$G8LYT&Hmf_gRDyC$)~HY??qmkXsrVq>P7qfj{Vf>VF8$^~Mv^t<`sj%YXF- z$-I+K;4tzmOFaiQjrcuehTlIvA$wECTU=hM8+i{mHc*G6?EsO9}-y`$b$eZ(}9W9N!SiWp3 zl=J1A66DDF@}Kfxme{V4857^Ex&2nMuGQHux?mXSoBcMW&Wg%aoQ_;)@fNGIg2Gwv rrxN6N$Nrtgr;q6B$&c6E*=Va|H2I&zMe1t&f<28G8q;6rcnI+aLZVcB literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.pdb b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/SciLexer.pdb new file mode 100644 index 0000000000000000000000000000000000000000..d09e00fa06ca9ad6172bb1592c237fb0954de3c6 GIT binary patch literal 1403904 zcmeFa3!GQe{{O%Co=PdqPzljcQY2GNx(L%%X{ymAA*N~OGtFq48FQg1awNHj5Rwq$ zxa0_-qYy%e&>@64Ax@5v%Q-InpRcv{`h50$Ohmrl|M&O(wjYn@eCD%0Yrogp>;8VP z%N|lvP+4A8UR0gl_prW)9FjgHcR+f#ju{!d?7VlsT#u`s=LLM6T;KCnwUC47jo|+s z*Nzm}kplnkr@*qGw(Pieq`;08_&=Hg-TnVp+OT#1M{{V$WA8|T|GE@7vr#9%yPAO1 z5Iz)|#{9-D{I0KZcc1%t^_qF!RDF$HCzMwfW|ve|dtdDA1*y7!XMWZHe)xag_w_C) zh*edUSK9kh@Ov=#?d|!yOVy}>>JX%Q^$+iZ@#BAtKeIjoa%XB?8?1* z2Km+f@}HsK&}JIq=HjPt4#4kDd{&_A$ZwRL@mo8*oxH~8et&m0ow`S|b&u!y-Y)jH zRK?f#z9b!d6R!Suanf&2W`=GfPHEwN6@0Vz1*v<;&B#w_)|CJCYz;l{iw49##XCMn z&(Pf5r)Q`uKeA&JW0gb73u{VaVO(0^-^t%AyjOA>ME+M;F5gTX2v>e|ui~Tsb+6Kn z=QHWhitl@SjZB`%-+s`Tkka|owIBW(@Tq6t2e*AepDjUdKfkwucL2ZX+TClgyYhN} zzU#9!pAC?w-+q3d=N%Z{=i;e2%b&tg+zx`q{Gae=pTWgLaZC8~z4oEJXjoQ;EqjWG zO9$O6e|v{A^qW$=LqtaMZ%)3RQ}H?!zxwRRr<1GlWy0rKqUoCv%KeLa5LIXbsGzq+I#x8LBZ(I=FYb?L0ZKv{K3X=(oGGtGBK z1}F9&lHIX<>}lRT=HBLMvJW)yc472izlr@@dwejgqPk>4$yp_3j?pI#mHTN2)VFx#M`l{;u z%Ici*Dhb>j_a=PG)>#@W8&_TI8NY}!Zh3h{<&_iiOZm!}r*RtQQY_h?H$FD$%y8l& zE=Nn9WTUUDo@An{FgJX^WsB^vCd}-TvKap>%#ERYtgO()L~ffxw+Z?+VJ34tZ#2ItzehnM!khV=N#EYp{fA}aWWrRsMxR@raW&=D zF$q!{xx7*uDW5V)^W_YhC+YhNn@^IxiTfq2B463BXHk`lnR`aye;M@Q_aWgu`*F|4 zFh2I4LA7G#^4f*jhIGA$upfHfhN(Dj2=6C7M&_5+sMwnDRuEP?{%7(1#TO%GDfC~` zIZ>7rPUkS+mCw2|LH{Z}b|r0Rt~BK-w<5nF);CNvg|9MISAHwa6ZFnP1RZGz(3lyKB0MB570#k$&qwZva}S)~UaP?|rE z!XUp|sjwR|T-{e5t`Gg%G8NYT?ARGGS|gW7lBGJ~+6I*u<-Oc%-N$#`qp}o+qk?40 zwA@vGbx%cQ>C*UHKq6h2;eiIVowX*Hg)ZD$3KIFWK^;XIT{b zFR-%n3d-ps%3Qf>oVGX&-^O{U=AfAWYn-+uysuP`-MgqdRvE^EnCtgt(ck;TXsjtl zCi+z-U6K{3_rdnAG!{5#?ZUkWhi$-U16jB zrDMUwiG;1VDXdO}uUIO)f5Xo7!+SwGw1=)R0MtKU50AsU@CocgoDP6akOlo=Fq{WB zd$)MEdbfG=z1zJzygR+Syt}+mCHx2tUf@3H1V_NhPzY1uBDf0{!ISVNd755cpbKI|3v2-=d@-Jl2bfqsw;r$8R$!)Z_sQ=yl4ly|h3Mc>=k>+cQl zj`0S1gS=zCq24er$2-m&?j7%q^iE*xbfR|>BdJroQ@uPd-y7={c!gff8|M`>K03`C z@0EHJyfUxctME?uD!nRdW{r1-ccwRyvA|j0+1_OD9PeE3Pu}_7H17iMLT`q5k#~uA zsdt%oxp#$krFWHgH9hh*-fZs%??%e~P2_h&Q-)^|&l_Ma%!m8oF<1usvjU{Gcc6EW z*N!@R2=^Re%KPj5_8x48w7*dfpfmJ?(J&Uq!}%~D?tlfb2p)$g;oqS4e_v<|9Uv33 z;XHT|K7+4d3pAolYzF(lL2wv!htr@6E`iHnHvAbLfJZ=W={oofzJe{#jC#5!w1XpI z5R8X&U>aNuSHP3rQ{L0wa_<@MMQ^3|H}56yWp9=Ds`r|=+WWh=#(UFy%X`oJ!25@{ z-uuw|*!#r$r?=7j%=?%3xwpyt(%bBP?S1R{eu`huukY{VH}rS&Q&|?Whu_R^?ziyw z_V@Ak^IQ4pejEQFzn$OSKiEIav;($1nNJ)Zh8N%^SOt%Ik9m)KOT52&Pk7nXPqkT@ zVY^k#=QNlHOJFT*hK96f8k1#04irK)Oat}D3t$QCxOSw#ACUqoO21M6A33n!AFKb@ zV|=M=3gut=|0F&&&d+(!jCtaEqypX^{lBEFq&ipnJYi_Org26@|1V|Xq8UTiHQv|w zN#oBah?mAj8fR-vFP(BgO<6&8NqL!Wc4K+D537h(=2w?jI(Lob<=(rxy0T|30VjY6_Ctds6gP;6y6 zIkQ4JJwrK%hjLOMiONCut*rh?p3$ zalYj4grb*$d#LB3orY-~+K5i$w*&Ckgzq*@(8q?9l!ba{pV+vPGQ61dOvMk1fYBwD z^Kw_X8KJDGjV05`Ll24Su-%=zbSU=&a-{S1ivE^}L;upMm?;-o;qQcF;?vLh&#$5` ztMu6T48&b4LS@KPxmFxs#;t1xZ%VJ#PNw1)Du!}jgZm)LcGPAh zch%QO`WpROvcJIHhL?lpZQGbH<8Bf>w7jejYcR*#aKDPk!3d{EHtmwr-_IziDj8cE zQ)P?S|Nqpw$7Yq%k*LPw)Vn&zSeJ@pl4!$H`=GQk&mDipl(Z}=)jWp!%V;CIK5E+t zJ>z!-6!&i*$K=Nr-0d^kH1?9xQ|ZtEw`e-3E&8E8IvnBLO&kV4X2VhYu^M@*uOx|) zzlQHBZ_&0%_bZ<3q<7=5`{RlGUrvh8D!#|#jbGJKcmE73++aGG&m#A)2*Y=FyY+-I z4S##%Z?7eZaF&JsRCg5bXOqHxj_)cH`JpjYVKzZ=?S-r@G^AfXk&rbfl;xgz9^c(F z>%LLk*PZ*OkSF^+Y41~iH5-3=wmy|~3bE?YdZheKNt?+%ncP#wJ?+AK6wg`VJ&L#d zEyrI5{<`5WFZ8GUzBKfgU0#r18g`P(%d2r$UK;nl#qs{a^&9Rvry{c{GQH(CzLGgD zlxgfebZ-1=(r!k={b%x=9b~Eo0$g)}EI}8dyrt3~qHm*zZC+yQ`b7yfnqnM361$`iQ?oy|?v5b=}qpTlrl2>2q)FECFRc zJxk?PJ%qEvD1O!k(~q=Gw!y3;TwR%bAjX{Phnb z%jUsM?v)*;9esNa6vG1QwZfA>`CrL6L1TqM^m{o_3hDy~k~Ye_&tRMEE@u=MM(r*N z;}O!p_j;RfP9&^+I1Q>`GF$+c!*%cgyacP^UHAk7#z0MAAJBMc5adA#OoPkeE_e`L zf;F%S{sX%|gq;O4;V2jgBjHpih6*?XX249C4Y$HQ@GvZc7vLN4NS_vP0Q7`@kPZ1z z1Lwkda0y%uH^EXb9O$*h79mSS z2mGa&vXG1Wc(}vA(_din@?ujq&c^Kum;?90^YAeQi?KOCcQ_gb!x%UXs^Chv3GRZ2 zU@6q258D;gmt{i{oCi0<1MnXF3mVb~sUMmSi{N2c4)4HM@HPAg)YlAy(_k{pgGKNt zya+GDdQhLD{^CG58b(4Ml)+5686Jd(;0bsWTKyG!99#vrz&o%JeuPH!7cHPIbcJD1 z1sB3!;0aKla_CZQYH$jShZ$lS@r3)}F?b$cf`Im3{fYVqwf$$om2eL{2P2-s9t34D z1X`}+g@f&Q_6wm;NA&L8fN z@Q?RL`X~6O`eXcjzrZi_i~M5$G=G9$=9l{w{^@?DU+tgaPx8<9&+-4{pYLDbPxoi| z7x|a?m-?6aSNJpiEB&kdYy4~d+5YwZ4gO93T>n;|-Daju9ZC8WgZe4;K?~q9SP84( z1NaCwz^7m>jWZ**#%sOnus2>$otB+yv-)!SmJG;-VweH*VHvD}U$awf#19Jsc3eAB zU`Go44^DyKWB-d)mXwx{GfRLNhy4EaKW-h#3h518|7H-YX5(uNI_t*e6hx`O_b2dO{yK^=Ijl~r==?SvOId?su+@1Z-xogZWcW1wI?p=(# zUGKA;^tpxaD;HSZN_t%}^0#KgOJ!WHYXB&J`+#hC3;!JIPTOe18x_jQ3gtLkpm$GH z&em;sPM51E<%BEQhL>gK7#m(BYyXhERhl*>%xziAAsx(>#i_ca$Eg_$J%(ULv0}aF5#ZRBm66xc9COw|L`Ea#Ti>*FU*64y8%9 zxi$E$=02mNM{IMF^B%ud@LSY=rnN2dAGM!-jGK#N?`l~mw4TYXJBrJW+ngGweHQT_ zE@v>dz<)*DF*#LKmWQVT5T4|H9{Ig@#9pR47!N05L;OWs+Vi}A0x%D$oUFPn$_X5jaF>iygIC!TB3 zR{U1u_e$=c`WNe0&*s*^B-?xzA|uSBpV@p|-CK=3UAuys4B6~cu#04&yBnKNZgpiW ze?nMftdUC3uDTabPyW~QXl+Y9?p=ALzMo-hYr?D&x2t5)wkF$Ex?>~Ll?jT6v#V4X zTgcDXwiJi{lkF<&wqjRNzjm9EW!sopgfS1MlJ2u%8LXh;R(SF!{|jil)qZ~sYJW$A z+N7?ejnZcbY@1!hCPlcUMd@n7IfQiZu^T9?N8w3$0bYZ(@Bw@RDfB_<&=I=BQE)5_ zhf|>lilGc9!ufD5%!B(ueb_tj75oUz?m`cSZqOS}gaViV6X6oL2Ij$o@HjjJuff~! zA$$g3LH)bY8=)Drg7(k_j)Y@i5}Xe=!tL-^cn)5MkKuc$Px|fzY0whdLPzKhS#UBG zzy!D)X2VTzJM7O2j5fyd(H_f32iD(aP<9UYx^c2X4{B^LY*$)e(vKA;T3_OQ%CwbOZljn_Ljfq`hlCag3Zgpiz!dj!XCDW<(vej69Px+f-%FrU>`X#jR_M(ln zb{v(dde959pf8*ZE$OxQ^Y%4sAADk`GFJrW!5nx5UWL!#3-}s-g8H;~d%}Lu26{ms z7zigoDX8sKyLb)U1^2^4@HhAg_M*M&09|1?oD3zPcJB(94Y$BO@DRKRYUAFAooT=J zfM$>ZJzyl{K@29q8E`IK3|GVTa0}cG55g1h9H=dP9}a$i@&w&sJZ)YzsO_5$m%}WW z2lv5a@D#iRAHdh}6V#)g?hI+r1`dVJaD9BWk6ryGTZdMEIcta3f63yZ6=2rpkyu@_ zG`{xbS?@XQABpuZuf$itX#I<`g1A*MvV&+9%(vcm-oG(>1aX_k&VFNT9=l-maI0XN zvJPfXtRH)^5=P}nZBAdv2D@g-+79NR^Dl$7@Ex>h%p3@efvGSXe$94p4}O-xj%!B> z>_~zC+7ys}==T3_j0@=mqPzUvqr}xWCQ3#8}_yl!n!%~Y17_OVRvbM-Cij7zIwjxOC zx_a-SH510N!^f-?tozwqo$L_m&&hRis+AQf-?m(-l>h%x#+_YZC!Vt&8Gv13tTQRt z9W%1q*!3DZSJ&AUoSXKo$W3b@-M*OlTd_54!l*zx{%jx3>aFnykj`cXhegVwOm zfkmL_Yt4PSe->@8+WOU?b~vEksck!+GO4;U7gWyoqCBX4odGHvw}PHq;cuJmW2`ZI zDDDbh>y>=;TgBlU*aB(vk9)$t&=$JG(Qp!|FIGQ$Da-@)rGJBe!Z*;ExbF$-BM*T= zkO!wh1)L9;!j&L>Of%B&d76{fo&;%Q8zTD>HhEu!bJO3Tp?%Nn^p9qC+UoI2uleu_ z>|?Za>#p?s2l%94irLE`_cD{7d*QY}91LBc7Yu_cw19Nz1ic^^#zPI91vB9W_zNt7)$lHS1Kw@u!O#qjg%OYkF&Gb3Py^H9a+nRb z!d>tn{1sNfDtH6lhmEiqzK8nrSw8>=K?WQSSug@lg<`0HvtSxbhna9K+yuA7y|4(L zfEBO`-h%b;DQt!<(CBv7a6ub540^!vkO$-7e7Fp*foEYA{1d*0RLb?lV z8Z*X-kNpVLC^x@%+!wV0{8n8|j2Cc_n)Dkve=m=Mg7Xn=Jc)UNwO!nJq7tS)#ZbV z>>dkemng93Y78nv+bGf`Etlp&ovu_EE6T4at*-UE!XgSr_hE-fc$$D613LYEYM6&= z>ZDWCj2c~F(+wsMORC7cvtpIyIM=D;xN}ODhjNa?pUUMBkWS&rjx4_8=K!A~LGaEU#0Bd!C$7#wnqU6GIs@qcVysV@klf;^O2uTf@)NB_gMYqiqoL zi*nY5@#&90JyUNm=`tsgE=470nEb3OJ}zzA=w`0u_~b<7RFzEp#kiDiMaGz@4EE8K znQ(rt&pMs2u)c|k$I;}b-lyr4e5$Z=`WD9u##3y5Ru-k6Xx_T&SI+%WU7#7MWhYWu zm(HU4xwW$XTh8Lq*(GCmj*l2m%r^11`RLC1k)2Mul5k~WS@_(e6*m|K<}4m`55^~$ z6NeQgpY2eWtxIX(hZFBTbGhiN!A%PZhukM$e z4YVg}Q+@=s2|1v8eJQAJZiG$nBWSZxKTz2|1ynvKfXdf2(t0NR6ShfjBuS(5<%&}} zY3Z}RUh#SpK81h7Ue_}IhmkNI%ApD_hgonV+zR)@3-C643#r$kPeE(QfK2EPN5TnE z2-PqT?uEx-DXfAI;NMVhHgleEDD;G*ARA7EQ=kU^1W&*>ka9hG66_AGpf&V>0gwx0 zp$M*n`S1X&gg4)|H21#W|d@Hni1zrkDZA>2`G zmA75vtrgynqE$TMEn}v0xtGuUg*vql8~{C` zH%x$Pm;~p*c_2M>vX@w|;LgKvdZ>1oxHB=Ni|R~_YgwayBRj%wru|U*s}9eC4REA6 zy@NJ`-!f>Na-bNd!faRsD`7qS0D6Z;cgTSXm-ztnfzTHadUBg%ei&Q z$gt~*f8YGj_UKT}y`9n;Ge$}GvUBtL4Da2yf8Ma6*(VwuXEyqU#$=&>VunA`JLcdX zo!im#XnZ>lcc+U<$9g){PYSGdlC5prct!uRVWud|zQcwL=|6NtUY}v3^qb`CJcp=! zrK{XER~2=E2z;-)!QrK9GUnWf?6K=`-r(8_SKp?~~m>@3`ZKkyS>f(~n6Hvq@eu6;|>*qUw|K$nC37o-5REZXQwjT2fw9|8evn?^{AynAJsQBUt)eco_d0xj*8g()f#R8xHWiuQ z!63%Y`IlO`^4mJ}d%6y##INc}8{DL$C=UAGj_}fQnZ(p8}*Al;q zXA#F5@?rcZ>oOC=wtE1-6O7qsAiZ>*X?vNs-^Mw~Wucs`P>!41%wRl?P87=d^*KrH zx7QWbtrVY%xSU^~lawyignQ~SL5eO_kaQ#-`C{kyGDxpvU8*7sKUtTmF#6BW=kn%- z{uz!Si}!Ycbgy|^(WTTU&NH%XdQZgvG}ufYHX#iaj((TF*T@6q!+|`z((y&uHeIK{ zZM#<58=0r^tUj|-l6w~{hZV3AUV}H_UHAY#g74rb*okt#KXid3pdSo@k&p+)a5|g? z=Y#6WT(}b!!}IVy`~W{eD*bp<*c&>7>dHVk7EXZzm;hBU1ulRq;4XL&9)qV~1+0YE zVI6!3pToEC1MET{KN`k^#tJvVo$vr?oUja*gT@CM53GT8@F{!_0rj#8q{5*v2vk=m zz&UUOJOE2!6}$&uL8Hsi!Qdzu1yyhv+zw0ON!S97sHd%2B8@m@#h1{&vSTz4#JESC!!zm0~mPzq%*5vIb$a5-o^_Y$lDjpKfR zpCFC8y$_^AJFqiihho3UVEt5QX2QBMuGh+QtvgSg6JXb$C(bvt=LKjcQD+?H{=#_y zZWa2tq*D&v8He_S!?WW$oju>so)xh59mnpBLuqxjPrqCcU;X^^mCx=}z1l0E6Z^h( z_TMXJjxpI9p#9(O3_#7cYZv%Na|+;BoB${rz|SoJ+6%53#ORvnG|mQW&WdQA3)s?6 zXE*o(erwJKJkW1zP6s^1@4&wBj^WPmOuxI|!#~3B zVSWxf#dG~p{)zrcroBxu?e}`}_5j-M7K}!8_Lb&F)t+ZUsoHd?h83Wd`mLBj)eLDr z{ASVDkAcZB8y3N8*Z{w#>;8bBRQkak*Z=Mm(75e4p8q$ZcfZ^&W(M#}-Mjt!f8843 zeTj^Qg6kO!nKhXc%=j;%MkwHLO{UZPH=b_CtnT}Y@Vj*3m&?p=ZL+&$bnlE@xqT6F zlddAS-O3aGGDEjE5jUq>_lUSTyTQ1NwHmm&RxVWF9~*q$|- z(g9X7u6Me#^akl(noEeTV|HtslXXltk0^JyRaAPAn?D?zbS_m?M@rV8q)WJYsa5DQ zx}tM-yQ=M}H2FWMDtteI;;%Gw=M5_UnrpP*?fPZOb+)CJ8dP%~1S;pPLHnRHoZQ~# zsQO`*Bl5@clhKi)@}shfLs<{tPqOX>$r=-vWvYcc^07`?ZVqT+D63N_OV2G?=~dzL z8ojBuzT=)b8mAvZInjB_G}3KMT#nUutfa{E;isy=>U2ki=Xn?Fp}3-HwLAWnSy{E~ zVEie6E>mUI>BY9JS{sGp?ZRq-zM@O>QL69KrJ51eraHnJIATaP+dj<%c|8Ba_$w^s z{~mnbT3MxB^Y}(3HafK{s|Cg8^$kY%ckwMKb|KYHo2H?D`a~GtxnX=WYD|9BP8&sd zgw>f{oAl}#rhPGOO@+C~Gqy+9UR)b-bM3`_5jV9L|J~2tCoC7G;j<4U4k~T~K;>eS zd8Wj>Sxo5(-%F@PAhxTbh zWIz6qyx9{uQ>~oZc@wVGz}wH#Q*D*X($>y{Rr{OVPHz0k+R9i1QhBD%o$PFFNtpk+1*Mg%1B=3E)Sdtp+XKUAOg-DARqbcYE@8JpCH6|YAY)tOroPO@t**8yT9yd|DEkMY=SwJy0e8TGx21J+rBV4e2u&9P~vCLsdD=*?UC}UDi$m2&!=hAa7%u6 z`K3vU`|Ve5-+zUZaVses7@HV|6K$)sE?3tWP`Q+SS8dh0sjiH3pV{IQDIYw%`U<6& ztJ|eGMCDe?@XGZk#v_t@jy}0B>pUY@dk;;w_DAhEQMxoGoNe8EsIj`z(44grIVa>b z+$(U`^C?X=CzRYbt#S8~C$2wM_-jM=MVkwtQ) z_`3yvE&gQMD)ZcRoypP^e_Cs>o_qg(f%U6$w=D7O@%~rkPq;Q%dMolO;_}KOIcdv^)2ZBdbn=Xf*8lXHk~8u-yIqf% zXoI-d)d4-f;u{Uajdhmryt?iN)$==`6=7_$VN}Fo<9q8JWH_5LtN5xcRMoM@Fj~)4 z<`woIF>henwX&sGsYs|A}{onP2+(Tvc}HU&U4a zcM1K^+{#?lI&3!uMwUIZ|5D;G8}!Z-?Xg@0D?zp%>9Oe)&h@Z~Hn;f|8)1f=$fSy5ND-J(k50V|B14!>53DVh9(KV%S=YaI( zB_Lh*Baj~28vRT9-Dl`I(sjC_Pe?bJ4AL#8GQQPV`B^YyUB-eM4|Zp)lLga3C@L)ry+G zQ(E*VeSPfvN{^4=GuRB@Lp|(a&7mc9g28Yq)W8h55@y3ZxC8D3+0{0Jv87?x+8Ja+ zYX!2KWrA#FgFyDNQ$aSd$sjw}ESLlL!cuqxK88)uu!4ChI1oBOFX#_BFa}DX3@(G~ z;1;+Wo`Mzd61)NL!3Nk2KSDFg!TxYC91XcJ8Y*EjOoy3p72E)~!R>HAJOWE#IlSP% z=&$tu=D*~>?62})@n7@*?!V!$@!#~{^56E?`tSJf`ycur`Jej#^f&td@;~>#@W1po z`(OLt`rrBA`#<-71@(gl!A?QLpi!`M&^Txk>=Nu6>=vX3EawgO2$}}X zg66@VL5pCopk=UcuwT$BNDuZ84hUKYZGr=Xw!uL`yP$n=aL^&>7#tRK3OWZ}7`tn2 zoXUJXXb<-EA?vO>kO{e9=Y_1h>O>*zLfOw^kJ$j~x8$gfw1FXD_nd|P`CbT*!l|D2 zoXNeusYA*3pJzyV5hpoS-tM(k0-5Y1mQF7}MukdE_3>{3{a5%D# zh5;}HWZRhx)8R6h1+wkj3ip6)I*VZ`JPUt=H(?!o0H44Quovyke$WYyfc|hCVBHY2J^|sG= zT5+rWdoRY{tmSs``B+`lK7Iv1LVeoCU7rdz?BSiPK>!_=G2 znwl9*o#>=FOr7zS&bsD(O+VJ0zN{6rg)BH0M!?B%3XFr(p$ewJG`IvVgIVw>ybfz% z9ee=)fd=%6dq6AL9}b1C;AT(VyVatzr(JwIf7+Az(<8$f)ZXD7>Hx+R$^-Se{os7C zr>Duiqv?aM#qDNT0J25A3U9-Q@G*P=KY@p>VQ1JAT7hg7?Vtz9Hqi$Lfou}P;Y28f zi{Ns&9&U%b;aPYM-hq!{BlwI18bfnv3rE0U7zssC3>8oV1N}jqAU>EA#5FS{2EPk#()toNw zvqMW`lH1^3SORat+wd>=4(c&RX#l%IDqO&cd7lU!$zYh2SB4uhlM7#Ien zPzIO4b#Oi00=K~|bDH^ek@L)N_HW@#^V>{*?rX+K594nsJPpsm>#z2MXyg}dM(cpRRDRq!GF6TX0NL3*lm&~#`6hr(gd6$ZgDI36azpWq6(6YhmY z@EAM|ufSXIKKSQT)*%&|!U518GN2m_gwZe!E`uxJTKF?8f@fe2ybu3?PvLvmZ5nZf zy`c{bh9NKlPKFYg4Clkea3#!xJK;Wf5MF_|;CQ6Q(#_Yy?u@thfqkJhbbyYK2}i;J7y=`p1Wt!3a3#!#d*MNN6rKRR zA5{DBZwPJ-<^(qdbAy|MdBH8gt-)=<{NVQBj^NH;2Nwo2f{TKSgG++Tg3E)M!Ii<)!8O6P!F9-A z#JIH>-F61dfyJ-}Hbc`rSdR;ZFas9BD%cFEP0=@@5T?NbSP7e;X)|}=+yRY?6y{e}a6`{HpBp z@%aV4Pqk01sJt@PuWw|2-OV+GvZ_umtthRjvgZx_16fKl9Q*JlyDDvo>0ecluQ!+K zzW81f8;4GcgWThLP0q}#C@s`mAQE#7kvJ>cjQ_gMj8oj>^Hnx{UI0J7Dr&S=ShBfB zWOi{fHLqaY!$ThwIoW(C@+sS98Dqh(ZMiaWSRTF~v5d`UZXt^7>nm_ir;i~?%zS{8 zp|wFnaJ$Tw5tH_#@SA+boU?7aH9%S1qq^J+H2=HF)CaeBqV_z#%8qQyvLQP;{d6-| z^0{ys#VVIhj-5$z^8-;iZhl~9C`WVr`d9NQW)DVOPQUVrlg7o$Y?RFNshrm(Yi%g& zgiuy)C~I9@*01m9aL?z?K^uuoni_AoJ}J*xCFc1w*XNoTlW*Hw_f&?O63({H^}P|+ ziEuxbd1tHU7gWz`ki#6Vd7G{3^m`G%!^%o0xfix7%=kQB(i%P0DW!2;^LsjDq=Mg~ z?|E?Zc5co=vfZ4WkrAI$Yn=8?BtC9RT>f1>BklvrYs?m=TJv_vYXWu8*5>WjsVtBw zQ>h=jpJv)hQ+8Gnw$exCOm%2Jnfn9t1*hWgu+X36YJN}8Z=NSIm#X(`*k`wMRc^0d z^qmVUkvRsLW4M2x@iuJbP2znGN{r6&`N2e5c8S+*$ykI8&9Ofo%1}L0d#m`L28yTJ zSq5_HJe>JS@@8PwAZ7+9L_gdo366IflU8`4fxzU+=e6 znDVQ8KBZk%+gd^!Q~_&2?a|@1BWg>oA)S;~ufjIX*N`mHvn3SPNFwFaGbp@A;YoM_ zUW2vpA^Z!z1&_XFZ|Dj+Z~|0=#z2pN#yu~?o1pQI`lc#yqLe4m!iJFaiodV7EZmioS;xK{ zy}h$v_(ll#_D;JSC;I-*=-WGW!e60R6n}fCPWL35`)yvO{%C)S>c?j2 zK)ulTArp#VPxHnJds?Nj1K?H-Z$c~c?hI=mP<>htU8qlO%}IZ9A8zW`0NnCnGF%7q z;7)iHmcg^|F06;Ipdo$0uAn|c{Xth41jFEXm;iI(QCJCohcDrKXhb`$_Wm&F3&+E$ zPzh(ld2lgY4Rhg6P}}|#JP&Wdzu|k>m-e|890={<5aD*Df59%|s6Uv@E zTKiO#?|C<-7rq(O&L`BKOK4{15?X|F3H$l2{B*M;O8cP>^4s&4%tOq6sKd;TsILCu zemAoxs;Ai%b)?x9C7Zr>MIFQ2GgUs+j?RadVYNBch53ETSm~au1%L&x9M%Ix%WDZ) zFb1mO*K8>>@G}SgFJJ28f8+k2+=`Nd*dMw7NAI`OrSpPmy*-_4ud(^e(PqpMw*g=P zs4)i>;78*eb@>`Q)wNE?j(^I=RTo>oi2K9$&$J~D?ww@$96VoLSzb}BQyD6&t9nkClvGWX8oES!>hcy=T~03=TA;&mzQHyIk^rC>xJIY)mG1&91bh> z=dC7R$zj zx<$Oe$T(mR#dr3;AB-P8qqX;K4!_He_o@9~_P$Qi7hF214RCgj0pWg@EXMP?dV}Ji zb%%<>7{=08zll{4c6E8>u*y23Q>Sd58F3IYR(I3$R;hWcrRg|5rw#d+tmn)R<0Ctg z%98Xg={XCxqUWp*{U_@=6T@P6fpws;b&u|C zP2E!4kqfHd7lP_r8D&uAeFmsJK0zKRABU5UO3Qh$ZTgStO$sJX)ELh=9qbieeg1T44=an(1?8A0}g^i;TSj$VlW;qfGglOxCF!SZaI5{%)+ZE%y27*mp@d z_IsRuQe)6Z_|*HMrzYZR-VdD{iLk=@J@Ji>3pKWCN_ffhb=x{`pg62`_TPvOqWOgQ zoPiFYOWOOhJ^OqVraQweoY#n-Gw04>lY8`C&TijN@}3^WQ#z5xbiZZZK(=MsaX;a6 z=m?c3J3dUFH&{zPYA#x9xyDEQZOi;XJ?<-op2%)+ybVM7^*7>Mft#IINxYL;{x#1x zli#L?{*_kCUH&K^B6fQ>cR`w%@wCdL!|hR>7>=!5BNNe_0%Pk?)<{!_;yQGqT;|5i zjv|f?|GnjNd(NVW)+tkwi0>fAwps(wpU(!2rz^G^cdM)Xo;tNXaaI}BP(XT1L4H*% zk%G2NX}_MEBUhSg4lSxJt*>ehgu6Z7zAB8Z<7ANmZQsfcAI?wZ~YmeBhP)=4TM|0AWlggOg z&L!79ZyS~6=55z*MOIE+mQ=0U^U_f{?mgf$L*2}DBThT3(wv{&Bb83QPu9({G%>`b zd{doLen>Z~F#6Qb_ei<(|B`jHFHCzKX!z1t_ z{2kta_hBP^13vYrG3*7c;Sd-ALtzwD!4$XvE`^(60o)4@L0hjKXYw7)>{UlI_owst z^e!)F7toxa)7P_&#_neP>@JuS!(9xgpq*>BuOO~JpT@goO1%l_&J||PPulYtoarhJ z`s}1L)%6y5`(CfhJ?&hwbAPkEYdA~&y7*3i^pmE+&p3mrv`qpza11Xk6=Jp$0HTr~-vM!xR=N6Q3 zEJJB&{^&E!_w3lj+zI)mrPcYRCu1$|STklUC@(Fq zEIBI{4_lUiKSFPj4C&Rf18B}7yQIpzC|LTN<~*YMw&oS2D<{VIDQQ#jQ%GEog)2>5 ztuEknZ8!fQeaDSy7vfi!)&nd3yMpE*HVrn^i9>xT++w&Z+DQK^dTl)Bquz*DDg~n$gNddn( zS+aGw^5bM}3S}KD888qOrVaO{J*;SW zcyDsLsE%q5Rp}I!@5*FT^j}@cW!pP0lz)6h1=S<|T+#G)GD<@k?UAE6s%+{!s4Oc( zWr=r`C1kj?*;?FOSzi>&)qJ1i9tukP3M<#t>#qGSOJrtY6eaI=`1_tXNivIQ5==$w1N7cbr)uRgXFmPBovm{ zj4h4H+*HOY+bVOD^39cl63U6Pi=ZJs9jzFYF9VFCTLE3q_*uuP}|gr`m45ODyUs}6I7>fpd)7IJ7E_JPI)3ft_ab&A) z*8j@8LE-<(KegR*R~}}Y|Ld82{EF|5$-icBYB(#a^A(i0t)Vj<0sUYA426*}3ZnC? zRzFl2X{J0B@cSe1e}+fkQFs~t1>Zp`<)tlTfYmdr;##I#Tk75(J>5J5 zd+_nw52nB{G`!=C`_85eUX9!Ba6dc(kHS;15?+Ee;O5~Iy6P;Rp{*&qpYZ!vumu_o zV=X20hCy&5oD8Qx5mduexC&;$9dI8!0!!g#cm>wL7N|#kXaX(aKsXfo!Vnk+r$H%H z!*sYD=E6^+DIo+ACn>_=Y%SuhGJ;8Ius z%V9mR2-#~1yaH23j9w`0frpES^qDnEUBn2FE#4_ zRPNs%{om;V=^CJMy~dW3?@HY~Fhe!N1jz5p}H#;0xry{6N z=ugkxFz&C9IowfhjpA-LL*%w|-0hh1a&k}cjN;1ZXjK-jL4+fG&=M|w*! zZg)leO^f)eEA19=k1o1mql;;-pd)FQHNd3Zpej<#8f9wF<=S#<^Tg%D=1^8|Wa!@` zLs?@YvP>qZ7V(?28$@NfHOaF=J^Tgy>ECA{gLo~pvW|&WM|e>wx%#ie$B*t)Frb9RE|PA8>|tIRdlXR7B-J;tP)SsN8LiKZ+mUh(o~ zb`0Bcs4{2jW9*mrM5v6Z+^J1WUVl_qd+6FSXA98%TKAx^lG{DCPbLmY>yMm0p@g=u ztU9p(Nogayf=#PLJLlS>+HF)y+H5j#8R>N#WwD$k z4YG06{aQPsv`O6G+2rS{&^?isb{$c)jz!B$mI?sZI8favfSbw7nIH#86t4X1z7J@()pmEMuB#my4QfN?f$IMx>X_=; z<)od`?;F@QyNDYh*##BG9zeL--{gB!P1t)8{sGVy)Nl5NV___m!$oid+zOAt6YxCz z9oE7+SPx%8K%dtD)R(HCR3A79a$q!!g_&?8JP)tH7TD=1)&{^(7!D^xCCr6K;Z=AY zHbTnL_=m=@H?)FwkOiZm2&&*BxE=0?Wv~`Lf^T7uES?v7z$mDOE8zyX4IY5M!g5#v z@4>gQM{m}eLl4M>sczKPdctrhg7GjN=E7sJ9A1Tw!Rw2T z3%kLAFaX9vDNKRca3?H;=iq(V1dS*^d%%8h5>&wyI3F&BE8zxM4$s1SFrL}_GOQ|g zzpm!)_0~LVO_}CtzwV5;+3YJ&Q`uNw)9?G}SsqG1u|3IJC$`!o@?=$anPb*ix=1fbiTlwDm z(c40C_5Bpz?)PlO`|oz+)W~MM^{xeWv3<=RPrdW5ZTQB!Lw&ouGn2iYJ^Y@W)Y#j+ z=gyvFn9WIs$MJ@{>M+9<&VC7o@ZHu{Co?i4_>eK7!xAa3Y)xZvTZ@-^BMqxCs*HckX7|fQN8<7T$u5 z@GX1~t!WoJ!cj03j)yT&0X3jED&}~@jos!%EHi+cF)$SHLZB zC)^7U!wPr_{tj=z=kOhDfxYQ>(%}$LKc_zI6gV9&0`*G|!)ka7K7>tBpY|&a)bAV! zec?nXgwtROOovOro-%n#{FKSU$T^dxwN9Fho-=ulIbqVC6?u_=nSZ%|g@2`gm1*}H zn>KV7vTuO9;9*z-&%hh79=?FDpd+g+^%l!ab~f7ci;wb-Cf!CuMLPW%EQD3C2~zi` zy@!D?8cJar%!4KHYimeW;b%SkU%TiQ(cgd5{QvNpv6GDMufBGBbpMfFYMRn}gZ^-J z28KrDJ#*rW!DsGd#<*kR%Kz}OD5MAV>^Y`0e+I@la;m(bZ)sKZe{MB;m&S;h$fzzZ zLF=~Tl;L4`BYe3?@3GG_GSCThbqA$iXXrr~=?%A*ezl*6*SVE(OP%3FbtmZ+8t=Ke zQJv?gs~0fbGV|>{33rSQx4eP@Bs*a1)W4iAx0VlN#8pf|Mo3e+8f+3P2cOdze}Rd*s`TP9~Fe@ z&Y4o2vbeWN7$@^wp^Z#BVO=(n|HNg;7H0Fu%{}wHMX}1^{RzUP!`Adfy>E0Cn!`HiqSOfd0`ith^K?Ss7h^hA6=4&z%= zW={=7bUYr4i*WXnmTC~Ww7x=pU3-w-DaW)Qeu>^Ks@>)|*V#@vIfbE|qwps=T3aMJ zQ=OdL{Gymy$5Xc)XK#}3uPa&3vf6T3J~OOxV9B^*`Hi;0Zat8Tm(DuVrM61(Qah%2 zd2N(lJfGr~^lmIyc9l*pUfF8oxY#CPhb5k~Eo;40G4<@wXw1}sNBn5RonKNl9q$a^`OkU)yT2;CTv8BK1_0*-Nuy( z*XOKLN5fT3xl-IJ`0PvE(hoFg7A{6HcHH4(`Q5a$e8&3#{@1fBj&l2>?6{KY?5H{q zPTysJb+%i5SG%Y%Wy{ic+3WS4lQv8|U7J@>Y;C)GcGq8z*%^;qvI(iIsC9X!cn{#aOKZj3ez$f* zrN7cJ;b!N2WPg<$*Z(VCE>689l&dXhEY=2&#qTm3r~JY*^2-WhHd;zMRnP5>lfrlH z$ILMNBI=UD*P7VDguls#KfHfl-~2MH)ztd zLFSnDro2?uRK`XyydPAhj4fnAFqf05a#3Q32*yp(OD@k!_vE=0tz~mI?V^&(s%o`) z{mtQ+`=xhW={g2fkNUze(q-PkHeFbgXpWvSHe-HMc*<|331!E~zWsMO*H9mvB>gmcm$(wx-5P zC*_ruSLfyD6;r13Y@wFy_f&q6MfQ!0Rp*^vQ*P&?R9@XYMH!>dY*(HlWzx-mFh5zH zr-FqX$#msBlxd2Fi?1u=@p2eGhb!O7ZLG?-(l72m3}5FhtAExrs)4T}0^?(~pHJ!S z%25~>b*ZFVO4=OC&~olo`P!$0^4pd>y@yfh;mV5hy9vLUxYu+{_)VPCniyM3ZX3$g zbo{;%`c*uZMV=#1iPNm1|dz`%!0gsXx>(PW||C zxGl7DG#npcvOB3?Ryr5dR933OD$Xj;Chg7K9ySraD}PA}<8IS5UIol=O>6yT8#tUy zo1lIsTL1M7uDx>O5|>}D-n+V5y@#ZD-sNGNeKDUSc&3aDTURHQ6%<#Nm$8+Dc$z3B z+Na_8GlkH!M2YrE{gf?l)~}w+^j%aKS2lo4DvfZDceVI;Db zbu#ItY|hqnX?a<7eo2|>>CCu@rrPAa>&NR#w`f`@4_%(=dG!nviGMob4@1@$COrEL z(ebp4uN!CR9T&O=f#TaA)E1BF9G3n56Dq1FDauxs>vvolC%3&yW0wz!IZpd*EYgKczF2W}<*-dNBF0q>8jn8bIE+G%zD zIM*7JOq0=&>ov-CBR=ova};tjx|rvW%NJkKmV*wC(5R+Yw>&t$1Td6XdZ;s z)wCZj590MA$Bltf(%O;sQ%R?;ggGvJM#)O2ZY0~KlFL`do_d1X?`l1X^iAz*(wDwZ z^~mOoYg?7awoKc$FnLU;wjufX6(1F4-K%uN_Fy(MMB6&GZLZ$9v0Jo^jaK8rby8Rs zbZ)ci!ZI7zuq^Z|H|bWDU0z;c<^5WnP?-N!d6P-{jw0R<5bskDx8+f3ky=OIDBaxH zL1~nYnOJ_^xWTkJdA8Ik&KfgB(@AON+KqNv@WZu{v774620mryP48y%RqI%cbzgoI zhSEs=p$UVsC3iP=Q=TRF2Xc#^?cwrwTh_72?|Z~uV-yS%g{+S-=kLqijjhaiG`0yh z@pI{7`ijt9@l)C;-flcC8PTy+v>i)~rG9?y&euvyE^{joJg*g5kEqAKsq zlIr5Tm{yzFbaQ9!htFZo+fke8>haId3Q4r5=2@%?$}^^<=^WiQIOv{@Ap7Zto{6}+{b|v&b6lL$dA5`Af6Worzv83)PKqPr{=DctD$6=cMEj94 z4_BX1$uyYSA#Ihi2im)JFinYf@;aDH!}6brZ1qQKk08Oz!Yah~Rm=Bj?yO;@{5K__ca$pWDhYiq# zaq=vU!C@_I_HXpuTS^%m=+EYdMT*ia+`Q#py}VyxNDLIj=P4bu=$^0%)G- zM$lYOAMBs9$xa5@RR0RHiT(((WoAMaoB^^S?uV^T_PVRFUCDkG9K@a#xCms2c>-kn z_z+}+I3AmU>;_k(drD9K2&9L$M2D0fIfgN}#_#un#^3E3(`n55SH?vei=D{0L1UZ+ zpfN;i`g`^H0d2kdrbj{T{$8|aYSS9CE@vV6p*-29HA5;+;fnxthJw=WI+zD{!vpX* zoDhr(P7F>8P7X!~TDzdMeFNTuSWpy)-~cz0XDKP?mbjDCfR+wy!+C z3m$+aupC~34X_cuhM%B5^DK=a71}^o7yz25(HzW$Fc%iXOYjD~4WGj2u~i5;a&I~8aHMg2^l8<35gF$L4`G#2ASe2Rl8b|U8x%Hm?AV-zOf5xjtH_zEYU#Qx$ultO(pL<@94 z8U|w`reQud;xp{PZzx!p^8gn?ZKrB~x))RMEEZxd*5OlpgCB7Kze8=Sg>eolpaB{o z6>4K0fqU=>=HVrL2(`8D!cXu{re6!xmemaH&>dO09d}?7rsG*WkF{8befSM0(?(SS zrBNG=&;-|_J918$3)D1~4NIZsx zcpa;;9lzjL6g-!{awv&PXoM!X65TKa!?6ON;d>lMABu{&1WnKs$>@bl48t9`7ZdOZ z9>X*E03Tux8n{i}&dm4O&mF|voN#dU)bp4TwUn6? zwTxGec~L8RmGaDsTFcCeTGzY8yVR@aHS`*JjlCw`-nRkVErPthR;kEQyd9A&x zy*6Gu?;5X@cfHry>*}R=-Mv&V&FkUy^ltEac^O`BuaB4MWqCJxeZ6ci$Lr_y_Xc`{ zy&>Ms-cWCtH{83`8|B^R-R_O|?)C2XCVCHf4|_AbS>AKrJnu#CC2zjB&|Bm!_Lg`{ zy;r4dKL+>MRv-h#L)!XKM>V4*I_rCCUcssqX zyzjg{-uK>KZ=bi{`@#FsJK!Dke(?@@zk0uWygco@zUTXX+&{)I+@;CjY_}XpS~$hfc^uKa9fd7>n^p4&P|+NzK1TJX4QpJK|Y3 zH3P$9!($_28sF@;M1N_G;O@kZjj22^9Sg7oZ{S<(#V`03zr)Uiq_NQKTu7Q5UFC?P zsEUiBcHwsDi3|+I2#myScrr3BeDLnK#!H*awghF7`v@})Ph%dIB0A2X8Sk8B%dr+8 zVLRS2eYZ9GppF60^eo$lmgRUxKwBK7w@ol|fx;9sWt=pY55%3gAG0tIi}4k9V;_FT z?>M_W&yFaKdT53#aVN%MJT5RZ_*67KysI(FL2ak5nX{*1V#c0kP7Bk+`)c}k7cgZk zwG%GICVYZz_#8#U@fC8%JM>IhY(IZHy#i@&di-jCI+(IqX_i$(6LdrhdY~6Z;7;6w zCov0iu@IZE8%{;~>Ek$@0JS5PM^#*k&Pc@|48sVF#BCUjv6zlon2ROYiErUuNW3^1 z6;Ta!&MqVtloz**=l za$a$kIj=geIm?|lot4gN=N)IQ^S-m*`M}xWY;-m`A30l`&z#SlFPxpuF6T?>P9sIlnr;IlntG*K^};0k@!A$dpH!JDly#!P6*X##q#Oa-1o%&u7`2 zScMO<3BTa1B%Y;kJ}$tu&UH>FdbW3UlAUf&is{+jgSrxj99w1n8mN!!&<{6Z1k}#+ zGFD&}*5XtAfC81tS5XcPa2e~pFL5t*FEi;-*wjh9K^$xF0n~ouRw1v&=_rLu zQ4bB!6wT2V?ePI`uzeW&$c(Y@DI=I_Yz6x!$hW);qP$D>9ck!|TQCt%AUZRc@+H-W z%teW+oNG7_AN$+=FZ`YUm*&2Bj;SlD#q#Trg#ow&593M9$GdnB`%s`7Wm^=+$ta2v zI3H>&QQL^xLe#!643jYp(=ivD@G-VxufNaV@Bic<@PGCX`oH*x{9paw{8-$N9}_PS zFBCsEeq8*-_^I*J;%CLriIDc&GUF@t*M;;=SS;`^w?jK=oPW&=8$55W_JV4`Dq% z!k73B)oPM=L3P2`Art*D7H{GmY{qtcg9C7C@$MGRLj^QMGhB^c=!?7XIA&rF=Hva^ z)FVK3zTe|_6sbeG6G>=}cDN4RkcPn+iP0Df)&EYxQ+N??U_Ex=I~1(Tc^`i={#5+w z_%rcY@n_?+<1fVL#OKE6#b1oS6rUeo5MLNy6ki-)5?>mBIsQs~S^U-biumjCH{x%` z--^E-UmbrpzBc||{Qda)_y_TA@lWI1<6p#g#COJb#lMVy75_TEJN`}l+xU0!J@N13 z`{F;ue~kYWKM?;pelY$^{80S2`0wUeFlJC%f!Hw=+2`?0u=afFXG_ti7XvX1E3h4h zP@IR%nrMrGn1Fd$jXfw>n#W8uMNf>zbgaa7_+=O~1#OXq37Chq*pK7O^3Dw!;m_W$ z=}4Fi{QLR;RtorR&R>21C$(ER_{ZLV_WqA;t1qht5kBu12-*fS`Kh+S*1yEbf1KP` z3$)^(drP8C{X;isyRmKT%fsK*e%_IbhTA&xg4Ie=`AN&VN0wCymt{qk*>?6Jk!5PL zQ9E8ie*Tj-Nwve#G!j#rWY{?TKaMw|bEEczZq&2U@hg>7l-Ai_)Yro)BWU>C4F zcYM;ItuFd~yDwYv#Y>Zx*+<*n**!C-A#--;u-GhDduKEryIk#^(d9~0+vXWvu5{IM zwRuLDUmPya?R%hiBASQeiv;bK(eNtoDjkW&yhqV3BQvF!eWBgN#oPz{Jiv2OHxB;7 zU(GpS+VPw69ov^$?RjbkR9KCD(T$(&S;kjP*hYla&Z~`6Va2v(XNBi$HhWZ zbhvhY^#M@+745sJ{jz;|^0)ub=AbSkar=|Dg?Q+&*M{;A#gl0NC(II*AwcO!xc3Q{ zML63RZhd%7Mce;3vn+`)$*ExH0RK2 z=p8t%PvI2)Jn{hLCyzn-K^3lDUB44Kzjcm}C_tH`b|<>WFkW|d_g>lI_}tlv@w&77 zrgX|k9oX00trPPrY8{u71`gv5Xg${;88;vsLof>CFcH%*2Mh5!KEe)sgMB!NqD(Y% z21=nmnjsmza5Emlvv?lM@H#f4BzbdrB%vAF;5wwDHwNGyC=Y)dJ8%fc{1%U$iwbCh z*64+P7>|cA2TQOXpWsKtxThDvnK&PMKIjHL6ZC7;qlesfhKN_7`exVt) z;?>w|v0yw*JNt^prwnFZ31)Wukg+R*@gRdyAT`^{pN|0fTR4V=8yUsoSjMr?`R|$Q zs~XqVRGiI`E6Fvf<+|SHp%T~Id5OCEW6kx~gk{%Y3SPh&T#F^3dtNC_Kp8Xh$3^5P zs>!Tv=KL^YZ1T4!@jPC_N^Hd!*bn8g%0DZj9-5#t?#BI?f~PPCi=n*nExe15unYT9 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

    #o)h`TXniiO(atA4iXV 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

    a*{J>0rgmri8{6wUr}UqptdQLd)c)y; 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

    Uuky82ew{Y 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 )NhqCXosewZsIk7Ad&l~-&%ut-Sm4mD6GU#m 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 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/Scintilla.dll b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/Scintilla.dll new file mode 100644 index 0000000000000000000000000000000000000000..97a60ede234229f7da4984efa78121b90a49225b GIT binary patch literal 279552 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�m}eAwd)_2xi)r|7ZPwmOAuJaFbTZ6Qif9hBvVK(KrSDuC{^e z1-R9g^HjM%RFv?IZvJr<9XdPV2W7-eYZ-&|iFU zt2ZCY^k3AV_5AsF}=L)R*#3? zV{JD;LjOIB3D|At7v5uSuLm#x9V0H*_H@2J)9m}Jd}p)y3>I33?`QGJC%VAC_hZG& z?#TmPN1Ct{y~o-(&nifeGO5v=|4!TYO?+pQg}@m~G!+E0|4LxSSVjZqskd9lyFwCf zi})zLt{ffP1Go<{mSxr}c#_l_dXL`SS|&NL`78#=?1jn>t>3*D=7slS53*qF!Ln>j z&dyT2^m`xng62n)eVTB!&Bq+W)3%Hboyh)fc%PJhQzJOZ_v*vJ~AV`yGX8 zqUpgOMJQ*#ps+&G_ffBGT45gM%#X5~d%NXTk$kqi>Gg#v7%l)F=F=Dmf)1Un35sy7 zaJ6ds{caDH*(4=ye!r(wx>H}pF?CMI(~%-Mx-zy=F5@0D*=3qw>=WjFW(8(8DM*V@ zMs*J{&PFAVPVWE-aRd9)JijUlm*e(4qYnm7(w!O&5H=csWc3kqHEA!^UmfD9}8 z5bys4Oes58tN^S<{et;P6WL>cOwS05c0a`^L=)<&+1wO!D$ag_60&Rf;}JpBSz^qs zY=@?7ZumN>VFRxPEv$y*Zz-keV(afDB0%K$$264oQ*0wQI-AHwU-Z|%RHWBmLub)C z;&8e{ubD0SYi_8+Rf&Z6u)&oAgx)wYR~40QtC$Pit7;bby%)C97nXLU)*Cc ze&&4~4GkgE{_9o6^Lx<@V%to#blQJr#ky_QBVrhvQQyJ8qE*v@J^>hJ>Y6{lZkcv~K$`%IhfVU5$=#_%=nT7y@OZ%&?X@NPJso{!a^-2};& zeUavcht#zBNF&-F4CbLcSdBWBy-hk*-uRdw`R*=)d)JXcZZ85kPavL}uiMY;(7&Ee zdu5L!s*ABRl0DY)VFOBk5dS7}(236h8E%3M?o%rylUOc5jJ_7bA-$=O9(O%`i!E<+ zFkDyF@1^^M#T}$;2ia|X6NP*o1qzn+2>gu5Jmk;M%;UHk$N=P@FCfv)BnrQu#yhcXquYObQAexb;3eE=QRp>Lm|!muOqv|W$`ky3Tba^79Pp(%snFXYm1=w zp}FNV^r^o0Cp4*@3NLhrM8z)0dW{FTgKS|sTd}YZdYdgP;?jN(&EX|&cdeWDW|P&) zmhSQ96>P>HZ*C0ai21wJbabpha)d*|>wiJ>#xsjJu%#O>15Vb*txNtr^*cLo+6oGP z-yf{14U&lF+tgL(FZy=LnaDKnaMta2fz!g>VeT(HH!dAu5HOnH>g)wIA}M+O=E zeSyYbJTl0xB9M;*;``{w1Z?6nu+@Ra`xJI~JFYFt;LU)DS6hw^YJSkk{D7-isPhJH z4c}Z1UsWhWb+$kQGKYsviup5Ri}8Fs$mg5ng9uEQNFTv@$2aPKDYQP0F~onb=i8={ zu?L5Ppsy5yx_1r4kLk&N+VV!L(N>2vA1K)A>Ky_663cQ&KFbg4yLty!n(TW8s~uz8 zpn|!6Zi_U(76;IAwtN}_LFL}?s49(X=(ce#O{m{)DYjK5wy$P0-!{gx?V+q*n#HP* zfn__bPK2+;8wo@OkkXv$uD72Q^th$PsFiQynd^vUGLgeXpi7WWwz=nO7@>^ zP2N#H-hAo=;%(cnqxzhME11wv( z()hc1uHgN0=E{-DCG2NplcN2b^k<^Z<9T|}C;9GgO5=Ucm&U)!^P_BO{2jzKk=4;d zrSS#)zWcvR<8i_+`yHDkc`g9vdY(IZEc+S!E76}BrDZc_l-t|i`cnp!|2JuJ`1W^y zj|w2lNF9AO=t9mO;^--_0(s1@J~-~22864lO8{TJXMk>tLRGXfHn6i888c>TZrR;D&6FSehtMr5MST&=Kw@x99h z$1U1=j=3UeEhL%6JU-UiGWHbmA-2Ib*4b8r=l>+z&iYUC)3pHMk^K??U2=HKZqeHt z^p?@v8}-(zx3hW6-O+W9es1YHS3fs(ou{9F=~}3t_jhSickZgLqX5s1S4#PIS*r>$*liQ@Y-#pE9bX zb&WTJ@M4~2Jm>Ko&vOz_oabns7qD67WVhmZmS+KBr}Dg$=UqJSiyasLUr+WoQ%sxw>d`YQXTl$~o*!U? zC!pnROt7(OjWzksDNF?QBPB=OeU<6;|jDcU?^&nFtF6Zz?qXHxbXM0Ks! zoAKcqy%`_gpf}^g59zH{a1Fe{hmHEVrR!t*xvA@B{rpSUE&6$X*KPW_s%xcwYP#;w z&!t^lsVhGGxPGd-wCxE#)P5=WQ2VLi!%yhv?w7D5Df&Z%L96u&Py#*r>4Jgd|hEZyJ$YnV(wEY8o`tUIT9ZB=+~bD|1f68 z5=5+hMXMffVgfwIaabt}^W7n^zMY1d=z26Xa$VzdU5a|+&#@yg-P%nqInE`*(N(V~ zcw?^KLoTy<0L$Hq&_6-30G!&vce;Hie}!Jr3Yq#Z)B-3l&jt8>L_1EgAe;_1Q!sc? z#-EOJ2Nara-AASYI(jv9dOTHHl34&BUS|59`QHuMeUFZIGLp_Q6M0#TmcTLZlV3g+Sdm06FiEIf3%*h+l1toSrTb=lZdM6H8n_les#vSWF7wOZtgwX@}&7S@dTA;?az*{NR zopYMFS>~L$KJUZ`q2D_Q1~_B;(AfV3=7cW)Au7JPn2&<)g}wkNpXa}c{b~KL4X=d( zB85x-^6+wH!iqv2-s54<29}j|xC+ekOM-^~9FD8trmopEVX!eOIK4eM&@b4+8*DTW zJQ;vwc!_lQt==@jfY!MO#yfWqiu*!Ubw$%W_0s`&{8!|Z!2E3#gVka1+XWxul*gY= zDZ6~F@9Fi-{DJvFfgZ$bk6F-8=NH8%hZeCZs3-Aj?M?G_8l;GStZIWuI_fllX$^CL zJcql-@3b1pzf@)h%ojJQm4Bjjk3Gs*m;IS9Mpa@@Gy6D)sd_Uf-LYAI31n4{0a?vH zfx^gf4jjNTqQt@4iMQlYC^w!##_sV>k%dZN`s z*sokXUe_C5crPWoS+aq85nWzy0OaB#QJJ~M0+eJ@gvs;esgglWs)sl{kDAmEv-=^a z$<>F{M4s_Ou$LVrb4X3t@u%+8Eyo(j7uG}}DPdn#J=QWs9jx13!%|Xv;@5|@KYXkG zBUthS_nf+G0{cR@G}*G62CV{%z3&j%_{@7@Z+^7CVvG`K>>)n^So$2n1^G2`KRx5!b@aGko)UYo&)ep#M!RK;>8qvE zaaEdiliCIyZKYz~=!*$xXrr{rhgmv5&CX5B*p{oRP3D0BFZ+8hq>lE?E zsXp2d^x=t?k_T+ZME)`!a~4^X%=MIyL#ElwMqJfZEV=BmE&|M^F?t0O`8JclGJ9n< z-9vi=?geItjN7NLV|{av93OnJu7DWHcq=5rIxxJP!d0_gzO!Z($n!FD;2v>LVZCM0 z_S^VsTrIOGtmV0j3)qDNfO7U5B%ZuO`E^qXj8!9k2a8VD*6^@i`2|B(elDsPtN%@Y zKeqcQdy8O`s8Nv}R62-8dnNsTz%PH3JaC(O=t_8l-zWFUSjG_vn_~}ddY)mF(NPWH zDa?)zk-UO{8oBzGF>LAg<2I^+WvtDPl-J=Zc5_CoZ4QJ~#-k5}>lp7-iVu@QSK=iG zp46HY*Z*Dh29X1K%C=n{z*y(b@&v+mhe-}k6bT~C?kr%zOEgYC>D`tzZYM|CUSMm( zl)RMyWpD68O#C&jUgl7ha3ItkbZ`Mjvm@{hP)~XI*b4R_N0s2xQ{%o#SEY&4Y@Mmz z2EQ8ywtFMMQ7dXk$V7;qi9$im~7~&(@l~cb|4juU-(4eFv6XC2`BctnSIw--D?h z>wcsOtLF}K!P+jjKqdSyOBiQ=HaCxE>hG>|CtYpyGWa6;vZvlI1l{aP$3$PO{&!{S z@2Ycuc#py7W)<3EFR)s-+3bGy7kL-%bE{wP3HUaI7CxNtHnBl>{>oN$+07DMo5yQd zuiWgu^kZ!p8+OM79tJ#^~6cK7=uSOyz3i@S{pWB!HjISL6_E>sUX0UBFJPrjTvp z%aW^ad&&ew1kafojl*MZp+Xuf8$M0GOs}qQ;CF4qBD{VZ=JH34eL$iB*dXI2r<#as z-K}B}QiQMM&3%pn3!=*C#eCDBA7k6Y9-AwXe`?p#DV z1ZWD5Sg$ahLS(SmN>gtUkF1azaTKa@3%YP1AB`R%oxq2Yp;zRKG;E8M!vHu99S6>c z3kstpm>)&cr44t7kuRNcSR@MNZ|G2j|CD{6u! z#>4V=lGQc^q5R}fl=J)K7}*Pq~y zSBbafyra5G@EgxDF^{vs0nU{hxt4zNCHXd@yslEB!ZzVMuz&PMRv0nZ<4_Jsk0^bP zSf7XRYRs2XRG(U_=d0Ie@vFv9q}NL`Cg$H&LPrP-q&(Uav3diST)-Te%gSBDNr6ag zT0rmDJq>bEsK_j_y&tnItYVy-VihKGHR*)1+X#<6$YI(gO{bAx>_N_np33d(KF{)R zhsx~X)-euX2v5FhnnzOg?&g!pj*ggouB3@hLl`CMyu3_poG=Y=*vr0K z7Yzy2$FL7??kRkG8Xw_1&;M=zoBWTPSICZmeAAMzt3^}&Tm5^RZV6t>U$@@4_AeH1MC`)LjrZq>O{m`I0Y$O~xdAIg&_E&fRT^VQ3L#L`SGKTp3M z{}}y}L#xu&sZ1h;J#9PI0_{IH2X9>2I`*QbxD%@VjDoEdfon+heq?>h9or>;dU%d^2*-WhgkQ#J2MuOILp@UHzSO z^>@?N52dRgNmuVmSO3=y>FUQ))j!0g=*FL5@JMAg#urcpFe_^xX1?QhEtjr=)%RM} z5-Fx+``kT$(nM}An|btg^9kBH({!rdZ{R(hXv@^MNu6yQubGV>?%vNnvP>RlVXnjtLldZcu`D@q87r|&ttYBXJMXLT@ zDqYf(*amF_h~&-^9m^2WMwddZ6okcdTb)b1Lq$hB5}lNZYg_&0UNQfOs$>U7rQDYv zS6iP*jGFdSa_0)QxIDXxzm308VW%Alg81YR_RT?8`h;1({Qi|6f*Z3-SG_e5+p)GT z1x>wzmXbArn?8_cOgy5g#1PxEQ`NuMt)h}ZqjeHuLtb%4PuCR14gflvd1W(nLbi-;52vvleRwm9S^>L^F-4@k-lXh1;r zVVmr*m#D4${Cv^Cvd9S*f2gb;2e^dXrV)92!(*U0K68}1cA$7| zV(Yn}vR?~m+{B~`CwDKag}j1=7@~(-1{ugK;y%y_Pw}$+RT4gfJ@4s6uCy-9`e)g&Y8+r79|c$Qat=8 z^G@)}0k$^H7-pKGHdB&xKFLSj`(|kEf`P92D(LFIy@V=ywDftc72@1gmO{hf8JZ{O z5g)44K2Wyz+2mO^2yuyX9}3dl&zMB7KH?s+IYGn_`&{xH>Wi6J(!QWd>-~ABa7;PKF;UI|ed-My%pb6D<00dBoa# z9_`&Yo732^-Mh!@EOW%{Y#5TEn-&A5L0RfKQXif?5Uv3n$ZV1g-|pjx*0}CpnOiCL zNRA4^e0|hyHz1{1iGI&?rf%GYt1SfsIhb?64x}AmOE&!T+3irEeqesTI>JJQY#@YN zBn3Q|jzRg6+zE>KA`uEb7Aos^GL6gHhA(;4!NEj`0PWn^h>;IOK_cgl6@ywd}0rF zgG#c6W3xAW9RO;_;hLElRGF)EpVyt&*?&{GPT#0xK4Z)5OdRG5_tD&~fR&jkQAJ&> zmSL0FCfU*F_jBOg>f;nXcVal1=H3#l*FZDjZp#Mb$I2+kEiFQBRJZBSAI}cMptPnx zbd5pxy}poQ2cXLHJfl2ipnoOLhj=dLN$?oOj9%D_CEVOcza`!sc2DmyIk68JLj4-2 zjn7PB{9vdXh4WVMDoLm{ybi?pFQ|!rzfx)A+2wlwH1CY#{?xHy4kn=TBp4F64UN~V z3Kj)5Wgd3foobaa_W?`fu)%2=$Boy)W2)1zd%MSHKlli6lp}^bQv~+Zz+T}_F2>s? z;F?gr#u^c!!TT#eu$n0+KR8+aXM1~jVN2~qqxf8~@oS-(I7_9RdC2{zHBl{YcM82e%IR? zZXi`zzsrUcvHEC4lodMWYa6y>sZfIowVVfOGQnHJKhk)0KBA*IVtM1c^ASzO5mk*W zSp`{PFO=-^S-q~2o*YChDUM*0HLUSa1LH8iY;*eCC8_-rD|y_sYQw`24h|U@!VzOi0RJ#3EU=b+>k<0hW$8 z?2QB_r5_vuqTSsdWM>F+&mka%TsDRvEr)<;kH6(I(E?SN$AhoU9K(sl5xzO!OC|yK zCX{I!R{rT#3k`|5b>ADr(1T5L;t9@P)0x zn>iLHn(A~ylzY`5*rYecOJ%yRgxqX75(BE558>@I9n@DxVjt~NL2qT2GG0ff{W^97 z%lGD8kU77!6-+RbWj;*W@}O`jN-{>=QMG@ zP!I<#lLJu#s%Q6>2*O*|3KJOUr~Xw`D62DQ3x3Jw$3r2_=tHw~h>Q8Ui|YZxh0BoX z)-koj2FTRU>eUV|+OZ-zAY{Tg2+D>g0urE|QwV zZU+aM`Lc`5IOO<+qO!3QY78{O|{fVix^S&DT@f%B(G2t-#WC`u={^c%wjfM zO6`5guc2gSGL%94_xqoH{%4Q>>C;b7;u+)1`|cKvQKtO1IM`^rcyosbHD>Qk?DTBZ zExPIe+m7n(#r(;7yBXS7-6nQw`WTZEl=2ihkJfuwBp9E$);_l4k8S(ZA>OuH_w5q?Sm7TZQeEwC>VG~ z=&71S&Q4xbiXfmCeS9&r!*2)gAdQi|4N7aH5VDhN_^9PRY8lOOV~;Afl2{g}pFu~( zw#EY2+Du|?jf*WZx*ys-C7+W+vmsYZ_P7%5p5>M6wx(jgqu851C+- z(+bmZr-A&~>0`V7&%NDqSn!Ih(L{TErZat&v!eHC39L8KC!mhR-QrR!yiXn!FaA7L z{X~1>iM;+-`(+hU(;$pl<^wXsaokt7)|S+K6bpRjrsma@V*jRO*PJ&}9@b$yKPS_w zlX9xoDUj`FEWJD=Uo|bJdLPDi9iCHV$10lxMR4b5Ub6caYo8*2j!nuh-Ko4;xK4K2 zaXgHx(g^jgjv{vsygw}Kv=yU}b&5`Q$C3lXT)Ikv*!`_S!KOd3+0}0eB;|016Ep1; zOW=GC~Mb0c$X$N2u?=(Z@UD$7c>%I<)@LWS5;- zr1P&wGn@3z;9(wxED;q>Gh*6zMEg{7Xa)VD#w}(9w4uY!P-$Kyqq>#>yx8SPO=`j{ z%yx3^VOcCYnFNOzc`0~143*8%F6=Q~N9#0)HU`-8F5(U`^+HKY@enB}F&0~|rTz|E z%8!!d9+J4zPLpkHN3vxHUIv@gZ+59k%~V8JOx6Z2an|zT4gYOXOIvJzA^ShG=Dp5FK+H@6$%W+-gdY{?>es$lFoB zxz2sC#0*V&r7KMN3#;OotMe(BLi9PTaTf~}$UbDX$el*MZNi+TWPWs}+{EjN3a7Z_ z*JI3}L>5}Tw>>DuT^xRv_6&?oaKsQ3U@U$@5o+J4zxl-bR~~{walE{PX;jG0AF~z89Y&>lJTkHKb?z~HlT2{s0QF~_s8x8|`8q;32?(3Y}Nu+z{Wp)X6 zv6zwkk{o%BW%@~CA~eV19g?2)SSgwaV16-`N%A32 zC6tR-+QVDqCrZ^q`AiL2N9CBZ_FY8;Da2!8_F8>W>>>DLi`GHR;3pHRxil2x8q`<) z$(kuHhTaoBv_CZ>#sY|Mzf}+w8t)f*Ftv~BE9()H(-o*DC@Cj!yb@abSnR=+?5|Z% zN;@4zLYqNjRP=1O0qW4@{sR4@HH}NgXRb1;jir&AV_YHz`Kf>^uK59BI;bq)cE#<7 zX!{sGP&Hy3Z~gUfhOhkl#rnE`W6WJDfu>=0$bFI5Fk;l5tqAquq2#WpTW4~Q*9GqM zNpKR2Axq*Mf?_*aEi+3=hh_TRtfZ9Q|~hr z;OX-#nozui1!D-e8sjRx%2#GcTKtmP>BYz&y|*u!mRgJu^zYMCi|INZhV2q4eLj3n zYVn-#{gl*VW||&mZhA2j1rIYXwV3IPe?KF&_>A!V%+%sD0|h-ae--!#Zqy=ZQ%pkz zemo1@m+8kjCQbT~v|O@a+=dfa;!TxOyb}V_NnwdWsc_ z+B=}(6-dKrsSM4g>lLW2^uFjY(GWO!j}*_lMe9#|rexI&x#o?h{)Z3fN@Cpcdf%kOCW zvNNSowJ$kC(yhIA9*P_Eo$CWn@qu#`*uHGK51gjJOzmtBDGvlVTFQi^rv<97j=86i zECIy|^n%}}k$@^ptLkH$ytX3NPA7XJrXvAoQ$JyFS!LC-N^L&yU$d*0&2B2kLHzAu z$gHYmvzp~1em)_|J7z`9f1DPJCRYX@ECzwnEKnNDESpv9ZoSYXx5?#vQ%W3q{RRHD z@n{)$MOH8%r7vU2WHh;QVd)w!G{O5zw-KO*g>gvf~p@Wg-o^n6E>jz$`&Bs{|teUN3$G7~vPjAJwIK%aG$?!FacGSK|Wr zKl5$gV<~m8k11z&I z_;Qoo3xJgz6Z+Q^nO=Oc5Z%3?s9FsczUZ*<6&Akqu<#}ezwof|l@`9Nan4~e*ICRJ zYO+9$D)1AirI#Htx`pi-7z)A#Ff6LU&En+8=A2vJK}q7g2PgyPlui`gphUNW{4^gsZkgP$HAhUDEpa+9O5g zuC}xlt&a~JDcWcJ>dF6R-r32hoQ8ZQmwE;S`iJOq%Nr0BW%Z-F%@hM&^^2`93Hw#7 z3e~^Z`f@k#t7+5l9?@Ng(jQxm=}vC&lZ z!F-Ej6g>DA71>gG1LYF|gT!x0SJY%>w^mT=O9P63J18B*QE1n5_-@uFFuAN> zX@pv>zp{g2l~GE znTCpa$3j$pI)x}W*0ZQWKVdI0(HhFvF#Xzyq^O6_1BU&XQDd5-4z-|oexW-AtvJEc z7Qd0Ps!htag(I36K}Rx`t-;#0`{fxnd&xaPaBpkA>_ZC3=db>y1qF*K;EZez_MVT*D^fN`#gYF}W4l=)3(L0Nx2i!Xp9b~>t(O)Qv zKH%P{XlbgTwp`Jxi=y|txT1s1uT%83MbZ1*pZ;9P{3b;&BD!JiIS_jib4n3AMR1*4 zc~XJc{qA8U3vzWzc63p$d)?=Yavf3h^8|$S=yR(S9iSY+^y_}80C?jElhu*;9NGb` ze_Wwcnm9!IjriP7_Ak=Y2)dd)*(03z-}L{8LeMpX*n2kh$^C z$wkq7+;;|uBN81z=Ibpz#j*S-J{)2<$5aQ;A8qI7jnT=pl zqq_IVi#tfZBi%Md5xFlMuG?`8?8$4GH;vx-*ldKU3POg_Ha@@#BEY=|J5KJgU}Z_; z$%t*6lPU11HRmE~Z+r|&6I7x9YFKh`+6LdkJ8oS}$wWUth1xx(W-?UqX6X)vspUeJ%XSTvgF(mUl9W;g-#a?m{Pz^>u9uZ-@^cweplGJ9f-B^5 z4}mI)+BdBwO{!CWeMn|g8}I2({S8C*TZsOKAqOl(f5VUg3(;R6(!Obs_f)6;h9SST z5d959erF;28-`4SAgNCMRY+(^33QY||7%!rX`p-Hc5L0&y>1pmN6i@pWkqW<5*@Yf zD58)E?)xtw873-pHeRg=_Cs4d`#4`@b)0?By=&EhHOEHWt?|aYn{p;ZW!T%LS6v4@ zl3tgxUObv^->8@R&Asb7d7*G`!IHSt)x>X5k_g@=&L#EAW>QuxH*HoT%zmwRSvH${ zTkqyY|CBJWeC>C`&yz$KDIIVLrPi&s2hyo~l!*PHo%dQcpgqWo&?f)hRu_HrdrUMHm%Yz5scb>Q??6(;o^|CkT1i7UITZeMaIev_0&EnJhV6pL z|LLgS>rS(pP=R!390)^T7B9luvRqRRKVgV{U+%alY2NLCe<-O$Zv2f9gb0d{!^ zJ6o{IQ7zH!?k#}K2|?xwnv8u!V=tam_BG1VO7=v&+g)48={h0} z!-hOmF_L$=^9ryw5GcncU|G%I+|w$#&zb}rD%od+|39>S4_s8&mH!O$2A?o6qoNXn zIx0y_(Ub&Dprgs43}PY%gMviC{|tOSNgc)#ztZwAn$-QC}QetbT|d++`^_uPBWJ@?#m z&pnqYv^w@dgj^r{pk%x8L4PQ-Ex0H4LCKzseNeKe@PX;uO7=89pkkmB+iIU1oorw1 zgP^?-`=HSY4*`UcTHAsD9MvF2b5!f_26(WM8YEOkEj3};=ydDfr-X%r&F={u4u`Pn z2}fN5ArDwlpsR4>{4Rou^&r+#(e%oJ1YTj<>Dj}<)7EyhWf}_i)YnrMENGkG$Bxt! z$^j2=k{!_U$4abApoZx`j-qDO60$%;-9;~550I*bDti`R^vBzzGxk9pe}O)DmlC_S z>P~E$x~X*+vFFdOzN7jy+OPX;qr_yp1^htCszH8uDWdQ8a3+nZh+V>kXgC)`YFIAT zMKGheEU^yzFNidL^;xiJYjN}RVVEwcBmk2bGK5HxeV?+O9LW|_(jl7{*`hmfXveRd zgf5<=LqcC3ZXPmDT2Lb}*abKUmm*t6DDq6Wy;MYV@Ivk!Mm*y5AP_>N$YxL=ot;?) z00ju;01`(7AnwEgeEfc_<`Jhi2B4DyV*uvF034yv7zV_6IDi*N0ay?N@T1WHSPXzV z8UQg24q)>r0K^t}6)Hvpcqj&-U^IY7VgT+yD9zR*XHRL$=JPA;hq`qb@33>XSnLj5 zty^s%@gT90uJq1)kB>GxrwH_ zSV$fZq4^FWQ8qS01u(8*fl-c4Yh7)OEX9**ucc?ezP$@{!K6sYdu z6^iB2ciU;~uqCfVOlLfbwg}2rBF@u@;M_2TvPjfea<`&2Gk7U|}~|V{*|IgO-cG-8e%SV}UUUI2p-V z;0Y8ptAVpXh*|X`Ebuz%GPWN8{UPQ!^6VU)kLim&1XCgOd@t)KH_7Z_`W%_$_#5B< zevc%T;a%wn%ccOL+}3_$B6xMPdJ6!x!@Kl~ z!;7%}A*^61WwsbtobQW)#RcM6>fr+M&*H(@8|Q#6EfBg{I%Pm1=)nSLCwf#| zCr71Qc%Zlyu-op$Av)OaDm52u*dvBqbb(dlmNhtwBjc(L!UmOZ%N>RC^#&#!e}3Kv zO63ADdTd5SV2%ZvjlvvcvpLKjg4x4ij#`2Z(+3)BP+M7>jw6;JC>W?J zF#U8NEJ(?L*T8b@iuhKgN~>UrMr!lXhux_Yx4lc9h#Ov4bz=%B6KI8h~HoSk0O@v;y6Dg{MO7RkGe`xDrIzM3Rf*iW6 zYHX#4g2|#z91g|~I?e({p!yv#+>@rEVw^2z5>osSB52?Nwj1?9G6NDBO_;rm>PM|P zY+MqP4+`kU$HD{b5)MS-4nz2sv6%dUVc#+b+7{8dBP%xAP6t|ZQ@rp9)wU=~pU7x6 ze<3n@ks32PHffw)?_hufbB#7)f#3?3ej`T>naLD`9ObkiiB%__z%eQT8GIG7z1Jpq zf6531R1gVvU7MiUN}xpSlC!IfBY0UJ(G~2w@la^2I;yVPPlIYdRh3}C8&(0PPn3f( zM7Lk+c^({*?ZF9w=J(=Ouzq*6lI@fz(5^1>M%AKGQ%ah@Vc~@Nn`9W@s-7BjE@T!F z`(zI=k5xnPV|}Hawg8P2V8}(xI+q4lXf3=(E%0;0YeZ}zc8CH>E5?;pG;n=^o|@Gh zLoZoy!zG++7ujcH$h&YA(sSr(-WM05{?VccV3^|WCte2(Q0!Y?KmpkKqTS+{Qr7sl z*a2TuC3-Blwx*M~Zw)wcCy`g)o^)z4<6QJ)0o5Aw0cs6Kk9IlhTjX-<&r+jOh^XZ? zpmH4?Ve}Q4YDdG^dRH6{BP*{8>Z%Sc&;i#}%D^mnGTo@dE02CStGiCH4Cs=KyFN2% zG_jed!Ie+~`Y8?NsKOE6bE&)y&o&62da~-`u>nAOH5!XU*e~J8ASid)uJkS##&My0 zcu0Ytpc%QNEI7h~`OHC_ZSm4RqsVJ08l6@s%!0^Kmir())Oc~=4AwBXsWL$EJQRn~ zVO&g*lmW_5ev;7V6dxF6(n$O(D!L#X8{<~d5O}d6j}MeSla8}RceRaoFjhu0CFHm6 zyCpg+0L2VKx_N++2nrG)88gNfyZMwA;S_g!G#X-4jQ4hHu$7~|(cTqod^CDh_b@{S zaCmj-0lR;IT}d1P?j=eXg%HOlDvv1JaDxd=CIg zRzsvh6+uBYH%8ZPKn(TQQ*hi1tHQ)TVqtlL*dSjh{m;tF{Cnk10H#Dsfk99U3y%Ta zogP)QQSugA0=?K7t6wL#1Va5fWA#H6>gVhtKR}o{*?;^BYrDeEj9h^qcEiCX2X3$? zX6j<^{4yFR4#$DdV*SsJl1M2M-7f(E3|8L?!L7f5M!){xjwRtEIpc%~wt(c!w?YJ~ zfHz2tcM@beHCVwdtA|E~xep5CAiY3p@fCJUx1-@5{P^>r1AGE);o#?@Fy-^K?){_5Nxl*9fF2jUD?rLCR+Xt(xTlk z%sjav)`)%{XWM8jf$#R7crT|g;@1C2R7OLh2Sb8GQ5hP}s}4{{T1<2g1;J zQ4tL^ybzVJx@%qTh9YKRyiOX44vwX-GtVx)YYy+~Wk5fx%RD3;dR97rrbSAyx(y^oW!HlB*@rbGMN{kYOt>GD6&%mD?4bb6QcSdVH zvWXe9!m&u$S0L2*X>h>eE26pT*>_G2nq*#kD~2klx|3>$h;Oq&+5#E%T@6o{T~~ph zv9B^6D6>;mU2wocsp-Z68*8Q4%^b)G_oyUqoaObwlm(tgN4V==;q{~OGRVF{G8~?> z0GK1!iL){o#qxIgD!?p6h+_R>eR9fN7c0}EOb87G@?pygll)|^I0B#%834?y_T`#J(8s^8JM=x00 zaC%7xp-$QTTAP)fA2tbo~?})MQe3q6-E! z*DiKD36X`7oLmijOCTkLSOMj|cRR{qe&|yJW`-23I$fm(rm-kry5m?;4$s@;V=2 zvqsVlq63OhQ_KmNBCw$Rw>Jh&PBh%#jr$;Er$}b?c=D>G4CL$_%R^YEkxg!)luvRg ziqhoAqV6)LT8ST~C`z+27FEifEDl@LjpA?3C>3Zxk`kB`*j>Z_XfCGnHP|i4_lRS-Pu~`#wX7CB@olO-fJcZ^aMd{x$+EO^N|=*FA}ED^&i-0wP(F$kw-}0vW}1VRf?7CQz9_`kgs6 zv_pf1qtat$y{b)8T7{q3?DkseY~4v|PF&j|UQ7XP&N`fO0{tFpFAar9a_R6UX^C8KkIcz zk7Gc&Y*L-x$ZEx!gIv$sYvr_?c->CX!!C=pAD~J>xus&=nyjXi4Z%(6mH%kSH?t0ZX^FYjDLIv{xc(DbGkWrHjDSHuLVq)_b9#dvOAr*k z+zOB{5f$5Fjb*i&X6IcP?Ld&a)qPCM9YoGMfO7gzcn40v!)8ix#vk-tlS;{G4(qu? z!*8DszYRv;y7oXqC?1)p*X*D1sAW2ri5vi1@#Y&n)0G#lk0f1fT7uk)&@Q zGDLm*r-nn{P6)W7WlU`tM~!e&+E@>wwMPs3$Z&FMv&h#TBVQzq*8e@^mG1dbe3S z;b`>@Z4rGD_pWM4|Ix9Rg5j&Y?@$KBO}Aco7B0A|E1(=|`yF+x(j{zyXA*~_9aog? zDF<&!*LNd&51t-+K8>)W`1Rm7>qJ9*{g{S)d;Jo7Lw+2<)-Q=uS{+Io2Yz%IxW3dj zoR+7uE3I)#TO6VAAH%8kSZTm_2thC&#MTeT0>)+_@DP5h@hifw06#x|+4yDRr{#U? zf|~dE1w0v^49{*nyYcM7vj@*!JYg{Vtq7hGJO}X{)CHZ07b7zgYYmes{RDnb>n7k8yTgi_4@(`f1T zb#P9#)E4WL0jp0ks87-mg%p%{&m6oL;kOOHhJ44Y6ZK0R%0=FKmw4;_Y7}U~Et+Pe zuP(+Zmv}GKk4m{`R65iEHH`MzMTc^Ude*(G1RQfz4!}MN9Nxt5EPfa8>&CAazd`)m zRI|KqrJ=3U@Jz)s70(nrQ}FcQN%1Z`U3iLkLQnH80Z-xDzOmY9HsEFA=f`g)!H)G! zz5^{VsxSCAYCEbQP&cHp!mjB%b^&@i&A;B~TscGVNKW<;>Fu`7w(o$WJ{oUDEI*mV z4Odw67Z4kQ?xp~%t1$cNbvbVy$r)$mbbVgVxG;P4x}4pl5VOh@tehvYWAm9Zg)qDG zx}3in$?36jKKXe$Jz+M8l%wmpEtYda_`|OLZKSGG$zc(pdxt zHXyZ~&@U{)2vudK%AM@x3A{~0eOg4CVisQ5M(dAONG(bH@*W$z#5tqZwyxqZ!Cg_T zmAeSCvLgj6oTmXv*^v(Ak8*XV`_C4fSH%^VurjW9a=|rCegS%V zHLmmm@{(3kfN4E~gKO=5dH33b*lWR253#;{Y-=FFw%SO@YN{_6TLWWlevr);-1#tA zQqI1zRj@CCq+%8BvE1jvL57vz%~d{2b;+}L!Y6AGws|Ep+HIDBb{gRBWK zA42j#rnCL8f~aiLGaEHT5d32ZZ6)}x0silm<~XCC=wby%6?t6r<7~LK6>iFd?BB7+ zlHqK^z{Ha1#)&O>$udhF&6S>8Y_ zP7?-Sc>nt$hiC88GvN-%`3(L)x(m*9oX_FykNj;P-sbVQ7x4B9-uAwTmoN*Xqgl`k z>aB(riqC5 zS80!6QK&_H16v*}F45=R>g(4|Z=D8Fcd4zo#9W!44zCBHCXCHjX}ctiU*DuFkoHl*0=)9m#g|8n`SlrZB{-@w+Nq7}G91R5H(-Nb2?NSu4u zmqEF!w0-Fw1cd(Lu3L@oV(qDP+8ivweedpHeC)W=mX{9wCs_a5beAfndrGy|jBwV; z{*V1iMBKVSt4sGHdC{t3t(C$45}H+r5b3D}D*n0a$p@gevd*j3@x`aQG9AK3dW!ar z)?>VM0$z(=bA+`>X|ef4`fNgrS9K<@UYb?4>*};N-9wA!lIwGv(^@6UaZLO8Z{#Sw zri^L6A>>Ha-uT-&G}Cu=OPb?d-}%jxuEX#n-_@-%v7SLy5Z|%&`uGpHxuPwDI?PjA zGP>lQANkI0U4*i~sO?TqL!C7vbl4tIOi7k87gAQu><`rdIXx6<)Mv^%K4)Ah-xrs;bpns_Y|KQStCt6LdF zyoQ7zmz1tQ-~*FNd2I{P-KfP9@rS&0?2k+L5}z(9)&5FsBJpi7b}9QONDPH(L-Lp0 z@@nu2T8=QgG_@hQVRH5fInc1&-cV}Cn~#<;Sj@0E`A_RA!dg2PPc$!NT=`Gw7H#>U zRyv4Op6rihS96yK9*OW3m;Eu{=Do3Lozh`egn>hgHc4}59|2oODQVdk<#Y5h0lEPw zIBMUg|2QUp7=b7R%ov(nhz=$0P$%%*{{;TAi*O{P^_H~7(--c<1EZ(SiFwq=%ROq6 zmzICZ`YA(E^1m@S9_$G>RKd$r2G*Mk`L5+;RE zQ<=N2lgj%(m=&xG5?Bv{yZ%5yS8k?~vr+Qi%mQR+zR70W`zBa7d(umZt8Zg21mVTY zj?d&Tli?C14IA`;D<_)H<4D}~F9d5n;+Yp(1>=V(Wlj!{+VN#GiIU_v^MDXM+f2vo z!*VF;$E5Jy091?;a2GkRayOvB(CZ&y@SydC`pU|rIVI-&-I!hArW`OrO(jwaJA-Ux z=EMcSSy?WaYP>O-QInOyfsRLsIAjqB)Qp1gZSDccog)Mn6hXdN3NWj3+}oQQbq&W zOwc*Pk#3V#?g9UmMK)PjnE+IC*ThGRx9D35rn34s5Jywnh+~8 zfk*QK75GhpITPF#@lBGkMJw1u)B&=iwt-P(Rud~n0P5Iy7WDv)NlAd2tSs!rryVW+ z&Qzcasgpg3rsrGXxOmYkcmM;^!}qqM-b-B}MEieU?%A41y?j&Mw&Gsi>uqfNY^#5g9IlQ@;5kV3`x=p|9A>D1!?0 zQ-NQz3XBzYn{R$3U+`?b8My~3w^is;oYbX4c{y1q2Q7iR%?kC&_IxX^UBV3SRzBz% zRa_<&S3t#~PJs9^HCUL$lFqF&c}vLPheW@VaxlS3wEPlS$2^Os&q0zf=O4WTh?CCd zH6)En>KMX$fxUKs_!9KJTgPfk3vf<5*H~Gi9dG_bEY-F{2>5GX-d1~XU4^yI__Ti2 zU~6EA)*1Eiy4%zm_>`|R9!oJF!%`!e78-CdZ)XyUS6b3gy|}|+_sfCgSpm=Ca0z&* zsO2vx1+6;I0w?JoKx*r>{5d(<@@g%A9=_7>n(A|2Km$siA~qWoX>M7-jzX_qR;J}= zjwE5xB+h7(dwCLb%N%wElt`HjJSmgk=j^1!(~x-z;7=-I#}TFF=gl2LKNPW7qX~Zo8Av=dA2he6h<%?YD`EdiPcXs~_C;#E9&P!0UlPey?z(4@CF>+E zk^Z8VJI5UBdllR12)sj?O%LJlT@%a(=BiE}`hu)k^~mZn5ZDVdx((V$J@Os=aRtQJ$8 z(B@ycZ59M@e+nAQ76vz%n2xL#FVs5BiM_}~EK*(?W16>N-4O1EGc6C zSH`=GDb_phRub@MQLkcXkAbf*>boNLJx~-e$dTDC%zlC9Mk0}&!Ds;&Dk-f@zl?65 z*iIc237OJYwE6q-UYUyEQuD1!7d`j(03tqY{DZ*bBZB@b-Al8k`IfSf0&RPV)a2QX zserJTLK@TPjnXuF@z&-fKvN$>7^SRi!y9G_ynW-Ar#HO_wW0;Ay%xbb^qGGQ5&UZbxORE4U=tJW^=XZxL($g+YRyGQ@=Ma7l z-`aAxx*JlH)qIzh@1huGgUhCLUQ*iQHvYtAN4Pw*cBt|eU@6uP)#E8A7PDLKwwU^u z64tR0{j;$@-zC~hrS)3AsND^2t|n{CQ!^qp~ z{N(~31*kbSu63BQmb<(LX+dwo&KGvtm3A*GptNHVS|0Apb@`EGyJ(Zr&85P&L~UGs zoQXweli_k7Y+6yonu#5ip;u(b*!Zv+Px5667D%u`^5E756Lvu_gB>Y%n}7p{f;&H~ z0N5oiKN`G8J1xyncEU3AMi8aT9&b!(aG2O4(cWOYkZCdH8iUCa***X-Sf%evpM&yh zyI016=X!4j&kY^NIK!q~U_acDs)47qP$vS48&b0l*PU_K?H+?cf)%O(KeKz5kegEV z_msfB`%%OMuX?Lff>*cgbh{6R-3Nn2%kZT_8?Wwzb&^D;R`&a2A+dAa9BOUC25z4G=QcsujEshLz=5FQJP**AdSbDJJbbf zP$_H?yE?5Gq3dny#ol!cFTui=VApF_NL<$e3c30P`4%NauDAt_KQ#Yf2o-+Ltv_WY z6#C1b>)L~NcoBCWe4SSCdASe~pX>StCD_np>)I5Bl>T{;n%w#W98#0nwUmQF^+&)o zbJA>Y;kBL7sKXSs-d)#B&o8^{eoN1_7l&d#rI^7kZ%P%&B*&X0x4bzucrHqca z0TErrfI^)X`A7HTq)AXB8{GOgF&>o2YWL$b=z$T1v&HT@(u1NuAHr{~!d&hUZauVZ z><_eLin2h>fy-sVX=UecLBl(v4NmbMakvB3zyWt19poV<+xP?+8bZhRN z8hArg`Bzr~A{zXI^?`QeBA6%)HR}2R;h!ZrX)c`K!hhDyeSz0R_aPgszNK;8^ek5Y z=P4gLv+KurY20JAPf^z|0a;ECUj0WI*fnw7@7hnA@ti=@4u=|OI|H8!|#{~2fj%@r&uQVWCdbd2J) znii!crS?NK1pLoaXUHOCp?!!dBYa#TEIANBddjlk-E(CTlwi+Cca<Hsnd z14%y(7N#(#yY4rDRp73Bf*w)@v4^^5P-6kYf;Pkl7z==rLF2r*hinbAG$QZLUh-6Mm^)XU)Rd5(hB4FUsypUNR_+?vQA6nDaH3s3)gp>^vA`nB3;=2BVmuQb7fp?gqrLh~~#bBc<8csY_ zF-luqf2L|%?5i&vO)ib^3;U%pM3vt&;EUZqX!Iilj7`3jb&}#HhEDDrR}DF3b7Fso zauOHDQFDgm7F@dWi5!<@QV-PNM65*wN zcE(c-?(hoi|K0;drN!)-Y~a_N2}P({zMG)$a5xH|tWREU9MwaVGov*hWC|0%{pd_+ z-HruwGG&sWl1vz77anM}XZjJT)I4Q#hx*Z27%)_YoBP_ZB1F5+HB$P*)@z0_wl5qf zB_h;|rUrBfwa;b9`!eMHePNfJV*ZA+tiX#URbKW>C}l8+EHi`3t_pn zxNI?o#g4faaM1gUx=yjIoFLWgww1|khTD|)qmooWK7z~8=Yn)CnhCLjpSDR!ru;O! zy$AU>JZ&q}_*~mC9W!yL8mZP*X!>!K0Cx^+*!T==oxS!ZJ`aa4ZFP9oZE<+2@oUF# zEWXF%SA^f~+b|1fSH6JBn1vzfWu3{HY9j?sc5!i5Uw%pDKEzbo00-*^4}?eP1w?f| zB{3#is}&`(O-^WYQV>yhPI<}>SaYYq^V!nuR7v#vUWYLc^xy4fMzcMT(ntwO%UnmT z?hCu?2ur9l+@T@7WSMHsn`(KxT7ERA+za-dmPPE_TU>0S17(N?Ipyi@??5zr4XJTQ zn(VdigwtSZcHDObHwGd+xgJ_VJ zXtkLZL7jY8-F0nwVw4bFfX03bRG>w2%8yE85yl$-f+or-Z>O&d>|g0iYwKDA;A(jXdb7MGryLGw z8(q8~#cMj1)Mb;PK|m8Xk(4_I9^_{rr!U}j*U{>rxLAi{cXT`TGrJ8*m6lW-5(Y&2g2rH1lSt5ZSBdb~tM})lZzlL+5)LMgT&4H5QQuZT!W8@p4C5r@p1drNT zXTbK*l)kI-EK@$q-scsG_^wK~;}f~QY~{kW6Vz@9LyLeQJ4Pb_%Uxes7P9(`o2^!( zd*zYNiy4qTI*((o_4J>+v9JFegl2hvPI-$otCW>@qt%QlRvwG6MuXLQa>_fXeLGkd zrPNyW4g1*rDNq?ica0$LV^<}y3HIdJ+rhgB8!?x`a3T5Qc?uVkv_tWp0)0B{InrGx zx=x%q@zIHn#KUrL{}rILK0OsFuxru(f#bxcWFOLP6*rCXA?+6X@~o4jE{Hk%wDj|| z6X8j0$A1@0N3rbfe-8U7w4U#K35QxQG(vBAdvoobnvQuSyK|_LnQQhHs5w?sR)VsE`Q#-)e&Uf~;*wJ60 z11!V&PAZ*c1HDN%(vB+I4dwuCAT(ug1_;q}5bk{rhiR?Hem)Wfi_pX2kcl;ylv2VT z?J!M$QHi6!g#D4GzZMVQ7YNo+6ZA+^!NCGzsryGwaFu@dT)IRn0yDp6_6h{rbqLYI za(t^}uD%%86N$DRYu7GYL#ks`2Nas9B*sO5f+E~YHsd7 zM}^4F9NAUvP+^Z(<3d8*xN4SGWfrs0dCnP7PuiFo-437I4C_%nG=tSN9gfzKR&c;` z1WSRQpo-=SY9%%S-AktA*d9Uyu*YUaYd_r5B`5=GgzK8x#>?oyo2nK8_2JR77t;2}96&B6bT@&wMBh~KjJ00lg>Jd8POBouCW0Q;IC zT>XiJxb316W_&jMvtu&~b+T0LN_XnJ&Il;^-SWu0&K~~K^KPK$tQ_Vkojnp$kvgaQW!6d5aqGQfptxy77wtnk%G0e58C=omny=I9oSz% z=okw|SxM<{QxS4#4*q2|`QFejZ=MWc_8u)63$i%#1ZC~t^cMupsqFq2$Xw^p6bEfc zt+gIX!Nb>9w%I+pp#DvlxfZQTtSkW9Y+5piN@Rx!Es~W6A*JQaFCdZ4!zV%kH9#wf zc&PJfFRmfr`LQ2QXr$!3au&|UZkIH@F&z(u347`3b5h`@1kDCz>YvTy#(S07z-50X z{xRIAVwP3<>3acv<0|N=jx-hXU`~xko}u#ci7fsFky$38R0H)K@ccipK`RThy)YXJ z!#@C->$zAaK!SL}mbD9;5&hJX2BB%wUM5{Wk&!i}(lqH?j||<{$Lm$Y76<>yr+({H#{I68c`Y28e1sN-H#qS%-~@2bb~0 z^dH7>Bo%1oXVscc1v^?1aZXL<`f}iAsgbX%2TG4XYt$>vJcG+jp8K)J- zaDfZui9KLT+kUKPy@-F4@Y#rLY56y3yxDEXHFkP-QD+i%1Y@A%)GCNJVHCUd=S42g4j!n z`ZlKr|GmIMucvgzG(|L(heTVeU%=0qpe0c39^g1CV5?$+Hy; z5j8%F$E54=NCpm$*HQilm;)>*)rW8sHiwsWCS>lYLh&9AW8IWiHWb?Wn(flEzGhKQ z1g5>5;H3rH>#?ngkhlFX-&IM@0*aUyt3jYwCf#P{iweLCivzy% z)!)rJW3`JeoI!4E1jt_tooUS%YUPBgzSXdCZ8weWTDX92W>A_w1_DH+T0}8T>7LSI zbmsA04XowWyXE-ca>0)c4%lS~coj#G_&!!w9EqO*2_u20qa?w_%2kJZ_a^kdIiFe; z#viCA&35H$l$HW)iyh>fF?WN!ZrA^r_wN+i(U21KCAf-q;?smwywaBLQwa%mdYh9l1yy6WrK}vI z9+nAI5L?U>(JPgBftDyFvSppjStXXLleB?02j#+Pc^rxhh=Q6E%$_MHLkd5M#erWt z@ynUUTr(am=!7qQ_mT#fN1yihDb3hm6=3pW#iZ6xUxI}c084k^Qs4u@0k7l?4vdw5 zg^WV$5)7T7x*8Al`3WE->EXQLiA1mMo)?F&$ZF7H6F_;{1z(*=wZ% zLhk0iS8>4}_gq6}7QvjEvY<6%Tj&hUOoKoh)8mPc=%h0*eazY=X8gVK6&9xY?WJ(T$ss`dCf8sfKoa1&wr|Xt21CST$7>sNQsn{b@mFmj zseX!!i!)k_Peic9EiMLkoQFKjXGnLfVn07?Q9T%qG^@CHRS~4Ubfhxh#*~$e4-eaX z219{t1<4E$8vP5vS_>ORzSkrXN*%f!Ee}_GQYfL{lRZsJ@M8^smHzc*wE8u+ishpm zJ{qxfH;qJ8pMC!bpLPJToATm(LHQcX?kPxfAA|l3GFr7=@YL4u2Tl}#q+z_FfjLQu z5PG3};_4up*p$Ep!T2NVMmDkp*+Vd%v0&6$FzE5$g7F-|P!@{lwm1@U$jHyRiR&Dy z9ILUA$$@U8imvFbB%^q+wGPxE)U=mOs-!QlW{@Vh>Rz(elBZQh9GLF^3a&(xp*uFe zo<$bO#8iXWO#Wrg@_Jz0vr3ywhFLoTCEEO6e1lltM0%ywRcZ5spviZqd8heyr7g&p z>v+YX0WwIwFCbe3kXrb?X?4XEMOM}cxwk9j|I@irvZ~Am{P({1f?_}z}!MI z`l&K?$68|y)wRexg;8IIiV@$3>d$3PJdRG|<9`1?Z2oE|kqvf9un;X`{|qJpY6k^C zH&@y79fxOqBcR%fNH9Rogi!(F1Mo2_{IBzI-fm8PT1==8Whn~lOz<{+h$x_D6A?9#p= zLQ4K3`l>}pY?8jA44G1m?%JZ^+!(&gN>=G#A#Qaz>tt46mT4X@uK--c+_SBK=Y4)W z&x=$g#jKNmh{zA`X*hw#{sG5~G~sb|F)Yd-7e~WEW-4ah98J@ofn%-QZIdSyu_cHi&L&okV<`jxjA`Oo)CX$GN@xH{oj7Vu0X9h- zO}2;8b%JtJH$H62O~k8U28I7HUP+=w4ECqMKfzyZ{%|8j>#krUWz}8v#&|L_&nR-$ zenPBp(^#b`1%moVmP%NG|GGlhDSF6;sT4->+g(cQVDlxn_QsbF3t;YW51I$W$d^L$ zQb_HPUfiL#5mBtC-L6f5GX||>kgmU3)eFf-EqBRM^C!Z+$|v`1aa7&&pmv|&yHAuC z!9NrUP(gSxJ#xy>m&FPFr)I3(pEopCo&^ME++d{X_jV4)Jhi_CsSz1Zk!1o6%VJSm zBS4YofUBhq69D| zc=^)GkXlqL1Qq$0=`8_v0DZN5dYViv5Ei0a9iNYRRB=S$q@3A74XX>d-Ew~6BDp+hK zDDVWgFSf~Jm4}2O9-?f6NyV3oF%%wknQ!qR^HI;p$e`Jt6FF2xMB+k_X1{ixFr!^x zHm-YxUXTL&viV+M4*rO3@FW@(J}1>_i=cc}OyQ%0k0GSkOX2E5s^c0ux@kdAqA^7l z%gypUJx?t{DMgleP#PPg?Pw2}#zWj$7gnq>$VY4x0HIe>*^)~-O)CqK#gf#VNE!nO z)A=6CYb=KUL>rLhvI-WUM>$xs1sreU98Ih=snK9VyC`lKns7li4W(xOd8G@2!Y6hDlQZ83ov21SMIG6w5wc1 zYyQyf*bdn#NZY3z74Su_m-qp?PyaXDqtF(70@qJl7i&i{-e~S}=}SDhNggD< zsqdkazN!aL?8vtH2YcwVNN5W(ywHb}M8T!<>*Opc5OGF;kbb`ln@{kJs^5zj+;Ji( z>wlO>XGYZnKP19w$Y0#hKGa;-g#M~uz8EHh+XPN)7@HsUpPYePL#;!cd@_eC0%B@t z1{u>3wn5&`SOLa1x%nfvcH}z7)~nq=2+RAWzRPq@UYo8KdN4bXf2S?+Rri#UztVMrZ=EPFhMPN!%}t>}8PS*7IiK4@eC}rA z8aEizm1gj{k;td^Q1wQ!y8A^yQ^E7@7qp$Cx*Z#Ho{VF5Y%P(Wq;EZo)EaDx3ti2o zaKB>Sqc6LCka+Dnt-M!HyQ-FZz$YnxxkoJ%)%yfuEcc1{?<*7K(uQSYz)U89nV4WE zV}#9V!I}xgOx_s9CT`I4_T*`A7+xOd(eJ;GhtzST5gZ%t9^%JvP?vYdpem}%?1Nx) zt6bPSQy0P2!5xvV1!y|#f0KgRt$&f)vD|elbezlW>V1Ry+N?qJRP&IqTp79tlL*47 zN)>&ug?5L6RAqTE8qFinIKKO7UKtvmSB_!!km6(QojfwHEa&qIkNHevV_rdfZ{<99 zs|cvXc4ZtK8B~Yyu?j5g)m@jh?bMX&H+cKrhyP-9f$M|t!Rnl+v`E9cS2NM;Y7 zxH7^xsE+A`J>vs4$1h7#oP-&j5yw_8V zIU1B%0v=Ne^7QHpc3}ghCQ&(Np1PqEEchP73RN%2({7(7_a?rj$-QuV`RzJ17UE3!S8Eou9ANXd z)J1&av!iy|p1dq`)-n03(Na)Cm3N&{p?m{Y!Bhia2jn+hq8i+c8jR&JBbfL2I^<;L zrkkER_=-$QEoLv1QM`7a2hM{Rdz5U`9ZHAL2Fm?9X!gW=OG1aiOyMzf<$$0iWI!sq z9HZL8$POY|I%Hrc`o352-7Cs#G0gbDs<~NvgT_6)ab$x?)$;CHYwPn={D$0e7Wt7Y z7iT0Ib1(FPou`a}(CM0Q6sc`FtsZ7p1rw=`km;_Y_ThA&b`MnD zmI2St4_O90y{h~sq@EJ?E&?F5kfz3%NcAH_uG3hpW%W^*22g$SKbkrET)+t4ZfyoxN0W5ixnogU9^8`)pMDOs)_fJ}Rn(rJ0 z*r{a8q}8NiKLZMUGEggK_hZ85mii>F&>@&<_h&X!_M<(CVQJd=dm~VGmW+JXEr;;p9|Q_ zcq%P^bBmwt<3V}s8U8F_Px0q6_5^>zZiYWsvO4};&8qoxEtB~3A@&G=u4n7{vy!dl z&qvrw{(O`z<4=j@@#i+SfIq9*JUj`7mxAa>b~A;(L=2JL$Y1%snTNm9rWtec*FE(5 zNh_tLT{w1$zdlK?7x?Q_^m>}VK25L3`0F$D8s@Lh(d&M^DlKzxwvEo}p{rJE@mf;a z%@CXI63#4jW3^6aoAw|b4oL~oxLkoEd!mt>dE^$DHB!DuMa0eT<#8pfl;VcLyX%gi zRG9I^;J64*Bnl_PDg;Xh4k*_{k%Q65WGgZy7MVhkHWWzcUB*F2pqCnpOr=O68u_Lb znHGyoqew9t`LY%1jYWDX(iM&TcOJPVJzBAJ#Lf5cxDvL5;woacYGkqo5ws+$sVMqK zauCkA$H70wSoqLzKQE*km!YzN)hNQ@PNN~7N447BdxiAP7qw=k#fw!4G#ovHU}*`k z)gzuk1_taq(@9ftjW)N+VOoYAz9c%<0&PJTX;lf4=-+=2AI;>5Kuop}xvb}dA_x=Y zC*>Fo`O4WpfmI5u1zP6WY%$FeX(#$~xyp=doFq3ibL6ScqsHxrMwib1m6EcqsA{M( z69CqmuywTA)~h$s12JB0wWvMnB1PJ5us%tbX6y5|Xs%zYP+8&b0j2uaf=wP@$>-I) zUyIC-Z05>ei$-|;<4%GBeu*gpI>Pl@5`EWh7pNduajkG^54dvRJJiW>3m30=WdWMW zNQk~blp>LCv7mm%RkjAou({6wWoahaR!mk{lH@9o98i|*7X(yf|9Jji5b1p!y_Z(` zv8#2cDNpcRdH$zaO?z7cf@kkOeC~Y){|+6;=kw1$&zEPl-KoIVI(@_~&lpyel-``U z2a7w*F$ZBm1id5O5uMdkU*}}defoW^n|d1p7wei@L-g&H?kv_&#>Q%zbnscufv(lm7gwZh+70ZsT1qg&>EU>(nAbVj#Y9|3BCZ74)4AU zJ8Mcq7$Fq;tdikq9FC)`>}MyhNdqnGNE*x0kd-ckbV>`%DmH4n15_OC_)ZGe(_WRQ zVYic@*VlKVBqOb_IZpCeQC^AyP~%UnL5j3dW@W*;U1mA9Tcl^VQ1`reufYCOSz`|rITIIwsR;k2)gS5T=oTv?Vsx3s{{%S4(*C@YeNb7 z`@0$a?SAPKU)$CcxSwwLmmCF^jm)@((J{)Hq4 z!RV!xoj_yqtZEWLH{#W)^vr6CQN59rGqnH`gp|zQL|PWXKS&S`tU}Nm7_n>#&cK4# zrBvEJ1+SaNKsAM(CJv&$f?HuM8!&YtfgnvzWJn(0=APzfW@8OT?LEXGvrnDW8&5wi^#apQn# zUab)HeoFh5w9n|F9$xSPmRneuO(mO%&D+SQnNwd7;-(P%T>X=k`e58=msS_E z=ciB+w3`_XqjM6FEQ{HE5GMFO+8n+!LfAC1YQPk{DqfpXc!ArzGw^EK3N>b+++uc> z;Dy<$k@UsU^z9}){b#fynSeb6Lapugg28hpLEGfNe*yD?8hVQU*VL;ub=;s|MVo4& z=MY$|{eT`gTiblq3B`Ytp7sM?Ee9$@)$vN>tKv#a&EHTm_8s7%w0Ja!qZP+*gYLRN z08Oh9Ewqmc3G7yDo^XHj3ZY-PJ42^HJ@BMjv&W|sE}L)BR_!(eyBo&m20uxV6U;ze zt1ZcI2KK-a68_iL?6IK&)hEb2?8%G>1a_F2!3g)sBF$mW%QPpFRTesOx)uo91NFYP zYTVSZJA;=Fy=RCD*;HpFTD^_~t2iW1$=o6ABf>cH3F*NqXGM554!=(|8AKaaDSW@WMFZ@INBF zJ78q^`GiLY+lL9y#nGXm!xHfNw+U|-!3(pqSV~13>K(jNdJx`2z%Fb$mw~l$EiCgN z;9Er`T0tu+B`Hf?BHdGgEXFu|2zqV`A`k?QWrNCBd04Qi*eSxssgIj$Z6c|K`~r;{ zmKc=ogBX=cCpL@QXT+FL&lg5|U- zp+uceaqOGdL^!Latge7_tlt*gf=R&HNU2pTZ9rZJv5ZHLK;%gvHbYBW0;829fLV+r2Lh=tq~qLDu5I86#t z@jgK$Mb3r&|fH7(KB@=X)-x% zr8)9VKqv8`9b(05=wL?<>b?3iOV@K&E#FT^@L+9w2|+P!&*Yf4$3V%jG(W@jJ%Dpw zo>l^VkDK<4%d`%B(_yj~k*Y0lfY5|vIJxOs3~2LC;WbE=yqI>Z6L{ps3Dy>5>x8<3 z5^><{s8Kkcw2H$;?3Pwt(e(pdbSVxT{gHV)g%14>}5)z^H z;lHY;$%Idu!W7j`ddmGdH3y_I#cU-d&;mzszDp~-z?MN*$Nupg&A3Dh%dkkS5>~Sx z?gLf#R;B5I4idLlTbOQzZ8uJmVnDB?)-BxMNH$0McC-KOSwk5ETF-Jic&2=CS!o^T z{>5d-72go{Wj6V-rjpiTKj#(VoEA_xpd1G4gKmTpx=R{J zRo=4i^$%1{FUvTh95GM5&)3Z-Ln*b=Sg>kkL9I>h2f~i(UQP$dffnI;;33YX_2|OO zMLc)b$wjB$e*j07-iG`I;#s~eO<(Xb`g39_NboM!6b)7`2{;$@A;d4V@Cp*5XdzzX zy1&Y+FR@$5RAl@W;Gpn*A4l6@B$)D~G(*4Nj{Q>-{E7R^$>?j5J%Uf70qog{K%a6{ zIm%|FJh|9+x@N4t;K_{FvzoB#43J!Zm>47-q7$kd4`#HNXqoCfJDjE7D}RicqKP7S zeG_0*fcWZSpNX#Y~>A;Otz1l`ViX|SxvXDa`nGWc+69@W3%3% z`@4>ux(HUAh+P|m$-SpOAh77N>N8bwtF`tFY{j`WUm#LFMjNN5D2AY2J&bpyg+`-&2`SdvGnR|K zff_jUwK9VKB0Cpkw2>6Ya8eETmyt-1m(n73WE7xOyhEerx6-`8!FsU=9P-_5ZgEx5 zhye^1_6Io_oibFUIHtkJT$$gPg!kZv{+$%(IM2R@*fPWr#VvPfTRf-!1YJr0=^GD| zcR*V(@pNK&uQKG9lgPpWcXeQ!ah#EW?F1Re_+9D^yj?4gV{XQTBLsC&B7c7d4TzaQX-Q`S6lBpQ{ol#FepF_f z8l#!px+!x^Q)WGZ$N!T*`(?ieXZjp}_75GvY?H39MKt-d@5JYxNAd3|{F?9^_EG=$ z{Mi$5=nIF_Mt!4S=g*!}{cV5tQ5cJnC=y_AgxvlMH$I+(f(_(*=%~E)uly z?w3#BWSVXpOzbE4&P$^HTiXJxt!5u0)^5yzMldXo_nm@aL_kzm3u@{{b+u>|>c6!k z83YRLNORZ3=FY^#_C$W(t@-`L&@r-M@yOHR>bpqLPM>(c*_fVq%5k_BJ2fz!d<#}E zswlljQ`H+AVdgS2^UR5HJsjV``D@PNZUvXZsV|&_h30)6gk>F9RSiD-?8ax=>sVrA ziqdLQpnk%=KyRt>?n8k$p_3x1sgym4CgKdvtL|_?QTHCSLuAbSPf{qm_2?Z`ngrJQxF^pFkf>)(?H z9cc!93_{Z&rQiu|3yj^5$w`TA#@NKy4CIAiH0y0C(f3E$1weKig&|*G8@jHX0~c%@ zBadYFkKhr*D~|B+aD3rmtUpY+a1^!Ie@8gXIs$uBLSvTY)KZ${JBHGZ$PDZb1``2@CH==| zk&HAe;7aUZ_n~`0OsBt!L)KAw2L#n`(zyVUpLd7RGSudD`joAjh{eB_MCVbpo<<7N zPGlU%^4Ry+&C7pF`1vkxj?<1gjzRGH2Bs=5cuiJXag4x%urU@G2ZsS1P(~8L*-QVL z2jXDrsq$boM{!a)0^ERyD?#%(lKecE>sNgjveZk-JF@vqb*l zg11aN-XQYPRt{Ffu8(bBfJ%58jELuMx~vRL*p#jeq;H+h-oa9+%`bvDY*_Ul#h2## zSU_3~m5OlfzyU;Tnp8VLTZdbd+4EL%Qtu)853H|A{EJHx9)3ok2r3mEi~4Hi3#gwt z@c^wJU>EGJ`&XolIwE`qQE)^Eds|8^RaZV?1yly7$Vx$)ipv;QRJm}CG*$m0Gw=H>}~3G!uO0h+84^ zJY|~zmS6_NMReZb?Jb@Bq*jWSAMxQf*T&Hd65FMz*P4)X^&6Kb@@6rxwRFd z<2YQ4$M@yl5|p=A*(+ZFKIX)==m8z8XGu7jD;X70V~Ct0B)*Q|XiG`A@m7Sd{7_%DCl=^fzP%UAO>AR&YU2HS30PnlPZR-G#JnereiYUqR$!7B2t5|BAeP z)SVJAEzH7-ed0qjYI-bJpY(i!f74V93l_ZN9>}afPgajQpYOB7D-=Bm9A?)4+upl? zH&Lx^!_%ffp@l{c0)i3*1O;IxGn1LjWRjLPP=wM#3*{)3Hl>Bq)F$B|r(!{<6+uBk zWeW-jTiAkvqQZtE2q-8hZb3n*f)aL7svrkJ``^!GLW{`W`~Cj+y59f$U#Hxx^Ledj zJ?mMAVP-S)R(J1?a&bai~rad=L{ldQ(Q@n-s^@%bU^ zVA>*`iTDe7D&LhB-1rpCr4=!_Kt8b(SuBS|`O%Q~(jaF#I?kkDAx{sw7wYU)dT<(r znV-bQ8Yh;nJ!hO)0uNRVw;gm@9Qq5;=LyK*;NTf(@%t{{K!L+CC^c_Tp#}#JlBes} z$Oi`pU~jl|w*xN;yt5sr7DzInHm1#?!I{1xr*$sJvjIZrW$?2 zE0_T-rbE5=>0;fG_}q8b&9Za6n5rjhD#s5JHLH!{w(C*Qsf{3Yf{gvBR@@LfNPA*? zDd&)Lhu&Ys32@v~c`!PVfZHOUqO<6?QDE+(-?hQrV*L0nYDK@1a+`4h<(;p{0(tL7 zmz{~I-iuC9zL!Mar`yesttpSvH__%N&7;f1$Eok)Qc5(ku?zV?N^cxM40-|opkPU} zC{$Q%JzxNs9U|1ByHA<)cuVL}l!G;R!vl9}ZpTr9Q2kZii|t98;2`xv6tr20lkM)y zftI0t9gMlgX~D~vka+pt@*`F4%J(48MsBfZI6w9x@~rO@;-b|c2#>&~n)5DGCC1_2 zqyBmsM3Cz!`{29BnMPsnQ`Kl6V*23nOXv*xhC_K&eO$5Zc`-h=)E}1?YymEUPOQ-8JvcHVm*Rfr4(P?bY*k_G3Z z=<(k2eI;Mx{%^^Z$bgdj*XRhpfG2Zt<*6$k0+`V`GYu#AgC*Ebgm?}D(l~^eT`?uD zwDMPM6yp{gp3W`ZUxO#mqv;jkvikIBP%?>3- zi$rMHQf;0W&49N&gM)DTTL)#+k0YOtP(JmiQ*cgE)%R=S=FdWBF)CmqWVj>rYJwOy z_zm`B(XT$~i37V~59P_R7k8HWd*Bv!;I_12F0!LnJ;*-QZwoXw-gAfu z(t%raRNPw9>w12y6%THFP#mRx*tCZ87Il;rx?K^OE?IKVpC~LYk=9VQk8XlbX`E@zmh*0T>ZtxY5IGg7O4wVHC zl({$Vb#KN*i7e6^ulno9tHKW;zlEn&AB&^Q@A$8wAJ^a;(LMdf0aNd)(|CXvkIBT% z^T!Kk}7uIUA?M6(RQOc@K|wL(26-4 zq#|(4$Z4-L0}vEZAP_2n@u@H zVdG}IpgBV?e37TU?zPAy4vdEU#?4+2)3~TJ&q6`n%}>R?ONCL!QrLxBA#V1o(9`p@ z&q39nv)%&Dxac!Wp%_p1R+)@UUZ$qiTOcVF-KKQf(V z8JWkkh%Msi>FYP=`x6e$`YjE6Ff*Hl+v%|gw|xLy$^z?AnN{`ih8fOur3J?s z?&r#>f5sr@nv{JgR{s(w@S4Cr+NBI`$APJG_j)`bw>#FJvYCFv(B$52PgxZ( zncS`HDQg2b3(;27lkcu=tg5M*h8IwpL!@*zE$GCgM!z~g@`rN#P?q~5;;8C@7kwcZ z!3zrfvA0P0rAy8DPhgEfgxlQEP>a*4I9z8whE0GN_r_6W57jRl8kt*`&Bhk&l1n9R+9lhErdIvW;&VRsyK zvF+gUecSbI7JU<@!=BtZ?`Q6DpE|I|{Vnhva2f~#XMleKRlr%`d*Fwwd)((-d)=Q> z*2@p!HQmI>`>Uexza@9hC)fiE;Qft2Lri9Iv(LjzuKHPE@>|f9sF!o;*^vO=oQIq# z?h-v~8XstoCu6Ry>V;hRTg(Cp#4;B<#kgwHJ&z@qIQ4R%gKlwWUKd!*Ps9O=PcWhE zNi@|f^?ZtByJrfJnLJC_@r{sU{i?6yw!B+jonK*2K|V@7)eN}CGp%<1fXm+f$G@L4 ze{)3a_uI~b#+SK&+WDI~ZpWTWM=$EqT7lR&1JJFcAv!?%2<0>|X9@?u; z>lM7C9;h{*-fa~DVPS3=?{A9WCh#aT8}3PrV$b}7l9bl8z}1x&qvEy%ewp>dZ!SF- zzhG3FNwz}C1iuHrhdE?u;76?RT3-4JKi&kV8TjE#IQ|C5{zC)jv2BPe9XLtrJ~RW* zUxVmZ&!2vD^=k68I`|5F>E2(|HAnUQ2W?^7a%diNUoB5L6vdYAZeHU)g!JY`>~UXZ z0&kYP4<$bqom_H7oZmNUnzO1Q#KJfx0Qs=G({~<-iqEYv{l?t4tD9+Fq6tgCJxQ&i zKr}wp=cnPp%1@|Y;>pNBJqmIWIE>eqb4xpb-cqMPD@E){tcP=VAA+xnM;is7+@Z7Z zkOW_)4er?B`lVn@&df$5Vr_~CMPf69v+x4n&@*0G881_W7XI|$?r)WGbALo7jf)DN z!+~t#T{D9ZzDqhBB*c@utKew-Vo<-q*vsp=H_X7)!^2foGS2!DO9b%>7~A{GdtXbY z7g+ZcN0#rQXID=JXFrP7RWJroB1S~e4Z70Zt>}T@zQ|sEyi|vM)9Y_u;fz3?%jKbH zJN!Yk_fxd=`0a6I8%?3JZK=qtePm_eZbWQC#I>>FW?MBRlZ;TL8wJ@S;JUDn|zaCZK0N!is zww)M9b6y%X)~UZEuf~r)DTsL*#jFc4@wR&KG?`FIU=Cx-CtfT4pT zaC9Li&itEeACq+hnCtrr{D0F+|-MF!a--Z7eub=Mr zH+&m|t)@8g(&xtSpz8-VGqDYR5kIft7X}9>;{40t^2@=d7~Qlo@PdHryzlgS@P#Y? zz1mVy(}G_wMz!R&#Zo9Y7OTb3LCn!qVViJRh>l1izlb#+$6m+>q`Uus17x<(ePLAK zVErT!bwj-)AVsF?2to}GzE56AbX-IcjzPHV2khcX=fmH>Io&xSJ@_Gna5o&XOS3Q;Z)!>h}~_=`!~PEN64edo==U{|?Pa9X{YM zcX$^P#1c?St!C!@<`ED|#+A}EnLF!B&CIyczvy~xoGg}3|EVU>t<>`q zy1~Voz@4R@i>Pu;dNt@L?&#dTI5}2Q)g-sZ{YzDJjr$@o%V2b(shNCja?4YG%EfQx z;01~Pk+_ps*1vwt-rTZYjjGz@UOLeRBPEJKvw{hjwU;TWnTeOy%4X2R$dF40{{kvz z95XP7dd${^-AK4sS~c2^^A0#RG;(^(rIE2)tMA7JLb5dD1|8Bai-x;D9!o=a?9S=2 zrqap=rMog96TFAs2og$jXE~XY;#hr|x4I_fC(N64S5JSr7Hf078x|YbjR){kt_=^i z)N>Ic2!n$u-=qC-gd0zR{uV($$`={99eaE>SmQC7%fb7hfxu>{zwO{)d+2Fc6@`XX zqmXz8zi)$|L>L^LeHO#%M#;<^2>oI&?tyRyyJeX{krwfgNYLr;5FII@VL$$@I>X+V z_iu)La(eI!q;$iO$5HNX7!gEas^Ws%ATzneXbv6VR;V1if{U;F3qA#HsYO3W+X}e} ziBTYMSzGGAb8WQ0HQgpWisee~tlZd3S8>(_uU+V43AeUtIwhk%ZaV)#cb(o|i`VOd z2iyNvqyLI*tshVgxJSOdg4{RU%01%krR2UqcSoQ4^ZUQ02JViHVm@sg#k>n_1eO9N zfFH;K`TX5bZI1+W;H4HN?p0s{dVXbMCEKgC2b-vNh! z3Sc8}5%EShiDFs-_W({H2s`@Tg}wlI6nF`E9T)=8HxC!nCIWfD5WoR+2P{BS;IcW2 z`3l$rtOuS09tR!@{| zF9W-Q&w%fMi$E+c(cKML0VgmJu+&4|8z7zfNC)^7*aK_^)&fg`M}Z<>2#^eP1MUK% zfnN|l2pk2r0WSeh0JDK2U>J}F$Up|lL*G)=@jT#Rpb!`VBm*pPH_#OLtx*(n7WfEg zi~Mx~WFQI10CIrsaC;S40W1Vc06&li3;`TKH{cGS32?0;$_Z2g2Y_wB3gB^I22cbH z1Cjv+=mIncB7y4YDCQgBGvEW@E#PHfDKHNx26BMGzyp92=nmWi+yO)b7m?>vz-Pb* zz&pTN;5pziU@}189cagB;3E3fcR&~D!(X#>#!Y$>5hMd$BH;crPGj7Q^Amj|&HcTW z37!ew;whG_iCOstSz`)3jmZD4><4?`wP#k2rO4~c_vd>HEuC^K-dz0Oh#~9V9)Dt9 zR`I~B!m%E5%Jml4@p0>T)3O<}1)#(U*cQ++`mc;Rem!F@0Y1vY87Qdz2lZppQ^1_FbCo(V}zV`P%@NO|p)17vr~ zwsd0oA^Lv|gPWaUZ}*nnm`+ z|2_=;{f7VF;D_{NtTFWDzq8?o{7;2`I8?It8hZ3zj6K|IHtZ)Dda^Hsp0@P$&(M=G z!q8JZQHGxU*E95#)->qp$s7724Luo6hMvOKH}n*?fuX0c(@77N{0uhq+8BB=wl(w=PpqLQ|BVejjqH03J*7Jndd`Tak)bDJjG?D^nizVDC(h7Q zJY5Yv#Zv;k)riMz=*f7Sp(j7Hpyv%gO$|L6n;ClY^9b~U;b%75l=NhrW#}nC7Q+wa zX9V=3;lH_|Cu1r6lRf!=1bWEL^sfc{z^H$>8+!5|)|3BshCP+B1fI!1r8OM(!=REM zvL9;bk0J6QhMw}7Xy_^3!G@mllVIq-gnvY(e=VUQJ>{VlG(;2VYi;Psp7b_oDBU}u zmkj+~(94G24Shejlb`m6p7MM*^dMn+{+Aef^3wtOB&eic4gDZP-_g)h*iO*@{!7o! zFZAaZ6lB2x`hK3to?`OTFW=|y>77i~z!X3~#ygn-txXypjkM%q zp77*Yviz1#KG1g5%XN2h3KV+~2hqnAU;c!#yp>5PD9X#~S>P#z-;Lf#Gkbre`Otll zW~L~lFM^rUT>>Ne}UROcVcsxPTlXzC9fRNu?w*wA@%oqGS@{!WK^`-5?=zoq^Q_4G2a{T zS&BVl0|i;d7SCi@`FvnT@YrmY-|s0dwD`SVOM$m=ETxr_568l>7Md^I6N>y(EZGHK zU#Oq>DKq(336aEfI_ml5|O!u(<%^M`T6nc^N)I3dfQorg$5=Ii0RghPXO z`h&c6LFn4B_tw#OsiPl(+SK#vC3T=DM^_UAekRddG==QavV49Tw@ID?ln1l{YPR2! zP*A`yqd!CY--17(@eu!a^nriDpOnCz{QWEbggJzM2>xW@LC}O9=ySj0Pby$faombO zu^@bSY}Tc5iCF~&8Tmy_N_vueaH6*$FrhG1G?X_`=WZ*(W{NqNU_B;1EFNw7|V?ITyy^-UWED3Zy~h zto#}AT!g<%7;nHrAPsgKffZjOEfeme01VP+fgbl+bh=GyT8ia9;zQ^o>GbdkCUXH0 zHj`>9ywSYSFk6@#|MHkF#@~(he{4#|g5qEO&1P=?yV3uwh}yr1|M06D!y^&3e~}UY z*zd>}vMlrFCo5Y!KYuc=Ni`!eaYJEqWws+pu&eHzw4jnt))49vN z_jT=dfA=0NXXORaCdqb1b!bjkd_vE}BzLdg$$cK^+b^YmYTAH-=^1zye#p>a!$*vK zaMb9mG1)nu+_8E2<>9H*rq7tEw->cf^M`J1p9{q}5si=B zB7G)vuL57MVvmP`n(pxr&MFA#dMv%J>oIP<+2nsbh@q!|w|38=ksSK!gN&d(7ddT6gJ?!#*~~OBI~EJLAMjD5^G4$X^IbF(>65><^KPv7H_J@s;sDsC2_B25xEL$h5MPtgFEu?qWJ7EY z)|CBw*D*3lsVND4`q$bFw6Ruu<3{&eGV=0$maL*8nniSeDm$yN(CfD#5lcXyqWpOt zOIEf&f1<}S(Nj!Q8ZEBLHYGp1*z5D=`Yql2yE7~UL-}fyR^Z9e3*YRuA5m*jPkC?5gW@=j+bE}Qz^!f=PMZ?d8K!HEspI7XGd<4UC{O|qWx?YSx zIa&TJWVdmnn>tWl7O_DO1w5F&bM)3XItDdhh%Z5zEhuw-p(P8wtT5oC3Y?TRrTc$p z-(mGS{KG!sDMYJz3nyS)q70}XRnfhdeE&YGZLXfxfr zcV`?$Aqz`k7Dj!baD1V6QlZZMh2~RuW6~e5r4#>n%4Gf=AbX-CtrwZhcLHHEsp`^N z^W&>jz8LA(?vgSaRjpb+7vP#hGV!ZHZMq!aDUfdj=?oUiLL@&d&^!!Fa4Q|K`a z8R)8Ho#HLjUDE@Gn&qdk48k$+&%ht~Vc<^D{;&RS=C{IpS(o2&Y&ZKPoBsyRjG1-) zFM|2c{=%FUsgKn8i-gSLKluxXxBRbe`%vL;ZrA@^y!p-L|F7BnyAeQrtvUX}eU41E zdNcDME=CM*zxV&2twd^lnVDLBNEZQaJv6_pTx&9~0m5cd-+uK*^U)1Ang_gIYkv4m z{00+HH+-jECNmZf27jEHUzp>alB2Xfc5aAfoU}q|bHcGO-wJCiu8?BmkB!p)e=CD8De#3kH`m ziI3*hF7KJlHURShrUlFnm?SV)V9LPk13#FDFdbo5!sLWG3R4wkEKFROzc7tqHls-m zU)}jW!7?V03rUUzb7?X0;KWUh@qqgynU2ohP0Gv9&eQF&WGD_`ehqP12A9y;*9Z&= zmRMk^e5f%`cMF(&4@6WtR{{1^=kI-4xt;<@oxr5$7Y4AHnC!_8==^I|VUA@2ChA6D zGmvyG%a+p}`)N1kE}W_MR1W$QEI!18>YEbcC~2u+a4bj}wf(OO)aVuk0;Bk!76?(? zvOr+cZY~h=JSfCb)#ls~8DFn*l#bfFU>{^60F{%>q#pXyWP7hvOY}bzKYF9N!{;}e_y5lP?3X5U6MckYfr|L? z_0oS35t<4z!5mU*&2-h2z6$++rdRvlnaMgi3BRb=|HB_N4K2ITJM1+O5L=eDG_8tK+Xf_Nf{8TUE#IIP6xzE*g9P;m?7+?C6s> zr%CRAp8xps{Kuc?KW=^gqjzLKPu4`w?JVRdgT+!4!CikY~Rgq$4O=wX<1 zSSxxw;idho$kc$pDBw@>jKPHOFNUN5p?p{`!`u^Mm+{zxjl3nt!JMG@vDr!DHaGZ` zhmBp3-&fFFQ@AQJyw~f8DWK5(yx?Fw#2+E$e~?W^e)f2SiMd8~l$b`YjEFjflr}#n z(VOE*182lsiRfM69g|gnD3DJkGGrQ5NRlF)qcB|4A%*dW3peFH{na3Zp*FBd;LZlorr+MK%lHAj$ zK)f^|#e=HNhFxK8n10w|Ly2qj5*Fk`bfd@eRAhRdcao7DvnZlZ;Y2Dk7^F~+RHRav zWAKIupF*i7LSo=8X5NmV)(;sHm`7{#li``{hu_a5QnHG|IRvxfV@5NHXk@=9WJxV} zU*tgSUqLXMl*q(VZlP{sl8Un?>BZJFu}wFJBHeLapUFIi+~yX9vpUQuKempLw@4-- zXB^nIP)ZaHje!{Q6Uwk&XGwZ#vWiF|%`i`(_H&K=QXgiRHVkb%)wSA(5t);gh0?Is1W<2*ulpDFUa!wkluqv{pc)u zil;EZED84r-Lxt)qc|&je62InLa#G#0AkWa2&>0uKm?9+31r*XGliH25A#(-UEaPW z@>VBRY6RwvI(GR*W4u|#IlXkLGu%8B*PtT3ON@nx=WZiz-B-V?DPHVN#xqD9)sW#$ z&&F_vuNNZv_!9H-3vw7PGGv$G4f(-I4XO*HS8EiTk-bpfBO=i>VJoD6HX_A0HZv<9 zA&T{}TvXr*EpmK}nL;NOj7{_x7xYPDn9UJ=F_tKvV&-8=C8TAxMurLyYDt`Rp*(rR zEeobE)DMc$oES=lWD0rEDZwC0rH*+ym95~$dI}%O13!zp@cO7VLKTVKDhhwyoCFau zsBuIpgxA+Cu_=>K1PhF`!axG^Y-DI4qjN#Hi4Nim5v&LFe1>BV#k4cDQ(hYtGhZLm ziRhSqroYjnLZ-#x-V`!2Gb55|%FuldN_P(oulW|hAMGxO>KQ#f+%G{ov1`vLhPfA( z6@3bG@{2LP5}3!0P#D5Y9CAwg(rHGBCWbj2qe3Oohjc+c^{V^B`Seb}o_;>MGsV*F zCflC*{s~#MdJX3Yoh{AKr+Y!UdKL$K5SK2ZI`8j6$$b70MHsL56@8NcqteHG9Fc<6 zWQLdM!iN$4a4aALd+pyxq>A zU`itqI&aNnzN(XYCi?gv`AT3M;Tkpab$ck6nCCsr#v8-9C+l%1Ft7iieI~OlY;VjY z3CwxahQ7lTZct_~s4w+QR1)<)y`)c5xKI;g5X++%$eMHfTi}87MF$PW<8lkSYeNrEzqvkSwCQL9UkVN!F)E&49*r9?heNs}4+;OhyF14;M=ehDj`v`G8=F+@ML(X$BzbF1P2nD*&9!#e+cM&>(Cbs5;fby# z_`o=LbfW1b7tCuk6=mq#SABA@)vMcjC*X7oc(g*Wd9jhn*aZu5A@y~Ah(F0NWfb^A zTBeN&l}FMdedC2;@+p=&=0W6Fk0O(F@EqF99!w^94Vje4eunfp{PsZMnx}eiw4_?;!i$|8A}Qf=#vh zu-~vsZJQQ_vsFMPV5w&^9|EcYbA8wWOas^h@jwx<5jX@?1LkO)#|Pp8b3?cTB|usu zlX)~y1Z)Hj0o8yx27ZAeU?C7#im?Y=rMw#m=Z#cxUMTNiVf2sk`|rMRf353}ej~1~ zId~aw42SL7hur>?xWoCV8`uAY{{IX4AX)ONA5G@PK)4RRs4$uT2qcrO{2A{!L){Fu zA=G(L=`2z81@sH3yP@6&burYYP>Y~8gPH`j71UU$t)ZSeZ!+Hjbv;!4P7vdV+7@a* zsCPnL`;*BGR+L!^l{V<-L46qNWT@Am`k|6=J%?0$C6tMO@loFDL2Mmk_(2*KVQK9% zb@cP0r}EMFL>>KtI{Jlm^iS5&KUGJ+sE*!*xNsVcq4I>Y8SbZH6;ApWHiy+)+26|j zzv@36Pgo6y|DRODWBSx3&@SLOa1{6yI0PI3b_3gh&A?h<8L$|b2h0XafN8*Fzz-Aw z1wbB<1B?bn07HN@APH~)0zm$|0Ud$1fEkDeuHhNNYTzso1dapyfNcP-&gpY3)f>^J zfBs@JcPBmQ)2H~s*MEnZ#z!=+Kc0N7+a9E+xJb3sTSUX>hL6self^1@w9$`Dd8EDJ z`tpg8=Ec<^b=LAq8ahn;j1pa?--+6}@{5(0kn>Id&!YEA@x>eJ8~>sI1jGNE@PF{7 zQ!m}YeEU-HCAtlD<99v&887M~24H{=_~OAYEX=4c#(iPE(KAf54j=uh@(T9BW`shn zz}uJPiPX?evk^qMHS~r;w5x8N-sl>-k3xR_{Qj(g|8fn`*vDJ(A_CI<5)ag%@0UQG zg}I65JsO}EU?xEQklvA|{ziaTU8VVm{9+jB_p$0jr8$=7j#z-=AeHP$rMZOMD9x}N z#Y=a&NR0z1ei{yBNAohx$po?|Izjf7Z!#ws=0wAs29?4LH0;w26$>Rj{-IDQZ911n z@k{_LKoLN5c`-nFrE*YyN&w39EJJ++D!G>e5}GyKs#(G-B5 z3cKlB5gC8oZeP7x{V&ni_3_JMuj|ux#a-8TywEhPFL`F^?P0xT+xFHWebkQk+dLtV z{>ASPeAMRM!~Nqe)i1nJ)OGFO-up{_{GpM*cKY^?CsJEimBv5x%#o)Po79_s?}iuR zuk2Wn^!==+3uZm?cKi;$&aAC%(G!m#bAXUKx=1;&&5I9{B0r9TmAx8!TE+%uJsOz!esao?o~PCORx|SD>K82)8LJP9w{%92@(})0fY8-tp4DkzbY+_Iv;NC6>n` z2aev|DS+}aA0GJ7c--Tv>mjYtk(EXC-(}aZ9Q8!%{3V4=OMiClI^50E`kNsww|xAg z>rh;W*}dEJUjN>jA6)S-znNdKq2Ax6N2^@Cwyo#G-~MCqj?YfJ-u?C0kFsXpHtE2+ zQ?4HUhJT&1d;8+2zkA>H)b4aOkn~~m?ays+eKq!-#di1K?t1OuVpqowd%oHBj>=_~ zKkUjHG<4^T*V3mo-8#y3u-N*PK&Eau8ywNXY8vx|8wTr`&`r4zj$g}&a_^K zz8vJto?8Dvhm1+3HBZjcp8Q2={cv`tM6P1JqkY-O@*};Myv6)_L_I!lNOYUmf+rt+ z*`)57?e5v&&1VDBvF-~0P}@&B9JBt=XW=mW%X7C^WPY*wk(Xb3!}?9$>$j~LxIAg- z7IU{weNNqP6H?P({b+or1w*4&RebgC^sB)=cfD|W-muIk9XrxW?{3w7!r3*IH zUp)P`Q}=(h{G(6yMSc15(J(tr)%<_yXVPg@7mzEWS@BTKP=|!bWt}q7=~bP!mejF? zHiB6Qhk3W~zv2(nso0Gf1+_o+3{v$Fe~&r`J}?4v8YNPzx{Uaun2;>XFzQN@ z?sG7ch1sSTlaIN`gE$MBT*#>D>xeLP6S){Z3Ly-}fRnWA!*@FkO*GxO7PfNB$|VMK_cs{SRVE!tLyS zNd0~w3rI)20ffy)EEcpy4q~BHsnnM1<)GT}0De9F`@oUZrau@i#mGTz9dgL6q~DCi zULt1r2)zaTy1zoCT2QA(Zq6g|4;>MvAN-HieN$}~p&qG(W0^cX7dP8kkfI%bGRkH} zTd<4`B^ZNn7NqybwGKH1(*$Xy8gWy(DSzR*B~z&7Zq8?Sgr&L+moW`-P>Tjom$b7O z4hd=MP3=JIH>$%M!~DJ`>((Pxd<%pDT|jwsx`6Nz(BU(bkfHYfI&4L)-hureRtAaIO8>og_jkjhm`+)44 znro&IDxg*Z)xc%L?l-74gi>G^a1x-%ZvH~qibNGzFs`V^!Y?`8BE$a^)|L=YzD;iL zeA}7gB0@qmza?w7cOV8XKGZ~4tZ(piv5{zO1@DliNyjChTH;_vhy0J`5AfZDLXjy6 znU`62S?lxn@ytTH&xp_3z&eF5;YSFgg@=V1!Xv_?!ehb{!c)RC!fs`fgIPg)%6QvA z-Xec3e=DDnzn6cMf0nPvzseEzRqAo|n!3%gOk3t8PvJCT`OoCvG^PTwI#@gSME(JO z06&Bu#pm)9ct1ate}pgN7xGK^7x>ludVUlC4!@J%#~6H&ayG_5qjNRlp4s3i6I_MEsOI-a+JM;{XTmydy0Lf zeT{v$eV;u~DNvZA2)(`pE``hEeh`1A#Z5_s&Ij+by=Qyh_Mz=#+dpid+rG4YZTr@C z#`eAKN88V~E4E*45mG&=q10HqO==;vmhP0=OP!>9rTZmL5+%E&NePl$>LaB{1EfLH zP-&!;C3&QLX@c~S6p*G!)1_I`9H~s2FFh$eEj=s!MS4+ME&WycoAjFWhV-`dcWI~e zp7g%-q4crz59xF1OX+LrTj`ASz4W8>vvfuJRf>@7$qnVk@@;Yp8JpDlFda@qt}=rD zG3ky>$8g6eN48_EW4y!b@Hr+qraERiN*!|@<&Fi8MUEwo=N-!(D;;Yb>l_;#n;csl zTOB(byBvER`yB@zhaE>8#~dddCmp99RgQCx^Nvf7tBx9nNo%0RXmMIIt)wM=ceHcHDzJB`=8nopagP1R;$S`PM>p< zbES>-(EJny{Zyy~oRnp_QBF|IgQGgnJj8&^A52UllTS62@g?~+`q%jHUR^>+1j zrMl8xnXcikQLb#)Sl4)$*X46fa!qy3bd|d1y2@P(T#H;wT+h3fyH>i^xYoHgxHh@A zxVE}>xOTbry7s#cx(>UJxQ@9_xK6rGyQ*C0T<2YvTvuKAkhsy*e&cU{W<63fUxOQ9zt~1w_>%sAy#HpN%OXPZUeYsRFoy+8gbECLyZY(#R z^Kw3J5;v8b$(3?*xpHm+w}@N9J!P?o{)!M_#TP3S%by*Xwy{&z% zsn&FBrggY=lr`Hr);iwmwfd}+tW&Kst)mutC>+{y-)|J*Z)^*km)=kze z)~(hZppJX3`>hA9hpk7f$E+u;C#|QgRn~LX^VUn&tJWH;iEqHi@Ns-Iz9rv=Z^w7w zJM&%n9z4%Wyvn;k>3Z{h`BXlg&*X=L-evP+`SHA$_wkcJ^=9&={9L}AU%)Q{?R%bI z&adRx@ay;upnzNWt^5vt7r&R^4?1|5Kf)j5Pw*%C)1Zdu`1AZF{wjWs*#w#xBg6^K zgqA`Zp`Fk{=qz*?@{<>0+ihTpT54i(|#{ zqF3~ZlfL7KNx=KAHUXmnLa!H9&Z>g`8Dy2)A(r{^%lr4>w#!FtwCry&3N;9QWX|7Z*Esz#T zOQh$e<uaVcu8{|!x-?z#;*!Z(xtH$Jv|NTiV;$+hKm~Z0~CCVdw3VUA4RHiT2+1zV=jmx;@iA z+&;>lZ69kNZ}-}L_DS}s_L=rl`&@gueSv+EeTn^f`*M9=UT5E6-(=rn-)i4s-(}xx z-)}!?KWsl@KW0B+KWRU0ud<)BpSNGKU$xiRO-chLMu}6JDJ_*YN;{>4(pl-M^iX(3 zQdGsIBr3g?zDlZ+u4F31l~GEzGFBO{com;ANtvq5R7#b(O1ZK?S)?pco>!JDE0s0M zI%R{hN!g-oRdy)5l)cJ+<)Cs{Iieg>PADgp(@K?cPC2h!Qm!gBib-vt#;9>>Gqt7K zMs25dP&=z#)gCIZN~)^5)I_zn+E-0g)74CMxH?MBR>!L2Rj=w(C#h4_nQEyzS1nf; zsEgDk>htPyb)~vSU8inPH>q3Ht?CYSm%3NouO3tnt4Gvh>IwCvdRnbg&#C9tOX_;< z8aSUwo!2pA4(_JeHO3N2>>qQ1pDA?2D2ul(lU|cA$T{{iN)xrYYDefxYCT7cqqF0F zhv2Ydj{k?_tfQ^AS$o`h$oZwS3FfnYuIaAFTz|*BgEy&&Rb*`JK=x703?G92Ut?KL z<8rte-0PswXSfJZ-Z9pPt*?VZHsWQ_wEg@*P@9RM7*)bBF-Lp@WB7zP-}Zv7QvN}{ zEJvaZoysirb2Y*-&hZlRz-c43zhd5RjX8Xi^Ic~H*HYI{w1|(7BsP+1%Eq%}*f{P% z?!0v(zY6?dq7W_i6pO`|#81RXo7I+Md)BtzR%sgzj<=zFuRK_ORsIxw>HT&Yy>I|} zi-MluN83Gs`k#iHe_CCpzN)@~8vaPFM7>^78PrQNM{CDjj*gCQ4y!|QXpUq@iem^k zsA9(~$2@RLFF4jZ);l&jHap&OY;#mNc02YtPB}Wd9IhbcH!TvUIC1(1GxA-mgB{1t zW|y*WvhQ)nxtZ3#U~YNO`nk0g?*OO!SN?TS@Oy;`LVIzjxEPdYgYEC&HxGltoCjyw z5HuwpoZ<{{epjRx7_rmkyY2U(7d@-)RFA4V9iKVu+CXqpd$rqKEnKZ#ce>iUI=Sw3 z-48C~ITte;eFHpTB0HEJ#y*Jp%SC<)*<$uZ_ARy*SIkZ19!4uZ!ad48#y!D}#vFLq zT50{!dKYiwhw@o`0sk;Rn}33j6j{5GKN^FXy@Y=X^hOR)6=ZGFi0 zhV47s1zSt$LCG(fWf62ITYg9m$WxH-S@ImYOr9@y0!_%U53`T9zlf3DQ0br3qI|0~!o-Q*%xONBq6B8#vivl;1;<%u*;>;20lc-x0P8?+SZ_4}_0|PlV5eFNCjz zZ-noJe+sqvj1=pOjl?EmQ}K534)HGWZt))RKC!!K6>Xv-I#J`j#0SLw;y`h*I80n- zYc9=`o{-vN)_O`_3JOtX|GWJ&`za+>4LBUyFsvC&&PL9G&Y^ny{^E>q{XqGu42`!A z>@#d*?vOQ(xAKd@FLB^;)(Llsf|y`Cfpy@0@7d@LxwQ$}Ls|g+ zX}UH`o1-n&mT4=rRoYr@y|xkb@GWhdR-x_I_Gt&SL)xd>QSG=^sh!eJ)>#Yu6S?@w zbU3%^lP9immAv0t)Ja8Gg1aL;isa4&H$bFXl(f>yrCy~Azi-sSdiA8;RWpKzaX zUvOV>-*DfdFPeCf&j5#byKskamvFamk8q#RU9bu^K@prnPobgMSiDVaA+{Fp6x)lP z#Cyg2MNa%k>}cz6dmZc2WGPL$C^eQ@%=ZK2N9CvGi}GFe2kiGcSjRJtzc}7=G}fAH ze(eXXu~Wc!D51K8ASW6BXR^!Kuh|#45!Nrj(>CTu@-slg%lU8d(>l$OkCBiBj0CUr zxFjf#D+km)jv}m=_B!XeR=GA&JZ6(VM@+>EA&)EMzU78mM_RM29&5gJg7qP5zyeuphHQR%>Lfd@XM$o>CwpR8LO1{!cor78LNA(@70#9lUop(6X zuzs87d=#Vo4Xn|2VvK+0JnsC(#aK-AJK0Q6HiPxE53|p*P0^E+xGb(I`2LTPdL(#r z7q1BOFxuZ1&xkHtJ4u#uB?Mwk`o7~p(7M_DkHS;pdU3bNN%)arm5Dd$bE|+E{0JqG zW`bl9@?ppjSi8;cu=lj*X(gb)3$(?0?|Bbv)P_#8v$?aCv#rzO?C9*`?B<+^T$g}S z&2#?Xyy9$*`PG7blP<14SPiart#@s7ZFaro+UBZo?RM>>`W$UymO*LCwqo0|7PceS zYOi6{)d^JM1+0Zy@sUD?m?u62o_Uk_HuC$v&LMv;eyQ`v--|zr7eSjpv1w|5bui|O zhrnr+gQM7{?g8h}#BsZ$ts}!R3=)B5jyD}2I^wk%+I-Bvo3!_|+pwbU=(J)LU5H#i z>iod@plcdds0Um>Q{IY9%mFCfF^^1Sr-PC{4+{1+dx-s(ZHKiV&)K;LxMAFANK(qM zu6qvax$WrhvURj|H&#++z9UAc4CzlM)CY=z{cPsn*4IA! z_z$u2ImZ72*-wnnT$m-4fv5Oh_(l}K*$=n&Z)k68f7f=R z9PeYyeyROayQty8X~+p2SpP3@nn43SuFqZ9s4OKW<}4H*yv0*&Gj1F=46DLj)=9|E zxBM$eCsjTmv{u?EZI#}Y zK9CMUPV*gPG#4=sW@0q%kl&LJ$e+lcgM+SbkF^KvGwieNF-j*z28TKrd}z6{9X#ew zN|b6=E#T7nfn)URJjw=)f}naI#<9aO+wmBrG^5e+HC1FnIt9E|pzE@ok{V3qtl_>?Wy?O5kt#n`c8B|DPO!J3s?=`Y}0Hllqhuo6Ab|H{|H>azt_oGXOY zSTDXO1jN6H7ane6DavT25d7tFC8#i14Ri$eKSCV`K7X$IqWU-R@*g7q^}$<;4yPjta=0?b zUma1U{ZFw30f9=5(~{S4BJ2)-$Q zCu9_MJ|1$3A$&eRhku4&%fE)PR*jKnf)t@A#@TB^0oLa0z`K8gRm8jCf}*gposV{W zSvn=%jdfaI=^uSooAiZ&I=?*t}yAl2?4ghbr|NA zg^<$x)%vRS9hB&El<2JWqBV=3kNkheALq~VKSDARDKrwyLMzOrozORJm_rjG(JsOa z@T~BPfMX>~@r_ zQZH$kQ~*hA6;|Ih;3}HQ?Xl8OmeQ2VI4>TGqj`Wn`C)UPk8uYe98bzE^IYq`!O$OGSTF^5e04oWN} zc5ku?yqoXCr+{(}0&hMN+_?vBH-VoFo`Dq-ga?I4$UG)MO7XSLirFn0vV}6p6*frk zNRe{1Tq$$ly$4{u{iOXzJB^-r$5&_}M(d*4v?Oo_(U`50slF@m_5t=T9q1tk_(PDt z2T`ll`~|*3*e&c64hV;YPf>rzu@*Qb1ckGx#S6k^;Tmc(N{kj`MYGskY$di8En-Kp zi`Y$M!H3DBLyQ-b#ALCbm?mb3L&Oo{XsiwMz-i<|9l*|*s%bm{yd z)ZJ10ag4K5_MrW&z1n`ke%XG_&L~kzv=XbBmF7w-rLAI7Ix1bTmS7bD6xE@`D@hoU z{ggDUDuyT{Ffwx#lJpm0d`?!TDJ9BmWgd8$h00=ODLBFv$|_~8vR>J!Y(^VxQ!13* z%0A_Qa!C19If@Zoshq;7K8rc#f^u28hVdPxMys)))y>sbYFkk3j%pXRo64#JC|JCj zq$Y!cr>Pn05YT|pY7XdGfm#HLJ{c>|614n0(6(jj3U!sbR$Z^=X$7FWg4gADx z(DcW(g$7UYpKzo>?W{4HAQ5mzVZMvixsWtxhI5E>1n5N$=7s`ik<;&-eA8H4=3L=i zg?VE=)=!(AZ#lO)D?nNHIS+ut`qX(8JXWRi)b;Ur*?G;$xT16pMs_6=&8ZIYkw@7e zdx71InWO@9$pO%vG|c*wZL@9j^!3VG+h*HaHlha6S}XKAL6fy)J?F<=l`eYo62E7G z=or=vz9*R-!sfA)*?DYR&cZPk{N@POvQca_8>_n$U(|&a!1E1ZN3f&W9QZC^i@*U+ zW~Z?wSa;6@ce0ROjP>_2b_KhNU5oi(BfA-E-)(FKyPMs|9>Ch`I9rJsQRb4leq08( z(7M>V!n(@3*1F!h(Yo2X4P46s>!;S^)>EJ}7p&K;QG6`loNo(0rW-F{9iPPa<1PQ? zUOrEJTwEwF7MF_4#1-NyaV_}HjpAnUEv!N-z|rj!4~U1vO7WC&l>o%2L7ypKWpI68u+sY{;Yxj|JA_%1J~MzBLDyZ literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/Scintilla.exp b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/Scintilla.exp new file mode 100644 index 0000000000000000000000000000000000000000..7b464bf48f40cc66fe79ef7580c7549f36fce1e4 GIT binary patch literal 644 zcmah`O;5r=5FJ3Q8V*FfsR;)Tn3xTC@LaGM38j#>985?yrLAO>(l({2@#0VNANa$( z;LJjcA2CikuW#PI-PsSlP=uNVyVWnDG2W|J0)8YOu5tz2msEsQojz}ABV&Iz_rgg0dQQGQ+;|7%2TUjyf25%Yg6{o#0I+Fa{d hH)elqkIbDP7ceHe&VO0pb=Oup?7N9E;9AG-z$bIWc>n+a literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/Scintilla.lib b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/bin/Scintilla.lib new file mode 100644 index 0000000000000000000000000000000000000000..963c00089ca29b7bd9015ae06c530a4cf496566d GIT binary patch literal 1826 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%0D2*Cuju<2&6$3F6<1huYum@*w3DX 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

    ;_NJMWB$ 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 UI9vmVYv6DV9Ik=?H)`Pj08>kJrvLx| literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Design.html b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Design.html new file mode 100644 index 00000000..d426cb36 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Design.html @@ -0,0 +1,249 @@ + + + + + + + + + Scintilla and SciTE + + + + + + + + +
    + Scintilla icon + + Scintilla + Component Design +
    +

    + Top level structure +

    +

    + Scintilla consists of three major layers of C++ code +

    +
      +
    • + Portability Library +
    • +
    • + Core Code +
    • +
    • + Platform Events and API +
    • +
    +

    + The primary purpose of this structure is to separate the platform dependent code from the + platform independent core code. This makes it easier to port Scintilla to a new platform and + ensures that most readers of the code do not have to deal with platform details. To minimise + portability problems and avoid code bloat, a conservative subset of C++ is used in Scintilla + with no exception handling, run time type information or use of the standard C++ + library and with limited use of templates. +

    +

    + The currently supported platforms, Windows, GTK+/Linux and wxWindows are fairly similar in + many ways. + Each has windows, menus and bitmaps. These features generally work in similar ways so each + has a way to move a window or draw a red line. Sometimes one platform requires a sequence of + calls rather than a single call. At other times, the differences are more profound. Reading + the Windows clipboard occurs synchronously but reading the GTK+ clipboard requires a request + call that will be asynchronously answered with a message containing the clipboard data. + The wxWindows platform is available from the wxWindows site +

    +
    +

    + Portability Library +

    +

    + This is a fairly small and thin layer over the platform's native capabilities. +

    +

    + The portability library is defined in Platform.h and is implemented once for each platform. + PlatWin.cxx defines the Windows variants of the methods and PlatGTK.cxx the GTK+ variants. +

    +

    + Several of the classes here hold platform specific object identifiers and act as proxies to + these platform objects. Most client code can thus manipulate the platform objects without + caring which is the current platform. Sometimes client code needs access to the underlying + object identifiers and this is provided by the GetID method. The underlying types of the + platform specific identifiers are typedefed to common names to allow them to be transferred + around in client code where needed. +

    +

    + Point, PRectangle +

    +

    + These are simple classes provided to hold the commonly used geometric primitives. A + PRectangle follows the Mac / Windows convention of not including its bottom and right sides + instead of including all its sides as is normal in GTK+. It is not called Rectangle as this may be + the name of a macro on Windows. +

    +

    + Colour, ColourPair, Palette +

    +

    + Colour holds a platform specific colour identifier - COLORREF for Windows and GdkColor for + GTK+. The red, green and blue components that make up the colour are limited to the 8 bits of + precision available on Windows. ColourPairs are used because not all possible colours are + always available. Using an 8 bit colour mode, which is a common setting for both Windows and + GTK+, only 256 colours are possible on the display. Thus when an application asks for a dull + red, say #400000, it may only be allocated an already available colour such as #800000 or + #330000. With 16 or 2 colour modes even less choice is available and the application will + have to use the limited set of already available colours. +

    + A Palette object holds a set of colour pairs and can make the appropriate calls to ask to + allocate these colours and to see what the platform has decided will be allowed. +

    + Font +

    +

    + Font holds a platform specific font identifier - HFONT for Windows, GdkFont* for GTK+. It + does not own the identifier and so will not delete the platform font object in its + destructor. Client code should call Destroy at appropriate times. +

    +

    + Surface +

    +

    + Surface is an abstraction over each platform's concept of somewhere that graphical drawing + operations can be done. It may wrap an already created drawing place such as a window or be + used to create a bitmap that can be drawn into and later copied onto another surface. On + Windows it wraps a HDC and possibly a HBITMAP. On GTK+ it wraps a GdkDrawable* and possibly a + GdkPixmap*. Other platform specific objects are created (and correctly destroyed) whenever + required to perform drawing actions. +

    +

    + Drawing operations provided include drawing filled and unfilled polygons, lines, rectangles, + ellipses and text. The height and width of text as well as other details can be measured. + Operations can be clipped to a rectangle. Most of the calls are stateless with all parameters + being passed at each call. The exception to this is line drawing which is performed by + calling MoveTo and then LineTo. +

    +

    + Window +

    +

    + Window acts as a proxy to a platform window allowing operations such as showing, moving, + redrawing, and destroying to be performed. It contains a platform specific window identifier + - HWND for Windows, GtkWidget* for GTK+. +

    +

    + ListBox +

    +

    + ListBox is a subclass of Window and acts as a proxy to a platform listbox adding methods for + operations such as adding, retrieving, and selecting items. +

    +

    + Menu +

    +

    + Menu is a small helper class for constructing popup menus. It contains the platform specific + menu identifier - HMENU for Windows, GtkItemFactory* for GTK+. Most of the work in + constructing menus requires access to platform events and so is done in the Platform Events + and API layer. +

    +

    + Platform +

    +

    + The Platform class is used to access the facilities of the platform. System wide parameters + such as double click speed and chrome colour are available from Platform. Utility functions + such as DebugPrintf are also available from Platform. +

    +

    + Core Code +

    +

    + The bulk of Scintilla's code is platform independent. This is made up of the CellBuffer, + ContractionState, Document, Editor, Indicator, LineMarker, Style, ViewStyle, KeyMap, + ScintillaBase, CallTip, + and AutoComplete primary classes. +

    +

    + CellBuffer +

    +

    + A CellBuffer holds text and styling information, the undo stack, the assignment of line + markers to lines, and the fold structure. +

    +

    + A cell contains a character byte and its associated style byte. The current state of the + cell buffer is the sequence of cells that make up the text and a sequence of line information + containing the starting position of each line and any markers assigned to each line. +

    +

    + The undo stack holds a sequence of actions on the cell buffer. Each action is one of a text + insertion, a text deletion or an undo start action. The start actions are used to group + sequences of text insertions and deletions together so they can be undone together. To + perform an undo operation, each insertion or deletion is undone in reverse sequence. + Similarly, redo reapplies each action to the buffer in sequence. Whenever a character is + inserted in the buffer either directly through a call such as InsertString or through undo or + redo, its styling byte is initially set to zero. Client code is responsible for styling each + character whenever convenient. Styling information is not stored in undo actions. +

    +

    + Document +

    +

    + A document contains a CellBuffer and deals with some higher level abstractions such as + words, DBCS character sequences and line end character sequences. It is responsible for + managing the styling process and for notifying other objects when changes occur to the + document. +

    +

    + Editor +

    +

    + The Editor object is central to Scintilla. It is responsible for displaying a document and + responding to user actions and requests from the container. It uses ContractionState, Indicator, + LineMarker, Style, and ViewStyle objects to display the document and a KeyMap class to + map key presses to functions. + The visibility of each line is kept in the ContractionState which is also responsible for mapping + from display lines to documents lines and vice versa. +

    +

    + There may be multiple Editor objects attached to one Document object. Changes to a + document are broadcast to the editors through the DocWatcher mechanism. +

    +

    + ScintillaBase +

    +

    + ScintillaBase is a subclass of Editor and adds extra windowing features including display of + calltips, autocompletion lists and context menus. These features use CallTip and AutoComplete + objects. This class is optional so a lightweight implementation of Scintilla may bypass it if + the added functionality is not required. +

    +

    + Platform Events and API +

    +

    + Each platform uses different mechanisms for receiving events. On Windows, events are + received through messages and COM. On GTK+, callback functions are used. +

    +

    + For each platform, a class is derived from ScintillaBase (and thus from Editor). This is + ScintillaWin on Windows and ScintillaGTK on GTK+. These classes are responsible for + connecting to the platforms event mechanism and also to implement some virtual methods in + Editor and ScintillaBase which are different on the platforms. For example, this layer has to + support this difference between the synchronous Windows clipboard and the asynchronous GTK+ + clipboard. +

    +

    + The external API is defined in this layer as each platform has different preferred styles of + API - messages on Windows and function calls on GTK+. This also allows multiple APIs to be + defined on a platform. The currently available API on GTK+ is similar to the Windows API and + does not follow platform conventions well. A second API could be implemented here that did + follow platform conventions. +

    + + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Icons.html b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Icons.html new file mode 100644 index 00000000..0d8b8bf5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Icons.html @@ -0,0 +1,56 @@ + + + + + + + + + Scintilla icons + + + + + + + + +
    + Scintilla icon + + Scintilla + and SciTE +
    +

    + Icons +

    +

    + These images may be used under the same license as Scintilla. +

    +

    + Drawn by Iago Rubio, Philippe Lhoste, and Neil Hodgson. +

    +

    + zip format (70K) +

    + + + + + + + + + + + + + + + + + +
    For autocompletion listsFor margin markers
    12x1216x1624x2432x32
    + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Lexer.txt b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Lexer.txt new file mode 100644 index 00000000..5f118e73 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Lexer.txt @@ -0,0 +1,226 @@ +How to write a scintilla lexer + +A lexer for a particular language determines how a specified range of +text shall be colored. Writing a lexer is relatively straightforward +because the lexer need only color given text. The harder job of +determining how much text actually needs to be colored is handled by +Scintilla itself, that is, the lexer's caller. + + +Parameters + +The lexer for language LLL has the following prototype: + + static void ColouriseLLLDoc ( + unsigned int startPos, int length, + int initStyle, + WordList *keywordlists[], + Accessor &styler); + +The styler parameter is an Accessor object. The lexer must use this +object to access the text to be colored. The lexer gets the character +at position i using styler.SafeGetCharAt(i); + +The startPos and length parameters indicate the range of text to be +recolored; the lexer must determine the proper color for all characters +in positions startPos through startPos+length. + +The initStyle parameter indicates the initial state, that is, the state +at the character before startPos. States also indicate the coloring to +be used for a particular range of text. + +Note: the character at StartPos is assumed to start a line, so if a +newline terminates the initStyle state the lexer should enter its +default state (or whatever state should follow initStyle). + +The keywordlists parameter specifies the keywords that the lexer must +recognize. A WordList class object contains methods that make simplify +the recognition of keywords. Present lexers use a helper function +called classifyWordLLL to recognize keywords. These functions show how +to use the keywordlists parameter to recognize keywords. This +documentation will not discuss keywords further. + + +The lexer code + +The task of a lexer can be summarized briefly: for each range r of +characters that are to be colored the same, the lexer should call + + styler.ColourTo(i, state) + +where i is the position of the last character of the range r. The lexer +should set the state variable to the coloring state of the character at +position i and continue until the entire text has been colored. + +Note 1: the styler (Accessor) object remembers the i parameter in the +previous calls to styler.ColourTo, so the single i parameter suffices to +indicate a range of characters. + +Note 2: As a side effect of calling styler.ColourTo(i,state), the +coloring states of all characters in the range are remembered so that +Scintilla may set the initStyle parameter correctly on future calls to +the +lexer. + + +Lexer organization + +There are at least two ways to organize the code of each lexer. Present +lexers use what might be called a "character-based" approach: the outer +loop iterates over characters, like this: + + lengthDoc = startPos + length ; + for (unsigned int i = startPos; i < lengthDoc; i++) { + chNext = styler.SafeGetCharAt(i + 1); + << handle special cases >> + switch(state) { + // Handlers examine only ch and chNext. + // Handlers call styler.ColorTo(i,state) if the state changes. + case state_1: << handle ch in state 1 >> + case state_2: << handle ch in state 2 >> + ... + case state_n: << handle ch in state n >> + } + chPrev = ch; + } + styler.ColourTo(lengthDoc - 1, state); + + +An alternative would be to use a "state-based" approach. The outer loop +would iterate over states, like this: + + lengthDoc = startPos+lenth ; + for ( unsigned int i = startPos ;; ) { + char ch = styler.SafeGetCharAt(i); + int new_state = 0 ; + switch ( state ) { + // scanners set new_state if they set the next state. + case state_1: << scan to the end of state 1 >> break ; + case state_2: << scan to the end of state 2 >> break ; + case default_state: + << scan to the next non-default state and set new_state >> + } + styler.ColourTo(i, state); + if ( i >= lengthDoc ) break ; + if ( ! new_state ) { + ch = styler.SafeGetCharAt(i); + << set state based on ch in the default state >> + } + } + styler.ColourTo(lengthDoc - 1, state); + +This approach might seem to be more natural. State scanners are simpler +than character scanners because less needs to be done. For example, +there is no need to test for the start of a C string inside the scanner +for a C comment. Also this way makes it natural to define routines that +could be used by more than one scanner; for example, a scanToEndOfLine +routine. + +However, the special cases handled in the main loop in the +character-based approach would have to be handled by each state scanner, +so both approaches have advantages. These special cases are discussed +below. + +Special case: Lead characters + +Lead bytes are part of DBCS processing for languages such as Japanese +using an encoding such as Shift-JIS. In these encodings, extended +(16-bit) characters are encoded as a lead byte followed by a trail byte. + +Lead bytes are rarely of any lexical significance, normally only being +allowed within strings and comments. In such contexts, lexers should +ignore ch if styler.IsLeadByte(ch) returns TRUE. + +Note: UTF-8 is simpler than Shift-JIS, so no special handling is +applied for it. All UTF-8 extended characters are >= 128 and none are +lexically significant in programming languages which, so far, use only +characters in ASCII for operators, comment markers, etc. + + +Special case: Folding + +Folding may be performed in the lexer function. It is better to use a +separate folder function as that avoids some troublesome interaction +between styling and folding. The folder function will be run after the +lexer function if folding is enabled. The rest of this section explains +how to perform folding within the lexer function. + +During initialization, lexers that support folding set + + bool fold = styler.GetPropertyInt("fold"); + +If folding is enabled in the editor, fold will be TRUE and the lexer +should call: + + styler.SetLevel(line, level); + +at the end of each line and just before exiting. + +The line parameter is simply the count of the number of newlines seen. +It's initial value is styler.GetLine(startPos) and it is incremented +(after calling styler.SetLevel) whenever a newline is seen. + +The level parameter is the desired indentation level in the low 12 bits, +along with flag bits in the upper four bits. The indentation level +depends on the language. For C++, it is incremented when the lexer sees +a '{' and decremented when the lexer sees a '}' (outside of strings and +comments, of course). + +The following flag bits, defined in Scintilla.h, may be set or cleared +in the flags parameter. The SC_FOLDLEVELWHITEFLAG flag is set if the +lexer considers that the line contains nothing but whitespace. The +SC_FOLDLEVELHEADERFLAG flag indicates that the line is a fold point. +This normally means that the next line has a greater level than present +line. However, the lexer may have some other basis for determining a +fold point. For example, a lexer might create a header line for the +first line of a function definition rather than the last. + +The SC_FOLDLEVELNUMBERMASK mask denotes the level number in the low 12 +bits of the level param. This mask may be used to isolate either flags +or level numbers. + +For example, the C++ lexer contains the following code when a newline is +seen: + + if (fold) { + int lev = levelPrev; + + // Set the "all whitespace" bit if the line is blank. + if (visChars == 0) + lev |= SC_FOLDLEVELWHITEFLAG; + + // Set the "header" bit if needed. + if ((levelCurrent > levelPrev) && (visChars > 0)) + lev |= SC_FOLDLEVELHEADERFLAG; + styler.SetLevel(lineCurrent, lev); + + // reinitialize the folding vars describing the present line. + lineCurrent++; + visChars = 0; // Number of non-whitespace characters on the line. + levelPrev = levelCurrent; + } + +The following code appears in the C++ lexer just before exit: + + // Fill in the real level of the next line, keeping the current flags + // as they will be filled in later. + if (fold) { + // Mask off the level number, leaving only the previous flags. + int flagsNext = styler.LevelAt(lineCurrent); + flagsNext &= ~SC_FOLDLEVELNUMBERMASK; + styler.SetLevel(lineCurrent, levelPrev | flagsNext); + } + + +Don't worry about performance + +The writer of a lexer may safely ignore performance considerations: the +cost of redrawing the screen is several orders of magnitude greater than +the cost of function calls, etc. Moreover, Scintilla performs all the +important optimizations; Scintilla ensures that a lexer will be called +only to recolor text that actually needs to be recolored. Finally, it +is not necessary to avoid extra calls to styler.ColourTo: the sytler +object buffers calls to ColourTo to avoid multiple updates of the +screen. + +Page contributed by Edward K. Ream \ No newline at end of file diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciBreak.jpg b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciBreak.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65c9fc740133c9c1ed590b0de368e4dc57a04ec6 GIT binary patch literal 14998 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 + + + + + + + Scintilla and SciTE Code Style Preferences + + + + + + + + + +
    + Scintilla icon + + Scintilla + and SciTE +
    +

    + Code Style +

    +

    + Introduction +

    +

    + The source code of Scintilla and SciTE follow my preferences. + Some of these decisions are arbitrary and based on my sense of aesthetics + but its good to have all the code look the same even if its not exactly how + everyone would prefer. +

    +

    + Code that does not follow these conventions will be accepted, but will be modified + as time goes by to fit the conventions. Scintilla code follows the conventions more + closely than SciTE except for lexers which are relatively independent modules. + Lexers that are maintained by others are left as they are submitted except that + warnings will be fixed so the whole project can compile cleanly. +

    +

    + The AStyle formatting + program with a '-tapO' argument formats code in much the right way although + there are a few bugs in AStyle. The scite/scripts/Fixer.py script will run AStyle + over a C++ source file and fix up some of those bugs. +

    +

    + Language features +

    +

    + Design goals for Scintilla and SciTE include portability to currently available C++ + compilers on diverse platforms with high performance and low resource usage. + Scintilla has stricter portability requirements to SciTE as it may be ported to + low capability platforms such as Windows CE or PalmOS but it is less likely + SciTE will be. +

    +

    + To achieve portability, only a subset of C++ features are used. Exceptions are + not available on some platforms such as Windows CE so exceptions are not used + and thus the standard C++ library can not be used. + Template support differs between compilers so is not used in Scintilla but there + are some simple uses in SciTE. + Run-time type information adds to memory use so is turned off. + Name spaces are not used. +

    +

    + The goto statement is not used because of bad memories from my first job + maintaining FORTRAN programs. The union feature is not used as it can lead to + non-type-safe value access. +

    +

    + Casting +

    +

    + Do not use old C style casts like (char *)s. Instead use the most strict form of C++ + cast possible like const_cast<char *>(s). Use static_cast and const_cast + where possible rather than reinterpret_cast. Because the code is compiled with + run-time type information turned off, dynamic_cast will not work. +

    +

    + The benefit to using the new style casts is that they explicitly detail what evil is + occurring and act as signals that something potentially unsafe is being done. +

    +

    + Code that treats const seriously is easier to reason about both for humans + and compilers, so use const parameters and avoid const_cast. +

    +

    + Warnings +

    +

    + To help ensure code is well written and portable, it is compiled with almost all + warnings turned on. This sometimes results in warnings about code that is + completely good (false positives) but changing the code to avoid the warnings + is generally fast and has little impact on readability. +

    +

    + Initialise all variables and minimise the scope of variables. If a variable is defined + just before its use then it can't be misused by code before that point. + Use loop declarations that are compatible with both the C++ standard and currently + available compilers. +

    +

    + Allocation +

    +

    + As exceptions are not used, memory exhaustion can occur. + This should be checked for and handled but there is quite a lot of Scintilla and + SciTE code that doesn't yet. + Fixed length buffers are often used as these are simple and avoid the need to + worry about memory exhaustion but then require that buffer lengths are + respected. +

    +

    + The C++ new and delete operators are preferred over C's malloc and free + as new and delete are type safe. +

    +

    + Bracketing +

    +

    + Start brackets, '{', should be located on the line of the control structure they + start and end brackets, '}', should be at the indented start of a line. When there is + an else clause, this occurs on the same line as the '}'. + This format uses less lines than alternatives, allowing more code to be seen on screen. + Fully bracketed control + structures are preferred because this makes it more likely that modifications will + be correct and it allows Scintilla's folder to work. No braces on returned + expressions as return is a keyword, not a function call. +

    +bool fn(int a) {
    +        
    if (a) {
    +                
    s();
    +                
    t();
    +        
    } else {
    +                
    u();
    +        
    }
    +        
    return !a;
    +
    }
    +

    + Spacing +

    +

    + Spaces on both sides of '=' and comparison operators and no attempt to line up '='. + No space before or after '(', when used in calls, but a space after every ','. + No spaces between tokens in short expressions but may be present in + longer expressions. Space before '{'. No space before ';'. + No space after '*' when used to mean pointer and no space after '[' or ']'. + One space between keywords and '('. +

    +void StoreConditionally(int c, const char *s) {
    +        
    if (c && (baseSegment == trustSegment["html"])) {
    +                
    baseSegment = s+1;
    +                
    Store(s, baseSegment, "html");
    +        
    }
    +
    } +

    + Names +

    +

    + Identifiers use mixed case and no underscores. + Class, function and method names start with an uppercase letter and use + further upper case letters to distinguish words. Variables start with a lower + case letter and use upper case letters to distinguish words. + Loop counters and similar variables can have simple names like 'i'. + Function calls should be differentiated from method calls with an initial '::' + global scope modifier. +

    +class StorageZone {
    +
    public:
    +        
    void Store(const char *s) {
    +                
    Media *mediaStore = ::GetBaseMedia(zoneDefault);
    +                
    for (int i=mediaStore->cursor; mediaStore[i], i++) {
    +                        
    mediaStore->Persist(s[i]);
    +                
    }
    +        
    }
    +
    }; + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciRest.jpg b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciRest.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4b052239cd953c92a5ec4bc22fb9aed2cc545a1b GIT binary patch literal 16680 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 Uw^L7}bKU68RwQB4@Net?2Xk}R0ssI2 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciTEIco.png b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciTEIco.png new file mode 100644 index 0000000000000000000000000000000000000000..d0cc869b8c56c3d77ba76ff1a81b22609836022e GIT binary patch literal 10091 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 N002ovPDHLkV1k*@zUlw~ literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciWord.jpg b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/SciWord.jpg new file mode 100644 index 0000000000000000000000000000000000000000..60e70e897b41b1943952b42aab2592cfe6452883 GIT binary patch literal 6164 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 literal 0 HcmV?d00001 diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaDoc.html b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaDoc.html new file mode 100644 index 00000000..08dad961 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaDoc.html @@ -0,0 +1,5350 @@ + + + + + + + + + + Scintilla and SciTE + + + + + + + + + + + +
    Scintilla iconScintilla
    + +

    Scintilla Documentation

    + +

    Last edited 4/February/2006 NH

    + +

    There is an overview of the internal design of + Scintilla.
    + Some notes on using Scintilla.
    + How to use the Scintilla Edit Control on Windows.
    + A simple sample using Scintilla from + C++ on Windows.
    + A simple sample using Scintilla from + Visual Basic.
    + Bait is a tiny sample using Scintilla + on GTK+.
    + A detailed description of how to write a lexer, including a + discussion of folding.
    + + How to implement a lexer in the container.
    + + How to implement folding.
    + The coding style used in Scintilla and SciTE is + worth following if you want to contribute code to Scintilla but is not compulsory.

    + +

    Introduction

    + +

    The Windows version of Scintilla is a Windows Control. As such, its primary programming + interface is through Windows messages. Early versions of Scintilla emulated much of the API + defined by the standard Windows Edit and RichEdit controls but those APIs are now deprecated in + favour of Scintilla's own, more consistent API. In addition to messages performing the actions + of a normal Edit control, Scintilla allows control of syntax styling, folding, markers, autocompletion + and call tips.

    + +

    The GTK+ version also uses messages in a similar way to the Windows version. This is + different to normal GTK+ practice but made it easier to implement rapidly.

    + +

    This documentation describes the individual messages and notifications used by Scintilla. It + does not describe how to link them together to form a useful editor. For now, the best way to + work out how to develop using Scintilla is to see how SciTE uses it. SciTE exercises most of + Scintilla's facilities.

    + +

    In the descriptions that follow, the messages are described as function calls with zero, one + or two arguments. These two arguments are the standard wParam and + lParam familiar to Windows programmers. These parameters are integers that + are large enough to hold pointers, and the return value is also an integer large enough to contain a + pointer. + Although the commands only use the + arguments described, because all messages have two arguments whether Scintilla uses them or + not, it is strongly recommended that any unused arguments are set to 0. This allows future + enhancement of messages without the risk of breaking existing code. Common argument types + are:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    boolArguments expect the values 0 for false and 1 for + true.
    intArguments are 32-bit signed integers.
    const char *Arguments point at text that is being passed to Scintilla but not modified. The text + may be zero terminated or another argument may specify the character count, the + description will make this clear.
    char *Arguments point at text buffers that Scintilla will fill with text. In some cases, + another argument will tell Scintilla the buffer size. In others, you must make sure that + the buffer is big enough to hold the requested text. If a NULL pointer (0) is passed + then, for SCI_* calls, the length that should be allocated is returned.
    colourColours are set using the RGB format (Red, Green, Blue). The intensity of each colour + is set in the range 0 to 255. If you have three such intensities, they are combined as: + red | (green << 8) | (blue << 16). If you set all intensities to 255, the + colour is white. If you set all intensities to 0, the colour is black. When you set a + colour, you are making a request. What you will get depends on the capabilities of the + system and the current screen mode.
    alphaTranslucency is set using an alpha value. + Alpha ranges from 0 (SC_ALPHA_TRANSPARENT) which is completely transparent to + 255 (SC_ALPHA_OPAQUE) which is opaque. The value 256 (SC_ALPHA_NOALPHA) + is opaque and uses code that is not alpha-aware and may be faster. Not all platforms support + translucency and only some Scintilla features implement translucency. + The default alpha value for most features is SC_ALPHA_NOALPHA.
    <unused>This is an unused argument. Setting it to 0 will ensure compatibility with future + enhancements.
    + +

    Contents

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    o Text retrieval and + modificationo Searching and replacingo Overtype
    o Cut, copy and pasteo Error handlingo Undo and Redo
    o Selection and informationo Scrolling and automatic + scrollingo White space
    o Cursoro Mouse captureo Line endings
    o Stylingo Style definitiono Caret, selection, and hotspot styles
    o Marginso Other settingso Brace highlighting
    o Tabs and Indentation + Guideso Markerso Indicators
    o Autocompletiono User listso Call tips
    o Keyboard commandso Key bindingso Popup edit menu
    o Macro recordingo Printingo Direct access
    o Multiple viewso Foldingo Line wrapping
    o Zoomingo Long lineso Lexer
    o Notificationso GTK+o Deprecated messages
    o Edit messages never + supported by Scintillao Building Scintilla
    + +

    Messages with names of the form SCI_SETxxxxx often have a companion + SCI_GETxxxxx. To save tedious repetition, if the SCI_GETxxxxx message + returns the value set by the SCI_SETxxxxx message, the SET routine is + described and the GET routine is left to your imagination.

    + +

    Text retrieval and modification

    + +

    Each character in a Scintilla document is followed by an associated byte of styling + information. The combination of a character byte and a style byte is called a cell. Style bytes + are interpreted as a style index in the low 5 bits and as 3 individual bits of indicators. This allows 32 fundamental styles, which is enough for most + languages, and three independent indicators so that, for example, syntax errors, deprecated + names and bad indentation could all be displayed at once. The number of bits used for styles + can be altered with SCI_SETSTYLEBITS up to a maximum of 7 bits. + The remaining bits can be used for indicators.

    + +

    Positions within the Scintilla document refer to a character or the gap before that + character. The first character in a document is 0, the second 1 and so on. If a document + contains nLen characters, the last character is numbered nLen-1. + The caret exists between character positions and can be located from before the first character (0) + to after the last character (nLen).

    + +

    There are places where the caret can not go where two character bytes make up one character. + This occurs when a DBCS character from a language like Japanese is included in the document or + when line ends are marked with the CP/M standard of a carriage return followed by a line feed. + The INVALID_POSITION constant (-1) represents an invalid position within the + document.

    + +

    All lines of text in Scintilla are the same height, and this height is calculated from the + largest font in any current style. This restriction is for performance; if lines differed in + height then calculations involving positioning of text would require the text to be styled + first.

    + SCI_GETTEXT(int length, char *text)
    + SCI_SETTEXT(<unused>, const char *text)
    + SCI_SETSAVEPOINT
    + SCI_GETLINE(int line, char *text)
    + SCI_REPLACESEL(<unused>, const char + *text)
    + SCI_SETREADONLY(bool readOnly)
    + SCI_GETREADONLY
    + SCI_GETTEXTRANGE(<unused>, TextRange + *tr)
    + SCI_ALLOCATE(int bytes, <unused>)
    + SCI_ADDTEXT(int length, const char *s)
    + SCI_ADDSTYLEDTEXT(int length, cell *s)
    + SCI_APPENDTEXT(int length, const char *s)
    + SCI_INSERTTEXT(int pos, const char *text)
    + SCI_CLEARALL
    + SCI_CLEARDOCUMENTSTYLE
    + SCI_GETCHARAT(int position)
    + SCI_GETSTYLEAT(int position)
    + SCI_GETSTYLEDTEXT(<unused>, TextRange + *tr)
    + SCI_SETSTYLEBITS(int bits)
    + SCI_GETSTYLEBITS
    + SCI_TARGETASUTF8(<unused>, char *s)
    + SCI_ENCODEDFROMUTF8(const char *utf8, char *encoded)
    + SCI_SETLENGTHFORENCODE(int bytes)
    +
    + +

    SCI_GETTEXT(int length, char *text)
    + This returns length-1 characters of text from the start of the document plus one + terminating 0 character. To collect all the text in a document, use SCI_GETLENGTH + to get the number of characters in the document (nLen), allocate a character + buffer of length nLen+1 bytes, then call SCI_GETTEXT(nLen+1, char + *text). If the text argument is 0 then the length that should be allocated to store the + entire document is returned. + If you then save the text, you should use SCI_SETSAVEPOINT to mark + the text as unmodified.

    + +

    See also: SCI_GETSELTEXT, SCI_GETCURLINE, SCI_GETLINE, SCI_GETSTYLEDTEXT, SCI_GETTEXTRANGE

    + +

    SCI_SETTEXT(<unused>, const char *text)
    + This replaces all the text in the document with the zero terminated text string you pass + in.

    + +

    SCI_SETSAVEPOINT
    + This message tells Scintilla that the current state of the document is unmodified. This is + usually done when the file is saved or loaded, hence the name "save point". As Scintilla + performs undo and redo operations, it notifies the container that it has entered or left the + save point with SCN_SAVEPOINTREACHED and SCN_SAVEPOINTLEFT notification messages, allowing the container to know if the file + should be considered dirty or not.

    + +

    See also: SCI_EMPTYUNDOBUFFER, SCI_GETMODIFY

    + +

    SCI_GETLINE(int line, char *text)
    + This fills the buffer defined by text with the contents of the nominated line (lines start at + 0). The buffer is not terminated by a 0 character. It is up to you to make sure that the buffer + is long enough for the text, use SCI_LINELENGTH(int line). The returned value is the + number of characters copied to the buffer. The returned text includes any end of line + characters. If you ask for a line number outside the range of lines in the document, 0 + characters are copied. If the text argument is 0 then the length that should be allocated + to store the entire line is returned.

    + +

    See also: SCI_GETCURLINE, SCI_GETSELTEXT, SCI_GETTEXTRANGE, SCI_GETSTYLEDTEXT, SCI_GETTEXT

    + +

    SCI_REPLACESEL(<unused>, const char *text)
    + The currently selected text between the anchor + and the current position is replaced by the 0 terminated text string. If the anchor and + current position are the same, the text is inserted at the caret position. The caret is + positioned after the inserted text and the caret is scrolled into view.

    + +

    SCI_SETREADONLY(bool readOnly)
    + SCI_GETREADONLY
    + These messages set and get the read-only flag for the document. If you mark a document as read + only, attempts to modify the text cause the SCN_MODIFYATTEMPTRO notification.

    + +

    SCI_GETTEXTRANGE(<unused>, TextRange *tr)
    + This collects the text between the positions cpMin and cpMax and + copies it to lpstrText (see struct TextRange in + Scintilla.h). If cpMax is -1, text is returned to the end of the + document. The text is 0 terminated, so you must supply a buffer that is at least 1 character + longer than the number of characters you wish to read. The return value is the length of the + returned text not including the terminating 0.

    + +

    See also: SCI_GETSELTEXT, SCI_GETLINE, SCI_GETCURLINE, SCI_GETSTYLEDTEXT, SCI_GETTEXT

    + +

    SCI_GETSTYLEDTEXT(<unused>, TextRange *tr)
    + This collects styled text into a buffer using two bytes for each cell, with the character at + the lower address of each pair and the style byte at the upper address. Characters between the + positions cpMin and cpMax are copied to lpstrText (see + struct TextRange in Scintilla.h). Two 0 bytes are added to the end of + the text, so the buffer that lpstrText points at must be at least + 2*(cpMax-cpMin)+2 bytes long. No check is made for sensible values of + cpMin or cpMax. Positions outside the document return character codes + and style bytes of 0.

    + +

    See also: SCI_GETSELTEXT, SCI_GETLINE, SCI_GETCURLINE, SCI_GETTEXTRANGE, SCI_GETTEXT

    + +

    SCI_ALLOCATE(int bytes, <unused>)
    + Allocate a document buffer large enough to store a given number of bytes. + The document will not be made smaller than its current contents.

    + +

    SCI_ADDTEXT(int length, const char *s)
    + This inserts the first length characters from the string s + at the current position. This will include any 0's in the string that you might have expected + to stop the insert operation. The current position is set at the end of the inserted text, + but it is not scrolled into view.

    + +

    SCI_ADDSTYLEDTEXT(int length, cell *s)
    + This behaves just like SCI_ADDTEXT, but inserts styled text.

    + +

    SCI_APPENDTEXT(int length, const char *s)
    + This adds the first length characters from the string s to the end + of the document. This will include any 0's in the string that you might have expected to stop + the operation. The current selection is not changed and the new text is not scrolled into + view.

    + +

    SCI_INSERTTEXT(int pos, const char *text)
    + This inserts the zero terminated text string at position pos or at + the current position if pos is -1. If the current position is after the insertion point + then it is moved along with its surrounding text but no scrolling is performed.

    + +

    SCI_CLEARALL
    + Unless the document is read-only, this deletes all the text.

    + +

    SCI_CLEARDOCUMENTSTYLE
    + When wanting to completely restyle the document, for example after choosing a lexer, the + SCI_CLEARDOCUMENTSTYLE can be used to clear all styling information and reset the + folding state.

    + +

    SCI_GETCHARAT(int pos)
    + This returns the character at pos in the document or 0 if pos is + negative or past the end of the document.

    + +

    SCI_GETSTYLEAT(int pos)
    + This returns the style at pos in the document, or 0 if pos is + negative or past the end of the document.

    + +

    SCI_SETSTYLEBITS(int bits)
    + SCI_GETSTYLEBITS
    + This pair of routines sets and reads back the number of bits in each cell to use for styling, + to a maximum of 7 style bits. The remaining bits can be used as indicators. The standard + setting is SCI_SETSTYLEBITS(5). + The number of styling bits needed by the current lexer can be found with + SCI_GETSTYLEBITSNEEDED.

    + +

    TextRange and CharacterRange
    + These structures are defined to be exactly the same shape as the Win32 TEXTRANGE + and CHARRANGE, so that older code that treats Scintilla as a RichEdit will + work.

    +
    +struct CharacterRange {
    +    long cpMin;
    +    long cpMax;
    +};
    +
    +struct TextRange {
    +    struct CharacterRange chrg;
    +    char *lpstrText;
    +};
    +
    + +

    GTK+-specific: Access to encoded text

    + +

    SCI_TARGETASUTF8(<unused>, char *s)
    + This method retrieves the value of the target encoded as UTF-8 which is the default + encoding of GTK+ so is useful for retrieving text for use in other parts of the user interface, + such as find and replace dialogs. The length of the encoded text in bytes is returned. +

    + +

    SCI_ENCODEDFROMUTF8(const char *utf8, char *encoded)
    + SCI_SETLENGTHFORENCODE(int bytes)
    + SCI_ENCODEDFROMUTF8 converts a UTF-8 string into the document's + encoding which is useful for taking the results of a find dialog, for example, and receiving + a string of bytes that can be searched for in the document. Since the text can contain nul bytes, + the SCI_SETLENGTHFORENCODE method can be used to set the + length that will be converted. If set to -1, the length is determined by finding a nul byte. + The length of the converted string is returned. +

    + + +

    Searching

    + SCI_FINDTEXT(int flags, TextToFind + *ttf)
    + SCI_SEARCHANCHOR
    + SCI_SEARCHNEXT(int searchFlags, const char + *text)
    + SCI_SEARCHPREV(int searchFlags, const char + *text)
    + Search and replace using the + target
    +
    + +

    searchFlags
    + Several of the search routines use flag options, which include a simple regular expression + search. Combine the flag options by adding them:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SCFIND_MATCHCASEA match only occurs with text that matches the case of the search string.
    SCFIND_WHOLEWORDA match only occurs if the characters before and after are not word characters.
    SCFIND_WORDSTARTA match only occurs if the character before is not a word character.
    SCFIND_REGEXPThe search string should be interpreted as a regular expression.
    SCFIND_POSIXTreat regular expression in a more POSIX compatible manner + by interpreting bare ( and ) for tagged sections rather than \( and \).
    + +

    If SCFIND_REGEXP is not included in the searchFlags, you can + search backwards to find the previous occurrence of a search string by setting the end of the + search range before the start. If SCFIND_REGEXP is included, searches are always + from a lower position to a higher position, even if the search range is backwards.

    + +

    In a regular expression, special characters interpreted are:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    .Matches any character
    \(This marks the start of a region for tagging a match.
    \)This marks the end of a tagged region.
    \nWhere n is 1 through 9 refers to the first through ninth tagged region + when replacing. For example, if the search string was Fred\([1-9]\)XXX and + the replace string was Sam\1YYY, when applied to Fred2XXX this + would generate Sam2YYY.
    \<This matches the start of a word using Scintilla's definitions of words.
    \>This matches the end of a word using Scintilla's definition of words.
    \xThis allows you to use a character x that would otherwise have a special meaning. For + example, \[ would be interpreted as [ and not as the start of a character set.
    [...]This indicates a set of characters, for example, [abc] means any of the characters a, + b or c. You can also use ranges, for example [a-z] for any lower case character.
    [^...]The complement of the characters in the set. For example, [^A-Za-z] means any + character except an alphabetic character.
    ^This matches the start of a line (unless used inside a set, see above).
    $This matches the end of a line.
    *This matches 0 or more times. For example, Sa*m matches Sm, + Sam, Saam, Saaam and so on.
    +This matches 1 or more times. For example, Sa+m matches + Sam, Saam, Saaam and so on.
    + +

    SCI_FINDTEXT(int searchFlags, TextToFind *ttf)
    + This message searches for text in the document. It does not use or move the current selection. + The searchFlags argument controls the + search type, which includes regular expression searches.

    + +

    The TextToFind structure is defined in Scintilla.h; set + chrg.cpMin and chrg.cpMax with the range of positions in the document + to search. If SCFIND_REGEXP is not included in the flags, you can search backwards by + setting chrg.cpMax less than chrg.cpMin. If SCFIND_REGEXP + is included, the search is always forwards (even if chrg.cpMax is less than chrg.cpMin). + Set the lpstrText member of TextToFind to point at a zero terminated + text string holding the search pattern. If your language makes the use of TextToFind + difficult, you should consider using SCI_SEARCHINTARGET instead.

    + +

    The return value is -1 if the search fails or the position of the start of the found text if + it succeeds. The chrgText.cpMin and chrgText.cpMax members of + TextToFind are filled in with the start and end positions of the found text.

    + +

    See also: SCI_SEARCHINTARGET

    + +

    TextToFind
    + This structure is defined to have exactly the same shape as the Win32 structure + FINDTEXTEX for old code that treated Scintilla as a RichEdit control.

    +
    +struct TextToFind {
    +    struct CharacterRange chrg;     // range to search
    +    char *lpstrText;                // the search pattern (zero terminated)
    +    struct CharacterRange chrgText; // returned as position of matching text
    +};
    +
    + +

    SCI_SEARCHANCHOR
    + SCI_SEARCHNEXT(int searchFlags, const char *text)
    + SCI_SEARCHPREV(int searchFlags, const char *text)
    + These messages provide relocatable search support. This allows multiple incremental + interactive searches to be macro recorded while still setting the selection to found text so + the find/select operation is self-contained. These three messages send SCN_MACRORECORD notifications if macro recording is enabled.

    + +

    SCI_SEARCHANCHOR sets the search start point used by + SCI_SEARCHNEXT and SCI_SEARCHPREV to the start of the current + selection, that is, the end of the selection that is nearer to the start of the document. You + should always call this before calling either of SCI_SEARCHNEXT or + SCI_SEARCHPREV.

    + +

    SCI_SEARCHNEXT and SCI_SEARCHPREV search for the next and previous + occurrence of the zero terminated search string pointed at by text. The search is modified by + the searchFlags. If you request a regular + expression, SCI_SEARCHPREV finds the first occurrence of the search string in the + document, not the previous one before the anchor point.

    + +

    The return value is -1 if nothing is found, otherwise the return value is the start position + of the matching text. The selection is updated to show the matched text, but is not scrolled + into view.

    + +

    See also: SCI_SEARCHINTARGET, + SCI_FINDTEXT

    + +

    Search and replace using the target

    + +

    Using SCI_REPLACESEL, + modifications cause scrolling and other visible changes, which may take some time and cause + unwanted display updates. If performing many changes, such as a replace all command, the target + can be used instead. First, set the target, ie. the range to be replaced. Then call + SCI_REPLACETARGET or SCI_REPLACETARGETRE.

    + +

    Searching can be performed within the target range with SCI_SEARCHINTARGET, + which uses a counted string to allow searching for null characters. It returns the length of + range or -1 for failure, in which case the target is not moved. The flags used by + SCI_SEARCHINTARGET such as SCFIND_MATCHCASE, + SCFIND_WHOLEWORD, SCFIND_WORDSTART, and SCFIND_REGEXP + can be set with SCI_SETSEARCHFLAGS. SCI_SEARCHINTARGET may be simpler + for some clients to use than SCI_FINDTEXT, as that requires using a pointer to a + structure.

    + SCI_SETTARGETSTART(int pos)
    + SCI_GETTARGETSTART
    + SCI_SETTARGETEND(int pos)
    + SCI_GETTARGETEND
    + SCI_TARGETFROMSELECTION
    + SCI_SETSEARCHFLAGS(int searchFlags)
    + SCI_GETSEARCHFLAGS
    + SCI_SEARCHINTARGET(int length, const char + *text)
    + SCI_REPLACETARGET(int length, const char + *text)
    + SCI_REPLACETARGETRE(int length, const char + *text)
    +
    + +

    SCI_SETTARGETSTART(int pos)
    + SCI_GETTARGETSTART
    + SCI_SETTARGETEND(int pos)
    + SCI_GETTARGETEND
    + These functions set and return the start and end of the target. When searching in non-regular + expression mode, you can set start greater than end to find the last matching text in the + target rather than the first matching text. The target is also set by a successful + SCI_SEARCHINTARGET.

    + +

    SCI_TARGETFROMSELECTION
    + Set the target start and end to the start and end positions of the selection.

    + +

    SCI_SETSEARCHFLAGS(int searchFlags)
    + SCI_GETSEARCHFLAGS
    + These get and set the searchFlags used by + SCI_SEARCHINTARGET. There are several option flags including a simple regular + expression search.

    + +

    SCI_SEARCHINTARGET(int length, const char *text)
    + This searches for the first occurrence of a text string in the target defined by + SCI_SETTARGETSTART and SCI_SETTARGETEND. The text string is not zero + terminated; the size is set by length. The search is modified by the search flags + set by SCI_SETSEARCHFLAGS. If the search succeeds, the target is set to the found + text and the return value is the position of the start of the matching text. If the search + fails, the result is -1.

    + +

    SCI_REPLACETARGET(int length, const char *text)
    + If length is -1, text is a zero terminated string, otherwise + length sets the number of character to replace the target with. + After replacement, the target range refers to the replacement text. + The return value + is the length of the replacement string.
    + Note that the recommended way to delete text in the document is to set the target to the text to be removed, + and to perform a replace target with an empty string.

    + +

    SCI_REPLACETARGETRE(int length, const char *text)
    + This replaces the target using regular expressions. If length is -1, + text is a zero terminated string, otherwise length is the number of + characters to use. The replacement string is formed from the text string with any sequences of + \1 through \9 replaced by tagged matches from the most recent regular + expression search. + After replacement, the target range refers to the replacement text. + The return value is the length of the replacement string.

    + +

    See also: SCI_FINDTEXT

    + +

    Overtype

    + +

    SCI_SETOVERTYPE(bool overType)
    + SCI_GETOVERTYPE
    + When overtype is enabled, each typed character replaces the character to the right of the text + caret. When overtype is disabled, characters are inserted at the caret. + SCI_GETOVERTYPE returns TRUE (1) if overtyping is active, otherwise + FALSE (0) will be returned. Use SCI_SETOVERTYPE to set the overtype + mode.

    + +

    Cut, copy and paste

    + + SCI_CUT
    + SCI_COPY
    + SCI_PASTE
    + SCI_CLEAR
    + SCI_CANPASTE
    + SCI_COPYRANGE(int start, int end)
    + SCI_COPYTEXT(int length, + const char *text)
    + SCI_SETPASTECONVERTENDINGS(bool convert)
    + SCI_GETPASTECONVERTENDINGS
    +
    + +

    SCI_CUT
    + SCI_COPY
    + SCI_PASTE
    + SCI_CLEAR
    + SCI_CANPASTE
    + These commands perform the standard tasks of cutting and copying data to the clipboard, + pasting from the clipboard into the document, and clearing the document. + SCI_CANPASTE returns non-zero if the document isn't read-only and if the selection + doesn't contain protected text. If you need a "can copy" or "can cut", use + SCI_GETSELECTIONSTART()-SCI_GETSELECTIONEND(), which will be non-zero if you can + copy or cut to the clipboard.

    + +

    GTK+ does not really support SCI_CANPASTE and always returns TRUE + unless the document is read-only.

    + +

    On X, the clipboard is asynchronous and may require several messages between + the destination and source applications. Data from SCI_PASTE will not arrive in the + document immediately.

    + + SCI_COPYRANGE(int start, int end)
    + SCI_COPYTEXT(int length, const char *text)
    +

    SCI_COPYRANGE copies a range of text from the document to + the system clipboard and SCI_COPYTEXT copies a supplied piece of + text to the system clipboard.

    + +

    SCI_SETPASTECONVERTENDINGS(bool convert)
    + SCI_GETPASTECONVERTENDINGS
    + If this property is set then when text is pasted any line ends are converted to match the document's + end of line mode as set with + SCI_SETEOLMODE. + Currently only changeable on Windows. On GTK+ pasted text is always converted.

    + +

    Error handling

    + +

    SCI_SETSTATUS(int status)
    + SCI_GETSTATUS
    + If an error occurs, Scintilla may set an internal error number that can be retrieved with + SCI_GETSTATUS. Not currently used but will be in the future. To clear the error + status call SCI_SETSTATUS(0).

    + +

    Undo and Redo

    + +

    Scintilla has multiple level undo and redo. It will continue to collect undoable actions + until memory runs out. Scintilla saves actions that change the document. Scintilla does not + save caret and selection movements, view scrolling and the like. Sequences of typing or + deleting are compressed into single actions to make it easier to undo and redo at a sensible + level of detail. Sequences of actions can be combined into actions that are undone as a unit. + These sequences occur between SCI_BEGINUNDOACTION and + SCI_ENDUNDOACTION messages. These sequences can be nested and only the top-level + sequences are undone as units.

    + SCI_UNDO
    + SCI_CANUNDO
    + SCI_EMPTYUNDOBUFFER
    + SCI_REDO
    + SCI_CANREDO
    + SCI_SETUNDOCOLLECTION(bool + collectUndo)
    + SCI_GETUNDOCOLLECTION
    + SCI_BEGINUNDOACTION
    + SCI_ENDUNDOACTION
    +
    + +

    SCI_UNDO
    + SCI_CANUNDO
    + SCI_UNDO undoes one action, or if the undo buffer has reached a + SCI_ENDUNDOACTION point, all the actions back to the corresponding + SCI_BEGINUNDOACTION.

    + +

    SCI_CANUNDO returns 0 if there is nothing to undo, and 1 if there is. You would + typically use the result of this message to enable/disable the Edit menu Undo command.

    + +

    SCI_REDO
    + SCI_CANREDO
    + SCI_REDO undoes the effect of the last SCI_UNDO operation.

    + +

    SCI_CANREDO returns 0 if there is no action to redo and 1 if there are undo + actions to redo. You could typically use the result of this message to enable/disable the Edit + menu Redo command.

    + +

    SCI_EMPTYUNDOBUFFER
    + This command tells Scintilla to forget any saved undo or redo history. It also sets the save + point to the start of the undo buffer, so the document will appear to be unmodified. This does + not cause the SCN_SAVEPOINTREACHED notification to be sent to the + container.

    + +

    See also: SCI_SETSAVEPOINT

    + +

    SCI_SETUNDOCOLLECTION(bool collectUndo)
    + SCI_GETUNDOCOLLECTION
    + You can control whether Scintilla collects undo information with + SCI_SETUNDOCOLLECTION. Pass in true (1) to collect information and + false (0) to stop collecting. If you stop collection, you should also use + SCI_EMPTYUNDOBUFFER to avoid the undo buffer being unsynchronized with the data in + the buffer.

    + +

    You might wish to turn off saving undo information if you use the Scintilla to store text + generated by a program (a Log view) or in a display window where text is often deleted and + regenerated.

    + +

    SCI_BEGINUNDOACTION
    + SCI_ENDUNDOACTION
    + Send these two messages to Scintilla to mark the beginning and end of a set of operations that + you want to undo all as one operation but that you have to generate as several operations. + Alternatively, you can use these to mark a set of operations that you do not want to have + combined with the preceding or following operations if they are undone.

    + +

    Selection and information

    + +

    Scintilla maintains a selection that stretches between two points, the anchor and the + current position. If the anchor and the current position are the same, there is no selected + text. Positions in the document range from 0 (before the first character), to the document size + (after the last character). If you use messages, there is nothing to stop you setting a + position that is in the middle of a CRLF pair, or in the middle of a 2 byte character. However, + keyboard commands will not move the caret into such positions.

    + SCI_GETTEXTLENGTH
    + SCI_GETLENGTH
    + SCI_GETLINECOUNT
    + SCI_GETFIRSTVISIBLELINE
    + SCI_LINESONSCREEN
    + SCI_GETMODIFY
    + SCI_SETSEL(int anchorPos, int currentPos)
    + SCI_GOTOPOS(int position)
    + SCI_GOTOLINE(int line)
    + SCI_SETCURRENTPOS(int position)
    + SCI_GETCURRENTPOS
    + SCI_SETANCHOR(int position)
    + SCI_GETANCHOR
    + SCI_SETSELECTIONSTART(int position)
    + SCI_GETSELECTIONSTART
    + SCI_SETSELECTIONEND(int position)
    + SCI_GETSELECTIONEND
    + SCI_SELECTALL
    + SCI_LINEFROMPOSITION(int position)
    + SCI_POSITIONFROMLINE(int line)
    + SCI_GETLINEENDPOSITION(int line)
    + SCI_LINELENGTH(int line)
    + SCI_GETCOLUMN(int position)
    + SCI_FINDCOLUMN(int line, int column)
    + SCI_POSITIONFROMPOINT(int x, int y)
    + SCI_POSITIONFROMPOINTCLOSE(int x, int + y)
    + SCI_POINTXFROMPOSITION(<unused>, int + position)
    + SCI_POINTYFROMPOSITION(<unused>, int + position)
    + SCI_HIDESELECTION(bool hide)
    + SCI_GETSELTEXT(<unused>, char *text)
    + SCI_GETCURLINE(int textLen, char *text)
    + SCI_SELECTIONISRECTANGLE
    + SCI_SETSELECTIONMODE(int mode)
    + SCI_GETSELECTIONMODE
    + SCI_GETLINESELSTARTPOSITION(int line)
    + SCI_GETLINESELENDPOSITION(int line)
    + SCI_MOVECARETINSIDEVIEW
    + SCI_WORDENDPOSITION(int position, bool + onlyWordCharacters)
    + SCI_WORDSTARTPOSITION(int position, bool + onlyWordCharacters)
    + SCI_POSITIONBEFORE(int position)
    + SCI_POSITIONAFTER(int position)
    + SCI_TEXTWIDTH(int styleNumber, const char *text)
    + SCI_TEXTHEIGHT(int line)
    + SCI_CHOOSECARETX
    +
    + +

    SCI_GETTEXTLENGTH
    + SCI_GETLENGTH
    + Both these messages return the length of the document in characters.

    + +

    SCI_GETLINECOUNT
    + This returns the number of lines in the document. An empty document contains 1 line. A + document holding only an end of line sequence has 2 lines.

    + +

    SCI_GETFIRSTVISIBLELINE
    + This returns the line number of the first visible line in the Scintilla view. The first line + in the document is numbered 0. The value is a visible line rather than a document line.

    + +

    SCI_LINESONSCREEN
    + This returns the number of complete lines visible on the screen. With a constant line height, + this is the vertical space available divided by the line separation. Unless you arrange to size + your window to an integral number of lines, there may be a partial line visible at the bottom + of the view.

    + +

    SCI_GETMODIFY
    + This returns non-zero if the document is modified and 0 if it is unmodified. The modified + status of a document is determined by the undo position relative to the save point. The save + point is set by SCI_SETSAVEPOINT, + usually when you have saved data to a file.

    + +

    If you need to be notified when the document becomes modified, Scintilla notifies the + container that it has entered or left the save point with the SCN_SAVEPOINTREACHED and SCN_SAVEPOINTLEFT notification messages.

    + +

    SCI_SETSEL(int anchorPos, int currentPos)
    + This message sets both the anchor and the current position. If currentPos is + negative, it means the end of the document. If anchorPos is negative, it means + remove any selection (i.e. set the anchor to the same position as currentPos). The + caret is scrolled into view after this operation.

    + +

    SCI_GOTOPOS(int pos)
    + This removes any selection, sets the caret at pos and scrolls the view to make + the caret visible, if necessary. It is equivalent to + SCI_SETSEL(pos, pos). The anchor position is set the same as the current + position.

    + +

    SCI_GOTOLINE(int line)
    + This removes any selection and sets the caret at the start of line number line + and scrolls the view (if needed) to make it visible. The anchor position is set the same as the + current position. If line is outside the lines in the document (first line is 0), + the line set is the first or last.

    + +

    SCI_SETCURRENTPOS(int pos)
    + This sets the current position and creates a selection between the anchor and the current + position. The caret is not scrolled into view.

    + +

    See also: SCI_SCROLLCARET

    + +

    SCI_GETCURRENTPOS
    + This returns the current position.

    + +

    SCI_SETANCHOR(int pos)
    + This sets the anchor position and creates a selection between the anchor position and the + current position. The caret is not scrolled into view.

    + +

    See also: SCI_SCROLLCARET

    + +

    SCI_GETANCHOR
    + This returns the current anchor position.

    + +

    SCI_SETSELECTIONSTART(int pos)
    + SCI_SETSELECTIONEND(int pos)
    + These set the selection based on the assumption that the anchor position is less than the + current position. They do not make the caret visible. The table shows the positions of the + anchor and the current position after using these messages.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + anchorcurrent
    SCI_SETSELECTIONSTARTposMax(pos, current)
    SCI_SETSELECTIONENDMin(anchor, pos)pos
    + +

    See also: SCI_SCROLLCARET

    + +

    SCI_GETSELECTIONSTART
    + SCI_GETSELECTIONEND
    + These return the start and end of the selection without regard to which end is the current + position and which is the anchor. SCI_GETSELECTIONSTART returns the smaller of the + current position or the anchor position. SCI_GETSELECTIONEND returns the larger of + the two values.

    + +

    SCI_SELECTALL
    + This selects all the text in the document. The current position is not scrolled into view.

    + +

    SCI_LINEFROMPOSITION(int pos)
    + This message returns the line that contains the position pos in the document. The + return value is 0 if pos <= 0. The return value is the last line if + pos is beyond the end of the document.

    + +

    SCI_POSITIONFROMLINE(int line)
    + This returns the document position that corresponds with the start of the line. If + line is negative, the position of the line holding the start of the selection is + returned. If line is greater than the lines in the document, the return value is + -1. If line is equal to the number of lines in the document (i.e. 1 line past the + last line), the return value is the end of the document.

    + +

    SCI_GETLINEENDPOSITION(int line)
    + This returns the position at the end of the line, before any line end characters. If line + is the last line in the document (which does not have any end of line characters), the result is the size of the + document. If line is negative or line >= SCI_GETLINECOUNT(), the result is undefined.

    + +

    SCI_LINELENGTH(int line)
    + This returns the length of the line, including any line end characters. If line + is negative or beyond the last line in the document, the result is 0. If you want the length of + the line not including any end of line characters, use SCI_GETLINEENDPOSITION(line) - SCI_POSITIONFROMLINE(line).

    + SCI_GETSELTEXT(<unused>, char *text)
    + This copies the currently selected text and a terminating 0 byte to the text + buffer. The buffer must be at least + SCI_GETSELECTIONEND()-SCI_GETSELECTIONSTART()+1 bytes long.
    + If the text argument is 0 then the length that should be allocated + to store the entire selection is returned.
    + +

    See also: SCI_GETCURLINE, SCI_GETLINE, SCI_GETTEXT, SCI_GETSTYLEDTEXT, SCI_GETTEXTRANGE

    + +

    SCI_GETCURLINE(int textLen, char *text)
    + This retrieves the text of the line containing the caret and returns the position within the + line of the caret. Pass in char* text pointing at a buffer large enough to hold + the text you wish to retrieve and a terminating 0 character. + Set textLen to the + length of the buffer which must be at least 1 to hold the terminating 0 character. + If the text argument is 0 then the length that should be allocated + to store the entire current line is returned.

    + +

    See also: SCI_GETSELTEXT, SCI_GETLINE, SCI_GETTEXT, SCI_GETSTYLEDTEXT, SCI_GETTEXTRANGE

    + +

    SCI_SELECTIONISRECTANGLE
    + This returns 1 if the current selection is in rectangle mode, 0 if not.

    + +

    SCI_SETSELECTIONMODE(int mode)
    + SCI_GETSELECTIONMODE
    + The two functions set and get the selection mode, which can be + stream (SC_SEL_STREAM=0) or + rectangular (SC_SEL_RECTANGLE=1) + or by lines (SC_SEL_LINES=2). + When set in these modes, regular caret moves will extend or reduce the selection, + until the mode is cancelled by a call with same value or with SCI_CANCEL. + The get function returns the current mode even if the selection was made by mouse + or with regular extended moves.

    + +

    SCI_GETLINESELSTARTPOSITION(int line)
    + SCI_GETLINESELENDPOSITION(int line)
    + Retrieve the position of the start and end of the selection at the given line with + INVALID_POSITION returned if no selection on this line.

    + +

    SCI_MOVECARETINSIDEVIEW
    + If the caret is off the top or bottom of the view, it is moved to the nearest line that is + visible to its current position. Any selection is lost.

    + +

    SCI_WORDENDPOSITION(int position, bool + onlyWordCharacters)
    + SCI_WORDSTARTPOSITION(int position, bool + onlyWordCharacters)
    + These messages return the start and end of words using the same definition of words as used + internally within Scintilla. You can set your own list of characters that count as words with + SCI_SETWORDCHARS. The position + sets the start or the search, which is forwards when searching for the end and backwards when + searching for the start.

    + +

    Set onlyWordCharacters to true (1) to stop searching at the first + non-word character in the search direction. If onlyWordCharacters is + false (0), the first character in the search direction sets the type of the search + as word or non-word and the search stops at the first non-matching character. Searches are also + terminated by the start or end of the document.

    + +

    If "w" represents word characters and "." represents non-word characters and "|" represents + the position and true or false is the state of + onlyWordCharacters:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Initial stateend, trueend, falsestart, truestart, false
    ..ww..|..ww....ww..|..ww....ww....|ww....ww..|..ww....ww|....ww..
    ....ww|ww........wwww|........wwww|........|wwww........|wwww....
    ..ww|....ww....ww|....ww....ww....|ww....|ww....ww....|ww....ww..
    ..ww....|ww....ww....ww|....ww....ww|....ww....|ww....ww|....ww..
    + +

    SCI_POSITIONBEFORE(int position)
    + SCI_POSITIONAFTER(int position)
    + These messages return the position before and after another position + in the document taking into account the current code page. The minimum + position returned is 0 and the maximum is the last position in the document. + If called with a position within a multi byte character will return the position + of the start/end of that character.

    + +

    SCI_TEXTWIDTH(int styleNumber, const char *text)
    + This returns the pixel width of a string drawn in the given styleNumber which can + be used, for example, to decide how wide to make the line number margin in order to display a + given number of numerals.

    + +

    SCI_TEXTHEIGHT(int line)
    + This returns the height in pixels of a particular line. Currently all lines are the same + height.

    + +

    SCI_GETCOLUMN(int pos)
    + This message returns the column number of a position pos within the document + taking the width of tabs into account. This returns the column number of the last tab on the + line before pos, plus the number of characters between the last tab and + pos. If there are no tab characters on the line, the return value is the number of + characters up to the position on the line. In both cases, double byte characters count as a + single character. This is probably only useful with monospaced fonts.

    + +

    SCI_FINDCOLUMN(int line, int column)
    + This message returns the position of a column on a line + taking the width of tabs into account. It treats a multi-byte character as a single column. + Column numbers, like lines start at 0.

    + +

    SCI_POSITIONFROMPOINT(int x, int y)
    + SCI_POSITIONFROMPOINTCLOSE(int x, int y)
    + SCI_POSITIONFROMPOINT finds the closest character position to a point and + SCI_POSITIONFROMPOINTCLOSE is similar but returns -1 if the point is outside the + window or not close to any characters.

    + +

    SCI_POINTXFROMPOSITION(<unused>, int pos)
    + SCI_POINTYFROMPOSITION(<unused>, int pos)
    + These messages return the x and y display pixel location of text at position pos + in the document.

    + +

    SCI_HIDESELECTION(bool hide)
    + The normal state is to make the selection visible by drawing it as set by SCI_SETSELFORE and SCI_SETSELBACK. However, if you hide the selection, it + is drawn as normal text.

    + +

    SCI_CHOOSECARETX
    + Scintilla remembers the x value of the last position horizontally moved to explicitly by the + user and this value is then used when moving vertically such as by using the up and down keys. + This message sets the current x position of the caret as the remembered value.

    + +

    Scrolling and automatic scrolling

    + SCI_LINESCROLL(int column, int line)
    + SCI_SCROLLCARET
    + SCI_SETXCARETPOLICY(int caretPolicy, int + caretSlop)
    + SCI_SETYCARETPOLICY(int caretPolicy, int + caretSlop)
    + SCI_SETVISIBLEPOLICY(int caretPolicy, int + caretSlop)
    + SCI_SETHSCROLLBAR(bool visible)
    + SCI_GETHSCROLLBAR
    + SCI_SETVSCROLLBAR(bool visible)
    + SCI_GETVSCROLLBAR
    + SCI_GETXOFFSET
    + SCI_SETXOFFSET(int xOffset)
    + SCI_SETSCROLLWIDTH(int pixelWidth)
    + SCI_GETSCROLLWIDTH
    + SCI_SETENDATLASTLINE(bool + endAtLastLine)
    + SCI_GETENDATLASTLINE
    +
    + +

    SCI_LINESCROLL(int column, int line)
    + This will attempt to scroll the display by the number of columns and lines that you specify. + Positive line values increase the line number at the top of the screen (i.e. they move the text + upwards as far as the user is concerned), Negative line values do the reverse.

    + +

    The column measure is the width of a space in the default style. Positive values increase + the column at the left edge of the view (i.e. they move the text leftwards as far as the user + is concerned). Negative values do the reverse.

    + +

    See also: SCI_SETXOFFSET

    + +

    SCI_SCROLLCARET
    + If the current position (this is the caret if there is no selection) is not visible, the view + is scrolled to make it visible according to the current caret policy.

    + +

    SCI_SETXCARETPOLICY(int caretPolicy, int caretSlop)
    + SCI_SETYCARETPOLICY(int caretPolicy, int caretSlop)
    + These set the caret policy. The value of caretPolicy is a combination of + CARET_SLOP, CARET_STRICT, CARET_JUMPS and + CARET_EVEN.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CARET_SLOPIf set, we can define a slop value: caretSlop. This value defines an + unwanted zone (UZ) where the caret is... unwanted. This zone is defined as a number of + pixels near the vertical margins, and as a number of lines near the horizontal margins. + By keeping the caret away from the edges, it is seen within its context. This makes it + likely that the identifier that the caret is on can be completely seen, and that the + current line is seen with some of the lines following it, which are often dependent on + that line.
    CARET_STRICTIf set, the policy set by CARET_SLOP is enforced... strictly. The caret + is centred on the display if caretSlop is not set, and cannot go in the UZ + if caretSlop is set.
    CARET_JUMPSIf set, the display is moved more energetically so the caret can move in the same + direction longer before the policy is applied again. '3UZ' notation is used to indicate + three time the size of the UZ as a distance to the margin.
    CARET_EVENIf not set, instead of having symmetrical UZs, the left and bottom UZs are extended + up to right and top UZs respectively. This way, we favour the displaying of useful + information: the beginning of lines, where most code reside, and the lines after the + caret, for example, the body of a function.
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    slopstrictjumpsevenCaret can go to the marginOn reaching limit (going out of visibility
    + or going into the UZ) display is...
    0000Yesmoved to put caret on top/on right
    0001Yesmoved by one position
    0010Yesmoved to put caret on top/on right
    0011Yescentred on the caret
    01-0Caret is always on top/on right of display-
    01-1No, caret is always centred-
    1000Yesmoved to put caret out of the asymmetrical UZ
    1001Yesmoved to put caret out of the UZ
    1010Yesmoved to put caret at 3UZ of the top or right margin
    1011Yesmoved to put caret at 3UZ of the margin
    11-0Caret is always at UZ of top/right margin-
    1101No, kept out of UZmoved by one position
    1110No, kept out of UZmoved to put caret at 3UZ of the margin
    + +

    SCI_SETVISIBLEPOLICY(int caretPolicy, int caretSlop)
    + This determines how the vertical positioning is determined when SCI_ENSUREVISIBLEENFORCEPOLICY is + called. It takes VISIBLE_SLOP and VISIBLE_STRICT flags for the policy + parameter. It is similar in operation to SCI_SETYCARETPOLICY(int caretPolicy, int + caretSlop).

    + +

    SCI_SETHSCROLLBAR(bool visible)
    + SCI_GETHSCROLLBAR
    + The horizontal scroll bar is only displayed if it is needed for the assumed width. + If you never wish to see it, call + SCI_SETHSCROLLBAR(0). Use SCI_SETHSCROLLBAR(1) to enable it again. + SCI_GETHSCROLLBAR returns the current state. The default state is to display it + when needed. + See also: SCI_SETSCROLLWIDTH.

    + +

    SCI_SETVSCROLLBAR(bool visible)
    + SCI_GETVSCROLLBAR
    + By default, the vertical scroll bar is always displayed when required. You can choose to hide + or show it with SCI_SETVSCROLLBAR and get the current state with + SCI_GETVSCROLLBAR.

    + +

    SCI_SETXOFFSET(int xOffset)
    + SCI_GETXOFFSET
    + The xOffset is the horizontal scroll position in pixels of the start of the text + view. A value of 0 is the normal position with the first text column visible at the left of the + view.

    + +

    See also: SCI_LINESCROLL

    + +

    SCI_SETSCROLLWIDTH(int pixelWidth)
    + SCI_GETSCROLLWIDTH
    + For performance, Scintilla does not measure the display width of the document to determine + the properties of the horizontal scroll bar. Instead, an assumed width is used. + These messages set and get the document width in pixels assumed by Scintilla. + The default value is 2000.

    + +

    SCI_SETENDATLASTLINE(bool endAtLastLine)
    + SCI_GETENDATLASTLINE
    + SCI_SETENDATLASTLINE sets the scroll range so that maximum scroll position has + the last line at the bottom of the view (default). Setting this to false allows + scrolling one page below the last line.

    + +

    White space

    + SCI_SETVIEWWS(int wsMode)
    + SCI_GETVIEWWS
    + SCI_SETWHITESPACEFORE(bool + useWhitespaceForeColour, int colour)
    + SCI_SETWHITESPACEBACK(bool + useWhitespaceBackColour, int colour)
    +
    + +

    SCI_SETVIEWWS(int wsMode)
    + SCI_GETVIEWWS
    + White space can be made visible which may useful for languages in which white space is + significant, such as Python. Space characters appear as small centred dots and tab characters + as light arrows pointing to the right. There are also ways to control the display of end of line characters. The two messages set and get the + white space display mode. The wsMode argument can be one of:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SCWS_INVISIBLE0The normal display mode with white space displayed as an empty background + colour.
    SCWS_VISIBLEALWAYS1White space characters are drawn as dots and arrows,
    SCWS_VISIBLEAFTERINDENT2White space used for indentation is displayed normally but after the first visible + character, it is shown as dots and arrows.
    + +

    The effect of using any other wsMode value is undefined.

    + +

    SCI_SETWHITESPACEFORE<(bool useWhitespaceForeColour, int colour)
    + SCI_SETWHITESPACEBACK(bool useWhitespaceBackColour, int colour)
    + By default, the colour of visible white space is determined by the lexer in use. The + foreground and/or background colour of all visible white space can be set globally, overriding + the lexer's colours with SCI_SETWHITESPACEFORE and + SCI_SETWHITESPACEBACK.

    + +

    Cursor

    + +

    SCI_SETCURSOR(int curType)
    + SCI_GETCURSOR
    + The cursor is normally chosen in a context sensitive way, so it will be different over the + margin than when over the text. When performing a slow action, you may wish to change to a wait + cursor. You set the cursor type with SCI_SETCURSOR. The curType + argument can be:

    + + + + + + + + + + + + + + + + + + + +
    SC_CURSORNORMAL-1The normal cursor is displayed.
    SC_CURSORWAIT 4The wait cursor is displayed when the mouse is over or owned by the Scintilla + window.
    + +

    Cursor values 1 through 7 have defined cursors, but only SC_CURSORWAIT is + usefully controllable. Other values of curType cause a pointer to be displayed. + The SCI_GETCURSOR message returns the last cursor type you set, or + SC_CURSORNORMAL (-1) if you have not set a cursor type.

    + +

    Mouse capture

    + +

    SCI_SETMOUSEDOWNCAPTURES(bool captures)
    + SCI_GETMOUSEDOWNCAPTURES
    + When the mouse is pressed inside Scintilla, it is captured so future mouse movement events are + sent to Scintilla. This behavior may be turned off with + SCI_SETMOUSEDOWNCAPTURES(0).

    + +

    Line endings

    + +

    Scintilla can interpret any of the three major line end conventions, Macintosh (\r), Unix + (\n) and CP/M / DOS / Windows (\r\n). When the user presses the Enter key, one of these line + end strings is inserted into the buffer. The default is \r\n in Windows and \n in Unix, but + this can be changed with the SCI_SETEOLMODE message. You can also convert the + entire document to one of these line endings with SCI_CONVERTEOLS. Finally, you + can choose to display the line endings with SCI_SETVIEWEOL.

    + SCI_SETEOLMODE(int eolMode)
    + SCI_GETEOLMODE
    + SCI_CONVERTEOLS(int eolMode)
    + SCI_SETVIEWEOL(bool visible)
    + SCI_GETVIEWEOL
    +
    + +

    SCI_SETEOLMODE(int eolMode)
    + SCI_GETEOLMODE
    + SCI_SETEOLMODE sets the characters that are added into the document when the user + presses the Enter key. You can set eolMode to one of SC_EOL_CRLF (0), + SC_EOL_CR (1), or SC_EOL_LF (2). The SCI_GETEOLMODE + message retrieves the current state.

    + +

    SCI_CONVERTEOLS(int eolMode)
    + This message changes all the end of line characters in the document to match + eolMode. Valid values are: SC_EOL_CRLF (0), SC_EOL_CR + (1), or SC_EOL_LF (2).

    + +

    SCI_SETVIEWEOL(bool visible)
    + SCI_GETVIEWEOL
    + Normally, the end of line characters are hidden, but SCI_SETVIEWEOL allows you to + display (or hide) them by setting visible true (or + false). The visible rendering of the end of line characters is similar to + (CR), (LF), or (CR)(LF). SCI_GETVIEWEOL + returns the current state.

    + +

    Styling

    + +

    The styling messages allow you to assign styles to text. The standard Scintilla settings + divide the 8 style bits available for each character into 5 bits (0 to 4 = styles 0 to 31) that set a style and three bits (5 to 7) that + define indicators. You can change the balance between + styles and indicators with SCI_SETSTYLEBITS. If your styling needs can be met by + one of the standard lexers, or if you can write your own, then a lexer is probably the easiest + way to style your document. If you choose to use the container to do the styling you can use + the SCI_SETLEXER command to select + SCLEX_CONTAINER, in which case the container is sent a SCN_STYLENEEDED notification each time text needs styling for display. As another + alternative, you might use idle time to style the document. Even if you use a lexer, you might + use the styling commands to mark errors detected by a compiler. The following commands can be + used.

    + SCI_GETENDSTYLED
    + SCI_STARTSTYLING(int position, int mask)
    + SCI_SETSTYLING(int length, int style)
    + SCI_SETSTYLINGEX(int length, const char + *styles)
    + SCI_SETLINESTATE(int line, int value)
    + SCI_GETLINESTATE(int line)
    + SCI_GETMAXLINESTATE
    +
    + +

    SCI_GETENDSTYLED
    + Scintilla keeps a record of the last character that is likely to be styled correctly. This is + moved forwards when characters after it are styled and moved backwards if changes are made to + the text of the document before it. Before drawing text, this position is checked to see if any + styling is needed and, if so, a SCN_STYLENEEDED notification message is sent to the + container. The container can send SCI_GETENDSTYLED to work out where it needs to + start styling. Scintilla will always ask to style whole lines.

    + +

    SCI_STARTSTYLING(int pos, int mask)
    + This prepares for styling by setting the styling position pos to start at and a + mask indicating which bits of the style bytes can be set. The mask allows styling + to occur over several passes, with, for example, basic styling done on an initial pass to + ensure that the text of the code is seen quickly and correctly, and then a second slower pass, + detecting syntax errors and using indicators to show where these are. For example, with the + standard settings of 5 style bits and 3 indicator bits, you would use a mask value + of 31 (0x1f) if you were setting text styles and did not want to change the indicators. After + SCI_STARTSTYLING, send multiple SCI_SETSTYLING messages for each + lexical entity to style.

    + +

    SCI_SETSTYLING(int length, int style)
    + This message sets the style of length characters starting at the styling position + and then increases the styling position by length, ready for the next call. If + sCell is the style byte, the operation is:
    + if ((sCell & mask) != style) sCell = (sCell & ~mask) | (style & + mask);
    +

    + +

    SCI_SETSTYLINGEX(int length, const char *styles)
    + As an alternative to SCI_SETSTYLING, which applies the same style to each byte, + you can use this message which specifies the styles for each of length bytes from + the styling position and then increases the styling position by length, ready for + the next call. The length styling bytes pointed at by styles should + not contain any bits not set in mask.

    + +

    SCI_SETLINESTATE(int line, int value)
    + SCI_GETLINESTATE(int line)
    + As well as the 8 bits of lexical state stored for each character there is also an integer + stored for each line. This can be used for longer lived parse states such as what the current + scripting language is in an ASP page. Use SCI_SETLINESTATE to set the integer + value and SCI_GETLINESTATE to get the value.

    + +

    SCI_GETMAXLINESTATE
    + This returns the last line that has any line state.

    + +

    Style definition

    + +

    While the style setting messages mentioned above change the style numbers associated with + text, these messages define how those style numbers are interpreted visually. There are 128 + lexer styles that can be set, numbered 0 to STYLEMAX (127). Unless you use SCI_SETSTYLEBITS to change the number + of style bits, styles 0 to 31 are used to set the text attributes. There are also some + predefined numbered styles starting at 32, The following STYLE_* constants are + defined.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    STYLE_DEFAULT32This style defines the attributes that all styles receive when the + SCI_STYLECLEARALL message is used.
    STYLE_LINENUMBER33This style sets the attributes of the text used to display line numbers in a line + number margin. The background colour set for this style also sets the background colour + for all margins that do not have any folding mask bits set. That is, any margin for which + mask & SC_MASK_FOLDERS is 0. See SCI_SETMARGINMASKN for more about masks.
    STYLE_BRACELIGHT34This style sets the attributes used when highlighting braces with the SCI_BRACEHIGHLIGHT message and + when highlighting the corresponding indentation with SCI_SETHIGHLIGHTGUIDE.
    STYLE_BRACEBAD35This style sets the display attributes used when marking an unmatched brace with the + SCI_BRACEBADLIGHT + message.
    STYLE_CONTROLCHAR36This style sets the font used when drawing control characters. + Only the font, size, bold, italics, and character set attributes are used and not + the colour attributes. See + also: SCI_SETCONTROLCHARSYMBOL.
    STYLE_INDENTGUIDE37This style sets the foreground and background colours used when drawing the + indentation guides.
    STYLE_CALLTIP38 Call tips normally use the font attributes defined by STYLE_DEFAULT. + Use of SCI_CALLTIPUSESTYLE + causes call tips to use this style instead. Only the font face name, font size, + foreground and background colours and character set attributes are used.
    STYLE_LASTPREDEFINED39To make it easier for client code to discover the range of styles that are + predefined, this is set to the style number of the last predefined style. This is + currently set to 39 and the last style with an identifier is 38, which reserves space + for one future predefined style.
    STYLE_MAX127This is not a style but is the number of the maximum style that can be set. Styles + between STYLE_LASTPREDEFINED and STYLE_MAX would be appropriate + if you used SCI_SETSTYLEBITS + to set more than 5 style bits.
    + +

    For each style you can set the font name, size and use of bold, italic and underline, + foreground and background colour and the character set. You can also choose to hide text with a + given style, display all characters as upper or lower case and fill from the last character on + a line to the end of the line (for embedded languages). There is also an experimental attribute + to make text read-only.

    + +

    It is entirely up to you how you use styles. If you want to use syntax colouring you might + use style 0 for white space, style 1 for numbers, style 2 for keywords, style 3 for strings, + style 4 for preprocessor, style 5 for operators, and so on.

    + SCI_STYLERESETDEFAULT
    + SCI_STYLECLEARALL
    + SCI_STYLESETFONT(int styleNumber, char + *fontName)
    + SCI_STYLESETSIZE(int styleNumber, int + sizeInPoints)
    + SCI_STYLESETBOLD(int styleNumber, bool + bold)
    + SCI_STYLESETITALIC(int styleNumber, bool + italic)
    + SCI_STYLESETUNDERLINE(int styleNumber, bool + underline)
    + SCI_STYLESETFORE(int styleNumber, int + colour)
    + SCI_STYLESETBACK(int styleNumber, int + colour)
    + SCI_STYLESETEOLFILLED(int styleNumber, bool + eolFilled)
    + SCI_STYLESETCHARACTERSET(int styleNumber, + int charSet)
    + SCI_STYLESETCASE(int styleNumber, int + caseMode)
    + SCI_STYLESETVISIBLE(int styleNumber, bool + visible)
    + SCI_STYLESETCHANGEABLE(int styleNumber, bool + changeable)
    + SCI_STYLESETHOTSPOT(int styleNumber, bool + hotspot)
    +
    + +

    SCI_STYLERESETDEFAULT
    + This message resets STYLE_DEFAULT to its state when Scintilla was + initialised.

    + +

    SCI_STYLECLEARALL
    + This message sets all styles to have the same attributes as STYLE_DEFAULT. If you + are setting up Scintilla for syntax colouring, it is likely that the lexical styles you set + will be very similar. One way to set the styles is to:
    + 1. Set STYLE_DEFAULT to the common features of all styles.
    + 2. Use SCI_STYLECLEARALL to copy this to all styles.
    + 3. Set the style attributes that make your lexical styles different.

    + +

    SCI_STYLESETFONT(int styleNumber, const char *fontName)
    + SCI_STYLESETSIZE(int styleNumber, int sizeInPoints)
    + SCI_STYLESETBOLD(int styleNumber, bool bold)
    + SCI_STYLESETITALIC(int styleNumber, bool italic)
    + These messages (plus SCI_STYLESETCHARACTERSET) set the font + attributes that are used to match the fonts you request to those available. The + fontName is a zero terminated string holding the name of a font. Under Windows, + only the first 32 characters of the name are used and the name is not case sensitive. For + internal caching, Scintilla tracks fonts by name and does care about the casing of font names, + so please be consistent. On GTK+ 2.x, either GDK or Pango can be used to display text. + Pango antialiases text, works well with Unicode and is better supported in recent versions of GTK+ + but GDK is faster. + Prepend a '!' character to the font name to use Pango.

    + +

    SCI_STYLESETUNDERLINE(int styleNumber, bool + underline)
    + You can set a style to be underlined. The underline is drawn in the foreground colour. All + characters with a style that includes the underline attribute are underlined, even if they are + white space.

    + +

    SCI_STYLESETFORE(int styleNumber, int colour)
    + SCI_STYLESETBACK(int styleNumber, int colour)
    + Text is drawn in the foreground colour. The space in each character cell that is not occupied + by the character is drawn in the background colour.

    + +

    SCI_STYLESETEOLFILLED(int styleNumber, bool + eolFilled)
    + If the last character in the line has a style with this attribute set, the remainder of the + line up to the right edge of the window is filled with the background colour set for the last + character. This is useful when a document contains embedded sections in another language such + as HTML pages with embedded JavaScript. By setting eolFilled to true + and a consistent background colour (different from the background colour set for the HTML + styles) to all JavaScript styles then JavaScript sections will be easily distinguished from + HTML.

    + +

    SCI_STYLESETCHARACTERSET(int styleNumber, int + charSet)
    + You can set a style to use a different character set than the default. The places where such + characters sets are likely to be useful are comments and literal strings. For example, + SCI_STYLESETCHARACTERSET(SCE_C_STRING, SC_CHARSET_RUSSIAN) would ensure that + strings in Russian would display correctly in C and C++ (SCE_C_STRING is the style + number used by the C and C++ lexer to display literal strings; it has the value 6). This + feature works differently on Windows and GTK+.

    + +

    The character sets supported on Windows are:
    + SC_CHARSET_ANSI, SC_CHARSET_ARABIC, SC_CHARSET_BALTIC, + SC_CHARSET_CHINESEBIG5, SC_CHARSET_DEFAULT, + SC_CHARSET_EASTEUROPE, SC_CHARSET_GB2312, + SC_CHARSET_GREEK, SC_CHARSET_HANGUL, SC_CHARSET_HEBREW, + SC_CHARSET_JOHAB, SC_CHARSET_MAC, SC_CHARSET_OEM, + SC_CHARSET_RUSSIAN (code page 1251), + SC_CHARSET_SHIFTJIS, SC_CHARSET_SYMBOL, SC_CHARSET_THAI, + SC_CHARSET_TURKISH, and SC_CHARSET_VIETNAMESE.

    + +

    The character sets supported on GTK+ are:
    + SC_CHARSET_ANSI, SC_CHARSET_CYRILLIC (code page 1251), + SC_CHARSET_EASTEUROPE, + SC_CHARSET_GB2312, SC_CHARSET_HANGUL, + SC_CHARSET_RUSSIAN (KOI8-R), SC_CHARSET_SHIFTJIS, and + SC_CHARSET_8859_15.

    + +

    SCI_STYLESETCASE(int styleNumber, int caseMode)
    + The value of caseMode determines how text is displayed. You can set upper case + (SC_CASE_UPPER, 1) or lower case (SC_CASE_LOWER, 2) or display + normally (SC_CASE_MIXED, 0). This does not change the stored text, only how it is + displayed.

    + +

    SCI_STYLESETVISIBLE(int styleNumber, bool visible)
    + Text is normally visible. However, you can completely hide it by giving it a style with the + visible set to 0. This could be used to hide embedded formatting instructions or + hypertext keywords in HTML or XML.

    + +

    SCI_STYLESETCHANGEABLE(int styleNumber, bool + changeable)
    + This is an experimental and incompletely implemented style attribute. The default setting is + changeable set true but when set false it makes text + read-only. Currently it only stops the caret from being within not-changeable text and does not + yet stop deleting a range that contains not-changeable text.

    + +

    SCI_STYLESETHOTSPOT(int styleNumber, bool + hotspot)
    + This style is used to mark ranges of text that can detect mouse clicks. + The cursor changes to a hand over hotspots, and the foreground, and background colours + may change and an underline appear to indicate that these areas are sensitive to clicking. + This may be used to allow hyperlinks to other documents.

    + + SCI_STYLESETHOTSPOT(int styleNumber, bool + hotspot)
    + +

    Caret, selection, and hotspot styles

    + +

    The selection is shown by changing the foreground and/or background colours. If one of these + is not set then that attribute is not changed for the selection. The default is to show the + selection by changing the background to light gray and leaving the foreground the same as when + it was not selected. When there is no selection, the current insertion point is marked by the + text caret. This is a vertical line that is normally blinking on and off to attract the users + attention.

    + SCI_SETSELFORE(bool useSelectionForeColour, int + colour)
    + SCI_SETSELBACK(bool useSelectionBackColour, int + colour)
    + SCI_SETSELALPHA(int alpha)
    + SCI_GETSELALPHA
    + SCI_SETCARETFORE(int colour)
    + SCI_GETCARETFORE
    + SCI_SETCARETLINEVISIBLE(bool + show)
    + SCI_GETCARETLINEVISIBLE
    + SCI_SETCARETLINEBACK(int colour)
    + SCI_GETCARETLINEBACK
    + SCI_SETCARETLINEBACKALPHA(int alpha)
    + SCI_GETCARETLINEBACKALPHA
    + SCI_SETCARETPERIOD(int milliseconds)
    + SCI_GETCARETPERIOD
    + SCI_SETCARETWIDTH(int pixels)
    + SCI_GETCARETWIDTH
    + SCI_SETHOTSPOTACTIVEFORE
    + SCI_SETHOTSPOTACTIVEBACK
    + SCI_SETHOTSPOTACTIVEUNDERLINE
    + SCI_SETHOTSPOTSINGLELINE
    + SCI_SETCONTROLCHARSYMBOL(int + symbol)
    + SCI_GETCONTROLCHARSYMBOL
    + SCI_SETCARETSTICKY
    + SCI_GETCARETSTICKY
    + SCI_TOGGLECARETSTICKY
    +
    + +

    SCI_SETSELFORE(bool useSelectionForeColour, int colour)
    + SCI_SETSELBACK(bool useSelectionBackColour, int colour)
    + You can choose to override the default selection colouring with these two messages. The colour + you provide is used if you set useSelection*Colour to true. If it is + set to false, the default colour colouring is used and the colour + argument has no effect.

    +

    SCI_SETSELALPHA(int alpha)
    + SCI_GETSELALPHA
    + The selection can be drawn translucently in the selection background colour by + setting an alpha value.

    + +

    SCI_SETCARETFORE(int colour)
    + SCI_GETCARETFORE
    + The colour of the caret can be set with SCI_SETCARETFORE and retrieved with + SCI_CETCARETFORE.

    + +

    SCI_SETCARETLINEVISIBLE(bool show)
    + SCI_GETCARETLINEVISIBLE
    + SCI_SETCARETLINEBACK(int colour)
    + SCI_GETCARETLINEBACK
    + SCI_SETCARETLINEBACKALPHA(int alpha)
    + SCI_GETCARETLINEBACKALPHA
    + You can choose to make the background colour of the line containing the caret different with + these messages. To do this, set the desired background colour with + SCI_SETCARETLINEBACK, then use SCI_SETCARETLINEVISIBLE(true) to + enable the effect. You can cancel the effect with SCI_SETCARETLINEVISIBLE(false). + The two SCI_GETCARET* functions return the state and the colour. This form of + background colouring has highest priority when a line has markers that would otherwise change + the background colour. + The caret line may also be drawn translucently which allows other background colours to show + through. This is done by setting the alpha (translucency) value by calling + SCI_SETCARETLINEBACKALPHA. When the alpha is not SC_ALPHA_NOALPHA, + the caret line is drawn after all other features so will affect the colour of all other features. +

    + +

    SCI_SETCARETPERIOD(int milliseconds)
    + SCI_GETCARETPERIOD
    + The rate at which the caret blinks can be set with SCI_SETCARETPERIOD which + determines the time in milliseconds that the caret is visible or invisible before changing + state. Setting the period to 0 stops the caret blinking. The default value is 500 milliseconds. + SCI_GETCARETPERIOD returns the current setting.

    + +

    SCI_SETCARETWIDTH(int pixels)
    + SCI_GETCARETWIDTH
    + The width of the caret can be set with SCI_SETCARETWIDTH to a value of 0, 1, 2 or + 3 pixels. The default width is 1 pixel. You can read back the current width with + SCI_GETCARETWIDTH. A width of 0 makes the caret invisible (added at version + 1.50).

    + +

    SCI_SETHOTSPOTACTIVEFORE(bool useHotSpotForeColour, int colour)
    + SCI_SETHOTSPOTACTIVEBACK(bool useHotSpotBackColour, int colour)
    + SCI_SETHOTSPOTACTIVEUNDERLINE(bool underline,)
    + SCI_SETHOTSPOTSINGLELINE(bool singleLine,)
    + While the cursor hovers over text in a style with the hotspot attribute set, + the default colouring can be modified and an underline drawn with these settings. + Single line mode stops a hotspot from wrapping onto next line.

    + +

    SCI_SETCONTROLCHARSYMBOL(int symbol)
    + SCI_GETCONTROLCHARSYMBOL
    + By default, Scintilla displays control characters (characters with codes less than 32) in a + rounded rectangle as ASCII mnemonics: "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", + "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", + "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US". These mnemonics come from the + early days of signaling, though some are still used (LF = Line Feed, BS = Back Space, CR = + Carriage Return, for example).

    + +

    You can choose to replace these mnemonics by a nominated symbol with an ASCII code in the + range 32 to 255. If you set a symbol value less than 32, all control characters are displayed + as mnemonics. The symbol you set is rendered in the font of the style set for the character. + You can read back the current symbol with the SCI_GETCONTROLCHARSYMBOL message. + The default symbol value is 0.

    + +

    SCI_SETCARETSTICKY(bool useCaretStickyBehaviour)
    + SCI_GETCARETSTICKY
    + SCI_TOGGLECARETSTICKY
    + These messages set, get or toggle the caretSticky flag which controls when the last position + of the caret on the line is saved. When set to true, the position is not saved when you type + a character, a tab, paste the clipboard content or press backspace.

    + +

    Margins

    + +

    There may be up to five margins to the left of the text display, plus a gap either side of + the text. Each margin can be set to display either symbols or line numbers with SCI_SETMARGINTYPEN. The markers + that can be displayed in each margin are set with SCI_SETMARGINMASKN. Any markers not associated with + a visible margin will be displayed as changes in background colour in the text. A width in + pixels can be set for each margin. Margins with a zero width are ignored completely. You can + choose if a mouse click in a margin sends a SCN_MARGINCLICK notification to the container or + selects a line of text.

    + +

    The margins are numbered 0 to 4. Using a margin number outside the valid range has no + effect. By default, margin 0 is set to display line numbers, but is given a width of 0, so it + is hidden. Margin 1 is set to display non-folding symbols and is given a width of 16 pixels, so + it is visible. Margin 2 is set to display the folding symbols, but is given a width of 0, so it + is hidden. Of course, you can set the margins to be whatever you wish.

    + SCI_SETMARGINTYPEN(int margin, int + type)
    + SCI_GETMARGINTYPEN(int margin)
    + SCI_SETMARGINWIDTHN(int margin, int + pixelWidth)
    + SCI_GETMARGINWIDTHN(int margin)
    + SCI_SETMARGINMASKN(int margin, int + mask)
    + SCI_GETMARGINMASKN(int margin)
    + SCI_SETMARGINSENSITIVEN(int margin, bool + sensitive)
    + SCI_GETMARGINSENSITIVEN(int + margin)
    + SCI_SETMARGINLEFT(<unused>, int + pixels)
    + SCI_GETMARGINLEFT
    + SCI_SETMARGINRIGHT(<unused>, int + pixels)
    + SCI_GETMARGINRIGHT
    + SCI_SETFOLDMARGINCOLOUR(bool useSetting, int colour)
    + SCI_SETFOLDMARGINHICOLOUR(bool useSetting, int colour)
    +
    + +

    SCI_SETMARGINTYPEN(int margin, int iType)
    + SCI_GETMARGINTYPEN(int margin)
    + These two routines set and get the type of a margin. The margin argument should be 0, 1, 2, 3 or 4. + You can use the predefined constants SC_MARGIN_SYMBOL (0) and + SC_MARGIN_NUMBER (1) to set a margin as either a line number or a symbol margin. + By convention, margin 0 is used for line numbers and the next two are used for symbols. You can + also use the constants SC_MARGIN_BACK (2) and SC_MARGIN_FORE (3) for + symbol margins that set their background colour to match the STYLE_DEFAULT background and + foreground colours.

    + +

    SCI_SETMARGINWIDTHN(int margin, int pixelWidth)
    + SCI_GETMARGINWIDTHN(int margin)
    + These routines set and get the width of a margin in pixels. A margin with zero width is + invisible. By default, Scintilla sets margin 1 for symbols with a width of 16 pixels, so this + is a reasonable guess if you are not sure what would be appropriate. Line number margins widths + should take into account the number of lines in the document and the line number style. You + could use something like SCI_TEXTWIDTH(STYLE_LINENUMBER, "_99999") to get a + suitable width.

    + +

    SCI_SETMARGINMASKN(int margin, int mask)
    + SCI_GETMARGINMASKN(int margin)
    + The mask is a 32-bit value. Each bit corresponds to one of 32 logical symbols that can be + displayed in a margin that is enabled for symbols. There is a useful constant, + SC_MASK_FOLDERS (0xFE000000 or -33554432), that is a mask for the 7 logical + symbols used to denote folding. You can assign a wide range of symbols and colours to each of + the 32 logical symbols, see Markers for more information. If (mask + & SC_MASK_FOLDERS)==0, the margin background colour is controlled by style 33 (STYLE_LINENUMBER).

    + +

    You add logical markers to a line with SCI_MARKERADD. If a line has an associated marker that + does not appear in the mask of any margin with a non-zero width, the marker changes the + background colour of the line. For example, suppose you decide to use logical marker 10 to mark + lines with a syntax error and you want to show such lines by changing the background colour. + The mask for this marker is 1 shifted left 10 times (1<<10) which is 0x400. If you make + sure that no symbol margin includes 0x400 in its mask, any line with the marker gets the + background colour changed.

    + +

    To set a non-folding margin 1 use SCI_SETMARGINMASKN(1, ~SC_MASK_FOLDERS); to + set a folding margin 2 use SCI_SETMARGINMASKN(2, SC_MASK_FOLDERS). This is the + default set by Scintilla. ~SC_MASK_FOLDERS is 0x1FFFFFF in hexadecimal or 33554431 + decimal. Of course, you may need to display all 32 symbols in a margin, in which case use + SCI_SETMARGINMASKN(margin, -1).

    + +

    SCI_SETMARGINSENSITIVEN(int margin, bool + sensitive)
    + SCI_GETMARGINSENSITIVEN(int margin)
    + Each of the five margins can be set sensitive or insensitive to mouse clicks. A click in a + sensitive margin sends a SCN_MARGINCLICK notification to the container. Margins that are not sensitive act as + selection margins which make it easy to select ranges of lines. By default, all margins are + insensitive.

    + +

    SCI_SETMARGINLEFT(<unused>, int pixels)
    + SCI_GETMARGINLEFT
    + SCI_SETMARGINRIGHT(<unused>, int pixels)
    + SCI_GETMARGINRIGHT
    + These messages set and get the width of the blank margin on both sides of the text in pixels. + The default is to one pixel on each side.

    + +

    SCI_SETFOLDMARGINCOLOUR(bool useSetting, int colour)
    + SCI_SETFOLDMARGINHICOLOUR(bool useSetting, int colour)
    + These messages allow changing the colour of the fold margin and fold margin highlight. + On Windows the fold margin colour defaults to ::GetSysColor(COLOR_3DFACE) and the fold margin highlight + colour to ::GetSysColor(COLOR_3DHIGHLIGHT).

    + +

    Other settings

    + SCI_SETUSEPALETTE(bool + allowPaletteUse)
    + SCI_GETUSEPALETTE
    + SCI_SETBUFFEREDDRAW(bool isBuffered)
    + SCI_GETBUFFEREDDRAW
    + SCI_SETTWOPHASEDRAW(bool twoPhase)
    + SCI_GETTWOPHASEDRAW
    + SCI_SETCODEPAGE(int codePage)
    + SCI_GETCODEPAGE
    + SCI_SETWORDCHARS(<unused>, const char + *chars)
    + SCI_SETWHITESPACECHARS(<unused>, const char + *chars)
    + SCI_SETCHARSDEFAULT
    + SCI_GRABFOCUS
    + SCI_SETFOCUS(bool focus)
    + SCI_GETFOCUS
    +
    + +

    SCI_SETUSEPALETTE(bool allowPaletteUse)
    + SCI_GETUSEPALETTE
    + On 8 bit displays, which can only display a maximum of 256 colours, the graphics environment + mediates between the colour needs of applications through the use of palettes. On GTK+, + Scintilla always uses a palette.

    + +

    On Windows, there are some problems with visual flashing when switching between applications + with palettes and it is also necessary for the application containing the Scintilla control to + forward some messages to Scintilla for its palette code to work. Because of this, by default, + the palette is not used and the application must tell Scintilla to use one. If Scintilla is not + using a palette, it will only display in those colours already available, which are often the + 20 Windows system colours.

    + +

    To see an example of how to enable palette support in Scintilla, search the text of SciTE + for WM_PALETTECHANGED, WM_QUERYNEWPALETTE and + SCI_SETUSEPALETTE. The Windows messages to forward are:
    + WM_SYSCOLORCHANGE, WM_PALETTECHANGED, + WM_QUERYNEWPALETTE (should return TRUE).

    + +

    To forward a message (WM_XXXX, WPARAM, LPARAM) to Scintilla, you can use + SendMessage(hScintilla, WM_XXXX, WPARAM, LPARAM) where hScintilla is + the handle to the Scintilla window you created as your editor.

    + +

    While we are on the subject of forwarding messages in Windows, the top level window should + forward any WM_SETTINGCHANGE messages to Scintilla (this is currently used to + collect changes to mouse settings, but could be used for other user interface items in the + future).

    + +

    SCI_SETBUFFEREDDRAW(bool isBuffered)
    + SCI_GETBUFFEREDDRAW
    + These messages turn buffered drawing on or off and report the buffered drawing state. Buffered + drawing draws each line into a bitmap rather than directly to the screen and then copies the + bitmap to the screen. This avoids flickering although it does take longer. The default is for + drawing to be buffered.

    + +

    SCI_SETTWOPHASEDRAW(bool twoPhase)
    + SCI_GETTWOPHASEDRAW
    + Two phase drawing is a better but slower way of drawing text. + In single phase drawing each run of characters in one style is drawn along with its background. + If a character overhangs the end of a run, such as in "V_" where the + "V" is in a different style from the "_", then this can cause the right hand + side of the "V" to be overdrawn by the background of the "_" which + cuts it off. Two phase drawing + fixes this by drawing all the backgrounds first and then drawing the text in + transparent mode. Two phase drawing may flicker more than single phase + unless buffered drawing is on. The default is for drawing to be two phase.

    + +

    SCI_SETCODEPAGE(int codePage)
    + SCI_GETCODEPAGE
    + Scintilla has some support for Japanese, Chinese and Korean DBCS. Use this message with + codePage set to the code page number to set Scintilla to use code page information + to ensure double byte characters are treated as one character rather than two. This also stops + the caret from moving between the two bytes in a double byte character. + Do not use this message to choose between different single byte character sets: it doesn't do that. + Call with + codePage set to zero to disable DBCS support. The default is + SCI_SETCODEPAGE(0).

    + +

    Code page SC_CP_UTF8 (65001) sets Scintilla into Unicode mode with the document + treated as a sequence of characters expressed in UTF-8. The text is converted to the platform's + normal Unicode encoding before being drawn by the OS and thus can display Hebrew, Arabic, + Cyrillic, and Han characters. Languages which can use two characters stacked vertically in one + horizontal space, such as Thai, will mostly work but there are some issues where the characters + are drawn separately leading to visual glitches. Bi-directional text is not supported. Characters outside the + Basic Multilingual Plane are unlikely to work.

    + +

    On Windows, code page can be set to 932 (Japanese Shift-JIS), 936 (Simplified Chinese GBK), + 949 (Korean Unified Hangul Code), 950 (Traditional Chinese Big5), or 1361 (Korean Johab) + although these may require installation of language specific support.

    + +

    On GTK+, code page SC_CP_DBCS (1) sets Scintilla into + multi byte character mode as is required for Japanese language processing with + the EUC encoding.

    + +

    For GTK+ 1.x, the locale should be set to a Unicode locale with a call similar to + setlocale(LC_CTYPE, "en_US.UTF-8"). Fonts with an "iso10646" registry + should be used in a font set. Font sets are a comma separated list of partial font + specifications where each partial font specification can be in the form: + foundry-fontface-charsetregistry-encoding or + fontface-charsetregistry-encoding or foundry-fontface or + fontface. An example is "misc-fixed-iso10646-1,*". + On GTK+ 2.x, Pango fonts should be used rather than font sets.

    + +

    Setting codePage to a non-zero value that is not SC_CP_UTF8 is + operating system dependent.

    + +

    SCI_SETWORDCHARS(<unused>, const char *chars)
    + Scintilla has several functions that operate on words, which are defined to be contiguous + sequences of characters from a particular set of characters. This message defines which + characters are members of that set. The character sets are set to default values before processing this + function. + For example, if you don't allow '_' in your set of characters + use:
    + SCI_SETWORDCHARS(0, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");

    + +

    SCI_SETWHITESPACECHARS(<unused>, const char *chars)
    + Similar to SCI_SETWORDCHARS, this message allows the user to define which chars Scintilla considers + as whitespace. Setting the whitespace chars allows the user to fine-tune Scintilla's behaviour doing + such things as moving the cursor to the start or end of a word; for example, by defining punctuation chars + as whitespace, they will be skipped over when the user presses ctrl+left or ctrl+right. + This function should be called after SCI_SETWORDCHARS as it will + reset the whitespace characters to the default set.

    +

    SCI_SETCHARSDEFAULT
    + Use the default sets of word and whitespace characters. This sets whitespace to space, tab and other + characters with codes less than 0x20, with word characters set to alphanumeric and '_'. +

    + + +

    SCI_GRABFOCUS
    + SCI_SETFOCUS(bool focus)
    + SCI_GETFOCUS
    + Scintilla can be told to grab the focus with this message. This is needed more on GTK+ where + focus handling is more complicated than on Windows.

    + +

    The internal focus flag can be set with SCI_SETFOCUS. This is used by clients + that have complex focus requirements such as having their own window that gets the real focus + but with the need to indicate that Scintilla has the logical focus.

    + +

    Brace highlighting

    + SCI_BRACEHIGHLIGHT(int pos1, int + pos2)
    + SCI_BRACEBADLIGHT(int pos1)
    + SCI_BRACEMATCH(int position, int + maxReStyle)
    +
    + +

    SCI_BRACEHIGHLIGHT(int pos1, int pos2)
    + Up to two characters can be highlighted in a 'brace highlighting style', which is defined as + style number STYLE_BRACELIGHT (34). + If you have enabled indent guides, you may also wish to highlight the indent that corresponds + with the brace. You can locate the column with SCI_GETCOLUMN and highlight the indent with SCI_SETHIGHLIGHTGUIDE.

    + +

    SCI_BRACEBADLIGHT(int pos1)
    + If there is no matching brace then the brace + badlighting style, style BRACE_BADLIGHT (35), can be used to show the brace + that is unmatched. Using a position of INVALID_POSITION (-1) removes the + highlight.

    + +

    SCI_BRACEMATCH(int pos, int maxReStyle)
    + The SCI_BRACEMATCH message finds a corresponding matching brace given + pos, the position of one brace. The brace characters handled are '(', ')', '[', + ']', '{', '}', '<', and '>'. The search is forwards from an opening brace and backwards + from a closing brace. If the character at position is not a brace character, or a matching + brace cannot be found, the return value is -1. Otherwise, the return value is the position of + the matching brace.

    + +

    A match only occurs if the style of the matching brace is the same as the starting brace or + the matching brace is beyond the end of styling. Nested braces are handled correctly. The + maxReStyle parameter must currently be 0 - it may be used in the future to limit + the length of brace searches.

    + +

    Tabs and Indentation Guides

    + +

    Indentation (the white space at the start of a line) is often used by programmers to clarify + program structure and in some languages, for example Python, it may be part of the language + syntax. Tabs are normally used in editors to insert a tab character or to pad text with spaces + up to the next tab.

    + +

    Scintilla can be set to treat tab and backspace in the white space at the start of a line in + a special way: inserting a tab indents the line to the next indent position rather than just + inserting a tab at the current character position and backspace unindents the line rather than + deleting a character. Scintilla can also display indentation guides (vertical lines) to help + you to generate code.

    + SCI_SETTABWIDTH(int widthInChars)
    + SCI_GETTABWIDTH
    + SCI_SETUSETABS(bool useTabs)
    + SCI_GETUSETABS
    + SCI_SETINDENT(int widthInChars)
    + SCI_GETINDENT
    + SCI_SETTABINDENTS(bool tabIndents)
    + SCI_GETTABINDENTS
    + SCI_SETBACKSPACEUNINDENTS(bool + bsUnIndents)
    + SCI_GETBACKSPACEUNINDENTS
    + SCI_SETLINEINDENTATION(int line, int + indentation)
    + SCI_GETLINEINDENTATION(int line)
    + SCI_GETLINEINDENTPOSITION(int + line)
    + SCI_SETINDENTATIONGUIDES(bool + view)
    + SCI_GETINDENTATIONGUIDES
    + SCI_SETHIGHLIGHTGUIDE(int column)
    + SCI_GETHIGHLIGHTGUIDE
    +
    + +

    SCI_SETTABWIDTH(int widthInChars)
    + SCI_GETTABWIDTH
    + SCI_SETTABWIDTH sets the size of a tab as a multiple of the size of a space + character in STYLE_DEFAULT. The default tab width is 8 characters. There are no + limits on tab sizes, but values less than 1 or large values may have undesirable effects.

    + +

    SCI_SETUSETABS(bool useTabs)
    + SCI_GETUSETABS
    + SCI_SETUSETABS determines whether indentation should be created out of a mixture + of tabs and spaces or be based purely on spaces. Set useTabs to false + (0) to create all tabs and indents out of spaces. The default is true. You can use + SCI_GETCOLUMN to get the column of a + position taking the width of a tab into account.

    + SCI_SETINDENT(int widthInChars)
    + SCI_GETINDENT
    + SCI_SETINDENT sets the size of indentation in terms of the width of a space in STYLE_DEFAULT. If you set a width of + 0, the indent size is the same as the tab size. There are no limits on indent sizes, but values + less than 0 or large values may have undesirable effects.
    +
    + + +

    SCI_SETTABINDENTS(bool tabIndents)
    + SCI_GETTABINDENTS
    + SCI_SETBACKSPACEUNINDENTS(bool bsUnIndents)
    + SCI_GETBACKSPACEUNINDENTS
    +

    + +

    Inside indentation white space, the tab and backspace keys can be made to indent and + unindent rather than insert a tab character or delete a character with the + SCI_SETTABINDENTS and SCI_SETBACKSPACEUNINDENTS functions.

    + +

    SCI_SETLINEINDENTATION(int line, int indentation)
    + SCI_GETLINEINDENTATION(int line)
    + The amount of indentation on a line can be discovered and set with + SCI_GETLINEINDENTATION and SCI_SETLINEINDENTATION. The indentation is + measured in character columns, which correspond to the width of space characters.

    + +

    SCI_GETLINEINDENTPOSITION(int line)
    + This returns the position at the end of indentation of a line.

    + +

    SCI_SETINDENTATIONGUIDES(bool view)
    + SCI_GETINDENTATIONGUIDES
    + Indentation guides are dotted vertical lines that appear within indentation white space every + indent size columns. They make it easy to see which constructs line up especially when they + extend over multiple pages. Style STYLE_INDENTGUIDE (37) is used to specify the + foreground and background colour of the indentation guides.

    + +

    SCI_SETHIGHLIGHTGUIDE(int column)
    + SCI_GETHIGHLIGHTGUIDE
    + When brace highlighting occurs, the indentation guide corresponding to the braces may be + highlighted with the brace highlighting style, STYLE_BRACELIGHT (34). Set column to 0 to + cancel this highlight.

    + +

    Markers

    + +

    There are 32 markers, numbered 0 to 31, and you can assign any combination of them to each + line in the document. Markers appear in the selection + margin to the left of the text. If the selection margin is set to zero width, the + background colour of the whole line is changed instead. Marker numbers 25 to 31 are used by + Scintilla in folding margins, and have symbolic names of the form SC_MARKNUM_*, + for example SC_MARKNUM_FOLDEROPEN.

    + +

    Marker numbers 0 to 24 have no pre-defined function; you can use them to mark syntax errors + or the current point of execution, break points, or whatever you need marking. If you do not + need folding, you can use all 32 for any purpose you wish.

    + +

    Each marker number has a symbol associated with it. You can also set the foreground and + background colour for each marker number, so you can use the same symbol more than once with + different colouring for different uses. Scintilla has a set of symbols you can assign + (SC_MARK_*) or you can use characters. By default, all 32 markers are set to + SC_MARK_CIRCLE with a black foreground and a white background.

    + +

    The markers are drawn in the order of their numbers, so higher numbered markers appear on + top of lower numbered ones. Markers try to move with their text by tracking where the start of + their line moves. When a line is deleted, its markers are combined, by an OR + operation, with the markers of the previous line.

    + SCI_MARKERDEFINE(int markerNumber, int + markerSymbols)
    + SCI_MARKERDEFINEPIXMAP(int markerNumber, + const char *xpm)
    + SCI_MARKERSETFORE(int markerNumber, int + colour)
    + SCI_MARKERSETBACK(int markerNumber, int + colour)
    + SCI_MARKERSETALPHA(int markerNumber, int + alpha)
    + SCI_MARKERADD(int line, int markerNumber)
    + SCI_MARKERADDSET(int line, int markerMask)
    + SCI_MARKERDELETE(int line, int + markerNumber)
    + SCI_MARKERDELETEALL(int markerNumber)
    + SCI_MARKERGET(int line)
    + SCI_MARKERNEXT(int lineStart, int + markerMask)
    + SCI_MARKERPREVIOUS(int lineStart, int + markerMask)
    + SCI_MARKERLINEFROMHANDLE(int + handle)
    + SCI_MARKERDELETEHANDLE(int handle)
    +
    + +

    SCI_MARKERDEFINE(int markerNumber, int markerSymbols)
    + This message associates a marker number in the range 0 to 31 with one of the marker symbols or + an ASCII character. The general-purpose marker symbols currently available are:
    + SC_MARK_CIRCLE, SC_MARK_ROUNDRECT, SC_MARK_ARROW, + SC_MARK_SMALLRECT, SC_MARK_SHORTARROW, SC_MARK_EMPTY, + SC_MARK_ARROWDOWN, SC_MARK_MINUS, SC_MARK_PLUS, + SC_MARK_ARROWS, SC_MARK_DOTDOTDOT, SC_MARK_EMPTY, + SC_MARK_BACKGROUND and SC_MARK_FULLRECT.

    + +

    The SC_MARK_BACKGROUND marker changes the background colour of the line only. + The SC_MARK_FULLRECT symbol mirrors this, changing only the margin background colour. + The SC_MARK_EMPTY symbol is invisible, allowing client code to track the movement + of lines. You would also use it if you changed the folding style and wanted one or more of the + SC_FOLDERNUM_* markers to have no associated symbol.

    + +

    There are also marker symbols designed for use in the folding margin in a flattened tree + style.
    + SC_MARK_BOXMINUS, SC_MARK_BOXMINUSCONNECTED, + SC_MARK_BOXPLUS, SC_MARK_BOXPLUSCONNECTED, + SC_MARK_CIRCLEMINUS, SC_MARK_CIRCLEMINUSCONNECTED, + SC_MARK_CIRCLEPLUS, SC_MARK_CIRCLEPLUSCONNECTED, + SC_MARK_LCORNER, SC_MARK_LCORNERCURVE, SC_MARK_TCORNER, + SC_MARK_TCORNERCURVE, and SC_MARK_VLINE.

    + Characters can be used as markers by adding the ASCII value of the character to + SC_MARK_CHARACTER (10000). For example, to use 'A' (ASCII code 65) as marker + number 1 use:
    + SCI_MARKERDEFINE(1, SC_MARK_CHARACTER+65).
    + +

    The marker numbers SC_MARKNUM_FOLDER and SC_MARKNUM_FOLDEROPEN are + used for showing that a fold is present and open or closed. Any symbols may be assigned for + this purpose although the (SC_MARK_PLUS, SC_MARK_MINUS) pair or the + (SC_MARK_ARROW, SC_MARK_ARROWDOWN) pair are good choices. As well as + these two, more assignments are needed for the flattened tree style: + SC_MARKNUM_FOLDEREND, SC_MARKNUM_FOLDERMIDTAIL, + SC_MARKNUM_FOLDEROPENMID, SC_MARKNUM_FOLDERSUB, and + SC_MARKNUM_FOLDERTAIL. The bits used for folding are specified by + SC_MASK_FOLDERS, which is commonly used as an argument to + SCI_SETMARGINMASKN when defining a margin to be used for folding.

    + +

    This table shows which SC_MARK_* symbols should be assigned to which + SC_MARKNUM_* marker numbers to obtain four folding styles: Arrow (mimics + Macintosh), plus/minus shows folded lines as '+' and opened folds as '-', Circle tree, Box + tree.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SC_MARKNUM_*ArrowPlus/minusCircle treeBox tree
    FOLDEROPENARROWDOWNMINUSCIRCLEMINUSBOXMINUS
    FOLDERARROWPLUSCIRCLEPLUSBOXPLUS
    FOLDERSUBEMPTYEMPTYVLINEVLINE
    FOLDERTAILEMPTYEMPTYLCORNERCURVELCORNER
    FOLDERENDEMPTYEMPTYCIRCLEPLUSCONNECTEDBOXPLUSCONNECTED
    FOLDEROPENMIDEMPTYEMPTYCIRCLEMINUSCONNECTEDBOXMINUSCONNECTED
    FOLDERMIDTAILEMPTYEMPTYTCORNERCURVETCORNER
    + +

    SCI_MARKERDEFINEPIXMAP(int markerNumber, const char + *xpm)
    + Markers can be set to pixmaps with this message. The XPM format is used for the pixmap and it + is limited to pixmaps that use one character per pixel. The data should be null terminated. + Pixmaps use the SC_MARK_PIXMAP marker symbol. You can find the full description of + the XPM format here.

    + +

    SCI_MARKERSETFORE(int markerNumber, int colour)
    + SCI_MARKERSETBACK(int markerNumber, int colour)
    + These two messages set the foreground and background colour of a marker number.

    +

    SCI_MARKERSETALPHA(int markerNumber, int alpha)
    + When markers are drawn in the content area, either because there is no margin for them or + they are of SC_MARK_BACKGROUND type, they may be drawn translucently by + setting an alpha value.

    + +

    SCI_MARKERADD(int line, int markerNumber)
    + This message adds marker number markerNumber to a line. The message returns -1 if + this fails (illegal line number, out of memory) or it returns a marker handle number that + identifies the added marker. You can use this returned handle with SCI_MARKERLINEFROMHANDLE to find where a + marker is after moving or combining lines and with SCI_MARKERDELETEHANDLE to delete the marker + based on its handle. The message does not check the value of markerNumber, nor does it + check if the line already contains the marker.

    + +

    SCI_MARKERADDSET(int line, int markerMask)
    + This message can add one or more markers to a line with a single call, specified in the same "one-bit-per-marker" 32-bit integer format returned by + SCI_MARKERGET + (and used by the mask-based marker search functions + SCI_MARKERNEXT and + SCI_MARKERPREVIOUS). + As with + SCI_MARKERADD, no check is made + to see if any of the markers are already present on the targeted line.

    + +

    SCI_MARKERDELETE(int line, int markerNumber)
    + This searches the given line number for the given marker number and deletes it if it is + present. If you added the same marker more than once to the line, this will delete one copy + each time it is used. If you pass in a marker number of -1, all markers are deleted from the + line.

    + +

    SCI_MARKERDELETEALL(int markerNumber)
    + This removes markers of the given number from all lines. If markerNumber is -1, it deletes all + markers from all lines.

    + +

    SCI_MARKERGET(int line)
    + This returns a 32-bit integer that indicates which markers were present on the line. Bit 0 is + set if marker 0 is present, bit 1 for marker 1 and so on.

    + +

    SCI_MARKERNEXT(int lineStart, int markerMask)
    + SCI_MARKERPREVIOUS(int lineStart, int markerMask)
    + These messages search efficiently for lines that include a given set of markers. The search + starts at line number lineStart and continues forwards to the end of the file + (SCI_MARKERNEXT) or backwards to the start of the file + (SCI_MARKERPREVIOUS). The markerMask argument should have one bit set + for each marker you wish to find. Set bit 0 to find marker 0, bit 1 for marker 1 and so on. The + message returns the line number of the first line that contains one of the markers in + markerMask or -1 if no marker is found.

    + +

    SCI_MARKERLINEFROMHANDLE(int markerHandle)
    + The markerHandle argument is an identifier for a marker returned by SCI_MARKERADD. This function searches + the document for the marker with this handle and returns the line number that contains it or -1 + if it is not found.

    + +

    SCI_MARKERDELETEHANDLE(int markerHandle)
    + The markerHandle argument is an identifier for a marker returned by SCI_MARKERADD. This function searches + the document for the marker with this handle and deletes the marker if it is found.

    + +

    Indicators

    + +

    By default, Scintilla organizes the style byte associated with each text byte as 5 bits of + style information (for 32 styles) and 3 bits of indicator information for 3 independent + indicators so that, for example, syntax errors, deprecated names and bad indentation could all + be displayed at once. Indicators may be displayed as simple underlines, squiggly underlines, a + line of small 'T' shapes, a line of diagonal hatching, a strike-out or a rectangle around the text.

    + +

    The indicators are set using SCI_STARTSTYLING with a INDICS_MASK mask + and SCI_SETSTYLING with the values + INDIC0_MASK, INDIC1_MASK and INDIC2_MASK.

    + +

    If you are using indicators in a buffer that has a lexer active + (see SCI_SETLEXER), + you must save lexing state information before setting any indicators and restore it afterwards. + Use SCI_GETENDSTYLED + to retrieve the current "styled to" position and + SCI_STARTSTYLING + to reset the styling position and mask (0x1f in the default layout of 5 style bits and 3 indicator bits) + when you are done.

    + +

    The number of bits used for styles can be altered with SCI_SETSTYLEBITS from 0 to 7 bits. The remaining bits + can be used for indicators, so there can be from 1 to 8 indicators. However, the + INDIC*_MASK constants defined in Scintilla.h all assume 5 bits of + styling information and 3 indicators. If you use a different arrangement, you must define your + own constants.

    + +

    The SCI_INDIC* messages allow you to get and set the visual appearance of the + indicators. They all use an indicatorNumber argument in the range 0 to 7 to set + the indicator to style. With the default settings, only indicators 0, 1 and 2 will have any + visible effect.

    + SCI_INDICSETSTYLE(int indicatorNumber, int + indicatorStyle)
    + SCI_INDICGETSTYLE(int indicatorNumber)
    + SCI_INDICSETFORE(int indicatorNumber, int + colour)
    + SCI_INDICGETFORE(int indicatorNumber)
    +
    + +

    SCI_INDICSETSTYLE(int indicatorNumber, int + indicatorStyle)
    + SCI_INDICGETSTYLE(int indicatorNumber)
    + These two messages set and get the style for a particular indicator. The indicator styles + currently available are:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SymbolValueVisual effect
    INDIC_PLAIN0Underlined with a single, straight line.
    INDIC_SQUIGGLE1A squiggly underline.
    INDIC_TT2A line of small T shapes.
    INDIC_DIAGONAL3Diagonal hatching.
    INDIC_STRIKE4Strike out.
    INDIC_HIDDEN5An indicator with no visual effect.
    INDIC_BOX6A rectangle around the text.
    INDIC_ROUNDBOX7A rectangle with rounded corners around the text using translucent drawing with the + interior more transparent than the border.
    + +

    The default indicator styles are equivalent to:
    + SCI_INDICSETSTYLE(0, INDIC_SQUIGGLE);
    + SCI_INDICSETSTYLE(1, INDIC_TT);
    + SCI_INDICSETSTYLE(2, INDIC_PLAIN);

    + +

    SCI_INDICSETFORE(int indicatorNumber, int colour)
    + SCI_INDICGETFORE(int indicatorNumber)
    + These two messages set and get the colour used to draw an indicator. The default indicator + colours are equivalent to:
    + SCI_INDICSETFORE(0, 0x007f00); (dark green)
    + SCI_INDICSETFORE(1, 0xff0000); (light blue)
    + SCI_INDICSETFORE(2, 0x0000ff); (light red)

    + +

    Autocompletion

    + +

    Autocompletion displays a list box showing likely identifiers based upon the user's typing. + The user chooses the currently selected item by pressing the tab character or another character + that is a member of the fillup character set defined with SCI_AUTOCSETFILLUPS. + Autocompletion is triggered by your application. For example, in C if you detect that the user + has just typed fred. you could look up fred, and if it has a known + list of members, you could offer them in an autocompletion list. Alternatively, you could + monitor the user's typing and offer a list of likely items once their typing has narrowed down + the choice to a reasonable list. As yet another alternative, you could define a key code to + activate the list.

    + +

    When the user makes a selection from the list the container is sent a SCN_AUTOCSELECTION notification message. On return from the notification Scintilla will insert + the selected text unless the autocompletion list has been cancelled, for example by the container sending + SCI_AUTOCCANCEL.

    + +

    To make use of autocompletion you must monitor each character added to the document. See + SciTEBase::CharAdded() in SciTEBase.cxx for an example of autocompletion.

    + SCI_AUTOCSHOW(int lenEntered, const char + *list)
    + SCI_AUTOCCANCEL
    + SCI_AUTOCACTIVE
    + SCI_AUTOCPOSSTART
    + SCI_AUTOCCOMPLETE
    + SCI_AUTOCSTOPS(<unused>, const char + *chars)
    + SCI_AUTOCSETSEPARATOR(char + separator)
    + SCI_AUTOCGETSEPARATOR
    + SCI_AUTOCSELECT(<unused>, const char + *select)
    + SCI_AUTOCGETCURRENT
    + SCI_AUTOCSETCANCELATSTART(bool + cancel)
    + SCI_AUTOCGETCANCELATSTART
    + SCI_AUTOCSETFILLUPS(<unused>, const char + *chars)
    + SCI_AUTOCSETCHOOSESINGLE(bool + chooseSingle)
    + SCI_AUTOCGETCHOOSESINGLE
    + SCI_AUTOCSETIGNORECASE(bool + ignoreCase)
    + SCI_AUTOCGETIGNORECASE
    + SCI_AUTOCSETAUTOHIDE(bool autoHide)
    + SCI_AUTOCGETAUTOHIDE
    + SCI_AUTOCSETDROPRESTOFWORD(bool + dropRestOfWord)
    + SCI_AUTOCGETDROPRESTOFWORD
    + SCI_REGISTERIMAGE
    + SCI_CLEARREGISTEREDIMAGES
    + SCI_AUTOCSETTYPESEPARATOR(char separatorCharacter)
    + SCI_AUTOCGETTYPESEPARATOR
    + SCI_AUTOCSETMAXHEIGHT(int rowCount)
    + SCI_AUTOCGETMAXHEIGHT
    + SCI_AUTOCSETMAXWIDTH(int characterCount)
    + SCI_AUTOCGETMAXWIDTH
    +
    + +

    SCI_AUTOCSHOW(int lenEntered, const char *list)
    + This message causes a list to be displayed. lenEntered is the number of + characters of the word already entered and list is the list of words separated by + separator characters. The initial separator character is a space but this can be set or got + with SCI_AUTOCSETSEPARATOR + and SCI_AUTOCGETSEPARATOR.

    + +

    The list of words should be in sorted order. If set to ignore case mode with SCI_AUTOCSETIGNORECASE, then + strings are matched after being converted to upper case. One result of this is that the list + should be sorted with the punctuation characters '[', '\', ']', '^', '_', and '`' sorted after + letters.

    + +

    SCI_AUTOCCANCEL
    + This message cancels any displayed autocompletion list. When in autocompletion mode, the list + should disappear when the user types a character that can not be part of the autocompletion, + such as '.', '(' or '[' when typing an identifier. A set of characters that will cancel + autocompletion can be specified with SCI_AUTOCSTOPS.

    + +

    SCI_AUTOCACTIVE
    + This message returns non-zero if there is an active autocompletion list and zero if there is + not.

    + +

    SCI_AUTOCPOSSTART
    + This returns the value of the current position when SCI_AUTOCSHOW started display + of the list.

    + +

    SCI_AUTOCCOMPLETE
    + This message triggers autocompletion. This has the same effect as the tab key.

    + +

    SCI_AUTOCSTOPS(<unused>, const char *chars)
    + The chars argument is a string containing a list of characters that will + automatically cancel the autocompletion list. When you start the editor, this list is + empty.

    + +

    SCI_AUTOCSETSEPARATOR(char separator)
    + SCI_AUTOCGETSEPARATOR
    + These two messages set and get the separator character used to separate words in the + SCI_AUTOCSHOW list. The default is the space character.

    + +

    SCI_AUTOCSELECT(<unused>, const char *select)
    + SCI_AUTOCGETCURRENT
    + This message selects an item in the autocompletion list. It searches the list of words for the + first that matches select. By default, comparisons are case sensitive, but you can + change this with SCI_AUTOCSETIGNORECASE. The match is character + by character for the length of the select string. That is, if select is "Fred" it + will match "Frederick" if this is the first item in the list that begins with "Fred". If an + item is found, it is selected. If the item is not found, the autocompletion list closes if + auto-hide is true (see SCI_AUTOCSETAUTOHIDE).
    + The current selection can be retrieved with SCI_AUTOCGETCURRENT +

    + +

    SCI_AUTOCSETCANCELATSTART(bool cancel)
    + SCI_AUTOCGETCANCELATSTART
    + The default behavior is for the list to be cancelled if the caret moves before the location it + was at when the list was displayed. By calling this message with a false argument, + the list is not cancelled until the caret moves before the first character of the word being + completed.

    + +

    SCI_AUTOCSETFILLUPS(<unused>, const char *chars)
    + If a fillup character is typed with an autocompletion list active, the currently selected item + in the list is added into the document, then the fillup character is added. Common fillup + characters are '(', '[' and '.' but others are possible depending on the language. By default, + no fillup characters are set.

    + +

    SCI_AUTOCSETCHOOSESINGLE(bool chooseSingle)
    + SCI_AUTOCGETCHOOSESINGLE
    + If you use SCI_AUTOCSETCHOOSESINGLE(1) and a list has only one item, it is + automatically added and no list is displayed. The default is to display the list even if there + is only a single item.

    + +

    SCI_AUTOCSETIGNORECASE(bool ignoreCase)
    + SCI_AUTOCGETIGNORECASE
    + By default, matching of characters to list members is case sensitive. These messages let you + set and get case sensitivity.

    + +

    SCI_AUTOCSETAUTOHIDE(bool autoHide)
    + SCI_AUTOCGETAUTOHIDE
    + By default, the list is cancelled if there are no viable matches (the user has typed + characters that no longer match a list entry). If you want to keep displaying the original + list, set autoHide to false. This also effects SCI_AUTOCSELECT.

    + +

    SCI_AUTOCSETDROPRESTOFWORD(bool dropRestOfWord)
    + SCI_AUTOCGETDROPRESTOFWORD
    + When an item is selected, any word characters following the caret are first erased if + dropRestOfWord is set true. The default is false.

    + +

    + SCI_REGISTERIMAGE(int type, const char *xpmData)
    + SCI_CLEARREGISTEREDIMAGES
    + SCI_AUTOCSETTYPESEPARATOR(char separatorCharacter)
    + SCI_AUTOCGETTYPESEPARATOR
    + + Autocompletion list items may display an image as well as text. Each image is first registered with an integer + type. Then this integer is included in the text of the list separated by a '?' from the text. For example, + "fclose?2 fopen" displays image 2 before the string "fclose" and no image before "fopen". + The images are in XPM format as is described for + SCI_MARKERDEFINEPIXMAP + The set of registered images can be cleared with SCI_CLEARREGISTEREDIMAGES and the '?' separator changed + with SCI_AUTOCSETTYPESEPARATOR. +

    + +

    + SCI_AUTOCSETMAXHEIGHT(int rowCount)
    + SCI_AUTOCGETMAXHEIGHT
    + + Get or set the maximum number of rows that will be visible in an autocompletion list. If there are more rows in the list, then a vertical + scrollbar is shown. The default is 5. +

    + +

    + SCI_AUTOCSETMAXWIDTH(int characterCount)
    + SCI_AUTOCGETMAXWIDTH
    + + Get or set the maximum width of an autocompletion list expressed as the number of characters in the longest item that will be totally visible. + If zero (the default) then the list's width is calculated to fit the item with the most characters. Any items that cannot be fully displayed within + the available width are indicated by the presence of ellipsis. +

    + +

    User lists

    + +

    User lists use the same internal mechanisms as autocompletion lists, and all the calls + listed for autocompletion work on them; you cannot display a user list at the same time as an + autocompletion list is active. They differ in the following respects:

    + +

    o The SCI_AUTOCSETCHOOSESINGLE message has no + effect.
    + o When the user makes a selection you are sent a SCN_USERLISTSELECTION notification message rather than SCN_AUTOCSELECTION.

    + +

    BEWARE: if you have set fillup characters or stop characters, these will still be active + with the user list, and may result in items being selected or the user list cancelled due to + the user typing into the editor.

    + +

    SCI_USERLISTSHOW(int listType, const char *list)
    + The listType parameter is returned to the container as the wParam + field of the SCNotification + structure. It must be greater than 0 as this is how Scintilla tells the difference between an + autocompletion list and a user list. If you have different types of list, for example a list of + buffers and a list of macros, you can use listType to tell which one has returned + a selection.

    + +

    Call tips

    + +

    Call tips are small windows displaying the arguments to a function and are displayed after + the user has typed the name of the function. They normally display characters using the font + facename, size and character set defined by + STYLE_DEFAULT. You can choose to + use STYLE_CALLTIP to define the + facename, size, foreground and background colours and character set with + SCI_CALLTIPUSESTYLE. + This also enables support for Tab characters. + + There is some interaction between call tips and autocompletion lists in that showing a + call tip cancels any active autocompletion list, and vice versa.

    + +

    Call tips can highlight part of the text within them. You could use this to highlight the + current argument to a function by counting the number of commas (or whatever separator your + language uses). See SciTEBase::CharAdded() in SciTEBase.cxx for an + example of call tip use.

    + +

    The mouse may be clicked on call tips and this causes a + SCN_CALLTIPCLICK + notification to be sent to the container. Small up an down arrows may be displayed within + a call tip by, respectively, including the characters '\001', or '\002'. This is useful + for showing that there are overloaded variants of one function name and that the user can + click on the arrows to cycle through the overloads.

    + +

    Alternatively, call tips can be displayed when you leave the mouse pointer for a while over + a word in response to the SCN_DWELLSTART notification and cancelled in response to SCN_DWELLEND. This method could be used in a debugger to give + the value of a variable, or during editing to give information about the word under the + pointer.

    + SCI_CALLTIPSHOW(int posStart, const char + *definition)
    + SCI_CALLTIPCANCEL
    + SCI_CALLTIPACTIVE
    + SCI_CALLTIPPOSSTART
    + SCI_CALLTIPSETHLT(int highlightStart, int + highlightEnd)
    + SCI_CALLTIPSETBACK(int colour)
    + SCI_CALLTIPSETFORE(int colour)
    + SCI_CALLTIPSETFOREHLT(int colour)
    + SCI_CALLTIPUSESTYLE(int tabsize)
    +
    + +

    SCI_CALLTIPSHOW(int posStart, const char *definition)
    + This message starts the process by displaying the call tip window. If a call tip is already + active, this has no effect.
    + posStart is the position in the document at which to align the call tip. The call + tip text is aligned to start 1 line below this character unless you have included up and/or + down arrows in the call tip text in which case the tip is aligned to the right-hand edge of + the rightmost arrow. The assumption is that you will start the text with something like + "\001 1 of 3 \002".
    + definition is the call tip text. This can contain multiple lines separated by + '\n' (Line Feed, ASCII code 10) characters. Do not include '\r' (Carriage Return, ASCII + code 13), as this will most likely print as an empty box. '\t' (Tab, ASCII code 9) is + supported if you set a tabsize with + SCI_CALLTIPUSESTYLE.

    + +

    SCI_CALLTIPCANCEL
    + This message cancels any displayed call tip. Scintilla will also cancel call tips for you if + you use any keyboard commands that are not compatible with editing the argument list of a + function.

    + +

    SCI_CALLTIPACTIVE
    + This returns 1 if a call tip is active and 0 if it is not active.

    + +

    SCI_CALLTIPPOSSTART
    + This message returns the value of the current position when SCI_CALLTIPSHOW + started to display the tip.

    + +

    SCI_CALLTIPSETHLT(int hlStart, int hlEnd)
    + This sets the region of the call tips text to display in a highlighted style. + hlStart is the zero-based index into the string of the first character to + highlight and hlEnd is the index of the first character after the highlight. + hlEnd must be greater than hlStart; hlEnd-hlStart is the + number of characters to highlight. Highlights can extend over line ends if this is + required.

    + +

    Unhighlighted text is drawn in a mid gray. Selected text is drawn in a dark blue. The + background is white. These can be changed with + SCI_CALLTIPSETBACK, + SCI_CALLTIPSETFORE, and + SCI_CALLTIPSETFOREHLT. +

    + +

    SCI_CALLTIPSETBACK(int colour)
    + The background colour of call tips can be set with this message; the default colour is white. + It is not a good idea to set a dark colour as the background as the default colour for normal + calltip text is mid gray and the defaultcolour for highlighted text is dark blue. This also + sets the background colour of STYLE_CALLTIP.

    + +

    SCI_CALLTIPSETFORE(int colour)
    + The colour of call tip text can be set with this message; the default colour is mid gray. + This also sets the foreground colour of STYLE_CALLTIP.

    + +

    SCI_CALLTIPSETFOREHLT(int colour)
    + The colour of highlighted call tip text can be set with this message; the default colour + is dark blue.

    + +

    SCI_CALLTIPUSESTYLE(int tabsize)
    + This message changes the style used for call tips from STYLE_DEFAULT to + STYLE_CALLTIP and sets a tab size in screen pixels. If tabsize is + less than 1, Tab characters are not treated specially. Once this call has been used, the + call tip foreground and background colours are also taken from the style.

    + + +

    Keyboard commands

    + +

    To allow the container application to perform any of the actions available to the user with + keyboard, all the keyboard actions are messages. They do not take any parameters. These + commands are also used when redefining the key bindings with the SCI_ASSIGNCMDKEY message.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SCI_LINEDOWNSCI_LINEDOWNEXTENDSCI_LINEDOWNRECTEXTENDSCI_LINESCROLLDOWN
    SCI_LINEUPSCI_LINEUPEXTENDSCI_LINEUPRECTEXTENDSCI_LINESCROLLUP
    SCI_PARADOWNSCI_PARADOWNEXTENDSCI_PARAUPSCI_PARAUPEXTEND
    SCI_CHARLEFTSCI_CHARLEFTEXTENDSCI_CHARLEFTRECTEXTEND
    SCI_CHARRIGHTSCI_CHARRIGHTEXTENDSCI_CHARRIGHTRECTEXTEND
    SCI_WORDLEFTSCI_WORDLEFTEXTENDSCI_WORDRIGHTSCI_WORDRIGHTEXTEND
    SCI_WORDLEFTENDSCI_WORDLEFTENDEXTENDSCI_WORDRIGHTENDSCI_WORDRIGHTENDEXTEND
    SCI_WORDPARTLEFTSCI_WORDPARTLEFTEXTENDSCI_WORDPARTRIGHTSCI_WORDPARTRIGHTEXTEND
    SCI_HOMESCI_HOMEEXTEND[SCI_HOMERECTEXTEND]
    SCI_HOMEDISPLAYSCI_HOMEDISPLAYEXTENDSCI_HOMEWRAPSCI_HOMEWRAPEXTEND
    SCI_VCHOMESCI_VCHOMEEXTENDSCI_VCHOMERECTEXTEND
    SCI_VCHOMEWRAPSCI_VCHOMEWRAPEXTEND
    SCI_LINEENDSCI_LINEENDEXTENDSCI_LINEENDRECTEXTEND
    SCI_LINEENDDISPLAYSCI_LINEENDDISPLAYEXTENDSCI_LINEENDWRAPSCI_LINEENDWRAPEXTEND
    SCI_DOCUMENTSTARTSCI_DOCUMENTSTARTEXTENDSCI_DOCUMENTENDSCI_DOCUMENTENDEXTEND
    SCI_PAGEUPSCI_PAGEUPEXTENDSCI_PAGEUPRECTEXTEND
    SCI_PAGEDOWNSCI_PAGEDOWNEXTENDSCI_PAGEDOWNRECTEXTEND
    SCI_STUTTEREDPAGEUPSCI_STUTTEREDPAGEUPEXTEND
    SCI_STUTTEREDPAGEDOWNSCI_STUTTEREDPAGEDOWNEXTEND
    SCI_DELETEBACKSCI_DELETEBACKNOTLINESCI_DELWORDLEFTSCI_DELWORDRIGHT
    SCI_DELLINELEFTSCI_DELLINERIGHTSCI_LINEDELETE
    SCI_LINECUTSCI_LINECOPYSCI_LINETRANSPOSESCI_LINEDUPLICATE
    SCI_LOWERCASESCI_UPPERCASESCI_CANCELSCI_EDITTOGGLEOVERTYPE
    SCI_NEWLINESCI_FORMFEEDSCI_TABSCI_BACKTAB
    SCI_SELECTIONDUPLICATE
    + +

    The SCI_*EXTEND messages extend the selection.

    + +

    The SCI_*RECTEXTEND messages extend the rectangular selection + (and convert regular selection to rectangular one, if any).

    + +

    The SCI_WORDPART* commands are used to move between word segments marked by + capitalisation (aCamelCaseIdentifier) or underscores (an_under_bar_ident).

    + +

    The SCI_HOME* commands move the caret to the start of the line, while the + SCI_VCHOME*commands move the caret to the first non-blank character of the line + (ie. just after the indentation) unless it is already there; in this case, it acts as SCI_HOME*.

    + +

    The SCI_[HOME|LINEEND]DISPLAY* commands are used when in line wrap mode to + allow movement to the start or end of display lines as opposed to the normal + SCI_[HOME|LINEEND] commands which move to the start or end of document lines.

    + +

    The SCI_[[VC]HOME|LINEEND]WRAP* commands are like their namesakes + SCI_[[VC]HOME|LINEEND]* except they behave differently when word-wrap is enabled: + They go first to the start / end of the display line, like SCI_[HOME|LINEEND]DISPLAY*, + but if the cursor is already at the point, it goes on to the start or end of the document line, + as appropriate for SCI_[[VC]HOME|LINEEND]*. +

    + +

    Key bindings

    + +

    There is a default binding of keys to commands that is defined in the Scintilla source in + the file KeyMap.cxx by the constant KeyMap::MapDefault[]. This table + maps key definitions to SCI_* messages with no parameters (mostly the keyboard commands discussed above, but any Scintilla + command that has no arguments can be mapped). You can change the mapping to suit your own + requirements.

    + SCI_ASSIGNCMDKEY(int keyDefinition, int + sciCommand)
    + SCI_CLEARCMDKEY(int keyDefinition)
    + SCI_CLEARALLCMDKEYS
    + SCI_NULL
    +
    + +

    keyDefinition
    + A key definition contains the key code in the low 16-bits and the key modifiers in the high + 16-bits. To combine keyCode and keyMod set:
    +
    + keyDefinition = keyCode + (keyMod << 16)

    + +

    The key code is a visible or control character or a key from the SCK_* + enumeration, which contains:
    + SCK_ADD, SCK_BACK, SCK_DELETE, SCK_DIVIDE, + SCK_DOWN, SCK_END, SCK_ESCAPE, SCK_HOME, + SCK_INSERT, SCK_LEFT, SCK_NEXT (Page Down), + SCK_PRIOR (Page Up), SCK_RETURN, SCK_RIGHT, + SCK_SUBTRACT, SCK_TAB, and SCK_UP.

    + +

    The modifiers are a combination of zero or more of SCMOD_ALT, + SCMOD_CTRL, and SCMOD_SHIFT. If you are building a table, you might + want to use SCMOD_NORM, which has the value 0, to mean no modifiers.

    + +

    SCI_ASSIGNCMDKEY(int keyDefinition, int sciCommand)
    + This assigns the given key definition to a Scintilla command identified by + sciCommand. sciCommand can be any SCI_* command that has + no arguments.

    + +

    SCI_CLEARCMDKEY(int keyDefinition)
    + This makes the given key definition do nothing by assigning the action SCI_NULL + to it.

    + +

    SCI_CLEARALLCMDKEYS
    + This command removes all keyboard command mapping by setting an empty mapping table.

    + +

    SCI_NULL
    + The SCI_NULL does nothing and is the value assigned to keys that perform no + action. SCI_NULL ensures that keys do not propagate to the parent window as that may + cause focus to move. If you want the standard platform behaviour use the constant 0 instead.

    + +

    Popup edit menu

    + +

    SCI_USEPOPUP(bool bEnablePopup)
    + Clicking the wrong button on the mouse pops up a short default editing menu. This may be + turned off with SCI_USEPOPUP(0). If you turn it off, context menu commands (in + Windows, WM_CONTEXTMENU) will not be handled by Scintilla, so the parent of the + Scintilla window will have the opportunity to handle the message.

    + +

    Macro recording

    + +

    Start and stop macro recording mode. In macro recording mode, actions are reported to the + container through SCN_MACRORECORD + notifications. It is then up to the container to + record these actions for future replay.

    + +

    SCI_STARTRECORD
    + SCI_STOPRECORD
    + These two messages turn macro recording on and off.

    + +

    Printing

    + +

    On Windows SCI_FORMATRANGE can be used to draw the text onto a display context + which can include a printer display context. Printed output shows text styling as on the + screen, but it hides all margins except a line number margin. All special marker effects are + removed and the selection and caret are hidden.

    + SCI_FORMATRANGE(bool bDraw, RangeToFormat + *pfr)
    + SCI_SETPRINTMAGNIFICATION(int + magnification)
    + SCI_GETPRINTMAGNIFICATION
    + SCI_SETPRINTCOLOURMODE(int mode)
    + SCI_GETPRINTCOLOURMODE
    + SCI_SETPRINTWRAPMODE
    + SCI_GETPRINTWRAPMODE
    +
    + +

    SCI_FORMATRANGE(bool bDraw, RangeToFormat *pfr)
    + This call allows Windows users to render a range of text into a device context. If you use + this for printing, you will probably want to arrange a page header and footer; Scintilla does + not do this for you. See SciTEWin::Print() in SciTEWinDlg.cxx for an + example. Each use of this message renders a range of text into a rectangular area and returns + the position in the document of the next character to print.

    + +

    bDraw controls if any output is done. Set this to false if you are paginating + (for example, if you use this with MFC you will need to paginate in + OnBeginPrinting() before you output each page.

    +
    +struct RangeToFormat {
    +    SurfaceID hdc;        // The HDC (device context) we print to
    +    SurfaceID hdcTarget;  // The HDC we use for measuring (may be same as hdc)
    +    PRectangle rc;        // Rectangle in which to print
    +    PRectangle rcPage;    // Physically printable page size
    +    CharacterRange chrg;  // Range of characters to print
    +};
    +
    + +

    hdc and hdcTarget should both be set to the device context handle + of the output device (usually a printer). If you print to a metafile these will not be the same + as Windows metafiles (unlike extended metafiles) do not implement the full API for returning + information. In this case, set hdcTarget to the screen DC.
    + rcPage is the rectangle {0, 0, maxX, maxY} where maxX+1 + and maxY+1 are the number of physically printable pixels in x and y.
    + rc is the rectangle to render the text in (which will, of course, fit within the + rectangle defined by rcPage).
    + chrg.cpMin and chrg.cpMax define the start position and maximum + position of characters to output. All of each line within this character range is drawn.

    + +

    When printing, the most tedious part is always working out what the margins should be to + allow for the non-printable area of the paper and printing a header and footer. If you look at + the printing code in SciTE, you will find that most of it is taken up with this. The loop that + causes Scintilla to render text is quite simple if you strip out all the margin, non-printable + area, header and footer code.

    + +

    SCI_SETPRINTMAGNIFICATION(int magnification)
    + SCI_GETPRINTMAGNIFICATION
    + SCI_GETPRINTMAGNIFICATION lets you to print at a different size than the screen + font. magnification is the number of points to add to the size of each screen + font. A value of -3 or -4 gives reasonably small print. You can get this value with + SCI_GETPRINTMAGNIFICATION.

    + +

    SCI_SETPRINTCOLOURMODE(int mode)
    + SCI_GETPRINTCOLOURMODE
    + These two messages set and get the method used to render coloured text on a printer that is + probably using white paper. It is especially important to consider the treatment of colour if + you use a dark or black screen background. Printing white on black uses up toner and ink very + many times faster than the other way around. You can set the mode to one of:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SymbolValuePurpose
    SC_PRINT_NORMAL0Print using the current screen colours. This is the default.
    SC_PRINT_INVERTLIGHT1If you use a dark screen background this saves ink by inverting the light value of + all colours and printing on a white background.
    SC_PRINT_BLACKONWHITE2Print all text as black on a white background.
    SC_PRINT_COLOURONWHITE3Everything prints in its own colour on a white background.
    SC_PRINT_COLOURONWHITEDEFAULTBG4Everything prints in its own colour on a white background except that line numbers + use their own background colour.
    + +

    SCI_SETPRINTWRAPMODE(int wrapMode)
    + SCI_GETPRINTWRAPMODE
    + These two functions get and set the printer wrap mode. wrapMode can be + set to SC_WRAP_NONE (0), SC_WRAP_WORD (1) or + SC_WRAP_CHAR (2). The default is + SC_WRAP_WORD, which wraps printed output so that all characters fit + into the print rectangle. If you set SC_WRAP_NONE, each line of text + generates one line of output and the line is truncated if it is too long to fit + into the print area.
    + SC_WRAP_WORD tries to wrap only between words as indicated by + white space or style changes although if a word is longer than a line, it will be wrapped before + the line end. SC_WRAP_CHAR is preferred to + SC_WRAP_WORD for Asian languages where there is no white space + between words.

    + +

    Direct access

    + SCI_GETDIRECTFUNCTION
    + SCI_GETDIRECTPOINTER
    +
    + +

    On Windows, the message-passing scheme used to communicate between the container and + Scintilla is mediated by the operating system SendMessage function and can lead to + bad performance when calling intensively. To avoid this overhead, Scintilla provides messages + that allow you to call the Scintilla message function directly. The code to do this in C/C++ is + of the form:

    +
    +#include "Scintilla.h"
    +SciFnDirect pSciMsg = (SciFnDirect)SendMessage(hSciWnd, SCI_GETDIRECTFUNCTION, 0, 0);
    +sptr_t pSciWndData = (sptr_t)SendMessage(hSciWnd, SCI_GETDIRECTPOINTER, 0, 0);
    +
    +// now a wrapper to call Scintilla directly
    +sptr_t CallScintilla(unsigned int iMessage, uptr_t wParam, sptr_t lParam){
    +    return pSciMsg(pSciWndData, iMessage, wParam, lParam);
    +}
    +
    + +

    SciFnDirect, sptr_t and uptr_t are declared in + Scintilla.h. hSciWnd is the window handle returned when you created + the Scintilla window.

    + +

    While faster, this direct calling will cause problems if performed from a different thread + to the native thread of the Scintilla window in which case SendMessage(hSciWnd, SCI_*, + wParam, lParam) should be used to synchronize with the window's thread.

    + +

    This feature also works on GTK+ but has no significant impact on speed.

    + +

    From version 1.47 on Windows, Scintilla exports a function called + Scintilla_DirectFunction that can be used the same as the function returned by + SCI_GETDIRECTFUNCTION. This saves you the call to + SCI_GETDIRECTFUNCTION and the need to call Scintilla indirectly via the function + pointer.

    + +

    SCI_GETDIRECTFUNCTION
    + This message returns the address of the function to call to handle Scintilla messages without + the overhead of passing through the Windows messaging system. You need only call this once, + regardless of the number of Scintilla windows you create.

    + +

    SCI_GETDIRECTPOINTER
    + This returns a pointer to data that identifies which Scintilla window is in use. You must call + this once for each Scintilla window you create. When you call the direct function, you must + pass in the direct pointer associated with the target window.

    + +

    Multiple views

    + +

    A Scintilla window and the document that it displays are separate entities. When you create + a new window, you also create a new, empty document. Each document has a reference count that + is initially set to 1. The document also has a list of the Scintilla windows that are linked to + it so when any window changes the document, all other windows in which it appears are notified + to cause them to update. The system is arranged in this way so that you can work with many + documents in a single Scintilla window and so you can display a single document in multiple + windows (for use with splitter windows).

    + +

    Although these messages use document *pDoc, to ensure compatibility with future + releases of Scintilla you should treat pDoc as an opaque void*. That + is, you can use and store the pointer as described in this section but you should not + dereference it.

    + SCI_GETDOCPOINTER
    + SCI_SETDOCPOINTER(<unused>, document + *pDoc)
    + SCI_CREATEDOCUMENT
    + SCI_ADDREFDOCUMENT(<unused>, document + *pDoc)
    + SCI_RELEASEDOCUMENT(<unused>, document + *pDoc)
    +
    + +

    SCI_GETDOCPOINTER
    + This returns a pointer to the document currently in use by the window. It has no other + effect.

    + +

    SCI_SETDOCPOINTER(<unused>, document *pDoc)
    + This message does the following:
    + 1. It removes the current window from the list held by the current document.
    + 2. It reduces the reference count of the current document by 1.
    + 3. If the reference count reaches 0, the document is deleted.
    + 4. pDoc is set as the new document for the window.
    + 5. If pDoc was 0, a new, empty document is created and attached to the + window.
    + 6. If pDoc was not 0, its reference count is increased by 1.

    + +

    SCI_CREATEDOCUMENT
    + This message creates a new, empty document and returns a pointer to it. This document is not + selected into the editor and starts with a reference count of 1. This means that you have + ownership of it and must either reduce its reference count by 1 after using + SCI_SETDOCPOINTER so that the Scintilla window owns it or you must make sure that + you reduce the reference count by 1 with SCI_RELEASEDOCUMENT before you close the + application to avoid memory leaks.

    + +

    SCI_ADDREFDOCUMENT(<unused>, document *pDoc)
    + This increases the reference count of a document by 1. If you want to replace the current + document in the Scintilla window and take ownership of the current document, for example if you + are editing many documents in one window, do the following:
    + 1. Use SCI_GETDOCPOINTER to get a pointer to the document, + pDoc.
    + 2. Use SCI_ADDREFDOCUMENT(0, pDoc) to increment the reference count.
    + 3. Use SCI_SETDOCPOINTER(0, pNewDoc) to set a different document or + SCI_SETDOCPOINTER(0, 0) to set a new, empty document.

    + +

    SCI_RELEASEDOCUMENT(<unused>, document *pDoc)
    + This message reduces the reference count of the document identified by pDoc. pDoc + must be the result of SCI_GETDOCPOINTER or SCI_CREATEDOCUMENT and + must point at a document that still exists. If you call this on a document with a reference + count of 1 that is still attached to a Scintilla window, bad things will happen. To keep the + world spinning in its orbit you must balance each call to SCI_CREATEDOCUMENT or + SCI_ADDREFDOCUMENT with a call to SCI_RELEASEDOCUMENT.

    + +

    Folding

    + +

    The fundamental operation in folding is making lines invisible or visible. Line visibility + is a property of the view rather than the document so each view may be displaying a different + set of lines. From the point of view of the user, lines are hidden and displayed using fold + points. Generally, the fold points of a document are based on the hierarchical structure of the + document contents. In Python, the hierarchy is determined by indentation and in C++ by brace + characters. This hierarchy can be represented within a Scintilla document object by attaching a + numeric "fold level" to each line. The fold level is most easily set by a lexer, but you can + also set it with messages.

    + +

    It is up to your code to set the connection between user actions and folding and unfolding. + The best way to see how this is done is to search the SciTE source code for the messages used + in this section of the documentation and see how they are used. You will also need to use + markers and a folding margin to complete your folding implementation. + The "fold" property should be set to "1" with + SCI_SETPROPERTY("fold", "1") to enable folding.

    + SCI_VISIBLEFROMDOCLINE(int + docLine)
    + SCI_DOCLINEFROMVISIBLE(int + displayLine)
    + SCI_SHOWLINES(int lineStart, int lineEnd)
    + SCI_HIDELINES(int lineStart, int lineEnd)
    + SCI_GETLINEVISIBLE(int line)
    + SCI_SETFOLDLEVEL(int line, int level)
    + SCI_GETFOLDLEVEL(int line)
    + SCI_SETFOLDFLAGS(int flags)
    + SCI_GETLASTCHILD(int line, int level)
    + SCI_GETFOLDPARENT(int line)
    + SCI_SETFOLDEXPANDED(int line, bool + expanded)
    + SCI_GETFOLDEXPANDED(int line)
    + SCI_TOGGLEFOLD(int line)
    + SCI_ENSUREVISIBLE(int line)
    + SCI_ENSUREVISIBLEENFORCEPOLICY(int + line)
    +
    + +

    SCI_VISIBLEFROMDOCLINE(int docLine)
    + When some lines are folded, then a particular line in the document may be displayed at a + different position to its document position. If no lines are folded, this message returns + docLine. Otherwise, this returns the display line (counting the very first visible + line as 0). The display line of an invisible line is the same as the previous visible line. The + display line number of the first line in the document is 0. If there is folding and + docLine is outside the range of lines in the document, the return value is -1. + Lines can occupy more than one display line if they wrap.

    + +

    SCI_DOCLINEFROMVISIBLE(int displayLine)
    + When some lines are hidden, then a particular line in the document may be displayed at a + different position to its document position. This message returns the document line number that + corresponds to a display line (counting the display line of the first line in the document as + 0). If displayLine is less than or equal to 0, the result is 0. If + displayLine is greater than or equal to the number of displayed lines, the result + is the number of lines in the document.

    + +

    SCI_SHOWLINES(int lineStart, int lineEnd)
    + SCI_HIDELINES(int lineStart, int lineEnd)
    + SCI_GETLINEVISIBLE(int line)
    + The first two messages mark a range of lines as visible or invisible and then redraw the + display. The third message reports on the visible state of a line and returns 1 if it is + visible and 0 if it is not visible. These messages have no effect on fold levels or fold + flags.

    + +

    SCI_SETFOLDLEVEL(int line, int level)
    + SCI_GETFOLDLEVEL(int line)
    + These two messages set and get a 32-bit value that contains the fold level of a line and some + flags associated with folding. The fold level is a number in the range 0 to + SC_FOLDLEVELNUMBERMASK (4095). However, the initial fold level is set to + SC_FOLDLEVELBASE (1024) to allow unsigned arithmetic on folding levels. There are + two addition flag bits. SC_FOLDLEVELWHITEFLAG indicates that the line is blank and + allows it to be treated slightly different then its level may indicate. For example, blank + lines should generally not be fold points and will be considered part of the preceding section even though + they may have a lesser fold level. + SC_FOLDLEVELHEADERFLAG indicates that + the line is a header (fold point).

    + +

    Use SCI_GETFOLDLEVEL(line) & SC_FOLDLEVELNUMBERMASK to get the fold level + of a line. Likewise, use SCI_GETFOLDLEVEL(line) & SC_FOLDLEVEL*FLAG to get the + state of the flags. To set the fold level you must or in the associated flags. For instance, to + set the level to thisLevel and mark a line as being a fold point use: + SCI_SETFOLDLEVEL(line, thisLevel | SC_FOLDLEVELHEADERFLAG).

    + If you use a lexer, you should not need to use SCI_SETFOLDLEVEL as this is far + better handled by the lexer. You will need to use SCI_GETFOLDLEVEL to decide how + to handle user folding requests. If you do change the fold levels, the folding margin will + update to match your changes. + +

    SCI_SETFOLDFLAGS(int flags)
    + In addition to showing markers in the folding margin, you can indicate folds to the user by + drawing lines in the text area. The lines are drawn in the foreground colour set for STYLE_DEFAULT. Bits set in + flags determine where folding lines are drawn:
    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ValueEffect
    1Experimental - draw boxes if expanded
    2Draw above if expanded
    4Draw above if not expanded
    8Draw below if expanded
    16Draw below if not expanded
    64display hexadecimal fold levels in line margin to aid debugging of + folding. This feature needs to be redesigned to be sensible.
    + +

    This message causes the display to redraw.

    + +

    SCI_GETLASTCHILD(int startLine, int level)
    + This message searches for the next line after startLine, that has a folding level + that is less than or equal to level and then returns the previous line number. If + you set level to -1, level is set to the folding level of line + startLine. If from is a fold point, SCI_GETLASTCHILD(from, + -1) returns the last line that would be in made visible or hidden by toggling the fold + state.

    + +

    SCI_GETFOLDPARENT(int startLine)
    + This message returns the line number of the first line before startLine that is + marked as a fold point with SC_FOLDLEVELHEADERFLAG and has a fold level less than + the startLine. If no line is found, or if the header flags and fold levels are + inconsistent, the return value is -1.

    + +

    SCI_TOGGLEFOLD(int line)
    + Each fold point may be either expanded, displaying all its child lines, or contracted, hiding + all the child lines. This message toggles the folding state of the given line as long as it has + the SC_FOLDLEVELHEADERFLAG set. This message takes care of folding or expanding + all the lines that depend on the line. The display updates after this message.

    + +

    SCI_SETFOLDEXPANDED(int line, bool expanded)
    + SCI_GETFOLDEXPANDED(int line)
    + These messages set and get the expanded state of a single line. The set message has no effect + on the visible state of the line or any lines that depend on it. It does change the markers in + the folding margin. If you ask for the expansion state of a line that is outside the document, + the result is false (0).

    + +

    If you just want to toggle the fold state of one line and handle all the lines that are + dependent on it, it is much easier to use SCI_TOGGLEFOLD. You would use the + SCI_SETFOLDEXPANDED message to process many folds without updating the display + until you had finished. See SciTEBase::FoldAll() and + SciTEBase::Expand() for examples of the use of these messages.

    + +

    SCI_ENSUREVISIBLE(int line)
    + SCI_ENSUREVISIBLEENFORCEPOLICY(int line)
    + A line may be hidden because more than one of its parent lines is contracted. Both these + message travels up the fold hierarchy, expanding any contracted folds until they reach the top + level. The line will then be visible. If you use SCI_ENSUREVISIBLEENFORCEPOLICY, + the vertical caret policy set by SCI_SETVISIBLEPOLICY is then applied.

    + +

    Line wrapping

    + + SCI_SETWRAPMODE(int wrapMode)
    + SCI_GETWRAPMODE
    + SCI_SETWRAPVISUALFLAGS(int wrapVisualFlags)
    + SCI_GETWRAPVISUALFLAGS
    + SCI_SETWRAPSTARTINDENT(int indent)
    + SCI_GETWRAPSTARTINDENT
    + SCI_SETLAYOUTCACHE(int cacheMode)
    + SCI_GETLAYOUTCACHE
    + SCI_LINESSPLIT(int pixelWidth)
    + SCI_LINESJOIN
    + SCI_WRAPCOUNT(int docLine)
    +
    + +

    By default, Scintilla does not wrap lines of text. If you enable line wrapping, lines wider + than the window width are continued on the following lines. Lines are broken after space or tab + characters or between runs of different styles. If this is not possible because a word in one + style is wider than the window then the break occurs after the last character that completely + fits on the line. The horizontal scroll bar does not appear when wrap mode is on.

    + +

    For wrapped lines Scintilla can draw visual flags (little arrows) at end of a a subline of a + wrapped line and at begin of the next subline. These can be enabled individually, but if Scintilla + draws the visual flag at begin of the next subline this subline will be indented by one char. + Independent from drawing a visual flag at the begin the subline can have an indention.

    + +

    Much of the time used by Scintilla is spent on laying out and drawing text. The same text + layout calculations may be performed many times even when the data used in these calculations + does not change. To avoid these unnecessary calculations in some circumstances, the line layout + cache can store the results of the calculations. The cache is invalidated whenever the + underlying data, such as the contents or styling of the document changes. Caching the layout of + the whole document has the most effect, making dynamic line wrap as much as 20 times faster but + this requires 7 times the memory required by the document contents plus around 80 bytes per + line.

    + +

    Wrapping is not performed immediately there is a change but is delayed until the display + is redrawn. This delay improves peformance by allowing a set of changes to be performed + and then wrapped and displayed once. Because of this, some operations may not occur as + expected. If a file is read and the scroll position moved to a particular line in the text, + such as occurs when a container tries to restore a previous editing session, then + the scroll position will have been determined before wrapping so an unexpected range + of text will be displayed. To scroll to the position correctly, delay the scroll until the + wrapping has been performed by waiting for an initial + SCN_PAINTED notification.

    + +

    SCI_SETWRAPMODE(int wrapMode)
    + SCI_GETWRAPMODE
    + Set wrapMode to SC_WRAP_WORD (1) to enable wrapping + on word boundaries, SC_WRAP_CHAR (2) to enable wrapping + between any characters, and to SC_WRAP_NONE (0) to disable line + wrapping. SC_WRAP_CHAR is preferred to + SC_WRAP_WORD for Asian languages where there is no white space + between words.

    + + +

    SCI_SETWRAPVISUALFLAGS(int wrapVisualFlags)
    + SCI_GETWRAPVISUALFLAGS
    + You can enable the drawing of visual flags to indicate a line is wrapped. Bits set in + wrapVisualFlags determine which visual flags are drawn. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SymbolValueEffect
    SC_WRAPVISUALFLAG_NONE0No visual flags
    SC_WRAPVISUALFLAG_END1Visual flag at end of subline of a wrapped line.
    SC_WRAPVISUALFLAG_START2Visual flag at begin of subline of a wrapped line.
    + Subline is indented by at least 1 to make room for the flag.
    +
    + +

    SCI_SETWRAPVISUALFLAGSLOCATION(int wrapVisualFlagsLocation)
    + SCI_GETWRAPVISUALFLAGSLOCATION
    + You can set wether the visual flags to indicate a line is wrapped are drawn near the border or near the text. + Bits set in wrapVisualFlagsLocation set the location to near the text for the corresponding visual flag. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SymbolValueEffect
    SC_WRAPVISUALFLAGLOC_DEFAULT0Visual flags drawn near border
    SC_WRAPVISUALFLAGLOC_END_BY_TEXT1Visual flag at end of subline drawn near text
    SC_WRAPVISUALFLAGLOC_START_BY_TEXT2Visual flag at begin of subline drawn near text
    + +
    + +

    SCI_SETWRAPSTARTINDENT(int indent)
    + SCI_GETWRAPSTARTINDENT
    + SCI_SETWRAPSTARTINDENT sets the size of indentation of sublines for + wrapped lines in terms of the width of a space in + STYLE_DEFAULT. + There are no limits on indent sizes, but values less than 0 or large values may have + undesirable effects.
    + The indention of sublines is independent of visual flags, but if + SC_WRAPVISUALFLAG_START is set an indent of at least 1 is used. +

    + +

    SCI_SETLAYOUTCACHE(int cacheMode)
    + SCI_GETLAYOUTCACHE
    + You can set cacheMode to one of the symbols in the table:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SymbolValueLayout cached for these lines
    SC_CACHE_NONE0No lines are cached.
    SC_CACHE_CARET1The line containing the text caret. This is the default.
    SC_CACHE_PAGE2Visible lines plus the line containing the caret.
    SC_CACHE_DOCUMENT3All lines in the document.
    +
    + +

    SCI_LINESSPLIT(int pixelWidth)
    + Split a range of lines indicated by the target into lines that are at most pixelWidth wide. + Splitting occurs on word boundaries wherever possible in a similar manner to line wrapping. + When pixelWidth is 0 then the width of the window is used. +

    + +

    SCI_LINESJOIN
    + Join a range of lines indicated by the target into one line by + removing line end characters. + Where this would lead to no space between words, an extra space is inserted. +

    + +

    SCI_WRAPCOUNT(int docLine)
    + Document lines can occupy more than one display line if they wrap and this + returns the number of display lines needed to wrap a document line.

    + +

    Zooming

    + +

    Scintilla incorporates a "zoom factor" that lets you make all the text in the document + larger or smaller in steps of one point. The displayed point size never goes below 2, whatever + zoom factor you set. You can set zoom factors in the range -10 to +20 points.

    + SCI_ZOOMIN
    + SCI_ZOOMOUT
    + SCI_SETZOOM(int zoomInPoints)
    + SCI_GETZOOM
    +
    + +

    SCI_ZOOMIN
    + SCI_ZOOMOUT
    + SCI_ZOOMIN increases the zoom factor by one point if the current zoom factor is + less than 20 points. SCI_ZOOMOUT decreases the zoom factor by one point if the + current zoom factor is greater than -10 points.

    + +

    SCI_SETZOOM(int zoomInPoints)
    + SCI_GETZOOM
    + These messages let you set and get the zoom factor directly. There is no limit set on the + factors you can set, so limiting yourself to -10 to +20 to match the incremental zoom functions + is a good idea.

    + +

    Long lines

    + +

    You can choose to mark lines that exceed a given length by drawing a vertical line or by + colouring the background of characters that exceed the set length.

    + SCI_SETEDGEMODE(int mode)
    + SCI_GETEDGEMODE
    + SCI_SETEDGECOLUMN(int column)
    + SCI_GETEDGECOLUMN
    + SCI_SETEDGECOLOUR(int colour)
    + SCI_GETEDGECOLOUR
    +
    + +

    SCI_SETEDGEMODE(int edgeMode)
    + SCI_GETEDGEMODE
    + These two messages set and get the mode used to display long lines. You can set one of the + values in the table:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SymbolValueLong line display mode
    EDGE_NONE0Long lines are not marked. This is the default state.
    EDGE_LINE1A vertical line is drawn at the column number set by SCI_SETEDGECOLUMN. + This works well for monospaced fonts. The line is drawn at a position based on the width + of a space character in STYLE_DEFAULT, so it may not work very well if + your styles use proportional fonts or if your style have varied font sizes or you use a + mixture of bold, italic and normal text. .
    EDGE_BACKGROUND2The background colour of characters after the column limit is changed to the colour + set by SCI_SETEDGECOLOUR. This is recommended for proportional fonts.
    +
    +
    + + +

    SCI_SETEDGECOLUMN(int column)
    + SCI_GETEDGECOLUMN
    + These messages set and get the column number at which to display the long line marker. When + drawing lines, the column sets a position in units of the width of a space character in + STYLE_DEFAULT. When setting the background colour, the column is a character count + (allowing for tabs) into the line.

    + +

    SCI_SETEDGECOLOUR(int colour)
    + SCI_GETEDGECOLOUR
    + These messages set and get the colour of the marker used to show that a line has exceeded the + length set by SCI_SETEDGECOLUMN.

    + +

    Lexer

    + +

    If you define the symbol SCI_LEXER when building Scintilla, (this is sometimes + called the SciLexer version of Scintilla), lexing support for a wide range programming + languages is included and the messages in this section are supported. If you want to set + styling and fold points for an unsupported language you can either do this in the container or + better still, write your own lexer following the pattern of one of the existing ones.

    + +

    Scintilla also supports external lexers. These are DLLs (on Windows) or .so modules (on GTK+/Linux) that export four + functions: GetLexerCount, GetLexerName, Lex and + Fold. See externalLexer.cxx for more.

    + SCI_SETLEXER(int lexer)
    + SCI_GETLEXER
    + SCI_SETLEXERLANGUAGE(<unused>, char + *name)
    + SCI_LOADLEXERLIBRARY(<unused>, char + *path)
    + SCI_COLOURISE(int start, int end)
    + SCI_SETPROPERTY(const char *key, const char *value)
    + SCI_GETPROPERTY(const char *key, char *value)
    + SCI_GETPROPERTYEXPANDED(const char *key, char *value)
    + SCI_GETPROPERTYINT(const char *key, int default)
    + SCI_SETKEYWORDS(int keyWordSet, const char + *keyWordList)
    + SCI_GETSTYLEBITSNEEDED +
    +
    + +

    SCI_SETLEXER(int lexer)
    + SCI_GETLEXER
    + You can select the lexer to use with an integer code from the SCLEX_* enumeration + in Scintilla.h. There are two codes in this sequence that do not use lexers: + SCLEX_NULL to select no lexing action and SCLEX_CONTAINER which sends + the SCN_STYLENEEDED notification to + the container whenever a range of text needs to be styled. You cannot use the + SCLEX_AUTOMATIC value; this identifies additional external lexers that Scintilla + assigns unused lexer numbers to.

    + +

    SCI_SETLEXERLANGUAGE(<unused>, const char *name)
    + This message lets you select a lexer by name, and is the only method if you are using an + external lexer or if you have written a lexer module for a language of your own and do not wish + to assign it an explicit lexer number. To select an existing lexer, set name to + match the (case sensitive) name given to the module, for example "ada" or "python", not "Ada" + or "Python". To locate the name for the built-in lexers, open the relevant + Lex*.cxx file and search for LexerModule. The third argument in the + LexerModule constructor is the name to use.

    + +

    To test if your lexer assignment worked, use SCI_GETLEXER before and after setting the new lexer to + see if the lexer number changed.

    + +

    SCI_LOADLEXERLIBRARY(<unused>, const char *path)
    + Load a lexer implemented in a shared library. This is a .so file on GTK+/Linux or a .DLL file on Windows. +

    + +

    SCI_COLOURISE(int startPos, int endPos)
    + This requests the current lexer or the container (if the lexer is set to + SCLEX_CONTAINER) to style the document between startPos and + endPos. If endPos is -1, the document is styled from + startPos to the end. If the "fold" property is set to + "1" and your lexer or container supports folding, fold levels are also set. This + message causes a redraw.

    + +

    SCI_SETPROPERTY(const char *key, const char *value)
    + You can communicate settings to lexers with keyword:value string pairs. There is no limit to + the number of keyword pairs you can set, other than available memory. key is a + case sensitive keyword, value is a string that is associated with the keyword. If + there is already a value string associated with the keyword, it is replaced. If you pass a zero + length string, the message does nothing. Both key and value are used + without modification; extra spaces at the beginning or end of key are + significant.

    + +

    The value string can refer to other keywords. For example, + SCI_SETPROPERTY("foldTimes10", "$(fold)0") stores the string + "$(fold)0", but when this is accessed, the $(fold) is replaced by the + value of the "fold" keyword (or by nothing if this keyword does not exist).

    + +

    Currently the "fold" property is defined for most of the lexers to set the fold structure if + set to "1". SCLEX_PYTHON understands "tab.timmy.whinge.level" as a + setting that determines how to indicate bad indentation. Most keywords have values that are + interpreted as integers. Search the lexer sources for GetPropertyInt to see how + properties are used.

    + +

    SCI_GETPROPERTY(const char *key, char *value)
    + Lookup a keyword:value pair using the specified key; if found, copy the value to the user-supplied + buffer and return the length (not including the terminating 0). If not found, copy an empty string + to the buffer and return 0.

    + +

    Note that "keyword replacement" as described in + SCI_SETPROPERTY will not be performed.

    + +

    If the value argument is 0 then the length that should be allocated to store the value is returned; + again, the terminating 0 is not included.

    + +

    SCI_GETPROPERTYEXPANDED(const char *key, char *value)
    + Lookup a keyword:value pair using the specified key; if found, copy the value to the user-supplied + buffer and return the length (not including the terminating 0). If not found, copy an empty string + to the buffer and return 0.

    + +

    Note that "keyword replacement" as described in + SCI_SETPROPERTY will be performed.

    + +

    If the value argument is 0 then the length that should be allocated to store the value (including any indicated keyword replacement) + is returned; again, the terminating 0 is not included.

    + +

    SCI_GETPROPERTYINT(const char *key, int default)
    + Lookup a keyword:value pair using the specified key; if found, interpret the value as an integer and return it. + If not found (or the value is an empty string) then return the supplied default. If the keyword:value pair is found but is not + a number, then return 0.

    + +

    Note that "keyword replacement" as described in + SCI_SETPROPERTY will be performed before any numeric interpretation.

    + +

    SCI_SETKEYWORDS(int keyWordSet, const char *keyWordList)
    + You can set up to 9 lists of keywords for use by the current lexer. This was increased from 6 + at revision 1.50. keyWordSet can be 0 to 8 (actually 0 to KEYWORDSET_MAX) + and selects which keyword list to replace. keyWordList is a list of keywords + separated by spaces, tabs, "\n" or "\r" or any combination of these. + It is expected that the keywords will be composed of standard ASCII printing characters, + but there is nothing to stop you using any non-separator character codes from 1 to 255 + (except common sense).

    + +

    How these keywords are used is entirely up to the lexer. Some languages, such as HTML may + contain embedded languages, VBScript and JavaScript are common for HTML. For HTML, key word set + 0 is for HTML, 1 is for JavaScript and 2 is for VBScript, 3 is for Python, 4 is for PHP and 5 + is for SGML and DTD keywords. Review the lexer code to see examples of keyword list. A fully + conforming lexer sets the fourth argument of the LexerModule constructor to be a + list of strings that describe the uses of the keyword lists.

    + +

    Alternatively, you might use set 0 for general keywords, set 1 for keywords that cause + indentation and set 2 for keywords that cause unindentation. Yet again, you might have a simple + lexer that colours keywords and you could change languages by changing the keywords in set 0. + There is nothing to stop you building your own keyword lists into the lexer, but this means + that the lexer must be rebuilt if more keywords are added.

    + +

    SCI_GETSTYLEBITSNEEDED
    + Retrieve the number of bits the current lexer needs for styling. This should normally be the argument + to SCI_SETSTYLEBITS. +

    + +

    Notifications

    + +

    Notifications are sent (fired) from the Scintilla control to its container when an event has + occurred that may interest the container. Notifications are sent using the + WM_NOTIFY message on Windows and the "notify" signal on GTK+. The container is + passed a SCNotification structure containing information about the event.

    +
    +struct NotifyHeader {   // This matches the Win32 NMHDR structure
    +    void *hwndFrom;     // environment specific window handle/pointer
    +    uptr_t idFrom;	// CtrlID of the window issuing the notification
    +    unsigned int code;  // The SCN_* notification code
    +};
    +
    +struct SCNotification {
    +    struct NotifyHeader nmhdr;
    +    int position;
    +    // SCN_STYLENEEDED, SCN_DOUBLECLICK, SCN_MODIFIED, SCN_DWELLSTART,
    +    // SCN_DWELLEND, SCN_CALLTIPCLICK,
    +    // SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK
    +    int ch;             // SCN_CHARADDED, SCN_KEY
    +    int modifiers;      // SCN_KEY, SCN_HOTSPOTCLICK, SCN_HOTSPOTDOUBLECLICK
    +    int modificationType; // SCN_MODIFIED
    +    const char *text;   // SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_AUTOCSELECTION
    +    int length;         // SCN_MODIFIED
    +    int linesAdded;     // SCN_MODIFIED
    +    int message;        // SCN_MACRORECORD
    +    uptr_t wParam;      // SCN_MACRORECORD
    +    sptr_t lParam;      // SCN_MACRORECORD
    +    int line;           // SCN_MODIFIED, SCN_DOUBLECLICK
    +    int foldLevelNow;   // SCN_MODIFIED
    +    int foldLevelPrev;  // SCN_MODIFIED
    +    int margin;         // SCN_MARGINCLICK
    +    int listType;       // SCN_USERLISTSELECTION, SCN_AUTOCSELECTION
    +    int x;              // SCN_DWELLSTART, SCN_DWELLEND
    +    int y;              // SCN_DWELLSTART, SCN_DWELLEND
    +};
    +
    + +

    The notification messages that your container can choose to handle and the messages + associated with them are:

    + SCN_STYLENEEDED
    + SCN_CHARADDED
    + SCN_SAVEPOINTREACHED
    + SCN_SAVEPOINTLEFT
    + SCN_MODIFYATTEMPTRO
    + SCN_KEY
    + SCN_DOUBLECLICK
    + SCN_UPDATEUI
    + SCN_MODIFIED
    + SCN_MACRORECORD
    + SCN_MARGINCLICK
    + SCN_NEEDSHOWN
    + SCN_PAINTED
    + SCN_USERLISTSELECTION
    + SCN_URIDROPPED
    + SCN_DWELLSTART
    + SCN_DWELLEND
    + SCN_ZOOM
    + SCN_HOTSPOTCLICK
    + SCN_HOTSPOTDOUBLECLICK
    + SCN_CALLTIPCLICK
    + SCN_AUTOCSELECTION
    +
    + +

    The following SCI_* messages are associated with these notifications:

    + SCI_SETMODEVENTMASK(int + eventMask)
    + SCI_GETMODEVENTMASK
    + SCI_SETMOUSEDWELLTIME
    + SCI_GETMOUSEDWELLTIME
    +
    + +

    The following additional notifications are sent using the WM_COMMAND message on + Windows and the "Command" signal on GTK+. This emulates the Windows Edit control. Only the lower + 16 bits of the control's ID is passed in these notifications.

    + SCEN_CHANGE
    + SCEN_SETFOCUS
    + SCEN_KILLFOCUS
    +
    + +

    SCN_STYLENEEDED
    + If you used SCI_SETLEXER(SCLEX_CONTAINER) to make the container act as the + lexer, you will receive this notification when Scintilla is about to display or print text that + requires styling. You are required to style the text from the line that contains the position + returned by SCI_GETENDSTYLED up to + the position passed in SCNotification.position. Symbolically, you need code of the + form:

    +
    +    startPos = SCI_GETENDSTYLED()
    +    lineNumber = SCI_LINEFROMPOSITION(startPos);
    +    startPos = SCI_POSITIONFROMLINE(lineNumber);
    +    MyStyleRoutine(startPos, SCNotification.position);
    +
    + +

    SCN_CHARADDED
    + This is sent when the user types an ordinary text character (as opposed to a command + character) that is entered into the text. The container can use this to decide to display a call tip or an auto + completion list. The character is in SCNotification.ch. + This notification is sent before the character has been styled so processing that depends on + styling should instead be performed in the SCN_UPDATEUI notification.

    + +

    SCN_SAVEPOINTREACHED
    + SCN_SAVEPOINTLEFT
    + Sent to the container when the save point is entered or left, allowing the container to + display a "document dirty" indicator and change its menus.
    + See also: SCI_SETSAVEPOINT, SCI_GETMODIFY

    + +

    SCN_MODIFYATTEMPTRO
    + When in read-only mode, this notification is sent to the container if the user tries to change + the text. This can be used to check the document out of a version control system. You can set + the read-only state of a document with SCI_SETREADONLY.

    + +

    SCN_KEY
    + Reports all keys pressed but not consumed by Scintilla. Used on GTK+ because of + some problems with keyboard focus and is not sent by the Windows version. SCNotification.ch holds the key code and + SCNotification.modifiers holds the modifiers. This notification is sent if the + modifiers include SCMOD_ALT or SCMOD_CTRL and the key code is less + than 256.

    + +

    SCN_DOUBLECLICK
    + The mouse button was double clicked in editor. The position field is set to the text position of the + double click and the line field is set to the line of the double click.

    + +

    SCN_UPDATEUI
    + Either the text or styling of the document has changed or the selection range has changed. Now + would be a good time to update any container UI elements that depend on document or view state. + This was previously called SCN_CHECKBRACE because a common use is to check whether the + caret is next to a brace and set highlights on this brace and its corresponding matching brace. + This also replaces SCN_POSCHANGED, + which is now deprecated.

    + +

    SCN_MODIFIED
    + This notification is sent when the text or styling of the document changes or is about to + change. You can set a mask for the notifications that are sent to the container with SCI_SETMODEVENTMASK. The + notification structure contains information about what changed, how the change occurred and + whether this changed the number of lines in the document. No modifications may be performed + while in a SCN_MODIFIED event. The SCNotification fields used + are:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldUsage
    modificationTypeA set of flags that identify the change(s) made. See the next + table.
    positionStart position of a text or styling change. Set to 0 if not used.
    lengthLength of the change in cells or characters when the text or styling + changes. Set to 0 if not used.
    linesAddedNumber of added lines. If negative, the number of deleted lines. Set to + 0 if not used or no lines added or deleted.
    textValid for text changes, not for style changes. If we are collecting undo + information this holds a pointer to the text that is handed to the Undo system, otherwise + it is zero. For user performed SC_MOD_BEFOREDELETE the text field is 0 and + for user performed SC_MOD_BEFOREINSERT the text field points to an array of cells, + not bytes and the length is the number of cells.
    lineThe line number at which a fold level or marker change occurred. This is + 0 if unused and may be -1 if more than one line changed.
    foldLevelNowThe new fold level applied to the line or 0 if this field is + unused.
    foldLevelPrevThe previous folding level of the line or 0 if this field is + unused.
    + +

    The SCNotification.modificationType field has bits set to tell you what has + been done. The SC_MOD_* bits correspond to actions. The + SC_PERFORMED_* bits tell you if the action was done by the user, or the result of + Undo or Redo of a previous action.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SymbolValueMeaningSCNotification fields
    SC_MOD_INSERTTEXT0x01Text has been inserted into the document.position, length, text, linesAdded
    SC_MOD_DELETETEXT0x02Text has been removed from the document.position, length, text, linesAdded
    SC_MOD_CHANGESTYLE0x04A style change has occurred.position, length
    SC_MOD_CHANGEFOLD0x08A folding change has occurred.line, foldLevelNow, foldLevelPrev
    SC_PERFORMED_USER0x10Information: the operation was done by the user.None
    SC_PERFORMED_UNDO0x20Information: this was the result of an Undo.None
    SC_PERFORMED_REDO0x40Information: this was the result of a Redo.None
    SC_MULTISTEPUNDOREDO0x80This is part of a multi-step Undo or Redo.None
    SC_LASTSTEPINUNDOREDO0x100This is the final step in an Undo or Redo.None
    SC_MOD_CHANGEMARKER0x200One or more markers has changed in a line.line
    SC_MOD_BEFOREINSERT0x400Text is about to be inserted into the document.position, if performed by user then text in cells, length in cells
    SC_MOD_BEFOREDELETE0x800Text is about to be deleted from the document.position, length
    SC_MULTILINEUNDOREDO0x1000This is part of an Undo or Redo with multi-line changes.None
    SC_MODEVENTMASKALL0x1fffThis is a mask for all valid flags. This is the default mask state set by SCI_SETMODEVENTMASK.None
    + +

    SCEN_CHANGE
    + SCEN_CHANGE (768) is fired when the text (not the style) of the document changes. + This notification is sent using the WM_COMMAND message on Windows and the + "Command" signal on GTK+ as this is the behavior of the standard Edit control + (SCEN_CHANGE has the same value as the Windows Edit control + EN_CHANGE). No other information is sent. If you need more detailed information + use SCN_MODIFIED. You can filter the + types of changes you are notified about with SCI_SETMODEVENTMASK.

    + +

    SCI_SETMODEVENTMASK(int eventMask)
    + SCI_GETMODEVENTMASK
    + These messages set and get an event mask that determines which document change events are + notified to the container with SCN_MODIFIED and SCEN_CHANGE. For example, a container may decide to see + only notifications about changes to text and not styling changes by calling + SCI_SETMODEVENTMASK(SC_MOD_INSERTTEXT|SC_MOD_DELETETEXT).

    + +

    The possible notification types are the same as the modificationType bit flags + used by SCN_MODIFIED: SC_MOD_INSERTTEXT, + SC_MOD_DELETETEXT, SC_MOD_CHANGESTYLE, + SC_MOD_CHANGEFOLD, SC_PERFORMED_USER, SC_PERFORMED_UNDO, + SC_PERFORMED_REDO, SC_MULTISTEPUNDOREDO, + SC_LASTSTEPINUNDOREDO, SC_MOD_CHANGEMARKER, + SC_MOD_BEFOREINSERT, SC_MOD_BEFOREDELETE, + SC_MULTILINEUNDOREDO, and SC_MODEVENTMASKALL.

    + +

    SCEN_SETFOCUS
    + SCEN_KILLFOCUS
    + SCEN_SETFOCUS (512) is fired when Scintilla receives focus and + SCEN_KILLFOCUS (256) when it loses focus. These notifications are sent using the + WM_COMMAND message on Windows and the "Command" signal on GTK+ as this is the + behavior of the standard Edit control. Unfortunately, these codes do not match the Windows Edit + notification codes EN_SETFOCUS (256) and EN_KILLFOCUS (512). It is + now too late to change the Scintilla codes as clients depend on the current values.

    + +

    SCN_MACRORECORD
    + The SCI_STARTRECORD and SCI_STOPRECORD messages enable and + disable macro recording. When enabled, each time a recordable change occurs, the + SCN_MACRORECORD notification is sent to the container. It is up to the container + to record the action. To see the complete list of SCI_* messages that are + recordable, search the Scintilla source Editor.cxx for + Editor::NotifyMacroRecord. The fields of SCNotification set in this + notification are:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldUsage
    messageThe SCI_* message that caused the notification.
    wParamThe value of wParam in the SCI_* message.
    lParamThe value of lParam in the SCI_* message.
    + +

    SCN_MARGINCLICK
    + This notification tells the container that the mouse was clicked inside a margin that was marked as sensitive (see SCI_SETMARGINSENSITIVEN). This can be used to + perform folding or to place breakpoints. The following SCNotification fields are + used:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldUsage
    modifiersThe appropriate combination of SCI_SHIFT, + SCI_CTRL and SCI_ALT to indicate the keys that were held down + at the time of the margin click.
    positionThe position of the start of the line in the document that corresponds + to the margin click.
    marginThe margin number that was clicked.
    + +

    SCN_NEEDSHOWN
    + Scintilla has determined that a range of lines that is currently invisible should be made + visible. An example of where this may be needed is if the end of line of a contracted fold + point is deleted. This message is sent to the container in case it wants to make the line + visible in some unusual way such as making the whole document visible. Most containers will + just ensure each line in the range is visible by calling SCI_ENSUREVISIBLE. The position and + length fields of SCNotification indicate the range of the document + that should be made visible. The container code will be similar to the following code + skeleton:

    +
    +firstLine = SCI_LINEFROMPOSITION(scn.position)
    +lastLine = SCI_LINEFROMPOSITION(scn.position+scn.length-1)
    +for line = lineStart to lineEnd do SCI_ENSUREVISIBLE(line) next
    +
    + +

    SCN_PAINTED
    + Painting has just been done. Useful when you want to update some other widgets based on a + change in Scintilla, but want to have the paint occur first to appear more responsive. There is + no other information in SCNotification.

    + +

    SCN_USERLISTSELECTION
    + The user has selected an item in a user list. The + SCNotification fields used are:

    + + + + + + + + + + + + + + + + + + + + + + + +
    FieldUsage
    wParamThis is set to the listType parameter from the SCI_USERLISTSHOW message that + initiated the list.
    textThe text of the selection.
    +
    +
    + + +

    SCN_URIDROPPED
    + Only on the GTK+ version. Indicates that the user has dragged a URI such as a file name or Web + address onto Scintilla. The container could interpret this as a request to open the file. The + text field of SCNotification points at the URI text.

    + +

    SCN_DWELLSTART
    + SCN_DWELLEND
    + SCN_DWELLSTART is generated when the user keeps the mouse in one position for the + dwell period (see SCI_SETMOUSEDWELLTIME). SCN_DWELLEND is + generated after a SCN_DWELLSTART and the mouse is moved or other activity such as + key press indicates the dwell is over. Both notifications set the same fields in + SCNotification:

    + + + + + + + + + + + + + + + + + + + + + + + +
    FieldUsage
    positionThis is the nearest position in the document to the position where the + mouse pointer was lingering.
    x, yWhere the pointer lingered. The position field is set to + SCI_POSITIONFROMPOINTCLOSE(x, y).
    +
    +
    + +

    SCI_SETMOUSEDWELLTIME
    + SCI_GETMOUSEDWELLTIME
    + These two messages set and get the time the mouse must sit still, in milliseconds, to generate + a SCN_DWELLSTART notification. If + set to SC_TIME_FOREVER, the default, no dwell events are generated.

    + +

    SCN_ZOOM
    + This notification is generated when the user zooms the display using the keyboard or the + SCI_SETZOOM method is called. This + notification can be used to recalculate positions, such as the width of the line number margin + to maintain sizes in terms of characters rather than pixels. SCNotification has no + additional information.

    + +

    + SCN_HOTSPOTCLICK
    + SCN_HOTSPOTDOUBLECLICK
    + These notifications are generated when the user clicks or double clicks on + text that is in a style with the hotspot attribute set. + This notification can be used to link to variable definitions or web pages. + The position field is set the text position of the click or + double click and the modifiers field set to the key modifiers + held down in a similar manner to SCN_KEY.

    + +

    SCN_CALLTIPCLICK
    + This notification is generated when the user clicks on a calltip. + This notification can be used to display the next function prototype when a + function name is overloaded with different arguments. + The position field is set to 1 if the click is in an up arrow, + 2 if in a down arrow, and 0 if elsewhere.

    + +

    SCN_AUTOCSELECTION
    + The user has selected an item in an autocompletion list. The + notification is sent before the selection is inserted. Automatic insertion can be cancelled by sending a + SCI_AUTOCCANCEL message + before returning from the notification. The SCNotification fields used are:

    + + + + + + + + + + + + + + + + + + + + + + +
    FieldUsage
    lParamThe start position of the word being completed.
    textThe text of the selection.
    + +

    GTK+

    +

    On GTK+, the following functions create a Scintilla widget, communicate with it and allow + resources to be released after all Scintilla widgets hace been destroyed.

    + GtkWidget *scintilla_new()
    + void scintilla_set_id(ScintillaObject *sci, uptr_t id)
    + sptr_t scintilla_send_message(ScintillaObject *sci,unsigned int iMessage, uptr_t wParam, sptr_t lParam)
    + void scintilla_release_resources()
    +
    + +
    + +

    HZSE7<_oN*+o_s@_i&cCp@BvVq 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

    GtkWidget *scintilla_new()
    + Create a new Scintilla widget. The returned pointer can be added to a container and displayed in the same way as other + widgets.

    void scintilla_set_id(ScintillaObject *sci, uptr_t id)
    + Set the control ID which will be used in the idFrom field of the NotifyHeader structure of all + notifications for this instance. When an application creates multiple Scintilla widgets, this allows + the source of each notification to be found. The value should be small, preferrably less than 16 bits, + rather than a pointer as some of the functions will only transmit 16 or 32 bits.

    + +

    sptr_t scintilla_send_message(ScintillaObject *sci,unsigned int iMessage, uptr_t wParam, sptr_t lParam)
    + The main entry point allows sending any of the messages described in this document.

    + +

    void scintilla_release_resources()
    + Call this to free any remaining resources after all the Scintilla widgets have been destroyed.

    + +

    Deprecated messages and notifications

    + +

    The following messages are currently supported to emulate existing Windows controls, but + they will be removed in future versions of Scintilla. If you use these messages you should + replace them with the Scintilla equivalent.

    +
    +WM_GETTEXT(int length, char *text)
    +WM_SETTEXT(<unused>, const char *text)
    +EM_GETLINE(int line, char *text)
    +EM_REPLACESEL(<unused>, const char *text)
    +EM_SETREADONLY
    +EM_GETTEXTRANGE(<unused>, TEXTRANGE *tr)
    +WM_CUT
    +WM_COPY
    +WM_PASTE
    +WM_CLEAR
    +WM_UNDO
    +EM_CANUNDO
    +EM_EMPTYUNDOBUFFER
    +WM_GETTEXTLENGTH
    +EM_GETFIRSTVISIBLELINE
    +EM_GETLINECOUNT
    +EM_GETMODIFY
    +EM_SETMODIFY(bool isModified)
    +EM_GETRECT(RECT *rect)
    +EM_GETSEL(int *start, int *end)
    +EM_EXGETSEL(<unused>, CHARRANGE *cr)
    +EM_SETSEL(int start, int end)
    +EM_EXSETSEL(<unused>, CHARRANGE *cr)
    +EM_GETSELTEXT(<unused>, char *text)
    +EM_LINEFROMCHAR(int position)
    +EM_EXLINEFROMCHAR(int position)
    +EM_LINEINDEX(int line)
    +EM_LINELENGTH(int position)
    +EM_SCROLL(int line)
    +EM_LINESCROLL(int column, int line)
    +EM_SCROLLCARET()
    +EM_CANPASTE
    +EM_CHARFROMPOS(<unused>, POINT *location)
    +EM_POSFROMCHAR(int position, POINT *location)
    +EM_SELECTIONTYPE
    +EM_HIDESELECTION(bool hide)
    +EM_FINDTEXT(int flags, FINDTEXTEX *ft)
    +EM_FINDTEXTEX(int flags, FINDTEXTEX *ft)
    +EM_GETMARGINS
    +EM_SETMARGINS(EC_LEFTMARGIN or EC_RIGHTMARGIN or EC_USEFONTINFO, int val)
    +EM_FORMATRANGE
    +
    + +

    The following are features that are only included if you define + INCLUDE_DEPRECATED_FEATURES in Scintilla.h. To ensure future + compatibility you should change them as indicated.

    + +

    SCN_POSCHANGED() Deprecated
    + Fired when the user moves the cursor to a different position in the text. Use SCN_UPDATEUI instead.

    + +

    SCN_CHECKBRACE Deprecated
    + Either the text or styling of the document has changed or the selection range has changed. + This is replaced by SCN_UPDATEUI. You + can also use SCN_MODIFIED for more + detailed information on text and styling changes,

    + +

    Edit messages never supported by Scintilla

    +
    +EM_GETWORDBREAKPROC EM_GETWORDBREAKPROCEX
    +EM_SETWORDBREAKPROC EM_SETWORDBREAKPROCEX
    +EM_GETWORDWRAPMODE EM_SETWORDWRAPMODE
    +EM_LIMITTEXT EM_EXLIMITTEXT
    +EM_SETRECT EM_SETRECTNP
    +EM_FMTLINES
    +EM_GETHANDLE EM_SETHANDLE
    +EM_GETPASSWORDCHAR EM_SETPASSWORDCHAR
    +EM_SETTABSTOPS
    +EM_FINDWORDBREAK
    +EM_GETCHARFORMAT EM_SETCHARFORMAT
    +EM_GETOLEINTERFACE EM_SETOLEINTERFACE
    +EM_SETOLECALLBACK
    +EM_GETPARAFORMAT EM_SETPARAFORMAT
    +EM_PASTESPECIAL
    +EM_REQUESTRESIZE
    +EM_GETBKGNDCOLOR EM_SETBKGNDCOLOR
    +EM_STREAMIN EM_STREAMOUT
    +EM_GETIMECOLOR EM_SETIMECOLOR
    +EM_GETIMEOPTIONS EM_SETIMEOPTIONS
    +EM_GETOPTIONS EM_SETOPTIONS
    +EM_GETPUNCTUATION EM_SETPUNCTUATION
    +EM_GETTHUMB
    +EM_GETEVENTMASK
    +EM_SETEVENTMASK
    +EM_DISPLAYBAND
    +EM_SETTARGETDEVICE
    +
    + +

    Scintilla tries to be a superset of the standard windows Edit and RichEdit controls wherever + that makes sense. As it is not intended for use in a word processor, some edit messages can not + be sensibly handled. Unsupported messages have no effect.

    + +

    Building Scintilla

    + +

    To build Scintilla or SciTE, see the README file present in both the Scintilla and SciTE + directories. For Windows, GCC 3.2, Borland C++ or Microsoft Visual Studio .NET can be used + for building. There is a make file for building Scintilla but not SciTE with Visual C++ 6 at + scintilla/win32/scintilla_vc6.mak. For GTK+, GCC 3.1 should be used. GTK+ 1.2x and 2.0x are + supported. The version of GTK+ installed should be detected automatically. + When both GTK+ 1 and GTK+ 2 are present, building for GTK+ 1.x requires defining GTK1 + on the command line.

    + +

    Static linking

    + +

    On Windows, Scintilla is normally used as a dynamic library as a .DLL file. If you want to + link Scintilla directly into your application .EXE or .DLL file, then the + STATIC_BUILD preprocessor symbol should be defined and + Scintilla_RegisterClasses called. STATIC_BUILD prevents compiling the + DllMain function which will conflict with any DllMain defined in your + code. Scintilla_RegisterClasses takes the HINSTANCE of your + application and ensures that the "Scintilla" window class is registered. To make sure that the + right pointing arrow cursor used in the margin is displayed by Scintilla add the + scintilla/win32/Margin.cur file to your application's resources with the ID + IDC_MARGIN which is defined in scintilla/win32/platfromRes.h as + 400.

    + +

    Ensuring lexers are linked into Scintilla

    + +

    Depending on the compiler and linker used, the lexers may be stripped out. This is most + often caused when building a static library. To ensure the lexers are linked in, the + Scintilla_LinkLexers() function may be called.

    + +

    Changing set of lexers

    + +

    To change the set of lexers in Scintilla, add and remove lexer source files + (Lex*.cxx) from the scintilla/src directory and run the + src/LexGen.py script from the src directory to update the make files + and KeyWords.cxx. LexGen.py requires Python 2.1 or later. If you do + not have access to Python, you can hand edit KeyWords.cxx in a simple-minded way, + following the patterns of other lexers. The important thing is to include + LINK_LEXER(lmMyLexer); to correspond with the LexerModule + lmMyLexer(...); in your lexer source code.

    + + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaDownload.html b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaDownload.html new file mode 100644 index 00000000..6e057a6c --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaDownload.html @@ -0,0 +1,70 @@ + + + + + + + + + Download Scintilla + + + + + + + + +
    + Scintilla icon + + Download + Scintilla +
    + + + + +
    + + Windows   + + GTK+/Linux   + +
    +

    + Download. +

    +

    + The license for using Scintilla or SciTE is similar to that of Python + containing very few restrictions. +

    +

    + Release 1.71 +

    +

    + Source Code +

    + The source code package contains all of the source code for Scintilla but no binary + executable code and is available in +
      +
    • zip format (720K) commonly used on Windows
    • +
    • tgz format (620K) commonly used on Linux and compatible operating systems
    • +
    + Instructions for building on both Windows and Linux are included in the readme file. +

    + Windows Executable Code +

    + There is no download available containing only the Scintilla DLL. + However, it is included in the SciTE + executable full download as SciLexer.DLL. +

    + SciTE is a good demonstration of Scintilla. +

    +

    + Previous versions can be downloaded from the history + page. +

    + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaHistory.html b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaHistory.html new file mode 100644 index 00000000..3b4dbdce --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaHistory.html @@ -0,0 +1,5296 @@ + + + + + + + + + Scintilla and SciTE + + + + + + + + +
    + Scintilla icon + + Scintilla + and SciTE +
    +

    + History of Scintilla and SciTE +

    +

    + Contributors +

    +

    + Thanks to all the people that have contributed patches, bug reports and suggestions. +

    +

    + Source code and documentation have been contributed by +

    +
      +
    • Atsuo Ishimoto
    • +
    • Mark Hammond
    • +
    • Francois Le Coguiec
    • +
    • Dale Nagata
    • +
    • Ralf Reinhardt
    • +
    • Philippe Lhoste
    • +
    • Andrew McKinlay
    • +
    • Stephan R. A. Deibel
    • +
    • Hans Eckardt
    • +
    • Vassili Bourdo
    • +
    • Maksim Lin
    • +
    • Robin Dunn
    • +
    • John Ehresman
    • +
    • Steffen Goeldner
    • +
    • Deepak S.
    • +
    • Yann Gaillard
    • +
    • Aubin Paul
    • +
    • Jason Diamond
    • +
    • Ahmad Baitalmal
    • +
    • Paul Winwood
    • +
    • Maxim Baranov
    • +
    • Ragnar Højland
    • +
    • Christian Obrecht
    • +
    • Andreas Neukoetter
    • +
    • Adam Gates
    • +
    • Steve Lhomme
    • +
    • Ferdinand Prantl
    • +
    • Jan Dries
    • +
    • Markus Gritsch
    • +
    • Tahir Karaca
    • +
    • Ahmad Zawawi
    • +
    • Laurent le Tynevez
    • +
    • Walter Braeu
    • +
    • Ashley Cambrell
    • +
    • Garrett Serack
    • +
    • Holger Schmidt
    • +
    • ActiveState
    • +
    • James Larcombe
    • +
    • Alexey Yutkin
    • +
    • Jan Hercek
    • +
    • Richard Pecl
    • +
    • Edward K. Ream
    • +
    • Valery Kondakoff
    • +
    • Smári McCarthy
    • +
    • Clemens Wyss
    • +
    • Simon Steele
    • +
    • Serge A. Baranov
    • +
    • Xavier Nodet
    • +
    • Willy Devaux
    • +
    • David Clain
    • +
    • Brendon Yenson
    • +
    • Vamsi Potluru
    • +
    • Praveen Ambekar
    • +
    • Alan Knowles
    • +
    • Kengo Jinno
    • +
    • Valentin Valchev
    • +
    • Marcos E. Wurzius
    • +
    • Martin Alderson
    • +
    • Robert Gustavsson
    • +
    • José Fonseca
    • +
    • Holger Kiemes
    • +
    • Francis Irving
    • +
    • Scott Kirkwood
    • +
    • Brian Quinlan
    • +
    • Ubi
    • +
    • Michael R. Duerig
    • +
    • Deepak T
    • +
    • Don Paul Beletsky
    • +
    • Gerhard Kalab
    • +
    • Olivier Dagenais
    • +
    • Josh Wingstrom
    • +
    • Bruce Dodson
    • +
    • Sergey Koshcheyev
    • +
    • Chuan-jian Shen
    • +
    • Shane Caraveo
    • +
    • Alexander Scripnik
    • +
    • Ryan Christianson
    • +
    • Martin Steffensen
    • +
    • Jakub Vrána
    • +
    • The Black Horus
    • +
    • Bernd Kreuss
    • +
    • Thomas Lauer
    • +
    • Mike Lansdaal
    • +
    • Yukihiro Nakai
    • +
    • Jochen Tucht
    • +
    • Greg Smith
    • +
    • Steve Schoettler
    • +
    • Mauritius Thinnes
    • +
    • Darren Schroeder
    • +
    • Pedro Guerreiro
    • +
    • Dan Petitt
    • +
    • Biswapesh Chattopadhyay
    • +
    • Kein-Hong Man
    • +
    • Patrizio Bekerle
    • +
    • Nigel Hathaway
    • +
    • Hrishikesh Desai
    • +
    • Sergey Puljajev
    • +
    • Mathias Rauen
    • +
    • Angelo Mandato
    • +
    • Denis Sureau
    • +
    • Kaspar Schiess
    • +
    • Christoph Hösler
    • +
    • João Paulo F Farias
    • +
    • Ron Schofield
    • +
    • Stefan Wosnik
    • +
    • Marius Gheorghe
    • +
    • Naba Kumar
    • +
    • Sean O'Dell
    • +
    • Stefanos Togoulidis
    • +
    • Hans Hagen
    • +
    • Jim Cape
    • +
    • Roland Walter
    • +
    • Brian Mosher
    • +
    • Nicholas Nemtsev
    • +
    • Roy Wood
    • +
    • Peter-Henry Mander
    • +
    • Robert Boucher
    • +
    • Christoph Dalitz
    • +
    • April White
    • +
    • S. Umar
    • +
    • Trent Mick
    • +
    • Filip Yaghob
    • +
    • Avi Yegudin
    • +
    • Vivi Orunitia
    • +
    • Manfred Becker
    • +
    • Dimitris Keletsekis
    • +
    • Yuiga
    • +
    • Davide Scola
    • +
    • Jason Boggs
    • +
    • Reinhold Niesner
    • +
    • Jos van der Zande
    • +
    • Pescuma
    • +
    • Pavol Bosik
    • +
    • Johannes Schmid
    • +
    • Blair McGlashan
    • +
    • Mikael Hultgren
    • +
    • Florian Balmer
    • +
    • Hadar Raz
    • +
    • Herr Pfarrer
    • +
    • Ben Key
    • +
    • Gene Barry
    • +
    • Niki Spahiev
    • +
    • Carsten Sperber
    • +
    • Phil Reid
    • +
    • Iago Rubio
    • +
    • Régis Vaquette
    • +
    • Massimo Corà
    • +
    • Elias Pschernig
    • +
    • Chris Jones
    • +
    • Josiah Reynolds
    • +
    • Robert Roessler rftp.com
    • +
    • Steve Donovan
    • +
    • Jan Martin Pettersen
    • +
    • Sergey Philippov
    • +
    • Borujoa
    • +
    • Michael Owens
    • +
    • Franck Marcia
    • +
    • Massimo Maria Ghisalberti
    • +
    • Frank Wunderlich
    • +
    • Josepmaria Roca
    • +
    • Tobias Engvall
    • +
    • Suzumizaki Kimitaka
    • +
    • Michael Cartmell
    • +
    • Pascal Hurni
    • +
    • Andre
    • +
    • Randy Butler
    • +
    • Georg Ritter
    • +
    • Michael Goffioul
    • +
    • Ben Harper
    • +
    • Adam Strzelecki
    • +
    • Kamen Stanev
    • +
    • Steve Menard
    • +
    • Oliver Yeoh
    • +
    • Eric Promislow
    • +
    • Joseph Galbraith
    • +
    • Jeffrey Ren
    • +
    • Armel Asselin
    • +
    • Jim Pattee
    • +
    • Friedrich Vedder
    • +
    • Sebastian Pipping
    • +
    • Andre Arpin
    • +
    • Stanislav Maslovski
    • +
    • Martin Stone
    • +
    • Fabien Proriol
    • +
    • mimir
    • +
    • Nicola Civran
    • +
    +

    + Images used in GTK+ version +

    +
      +
    • + + Icons Copyright(C) 1998 by Dean S. Jones
      +
    • +
    +

    + Release 1.71 +

    +
      +
    • + Released on 21 August 2006. +
    • +
    • + On GTK+ drag and drop defaults to move rather than copy. +
    • +
    • + Double click notification includes line and position. +
    • +
    • + VB lexer bugs fixed for preprocessor directive below a comment or some other states and + to use string not closed style back to the starting quote when there are internal doubled quotes. +
    • +
    • + C++ lexer allows identifiers to contain '$' and non-ASCII characters such as UTF-8. + The '$' character can be disallowed with lexer.cpp.allow.dollars=0. +
    • +
    • + Perl lexer allows UTF-8 identifiers and has some other small improvements. +
    • +
    • + SciTE's $(CurrentWord) uses word.characters.<filepattern> to define the word + rather than a hardcoded list of word characters. +
    • +
    • + SciTE Export as HTML adds encoding information for UTF-8 file and fixes DOCTYPE. +
    • +
    • + SciTE session and .recent files default to the user properties directory rather than global + properties directory. +
    • +
    • + Left and right scroll events handled correctly on GTK+ and horizontal scroll bar has more sensible + distances for page and arrow clicks. +
    • +
    • + SciTE on GTK+ tab bar fixed to work on recent versions of GTK+. +
    • +
    • + On GTK+, if the approximate character set conversion is unavailable, a second attempt is made + without approximations. This may allow keyboard input and paste to work on older systems. +
    • +
    • + SciTE on GTK+ can redefine the Insert key. +
    • +
    • + SciTE scripting interface bug fixed where some string properties could not be changed. +
    • +
    +

    + Release 1.70 +

    +
      +
    • + Released on 20 June 2006. +
    • +
    • + On GTK+, character set conversion is performed using an option that allows approximate conversions rather + than failures when a character can not be converted. This may lead to similar characters being inserted or + when no similar character is available a '?' may be inserted. +
    • +
    • + On GTK+, the internationalised IM (Input Method) feature is used for all typed input for all character sets. +
    • +
    • + Scintilla has new margin types SC_MARGIN_BACK and SC_MARGIN_FORE that use the default + style's background and foreground colours (normally white and black) as the background to the margin. +
    • +
    • + Scintilla/GTK+ allows file drops on Windows when drop is of type DROPFILES_DND + as well as text/uri-list. +
    • +
    • + Code page can only be set to one of the listed valid values. +
    • +
    • + Text wrapping fixed for cases where insertion was not wide enough to trigger + wrapping before being styled but was after styling. +
    • +
    • + SciTE find marks are removed before printing or exporting to avoid producing incorrect styles. +
    • +
    +

    + Release 1.69 +

    +
      +
    • + Released on 29 May 2006. +
    • +
    • + SciTE supports z-order based buffer switching on Ctrl+Tab. +
    • +
    • + Translucent support for selection and whole line markers. +
    • +
    • + SciTE may have per-language abbreviations files. +
    • +
    • + Support for Spice language. +
    • +
    • + On GTK+ autocompletion lists are optimised and use correct selection colours. +
    • +
    • + On GTK+ the URI data type is preferred in drag and drop so that applications + will see files dragged from the shell rather than dragging the text of the file name + into the document. +
    • +
    • + Increased number of margins to 5. +
    • +
    • + Basic lexer allows include directive $include: "file name". +
    • +
    • + SQL lexer no longer bases folding on indentation. +
    • +
    • + Line ends are transformed when copied to clipboard on + Windows/GTK+2 as well as Windows/GTK+ 1. +
    • +
    • + Lexing code masks off the indicator bits on the start style before calling the lexer + to avoid confusing the lexer when an application has used an indicator. +
    • +
    • + SciTE savebefore:yes only saves the file when it has been changed. +
    • +
    • + SciTE adds output.initial.hide setting to allow setting the size of the output pane + without it showing initially. +
    • +
    • + SciTE on Windows Go To dialog allows line number with more digits. +
    • +
    • + Bug in HTML lexer fixed where a segment of PHP could switch scripting language + based on earlier text on that line. +
    • +
    • + Memory bug fixed when freeing regions on GTK+. + Other minor bugs fixed on GTK+. +
    • +
    • + Deprecated GTK+ calls in Scintilla replaced with current calls. +
    • +
    • + Fixed a SciTE bug where closing the final buffer, if read-only, left the text present in an + untitled buffer. +
    • +
    • + Bug fixed in bash lexer that prevented folding. +
    • +
    • + Crash fixed in bash lexer when backslash at end of file. +
    • +
    • + Crash on recent releases of GTK+ 2.x avoided by changing default font from X + core font to Pango font "!Sans". +
    • +
    • + Fix for SciTE properties files where multiline properties continued over completely blank lines. +
    • +
    • + Bug fixed in SciTE/GTK+ director interface where more data available than + buffer size. +
    • +
    • + Minor visual fixes to SciTE splitter on GTK+. +
    • +
    +

    + Release 1.68 +

    +
      +
    • + Released on 9 March 2006. +
    • +
    • + Translucent drawing implemented for caret line and box indicators. +
    • +
    • + Lexer specifically for TCL is much more accurate than reusing C++ lexer. +
    • +
    • + Support for Inno Setup scripts. +
    • +
    • + Support for Opal language. +
    • +
    • + Calltips may use a new style, STYLE_CALLTIP which allows choosing a + different font for calltips. +
    • +
    • + Python lexer styles comments on decorators. +
    • +
    • + HTML lexer refined handling of "?>" and "%>" within server + side scripts. +
    • +
    • + Batch file lexer improved. +
    • +
    • + Eiffel lexer doesn't treat '.' as a name character. +
    • +
    • + Lua lexer handles length operator, #, and hex literals. +
    • +
    • + Properties file lexer has separate style for keys. +
    • +
    • + PL/SQL folding improved. +
    • +
    • + SciTE Replace dialog always searches in forwards direction. +
    • +
    • + SciTE can detect language of file from initial #! line. +
    • +
    • + SciTE on GTK+ supports output.scroll=2 setting. +
    • +
    • + SciTE can perform an import a properties file from the command line. +
    • +
    • + Set of word characters used for regular expression \< and \>. +
    • +
    • + Bug fixed with SCI_COPYTEXT stopping too early. +
    • +
    • + Bug fixed with splitting lines so that all lines are split. +
    • +
    • + SciTE calls OnSwitchFile when closing one buffer causes a switch to another. +
    • +
    • + SciTE bug fixed where properties were being reevaluated without good reason + after running a macro. +
    • +
    • + Crash fixed when clearing document with some lines contracted in word wrap mode. +
    • +
    • + Palette expands as more entries are needed. +
    • +
    • + SCI_POSITIONFROMPOINT returns more reasonable value when close to + last text on a line. +
    • +
    • + On Windows, long pieces of text may be drawn in segments if they fail to draw + as a whole. +
    • +
    • + Bug fixed with bad drawing when some visual changes made inside SCN_UPDATEUI + notification. +
    • +
    • + SciTE bug fixed with groupundo setting. +
    • +
    +

    + Release 1.67 +

    +
      +
    • + Released on 17 December 2005. +
    • +
    • + Scintilla checks the paint region more accurately when seeing if an area is being + repainted. Platform layer implementations may need to change for this to take + effect. This fixes some drawing and styling bugs. Also optimized some parts of + marker code to only redraw the line of the marker rather than whole of the margin. +
    • +
    • + Quoted identifier style for SQL. SQL folding performed more simply. +
    • +
    • + Ruby lexer improved to better handle here documents and non-ASCII + characters. +
    • +
    • + Lua lexer supports long string and block comment syntax from Lua 5.1. +
    • +
    • + Bash lexer handles here documents better. +
    • +
    • + JavaScript lexing recognises regular expressions more accurately and includes flag + characters in the regular expression style. This is both in JavaScript files and when + JavaScript is embedded in HTML. +
    • +
    • + Scintilla API provided to reveal how many style bits are needed for the + current lexer. +
    • +
    • + Selection duplicate added. +
    • +
    • + Scintilla API for adding a set of markers to a line. +
    • +
    • + DBCS encodings work on Windows 9x. +
    • +
    • + Convention defined for property names to be used by lexers and folders + so they can be automatically discovered and forwarded from containers. +
    • +
    • + Default bookmark in SciTE changed to a blue sphere image. +
    • +
    • + SciTE stores the time of last asking for a save separately for each buffer + which fixes bugs with automatic reloading. +
    • +
    • + On Windows, pasted text has line ends converted to current preference. + GTK+ already did this. +
    • +
    • + Kid template language better handled by HTML lexer by finishing ASP Python + mode when a ?> is found. +
    • +
    • + SciTE counts number of characters in a rectangular selection correctly. +
    • +
    • + 64-bit compatibility improved. One change that may affect user code is that + the notification message header changed to include a pointer-sized id field + to match the current Windows definition. +
    • +
    • + Empty ranges can no longer be dragged. +
    • +
    • + Crash fixed when calls made that use layout inside the painted notification. +
    • +
    • + Bug fixed where Scintilla created pixmap buffers that were too large leading + to failures when many instances used. +
    • +
    • + SciTE sets the directory of a new file to the directory of the currently + active file. +
    • +
    • + SciTE allows choosing a code page for the output pane. +
    • +
    • + SciTE HTML exporter no longer honours monospaced font setting. +
    • +
    • + Line layout cache in page mode caches the line of the caret. An assertion is + now used to ensure that the layout reentrancy problem that caused this + is easier to find. +
    • +
    • + Speed optimized for long lines and lines containing many control characters. +
    • +
    • + Bug fixed in brace matching in DBCS files where byte inside character + is same as brace. +
    • +
    • + Indent command does not indent empty lines. +
    • +
    • + SciTE bug fixed for commands that operate on files with empty extensions. +
    • +
    • + SciTE bug fixed where monospaced option was copied for subsequently opened files. +
    • +
    • + SciTE on Windows bug fixed in the display of a non-ASCII search string + which can not be found. +
    • +
    • + Bugs fixed with nested calls displaying a new calltip while one is already + displayed. +
    • +
    • + Bug fixed when styling PHP strings. +
    • +
    • + Bug fixed when styling C++ continued preprocessor lines. +
    • +
    • + SciTE bug fixed where opening file from recently used list reset choice of + language. +
    • +
    • + SciTE bug fixed when compiled with NO_EXTENSIONS and + closing one file closes the application. +
    • +
    • + SciTE crash fixed for error messages that look like Lua messages but aren't + in the same order. +
    • +
    • + Remaining fold box support deprecated. The symbols SC_FOLDLEVELBOXHEADERFLAG, + SC_FOLDLEVELBOXFOOTERFLAG, SC_FOLDLEVELCONTRACTED, + SC_FOLDLEVELUNINDENT, and SC_FOLDFLAG_BOX are deprecated. +
    • +
    +

    + Release 1.66 +

    +
      +
    • + Released on 26 August 2005. +
    • +
    • + New, more ambitious Ruby lexer. +
    • +
    • + SciTE Find in Files dialog has options for matching case and whole words which are + enabled when the internal find command is used. +
    • +
    • + SciTE output pane can display automatic completion after "$(" typed. + An initial ">" on a line is ignored when Enter pressed. +
    • +
    • + C++ lexer recognises keywords within line doc comments. It continues styles over line + end characters more consistently so that eolfilled style can be used for preprocessor lines + and line comments. +
    • +
    • + VB lexer improves handling of file numbers and date literals. +
    • +
    • + Lua folder handles repeat until, nested comments and nested strings. +
    • +
    • + POV lexer improves handling of comment lines. +
    • +
    • + AU3 lexer and folder updated. COMOBJ style added. +
    • +
    • + Bug fixed with text display on GTK+ with Pango 1.8. +
    • +
    • + Caret painting avoided when not focused. +
    • +
    • + SciTE on GTK+ handles file names used to reference properties as case-sensitive. +
    • +
    • + SciTE on GTK+ Save As and Export commands set the file name field. + On GTK+ the Export commands modify the file name in the same way as on Windows. +
    • +
    • + Fixed SciTE problem where confirmation was not displaying when closing a file where all + contents had been deleted. +
    • +
    • + Middle click on SciTE tab now closes correct buffer on Windows when tool bar is visible. +
    • +
    • + SciTE bugs fixed where files contained in directory that includes '.' character. +
    • +
    • + SciTE bug fixed where import in user options was reading file from directory of + global options. +
    • +
    • + SciTE calltip bug fixed where single line calltips had arrow displayed incorrectly. +
    • +
    • + SciTE folding bug fixed where empty lines were shown for no reason. +
    • +
    • + Bug fixed where 2 byte per pixel XPM images caused crash although they are still not + displayed. +
    • +
    • + Autocompletion list size tweaked. +
    • +
    +

    + Release 1.65 +

    +
      +
    • + Released on 1 August 2005. +
    • +
    • + FreeBasic support. +
    • +
    • + SciTE on Windows handles command line arguments + "-" (read standard input into buffer), + "--" (read standard input into output pane) and + "-@" (read file names from standard input and open each). +
    • +
    • + SciTE includes a simple implementation of Find in Files which is used if no find.command is set. +
    • +
    • + SciTE can close tabs with a mouse middle click. +
    • +
    • + SciTE includes a save.all.for.build setting. +
    • +
    • + Folder for MSSQL. +
    • +
    • + Batch file lexer understands more of the syntax and the behaviour of built in commands. +
    • +
    • + Perl lexer handles here docs better; disambiguates barewords, quote-like delimiters, and repetition operators; + handles Pods after __END__; recognises numbers better; and handles some typeglob special variables. +
    • +
    • + Lisp adds more lexical states. +
    • +
    • + PHP allows spaces after <<<. +
    • +
    • + TADS3 has a simpler set of states and recognizes identifiers. +
    • +
    • + Avenue elseif folds better. +
    • +
    • + Errorlist lexer treats lines starting with '+++' and '---' as separate + styles from '+' and '-' as they indicate file names in diffs. +
    • +
    • + SciTE error recogniser handles file paths in extra explanatory lines from MSVC + and in '+++' and '---' lines from diff. +
    • +
    • + Bugs fixed in SciTE and Scintilla folding behaviour when text pasted before + folded text caused unnecessary + unfolding and cutting text could lead to text being irretrievably hidden. +
    • +
    • + SciTE on Windows uses correct font for dialogs and better font for tab bar + allowing better localisation +
    • +
    • + When Windows is used with a secondary monitor before the primary + monitor, autocompletion lists are not forced onto the primary monitor. +
    • +
    • + Scintilla calltip bug fixed where down arrow setting wrong value in notification + if not in first line. SciTE bug fixed where second arrow only shown on multiple line + calltip and was therefore misinterpreting the notification value. +
    • +
    • + Lexers will no longer be re-entered recursively during, for example, fold level setting. +
    • +
    • + Undo of typing in overwrite mode undoes one character at a time rather than requiring a removal + and addition step for each character. +
    • +
    • + EM_EXSETSEL(0,-1) fixed. +
    • +
    • + Bug fixed where part of a rectangular selection was not shown as selected. +
    • +
    • + Autocomplete window size fixed. +
    • +
    +

    + Release 1.64 +

    +
      +
    • + Released on 6 June 2005. +
    • +
    • + TADS3 support +
    • +
    • + Smalltalk support. +
    • +
    • + Rebol support. +
    • +
    • + Flagship (Clipper / XBase) support. +
    • +
    • + CSound support. +
    • +
    • + SQL enhanced to support SQL*Plus. +
    • +
    • + SC_MARK_FULLRECT margin marker fills the whole marker margin for marked + lines with a colour. +
    • +
    • + Performance improved for some large undo and redo operations and modification flags + added in notifications. +
    • +
    • + SciTE adds command equivalents for fold margin mouse actions. +
    • +
    • + SciTE adds OnUpdateUI to set of events that can be handled by a Lua script. +
    • +
    • + Properties set in Scintilla can be read. +
    • +
    • + GTK+ SciTE exit confirmation adds Cancel button. +
    • +
    • + More accurate lexing of numbers in PHP and Caml. +
    • +
    • + Perl can fold POD and package sections. POD verbatim section style. + Globbing syntax recognized better. +
    • +
    • + Context menu moved slightly on GTK+ so that it will be under the mouse and will + stay open if just clicked rather than held. +
    • +
    • + Rectangular selection paste works the same whichever direction the selection was dragged in. +
    • +
    • + EncodedFromUTF8 handles -1 length argument as documented. +
    • +
    • + Undo and redo can cause SCN_MODIFYATTEMPTRO notifications. +
    • +
    • + Indicators display correctly when they start at the second character on a line. +
    • +
    • + SciTE Export As HTML uses standards compliant CSS. +
    • +
    • + SciTE automatic indentation handles keywords for indentation better. +
    • +
    • + SciTE fold.comment.python property removed as does not work. +
    • +
    • + Fixed problem with character set conversion when pasting on GTK+. +
    • +
    • + SciTE default character set changed from ANSI_CHARSET to DEFAULT_CHARSET. +
    • +
    • + Fixed crash when creating empty autocompletion list. +
    • +
    • + Autocomplete window size made larger under some conditions to make truncation less common. +
    • +
    • + Bug fixed where changing case of a selection did not affect initial character of lines + in multi-byte encodings. +
    • +
    • + Bug fixed where rectangular selection not displayed after Alt+Shift+Click. +
    • +
    +

    + Release 1.63 +

    +
      +
    • + Released on 4 April 2005. +
    • +
    • + Autocompletion on Windows changed to use popup window, be faster, + allow choice of maximum width and height, and to highlight only the text of the + selected item rather than both the text and icon if any. +
    • +
    • + Extra items can be added to the context menu in SciTE. +
    • +
    • + Character wrap mode in Scintilla helps East Asian languages. +
    • +
    • + Lexer added for Haskell. +
    • +
    • + Objective Caml support. +
    • +
    • + BlitzBasic and PureBasic support. +
    • +
    • + CSS support updated to handle CSS2. +
    • +
    • + C++ lexer is more selective about document comment keywords. +
    • +
    • + AutoIt 3 lexer improved. +
    • +
    • + Lua lexer styles end of line characters on comment and preprocessor + lines so that the eolfilled style can be applied to them. +
    • +
    • + NSIS support updated for line continuations, box comments, SectionGroup and + PageEx, and with more up-to-date properties. +
    • +
    • + Clarion lexer updated to perform folding and have more styles. +
    • +
    • + SQL lexer gains second set of keywords. +
    • +
    • + Errorlist lexer recognises Borland Delphi error messages. +
    • +
    • + Method added for determining number of visual lines occupied by a document + line due to wrapping. +
    • +
    • + Sticky caret mode does not modify the preferred caret x position when typing + and may be useful for typing columns of text. +
    • +
    • + Dwell end notification sent when scroll occurs. +
    • +
    • + On GTK+, Scintilla requisition height is screen height rather than large fixed value. +
    • +
    • + Case insensitive autocompletion prefers exact case match. +
    • +
    • + SCI_PARADOWN and SCI_PARAUP treat lines containing only white + space as empty and handle text hidden by folding. +
    • +
    • + Scintilla on Windows supports WM_PRINTCLIENT although there are some + limitations. +
    • +
    • + SCN_AUTOCSELECTION notification sent when user selects from autoselection list. +
    • +
    • + SciTE's standard properties file sets buffers to 10, uses Pango fonts on GTK+ and + has dropped several languages to make the menu fit on screen. +
    • +
    • + SciTE's encoding cookie detection loosened so that common XML files will load + in UTF-8 if that is their declared encoding. +
    • +
    • + SciTE on GTK+ changes menus and toolbars to not be detachable unless turned + on with a property. Menus no longer tear off. The toolbar may be set to use the + default theme icons rather than SciTE's set. Changed key for View | End of Line + because of a conflict. Language menu can contain more items. +
    • +
    • + SciTE on GTK+ 2.x allows the height and width of the file open file chooser to + be set, for the show hidden files check box to be set from an option and for it + to be opened in the directory of the current file explicitly. Enter key works in + save chooser. +
    • +
    • + Scintilla lexers should no longer see bits in style bytes that are outside the set + they modify so should be able to correctly lex documents where the container + has used indicators. +
    • +
    • + SciTE no longer asks to save before performing a revert. +
    • +
    • + SciTE director interface adds a reloadproperties command to reload properties + from files. +
    • +
    • + Allow build on CYGWIN platform. +
    • +
    • + Allow use from LccWin compiler. +
    • +
    • + SCI_COLOURISE for SCLEX_CONTAINER causes a + SCN_STYLENEEDED notification. +
    • +
    • + Bugs fixed in lexing of HTML/ASP/JScript. +
    • +
    • + Fix for folding becoming confused. +
    • +
    • + On Windows, fixes for Japanese Input Method Editor and for 8 bit Katakana + characters. +
    • +
    • + Fixed buffer size bug avoided when typing long words by making buffer bigger. +
    • +
    • + Undo after automatic indentation more sensible. +
    • +
    • + SciTE menus on GTK+ uses Shift and Ctrl rather than old style abbreviations. +
    • +
    • + SciTE full screen mode on Windows calculates size more correctly. +
    • +
    • + SciTE on Windows menus work better with skinning applications. +
    • +
    • + Searching bugs fixed. +
    • +
    • + Colours reallocated when changing image using SCI_REGISTERIMAGE. +
    • +
    • + Caret stays visible when Enter held down. +
    • +
    • + Undo of automatic indentation more reasonable. +
    • +
    • + High processor usage fixed in background wrapping under some + circumstances. +
    • +
    • + Crashing bug fixed on AMD64. +
    • +
    • + SciTE crashing bug fixed when position.height or position.width not set. +
    • +
    • + Crashing bug on GTK+ fixed when setting cursor and window is NULL. +
    • +
    • + Crashing bug on GTK+ preedit window fixed. +
    • +
    • + SciTE crashing bug fixed in incremental search on Windows ME. +
    • +
    • + SciTE on Windows has a optional find and replace dialogs that can search through + all buffers and search within a particular style number. +
    • +
    +

    + Release 1.62 +

    +
      +
    • + Released on 31 October 2004. +
    • +
    • + Lexer added for ASN.1. +
    • +
    • + Lexer added for VHDL. +
    • +
    • + On Windows, an invisible system caret is used to allow screen readers to determine + where the caret is. The visible caret is still drawn by the painting code. +
    • +
    • + On GTK+, Scintilla has methods to read the target as UTF-8 and to convert + a string from UTF-8 to the document encoding. This eases integration with + containers that use the UTF-8 encoding which is the API encoding for GTK+ 2. +
    • +
    • + SciTE on GTK+2 and Windows NT/2000/XP allows search and replace of Unicode text. +
    • +
    • + SciTE calltips allow setting the characters used to start and end parameter lists and + to separate parameters. +
    • +
    • + FindColumn method converts a line and column into a position, taking into account + tabs and multi-byte characters. +
    • +
    • + On Windows, when Scintilla copies text to the clipboard as Unicode, it avoids + adding an ANSI copy as the system will automatically convert as required in + a context-sensitive manner. +
    • +
    • + SciTE indent.auto setting automatically determines indent.size and use.tabs from + document contents. +
    • +
    • + SciTE defines a CurrentMessage property that holds the most recently selected + output pane message. +
    • +
    • + SciTE Lua scripting enhanced with +
        +
      • A Lua table called 'buffer' is associated with each buffer and can be used to + maintain buffer-specific state.
      • +
      • A 'scite' object allows interaction with the application such as opening + files from script.
      • +
      • Dynamic properties can be reset by assigning nil to a given key in + the props table.
      • +
      • An 'OnClear' event fires whenever properties and extension scripts are + about to be reloaded.
      • +
      • On Windows, loadlib is enabled and can be used to access Lua + binary modules / DLLs.
      +
    • +
    • + SciTE Find in Files on Windows can be used in a modeless way and gains a '..' + button to move up to the parent directory. It is also wider so that longer paths + can be seen. +
    • +
    • + Close buttons added to dialogs in SciTE on Windows. +
    • +
    • + SciTE on GTK+ 2 has a "hidden files" check box in file open dialog. +
    • +
    • + SciTE use.monospaced setting removed. More information in the + FAQ. +
    • +
    • + APDL lexer updated with more lexical classes +
    • +
    • + AutoIt3 lexer updated. +
    • +
    • + Ada lexer fixed to support non-ASCII text. +
    • +
    • + Cpp lexer now only matches exactly three slashes as starting a doc-comment so that + lines of slashes are seen as a normal comment. + Line ending characters are appear in default style on preprocessor and single line + comment lines. +
    • +
    • + CSS lexer updated to support CSS2 including second set of keywords. +
    • +
    • + Errorlist lexer now understands Java stack trace lines. +
    • +
    • + SciTE's handling of HTML Tidy messages jumps to column as well as line indicated. +
    • +
    • + Lisp lexer allows multiline strings. +
    • +
    • + Lua lexer treats .. as an operator when between identifiers. +
    • +
    • + PHP lexer handles 'e' in numerical literals. +
    • +
    • + PowerBasic lexer updated for macros and optimised. +
    • +
    • + Properties file folder changed to leave lines before a header at the base level + and thus avoid a vertical line when using connected folding symbols. +
    • +
    • + GTK+ on Windows version uses Alt for rectangular selection to be compatible with + platform convention. +
    • +
    • + SciTE abbreviations file moved from system directory to user directory + so each user can have separate abbreviations. +
    • +
    • + SciTE on GTK+ has improved .desktop file and make install support that may + lead to better integration with system shell. +
    • +
    • + Disabling of themed background drawing on GTK+ extended to all cases. +
    • +
    • + SciTE date formatting on Windows performed with the user setting rather than the + system setting. +
    • +
    • + GTK+ 2 redraw while scrolling fixed. +
    • +
    • + Recursive property definitions are safer, avoiding expansion when detected. +
    • +
    • + SciTE thread synchronization for scripts no longer uses HWND_MESSAGE + so is compatible with older versions of Windows. + Other Lua scripting bugs fixed. +
    • +
    • + SciTE on Windows localisation of menu accelerators changed to be compatible + with alternative UI themes. +
    • +
    • + SciTE on Windows full screen mode now fits better when menu different height + to title bar height. +
    • +
    • + SC_MARK_EMPTY marker is now invisible and does not change the background + colour. +
    • +
    • + Bug fixed in HTML lexer to allow use of <?xml in strings in scripts without + triggering xml mode. +
    • +
    • + Bug fixed in SciTE abbreviation expansion that could break indentation or crash. +
    • +
    • + Bug fixed when searching for a whole word string that ends one character before + end of document. +
    • +
    • + Drawing bug fixed when indicators drawn on wrapped lines. +
    • +
    • + Bug fixed when double clicking a hotspot. +
    • +
    • + Bug fixed where autocompletion would remove typed text if no match found. +
    • +
    • + Bug fixed where display does not scroll when inserting in long wrapped line. +
    • +
    • + Bug fixed where SCI_MARKERDELETEALL would only remove one of the markers + on a line that contained multiple markers with the same number. +
    • +
    • + Bug fixed where markers would move when converting line endings. +
    • +
    • + Bug fixed where SCI_LINEENDWRAP would move too far when line ends are visible. +
    • +
    • + Bugs fixed where calltips with unicode or other non-ASCII text would display + incorrectly. +
    • +
    • + Bug fixed in determining if at save point after undoing from save point and then + performing changes. +
    • +
    • + Bug fixed on GTK+ using unsupported code pages where extraneous text could + be drawn. +
    • +
    • + Bug fixed in drag and drop code on Windows where dragging from SciTE to + Firefox could hang both applications. +
    • +
    • + Crashing bug fixed on GTK+ when no font allocation succeeds. +
    • +
    • + Crashing bug fixed when autocompleting word longer than 1000 characters. +
    • +
    • + SciTE crashing bug fixed when both Find and Replace dialogs shown by disallowing + this situation. +
    • +
    +

    + Release 1.61 +

    +
      +
    • + Released on 29 May 2004. +
    • +
    • + Improvements to selection handling on GTK+. +
    • +
    • + SciTE on GTK+ 2.4 uses the improved file chooser which allows + file extension filters, multiple selection, and remembers favourite + directories. +
    • +
    • + SciTE Load Session and Save Session commands available on GTK+. +
    • +
    • + SciTE lists Lua Startup Script in Options menu when loaded. +
    • +
    • + In SciTE, OnUserListSelection can be implemented in Lua. +
    • +
    • + SciTE on Windows has a context menu on the file tabs. +
    • +
    • + SQL lexer allows '#' comments and optionally '\' quoting inside strings. +
    • +
    • + Mssql lexer improved. +
    • +
    • + AutoIt3 lexer updated. +
    • +
    • + Perl lexer recognizes regular expression use better. +
    • +
    • + Errorlist lexer understands Lua tracebacks and copes with findstr + output for file names that end with digits. +
    • +
    • + Drawing of lines on GTK+ improved and made more like Windows + without final point. +
    • +
    • + SciTE on GTK+ uses a high resolution window icon. +
    • +
    • + SciTE can be set to warn before loading files larger than a particular size. +
    • +
    • + SciTE Lua scripting bugs fixed included a crashing bug when using + an undefined function name that would go before first actual name. +
    • +
    • + SciTE bug fixed where a modified buffer was not saved if it was + the last buffer and was not current when the New command used. +
    • +
    • + SciTE monofont mode no longer affects line numbers. +
    • +
    • + Crashing bug in SciTE avoided by not allowing both the Find and Replace + dialogs to be visible at one time. +
    • +
    • + Crashing bug in SciTE fixed when Lua scripts were being run + concurrently. +
    • +
    • + Bug fixed that caused incorrect line number width in SciTE. +
    • +
    • + PHP folding bug fixed. +
    • +
    • + Regression fixed when setting word characters to not include + some of the standard word characters. +
    • +
    +

    + Release 1.60 +

    +
      +
    • + Released on 1 May 2004. +
    • +
    • + SciTE can be scripted using the Lua programming language. +
    • +
    • + command.mode is a better way to specify tool command options in SciTE. +
    • +
    • + Continuation markers can be displayed so that you can see which lines are wrapped. +
    • +
    • + Lexer for Gui4Cli language. +
    • +
    • + Lexer for Kix language. +
    • +
    • + Lexer for Specman E language. +
    • +
    • + Lexer for AutoIt3 language. +
    • +
    • + Lexer for APDL language. +
    • +
    • + Lexer for Bash language. Also reasonable for other Unix shells. +
    • +
    • + SciTE can load lexers implemented in external shared libraries. +
    • +
    • + Perl treats "." not as part of an identifier and interprets '/' and '->' + correctly in more circumstances. +
    • +
    • + PHP recognises variables within strings. +
    • +
    • + NSIS has properties "nsis.uservars" and "nsis.ignorecase". +
    • +
    • + MSSQL lexer adds keyword list for operators and stored procedures, + defines '(', ')', and ',' as operators and changes some other details. +
    • +
    • + Input method preedit window on GTK+ 2 may support some Asian languages. +
    • +
    • + Platform interface adds an extra platform-specific flag to Font::Create. + Used on wxWidgets to choose antialiased text display but may be used for + any task that a platform needs. +
    • +
    • + OnBeforeSave method added to Extension interface. +
    • +
    • + Scintilla methods that return strings can be called with a NULL pointer + to find out how long the string should be. +
    • +
    • + Visual Studio .NET project file now in VS .NET 2003 format so can not be used + directly in VS .NET 2002. +
    • +
    • + Scintilla can be built with GTK+ 2 on Windows. +
    • +
    • + Updated RPM spec for SciTE on GTK+. +
    • +
    • + GTK+ makefile for SciTE allows selection of destination directory, creates destination + directories and sets file modes and owners better. +
    • +
    • + Tab indents now go to next tab multiple rather than add tab size. +
    • +
    • + SciTE abbreviations now use the longest possible match rather than the shortest. +
    • +
    • + Autocompletion does not remove prefix when actioned with no choice selected. +
    • +
    • + Autocompletion cancels when moving beyond the start position, not at the start position. +
    • +
    • + SciTE now shows only calltips for functions that match exactly, not + those that match as a prefix. +
    • +
    • + SciTE can repair box comment sections where some lines were added without + the box comment middle line prefix. +
    • +
    • + Alt+ works in user.shortcuts on Windows. +
    • +
    • + SciTE on GTK+ enables replace in selection for rectangular selections. +
    • +
    • + Key bindings for command.shortcut implemented in a way that doesn't break + when the menus are localised. +
    • +
    • + Drawing of background on GTK+ faster as theme drawing disabled. +
    • +
    • + On GTK+, calltips are moved back onto the screen if they extend beyond the screen bounds. +
    • +
    • + On Windows, the Scintilla object is destroyed on WM_NCDESTROY rather than + WM_DESTROY which arrives earlier. This fixes some problems when Scintilla was subclassed. +
    • +
    • + The zorder switching feature removed due to number of crashing bugs. +
    • +
    • + Code for XPM images made more robust. +
    • +
    • + Bug fixed with primary selection on GTK+. +
    • +
    • + On GTK+ 2, copied or cut text can still be pasted after the Scintilla widget is destroyed. +
    • +
    • + Styling change not visible problem fixed when line was cached. +
    • +
    • + Bug in SciTE on Windows fixed where clipboard commands stopped working. +
    • +
    • + Crashing bugs in display fixed in line layout cache. +
    • +
    • + Crashing bug may be fixed on AMD64 processor on GTK+. +
    • +
    • + Rare hanging crash fixed in Python lexer. +
    • +
    • + Display bugs fixed with DBCS characters on GTK+. +
    • +
    • + Autocompletion lists on GTK+ 2 are not sorted by the ListModel as the + contents are sorted correctly by Scintilla. +
    • +
    • + SciTE fixed to not open extra untitled buffers with check.if.already.open. +
    • +
    • + Sizing bug fixed on GTK+ when window resized while unmapped. +
    • +
    • + Text drawing crashing bug fixed on GTK+ with non-Pango fonts and long strings. +
    • +
    • + Fixed some issues if characters are unsigned. +
    • +
    • + Fixes in NSIS support. +
    • +
    +

    + Release 1.59 +

    +
      +
    • + Released on 19 February 2004. +
    • +
    • + SciTE Options and Language menus reduced in length by commenting + out some languages. Languages can be enabled by editing the global + properties file. +
    • +
    • + Verilog language supported. +
    • +
    • + Lexer for Microsoft dialect of SQL. SciTE properties file available from extras page. +
    • +
    • + Perl lexer disambiguates '/' better. +
    • +
    • + NSIS lexer improved with a lexical class for numbers, option for ignoring case + of keywords, and folds only occurring when folding keyword first on line. +
    • +
    • + PowerBasic lexer improved with styles for constants and assembler and + folding improvements. +
    • +
    • + On GTK+, input method support only invoked for Asian languages and not + European languages as the old European keyboard code works better. +
    • +
    • + Scintilla can be requested to allocate a certain amount and so avoid repeated + reallocations and memory inefficiencies. SciTE uses this and so should require + less memory. +
    • +
    • + SciTE's "toggle current fold" works when invoked on child line as well as + fold header. +
    • +
    • + SciTE output pane scrolling can be set to not scroll back to start after + completion of command. +
    • +
    • + SciTE has a $(SessionPath) property. +
    • +
    • + SciTE on Windows can use VK_* codes for keys in user.shortcuts. +
    • +
    • + Stack overwrite bug fixed in SciTE's command to move to the end of a + preprocessor conditional. +
    • +
    • + Bug fixed where vertical selection appeared to select a different set of characters + then would be used by, for example, a copy. +
    • +
    • + SciTE memory leak fixed in fold state remembering. +
    • +
    • + Bug fixed where changing the style of some text outside the + standard StyleNeeded notification would not be visible. +
    • +
    • + On GTK+ 2 g_iconv is used in preference to iconv, as it is provided by GTK+ + so should avoid problems finding the iconv library. +
    • +
    • + On GTK+ fixed a style reference count bug. +
    • +
    • + Memory corruption bug fixed with GetSelText. +
    • +
    • + On Windows Scintilla deletes memory on WM_NCDESTROY rather than + the earlier WM_DESTROY to avoid problems when the window is subclassed. +
    • +
    +

    + Release 1.58 +

    +
      +
    • + Released on 11 January 2004. +
    • +
    • + Method to discover the currently highlighted element in an autocompletion list. +
    • +
    • + On GTK+, the lexers are now included in the scintilla.a library file. This + will require changes to the make files of dependent projects. +
    • +
    • + Octave support added alongside related Matlab language and Matlab support improved. +
    • +
    • + VB lexer gains an unterminated string state and 4 sets of keywords. +
    • +
    • + Ruby lexer handles $' correctly. +
    • +
    • + Error line handling improved for FORTRAN compilers from Absoft and Intel. +
    • +
    • + International input enabled on GTK+ 2 although there is no way to choose an + input method. +
    • +
    • + MultiplexExtension in SciTE allows multiple extensions to be used at once. +
    • +
    • + Regular expression replace interprets backslash expressions \a, \b, \f, \n, \r, \t, + and \v in the replacement value. +
    • +
    • + SciTE Replace dialog displays number of replacements made when Replace All or + Replace in Selection performed. +
    • +
    • + Localisation files may contain a translation.encoding setting which is used + on GTK+ 2 to automatically reencode the translation to UTF-8 so it will be + the localised text will be displayed correctly. +
    • +
    • + SciTE on GTK+ implements check.if.already.open. +
    • +
    • + Make files for Mac OS X made more robust. +
    • +
    • + Performance improved in SciTE when switching buffers when there + is a rectangular selection. +
    • +
    • + Fixed failure to display some text when wrapped. +
    • +
    • + SciTE crashes from Ctrl+Tab buffer cycling fixed. + May still be some rare bugs here. +
    • +
    • + Crash fixed when decoding an error message that appears similar to a + Borland error message. +
    • +
    • + Fix to auto-scrolling allows containers to implement enhanced double click selection. +
    • +
    • + Hang fixed in idle word wrap. +
    • +
    • + Crash fixed in hotspot display code.. +
    • +
    • + SciTE on Windows Incremental Search no longer moves caret back. +
    • +
    • + SciTE hang fixed when performing a replace with a find string that + matched zero length strings such as ".*". +
    • +
    • + SciTE no longer styles the whole file when saving buffer fold state + as that was slow. +
    • +
    +

    + Release 1.57 +

    +
      +
    • + Released on 27 November 2003. +
    • +
    • + SciTE remembers folding of each buffer. +
    • +
    • + Lexer for Erlang language. +
    • +
    • + Scintilla allows setting the set of white space characters. +
    • +
    • + Scintilla has 'stuttered' page movement commands to first move + to top or bottom within current visible lines before scrolling. +
    • +
    • + Scintilla commands for moving to end of words. +
    • +
    • + Incremental line wrap enabled on Windows. +
    • +
    • + SciTE PDF exporter produces output that is more compliant with reader + applications, is smaller and allows more configuration. + HTML exporter optimizes size of output files. +
    • +
    • + SciTE defines properties PLAT_WINNT and PLAT_WIN95 on the + corresponding platforms. +
    • +
    • + SciTE can adjust the line margin width to fit the largest line number. + The line.numbers property is split between line.margin.visible and + line.margin.width. +
    • +
    • + SciTE on GTK+ allows user defined menu accelerators. + Alt can be included in user.shortcuts. +
    • +
    • + SciTE Language menu can have items commented out. +
    • +
    • + SciTE on Windows Go to dialog allows choosing a column number as + well as a line number. +
    • +
    • + SciTE on GTK+ make file uses prefix setting more consistently. +
    • +
    • + Bug fixed that caused word wrapping to fail to display all text. +
    • +
    • + Crashing bug fixed in GTK+ version of Scintilla when using GDK fonts + and opening autocompletion. +
    • +
    • + Bug fixed in Scintilla SCI_GETSELTEXT where an extra NUL + was included at end of returned string +
    • +
    • + Crashing bug fixed in SciTE z-order switching implementation. +
    • +
    • + Hanging bug fixed in Perl lexer. +
    • +
    • + SciTE crashing bug fixed for using 'case' without argument in style definition. +
    • +
    +

    + Release 1.56 +

    +
      +
    • + Released on 25 October 2003. +
    • +
    • + Rectangular selection can be performed using the keyboard. + Greater programmatic control over rectangular selection. + This has caused several changes to key bindings. +
    • +
    • + SciTE Replace In Selection works on rectangular selections. +
    • +
    • + Improved lexer for TeX, new lexer for Metapost and other support for these + languages. +
    • +
    • + Lexer for PowerBasic. +
    • +
    • + Lexer for Forth. +
    • +
    • + YAML lexer improved to include error styling. +
    • +
    • + Perl lexer improved to correctly handle more cases. +
    • +
    • + Assembler lexer updated to support single-quote strings and fix some + problems. +
    • +
    • + SciTE on Windows can switch between buffers in order of use (z-order) rather + than static order. +
    • +
    • + SciTE supports adding an extension for "Open Selected Filename". + The openpath setting works on GTK+. +
    • +
    • + SciTE can Export as XML. +
    • +
    • + SciTE $(SelHeight) variable gives a more natural result for empty and whole line + selections. +
    • +
    • + Fixes to wrapping problems, such as only first display line being visible in some + cases. +
    • +
    • + Fixes to hotspot to only highlight when over the hotspot, only use background + colour when set and option to limit hotspots to a single line. +
    • +
    • + Small fixes to FORTRAN lexing and folding. +
    • +
    • + SQL lexer treats single quote strings as a separate class to double quote strings.. +
    • +
    • + Scintilla made compatible with expectations of container widget in GTK+ 2.3. +
    • +
    • + Fix to strip out pixmap ID when automatically choosing from an autocompletion + list with only one element. +
    • +
    • + SciTE bug fixed where UTF-8 files longer than 128K were gaining more than one + BOM. +
    • +
    • + Crashing bug fixed in SciTE on GTK+ where using "Stop Executing" twice leads + to all applications exiting. +
    • +
    • + Bug fixed in autocompletion scrolling on GTK+ 2 with a case sensitive list. + The ListBox::Sort method is no longer needed or available so platform + maintainers should remove it. +
    • +
    • + SciTE check.if.already.open setting removed from GTK+ version as unmaintained. +
    • +
    +

    + Release 1.55 +

    +
      +
    • + Released on 25 September 2003. +
    • +
    • + Fix a crashing bug in indicator display in Scintilla. +
    • +
    • + GTK+ version now defaults to building for GTK+ 2 rather than 1. +
    • +
    • + Mingw make file detects compiler version and avoids options + that are cause problems for some versions. +
    • +
    • + Large performance improvement on GTK+ 2 for long lines. +
    • +
    • + Incremental line wrap on GTK+. +
    • +
    • + International text entry works much better on GTK+ with particular + improvements for Baltic languages and languages that use 'dead' accents. + NUL key events such as those generated by some function keys, ignored. +
    • +
    • + Unicode clipboard support on GTK+. +
    • +
    • + Indicator type INDIC_BOX draws a rectangle around the text. +
    • +
    • + Clarion language support. +
    • +
    • + YAML language support. +
    • +
    • + MPT LOG language support. +
    • +
    • + On Windows, SciTE can switch buffers based on activation order rather + than buffer number. +
    • +
    • + SciTE save.on.deactivate saves all buffers rather than just the current buffer. +
    • +
    • + Lua lexer handles non-ASCII characters correctly. +
    • +
    • + Error lexer understands Borland errors with pathnames that contain space. +
    • +
    • + On GTK+ 2, autocompletion uses TreeView rather than deprecated CList. +
    • +
    • + SciTE autocompletion removed when expand abbreviation command used. +
    • +
    • + SciTE calltips support overloaded functions. +
    • +
    • + When Save fails in SciTE, choice offered to Save As. +
    • +
    • + SciTE message boxes on Windows may be moved to front when needed. +
    • +
    • + Indicators drawn correctly on wrapped lines. +
    • +
    • + Regular expression search no longer matches characters with high bit + set to characters without high bit set. +
    • +
    • + Hang fixed in backwards search in multi byte character documents. +
    • +
    • + Hang fixed in SciTE Mark All command when wrap around turned off. +
    • +
    • + SciTE Incremental Search no longer uses hot keys on Windows. +
    • +
    • + Calltips draw non-ASCII characters correctly rather than as arrows. +
    • +
    • + SciTE crash fixed when going to an error message with empty file name. +
    • +
    • + Bugs fixed in XPM image handling code. +
    • +
    +

    + Release 1.54 +

    +
      +
    • + Released on 12 August 2003. +
    • +
    • + SciTE on GTK+ 2.x can display a tab bar. +
    • +
    • + SciTE on Windows provides incremental search. +
    • +
    • + Lexer for PostScript. +
    • +
    • + Lexer for the NSIS scripting language. +
    • +
    • + New lexer for POV-Ray Scene Description Language + replaces previous implementation. +
    • +
    • + Lexer for the MMIX Assembler language. +
    • +
    • + Lexer for the Scriptol language. +
    • +
    • + Incompatibility: SQL keywords are specified in lower case rather than upper case. + SQL lexer allows double quoted strings. +
    • +
    • + Pascal lexer: character constants that start with '#' understood, + '@' only allowed within assembler blocks, + '$' can be the start of a number, + initial '.' in 0..constant not treated as part of a number, + and assembler blocks made more distinctive. +
    • +
    • + Lua lexer allows '.' in keywords. + Multi-line strings and comments can be folded. +
    • +
    • + CSS lexer handles multiple psuedoclasses. +
    • +
    • + Properties file folder works for INI file format. +
    • +
    • + Hidden indicator style allows the container to mark text within Scintilla + without there being any visual effect. +
    • +
    • + SciTE does not prompt to save changes when the buffer is empty and untitled. +
    • +
    • + Modification notifications caused by SCI_INSERTSTYLEDSTRING + now include the contents of the insertion. +
    • +
    • + SCI_MARKERDELETEALL deletes all the markers on a line + rather than just the first match. +
    • +
    • + Better handling of 'dead' accents on GTK+ 2 for languages + that use accented characters. +
    • +
    • + SciTE now uses value of output.vertical.size property. +
    • +
    • + Crash fixed in SciTE autocompletion on long lines. +
    • +
    • + Crash fixed in SciTE comment command on long lines. +
    • +
    • + Bug fixed with backwards regular expression search skipping + every second match. +
    • +
    • + Hang fixed with regular expression replace where both target and replacement were empty. +
    • +
    +

    + Release 1.53 +

    +
      +
    • + Released on 16 May 2003. +
    • +
    • + On GTK+ 2, encodings other than ASCII, Latin1, and Unicode are + supported for both display and input using iconv. +
    • +
    • + External lexers supported on GTK+/Linux. + External lexers must now be explicitly loaded with SCI_LOADLEXERLIBRARY + rather than relying upon a naming convention and automatic loading. +
    • +
    • + Support of Lout typesetting language. +
    • +
    • + Support of E-Scripts language used in the POL Ultima Online Emulator. +
    • +
    • + Scrolling and drawing performance on GTK+ enhanced, particularly for GTK+ 2.x + with an extra window for the text area avoiding conflicts with the scroll bars. +
    • +
    • + CopyText and CopyRange methods in Scintilla allow container to + easily copy to the system clipboard. +
    • +
    • + Line Copy command implemented and bound to Ctrl+Shift+T. +
    • +
    • + Scintilla APIs PositionBefore and PositionAfter can be used to iterate through + a document taking into account the encoding and multi-byte characters. +
    • +
    • + C++ folder can fold on the "} else {" line of an if statement by setting + fold.at.else property to 1. +
    • +
    • + C++ lexer allows an extra set of keywords. +
    • +
    • + Property names and thus abbreviations may be non-ASCII. +
    • +
    • + Removed attempt to load a file when setting properties that was + part of an old scripting experiment. +
    • +
    • + SciTE no longer warns about a file not existing when opening + properties files from the Options menu as there is a good chance + the user wants to create one. +
    • +
    • + Bug fixed with brace recognition in multi-byte encoded files where a partial + character matched a brace byte. +
    • +
    • + More protection against infinite loops or recursion with recursive property definitions. +
    • +
    • + On Windows, cursor will no longer disappear over margins in custom builds when + cursor resource not present. The Windows default cursor is displayed instead. +
    • +
    • + load.on.activate fixed in SciTE as was broken in 1.52. +
    • +
    +

    + Release 1.52 +

    +
      +
    • + Released on 17 April 2003. +
    • +
    • + Pango font support on GTK+ 2. + Unicode input improved on GTK+ 2. +
    • +
    • + Hotspot style implemented in Scintilla. +
    • +
    • + Small up and down arrows can be displayed in calltips and the container + is notified when the mouse is clicked on a calltip. + Normal and selected calltip text colours can be set. +
    • +
    • + POSIX compatibility flag in Scintilla regular expression search + interprets bare ( and ) as tagged sections. +
    • +
    • + Error message lexer tightened to yield fewer false matches. + Recognition of Lahey and Intel FORTRAN error formats. +
    • +
    • + Scintilla keyboard commands for moving to start and end of + screen lines rather than document lines, unless already there + where these keys move to the start or end of the document line. +
    • +
    • + Line joining command. +
    • +
    • + Lexer for POV-Ray. +
    • +
    • + Calltips on Windows are no longer clipped by the parent window. +
    • +
    • + Autocompletion lists are cancelled when focus leaves their parent window. +
    • +
    • + Move to next/previous empty line delimited paragraph key commands. +
    • +
    • + SciTE hang fixed with recursive property definitions by placing limit + on number of substitutions performed. +
    • +
    • + SciTE Export as PDF reenabled and works. +
    • +
    • + Added loadsession: command line command to SciTE. +
    • +
    • + SciTE option to quit application when last document closed. +
    • +
    • + SciTE option to ask user if it is OK to reload a file that has been + modified outside SciTE. +
    • +
    • + SciTE option to automatically save before running particular command tools + or to ask user or to not save. +
    • +
    • + SciTE on Windows 9x will write a Ctrl+Z to the process input pipe before + closing the pipe when running tool commands that take input. +
    • +
    • + Added a manifest resource to SciTE on Windows to enable Windows XP + themed UI. +
    • +
    • + SciTE calltips handle nested calls and other situations better. +
    • +
    • + CSS lexer improved. +
    • +
    • + Interface to platform layer changed - Surface initialisation now requires + a WindowID parameter. +
    • +
    • + Bug fixed with drawing or measuring long pieces of text on Windows 9x + by truncating the pieces. +
    • +
    • + Bug fixed with SciTE on GTK+ where a user shortcut for a visible character + inserted the character as well as executing the command. +
    • +
    • + Bug fixed where primary selection on GTK+ was reset by + Scintilla during creation. +
    • +
    • + Bug fixed where SciTE would close immediately on startup + when using save.session. +
    • +
    • + Crash fixed when entering '\' in LaTeX file. +
    • +
    • + Hang fixed when '#' last character in VB file. +
    • +
    • + Crash fixed in error message lexer. +
    • +
    • + Crash fixed when searching for long regular expressions. +
    • +
    • + Pressing return when nothing selected in user list sends notification with + empty text rather than random text. +
    • +
    • + Mouse debouncing disabled on Windows as it interfered with some + mouse utilities. +
    • +
    • + Bug fixed where overstrike mode inserted before rather than replaced last + character in document. +
    • +
    • + Bug fixed with syntax highlighting of Japanese text. +
    • +
    • + Bug fixed in split lines function. +
    • +
    • + Cosmetic fix to SciTE tab bar on Windows when window resized. + Focus sticks to either pane more consistently. +
    • +
    +

    + Release 1.51 +

    +
      +
    • + Released on 16 February 2003. +
    • +
    • + Two phase drawing avoids cutting off text that overlaps runs by drawing + all the backgrounds of a line then drawing all the text transparently. + Single phase drawing is an option. +
    • +
    • + Scintilla method to split lines at a particular width by adding new line + characters. +
    • +
    • + The character used in autocompletion lists to separate the text from the image + number can be changed. +
    • +
    • + The scrollbar range will automatically expand when the caret is moved + beyond the current range. + The scroll bar is updated when SCI_SETXOFFSET is called. +
    • +
    • + Mouse cursors on GTK+ improved to be consistent with other applications + and the Windows version. +
    • +
    • + Horizontal scrollbar on GTK+ now disappears in wrapped mode. +
    • +
    • + Scintilla on GTK+ 2: mouse wheel scrolling, cursor over scrollbars, focus, + and syntax highlighting now work. + gtk_selection_notify avoided for compatibility with GTK+ 2.2. +
    • +
    • + Fold margin colours can now be set. +
    • +
    • + SciTE can be built for GTK+ 2. +
    • +
    • + SciTE can optionally preserve the undo history over an automatic file reload. +
    • +
    • + Tags can optionally be case insensitive in XML and HTML. +
    • +
    • + SciTE on Windows handles input to tool commands in a way that should avoid + deadlock. Output from tools can be used to replace the selection. +
    • +
    • + SciTE on GTK+ automatically substitutes '|' for '/' in menu items as '/' + is used to define the menu hierarchy. +
    • +
    • + Optional buffer number in SciTE title bar. +
    • +
    • + Crash fixed in SciTE brace matching. +
    • +
    • + Bug fixed where automatic scrolling past end of document + flipped back to the beginning. +
    • +
    • + Bug fixed where wrapping caused text to disappear. +
    • +
    • + Bug fixed on Windows where images in autocompletion lists were + shown on the wrong item. +
    • +
    • + Crash fixed due to memory bug in autocompletion lists on Windows. +
    • +
    • + Crash fixed when double clicking some error messages. +
    • +
    • + Bug fixed in word part movement where sometimes no movement would occur. +
    • +
    • + Bug fixed on Windows NT where long text runs were truncated by + treating NT differently to 9x where there is a limitation. +
    • +
    • + Text in not-changeable style works better but there remain some cases where + it is still possible to delete text protected this way. +
    • +
    +

    + Release 1.50 +

    +
      +
    • + Released on 24 January 2003. +
    • +
    • + Autocompletion lists may have a per-item pixmap. +
    • +
    • + Autocompletion lists allow Unicode text on Windows. +
    • +
    • + Scintilla documentation rewritten. +
    • +
    • + Additional DBCS encoding support in Scintilla on GTK+ primarily aimed at + Japanese EUC encoding. +
    • +
    • + CSS (Cascading Style Sheets) lexer added. +
    • +
    • + diff lexer understands some more formats. +
    • +
    • + Fold box feature is an alternative way to show the structure of code. +
    • +
    • + Avenue lexer supports multiple keyword lists. +
    • +
    • + The caret may now be made invisible by setting the caret width to 0. +
    • +
    • + Python folder attaches comments before blocks to the next block rather + than the previous block. +
    • +
    • + SciTE openpath property on Windows searches a path for files that are + the subject of the Open Selected Filename command. +
    • +
    • + The localisation file name can be changed with the locale.properties property. +
    • +
    • + On Windows, SciTE can pipe the result of a string expression into a command line tool. +
    • +
    • + On Windows, SciTE's Find dialog has a Mark All button. +
    • +
    • + On Windows, there is an Insert Abbreviation command that allows a choice from + the defined abbreviations and inserts the selection into the abbreviation at the + position of a '|'. +
    • +
    • + Minor fixes to Fortran lexer. +
    • +
    • + fold.html.preprocessor decides whether to fold <? and ?>. + Minor improvements to PHP folding. +
    • +
    • + Maximum number of keyword lists allowed increased from 6 to 9. +
    • +
    • + Duplicate line command added with default assignment to Ctrl+D. +
    • +
    • + SciTE sets $(Replacements) to the number of replacements made by the + Replace All command. $(CurrentWord) is set to the word before the caret if the caret + is at the end of a word. +
    • +
    • + Opening a SciTE session now loads files in remembered order, sets the current file + as remembered, and moves the caret to the remembered line. +
    • +
    • + Bugs fixed with printing on Windows where line wrapping was causing some text + to not print. +
    • +
    • + Bug fixed with Korean Input Method Editor on Windows. +
    • +
    • + Bugs fixed with line wrap which would sometimes choose different break positions + after switching focus away and back. +
    • +
    • + Bug fixed where wheel scrolling had no effect on GTK+ after opening a fold. +
    • +
    • + Bug fixed with file paths containing non-ASCII characters on Windows. +
    • +
    • + Crash fixed with printing on Windows after defining pixmap marker. +
    • +
    • + Crash fixed in makefile lexer when first character on line was '='. +
    • +
    • + Bug fixed where local properties were not always being applied. +
    • +
    • + Ctrl+Keypad* fold command works on GTK+. +
    • +
    • + Hangs fixed in SciTE's Replace All command when replacing regular expressions '^' + or '$'. +
    • +
    • + SciTE monospace setting behaves more sensibly. +
    • +
    +

    + Release 1.49 +

    +
      +
    • + Released on 1 November 2002. +
    • +
    • + Unicode supported on GTK+. To perform well, this added a font cache to GTK+ + and to make that safe, a mutex is used. The mutex requires the application to link in + the threading library by evaluating `glib-config --libs gthread`. A Unicode locale + should also be set up by a call like setlocale(LC_CTYPE, "en_US.UTF-8"). + scintilla_release_resources function added to release mutex. +
    • +
    • + FORTRAN and assembler lexers added along with other support for these + languages in SciTE. +
    • +
    • + Ada lexer improved handling of based numbers, identifier validity and attributes + distinguished from character literals. +
    • +
    • + Lua lexer handles block comments and a deep level of nesting for literal strings + and block comments. +
    • +
    • + Errorlist lexer recognises PHP error messages. +
    • +
    • + Variant of the C++ lexer with case insensitive keywords + called cppnocase. Whitespace in preprocessor text handled more correctly. +
    • +
    • + Folder added for Perl. +
    • +
    • + Compilation with GCC 3.2 supported. +
    • +
    • + Markers can be pixmaps. +
    • +
    • + Lines are wrapped when printing. + Bug fixed which printed line numbers in different styles. +
    • +
    • + Text can be appended to end with AppendText method. +
    • +
    • + ChooseCaretX method added. +
    • +
    • + Vertical scroll bar can be turned off with SetVScrollBar method. +
    • +
    • + SciTE Save All command saves all buffers. +
    • +
    • + SciTE localisation compares keys case insensitively to make translations more flexible. +
    • +
    • + SciTE detects a utf-8 coding cookie "coding: utf-8" in first two + lines and goes into Unicode mode. +
    • +
    • + SciTE key bindings are definable. +
    • +
    • + SciTE Find in Files dialog can display directory browser to + choose directory to search. +
    • +
    • + SciTE enabling of undo and redo toolbar buttons improved. +
    • +
    • + SciTE on Windows file type filters in open dialog sorted. +
    • +
    • + Fixed crashing bug when using automatic tag closing in XML or HTML. +
    • +
    • + Fixed bug on Windows causing very long (>64K) lines to not display. +
    • +
    • + Fixed bug in backwards regular expression searching. +
    • +
    • + Fixed bug in calltips where wrong argument was highlighted. +
    • +
    • + Fixed bug in tab timmy feature when file has line feed line endings. +
    • +
    • + Fixed bug in compiling without INCLUDE_DEPRECATED_FEATURES + defined. +
    • +
    +

    + Release 1.48 +

    +
      +
    • + Released on 9 September 2002. +
    • +
    • + Improved Pascal lexer with context sensitive keywords + and separate folder which handles //{ and //} folding comments and + {$region} and {$end} folding directives. + The "case" statement now folds correctly. +
    • +
    • + C++ lexer correctly handles comments on preprocessor lines. +
    • +
    • + New commands for moving to beginning and end of display lines when in line + wrap mode. Key bindings added for these commands. +
    • +
    • + New marker symbols that look like ">>>" and "..." which can be used for + interactive shell prompts for Python. +
    • +
    • + The foreground and background colours of visible whitespace can be chosen + independent of the colours chosen for the lexical class of that whitespace. +
    • +
    • + Per line data optimised by using an exponential allocation scheme. +
    • +
    • + SciTE API file loading optimised. +
    • +
    • + SciTE for GTK+ subsystem 2 documented. The exit status of commands + is decoded into more understandable fields. +
    • +
    • + SciTE find dialog remembers previous find string when there is no selection. + Find in Selection button disabled when selection is rectangular as command + did not work. +
    • +
    • + Shift+Enter made equivalent to Enter to avoid users having to let go of + the shift key when typing. Avoids the possibility of entering single carriage + returns in a file that contains CR+LF line ends. +
    • +
    • + Autocompletion does not immediately disappear when the length parameter + to SCI_AUTOCSHOW is 0. +
    • +
    • + SciTE focuses on the editor pane when File | New executed and when the + output pane is closed with F8. Double clicking on a non-highlighted output + pane line selects the word under the cursor rather than seeking the next + highlighted line. +
    • +
    • + SciTE director interface implements an "askproperty" command. +
    • +
    • + SciTE's Export as LaTeX output improved. +
    • +
    • + Better choice of autocompletion displaying above the caret rather then + below when that is more sensible. +
    • +
    • + Bug fixed where context menu would not be completely visible if invoked + when cursor near bottom or left of screen. +
    • +
    • + Crashing bug fixed when displaying long strings on GTK+ caused failure of X server + by displaying long text in segments. +
    • +
    • + Crashing bug fixed on GTK+ when a Scintilla window was removed from its parent + but was still the selection owner. +
    • +
    • + Bug fixed on Windows in Unicode mode where not all characters on a line + were displayed when that line contained some characters not in ASCII. +
    • +
    • + Crashing bug fixed in SciTE on Windows with clearing output while running command. +
    • +
    • + Bug fixed in SciTE for GTK+ with command completion not detected when + no output was produced by the command. +
    • +
    • + Bug fixed in SciTE for Windows where menus were not shown translated. +
    • +
    • + Bug fixed where words failed to display in line wrapping mode with visible + line ends. +
    • +
    • + Bug fixed in SciTE where files opened from a session file were not closed. +
    • +
    • + Cosmetic flicker fixed when using Ctrl+Up and Ctrl+Down with some caret policies. +
    • +
    +

    + Release 1.47 +

    +
      +
    • + Released on 1 August 2002. +
    • +
    • + Support for GTK+ 2 in Scintilla. International input methods not supported + on GTK+2. +
    • +
    • + Line wrapping performance improved greatly. +
    • +
    • + New caret policy implementation that treats horizontal and vertical + positioning equivalently and independently. Old caret policy methods + deprecated and not all options work correctly with old methods. +
    • +
    • + Extra fold points for C, C++, Java, ... for fold comments //{ .. //} and + #if / #ifdef .. #endif and the #region .. #endregion feature of C#. +
    • +
    • + Scintilla method to find the height in pixels of a line. Currently returns the + same result for every line as all lines are same height. +
    • +
    • + Separate make file, scintilla_vc6.mak, for Scintilla to use Visual C++ + version 6 since main makefile now assumes VS .NET. + VS .NET project files available for combined Scintilla and + SciTE in scite/boundscheck. +
    • +
    • + SciTE automatically recognises Unicode files based + on their Byte Order Marks and switches to Unicode mode. + On Windows, where SciTE supports Unicode display, this + allows display of non European characters. + The file is saved back into the same character encoding unless + the user decides to switch using the File | Encoding menu. +
    • +
    • + Handling of character input changed so that a fillup character, typically '(' + displays a calltip when an autocompletion list was being displayed. +
    • +
    • + Multiline strings lexed better for C++ and Lua. +
    • +
    • + Regular expressions in JavaScript within hypertext files are lexed better. +
    • +
    • + On Windows, Scintilla exports a function called Scintilla_DirectFunction + that can be used the same as the function returned by GetDirectFunction. +
    • +
    • + Scintilla converts line endings of text obtained from the clipboard to + the current default line endings. +
    • +
    • + New SciTE property ensure.final.line.end can ensure that saved files + always end with a new line as this is required by some tools. + The ensure.consistent.line.ends property ensures all line ends are the + current default when saving files. + The strip.trailing.spaces property now works on the buffer so the + buffer in memory and the file on disk are the same after a save is performed. +
    • +
    • + The SciTE expand abbreviation command again allows '|' characters + in expansions to be quoted by using '||'. +
    • +
    • + SciTE on Windows can send data to the find tool through standard + input rather than using a command line argument to avoid problems + with quoting command line arguments. +
    • +
    • + The Stop Executing command in SciTE on Windows improved to send + a Ctrl+Z character to the tool. Better messages when stopping a tool. +
    • +
    • + Autocompletion can automatically "fill up" when one of a set of characters is + type with the autocomplete.<lexer>.fillups property. +
    • +
    • + New predefined properties in SciTE, SelectionStartColumn, SelectionStartLine, + SelectionEndColumn, SelectionEndLine can be used to integrate with other + applications. +
    • +
    • + Environment variables are available as properties in SciTE. +
    • +
    • + SciTE on Windows keeps status line more current. +
    • +
    • + Abbreviations work in SciTE on Linux when first opened. +
    • +
    • + File saving fixed in SciTE to ensure files are not closed when they can not be + saved because of file permissions. Also fixed a problem with buffers that + caused files to not be saved. +
    • +
    • + SciTE bug fixed where monospace mode not remembered when saving files. + Some searching options now remembered when switching files. +
    • +
    • + SciTE on Linux now waits on child termination when it shuts a child down + to avoid zombies. +
    • +
    • + SciTE on Linux has a Print menu command that defaults to invoking a2ps. +
    • +
    • + Fixed incorrect highlighting of indentation guides in SciTE for Python. +
    • +
    • + Crash fixed in Scintilla when calling GetText for 0 characters. +
    • +
    • + Exporting as LaTeX improved when processing backslashes and tabs + and setting up font. +
    • +
    • + Crash fixed in SciTE when exporting or copying as RTF. +
    • +
    • + SciTE session loading fixed to handle more than 10 files in session. +
    • +
    +

    + Release 1.46 +

    +
      +
    • + Released on 10 May 2002. +
    • +
    • + Set of lexers compiled into Scintilla can now be changed by adding and + removing lexer source files from scintilla/src and running LexGen.py. +
    • +
    • + SCN_ZOOM notification provided by Scintilla when user changes zoom level. + Method to determine width of strings in pixels so that elements can be sized + relative to text size. + SciTE changed to keep line number column displaying a given + number of characters. +
    • +
    • + The logical width of the document used to determine scroll bar range can be set. +
    • +
    • + Setting to allow vertical scrolling to display last line at top rather than + bottom of window. +
    • +
    • + Read-only mode improved to avoid changing the selection in most cases + when a modification is attempted. Drag and drop cursors display correctly + for read-only in some cases. +
    • +
    • + Visual C++ options in make files changed to suit Visual Studio .NET. +
    • +
    • + Scintilla.iface includes feature types for enumerations and lexers. +
    • +
    • + Lua lexer improves handling of literal strings and copes with nested literal strings. +
    • +
    • + Diff lexer changed to treat lines starting with "***" similarly to "---". + Symbolic names defined for lexical classes. +
    • +
    • + nncrontab lexer improved. +
    • +
    • + Turkish fonts (iso8859-9) supported on GTK+. +
    • +
    • + Automatic close tag feature for XML and HTML in SciTE. +
    • +
    • + Automatic indentation in SciTE improved. +
    • +
    • + Maximum number of buffers available in SciTE increased. May be up to 100 + although other restrictions on menu length limit the real maximum. +
    • +
    • + Save a Copy command added to SciTE. +
    • +
    • + Export as TeX command added to SciTE. +
    • +
    • + Export as HTML command in SciTE respects Use Monospaced Font and + background colour settings. +
    • +
    • + Compilation problem on Solaris fixed. +
    • +
    • + Order of files displayed for SciTE's previous and next menu and key commands + are now consistent. +
    • +
    • + Saving of MRU in recent file changed so files open when SciTE quit + are remembered. +
    • +
    • + More variants of ctags tags handled by Open Selected Filename in SciTE. +
    • +
    • + JavaScript embedded in XML highlighted again. +
    • +
    • + SciTE status bar updated after changing parameters in case they are being + displayed in status bar. +
    • +
    • + Crash fixed when handling some multi-byte languages. +
    • +
    • + Crash fixed when replacing end of line characters. +
    • +
    • + Bug in SciTE fixed in multiple buffer mode where automatic loading + turned on could lead to losing file contents. +
    • +
    • + Bug in SciTE on GTK+ fixed where dismissing dialogs with close box led to + those dialogs never being shown again. +
    • +
    • + Bug in SciTE on Windows fixed where position.tile with default positions + led to SciTE being positioned off-screen. +
    • +
    • + Bug fixed in read-only mode, clearing all deletes contraction state data + leading to it not being synchronized with text. +
    • +
    • + Crash fixed in SciTE on Windows when tab bar displayed. +
    • +
    +

    + Release 1.45 +

    +
      +
    • + Released on 15 March 2002. +
    • +
    • + Line layout cache implemented to improve performance by maintaining + the positioning of characters on lines. Can be set to cache nothing, + the line with the caret, the visible page or the whole document. +
    • +
    • + Support, including a new lexer, added for Matlab programs. +
    • +
    • + Lua folder supports folding {} ranges and compact mode. + Lua lexer styles floating point numbers in number style instead of + setting the '.' in operator style. + Up to 6 sets of keywords. + Better support for [[ although only works well + when all on one line. +
    • +
    • + Python lexer improved to handle floating point numbers that contain negative + exponents and that start with '.'. +
    • +
    • + When performing a rectangular paste, the caret now remains at the + insertion point. +
    • +
    • + On Windows with a wheel mouse, page-at-a-time mode is recognised. +
    • +
    • + Read-only mode added to SciTE with a property to initialise it and another property, + $(ReadOnly) available to show this mode in the status bar. +
    • +
    • + SciTE status bar can show the number of lines in the selection + with the $(SelHeight) property. +
    • +
    • + SciTE's "Export as HTML" command uses the current character set to produce + correct output for non-Western-European character sets, such as Russian. +
    • +
    • + SciTE's "Export as RTF" fixed to produce correct output when file contains '\'. +
    • +
    • + SciTE goto command accepts a column as well as a line. + If given a column, it selects the word at that column. +
    • +
    • + SciTE's Build, Compile and Go commands are now disabled if no + action has been assigned to them. +
    • +
    • + The Refresh button in the status bar has been removed from SciTE on Windows. +
    • +
    • + Bug fixed in line wrap mode where cursor up or down command did not work. +
    • +
    • + Some styling bugs fixed that were due to a compilation problem with + gcc and inline functions with same name but different code. +
    • +
    • + The way that lexers loop over text was changed to avoid accessing beyond the + end or setting beyond the end. May fix some bugs and make the code safer but + may also cause new bugs. +
    • +
    • + Bug fixed in HTML lexer's handling of SGML. +
    • +
    • + Bug fixed on GTK+/X where lines wider than 32767 pixels did not display. +
    • +
    • + SciTE bug fixed with file name generation for standard property files. +
    • +
    • + SciTE bug fixed with Open Selected Filename command when used with + file name and line number combination. +
    • +
    • + In SciTE, indentation and tab settings stored with buffers so maintained correctly + as buffers selected. + The properties used to initialise these settings can now be set separately for different + file patterns. +
    • +
    • + Thread safety improved on Windows with a critical section protecting the font + cache and initialisation of globals performed within Scintilla_RegisterClasses. + New Scintilla_ReleaseResources call provided to allow explicit freeing of resources + when statically bound into another application. Resources automatically freed + in DLL version. The window classes are now unregistered as part of resource + freeing which fixes bugs that occurred in some containers such as Internet Explorer. +
    • +
    • + 'make install' fixed on Solaris. +
    • +
    • + Bug fixed that could lead to a file being opened twice in SciTE. +
    • +
    +

    + Release 1.44 +

    +
      +
    • + Released on 4 February 2002. +
    • +
    • + Crashing bug fixed in Editor::Paint. +
    • +
    • + Lua lexer no longer treats '.' as a word character and + handles 6 keyword sets. +
    • +
    • + WordStartPosition and WordEndPosition take an onlyWordCharacters + argument. +
    • +
    • + SciTE option for simplified automatic indentation which repeats + the indentation of the previous line. +
    • +
    • + Compilation fix on Alpha because of 64 bit. +
    • +
    • + Compilation fix for static linking. +
    • +
    • + Limited maximum line length handled to 8000 characters as previous + value of 16000 was causing stack exhaustion crashes for some. +
    • +
    • + When whole document line selected, only the last display line gets + the extra selected rectangle at the right hand side rather than + every display line. +
    • +
    • + Caret disappearing bug fixed for the case that the caret was not on the + first display line of a document line. +
    • +
    • + SciTE bug fixed where untitled buffer containing text was sometimes + deleted without chance to save. +
    • +
    • + SciTE bug fixed where use.monospaced not working with + multiple buffers. +
    • +
    +

    + Release 1.43 +

    +
      +
    • + Released on 19 January 2002. +
    • +
    • + Line wrapping robustness and performance improved in Scintilla. +
    • +
    • + Line wrapping option added to SciTE for both edit and output panes. +
    • +
    • + Static linking on Windows handles cursor resource better. + Documentation of static linking improved. +
    • +
    • + Autocompletion has an option to delete any word characters after the caret + upon selecting an item. +
    • +
    • + FOX version identified by PLAT_FOX in Platform.h. +
    • +
    • + Calltips in SciTE use the calltip.<lexer>.word.characters setting to + correctly find calltips for functions that include characters like '$' which + is not normally considered a word character. +
    • +
    • + SciTE has a command to show help on itself which gets hooked up to displaying + SciTEDoc.html. +
    • +
    • + SciTE option calltip.<lexer>.end.definition to display help text on a + second line of calltip. +
    • +
    • + Fixed the handling of the Buffers menu on GTK+ to ensure current buffer + indicated and no warnings occur. + Changed some menu items on GTK+ version to be same as Windows version. +
    • +
    • + use.monospaced property for SciTE determines initial state of Use Monospaced Font + setting. +
    • +
    • + The SciTE Complete Symbol command now works when there are no word + characters before the caret, even though it is slow to display the whole set of + symbols. +
    • +
    • + Function names removed from SciTE's list of PHP keywords. The full list of + predefined functions is available from another web site mentioned on the + Extras page. +
    • +
    • + Crashing bug at startup on GTK+ for some configurations fixed. +
    • +
    • + Crashing bug on GTK+ on 64 bit platforms fixed. +
    • +
    • + Compilation problem with some compilers fixed in GTK+. +
    • +
    • + Japanese text entry improved on Windows 9x. +
    • +
    • + SciTE recent files directory problem on Windows when HOME and SciTE_HOME + environment variables not set is now the directory of the executable. +
    • +
    • + Session files no longer include untitled buffers. +
    • +
    +

    + Release 1.42 +

    +
      +
    • + Released on 24 December 2001. +
    • +
    • + Better localisation support including context menus and most messages. + Translations of the SciTE user interface available for Bulgarian, + French, German, Italian, Russian, and Turkish. +
    • +
    • + Can specify a character to use to indicate control characters + rather than having them displayed as mnemonics. +
    • +
    • + Scintilla key command for backspace that will not delete line + end characters. +
    • +
    • + Scintilla method to find start and end of words. +
    • +
    • + SciTE on GTK+ now supports the load.on.activate and save.on.deactivate + properties in an equivalent way to the Windows version. +
    • +
    • + The output pane of SciTE on Windows is now interactive so command line + utilities that prompt for input or confirmation can be used. +
    • +
    • + SciTE on Windows can choose directory for a "Find in Files" + command like the GTK+ version could. +
    • +
    • + SciTE can now load a set of API files rather than just one file. +
    • +
    • + ElapsedTime class added to Platform for accurate measurement of durations. + Used for debugging and for showing the user how long commands take in SciTE. +
    • +
    • + Baan lexer added. +
    • +
    • + In C++ lexer, document comment keywords no longer have to be at the start + of the line. +
    • +
    • + PHP lexer changed to match keywords case insensitively. +
    • +
    • + More shell keywords added. +
    • +
    • + SciTE support for VoiceXML added to xml.properties. +
    • +
    • + In SciTE the selection is not copied to the find field of the Search and Replace + dialogs if it contains end of line characters. +
    • +
    • + SciTE on Windows has a menu item to decide whether to respond to other + instances which are performing their check.if.already.open check. +
    • +
    • + SciTE accelerator key for Box Comment command changed to avoid problems + in non-English locales. +
    • +
    • + SciTE context menu includes Close command for the editor pane and + Hide command for the output pane. +
    • +
    • + output: command added to SciTE director interface to add text to the + output pane. The director interface can execute commands (such as tool + commands with subsystem set to 3) by sending a macro:run message. +
    • +
    • + SciTE on GTK+ will defer to the Window Manager for position if position.left or + position.top not set and for size if position.width or position.height not set. +
    • +
    • + SciTE on Windows has a position.tile property to place a second instance + to the right of the first. +
    • +
    • + Scintilla on Windows again supports EM_GETSEL and EM_SETSEL. +
    • +
    • + Problem fixed in Scintilla on Windows where control ID is no longer cached + as it could be changed by external code. +
    • +
    • + Problems fixed in SciTE on Windows when finding any other open instances at + start up when check.if.already.open is true. +
    • +
    • + Bugs fixed in SciTE where command strings were not always having + variables evaluated. +
    • +
    • + Bugs fixed with displaying partial double-byte and Unicode characters + in rectangular selections and at the edge when edge mode is EDGE_BACKGROUND. + Column numbers reported by GetColumn treat multiple byte characters as one column + rather than counting bytes. +
    • +
    • + Bug fixed with caret movement over folded lines. +
    • +
    • + Another bug fixed with tracking selection in secondary views when performing + modifications. +
    • +
    • + Horizontal scrolling and display of long lines optimised. +
    • +
    • + Cursor setting in Scintilla on GTK+ optimised. +
    • +
    • + Experimental changeable style attribute. + Set to false to make text read-only. + Currently only stops caret from being within not-changeable + text and does not yet stop deleting a range that contains + not-changeable text. + Can be used from SciTE by adding notchangeable to style entries. +
    • +
    • + Experimental line wrapping. + Currently has performance and appearence problems. +
    • +
    +

    + Release 1.41 +

    +
      +
    • + Released on 6 November 2001. +
    • +
    • + Changed Platform.h to not include platform headers. This lessens likelihood and impact of + name clashes from system headers and also speeds up compilation. + Renamed DrawText to DrawTextNoClip to avoid name clash. +
    • +
    • + Changed way word functions work to treat a sequence of punctuation as + a word. This is more sensible and also more compatible with other editors. +
    • +
    • + Cursor changes over the margins and selection on GTK+ platform. +
    • +
    • + SC_MARK_BACKGROUND is a marker that only changes the line's background colour. +
    • +
    • + Enhanced Visual Basic lexer handles character date and octal literals, + and bracketed keywords for VB.NET. There are two VB lexers, vb and vbscript + with type indication characters like ! and $ allowed at the end of identifiers + in vb but not vbscript. Lexer states now separate from those used for C++ and + names start with SCE_B. +
    • +
    • + Lexer added for Bullant language. +
    • +
    • + The horizontal scroll position, xOffset, is now exposed through the API. +
    • +
    • + The SCN_POSCHANGED notification is deprecated as it was causing confusion. + Use SCN_UPDATEUI instead. +
    • +
    • + Compilation problems fixed for some versions of gcc. +
    • +
    • + Support for WM_GETTEXT restored on Windows. +
    • +
    • + Double clicking on an autocompletion list entry works on GTK+. +
    • +
    • + Bug fixed with case insensitive sorts for autocompletion lists. +
    • +
    • + Bug fixed with tracking selection in secondary views when performing modifications. +
    • +
    • + SciTE's abbreviation expansion feature will now indent expansions to the current + indentation level if indent.automatic is on. +
    • +
    • + SciTE allows setting up of parameters to commands from a dialog and can also + show this dialog automatically to prompt for arguments when running a command. +
    • +
    • + SciTE's Language menu (formerly Options | Use Lexer) is now defined by the + menu.language property rather than being hardcoded. +
    • +
    • + The user interface of SciTE can be localised to a particular language by editing + a locale.properties file. +
    • +
    • + On Windows, SciTE will try to move to the front when opening a new file from + the shell and using check.if.already.open. +
    • +
    • + SciTE can display the file name and directory in the title bar in the form + "file @ directory" when title.full.path=2. +
    • +
    • + The SciTE time.commands property reports the time taken by a command as well + as its status when completed. +
    • +
    • + The SciTE find.files property is now a list separated by '|' characters and this list is + added into the Files pull down of the Find in Files dialog. +
    • +
    +

    + Release 1.40 +

    +
      +
    • + Released on 23 September 2001. +
    • +
    • + Removal of emulation of Win32 RichEdit control in core of Scintilla. + This change may be incompatible with existing client code. + Some emulation still done in Windows platform layer. +
    • +
    • + SGML support in the HTML/XML lexer. +
    • +
    • + SciTE's "Stop Executing" command will terminate GUI programs on + Windows NT and Windows 2000. +
    • +
    • + StyleContext class helps construct lexers that are simple and accurate. + Used in the C++, Eiffel, and Python lexers. +
    • +
    • + Clipboard operations in GTK+ version convert between platform '\n' line endings and + currently chosen line endings. +
    • +
    • + Any character in range 0..255 can be used as a marker. + This can be used to support numbered bookmarks, for example. +
    • +
    • + The default scripting language for ASP can be set. +
    • +
    • + New lexer and other support for crontab files used with the nncron scheduler. +
    • +
    • + Folding of Python improved. +
    • +
    • + The ` character is treated as a Python operator. +
    • +
    • + Line continuations ("\" at end of line) handled inside Python strings. +
    • +
    • + More consistent handling of line continuation ('\' at end of line) in + C++ lexer. + This fixes macro definitions that span more than one line. +
    • +
    • + C++ lexer can understand Doxygen keywords in doc comments. +
    • +
    • + SciTE on Windows allows choosing to open the "open" dialog on the directory + of the current file rather than in the default directory. +
    • +
    • + SciTE on Windows handles command line arguments in "check.if.already.open" + correctly when the current directory of the new instance is different to the + already open instance of SciTE. +
    • +
    • + "cwd" command (change working directory) defined for SciTE director interface. +
    • +
    • + SciTE "Export As HTML" produces better, more compliant, and shorter files. +
    • +
    • + SciTE on Windows allows several options for determining default file name + for exported files. +
    • +
    • + Automatic indentation of Python in SciTE fixed. +
    • +
    • + Exported HTML can support folding. +
    • +
    • + Bug fixed in SCI_GETTEXT macro command of director interface. +
    • +
    • + Cursor leak fixed on GTK+. +
    • +
    • + During SciTE shutdown, "identity" messages are no longer sent over the director interface. +
    • +
    +

    + Release 1.39 +

    +
      +
    • + Released on 22 August 2001. +
    • +
    • + Windows version requires msvcrt.dll to be available so will not work + on original Windows 95 version 1. The msvcrt.dll file is installed + by almost everything including Internet Explorer so should be available. +
    • +
    • + Flattened tree control style folding margin. The SciTE fold.plus option is + now fold.symbols and has more values for the new styles. +
    • +
    • + Mouse dwell events are generated when the user holds the mouse steady + over Scintilla. +
    • +
    • + PositionFromPointClose is like PositionFromPoint but returns + INVALID_POSITION when point outside window or after end of line. +
    • +
    • + Input of Hungarian and Russian characters in GTK+ version works by + truncating input to 8 bits if in the range of normal characters. +
    • +
    • + Better choices for font descriptors on GTK+ for most character sets. +
    • +
    • + GTK+ Scintilla is destroyed upon receiving destroy signal rather than + destroy_event signal. +
    • +
    • + Style setting that force upper or lower case text. +
    • +
    • + Case-insensitive autocompletion lists work correctly. +
    • +
    • + Keywords can be prefix based so ^GTK_ will treat all words that start + with GTK_ as keywords. +
    • +
    • + Horizontal scrolling can be jumpy rather than gradual. +
    • +
    • + GetSelText places a '\0' in the buffer if the selection is empty.. +
    • +
    • + EnsureVisible split into two methods EnsureVisible which will not scroll to show + the line and EnsureVisibleEnforcePolicy which may scroll. +
    • +
    • + Python folder has options to fold multi-line comments and triple quoted strings. +
    • +
    • + C++ lexer handles keywords before '.' like "this.x" in Java as keywords. + Compact folding mode option chooses whether blank lines after a structure are + folded with that structure. Second set of keywords with separate style supported. +
    • +
    • + Ruby lexer handles multi-line comments. +
    • +
    • + VB has folder. +
    • +
    • + PHP lexer has an operator style, handles "<?" and "?>" inside strings + and some comments. +
    • +
    • + TCL lexer which is just an alias for the C++ lexer so does not really + understand TCL syntax. +
    • +
    • + Error lines lexer has styles for Lua error messages and .NET stack traces. +
    • +
    • + Makefile lexer has a target style. +
    • +
    • + Lua lexer handles some [[]] string literals. +
    • +
    • + HTML and XML lexer have a SCE_H_SGML state for tags that + start with "<!". +
    • +
    • + Fixed Scintilla bugs with folding. When modifications were performed near + folded regions sometimes no unfolding occurred when it should have. Deleting a + fold causing character sometimes failed to update fold information correctly. +
    • +
    • + Better support for Scintilla on GTK+ for Win32 including separate + PLAT_GTK_WIN32 definition and correct handling of rectangular selection + with clipboard operations. +
    • +
    • + SciTE has a Tools | Switch Pane (Ctrl+F6) command to switch focus between + edit and output panes. +
    • +
    • + SciTE option output.scroll allows automatic scrolling of output pane to + be turned off. +
    • +
    • + Commands can be typed into the SciTE output pane similar to a shell window. +
    • +
    • + SciTE properties magnification and output magnification set initial zoom levels. +
    • +
    • + Option for SciTE comment block command to place comments at start of line. +
    • +
    • + SciTE for Win32 has an option to minimize to the tray rather than the task bar. +
    • +
    • + Close button on SciTE tool bar for Win32. +
    • +
    • + SciTE compiles with GCC 3.0. +
    • +
    • + SciTE's automatic indentation of C++ handles braces without preceding keyword + correctly. +
    • +
    • + Bug fixed with GetLine method writing past the end of where it should. +
    • +
    • + Bug fixed with mouse drag automatic scrolling when some lines were folded. +
    • +
    • + Bug fixed because caret XEven setting was inverted. +
    • +
    • + Bug fixed where caret was initially visible even though window was not focussed. +
    • +
    • + Bug fixed where some file names could end with "\\" which caused slow + downs on Windows 9x. +
    • +
    • + On Win32, SciTE Replace dialog starts with focus on replacement text. +
    • +
    • + SciTE Go to dialog displays correct current line. +
    • +
    • + Fixed bug with SciTE opening multiple files at once. +
    • +
    • + Fixed bug with Unicode key values reported to container truncated. +
    • +
    • + Fixed bug with unnecessary save point notifications. +
    • +
    • + Fixed bugs with indenting and unindenting at start of line. +
    • +
    • + Monospace Font setting behaves more consistently. +
    • +
    +

    + Release 1.38 +

    +
      +
    • + Released on 23 May 2001. +
    • +
    • + Loadable lexer plugins on Windows. +
    • +
    • + Ruby lexer and support. +
    • +
    • + Lisp lexer and support. +
    • +
    • + Eiffel lexer and support. +
    • +
    • + Modes for better handling of Tab and BackSpace keys within + indentation. Mode to avoid autocompletion list cancelling when + there are no viable matches. +
    • +
    • + ReplaceTarget replaced with two calls ReplaceTarget + (which is incompatible with previous ReplaceTarget) and + ReplaceTargetRE. Both of these calls have a count first + parameter which allows using strings containing nulls. + SearchInTarget and SetSearchFlags functions allow + specifying a search in several simple steps which helps + some clients which can not create structs or pointers easily. +
    • +
    • + Asian language input through an Input Method Editor works + on Windows 2000. +
    • +
    • + On Windows, control characters can be entered through use of + the numeric keypad in conjunction with the Alt key. +
    • +
    • + Document memory allocation changed to grow exponentially + which reduced time to load a 30 Megabyte file from + 1000 seconds to 25. Change means more memory may be used. +
    • +
    • + Word part movement keys now handled in Scintilla rather than + SciTE. +
    • +
    • + Regular expression '^' and '$' work more often allowing insertion + of text at start or end of line with a replace command. + Backslash quoted control characters \a, \b, \f, \t, and \v + recognised within sets. +
    • +
    • + Session files for SciTE. +
    • +
    • + Export as PDF command hidden in SciTE as it often failed. + Code still present so can be turned on by those willing to cope. +
    • +
    • + Bug fixed in HTML lexer handling % before > as end ASP + even when no start ASP encountered. + Bug fixed when scripts ended with a quoted string and + end tag was not seen. +
    • +
    • + Bug fixed on Windows where context menu key caused menu to + appear in corner of screen rather than within window. +
    • +
    • + Bug fixed in SciTE's Replace All command not processing + whole file when replace string longer than search string. +
    • +
    • + Bug fixed in SciTE's MRU list repeating entries if Ctrl+Tab + used when all entries filled. +
    • +
    • + ConvertEOLs call documentation fixed. +
    • +
    +

    + Release 1.37 +

    +
      +
    • + Released on 17 April 2001. +
    • +
    • + Bug fixed with scroll bars being invisible on GTK+ 1.2.9. +
    • +
    • + Scintilla and SciTE support find and replace using simple regular + expressions with tagged expressions. SciTE supports C '\' escapes + in the Find and Replace dialogs. + Replace in Selection available in SciTE. +
    • +
    • + Scintilla has a 'target' feature for replacing code rapidly without + causing display updates. +
    • +
    • + Scintilla and SciTE on GTK+ support file dropping from file managers + such as Nautilus and gmc. Files or other URIs dropped on Scintilla + result in a URIDropped notification. +
    • +
    • + Lexers may have separate Lex and Fold functions. +
    • +
    • + Lexer infrastructure improved to allow for plug in lexers and for referring + to lexers by name rather than by ID. +
    • +
    • + Ada lexer and support added. +
    • +
    • + Option in both Scintilla and SciTE to treat both left and right margin + as equally important when repositioning visible area in response to + caret movement. Default is to prefer visible area positioning which + minimises the horizontal scroll position thus favouring the left margin. +
    • +
    • + Caret line highlighting. +
    • +
    • + Commands to delete from the caret to the end of line and + from the caret to the beginning of line. +
    • +
    • + SciTE has commands for inserting and removing block comments and + for inserting stream comments. +
    • +
    • + SciTE Director interface uses C++ '\' escapes to send control characters. +
    • +
    • + SciTE Director interface adds more commands including support for macros. +
    • +
    • + SciTE has menu options for recording and playing macros which are visible + when used with a companion program that supports these features. +
    • +
    • + SciTE has an Expand Abbreviation command. + Abbreviations are stored in a global abbrev.properties file. +
    • +
    • + SciTE has a Full Screen command to switch between a normal window + size and using the full screen. On Windows, the menu bar can be turned + off when in full screen mode. +
    • +
    • + SciTE has a Use monospaced font command to switch between the normal + set of fonts and one size of a particular fixed width font. +
    • +
    • + SciTE's use of tabs can be controlled for particular file names + as well as globally. +
    • +
    • + The contents of SciTE's status bar can be defined by a property and + include variables. On Windows, several status bar definitions can be active + with a click on the status bar cycling through them. +
    • +
    • + Copy as RTF command in SciTE on Windows to allow pasting + styled text into word processors. +
    • +
    • + SciTE can allow the use of non-alphabetic characters in + Complete Symbol lists and can automatically display this autocompletion + list when a trigger character such as '.' is typed. + Complete word can be set to pop up when the user is typing a word and + there is only one matching word in the document. +
    • +
    • + SciTE lists the imported properties files on a menu to allow rapid + access to them. +
    • +
    • + SciTE on GTK+ improvements to handling accelerator keys and focus + in dialogs. Message boxes respond to key presses without the Alt key as + they have no text entries to accept normal keystrokes. +
    • +
    • + SciTE on GTK+ sets the application icon. +
    • +
    • + SciTE allows setting the colours used to indicate the current + error line. +
    • +
    • + Variables within PHP strings have own style. Keyword list updated. +
    • +
    • + Keyword list for Lua updated for Lua 4.0. +
    • +
    • + Bug fixed in rectangular selection where rectangle still appeared + selected after using cursor keys to move caret. +
    • +
    • + Bug fixed in C++ lexer when deleting a '{' controlling a folded range + led to that range becoming permanently invisible. +
    • +
    • + Bug fixed in Batch lexer where comments were not recognised. +
    • +
    • + Bug fixed with undo actions coalescing into steps incorrectly. +
    • +
    • + Bug fixed with Scintilla on GTK+ positioning scroll bars 1 pixel + over the Scintilla window leading to their sides being chopped off. +
    • +
    • + Bugs fixed in SciTE when doing some actions led to the start + or end of the file being displayed rather than the current location. +
    • +
    • + Appearance of calltips fixed to look like document text including + any zoom factor. Positioned to be outside current line even when + multiple fonts and sizes used. +
    • +
    • + Bug fixed in Scintilla macro support where typing Enter caused both a newline + command and newline character insertion to be recorded. +
    • +
    • + Bug fixed in SciTE on GTK+ where focus was moving + between widgets incorrectly. +
    • +
    • + Bug fixed with fold symbols sometimes not updating when + the text changed. +
    • +
    • + Bugs fixed in SciTE's handling of folding commands. +
    • +
    • + Deprecated undo collection enumeration removed from API. +
    • +
    +

    + Release 1.36 +

    +
      +
    • + Released on 1 March 2001. +
    • +
    • + Scintilla supports GTK+ on Win32. +
    • +
    • + Some untested work on making Scintilla and SciTE 64 bit compatible. + For users on GTK+ this requires including Scintilla.h before + ScintillaWidget.h. +
    • +
    • + HTML lexer allows folding HTML. +
    • +
    • + New lexer for Avenue files which are used in the ESRI ArcView GIS. +
    • +
    • + DOS Batch file lexer has states for '@', external commands, variables and + operators. +
    • +
    • + C++ lexer can fold comments of /* .. */ form. +
    • +
    • + Better disabling of popup menu items in Scintilla when in read-only mode. +
    • +
    • + Starting to move to Doxygen compatible commenting. +
    • +
    • + Director interface on Windows enables another application to control SciTE. +
    • +
    • + Opening SciTE on Windows 9x sped up greatly for some cases. +
    • +
    • + The command.build.directory property allows SciTE to run the build + command in a different directory to the source files. +
    • +
    • + SciTE on Windows allows setting foreground and background colours + for printed headers and footers. +
    • +
    • + Bug fixed in finding calltips in SciTE which led to no calltips for some identifiers. +
    • +
    • + Documentation added for lexers and for the extension and director interfaces. +
    • +
    • + SciTE menus rearranged with new View menu taking over some of the items that + were under the Options menu. Clear All Bookmarks command added. +
    • +
    • + Clear Output command in SciTE. +
    • +
    • + SciTE on Windows gains an Always On Top command. +
    • +
    • + Bug fixed in SciTE with attempts to define properties recursively. +
    • +
    • + Bug fixed in SciTE properties where only one level of substitution was done. +
    • +
    • + Bug fixed in SciTE properties where extensions were not being + matched in a case insensitive manner. +
    • +
    • + Bug fixed in SciTE on Windows where the Go to dialog displays the correct + line number. +
    • +
    • + In SciTE, if fold.on.open set then switching buffers also performs fold. +
    • +
    • + Bug fixed in Scintilla where ensuring a line was visible in the presence of folding + operated on the document line instead of the visible line. +
    • +
    • + SciTE command line processing modified to operate on arguments in order and in + two phases. First any arguments before the first file name are processed, then the + UI is opened, then the remaining arguments are processed. Actions defined for the + Director interface (currently only "open") may also be used on the command line. + For example, "SciTE -open:x.txt" will start SciTE and open x.txt. +
    • +
    • + Numbered menu items SciTE's Buffers menu and the Most Recently Used portion + of the File menu go from 1..0 rather than 0..9. +
    • +
    • + The tab bar in SciTE for Windows has numbers. + The tab.hide.one option hides the tab bar until there is more than one buffer open. +
    • +
    +

    + Release 1.35 +

    +
      +
    • + Released on 29 January 2001. +
    • +
    • + Rewritten and simplified widget code for the GTK+ version to enhance + solidity and make more fully compliant with platform norms. This includes more + normal handling of keystrokes so they are forwarded to containers correctly. +
    • +
    • + User defined lists can be shown. +
    • +
    • + Many fixes to the Perl lexer. +
    • +
    • + Pascal lexer handles comments more correctly. +
    • +
    • + C/C++/Java/JavaScipt lexer has a state for line doc comments. +
    • +
    • + Error output lexer understands Sun CC messages. +
    • +
    • + Make file lexer has variable, preprocessor, and operator states. +
    • +
    • + Wider area given to an italics character that is at the end of a line to prevent it + being cut off. +
    • +
    • + Call to move the caret inside the currently visible area. +
    • +
    • + Paste Rectangular will space fill on the left hand side of the pasted text as + needed to ensure it is kept rectangular. +
    • +
    • + Cut and Paste Rectangular does nothing in read-only mode. +
    • +
    • + Undo batching changed so that a paste followed by typing creates two undo actions.. +
    • +
    • + A "visibility policy" setting for Scintilla determines which range of lines are displayed + when a particular line is moved to. Also exposed as a property in SciTE. +
    • +
    • + SciTE command line allows property settings. +
    • +
    • + SciTE has a View Output command to hide or show the output pane. +
    • +
    • + SciTE's Edit menu has been split in two with searching commands moved to a + new Search menu. Find Previous and Previous Bookmark are in the Search menu. +
    • +
    • + SciTE on Windows has options for setting print margins, headers and footers. +
    • +
    • + SciTE on Windows has tooltips for toolbar. +
    • +
    • + SciTE on GTK+ has properties for setting size of file selector. +
    • +
    • + Visual and audio cues in SciTE on Windows enhanced. +
    • +
    • + Fixed performance problem in SciTE for GTK+ by dropping the extra 3D + effect on the content windows. +
    • +
    • + Fixed problem in SciTE where choosing a specific lexer then meant + that no lexer was chosen when files opened. +
    • +
    • + Default selection colour changed to be visible on low colour displays. +
    • +
    • + Fixed problems with automatically reloading changed documents in SciTE on + Windows. +
    • +
    • + Fixed problem with uppercase file extensions in SciTE. +
    • +
    • + Fixed some problems when using characters >= 128, some of which were being + incorrectly treated as spaces. +
    • +
    • + Fixed handling multiple line tags, non-inline scripts, and XML end tags /> in HTML/XML lexer. +
    • +
    • + Bookmarks in SciTE no longer disappear when switching between buffers. +
    • +
    +

    + Release 1.34 +

    +
      +
    • + Released on 28 November 2000. +
    • +
    • + Pascal lexer. +
    • +
    • + Export as PDF in SciTE. +
    • +
    • + Support for the OpenVMS operating system in SciTE. +
    • +
    • + SciTE for GTK+ can check for another instance of SciTE + editing a file and switch to it rather than open a second instance + on one file. +
    • +
    • + Fixes to quoting and here documents in the Perl lexer. +
    • +
    • + SciTE on Windows can give extra visual and audio cues when a + warning is shown or find restarts from beginning of file. +
    • +
    • + Open Selected Filename command in SciTE. Also understands some + warning message formats. +
    • +
    • + Wider area for line numbers when printing. +
    • +
    • + Better scrolling performance on GTK+. +
    • +
    • + Fixed problem where rectangles with negative coordinates were + invalidated leading to trouble with platforms that use + unsigned coordinates. +
    • +
    • + GTK+ Scintilla uses more compliant signalling code so that keyboard + events should propagate to containers. +
    • +
    • + Bug fixed with opening full or partial paths. +
    • +
    • + Improved handling of paths in error messages in SciTE. +
    • +
    • + Better handling of F6 in SciTE. +
    • +
    +

    + Release 1.33 +

    +
      +
    • + Released on 6 November 2000. +
    • +
    • + XIM support for the GTK+ version of Scintilla ensures that more non-English + characters can be typed. +
    • +
    • + Caret may be 1, 2, or 3 pixels wide. +
    • +
    • + Cursor may be switched to wait image during lengthy processing. +
    • +
    • + Scintilla's internal focus flag is exposed for clients where focus is handled in + complex ways. +
    • +
    • + Error status defined for Scintilla to hold indication that an operation failed and the reason + for that failure. No detection yet implemented but clients may start using the interface + so as to be ready for when it does. +
    • +
    • + Context sensitive help in SciTE. +
    • +
    • + CurrentWord property available in SciTE holding the value of the word the + caret is within or near. +
    • +
    • + Apache CONF file lexer. +
    • +
    • + Changes to Python lexer to allow 'as' as a context sensitive keyword and the + string forms starting with u, r, and ur to be recognised. +
    • +
    • + SCN_POSCHANGED notification now working and SCN_PAINTED notification added. +
    • +
    • + Word part movement commands for cursoring between the parts of reallyLongCamelIdentifiers and + other_ways_of_making_words. +
    • +
    • + When text on only one line is selected, Shift+Tab moves to the previous tab stop. +
    • +
    • + Tab control available for Windows version of SciTE listing all the buffers + and making it easy to switch between them. +
    • +
    • + SciTE can be set to automatically determine the line ending type from the contents of a + file when it is opened. +
    • +
    • + Dialogs in GTK+ version of SciTE made more modal and have accelerator keys. +
    • +
    • + Find in Files command in GTK+ version of SciTE allows choice of directory. +
    • +
    • + On Windows, multiple files can be opened at once. +
    • +
    • + SciTE source broken up into more files. +
    • +
    • + Scintilla headers made safe for C language, not just C++. +
    • +
    • + New printing modes - force background to white and force default background to white. +
    • +
    • + Automatic unfolding not occurring when Enter pressed at end of line bug fixed. +
    • +
    • + Bugs fixed in line selection. +
    • +
    • + Bug fixed with escapes in PHP strings in the HTML lexer. +
    • +
    • + Bug fixed in SciTE for GTK+ opening files when given full paths. +
    • +
    • + Bug fixed in autocompletion where user backspaces into existing text. +
    • +
    • + Bugs fixed in opening files and ensuring they are saved before running. + A case bug also fixed here. +
    • +
    +

    + Release 1.32 +

    +
      +
    • + Released on 8 September 2000. +
    • +
    • + Fixes bugs in complete word and related code. Protection against a bug when + receiving a bad argument. +
    • +
    +

    + Release 1.31 +

    +
      +
    • + Released on 6 September 2000. +
    • +
    • + Scintilla is available as a COM control from the scintillactrl module in CVS. +
    • +
    • + Style setting to underline text. Exposed in SciTE as "underlined". +
    • +
    • + Style setting to make text invisible. +
    • +
    • + SciTE has an extensibility interface that can be used to implement features such as + a scripting language or remote control. An example use of this is the extlua module + available from CVS which allows SciTE to be scripted in Lua. +
    • +
    • + Many minor fixes to all of the lexers. +
    • +
    • + New lexer for diff and patch files. +
    • +
    • + Error message lexer understands Perl error messages. +
    • +
    • + C/C++/Java lexer now supports C#, specifically verbatim strings and + @ quoting of identifiers that are the same as keywords. SciTE has + a set of keywords for C# and a build command set up for C#. +
    • +
    • + Scintilla property to see whether in overtype or insert state. +
    • +
    • + PosChanged notification fired when caret moved. +
    • +
    • + Comboboxes in dialogs in SciTE on Windows can be horizontally scrolled. +
    • +
    • + Autocompletion and calltips can treat the document as case sensitive or + case insensitive. +
    • +
    • + Autocompletion can be set to automatically choose the only + element in a single element list. +
    • +
    • + Set of characters that automatically complete an autocompletion list + can be set. +
    • +
    • + SciTE command to display calltip - useful when dropped because of + editing. +
    • +
    • + SciTE has a Revert command to go back to the last saved version. +
    • +
    • + SciTE has an Export as RTF command. Save as HTML is renamed + to Export as HTML and is located on the Export sub menu. +
    • +
    • + SciTE command "Complete Word" searches document for any + words starting with characters before caret. +
    • +
    • + SciTE options for changing aspects of the formatting of files exported + as HTML or RTF. +
    • +
    • + SciTE "character.set" option for choosing the character + set for all fonts. +
    • +
    • + SciTE has a "Toggle all folds" command. +
    • +
    • + The makefiles have changed. The makefile_vc and + makefile_bor files in scintilla/win32 and scite/win32 have been + merged into scintilla/win32/scintilla.mak and scite/win32/scite.mak. + DEBUG may be defined for all make files and this will turn on + assertions and for some make files will choose other debugging + options. +
    • +
    • + To make debugging easier and allow good use of BoundsChecker + there is a Visual C++ project file in scite/boundscheck that builds + all of Scintilla and SciTE into one executable. +
    • +
    • + The size of the SciTE output window can be set with the + output.horizontal.size and output.vertical.size settings. +
    • +
    • + SciTE status bar indicator for insert or overwrite mode. +
    • +
    • + Performance improvements to autocompletion and calltips. +
    • +
    • + A caret redraw problem when undoing is fixed. +
    • +
    • + Crash with long lines fixed. +
    • +
    • + Bug fixed with merging markers when lines merged. +
    • +
    +

    + Release 1.30 +

    +
      +
    • + Released on 26 July 2000. +
    • +
    • + Much better support for PHP which is now an integral part of the HTML support. +
    • +
    • + Start replacement of Windows-specific APIs with cross platform APIs. + In 1.30, the new APIs are introduced but the old APIs are still available. + For the GTK+ version, may have to include "WinDefs.h" explicitly to + use the old APIs. +
    • +
    • + "if" and "import" statements in SciTE properties files allows modularisation into + language-specific properties files and choices based upon platform. + This means that SciTE is delivered with 9 language-specific properties files + as well as the standard SciTEGlobal.properties file. +
    • +
    • + Much lower resource usage on Windows 9x. +
    • +
    • + "/p" option in SciTE on Windows for printing a file and then exiting. +
    • +
    • + Options for printing with inverted brightness (when the screen is set to use + a dark background) and to force black on white printing. +
    • +
    • + Option for printing magnified or miniaturised from screen settings. +
    • +
    • + In SciTE, Ctrl+F3 and Ctrl+Shift+F3 find the selection in the forwards and backwards + directions respectively. +
    • +
    • + Auto-completion lists may be set to cancel when the cursor goes before + its start position or before the start of string being completed. +
    • +
    • + Auto-completion lists automatically size more sensibly. +
    • +
    • + SCI_CLEARDOCUMENTSTYLE zeroes all style bytes, ensures all + lines are shown and deletes all folding information. +
    • +
    • + On Windows, auto-completion lists are visually outdented rather than indented. +
    • +
    • + Close all command in SciTE. +
    • +
    • + On Windows multiple files can be dragged into SciTE. +
    • +
    • + When saving a file, the SciTE option save.deletes.first deletes it before doing the save. + This allows saving with a different capitalisation on Windows. +
    • +
    • + When use tabs option is off pressing the tab key inserts spaces. +
    • +
    • + Bug in indicators leading to extra line drawn fixed. +
    • +
    +

    + Release 1.28 +

    +
      +
    • + Released on 27 June 2000. +
    • +
    • + Fixes crash in indentation guides when indent size set to 0. +
    • +
    • + Fixes to installation on GTK+/Linux. User properties file on GTK+ has a dot at front of name: + .SciTEUser.properties. Global properties file location configurable at compile time + defaulting to $prefix/share/scite. $prefix determined from Gnome if present else its + /usr/local and can be overridden by installer. Gnome menu integration performed in + make install if Gnome present. +
    • +
    +

    + Release 1.27 +

    +
      +
    • + Released on 23 June 2000. +
    • +
    • + Indentation guides. View whitespace mode may be set to not display whitespace + in indentation. +
    • +
    • + Set methods have corresponding gets for UndoCollection, BufferedDraw, + CodePage, UsePalette, ReadOnly, CaretFore, and ModEventMask. +
    • +
    • + Caret is continuously on rather than blinking while typing or holding down + delete or backspace. And is now always shown if non blinking when focused on GTK+. +
    • +
    • + Bug fixed in SciTE with file extension comparison now done in case insensitive way. +
    • +
    • + Bugs fixed in SciTE's file path handling on Windows. +
    • +
    • + Bug fixed with preprocessor '#' last visible character causing hang. +
    • +
    +

    + Release 1.26 +

    +
      +
    • + Released on 13 June 2000. +
    • +
    • + Support for the Lua language in both Scintilla and SciTE. +
    • +
    • + Multiple buffers may be open in SciTE. +
    • +
    • + Each style may have a character set configured. This may determine + the characters that are displayed by the style. +
    • +
    • + In the C++ lexer, lexing of preprocessor source may either treat it all as being in + the preprocessor class or only the initial # and preprocessor command word as + being in the preprocessor class. +
    • +
    • + Scintilla provides SCI_CREATEDOCUMENT, SCI_ADDREFDOCUMENT, and + SCI_RELEASEDOCUMENT to make it easier for a container to deal with multiple + documents. +
    • +
    • + GTK+ specific definitions in Scintilla.h were removed to ScintillaWidget.h. All GTK+ clients will need to + #include "ScintillaWidget.h". +
    • +
    • + For GTK+, tools can be executed in the background by setting subsystem to 2. +
    • +
    • + Keys in the properties files are now case sensitive. This leads to a performance increase. +
    • +
    • + Menu to choose which lexer to use on a file. +
    • +
    • + Tab size dialog on Windows. +
    • +
    • + File dialogs enlarged on GTK+. +
    • +
    • + Match Brace command bound to Ctrl+E on both platforms with Ctrl+] a synonym on Windows. + Ctrl+Shift+E is select to matching brace. Brace matching tries to match to either the inside or the + outside, depending on whether the cursor is inside or outside the braces initially. + View End of Line bound to Ctrl+Shift+O. +
    • +
    • + The Home key may be bound to move the caret to either the start of the line or the start of the + text on the line. +
    • +
    • + Visual C++ project file for SciTE. +
    • +
    • + Bug fixed with current x location after Tab key. +
    • +
    • + Bug fixed with hiding fold margin by setting fold.margin.width to 0. +
    • +
    • + Bugs fixed with file name confusion on Windows when long and short names used, or different capitalisations, + or relative paths. +
    • +
    +

    + Release 1.25 +

    +
      +
    • + Released on 9 May 2000. +
    • +
    • + Some Unicode support on Windows. Treats buffer and API as UTF-8 and displays + through UCS-2 of Windows. +
    • +
    • + Automatic indentation. Indentation size can be different to tab size. +
    • +
    • + Tool bar. +
    • +
    • + Status bar now on Windows as well as GTK+. +
    • +
    • + Input fields in Find and Replace dialogs now have history on both Windows and + GTK+. +
    • +
    • + Auto completion list items may be separated by a chosen character to allow spaces + in items. The selected item may be changed through the API. +
    • +
    • + Horizontal scrollbar can be turned off. +
    • +
    • + Property to remove trailing spaces when saving file. +
    • +
    • + On Windows, changed font size calculation to be more compatible with + other applications. +
    • +
    • + On GTK+, SciTE's global properties files are looked for in the directory specified in the + SCITE_HOME environment variable if it is set. This allows hiding in a dot directory. +
    • +
    • + Keyword lists in SciTE updated for JavaScript to include those destined to be used in + the future. IDL includes XPIDL keywords as well as MSIDL keywords. +
    • +
    • + Zoom level can be set and queried through API. +
    • +
    • + New notification sent before insertions and deletions. +
    • +
    • + LaTeX lexer. +
    • +
    • + Fixes to folding including when deletions and additions are performed. +
    • +
    • + Fix for crash with very long lines. +
    • +
    • + Fix to affect all of rectangular selections with deletion and case changing. +
    • +
    • + Removed non-working messages that had been included only for Richedit compatibility. +
    • +
    +

    + Release 1.24 +

    +
      +
    • + Released on 29 March 2000. +
    • +
    • + Added lexing of IDL based on C++ lexer with extra UUID lexical class. +
    • +
    • + Functions and associated keys for Line Delete, Line Cut, Line Transpose, + Selection Lower Case and Selection Upper Case. +
    • +
    • + Property setting for SciTE, eol.mode, chooses initial state of line end characters. +
    • +
    • + Fixed bugs in undo history with small almost-contiguous changes being incorrectly coalesced. +
    • +
    • + Fixed bugs with incorrect expansion of ContractionState data structures causing crash. +
    • +
    • + Fixed bugs relating to null fonts. +
    • +
    • + Fixed bugs where recolourisation was not done sometimes when required. +
    • +
    • + Fixed compilation problems with SVector.h. +
    • +
    • + Fixed bad setting of fold points in Python. +
    • +
    +

    + Release 1.23 +

    +
      +
    • + Released on 21 March 2000. +
    • +
    • + Directory structure to separate on basis of product (Scintilla, SciTE, DMApp) + and environment (Cross-platform, Win32, GTK+). +
    • +
    • + Download packaging to allow download of the source or platform dependent executables. +
    • +
    • + Source code now available from CVS at SourceForge. +
    • +
    • + Very simple Windows-only demonstration application DMApp is available from cvs as dmapp. +
    • +
    • + Lexing functionality may optionally be included in Scintilla rather than be provided by + the container. +
    • +
    • + Set of lexers included is determined at link time by defining which of the Lex* object files + are linked in. +
    • +
    • + On Windows, the SciLexer.DLL extends Scintilla.DLL with the standard lexers. +
    • +
    • + Enhanced HTML lexer styles embedded VBScript and Python. + ASP segments are styled and ASP scripts in JavaScript, VBScript and Python are styled. +
    • +
    • + PLSQL and PHP supported. +
    • +
    • + Maximum number of lexical states extended to 128. +
    • +
    • + Lexers may store per line parse state for multiple line features such as ASP script language choice. +
    • +
    • + Lexing API simplified. +
    • +
    • + Project file for Visual C++. +
    • +
    • + Can now cycle through all recent files with Ctrl+Tab in SciTE. +
    • +
    • + Bookmarks in SciTE. +
    • +
    • + Drag and drop copy works when dragging to the edge of the selection. +
    • +
    • + Fixed bug with value sizes in properties file. +
    • +
    • + Fixed bug with last line in properties file not being used. +
    • +
    • + Bug with multiple views of one document fixed. +
    • +
    • + Keypad now works on GTK+. +
    • +
    +

    + Release 1.22 +

    +
      +
    • + Released on 27 February 2000. +
    • +
    • + wxWindows platform defined. + Implementation for wxWindows will be available separately + from main Scintilla distribution. +
    • +
    • + Line folding in Scintilla. +
    • +
    • + SciTE performs syntax directed folding for C/C++/Java/JavaScript and for Python. +
    • +
    • + Optional macro recording support. +
    • +
    • + User properties file (SciTEUser.properties) allows for customisation by the user + that is not overwritten with each installation of SciTE. +
    • +
    • + Python lexer detects and highlights inconsistent indentation. +
    • +
    • + Margin API made more orthogonal. SCI_SETMARGINWIDTH and SCI_SETLINENUMBERWIDTH + are deprecated in favour of this new API. +
    • +
    • + Margins may be made sensitive to forward mouse click events to container. +
    • +
    • + SQL lexer and styles included. +
    • +
    • + Perl lexer handles regular expressions better. +
    • +
    • + Caret policy determines how closely caret is tracked by visible area. +
    • +
    • + New marker shapes: arrow pointing down, plus and minus. +
    • +
    • + Optionally display full path in title rather than just file name. +
    • +
    • + Container is notified when Scintilla gains or loses focus. +
    • +
    • + SciTE handles focus in a more standard way and applies the main + edit commands to the focused pane. +
    • +
    • + Container is notified when Scintilla determines that a line needs to be made visible. +
    • +
    • + Document watchers receive notification when document about to be deleted. +
    • +
    • + Document interface allows access to list of watchers. +
    • +
    • + Line end determined correctly for lines ending with only a '\n'. +
    • +
    • + Search variant that searches form current selection and sets selection. +
    • +
    • + SciTE understands format of diagnostic messages from WScript. +
    • +
    • + SciTE remembers top line of window for each file in MRU list so switching to a recent file + is more likely to show the same text as when the file was previously visible. +
    • +
    • + Document reference count now initialised correctly. +
    • +
    • + Setting a null document pointer creates an empty document. +
    • +
    • + WM_GETTEXT can no longer overrun buffer. +
    • +
    • + Polygon drawing bug fixed on GTK+. +
    • +
    • + Java and JavaScript lexers merged into C++ lexer. +
    • +
    • + C++ lexer indicates unterminated strings by colouring the end of the line + rather than changing the rest of the file to string style. This is less + obtrusive and helps the folding. +
    • +
    +

    + Release 1.21 +

    +
      +
    • + Released on 2 February 2000. +
    • +
    • + Blank margins on left and right side of text. +
    • +
    • + SCN_CHECKBRACE renamed SCN_UPDATEUI and made more efficient. +
    • +
    • + SciTE source code refactored into platform independent and platform specific classes. +
    • +
    • + XML and Perl subset lexers in SciTE. +
    • +
    • + Large improvement to lexing speed. +
    • +
    • + A new subsystem, 2, allows use of ShellExec on Windows. +
    • +
    • + Borland compatible makefile. +
    • +
    • + Status bar showing caret position in GTK+ version of SciTE. +
    • +
    • + Bug fixes to selection drawing when part of selection outside window, mouse release over + scroll bars, and scroll positioning after deletion. +
    • +
    +

    + Release 1.2 +

    +
      +
    • + Released on 21 January 2000. +
    • +
    • + Multiple views of one document. +
    • +
    • + Rectangular selection, cut, copy, paste, drag and drop. +
    • +
    • + Long line indication. +
    • +
    • + Reverse searching +
    • +
    • + Line end conversion. +
    • +
    • + Generic autocompletion and calltips in SciTE. +
    • +
    • + Call tip background colour can be set. +
    • +
    • + SCI_MARKERPREV for moving to a previous marker. +
    • +
    • + Caret kept more within window where possible. +
    • +
    +

    + Release 1.15 +

    +
      +
    • + Released on 15 December 1999. +
    • +
    • + Brace highlighting and badlighting (for mismatched braces). +
    • +
    • + Visible line ends. +
    • +
    • + Multiple line call tips. +
    • +
    • + Printing now works from SciTE on Windows. +
    • +
    • + SciTE has a global "*" lexer style that is used as the basis for all the lexers' styles. +
    • +
    • + Fixes some warnings on GTK+ 1.2.6. +
    • +
    • + Better handling of modal dialogs on GTK+. +
    • +
    • + Resize handle drawn on pane splitter in SciTE on GTK+ so it looks more like a regular GTK+ + *paned widget. +
    • +
    • + SciTE does not place window origin offscreen if no properties file found on GTK+. +
    • +
    • + File open filter remembered in SciTE on Windows. +
    • +
    • + New mechanism using style numbers 32 to 36 standardises the setting of styles for brace + highlighting, brace badlighting, line numbers, control characters and the default style. +
    • +
    • + Old messages SCI_SETFORE .. SCI_SETFONT have been replaced by the default style 32. The old + messages are deprecated and will disappear in a future version. +
    • +
    +

    + Release 1.14 +

    +
      +
    • + Released on 20 November 1999. +
    • +
    • + Fixes a scrolling bug reported on GTK+. +
    • +
    +

    + Release 1.13 +

    +
      +
    • + Released on 18 November 1999. +
    • +
    • + Fixes compilation problems with the mingw32 GCC 2.95.2 on Windows. +
    • +
    • + Control characters are now visible. +
    • +
    • + Performance has improved, particularly for scrolling. +
    • +
    • + Windows RichEdit emulation is more accurate. This may break client code that uses these + messages: EM_GETLINE, EM_GETLINECOUNT, EM_EXGETSEL, EM_EXSETSEL, EM_EXLINEFROMCHAR, + EM_LINELENGTH, EM_LINEINDEX, EM_CHARFROMPOS, EM_POSFROMCHAR, and EM_GETTEXTRANGE. +
    • +
    • + Menus rearranged and accelerator keys set for all static items. +
    • +
    • + Placement of space indicators in view whitespace mode is more accurate with some fonts. +
    • +
    +

    + Release 1.12 +

    +
      +
    • + Released on 9 November 1999. +
    • +
    • + Packaging error in 1.11 meant that the compilation error was not fixed in that release. + Linux/GTK+ should compile with GCC 2.95 this time. +
    • +
    +

    + Release 1.11 +

    +
      +
    • + Released on 7 November 1999. +
    • +
    • + Fixed a compilation bug in ScintillaGTK.cxx. +
    • +
    • + Added a README file to explain how to build. +
    • +
    • + GTK+/Linux downloads now include documentation. +
    • +
    • + Binary only Sc1.EXE one file download for Windows. +
    • +
    +

    + Release 1.1 +

    +
      +
    • + Released on 6 November 1999. +
    • +
    • + Major restructuring for better modularity and platform independence. +
    • +
    • + Inter-application drag and drop. +
    • +
    • + Printing support in Scintilla on Windows. +
    • +
    • + Styles can select colouring to end of line. This can be used when a file contains more than + one language to differentiate between the areas in each language. An example is the HTML + + JavaScript styling in SciTE. +
    • +
    • + Actions can be grouped in the undo stack, so they will be undone together. This grouping is + hierarchical so higher level actions such as replace all can be undone in one go. Call to + discover whether there are any actions to redo. +
    • +
    • + The set of characters that define words can be changed. +
    • +
    • + Markers now have identifiers and can be found and deleted by their identifier. The empty + marker type can be used to make a marker that is invisible and which is only used to trace + where a particular line moves to. +
    • +
    • + Double click notification. +
    • +
    • + HTML styling in SciTE also styles embedded JavaScript. +
    • +
    • + Additional tool commands can be added to SciTE. +
    • +
    • + SciTE option to allow reloading if changed upon application activation and saving on + application deactivation. Not yet working on GTK+ version. +
    • +
    • + Entry fields in search dialogs remember last 10 user entries. Not working in all cases in + Windows version. +
    • +
    • + SciTE can save a styled copy of the current file in HTML format. As SciTE does not yet + support printing, this can be used to print a file by then using a browser to print the + HTML file. +
    • +
    +

    + Release 1.02 +

    +
      +
    • + Released on 1 October 1999. +
    • +
    • + GTK+ version compiles with GCC 2.95. +
    • +
    • + Properly deleting objects when window destroyed under GTK+. +
    • +
    • + If the selection is not empty backspace deletes the selection. +
    • +
    • + Some X style middle mouse button handling for copying the primary selection to and from + Scintilla. Does not work in all cases. +
    • +
    • + HTML styling in SciTE. +
    • +
    • + Stopped dirty flag being set in SciTE when results pane modified. +
    • +
    +

    + Release 1.01 +

    +
      +
    • + Released on 28 September 1999. +
    • +
    • + Better DBCS support on Windows including IME. +
    • +
    • + Wheel mouse support for scrolling and zooming on Windows. Zooming with Ctrl+KeypadPlus and + Ctrl+KeypadMinus. +
    • +
    • + Performance improvements especially on GTK+. +
    • +
    • + Caret blinking and settable colour on both GTK+ and Windows. +
    • +
    • + Drag and drop within a Scintilla window. On Windows, files can be dragged into SciTE. +
    • +
    +

    + Release 1.0 +

    +
      +
    • + Released on 17 May 1999. +
    • +
    • + Changed name of "Tide" to "SciTE" to avoid clash with a TCL based IDE. "SciTE" is a + SCIntilla based Text Editor and is Latin meaning something like "understanding in a neat + way" and is also an Old English version of the word "shit". +
    • +
    • + There is a SCI_AUTOCSTOPS message for defining a string of characters that will stop + autocompletion mode. Autocompletion mode is cancelled when any cursor movement occurs apart + from backspace. +
    • +
    • + GTK+ version now splits horizontally as well as vertically and all dialogs cancel when the + escape key is pressed. +
    • +
    +

    + Beta release 0.93 +

    +
      +
    • + Released on 12 May 1999. +
    • +
    • + A bit more robust than 0.92 and supports SCI_MARKERNEXT message. +
    • +
    +

    + Beta release 0.92 +

    +
      +
    • + Released on 11 May 1999. +
    • +
    • + GTK+ version now contains all features of Windows version with some very small differences. + Executing programs works much better now. +
    • +
    • + New palette code to allow more colours to be displayed in 256 colour screen modes. A line + number column can be displayed to the left of the selection margin. +
    • +
    • + The code that maps from line numbers to text positions and back has been completely + rewritten to be faster, and to allow markers to move with the text. +
    • +
    +

    + Beta release 0.91 +

    +
      +
    • + Released on 30 April 1999, containing fixes to text measuring to make Scintilla work better + with bitmap fonts. Also some small fixes to make compiling work with Visual C++. +
    • +
    +

    + Beta release 0.90 +

    +
      +
    • + Released on 29 April 1999, containing working GTK+/Linux version. +
    • +
    • + The Java, C++ and Python lexers recognise operators as distinct from default allowing them + to be highlighted. +
    • +
    +

    + Beta release 0.82 +

    +
      +
    • + Released on 1 April 1999, to fix a problem with handling the Enter key in PythonWin. Also + fixes some problems with cmd key mapping. +
    • +
    +

    + Beta release 0.81 +

    +
      +
    • + Released on 30th March 1999, containing bug fixes and a few more features. +
    • +
    • + Static linking supported and Tidy.EXE, a statically linked version of Tide.EXE. Changes to + compiler flags in the makefiles to optimise for size. +
    • +
    • + Scintilla supports a 'savepoint' in the undo stack which can be set by the container when + the document is saved. Notifications are sent to the container when the savepoint is + entered or left, allowing the container to to display a dirty indicator and change its + menus. +
    • +
    • + When Scintilla is set to read-only mode, a notification is sent to the container should the + user try to edit the document. This can be used to check the document out of a version + control system. +
    • +
    • + There is an API for setting the appearance of indicators. +
    • +
    • + The keyboard mapping can be redefined or removed so it can be implemented completely by the + container. All of the keyboard commands are now commands which can be sent by the + container. +
    • +
    • + A home command like Visual C++ with one hit going to the start of the text on the line and + the next going to the left margin is available. I do not personally like this but my + fingers have become trained to it by much repetition. +
    • +
    • + SCI_MARKERDELETEALL has an argument in wParam which is the number of the type marker to + delete with -1 performing the old action of removing all marker types. +
    • +
    • + Tide now understands both the file name and line numbers in error messages in most cases. +
    • +
    • + Tide remembers the current lines of files in the recently used list. +
    • +
    • + Tide has a Find in Files command. +
    • +
    +

    + Beta release 0.80 +

    +
      +
    • + This was the first public release on 14th March 1999, containing a mostly working Win32 + Scintilla DLL and Tide EXE. +
    • +
    +

    + Beta releases of SciTE were called Tide +

    + + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaRelated.html b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaRelated.html new file mode 100644 index 00000000..5fe04213 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaRelated.html @@ -0,0 +1,507 @@ + + + + + + + + + Scintilla and SciTE Related Sites + + + + + + + + +
    + Scintilla icon + + Scintilla + and SciTE +
    +

    + Related Sites +

    +

    + Ports and Bindings of Scintilla +

    +

    + Editawy + is an ActiveX Control wrapper that support all Scintilla functions and additional high level functions. +

    +

    + Jintilla + is a JNI wrapper that allows Scintilla to be used in Java with + both SWT and AWT. +

    +

    + Delphi Scintilla Interface Components + is a FREE collection of components that makes it easy to use the + Scintilla source code editing control from within Delphi and C++ Builder. +

    +

    + wxStEdit + is a library and sample program that provides extra features over wxStyledTextControl. +

    +

    + CScintillaCtrl, CScintillaView & CScintillaDoc + are freeware MFC classes to encapsulate Scintilla. +

    +

    + ScintillaNet + is an encapsulation of Scintilla for use within the .NET framework. +

    +

    + QScintilla + is a port of Scintilla to the Qt platform. It has a similar license to Qt: GPL for use in + free software and commercial for use in close-source applications. +

    +

    + + GWindows is a Win32 RAD GUI Framework for Ada 95 that + includes a binding of Scintilla. +

    +

    + + DolphinScintilla is a DolphinSmalltalk wrapper for Scintilla. +

    +

    + ScintillaVB + is an ActiveX control written in VB that encapsulates Scintilla. +

    +

    + FXScintilla + is a port of Scintilla to the FOX platform. FXRuby includes Ruby + bindings for FXScintilla. +

    +

    + Delphi wrapper for + Scintilla which is also usable from Borland C++ Builder. +

    +

    + The wxStyledTextCtrl editor component in the + wxWindows cross platform toolkit is based on Scintilla.
    + A Python binding for wxStyledTextCtrl is part of wxPython. +

    +

    + gtkscintilla + is an alternative GTK class implementation for scintilla. + This implementation acts more like a Gtk+ object, with many methods rather + than just scintilla_send_message() and is available as a shared library. + This implementation works with GTK 1.x. +

    +

    + gtkscintilla2 + is an alternative GTK class implementation for scintilla + similar to the above, but for GTK 2.x. +

    +

    + pyscintilla + is the original Python binding for Scintilla's default GTK + 1.x class. Includes some additional support, such as native printing on + Windows. The binding is hand-written rather than auto-generated from the + Scintilla.iface file. +

    +

    + pygtkscintilla + is a Python binding for gtk1.x scintilla that uses + gtkscintilla instead of the default GTK class. +

    +

    + pyscintilla2 + is a Python binding for GTK 2.x scintilla that uses + gtkscintilla2. +

    +

    + ScintillaCtrl + is an unmaintained ActiveX control wrapper for Scintilla. +

    +

    + Projects using Scintilla +

    +

    + PyPE + is an editor written in Python with the wxPython GUI toolkit. +

    +

    + Sciboo + is an editor based on ScintillaNET. +

    +

    + The Scite Config Tool + is a graphical user interface for changing SciTE properties files. +

    +

    + Scintilla Lister + is a plugin for Total Commander allowing viewing all documents with syntax highlighting + inside Total Commander. +

    +

    + ChSciTE + is a free IDE for C/C++ interpreter Ch. It runs cross platform. + Ch is for cross-platform scripting, shell + programming, 2D/3D plotting, numerical computing, and embedded + scripting. +

    +

    + + Code::Blocks is an open source, cross platform free C++ IDE. +

    +

    + + Notepad++ is a free source code editor under Windows. +

    +

    + + Gubed is a cross platform program to debug PHP scripts. +

    +

    + + LSW DotNet-Lab is a development environment for the .NET platform. +

    +

    + + GLIntercept is an OpenGL function call interceptor that uses SciTE as a + run-time shader editor. +

    +

    + + Xin is an open-source XML editor for Windows. +

    +

    + + wyoEditor is "A nice editor with a well designed and consistent look and feel". +

    +

    + + Notepad2 is "Yet another Notepad replacement". +

    +

    + + PyCrash Viewer can examine crash dumps of Python programs. +

    +

    + + MPT series Wire Analyzers use Scintilla and SciTE. +

    +

    + MyGeneration + is a .NET based code generator. +

    +

    + CSSED + is a tiny GTK2 CSS editor. +

    +

    + DIDE + is a free IDE for the D language on Windows. +

    +

    + + IdePS + is a free Integrated Development Environment for PostScript +

    +

    + + CUTE + is a user-friendly source code editor easily extended using Python. +

    +

    + + Venis IX, + the Visual Environment for NSIS (Nullsoft Scriptable Install System). +

    +

    + + MinGW Developer Studio + is a simple C/C++ IDE for the MinGW compiler on Windows. +

    +

    + Eric3 + is a Python IDE written using PyQt and QScintilla. +

    +

    + TemplateTamer + is a tool for development of template based PHP web pages. +

    +

    + SciTE|Flash + is a free Scintilla-based ActionScript editor for Windows. +

    +

    + CPPIDE + is part of some commercial high-school oriented programming course software. +

    +

    + phpSciTE + is a free distribution of SciTE for Windows customised for use with PHP + and bundled with a PHP API file and online help. +

    +

    + Instant Source + is a commercial tool for looking at the HTML on web sites. +

    +

    + RAD.On++ + is a free C++ Rapid Application Developer for Win32. +

    +

    + wxLua is both + a binding of the wxWindows classes for Lua and a small IDE that works on Linux + and Windows. +

    +

    + wxBasic is an open source + Basic interpreter that uses the wxWindows toolkit. A small IDE is under construction. +

    +

    + FreeRIDE will be a + cross-platform IDE for the Ruby programming language. +

    +

    + Visual MinGW is an + IDE for the MinGW compiler system.This runs on Windows with gcc. +

    +

    + The Wing IDE is a + complete integrated development environment for the Python programming + language. + Available on Intel based Linux and Windows and on MacOS X through XDarwin. +

    +

    + LuaIDE + is an IDE for Lua on Windows. +

    +

    + Sphere + is 2D RPG engine with a development environment. +

    +

    + Practical Ruby + is an IDE for Ruby on Windows. +

    +

    + GNUe + is a suite of tools and applications for solving the needs of the enterprise. +

    +

    + SilverCity + is a lexing package that can provide lexical analysis for over 20 programming + and markup languages. +

    +

    + Php mole + is an integrated development enviroment for developing (primarily) + web based and phpgtk based applications. +

    +

    + HAP Python Remote Debugger + is a Python debugger that can run on one Windows machine debugging a Python program running + on either the same or another machine. +

    +

    + pyeditor and wxEditor + are scriptable editors implemented in Python. pyeditor is based on GTK+ and + the pyscintilla wrapper. wxEditor is based on wxWindows, wxPython and + wxStyledTextControl. +

    +

    + Ruby installation + that includes SciTE set up for Ruby using an included copy of the "Programming Ruby" book for help. +

    +

    + Interactive LuaSpace Development + is a graphical environment for LuaSpace which combines the CORBA platform + with the language Lua. +

    +

    + PyCrust is an interactive + Python shell based on wxPython. +

    +

    + Black Adder is a + Qt based development environment for Python and Ruby. +

    +

    + Komodo + is a cross-platform multi-language development environment built + as an application of Mozilla. +

    +

    + titmouse + is a Lua editor/debugger for Windows. It is available as both a component + and an application. +

    +

    + Filerx + is a project manager for SciTE on Windows. + Open source and includes an implementation of SciTE's Director interface so + will be of interest to others wanting to control SciTE. +

    +

    + Anjuta + is an open source C/C++ IDE for Linux/GNOME. +

    +

    + Gen<X> + is a code generalisation product for Win32 that uses Scintilla in the X-Code Editor (which + can also be used for general purpose editing) and for editing HTML in the HTML Dialog + Editor. +

    +

    + Moleskine is a Scintilla + based editor for GTK+. More ambitious than SciTE with plans + for MDI, printing, and session management. + Includes a new GTK+ wrapper widget for Scintilla. +

    +

    + A version of SciTE for Win32 enhanced + with a tab control to allow easy movement between buffers. + Go to the "Goodies" area on this site. +

    +

    + + Suneido is an integrated application platform currently available for Win32 that includes an + object-oriented language, client-server database, and user interface and reporting frameworks. +

    +

    + + BitLeaf is a new GNOME based development environment. + Currently at an early stage of development. +

    +

    + + Agast is an authoring system for adventure games which includes + a customised version of SciTE. +

    +

    + + SashXB for Linux is an open source application development tool by + IBM that uses Scintilla. +

    +

    + Boa Constructor is a RAD GUI + Building IDE for the wxWindows cross platform platform. Written using wxPython with the + wxStyledTextCtrl used as its editor. +

    +

    + PythonWin, a Win32 IDE for Python, uses + Scintilla for both its editing and interactive windows. +

    +

    + Editing Components +

    +

    + GtkSourceView + is a text widget that extends the standard GTK+ 2.x text widget and improves it + by implementing syntax highlighting and other features typical of a source editor. +

    +

    + AEditor + is a free source code editing component implemented in Ruby. +

    +

    + SyntaxEditor + is a commercial native .Net source code editing component. +

    +

    + jEdit is a good Open Source syntax colouring + editor written in and for Java. +

    +

    + GTK+, the GIMP Toolkit, contains a rich text editing + widget.
    + Gedit is an editor for GTK+/GNOME.
    + +

    +

    + CodeGuru has source code for several Win32 MFC based + editors. +

    + SynEdit is a Win32 edit control written + in Delphi. +

    + SourceView is a commercial editing + component for Win32. +

    +

    + CodeMax is another commercial component for Win32. +

    +

    + Documents +

    +

    + The Craft of Text Editing + describes how EMACS works, Craig A. Finseth +

    +

    + Span Tables + are another data structure that can be used to represent documents in memory in a way + that performs well when data is inserted and deleted, James Brown +

    +

    + Data Structures in a Bit-Mapped Text + Editor, Wilfred J. Hanson, Byte January 1987 +

    +

    + Text Editors: Algorithms and Architectures, Ray Valdés, Dr. Dobbs Journal + April 1993 +

    +

    + Macintosh User Interface Guidelines and TextEdit chapters of Inside Macintosh +

    +

    + Development Tools +

    +

    + Scintilla and SciTE were developed using the + Mingw version of GCC. +

    +

    + AStyle is a source code formatter for C++ and + Java code. SciTE has an Indent command defined for .cxx files that uses AStyle. +

    +

    + WinMerge is an interactive diff / merge + for Windows. I prefer code submissions in the form of source files rather than diffs and then run + WinMerge over the files to work out how to merge. +

    +

    + Python is my favourite programming language. Scintilla + was started after I tried to improve the editor built into PythonWin, but was frustrated by the limitations of + the Windows Richedit control which PythonWin used. +

    +

    + regex is a public domain + implementation of regular expression pattern matching used in Scintilla. +

    + +

    + + Inspirational coding soundscapes by David Bridie. +

    +

    + Get away from hacking without any of that tedious standing up bother: Virtually There ;). +

    + + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaToDo.html b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaToDo.html new file mode 100644 index 00000000..a8098006 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaToDo.html @@ -0,0 +1,178 @@ + + + + + + + + + Scintilla and SciTE To Do + + + + + + + + +
    + Scintilla icon + + Scintilla + and SciTE +
    +

    + Bugs and To Do List +

    +

    + Feedback +

    +

    + Issues can be reported on the Bug Tracker + and features requested on the Feature Request Tracker. +

    +

    + Scintilla Bugs +

    +

    + At the end of italics style runs characters can be chopped off. An example + is using Verdana 12 point italics for strings makes an ending double quote + half visible and an ending single quote invisible. This is hard to solve + completely, may be better to avoid these situations by, for example, + choosing a font like Times New Roman for strings. There is a specific kluge + for the end of line which adds some room for italics but this does not + work elsewhere. +

    +

    + Dragging over bold text in some fonts will ripple because of the difference in + size between drawing all of a string at once and drawing it in parts. +

    +

    + Automatic scrolling when text dragged near edge of window. +

    +

    + GTK+ Version Bugs +

    +

    + Scintilla To Do +

    +

    + Folding for languages that don't have it yet and good folding for languages + that inherited poor folding from another languages folding code. +

    +

    + Simple pattern based styling. +

    +

    + Different height lines based upon tallest text on the line rather than on the tallest style + possible. +

    +

    + Composition of lexing for mixed languages (such as ASP+ over COBOL) by + combining lexers. +

    +

    + Printing support on GTK+. Maybe Postscript output or use Gnome? +

    +

    + Stream folding which could be used to fold up the contents of HTML elements. +

    +

    + Persisting view state such as current folding into a stream or blob so it is easy + to restore. +

    +

    + Move line up and move line down keys or move selected lines up / down. +

    +

    + Printing of highlight lines and folding margin. +

    +

    + Flow diagrams inside editor similar to + + GRASP. +

    +

    + A VCL component wrapper around Scintilla so it can be used with Delphi or + Borland C++ Builder. + There is some work + on this available. +

    +

    + Port to MacOS X. +

    +

    + More lexers for other languages. +

    +

    + Automatically calculated range for horizontal scrolling. +

    +

    + Virtual space at the end of lines so the caret can be moved beyond the end + of lines with the cursor keys. May also make rectangular operations easier + to perform. +

    +

    + SciTE To Do +

    +

    + Good regular expression support through a plugin. +

    +

    + Allow tools to transform the selection, performing an operation like + indentation or sorting. +

    +

    + Allow file name based selection on all properties rather than just a chosen few. +

    +

    + Opening from and saving to FTP servers. +

    +

    + Setting to fold away comments upon opening. +

    +

    + User defined fold ranges. +

    +

    + Silent mode that does not display any message boxes. +

    +

    + Features I am unlikely to do +

    +

    + These are features I don't like or don't think are important enough to work on. + Implementations are welcome from others though. +

    +

    + Automatically saving modified menu shortcuts on exit. +

    +

    + Mouse wheel panning (press the mouse wheel and then move the mouse) on + Windows. +

    +

    + Adding options to the save dialog to save in a particular encoding or with a + chosen line ending. +

    +

    + Directions +

    +

    + The main point of this development is Scintilla, and this is where most effort will + go. SciTE will get new features, but only when they make my life easier - I am + not intending to make it grow up to be a huge full-function IDE like Visual + Cafe. The lines I've currently decided not to step over in SciTE are any sort of + project facility and any configuration dialogs. SciTE for Windows now has a + Director interface for communicating with a separate project manager + application. +

    +

    + If you are interested in contributing code, do not feel any need to make it cross + platform. + Just code it for your platform and I'll either reimplement for the other platform or + ensure that there is no effect on the other platform. +

    + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaUsage.html b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaUsage.html new file mode 100644 index 00000000..c19cbc55 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/ScintillaUsage.html @@ -0,0 +1,375 @@ + + + + + + + + + Scintilla Usage Notes + + + + + + + + + +
    + Scintilla icon + + Scintilla + Usage Notes +
    +

    + Implementing Auto-Indent +

    +

    + The key idea is to use the SCN_CHARADDED notification to add indentation after a newline. +

    +

    + The lParam on the notification is a pointer to a SCNotification structure whose ch member + specifies the character added. If a newline was added, the previous line can be retrieved and + the same indentation can be added to the new line. +

    +

    + Here is the relevant portion of code from SciTE: (SciTE.cxx SciTEWindow::CharAdded) +

    + if  (ch  ==  '\r'  ||  ch  ==  '\n')  {
    +     
    char  + linebuf[1000];
    +     
    int  + curLine  =  GetCurrentLineNumber();
    +     
    int  + lineLength  + =  SendEditor(SCI_LINELENGTH,  curLine);
    +     
    + //Platform::DebugPrintf("[CR] %d len = %d\n", curLine, lineLength);
    +     
    if  (curLine  >  0  &&  + lineLength  + <=  2)  {
    +     
    int  + prevLineLength  + =  SendEditor(SCI_LINELENGTH,  curLine  -  1);
    +     
    if  (prevLineLength  <  sizeof(linebuf))  {
    +         
    WORD  buflen  =  sizeof(linebuf);
    +         
    memcpy(linebuf,  &buflen,  sizeof(buflen));
    +         
    + SendEditor(EM_GETLINE,  curLine  -  1,
    +                    
    + reinterpret_cast<LPARAM>(static_cast<char  *>(linebuf)));
    +         
    linebuf[prevLineLength]  =  '\0';
    +         
    for  (int  pos  =  0;  linebuf[pos];  pos++)  {
    +             
    if  (linebuf[pos]  !=  ' '  &&  + linebuf[pos]  !=  '\t')
    +                 
    + linebuf[pos]  =  '\0';
    +         
    }
    +         
    + SendEditor(EM_REPLACESEL,  0,  + reinterpret_cast<LPARAM>(static_cast<char  *>(linebuf)));
    +     
    }
    +
    }
    + +

    + Of course, fancier handling could be implemented. For example, if the previous line was the + start of a control construct, the next line could be automatically indented one tab further. + (Assuming that is your indenting style.) +

    +

    + Implementing Syntax Styling +

    +

    + Syntax styling is handled by the SCN_STYLENEEDED notification. Scintilla keeps track of the + end of the styled text - this is retrieved with SCI_GETENDSTYLED. In response to the + SCN_STYLENEEDED notification, you should apply styles to the text from ENDSTYLED to the + position specified by the notification. +

    +

    + Here is the relevant portion of code from SciTE: (SciTE.cxx) +

    + void  + SciTEWindow::Notify(SCNotification  *notification)  {
    +     
    switch  + (notification->nmhdr.code)  {
    +     
    case  + SCN_STYLENEEDED:  {
    +             
    if  (notification->nmhdr.idFrom  ==  IDM_SRCWIN)  {
    +                 
    + int  + endStyled  =  SendEditor(SCI_GETENDSTYLED);
    +                 
    + int  + lineEndStyled  =  SendEditor(EM_LINEFROMCHAR,  + endStyled);
    +                 
    + endStyled  + =  SendEditor(EM_LINEINDEX,  lineEndStyled);
    +                 
    + Colourise(endStyled,  notification->position);
    + +

    + Colourize(start, end) retrieves the specified range of text and then calls ColourizeDoc in + keywords.cxx. It starts the process by calling: +

    +     SendMessage(hwnd,  SCI_STARTSTYLING,  + startPos,  31);
    + +

    + and then for each token of the text, calling: +

    +     SendMessage(hwnd,  SCI_SETSTYLING,  length,  style);
    + +

    + where style is a number from 0 to 31 whose appearance has been defined using the + SCI_STYLESET... messages. +

    +

    + Implementing Calltips +

    +

    + Again, the SCN_CHARADDED notification is used to catch when an opening parenthesis is added. + The preceding word can then be retrieved from the current line: +

    +     char  linebuf[1000];
    +
        int  current  =  SendEditor(SCI_GETCURLINE,  sizeof(linebuf),
    +         
    + reinterpret_cast<LPARAM>(static_cast<char  *>(linebuf)));
    +
        int  pos  =  SendEditor(SCI_GETCURRENTPOS);
    +
    +
        int  startword  =  current  -  1;
    +
        while  + (startword  + >  0  &&  + isalpha(linebuf[startword  -  1]))
    +         
    + startword--;
    +
        linebuf[current  -  1]  =  '\0';
    +
        char*  word  =  linebuf  +  startword;
    + +

    + Then if a calltip is available it can be displayed. The calltip appears immediately below + the position specified. The calltip can be multiple lines separated by newlines (\n). +

    +     pos  =  SendMessage(hwnd,  SCI_GETCURRENTPOS,  0,  0);
    +
        SendMessageText(hwnd,  SCI_CALLTIPSHOW,  pos  -  wordLen  -  1,  calltip);
    + +

    + The calltip can be removed when a closing parenthesis is entered: +

    +     if  (SendMessage(hwnd,  SCI_CALLTIPACTIVE,  + 0,  0))
    +         
    + SendMessage(hwnd,  + SCI_CALLTIPCANCEL,  0,  + 0);
    + +

    + Obviously, it is up the application to look after supplying the appropriate calltip text. +

    +

    + SciTE goes one step further, counting the commas between arguments and highlighting the + corresponding part of the calltip. This code is in ContinueCallTip. +

    +

    + Page contributed by Andrew McKinlay. +

    + + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Steps.html b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Steps.html new file mode 100644 index 00000000..765268da --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/Steps.html @@ -0,0 +1,142 @@ + +How to use the Scintilla Edit Control in windows? +

    How to use the Scintilla Edit Control in windows?

    +

    + This should be a little step by step explanation how to use Scintilla in the windows environment. +

    +

    +

    How to create Scintilla Edit Control?

    +

    + First of all, load the Scintilla DLL with something like: +

    +
    +
    +	hmod = LoadLibrary("SciLexer.DLL");
    +		if (hmod==NULL)
    +		{
    +			MessageBox(hwndParent,
    +			"The Scintilla DLL could not be loaded.",
    +			"Error loading Scintilla",
    +			MB_OK | MB_ICONERROR);
    +		}
    +		
    +

    + If the DLL was loaded successfully, then the DLL has registered (yes, by itself) a new + window class. The new class called "Scintilla" is the new scintilla edit control. +

    +

    + Now you can use this new control just like any other windows control. +

    +
    +
    +	hwndScintilla = CreateWindowEx(0,
    +		"Scintilla","", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPCHILDREN,
    +		10,10,500,400,hwndParent,(HMENU)GuiID, hInstance,NULL);
    +		
    +

    + Note the new window class name: "Scintilla". By reaching this point you actually included + a Scintilla Edit Control to your windows program. +

    +

    +

    How to control the Scintilla Edit Control?

    +

    + You can control Scintilla by sending commands to the Edit Control. + There a 2 ways of doing this. A simple and fast way. +

    +

    The simple way to control Scintilla

    +

    + The simple way is just like with any other windows control. You can send messages to the + Scintilla Edit Control and receive notifications from the control. (Note that the notifications + are sent to the parent window of the Scintilla Edit Control.) +

    +

    + The Scintilla Edit Control knows a special message for each command. + To send commands to the Scintilla Edit Control you can use the SendMessage function. +

    +
    +
    +	SendMessage(hwndScintilla,sci_command,wparam,lparam);
    +			
    +

    + like: +

    +
    +
    +	SendMessage(hwndScintilla,SCI_CREATEDOCUMENT, 0, 0);
    +			
    +

    + Some of the commands will return a value and unused parameters should be set to NULL. +

    +

    +

    The fast way to control Scintilla

    +

    + The fast way of controlling the Scintilla Edit Control is to call message handling function by yourself. + You can retrieve a pointer to the message handling function of the Scintilla Edit Control and + call it directly to execute a command. This way is much more faster than the SendMessage() way. +

    +

    + 1st you have to use the SCI_GETDIRECTFUNCTION and SCI_GETDIRECTPOINTER commands to + retrieve the pointer to the function and a pointer which must be the first parameter when calling the retrieved + function pointer. + You have to do this with the SendMessage way :) +

    +

    + The whole thing has to look like this: +

    +
    +
    +	int (*fn)(void*,int,int,int);
    +	void * ptr;
    +	int canundo;
    +
    +	fn = (int (__cdecl *)(void *,int,int,int))SendMessage(
    +		hwndScintilla,SCI_GETDIRECTFUNCTION,0,0);
    +	ptr = (void *)SendMessage(hwndScintilla,SCI_GETDIRECTPOINTER,0,0);
    +
    +	canundo = fn(ptr,SCI_CANUNDO,0,0);
    +			
    +

    + with "fn" as the function pointer to the message handling function of the Scintilla Control + and "ptr" as the pointer that must be used as 1st parameter. + The next parameters are the Scintilla Command with its two (optional) parameters. +

    + +

    +

    How will I receive notifications?

    +

    + Whenever an event occurs where Scintilla wants to inform you about something, the Scintilla Edit Control + will send notification to the parent window. This is done by a WM_NOTITY message. + When receiving that message, you have to look in the xxx struct for the actual message. +

    +

    + So in Scintillas parent window message handling function you have to include some code like this: +

    +
    +	NMHDR *lpnmhdr;
    +
    +	[...]
    +
    +	case WM_NOTIFY:
    +		lpnmhdr = (LPNMHDR) lParam;
    +
    +		if(lpnmhdr->hwndFrom==hwndScintilla)
    +		{
    +			switch(lpnmhdr->code)
    +			{
    +				case SCN_CHARADDED:
    +					/* Hey, Scintilla just told me that a new */
    +					/* character was added to the Edit Control.*/
    +					/* Now i do something cool with that char. */
    +				break;
    +			}
    +		}
    +	break;
    +			
    +

    +

    + +

    + Page contributed by Holger Schmidt. +

    + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/index.html b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/index.html new file mode 100644 index 00000000..5c891001 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/doc/index.html @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + Scintilla and SciTE + + + + + + + + + + +
    + Scintilla + + A free source code editing component for Win32 and + GTK+ + + Release version 1.71
    + Site last modified August 21 2006
    +
    +   +
    + + + + +
    + Sci Break +
    + + + + + + + + + + + + + + + + + + + +
    + Version 1.71 defaults mouse drag to be move on GTK+ + and GTK+ also has some internationalisation fixes. +
    + Version 1.70 allows, on GTK+, approximate character set conversions + for pasting and uses internationalised input at all times. +
    + Version 1.69 supports the Spice language and can draw + the selection and whole line markers translucently. +
    + Version 1.68 can draw the caret line + and box indicators translucently and has an accurate TCL lexer. +
    + Version 1.67 enhances some lexers and fixes bugs. +
    + Version 1.66 has a new Ruby lexer and fixes bugs on GTK+ + with Pango 1.8. +
    + + + + +
    + + + Documentation   + Bugs   + + + History   Related   +
    +

    + Scintilla is a free source code editing component. + It comes with complete source code and a license that + permits use in any free project or commercial product. +

    +

    + As well as features found in standard text editing components, Scintilla includes features + especially useful when editing and debugging source code. + These include support for syntax styling, error indicators, code completion and call tips. + The selection margin can contain markers like those used in debuggers to indicate + breakpoints and the current line. Styling choices are more open than with many editors, + allowing the use of proportional fonts, bold and italics, multiple foreground and background + colours and multiple fonts. +

    +

    + The SinkWorld project + investigates possible future directions for Scintilla to make it more flexible, robust, perform + better and run on the .NET and Java virtual machines. +

    +

    + SciTE is a SCIntilla based Text Editor. Originally built to + demonstrate Scintilla, it has grown to be a generally useful editor with facilities for + building and running programs. It is best used for jobs with simple configurations - I use it + for building test and demonstration programs as well as SciTE and Scintilla, themselves. +

    +

    + Development of Scintilla started as an effort to improve the text editor in PythonWin. After + being frustrated by problems in the Richedit control used by PythonWin, it looked like the + best way forward was to write a new edit control. The biggest problem with Richedit and other + similar controls is that they treat styling changes as important persistent changes to the + document so they are saved into the undo stack and set the document's dirty flag. For source + code, styling should not be persisted as it can be mechanically recreated. +

    +

    + Scintilla and SciTE are currently available for Intel Win32 and Linux compatible operating + systems with GTK+. They have been run on Windows 95, NT 4.0, Windows 2000, and on Red Hat + Linux 8 and 9 with GTK+ 1.2 and 2.0. Here is a screenshot of + SciTE.
    +

    +

    + You can download Scintilla. +

    +

    + The source code can be downloaded via CVS at the Source Forge + Scintilla project page. +

    +

    + Related sites. +

    +

    + Bugs and To Do list. +

    +

    + History and contribution credits. +

    +

    + Icons that can be used with Scintilla. +

    +

    + Questions and comments about Scintilla should be directed to the + scintilla-interest + mailing list, + which is for discussion of Scintilla and related projects, their bugs and future features. + This is a low traffic list, averaging less than 50 messages per week. + To avoid spam, only list members can write to the list. + Announcements of new versions of Scintilla go to both the scintilla-interest list and + scintilla-announce. + Messages sent to my personal email address that could have been sent to the list + may receive no response. +
    +

    + + + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Accessor.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Accessor.h new file mode 100644 index 00000000..5364b9e3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Accessor.h @@ -0,0 +1,78 @@ +// Scintilla source code edit control +/** @file Accessor.h + ** Rapid easy access to contents of a Scintilla. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +enum { wsSpace = 1, wsTab = 2, wsSpaceTab = 4, wsInconsistent=8}; + +class Accessor; + +typedef bool (*PFNIsCommentLeader)(Accessor &styler, int pos, int len); + +/** + * Interface to data in a Scintilla. + */ +class Accessor { +protected: + enum {extremePosition=0x7FFFFFFF}; + /** @a bufferSize is a trade off between time taken to copy the characters + * and retrieval overhead. + * @a slopSize positions the buffer before the desired position + * in case there is some backtracking. */ + enum {bufferSize=4000, slopSize=bufferSize/8}; + char buf[bufferSize+1]; + int startPos; + int endPos; + int codePage; + + virtual bool InternalIsLeadByte(char ch)=0; + virtual void Fill(int position)=0; + +public: + Accessor() : startPos(extremePosition), endPos(0), codePage(0) {} + virtual ~Accessor() {} + char operator[](int position) { + if (position < startPos || position >= endPos) { + Fill(position); + } + return buf[position - startPos]; + } + /** Safe version of operator[], returning a defined value for invalid position. */ + char SafeGetCharAt(int position, char chDefault=' ') { + if (position < startPos || position >= endPos) { + Fill(position); + if (position < startPos || position >= endPos) { + // Position is outside range of document + return chDefault; + } + } + return buf[position - startPos]; + } + bool IsLeadByte(char ch) { + return codePage && InternalIsLeadByte(ch); + } + void SetCodePage(int codePage_) { codePage = codePage_; } + + virtual bool Match(int pos, const char *s)=0; + virtual char StyleAt(int position)=0; + virtual int GetLine(int position)=0; + virtual int LineStart(int line)=0; + virtual int LevelAt(int line)=0; + virtual int Length()=0; + virtual void Flush()=0; + virtual int GetLineState(int line)=0; + virtual int SetLineState(int line, int state)=0; + virtual int GetPropertyInt(const char *key, int defaultValue=0)=0; + virtual char *GetProperties()=0; + + // Style setting + virtual void StartAt(unsigned int start, char chMask=31)=0; + virtual void SetFlags(char chFlags_, char chWhile_)=0; + virtual unsigned int GetStartSegment()=0; + virtual void StartSegment(unsigned int pos)=0; + virtual void ColourTo(unsigned int pos, int chAttr)=0; + virtual void SetLevel(int line, int level)=0; + virtual int IndentAmount(int line, int *flags, PFNIsCommentLeader pfnIsCommentLeader = 0)=0; +}; diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Face.py b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Face.py new file mode 100644 index 00000000..59ada3dc --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Face.py @@ -0,0 +1,107 @@ +# Module for reading and parsing Scintilla.iface file +import string + +def sanitiseLine(line): + if line[-1:] == '\n': line = line[:-1] + if string.find(line, "##") != -1: + line = line[:string.find(line, "##")] + line = string.strip(line) + return line + +def decodeFunction(featureVal): + retType, rest = string.split(featureVal, " ", 1) + nameIdent, params = string.split(rest, "(") + name, value = string.split(nameIdent, "=") + params, rest = string.split(params, ")") + param1, param2 = string.split(params, ",")[0:2] + return retType, name, value, param1, param2 + +def decodeEvent(featureVal): + retType, rest = string.split(featureVal, " ", 1) + nameIdent, params = string.split(rest, "(") + name, value = string.split(nameIdent, "=") + return retType, name, value + +def decodeParam(p): + param = string.strip(p) + type = "" + name = "" + value = "" + if " " in param: + type, nv = string.split(param, " ") + if "=" in nv: + name, value = string.split(nv, "=") + else: + name = nv + return type, name, value + +class Face: + + def __init__(self): + self.order = [] + self.features = {} + self.values = {} + self.events = {} + + def ReadFromFile(self, name): + currentCategory = "" + currentComment = [] + currentCommentFinished = 0 + file = open(name) + for line in file.readlines(): + line = sanitiseLine(line) + if line: + if line[0] == "#": + if line[1] == " ": + if currentCommentFinished: + currentComment = [] + currentCommentFinished = 0 + currentComment.append(line[2:]) + else: + currentCommentFinished = 1 + featureType, featureVal = string.split(line, " ", 1) + if featureType in ["fun", "get", "set"]: + retType, name, value, param1, param2 = decodeFunction(featureVal) + p1 = decodeParam(param1) + p2 = decodeParam(param2) + self.features[name] = { + "FeatureType": featureType, + "ReturnType": retType, + "Value": value, + "Param1Type": p1[0], "Param1Name": p1[1], "Param1Value": p1[2], + "Param2Type": p2[0], "Param2Name": p2[1], "Param2Value": p2[2], + "Category": currentCategory, "Comment": currentComment + } + if self.values.has_key(value): + raise "Duplicate value " + value + " " + name + self.values[value] = 1 + self.order.append(name) + elif featureType == "evt": + retType, name, value = decodeEvent(featureVal) + self.features[name] = { + "FeatureType": featureType, + "ReturnType": retType, + "Value": value, + "Category": currentCategory, "Comment": currentComment + } + if self.events.has_key(value): + raise "Duplicate event " + value + " " + name + self.events[value] = 1 + self.order.append(name) + elif featureType == "cat": + currentCategory = featureVal + elif featureType == "val": + name, value = string.split(featureVal, "=", 1) + self.features[name] = { + "FeatureType": featureType, + "Category": currentCategory, + "Value": value } + self.order.append(name) + elif featureType == "enu" or featureType == "lex": + name, value = string.split(featureVal, "=", 1) + self.features[name] = { + "FeatureType": featureType, + "Category": currentCategory, + "Value": value } + self.order.append(name) + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/HFacer.py b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/HFacer.py new file mode 100644 index 00000000..5639bcf7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/HFacer.py @@ -0,0 +1,76 @@ +# HFacer.py - regenerate the Scintilla.h and SciLexer.h files from the Scintilla.iface interface +# definition file. +# The header files are copied to a temporary file apart from the section between a //++Autogenerated +# comment and a //--Autogenerated comment which is generated by the printHFile and printLexHFile +# functions. After the temporary file is created, it is copied back to the original file name. + +import string +import sys +import os +import Face + +def Contains(s,sub): + return string.find(s, sub) != -1 + +def printLexHFile(f,out): + for name in f.order: + v = f.features[name] + if v["FeatureType"] in ["val"]: + if Contains(name, "SCE_") or Contains(name, "SCLEX_"): + out.write("#define " + name + " " + v["Value"] + "\n") + +def printHFile(f,out): + for name in f.order: + v = f.features[name] + if v["Category"] != "Deprecated": + if v["FeatureType"] in ["fun", "get", "set"]: + featureDefineName = "SCI_" + string.upper(name) + out.write("#define " + featureDefineName + " " + v["Value"] + "\n") + elif v["FeatureType"] in ["evt"]: + featureDefineName = "SCN_" + string.upper(name) + out.write("#define " + featureDefineName + " " + v["Value"] + "\n") + elif v["FeatureType"] in ["val"]: + if not (Contains(name, "SCE_") or Contains(name, "SCLEX_")): + out.write("#define " + name + " " + v["Value"] + "\n") + +def CopyWithInsertion(input, output, genfn, definition): + copying = 1 + for line in input.readlines(): + if copying: + output.write(line) + if Contains(line, "//++Autogenerated"): + copying = 0 + genfn(definition, output) + if Contains(line, "//--Autogenerated"): + copying = 1 + output.write(line) + +def contents(filename): + f = file(filename) + t = f.read() + f.close() + return t + +def Regenerate(filename, genfn, definition): + inText = contents(filename) + tempname = "HFacer.tmp" + out = open(tempname,"w") + hfile = open(filename) + CopyWithInsertion(hfile, out, genfn, definition) + out.close() + hfile.close() + outText = contents(tempname) + if inText == outText: + os.unlink(tempname) + else: + os.unlink(filename) + os.rename(tempname, filename) + +f = Face.Face() +try: + f.ReadFromFile("Scintilla.iface") + Regenerate("Scintilla.h", printHFile, f) + Regenerate("SciLexer.h", printLexHFile, f) + print "Maximum ID is", max(x for x in f.values if int(x) < 3000) +except: + raise diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/KeyWords.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/KeyWords.h new file mode 100644 index 00000000..2f173e0e --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/KeyWords.h @@ -0,0 +1,82 @@ +// Scintilla source code edit control +/** @file KeyWords.h + ** Colourise for particular languages. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +typedef void (*LexerFunction)(unsigned int startPos, int lengthDoc, int initStyle, + WordList *keywordlists[], Accessor &styler); + +/** + * A LexerModule is responsible for lexing and folding a particular language. + * The class maintains a list of LexerModules which can be searched to find a + * module appropriate to a particular language. + */ +class LexerModule { +protected: + const LexerModule *next; + int language; + LexerFunction fnLexer; + LexerFunction fnFolder; + const char * const * wordListDescriptions; + int styleBits; + + static const LexerModule *base; + static int nextLanguage; + +public: + const char *languageName; + LexerModule(int language_, + LexerFunction fnLexer_, + const char *languageName_=0, + LexerFunction fnFolder_=0, + const char * const wordListDescriptions_[] = NULL, + int styleBits_=5); + virtual ~LexerModule() { + } + int GetLanguage() const { return language; } + + // -1 is returned if no WordList information is available + int GetNumWordLists() const; + const char *GetWordListDescription(int index) const; + + int GetStyleBitsNeeded() const; + + virtual void Lex(unsigned int startPos, int lengthDoc, int initStyle, + WordList *keywordlists[], Accessor &styler) const; + virtual void Fold(unsigned int startPos, int lengthDoc, int initStyle, + WordList *keywordlists[], Accessor &styler) const; + static const LexerModule *Find(int language); + static const LexerModule *Find(const char *languageName); +}; + +/** + * Check if a character is a space. + * This is ASCII specific but is safe with chars >= 0x80. + */ +inline bool isspacechar(unsigned char ch) { + return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d)); +} + +inline bool iswordchar(char ch) { + return isascii(ch) && (isalnum(ch) || ch == '.' || ch == '_'); +} + +inline bool iswordstart(char ch) { + return isascii(ch) && (isalnum(ch) || ch == '_'); +} + +inline bool isoperator(char ch) { + if (isascii(ch) && isalnum(ch)) + return false; + // '.' left out as it is used to make up numbers + if (ch == '%' || ch == '^' || ch == '&' || ch == '*' || + ch == '(' || ch == ')' || ch == '-' || ch == '+' || + ch == '=' || ch == '|' || ch == '{' || ch == '}' || + ch == '[' || ch == ']' || ch == ':' || ch == ';' || + ch == '<' || ch == '>' || ch == ',' || ch == '/' || + ch == '?' || ch == '!' || ch == '.' || ch == '~') + return true; + return false; +} diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Platform.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Platform.h new file mode 100644 index 00000000..b713a528 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Platform.h @@ -0,0 +1,517 @@ +// Scintilla source code edit control +/** @file Platform.h + ** Interface to platform facilities. Also includes some basic utilities. + ** Implemented in PlatGTK.cxx for GTK+/Linux, PlatWin.cxx for Windows, and PlatWX.cxx for wxWindows. + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef PLATFORM_H +#define PLATFORM_H + +// PLAT_GTK = GTK+ on Linux or Win32 +// PLAT_GTK_WIN32 is defined additionally when running PLAT_GTK under Win32 +// PLAT_WIN = Win32 API on Win32 OS +// PLAT_WX is wxWindows on any supported platform + +#define PLAT_GTK 0 +#define PLAT_GTK_WIN32 0 +#define PLAT_WIN 0 +#define PLAT_WX 0 +#define PLAT_FOX 0 + +#if defined(FOX) +#undef PLAT_FOX +#define PLAT_FOX 1 + +#elif defined(__WX__) +#undef PLAT_WX +#define PLAT_WX 1 + +#elif defined(GTK) +#undef PLAT_GTK +#define PLAT_GTK 1 + +#ifdef _MSC_VER +#undef PLAT_GTK_WIN32 +#define PLAT_GTK_WIN32 1 +#endif + +#else +#undef PLAT_WIN +#define PLAT_WIN 1 + +#endif + + +// Underlying the implementation of the platform classes are platform specific types. +// Sometimes these need to be passed around by client code so they are defined here + +typedef void *FontID; +typedef void *SurfaceID; +typedef void *WindowID; +typedef void *MenuID; +typedef void *TickerID; +typedef void *Function; +typedef void *IdlerID; + +/** + * A geometric point class. + * Point is exactly the same as the Win32 POINT and GTK+ GdkPoint so can be used interchangeably. + */ +class Point { +public: + int x; + int y; + + explicit Point(int x_=0, int y_=0) : x(x_), y(y_) { + } + + // Other automatically defined methods (assignment, copy constructor, destructor) are fine + + static Point FromLong(long lpoint); +}; + +/** + * A geometric rectangle class. + * PRectangle is exactly the same as the Win32 RECT so can be used interchangeably. + * PRectangles contain their top and left sides, but not their right and bottom sides. + */ +class PRectangle { +public: + int left; + int top; + int right; + int bottom; + + PRectangle(int left_=0, int top_=0, int right_=0, int bottom_ = 0) : + left(left_), top(top_), right(right_), bottom(bottom_) { + } + + // Other automatically defined methods (assignment, copy constructor, destructor) are fine + + bool operator==(PRectangle &rc) { + return (rc.left == left) && (rc.right == right) && + (rc.top == top) && (rc.bottom == bottom); + } + bool Contains(Point pt) { + return (pt.x >= left) && (pt.x <= right) && + (pt.y >= top) && (pt.y <= bottom); + } + bool Contains(PRectangle rc) { + return (rc.left >= left) && (rc.right <= right) && + (rc.top >= top) && (rc.bottom <= bottom); + } + bool Intersects(PRectangle other) { + return (right > other.left) && (left < other.right) && + (bottom > other.top) && (top < other.bottom); + } + void Move(int xDelta, int yDelta) { + left += xDelta; + top += yDelta; + right += xDelta; + bottom += yDelta; + } + int Width() { return right - left; } + int Height() { return bottom - top; } +}; + +/** + * In some circumstances, including Win32 in paletted mode and GTK+, each colour + * must be allocated before use. The desired colours are held in the ColourDesired class, + * and after allocation the allocation entry is stored in the ColourAllocated class. In other + * circumstances, such as Win32 in true colour mode, the allocation process just copies + * the RGB values from the desired to the allocated class. + * As each desired colour requires allocation before it can be used, the ColourPair class + * holds both a ColourDesired and a ColourAllocated + * The Palette class is responsible for managing the palette of colours which contains a + * list of ColourPair objects and performs the allocation. + */ + +/** + * Holds a desired RGB colour. + */ +class ColourDesired { + long co; +public: + ColourDesired(long lcol=0) { + co = lcol; + } + + ColourDesired(unsigned int red, unsigned int green, unsigned int blue) { + Set(red, green, blue); + } + + bool operator==(const ColourDesired &other) const { + return co == other.co; + } + + void Set(long lcol) { + co = lcol; + } + + void Set(unsigned int red, unsigned int green, unsigned int blue) { + co = red | (green << 8) | (blue << 16); + } + + static inline unsigned int ValueOfHex(const char ch) { + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else + return 0; + } + + void Set(const char *val) { + if (*val == '#') { + val++; + } + unsigned int r = ValueOfHex(val[0]) * 16 + ValueOfHex(val[1]); + unsigned int g = ValueOfHex(val[2]) * 16 + ValueOfHex(val[3]); + unsigned int b = ValueOfHex(val[4]) * 16 + ValueOfHex(val[5]); + Set(r, g, b); + } + + long AsLong() const { + return co; + } + + unsigned int GetRed() { + return co & 0xff; + } + + unsigned int GetGreen() { + return (co >> 8) & 0xff; + } + + unsigned int GetBlue() { + return (co >> 16) & 0xff; + } +}; + +/** + * Holds an allocated RGB colour which may be an approximation to the desired colour. + */ +class ColourAllocated { + long coAllocated; + +public: + + ColourAllocated(long lcol=0) { + coAllocated = lcol; + } + + void Set(long lcol) { + coAllocated = lcol; + } + + long AsLong() const { + return coAllocated; + } +}; + +/** + * Colour pairs hold a desired colour and an allocated colour. + */ +struct ColourPair { + ColourDesired desired; + ColourAllocated allocated; + + ColourPair(ColourDesired desired_=ColourDesired(0,0,0)) { + desired = desired_; + allocated.Set(desired.AsLong()); + } + void Copy() { + allocated.Set(desired.AsLong()); + } +}; + +class Window; // Forward declaration for Palette + +/** + * Colour palette management. + */ +class Palette { + int used; + int size; + ColourPair *entries; +#if PLAT_GTK + void *allocatedPalette; // GdkColor * + int allocatedLen; +#endif + // Private so Palette objects can not be copied + Palette(const Palette &) {} + Palette &operator=(const Palette &) { return *this; } +public: +#if PLAT_WIN + void *hpal; +#endif + bool allowRealization; + + Palette(); + ~Palette(); + + void Release(); + + /** + * This method either adds a colour to the list of wanted colours (want==true) + * or retrieves the allocated colour back to the ColourPair. + * This is one method to make it easier to keep the code for wanting and retrieving in sync. + */ + void WantFind(ColourPair &cp, bool want); + + void Allocate(Window &w); +}; + +/** + * Font management. + */ +class Font { +protected: + FontID id; +#if PLAT_WX + int ascent; +#endif + // Private so Font objects can not be copied + Font(const Font &) {} + Font &operator=(const Font &) { id=0; return *this; } +public: + Font(); + virtual ~Font(); + + virtual void Create(const char *faceName, int characterSet, int size, + bool bold, bool italic, bool extraFontFlag=false); + virtual void Release(); + + FontID GetID() { return id; } + // Alias another font - caller guarantees not to Release + void SetID(FontID id_) { id = id_; } + friend class Surface; + friend class SurfaceImpl; +}; + +/** + * A surface abstracts a place to draw. + */ +class Surface { +private: + // Private so Surface objects can not be copied + Surface(const Surface &) {} + Surface &operator=(const Surface &) { return *this; } +public: + Surface() {}; + virtual ~Surface() {}; + static Surface *Allocate(); + + virtual void Init(WindowID wid)=0; + virtual void Init(SurfaceID sid, WindowID wid)=0; + virtual void InitPixMap(int width, int height, Surface *surface_, WindowID wid)=0; + + virtual void Release()=0; + virtual bool Initialised()=0; + virtual void PenColour(ColourAllocated fore)=0; + virtual int LogPixelsY()=0; + virtual int DeviceHeightFont(int points)=0; + virtual void MoveTo(int x_, int y_)=0; + virtual void LineTo(int x_, int y_)=0; + virtual void Polygon(Point *pts, int npts, ColourAllocated fore, ColourAllocated back)=0; + virtual void RectangleDraw(PRectangle rc, ColourAllocated fore, ColourAllocated back)=0; + virtual void FillRectangle(PRectangle rc, ColourAllocated back)=0; + virtual void FillRectangle(PRectangle rc, Surface &surfacePattern)=0; + virtual void RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back)=0; + virtual void AlphaRectangle(PRectangle rc, int cornerSize, ColourAllocated fill, int alphaFill, + ColourAllocated outline, int alphaOutline, int flags)=0; + virtual void Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back)=0; + virtual void Copy(PRectangle rc, Point from, Surface &surfaceSource)=0; + + virtual void DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back)=0; + virtual void DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore, ColourAllocated back)=0; + virtual void DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len, ColourAllocated fore)=0; + virtual void MeasureWidths(Font &font_, const char *s, int len, int *positions)=0; + virtual int WidthText(Font &font_, const char *s, int len)=0; + virtual int WidthChar(Font &font_, char ch)=0; + virtual int Ascent(Font &font_)=0; + virtual int Descent(Font &font_)=0; + virtual int InternalLeading(Font &font_)=0; + virtual int ExternalLeading(Font &font_)=0; + virtual int Height(Font &font_)=0; + virtual int AverageCharWidth(Font &font_)=0; + + virtual int SetPalette(Palette *pal, bool inBackGround)=0; + virtual void SetClip(PRectangle rc)=0; + virtual void FlushCachedState()=0; + + virtual void SetUnicodeMode(bool unicodeMode_)=0; + virtual void SetDBCSMode(int codePage)=0; +}; + +/** + * A simple callback action passing one piece of untyped user data. + */ +typedef void (*CallBackAction)(void*); + +/** + * Class to hide the details of window manipulation. + * Does not own the window which will normally have a longer life than this object. + */ +class Window { +protected: + WindowID id; +public: + Window() : id(0), cursorLast(cursorInvalid) {} + Window(const Window &source) : id(source.id), cursorLast(cursorInvalid) {} + virtual ~Window(); + Window &operator=(WindowID id_) { + id = id_; + return *this; + } + WindowID GetID() const { return id; } + bool Created() const { return id != 0; } + void Destroy(); + bool HasFocus(); + PRectangle GetPosition(); + void SetPosition(PRectangle rc); + void SetPositionRelative(PRectangle rc, Window relativeTo); + PRectangle GetClientPosition(); + void Show(bool show=true); + void InvalidateAll(); + void InvalidateRectangle(PRectangle rc); + virtual void SetFont(Font &font); + enum Cursor { cursorInvalid, cursorText, cursorArrow, cursorUp, cursorWait, cursorHoriz, cursorVert, cursorReverseArrow, cursorHand }; + void SetCursor(Cursor curs); + void SetTitle(const char *s); +private: + Cursor cursorLast; +}; + +/** + * Listbox management. + */ + +class ListBox : public Window { +public: + ListBox(); + virtual ~ListBox(); + static ListBox *Allocate(); + + virtual void SetFont(Font &font)=0; + virtual void Create(Window &parent, int ctrlID, Point location, int lineHeight_, bool unicodeMode_)=0; + virtual void SetAverageCharWidth(int width)=0; + virtual void SetVisibleRows(int rows)=0; + virtual int GetVisibleRows() const=0; + virtual PRectangle GetDesiredRect()=0; + virtual int CaretFromEdge()=0; + virtual void Clear()=0; + virtual void Append(char *s, int type = -1)=0; + virtual int Length()=0; + virtual void Select(int n)=0; + virtual int GetSelection()=0; + virtual int Find(const char *prefix)=0; + virtual void GetValue(int n, char *value, int len)=0; + virtual void RegisterImage(int type, const char *xpm_data)=0; + virtual void ClearRegisteredImages()=0; + virtual void SetDoubleClickAction(CallBackAction, void *)=0; + virtual void SetList(const char* list, char separator, char typesep)=0; +}; + +/** + * Menu management. + */ +class Menu { + MenuID id; +public: + Menu(); + MenuID GetID() { return id; } + void CreatePopUp(); + void Destroy(); + void Show(Point pt, Window &w); +}; + +class ElapsedTime { + long bigBit; + long littleBit; +public: + ElapsedTime(); + double Duration(bool reset=false); +}; + +/** + * Dynamic Library (DLL/SO/...) loading + */ +class DynamicLibrary { +public: + virtual ~DynamicLibrary() {}; + + /// @return Pointer to function "name", or NULL on failure. + virtual Function FindFunction(const char *name) = 0; + + /// @return true if the library was loaded successfully. + virtual bool IsValid() = 0; + + /// @return An instance of a DynamicLibrary subclass with "modulePath" loaded. + static DynamicLibrary *Load(const char *modulePath); +}; + +/** + * Platform class used to retrieve system wide parameters such as double click speed + * and chrome colour. Not a creatable object, more of a module with several functions. + */ +class Platform { + // Private so Platform objects can not be copied + Platform(const Platform &) {} + Platform &operator=(const Platform &) { return *this; } +public: + // Should be private because no new Platforms are ever created + // but gcc warns about this + Platform() {} + ~Platform() {} + static ColourDesired Chrome(); + static ColourDesired ChromeHighlight(); + static const char *DefaultFont(); + static int DefaultFontSize(); + static unsigned int DoubleClickTime(); + static bool MouseButtonBounce(); + static void DebugDisplay(const char *s); + static bool IsKeyDown(int key); + static long SendScintilla( + WindowID w, unsigned int msg, unsigned long wParam=0, long lParam=0); + static long SendScintillaPointer( + WindowID w, unsigned int msg, unsigned long wParam=0, void *lParam=0); + static bool IsDBCSLeadByte(int codePage, char ch); + static int DBCSCharLength(int codePage, const char *s); + static int DBCSCharMaxLength(); + + // These are utility functions not really tied to a platform + static int Minimum(int a, int b); + static int Maximum(int a, int b); + // Next three assume 16 bit shorts and 32 bit longs + static long LongFromTwoShorts(short a,short b) { + return (a) | ((b) << 16); + } + static short HighShortFromLong(long x) { + return static_cast(x >> 16); + } + static short LowShortFromLong(long x) { + return static_cast(x & 0xffff); + } + static void DebugPrintf(const char *format, ...); + static bool ShowAssertionPopUps(bool assertionPopUps_); + static void Assert(const char *c, const char *file, int line); + static int Clamp(int val, int minVal, int maxVal); +}; + +#ifdef NDEBUG +#define PLATFORM_ASSERT(c) ((void)0) +#else +#define PLATFORM_ASSERT(c) ((c) ? (void)(0) : Platform::Assert(#c, __FILE__, __LINE__)) +#endif + +// Shut up annoying Visual C++ warnings: +#ifdef _MSC_VER +#pragma warning(disable: 4244 4309 4514 4710) +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/PropSet.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/PropSet.h new file mode 100644 index 00000000..563a3291 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/PropSet.h @@ -0,0 +1,114 @@ +// Scintilla source code edit control +/** @file PropSet.h + ** A Java style properties file module. + **/ +// Copyright 1998-2002 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef PROPSET_H +#define PROPSET_H +#include "SString.h" + +bool EqualCaseInsensitive(const char *a, const char *b); + +bool isprefix(const char *target, const char *prefix); + +struct Property { + unsigned int hash; + char *key; + char *val; + Property *next; + Property() : hash(0), key(0), val(0), next(0) {} +}; + +/** + */ +class PropSet { +protected: + enum { hashRoots=31 }; + Property *props[hashRoots]; + Property *enumnext; + int enumhash; + static bool caseSensitiveFilenames; + static unsigned int HashString(const char *s, size_t len) { + unsigned int ret = 0; + while (len--) { + ret <<= 4; + ret ^= *s; + s++; + } + return ret; + } + static bool IncludesVar(const char *value, const char *key); + +public: + PropSet *superPS; + PropSet(); + ~PropSet(); + void Set(const char *key, const char *val, int lenKey=-1, int lenVal=-1); + void Set(const char *keyVal); + void Unset(const char *key, int lenKey=-1); + void SetMultiple(const char *s); + SString Get(const char *key); + SString GetExpanded(const char *key); + SString Expand(const char *withVars, int maxExpands=100); + int GetInt(const char *key, int defaultValue=0); + SString GetWild(const char *keybase, const char *filename); + SString GetNewExpand(const char *keybase, const char *filename=""); + void Clear(); + char *ToString(); // Caller must delete[] the return value + bool GetFirst(char **key, char **val); + bool GetNext(char **key, char **val); + static void SetCaseSensitiveFilenames(bool caseSensitiveFilenames_) { + caseSensitiveFilenames = caseSensitiveFilenames_; + } + +private: + // copy-value semantics not implemented + PropSet(const PropSet ©); + void operator=(const PropSet &assign); +}; + +/** + */ +class WordList { +public: + // Each word contains at least one character - a empty word acts as sentinel at the end. + char **words; + char **wordsNoCase; + char *list; + int len; + bool onlyLineEnds; ///< Delimited by any white space or only line ends + bool sorted; + bool sortedNoCase; + int starts[256]; + WordList(bool onlyLineEnds_ = false) : + words(0), wordsNoCase(0), list(0), len(0), onlyLineEnds(onlyLineEnds_), + sorted(false), sortedNoCase(false) {} + ~WordList() { Clear(); } + operator bool() { return len ? true : false; } + char *operator[](int ind) { return words[ind]; } + void Clear(); + void Set(const char *s); + char *Allocate(int size); + void SetFromAllocated(); + bool InList(const char *s); + bool InListAbbreviated(const char *s, const char marker); + const char *GetNearestWord(const char *wordStart, int searchLen, + bool ignoreCase = false, SString wordCharacters="", int wordIndex = -1); + char *GetNearestWords(const char *wordStart, int searchLen, + bool ignoreCase=false, char otherSeparator='\0', bool exactLen=false); +}; + +inline bool IsAlphabetic(unsigned int ch) { + return ((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z')); +} + + +#ifdef _MSC_VER +// Visual C++ doesn't like the private copy idiom for disabling +// the default copy constructor and operator=, but it's fine. +#pragma warning(disable: 4511 4512) +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/SString.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/SString.h new file mode 100644 index 00000000..780d1a11 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/SString.h @@ -0,0 +1,280 @@ +// SciTE - Scintilla based Text Editor +/** @file SString.h + ** A simple string class. + **/ +// Copyright 1998-2004 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef SSTRING_H +#define SSTRING_H + +// These functions are implemented because each platform calls them something different. +int CompareCaseInsensitive(const char *a, const char *b); +int CompareNCaseInsensitive(const char *a, const char *b, size_t len); +bool EqualCaseInsensitive(const char *a, const char *b); + +// Define another string class. +// While it would be 'better' to use std::string, that doubles the executable size. +// An SString may contain embedded nul characters. + +/** + * Base class from which the two other classes (SBuffer & SString) + * are derived. + */ +class SContainer { +public: + /** Type of string lengths (sizes) and positions (indexes). */ + typedef size_t lenpos_t; + /** Out of bounds value indicating that the string argument should be measured. */ + enum { measure_length=0xffffffffU}; + +protected: + char *s; ///< The C string + lenpos_t sSize; ///< The size of the buffer, less 1: ie. the maximum size of the string + + SContainer() : s(0), sSize(0) {} + ~SContainer() { + delete []s; // Suppose it was allocated using StringAllocate + s = 0; + sSize = 0; + } + /** Size of buffer. */ + lenpos_t size() const { + if (s) { + return sSize; + } else { + return 0; + } + } +public: + /** + * Allocate uninitialized memory big enough to fit a string of the given length. + * @return the pointer to the new string + */ + static char *StringAllocate(lenpos_t len); + /** + * Duplicate a buffer/C string. + * Allocate memory of the given size, or big enough to fit the string if length isn't given; + * then copy the given string in the allocated memory. + * @return the pointer to the new string + */ + static char *StringAllocate( + const char *s, ///< The string to duplicate + lenpos_t len=measure_length); ///< The length of memory to allocate. Optional. +}; + + +/** + * @brief A string buffer class. + * + * Main use is to ask an API the length of a string it can provide, + * then to allocate a buffer of the given size, and to provide this buffer + * to the API to put the string. + * This class is intended to be shortlived, to be transformed as SString + * as soon as it holds the string, so it has little members. + * Note: we assume the buffer is filled by the API. If the length can be shorter, + * we should set sLen to strlen(sb.ptr()) in related SString constructor and assignment. + */ +class SBuffer : protected SContainer { +public: + SBuffer(lenpos_t len) { + s = StringAllocate(len); + if (s) { + *s = '\0'; + sSize = len; + } else { + sSize = 0; + } + } +private: + /// Copy constructor + // Here only to be on the safe size, user should avoid returning SBuffer values. + SBuffer(const SBuffer &source) : SContainer() { + s = StringAllocate(source.s, source.sSize); + sSize = (s) ? source.sSize : 0; + } + /// Default assignment operator + // Same here, shouldn't be used + SBuffer &operator=(const SBuffer &source) { + if (this != &source) { + delete []s; + s = StringAllocate(source.s, source.sSize); + sSize = (s) ? source.sSize : 0; + } + return *this; + } +public: + /** Provide direct read/write access to buffer. */ + char *ptr() { + return s; + } + /** Ownership of the buffer have been taken, so release it. */ + void reset() { + s = 0; + sSize = 0; + } + /** Size of buffer. */ + lenpos_t size() const { + return SContainer::size(); + } +}; + + +/** + * @brief A simple string class. + * + * Hold the length of the string for quick operations, + * can have a buffer bigger than the string to avoid too many memory allocations and copies. + * May have embedded zeroes as a result of @a substitute, but relies too heavily on C string + * functions to allow reliable manipulations of these strings, other than simple appends, etc. + */ +class SString : protected SContainer { + lenpos_t sLen; ///< The size of the string in s + lenpos_t sizeGrowth; ///< Minimum growth size when appending strings + enum { sizeGrowthDefault = 64 }; + + bool grow(lenpos_t lenNew); + SString &assign(const char *sOther, lenpos_t sSize_=measure_length); + +public: + SString() : sLen(0), sizeGrowth(sizeGrowthDefault) {} + SString(const SString &source) : SContainer(), sizeGrowth(sizeGrowthDefault) { + s = StringAllocate(source.s, source.sLen); + sSize = sLen = (s) ? source.sLen : 0; + } + SString(const char *s_) : sizeGrowth(sizeGrowthDefault) { + s = StringAllocate(s_); + sSize = sLen = (s) ? strlen(s) : 0; + } + SString(SBuffer &buf) : sizeGrowth(sizeGrowthDefault) { + s = buf.ptr(); + sSize = sLen = buf.size(); + // Consumes the given buffer! + buf.reset(); + } + SString(const char *s_, lenpos_t first, lenpos_t last) : sizeGrowth(sizeGrowthDefault) { + // note: expects the "last" argument to point one beyond the range end (a la STL iterators) + s = StringAllocate(s_ + first, last - first); + sSize = sLen = (s) ? last - first : 0; + } + SString(int i); + SString(double d, int precision); + ~SString() { + sLen = 0; + } + void clear() { + if (s) { + *s = '\0'; + } + sLen = 0; + } + /** Size of buffer. */ + lenpos_t size() const { + return SContainer::size(); + } + /** Size of string in buffer. */ + lenpos_t length() const { + return sLen; + } + /** Read access to a character of the string. */ + char operator[](lenpos_t i) const { + return (s && i < sSize) ? s[i] : '\0'; + } + SString &operator=(const char *source) { + return assign(source); + } + SString &operator=(const SString &source) { + if (this != &source) { + assign(source.s, source.sLen); + } + return *this; + } + bool operator==(const SString &sOther) const; + bool operator!=(const SString &sOther) const { + return !operator==(sOther); + } + bool operator==(const char *sOther) const; + bool operator!=(const char *sOther) const { + return !operator==(sOther); + } + bool contains(char ch) const { + return (s && *s) ? strchr(s, ch) != 0 : false; + } + void setsizegrowth(lenpos_t sizeGrowth_) { + sizeGrowth = sizeGrowth_; + } + const char *c_str() const { + return s ? s : ""; + } + /** Give ownership of buffer to caller which must use delete[] to free buffer. */ + char *detach() { + char *sRet = s; + s = 0; + sSize = 0; + sLen = 0; + return sRet; + } + SString substr(lenpos_t subPos, lenpos_t subLen=measure_length) const; + SString &lowercase(lenpos_t subPos = 0, lenpos_t subLen=measure_length); + SString &uppercase(lenpos_t subPos = 0, lenpos_t subLen=measure_length); + SString &append(const char *sOther, lenpos_t sLenOther=measure_length, char sep = '\0'); + SString &operator+=(const char *sOther) { + return append(sOther, static_cast(measure_length)); + } + SString &operator+=(const SString &sOther) { + return append(sOther.s, sOther.sLen); + } + SString &operator+=(char ch) { + return append(&ch, 1); + } + SString &appendwithseparator(const char *sOther, char sep) { + return append(sOther, strlen(sOther), sep); + } + SString &insert(lenpos_t pos, const char *sOther, lenpos_t sLenOther=measure_length); + + /** + * Remove @a len characters from the @a pos position, included. + * Characters at pos + len and beyond replace characters at pos. + * If @a len is 0, or greater than the length of the string + * starting at @a pos, the string is just truncated at @a pos. + */ + void remove(lenpos_t pos, lenpos_t len); + + SString &change(lenpos_t pos, char ch) { + if (pos < sLen) { // character changed must be in string bounds + *(s + pos) = ch; + } + return *this; + } + /** Read an integral numeric value from the string. */ + int value() const { + return s ? atoi(s) : 0; + } + bool startswith(const char *prefix); + bool endswith(const char *suffix); + int search(const char *sFind, lenpos_t start=0) const; + bool contains(const char *sFind) const { + return search(sFind) >= 0; + } + int substitute(char chFind, char chReplace); + int substitute(const char *sFind, const char *sReplace); + int remove(const char *sFind) { + return substitute(sFind, ""); + } +}; + + +/** + * Duplicate a C string. + * Allocate memory of the given size, or big enough to fit the string if length isn't given; + * then copy the given string in the allocated memory. + * @return the pointer to the new string + */ +inline char *StringDup( + const char *s, ///< The string to duplicate + SContainer::lenpos_t len=SContainer::measure_length) ///< The length of memory to allocate. Optional. +{ + return SContainer::StringAllocate(s, len); +} + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/SciLexer.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/SciLexer.h new file mode 100644 index 00000000..9fda1180 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/SciLexer.h @@ -0,0 +1,1074 @@ +// Scintilla source code edit control +/** @file SciLexer.h + ** Interface to the added lexer functions in the SciLexer version of the edit control. + **/ +// Copyright 1998-2002 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +// Most of this file is automatically generated from the Scintilla.iface interface definition +// file which contains any comments about the definitions. HFacer.py does the generation. + +#ifndef SCILEXER_H +#define SCILEXER_H + +// SciLexer features - not in standard Scintilla + +//++Autogenerated -- start of section automatically generated from Scintilla.iface +#define SCLEX_CONTAINER 0 +#define SCLEX_NULL 1 +#define SCLEX_PYTHON 2 +#define SCLEX_CPP 3 +#define SCLEX_HTML 4 +#define SCLEX_XML 5 +#define SCLEX_PERL 6 +#define SCLEX_SQL 7 +#define SCLEX_VB 8 +#define SCLEX_PROPERTIES 9 +#define SCLEX_ERRORLIST 10 +#define SCLEX_MAKEFILE 11 +#define SCLEX_BATCH 12 +#define SCLEX_XCODE 13 +#define SCLEX_LATEX 14 +#define SCLEX_LUA 15 +#define SCLEX_DIFF 16 +#define SCLEX_CONF 17 +#define SCLEX_PASCAL 18 +#define SCLEX_AVE 19 +#define SCLEX_ADA 20 +#define SCLEX_LISP 21 +#define SCLEX_RUBY 22 +#define SCLEX_EIFFEL 23 +#define SCLEX_EIFFELKW 24 +#define SCLEX_TCL 25 +#define SCLEX_NNCRONTAB 26 +#define SCLEX_BULLANT 27 +#define SCLEX_VBSCRIPT 28 +#define SCLEX_BAAN 31 +#define SCLEX_MATLAB 32 +#define SCLEX_SCRIPTOL 33 +#define SCLEX_ASM 34 +#define SCLEX_CPPNOCASE 35 +#define SCLEX_FORTRAN 36 +#define SCLEX_F77 37 +#define SCLEX_CSS 38 +#define SCLEX_POV 39 +#define SCLEX_LOUT 40 +#define SCLEX_ESCRIPT 41 +#define SCLEX_PS 42 +#define SCLEX_NSIS 43 +#define SCLEX_MMIXAL 44 +#define SCLEX_CLW 45 +#define SCLEX_CLWNOCASE 46 +#define SCLEX_LOT 47 +#define SCLEX_YAML 48 +#define SCLEX_TEX 49 +#define SCLEX_METAPOST 50 +#define SCLEX_POWERBASIC 51 +#define SCLEX_FORTH 52 +#define SCLEX_ERLANG 53 +#define SCLEX_OCTAVE 54 +#define SCLEX_MSSQL 55 +#define SCLEX_VERILOG 56 +#define SCLEX_KIX 57 +#define SCLEX_GUI4CLI 58 +#define SCLEX_SPECMAN 59 +#define SCLEX_AU3 60 +#define SCLEX_APDL 61 +#define SCLEX_BASH 62 +#define SCLEX_ASN1 63 +#define SCLEX_VHDL 64 +#define SCLEX_CAML 65 +#define SCLEX_BLITZBASIC 66 +#define SCLEX_PUREBASIC 67 +#define SCLEX_HASKELL 68 +#define SCLEX_PHPSCRIPT 69 +#define SCLEX_TADS3 70 +#define SCLEX_REBOL 71 +#define SCLEX_SMALLTALK 72 +#define SCLEX_FLAGSHIP 73 +#define SCLEX_CSOUND 74 +#define SCLEX_FREEBASIC 75 +#define SCLEX_INNOSETUP 76 +#define SCLEX_OPAL 77 +#define SCLEX_SPICE 78 +#define SCLEX_AUTOMATIC 1000 +#define SCE_P_DEFAULT 0 +#define SCE_P_COMMENTLINE 1 +#define SCE_P_NUMBER 2 +#define SCE_P_STRING 3 +#define SCE_P_CHARACTER 4 +#define SCE_P_WORD 5 +#define SCE_P_TRIPLE 6 +#define SCE_P_TRIPLEDOUBLE 7 +#define SCE_P_CLASSNAME 8 +#define SCE_P_DEFNAME 9 +#define SCE_P_OPERATOR 10 +#define SCE_P_IDENTIFIER 11 +#define SCE_P_COMMENTBLOCK 12 +#define SCE_P_STRINGEOL 13 +#define SCE_P_WORD2 14 +#define SCE_P_DECORATOR 15 +#define SCE_C_DEFAULT 0 +#define SCE_C_COMMENT 1 +#define SCE_C_COMMENTLINE 2 +#define SCE_C_COMMENTDOC 3 +#define SCE_C_NUMBER 4 +#define SCE_C_WORD 5 +#define SCE_C_STRING 6 +#define SCE_C_CHARACTER 7 +#define SCE_C_UUID 8 +#define SCE_C_PREPROCESSOR 9 +#define SCE_C_OPERATOR 10 +#define SCE_C_IDENTIFIER 11 +#define SCE_C_STRINGEOL 12 +#define SCE_C_VERBATIM 13 +#define SCE_C_REGEX 14 +#define SCE_C_COMMENTLINEDOC 15 +#define SCE_C_WORD2 16 +#define SCE_C_COMMENTDOCKEYWORD 17 +#define SCE_C_COMMENTDOCKEYWORDERROR 18 +#define SCE_C_GLOBALCLASS 19 +#define SCE_TCL_DEFAULT 0 +#define SCE_TCL_COMMENT 1 +#define SCE_TCL_COMMENTLINE 2 +#define SCE_TCL_NUMBER 3 +#define SCE_TCL_WORD_IN_QUOTE 4 +#define SCE_TCL_IN_QUOTE 5 +#define SCE_TCL_OPERATOR 6 +#define SCE_TCL_IDENTIFIER 7 +#define SCE_TCL_SUBSTITUTION 8 +#define SCE_TCL_SUB_BRACE 9 +#define SCE_TCL_MODIFIER 10 +#define SCE_TCL_EXPAND 11 +#define SCE_TCL_WORD 12 +#define SCE_TCL_WORD2 13 +#define SCE_TCL_WORD3 14 +#define SCE_TCL_WORD4 15 +#define SCE_TCL_WORD5 16 +#define SCE_TCL_WORD6 17 +#define SCE_TCL_WORD7 18 +#define SCE_TCL_WORD8 19 +#define SCE_TCL_COMMENT_BOX 20 +#define SCE_TCL_BLOCK_COMMENT 21 +#define SCE_H_DEFAULT 0 +#define SCE_H_TAG 1 +#define SCE_H_TAGUNKNOWN 2 +#define SCE_H_ATTRIBUTE 3 +#define SCE_H_ATTRIBUTEUNKNOWN 4 +#define SCE_H_NUMBER 5 +#define SCE_H_DOUBLESTRING 6 +#define SCE_H_SINGLESTRING 7 +#define SCE_H_OTHER 8 +#define SCE_H_COMMENT 9 +#define SCE_H_ENTITY 10 +#define SCE_H_TAGEND 11 +#define SCE_H_XMLSTART 12 +#define SCE_H_XMLEND 13 +#define SCE_H_SCRIPT 14 +#define SCE_H_ASP 15 +#define SCE_H_ASPAT 16 +#define SCE_H_CDATA 17 +#define SCE_H_QUESTION 18 +#define SCE_H_VALUE 19 +#define SCE_H_XCCOMMENT 20 +#define SCE_H_SGML_DEFAULT 21 +#define SCE_H_SGML_COMMAND 22 +#define SCE_H_SGML_1ST_PARAM 23 +#define SCE_H_SGML_DOUBLESTRING 24 +#define SCE_H_SGML_SIMPLESTRING 25 +#define SCE_H_SGML_ERROR 26 +#define SCE_H_SGML_SPECIAL 27 +#define SCE_H_SGML_ENTITY 28 +#define SCE_H_SGML_COMMENT 29 +#define SCE_H_SGML_1ST_PARAM_COMMENT 30 +#define SCE_H_SGML_BLOCK_DEFAULT 31 +#define SCE_HJ_START 40 +#define SCE_HJ_DEFAULT 41 +#define SCE_HJ_COMMENT 42 +#define SCE_HJ_COMMENTLINE 43 +#define SCE_HJ_COMMENTDOC 44 +#define SCE_HJ_NUMBER 45 +#define SCE_HJ_WORD 46 +#define SCE_HJ_KEYWORD 47 +#define SCE_HJ_DOUBLESTRING 48 +#define SCE_HJ_SINGLESTRING 49 +#define SCE_HJ_SYMBOLS 50 +#define SCE_HJ_STRINGEOL 51 +#define SCE_HJ_REGEX 52 +#define SCE_HJA_START 55 +#define SCE_HJA_DEFAULT 56 +#define SCE_HJA_COMMENT 57 +#define SCE_HJA_COMMENTLINE 58 +#define SCE_HJA_COMMENTDOC 59 +#define SCE_HJA_NUMBER 60 +#define SCE_HJA_WORD 61 +#define SCE_HJA_KEYWORD 62 +#define SCE_HJA_DOUBLESTRING 63 +#define SCE_HJA_SINGLESTRING 64 +#define SCE_HJA_SYMBOLS 65 +#define SCE_HJA_STRINGEOL 66 +#define SCE_HJA_REGEX 67 +#define SCE_HB_START 70 +#define SCE_HB_DEFAULT 71 +#define SCE_HB_COMMENTLINE 72 +#define SCE_HB_NUMBER 73 +#define SCE_HB_WORD 74 +#define SCE_HB_STRING 75 +#define SCE_HB_IDENTIFIER 76 +#define SCE_HB_STRINGEOL 77 +#define SCE_HBA_START 80 +#define SCE_HBA_DEFAULT 81 +#define SCE_HBA_COMMENTLINE 82 +#define SCE_HBA_NUMBER 83 +#define SCE_HBA_WORD 84 +#define SCE_HBA_STRING 85 +#define SCE_HBA_IDENTIFIER 86 +#define SCE_HBA_STRINGEOL 87 +#define SCE_HP_START 90 +#define SCE_HP_DEFAULT 91 +#define SCE_HP_COMMENTLINE 92 +#define SCE_HP_NUMBER 93 +#define SCE_HP_STRING 94 +#define SCE_HP_CHARACTER 95 +#define SCE_HP_WORD 96 +#define SCE_HP_TRIPLE 97 +#define SCE_HP_TRIPLEDOUBLE 98 +#define SCE_HP_CLASSNAME 99 +#define SCE_HP_DEFNAME 100 +#define SCE_HP_OPERATOR 101 +#define SCE_HP_IDENTIFIER 102 +#define SCE_HPHP_COMPLEX_VARIABLE 104 +#define SCE_HPA_START 105 +#define SCE_HPA_DEFAULT 106 +#define SCE_HPA_COMMENTLINE 107 +#define SCE_HPA_NUMBER 108 +#define SCE_HPA_STRING 109 +#define SCE_HPA_CHARACTER 110 +#define SCE_HPA_WORD 111 +#define SCE_HPA_TRIPLE 112 +#define SCE_HPA_TRIPLEDOUBLE 113 +#define SCE_HPA_CLASSNAME 114 +#define SCE_HPA_DEFNAME 115 +#define SCE_HPA_OPERATOR 116 +#define SCE_HPA_IDENTIFIER 117 +#define SCE_HPHP_DEFAULT 118 +#define SCE_HPHP_HSTRING 119 +#define SCE_HPHP_SIMPLESTRING 120 +#define SCE_HPHP_WORD 121 +#define SCE_HPHP_NUMBER 122 +#define SCE_HPHP_VARIABLE 123 +#define SCE_HPHP_COMMENT 124 +#define SCE_HPHP_COMMENTLINE 125 +#define SCE_HPHP_HSTRING_VARIABLE 126 +#define SCE_HPHP_OPERATOR 127 +#define SCE_PL_DEFAULT 0 +#define SCE_PL_ERROR 1 +#define SCE_PL_COMMENTLINE 2 +#define SCE_PL_POD 3 +#define SCE_PL_NUMBER 4 +#define SCE_PL_WORD 5 +#define SCE_PL_STRING 6 +#define SCE_PL_CHARACTER 7 +#define SCE_PL_PUNCTUATION 8 +#define SCE_PL_PREPROCESSOR 9 +#define SCE_PL_OPERATOR 10 +#define SCE_PL_IDENTIFIER 11 +#define SCE_PL_SCALAR 12 +#define SCE_PL_ARRAY 13 +#define SCE_PL_HASH 14 +#define SCE_PL_SYMBOLTABLE 15 +#define SCE_PL_VARIABLE_INDEXER 16 +#define SCE_PL_REGEX 17 +#define SCE_PL_REGSUBST 18 +#define SCE_PL_LONGQUOTE 19 +#define SCE_PL_BACKTICKS 20 +#define SCE_PL_DATASECTION 21 +#define SCE_PL_HERE_DELIM 22 +#define SCE_PL_HERE_Q 23 +#define SCE_PL_HERE_QQ 24 +#define SCE_PL_HERE_QX 25 +#define SCE_PL_STRING_Q 26 +#define SCE_PL_STRING_QQ 27 +#define SCE_PL_STRING_QX 28 +#define SCE_PL_STRING_QR 29 +#define SCE_PL_STRING_QW 30 +#define SCE_PL_POD_VERB 31 +#define SCE_RB_DEFAULT 0 +#define SCE_RB_ERROR 1 +#define SCE_RB_COMMENTLINE 2 +#define SCE_RB_POD 3 +#define SCE_RB_NUMBER 4 +#define SCE_RB_WORD 5 +#define SCE_RB_STRING 6 +#define SCE_RB_CHARACTER 7 +#define SCE_RB_CLASSNAME 8 +#define SCE_RB_DEFNAME 9 +#define SCE_RB_OPERATOR 10 +#define SCE_RB_IDENTIFIER 11 +#define SCE_RB_REGEX 12 +#define SCE_RB_GLOBAL 13 +#define SCE_RB_SYMBOL 14 +#define SCE_RB_MODULE_NAME 15 +#define SCE_RB_INSTANCE_VAR 16 +#define SCE_RB_CLASS_VAR 17 +#define SCE_RB_BACKTICKS 18 +#define SCE_RB_DATASECTION 19 +#define SCE_RB_HERE_DELIM 20 +#define SCE_RB_HERE_Q 21 +#define SCE_RB_HERE_QQ 22 +#define SCE_RB_HERE_QX 23 +#define SCE_RB_STRING_Q 24 +#define SCE_RB_STRING_QQ 25 +#define SCE_RB_STRING_QX 26 +#define SCE_RB_STRING_QR 27 +#define SCE_RB_STRING_QW 28 +#define SCE_RB_WORD_DEMOTED 29 +#define SCE_RB_STDIN 30 +#define SCE_RB_STDOUT 31 +#define SCE_RB_STDERR 40 +#define SCE_RB_UPPER_BOUND 41 +#define SCE_B_DEFAULT 0 +#define SCE_B_COMMENT 1 +#define SCE_B_NUMBER 2 +#define SCE_B_KEYWORD 3 +#define SCE_B_STRING 4 +#define SCE_B_PREPROCESSOR 5 +#define SCE_B_OPERATOR 6 +#define SCE_B_IDENTIFIER 7 +#define SCE_B_DATE 8 +#define SCE_B_STRINGEOL 9 +#define SCE_B_KEYWORD2 10 +#define SCE_B_KEYWORD3 11 +#define SCE_B_KEYWORD4 12 +#define SCE_B_CONSTANT 13 +#define SCE_B_ASM 14 +#define SCE_B_LABEL 15 +#define SCE_B_ERROR 16 +#define SCE_B_HEXNUMBER 17 +#define SCE_B_BINNUMBER 18 +#define SCE_PROPS_DEFAULT 0 +#define SCE_PROPS_COMMENT 1 +#define SCE_PROPS_SECTION 2 +#define SCE_PROPS_ASSIGNMENT 3 +#define SCE_PROPS_DEFVAL 4 +#define SCE_PROPS_KEY 5 +#define SCE_L_DEFAULT 0 +#define SCE_L_COMMAND 1 +#define SCE_L_TAG 2 +#define SCE_L_MATH 3 +#define SCE_L_COMMENT 4 +#define SCE_LUA_DEFAULT 0 +#define SCE_LUA_COMMENT 1 +#define SCE_LUA_COMMENTLINE 2 +#define SCE_LUA_COMMENTDOC 3 +#define SCE_LUA_NUMBER 4 +#define SCE_LUA_WORD 5 +#define SCE_LUA_STRING 6 +#define SCE_LUA_CHARACTER 7 +#define SCE_LUA_LITERALSTRING 8 +#define SCE_LUA_PREPROCESSOR 9 +#define SCE_LUA_OPERATOR 10 +#define SCE_LUA_IDENTIFIER 11 +#define SCE_LUA_STRINGEOL 12 +#define SCE_LUA_WORD2 13 +#define SCE_LUA_WORD3 14 +#define SCE_LUA_WORD4 15 +#define SCE_LUA_WORD5 16 +#define SCE_LUA_WORD6 17 +#define SCE_LUA_WORD7 18 +#define SCE_LUA_WORD8 19 +#define SCE_ERR_DEFAULT 0 +#define SCE_ERR_PYTHON 1 +#define SCE_ERR_GCC 2 +#define SCE_ERR_MS 3 +#define SCE_ERR_CMD 4 +#define SCE_ERR_BORLAND 5 +#define SCE_ERR_PERL 6 +#define SCE_ERR_NET 7 +#define SCE_ERR_LUA 8 +#define SCE_ERR_CTAG 9 +#define SCE_ERR_DIFF_CHANGED 10 +#define SCE_ERR_DIFF_ADDITION 11 +#define SCE_ERR_DIFF_DELETION 12 +#define SCE_ERR_DIFF_MESSAGE 13 +#define SCE_ERR_PHP 14 +#define SCE_ERR_ELF 15 +#define SCE_ERR_IFC 16 +#define SCE_ERR_IFORT 17 +#define SCE_ERR_ABSF 18 +#define SCE_ERR_TIDY 19 +#define SCE_ERR_JAVA_STACK 20 +#define SCE_BAT_DEFAULT 0 +#define SCE_BAT_COMMENT 1 +#define SCE_BAT_WORD 2 +#define SCE_BAT_LABEL 3 +#define SCE_BAT_HIDE 4 +#define SCE_BAT_COMMAND 5 +#define SCE_BAT_IDENTIFIER 6 +#define SCE_BAT_OPERATOR 7 +#define SCE_MAKE_DEFAULT 0 +#define SCE_MAKE_COMMENT 1 +#define SCE_MAKE_PREPROCESSOR 2 +#define SCE_MAKE_IDENTIFIER 3 +#define SCE_MAKE_OPERATOR 4 +#define SCE_MAKE_TARGET 5 +#define SCE_MAKE_IDEOL 9 +#define SCE_DIFF_DEFAULT 0 +#define SCE_DIFF_COMMENT 1 +#define SCE_DIFF_COMMAND 2 +#define SCE_DIFF_HEADER 3 +#define SCE_DIFF_POSITION 4 +#define SCE_DIFF_DELETED 5 +#define SCE_DIFF_ADDED 6 +#define SCE_CONF_DEFAULT 0 +#define SCE_CONF_COMMENT 1 +#define SCE_CONF_NUMBER 2 +#define SCE_CONF_IDENTIFIER 3 +#define SCE_CONF_EXTENSION 4 +#define SCE_CONF_PARAMETER 5 +#define SCE_CONF_STRING 6 +#define SCE_CONF_OPERATOR 7 +#define SCE_CONF_IP 8 +#define SCE_CONF_DIRECTIVE 9 +#define SCE_AVE_DEFAULT 0 +#define SCE_AVE_COMMENT 1 +#define SCE_AVE_NUMBER 2 +#define SCE_AVE_WORD 3 +#define SCE_AVE_STRING 6 +#define SCE_AVE_ENUM 7 +#define SCE_AVE_STRINGEOL 8 +#define SCE_AVE_IDENTIFIER 9 +#define SCE_AVE_OPERATOR 10 +#define SCE_AVE_WORD1 11 +#define SCE_AVE_WORD2 12 +#define SCE_AVE_WORD3 13 +#define SCE_AVE_WORD4 14 +#define SCE_AVE_WORD5 15 +#define SCE_AVE_WORD6 16 +#define SCE_ADA_DEFAULT 0 +#define SCE_ADA_WORD 1 +#define SCE_ADA_IDENTIFIER 2 +#define SCE_ADA_NUMBER 3 +#define SCE_ADA_DELIMITER 4 +#define SCE_ADA_CHARACTER 5 +#define SCE_ADA_CHARACTEREOL 6 +#define SCE_ADA_STRING 7 +#define SCE_ADA_STRINGEOL 8 +#define SCE_ADA_LABEL 9 +#define SCE_ADA_COMMENTLINE 10 +#define SCE_ADA_ILLEGAL 11 +#define SCE_BAAN_DEFAULT 0 +#define SCE_BAAN_COMMENT 1 +#define SCE_BAAN_COMMENTDOC 2 +#define SCE_BAAN_NUMBER 3 +#define SCE_BAAN_WORD 4 +#define SCE_BAAN_STRING 5 +#define SCE_BAAN_PREPROCESSOR 6 +#define SCE_BAAN_OPERATOR 7 +#define SCE_BAAN_IDENTIFIER 8 +#define SCE_BAAN_STRINGEOL 9 +#define SCE_BAAN_WORD2 10 +#define SCE_LISP_DEFAULT 0 +#define SCE_LISP_COMMENT 1 +#define SCE_LISP_NUMBER 2 +#define SCE_LISP_KEYWORD 3 +#define SCE_LISP_KEYWORD_KW 4 +#define SCE_LISP_SYMBOL 5 +#define SCE_LISP_STRING 6 +#define SCE_LISP_STRINGEOL 8 +#define SCE_LISP_IDENTIFIER 9 +#define SCE_LISP_OPERATOR 10 +#define SCE_LISP_SPECIAL 11 +#define SCE_LISP_MULTI_COMMENT 12 +#define SCE_EIFFEL_DEFAULT 0 +#define SCE_EIFFEL_COMMENTLINE 1 +#define SCE_EIFFEL_NUMBER 2 +#define SCE_EIFFEL_WORD 3 +#define SCE_EIFFEL_STRING 4 +#define SCE_EIFFEL_CHARACTER 5 +#define SCE_EIFFEL_OPERATOR 6 +#define SCE_EIFFEL_IDENTIFIER 7 +#define SCE_EIFFEL_STRINGEOL 8 +#define SCE_NNCRONTAB_DEFAULT 0 +#define SCE_NNCRONTAB_COMMENT 1 +#define SCE_NNCRONTAB_TASK 2 +#define SCE_NNCRONTAB_SECTION 3 +#define SCE_NNCRONTAB_KEYWORD 4 +#define SCE_NNCRONTAB_MODIFIER 5 +#define SCE_NNCRONTAB_ASTERISK 6 +#define SCE_NNCRONTAB_NUMBER 7 +#define SCE_NNCRONTAB_STRING 8 +#define SCE_NNCRONTAB_ENVIRONMENT 9 +#define SCE_NNCRONTAB_IDENTIFIER 10 +#define SCE_FORTH_DEFAULT 0 +#define SCE_FORTH_COMMENT 1 +#define SCE_FORTH_COMMENT_ML 2 +#define SCE_FORTH_IDENTIFIER 3 +#define SCE_FORTH_CONTROL 4 +#define SCE_FORTH_KEYWORD 5 +#define SCE_FORTH_DEFWORD 6 +#define SCE_FORTH_PREWORD1 7 +#define SCE_FORTH_PREWORD2 8 +#define SCE_FORTH_NUMBER 9 +#define SCE_FORTH_STRING 10 +#define SCE_FORTH_LOCALE 11 +#define SCE_MATLAB_DEFAULT 0 +#define SCE_MATLAB_COMMENT 1 +#define SCE_MATLAB_COMMAND 2 +#define SCE_MATLAB_NUMBER 3 +#define SCE_MATLAB_KEYWORD 4 +#define SCE_MATLAB_STRING 5 +#define SCE_MATLAB_OPERATOR 6 +#define SCE_MATLAB_IDENTIFIER 7 +#define SCE_MATLAB_DOUBLEQUOTESTRING 8 +#define SCE_SCRIPTOL_DEFAULT 0 +#define SCE_SCRIPTOL_WHITE 1 +#define SCE_SCRIPTOL_COMMENTLINE 2 +#define SCE_SCRIPTOL_PERSISTENT 3 +#define SCE_SCRIPTOL_CSTYLE 4 +#define SCE_SCRIPTOL_COMMENTBLOCK 5 +#define SCE_SCRIPTOL_NUMBER 6 +#define SCE_SCRIPTOL_STRING 7 +#define SCE_SCRIPTOL_CHARACTER 8 +#define SCE_SCRIPTOL_STRINGEOL 9 +#define SCE_SCRIPTOL_KEYWORD 10 +#define SCE_SCRIPTOL_OPERATOR 11 +#define SCE_SCRIPTOL_IDENTIFIER 12 +#define SCE_SCRIPTOL_TRIPLE 13 +#define SCE_SCRIPTOL_CLASSNAME 14 +#define SCE_SCRIPTOL_PREPROCESSOR 15 +#define SCE_ASM_DEFAULT 0 +#define SCE_ASM_COMMENT 1 +#define SCE_ASM_NUMBER 2 +#define SCE_ASM_STRING 3 +#define SCE_ASM_OPERATOR 4 +#define SCE_ASM_IDENTIFIER 5 +#define SCE_ASM_CPUINSTRUCTION 6 +#define SCE_ASM_MATHINSTRUCTION 7 +#define SCE_ASM_REGISTER 8 +#define SCE_ASM_DIRECTIVE 9 +#define SCE_ASM_DIRECTIVEOPERAND 10 +#define SCE_ASM_COMMENTBLOCK 11 +#define SCE_ASM_CHARACTER 12 +#define SCE_ASM_STRINGEOL 13 +#define SCE_ASM_EXTINSTRUCTION 14 +#define SCE_F_DEFAULT 0 +#define SCE_F_COMMENT 1 +#define SCE_F_NUMBER 2 +#define SCE_F_STRING1 3 +#define SCE_F_STRING2 4 +#define SCE_F_STRINGEOL 5 +#define SCE_F_OPERATOR 6 +#define SCE_F_IDENTIFIER 7 +#define SCE_F_WORD 8 +#define SCE_F_WORD2 9 +#define SCE_F_WORD3 10 +#define SCE_F_PREPROCESSOR 11 +#define SCE_F_OPERATOR2 12 +#define SCE_F_LABEL 13 +#define SCE_F_CONTINUATION 14 +#define SCE_CSS_DEFAULT 0 +#define SCE_CSS_TAG 1 +#define SCE_CSS_CLASS 2 +#define SCE_CSS_PSEUDOCLASS 3 +#define SCE_CSS_UNKNOWN_PSEUDOCLASS 4 +#define SCE_CSS_OPERATOR 5 +#define SCE_CSS_IDENTIFIER 6 +#define SCE_CSS_UNKNOWN_IDENTIFIER 7 +#define SCE_CSS_VALUE 8 +#define SCE_CSS_COMMENT 9 +#define SCE_CSS_ID 10 +#define SCE_CSS_IMPORTANT 11 +#define SCE_CSS_DIRECTIVE 12 +#define SCE_CSS_DOUBLESTRING 13 +#define SCE_CSS_SINGLESTRING 14 +#define SCE_CSS_IDENTIFIER2 15 +#define SCE_CSS_ATTRIBUTE 16 +#define SCE_POV_DEFAULT 0 +#define SCE_POV_COMMENT 1 +#define SCE_POV_COMMENTLINE 2 +#define SCE_POV_NUMBER 3 +#define SCE_POV_OPERATOR 4 +#define SCE_POV_IDENTIFIER 5 +#define SCE_POV_STRING 6 +#define SCE_POV_STRINGEOL 7 +#define SCE_POV_DIRECTIVE 8 +#define SCE_POV_BADDIRECTIVE 9 +#define SCE_POV_WORD2 10 +#define SCE_POV_WORD3 11 +#define SCE_POV_WORD4 12 +#define SCE_POV_WORD5 13 +#define SCE_POV_WORD6 14 +#define SCE_POV_WORD7 15 +#define SCE_POV_WORD8 16 +#define SCE_LOUT_DEFAULT 0 +#define SCE_LOUT_COMMENT 1 +#define SCE_LOUT_NUMBER 2 +#define SCE_LOUT_WORD 3 +#define SCE_LOUT_WORD2 4 +#define SCE_LOUT_WORD3 5 +#define SCE_LOUT_WORD4 6 +#define SCE_LOUT_STRING 7 +#define SCE_LOUT_OPERATOR 8 +#define SCE_LOUT_IDENTIFIER 9 +#define SCE_LOUT_STRINGEOL 10 +#define SCE_ESCRIPT_DEFAULT 0 +#define SCE_ESCRIPT_COMMENT 1 +#define SCE_ESCRIPT_COMMENTLINE 2 +#define SCE_ESCRIPT_COMMENTDOC 3 +#define SCE_ESCRIPT_NUMBER 4 +#define SCE_ESCRIPT_WORD 5 +#define SCE_ESCRIPT_STRING 6 +#define SCE_ESCRIPT_OPERATOR 7 +#define SCE_ESCRIPT_IDENTIFIER 8 +#define SCE_ESCRIPT_BRACE 9 +#define SCE_ESCRIPT_WORD2 10 +#define SCE_ESCRIPT_WORD3 11 +#define SCE_PS_DEFAULT 0 +#define SCE_PS_COMMENT 1 +#define SCE_PS_DSC_COMMENT 2 +#define SCE_PS_DSC_VALUE 3 +#define SCE_PS_NUMBER 4 +#define SCE_PS_NAME 5 +#define SCE_PS_KEYWORD 6 +#define SCE_PS_LITERAL 7 +#define SCE_PS_IMMEVAL 8 +#define SCE_PS_PAREN_ARRAY 9 +#define SCE_PS_PAREN_DICT 10 +#define SCE_PS_PAREN_PROC 11 +#define SCE_PS_TEXT 12 +#define SCE_PS_HEXSTRING 13 +#define SCE_PS_BASE85STRING 14 +#define SCE_PS_BADSTRINGCHAR 15 +#define SCE_NSIS_DEFAULT 0 +#define SCE_NSIS_COMMENT 1 +#define SCE_NSIS_STRINGDQ 2 +#define SCE_NSIS_STRINGLQ 3 +#define SCE_NSIS_STRINGRQ 4 +#define SCE_NSIS_FUNCTION 5 +#define SCE_NSIS_VARIABLE 6 +#define SCE_NSIS_LABEL 7 +#define SCE_NSIS_USERDEFINED 8 +#define SCE_NSIS_SECTIONDEF 9 +#define SCE_NSIS_SUBSECTIONDEF 10 +#define SCE_NSIS_IFDEFINEDEF 11 +#define SCE_NSIS_MACRODEF 12 +#define SCE_NSIS_STRINGVAR 13 +#define SCE_NSIS_NUMBER 14 +#define SCE_NSIS_SECTIONGROUP 15 +#define SCE_NSIS_PAGEEX 16 +#define SCE_NSIS_FUNCTIONDEF 17 +#define SCE_NSIS_COMMENTBOX 18 +#define SCE_MMIXAL_LEADWS 0 +#define SCE_MMIXAL_COMMENT 1 +#define SCE_MMIXAL_LABEL 2 +#define SCE_MMIXAL_OPCODE 3 +#define SCE_MMIXAL_OPCODE_PRE 4 +#define SCE_MMIXAL_OPCODE_VALID 5 +#define SCE_MMIXAL_OPCODE_UNKNOWN 6 +#define SCE_MMIXAL_OPCODE_POST 7 +#define SCE_MMIXAL_OPERANDS 8 +#define SCE_MMIXAL_NUMBER 9 +#define SCE_MMIXAL_REF 10 +#define SCE_MMIXAL_CHAR 11 +#define SCE_MMIXAL_STRING 12 +#define SCE_MMIXAL_REGISTER 13 +#define SCE_MMIXAL_HEX 14 +#define SCE_MMIXAL_OPERATOR 15 +#define SCE_MMIXAL_SYMBOL 16 +#define SCE_MMIXAL_INCLUDE 17 +#define SCE_CLW_DEFAULT 0 +#define SCE_CLW_LABEL 1 +#define SCE_CLW_COMMENT 2 +#define SCE_CLW_STRING 3 +#define SCE_CLW_USER_IDENTIFIER 4 +#define SCE_CLW_INTEGER_CONSTANT 5 +#define SCE_CLW_REAL_CONSTANT 6 +#define SCE_CLW_PICTURE_STRING 7 +#define SCE_CLW_KEYWORD 8 +#define SCE_CLW_COMPILER_DIRECTIVE 9 +#define SCE_CLW_RUNTIME_EXPRESSIONS 10 +#define SCE_CLW_BUILTIN_PROCEDURES_FUNCTION 11 +#define SCE_CLW_STRUCTURE_DATA_TYPE 12 +#define SCE_CLW_ATTRIBUTE 13 +#define SCE_CLW_STANDARD_EQUATE 14 +#define SCE_CLW_ERROR 15 +#define SCE_CLW_DEPRECATED 16 +#define SCE_LOT_DEFAULT 0 +#define SCE_LOT_HEADER 1 +#define SCE_LOT_BREAK 2 +#define SCE_LOT_SET 3 +#define SCE_LOT_PASS 4 +#define SCE_LOT_FAIL 5 +#define SCE_LOT_ABORT 6 +#define SCE_YAML_DEFAULT 0 +#define SCE_YAML_COMMENT 1 +#define SCE_YAML_IDENTIFIER 2 +#define SCE_YAML_KEYWORD 3 +#define SCE_YAML_NUMBER 4 +#define SCE_YAML_REFERENCE 5 +#define SCE_YAML_DOCUMENT 6 +#define SCE_YAML_TEXT 7 +#define SCE_YAML_ERROR 8 +#define SCE_TEX_DEFAULT 0 +#define SCE_TEX_SPECIAL 1 +#define SCE_TEX_GROUP 2 +#define SCE_TEX_SYMBOL 3 +#define SCE_TEX_COMMAND 4 +#define SCE_TEX_TEXT 5 +#define SCE_METAPOST_DEFAULT 0 +#define SCE_METAPOST_SPECIAL 1 +#define SCE_METAPOST_GROUP 2 +#define SCE_METAPOST_SYMBOL 3 +#define SCE_METAPOST_COMMAND 4 +#define SCE_METAPOST_TEXT 5 +#define SCE_METAPOST_EXTRA 6 +#define SCE_ERLANG_DEFAULT 0 +#define SCE_ERLANG_COMMENT 1 +#define SCE_ERLANG_VARIABLE 2 +#define SCE_ERLANG_NUMBER 3 +#define SCE_ERLANG_KEYWORD 4 +#define SCE_ERLANG_STRING 5 +#define SCE_ERLANG_OPERATOR 6 +#define SCE_ERLANG_ATOM 7 +#define SCE_ERLANG_FUNCTION_NAME 8 +#define SCE_ERLANG_CHARACTER 9 +#define SCE_ERLANG_MACRO 10 +#define SCE_ERLANG_RECORD 11 +#define SCE_ERLANG_SEPARATOR 12 +#define SCE_ERLANG_NODE_NAME 13 +#define SCE_ERLANG_UNKNOWN 31 +#define SCE_MSSQL_DEFAULT 0 +#define SCE_MSSQL_COMMENT 1 +#define SCE_MSSQL_LINE_COMMENT 2 +#define SCE_MSSQL_NUMBER 3 +#define SCE_MSSQL_STRING 4 +#define SCE_MSSQL_OPERATOR 5 +#define SCE_MSSQL_IDENTIFIER 6 +#define SCE_MSSQL_VARIABLE 7 +#define SCE_MSSQL_COLUMN_NAME 8 +#define SCE_MSSQL_STATEMENT 9 +#define SCE_MSSQL_DATATYPE 10 +#define SCE_MSSQL_SYSTABLE 11 +#define SCE_MSSQL_GLOBAL_VARIABLE 12 +#define SCE_MSSQL_FUNCTION 13 +#define SCE_MSSQL_STORED_PROCEDURE 14 +#define SCE_MSSQL_DEFAULT_PREF_DATATYPE 15 +#define SCE_MSSQL_COLUMN_NAME_2 16 +#define SCE_V_DEFAULT 0 +#define SCE_V_COMMENT 1 +#define SCE_V_COMMENTLINE 2 +#define SCE_V_COMMENTLINEBANG 3 +#define SCE_V_NUMBER 4 +#define SCE_V_WORD 5 +#define SCE_V_STRING 6 +#define SCE_V_WORD2 7 +#define SCE_V_WORD3 8 +#define SCE_V_PREPROCESSOR 9 +#define SCE_V_OPERATOR 10 +#define SCE_V_IDENTIFIER 11 +#define SCE_V_STRINGEOL 12 +#define SCE_V_USER 19 +#define SCE_KIX_DEFAULT 0 +#define SCE_KIX_COMMENT 1 +#define SCE_KIX_STRING1 2 +#define SCE_KIX_STRING2 3 +#define SCE_KIX_NUMBER 4 +#define SCE_KIX_VAR 5 +#define SCE_KIX_MACRO 6 +#define SCE_KIX_KEYWORD 7 +#define SCE_KIX_FUNCTIONS 8 +#define SCE_KIX_OPERATOR 9 +#define SCE_KIX_IDENTIFIER 31 +#define SCE_GC_DEFAULT 0 +#define SCE_GC_COMMENTLINE 1 +#define SCE_GC_COMMENTBLOCK 2 +#define SCE_GC_GLOBAL 3 +#define SCE_GC_EVENT 4 +#define SCE_GC_ATTRIBUTE 5 +#define SCE_GC_CONTROL 6 +#define SCE_GC_COMMAND 7 +#define SCE_GC_STRING 8 +#define SCE_GC_OPERATOR 9 +#define SCE_SN_DEFAULT 0 +#define SCE_SN_CODE 1 +#define SCE_SN_COMMENTLINE 2 +#define SCE_SN_COMMENTLINEBANG 3 +#define SCE_SN_NUMBER 4 +#define SCE_SN_WORD 5 +#define SCE_SN_STRING 6 +#define SCE_SN_WORD2 7 +#define SCE_SN_WORD3 8 +#define SCE_SN_PREPROCESSOR 9 +#define SCE_SN_OPERATOR 10 +#define SCE_SN_IDENTIFIER 11 +#define SCE_SN_STRINGEOL 12 +#define SCE_SN_REGEXTAG 13 +#define SCE_SN_SIGNAL 14 +#define SCE_SN_USER 19 +#define SCE_AU3_DEFAULT 0 +#define SCE_AU3_COMMENT 1 +#define SCE_AU3_COMMENTBLOCK 2 +#define SCE_AU3_NUMBER 3 +#define SCE_AU3_FUNCTION 4 +#define SCE_AU3_KEYWORD 5 +#define SCE_AU3_MACRO 6 +#define SCE_AU3_STRING 7 +#define SCE_AU3_OPERATOR 8 +#define SCE_AU3_VARIABLE 9 +#define SCE_AU3_SENT 10 +#define SCE_AU3_PREPROCESSOR 11 +#define SCE_AU3_SPECIAL 12 +#define SCE_AU3_EXPAND 13 +#define SCE_AU3_COMOBJ 14 +#define SCE_AU3_UDF 15 +#define SCE_APDL_DEFAULT 0 +#define SCE_APDL_COMMENT 1 +#define SCE_APDL_COMMENTBLOCK 2 +#define SCE_APDL_NUMBER 3 +#define SCE_APDL_STRING 4 +#define SCE_APDL_OPERATOR 5 +#define SCE_APDL_WORD 6 +#define SCE_APDL_PROCESSOR 7 +#define SCE_APDL_COMMAND 8 +#define SCE_APDL_SLASHCOMMAND 9 +#define SCE_APDL_STARCOMMAND 10 +#define SCE_APDL_ARGUMENT 11 +#define SCE_APDL_FUNCTION 12 +#define SCE_SH_DEFAULT 0 +#define SCE_SH_ERROR 1 +#define SCE_SH_COMMENTLINE 2 +#define SCE_SH_NUMBER 3 +#define SCE_SH_WORD 4 +#define SCE_SH_STRING 5 +#define SCE_SH_CHARACTER 6 +#define SCE_SH_OPERATOR 7 +#define SCE_SH_IDENTIFIER 8 +#define SCE_SH_SCALAR 9 +#define SCE_SH_PARAM 10 +#define SCE_SH_BACKTICKS 11 +#define SCE_SH_HERE_DELIM 12 +#define SCE_SH_HERE_Q 13 +#define SCE_ASN1_DEFAULT 0 +#define SCE_ASN1_COMMENT 1 +#define SCE_ASN1_IDENTIFIER 2 +#define SCE_ASN1_STRING 3 +#define SCE_ASN1_OID 4 +#define SCE_ASN1_SCALAR 5 +#define SCE_ASN1_KEYWORD 6 +#define SCE_ASN1_ATTRIBUTE 7 +#define SCE_ASN1_DESCRIPTOR 8 +#define SCE_ASN1_TYPE 9 +#define SCE_ASN1_OPERATOR 10 +#define SCE_VHDL_DEFAULT 0 +#define SCE_VHDL_COMMENT 1 +#define SCE_VHDL_COMMENTLINEBANG 2 +#define SCE_VHDL_NUMBER 3 +#define SCE_VHDL_STRING 4 +#define SCE_VHDL_OPERATOR 5 +#define SCE_VHDL_IDENTIFIER 6 +#define SCE_VHDL_STRINGEOL 7 +#define SCE_VHDL_KEYWORD 8 +#define SCE_VHDL_STDOPERATOR 9 +#define SCE_VHDL_ATTRIBUTE 10 +#define SCE_VHDL_STDFUNCTION 11 +#define SCE_VHDL_STDPACKAGE 12 +#define SCE_VHDL_STDTYPE 13 +#define SCE_VHDL_USERWORD 14 +#define SCE_CAML_DEFAULT 0 +#define SCE_CAML_IDENTIFIER 1 +#define SCE_CAML_TAGNAME 2 +#define SCE_CAML_KEYWORD 3 +#define SCE_CAML_KEYWORD2 4 +#define SCE_CAML_KEYWORD3 5 +#define SCE_CAML_LINENUM 6 +#define SCE_CAML_OPERATOR 7 +#define SCE_CAML_NUMBER 8 +#define SCE_CAML_CHAR 9 +#define SCE_CAML_STRING 11 +#define SCE_CAML_COMMENT 12 +#define SCE_CAML_COMMENT1 13 +#define SCE_CAML_COMMENT2 14 +#define SCE_CAML_COMMENT3 15 +#define SCE_HA_DEFAULT 0 +#define SCE_HA_IDENTIFIER 1 +#define SCE_HA_KEYWORD 2 +#define SCE_HA_NUMBER 3 +#define SCE_HA_STRING 4 +#define SCE_HA_CHARACTER 5 +#define SCE_HA_CLASS 6 +#define SCE_HA_MODULE 7 +#define SCE_HA_CAPITAL 8 +#define SCE_HA_DATA 9 +#define SCE_HA_IMPORT 10 +#define SCE_HA_OPERATOR 11 +#define SCE_HA_INSTANCE 12 +#define SCE_HA_COMMENTLINE 13 +#define SCE_HA_COMMENTBLOCK 14 +#define SCE_HA_COMMENTBLOCK2 15 +#define SCE_HA_COMMENTBLOCK3 16 +#define SCE_T3_DEFAULT 0 +#define SCE_T3_X_DEFAULT 1 +#define SCE_T3_PREPROCESSOR 2 +#define SCE_T3_BLOCK_COMMENT 3 +#define SCE_T3_LINE_COMMENT 4 +#define SCE_T3_OPERATOR 5 +#define SCE_T3_KEYWORD 6 +#define SCE_T3_NUMBER 7 +#define SCE_T3_IDENTIFIER 8 +#define SCE_T3_S_STRING 9 +#define SCE_T3_D_STRING 10 +#define SCE_T3_X_STRING 11 +#define SCE_T3_LIB_DIRECTIVE 12 +#define SCE_T3_MSG_PARAM 13 +#define SCE_T3_HTML_TAG 14 +#define SCE_T3_HTML_DEFAULT 15 +#define SCE_T3_HTML_STRING 16 +#define SCE_T3_USER1 17 +#define SCE_T3_USER2 18 +#define SCE_T3_USER3 19 +#define SCE_REBOL_DEFAULT 0 +#define SCE_REBOL_COMMENTLINE 1 +#define SCE_REBOL_COMMENTBLOCK 2 +#define SCE_REBOL_PREFACE 3 +#define SCE_REBOL_OPERATOR 4 +#define SCE_REBOL_CHARACTER 5 +#define SCE_REBOL_QUOTEDSTRING 6 +#define SCE_REBOL_BRACEDSTRING 7 +#define SCE_REBOL_NUMBER 8 +#define SCE_REBOL_PAIR 9 +#define SCE_REBOL_TUPLE 10 +#define SCE_REBOL_BINARY 11 +#define SCE_REBOL_MONEY 12 +#define SCE_REBOL_ISSUE 13 +#define SCE_REBOL_TAG 14 +#define SCE_REBOL_FILE 15 +#define SCE_REBOL_EMAIL 16 +#define SCE_REBOL_URL 17 +#define SCE_REBOL_DATE 18 +#define SCE_REBOL_TIME 19 +#define SCE_REBOL_IDENTIFIER 20 +#define SCE_REBOL_WORD 21 +#define SCE_REBOL_WORD2 22 +#define SCE_REBOL_WORD3 23 +#define SCE_REBOL_WORD4 24 +#define SCE_REBOL_WORD5 25 +#define SCE_REBOL_WORD6 26 +#define SCE_REBOL_WORD7 27 +#define SCE_REBOL_WORD8 28 +#define SCE_SQL_DEFAULT 0 +#define SCE_SQL_COMMENT 1 +#define SCE_SQL_COMMENTLINE 2 +#define SCE_SQL_COMMENTDOC 3 +#define SCE_SQL_NUMBER 4 +#define SCE_SQL_WORD 5 +#define SCE_SQL_STRING 6 +#define SCE_SQL_CHARACTER 7 +#define SCE_SQL_SQLPLUS 8 +#define SCE_SQL_SQLPLUS_PROMPT 9 +#define SCE_SQL_OPERATOR 10 +#define SCE_SQL_IDENTIFIER 11 +#define SCE_SQL_SQLPLUS_COMMENT 13 +#define SCE_SQL_COMMENTLINEDOC 15 +#define SCE_SQL_WORD2 16 +#define SCE_SQL_COMMENTDOCKEYWORD 17 +#define SCE_SQL_COMMENTDOCKEYWORDERROR 18 +#define SCE_SQL_USER1 19 +#define SCE_SQL_USER2 20 +#define SCE_SQL_USER3 21 +#define SCE_SQL_USER4 22 +#define SCE_SQL_QUOTEDIDENTIFIER 23 +#define SCE_ST_DEFAULT 0 +#define SCE_ST_STRING 1 +#define SCE_ST_NUMBER 2 +#define SCE_ST_COMMENT 3 +#define SCE_ST_SYMBOL 4 +#define SCE_ST_BINARY 5 +#define SCE_ST_BOOL 6 +#define SCE_ST_SELF 7 +#define SCE_ST_SUPER 8 +#define SCE_ST_NIL 9 +#define SCE_ST_GLOBAL 10 +#define SCE_ST_RETURN 11 +#define SCE_ST_SPECIAL 12 +#define SCE_ST_KWSEND 13 +#define SCE_ST_ASSIGN 14 +#define SCE_ST_CHARACTER 15 +#define SCE_ST_SPEC_SEL 16 +#define SCE_FS_DEFAULT 0 +#define SCE_FS_COMMENT 1 +#define SCE_FS_COMMENTLINE 2 +#define SCE_FS_COMMENTDOC 3 +#define SCE_FS_COMMENTLINEDOC 4 +#define SCE_FS_COMMENTDOCKEYWORD 5 +#define SCE_FS_COMMENTDOCKEYWORDERROR 6 +#define SCE_FS_KEYWORD 7 +#define SCE_FS_KEYWORD2 8 +#define SCE_FS_KEYWORD3 9 +#define SCE_FS_KEYWORD4 10 +#define SCE_FS_NUMBER 11 +#define SCE_FS_STRING 12 +#define SCE_FS_PREPROCESSOR 13 +#define SCE_FS_OPERATOR 14 +#define SCE_FS_IDENTIFIER 15 +#define SCE_FS_DATE 16 +#define SCE_FS_STRINGEOL 17 +#define SCE_FS_CONSTANT 18 +#define SCE_FS_ASM 19 +#define SCE_FS_LABEL 20 +#define SCE_FS_ERROR 21 +#define SCE_FS_HEXNUMBER 22 +#define SCE_FS_BINNUMBER 23 +#define SCE_CSOUND_DEFAULT 0 +#define SCE_CSOUND_COMMENT 1 +#define SCE_CSOUND_NUMBER 2 +#define SCE_CSOUND_OPERATOR 3 +#define SCE_CSOUND_INSTR 4 +#define SCE_CSOUND_IDENTIFIER 5 +#define SCE_CSOUND_OPCODE 6 +#define SCE_CSOUND_HEADERSTMT 7 +#define SCE_CSOUND_USERKEYWORD 8 +#define SCE_CSOUND_COMMENTBLOCK 9 +#define SCE_CSOUND_PARAM 10 +#define SCE_CSOUND_ARATE_VAR 11 +#define SCE_CSOUND_KRATE_VAR 12 +#define SCE_CSOUND_IRATE_VAR 13 +#define SCE_CSOUND_GLOBAL_VAR 14 +#define SCE_CSOUND_STRINGEOL 15 +#define SCE_INNO_DEFAULT 0 +#define SCE_INNO_COMMENT 1 +#define SCE_INNO_KEYWORD 2 +#define SCE_INNO_PARAMETER 3 +#define SCE_INNO_SECTION 4 +#define SCE_INNO_PREPROC 5 +#define SCE_INNO_PREPROC_INLINE 6 +#define SCE_INNO_COMMENT_PASCAL 7 +#define SCE_INNO_KEYWORD_PASCAL 8 +#define SCE_INNO_KEYWORD_USER 9 +#define SCE_INNO_STRING_DOUBLE 10 +#define SCE_INNO_STRING_SINGLE 11 +#define SCE_INNO_IDENTIFIER 12 +#define SCE_OPAL_SPACE 0 +#define SCE_OPAL_COMMENT_BLOCK 1 +#define SCE_OPAL_COMMENT_LINE 2 +#define SCE_OPAL_INTEGER 3 +#define SCE_OPAL_KEYWORD 4 +#define SCE_OPAL_SORT 5 +#define SCE_OPAL_STRING 6 +#define SCE_OPAL_PAR 7 +#define SCE_OPAL_BOOL_CONST 8 +#define SCE_OPAL_DEFAULT 32 +#define SCE_SPICE_DEFAULT 0 +#define SCE_SPICE_IDENTIFIER 1 +#define SCE_SPICE_KEYWORD 2 +#define SCE_SPICE_KEYWORD2 3 +#define SCE_SPICE_KEYWORD3 4 +#define SCE_SPICE_NUMBER 5 +#define SCE_SPICE_DELIMITER 6 +#define SCE_SPICE_VALUE 7 +#define SCE_SPICE_COMMENTLINE 8 +#define SCLEX_ASP 29 +#define SCLEX_PHP 30 +//--Autogenerated -- end of section automatically generated from Scintilla.iface + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Scintilla.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Scintilla.h new file mode 100644 index 00000000..792f037e --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Scintilla.h @@ -0,0 +1,780 @@ +// Scintilla source code edit control +/** @file Scintilla.h + ** Interface to the edit control. + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +// Most of this file is automatically generated from the Scintilla.iface interface definition +// file which contains any comments about the definitions. HFacer.py does the generation. + +#ifndef SCINTILLA_H +#define SCINTILLA_H + +#if LCCWIN +typedef BOOL bool; +#endif + +#if PLAT_WIN +// Return false on failure: +bool Scintilla_RegisterClasses(void *hInstance); +bool Scintilla_ReleaseResources(); +#endif +int Scintilla_LinkLexers(); + +// Here should be placed typedefs for uptr_t, an unsigned integer type large enough to +// hold a pointer and sptr_t, a signed integer large enough to hold a pointer. +// May need to be changed for 64 bit platforms. +#if _MSC_VER >= 1300 +#include +#endif +#ifdef MAXULONG_PTR +typedef ULONG_PTR uptr_t; +typedef LONG_PTR sptr_t; +#else +typedef unsigned long uptr_t; +typedef long sptr_t; +#endif + +typedef sptr_t (*SciFnDirect)(sptr_t ptr, unsigned int iMessage, uptr_t wParam, sptr_t lParam); + +//++Autogenerated -- start of section automatically generated from Scintilla.iface +#define INVALID_POSITION -1 +#define SCI_START 2000 +#define SCI_OPTIONAL_START 3000 +#define SCI_LEXER_START 4000 +#define SCI_ADDTEXT 2001 +#define SCI_ADDSTYLEDTEXT 2002 +#define SCI_INSERTTEXT 2003 +#define SCI_CLEARALL 2004 +#define SCI_CLEARDOCUMENTSTYLE 2005 +#define SCI_GETLENGTH 2006 +#define SCI_GETCHARAT 2007 +#define SCI_GETCURRENTPOS 2008 +#define SCI_GETANCHOR 2009 +#define SCI_GETSTYLEAT 2010 +#define SCI_REDO 2011 +#define SCI_SETUNDOCOLLECTION 2012 +#define SCI_SELECTALL 2013 +#define SCI_SETSAVEPOINT 2014 +#define SCI_GETSTYLEDTEXT 2015 +#define SCI_CANREDO 2016 +#define SCI_MARKERLINEFROMHANDLE 2017 +#define SCI_MARKERDELETEHANDLE 2018 +#define SCI_GETUNDOCOLLECTION 2019 +#define SCWS_INVISIBLE 0 +#define SCWS_VISIBLEALWAYS 1 +#define SCWS_VISIBLEAFTERINDENT 2 +#define SCI_GETVIEWWS 2020 +#define SCI_SETVIEWWS 2021 +#define SCI_POSITIONFROMPOINT 2022 +#define SCI_POSITIONFROMPOINTCLOSE 2023 +#define SCI_GOTOLINE 2024 +#define SCI_GOTOPOS 2025 +#define SCI_SETANCHOR 2026 +#define SCI_GETCURLINE 2027 +#define SCI_GETENDSTYLED 2028 +#define SC_EOL_CRLF 0 +#define SC_EOL_CR 1 +#define SC_EOL_LF 2 +#define SCI_CONVERTEOLS 2029 +#define SCI_GETEOLMODE 2030 +#define SCI_SETEOLMODE 2031 +#define SCI_STARTSTYLING 2032 +#define SCI_SETSTYLING 2033 +#define SCI_GETBUFFEREDDRAW 2034 +#define SCI_SETBUFFEREDDRAW 2035 +#define SCI_SETTABWIDTH 2036 +#define SCI_GETTABWIDTH 2121 +#define SC_CP_UTF8 65001 +#define SC_CP_DBCS 1 +#define SCI_SETCODEPAGE 2037 +#define SCI_SETUSEPALETTE 2039 +#define MARKER_MAX 31 +#define SC_MARK_CIRCLE 0 +#define SC_MARK_ROUNDRECT 1 +#define SC_MARK_ARROW 2 +#define SC_MARK_SMALLRECT 3 +#define SC_MARK_SHORTARROW 4 +#define SC_MARK_EMPTY 5 +#define SC_MARK_ARROWDOWN 6 +#define SC_MARK_MINUS 7 +#define SC_MARK_PLUS 8 +#define SC_MARK_VLINE 9 +#define SC_MARK_LCORNER 10 +#define SC_MARK_TCORNER 11 +#define SC_MARK_BOXPLUS 12 +#define SC_MARK_BOXPLUSCONNECTED 13 +#define SC_MARK_BOXMINUS 14 +#define SC_MARK_BOXMINUSCONNECTED 15 +#define SC_MARK_LCORNERCURVE 16 +#define SC_MARK_TCORNERCURVE 17 +#define SC_MARK_CIRCLEPLUS 18 +#define SC_MARK_CIRCLEPLUSCONNECTED 19 +#define SC_MARK_CIRCLEMINUS 20 +#define SC_MARK_CIRCLEMINUSCONNECTED 21 +#define SC_MARK_BACKGROUND 22 +#define SC_MARK_DOTDOTDOT 23 +#define SC_MARK_ARROWS 24 +#define SC_MARK_PIXMAP 25 +#define SC_MARK_FULLRECT 26 +#define SC_MARK_CHARACTER 10000 +#define SC_MARKNUM_FOLDEREND 25 +#define SC_MARKNUM_FOLDEROPENMID 26 +#define SC_MARKNUM_FOLDERMIDTAIL 27 +#define SC_MARKNUM_FOLDERTAIL 28 +#define SC_MARKNUM_FOLDERSUB 29 +#define SC_MARKNUM_FOLDER 30 +#define SC_MARKNUM_FOLDEROPEN 31 +#define SC_MASK_FOLDERS 0xFE000000 +#define SCI_MARKERDEFINE 2040 +#define SCI_MARKERSETFORE 2041 +#define SCI_MARKERSETBACK 2042 +#define SCI_MARKERADD 2043 +#define SCI_MARKERDELETE 2044 +#define SCI_MARKERDELETEALL 2045 +#define SCI_MARKERGET 2046 +#define SCI_MARKERNEXT 2047 +#define SCI_MARKERPREVIOUS 2048 +#define SCI_MARKERDEFINEPIXMAP 2049 +#define SCI_MARKERADDSET 2466 +#define SCI_MARKERSETALPHA 2476 +#define SC_MARGIN_SYMBOL 0 +#define SC_MARGIN_NUMBER 1 +#define SC_MARGIN_BACK 2 +#define SC_MARGIN_FORE 3 +#define SCI_SETMARGINTYPEN 2240 +#define SCI_GETMARGINTYPEN 2241 +#define SCI_SETMARGINWIDTHN 2242 +#define SCI_GETMARGINWIDTHN 2243 +#define SCI_SETMARGINMASKN 2244 +#define SCI_GETMARGINMASKN 2245 +#define SCI_SETMARGINSENSITIVEN 2246 +#define SCI_GETMARGINSENSITIVEN 2247 +#define STYLE_DEFAULT 32 +#define STYLE_LINENUMBER 33 +#define STYLE_BRACELIGHT 34 +#define STYLE_BRACEBAD 35 +#define STYLE_CONTROLCHAR 36 +#define STYLE_INDENTGUIDE 37 +#define STYLE_CALLTIP 38 +#define STYLE_LASTPREDEFINED 39 +#define STYLE_MAX 127 +#define SC_CHARSET_ANSI 0 +#define SC_CHARSET_DEFAULT 1 +#define SC_CHARSET_BALTIC 186 +#define SC_CHARSET_CHINESEBIG5 136 +#define SC_CHARSET_EASTEUROPE 238 +#define SC_CHARSET_GB2312 134 +#define SC_CHARSET_GREEK 161 +#define SC_CHARSET_HANGUL 129 +#define SC_CHARSET_MAC 77 +#define SC_CHARSET_OEM 255 +#define SC_CHARSET_RUSSIAN 204 +#define SC_CHARSET_CYRILLIC 1251 +#define SC_CHARSET_SHIFTJIS 128 +#define SC_CHARSET_SYMBOL 2 +#define SC_CHARSET_TURKISH 162 +#define SC_CHARSET_JOHAB 130 +#define SC_CHARSET_HEBREW 177 +#define SC_CHARSET_ARABIC 178 +#define SC_CHARSET_VIETNAMESE 163 +#define SC_CHARSET_THAI 222 +#define SC_CHARSET_8859_15 1000 +#define SCI_STYLECLEARALL 2050 +#define SCI_STYLESETFORE 2051 +#define SCI_STYLESETBACK 2052 +#define SCI_STYLESETBOLD 2053 +#define SCI_STYLESETITALIC 2054 +#define SCI_STYLESETSIZE 2055 +#define SCI_STYLESETFONT 2056 +#define SCI_STYLESETEOLFILLED 2057 +#define SCI_STYLERESETDEFAULT 2058 +#define SCI_STYLESETUNDERLINE 2059 +#define SC_CASE_MIXED 0 +#define SC_CASE_UPPER 1 +#define SC_CASE_LOWER 2 +#define SCI_STYLESETCASE 2060 +#define SCI_STYLESETCHARACTERSET 2066 +#define SCI_STYLESETHOTSPOT 2409 +#define SCI_SETSELFORE 2067 +#define SCI_SETSELBACK 2068 +#define SCI_GETSELALPHA 2477 +#define SCI_SETSELALPHA 2478 +#define SCI_SETCARETFORE 2069 +#define SCI_ASSIGNCMDKEY 2070 +#define SCI_CLEARCMDKEY 2071 +#define SCI_CLEARALLCMDKEYS 2072 +#define SCI_SETSTYLINGEX 2073 +#define SCI_STYLESETVISIBLE 2074 +#define SCI_GETCARETPERIOD 2075 +#define SCI_SETCARETPERIOD 2076 +#define SCI_SETWORDCHARS 2077 +#define SCI_BEGINUNDOACTION 2078 +#define SCI_ENDUNDOACTION 2079 +#define INDIC_MAX 7 +#define INDIC_PLAIN 0 +#define INDIC_SQUIGGLE 1 +#define INDIC_TT 2 +#define INDIC_DIAGONAL 3 +#define INDIC_STRIKE 4 +#define INDIC_HIDDEN 5 +#define INDIC_BOX 6 +#define INDIC_ROUNDBOX 7 +#define INDIC0_MASK 0x20 +#define INDIC1_MASK 0x40 +#define INDIC2_MASK 0x80 +#define INDICS_MASK 0xE0 +#define SCI_INDICSETSTYLE 2080 +#define SCI_INDICGETSTYLE 2081 +#define SCI_INDICSETFORE 2082 +#define SCI_INDICGETFORE 2083 +#define SCI_SETWHITESPACEFORE 2084 +#define SCI_SETWHITESPACEBACK 2085 +#define SCI_SETSTYLEBITS 2090 +#define SCI_GETSTYLEBITS 2091 +#define SCI_SETLINESTATE 2092 +#define SCI_GETLINESTATE 2093 +#define SCI_GETMAXLINESTATE 2094 +#define SCI_GETCARETLINEVISIBLE 2095 +#define SCI_SETCARETLINEVISIBLE 2096 +#define SCI_GETCARETLINEBACK 2097 +#define SCI_SETCARETLINEBACK 2098 +#define SCI_STYLESETCHANGEABLE 2099 +#define SCI_AUTOCSHOW 2100 +#define SCI_AUTOCCANCEL 2101 +#define SCI_AUTOCACTIVE 2102 +#define SCI_AUTOCPOSSTART 2103 +#define SCI_AUTOCCOMPLETE 2104 +#define SCI_AUTOCSTOPS 2105 +#define SCI_AUTOCSETSEPARATOR 2106 +#define SCI_AUTOCGETSEPARATOR 2107 +#define SCI_AUTOCSELECT 2108 +#define SCI_AUTOCSETCANCELATSTART 2110 +#define SCI_AUTOCGETCANCELATSTART 2111 +#define SCI_AUTOCSETFILLUPS 2112 +#define SCI_AUTOCSETCHOOSESINGLE 2113 +#define SCI_AUTOCGETCHOOSESINGLE 2114 +#define SCI_AUTOCSETIGNORECASE 2115 +#define SCI_AUTOCGETIGNORECASE 2116 +#define SCI_USERLISTSHOW 2117 +#define SCI_AUTOCSETAUTOHIDE 2118 +#define SCI_AUTOCGETAUTOHIDE 2119 +#define SCI_AUTOCSETDROPRESTOFWORD 2270 +#define SCI_AUTOCGETDROPRESTOFWORD 2271 +#define SCI_REGISTERIMAGE 2405 +#define SCI_CLEARREGISTEREDIMAGES 2408 +#define SCI_AUTOCGETTYPESEPARATOR 2285 +#define SCI_AUTOCSETTYPESEPARATOR 2286 +#define SCI_AUTOCSETMAXWIDTH 2208 +#define SCI_AUTOCGETMAXWIDTH 2209 +#define SCI_AUTOCSETMAXHEIGHT 2210 +#define SCI_AUTOCGETMAXHEIGHT 2211 +#define SCI_SETINDENT 2122 +#define SCI_GETINDENT 2123 +#define SCI_SETUSETABS 2124 +#define SCI_GETUSETABS 2125 +#define SCI_SETLINEINDENTATION 2126 +#define SCI_GETLINEINDENTATION 2127 +#define SCI_GETLINEINDENTPOSITION 2128 +#define SCI_GETCOLUMN 2129 +#define SCI_SETHSCROLLBAR 2130 +#define SCI_GETHSCROLLBAR 2131 +#define SCI_SETINDENTATIONGUIDES 2132 +#define SCI_GETINDENTATIONGUIDES 2133 +#define SCI_SETHIGHLIGHTGUIDE 2134 +#define SCI_GETHIGHLIGHTGUIDE 2135 +#define SCI_GETLINEENDPOSITION 2136 +#define SCI_GETCODEPAGE 2137 +#define SCI_GETCARETFORE 2138 +#define SCI_GETUSEPALETTE 2139 +#define SCI_GETREADONLY 2140 +#define SCI_SETCURRENTPOS 2141 +#define SCI_SETSELECTIONSTART 2142 +#define SCI_GETSELECTIONSTART 2143 +#define SCI_SETSELECTIONEND 2144 +#define SCI_GETSELECTIONEND 2145 +#define SCI_SETPRINTMAGNIFICATION 2146 +#define SCI_GETPRINTMAGNIFICATION 2147 +#define SC_PRINT_NORMAL 0 +#define SC_PRINT_INVERTLIGHT 1 +#define SC_PRINT_BLACKONWHITE 2 +#define SC_PRINT_COLOURONWHITE 3 +#define SC_PRINT_COLOURONWHITEDEFAULTBG 4 +#define SCI_SETPRINTCOLOURMODE 2148 +#define SCI_GETPRINTCOLOURMODE 2149 +#define SCFIND_WHOLEWORD 2 +#define SCFIND_MATCHCASE 4 +#define SCFIND_WORDSTART 0x00100000 +#define SCFIND_REGEXP 0x00200000 +#define SCFIND_POSIX 0x00400000 +#define SCI_FINDTEXT 2150 +#define SCI_FORMATRANGE 2151 +#define SCI_GETFIRSTVISIBLELINE 2152 +#define SCI_GETLINE 2153 +#define SCI_GETLINECOUNT 2154 +#define SCI_SETMARGINLEFT 2155 +#define SCI_GETMARGINLEFT 2156 +#define SCI_SETMARGINRIGHT 2157 +#define SCI_GETMARGINRIGHT 2158 +#define SCI_GETMODIFY 2159 +#define SCI_SETSEL 2160 +#define SCI_GETSELTEXT 2161 +#define SCI_GETTEXTRANGE 2162 +#define SCI_HIDESELECTION 2163 +#define SCI_POINTXFROMPOSITION 2164 +#define SCI_POINTYFROMPOSITION 2165 +#define SCI_LINEFROMPOSITION 2166 +#define SCI_POSITIONFROMLINE 2167 +#define SCI_LINESCROLL 2168 +#define SCI_SCROLLCARET 2169 +#define SCI_REPLACESEL 2170 +#define SCI_SETREADONLY 2171 +#define SCI_NULL 2172 +#define SCI_CANPASTE 2173 +#define SCI_CANUNDO 2174 +#define SCI_EMPTYUNDOBUFFER 2175 +#define SCI_UNDO 2176 +#define SCI_CUT 2177 +#define SCI_COPY 2178 +#define SCI_PASTE 2179 +#define SCI_CLEAR 2180 +#define SCI_SETTEXT 2181 +#define SCI_GETTEXT 2182 +#define SCI_GETTEXTLENGTH 2183 +#define SCI_GETDIRECTFUNCTION 2184 +#define SCI_GETDIRECTPOINTER 2185 +#define SCI_SETOVERTYPE 2186 +#define SCI_GETOVERTYPE 2187 +#define SCI_SETCARETWIDTH 2188 +#define SCI_GETCARETWIDTH 2189 +#define SCI_SETTARGETSTART 2190 +#define SCI_GETTARGETSTART 2191 +#define SCI_SETTARGETEND 2192 +#define SCI_GETTARGETEND 2193 +#define SCI_REPLACETARGET 2194 +#define SCI_REPLACETARGETRE 2195 +#define SCI_SEARCHINTARGET 2197 +#define SCI_SETSEARCHFLAGS 2198 +#define SCI_GETSEARCHFLAGS 2199 +#define SCI_CALLTIPSHOW 2200 +#define SCI_CALLTIPCANCEL 2201 +#define SCI_CALLTIPACTIVE 2202 +#define SCI_CALLTIPPOSSTART 2203 +#define SCI_CALLTIPSETHLT 2204 +#define SCI_CALLTIPSETBACK 2205 +#define SCI_CALLTIPSETFORE 2206 +#define SCI_CALLTIPSETFOREHLT 2207 +#define SCI_CALLTIPUSESTYLE 2212 +#define SCI_VISIBLEFROMDOCLINE 2220 +#define SCI_DOCLINEFROMVISIBLE 2221 +#define SCI_WRAPCOUNT 2235 +#define SC_FOLDLEVELBASE 0x400 +#define SC_FOLDLEVELWHITEFLAG 0x1000 +#define SC_FOLDLEVELHEADERFLAG 0x2000 +#define SC_FOLDLEVELBOXHEADERFLAG 0x4000 +#define SC_FOLDLEVELBOXFOOTERFLAG 0x8000 +#define SC_FOLDLEVELCONTRACTED 0x10000 +#define SC_FOLDLEVELUNINDENT 0x20000 +#define SC_FOLDLEVELNUMBERMASK 0x0FFF +#define SCI_SETFOLDLEVEL 2222 +#define SCI_GETFOLDLEVEL 2223 +#define SCI_GETLASTCHILD 2224 +#define SCI_GETFOLDPARENT 2225 +#define SCI_SHOWLINES 2226 +#define SCI_HIDELINES 2227 +#define SCI_GETLINEVISIBLE 2228 +#define SCI_SETFOLDEXPANDED 2229 +#define SCI_GETFOLDEXPANDED 2230 +#define SCI_TOGGLEFOLD 2231 +#define SCI_ENSUREVISIBLE 2232 +#define SC_FOLDFLAG_LINEBEFORE_EXPANDED 0x0002 +#define SC_FOLDFLAG_LINEBEFORE_CONTRACTED 0x0004 +#define SC_FOLDFLAG_LINEAFTER_EXPANDED 0x0008 +#define SC_FOLDFLAG_LINEAFTER_CONTRACTED 0x0010 +#define SC_FOLDFLAG_LEVELNUMBERS 0x0040 +#define SC_FOLDFLAG_BOX 0x0001 +#define SCI_SETFOLDFLAGS 2233 +#define SCI_ENSUREVISIBLEENFORCEPOLICY 2234 +#define SCI_SETTABINDENTS 2260 +#define SCI_GETTABINDENTS 2261 +#define SCI_SETBACKSPACEUNINDENTS 2262 +#define SCI_GETBACKSPACEUNINDENTS 2263 +#define SC_TIME_FOREVER 10000000 +#define SCI_SETMOUSEDWELLTIME 2264 +#define SCI_GETMOUSEDWELLTIME 2265 +#define SCI_WORDSTARTPOSITION 2266 +#define SCI_WORDENDPOSITION 2267 +#define SC_WRAP_NONE 0 +#define SC_WRAP_WORD 1 +#define SC_WRAP_CHAR 2 +#define SCI_SETWRAPMODE 2268 +#define SCI_GETWRAPMODE 2269 +#define SC_WRAPVISUALFLAG_NONE 0x0000 +#define SC_WRAPVISUALFLAG_END 0x0001 +#define SC_WRAPVISUALFLAG_START 0x0002 +#define SCI_SETWRAPVISUALFLAGS 2460 +#define SCI_GETWRAPVISUALFLAGS 2461 +#define SC_WRAPVISUALFLAGLOC_DEFAULT 0x0000 +#define SC_WRAPVISUALFLAGLOC_END_BY_TEXT 0x0001 +#define SC_WRAPVISUALFLAGLOC_START_BY_TEXT 0x0002 +#define SCI_SETWRAPVISUALFLAGSLOCATION 2462 +#define SCI_GETWRAPVISUALFLAGSLOCATION 2463 +#define SCI_SETWRAPSTARTINDENT 2464 +#define SCI_GETWRAPSTARTINDENT 2465 +#define SC_CACHE_NONE 0 +#define SC_CACHE_CARET 1 +#define SC_CACHE_PAGE 2 +#define SC_CACHE_DOCUMENT 3 +#define SCI_SETLAYOUTCACHE 2272 +#define SCI_GETLAYOUTCACHE 2273 +#define SCI_SETSCROLLWIDTH 2274 +#define SCI_GETSCROLLWIDTH 2275 +#define SCI_TEXTWIDTH 2276 +#define SCI_SETENDATLASTLINE 2277 +#define SCI_GETENDATLASTLINE 2278 +#define SCI_TEXTHEIGHT 2279 +#define SCI_SETVSCROLLBAR 2280 +#define SCI_GETVSCROLLBAR 2281 +#define SCI_APPENDTEXT 2282 +#define SCI_GETTWOPHASEDRAW 2283 +#define SCI_SETTWOPHASEDRAW 2284 +#define SCI_TARGETFROMSELECTION 2287 +#define SCI_LINESJOIN 2288 +#define SCI_LINESSPLIT 2289 +#define SCI_SETFOLDMARGINCOLOUR 2290 +#define SCI_SETFOLDMARGINHICOLOUR 2291 +#define SCI_LINEDOWN 2300 +#define SCI_LINEDOWNEXTEND 2301 +#define SCI_LINEUP 2302 +#define SCI_LINEUPEXTEND 2303 +#define SCI_CHARLEFT 2304 +#define SCI_CHARLEFTEXTEND 2305 +#define SCI_CHARRIGHT 2306 +#define SCI_CHARRIGHTEXTEND 2307 +#define SCI_WORDLEFT 2308 +#define SCI_WORDLEFTEXTEND 2309 +#define SCI_WORDRIGHT 2310 +#define SCI_WORDRIGHTEXTEND 2311 +#define SCI_HOME 2312 +#define SCI_HOMEEXTEND 2313 +#define SCI_LINEEND 2314 +#define SCI_LINEENDEXTEND 2315 +#define SCI_DOCUMENTSTART 2316 +#define SCI_DOCUMENTSTARTEXTEND 2317 +#define SCI_DOCUMENTEND 2318 +#define SCI_DOCUMENTENDEXTEND 2319 +#define SCI_PAGEUP 2320 +#define SCI_PAGEUPEXTEND 2321 +#define SCI_PAGEDOWN 2322 +#define SCI_PAGEDOWNEXTEND 2323 +#define SCI_EDITTOGGLEOVERTYPE 2324 +#define SCI_CANCEL 2325 +#define SCI_DELETEBACK 2326 +#define SCI_TAB 2327 +#define SCI_BACKTAB 2328 +#define SCI_NEWLINE 2329 +#define SCI_FORMFEED 2330 +#define SCI_VCHOME 2331 +#define SCI_VCHOMEEXTEND 2332 +#define SCI_ZOOMIN 2333 +#define SCI_ZOOMOUT 2334 +#define SCI_DELWORDLEFT 2335 +#define SCI_DELWORDRIGHT 2336 +#define SCI_LINECUT 2337 +#define SCI_LINEDELETE 2338 +#define SCI_LINETRANSPOSE 2339 +#define SCI_LINEDUPLICATE 2404 +#define SCI_LOWERCASE 2340 +#define SCI_UPPERCASE 2341 +#define SCI_LINESCROLLDOWN 2342 +#define SCI_LINESCROLLUP 2343 +#define SCI_DELETEBACKNOTLINE 2344 +#define SCI_HOMEDISPLAY 2345 +#define SCI_HOMEDISPLAYEXTEND 2346 +#define SCI_LINEENDDISPLAY 2347 +#define SCI_LINEENDDISPLAYEXTEND 2348 +#define SCI_HOMEWRAP 2349 +#define SCI_HOMEWRAPEXTEND 2450 +#define SCI_LINEENDWRAP 2451 +#define SCI_LINEENDWRAPEXTEND 2452 +#define SCI_VCHOMEWRAP 2453 +#define SCI_VCHOMEWRAPEXTEND 2454 +#define SCI_LINECOPY 2455 +#define SCI_MOVECARETINSIDEVIEW 2401 +#define SCI_LINELENGTH 2350 +#define SCI_BRACEHIGHLIGHT 2351 +#define SCI_BRACEBADLIGHT 2352 +#define SCI_BRACEMATCH 2353 +#define SCI_GETVIEWEOL 2355 +#define SCI_SETVIEWEOL 2356 +#define SCI_GETDOCPOINTER 2357 +#define SCI_SETDOCPOINTER 2358 +#define SCI_SETMODEVENTMASK 2359 +#define EDGE_NONE 0 +#define EDGE_LINE 1 +#define EDGE_BACKGROUND 2 +#define SCI_GETEDGECOLUMN 2360 +#define SCI_SETEDGECOLUMN 2361 +#define SCI_GETEDGEMODE 2362 +#define SCI_SETEDGEMODE 2363 +#define SCI_GETEDGECOLOUR 2364 +#define SCI_SETEDGECOLOUR 2365 +#define SCI_SEARCHANCHOR 2366 +#define SCI_SEARCHNEXT 2367 +#define SCI_SEARCHPREV 2368 +#define SCI_LINESONSCREEN 2370 +#define SCI_USEPOPUP 2371 +#define SCI_SELECTIONISRECTANGLE 2372 +#define SCI_SETZOOM 2373 +#define SCI_GETZOOM 2374 +#define SCI_CREATEDOCUMENT 2375 +#define SCI_ADDREFDOCUMENT 2376 +#define SCI_RELEASEDOCUMENT 2377 +#define SCI_GETMODEVENTMASK 2378 +#define SCI_SETFOCUS 2380 +#define SCI_GETFOCUS 2381 +#define SCI_SETSTATUS 2382 +#define SCI_GETSTATUS 2383 +#define SCI_SETMOUSEDOWNCAPTURES 2384 +#define SCI_GETMOUSEDOWNCAPTURES 2385 +#define SC_CURSORNORMAL -1 +#define SC_CURSORWAIT 4 +#define SCI_SETCURSOR 2386 +#define SCI_GETCURSOR 2387 +#define SCI_SETCONTROLCHARSYMBOL 2388 +#define SCI_GETCONTROLCHARSYMBOL 2389 +#define SCI_WORDPARTLEFT 2390 +#define SCI_WORDPARTLEFTEXTEND 2391 +#define SCI_WORDPARTRIGHT 2392 +#define SCI_WORDPARTRIGHTEXTEND 2393 +#define VISIBLE_SLOP 0x01 +#define VISIBLE_STRICT 0x04 +#define SCI_SETVISIBLEPOLICY 2394 +#define SCI_DELLINELEFT 2395 +#define SCI_DELLINERIGHT 2396 +#define SCI_SETXOFFSET 2397 +#define SCI_GETXOFFSET 2398 +#define SCI_CHOOSECARETX 2399 +#define SCI_GRABFOCUS 2400 +#define CARET_SLOP 0x01 +#define CARET_STRICT 0x04 +#define CARET_JUMPS 0x10 +#define CARET_EVEN 0x08 +#define SCI_SETXCARETPOLICY 2402 +#define SCI_SETYCARETPOLICY 2403 +#define SCI_SETPRINTWRAPMODE 2406 +#define SCI_GETPRINTWRAPMODE 2407 +#define SCI_SETHOTSPOTACTIVEFORE 2410 +#define SCI_SETHOTSPOTACTIVEBACK 2411 +#define SCI_SETHOTSPOTACTIVEUNDERLINE 2412 +#define SCI_SETHOTSPOTSINGLELINE 2421 +#define SCI_PARADOWN 2413 +#define SCI_PARADOWNEXTEND 2414 +#define SCI_PARAUP 2415 +#define SCI_PARAUPEXTEND 2416 +#define SCI_POSITIONBEFORE 2417 +#define SCI_POSITIONAFTER 2418 +#define SCI_COPYRANGE 2419 +#define SCI_COPYTEXT 2420 +#define SC_SEL_STREAM 0 +#define SC_SEL_RECTANGLE 1 +#define SC_SEL_LINES 2 +#define SCI_SETSELECTIONMODE 2422 +#define SCI_GETSELECTIONMODE 2423 +#define SCI_GETLINESELSTARTPOSITION 2424 +#define SCI_GETLINESELENDPOSITION 2425 +#define SCI_LINEDOWNRECTEXTEND 2426 +#define SCI_LINEUPRECTEXTEND 2427 +#define SCI_CHARLEFTRECTEXTEND 2428 +#define SCI_CHARRIGHTRECTEXTEND 2429 +#define SCI_HOMERECTEXTEND 2430 +#define SCI_VCHOMERECTEXTEND 2431 +#define SCI_LINEENDRECTEXTEND 2432 +#define SCI_PAGEUPRECTEXTEND 2433 +#define SCI_PAGEDOWNRECTEXTEND 2434 +#define SCI_STUTTEREDPAGEUP 2435 +#define SCI_STUTTEREDPAGEUPEXTEND 2436 +#define SCI_STUTTEREDPAGEDOWN 2437 +#define SCI_STUTTEREDPAGEDOWNEXTEND 2438 +#define SCI_WORDLEFTEND 2439 +#define SCI_WORDLEFTENDEXTEND 2440 +#define SCI_WORDRIGHTEND 2441 +#define SCI_WORDRIGHTENDEXTEND 2442 +#define SCI_SETWHITESPACECHARS 2443 +#define SCI_SETCHARSDEFAULT 2444 +#define SCI_AUTOCGETCURRENT 2445 +#define SCI_ALLOCATE 2446 +#define SCI_TARGETASUTF8 2447 +#define SCI_SETLENGTHFORENCODE 2448 +#define SCI_ENCODEDFROMUTF8 2449 +#define SCI_FINDCOLUMN 2456 +#define SCI_GETCARETSTICKY 2457 +#define SCI_SETCARETSTICKY 2458 +#define SCI_TOGGLECARETSTICKY 2459 +#define SCI_SETPASTECONVERTENDINGS 2467 +#define SCI_GETPASTECONVERTENDINGS 2468 +#define SCI_SELECTIONDUPLICATE 2469 +#define SC_ALPHA_TRANSPARENT 0 +#define SC_ALPHA_OPAQUE 255 +#define SC_ALPHA_NOALPHA 256 +#define SCI_SETCARETLINEBACKALPHA 2470 +#define SCI_GETCARETLINEBACKALPHA 2471 +#define SCI_STARTRECORD 3001 +#define SCI_STOPRECORD 3002 +#define SCI_SETLEXER 4001 +#define SCI_GETLEXER 4002 +#define SCI_COLOURISE 4003 +#define SCI_SETPROPERTY 4004 +#define KEYWORDSET_MAX 8 +#define SCI_SETKEYWORDS 4005 +#define SCI_SETLEXERLANGUAGE 4006 +#define SCI_LOADLEXERLIBRARY 4007 +#define SCI_GETPROPERTY 4008 +#define SCI_GETPROPERTYEXPANDED 4009 +#define SCI_GETPROPERTYINT 4010 +#define SCI_GETSTYLEBITSNEEDED 4011 +#define SC_MOD_INSERTTEXT 0x1 +#define SC_MOD_DELETETEXT 0x2 +#define SC_MOD_CHANGESTYLE 0x4 +#define SC_MOD_CHANGEFOLD 0x8 +#define SC_PERFORMED_USER 0x10 +#define SC_PERFORMED_UNDO 0x20 +#define SC_PERFORMED_REDO 0x40 +#define SC_MULTISTEPUNDOREDO 0x80 +#define SC_LASTSTEPINUNDOREDO 0x100 +#define SC_MOD_CHANGEMARKER 0x200 +#define SC_MOD_BEFOREINSERT 0x400 +#define SC_MOD_BEFOREDELETE 0x800 +#define SC_MULTILINEUNDOREDO 0x1000 +#define SC_MODEVENTMASKALL 0x1FFF +#define SCEN_CHANGE 768 +#define SCEN_SETFOCUS 512 +#define SCEN_KILLFOCUS 256 +#define SCK_DOWN 300 +#define SCK_UP 301 +#define SCK_LEFT 302 +#define SCK_RIGHT 303 +#define SCK_HOME 304 +#define SCK_END 305 +#define SCK_PRIOR 306 +#define SCK_NEXT 307 +#define SCK_DELETE 308 +#define SCK_INSERT 309 +#define SCK_ESCAPE 7 +#define SCK_BACK 8 +#define SCK_TAB 9 +#define SCK_RETURN 13 +#define SCK_ADD 310 +#define SCK_SUBTRACT 311 +#define SCK_DIVIDE 312 +#define SCMOD_NORM 0 +#define SCMOD_SHIFT 1 +#define SCMOD_CTRL 2 +#define SCMOD_ALT 4 +#define SCN_STYLENEEDED 2000 +#define SCN_CHARADDED 2001 +#define SCN_SAVEPOINTREACHED 2002 +#define SCN_SAVEPOINTLEFT 2003 +#define SCN_MODIFYATTEMPTRO 2004 +#define SCN_KEY 2005 +#define SCN_DOUBLECLICK 2006 +#define SCN_UPDATEUI 2007 +#define SCN_MODIFIED 2008 +#define SCN_MACRORECORD 2009 +#define SCN_MARGINCLICK 2010 +#define SCN_NEEDSHOWN 2011 +#define SCN_PAINTED 2013 +#define SCN_USERLISTSELECTION 2014 +#define SCN_URIDROPPED 2015 +#define SCN_DWELLSTART 2016 +#define SCN_DWELLEND 2017 +#define SCN_ZOOM 2018 +#define SCN_HOTSPOTCLICK 2019 +#define SCN_HOTSPOTDOUBLECLICK 2020 +#define SCN_CALLTIPCLICK 2021 +#define SCN_AUTOCSELECTION 2022 +//--Autogenerated -- end of section automatically generated from Scintilla.iface + +// These structures are defined to be exactly the same shape as the Win32 +// CHARRANGE, TEXTRANGE, FINDTEXTEX, FORMATRANGE, and NMHDR structs. +// So older code that treats Scintilla as a RichEdit will work. + +struct CharacterRange { + long cpMin; + long cpMax; +}; + +struct TextRange { + struct CharacterRange chrg; + char *lpstrText; +}; + +struct TextToFind { + struct CharacterRange chrg; + char *lpstrText; + struct CharacterRange chrgText; +}; + +#ifdef PLATFORM_H + +// This structure is used in printing and requires some of the graphics types +// from Platform.h. Not needed by most client code. + +struct RangeToFormat { + SurfaceID hdc; + SurfaceID hdcTarget; + PRectangle rc; + PRectangle rcPage; + CharacterRange chrg; +}; + +#endif + +struct NotifyHeader { + // Compatible with Windows NMHDR. + // hwndFrom is really an environment specific window handle or pointer + // but most clients of Scintilla.h do not have this type visible. + void *hwndFrom; + uptr_t idFrom; + unsigned int code; +}; + +struct SCNotification { + struct NotifyHeader nmhdr; + int position; // SCN_STYLENEEDED, SCN_MODIFIED, SCN_DWELLSTART, SCN_DWELLEND + int ch; // SCN_CHARADDED, SCN_KEY + int modifiers; // SCN_KEY + int modificationType; // SCN_MODIFIED + const char *text; // SCN_MODIFIED, SCN_USERLISTSELECTION, SCN_AUTOCSELECTION + int length; // SCN_MODIFIED + int linesAdded; // SCN_MODIFIED + int message; // SCN_MACRORECORD + uptr_t wParam; // SCN_MACRORECORD + sptr_t lParam; // SCN_MACRORECORD + int line; // SCN_MODIFIED + int foldLevelNow; // SCN_MODIFIED + int foldLevelPrev; // SCN_MODIFIED + int margin; // SCN_MARGINCLICK + int listType; // SCN_USERLISTSELECTION + int x; // SCN_DWELLSTART, SCN_DWELLEND + int y; // SCN_DWELLSTART, SCN_DWELLEND +}; + +// Deprecation section listing all API features that are deprecated and will +// will be removed completely in a future version. +// To enable these features define INCLUDE_DEPRECATED_FEATURES + +#ifdef INCLUDE_DEPRECATED_FEATURES + +#define SCI_SETCARETPOLICY 2369 +#define CARET_CENTER 0x02 +#define CARET_XEVEN 0x08 +#define CARET_XJUMPS 0x10 + +#define SCN_POSCHANGED 2012 +#define SCN_CHECKBRACE 2007 + +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Scintilla.iface b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Scintilla.iface new file mode 100644 index 00000000..d8f1923c --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/Scintilla.iface @@ -0,0 +1,3012 @@ +## First line may be used for shbang + +## This file defines the interface to Scintilla + +## Copyright 2000-2003 by Neil Hodgson +## The License.txt file describes the conditions under which this software may be distributed. + +## A line starting with ## is a pure comment and should be stripped by readers. +## A line starting with #! is for future shbang use +## A line starting with # followed by a space is a documentation comment and refers +## to the next feature definition. + +## Each feature is defined by a line starting with fun, get, set, val or evt. +## cat -> start a category +## fun -> a function +## get -> a property get function +## set -> a property set function +## val -> definition of a constant +## evt -> an event +## enu -> associate an enumeration with a set of vals with a prefix +## lex -> associate a lexer with the lexical classes it produces +## +## All other feature names should be ignored. They may be defined in the future. +## A property may have a set function, a get function or both. Each will have +## "Get" or "Set" in their names and the corresponding name will have the obvious switch. +## A property may be subscripted, in which case the first parameter is the subscript. +## fun, get, and set features have a strict syntax: +## [=,) +## where stands for white space. +## param may be empty (null value) or is [=] +## Additional white space is allowed between elements. +## The syntax for evt is [=[,]*]) +## Feature names that contain an underscore are defined by Windows, so in these +## cases, using the Windows definition is preferred where available. +## The feature numbers are stable so features will not be renumbered. +## Features may be removed but they will go through a period of deprecation +## before removal which is signalled by moving them into the Deprecated category. +## +## enu has the syntax enu=[]* where all the val +## features in this file starting with a given are considered part of the +## enumeration. +## +## lex has the syntax lex=[]* +## where name is a reasonably capitalised (Python, XML) identifier or UI name, +## lexerVal is the val used to specify the lexer, and the list of prefixes is similar +## to enu. The name may not be the same as that used within the lexer so the lexerVal +## should be used to tie these entities together. + +## Types: +## void +## int +## bool -> integer, 1=true, 0=false +## position -> integer position in a document +## colour -> colour integer containing red, green and blue bytes. +## string -> pointer to const character +## stringresult -> pointer to character, NULL-> return size of result +## cells -> pointer to array of cells, each cell containing a style byte and character byte +## textrange -> range of a min and a max position with an output string +## findtext -> searchrange, text -> foundposition +## keymod -> integer containing key in low half and modifiers in high half +## formatrange +## Types no longer used: +## findtextex -> searchrange +## charrange -> range of a min and a max position +## charrangeresult -> like charrange, but output param +## countedstring +## point -> x,y +## pointresult -> like point, but output param +## rectangle -> left,top,right,bottom +## Client code should ignore definitions containing types it does not understand, except +## for possibly #defining the constants + +## Line numbers and positions start at 0. +## String arguments may contain NUL ('\0') characters where the calls provide a length +## argument and retrieve NUL characters. All retrieved strings except for those retrieved +## by GetLine also have a NUL appended but client code should calculate the size that +## will be returned rather than relying upon the NUL whenever possible. Allow for the +## extra NUL character when allocating buffers. The size to allocate for a stringresult +## can be determined by calling with a NULL (0) pointer. + +cat Basics + +################################################ +## For Scintilla.h +val INVALID_POSITION=-1 +# Define start of Scintilla messages to be greater than all Windows edit (EM_*) messages +# as many EM_ messages can be used although that use is deprecated. +val SCI_START=2000 +val SCI_OPTIONAL_START=3000 +val SCI_LEXER_START=4000 + +# Add text to the document at current position. +fun void AddText=2001(int length, string text) + +# Add array of cells to document. +fun void AddStyledText=2002(int length, cells c) + +# Insert string at a position. +fun void InsertText=2003(position pos, string text) + +# Delete all text in the document. +fun void ClearAll=2004(,) + +# Set all style bytes to 0, remove all folding information. +fun void ClearDocumentStyle=2005(,) + +# Returns the number of characters in the document. +get int GetLength=2006(,) + +# Returns the character byte at the position. +get int GetCharAt=2007(position pos,) + +# Returns the position of the caret. +get position GetCurrentPos=2008(,) + +# Returns the position of the opposite end of the selection to the caret. +get position GetAnchor=2009(,) + +# Returns the style byte at the position. +get int GetStyleAt=2010(position pos,) + +# Redoes the next action on the undo history. +fun void Redo=2011(,) + +# Choose between collecting actions into the undo +# history and discarding them. +set void SetUndoCollection=2012(bool collectUndo,) + +# Select all the text in the document. +fun void SelectAll=2013(,) + +# Remember the current position in the undo history as the position +# at which the document was saved. +fun void SetSavePoint=2014(,) + +# Retrieve a buffer of cells. +# Returns the number of bytes in the buffer not including terminating NULs. +fun int GetStyledText=2015(, textrange tr) + +# Are there any redoable actions in the undo history? +fun bool CanRedo=2016(,) + +# Retrieve the line number at which a particular marker is located. +fun int MarkerLineFromHandle=2017(int handle,) + +# Delete a marker. +fun void MarkerDeleteHandle=2018(int handle,) + +# Is undo history being collected? +get bool GetUndoCollection=2019(,) + +enu WhiteSpace=SCWS_ +val SCWS_INVISIBLE=0 +val SCWS_VISIBLEALWAYS=1 +val SCWS_VISIBLEAFTERINDENT=2 + +# Are white space characters currently visible? +# Returns one of SCWS_* constants. +get int GetViewWS=2020(,) + +# Make white space characters invisible, always visible or visible outside indentation. +set void SetViewWS=2021(int viewWS,) + +# Find the position from a point within the window. +fun position PositionFromPoint=2022(int x, int y) + +# Find the position from a point within the window but return +# INVALID_POSITION if not close to text. +fun position PositionFromPointClose=2023(int x, int y) + +# Set caret to start of a line and ensure it is visible. +fun void GotoLine=2024(int line,) + +# Set caret to a position and ensure it is visible. +fun void GotoPos=2025(position pos,) + +# Set the selection anchor to a position. The anchor is the opposite +# end of the selection from the caret. +set void SetAnchor=2026(position posAnchor,) + +# Retrieve the text of the line containing the caret. +# Returns the index of the caret on the line. +fun int GetCurLine=2027(int length, stringresult text) + +# Retrieve the position of the last correctly styled character. +get position GetEndStyled=2028(,) + +enu EndOfLine=SC_EOL_ +val SC_EOL_CRLF=0 +val SC_EOL_CR=1 +val SC_EOL_LF=2 + +# Convert all line endings in the document to one mode. +fun void ConvertEOLs=2029(int eolMode,) + +# Retrieve the current end of line mode - one of CRLF, CR, or LF. +get int GetEOLMode=2030(,) + +# Set the current end of line mode. +set void SetEOLMode=2031(int eolMode,) + +# Set the current styling position to pos and the styling mask to mask. +# The styling mask can be used to protect some bits in each styling byte from modification. +fun void StartStyling=2032(position pos, int mask) + +# Change style from current styling position for length characters to a style +# and move the current styling position to after this newly styled segment. +fun void SetStyling=2033(int length, int style) + +# Is drawing done first into a buffer or direct to the screen? +get bool GetBufferedDraw=2034(,) + +# If drawing is buffered then each line of text is drawn into a bitmap buffer +# before drawing it to the screen to avoid flicker. +set void SetBufferedDraw=2035(bool buffered,) + +# Change the visible size of a tab to be a multiple of the width of a space character. +set void SetTabWidth=2036(int tabWidth,) + +# Retrieve the visible size of a tab. +get int GetTabWidth=2121(,) + +# The SC_CP_UTF8 value can be used to enter Unicode mode. +# This is the same value as CP_UTF8 in Windows +val SC_CP_UTF8=65001 + +# The SC_CP_DBCS value can be used to indicate a DBCS mode for GTK+. +val SC_CP_DBCS=1 + +# Set the code page used to interpret the bytes of the document as characters. +# The SC_CP_UTF8 value can be used to enter Unicode mode. +set void SetCodePage=2037(int codePage,) + +# In palette mode, Scintilla uses the environment's palette calls to display +# more colours. This may lead to ugly displays. +set void SetUsePalette=2039(bool usePalette,) + +enu MarkerSymbol=SC_MARK_ +val MARKER_MAX=31 +val SC_MARK_CIRCLE=0 +val SC_MARK_ROUNDRECT=1 +val SC_MARK_ARROW=2 +val SC_MARK_SMALLRECT=3 +val SC_MARK_SHORTARROW=4 +val SC_MARK_EMPTY=5 +val SC_MARK_ARROWDOWN=6 +val SC_MARK_MINUS=7 +val SC_MARK_PLUS=8 + +# Shapes used for outlining column. +val SC_MARK_VLINE=9 +val SC_MARK_LCORNER=10 +val SC_MARK_TCORNER=11 +val SC_MARK_BOXPLUS=12 +val SC_MARK_BOXPLUSCONNECTED=13 +val SC_MARK_BOXMINUS=14 +val SC_MARK_BOXMINUSCONNECTED=15 +val SC_MARK_LCORNERCURVE=16 +val SC_MARK_TCORNERCURVE=17 +val SC_MARK_CIRCLEPLUS=18 +val SC_MARK_CIRCLEPLUSCONNECTED=19 +val SC_MARK_CIRCLEMINUS=20 +val SC_MARK_CIRCLEMINUSCONNECTED=21 + +# Invisible mark that only sets the line background color. +val SC_MARK_BACKGROUND=22 +val SC_MARK_DOTDOTDOT=23 +val SC_MARK_ARROWS=24 +val SC_MARK_PIXMAP=25 +val SC_MARK_FULLRECT=26 + +val SC_MARK_CHARACTER=10000 + +enu MarkerOutline=SC_MARKNUM_ +# Markers used for outlining column. +val SC_MARKNUM_FOLDEREND=25 +val SC_MARKNUM_FOLDEROPENMID=26 +val SC_MARKNUM_FOLDERMIDTAIL=27 +val SC_MARKNUM_FOLDERTAIL=28 +val SC_MARKNUM_FOLDERSUB=29 +val SC_MARKNUM_FOLDER=30 +val SC_MARKNUM_FOLDEROPEN=31 + +val SC_MASK_FOLDERS=0xFE000000 + +# Set the symbol used for a particular marker number. +fun void MarkerDefine=2040(int markerNumber, int markerSymbol) + +# Set the foreground colour used for a particular marker number. +fun void MarkerSetFore=2041(int markerNumber, colour fore) + +# Set the background colour used for a particular marker number. +fun void MarkerSetBack=2042(int markerNumber, colour back) + +# Add a marker to a line, returning an ID which can be used to find or delete the marker. +fun int MarkerAdd=2043(int line, int markerNumber) + +# Delete a marker from a line. +fun void MarkerDelete=2044(int line, int markerNumber) + +# Delete all markers with a particular number from all lines. +fun void MarkerDeleteAll=2045(int markerNumber,) + +# Get a bit mask of all the markers set on a line. +fun int MarkerGet=2046(int line,) + +# Find the next line after lineStart that includes a marker in mask. +fun int MarkerNext=2047(int lineStart, int markerMask) + +# Find the previous line before lineStart that includes a marker in mask. +fun int MarkerPrevious=2048(int lineStart, int markerMask) + +# Define a marker from a pixmap. +fun void MarkerDefinePixmap=2049(int markerNumber, string pixmap) + +# Add a set of markers to a line. +fun void MarkerAddSet=2466(int line, int set) + +# Set the alpha used for a marker that is drawn in the text area, not the margin. +fun void MarkerSetAlpha=2476(int markerNumber, int alpha) + +enu MarginType=SC_MARGIN_ +val SC_MARGIN_SYMBOL=0 +val SC_MARGIN_NUMBER=1 +val SC_MARGIN_BACK=2 +val SC_MARGIN_FORE=3 + +# Set a margin to be either numeric or symbolic. +set void SetMarginTypeN=2240(int margin, int marginType) + +# Retrieve the type of a margin. +get int GetMarginTypeN=2241(int margin,) + +# Set the width of a margin to a width expressed in pixels. +set void SetMarginWidthN=2242(int margin, int pixelWidth) + +# Retrieve the width of a margin in pixels. +get int GetMarginWidthN=2243(int margin,) + +# Set a mask that determines which markers are displayed in a margin. +set void SetMarginMaskN=2244(int margin, int mask) + +# Retrieve the marker mask of a margin. +get int GetMarginMaskN=2245(int margin,) + +# Make a margin sensitive or insensitive to mouse clicks. +set void SetMarginSensitiveN=2246(int margin, bool sensitive) + +# Retrieve the mouse click sensitivity of a margin. +get bool GetMarginSensitiveN=2247(int margin,) + +# Styles in range 32..38 are predefined for parts of the UI and are not used as normal styles. +# Style 39 is for future use. +enu StylesCommon=STYLE_ +val STYLE_DEFAULT=32 +val STYLE_LINENUMBER=33 +val STYLE_BRACELIGHT=34 +val STYLE_BRACEBAD=35 +val STYLE_CONTROLCHAR=36 +val STYLE_INDENTGUIDE=37 +val STYLE_CALLTIP=38 +val STYLE_LASTPREDEFINED=39 +val STYLE_MAX=127 + +# Character set identifiers are used in StyleSetCharacterSet. +# The values are the same as the Windows *_CHARSET values. +enu CharacterSet=SC_CHARSET_ +val SC_CHARSET_ANSI=0 +val SC_CHARSET_DEFAULT=1 +val SC_CHARSET_BALTIC=186 +val SC_CHARSET_CHINESEBIG5=136 +val SC_CHARSET_EASTEUROPE=238 +val SC_CHARSET_GB2312=134 +val SC_CHARSET_GREEK=161 +val SC_CHARSET_HANGUL=129 +val SC_CHARSET_MAC=77 +val SC_CHARSET_OEM=255 +val SC_CHARSET_RUSSIAN=204 +val SC_CHARSET_CYRILLIC=1251 +val SC_CHARSET_SHIFTJIS=128 +val SC_CHARSET_SYMBOL=2 +val SC_CHARSET_TURKISH=162 +val SC_CHARSET_JOHAB=130 +val SC_CHARSET_HEBREW=177 +val SC_CHARSET_ARABIC=178 +val SC_CHARSET_VIETNAMESE=163 +val SC_CHARSET_THAI=222 +val SC_CHARSET_8859_15=1000 + +# Clear all the styles and make equivalent to the global default style. +set void StyleClearAll=2050(,) + +# Set the foreground colour of a style. +set void StyleSetFore=2051(int style, colour fore) + +# Set the background colour of a style. +set void StyleSetBack=2052(int style, colour back) + +# Set a style to be bold or not. +set void StyleSetBold=2053(int style, bool bold) + +# Set a style to be italic or not. +set void StyleSetItalic=2054(int style, bool italic) + +# Set the size of characters of a style. +set void StyleSetSize=2055(int style, int sizePoints) + +# Set the font of a style. +set void StyleSetFont=2056(int style, string fontName) + +# Set a style to have its end of line filled or not. +set void StyleSetEOLFilled=2057(int style, bool filled) + +# Reset the default style to its state at startup +fun void StyleResetDefault=2058(,) + +# Set a style to be underlined or not. +set void StyleSetUnderline=2059(int style, bool underline) + +enu CaseVisible=SC_CASE_ +val SC_CASE_MIXED=0 +val SC_CASE_UPPER=1 +val SC_CASE_LOWER=2 +# Set a style to be mixed case, or to force upper or lower case. +set void StyleSetCase=2060(int style, int caseForce) + +# Set the character set of the font in a style. +set void StyleSetCharacterSet=2066(int style, int characterSet) + +# Set a style to be a hotspot or not. +set void StyleSetHotSpot=2409(int style, bool hotspot) + +# Set the foreground colour of the selection and whether to use this setting. +fun void SetSelFore=2067(bool useSetting, colour fore) + +# Set the background colour of the selection and whether to use this setting. +fun void SetSelBack=2068(bool useSetting, colour back) + +# Get the alpha of the selection. +get int GetSelAlpha=2477(,) + +# Set the alpha of the selection. +set void SetSelAlpha=2478(int alpha,) + +# Set the foreground colour of the caret. +set void SetCaretFore=2069(colour fore,) + +# When key+modifier combination km is pressed perform msg. +fun void AssignCmdKey=2070(keymod km, int msg) + +# When key+modifier combination km is pressed do nothing. +fun void ClearCmdKey=2071(keymod km,) + +# Drop all key mappings. +fun void ClearAllCmdKeys=2072(,) + +# Set the styles for a segment of the document. +fun void SetStylingEx=2073(int length, string styles) + +# Set a style to be visible or not. +set void StyleSetVisible=2074(int style, bool visible) + +# Get the time in milliseconds that the caret is on and off. +get int GetCaretPeriod=2075(,) + +# Get the time in milliseconds that the caret is on and off. 0 = steady on. +set void SetCaretPeriod=2076(int periodMilliseconds,) + +# Set the set of characters making up words for when moving or selecting by word. +# First sets deaults like SetCharsDefault. +set void SetWordChars=2077(, string characters) + +# Start a sequence of actions that is undone and redone as a unit. +# May be nested. +fun void BeginUndoAction=2078(,) + +# End a sequence of actions that is undone and redone as a unit. +fun void EndUndoAction=2079(,) + +enu IndicatorStyle=INDIC_ +val INDIC_MAX=7 +val INDIC_PLAIN=0 +val INDIC_SQUIGGLE=1 +val INDIC_TT=2 +val INDIC_DIAGONAL=3 +val INDIC_STRIKE=4 +val INDIC_HIDDEN=5 +val INDIC_BOX=6 +val INDIC_ROUNDBOX=7 +val INDIC0_MASK=0x20 +val INDIC1_MASK=0x40 +val INDIC2_MASK=0x80 +val INDICS_MASK=0xE0 + +# Set an indicator to plain, squiggle or TT. +set void IndicSetStyle=2080(int indic, int style) + +# Retrieve the style of an indicator. +get int IndicGetStyle=2081(int indic,) + +# Set the foreground colour of an indicator. +set void IndicSetFore=2082(int indic, colour fore) + +# Retrieve the foreground colour of an indicator. +get colour IndicGetFore=2083(int indic,) + +# Set the foreground colour of all whitespace and whether to use this setting. +fun void SetWhitespaceFore=2084(bool useSetting, colour fore) + +# Set the background colour of all whitespace and whether to use this setting. +fun void SetWhitespaceBack=2085(bool useSetting, colour back) + +# Divide each styling byte into lexical class bits (default: 5) and indicator +# bits (default: 3). If a lexer requires more than 32 lexical states, then this +# is used to expand the possible states. +set void SetStyleBits=2090(int bits,) + +# Retrieve number of bits in style bytes used to hold the lexical state. +get int GetStyleBits=2091(,) + +# Used to hold extra styling information for each line. +set void SetLineState=2092(int line, int state) + +# Retrieve the extra styling information for a line. +get int GetLineState=2093(int line,) + +# Retrieve the last line number that has line state. +get int GetMaxLineState=2094(,) + +# Is the background of the line containing the caret in a different colour? +get bool GetCaretLineVisible=2095(,) + +# Display the background of the line containing the caret in a different colour. +set void SetCaretLineVisible=2096(bool show,) + +# Get the colour of the background of the line containing the caret. +get colour GetCaretLineBack=2097(,) + +# Set the colour of the background of the line containing the caret. +set void SetCaretLineBack=2098(colour back,) + +# Set a style to be changeable or not (read only). +# Experimental feature, currently buggy. +set void StyleSetChangeable=2099(int style, bool changeable) + +# Display a auto-completion list. +# The lenEntered parameter indicates how many characters before +# the caret should be used to provide context. +fun void AutoCShow=2100(int lenEntered, string itemList) + +# Remove the auto-completion list from the screen. +fun void AutoCCancel=2101(,) + +# Is there an auto-completion list visible? +fun bool AutoCActive=2102(,) + +# Retrieve the position of the caret when the auto-completion list was displayed. +fun position AutoCPosStart=2103(,) + +# User has selected an item so remove the list and insert the selection. +fun void AutoCComplete=2104(,) + +# Define a set of character that when typed cancel the auto-completion list. +fun void AutoCStops=2105(, string characterSet) + +# Change the separator character in the string setting up an auto-completion list. +# Default is space but can be changed if items contain space. +set void AutoCSetSeparator=2106(int separatorCharacter,) + +# Retrieve the auto-completion list separator character. +get int AutoCGetSeparator=2107(,) + +# Select the item in the auto-completion list that starts with a string. +fun void AutoCSelect=2108(, string text) + +# Should the auto-completion list be cancelled if the user backspaces to a +# position before where the box was created. +set void AutoCSetCancelAtStart=2110(bool cancel,) + +# Retrieve whether auto-completion cancelled by backspacing before start. +get bool AutoCGetCancelAtStart=2111(,) + +# Define a set of characters that when typed will cause the autocompletion to +# choose the selected item. +set void AutoCSetFillUps=2112(, string characterSet) + +# Should a single item auto-completion list automatically choose the item. +set void AutoCSetChooseSingle=2113(bool chooseSingle,) + +# Retrieve whether a single item auto-completion list automatically choose the item. +get bool AutoCGetChooseSingle=2114(,) + +# Set whether case is significant when performing auto-completion searches. +set void AutoCSetIgnoreCase=2115(bool ignoreCase,) + +# Retrieve state of ignore case flag. +get bool AutoCGetIgnoreCase=2116(,) + +# Display a list of strings and send notification when user chooses one. +fun void UserListShow=2117(int listType, string itemList) + +# Set whether or not autocompletion is hidden automatically when nothing matches. +set void AutoCSetAutoHide=2118(bool autoHide,) + +# Retrieve whether or not autocompletion is hidden automatically when nothing matches. +get bool AutoCGetAutoHide=2119(,) + +# Set whether or not autocompletion deletes any word characters +# after the inserted text upon completion. +set void AutoCSetDropRestOfWord=2270(bool dropRestOfWord,) + +# Retrieve whether or not autocompletion deletes any word characters +# after the inserted text upon completion. +get bool AutoCGetDropRestOfWord=2271(,) + +# Register an XPM image for use in autocompletion lists. +fun void RegisterImage=2405(int type, string xpmData) + +# Clear all the registered XPM images. +fun void ClearRegisteredImages=2408(,) + +# Retrieve the auto-completion list type-separator character. +get int AutoCGetTypeSeparator=2285(,) + +# Change the type-separator character in the string setting up an auto-completion list. +# Default is '?' but can be changed if items contain '?'. +set void AutoCSetTypeSeparator=2286(int separatorCharacter,) + +# Set the maximum width, in characters, of auto-completion and user lists. +# Set to 0 to autosize to fit longest item, which is the default. +set void AutoCSetMaxWidth=2208(int characterCount,) + +# Get the maximum width, in characters, of auto-completion and user lists. +get int AutoCGetMaxWidth=2209(,) + +# Set the maximum height, in rows, of auto-completion and user lists. +# The default is 5 rows. +set void AutoCSetMaxHeight=2210(int rowCount,) + +# Set the maximum height, in rows, of auto-completion and user lists. +get int AutoCGetMaxHeight=2211(,) + +# Set the number of spaces used for one level of indentation. +set void SetIndent=2122(int indentSize,) + +# Retrieve indentation size. +get int GetIndent=2123(,) + +# Indentation will only use space characters if useTabs is false, otherwise +# it will use a combination of tabs and spaces. +set void SetUseTabs=2124(bool useTabs,) + +# Retrieve whether tabs will be used in indentation. +get bool GetUseTabs=2125(,) + +# Change the indentation of a line to a number of columns. +set void SetLineIndentation=2126(int line, int indentSize) + +# Retrieve the number of columns that a line is indented. +get int GetLineIndentation=2127(int line,) + +# Retrieve the position before the first non indentation character on a line. +get position GetLineIndentPosition=2128(int line,) + +# Retrieve the column number of a position, taking tab width into account. +get int GetColumn=2129(position pos,) + +# Show or hide the horizontal scroll bar. +set void SetHScrollBar=2130(bool show,) + +# Is the horizontal scroll bar visible? +get bool GetHScrollBar=2131(,) + +# Show or hide indentation guides. +set void SetIndentationGuides=2132(bool show,) + +# Are the indentation guides visible? +get bool GetIndentationGuides=2133(,) + +# Set the highlighted indentation guide column. +# 0 = no highlighted guide. +set void SetHighlightGuide=2134(int column,) + +# Get the highlighted indentation guide column. +get int GetHighlightGuide=2135(,) + +# Get the position after the last visible characters on a line. +get int GetLineEndPosition=2136(int line,) + +# Get the code page used to interpret the bytes of the document as characters. +get int GetCodePage=2137(,) + +# Get the foreground colour of the caret. +get colour GetCaretFore=2138(,) + +# In palette mode? +get bool GetUsePalette=2139(,) + +# In read-only mode? +get bool GetReadOnly=2140(,) + +# Sets the position of the caret. +set void SetCurrentPos=2141(position pos,) + +# Sets the position that starts the selection - this becomes the anchor. +set void SetSelectionStart=2142(position pos,) + +# Returns the position at the start of the selection. +get position GetSelectionStart=2143(,) + +# Sets the position that ends the selection - this becomes the currentPosition. +set void SetSelectionEnd=2144(position pos,) + +# Returns the position at the end of the selection. +get position GetSelectionEnd=2145(,) + +# Sets the print magnification added to the point size of each style for printing. +set void SetPrintMagnification=2146(int magnification,) + +# Returns the print magnification. +get int GetPrintMagnification=2147(,) + +enu PrintOption=SC_PRINT_ +# PrintColourMode - use same colours as screen. +val SC_PRINT_NORMAL=0 +# PrintColourMode - invert the light value of each style for printing. +val SC_PRINT_INVERTLIGHT=1 +# PrintColourMode - force black text on white background for printing. +val SC_PRINT_BLACKONWHITE=2 +# PrintColourMode - text stays coloured, but all background is forced to be white for printing. +val SC_PRINT_COLOURONWHITE=3 +# PrintColourMode - only the default-background is forced to be white for printing. +val SC_PRINT_COLOURONWHITEDEFAULTBG=4 + +# Modify colours when printing for clearer printed text. +set void SetPrintColourMode=2148(int mode,) + +# Returns the print colour mode. +get int GetPrintColourMode=2149(,) + +enu FindOption=SCFIND_ +val SCFIND_WHOLEWORD=2 +val SCFIND_MATCHCASE=4 +val SCFIND_WORDSTART=0x00100000 +val SCFIND_REGEXP=0x00200000 +val SCFIND_POSIX=0x00400000 + +# Find some text in the document. +fun position FindText=2150(int flags, findtext ft) + +# On Windows, will draw the document into a display context such as a printer. +fun position FormatRange=2151(bool draw, formatrange fr) + +# Retrieve the display line at the top of the display. +get int GetFirstVisibleLine=2152(,) + +# Retrieve the contents of a line. +# Returns the length of the line. +fun int GetLine=2153(int line, stringresult text) + +# Returns the number of lines in the document. There is always at least one. +get int GetLineCount=2154(,) + +# Sets the size in pixels of the left margin. +set void SetMarginLeft=2155(, int pixelWidth) + +# Returns the size in pixels of the left margin. +get int GetMarginLeft=2156(,) + +# Sets the size in pixels of the right margin. +set void SetMarginRight=2157(, int pixelWidth) + +# Returns the size in pixels of the right margin. +get int GetMarginRight=2158(,) + +# Is the document different from when it was last saved? +get bool GetModify=2159(,) + +# Select a range of text. +fun void SetSel=2160(position start, position end) + +# Retrieve the selected text. +# Return the length of the text. +fun int GetSelText=2161(, stringresult text) + +# Retrieve a range of text. +# Return the length of the text. +fun int GetTextRange=2162(, textrange tr) + +# Draw the selection in normal style or with selection highlighted. +fun void HideSelection=2163(bool normal,) + +# Retrieve the x value of the point in the window where a position is displayed. +fun int PointXFromPosition=2164(, position pos) + +# Retrieve the y value of the point in the window where a position is displayed. +fun int PointYFromPosition=2165(, position pos) + +# Retrieve the line containing a position. +fun int LineFromPosition=2166(position pos,) + +# Retrieve the position at the start of a line. +fun position PositionFromLine=2167(int line,) + +# Scroll horizontally and vertically. +fun void LineScroll=2168(int columns, int lines) + +# Ensure the caret is visible. +fun void ScrollCaret=2169(,) + +# Replace the selected text with the argument text. +fun void ReplaceSel=2170(, string text) + +# Set to read only or read write. +set void SetReadOnly=2171(bool readOnly,) + +# Null operation. +fun void Null=2172(,) + +# Will a paste succeed? +fun bool CanPaste=2173(,) + +# Are there any undoable actions in the undo history? +fun bool CanUndo=2174(,) + +# Delete the undo history. +fun void EmptyUndoBuffer=2175(,) + +# Undo one action in the undo history. +fun void Undo=2176(,) + +# Cut the selection to the clipboard. +fun void Cut=2177(,) + +# Copy the selection to the clipboard. +fun void Copy=2178(,) + +# Paste the contents of the clipboard into the document replacing the selection. +fun void Paste=2179(,) + +# Clear the selection. +fun void Clear=2180(,) + +# Replace the contents of the document with the argument text. +fun void SetText=2181(, string text) + +# Retrieve all the text in the document. +# Returns number of characters retrieved. +fun int GetText=2182(int length, stringresult text) + +# Retrieve the number of characters in the document. +get int GetTextLength=2183(,) + +# Retrieve a pointer to a function that processes messages for this Scintilla. +get int GetDirectFunction=2184(,) + +# Retrieve a pointer value to use as the first argument when calling +# the function returned by GetDirectFunction. +get int GetDirectPointer=2185(,) + +# Set to overtype (true) or insert mode. +set void SetOvertype=2186(bool overtype,) + +# Returns true if overtype mode is active otherwise false is returned. +get bool GetOvertype=2187(,) + +# Set the width of the insert mode caret. +set void SetCaretWidth=2188(int pixelWidth,) + +# Returns the width of the insert mode caret. +get int GetCaretWidth=2189(,) + +# Sets the position that starts the target which is used for updating the +# document without affecting the scroll position. +set void SetTargetStart=2190(position pos,) + +# Get the position that starts the target. +get position GetTargetStart=2191(,) + +# Sets the position that ends the target which is used for updating the +# document without affecting the scroll position. +set void SetTargetEnd=2192(position pos,) + +# Get the position that ends the target. +get position GetTargetEnd=2193(,) + +# Replace the target text with the argument text. +# Text is counted so it can contain NULs. +# Returns the length of the replacement text. +fun int ReplaceTarget=2194(int length, string text) + +# Replace the target text with the argument text after \d processing. +# Text is counted so it can contain NULs. +# Looks for \d where d is between 1 and 9 and replaces these with the strings +# matched in the last search operation which were surrounded by \( and \). +# Returns the length of the replacement text including any change +# caused by processing the \d patterns. +fun int ReplaceTargetRE=2195(int length, string text) + +# Search for a counted string in the target and set the target to the found +# range. Text is counted so it can contain NULs. +# Returns length of range or -1 for failure in which case target is not moved. +fun int SearchInTarget=2197(int length, string text) + +# Set the search flags used by SearchInTarget. +set void SetSearchFlags=2198(int flags,) + +# Get the search flags used by SearchInTarget. +get int GetSearchFlags=2199(,) + +# Show a call tip containing a definition near position pos. +fun void CallTipShow=2200(position pos, string definition) + +# Remove the call tip from the screen. +fun void CallTipCancel=2201(,) + +# Is there an active call tip? +fun bool CallTipActive=2202(,) + +# Retrieve the position where the caret was before displaying the call tip. +fun position CallTipPosStart=2203(,) + +# Highlight a segment of the definition. +fun void CallTipSetHlt=2204(int start, int end) + +# Set the background colour for the call tip. +set void CallTipSetBack=2205(colour back,) + +# Set the foreground colour for the call tip. +set void CallTipSetFore=2206(colour fore,) + +# Set the foreground colour for the highlighted part of the call tip. +set void CallTipSetForeHlt=2207(colour fore,) + +# Enable use of STYLE_CALLTIP and set call tip tab size in pixels. +set void CallTipUseStyle=2212(int tabSize,) + +# Find the display line of a document line taking hidden lines into account. +fun int VisibleFromDocLine=2220(int line,) + +# Find the document line of a display line taking hidden lines into account. +fun int DocLineFromVisible=2221(int lineDisplay,) + +# The number of display lines needed to wrap a document line +fun int WrapCount=2235(int line,) + +enu FoldLevel=SC_FOLDLEVEL +val SC_FOLDLEVELBASE=0x400 +val SC_FOLDLEVELWHITEFLAG=0x1000 +val SC_FOLDLEVELHEADERFLAG=0x2000 +val SC_FOLDLEVELBOXHEADERFLAG=0x4000 +val SC_FOLDLEVELBOXFOOTERFLAG=0x8000 +val SC_FOLDLEVELCONTRACTED=0x10000 +val SC_FOLDLEVELUNINDENT=0x20000 +val SC_FOLDLEVELNUMBERMASK=0x0FFF + +# Set the fold level of a line. +# This encodes an integer level along with flags indicating whether the +# line is a header and whether it is effectively white space. +set void SetFoldLevel=2222(int line, int level) + +# Retrieve the fold level of a line. +get int GetFoldLevel=2223(int line,) + +# Find the last child line of a header line. +get int GetLastChild=2224(int line, int level) + +# Find the parent line of a child line. +get int GetFoldParent=2225(int line,) + +# Make a range of lines visible. +fun void ShowLines=2226(int lineStart, int lineEnd) + +# Make a range of lines invisible. +fun void HideLines=2227(int lineStart, int lineEnd) + +# Is a line visible? +get bool GetLineVisible=2228(int line,) + +# Show the children of a header line. +set void SetFoldExpanded=2229(int line, bool expanded) + +# Is a header line expanded? +get bool GetFoldExpanded=2230(int line,) + +# Switch a header line between expanded and contracted. +fun void ToggleFold=2231(int line,) + +# Ensure a particular line is visible by expanding any header line hiding it. +fun void EnsureVisible=2232(int line,) + +enu FoldFlag=SC_FOLDFLAG_ +val SC_FOLDFLAG_LINEBEFORE_EXPANDED=0x0002 +val SC_FOLDFLAG_LINEBEFORE_CONTRACTED=0x0004 +val SC_FOLDFLAG_LINEAFTER_EXPANDED=0x0008 +val SC_FOLDFLAG_LINEAFTER_CONTRACTED=0x0010 +val SC_FOLDFLAG_LEVELNUMBERS=0x0040 +val SC_FOLDFLAG_BOX=0x0001 + +# Set some style options for folding. +fun void SetFoldFlags=2233(int flags,) + +# Ensure a particular line is visible by expanding any header line hiding it. +# Use the currently set visibility policy to determine which range to display. +fun void EnsureVisibleEnforcePolicy=2234(int line,) + +# Sets whether a tab pressed when caret is within indentation indents. +set void SetTabIndents=2260(bool tabIndents,) + +# Does a tab pressed when caret is within indentation indent? +get bool GetTabIndents=2261(,) + +# Sets whether a backspace pressed when caret is within indentation unindents. +set void SetBackSpaceUnIndents=2262(bool bsUnIndents,) + +# Does a backspace pressed when caret is within indentation unindent? +get bool GetBackSpaceUnIndents=2263(,) + +val SC_TIME_FOREVER=10000000 + +# Sets the time the mouse must sit still to generate a mouse dwell event. +set void SetMouseDwellTime=2264(int periodMilliseconds,) + +# Retrieve the time the mouse must sit still to generate a mouse dwell event. +get int GetMouseDwellTime=2265(,) + +# Get position of start of word. +fun int WordStartPosition=2266(position pos, bool onlyWordCharacters) + +# Get position of end of word. +fun int WordEndPosition=2267(position pos, bool onlyWordCharacters) + +enu Wrap=SC_WRAP_ +val SC_WRAP_NONE=0 +val SC_WRAP_WORD=1 +val SC_WRAP_CHAR=2 + +# Sets whether text is word wrapped. +set void SetWrapMode=2268(int mode,) + +# Retrieve whether text is word wrapped. +get int GetWrapMode=2269(,) + +enu WrapVisualFlag=SC_WRAPVISUALFLAG_ +val SC_WRAPVISUALFLAG_NONE=0x0000 +val SC_WRAPVISUALFLAG_END=0x0001 +val SC_WRAPVISUALFLAG_START=0x0002 + +# Set the display mode of visual flags for wrapped lines. +set void SetWrapVisualFlags=2460(int wrapVisualFlags,) + +# Retrive the display mode of visual flags for wrapped lines. +get int GetWrapVisualFlags=2461(,) + +enu WrapVisualLocation=SC_WRAPVISUALFLAGLOC_ +val SC_WRAPVISUALFLAGLOC_DEFAULT=0x0000 +val SC_WRAPVISUALFLAGLOC_END_BY_TEXT=0x0001 +val SC_WRAPVISUALFLAGLOC_START_BY_TEXT=0x0002 + +# Set the location of visual flags for wrapped lines. +set void SetWrapVisualFlagsLocation=2462(int wrapVisualFlagsLocation,) + +# Retrive the location of visual flags for wrapped lines. +get int GetWrapVisualFlagsLocation=2463(,) + +# Set the start indent for wrapped lines. +set void SetWrapStartIndent=2464(int indent,) + +# Retrive the start indent for wrapped lines. +get int GetWrapStartIndent=2465(,) + +enu LineCache=SC_CACHE_ +val SC_CACHE_NONE=0 +val SC_CACHE_CARET=1 +val SC_CACHE_PAGE=2 +val SC_CACHE_DOCUMENT=3 + +# Sets the degree of caching of layout information. +set void SetLayoutCache=2272(int mode,) + +# Retrieve the degree of caching of layout information. +get int GetLayoutCache=2273(,) + +# Sets the document width assumed for scrolling. +set void SetScrollWidth=2274(int pixelWidth,) + +# Retrieve the document width assumed for scrolling. +get int GetScrollWidth=2275(,) + +# Measure the pixel width of some text in a particular style. +# NUL terminated text argument. +# Does not handle tab or control characters. +fun int TextWidth=2276(int style, string text) + +# Sets the scroll range so that maximum scroll position has +# the last line at the bottom of the view (default). +# Setting this to false allows scrolling one page below the last line. +set void SetEndAtLastLine=2277(bool endAtLastLine,) + +# Retrieve whether the maximum scroll position has the last +# line at the bottom of the view. +get bool GetEndAtLastLine=2278(,) + +# Retrieve the height of a particular line of text in pixels. +fun int TextHeight=2279(int line,) + +# Show or hide the vertical scroll bar. +set void SetVScrollBar=2280(bool show,) + +# Is the vertical scroll bar visible? +get bool GetVScrollBar=2281(,) + +# Append a string to the end of the document without changing the selection. +fun void AppendText=2282(int length, string text) + +# Is drawing done in two phases with backgrounds drawn before faoregrounds? +get bool GetTwoPhaseDraw=2283(,) + +# In twoPhaseDraw mode, drawing is performed in two phases, first the background +# and then the foreground. This avoids chopping off characters that overlap the next run. +set void SetTwoPhaseDraw=2284(bool twoPhase,) + +# Make the target range start and end be the same as the selection range start and end. +fun void TargetFromSelection=2287(,) + +# Join the lines in the target. +fun void LinesJoin=2288(,) + +# Split the lines in the target into lines that are less wide than pixelWidth +# where possible. +fun void LinesSplit=2289(int pixelWidth,) + +# Set the colours used as a chequerboard pattern in the fold margin +fun void SetFoldMarginColour=2290(bool useSetting, colour back) +fun void SetFoldMarginHiColour=2291(bool useSetting, colour fore) + +## New messages go here + +## Start of key messages +# Move caret down one line. +fun void LineDown=2300(,) + +# Move caret down one line extending selection to new caret position. +fun void LineDownExtend=2301(,) + +# Move caret up one line. +fun void LineUp=2302(,) + +# Move caret up one line extending selection to new caret position. +fun void LineUpExtend=2303(,) + +# Move caret left one character. +fun void CharLeft=2304(,) + +# Move caret left one character extending selection to new caret position. +fun void CharLeftExtend=2305(,) + +# Move caret right one character. +fun void CharRight=2306(,) + +# Move caret right one character extending selection to new caret position. +fun void CharRightExtend=2307(,) + +# Move caret left one word. +fun void WordLeft=2308(,) + +# Move caret left one word extending selection to new caret position. +fun void WordLeftExtend=2309(,) + +# Move caret right one word. +fun void WordRight=2310(,) + +# Move caret right one word extending selection to new caret position. +fun void WordRightExtend=2311(,) + +# Move caret to first position on line. +fun void Home=2312(,) + +# Move caret to first position on line extending selection to new caret position. +fun void HomeExtend=2313(,) + +# Move caret to last position on line. +fun void LineEnd=2314(,) + +# Move caret to last position on line extending selection to new caret position. +fun void LineEndExtend=2315(,) + +# Move caret to first position in document. +fun void DocumentStart=2316(,) + +# Move caret to first position in document extending selection to new caret position. +fun void DocumentStartExtend=2317(,) + +# Move caret to last position in document. +fun void DocumentEnd=2318(,) + +# Move caret to last position in document extending selection to new caret position. +fun void DocumentEndExtend=2319(,) + +# Move caret one page up. +fun void PageUp=2320(,) + +# Move caret one page up extending selection to new caret position. +fun void PageUpExtend=2321(,) + +# Move caret one page down. +fun void PageDown=2322(,) + +# Move caret one page down extending selection to new caret position. +fun void PageDownExtend=2323(,) + +# Switch from insert to overtype mode or the reverse. +fun void EditToggleOvertype=2324(,) + +# Cancel any modes such as call tip or auto-completion list display. +fun void Cancel=2325(,) + +# Delete the selection or if no selection, the character before the caret. +fun void DeleteBack=2326(,) + +# If selection is empty or all on one line replace the selection with a tab character. +# If more than one line selected, indent the lines. +fun void Tab=2327(,) + +# Dedent the selected lines. +fun void BackTab=2328(,) + +# Insert a new line, may use a CRLF, CR or LF depending on EOL mode. +fun void NewLine=2329(,) + +# Insert a Form Feed character. +fun void FormFeed=2330(,) + +# Move caret to before first visible character on line. +# If already there move to first character on line. +fun void VCHome=2331(,) + +# Like VCHome but extending selection to new caret position. +fun void VCHomeExtend=2332(,) + +# Magnify the displayed text by increasing the sizes by 1 point. +fun void ZoomIn=2333(,) + +# Make the displayed text smaller by decreasing the sizes by 1 point. +fun void ZoomOut=2334(,) + +# Delete the word to the left of the caret. +fun void DelWordLeft=2335(,) + +# Delete the word to the right of the caret. +fun void DelWordRight=2336(,) + +# Cut the line containing the caret. +fun void LineCut=2337(,) + +# Delete the line containing the caret. +fun void LineDelete=2338(,) + +# Switch the current line with the previous. +fun void LineTranspose=2339(,) + +# Duplicate the current line. +fun void LineDuplicate=2404(,) + +# Transform the selection to lower case. +fun void LowerCase=2340(,) + +# Transform the selection to upper case. +fun void UpperCase=2341(,) + +# Scroll the document down, keeping the caret visible. +fun void LineScrollDown=2342(,) + +# Scroll the document up, keeping the caret visible. +fun void LineScrollUp=2343(,) + +# Delete the selection or if no selection, the character before the caret. +# Will not delete the character before at the start of a line. +fun void DeleteBackNotLine=2344(,) + +# Move caret to first position on display line. +fun void HomeDisplay=2345(,) + +# Move caret to first position on display line extending selection to +# new caret position. +fun void HomeDisplayExtend=2346(,) + +# Move caret to last position on display line. +fun void LineEndDisplay=2347(,) + +# Move caret to last position on display line extending selection to new +# caret position. +fun void LineEndDisplayExtend=2348(,) + +# These are like their namesakes Home(Extend)?, LineEnd(Extend)?, VCHome(Extend)? +# except they behave differently when word-wrap is enabled: +# They go first to the start / end of the display line, like (Home|LineEnd)Display +# The difference is that, the cursor is already at the point, it goes on to the start +# or end of the document line, as appropriate for (Home|LineEnd|VCHome)(Extend)?. + +fun void HomeWrap=2349(,) +fun void HomeWrapExtend=2450(,) +fun void LineEndWrap=2451(,) +fun void LineEndWrapExtend=2452(,) +fun void VCHomeWrap=2453(,) +fun void VCHomeWrapExtend=2454(,) + +# Copy the line containing the caret. +fun void LineCopy=2455(,) + +# Move the caret inside current view if it's not there already. +fun void MoveCaretInsideView=2401(,) + +# How many characters are on a line, not including end of line characters? +fun int LineLength=2350(int line,) + +# Highlight the characters at two positions. +fun void BraceHighlight=2351(position pos1, position pos2) + +# Highlight the character at a position indicating there is no matching brace. +fun void BraceBadLight=2352(position pos,) + +# Find the position of a matching brace or INVALID_POSITION if no match. +fun position BraceMatch=2353(position pos,) + +# Are the end of line characters visible? +get bool GetViewEOL=2355(,) + +# Make the end of line characters visible or invisible. +set void SetViewEOL=2356(bool visible,) + +# Retrieve a pointer to the document object. +get int GetDocPointer=2357(,) + +# Change the document object used. +set void SetDocPointer=2358(, int pointer) + +# Set which document modification events are sent to the container. +set void SetModEventMask=2359(int mask,) + +enu EdgeVisualStyle=EDGE_ +val EDGE_NONE=0 +val EDGE_LINE=1 +val EDGE_BACKGROUND=2 + +# Retrieve the column number which text should be kept within. +get int GetEdgeColumn=2360(,) + +# Set the column number of the edge. +# If text goes past the edge then it is highlighted. +set void SetEdgeColumn=2361(int column,) + +# Retrieve the edge highlight mode. +get int GetEdgeMode=2362(,) + +# The edge may be displayed by a line (EDGE_LINE) or by highlighting text that +# goes beyond it (EDGE_BACKGROUND) or not displayed at all (EDGE_NONE). +set void SetEdgeMode=2363(int mode,) + +# Retrieve the colour used in edge indication. +get colour GetEdgeColour=2364(,) + +# Change the colour used in edge indication. +set void SetEdgeColour=2365(colour edgeColour,) + +# Sets the current caret position to be the search anchor. +fun void SearchAnchor=2366(,) + +# Find some text starting at the search anchor. +# Does not ensure the selection is visible. +fun int SearchNext=2367(int flags, string text) + +# Find some text starting at the search anchor and moving backwards. +# Does not ensure the selection is visible. +fun int SearchPrev=2368(int flags, string text) + +# Retrieves the number of lines completely visible. +get int LinesOnScreen=2370(,) + +# Set whether a pop up menu is displayed automatically when the user presses +# the wrong mouse button. +fun void UsePopUp=2371(bool allowPopUp,) + +# Is the selection rectangular? The alternative is the more common stream selection. +get bool SelectionIsRectangle=2372(,) + +# Set the zoom level. This number of points is added to the size of all fonts. +# It may be positive to magnify or negative to reduce. +set void SetZoom=2373(int zoom,) +# Retrieve the zoom level. +get int GetZoom=2374(,) + +# Create a new document object. +# Starts with reference count of 1 and not selected into editor. +fun int CreateDocument=2375(,) +# Extend life of document. +fun void AddRefDocument=2376(, int doc) +# Release a reference to the document, deleting document if it fades to black. +fun void ReleaseDocument=2377(, int doc) + +# Get which document modification events are sent to the container. +get int GetModEventMask=2378(,) + +# Change internal focus flag. +set void SetFocus=2380(bool focus,) +# Get internal focus flag. +get bool GetFocus=2381(,) + +# Change error status - 0 = OK. +set void SetStatus=2382(int statusCode,) +# Get error status. +get int GetStatus=2383(,) + +# Set whether the mouse is captured when its button is pressed. +set void SetMouseDownCaptures=2384(bool captures,) +# Get whether mouse gets captured. +get bool GetMouseDownCaptures=2385(,) + +enu CursorShape=SC_CURSOR +val SC_CURSORNORMAL=-1 +val SC_CURSORWAIT=4 +# Sets the cursor to one of the SC_CURSOR* values. +set void SetCursor=2386(int cursorType,) +# Get cursor type. +get int GetCursor=2387(,) + +# Change the way control characters are displayed: +# If symbol is < 32, keep the drawn way, else, use the given character. +set void SetControlCharSymbol=2388(int symbol,) +# Get the way control characters are displayed. +get int GetControlCharSymbol=2389(,) + +# Move to the previous change in capitalisation. +fun void WordPartLeft=2390(,) +# Move to the previous change in capitalisation extending selection +# to new caret position. +fun void WordPartLeftExtend=2391(,) +# Move to the change next in capitalisation. +fun void WordPartRight=2392(,) +# Move to the next change in capitalisation extending selection +# to new caret position. +fun void WordPartRightExtend=2393(,) + +# Constants for use with SetVisiblePolicy, similar to SetCaretPolicy. +val VISIBLE_SLOP=0x01 +val VISIBLE_STRICT=0x04 +# Set the way the display area is determined when a particular line +# is to be moved to by Find, FindNext, GotoLine, etc. +fun void SetVisiblePolicy=2394(int visiblePolicy, int visibleSlop) + +# Delete back from the current position to the start of the line. +fun void DelLineLeft=2395(,) + +# Delete forwards from the current position to the end of the line. +fun void DelLineRight=2396(,) + +# Get and Set the xOffset (ie, horizonal scroll position). +set void SetXOffset=2397(int newOffset,) +get int GetXOffset=2398(,) + +# Set the last x chosen value to be the caret x position. +fun void ChooseCaretX=2399(,) + +# Set the focus to this Scintilla widget. +fun void GrabFocus=2400(,) + +enu CaretPolicy = CARET_ +# Caret policy, used by SetXCaretPolicy and SetYCaretPolicy. +# If CARET_SLOP is set, we can define a slop value: caretSlop. +# This value defines an unwanted zone (UZ) where the caret is... unwanted. +# This zone is defined as a number of pixels near the vertical margins, +# and as a number of lines near the horizontal margins. +# By keeping the caret away from the edges, it is seen within its context, +# so it is likely that the identifier that the caret is on can be completely seen, +# and that the current line is seen with some of the lines following it which are +# often dependent on that line. +val CARET_SLOP=0x01 +# If CARET_STRICT is set, the policy is enforced... strictly. +# The caret is centred on the display if slop is not set, +# and cannot go in the UZ if slop is set. +val CARET_STRICT=0x04 +# If CARET_JUMPS is set, the display is moved more energetically +# so the caret can move in the same direction longer before the policy is applied again. +val CARET_JUMPS=0x10 +# If CARET_EVEN is not set, instead of having symmetrical UZs, +# the left and bottom UZs are extended up to right and top UZs respectively. +# This way, we favour the displaying of useful information: the begining of lines, +# where most code reside, and the lines after the caret, eg. the body of a function. +val CARET_EVEN=0x08 + +# Set the way the caret is kept visible when going sideway. +# The exclusion zone is given in pixels. +fun void SetXCaretPolicy=2402(int caretPolicy, int caretSlop) + +# Set the way the line the caret is on is kept visible. +# The exclusion zone is given in lines. +fun void SetYCaretPolicy=2403(int caretPolicy, int caretSlop) + +# Set printing to line wrapped (SC_WRAP_WORD) or not line wrapped (SC_WRAP_NONE). +set void SetPrintWrapMode=2406(int mode,) + +# Is printing line wrapped? +get int GetPrintWrapMode=2407(,) + +# Set a fore colour for active hotspots. +set void SetHotspotActiveFore=2410(bool useSetting, colour fore) + +# Set a back colour for active hotspots. +set void SetHotspotActiveBack=2411(bool useSetting, colour back) + +# Enable / Disable underlining active hotspots. +set void SetHotspotActiveUnderline=2412(bool underline,) + +# Limit hotspots to single line so hotspots on two lines don't merge. +set void SetHotspotSingleLine=2421(bool singleLine,) + +# Move caret between paragraphs (delimited by empty lines). +fun void ParaDown=2413(,) +fun void ParaDownExtend=2414(,) +fun void ParaUp=2415(,) +fun void ParaUpExtend=2416(,) + +# Given a valid document position, return the previous position taking code +# page into account. Returns 0 if passed 0. +fun position PositionBefore=2417(position pos,) + +# Given a valid document position, return the next position taking code +# page into account. Maximum value returned is the last position in the document. +fun position PositionAfter=2418(position pos,) + +# Copy a range of text to the clipboard. Positions are clipped into the document. +fun void CopyRange=2419(position start, position end) + +# Copy argument text to the clipboard. +fun void CopyText=2420(int length, string text) + +enu SelectionMode=SC_SEL_ +val SC_SEL_STREAM=0 +val SC_SEL_RECTANGLE=1 +val SC_SEL_LINES=2 + +# Set the selection mode to stream (SC_SEL_STREAM) or rectangular (SC_SEL_RECTANGLE) or +# by lines (SC_SEL_LINES). +set void SetSelectionMode=2422(int mode,) + +# Get the mode of the current selection. +get int GetSelectionMode=2423(,) + +# Retrieve the position of the start of the selection at the given line (INVALID_POSITION if no selection on this line). +fun position GetLineSelStartPosition=2424(int line,) + +# Retrieve the position of the end of the selection at the given line (INVALID_POSITION if no selection on this line). +fun position GetLineSelEndPosition=2425(int line,) + +## RectExtended rectangular selection moves +# Move caret down one line, extending rectangular selection to new caret position. +fun void LineDownRectExtend=2426(,) + +# Move caret up one line, extending rectangular selection to new caret position. +fun void LineUpRectExtend=2427(,) + +# Move caret left one character, extending rectangular selection to new caret position. +fun void CharLeftRectExtend=2428(,) + +# Move caret right one character, extending rectangular selection to new caret position. +fun void CharRightRectExtend=2429(,) + +# Move caret to first position on line, extending rectangular selection to new caret position. +fun void HomeRectExtend=2430(,) + +# Move caret to before first visible character on line. +# If already there move to first character on line. +# In either case, extend rectangular selection to new caret position. +fun void VCHomeRectExtend=2431(,) + +# Move caret to last position on line, extending rectangular selection to new caret position. +fun void LineEndRectExtend=2432(,) + +# Move caret one page up, extending rectangular selection to new caret position. +fun void PageUpRectExtend=2433(,) + +# Move caret one page down, extending rectangular selection to new caret position. +fun void PageDownRectExtend=2434(,) + + +# Move caret to top of page, or one page up if already at top of page. +fun void StutteredPageUp=2435(,) + +# Move caret to top of page, or one page up if already at top of page, extending selection to new caret position. +fun void StutteredPageUpExtend=2436(,) + +# Move caret to bottom of page, or one page down if already at bottom of page. +fun void StutteredPageDown=2437(,) + +# Move caret to bottom of page, or one page down if already at bottom of page, extending selection to new caret position. +fun void StutteredPageDownExtend=2438(,) + + +# Move caret left one word, position cursor at end of word. +fun void WordLeftEnd=2439(,) + +# Move caret left one word, position cursor at end of word, extending selection to new caret position. +fun void WordLeftEndExtend=2440(,) + +# Move caret right one word, position cursor at end of word. +fun void WordRightEnd=2441(,) + +# Move caret right one word, position cursor at end of word, extending selection to new caret position. +fun void WordRightEndExtend=2442(,) + +# Set the set of characters making up whitespace for when moving or selecting by word. +# Should be called after SetWordChars. +set void SetWhitespaceChars=2443(, string characters) + +# Reset the set of characters for whitespace and word characters to the defaults. +fun void SetCharsDefault=2444(,) + +# Get currently selected item position in the auto-completion list +fun int AutoCGetCurrent=2445(,) + +# Enlarge the document to a particular size of text bytes. +fun void Allocate=2446(int bytes,) + +# Returns the target converted to UTF8. +# Return the length in bytes. +fun int TargetAsUTF8=2447(, stringresult s) + +# Set the length of the utf8 argument for calling EncodedFromUTF8. +# Set to -1 and the string will be measured to the first nul. +fun void SetLengthForEncode=2448(int bytes,) + +# Translates a UTF8 string into the document encoding. +# Return the length of the result in bytes. +# On error return 0. +fun int EncodedFromUTF8=2449(string utf8, stringresult encoded) + +# Find the position of a column on a line taking into account tabs and +# multi-byte characters. If beyond end of line, return line end position. +fun int FindColumn=2456(int line, int column) + +# Can the caret preferred x position only be changed by explicit movement commands? +get bool GetCaretSticky=2457(,) + +# Stop the caret preferred x position changing when the user types. +set void SetCaretSticky=2458(bool useCaretStickyBehaviour,) + +# Switch between sticky and non-sticky: meant to be bound to a key. +fun void ToggleCaretSticky=2459(,) + +# Enable/Disable convert-on-paste for line endings +set void SetPasteConvertEndings=2467(bool convert,) + +# Get convert-on-paste setting +get bool GetPasteConvertEndings=2468(,) + +# Duplicate the selection. If selection empty duplicate the line containing the caret. +fun void SelectionDuplicate=2469(,) + +val SC_ALPHA_TRANSPARENT=0 +val SC_ALPHA_OPAQUE=255 +val SC_ALPHA_NOALPHA=256 + +# Set background alpha of the caret line. +set void SetCaretLineBackAlpha=2470(int alpha,) + +# Get the background alpha of the caret line. +get int GetCaretLineBackAlpha=2471(,) + +# Start notifying the container of all key presses and commands. +fun void StartRecord=3001(,) + +# Stop notifying the container of all key presses and commands. +fun void StopRecord=3002(,) + +# Set the lexing language of the document. +set void SetLexer=4001(int lexer,) + +# Retrieve the lexing language of the document. +get int GetLexer=4002(,) + +# Colourise a segment of the document using the current lexing language. +fun void Colourise=4003(position start, position end) + +# Set up a value that may be used by a lexer for some optional feature. +set void SetProperty=4004(string key, string value) + +# Maximum value of keywordSet parameter of SetKeyWords. +val KEYWORDSET_MAX=8 + +# Set up the key words used by the lexer. +set void SetKeyWords=4005(int keywordSet, string keyWords) + +# Set the lexing language of the document based on string name. +set void SetLexerLanguage=4006(, string language) + +# Load a lexer library (dll / so). +fun void LoadLexerLibrary=4007(, string path) + +# Retrieve a "property" value previously set with SetProperty. +fun int GetProperty=4008(string key, stringresult buf) + +# Retrieve a "property" value previously set with SetProperty, +# with "$()" variable replacement on returned buffer. +fun int GetPropertyExpanded=4009(string key, stringresult buf) + +# Retrieve a "property" value previously set with SetProperty, +# interpreted as an int AFTER any "$()" variable replacement. +get int GetPropertyInt=4010(string key,) + +# Retrieve the number of bits the current lexer needs for styling. +get int GetStyleBitsNeeded=4011(,) + +# Notifications +# Type of modification and the action which caused the modification. +# These are defined as a bit mask to make it easy to specify which notifications are wanted. +# One bit is set from each of SC_MOD_* and SC_PERFORMED_*. +enu ModificationFlags=SC_MOD_ SC_PERFORMED_ SC_LAST +val SC_MOD_INSERTTEXT=0x1 +val SC_MOD_DELETETEXT=0x2 +val SC_MOD_CHANGESTYLE=0x4 +val SC_MOD_CHANGEFOLD=0x8 +val SC_PERFORMED_USER=0x10 +val SC_PERFORMED_UNDO=0x20 +val SC_PERFORMED_REDO=0x40 +val SC_MULTISTEPUNDOREDO=0x80 +val SC_LASTSTEPINUNDOREDO=0x100 +val SC_MOD_CHANGEMARKER=0x200 +val SC_MOD_BEFOREINSERT=0x400 +val SC_MOD_BEFOREDELETE=0x800 +val SC_MULTILINEUNDOREDO=0x1000 +val SC_MODEVENTMASKALL=0x1FFF + +# For compatibility, these go through the COMMAND notification rather than NOTIFY +# and should have had exactly the same values as the EN_* constants. +# Unfortunately the SETFOCUS and KILLFOCUS are flipped over from EN_* +# As clients depend on these constants, this will not be changed. +val SCEN_CHANGE=768 +val SCEN_SETFOCUS=512 +val SCEN_KILLFOCUS=256 + +# Symbolic key codes and modifier flags. +# ASCII and other printable characters below 256. +# Extended keys above 300. + +enu Keys=SCK_ +val SCK_DOWN=300 +val SCK_UP=301 +val SCK_LEFT=302 +val SCK_RIGHT=303 +val SCK_HOME=304 +val SCK_END=305 +val SCK_PRIOR=306 +val SCK_NEXT=307 +val SCK_DELETE=308 +val SCK_INSERT=309 +val SCK_ESCAPE=7 +val SCK_BACK=8 +val SCK_TAB=9 +val SCK_RETURN=13 +val SCK_ADD=310 +val SCK_SUBTRACT=311 +val SCK_DIVIDE=312 + +enu KeyMod=SCMOD_ +val SCMOD_NORM=0 +val SCMOD_SHIFT=1 +val SCMOD_CTRL=2 +val SCMOD_ALT=4 + +################################################ +# For SciLexer.h +enu Lexer=SCLEX_ +val SCLEX_CONTAINER=0 +val SCLEX_NULL=1 +val SCLEX_PYTHON=2 +val SCLEX_CPP=3 +val SCLEX_HTML=4 +val SCLEX_XML=5 +val SCLEX_PERL=6 +val SCLEX_SQL=7 +val SCLEX_VB=8 +val SCLEX_PROPERTIES=9 +val SCLEX_ERRORLIST=10 +val SCLEX_MAKEFILE=11 +val SCLEX_BATCH=12 +val SCLEX_XCODE=13 +val SCLEX_LATEX=14 +val SCLEX_LUA=15 +val SCLEX_DIFF=16 +val SCLEX_CONF=17 +val SCLEX_PASCAL=18 +val SCLEX_AVE=19 +val SCLEX_ADA=20 +val SCLEX_LISP=21 +val SCLEX_RUBY=22 +val SCLEX_EIFFEL=23 +val SCLEX_EIFFELKW=24 +val SCLEX_TCL=25 +val SCLEX_NNCRONTAB=26 +val SCLEX_BULLANT=27 +val SCLEX_VBSCRIPT=28 +val SCLEX_BAAN=31 +val SCLEX_MATLAB=32 +val SCLEX_SCRIPTOL=33 +val SCLEX_ASM=34 +val SCLEX_CPPNOCASE=35 +val SCLEX_FORTRAN=36 +val SCLEX_F77=37 +val SCLEX_CSS=38 +val SCLEX_POV=39 +val SCLEX_LOUT=40 +val SCLEX_ESCRIPT=41 +val SCLEX_PS=42 +val SCLEX_NSIS=43 +val SCLEX_MMIXAL=44 +val SCLEX_CLW=45 +val SCLEX_CLWNOCASE=46 +val SCLEX_LOT=47 +val SCLEX_YAML=48 +val SCLEX_TEX=49 +val SCLEX_METAPOST=50 +val SCLEX_POWERBASIC=51 +val SCLEX_FORTH=52 +val SCLEX_ERLANG=53 +val SCLEX_OCTAVE=54 +val SCLEX_MSSQL=55 +val SCLEX_VERILOG=56 +val SCLEX_KIX=57 +val SCLEX_GUI4CLI=58 +val SCLEX_SPECMAN=59 +val SCLEX_AU3=60 +val SCLEX_APDL=61 +val SCLEX_BASH=62 +val SCLEX_ASN1=63 +val SCLEX_VHDL=64 +val SCLEX_CAML=65 +val SCLEX_BLITZBASIC=66 +val SCLEX_PUREBASIC=67 +val SCLEX_HASKELL=68 +val SCLEX_PHPSCRIPT=69 +val SCLEX_TADS3=70 +val SCLEX_REBOL=71 +val SCLEX_SMALLTALK=72 +val SCLEX_FLAGSHIP=73 +val SCLEX_CSOUND=74 +val SCLEX_FREEBASIC=75 +val SCLEX_INNOSETUP=76 +val SCLEX_OPAL=77 +val SCLEX_SPICE=78 + +# When a lexer specifies its language as SCLEX_AUTOMATIC it receives a +# value assigned in sequence from SCLEX_AUTOMATIC+1. +val SCLEX_AUTOMATIC=1000 +# Lexical states for SCLEX_PYTHON +lex Python=SCLEX_PYTHON SCE_P_ +val SCE_P_DEFAULT=0 +val SCE_P_COMMENTLINE=1 +val SCE_P_NUMBER=2 +val SCE_P_STRING=3 +val SCE_P_CHARACTER=4 +val SCE_P_WORD=5 +val SCE_P_TRIPLE=6 +val SCE_P_TRIPLEDOUBLE=7 +val SCE_P_CLASSNAME=8 +val SCE_P_DEFNAME=9 +val SCE_P_OPERATOR=10 +val SCE_P_IDENTIFIER=11 +val SCE_P_COMMENTBLOCK=12 +val SCE_P_STRINGEOL=13 +val SCE_P_WORD2=14 +val SCE_P_DECORATOR=15 +# Lexical states for SCLEX_CPP +lex Cpp=SCLEX_CPP SCE_C_ +lex Pascal=SCLEX_PASCAL SCE_C_ +lex BullAnt=SCLEX_BULLANT SCE_C_ +val SCE_C_DEFAULT=0 +val SCE_C_COMMENT=1 +val SCE_C_COMMENTLINE=2 +val SCE_C_COMMENTDOC=3 +val SCE_C_NUMBER=4 +val SCE_C_WORD=5 +val SCE_C_STRING=6 +val SCE_C_CHARACTER=7 +val SCE_C_UUID=8 +val SCE_C_PREPROCESSOR=9 +val SCE_C_OPERATOR=10 +val SCE_C_IDENTIFIER=11 +val SCE_C_STRINGEOL=12 +val SCE_C_VERBATIM=13 +val SCE_C_REGEX=14 +val SCE_C_COMMENTLINEDOC=15 +val SCE_C_WORD2=16 +val SCE_C_COMMENTDOCKEYWORD=17 +val SCE_C_COMMENTDOCKEYWORDERROR=18 +val SCE_C_GLOBALCLASS=19 +# Lexical states for SCLEX_TCL +lex TCL=SCLEX_TCL SCE_TCL_ +val SCE_TCL_DEFAULT=0 +val SCE_TCL_COMMENT=1 +val SCE_TCL_COMMENTLINE=2 +val SCE_TCL_NUMBER=3 +val SCE_TCL_WORD_IN_QUOTE=4 +val SCE_TCL_IN_QUOTE=5 +val SCE_TCL_OPERATOR=6 +val SCE_TCL_IDENTIFIER=7 +val SCE_TCL_SUBSTITUTION=8 +val SCE_TCL_SUB_BRACE=9 +val SCE_TCL_MODIFIER=10 +val SCE_TCL_EXPAND=11 +val SCE_TCL_WORD=12 +val SCE_TCL_WORD2=13 +val SCE_TCL_WORD3=14 +val SCE_TCL_WORD4=15 +val SCE_TCL_WORD5=16 +val SCE_TCL_WORD6=17 +val SCE_TCL_WORD7=18 +val SCE_TCL_WORD8=19 +val SCE_TCL_COMMENT_BOX=20 +val SCE_TCL_BLOCK_COMMENT=21 +# Lexical states for SCLEX_HTML, SCLEX_XML +lex HTML=SCLEX_HTML SCE_H +lex XML=SCLEX_XML SCE_H +lex ASP=SCLEX_ASP SCE_H +lex PHP=SCLEX_PHP SCE_H +val SCE_H_DEFAULT=0 +val SCE_H_TAG=1 +val SCE_H_TAGUNKNOWN=2 +val SCE_H_ATTRIBUTE=3 +val SCE_H_ATTRIBUTEUNKNOWN=4 +val SCE_H_NUMBER=5 +val SCE_H_DOUBLESTRING=6 +val SCE_H_SINGLESTRING=7 +val SCE_H_OTHER=8 +val SCE_H_COMMENT=9 +val SCE_H_ENTITY=10 +# XML and ASP +val SCE_H_TAGEND=11 +val SCE_H_XMLSTART=12 +val SCE_H_XMLEND=13 +val SCE_H_SCRIPT=14 +val SCE_H_ASP=15 +val SCE_H_ASPAT=16 +val SCE_H_CDATA=17 +val SCE_H_QUESTION=18 +# More HTML +val SCE_H_VALUE=19 +# X-Code +val SCE_H_XCCOMMENT=20 +# SGML +val SCE_H_SGML_DEFAULT=21 +val SCE_H_SGML_COMMAND=22 +val SCE_H_SGML_1ST_PARAM=23 +val SCE_H_SGML_DOUBLESTRING=24 +val SCE_H_SGML_SIMPLESTRING=25 +val SCE_H_SGML_ERROR=26 +val SCE_H_SGML_SPECIAL=27 +val SCE_H_SGML_ENTITY=28 +val SCE_H_SGML_COMMENT=29 +val SCE_H_SGML_1ST_PARAM_COMMENT=30 +val SCE_H_SGML_BLOCK_DEFAULT=31 +# Embedded Javascript +val SCE_HJ_START=40 +val SCE_HJ_DEFAULT=41 +val SCE_HJ_COMMENT=42 +val SCE_HJ_COMMENTLINE=43 +val SCE_HJ_COMMENTDOC=44 +val SCE_HJ_NUMBER=45 +val SCE_HJ_WORD=46 +val SCE_HJ_KEYWORD=47 +val SCE_HJ_DOUBLESTRING=48 +val SCE_HJ_SINGLESTRING=49 +val SCE_HJ_SYMBOLS=50 +val SCE_HJ_STRINGEOL=51 +val SCE_HJ_REGEX=52 +# ASP Javascript +val SCE_HJA_START=55 +val SCE_HJA_DEFAULT=56 +val SCE_HJA_COMMENT=57 +val SCE_HJA_COMMENTLINE=58 +val SCE_HJA_COMMENTDOC=59 +val SCE_HJA_NUMBER=60 +val SCE_HJA_WORD=61 +val SCE_HJA_KEYWORD=62 +val SCE_HJA_DOUBLESTRING=63 +val SCE_HJA_SINGLESTRING=64 +val SCE_HJA_SYMBOLS=65 +val SCE_HJA_STRINGEOL=66 +val SCE_HJA_REGEX=67 +# Embedded VBScript +val SCE_HB_START=70 +val SCE_HB_DEFAULT=71 +val SCE_HB_COMMENTLINE=72 +val SCE_HB_NUMBER=73 +val SCE_HB_WORD=74 +val SCE_HB_STRING=75 +val SCE_HB_IDENTIFIER=76 +val SCE_HB_STRINGEOL=77 +# ASP VBScript +val SCE_HBA_START=80 +val SCE_HBA_DEFAULT=81 +val SCE_HBA_COMMENTLINE=82 +val SCE_HBA_NUMBER=83 +val SCE_HBA_WORD=84 +val SCE_HBA_STRING=85 +val SCE_HBA_IDENTIFIER=86 +val SCE_HBA_STRINGEOL=87 +# Embedded Python +val SCE_HP_START=90 +val SCE_HP_DEFAULT=91 +val SCE_HP_COMMENTLINE=92 +val SCE_HP_NUMBER=93 +val SCE_HP_STRING=94 +val SCE_HP_CHARACTER=95 +val SCE_HP_WORD=96 +val SCE_HP_TRIPLE=97 +val SCE_HP_TRIPLEDOUBLE=98 +val SCE_HP_CLASSNAME=99 +val SCE_HP_DEFNAME=100 +val SCE_HP_OPERATOR=101 +val SCE_HP_IDENTIFIER=102 +# PHP +val SCE_HPHP_COMPLEX_VARIABLE=104 +# ASP Python +val SCE_HPA_START=105 +val SCE_HPA_DEFAULT=106 +val SCE_HPA_COMMENTLINE=107 +val SCE_HPA_NUMBER=108 +val SCE_HPA_STRING=109 +val SCE_HPA_CHARACTER=110 +val SCE_HPA_WORD=111 +val SCE_HPA_TRIPLE=112 +val SCE_HPA_TRIPLEDOUBLE=113 +val SCE_HPA_CLASSNAME=114 +val SCE_HPA_DEFNAME=115 +val SCE_HPA_OPERATOR=116 +val SCE_HPA_IDENTIFIER=117 +# PHP +val SCE_HPHP_DEFAULT=118 +val SCE_HPHP_HSTRING=119 +val SCE_HPHP_SIMPLESTRING=120 +val SCE_HPHP_WORD=121 +val SCE_HPHP_NUMBER=122 +val SCE_HPHP_VARIABLE=123 +val SCE_HPHP_COMMENT=124 +val SCE_HPHP_COMMENTLINE=125 +val SCE_HPHP_HSTRING_VARIABLE=126 +val SCE_HPHP_OPERATOR=127 +# Lexical states for SCLEX_PERL +lex Perl=SCLEX_PERL SCE_PL_ +val SCE_PL_DEFAULT=0 +val SCE_PL_ERROR=1 +val SCE_PL_COMMENTLINE=2 +val SCE_PL_POD=3 +val SCE_PL_NUMBER=4 +val SCE_PL_WORD=5 +val SCE_PL_STRING=6 +val SCE_PL_CHARACTER=7 +val SCE_PL_PUNCTUATION=8 +val SCE_PL_PREPROCESSOR=9 +val SCE_PL_OPERATOR=10 +val SCE_PL_IDENTIFIER=11 +val SCE_PL_SCALAR=12 +val SCE_PL_ARRAY=13 +val SCE_PL_HASH=14 +val SCE_PL_SYMBOLTABLE=15 +val SCE_PL_VARIABLE_INDEXER=16 +val SCE_PL_REGEX=17 +val SCE_PL_REGSUBST=18 +val SCE_PL_LONGQUOTE=19 +val SCE_PL_BACKTICKS=20 +val SCE_PL_DATASECTION=21 +val SCE_PL_HERE_DELIM=22 +val SCE_PL_HERE_Q=23 +val SCE_PL_HERE_QQ=24 +val SCE_PL_HERE_QX=25 +val SCE_PL_STRING_Q=26 +val SCE_PL_STRING_QQ=27 +val SCE_PL_STRING_QX=28 +val SCE_PL_STRING_QR=29 +val SCE_PL_STRING_QW=30 +val SCE_PL_POD_VERB=31 +# Lexical states for SCLEX_RUBY +lex Ruby=SCLEX_RUBY SCE_RB_ +val SCE_RB_DEFAULT=0 +val SCE_RB_ERROR=1 +val SCE_RB_COMMENTLINE=2 +val SCE_RB_POD=3 +val SCE_RB_NUMBER=4 +val SCE_RB_WORD=5 +val SCE_RB_STRING=6 +val SCE_RB_CHARACTER=7 +val SCE_RB_CLASSNAME=8 +val SCE_RB_DEFNAME=9 +val SCE_RB_OPERATOR=10 +val SCE_RB_IDENTIFIER=11 +val SCE_RB_REGEX=12 +val SCE_RB_GLOBAL=13 +val SCE_RB_SYMBOL=14 +val SCE_RB_MODULE_NAME=15 +val SCE_RB_INSTANCE_VAR=16 +val SCE_RB_CLASS_VAR=17 +val SCE_RB_BACKTICKS=18 +val SCE_RB_DATASECTION=19 +val SCE_RB_HERE_DELIM=20 +val SCE_RB_HERE_Q=21 +val SCE_RB_HERE_QQ=22 +val SCE_RB_HERE_QX=23 +val SCE_RB_STRING_Q=24 +val SCE_RB_STRING_QQ=25 +val SCE_RB_STRING_QX=26 +val SCE_RB_STRING_QR=27 +val SCE_RB_STRING_QW=28 +val SCE_RB_WORD_DEMOTED=29 +val SCE_RB_STDIN=30 +val SCE_RB_STDOUT=31 +val SCE_RB_STDERR=40 +val SCE_RB_UPPER_BOUND=41 +# Lexical states for SCLEX_VB, SCLEX_VBSCRIPT, SCLEX_POWERBASIC +lex VB=SCLEX_VB SCE_B_ +lex VBScript=SCLEX_VBSCRIPT SCE_B_ +lex PowerBasic=SCLEX_POWERBASIC SCE_B_ +val SCE_B_DEFAULT=0 +val SCE_B_COMMENT=1 +val SCE_B_NUMBER=2 +val SCE_B_KEYWORD=3 +val SCE_B_STRING=4 +val SCE_B_PREPROCESSOR=5 +val SCE_B_OPERATOR=6 +val SCE_B_IDENTIFIER=7 +val SCE_B_DATE=8 +val SCE_B_STRINGEOL=9 +val SCE_B_KEYWORD2=10 +val SCE_B_KEYWORD3=11 +val SCE_B_KEYWORD4=12 +val SCE_B_CONSTANT=13 +val SCE_B_ASM=14 +val SCE_B_LABEL=15 +val SCE_B_ERROR=16 +val SCE_B_HEXNUMBER=17 +val SCE_B_BINNUMBER=18 +# Lexical states for SCLEX_PROPERTIES +lex Properties=SCLEX_PROPERTIES SCE_PROPS_ +val SCE_PROPS_DEFAULT=0 +val SCE_PROPS_COMMENT=1 +val SCE_PROPS_SECTION=2 +val SCE_PROPS_ASSIGNMENT=3 +val SCE_PROPS_DEFVAL=4 +val SCE_PROPS_KEY=5 +# Lexical states for SCLEX_LATEX +lex LaTeX=SCLEX_LATEX SCE_L_ +val SCE_L_DEFAULT=0 +val SCE_L_COMMAND=1 +val SCE_L_TAG=2 +val SCE_L_MATH=3 +val SCE_L_COMMENT=4 +# Lexical states for SCLEX_LUA +lex Lua=SCLEX_LUA SCE_LUA_ +val SCE_LUA_DEFAULT=0 +val SCE_LUA_COMMENT=1 +val SCE_LUA_COMMENTLINE=2 +val SCE_LUA_COMMENTDOC=3 +val SCE_LUA_NUMBER=4 +val SCE_LUA_WORD=5 +val SCE_LUA_STRING=6 +val SCE_LUA_CHARACTER=7 +val SCE_LUA_LITERALSTRING=8 +val SCE_LUA_PREPROCESSOR=9 +val SCE_LUA_OPERATOR=10 +val SCE_LUA_IDENTIFIER=11 +val SCE_LUA_STRINGEOL=12 +val SCE_LUA_WORD2=13 +val SCE_LUA_WORD3=14 +val SCE_LUA_WORD4=15 +val SCE_LUA_WORD5=16 +val SCE_LUA_WORD6=17 +val SCE_LUA_WORD7=18 +val SCE_LUA_WORD8=19 +# Lexical states for SCLEX_ERRORLIST +lex ErrorList=SCLEX_ERRORLIST SCE_ERR_ +val SCE_ERR_DEFAULT=0 +val SCE_ERR_PYTHON=1 +val SCE_ERR_GCC=2 +val SCE_ERR_MS=3 +val SCE_ERR_CMD=4 +val SCE_ERR_BORLAND=5 +val SCE_ERR_PERL=6 +val SCE_ERR_NET=7 +val SCE_ERR_LUA=8 +val SCE_ERR_CTAG=9 +val SCE_ERR_DIFF_CHANGED=10 +val SCE_ERR_DIFF_ADDITION=11 +val SCE_ERR_DIFF_DELETION=12 +val SCE_ERR_DIFF_MESSAGE=13 +val SCE_ERR_PHP=14 +val SCE_ERR_ELF=15 +val SCE_ERR_IFC=16 +val SCE_ERR_IFORT=17 +val SCE_ERR_ABSF=18 +val SCE_ERR_TIDY=19 +val SCE_ERR_JAVA_STACK=20 +# Lexical states for SCLEX_BATCH +lex Batch=SCLEX_BATCH SCE_BAT_ +val SCE_BAT_DEFAULT=0 +val SCE_BAT_COMMENT=1 +val SCE_BAT_WORD=2 +val SCE_BAT_LABEL=3 +val SCE_BAT_HIDE=4 +val SCE_BAT_COMMAND=5 +val SCE_BAT_IDENTIFIER=6 +val SCE_BAT_OPERATOR=7 +# Lexical states for SCLEX_MAKEFILE +lex MakeFile=SCLEX_MAKEFILE SCE_MAKE_ +val SCE_MAKE_DEFAULT=0 +val SCE_MAKE_COMMENT=1 +val SCE_MAKE_PREPROCESSOR=2 +val SCE_MAKE_IDENTIFIER=3 +val SCE_MAKE_OPERATOR=4 +val SCE_MAKE_TARGET=5 +val SCE_MAKE_IDEOL=9 +# Lexical states for SCLEX_DIFF +lex Diff=SCLEX_DIFF SCE_DIFF_ +val SCE_DIFF_DEFAULT=0 +val SCE_DIFF_COMMENT=1 +val SCE_DIFF_COMMAND=2 +val SCE_DIFF_HEADER=3 +val SCE_DIFF_POSITION=4 +val SCE_DIFF_DELETED=5 +val SCE_DIFF_ADDED=6 +# Lexical states for SCLEX_CONF (Apache Configuration Files Lexer) +lex Conf=SCLEX_CONF SCE_CONF_ +val SCE_CONF_DEFAULT=0 +val SCE_CONF_COMMENT=1 +val SCE_CONF_NUMBER=2 +val SCE_CONF_IDENTIFIER=3 +val SCE_CONF_EXTENSION=4 +val SCE_CONF_PARAMETER=5 +val SCE_CONF_STRING=6 +val SCE_CONF_OPERATOR=7 +val SCE_CONF_IP=8 +val SCE_CONF_DIRECTIVE=9 +# Lexical states for SCLEX_AVE, Avenue +lex Avenue=SCLEX_AVE SCE_AVE_ +val SCE_AVE_DEFAULT=0 +val SCE_AVE_COMMENT=1 +val SCE_AVE_NUMBER=2 +val SCE_AVE_WORD=3 +val SCE_AVE_STRING=6 +val SCE_AVE_ENUM=7 +val SCE_AVE_STRINGEOL=8 +val SCE_AVE_IDENTIFIER=9 +val SCE_AVE_OPERATOR=10 +val SCE_AVE_WORD1=11 +val SCE_AVE_WORD2=12 +val SCE_AVE_WORD3=13 +val SCE_AVE_WORD4=14 +val SCE_AVE_WORD5=15 +val SCE_AVE_WORD6=16 +# Lexical states for SCLEX_ADA +lex Ada=SCLEX_ADA SCE_ADA_ +val SCE_ADA_DEFAULT=0 +val SCE_ADA_WORD=1 +val SCE_ADA_IDENTIFIER=2 +val SCE_ADA_NUMBER=3 +val SCE_ADA_DELIMITER=4 +val SCE_ADA_CHARACTER=5 +val SCE_ADA_CHARACTEREOL=6 +val SCE_ADA_STRING=7 +val SCE_ADA_STRINGEOL=8 +val SCE_ADA_LABEL=9 +val SCE_ADA_COMMENTLINE=10 +val SCE_ADA_ILLEGAL=11 +# Lexical states for SCLEX_BAAN +lex Baan=SCLEX_BAAN SCE_BAAN_ +val SCE_BAAN_DEFAULT=0 +val SCE_BAAN_COMMENT=1 +val SCE_BAAN_COMMENTDOC=2 +val SCE_BAAN_NUMBER=3 +val SCE_BAAN_WORD=4 +val SCE_BAAN_STRING=5 +val SCE_BAAN_PREPROCESSOR=6 +val SCE_BAAN_OPERATOR=7 +val SCE_BAAN_IDENTIFIER=8 +val SCE_BAAN_STRINGEOL=9 +val SCE_BAAN_WORD2=10 +# Lexical states for SCLEX_LISP +lex Lisp=SCLEX_LISP SCE_LISP_ +val SCE_LISP_DEFAULT=0 +val SCE_LISP_COMMENT=1 +val SCE_LISP_NUMBER=2 +val SCE_LISP_KEYWORD=3 +val SCE_LISP_KEYWORD_KW=4 +val SCE_LISP_SYMBOL=5 +val SCE_LISP_STRING=6 +val SCE_LISP_STRINGEOL=8 +val SCE_LISP_IDENTIFIER=9 +val SCE_LISP_OPERATOR=10 +val SCE_LISP_SPECIAL=11 +val SCE_LISP_MULTI_COMMENT=12 +# Lexical states for SCLEX_EIFFEL and SCLEX_EIFFELKW +lex Eiffel=SCLEX_EIFFEL SCE_EIFFEL_ +lex EiffelKW=SCLEX_EIFFELKW SCE_EIFFEL_ +val SCE_EIFFEL_DEFAULT=0 +val SCE_EIFFEL_COMMENTLINE=1 +val SCE_EIFFEL_NUMBER=2 +val SCE_EIFFEL_WORD=3 +val SCE_EIFFEL_STRING=4 +val SCE_EIFFEL_CHARACTER=5 +val SCE_EIFFEL_OPERATOR=6 +val SCE_EIFFEL_IDENTIFIER=7 +val SCE_EIFFEL_STRINGEOL=8 +# Lexical states for SCLEX_NNCRONTAB (nnCron crontab Lexer) +lex NNCronTab=SCLEX_NNCRONTAB SCE_NNCRONTAB_ +val SCE_NNCRONTAB_DEFAULT=0 +val SCE_NNCRONTAB_COMMENT=1 +val SCE_NNCRONTAB_TASK=2 +val SCE_NNCRONTAB_SECTION=3 +val SCE_NNCRONTAB_KEYWORD=4 +val SCE_NNCRONTAB_MODIFIER=5 +val SCE_NNCRONTAB_ASTERISK=6 +val SCE_NNCRONTAB_NUMBER=7 +val SCE_NNCRONTAB_STRING=8 +val SCE_NNCRONTAB_ENVIRONMENT=9 +val SCE_NNCRONTAB_IDENTIFIER=10 +# Lexical states for SCLEX_FORTH (Forth Lexer) +lex Forth=SCLEX_FORTH SCE_FORTH_ +val SCE_FORTH_DEFAULT=0 +val SCE_FORTH_COMMENT=1 +val SCE_FORTH_COMMENT_ML=2 +val SCE_FORTH_IDENTIFIER=3 +val SCE_FORTH_CONTROL=4 +val SCE_FORTH_KEYWORD=5 +val SCE_FORTH_DEFWORD=6 +val SCE_FORTH_PREWORD1=7 +val SCE_FORTH_PREWORD2=8 +val SCE_FORTH_NUMBER=9 +val SCE_FORTH_STRING=10 +val SCE_FORTH_LOCALE=11 +# Lexical states for SCLEX_MATLAB +lex MatLab=SCLEX_MATLAB SCE_MATLAB_ +val SCE_MATLAB_DEFAULT=0 +val SCE_MATLAB_COMMENT=1 +val SCE_MATLAB_COMMAND=2 +val SCE_MATLAB_NUMBER=3 +val SCE_MATLAB_KEYWORD=4 +# single quoted string +val SCE_MATLAB_STRING=5 +val SCE_MATLAB_OPERATOR=6 +val SCE_MATLAB_IDENTIFIER=7 +val SCE_MATLAB_DOUBLEQUOTESTRING=8 +# Lexical states for SCLEX_SCRIPTOL +lex Sol=SCLEX_SCRIPTOL SCE_SCRIPTOL_ +val SCE_SCRIPTOL_DEFAULT=0 +val SCE_SCRIPTOL_WHITE=1 +val SCE_SCRIPTOL_COMMENTLINE=2 +val SCE_SCRIPTOL_PERSISTENT=3 +val SCE_SCRIPTOL_CSTYLE=4 +val SCE_SCRIPTOL_COMMENTBLOCK=5 +val SCE_SCRIPTOL_NUMBER=6 +val SCE_SCRIPTOL_STRING=7 +val SCE_SCRIPTOL_CHARACTER=8 +val SCE_SCRIPTOL_STRINGEOL=9 +val SCE_SCRIPTOL_KEYWORD=10 +val SCE_SCRIPTOL_OPERATOR=11 +val SCE_SCRIPTOL_IDENTIFIER=12 +val SCE_SCRIPTOL_TRIPLE=13 +val SCE_SCRIPTOL_CLASSNAME=14 +val SCE_SCRIPTOL_PREPROCESSOR=15 +# Lexical states for SCLEX_ASM +lex Asm=SCLEX_ASM SCE_ASM_ +val SCE_ASM_DEFAULT=0 +val SCE_ASM_COMMENT=1 +val SCE_ASM_NUMBER=2 +val SCE_ASM_STRING=3 +val SCE_ASM_OPERATOR=4 +val SCE_ASM_IDENTIFIER=5 +val SCE_ASM_CPUINSTRUCTION=6 +val SCE_ASM_MATHINSTRUCTION=7 +val SCE_ASM_REGISTER=8 +val SCE_ASM_DIRECTIVE=9 +val SCE_ASM_DIRECTIVEOPERAND=10 +val SCE_ASM_COMMENTBLOCK=11 +val SCE_ASM_CHARACTER=12 +val SCE_ASM_STRINGEOL=13 +val SCE_ASM_EXTINSTRUCTION=14 +# Lexical states for SCLEX_FORTRAN +lex Fortran=SCLEX_FORTRAN SCE_F_ +lex F77=SCLEX_F77 SCE_F_ +val SCE_F_DEFAULT=0 +val SCE_F_COMMENT=1 +val SCE_F_NUMBER=2 +val SCE_F_STRING1=3 +val SCE_F_STRING2=4 +val SCE_F_STRINGEOL=5 +val SCE_F_OPERATOR=6 +val SCE_F_IDENTIFIER=7 +val SCE_F_WORD=8 +val SCE_F_WORD2=9 +val SCE_F_WORD3=10 +val SCE_F_PREPROCESSOR=11 +val SCE_F_OPERATOR2=12 +val SCE_F_LABEL=13 +val SCE_F_CONTINUATION=14 +# Lexical states for SCLEX_CSS +lex CSS=SCLEX_CSS SCE_CSS_ +val SCE_CSS_DEFAULT=0 +val SCE_CSS_TAG=1 +val SCE_CSS_CLASS=2 +val SCE_CSS_PSEUDOCLASS=3 +val SCE_CSS_UNKNOWN_PSEUDOCLASS=4 +val SCE_CSS_OPERATOR=5 +val SCE_CSS_IDENTIFIER=6 +val SCE_CSS_UNKNOWN_IDENTIFIER=7 +val SCE_CSS_VALUE=8 +val SCE_CSS_COMMENT=9 +val SCE_CSS_ID=10 +val SCE_CSS_IMPORTANT=11 +val SCE_CSS_DIRECTIVE=12 +val SCE_CSS_DOUBLESTRING=13 +val SCE_CSS_SINGLESTRING=14 +val SCE_CSS_IDENTIFIER2=15 +val SCE_CSS_ATTRIBUTE=16 +# Lexical states for SCLEX_POV +lex POV=SCLEX_POV SCE_POV_ +val SCE_POV_DEFAULT=0 +val SCE_POV_COMMENT=1 +val SCE_POV_COMMENTLINE=2 +val SCE_POV_NUMBER=3 +val SCE_POV_OPERATOR=4 +val SCE_POV_IDENTIFIER=5 +val SCE_POV_STRING=6 +val SCE_POV_STRINGEOL=7 +val SCE_POV_DIRECTIVE=8 +val SCE_POV_BADDIRECTIVE=9 +val SCE_POV_WORD2=10 +val SCE_POV_WORD3=11 +val SCE_POV_WORD4=12 +val SCE_POV_WORD5=13 +val SCE_POV_WORD6=14 +val SCE_POV_WORD7=15 +val SCE_POV_WORD8=16 +# Lexical states for SCLEX_LOUT +lex LOUT=SCLEX_LOUT SCE_LOUT_ +val SCE_LOUT_DEFAULT=0 +val SCE_LOUT_COMMENT=1 +val SCE_LOUT_NUMBER=2 +val SCE_LOUT_WORD=3 +val SCE_LOUT_WORD2=4 +val SCE_LOUT_WORD3=5 +val SCE_LOUT_WORD4=6 +val SCE_LOUT_STRING=7 +val SCE_LOUT_OPERATOR=8 +val SCE_LOUT_IDENTIFIER=9 +val SCE_LOUT_STRINGEOL=10 +# Lexical states for SCLEX_ESCRIPT +lex ESCRIPT=SCLEX_ESCRIPT SCE_ESCRIPT_ +val SCE_ESCRIPT_DEFAULT=0 +val SCE_ESCRIPT_COMMENT=1 +val SCE_ESCRIPT_COMMENTLINE=2 +val SCE_ESCRIPT_COMMENTDOC=3 +val SCE_ESCRIPT_NUMBER=4 +val SCE_ESCRIPT_WORD=5 +val SCE_ESCRIPT_STRING=6 +val SCE_ESCRIPT_OPERATOR=7 +val SCE_ESCRIPT_IDENTIFIER=8 +val SCE_ESCRIPT_BRACE=9 +val SCE_ESCRIPT_WORD2=10 +val SCE_ESCRIPT_WORD3=11 +# Lexical states for SCLEX_PS +lex PS=SCLEX_PS SCE_PS_ +val SCE_PS_DEFAULT=0 +val SCE_PS_COMMENT=1 +val SCE_PS_DSC_COMMENT=2 +val SCE_PS_DSC_VALUE=3 +val SCE_PS_NUMBER=4 +val SCE_PS_NAME=5 +val SCE_PS_KEYWORD=6 +val SCE_PS_LITERAL=7 +val SCE_PS_IMMEVAL=8 +val SCE_PS_PAREN_ARRAY=9 +val SCE_PS_PAREN_DICT=10 +val SCE_PS_PAREN_PROC=11 +val SCE_PS_TEXT=12 +val SCE_PS_HEXSTRING=13 +val SCE_PS_BASE85STRING=14 +val SCE_PS_BADSTRINGCHAR=15 +# Lexical states for SCLEX_NSIS +lex NSIS=SCLEX_NSIS SCE_NSIS_ +val SCE_NSIS_DEFAULT=0 +val SCE_NSIS_COMMENT=1 +val SCE_NSIS_STRINGDQ=2 +val SCE_NSIS_STRINGLQ=3 +val SCE_NSIS_STRINGRQ=4 +val SCE_NSIS_FUNCTION=5 +val SCE_NSIS_VARIABLE=6 +val SCE_NSIS_LABEL=7 +val SCE_NSIS_USERDEFINED=8 +val SCE_NSIS_SECTIONDEF=9 +val SCE_NSIS_SUBSECTIONDEF=10 +val SCE_NSIS_IFDEFINEDEF=11 +val SCE_NSIS_MACRODEF=12 +val SCE_NSIS_STRINGVAR=13 +val SCE_NSIS_NUMBER=14 +val SCE_NSIS_SECTIONGROUP=15 +val SCE_NSIS_PAGEEX=16 +val SCE_NSIS_FUNCTIONDEF=17 +val SCE_NSIS_COMMENTBOX=18 +# Lexical states for SCLEX_MMIXAL +lex MMIXAL=SCLEX_MMIXAL SCE_MMIXAL_ +val SCE_MMIXAL_LEADWS=0 +val SCE_MMIXAL_COMMENT=1 +val SCE_MMIXAL_LABEL=2 +val SCE_MMIXAL_OPCODE=3 +val SCE_MMIXAL_OPCODE_PRE=4 +val SCE_MMIXAL_OPCODE_VALID=5 +val SCE_MMIXAL_OPCODE_UNKNOWN=6 +val SCE_MMIXAL_OPCODE_POST=7 +val SCE_MMIXAL_OPERANDS=8 +val SCE_MMIXAL_NUMBER=9 +val SCE_MMIXAL_REF=10 +val SCE_MMIXAL_CHAR=11 +val SCE_MMIXAL_STRING=12 +val SCE_MMIXAL_REGISTER=13 +val SCE_MMIXAL_HEX=14 +val SCE_MMIXAL_OPERATOR=15 +val SCE_MMIXAL_SYMBOL=16 +val SCE_MMIXAL_INCLUDE=17 +# Lexical states for SCLEX_CLW +lex Clarion=SCLEX_CLW SCE_CLW_ +val SCE_CLW_DEFAULT=0 +val SCE_CLW_LABEL=1 +val SCE_CLW_COMMENT=2 +val SCE_CLW_STRING=3 +val SCE_CLW_USER_IDENTIFIER=4 +val SCE_CLW_INTEGER_CONSTANT=5 +val SCE_CLW_REAL_CONSTANT=6 +val SCE_CLW_PICTURE_STRING=7 +val SCE_CLW_KEYWORD=8 +val SCE_CLW_COMPILER_DIRECTIVE=9 +val SCE_CLW_RUNTIME_EXPRESSIONS=10 +val SCE_CLW_BUILTIN_PROCEDURES_FUNCTION=11 +val SCE_CLW_STRUCTURE_DATA_TYPE=12 +val SCE_CLW_ATTRIBUTE=13 +val SCE_CLW_STANDARD_EQUATE=14 +val SCE_CLW_ERROR=15 +val SCE_CLW_DEPRECATED=16 +# Lexical states for SCLEX_LOT +lex LOT=SCLEX_LOT SCE_LOT_ +val SCE_LOT_DEFAULT=0 +val SCE_LOT_HEADER=1 +val SCE_LOT_BREAK=2 +val SCE_LOT_SET=3 +val SCE_LOT_PASS=4 +val SCE_LOT_FAIL=5 +val SCE_LOT_ABORT=6 +# Lexical states for SCLEX_YAML +lex YAML=SCLEX_YAML SCE_YAML_ +val SCE_YAML_DEFAULT=0 +val SCE_YAML_COMMENT=1 +val SCE_YAML_IDENTIFIER=2 +val SCE_YAML_KEYWORD=3 +val SCE_YAML_NUMBER=4 +val SCE_YAML_REFERENCE=5 +val SCE_YAML_DOCUMENT=6 +val SCE_YAML_TEXT=7 +val SCE_YAML_ERROR=8 +# Lexical states for SCLEX_TEX +lex TeX=SCLEX_TEX SCE_TEX_ +val SCE_TEX_DEFAULT=0 +val SCE_TEX_SPECIAL=1 +val SCE_TEX_GROUP=2 +val SCE_TEX_SYMBOL=3 +val SCE_TEX_COMMAND=4 +val SCE_TEX_TEXT=5 +lex Metapost=SCLEX_METAPOST SCE_METAPOST_ +val SCE_METAPOST_DEFAULT=0 +val SCE_METAPOST_SPECIAL=1 +val SCE_METAPOST_GROUP=2 +val SCE_METAPOST_SYMBOL=3 +val SCE_METAPOST_COMMAND=4 +val SCE_METAPOST_TEXT=5 +val SCE_METAPOST_EXTRA=6 +# Lexical states for SCLEX_ERLANG +lex Erlang=SCLEX_ERLANG SCE_ERLANG_ +val SCE_ERLANG_DEFAULT=0 +val SCE_ERLANG_COMMENT=1 +val SCE_ERLANG_VARIABLE=2 +val SCE_ERLANG_NUMBER=3 +val SCE_ERLANG_KEYWORD=4 +val SCE_ERLANG_STRING=5 +val SCE_ERLANG_OPERATOR=6 +val SCE_ERLANG_ATOM=7 +val SCE_ERLANG_FUNCTION_NAME=8 +val SCE_ERLANG_CHARACTER=9 +val SCE_ERLANG_MACRO=10 +val SCE_ERLANG_RECORD=11 +val SCE_ERLANG_SEPARATOR=12 +val SCE_ERLANG_NODE_NAME=13 +val SCE_ERLANG_UNKNOWN=31 +# Lexical states for SCLEX_OCTAVE are identical to MatLab +lex Octave=SCLEX_OCTAVE SCE_MATLAB_ +# Lexical states for SCLEX_MSSQL +lex MSSQL=SCLEX_MSSQL SCE_MSSQL_ +val SCE_MSSQL_DEFAULT=0 +val SCE_MSSQL_COMMENT=1 +val SCE_MSSQL_LINE_COMMENT=2 +val SCE_MSSQL_NUMBER=3 +val SCE_MSSQL_STRING=4 +val SCE_MSSQL_OPERATOR=5 +val SCE_MSSQL_IDENTIFIER=6 +val SCE_MSSQL_VARIABLE=7 +val SCE_MSSQL_COLUMN_NAME=8 +val SCE_MSSQL_STATEMENT=9 +val SCE_MSSQL_DATATYPE=10 +val SCE_MSSQL_SYSTABLE=11 +val SCE_MSSQL_GLOBAL_VARIABLE=12 +val SCE_MSSQL_FUNCTION=13 +val SCE_MSSQL_STORED_PROCEDURE=14 +val SCE_MSSQL_DEFAULT_PREF_DATATYPE=15 +val SCE_MSSQL_COLUMN_NAME_2=16 +# Lexical states for SCLEX_VERILOG +lex Verilog=SCLEX_VERILOG SCE_V_ +val SCE_V_DEFAULT=0 +val SCE_V_COMMENT=1 +val SCE_V_COMMENTLINE=2 +val SCE_V_COMMENTLINEBANG=3 +val SCE_V_NUMBER=4 +val SCE_V_WORD=5 +val SCE_V_STRING=6 +val SCE_V_WORD2=7 +val SCE_V_WORD3=8 +val SCE_V_PREPROCESSOR=9 +val SCE_V_OPERATOR=10 +val SCE_V_IDENTIFIER=11 +val SCE_V_STRINGEOL=12 +val SCE_V_USER=19 +# Lexical states for SCLEX_KIX +lex Kix=SCLEX_KIX SCE_KIX_ +val SCE_KIX_DEFAULT=0 +val SCE_KIX_COMMENT=1 +val SCE_KIX_STRING1=2 +val SCE_KIX_STRING2=3 +val SCE_KIX_NUMBER=4 +val SCE_KIX_VAR=5 +val SCE_KIX_MACRO=6 +val SCE_KIX_KEYWORD=7 +val SCE_KIX_FUNCTIONS=8 +val SCE_KIX_OPERATOR=9 +val SCE_KIX_IDENTIFIER=31 +# Lexical states for SCLEX_GUI4CLI +val SCE_GC_DEFAULT=0 +val SCE_GC_COMMENTLINE=1 +val SCE_GC_COMMENTBLOCK=2 +val SCE_GC_GLOBAL=3 +val SCE_GC_EVENT=4 +val SCE_GC_ATTRIBUTE=5 +val SCE_GC_CONTROL=6 +val SCE_GC_COMMAND=7 +val SCE_GC_STRING=8 +val SCE_GC_OPERATOR=9 +# Lexical states for SCLEX_SPECMAN +lex Specman=SCLEX_SPECMAN SCE_SN_ +val SCE_SN_DEFAULT=0 +val SCE_SN_CODE=1 +val SCE_SN_COMMENTLINE=2 +val SCE_SN_COMMENTLINEBANG=3 +val SCE_SN_NUMBER=4 +val SCE_SN_WORD=5 +val SCE_SN_STRING=6 +val SCE_SN_WORD2=7 +val SCE_SN_WORD3=8 +val SCE_SN_PREPROCESSOR=9 +val SCE_SN_OPERATOR=10 +val SCE_SN_IDENTIFIER=11 +val SCE_SN_STRINGEOL=12 +val SCE_SN_REGEXTAG=13 +val SCE_SN_SIGNAL=14 +val SCE_SN_USER=19 +# Lexical states for SCLEX_AU3 +lex Au3=SCLEX_AU3 SCE_AU3_ +val SCE_AU3_DEFAULT=0 +val SCE_AU3_COMMENT=1 +val SCE_AU3_COMMENTBLOCK=2 +val SCE_AU3_NUMBER=3 +val SCE_AU3_FUNCTION=4 +val SCE_AU3_KEYWORD=5 +val SCE_AU3_MACRO=6 +val SCE_AU3_STRING=7 +val SCE_AU3_OPERATOR=8 +val SCE_AU3_VARIABLE=9 +val SCE_AU3_SENT=10 +val SCE_AU3_PREPROCESSOR=11 +val SCE_AU3_SPECIAL=12 +val SCE_AU3_EXPAND=13 +val SCE_AU3_COMOBJ=14 +val SCE_AU3_UDF=15 +# Lexical states for SCLEX_APDL +lex APDL=SCLEX_APDL SCE_APDL_ +val SCE_APDL_DEFAULT=0 +val SCE_APDL_COMMENT=1 +val SCE_APDL_COMMENTBLOCK=2 +val SCE_APDL_NUMBER=3 +val SCE_APDL_STRING=4 +val SCE_APDL_OPERATOR=5 +val SCE_APDL_WORD=6 +val SCE_APDL_PROCESSOR=7 +val SCE_APDL_COMMAND=8 +val SCE_APDL_SLASHCOMMAND=9 +val SCE_APDL_STARCOMMAND=10 +val SCE_APDL_ARGUMENT=11 +val SCE_APDL_FUNCTION=12 +# Lexical states for SCLEX_BASH +lex Bash=SCLEX_BASH SCE_SH_ +val SCE_SH_DEFAULT=0 +val SCE_SH_ERROR=1 +val SCE_SH_COMMENTLINE=2 +val SCE_SH_NUMBER=3 +val SCE_SH_WORD=4 +val SCE_SH_STRING=5 +val SCE_SH_CHARACTER=6 +val SCE_SH_OPERATOR=7 +val SCE_SH_IDENTIFIER=8 +val SCE_SH_SCALAR=9 +val SCE_SH_PARAM=10 +val SCE_SH_BACKTICKS=11 +val SCE_SH_HERE_DELIM=12 +val SCE_SH_HERE_Q=13 +# Lexical states for SCLEX_ASN1 +lex Asn1=SCLEX_ASN1 SCE_ASN1_ +val SCE_ASN1_DEFAULT=0 +val SCE_ASN1_COMMENT=1 +val SCE_ASN1_IDENTIFIER=2 +val SCE_ASN1_STRING=3 +val SCE_ASN1_OID=4 +val SCE_ASN1_SCALAR=5 +val SCE_ASN1_KEYWORD=6 +val SCE_ASN1_ATTRIBUTE=7 +val SCE_ASN1_DESCRIPTOR=8 +val SCE_ASN1_TYPE=9 +val SCE_ASN1_OPERATOR=10 +# Lexical states for SCLEX_VHDL +lex VHDL=SCLEX_VHDL SCE_VHDL_ +val SCE_VHDL_DEFAULT=0 +val SCE_VHDL_COMMENT=1 +val SCE_VHDL_COMMENTLINEBANG=2 +val SCE_VHDL_NUMBER=3 +val SCE_VHDL_STRING=4 +val SCE_VHDL_OPERATOR=5 +val SCE_VHDL_IDENTIFIER=6 +val SCE_VHDL_STRINGEOL=7 +val SCE_VHDL_KEYWORD=8 +val SCE_VHDL_STDOPERATOR=9 +val SCE_VHDL_ATTRIBUTE=10 +val SCE_VHDL_STDFUNCTION=11 +val SCE_VHDL_STDPACKAGE=12 +val SCE_VHDL_STDTYPE=13 +val SCE_VHDL_USERWORD=14 +# Lexical states for SCLEX_CAML +lex Caml=SCLEX_CAML SCE_CAML_ +val SCE_CAML_DEFAULT=0 +val SCE_CAML_IDENTIFIER=1 +val SCE_CAML_TAGNAME=2 +val SCE_CAML_KEYWORD=3 +val SCE_CAML_KEYWORD2=4 +val SCE_CAML_KEYWORD3=5 +val SCE_CAML_LINENUM=6 +val SCE_CAML_OPERATOR=7 +val SCE_CAML_NUMBER=8 +val SCE_CAML_CHAR=9 +val SCE_CAML_STRING=11 +val SCE_CAML_COMMENT=12 +val SCE_CAML_COMMENT1=13 +val SCE_CAML_COMMENT2=14 +val SCE_CAML_COMMENT3=15 +# Lexical states for SCLEX_HASKELL +lex Haskell=SCLEX_HASKELL SCE_HA_ +val SCE_HA_DEFAULT=0 +val SCE_HA_IDENTIFIER=1 +val SCE_HA_KEYWORD=2 +val SCE_HA_NUMBER=3 +val SCE_HA_STRING=4 +val SCE_HA_CHARACTER=5 +val SCE_HA_CLASS=6 +val SCE_HA_MODULE=7 +val SCE_HA_CAPITAL=8 +val SCE_HA_DATA=9 +val SCE_HA_IMPORT=10 +val SCE_HA_OPERATOR=11 +val SCE_HA_INSTANCE=12 +val SCE_HA_COMMENTLINE=13 +val SCE_HA_COMMENTBLOCK=14 +val SCE_HA_COMMENTBLOCK2=15 +val SCE_HA_COMMENTBLOCK3=16 +# Lexical states of SCLEX_TADS3 +lex TADS3=SCLEX_TADS3 SCE_T3_ +val SCE_T3_DEFAULT=0 +val SCE_T3_X_DEFAULT=1 +val SCE_T3_PREPROCESSOR=2 +val SCE_T3_BLOCK_COMMENT=3 +val SCE_T3_LINE_COMMENT=4 +val SCE_T3_OPERATOR=5 +val SCE_T3_KEYWORD=6 +val SCE_T3_NUMBER=7 +val SCE_T3_IDENTIFIER=8 +val SCE_T3_S_STRING=9 +val SCE_T3_D_STRING=10 +val SCE_T3_X_STRING=11 +val SCE_T3_LIB_DIRECTIVE=12 +val SCE_T3_MSG_PARAM=13 +val SCE_T3_HTML_TAG=14 +val SCE_T3_HTML_DEFAULT=15 +val SCE_T3_HTML_STRING=16 +val SCE_T3_USER1=17 +val SCE_T3_USER2=18 +val SCE_T3_USER3=19 +# Lexical states for SCLEX_REBOL +lex Rebol=SCLEX_REBOL SCE_REBOL_ +val SCE_REBOL_DEFAULT=0 +val SCE_REBOL_COMMENTLINE=1 +val SCE_REBOL_COMMENTBLOCK=2 +val SCE_REBOL_PREFACE=3 +val SCE_REBOL_OPERATOR=4 +val SCE_REBOL_CHARACTER=5 +val SCE_REBOL_QUOTEDSTRING=6 +val SCE_REBOL_BRACEDSTRING=7 +val SCE_REBOL_NUMBER=8 +val SCE_REBOL_PAIR=9 +val SCE_REBOL_TUPLE=10 +val SCE_REBOL_BINARY=11 +val SCE_REBOL_MONEY=12 +val SCE_REBOL_ISSUE=13 +val SCE_REBOL_TAG=14 +val SCE_REBOL_FILE=15 +val SCE_REBOL_EMAIL=16 +val SCE_REBOL_URL=17 +val SCE_REBOL_DATE=18 +val SCE_REBOL_TIME=19 +val SCE_REBOL_IDENTIFIER=20 +val SCE_REBOL_WORD=21 +val SCE_REBOL_WORD2=22 +val SCE_REBOL_WORD3=23 +val SCE_REBOL_WORD4=24 +val SCE_REBOL_WORD5=25 +val SCE_REBOL_WORD6=26 +val SCE_REBOL_WORD7=27 +val SCE_REBOL_WORD8=28 +# Lexical states for SCLEX_SQL +lex SQL=SCLEX_SQL SCE_SQL_ +val SCE_SQL_DEFAULT=0 +val SCE_SQL_COMMENT=1 +val SCE_SQL_COMMENTLINE=2 +val SCE_SQL_COMMENTDOC=3 +val SCE_SQL_NUMBER=4 +val SCE_SQL_WORD=5 +val SCE_SQL_STRING=6 +val SCE_SQL_CHARACTER=7 +val SCE_SQL_SQLPLUS=8 +val SCE_SQL_SQLPLUS_PROMPT=9 +val SCE_SQL_OPERATOR=10 +val SCE_SQL_IDENTIFIER=11 +val SCE_SQL_SQLPLUS_COMMENT=13 +val SCE_SQL_COMMENTLINEDOC=15 +val SCE_SQL_WORD2=16 +val SCE_SQL_COMMENTDOCKEYWORD=17 +val SCE_SQL_COMMENTDOCKEYWORDERROR=18 +val SCE_SQL_USER1=19 +val SCE_SQL_USER2=20 +val SCE_SQL_USER3=21 +val SCE_SQL_USER4=22 +val SCE_SQL_QUOTEDIDENTIFIER=23 +# Lexical states for SCLEX_SMALLTALK +lex Smalltalk=SCLEX_SMALLTALK SCE_ST_ +val SCE_ST_DEFAULT=0 +val SCE_ST_STRING=1 +val SCE_ST_NUMBER=2 +val SCE_ST_COMMENT=3 +val SCE_ST_SYMBOL=4 +val SCE_ST_BINARY=5 +val SCE_ST_BOOL=6 +val SCE_ST_SELF=7 +val SCE_ST_SUPER=8 +val SCE_ST_NIL=9 +val SCE_ST_GLOBAL=10 +val SCE_ST_RETURN=11 +val SCE_ST_SPECIAL=12 +val SCE_ST_KWSEND=13 +val SCE_ST_ASSIGN=14 +val SCE_ST_CHARACTER=15 +val SCE_ST_SPEC_SEL=16 +# Lexical states for SCLEX_FLAGSHIP (clipper) +lex FlagShip=SCLEX_FLAGSHIP SCE_B_ +val SCE_FS_DEFAULT=0 +val SCE_FS_COMMENT=1 +val SCE_FS_COMMENTLINE=2 +val SCE_FS_COMMENTDOC=3 +val SCE_FS_COMMENTLINEDOC=4 +val SCE_FS_COMMENTDOCKEYWORD=5 +val SCE_FS_COMMENTDOCKEYWORDERROR=6 +val SCE_FS_KEYWORD=7 +val SCE_FS_KEYWORD2=8 +val SCE_FS_KEYWORD3=9 +val SCE_FS_KEYWORD4=10 +val SCE_FS_NUMBER=11 +val SCE_FS_STRING=12 +val SCE_FS_PREPROCESSOR=13 +val SCE_FS_OPERATOR=14 +val SCE_FS_IDENTIFIER=15 +val SCE_FS_DATE=16 +val SCE_FS_STRINGEOL=17 +val SCE_FS_CONSTANT=18 +val SCE_FS_ASM=19 +val SCE_FS_LABEL=20 +val SCE_FS_ERROR=21 +val SCE_FS_HEXNUMBER=22 +val SCE_FS_BINNUMBER=23 +# Lexical states for SCLEX_CSOUND +lex Csound=SCLEX_CSOUND SCE_CSOUND_ +val SCE_CSOUND_DEFAULT=0 +val SCE_CSOUND_COMMENT=1 +val SCE_CSOUND_NUMBER=2 +val SCE_CSOUND_OPERATOR=3 +val SCE_CSOUND_INSTR=4 +val SCE_CSOUND_IDENTIFIER=5 +val SCE_CSOUND_OPCODE=6 +val SCE_CSOUND_HEADERSTMT=7 +val SCE_CSOUND_USERKEYWORD=8 +val SCE_CSOUND_COMMENTBLOCK=9 +val SCE_CSOUND_PARAM=10 +val SCE_CSOUND_ARATE_VAR=11 +val SCE_CSOUND_KRATE_VAR=12 +val SCE_CSOUND_IRATE_VAR=13 +val SCE_CSOUND_GLOBAL_VAR=14 +val SCE_CSOUND_STRINGEOL=15 +# Lexical states for SCLEX_INNOSETUP +lex Inno=SCLEX_INNOSETUP SCE_INNO_ +val SCE_INNO_DEFAULT=0 +val SCE_INNO_COMMENT=1 +val SCE_INNO_KEYWORD=2 +val SCE_INNO_PARAMETER=3 +val SCE_INNO_SECTION=4 +val SCE_INNO_PREPROC=5 +val SCE_INNO_PREPROC_INLINE=6 +val SCE_INNO_COMMENT_PASCAL=7 +val SCE_INNO_KEYWORD_PASCAL=8 +val SCE_INNO_KEYWORD_USER=9 +val SCE_INNO_STRING_DOUBLE=10 +val SCE_INNO_STRING_SINGLE=11 +val SCE_INNO_IDENTIFIER=12 +# Lexical states for SCLEX_OPAL +lex Opal=SCLEX_OPAL SCE_OPAL_ +val SCE_OPAL_SPACE=0 +val SCE_OPAL_COMMENT_BLOCK=1 +val SCE_OPAL_COMMENT_LINE=2 +val SCE_OPAL_INTEGER=3 +val SCE_OPAL_KEYWORD=4 +val SCE_OPAL_SORT=5 +val SCE_OPAL_STRING=6 +val SCE_OPAL_PAR=7 +val SCE_OPAL_BOOL_CONST=8 +val SCE_OPAL_DEFAULT=32 +# Lexical states for SCLEX_SPICE +lex Spice=SCLEX_SPICE SCE_SPICE_ +val SCE_SPICE_DEFAULT=0 +val SCE_SPICE_IDENTIFIER=1 +val SCE_SPICE_KEYWORD=2 +val SCE_SPICE_KEYWORD2=3 +val SCE_SPICE_KEYWORD3=4 +val SCE_SPICE_NUMBER=5 +val SCE_SPICE_DELIMITER=6 +val SCE_SPICE_VALUE=7 +val SCE_SPICE_COMMENTLINE=8 + +# Events + +evt void StyleNeeded=2000(int position) +evt void CharAdded=2001(int ch) +evt void SavePointReached=2002(void) +evt void SavePointLeft=2003(void) +evt void ModifyAttemptRO=2004(void) +# GTK+ Specific to work around focus and accelerator problems: +evt void Key=2005(int ch, int modifiers) +evt void DoubleClick=2006(void) +evt void UpdateUI=2007(void) +evt void Modified=2008(int position, int modificationType, string text, int length, int linesAdded, int line, int foldLevelNow, int foldLevelPrev) +evt void MacroRecord=2009(int message, int wParam, int lParam) +evt void MarginClick=2010(int modifiers, int position, int margin) +evt void NeedShown=2011(int position, int length) +evt void Painted=2013(void) +evt void UserListSelection=2014(int listType, string text) +evt void URIDropped=2015(string text) +evt void DwellStart=2016(int position) +evt void DwellEnd=2017(int position) +evt void Zoom=2018(void) +evt void HotSpotClick=2019(int modifiers, int position) +evt void HotSpotDoubleClick=2020(int modifiers, int position) +evt void CallTipClick=2021(int position) +evt void AutoCSelection=2022(string text) + +cat Deprecated + +# CARET_POLICY changed in 1.47 +fun void SetCaretPolicy=2369(int caretPolicy, int caretSlop) +val CARET_CENTER=0x02 +val CARET_XEVEN=0x08 +val CARET_XJUMPS=0x10 + +# The old name for SCN_UPDATEUI +val SCN_CHECKBRACE=2007 +evt void PosChanged=2012(int position) + +# SCLEX_HTML should be used in preference to these. +val SCLEX_ASP=29 +val SCLEX_PHP=30 diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/ScintillaWidget.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/ScintillaWidget.h new file mode 100644 index 00000000..d9909524 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/ScintillaWidget.h @@ -0,0 +1,59 @@ +// Scintilla source code edit control +/** @file ScintillaWidget.h + ** Definition of Scintilla widget for GTK+. + ** Only needed by GTK+ code but is harmless on other platforms. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef SCINTILLAWIDGET_H +#define SCINTILLAWIDGET_H + +#if PLAT_GTK + +#ifdef __cplusplus +extern "C" { +#endif + +#define SCINTILLA(obj) GTK_CHECK_CAST (obj, scintilla_get_type (), ScintillaObject) +#define SCINTILLA_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, scintilla_get_type (), ScintillaClass) +#define IS_SCINTILLA(obj) GTK_CHECK_TYPE (obj, scintilla_get_type ()) + +typedef struct _ScintillaObject ScintillaObject; +typedef struct _ScintillaClass ScintillaClass; + +struct _ScintillaObject { + GtkContainer cont; + void *pscin; +}; + +struct _ScintillaClass { + GtkContainerClass parent_class; + + void (* command) (ScintillaObject *ttt); + void (* notify) (ScintillaObject *ttt); +}; + +#if GLIB_MAJOR_VERSION < 2 +GtkType scintilla_get_type (void); +#else +GType scintilla_get_type (void); +#endif +GtkWidget* scintilla_new (void); +void scintilla_set_id (ScintillaObject *sci, uptr_t id); +sptr_t scintilla_send_message (ScintillaObject *sci,unsigned int iMessage, uptr_t wParam, sptr_t lParam); +void scintilla_release_resources(void); + +#if GTK_MAJOR_VERSION < 2 +#define SCINTILLA_NOTIFY "notify" +#else +#define SCINTILLA_NOTIFY "sci-notify" +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/WindowAccessor.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/WindowAccessor.h new file mode 100644 index 00000000..baae30cb --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/include/WindowAccessor.h @@ -0,0 +1,57 @@ +// Scintilla source code edit control +/** @file WindowAccessor.h + ** Implementation of BufferAccess and StylingAccess on a Scintilla + ** rapid easy access to contents of a Scintilla. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +/** + */ +class WindowAccessor : public Accessor { + // Private so WindowAccessor objects can not be copied + WindowAccessor(const WindowAccessor &source) : Accessor(), props(source.props) {} + WindowAccessor &operator=(const WindowAccessor &) { return *this; } +protected: + WindowID id; + PropSet &props; + int lenDoc; + + char styleBuf[bufferSize]; + int validLen; + char chFlags; + char chWhile; + unsigned int startSeg; + + bool InternalIsLeadByte(char ch); + void Fill(int position); +public: + WindowAccessor(WindowID id_, PropSet &props_) : + Accessor(), id(id_), props(props_), + lenDoc(-1), validLen(0), chFlags(0), chWhile(0) { + } + ~WindowAccessor(); + bool Match(int pos, const char *s); + char StyleAt(int position); + int GetLine(int position); + int LineStart(int line); + int LevelAt(int line); + int Length(); + void Flush(); + int GetLineState(int line); + int SetLineState(int line, int state); + int GetPropertyInt(const char *key, int defaultValue=0) { + return props.GetInt(key, defaultValue); + } + char *GetProperties() { + return props.ToString(); + } + + void StartAt(unsigned int start, char chMask=31); + void SetFlags(char chFlags_, char chWhile_) {chFlags = chFlags_; chWhile = chWhile_; }; + unsigned int GetStartSegment() { return startSeg; } + void StartSegment(unsigned int pos); + void ColourTo(unsigned int pos, int chAttr); + void SetLevel(int line, int level); + int IndentAmount(int line, int *flags, PFNIsCommentLeader pfnIsCommentLeader = 0); +}; diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/AutoComplete.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/AutoComplete.cxx new file mode 100644 index 00000000..83ee0769 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/AutoComplete.cxx @@ -0,0 +1,174 @@ +// Scintilla source code edit control +/** @file AutoComplete.cxx + ** Defines the auto completion list box. + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "AutoComplete.h" + +AutoComplete::AutoComplete() : + active(false), + separator(' '), + typesep('?'), + ignoreCase(false), + chooseSingle(false), + lb(0), + posStart(0), + startLen(0), + cancelAtStartPos(true), + autoHide(true), + dropRestOfWord(false) { + lb = ListBox::Allocate(); + stopChars[0] = '\0'; + fillUpChars[0] = '\0'; +} + +AutoComplete::~AutoComplete() { + if (lb) { + lb->Destroy(); + delete lb; + lb = 0; + } +} + +bool AutoComplete::Active() { + return active; +} + +void AutoComplete::Start(Window &parent, int ctrlID, + int position, Point location, int startLen_, + int lineHeight, bool unicodeMode) { + if (active) { + Cancel(); + } + lb->Create(parent, ctrlID, location, lineHeight, unicodeMode); + lb->Clear(); + active = true; + startLen = startLen_; + posStart = position; +} + +void AutoComplete::SetStopChars(const char *stopChars_) { + strncpy(stopChars, stopChars_, sizeof(stopChars)); + stopChars[sizeof(stopChars) - 1] = '\0'; +} + +bool AutoComplete::IsStopChar(char ch) { + return ch && strchr(stopChars, ch); +} + +void AutoComplete::SetFillUpChars(const char *fillUpChars_) { + strncpy(fillUpChars, fillUpChars_, sizeof(fillUpChars)); + fillUpChars[sizeof(fillUpChars) - 1] = '\0'; +} + +bool AutoComplete::IsFillUpChar(char ch) { + return ch && strchr(fillUpChars, ch); +} + +void AutoComplete::SetSeparator(char separator_) { + separator = separator_; +} + +char AutoComplete::GetSeparator() { + return separator; +} + +void AutoComplete::SetTypesep(char separator_) { + typesep = separator_; +} + +char AutoComplete::GetTypesep() { + return typesep; +} + +void AutoComplete::SetList(const char *list) { + lb->SetList(list, separator, typesep); +} + +void AutoComplete::Show(bool show) { + lb->Show(show); + if (show) + lb->Select(0); +} + +void AutoComplete::Cancel() { + if (lb->Created()) { + lb->Clear(); + lb->Destroy(); + active = false; + } +} + + +void AutoComplete::Move(int delta) { + int count = lb->Length(); + int current = lb->GetSelection(); + current += delta; + if (current >= count) + current = count - 1; + if (current < 0) + current = 0; + lb->Select(current); +} + +void AutoComplete::Select(const char *word) { + size_t lenWord = strlen(word); + int location = -1; + const int maxItemLen=1000; + char item[maxItemLen]; + int start = 0; // lower bound of the api array block to search + int end = lb->Length() - 1; // upper bound of the api array block to search + while ((start <= end) && (location == -1)) { // Binary searching loop + int pivot = (start + end) / 2; + lb->GetValue(pivot, item, maxItemLen); + int cond; + if (ignoreCase) + cond = CompareNCaseInsensitive(word, item, lenWord); + else + cond = strncmp(word, item, lenWord); + if (!cond) { + // Find first match + while (pivot > start) { + lb->GetValue(pivot-1, item, maxItemLen); + if (ignoreCase) + cond = CompareNCaseInsensitive(word, item, lenWord); + else + cond = strncmp(word, item, lenWord); + if (0 != cond) + break; + --pivot; + } + location = pivot; + if (ignoreCase) { + // Check for exact-case match + for (; pivot <= end; pivot++) { + lb->GetValue(pivot, item, maxItemLen); + if (!strncmp(word, item, lenWord)) { + location = pivot; + break; + } + if (CompareNCaseInsensitive(word, item, lenWord)) + break; + } + } + } else if (cond < 0) { + end = pivot - 1; + } else if (cond > 0) { + start = pivot + 1; + } + } + if (location == -1 && autoHide) + Cancel(); + else + lb->Select(location); +} + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/AutoComplete.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/AutoComplete.h new file mode 100644 index 00000000..eefc6a84 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/AutoComplete.h @@ -0,0 +1,70 @@ +// Scintilla source code edit control +/** @file AutoComplete.h + ** Defines the auto completion list box. + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef AUTOCOMPLETE_H +#define AUTOCOMPLETE_H + +/** + */ +class AutoComplete { + bool active; + char stopChars[256]; + char fillUpChars[256]; + char separator; + char typesep; // Type seperator + +public: + bool ignoreCase; + bool chooseSingle; + ListBox *lb; + int posStart; + int startLen; + /// Should autocompletion be canceled if editor's currentPos <= startPos? + bool cancelAtStartPos; + bool autoHide; + bool dropRestOfWord; + + AutoComplete(); + ~AutoComplete(); + + /// Is the auto completion list displayed? + bool Active(); + + /// Display the auto completion list positioned to be near a character position + void Start(Window &parent, int ctrlID, int position, Point location, + int startLen_, int lineHeight, bool unicodeMode); + + /// The stop chars are characters which, when typed, cause the auto completion list to disappear + void SetStopChars(const char *stopChars_); + bool IsStopChar(char ch); + + /// The fillup chars are characters which, when typed, fill up the selected word + void SetFillUpChars(const char *fillUpChars_); + bool IsFillUpChar(char ch); + + /// The separator character is used when interpreting the list in SetList + void SetSeparator(char separator_); + char GetSeparator(); + + /// The typesep character is used for seperating the word from the type + void SetTypesep(char separator_); + char GetTypesep(); + + /// The list string contains a sequence of words separated by the separator character + void SetList(const char *list); + + void Show(bool show); + void Cancel(); + + /// Move the current list element by delta, scrolling appropriately + void Move(int delta); + + /// Select a list element that starts with word as the current element + void Select(const char *word); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CallTip.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CallTip.cxx new file mode 100644 index 00000000..729bf29a --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CallTip.cxx @@ -0,0 +1,314 @@ +// Scintilla source code edit control +/** @file CallTip.cxx + ** Code for displaying call tips. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include + +#include "Platform.h" + +#include "Scintilla.h" +#include "CallTip.h" + +static const int insetX = 5; // text inset in x from calltip border +static const int widthArrow = 14; + +CallTip::CallTip() { + wCallTip = 0; + inCallTipMode = false; + posStartCallTip = 0; + val = 0; + rectUp = PRectangle(0,0,0,0); + rectDown = PRectangle(0,0,0,0); + lineHeight = 1; + startHighlight = 0; + endHighlight = 0; + tabSize = 0; + useStyleCallTip = false; // for backwards compatibility + + colourBG.desired = ColourDesired(0xff, 0xff, 0xff); + colourUnSel.desired = ColourDesired(0x80, 0x80, 0x80); + colourSel.desired = ColourDesired(0, 0, 0x80); + colourShade.desired = ColourDesired(0, 0, 0); + colourLight.desired = ColourDesired(0xc0, 0xc0, 0xc0); +} + +CallTip::~CallTip() { + font.Release(); + wCallTip.Destroy(); + delete []val; + val = 0; +} + +void CallTip::RefreshColourPalette(Palette &pal, bool want) { + pal.WantFind(colourBG, want); + pal.WantFind(colourUnSel, want); + pal.WantFind(colourSel, want); + pal.WantFind(colourShade, want); + pal.WantFind(colourLight, want); +} + +// Although this test includes 0, we should never see a \0 character. +static bool IsArrowCharacter(char ch) { + return (ch == 0) || (ch == '\001') || (ch == '\002'); +} + +// We ignore tabs unless a tab width has been set. +bool CallTip::IsTabCharacter(char ch) { + return (tabSize > 0) && (ch == '\t'); +} + +int CallTip::NextTabPos(int x) { + if (tabSize > 0) { // paranoia... not called unless this is true + x -= insetX; // position relative to text + x = (x + tabSize) / tabSize; // tab "number" + return tabSize*x + insetX; // position of next tab + } else { + return x + 1; // arbitrary + } +} + +// Draw a section of the call tip that does not include \n in one colour. +// The text may include up to numEnds tabs or arrow characters. +void CallTip::DrawChunk(Surface *surface, int &x, const char *s, + int posStart, int posEnd, int ytext, PRectangle rcClient, + bool highlight, bool draw) { + s += posStart; + int len = posEnd - posStart; + + // Divide the text into sections that are all text, or that are + // single arrows or single tab characters (if tabSize > 0). + int maxEnd = 0; + const int numEnds = 10; + int ends[numEnds + 2]; + for (int i=0;i 0) + ends[maxEnd++] = i; + ends[maxEnd++] = i+1; + } + } + ends[maxEnd++] = len; + int startSeg = 0; + int xEnd; + for (int seg = 0; seg startSeg) { + if (IsArrowCharacter(s[startSeg])) { + bool upArrow = s[startSeg] == '\001'; + rcClient.left = x; + rcClient.right = rcClient.left + widthArrow; + if (draw) { + const int halfWidth = widthArrow / 2 - 3; + const int centreX = rcClient.left + widthArrow / 2 - 1; + const int centreY = (rcClient.top + rcClient.bottom) / 2; + surface->FillRectangle(rcClient, colourBG.allocated); + PRectangle rcClientInner(rcClient.left + 1, rcClient.top + 1, + rcClient.right - 2, rcClient.bottom - 1); + surface->FillRectangle(rcClientInner, colourUnSel.allocated); + + if (upArrow) { // Up arrow + Point pts[] = { + Point(centreX - halfWidth, centreY + halfWidth / 2), + Point(centreX + halfWidth, centreY + halfWidth / 2), + Point(centreX, centreY - halfWidth + halfWidth / 2), + }; + surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), + colourBG.allocated, colourBG.allocated); + } else { // Down arrow + Point pts[] = { + Point(centreX - halfWidth, centreY - halfWidth / 2), + Point(centreX + halfWidth, centreY - halfWidth / 2), + Point(centreX, centreY + halfWidth - halfWidth / 2), + }; + surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]), + colourBG.allocated, colourBG.allocated); + } + } + xEnd = rcClient.right; + offsetMain = xEnd; + if (upArrow) { + rectUp = rcClient; + } else { + rectDown = rcClient; + } + } else if (IsTabCharacter(s[startSeg])) { + xEnd = NextTabPos(x); + } else { + xEnd = x + surface->WidthText(font, s + startSeg, endSeg - startSeg); + if (draw) { + rcClient.left = x; + rcClient.right = xEnd; + surface->DrawTextTransparent(rcClient, font, ytext, + s+startSeg, endSeg - startSeg, + highlight ? colourSel.allocated : colourUnSel.allocated); + } + } + x = xEnd; + startSeg = endSeg; + } + } +} + +int CallTip::PaintContents(Surface *surfaceWindow, bool draw) { + PRectangle rcClientPos = wCallTip.GetClientPosition(); + PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left, + rcClientPos.bottom - rcClientPos.top); + PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1); + + // To make a nice small call tip window, it is only sized to fit most normal characters without accents + int ascent = surfaceWindow->Ascent(font) - surfaceWindow->InternalLeading(font); + + // For each line... + // Draw the definition in three parts: before highlight, highlighted, after highlight + int ytext = rcClient.top + ascent + 1; + rcClient.bottom = ytext + surfaceWindow->Descent(font) + 1; + char *chunkVal = val; + bool moreChunks = true; + int maxWidth = 0; + while (moreChunks) { + char *chunkEnd = strchr(chunkVal, '\n'); + if (chunkEnd == NULL) { + chunkEnd = chunkVal + strlen(chunkVal); + moreChunks = false; + } + int chunkOffset = chunkVal - val; + int chunkLength = chunkEnd - chunkVal; + int chunkEndOffset = chunkOffset + chunkLength; + int thisStartHighlight = Platform::Maximum(startHighlight, chunkOffset); + thisStartHighlight = Platform::Minimum(thisStartHighlight, chunkEndOffset); + thisStartHighlight -= chunkOffset; + int thisEndHighlight = Platform::Maximum(endHighlight, chunkOffset); + thisEndHighlight = Platform::Minimum(thisEndHighlight, chunkEndOffset); + thisEndHighlight -= chunkOffset; + rcClient.top = ytext - ascent - 1; + + int x = insetX; // start each line at this inset + + DrawChunk(surfaceWindow, x, chunkVal, 0, thisStartHighlight, + ytext, rcClient, false, draw); + DrawChunk(surfaceWindow, x, chunkVal, thisStartHighlight, thisEndHighlight, + ytext, rcClient, true, draw); + DrawChunk(surfaceWindow, x, chunkVal, thisEndHighlight, chunkLength, + ytext, rcClient, false, draw); + + chunkVal = chunkEnd + 1; + ytext += lineHeight; + rcClient.bottom += lineHeight; + maxWidth = Platform::Maximum(maxWidth, x); + } + return maxWidth; +} + +void CallTip::PaintCT(Surface *surfaceWindow) { + if (!val) + return; + PRectangle rcClientPos = wCallTip.GetClientPosition(); + PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left, + rcClientPos.bottom - rcClientPos.top); + PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1); + + surfaceWindow->FillRectangle(rcClient, colourBG.allocated); + + offsetMain = insetX; // initial alignment assuming no arrows + PaintContents(surfaceWindow, true); + + // Draw a raised border around the edges of the window + surfaceWindow->MoveTo(0, rcClientSize.bottom - 1); + surfaceWindow->PenColour(colourShade.allocated); + surfaceWindow->LineTo(rcClientSize.right - 1, rcClientSize.bottom - 1); + surfaceWindow->LineTo(rcClientSize.right - 1, 0); + surfaceWindow->PenColour(colourLight.allocated); + surfaceWindow->LineTo(0, 0); + surfaceWindow->LineTo(0, rcClientSize.bottom - 1); +} + +void CallTip::MouseClick(Point pt) { + clickPlace = 0; + if (rectUp.Contains(pt)) + clickPlace = 1; + if (rectDown.Contains(pt)) + clickPlace = 2; +} + +PRectangle CallTip::CallTipStart(int pos, Point pt, const char *defn, + const char *faceName, int size, + int codePage_, int characterSet, Window &wParent) { + clickPlace = 0; + if (val) + delete []val; + val = new char[strlen(defn) + 1]; + if (!val) + return PRectangle(); + strcpy(val, defn); + codePage = codePage_; + Surface *surfaceMeasure = Surface::Allocate(); + if (!surfaceMeasure) + return PRectangle(); + surfaceMeasure->Init(wParent.GetID()); + surfaceMeasure->SetUnicodeMode(SC_CP_UTF8 == codePage); + surfaceMeasure->SetDBCSMode(codePage); + startHighlight = 0; + endHighlight = 0; + inCallTipMode = true; + posStartCallTip = pos; + int deviceHeight = surfaceMeasure->DeviceHeightFont(size); + font.Create(faceName, characterSet, deviceHeight, false, false); + // Look for multiple lines in the text + // Only support \n here - simply means container must avoid \r! + int numLines = 1; + const char *newline; + const char *look = val; + rectUp = PRectangle(0,0,0,0); + rectDown = PRectangle(0,0,0,0); + offsetMain = insetX; // changed to right edge of any arrows + int width = PaintContents(surfaceMeasure, false) + insetX; + while ((newline = strchr(look, '\n')) != NULL) { + look = newline + 1; + numLines++; + } + lineHeight = surfaceMeasure->Height(font); + + // Extra line for border and an empty line at top and bottom. The returned + // rectangle is aligned to the right edge of the last arrow encountered in + // the tip text, else to the tip text left edge. + int height = lineHeight * numLines - surfaceMeasure->InternalLeading(font) + 2 + 2; + delete surfaceMeasure; + return PRectangle(pt.x - offsetMain, pt.y + 1, pt.x + width - offsetMain, pt.y + 1 + height); +} + +void CallTip::CallTipCancel() { + inCallTipMode = false; + if (wCallTip.Created()) { + wCallTip.Destroy(); + } +} + +void CallTip::SetHighlight(int start, int end) { + // Avoid flashing by checking something has really changed + if ((start != startHighlight) || (end != endHighlight)) { + startHighlight = start; + endHighlight = end; + if (wCallTip.Created()) { + wCallTip.InvalidateAll(); + } + } +} + +// Set the tab size (sizes > 0 enable the use of tabs). This also enables the +// use of the STYLE_CALLTIP. +void CallTip::SetTabSize(int tabSz) { + tabSize = tabSz; + useStyleCallTip = true; +} + +// It might be better to have two access functions for this and to use +// them for all settings of colours. +void CallTip::SetForeBack(const ColourPair &fore, const ColourPair &back) { + colourBG = back; + colourUnSel = fore; +} diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CallTip.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CallTip.h new file mode 100644 index 00000000..3f7557e4 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CallTip.h @@ -0,0 +1,79 @@ +// Scintilla source code edit control +/** @file CallTip.h + ** Interface to the call tip control. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef CALLTIP_H +#define CALLTIP_H + +/** + */ +class CallTip { + int startHighlight; // character offset to start and... + int endHighlight; // ...end of highlighted text + char *val; + Font font; + PRectangle rectUp; // rectangle of last up angle in the tip + PRectangle rectDown; // rectangle of last down arrow in the tip + int lineHeight; // vertical line spacing + int offsetMain; // The alignment point of the call tip + int tabSize; // Tab size in pixels, <=0 no TAB expand + bool useStyleCallTip; // if true, STYLE_CALLTIP should be used + + // Private so CallTip objects can not be copied + CallTip(const CallTip &) {} + CallTip &operator=(const CallTip &) { return *this; } + void DrawChunk(Surface *surface, int &x, const char *s, + int posStart, int posEnd, int ytext, PRectangle rcClient, + bool highlight, bool draw); + int PaintContents(Surface *surfaceWindow, bool draw); + bool IsTabCharacter(char c); + int NextTabPos(int x); + +public: + Window wCallTip; + Window wDraw; + bool inCallTipMode; + int posStartCallTip; + ColourPair colourBG; + ColourPair colourUnSel; + ColourPair colourSel; + ColourPair colourShade; + ColourPair colourLight; + int codePage; + int clickPlace; + + CallTip(); + ~CallTip(); + + /// Claim or accept palette entries for the colours required to paint a calltip. + void RefreshColourPalette(Palette &pal, bool want); + + void PaintCT(Surface *surfaceWindow); + + void MouseClick(Point pt); + + /// Setup the calltip and return a rectangle of the area required. + PRectangle CallTipStart(int pos, Point pt, const char *defn, + const char *faceName, int size, int codePage_, + int characterSet, Window &wParent); + + void CallTipCancel(); + + /// Set a range of characters to be displayed in a highlight style. + /// Commonly used to highlight the current parameter. + void SetHighlight(int start, int end); + + /// Set the tab size in pixels for the call tip. 0 or -ve means no tab expand. + void SetTabSize(int tabSz); + + /// Used to determine which STYLE_xxxx to use for call tip information + bool UseStyleCallTip() const { return useStyleCallTip;} + + // Modify foreground and background colours + void SetForeBack(const ColourPair &fore, const ColourPair &back); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CellBuffer.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CellBuffer.cxx new file mode 100644 index 00000000..162d6bb7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CellBuffer.cxx @@ -0,0 +1,1120 @@ +// Scintilla source code edit control +/** @file CellBuffer.cxx + ** Manages a buffer of cells. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include + +#include "Platform.h" + +#include "Scintilla.h" +#include "SVector.h" +#include "CellBuffer.h" + +MarkerHandleSet::MarkerHandleSet() { + root = 0; +} + +MarkerHandleSet::~MarkerHandleSet() { + MarkerHandleNumber *mhn = root; + while (mhn) { + MarkerHandleNumber *mhnToFree = mhn; + mhn = mhn->next; + delete mhnToFree; + } + root = 0; +} + +int MarkerHandleSet::Length() { + int c = 0; + MarkerHandleNumber *mhn = root; + while (mhn) { + c++; + mhn = mhn->next; + } + return c; +} + +int MarkerHandleSet::NumberFromHandle(int handle) { + MarkerHandleNumber *mhn = root; + while (mhn) { + if (mhn->handle == handle) { + return mhn->number; + } + mhn = mhn->next; + } + return - 1; +} + +int MarkerHandleSet::MarkValue() { + unsigned int m = 0; + MarkerHandleNumber *mhn = root; + while (mhn) { + m |= (1 << mhn->number); + mhn = mhn->next; + } + return m; +} + +bool MarkerHandleSet::Contains(int handle) { + MarkerHandleNumber *mhn = root; + while (mhn) { + if (mhn->handle == handle) { + return true; + } + mhn = mhn->next; + } + return false; +} + +bool MarkerHandleSet::InsertHandle(int handle, int markerNum) { + MarkerHandleNumber *mhn = new MarkerHandleNumber; + if (!mhn) + return false; + mhn->handle = handle; + mhn->number = markerNum; + mhn->next = root; + root = mhn; + return true; +} + +void MarkerHandleSet::RemoveHandle(int handle) { + MarkerHandleNumber **pmhn = &root; + while (*pmhn) { + MarkerHandleNumber *mhn = *pmhn; + if (mhn->handle == handle) { + *pmhn = mhn->next; + delete mhn; + return ; + } + pmhn = &((*pmhn)->next); + } +} + +bool MarkerHandleSet::RemoveNumber(int markerNum) { + bool performedDeletion = false; + MarkerHandleNumber **pmhn = &root; + while (*pmhn) { + MarkerHandleNumber *mhn = *pmhn; + if (mhn->number == markerNum) { + *pmhn = mhn->next; + delete mhn; + performedDeletion = true; + } else { + pmhn = &((*pmhn)->next); + } + } + return performedDeletion; +} + +void MarkerHandleSet::CombineWith(MarkerHandleSet *other) { + MarkerHandleNumber **pmhn = &root; + while (*pmhn) { + pmhn = &((*pmhn)->next); + } + *pmhn = other->root; + other->root = 0; +} + +LineVector::LineVector() { + linesData = 0; + lines = 0; + size = 0; + levels = 0; + sizeLevels = 0; + handleCurrent = 1; + growSize = 1000; + + Init(); +} + +LineVector::~LineVector() { + for (int line = 0; line < lines; line++) { + delete linesData[line].handleSet; + linesData[line].handleSet = 0; + } + delete []linesData; + linesData = 0; + delete []levels; + levels = 0; +} + +void LineVector::Init() { + for (int line = 0; line < lines; line++) { + delete linesData[line].handleSet; + linesData[line].handleSet = 0; + } + delete []linesData; + linesData = new LineData[static_cast(growSize)]; + size = growSize; + lines = 1; + delete []levels; + levels = 0; + sizeLevels = 0; +} + +void LineVector::Expand(int sizeNew) { + LineData *linesDataNew = new LineData[sizeNew]; + if (linesDataNew) { + for (int i = 0; i < size; i++) + linesDataNew[i] = linesData[i]; + // Do not delete handleSets here as they are transferred to new linesData + delete []linesData; + linesData = linesDataNew; + size = sizeNew; + } else { + Platform::DebugPrintf("No memory available\n"); + // TODO: Blow up + } + +} + +void LineVector::ExpandLevels(int sizeNew) { + if (sizeNew == -1) + sizeNew = size; + int *levelsNew = new int[sizeNew]; + if (levelsNew) { + int i = 0; + for (; i < sizeLevels; i++) + levelsNew[i] = levels[i]; + for (; i < sizeNew; i++) + levelsNew[i] = SC_FOLDLEVELBASE; + delete []levels; + levels = levelsNew; + sizeLevels = sizeNew; + } else { + Platform::DebugPrintf("No memory available\n"); + // TODO: Blow up + } + +} + +void LineVector::ClearLevels() { + delete []levels; + levels = 0; + sizeLevels = 0; +} + +void LineVector::InsertValue(int pos, int value) { + //Platform::DebugPrintf("InsertValue[%d] = %d\n", pos, value); + if ((lines + 2) >= size) { + if (growSize * 6 < size) + growSize *= 2; + Expand(size + growSize); + if (levels) { + ExpandLevels(size + growSize); + } + } + lines++; + for (int i = lines; i > pos; i--) { + linesData[i] = linesData[i - 1]; + } + linesData[pos].startPosition = value; + linesData[pos].handleSet = 0; + if (levels) { + for (int j = lines; j > pos; j--) { + levels[j] = levels[j - 1]; + } + if (pos == 0) { + levels[pos] = SC_FOLDLEVELBASE; + } else if (pos == (lines - 1)) { // Last line will not be a folder + levels[pos] = SC_FOLDLEVELBASE; + } else { + levels[pos] = levels[pos - 1]; + } + } +} + +void LineVector::SetValue(int pos, int value) { + //Platform::DebugPrintf("SetValue[%d] = %d\n", pos, value); + if ((pos + 2) >= size) { + //Platform::DebugPrintf("Resize %d %d\n", size,pos); + Expand(pos + growSize); + //Platform::DebugPrintf("end Resize %d %d\n", size,pos); + lines = pos; + if (levels) { + ExpandLevels(pos + growSize); + } + } + linesData[pos].startPosition = value; +} + +void LineVector::Remove(int pos) { + //Platform::DebugPrintf("Remove %d\n", pos); + // Retain the markers from the deleted line by oring them into the previous line + if (pos > 0) { + MergeMarkers(pos - 1); + } + for (int i = pos; i < lines; i++) { + linesData[i] = linesData[i + 1]; + } + if (levels) { + // Move up following lines but merge header flag from this line + // to line before to avoid a temporary disappearence causing expansion. + int firstHeader = levels[pos] & SC_FOLDLEVELHEADERFLAG; + for (int j = pos; j < lines; j++) { + levels[j] = levels[j + 1]; + } + if (pos > 0) + levels[pos-1] |= firstHeader; + } + lines--; +} + +int LineVector::LineFromPosition(int pos) { + //Platform::DebugPrintf("LineFromPostion %d lines=%d end = %d\n", pos, lines, linesData[lines].startPosition); + if (lines == 0) + return 0; + //Platform::DebugPrintf("LineFromPosition %d\n", pos); + if (pos >= linesData[lines].startPosition) + return lines - 1; + int lower = 0; + int upper = lines; + do { + int middle = (upper + lower + 1) / 2; // Round high + if (pos < linesData[middle].startPosition) { + upper = middle - 1; + } else { + lower = middle; + } + } while (lower < upper); + //Platform::DebugPrintf("LineFromPostion %d %d %d\n", pos, lower, linesData[lower].startPosition, linesData[lower > 1 ? lower - 1 : 0].startPosition); + return lower; +} + +int LineVector::AddMark(int line, int markerNum) { + handleCurrent++; + if (!linesData[line].handleSet) { + // Need new structure to hold marker handle + linesData[line].handleSet = new MarkerHandleSet; + if (!linesData[line].handleSet) + return - 1; + } + linesData[line].handleSet->InsertHandle(handleCurrent, markerNum); + + return handleCurrent; +} + +void LineVector::MergeMarkers(int pos) { + if (linesData[pos + 1].handleSet != NULL) { + if (linesData[pos].handleSet == NULL ) + linesData[pos].handleSet = new MarkerHandleSet; + linesData[pos].handleSet->CombineWith(linesData[pos + 1].handleSet); + delete linesData[pos + 1].handleSet; + linesData[pos + 1].handleSet = NULL; + } +} + +void LineVector::DeleteMark(int line, int markerNum, bool all) { + if (linesData[line].handleSet) { + if (markerNum == -1) { + delete linesData[line].handleSet; + linesData[line].handleSet = 0; + } else { + bool performedDeletion = + linesData[line].handleSet->RemoveNumber(markerNum); + while (all && performedDeletion) { + performedDeletion = + linesData[line].handleSet->RemoveNumber(markerNum); + } + if (linesData[line].handleSet->Length() == 0) { + delete linesData[line].handleSet; + linesData[line].handleSet = 0; + } + } + } +} + +void LineVector::DeleteMarkFromHandle(int markerHandle) { + int line = LineFromHandle(markerHandle); + if (line >= 0) { + linesData[line].handleSet->RemoveHandle(markerHandle); + if (linesData[line].handleSet->Length() == 0) { + delete linesData[line].handleSet; + linesData[line].handleSet = 0; + } + } +} + +int LineVector::LineFromHandle(int markerHandle) { + for (int line = 0; line < lines; line++) { + if (linesData[line].handleSet) { + if (linesData[line].handleSet->Contains(markerHandle)) { + return line; + } + } + } + return - 1; +} + +Action::Action() { + at = startAction; + position = 0; + data = 0; + lenData = 0; +} + +Action::~Action() { + Destroy(); +} + +void Action::Create(actionType at_, int position_, char *data_, int lenData_, bool mayCoalesce_) { + delete []data; + position = position_; + at = at_; + data = data_; + lenData = lenData_; + mayCoalesce = mayCoalesce_; +} + +void Action::Destroy() { + delete []data; + data = 0; +} + +void Action::Grab(Action *source) { + delete []data; + + position = source->position; + at = source->at; + data = source->data; + lenData = source->lenData; + mayCoalesce = source->mayCoalesce; + + // Ownership of source data transferred to this + source->position = 0; + source->at = startAction; + source->data = 0; + source->lenData = 0; + source->mayCoalesce = true; +} + +// The undo history stores a sequence of user operations that represent the user's view of the +// commands executed on the text. +// Each user operation contains a sequence of text insertion and text deletion actions. +// All the user operations are stored in a list of individual actions with 'start' actions used +// as delimiters between user operations. +// Initially there is one start action in the history. +// As each action is performed, it is recorded in the history. The action may either become +// part of the current user operation or may start a new user operation. If it is to be part of the +// current operation, then it overwrites the current last action. If it is to be part of a new +// operation, it is appended after the current last action. +// After writing the new action, a new start action is appended at the end of the history. +// The decision of whether to start a new user operation is based upon two factors. If a +// compound operation has been explicitly started by calling BeginUndoAction and no matching +// EndUndoAction (these calls nest) has been called, then the action is coalesced into the current +// operation. If there is no outstanding BeginUndoAction call then a new operation is started +// unless it looks as if the new action is caused by the user typing or deleting a stream of text. +// Sequences that look like typing or deletion are coalesced into a single user operation. + +UndoHistory::UndoHistory() { + + lenActions = 100; + actions = new Action[lenActions]; + maxAction = 0; + currentAction = 0; + undoSequenceDepth = 0; + savePoint = 0; + + actions[currentAction].Create(startAction); +} + +UndoHistory::~UndoHistory() { + delete []actions; + actions = 0; +} + +void UndoHistory::EnsureUndoRoom() { + // Have to test that there is room for 2 more actions in the array + // as two actions may be created by the calling function + if (currentAction >= (lenActions - 2)) { + // Run out of undo nodes so extend the array + int lenActionsNew = lenActions * 2; + Action *actionsNew = new Action[lenActionsNew]; + if (!actionsNew) + return ; + for (int act = 0; act <= currentAction; act++) + actionsNew[act].Grab(&actions[act]); + delete []actions; + lenActions = lenActionsNew; + actions = actionsNew; + } +} + +void UndoHistory::AppendAction(actionType at, int position, char *data, int lengthData) { + EnsureUndoRoom(); + //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction); + //Platform::DebugPrintf("^ %d action %d %d\n", actions[currentAction - 1].at, + // actions[currentAction - 1].position, actions[currentAction - 1].lenData); + if (currentAction < savePoint) { + savePoint = -1; + } + if (currentAction >= 1) { + if (0 == undoSequenceDepth) { + // Top level actions may not always be coalesced + Action &actPrevious = actions[currentAction - 1]; + // See if current action can be coalesced into previous action + // Will work if both are inserts or deletes and position is same + if (at != actPrevious.at) { + currentAction++; + } else if (currentAction == savePoint) { + currentAction++; + } else if ((at == insertAction) && + (position != (actPrevious.position + actPrevious.lenData))) { + // Insertions must be immediately after to coalesce + currentAction++; + } else if (!actions[currentAction].mayCoalesce) { + // Not allowed to coalesce if this set + currentAction++; + } else if (at == removeAction) { + if ((lengthData == 1) || (lengthData == 2)){ + if ((position + lengthData) == actPrevious.position) { + ; // Backspace -> OK + } else if (position == actPrevious.position) { + ; // Delete -> OK + } else { + // Removals must be at same position to coalesce + currentAction++; + } + } else { + // Removals must be of one character to coalesce + currentAction++; + } + } else { + //Platform::DebugPrintf("action coalesced\n"); + } + + } else { + // Actions not at top level are always coalesced unless this is after return to top level + if (!actions[currentAction].mayCoalesce) + currentAction++; + } + } else { + currentAction++; + } + actions[currentAction].Create(at, position, data, lengthData); + currentAction++; + actions[currentAction].Create(startAction); + maxAction = currentAction; +} + +void UndoHistory::BeginUndoAction() { + EnsureUndoRoom(); + if (undoSequenceDepth == 0) { + if (actions[currentAction].at != startAction) { + currentAction++; + actions[currentAction].Create(startAction); + maxAction = currentAction; + } + actions[currentAction].mayCoalesce = false; + } + undoSequenceDepth++; +} + +void UndoHistory::EndUndoAction() { + EnsureUndoRoom(); + undoSequenceDepth--; + if (0 == undoSequenceDepth) { + if (actions[currentAction].at != startAction) { + currentAction++; + actions[currentAction].Create(startAction); + maxAction = currentAction; + } + actions[currentAction].mayCoalesce = false; + } +} + +void UndoHistory::DropUndoSequence() { + undoSequenceDepth = 0; +} + +void UndoHistory::DeleteUndoHistory() { + for (int i = 1; i < maxAction; i++) + actions[i].Destroy(); + maxAction = 0; + currentAction = 0; + actions[currentAction].Create(startAction); + savePoint = 0; +} + +void UndoHistory::SetSavePoint() { + savePoint = currentAction; +} + +bool UndoHistory::IsSavePoint() const { + return savePoint == currentAction; +} + +bool UndoHistory::CanUndo() const { + return (currentAction > 0) && (maxAction > 0); +} + +int UndoHistory::StartUndo() { + // Drop any trailing startAction + if (actions[currentAction].at == startAction && currentAction > 0) + currentAction--; + + // Count the steps in this action + int act = currentAction; + while (actions[act].at != startAction && act > 0) { + act--; + } + return currentAction - act; +} + +const Action &UndoHistory::GetUndoStep() const { + return actions[currentAction]; +} + +void UndoHistory::CompletedUndoStep() { + currentAction--; +} + +bool UndoHistory::CanRedo() const { + return maxAction > currentAction; +} + +int UndoHistory::StartRedo() { + // Drop any leading startAction + if (actions[currentAction].at == startAction && currentAction < maxAction) + currentAction++; + + // Count the steps in this action + int act = currentAction; + while (actions[act].at != startAction && act < maxAction) { + act++; + } + return act - currentAction; +} + +const Action &UndoHistory::GetRedoStep() const { + return actions[currentAction]; +} + +void UndoHistory::CompletedRedoStep() { + currentAction++; +} + +CellBuffer::CellBuffer(int initialLength) { + body = new char[initialLength]; + size = initialLength; + length = 0; + part1len = 0; + gaplen = initialLength; + part2body = body + gaplen; + readOnly = false; + collectingUndo = true; + growSize = 4000; +} + +CellBuffer::~CellBuffer() { + delete []body; + body = 0; +} + +void CellBuffer::GapTo(int position) { + if (position == part1len) + return ; + if (position < part1len) { + int diff = part1len - position; + //Platform::DebugPrintf("Move gap backwards to %d diff = %d part1len=%d length=%d \n", position,diff, part1len, length); + for (int i = 0; i < diff; i++) + body[part1len + gaplen - i - 1] = body[part1len - i - 1]; + } else { // position > part1len + int diff = position - part1len; + //Platform::DebugPrintf("Move gap forwards to %d diff =%d\n", position,diff); + for (int i = 0; i < diff; i++) + body[part1len + i] = body[part1len + gaplen + i]; + } + part1len = position; + part2body = body + gaplen; +} + +void CellBuffer::RoomFor(int insertionLength) { + //Platform::DebugPrintf("need room %d %d\n", gaplen, insertionLength); + if (gaplen <= insertionLength) { + //Platform::DebugPrintf("need room %d %d\n", gaplen, insertionLength); + if (growSize * 6 < size) + growSize *= 2; + int newSize = size + insertionLength + growSize; + Allocate(newSize); + } +} + +// To make it easier to write code that uses ByteAt, a position outside the range of the buffer +// can be retrieved. All characters outside the range have the value '\0'. +char CellBuffer::ByteAt(int position) { + if (position < part1len) { + if (position < 0) { + return '\0'; + } else { + return body[position]; + } + } else { + if (position >= length) { + return '\0'; + } else { + return part2body[position]; + } + } +} + +void CellBuffer::SetByteAt(int position, char ch) { + + if (position < 0) { + //Platform::DebugPrintf("Bad position %d\n",position); + return ; + } + if (position >= length + 11) { + Platform::DebugPrintf("Very Bad position %d of %d\n", position, length); + //exit(2); + return ; + } + if (position >= length) { + //Platform::DebugPrintf("Bad position %d of %d\n",position,length); + return ; + } + + if (position < part1len) { + body[position] = ch; + } else { + part2body[position] = ch; + } +} + +char CellBuffer::CharAt(int position) { + return ByteAt(position*2); +} + +void CellBuffer::GetCharRange(char *buffer, int position, int lengthRetrieve) { + if (lengthRetrieve < 0) + return ; + if (position < 0) + return ; + int bytePos = position * 2; + if ((bytePos + lengthRetrieve * 2) > length) { + Platform::DebugPrintf("Bad GetCharRange %d for %d of %d\n", bytePos, + lengthRetrieve, length); + return ; + } + GapTo(0); // Move the buffer so its easy to subscript into it + char *pb = part2body + bytePos; + while (lengthRetrieve--) { + *buffer++ = *pb; + pb += 2; + } +} + +char CellBuffer::StyleAt(int position) { + return ByteAt(position*2 + 1); +} + +const char *CellBuffer::InsertString(int position, char *s, int insertLength) { + char *data = 0; + // InsertString and DeleteChars are the bottleneck though which all changes occur + if (!readOnly) { + if (collectingUndo) { + // Save into the undo/redo stack, but only the characters - not the formatting + // This takes up about half load time + data = new char[insertLength / 2]; + for (int i = 0; i < insertLength / 2; i++) { + data[i] = s[i * 2]; + } + uh.AppendAction(insertAction, position / 2, data, insertLength / 2); + } + + BasicInsertString(position, s, insertLength); + } + return data; +} + +bool CellBuffer::SetStyleAt(int position, char style, char mask) { + style &= mask; + char curVal = ByteAt(position * 2 + 1); + if ((curVal & mask) != style) { + SetByteAt(position*2 + 1, static_cast((curVal & ~mask) | style)); + return true; + } else { + return false; + } +} + +bool CellBuffer::SetStyleFor(int position, int lengthStyle, char style, char mask) { + int bytePos = position * 2 + 1; + bool changed = false; + PLATFORM_ASSERT(lengthStyle == 0 || + (lengthStyle > 0 && lengthStyle + position < length)); + while (lengthStyle--) { + char curVal = ByteAt(bytePos); + if ((curVal & mask) != style) { + SetByteAt(bytePos, static_cast((curVal & ~mask) | style)); + changed = true; + } + bytePos += 2; + } + return changed; +} + +const char *CellBuffer::DeleteChars(int position, int deleteLength) { + // InsertString and DeleteChars are the bottleneck though which all changes occur + PLATFORM_ASSERT(deleteLength > 0); + char *data = 0; + if (!readOnly) { + if (collectingUndo) { + // Save into the undo/redo stack, but only the characters - not the formatting + data = new char[deleteLength / 2]; + for (int i = 0; i < deleteLength / 2; i++) { + data[i] = ByteAt(position + i * 2); + } + uh.AppendAction(removeAction, position / 2, data, deleteLength / 2); + } + + BasicDeleteChars(position, deleteLength); + } + return data; +} + +int CellBuffer::ByteLength() { + return length; +} + +int CellBuffer::Length() { + return ByteLength() / 2; +} + +void CellBuffer::Allocate(int newSize) { + if (newSize > length) { + GapTo(length); + char *newBody = new char[newSize]; + memcpy(newBody, body, length); + delete []body; + body = newBody; + gaplen += newSize - size; + part2body = body + gaplen; + size = newSize; + } +} + +int CellBuffer::Lines() { + //Platform::DebugPrintf("Lines = %d\n", lv.lines); + return lv.lines; +} + +int CellBuffer::LineStart(int line) { + if (line < 0) + return 0; + else if (line > lv.lines) + return Length(); + else + return lv.linesData[line].startPosition; +} + +bool CellBuffer::IsReadOnly() { + return readOnly; +} + +void CellBuffer::SetReadOnly(bool set) { + readOnly = set; +} + +void CellBuffer::SetSavePoint() { + uh.SetSavePoint(); +} + +bool CellBuffer::IsSavePoint() { + return uh.IsSavePoint(); +} + +int CellBuffer::AddMark(int line, int markerNum) { + if ((line >= 0) && (line < lv.lines)) { + return lv.AddMark(line, markerNum); + } + return - 1; +} + +void CellBuffer::DeleteMark(int line, int markerNum) { + if ((line >= 0) && (line < lv.lines)) { + lv.DeleteMark(line, markerNum, false); + } +} + +void CellBuffer::DeleteMarkFromHandle(int markerHandle) { + lv.DeleteMarkFromHandle(markerHandle); +} + +int CellBuffer::GetMark(int line) { + if ((line >= 0) && (line < lv.lines) && (lv.linesData[line].handleSet)) + return lv.linesData[line].handleSet->MarkValue(); + return 0; +} + +void CellBuffer::DeleteAllMarks(int markerNum) { + for (int line = 0; line < lv.lines; line++) { + lv.DeleteMark(line, markerNum, true); + } +} + +int CellBuffer::LineFromHandle(int markerHandle) { + return lv.LineFromHandle(markerHandle); +} + +// Without undo + +void CellBuffer::BasicInsertString(int position, char *s, int insertLength) { + //Platform::DebugPrintf("Inserting at %d for %d\n", position, insertLength); + if (insertLength == 0) + return ; + PLATFORM_ASSERT(insertLength > 0); + RoomFor(insertLength); + GapTo(position); + + memcpy(body + part1len, s, insertLength); + length += insertLength; + part1len += insertLength; + gaplen -= insertLength; + part2body = body + gaplen; + + int lineInsert = lv.LineFromPosition(position / 2) + 1; + // Point all the lines after the insertion point further along in the buffer + for (int lineAfter = lineInsert; lineAfter <= lv.lines; lineAfter++) { + lv.linesData[lineAfter].startPosition += insertLength / 2; + } + char chPrev = ' '; + if ((position - 2) >= 0) + chPrev = ByteAt(position - 2); + char chAfter = ' '; + if ((position + insertLength) < length) + chAfter = ByteAt(position + insertLength); + if (chPrev == '\r' && chAfter == '\n') { + //Platform::DebugPrintf("Splitting a crlf pair at %d\n", lineInsert); + // Splitting up a crlf pair at position + lv.InsertValue(lineInsert, position / 2); + lineInsert++; + } + char ch = ' '; + for (int i = 0; i < insertLength; i += 2) { + ch = s[i]; + if (ch == '\r') { + //Platform::DebugPrintf("Inserting cr at %d\n", lineInsert); + lv.InsertValue(lineInsert, (position + i) / 2 + 1); + lineInsert++; + } else if (ch == '\n') { + if (chPrev == '\r') { + //Platform::DebugPrintf("Patching cr before lf at %d\n", lineInsert-1); + // Patch up what was end of line + lv.SetValue(lineInsert - 1, (position + i) / 2 + 1); + } else { + //Platform::DebugPrintf("Inserting lf at %d\n", lineInsert); + lv.InsertValue(lineInsert, (position + i) / 2 + 1); + lineInsert++; + } + } + chPrev = ch; + } + // Joining two lines where last insertion is cr and following text starts with lf + if (chAfter == '\n') { + if (ch == '\r') { + //Platform::DebugPrintf("Joining cr before lf at %d\n", lineInsert-1); + // End of line already in buffer so drop the newly created one + lv.Remove(lineInsert - 1); + } + } +} + +void CellBuffer::BasicDeleteChars(int position, int deleteLength) { + //Platform::DebugPrintf("Deleting at %d for %d\n", position, deleteLength); + if (deleteLength == 0) + return ; + + if ((position == 0) && (deleteLength == length)) { + // If whole buffer is being deleted, faster to reinitialise lines data + // than to delete each line. + //printf("Whole buffer being deleted\n"); + lv.Init(); + } else { + // Have to fix up line positions before doing deletion as looking at text in buffer + // to work out which lines have been removed + + int lineRemove = lv.LineFromPosition(position / 2) + 1; + // Point all the lines after the insertion point further along in the buffer + for (int lineAfter = lineRemove; lineAfter <= lv.lines; lineAfter++) { + lv.linesData[lineAfter].startPosition -= deleteLength / 2; + } + char chPrev = ' '; + if (position >= 2) + chPrev = ByteAt(position - 2); + char chBefore = chPrev; + char chNext = ' '; + if (position < length) + chNext = ByteAt(position); + bool ignoreNL = false; + if (chPrev == '\r' && chNext == '\n') { + //Platform::DebugPrintf("Deleting lf after cr, move line end to cr at %d\n", lineRemove); + // Move back one + lv.SetValue(lineRemove, position / 2); + lineRemove++; + ignoreNL = true; // First \n is not real deletion + } + + char ch = chNext; + for (int i = 0; i < deleteLength; i += 2) { + chNext = ' '; + if ((position + i + 2) < length) + chNext = ByteAt(position + i + 2); + //Platform::DebugPrintf("Deleting %d %x\n", i, ch); + if (ch == '\r') { + if (chNext != '\n') { + //Platform::DebugPrintf("Removing cr end of line\n"); + lv.Remove(lineRemove); + } + } else if (ch == '\n') { + if (ignoreNL) { + ignoreNL = false; // Further \n are real deletions + } else { + //Platform::DebugPrintf("Removing lf end of line\n"); + lv.Remove(lineRemove); + } + } + + ch = chNext; + } + // May have to fix up end if last deletion causes cr to be next to lf + // or removes one of a crlf pair + char chAfter = ' '; + if ((position + deleteLength) < length) + chAfter = ByteAt(position + deleteLength); + if (chBefore == '\r' && chAfter == '\n') { + //d.printf("Joining cr before lf at %d\n", lineRemove); + // Using lineRemove-1 as cr ended line before start of deletion + lv.Remove(lineRemove - 1); + lv.SetValue(lineRemove - 1, position / 2 + 1); + } + } + GapTo(position); + length -= deleteLength; + gaplen += deleteLength; + part2body = body + gaplen; +} + +bool CellBuffer::SetUndoCollection(bool collectUndo) { + collectingUndo = collectUndo; + uh.DropUndoSequence(); + return collectingUndo; +} + +bool CellBuffer::IsCollectingUndo() { + return collectingUndo; +} + +void CellBuffer::BeginUndoAction() { + uh.BeginUndoAction(); +} + +void CellBuffer::EndUndoAction() { + uh.EndUndoAction(); +} + +void CellBuffer::DeleteUndoHistory() { + uh.DeleteUndoHistory(); +} + +bool CellBuffer::CanUndo() { + return uh.CanUndo(); +} + +int CellBuffer::StartUndo() { + return uh.StartUndo(); +} + +const Action &CellBuffer::GetUndoStep() const { + return uh.GetUndoStep(); +} + +void CellBuffer::PerformUndoStep() { + const Action &actionStep = uh.GetUndoStep(); + if (actionStep.at == insertAction) { + BasicDeleteChars(actionStep.position*2, actionStep.lenData*2); + } else if (actionStep.at == removeAction) { + char *styledData = new char[actionStep.lenData * 2]; + for (int i = 0; i < actionStep.lenData; i++) { + styledData[i*2] = actionStep.data[i]; + styledData[i*2 + 1] = 0; + } + BasicInsertString(actionStep.position*2, styledData, actionStep.lenData*2); + delete []styledData; + } + uh.CompletedUndoStep(); +} + +bool CellBuffer::CanRedo() { + return uh.CanRedo(); +} + +int CellBuffer::StartRedo() { + return uh.StartRedo(); +} + +const Action &CellBuffer::GetRedoStep() const { + return uh.GetRedoStep(); +} + +void CellBuffer::PerformRedoStep() { + const Action &actionStep = uh.GetRedoStep(); + if (actionStep.at == insertAction) { + char *styledData = new char[actionStep.lenData * 2]; + for (int i = 0; i < actionStep.lenData; i++) { + styledData[i*2] = actionStep.data[i]; + styledData[i*2 + 1] = 0; + } + BasicInsertString(actionStep.position*2, styledData, actionStep.lenData*2); + delete []styledData; + } else if (actionStep.at == removeAction) { + BasicDeleteChars(actionStep.position*2, actionStep.lenData*2); + } + uh.CompletedRedoStep(); +} + +int CellBuffer::SetLineState(int line, int state) { + int stateOld = lineStates[line]; + lineStates[line] = state; + return stateOld; +} + +int CellBuffer::GetLineState(int line) { + return lineStates[line]; +} + +int CellBuffer::GetMaxLineState() { + return lineStates.Length(); +} + +int CellBuffer::SetLevel(int line, int level) { + int prev = 0; + if ((line >= 0) && (line < lv.lines)) { + if (!lv.levels) { + lv.ExpandLevels(); + } + prev = lv.levels[line]; + if (lv.levels[line] != level) { + lv.levels[line] = level; + } + } + return prev; +} + +int CellBuffer::GetLevel(int line) { + if (lv.levels && (line >= 0) && (line < lv.lines)) { + return lv.levels[line]; + } else { + return SC_FOLDLEVELBASE; + } +} + +void CellBuffer::ClearLevels() { + lv.ClearLevels(); +} diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CellBuffer.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CellBuffer.h new file mode 100644 index 00000000..99db345c --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CellBuffer.h @@ -0,0 +1,250 @@ +// Scintilla source code edit control +/** @file CellBuffer.h + ** Manages the text of the document. + **/ +// Copyright 1998-2004 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef CELLBUFFER_H +#define CELLBUFFER_H + +/** + * This holds the marker identifier and the marker type to display. + * MarkerHandleNumbers are members of lists. + */ +struct MarkerHandleNumber { + int handle; + int number; + MarkerHandleNumber *next; +}; + +/** + * A marker handle set contains any number of MarkerHandleNumbers. + */ +class MarkerHandleSet { + MarkerHandleNumber *root; + +public: + MarkerHandleSet(); + ~MarkerHandleSet(); + int Length(); + int NumberFromHandle(int handle); + int MarkValue(); ///< Bit set of marker numbers. + bool Contains(int handle); + bool InsertHandle(int handle, int markerNum); + void RemoveHandle(int handle); + bool RemoveNumber(int markerNum); + void CombineWith(MarkerHandleSet *other); +}; + +/** + * Each line stores the starting position of the first character of the line in the cell buffer + * and potentially a marker handle set. Often a line will not have any attached markers. + */ +struct LineData { + int startPosition; + MarkerHandleSet *handleSet; + LineData() : startPosition(0), handleSet(0) { + } +}; + +/** + * The line vector contains information about each of the lines in a cell buffer. + */ +class LineVector { +public: + int growSize; + int lines; + LineData *linesData; + int size; + int *levels; + int sizeLevels; + + /// Handles are allocated sequentially and should never have to be reused as 32 bit ints are very big. + int handleCurrent; + + LineVector(); + ~LineVector(); + void Init(); + + void Expand(int sizeNew); + void ExpandLevels(int sizeNew=-1); + void ClearLevels(); + void InsertValue(int pos, int value); + void SetValue(int pos, int value); + void Remove(int pos); + int LineFromPosition(int pos); + + int AddMark(int line, int marker); + void MergeMarkers(int pos); + void DeleteMark(int line, int markerNum, bool all); + void DeleteMarkFromHandle(int markerHandle); + int LineFromHandle(int markerHandle); +}; + +enum actionType { insertAction, removeAction, startAction }; + +/** + * Actions are used to store all the information required to perform one undo/redo step. + */ +class Action { +public: + actionType at; + int position; + char *data; + int lenData; + bool mayCoalesce; + + Action(); + ~Action(); + void Create(actionType at_, int position_=0, char *data_=0, int lenData_=0, bool mayCoalesce_=true); + void Destroy(); + void Grab(Action *source); +}; + +/** + * + */ +class UndoHistory { + Action *actions; + int lenActions; + int maxAction; + int currentAction; + int undoSequenceDepth; + int savePoint; + + void EnsureUndoRoom(); + +public: + UndoHistory(); + ~UndoHistory(); + + void AppendAction(actionType at, int position, char *data, int length); + + void BeginUndoAction(); + void EndUndoAction(); + void DropUndoSequence(); + void DeleteUndoHistory(); + + /// The save point is a marker in the undo stack where the container has stated that + /// the buffer was saved. Undo and redo can move over the save point. + void SetSavePoint(); + bool IsSavePoint() const; + + /// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is + /// called that many times. Similarly for redo. + bool CanUndo() const; + int StartUndo(); + const Action &GetUndoStep() const; + void CompletedUndoStep(); + bool CanRedo() const; + int StartRedo(); + const Action &GetRedoStep() const; + void CompletedRedoStep(); +}; + +/** + * Holder for an expandable array of characters that supports undo and line markers. + * Based on article "Data Structures in a Bit-Mapped Text Editor" + * by Wilfred J. Hansen, Byte January 1987, page 183. + */ +class CellBuffer { +private: + char *body; ///< The cell buffer itself. + int size; ///< Allocated size of the buffer. + int length; ///< Total length of the data. + int part1len; ///< Length of the first part. + int gaplen; ///< Length of the gap between the two parts. + char *part2body; ///< The second part of the cell buffer. + ///< Doesn't point after the gap but set so that + ///< part2body[position] is consistent with body[position]. + bool readOnly; + int growSize; + + bool collectingUndo; + UndoHistory uh; + + LineVector lv; + + SVector lineStates; + + void GapTo(int position); + void RoomFor(int insertionLength); + + inline char ByteAt(int position); + void SetByteAt(int position, char ch); + +public: + + CellBuffer(int initialLength = 4000); + ~CellBuffer(); + + /// Retrieving positions outside the range of the buffer works and returns 0 + char CharAt(int position); + void GetCharRange(char *buffer, int position, int lengthRetrieve); + char StyleAt(int position); + + int ByteLength(); + int Length(); + void Allocate(int newSize); + int Lines(); + int LineStart(int line); + int LineFromPosition(int pos) { return lv.LineFromPosition(pos); } + const char *InsertString(int position, char *s, int insertLength); + + /// Setting styles for positions outside the range of the buffer is safe and has no effect. + /// @return true if the style of a character is changed. + bool SetStyleAt(int position, char style, char mask='\377'); + bool SetStyleFor(int position, int length, char style, char mask); + + const char *DeleteChars(int position, int deleteLength); + + bool IsReadOnly(); + void SetReadOnly(bool set); + + /// The save point is a marker in the undo stack where the container has stated that + /// the buffer was saved. Undo and redo can move over the save point. + void SetSavePoint(); + bool IsSavePoint(); + + /// Line marker functions + int AddMark(int line, int markerNum); + void DeleteMark(int line, int markerNum); + void DeleteMarkFromHandle(int markerHandle); + int GetMark(int line); + void DeleteAllMarks(int markerNum); + int LineFromHandle(int markerHandle); + + /// Actions without undo + void BasicInsertString(int position, char *s, int insertLength); + void BasicDeleteChars(int position, int deleteLength); + + bool SetUndoCollection(bool collectUndo); + bool IsCollectingUndo(); + void BeginUndoAction(); + void EndUndoAction(); + void DeleteUndoHistory(); + + /// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is + /// called that many times. Similarly for redo. + bool CanUndo(); + int StartUndo(); + const Action &GetUndoStep() const; + void PerformUndoStep(); + bool CanRedo(); + int StartRedo(); + const Action &GetRedoStep() const; + void PerformRedoStep(); + + int SetLineState(int line, int state); + int GetLineState(int line); + int GetMaxLineState(); + + int SetLevel(int line, int level); + int GetLevel(int line); + void ClearLevels(); +}; + +#define CELL_SIZE 2 + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CharClassify.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CharClassify.cxx new file mode 100644 index 00000000..76eb905c --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CharClassify.cxx @@ -0,0 +1,43 @@ +// Scintilla source code edit control +/** @file CharClassify.cxx + ** Character classifications used by Document and RESearch. + **/ +// Copyright 2006 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include + +#include "CharClassify.h" + +// Shut up annoying Visual C++ warnings: +#ifdef _MSC_VER +#pragma warning(disable: 4514) +#endif + +CharClassify::CharClassify() { + SetDefaultCharClasses(true); +} + +void CharClassify::SetDefaultCharClasses(bool includeWordClass) { + // Initialize all char classes to default values + for (int ch = 0; ch < 256; ch++) { + if (ch == '\r' || ch == '\n') + charClass[ch] = ccNewLine; + else if (ch < 0x20 || ch == ' ') + charClass[ch] = ccSpace; + else if (includeWordClass && (ch >= 0x80 || isalnum(ch) || ch == '_')) + charClass[ch] = ccWord; + else + charClass[ch] = ccPunctuation; + } +} + +void CharClassify::SetCharClasses(const unsigned char *chars, cc newCharClass) { + // Apply the newCharClass to the specifed chars + if (chars) { + while (*chars) { + charClass[*chars] = static_cast(newCharClass); + chars++; + } + } +} diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CharClassify.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CharClassify.h new file mode 100644 index 00000000..d26f4b34 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/CharClassify.h @@ -0,0 +1,25 @@ +// Scintilla source code edit control +/** @file CharClassify.h + ** Character classifications used by Document and RESearch. + **/ +// Copyright 2006 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef CHARCLASSIFY_H +#define CHARCLASSIFY_H + +class CharClassify { +public: + CharClassify(); + + enum cc { ccSpace, ccNewLine, ccWord, ccPunctuation }; + void SetDefaultCharClasses(bool includeWordClass); + void SetCharClasses(const unsigned char *chars, cc newCharClass); + cc GetClass(unsigned char ch) const { return static_cast(charClass[ch]);} + bool IsWord(unsigned char ch) const { return static_cast(charClass[ch]) == ccWord;} + +private: + enum { maxChar=256 }; + unsigned char charClass[maxChar]; // not type cc to save space +}; +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ContractionState.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ContractionState.cxx new file mode 100644 index 00000000..70ff77a9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ContractionState.cxx @@ -0,0 +1,289 @@ +// Scintilla source code edit control +/** @file ContractionState.cxx + ** Manages visibility of lines for folding. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include "Platform.h" + +#include "ContractionState.h" + +OneLine::OneLine() { + displayLine = 0; + //docLine = 0; + visible = true; + height = 1; + expanded = true; +} + +ContractionState::ContractionState() { + lines = 0; + size = 0; + linesInDoc = 1; + linesInDisplay = 1; + valid = false; + docLines = 0; + sizeDocLines = 0; +} + +ContractionState::~ContractionState() { + Clear(); +} + +void ContractionState::MakeValid() const { + if (!valid) { + // Could be cleverer by keeping the index of the last still valid entry + // rather than invalidating all. + linesInDisplay = 0; + for (int lineInDoc=0; lineInDoc= 0) && (lineDoc < linesInDoc)) { + return lines[lineDoc].displayLine; + } + return -1; +} + +int ContractionState::DocFromDisplay(int lineDisplay) const { + if (lineDisplay <= 0) + return 0; + if (lineDisplay >= linesInDisplay) + return linesInDoc; + if (size == 0) + return lineDisplay; + MakeValid(); + if (docLines) { // Valid allocation + return docLines[lineDisplay]; + } else { + return 0; + } +} + +void ContractionState::Grow(int sizeNew) { + OneLine *linesNew = new OneLine[sizeNew]; + if (linesNew) { + int i = 0; + for (; i < size; i++) { + linesNew[i] = lines[i]; + } + for (; i < sizeNew; i++) { + linesNew[i].displayLine = i; + } + delete []lines; + lines = linesNew; + size = sizeNew; + valid = false; + } else { + Platform::DebugPrintf("No memory available\n"); + // TODO: Blow up + } +} + +void ContractionState::InsertLines(int lineDoc, int lineCount) { + if (size == 0) { + linesInDoc += lineCount; + linesInDisplay += lineCount; + return; + } + //Platform::DebugPrintf("InsertLine[%d] = %d\n", lineDoc); + if ((linesInDoc + lineCount + 2) >= size) { + Grow(linesInDoc + lineCount + growSize); + } + linesInDoc += lineCount; + for (int i = linesInDoc; i >= lineDoc + lineCount; i--) { + lines[i].visible = lines[i - lineCount].visible; + lines[i].height = lines[i - lineCount].height; + linesInDisplay += lines[i].height; + lines[i].expanded = lines[i - lineCount].expanded; + } + for (int d=0;d= 0) && (lineDoc < linesInDoc)) { + return lines[lineDoc].visible; + } else { + return false; + } +} + +bool ContractionState::SetVisible(int lineDocStart, int lineDocEnd, bool visible) { + if (lineDocStart == 0) + lineDocStart++; + if (lineDocStart > lineDocEnd) + return false; + if (size == 0) { + Grow(linesInDoc + growSize); + } + // TODO: modify docLine members to mirror displayLine + int delta = 0; + // Change lineDocs + if ((lineDocStart <= lineDocEnd) && (lineDocStart >= 0) && (lineDocEnd < linesInDoc)) { + for (int line=lineDocStart; line <= lineDocEnd; line++) { + if (lines[line].visible != visible) { + delta += visible ? lines[line].height : -lines[line].height; + lines[line].visible = visible; + } + } + } + linesInDisplay += delta; + valid = false; + return delta != 0; +} + +bool ContractionState::GetExpanded(int lineDoc) const { + if (size == 0) + return true; + if ((lineDoc >= 0) && (lineDoc < linesInDoc)) { + return lines[lineDoc].expanded; + } else { + return false; + } +} + +bool ContractionState::SetExpanded(int lineDoc, bool expanded) { + if (size == 0) { + if (expanded) { + // If in completely expanded state then setting + // one line to expanded has no effect. + return false; + } + Grow(linesInDoc + growSize); + } + if ((lineDoc >= 0) && (lineDoc < linesInDoc)) { + if (lines[lineDoc].expanded != expanded) { + lines[lineDoc].expanded = expanded; + return true; + } + } + return false; +} + +int ContractionState::GetHeight(int lineDoc) const { + if (size == 0) + return 1; + if ((lineDoc >= 0) && (lineDoc < linesInDoc)) { + return lines[lineDoc].height; + } else { + return 1; + } +} + +// Set the number of display lines needed for this line. +// Return true if this is a change. +bool ContractionState::SetHeight(int lineDoc, int height) { + if (lineDoc > linesInDoc) + return false; + if (size == 0) { + if (height == 1) { + // If in completely expanded state then all lines + // assumed to have height of one so no effect here. + return false; + } + Grow(linesInDoc + growSize); + } + if (lines[lineDoc].height != height) { + lines[lineDoc].height = height; + valid = false; + return true; + } else { + return false; + } +} + +void ContractionState::ShowAll() { + delete []lines; + lines = 0; + size = 0; + + delete []docLines; + docLines = 0; + sizeDocLines = 0; + + linesInDisplay = linesInDoc; +} diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ContractionState.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ContractionState.h new file mode 100644 index 00000000..735735b0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ContractionState.h @@ -0,0 +1,65 @@ +// Scintilla source code edit control +/** @file ContractionState.h + ** Manages visibility of lines for folding. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef CONTRACTIONSTATE_H +#define CONTRACTIONSTATE_H + +/** + */ +class OneLine { +public: + int displayLine; ///< Position within set of visible lines + //int docLine; ///< Inverse of @a displayLine + int height; ///< Number of display lines needed to show all of the line + bool visible; + bool expanded; + + OneLine(); + virtual ~OneLine() {} +}; + +/** + */ +class ContractionState { + void Grow(int sizeNew); + enum { growSize = 4000 }; + int linesInDoc; + mutable int linesInDisplay; + mutable OneLine *lines; + int size; + mutable int *docLines; + mutable int sizeDocLines; + mutable bool valid; + void MakeValid() const; + +public: + ContractionState(); + virtual ~ContractionState(); + + void Clear(); + + int LinesInDoc() const; + int LinesDisplayed() const; + int DisplayFromDoc(int lineDoc) const; + int DocFromDisplay(int lineDisplay) const; + + void InsertLines(int lineDoc, int lineCount); + void DeleteLines(int lineDoc, int lineCount); + + bool GetVisible(int lineDoc) const; + bool SetVisible(int lineDocStart, int lineDocEnd, bool visible); + + bool GetExpanded(int lineDoc) const; + bool SetExpanded(int lineDoc, bool expanded); + + int GetHeight(int lineDoc) const; + bool SetHeight(int lineDoc, int height); + + void ShowAll(); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Document.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Document.cxx new file mode 100644 index 00000000..cde8e3e2 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Document.cxx @@ -0,0 +1,1577 @@ +// Scintilla source code edit control +/** @file Document.cxx + ** Text document that handles notifications, DBCS, styling, words and end of line. + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include + +#include "Platform.h" + +#include "Scintilla.h" +#include "SVector.h" +#include "CellBuffer.h" +#include "CharClassify.h" +#include "Document.h" +#include "RESearch.h" + +// This is ASCII specific but is safe with chars >= 0x80 +static inline bool isspacechar(unsigned char ch) { + return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d)); +} + +static inline bool IsPunctuation(char ch) { + return isascii(ch) && ispunct(ch); +} + +static inline bool IsADigit(char ch) { + return isascii(ch) && isdigit(ch); +} + +static inline bool IsLowerCase(char ch) { + return isascii(ch) && islower(ch); +} + +static inline bool IsUpperCase(char ch) { + return isascii(ch) && isupper(ch); +} + +Document::Document() { + refCount = 0; +#ifdef unix + eolMode = SC_EOL_LF; +#else + eolMode = SC_EOL_CRLF; +#endif + dbcsCodePage = 0; + stylingBits = 5; + stylingBitsMask = 0x1F; + stylingMask = 0; + endStyled = 0; + styleClock = 0; + enteredCount = 0; + enteredReadOnlyCount = 0; + tabInChars = 8; + indentInChars = 0; + actualIndentInChars = 8; + useTabs = true; + tabIndents = true; + backspaceUnindents = false; + watchers = 0; + lenWatchers = 0; + + matchesValid = false; + pre = 0; + substituted = 0; +} + +Document::~Document() { + for (int i = 0; i < lenWatchers; i++) { + watchers[i].watcher->NotifyDeleted(this, watchers[i].userData); + } + delete []watchers; + watchers = 0; + lenWatchers = 0; + delete pre; + pre = 0; + delete []substituted; + substituted = 0; +} + +// Increase reference count and return its previous value. +int Document::AddRef() { + return refCount++; +} + +// Decrease reference count and return its previous value. +// Delete the document if reference count reaches zero. +int Document::Release() { + int curRefCount = --refCount; + if (curRefCount == 0) + delete this; + return curRefCount; +} + +void Document::SetSavePoint() { + cb.SetSavePoint(); + NotifySavePoint(true); +} + +int Document::AddMark(int line, int markerNum) { + int prev = cb.AddMark(line, markerNum); + DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line); + mh.line = line; + NotifyModified(mh); + return prev; +} + +void Document::AddMarkSet(int line, int valueSet) { + unsigned int m = valueSet; + for (int i = 0; m; i++, m >>= 1) + if (m & 1) + cb.AddMark(line, i); + DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line); + mh.line = line; + NotifyModified(mh); +} + +void Document::DeleteMark(int line, int markerNum) { + cb.DeleteMark(line, markerNum); + DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line); + mh.line = line; + NotifyModified(mh); +} + +void Document::DeleteMarkFromHandle(int markerHandle) { + cb.DeleteMarkFromHandle(markerHandle); + DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0); + mh.line = -1; + NotifyModified(mh); +} + +void Document::DeleteAllMarks(int markerNum) { + cb.DeleteAllMarks(markerNum); + DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0); + mh.line = -1; + NotifyModified(mh); +} + +int Document::LineStart(int line) { + return cb.LineStart(line); +} + +int Document::LineEnd(int line) { + if (line == LinesTotal() - 1) { + return LineStart(line + 1); + } else { + int position = LineStart(line + 1) - 1; + // When line terminator is CR+LF, may need to go back one more + if ((position > LineStart(line)) && (cb.CharAt(position - 1) == '\r')) { + position--; + } + return position; + } +} + +int Document::LineFromPosition(int pos) { + return cb.LineFromPosition(pos); +} + +int Document::LineEndPosition(int position) { + return LineEnd(LineFromPosition(position)); +} + +int Document::VCHomePosition(int position) { + int line = LineFromPosition(position); + int startPosition = LineStart(line); + int endLine = LineStart(line + 1) - 1; + int startText = startPosition; + while (startText < endLine && (cb.CharAt(startText) == ' ' || cb.CharAt(startText) == '\t' ) ) + startText++; + if (position == startText) + return startPosition; + else + return startText; +} + +int Document::SetLevel(int line, int level) { + int prev = cb.SetLevel(line, level); + if (prev != level) { + DocModification mh(SC_MOD_CHANGEFOLD | SC_MOD_CHANGEMARKER, + LineStart(line), 0, 0, 0); + mh.line = line; + mh.foldLevelNow = level; + mh.foldLevelPrev = prev; + NotifyModified(mh); + } + return prev; +} + +static bool IsSubordinate(int levelStart, int levelTry) { + if (levelTry & SC_FOLDLEVELWHITEFLAG) + return true; + else + return (levelStart & SC_FOLDLEVELNUMBERMASK) < (levelTry & SC_FOLDLEVELNUMBERMASK); +} + +int Document::GetLastChild(int lineParent, int level) { + if (level == -1) + level = GetLevel(lineParent) & SC_FOLDLEVELNUMBERMASK; + int maxLine = LinesTotal(); + int lineMaxSubord = lineParent; + while (lineMaxSubord < maxLine - 1) { + EnsureStyledTo(LineStart(lineMaxSubord + 2)); + if (!IsSubordinate(level, GetLevel(lineMaxSubord + 1))) + break; + lineMaxSubord++; + } + if (lineMaxSubord > lineParent) { + if (level > (GetLevel(lineMaxSubord + 1) & SC_FOLDLEVELNUMBERMASK)) { + // Have chewed up some whitespace that belongs to a parent so seek back + if (GetLevel(lineMaxSubord) & SC_FOLDLEVELWHITEFLAG) { + lineMaxSubord--; + } + } + } + return lineMaxSubord; +} + +int Document::GetFoldParent(int line) { + int level = GetLevel(line) & SC_FOLDLEVELNUMBERMASK; + int lineLook = line - 1; + while ((lineLook > 0) && ( + (!(GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG)) || + ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) >= level)) + ) { + lineLook--; + } + if ((GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG) && + ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) < level)) { + return lineLook; + } else { + return -1; + } +} + +int Document::ClampPositionIntoDocument(int pos) { + return Platform::Clamp(pos, 0, Length()); +} + +bool Document::IsCrLf(int pos) { + if (pos < 0) + return false; + if (pos >= (Length() - 1)) + return false; + return (cb.CharAt(pos) == '\r') && (cb.CharAt(pos + 1) == '\n'); +} + +static const int maxBytesInDBCSCharacter=5; + +int Document::LenChar(int pos) { + if (pos < 0) { + return 1; + } else if (IsCrLf(pos)) { + return 2; + } else if (SC_CP_UTF8 == dbcsCodePage) { + unsigned char ch = static_cast(cb.CharAt(pos)); + if (ch < 0x80) + return 1; + int len = 2; + if (ch >= (0x80 + 0x40 + 0x20)) + len = 3; + int lengthDoc = Length(); + if ((pos + len) > lengthDoc) + return lengthDoc -pos; + else + return len; + } else if (dbcsCodePage) { + char mbstr[maxBytesInDBCSCharacter+1]; + int i; + for (i=0; i= Length()) + return Length(); + + // PLATFORM_ASSERT(pos > 0 && pos < Length()); + if (checkLineEnd && IsCrLf(pos - 1)) { + if (moveDir > 0) + return pos + 1; + else + return pos - 1; + } + + // Not between CR and LF + + if (dbcsCodePage) { + if (SC_CP_UTF8 == dbcsCodePage) { + unsigned char ch = static_cast(cb.CharAt(pos)); + while ((pos > 0) && (pos < Length()) && (ch >= 0x80) && (ch < (0x80 + 0x40))) { + // ch is a trail byte + if (moveDir > 0) + pos++; + else + pos--; + ch = static_cast(cb.CharAt(pos)); + } + } else { + // Anchor DBCS calculations at start of line because start of line can + // not be a DBCS trail byte. + int posCheck = LineStart(LineFromPosition(pos)); + while (posCheck < pos) { + char mbstr[maxBytesInDBCSCharacter+1]; + int i; + for(i=0;i pos) { + if (moveDir > 0) { + return posCheck + mbsize; + } else { + return posCheck; + } + } + posCheck += mbsize; + } + } + } + + return pos; +} + +void Document::ModifiedAt(int pos) { + if (endStyled > pos) + endStyled = pos; +} + +void Document::CheckReadOnly() { + if (cb.IsReadOnly() && enteredReadOnlyCount == 0) { + enteredReadOnlyCount++; + NotifyModifyAttempt(); + enteredReadOnlyCount--; + } +} + +// Document only modified by gateways DeleteChars, InsertStyledString, Undo, Redo, and SetStyleAt. +// SetStyleAt does not change the persistent state of a document + +// Unlike Undo, Redo, and InsertStyledString, the pos argument is a cell number not a char number +bool Document::DeleteChars(int pos, int len) { + if (len == 0) + return false; + if ((pos + len) > Length()) + return false; + CheckReadOnly(); + if (enteredCount != 0) { + return false; + } else { + enteredCount++; + if (!cb.IsReadOnly()) { + NotifyModified( + DocModification( + SC_MOD_BEFOREDELETE | SC_PERFORMED_USER, + pos, len, + 0, 0)); + int prevLinesTotal = LinesTotal(); + bool startSavePoint = cb.IsSavePoint(); + const char *text = cb.DeleteChars(pos * 2, len * 2); + if (startSavePoint && cb.IsCollectingUndo()) + NotifySavePoint(!startSavePoint); + if ((pos < Length()) || (pos == 0)) + ModifiedAt(pos); + else + ModifiedAt(pos-1); + NotifyModified( + DocModification( + SC_MOD_DELETETEXT | SC_PERFORMED_USER, + pos, len, + LinesTotal() - prevLinesTotal, text)); + } + enteredCount--; + } + return !cb.IsReadOnly(); +} + +/** + * Insert a styled string (char/style pairs) with a length. + */ +bool Document::InsertStyledString(int position, char *s, int insertLength) { + CheckReadOnly(); + if (enteredCount != 0) { + return false; + } else { + enteredCount++; + if (!cb.IsReadOnly()) { + NotifyModified( + DocModification( + SC_MOD_BEFOREINSERT | SC_PERFORMED_USER, + position / 2, insertLength / 2, + 0, s)); + int prevLinesTotal = LinesTotal(); + bool startSavePoint = cb.IsSavePoint(); + const char *text = cb.InsertString(position, s, insertLength); + if (startSavePoint && cb.IsCollectingUndo()) + NotifySavePoint(!startSavePoint); + ModifiedAt(position / 2); + NotifyModified( + DocModification( + SC_MOD_INSERTTEXT | SC_PERFORMED_USER, + position / 2, insertLength / 2, + LinesTotal() - prevLinesTotal, text)); + } + enteredCount--; + } + return !cb.IsReadOnly(); +} + +int Document::Undo() { + int newPos = -1; + CheckReadOnly(); + if (enteredCount == 0) { + enteredCount++; + if (!cb.IsReadOnly()) { + bool startSavePoint = cb.IsSavePoint(); + bool multiLine = false; + int steps = cb.StartUndo(); + //Platform::DebugPrintf("Steps=%d\n", steps); + for (int step = 0; step < steps; step++) { + const int prevLinesTotal = LinesTotal(); + const Action &action = cb.GetUndoStep(); + if (action.at == removeAction) { + NotifyModified(DocModification( + SC_MOD_BEFOREINSERT | SC_PERFORMED_UNDO, action)); + } else { + NotifyModified(DocModification( + SC_MOD_BEFOREDELETE | SC_PERFORMED_UNDO, action)); + } + cb.PerformUndoStep(); + int cellPosition = action.position; + ModifiedAt(cellPosition); + newPos = cellPosition; + + int modFlags = SC_PERFORMED_UNDO; + // With undo, an insertion action becomes a deletion notification + if (action.at == removeAction) { + newPos += action.lenData; + modFlags |= SC_MOD_INSERTTEXT; + } else { + modFlags |= SC_MOD_DELETETEXT; + } + if (steps > 1) + modFlags |= SC_MULTISTEPUNDOREDO; + const int linesAdded = LinesTotal() - prevLinesTotal; + if (linesAdded != 0) + multiLine = true; + if (step == steps - 1) { + modFlags |= SC_LASTSTEPINUNDOREDO; + if (multiLine) + modFlags |= SC_MULTILINEUNDOREDO; + } + NotifyModified(DocModification(modFlags, cellPosition, action.lenData, + linesAdded, action.data)); + } + + bool endSavePoint = cb.IsSavePoint(); + if (startSavePoint != endSavePoint) + NotifySavePoint(endSavePoint); + } + enteredCount--; + } + return newPos; +} + +int Document::Redo() { + int newPos = -1; + CheckReadOnly(); + if (enteredCount == 0) { + enteredCount++; + if (!cb.IsReadOnly()) { + bool startSavePoint = cb.IsSavePoint(); + bool multiLine = false; + int steps = cb.StartRedo(); + for (int step = 0; step < steps; step++) { + const int prevLinesTotal = LinesTotal(); + const Action &action = cb.GetRedoStep(); + if (action.at == insertAction) { + NotifyModified(DocModification( + SC_MOD_BEFOREINSERT | SC_PERFORMED_REDO, action)); + } else { + NotifyModified(DocModification( + SC_MOD_BEFOREDELETE | SC_PERFORMED_REDO, action)); + } + cb.PerformRedoStep(); + ModifiedAt(action.position); + newPos = action.position; + + int modFlags = SC_PERFORMED_REDO; + if (action.at == insertAction) { + newPos += action.lenData; + modFlags |= SC_MOD_INSERTTEXT; + } else { + modFlags |= SC_MOD_DELETETEXT; + } + if (steps > 1) + modFlags |= SC_MULTISTEPUNDOREDO; + const int linesAdded = LinesTotal() - prevLinesTotal; + if (linesAdded != 0) + multiLine = true; + if (step == steps - 1) { + modFlags |= SC_LASTSTEPINUNDOREDO; + if (multiLine) + modFlags |= SC_MULTILINEUNDOREDO; + } + NotifyModified( + DocModification(modFlags, action.position, action.lenData, + linesAdded, action.data)); + } + + bool endSavePoint = cb.IsSavePoint(); + if (startSavePoint != endSavePoint) + NotifySavePoint(endSavePoint); + } + enteredCount--; + } + return newPos; +} + +/** + * Insert a single character. + */ +bool Document::InsertChar(int pos, char ch) { + char chs[2]; + chs[0] = ch; + chs[1] = 0; + return InsertStyledString(pos*2, chs, 2); +} + +/** + * Insert a null terminated string. + */ +bool Document::InsertString(int position, const char *s) { + return InsertString(position, s, strlen(s)); +} + +/** + * Insert a string with a length. + */ +bool Document::InsertString(int position, const char *s, size_t insertLength) { + bool changed = false; + if (insertLength > 0) { + char *sWithStyle = new char[insertLength * 2]; + if (sWithStyle) { + for (size_t i = 0; i < insertLength; i++) { + sWithStyle[i*2] = s[i]; + sWithStyle[i*2 + 1] = 0; + } + changed = InsertStyledString(position*2, sWithStyle, + static_cast(insertLength*2)); + delete []sWithStyle; + } + } + return changed; +} + +void Document::ChangeChar(int pos, char ch) { + DeleteChars(pos, 1); + InsertChar(pos, ch); +} + +void Document::DelChar(int pos) { + DeleteChars(pos, LenChar(pos)); +} + +void Document::DelCharBack(int pos) { + if (pos <= 0) { + return; + } else if (IsCrLf(pos - 2)) { + DeleteChars(pos - 2, 2); + } else if (dbcsCodePage) { + int startChar = MovePositionOutsideChar(pos - 1, -1, false); + DeleteChars(startChar, pos - startChar); + } else { + DeleteChars(pos - 1, 1); + } +} + +static bool isindentchar(char ch) { + return (ch == ' ') || (ch == '\t'); +} + +static int NextTab(int pos, int tabSize) { + return ((pos / tabSize) + 1) * tabSize; +} + +static void CreateIndentation(char *linebuf, int length, int indent, int tabSize, bool insertSpaces) { + length--; // ensure space for \0 + if (!insertSpaces) { + while ((indent >= tabSize) && (length > 0)) { + *linebuf++ = '\t'; + indent -= tabSize; + length--; + } + } + while ((indent > 0) && (length > 0)) { + *linebuf++ = ' '; + indent--; + length--; + } + *linebuf = '\0'; +} + +int Document::GetLineIndentation(int line) { + int indent = 0; + if ((line >= 0) && (line < LinesTotal())) { + int lineStart = LineStart(line); + int length = Length(); + for (int i = lineStart;i < length;i++) { + char ch = cb.CharAt(i); + if (ch == ' ') + indent++; + else if (ch == '\t') + indent = NextTab(indent, tabInChars); + else + return indent; + } + } + return indent; +} + +void Document::SetLineIndentation(int line, int indent) { + int indentOfLine = GetLineIndentation(line); + if (indent < 0) + indent = 0; + if (indent != indentOfLine) { + char linebuf[1000]; + CreateIndentation(linebuf, sizeof(linebuf), indent, tabInChars, !useTabs); + int thisLineStart = LineStart(line); + int indentPos = GetLineIndentPosition(line); + BeginUndoAction(); + DeleteChars(thisLineStart, indentPos - thisLineStart); + InsertString(thisLineStart, linebuf); + EndUndoAction(); + } +} + +int Document::GetLineIndentPosition(int line) { + if (line < 0) + return 0; + int pos = LineStart(line); + int length = Length(); + while ((pos < length) && isindentchar(cb.CharAt(pos))) { + pos++; + } + return pos; +} + +int Document::GetColumn(int pos) { + int column = 0; + int line = LineFromPosition(pos); + if ((line >= 0) && (line < LinesTotal())) { + for (int i = LineStart(line);i < pos;) { + char ch = cb.CharAt(i); + if (ch == '\t') { + column = NextTab(column, tabInChars); + i++; + } else if (ch == '\r') { + return column; + } else if (ch == '\n') { + return column; + } else { + column++; + i = MovePositionOutsideChar(i + 1, 1); + } + } + } + return column; +} + +int Document::FindColumn(int line, int column) { + int position = LineStart(line); + int columnCurrent = 0; + if ((line >= 0) && (line < LinesTotal())) { + while ((columnCurrent < column) && (position < Length())) { + char ch = cb.CharAt(position); + if (ch == '\t') { + columnCurrent = NextTab(columnCurrent, tabInChars); + position++; + } else if (ch == '\r') { + return position; + } else if (ch == '\n') { + return position; + } else { + columnCurrent++; + position = MovePositionOutsideChar(position + 1, 1); + } + } + } + return position; +} + +void Document::Indent(bool forwards, int lineBottom, int lineTop) { + // Dedent - suck white space off the front of the line to dedent by equivalent of a tab + for (int line = lineBottom; line >= lineTop; line--) { + int indentOfLine = GetLineIndentation(line); + if (forwards) { + if (LineStart(line) < LineEnd(line)) { + SetLineIndentation(line, indentOfLine + IndentSize()); + } + } else { + SetLineIndentation(line, indentOfLine - IndentSize()); + } + } +} + +// Convert line endings for a piece of text to a particular mode. +// Stop at len or when a NUL is found. +// Caller must delete the returned pointer. +char *Document::TransformLineEnds(int *pLenOut, const char *s, size_t len, int eolMode) { + char *dest = new char[2 * len + 1]; + const char *sptr = s; + char *dptr = dest; + for (size_t i = 0; (i < len) && (*sptr != '\0'); i++) { + if (*sptr == '\n' || *sptr == '\r') { + if (eolMode == SC_EOL_CR) { + *dptr++ = '\r'; + } else if (eolMode == SC_EOL_LF) { + *dptr++ = '\n'; + } else { // eolMode == SC_EOL_CRLF + *dptr++ = '\r'; + *dptr++ = '\n'; + } + if ((*sptr == '\r') && (i+1 < len) && (*(sptr+1) == '\n')) { + i++; + sptr++; + } + sptr++; + } else { + *dptr++ = *sptr++; + } + } + *dptr++ = '\0'; + *pLenOut = (dptr - dest) - 1; + return dest; +} + +void Document::ConvertLineEnds(int eolModeSet) { + BeginUndoAction(); + + for (int pos = 0; pos < Length(); pos++) { + if (cb.CharAt(pos) == '\r') { + if (cb.CharAt(pos + 1) == '\n') { + // CRLF + if (eolModeSet == SC_EOL_CR) { + DeleteChars(pos + 1, 1); // Delete the LF + } else if (eolModeSet == SC_EOL_LF) { + DeleteChars(pos, 1); // Delete the CR + } else { + pos++; + } + } else { + // CR + if (eolModeSet == SC_EOL_CRLF) { + InsertString(pos + 1, "\n", 1); // Insert LF + pos++; + } else if (eolModeSet == SC_EOL_LF) { + InsertString(pos, "\n", 1); // Insert LF + DeleteChars(pos + 1, 1); // Delete CR + } + } + } else if (cb.CharAt(pos) == '\n') { + // LF + if (eolModeSet == SC_EOL_CRLF) { + InsertString(pos, "\r", 1); // Insert CR + pos++; + } else if (eolModeSet == SC_EOL_CR) { + InsertString(pos, "\r", 1); // Insert CR + DeleteChars(pos + 1, 1); // Delete LF + } + } + } + + EndUndoAction(); +} + +bool Document::IsWhiteLine(int line) { + int currentChar = LineStart(line); + int endLine = LineEnd(line); + while (currentChar < endLine) { + if (cb.CharAt(currentChar) != ' ' && cb.CharAt(currentChar) != '\t') { + return false; + } + ++currentChar; + } + return true; +} + +int Document::ParaUp(int pos) { + int line = LineFromPosition(pos); + line--; + while (line >= 0 && IsWhiteLine(line)) { // skip empty lines + line--; + } + while (line >= 0 && !IsWhiteLine(line)) { // skip non-empty lines + line--; + } + line++; + return LineStart(line); +} + +int Document::ParaDown(int pos) { + int line = LineFromPosition(pos); + while (line < LinesTotal() && !IsWhiteLine(line)) { // skip non-empty lines + line++; + } + while (line < LinesTotal() && IsWhiteLine(line)) { // skip empty lines + line++; + } + if (line < LinesTotal()) + return LineStart(line); + else // end of a document + return LineEnd(line-1); +} + +CharClassify::cc Document::WordCharClass(unsigned char ch) { + if ((SC_CP_UTF8 == dbcsCodePage) && (ch >= 0x80)) + return CharClassify::ccWord; + return charClass.GetClass(ch); +} + +/** + * Used by commmands that want to select whole words. + * Finds the start of word at pos when delta < 0 or the end of the word when delta >= 0. + */ +int Document::ExtendWordSelect(int pos, int delta, bool onlyWordCharacters) { + CharClassify::cc ccStart = CharClassify::ccWord; + if (delta < 0) { + if (!onlyWordCharacters) + ccStart = WordCharClass(cb.CharAt(pos-1)); + while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart)) + pos--; + } else { + if (!onlyWordCharacters) + ccStart = WordCharClass(cb.CharAt(pos)); + while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart)) + pos++; + } + return MovePositionOutsideChar(pos, delta); +} + +/** + * Find the start of the next word in either a forward (delta >= 0) or backwards direction + * (delta < 0). + * This is looking for a transition between character classes although there is also some + * additional movement to transit white space. + * Used by cursor movement by word commands. + */ +int Document::NextWordStart(int pos, int delta) { + if (delta < 0) { + while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == CharClassify::ccSpace)) + pos--; + if (pos > 0) { + CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos-1)); + while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart)) { + pos--; + } + } + } else { + CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos)); + while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart)) + pos++; + while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == CharClassify::ccSpace)) + pos++; + } + return pos; +} + +/** + * Find the end of the next word in either a forward (delta >= 0) or backwards direction + * (delta < 0). + * This is looking for a transition between character classes although there is also some + * additional movement to transit white space. + * Used by cursor movement by word commands. + */ +int Document::NextWordEnd(int pos, int delta) { + if (delta < 0) { + if (pos > 0) { + CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos-1)); + if (ccStart != CharClassify::ccSpace) { + while (pos > 0 && WordCharClass(cb.CharAt(pos - 1)) == ccStart) { + pos--; + } + } + while (pos > 0 && WordCharClass(cb.CharAt(pos - 1)) == CharClassify::ccSpace) { + pos--; + } + } + } else { + while (pos < Length() && WordCharClass(cb.CharAt(pos)) == CharClassify::ccSpace) { + pos++; + } + if (pos < Length()) { + CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos)); + while (pos < Length() && WordCharClass(cb.CharAt(pos)) == ccStart) { + pos++; + } + } + } + return pos; +} + +/** + * Check that the character at the given position is a word or punctuation character and that + * the previous character is of a different character class. + */ +bool Document::IsWordStartAt(int pos) { + if (pos > 0) { + CharClassify::cc ccPos = WordCharClass(CharAt(pos)); + return (ccPos == CharClassify::ccWord || ccPos == CharClassify::ccPunctuation) && + (ccPos != WordCharClass(CharAt(pos - 1))); + } + return true; +} + +/** + * Check that the character at the given position is a word or punctuation character and that + * the next character is of a different character class. + */ +bool Document::IsWordEndAt(int pos) { + if (pos < Length()) { + CharClassify::cc ccPrev = WordCharClass(CharAt(pos-1)); + return (ccPrev == CharClassify::ccWord || ccPrev == CharClassify::ccPunctuation) && + (ccPrev != WordCharClass(CharAt(pos))); + } + return true; +} + +/** + * Check that the given range is has transitions between character classes at both + * ends and where the characters on the inside are word or punctuation characters. + */ +bool Document::IsWordAt(int start, int end) { + return IsWordStartAt(start) && IsWordEndAt(end); +} + +// The comparison and case changing functions here assume ASCII +// or extended ASCII such as the normal Windows code page. + +static inline char MakeUpperCase(char ch) { + if (ch < 'a' || ch > 'z') + return ch; + else + return static_cast(ch - 'a' + 'A'); +} + +static inline char MakeLowerCase(char ch) { + if (ch < 'A' || ch > 'Z') + return ch; + else + return static_cast(ch - 'A' + 'a'); +} + +// Define a way for the Regular Expression code to access the document +class DocumentIndexer : public CharacterIndexer { + Document *pdoc; + int end; +public: + DocumentIndexer(Document *pdoc_, int end_) : + pdoc(pdoc_), end(end_) { + } + + virtual ~DocumentIndexer() { + } + + virtual char CharAt(int index) { + if (index < 0 || index >= end) + return 0; + else + return pdoc->CharAt(index); + } +}; + +/** + * Find text in document, supporting both forward and backward + * searches (just pass minPos > maxPos to do a backward search) + * Has not been tested with backwards DBCS searches yet. + */ +long Document::FindText(int minPos, int maxPos, const char *s, + bool caseSensitive, bool word, bool wordStart, bool regExp, bool posix, + int *length) { + if (regExp) { + if (!pre) + pre = new RESearch(&charClass); + if (!pre) + return -1; + + int increment = (minPos <= maxPos) ? 1 : -1; + + int startPos = minPos; + int endPos = maxPos; + + // Range endpoints should not be inside DBCS characters, but just in case, move them. + startPos = MovePositionOutsideChar(startPos, 1, false); + endPos = MovePositionOutsideChar(endPos, 1, false); + + const char *errmsg = pre->Compile(s, *length, caseSensitive, posix); + if (errmsg) { + return -1; + } + // Find a variable in a property file: \$(\([A-Za-z0-9_.]+\)) + // Replace first '.' with '-' in each property file variable reference: + // Search: \$(\([A-Za-z0-9_-]+\)\.\([A-Za-z0-9_.]+\)) + // Replace: $(\1-\2) + int lineRangeStart = LineFromPosition(startPos); + int lineRangeEnd = LineFromPosition(endPos); + if ((increment == 1) && + (startPos >= LineEnd(lineRangeStart)) && + (lineRangeStart < lineRangeEnd)) { + // the start position is at end of line or between line end characters. + lineRangeStart++; + startPos = LineStart(lineRangeStart); + } + int pos = -1; + int lenRet = 0; + char searchEnd = s[*length - 1]; + int lineRangeBreak = lineRangeEnd + increment; + for (int line = lineRangeStart; line != lineRangeBreak; line += increment) { + int startOfLine = LineStart(line); + int endOfLine = LineEnd(line); + if (increment == 1) { + if (line == lineRangeStart) { + if ((startPos != startOfLine) && (s[0] == '^')) + continue; // Can't match start of line if start position after start of line + startOfLine = startPos; + } + if (line == lineRangeEnd) { + if ((endPos != endOfLine) && (searchEnd == '$')) + continue; // Can't match end of line if end position before end of line + endOfLine = endPos; + } + } else { + if (line == lineRangeEnd) { + if ((endPos != startOfLine) && (s[0] == '^')) + continue; // Can't match start of line if end position after start of line + startOfLine = endPos; + } + if (line == lineRangeStart) { + if ((startPos != endOfLine) && (searchEnd == '$')) + continue; // Can't match end of line if start position before end of line + endOfLine = startPos; + } + } + + DocumentIndexer di(this, endOfLine); + int success = pre->Execute(di, startOfLine, endOfLine); + if (success) { + pos = pre->bopat[0]; + lenRet = pre->eopat[0] - pre->bopat[0]; + if (increment == -1) { + // Check for the last match on this line. + int repetitions = 1000; // Break out of infinite loop + while (success && (pre->eopat[0] <= endOfLine) && (repetitions--)) { + success = pre->Execute(di, pos+1, endOfLine); + if (success) { + if (pre->eopat[0] <= minPos) { + pos = pre->bopat[0]; + lenRet = pre->eopat[0] - pre->bopat[0]; + } else { + success = 0; + } + } + } + } + break; + } + } + *length = lenRet; + return pos; + + } else { + + bool forward = minPos <= maxPos; + int increment = forward ? 1 : -1; + + // Range endpoints should not be inside DBCS characters, but just in case, move them. + int startPos = MovePositionOutsideChar(minPos, increment, false); + int endPos = MovePositionOutsideChar(maxPos, increment, false); + + // Compute actual search ranges needed + int lengthFind = *length; + if (lengthFind == -1) + lengthFind = static_cast(strlen(s)); + int endSearch = endPos; + if (startPos <= endPos) { + endSearch = endPos - lengthFind + 1; + } + //Platform::DebugPrintf("Find %d %d %s %d\n", startPos, endPos, ft->lpstrText, lengthFind); + char firstChar = s[0]; + if (!caseSensitive) + firstChar = static_cast(MakeUpperCase(firstChar)); + int pos = forward ? startPos : (startPos - 1); + while (forward ? (pos < endSearch) : (pos >= endSearch)) { + char ch = CharAt(pos); + if (caseSensitive) { + if (ch == firstChar) { + bool found = true; + if (pos + lengthFind > Platform::Maximum(startPos, endPos)) found = false; + for (int posMatch = 1; posMatch < lengthFind && found; posMatch++) { + ch = CharAt(pos + posMatch); + if (ch != s[posMatch]) + found = false; + } + if (found) { + if ((!word && !wordStart) || + word && IsWordAt(pos, pos + lengthFind) || + wordStart && IsWordStartAt(pos)) + return pos; + } + } + } else { + if (MakeUpperCase(ch) == firstChar) { + bool found = true; + if (pos + lengthFind > Platform::Maximum(startPos, endPos)) found = false; + for (int posMatch = 1; posMatch < lengthFind && found; posMatch++) { + ch = CharAt(pos + posMatch); + if (MakeUpperCase(ch) != MakeUpperCase(s[posMatch])) + found = false; + } + if (found) { + if ((!word && !wordStart) || + word && IsWordAt(pos, pos + lengthFind) || + wordStart && IsWordStartAt(pos)) + return pos; + } + } + } + pos += increment; + if (dbcsCodePage && (pos >= 0)) { + // Ensure trying to match from start of character + pos = MovePositionOutsideChar(pos, increment, false); + } + } + } + //Platform::DebugPrintf("Not found\n"); + return -1; +} + +const char *Document::SubstituteByPosition(const char *text, int *length) { + if (!pre) + return 0; + delete []substituted; + substituted = 0; + DocumentIndexer di(this, Length()); + if (!pre->GrabMatches(di)) + return 0; + unsigned int lenResult = 0; + for (int i = 0; i < *length; i++) { + if (text[i] == '\\') { + if (text[i + 1] >= '1' && text[i + 1] <= '9') { + unsigned int patNum = text[i + 1] - '0'; + lenResult += pre->eopat[patNum] - pre->bopat[patNum]; + i++; + } else { + switch (text[i + 1]) { + case 'a': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + i++; + } + lenResult++; + } + } else { + lenResult++; + } + } + substituted = new char[lenResult + 1]; + if (!substituted) + return 0; + char *o = substituted; + for (int j = 0; j < *length; j++) { + if (text[j] == '\\') { + if (text[j + 1] >= '1' && text[j + 1] <= '9') { + unsigned int patNum = text[j + 1] - '0'; + unsigned int len = pre->eopat[patNum] - pre->bopat[patNum]; + if (pre->pat[patNum]) // Will be null if try for a match that did not occur + memcpy(o, pre->pat[patNum], len); + o += len; + j++; + } else { + j++; + switch (text[j]) { + case 'a': + *o++ = '\a'; + break; + case 'b': + *o++ = '\b'; + break; + case 'f': + *o++ = '\f'; + break; + case 'n': + *o++ = '\n'; + break; + case 'r': + *o++ = '\r'; + break; + case 't': + *o++ = '\t'; + break; + case 'v': + *o++ = '\v'; + break; + default: + *o++ = '\\'; + j--; + } + } + } else { + *o++ = text[j]; + } + } + *o = '\0'; + *length = lenResult; + return substituted; +} + +int Document::LinesTotal() { + return cb.Lines(); +} + +void Document::ChangeCase(Range r, bool makeUpperCase) { + for (int pos = r.start; pos < r.end;) { + int len = LenChar(pos); + if (len == 1) { + char ch = CharAt(pos); + if (makeUpperCase) { + if (IsLowerCase(ch)) { + ChangeChar(pos, static_cast(MakeUpperCase(ch))); + } + } else { + if (IsUpperCase(ch)) { + ChangeChar(pos, static_cast(MakeLowerCase(ch))); + } + } + } + pos += len; + } +} + +void Document::SetDefaultCharClasses(bool includeWordClass) { + charClass.SetDefaultCharClasses(includeWordClass); +} + +void Document::SetCharClasses(const unsigned char *chars, CharClassify::cc newCharClass) { + charClass.SetCharClasses(chars, newCharClass); +} + +void Document::SetStylingBits(int bits) { + stylingBits = bits; + stylingBitsMask = 0; + for (int bit = 0; bit < stylingBits; bit++) { + stylingBitsMask <<= 1; + stylingBitsMask |= 1; + } +} + +void Document::StartStyling(int position, char mask) { + stylingMask = mask; + endStyled = position; +} + +bool Document::SetStyleFor(int length, char style) { + if (enteredCount != 0) { + return false; + } else { + enteredCount++; + style &= stylingMask; + int prevEndStyled = endStyled; + if (cb.SetStyleFor(endStyled, length, style, stylingMask)) { + DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER, + prevEndStyled, length); + NotifyModified(mh); + } + endStyled += length; + enteredCount--; + return true; + } +} + +bool Document::SetStyles(int length, char *styles) { + if (enteredCount != 0) { + return false; + } else { + enteredCount++; + bool didChange = false; + int startMod = 0; + int endMod = 0; + for (int iPos = 0; iPos < length; iPos++, endStyled++) { + PLATFORM_ASSERT(endStyled < Length()); + if (cb.SetStyleAt(endStyled, styles[iPos], stylingMask)) { + if (!didChange) { + startMod = endStyled; + } + didChange = true; + endMod = endStyled; + } + } + if (didChange) { + DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER, + startMod, endMod - startMod + 1); + NotifyModified(mh); + } + enteredCount--; + return true; + } +} + +bool Document::EnsureStyledTo(int pos) { + if (pos > GetEndStyled()) { + IncrementStyleClock(); + // Ask the watchers to style, and stop as soon as one responds. + for (int i = 0; pos > GetEndStyled() && i < lenWatchers; i++) { + watchers[i].watcher->NotifyStyleNeeded(this, watchers[i].userData, pos); + } + } + return pos <= GetEndStyled(); +} + +void Document::IncrementStyleClock() { + styleClock++; + if (styleClock > 0x100000) { + styleClock = 0; + } +} + +bool Document::AddWatcher(DocWatcher *watcher, void *userData) { + for (int i = 0; i < lenWatchers; i++) { + if ((watchers[i].watcher == watcher) && + (watchers[i].userData == userData)) + return false; + } + WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers + 1]; + if (!pwNew) + return false; + for (int j = 0; j < lenWatchers; j++) + pwNew[j] = watchers[j]; + pwNew[lenWatchers].watcher = watcher; + pwNew[lenWatchers].userData = userData; + delete []watchers; + watchers = pwNew; + lenWatchers++; + return true; +} + +bool Document::RemoveWatcher(DocWatcher *watcher, void *userData) { + for (int i = 0; i < lenWatchers; i++) { + if ((watchers[i].watcher == watcher) && + (watchers[i].userData == userData)) { + if (lenWatchers == 1) { + delete []watchers; + watchers = 0; + lenWatchers = 0; + } else { + WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers]; + if (!pwNew) + return false; + for (int j = 0; j < lenWatchers - 1; j++) { + pwNew[j] = (j < i) ? watchers[j] : watchers[j + 1]; + } + delete []watchers; + watchers = pwNew; + lenWatchers--; + } + return true; + } + } + return false; +} + +void Document::NotifyModifyAttempt() { + for (int i = 0; i < lenWatchers; i++) { + watchers[i].watcher->NotifyModifyAttempt(this, watchers[i].userData); + } +} + +void Document::NotifySavePoint(bool atSavePoint) { + for (int i = 0; i < lenWatchers; i++) { + watchers[i].watcher->NotifySavePoint(this, watchers[i].userData, atSavePoint); + } +} + +void Document::NotifyModified(DocModification mh) { + for (int i = 0; i < lenWatchers; i++) { + watchers[i].watcher->NotifyModified(this, mh, watchers[i].userData); + } +} + +bool Document::IsWordPartSeparator(char ch) { + return (WordCharClass(ch) == CharClassify::ccWord) && IsPunctuation(ch); +} + +int Document::WordPartLeft(int pos) { + if (pos > 0) { + --pos; + char startChar = cb.CharAt(pos); + if (IsWordPartSeparator(startChar)) { + while (pos > 0 && IsWordPartSeparator(cb.CharAt(pos))) { + --pos; + } + } + if (pos > 0) { + startChar = cb.CharAt(pos); + --pos; + if (IsLowerCase(startChar)) { + while (pos > 0 && IsLowerCase(cb.CharAt(pos))) + --pos; + if (!IsUpperCase(cb.CharAt(pos)) && !IsLowerCase(cb.CharAt(pos))) + ++pos; + } else if (IsUpperCase(startChar)) { + while (pos > 0 && IsUpperCase(cb.CharAt(pos))) + --pos; + if (!IsUpperCase(cb.CharAt(pos))) + ++pos; + } else if (IsADigit(startChar)) { + while (pos > 0 && IsADigit(cb.CharAt(pos))) + --pos; + if (!IsADigit(cb.CharAt(pos))) + ++pos; + } else if (IsPunctuation(startChar)) { + while (pos > 0 && IsPunctuation(cb.CharAt(pos))) + --pos; + if (!IsPunctuation(cb.CharAt(pos))) + ++pos; + } else if (isspacechar(startChar)) { + while (pos > 0 && isspacechar(cb.CharAt(pos))) + --pos; + if (!isspacechar(cb.CharAt(pos))) + ++pos; + } else if (!isascii(startChar)) { + while (pos > 0 && !isascii(cb.CharAt(pos))) + --pos; + if (isascii(cb.CharAt(pos))) + ++pos; + } else { + ++pos; + } + } + } + return pos; +} + +int Document::WordPartRight(int pos) { + char startChar = cb.CharAt(pos); + int length = Length(); + if (IsWordPartSeparator(startChar)) { + while (pos < length && IsWordPartSeparator(cb.CharAt(pos))) + ++pos; + startChar = cb.CharAt(pos); + } + if (!isascii(startChar)) { + while (pos < length && !isascii(cb.CharAt(pos))) + ++pos; + } else if (IsLowerCase(startChar)) { + while (pos < length && IsLowerCase(cb.CharAt(pos))) + ++pos; + } else if (IsUpperCase(startChar)) { + if (IsLowerCase(cb.CharAt(pos + 1))) { + ++pos; + while (pos < length && IsLowerCase(cb.CharAt(pos))) + ++pos; + } else { + while (pos < length && IsUpperCase(cb.CharAt(pos))) + ++pos; + } + if (IsLowerCase(cb.CharAt(pos)) && IsUpperCase(cb.CharAt(pos - 1))) + --pos; + } else if (IsADigit(startChar)) { + while (pos < length && IsADigit(cb.CharAt(pos))) + ++pos; + } else if (IsPunctuation(startChar)) { + while (pos < length && IsPunctuation(cb.CharAt(pos))) + ++pos; + } else if (isspacechar(startChar)) { + while (pos < length && isspacechar(cb.CharAt(pos))) + ++pos; + } else { + ++pos; + } + return pos; +} + +bool IsLineEndChar(char c) { + return (c == '\n' || c == '\r'); +} + +int Document::ExtendStyleRange(int pos, int delta, bool singleLine) { + int sStart = cb.StyleAt(pos); + if (delta < 0) { + while (pos > 0 && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))) ) + pos--; + pos++; + } else { + while (pos < (Length()) && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))) ) + pos++; + } + return pos; +} + +static char BraceOpposite(char ch) { + switch (ch) { + case '(': + return ')'; + case ')': + return '('; + case '[': + return ']'; + case ']': + return '['; + case '{': + return '}'; + case '}': + return '{'; + case '<': + return '>'; + case '>': + return '<'; + default: + return '\0'; + } +} + +// TODO: should be able to extend styled region to find matching brace +int Document::BraceMatch(int position, int /*maxReStyle*/) { + char chBrace = CharAt(position); + char chSeek = BraceOpposite(chBrace); + if (chSeek == '\0') + return - 1; + char styBrace = static_cast(StyleAt(position) & stylingBitsMask); + int direction = -1; + if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<') + direction = 1; + int depth = 1; + position = position + direction; + while ((position >= 0) && (position < Length())) { + position = MovePositionOutsideChar(position, direction); + char chAtPos = CharAt(position); + char styAtPos = static_cast(StyleAt(position) & stylingBitsMask); + if ((position > GetEndStyled()) || (styAtPos == styBrace)) { + if (chAtPos == chBrace) + depth++; + if (chAtPos == chSeek) + depth--; + if (depth == 0) + return position; + } + position = position + direction; + } + return - 1; +} diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Document.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Document.h new file mode 100644 index 00000000..2d91d8d3 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Document.h @@ -0,0 +1,305 @@ +// Scintilla source code edit control +/** @file Document.h + ** Text document that handles notifications, DBCS, styling, words and end of line. + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef DOCUMENT_H +#define DOCUMENT_H + +/** + * A Position is a position within a document between two characters or at the beginning or end. + * Sometimes used as a character index where it identifies the character after the position. + */ +typedef int Position; +const Position invalidPosition = -1; + +/** + * The range class represents a range of text in a document. + * The two values are not sorted as one end may be more significant than the other + * as is the case for the selection where the end position is the position of the caret. + * If either position is invalidPosition then the range is invalid and most operations will fail. + */ +class Range { +public: + Position start; + Position end; + + Range(Position pos=0) : + start(pos), end(pos) { + }; + Range(Position start_, Position end_) : + start(start_), end(end_) { + }; + + bool Valid() const { + return (start != invalidPosition) && (end != invalidPosition); + } + + // Is the position within the range? + bool Contains(Position pos) const { + if (start < end) { + return (pos >= start && pos <= end); + } else { + return (pos <= start && pos >= end); + } + } + + // Is the character after pos within the range? + bool ContainsCharacter(Position pos) const { + if (start < end) { + return (pos >= start && pos < end); + } else { + return (pos < start && pos >= end); + } + } + + bool Contains(Range other) const { + return Contains(other.start) && Contains(other.end); + } + + bool Overlaps(Range other) const { + return + Contains(other.start) || + Contains(other.end) || + other.Contains(start) || + other.Contains(end); + } +}; + +class DocWatcher; +class DocModification; +class RESearch; + +/** + */ +class Document { + +public: + /** Used to pair watcher pointer with user data. */ + class WatcherWithUserData { + public: + DocWatcher *watcher; + void *userData; + WatcherWithUserData() { + watcher = 0; + userData = 0; + } + }; + + enum charClassification { ccSpace, ccNewLine, ccWord, ccPunctuation }; + +private: + int refCount; + CellBuffer cb; + CharClassify charClass; + char stylingMask; + int endStyled; + int styleClock; + int enteredCount; + int enteredReadOnlyCount; + + WatcherWithUserData *watchers; + int lenWatchers; + + bool matchesValid; + RESearch *pre; + char *substituted; + +public: + int stylingBits; + int stylingBitsMask; + + int eolMode; + /// Can also be SC_CP_UTF8 to enable UTF-8 mode + int dbcsCodePage; + int tabInChars; + int indentInChars; + int actualIndentInChars; + bool useTabs; + bool tabIndents; + bool backspaceUnindents; + + Document(); + virtual ~Document(); + + int AddRef(); + int Release(); + + int LineFromPosition(int pos); + int ClampPositionIntoDocument(int pos); + bool IsCrLf(int pos); + int LenChar(int pos); + int MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd=true); + + // Gateways to modifying document + void ModifiedAt(int pos); + bool DeleteChars(int pos, int len); + bool InsertStyledString(int position, char *s, int insertLength); + int Undo(); + int Redo(); + bool CanUndo() { return cb.CanUndo(); } + bool CanRedo() { return cb.CanRedo(); } + void DeleteUndoHistory() { cb.DeleteUndoHistory(); } + bool SetUndoCollection(bool collectUndo) { + return cb.SetUndoCollection(collectUndo); + } + bool IsCollectingUndo() { return cb.IsCollectingUndo(); } + void BeginUndoAction() { cb.BeginUndoAction(); } + void EndUndoAction() { cb.EndUndoAction(); } + void SetSavePoint(); + bool IsSavePoint() { return cb.IsSavePoint(); } + + int GetLineIndentation(int line); + void SetLineIndentation(int line, int indent); + int GetLineIndentPosition(int line); + int GetColumn(int position); + int FindColumn(int line, int column); + void Indent(bool forwards, int lineBottom, int lineTop); + static char *TransformLineEnds(int *pLenOut, const char *s, size_t len, int eolMode); + void ConvertLineEnds(int eolModeSet); + void SetReadOnly(bool set) { cb.SetReadOnly(set); } + bool IsReadOnly() { return cb.IsReadOnly(); } + + bool InsertChar(int pos, char ch); + bool InsertString(int position, const char *s); + bool InsertString(int position, const char *s, size_t insertLength); + void ChangeChar(int pos, char ch); + void DelChar(int pos); + void DelCharBack(int pos); + + char CharAt(int position) { return cb.CharAt(position); } + void GetCharRange(char *buffer, int position, int lengthRetrieve) { + cb.GetCharRange(buffer, position, lengthRetrieve); + } + char StyleAt(int position) { return cb.StyleAt(position); } + int GetMark(int line) { return cb.GetMark(line); } + int AddMark(int line, int markerNum); + void AddMarkSet(int line, int valueSet); + void DeleteMark(int line, int markerNum); + void DeleteMarkFromHandle(int markerHandle); + void DeleteAllMarks(int markerNum); + int LineFromHandle(int markerHandle) { return cb.LineFromHandle(markerHandle); } + int LineStart(int line); + int LineEnd(int line); + int LineEndPosition(int position); + int VCHomePosition(int position); + + int SetLevel(int line, int level); + int GetLevel(int line) { return cb.GetLevel(line); } + void ClearLevels() { cb.ClearLevels(); } + int GetLastChild(int lineParent, int level=-1); + int GetFoldParent(int line); + + void Indent(bool forwards); + int ExtendWordSelect(int pos, int delta, bool onlyWordCharacters=false); + int NextWordStart(int pos, int delta); + int NextWordEnd(int pos, int delta); + int Length() { return cb.Length(); } + void Allocate(int newSize) { cb.Allocate(newSize*2); } + long FindText(int minPos, int maxPos, const char *s, + bool caseSensitive, bool word, bool wordStart, bool regExp, bool posix, int *length); + long FindText(int iMessage, unsigned long wParam, long lParam); + const char *SubstituteByPosition(const char *text, int *length); + int LinesTotal(); + + void ChangeCase(Range r, bool makeUpperCase); + + void SetDefaultCharClasses(bool includeWordClass); + void SetCharClasses(const unsigned char *chars, CharClassify::cc newCharClass); + void SetStylingBits(int bits); + void StartStyling(int position, char mask); + bool SetStyleFor(int length, char style); + bool SetStyles(int length, char *styles); + int GetEndStyled() { return endStyled; } + bool EnsureStyledTo(int pos); + int GetStyleClock() { return styleClock; } + void IncrementStyleClock(); + + int SetLineState(int line, int state) { return cb.SetLineState(line, state); } + int GetLineState(int line) { return cb.GetLineState(line); } + int GetMaxLineState() { return cb.GetMaxLineState(); } + + bool AddWatcher(DocWatcher *watcher, void *userData); + bool RemoveWatcher(DocWatcher *watcher, void *userData); + const WatcherWithUserData *GetWatchers() const { return watchers; } + int GetLenWatchers() const { return lenWatchers; } + + bool IsWordPartSeparator(char ch); + int WordPartLeft(int pos); + int WordPartRight(int pos); + int ExtendStyleRange(int pos, int delta, bool singleLine = false); + bool IsWhiteLine(int line); + int ParaUp(int pos); + int ParaDown(int pos); + int IndentSize() { return actualIndentInChars; } + int BraceMatch(int position, int maxReStyle); + +private: + void CheckReadOnly(); + + CharClassify::cc WordCharClass(unsigned char ch); + bool IsWordStartAt(int pos); + bool IsWordEndAt(int pos); + bool IsWordAt(int start, int end); + + void NotifyModifyAttempt(); + void NotifySavePoint(bool atSavePoint); + void NotifyModified(DocModification mh); +}; + +/** + * To optimise processing of document modifications by DocWatchers, a hint is passed indicating the + * scope of the change. + * If the DocWatcher is a document view then this can be used to optimise screen updating. + */ +class DocModification { +public: + int modificationType; + int position; + int length; + int linesAdded; /**< Negative if lines deleted. */ + const char *text; /**< Only valid for changes to text, not for changes to style. */ + int line; + int foldLevelNow; + int foldLevelPrev; + + DocModification(int modificationType_, int position_=0, int length_=0, + int linesAdded_=0, const char *text_=0, int line_=0) : + modificationType(modificationType_), + position(position_), + length(length_), + linesAdded(linesAdded_), + text(text_), + line(line_), + foldLevelNow(0), + foldLevelPrev(0) {} + + DocModification(int modificationType_, const Action &act, int linesAdded_=0) : + modificationType(modificationType_), + position(act.position), + length(act.lenData), + linesAdded(linesAdded_), + text(act.data), + line(0), + foldLevelNow(0), + foldLevelPrev(0) {} +}; + +/** + * A class that wants to receive notifications from a Document must be derived from DocWatcher + * and implement the notification methods. It can then be added to the watcher list with AddWatcher. + */ +class DocWatcher { +public: + virtual ~DocWatcher() {} + + virtual void NotifyModifyAttempt(Document *doc, void *userData) = 0; + virtual void NotifySavePoint(Document *doc, void *userData, bool atSavePoint) = 0; + virtual void NotifyModified(Document *doc, DocModification mh, void *userData) = 0; + virtual void NotifyDeleted(Document *doc, void *userData) = 0; + virtual void NotifyStyleNeeded(Document *doc, void *userData, int endPos) = 0; +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/DocumentAccessor.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/DocumentAccessor.cxx new file mode 100644 index 00000000..6575875a --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/DocumentAccessor.cxx @@ -0,0 +1,187 @@ +// Scintilla source code edit control +/** @file DocumentAccessor.cxx + ** Rapid easy access to contents of a Scintilla. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "SVector.h" +#include "Accessor.h" +#include "DocumentAccessor.h" +#include "CellBuffer.h" +#include "Scintilla.h" +#include "CharClassify.h" +#include "Document.h" + +DocumentAccessor::~DocumentAccessor() { +} + +bool DocumentAccessor::InternalIsLeadByte(char ch) { + if (SC_CP_UTF8 == codePage) + // For lexing, all characters >= 0x80 are treated the + // same so none is considered a lead byte. + return false; + else + return Platform::IsDBCSLeadByte(codePage, ch); +} + +void DocumentAccessor::Fill(int position) { + if (lenDoc == -1) + lenDoc = pdoc->Length(); + startPos = position - slopSize; + if (startPos + bufferSize > lenDoc) + startPos = lenDoc - bufferSize; + if (startPos < 0) + startPos = 0; + endPos = startPos + bufferSize; + if (endPos > lenDoc) + endPos = lenDoc; + + pdoc->GetCharRange(buf, startPos, endPos-startPos); + buf[endPos-startPos] = '\0'; +} + +bool DocumentAccessor::Match(int pos, const char *s) { + for (int i=0; *s; i++) { + if (*s != SafeGetCharAt(pos+i)) + return false; + s++; + } + return true; +} + +char DocumentAccessor::StyleAt(int position) { + // Mask off all bits which aren't in the 'mask'. + return static_cast(pdoc->StyleAt(position) & mask); +} + +int DocumentAccessor::GetLine(int position) { + return pdoc->LineFromPosition(position); +} + +int DocumentAccessor::LineStart(int line) { + return pdoc->LineStart(line); +} + +int DocumentAccessor::LevelAt(int line) { + return pdoc->GetLevel(line); +} + +int DocumentAccessor::Length() { + if (lenDoc == -1) + lenDoc = pdoc->Length(); + return lenDoc; +} + +int DocumentAccessor::GetLineState(int line) { + return pdoc->GetLineState(line); +} + +int DocumentAccessor::SetLineState(int line, int state) { + return pdoc->SetLineState(line, state); +} + +void DocumentAccessor::StartAt(unsigned int start, char chMask) { + // Store the mask specified for use with StyleAt. + mask = chMask; + pdoc->StartStyling(start, chMask); + startPosStyling = start; +} + +void DocumentAccessor::StartSegment(unsigned int pos) { + startSeg = pos; +} + +void DocumentAccessor::ColourTo(unsigned int pos, int chAttr) { + // Only perform styling if non empty range + if (pos != startSeg - 1) { + if (pos < startSeg) { + Platform::DebugPrintf("Bad colour positions %d - %d\n", startSeg, pos); + } + + if (validLen + (pos - startSeg + 1) >= bufferSize) + Flush(); + if (validLen + (pos - startSeg + 1) >= bufferSize) { + // Too big for buffer so send directly + pdoc->SetStyleFor(pos - startSeg + 1, static_cast(chAttr)); + } else { + if (chAttr != chWhile) + chFlags = 0; + chAttr |= chFlags; + for (unsigned int i = startSeg; i <= pos; i++) { + PLATFORM_ASSERT((startPosStyling + validLen) < Length()); + styleBuf[validLen++] = static_cast(chAttr); + } + } + } + startSeg = pos+1; +} + +void DocumentAccessor::SetLevel(int line, int level) { + pdoc->SetLevel(line, level); +} + +void DocumentAccessor::Flush() { + startPos = extremePosition; + lenDoc = -1; + if (validLen > 0) { + pdoc->SetStyles(validLen, styleBuf); + startPosStyling += validLen; + validLen = 0; + } +} + +int DocumentAccessor::IndentAmount(int line, int *flags, PFNIsCommentLeader pfnIsCommentLeader) { + int end = Length(); + int spaceFlags = 0; + + // Determines the indentation level of the current line and also checks for consistent + // indentation compared to the previous line. + // Indentation is judged consistent when the indentation whitespace of each line lines + // the same or the indentation of one line is a prefix of the other. + + int pos = LineStart(line); + char ch = (*this)[pos]; + int indent = 0; + bool inPrevPrefix = line > 0; + int posPrev = inPrevPrefix ? LineStart(line-1) : 0; + while ((ch == ' ' || ch == '\t') && (pos < end)) { + if (inPrevPrefix) { + char chPrev = (*this)[posPrev++]; + if (chPrev == ' ' || chPrev == '\t') { + if (chPrev != ch) + spaceFlags |= wsInconsistent; + } else { + inPrevPrefix = false; + } + } + if (ch == ' ') { + spaceFlags |= wsSpace; + indent++; + } else { // Tab + spaceFlags |= wsTab; + if (spaceFlags & wsSpace) + spaceFlags |= wsSpaceTab; + indent = (indent / 8 + 1) * 8; + } + ch = (*this)[++pos]; + } + + *flags = spaceFlags; + indent += SC_FOLDLEVELBASE; + // if completely empty line or the start of a comment... + if ((ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') || + (pfnIsCommentLeader && (*pfnIsCommentLeader)(*this, pos, end-pos)) ) + return indent | SC_FOLDLEVELWHITEFLAG; + else + return indent; +} + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/DocumentAccessor.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/DocumentAccessor.h new file mode 100644 index 00000000..05eefda6 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/DocumentAccessor.h @@ -0,0 +1,67 @@ +// Scintilla source code edit control +/** @file DocumentAccessor.h + ** Implementation of BufferAccess and StylingAccess on a Scintilla + ** rapid easy access to contents of a Scintilla. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +class Document; + +/** + */ +class DocumentAccessor : public Accessor { + // Private so DocumentAccessor objects can not be copied + DocumentAccessor(const DocumentAccessor &source) : Accessor(), props(source.props) {} + DocumentAccessor &operator=(const DocumentAccessor &) { return *this; } + +protected: + Document *pdoc; + PropSet &props; + WindowID id; + int lenDoc; + + char styleBuf[bufferSize]; + int validLen; + char chFlags; + char chWhile; + unsigned int startSeg; + int startPosStyling; + int mask; + + bool InternalIsLeadByte(char ch); + void Fill(int position); + +public: + DocumentAccessor(Document *pdoc_, PropSet &props_, WindowID id_=0) : + Accessor(), pdoc(pdoc_), props(props_), id(id_), + lenDoc(-1), validLen(0), chFlags(0), chWhile(0), + startSeg(0), startPosStyling(0), + mask(127) { // Initialize the mask to be big enough for any lexer. + } + ~DocumentAccessor(); + bool Match(int pos, const char *s); + char StyleAt(int position); + int GetLine(int position); + int LineStart(int line); + int LevelAt(int line); + int Length(); + void Flush(); + int GetLineState(int line); + int SetLineState(int line, int state); + int GetPropertyInt(const char *key, int defaultValue=0) { + return props.GetInt(key, defaultValue); + } + char *GetProperties() { + return props.ToString(); + } + WindowID GetWindow() { return id; } + + void StartAt(unsigned int start, char chMask=31); + void SetFlags(char chFlags_, char chWhile_) {chFlags = chFlags_; chWhile = chWhile_; }; + unsigned int GetStartSegment() { return startSeg; } + void StartSegment(unsigned int pos); + void ColourTo(unsigned int pos, int chAttr); + void SetLevel(int line, int level); + int IndentAmount(int line, int *flags, PFNIsCommentLeader pfnIsCommentLeader = 0); +}; diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Editor.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Editor.cxx new file mode 100644 index 00000000..4344e1c0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Editor.cxx @@ -0,0 +1,7297 @@ +// Scintilla source code edit control +/** @file Editor.cxx + ** Main code for the edit control. + **/ +// Copyright 1998-2004 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include + +#include "Platform.h" + +#ifndef PLAT_QT +#define INCLUDE_DEPRECATED_FEATURES +#endif +#include "Scintilla.h" + +#include "ContractionState.h" +#include "SVector.h" +#include "CellBuffer.h" +#include "KeyMap.h" +#include "Indicator.h" +#include "XPM.h" +#include "LineMarker.h" +#include "Style.h" +#include "ViewStyle.h" +#include "CharClassify.h" +#include "Document.h" +#include "Editor.h" + +/* + return whether this modification represents an operation that + may reasonably be deferred (not done now OR [possibly] at all) +*/ +static bool CanDeferToLastStep(const DocModification& mh) { + if (mh.modificationType & (SC_MOD_BEFOREINSERT|SC_MOD_BEFOREDELETE)) + return true; // CAN skip + if (!(mh.modificationType & (SC_PERFORMED_UNDO|SC_PERFORMED_REDO))) + return false; // MUST do + if (mh.modificationType & SC_MULTISTEPUNDOREDO) + return true; // CAN skip + return false; // PRESUMABLY must do +} + +static bool CanEliminate(const DocModification& mh) { + return + (mh.modificationType & (SC_MOD_BEFOREINSERT|SC_MOD_BEFOREDELETE)) != 0; +} + +/* + return whether this modification represents the FINAL step + in a [possibly lengthy] multi-step Undo/Redo sequence +*/ +static bool IsLastStep(const DocModification& mh) { + return + (mh.modificationType & (SC_PERFORMED_UNDO|SC_PERFORMED_REDO)) != 0 + && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0 + && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0 + && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0; +} + +Caret::Caret() : +active(false), on(false), period(500) {} + +Timer::Timer() : +ticking(false), ticksToWait(0), tickerID(0) {} + +Idler::Idler() : +state(false), idlerID(0) {} + +LineLayout::LineLayout(int maxLineLength_) : + lineStarts(0), + lenLineStarts(0), + lineNumber(-1), + inCache(false), + maxLineLength(-1), + numCharsInLine(0), + validity(llInvalid), + xHighlightGuide(0), + highlightColumn(0), + selStart(0), + selEnd(0), + containsCaret(false), + edgeColumn(0), + chars(0), + styles(0), + styleBitsSet(0), + indicators(0), + positions(0), + hsStart(0), + hsEnd(0), + widthLine(wrapWidthInfinite), + lines(1) { + Resize(maxLineLength_); +} + +LineLayout::~LineLayout() { + Free(); +} + +void LineLayout::Resize(int maxLineLength_) { + if (maxLineLength_ > maxLineLength) { + Free(); + chars = new char[maxLineLength_ + 1]; + styles = new unsigned char[maxLineLength_ + 1]; + indicators = new char[maxLineLength_ + 1]; + // Extra position allocated as sometimes the Windows + // GetTextExtentExPoint API writes an extra element. + positions = new int[maxLineLength_ + 1 + 1]; + maxLineLength = maxLineLength_; + } +} + +void LineLayout::Free() { + delete []chars; + chars = 0; + delete []styles; + styles = 0; + delete []indicators; + indicators = 0; + delete []positions; + positions = 0; + delete []lineStarts; + lineStarts = 0; +} + +void LineLayout::Invalidate(validLevel validity_) { + if (validity > validity_) + validity = validity_; +} + +void LineLayout::SetLineStart(int line, int start) { + if ((line >= lenLineStarts) && (line != 0)) { + int newMaxLines = line + 20; + int *newLineStarts = new int[newMaxLines]; + if (!newLineStarts) + return; + for (int i = 0; i < newMaxLines; i++) { + if (i < lenLineStarts) + newLineStarts[i] = lineStarts[i]; + else + newLineStarts[i] = 0; + } + delete []lineStarts; + lineStarts = newLineStarts; + lenLineStarts = newMaxLines; + } + lineStarts[line] = start; +} + +void LineLayout::SetBracesHighlight(Range rangeLine, Position braces[], + char bracesMatchStyle, int xHighlight) { + if (rangeLine.ContainsCharacter(braces[0])) { + int braceOffset = braces[0] - rangeLine.start; + if (braceOffset < numCharsInLine) { + bracePreviousStyles[0] = styles[braceOffset]; + styles[braceOffset] = bracesMatchStyle; + } + } + if (rangeLine.ContainsCharacter(braces[1])) { + int braceOffset = braces[1] - rangeLine.start; + if (braceOffset < numCharsInLine) { + bracePreviousStyles[1] = styles[braceOffset]; + styles[braceOffset] = bracesMatchStyle; + } + } + if ((braces[0] >= rangeLine.start && braces[1] <= rangeLine.end) || + (braces[1] >= rangeLine.start && braces[0] <= rangeLine.end)) { + xHighlightGuide = xHighlight; + } +} + +void LineLayout::RestoreBracesHighlight(Range rangeLine, Position braces[]) { + if (rangeLine.ContainsCharacter(braces[0])) { + int braceOffset = braces[0] - rangeLine.start; + if (braceOffset < numCharsInLine) { + styles[braceOffset] = bracePreviousStyles[0]; + } + } + if (rangeLine.ContainsCharacter(braces[1])) { + int braceOffset = braces[1] - rangeLine.start; + if (braceOffset < numCharsInLine) { + styles[braceOffset] = bracePreviousStyles[1]; + } + } + xHighlightGuide = 0; +} + +LineLayoutCache::LineLayoutCache() : + level(0), length(0), size(0), cache(0), + allInvalidated(false), styleClock(-1), useCount(0) { + Allocate(0); +} + +LineLayoutCache::~LineLayoutCache() { + Deallocate(); +} + +void LineLayoutCache::Allocate(int length_) { + PLATFORM_ASSERT(cache == NULL); + allInvalidated = false; + length = length_; + size = length; + if (size > 1) { + size = (size / 16 + 1) * 16; + } + if (size > 0) { + cache = new LineLayout * [size]; + } + for (int i = 0; i < size; i++) + cache[i] = 0; +} + +void LineLayoutCache::AllocateForLevel(int linesOnScreen, int linesInDoc) { + PLATFORM_ASSERT(useCount == 0); + int lengthForLevel = 0; + if (level == llcCaret) { + lengthForLevel = 1; + } else if (level == llcPage) { + lengthForLevel = linesOnScreen + 1; + } else if (level == llcDocument) { + lengthForLevel = linesInDoc; + } + if (lengthForLevel > size) { + Deallocate(); + Allocate(lengthForLevel); + } else { + if (lengthForLevel < length) { + for (int i = lengthForLevel; i < length; i++) { + delete cache[i]; + cache[i] = 0; + } + } + length = lengthForLevel; + } + PLATFORM_ASSERT(length == lengthForLevel); + PLATFORM_ASSERT(cache != NULL || length == 0); +} + +void LineLayoutCache::Deallocate() { + PLATFORM_ASSERT(useCount == 0); + for (int i = 0; i < length; i++) + delete cache[i]; + delete []cache; + cache = 0; + length = 0; + size = 0; +} + +void LineLayoutCache::Invalidate(LineLayout::validLevel validity_) { + if (cache && !allInvalidated) { + for (int i = 0; i < length; i++) { + if (cache[i]) { + cache[i]->Invalidate(validity_); + } + } + if (validity_ == LineLayout::llInvalid) { + allInvalidated = true; + } + } +} + +void LineLayoutCache::SetLevel(int level_) { + allInvalidated = false; + if ((level_ != -1) && (level != level_)) { + level = level_; + Deallocate(); + } +} + +LineLayout *LineLayoutCache::Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_, + int linesOnScreen, int linesInDoc) { + AllocateForLevel(linesOnScreen, linesInDoc); + if (styleClock != styleClock_) { + Invalidate(LineLayout::llCheckTextAndStyle); + styleClock = styleClock_; + } + allInvalidated = false; + int pos = -1; + LineLayout *ret = 0; + if (level == llcCaret) { + pos = 0; + } else if (level == llcPage) { + if (lineNumber == lineCaret) { + pos = 0; + } else if (length > 1) { + pos = 1 + (lineNumber % (length - 1)); + } + } else if (level == llcDocument) { + pos = lineNumber; + } + if (pos >= 0) { + PLATFORM_ASSERT(useCount == 0); + if (cache && (pos < length)) { + if (cache[pos]) { + if ((cache[pos]->lineNumber != lineNumber) || + (cache[pos]->maxLineLength < maxChars)) { + delete cache[pos]; + cache[pos] = 0; + } + } + if (!cache[pos]) { + cache[pos] = new LineLayout(maxChars); + } + if (cache[pos]) { + cache[pos]->lineNumber = lineNumber; + cache[pos]->inCache = true; + ret = cache[pos]; + useCount++; + } + } + } + + if (!ret) { + ret = new LineLayout(maxChars); + ret->lineNumber = lineNumber; + } + + return ret; +} + +void LineLayoutCache::Dispose(LineLayout *ll) { + allInvalidated = false; + if (ll) { + if (!ll->inCache) { + delete ll; + } else { + useCount--; + } + } +} + +Editor::Editor() { + ctrlID = 0; + + stylesValid = false; + + printMagnification = 0; + printColourMode = SC_PRINT_NORMAL; + printWrapState = eWrapWord; + cursorMode = SC_CURSORNORMAL; + controlCharSymbol = 0; /* Draw the control characters */ + + hasFocus = false; + hideSelection = false; + inOverstrike = false; + errorStatus = 0; + mouseDownCaptures = true; + + bufferedDraw = true; + twoPhaseDraw = true; + + lastClickTime = 0; + dwellDelay = SC_TIME_FOREVER; + ticksToDwell = SC_TIME_FOREVER; + dwelling = false; + ptMouseLast.x = 0; + ptMouseLast.y = 0; + inDragDrop = false; + dropWentOutside = false; + posDrag = invalidPosition; + posDrop = invalidPosition; + selectionType = selChar; + + lastXChosen = 0; + lineAnchor = 0; + originalAnchorPos = 0; + + selType = selStream; + moveExtendsSelection = false; + xStartSelect = 0; + xEndSelect = 0; + primarySelection = true; + + caretXPolicy = CARET_SLOP | CARET_EVEN; + caretXSlop = 50; + + caretYPolicy = CARET_EVEN; + caretYSlop = 0; + + searchAnchor = 0; + + xOffset = 0; + xCaretMargin = 50; + horizontalScrollBarVisible = true; + scrollWidth = 2000; + verticalScrollBarVisible = true; + endAtLastLine = true; + caretSticky = false; + + pixmapLine = Surface::Allocate(); + pixmapSelMargin = Surface::Allocate(); + pixmapSelPattern = Surface::Allocate(); + pixmapIndentGuide = Surface::Allocate(); + pixmapIndentGuideHighlight = Surface::Allocate(); + + currentPos = 0; + anchor = 0; + + targetStart = 0; + targetEnd = 0; + searchFlags = 0; + + topLine = 0; + posTopLine = 0; + + lengthForEncode = -1; + + needUpdateUI = true; + braces[0] = invalidPosition; + braces[1] = invalidPosition; + bracesMatchStyle = STYLE_BRACEBAD; + highlightGuideColumn = 0; + + theEdge = 0; + + paintState = notPainting; + + modEventMask = SC_MODEVENTMASKALL; + + pdoc = new Document(); + pdoc->AddRef(); + pdoc->AddWatcher(this, 0); + + recordingMacro = false; + foldFlags = 0; + + wrapState = eWrapNone; + wrapWidth = LineLayout::wrapWidthInfinite; + wrapStart = wrapLineLarge; + wrapEnd = wrapLineLarge; + wrapVisualFlags = 0; + wrapVisualFlagsLocation = 0; + wrapVisualStartIndent = 0; + actualWrapVisualStartIndent = 0; + + convertPastes = true; + + hsStart = -1; + hsEnd = -1; + + llc.SetLevel(LineLayoutCache::llcCaret); +} + +Editor::~Editor() { + pdoc->RemoveWatcher(this, 0); + pdoc->Release(); + pdoc = 0; + DropGraphics(); + delete pixmapLine; + delete pixmapSelMargin; + delete pixmapSelPattern; + delete pixmapIndentGuide; + delete pixmapIndentGuideHighlight; +} + +void Editor::Finalise() { + SetIdle(false); + CancelModes(); +} + +void Editor::DropGraphics() { + pixmapLine->Release(); + pixmapSelMargin->Release(); + pixmapSelPattern->Release(); + pixmapIndentGuide->Release(); + pixmapIndentGuideHighlight->Release(); +} + +void Editor::InvalidateStyleData() { + stylesValid = false; + palette.Release(); + DropGraphics(); + llc.Invalidate(LineLayout::llInvalid); + if (selType == selRectangle) { + xStartSelect = XFromPosition(anchor); + xEndSelect = XFromPosition(currentPos); + } +} + +void Editor::InvalidateStyleRedraw() { + NeedWrapping(); + InvalidateStyleData(); + Redraw(); +} + +void Editor::RefreshColourPalette(Palette &pal, bool want) { + vs.RefreshColourPalette(pal, want); +} + +void Editor::RefreshStyleData() { + if (!stylesValid) { + stylesValid = true; + AutoSurface surface(this); + if (surface) { + vs.Refresh(*surface); + RefreshColourPalette(palette, true); + palette.Allocate(wMain); + RefreshColourPalette(palette, false); + } + SetScrollBars(); + } +} + +PRectangle Editor::GetClientRectangle() { + return wMain.GetClientPosition(); +} + +PRectangle Editor::GetTextRectangle() { + PRectangle rc = GetClientRectangle(); + rc.left += vs.fixedColumnWidth; + rc.right -= vs.rightMarginWidth; + return rc; +} + +int Editor::LinesOnScreen() { + PRectangle rcClient = GetClientRectangle(); + int htClient = rcClient.bottom - rcClient.top; + //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1); + return htClient / vs.lineHeight; +} + +int Editor::LinesToScroll() { + int retVal = LinesOnScreen() - 1; + if (retVal < 1) + return 1; + else + return retVal; +} + +int Editor::MaxScrollPos() { + //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n", + //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1); + int retVal = cs.LinesDisplayed(); + if (endAtLastLine) { + retVal -= LinesOnScreen(); + } else { + retVal--; + } + if (retVal < 0) { + return 0; + } else { + return retVal; + } +} + +static inline bool IsControlCharacter(int ch) { + // iscntrl returns true for lots of chars > 127 which are displayable + return ch >= 0 && ch < ' '; +} + +const char *ControlCharacterString(unsigned char ch) { + const char *reps[] = { + "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", + "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", + "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", + "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" + }; + if (ch < (sizeof(reps) / sizeof(reps[0]))) { + return reps[ch]; + } else { + return "BAD"; + } +} + +/** + * Convenience class to ensure LineLayout objects are always disposed. + */ +class AutoLineLayout { + LineLayoutCache &llc; + LineLayout *ll; + AutoLineLayout &operator=(const AutoLineLayout &) { return * this; } +public: + AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {} + ~AutoLineLayout() { + llc.Dispose(ll); + ll = 0; + } + LineLayout *operator->() const { + return ll; + } + operator LineLayout *() const { + return ll; + } + void Set(LineLayout *ll_) { + llc.Dispose(ll); + ll = ll_; + } +}; + +/** + * Allows to iterate through the lines of a selection. + * Althought it can be called for a stream selection, in most cases + * it is inefficient and it should be used only for + * a rectangular or a line selection. + */ +class SelectionLineIterator { +private: + Editor *ed; + int line; ///< Current line within the iteration. + bool forward; ///< True if iterating by increasing line number, false otherwise. + int selStart, selEnd; ///< Positions of the start and end of the selection relative to the start of the document. + int minX, maxX; ///< Left and right of selection rectangle. + +public: + int lineStart, lineEnd; ///< Line numbers, first and last lines of the selection. + int startPos, endPos; ///< Positions of the beginning and end of the selection on the current line. + + void Reset() { + if (forward) { + line = lineStart; + } else { + line = lineEnd; + } + } + + SelectionLineIterator(Editor *ed_, bool forward_ = true) : line(0), startPos(0), endPos(0) { + ed = ed_; + forward = forward_; + selStart = ed->SelectionStart(); + selEnd = ed->SelectionEnd(); + lineStart = ed->pdoc->LineFromPosition(selStart); + lineEnd = ed->pdoc->LineFromPosition(selEnd); + // Left of rectangle + minX = Platform::Minimum(ed->xStartSelect, ed->xEndSelect); + // Right of rectangle + maxX = Platform::Maximum(ed->xStartSelect, ed->xEndSelect); + Reset(); + } + ~SelectionLineIterator() {} + + void SetAt(int line) { + if (line < lineStart || line > lineEnd) { + startPos = endPos = INVALID_POSITION; + } else { + if (ed->selType == ed->selRectangle) { + // Measure line and return character closest to minX + startPos = ed->PositionFromLineX(line, minX); + // Measure line and return character closest to maxX + endPos = ed->PositionFromLineX(line, maxX); + } else if (ed->selType == ed->selLines) { + startPos = ed->pdoc->LineStart(line); + endPos = ed->pdoc->LineStart(line + 1); + } else { // Stream selection, here only for completion + if (line == lineStart) { + startPos = selStart; + } else { + startPos = ed->pdoc->LineStart(line); + } + if (line == lineEnd) { + endPos = selEnd; + } else { + endPos = ed->pdoc->LineStart(line + 1); + } + } + } + } + bool Iterate() { + SetAt(line); + if (forward) { + line++; + } else { + line--; + } + return startPos != INVALID_POSITION; + } +}; + +Point Editor::LocationFromPosition(int pos) { + Point pt; + RefreshStyleData(); + if (pos == INVALID_POSITION) + return pt; + int line = pdoc->LineFromPosition(pos); + int lineVisible = cs.DisplayFromDoc(line); + //Platform::DebugPrintf("line=%d\n", line); + AutoSurface surface(this); + AutoLineLayout ll(llc, RetrieveLineLayout(line)); + if (surface && ll) { + // -1 because of adding in for visible lines in following loop. + pt.y = (lineVisible - topLine - 1) * vs.lineHeight; + pt.x = 0; + unsigned int posLineStart = pdoc->LineStart(line); + LayoutLine(line, surface, vs, ll, wrapWidth); + int posInLine = pos - posLineStart; + // In case of very long line put x at arbitrary large position + if (posInLine > ll->maxLineLength) { + pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)]; + } + + for (int subLine = 0; subLine < ll->lines; subLine++) { + if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) { + pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)]; + if (actualWrapVisualStartIndent != 0) { + int lineStart = ll->LineStart(subLine); + if (lineStart != 0) // Wrapped + pt.x += actualWrapVisualStartIndent * vs.aveCharWidth; + } + } + if (posInLine >= ll->LineStart(subLine)) { + pt.y += vs.lineHeight; + } + } + pt.x += vs.fixedColumnWidth - xOffset; + } + return pt; +} + +int Editor::XFromPosition(int pos) { + Point pt = LocationFromPosition(pos); + return pt.x - vs.fixedColumnWidth + xOffset; +} + +int Editor::LineFromLocation(Point pt) { + return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine); +} + +void Editor::SetTopLine(int topLineNew) { + topLine = topLineNew; + posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine)); +} + +static inline bool IsEOLChar(char ch) { + return (ch == '\r') || (ch == '\n'); +} + +int Editor::PositionFromLocation(Point pt) { + RefreshStyleData(); + pt.x = pt.x - vs.fixedColumnWidth + xOffset; + int visibleLine = pt.y / vs.lineHeight + topLine; + if (pt.y < 0) { // Division rounds towards 0 + visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine; + } + if (visibleLine < 0) + visibleLine = 0; + int lineDoc = cs.DocFromDisplay(visibleLine); + if (lineDoc >= pdoc->LinesTotal()) + return pdoc->Length(); + unsigned int posLineStart = pdoc->LineStart(lineDoc); + int retVal = posLineStart; + AutoSurface surface(this); + AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc)); + if (surface && ll) { + LayoutLine(lineDoc, surface, vs, ll, wrapWidth); + int lineStartSet = cs.DisplayFromDoc(lineDoc); + int subLine = visibleLine - lineStartSet; + if (subLine < ll->lines) { + int lineStart = ll->LineStart(subLine); + int lineEnd = ll->LineStart(subLine + 1); + int subLineStart = ll->positions[lineStart]; + + if (actualWrapVisualStartIndent != 0) { + if (lineStart != 0) // Wrapped + pt.x -= actualWrapVisualStartIndent * vs.aveCharWidth; + } + for (int i = lineStart; i < lineEnd; i++) { + if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) || + IsEOLChar(ll->chars[i])) { + return pdoc->MovePositionOutsideChar(i + posLineStart, 1); + } + } + return lineEnd + posLineStart; + } + retVal = ll->numCharsInLine + posLineStart; + } + return retVal; +} + +// Like PositionFromLocation but INVALID_POSITION returned when not near any text. +int Editor::PositionFromLocationClose(Point pt) { + RefreshStyleData(); + PRectangle rcClient = GetTextRectangle(); + if (!rcClient.Contains(pt)) + return INVALID_POSITION; + if (pt.x < vs.fixedColumnWidth) + return INVALID_POSITION; + if (pt.y < 0) + return INVALID_POSITION; + pt.x = pt.x - vs.fixedColumnWidth + xOffset; + int visibleLine = pt.y / vs.lineHeight + topLine; + if (pt.y < 0) { // Division rounds towards 0 + visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine; + } + int lineDoc = cs.DocFromDisplay(visibleLine); + if (lineDoc < 0) + return INVALID_POSITION; + if (lineDoc >= pdoc->LinesTotal()) + return INVALID_POSITION; + AutoSurface surface(this); + AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc)); + if (surface && ll) { + LayoutLine(lineDoc, surface, vs, ll, wrapWidth); + unsigned int posLineStart = pdoc->LineStart(lineDoc); + int lineStartSet = cs.DisplayFromDoc(lineDoc); + int subLine = visibleLine - lineStartSet; + if (subLine < ll->lines) { + int lineStart = ll->LineStart(subLine); + int lineEnd = ll->LineStart(subLine + 1); + int subLineStart = ll->positions[lineStart]; + + if (actualWrapVisualStartIndent != 0) { + if (lineStart != 0) // Wrapped + pt.x -= actualWrapVisualStartIndent * vs.aveCharWidth; + } + for (int i = lineStart; i < lineEnd; i++) { + if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) || + IsEOLChar(ll->chars[i])) { + return pdoc->MovePositionOutsideChar(i + posLineStart, 1); + } + } + if (pt.x < (ll->positions[lineEnd] - subLineStart)) { + return pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1); + } + } + } + + return INVALID_POSITION; +} + +/** + * Find the document position corresponding to an x coordinate on a particular document line. + * Ensure is between whole characters when document is in multi-byte or UTF-8 mode. + */ +int Editor::PositionFromLineX(int lineDoc, int x) { + RefreshStyleData(); + if (lineDoc >= pdoc->LinesTotal()) + return pdoc->Length(); + //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine); + AutoSurface surface(this); + AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc)); + int retVal = 0; + if (surface && ll) { + unsigned int posLineStart = pdoc->LineStart(lineDoc); + LayoutLine(lineDoc, surface, vs, ll, wrapWidth); + retVal = ll->numCharsInLine + posLineStart; + int subLine = 0; + int lineStart = ll->LineStart(subLine); + int lineEnd = ll->LineStart(subLine + 1); + int subLineStart = ll->positions[lineStart]; + + if (actualWrapVisualStartIndent != 0) { + if (lineStart != 0) // Wrapped + x -= actualWrapVisualStartIndent * vs.aveCharWidth; + } + for (int i = lineStart; i < lineEnd; i++) { + if (x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) || + IsEOLChar(ll->chars[i])) { + retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1); + break; + } + } + } + return retVal; +} + +/** + * If painting then abandon the painting because a wider redraw is needed. + * @return true if calling code should stop drawing. + */ +bool Editor::AbandonPaint() { + if ((paintState == painting) && !paintingAllText) { + paintState = paintAbandoned; + } + return paintState == paintAbandoned; +} + +void Editor::RedrawRect(PRectangle rc) { + //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom); + + // Clip the redraw rectangle into the client area + PRectangle rcClient = GetClientRectangle(); + if (rc.top < rcClient.top) + rc.top = rcClient.top; + if (rc.bottom > rcClient.bottom) + rc.bottom = rcClient.bottom; + if (rc.left < rcClient.left) + rc.left = rcClient.left; + if (rc.right > rcClient.right) + rc.right = rcClient.right; + + if ((rc.bottom > rc.top) && (rc.right > rc.left)) { + wMain.InvalidateRectangle(rc); + } +} + +void Editor::Redraw() { + //Platform::DebugPrintf("Redraw all\n"); + PRectangle rcClient = GetClientRectangle(); + wMain.InvalidateRectangle(rcClient); + //wMain.InvalidateAll(); +} + +void Editor::RedrawSelMargin(int line) { + if (!AbandonPaint()) { + if (vs.maskInLine) { + Redraw(); + } else { + PRectangle rcSelMargin = GetClientRectangle(); + rcSelMargin.right = vs.fixedColumnWidth; + if (line != -1) { + int position = pdoc->LineStart(line); + PRectangle rcLine = RectangleFromRange(position, position); + rcSelMargin.top = rcLine.top; + rcSelMargin.bottom = rcLine.bottom; + } + wMain.InvalidateRectangle(rcSelMargin); + } + } +} + +PRectangle Editor::RectangleFromRange(int start, int end) { + int minPos = start; + if (minPos > end) + minPos = end; + int maxPos = start; + if (maxPos < end) + maxPos = end; + int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos)); + int lineDocMax = pdoc->LineFromPosition(maxPos); + int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1; + PRectangle rcClient = GetTextRectangle(); + PRectangle rc; + rc.left = vs.fixedColumnWidth; + rc.top = (minLine - topLine) * vs.lineHeight; + if (rc.top < 0) + rc.top = 0; + rc.right = rcClient.right; + rc.bottom = (maxLine - topLine + 1) * vs.lineHeight; + // Ensure PRectangle is within 16 bit space + rc.top = Platform::Clamp(rc.top, -32000, 32000); + rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000); + + return rc; +} + +void Editor::InvalidateRange(int start, int end) { + RedrawRect(RectangleFromRange(start, end)); +} + +int Editor::CurrentPosition() { + return currentPos; +} + +bool Editor::SelectionEmpty() { + return anchor == currentPos; +} + +int Editor::SelectionStart() { + return Platform::Minimum(currentPos, anchor); +} + +int Editor::SelectionEnd() { + return Platform::Maximum(currentPos, anchor); +} + +void Editor::SetRectangularRange() { + if (selType == selRectangle) { + xStartSelect = XFromPosition(anchor); + xEndSelect = XFromPosition(currentPos); + } +} + +void Editor::InvalidateSelection(int currentPos_, int anchor_) { + int firstAffected = anchor; + if (firstAffected > currentPos) + firstAffected = currentPos; + if (firstAffected > anchor_) + firstAffected = anchor_; + if (firstAffected > currentPos_) + firstAffected = currentPos_; + int lastAffected = anchor; + if (lastAffected < currentPos) + lastAffected = currentPos; + if (lastAffected < anchor_) + lastAffected = anchor_; + if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted + lastAffected = (currentPos_ + 1); + needUpdateUI = true; + InvalidateRange(firstAffected, lastAffected); +} + +void Editor::SetSelection(int currentPos_, int anchor_) { + currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_); + anchor_ = pdoc->ClampPositionIntoDocument(anchor_); + if ((currentPos != currentPos_) || (anchor != anchor_)) { + InvalidateSelection(currentPos_, anchor_); + currentPos = currentPos_; + anchor = anchor_; + } + SetRectangularRange(); + ClaimSelection(); +} + +void Editor::SetSelection(int currentPos_) { + currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_); + if (currentPos != currentPos_) { + InvalidateSelection(currentPos_, currentPos_); + currentPos = currentPos_; + } + SetRectangularRange(); + ClaimSelection(); +} + +void Editor::SetEmptySelection(int currentPos_) { + selType = selStream; + moveExtendsSelection = false; + SetSelection(currentPos_, currentPos_); +} + +bool Editor::RangeContainsProtected(int start, int end) const { + if (vs.ProtectionActive()) { + if (start > end) { + int t = start; + start = end; + end = t; + } + int mask = pdoc->stylingBitsMask; + for (int pos = start; pos < end; pos++) { + if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()) + return true; + } + } + return false; +} + +bool Editor::SelectionContainsProtected() { + // DONE, but untested...: make support rectangular selection + bool scp = false; + if (selType == selStream) { + scp = RangeContainsProtected(anchor, currentPos); + } else { + SelectionLineIterator lineIterator(this); + while (lineIterator.Iterate()) { + if (RangeContainsProtected(lineIterator.startPos, lineIterator.endPos)) { + scp = true; + break; + } + } + } + return scp; +} + +/** + * Asks document to find a good position and then moves out of any invisible positions. + */ +int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) { + pos = pdoc->MovePositionOutsideChar(pos, moveDir, checkLineEnd); + if (vs.ProtectionActive()) { + int mask = pdoc->stylingBitsMask; + if (moveDir > 0) { + if ((pos > 0) && vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected()) { + while ((pos < pdoc->Length()) && + (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())) + pos++; + } + } else if (moveDir < 0) { + if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()) { + while ((pos > 0) && + (vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected())) + pos--; + } + } + } + return pos; +} + +int Editor::MovePositionTo(int newPos, selTypes sel, bool ensureVisible) { + int delta = newPos - currentPos; + newPos = pdoc->ClampPositionIntoDocument(newPos); + newPos = MovePositionOutsideChar(newPos, delta); + if (sel != noSel) { + selType = sel; + } + if (sel != noSel || moveExtendsSelection) { + SetSelection(newPos); + } else { + SetEmptySelection(newPos); + } + ShowCaretAtCurrentPosition(); + if (ensureVisible) { + EnsureCaretVisible(); + } + NotifyMove(newPos); + return 0; +} + +int Editor::MovePositionSoVisible(int pos, int moveDir) { + pos = pdoc->ClampPositionIntoDocument(pos); + pos = MovePositionOutsideChar(pos, moveDir); + int lineDoc = pdoc->LineFromPosition(pos); + if (cs.GetVisible(lineDoc)) { + return pos; + } else { + int lineDisplay = cs.DisplayFromDoc(lineDoc); + if (moveDir > 0) { + // lineDisplay is already line before fold as lines in fold use display line of line after fold + lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed()); + return pdoc->LineStart(cs.DocFromDisplay(lineDisplay)); + } else { + lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed()); + return pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)); + } + } +} + +/** + * Choose the x position that the caret will try to stick to + * as it moves up and down. + */ +void Editor::SetLastXChosen() { + Point pt = LocationFromPosition(currentPos); + lastXChosen = pt.x; +} + +void Editor::ScrollTo(int line, bool moveThumb) { + int topLineNew = Platform::Clamp(line, 0, MaxScrollPos()); + if (topLineNew != topLine) { + // Try to optimise small scrolls + int linesToMove = topLine - topLineNew; + SetTopLine(topLineNew); + ShowCaretAtCurrentPosition(); + // Perform redraw rather than scroll if many lines would be redrawn anyway. +#ifndef UNDER_CE + if (abs(linesToMove) <= 10) { + ScrollText(linesToMove); + } else { + Redraw(); + } +#else + Redraw(); +#endif + if (moveThumb) { + SetVerticalScrollPos(); + } + } +} + +void Editor::ScrollText(int /* linesToMove */) { + //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove); + Redraw(); +} + +void Editor::HorizontalScrollTo(int xPos) { + //Platform::DebugPrintf("HorizontalScroll %d\n", xPos); + if (xPos < 0) + xPos = 0; + if ((wrapState == eWrapNone) && (xOffset != xPos)) { + xOffset = xPos; + SetHorizontalScrollPos(); + RedrawRect(GetClientRectangle()); + } +} + +void Editor::MoveCaretInsideView(bool ensureVisible) { + PRectangle rcClient = GetTextRectangle(); + Point pt = LocationFromPosition(currentPos); + if (pt.y < rcClient.top) { + MovePositionTo(PositionFromLocation( + Point(lastXChosen, rcClient.top)), + noSel, ensureVisible); + } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) { + int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight; + MovePositionTo(PositionFromLocation( + Point(lastXChosen, rcClient.top + yOfLastLineFullyDisplayed)), + noSel, ensureVisible); + } +} + +int Editor::DisplayFromPosition(int pos) { + int lineDoc = pdoc->LineFromPosition(pos); + int lineDisplay = cs.DisplayFromDoc(lineDoc); + AutoSurface surface(this); + AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc)); + if (surface && ll) { + LayoutLine(lineDoc, surface, vs, ll, wrapWidth); + unsigned int posLineStart = pdoc->LineStart(lineDoc); + int posInLine = pos - posLineStart; + lineDisplay--; // To make up for first increment ahead. + for (int subLine = 0; subLine < ll->lines; subLine++) { + if (posInLine >= ll->LineStart(subLine)) { + lineDisplay++; + } + } + } + return lineDisplay; +} + +/** + * Ensure the caret is reasonably visible in context. + * +Caret policy in SciTE + +If slop is set, we can define a slop value. +This value defines an unwanted zone (UZ) where the caret is... unwanted. +This zone is defined as a number of pixels near the vertical margins, +and as a number of lines near the horizontal margins. +By keeping the caret away from the edges, it is seen within its context, +so it is likely that the identifier that the caret is on can be completely seen, +and that the current line is seen with some of the lines following it which are +often dependent on that line. + +If strict is set, the policy is enforced... strictly. +The caret is centred on the display if slop is not set, +and cannot go in the UZ if slop is set. + +If jumps is set, the display is moved more energetically +so the caret can move in the same direction longer before the policy is applied again. +'3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin. + +If even is not set, instead of having symmetrical UZs, +the left and bottom UZs are extended up to right and top UZs respectively. +This way, we favour the displaying of useful information: the begining of lines, +where most code reside, and the lines after the caret, eg. the body of a function. + + | | | | | +slop | strict | jumps | even | Caret can go to the margin | When reaching limitÝ(caret going out of + | | | | | visibility or going into the UZ) display is... +-----+--------+-------+------+--------------------------------------------+-------------------------------------------------------------- + 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right + 0 | 0 | 0 | 1 | Yes | moved by one position + 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right + 0 | 0 | 1 | 1 | Yes | centred on the caret + 0 | 1 | - | 0 | Caret is always on top/on right of display | - + 0 | 1 | - | 1 | No, caret is always centred | - + 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ + 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ + 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin + 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin + 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | - + 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position + 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin +*/ +void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) { + //Platform::DebugPrintf("EnsureCaretVisible %d %s\n", xOffset, useMargin ? " margin" : " "); + PRectangle rcClient = GetTextRectangle(); + //int rcClientFullWidth = rcClient.Width(); + int posCaret = currentPos; + if (posDrag >= 0) { + posCaret = posDrag; + } + Point pt = LocationFromPosition(posCaret); + Point ptBottomCaret = pt; + ptBottomCaret.y += vs.lineHeight - 1; + int lineCaret = DisplayFromPosition(posCaret); + bool bSlop, bStrict, bJump, bEven; + + // Vertical positioning + if (vert && (pt.y < rcClient.top || ptBottomCaret.y > rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) { + int linesOnScreen = LinesOnScreen(); + int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2; + int newTopLine = topLine; + bSlop = (caretYPolicy & CARET_SLOP) != 0; + bStrict = (caretYPolicy & CARET_STRICT) != 0; + bJump = (caretYPolicy & CARET_JUMPS) != 0; + bEven = (caretYPolicy & CARET_EVEN) != 0; + + // It should be possible to scroll the window to show the caret, + // but this fails to remove the caret on GTK+ + if (bSlop) { // A margin is defined + int yMoveT, yMoveB; + if (bStrict) { + int yMarginT, yMarginB; + if (!useMargin) { + // In drag mode, avoid moves + // otherwise, a double click will select several lines. + yMarginT = yMarginB = 0; + } else { + // yMarginT must equal to caretYSlop, with a minimum of 1 and + // a maximum of slightly less than half the heigth of the text area. + yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen); + if (bEven) { + yMarginB = yMarginT; + } else { + yMarginB = linesOnScreen - yMarginT - 1; + } + } + yMoveT = yMarginT; + if (bEven) { + if (bJump) { + yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen); + } + yMoveB = yMoveT; + } else { + yMoveB = linesOnScreen - yMoveT - 1; + } + if (lineCaret < topLine + yMarginT) { + // Caret goes too high + newTopLine = lineCaret - yMoveT; + } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) { + // Caret goes too low + newTopLine = lineCaret - linesOnScreen + 1 + yMoveB; + } + } else { // Not strict + yMoveT = bJump ? caretYSlop * 3 : caretYSlop; + yMoveT = Platform::Clamp(yMoveT, 1, halfScreen); + if (bEven) { + yMoveB = yMoveT; + } else { + yMoveB = linesOnScreen - yMoveT - 1; + } + if (lineCaret < topLine) { + // Caret goes too high + newTopLine = lineCaret - yMoveT; + } else if (lineCaret > topLine + linesOnScreen - 1) { + // Caret goes too low + newTopLine = lineCaret - linesOnScreen + 1 + yMoveB; + } + } + } else { // No slop + if (!bStrict && !bJump) { + // Minimal move + if (lineCaret < topLine) { + // Caret goes too high + newTopLine = lineCaret; + } else if (lineCaret > topLine + linesOnScreen - 1) { + // Caret goes too low + if (bEven) { + newTopLine = lineCaret - linesOnScreen + 1; + } else { + newTopLine = lineCaret; + } + } + } else { // Strict or going out of display + if (bEven) { + // Always center caret + newTopLine = lineCaret - halfScreen; + } else { + // Always put caret on top of display + newTopLine = lineCaret; + } + } + } + newTopLine = Platform::Clamp(newTopLine, 0, MaxScrollPos()); + if (newTopLine != topLine) { + Redraw(); + SetTopLine(newTopLine); + SetVerticalScrollPos(); + } + } + + // Horizontal positioning + if (horiz && (wrapState == eWrapNone)) { + int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2; + int xOffsetNew = xOffset; + bSlop = (caretXPolicy & CARET_SLOP) != 0; + bStrict = (caretXPolicy & CARET_STRICT) != 0; + bJump = (caretXPolicy & CARET_JUMPS) != 0; + bEven = (caretXPolicy & CARET_EVEN) != 0; + + if (bSlop) { // A margin is defined + int xMoveL, xMoveR; + if (bStrict) { + int xMarginL, xMarginR; + if (!useMargin) { + // In drag mode, avoid moves unless very near of the margin + // otherwise, a simple click will select text. + xMarginL = xMarginR = 2; + } else { + // xMargin must equal to caretXSlop, with a minimum of 2 and + // a maximum of slightly less than half the width of the text area. + xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen); + if (bEven) { + xMarginL = xMarginR; + } else { + xMarginL = rcClient.Width() - xMarginR - 4; + } + } + if (bJump && bEven) { + // Jump is used only in even mode + xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen); + } else { + xMoveL = xMoveR = 0; // Not used, avoid a warning + } + if (pt.x < rcClient.left + xMarginL) { + // Caret is on the left of the display + if (bJump && bEven) { + xOffsetNew -= xMoveL; + } else { + // Move just enough to allow to display the caret + xOffsetNew -= (rcClient.left + xMarginL) - pt.x; + } + } else if (pt.x >= rcClient.right - xMarginR) { + // Caret is on the right of the display + if (bJump && bEven) { + xOffsetNew += xMoveR; + } else { + // Move just enough to allow to display the caret + xOffsetNew += pt.x - (rcClient.right - xMarginR) + 1; + } + } + } else { // Not strict + xMoveR = bJump ? caretXSlop * 3 : caretXSlop; + xMoveR = Platform::Clamp(xMoveR, 1, halfScreen); + if (bEven) { + xMoveL = xMoveR; + } else { + xMoveL = rcClient.Width() - xMoveR - 4; + } + if (pt.x < rcClient.left) { + // Caret is on the left of the display + xOffsetNew -= xMoveL; + } else if (pt.x >= rcClient.right) { + // Caret is on the right of the display + xOffsetNew += xMoveR; + } + } + } else { // No slop + if (bStrict || + (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) { + // Strict or going out of display + if (bEven) { + // Center caret + xOffsetNew += pt.x - rcClient.left - halfScreen; + } else { + // Put caret on right + xOffsetNew += pt.x - rcClient.right + 1; + } + } else { + // Move just enough to allow to display the caret + if (pt.x < rcClient.left) { + // Caret is on the left of the display + if (bEven) { + xOffsetNew -= rcClient.left - pt.x; + } else { + xOffsetNew += pt.x - rcClient.right + 1; + } + } else if (pt.x >= rcClient.right) { + // Caret is on the right of the display + xOffsetNew += pt.x - rcClient.right + 1; + } + } + } + // In case of a jump (find result) largely out of display, adjust the offset to display the caret + if (pt.x + xOffset < rcClient.left + xOffsetNew) { + xOffsetNew = pt.x + xOffset - rcClient.left; + } else if (pt.x + xOffset >= rcClient.right + xOffsetNew) { + xOffsetNew = pt.x + xOffset - rcClient.right + 1; + } + if (xOffsetNew < 0) { + xOffsetNew = 0; + } + if (xOffset != xOffsetNew) { + xOffset = xOffsetNew; + if (xOffsetNew > 0) { + PRectangle rcText = GetTextRectangle(); + if (horizontalScrollBarVisible == true && + rcText.Width() + xOffset > scrollWidth) { + scrollWidth = xOffset + rcText.Width(); + SetScrollBars(); + } + } + SetHorizontalScrollPos(); + Redraw(); + } + } + UpdateSystemCaret(); +} + +void Editor::ShowCaretAtCurrentPosition() { + if (hasFocus) { + caret.active = true; + caret.on = true; + SetTicking(true); + } else { + caret.active = false; + caret.on = false; + } + InvalidateCaret(); +} + +void Editor::DropCaret() { + caret.active = false; + InvalidateCaret(); +} + +void Editor::InvalidateCaret() { + if (posDrag >= 0) + InvalidateRange(posDrag, posDrag + 1); + else + InvalidateRange(currentPos, currentPos + 1); + UpdateSystemCaret(); +} + +void Editor::UpdateSystemCaret() { +} + +void Editor::NeedWrapping(int docLineStart, int docLineEnd) { + docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal()); + if (wrapStart > docLineStart) { + wrapStart = docLineStart; + llc.Invalidate(LineLayout::llPositions); + } + if (wrapEnd < docLineEnd) { + wrapEnd = docLineEnd; + } + wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal()); + // Wrap lines during idle. + if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) { + SetIdle(true); + } +} + +// Check if wrapping needed and perform any needed wrapping. +// fullwrap: if true, all lines which need wrapping will be done, +// in this single call. +// priorityWrapLineStart: If greater than zero, all lines starting from +// here to 1 page + 100 lines past will be wrapped (even if there are +// more lines under wrapping process in idle). +// If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be +// wrapped, if there are any wrapping going on in idle. (Generally this +// condition is called only from idler). +// Return true if wrapping occurred. +bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) { + // If there are any pending wraps, do them during idle if possible. + int linesInOneCall = LinesOnScreen() + 100; + if (wrapState != eWrapNone) { + if (wrapStart < wrapEnd) { + if (!SetIdle(true)) { + // Idle processing not supported so full wrap required. + fullWrap = true; + } + } + if (!fullWrap && priorityWrapLineStart >= 0 && + // .. and if the paint window is outside pending wraps + (((priorityWrapLineStart + linesInOneCall) < wrapStart) || + (priorityWrapLineStart > wrapEnd))) { + // No priority wrap pending + return false; + } + } + int goodTopLine = topLine; + bool wrapOccurred = false; + if (wrapStart <= pdoc->LinesTotal()) { + if (wrapState == eWrapNone) { + if (wrapWidth != LineLayout::wrapWidthInfinite) { + wrapWidth = LineLayout::wrapWidthInfinite; + for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) { + cs.SetHeight(lineDoc, 1); + } + wrapOccurred = true; + } + wrapStart = wrapLineLarge; + wrapEnd = wrapLineLarge; + } else { + if (wrapEnd >= pdoc->LinesTotal()) + wrapEnd = pdoc->LinesTotal(); + //ElapsedTime et; + int lineDocTop = cs.DocFromDisplay(topLine); + int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop); + PRectangle rcTextArea = GetClientRectangle(); + rcTextArea.left = vs.fixedColumnWidth; + rcTextArea.right -= vs.rightMarginWidth; + wrapWidth = rcTextArea.Width(); + // Ensure all of the document is styled. + pdoc->EnsureStyledTo(pdoc->Length()); + RefreshStyleData(); + AutoSurface surface(this); + if (surface) { + bool priorityWrap = false; + int lastLineToWrap = wrapEnd; + int lineToWrap = wrapStart; + if (!fullWrap) { + if (priorityWrapLineStart >= 0) { + // This is a priority wrap. + lineToWrap = priorityWrapLineStart; + lastLineToWrap = priorityWrapLineStart + linesInOneCall; + priorityWrap = true; + } else { + // This is idle wrap. + lastLineToWrap = wrapStart + linesInOneCall; + } + if (lastLineToWrap >= wrapEnd) + lastLineToWrap = wrapEnd; + } // else do a fullWrap. + + // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap); + // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd); + while (lineToWrap < lastLineToWrap) { + AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap)); + int linesWrapped = 1; + if (ll) { + LayoutLine(lineToWrap, surface, vs, ll, wrapWidth); + linesWrapped = ll->lines; + } + if (cs.SetHeight(lineToWrap, linesWrapped)) { + wrapOccurred = true; + } + lineToWrap++; + } + if (!priorityWrap) + wrapStart = lineToWrap; + // If wrapping is done, bring it to resting position + if (wrapStart >= wrapEnd) { + wrapStart = wrapLineLarge; + wrapEnd = wrapLineLarge; + } + } + goodTopLine = cs.DisplayFromDoc(lineDocTop); + if (subLineTop < cs.GetHeight(lineDocTop)) + goodTopLine += subLineTop; + else + goodTopLine += cs.GetHeight(lineDocTop); + //double durWrap = et.Duration(true); + //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap); + } + } + if (wrapOccurred) { + SetScrollBars(); + SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos())); + SetVerticalScrollPos(); + } + return wrapOccurred; +} + +void Editor::LinesJoin() { + if (!RangeContainsProtected(targetStart, targetEnd)) { + pdoc->BeginUndoAction(); + bool prevNonWS = true; + for (int pos = targetStart; pos < targetEnd; pos++) { + if (IsEOLChar(pdoc->CharAt(pos))) { + targetEnd -= pdoc->LenChar(pos); + pdoc->DelChar(pos); + if (prevNonWS) { + // Ensure at least one space separating previous lines + pdoc->InsertChar(pos, ' '); + } + } else { + prevNonWS = pdoc->CharAt(pos) != ' '; + } + } + pdoc->EndUndoAction(); + } +} + +const char *StringFromEOLMode(int eolMode) { + if (eolMode == SC_EOL_CRLF) { + return "\r\n"; + } else if (eolMode == SC_EOL_CR) { + return "\r"; + } else { + return "\n"; + } +} + +void Editor::LinesSplit(int pixelWidth) { + if (!RangeContainsProtected(targetStart, targetEnd)) { + if (pixelWidth == 0) { + PRectangle rcText = GetTextRectangle(); + pixelWidth = rcText.Width(); + } + int lineStart = pdoc->LineFromPosition(targetStart); + int lineEnd = pdoc->LineFromPosition(targetEnd); + const char *eol = StringFromEOLMode(pdoc->eolMode); + pdoc->BeginUndoAction(); + for (int line = lineStart; line <= lineEnd; line++) { + AutoSurface surface(this); + AutoLineLayout ll(llc, RetrieveLineLayout(line)); + if (surface && ll) { + unsigned int posLineStart = pdoc->LineStart(line); + LayoutLine(line, surface, vs, ll, pixelWidth); + for (int subLine = 1; subLine < ll->lines; subLine++) { + pdoc->InsertString(posLineStart + (subLine - 1) * strlen(eol) + + ll->LineStart(subLine), eol); + targetEnd += static_cast(strlen(eol)); + } + } + lineEnd = pdoc->LineFromPosition(targetEnd); + } + pdoc->EndUndoAction(); + } +} + +int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) { + if (vs.markers[markerCheck].markType == SC_MARK_EMPTY) + return markerDefault; + return markerCheck; +} + +// Avoid 64 bit compiler warnings. +// Scintilla does not support text buffers larger than 2**31 +static int istrlen(const char *s) { + return static_cast(strlen(s)); +} + +void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) { + if (vs.fixedColumnWidth == 0) + return; + + PRectangle rcMargin = GetClientRectangle(); + rcMargin.right = vs.fixedColumnWidth; + + if (!rc.Intersects(rcMargin)) + return; + + Surface *surface; + if (bufferedDraw) { + surface = pixmapSelMargin; + } else { + surface = surfWindow; + } + + PRectangle rcSelMargin = rcMargin; + rcSelMargin.right = rcMargin.left; + + for (int margin = 0; margin < vs.margins; margin++) { + if (vs.ms[margin].width > 0) { + + rcSelMargin.left = rcSelMargin.right; + rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width; + + if (vs.ms[margin].style != SC_MARGIN_NUMBER) { + /* alternate scheme: + if (vs.ms[margin].mask & SC_MASK_FOLDERS) + surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated); + else + // Required because of special way brush is created for selection margin + surface->FillRectangle(rcSelMargin, pixmapSelPattern); + */ + if (vs.ms[margin].mask & SC_MASK_FOLDERS) + // Required because of special way brush is created for selection margin + surface->FillRectangle(rcSelMargin, *pixmapSelPattern); + else { + ColourAllocated colour; + switch (vs.ms[margin].style) { + case SC_MARGIN_BACK: + colour = vs.styles[STYLE_DEFAULT].back.allocated; + break; + case SC_MARGIN_FORE: + colour = vs.styles[STYLE_DEFAULT].fore.allocated; + break; + default: + colour = vs.styles[STYLE_LINENUMBER].back.allocated; + break; + } + surface->FillRectangle(rcSelMargin, colour); + } + } else { + surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated); + } + + int visibleLine = topLine; + int yposScreen = 0; + + // Work out whether the top line is whitespace located after a + // lessening of fold level which implies a 'fold tail' but which should not + // be displayed until the last of a sequence of whitespace. + bool needWhiteClosure = false; + int level = pdoc->GetLevel(cs.DocFromDisplay(topLine)); + if (level & SC_FOLDLEVELWHITEFLAG) { + int lineBack = cs.DocFromDisplay(topLine); + int levelPrev = level; + while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) { + lineBack--; + levelPrev = pdoc->GetLevel(lineBack); + } + if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) { + if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK)) + needWhiteClosure = true; + } + } + + // Old code does not know about new markers needed to distinguish all cases + int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID, + SC_MARKNUM_FOLDEROPEN); + int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND, + SC_MARKNUM_FOLDER); + + while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) { + + PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed()); + + int lineDoc = cs.DocFromDisplay(visibleLine); + PLATFORM_ASSERT(cs.GetVisible(lineDoc)); + bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc); + + // Decide which fold indicator should be displayed + level = pdoc->GetLevel(lineDoc); + int levelNext = pdoc->GetLevel(lineDoc + 1); + int marks = pdoc->GetMark(lineDoc); + if (!firstSubLine) + marks = 0; + int levelNum = level & SC_FOLDLEVELNUMBERMASK; + int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK; + if (level & SC_FOLDLEVELHEADERFLAG) { + if (firstSubLine) { + if (cs.GetExpanded(lineDoc)) { + if (levelNum == SC_FOLDLEVELBASE) + marks |= 1 << SC_MARKNUM_FOLDEROPEN; + else + marks |= 1 << folderOpenMid; + } else { + if (levelNum == SC_FOLDLEVELBASE) + marks |= 1 << SC_MARKNUM_FOLDER; + else + marks |= 1 << folderEnd; + } + } else { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } + needWhiteClosure = false; + } else if (level & SC_FOLDLEVELWHITEFLAG) { + if (needWhiteClosure) { + if (levelNext & SC_FOLDLEVELWHITEFLAG) { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } else if (levelNum > SC_FOLDLEVELBASE) { + marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL; + needWhiteClosure = false; + } else { + marks |= 1 << SC_MARKNUM_FOLDERTAIL; + needWhiteClosure = false; + } + } else if (levelNum > SC_FOLDLEVELBASE) { + if (levelNextNum < levelNum) { + if (levelNextNum > SC_FOLDLEVELBASE) { + marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL; + } else { + marks |= 1 << SC_MARKNUM_FOLDERTAIL; + } + } else { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } + } + } else if (levelNum > SC_FOLDLEVELBASE) { + if (levelNextNum < levelNum) { + needWhiteClosure = false; + if (levelNext & SC_FOLDLEVELWHITEFLAG) { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + needWhiteClosure = true; + } else if (levelNextNum > SC_FOLDLEVELBASE) { + marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL; + } else { + marks |= 1 << SC_MARKNUM_FOLDERTAIL; + } + } else { + marks |= 1 << SC_MARKNUM_FOLDERSUB; + } + } + + marks &= vs.ms[margin].mask; + PRectangle rcMarker = rcSelMargin; + rcMarker.top = yposScreen; + rcMarker.bottom = yposScreen + vs.lineHeight; + if (vs.ms[margin].style == SC_MARGIN_NUMBER) { + char number[100]; + number[0] = '\0'; + if (firstSubLine) + sprintf(number, "%d", lineDoc + 1); + if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) { + int lev = pdoc->GetLevel(lineDoc); + sprintf(number, "%c%c %03X %03X", + (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_', + (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_', + lev & SC_FOLDLEVELNUMBERMASK, + lev >> 16 + ); + } + PRectangle rcNumber = rcMarker; + // Right justify + int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number)); + int xpos = rcNumber.right - width - 3; + rcNumber.left = xpos; + surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font, + rcNumber.top + vs.maxAscent, number, istrlen(number), + vs.styles[STYLE_LINENUMBER].fore.allocated, + vs.styles[STYLE_LINENUMBER].back.allocated); + } + + if (marks) { + for (int markBit = 0; (markBit < 32) && marks; markBit++) { + if (marks & 1) { + vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font); + } + marks >>= 1; + } + } + + visibleLine++; + yposScreen += vs.lineHeight; + } + } + } + + PRectangle rcBlankMargin = rcMargin; + rcBlankMargin.left = rcSelMargin.right; + surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated); + + if (bufferedDraw) { + surfWindow->Copy(rcMargin, Point(), *pixmapSelMargin); + } +} + +void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) { + int ydiff = (rcTab.bottom - rcTab.top) / 2; + int xhead = rcTab.right - 1 - ydiff; + if (xhead <= rcTab.left) { + ydiff -= rcTab.left - xhead - 1; + xhead = rcTab.left - 1; + } + if ((rcTab.left + 2) < (rcTab.right - 1)) + surface->MoveTo(rcTab.left + 2, ymid); + else + surface->MoveTo(rcTab.right - 1, ymid); + surface->LineTo(rcTab.right - 1, ymid); + surface->LineTo(xhead, ymid - ydiff); + surface->MoveTo(rcTab.right - 1, ymid); + surface->LineTo(xhead, ymid + ydiff); +} + +static bool IsSpaceOrTab(char ch) { + return ch == ' ' || ch == '\t'; +} + +LineLayout *Editor::RetrieveLineLayout(int lineNumber) { + int posLineStart = pdoc->LineStart(lineNumber); + int posLineEnd = pdoc->LineStart(lineNumber + 1); + int lineCaret = pdoc->LineFromPosition(currentPos); + return llc.Retrieve(lineNumber, lineCaret, + posLineEnd - posLineStart, pdoc->GetStyleClock(), + LinesOnScreen() + 1, pdoc->LinesTotal()); +} + +/** + * Fill in the LineLayout data for the given line. + * Copy the given @a line and its styles from the document into local arrays. + * Also determine the x position at which each character starts. + */ +void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) { + if (!ll) + return; + PLATFORM_ASSERT(line < pdoc->LinesTotal()); + int posLineStart = pdoc->LineStart(line); + int posLineEnd = pdoc->LineStart(line + 1); + // If the line is very long, limit the treatment to a length that should fit in the viewport + if (posLineEnd > (posLineStart + ll->maxLineLength)) { + posLineEnd = posLineStart + ll->maxLineLength; + } + if (ll->validity == LineLayout::llCheckTextAndStyle) { + int lineLength = posLineEnd - posLineStart; + if (!vstyle.viewEOL) { + int cid = posLineEnd - 1; + while ((cid > posLineStart) && IsEOLChar(pdoc->CharAt(cid))) { + cid--; + lineLength--; + } + } + if (lineLength == ll->numCharsInLine) { + // See if chars, styles, indicators, are all the same + bool allSame = true; + const int styleMask = pdoc->stylingBitsMask; + // Check base line layout + char styleByte = 0; + int numCharsInLine = 0; + while (numCharsInLine < lineLength) { + int charInDoc = numCharsInLine + posLineStart; + char chDoc = pdoc->CharAt(charInDoc); + styleByte = pdoc->StyleAt(charInDoc); + allSame = allSame && + (ll->styles[numCharsInLine] == static_cast(styleByte & styleMask)); + allSame = allSame && + (ll->indicators[numCharsInLine] == static_cast(styleByte & ~styleMask)); + if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed) + allSame = allSame && + (ll->chars[numCharsInLine] == chDoc); + else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower) + allSame = allSame && + (ll->chars[numCharsInLine] == static_cast(tolower(chDoc))); + else // Style::caseUpper + allSame = allSame && + (ll->chars[numCharsInLine] == static_cast(toupper(chDoc))); + numCharsInLine++; + } + allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled + if (allSame) { + ll->validity = LineLayout::llPositions; + } else { + ll->validity = LineLayout::llInvalid; + } + } else { + ll->validity = LineLayout::llInvalid; + } + } + if (ll->validity == LineLayout::llInvalid) { + ll->widthLine = LineLayout::wrapWidthInfinite; + ll->lines = 1; + int numCharsInLine = 0; + if (vstyle.edgeState == EDGE_BACKGROUND) { + ll->edgeColumn = pdoc->FindColumn(line, theEdge); + if (ll->edgeColumn >= posLineStart) { + ll->edgeColumn -= posLineStart; + } + } else { + ll->edgeColumn = -1; + } + + char styleByte = 0; + int styleMask = pdoc->stylingBitsMask; + ll->styleBitsSet = 0; + // Fill base line layout + for (int charInDoc = posLineStart; charInDoc < posLineEnd; charInDoc++) { + char chDoc = pdoc->CharAt(charInDoc); + styleByte = pdoc->StyleAt(charInDoc); + ll->styleBitsSet |= styleByte; + if (vstyle.viewEOL || (!IsEOLChar(chDoc))) { + ll->chars[numCharsInLine] = chDoc; + ll->styles[numCharsInLine] = static_cast(styleByte & styleMask); + ll->indicators[numCharsInLine] = static_cast(styleByte & ~styleMask); + if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseUpper) + ll->chars[numCharsInLine] = static_cast(toupper(chDoc)); + else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower) + ll->chars[numCharsInLine] = static_cast(tolower(chDoc)); + numCharsInLine++; + } + } + ll->xHighlightGuide = 0; + // Extra element at the end of the line to hold end x position and act as + ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character + ll->styles[numCharsInLine] = styleByte; // For eolFilled + ll->indicators[numCharsInLine] = 0; + + // Layout the line, determining the position of each character, + // with an extra element at the end for the end of the line. + int startseg = 0; // Start of the current segment, in char. number + int startsegx = 0; // Start of the current segment, in pixels + ll->positions[0] = 0; + unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars; + bool lastSegItalics = false; + Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font; + + int ctrlCharWidth[32] = {0}; + bool isControlNext = IsControlCharacter(ll->chars[0]); + for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) { + bool isControl = isControlNext; + isControlNext = IsControlCharacter(ll->chars[charInLine + 1]); + if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) || + isControl || isControlNext) { + ll->positions[startseg] = 0; + if (vstyle.styles[ll->styles[charInLine]].visible) { + if (isControl) { + if (ll->chars[charInLine] == '\t') { + ll->positions[charInLine + 1] = ((((startsegx + 2) / + tabWidth) + 1) * tabWidth) - startsegx; + } else if (controlCharSymbol < 32) { + if (ctrlCharWidth[ll->chars[charInLine]] == 0) { + const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]); + // +3 For a blank on front and rounded edge each side: + ctrlCharWidth[ll->chars[charInLine]] = + surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + 3; + } + ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]]; + } else { + char cc[2] = { static_cast(controlCharSymbol), '\0' }; + surface->MeasureWidths(ctrlCharsFont, cc, 1, + ll->positions + startseg + 1); + } + lastSegItalics = false; + } else { // Regular character + int lenSeg = charInLine - startseg + 1; + if ((lenSeg == 1) && (' ' == ll->chars[startseg])) { + lastSegItalics = false; + // Over half the segments are single characters and of these about half are space characters. + ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth; + } else { + lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic; + surface->MeasureWidths(vstyle.styles[ll->styles[charInLine]].font, ll->chars + startseg, + lenSeg, ll->positions + startseg + 1); + } + } + } else { // invisible + for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) { + ll->positions[posToZero] = 0; + } + } + for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) { + ll->positions[posToIncrease] += startsegx; + } + startsegx = ll->positions[charInLine + 1]; + startseg = charInLine + 1; + } + } + // Small hack to make lines that end with italics not cut off the edge of the last character + if ((startseg > 0) && lastSegItalics) { + ll->positions[startseg] += 2; + } + ll->numCharsInLine = numCharsInLine; + ll->validity = LineLayout::llPositions; + } + // Hard to cope when too narrow, so just assume there is space + if (width < 20) { + width = 20; + } + if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) { + ll->widthLine = width; + if (width == LineLayout::wrapWidthInfinite) { + ll->lines = 1; + } else if (width > ll->positions[ll->numCharsInLine]) { + // Simple common case where line does not need wrapping. + ll->lines = 1; + } else { + if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) { + width -= vstyle.aveCharWidth; // take into account the space for end wrap mark + } + ll->lines = 0; + // Calculate line start positions based upon width. + // For now this is simplistic - wraps on byte rather than character and + // in the middle of words. Should search for spaces or style changes. + int lastGoodBreak = 0; + int lastLineStart = 0; + int startOffset = 0; + int p = 0; + while (p < ll->numCharsInLine) { + if ((ll->positions[p + 1] - startOffset) >= width) { + if (lastGoodBreak == lastLineStart) { + // Try moving to start of last character + if (p > 0) { + lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1) + - posLineStart; + } + if (lastGoodBreak == lastLineStart) { + // Ensure at least one character on line. + lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1) + - posLineStart; + } + } + lastLineStart = lastGoodBreak; + ll->lines++; + ll->SetLineStart(ll->lines, lastGoodBreak); + startOffset = ll->positions[lastGoodBreak]; + // take into account the space for start wrap mark and indent + startOffset -= actualWrapVisualStartIndent * vstyle.aveCharWidth; + p = lastGoodBreak + 1; + continue; + } + if (p > 0) { + if (wrapState == eWrapChar) { + lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1) + - posLineStart; + p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart; + continue; + } else if (ll->styles[p] != ll->styles[p - 1]) { + lastGoodBreak = p; + } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) { + lastGoodBreak = p; + } + } + p++; + } + ll->lines++; + } + ll->validity = LineLayout::llLines; + } +} + +ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw) { + return primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated; +} + +ColourAllocated Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground, + ColourAllocated background, bool inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) { + if (inSelection) { + if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) { + return SelectionBackground(vsDraw); + } + } else { + if ((vsDraw.edgeState == EDGE_BACKGROUND) && + (i >= ll->edgeColumn) && + !IsEOLChar(ll->chars[i])) + return vsDraw.edgecolour.allocated; + if (inHotspot && vsDraw.hotspotBackgroundSet) + return vsDraw.hotspotBackground.allocated; + if (overrideBackground) + return background; + } + return vsDraw.styles[styleMain].back.allocated; +} + +void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) { + Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0); + PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom); + surface->Copy(rcCopyArea, from, + highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide); +} + +void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace, + bool isEndMarker, ColourAllocated wrapColour) { + surface->PenColour(wrapColour); + + enum { xa = 1 }; // gap before start + int w = rcPlace.right - rcPlace.left - xa - 1; + + bool xStraight = isEndMarker; // x-mirrored symbol for start marker + bool yStraight = true; + //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed + + int x0 = xStraight ? rcPlace.left : rcPlace.right - 1; + int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1; + + int dy = (rcPlace.bottom - rcPlace.top) / 5; + int y = (rcPlace.bottom - rcPlace.top) / 2 + dy; + + struct Relative { + Surface *surface; + int xBase; + int xDir; + int yBase; + int yDir; + void MoveTo(int xRelative, int yRelative) { + surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative); + } + void LineTo(int xRelative, int yRelative) { + surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative); + } + }; + Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1}; + + // arrow head + rel.MoveTo(xa, y); + rel.LineTo(xa + 2*w / 3, y - dy); + rel.MoveTo(xa, y); + rel.LineTo(xa + 2*w / 3, y + dy); + + // arrow body + rel.MoveTo(xa, y); + rel.LineTo(xa + w, y); + rel.LineTo(xa + w, y - 2 * dy); + rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not... + y - 2 * dy); +} + +static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourAllocated fill, int alpha) { + if (alpha != SC_ALPHA_NOALPHA) { + surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0); + } +} + +void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll, + int line, int lineEnd, int xStart, int subLine, int subLineStart, + bool overrideBackground, ColourAllocated background, + bool drawWrapMarkEnd, ColourAllocated wrapColour) { + + int styleMask = pdoc->stylingBitsMask; + PRectangle rcSegment = rcLine; + + // Fill in a PRectangle representing the end of line characters + int xEol = ll->positions[lineEnd] - subLineStart; + rcSegment.left = xEol + xStart; + rcSegment.right = xEol + vsDraw.aveCharWidth + xStart; + int posLineEnd = pdoc->LineStart(line + 1); + bool eolInSelection = (subLine == (ll->lines - 1)) && + (posLineEnd > ll->selStart) && (posLineEnd <= ll->selEnd) && (ll->selStart != ll->selEnd); + + if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) { + surface->FillRectangle(rcSegment, SelectionBackground(vsDraw)); + } else { + if (overrideBackground) { + surface->FillRectangle(rcSegment, background); + } else { + surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated); + } + if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha != SC_ALPHA_NOALPHA)) { + SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha); + } + } + + rcSegment.left = xEol + vsDraw.aveCharWidth + xStart; + rcSegment.right = rcLine.right; + if (overrideBackground) { + surface->FillRectangle(rcSegment, background); + } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) { + surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated); + } else { + surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated); + } + + if (drawWrapMarkEnd) { + PRectangle rcPlace = rcSegment; + + if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) { + rcPlace.left = xEol + xStart; + rcPlace.right = rcPlace.left + vsDraw.aveCharWidth; + } else { + // draw left of the right text margin, to avoid clipping by the current clip rect + rcPlace.right = rcLine.right - vs.rightMarginWidth; + rcPlace.left = rcPlace.right - vsDraw.aveCharWidth; + } + DrawWrapMarker(surface, rcPlace, true, wrapColour); + } +} + +void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart, + PRectangle rcLine, LineLayout *ll, int subLine) { + + PRectangle rcSegment = rcLine; + + // Using one font for all control characters so it can be controlled independently to ensure + // the box goes around the characters tightly. Seems to be no way to work out what height + // is taken by an individual character - internal leading gives varying results. + Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font; + + // See if something overrides the line background color: Either if caret is on the line + // and background color is set for that, or if a marker is defined that forces its background + // color onto the line, or if a marker is defined but has no selection margin in which to + // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order + // with the earlier taking precedence. When multiple markers cause background override, + // the color for the highest numbered one is used. + bool overrideBackground = false; + ColourAllocated background; + if (caret.active && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) { + overrideBackground = true; + background = vsDraw.caretLineBackground.allocated; + } + if (!overrideBackground) { + int marks = pdoc->GetMark(line); + for (int markBit = 0; (markBit < 32) && marks; markBit++) { + if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) && + (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) { + background = vsDraw.markers[markBit].back.allocated; + overrideBackground = true; + } + marks >>= 1; + } + } + if (!overrideBackground) { + if (vsDraw.maskInLine) { + int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine; + if (marksMasked) { + for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) { + if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) && + (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) { + overrideBackground = true; + background = vsDraw.markers[markBit].back.allocated; + } + marksMasked >>= 1; + } + } + } + } + + bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) && + (!overrideBackground) && (vsDraw.whitespaceBackgroundSet); + + bool inIndentation = subLine == 0; // Do not handle indentation except on first subline. + int indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth; + + int posLineStart = pdoc->LineStart(line); + + int startseg = ll->LineStart(subLine); + int subLineStart = ll->positions[startseg]; + int lineStart = 0; + int lineEnd = 0; + if (subLine < ll->lines) { + lineStart = ll->LineStart(subLine); + lineEnd = ll->LineStart(subLine + 1); + } + + bool drawWrapMarkEnd = false; + + if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) { + if (subLine + 1 < ll->lines) { + drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0; + } + } + + if (actualWrapVisualStartIndent != 0) { + + bool continuedWrapLine = false; + if (subLine < ll->lines) { + continuedWrapLine = ll->LineStart(subLine) != 0; + } + + if (continuedWrapLine) { + // draw continuation rect + PRectangle rcPlace = rcSegment; + + rcPlace.left = ll->positions[startseg] + xStart - subLineStart; + rcPlace.right = rcPlace.left + actualWrapVisualStartIndent * vsDraw.aveCharWidth; + + // default bgnd here.. + surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated); + + // main line style would be below but this would be inconsistent with end markers + // also would possibly not be the style at wrap point + //int styleMain = ll->styles[lineStart]; + //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back.allocated); + + if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) { + + if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT) + rcPlace.left = rcPlace.right - vsDraw.aveCharWidth; + else + rcPlace.right = rcPlace.left + vsDraw.aveCharWidth; + + DrawWrapMarker(surface, rcPlace, false, vsDraw.whitespaceForeground.allocated); + } + + xStart += actualWrapVisualStartIndent * vsDraw.aveCharWidth; + } + } + + int i; + + // Background drawing loop + for (i = lineStart; twoPhaseDraw && (i < lineEnd); i++) { + + int iDoc = i + posLineStart; + // If there is the end of a style run for any reason + if ((ll->styles[i] != ll->styles[i + 1]) || + i == (lineEnd - 1) || + IsControlCharacter(ll->chars[i]) || IsControlCharacter(ll->chars[i + 1]) || + ((ll->selStart != ll->selEnd) && ((iDoc + 1 == ll->selStart) || (iDoc + 1 == ll->selEnd))) || + (i == (ll->edgeColumn - 1))) { + rcSegment.left = ll->positions[startseg] + xStart - subLineStart; + rcSegment.right = ll->positions[i + 1] + xStart - subLineStart; + // Only try to draw if really visible - enhances performance by not calling environment to + // draw strings that are completely past the right side of the window. + if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) { + int styleMain = ll->styles[i]; + bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd); + bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd); + ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll); + if (ll->chars[i] == '\t') { + // Tab display + if (drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) + textBack = vsDraw.whitespaceBackground.allocated; + surface->FillRectangle(rcSegment, textBack); + } else if (IsControlCharacter(ll->chars[i])) { + // Control character display + inIndentation = false; + surface->FillRectangle(rcSegment, textBack); + } else { + // Normal text display + surface->FillRectangle(rcSegment, textBack); + if (vsDraw.viewWhitespace != wsInvisible || + (inIndentation && vsDraw.viewIndentationGuides)) { + for (int cpos = 0; cpos <= i - startseg; cpos++) { + if (ll->chars[cpos + startseg] == ' ') { + if (drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) { + PRectangle rcSpace(ll->positions[cpos + startseg] + xStart, rcSegment.top, + ll->positions[cpos + startseg + 1] + xStart, rcSegment.bottom); + surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated); + } + } else { + inIndentation = false; + } + } + } + } + } else if (rcSegment.left > rcLine.right) { + break; + } + startseg = i + 1; + } + } + + if (twoPhaseDraw) { + DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd, + xStart, subLine, subLineStart, overrideBackground, background, + drawWrapMarkEnd, vsDraw.whitespaceForeground.allocated); + } + + inIndentation = subLine == 0; // Do not handle indentation except on first subline. + startseg = ll->LineStart(subLine); + // Foreground drawing loop + for (i = lineStart; i < lineEnd; i++) { + + int iDoc = i + posLineStart; + // If there is the end of a style run for any reason + if ((ll->styles[i] != ll->styles[i + 1]) || + i == (lineEnd - 1) || + IsControlCharacter(ll->chars[i]) || IsControlCharacter(ll->chars[i + 1]) || + ((ll->selStart != ll->selEnd) && ((iDoc + 1 == ll->selStart) || (iDoc + 1 == ll->selEnd))) || + (i == (ll->edgeColumn - 1))) { + rcSegment.left = ll->positions[startseg] + xStart - subLineStart; + rcSegment.right = ll->positions[i + 1] + xStart - subLineStart; + // Only try to draw if really visible - enhances performance by not calling environment to + // draw strings that are completely past the right side of the window. + if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) { + int styleMain = ll->styles[i]; + ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated; + Font &textFont = vsDraw.styles[styleMain].font; + //hotspot foreground + if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) { + if (vsDraw.hotspotForegroundSet) + textFore = vsDraw.hotspotForeground.allocated; + } + bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd); + if (inSelection && (vsDraw.selforeset)) { + textFore = vsDraw.selforeground.allocated; + } + bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd); + ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll); + if (ll->chars[i] == '\t') { + // Tab display + if (!twoPhaseDraw) { + if (drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) + textBack = vsDraw.whitespaceBackground.allocated; + surface->FillRectangle(rcSegment, textBack); + } + if ((vsDraw.viewWhitespace != wsInvisible) || ((inIndentation && vsDraw.viewIndentationGuides))) { + if (vsDraw.whitespaceForegroundSet) + textFore = vsDraw.whitespaceForeground.allocated; + surface->PenColour(textFore); + } + if (inIndentation && vsDraw.viewIndentationGuides) { + for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) { + if (xIG >= ll->positions[i] && xIG > 0) { + DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment, + (ll->xHighlightGuide == xIG)); + } + } + } + if (vsDraw.viewWhitespace != wsInvisible) { + if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) { + PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4, + rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent); + DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2); + } + } + } else if (IsControlCharacter(ll->chars[i])) { + // Control character display + inIndentation = false; + if (controlCharSymbol < 32) { + // Draw the character + const char *ctrlChar = ControlCharacterString(ll->chars[i]); + if (!twoPhaseDraw) { + surface->FillRectangle(rcSegment, textBack); + } + int normalCharHeight = surface->Ascent(ctrlCharsFont) - + surface->InternalLeading(ctrlCharsFont); + PRectangle rcCChar = rcSegment; + rcCChar.left = rcCChar.left + 1; + rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight; + rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1; + PRectangle rcCentral = rcCChar; + rcCentral.top++; + rcCentral.bottom--; + surface->FillRectangle(rcCentral, textFore); + PRectangle rcChar = rcCChar; + rcChar.left++; + rcChar.right--; + surface->DrawTextClipped(rcChar, ctrlCharsFont, + rcSegment.top + vsDraw.maxAscent, ctrlChar, istrlen(ctrlChar), + textBack, textFore); + } else { + char cc[2] = { static_cast(controlCharSymbol), '\0' }; + surface->DrawTextNoClip(rcSegment, ctrlCharsFont, + rcSegment.top + vsDraw.maxAscent, + cc, 1, textBack, textFore); + } + } else { + // Normal text display + if (vsDraw.styles[styleMain].visible) { + if (twoPhaseDraw) { + surface->DrawTextTransparent(rcSegment, textFont, + rcSegment.top + vsDraw.maxAscent, ll->chars + startseg, + i - startseg + 1, textFore); + } else { + surface->DrawTextNoClip(rcSegment, textFont, + rcSegment.top + vsDraw.maxAscent, ll->chars + startseg, + i - startseg + 1, textFore, textBack); + } + } + if (vsDraw.viewWhitespace != wsInvisible || + (inIndentation && vsDraw.viewIndentationGuides)) { + for (int cpos = 0; cpos <= i - startseg; cpos++) { + if (ll->chars[cpos + startseg] == ' ') { + if (vsDraw.viewWhitespace != wsInvisible) { + if (vsDraw.whitespaceForegroundSet) + textFore = vsDraw.whitespaceForeground.allocated; + if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) { + int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2; + if (!twoPhaseDraw && drawWhitespaceBackground && + (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) { + textBack = vsDraw.whitespaceBackground.allocated; + PRectangle rcSpace(ll->positions[cpos + startseg] + xStart, rcSegment.top, ll->positions[cpos + startseg + 1] + xStart, rcSegment.bottom); + surface->FillRectangle(rcSpace, textBack); + } + PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0); + rcDot.right = rcDot.left + 1; + rcDot.bottom = rcDot.top + 1; + surface->FillRectangle(rcDot, textFore); + } + } + if (inIndentation && vsDraw.viewIndentationGuides) { + int startSpace = ll->positions[cpos + startseg]; + if (startSpace > 0 && (startSpace % indentWidth == 0)) { + DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment, + (ll->xHighlightGuide == ll->positions[cpos + startseg])); + } + } + } else { + inIndentation = false; + } + } + } + } + if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd ) { + PRectangle rcUL = rcSegment; + rcUL.top = rcUL.top + vsDraw.maxAscent + 1; + rcUL.bottom = rcUL.top + 1; + if (vsDraw.hotspotForegroundSet) + surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated); + else + surface->FillRectangle(rcUL, textFore); + } else if (vsDraw.styles[styleMain].underline) { + PRectangle rcUL = rcSegment; + rcUL.top = rcUL.top + vsDraw.maxAscent + 1; + rcUL.bottom = rcUL.top + 1; + surface->FillRectangle(rcUL, textFore); + } + } else if (rcSegment.left > rcLine.right) { + break; + } + startseg = i + 1; + } + } + + // Draw indicators + // foreach indicator... + for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) { + if (!(mask & ll->styleBitsSet)) { + mask <<= 1; + continue; + } + int startPos = -1; + // foreach style pos in line... + for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) { + // look for starts... + if (startPos < 0) { + // NOT in indicator run, looking for START + if (indicPos < lineEnd && (ll->indicators[indicPos] & mask)) + startPos = indicPos; + } + // ... or ends + if (startPos >= 0) { + // IN indicator run, looking for END + if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) { + // AT end of indicator run, DRAW it! + PRectangle rcIndic( + ll->positions[startPos] + xStart - subLineStart, + rcLine.top + vsDraw.maxAscent, + ll->positions[indicPos] + xStart - subLineStart, + rcLine.top + vsDraw.maxAscent + 3); + vsDraw.indicators[indicnum].Draw(surface, rcIndic, rcLine); + // RESET control var + startPos = -1; + } + } + } + mask <<= 1; + } + // End of the drawing of the current line + if (!twoPhaseDraw) { + DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd, + xStart, subLine, subLineStart, overrideBackground, background, + drawWrapMarkEnd, vsDraw.whitespaceForeground.allocated); + } + if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) && (ll->selStart >= 0) && (ll->selEnd >= 0)) { + int startPosSel = (ll->selStart < posLineStart) ? posLineStart : ll->selStart; + int endPosSel = (ll->selEnd < (lineEnd + posLineStart)) ? ll->selEnd : (lineEnd + posLineStart); + if (startPosSel < endPosSel) { + rcSegment.left = xStart + ll->positions[startPosSel - posLineStart] - subLineStart; + rcSegment.right = xStart + ll->positions[endPosSel - posLineStart] - subLineStart; + SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha); + } + } + + if (vsDraw.edgeState == EDGE_LINE) { + int edgeX = theEdge * vsDraw.spaceWidth; + rcSegment.left = edgeX + xStart; + rcSegment.right = rcSegment.left + 1; + surface->FillRectangle(rcSegment, vsDraw.edgecolour.allocated); + } + + // Draw any translucent whole line states + rcSegment.left = xStart; + rcSegment.right = rcLine.right - 1; + if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) { + SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground.allocated, vsDraw.caretLineAlpha); + } + int marks = pdoc->GetMark(line); + for (int markBit = 0; (markBit < 32) && marks; markBit++) { + if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) { + SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha); + } + marks >>= 1; + } + if (vsDraw.maskInLine) { + int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine; + if (marksMasked) { + for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) { + if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) { + SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha); + } + marksMasked >>= 1; + } + } + } +} + +void Editor::RefreshPixMaps(Surface *surfaceWindow) { + if (!pixmapSelPattern->Initialised()) { + const int patternSize = 8; + pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID()); + // This complex procedure is to reproduce the checkerboard dithered pattern used by windows + // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half + // way between the chrome colour and the chrome highlight colour making a nice transition + // between the window chrome and the content area. And it works in low colour depths. + PRectangle rcPattern(0, 0, patternSize, patternSize); + + // Initialize default colours based on the chrome colour scheme. Typically the highlight is white. + ColourAllocated colourFMFill = vs.selbar.allocated; + ColourAllocated colourFMStripes = vs.selbarlight.allocated; + + if (!(vs.selbarlight.desired == ColourDesired(0xff, 0xff, 0xff))) { + // User has chosen an unusual chrome colour scheme so just use the highlight edge colour. + // (Typically, the highlight colour is white.) + colourFMFill = vs.selbarlight.allocated; + } + + if (vs.foldmarginColourSet) { + // override default fold margin colour + colourFMFill = vs.foldmarginColour.allocated; + } + if (vs.foldmarginHighlightColourSet) { + // override default fold margin highlight colour + colourFMStripes = vs.foldmarginHighlightColour.allocated; + } + + pixmapSelPattern->FillRectangle(rcPattern, colourFMFill); + pixmapSelPattern->PenColour(colourFMStripes); + for (int stripe = 0; stripe < patternSize; stripe++) { + // Alternating 1 pixel stripes is same as checkerboard. + pixmapSelPattern->MoveTo(0, stripe * 2); + pixmapSelPattern->LineTo(patternSize, stripe * 2 - patternSize); + } + } + + if (!pixmapIndentGuide->Initialised()) { + // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line + pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID()); + pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID()); + PRectangle rcIG(0, 0, 1, vs.lineHeight); + pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back.allocated); + pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore.allocated); + pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back.allocated); + pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore.allocated); + for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) { + pixmapIndentGuide->MoveTo(0, stripe); + pixmapIndentGuide->LineTo(2, stripe); + pixmapIndentGuideHighlight->MoveTo(0, stripe); + pixmapIndentGuideHighlight->LineTo(2, stripe); + } + } + + if (bufferedDraw) { + if (!pixmapLine->Initialised()) { + PRectangle rcClient = GetClientRectangle(); + pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight, + surfaceWindow, wMain.GetID()); + pixmapSelMargin->InitPixMap(vs.fixedColumnWidth, + rcClient.Height(), surfaceWindow, wMain.GetID()); + } + } +} + +void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { + //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n", + // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom); + + RefreshStyleData(); + RefreshPixMaps(surfaceWindow); + + PRectangle rcClient = GetClientRectangle(); + //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n", + // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); + + surfaceWindow->SetPalette(&palette, true); + pixmapLine->SetPalette(&palette, !hasFocus); + + int screenLinePaintFirst = rcArea.top / vs.lineHeight; + // The area to be painted plus one extra line is styled. + // The extra line is to determine when a style change, such as starting a comment flows on to other lines. + int lineStyleLast = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1; + //Platform::DebugPrintf("Paint lines = %d .. %d\n", topLine + screenLinePaintFirst, lineStyleLast); + int endPosPaint = pdoc->Length(); + if (lineStyleLast < cs.LinesDisplayed()) + endPosPaint = pdoc->LineStart(cs.DocFromDisplay(lineStyleLast + 1)); + + int xStart = vs.fixedColumnWidth - xOffset; + int ypos = 0; + if (!bufferedDraw) + ypos += screenLinePaintFirst * vs.lineHeight; + int yposScreen = screenLinePaintFirst * vs.lineHeight; + + // Ensure we are styled as far as we are painting. + pdoc->EnsureStyledTo(endPosPaint); + bool paintAbandonedByStyling = paintState == paintAbandoned; + if (needUpdateUI) { + NotifyUpdateUI(); + needUpdateUI = false; + RefreshStyleData(); + RefreshPixMaps(surfaceWindow); + } + + // Call priority lines wrap on a window of lines which are likely + // to rendered with the following paint (that is wrap the visible + // lines first). + int startLineToWrap = cs.DocFromDisplay(topLine) - 5; + if (startLineToWrap < 0) + startLineToWrap = -1; + if (WrapLines(false, startLineToWrap)) { + // The wrapping process has changed the height of some lines so + // abandon this paint for a complete repaint. + if (AbandonPaint()) { + return; + } + RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change + } + PLATFORM_ASSERT(pixmapSelPattern->Initialised()); + + PaintSelMargin(surfaceWindow, rcArea); + + PRectangle rcRightMargin = rcClient; + rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth; + if (rcArea.Intersects(rcRightMargin)) { + surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated); + } + + if (paintState == paintAbandoned) { + // Either styling or NotifyUpdateUI noticed that painting is needed + // outside the current painting rectangle + //Platform::DebugPrintf("Abandoning paint\n"); + if (wrapState != eWrapNone) { + if (paintAbandonedByStyling) { + // Styling has spilled over a line end, such as occurs by starting a multiline + // comment. The width of subsequent text may have changed, so rewrap. + NeedWrapping(cs.DocFromDisplay(topLine)); + } + } + return; + } + //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset); + + // Do the painting + if (rcArea.right > vs.fixedColumnWidth) { + + Surface *surface = surfaceWindow; + if (bufferedDraw) { + surface = pixmapLine; + PLATFORM_ASSERT(pixmapLine->Initialised()); + } + surface->SetUnicodeMode(IsUnicodeMode()); + surface->SetDBCSMode(CodePage()); + + int visibleLine = topLine + screenLinePaintFirst; + + int posCaret = currentPos; + if (posDrag >= 0) + posCaret = posDrag; + int lineCaret = pdoc->LineFromPosition(posCaret); + + // Remove selection margin from drawing area so text will not be drawn + // on it in unbuffered mode. + PRectangle rcTextArea = rcClient; + rcTextArea.left = vs.fixedColumnWidth; + rcTextArea.right -= vs.rightMarginWidth; + surfaceWindow->SetClip(rcTextArea); + + // Loop on visible lines + //double durLayout = 0.0; + //double durPaint = 0.0; + //double durCopy = 0.0; + //ElapsedTime etWhole; + int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times + AutoLineLayout ll(llc, 0); + SelectionLineIterator lineIterator(this); + while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) { + + int lineDoc = cs.DocFromDisplay(visibleLine); + // Only visible lines should be handled by the code within the loop + PLATFORM_ASSERT(cs.GetVisible(lineDoc)); + int lineStartSet = cs.DisplayFromDoc(lineDoc); + int subLine = visibleLine - lineStartSet; + + // Copy this line and its styles from the document into local arrays + // and determine the x position at which each character starts. + //ElapsedTime et; + if (lineDoc != lineDocPrevious) { + ll.Set(0); + // For rectangular selection this accesses the layout cache so should be after layout returned. + lineIterator.SetAt(lineDoc); + ll.Set(RetrieveLineLayout(lineDoc)); + LayoutLine(lineDoc, surface, vs, ll, wrapWidth); + lineDocPrevious = lineDoc; + } + //durLayout += et.Duration(true); + + if (ll) { + if (selType == selStream) { + ll->selStart = SelectionStart(); + ll->selEnd = SelectionEnd(); + } else { + ll->selStart = lineIterator.startPos; + ll->selEnd = lineIterator.endPos; + } + ll->containsCaret = lineDoc == lineCaret; + if (hideSelection) { + ll->selStart = -1; + ll->selEnd = -1; + ll->containsCaret = false; + } + + GetHotSpotRange(ll->hsStart, ll->hsEnd); + + PRectangle rcLine = rcClient; + rcLine.top = ypos; + rcLine.bottom = ypos + vs.lineHeight; + + Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1)); + // Highlight the current braces if any + ll->SetBracesHighlight(rangeLine, braces, static_cast(bracesMatchStyle), + highlightGuideColumn * vs.spaceWidth); + + // Draw the line + DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine); + //durPaint += et.Duration(true); + + // Restore the previous styles for the brace highlights in case layout is in cache. + ll->RestoreBracesHighlight(rangeLine, braces); + + bool expanded = cs.GetExpanded(lineDoc); + if ((foldFlags & SC_FOLDFLAG_BOX) == 0) { + // Paint the line above the fold + if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED)) + || + (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) { + if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) { + PRectangle rcFoldLine = rcLine; + rcFoldLine.bottom = rcFoldLine.top + 1; + surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated); + } + } + // Paint the line below the fold + if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED)) + || + (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) { + if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) { + PRectangle rcFoldLine = rcLine; + rcFoldLine.top = rcFoldLine.bottom - 1; + surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated); + } + } + } else { + int FoldLevelCurr = (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELNUMBERMASK) - SC_FOLDLEVELBASE; + int FoldLevelPrev = (pdoc->GetLevel(lineDoc - 1) & SC_FOLDLEVELNUMBERMASK) - SC_FOLDLEVELBASE; + int FoldLevelFlags = (pdoc->GetLevel(lineDoc) & ~SC_FOLDLEVELNUMBERMASK) & ~(0xFFF0000); + int indentationStep = pdoc->IndentSize(); + // Draw line above fold + if ((FoldLevelPrev < FoldLevelCurr) + || + (FoldLevelFlags & SC_FOLDLEVELBOXHEADERFLAG + && + (pdoc->GetLevel(lineDoc - 1) & SC_FOLDLEVELBOXFOOTERFLAG) == 0)) { + PRectangle rcFoldLine = rcLine; + rcFoldLine.bottom = rcFoldLine.top + 1; + rcFoldLine.left += xStart + FoldLevelCurr * vs.spaceWidth * indentationStep - 1; + surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated); + } + + // Line below the fold (or below a contracted fold) + if (FoldLevelFlags & SC_FOLDLEVELBOXFOOTERFLAG + || + (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) { + PRectangle rcFoldLine = rcLine; + rcFoldLine.top = rcFoldLine.bottom - 1; + rcFoldLine.left += xStart + (FoldLevelCurr) * vs.spaceWidth * indentationStep - 1; + surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated); + } + + PRectangle rcBoxLine = rcLine; + // Draw vertical line for every fold level + for (int i = 0; i <= FoldLevelCurr; i++) { + rcBoxLine.left = xStart + i * vs.spaceWidth * indentationStep - 1; + rcBoxLine.right = rcBoxLine.left + 1; + surface->FillRectangle(rcBoxLine, vs.styles[STYLE_DEFAULT].fore.allocated); + } + } + + // Draw the Caret + if (lineDoc == lineCaret) { + int offset = Platform::Minimum(posCaret - rangeLine.start, ll->maxLineLength); + if ((offset >= ll->LineStart(subLine)) && + ((offset < ll->LineStart(subLine + 1)) || offset == ll->numCharsInLine)) { + int xposCaret = ll->positions[offset] - ll->positions[ll->LineStart(subLine)] + xStart; + + if (actualWrapVisualStartIndent != 0) { + int lineStart = ll->LineStart(subLine); + if (lineStart != 0) // Wrapped + xposCaret += actualWrapVisualStartIndent * vs.aveCharWidth; + } + int widthOverstrikeCaret; + if (posCaret == pdoc->Length()) { // At end of document + widthOverstrikeCaret = vs.aveCharWidth; + } else if ((posCaret - rangeLine.start) >= ll->numCharsInLine) { // At end of line + widthOverstrikeCaret = vs.aveCharWidth; + } else { + widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset]; + } + if (widthOverstrikeCaret < 3) // Make sure its visible + widthOverstrikeCaret = 3; + if (((caret.active && caret.on) || (posDrag >= 0)) && xposCaret >= 0) { + PRectangle rcCaret = rcLine; + int caretWidthOffset = 0; + if ((offset > 0) && (vs.caretWidth > 1)) + caretWidthOffset = 1; // Move back so overlaps both character cells. + if (posDrag >= 0) { + rcCaret.left = xposCaret - caretWidthOffset; + rcCaret.right = rcCaret.left + vs.caretWidth; + } else { + if (inOverstrike) { + rcCaret.top = rcCaret.bottom - 2; + rcCaret.left = xposCaret + 1; + rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1; + } else { + rcCaret.left = xposCaret - caretWidthOffset; + rcCaret.right = rcCaret.left + vs.caretWidth; + } + } + surface->FillRectangle(rcCaret, vs.caretcolour.allocated); + } + } + } + + if (bufferedDraw) { + Point from(vs.fixedColumnWidth, 0); + PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen, + rcClient.right, yposScreen + vs.lineHeight); + surfaceWindow->Copy(rcCopyArea, from, *pixmapLine); + } + //durCopy += et.Duration(true); + } + + if (!bufferedDraw) { + ypos += vs.lineHeight; + } + + yposScreen += vs.lineHeight; + visibleLine++; + //gdk_flush(); + } + ll.Set(0); + //if (durPaint < 0.00000001) + // durPaint = 0.00000001; + + // Right column limit indicator + PRectangle rcBeyondEOF = rcClient; + rcBeyondEOF.left = vs.fixedColumnWidth; + rcBeyondEOF.right = rcBeyondEOF.right; + rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight; + if (rcBeyondEOF.top < rcBeyondEOF.bottom) { + surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated); + if (vs.edgeState == EDGE_LINE) { + int edgeX = theEdge * vs.spaceWidth; + rcBeyondEOF.left = edgeX + xStart; + rcBeyondEOF.right = rcBeyondEOF.left + 1; + surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated); + } + } + //Platform::DebugPrintf( + //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n", + //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration()); + NotifyPainted(); + } +} + +// Space (3 space characters) between line numbers and text when printing. +#define lineNumberPrintSpace " " + +ColourDesired InvertedLight(ColourDesired orig) { + unsigned int r = orig.GetRed(); + unsigned int g = orig.GetGreen(); + unsigned int b = orig.GetBlue(); + unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye + unsigned int il = 0xff - l; + if (l == 0) + return ColourDesired(0xff, 0xff, 0xff); + r = r * il / l; + g = g * il / l; + b = b * il / l; + return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff)); +} + +// This is mostly copied from the Paint method but with some things omitted +// such as the margin markers, line numbers, selection and caret +// Should be merged back into a combined Draw method. +long Editor::FormatRange(bool draw, RangeToFormat *pfr) { + if (!pfr) + return 0; + + AutoSurface surface(pfr->hdc, this); + if (!surface) + return 0; + AutoSurface surfaceMeasure(pfr->hdcTarget, this); + if (!surfaceMeasure) { + return 0; + } + + ViewStyle vsPrint(vs); + + // Modify the view style for printing as do not normally want any of the transient features to be printed + // Printing supports only the line number margin. + int lineNumberIndex = -1; + for (int margin = 0; margin < ViewStyle::margins; margin++) { + if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) { + lineNumberIndex = margin; + } else { + vsPrint.ms[margin].width = 0; + } + } + vsPrint.showMarkedLines = false; + vsPrint.fixedColumnWidth = 0; + vsPrint.zoomLevel = printMagnification; + vsPrint.viewIndentationGuides = false; + // Don't show the selection when printing + vsPrint.selbackset = false; + vsPrint.selforeset = false; + vsPrint.selAlpha = SC_ALPHA_NOALPHA; + vsPrint.whitespaceBackgroundSet = false; + vsPrint.whitespaceForegroundSet = false; + vsPrint.showCaretLineBackground = false; + + // Set colours for printing according to users settings + for (int sty = 0;sty <= STYLE_MAX;sty++) { + if (printColourMode == SC_PRINT_INVERTLIGHT) { + vsPrint.styles[sty].fore.desired = InvertedLight(vsPrint.styles[sty].fore.desired); + vsPrint.styles[sty].back.desired = InvertedLight(vsPrint.styles[sty].back.desired); + } else if (printColourMode == SC_PRINT_BLACKONWHITE) { + vsPrint.styles[sty].fore.desired = ColourDesired(0, 0, 0); + vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff); + } else if (printColourMode == SC_PRINT_COLOURONWHITE) { + vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff); + } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) { + if (sty <= STYLE_DEFAULT) { + vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff); + } + } + } + // White background for the line numbers + vsPrint.styles[STYLE_LINENUMBER].back.desired = ColourDesired(0xff, 0xff, 0xff); + + vsPrint.Refresh(*surfaceMeasure); + // Ensure colours are set up + vsPrint.RefreshColourPalette(palette, true); + vsPrint.RefreshColourPalette(palette, false); + // Determining width must hapen after fonts have been realised in Refresh + int lineNumberWidth = 0; + if (lineNumberIndex >= 0) { + lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font, + "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace)); + vsPrint.ms[lineNumberIndex].width = lineNumberWidth; + } + + int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin); + int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1; + if (linePrintLast < linePrintStart) + linePrintLast = linePrintStart; + int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax); + if (linePrintLast > linePrintMax) + linePrintLast = linePrintMax; + //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n", + // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight, + // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font)); + int endPosPrint = pdoc->Length(); + if (linePrintLast < pdoc->LinesTotal()) + endPosPrint = pdoc->LineStart(linePrintLast + 1); + + // Ensure we are styled to where we are formatting. + pdoc->EnsureStyledTo(endPosPrint); + + int xStart = vsPrint.fixedColumnWidth + pfr->rc.left + lineNumberWidth; + int ypos = pfr->rc.top; + + int lineDoc = linePrintStart; + + int nPrintPos = pfr->chrg.cpMin; + int visibleLine = 0; + int widthPrint = pfr->rc.Width() - lineNumberWidth; + if (printWrapState == eWrapNone) + widthPrint = LineLayout::wrapWidthInfinite; + + while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) { + + // When printing, the hdc and hdcTarget may be the same, so + // changing the state of surfaceMeasure may change the underlying + // state of surface. Therefore, any cached state is discarded before + // using each surface. + surfaceMeasure->FlushCachedState(); + + // Copy this line and its styles from the document into local arrays + // and determine the x position at which each character starts. + LineLayout ll(8000); + LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint); + + ll.selStart = -1; + ll.selEnd = -1; + ll.containsCaret = false; + + PRectangle rcLine; + rcLine.left = pfr->rc.left + lineNumberWidth; + rcLine.top = ypos; + rcLine.right = pfr->rc.right - 1; + rcLine.bottom = ypos + vsPrint.lineHeight; + + // When document line is wrapped over multiple display lines, find where + // to start printing from to ensure a particular position is on the first + // line of the page. + if (visibleLine == 0) { + int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc); + for (int iwl = 0; iwl < ll.lines - 1; iwl++) { + if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) { + visibleLine = -iwl; + } + } + + if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) { + visibleLine = -(ll.lines - 1); + } + } + + if (draw && lineNumberWidth && + (ypos + vsPrint.lineHeight <= pfr->rc.bottom) && + (visibleLine >= 0)) { + char number[100]; + sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1); + PRectangle rcNumber = rcLine; + rcNumber.right = rcNumber.left + lineNumberWidth; + // Right justify + rcNumber.left -= surfaceMeasure->WidthText( + vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number)); + surface->FlushCachedState(); + surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font, + ypos + vsPrint.maxAscent, number, istrlen(number), + vsPrint.styles[STYLE_LINENUMBER].fore.allocated, + vsPrint.styles[STYLE_LINENUMBER].back.allocated); + } + + // Draw the line + surface->FlushCachedState(); + + for (int iwl = 0; iwl < ll.lines; iwl++) { + if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) { + if (visibleLine >= 0) { + if (draw) { + rcLine.top = ypos; + rcLine.bottom = ypos + vsPrint.lineHeight; + DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl); + } + ypos += vsPrint.lineHeight; + } + visibleLine++; + if (iwl == ll.lines - 1) + nPrintPos = pdoc->LineStart(lineDoc + 1); + else + nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl); + } + } + + ++lineDoc; + } + + return nPrintPos; +} + +int Editor::TextWidth(int style, const char *text) { + RefreshStyleData(); + AutoSurface surface(this); + if (surface) { + return surface->WidthText(vs.styles[style].font, text, istrlen(text)); + } else { + return 1; + } +} + +// Empty method is overridden on GTK+ to show / hide scrollbars +void Editor::ReconfigureScrollBars() {} + +void Editor::SetScrollBars() { + RefreshStyleData(); + + int nMax = MaxScrollPos(); + int nPage = LinesOnScreen(); + bool modified = ModifyScrollBars(nMax + nPage - 1, nPage); + if (modified) { + DwellEnd(true); + } + + // TODO: ensure always showing as many lines as possible + // May not be, if, for example, window made larger + if (topLine > MaxScrollPos()) { + SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos())); + SetVerticalScrollPos(); + Redraw(); + } + if (modified) { + if (!AbandonPaint()) + Redraw(); + } + //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage); +} + +void Editor::ChangeSize() { + DropGraphics(); + SetScrollBars(); + if (wrapState != eWrapNone) { + PRectangle rcTextArea = GetClientRectangle(); + rcTextArea.left = vs.fixedColumnWidth; + rcTextArea.right -= vs.rightMarginWidth; + if (wrapWidth != rcTextArea.Width()) { + NeedWrapping(); + Redraw(); + } + } +} + +void Editor::AddChar(char ch) { + char s[2]; + s[0] = ch; + s[1] = '\0'; + AddCharUTF(s, 1); +} + +void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) { + bool wasSelection = currentPos != anchor; + ClearSelection(); + bool charReplaceAction = false; + if (inOverstrike && !wasSelection && !RangeContainsProtected(currentPos, currentPos + 1)) { + if (currentPos < (pdoc->Length())) { + if (!IsEOLChar(pdoc->CharAt(currentPos))) { + charReplaceAction = true; + pdoc->BeginUndoAction(); + pdoc->DelChar(currentPos); + } + } + } + if (pdoc->InsertString(currentPos, s, len)) { + SetEmptySelection(currentPos + len); + } + if (charReplaceAction) { + pdoc->EndUndoAction(); + } + EnsureCaretVisible(); + // Avoid blinking during rapid typing: + ShowCaretAtCurrentPosition(); + if (!caretSticky) { + SetLastXChosen(); + } + + if (treatAsDBCS) { + NotifyChar((static_cast(s[0]) << 8) | + static_cast(s[1])); + } else { + int byte = static_cast(s[0]); + if ((byte < 0xC0) || (1 == len)) { + // Handles UTF-8 characters between 0x01 and 0x7F and single byte + // characters when not in UTF-8 mode. + // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid + // characters representing themselves. + } else { + // Unroll 1 to 3 byte UTF-8 sequences. See reference data at: + // http://www.cl.cam.ac.uk/~mgk25/unicode.html + // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt + if (byte < 0xE0) { + int byte2 = static_cast(s[1]); + if ((byte2 & 0xC0) == 0x80) { + // Two-byte-character lead-byte followed by a trail-byte. + byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F)); + } + // A two-byte-character lead-byte not followed by trail-byte + // represents itself. + } else if (byte < 0xF0) { + int byte2 = static_cast(s[1]); + int byte3 = static_cast(s[2]); + if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) { + // Three-byte-character lead byte followed by two trail bytes. + byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | + (byte3 & 0x3F)); + } + // A three-byte-character lead-byte not followed by two trail-bytes + // represents itself. + } + } + NotifyChar(byte); + } +} + +void Editor::ClearSelection() { + if (!SelectionContainsProtected()) { + int startPos = SelectionStart(); + if (selType == selStream) { + unsigned int chars = SelectionEnd() - startPos; + if (0 != chars) { + pdoc->BeginUndoAction(); + pdoc->DeleteChars(startPos, chars); + pdoc->EndUndoAction(); + } + } else { + pdoc->BeginUndoAction(); + SelectionLineIterator lineIterator(this, false); + while (lineIterator.Iterate()) { + startPos = lineIterator.startPos; + unsigned int chars = lineIterator.endPos - startPos; + if (0 != chars) { + pdoc->DeleteChars(startPos, chars); + } + } + pdoc->EndUndoAction(); + selType = selStream; + } + SetEmptySelection(startPos); + } +} + +void Editor::ClearAll() { + pdoc->BeginUndoAction(); + if (0 != pdoc->Length()) { + pdoc->DeleteChars(0, pdoc->Length()); + } + if (!pdoc->IsReadOnly()) { + cs.Clear(); + } + pdoc->EndUndoAction(); + anchor = 0; + currentPos = 0; + SetTopLine(0); + SetVerticalScrollPos(); + InvalidateStyleRedraw(); +} + +void Editor::ClearDocumentStyle() { + pdoc->StartStyling(0, '\377'); + pdoc->SetStyleFor(pdoc->Length(), 0); + cs.ShowAll(); + pdoc->ClearLevels(); +} + +void Editor::Cut() { + if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) { + Copy(); + ClearSelection(); + } +} + +void Editor::PasteRectangular(int pos, const char *ptr, int len) { + if (pdoc->IsReadOnly() || SelectionContainsProtected()) { + return; + } + currentPos = pos; + int xInsert = XFromPosition(currentPos); + int line = pdoc->LineFromPosition(currentPos); + bool prevCr = false; + pdoc->BeginUndoAction(); + for (int i = 0; i < len; i++) { + if (IsEOLChar(ptr[i])) { + if ((ptr[i] == '\r') || (!prevCr)) + line++; + if (line >= pdoc->LinesTotal()) { + if (pdoc->eolMode != SC_EOL_LF) + pdoc->InsertChar(pdoc->Length(), '\r'); + if (pdoc->eolMode != SC_EOL_CR) + pdoc->InsertChar(pdoc->Length(), '\n'); + } + // Pad the end of lines with spaces if required + currentPos = PositionFromLineX(line, xInsert); + if ((XFromPosition(currentPos) < xInsert) && (i + 1 < len)) { + for (int i = 0; i < xInsert - XFromPosition(currentPos); i++) { + pdoc->InsertChar(currentPos, ' '); + currentPos++; + } + } + prevCr = ptr[i] == '\r'; + } else { + pdoc->InsertString(currentPos, ptr + i, 1); + currentPos++; + prevCr = false; + } + } + pdoc->EndUndoAction(); + SetEmptySelection(pos); +} + +bool Editor::CanPaste() { + return !pdoc->IsReadOnly() && !SelectionContainsProtected(); +} + +void Editor::Clear() { + if (currentPos == anchor) { + if (!RangeContainsProtected(currentPos, currentPos + 1)) { + DelChar(); + } + } else { + ClearSelection(); + } + SetEmptySelection(currentPos); +} + +void Editor::SelectAll() { + SetSelection(0, pdoc->Length()); + Redraw(); +} + +void Editor::Undo() { + if (pdoc->CanUndo()) { + InvalidateCaret(); + int newPos = pdoc->Undo(); + if (newPos >= 0) + SetEmptySelection(newPos); + EnsureCaretVisible(); + } +} + +void Editor::Redo() { + if (pdoc->CanRedo()) { + int newPos = pdoc->Redo(); + if (newPos >= 0) + SetEmptySelection(newPos); + EnsureCaretVisible(); + } +} + +void Editor::DelChar() { + if (!RangeContainsProtected(currentPos, currentPos + 1)) { + pdoc->DelChar(currentPos); + } + // Avoid blinking during rapid typing: + ShowCaretAtCurrentPosition(); +} + +void Editor::DelCharBack(bool allowLineStartDeletion) { + if (currentPos == anchor) { + if (!RangeContainsProtected(currentPos - 1, currentPos)) { + int lineCurrentPos = pdoc->LineFromPosition(currentPos); + if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != currentPos)) { + if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) && + pdoc->GetColumn(currentPos) > 0 && pdoc->backspaceUnindents) { + pdoc->BeginUndoAction(); + int indentation = pdoc->GetLineIndentation(lineCurrentPos); + int indentationStep = pdoc->IndentSize(); + if (indentation % indentationStep == 0) { + pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep); + } else { + pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep)); + } + SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos)); + pdoc->EndUndoAction(); + } else { + pdoc->DelCharBack(currentPos); + } + } + } + } else { + ClearSelection(); + SetEmptySelection(currentPos); + } + // Avoid blinking during rapid typing: + ShowCaretAtCurrentPosition(); +} + +void Editor::NotifyFocus(bool) {} + +void Editor::NotifyStyleToNeeded(int endStyleNeeded) { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_STYLENEEDED; + scn.position = endStyleNeeded; + NotifyParent(scn); +} + +void Editor::NotifyStyleNeeded(Document*, void *, int endStyleNeeded) { + NotifyStyleToNeeded(endStyleNeeded); +} + +void Editor::NotifyChar(int ch) { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_CHARADDED; + scn.ch = ch; + NotifyParent(scn); + if (recordingMacro) { + char txt[2]; + txt[0] = static_cast(ch); + txt[1] = '\0'; + NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast(txt)); + } +} + +void Editor::NotifySavePoint(bool isSavePoint) { + SCNotification scn = {0}; + if (isSavePoint) { + scn.nmhdr.code = SCN_SAVEPOINTREACHED; + } else { + scn.nmhdr.code = SCN_SAVEPOINTLEFT; + } + NotifyParent(scn); +} + +void Editor::NotifyModifyAttempt() { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_MODIFYATTEMPTRO; + NotifyParent(scn); +} + +void Editor::NotifyDoubleClick(Point pt, bool) { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_DOUBLECLICK; + scn.line = LineFromLocation(pt); + scn.position = PositionFromLocationClose(pt); + NotifyParent(scn); +} + +void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK; + scn.position = position; + scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | + (alt ? SCI_ALT : 0); + NotifyParent(scn); +} + +void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_HOTSPOTCLICK; + scn.position = position; + scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | + (alt ? SCI_ALT : 0); + NotifyParent(scn); +} + +void Editor::NotifyUpdateUI() { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_UPDATEUI; + NotifyParent(scn); +} + +void Editor::NotifyPainted() { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_PAINTED; + NotifyParent(scn); +} + +bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) { + int marginClicked = -1; + int x = 0; + for (int margin = 0; margin < ViewStyle::margins; margin++) { + if ((pt.x > x) && (pt.x < x + vs.ms[margin].width)) + marginClicked = margin; + x += vs.ms[margin].width; + } + if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_MARGINCLICK; + scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | + (alt ? SCI_ALT : 0); + scn.position = pdoc->LineStart(LineFromLocation(pt)); + scn.margin = marginClicked; + NotifyParent(scn); + return true; + } else { + return false; + } +} + +void Editor::NotifyNeedShown(int pos, int len) { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_NEEDSHOWN; + scn.position = pos; + scn.length = len; + NotifyParent(scn); +} + +void Editor::NotifyDwelling(Point pt, bool state) { + SCNotification scn = {0}; + scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND; + scn.position = PositionFromLocationClose(pt); + scn.x = pt.x; + scn.y = pt.y; + NotifyParent(scn); +} + +void Editor::NotifyZoom() { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_ZOOM; + NotifyParent(scn); +} + +// Notifications from document +void Editor::NotifyModifyAttempt(Document*, void *) { + //Platform::DebugPrintf("** Modify Attempt\n"); + NotifyModifyAttempt(); +} + +void Editor::NotifyMove(int position) { + SCNotification scn = {0}; + scn.nmhdr.code = SCN_POSCHANGED; + scn.position = position; + NotifyParent(scn); +} + +void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) { + //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off"); + NotifySavePoint(atSavePoint); +} + +void Editor::CheckModificationForWrap(DocModification mh) { + if (mh.modificationType & (SC_MOD_INSERTTEXT|SC_MOD_DELETETEXT)) { + llc.Invalidate(LineLayout::llCheckTextAndStyle); + if (wrapState != eWrapNone) { + int lineDoc = pdoc->LineFromPosition(mh.position); + int lines = Platform::Maximum(0, mh.linesAdded); + NeedWrapping(lineDoc, lineDoc + lines + 1); + } + } +} + +// Move a position so it is still after the same character as before the insertion. +static inline int MovePositionForInsertion(int position, int startInsertion, int length) { + if (position > startInsertion) { + return position + length; + } + return position; +} + +// Move a position so it is still after the same character as before the deletion if that +// character is still present else after the previous surviving character. +static inline int MovePositionForDeletion(int position, int startDeletion, int length) { + if (position > startDeletion) { + int endDeletion = startDeletion + length; + if (position > endDeletion) { + return position - length; + } else { + return startDeletion; + } + } else { + return position; + } +} + +void Editor::NotifyModified(Document*, DocModification mh, void *) { + needUpdateUI = true; + if (paintState == painting) { + CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length)); + } + if (mh.modificationType & SC_MOD_CHANGESTYLE) { + pdoc->IncrementStyleClock(); + if (paintState == notPainting) { + if (mh.position < pdoc->LineStart(topLine)) { + // Styling performed before this view + Redraw(); + } else { + InvalidateRange(mh.position, mh.position + mh.length); + } + } + llc.Invalidate(LineLayout::llCheckTextAndStyle); + } else { + // Move selection and brace highlights + if (mh.modificationType & SC_MOD_INSERTTEXT) { + currentPos = MovePositionForInsertion(currentPos, mh.position, mh.length); + anchor = MovePositionForInsertion(anchor, mh.position, mh.length); + braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length); + braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length); + } else if (mh.modificationType & SC_MOD_DELETETEXT) { + currentPos = MovePositionForDeletion(currentPos, mh.position, mh.length); + anchor = MovePositionForDeletion(anchor, mh.position, mh.length); + braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length); + braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length); + } + if (cs.LinesDisplayed() < cs.LinesInDoc()) { + // Some lines are hidden so may need shown. + // TODO: check if the modified area is hidden. + if (mh.modificationType & SC_MOD_BEFOREINSERT) { + NotifyNeedShown(mh.position, 0); + } else if (mh.modificationType & SC_MOD_BEFOREDELETE) { + NotifyNeedShown(mh.position, mh.length); + } + } + if (mh.linesAdded != 0) { + // Update contraction state for inserted and removed lines + // lineOfPos should be calculated in context of state before modification, shouldn't it + int lineOfPos = pdoc->LineFromPosition(mh.position); + if (mh.linesAdded > 0) { + cs.InsertLines(lineOfPos, mh.linesAdded); + } else { + cs.DeleteLines(lineOfPos, -mh.linesAdded); + } + } + CheckModificationForWrap(mh); + if (mh.linesAdded != 0) { + // Avoid scrolling of display if change before current display + if (mh.position < posTopLine && !CanDeferToLastStep(mh)) { + int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos()); + if (newTop != topLine) { + SetTopLine(newTop); + SetVerticalScrollPos(); + } + } + + //Platform::DebugPrintf("** %x Doc Changed\n", this); + // TODO: could invalidate from mh.startModification to end of screen + //InvalidateRange(mh.position, mh.position + mh.length); + if (paintState == notPainting && !CanDeferToLastStep(mh)) { + Redraw(); + } + } else { + //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this, + // mh.position, mh.position + mh.length); + if (paintState == notPainting && mh.length && !CanEliminate(mh)) { + InvalidateRange(mh.position, mh.position + mh.length); + } + } + } + + if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) { + SetScrollBars(); + } + + if (mh.modificationType & SC_MOD_CHANGEMARKER) { + if ((paintState == notPainting) || !PaintContainsMargin()) { + if (mh.modificationType & SC_MOD_CHANGEFOLD) { + // Fold changes can affect the drawing of following lines so redraw whole margin + RedrawSelMargin(); + } else { + RedrawSelMargin(mh.line); + } + } + } + + // NOW pay the piper WRT "deferred" visual updates + if (IsLastStep(mh)) { + SetScrollBars(); + Redraw(); + } + + // If client wants to see this modification + if (mh.modificationType & modEventMask) { + if ((mh.modificationType & SC_MOD_CHANGESTYLE) == 0) { + // Real modification made to text of document. + NotifyChange(); // Send EN_CHANGE + } + + SCNotification scn = {0}; + scn.nmhdr.code = SCN_MODIFIED; + scn.position = mh.position; + scn.modificationType = mh.modificationType; + scn.text = mh.text; + scn.length = mh.length; + scn.linesAdded = mh.linesAdded; + scn.line = mh.line; + scn.foldLevelNow = mh.foldLevelNow; + scn.foldLevelPrev = mh.foldLevelPrev; + NotifyParent(scn); + } +} + +void Editor::NotifyDeleted(Document *, void *) { + /* Do nothing */ +} + +void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { + + // Enumerates all macroable messages + switch (iMessage) { + case SCI_CUT: + case SCI_COPY: + case SCI_PASTE: + case SCI_CLEAR: + case SCI_REPLACESEL: + case SCI_ADDTEXT: + case SCI_INSERTTEXT: + case SCI_APPENDTEXT: + case SCI_CLEARALL: + case SCI_SELECTALL: + case SCI_GOTOLINE: + case SCI_GOTOPOS: + case SCI_SEARCHANCHOR: + case SCI_SEARCHNEXT: + case SCI_SEARCHPREV: + case SCI_LINEDOWN: + case SCI_LINEDOWNEXTEND: + case SCI_PARADOWN: + case SCI_PARADOWNEXTEND: + case SCI_LINEUP: + case SCI_LINEUPEXTEND: + case SCI_PARAUP: + case SCI_PARAUPEXTEND: + case SCI_CHARLEFT: + case SCI_CHARLEFTEXTEND: + case SCI_CHARRIGHT: + case SCI_CHARRIGHTEXTEND: + case SCI_WORDLEFT: + case SCI_WORDLEFTEXTEND: + case SCI_WORDRIGHT: + case SCI_WORDRIGHTEXTEND: + case SCI_WORDPARTLEFT: + case SCI_WORDPARTLEFTEXTEND: + case SCI_WORDPARTRIGHT: + case SCI_WORDPARTRIGHTEXTEND: + case SCI_WORDLEFTEND: + case SCI_WORDLEFTENDEXTEND: + case SCI_WORDRIGHTEND: + case SCI_WORDRIGHTENDEXTEND: + case SCI_HOME: + case SCI_HOMEEXTEND: + case SCI_LINEEND: + case SCI_LINEENDEXTEND: + case SCI_HOMEWRAP: + case SCI_HOMEWRAPEXTEND: + case SCI_LINEENDWRAP: + case SCI_LINEENDWRAPEXTEND: + case SCI_DOCUMENTSTART: + case SCI_DOCUMENTSTARTEXTEND: + case SCI_DOCUMENTEND: + case SCI_DOCUMENTENDEXTEND: + case SCI_STUTTEREDPAGEUP: + case SCI_STUTTEREDPAGEUPEXTEND: + case SCI_STUTTEREDPAGEDOWN: + case SCI_STUTTEREDPAGEDOWNEXTEND: + case SCI_PAGEUP: + case SCI_PAGEUPEXTEND: + case SCI_PAGEDOWN: + case SCI_PAGEDOWNEXTEND: + case SCI_EDITTOGGLEOVERTYPE: + case SCI_CANCEL: + case SCI_DELETEBACK: + case SCI_TAB: + case SCI_BACKTAB: + case SCI_FORMFEED: + case SCI_VCHOME: + case SCI_VCHOMEEXTEND: + case SCI_VCHOMEWRAP: + case SCI_VCHOMEWRAPEXTEND: + case SCI_DELWORDLEFT: + case SCI_DELWORDRIGHT: + case SCI_DELLINELEFT: + case SCI_DELLINERIGHT: + case SCI_LINECOPY: + case SCI_LINECUT: + case SCI_LINEDELETE: + case SCI_LINETRANSPOSE: + case SCI_LINEDUPLICATE: + case SCI_LOWERCASE: + case SCI_UPPERCASE: + case SCI_LINESCROLLDOWN: + case SCI_LINESCROLLUP: + case SCI_DELETEBACKNOTLINE: + case SCI_HOMEDISPLAY: + case SCI_HOMEDISPLAYEXTEND: + case SCI_LINEENDDISPLAY: + case SCI_LINEENDDISPLAYEXTEND: + case SCI_SETSELECTIONMODE: + case SCI_LINEDOWNRECTEXTEND: + case SCI_LINEUPRECTEXTEND: + case SCI_CHARLEFTRECTEXTEND: + case SCI_CHARRIGHTRECTEXTEND: + case SCI_HOMERECTEXTEND: + case SCI_VCHOMERECTEXTEND: + case SCI_LINEENDRECTEXTEND: + case SCI_PAGEUPRECTEXTEND: + case SCI_PAGEDOWNRECTEXTEND: + case SCI_SELECTIONDUPLICATE: + break; + + // Filter out all others like display changes. Also, newlines are redundant + // with char insert messages. + case SCI_NEWLINE: + default: + // printf("Filtered out %ld of macro recording\n", iMessage); + return ; + } + + // Send notification + SCNotification scn = {0}; + scn.nmhdr.code = SCN_MACRORECORD; + scn.message = iMessage; + scn.wParam = wParam; + scn.lParam = lParam; + NotifyParent(scn); +} + +/** + * Force scroll and keep position relative to top of window. + * + * If stuttered = true and not already at first/last row, move to first/last row of window. + * If stuttered = true and already at first/last row, scroll as normal. + */ +void Editor::PageMove(int direction, selTypes sel, bool stuttered) { + int topLineNew, newPos; + + // I consider only the caretYSlop, and ignore the caretYPolicy-- is that a problem? + int currentLine = pdoc->LineFromPosition(currentPos); + int topStutterLine = topLine + caretYSlop; + int bottomStutterLine = topLine + LinesToScroll() - caretYSlop; + + if (stuttered && (direction < 0 && currentLine > topStutterLine)) { + topLineNew = topLine; + newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * caretYSlop)); + + } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) { + topLineNew = topLine; + newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * (LinesToScroll() - caretYSlop))); + + } else { + Point pt = LocationFromPosition(currentPos); + + topLineNew = Platform::Clamp( + topLine + direction * LinesToScroll(), 0, MaxScrollPos()); + newPos = PositionFromLocation( + Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll()))); + } + + if (topLineNew != topLine) { + SetTopLine(topLineNew); + MovePositionTo(newPos, sel); + Redraw(); + SetVerticalScrollPos(); + } else { + MovePositionTo(newPos, sel); + } +} + +void Editor::ChangeCaseOfSelection(bool makeUpperCase) { + pdoc->BeginUndoAction(); + int startCurrent = currentPos; + int startAnchor = anchor; + if (selType == selStream) { + pdoc->ChangeCase(Range(SelectionStart(), SelectionEnd()), + makeUpperCase); + SetSelection(startCurrent, startAnchor); + } else { + SelectionLineIterator lineIterator(this, false); + while (lineIterator.Iterate()) { + pdoc->ChangeCase( + Range(lineIterator.startPos, lineIterator.endPos), + makeUpperCase); + } + // Would be nicer to keep the rectangular selection but this is complex + SetEmptySelection(startCurrent); + } + pdoc->EndUndoAction(); +} + +void Editor::LineTranspose() { + int line = pdoc->LineFromPosition(currentPos); + if (line > 0) { + int startPrev = pdoc->LineStart(line - 1); + int endPrev = pdoc->LineEnd(line - 1); + int start = pdoc->LineStart(line); + int end = pdoc->LineEnd(line); + int startNext = pdoc->LineStart(line + 1); + if (end < pdoc->Length()) { + end = startNext; + char *thisLine = CopyRange(start, end); + pdoc->DeleteChars(start, end - start); + if (pdoc->InsertString(startPrev, thisLine, end - start)) { + MovePositionTo(startPrev + end - start); + } + delete []thisLine; + } else { + // Last line so line has no line end + char *thisLine = CopyRange(start, end); + char *prevEnd = CopyRange(endPrev, start); + pdoc->DeleteChars(endPrev, end - endPrev); + pdoc->InsertString(startPrev, thisLine, end - start); + if (pdoc->InsertString(startPrev + end - start, prevEnd, start - endPrev)) { + MovePositionTo(startPrev + end - endPrev); + } + delete []thisLine; + delete []prevEnd; + } + + } +} + +void Editor::Duplicate(bool forLine) { + int start = SelectionStart(); + int end = SelectionEnd(); + if (start == end) { + forLine = true; + } + if (forLine) { + int line = pdoc->LineFromPosition(currentPos); + start = pdoc->LineStart(line); + end = pdoc->LineEnd(line); + } + char *text = CopyRange(start, end); + if (forLine) { + const char *eol = StringFromEOLMode(pdoc->eolMode); + pdoc->InsertString(end, eol); + pdoc->InsertString(end + istrlen(eol), text, end - start); + } else { + pdoc->InsertString(end, text, end - start); + } + delete []text; +} + +void Editor::CancelModes() { + moveExtendsSelection = false; +} + +void Editor::NewLine() { + ClearSelection(); + const char *eol = "\n"; + if (pdoc->eolMode == SC_EOL_CRLF) { + eol = "\r\n"; + } else if (pdoc->eolMode == SC_EOL_CR) { + eol = "\r"; + } // else SC_EOL_LF -> "\n" already set + if (pdoc->InsertString(currentPos, eol)) { + SetEmptySelection(currentPos + istrlen(eol)); + while (*eol) { + NotifyChar(*eol); + eol++; + } + } + SetLastXChosen(); + EnsureCaretVisible(); + // Avoid blinking during rapid typing: + ShowCaretAtCurrentPosition(); +} + +void Editor::CursorUpOrDown(int direction, selTypes sel) { + Point pt = LocationFromPosition(currentPos); + int posNew = PositionFromLocation( + Point(lastXChosen, pt.y + direction * vs.lineHeight)); + if (direction < 0) { + // Line wrapping may lead to a location on the same line, so + // seek back if that is the case. + // There is an equivalent case when moving down which skips + // over a line but as that does not trap the user it is fine. + Point ptNew = LocationFromPosition(posNew); + while ((posNew > 0) && (pt.y == ptNew.y)) { + posNew--; + ptNew = LocationFromPosition(posNew); + } + } + MovePositionTo(posNew, sel); +} + +void Editor::ParaUpOrDown(int direction, selTypes sel) { + int lineDoc, savedPos = currentPos; + do { + MovePositionTo(direction > 0 ? pdoc->ParaDown(currentPos) : pdoc->ParaUp(currentPos), sel); + lineDoc = pdoc->LineFromPosition(currentPos); + if (direction > 0) { + if (currentPos >= pdoc->Length() && !cs.GetVisible(lineDoc)) { + if (sel == noSel) { + MovePositionTo(pdoc->LineEndPosition(savedPos)); + } + break; + } + } + } while (!cs.GetVisible(lineDoc)); +} + +int Editor::StartEndDisplayLine(int pos, bool start) { + RefreshStyleData(); + int line = pdoc->LineFromPosition(pos); + AutoSurface surface(this); + AutoLineLayout ll(llc, RetrieveLineLayout(line)); + int posRet = INVALID_POSITION; + if (surface && ll) { + unsigned int posLineStart = pdoc->LineStart(line); + LayoutLine(line, surface, vs, ll, wrapWidth); + int posInLine = pos - posLineStart; + if (posInLine <= ll->maxLineLength) { + for (int subLine = 0; subLine < ll->lines; subLine++) { + if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) { + if (start) { + posRet = ll->LineStart(subLine) + posLineStart; + } else { + if (subLine == ll->lines - 1) + posRet = ll->LineStart(subLine + 1) + posLineStart; + else + posRet = ll->LineStart(subLine + 1) + posLineStart - 1; + } + } + } + } + } + if (posRet == INVALID_POSITION) { + return pos; + } else { + return posRet; + } +} + +int Editor::KeyCommand(unsigned int iMessage) { + switch (iMessage) { + case SCI_LINEDOWN: + CursorUpOrDown(1); + break; + case SCI_LINEDOWNEXTEND: + CursorUpOrDown(1, selStream); + break; + case SCI_LINEDOWNRECTEXTEND: + CursorUpOrDown(1, selRectangle); + break; + case SCI_PARADOWN: + ParaUpOrDown(1); + break; + case SCI_PARADOWNEXTEND: + ParaUpOrDown(1, selStream); + break; + case SCI_LINESCROLLDOWN: + ScrollTo(topLine + 1); + MoveCaretInsideView(false); + break; + case SCI_LINEUP: + CursorUpOrDown(-1); + break; + case SCI_LINEUPEXTEND: + CursorUpOrDown(-1, selStream); + break; + case SCI_LINEUPRECTEXTEND: + CursorUpOrDown(-1, selRectangle); + break; + case SCI_PARAUP: + ParaUpOrDown(-1); + break; + case SCI_PARAUPEXTEND: + ParaUpOrDown(-1, selStream); + break; + case SCI_LINESCROLLUP: + ScrollTo(topLine - 1); + MoveCaretInsideView(false); + break; + case SCI_CHARLEFT: + if (SelectionEmpty() || moveExtendsSelection) { + MovePositionTo(MovePositionSoVisible(currentPos - 1, -1)); + } else { + MovePositionTo(SelectionStart()); + } + SetLastXChosen(); + break; + case SCI_CHARLEFTEXTEND: + MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selStream); + SetLastXChosen(); + break; + case SCI_CHARLEFTRECTEXTEND: + MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selRectangle); + SetLastXChosen(); + break; + case SCI_CHARRIGHT: + if (SelectionEmpty() || moveExtendsSelection) { + MovePositionTo(MovePositionSoVisible(currentPos + 1, 1)); + } else { + MovePositionTo(SelectionEnd()); + } + SetLastXChosen(); + break; + case SCI_CHARRIGHTEXTEND: + MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selStream); + SetLastXChosen(); + break; + case SCI_CHARRIGHTRECTEXTEND: + MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selRectangle); + SetLastXChosen(); + break; + case SCI_WORDLEFT: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1)); + SetLastXChosen(); + break; + case SCI_WORDLEFTEXTEND: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1), selStream); + SetLastXChosen(); + break; + case SCI_WORDRIGHT: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1)); + SetLastXChosen(); + break; + case SCI_WORDRIGHTEXTEND: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1), selStream); + SetLastXChosen(); + break; + + case SCI_WORDLEFTEND: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1)); + SetLastXChosen(); + break; + case SCI_WORDLEFTENDEXTEND: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1), selStream); + SetLastXChosen(); + break; + case SCI_WORDRIGHTEND: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1)); + SetLastXChosen(); + break; + case SCI_WORDRIGHTENDEXTEND: + MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1), selStream); + SetLastXChosen(); + break; + + case SCI_HOME: + MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos))); + SetLastXChosen(); + break; + case SCI_HOMEEXTEND: + MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selStream); + SetLastXChosen(); + break; + case SCI_HOMERECTEXTEND: + MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selRectangle); + SetLastXChosen(); + break; + case SCI_LINEEND: + MovePositionTo(pdoc->LineEndPosition(currentPos)); + SetLastXChosen(); + break; + case SCI_LINEENDEXTEND: + MovePositionTo(pdoc->LineEndPosition(currentPos), selStream); + SetLastXChosen(); + break; + case SCI_LINEENDRECTEXTEND: + MovePositionTo(pdoc->LineEndPosition(currentPos), selRectangle); + SetLastXChosen(); + break; + case SCI_HOMEWRAP: { + int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1); + if (currentPos <= homePos) + homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos)); + MovePositionTo(homePos); + SetLastXChosen(); + } + break; + case SCI_HOMEWRAPEXTEND: { + int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1); + if (currentPos <= homePos) + homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos)); + MovePositionTo(homePos, selStream); + SetLastXChosen(); + } + break; + case SCI_LINEENDWRAP: { + int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1); + int realEndPos = pdoc->LineEndPosition(currentPos); + if (endPos > realEndPos // if moved past visible EOLs + || currentPos >= endPos) // if at end of display line already + endPos = realEndPos; + MovePositionTo(endPos); + SetLastXChosen(); + } + break; + case SCI_LINEENDWRAPEXTEND: { + int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1); + int realEndPos = pdoc->LineEndPosition(currentPos); + if (endPos > realEndPos // if moved past visible EOLs + || currentPos >= endPos) // if at end of display line already + endPos = realEndPos; + MovePositionTo(endPos, selStream); + SetLastXChosen(); + } + break; + case SCI_DOCUMENTSTART: + MovePositionTo(0); + SetLastXChosen(); + break; + case SCI_DOCUMENTSTARTEXTEND: + MovePositionTo(0, selStream); + SetLastXChosen(); + break; + case SCI_DOCUMENTEND: + MovePositionTo(pdoc->Length()); + SetLastXChosen(); + break; + case SCI_DOCUMENTENDEXTEND: + MovePositionTo(pdoc->Length(), selStream); + SetLastXChosen(); + break; + case SCI_STUTTEREDPAGEUP: + PageMove(-1, noSel, true); + break; + case SCI_STUTTEREDPAGEUPEXTEND: + PageMove(-1, selStream, true); + break; + case SCI_STUTTEREDPAGEDOWN: + PageMove(1, noSel, true); + break; + case SCI_STUTTEREDPAGEDOWNEXTEND: + PageMove(1, selStream, true); + break; + case SCI_PAGEUP: + PageMove(-1); + break; + case SCI_PAGEUPEXTEND: + PageMove(-1, selStream); + break; + case SCI_PAGEUPRECTEXTEND: + PageMove(-1, selRectangle); + break; + case SCI_PAGEDOWN: + PageMove(1); + break; + case SCI_PAGEDOWNEXTEND: + PageMove(1, selStream); + break; + case SCI_PAGEDOWNRECTEXTEND: + PageMove(1, selRectangle); + break; + case SCI_EDITTOGGLEOVERTYPE: + inOverstrike = !inOverstrike; + DropCaret(); + ShowCaretAtCurrentPosition(); + NotifyUpdateUI(); + break; + case SCI_CANCEL: // Cancel any modes - handled in subclass + // Also unselect text + CancelModes(); + break; + case SCI_DELETEBACK: + DelCharBack(true); + if (!caretSticky) { + SetLastXChosen(); + } + EnsureCaretVisible(); + break; + case SCI_DELETEBACKNOTLINE: + DelCharBack(false); + if (!caretSticky) { + SetLastXChosen(); + } + EnsureCaretVisible(); + break; + case SCI_TAB: + Indent(true); + if (!caretSticky) { + SetLastXChosen(); + } + EnsureCaretVisible(); + break; + case SCI_BACKTAB: + Indent(false); + if (!caretSticky) { + SetLastXChosen(); + } + EnsureCaretVisible(); + break; + case SCI_NEWLINE: + NewLine(); + break; + case SCI_FORMFEED: + AddChar('\f'); + break; + case SCI_VCHOME: + MovePositionTo(pdoc->VCHomePosition(currentPos)); + SetLastXChosen(); + break; + case SCI_VCHOMEEXTEND: + MovePositionTo(pdoc->VCHomePosition(currentPos), selStream); + SetLastXChosen(); + break; + case SCI_VCHOMERECTEXTEND: + MovePositionTo(pdoc->VCHomePosition(currentPos), selRectangle); + SetLastXChosen(); + break; + case SCI_VCHOMEWRAP: { + int homePos = pdoc->VCHomePosition(currentPos); + int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1); + if ((viewLineStart < currentPos) && (viewLineStart > homePos)) + homePos = viewLineStart; + + MovePositionTo(homePos); + SetLastXChosen(); + } + break; + case SCI_VCHOMEWRAPEXTEND: { + int homePos = pdoc->VCHomePosition(currentPos); + int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1); + if ((viewLineStart < currentPos) && (viewLineStart > homePos)) + homePos = viewLineStart; + + MovePositionTo(homePos, selStream); + SetLastXChosen(); + } + break; + case SCI_ZOOMIN: + if (vs.zoomLevel < 20) { + vs.zoomLevel++; + InvalidateStyleRedraw(); + NotifyZoom(); + } + break; + case SCI_ZOOMOUT: + if (vs.zoomLevel > -10) { + vs.zoomLevel--; + InvalidateStyleRedraw(); + NotifyZoom(); + } + break; + case SCI_DELWORDLEFT: { + int startWord = pdoc->NextWordStart(currentPos, -1); + pdoc->DeleteChars(startWord, currentPos - startWord); + SetLastXChosen(); + } + break; + case SCI_DELWORDRIGHT: { + int endWord = pdoc->NextWordStart(currentPos, 1); + pdoc->DeleteChars(currentPos, endWord - currentPos); + } + break; + case SCI_DELLINELEFT: { + int line = pdoc->LineFromPosition(currentPos); + int start = pdoc->LineStart(line); + pdoc->DeleteChars(start, currentPos - start); + SetLastXChosen(); + } + break; + case SCI_DELLINERIGHT: { + int line = pdoc->LineFromPosition(currentPos); + int end = pdoc->LineEnd(line); + pdoc->DeleteChars(currentPos, end - currentPos); + } + break; + case SCI_LINECOPY: { + int lineStart = pdoc->LineFromPosition(SelectionStart()); + int lineEnd = pdoc->LineFromPosition(SelectionEnd()); + CopyRangeToClipboard(pdoc->LineStart(lineStart), + pdoc->LineStart(lineEnd + 1)); + } + break; + case SCI_LINECUT: { + int lineStart = pdoc->LineFromPosition(SelectionStart()); + int lineEnd = pdoc->LineFromPosition(SelectionEnd()); + int start = pdoc->LineStart(lineStart); + int end = pdoc->LineStart(lineEnd + 1); + SetSelection(start, end); + Cut(); + SetLastXChosen(); + } + break; + case SCI_LINEDELETE: { + int line = pdoc->LineFromPosition(currentPos); + int start = pdoc->LineStart(line); + int end = pdoc->LineStart(line + 1); + pdoc->DeleteChars(start, end - start); + } + break; + case SCI_LINETRANSPOSE: + LineTranspose(); + break; + case SCI_LINEDUPLICATE: + Duplicate(true); + break; + case SCI_SELECTIONDUPLICATE: + Duplicate(false); + break; + case SCI_LOWERCASE: + ChangeCaseOfSelection(false); + break; + case SCI_UPPERCASE: + ChangeCaseOfSelection(true); + break; + case SCI_WORDPARTLEFT: + MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1)); + SetLastXChosen(); + break; + case SCI_WORDPARTLEFTEXTEND: + MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1), selStream); + SetLastXChosen(); + break; + case SCI_WORDPARTRIGHT: + MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1)); + SetLastXChosen(); + break; + case SCI_WORDPARTRIGHTEXTEND: + MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1), selStream); + SetLastXChosen(); + break; + case SCI_HOMEDISPLAY: + MovePositionTo(MovePositionSoVisible( + StartEndDisplayLine(currentPos, true), -1)); + SetLastXChosen(); + break; + case SCI_HOMEDISPLAYEXTEND: + MovePositionTo(MovePositionSoVisible( + StartEndDisplayLine(currentPos, true), -1), selStream); + SetLastXChosen(); + break; + case SCI_LINEENDDISPLAY: + MovePositionTo(MovePositionSoVisible( + StartEndDisplayLine(currentPos, false), 1)); + SetLastXChosen(); + break; + case SCI_LINEENDDISPLAYEXTEND: + MovePositionTo(MovePositionSoVisible( + StartEndDisplayLine(currentPos, false), 1), selStream); + SetLastXChosen(); + break; + } + return 0; +} + +int Editor::KeyDefault(int, int) { + return 0; +} + +int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) { + DwellEnd(false); + int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | + (alt ? SCI_ALT : 0); + int msg = kmap.Find(key, modifiers); + if (msg) { + if (consumed) + *consumed = true; + return WndProc(msg, 0, 0); + } else { + if (consumed) + *consumed = false; + return KeyDefault(key, modifiers); + } +} + +void Editor::SetWhitespaceVisible(int view) { + vs.viewWhitespace = static_cast(view); +} + +int Editor::GetWhitespaceVisible() { + return vs.viewWhitespace; +} + +void Editor::Indent(bool forwards) { + //Platform::DebugPrintf("INdent %d\n", forwards); + int lineOfAnchor = pdoc->LineFromPosition(anchor); + int lineCurrentPos = pdoc->LineFromPosition(currentPos); + if (lineOfAnchor == lineCurrentPos) { + if (forwards) { + pdoc->BeginUndoAction(); + ClearSelection(); + if (pdoc->GetColumn(currentPos) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) && + pdoc->tabIndents) { + int indentation = pdoc->GetLineIndentation(lineCurrentPos); + int indentationStep = pdoc->IndentSize(); + pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep); + SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos)); + } else { + if (pdoc->useTabs) { + pdoc->InsertChar(currentPos, '\t'); + SetEmptySelection(currentPos + 1); + } else { + int numSpaces = (pdoc->tabInChars) - + (pdoc->GetColumn(currentPos) % (pdoc->tabInChars)); + if (numSpaces < 1) + numSpaces = pdoc->tabInChars; + for (int i = 0; i < numSpaces; i++) { + pdoc->InsertChar(currentPos + i, ' '); + } + SetEmptySelection(currentPos + numSpaces); + } + } + pdoc->EndUndoAction(); + } else { + if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) && + pdoc->tabIndents) { + pdoc->BeginUndoAction(); + int indentation = pdoc->GetLineIndentation(lineCurrentPos); + int indentationStep = pdoc->IndentSize(); + pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep); + SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos)); + pdoc->EndUndoAction(); + } else { + int newColumn = ((pdoc->GetColumn(currentPos) - 1) / pdoc->tabInChars) * + pdoc->tabInChars; + if (newColumn < 0) + newColumn = 0; + int newPos = currentPos; + while (pdoc->GetColumn(newPos) > newColumn) + newPos--; + SetEmptySelection(newPos); + } + } + } else { + int anchorPosOnLine = anchor - pdoc->LineStart(lineOfAnchor); + int currentPosPosOnLine = currentPos - pdoc->LineStart(lineCurrentPos); + // Multiple lines selected so indent / dedent + int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos); + int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos); + if (pdoc->LineStart(lineBottomSel) == anchor || pdoc->LineStart(lineBottomSel) == currentPos) + lineBottomSel--; // If not selecting any characters on a line, do not indent + pdoc->BeginUndoAction(); + pdoc->Indent(forwards, lineBottomSel, lineTopSel); + pdoc->EndUndoAction(); + if (lineOfAnchor < lineCurrentPos) { + if (currentPosPosOnLine == 0) + SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor)); + else + SetSelection(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor)); + } else { + if (anchorPosOnLine == 0) + SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor)); + else + SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1)); + } + } +} + +/** + * Search of a text in the document, in the given range. + * @return The position of the found text, -1 if not found. + */ +long Editor::FindText( + uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD, + ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX. + sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range. + + TextToFind *ft = reinterpret_cast(lParam); + int lengthFound = istrlen(ft->lpstrText); + int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText, + (wParam & SCFIND_MATCHCASE) != 0, + (wParam & SCFIND_WHOLEWORD) != 0, + (wParam & SCFIND_WORDSTART) != 0, + (wParam & SCFIND_REGEXP) != 0, + (wParam & SCFIND_POSIX) != 0, + &lengthFound); + if (pos != -1) { + ft->chrgText.cpMin = pos; + ft->chrgText.cpMax = pos + lengthFound; + } + return pos; +} + +/** + * Relocatable search support : Searches relative to current selection + * point and sets the selection to the found text range with + * each search. + */ +/** + * Anchor following searches at current selection start: This allows + * multiple incremental interactive searches to be macro recorded + * while still setting the selection to found text so the find/select + * operation is self-contained. + */ +void Editor::SearchAnchor() { + searchAnchor = SelectionStart(); +} + +/** + * Find text from current search anchor: Must call @c SearchAnchor first. + * Used for next text and previous text requests. + * @return The position of the found text, -1 if not found. + */ +long Editor::SearchText( + unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV. + uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD, + ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX. + sptr_t lParam) { ///< The text to search for. + + const char *txt = reinterpret_cast(lParam); + int pos; + int lengthFound = istrlen(txt); + if (iMessage == SCI_SEARCHNEXT) { + pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt, + (wParam & SCFIND_MATCHCASE) != 0, + (wParam & SCFIND_WHOLEWORD) != 0, + (wParam & SCFIND_WORDSTART) != 0, + (wParam & SCFIND_REGEXP) != 0, + (wParam & SCFIND_POSIX) != 0, + &lengthFound); + } else { + pos = pdoc->FindText(searchAnchor, 0, txt, + (wParam & SCFIND_MATCHCASE) != 0, + (wParam & SCFIND_WHOLEWORD) != 0, + (wParam & SCFIND_WORDSTART) != 0, + (wParam & SCFIND_REGEXP) != 0, + (wParam & SCFIND_POSIX) != 0, + &lengthFound); + } + + if (pos != -1) { + SetSelection(pos, pos + lengthFound); + } + + return pos; +} + +/** + * Search for text in the target range of the document. + * @return The position of the found text, -1 if not found. + */ +long Editor::SearchInTarget(const char *text, int length) { + int lengthFound = length; + int pos = pdoc->FindText(targetStart, targetEnd, text, + (searchFlags & SCFIND_MATCHCASE) != 0, + (searchFlags & SCFIND_WHOLEWORD) != 0, + (searchFlags & SCFIND_WORDSTART) != 0, + (searchFlags & SCFIND_REGEXP) != 0, + (searchFlags & SCFIND_POSIX) != 0, + &lengthFound); + if (pos != -1) { + targetStart = pos; + targetEnd = pos + lengthFound; + } + return pos; +} + +void Editor::GoToLine(int lineNo) { + if (lineNo > pdoc->LinesTotal()) + lineNo = pdoc->LinesTotal(); + if (lineNo < 0) + lineNo = 0; + SetEmptySelection(pdoc->LineStart(lineNo)); + ShowCaretAtCurrentPosition(); + EnsureCaretVisible(); +} + +static bool Close(Point pt1, Point pt2) { + if (abs(pt1.x - pt2.x) > 3) + return false; + if (abs(pt1.y - pt2.y) > 3) + return false; + return true; +} + +char *Editor::CopyRange(int start, int end) { + char *text = 0; + if (start < end) { + int len = end - start; + text = new char[len + 1]; + if (text) { + for (int i = 0; i < len; i++) { + text[i] = pdoc->CharAt(start + i); + } + text[len] = '\0'; + } + } + return text; +} + +void Editor::CopySelectionFromRange(SelectionText *ss, int start, int end) { + ss->Set(CopyRange(start, end), end - start + 1, + pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false); +} + +void Editor::CopySelectionRange(SelectionText *ss) { + if (selType == selStream) { + CopySelectionFromRange(ss, SelectionStart(), SelectionEnd()); + } else { + char *text = 0; + int size = 0; + SelectionLineIterator lineIterator(this); + while (lineIterator.Iterate()) { + size += lineIterator.endPos - lineIterator.startPos; + if (selType != selLines) { + size++; + if (pdoc->eolMode == SC_EOL_CRLF) { + size++; + } + } + } + if (size > 0) { + text = new char[size + 1]; + if (text) { + int j = 0; + lineIterator.Reset(); + while (lineIterator.Iterate()) { + for (int i = lineIterator.startPos; + i < lineIterator.endPos; + i++) { + text[j++] = pdoc->CharAt(i); + } + if (selType != selLines) { + if (pdoc->eolMode != SC_EOL_LF) { + text[j++] = '\r'; + } + if (pdoc->eolMode != SC_EOL_CR) { + text[j++] = '\n'; + } + } + } + text[size] = '\0'; + } + } + ss->Set(text, size + 1, pdoc->dbcsCodePage, + vs.styles[STYLE_DEFAULT].characterSet, selType == selRectangle); + } +} + +void Editor::CopyRangeToClipboard(int start, int end) { + start = pdoc->ClampPositionIntoDocument(start); + end = pdoc->ClampPositionIntoDocument(end); + SelectionText selectedText; + selectedText.Set(CopyRange(start, end), end - start + 1, + pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false); + CopyToClipboard(selectedText); +} + +void Editor::CopyText(int length, const char *text) { + SelectionText selectedText; + selectedText.Copy(text, length + 1, + pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false); + CopyToClipboard(selectedText); +} + +void Editor::SetDragPosition(int newPos) { + if (newPos >= 0) { + newPos = MovePositionOutsideChar(newPos, 1); + posDrop = newPos; + } + if (posDrag != newPos) { + caret.on = true; + SetTicking(true); + InvalidateCaret(); + posDrag = newPos; + InvalidateCaret(); + } +} + +void Editor::DisplayCursor(Window::Cursor c) { + if (cursorMode == SC_CURSORNORMAL) + wMain.SetCursor(c); + else + wMain.SetCursor(static_cast(cursorMode)); +} + +void Editor::StartDrag() { + // Always handled by subclasses + //SetMouseCapture(true); + //DisplayCursor(Window::cursorArrow); +} + +void Editor::DropAt(int position, const char *value, bool moving, bool rectangular) { + //Platform::DebugPrintf("DropAt %d\n", inDragDrop); + if (inDragDrop) + dropWentOutside = false; + + int positionWasInSelection = PositionInSelection(position); + + bool positionOnEdgeOfSelection = + (position == SelectionStart()) || (position == SelectionEnd()); + + if ((!inDragDrop) || !(0 == positionWasInSelection) || + (positionOnEdgeOfSelection && !moving)) { + + int selStart = SelectionStart(); + int selEnd = SelectionEnd(); + + pdoc->BeginUndoAction(); + + int positionAfterDeletion = position; + if (inDragDrop && moving) { + // Remove dragged out text + if (rectangular || selType == selLines) { + SelectionLineIterator lineIterator(this); + while (lineIterator.Iterate()) { + if (position >= lineIterator.startPos) { + if (position > lineIterator.endPos) { + positionAfterDeletion -= lineIterator.endPos - lineIterator.startPos; + } else { + positionAfterDeletion -= position - lineIterator.startPos; + } + } + } + } else { + if (position > selStart) { + positionAfterDeletion -= selEnd - selStart; + } + } + ClearSelection(); + } + position = positionAfterDeletion; + + if (rectangular) { + PasteRectangular(position, value, istrlen(value)); + pdoc->EndUndoAction(); + // Should try to select new rectangle but it may not be a rectangle now so just select the drop position + SetEmptySelection(position); + } else { + position = MovePositionOutsideChar(position, currentPos - position); + if (pdoc->InsertString(position, value)) { + SetSelection(position + istrlen(value), position); + } + pdoc->EndUndoAction(); + } + } else if (inDragDrop) { + SetEmptySelection(position); + } +} + +/** + * @return -1 if given position is before the selection, + * 1 if position is after the selection, + * 0 if position is inside the selection, + */ +int Editor::PositionInSelection(int pos) { + pos = MovePositionOutsideChar(pos, currentPos - pos); + if (pos < SelectionStart()) { + return -1; + } + if (pos > SelectionEnd()) { + return 1; + } + if (selType == selStream) { + return 0; + } else { + SelectionLineIterator lineIterator(this); + lineIterator.SetAt(pdoc->LineFromPosition(pos)); + if (pos < lineIterator.startPos) { + return -1; + } else if (pos > lineIterator.endPos) { + return 1; + } else { + return 0; + } + } +} + +bool Editor::PointInSelection(Point pt) { + int pos = PositionFromLocation(pt); + if (0 == PositionInSelection(pos)) { + // Probably inside, but we must make a finer test + int selStart, selEnd; + if (selType == selStream) { + selStart = SelectionStart(); + selEnd = SelectionEnd(); + } else { + SelectionLineIterator lineIterator(this); + lineIterator.SetAt(pdoc->LineFromPosition(pos)); + selStart = lineIterator.startPos; + selEnd = lineIterator.endPos; + } + if (pos == selStart) { + // see if just before selection + Point locStart = LocationFromPosition(pos); + if (pt.x < locStart.x) { + return false; + } + } + if (pos == selEnd) { + // see if just after selection + Point locEnd = LocationFromPosition(pos); + if (pt.x > locEnd.x) { + return false; + } + } + return true; + } + return false; +} + +bool Editor::PointInSelMargin(Point pt) { + // Really means: "Point in a margin" + if (vs.fixedColumnWidth > 0) { // There is a margin + PRectangle rcSelMargin = GetClientRectangle(); + rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth; + return rcSelMargin.Contains(pt); + } else { + return false; + } +} + +void Editor::LineSelection(int lineCurrent_, int lineAnchor_) { + if (lineAnchor_ < lineCurrent_) { + SetSelection(pdoc->LineStart(lineCurrent_ + 1), + pdoc->LineStart(lineAnchor_)); + } else if (lineAnchor_ > lineCurrent_) { + SetSelection(pdoc->LineStart(lineCurrent_), + pdoc->LineStart(lineAnchor_ + 1)); + } else { // Same line, select it + SetSelection(pdoc->LineStart(lineAnchor_ + 1), + pdoc->LineStart(lineAnchor_)); + } +} + +void Editor::DwellEnd(bool mouseMoved) { + if (mouseMoved) + ticksToDwell = dwellDelay; + else + ticksToDwell = SC_TIME_FOREVER; + if (dwelling && (dwellDelay < SC_TIME_FOREVER)) { + dwelling = false; + NotifyDwelling(ptMouseLast, dwelling); + } +} + +void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) { + //Platform::DebugPrintf("Scintilla:ButtonDown %d %d = %d alt=%d\n", curTime, lastClickTime, curTime - lastClickTime, alt); + ptMouseLast = pt; + int newPos = PositionFromLocation(pt); + newPos = MovePositionOutsideChar(newPos, currentPos - newPos); + inDragDrop = false; + moveExtendsSelection = false; + + bool processed = NotifyMarginClick(pt, shift, ctrl, alt); + if (processed) + return; + + bool inSelMargin = PointInSelMargin(pt); + if (shift & !inSelMargin) { + SetSelection(newPos); + } + if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) { + //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime); + SetMouseCapture(true); + SetEmptySelection(newPos); + bool doubleClick = false; + // Stop mouse button bounce changing selection type + if (!Platform::MouseButtonBounce() || curTime != lastClickTime) { + if (selectionType == selChar) { + selectionType = selWord; + doubleClick = true; + } else if (selectionType == selWord) { + selectionType = selLine; + } else { + selectionType = selChar; + originalAnchorPos = currentPos; + } + } + + if (selectionType == selWord) { + if (currentPos >= originalAnchorPos) { // Moved forward + SetSelection(pdoc->ExtendWordSelect(currentPos, 1), + pdoc->ExtendWordSelect(originalAnchorPos, -1)); + } else { // Moved backward + SetSelection(pdoc->ExtendWordSelect(currentPos, -1), + pdoc->ExtendWordSelect(originalAnchorPos, 1)); + } + } else if (selectionType == selLine) { + lineAnchor = LineFromLocation(pt); + SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor)); + //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos); + } else { + SetEmptySelection(currentPos); + } + //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos); + if (doubleClick) { + NotifyDoubleClick(pt, shift); + if (PositionIsHotspot(newPos)) + NotifyHotSpotDoubleClicked(newPos, shift, ctrl, alt); + } + } else { // Single click + if (inSelMargin) { + selType = selStream; + if (ctrl) { + SelectAll(); + lastClickTime = curTime; + return; + } + if (!shift) { + lineAnchor = LineFromLocation(pt); + // Single click in margin: select whole line + LineSelection(lineAnchor, lineAnchor); + SetSelection(pdoc->LineStart(lineAnchor + 1), + pdoc->LineStart(lineAnchor)); + } else { + // Single shift+click in margin: select from line anchor to clicked line + if (anchor > currentPos) + lineAnchor = pdoc->LineFromPosition(anchor - 1); + else + lineAnchor = pdoc->LineFromPosition(anchor); + int lineStart = LineFromLocation(pt); + LineSelection(lineStart, lineAnchor); + //lineAnchor = lineStart; // Keep the same anchor for ButtonMove + } + + SetDragPosition(invalidPosition); + SetMouseCapture(true); + selectionType = selLine; + } else { + if (PointIsHotspot(pt)) { + NotifyHotSpotClicked(newPos, shift, ctrl, alt); + } + if (!shift) { + inDragDrop = PointInSelection(pt) && !SelectionEmpty(); + } + if (inDragDrop) { + SetMouseCapture(false); + SetDragPosition(newPos); + CopySelectionRange(&drag); + StartDrag(); + } else { + SetDragPosition(invalidPosition); + SetMouseCapture(true); + if (!shift) { + SetEmptySelection(newPos); + } + selType = alt ? selRectangle : selStream; + selectionType = selChar; + originalAnchorPos = currentPos; + SetRectangularRange(); + } + } + } + lastClickTime = curTime; + lastXChosen = pt.x; + ShowCaretAtCurrentPosition(); +} + +bool Editor::PositionIsHotspot(int position) { + return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot; +} + +bool Editor::PointIsHotspot(Point pt) { + int pos = PositionFromLocationClose(pt); + if (pos == INVALID_POSITION) + return false; + return PositionIsHotspot(pos); +} + +void Editor::SetHotSpotRange(Point *pt) { + if (pt) { + int pos = PositionFromLocation(*pt); + + // If we don't limit this to word characters then the + // range can encompass more than the run range and then + // the underline will not be drawn properly. + int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine); + int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine); + + // Only invalidate the range if the hotspot range has changed... + if (hsStart_ != hsStart || hsEnd_ != hsEnd) { + if (hsStart != -1) { + InvalidateRange(hsStart, hsEnd); + } + hsStart = hsStart_; + hsEnd = hsEnd_; + InvalidateRange(hsStart, hsEnd); + } + } else { + if (hsStart != -1) { + int hsStart_ = hsStart; + int hsEnd_ = hsEnd; + hsStart = -1; + hsEnd = -1; + InvalidateRange(hsStart_, hsEnd_); + } else { + hsStart = -1; + hsEnd = -1; + } + } +} + +void Editor::GetHotSpotRange(int& hsStart_, int& hsEnd_) { + hsStart_ = hsStart; + hsEnd_ = hsEnd; +} + +void Editor::ButtonMove(Point pt) { + if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) { + DwellEnd(true); + } + ptMouseLast = pt; + //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y); + if (HaveMouseCapture()) { + + // Slow down autoscrolling/selection + autoScrollTimer.ticksToWait -= timer.tickSize; + if (autoScrollTimer.ticksToWait > 0) + return; + autoScrollTimer.ticksToWait = autoScrollDelay; + + // Adjust selection + int movePos = PositionFromLocation(pt); + movePos = MovePositionOutsideChar(movePos, currentPos - movePos); + if (posDrag >= 0) { + SetDragPosition(movePos); + } else { + if (selectionType == selChar) { + SetSelection(movePos); + } else if (selectionType == selWord) { + // Continue selecting by word + if (movePos == originalAnchorPos) { // Didn't move + // No need to do anything. Previously this case was lumped + // in with "Moved forward", but that can be harmful in this + // case: a handler for the NotifyDoubleClick re-adjusts + // the selection for a fancier definition of "word" (for + // example, in Perl it is useful to include the leading + // '$', '%' or '@' on variables for word selection). In this + // the ButtonMove() called via Tick() for auto-scrolling + // could result in the fancier word selection adjustment + // being unmade. + } else if (movePos > originalAnchorPos) { // Moved forward + SetSelection(pdoc->ExtendWordSelect(movePos, 1), + pdoc->ExtendWordSelect(originalAnchorPos, -1)); + } else { // Moved backward + SetSelection(pdoc->ExtendWordSelect(movePos, -1), + pdoc->ExtendWordSelect(originalAnchorPos, 1)); + } + } else { + // Continue selecting by line + int lineMove = LineFromLocation(pt); + LineSelection(lineMove, lineAnchor); + } + } + // While dragging to make rectangular selection, we don't want the current + // position to jump to the end of smaller or empty lines. + //xEndSelect = pt.x - vs.fixedColumnWidth + xOffset; + xEndSelect = XFromPosition(movePos); + + // Autoscroll + PRectangle rcClient = GetClientRectangle(); + if (pt.y > rcClient.bottom) { + int lineMove = cs.DisplayFromDoc(LineFromLocation(pt)); + if (lineMove < 0) { + lineMove = cs.DisplayFromDoc(pdoc->LinesTotal() - 1); + } + ScrollTo(lineMove - LinesOnScreen() + 5); + Redraw(); + } else if (pt.y < rcClient.top) { + int lineMove = cs.DisplayFromDoc(LineFromLocation(pt)); + ScrollTo(lineMove - 5); + Redraw(); + } + EnsureCaretVisible(false, false, true); + + if (hsStart != -1 && !PositionIsHotspot(movePos)) + SetHotSpotRange(NULL); + + } else { + if (vs.fixedColumnWidth > 0) { // There is a margin + if (PointInSelMargin(pt)) { + DisplayCursor(Window::cursorReverseArrow); + return; // No need to test for selection + } + } + // Display regular (drag) cursor over selection + if (PointInSelection(pt) && !SelectionEmpty()) { + DisplayCursor(Window::cursorArrow); + } else if (PointIsHotspot(pt)) { + DisplayCursor(Window::cursorHand); + SetHotSpotRange(&pt); + } else { + DisplayCursor(Window::cursorText); + SetHotSpotRange(NULL); + } + } +} + +void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) { + //Platform::DebugPrintf("ButtonUp %d\n", HaveMouseCapture()); + if (HaveMouseCapture()) { + if (PointInSelMargin(pt)) { + DisplayCursor(Window::cursorReverseArrow); + } else { + DisplayCursor(Window::cursorText); + SetHotSpotRange(NULL); + } + ptMouseLast = pt; + SetMouseCapture(false); + int newPos = PositionFromLocation(pt); + newPos = MovePositionOutsideChar(newPos, currentPos - newPos); + if (inDragDrop) { + int selStart = SelectionStart(); + int selEnd = SelectionEnd(); + if (selStart < selEnd) { + if (drag.len) { + if (ctrl) { + if (pdoc->InsertString(newPos, drag.s, drag.len)) { + SetSelection(newPos, newPos + drag.len); + } + } else if (newPos < selStart) { + pdoc->DeleteChars(selStart, drag.len); + if (pdoc->InsertString(newPos, drag.s, drag.len)) { + SetSelection(newPos, newPos + drag.len); + } + } else if (newPos > selEnd) { + pdoc->DeleteChars(selStart, drag.len); + newPos -= drag.len; + if (pdoc->InsertString(newPos, drag.s, drag.len)) { + SetSelection(newPos, newPos + drag.len); + } + } else { + SetEmptySelection(newPos); + } + drag.Free(); + } + selectionType = selChar; + } + } else { + if (selectionType == selChar) { + SetSelection(newPos); + } + } + SetRectangularRange(); + lastClickTime = curTime; + lastClick = pt; + lastXChosen = pt.x; + if (selType == selStream) { + SetLastXChosen(); + } + inDragDrop = false; + EnsureCaretVisible(false); + } +} + +// Called frequently to perform background UI including +// caret blinking and automatic scrolling. +void Editor::Tick() { + if (HaveMouseCapture()) { + // Auto scroll + ButtonMove(ptMouseLast); + } + if (caret.period > 0) { + timer.ticksToWait -= timer.tickSize; + if (timer.ticksToWait <= 0) { + caret.on = !caret.on; + timer.ticksToWait = caret.period; + if (caret.active) { + InvalidateCaret(); + } + } + } + if ((dwellDelay < SC_TIME_FOREVER) && + (ticksToDwell > 0) && + (!HaveMouseCapture())) { + ticksToDwell -= timer.tickSize; + if (ticksToDwell <= 0) { + dwelling = true; + NotifyDwelling(ptMouseLast, dwelling); + } + } +} + +bool Editor::Idle() { + + bool idleDone; + + bool wrappingDone = wrapState == eWrapNone; + + if (!wrappingDone) { + // Wrap lines during idle. + WrapLines(false, -1); + // No more wrapping + if (wrapStart == wrapEnd) + wrappingDone = true; + } + + // Add more idle things to do here, but make sure idleDone is + // set correctly before the function returns. returning + // false will stop calling this idle funtion until SetIdle() is + // called again. + + idleDone = wrappingDone; // && thatDone && theOtherThingDone... + + return !idleDone; +} + +void Editor::SetFocusState(bool focusState) { + hasFocus = focusState; + NotifyFocus(hasFocus); + if (hasFocus) { + ShowCaretAtCurrentPosition(); + } else { + CancelModes(); + DropCaret(); + } +} + +bool Editor::PaintContains(PRectangle rc) { + return rcPaint.Contains(rc); +} + +bool Editor::PaintContainsMargin() { + PRectangle rcSelMargin = GetClientRectangle(); + rcSelMargin.right = vs.fixedColumnWidth; + return PaintContains(rcSelMargin); +} + +void Editor::CheckForChangeOutsidePaint(Range r) { + if (paintState == painting && !paintingAllText) { + //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end); + if (!r.Valid()) + return; + + PRectangle rcRange = RectangleFromRange(r.start, r.end); + PRectangle rcText = GetTextRectangle(); + if (rcRange.top < rcText.top) { + rcRange.top = rcText.top; + } + if (rcRange.bottom > rcText.bottom) { + rcRange.bottom = rcText.bottom; + } + + if (!PaintContains(rcRange)) { + AbandonPaint(); + } + } +} + +void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) { + if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) { + if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) { + CheckForChangeOutsidePaint(Range(braces[0])); + CheckForChangeOutsidePaint(Range(pos0)); + braces[0] = pos0; + } + if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) { + CheckForChangeOutsidePaint(Range(braces[1])); + CheckForChangeOutsidePaint(Range(pos1)); + braces[1] = pos1; + } + bracesMatchStyle = matchStyle; + if (paintState == notPainting) { + Redraw(); + } + } +} + +void Editor::SetDocPointer(Document *document) { + //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document); + pdoc->RemoveWatcher(this, 0); + pdoc->Release(); + if (document == NULL) { + pdoc = new Document(); + } else { + pdoc = document; + } + pdoc->AddRef(); + + // Ensure all positions within document + selType = selStream; + currentPos = 0; + anchor = 0; + targetStart = 0; + targetEnd = 0; + + braces[0] = invalidPosition; + braces[1] = invalidPosition; + + // Reset the contraction state to fully shown. + cs.Clear(); + cs.InsertLines(0, pdoc->LinesTotal() - 1); + llc.Deallocate(); + NeedWrapping(); + + pdoc->AddWatcher(this, 0); + SetScrollBars(); + Redraw(); +} + +/** + * Recursively expand a fold, making lines visible except where they have an unexpanded parent. + */ +void Editor::Expand(int &line, bool doExpand) { + int lineMaxSubord = pdoc->GetLastChild(line); + line++; + while (line <= lineMaxSubord) { + if (doExpand) + cs.SetVisible(line, line, true); + int level = pdoc->GetLevel(line); + if (level & SC_FOLDLEVELHEADERFLAG) { + if (doExpand && cs.GetExpanded(line)) { + Expand(line, true); + } else { + Expand(line, false); + } + } else { + line++; + } + } +} + +void Editor::ToggleContraction(int line) { + if (line >= 0) { + if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) { + line = pdoc->GetFoldParent(line); + if (line < 0) + return; + } + + if (cs.GetExpanded(line)) { + int lineMaxSubord = pdoc->GetLastChild(line); + cs.SetExpanded(line, 0); + if (lineMaxSubord > line) { + cs.SetVisible(line + 1, lineMaxSubord, false); + + int lineCurrent = pdoc->LineFromPosition(currentPos); + if (lineCurrent > line && lineCurrent <= lineMaxSubord) { + // This does not re-expand the fold + EnsureCaretVisible(); + } + + SetScrollBars(); + Redraw(); + } + + } else { + if (!(cs.GetVisible(line))) { + EnsureLineVisible(line, false); + GoToLine(line); + } + cs.SetExpanded(line, 1); + Expand(line, true); + SetScrollBars(); + Redraw(); + } + } +} + +/** + * Recurse up from this line to find any folds that prevent this line from being visible + * and unfold them all. + */ +void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) { + + // In case in need of wrapping to ensure DisplayFromDoc works. + WrapLines(true, -1); + + if (!cs.GetVisible(lineDoc)) { + int lineParent = pdoc->GetFoldParent(lineDoc); + if (lineParent >= 0) { + if (lineDoc != lineParent) + EnsureLineVisible(lineParent, enforcePolicy); + if (!cs.GetExpanded(lineParent)) { + cs.SetExpanded(lineParent, 1); + Expand(lineParent, true); + } + } + SetScrollBars(); + Redraw(); + } + if (enforcePolicy) { + int lineDisplay = cs.DisplayFromDoc(lineDoc); + if (visiblePolicy & VISIBLE_SLOP) { + if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) { + SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos())); + SetVerticalScrollPos(); + Redraw(); + } else if ((lineDisplay > topLine + LinesOnScreen() - 1) || + ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) { + SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos())); + SetVerticalScrollPos(); + Redraw(); + } + } else { + if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) { + SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos())); + SetVerticalScrollPos(); + Redraw(); + } + } + } +} + +int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) { + pdoc->BeginUndoAction(); + if (length == -1) + length = istrlen(text); + if (replacePatterns) { + text = pdoc->SubstituteByPosition(text, &length); + if (!text) + return 0; + } + if (targetStart != targetEnd) + pdoc->DeleteChars(targetStart, targetEnd - targetStart); + targetEnd = targetStart; + pdoc->InsertString(targetStart, text, length); + targetEnd = targetStart + length; + pdoc->EndUndoAction(); + return length; +} + +bool Editor::IsUnicodeMode() const { + return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage); +} + +int Editor::CodePage() const { + if (pdoc) + return pdoc->dbcsCodePage; + else + return 0; +} + +int Editor::WrapCount(int line) { + AutoSurface surface(this); + AutoLineLayout ll(llc, RetrieveLineLayout(line)); + + if (surface && ll) { + LayoutLine(line, surface, vs, ll, wrapWidth); + return ll->lines; + } else { + return 1; + } +} + +static bool ValidMargin(unsigned long wParam) { + return wParam < ViewStyle::margins; +} + +static char *CharPtrFromSPtr(sptr_t lParam) { + return reinterpret_cast(lParam); +} + +sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { + //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam); + + // Optional macro recording hook + if (recordingMacro) + NotifyMacroRecord(iMessage, wParam, lParam); + + switch (iMessage) { + + case SCI_GETTEXT: { + if (lParam == 0) + return pdoc->Length() + 1; + if (wParam == 0) + return 0; + char *ptr = CharPtrFromSPtr(lParam); + unsigned int iChar = 0; + for (; iChar < wParam - 1; iChar++) + ptr[iChar] = pdoc->CharAt(iChar); + ptr[iChar] = '\0'; + return iChar; + } + + case SCI_SETTEXT: { + if (lParam == 0) + return 0; + pdoc->BeginUndoAction(); + pdoc->DeleteChars(0, pdoc->Length()); + SetEmptySelection(0); + pdoc->InsertString(0, CharPtrFromSPtr(lParam)); + pdoc->EndUndoAction(); + return 1; + } + + case SCI_GETTEXTLENGTH: + return pdoc->Length(); + + case SCI_CUT: + Cut(); + SetLastXChosen(); + break; + + case SCI_COPY: + Copy(); + break; + + case SCI_COPYRANGE: + CopyRangeToClipboard(wParam, lParam); + break; + + case SCI_COPYTEXT: + CopyText(wParam, CharPtrFromSPtr(lParam)); + break; + + case SCI_PASTE: + Paste(); + if (!caretSticky) { + SetLastXChosen(); + } + EnsureCaretVisible(); + break; + + case SCI_CLEAR: + Clear(); + SetLastXChosen(); + EnsureCaretVisible(); + break; + + case SCI_UNDO: + Undo(); + SetLastXChosen(); + break; + + case SCI_CANUNDO: + return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0; + + case SCI_EMPTYUNDOBUFFER: + pdoc->DeleteUndoHistory(); + return 0; + + case SCI_GETFIRSTVISIBLELINE: + return topLine; + + case SCI_GETLINE: { // Risk of overwriting the end of the buffer + int lineStart = pdoc->LineStart(wParam); + int lineEnd = pdoc->LineStart(wParam + 1); + if (lParam == 0) { + return lineEnd - lineStart; + } + char *ptr = CharPtrFromSPtr(lParam); + int iPlace = 0; + for (int iChar = lineStart; iChar < lineEnd; iChar++) { + ptr[iPlace++] = pdoc->CharAt(iChar); + } + return iPlace; + } + + case SCI_GETLINECOUNT: + if (pdoc->LinesTotal() == 0) + return 1; + else + return pdoc->LinesTotal(); + + case SCI_GETMODIFY: + return !pdoc->IsSavePoint(); + + case SCI_SETSEL: { + int nStart = static_cast(wParam); + int nEnd = static_cast(lParam); + if (nEnd < 0) + nEnd = pdoc->Length(); + if (nStart < 0) + nStart = nEnd; // Remove selection + selType = selStream; + SetSelection(nEnd, nStart); + EnsureCaretVisible(); + } + break; + + case SCI_GETSELTEXT: { + if (lParam == 0) { + if (selType == selStream) { + return 1 + SelectionEnd() - SelectionStart(); + } else { + // TODO: why is selLines handled the slow way? + int size = 0; + int extraCharsPerLine = 0; + if (selType != selLines) + extraCharsPerLine = (pdoc->eolMode == SC_EOL_CRLF) ? 2 : 1; + SelectionLineIterator lineIterator(this); + while (lineIterator.Iterate()) { + size += lineIterator.endPos + extraCharsPerLine - lineIterator.startPos; + } + + return 1 + size; + } + } + SelectionText selectedText; + CopySelectionRange(&selectedText); + char *ptr = CharPtrFromSPtr(lParam); + int iChar = 0; + if (selectedText.len) { + for (; iChar < selectedText.len; iChar++) + ptr[iChar] = selectedText.s[iChar]; + } else { + ptr[0] = '\0'; + } + return iChar; + } + + case SCI_LINEFROMPOSITION: + if (static_cast(wParam) < 0) + return 0; + return pdoc->LineFromPosition(wParam); + + case SCI_POSITIONFROMLINE: + if (static_cast(wParam) < 0) + wParam = pdoc->LineFromPosition(SelectionStart()); + if (wParam == 0) + return 0; // Even if there is no text, there is a first line that starts at 0 + if (static_cast(wParam) > pdoc->LinesTotal()) + return -1; + //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway... + // return -1; + return pdoc->LineStart(wParam); + + // Replacement of the old Scintilla interpretation of EM_LINELENGTH + case SCI_LINELENGTH: + if ((static_cast(wParam) < 0) || + (static_cast(wParam) > pdoc->LineFromPosition(pdoc->Length()))) + return 0; + return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam); + + case SCI_REPLACESEL: { + if (lParam == 0) + return 0; + pdoc->BeginUndoAction(); + ClearSelection(); + char *replacement = CharPtrFromSPtr(lParam); + pdoc->InsertString(currentPos, replacement); + pdoc->EndUndoAction(); + SetEmptySelection(currentPos + istrlen(replacement)); + EnsureCaretVisible(); + } + break; + + case SCI_SETTARGETSTART: + targetStart = wParam; + break; + + case SCI_GETTARGETSTART: + return targetStart; + + case SCI_SETTARGETEND: + targetEnd = wParam; + break; + + case SCI_GETTARGETEND: + return targetEnd; + + case SCI_TARGETFROMSELECTION: + if (currentPos < anchor) { + targetStart = currentPos; + targetEnd = anchor; + } else { + targetStart = anchor; + targetEnd = currentPos; + } + break; + + case SCI_REPLACETARGET: + PLATFORM_ASSERT(lParam); + return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam); + + case SCI_REPLACETARGETRE: + PLATFORM_ASSERT(lParam); + return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam); + + case SCI_SEARCHINTARGET: + PLATFORM_ASSERT(lParam); + return SearchInTarget(CharPtrFromSPtr(lParam), wParam); + + case SCI_SETSEARCHFLAGS: + searchFlags = wParam; + break; + + case SCI_GETSEARCHFLAGS: + return searchFlags; + + case SCI_POSITIONBEFORE: + return pdoc->MovePositionOutsideChar(wParam-1, -1, true); + + case SCI_POSITIONAFTER: + return pdoc->MovePositionOutsideChar(wParam+1, 1, true); + + case SCI_LINESCROLL: + ScrollTo(topLine + lParam); + HorizontalScrollTo(xOffset + wParam * vs.spaceWidth); + return 1; + + case SCI_SETXOFFSET: + xOffset = wParam; + SetHorizontalScrollPos(); + Redraw(); + break; + + case SCI_GETXOFFSET: + return xOffset; + + case SCI_CHOOSECARETX: + SetLastXChosen(); + break; + + case SCI_SCROLLCARET: + EnsureCaretVisible(); + break; + + case SCI_SETREADONLY: + pdoc->SetReadOnly(wParam != 0); + return 1; + + case SCI_GETREADONLY: + return pdoc->IsReadOnly(); + + case SCI_CANPASTE: + return CanPaste(); + + case SCI_POINTXFROMPOSITION: + if (lParam < 0) { + return 0; + } else { + Point pt = LocationFromPosition(lParam); + return pt.x; + } + + case SCI_POINTYFROMPOSITION: + if (lParam < 0) { + return 0; + } else { + Point pt = LocationFromPosition(lParam); + return pt.y; + } + + case SCI_FINDTEXT: + return FindText(wParam, lParam); + + case SCI_GETTEXTRANGE: { + if (lParam == 0) + return 0; + TextRange *tr = reinterpret_cast(lParam); + int cpMax = tr->chrg.cpMax; + if (cpMax == -1) + cpMax = pdoc->Length(); + PLATFORM_ASSERT(cpMax <= pdoc->Length()); + int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions + pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len); + // Spec says copied text is terminated with a NUL + tr->lpstrText[len] = '\0'; + return len; // Not including NUL + } + + case SCI_HIDESELECTION: + hideSelection = wParam != 0; + Redraw(); + break; + + case SCI_FORMATRANGE: + return FormatRange(wParam != 0, reinterpret_cast(lParam)); + + case SCI_GETMARGINLEFT: + return vs.leftMarginWidth; + + case SCI_GETMARGINRIGHT: + return vs.rightMarginWidth; + + case SCI_SETMARGINLEFT: + vs.leftMarginWidth = lParam; + InvalidateStyleRedraw(); + break; + + case SCI_SETMARGINRIGHT: + vs.rightMarginWidth = lParam; + InvalidateStyleRedraw(); + break; + + // Control specific mesages + + case SCI_ADDTEXT: { + if (lParam == 0) + return 0; + pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam); + SetEmptySelection(currentPos + wParam); + return 0; + } + + case SCI_ADDSTYLEDTEXT: { + if (lParam == 0) + return 0; + pdoc->InsertStyledString(CurrentPosition() * 2, CharPtrFromSPtr(lParam), wParam); + SetEmptySelection(currentPos + wParam / 2); + return 0; + } + + case SCI_INSERTTEXT: { + if (lParam == 0) + return 0; + int insertPos = wParam; + if (static_cast(wParam) == -1) + insertPos = CurrentPosition(); + int newCurrent = CurrentPosition(); + char *sz = CharPtrFromSPtr(lParam); + pdoc->InsertString(insertPos, sz); + if (newCurrent > insertPos) + newCurrent += istrlen(sz); + SetEmptySelection(newCurrent); + return 0; + } + + case SCI_APPENDTEXT: + pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam); + return 0; + + case SCI_CLEARALL: + ClearAll(); + return 0; + + case SCI_CLEARDOCUMENTSTYLE: + ClearDocumentStyle(); + return 0; + + case SCI_SETUNDOCOLLECTION: + pdoc->SetUndoCollection(wParam != 0); + return 0; + + case SCI_GETUNDOCOLLECTION: + return pdoc->IsCollectingUndo(); + + case SCI_BEGINUNDOACTION: + pdoc->BeginUndoAction(); + return 0; + + case SCI_ENDUNDOACTION: + pdoc->EndUndoAction(); + return 0; + + case SCI_GETCARETPERIOD: + return caret.period; + + case SCI_SETCARETPERIOD: + caret.period = wParam; + break; + + case SCI_SETWORDCHARS: { + pdoc->SetDefaultCharClasses(false); + if (lParam == 0) + return 0; + pdoc->SetCharClasses(reinterpret_cast(lParam), CharClassify::ccWord); + } + break; + + case SCI_SETWHITESPACECHARS: { + if (lParam == 0) + return 0; + pdoc->SetCharClasses(reinterpret_cast(lParam), CharClassify::ccSpace); + } + break; + + case SCI_SETCHARSDEFAULT: + pdoc->SetDefaultCharClasses(true); + break; + + case SCI_GETLENGTH: + return pdoc->Length(); + + case SCI_ALLOCATE: + pdoc->Allocate(wParam); + break; + + case SCI_GETCHARAT: + return pdoc->CharAt(wParam); + + case SCI_SETCURRENTPOS: + SetSelection(wParam, anchor); + break; + + case SCI_GETCURRENTPOS: + return currentPos; + + case SCI_SETANCHOR: + SetSelection(currentPos, wParam); + break; + + case SCI_GETANCHOR: + return anchor; + + case SCI_SETSELECTIONSTART: + SetSelection(Platform::Maximum(currentPos, wParam), wParam); + break; + + case SCI_GETSELECTIONSTART: + return Platform::Minimum(anchor, currentPos); + + case SCI_SETSELECTIONEND: + SetSelection(wParam, Platform::Minimum(anchor, wParam)); + break; + + case SCI_GETSELECTIONEND: + return Platform::Maximum(anchor, currentPos); + + case SCI_SETPRINTMAGNIFICATION: + printMagnification = wParam; + break; + + case SCI_GETPRINTMAGNIFICATION: + return printMagnification; + + case SCI_SETPRINTCOLOURMODE: + printColourMode = wParam; + break; + + case SCI_GETPRINTCOLOURMODE: + return printColourMode; + + case SCI_SETPRINTWRAPMODE: + printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone; + break; + + case SCI_GETPRINTWRAPMODE: + return printWrapState; + + case SCI_GETSTYLEAT: + if (static_cast(wParam) >= pdoc->Length()) + return 0; + else + return pdoc->StyleAt(wParam); + + case SCI_REDO: + Redo(); + break; + + case SCI_SELECTALL: + SelectAll(); + break; + + case SCI_SETSAVEPOINT: + pdoc->SetSavePoint(); + break; + + case SCI_GETSTYLEDTEXT: { + if (lParam == 0) + return 0; + TextRange *tr = reinterpret_cast(lParam); + int iPlace = 0; + for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) { + tr->lpstrText[iPlace++] = pdoc->CharAt(iChar); + tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar); + } + tr->lpstrText[iPlace] = '\0'; + tr->lpstrText[iPlace + 1] = '\0'; + return iPlace; + } + + case SCI_CANREDO: + return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0; + + case SCI_MARKERLINEFROMHANDLE: + return pdoc->LineFromHandle(wParam); + + case SCI_MARKERDELETEHANDLE: + pdoc->DeleteMarkFromHandle(wParam); + break; + + case SCI_GETVIEWWS: + return vs.viewWhitespace; + + case SCI_SETVIEWWS: + vs.viewWhitespace = static_cast(wParam); + Redraw(); + break; + + case SCI_POSITIONFROMPOINT: + return PositionFromLocation(Point(wParam, lParam)); + + case SCI_POSITIONFROMPOINTCLOSE: + return PositionFromLocationClose(Point(wParam, lParam)); + + case SCI_GOTOLINE: + GoToLine(wParam); + break; + + case SCI_GOTOPOS: + SetEmptySelection(wParam); + EnsureCaretVisible(); + Redraw(); + break; + + case SCI_GETCURLINE: { + int lineCurrentPos = pdoc->LineFromPosition(currentPos); + int lineStart = pdoc->LineStart(lineCurrentPos); + unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1); + if (lParam == 0) { + return 1 + lineEnd - lineStart; + } + PLATFORM_ASSERT(wParam > 0); + char *ptr = CharPtrFromSPtr(lParam); + unsigned int iPlace = 0; + for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) { + ptr[iPlace++] = pdoc->CharAt(iChar); + } + ptr[iPlace] = '\0'; + return currentPos - lineStart; + } + + case SCI_GETENDSTYLED: + return pdoc->GetEndStyled(); + + case SCI_GETEOLMODE: + return pdoc->eolMode; + + case SCI_SETEOLMODE: + pdoc->eolMode = wParam; + break; + + case SCI_STARTSTYLING: + pdoc->StartStyling(wParam, static_cast(lParam)); + break; + + case SCI_SETSTYLING: + pdoc->SetStyleFor(wParam, static_cast(lParam)); + break; + + case SCI_SETSTYLINGEX: // Specify a complete styling buffer + if (lParam == 0) + return 0; + pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam)); + break; + + case SCI_SETBUFFEREDDRAW: + bufferedDraw = wParam != 0; + break; + + case SCI_GETBUFFEREDDRAW: + return bufferedDraw; + + case SCI_GETTWOPHASEDRAW: + return twoPhaseDraw; + + case SCI_SETTWOPHASEDRAW: + twoPhaseDraw = wParam != 0; + InvalidateStyleRedraw(); + break; + + case SCI_SETTABWIDTH: + if (wParam > 0) { + pdoc->tabInChars = wParam; + if (pdoc->indentInChars == 0) + pdoc->actualIndentInChars = pdoc->tabInChars; + } + InvalidateStyleRedraw(); + break; + + case SCI_GETTABWIDTH: + return pdoc->tabInChars; + + case SCI_SETINDENT: + pdoc->indentInChars = wParam; + if (pdoc->indentInChars != 0) + pdoc->actualIndentInChars = pdoc->indentInChars; + else + pdoc->actualIndentInChars = pdoc->tabInChars; + InvalidateStyleRedraw(); + break; + + case SCI_GETINDENT: + return pdoc->indentInChars; + + case SCI_SETUSETABS: + pdoc->useTabs = wParam != 0; + InvalidateStyleRedraw(); + break; + + case SCI_GETUSETABS: + return pdoc->useTabs; + + case SCI_SETLINEINDENTATION: + pdoc->SetLineIndentation(wParam, lParam); + break; + + case SCI_GETLINEINDENTATION: + return pdoc->GetLineIndentation(wParam); + + case SCI_GETLINEINDENTPOSITION: + return pdoc->GetLineIndentPosition(wParam); + + case SCI_SETTABINDENTS: + pdoc->tabIndents = wParam != 0; + break; + + case SCI_GETTABINDENTS: + return pdoc->tabIndents; + + case SCI_SETBACKSPACEUNINDENTS: + pdoc->backspaceUnindents = wParam != 0; + break; + + case SCI_GETBACKSPACEUNINDENTS: + return pdoc->backspaceUnindents; + + case SCI_SETMOUSEDWELLTIME: + dwellDelay = wParam; + ticksToDwell = dwellDelay; + break; + + case SCI_GETMOUSEDWELLTIME: + return dwellDelay; + + case SCI_WORDSTARTPOSITION: + return pdoc->ExtendWordSelect(wParam, -1, lParam != 0); + + case SCI_WORDENDPOSITION: + return pdoc->ExtendWordSelect(wParam, 1, lParam != 0); + + case SCI_SETWRAPMODE: + switch (wParam) { + case SC_WRAP_WORD: + wrapState = eWrapWord; + break; + case SC_WRAP_CHAR: + wrapState = eWrapChar; + break; + default: + wrapState = eWrapNone; + break; + } + xOffset = 0; + InvalidateStyleRedraw(); + ReconfigureScrollBars(); + break; + + case SCI_GETWRAPMODE: + return wrapState; + + case SCI_SETWRAPVISUALFLAGS: + wrapVisualFlags = wParam; + actualWrapVisualStartIndent = wrapVisualStartIndent; + if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (actualWrapVisualStartIndent == 0)) + actualWrapVisualStartIndent = 1; // must indent to show start visual + InvalidateStyleRedraw(); + ReconfigureScrollBars(); + break; + + case SCI_GETWRAPVISUALFLAGS: + return wrapVisualFlags; + + case SCI_SETWRAPVISUALFLAGSLOCATION: + wrapVisualFlagsLocation = wParam; + InvalidateStyleRedraw(); + break; + + case SCI_GETWRAPVISUALFLAGSLOCATION: + return wrapVisualFlagsLocation; + + case SCI_SETWRAPSTARTINDENT: + wrapVisualStartIndent = wParam; + actualWrapVisualStartIndent = wrapVisualStartIndent; + if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (actualWrapVisualStartIndent == 0)) + actualWrapVisualStartIndent = 1; // must indent to show start visual + InvalidateStyleRedraw(); + ReconfigureScrollBars(); + break; + + case SCI_GETWRAPSTARTINDENT: + return wrapVisualStartIndent; + + case SCI_SETLAYOUTCACHE: + llc.SetLevel(wParam); + break; + + case SCI_GETLAYOUTCACHE: + return llc.GetLevel(); + + case SCI_SETSCROLLWIDTH: + PLATFORM_ASSERT(wParam > 0); + if ((wParam > 0) && (wParam != static_cast(scrollWidth))) { + scrollWidth = wParam; + SetScrollBars(); + } + break; + + case SCI_GETSCROLLWIDTH: + return scrollWidth; + + case SCI_LINESJOIN: + LinesJoin(); + break; + + case SCI_LINESSPLIT: + LinesSplit(wParam); + break; + + case SCI_TEXTWIDTH: + PLATFORM_ASSERT(wParam <= STYLE_MAX); + PLATFORM_ASSERT(lParam); + return TextWidth(wParam, CharPtrFromSPtr(lParam)); + + case SCI_TEXTHEIGHT: + return vs.lineHeight; + + case SCI_SETENDATLASTLINE: + PLATFORM_ASSERT((wParam == 0) || (wParam == 1)); + if (endAtLastLine != (wParam != 0)) { + endAtLastLine = wParam != 0; + SetScrollBars(); + } + break; + + case SCI_GETENDATLASTLINE: + return endAtLastLine; + + case SCI_SETCARETSTICKY: + PLATFORM_ASSERT((wParam == 0) || (wParam == 1)); + if (caretSticky != (wParam != 0)) { + caretSticky = wParam != 0; + } + break; + + case SCI_GETCARETSTICKY: + return caretSticky; + + case SCI_TOGGLECARETSTICKY: + caretSticky = !caretSticky; + break; + + case SCI_GETCOLUMN: + return pdoc->GetColumn(wParam); + + case SCI_FINDCOLUMN: + return pdoc->FindColumn(wParam, lParam); + + case SCI_SETHSCROLLBAR : + if (horizontalScrollBarVisible != (wParam != 0)) { + horizontalScrollBarVisible = wParam != 0; + SetScrollBars(); + ReconfigureScrollBars(); + } + break; + + case SCI_GETHSCROLLBAR: + return horizontalScrollBarVisible; + + case SCI_SETVSCROLLBAR: + if (verticalScrollBarVisible != (wParam != 0)) { + verticalScrollBarVisible = wParam != 0; + SetScrollBars(); + ReconfigureScrollBars(); + } + break; + + case SCI_GETVSCROLLBAR: + return verticalScrollBarVisible; + + case SCI_SETINDENTATIONGUIDES: + vs.viewIndentationGuides = wParam != 0; + Redraw(); + break; + + case SCI_GETINDENTATIONGUIDES: + return vs.viewIndentationGuides; + + case SCI_SETHIGHLIGHTGUIDE: + if ((highlightGuideColumn != static_cast(wParam)) || (wParam > 0)) { + highlightGuideColumn = wParam; + Redraw(); + } + break; + + case SCI_GETHIGHLIGHTGUIDE: + return highlightGuideColumn; + + case SCI_GETLINEENDPOSITION: + return pdoc->LineEnd(wParam); + + case SCI_SETCODEPAGE: + if (ValidCodePage(wParam)) { + pdoc->dbcsCodePage = wParam; + InvalidateStyleRedraw(); + } + break; + + case SCI_GETCODEPAGE: + return pdoc->dbcsCodePage; + + case SCI_SETUSEPALETTE: + palette.allowRealization = wParam != 0; + InvalidateStyleRedraw(); + break; + + case SCI_GETUSEPALETTE: + return palette.allowRealization; + + // Marker definition and setting + case SCI_MARKERDEFINE: + if (wParam <= MARKER_MAX) + vs.markers[wParam].markType = lParam; + InvalidateStyleData(); + RedrawSelMargin(); + break; + case SCI_MARKERSETFORE: + if (wParam <= MARKER_MAX) + vs.markers[wParam].fore.desired = ColourDesired(lParam); + InvalidateStyleData(); + RedrawSelMargin(); + break; + case SCI_MARKERSETBACK: + if (wParam <= MARKER_MAX) + vs.markers[wParam].back.desired = ColourDesired(lParam); + InvalidateStyleData(); + RedrawSelMargin(); + break; + case SCI_MARKERSETALPHA: + if (wParam <= MARKER_MAX) + vs.markers[wParam].alpha = lParam; + InvalidateStyleRedraw(); + break; + case SCI_MARKERADD: { + int markerID = pdoc->AddMark(wParam, lParam); + return markerID; + } + case SCI_MARKERADDSET: + if (lParam != 0) + pdoc->AddMarkSet(wParam, lParam); + break; + + case SCI_MARKERDELETE: + pdoc->DeleteMark(wParam, lParam); + break; + + case SCI_MARKERDELETEALL: + pdoc->DeleteAllMarks(static_cast(wParam)); + break; + + case SCI_MARKERGET: + return pdoc->GetMark(wParam); + + case SCI_MARKERNEXT: { + int lt = pdoc->LinesTotal(); + for (int iLine = wParam; iLine < lt; iLine++) { + if ((pdoc->GetMark(iLine) & lParam) != 0) + return iLine; + } + } + return -1; + + case SCI_MARKERPREVIOUS: { + for (int iLine = wParam; iLine >= 0; iLine--) { + if ((pdoc->GetMark(iLine) & lParam) != 0) + return iLine; + } + } + return -1; + + case SCI_MARKERDEFINEPIXMAP: + if (wParam <= MARKER_MAX) { + vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam)); + }; + InvalidateStyleData(); + RedrawSelMargin(); + break; + + case SCI_SETMARGINTYPEN: + if (ValidMargin(wParam)) { + vs.ms[wParam].style = lParam; + InvalidateStyleRedraw(); + } + break; + + case SCI_GETMARGINTYPEN: + if (ValidMargin(wParam)) + return vs.ms[wParam].style; + else + return 0; + + case SCI_SETMARGINWIDTHN: + if (ValidMargin(wParam)) { + // Short-circuit if the width is unchanged, to avoid unnecessary redraw. + if (vs.ms[wParam].width != lParam) { + vs.ms[wParam].width = lParam; + InvalidateStyleRedraw(); + } + } + break; + + case SCI_GETMARGINWIDTHN: + if (ValidMargin(wParam)) + return vs.ms[wParam].width; + else + return 0; + + case SCI_SETMARGINMASKN: + if (ValidMargin(wParam)) { + vs.ms[wParam].mask = lParam; + InvalidateStyleRedraw(); + } + break; + + case SCI_GETMARGINMASKN: + if (ValidMargin(wParam)) + return vs.ms[wParam].mask; + else + return 0; + + case SCI_SETMARGINSENSITIVEN: + if (ValidMargin(wParam)) { + vs.ms[wParam].sensitive = lParam != 0; + InvalidateStyleRedraw(); + } + break; + + case SCI_GETMARGINSENSITIVEN: + if (ValidMargin(wParam)) + return vs.ms[wParam].sensitive ? 1 : 0; + else + return 0; + + case SCI_STYLECLEARALL: + vs.ClearStyles(); + InvalidateStyleRedraw(); + break; + + case SCI_STYLESETFORE: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].fore.desired = ColourDesired(lParam); + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETBACK: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].back.desired = ColourDesired(lParam); + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETBOLD: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].bold = lParam != 0; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETITALIC: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].italic = lParam != 0; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETEOLFILLED: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].eolFilled = lParam != 0; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETSIZE: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].size = lParam; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETFONT: + if (lParam == 0) + return 0; + if (wParam <= STYLE_MAX) { + vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam)); + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETUNDERLINE: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].underline = lParam != 0; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETCASE: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].caseForce = static_cast(lParam); + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETCHARACTERSET: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].characterSet = lParam; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETVISIBLE: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].visible = lParam != 0; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETCHANGEABLE: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].changeable = lParam != 0; + InvalidateStyleRedraw(); + } + break; + case SCI_STYLESETHOTSPOT: + if (wParam <= STYLE_MAX) { + vs.styles[wParam].hotspot = lParam != 0; + InvalidateStyleRedraw(); + } + break; + + case SCI_STYLERESETDEFAULT: + vs.ResetDefaultStyle(); + InvalidateStyleRedraw(); + break; + case SCI_SETSTYLEBITS: + pdoc->SetStylingBits(wParam); + break; + + case SCI_GETSTYLEBITS: + return pdoc->stylingBits; + + case SCI_SETLINESTATE: + return pdoc->SetLineState(wParam, lParam); + + case SCI_GETLINESTATE: + return pdoc->GetLineState(wParam); + + case SCI_GETMAXLINESTATE: + return pdoc->GetMaxLineState(); + + case SCI_GETCARETLINEVISIBLE: + return vs.showCaretLineBackground; + case SCI_SETCARETLINEVISIBLE: + vs.showCaretLineBackground = wParam != 0; + InvalidateStyleRedraw(); + break; + case SCI_GETCARETLINEBACK: + return vs.caretLineBackground.desired.AsLong(); + case SCI_SETCARETLINEBACK: + vs.caretLineBackground.desired = wParam; + InvalidateStyleRedraw(); + break; + case SCI_GETCARETLINEBACKALPHA: + return vs.caretLineAlpha; + case SCI_SETCARETLINEBACKALPHA: + vs.caretLineAlpha = wParam; + InvalidateStyleRedraw(); + break; + + // Folding messages + + case SCI_VISIBLEFROMDOCLINE: + return cs.DisplayFromDoc(wParam); + + case SCI_DOCLINEFROMVISIBLE: + return cs.DocFromDisplay(wParam); + + case SCI_WRAPCOUNT: + return WrapCount(wParam); + + case SCI_SETFOLDLEVEL: { + int prev = pdoc->SetLevel(wParam, lParam); + if (prev != lParam) + RedrawSelMargin(); + return prev; + } + + case SCI_GETFOLDLEVEL: + return pdoc->GetLevel(wParam); + + case SCI_GETLASTCHILD: + return pdoc->GetLastChild(wParam, lParam); + + case SCI_GETFOLDPARENT: + return pdoc->GetFoldParent(wParam); + + case SCI_SHOWLINES: + cs.SetVisible(wParam, lParam, true); + SetScrollBars(); + Redraw(); + break; + + case SCI_HIDELINES: + cs.SetVisible(wParam, lParam, false); + SetScrollBars(); + Redraw(); + break; + + case SCI_GETLINEVISIBLE: + return cs.GetVisible(wParam); + + case SCI_SETFOLDEXPANDED: + if (cs.SetExpanded(wParam, lParam != 0)) { + RedrawSelMargin(); + } + break; + + case SCI_GETFOLDEXPANDED: + return cs.GetExpanded(wParam); + + case SCI_SETFOLDFLAGS: + foldFlags = wParam; + Redraw(); + break; + + case SCI_TOGGLEFOLD: + ToggleContraction(wParam); + break; + + case SCI_ENSUREVISIBLE: + EnsureLineVisible(wParam, false); + break; + + case SCI_ENSUREVISIBLEENFORCEPOLICY: + EnsureLineVisible(wParam, true); + break; + + case SCI_SEARCHANCHOR: + SearchAnchor(); + break; + + case SCI_SEARCHNEXT: + case SCI_SEARCHPREV: + return SearchText(iMessage, wParam, lParam); + +#ifdef INCLUDE_DEPRECATED_FEATURES + case SCI_SETCARETPOLICY: // Deprecated + caretXPolicy = caretYPolicy = wParam; + caretXSlop = caretYSlop = lParam; + break; +#endif + + case SCI_SETXCARETPOLICY: + caretXPolicy = wParam; + caretXSlop = lParam; + break; + + case SCI_SETYCARETPOLICY: + caretYPolicy = wParam; + caretYSlop = lParam; + break; + + case SCI_SETVISIBLEPOLICY: + visiblePolicy = wParam; + visibleSlop = lParam; + break; + + case SCI_LINESONSCREEN: + return LinesOnScreen(); + + case SCI_SETSELFORE: + vs.selforeset = wParam != 0; + vs.selforeground.desired = ColourDesired(lParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETSELBACK: + vs.selbackset = wParam != 0; + vs.selbackground.desired = ColourDesired(lParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETSELALPHA: + vs.selAlpha = wParam; + InvalidateStyleRedraw(); + break; + + case SCI_GETSELALPHA: + return vs.selAlpha; + + case SCI_SETWHITESPACEFORE: + vs.whitespaceForegroundSet = wParam != 0; + vs.whitespaceForeground.desired = ColourDesired(lParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETWHITESPACEBACK: + vs.whitespaceBackgroundSet = wParam != 0; + vs.whitespaceBackground.desired = ColourDesired(lParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETCARETFORE: + vs.caretcolour.desired = ColourDesired(wParam); + InvalidateStyleRedraw(); + break; + + case SCI_GETCARETFORE: + return vs.caretcolour.desired.AsLong(); + + case SCI_SETCARETWIDTH: + if (wParam <= 0) + vs.caretWidth = 0; + else if (wParam >= 3) + vs.caretWidth = 3; + else + vs.caretWidth = wParam; + InvalidateStyleRedraw(); + break; + + case SCI_GETCARETWIDTH: + return vs.caretWidth; + + case SCI_ASSIGNCMDKEY: + kmap.AssignCmdKey(Platform::LowShortFromLong(wParam), + Platform::HighShortFromLong(wParam), lParam); + break; + + case SCI_CLEARCMDKEY: + kmap.AssignCmdKey(Platform::LowShortFromLong(wParam), + Platform::HighShortFromLong(wParam), SCI_NULL); + break; + + case SCI_CLEARALLCMDKEYS: + kmap.Clear(); + break; + + case SCI_INDICSETSTYLE: + if (wParam <= INDIC_MAX) { + vs.indicators[wParam].style = lParam; + InvalidateStyleRedraw(); + } + break; + + case SCI_INDICGETSTYLE: + return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0; + + case SCI_INDICSETFORE: + if (wParam <= INDIC_MAX) { + vs.indicators[wParam].fore.desired = ColourDesired(lParam); + InvalidateStyleRedraw(); + } + break; + + case SCI_INDICGETFORE: + return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0; + + case SCI_LINEDOWN: + case SCI_LINEDOWNEXTEND: + case SCI_PARADOWN: + case SCI_PARADOWNEXTEND: + case SCI_LINEUP: + case SCI_LINEUPEXTEND: + case SCI_PARAUP: + case SCI_PARAUPEXTEND: + case SCI_CHARLEFT: + case SCI_CHARLEFTEXTEND: + case SCI_CHARRIGHT: + case SCI_CHARRIGHTEXTEND: + case SCI_WORDLEFT: + case SCI_WORDLEFTEXTEND: + case SCI_WORDRIGHT: + case SCI_WORDRIGHTEXTEND: + case SCI_WORDLEFTEND: + case SCI_WORDLEFTENDEXTEND: + case SCI_WORDRIGHTEND: + case SCI_WORDRIGHTENDEXTEND: + case SCI_HOME: + case SCI_HOMEEXTEND: + case SCI_LINEEND: + case SCI_LINEENDEXTEND: + case SCI_HOMEWRAP: + case SCI_HOMEWRAPEXTEND: + case SCI_LINEENDWRAP: + case SCI_LINEENDWRAPEXTEND: + case SCI_DOCUMENTSTART: + case SCI_DOCUMENTSTARTEXTEND: + case SCI_DOCUMENTEND: + case SCI_DOCUMENTENDEXTEND: + + case SCI_STUTTEREDPAGEUP: + case SCI_STUTTEREDPAGEUPEXTEND: + case SCI_STUTTEREDPAGEDOWN: + case SCI_STUTTEREDPAGEDOWNEXTEND: + + case SCI_PAGEUP: + case SCI_PAGEUPEXTEND: + case SCI_PAGEDOWN: + case SCI_PAGEDOWNEXTEND: + case SCI_EDITTOGGLEOVERTYPE: + case SCI_CANCEL: + case SCI_DELETEBACK: + case SCI_TAB: + case SCI_BACKTAB: + case SCI_NEWLINE: + case SCI_FORMFEED: + case SCI_VCHOME: + case SCI_VCHOMEEXTEND: + case SCI_VCHOMEWRAP: + case SCI_VCHOMEWRAPEXTEND: + case SCI_ZOOMIN: + case SCI_ZOOMOUT: + case SCI_DELWORDLEFT: + case SCI_DELWORDRIGHT: + case SCI_DELLINELEFT: + case SCI_DELLINERIGHT: + case SCI_LINECOPY: + case SCI_LINECUT: + case SCI_LINEDELETE: + case SCI_LINETRANSPOSE: + case SCI_LINEDUPLICATE: + case SCI_LOWERCASE: + case SCI_UPPERCASE: + case SCI_LINESCROLLDOWN: + case SCI_LINESCROLLUP: + case SCI_WORDPARTLEFT: + case SCI_WORDPARTLEFTEXTEND: + case SCI_WORDPARTRIGHT: + case SCI_WORDPARTRIGHTEXTEND: + case SCI_DELETEBACKNOTLINE: + case SCI_HOMEDISPLAY: + case SCI_HOMEDISPLAYEXTEND: + case SCI_LINEENDDISPLAY: + case SCI_LINEENDDISPLAYEXTEND: + case SCI_LINEDOWNRECTEXTEND: + case SCI_LINEUPRECTEXTEND: + case SCI_CHARLEFTRECTEXTEND: + case SCI_CHARRIGHTRECTEXTEND: + case SCI_HOMERECTEXTEND: + case SCI_VCHOMERECTEXTEND: + case SCI_LINEENDRECTEXTEND: + case SCI_PAGEUPRECTEXTEND: + case SCI_PAGEDOWNRECTEXTEND: + case SCI_SELECTIONDUPLICATE: + return KeyCommand(iMessage); + + case SCI_BRACEHIGHLIGHT: + SetBraceHighlight(static_cast(wParam), lParam, STYLE_BRACELIGHT); + break; + + case SCI_BRACEBADLIGHT: + SetBraceHighlight(static_cast(wParam), -1, STYLE_BRACEBAD); + break; + + case SCI_BRACEMATCH: + // wParam is position of char to find brace for, + // lParam is maximum amount of text to restyle to find it + return pdoc->BraceMatch(wParam, lParam); + + case SCI_GETVIEWEOL: + return vs.viewEOL; + + case SCI_SETVIEWEOL: + vs.viewEOL = wParam != 0; + InvalidateStyleRedraw(); + break; + + case SCI_SETZOOM: + vs.zoomLevel = wParam; + InvalidateStyleRedraw(); + NotifyZoom(); + break; + + case SCI_GETZOOM: + return vs.zoomLevel; + + case SCI_GETEDGECOLUMN: + return theEdge; + + case SCI_SETEDGECOLUMN: + theEdge = wParam; + InvalidateStyleRedraw(); + break; + + case SCI_GETEDGEMODE: + return vs.edgeState; + + case SCI_SETEDGEMODE: + vs.edgeState = wParam; + InvalidateStyleRedraw(); + break; + + case SCI_GETEDGECOLOUR: + return vs.edgecolour.desired.AsLong(); + + case SCI_SETEDGECOLOUR: + vs.edgecolour.desired = ColourDesired(wParam); + InvalidateStyleRedraw(); + break; + + case SCI_GETDOCPOINTER: + return reinterpret_cast(pdoc); + + case SCI_SETDOCPOINTER: + CancelModes(); + SetDocPointer(reinterpret_cast(lParam)); + return 0; + + case SCI_CREATEDOCUMENT: { + Document *doc = new Document(); + if (doc) { + doc->AddRef(); + } + return reinterpret_cast(doc); + } + + case SCI_ADDREFDOCUMENT: + (reinterpret_cast(lParam))->AddRef(); + break; + + case SCI_RELEASEDOCUMENT: + (reinterpret_cast(lParam))->Release(); + break; + + case SCI_SETMODEVENTMASK: + modEventMask = wParam; + return 0; + + case SCI_GETMODEVENTMASK: + return modEventMask; + + case SCI_CONVERTEOLS: + pdoc->ConvertLineEnds(wParam); + SetSelection(currentPos, anchor); // Ensure selection inside document + return 0; + + case SCI_SETLENGTHFORENCODE: + lengthForEncode = wParam; + return 0; + + case SCI_SELECTIONISRECTANGLE: + return selType == selRectangle ? 1 : 0; + + case SCI_SETSELECTIONMODE: { + switch (wParam) { + case SC_SEL_STREAM: + moveExtendsSelection = !moveExtendsSelection || (selType != selStream); + selType = selStream; + break; + case SC_SEL_RECTANGLE: + moveExtendsSelection = !moveExtendsSelection || (selType != selRectangle); + selType = selRectangle; + break; + case SC_SEL_LINES: + moveExtendsSelection = !moveExtendsSelection || (selType != selLines); + selType = selLines; + break; + default: + moveExtendsSelection = !moveExtendsSelection || (selType != selStream); + selType = selStream; + } + InvalidateSelection(currentPos, anchor); + } + case SCI_GETSELECTIONMODE: + switch (selType) { + case selStream: + return SC_SEL_STREAM; + case selRectangle: + return SC_SEL_RECTANGLE; + case selLines: + return SC_SEL_LINES; + default: // ?! + return SC_SEL_STREAM; + } + case SCI_GETLINESELSTARTPOSITION: { + SelectionLineIterator lineIterator(this); + lineIterator.SetAt(wParam); + return lineIterator.startPos; + } + case SCI_GETLINESELENDPOSITION: { + SelectionLineIterator lineIterator(this); + lineIterator.SetAt(wParam); + return lineIterator.endPos; + } + + case SCI_SETOVERTYPE: + inOverstrike = wParam != 0; + break; + + case SCI_GETOVERTYPE: + return inOverstrike ? 1 : 0; + + case SCI_SETFOCUS: + SetFocusState(wParam != 0); + break; + + case SCI_GETFOCUS: + return hasFocus; + + case SCI_SETSTATUS: + errorStatus = wParam; + break; + + case SCI_GETSTATUS: + return errorStatus; + + case SCI_SETMOUSEDOWNCAPTURES: + mouseDownCaptures = wParam != 0; + break; + + case SCI_GETMOUSEDOWNCAPTURES: + return mouseDownCaptures; + + case SCI_SETCURSOR: + cursorMode = wParam; + DisplayCursor(Window::cursorText); + break; + + case SCI_GETCURSOR: + return cursorMode; + + case SCI_SETCONTROLCHARSYMBOL: + controlCharSymbol = wParam; + break; + + case SCI_GETCONTROLCHARSYMBOL: + return controlCharSymbol; + + case SCI_STARTRECORD: + recordingMacro = true; + return 0; + + case SCI_STOPRECORD: + recordingMacro = false; + return 0; + + case SCI_MOVECARETINSIDEVIEW: + MoveCaretInsideView(); + break; + + case SCI_SETFOLDMARGINCOLOUR: + vs.foldmarginColourSet = wParam != 0; + vs.foldmarginColour.desired = ColourDesired(lParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETFOLDMARGINHICOLOUR: + vs.foldmarginHighlightColourSet = wParam != 0; + vs.foldmarginHighlightColour.desired = ColourDesired(lParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETHOTSPOTACTIVEFORE: + vs.hotspotForegroundSet = wParam != 0; + vs.hotspotForeground.desired = ColourDesired(lParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETHOTSPOTACTIVEBACK: + vs.hotspotBackgroundSet = wParam != 0; + vs.hotspotBackground.desired = ColourDesired(lParam); + InvalidateStyleRedraw(); + break; + + case SCI_SETHOTSPOTACTIVEUNDERLINE: + vs.hotspotUnderline = wParam != 0; + InvalidateStyleRedraw(); + break; + + case SCI_SETHOTSPOTSINGLELINE: + vs.hotspotSingleLine = wParam != 0; + InvalidateStyleRedraw(); + break; + + case SCI_SETPASTECONVERTENDINGS: + convertPastes = wParam != 0; + break; + + case SCI_GETPASTECONVERTENDINGS: + return convertPastes ? 1 : 0; + + default: + return DefWndProc(iMessage, wParam, lParam); + } + //Platform::DebugPrintf("end wnd proc\n"); + return 0l; +} diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Editor.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Editor.h new file mode 100644 index 00000000..23b7f921 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Editor.h @@ -0,0 +1,582 @@ +// Scintilla source code edit control +/** @file Editor.h + ** Defines the main editor class. + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef EDITOR_H +#define EDITOR_H + +/** + */ +class Caret { +public: + bool active; + bool on; + int period; + + Caret(); +}; + +/** + */ +class Timer { +public: + bool ticking; + int ticksToWait; + enum {tickSize = 100}; + TickerID tickerID; + + Timer(); +}; + +/** + */ +class Idler { +public: + bool state; + IdlerID idlerID; + + Idler(); +}; + +/** + */ +class LineLayout { +private: + friend class LineLayoutCache; + int *lineStarts; + int lenLineStarts; + /// Drawing is only performed for @a maxLineLength characters on each line. + int lineNumber; + bool inCache; +public: + enum { wrapWidthInfinite = 0x7ffffff }; + int maxLineLength; + int numCharsInLine; + enum validLevel { llInvalid, llCheckTextAndStyle, llPositions, llLines } validity; + int xHighlightGuide; + bool highlightColumn; + int selStart; + int selEnd; + bool containsCaret; + int edgeColumn; + char *chars; + unsigned char *styles; + int styleBitsSet; + char *indicators; + int *positions; + char bracePreviousStyles[2]; + + // Hotspot support + int hsStart; + int hsEnd; + + // Wrapped line support + int widthLine; + int lines; + + LineLayout(int maxLineLength_); + virtual ~LineLayout(); + void Resize(int maxLineLength_); + void Free(); + void Invalidate(validLevel validity_); + int LineStart(int line) { + if (line <= 0) { + return 0; + } else if ((line >= lines) || !lineStarts) { + return numCharsInLine; + } else { + return lineStarts[line]; + } + } + void SetLineStart(int line, int start); + void SetBracesHighlight(Range rangeLine, Position braces[], + char bracesMatchStyle, int xHighlight); + void RestoreBracesHighlight(Range rangeLine, Position braces[]); +}; + +/** + */ +class LineLayoutCache { + int level; + int length; + int size; + LineLayout **cache; + bool allInvalidated; + int styleClock; + int useCount; + void Allocate(int length_); + void AllocateForLevel(int linesOnScreen, int linesInDoc); +public: + LineLayoutCache(); + virtual ~LineLayoutCache(); + void Deallocate(); + enum { + llcNone=SC_CACHE_NONE, + llcCaret=SC_CACHE_CARET, + llcPage=SC_CACHE_PAGE, + llcDocument=SC_CACHE_DOCUMENT + }; + void Invalidate(LineLayout::validLevel validity_); + void SetLevel(int level_); + int GetLevel() { return level; } + LineLayout *Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_, + int linesOnScreen, int linesInDoc); + void Dispose(LineLayout *ll); +}; + +/** + * Hold a piece of text selected for copying or dragging. + * The text is expected to hold a terminating '\0' and this is counted in len. + */ +class SelectionText { +public: + char *s; + int len; + bool rectangular; + int codePage; + int characterSet; + SelectionText() : s(0), len(0), rectangular(false), codePage(0), characterSet(0) {} + ~SelectionText() { + Free(); + } + void Free() { + Set(0, 0, 0, 0, false); + } + void Set(char *s_, int len_, int codePage_, int characterSet_, bool rectangular_) { + delete []s; + s = s_; + if (s) + len = len_; + else + len = 0; + codePage = codePage_; + characterSet = characterSet_; + rectangular = rectangular_; + } + void Copy(const char *s_, int len_, int codePage_, int characterSet_, bool rectangular_) { + delete []s; + s = new char[len_]; + if (s) { + len = len_; + for (int i = 0; i < len_; i++) { + s[i] = s_[i]; + } + } else { + len = 0; + } + codePage = codePage_; + characterSet = characterSet_; + rectangular = rectangular_; + } + void Copy(const SelectionText &other) { + Copy(other.s, other.len, other.codePage, other.characterSet, other.rectangular); + } +}; + +/** + */ +class Editor : public DocWatcher { + // Private so Editor objects can not be copied + Editor(const Editor &) : DocWatcher() {} + Editor &operator=(const Editor &) { return *this; } + +protected: // ScintillaBase subclass needs access to much of Editor + + /** On GTK+, Scintilla is a container widget holding two scroll bars + * whereas on Windows there is just one window with both scroll bars turned on. */ + Window wMain; ///< The Scintilla parent window + + /** Style resources may be expensive to allocate so are cached between uses. + * When a style attribute is changed, this cache is flushed. */ + bool stylesValid; + ViewStyle vs; + Palette palette; + + int printMagnification; + int printColourMode; + int printWrapState; + int cursorMode; + int controlCharSymbol; + + bool hasFocus; + bool hideSelection; + bool inOverstrike; + int errorStatus; + bool mouseDownCaptures; + + /** In bufferedDraw mode, graphics operations are drawn to a pixmap and then copied to + * the screen. This avoids flashing but is about 30% slower. */ + bool bufferedDraw; + /** In twoPhaseDraw mode, drawing is performed in two phases, first the background + * and then the foreground. This avoids chopping off characters that overlap the next run. */ + bool twoPhaseDraw; + + int xOffset; ///< Horizontal scrolled amount in pixels + int xCaretMargin; ///< Ensure this many pixels visible on both sides of caret + bool horizontalScrollBarVisible; + int scrollWidth; + bool verticalScrollBarVisible; + bool endAtLastLine; + bool caretSticky; + + Surface *pixmapLine; + Surface *pixmapSelMargin; + Surface *pixmapSelPattern; + Surface *pixmapIndentGuide; + Surface *pixmapIndentGuideHighlight; + + LineLayoutCache llc; + + KeyMap kmap; + + Caret caret; + Timer timer; + Timer autoScrollTimer; + enum { autoScrollDelay = 200 }; + + Idler idler; + + Point lastClick; + unsigned int lastClickTime; + int dwellDelay; + int ticksToDwell; + bool dwelling; + enum { selChar, selWord, selLine } selectionType; + Point ptMouseLast; + bool inDragDrop; + bool dropWentOutside; + int posDrag; + int posDrop; + int lastXChosen; + int lineAnchor; + int originalAnchorPos; + int currentPos; + int anchor; + int targetStart; + int targetEnd; + int searchFlags; + int topLine; + int posTopLine; + int lengthForEncode; + + bool needUpdateUI; + Position braces[2]; + int bracesMatchStyle; + int highlightGuideColumn; + + int theEdge; + + enum { notPainting, painting, paintAbandoned } paintState; + PRectangle rcPaint; + bool paintingAllText; + + int modEventMask; + + SelectionText drag; + enum selTypes { noSel, selStream, selRectangle, selLines }; + selTypes selType; + bool moveExtendsSelection; + int xStartSelect; ///< x position of start of rectangular selection + int xEndSelect; ///< x position of end of rectangular selection + bool primarySelection; + + int caretXPolicy; + int caretXSlop; ///< Ensure this many pixels visible on both sides of caret + + int caretYPolicy; + int caretYSlop; ///< Ensure this many lines visible on both sides of caret + + int visiblePolicy; + int visibleSlop; + + int searchAnchor; + + bool recordingMacro; + + int foldFlags; + ContractionState cs; + + // Hotspot support + int hsStart; + int hsEnd; + + // Wrapping support + enum { eWrapNone, eWrapWord, eWrapChar } wrapState; + enum { wrapLineLarge = 0x7ffffff }; + int wrapWidth; + int wrapStart; + int wrapEnd; + int wrapVisualFlags; + int wrapVisualFlagsLocation; + int wrapVisualStartIndent; + int actualWrapVisualStartIndent; + + bool convertPastes; + + Document *pdoc; + + Editor(); + virtual ~Editor(); + virtual void Initialise() = 0; + virtual void Finalise(); + + void InvalidateStyleData(); + void InvalidateStyleRedraw(); + virtual void RefreshColourPalette(Palette &pal, bool want); + void RefreshStyleData(); + void DropGraphics(); + + virtual PRectangle GetClientRectangle(); + PRectangle GetTextRectangle(); + + int LinesOnScreen(); + int LinesToScroll(); + int MaxScrollPos(); + Point LocationFromPosition(int pos); + int XFromPosition(int pos); + int PositionFromLocation(Point pt); + int PositionFromLocationClose(Point pt); + int PositionFromLineX(int line, int x); + int LineFromLocation(Point pt); + void SetTopLine(int topLineNew); + + bool AbandonPaint(); + void RedrawRect(PRectangle rc); + void Redraw(); + void RedrawSelMargin(int line=-1); + PRectangle RectangleFromRange(int start, int end); + void InvalidateRange(int start, int end); + + int CurrentPosition(); + bool SelectionEmpty(); + int SelectionStart(); + int SelectionEnd(); + void SetRectangularRange(); + void InvalidateSelection(int currentPos_, int anchor_); + void SetSelection(int currentPos_, int anchor_); + void SetSelection(int currentPos_); + void SetEmptySelection(int currentPos_); + bool RangeContainsProtected(int start, int end) const; + bool SelectionContainsProtected(); + int MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd=true); + int MovePositionTo(int newPos, selTypes sel=noSel, bool ensureVisible=true); + int MovePositionSoVisible(int pos, int moveDir); + void SetLastXChosen(); + + void ScrollTo(int line, bool moveThumb=true); + virtual void ScrollText(int linesToMove); + void HorizontalScrollTo(int xPos); + void MoveCaretInsideView(bool ensureVisible=true); + int DisplayFromPosition(int pos); + void EnsureCaretVisible(bool useMargin=true, bool vert=true, bool horiz=true); + void ShowCaretAtCurrentPosition(); + void DropCaret(); + void InvalidateCaret(); + virtual void UpdateSystemCaret(); + + void NeedWrapping(int docLineStart = 0, int docLineEnd = wrapLineLarge); + bool WrapLines(bool fullWrap, int priorityWrapLineStart); + void LinesJoin(); + void LinesSplit(int pixelWidth); + + int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault); + void PaintSelMargin(Surface *surface, PRectangle &rc); + LineLayout *RetrieveLineLayout(int lineNumber); + void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, + int width=LineLayout::wrapWidthInfinite); + ColourAllocated SelectionBackground(ViewStyle &vsDraw); + ColourAllocated TextBackground(ViewStyle &vsDraw, bool overrideBackground, ColourAllocated background, bool inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll); + void DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight); + void DrawWrapMarker(Surface *surface, PRectangle rcPlace, bool isEndMarker, ColourAllocated wrapColour); + void DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll, + int line, int lineEnd, int xStart, int subLine, int subLineStart, + bool overrideBackground, ColourAllocated background, + bool drawWrapMark, ColourAllocated wrapColour); + void DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart, + PRectangle rcLine, LineLayout *ll, int subLine=0); + void RefreshPixMaps(Surface *surfaceWindow); + void Paint(Surface *surfaceWindow, PRectangle rcArea); + long FormatRange(bool draw, RangeToFormat *pfr); + int TextWidth(int style, const char *text); + + virtual void SetVerticalScrollPos() = 0; + virtual void SetHorizontalScrollPos() = 0; + virtual bool ModifyScrollBars(int nMax, int nPage) = 0; + virtual void ReconfigureScrollBars(); + void SetScrollBars(); + void ChangeSize(); + + void AddChar(char ch); + virtual void AddCharUTF(char *s, unsigned int len, bool treatAsDBCS=false); + void ClearSelection(); + void ClearAll(); + void ClearDocumentStyle(); + void Cut(); + void PasteRectangular(int pos, const char *ptr, int len); + virtual void Copy() = 0; + virtual bool CanPaste(); + virtual void Paste() = 0; + void Clear(); + void SelectAll(); + void Undo(); + void Redo(); + void DelChar(); + void DelCharBack(bool allowLineStartDeletion); + virtual void ClaimSelection() = 0; + + virtual void NotifyChange() = 0; + virtual void NotifyFocus(bool focus); + virtual int GetCtrlID() { return ctrlID; } + virtual void NotifyParent(SCNotification scn) = 0; + virtual void NotifyStyleToNeeded(int endStyleNeeded); + void NotifyChar(int ch); + void NotifyMove(int position); + void NotifySavePoint(bool isSavePoint); + void NotifyModifyAttempt(); + virtual void NotifyDoubleClick(Point pt, bool shift); + void NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt); + void NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt); + void NotifyUpdateUI(); + void NotifyPainted(); + bool NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt); + void NotifyNeedShown(int pos, int len); + void NotifyDwelling(Point pt, bool state); + void NotifyZoom(); + + void NotifyModifyAttempt(Document *document, void *userData); + void NotifySavePoint(Document *document, void *userData, bool atSavePoint); + void CheckModificationForWrap(DocModification mh); + void NotifyModified(Document *document, DocModification mh, void *userData); + void NotifyDeleted(Document *document, void *userData); + void NotifyStyleNeeded(Document *doc, void *userData, int endPos); + void NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam); + + void PageMove(int direction, selTypes sel=noSel, bool stuttered = false); + void ChangeCaseOfSelection(bool makeUpperCase); + void LineTranspose(); + void Duplicate(bool forLine); + virtual void CancelModes(); + void NewLine(); + void CursorUpOrDown(int direction, selTypes sel=noSel); + void ParaUpOrDown(int direction, selTypes sel=noSel); + int StartEndDisplayLine(int pos, bool start); + virtual int KeyCommand(unsigned int iMessage); + virtual int KeyDefault(int /* key */, int /*modifiers*/); + int KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed=0); + + int GetWhitespaceVisible(); + void SetWhitespaceVisible(int view); + + void Indent(bool forwards); + + long FindText(uptr_t wParam, sptr_t lParam); + void SearchAnchor(); + long SearchText(unsigned int iMessage, uptr_t wParam, sptr_t lParam); + long SearchInTarget(const char *text, int length); + void GoToLine(int lineNo); + + virtual void CopyToClipboard(const SelectionText &selectedText) = 0; + char *CopyRange(int start, int end); + void CopySelectionFromRange(SelectionText *ss, int start, int end); + void CopySelectionRange(SelectionText *ss); + void CopyRangeToClipboard(int start, int end); + void CopyText(int length, const char *text); + void SetDragPosition(int newPos); + virtual void DisplayCursor(Window::Cursor c); + virtual void StartDrag(); + void DropAt(int position, const char *value, bool moving, bool rectangular); + /** PositionInSelection returns 0 if position in selection, -1 if position before selection, and 1 if after. + * Before means either before any line of selection or before selection on its line, with a similar meaning to after. */ + int PositionInSelection(int pos); + bool PointInSelection(Point pt); + bool PointInSelMargin(Point pt); + void LineSelection(int lineCurrent_, int lineAnchor_); + void DwellEnd(bool mouseMoved); + virtual void ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt); + void ButtonMove(Point pt); + void ButtonUp(Point pt, unsigned int curTime, bool ctrl); + + void Tick(); + bool Idle(); + virtual void SetTicking(bool on) = 0; + virtual bool SetIdle(bool) { return false; } + virtual void SetMouseCapture(bool on) = 0; + virtual bool HaveMouseCapture() = 0; + void SetFocusState(bool focusState); + + virtual bool PaintContains(PRectangle rc); + bool PaintContainsMargin(); + void CheckForChangeOutsidePaint(Range r); + void SetBraceHighlight(Position pos0, Position pos1, int matchStyle); + + void SetDocPointer(Document *document); + + void Expand(int &line, bool doExpand); + void ToggleContraction(int line); + void EnsureLineVisible(int lineDoc, bool enforcePolicy); + int ReplaceTarget(bool replacePatterns, const char *text, int length=-1); + + bool PositionIsHotspot(int position); + bool PointIsHotspot(Point pt); + void SetHotSpotRange(Point *pt); + void GetHotSpotRange(int& hsStart, int& hsEnd); + + int CodePage() const; + virtual bool ValidCodePage(int /* codePage */) const { return true; } + int WrapCount(int line); + + virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) = 0; + +public: + // Public so the COM thunks can access it. + bool IsUnicodeMode() const; + // Public so scintilla_send_message can use it. + virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam); + // Public so scintilla_set_id can use it. + int ctrlID; + friend class AutoSurface; + friend class SelectionLineIterator; +}; + +/** + * A smart pointer class to ensure Surfaces are set up and deleted correctly. + */ +class AutoSurface { +private: + Surface *surf; +public: + AutoSurface(Editor *ed) : surf(0) { + if (ed->wMain.GetID()) { + surf = Surface::Allocate(); + if (surf) { + surf->Init(ed->wMain.GetID()); + surf->SetUnicodeMode(SC_CP_UTF8 == ed->CodePage()); + surf->SetDBCSMode(ed->CodePage()); + } + } + } + AutoSurface(SurfaceID sid, Editor *ed) : surf(0) { + if (ed->wMain.GetID()) { + surf = Surface::Allocate(); + if (surf) { + surf->Init(sid, ed->wMain.GetID()); + surf->SetUnicodeMode(SC_CP_UTF8 == ed->CodePage()); + surf->SetDBCSMode(ed->CodePage()); + } + } + } + ~AutoSurface() { + delete surf; + } + Surface *operator->() const { + return surf; + } + operator Surface *() const { + return surf; + } +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ExternalLexer.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ExternalLexer.cxx new file mode 100644 index 00000000..7abfbdd9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ExternalLexer.cxx @@ -0,0 +1,259 @@ +// Scintilla source code edit control +/** @file ExternalLexer.cxx + ** Support external lexers in DLLs. + **/ +// Copyright 2001 Simon Steele , portions copyright Neil Hodgson. +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include + +#include "Platform.h" + +#include "Scintilla.h" + +#include "SciLexer.h" +#include "PropSet.h" +#include "Accessor.h" +#include "DocumentAccessor.h" +#include "KeyWords.h" +#include "ExternalLexer.h" + +LexerManager *LexerManager::theInstance = NULL; + +//------------------------------------------ +// +// ExternalLexerModule +// +//------------------------------------------ + +char **WordListsToStrings(WordList *val[]) { + int dim = 0; + while (val[dim]) + dim++; + char **wls = new char * [dim + 1]; + for (int i = 0;i < dim;i++) { + SString words; + words = ""; + for (int n = 0; n < val[i]->len; n++) { + words += val[i]->words[n]; + if (n != val[i]->len - 1) + words += " "; + } + wls[i] = new char[words.length() + 1]; + strcpy(wls[i], words.c_str()); + } + wls[dim] = 0; + return wls; +} + +void DeleteWLStrings(char *strs[]) { + int dim = 0; + while (strs[dim]) { + delete strs[dim]; + dim++; + } + delete [] strs; +} + +void ExternalLexerModule::Lex(unsigned int startPos, int lengthDoc, int initStyle, + WordList *keywordlists[], Accessor &styler) const { + if (!fneLexer) + return ; + + char **kwds = WordListsToStrings(keywordlists); + char *ps = styler.GetProperties(); + + // The accessor passed in is always a DocumentAccessor so this cast and the subsequent + // access will work. Can not use the stricter dynamic_cast as that requires RTTI. + DocumentAccessor &da = static_cast(styler); + WindowID wID = da.GetWindow(); + + fneLexer(externalLanguage, startPos, lengthDoc, initStyle, kwds, wID, ps); + + delete ps; + DeleteWLStrings(kwds); +} + +void ExternalLexerModule::Fold(unsigned int startPos, int lengthDoc, int initStyle, + WordList *keywordlists[], Accessor &styler) const { + if (!fneFolder) + return ; + + char **kwds = WordListsToStrings(keywordlists); + char *ps = styler.GetProperties(); + + // The accessor passed in is always a DocumentAccessor so this cast and the subsequent + // access will work. Can not use the stricter dynamic_cast as that requires RTTI. + DocumentAccessor &da = static_cast(styler); + WindowID wID = da.GetWindow(); + + fneFolder(externalLanguage, startPos, lengthDoc, initStyle, kwds, wID, ps); + + delete ps; + DeleteWLStrings(kwds); +} + +void ExternalLexerModule::SetExternal(ExtLexerFunction fLexer, ExtFoldFunction fFolder, int index) { + fneLexer = fLexer; + fneFolder = fFolder; + externalLanguage = index; +} + +//------------------------------------------ +// +// LexerLibrary +// +//------------------------------------------ + +LexerLibrary::LexerLibrary(const char* ModuleName) { + // Initialise some members... + first = NULL; + last = NULL; + + // Load the DLL + lib = DynamicLibrary::Load(ModuleName); + if (lib->IsValid()) { + m_sModuleName = ModuleName; + //Cannot use reinterpret_cast because: ANSI C++ forbids casting between pointers to functions and objects + GetLexerCountFn GetLexerCount = (GetLexerCountFn)(sptr_t)lib->FindFunction("GetLexerCount"); + + if (GetLexerCount) { + ExternalLexerModule *lex; + LexerMinder *lm; + + // Find functions in the DLL + GetLexerNameFn GetLexerName = (GetLexerNameFn)(sptr_t)lib->FindFunction("GetLexerName"); + ExtLexerFunction Lexer = (ExtLexerFunction)(sptr_t)lib->FindFunction("Lex"); + ExtFoldFunction Folder = (ExtFoldFunction)(sptr_t)lib->FindFunction("Fold"); + + // Assign a buffer for the lexer name. + char lexname[100]; + strcpy(lexname, ""); + + int nl = GetLexerCount(); + + for (int i = 0; i < nl; i++) { + GetLexerName(i, lexname, 100); + lex = new ExternalLexerModule(SCLEX_AUTOMATIC, NULL, lexname, NULL); + + // Create a LexerMinder so we don't leak the ExternalLexerModule... + lm = new LexerMinder; + lm->self = lex; + lm->next = NULL; + if (first != NULL) { + last->next = lm; + last = lm; + } else { + first = lm; + last = lm; + } + + // The external lexer needs to know how to call into its DLL to + // do its lexing and folding, we tell it here. Folder may be null. + lex->SetExternal(Lexer, Folder, i); + } + } + } + next = NULL; +} + +LexerLibrary::~LexerLibrary() { + Release(); + delete lib; +} + +void LexerLibrary::Release() { + //TODO maintain a list of lexers created, and delete them! + LexerMinder *lm; + LexerMinder *next; + lm = first; + while (NULL != lm) { + next = lm->next; + delete lm->self; + delete lm; + lm = next; + } + + first = NULL; + last = NULL; +} + +//------------------------------------------ +// +// LexerManager +// +//------------------------------------------ + +/// Return the single LexerManager instance... +LexerManager *LexerManager::GetInstance() { + if(!theInstance) + theInstance = new LexerManager; + return theInstance; +} + +/// Delete any LexerManager instance... +void LexerManager::DeleteInstance() +{ + if(theInstance) { + delete theInstance; + theInstance = NULL; + } +} + +/// protected constructor - this is a singleton... +LexerManager::LexerManager() { + first = NULL; + last = NULL; +} + +LexerManager::~LexerManager() { + Clear(); +} + +void LexerManager::Load(const char* path) +{ + LoadLexerLibrary(path); +} + +void LexerManager::LoadLexerLibrary(const char* module) +{ + LexerLibrary *lib = new LexerLibrary(module); + if (NULL != first) { + last->next = lib; + last = lib; + } else { + first = lib; + last = lib; + } +} + +void LexerManager::Clear() +{ + if (NULL != first) { + LexerLibrary *cur = first; + LexerLibrary *next; + while (cur) { + next = cur->next; + delete cur; + cur = next; + } + first = NULL; + last = NULL; + } +} + +//------------------------------------------ +// +// LexerManager +// +//------------------------------------------ + +LMMinder::~LMMinder() +{ + LexerManager::DeleteInstance(); +} + +LMMinder minder; diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ExternalLexer.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ExternalLexer.h new file mode 100644 index 00000000..ad42ce04 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/ExternalLexer.h @@ -0,0 +1,95 @@ +// Scintilla source code edit control +/** @file ExternalLexer.h + ** Support external lexers in DLLs. + **/ +// Copyright 2001 Simon Steele , portions copyright Neil Hodgson. +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef EXTERNALLEXER_H +#define EXTERNALLEXER_H + +#if PLAT_WIN +#define EXT_LEXER_DECL __stdcall +#elif PLAT_GTK +#define EXT_LEXER_DECL +#endif + +// External Lexer function definitions... +typedef void (EXT_LEXER_DECL *ExtLexerFunction)(unsigned int lexer, unsigned int startPos, int length, int initStyle, + char *words[], WindowID window, char *props); +typedef void (EXT_LEXER_DECL *ExtFoldFunction)(unsigned int lexer, unsigned int startPos, int length, int initStyle, + char *words[], WindowID window, char *props); +typedef void* (EXT_LEXER_DECL *GetLexerFunction)(unsigned int Index); +typedef int (EXT_LEXER_DECL *GetLexerCountFn)(); +typedef void (EXT_LEXER_DECL *GetLexerNameFn)(unsigned int Index, char *name, int buflength); + +//class DynamicLibrary; + +/// Sub-class of LexerModule to use an external lexer. +class ExternalLexerModule : protected LexerModule { +protected: + ExtLexerFunction fneLexer; + ExtFoldFunction fneFolder; + int externalLanguage; + char name[100]; +public: + ExternalLexerModule(int language_, LexerFunction fnLexer_, + const char *languageName_=0, LexerFunction fnFolder_=0) : LexerModule(language_, fnLexer_, 0, fnFolder_){ + strncpy(name, languageName_, sizeof(name)); + languageName = name; + }; + virtual void Lex(unsigned int startPos, int lengthDoc, int initStyle, + WordList *keywordlists[], Accessor &styler) const; + virtual void Fold(unsigned int startPos, int lengthDoc, int initStyle, + WordList *keywordlists[], Accessor &styler) const; + virtual void SetExternal(ExtLexerFunction fLexer, ExtFoldFunction fFolder, int index); +}; + +/// LexerMinder points to an ExternalLexerModule - so we don't leak them. +class LexerMinder { +public: + ExternalLexerModule *self; + LexerMinder *next; +}; + +/// LexerLibrary exists for every External Lexer DLL, contains LexerMinders. +class LexerLibrary { + DynamicLibrary *lib; + LexerMinder *first; + LexerMinder *last; + +public: + LexerLibrary(const char* ModuleName); + ~LexerLibrary(); + void Release(); + + LexerLibrary *next; + SString m_sModuleName; +}; + +/// LexerManager manages external lexers, contains LexerLibrarys. +class LexerManager { +public: + ~LexerManager(); + + static LexerManager *GetInstance(); + static void DeleteInstance(); + + void Load(const char* path); + void Clear(); + +private: + LexerManager(); + static LexerManager *theInstance; + + void LoadLexerLibrary(const char* module); + LexerLibrary *first; + LexerLibrary *last; +}; + +class LMMinder { +public: + ~LMMinder(); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Indicator.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Indicator.cxx new file mode 100644 index 00000000..1c41036c --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Indicator.cxx @@ -0,0 +1,77 @@ +// Scintilla source code edit control +/** @file Indicator.cxx + ** Defines the style of indicators which are text decorations such as underlining. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include "Platform.h" + +#include "Scintilla.h" +#include "Indicator.h" + +void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine) { + surface->PenColour(fore.allocated); + int ymid = (rc.bottom + rc.top) / 2; + if (style == INDIC_SQUIGGLE) { + surface->MoveTo(rc.left, rc.top); + int x = rc.left + 2; + int y = 2; + while (x < rc.right) { + surface->LineTo(x, rc.top + y); + x += 2; + y = 2 - y; + } + surface->LineTo(rc.right, rc.top + y); // Finish the line + } else if (style == INDIC_TT) { + surface->MoveTo(rc.left, ymid); + int x = rc.left + 5; + while (x < rc.right) { + surface->LineTo(x, ymid); + surface->MoveTo(x-3, ymid); + surface->LineTo(x-3, ymid+2); + x++; + surface->MoveTo(x, ymid); + x += 5; + } + surface->LineTo(rc.right, ymid); // Finish the line + if (x - 3 <= rc.right) { + surface->MoveTo(x-3, ymid); + surface->LineTo(x-3, ymid+2); + } + } else if (style == INDIC_DIAGONAL) { + int x = rc.left; + while (x < rc.right) { + surface->MoveTo(x, rc.top+2); + int endX = x+3; + int endY = rc.top - 1; + if (endX > rc.right) { + endY += endX - rc.right; + endX = rc.right; + } + surface->LineTo(endX, endY); + x += 4; + } + } else if (style == INDIC_STRIKE) { + surface->MoveTo(rc.left, rc.top - 4); + surface->LineTo(rc.right, rc.top - 4); + } else if (style == INDIC_HIDDEN) { + // Draw nothing + } else if (style == INDIC_BOX) { + surface->MoveTo(rc.left, ymid+1); + surface->LineTo(rc.right, ymid+1); + surface->LineTo(rc.right, rcLine.top+1); + surface->LineTo(rc.left, rcLine.top+1); + surface->LineTo(rc.left, ymid+1); + } else if (style == INDIC_ROUNDBOX) { + PRectangle rcBox = rcLine; + rcBox.top = rcLine.top + 1; + rcBox.left = rc.left; + rcBox.right = rc.right; + surface->AlphaRectangle(rcBox, 1, fore.allocated, 30, fore.allocated, 50, 0); + } else { // Either INDIC_PLAIN or unknown + surface->MoveTo(rc.left, ymid); + surface->LineTo(rc.right, ymid); + } +} + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Indicator.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Indicator.h new file mode 100644 index 00000000..646728ce --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/Indicator.h @@ -0,0 +1,22 @@ +// Scintilla source code edit control +/** @file Indicator.h + ** Defines the style of indicators which are text decorations such as underlining. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef INDICATOR_H +#define INDICATOR_H + +/** + */ +class Indicator { +public: + int style; + ColourPair fore; + Indicator() : style(INDIC_PLAIN), fore(ColourDesired(0,0,0)) { + } + void Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine); +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyMap.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyMap.cxx new file mode 100644 index 00000000..faf8a21f --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyMap.cxx @@ -0,0 +1,148 @@ +// Scintilla source code edit control +/** @file KeyMap.cxx + ** Defines a mapping between keystrokes and commands. + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include "Platform.h" + +#include "Scintilla.h" + +#include "KeyMap.h" + +KeyMap::KeyMap() : kmap(0), len(0), alloc(0) { + for (int i = 0; MapDefault[i].key; i++) { + AssignCmdKey(MapDefault[i].key, + MapDefault[i].modifiers, + MapDefault[i].msg); + } +} + +KeyMap::~KeyMap() { + Clear(); +} + +void KeyMap::Clear() { + delete []kmap; + kmap = 0; + len = 0; + alloc = 0; +} + +void KeyMap::AssignCmdKey(int key, int modifiers, unsigned int msg) { + if ((len+1) >= alloc) { + KeyToCommand *ktcNew = new KeyToCommand[alloc + 5]; + if (!ktcNew) + return; + for (int k = 0; k < len; k++) + ktcNew[k] = kmap[k]; + alloc += 5; + delete []kmap; + kmap = ktcNew; + } + for (int keyIndex = 0; keyIndex < len; keyIndex++) { + if ((key == kmap[keyIndex].key) && (modifiers == kmap[keyIndex].modifiers)) { + kmap[keyIndex].msg = msg; + return; + } + } + kmap[len].key = key; + kmap[len].modifiers = modifiers; + kmap[len].msg = msg; + len++; +} + +unsigned int KeyMap::Find(int key, int modifiers) { + for (int i = 0; i < len; i++) { + if ((key == kmap[i].key) && (modifiers == kmap[i].modifiers)) { + return kmap[i].msg; + } + } + return 0; +} + +const KeyToCommand KeyMap::MapDefault[] = { + {SCK_DOWN, SCI_NORM, SCI_LINEDOWN}, + {SCK_DOWN, SCI_SHIFT, SCI_LINEDOWNEXTEND}, + {SCK_DOWN, SCI_CTRL, SCI_LINESCROLLDOWN}, + {SCK_DOWN, SCI_ASHIFT, SCI_LINEDOWNRECTEXTEND}, + {SCK_UP, SCI_NORM, SCI_LINEUP}, + {SCK_UP, SCI_SHIFT, SCI_LINEUPEXTEND}, + {SCK_UP, SCI_CTRL, SCI_LINESCROLLUP}, + {SCK_UP, SCI_ASHIFT, SCI_LINEUPRECTEXTEND}, + {'[', SCI_CTRL, SCI_PARAUP}, + {'[', SCI_CSHIFT, SCI_PARAUPEXTEND}, + {']', SCI_CTRL, SCI_PARADOWN}, + {']', SCI_CSHIFT, SCI_PARADOWNEXTEND}, + {SCK_LEFT, SCI_NORM, SCI_CHARLEFT}, + {SCK_LEFT, SCI_SHIFT, SCI_CHARLEFTEXTEND}, + {SCK_LEFT, SCI_CTRL, SCI_WORDLEFT}, + {SCK_LEFT, SCI_CSHIFT, SCI_WORDLEFTEXTEND}, + {SCK_LEFT, SCI_ASHIFT, SCI_CHARLEFTRECTEXTEND}, + {SCK_RIGHT, SCI_NORM, SCI_CHARRIGHT}, + {SCK_RIGHT, SCI_SHIFT, SCI_CHARRIGHTEXTEND}, + {SCK_RIGHT, SCI_CTRL, SCI_WORDRIGHT}, + {SCK_RIGHT, SCI_CSHIFT, SCI_WORDRIGHTEXTEND}, + {SCK_RIGHT, SCI_ASHIFT, SCI_CHARRIGHTRECTEXTEND}, + {'/', SCI_CTRL, SCI_WORDPARTLEFT}, + {'/', SCI_CSHIFT, SCI_WORDPARTLEFTEXTEND}, + {'\\', SCI_CTRL, SCI_WORDPARTRIGHT}, + {'\\', SCI_CSHIFT, SCI_WORDPARTRIGHTEXTEND}, + {SCK_HOME, SCI_NORM, SCI_VCHOME}, + {SCK_HOME, SCI_SHIFT, SCI_VCHOMEEXTEND}, + {SCK_HOME, SCI_CTRL, SCI_DOCUMENTSTART}, + {SCK_HOME, SCI_CSHIFT, SCI_DOCUMENTSTARTEXTEND}, + {SCK_HOME, SCI_ALT, SCI_HOMEDISPLAY}, +// {SCK_HOME, SCI_ASHIFT, SCI_HOMEDISPLAYEXTEND}, + {SCK_HOME, SCI_ASHIFT, SCI_VCHOMERECTEXTEND}, + {SCK_END, SCI_NORM, SCI_LINEEND}, + {SCK_END, SCI_SHIFT, SCI_LINEENDEXTEND}, + {SCK_END, SCI_CTRL, SCI_DOCUMENTEND}, + {SCK_END, SCI_CSHIFT, SCI_DOCUMENTENDEXTEND}, + {SCK_END, SCI_ALT, SCI_LINEENDDISPLAY}, +// {SCK_END, SCI_ASHIFT, SCI_LINEENDDISPLAYEXTEND}, + {SCK_END, SCI_ASHIFT, SCI_LINEENDRECTEXTEND}, + {SCK_PRIOR, SCI_NORM, SCI_PAGEUP}, + {SCK_PRIOR, SCI_SHIFT, SCI_PAGEUPEXTEND}, + {SCK_PRIOR, SCI_ASHIFT, SCI_PAGEUPRECTEXTEND}, + {SCK_NEXT, SCI_NORM, SCI_PAGEDOWN}, + {SCK_NEXT, SCI_SHIFT, SCI_PAGEDOWNEXTEND}, + {SCK_NEXT, SCI_ASHIFT, SCI_PAGEDOWNRECTEXTEND}, + {SCK_DELETE, SCI_NORM, SCI_CLEAR}, + {SCK_DELETE, SCI_SHIFT, SCI_CUT}, + {SCK_DELETE, SCI_CTRL, SCI_DELWORDRIGHT}, + {SCK_DELETE, SCI_CSHIFT, SCI_DELLINERIGHT}, + {SCK_INSERT, SCI_NORM, SCI_EDITTOGGLEOVERTYPE}, + {SCK_INSERT, SCI_SHIFT, SCI_PASTE}, + {SCK_INSERT, SCI_CTRL, SCI_COPY}, + {SCK_ESCAPE, SCI_NORM, SCI_CANCEL}, + {SCK_BACK, SCI_NORM, SCI_DELETEBACK}, + {SCK_BACK, SCI_SHIFT, SCI_DELETEBACK}, + {SCK_BACK, SCI_CTRL, SCI_DELWORDLEFT}, + {SCK_BACK, SCI_ALT, SCI_UNDO}, + {SCK_BACK, SCI_CSHIFT, SCI_DELLINELEFT}, + {'Z', SCI_CTRL, SCI_UNDO}, + {'Y', SCI_CTRL, SCI_REDO}, + {'X', SCI_CTRL, SCI_CUT}, + {'C', SCI_CTRL, SCI_COPY}, + {'V', SCI_CTRL, SCI_PASTE}, + {'A', SCI_CTRL, SCI_SELECTALL}, + {SCK_TAB, SCI_NORM, SCI_TAB}, + {SCK_TAB, SCI_SHIFT, SCI_BACKTAB}, + {SCK_RETURN, SCI_NORM, SCI_NEWLINE}, + {SCK_RETURN, SCI_SHIFT, SCI_NEWLINE}, + {SCK_ADD, SCI_CTRL, SCI_ZOOMIN}, + {SCK_SUBTRACT, SCI_CTRL, SCI_ZOOMOUT}, + {SCK_DIVIDE, SCI_CTRL, SCI_SETZOOM}, + //'L', SCI_CTRL, SCI_FORMFEED, + {'L', SCI_CTRL, SCI_LINECUT}, + {'L', SCI_CSHIFT, SCI_LINEDELETE}, + {'T', SCI_CSHIFT, SCI_LINECOPY}, + {'T', SCI_CTRL, SCI_LINETRANSPOSE}, + {'D', SCI_CTRL, SCI_SELECTIONDUPLICATE}, + {'U', SCI_CTRL, SCI_LOWERCASE}, + {'U', SCI_CSHIFT, SCI_UPPERCASE}, + {0,0,0}, +}; + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyMap.h b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyMap.h new file mode 100644 index 00000000..985d2cd5 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyMap.h @@ -0,0 +1,43 @@ +// Scintilla source code edit control +/** @file KeyMap.h + ** Defines a mapping between keystrokes and commands. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef KEYTOCOMMAND_H +#define KEYTOCOMMAND_H + +#define SCI_NORM 0 +#define SCI_SHIFT SCMOD_SHIFT +#define SCI_CTRL SCMOD_CTRL +#define SCI_ALT SCMOD_ALT +#define SCI_CSHIFT (SCI_CTRL | SCI_SHIFT) +#define SCI_ASHIFT (SCI_ALT | SCI_SHIFT) + +/** + */ +class KeyToCommand { +public: + int key; + int modifiers; + unsigned int msg; +}; + +/** + */ +class KeyMap { + KeyToCommand *kmap; + int len; + int alloc; + static const KeyToCommand MapDefault[]; + +public: + KeyMap(); + ~KeyMap(); + void Clear(); + void AssignCmdKey(int key, int modifiers, unsigned int msg); + unsigned int Find(int key, int modifiers); // 0 returned on failure +}; + +#endif diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyWords.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyWords.cxx new file mode 100644 index 00000000..bb33bd34 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/KeyWords.cxx @@ -0,0 +1,221 @@ +// Scintilla source code edit control +/** @file KeyWords.cxx + ** Colourise for particular languages. + **/ +// Copyright 1998-2002 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +const LexerModule *LexerModule::base = 0; +int LexerModule::nextLanguage = SCLEX_AUTOMATIC+1; + +LexerModule::LexerModule(int language_, + LexerFunction fnLexer_, + const char *languageName_, + LexerFunction fnFolder_, + const char * const wordListDescriptions_[], + int styleBits_) : + language(language_), + fnLexer(fnLexer_), + fnFolder(fnFolder_), + wordListDescriptions(wordListDescriptions_), + styleBits(styleBits_), + languageName(languageName_) { + next = base; + base = this; + if (language == SCLEX_AUTOMATIC) { + language = nextLanguage; + nextLanguage++; + } +} + +int LexerModule::GetNumWordLists() const { + if (wordListDescriptions == NULL) { + return -1; + } else { + int numWordLists = 0; + + while (wordListDescriptions[numWordLists]) { + ++numWordLists; + } + + return numWordLists; + } +} + +const char *LexerModule::GetWordListDescription(int index) const { + static const char *emptyStr = ""; + + PLATFORM_ASSERT(index < GetNumWordLists()); + if (index >= GetNumWordLists()) { + return emptyStr; + } else { + return wordListDescriptions[index]; + } +} + +int LexerModule::GetStyleBitsNeeded() const { + return styleBits; +} + +const LexerModule *LexerModule::Find(int language) { + const LexerModule *lm = base; + while (lm) { + if (lm->language == language) { + return lm; + } + lm = lm->next; + } + return 0; +} + +const LexerModule *LexerModule::Find(const char *languageName) { + if (languageName) { + const LexerModule *lm = base; + while (lm) { + if (lm->languageName && 0 == strcmp(lm->languageName, languageName)) { + return lm; + } + lm = lm->next; + } + } + return 0; +} + +void LexerModule::Lex(unsigned int startPos, int lengthDoc, int initStyle, + WordList *keywordlists[], Accessor &styler) const { + if (fnLexer) + fnLexer(startPos, lengthDoc, initStyle, keywordlists, styler); +} + +void LexerModule::Fold(unsigned int startPos, int lengthDoc, int initStyle, + WordList *keywordlists[], Accessor &styler) const { + if (fnFolder) { + int lineCurrent = styler.GetLine(startPos); + // Move back one line in case deletion wrecked current line fold state + if (lineCurrent > 0) { + lineCurrent--; + int newStartPos = styler.LineStart(lineCurrent); + lengthDoc += startPos - newStartPos; + startPos = newStartPos; + initStyle = 0; + if (startPos > 0) { + initStyle = styler.StyleAt(startPos - 1); + } + } + fnFolder(startPos, lengthDoc, initStyle, keywordlists, styler); + } +} + +// Alternative historical name for Scintilla_LinkLexers +int wxForceScintillaLexers(void) { + return Scintilla_LinkLexers(); +} + +// To add or remove a lexer, add or remove its file and run LexGen.py. + +// Force a reference to all of the Scintilla lexers so that the linker will +// not remove the code of the lexers. +int Scintilla_LinkLexers() { + static int forcer = 0; + +// Shorten the code that declares a lexer and ensures it is linked in by calling a method. +#define LINK_LEXER(lexer) extern LexerModule lexer; forcer += lexer.GetLanguage(); + +//++Autogenerated -- run src/LexGen.py to regenerate +//**\(\tLINK_LEXER(\*);\n\) + LINK_LEXER(lmAda); + LINK_LEXER(lmAns1); + LINK_LEXER(lmAPDL); + LINK_LEXER(lmAsm); + LINK_LEXER(lmASP); + LINK_LEXER(lmAU3); + LINK_LEXER(lmAVE); + LINK_LEXER(lmBaan); + LINK_LEXER(lmBash); + LINK_LEXER(lmBatch); + LINK_LEXER(lmBlitzBasic); + LINK_LEXER(lmBullant); + LINK_LEXER(lmCaml); + LINK_LEXER(lmClw); + LINK_LEXER(lmClwNoCase); + LINK_LEXER(lmConf); + LINK_LEXER(lmCPP); + LINK_LEXER(lmCPPNoCase); + LINK_LEXER(lmCsound); + LINK_LEXER(lmCss); + LINK_LEXER(lmDiff); + LINK_LEXER(lmEiffel); + LINK_LEXER(lmEiffelkw); + LINK_LEXER(lmErlang); + LINK_LEXER(lmErrorList); + LINK_LEXER(lmESCRIPT); + LINK_LEXER(lmF77); + LINK_LEXER(lmFlagShip); + LINK_LEXER(lmForth); + LINK_LEXER(lmFortran); + LINK_LEXER(lmFreeBasic); + LINK_LEXER(lmGui4Cli); + LINK_LEXER(lmHaskell); + LINK_LEXER(lmHTML); + LINK_LEXER(lmInno); + LINK_LEXER(lmKix); + LINK_LEXER(lmLatex); + LINK_LEXER(lmLISP); + LINK_LEXER(lmLot); + LINK_LEXER(lmLout); + LINK_LEXER(lmLua); + LINK_LEXER(lmMake); + LINK_LEXER(lmMatlab); + LINK_LEXER(lmMETAPOST); + LINK_LEXER(lmMMIXAL); + LINK_LEXER(lmMSSQL); + LINK_LEXER(lmNncrontab); + LINK_LEXER(lmNsis); + LINK_LEXER(lmNull); + LINK_LEXER(lmOctave); + LINK_LEXER(lmOpal); + LINK_LEXER(lmPascal); + LINK_LEXER(lmPB); + LINK_LEXER(lmPerl); + LINK_LEXER(lmPHP); + LINK_LEXER(lmPHPSCRIPT); + LINK_LEXER(lmPOV); + LINK_LEXER(lmProps); + LINK_LEXER(lmPS); + LINK_LEXER(lmPureBasic); + LINK_LEXER(lmPython); + LINK_LEXER(lmREBOL); + LINK_LEXER(lmRuby); + LINK_LEXER(lmScriptol); + LINK_LEXER(lmSmalltalk); + LINK_LEXER(lmSpecman); + LINK_LEXER(lmSpice); + LINK_LEXER(lmSQL); + LINK_LEXER(lmTADS3); + LINK_LEXER(lmTCL); + LINK_LEXER(lmTeX); + LINK_LEXER(lmVB); + LINK_LEXER(lmVBScript); + LINK_LEXER(lmVerilog); + LINK_LEXER(lmVHDL); + LINK_LEXER(lmXML); + LINK_LEXER(lmYAML); + +//--Autogenerated -- end of automatically generated section + + return 1; +} diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAPDL.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAPDL.cxx new file mode 100644 index 00000000..d1347c2e --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAPDL.cxx @@ -0,0 +1,136 @@ +// Scintilla source code edit control +/** @file LexAPDL.cxx + ** Lexer for APDL. Based on the lexer for Assembler by The Black Horus. + ** By Hadar Raz. + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + + +static inline bool IsAWordChar(const int ch) { + return (ch < 0x80 && (isalnum(ch) || ch == '_')); +} + +static inline bool IsAnOperator(char ch) { + // '.' left out as it is used to make up numbers + if (ch == '*' || ch == '/' || ch == '-' || ch == '+' || + ch == '(' || ch == ')' || ch == '=' || ch == '^' || + ch == '[' || ch == ']' || ch == '<' || ch == '&' || + ch == '>' || ch == ',' || ch == '|' || ch == '~' || + ch == '$' || ch == ':' || ch == '%') + return true; + return false; +} + +static void ColouriseAPDLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) { + + int stringStart = ' '; + + WordList &processors = *keywordlists[0]; + WordList &commands = *keywordlists[1]; + WordList &slashcommands = *keywordlists[2]; + WordList &starcommands = *keywordlists[3]; + WordList &arguments = *keywordlists[4]; + WordList &functions = *keywordlists[5]; + + // Do not leak onto next line + initStyle = SCE_APDL_DEFAULT; + StyleContext sc(startPos, length, initStyle, styler); + + for (; sc.More(); sc.Forward()) { + // Determine if the current state should terminate. + if (sc.state == SCE_APDL_NUMBER) { + if (!(IsADigit(sc.ch) || sc.ch == '.' || (sc.ch == 'e' || sc.ch == 'E') || + ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E')))) { + sc.SetState(SCE_APDL_DEFAULT); + } + } else if (sc.state == SCE_APDL_COMMENT) { + if (sc.atLineEnd) { + sc.SetState(SCE_APDL_DEFAULT); + } + } else if (sc.state == SCE_APDL_COMMENTBLOCK) { + if (sc.atLineEnd) { + if (sc.ch == '\r') { + sc.Forward(); + } + sc.ForwardSetState(SCE_APDL_DEFAULT); + } + } else if (sc.state == SCE_APDL_STRING) { + if (sc.atLineEnd) { + sc.SetState(SCE_APDL_DEFAULT); + } else if ((sc.ch == '\'' && stringStart == '\'') || (sc.ch == '\"' && stringStart == '\"')) { + sc.ForwardSetState(SCE_APDL_DEFAULT); + } + } else if (sc.state == SCE_APDL_WORD) { + if (!IsAWordChar(sc.ch)) { + char s[100]; + sc.GetCurrentLowered(s, sizeof(s)); + if (processors.InList(s)) { + sc.ChangeState(SCE_APDL_PROCESSOR); + } else if (slashcommands.InList(s)) { + sc.ChangeState(SCE_APDL_SLASHCOMMAND); + } else if (starcommands.InList(s)) { + sc.ChangeState(SCE_APDL_STARCOMMAND); + } else if (commands.InList(s)) { + sc.ChangeState(SCE_APDL_COMMAND); + } else if (arguments.InList(s)) { + sc.ChangeState(SCE_APDL_ARGUMENT); + } else if (functions.InList(s)) { + sc.ChangeState(SCE_APDL_FUNCTION); + } + sc.SetState(SCE_APDL_DEFAULT); + } + } else if (sc.state == SCE_APDL_OPERATOR) { + if (!IsAnOperator(static_cast(sc.ch))) { + sc.SetState(SCE_APDL_DEFAULT); + } + } + + // Determine if a new state should be entered. + if (sc.state == SCE_APDL_DEFAULT) { + if (sc.ch == '!' && sc.chNext == '!') { + sc.SetState(SCE_APDL_COMMENTBLOCK); + } else if (sc.ch == '!') { + sc.SetState(SCE_APDL_COMMENT); + } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { + sc.SetState(SCE_APDL_NUMBER); + } else if (sc.ch == '\'' || sc.ch == '\"') { + sc.SetState(SCE_APDL_STRING); + stringStart = sc.ch; + } else if (IsAWordChar(sc.ch) || ((sc.ch == '*' || sc.ch == '/') && !isgraph(sc.chPrev))) { + sc.SetState(SCE_APDL_WORD); + } else if (IsAnOperator(static_cast(sc.ch))) { + sc.SetState(SCE_APDL_OPERATOR); + } + } + } + sc.Complete(); +} + +static const char * const apdlWordListDesc[] = { + "processors", + "commands", + "slashommands", + "starcommands", + "arguments", + "functions", + 0 +}; + +LexerModule lmAPDL(SCLEX_APDL, ColouriseAPDLDoc, "apdl", 0, apdlWordListDesc); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAU3.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAU3.cxx new file mode 100644 index 00000000..2446b238 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAU3.cxx @@ -0,0 +1,891 @@ +// Scintilla source code edit control +// @file LexAU3.cxx +// Lexer for AutoIt3 http://www.hiddensoft.com/autoit3 +// by Jos van der Zande, jvdzande@yahoo.com +// +// Changes: +// March 28, 2004 - Added the standard Folding code +// April 21, 2004 - Added Preprosessor Table + Syntax Highlighting +// Fixed Number highlighting +// Changed default isoperator to IsAOperator to have a better match to AutoIt3 +// Fixed "#comments_start" -> "#comments-start" +// Fixed "#comments_end" -> "#comments-end" +// Fixed Sendkeys in Strings when not terminated with } +// Added support for Sendkey strings that have second parameter e.g. {UP 5} or {a down} +// April 26, 2004 - Fixed # pre-processor statement inside of comment block would invalidly change the color. +// Added logic for #include to treat the <> as string +// Added underscore to IsAOperator. +// May 17, 2004 - Changed the folding logic from indent to keyword folding. +// Added Folding logic for blocks of single-commentlines or commentblock. +// triggered by: fold.comment=1 +// Added Folding logic for preprocessor blocks triggered by fold.preprocessor=1 +// Added Special for #region - #endregion syntax highlight and folding. +// May 30, 2004 - Fixed issue with continuation lines on If statements. +// June 5, 2004 - Added comma to Operators for better readability. +// Added fold.compact support set with fold.compact=1 +// Changed folding inside of #cs-#ce. Default is no keyword folding inside comment blocks when fold.comment=1 +// it will now only happen when fold.comment=2. +// Sep 5, 2004 - Added logic to handle colourizing words on the last line. +// Typed Characters now show as "default" till they match any table. +// Oct 10, 2004 - Added logic to show Comments in "Special" directives. +// Nov 1, 2004 - Added better testing for Numbers supporting x and e notation. +// Nov 28, 2004 - Added logic to handle continuation lines for syntax highlighting. +// Jan 10, 2005 - Added Abbreviations Keyword used for expansion +// Mar 24, 2005 - Updated Abbreviations Keywords to fix when followed by Operator. +// Apr 18, 2005 - Updated #CE/#Comment-End logic to take a linecomment ";" into account +// - Added folding support for With...EndWith +// - Added support for a DOT in variable names +// - Fixed Underscore in CommentBlock +// May 23, 2005 - Fixed the SentKey lexing in case of a missing } +// Aug 11, 2005 - Fixed possible bug with s_save length > 100. +// Aug 23, 2005 - Added Switch/endswitch support to the folding logic. +// Sep 27, 2005 - Fixed the SentKey lexing logic in case of multiple sentkeys. +// Mar 12, 2006 - Fixed issue with <> coloring as String in stead of Operator in rare occasions. +// Apr 8, 2006 - Added support for AutoIt3 Standard UDF library (SCE_AU3_UDF) +// +// Copyright for Scintilla: 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. +// Scintilla source code edit control + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +static inline bool IsTypeCharacter(const int ch) +{ + return ch == '$'; +} +static inline bool IsAWordChar(const int ch) +{ + return (ch < 0x80) && (isalnum(ch) || ch == '_'); +} + +static inline bool IsAWordStart(const int ch) +{ + return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '@' || ch == '#' || ch == '$' || ch == '.'); +} + +static inline bool IsAOperator(char ch) { + if (isascii(ch) && isalnum(ch)) + return false; + if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || + ch == '&' || ch == '^' || ch == '=' || ch == '<' || ch == '>' || + ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == ',' ) + return true; + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// GetSendKey() filters the portion before and after a/multiple space(s) +// and return the first portion to be looked-up in the table +// also check if the second portion is valid... (up,down.on.off,toggle or a number) +/////////////////////////////////////////////////////////////////////////////// + +static int GetSendKey(const char *szLine, char *szKey) +{ + int nFlag = 0; + int nStartFound = 0; + int nKeyPos = 0; + int nSpecPos= 0; + int nSpecNum= 1; + int nPos = 0; + char cTemp; + char szSpecial[100]; + + // split the portion of the sendkey in the part before and after the spaces + while ( ( (cTemp = szLine[nPos]) != '\0')) + { + // skip leading Ctrl/Shift/Alt state + if (cTemp == '{') { + nStartFound = 1; + } + // + if (nStartFound == 1) { + if ((cTemp == ' ') && (nFlag == 0) ) // get the stuff till first space + { + nFlag = 1; + // Add } to the end of the first bit for table lookup later. + szKey[nKeyPos++] = '}'; + } + else if (cTemp == ' ') + { + // skip other spaces + } + else if (nFlag == 0) + { + // save first portion into var till space or } is hit + szKey[nKeyPos++] = cTemp; + } + else if ((nFlag == 1) && (cTemp != '}')) + { + // Save second portion into var... + szSpecial[nSpecPos++] = cTemp; + // check if Second portion is all numbers for repeat fuction + if (isdigit(cTemp) == false) {nSpecNum = 0;} + } + } + nPos++; // skip to next char + + } // End While + + + // Check if the second portion is either a number or one of these keywords + szKey[nKeyPos] = '\0'; + szSpecial[nSpecPos] = '\0'; + if (strcmp(szSpecial,"down")== 0 || strcmp(szSpecial,"up")== 0 || + strcmp(szSpecial,"on")== 0 || strcmp(szSpecial,"off")== 0 || + strcmp(szSpecial,"toggle")== 0 || nSpecNum == 1 ) + { + nFlag = 0; + } + else + { + nFlag = 1; + } + return nFlag; // 1 is bad, 0 is good + +} // GetSendKey() + +// +// Routine to check the last "none comment" character on a line to see if its a continuation +// +static bool IsContinuationLine(unsigned int szLine, Accessor &styler) +{ + int nsPos = styler.LineStart(szLine); + int nePos = styler.LineStart(szLine+1) - 2; + //int stylech = styler.StyleAt(nsPos); + while (nsPos < nePos) + { + //stylech = styler.StyleAt(nePos); + int stylech = styler.StyleAt(nsPos); + if (!(stylech == SCE_AU3_COMMENT)) { + char ch = styler.SafeGetCharAt(nePos); + if (!isspacechar(ch)) { + if (ch == '_') + return true; + else + return false; + } + } + nePos--; // skip to next char + } // End While + return false; +} // IsContinuationLine() + +// +// syntax highlighting logic +static void ColouriseAU3Doc(unsigned int startPos, + int length, int initStyle, + WordList *keywordlists[], + Accessor &styler) { + + WordList &keywords = *keywordlists[0]; + WordList &keywords2 = *keywordlists[1]; + WordList &keywords3 = *keywordlists[2]; + WordList &keywords4 = *keywordlists[3]; + WordList &keywords5 = *keywordlists[4]; + WordList &keywords6 = *keywordlists[5]; + WordList &keywords7 = *keywordlists[6]; + WordList &keywords8 = *keywordlists[7]; + // find the first previous line without continuation character at the end + int lineCurrent = styler.GetLine(startPos); + int s_startPos = startPos; + // When not inside a Block comment: find First line without _ + if (!(initStyle==SCE_AU3_COMMENTBLOCK)) { + while ((lineCurrent > 0 && IsContinuationLine(lineCurrent,styler)) || + (lineCurrent > 1 && IsContinuationLine(lineCurrent-1,styler))) { + lineCurrent--; + startPos = styler.LineStart(lineCurrent); // get start position + initStyle = 0; // reset the start style to 0 + } + } + // Set the new length to include it from the start and set the start position + length = length + s_startPos - startPos; // correct the total length to process + styler.StartAt(startPos); + + StyleContext sc(startPos, length, initStyle, styler); + char si; // string indicator "=1 '=2 + char ni; // Numeric indicator error=9 normal=0 normal+dec=1 hex=2 Enot=3 + char ci; // comment indicator 0=not linecomment(;) + char s_save[100]; + si=0; + ni=0; + ci=0; + //$$$ + for (; sc.More(); sc.Forward()) { + char s[100]; + sc.GetCurrentLowered(s, sizeof(s)); + // ********************************************** + // save the total current word for eof processing + if (IsAWordChar(sc.ch) || sc.ch == '}') + { + strcpy(s_save,s); + int tp = strlen(s_save); + if (tp < 99) { + s_save[tp] = static_cast(tolower(sc.ch)); + s_save[tp+1] = '\0'; + } + } + // ********************************************** + // + switch (sc.state) + { + case SCE_AU3_COMMENTBLOCK: + { + //Reset at line end + if (sc.atLineEnd) { + ci=0; + sc.SetState(SCE_AU3_COMMENTBLOCK); + } + //skip rest of line when a ; is encountered + if (sc.chPrev == ';') { + ci=2; + sc.SetState(SCE_AU3_COMMENTBLOCK); + } + // skip rest of the line + if (ci==2) + break; + // check when first character is detected on the line + if (ci==0) { + if (IsAWordStart(static_cast(sc.ch)) || IsAOperator(static_cast(sc.ch))) { + ci=1; + sc.SetState(SCE_AU3_COMMENTBLOCK); + } + break; + } + if (!(IsAWordChar(sc.ch) || (sc.ch == '-' && strcmp(s, "#comments") == 0))) { + if ((strcmp(s, "#ce")== 0 || strcmp(s, "#comments-end")== 0)) + sc.SetState(SCE_AU3_COMMENT); // set to comment line for the rest of the line + else + ci=2; // line doesn't begin with #CE so skip the rest of the line + } + break; + } + case SCE_AU3_COMMENT: + { + if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);} + break; + } + case SCE_AU3_OPERATOR: + { + // check if its a COMobject + if (sc.chPrev == '.' && IsAWordChar(sc.ch)) { + sc.SetState(SCE_AU3_COMOBJ); + } + else { + sc.SetState(SCE_AU3_DEFAULT); + } + break; + } + case SCE_AU3_SPECIAL: + { + if (sc.ch == ';') {sc.SetState(SCE_AU3_COMMENT);} + if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);} + break; + } + case SCE_AU3_KEYWORD: + { + if (!(IsAWordChar(sc.ch) || (sc.ch == '-' && (strcmp(s, "#comments") == 0 || strcmp(s, "#include") == 0)))) + { + if (!IsTypeCharacter(sc.ch)) + { + if (strcmp(s, "#cs")== 0 || strcmp(s, "#comments-start")== 0 ) + { + sc.ChangeState(SCE_AU3_COMMENTBLOCK); + sc.SetState(SCE_AU3_COMMENTBLOCK); + } + else if (keywords.InList(s)) { + sc.ChangeState(SCE_AU3_KEYWORD); + sc.SetState(SCE_AU3_DEFAULT); + } + else if (keywords2.InList(s)) { + sc.ChangeState(SCE_AU3_FUNCTION); + sc.SetState(SCE_AU3_DEFAULT); + } + else if (keywords3.InList(s)) { + sc.ChangeState(SCE_AU3_MACRO); + sc.SetState(SCE_AU3_DEFAULT); + } + else if (keywords5.InList(s)) { + sc.ChangeState(SCE_AU3_PREPROCESSOR); + sc.SetState(SCE_AU3_DEFAULT); + if (strcmp(s, "#include")== 0) + { + si = 3; // use to determine string start for #inlude <> + } + } + else if (keywords6.InList(s)) { + sc.ChangeState(SCE_AU3_SPECIAL); + sc.SetState(SCE_AU3_SPECIAL); + } + else if ((keywords7.InList(s)) && (!IsAOperator(static_cast(sc.ch)))) { + sc.ChangeState(SCE_AU3_EXPAND); + sc.SetState(SCE_AU3_DEFAULT); + } + else if (keywords8.InList(s)) { + sc.ChangeState(SCE_AU3_UDF); + sc.SetState(SCE_AU3_DEFAULT); + } + else if (strcmp(s, "_") == 0) { + sc.ChangeState(SCE_AU3_OPERATOR); + sc.SetState(SCE_AU3_DEFAULT); + } + else if (!IsAWordChar(sc.ch)) { + sc.ChangeState(SCE_AU3_DEFAULT); + sc.SetState(SCE_AU3_DEFAULT); + } + } + } + if (sc.atLineEnd) { + sc.SetState(SCE_AU3_DEFAULT);} + break; + } + case SCE_AU3_NUMBER: + { + // Numeric indicator error=9 normal=0 normal+dec=1 hex=2 E-not=3 + // + // test for Hex notation + if (strcmp(s, "0") == 0 && (sc.ch == 'x' || sc.ch == 'X') && ni == 0) + { + ni = 2; + break; + } + // test for E notation + if (IsADigit(sc.chPrev) && (sc.ch == 'e' || sc.ch == 'E') && ni <= 1) + { + ni = 3; + break; + } + // Allow Hex characters inside hex numeric strings + if ((ni == 2) && + (sc.ch == 'a' || sc.ch == 'b' || sc.ch == 'c' || sc.ch == 'd' || sc.ch == 'e' || sc.ch == 'f' || + sc.ch == 'A' || sc.ch == 'B' || sc.ch == 'C' || sc.ch == 'D' || sc.ch == 'E' || sc.ch == 'F' )) + { + break; + } + // test for 1 dec point only + if (sc.ch == '.') + { + if (ni==0) + { + ni=1; + } + else + { + ni=9; + } + break; + } + // end of numeric string ? + if (!(IsADigit(sc.ch))) + { + if (ni==9) + { + sc.ChangeState(SCE_AU3_DEFAULT); + } + sc.SetState(SCE_AU3_DEFAULT); + } + break; + } + case SCE_AU3_VARIABLE: + { + // Check if its a COMObject + if (sc.ch == '.' && !IsADigit(sc.chNext)) { + sc.SetState(SCE_AU3_OPERATOR); + } + else if (!IsAWordChar(sc.ch)) { + sc.SetState(SCE_AU3_DEFAULT); + } + break; + } + case SCE_AU3_COMOBJ: + { + if (!(IsAWordChar(sc.ch))) { + sc.SetState(SCE_AU3_DEFAULT); + } + break; + } + case SCE_AU3_STRING: + { + // check for " to end a double qouted string or + // check for ' to end a single qouted string + if ((si == 1 && sc.ch == '\"') || (si == 2 && sc.ch == '\'') || (si == 3 && sc.ch == '>')) + { + sc.ForwardSetState(SCE_AU3_DEFAULT); + si=0; + } + if (sc.atLineEnd) + { + si=0; + // at line end and not found a continuation char then reset to default + int lineCurrent = styler.GetLine(sc.currentPos); + if (!IsContinuationLine(lineCurrent,styler)) + { + sc.SetState(SCE_AU3_DEFAULT); + } + } + // find Sendkeys in a STRING + if (sc.ch == '{' || sc.ch == '+' || sc.ch == '!' || sc.ch == '^' || sc.ch == '#' ) { + sc.SetState(SCE_AU3_SENT);} + break; + } + + case SCE_AU3_SENT: + { + // Send key string ended + if (sc.chPrev == '}' && sc.ch != '}') + { + // set color to SENDKEY when valid sendkey .. else set back to regular string + char sk[100]; + // split {111 222} and return {111} and check if 222 is valid. + // if return code = 1 then invalid 222 so must be string + if (GetSendKey(s,sk)) + { + sc.ChangeState(SCE_AU3_STRING); + } + // if single char between {?} then its ok as sendkey for a single character + else if (strlen(sk) == 3) + { + sc.ChangeState(SCE_AU3_SENT); + } + // if sendkey {111} is in table then ok as sendkey + else if (keywords4.InList(sk)) + { + sc.ChangeState(SCE_AU3_SENT); + } + else + { + sc.ChangeState(SCE_AU3_STRING); + } + sc.SetState(SCE_AU3_STRING); + } + else + { + // check if the start is a valid SendKey start + int nPos = 0; + int nState = 1; + char cTemp; + while (!(nState == 2) && ((cTemp = s[nPos]) != '\0')) + { + if (cTemp == '{' && nState == 1) + { + nState = 2; + } + if (nState == 1 && !(cTemp == '+' || cTemp == '!' || cTemp == '^' || cTemp == '#' )) + { + nState = 0; + } + nPos++; + } + //Verify characters infront of { ... if not assume regular string + if (nState == 1 && (!(sc.ch == '{' || sc.ch == '+' || sc.ch == '!' || sc.ch == '^' || sc.ch == '#' ))) { + sc.ChangeState(SCE_AU3_STRING); + sc.SetState(SCE_AU3_STRING); + } + // If invalid character found then assume its a regular string + if (nState == 0) { + sc.ChangeState(SCE_AU3_STRING); + sc.SetState(SCE_AU3_STRING); + } + } + // check if next portion is again a sendkey + if (sc.atLineEnd) + { + sc.ChangeState(SCE_AU3_STRING); + sc.SetState(SCE_AU3_DEFAULT); + si = 0; // reset string indicator + } + //* check in next characters following a sentkey are again a sent key + // Need this test incase of 2 sentkeys like {F1}{ENTER} but not detect {{} + if (sc.state == SCE_AU3_STRING && (sc.ch == '{' || sc.ch == '+' || sc.ch == '!' || sc.ch == '^' || sc.ch == '#' )) { + sc.SetState(SCE_AU3_SENT);} + // check to see if the string ended... + // Sendkey string isn't complete but the string ended.... + if ((si == 1 && sc.ch == '\"') || (si == 2 && sc.ch == '\'')) + { + sc.ChangeState(SCE_AU3_STRING); + sc.ForwardSetState(SCE_AU3_DEFAULT); + } + break; + } + } //switch (sc.state) + + // Determine if a new state should be entered: + + if (sc.state == SCE_AU3_DEFAULT) + { + if (sc.ch == ';') {sc.SetState(SCE_AU3_COMMENT);} + else if (sc.ch == '#') {sc.SetState(SCE_AU3_KEYWORD);} + else if (sc.ch == '$') {sc.SetState(SCE_AU3_VARIABLE);} + else if (sc.ch == '.' && !IsADigit(sc.chNext)) {sc.SetState(SCE_AU3_OPERATOR);} + else if (sc.ch == '@') {sc.SetState(SCE_AU3_KEYWORD);} + //else if (sc.ch == '_') {sc.SetState(SCE_AU3_KEYWORD);} + else if (sc.ch == '<' && si==3) {sc.SetState(SCE_AU3_STRING);} // string after #include + else if (sc.ch == '\"') { + sc.SetState(SCE_AU3_STRING); + si = 1; } + else if (sc.ch == '\'') { + sc.SetState(SCE_AU3_STRING); + si = 2; } + else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) + { + sc.SetState(SCE_AU3_NUMBER); + ni = 0; + } + else if (IsAWordStart(sc.ch)) {sc.SetState(SCE_AU3_KEYWORD);} + else if (IsAOperator(static_cast(sc.ch))) {sc.SetState(SCE_AU3_OPERATOR);} + else if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);} + } + } //for (; sc.More(); sc.Forward()) + + //************************************* + // Colourize the last word correctly + //************************************* + if (sc.state == SCE_AU3_KEYWORD) + { + if (strcmp(s_save, "#cs")== 0 || strcmp(s_save, "#comments-start")== 0 ) + { + sc.ChangeState(SCE_AU3_COMMENTBLOCK); + sc.SetState(SCE_AU3_COMMENTBLOCK); + } + else if (keywords.InList(s_save)) { + sc.ChangeState(SCE_AU3_KEYWORD); + sc.SetState(SCE_AU3_KEYWORD); + } + else if (keywords2.InList(s_save)) { + sc.ChangeState(SCE_AU3_FUNCTION); + sc.SetState(SCE_AU3_FUNCTION); + } + else if (keywords3.InList(s_save)) { + sc.ChangeState(SCE_AU3_MACRO); + sc.SetState(SCE_AU3_MACRO); + } + else if (keywords5.InList(s_save)) { + sc.ChangeState(SCE_AU3_PREPROCESSOR); + sc.SetState(SCE_AU3_PREPROCESSOR); + } + else if (keywords6.InList(s_save)) { + sc.ChangeState(SCE_AU3_SPECIAL); + sc.SetState(SCE_AU3_SPECIAL); + } + else if (keywords7.InList(s_save) && sc.atLineEnd) { + sc.ChangeState(SCE_AU3_EXPAND); + sc.SetState(SCE_AU3_EXPAND); + } + else if (keywords8.InList(s_save)) { + sc.ChangeState(SCE_AU3_UDF); + sc.SetState(SCE_AU3_UDF); + } + else { + sc.ChangeState(SCE_AU3_DEFAULT); + sc.SetState(SCE_AU3_DEFAULT); + } + } + if (sc.state == SCE_AU3_SENT) + { + // Send key string ended + if (sc.chPrev == '}' && sc.ch != '}') + { + // set color to SENDKEY when valid sendkey .. else set back to regular string + char sk[100]; + // split {111 222} and return {111} and check if 222 is valid. + // if return code = 1 then invalid 222 so must be string + if (GetSendKey(s_save,sk)) + { + sc.ChangeState(SCE_AU3_STRING); + } + // if single char between {?} then its ok as sendkey for a single character + else if (strlen(sk) == 3) + { + sc.ChangeState(SCE_AU3_SENT); + } + // if sendkey {111} is in table then ok as sendkey + else if (keywords4.InList(sk)) + { + sc.ChangeState(SCE_AU3_SENT); + } + else + { + sc.ChangeState(SCE_AU3_STRING); + } + sc.SetState(SCE_AU3_STRING); + } + // check if next portion is again a sendkey + if (sc.atLineEnd) + { + sc.ChangeState(SCE_AU3_STRING); + sc.SetState(SCE_AU3_DEFAULT); + } + } + //************************************* + sc.Complete(); +} + +// +static bool IsStreamCommentStyle(int style) { + return style == SCE_AU3_COMMENT || style == SCE_AU3_COMMENTBLOCK; +} + +// +// Routine to find first none space on the current line and return its Style +// needed for comment lines not starting on pos 1 +static int GetStyleFirstWord(unsigned int szLine, Accessor &styler) +{ + int nsPos = styler.LineStart(szLine); + int nePos = styler.LineStart(szLine+1) - 1; + while (isspacechar(styler.SafeGetCharAt(nsPos)) && nsPos < nePos) + { + nsPos++; // skip to next char + + } // End While + return styler.StyleAt(nsPos); + +} // GetStyleFirstWord() + + +// +static void FoldAU3Doc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) +{ + int endPos = startPos + length; + // get settings from the config files for folding comments and preprocessor lines + bool foldComment = styler.GetPropertyInt("fold.comment") != 0; + bool foldInComment = styler.GetPropertyInt("fold.comment") == 2; + bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + bool foldpreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0; + // Backtrack to previous line in case need to fix its fold status + int lineCurrent = styler.GetLine(startPos); + if (startPos > 0) { + if (lineCurrent > 0) { + lineCurrent--; + startPos = styler.LineStart(lineCurrent); + } + } + // vars for style of previous/current/next lines + int style = GetStyleFirstWord(lineCurrent,styler); + int stylePrev = 0; + // find the first previous line without continuation character at the end + while ((lineCurrent > 0 && IsContinuationLine(lineCurrent,styler)) || + (lineCurrent > 1 && IsContinuationLine(lineCurrent-1,styler))) { + lineCurrent--; + startPos = styler.LineStart(lineCurrent); + } + if (lineCurrent > 0) { + stylePrev = GetStyleFirstWord(lineCurrent-1,styler); + } + // vars for getting first word to check for keywords + bool FirstWordStart = false; + bool FirstWordEnd = false; + char szKeyword[10]=""; + int szKeywordlen = 0; + char szThen[5]=""; + int szThenlen = 0; + bool ThenFoundLast = false; + // var for indentlevel + int levelCurrent = SC_FOLDLEVELBASE; + if (lineCurrent > 0) + levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; + int levelNext = levelCurrent; + // + int visibleChars = 0; + char chNext = styler.SafeGetCharAt(startPos); + char chPrev = ' '; + // + for (int i = startPos; i < endPos; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + if (IsAWordChar(ch)) { + visibleChars++; + } + // get the syle for the current character neede to check in comment + int stylech = styler.StyleAt(i); + // get first word for the line for indent check max 9 characters + if (FirstWordStart && (!(FirstWordEnd))) { + if (!IsAWordChar(ch)) { + FirstWordEnd = true; + szKeyword[szKeywordlen] = '\0'; + } + else { + if (szKeywordlen < 10) { + szKeyword[szKeywordlen++] = static_cast(tolower(ch)); + } + } + } + // start the capture of the first word + if (!(FirstWordStart)) { + if (IsAWordChar(ch) || IsAWordStart(ch) || ch == ';') { + FirstWordStart = true; + szKeyword[szKeywordlen++] = static_cast(tolower(ch)); + } + } + // only process this logic when not in comment section + if (!(stylech == SCE_AU3_COMMENT)) { + if (ThenFoundLast) { + if (IsAWordChar(ch)) { + ThenFoundLast = false; + } + } + // find out if the word "then" is the last on a "if" line + if (FirstWordEnd && strcmp(szKeyword,"if") == 0) { + if (szThenlen == 4) { + szThen[0] = szThen[1]; + szThen[1] = szThen[2]; + szThen[2] = szThen[3]; + szThen[3] = static_cast(tolower(ch)); + if (strcmp(szThen,"then") == 0 ) { + ThenFoundLast = true; + } + } + else { + szThen[szThenlen++] = static_cast(tolower(ch)); + if (szThenlen == 5) { + szThen[4] = '\0'; + } + } + } + } + // End of Line found so process the information + if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) { + // ************************** + // Folding logic for Keywords + // ************************** + // if a keyword is found on the current line and the line doesn't end with _ (continuation) + // and we are not inside a commentblock. + if (szKeywordlen > 0 && (!(chPrev == '_')) && + ((!(IsStreamCommentStyle(style)) || foldInComment)) ) { + szKeyword[szKeywordlen] = '\0'; + // only fold "if" last keyword is "then" (else its a one line if) + if (strcmp(szKeyword,"if") == 0 && ThenFoundLast) { + levelNext++; + } + // create new fold for these words + if (strcmp(szKeyword,"do") == 0 || strcmp(szKeyword,"for") == 0 || + strcmp(szKeyword,"func") == 0 || strcmp(szKeyword,"while") == 0|| + strcmp(szKeyword,"with") == 0 || strcmp(szKeyword,"#region") == 0 ) { + levelNext++; + } + // create double Fold for select&switch because Case will subtract one of the current level + if (strcmp(szKeyword,"select") == 0 || strcmp(szKeyword,"switch") == 0) { + levelNext++; + levelNext++; + } + // end the fold for these words before the current line + if (strcmp(szKeyword,"endfunc") == 0 || strcmp(szKeyword,"endif") == 0 || + strcmp(szKeyword,"next") == 0 || strcmp(szKeyword,"until") == 0 || + strcmp(szKeyword,"endwith") == 0 ||strcmp(szKeyword,"wend") == 0){ + levelNext--; + levelCurrent--; + } + // end the fold for these words before the current line and Start new fold + if (strcmp(szKeyword,"case") == 0 || strcmp(szKeyword,"else") == 0 || + strcmp(szKeyword,"elseif") == 0 ) { + levelCurrent--; + } + // end the double fold for this word before the current line + if (strcmp(szKeyword,"endselect") == 0 || strcmp(szKeyword,"endswitch") == 0 ) { + levelNext--; + levelNext--; + levelCurrent--; + levelCurrent--; + } + // end the fold for these words on the current line + if (strcmp(szKeyword,"#endregion") == 0 ) { + levelNext--; + } + } + // Preprocessor and Comment folding + int styleNext = GetStyleFirstWord(lineCurrent + 1,styler); + // ************************************* + // Folding logic for preprocessor blocks + // ************************************* + // process preprosessor line + if (foldpreprocessor && style == SCE_AU3_PREPROCESSOR) { + if (!(stylePrev == SCE_AU3_PREPROCESSOR) && (styleNext == SCE_AU3_PREPROCESSOR)) { + levelNext++; + } + // fold till the last line for normal comment lines + else if (stylePrev == SCE_AU3_PREPROCESSOR && !(styleNext == SCE_AU3_PREPROCESSOR)) { + levelNext--; + } + } + // ********************************* + // Folding logic for Comment blocks + // ********************************* + if (foldComment && IsStreamCommentStyle(style)) { + // Start of a comment block + if (!(stylePrev==style) && IsStreamCommentStyle(styleNext) && styleNext==style) { + levelNext++; + } + // fold till the last line for normal comment lines + else if (IsStreamCommentStyle(stylePrev) + && !(styleNext == SCE_AU3_COMMENT) + && stylePrev == SCE_AU3_COMMENT + && style == SCE_AU3_COMMENT) { + levelNext--; + } + // fold till the one but last line for Blockcomment lines + else if (IsStreamCommentStyle(stylePrev) + && !(styleNext == SCE_AU3_COMMENTBLOCK) + && style == SCE_AU3_COMMENTBLOCK) { + levelNext--; + levelCurrent--; + } + } + int levelUse = levelCurrent; + int lev = levelUse | levelNext << 16; + if (visibleChars == 0 && foldCompact) + lev |= SC_FOLDLEVELWHITEFLAG; + if (levelUse < levelNext) { + lev |= SC_FOLDLEVELHEADERFLAG; + } + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + // reset values for the next line + lineCurrent++; + stylePrev = style; + style = styleNext; + levelCurrent = levelNext; + visibleChars = 0; + // if the last character is an Underscore then don't reset since the line continues on the next line. + if (!(chPrev == '_')) { + szKeywordlen = 0; + szThenlen = 0; + FirstWordStart = false; + FirstWordEnd = false; + ThenFoundLast = false; + } + } + // save the last processed character + if (!isspacechar(ch)) { + chPrev = ch; + visibleChars++; + } + } +} + + +// + +static const char * const AU3WordLists[] = { + "#autoit keywords", + "#autoit functions", + "#autoit macros", + "#autoit Sent keys", + "#autoit Pre-processors", + "#autoit Special", + "#autoit Expand", + "#autoit UDF", + 0 +}; +LexerModule lmAU3(SCLEX_AU3, ColouriseAU3Doc, "au3", FoldAU3Doc , AU3WordLists); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAVE.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAVE.cxx new file mode 100644 index 00000000..aa57f169 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAVE.cxx @@ -0,0 +1,225 @@ +// SciTE - Scintilla based Text Editor +/** @file LexAVE.cxx + ** Lexer for Avenue. + ** + ** Written by Alexey Yutkin . + **/ +// Copyright 1998-2002 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + + + +static inline bool IsAWordChar(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_'); +} +static inline bool IsEnumChar(const int ch) { + return (ch < 0x80) && (isalnum(ch)|| ch == '_'); +} +static inline bool IsANumberChar(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '.' ); +} + +inline bool IsAWordStart(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '_'); +} + +inline bool isAveOperator(char ch) { + if (isalnum(ch)) + return false; + // '.' left out as it is used to make up numbers + if (ch == '*' || ch == '/' || ch == '-' || ch == '+' || + ch == '(' || ch == ')' || ch == '=' || + ch == '{' || ch == '}' || + ch == '[' || ch == ']' || ch == ';' || + ch == '<' || ch == '>' || ch == ',' || + ch == '.' ) + return true; + return false; +} + +static void ColouriseAveDoc( + unsigned int startPos, + int length, + int initStyle, + WordList *keywordlists[], + Accessor &styler) { + + WordList &keywords = *keywordlists[0]; + WordList &keywords2 = *keywordlists[1]; + WordList &keywords3 = *keywordlists[2]; + WordList &keywords4 = *keywordlists[3]; + WordList &keywords5 = *keywordlists[4]; + WordList &keywords6 = *keywordlists[5]; + + // Do not leak onto next line + if (initStyle == SCE_AVE_STRINGEOL) { + initStyle = SCE_AVE_DEFAULT; + } + + StyleContext sc(startPos, length, initStyle, styler); + + for (; sc.More(); sc.Forward()) { + if (sc.atLineEnd) { + // Update the line state, so it can be seen by next line + int currentLine = styler.GetLine(sc.currentPos); + styler.SetLineState(currentLine, 0); + } + if (sc.atLineStart && (sc.state == SCE_AVE_STRING)) { + // Prevent SCE_AVE_STRINGEOL from leaking back to previous line + sc.SetState(SCE_AVE_STRING); + } + + + // Determine if the current state should terminate. + if (sc.state == SCE_AVE_OPERATOR) { + sc.SetState(SCE_AVE_DEFAULT); + } else if (sc.state == SCE_AVE_NUMBER) { + if (!IsANumberChar(sc.ch)) { + sc.SetState(SCE_AVE_DEFAULT); + } + } else if (sc.state == SCE_AVE_ENUM) { + if (!IsEnumChar(sc.ch)) { + sc.SetState(SCE_AVE_DEFAULT); + } + } else if (sc.state == SCE_AVE_IDENTIFIER) { + if (!IsAWordChar(sc.ch) || (sc.ch == '.')) { + char s[100]; + //sc.GetCurrent(s, sizeof(s)); + sc.GetCurrentLowered(s, sizeof(s)); + if (keywords.InList(s)) { + sc.ChangeState(SCE_AVE_WORD); + } else if (keywords2.InList(s)) { + sc.ChangeState(SCE_AVE_WORD2); + } else if (keywords3.InList(s)) { + sc.ChangeState(SCE_AVE_WORD3); + } else if (keywords4.InList(s)) { + sc.ChangeState(SCE_AVE_WORD4); + } else if (keywords5.InList(s)) { + sc.ChangeState(SCE_AVE_WORD5); + } else if (keywords6.InList(s)) { + sc.ChangeState(SCE_AVE_WORD6); + } + sc.SetState(SCE_AVE_DEFAULT); + } + } else if (sc.state == SCE_AVE_COMMENT) { + if (sc.atLineEnd) { + sc.SetState(SCE_AVE_DEFAULT); + } + } else if (sc.state == SCE_AVE_STRING) { + if (sc.ch == '\"') { + sc.ForwardSetState(SCE_AVE_DEFAULT); + } else if (sc.atLineEnd) { + sc.ChangeState(SCE_AVE_STRINGEOL); + sc.ForwardSetState(SCE_AVE_DEFAULT); + } + } + + // Determine if a new state should be entered. + if (sc.state == SCE_AVE_DEFAULT) { + if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { + sc.SetState(SCE_AVE_NUMBER); + } else if (IsAWordStart(sc.ch)) { + sc.SetState(SCE_AVE_IDENTIFIER); + } else if (sc.Match('\"')) { + sc.SetState(SCE_AVE_STRING); + } else if (sc.Match('\'')) { + sc.SetState(SCE_AVE_COMMENT); + sc.Forward(); + } else if (isAveOperator(static_cast(sc.ch))) { + sc.SetState(SCE_AVE_OPERATOR); + } else if (sc.Match('#')) { + sc.SetState(SCE_AVE_ENUM); + sc.Forward(); + } + } + } + sc.Complete(); +} + +static void FoldAveDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[], + Accessor &styler) { + unsigned int lengthDoc = startPos + length; + int visibleChars = 0; + int lineCurrent = styler.GetLine(startPos); + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + char chNext = static_cast(tolower(styler[startPos])); + bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + int styleNext = styler.StyleAt(startPos); + char s[10]; + + for (unsigned int i = startPos; i < lengthDoc; i++) { + char ch = static_cast(tolower(chNext)); + chNext = static_cast(tolower(styler.SafeGetCharAt(i + 1))); + int style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + if (style == SCE_AVE_WORD) { + if (ch == 't' || ch == 'f' || ch == 'w' || ch == 'e') { + for (unsigned int j = 0; j < 6; j++) { + if (!iswordchar(styler[i + j])) { + break; + } + s[j] = static_cast(tolower(styler[i + j])); + s[j + 1] = '\0'; + } + + if ((strcmp(s, "then") == 0) || (strcmp(s, "for") == 0) || (strcmp(s, "while") == 0)) { + levelCurrent++; + } + if ((strcmp(s, "end") == 0) || (strcmp(s, "elseif") == 0)) { + // Normally "elseif" and "then" will be on the same line and will cancel + // each other out. // As implemented, this does not support fold.at.else. + levelCurrent--; + } + } + } else if (style == SCE_AVE_OPERATOR) { + if (ch == '{' || ch == '(') { + levelCurrent++; + } else if (ch == '}' || ch == ')') { + levelCurrent--; + } + } + + if (atEOL) { + int lev = levelPrev; + if (visibleChars == 0 && foldCompact) { + lev |= SC_FOLDLEVELWHITEFLAG; + } + if ((levelCurrent > levelPrev) && (visibleChars > 0)) { + lev |= SC_FOLDLEVELHEADERFLAG; + } + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + levelPrev = levelCurrent; + visibleChars = 0; + } + if (!isspacechar(ch)) { + visibleChars++; + } + } + // Fill in the real level of the next line, keeping the current flags as they will be filled in later + + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + styler.SetLevel(lineCurrent, levelPrev | flagsNext); +} + +LexerModule lmAVE(SCLEX_AVE, ColouriseAveDoc, "ave", FoldAveDoc); + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAda.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAda.cxx new file mode 100644 index 00000000..ac284ba7 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAda.cxx @@ -0,0 +1,520 @@ +// Scintilla source code edit control +/** @file LexAda.cxx + ** Lexer for Ada 95 + **/ +// Copyright 2002 by Sergey Koshcheyev +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include + +#include "Platform.h" + +#include "Accessor.h" +#include "StyleContext.h" +#include "PropSet.h" +#include "KeyWords.h" +#include "SciLexer.h" +#include "SString.h" + +/* + * Interface + */ + +static void ColouriseDocument( + unsigned int startPos, + int length, + int initStyle, + WordList *keywordlists[], + Accessor &styler); + +static const char * const adaWordListDesc[] = { + "Keywords", + 0 +}; + +LexerModule lmAda(SCLEX_ADA, ColouriseDocument, "ada", NULL, adaWordListDesc); + +/* + * Implementation + */ + +// Functions that have apostropheStartsAttribute as a parameter set it according to whether +// an apostrophe encountered after processing the current token will start an attribute or +// a character literal. +static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute); +static void ColouriseComment(StyleContext& sc, bool& apostropheStartsAttribute); +static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL); +static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute); +static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute); +static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute); +static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute); +static void ColouriseWhiteSpace(StyleContext& sc, bool& apostropheStartsAttribute); +static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute); + +static inline bool IsDelimiterCharacter(int ch); +static inline bool IsNumberStartCharacter(int ch); +static inline bool IsNumberCharacter(int ch); +static inline bool IsSeparatorOrDelimiterCharacter(int ch); +static bool IsValidIdentifier(const SString& identifier); +static bool IsValidNumber(const SString& number); +static inline bool IsWordStartCharacter(int ch); +static inline bool IsWordCharacter(int ch); + +static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute) { + apostropheStartsAttribute = true; + + sc.SetState(SCE_ADA_CHARACTER); + + // Skip the apostrophe and one more character (so that '' is shown as non-terminated and ''' + // is handled correctly) + sc.Forward(); + sc.Forward(); + + ColouriseContext(sc, '\'', SCE_ADA_CHARACTEREOL); +} + +static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL) { + while (!sc.atLineEnd && !sc.Match(chEnd)) { + sc.Forward(); + } + + if (!sc.atLineEnd) { + sc.ForwardSetState(SCE_ADA_DEFAULT); + } else { + sc.ChangeState(stateEOL); + } +} + +static void ColouriseComment(StyleContext& sc, bool& /*apostropheStartsAttribute*/) { + // Apostrophe meaning is not changed, but the parameter is present for uniformity + + sc.SetState(SCE_ADA_COMMENTLINE); + + while (!sc.atLineEnd) { + sc.Forward(); + } +} + +static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute) { + apostropheStartsAttribute = sc.Match (')'); + sc.SetState(SCE_ADA_DELIMITER); + sc.ForwardSetState(SCE_ADA_DEFAULT); +} + +static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) { + apostropheStartsAttribute = false; + + sc.SetState(SCE_ADA_LABEL); + + // Skip "<<" + sc.Forward(); + sc.Forward(); + + SString identifier; + + while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) { + identifier += static_cast(tolower(sc.ch)); + sc.Forward(); + } + + // Skip ">>" + if (sc.Match('>', '>')) { + sc.Forward(); + sc.Forward(); + } else { + sc.ChangeState(SCE_ADA_ILLEGAL); + } + + // If the name is an invalid identifier or a keyword, then make it invalid label + if (!IsValidIdentifier(identifier) || keywords.InList(identifier.c_str())) { + sc.ChangeState(SCE_ADA_ILLEGAL); + } + + sc.SetState(SCE_ADA_DEFAULT); + +} + +static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute) { + apostropheStartsAttribute = true; + + SString number; + sc.SetState(SCE_ADA_NUMBER); + + // Get all characters up to a delimiter or a separator, including points, but excluding + // double points (ranges). + while (!IsSeparatorOrDelimiterCharacter(sc.ch) || (sc.ch == '.' && sc.chNext != '.')) { + number += static_cast(sc.ch); + sc.Forward(); + } + + // Special case: exponent with sign + if ((sc.chPrev == 'e' || sc.chPrev == 'E') && + (sc.ch == '+' || sc.ch == '-')) { + number += static_cast(sc.ch); + sc.Forward (); + + while (!IsSeparatorOrDelimiterCharacter(sc.ch)) { + number += static_cast(sc.ch); + sc.Forward(); + } + } + + if (!IsValidNumber(number)) { + sc.ChangeState(SCE_ADA_ILLEGAL); + } + + sc.SetState(SCE_ADA_DEFAULT); +} + +static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute) { + apostropheStartsAttribute = true; + + sc.SetState(SCE_ADA_STRING); + sc.Forward(); + + ColouriseContext(sc, '"', SCE_ADA_STRINGEOL); +} + +static void ColouriseWhiteSpace(StyleContext& sc, bool& /*apostropheStartsAttribute*/) { + // Apostrophe meaning is not changed, but the parameter is present for uniformity + sc.SetState(SCE_ADA_DEFAULT); + sc.ForwardSetState(SCE_ADA_DEFAULT); +} + +static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) { + apostropheStartsAttribute = true; + sc.SetState(SCE_ADA_IDENTIFIER); + + SString word; + + while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) { + word += static_cast(tolower(sc.ch)); + sc.Forward(); + } + + if (!IsValidIdentifier(word)) { + sc.ChangeState(SCE_ADA_ILLEGAL); + + } else if (keywords.InList(word.c_str())) { + sc.ChangeState(SCE_ADA_WORD); + + if (word != "all") { + apostropheStartsAttribute = false; + } + } + + sc.SetState(SCE_ADA_DEFAULT); +} + +// +// ColouriseDocument +// + +static void ColouriseDocument( + unsigned int startPos, + int length, + int initStyle, + WordList *keywordlists[], + Accessor &styler) { + WordList &keywords = *keywordlists[0]; + + StyleContext sc(startPos, length, initStyle, styler); + + int lineCurrent = styler.GetLine(startPos); + bool apostropheStartsAttribute = (styler.GetLineState(lineCurrent) & 1) != 0; + + while (sc.More()) { + if (sc.atLineEnd) { + // Go to the next line + sc.Forward(); + lineCurrent++; + + // Remember the line state for future incremental lexing + styler.SetLineState(lineCurrent, apostropheStartsAttribute); + + // Don't continue any styles on the next line + sc.SetState(SCE_ADA_DEFAULT); + } + + // Comments + if (sc.Match('-', '-')) { + ColouriseComment(sc, apostropheStartsAttribute); + + // Strings + } else if (sc.Match('"')) { + ColouriseString(sc, apostropheStartsAttribute); + + // Characters + } else if (sc.Match('\'') && !apostropheStartsAttribute) { + ColouriseCharacter(sc, apostropheStartsAttribute); + + // Labels + } else if (sc.Match('<', '<')) { + ColouriseLabel(sc, keywords, apostropheStartsAttribute); + + // Whitespace + } else if (IsASpace(sc.ch)) { + ColouriseWhiteSpace(sc, apostropheStartsAttribute); + + // Delimiters + } else if (IsDelimiterCharacter(sc.ch)) { + ColouriseDelimiter(sc, apostropheStartsAttribute); + + // Numbers + } else if (IsADigit(sc.ch) || sc.ch == '#') { + ColouriseNumber(sc, apostropheStartsAttribute); + + // Keywords or identifiers + } else { + ColouriseWord(sc, keywords, apostropheStartsAttribute); + } + } + + sc.Complete(); +} + +static inline bool IsDelimiterCharacter(int ch) { + switch (ch) { + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case '-': + case '.': + case '/': + case ':': + case ';': + case '<': + case '=': + case '>': + case '|': + return true; + default: + return false; + } +} + +static inline bool IsNumberCharacter(int ch) { + return IsNumberStartCharacter(ch) || + ch == '_' || + ch == '.' || + ch == '#' || + (ch >= 'a' && ch <= 'f') || + (ch >= 'A' && ch <= 'F'); +} + +static inline bool IsNumberStartCharacter(int ch) { + return IsADigit(ch); +} + +static inline bool IsSeparatorOrDelimiterCharacter(int ch) { + return IsASpace(ch) || IsDelimiterCharacter(ch); +} + +static bool IsValidIdentifier(const SString& identifier) { + // First character can't be '_', so initialize the flag to true + bool lastWasUnderscore = true; + + size_t length = identifier.length(); + + // Zero-length identifiers are not valid (these can occur inside labels) + if (length == 0) { + return false; + } + + // Check for valid character at the start + if (!IsWordStartCharacter(identifier[0])) { + return false; + } + + // Check for only valid characters and no double underscores + for (size_t i = 0; i < length; i++) { + if (!IsWordCharacter(identifier[i]) || + (identifier[i] == '_' && lastWasUnderscore)) { + return false; + } + lastWasUnderscore = identifier[i] == '_'; + } + + // Check for underscore at the end + if (lastWasUnderscore == true) { + return false; + } + + // All checks passed + return true; +} + +static bool IsValidNumber(const SString& number) { + int hashPos = number.search("#"); + bool seenDot = false; + + size_t i = 0; + size_t length = number.length(); + + if (length == 0) + return false; // Just in case + + // Decimal number + if (hashPos == -1) { + bool canBeSpecial = false; + + for (; i < length; i++) { + if (number[i] == '_') { + if (!canBeSpecial) { + return false; + } + canBeSpecial = false; + } else if (number[i] == '.') { + if (!canBeSpecial || seenDot) { + return false; + } + canBeSpecial = false; + seenDot = true; + } else if (IsADigit(number[i])) { + canBeSpecial = true; + } else { + break; + } + } + + if (!canBeSpecial) + return false; + } else { + // Based number + bool canBeSpecial = false; + int base = 0; + + // Parse base + for (; i < length; i++) { + int ch = number[i]; + if (ch == '_') { + if (!canBeSpecial) + return false; + canBeSpecial = false; + } else if (IsADigit(ch)) { + base = base * 10 + (ch - '0'); + if (base > 16) + return false; + canBeSpecial = true; + } else if (ch == '#' && canBeSpecial) { + break; + } else { + return false; + } + } + + if (base < 2) + return false; + if (i == length) + return false; + + i++; // Skip over '#' + + // Parse number + canBeSpecial = false; + + for (; i < length; i++) { + int ch = tolower(number[i]); + + if (ch == '_') { + if (!canBeSpecial) { + return false; + } + canBeSpecial = false; + + } else if (ch == '.') { + if (!canBeSpecial || seenDot) { + return false; + } + canBeSpecial = false; + seenDot = true; + + } else if (IsADigit(ch)) { + if (ch - '0' >= base) { + return false; + } + canBeSpecial = true; + + } else if (ch >= 'a' && ch <= 'f') { + if (ch - 'a' + 10 >= base) { + return false; + } + canBeSpecial = true; + + } else if (ch == '#' && canBeSpecial) { + break; + + } else { + return false; + } + } + + if (i == length) { + return false; + } + + i++; + } + + // Exponent (optional) + if (i < length) { + if (number[i] != 'e' && number[i] != 'E') + return false; + + i++; // Move past 'E' + + if (i == length) { + return false; + } + + if (number[i] == '+') + i++; + else if (number[i] == '-') { + if (seenDot) { + i++; + } else { + return false; // Integer literals should not have negative exponents + } + } + + if (i == length) { + return false; + } + + bool canBeSpecial = false; + + for (; i < length; i++) { + if (number[i] == '_') { + if (!canBeSpecial) { + return false; + } + canBeSpecial = false; + } else if (IsADigit(number[i])) { + canBeSpecial = true; + } else { + return false; + } + } + + if (!canBeSpecial) + return false; + } + + // if i == length, number was parsed successfully. + return i == length; +} + +static inline bool IsWordCharacter(int ch) { + return IsWordStartCharacter(ch) || IsADigit(ch); +} + +static inline bool IsWordStartCharacter(int ch) { + return (isascii(ch) && isalpha(ch)) || ch == '_'; +} diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAsm.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAsm.cxx new file mode 100644 index 00000000..dad70628 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAsm.cxx @@ -0,0 +1,177 @@ +// Scintilla source code edit control +/** @file LexAsm.cxx + ** Lexer for Assembler, just for the MASM syntax + ** Written by The Black Horus + ** Enhancements and NASM stuff by Kein-Hong Man, 2003-10 + ** SCE_ASM_COMMENTBLOCK and SCE_ASM_CHARACTER are for future GNU as colouring + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + + +static inline bool IsAWordChar(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '.' || + ch == '_' || ch == '?'); +} + +static inline bool IsAWordStart(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '.' || + ch == '%' || ch == '@' || ch == '$' || ch == '?'); +} + +static inline bool IsAsmOperator(char ch) { + if (isalnum(ch)) + return false; + // '.' left out as it is used to make up numbers + if (ch == '*' || ch == '/' || ch == '-' || ch == '+' || + ch == '(' || ch == ')' || ch == '=' || ch == '^' || + ch == '[' || ch == ']' || ch == '<' || ch == '&' || + ch == '>' || ch == ',' || ch == '|' || ch == '~' || + ch == '%' || ch == ':') + return true; + return false; +} + +static void ColouriseAsmDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) { + + WordList &cpuInstruction = *keywordlists[0]; + WordList &mathInstruction = *keywordlists[1]; + WordList ®isters = *keywordlists[2]; + WordList &directive = *keywordlists[3]; + WordList &directiveOperand = *keywordlists[4]; + WordList &extInstruction = *keywordlists[5]; + + // Do not leak onto next line + if (initStyle == SCE_ASM_STRINGEOL) + initStyle = SCE_ASM_DEFAULT; + + StyleContext sc(startPos, length, initStyle, styler); + + for (; sc.More(); sc.Forward()) + { + + // Prevent SCE_ASM_STRINGEOL from leaking back to previous line + if (sc.atLineStart && (sc.state == SCE_ASM_STRING)) { + sc.SetState(SCE_ASM_STRING); + } else if (sc.atLineStart && (sc.state == SCE_ASM_CHARACTER)) { + sc.SetState(SCE_ASM_CHARACTER); + } + + // Handle line continuation generically. + if (sc.ch == '\\') { + if (sc.chNext == '\n' || sc.chNext == '\r') { + sc.Forward(); + if (sc.ch == '\r' && sc.chNext == '\n') { + sc.Forward(); + } + continue; + } + } + + // Determine if the current state should terminate. + if (sc.state == SCE_ASM_OPERATOR) { + if (!IsAsmOperator(static_cast(sc.ch))) { + sc.SetState(SCE_ASM_DEFAULT); + } + }else if (sc.state == SCE_ASM_NUMBER) { + if (!IsAWordChar(sc.ch)) { + sc.SetState(SCE_ASM_DEFAULT); + } + } else if (sc.state == SCE_ASM_IDENTIFIER) { + if (!IsAWordChar(sc.ch) ) { + char s[100]; + sc.GetCurrentLowered(s, sizeof(s)); + + if (cpuInstruction.InList(s)) { + sc.ChangeState(SCE_ASM_CPUINSTRUCTION); + } else if (mathInstruction.InList(s)) { + sc.ChangeState(SCE_ASM_MATHINSTRUCTION); + } else if (registers.InList(s)) { + sc.ChangeState(SCE_ASM_REGISTER); + } else if (directive.InList(s)) { + sc.ChangeState(SCE_ASM_DIRECTIVE); + } else if (directiveOperand.InList(s)) { + sc.ChangeState(SCE_ASM_DIRECTIVEOPERAND); + } else if (extInstruction.InList(s)) { + sc.ChangeState(SCE_ASM_EXTINSTRUCTION); + } + sc.SetState(SCE_ASM_DEFAULT); + } + } + else if (sc.state == SCE_ASM_COMMENT ) { + if (sc.atLineEnd) { + sc.SetState(SCE_ASM_DEFAULT); + } + } else if (sc.state == SCE_ASM_STRING) { + if (sc.ch == '\\') { + if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { + sc.Forward(); + } + } else if (sc.ch == '\"') { + sc.ForwardSetState(SCE_ASM_DEFAULT); + } else if (sc.atLineEnd) { + sc.ChangeState(SCE_ASM_STRINGEOL); + sc.ForwardSetState(SCE_ASM_DEFAULT); + } + } else if (sc.state == SCE_ASM_CHARACTER) { + if (sc.ch == '\\') { + if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { + sc.Forward(); + } + } else if (sc.ch == '\'') { + sc.ForwardSetState(SCE_ASM_DEFAULT); + } else if (sc.atLineEnd) { + sc.ChangeState(SCE_ASM_STRINGEOL); + sc.ForwardSetState(SCE_ASM_DEFAULT); + } + } + + // Determine if a new state should be entered. + if (sc.state == SCE_ASM_DEFAULT) { + if (sc.ch == ';'){ + sc.SetState(SCE_ASM_COMMENT); + } else if (isdigit(sc.ch) || (sc.ch == '.' && isdigit(sc.chNext))) { + sc.SetState(SCE_ASM_NUMBER); + } else if (IsAWordStart(sc.ch)) { + sc.SetState(SCE_ASM_IDENTIFIER); + } else if (sc.ch == '\"') { + sc.SetState(SCE_ASM_STRING); + } else if (sc.ch == '\'') { + sc.SetState(SCE_ASM_CHARACTER); + } else if (IsAsmOperator(static_cast(sc.ch))) { + sc.SetState(SCE_ASM_OPERATOR); + } + } + + } + sc.Complete(); +} + +static const char * const asmWordListDesc[] = { + "CPU instructions", + "FPU instructions", + "Registers", + "Directives", + "Directive operands", + "Extended instructions", + 0 +}; + +LexerModule lmAsm(SCLEX_ASM, ColouriseAsmDoc, "asm", 0, asmWordListDesc); + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAsn1.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAsn1.cxx new file mode 100644 index 00000000..a4b08617 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexAsn1.cxx @@ -0,0 +1,181 @@ +// Scintilla source code edit control +/** @file LexAsn1.cxx + ** Lexer for ASN.1 + **/ +// Copyright 2004 by Herr Pfarrer rpfarrer yahoo de +// Last Updated: 20/07/2004 +// The License.txt file describes the conditions under which this software may be distributed. +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +// Some char test functions +static bool isAsn1Number(int ch) +{ + return (ch >= '0' && ch <= '9'); +} + +static bool isAsn1Letter(int ch) +{ + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); +} + +static bool isAsn1Char(int ch) +{ + return (ch == '-' ) || isAsn1Number(ch) || isAsn1Letter (ch); +} + +// +// Function determining the color of a given code portion +// Based on a "state" +// +static void ColouriseAsn1Doc(unsigned int startPos, int length, int initStyle, WordList *keywordLists[], Accessor &styler) +{ + // The keywords + WordList &Keywords = *keywordLists[0]; + WordList &Attributes = *keywordLists[1]; + WordList &Descriptors = *keywordLists[2]; + WordList &Types = *keywordLists[3]; + + // Parse the whole buffer character by character using StyleContext + StyleContext sc(startPos, length, initStyle, styler); + for (; sc.More(); sc.Forward()) + { + // The state engine + switch (sc.state) + { + case SCE_ASN1_DEFAULT: // Plain characters +asn1_default: + if (sc.ch == '-' && sc.chNext == '-') + // A comment begins here + sc.SetState(SCE_ASN1_COMMENT); + else if (sc.ch == '"') + // A string begins here + sc.SetState(SCE_ASN1_STRING); + else if (isAsn1Number (sc.ch)) + // A number starts here (identifier should start with a letter in ASN.1) + sc.SetState(SCE_ASN1_SCALAR); + else if (isAsn1Char (sc.ch)) + // An identifier starts here (identifier always start with a letter) + sc.SetState(SCE_ASN1_IDENTIFIER); + else if (sc.ch == ':') + // A ::= operator starts here + sc.SetState(SCE_ASN1_OPERATOR); + break; + case SCE_ASN1_COMMENT: // A comment + if (sc.ch == '\r' || sc.ch == '\n') + // A comment ends here + sc.SetState(SCE_ASN1_DEFAULT); + break; + case SCE_ASN1_IDENTIFIER: // An identifier (keyword, attribute, descriptor or type) + if (!isAsn1Char (sc.ch)) + { + // The end of identifier is here: we can look for it in lists by now and change its state + char s[100]; + sc.GetCurrent(s, sizeof(s)); + if (Keywords.InList(s)) + // It's a keyword, change its state + sc.ChangeState(SCE_ASN1_KEYWORD); + else if (Attributes.InList(s)) + // It's an attribute, change its state + sc.ChangeState(SCE_ASN1_ATTRIBUTE); + else if (Descriptors.InList(s)) + // It's a descriptor, change its state + sc.ChangeState(SCE_ASN1_DESCRIPTOR); + else if (Types.InList(s)) + // It's a type, change its state + sc.ChangeState(SCE_ASN1_TYPE); + + // Set to default now + sc.SetState(SCE_ASN1_DEFAULT); + } + break; + case SCE_ASN1_STRING: // A string delimited by "" + if (sc.ch == '"') + { + // A string ends here + sc.ForwardSetState(SCE_ASN1_DEFAULT); + + // To correctly manage a char sticking to the string quote + goto asn1_default; + } + break; + case SCE_ASN1_SCALAR: // A plain number + if (!isAsn1Number (sc.ch)) + // A number ends here + sc.SetState(SCE_ASN1_DEFAULT); + break; + case SCE_ASN1_OPERATOR: // The affectation operator ::= and wath follows (eg: ::= { org 6 } OID or ::= 12 trap) + if (sc.ch == '{') + { + // An OID definition starts here: enter the sub loop + for (; sc.More(); sc.Forward()) + { + if (isAsn1Number (sc.ch) && (!isAsn1Char (sc.chPrev) || isAsn1Number (sc.chPrev))) + // The OID number is highlighted + sc.SetState(SCE_ASN1_OID); + else if (isAsn1Char (sc.ch)) + // The OID parent identifier is plain + sc.SetState(SCE_ASN1_IDENTIFIER); + else + sc.SetState(SCE_ASN1_DEFAULT); + + if (sc.ch == '}') + // Here ends the OID and the operator sub loop: go back to main loop + break; + } + } + else if (isAsn1Number (sc.ch)) + { + // A trap number definition starts here: enter the sub loop + for (; sc.More(); sc.Forward()) + { + if (isAsn1Number (sc.ch)) + // The trap number is highlighted + sc.SetState(SCE_ASN1_OID); + else + { + // The number ends here: go back to main loop + sc.SetState(SCE_ASN1_DEFAULT); + break; + } + } + } + else if (sc.ch != ':' && sc.ch != '=' && sc.ch != ' ') + // The operator doesn't imply an OID definition nor a trap, back to main loop + goto asn1_default; // To be sure to handle actually the state change + break; + } + } + sc.Complete(); +} + +static void FoldAsn1Doc(unsigned int, int, int, WordList *[], Accessor &styler) +{ + // No folding enabled, no reason to continue... + if( styler.GetPropertyInt("fold") == 0 ) + return; + + // No folding implemented: doesn't make sense for ASN.1 +} + +static const char * const asn1WordLists[] = { + "Keywords", + "Attributes", + "Descriptors", + "Types", + 0, }; + + +LexerModule lmAns1(SCLEX_ASN1, ColouriseAsn1Doc, "asn1", FoldAsn1Doc, asn1WordLists); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBaan.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBaan.cxx new file mode 100644 index 00000000..e38a2715 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBaan.cxx @@ -0,0 +1,189 @@ +// Scintilla source code edit control +/** @file LexBaan.cxx + ** Lexer for Baan. + ** Based heavily on LexCPP.cxx + **/ +// Copyright 2001- by Vamsi Potluru & Praveen Ambekar +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +static inline bool IsAWordChar(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch == '$' || ch == ':'); +} + +static inline bool IsAWordStart(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '_'); +} + +static void ColouriseBaanDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) { + + WordList &keywords = *keywordlists[0]; + WordList &keywords2 = *keywordlists[1]; + bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0; + + if (initStyle == SCE_BAAN_STRINGEOL) // Does not leak onto next line + initStyle = SCE_BAAN_DEFAULT; + + int visibleChars = 0; + + StyleContext sc(startPos, length, initStyle, styler); + + for (; sc.More(); sc.Forward()) { + + if (sc.state == SCE_BAAN_OPERATOR) { + sc.SetState(SCE_BAAN_DEFAULT); + } else if (sc.state == SCE_BAAN_NUMBER) { + if (!IsAWordChar(sc.ch)) { + sc.SetState(SCE_BAAN_DEFAULT); + } + } else if (sc.state == SCE_BAAN_IDENTIFIER) { + if (!IsAWordChar(sc.ch)) { + char s[100]; + sc.GetCurrentLowered(s, sizeof(s)); + if (keywords.InList(s)) { + sc.ChangeState(SCE_BAAN_WORD); + } else if (keywords2.InList(s)) { + sc.ChangeState(SCE_BAAN_WORD2); + } + sc.SetState(SCE_BAAN_DEFAULT); + } + } else if (sc.state == SCE_BAAN_PREPROCESSOR) { + if (stylingWithinPreprocessor) { + if (IsASpace(sc.ch)) { + sc.SetState(SCE_BAAN_DEFAULT); + } + } else { + if (sc.atLineEnd && (sc.chNext != '^')) { + sc.SetState(SCE_BAAN_DEFAULT); + } + } + } else if (sc.state == SCE_BAAN_COMMENT) { + if (sc.atLineEnd) { + sc.SetState(SCE_BAAN_DEFAULT); + } + } else if (sc.state == SCE_BAAN_COMMENTDOC) { + if (sc.MatchIgnoreCase("enddllusage")) { + for (unsigned int i = 0; i < 10; i++){ + sc.Forward(); + } + sc.ForwardSetState(SCE_BAAN_DEFAULT); + } + } else if (sc.state == SCE_BAAN_STRING) { + if (sc.ch == '\"') { + sc.ForwardSetState(SCE_BAAN_DEFAULT); + } else if ((sc.atLineEnd) && (sc.chNext != '^')) { + sc.ChangeState(SCE_BAAN_STRINGEOL); + sc.ForwardSetState(SCE_C_DEFAULT); + visibleChars = 0; + } + } + + if (sc.state == SCE_BAAN_DEFAULT) { + if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { + sc.SetState(SCE_BAAN_NUMBER); + } else if (sc.MatchIgnoreCase("dllusage")){ + sc.SetState(SCE_BAAN_COMMENTDOC); + do { + sc.Forward(); + } while ((!sc.atLineEnd) && sc.More()); + } else if (IsAWordStart(sc.ch)) { + sc.SetState(SCE_BAAN_IDENTIFIER); + } else if (sc.Match('|')){ + sc.SetState(SCE_BAAN_COMMENT); + } else if (sc.ch == '\"') { + sc.SetState(SCE_BAAN_STRING); + } else if (sc.ch == '#' && visibleChars == 0) { + // Preprocessor commands are alone on their line + sc.SetState(SCE_BAAN_PREPROCESSOR); + // Skip whitespace between # and preprocessor word + do { + sc.Forward(); + } while (IsASpace(sc.ch) && sc.More()); + } else if (isoperator(static_cast(sc.ch))) { + sc.SetState(SCE_BAAN_OPERATOR); + } + } + if (sc.atLineEnd) { + // Reset states to begining of colourise so no surprises + // if different sets of lines lexed. + visibleChars = 0; + } + if (!IsASpace(sc.ch)) { + visibleChars++; + } + } + sc.Complete(); +} + +static void FoldBaanDoc(unsigned int startPos, int length, int initStyle, WordList *[], + Accessor &styler) { + bool foldComment = styler.GetPropertyInt("fold.comment") != 0; + bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + unsigned int endPos = startPos + length; + int visibleChars = 0; + int lineCurrent = styler.GetLine(startPos); + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + for (unsigned int i = startPos; i < endPos; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + int stylePrev = style; + style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + if (foldComment && + (style == SCE_BAAN_COMMENT || style == SCE_BAAN_COMMENTDOC)) { + if (style != stylePrev) { + levelCurrent++; + } else if ((style != styleNext) && !atEOL) { + // Comments don't end at end of line and the next character may be unstyled. + levelCurrent--; + } + } + if (style == SCE_BAAN_OPERATOR) { + if (ch == '{') { + levelCurrent++; + } else if (ch == '}') { + levelCurrent--; + } + } + if (atEOL) { + int lev = levelPrev; + if (visibleChars == 0 && foldCompact) + lev |= SC_FOLDLEVELWHITEFLAG; + if ((levelCurrent > levelPrev) && (visibleChars > 0)) + lev |= SC_FOLDLEVELHEADERFLAG; + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + levelPrev = levelCurrent; + visibleChars = 0; + } + if (!isspacechar(ch)) + visibleChars++; + } + // Fill in the real level of the next line, keeping the current flags as they will be filled in later + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + styler.SetLevel(lineCurrent, levelPrev | flagsNext); +} + +LexerModule lmBaan(SCLEX_BAAN, ColouriseBaanDoc, "baan", FoldBaanDoc); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBash.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBash.cxx new file mode 100644 index 00000000..f6401ece --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBash.cxx @@ -0,0 +1,663 @@ +// Scintilla source code edit control +/** @file LexBash.cxx + ** Lexer for Bash. + **/ +// Copyright 2004-2005 by Neil Hodgson +// Adapted from LexPerl by Kein-Hong Man 2004 +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +#define BASH_BASE_ERROR 65 +#define BASH_BASE_DECIMAL 66 +#define BASH_BASE_HEX 67 +#define BASH_BASE_OCTAL 68 +#define BASH_BASE_OCTAL_ERROR 69 + +#define HERE_DELIM_MAX 256 + +static inline int translateBashDigit(char ch) { + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } else if (ch >= 'a' && ch <= 'z') { + return ch - 'a' + 10; + } else if (ch >= 'A' && ch <= 'Z') { + return ch - 'A' + 36; + } else if (ch == '@') { + return 62; + } else if (ch == '_') { + return 63; + } + return BASH_BASE_ERROR; +} + +static inline bool isEOLChar(char ch) { + return (ch == '\r') || (ch == '\n'); +} + +static bool isSingleCharOp(char ch) { + char strCharSet[2]; + strCharSet[0] = ch; + strCharSet[1] = '\0'; + return (NULL != strstr("rwxoRWXOezsfdlpSbctugkTBMACahGLNn", strCharSet)); +} + +static inline bool isBashOperator(char ch) { + if (ch == '^' || ch == '&' || ch == '\\' || ch == '%' || + ch == '(' || ch == ')' || ch == '-' || ch == '+' || + ch == '=' || ch == '|' || ch == '{' || ch == '}' || + ch == '[' || ch == ']' || ch == ':' || ch == ';' || + ch == '>' || ch == ',' || ch == '/' || ch == '<' || + ch == '?' || ch == '!' || ch == '.' || ch == '~' || + ch == '@') + return true; + return false; +} + +static int classifyWordBash(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) { + char s[100]; + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + s[i + 1] = '\0'; + } + char chAttr = SCE_SH_IDENTIFIER; + if (keywords.InList(s)) + chAttr = SCE_SH_WORD; + styler.ColourTo(end, chAttr); + return chAttr; +} + +static inline int getBashNumberBase(unsigned int start, unsigned int end, Accessor &styler) { + int base = 0; + for (unsigned int i = 0; i < end - start + 1 && i < 10; i++) { + base = base * 10 + (styler[start + i] - '0'); + } + if (base > 64 || (end - start) > 1) { + return BASH_BASE_ERROR; + } + return base; +} + +static inline bool isEndVar(char ch) { + return !isalnum(ch) && ch != '$' && ch != '_'; +} + +static inline bool isNonQuote(char ch) { + return isalnum(ch) || ch == '_'; +} + +static bool isMatch(Accessor &styler, int lengthDoc, int pos, const char *val) { + if ((pos + static_cast(strlen(val))) >= lengthDoc) { + return false; + } + while (*val) { + if (*val != styler[pos++]) { + return false; + } + val++; + } + return true; +} + +static char opposite(char ch) { + if (ch == '(') + return ')'; + if (ch == '[') + return ']'; + if (ch == '{') + return '}'; + if (ch == '<') + return '>'; + return ch; +} + +static void ColouriseBashDoc(unsigned int startPos, int length, int initStyle, + WordList *keywordlists[], Accessor &styler) { + + // Lexer for bash often has to backtrack to start of current style to determine + // which characters are being used as quotes, how deeply nested is the + // start position and what the termination string is for here documents + + WordList &keywords = *keywordlists[0]; + + class HereDocCls { + public: + int State; // 0: '<<' encountered + // 1: collect the delimiter + // 2: here doc text (lines after the delimiter) + char Quote; // the char after '<<' + bool Quoted; // true if Quote in ('\'','"','`') + bool Indent; // indented delimiter (for <<-) + int DelimiterLength; // strlen(Delimiter) + char *Delimiter; // the Delimiter, 256: sizeof PL_tokenbuf + HereDocCls() { + State = 0; + Quote = 0; + Quoted = false; + Indent = 0; + DelimiterLength = 0; + Delimiter = new char[HERE_DELIM_MAX]; + Delimiter[0] = '\0'; + } + ~HereDocCls() { + delete []Delimiter; + } + }; + HereDocCls HereDoc; + + class QuoteCls { + public: + int Rep; + int Count; + char Up; + char Down; + QuoteCls() { + this->New(1); + } + void New(int r) { + Rep = r; + Count = 0; + Up = '\0'; + Down = '\0'; + } + void Open(char u) { + Count++; + Up = u; + Down = opposite(Up); + } + }; + QuoteCls Quote; + + int state = initStyle; + int numBase = 0; + unsigned int lengthDoc = startPos + length; + + // If in a long distance lexical state, seek to the beginning to find quote characters + // Bash strings can be multi-line with embedded newlines, so backtrack. + // Bash numbers have additional state during lexing, so backtrack too. + if (state == SCE_SH_HERE_Q) { + while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_SH_HERE_DELIM)) { + startPos--; + } + startPos = styler.LineStart(styler.GetLine(startPos)); + state = styler.StyleAt(startPos - 1); + } + if (state == SCE_SH_STRING + || state == SCE_SH_BACKTICKS + || state == SCE_SH_CHARACTER + || state == SCE_SH_NUMBER + || state == SCE_SH_IDENTIFIER + || state == SCE_SH_COMMENTLINE + ) { + while ((startPos > 1) && (styler.StyleAt(startPos - 1) == state)) { + startPos--; + } + state = SCE_SH_DEFAULT; + } + + styler.StartAt(startPos); + char chPrev = styler.SafeGetCharAt(startPos - 1); + if (startPos == 0) + chPrev = '\n'; + char chNext = styler[startPos]; + styler.StartSegment(startPos); + + for (unsigned int i = startPos; i < lengthDoc; i++) { + char ch = chNext; + // if the current character is not consumed due to the completion of an + // earlier style, lexing can be restarted via a simple goto + restartLexer: + chNext = styler.SafeGetCharAt(i + 1); + char chNext2 = styler.SafeGetCharAt(i + 2); + + if (styler.IsLeadByte(ch)) { + chNext = styler.SafeGetCharAt(i + 2); + chPrev = ' '; + i += 1; + continue; + } + + if ((chPrev == '\r' && ch == '\n')) { // skip on DOS/Windows + styler.ColourTo(i, state); + chPrev = ch; + continue; + } + + if (HereDoc.State == 1 && isEOLChar(ch)) { + // Begin of here-doc (the line after the here-doc delimiter): + // Lexically, the here-doc starts from the next line after the >>, but the + // first line of here-doc seem to follow the style of the last EOL sequence + HereDoc.State = 2; + if (HereDoc.Quoted) { + if (state == SCE_SH_HERE_DELIM) { + // Missing quote at end of string! We are stricter than bash. + // Colour here-doc anyway while marking this bit as an error. + state = SCE_SH_ERROR; + } + styler.ColourTo(i - 1, state); + // HereDoc.Quote always == '\'' + state = SCE_SH_HERE_Q; + } else { + styler.ColourTo(i - 1, state); + // always switch + state = SCE_SH_HERE_Q; + } + } + + if (state == SCE_SH_DEFAULT) { + if (ch == '\\') { // escaped character + if (i < lengthDoc - 1) + i++; + ch = chNext; + chNext = chNext2; + styler.ColourTo(i, SCE_SH_IDENTIFIER); + } else if (isdigit(ch)) { + state = SCE_SH_NUMBER; + numBase = BASH_BASE_DECIMAL; + if (ch == '0') { // hex,octal + if (chNext == 'x' || chNext == 'X') { + numBase = BASH_BASE_HEX; + i++; + ch = chNext; + chNext = chNext2; + } else if (isdigit(chNext)) { + numBase = BASH_BASE_OCTAL; + } + } + } else if (iswordstart(ch)) { + state = SCE_SH_WORD; + if (!iswordchar(chNext) && chNext != '+' && chNext != '-') { + // We need that if length of word == 1! + // This test is copied from the SCE_SH_WORD handler. + classifyWordBash(styler.GetStartSegment(), i, keywords, styler); + state = SCE_SH_DEFAULT; + } + } else if (ch == '#') { + state = SCE_SH_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_SH_STRING; + Quote.New(1); + Quote.Open(ch); + } else if (ch == '\'') { + state = SCE_SH_CHARACTER; + Quote.New(1); + Quote.Open(ch); + } else if (ch == '`') { + state = SCE_SH_BACKTICKS; + Quote.New(1); + Quote.Open(ch); + } else if (ch == '$') { + if (chNext == '{') { + state = SCE_SH_PARAM; + goto startQuote; + } else if (chNext == '\'') { + state = SCE_SH_CHARACTER; + goto startQuote; + } else if (chNext == '"') { + state = SCE_SH_STRING; + goto startQuote; + } else if (chNext == '(' && chNext2 == '(') { + styler.ColourTo(i, SCE_SH_OPERATOR); + state = SCE_SH_DEFAULT; + goto skipChar; + } else if (chNext == '(' || chNext == '`') { + state = SCE_SH_BACKTICKS; + startQuote: + Quote.New(1); + Quote.Open(chNext); + goto skipChar; + } else { + state = SCE_SH_SCALAR; + skipChar: + i++; + ch = chNext; + chNext = chNext2; + } + } else if (ch == '*') { + if (chNext == '*') { // exponentiation + i++; + ch = chNext; + chNext = chNext2; + } + styler.ColourTo(i, SCE_SH_OPERATOR); + } else if (ch == '<' && chNext == '<') { + state = SCE_SH_HERE_DELIM; + HereDoc.State = 0; + HereDoc.Indent = false; + } else if (ch == '-' // file test operators + && isSingleCharOp(chNext) + && !isalnum((chNext2 = styler.SafeGetCharAt(i+2)))) { + styler.ColourTo(i + 1, SCE_SH_WORD); + state = SCE_SH_DEFAULT; + i++; + ch = chNext; + chNext = chNext2; + } else if (isBashOperator(ch)) { + styler.ColourTo(i, SCE_SH_OPERATOR); + } else { + // keep colouring defaults to make restart easier + styler.ColourTo(i, SCE_SH_DEFAULT); + } + } else if (state == SCE_SH_NUMBER) { + int digit = translateBashDigit(ch); + if (numBase == BASH_BASE_DECIMAL) { + if (ch == '#') { + numBase = getBashNumberBase(styler.GetStartSegment(), i - 1, styler); + if (numBase == BASH_BASE_ERROR) // take the rest as comment + goto numAtEnd; + } else if (!isdigit(ch)) + goto numAtEnd; + } else if (numBase == BASH_BASE_HEX) { + if ((digit < 16) || (digit >= 36 && digit <= 41)) { + // hex digit 0-9a-fA-F + } else + goto numAtEnd; + } else if (numBase == BASH_BASE_OCTAL || + numBase == BASH_BASE_OCTAL_ERROR) { + if (digit > 7) { + if (digit <= 9) { + numBase = BASH_BASE_OCTAL_ERROR; + } else + goto numAtEnd; + } + } else if (numBase == BASH_BASE_ERROR) { + if (digit > 9) + goto numAtEnd; + } else { // DD#DDDD number style handling + if (digit != BASH_BASE_ERROR) { + if (numBase <= 36) { + // case-insensitive if base<=36 + if (digit >= 36) digit -= 26; + } + if (digit >= numBase) { + if (digit <= 9) { + numBase = BASH_BASE_ERROR; + } else + goto numAtEnd; + } + } else { + numAtEnd: + if (numBase == BASH_BASE_ERROR || + numBase == BASH_BASE_OCTAL_ERROR) + state = SCE_SH_ERROR; + styler.ColourTo(i - 1, state); + state = SCE_SH_DEFAULT; + goto restartLexer; + } + } + } else if (state == SCE_SH_WORD) { + if (!iswordchar(chNext) && chNext != '+' && chNext != '-') { + // "." never used in Bash variable names + // but used in file names + classifyWordBash(styler.GetStartSegment(), i, keywords, styler); + state = SCE_SH_DEFAULT; + ch = ' '; + } + } else if (state == SCE_SH_IDENTIFIER) { + if (!iswordchar(chNext) && chNext != '+' && chNext != '-') { + styler.ColourTo(i, SCE_SH_IDENTIFIER); + state = SCE_SH_DEFAULT; + ch = ' '; + } + } else { + if (state == SCE_SH_COMMENTLINE) { + if (ch == '\\' && isEOLChar(chNext)) { + // comment continuation + if (chNext == '\r' && chNext2 == '\n') { + i += 2; + ch = styler.SafeGetCharAt(i); + chNext = styler.SafeGetCharAt(i + 1); + } else { + i++; + ch = chNext; + chNext = chNext2; + } + } else if (isEOLChar(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_SH_DEFAULT; + goto restartLexer; + } else if (isEOLChar(chNext)) { + styler.ColourTo(i, state); + state = SCE_SH_DEFAULT; + } + } else if (state == SCE_SH_HERE_DELIM) { + // + // From Bash info: + // --------------- + // Specifier format is: <<[-]WORD + // Optional '-' is for removal of leading tabs from here-doc. + // Whitespace acceptable after <<[-] operator + // + if (HereDoc.State == 0) { // '<<' encountered + HereDoc.State = 1; + HereDoc.Quote = chNext; + HereDoc.Quoted = false; + HereDoc.DelimiterLength = 0; + HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0'; + if (chNext == '\'' || chNext == '\"') { // a quoted here-doc delimiter (' or ") + i++; + ch = chNext; + chNext = chNext2; + HereDoc.Quoted = true; + } else if (!HereDoc.Indent && chNext == '-') { // <<- indent case + HereDoc.Indent = true; + HereDoc.State = 0; + } else if (isalpha(chNext) || chNext == '_' || chNext == '\\' + || chNext == '-' || chNext == '+' || chNext == '!') { + // an unquoted here-doc delimiter, no special handling + // TODO check what exactly bash considers part of the delim + } else if (chNext == '<') { // HERE string <<< + i++; + ch = chNext; + chNext = chNext2; + styler.ColourTo(i, SCE_SH_HERE_DELIM); + state = SCE_SH_DEFAULT; + HereDoc.State = 0; + } else if (isspacechar(chNext)) { + // eat whitespace + HereDoc.State = 0; + } else if (isdigit(chNext) || chNext == '=' || chNext == '$') { + // left shift << or <<= operator cases + styler.ColourTo(i, SCE_SH_OPERATOR); + state = SCE_SH_DEFAULT; + HereDoc.State = 0; + } else { + // symbols terminates; deprecated zero-length delimiter + } + } else if (HereDoc.State == 1) { // collect the delimiter + if (HereDoc.Quoted) { // a quoted here-doc delimiter + if (ch == HereDoc.Quote) { // closing quote => end of delimiter + styler.ColourTo(i, state); + state = SCE_SH_DEFAULT; + } else { + if (ch == '\\' && chNext == HereDoc.Quote) { // escaped quote + i++; + ch = chNext; + chNext = chNext2; + } + HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch; + HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0'; + } + } else { // an unquoted here-doc delimiter + if (isalnum(ch) || ch == '_' || ch == '-' || ch == '+' || ch == '!') { + HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch; + HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0'; + } else if (ch == '\\') { + // skip escape prefix + } else { + styler.ColourTo(i - 1, state); + state = SCE_SH_DEFAULT; + goto restartLexer; + } + } + if (HereDoc.DelimiterLength >= HERE_DELIM_MAX - 1) { + styler.ColourTo(i - 1, state); + state = SCE_SH_ERROR; + goto restartLexer; + } + } + } else if (HereDoc.State == 2) { + // state == SCE_SH_HERE_Q + if (isMatch(styler, lengthDoc, i, HereDoc.Delimiter)) { + if (!HereDoc.Indent && isEOLChar(chPrev)) { + endHereDoc: + // standard HERE delimiter + i += HereDoc.DelimiterLength; + chPrev = styler.SafeGetCharAt(i - 1); + ch = styler.SafeGetCharAt(i); + if (isEOLChar(ch)) { + styler.ColourTo(i - 1, state); + state = SCE_SH_DEFAULT; + HereDoc.State = 0; + goto restartLexer; + } + chNext = styler.SafeGetCharAt(i + 1); + } else if (HereDoc.Indent) { + // indented HERE delimiter + unsigned int bk = (i > 0)? i - 1: 0; + while (i > 0) { + ch = styler.SafeGetCharAt(bk--); + if (isEOLChar(ch)) { + goto endHereDoc; + } else if (!isspacechar(ch)) { + break; // got leading non-whitespace + } + } + } + } + } else if (state == SCE_SH_SCALAR) { // variable names + if (isEndVar(ch)) { + if ((state == SCE_SH_SCALAR) + && i == (styler.GetStartSegment() + 1)) { + // Special variable: $(, $_ etc. + styler.ColourTo(i, state); + state = SCE_SH_DEFAULT; + } else { + styler.ColourTo(i - 1, state); + state = SCE_SH_DEFAULT; + goto restartLexer; + } + } + } else if (state == SCE_SH_STRING + || state == SCE_SH_CHARACTER + || state == SCE_SH_BACKTICKS + || state == SCE_SH_PARAM + ) { + if (!Quote.Down && !isspacechar(ch)) { + Quote.Open(ch); + } else if (ch == '\\' && Quote.Up != '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } else if (ch == Quote.Down) { + Quote.Count--; + if (Quote.Count == 0) { + Quote.Rep--; + if (Quote.Rep <= 0) { + styler.ColourTo(i, state); + state = SCE_SH_DEFAULT; + ch = ' '; + } + if (Quote.Up == Quote.Down) { + Quote.Count++; + } + } + } else if (ch == Quote.Up) { + Quote.Count++; + } + } + } + if (state == SCE_SH_ERROR) { + break; + } + chPrev = ch; + } + styler.ColourTo(lengthDoc - 1, state); +} + +static bool IsCommentLine(int line, Accessor &styler) { + int pos = styler.LineStart(line); + int eol_pos = styler.LineStart(line + 1) - 1; + for (int i = pos; i < eol_pos; i++) { + char ch = styler[i]; + if (ch == '#') + return true; + else if (ch != ' ' && ch != '\t') + return false; + } + return false; +} + +static void FoldBashDoc(unsigned int startPos, int length, int, WordList *[], + Accessor &styler) { + bool foldComment = styler.GetPropertyInt("fold.comment") != 0; + bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + unsigned int endPos = startPos + length; + int visibleChars = 0; + int lineCurrent = styler.GetLine(startPos); + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + for (unsigned int i = startPos; i < endPos; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + int style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + // Comment folding + if (foldComment && atEOL && IsCommentLine(lineCurrent, styler)) + { + if (!IsCommentLine(lineCurrent - 1, styler) + && IsCommentLine(lineCurrent + 1, styler)) + levelCurrent++; + else if (IsCommentLine(lineCurrent - 1, styler) + && !IsCommentLine(lineCurrent+1, styler)) + levelCurrent--; + } + if (style == SCE_SH_OPERATOR) { + if (ch == '{') { + levelCurrent++; + } else if (ch == '}') { + levelCurrent--; + } + } + if (atEOL) { + int lev = levelPrev; + if (visibleChars == 0 && foldCompact) + lev |= SC_FOLDLEVELWHITEFLAG; + if ((levelCurrent > levelPrev) && (visibleChars > 0)) + lev |= SC_FOLDLEVELHEADERFLAG; + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + levelPrev = levelCurrent; + visibleChars = 0; + } + if (!isspacechar(ch)) + visibleChars++; + } + // Fill in the real level of the next line, keeping the current flags as they will be filled in later + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + styler.SetLevel(lineCurrent, levelPrev | flagsNext); +} + +static const char * const bashWordListDesc[] = { + "Keywords", + 0 +}; + +LexerModule lmBash(SCLEX_BASH, ColouriseBashDoc, "bash", FoldBashDoc, bashWordListDesc); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBasic.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBasic.cxx new file mode 100644 index 00000000..5661b0b9 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBasic.cxx @@ -0,0 +1,369 @@ +// Scintilla source code edit control +/** @file LexBasic.cxx + ** Lexer for BlitzBasic and PureBasic. + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +// This tries to be a unified Lexer/Folder for all the BlitzBasic/BlitzMax/PurBasic basics +// and derivatives. Once they diverge enough, might want to split it into multiple +// lexers for more code clearity. +// +// Mail me (elias users sf net) for any bugs. + +// Folding only works for simple things like functions or types. + +// You may want to have a look at my ctags lexer as well, if you additionally to coloring +// and folding need to extract things like label tags in your editor. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +/* Bits: + * 1 - whitespace + * 2 - operator + * 4 - identifier + * 8 - decimal digit + * 16 - hex digit + * 32 - bin digit + */ +static int character_classification[128] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 2, + 60, 60, 28, 28, 28, 28, 28, 28, 28, 28, 2, 2, 2, 2, 2, 2, + 2, 20, 20, 20, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 4, + 2, 20, 20, 20, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 0 +}; + +static bool IsSpace(int c) { + return c < 128 && (character_classification[c] & 1); +} + +static bool IsOperator(int c) { + return c < 128 && (character_classification[c] & 2); +} + +static bool IsIdentifier(int c) { + return c < 128 && (character_classification[c] & 4); +} + +static bool IsDigit(int c) { + return c < 128 && (character_classification[c] & 8); +} + +static bool IsHexDigit(int c) { + return c < 128 && (character_classification[c] & 16); +} + +static bool IsBinDigit(int c) { + return c < 128 && (character_classification[c] & 32); +} + +static int LowerCase(int c) +{ + if (c >= 'A' && c <= 'Z') + return 'a' + c - 'A'; + return c; +} + +static void ColouriseBasicDoc(unsigned int startPos, int length, int initStyle, + WordList *keywordlists[], Accessor &styler, char comment_char) { + bool wasfirst = true, isfirst = true; // true if first token in a line + styler.StartAt(startPos); + + StyleContext sc(startPos, length, initStyle, styler); + + // Can't use sc.More() here else we miss the last character + for (; ; sc.Forward()) { + if (sc.state == SCE_B_IDENTIFIER) { + if (!IsIdentifier(sc.ch)) { + // Labels + if (wasfirst && sc.Match(':')) { + sc.ChangeState(SCE_B_LABEL); + sc.ForwardSetState(SCE_B_DEFAULT); + } else { + char s[100]; + int kstates[4] = { + SCE_B_KEYWORD, + SCE_B_KEYWORD2, + SCE_B_KEYWORD3, + SCE_B_KEYWORD4, + }; + sc.GetCurrentLowered(s, sizeof(s)); + for (int i = 0; i < 4; i++) { + if (keywordlists[i]->InList(s)) { + sc.ChangeState(kstates[i]); + } + } + // Types, must set them as operator else they will be + // matched as number/constant + if (sc.Match('.') || sc.Match('$') || sc.Match('%') || + sc.Match('#')) { + sc.SetState(SCE_B_OPERATOR); + } else { + sc.SetState(SCE_B_DEFAULT); + } + } + } + } else if (sc.state == SCE_B_OPERATOR) { + if (!IsOperator(sc.ch) || sc.Match('#')) + sc.SetState(SCE_B_DEFAULT); + } else if (sc.state == SCE_B_LABEL) { + if (!IsIdentifier(sc.ch)) + sc.SetState(SCE_B_DEFAULT); + } else if (sc.state == SCE_B_CONSTANT) { + if (!IsIdentifier(sc.ch)) + sc.SetState(SCE_B_DEFAULT); + } else if (sc.state == SCE_B_NUMBER) { + if (!IsDigit(sc.ch)) + sc.SetState(SCE_B_DEFAULT); + } else if (sc.state == SCE_B_HEXNUMBER) { + if (!IsHexDigit(sc.ch)) + sc.SetState(SCE_B_DEFAULT); + } else if (sc.state == SCE_B_BINNUMBER) { + if (!IsBinDigit(sc.ch)) + sc.SetState(SCE_B_DEFAULT); + } else if (sc.state == SCE_B_STRING) { + if (sc.ch == '"') { + sc.ForwardSetState(SCE_B_DEFAULT); + } + if (sc.atLineEnd) { + sc.ChangeState(SCE_B_ERROR); + sc.SetState(SCE_B_DEFAULT); + } + } else if (sc.state == SCE_B_COMMENT || sc.state == SCE_B_PREPROCESSOR) { + if (sc.atLineEnd) { + sc.SetState(SCE_B_DEFAULT); + } + } + + if (sc.atLineStart) + isfirst = true; + + if (sc.state == SCE_B_DEFAULT || sc.state == SCE_B_ERROR) { + if (isfirst && sc.Match('.')) { + sc.SetState(SCE_B_LABEL); + } else if (isfirst && sc.Match('#')) { + wasfirst = isfirst; + sc.SetState(SCE_B_IDENTIFIER); + } else if (sc.Match(comment_char)) { + // Hack to make deprecated QBASIC '$Include show + // up in freebasic with SCE_B_PREPROCESSOR. + if (comment_char == '\'' && sc.Match(comment_char, '$')) + sc.SetState(SCE_B_PREPROCESSOR); + else + sc.SetState(SCE_B_COMMENT); + } else if (sc.Match('"')) { + sc.SetState(SCE_B_STRING); + } else if (IsDigit(sc.ch)) { + sc.SetState(SCE_B_NUMBER); + } else if (sc.Match('$')) { + sc.SetState(SCE_B_HEXNUMBER); + } else if (sc.Match('%')) { + sc.SetState(SCE_B_BINNUMBER); + } else if (sc.Match('#')) { + sc.SetState(SCE_B_CONSTANT); + } else if (IsOperator(sc.ch)) { + sc.SetState(SCE_B_OPERATOR); + } else if (IsIdentifier(sc.ch)) { + wasfirst = isfirst; + sc.SetState(SCE_B_IDENTIFIER); + } else if (!IsSpace(sc.ch)) { + sc.SetState(SCE_B_ERROR); + } + } + + if (!IsSpace(sc.ch)) + isfirst = false; + + if (!sc.More()) + break; + } + sc.Complete(); +} + +static int CheckBlitzFoldPoint(char const *token, int &level) { + if (!strcmp(token, "function") || + !strcmp(token, "type")) { + level |= SC_FOLDLEVELHEADERFLAG; + return 1; + } + if (!strcmp(token, "end function") || + !strcmp(token, "end type")) { + return -1; + } + return 0; +} + +static int CheckPureFoldPoint(char const *token, int &level) { + if (!strcmp(token, "procedure") || + !strcmp(token, "enumeration") || + !strcmp(token, "interface") || + !strcmp(token, "structure")) { + level |= SC_FOLDLEVELHEADERFLAG; + return 1; + } + if (!strcmp(token, "endprocedure") || + !strcmp(token, "endenumeration") || + !strcmp(token, "endinterface") || + !strcmp(token, "endstructure")) { + return -1; + } + return 0; +} + +static int CheckFreeFoldPoint(char const *token, int &level) { + if (!strcmp(token, "function") || + !strcmp(token, "sub") || + !strcmp(token, "type")) { + level |= SC_FOLDLEVELHEADERFLAG; + return 1; + } + if (!strcmp(token, "end function") || + !strcmp(token, "end sub") || + !strcmp(token, "end type")) { + return -1; + } + return 0; +} + +static void FoldBasicDoc(unsigned int startPos, int length, + Accessor &styler, int (*CheckFoldPoint)(char const *, int &)) { + int line = styler.GetLine(startPos); + int level = styler.LevelAt(line); + int go = 0, done = 0; + int endPos = startPos + length; + char word[256]; + int wordlen = 0; + int i; + bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + // Scan for tokens at the start of the line (they may include + // whitespace, for tokens like "End Function" + for (i = startPos; i < endPos; i++) { + int c = styler.SafeGetCharAt(i); + if (!done && !go) { + if (wordlen) { // are we scanning a token already? + word[wordlen] = static_cast(LowerCase(c)); + if (!IsIdentifier(c)) { // done with token + word[wordlen] = '\0'; + go = CheckFoldPoint(word, level); + if (!go) { + // Treat any whitespace as single blank, for + // things like "End Function". + if (IsSpace(c) && IsIdentifier(word[wordlen - 1])) { + word[wordlen] = ' '; + if (wordlen < 255) + wordlen++; + } + else // done with this line + done = 1; + } + } else if (wordlen < 255) { + wordlen++; + } + } else { // start scanning at first non-whitespace character + if (!IsSpace(c)) { + if (IsIdentifier(c)) { + word[0] = static_cast(LowerCase(c)); + wordlen = 1; + } else // done with this line + done = 1; + } + } + } + if (c == '\n') { // line end + if (!done && wordlen == 0 && foldCompact) // line was only space + level |= SC_FOLDLEVELWHITEFLAG; + if (level != styler.LevelAt(line)) + styler.SetLevel(line, level); + level += go; + line++; + // reset state + wordlen = 0; + level &= ~SC_FOLDLEVELHEADERFLAG; + level &= ~SC_FOLDLEVELWHITEFLAG; + go = 0; + done = 0; + } + } +} + +static void ColouriseBlitzBasicDoc(unsigned int startPos, int length, int initStyle, + WordList *keywordlists[], Accessor &styler) { + ColouriseBasicDoc(startPos, length, initStyle, keywordlists, styler, ';'); +} + +static void ColourisePureBasicDoc(unsigned int startPos, int length, int initStyle, + WordList *keywordlists[], Accessor &styler) { + ColouriseBasicDoc(startPos, length, initStyle, keywordlists, styler, ';'); +} + +static void ColouriseFreeBasicDoc(unsigned int startPos, int length, int initStyle, + WordList *keywordlists[], Accessor &styler) { + ColouriseBasicDoc(startPos, length, initStyle, keywordlists, styler, '\''); +} + +static void FoldBlitzBasicDoc(unsigned int startPos, int length, int, + WordList *[], Accessor &styler) { + FoldBasicDoc(startPos, length, styler, CheckBlitzFoldPoint); +} + +static void FoldPureBasicDoc(unsigned int startPos, int length, int, + WordList *[], Accessor &styler) { + FoldBasicDoc(startPos, length, styler, CheckPureFoldPoint); +} + +static void FoldFreeBasicDoc(unsigned int startPos, int length, int, + WordList *[], Accessor &styler) { + FoldBasicDoc(startPos, length, styler, CheckFreeFoldPoint); +} + +static const char * const blitzbasicWordListDesc[] = { + "BlitzBasic Keywords", + "user1", + "user2", + "user3", + 0 +}; + +static const char * const purebasicWordListDesc[] = { + "PureBasic Keywords", + "PureBasic PreProcessor Keywords", + "user defined 1", + "user defined 2", + 0 +}; + +static const char * const freebasicWordListDesc[] = { + "FreeBasic Keywords", + "FreeBasic PreProcessor Keywords", + "user defined 1", + "user defined 2", + 0 +}; + +LexerModule lmBlitzBasic(SCLEX_BLITZBASIC, ColouriseBlitzBasicDoc, "blitzbasic", + FoldBlitzBasicDoc, blitzbasicWordListDesc); + +LexerModule lmPureBasic(SCLEX_PUREBASIC, ColourisePureBasicDoc, "purebasic", + FoldPureBasicDoc, purebasicWordListDesc); + +LexerModule lmFreeBasic(SCLEX_FREEBASIC, ColouriseFreeBasicDoc, "freebasic", + FoldFreeBasicDoc, freebasicWordListDesc); + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBullant.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBullant.cxx new file mode 100644 index 00000000..59fe6c41 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexBullant.cxx @@ -0,0 +1,225 @@ +// SciTE - Scintilla based Text Editor +// LexBullant.cxx - lexer for Bullant + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + + +static int classifyWordBullant(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) { + char s[100]; + for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) { + s[i] = static_cast(tolower(styler[start + i])); + s[i + 1] = '\0'; + } + int lev= 0; + char chAttr = SCE_C_IDENTIFIER; + if (isdigit(s[0]) || (s[0] == '.')){ + chAttr = SCE_C_NUMBER; + } + else { + if (keywords.InList(s)) { + chAttr = SCE_C_WORD; + if (strcmp(s, "end") == 0) + lev = -1; + else if (strcmp(s, "method") == 0 || + strcmp(s, "case") == 0 || + strcmp(s, "class") == 0 || + strcmp(s, "debug") == 0 || + strcmp(s, "test") == 0 || + strcmp(s, "if") == 0 || + strcmp(s, "lock") == 0 || + strcmp(s, "transaction") == 0 || + strcmp(s, "trap") == 0 || + strcmp(s, "until") == 0 || + strcmp(s, "while") == 0) + lev = 1; + } + } + styler.ColourTo(end, chAttr); + return lev; +} + +static void ColouriseBullantDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) { + WordList &keywords = *keywordlists[0]; + + styler.StartAt(startPos); + + bool fold = styler.GetPropertyInt("fold") != 0; + int lineCurrent = styler.GetLine(startPos); + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + + int state = initStyle; + if (state == SCE_C_STRINGEOL) // Does not leak onto next line + state = SCE_C_DEFAULT; + char chPrev = ' '; + char chNext = styler[startPos]; + unsigned int lengthDoc = startPos + length; + int visibleChars = 0; + styler.StartSegment(startPos); + int endFoundThisLine = 0; + for (unsigned int i = startPos; i < lengthDoc; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + + if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { + // Trigger on CR only (Mac style) or either on LF from CR+LF (Dos/Win) or on LF alone (Unix) + // Avoid triggering two times on Dos/Win + // End of line + endFoundThisLine = 0; + if (state == SCE_C_STRINGEOL) { + styler.ColourTo(i, state); + state = SCE_C_DEFAULT; + } + if (fold) { + int lev = levelPrev; + if (visibleChars == 0) + lev |= SC_FOLDLEVELWHITEFLAG; + if ((levelCurrent > levelPrev) && (visibleChars > 0)) + lev |= SC_FOLDLEVELHEADERFLAG; + styler.SetLevel(lineCurrent, lev); + lineCurrent++; + levelPrev = levelCurrent; + } + visibleChars = 0; + +/* int indentBlock = GetLineIndentation(lineCurrent); + if (blockChange==1){ + lineCurrent++; + int pos=SetLineIndentation(lineCurrent, indentBlock + indentSize); + } else if (blockChange==-1) { + indentBlock -= indentSize; + if (indentBlock < 0) + indentBlock = 0; + SetLineIndentation(lineCurrent, indentBlock); + lineCurrent++; + } + blockChange=0; +*/ } + if (!isspace(ch)) + visibleChars++; + + if (styler.IsLeadByte(ch)) { + chNext = styler.SafeGetCharAt(i + 2); + chPrev = ' '; + i += 1; + continue; + } + + if (state == SCE_C_DEFAULT) { + if (iswordstart(ch)) { + styler.ColourTo(i-1, state); + state = SCE_C_IDENTIFIER; + } else if (ch == '@' && chNext == 'o') { + if ((styler.SafeGetCharAt(i+2) =='f') && (styler.SafeGetCharAt(i+3) == 'f')) { + styler.ColourTo(i-1, state); + state = SCE_C_COMMENT; + } + } else if (ch == '#') { + styler.ColourTo(i-1, state); + state = SCE_C_COMMENTLINE; + } else if (ch == '\"') { + styler.ColourTo(i-1, state); + state = SCE_C_STRING; + } else if (ch == '\'') { + styler.ColourTo(i-1, state); + state = SCE_C_CHARACTER; + } else if (isoperator(ch)) { + styler.ColourTo(i-1, state); + styler.ColourTo(i, SCE_C_OPERATOR); + } + } else if (state == SCE_C_IDENTIFIER) { + if (!iswordchar(ch)) { + int levelChange = classifyWordBullant(styler.GetStartSegment(), i - 1, keywords, styler); + state = SCE_C_DEFAULT; + chNext = styler.SafeGetCharAt(i + 1); + if (ch == '#') { + state = SCE_C_COMMENTLINE; + } else if (ch == '\"') { + state = SCE_C_STRING; + } else if (ch == '\'') { + state = SCE_C_CHARACTER; + } else if (isoperator(ch)) { + styler.ColourTo(i, SCE_C_OPERATOR); + } + if (endFoundThisLine == 0) + levelCurrent+=levelChange; + if (levelChange == -1) + endFoundThisLine=1; + } + } else if (state == SCE_C_COMMENT) { + if (ch == '@' && chNext == 'o') { + if (styler.SafeGetCharAt(i+2) == 'n') { + styler.ColourTo(i+2, state); + state = SCE_C_DEFAULT; + i+=2; + } + } + } else if (state == SCE_C_COMMENTLINE) { + if (ch == '\r' || ch == '\n') { + endFoundThisLine = 0; + styler.ColourTo(i-1, state); + state = SCE_C_DEFAULT; + } + } else if (state == SCE_C_STRING) { + if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\"') { + styler.ColourTo(i, state); + state = SCE_C_DEFAULT; + } else if (chNext == '\r' || chNext == '\n') { + endFoundThisLine = 0; + styler.ColourTo(i-1, SCE_C_STRINGEOL); + state = SCE_C_STRINGEOL; + } + } else if (state == SCE_C_CHARACTER) { + if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) { + endFoundThisLine = 0; + styler.ColourTo(i-1, SCE_C_STRINGEOL); + state = SCE_C_STRINGEOL; + } else if (ch == '\\') { + if (chNext == '\"' || chNext == '\'' || chNext == '\\') { + i++; + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + } + } else if (ch == '\'') { + styler.ColourTo(i, state); + state = SCE_C_DEFAULT; + } + } + chPrev = ch; + } + styler.ColourTo(lengthDoc - 1, state); + + // Fill in the real level of the next line, keeping the current flags as they will be filled in later + if (fold) { + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + //styler.SetLevel(lineCurrent, levelCurrent | flagsNext); + styler.SetLevel(lineCurrent, levelPrev | flagsNext); + + } +} + +static const char * const bullantWordListDesc[] = { + "Keywords", + 0 +}; + +LexerModule lmBullant(SCLEX_BULLANT, ColouriseBullantDoc, "bullant", 0, bullantWordListDesc); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCLW.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCLW.cxx new file mode 100644 index 00000000..e768b731 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCLW.cxx @@ -0,0 +1,675 @@ +// Scintilla source code edit control +/** @file LexClw.cxx + ** Lexer for Clarion. + ** 2004/12/17 Updated Lexer + **/ +// Copyright 2003-2004 by Ron Schofield +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +// Is an end of line character +inline bool IsEOL(const int ch) { + + return(ch == '\n'); +} + +// Convert character to uppercase +static char CharacterUpper(char chChar) { + + if (chChar < 'a' || chChar > 'z') { + return(chChar); + } + else { + return(static_cast(chChar - 'a' + 'A')); + } +} + +// Convert string to uppercase +static void StringUpper(char *szString) { + + while (*szString) { + *szString = CharacterUpper(*szString); + szString++; + } +} + +// Is a label start character +inline bool IsALabelStart(const int iChar) { + + return(isalpha(iChar) || iChar == '_'); +} + +// Is a label character +inline bool IsALabelCharacter(const int iChar) { + + return(isalnum(iChar) || iChar == '_' || iChar == ':'); +} + +// Is the character is a ! and the the next character is not a ! +inline bool IsACommentStart(const int iChar) { + + return(iChar == '!'); +} + +// Is the character a Clarion hex character (ABCDEF) +inline bool IsAHexCharacter(const int iChar, bool bCaseSensitive) { + + // Case insensitive. + if (!bCaseSensitive) { + if (strchr("ABCDEFabcdef", iChar) != NULL) { + return(true); + } + } + // Case sensitive + else { + if (strchr("ABCDEF", iChar) != NULL) { + return(true); + } + } + return(false); +} + +// Is the character a Clarion base character (B=Binary, O=Octal, H=Hex) +inline bool IsANumericBaseCharacter(const int iChar, bool bCaseSensitive) { + + // Case insensitive. + if (!bCaseSensitive) { + // If character is a numeric base character + if (strchr("BOHboh", iChar) != NULL) { + return(true); + } + } + // Case sensitive + else { + // If character is a numeric base character + if (strchr("BOH", iChar) != NULL) { + return(true); + } + } + return(false); +} + +// Set the correct numeric constant state +inline bool SetNumericConstantState(StyleContext &scDoc) { + + int iPoints = 0; // Point counter + char cNumericString[512]; // Numeric string buffer + + // Buffer the current numberic string + scDoc.GetCurrent(cNumericString, sizeof(cNumericString)); + // Loop through the string until end of string (NULL termination) + for (int iIndex = 0; cNumericString[iIndex] != '\0'; iIndex++) { + // Depending on the character + switch (cNumericString[iIndex]) { + // Is a . (point) + case '.' : + // Increment point counter + iPoints++; + break; + default : + break; + } + } + // If points found (can be more than one for improper formatted number + if (iPoints > 0) { + return(true); + } + // Else no points found + else { + return(false); + } +} + +// Get the next word in uppercase from the current position (keyword lookahead) +inline bool GetNextWordUpper(Accessor &styler, unsigned int uiStartPos, int iLength, char *cWord) { + + unsigned int iIndex = 0; // Buffer Index + + // Loop through the remaining string from the current position + for (int iOffset = uiStartPos; iOffset < iLength; iOffset++) { + // Get the character from the buffer using the offset + char cCharacter = styler[iOffset]; + if (IsEOL(cCharacter)) { + break; + } + // If the character is alphabet character + if (isalpha(cCharacter)) { + // Add UPPERCASE character to the word buffer + cWord[iIndex++] = CharacterUpper(cCharacter); + } + } + // Add null termination + cWord[iIndex] = '\0'; + // If no word was found + if (iIndex == 0) { + // Return failure + return(false); + } + // Else word was found + else { + // Return success + return(true); + } +} + +// Clarion Language Colouring Procedure +static void ColouriseClarionDoc(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler, bool bCaseSensitive) { + + int iParenthesesLevel = 0; // Parenthese Level + int iColumn1Label = false; // Label starts in Column 1 + + WordList &wlClarionKeywords = *wlKeywords[0]; // Clarion Keywords + WordList &wlCompilerDirectives = *wlKeywords[1]; // Compiler Directives + WordList &wlRuntimeExpressions = *wlKeywords[2]; // Runtime Expressions + WordList &wlBuiltInProcsFuncs = *wlKeywords[3]; // Builtin Procedures and Functions + WordList &wlStructsDataTypes = *wlKeywords[4]; // Structures and Data Types + WordList &wlAttributes = *wlKeywords[5]; // Procedure Attributes + WordList &wlStandardEquates = *wlKeywords[6]; // Standard Equates + WordList &wlLabelReservedWords = *wlKeywords[7]; // Clarion Reserved Keywords (Labels) + WordList &wlProcLabelReservedWords = *wlKeywords[8]; // Clarion Reserved Keywords (Procedure Labels) + + const char wlProcReservedKeywordList[] = + "PROCEDURE FUNCTION"; + WordList wlProcReservedKeywords; + wlProcReservedKeywords.Set(wlProcReservedKeywordList); + + const char wlCompilerKeywordList[] = + "COMPILE OMIT"; + WordList wlCompilerKeywords; + wlCompilerKeywords.Set(wlCompilerKeywordList); + + const char wlLegacyStatementsList[] = + "BOF EOF FUNCTION POINTER SHARE"; + WordList wlLegacyStatements; + wlLegacyStatements.Set(wlLegacyStatementsList); + + StyleContext scDoc(uiStartPos, iLength, iInitStyle, accStyler); + + // lex source code + for (; scDoc.More(); scDoc.Forward()) + { + // + // Determine if the current state should terminate. + // + + // Label State Handling + if (scDoc.state == SCE_CLW_LABEL) { + // If the character is not a valid label + if (!IsALabelCharacter(scDoc.ch)) { + // If the character is a . (dot syntax) + if (scDoc.ch == '.') { + // Turn off column 1 label flag as label now cannot be reserved work + iColumn1Label = false; + // Uncolour the . (dot) to default state, move forward one character, + // and change back to the label state. + scDoc.SetState(SCE_CLW_DEFAULT); + scDoc.Forward(); + scDoc.SetState(SCE_CLW_LABEL); + } + // Else check label + else { + char cLabel[512]; // Label buffer + // Buffer the current label string + scDoc.GetCurrent(cLabel,sizeof(cLabel)); + // If case insensitive, convert string to UPPERCASE to match passed keywords. + if (!bCaseSensitive) { + StringUpper(cLabel); + } + // Else if UPPERCASE label string is in the Clarion compiler keyword list + if (wlCompilerKeywords.InList(cLabel) && iColumn1Label){ + // change the label to error state + scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE); + } + // Else if UPPERCASE label string is in the Clarion reserved keyword list + else if (wlLabelReservedWords.InList(cLabel) && iColumn1Label){ + // change the label to error state + scDoc.ChangeState(SCE_CLW_ERROR); + } + // Else if UPPERCASE label string is + else if (wlProcLabelReservedWords.InList(cLabel) && iColumn1Label) { + char cWord[512]; // Word buffer + // Get the next word from the current position + if (GetNextWordUpper(accStyler,scDoc.currentPos,uiStartPos+iLength,cWord)) { + // If the next word is a procedure reserved word + if (wlProcReservedKeywords.InList(cWord)) { + // Change the label to error state + scDoc.ChangeState(SCE_CLW_ERROR); + } + } + } + // Else if label string is in the compiler directive keyword list + else if (wlCompilerDirectives.InList(cLabel)) { + // change the state to compiler directive state + scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE); + } + // Terminate the label state and set to default state + scDoc.SetState(SCE_CLW_DEFAULT); + } + } + } + // Keyword State Handling + else if (scDoc.state == SCE_CLW_KEYWORD) { + // If character is : (colon) + if (scDoc.ch == ':') { + char cEquate[512]; // Equate buffer + // Move forward to include : (colon) in buffer + scDoc.Forward(); + // Buffer the equate string + scDoc.GetCurrent(cEquate,sizeof(cEquate)); + // If case insensitive, convert string to UPPERCASE to match passed keywords. + if (!bCaseSensitive) { + StringUpper(cEquate); + } + // If statement string is in the equate list + if (wlStandardEquates.InList(cEquate)) { + // Change to equate state + scDoc.ChangeState(SCE_CLW_STANDARD_EQUATE); + } + } + // If the character is not a valid label character + else if (!IsALabelCharacter(scDoc.ch)) { + char cStatement[512]; // Statement buffer + // Buffer the statement string + scDoc.GetCurrent(cStatement,sizeof(cStatement)); + // If case insensitive, convert string to UPPERCASE to match passed keywords. + if (!bCaseSensitive) { + StringUpper(cStatement); + } + // If statement string is in the Clarion keyword list + if (wlClarionKeywords.InList(cStatement)) { + // Change the statement string to the Clarion keyword state + scDoc.ChangeState(SCE_CLW_KEYWORD); + } + // Else if statement string is in the compiler directive keyword list + else if (wlCompilerDirectives.InList(cStatement)) { + // Change the statement string to the compiler directive state + scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE); + } + // Else if statement string is in the runtime expressions keyword list + else if (wlRuntimeExpressions.InList(cStatement)) { + // Change the statement string to the runtime expressions state + scDoc.ChangeState(SCE_CLW_RUNTIME_EXPRESSIONS); + } + // Else if statement string is in the builtin procedures and functions keyword list + else if (wlBuiltInProcsFuncs.InList(cStatement)) { + // Change the statement string to the builtin procedures and functions state + scDoc.ChangeState(SCE_CLW_BUILTIN_PROCEDURES_FUNCTION); + } + // Else if statement string is in the tructures and data types keyword list + else if (wlStructsDataTypes.InList(cStatement)) { + // Change the statement string to the structures and data types state + scDoc.ChangeState(SCE_CLW_STRUCTURE_DATA_TYPE); + } + // Else if statement string is in the procedure attribute keyword list + else if (wlAttributes.InList(cStatement)) { + // Change the statement string to the procedure attribute state + scDoc.ChangeState(SCE_CLW_ATTRIBUTE); + } + // Else if statement string is in the standard equate keyword list + else if (wlStandardEquates.InList(cStatement)) { + // Change the statement string to the standard equate state + scDoc.ChangeState(SCE_CLW_STANDARD_EQUATE); + } + // Else if statement string is in the deprecated or legacy keyword list + else if (wlLegacyStatements.InList(cStatement)) { + // Change the statement string to the standard equate state + scDoc.ChangeState(SCE_CLW_DEPRECATED); + } + // Else the statement string doesn't match any work list + else { + // Change the statement string to the default state + scDoc.ChangeState(SCE_CLW_DEFAULT); + } + // Terminate the keyword state and set to default state + scDoc.SetState(SCE_CLW_DEFAULT); + } + } + // String State Handling + else if (scDoc.state == SCE_CLW_STRING) { + // If the character is an ' (single quote) + if (scDoc.ch == '\'') { + // Set the state to default and move forward colouring + // the ' (single quote) as default state + // terminating the string state + scDoc.SetState(SCE_CLW_DEFAULT); + scDoc.Forward(); + } + // If the next character is an ' (single quote) + if (scDoc.chNext == '\'') { + // Move forward one character and set to default state + // colouring the next ' (single quote) as default state + // terminating the string state + scDoc.ForwardSetState(SCE_CLW_DEFAULT); + scDoc.Forward(); + } + } + // Picture String State Handling + else if (scDoc.state == SCE_CLW_PICTURE_STRING) { + // If the character is an ( (open parenthese) + if (scDoc.ch == '(') { + // Increment the parenthese level + iParenthesesLevel++; + } + // Else if the character is a ) (close parenthese) + else if (scDoc.ch == ')') { + // If the parenthese level is set to zero + // parentheses matched + if (!iParenthesesLevel) { + scDoc.SetState(SCE_CLW_DEFAULT); + } + // Else parenthese level is greater than zero + // still looking for matching parentheses + else { + // Decrement the parenthese level + iParenthesesLevel--; + } + } + } + // Standard Equate State Handling + else if (scDoc.state == SCE_CLW_STANDARD_EQUATE) { + if (!isalnum(scDoc.ch)) { + scDoc.SetState(SCE_CLW_DEFAULT); + } + } + // Integer Constant State Handling + else if (scDoc.state == SCE_CLW_INTEGER_CONSTANT) { + // If the character is not a digit (0-9) + // or character is not a hexidecimal character (A-F) + // or character is not a . (point) + // or character is not a numberic base character (B,O,H) + if (!(isdigit(scDoc.ch) + || IsAHexCharacter(scDoc.ch, bCaseSensitive) + || scDoc.ch == '.' + || IsANumericBaseCharacter(scDoc.ch, bCaseSensitive))) { + // If the number was a real + if (SetNumericConstantState(scDoc)) { + // Colour the matched string to the real constant state + scDoc.ChangeState(SCE_CLW_REAL_CONSTANT); + } + // Else the number was an integer + else { + // Colour the matched string to an integer constant state + scDoc.ChangeState(SCE_CLW_INTEGER_CONSTANT); + } + // Terminate the integer constant state and set to default state + scDoc.SetState(SCE_CLW_DEFAULT); + } + } + + // + // Determine if a new state should be entered. + // + + // Beginning of Line Handling + if (scDoc.atLineStart) { + // Reset the column 1 label flag + iColumn1Label = false; + // If column 1 character is a label start character + if (IsALabelStart(scDoc.ch)) { + // Label character is found in column 1 + // so set column 1 label flag and clear last column 1 label + iColumn1Label = true; + // Set the state to label + scDoc.SetState(SCE_CLW_LABEL); + } + // else if character is a space or tab + else if (IsASpace(scDoc.ch)){ + // Set to default state + scDoc.SetState(SCE_CLW_DEFAULT); + } + // else if comment start (!) or is an * (asterisk) + else if (IsACommentStart(scDoc.ch) || scDoc.ch == '*' ) { + // then set the state to comment. + scDoc.SetState(SCE_CLW_COMMENT); + } + // else the character is a ? (question mark) + else if (scDoc.ch == '?') { + // Change to the compiler directive state, move forward, + // colouring the ? (question mark), change back to default state. + scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE); + scDoc.Forward(); + scDoc.SetState(SCE_CLW_DEFAULT); + } + // else an invalid character in column 1 + else { + // Set to error state + scDoc.SetState(SCE_CLW_ERROR); + } + } + // End of Line Handling + else if (scDoc.atLineEnd) { + // Reset to the default state at the end of each line. + scDoc.SetState(SCE_CLW_DEFAULT); + } + // Default Handling + else { + // If in default state + if (scDoc.state == SCE_CLW_DEFAULT) { + // If is a letter could be a possible statement + if (isalpha(scDoc.ch)) { + // Set the state to Clarion Keyword and verify later + scDoc.SetState(SCE_CLW_KEYWORD); + } + // else is a number + else if (isdigit(scDoc.ch)) { + // Set the state to Integer Constant and verify later + scDoc.SetState(SCE_CLW_INTEGER_CONSTANT); + } + // else if the start of a comment or a | (line continuation) + else if (IsACommentStart(scDoc.ch) || scDoc.ch == '|') { + // then set the state to comment. + scDoc.SetState(SCE_CLW_COMMENT); + } + // else if the character is a ' (single quote) + else if (scDoc.ch == '\'') { + // If the character is also a ' (single quote) + // Embedded Apostrophe + if (scDoc.chNext == '\'') { + // Move forward colouring it as default state + scDoc.ForwardSetState(SCE_CLW_DEFAULT); + } + else { + // move to the next character and then set the state to comment. + scDoc.ForwardSetState(SCE_CLW_STRING); + } + } + // else the character is an @ (ampersand) + else if (scDoc.ch == '@') { + // Case insensitive. + if (!bCaseSensitive) { + // If character is a valid picture token character + if (strchr("DEKNPSTdeknpst", scDoc.chNext) != NULL) { + // Set to the picture string state + scDoc.SetState(SCE_CLW_PICTURE_STRING); + } + } + // Case sensitive + else { + // If character is a valid picture token character + if (strchr("DEKNPST", scDoc.chNext) != NULL) { + // Set the picture string state + scDoc.SetState(SCE_CLW_PICTURE_STRING); + } + } + } + } + } + } + // lexing complete + scDoc.Complete(); +} + +// Clarion Language Case Sensitive Colouring Procedure +static void ColouriseClarionDocSensitive(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler) { + + ColouriseClarionDoc(uiStartPos, iLength, iInitStyle, wlKeywords, accStyler, true); +} + +// Clarion Language Case Insensitive Colouring Procedure +static void ColouriseClarionDocInsensitive(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler) { + + ColouriseClarionDoc(uiStartPos, iLength, iInitStyle, wlKeywords, accStyler, false); +} + +// Fill Buffer + +static void FillBuffer(unsigned int uiStart, unsigned int uiEnd, Accessor &accStyler, char *szBuffer, unsigned int uiLength) { + + unsigned int uiPos = 0; + + while ((uiPos < uiEnd - uiStart + 1) && (uiPos < uiLength-1)) { + szBuffer[uiPos] = static_cast(toupper(accStyler[uiStart + uiPos])); + uiPos++; + } + szBuffer[uiPos] = '\0'; +} + +// Classify Clarion Fold Point + +static int ClassifyClarionFoldPoint(int iLevel, const char* szString) { + + if (!(isdigit(szString[0]) || (szString[0] == '.'))) { + if (strcmp(szString, "PROCEDURE") == 0) { + // iLevel = SC_FOLDLEVELBASE + 1; + } + else if (strcmp(szString, "MAP") == 0 || + strcmp(szString,"ACCEPT") == 0 || + strcmp(szString,"BEGIN") == 0 || + strcmp(szString,"CASE") == 0 || + strcmp(szString,"EXECUTE") == 0 || + strcmp(szString,"IF") == 0 || + strcmp(szString,"ITEMIZE") == 0 || + strcmp(szString,"INTERFACE") == 0 || + strcmp(szString,"JOIN") == 0 || + strcmp(szString,"LOOP") == 0 || + strcmp(szString,"MODULE") == 0 || + strcmp(szString,"RECORD") == 0) { + iLevel++; + } + else if (strcmp(szString, "APPLICATION") == 0 || + strcmp(szString, "CLASS") == 0 || + strcmp(szString, "DETAIL") == 0 || + strcmp(szString, "FILE") == 0 || + strcmp(szString, "FOOTER") == 0 || + strcmp(szString, "FORM") == 0 || + strcmp(szString, "GROUP") == 0 || + strcmp(szString, "HEADER") == 0 || + strcmp(szString, "INTERFACE") == 0 || + strcmp(szString, "MENU") == 0 || + strcmp(szString, "MENUBAR") == 0 || + strcmp(szString, "OLE") == 0 || + strcmp(szString, "OPTION") == 0 || + strcmp(szString, "QUEUE") == 0 || + strcmp(szString, "REPORT") == 0 || + strcmp(szString, "SHEET") == 0 || + strcmp(szString, "TAB") == 0 || + strcmp(szString, "TOOLBAR") == 0 || + strcmp(szString, "VIEW") == 0 || + strcmp(szString, "WINDOW") == 0) { + iLevel++; + } + else if (strcmp(szString, "END") == 0 || + strcmp(szString, "UNTIL") == 0 || + strcmp(szString, "WHILE") == 0) { + iLevel--; + } + } + return(iLevel); +} + +// Clarion Language Folding Procedure +static void FoldClarionDoc(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *[], Accessor &accStyler) { + + unsigned int uiEndPos = uiStartPos + iLength; + int iLineCurrent = accStyler.GetLine(uiStartPos); + int iLevelPrev = accStyler.LevelAt(iLineCurrent) & SC_FOLDLEVELNUMBERMASK; + int iLevelCurrent = iLevelPrev; + char chNext = accStyler[uiStartPos]; + int iStyle = iInitStyle; + int iStyleNext = accStyler.StyleAt(uiStartPos); + int iVisibleChars = 0; + int iLastStart = 0; + + for (unsigned int uiPos = uiStartPos; uiPos < uiEndPos; uiPos++) { + + char chChar = chNext; + chNext = accStyler.SafeGetCharAt(uiPos + 1); + int iStylePrev = iStyle; + iStyle = iStyleNext; + iStyleNext = accStyler.StyleAt(uiPos + 1); + bool bEOL = (chChar == '\r' && chNext != '\n') || (chChar == '\n'); + + if (iStylePrev == SCE_CLW_DEFAULT) { + if (iStyle == SCE_CLW_KEYWORD || iStyle == SCE_CLW_STRUCTURE_DATA_TYPE) { + // Store last word start point. + iLastStart = uiPos; + } + } + + if (iStylePrev == SCE_CLW_KEYWORD || iStylePrev == SCE_CLW_STRUCTURE_DATA_TYPE) { + if(iswordchar(chChar) && !iswordchar(chNext)) { + char chBuffer[100]; + FillBuffer(iLastStart, uiPos, accStyler, chBuffer, sizeof(chBuffer)); + iLevelCurrent = ClassifyClarionFoldPoint(iLevelCurrent,chBuffer); + // if ((iLevelCurrent == SC_FOLDLEVELBASE + 1) && iLineCurrent > 1) { + // accStyler.SetLevel(iLineCurrent-1,SC_FOLDLEVELBASE); + // iLevelPrev = SC_FOLDLEVELBASE; + // } + } + } + + if (bEOL) { + int iLevel = iLevelPrev; + if ((iLevelCurrent > iLevelPrev) && (iVisibleChars > 0)) + iLevel |= SC_FOLDLEVELHEADERFLAG; + if (iLevel != accStyler.LevelAt(iLineCurrent)) { + accStyler.SetLevel(iLineCurrent,iLevel); + } + iLineCurrent++; + iLevelPrev = iLevelCurrent; + iVisibleChars = 0; + } + + if (!isspacechar(chChar)) + iVisibleChars++; + } + + // Fill in the real level of the next line, keeping the current flags + // as they will be filled in later. + int iFlagsNext = accStyler.LevelAt(iLineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + accStyler.SetLevel(iLineCurrent, iLevelPrev | iFlagsNext); +} + +// Word List Descriptions +static const char * const rgWordListDescriptions[] = { + "Clarion Keywords", + "Compiler Directives", + "Built-in Procedures and Functions", + "Runtime Expressions", + "Structure and Data Types", + "Attributes", + "Standard Equates", + "Reserved Words (Labels)", + "Reserved Words (Procedure Labels)", + 0, +}; + +// Case Sensitive Clarion Language Lexer +LexerModule lmClw(SCLEX_CLW, ColouriseClarionDocSensitive, "clarion", FoldClarionDoc, rgWordListDescriptions); + +// Case Insensitive Clarion Language Lexer +LexerModule lmClwNoCase(SCLEX_CLWNOCASE, ColouriseClarionDocInsensitive, "clarionnocase", FoldClarionDoc, rgWordListDescriptions); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCPP.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCPP.cxx new file mode 100644 index 00000000..affa36e0 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCPP.cxx @@ -0,0 +1,489 @@ +// Scintilla source code edit control +/** @file LexCPP.cxx + ** Lexer for C++, C, Java, and JavaScript. + **/ +// Copyright 1998-2005 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +#define SET_LOWER "abcdefghijklmnopqrstuvwxyz" +#define SET_UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define SET_DIGITS "0123456789" + +class SetOfCharacters { + int size; + bool valueAfter; + bool *bset; +public: + SetOfCharacters(const char *setOfCharacters, int size_=0x80, bool valueAfter_=false) { + size = size_; + valueAfter = valueAfter_; + bset = new bool[size]; + for (int i=0; i < size; i++) { + bset[i] = false; + } + for (const char *cp=setOfCharacters; *cp; cp++) { + int val = static_cast(*cp); + PLATFORM_ASSERT(val >= 0); + PLATFORM_ASSERT(val < size); + bset[val] = true; + } + } + ~SetOfCharacters() { + delete []bset; + bset = 0; + size = 0; + } + void Add(int val) { + PLATFORM_ASSERT(val >= 0); + PLATFORM_ASSERT(val < size); + bset[val] = true; + } + bool Contains(int val) { + PLATFORM_ASSERT(val >= 0); + return (val < size) ? bset[val] : valueAfter; + } +}; + +static bool IsSpaceEquiv(int state) { + return (state <= SCE_C_COMMENTDOC) || + // including SCE_C_DEFAULT, SCE_C_COMMENT, SCE_C_COMMENTLINE + (state == SCE_C_COMMENTLINEDOC) || (state == SCE_C_COMMENTDOCKEYWORD) || + (state == SCE_C_COMMENTDOCKEYWORDERROR); +} + +static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler, bool caseSensitive) { + + WordList &keywords = *keywordlists[0]; + WordList &keywords2 = *keywordlists[1]; + WordList &keywords3 = *keywordlists[2]; + WordList &keywords4 = *keywordlists[3]; + + bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0; + + SetOfCharacters setOKBeforeRE("(=,"); + + SetOfCharacters setDoxygen("$@\\&<>#{}[]" SET_LOWER); + + SetOfCharacters setWordStart("_" SET_LOWER SET_UPPER, 0x80, true); + SetOfCharacters setWord("._" SET_LOWER SET_UPPER SET_DIGITS, 0x80, true); + if (styler.GetPropertyInt("lexer.cpp.allow.dollars", 1) != 0) { + setWordStart.Add('$'); + setWord.Add('$'); + } + + int chPrevNonWhite = ' '; + int visibleChars = 0; + bool lastWordWasUUID = false; + int styleBeforeDCKeyword = SCE_C_DEFAULT; + bool continuationLine = false; + + if (initStyle == SCE_C_PREPROCESSOR) { + // Set continuationLine if last character of previous line is '\' + int lineCurrent = styler.GetLine(startPos); + if (lineCurrent > 0) { + int chBack = styler.SafeGetCharAt(startPos-1, 0); + int chBack2 = styler.SafeGetCharAt(startPos-2, 0); + int lineEndChar = '!'; + if (chBack2 == '\r' && chBack == '\n') { + lineEndChar = styler.SafeGetCharAt(startPos-3, 0); + } else if (chBack == '\n' || chBack == '\r') { + lineEndChar = chBack2; + } + continuationLine = lineEndChar == '\\'; + } + } + + // look back to set chPrevNonWhite properly for better regex colouring + if (startPos > 0) { + int back = startPos; + while (--back && IsSpaceEquiv(styler.StyleAt(back))) + ; + if (styler.StyleAt(back) == SCE_C_OPERATOR) { + chPrevNonWhite = styler.SafeGetCharAt(back); + } + } + + StyleContext sc(startPos, length, initStyle, styler); + + for (; sc.More(); sc.Forward()) { + + if (sc.atLineStart) { + if (sc.state == SCE_C_STRING) { + // Prevent SCE_C_STRINGEOL from leaking back to previous line which + // ends with a line continuation by locking in the state upto this position. + sc.SetState(SCE_C_STRING); + } + // Reset states to begining of colourise so no surprises + // if different sets of lines lexed. + visibleChars = 0; + lastWordWasUUID = false; + } + + // Handle line continuation generically. + if (sc.ch == '\\') { + if (sc.chNext == '\n' || sc.chNext == '\r') { + sc.Forward(); + if (sc.ch == '\r' && sc.chNext == '\n') { + sc.Forward(); + } + continuationLine = true; + continue; + } + } + + // Determine if the current state should terminate. + switch (sc.state) { + case SCE_C_OPERATOR: + sc.SetState(SCE_C_DEFAULT); + break; + case SCE_C_NUMBER: + // We accept almost anything because of hex. and number suffixes + if (!setWord.Contains(sc.ch)) { + sc.SetState(SCE_C_DEFAULT); + } + break; + case SCE_C_IDENTIFIER: + if (!setWord.Contains(sc.ch) || (sc.ch == '.')) { + char s[1000]; + if (caseSensitive) { + sc.GetCurrent(s, sizeof(s)); + } else { + sc.GetCurrentLowered(s, sizeof(s)); + } + if (keywords.InList(s)) { + lastWordWasUUID = strcmp(s, "uuid") == 0; + sc.ChangeState(SCE_C_WORD); + } else if (keywords2.InList(s)) { + sc.ChangeState(SCE_C_WORD2); + } else if (keywords4.InList(s)) { + sc.ChangeState(SCE_C_GLOBALCLASS); + } + sc.SetState(SCE_C_DEFAULT); + } + break; + case SCE_C_PREPROCESSOR: + if (sc.atLineStart && !continuationLine) { + sc.SetState(SCE_C_DEFAULT); + } else if (stylingWithinPreprocessor) { + if (IsASpace(sc.ch)) { + sc.SetState(SCE_C_DEFAULT); + } + } else { + if (sc.Match('/', '*') || sc.Match('/', '/')) { + sc.SetState(SCE_C_DEFAULT); + } + } + break; + case SCE_C_COMMENT: + if (sc.Match('*', '/')) { + sc.Forward(); + sc.ForwardSetState(SCE_C_DEFAULT); + } + break; + case SCE_C_COMMENTDOC: + if (sc.Match('*', '/')) { + sc.Forward(); + sc.ForwardSetState(SCE_C_DEFAULT); + } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support + // Verify that we have the conditions to mark a comment-doc-keyword + if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) { + styleBeforeDCKeyword = SCE_C_COMMENTDOC; + sc.SetState(SCE_C_COMMENTDOCKEYWORD); + } + } + break; + case SCE_C_COMMENTLINE: + if (sc.atLineStart) { + sc.SetState(SCE_C_DEFAULT); + } + break; + case SCE_C_COMMENTLINEDOC: + if (sc.atLineStart) { + sc.SetState(SCE_C_DEFAULT); + } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support + // Verify that we have the conditions to mark a comment-doc-keyword + if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) { + styleBeforeDCKeyword = SCE_C_COMMENTLINEDOC; + sc.SetState(SCE_C_COMMENTDOCKEYWORD); + } + } + break; + case SCE_C_COMMENTDOCKEYWORD: + if ((styleBeforeDCKeyword == SCE_C_COMMENTDOC) && sc.Match('*', '/')) { + sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR); + sc.Forward(); + sc.ForwardSetState(SCE_C_DEFAULT); + } else if (!setDoxygen.Contains(sc.ch)) { + char s[100]; + if (caseSensitive) { + sc.GetCurrent(s, sizeof(s)); + } else { + sc.GetCurrentLowered(s, sizeof(s)); + } + if (!IsASpace(sc.ch) || !keywords3.InList(s + 1)) { + sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR); + } + sc.SetState(styleBeforeDCKeyword); + } + break; + case SCE_C_STRING: + if (sc.atLineEnd) { + sc.ChangeState(SCE_C_STRINGEOL); + } else if (sc.ch == '\\') { + if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { + sc.Forward(); + } + } else if (sc.ch == '\"') { + sc.ForwardSetState(SCE_C_DEFAULT); + } + break; + case SCE_C_CHARACTER: + if (sc.atLineEnd) { + sc.ChangeState(SCE_C_STRINGEOL); + } else if (sc.ch == '\\') { + if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { + sc.Forward(); + } + } else if (sc.ch == '\'') { + sc.ForwardSetState(SCE_C_DEFAULT); + } + break; + case SCE_C_REGEX: + if (sc.atLineStart) { + sc.SetState(SCE_C_DEFAULT); + } else if (sc.ch == '/') { + sc.Forward(); + while ((sc.ch < 0x80) && islower(sc.ch)) + sc.Forward(); // gobble regex flags + sc.SetState(SCE_C_DEFAULT); + } else if (sc.ch == '\\') { + // Gobble up the quoted character + if (sc.chNext == '\\' || sc.chNext == '/') { + sc.Forward(); + } + } + break; + case SCE_C_STRINGEOL: + if (sc.atLineStart) { + sc.SetState(SCE_C_DEFAULT); + } + break; + case SCE_C_VERBATIM: + if (sc.ch == '\"') { + if (sc.chNext == '\"') { + sc.Forward(); + } else { + sc.ForwardSetState(SCE_C_DEFAULT); + } + } + break; + case SCE_C_UUID: + if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') { + sc.SetState(SCE_C_DEFAULT); + } + } + + // Determine if a new state should be entered. + if (sc.state == SCE_C_DEFAULT) { + if (sc.Match('@', '\"')) { + sc.SetState(SCE_C_VERBATIM); + sc.Forward(); + } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { + if (lastWordWasUUID) { + sc.SetState(SCE_C_UUID); + lastWordWasUUID = false; + } else { + sc.SetState(SCE_C_NUMBER); + } + } else if (setWordStart.Contains(sc.ch) || (sc.ch == '@')) { + if (lastWordWasUUID) { + sc.SetState(SCE_C_UUID); + lastWordWasUUID = false; + } else { + sc.SetState(SCE_C_IDENTIFIER); + } + } else if (sc.Match('/', '*')) { + if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style + sc.SetState(SCE_C_COMMENTDOC); + } else { + sc.SetState(SCE_C_COMMENT); + } + sc.Forward(); // Eat the * so it isn't used for the end of the comment + } else if (sc.Match('/', '/')) { + if ((sc.Match("///") && !sc.Match("////")) || sc.Match("//!")) + // Support of Qt/Doxygen doc. style + sc.SetState(SCE_C_COMMENTLINEDOC); + else + sc.SetState(SCE_C_COMMENTLINE); + } else if (sc.ch == '/' && setOKBeforeRE.Contains(chPrevNonWhite)) { + sc.SetState(SCE_C_REGEX); // JavaScript's RegEx + } else if (sc.ch == '\"') { + sc.SetState(SCE_C_STRING); + } else if (sc.ch == '\'') { + sc.SetState(SCE_C_CHARACTER); + } else if (sc.ch == '#' && visibleChars == 0) { + // Preprocessor commands are alone on their line + sc.SetState(SCE_C_PREPROCESSOR); + // Skip whitespace between # and preprocessor word + do { + sc.Forward(); + } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More()); + if (sc.atLineEnd) { + sc.SetState(SCE_C_DEFAULT); + } + } else if (isoperator(static_cast(sc.ch))) { + sc.SetState(SCE_C_OPERATOR); + } + } + + if (!IsASpace(sc.ch) && !IsSpaceEquiv(sc.state)) { + chPrevNonWhite = sc.ch; + visibleChars++; + } + continuationLine = false; + } + sc.Complete(); +} + +static bool IsStreamCommentStyle(int style) { + return style == SCE_C_COMMENT || + style == SCE_C_COMMENTDOC || + style == SCE_C_COMMENTDOCKEYWORD || + style == SCE_C_COMMENTDOCKEYWORDERROR; +} + +// Store both the current line's fold level and the next lines in the +// level store to make it easy to pick up with each increment +// and to make it possible to fiddle the current level for "} else {". +static void FoldNoBoxCppDoc(unsigned int startPos, int length, int initStyle, + Accessor &styler) { + bool foldComment = styler.GetPropertyInt("fold.comment") != 0; + bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0; + bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0; + unsigned int endPos = startPos + length; + int visibleChars = 0; + int lineCurrent = styler.GetLine(startPos); + int levelCurrent = SC_FOLDLEVELBASE; + if (lineCurrent > 0) + levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; + int levelMinCurrent = levelCurrent; + int levelNext = levelCurrent; + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + for (unsigned int i = startPos; i < endPos; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + int stylePrev = style; + style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + if (foldComment && IsStreamCommentStyle(style)) { + if (!IsStreamCommentStyle(stylePrev)) { + levelNext++; + } else if (!IsStreamCommentStyle(styleNext) && !atEOL) { + // Comments don't end at end of line and the next character may be unstyled. + levelNext--; + } + } + if (foldComment && (style == SCE_C_COMMENTLINE)) { + if ((ch == '/') && (chNext == '/')) { + char chNext2 = styler.SafeGetCharAt(i + 2); + if (chNext2 == '{') { + levelNext++; + } else if (chNext2 == '}') { + levelNext--; + } + } + } + if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) { + if (ch == '#') { + unsigned int j = i + 1; + while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) { + j++; + } + if (styler.Match(j, "region") || styler.Match(j, "if")) { + levelNext++; + } else if (styler.Match(j, "end")) { + levelNext--; + } + } + } + if (style == SCE_C_OPERATOR) { + if (ch == '{') { + // Measure the minimum before a '{' to allow + // folding on "} else {" + if (levelMinCurrent > levelNext) { + levelMinCurrent = levelNext; + } + levelNext++; + } else if (ch == '}') { + levelNext--; + } + } + if (atEOL) { + int levelUse = levelCurrent; + if (foldAtElse) { + levelUse = levelMinCurrent; + } + int lev = levelUse | levelNext << 16; + if (visibleChars == 0 && foldCompact) + lev |= SC_FOLDLEVELWHITEFLAG; + if (levelUse < levelNext) + lev |= SC_FOLDLEVELHEADERFLAG; + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + levelCurrent = levelNext; + levelMinCurrent = levelCurrent; + visibleChars = 0; + } + if (!IsASpace(ch)) + visibleChars++; + } +} + +static void FoldCppDoc(unsigned int startPos, int length, int initStyle, WordList *[], + Accessor &styler) { + FoldNoBoxCppDoc(startPos, length, initStyle, styler); +} + +static const char * const cppWordLists[] = { + "Primary keywords and identifiers", + "Secondary keywords and identifiers", + "Documentation comment keywords", + "Unused", + "Global classes and typedefs", + 0, + }; + +static void ColouriseCppDocSensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) { + ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, true); +} + +static void ColouriseCppDocInsensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) { + ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, false); +} + +LexerModule lmCPP(SCLEX_CPP, ColouriseCppDocSensitive, "cpp", FoldCppDoc, cppWordLists); +LexerModule lmCPPNoCase(SCLEX_CPPNOCASE, ColouriseCppDocInsensitive, "cppnocase", FoldCppDoc, cppWordLists); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCSS.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCSS.cxx new file mode 100644 index 00000000..88e14545 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCSS.cxx @@ -0,0 +1,303 @@ +// Scintilla source code edit control +/** @file LexCSS.cxx + ** Lexer for Cascading Style Sheets + ** Written by Jakub Vrána + ** Improved by Philippe Lhoste (CSS2) + **/ +// Copyright 1998-2002 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + + +static inline bool IsAWordChar(const unsigned int ch) { + return (isalnum(ch) || ch == '-' || ch == '_' || ch >= 161); // _ is not in fact correct CSS word-character +} + +inline bool IsCssOperator(const char ch) { + if (!isalnum(ch) && + (ch == '{' || ch == '}' || ch == ':' || ch == ',' || ch == ';' || + ch == '.' || ch == '#' || ch == '!' || ch == '@' || + /* CSS2 */ + ch == '*' || ch == '>' || ch == '+' || ch == '=' || ch == '~' || ch == '|' || + ch == '[' || ch == ']' || ch == '(' || ch == ')')) { + return true; + } + return false; +} + +static void ColouriseCssDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], Accessor &styler) { + WordList &keywords = *keywordlists[0]; + WordList &pseudoClasses = *keywordlists[1]; + WordList &keywords2 = *keywordlists[2]; + + StyleContext sc(startPos, length, initStyle, styler); + + int lastState = -1; // before operator + int lastStateC = -1; // before comment + int op = ' '; // last operator + + for (; sc.More(); sc.Forward()) { + if (sc.state == SCE_CSS_COMMENT && sc.Match('*', '/')) { + if (lastStateC == -1) { + // backtrack to get last state: + // comments are like whitespace, so we must return to the previous state + unsigned int i = startPos; + for (; i > 0; i--) { + if ((lastStateC = styler.StyleAt(i-1)) != SCE_CSS_COMMENT) { + if (lastStateC == SCE_CSS_OPERATOR) { + op = styler.SafeGetCharAt(i-1); + while (--i) { + lastState = styler.StyleAt(i-1); + if (lastState != SCE_CSS_OPERATOR && lastState != SCE_CSS_COMMENT) + break; + } + if (i == 0) + lastState = SCE_CSS_DEFAULT; + } + break; + } + } + if (i == 0) + lastStateC = SCE_CSS_DEFAULT; + } + sc.Forward(); + sc.ForwardSetState(lastStateC); + } + + if (sc.state == SCE_CSS_COMMENT) + continue; + + if (sc.state == SCE_CSS_DOUBLESTRING || sc.state == SCE_CSS_SINGLESTRING) { + if (sc.ch != (sc.state == SCE_CSS_DOUBLESTRING ? '\"' : '\'')) + continue; + unsigned int i = sc.currentPos; + while (i && styler[i-1] == '\\') + i--; + if ((sc.currentPos - i) % 2 == 1) + continue; + sc.ForwardSetState(SCE_CSS_VALUE); + } + + if (sc.state == SCE_CSS_OPERATOR) { + if (op == ' ') { + unsigned int i = startPos; + op = styler.SafeGetCharAt(i-1); + while (--i) { + lastState = styler.StyleAt(i-1); + if (lastState != SCE_CSS_OPERATOR && lastState != SCE_CSS_COMMENT) + break; + } + } + switch (op) { + case '@': + if (lastState == SCE_CSS_DEFAULT) + sc.SetState(SCE_CSS_DIRECTIVE); + break; + case '*': + if (lastState == SCE_CSS_DEFAULT) + sc.SetState(SCE_CSS_TAG); + break; + case '>': + case '+': + if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_CLASS + || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS) + sc.SetState(SCE_CSS_DEFAULT); + break; + case '[': + if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT || + lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS) + sc.SetState(SCE_CSS_ATTRIBUTE); + break; + case ']': + if (lastState == SCE_CSS_ATTRIBUTE) + sc.SetState(SCE_CSS_TAG); + break; + case '{': + if (lastState == SCE_CSS_DIRECTIVE) + sc.SetState(SCE_CSS_DEFAULT); + else if (lastState == SCE_CSS_TAG) + sc.SetState(SCE_CSS_IDENTIFIER); + break; + case '}': + if (lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_VALUE || lastState == SCE_CSS_IMPORTANT || + lastState == SCE_CSS_IDENTIFIER || lastState == SCE_CSS_IDENTIFIER2) + sc.SetState(SCE_CSS_DEFAULT); + break; + case ':': + if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT || + lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS) + sc.SetState(SCE_CSS_PSEUDOCLASS); + else if (lastState == SCE_CSS_IDENTIFIER || lastState == SCE_CSS_IDENTIFIER2 || lastState == SCE_CSS_UNKNOWN_IDENTIFIER) + sc.SetState(SCE_CSS_VALUE); + break; + case '.': + if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT || + lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS) + sc.SetState(SCE_CSS_CLASS); + break; + case '#': + if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT || + lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS) + sc.SetState(SCE_CSS_ID); + break; + case ',': + if (lastState == SCE_CSS_TAG) + sc.SetState(SCE_CSS_DEFAULT); + break; + case ';': + if (lastState == SCE_CSS_DIRECTIVE) + sc.SetState(SCE_CSS_DEFAULT); + else if (lastState == SCE_CSS_VALUE || lastState == SCE_CSS_IMPORTANT) + sc.SetState(SCE_CSS_IDENTIFIER); + break; + case '!': + if (lastState == SCE_CSS_VALUE) + sc.SetState(SCE_CSS_IMPORTANT); + break; + } + } + + if (IsAWordChar(sc.ch)) { + if (sc.state == SCE_CSS_DEFAULT) + sc.SetState(SCE_CSS_TAG); + continue; + } + + if (IsAWordChar(sc.chPrev) && ( + sc.state == SCE_CSS_IDENTIFIER || sc.state == SCE_CSS_IDENTIFIER2 + || sc.state == SCE_CSS_UNKNOWN_IDENTIFIER + || sc.state == SCE_CSS_PSEUDOCLASS || sc.state == SCE_CSS_UNKNOWN_PSEUDOCLASS + || sc.state == SCE_CSS_IMPORTANT + )) { + char s[100]; + sc.GetCurrentLowered(s, sizeof(s)); + char *s2 = s; + while (*s2 && !IsAWordChar(*s2)) + s2++; + switch (sc.state) { + case SCE_CSS_IDENTIFIER: + if (!keywords.InList(s2)) { + if (keywords2.InList(s2)) { + sc.ChangeState(SCE_CSS_IDENTIFIER2); + } else { + sc.ChangeState(SCE_CSS_UNKNOWN_IDENTIFIER); + } + } + break; + case SCE_CSS_UNKNOWN_IDENTIFIER: + if (keywords.InList(s2)) + sc.ChangeState(SCE_CSS_IDENTIFIER); + else if (keywords2.InList(s2)) + sc.ChangeState(SCE_CSS_IDENTIFIER2); + break; + case SCE_CSS_PSEUDOCLASS: + if (!pseudoClasses.InList(s2)) + sc.ChangeState(SCE_CSS_UNKNOWN_PSEUDOCLASS); + break; + case SCE_CSS_UNKNOWN_PSEUDOCLASS: + if (pseudoClasses.InList(s2)) + sc.ChangeState(SCE_CSS_PSEUDOCLASS); + break; + case SCE_CSS_IMPORTANT: + if (strcmp(s2, "important") != 0) + sc.ChangeState(SCE_CSS_VALUE); + break; + } + } + + if (sc.ch != '.' && sc.ch != ':' && sc.ch != '#' && (sc.state == SCE_CSS_CLASS || sc.state == SCE_CSS_PSEUDOCLASS || sc.state == SCE_CSS_UNKNOWN_PSEUDOCLASS || sc.state == SCE_CSS_ID)) + sc.SetState(SCE_CSS_TAG); + + if (sc.Match('/', '*')) { + lastStateC = sc.state; + sc.SetState(SCE_CSS_COMMENT); + sc.Forward(); + } else if (sc.state == SCE_CSS_VALUE && (sc.ch == '\"' || sc.ch == '\'')) { + sc.SetState((sc.ch == '\"' ? SCE_CSS_DOUBLESTRING : SCE_CSS_SINGLESTRING)); + } else if (IsCssOperator(static_cast(sc.ch)) + && (sc.state != SCE_CSS_ATTRIBUTE || sc.ch == ']') + && (sc.state != SCE_CSS_VALUE || sc.ch == ';' || sc.ch == '}' || sc.ch == '!') + && (sc.state != SCE_CSS_DIRECTIVE || sc.ch == ';' || sc.ch == '{') + ) { + if (sc.state != SCE_CSS_OPERATOR) + lastState = sc.state; + sc.SetState(SCE_CSS_OPERATOR); + op = sc.ch; + } + } + + sc.Complete(); +} + +static void FoldCSSDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) { + bool foldComment = styler.GetPropertyInt("fold.comment") != 0; + bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + unsigned int endPos = startPos + length; + int visibleChars = 0; + int lineCurrent = styler.GetLine(startPos); + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + char chNext = styler[startPos]; + bool inComment = (styler.StyleAt(startPos-1) == SCE_CSS_COMMENT); + for (unsigned int i = startPos; i < endPos; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + int style = styler.StyleAt(i); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + if (foldComment) { + if (!inComment && (style == SCE_CSS_COMMENT)) + levelCurrent++; + else if (inComment && (style != SCE_CSS_COMMENT)) + levelCurrent--; + inComment = (style == SCE_CSS_COMMENT); + } + if (style == SCE_CSS_OPERATOR) { + if (ch == '{') { + levelCurrent++; + } else if (ch == '}') { + levelCurrent--; + } + } + if (atEOL) { + int lev = levelPrev; + if (visibleChars == 0 && foldCompact) + lev |= SC_FOLDLEVELWHITEFLAG; + if ((levelCurrent > levelPrev) && (visibleChars > 0)) + lev |= SC_FOLDLEVELHEADERFLAG; + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + levelPrev = levelCurrent; + visibleChars = 0; + } + if (!isspacechar(ch)) + visibleChars++; + } + // Fill in the real level of the next line, keeping the current flags as they will be filled in later + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + styler.SetLevel(lineCurrent, levelPrev | flagsNext); +} + +static const char * const cssWordListDesc[] = { + "CSS1 Keywords", + "Pseudo classes", + "CSS2 Keywords", + 0 +}; + +LexerModule lmCss(SCLEX_CSS, ColouriseCssDoc, "css", FoldCSSDoc, cssWordListDesc); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCaml.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCaml.cxx new file mode 100644 index 00000000..6d6eee75 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCaml.cxx @@ -0,0 +1,399 @@ +// Scintilla source code edit control +/** @file LexCaml.cxx + ** Lexer for Objective Caml. + **/ +// Copyright 2005 by Robert Roessler +// The License.txt file describes the conditions under which this software may be distributed. +/* Release History + 20050204 Initial release. + 20050205 Quick compiler standards/"cleanliness" adjustment. + 20050206 Added cast for IsLeadByte(). + 20050209 Changes to "external" build support. + 20050306 Fix for 1st-char-in-doc "corner" case. + 20050502 Fix for [harmless] one-past-the-end coloring. + 20050515 Refined numeric token recognition logic. + 20051125 Added 2nd "optional" keywords class. + 20051129 Support "magic" (read-only) comments for RCaml. + 20051204 Swtich to using StyleContext infrastructure. +*/ + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +// Since the Microsoft __iscsym[f] funcs are not ANSI... +inline int iscaml(int c) {return isalnum(c) || c == '_';} +inline int iscamlf(int c) {return isalpha(c) || c == '_';} +inline int iscamld(int c) {return isdigit(c) || c == '_';} + +static const int baseT[24] = { + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A - L */ + 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0,16 /* M - X */ +}; + +#ifdef BUILD_AS_EXTERNAL_LEXER +/* + (actually seems to work!) +*/ +#include "WindowAccessor.h" +#include "ExternalLexer.h" + +#if PLAT_WIN +#include +#endif + +static void ColouriseCamlDoc( + unsigned int startPos, int length, + int initStyle, + WordList *keywordlists[], + Accessor &styler); + +static void FoldCamlDoc( + unsigned int startPos, int length, + int initStyle, + WordList *keywordlists[], + Accessor &styler); + +static void InternalLexOrFold(int lexOrFold, unsigned int startPos, int length, + int initStyle, char *words[], WindowID window, char *props); + +static const char* LexerName = "caml"; + +#ifdef TRACE +void Platform::DebugPrintf(const char *format, ...) { + char buffer[2000]; + va_list pArguments; + va_start(pArguments, format); + vsprintf(buffer,format,pArguments); + va_end(pArguments); + Platform::DebugDisplay(buffer); +} +#else +void Platform::DebugPrintf(const char *, ...) { +} +#endif + +bool Platform::IsDBCSLeadByte(int codePage, char ch) { + return ::IsDBCSLeadByteEx(codePage, ch) != 0; +} + +long Platform::SendScintilla(WindowID w, unsigned int msg, unsigned long wParam, long lParam) { + return ::SendMessage(reinterpret_cast(w), msg, wParam, lParam); +} + +long Platform::SendScintillaPointer(WindowID w, unsigned int msg, unsigned long wParam, void *lParam) { + return ::SendMessage(reinterpret_cast(w), msg, wParam, + reinterpret_cast(lParam)); +} + +void EXT_LEXER_DECL Fold(unsigned int lexer, unsigned int startPos, int length, + int initStyle, char *words[], WindowID window, char *props) +{ + // below useless evaluation(s) to supress "not used" warnings + lexer; + // build expected data structures and do the Fold + InternalLexOrFold(1, startPos, length, initStyle, words, window, props); + +} + +int EXT_LEXER_DECL GetLexerCount() +{ + return 1; // just us [Objective] Caml lexers here! +} + +void EXT_LEXER_DECL GetLexerName(unsigned int Index, char *name, int buflength) +{ + // below useless evaluation(s) to supress "not used" warnings + Index; + // return as much of our lexer name as will fit (what's up with Index?) + if (buflength > 0) { + buflength--; + int n = strlen(LexerName); + if (n > buflength) + n = buflength; + memcpy(name, LexerName, n), name[n] = '\0'; + } +} + +void EXT_LEXER_DECL Lex(unsigned int lexer, unsigned int startPos, int length, + int initStyle, char *words[], WindowID window, char *props) +{ + // below useless evaluation(s) to supress "not used" warnings + lexer; + // build expected data structures and do the Lex + InternalLexOrFold(0, startPos, length, initStyle, words, window, props); +} + +static void InternalLexOrFold(int foldOrLex, unsigned int startPos, int length, + int initStyle, char *words[], WindowID window, char *props) +{ + // create and initialize a WindowAccessor (including contained PropSet) + PropSet ps; + ps.SetMultiple(props); + WindowAccessor wa(window, ps); + // create and initialize WordList(s) + int nWL = 0; + for (; words[nWL]; nWL++) ; // count # of WordList PTRs needed + WordList** wl = new WordList* [nWL + 1];// alloc WordList PTRs + int i = 0; + for (; i < nWL; i++) { + wl[i] = new WordList(); // (works or THROWS bad_alloc EXCEPTION) + wl[i]->Set(words[i]); + } + wl[i] = 0; + // call our "internal" folder/lexer (... then do Flush!) + if (foldOrLex) + FoldCamlDoc(startPos, length, initStyle, wl, wa); + else + ColouriseCamlDoc(startPos, length, initStyle, wl, wa); + wa.Flush(); + // clean up before leaving + for (i = nWL - 1; i >= 0; i--) + delete wl[i]; + delete [] wl; +} + +static +#endif /* BUILD_AS_EXTERNAL_LEXER */ + +void ColouriseCamlDoc( + unsigned int startPos, int length, + int initStyle, + WordList *keywordlists[], + Accessor &styler) +{ + // initialize styler + StyleContext sc(startPos, length, initStyle, styler); + // set up [initial] state info (terminating states that shouldn't "bleed") + int nesting = 0; + if (sc.state < SCE_CAML_STRING) + sc.state = SCE_CAML_DEFAULT; + if (sc.state >= SCE_CAML_COMMENT) + nesting = (sc.state & 0x0f) - SCE_CAML_COMMENT; + + int chBase = 0, chToken = 0, chLit = 0; + WordList& keywords = *keywordlists[0]; + WordList& keywords2 = *keywordlists[1]; + WordList& keywords3 = *keywordlists[2]; + const int useMagic = styler.GetPropertyInt("lexer.caml.magic", 0); + + // foreach char in range... + while (sc.More()) { + // set up [per-char] state info + int state2 = -1; // (ASSUME no state change) + int chColor = sc.currentPos - 1;// (ASSUME standard coloring range) + bool advance = true; // (ASSUME scanner "eats" 1 char) + + // step state machine + switch (sc.state & 0x0f) { + case SCE_CAML_DEFAULT: + chToken = sc.currentPos; // save [possible] token start (JIC) + // it's wide open; what do we have? + if (iscamlf(sc.ch)) + state2 = SCE_CAML_IDENTIFIER; + else if (sc.Match('`') && iscamlf(sc.chNext)) + state2 = SCE_CAML_TAGNAME; + else if (sc.Match('#') && isdigit(sc.chNext)) + state2 = SCE_CAML_LINENUM; + else if (isdigit(sc.ch)) { + state2 = SCE_CAML_NUMBER, chBase = 10; + if (sc.Match('0') && strchr("bBoOxX", sc.chNext)) + chBase = baseT[tolower(sc.chNext) - 'a'], sc.Forward(); + } else if (sc.Match('\'')) /* (char literal?) */ + state2 = SCE_CAML_CHAR, chLit = 0; + else if (sc.Match('\"')) + state2 = SCE_CAML_STRING; + else if (sc.Match('(', '*')) + state2 = SCE_CAML_COMMENT, + sc.ch = ' ', // (make SURE "(*)" isn't seen as a closed comment) + sc.Forward(); + else if (strchr("!?~" /* Caml "prefix-symbol" */ + "=<>@^|&+-*/$%" /* Caml "infix-symbol" */ + "()[]{};,:.#", sc.ch)) /* Caml "bracket" or ;,:.# */ + state2 = SCE_CAML_OPERATOR; + break; + + case SCE_CAML_IDENTIFIER: + // [try to] interpret as [additional] identifier char + if (!(iscaml(sc.ch) || sc.Match('\''))) { + const int n = sc.currentPos - chToken; + if (n < 24) { + // length is believable as keyword, [re-]construct token + char t[24]; + for (int i = -n; i < 0; i++) + t[n + i] = static_cast(sc.GetRelative(i)); + t[n] = '\0'; + // special-case "_" token as KEYWORD + if ((n == 1 && sc.chPrev == '_') || keywords.InList(t)) + sc.ChangeState(SCE_CAML_KEYWORD); + else if (keywords2.InList(t)) + sc.ChangeState(SCE_CAML_KEYWORD2); + else if (keywords3.InList(t)) + sc.ChangeState(SCE_CAML_KEYWORD3); + } + state2 = SCE_CAML_DEFAULT, advance = false; + } + break; + + case SCE_CAML_TAGNAME: + // [try to] interpret as [additional] tagname char + if (!(iscaml(sc.ch) || sc.Match('\''))) + state2 = SCE_CAML_DEFAULT, advance = false; + break; + + /*case SCE_CAML_KEYWORD: + case SCE_CAML_KEYWORD2: + case SCE_CAML_KEYWORD3: + // [try to] interpret as [additional] keyword char + if (!iscaml(ch)) + state2 = SCE_CAML_DEFAULT, advance = false; + break;*/ + + case SCE_CAML_LINENUM: + // [try to] interpret as [additional] linenum directive char + if (!isdigit(sc.ch)) + state2 = SCE_CAML_DEFAULT, advance = false; + break; + + case SCE_CAML_OPERATOR: { + // [try to] interpret as [additional] operator char + const char* o = 0; + if (iscaml(sc.ch) || isspace(sc.ch) /* ident or whitespace */ + || (o = strchr(")]};,\'\"`#", sc.ch),o)/* "termination" chars */ + || !strchr("!$%&*+-./:<=>?@^|~", sc.ch)/* "operator" chars */) { + // check for INCLUSIVE termination + if (o && strchr(")]};,", sc.ch)) { + if ((sc.Match(')') && sc.chPrev == '(') + || (sc.Match(']') && sc.chPrev == '[')) + // special-case "()" and "[]" tokens as KEYWORDS + sc.ChangeState(SCE_CAML_KEYWORD); + chColor++; + } else + advance = false; + state2 = SCE_CAML_DEFAULT; + } + break; + } + + case SCE_CAML_NUMBER: + // [try to] interpret as [additional] numeric literal char + // N.B. - improperly accepts "extra" digits in base 2 or 8 literals + if (iscamld(sc.ch) || IsADigit(sc.ch, chBase)) + break; + // how about an integer suffix? + if ((sc.Match('l') || sc.Match('L') || sc.Match('n')) + && (iscamld(sc.chPrev) || IsADigit(sc.chPrev, chBase))) + break; + // or a floating-point literal? + if (chBase == 10) { + // with a decimal point? + if (sc.Match('.') && iscamld(sc.chPrev)) + break; + // with an exponent? (I) + if ((sc.Match('e') || sc.Match('E')) + && (iscamld(sc.chPrev) || sc.chPrev == '.')) + break; + // with an exponent? (II) + if ((sc.Match('+') || sc.Match('-')) + && (sc.chPrev == 'e' || sc.chPrev == 'E')) + break; + } + // it looks like we have run out of number + state2 = SCE_CAML_DEFAULT, advance = false; + break; + + case SCE_CAML_CHAR: + // [try to] interpret as [additional] char literal char + if (sc.Match('\\')) { + chLit = 1; // (definitely IS a char literal) + if (sc.chPrev == '\\') + sc.ch = ' '; // (so termination test isn't fooled) + // should we be terminating - one way or another? + } else if ((sc.Match('\'') && sc.chPrev != '\\') || sc.atLineEnd) { + state2 = SCE_CAML_DEFAULT; + if (sc.Match('\'')) + chColor++; + else + sc.ChangeState(SCE_CAML_IDENTIFIER); + // ... maybe a char literal, maybe not + } else if (chLit < 1 && sc.currentPos - chToken >= 2) + sc.ChangeState(SCE_CAML_IDENTIFIER), advance = false; + break; + + case SCE_CAML_STRING: + // [try to] interpret as [additional] string literal char + if (sc.Match('\\') && sc.chPrev == '\\') + sc.ch = ' '; // (so '\\' doesn't cause us trouble) + else if (sc.Match('\"') && sc.chPrev != '\\') + state2 = SCE_CAML_DEFAULT, chColor++; + break; + + case SCE_CAML_COMMENT: + case SCE_CAML_COMMENT1: + case SCE_CAML_COMMENT2: + case SCE_CAML_COMMENT3: + // we're IN a comment - does this start a NESTED comment? + if (sc.Match('(', '*')) + state2 = sc.state + 1, chToken = sc.currentPos, + sc.ch = ' ', // (make SURE "(*)" isn't seen as a closed comment) + sc.Forward(), nesting++; + // [try to] interpret as [additional] comment char + else if (sc.Match(')') && sc.chPrev == '*') { + if (nesting) + state2 = (sc.state & 0x0f) - 1, chToken = 0, nesting--; + else + state2 = SCE_CAML_DEFAULT; + chColor++; + // enable "magic" (read-only) comment AS REQUIRED + } else if (useMagic && sc.currentPos - chToken == 4 + && sc.Match('c') && sc.chPrev == 'r' && sc.GetRelative(-2) == '@') + sc.state |= 0x10; // (switch to read-only comment style) + break; + } + + // handle state change and char coloring as required + if (state2 >= 0) + styler.ColourTo(chColor, sc.state), sc.ChangeState(state2); + // move to next char UNLESS re-scanning current char + if (advance) + sc.Forward(); + } + + // do any required terminal char coloring (JIC) + sc.Complete(); +} + +#ifdef BUILD_AS_EXTERNAL_LEXER +static +#endif /* BUILD_AS_EXTERNAL_LEXER */ +void FoldCamlDoc( + unsigned int startPos, int length, + int initStyle, + WordList *keywordlists[], + Accessor &styler) +{ + // below useless evaluation(s) to supress "not used" warnings + startPos || length || initStyle || keywordlists[0] || styler.Length(); +} + +static const char * const camlWordListDesc[] = { + "Keywords", // primary Objective Caml keywords + "Keywords2", // "optional" keywords (typically from Pervasives) + "Keywords3", // "optional" keywords (typically typenames) + 0 +}; + +#ifndef BUILD_AS_EXTERNAL_LEXER +LexerModule lmCaml(SCLEX_CAML, ColouriseCamlDoc, "caml", FoldCamlDoc, camlWordListDesc); +#endif /* BUILD_AS_EXTERNAL_LEXER */ diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexConf.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexConf.cxx new file mode 100644 index 00000000..3325b341 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexConf.cxx @@ -0,0 +1,184 @@ +// Scintilla source code edit control +/** @file LexConf.cxx + ** Lexer for Apache Configuration Files. + ** + ** First working version contributed by Ahmad Zawawi on October 28, 2000. + ** i created this lexer because i needed something pretty when dealing + ** when Apache Configuration files... + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +static void ColouriseConfDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler) +{ + int state = SCE_CONF_DEFAULT; + char chNext = styler[startPos]; + int lengthDoc = startPos + length; + // create a buffer large enough to take the largest chunk... + char *buffer = new char[length]; + int bufferCount = 0; + + // this assumes that we have 2 keyword list in conf.properties + WordList &directives = *keywordLists[0]; + WordList ¶ms = *keywordLists[1]; + + // go through all provided text segment + // using the hand-written state machine shown below + styler.StartAt(startPos); + styler.StartSegment(startPos); + for (int i = startPos; i < lengthDoc; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + + if (styler.IsLeadByte(ch)) { + chNext = styler.SafeGetCharAt(i + 2); + i++; + continue; + } + switch(state) { + case SCE_CONF_DEFAULT: + if( ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ') { + // whitespace is simply ignored here... + styler.ColourTo(i,SCE_CONF_DEFAULT); + break; + } else if( ch == '#' ) { + // signals the start of a comment... + state = SCE_CONF_COMMENT; + styler.ColourTo(i,SCE_CONF_COMMENT); + } else if( ch == '.' /*|| ch == '/'*/) { + // signals the start of a file... + state = SCE_CONF_EXTENSION; + styler.ColourTo(i,SCE_CONF_EXTENSION); + } else if( ch == '"') { + state = SCE_CONF_STRING; + styler.ColourTo(i,SCE_CONF_STRING); + } else if( ispunct(ch) ) { + // signals an operator... + // no state jump necessary for this + // simple case... + styler.ColourTo(i,SCE_CONF_OPERATOR); + } else if( isalpha(ch) ) { + // signals the start of an identifier + bufferCount = 0; + buffer[bufferCount++] = static_cast(tolower(ch)); + state = SCE_CONF_IDENTIFIER; + } else if( isdigit(ch) ) { + // signals the start of a number + bufferCount = 0; + buffer[bufferCount++] = ch; + //styler.ColourTo(i,SCE_CONF_NUMBER); + state = SCE_CONF_NUMBER; + } else { + // style it the default style.. + styler.ColourTo(i,SCE_CONF_DEFAULT); + } + break; + + case SCE_CONF_COMMENT: + // if we find a newline here, + // we simply go to default state + // else continue to work on it... + if( ch == '\n' || ch == '\r' ) { + state = SCE_CONF_DEFAULT; + } else { + styler.ColourTo(i,SCE_CONF_COMMENT); + } + break; + + case SCE_CONF_EXTENSION: + // if we find a non-alphanumeric char, + // we simply go to default state + // else we're still dealing with an extension... + if( isalnum(ch) || (ch == '_') || + (ch == '-') || (ch == '$') || + (ch == '/') || (ch == '.') || (ch == '*') ) + { + styler.ColourTo(i,SCE_CONF_EXTENSION); + } else { + state = SCE_CONF_DEFAULT; + chNext = styler[i--]; + } + break; + + case SCE_CONF_STRING: + // if we find the end of a string char, we simply go to default state + // else we're still dealing with an string... + if( (ch == '"' && styler.SafeGetCharAt(i-1)!='\\') || (ch == '\n') || (ch == '\r') ) { + state = SCE_CONF_DEFAULT; + } + styler.ColourTo(i,SCE_CONF_STRING); + break; + + case SCE_CONF_IDENTIFIER: + // stay in CONF_IDENTIFIER state until we find a non-alphanumeric + if( isalnum(ch) || (ch == '_') || (ch == '-') || (ch == '/') || (ch == '$') || (ch == '.') || (ch == '*')) { + buffer[bufferCount++] = static_cast(tolower(ch)); + } else { + state = SCE_CONF_DEFAULT; + buffer[bufferCount] = '\0'; + + // check if the buffer contains a keyword, and highlight it if it is a keyword... + if(directives.InList(buffer)) { + styler.ColourTo(i-1,SCE_CONF_DIRECTIVE ); + } else if(params.InList(buffer)) { + styler.ColourTo(i-1,SCE_CONF_PARAMETER ); + } else if(strchr(buffer,'/') || strchr(buffer,'.')) { + styler.ColourTo(i-1,SCE_CONF_EXTENSION); + } else { + styler.ColourTo(i-1,SCE_CONF_DEFAULT); + } + + // push back the faulty character + chNext = styler[i--]; + + } + break; + + case SCE_CONF_NUMBER: + // stay in CONF_NUMBER state until we find a non-numeric + if( isdigit(ch) || ch == '.') { + buffer[bufferCount++] = ch; + } else { + state = SCE_CONF_DEFAULT; + buffer[bufferCount] = '\0'; + + // Colourize here... + if( strchr(buffer,'.') ) { + // it is an IP address... + styler.ColourTo(i-1,SCE_CONF_IP); + } else { + // normal number + styler.ColourTo(i-1,SCE_CONF_NUMBER); + } + + // push back a character + chNext = styler[i--]; + } + break; + + } + } + delete []buffer; +} + +static const char * const confWordListDesc[] = { + "Directives", + "Parameters", + 0 +}; + +LexerModule lmConf(SCLEX_CONF, ColouriseConfDoc, "conf", 0, confWordListDesc); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCrontab.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCrontab.cxx new file mode 100644 index 00000000..f04ff4b8 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCrontab.cxx @@ -0,0 +1,218 @@ +// Scintilla source code edit control +/** @file LexCrontab.cxx + ** Lexer to use with extended crontab files used by a powerful + ** Windows scheduler/event monitor/automation manager nnCron. + ** (http://nemtsev.eserv.ru/) + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +static void ColouriseNncrontabDoc(unsigned int startPos, int length, int, WordList +*keywordLists[], Accessor &styler) +{ + int state = SCE_NNCRONTAB_DEFAULT; + char chNext = styler[startPos]; + int lengthDoc = startPos + length; + // create a buffer large enough to take the largest chunk... + char *buffer = new char[length]; + int bufferCount = 0; + // used when highliting environment variables inside quoted string: + bool insideString = false; + + // this assumes that we have 3 keyword list in conf.properties + WordList §ion = *keywordLists[0]; + WordList &keyword = *keywordLists[1]; + WordList &modifier = *keywordLists[2]; + + // go through all provided text segment + // using the hand-written state machine shown below + styler.StartAt(startPos); + styler.StartSegment(startPos); + for (int i = startPos; i < lengthDoc; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + + if (styler.IsLeadByte(ch)) { + chNext = styler.SafeGetCharAt(i + 2); + i++; + continue; + } + switch(state) { + case SCE_NNCRONTAB_DEFAULT: + if( ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ') { + // whitespace is simply ignored here... + styler.ColourTo(i,SCE_NNCRONTAB_DEFAULT); + break; + } else if( ch == '#' && styler.SafeGetCharAt(i+1) == '(') { + // signals the start of a task... + state = SCE_NNCRONTAB_TASK; + styler.ColourTo(i,SCE_NNCRONTAB_TASK); + } + else if( ch == '\\' && (styler.SafeGetCharAt(i+1) == ' ' || + styler.SafeGetCharAt(i+1) == '\t')) { + // signals the start of an extended comment... + state = SCE_NNCRONTAB_COMMENT; + styler.ColourTo(i,SCE_NNCRONTAB_COMMENT); + } else if( ch == '#' ) { + // signals the start of a plain comment... + state = SCE_NNCRONTAB_COMMENT; + styler.ColourTo(i,SCE_NNCRONTAB_COMMENT); + } else if( ch == ')' && styler.SafeGetCharAt(i+1) == '#') { + // signals the end of a task... + state = SCE_NNCRONTAB_TASK; + styler.ColourTo(i,SCE_NNCRONTAB_TASK); + } else if( ch == '"') { + state = SCE_NNCRONTAB_STRING; + styler.ColourTo(i,SCE_NNCRONTAB_STRING); + } else if( ch == '%') { + // signals environment variables + state = SCE_NNCRONTAB_ENVIRONMENT; + styler.ColourTo(i,SCE_NNCRONTAB_ENVIRONMENT); + } else if( ch == '<' && styler.SafeGetCharAt(i+1) == '%') { + // signals environment variables + state = SCE_NNCRONTAB_ENVIRONMENT; + styler.ColourTo(i,SCE_NNCRONTAB_ENVIRONMENT); + } else if( ch == '*' ) { + // signals an asterisk + // no state jump necessary for this simple case... + styler.ColourTo(i,SCE_NNCRONTAB_ASTERISK); + } else if( isalpha(ch) || ch == '<' ) { + // signals the start of an identifier + bufferCount = 0; + buffer[bufferCount++] = ch; + state = SCE_NNCRONTAB_IDENTIFIER; + } else if( isdigit(ch) ) { + // signals the start of a number + bufferCount = 0; + buffer[bufferCount++] = ch; + state = SCE_NNCRONTAB_NUMBER; + } else { + // style it the default style.. + styler.ColourTo(i,SCE_NNCRONTAB_DEFAULT); + } + break; + + case SCE_NNCRONTAB_COMMENT: + // if we find a newline here, + // we simply go to default state + // else continue to work on it... + if( ch == '\n' || ch == '\r' ) { + state = SCE_NNCRONTAB_DEFAULT; + } else { + styler.ColourTo(i,SCE_NNCRONTAB_COMMENT); + } + break; + + case SCE_NNCRONTAB_TASK: + // if we find a newline here, + // we simply go to default state + // else continue to work on it... + if( ch == '\n' || ch == '\r' ) { + state = SCE_NNCRONTAB_DEFAULT; + } else { + styler.ColourTo(i,SCE_NNCRONTAB_TASK); + } + break; + + case SCE_NNCRONTAB_STRING: + if( ch == '%' ) { + state = SCE_NNCRONTAB_ENVIRONMENT; + insideString = true; + styler.ColourTo(i-1,SCE_NNCRONTAB_STRING); + break; + } + // if we find the end of a string char, we simply go to default state + // else we're still dealing with an string... + if( (ch == '"' && styler.SafeGetCharAt(i-1)!='\\') || + (ch == '\n') || (ch == '\r') ) { + state = SCE_NNCRONTAB_DEFAULT; + } + styler.ColourTo(i,SCE_NNCRONTAB_STRING); + break; + + case SCE_NNCRONTAB_ENVIRONMENT: + // if we find the end of a string char, we simply go to default state + // else we're still dealing with an string... + if( ch == '%' && insideString ) { + state = SCE_NNCRONTAB_STRING; + insideString = false; + break; + } + if( (ch == '%' && styler.SafeGetCharAt(i-1)!='\\') + || (ch == '\n') || (ch == '\r') || (ch == '>') ) { + state = SCE_NNCRONTAB_DEFAULT; + styler.ColourTo(i,SCE_NNCRONTAB_ENVIRONMENT); + break; + } + styler.ColourTo(i+1,SCE_NNCRONTAB_ENVIRONMENT); + break; + + case SCE_NNCRONTAB_IDENTIFIER: + // stay in CONF_IDENTIFIER state until we find a non-alphanumeric + if( isalnum(ch) || (ch == '_') || (ch == '-') || (ch == '/') || + (ch == '$') || (ch == '.') || (ch == '<') || (ch == '>') || + (ch == '@') ) { + buffer[bufferCount++] = ch; + } else { + state = SCE_NNCRONTAB_DEFAULT; + buffer[bufferCount] = '\0'; + + // check if the buffer contains a keyword, + // and highlight it if it is a keyword... + if(section.InList(buffer)) { + styler.ColourTo(i,SCE_NNCRONTAB_SECTION ); + } else if(keyword.InList(buffer)) { + styler.ColourTo(i-1,SCE_NNCRONTAB_KEYWORD ); + } // else if(strchr(buffer,'/') || strchr(buffer,'.')) { + // styler.ColourTo(i-1,SCE_NNCRONTAB_EXTENSION); + // } + else if(modifier.InList(buffer)) { + styler.ColourTo(i-1,SCE_NNCRONTAB_MODIFIER ); + } else { + styler.ColourTo(i-1,SCE_NNCRONTAB_DEFAULT); + } + // push back the faulty character + chNext = styler[i--]; + } + break; + + case SCE_NNCRONTAB_NUMBER: + // stay in CONF_NUMBER state until we find a non-numeric + if( isdigit(ch) /* || ch == '.' */ ) { + buffer[bufferCount++] = ch; + } else { + state = SCE_NNCRONTAB_DEFAULT; + buffer[bufferCount] = '\0'; + // Colourize here... (normal number) + styler.ColourTo(i-1,SCE_NNCRONTAB_NUMBER); + // push back a character + chNext = styler[i--]; + } + break; + } + } + delete []buffer; +} + +static const char * const cronWordListDesc[] = { + "Section keywords and Forth words", + "nnCrontab keywords", + "Modifiers", + 0 +}; + +LexerModule lmNncrontab(SCLEX_NNCRONTAB, ColouriseNncrontabDoc, "nncrontab", 0, cronWordListDesc); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCsound.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCsound.cxx new file mode 100644 index 00000000..67656988 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexCsound.cxx @@ -0,0 +1,207 @@ +// Scintilla source code edit control +/** @file LexCsound.cxx + ** Lexer for Csound (Orchestra & Score) + ** Written by Georg Ritter - + **/ +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + + +static inline bool IsAWordChar(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '.' || + ch == '_' || ch == '?'); +} + +static inline bool IsAWordStart(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '.' || + ch == '%' || ch == '@' || ch == '$' || ch == '?'); +} + +static inline bool IsCsoundOperator(char ch) { + if (isalnum(ch)) + return false; + // '.' left out as it is used to make up numbers + if (ch == '*' || ch == '/' || ch == '-' || ch == '+' || + ch == '(' || ch == ')' || ch == '=' || ch == '^' || + ch == '[' || ch == ']' || ch == '<' || ch == '&' || + ch == '>' || ch == ',' || ch == '|' || ch == '~' || + ch == '%' || ch == ':') + return true; + return false; +} + +static void ColouriseCsoundDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) { + + WordList &opcode = *keywordlists[0]; + WordList &headerStmt = *keywordlists[1]; + WordList &otherKeyword = *keywordlists[2]; + + // Do not leak onto next line + if (initStyle == SCE_CSOUND_STRINGEOL) + initStyle = SCE_CSOUND_DEFAULT; + + StyleContext sc(startPos, length, initStyle, styler); + + for (; sc.More(); sc.Forward()) + { + // Handle line continuation generically. + if (sc.ch == '\\') { + if (sc.chNext == '\n' || sc.chNext == '\r') { + sc.Forward(); + if (sc.ch == '\r' && sc.chNext == '\n') { + sc.Forward(); + } + continue; + } + } + + // Determine if the current state should terminate. + if (sc.state == SCE_CSOUND_OPERATOR) { + if (!IsCsoundOperator(static_cast(sc.ch))) { + sc.SetState(SCE_CSOUND_DEFAULT); + } + }else if (sc.state == SCE_CSOUND_NUMBER) { + if (!IsAWordChar(sc.ch)) { + sc.SetState(SCE_CSOUND_DEFAULT); + } + } else if (sc.state == SCE_CSOUND_IDENTIFIER) { + if (!IsAWordChar(sc.ch) ) { + char s[100]; + sc.GetCurrent(s, sizeof(s)); + + if (opcode.InList(s)) { + sc.ChangeState(SCE_CSOUND_OPCODE); + } else if (headerStmt.InList(s)) { + sc.ChangeState(SCE_CSOUND_HEADERSTMT); + } else if (otherKeyword.InList(s)) { + sc.ChangeState(SCE_CSOUND_USERKEYWORD); + } else if (s[0] == 'p') { + sc.ChangeState(SCE_CSOUND_PARAM); + } else if (s[0] == 'a') { + sc.ChangeState(SCE_CSOUND_ARATE_VAR); + } else if (s[0] == 'k') { + sc.ChangeState(SCE_CSOUND_KRATE_VAR); + } else if (s[0] == 'i') { // covers both i-rate variables and i-statements + sc.ChangeState(SCE_CSOUND_IRATE_VAR); + } else if (s[0] == 'g') { + sc.ChangeState(SCE_CSOUND_GLOBAL_VAR); + } + sc.SetState(SCE_CSOUND_DEFAULT); + } + } + else if (sc.state == SCE_CSOUND_COMMENT ) { + if (sc.atLineEnd) { + sc.SetState(SCE_CSOUND_DEFAULT); + } + } + else if ((sc.state == SCE_CSOUND_ARATE_VAR) || + (sc.state == SCE_CSOUND_KRATE_VAR) || + (sc.state == SCE_CSOUND_IRATE_VAR)) { + if (!IsAWordChar(sc.ch)) { + sc.SetState(SCE_CSOUND_DEFAULT); + } + } + + // Determine if a new state should be entered. + if (sc.state == SCE_CSOUND_DEFAULT) { + if (sc.ch == ';'){ + sc.SetState(SCE_CSOUND_COMMENT); + } else if (isdigit(sc.ch) || (sc.ch == '.' && isdigit(sc.chNext))) { + sc.SetState(SCE_CSOUND_NUMBER); + } else if (IsAWordStart(sc.ch)) { + sc.SetState(SCE_CSOUND_IDENTIFIER); + } else if (IsCsoundOperator(static_cast(sc.ch))) { + sc.SetState(SCE_CSOUND_OPERATOR); + } else if (sc.ch == 'p') { + sc.SetState(SCE_CSOUND_PARAM); + } else if (sc.ch == 'a') { + sc.SetState(SCE_CSOUND_ARATE_VAR); + } else if (sc.ch == 'k') { + sc.SetState(SCE_CSOUND_KRATE_VAR); + } else if (sc.ch == 'i') { // covers both i-rate variables and i-statements + sc.SetState(SCE_CSOUND_IRATE_VAR); + } else if (sc.ch == 'g') { + sc.SetState(SCE_CSOUND_GLOBAL_VAR); + } + } + } + sc.Complete(); +} + +static void FoldCsoundInstruments(unsigned int startPos, int length, int /* initStyle */, WordList *[], + Accessor &styler) { + unsigned int lengthDoc = startPos + length; + int visibleChars = 0; + int lineCurrent = styler.GetLine(startPos); + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + char chNext = styler[startPos]; + int stylePrev = 0; + int styleNext = styler.StyleAt(startPos); + for (unsigned int i = startPos; i < lengthDoc; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + int style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + if ((stylePrev != SCE_CSOUND_OPCODE) && (style == SCE_CSOUND_OPCODE)) { + char s[20]; + unsigned int j = 0; + while ((j < (sizeof(s) - 1)) && (iswordchar(styler[i + j]))) { + s[j] = styler[i + j]; + j++; + } + s[j] = '\0'; + + if (strcmp(s, "instr") == 0) + levelCurrent++; + if (strcmp(s, "endin") == 0) + levelCurrent--; + } + + if (atEOL) { + int lev = levelPrev; + if (visibleChars == 0) + lev |= SC_FOLDLEVELWHITEFLAG; + if ((levelCurrent > levelPrev) && (visibleChars > 0)) + lev |= SC_FOLDLEVELHEADERFLAG; + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + levelPrev = levelCurrent; + visibleChars = 0; + } + if (!isspacechar(ch)) + visibleChars++; + stylePrev = style; + } + // Fill in the real level of the next line, keeping the current flags as they will be filled in later + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + styler.SetLevel(lineCurrent, levelPrev | flagsNext); +} + + +static const char * const csoundWordListDesc[] = { + "Opcodes", + "Header Statements", + "User keywords", + 0 +}; + +LexerModule lmCsound(SCLEX_CSOUND, ColouriseCsoundDoc, "csound", FoldCsoundInstruments, csoundWordListDesc); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexEScript.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexEScript.cxx new file mode 100644 index 00000000..9de3a3ea --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexEScript.cxx @@ -0,0 +1,270 @@ +// Scintilla source code edit control +/** @file LexESCRIPT.cxx + ** Lexer for ESCRIPT + **/ +// Copyright 2003 by Patrizio Bekerle (patrizio@bekerle.com) + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + + + +static inline bool IsAWordChar(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_'); +} + +static inline bool IsAWordStart(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '_'); +} + + + +static void ColouriseESCRIPTDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) { + + WordList &keywords = *keywordlists[0]; + WordList &keywords2 = *keywordlists[1]; + WordList &keywords3 = *keywordlists[2]; + + // Do not leak onto next line + /*if (initStyle == SCE_ESCRIPT_STRINGEOL) + initStyle = SCE_ESCRIPT_DEFAULT;*/ + + StyleContext sc(startPos, length, initStyle, styler); + + bool caseSensitive = styler.GetPropertyInt("escript.case.sensitive", 0) != 0; + + for (; sc.More(); sc.Forward()) { + + /*if (sc.atLineStart && (sc.state == SCE_ESCRIPT_STRING)) { + // Prevent SCE_ESCRIPT_STRINGEOL from leaking back to previous line + sc.SetState(SCE_ESCRIPT_STRING); + }*/ + + // Handle line continuation generically. + if (sc.ch == '\\') { + if (sc.chNext == '\n' || sc.chNext == '\r') { + sc.Forward(); + if (sc.ch == '\r' && sc.chNext == '\n') { + sc.Forward(); + } + continue; + } + } + + // Determine if the current state should terminate. + if (sc.state == SCE_ESCRIPT_OPERATOR || sc.state == SCE_ESCRIPT_BRACE) { + sc.SetState(SCE_ESCRIPT_DEFAULT); + } else if (sc.state == SCE_ESCRIPT_NUMBER) { + if (!IsADigit(sc.ch) || sc.ch != '.') { + sc.SetState(SCE_ESCRIPT_DEFAULT); + } + } else if (sc.state == SCE_ESCRIPT_IDENTIFIER) { + if (!IsAWordChar(sc.ch) || (sc.ch == '.')) { + char s[100]; + if (caseSensitive) { + sc.GetCurrent(s, sizeof(s)); + } else { + sc.GetCurrentLowered(s, sizeof(s)); + } + +// sc.GetCurrentLowered(s, sizeof(s)); + + if (keywords.InList(s)) { + sc.ChangeState(SCE_ESCRIPT_WORD); + } else if (keywords2.InList(s)) { + sc.ChangeState(SCE_ESCRIPT_WORD2); + } else if (keywords3.InList(s)) { + sc.ChangeState(SCE_ESCRIPT_WORD3); + // sc.state = SCE_ESCRIPT_IDENTIFIER; + } + sc.SetState(SCE_ESCRIPT_DEFAULT); + } + } else if (sc.state == SCE_ESCRIPT_COMMENT) { + if (sc.Match('*', '/')) { + sc.Forward(); + sc.ForwardSetState(SCE_ESCRIPT_DEFAULT); + } + } else if (sc.state == SCE_ESCRIPT_COMMENTDOC) { + if (sc.Match('*', '/')) { + sc.Forward(); + sc.ForwardSetState(SCE_ESCRIPT_DEFAULT); + } + } else if (sc.state == SCE_ESCRIPT_COMMENTLINE) { + if (sc.atLineEnd) { + sc.SetState(SCE_ESCRIPT_DEFAULT); + } + } else if (sc.state == SCE_ESCRIPT_STRING) { + if (sc.ch == '\\') { + if (sc.chNext == '\"' || sc.chNext == '\\') { + sc.Forward(); + } + } else if (sc.ch == '\"') { + sc.ForwardSetState(SCE_ESCRIPT_DEFAULT); + } + } + + // Determine if a new state should be entered. + if (sc.state == SCE_ESCRIPT_DEFAULT) { + if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { + sc.SetState(SCE_ESCRIPT_NUMBER); + } else if (IsAWordStart(sc.ch) || (sc.ch == '#')) { + sc.SetState(SCE_ESCRIPT_IDENTIFIER); + } else if (sc.Match('/', '*')) { + sc.SetState(SCE_ESCRIPT_COMMENT); + sc.Forward(); // Eat the * so it isn't used for the end of the comment + } else if (sc.Match('/', '/')) { + sc.SetState(SCE_ESCRIPT_COMMENTLINE); + } else if (sc.ch == '\"') { + sc.SetState(SCE_ESCRIPT_STRING); + //} else if (isoperator(static_cast(sc.ch))) { + } else if (sc.ch == '+' || sc.ch == '-' || sc.ch == '*' || sc.ch == '/' || sc.ch == '=' || sc.ch == '<' || sc.ch == '>' || sc.ch == '&' || sc.ch == '|' || sc.ch == '!' || sc.ch == '?' || sc.ch == ':') { + sc.SetState(SCE_ESCRIPT_OPERATOR); + } else if (sc.ch == '{' || sc.ch == '}') { + sc.SetState(SCE_ESCRIPT_BRACE); + } + } + + } + sc.Complete(); +} + + +static int classifyFoldPointESCRIPT(const char* s, const char* prevWord) { + int lev = 0; + if (strcmp(prevWord, "end") == 0) return lev; + if ((strcmp(prevWord, "else") == 0 && strcmp(s, "if") == 0) || strcmp(s, "elseif") == 0) + return -1; + + if (strcmp(s, "for") == 0 || strcmp(s, "foreach") == 0 + || strcmp(s, "program") == 0 || strcmp(s, "function") == 0 + || strcmp(s, "while") == 0 || strcmp(s, "case") == 0 + || strcmp(s, "if") == 0 ) { + lev = 1; + } else if ( strcmp(s, "endfor") == 0 || strcmp(s, "endforeach") == 0 + || strcmp(s, "endprogram") == 0 || strcmp(s, "endfunction") == 0 + || strcmp(s, "endwhile") == 0 || strcmp(s, "endcase") == 0 + || strcmp(s, "endif") == 0 ) { + lev = -1; + } + + return lev; +} + + +static bool IsStreamCommentStyle(int style) { + return style == SCE_ESCRIPT_COMMENT || + style == SCE_ESCRIPT_COMMENTDOC || + style == SCE_ESCRIPT_COMMENTLINE; +} + +static void FoldESCRIPTDoc(unsigned int startPos, int length, int initStyle, WordList *[], Accessor &styler) { + //~ bool foldComment = styler.GetPropertyInt("fold.comment") != 0; + // Do not know how to fold the comment at the moment. + bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + bool foldComment = true; + unsigned int endPos = startPos + length; + int visibleChars = 0; + int lineCurrent = styler.GetLine(startPos); + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + + int lastStart = 0; + char prevWord[32] = ""; + + for (unsigned int i = startPos; i < endPos; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + int stylePrev = style; + style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + + + if (foldComment && IsStreamCommentStyle(style)) { + if (!IsStreamCommentStyle(stylePrev)) { + levelCurrent++; + } else if (!IsStreamCommentStyle(styleNext) && !atEOL) { + // Comments don't end at end of line and the next character may be unstyled. + levelCurrent--; + } + } + + if (foldComment && (style == SCE_ESCRIPT_COMMENTLINE)) { + if ((ch == '/') && (chNext == '/')) { + char chNext2 = styler.SafeGetCharAt(i + 2); + if (chNext2 == '{') { + levelCurrent++; + } else if (chNext2 == '}') { + levelCurrent--; + } + } + } + + if (stylePrev == SCE_ESCRIPT_DEFAULT && style == SCE_ESCRIPT_WORD3) + { + // Store last word start point. + lastStart = i; + } + + if (style == SCE_ESCRIPT_WORD3) { + if(iswordchar(ch) && !iswordchar(chNext)) { + char s[32]; + unsigned int j; + for(j = 0; ( j < 31 ) && ( j < i-lastStart+1 ); j++) { + s[j] = static_cast(tolower(styler[lastStart + j])); + } + s[j] = '\0'; + levelCurrent += classifyFoldPointESCRIPT(s, prevWord); + strcpy(prevWord, s); + } + } + if (atEOL) { + int lev = levelPrev; + if (visibleChars == 0 && foldCompact) + lev |= SC_FOLDLEVELWHITEFLAG; + if ((levelCurrent > levelPrev) && (visibleChars > 0)) + lev |= SC_FOLDLEVELHEADERFLAG; + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + levelPrev = levelCurrent; + visibleChars = 0; + strcpy(prevWord, ""); + } + + if (!isspacechar(ch)) + visibleChars++; + } + + // Fill in the real level of the next line, keeping the current flags as they will be filled in later + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + styler.SetLevel(lineCurrent, levelPrev | flagsNext); +} + + + +static const char * const ESCRIPTWordLists[] = { + "Primary keywords and identifiers", + "Intrinsic functions", + "Extended and user defined functions", + 0, +}; + +LexerModule lmESCRIPT(SCLEX_ESCRIPT, ColouriseESCRIPTDoc, "escript", FoldESCRIPTDoc, ESCRIPTWordLists); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexEiffel.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexEiffel.cxx new file mode 100644 index 00000000..6bc8b16a --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexEiffel.cxx @@ -0,0 +1,234 @@ +// Scintilla source code edit control +/** @file LexEiffel.cxx + ** Lexer for Eiffel. + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +static inline bool isEiffelOperator(unsigned int ch) { + // '.' left out as it is used to make up numbers + return ch == '*' || ch == '/' || ch == '\\' || ch == '-' || ch == '+' || + ch == '(' || ch == ')' || ch == '=' || + ch == '{' || ch == '}' || ch == '~' || + ch == '[' || ch == ']' || ch == ';' || + ch == '<' || ch == '>' || ch == ',' || + ch == '.' || ch == '^' || ch == '%' || ch == ':' || + ch == '!' || ch == '@' || ch == '?'; +} + +static inline bool IsAWordChar(unsigned int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '_'); +} + +static inline bool IsAWordStart(unsigned int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '_'); +} + +static void ColouriseEiffelDoc(unsigned int startPos, + int length, + int initStyle, + WordList *keywordlists[], + Accessor &styler) { + + WordList &keywords = *keywordlists[0]; + + StyleContext sc(startPos, length, initStyle, styler); + + for (; sc.More(); sc.Forward()) { + + if (sc.state == SCE_EIFFEL_STRINGEOL) { + if (sc.ch != '\r' && sc.ch != '\n') { + sc.SetState(SCE_EIFFEL_DEFAULT); + } + } else if (sc.state == SCE_EIFFEL_OPERATOR) { + sc.SetState(SCE_EIFFEL_DEFAULT); + } else if (sc.state == SCE_EIFFEL_WORD) { + if (!IsAWordChar(sc.ch)) { + char s[100]; + sc.GetCurrentLowered(s, sizeof(s)); + if (!keywords.InList(s)) { + sc.ChangeState(SCE_EIFFEL_IDENTIFIER); + } + sc.SetState(SCE_EIFFEL_DEFAULT); + } + } else if (sc.state == SCE_EIFFEL_NUMBER) { + if (!IsAWordChar(sc.ch)) { + sc.SetState(SCE_EIFFEL_DEFAULT); + } + } else if (sc.state == SCE_EIFFEL_COMMENTLINE) { + if (sc.ch == '\r' || sc.ch == '\n') { + sc.SetState(SCE_EIFFEL_DEFAULT); + } + } else if (sc.state == SCE_EIFFEL_STRING) { + if (sc.ch == '%') { + sc.Forward(); + } else if (sc.ch == '\"') { + sc.Forward(); + sc.SetState(SCE_EIFFEL_DEFAULT); + } + } else if (sc.state == SCE_EIFFEL_CHARACTER) { + if (sc.ch == '\r' || sc.ch == '\n') { + sc.SetState(SCE_EIFFEL_STRINGEOL); + } else if (sc.ch == '%') { + sc.Forward(); + } else if (sc.ch == '\'') { + sc.Forward(); + sc.SetState(SCE_EIFFEL_DEFAULT); + } + } + + if (sc.state == SCE_EIFFEL_DEFAULT) { + if (sc.ch == '-' && sc.chNext == '-') { + sc.SetState(SCE_EIFFEL_COMMENTLINE); + } else if (sc.ch == '\"') { + sc.SetState(SCE_EIFFEL_STRING); + } else if (sc.ch == '\'') { + sc.SetState(SCE_EIFFEL_CHARACTER); + } else if (IsADigit(sc.ch) || (sc.ch == '.')) { + sc.SetState(SCE_EIFFEL_NUMBER); + } else if (IsAWordStart(sc.ch)) { + sc.SetState(SCE_EIFFEL_WORD); + } else if (isEiffelOperator(sc.ch)) { + sc.SetState(SCE_EIFFEL_OPERATOR); + } + } + } + sc.Complete(); +} + +static bool IsEiffelComment(Accessor &styler, int pos, int len) { + return len>1 && styler[pos]=='-' && styler[pos+1]=='-'; +} + +static void FoldEiffelDocIndent(unsigned int startPos, int length, int, + WordList *[], Accessor &styler) { + int lengthDoc = startPos + length; + + // Backtrack to previous line in case need to fix its fold status + int lineCurrent = styler.GetLine(startPos); + if (startPos > 0) { + if (lineCurrent > 0) { + lineCurrent--; + startPos = styler.LineStart(lineCurrent); + } + } + int spaceFlags = 0; + int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsEiffelComment); + char chNext = styler[startPos]; + for (int i = startPos; i < lengthDoc; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + + if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc)) { + int lev = indentCurrent; + int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsEiffelComment); + if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { + // Only non whitespace lines can be headers + if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) { + lev |= SC_FOLDLEVELHEADERFLAG; + } else if (indentNext & SC_FOLDLEVELWHITEFLAG) { + // Line after is blank so check the next - maybe should continue further? + int spaceFlags2 = 0; + int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsEiffelComment); + if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) { + lev |= SC_FOLDLEVELHEADERFLAG; + } + } + } + indentCurrent = indentNext; + styler.SetLevel(lineCurrent, lev); + lineCurrent++; + } + } +} + +static void FoldEiffelDocKeyWords(unsigned int startPos, int length, int /* initStyle */, WordList *[], + Accessor &styler) { + unsigned int lengthDoc = startPos + length; + int visibleChars = 0; + int lineCurrent = styler.GetLine(startPos); + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + char chNext = styler[startPos]; + int stylePrev = 0; + int styleNext = styler.StyleAt(startPos); + // lastDeferred should be determined by looking back to last keyword in case + // the "deferred" is on a line before "class" + bool lastDeferred = false; + for (unsigned int i = startPos; i < lengthDoc; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + int style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + if ((stylePrev != SCE_EIFFEL_WORD) && (style == SCE_EIFFEL_WORD)) { + char s[20]; + unsigned int j = 0; + while ((j < (sizeof(s) - 1)) && (iswordchar(styler[i + j]))) { + s[j] = styler[i + j]; + j++; + } + s[j] = '\0'; + + if ( + (strcmp(s, "check") == 0) || + (strcmp(s, "debug") == 0) || + (strcmp(s, "deferred") == 0) || + (strcmp(s, "do") == 0) || + (strcmp(s, "from") == 0) || + (strcmp(s, "if") == 0) || + (strcmp(s, "inspect") == 0) || + (strcmp(s, "once") == 0) + ) + levelCurrent++; + if (!lastDeferred && (strcmp(s, "class") == 0)) + levelCurrent++; + if (strcmp(s, "end") == 0) + levelCurrent--; + lastDeferred = strcmp(s, "deferred") == 0; + } + + if (atEOL) { + int lev = levelPrev; + if (visibleChars == 0) + lev |= SC_FOLDLEVELWHITEFLAG; + if ((levelCurrent > levelPrev) && (visibleChars > 0)) + lev |= SC_FOLDLEVELHEADERFLAG; + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + levelPrev = levelCurrent; + visibleChars = 0; + } + if (!isspacechar(ch)) + visibleChars++; + stylePrev = style; + } + // Fill in the real level of the next line, keeping the current flags as they will be filled in later + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + styler.SetLevel(lineCurrent, levelPrev | flagsNext); +} + +static const char * const eiffelWordListDesc[] = { + "Keywords", + 0 +}; + +LexerModule lmEiffel(SCLEX_EIFFEL, ColouriseEiffelDoc, "eiffel", FoldEiffelDocIndent, eiffelWordListDesc); +LexerModule lmEiffelkw(SCLEX_EIFFELKW, ColouriseEiffelDoc, "eiffelkw", FoldEiffelDocKeyWords, eiffelWordListDesc); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexErlang.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexErlang.cxx new file mode 100644 index 00000000..b75a766e --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexErlang.cxx @@ -0,0 +1,522 @@ +// Scintilla source code edit control +/** @file LexErlang.cxx + ** Lexer for Erlang. + ** Written by Peter-Henry Mander, based on Matlab lexer by José Fonseca + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +/* + TODO: + o _Param should be a new lexical type +*/ + +static int is_radix(int radix, int ch) { + int digit; + if ( 16 < radix || 2 > radix ) { + return 0; + } + if ( isdigit(ch) ) { + digit = ch - '0'; + } else if ( isxdigit(ch) ) { + digit = toupper(ch) - 'A' + 10; + } else { + return 0; + } + if ( digit < radix ) { + return 1; + } else { + return 0; + } +} + +typedef enum { + STATE_NULL, + ATOM_UNQUOTED, + ATOM_QUOTED, + ATOM_FUN_NAME, + NODE_NAME_UNQUOTED, + NODE_NAME_QUOTED, + MACRO_START, + MACRO_UNQUOTED, + MACRO_QUOTED, + RECORD_START, + RECORD_UNQUOTED, + RECORD_QUOTED, + NUMERAL_START, + NUMERAL_SIGNED, + NUMERAL_RADIX_LITERAL, + NUMERAL_SPECULATIVE_MANTISSA, + NUMERAL_FLOAT_MANTISSA, + NUMERAL_FLOAT_EXPONENT, + NUMERAL_FLOAT_SIGNED_EXPONENT, + PARSE_ERROR +} atom_parse_state_t; + +static void ColouriseErlangDoc(unsigned int startPos, int length, int initStyle, + WordList *keywordlists[], Accessor &styler) { + + WordList &keywords = *keywordlists[0]; + + styler.StartAt(startPos); + + StyleContext sc(startPos, length, initStyle, styler); + atom_parse_state_t parse_state = STATE_NULL; + int radix_digits = 0; + int exponent_digits = 0; + for (; sc.More(); sc.Forward()) { + if ( STATE_NULL != parse_state ) { + switch (parse_state) { + case STATE_NULL: + sc.SetState(SCE_ERLANG_DEFAULT); + break; + case ATOM_UNQUOTED: + if ( '@' == sc.ch ){ + parse_state = NODE_NAME_UNQUOTED; + } else if ( !isalnum(sc.ch) && sc.ch != '_' ) { + char s[100]; + sc.GetCurrent(s, sizeof(s)); + if (keywords.InList(s)) { + sc.ChangeState(SCE_ERLANG_KEYWORD); + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } else { + if ( '/' == sc.ch ) { + parse_state = ATOM_FUN_NAME; + } else { + sc.ChangeState(SCE_ERLANG_ATOM); + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + } + } + break; + case ATOM_QUOTED: + if ( '@' == sc.ch ){ + parse_state = NODE_NAME_QUOTED; + } else if ( '\'' == sc.ch && '\\' != sc.chPrev ) { + sc.ChangeState(SCE_ERLANG_ATOM); + sc.ForwardSetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case ATOM_FUN_NAME: + if ( !isdigit(sc.ch) ) { + sc.ChangeState(SCE_ERLANG_FUNCTION_NAME); + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case NODE_NAME_QUOTED: + if ( '@' == sc.ch ) { + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } else if ( '\'' == sc.ch && '\\' != sc.chPrev ) { + sc.ChangeState(SCE_ERLANG_NODE_NAME); + sc.ForwardSetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case NODE_NAME_UNQUOTED: + if ( '@' == sc.ch ) { + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } else if ( !isalnum(sc.ch) && sc.ch != '_' ) { + sc.ChangeState(SCE_ERLANG_NODE_NAME); + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case RECORD_START: + if ( '\'' == sc.ch ) { + parse_state = RECORD_QUOTED; + } else if (isalpha(sc.ch) && islower(sc.ch)) { + parse_state = RECORD_UNQUOTED; + } else { // error + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case RECORD_QUOTED: + if ( '\'' == sc.ch && '\\' != sc.chPrev ) { + sc.ChangeState(SCE_ERLANG_RECORD); + sc.ForwardSetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case RECORD_UNQUOTED: + if ( !isalpha(sc.ch) && '_' != sc.ch ) { + sc.ChangeState(SCE_ERLANG_RECORD); + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case MACRO_START: + if ( '\'' == sc.ch ) { + parse_state = MACRO_QUOTED; + } else if (isalpha(sc.ch)) { + parse_state = MACRO_UNQUOTED; + } else { // error + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case MACRO_UNQUOTED: + if ( !isalpha(sc.ch) && '_' != sc.ch ) { + sc.ChangeState(SCE_ERLANG_MACRO); + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case MACRO_QUOTED: + if ( '\'' == sc.ch && '\\' != sc.chPrev ) { + sc.ChangeState(SCE_ERLANG_MACRO); + sc.ForwardSetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case NUMERAL_START: + if ( isdigit(sc.ch) ) { + radix_digits *= 10; + radix_digits += sc.ch - '0'; // Assuming ASCII here! + } else if ( '#' == sc.ch ) { + if ( 2 > radix_digits || 16 < radix_digits) { + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } else { + parse_state = NUMERAL_RADIX_LITERAL; + } + } else if ( '.' == sc.ch && isdigit(sc.chNext)) { + radix_digits = 0; + parse_state = NUMERAL_FLOAT_MANTISSA; + } else if ( 'e' == sc.ch || 'E' == sc.ch ) { + exponent_digits = 0; + parse_state = NUMERAL_FLOAT_EXPONENT; + } else { + radix_digits = 0; + sc.ChangeState(SCE_ERLANG_NUMBER); + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case NUMERAL_RADIX_LITERAL: + if ( !is_radix(radix_digits,sc.ch) ) { + radix_digits = 0; + if ( !isalnum(sc.ch) ) { + sc.ChangeState(SCE_ERLANG_NUMBER); + } + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case NUMERAL_FLOAT_MANTISSA: + if ( 'e' == sc.ch || 'E' == sc.ch ) { + exponent_digits = 0; + parse_state = NUMERAL_FLOAT_EXPONENT; + } else if ( !isdigit(sc.ch) ) { + sc.ChangeState(SCE_ERLANG_NUMBER); + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } + break; + case NUMERAL_FLOAT_EXPONENT: + if ( '-' == sc.ch || '+' == sc.ch ) { + parse_state = NUMERAL_FLOAT_SIGNED_EXPONENT; + } else if ( !isdigit(sc.ch) ) { + if ( 0 < exponent_digits ) { + sc.ChangeState(SCE_ERLANG_NUMBER); + } + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } else { + ++exponent_digits; + } + break; + case NUMERAL_FLOAT_SIGNED_EXPONENT: + if ( !isdigit(sc.ch) ) { + if ( 0 < exponent_digits ) { + sc.ChangeState(SCE_ERLANG_NUMBER); + } + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } else { + ++exponent_digits; + } + break; + case NUMERAL_SIGNED: + if ( !isdigit(sc.ch) ) { + sc.ChangeState(SCE_ERLANG_NUMBER); + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } else if ( '.' == sc.ch ) { + parse_state = NUMERAL_FLOAT_MANTISSA; + } + break; + case NUMERAL_SPECULATIVE_MANTISSA: + if ( !isdigit(sc.ch) ) { + sc.ChangeState(SCE_ERLANG_OPERATOR); + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + } else { + parse_state = NUMERAL_FLOAT_MANTISSA; + } + break; + case PARSE_ERROR: + sc.SetState(SCE_ERLANG_DEFAULT); + parse_state = STATE_NULL; + break; + } + } else if (sc.state == SCE_ERLANG_OPERATOR) { + if (sc.chPrev == '.') { + if (sc.ch == '*' || sc.ch == '/' || sc.ch == '\\' || sc.ch == '^') { + sc.ForwardSetState(SCE_ERLANG_DEFAULT); + } else if (sc.ch == '\'') { + sc.ForwardSetState(SCE_ERLANG_DEFAULT); + } else { + sc.SetState(SCE_ERLANG_DEFAULT); + } + } else { + sc.SetState(SCE_ERLANG_DEFAULT); + } + } else if (sc.state == SCE_ERLANG_VARIABLE) { + if (!isalnum(sc.ch) && sc.ch != '_') { + sc.SetState(SCE_ERLANG_DEFAULT); + } + } else if (sc.state == SCE_ERLANG_STRING) { + if (sc.ch == '\"' && sc.chPrev != '\\') { + sc.ForwardSetState(SCE_ERLANG_DEFAULT); + } + } else if (sc.state == SCE_ERLANG_COMMENT ) { + if (sc.atLineEnd) { + sc.SetState(SCE_ERLANG_DEFAULT); + } + } else if (sc.state == SCE_ERLANG_CHARACTER ) { + if ( sc.chPrev == '\\' ) { + sc.ForwardSetState(SCE_ERLANG_DEFAULT); + } else if ( sc.ch != '\\' ) { + sc.ForwardSetState(SCE_ERLANG_DEFAULT); + } + } + + if (sc.state == SCE_ERLANG_DEFAULT) { + if (sc.ch == '%') { + sc.SetState(SCE_ERLANG_COMMENT); + } else if (sc.ch == '\"') { + sc.SetState(SCE_ERLANG_STRING); + } else if (sc.ch == '#') { + parse_state = RECORD_START; + sc.SetState(SCE_ERLANG_UNKNOWN); + } else if (sc.ch == '?') { + parse_state = MACRO_START; + sc.SetState(SCE_ERLANG_UNKNOWN); + } else if (sc.ch == '$') { + sc.SetState(SCE_ERLANG_CHARACTER); + } else if (sc.ch == '\'') { + parse_state = ATOM_QUOTED; + sc.SetState(SCE_ERLANG_UNKNOWN); + } else if ( isdigit(sc.ch) ) { + parse_state = NUMERAL_START; + radix_digits = sc.ch - '0'; + sc.SetState(SCE_ERLANG_UNKNOWN); + } else if ( '.' == sc.ch ) { + parse_state = NUMERAL_SPECULATIVE_MANTISSA; + sc.SetState(SCE_ERLANG_UNKNOWN); + } else if (isalpha(sc.ch) && isupper(sc.ch)) { + sc.SetState(SCE_ERLANG_VARIABLE); + } else if (isalpha(sc.ch)) { + parse_state = ATOM_UNQUOTED; + sc.SetState(SCE_ERLANG_UNKNOWN); + } else if (isoperator(static_cast(sc.ch)) || sc.ch == '\\') { + sc.SetState(SCE_ERLANG_OPERATOR); + } + } + } + sc.Complete(); +} + +static int ClassifyFoldPointErlang( + Accessor &styler, + int styleNext, + int keyword_start +) { + int lev = 0; + if ( styler.Match(keyword_start,"case") + || ( + styler.Match(keyword_start,"fun") + && SCE_ERLANG_FUNCTION_NAME != styleNext) + || styler.Match(keyword_start,"if") + || styler.Match(keyword_start,"query") + || styler.Match(keyword_start,"receive") + ) { + ++lev; + } else if ( styler.Match(keyword_start,"end") ) { + --lev; + } + return lev; +} + + +static void FoldErlangDoc( + unsigned int startPos, int length, int initStyle, + WordList** /*keywordlists*/, Accessor &styler +) { + unsigned int endPos = startPos + length; + //~ int visibleChars = 0; + int lineCurrent = styler.GetLine(startPos); + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + char chNext = styler.SafeGetCharAt(startPos); + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + int keyword_start = 0; + + bool fold_keywords = true; + bool fold_comments = true; + bool fold_braces = true; + bool fold_function_clauses = false; + bool fold_clauses = false; + + //int clause_level = 0; + + for (unsigned int i = startPos; i < endPos; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + int stylePrev = style; + style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + + if ( (stylePrev != SCE_ERLANG_KEYWORD) && (style == SCE_ERLANG_KEYWORD) ) { + keyword_start = i; + } + if ( fold_keywords ) { + if ( (stylePrev == SCE_ERLANG_KEYWORD) + && (style != SCE_ERLANG_KEYWORD) + && (style != SCE_ERLANG_ATOM) + ) { + levelCurrent += ClassifyFoldPointErlang(styler,styleNext,keyword_start); + } + } + + if ( fold_comments ) { + if (style == SCE_ERLANG_COMMENT) { + if ((ch == '%') && (chNext == '{')) { + levelCurrent++; + } else if ((ch == '%') && (chNext == '}')) { + levelCurrent--; + } + } + } + + if ( fold_function_clauses ) { + if ( (SC_FOLDLEVELBASE == levelCurrent) /*&& (style == SCE_ERLANG_OPERATOR)*/ ) { + if ( (ch == '-') && (chNext == '>')) { + //~ fprintf(stderr,"levelCurrent=%d\n", levelCurrent); + //++clause_level; + //~ if ( 0 < clause_level ) + ++levelCurrent; + } + } + //~ if ( (stylePrev != SCE_ERLANG_RECORD) + //~ && (style != SCE_ERLANG_NUMBER) + //~ && (style != SCE_ERLANG_STRING) + //~ && (style != SCE_ERLANG_COMMENT) + //~ ) { + if ( (SC_FOLDLEVELBASE+1 == levelCurrent) && (ch == '.') ) { + //--clause_level; + //~ if ( 0 == clause_level ) + --levelCurrent; + } + //~ } + } + + if ( fold_clauses ) { + if ( (0 < levelCurrent) && (style == SCE_ERLANG_OPERATOR) ) { + if ((ch == '-') && (chNext == '>')) { + levelCurrent++; + } + if ( (ch == ';') ) { + levelCurrent--; + } + } + if ( (stylePrev != SCE_ERLANG_RECORD) + && (style != SCE_ERLANG_NUMBER) + && (style != SCE_ERLANG_STRING) + && (style != SCE_ERLANG_COMMENT) + ) { + if ( (ch == '.') ) { + levelCurrent--; + } + } + if ( (stylePrev == SCE_ERLANG_KEYWORD) + && (style != SCE_ERLANG_KEYWORD) + && (style != SCE_ERLANG_ATOM) + && ( + styler.Match(keyword_start,"end") // 'end' counted twice if fold_keywords too + || styler.Match(keyword_start,"after") ) + ) { + levelCurrent--; + } + } + + if ( fold_braces ) { + if (style == SCE_ERLANG_OPERATOR) { + if ( (ch == '{') || (ch == '(') || (ch == '[') ) { + levelCurrent++; + } else if ( (ch == '}') || (ch == ')') || (ch == ']') ) { + levelCurrent--; + } + } + } + + if (atEOL) { + int lev = levelPrev; + //~ if (visibleChars == 0 && foldCompact) + //~ lev |= SC_FOLDLEVELWHITEFLAG; + //~ if ((levelCurrent > levelPrev) && (visibleChars > 0)) + if ((levelCurrent > levelPrev)) { + lev |= SC_FOLDLEVELHEADERFLAG; + } + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + levelPrev = levelCurrent; + //~ visibleChars = 0; + } + //~ if (!isspacechar(ch)) + //~ visibleChars++; + + } + // Fill in the real level of the next line, keeping the current flags as they will be filled in later + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + styler.SetLevel(lineCurrent, levelPrev | flagsNext); +} + +static const char * const erlangWordListDesc[] = { + "Keywords", + 0 +}; + +LexerModule lmErlang( + SCLEX_ERLANG, + ColouriseErlangDoc, + "erlang", + FoldErlangDoc, + erlangWordListDesc); + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexFlagship.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexFlagship.cxx new file mode 100644 index 00000000..335726ec --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexFlagship.cxx @@ -0,0 +1,226 @@ +// Scintilla source code edit control +/** @file LexFlagShip.cxx + ** Lexer for FlagShip + ** (Syntactically compatible to other XBase dialects, like dBase, Clipper, Fox etc.) + **/ +// Copyright 2005 by Randy Butler +// Copyright 1998-2003 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +static bool IsFlagShipComment(Accessor &styler, int pos, int len) { + return len>0 && styler[pos]=='\''; +} + +static inline bool IsTypeCharacter(int ch) { + return ch == '%' || ch == '&' || ch == '@' || ch == '!' || ch == '#' || ch == '$'; +} + +// Extended to accept accented characters +static inline bool IsAWordChar(int ch) { + return ch >= 0x80 || + (isalnum(ch) || ch == '.' || ch == '_'); +} + +static inline bool IsAWordStart(int ch) { + return ch >= 0x80 || + (isalnum(ch) || ch == '_'); +} + +static inline bool IsADateCharacter(const int ch) { + return (ch < 0x80) && + (isalnum(ch) || ch == '|' || ch == '-' || ch == '/' || ch == ':' || ch == ' ' || ch == '\t'); +} + + +static void ColouriseFlagShipDoc(unsigned int startPos, int length, int initStyle, + WordList *keywordlists[], Accessor &styler) { + + //bool FSScriptSyntax = true; + WordList &keywords = *keywordlists[0]; + WordList &keywords2 = *keywordlists[1]; + WordList &keywords3 = *keywordlists[2]; + WordList &keywords4 = *keywordlists[3]; + + styler.StartAt(startPos); + + int visibleChars = 0; + + StyleContext sc(startPos, length, initStyle, styler); + + for (; sc.More(); sc.Forward()) { + + if (sc.state == SCE_FS_OPERATOR) { + sc.SetState(SCE_FS_DEFAULT); + } else if (sc.state == SCE_FS_IDENTIFIER) { + if (!IsAWordChar(sc.ch)) { + char s[100]; + sc.GetCurrentLowered(s, sizeof(s)); + if (keywords.InList(s)) { + sc.ChangeState(SCE_FS_KEYWORD); + } else if (keywords2.InList(s)) { + sc.ChangeState(SCE_FS_KEYWORD2); + } else if (keywords3.InList(s)) { + sc.ChangeState(SCE_FS_KEYWORD3); + } else if (keywords4.InList(s)) { + sc.ChangeState(SCE_FS_KEYWORD4); + }// Else, it is really an identifier... + sc.SetState(SCE_FS_DEFAULT); + } + } else if (sc.state == SCE_FS_NUMBER) { + if (!IsAWordChar(sc.ch)) { + sc.SetState(SCE_FS_DEFAULT); + } + } else if (sc.state == SCE_FS_STRING) { + // VB doubles quotes to preserve them, so just end this string + // state now as a following quote will start again + if (sc.ch == '\"') { + if (tolower(sc.chNext) == 'c') { + sc.Forward(); + } + sc.ForwardSetState(SCE_FS_DEFAULT); + } else if (sc.atLineEnd) { + sc.ChangeState(SCE_FS_STRINGEOL); + sc.ForwardSetState(SCE_FS_DEFAULT); + } + } else if (sc.state == SCE_FS_COMMENT) { + if (sc.Match('*', '/')) { // new code + sc.Forward(); + sc.ForwardSetState(SCE_FS_DEFAULT); + //if (sc.atLineEnd) { // old code + // sc.SetState(SCE_FS_DEFAULT); + } + } else if (sc.state == SCE_FS_COMMENTLINE) { //new code + if (sc.ch == '\r' || sc.ch == '\n') { + sc.SetState(SCE_FS_DEFAULT); + visibleChars = 0; + } + } else if (sc.state == SCE_FS_PREPROCESSOR) { + if (sc.atLineEnd) { + sc.SetState(SCE_FS_DEFAULT); + } + } else if (sc.state == SCE_FS_DATE) { + if (sc.ch == '#' || !IsADateCharacter(sc.chNext)) { + sc.ForwardSetState(SCE_FS_DEFAULT); + } + } + + // Determine if a new state should be entered. + if (sc.state == SCE_FS_DEFAULT) { + if (sc.Match('/', '*')) { // New code + sc.SetState(SCE_FS_COMMENT); + sc.Forward(); // Eat the * so it isn't used for the end of the comment + //if (sc.ch == '\'') { // Old code + // sc.SetState(SCE_FS_COMMENT); // old code + } else if (sc.Match('/', '/')) { // New code + sc.SetState(SCE_FS_COMMENTLINE); + } else if (sc.ch == '\"') { + sc.SetState(SCE_FS_STRING); + } else if (sc.ch == '#' && visibleChars == 0) { + // Preprocessor commands are alone on their line + sc.SetState(SCE_FS_PREPROCESSOR); + } else if (sc.ch == '#') { + int n = 1; + int chSeek = ' '; + while ((n < 100) && (chSeek == ' ' || chSeek == '\t')) { + chSeek = sc.GetRelative(n); + n++; + } + if (IsADigit(chSeek)) { + sc.SetState(SCE_FS_DATE); + } else { + sc.SetState(SCE_FS_OPERATOR); + } + } else if (sc.ch == '&' && tolower(sc.chNext) == 'h') { + sc.SetState(SCE_FS_NUMBER); + } else if (sc.ch == '&' && tolower(sc.chNext) == 'o') { + sc.SetState(SCE_FS_NUMBER); + } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { + sc.SetState(SCE_FS_NUMBER); + } else if (IsAWordStart(sc.ch) || (sc.ch == '[')) { + sc.SetState(SCE_FS_IDENTIFIER); + } else if (isoperator(static_cast(sc.ch)) || (sc.ch == '\\')) { + sc.SetState(SCE_FS_OPERATOR); + } + } + + if (sc.atLineEnd) { + visibleChars = 0; + } + if (!IsASpace(sc.ch)) { + visibleChars++; + } + } + sc.Complete(); +} + +static void FoldFlagShipDoc(unsigned int startPos, int length, int, + WordList *[], Accessor &styler) { + + int endPos = startPos + length; + + // Backtrack to previous line in case need to fix its fold status + int lineCurrent = styler.GetLine(startPos); + if (startPos > 0) { + if (lineCurrent > 0) { + lineCurrent--; + startPos = styler.LineStart(lineCurrent); + } + } + int spaceFlags = 0; + int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsFlagShipComment); + char chNext = styler[startPos]; + for (int i = startPos; i < endPos; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + + if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) { + int lev = indentCurrent; + int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsFlagShipComment); + if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { + // Only non whitespace lines can be headers + if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) { + lev |= SC_FOLDLEVELHEADERFLAG; + } else if (indentNext & SC_FOLDLEVELWHITEFLAG) { + // Line after is blank so check the next - maybe should continue further? + int spaceFlags2 = 0; + int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsFlagShipComment); + if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) { + lev |= SC_FOLDLEVELHEADERFLAG; + } + } + } + indentCurrent = indentNext; + styler.SetLevel(lineCurrent, lev); + lineCurrent++; + } + } +} + + +static const char * const FSWordListDesc[] = { + "Keywords", + "functions", + "user2", + "user3", + 0 +}; + +LexerModule lmFlagShip(SCLEX_FLAGSHIP, ColouriseFlagShipDoc, "flagship", FoldFlagShipDoc, FSWordListDesc); + + + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexForth.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexForth.cxx new file mode 100644 index 00000000..9be7d644 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexForth.cxx @@ -0,0 +1,348 @@ +// Scintilla source code edit control +/** @file LexCrontab.cxx + ** Lexer to use with extended crontab files used by a powerful + ** Windows scheduler/event monitor/automation manager nnCron. + ** (http://nemtsev.eserv.ru/) + **/ +// Copyright 1998-2001 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +bool is_whitespace(int ch){ + return ch == '\n' || ch == '\r' || ch == '\t' || ch == ' '; +} + +bool is_blank(int ch){ + return ch == '\t' || ch == ' '; +} +//#define FORTH_DEBUG +#ifdef FORTH_DEBUG +static FILE *f_debug; +#define log(x) fputs(f_debug,x); +#else +#define log(x) +#endif + +#define STATE_LOCALE +#define BL ' ' + +static Accessor *st; +static int cur_pos,pos1,pos2,pos0,lengthDoc; +char *buffer; + +char getChar(bool is_bl){ + char ch=st->SafeGetCharAt(cur_pos); + if(is_bl) if(is_whitespace(ch)) ch=BL; + return ch; +} + +char getCharBL(){ + char ch=st->SafeGetCharAt(cur_pos); + return ch; +} +bool is_eol(char ch){ + return ch=='\n' || ch=='\r'; +} + +int parse(char ch, bool skip_eol){ +// pos1 - start pos of word +// pos2 - pos after of word +// pos0 - start pos + char c=0; + int len; + bool is_bl=ch==BL; + pos0=pos1=pos2=cur_pos; + for(;cur_pos9 && base>10) digit-=7; + if(digit<0) return false; + if(digit>=base) return false; + } + return true; +} + +bool is_number(char *s){ + if(strncmp(s,"0x",2)==0) return _is_number(s+2,16); + return _is_number(s,10); +} + +static void ColouriseForthDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler) +{ + st=&styler; + cur_pos=startPos; + lengthDoc = startPos + length; + buffer = new char[length]; + +#ifdef FORTH_DEBUG + f_debug=fopen("c:\\sci.log","at"); +#endif + + WordList &control = *keywordLists[0]; + WordList &keyword = *keywordLists[1]; + WordList &defword = *keywordLists[2]; + WordList &preword1 = *keywordLists[3]; + WordList &preword2 = *keywordLists[4]; + WordList &strings = *keywordLists[5]; + + // go through all provided text segment + // using the hand-written state machine shown below + styler.StartAt(startPos); + styler.StartSegment(startPos); + while(parse(BL,true)!=0){ + if(pos0!=pos1){ + styler.ColourTo(pos0,SCE_FORTH_DEFAULT); + styler.ColourTo(pos1-1,SCE_FORTH_DEFAULT); + } + if(strcmp("\\",buffer)==0){ + styler.ColourTo(pos1,SCE_FORTH_COMMENT); + parse(1,false); + styler.ColourTo(pos2,SCE_FORTH_COMMENT); + }else if(strcmp("(",buffer)==0){ + styler.ColourTo(pos1,SCE_FORTH_COMMENT); + parse(')',true); + if(cur_pos +// The License.txt file describes the conditions under which this software may be distributed. +/***************************************/ +#include +#include +#include +#include +#include +/***************************************/ +#include "Platform.h" +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" +/***********************************************/ +static inline bool IsAWordChar(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '%'); +} +/**********************************************/ +static inline bool IsAWordStart(const int ch) { + return (ch < 0x80) && (isalnum(ch)); +} +/***************************************/ +inline bool IsABlank(unsigned int ch) { + return (ch == ' ') || (ch == 0x09) || (ch == 0x0b) ; +} +/***************************************/ +inline bool IsALineEnd(char ch) { + return ((ch == '\n') || (ch == '\r')) ; +} +/***************************************/ +unsigned int GetContinuedPos(unsigned int pos, Accessor &styler) { + while (!IsALineEnd(styler.SafeGetCharAt(pos++))) continue; + if (styler.SafeGetCharAt(pos) == '\n') pos++; + while (IsABlank(styler.SafeGetCharAt(pos++))) continue; + char chCur = styler.SafeGetCharAt(pos); + if (chCur == '&') { + while (IsABlank(styler.SafeGetCharAt(++pos))) continue; + return pos; + } else { + return pos; + } +} +/***************************************/ +static void ColouriseFortranDoc(unsigned int startPos, int length, int initStyle, + WordList *keywordlists[], Accessor &styler, bool isFixFormat) { + WordList &keywords = *keywordlists[0]; + WordList &keywords2 = *keywordlists[1]; + WordList &keywords3 = *keywordlists[2]; + /***************************************/ + int posLineStart = 0, numNonBlank = 0, prevState = 0; + int endPos = startPos + length; + /***************************************/ + // backtrack to the nearest keyword + while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_F_WORD)) { + startPos--; + } + startPos = styler.LineStart(styler.GetLine(startPos)); + initStyle = styler.StyleAt(startPos - 1); + StyleContext sc(startPos, endPos-startPos, initStyle, styler); + /***************************************/ + for (; sc.More(); sc.Forward()) { + // remember the start position of the line + if (sc.atLineStart) { + posLineStart = sc.currentPos; + numNonBlank = 0; + sc.SetState(SCE_F_DEFAULT); + } + if (!IsASpaceOrTab(sc.ch)) numNonBlank ++; + /***********************************************/ + // Handle the fix format generically + int toLineStart = sc.currentPos - posLineStart; + if (isFixFormat && (toLineStart < 6 || toLineStart > 72)) { + if (toLineStart == 0 && (tolower(sc.ch) == 'c' || sc.ch == '*') || sc.ch == '!') { + sc.SetState(SCE_F_COMMENT); + while (!sc.atLineEnd && sc.More()) sc.Forward(); // Until line end + } else if (toLineStart > 72) { + sc.SetState(SCE_F_COMMENT); + while (!sc.atLineEnd && sc.More()) sc.Forward(); // Until line end + } else if (toLineStart < 5) { + if (IsADigit(sc.ch)) + sc.SetState(SCE_F_LABEL); + else + sc.SetState(SCE_F_DEFAULT); + } else if (toLineStart == 5) { + if (!IsASpace(sc.ch) && sc.ch != '0') { + sc.SetState(SCE_F_CONTINUATION); + sc.ForwardSetState(prevState); + } else + sc.SetState(SCE_F_DEFAULT); + } + continue; + } + /***************************************/ + // Handle line continuation generically. + if (!isFixFormat && sc.ch == '&') { + char chTemp = ' '; + int j = 1; + while (IsABlank(chTemp) && j<132) { + chTemp = static_cast(sc.GetRelative(j)); + j++; + } + if (chTemp == '!') { + sc.SetState(SCE_F_CONTINUATION); + if (sc.chNext == '!') sc.ForwardSetState(SCE_F_COMMENT); + } else if (chTemp == '\r' || chTemp == '\n') { + int currentState = sc.state; + sc.SetState(SCE_F_CONTINUATION); + sc.ForwardSetState(SCE_F_DEFAULT); + while (IsASpace(sc.ch) && sc.More()) sc.Forward(); + if (sc.ch == '&') { + sc.SetState(SCE_F_CONTINUATION); + sc.Forward(); + } + sc.SetState(currentState); + } + } + /***************************************/ + // Determine if the current state should terminate. + if (sc.state == SCE_F_OPERATOR) { + sc.SetState(SCE_F_DEFAULT); + } else if (sc.state == SCE_F_NUMBER) { + if (!(IsAWordChar(sc.ch) || sc.ch=='\'' || sc.ch=='\"' || sc.ch=='.')) { + sc.SetState(SCE_F_DEFAULT); + } + } else if (sc.state == SCE_F_IDENTIFIER) { + if (!IsAWordChar(sc.ch) || (sc.ch == '%')) { + char s[100]; + sc.GetCurrentLowered(s, sizeof(s)); + if (keywords.InList(s)) { + sc.ChangeState(SCE_F_WORD); + } else if (keywords2.InList(s)) { + sc.ChangeState(SCE_F_WORD2); + } else if (keywords3.InList(s)) { + sc.ChangeState(SCE_F_WORD3); + } + sc.SetState(SCE_F_DEFAULT); + } + } else if (sc.state == SCE_F_COMMENT || sc.state == SCE_F_PREPROCESSOR) { + if (sc.ch == '\r' || sc.ch == '\n') { + sc.SetState(SCE_F_DEFAULT); + } + } else if (sc.state == SCE_F_STRING1) { + prevState = sc.state; + if (sc.ch == '\'') { + if (sc.chNext == '\'') { + sc.Forward(); + } else { + sc.ForwardSetState(SCE_F_DEFAULT); + prevState = SCE_F_DEFAULT; + } + } else if (sc.atLineEnd) { + sc.ChangeState(SCE_F_STRINGEOL); + sc.ForwardSetState(SCE_F_DEFAULT); + } + } else if (sc.state == SCE_F_STRING2) { + prevState = sc.state; + if (sc.atLineEnd) { + sc.ChangeState(SCE_F_STRINGEOL); + sc.ForwardSetState(SCE_F_DEFAULT); + } else if (sc.ch == '\"') { + if (sc.chNext == '\"') { + sc.Forward(); + } else { + sc.ForwardSetState(SCE_F_DEFAULT); + prevState = SCE_F_DEFAULT; + } + } + } else if (sc.state == SCE_F_OPERATOR2) { + if (sc.ch == '.') { + sc.ForwardSetState(SCE_F_DEFAULT); + } + } else if (sc.state == SCE_F_CONTINUATION) { + sc.SetState(SCE_F_DEFAULT); + } else if (sc.state == SCE_F_LABEL) { + if (!IsADigit(sc.ch)) { + sc.SetState(SCE_F_DEFAULT); + } else { + if (isFixFormat && sc.currentPos-posLineStart > 4) + sc.SetState(SCE_F_DEFAULT); + else if (numNonBlank > 5) + sc.SetState(SCE_F_DEFAULT); + } + } + /***************************************/ + // Determine if a new state should be entered. + if (sc.state == SCE_F_DEFAULT) { + if (sc.ch == '!') { + if (sc.chNext == '$') { + sc.SetState(SCE_F_PREPROCESSOR); + } else { + sc.SetState(SCE_F_COMMENT); + } + } else if ((!isFixFormat) && IsADigit(sc.ch) && numNonBlank == 1) { + sc.SetState(SCE_F_LABEL); + } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { + sc.SetState(SCE_F_NUMBER); + } else if ((tolower(sc.ch) == 'b' || tolower(sc.ch) == 'o' || + tolower(sc.ch) == 'z') && (sc.chNext == '\"' || sc.chNext == '\'')) { + sc.SetState(SCE_F_NUMBER); + sc.Forward(); + } else if (sc.ch == '.' && isalpha(sc.chNext)) { + sc.SetState(SCE_F_OPERATOR2); + } else if (IsAWordStart(sc.ch)) { + sc.SetState(SCE_F_IDENTIFIER); + } else if (sc.ch == '\"') { + sc.SetState(SCE_F_STRING2); + } else if (sc.ch == '\'') { + sc.SetState(SCE_F_STRING1); + } else if (isoperator(static_cast(sc.ch))) { + sc.SetState(SCE_F_OPERATOR); + } + } + } + sc.Complete(); +} +/***************************************/ +// To determine the folding level depending on keywords +static int classifyFoldPointFortran(const char* s, const char* prevWord, const char chNextNonBlank) { + int lev = 0; + if ((strcmp(prevWord, "else") == 0 && strcmp(s, "if") == 0) || strcmp(s, "elseif") == 0) + return -1; + if (strcmp(s, "associate") == 0 || strcmp(s, "block") == 0 + || strcmp(s, "blockdata") == 0 || strcmp(s, "select") == 0 + || strcmp(s, "do") == 0 || strcmp(s, "enum") ==0 + || strcmp(s, "function") == 0 || strcmp(s, "interface") == 0 + || strcmp(s, "module") == 0 || strcmp(s, "program") == 0 + || strcmp(s, "subroutine") == 0 || strcmp(s, "then") == 0 + || (strcmp(s, "type") == 0 && chNextNonBlank != '(') ){ + if (strcmp(prevWord, "end") == 0) + lev = 0; + else + lev = 1; + } else if (strcmp(s, "end") == 0 && chNextNonBlank != '=' + || strcmp(s, "endassociate") == 0 || strcmp(s, "endblock") == 0 + || strcmp(s, "endblockdata") == 0 || strcmp(s, "endselect") == 0 + || strcmp(s, "enddo") == 0 || strcmp(s, "endenum") ==0 + || strcmp(s, "endif") == 0 || strcmp(s, "endforall") == 0 + || strcmp(s, "endfunction") == 0 || strcmp(s, "endinterface") == 0 + || strcmp(s, "endmodule") == 0 || strcmp(s, "endprogram") == 0 + || strcmp(s, "endsubroutine") == 0 || strcmp(s, "endtype") == 0 + || strcmp(s, "endwhere") == 0 + || strcmp(s, "procedure") == 0 ) { // Take care of the module procedure statement + lev = -1; + } else if (strcmp(prevWord, "end") == 0 && strcmp(s, "if") == 0){ // end if + lev = 0; + } + return lev; +} +// Folding the code +static void FoldFortranDoc(unsigned int startPos, int length, int initStyle, + Accessor &styler, bool isFixFormat) { + // + // bool foldComment = styler.GetPropertyInt("fold.comment") != 0; + // Do not know how to fold the comment at the moment. + // + bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + unsigned int endPos = startPos + length; + int visibleChars = 0; + int lineCurrent = styler.GetLine(startPos); + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + char chNext = styler[startPos]; + char chNextNonBlank; + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + /***************************************/ + int lastStart = 0; + char prevWord[32] = ""; + char Label[6] = ""; + // Variables for do label folding. + static int doLabels[100]; + static int posLabel=-1; + /***************************************/ + for (unsigned int i = startPos; i < endPos; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + chNextNonBlank = chNext; + unsigned int j=i+1; + while(IsABlank(chNextNonBlank) && j(tolower(styler[lastStart+k])); + } + s[k] = '\0'; + // Handle the forall and where statement and structure. + if (strcmp(s, "forall") == 0 || strcmp(s, "where") == 0) { + if (strcmp(prevWord, "end") != 0) { + j = i + 1; + char chBrace = '(', chSeek = ')', ch1 = styler.SafeGetCharAt(j); + // Find the position of the first ( + while (ch1 != chBrace && j -1) { + levelCurrent--; + posLabel--; + } + } + } + if (atEOL) { + int lev = levelPrev; + if (visibleChars == 0 && foldCompact) + lev |= SC_FOLDLEVELWHITEFLAG; + if ((levelCurrent > levelPrev) && (visibleChars > 0)) + lev |= SC_FOLDLEVELHEADERFLAG; + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + levelPrev = levelCurrent; + visibleChars = 0; + strcpy(prevWord, ""); + } + /***************************************/ + if (!isspacechar(ch)) visibleChars++; + } + /***************************************/ + // Fill in the real level of the next line, keeping the current flags as they will be filled in later + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + styler.SetLevel(lineCurrent, levelPrev | flagsNext); +} +/***************************************/ +static const char * const FortranWordLists[] = { + "Primary keywords and identifiers", + "Intrinsic functions", + "Extended and user defined functions", + 0, +}; +/***************************************/ +static void ColouriseFortranDocFreeFormat(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) { + ColouriseFortranDoc(startPos, length, initStyle, keywordlists, styler, false); +} +/***************************************/ +static void ColouriseFortranDocFixFormat(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) { + ColouriseFortranDoc(startPos, length, initStyle, keywordlists, styler, true); +} +/***************************************/ +static void FoldFortranDocFreeFormat(unsigned int startPos, int length, int initStyle, + WordList *[], Accessor &styler) { + FoldFortranDoc(startPos, length, initStyle,styler, false); +} +/***************************************/ +static void FoldFortranDocFixFormat(unsigned int startPos, int length, int initStyle, + WordList *[], Accessor &styler) { + FoldFortranDoc(startPos, length, initStyle,styler, true); +} +/***************************************/ +LexerModule lmFortran(SCLEX_FORTRAN, ColouriseFortranDocFreeFormat, "fortran", FoldFortranDocFreeFormat, FortranWordLists); +LexerModule lmF77(SCLEX_F77, ColouriseFortranDocFixFormat, "f77", FoldFortranDocFixFormat, FortranWordLists); diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexGen.py b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexGen.py new file mode 100644 index 00000000..59c25722 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexGen.py @@ -0,0 +1,241 @@ +# LexGen.py - implemented 2002 by Neil Hodgson neilh@scintilla.org +# Released to the public domain. + +# Regenerate the Scintilla and SciTE source files that list +# all the lexers and all the properties files. +# Should be run whenever a new lexer is added or removed. +# Requires Python 2.1 or later +# Most files are regenerated in place with templates stored in comments. +# The VS .NET project file is generated into a different file as the +# VS .NET environment will not retain comments when modifying the file. +# The files are copied to a string apart from sections between a +# ++Autogenerated comment and a --Autogenerated comment which is +# generated by the CopyWithInsertion function. After the whole +# string is instantiated, it is compared with the target file and +# if different the file is rewritten. +# Does not regenerate the Visual C++ 6 project files but does the VS .NET +# project file. + +import string +import sys +import os +import glob + +# EOL constants +CR = "\r" +LF = "\n" +CRLF = "\r\n" +if sys.platform == "win32": + NATIVE = CRLF +else: + # Yes, LF is the native EOL even on Mac OS X. CR is just for + # Mac OS <=9 (a.k.a. "Mac Classic") + NATIVE = LF + +# Automatically generated sections contain start and end comments, +# a definition line and the results. +# The results are replaced by regenerating based on the definition line. +# The definition line is a comment prefix followed by "**". +# If there is a digit after the ** then this indicates which list to use +# and the digit and next character are not part of the definition +# Backslash is used as an escape within the definition line. +# The part between \( and \) is repeated for each item in the list. +# \* is replaced by each list item. \t, and \n are tab and newline. +def CopyWithInsertion(input, commentPrefix, retainDefs, eolType, *lists): + copying = 1 + listid = 0 + output = [] + for line in input.splitlines(0): + isStartGenerated = line.startswith(commentPrefix + "++Autogenerated") + if copying and not isStartGenerated: + output.append(line) + if isStartGenerated: + if retainDefs: + output.append(line) + copying = 0 + definition = "" + elif not copying and line.startswith(commentPrefix + "**"): + if retainDefs: + output.append(line) + definition = line[len(commentPrefix + "**"):] + listid = 0 + if definition[0] in string.digits: + listid = int(definition[:1]) + definition = definition[2:] + # Hide double slashes as a control character + definition = definition.replace("\\\\", "\001") + # Do some normal C style transforms + definition = definition.replace("\\n", "\n") + definition = definition.replace("\\t", "\t") + # Get the doubled backslashes back as single backslashes + definition = definition.replace("\001", "\\") + startRepeat = definition.find("\\(") + endRepeat = definition.find("\\)") + intro = definition[:startRepeat] + out = "" + if intro.endswith("\n"): + pos = 0 + else: + pos = len(intro) + out += intro + middle = definition[startRepeat+2:endRepeat] + for i in lists[listid]: + item = middle.replace("\\*", i) + if pos and (pos + len(item) >= 80): + out += "\\\n" + pos = 0 + out += item + pos += len(item) + if item.endswith("\n"): + pos = 0 + outro = definition[endRepeat+2:] + out += outro + out = out.replace("\n", eolType) # correct EOLs in generated content + output.append(out) + elif line.startswith(commentPrefix + "--Autogenerated"): + copying = 1 + if retainDefs: + output.append(line) + output = [line.rstrip(" \t") for line in output] # trim trailing whitespace + return eolType.join(output) + eolType + +def UpdateFile(filename, updated): + """ If the file is different to updated then copy updated + into the file else leave alone so CVS and make don't treat + it as modified. """ + try: + infile = open(filename, "rb") + except IOError: # File is not there yet + out = open(filename, "wb") + out.write(updated) + out.close() + print "New", filename + return + original = infile.read() + infile.close() + if updated != original: + os.unlink(filename) + out = open(filename, "wb") + out.write(updated) + out.close() + print "Changed", filename + #~ else: + #~ print "Unchanged", filename + +def Generate(inpath, outpath, commentPrefix, eolType, *lists): + """Generate 'outpath' from 'inpath'. + + "eolType" indicates the type of EOLs to use in the generated + file. It should be one of following constants: LF, CRLF, + CR, or NATIVE. + """ + #print "generate '%s' -> '%s' (comment prefix: %r, eols: %r)"\ + # % (inpath, outpath, commentPrefix, eolType) + try: + infile = open(inpath, "r") + except IOError: + print "Can not open", inpath + return + original = infile.read() + infile.close() + updated = CopyWithInsertion(original, commentPrefix, + inpath == outpath, eolType, *lists) + UpdateFile(outpath, updated) + +def Regenerate(filename, commentPrefix, eolType, *lists): + """Regenerate the given file. + + "eolType" indicates the type of EOLs to use in the generated + file. It should be one of following constants: LF, CRLF, + CR, or NATIVE. + """ + Generate(filename, filename, commentPrefix, eolType, *lists) + +def FindModules(lexFile): + modules = [] + f = open(lexFile) + for l in f.readlines(): + if l.startswith("LexerModule"): + l = l.replace("(", " ") + modules.append(l.split()[1]) + return modules + +knownIrregularProperties = [ + "fold", + "styling.within.preprocessor", + "tab.timmy.whinge.level", + "asp.default.language", + "html.tags.case.sensitive", + "ps.level", + "ps.tokenize", + "sql.backslash.escapes", + "nsis.uservars", + "nsis.ignorecase" +] + +def FindProperties(lexFile): + properties = set() + f = open(lexFile) + for l in f.readlines(): + if "GetProperty" in l: + l = l.strip() + if not l.startswith("//"): # Drop comments + propertyName = l.split("\"")[1] + if propertyName.lower() == propertyName: + # Only allow lower case property names + if propertyName in knownIrregularProperties or \ + propertyName.startswith("fold.") or \ + propertyName.startswith("lexer."): + properties.add(propertyName) + return properties + +def ciCompare(a,b): + return cmp(a.lower(), b.lower()) + +def RegenerateAll(): + root="../../" + + # Find all the lexer source code files + lexFilePaths = glob.glob(root + "scintilla/src/Lex*.cxx") + lexFiles = [os.path.basename(f)[:-4] for f in lexFilePaths] + print lexFiles + lexerModules = [] + lexerProperties = set() + for lexFile in lexFilePaths: + lexerModules.extend(FindModules(lexFile)) + lexerProperties.update(FindProperties(lexFile)) + lexerModules.sort(ciCompare) + lexerProperties.remove("fold.comment.python") + lexerProperties = list(lexerProperties) + lexerProperties.sort(ciCompare) + + # Find all the SciTE properties files + otherProps = ["abbrev.properties", "Embedded.properties", "SciTEGlobal.properties", "SciTE.properties"] + propFilePaths = glob.glob(root + "scite/src/*.properties") + propFiles = [os.path.basename(f) for f in propFilePaths if os.path.basename(f) not in otherProps] + propFiles.sort(ciCompare) + print propFiles + + # Find all the menu command IDs in the SciTE header + SciTEHeader = file(root + "scite/src/SciTE.h") + lines = SciTEHeader.read().split("\n") + SciTEHeader.close() + ids = [id for id in [l.split()[1] for l in lines if l.startswith("#define")] if id.startswith("IDM_")] + #print ids + + Regenerate(root + "scintilla/src/KeyWords.cxx", "//", NATIVE, lexerModules) + Regenerate(root + "scintilla/win32/makefile", "#", NATIVE, lexFiles) + Regenerate(root + "scintilla/win32/scintilla.mak", "#", NATIVE, lexFiles) + Regenerate(root + "scintilla/win32/scintilla_vc6.mak", "#", NATIVE, lexFiles) + # Use Unix EOLs for gtk Makefiles so they work for Linux users when + # extracted from the Scintilla source ZIP (typically created on + # Windows). + Regenerate(root + "scintilla/gtk/makefile", "#", LF, lexFiles) + Regenerate(root + "scintilla/gtk/scintilla.mak", "#", NATIVE, lexFiles) + Regenerate(root + "scite/win32/makefile", "#", NATIVE, lexFiles, propFiles) + Regenerate(root + "scite/win32/scite.mak", "#", NATIVE, lexFiles, propFiles) + Regenerate(root + "scite/src/SciTEProps.cxx", "//", NATIVE, lexerProperties, ids) + Generate(root + "scite/boundscheck/vcproj.gen", + root + "scite/boundscheck/SciTE.vcproj", "#", NATIVE, lexFiles) + +RegenerateAll() diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexGui4Cli.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexGui4Cli.cxx new file mode 100644 index 00000000..417d1f2b --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexGui4Cli.cxx @@ -0,0 +1,309 @@ +// Scintilla source code edit control +// Copyright 1998-2002 by Neil Hodgson +/* +This is the Lexer for Gui4Cli, included in SciLexer.dll +- by d. Keletsekis, 2/10/2003 + +To add to SciLexer.dll: +1. Add the values below to INCLUDE\Scintilla.iface +2. Run the include/HFacer.py script +3. Run the src/lexGen.py script + +val SCE_GC_DEFAULT=0 +val SCE_GC_COMMENTLINE=1 +val SCE_GC_COMMENTBLOCK=2 +val SCE_GC_GLOBAL=3 +val SCE_GC_EVENT=4 +val SCE_GC_ATTRIBUTE=5 +val SCE_GC_CONTROL=6 +val SCE_GC_COMMAND=7 +val SCE_GC_STRING=8 +val SCE_GC_OPERATOR=9 +*/ + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +#define debug Platform::DebugPrintf + +static inline bool IsAWordChar(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch =='\\'); +} + +static inline bool IsAWordStart(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '.'); +} + +inline bool isGCOperator(int ch) +{ if (isalnum(ch)) + return false; + // '.' left out as it is used to make up numbers + if (ch == '*' || ch == '/' || ch == '-' || ch == '+' || + ch == '(' || ch == ')' || ch == '=' || ch == '%' || + ch == '[' || ch == ']' || ch == '<' || ch == '>' || + ch == ',' || ch == ';' || ch == ':') + return true; + return false; +} + +#define isSpace(x) ((x)==' ' || (x)=='\t') +#define isNL(x) ((x)=='\n' || (x)=='\r') +#define isSpaceOrNL(x) (isSpace(x) || isNL(x)) +#define BUFFSIZE 500 +#define isFoldPoint(x) ((styler.LevelAt(x) & SC_FOLDLEVELNUMBERMASK) == 1024) + +static void colorFirstWord(WordList *keywordlists[], Accessor &styler, + StyleContext *sc, char *buff, int length, int) +{ + int c = 0; + while (sc->More() && isSpaceOrNL(sc->ch)) + { sc->Forward(); + } + styler.ColourTo(sc->currentPos - 1, sc->state); + + if (!IsAWordChar(sc->ch)) // comment, marker, etc.. + return; + + while (sc->More() && !isSpaceOrNL(sc->ch) && (c < length-1) && !isGCOperator(sc->ch)) + { buff[c] = static_cast(sc->ch); + ++c; sc->Forward(); + } + buff[c] = '\0'; + char *p = buff; + while (*p) // capitalize.. + { if (islower(*p)) *p = static_cast(toupper(*p)); + ++p; + } + + WordList &kGlobal = *keywordlists[0]; // keyword lists set by the user + WordList &kEvent = *keywordlists[1]; + WordList &kAttribute = *keywordlists[2]; + WordList &kControl = *keywordlists[3]; + WordList &kCommand = *keywordlists[4]; + + int state = 0; + // int level = styler.LevelAt(line) & SC_FOLDLEVELNUMBERMASK; + // debug ("line = %d, level = %d", line, level); + + if (kGlobal.InList(buff)) state = SCE_GC_GLOBAL; + else if (kAttribute.InList(buff)) state = SCE_GC_ATTRIBUTE; + else if (kControl.InList(buff)) state = SCE_GC_CONTROL; + else if (kCommand.InList(buff)) state = SCE_GC_COMMAND; + else if (kEvent.InList(buff)) state = SCE_GC_EVENT; + + if (state) + { sc->ChangeState(state); + styler.ColourTo(sc->currentPos - 1, sc->state); + sc->ChangeState(SCE_GC_DEFAULT); + } + else + { sc->ChangeState(SCE_GC_DEFAULT); + styler.ColourTo(sc->currentPos - 1, sc->state); + } +} + +// Main colorizing function called by Scintilla +static void +ColouriseGui4CliDoc(unsigned int startPos, int length, int initStyle, + WordList *keywordlists[], Accessor &styler) +{ + styler.StartAt(startPos); + + int quotestart = 0, oldstate, currentline = styler.GetLine(startPos); + styler.StartSegment(startPos); + bool noforward; + char buff[BUFFSIZE+1]; // buffer for command name + + StyleContext sc(startPos, length, initStyle, styler); + buff[0] = '\0'; // cbuff = 0; + + if (sc.state != SCE_GC_COMMENTBLOCK) // colorize 1st word.. + colorFirstWord(keywordlists, styler, &sc, buff, BUFFSIZE, currentline); + + while (sc.More()) + { noforward = 0; + + switch (sc.ch) + { + case '/': + if (sc.state == SCE_GC_COMMENTBLOCK || sc.state == SCE_GC_STRING) + break; + if (sc.chNext == '/') // line comment + { sc.SetState (SCE_GC_COMMENTLINE); + sc.Forward(); + styler.ColourTo(sc.currentPos, sc.state); + } + else if (sc.chNext == '*') // block comment + { sc.SetState(SCE_GC_COMMENTBLOCK); + sc.Forward(); + styler.ColourTo(sc.currentPos, sc.state); + } + else + styler.ColourTo(sc.currentPos, sc.state); + break; + + case '*': // end of comment block, or operator.. + if (sc.state == SCE_GC_STRING) + break; + if (sc.state == SCE_GC_COMMENTBLOCK && sc.chNext == '/') + { sc.Forward(); + styler.ColourTo(sc.currentPos, sc.state); + sc.ChangeState (SCE_GC_DEFAULT); + } + else + styler.ColourTo(sc.currentPos, sc.state); + break; + + case '\'': case '\"': // strings.. + if (sc.state == SCE_GC_COMMENTBLOCK || sc.state == SCE_GC_COMMENTLINE) + break; + if (sc.state == SCE_GC_STRING) + { if (sc.ch == quotestart) // match same quote char.. + { styler.ColourTo(sc.currentPos, sc.state); + sc.ChangeState(SCE_GC_DEFAULT); + quotestart = 0; + } } + else + { styler.ColourTo(sc.currentPos - 1, sc.state); + sc.ChangeState(SCE_GC_STRING); + quotestart = sc.ch; + } + break; + + case ';': // end of commandline character + if (sc.state != SCE_GC_COMMENTBLOCK && sc.state != SCE_GC_COMMENTLINE && + sc.state != SCE_GC_STRING) + { + styler.ColourTo(sc.currentPos - 1, sc.state); + styler.ColourTo(sc.currentPos, SCE_GC_OPERATOR); + sc.ChangeState(SCE_GC_DEFAULT); + sc.Forward(); + colorFirstWord(keywordlists, styler, &sc, buff, BUFFSIZE, currentline); + noforward = 1; // don't move forward - already positioned at next char.. + } + break; + + case '+': case '-': case '=': case '!': // operators.. + case '<': case '>': case '&': case '|': case '$': + if (sc.state != SCE_GC_COMMENTBLOCK && sc.state != SCE_GC_COMMENTLINE && + sc.state != SCE_GC_STRING) + { + styler.ColourTo(sc.currentPos - 1, sc.state); + styler.ColourTo(sc.currentPos, SCE_GC_OPERATOR); + sc.ChangeState(SCE_GC_DEFAULT); + } + break; + + case '\\': // escape - same as operator, but also mark in strings.. + if (sc.state != SCE_GC_COMMENTBLOCK && sc.state != SCE_GC_COMMENTLINE) + { + oldstate = sc.state; + styler.ColourTo(sc.currentPos - 1, sc.state); + sc.Forward(); // mark also the next char.. + styler.ColourTo(sc.currentPos, SCE_GC_OPERATOR); + sc.ChangeState(oldstate); + } + break; + + case '\n': case '\r': + ++currentline; + if (sc.state == SCE_GC_COMMENTLINE) + { styler.ColourTo(sc.currentPos, sc.state); + sc.ChangeState (SCE_GC_DEFAULT); + } + else if (sc.state != SCE_GC_COMMENTBLOCK) + { colorFirstWord(keywordlists, styler, &sc, buff, BUFFSIZE, currentline); + noforward = 1; // don't move forward - already positioned at next char.. + } + break; + +// case ' ': case '\t': +// default : + } + + if (!noforward) sc.Forward(); + + } + sc.Complete(); +} + +// Main folding function called by Scintilla - (based on props (.ini) files function) +static void FoldGui4Cli(unsigned int startPos, int length, int, + WordList *[], Accessor &styler) +{ + bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + + unsigned int endPos = startPos + length; + int visibleChars = 0; + int lineCurrent = styler.GetLine(startPos); + + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + bool headerPoint = false; + + for (unsigned int i = startPos; i < endPos; i++) + { + char ch = chNext; + chNext = styler[i+1]; + + int style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); + + if (style == SCE_GC_EVENT || style == SCE_GC_GLOBAL) + { headerPoint = true; // fold at events and globals + } + + if (atEOL) + { int lev = SC_FOLDLEVELBASE+1; + + if (headerPoint) + lev = SC_FOLDLEVELBASE; + + if (visibleChars == 0 && foldCompact) + lev |= SC_FOLDLEVELWHITEFLAG; + + if (headerPoint) + lev |= SC_FOLDLEVELHEADERFLAG; + + if (lev != styler.LevelAt(lineCurrent)) // set level, if not already correct + { styler.SetLevel(lineCurrent, lev); + } + + lineCurrent++; // re-initialize our flags + visibleChars = 0; + headerPoint = false; + } + + if (!(isspacechar(ch))) // || (style == SCE_GC_COMMENTLINE) || (style != SCE_GC_COMMENTBLOCK))) + visibleChars++; + } + + int lev = headerPoint ? SC_FOLDLEVELBASE : SC_FOLDLEVELBASE+1; + int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; + styler.SetLevel(lineCurrent, lev | flagsNext); +} + +// I have no idea what these are for.. probably accessible by some message. +static const char * const gui4cliWordListDesc[] = { + "Globals", "Events", "Attributes", "Control", "Commands", + 0 +}; + +// Declare language & pass our function pointers to Scintilla +LexerModule lmGui4Cli(SCLEX_GUI4CLI, ColouriseGui4CliDoc, "gui4cli", FoldGui4Cli, gui4cliWordListDesc); + +#undef debug + diff --git a/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexHTML.cxx b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexHTML.cxx new file mode 100644 index 00000000..20a5a633 --- /dev/null +++ b/MOULOpenSourceClientPlugin/StaticSDKs/Win32/Scintilla/src/LexHTML.cxx @@ -0,0 +1,2042 @@ +// Scintilla source code edit control +/** @file LexHTML.cxx + ** Lexer for HTML. + **/ +// Copyright 1998-2005 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include + +#include "Platform.h" + +#include "PropSet.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "KeyWords.h" +#include "Scintilla.h" +#include "SciLexer.h" + +#define SCE_HA_JS (SCE_HJA_START - SCE_HJ_START) +#define SCE_HA_VBS (SCE_HBA_START - SCE_HB_START) +#define SCE_HA_PYTHON (SCE_HPA_START - SCE_HP_START) + +enum script_type { eScriptNone = 0, eScriptJS, eScriptVBS, eScriptPython, eScriptPHP, eScriptXML, eScriptSGML, eScriptSGMLblock }; +enum script_mode { eHtml = 0, eNonHtmlScript, eNonHtmlPreProc, eNonHtmlScriptPreProc }; + +static inline bool IsAWordChar(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_'); +} + +static inline bool IsAWordStart(const int ch) { + return (ch < 0x80) && (isalnum(ch) || ch == '_'); +} + +static inline int MakeLowerCase(int ch) { + if (ch < 'A' || ch > 'Z') + return ch; + else + return ch - 'A' + 'a'; +} + +static void GetTextSegment(Accessor &styler, unsigned int start, unsigned int end, char *s, size_t len) { + size_t i = 0; + for (; (i < end - start + 1) && (i < len-1); i++) { + s[i] = static_cast(MakeLowerCase(styler[start + i])); + } + s[i] = '\0'; +} + +static script_type segIsScriptingIndicator(Accessor &styler, unsigned int start, unsigned int end, script_type prevValue) { + char s[100]; + GetTextSegment(styler, start, end, s, sizeof(s)); + //Platform::DebugPrintf("Scripting indicator [%s]\n", s); + if (strstr(s, "src")) // External script + return eScriptNone; + if (strstr(s, "vbs")) + return eScriptVBS; + if (strstr(s, "pyth")) + return eScriptPython; + if (strstr(s, "javas")) + return eScriptJS; + if (strstr(s, "jscr")) + return eScriptJS; + if (strstr(s, "php")) + return eScriptPHP; + if (strstr(s, "xml")) + return eScriptXML; + + return prevValue; +} + +static int PrintScriptingIndicatorOffset(Accessor &styler, unsigned int start, unsigned int end) { + int iResult = 0; + char s[100]; + GetTextSegment(styler, start, end, s, sizeof(s)); + if (0 == strncmp(s, "php", 3)) { + iResult = 3; + } + + return iResult; +} + +static script_type ScriptOfState(int state) { + if ((state >= SCE_HP_START) && (state <= SCE_HP_IDENTIFIER)) { + return eScriptPython; + } else if ((state >= SCE_HB_START) && (state <= SCE_HB_STRINGEOL)) { + return eScriptVBS; + } else if ((state >= SCE_HJ_START) && (state <= SCE_HJ_REGEX)) { + return eScriptJS; + } else if ((state >= SCE_HPHP_DEFAULT) && (state <= SCE_HPHP_COMMENTLINE)) { + return eScriptPHP; + } else if ((state >= SCE_H_SGML_DEFAULT) && (state < SCE_H_SGML_BLOCK_DEFAULT)) { + return eScriptSGML; + } else if (state == SCE_H_SGML_BLOCK_DEFAULT) { + return eScriptSGMLblock; + } else { + return eScriptNone; + } +} + +static int statePrintForState(int state, script_mode inScriptType) { + int StateToPrint; + + if ((state >= SCE_HP_START) && (state <= SCE_HP_IDENTIFIER)) { + StateToPrint = state + ((inScriptType == eNonHtmlScript) ? 0 : SCE_HA_PYTHON); + } else if ((state >= SCE_HB_START) && (state <= SCE_HB_STRINGEOL)) { + StateToPrint = state + ((inScriptType == eNonHtmlScript) ? 0 : SCE_HA_VBS); + } else if ((state >= SCE_HJ_START) && (state <= SCE_HJ_REGEX)) { + StateToPrint = state + ((inScriptType == eNonHtmlScript) ? 0 : SCE_HA_JS); + } else { + StateToPrint = state; + } + + return StateToPrint; +} + +static int stateForPrintState(int StateToPrint) { + int state; + + if ((StateToPrint >= SCE_HPA_START) && (StateToPrint <= SCE_HPA_IDENTIFIER)) { + state = StateToPrint - SCE_HA_PYTHON; + } else if ((StateToPrint >= SCE_HBA_START) && (StateToPrint <= SCE_HBA_STRINGEOL)) { + state = StateToPrint - SCE_HA_VBS; + } else if ((StateToPrint >= SCE_HJA_START) && (StateToPrint <= SCE_HJA_REGEX)) { + state = StateToPrint - SCE_HA_JS; + } else { + state = StateToPrint; + } + + return state; +} + +static inline bool IsNumber(unsigned int start, Accessor &styler) { + return IsADigit(styler[start]) || (styler[start] == '.') || + (styler[start] == '-') || (styler[start] == '#'); +} + +static inline bool isStringState(int state) { + bool bResult; + + switch (state) { + case SCE_HJ_DOUBLESTRING: + case SCE_HJ_SINGLESTRING: + case SCE_HJA_DOUBLESTRING: + case SCE_HJA_SINGLESTRING: + case SCE_HB_STRING: + case SCE_HBA_STRING: + case SCE_HP_STRING: + case SCE_HP_CHARACTER: + case SCE_HP_TRIPLE: + case SCE_HP_TRIPLEDOUBLE: + case SCE_HPA_STRING: + case SCE_HPA_CHARACTER: + case SCE_HPA_TRIPLE: + case SCE_HPA_TRIPLEDOUBLE: + case SCE_HPHP_HSTRING: + case SCE_HPHP_SIMPLESTRING: + case SCE_HPHP_HSTRING_VARIABLE: + case SCE_HPHP_COMPLEX_VARIABLE: + bResult = true; + break; + default : + bResult = false; + break; + } + return bResult; +} + +static inline bool stateAllowsTermination(int state) { + bool allowTermination = !isStringState(state); + if (allowTermination) { + switch (state) { + case SCE_HPHP_COMMENT: + case SCE_HP_COMMENTLINE: + case SCE_HPA_COMMENTLINE: + allowTermination = false; + } + } + return allowTermination; +} + +// not really well done, since it's only comments that should lex the %> and <% +static inline bool isCommentASPState(int state) { + bool bResult; + + switch (state) { + case SCE_HJ_COMMENT: + case SCE_HJ_COMMENTLINE: + case SCE_HJ_COMMENTDOC: + case SCE_HB_COMMENTLINE: + case SCE_HP_COMMENTLINE: + case SCE_HPHP_COMMENT: + case SCE_HPHP_COMMENTLINE: + bResult = true; + break; + default : + bResult = false; + break; + } + return bResult; +} + +static void classifyAttribHTML(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) { + bool wordIsNumber = IsNumber(start, styler); + char chAttr = SCE_H_ATTRIBUTEUNKNOWN; + if (wordIsNumber) { + chAttr = SCE_H_NUMBER; + } else { + char s[100]; + GetTextSegment(styler, start, end, s, sizeof(s)); + if (keywords.InList(s)) + chAttr = SCE_H_ATTRIBUTE; + } + if ((chAttr == SCE_H_ATTRIBUTEUNKNOWN) && !keywords) + // No keywords -> all are known + chAttr = SCE_H_ATTRIBUTE; + styler.ColourTo(end, chAttr); +} + +static int classifyTagHTML(unsigned int start, unsigned int end, + WordList &keywords, Accessor &styler, bool &tagDontFold, + bool caseSensitive) { + char s[30 + 2]; + // Copy after the '<' + unsigned int i = 0; + for (unsigned int cPos = start; cPos <= end && i < 30; cPos++) { + char ch = styler[cPos]; + if ((ch != '<') && (ch != '/')) { + s[i++] = caseSensitive ? ch : static_cast(MakeLowerCase(ch)); + } + } + + //The following is only a quick hack, to see if this whole thing would work + //we first need the tagname with a trailing space... + s[i] = ' '; + s[i+1] = '\0'; + + //...to find it in the list of no-container-tags + // (There are many more. We will need a keywordlist in the property file for this) + tagDontFold = (NULL != strstr("meta link img area br hr input ",s)); + + //now we can remove the trailing space + s[i] = '\0'; + + bool isScript = false; + char chAttr = SCE_H_TAGUNKNOWN; + if (s[0] == '!') { + chAttr = SCE_H_SGML_DEFAULT; + } else if (s[0] == '/') { // Closing tag + if (keywords.InList(s + 1)) + chAttr = SCE_H_TAG; + } else { + if (keywords.InList(s)) { + chAttr = SCE_H_TAG; + isScript = 0 == strcmp(s, "script"); + } + } + if ((chAttr == SCE_H_TAGUNKNOWN) && !keywords) { + // No keywords -> all are known + chAttr = SCE_H_TAG; + isScript = 0 == strcmp(s, "script"); + } + styler.ColourTo(end, chAttr); + return isScript ? SCE_H_SCRIPT : chAttr; +} + +static void classifyWordHTJS(unsigned int start, unsigned int end, + WordList &keywords, Accessor &styler, script_mode inScriptType) { + char chAttr = SCE_HJ_WORD; + bool wordIsNumber = IsADigit(styler[start]) || (styler[start] == '.'); + if (wordIsNumber) + chAttr = SCE_HJ_NUMBER; + else { + char s[30 + 1]; + unsigned int i = 0; + for (; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + } + s[i] = '\0'; + if (keywords.InList(s)) + chAttr = SCE_HJ_KEYWORD; + } + styler.ColourTo(end, statePrintForState(chAttr, inScriptType)); +} + +static int classifyWordHTVB(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler, script_mode inScriptType) { + char chAttr = SCE_HB_IDENTIFIER; + bool wordIsNumber = IsADigit(styler[start]) || (styler[start] == '.'); + if (wordIsNumber) + chAttr = SCE_HB_NUMBER; + else { + char s[100]; + GetTextSegment(styler, start, end, s, sizeof(s)); + if (keywords.InList(s)) { + chAttr = SCE_HB_WORD; + if (strcmp(s, "rem") == 0) + chAttr = SCE_HB_COMMENTLINE; + } + } + styler.ColourTo(end, statePrintForState(chAttr, inScriptType)); + if (chAttr == SCE_HB_COMMENTLINE) + return SCE_HB_COMMENTLINE; + else + return SCE_HB_DEFAULT; +} + +static void classifyWordHTPy(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler, char *prevWord, script_mode inScriptType) { + bool wordIsNumber = IsADigit(styler[start]); + char s[30 + 1]; + unsigned int i = 0; + for (; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + } + s[i] = '\0'; + char chAttr = SCE_HP_IDENTIFIER; + if (0 == strcmp(prevWord, "class")) + chAttr = SCE_HP_CLASSNAME; + else if (0 == strcmp(prevWord, "def")) + chAttr = SCE_HP_DEFNAME; + else if (wordIsNumber) + chAttr = SCE_HP_NUMBER; + else if (keywords.InList(s)) + chAttr = SCE_HP_WORD; + styler.ColourTo(end, statePrintForState(chAttr, inScriptType)); + strcpy(prevWord, s); +} + +// Update the word colour to default or keyword +// Called when in a PHP word +static void classifyWordHTPHP(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) { + char chAttr = SCE_HPHP_DEFAULT; + bool wordIsNumber = IsADigit(styler[start]) || (styler[start] == '.' && start+1 <= end && IsADigit(styler[start+1])); + if (wordIsNumber) + chAttr = SCE_HPHP_NUMBER; + else { + char s[100]; + GetTextSegment(styler, start, end, s, sizeof(s)); + if (keywords.InList(s)) + chAttr = SCE_HPHP_WORD; + } + styler.ColourTo(end, chAttr); +} + +static bool isWordHSGML(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) { + char s[30 + 1]; + unsigned int i = 0; + for (; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + } + s[i] = '\0'; + return keywords.InList(s); +} + +static bool isWordCdata(unsigned int start, unsigned int end, Accessor &styler) { + char s[30 + 1]; + unsigned int i = 0; + for (; i < end - start + 1 && i < 30; i++) { + s[i] = styler[start + i]; + } + s[i] = '\0'; + return (0 == strcmp(s, "[CDATA[")); +} + +// Return the first state to reach when entering a scripting language +static int StateForScript(script_type scriptLanguage) { + int Result; + switch (scriptLanguage) { + case eScriptVBS: + Result = SCE_HB_START; + break; + case eScriptPython: + Result = SCE_HP_START; + break; + case eScriptPHP: + Result = SCE_HPHP_DEFAULT; + break; + case eScriptXML: + Result = SCE_H_TAGUNKNOWN; + break; + case eScriptSGML: + Result = SCE_H_SGML_DEFAULT; + break; + default : + Result = SCE_HJ_START; + break; + } + return Result; +} + +static inline bool ishtmlwordchar(char ch) { + return !isascii(ch) || + (isalnum(ch) || ch == '.' || ch == '-' || ch == '_' || ch == ':' || ch == '!' || ch == '#'); +} + +static inline bool issgmlwordchar(char ch) { + return !isascii(ch) || + (isalnum(ch) || ch == '.' || ch == '_' || ch == ':' || ch == '!' || ch == '#' || ch == '['); +} + +static inline bool IsPhpWordStart(const unsigned char ch) { + return (isascii(ch) && (isalpha(ch) || (ch == '_'))) || (ch >= 0x7f); +} + +static inline bool IsPhpWordChar(char ch) { + return IsADigit(ch) || IsPhpWordStart(ch); +} + +static bool InTagState(int state) { + return state == SCE_H_TAG || state == SCE_H_TAGUNKNOWN || + state == SCE_H_SCRIPT || + state == SCE_H_ATTRIBUTE || state == SCE_H_ATTRIBUTEUNKNOWN || + state == SCE_H_NUMBER || state == SCE_H_OTHER || + state == SCE_H_DOUBLESTRING || state == SCE_H_SINGLESTRING; +} + +static bool IsCommentState(const int state) { + return state == SCE_H_COMMENT || state == SCE_H_SGML_COMMENT; +} + +static bool IsScriptCommentState(const int state) { + return state == SCE_HJ_COMMENT || state == SCE_HJ_COMMENTLINE || state == SCE_HJA_COMMENT || + state == SCE_HJA_COMMENTLINE || state == SCE_HB_COMMENTLINE || state == SCE_HBA_COMMENTLINE; +} + +static bool isLineEnd(char ch) { + return ch == '\r' || ch == '\n'; +} + +static bool isOKBeforeRE(char ch) { + return (ch == '(') || (ch == '=') || (ch == ','); +} + +static bool isPHPStringState(int state) { + return + (state == SCE_HPHP_HSTRING) || + (state == SCE_HPHP_SIMPLESTRING) || + (state == SCE_HPHP_HSTRING_VARIABLE) || + (state == SCE_HPHP_COMPLEX_VARIABLE); +} + +static int FindPhpStringDelimiter(char *phpStringDelimiter, const int phpStringDelimiterSize, int i, const int lengthDoc, Accessor &styler) { + int j; + while (i < lengthDoc && (styler[i] == ' ' || styler[i] == '\t')) + i++; + phpStringDelimiter[0] = '\n'; + for (j = i; j < lengthDoc && styler[j] != '\n' && styler[j] != '\r'; j++) { + if (j - i < phpStringDelimiterSize - 2) + phpStringDelimiter[j-i+1] = styler[j]; + else + i++; + } + phpStringDelimiter[j-i+1] = '\0'; + return j; +} + +static void ColouriseHyperTextDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], + Accessor &styler) { + WordList &keywords = *keywordlists[0]; + WordList &keywords2 = *keywordlists[1]; + WordList &keywords3 = *keywordlists[2]; + WordList &keywords4 = *keywordlists[3]; + WordList &keywords5 = *keywordlists[4]; + WordList &keywords6 = *keywordlists[5]; // SGML (DTD) keywords + + // Lexer for HTML requires more lexical states (7 bits worth) than most lexers + styler.StartAt(startPos, STYLE_MAX); + char prevWord[200]; + prevWord[0] = '\0'; + char phpStringDelimiter[200]; // PHP is not limited in length, we are + phpStringDelimiter[0] = '\0'; + int StateToPrint = initStyle; + int state = stateForPrintState(StateToPrint); + + // If inside a tag, it may be a script tag, so reread from the start to ensure any language tags are seen + if (InTagState(state)) { + while ((startPos > 0) && (InTagState(styler.StyleAt(startPos - 1)))) { + startPos--; + length++; + } + state = SCE_H_DEFAULT; + } + // String can be heredoc, must find a delimiter first + while (startPos > 0 && isPHPStringState(state) && state != SCE_HPHP_SIMPLESTRING) { + startPos--; + length++; + state = styler.StyleAt(startPos); + } + styler.StartAt(startPos, STYLE_MAX); + + int lineCurrent = styler.GetLine(startPos); + int lineState; + if (lineCurrent > 0) { + lineState = styler.GetLineState(lineCurrent); + } else { + // Default client and ASP scripting language is JavaScript + lineState = eScriptJS << 8; + lineState |= styler.GetPropertyInt("asp.default.language", eScriptJS) << 4; + } + script_mode inScriptType = script_mode((lineState >> 0) & 0x03); // 2 bits of scripting mode + bool tagOpened = (lineState >> 2) & 0x01; // 1 bit to know if we are in an opened tag + bool tagClosing = (lineState >> 3) & 0x01; // 1 bit to know if we are in a closing tag + bool tagDontFold = false; //some HTML tags should not be folded + script_type aspScript = script_type((lineState >> 4) & 0x0F); // 4 bits of script name + script_type clientScript = script_type((lineState >> 8) & 0x0F); // 4 bits of script name + int beforePreProc = (lineState >> 12) & 0xFF; // 8 bits of state + + script_type scriptLanguage = ScriptOfState(state); + + const bool foldHTML = styler.GetPropertyInt("fold.html", 0) != 0; + const bool fold = foldHTML && styler.GetPropertyInt("fold", 0); + const bool foldHTMLPreprocessor = foldHTML && styler.GetPropertyInt("fold.html.preprocessor", 1); + const bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; + const bool caseSensitive = styler.GetPropertyInt("html.tags.case.sensitive", 0) != 0; + + int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; + int levelCurrent = levelPrev; + int visibleChars = 0; + + char chPrev = ' '; + char ch = ' '; + char chPrevNonWhite = ' '; + // look back to set chPrevNonWhite properly for better regex colouring + if (scriptLanguage == eScriptJS && startPos > 0) { + int back = startPos; + int style = 0; + while (--back) { + style = styler.StyleAt(back); + if (style < SCE_HJ_DEFAULT || style > SCE_HJ_COMMENTDOC) + // includes SCE_HJ_COMMENT & SCE_HJ_COMMENTLINE + break; + } + if (style == SCE_HJ_SYMBOLS) { + chPrevNonWhite = styler.SafeGetCharAt(back); + } + } + + styler.StartSegment(startPos); + const int lengthDoc = startPos + length; + for (int i = startPos; i < lengthDoc; i++) { + const char chPrev2 = chPrev; + chPrev = ch; + if (!isspacechar(ch) && state != SCE_HJ_COMMENT && + state != SCE_HJ_COMMENTLINE && state != SCE_HJ_COMMENTDOC) + chPrevNonWhite = ch; + ch = styler[i]; + char chNext = styler.SafeGetCharAt(i + 1); + const char chNext2 = styler.SafeGetCharAt(i + 2); + + // Handle DBCS codepages + if (styler.IsLeadByte(ch)) { + chPrev = ' '; + i += 1; + continue; + } + + if ((!isspacechar(ch) || !foldCompact) && fold) + visibleChars++; + + // decide what is the current state to print (depending of the script tag) + StateToPrint = statePrintForState(state, inScriptType); + + // handle script folding + if (fold) { + switch (scriptLanguage) { + case eScriptJS: + case eScriptPHP: + //not currently supported case eScriptVBS: + + if ((state != SCE_HPHP_COMMENT) && (state != SCE_HPHP_COMMENTLINE) && (state != SCE_HJ_COMMENT) && (state != SCE_HJ_COMMENTLINE) && (state != SCE_HJ_COMMENTDOC) && (!isStringState(state))) { + //Platform::DebugPrintf("state=%d, StateToPrint=%d, initStyle=%d\n", state, StateToPrint, initStyle); + //if ((state == SCE_HPHP_OPERATOR) || (state == SCE_HPHP_DEFAULT) || (state == SCE_HJ_SYMBOLS) || (state == SCE_HJ_START) || (state == SCE_HJ_DEFAULT)) { + if ((ch == '{') || (ch == '}')) { + levelCurrent += (ch == '{') ? 1 : -1; + } + } + break; + case eScriptPython: + if (state != SCE_HP_COMMENTLINE) { + if ((ch == ':') && ((chNext == '\n') || (chNext == '\r' && chNext2 == '\n'))) { + levelCurrent++; + } else if ((ch == '\n') && !((chNext == '\r') && (chNext2 == '\n')) && (chNext != '\n')) { + // check if the number of tabs is lower than the level + int Findlevel = (levelCurrent & ~SC_FOLDLEVELBASE) * 8; + for (int j = 0; Findlevel > 0; j++) { + char chTmp = styler.SafeGetCharAt(i + j + 1); + if (chTmp == '\t') { + Findlevel -= 8; + } else if (chTmp == ' ') { + Findlevel--; + } else { + break; + } + } + + if (Findlevel > 0) { + levelCurrent -= Findlevel / 8; + if (Findlevel % 8) + levelCurrent--; + } + } + } + break; + default: + break; + } + } + + if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { + // Trigger on CR only (Mac style) or either on LF from CR+LF (Dos/Win) or on LF alone (Unix) + // Avoid triggering two times on Dos/Win + // New line -> record any line state onto /next/ line + if (fold) { + int lev = levelPrev; + if (visibleChars == 0) + lev |= SC_FOLDLEVELWHITEFLAG; + if ((levelCurrent > levelPrev) && (visibleChars > 0)) + lev |= SC_FOLDLEVELHEADERFLAG; + + styler.SetLevel(lineCurrent, lev); + visibleChars = 0; + levelPrev = levelCurrent; + } + lineCurrent++; + styler.SetLineState(lineCurrent, + ((inScriptType & 0x03) << 0) | + ((tagOpened & 0x01) << 2) | + ((tagClosing & 0x01) << 3) | + ((aspScript & 0x0F) << 4) | + ((clientScript & 0x0F) << 8) | + ((beforePreProc & 0xFF) << 12)); + } + + // generic end of script processing + else if ((inScriptType == eNonHtmlScript) && (ch == '<') && (chNext == '/')) { + // Check if it's the end of the script tag (or any other HTML tag) + switch (state) { + // in these cases, you can embed HTML tags (to confirm !!!!!!!!!!!!!!!!!!!!!!) + case SCE_H_DOUBLESTRING: + case SCE_H_SINGLESTRING: + case SCE_HJ_COMMENT: + case SCE_HJ_COMMENTDOC: + //case SCE_HJ_COMMENTLINE: // removed as this is a common thing done to hide + // the end of script marker from some JS interpreters. + case SCE_HJ_DOUBLESTRING: + case SCE_HJ_SINGLESTRING: + case SCE_HJ_REGEX: + case SCE_HB_STRING: + case SCE_HP_STRING: + case SCE_HP_TRIPLE: + case SCE_HP_TRIPLEDOUBLE: + break; + default : + // check if the closing tag is a script tag + if (state == SCE_HJ_COMMENTLINE) { + char tag[7]; // room for the

    Intel® JPEG  Library 1.51
    +System Requirements
    +Compiler Support
    +License Definitions
    +Installation
    +Library Use
    +What's New in Version 1.51
    +History
    +Known Limitations in Version 1.51
    +Technical Support and Feedback
    +